From a020259bb50296ddbd2cc6c0ff61a3efce77d9d9 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 19 Sep 2024 09:46:23 +0800 Subject: [PATCH 001/479] core/vm: fix Byzantium address list (#22603) --- core/vm/contracts.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 175db51a87aa..d8161a2c9f98 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -110,7 +110,7 @@ func init() { PrecompiledAddressesHomestead = append(PrecompiledAddressesHomestead, k) } for k := range PrecompiledContractsByzantium { - PrecompiledAddressesHomestead = append(PrecompiledAddressesByzantium, k) + PrecompiledAddressesByzantium = append(PrecompiledAddressesByzantium, k) } for k := range PrecompiledContractsIstanbul { PrecompiledAddressesIstanbul = append(PrecompiledAddressesIstanbul, k) From 4e832ee6f03cd486cf749c091ada12106661ec38 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 2 Sep 2024 15:44:25 +0800 Subject: [PATCH 002/479] core/vm: avoid map lookups for accessing jumpdest analysis (#21411) --- core/vm/contract.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/core/vm/contract.go b/core/vm/contract.go index 2be61b51d0ce..95dd59ee0633 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -93,16 +93,25 @@ func (c *Contract) validJumpdest(dest *uint256.Int) bool { if OpCode(c.Code[udest]) != JUMPDEST { return false } - // Do we have it locally already? + return c.isCode(udest) +} + +// isCode returns true if the provided PC location is an actual opcode, as +// opposed to a data-segment following a PUSHN operation. +func (c *Contract) isCode(udest uint64) bool { + // Do we already have an analysis laying around? if c.analysis != nil { return c.analysis.codeSegment(udest) } - // If we have the code hash (but no analysis), we should look into the - // parent analysis map and see if the analysis has been made previously + // Do we have a contract hash already? + // If we do have a hash, that means it's a 'regular' contract. For regular + // contracts ( not temporary initcode), we store the analysis in a map if c.CodeHash != (common.Hash{}) { + // Does parent context have the analysis? analysis, exist := c.jumpdests[c.CodeHash] if !exist { // Do the analysis and save in parent context + // We do not need to store it in c.analysis analysis = codeBitmap(c.Code) c.jumpdests[c.CodeHash] = analysis } @@ -114,7 +123,9 @@ func (c *Contract) validJumpdest(dest *uint256.Int) bool { // in state trie. In that case, we do an analysis, and save it locally, so // we don't have to recalculate it for every JUMP instruction in the execution // However, we don't save it within the parent context - c.analysis = codeBitmap(c.Code) + if c.analysis == nil { + c.analysis = codeBitmap(c.Code) + } return c.analysis.codeSegment(udest) } From 05e52efbadb1ffcd91e9dcf2359b8f460cc02b53 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 2 Sep 2024 16:09:20 +0800 Subject: [PATCH 003/479] core/vm: marshall returnData as hexstring in trace logs (#21715) --- core/vm/gen_structlog.go | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/core/vm/gen_structlog.go b/core/vm/gen_structlog.go index 74c4ec39b351..1230944d57f3 100644 --- a/core/vm/gen_structlog.go +++ b/core/vm/gen_structlog.go @@ -6,30 +6,36 @@ import ( "encoding/json" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/hexutil" + "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/holiman/uint256" ) +var _ = (*structLogMarshaling)(nil) + // MarshalJSON marshals as JSON. func (s StructLog) MarshalJSON() ([]byte, error) { type StructLog struct { Pc uint64 `json:"pc"` Op OpCode `json:"op"` - Gas uint64 `json:"gas"` - GasCost uint64 `json:"gasCost"` - Memory []byte `json:"memory"` + Gas math.HexOrDecimal64 `json:"gas"` + GasCost math.HexOrDecimal64 `json:"gasCost"` + Memory hexutil.Bytes `json:"memory"` MemorySize int `json:"memSize"` Stack []uint256.Int `json:"stack"` - ReturnData []byte `json:"returnData"` + ReturnData hexutil.Bytes `json:"returnData"` Storage map[common.Hash]common.Hash `json:"-"` Depth int `json:"depth"` RefundCounter uint64 `json:"refund"` Err error `json:"-"` + OpName string `json:"opName"` + ErrorString string `json:"error"` } var enc StructLog enc.Pc = s.Pc enc.Op = s.Op - enc.Gas = s.Gas - enc.GasCost = s.GasCost + enc.Gas = math.HexOrDecimal64(s.Gas) + enc.GasCost = math.HexOrDecimal64(s.GasCost) enc.Memory = s.Memory enc.MemorySize = s.MemorySize enc.Stack = s.Stack @@ -38,6 +44,8 @@ func (s StructLog) MarshalJSON() ([]byte, error) { enc.Depth = s.Depth enc.RefundCounter = s.RefundCounter enc.Err = s.Err + enc.OpName = s.OpName() + enc.ErrorString = s.ErrorString() return json.Marshal(&enc) } @@ -46,12 +54,12 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { type StructLog struct { Pc *uint64 `json:"pc"` Op *OpCode `json:"op"` - Gas *uint64 `json:"gas"` - GasCost *uint64 `json:"gasCost"` - Memory []byte `json:"memory"` + Gas *math.HexOrDecimal64 `json:"gas"` + GasCost *math.HexOrDecimal64 `json:"gasCost"` + Memory *hexutil.Bytes `json:"memory"` MemorySize *int `json:"memSize"` Stack []uint256.Int `json:"stack"` - ReturnData []byte `json:"returnData"` + ReturnData *hexutil.Bytes `json:"returnData"` Storage map[common.Hash]common.Hash `json:"-"` Depth *int `json:"depth"` RefundCounter *uint64 `json:"refund"` @@ -68,13 +76,13 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { s.Op = *dec.Op } if dec.Gas != nil { - s.Gas = *dec.Gas + s.Gas = uint64(*dec.Gas) } if dec.GasCost != nil { - s.GasCost = *dec.GasCost + s.GasCost = uint64(*dec.GasCost) } if dec.Memory != nil { - s.Memory = dec.Memory + s.Memory = *dec.Memory } if dec.MemorySize != nil { s.MemorySize = *dec.MemorySize @@ -83,7 +91,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { s.Stack = dec.Stack } if dec.ReturnData != nil { - s.ReturnData = dec.ReturnData + s.ReturnData = *dec.ReturnData } if dec.Storage != nil { s.Storage = dec.Storage From 5470485450295ffe215782077429f55af56d4729 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 16 Sep 2024 15:52:59 +0800 Subject: [PATCH 004/479] all: split vm.Context into BlockContext and TxContext (#21672) --- accounts/abi/bind/backends/simulated.go | 6 ++- core/evm.go | 16 +++++--- core/state_processor.go | 41 +++++++++++++------- core/state_transition.go | 31 ++++++++++++--- core/token_validator.go | 5 ++- core/vm/contracts_test.go | 4 +- core/vm/evm.go | 51 ++++++++++++++++--------- core/vm/gas_table_test.go | 4 +- core/vm/instructions.go | 16 ++++---- core/vm/instructions_test.go | 16 ++++---- core/vm/logger_test.go | 2 +- core/vm/runtime/env.go | 10 +++-- eth/api_backend.go | 5 ++- eth/api_tracer.go | 42 ++++++++++---------- eth/tracers/testing/calltrace_test.go | 20 ++++++---- eth/tracers/tracer.go | 2 +- eth/tracers/tracer_test.go | 51 +++++++++++++------------ eth/tracers/tracers_test.go | 30 +++++++++------ les/api_backend.go | 5 ++- les/odr_test.go | 10 +++-- light/odr_test.go | 5 ++- tests/state_test_util.go | 5 ++- tests/vm_test_util.go | 10 +++-- 23 files changed, 231 insertions(+), 156 deletions(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 08a7260d264f..ba62d58a700a 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -379,10 +379,12 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.Cal msg.CallMsg.BalanceTokenFee = value } } - evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain, nil) + + txContext := core.NewEVMTxContext(msg) + evmContext := core.NewEVMBlockContext(block.Header(), b.blockchain, nil) // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. - vmenv := vm.NewEVM(evmContext, statedb, nil, b.config, vm.Config{}) + vmenv := vm.NewEVM(evmContext, txContext, statedb, nil, b.config, vm.Config{}) gaspool := new(core.GasPool).AddGas(math.MaxUint64) owner := common.Address{} ret, usedGas, failed, err, _ = core.NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner) diff --git a/core/evm.go b/core/evm.go index bc1c72ce5bc2..48d0aca12239 100644 --- a/core/evm.go +++ b/core/evm.go @@ -26,8 +26,8 @@ import ( "github.com/XinFinOrg/XDPoSChain/crypto" ) -// NewEVMContext creates a new context for use in the EVM. -func NewEVMContext(msg Message, header *types.Header, chain consensus.ChainContext, author *common.Address) vm.Context { +// NewEVMBlockContext creates a new context for use in the EVM. +func NewEVMBlockContext(header *types.Header, chain consensus.ChainContext, author *common.Address) vm.BlockContext { // If we don't have an explicit author (i.e. not mining), extract from the header var ( beneficiary common.Address @@ -40,21 +40,27 @@ func NewEVMContext(msg Message, header *types.Header, chain consensus.ChainConte } // since xdpos chain do not use difficulty and mixdigest, we use hash of the block number as random random = crypto.Keccak256Hash(header.Number.Bytes()) - return vm.Context{ + return vm.BlockContext{ CanTransfer: CanTransfer, Transfer: Transfer, GetHash: GetHashFn(header, chain), - Origin: msg.From(), Coinbase: beneficiary, BlockNumber: new(big.Int).Set(header.Number), Time: new(big.Int).Set(header.Time), Difficulty: new(big.Int).Set(header.Difficulty), GasLimit: header.GasLimit, - GasPrice: new(big.Int).Set(msg.GasPrice()), Random: &random, } } +// NewEVMTxContext creates a new transaction context for a single transaction. +func NewEVMTxContext(msg Message) vm.TxContext { + return vm.TxContext{ + Origin: msg.From(), + GasPrice: new(big.Int).Set(msg.GasPrice()), + } +} + // GetHashFn returns a GetHashFunc which retrieves header hashes by number func GetHashFn(ref *types.Header, chain consensus.ChainContext) func(n uint64) common.Hash { // Cache will initially contain [refHash.parent], diff --git a/core/state_processor.go b/core/state_processor.go index e4bab6797bc1..292538c39d7d 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -86,6 +86,9 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra InitSignerInTransactions(p.config, header, block.Transactions()) balanceUpdated := map[common.Address]*big.Int{} totalFeeUsed := big.NewInt(0) + blockContext := NewEVMBlockContext(header, p.bc, nil) + vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, tradingState, p.config, cfg) + // Iterate over and process the individual transactions for i, tx := range block.Transactions() { // check black-list txs after hf if (block.Number().Uint64() >= common.BlackListHFNumber) && !common.IsTestnet { @@ -113,7 +116,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra } } statedb.Prepare(tx.Hash(), block.Hash(), i) - receipt, gas, err, tokenFeeUsed := ApplyTransaction(p.config, balanceFee, p.bc, nil, gp, statedb, tradingState, header, tx, usedGas, cfg) + receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, p.bc, nil, gp, statedb, tradingState, header, tx, usedGas, vmenv) if err != nil { return nil, nil, 0, err } @@ -159,6 +162,8 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated if cBlock.stop { return nil, nil, 0, ErrStopPreparingBlock } + blockContext := NewEVMBlockContext(header, p.bc, nil) + vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, tradingState, p.config, cfg) // Iterate over and process the individual transactions receipts = make([]*types.Receipt, block.Transactions().Len()) for i, tx := range block.Transactions() { @@ -188,7 +193,7 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated } } statedb.Prepare(tx.Hash(), block.Hash(), i) - receipt, gas, err, tokenFeeUsed := ApplyTransaction(p.config, balanceFee, p.bc, nil, gp, statedb, tradingState, header, tx, usedGas, cfg) + receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, p.bc, nil, gp, statedb, tradingState, header, tx, usedGas, vmenv) if err != nil { return nil, nil, 0, err } @@ -210,11 +215,7 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated return receipts, allLogs, *usedGas, nil } -// ApplyTransaction attempts to apply a transaction to the given state database -// and uses the input parameters for its environment. It returns the receipt -// for the transaction, gas used and an error if the transaction failed, -// indicating the block was invalid. -func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*big.Int, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, XDCxState *tradingstate.TradingStateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, uint64, error, bool) { +func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*big.Int, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, XDCxState *tradingstate.TradingStateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, uint64, error, bool) { to := tx.To() if to != nil && *to == common.BlockSignersBinary && config.IsTIPSigning(header.Number) { return ApplySignTransaction(config, statedb, header, tx, usedGas) @@ -243,11 +244,12 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* if err != nil { return nil, 0, err, false } - // Create a new context to be used in the EVM environment. - context := NewEVMContext(msg, header, bc, author) - // Create a new environment which holds all relevant information - // about the transaction and calling mechanisms. - vmenv := vm.NewEVM(context, statedb, XDCxState, config, cfg) + + // Create a new context to be used in the EVM environment + txContext := NewEVMTxContext(msg) + + // Update the evm with the new transaction context. + evm.Reset(txContext, statedb) // If we don't have an explicit author (i.e. not mining), extract from the header var beneficiary common.Address @@ -404,7 +406,7 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* // End Bypass blacklist address // Apply the transaction to the current state (included in the env) - _, gas, failed, err, _ := ApplyMessage(vmenv, msg, gp, coinbaseOwner) + _, gas, failed, err, _ := ApplyMessage(evm, msg, gp, coinbaseOwner) if err != nil { return nil, 0, err, false @@ -432,7 +434,7 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* // If the transaction created a contract, store the creation address in the receipt. if msg.To() == nil { - receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce()) + receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce()) } // Set the receipt logs and create the bloom filter. @@ -447,6 +449,17 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* return receipt, gas, err, balanceFee != nil } +// ApplyTransaction attempts to apply a transaction to the given state database +// and uses the input parameters for its environment. It returns the receipt +// for the transaction, gas used and an error if the transaction failed, +// indicating the block was invalid. +func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*big.Int, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, XDCxState *tradingstate.TradingStateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, uint64, error, bool) { + // Create a new context to be used in the EVM environment + blockContext := NewEVMBlockContext(header, bc, author) + vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, XDCxState, config, cfg) + return applyTransaction(config, tokensFee, bc, author, gp, statedb, XDCxState, header, tx , usedGas, vmenv) +} + func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, error, bool) { // Update the state with pending changes var root []byte diff --git a/core/state_transition.go b/core/state_transition.go index 3e1120ceb986..591f2b423a02 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -216,16 +216,37 @@ func (st *StateTransition) preCheck() error { } // TransitionDb will transition the state by applying the current message and -// returning the result including the the used gas. It returns an error if it -// failed. An error indicates a consensus issue. +// returning the evm execution result with following fields. +// +// - used gas: +// total gas used (including gas being refunded) +// - returndata: +// the returned data from evm +// - concrete execution error: +// various **EVM** error which aborts the execution, +// e.g. ErrOutOfGas, ErrExecutionReverted +// +// However if any consensus issue encountered, return the error directly with +// nil evm execution result. func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedGas uint64, failed bool, err error, vmErr error) { + // First check this message satisfies all consensus rules before + // applying the message. The rules include these clauses + // + // 1. the nonce of the message caller is correct + // 2. caller has enough balance to cover transaction fee(gaslimit * gasprice) + // 3. the amount of gas required is available in the block + // 4. the purchased gas is enough to cover intrinsic usage + // 5. there is no overflow when calculating intrinsic gas + // 6. caller has enough balance to cover asset transfer for **topmost** call + + // Check clauses 1-3, buy gas if everything is correct if err = st.preCheck(); err != nil { return } msg := st.msg sender := st.from() // err checked in preCheck - homestead := st.evm.ChainConfig().IsHomestead(st.evm.BlockNumber) + homestead := st.evm.ChainConfig().IsHomestead(st.evm.Context.BlockNumber) contractCreation := msg.To() == nil // Pay intrinsic gas @@ -274,12 +295,12 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG } st.refundGas() - if st.evm.BlockNumber.Cmp(common.TIPTRC21Fee) > 0 { + if st.evm.Context.BlockNumber.Cmp(common.TIPTRC21Fee) > 0 { if (owner != common.Address{}) { st.state.AddBalance(owner, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice)) } } else { - st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice)) + st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice)) } return ret, st.gasUsed(), vmerr != nil, nil, vmerr diff --git a/core/token_validator.go b/core/token_validator.go index f604be504f35..59a4faa5a432 100644 --- a/core/token_validator.go +++ b/core/token_validator.go @@ -108,10 +108,11 @@ func CallContractWithState(call ethereum.CallMsg, chain consensus.ChainContext, msg.CallMsg.BalanceTokenFee = value } } - evmContext := NewEVMContext(msg, chain.CurrentHeader(), chain, nil) + txContext := NewEVMTxContext(msg) + evmContext := NewEVMBlockContext(chain.CurrentHeader(), chain, nil) // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. - vmenv := vm.NewEVM(evmContext, statedb, nil, chain.Config(), vm.Config{}) + vmenv := vm.NewEVM(evmContext, txContext, statedb, nil, chain.Config(), vm.Config{}) gaspool := new(GasPool).AddGas(1000000) owner := common.Address{} rval, _, _, err, _ := NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner) diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index 4d8a4c762fc1..1c0b1e18474e 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -519,7 +519,7 @@ func testXDCxPrecompiled(addr string, test precompiledTest, t *testing.T) { tradingStateDB.SetLastPrice(tradingstate.GetTradingOrderBookHash(common.HexToAddress(BTCAddress), common.HexToAddress(USDTAddress)), BTCUSDTLastPrice) tradingStateDB.SetMediumPriceBeforeEpoch(tradingstate.GetTradingOrderBookHash(common.HexToAddress(BTCAddress), common.HexToAddress(USDTAddress)), BTCUSDTEpochPrice) - evm := NewEVM(Context{BlockNumber: common.Big1}, nil, tradingStateDB, ¶ms.ChainConfig{ByzantiumBlock: common.Big0}, Config{}) + evm := NewEVM(BlockContext{BlockNumber: common.Big1}, TxContext{}, nil, tradingStateDB, ¶ms.ChainConfig{ByzantiumBlock: common.Big0}, Config{}) contractAddr := common.HexToAddress(addr) p := PrecompiledContractsByzantium[contractAddr] in := common.Hex2Bytes(test.input) @@ -536,7 +536,7 @@ func testXDCxPrecompiled(addr string, test precompiledTest, t *testing.T) { } func testPrecompiledWithEmptyTradingState(addr string, test precompiledTest, t *testing.T) { - evm := NewEVM(Context{BlockNumber: common.Big1}, nil, nil, ¶ms.ChainConfig{ByzantiumBlock: common.Big0}, Config{}) + evm := NewEVM(BlockContext{BlockNumber: common.Big1}, TxContext{}, nil, nil, ¶ms.ChainConfig{ByzantiumBlock: common.Big0}, Config{}) contractAddr := common.HexToAddress(addr) p := PrecompiledContractsByzantium[contractAddr] diff --git a/core/vm/evm.go b/core/vm/evm.go index 4f61fc3c80a0..c84d905b8d32 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -63,7 +63,7 @@ func (evm *EVM) precompile2(addr common.Address) (PrecompiledContract, bool) { switch { case evm.chainRules.IsXDCxDisable: precompiles = PrecompiledContractsXDCv2 - case evm.chainRules.IsIstanbul && evm.ChainConfig().IsTIPXDCXCancellationFee(evm.BlockNumber): + case evm.chainRules.IsIstanbul && evm.ChainConfig().IsTIPXDCXCancellationFee(evm.Context.BlockNumber): precompiles = PrecompiledContractsIstanbul case evm.chainRules.IsByzantium: precompiles = PrecompiledContractsByzantium @@ -78,7 +78,7 @@ func (evm *EVM) precompile2(addr common.Address) (PrecompiledContract, bool) { func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) { if contract.CodeAddr != nil { if p, isPrecompile := evm.precompile(*contract.CodeAddr); isPrecompile { - if evm.chainConfig.IsTIPXDCXReceiver(evm.BlockNumber) { + if evm.chainConfig.IsTIPXDCXReceiver(evm.Context.BlockNumber) { switch p := p.(type) { case *XDCxEpochPrice: p.SetTradingState(evm.tradingStateDB) @@ -89,7 +89,7 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err return RunPrecompiledContract(p, input, contract) } } - if evm.ChainConfig().IsTIPXDCXCancellationFee(evm.BlockNumber) { + if evm.ChainConfig().IsTIPXDCXCancellationFee(evm.Context.BlockNumber) { for _, interpreter := range evm.interpreters { if interpreter.CanRun(contract.Code) { if evm.interpreter != interpreter { @@ -110,9 +110,9 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err return nil, errors.New("no compatible interpreter") } -// Context provides the EVM with auxiliary information. Once provided +// BlockContext provides the EVM with auxiliary information. Once provided // it shouldn't be modified. -type Context struct { +type BlockContext struct { // CanTransfer returns whether the account contains // sufficient ether to transfer the value CanTransfer CanTransferFunc @@ -121,10 +121,6 @@ type Context struct { // GetHash returns the hash corresponding to n GetHash GetHashFunc - // Message information - Origin common.Address // Provides information for ORIGIN - GasPrice *big.Int // Provides information for GASPRICE - // Block information Coinbase common.Address // Provides information for COINBASE GasLimit uint64 // Provides information for GASLIMIT @@ -134,6 +130,14 @@ type Context struct { Random *common.Hash // Provides information for PREVRANDAO } +// TxContext provides the EVM with information about a transaction. +// All fields can change between transactions. +type TxContext struct { + // Message information + Origin common.Address // Provides information for ORIGIN + GasPrice *big.Int // Provides information for GASPRICE +} + // EVM is the Ethereum Virtual Machine base object and provides // the necessary tools to run a contract on the given state with // the provided context. It should be noted that any error @@ -144,8 +148,9 @@ type Context struct { // // The EVM should never be reused and is not thread safe. type EVM struct { - // Context provides auxiliary blockchain related information - Context + // BlockContext provides auxiliary blockchain related information + Context BlockContext + TxContext // StateDB gives access to the underlying state StateDB StateDB @@ -176,14 +181,15 @@ type EVM struct { // NewEVM returns a new EVM. The returned EVM is not thread safe and should // only ever be used *once*. -func NewEVM(ctx Context, statedb StateDB, tradingStateDB *tradingstate.TradingStateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM { +func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, tradingStateDB *tradingstate.TradingStateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM { evm := &EVM{ - Context: ctx, + Context: blockCtx, + TxContext: txCtx, StateDB: statedb, tradingStateDB: tradingStateDB, vmConfig: vmConfig, chainConfig: chainConfig, - chainRules: chainConfig.Rules(ctx.BlockNumber), + chainRules: chainConfig.Rules(blockCtx.BlockNumber), interpreters: make([]Interpreter, 0, 1), } @@ -195,6 +201,13 @@ func NewEVM(ctx Context, statedb StateDB, tradingStateDB *tradingstate.TradingSt return evm } +// Reset resets the EVM with a new transaction context.Reset +// This is not threadsafe and should only be done very cautiously. +func (evm *EVM) Reset(txCtx TxContext, statedb StateDB) { + evm.TxContext = txCtx + evm.StateDB = statedb +} + // Cancel cancels any running EVM operation. This may be called concurrently and // it's safe to be called multiple times. func (evm *EVM) Cancel() { @@ -245,7 +258,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } evm.StateDB.CreateAccount(addr) } - evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value) + evm.Context.Transfer(evm.StateDB, caller.Address(), to.Address(), value) // Capture the tracer start/end events in debug mode if evm.vmConfig.Debug { @@ -390,7 +403,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte contract := NewContract(caller, to, new(big.Int), gas) contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - if evm.ChainConfig().IsTIPXDCXCancellationFee(evm.BlockNumber) { + if evm.ChainConfig().IsTIPXDCXCancellationFee(evm.Context.BlockNumber) { // We do an AddBalance of zero here, just in order to trigger a touch. // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium, // but is the correct thing to do and matters on other networks, in tests, and potential @@ -409,7 +422,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in Homestead this also counts for code storage gas errors. - ret, err = run(evm, contract, input, evm.ChainConfig().IsTIPXDCXCancellationFee(evm.BlockNumber)) + ret, err = run(evm, contract, input, evm.ChainConfig().IsTIPXDCXCancellationFee(evm.Context.BlockNumber)) gas = contract.Gas if err != nil { evm.StateDB.RevertToSnapshot(snapshot) @@ -439,7 +452,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if evm.depth > int(params.CallCreateDepth) { return nil, common.Address{}, gas, ErrDepth } - if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { + if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { return nil, common.Address{}, gas, ErrInsufficientBalance } nonce := evm.StateDB.GetNonce(caller.Address()) @@ -463,7 +476,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if evm.chainRules.IsEIP158 { evm.StateDB.SetNonce(address, 1) } - evm.Transfer(evm.StateDB, caller.Address(), address, value) + evm.Context.Transfer(evm.StateDB, caller.Address(), address, value) // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go index 501a0364c65b..c9c7e9244dd1 100644 --- a/core/vm/gas_table_test.go +++ b/core/vm/gas_table_test.go @@ -88,11 +88,11 @@ func TestEIP2200(t *testing.T) { statedb.SetState(address, common.Hash{}, common.BytesToHash([]byte{tt.original})) statedb.Finalise(true) // Push the state into the "original" slot - vmctx := Context{ + vmctx := BlockContext{ CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true }, Transfer: func(StateDB, common.Address, common.Address, *big.Int) {}, } - vmenv := NewEVM(vmctx, statedb, nil, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}}) + vmenv := NewEVM(vmctx, TxContext{}, statedb, nil, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}}) _, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(big.Int)) if err != tt.failure { diff --git a/core/vm/instructions.go b/core/vm/instructions.go index b2bf296c1faf..e5d51fe30eb0 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -447,14 +447,14 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( return nil, nil } var upper, lower uint64 - upper = interpreter.evm.BlockNumber.Uint64() + upper = interpreter.evm.Context.BlockNumber.Uint64() if upper < 257 { lower = 0 } else { lower = upper - 256 } if num64 >= lower && num64 < upper { - num.SetBytes(interpreter.evm.GetHash(num64).Bytes()) + num.SetBytes(interpreter.evm.Context.GetHash(num64).Bytes()) } else { num.Clear() } @@ -462,24 +462,24 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( } func opCoinbase(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Coinbase.Bytes())) + scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Context.Coinbase.Bytes())) return nil, nil } func opTimestamp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - v, _ := uint256.FromBig(interpreter.evm.Time) + v, _ := uint256.FromBig(interpreter.evm.Context.Time) scope.Stack.push(v) return nil, nil } func opNumber(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - v, _ := uint256.FromBig(interpreter.evm.BlockNumber) + v, _ := uint256.FromBig(interpreter.evm.Context.BlockNumber) scope.Stack.push(v) return nil, nil } func opDifficulty(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - v, _ := uint256.FromBig(interpreter.evm.Difficulty) + v, _ := uint256.FromBig(interpreter.evm.Context.Difficulty) scope.Stack.push(v) return nil, nil } @@ -496,7 +496,7 @@ func opRandom(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b } func opGasLimit(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit)) + scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.Context.GasLimit)) return nil, nil } @@ -851,7 +851,7 @@ func makeLog(size int) executionFunc { Data: d, // This is a non-consensus field, but assigned here because // core/state doesn't know the current block number. - BlockNumber: interpreter.evm.BlockNumber.Uint64(), + BlockNumber: interpreter.evm.Context.BlockNumber.Uint64(), }) return nil, nil diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 580a008b4e72..c4a631729382 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -95,7 +95,7 @@ func init() { func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) { var ( - env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) evmInterpreter = env.interpreter.(*EVMInterpreter) @@ -194,7 +194,7 @@ func TestSAR(t *testing.T) { func TestAddMod(t *testing.T) { var ( - env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() evmInterpreter = NewEVMInterpreter(env, env.vmConfig) pc = uint64(0) @@ -233,7 +233,7 @@ func TestAddMod(t *testing.T) { // getResult is a convenience function to generate the expected values func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase { var ( - env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) interpreter = env.interpreter.(*EVMInterpreter) @@ -289,7 +289,7 @@ func TestJsonTestcases(t *testing.T) { func opBenchmark(bench *testing.B, op executionFunc, args ...string) { var ( - env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() evmInterpreter = NewEVMInterpreter(env, env.vmConfig) ) @@ -523,7 +523,7 @@ func BenchmarkOpIsZero(b *testing.B) { func TestOpMstore(t *testing.T) { var ( - env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() evmInterpreter = NewEVMInterpreter(env, env.vmConfig) @@ -549,7 +549,7 @@ func TestOpMstore(t *testing.T) { func BenchmarkOpMstore(bench *testing.B) { var ( - env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() evmInterpreter = NewEVMInterpreter(env, env.vmConfig) @@ -571,7 +571,7 @@ func BenchmarkOpMstore(bench *testing.B) { func BenchmarkOpKeccak256(bench *testing.B) { var ( - env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() evmInterpreter = NewEVMInterpreter(env, env.vmConfig) @@ -676,7 +676,7 @@ func TestRandom(t *testing.T) { {name: "hash(0x010203)", random: crypto.Keccak256Hash([]byte{0x01, 0x02, 0x03})}, } { var ( - env = NewEVM(Context{Random: &tt.random}, nil, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{Random: &tt.random}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) evmInterpreter = NewEVMInterpreter(env, env.vmConfig) diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index f668d068fb75..108656fee11c 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -50,7 +50,7 @@ func (*dummyStatedb) GetRefund() uint64 { return 1337 } func TestStoreCapture(t *testing.T) { var ( - env = NewEVM(Context{}, &dummyStatedb{}, nil, params.TestChainConfig, Config{}) + env = NewEVM(BlockContext{}, TxContext{}, &dummyStatedb{}, nil, params.TestChainConfig, Config{}) logger = NewStructLogger(nil) contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0) scope = &ScopeContext{ diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go index 24d3a7930182..89d44ad83311 100644 --- a/core/vm/runtime/env.go +++ b/core/vm/runtime/env.go @@ -22,18 +22,20 @@ import ( ) func NewEnv(cfg *Config) *vm.EVM { - context := vm.Context{ + txContext := vm.TxContext{ + Origin: cfg.Origin, + GasPrice: cfg.GasPrice, + } + blockContext := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, GetHash: cfg.GetHashFn, - Origin: cfg.Origin, Coinbase: cfg.Coinbase, BlockNumber: cfg.BlockNumber, Time: cfg.Time, Difficulty: cfg.Difficulty, GasLimit: cfg.GasLimit, - GasPrice: cfg.GasPrice, } - return vm.NewEVM(context, cfg.State, nil, cfg.ChainConfig, cfg.EVMConfig) + return vm.NewEVM(blockContext, txContext, cfg.State, nil, cfg.ChainConfig, cfg.EVMConfig) } diff --git a/eth/api_backend.go b/eth/api_backend.go index f9a585ba5e7f..98220fd1118f 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -258,8 +258,9 @@ func (b *EthApiBackend) GetEVM(ctx context.Context, msg core.Message, state *sta vmConfig = b.eth.blockchain.GetVMConfig() } state.SetBalance(msg.From(), math.MaxBig256) - context := core.NewEVMContext(msg, header, b.eth.BlockChain(), nil) - return vm.NewEVM(context, state, XDCxState, b.eth.chainConfig, *vmConfig), vmError, nil + txContext := core.NewEVMTxContext(msg) + context := core.NewEVMBlockContext(header, b.eth.BlockChain(), nil) + return vm.NewEVM(context, txContext, state, XDCxState, b.eth.chainConfig, *vmConfig), vmError, nil } func (b *EthApiBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { diff --git a/eth/api_tracer.go b/eth/api_tracer.go index d0f5e8f14b63..3277dd7e0870 100644 --- a/eth/api_tracer.go +++ b/eth/api_tracer.go @@ -232,6 +232,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl // Fetch and execute the next block trace tasks for task := range tasks { signer := types.MakeSigner(api.config, task.block.Number()) + blockCtx := core.NewEVMBlockContext(task.block.Header(), api.eth.blockchain, nil) feeCapacity := state.GetTRC21FeeCapacityFromState(task.statedb) // Trace all the transactions contained within for i, tx := range task.block.Transactions() { @@ -247,9 +248,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl TxIndex: i, TxHash: tx.Hash(), } - vmctx := core.NewEVMContext(msg, task.block.Header(), api.eth.blockchain, nil) - - res, err := api.traceTx(ctx, msg, txctx, vmctx, task.statedb, config) + res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config) if err != nil { task.results[i] = &txTraceResult{Error: err.Error()} log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) @@ -473,11 +472,11 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, threads = len(txs) } blockHash := block.Hash() + blockCtx := core.NewEVMBlockContext(block.Header(), api.eth.blockchain, nil) for th := 0; th < threads; th++ { pend.Add(1) go func() { defer pend.Done() - // Fetch and execute the next transaction trace tasks for task := range jobs { feeCapacity := state.GetTRC21FeeCapacityFromState(task.statedb) @@ -493,9 +492,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, TxIndex: task.index, TxHash: txs[task.index].Hash(), } - vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil) - - res, err := api.traceTx(ctx, msg, txctx, vmctx, task.statedb, config) + res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config) if err != nil { results[task.index] = &txTraceResult{Error: err.Error()} continue @@ -522,10 +519,10 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, } // Generate the next state snapshot fast without tracing msg, _ := tx.AsMessage(signer, balacne, block.Number()) + txContext := core.NewEVMTxContext(msg) statedb.Prepare(tx.Hash(), block.Hash(), i) - vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil) - vmenv := vm.NewEVM(vmctx, statedb, XDCxState, api.config, vm.Config{}) + vmenv := vm.NewEVM(blockCtx, txContext, statedb, XDCxState, api.config, vm.Config{}) owner := common.Address{} if _, _, _, err, _ := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), owner); err != nil { failed = err @@ -693,7 +690,7 @@ func (api *PrivateDebugAPI) TraceCall(ctx context.Context, args ethapi.Transacti } // Execute the trace msg := args.ToMessage(api.eth.ApiBackend, block.Number(), api.eth.ApiBackend.RPCGasCap()) - vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil) + vmctx := core.NewEVMBlockContext(block.Header(), api.eth.blockchain, nil) var traceConfig *TraceConfig if config != nil { traceConfig = &config.TraceConfig @@ -704,11 +701,12 @@ func (api *PrivateDebugAPI) TraceCall(ctx context.Context, args ethapi.Transacti // traceTx configures a new tracer according to the provided configuration, and // executes the given message in the provided environment. The return value will // be tracer dependent. -func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, txctx *tracers.Context, vmctx vm.Context, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { +func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, txctx *tracers.Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { // Assemble the structured logger or the JavaScript tracer var ( - tracer vm.EVMLogger - err error + tracer vm.EVMLogger + err error + txContext = core.NewEVMTxContext(message) ) switch { case config == nil: @@ -739,7 +737,7 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, t tracer = vm.NewStructLogger(config.LogConfig) } // Run the transaction with tracing enabled. - vmenv := vm.NewEVM(vmctx, statedb, nil, api.config, vm.Config{Debug: true, Tracer: tracer}) + vmenv := vm.NewEVM(vmctx, txContext, statedb, nil, api.config, vm.Config{Debug: true, Tracer: tracer}) // Call Prepare to clear out the statedb access list statedb.Prepare(txctx.TxHash, txctx.BlockHash, txctx.TxIndex) @@ -768,19 +766,19 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, t } // computeTxEnv returns the execution environment of a certain transaction. -func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, reexec uint64) (core.Message, vm.Context, *state.StateDB, error) { +func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) { // Create the parent state database block := api.eth.blockchain.GetBlockByHash(blockHash) if block == nil { - return nil, vm.Context{}, nil, fmt.Errorf("block %x not found", blockHash) + return nil, vm.BlockContext{}, nil, fmt.Errorf("block %x not found", blockHash) } parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) if parent == nil { - return nil, vm.Context{}, nil, fmt.Errorf("parent %x not found", block.ParentHash()) + return nil, vm.BlockContext{}, nil, fmt.Errorf("parent %x not found", block.ParentHash()) } statedb, XDCxState, err := api.computeStateDB(parent, reexec) if err != nil { - return nil, vm.Context{}, nil, err + return nil, vm.BlockContext{}, nil, err } // Recompute transactions up to the target index. feeCapacity := state.GetTRC21FeeCapacityFromState(statedb) @@ -804,14 +802,14 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree } msg, err := tx.AsMessage(types.MakeSigner(api.config, block.Header().Number), balanceFee, block.Number()) if err != nil { - return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err) + return nil, vm.BlockContext{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err) } - context := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil) + context := core.NewEVMBlockContext(block.Header(), api.eth.blockchain, nil) return msg, context, statedb, nil } _, gas, err, tokenFeeUsed := core.ApplyTransaction(api.config, feeCapacity, api.eth.blockchain, nil, gp, statedb, XDCxState, block.Header(), tx, usedGas, vm.Config{}) if err != nil { - return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err) + return nil, vm.BlockContext{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err) } if tokenFeeUsed { @@ -822,5 +820,5 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree } } statedb.DeleteSuicides() - return nil, vm.Context{}, nil, fmt.Errorf("tx index %d out of range for block %x", txIndex, blockHash) + return nil, vm.BlockContext{}, nil, fmt.Errorf("tx index %d out of range for block %x", txIndex, blockHash) } diff --git a/eth/tracers/testing/calltrace_test.go b/eth/tracers/testing/calltrace_test.go index 9db470251927..718231e79559 100644 --- a/eth/tracers/testing/calltrace_test.go +++ b/eth/tracers/testing/calltrace_test.go @@ -95,7 +95,11 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { var ( signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number))) origin, _ = signer.Sender(tx) - context = vm.Context{ + txContext = vm.TxContext{ + Origin: origin, + GasPrice: tx.GasPrice(), + } + context = vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, Coinbase: test.Context.Miner, @@ -103,8 +107,6 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { Time: new(big.Int).SetUint64(uint64(test.Context.Time)), Difficulty: (*big.Int)(test.Context.Difficulty), GasLimit: uint64(test.Context.GasLimit), - Origin: origin, - GasPrice: tx.GasPrice(), } statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc) ) @@ -112,7 +114,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { if err != nil { t.Fatalf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, statedb, nil, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) + evm := vm.NewEVM(context, txContext, statedb, nil, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) msg, err := tx.AsMessage(signer, nil, nil) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) @@ -204,7 +206,11 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { b.Fatalf("failed to prepare transaction for tracing: %v", err) } origin, _ := signer.Sender(tx) - context := vm.Context{ + txContext := vm.TxContext{ + Origin: origin, + GasPrice: tx.GasPrice(), + } + context := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, Coinbase: test.Context.Miner, @@ -212,8 +218,6 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { Time: new(big.Int).SetUint64(uint64(test.Context.Time)), Difficulty: (*big.Int)(test.Context.Difficulty), GasLimit: uint64(test.Context.GasLimit), - Origin: origin, - GasPrice: tx.GasPrice(), } statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc) @@ -224,7 +228,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { if err != nil { b.Fatalf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, statedb, nil, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) + evm := vm.NewEVM(context, txContext, statedb, nil, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) snap := statedb.Snapshot() st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if _, _, _, err, _ = st.TransitionDb(common.Address{}); err != nil { diff --git a/eth/tracers/tracer.go b/eth/tracers/tracer.go index bbec5ad8a952..9e27a04dade2 100644 --- a/eth/tracers/tracer.go +++ b/eth/tracers/tracer.go @@ -670,7 +670,7 @@ func (jst *JsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Ad jst.ctx["to"] = to jst.ctx["input"] = input jst.ctx["gas"] = gas - jst.ctx["gasPrice"] = env.Context.GasPrice + jst.ctx["gasPrice"] = env.TxContext.GasPrice jst.ctx["value"] = value // Initialize the context diff --git a/eth/tracers/tracer_test.go b/eth/tracers/tracer_test.go index d1b81cfbc6e1..7721dbed303a 100644 --- a/eth/tracers/tracer_test.go +++ b/eth/tracers/tracer_test.go @@ -51,19 +51,20 @@ func (*dummyStatedb) GetRefund() uint64 { return 1337 } func (*dummyStatedb) GetBalance(addr common.Address) *big.Int { return new(big.Int) } type vmContext struct { - ctx vm.Context // future pr should distinguish blockContext and txContext + ctx vm.BlockContext + txContext vm.TxContext } -func testCtx() *vmContext { - return &vmContext{ctx: vm.Context{BlockNumber: big.NewInt(1), GasPrice: big.NewInt(100000)}} -} - -func runTrace(tracer Tracer, vmctx *vmContext, chaincfg *params.ChainConfig) (json.RawMessage, error) { - env := vm.NewEVM(vmctx.ctx, &dummyStatedb{}, nil, chaincfg, vm.Config{Debug: true, Tracer: tracer}) +func runTrace(tracer Tracer, blockNumber *big.Int, chaincfg *params.ChainConfig) (json.RawMessage, error) { var ( - startGas uint64 = 10000 - value = big.NewInt(0) + startGas uint64 = 10000 + value = big.NewInt(0) + ctx = vm.BlockContext{BlockNumber: blockNumber} + txContext = vm.TxContext{GasPrice: big.NewInt(100000)} ) + + env := vm.NewEVM(ctx, txContext, &dummyStatedb{}, nil, chaincfg, vm.Config{Debug: true, Tracer: tracer}) + contract := vm.NewContract(account{}, account{}, value, startGas) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} @@ -83,7 +84,7 @@ func TestTracer(t *testing.T) { if err != nil { t.Fatal(err) } - ret, err := runTrace(tracer, testCtx(), params.TestChainConfig) + ret, err := runTrace(tracer, big.NewInt(1), params.TestChainConfig) if err != nil { return nil, err.Error() // Stringify to allow comparison without nil checks } @@ -138,7 +139,7 @@ func TestHalt(t *testing.T) { time.Sleep(1 * time.Second) tracer.Stop(timeout) }() - if _, err = runTrace(tracer, testCtx(), params.TestChainConfig); err.Error() != "stahp in server-side tracer function 'step'" { + if _, err = runTrace(tracer, big.NewInt(1), params.TestChainConfig); err.Error() != "stahp in server-side tracer function 'step'" { t.Errorf("Expected timeout error, got %v", err) } } @@ -148,7 +149,7 @@ func TestHaltBetweenSteps(t *testing.T) { if err != nil { t.Fatal(err) } - env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) + env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) scope := &vm.ScopeContext{ Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), } @@ -165,8 +166,10 @@ func TestHaltBetweenSteps(t *testing.T) { // TestNoStepExec tests a regular value transfer (no exec), and accessing the statedb // in 'result' func TestNoStepExec(t *testing.T) { - runEmptyTrace := func(tracer Tracer, vmctx *vmContext) (json.RawMessage, error) { - env := vm.NewEVM(vmctx.ctx, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) + runEmptyTrace := func(tracer Tracer) (json.RawMessage, error) { + ctx := vm.BlockContext{BlockNumber: big.NewInt(1)} + txContext := vm.TxContext{GasPrice: big.NewInt(100000)} + env := vm.NewEVM(ctx, txContext, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) startGas := uint64(10000) contract := vm.NewContract(account{}, account{}, big.NewInt(0), startGas) tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, big.NewInt(0)) @@ -179,7 +182,7 @@ func TestNoStepExec(t *testing.T) { if err != nil { t.Fatal(err) } - ret, err := runEmptyTrace(tracer, testCtx()) + ret, err := runEmptyTrace(tracer) if err != nil { t.Fatal(err) } @@ -205,12 +208,11 @@ func TestIsPrecompile(t *testing.T) { chaincfg.ByzantiumBlock = big.NewInt(100) chaincfg.IstanbulBlock = big.NewInt(200) chaincfg.BerlinBlock = big.NewInt(300) - ctx := vm.Context{BlockNumber: big.NewInt(150), GasPrice: big.NewInt(100000)} tracer, err := New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", new(Context)) if err != nil { t.Fatal(err) } - res, err := runTrace(tracer, &vmContext{ctx}, chaincfg) + res, err := runTrace(tracer, big.NewInt(150), chaincfg) if err != nil { t.Error(err) } @@ -219,8 +221,7 @@ func TestIsPrecompile(t *testing.T) { } tracer, _ = New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", new(Context)) - ctx = vm.Context{BlockNumber: big.NewInt(250), GasPrice: big.NewInt(100000)} - res, err = runTrace(tracer, &vmContext{ctx}, chaincfg) + res, err = runTrace(tracer, big.NewInt(250), chaincfg) if err != nil { t.Error(err) } @@ -267,7 +268,7 @@ func TestRegressionPanicSlice(t *testing.T) { if err != nil { t.Fatal(err) } - if _, err = runTrace(tracer, testCtx(), params.TestChainConfig); err != nil { + if _, err = runTrace(tracer, big.NewInt(1), params.TestChainConfig); err != nil { t.Fatal(err) } } @@ -278,7 +279,7 @@ func TestRegressionPanicPeek(t *testing.T) { if err != nil { t.Fatal(err) } - if _, err = runTrace(tracer, testCtx(), params.TestChainConfig); err != nil { + if _, err = runTrace(tracer, big.NewInt(1), params.TestChainConfig); err != nil { t.Fatal(err) } } @@ -289,7 +290,7 @@ func TestRegressionPanicGetUint(t *testing.T) { if err != nil { t.Fatal(err) } - if _, err = runTrace(tracer, testCtx(), params.TestChainConfig); err != nil { + if _, err = runTrace(tracer, big.NewInt(1), params.TestChainConfig); err != nil { t.Fatal(err) } } @@ -300,7 +301,7 @@ func TestTracing(t *testing.T) { t.Fatal(err) } - ret, err := runTrace(tracer, testCtx(), params.TestChainConfig) + ret, err := runTrace(tracer, big.NewInt(1), params.TestChainConfig) if err != nil { t.Fatal(err) } @@ -315,7 +316,7 @@ func TestStack(t *testing.T) { t.Fatal(err) } - ret, err := runTrace(tracer, testCtx(), params.TestChainConfig) + ret, err := runTrace(tracer, big.NewInt(1), params.TestChainConfig) if err != nil { t.Fatal(err) } @@ -330,7 +331,7 @@ func TestOpcodes(t *testing.T) { t.Fatal(err) } - ret, err := runTrace(tracer, testCtx(), params.TestChainConfig) + ret, err := runTrace(tracer, big.NewInt(1), params.TestChainConfig) if err != nil { t.Fatal(err) } diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 74c3ff284539..a0e17e7c538f 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -118,7 +118,11 @@ func TestZeroValueToNotExitCall(t *testing.T) { t.Fatalf("err %v", err) } origin, _ := signer.Sender(tx) - context := vm.Context{ + txContext := vm.TxContext{ + Origin: origin, + GasPrice: big.NewInt(1), + } + context := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, Coinbase: common.Address{}, @@ -126,8 +130,6 @@ func TestZeroValueToNotExitCall(t *testing.T) { Time: new(big.Int).SetUint64(5), Difficulty: big.NewInt(0x30000), GasLimit: uint64(6000000), - Origin: origin, - GasPrice: big.NewInt(1), } var code = []byte{ byte(vm.PUSH1), 0x0, byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), // in and outs zero @@ -150,7 +152,7 @@ func TestZeroValueToNotExitCall(t *testing.T) { if err != nil { t.Fatalf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, statedb, nil, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}) + evm := vm.NewEVM(context, txContext, statedb, nil, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}) msg, err := tx.AsMessage(signer, nil, nil) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) @@ -200,16 +202,18 @@ func TestPrestateTracerCreate2(t *testing.T) { result: 0x60f3f640a8508fC6a86d45DF051962668E1e8AC7 */ origin, _ := signer.Sender(tx) - context := vm.Context{ + txContext := vm.TxContext{ + Origin: origin, + GasPrice: big.NewInt(1), + } + context := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, - Origin: origin, Coinbase: common.Address{}, BlockNumber: new(big.Int).SetUint64(8000000), Time: new(big.Int).SetUint64(5), Difficulty: big.NewInt(0x30000), GasLimit: uint64(6000000), - GasPrice: big.NewInt(1), } alloc := core.GenesisAlloc{} @@ -233,7 +237,7 @@ func TestPrestateTracerCreate2(t *testing.T) { if err != nil { t.Fatalf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, statedb, nil, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}) + evm := vm.NewEVM(context, txContext, statedb, nil, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}) msg, err := tx.AsMessage(signer, nil, nil) if err != nil { @@ -291,7 +295,11 @@ func BenchmarkTransactionTrace(b *testing.B) { if err != nil { b.Fatal(err) } - context := vm.Context{ + txContext := vm.TxContext{ + Origin: from, + GasPrice: tx.GasPrice(), + } + context := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, Coinbase: common.Address{}, @@ -300,8 +308,6 @@ func BenchmarkTransactionTrace(b *testing.B) { Difficulty: big.NewInt(0xffffffff), GasLimit: gas, // BaseFee: big.NewInt(8), - Origin: from, - GasPrice: tx.GasPrice(), } alloc := core.GenesisAlloc{} // The code pushes 'deadbeef' into memory, then the other params, and calls CREATE2, then returns @@ -329,7 +335,7 @@ func BenchmarkTransactionTrace(b *testing.B) { //EnableMemory: false, //EnableReturnData: false, }) - evm := vm.NewEVM(context, statedb, nil, params.AllEthashProtocolChanges, vm.Config{Debug: true, Tracer: tracer}) + evm := vm.NewEVM(context, txContext, statedb, nil, params.AllEthashProtocolChanges, vm.Config{Debug: true, Tracer: tracer}) msg, err := tx.AsMessage(signer, nil, nil) if err != nil { b.Fatalf("failed to prepare transaction for tracing: %v", err) diff --git a/les/api_backend.go b/les/api_backend.go index 876a1dcbe9f8..c5873dc5dbfe 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -184,8 +184,9 @@ func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state *sta vmConfig = new(vm.Config) } state.SetBalance(msg.From(), math.MaxBig256) - context := core.NewEVMContext(msg, header, b.eth.blockchain, nil) - return vm.NewEVM(context, state, XDCxState, b.eth.chainConfig, *vmConfig), state.Error, nil + txContext := core.NewEVMTxContext(msg) + context := core.NewEVMBlockContext(header, b.eth.blockchain, nil) + return vm.NewEVM(context, txContext, state, XDCxState, b.eth.chainConfig, *vmConfig), state.Error, nil } func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { diff --git a/les/odr_test.go b/les/odr_test.go index 1234bd28536b..1495398379a0 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -135,8 +135,9 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai } msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, nil, false, balanceTokenFee, header.Number)} - context := core.NewEVMContext(msg, header, bc, nil) - vmenv := vm.NewEVM(context, statedb, nil, config, vm.Config{}) + context := core.NewEVMBlockContext(header, bc, nil) + txContext := core.NewEVMTxContext(msg) + vmenv := vm.NewEVM(context, txContext, statedb, nil, config, vm.Config{}) //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxUint64) @@ -154,8 +155,9 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai balanceTokenFee = value } msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, nil, false, balanceTokenFee, header.Number)} - context := core.NewEVMContext(msg, header, lc, nil) - vmenv := vm.NewEVM(context, statedb, nil, config, vm.Config{}) + context := core.NewEVMBlockContext(header, lc, nil) + txContext := core.NewEVMTxContext(msg) + vmenv := vm.NewEVM(context, txContext, statedb, nil, config, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxUint64) owner := common.Address{} ret, _, _, _, _ := core.ApplyMessage(vmenv, msg, gp, owner) diff --git a/light/odr_test.go b/light/odr_test.go index 83e1c807caf6..c2c22d265712 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -185,8 +185,9 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain balanceTokenFee = value } msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, new(big.Int), data, nil, false, balanceTokenFee, header.Number)} - context := core.NewEVMContext(msg, header, chain, nil) - vmenv := vm.NewEVM(context, st, nil, config, vm.Config{}) + txContext := core.NewEVMTxContext(msg) + context := core.NewEVMBlockContext(header, chain, nil) + vmenv := vm.NewEVM(context, txContext, st, nil, config, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxUint64) owner := common.Address{} ret, _, _, _, _ := core.ApplyMessage(vmenv, msg, gp, owner) diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 742d2c595544..afb35c9d02db 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -137,9 +137,10 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD } // Prepare the EVM. - context := core.NewEVMContext(msg, block.Header(), nil, &t.json.Env.Coinbase) + txContext := core.NewEVMTxContext(msg) + context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase) context.GetHash = vmTestBlockHash - evm := vm.NewEVM(context, statedb, nil, config, vmconfig) + evm := vm.NewEVM(context, txContext, statedb, nil, config, vmconfig) // Execute the message. snapshot := statedb.Snapshot() diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index b9bdffe48552..0dfa4f634922 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -132,19 +132,21 @@ func (t *VMTest) newEVM(statedb *state.StateDB, vmconfig vm.Config) *vm.EVM { return core.CanTransfer(db, address, amount) } transfer := func(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {} - context := vm.Context{ + txContext := vm.TxContext{ + Origin: t.json.Exec.Origin, + GasPrice: t.json.Exec.GasPrice, + } + context := vm.BlockContext{ CanTransfer: canTransfer, Transfer: transfer, GetHash: vmTestBlockHash, - Origin: t.json.Exec.Origin, Coinbase: t.json.Env.Coinbase, BlockNumber: new(big.Int).SetUint64(t.json.Env.Number), Time: new(big.Int).SetUint64(t.json.Env.Timestamp), GasLimit: t.json.Env.GasLimit, Difficulty: t.json.Env.Difficulty, - GasPrice: t.json.Exec.GasPrice, } - return vm.NewEVM(context, statedb, nil, params.MainnetChainConfig, vmconfig) + return vm.NewEVM(context, txContext, statedb, nil, params.MainnetChainConfig, vmconfig) } func vmTestBlockHash(n uint64) common.Hash { From a575d731d31939072a88bcf16bec7949672a7221 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 19 Sep 2024 16:28:31 +0800 Subject: [PATCH 005/479] core/vm: combine function precompile and precompile2 --- core/vm/evm.go | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index c84d905b8d32..22c2bdc3f7ca 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -58,22 +58,6 @@ func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { return p, ok } -func (evm *EVM) precompile2(addr common.Address) (PrecompiledContract, bool) { - var precompiles map[common.Address]PrecompiledContract - switch { - case evm.chainRules.IsXDCxDisable: - precompiles = PrecompiledContractsXDCv2 - case evm.chainRules.IsIstanbul && evm.ChainConfig().IsTIPXDCXCancellationFee(evm.Context.BlockNumber): - precompiles = PrecompiledContractsIstanbul - case evm.chainRules.IsByzantium: - precompiles = PrecompiledContractsByzantium - default: - precompiles = PrecompiledContractsHomestead - } - p, ok := precompiles[addr] - return p, ok -} - // run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter. func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) { if contract.CodeAddr != nil { @@ -242,7 +226,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas snapshot = evm.StateDB.Snapshot() ) if !evm.StateDB.Exist(addr) { - _, isPrecompile := evm.precompile2(addr) + _, isPrecompile := evm.precompile(addr) if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 { // Calling a non existing account, don't do anything, but ping the tracer if evm.vmConfig.Debug { From 67b5b2bf9abcd120e8dbac80c54ba6e358209b09 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 2 Sep 2024 12:49:47 +0800 Subject: [PATCH 006/479] core/vm: less allocations for various call variants (#21222) --- core/vm/contracts.go | 28 +++++-- core/vm/contracts_test.go | 57 ++++---------- core/vm/evm.go | 156 ++++++++++++++++++++----------------- core/vm/instructions.go | 31 +++++++- core/vm/interpreter.go | 6 ++ core/vm/runtime/runtime.go | 23 ++++-- core/vm/stack.go | 15 +++- trie/secure_trie.go | 4 +- 8 files changed, 188 insertions(+), 132 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index d8161a2c9f98..ba7aa1547b1b 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -135,12 +135,29 @@ func ActivePrecompiles(rules params.Rules) []common.Address { } // RunPrecompiledContract runs and evaluates the output of a precompiled contract. -func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) { - gas := p.RequiredGas(input) - if contract.UseGas(gas) { - return p.Run(input) +// It returns +// - the returned bytes, +// - the _remaining_ gas, +// - any error that occurred +func RunPrecompiledContract(evm *EVM, p PrecompiledContract, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) { + if evm != nil { + if evm.chainConfig.IsTIPXDCXReceiver(evm.Context.BlockNumber) { + switch p := p.(type) { + case *XDCxEpochPrice: + p.SetTradingState(evm.tradingStateDB) + case *XDCxLastPrice: + p.SetTradingState(evm.tradingStateDB) + } + } + } + + gasCost := p.RequiredGas(input) + if suppliedGas < gasCost { + return nil, 0, ErrOutOfGas } - return nil, ErrOutOfGas + suppliedGas -= gasCost + output, err := p.Run(input) + return output, suppliedGas, err } // ECRECOVER implemented as a native contract. @@ -230,6 +247,7 @@ func (c *dataCopy) Run(in []byte) ([]byte, error) { type bigModExp struct{} var ( + big0 = big.NewInt(0) big1 = big.NewInt(1) big4 = big.NewInt(4) big8 = big.NewInt(8) diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index 1c0b1e18474e..ed915683c7cb 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -23,10 +23,6 @@ import ( "reflect" "testing" - "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate" - "github.com/XinFinOrg/XDPoSChain/core/rawdb" - "github.com/XinFinOrg/XDPoSChain/params" - "github.com/XinFinOrg/XDPoSChain/common" ) @@ -496,10 +492,9 @@ var blake2FTests = []precompiledTest{ func testPrecompiled(addr string, test precompiledTest, t *testing.T) { p := PrecompiledContractsIstanbul[common.HexToAddress(addr)] in := common.Hex2Bytes(test.input) - contract := NewContract(AccountRef(common.HexToAddress("1337")), - nil, new(big.Int), p.RequiredGas(in)) - t.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) { - if res, err := RunPrecompiledContract(p, in, contract); err != nil { + gas := p.RequiredGas(in) + t.Run(fmt.Sprintf("%s-Gas=%d", test.name, gas), func(t *testing.T) { + if res, _, err := RunPrecompiledContract(nil, p, in, gas); err != nil { t.Error(err) } else if common.Bytes2Hex(res) != test.expected { t.Errorf("Expected %v, got %v", test.expected, common.Bytes2Hex(res)) @@ -513,21 +508,12 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) { } func testXDCxPrecompiled(addr string, test precompiledTest, t *testing.T) { - db := rawdb.NewMemoryDatabase() - stateCache := tradingstate.NewDatabase(db) - tradingStateDB, _ := tradingstate.New(common.Hash{}, stateCache) - tradingStateDB.SetLastPrice(tradingstate.GetTradingOrderBookHash(common.HexToAddress(BTCAddress), common.HexToAddress(USDTAddress)), BTCUSDTLastPrice) - tradingStateDB.SetMediumPriceBeforeEpoch(tradingstate.GetTradingOrderBookHash(common.HexToAddress(BTCAddress), common.HexToAddress(USDTAddress)), BTCUSDTEpochPrice) - - evm := NewEVM(BlockContext{BlockNumber: common.Big1}, TxContext{}, nil, tradingStateDB, ¶ms.ChainConfig{ByzantiumBlock: common.Big0}, Config{}) contractAddr := common.HexToAddress(addr) p := PrecompiledContractsByzantium[contractAddr] in := common.Hex2Bytes(test.input) - contract := NewContract(AccountRef(common.HexToAddress("1337")), - nil, new(big.Int), p.RequiredGas(in)) - contract.SetCallCode(&contractAddr, common.Hash{}, []byte{}) - t.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) { - if res, err := run(evm, contract, in, false); err != nil { + gas := p.RequiredGas(in) + t.Run(fmt.Sprintf("%s-Gas=%d", test.name, gas), func(t *testing.T) { + if res, _, err := RunPrecompiledContract(nil, p, in, gas); err != nil { t.Error(err) } else if common.Bytes2Hex(res) != test.expected { t.Errorf("Expected %v, got %v", test.expected, common.Bytes2Hex(res)) @@ -536,16 +522,12 @@ func testXDCxPrecompiled(addr string, test precompiledTest, t *testing.T) { } func testPrecompiledWithEmptyTradingState(addr string, test precompiledTest, t *testing.T) { - evm := NewEVM(BlockContext{BlockNumber: common.Big1}, TxContext{}, nil, nil, ¶ms.ChainConfig{ByzantiumBlock: common.Big0}, Config{}) - contractAddr := common.HexToAddress(addr) p := PrecompiledContractsByzantium[contractAddr] in := common.Hex2Bytes(test.input) - contract := NewContract(AccountRef(common.HexToAddress("1337")), - nil, new(big.Int), p.RequiredGas(in)) - contract.SetCallCode(&contractAddr, common.Hash{}, []byte{}) - t.Run(fmt.Sprintf("testPrecompiledWithEmptyTradingState-%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) { - if res, err := run(evm, contract, in, false); err != nil { + gas := p.RequiredGas(in) + t.Run(fmt.Sprintf("testPrecompiledWithEmptyTradingState-%s-Gas=%d", test.name, gas), func(t *testing.T) { + if res, _, err := RunPrecompiledContract(nil, p, in, gas); err != nil { t.Error(err) } else if common.Bytes2Hex(res) != test.expected { t.Errorf("Expected %v, got %v", test.expected, common.Bytes2Hex(res)) @@ -556,10 +538,10 @@ func testPrecompiledWithEmptyTradingState(addr string, test precompiledTest, t * func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) { p := PrecompiledContractsIstanbul[common.HexToAddress(addr)] in := common.Hex2Bytes(test.input) - contract := NewContract(AccountRef(common.HexToAddress("1337")), - nil, new(big.Int), p.RequiredGas(in)-1) - t.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) { - _, err := RunPrecompiledContract(p, in, contract) + gas := p.RequiredGas(in) - 1 + + t.Run(fmt.Sprintf("%s-Gas=%d", test.name, gas), func(t *testing.T) { + _, _, err := RunPrecompiledContract(nil, p, in, gas) if err.Error() != "out of gas" { t.Errorf("Expected error [out of gas], got [%v]", err) } @@ -574,11 +556,9 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) { func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing.T) { p := PrecompiledContractsIstanbul[common.HexToAddress(addr)] in := common.Hex2Bytes(test.input) - contract := NewContract(AccountRef(common.HexToAddress("31337")), - nil, new(big.Int), p.RequiredGas(in)) - + gas := p.RequiredGas(in) t.Run(test.name, func(t *testing.T) { - _, err := RunPrecompiledContract(p, in, contract) + _, _, err := RunPrecompiledContract(nil, p, in, gas) if !reflect.DeepEqual(err, test.expectedError) { t.Errorf("Expected error [%v], got [%v]", test.expectedError, err) } @@ -597,8 +577,6 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { p := PrecompiledContractsIstanbul[common.HexToAddress(addr)] in := common.Hex2Bytes(test.input) reqGas := p.RequiredGas(in) - contract := NewContract(AccountRef(common.HexToAddress("1337")), - nil, new(big.Int), reqGas) var ( res []byte @@ -606,12 +584,11 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { data = make([]byte, len(in)) ) - bench.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(bench *testing.B) { + bench.Run(fmt.Sprintf("%s-Gas=%d", test.name, reqGas), func(bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { - contract.Gas = reqGas copy(data, in) - res, err = RunPrecompiledContract(p, data, contract) + res, _, err = RunPrecompiledContract(nil, p, data, reqGas) } bench.StopTimer() //Check if it is correct diff --git a/core/vm/evm.go b/core/vm/evm.go index 22c2bdc3f7ca..78583d1e1bf2 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -26,6 +26,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/params" + "github.com/holiman/uint256" ) // emptyCodeHash is used by create to ensure deployment is disallowed to already @@ -60,19 +61,6 @@ func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { // run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter. func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) { - if contract.CodeAddr != nil { - if p, isPrecompile := evm.precompile(*contract.CodeAddr); isPrecompile { - if evm.chainConfig.IsTIPXDCXReceiver(evm.Context.BlockNumber) { - switch p := p.(type) { - case *XDCxEpochPrice: - p.SetTradingState(evm.tradingStateDB) - case *XDCxLastPrice: - p.SetTradingState(evm.tradingStateDB) - } - } - return RunPrecompiledContract(p, input, contract) - } - } if evm.ChainConfig().IsTIPXDCXCancellationFee(evm.Context.BlockNumber) { for _, interpreter := range evm.interpreters { if interpreter.CanRun(contract.Code) { @@ -218,15 +206,13 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas return nil, gas, ErrDepth } // Fail if we're trying to transfer more than the available balance - if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { + if value.Sign() != 0 && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { return nil, gas, ErrInsufficientBalance } - var ( - to = AccountRef(addr) - snapshot = evm.StateDB.Snapshot() - ) + snapshot := evm.StateDB.Snapshot() + p, isPrecompile := evm.precompile(addr) + if !evm.StateDB.Exist(addr) { - _, isPrecompile := evm.precompile(addr) if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 { // Calling a non existing account, don't do anything, but ping the tracer if evm.vmConfig.Debug { @@ -242,7 +228,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } evm.StateDB.CreateAccount(addr) } - evm.Context.Transfer(evm.StateDB, caller.Address(), to.Address(), value) + evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value) // Capture the tracer start/end events in debug mode if evm.vmConfig.Debug { @@ -261,16 +247,24 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } } - // Initialise a new contract and set the code that is to be used by the EVM. - // The contract is a scoped environment for this execution context only. - contract := NewContract(caller, to, value, gas) - contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - - // Even if the account has no code, we need to continue because it might be a precompile - - ret, err = run(evm, contract, input, false) - gas = contract.Gas - + if isPrecompile { + ret, gas, err = RunPrecompiledContract(evm, p, input, gas) + } else { + // Initialise a new contract and set the code that is to be used by the EVM. + // The contract is a scoped environment for this execution context only. + code := evm.StateDB.GetCode(addr) + if len(code) == 0 { + ret, err = nil, nil // gas is unchanged + } else { + addrCopy := addr + // If the account has no code, we can abort here + // The depth-check is already done, and precompiles handled above + contract := NewContract(caller, AccountRef(addrCopy), value, gas) + contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code) + ret, err = run(evm, contract, input, false) + gas = contract.Gas + } + } // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in homestead this also counts for code storage gas errors. @@ -279,6 +273,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas if err != ErrExecutionReverted { gas = 0 } + // TODO: consider clearing up unused snapshots: + //} else { + // evm.StateDB.DiscardSnapshot(snapshot) } return ret, gas, err } @@ -302,10 +299,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { return nil, gas, ErrInsufficientBalance } - var ( - snapshot = evm.StateDB.Snapshot() - to = AccountRef(caller.Address()) - ) + var snapshot = evm.StateDB.Snapshot() // Invoke tracer hooks that signal entering/exiting a call frame if evm.vmConfig.Debug { @@ -315,13 +309,18 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, }(gas) } - // Initialise a new contract and set the code that is to be used by the EVM. - // The contract is a scoped environment for this execution context only. - contract := NewContract(caller, to, value, gas) - contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - - ret, err = run(evm, contract, input, false) - gas = contract.Gas + // It is allowed to call precompiles, even via delegatecall + if p, isPrecompile := evm.precompile(addr); isPrecompile { + ret, gas, err = RunPrecompiledContract(evm, p, input, gas) + } else { + addrCopy := addr + // Initialise a new contract and set the code that is to be used by the EVM. + // The contract is a scoped environment for this execution context only. + contract := NewContract(caller, AccountRef(caller.Address()), value, gas) + contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) + ret, err = run(evm, contract, input, false) + gas = contract.Gas + } if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { @@ -341,10 +340,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth } - var ( - snapshot = evm.StateDB.Snapshot() - to = AccountRef(caller.Address()) - ) + var snapshot = evm.StateDB.Snapshot() // Invoke tracer hooks that signal entering/exiting a call frame if evm.vmConfig.Debug { @@ -354,12 +350,17 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by }(gas) } - // Initialise a new contract and make initialise the delegate values - contract := NewContract(caller, to, nil, gas).AsDelegate() - contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - - ret, err = run(evm, contract, input, false) - gas = contract.Gas + // It is allowed to call precompiles, even via delegatecall + if p, isPrecompile := evm.precompile(addr); isPrecompile { + ret, gas, err = RunPrecompiledContract(evm, p, input, gas) + } else { + addrCopy := addr + // Initialise a new contract and make initialise the delegate values + contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate() + contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) + ret, err = run(evm, contract, input, false) + gas = contract.Gas + } if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { @@ -378,22 +379,18 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth } - var ( - to = AccountRef(addr) - snapshot = evm.StateDB.Snapshot() - ) - // Initialise a new contract and set the code that is to be used by the EVM. - // The contract is a scoped environment for this execution context only. - contract := NewContract(caller, to, new(big.Int), gas) - contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) + // We take a snapshot here. This is a bit counter-intuitive, and could probably be skipped. + // However, even a staticcall is considered a 'touch'. On mainnet, static calls were introduced + // after all empty accounts were deleted, so this is not required. However, if we omit this, + // then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json. + // We could change this, but for now it's left for legacy reasons + var snapshot = evm.StateDB.Snapshot() - if evm.ChainConfig().IsTIPXDCXCancellationFee(evm.Context.BlockNumber) { - // We do an AddBalance of zero here, just in order to trigger a touch. - // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium, - // but is the correct thing to do and matters on other networks, in tests, and potential - // future scenarios - evm.StateDB.AddBalance(addr, big.NewInt(0)) - } + // We do an AddBalance of zero here, just in order to trigger a touch. + // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium, + // but is the correct thing to do and matters on other networks, in tests, and potential + // future scenarios + evm.StateDB.AddBalance(addr, big0) // Invoke tracer hooks that signal entering/exiting a call frame if evm.vmConfig.Debug { @@ -403,11 +400,24 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte }(gas) } - // When an error was returned by the EVM or when setting the creation code - // above we revert to the snapshot and consume any gas remaining. Additionally - // when we're in Homestead this also counts for code storage gas errors. - ret, err = run(evm, contract, input, evm.ChainConfig().IsTIPXDCXCancellationFee(evm.Context.BlockNumber)) - gas = contract.Gas + if p, isPrecompile := evm.precompile(addr); isPrecompile { + ret, gas, err = RunPrecompiledContract(evm, p, input, gas) + } else { + // At this point, we use a copy of address. If we don't, the go compiler will + // leak the 'contract' to the outer scope, and make allocation for 'contract' + // even if the actual execution ends on RunPrecompiled above. + addrCopy := addr + // Initialise a new contract and set the code that is to be used by the EVM. + // The contract is a scoped environment for this execution context only. + contract := NewContract(caller, AccountRef(addrCopy), new(big.Int), gas) + contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) + // When an error was returned by the EVM or when setting the creation code + // above we revert to the snapshot and consume any gas remaining. Additionally + // when we're in Homestead this also counts for code storage gas errors. + readOnly := evm.ChainConfig().IsTIPXDCXCancellationFee(evm.Context.BlockNumber) + ret, err = run(evm, contract, input, readOnly) + gas = contract.Gas + } if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { @@ -531,9 +541,9 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I // // The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:] // instead of the usual sender-and-nonce-hash as the address where the contract is initialized at. -func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { +func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { codeAndHash := &codeAndHash{code: code} - contractAddr = crypto.CreateAddress2(caller.Address(), common.BigToHash(salt), codeAndHash.Hash().Bytes()) + contractAddr = crypto.CreateAddress2(caller.Address(), common.Hash(salt.Bytes32()), codeAndHash.Hash().Bytes()) return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2) } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index e5d51fe30eb0..f44fcb8a126a 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -606,7 +606,13 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b stackvalue := size scope.Contract.UseGas(gas) - res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, value.ToBig()) + //TODO: use uint256.Int instead of converting with toBig() + var bigVal = big0 + if !value.IsZero() { + bigVal = value.ToBig() + } + + res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, bigVal) // Push item on the stack based on the returned error. If the ruleset is // homestead we must check for CodeStoreOutOfGasError (homestead only // rule) and treat as an error, if the ruleset is frontier we must @@ -646,8 +652,13 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] scope.Contract.UseGas(gas) // reuse size int for stackvalue stackvalue := size + //TODO: use uint256.Int instead of converting with toBig() + bigEndowment := big0 + if !endowment.IsZero() { + bigEndowment = endowment.ToBig() + } res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract, input, gas, - endowment.ToBig(), salt.ToBig()) + bigEndowment, &salt) // Push item on the stack based on the returned error. if suberr != nil { stackvalue.Clear() @@ -680,10 +691,18 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt if interpreter.readOnly && !value.IsZero() { return nil, ErrWriteProtection } + + var bigVal = big0 + //TODO: use uint256.Int instead of converting with toBig() + // By using big0 here, we save an alloc for the most common case (non-ether-transferring contract calls), + // but it would make more sense to extend the usage of uint256.Int if !value.IsZero() { gas += params.CallStipend + bigVal = value.ToBig() } - ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, value.ToBig()) + + ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, bigVal) + if err != nil { temp.Clear() } else { @@ -712,10 +731,14 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ // Get arguments from the memory. args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) + //TODO: use uint256.Int instead of converting with toBig() + var bigVal = big0 if !value.IsZero() { gas += params.CallStipend + bigVal = value.ToBig() } - ret, returnGas, err := interpreter.evm.CallCode(scope.Contract, toAddr, args, gas, value.ToBig()) + + ret, returnGas, err := interpreter.evm.CallCode(scope.Contract, toAddr, args, gas, bigVal) if err != nil { temp.Clear() } else { diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 6e71411ac143..7d8351f70278 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -186,6 +186,12 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( logged bool // deferred EVMLogger should ignore already logged steps res []byte // result of the opcode execution function ) + // Don't move this deferrred function, it's placed before the capturestate-deferred method, + // so that it get's executed _after_: the capturestate needs the stacks before + // they are returned to the pools + defer func() { + returnStack(stack) + }() contract.Input = input if in.cfg.Debug { diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index a8741fa12dee..db5a79ae2f3d 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -52,13 +52,22 @@ type Config struct { func setDefaults(cfg *Config) { if cfg.ChainConfig == nil { cfg.ChainConfig = ¶ms.ChainConfig{ - ChainId: big.NewInt(1), - HomesteadBlock: new(big.Int), - DAOForkBlock: new(big.Int), - DAOForkSupport: false, - EIP150Block: new(big.Int), - EIP155Block: new(big.Int), - EIP158Block: new(big.Int), + ChainId: big.NewInt(1), + HomesteadBlock: new(big.Int), + DAOForkBlock: new(big.Int), + DAOForkSupport: false, + EIP150Block: new(big.Int), + EIP150Hash: common.Hash{}, + EIP155Block: new(big.Int), + EIP158Block: new(big.Int), + ByzantiumBlock: new(big.Int), + ConstantinopleBlock: new(big.Int), + PetersburgBlock: new(big.Int), + IstanbulBlock: new(big.Int), + BerlinBlock: new(big.Int), + LondonBlock: new(big.Int), + MergeBlock: new(big.Int), + ShanghaiBlock: new(big.Int), } } diff --git a/core/vm/stack.go b/core/vm/stack.go index a389c04b725d..e1a957e2445a 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -17,9 +17,17 @@ package vm import ( + "sync" + "github.com/holiman/uint256" ) +var stackPool = sync.Pool{ + New: func() interface{} { + return &Stack{data: make([]uint256.Int, 0, 16)} + }, +} + // Stack is an object for basic stack operations. Items popped to the stack are // expected to be changed and modified. stack does not take care of adding newly // initialised objects. @@ -28,7 +36,12 @@ type Stack struct { } func newstack() *Stack { - return &Stack{data: make([]uint256.Int, 0, 16)} + return stackPool.Get().(*Stack) +} + +func returnStack(s *Stack) { + s.data = s.data[:0] + stackPool.Put(s) } // Data returns the underlying uint256.Int array. diff --git a/trie/secure_trie.go b/trie/secure_trie.go index 532d541436ab..eb79f8dedbce 100644 --- a/trie/secure_trie.go +++ b/trie/secure_trie.go @@ -179,9 +179,9 @@ func (t *SecureTrie) hashKey(key []byte) []byte { h := newHasher(false) h.sha.Reset() h.sha.Write(key) - buf := h.sha.Sum(t.hashKeyBuf[:0]) + h.sha.Read(t.hashKeyBuf[:]) returnHasherToPool(h) - return buf + return t.hashKeyBuf[:] } // getSecKeyCache returns the current secure key Cache, creating a new one if From a5531a24708609768be4faed9dce5fecf683b7c3 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 18 Sep 2024 10:23:21 +0800 Subject: [PATCH 007/479] core/vm: remove redundant conversions (#21903) --- core/vm/evm.go | 2 +- core/vm/gas_table.go | 26 +++++++++++++------------- core/vm/instructions.go | 8 ++++---- core/vm/operations_acl.go | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index 78583d1e1bf2..b34fe5cfb99f 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -543,7 +543,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I // instead of the usual sender-and-nonce-hash as the address where the contract is initialized at. func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { codeAndHash := &codeAndHash{code: code} - contractAddr = crypto.CreateAddress2(caller.Address(), common.Hash(salt.Bytes32()), codeAndHash.Hash().Bytes()) + contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes()) return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2) } diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 45589157bb00..98a737706eb7 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -97,7 +97,7 @@ var ( func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var ( y, x = stack.Back(1), stack.Back(0) - current = evm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32())) + current = evm.StateDB.GetState(contract.Address(), x.Bytes32()) ) // The legacy gas metering only takes into consideration the current state // Legacy rules should be applied if we are in Petersburg (removal of EIP-1283) @@ -136,7 +136,7 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi if current == value { // noop (1) return params.NetSstoreNoopGas, nil } - original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32())) + original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32()) if original == current { if original == (common.Hash{}) { // create slot (2.1.1) return params.NetSstoreInitGas, nil @@ -163,19 +163,19 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi return params.NetSstoreDirtyGas, nil } -// 0. If *gasleft* is less than or equal to 2300, fail the current call. -// 1. If current value equals new value (this is a no-op), SLOAD_GAS is deducted. -// 2. If current value does not equal new value: -// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context): +// 0. If *gasleft* is less than or equal to 2300, fail the current call. +// 1. If current value equals new value (this is a no-op), SLOAD_GAS is deducted. +// 2. If current value does not equal new value: +// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context): // 2.1.1. If original value is 0, SSTORE_SET_GAS (20K) gas is deducted. // 2.1.2. Otherwise, SSTORE_RESET_GAS gas is deducted. If new value is 0, add SSTORE_CLEARS_SCHEDULE to refund counter. -// 2.2. If original value does not equal current value (this storage slot is dirty), SLOAD_GAS gas is deducted. Apply both of the following clauses: +// 2.2. If original value does not equal current value (this storage slot is dirty), SLOAD_GAS gas is deducted. Apply both of the following clauses: // 2.2.1. If original value is not 0: -// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEARS_SCHEDULE gas from refund counter. -// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEARS_SCHEDULE gas to refund counter. +// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEARS_SCHEDULE gas from refund counter. +// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEARS_SCHEDULE gas to refund counter. // 2.2.2. If original value equals new value (this storage slot is reset): -// 2.2.2.1. If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter. -// 2.2.2.2. Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter. +// 2.2.2.1. If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter. +// 2.2.2.2. Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter. func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { // If we fail the minimum gas availability invariant, fail (0) if contract.Gas <= params.SstoreSentryGasEIP2200 { @@ -184,14 +184,14 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m // Gas sentry honoured, do the actual gas calculation based on the stored value var ( y, x = stack.Back(1), stack.Back(0) - current = evm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32())) + current = evm.StateDB.GetState(contract.Address(), x.Bytes32()) ) value := common.Hash(y.Bytes32()) if current == value { // noop (1) return params.SloadGasEIP2200, nil } - original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32())) + original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32()) if original == current { if original == (common.Hash{}) { // create slot (2.1.1) return params.SstoreSetGasEIP2200, nil diff --git a/core/vm/instructions.go b/core/vm/instructions.go index f44fcb8a126a..eaabc1a76ec6 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -343,7 +343,7 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { slot := scope.Stack.peek() - slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.Address(slot.Bytes20())))) + slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20()))) return nil, nil } @@ -540,7 +540,7 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b loc := scope.Stack.pop() val := scope.Stack.pop() interpreter.evm.StateDB.SetState(scope.Contract.Address(), - common.Hash(loc.Bytes32()), common.Hash(val.Bytes32())) + loc.Bytes32(), val.Bytes32()) return nil, nil } @@ -842,7 +842,7 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } beneficiary := scope.Stack.pop() balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) - interpreter.evm.StateDB.AddBalance(common.Address(beneficiary.Bytes20()), balance) + interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance) interpreter.evm.StateDB.Suicide(scope.Contract.Address()) if interpreter.cfg.Debug { interpreter.cfg.Tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) @@ -864,7 +864,7 @@ func makeLog(size int) executionFunc { mStart, mSize := stack.pop(), stack.pop() for i := 0; i < size; i++ { addr := stack.pop() - topics[i] = common.Hash(addr.Bytes32()) + topics[i] = addr.Bytes32() } d := scope.Memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64())) diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index eb3c0f43dd3a..ba811cdaa865 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -73,7 +73,7 @@ func gasSStoreEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m // return params.SloadGasEIP2200, nil return cost + WarmStorageReadCostEIP2929, nil // SLOAD_GAS } - original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32())) + original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32()) if original == current { if original == (common.Hash{}) { // create slot (2.1.1) return cost + params.SstoreSetGasEIP2200, nil From 96f58768962b2e5af16376853d82967a1cb1548a Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 18 Sep 2024 12:40:34 +0800 Subject: [PATCH 008/479] common/bitutil: improve the fuzzers (#21829) --- common/bitutil/compress_fuzz.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/bitutil/compress_fuzz.go b/common/bitutil/compress_fuzz.go index 1b87f50edc9b..714bbcd131d5 100644 --- a/common/bitutil/compress_fuzz.go +++ b/common/bitutil/compress_fuzz.go @@ -24,7 +24,7 @@ import "bytes" // invocations. func Fuzz(data []byte) int { if len(data) == 0 { - return -1 + return 0 } if data[0]%2 == 0 { return fuzzEncode(data[1:]) @@ -39,7 +39,7 @@ func fuzzEncode(data []byte) int { if !bytes.Equal(data, proc) { panic("content mismatch") } - return 0 + return 1 } // fuzzDecode implements a go-fuzz fuzzer method to test the bit decoding and @@ -52,5 +52,5 @@ func fuzzDecode(data []byte) int { if comp := bitsetEncodeBytes(blob); !bytes.Equal(comp, data) { panic("content mismatch") } - return 0 + return 1 } From 4f9501f12c02dc46847fa15b6ef49d8875281766 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 18 Sep 2024 12:47:22 +0800 Subject: [PATCH 009/479] common, crypto: move fuzzers out of core (#22029) --- {common => tests/fuzzers}/bitutil/compress_fuzz.go | 14 ++++++++------ {crypto => tests/fuzzers}/bn256/bn256_fuzz.go | 6 ++---- .../fuzzers/runtime/runtime_fuzz.go | 14 +++++++------- 3 files changed, 17 insertions(+), 17 deletions(-) rename {common => tests/fuzzers}/bitutil/compress_fuzz.go (84%) rename {crypto => tests/fuzzers}/bn256/bn256_fuzz.go (95%) rename core/vm/runtime/fuzz.go => tests/fuzzers/runtime/runtime_fuzz.go (82%) diff --git a/common/bitutil/compress_fuzz.go b/tests/fuzzers/bitutil/compress_fuzz.go similarity index 84% rename from common/bitutil/compress_fuzz.go rename to tests/fuzzers/bitutil/compress_fuzz.go index 714bbcd131d5..5f241255248e 100644 --- a/common/bitutil/compress_fuzz.go +++ b/tests/fuzzers/bitutil/compress_fuzz.go @@ -14,11 +14,13 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// +build gofuzz - package bitutil -import "bytes" +import ( + "bytes" + + "github.com/XinFinOrg/XDPoSChain/common/bitutil" +) // Fuzz implements a go-fuzz fuzzer method to test various encoding method // invocations. @@ -35,7 +37,7 @@ func Fuzz(data []byte) int { // fuzzEncode implements a go-fuzz fuzzer method to test the bitset encoding and // decoding algorithm. func fuzzEncode(data []byte) int { - proc, _ := bitsetDecodeBytes(bitsetEncodeBytes(data), len(data)) + proc, _ := bitutil.DecompressBytes(bitutil.CompressBytes(data), len(data)) if !bytes.Equal(data, proc) { panic("content mismatch") } @@ -45,11 +47,11 @@ func fuzzEncode(data []byte) int { // fuzzDecode implements a go-fuzz fuzzer method to test the bit decoding and // reencoding algorithm. func fuzzDecode(data []byte) int { - blob, err := bitsetDecodeBytes(data, 1024) + blob, err := bitutil.DecompressBytes(data, 1024) if err != nil { return 0 } - if comp := bitsetEncodeBytes(blob); !bytes.Equal(comp, data) { + if comp := bitutil.CompressBytes(blob); !bytes.Equal(comp, data) { panic("content mismatch") } return 1 diff --git a/crypto/bn256/bn256_fuzz.go b/tests/fuzzers/bn256/bn256_fuzz.go similarity index 95% rename from crypto/bn256/bn256_fuzz.go rename to tests/fuzzers/bn256/bn256_fuzz.go index c3e1833ef2df..d735745185b3 100644 --- a/crypto/bn256/bn256_fuzz.go +++ b/tests/fuzzers/bn256/bn256_fuzz.go @@ -14,8 +14,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// +build gofuzz - package bn256 import ( @@ -36,7 +34,7 @@ func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1) { } xg := new(google.G1) if _, err := xg.Unmarshal(xc.Marshal()); err != nil { - panic(fmt.Sprintf("Could not marshal cloudflare -> google:", err)) + panic(fmt.Sprintf("Could not marshal cloudflare -> google: %v", err)) } return xc, xg } @@ -49,7 +47,7 @@ func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2) { } xg := new(google.G2) if _, err := xg.Unmarshal(xc.Marshal()); err != nil { - panic(fmt.Sprintf("Could not marshal cloudflare -> google:", err)) + panic(fmt.Sprintf("Could not marshal cloudflare -> google: %v", err)) } return xc, xg } diff --git a/core/vm/runtime/fuzz.go b/tests/fuzzers/runtime/runtime_fuzz.go similarity index 82% rename from core/vm/runtime/fuzz.go rename to tests/fuzzers/runtime/runtime_fuzz.go index cb9ff08b5b08..c05254fab9ef 100644 --- a/core/vm/runtime/fuzz.go +++ b/tests/fuzzers/runtime/runtime_fuzz.go @@ -14,23 +14,23 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// +build gofuzz - package runtime +import ( + "github.com/XinFinOrg/XDPoSChain/core/vm/runtime" +) + // Fuzz is the basic entry point for the go-fuzz tool // // This returns 1 for valid parsable/runable code, 0 // for invalid opcode. func Fuzz(input []byte) int { - _, _, err := Execute(input, input, &Config{ - GasLimit: 3000000, + _, _, err := runtime.Execute(input, input, &runtime.Config{ + GasLimit: 12000000, }) - // invalid opcode - if err != nil && len(err.Error()) > 6 && string(err.Error()[:7]) == "invalid" { + if err != nil && len(err.Error()) > 6 && err.Error()[:7] == "invalid" { return 0 } - return 1 } From 47d27fed3b9a6da57979747f0b6df0cc6bfeb042 Mon Sep 17 00:00:00 2001 From: JukLee0ira Date: Fri, 27 Sep 2024 11:00:33 +0800 Subject: [PATCH 010/479] all: replace uses of ioutil with io and os (#24869) --- accounts/abi/bind/auth.go | 3 +-- core/rawdb/accessors_chain_test.go | 4 ++-- crypto/crypto.go | 3 +-- crypto/crypto_test.go | 5 ++--- eth/tracers/internal/tracers/assets.go | 3 +-- eth/tracers/testing/calltrace_test.go | 10 +++++----- 6 files changed, 12 insertions(+), 16 deletions(-) diff --git a/accounts/abi/bind/auth.go b/accounts/abi/bind/auth.go index 1db4a496af49..a53aeef366df 100644 --- a/accounts/abi/bind/auth.go +++ b/accounts/abi/bind/auth.go @@ -20,7 +20,6 @@ import ( "crypto/ecdsa" "errors" "io" - "io/ioutil" "math/big" "github.com/XinFinOrg/XDPoSChain/accounts" @@ -80,7 +79,7 @@ func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts { // NewTransactorWithChainID is a utility method to easily create a transaction signer from // an encrypted json key stream and the associated passphrase. func NewTransactorWithChainID(keyin io.Reader, passphrase string, chainID *big.Int) (*TransactOpts, error) { - json, err := ioutil.ReadAll(keyin) + json, err := io.ReadAll(keyin) if err != nil { return nil, err } diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index a514c5857fdc..5ecd5ed6c770 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -19,8 +19,8 @@ package rawdb import ( "bytes" "encoding/hex" - "io/ioutil" "math/big" + "os" "testing" "github.com/XinFinOrg/XDPoSChain/common" @@ -214,7 +214,7 @@ func TestDeriveLogFields(t *testing.T) { func BenchmarkDecodeRLPLogs(b *testing.B) { // Encoded receipts from block 0x14ee094309fbe8f70b65f45ebcc08fb33f126942d97464aad5eb91cfd1e2d269 - buf, err := ioutil.ReadFile("testdata/stored_receipts.bin") + buf, err := os.ReadFile("testdata/stored_receipts.bin") if err != nil { b.Fatal(err) } diff --git a/crypto/crypto.go b/crypto/crypto.go index f8387cb73317..22849aa6ea30 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -26,7 +26,6 @@ import ( "fmt" "hash" "io" - "io/ioutil" "math/big" "os" @@ -250,7 +249,7 @@ func checkKeyFileEnd(r *bufio.Reader) error { // restrictive permissions. The key data is saved hex-encoded. func SaveECDSA(file string, key *ecdsa.PrivateKey) error { k := hex.EncodeToString(FromECDSA(key)) - return ioutil.WriteFile(file, []byte(k), 0600) + return os.WriteFile(file, []byte(k), 0600) } // GenerateKey generates a new private key. diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index 9e1bb2639b4e..696f403e02ab 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -20,7 +20,6 @@ import ( "bytes" "crypto/ecdsa" "encoding/hex" - "io/ioutil" "math/big" "os" "reflect" @@ -175,7 +174,7 @@ func TestLoadECDSA(t *testing.T) { } for _, test := range tests { - f, err := ioutil.TempFile("", "loadecdsa_test.*.txt") + f, err := os.CreateTemp("", "loadecdsa_test.*.txt") if err != nil { t.Fatal(err) } @@ -196,7 +195,7 @@ func TestLoadECDSA(t *testing.T) { } func TestSaveECDSA(t *testing.T) { - f, err := ioutil.TempFile("", "saveecdsa_test.*.txt") + f, err := os.CreateTemp("", "saveecdsa_test.*.txt") if err != nil { t.Fatal(err) } diff --git a/eth/tracers/internal/tracers/assets.go b/eth/tracers/internal/tracers/assets.go index 185967bdb634..83772dab0ef5 100644 --- a/eth/tracers/internal/tracers/assets.go +++ b/eth/tracers/internal/tracers/assets.go @@ -20,7 +20,6 @@ import ( "crypto/sha256" "fmt" "io" - "io/ioutil" "os" "path/filepath" "strings" @@ -474,7 +473,7 @@ func RestoreAsset(dir, name string) error { if err != nil { return err } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + err = os.WriteFile(_filePath(dir, name), data, info.Mode()) if err != nil { return err } diff --git a/eth/tracers/testing/calltrace_test.go b/eth/tracers/testing/calltrace_test.go index 718231e79559..8542b17313bf 100644 --- a/eth/tracers/testing/calltrace_test.go +++ b/eth/tracers/testing/calltrace_test.go @@ -2,8 +2,8 @@ package testing import ( "encoding/json" - "io/ioutil" "math/big" + "os" "path/filepath" "reflect" "strings" @@ -66,7 +66,7 @@ func TestCallTracer(t *testing.T) { } func testCallTracer(tracerName string, dirPath string, t *testing.T) { - files, err := ioutil.ReadDir(filepath.Join("..", "testdata", dirPath)) + files, err := os.ReadDir(filepath.Join("..", "testdata", dirPath)) if err != nil { t.Fatalf("failed to retrieve tracer test suite: %v", err) } @@ -83,7 +83,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { tx = new(types.Transaction) ) // Call tracer test found, read if from disk - if blob, err := ioutil.ReadFile(filepath.Join("..", "testdata", dirPath, file.Name())); err != nil { + if blob, err := os.ReadFile(filepath.Join("..", "testdata", dirPath, file.Name())); err != nil { t.Fatalf("failed to read testcase: %v", err) } else if err := json.Unmarshal(blob, test); err != nil { t.Fatalf("failed to parse testcase: %v", err) @@ -171,7 +171,7 @@ func camel(str string) string { return strings.Join(pieces, "") } func BenchmarkTracers(b *testing.B) { - files, err := ioutil.ReadDir(filepath.Join("..", "testdata", "call_tracer")) + files, err := os.ReadDir(filepath.Join("..", "testdata", "call_tracer")) if err != nil { b.Fatalf("failed to retrieve tracer test suite: %v", err) } @@ -181,7 +181,7 @@ func BenchmarkTracers(b *testing.B) { } file := file // capture range variable b.Run(camel(strings.TrimSuffix(file.Name(), ".json")), func(b *testing.B) { - blob, err := ioutil.ReadFile(filepath.Join("..", "testdata", "call_tracer", file.Name())) + blob, err := os.ReadFile(filepath.Join("..", "testdata", "call_tracer", file.Name())) if err != nil { b.Fatalf("failed to read testcase: %v", err) } From edace6ac6cb5510e7be1e346a672f253d4126311 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 25 Sep 2024 11:10:49 +0800 Subject: [PATCH 011/479] all: change format 0x%x to %#x (#25221) --- cmd/evm/runner.go | 4 +- consensus/misc/forks.go | 2 +- core/asm/asm.go | 4 +- core/blockchain.go | 2 +- core/types/transaction.go | 2 +- core/vm/instructions_test.go | 2 +- core/vm/logger.go | 8 +- core/vm/runtime/runtime_test.go | 4 +- eth/api_test.go | 5 +- eth/filters/api_test.go | 2 +- internal/ethapi/api.go | 2 +- mobile/types.go | 4 +- p2p/discover/database_test.go | 6 +- p2p/discv5/database_test.go | 6 +- rlp/rlpgen/gen.go | 2 +- test.txt | 801 ++++++++++++++++++++++++++++++++ tests/state_test.go | 2 +- trie/trie_test.go | 2 +- 18 files changed, 831 insertions(+), 29 deletions(-) create mode 100644 test.txt diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 101069aff4f7..3c9e7ac00f6e 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -76,7 +76,7 @@ func runCmd(ctx *cli.Context) error { log.Root().SetHandler(glogger) logconfig := &vm.LogConfig{ EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name), - DisableStack: ctx.GlobalBool(DisableStackFlag.Name), + DisableStack: ctx.GlobalBool(DisableStackFlag.Name), } var ( @@ -238,7 +238,7 @@ Gas used: %d if tracer != nil { tracer.CaptureEnd(ret, initialGas-leftOverGas, execTime, err) } else { - fmt.Printf("0x%x\n", ret) + fmt.Printf("%#x\n", ret) if err != nil { fmt.Printf(" error: %v\n", err) } diff --git a/consensus/misc/forks.go b/consensus/misc/forks.go index 910ed620c545..b1e789f6013e 100644 --- a/consensus/misc/forks.go +++ b/consensus/misc/forks.go @@ -35,7 +35,7 @@ func VerifyForkHashes(config *params.ChainConfig, header *types.Header, uncle bo // If the homestead reprice hash is set, validate it if config.EIP150Block != nil && config.EIP150Block.Cmp(header.Number) == 0 { if config.EIP150Hash != (common.Hash{}) && config.EIP150Hash != header.Hash() { - return fmt.Errorf("homestead gas reprice fork: have 0x%x, want 0x%x", header.Hash(), config.EIP150Hash) + return fmt.Errorf("homestead gas reprice fork: have %#x, want %#x", header.Hash(), config.EIP150Hash) } } // All ok, return diff --git a/core/asm/asm.go b/core/asm/asm.go index 79406aaf6024..33aa034e7120 100644 --- a/core/asm/asm.go +++ b/core/asm/asm.go @@ -109,7 +109,7 @@ func PrintDisassembled(code string) error { it := NewInstructionIterator(script) for it.Next() { if it.Arg() != nil && 0 < len(it.Arg()) { - fmt.Printf("%06v: %v 0x%x\n", it.PC(), it.Op(), it.Arg()) + fmt.Printf("%06v: %v %#x\n", it.PC(), it.Op(), it.Arg()) } else { fmt.Printf("%06v: %v\n", it.PC(), it.Op()) } @@ -124,7 +124,7 @@ func Disassemble(script []byte) ([]string, error) { it := NewInstructionIterator(script) for it.Next() { if it.Arg() != nil && 0 < len(it.Arg()) { - instrs = append(instrs, fmt.Sprintf("%06v: %v 0x%x\n", it.PC(), it.Op(), it.Arg())) + instrs = append(instrs, fmt.Sprintf("%06v: %v %#x\n", it.PC(), it.Op(), it.Arg())) } else { instrs = append(instrs, fmt.Sprintf("%06v: %v\n", it.PC(), it.Op())) } diff --git a/core/blockchain.go b/core/blockchain.go index 006e94fd0e05..fb98f45dab93 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2365,7 +2365,7 @@ func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, e Chain config: %v Number: %v -Hash: 0x%x +Hash: %#x %v Round: %v diff --git a/core/types/transaction.go b/core/types/transaction.go index 1c02d70537c3..9549af34027d 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -539,7 +539,7 @@ func (tx *Transaction) String() string { GasPrice: %#x GasLimit %#x Value: %#x - Data: 0x%x + Data: %#x V: %#x R: %#x S: %#x diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index c4a631729382..000a22b2d8eb 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -654,7 +654,7 @@ func TestCreate2Addreses(t *testing.T) { stack.push(big.NewInt(0)) // memstart stack.push(big.NewInt(0)) // value gas, _ := gasCreate2(params.GasTable{}, nil, nil, stack, nil, 0) - fmt.Printf("Example %d\n* address `0x%x`\n* salt `0x%x`\n* init_code `0x%x`\n* gas (assuming no mem expansion): `%v`\n* result: `%s`\n\n", i,origin, salt, code, gas, address.String()) + fmt.Printf("Example %d\n* address `%#x`\n* salt `%#x`\n* init_code `%#x`\n* gas (assuming no mem expansion): `%v`\n* result: `%s`\n\n", i,origin, salt, code, gas, address.String()) */ expected := common.BytesToAddress(common.FromHex(tt.expected)) if !bytes.Equal(expected.Bytes(), address.Bytes()) { diff --git a/core/vm/logger.go b/core/vm/logger.go index e8294e3d674a..ce014b17fc1e 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -220,7 +220,7 @@ func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration l.output = output l.err = err if l.cfg.Debug { - fmt.Printf("0x%x\n", output) + fmt.Printf("%#x\n", output) if err != nil { fmt.Printf(" error: %v\n", err) } @@ -305,11 +305,11 @@ func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger { func (t *mdLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { if !create { - fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n", + fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `%#x`\nGas: `%d`\nValue `%v` wei\n", from.String(), to.String(), input, gas, value) } else { - fmt.Fprintf(t.out, "From: `%v`\nCreate at: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n", + fmt.Fprintf(t.out, "From: `%v`\nCreate at: `%v`\nData: `%#x`\nGas: `%d`\nValue `%v` wei\n", from.String(), to.String(), input, gas, value) } @@ -346,7 +346,7 @@ func (t *mdLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64 } func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, err error) { - fmt.Fprintf(t.out, "\nOutput: `0x%x`\nConsumed gas: `%d`\nError: `%v`\n", + fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n", output, gasUsed, err) } diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 826b431f48ad..21f5e1a57529 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -498,7 +498,7 @@ func TestEip2929Cases(t *testing.T) { it := asm.NewInstructionIterator(code) for it.Next() { if it.Arg() != nil && 0 < len(it.Arg()) { - instrs = append(instrs, fmt.Sprintf("%v 0x%x", it.Op(), it.Arg())) + instrs = append(instrs, fmt.Sprintf("%v %#x", it.Op(), it.Arg())) } else { instrs = append(instrs, fmt.Sprintf("%v", it.Op())) } @@ -506,7 +506,7 @@ func TestEip2929Cases(t *testing.T) { ops := strings.Join(instrs, ", ") fmt.Printf("### Case %d\n\n", id) id++ - fmt.Printf("%v\n\nBytecode: \n```\n0x%x\n```\nOperations: \n```\n%v\n```\n\n", + fmt.Printf("%v\n\nBytecode: \n```\n%#x\n```\nOperations: \n```\n%v\n```\n\n", comment, code, ops) Execute(code, nil, &Config{ diff --git a/eth/api_test.go b/eth/api_test.go index 37a16e8f790c..b7a6929b6ad1 100644 --- a/eth/api_test.go +++ b/eth/api_test.go @@ -17,10 +17,11 @@ package eth import ( - "github.com/XinFinOrg/XDPoSChain/core/rawdb" "reflect" "testing" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" + "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/davecgh/go-spew/spew" @@ -84,7 +85,7 @@ func TestStorageRangeAt(t *testing.T) { t.Error(err) } if !reflect.DeepEqual(result, test.want) { - t.Fatalf("wrong result for range 0x%x.., limit %d:\ngot %s\nwant %s", + t.Fatalf("wrong result for range %#x.., limit %d:\ngot %s\nwant %s", test.start, test.limit, dumper.Sdump(result), dumper.Sdump(&test.want)) } } diff --git a/eth/filters/api_test.go b/eth/filters/api_test.go index f74c13e64390..ca0f0c60eb56 100644 --- a/eth/filters/api_test.go +++ b/eth/filters/api_test.go @@ -56,7 +56,7 @@ func TestUnmarshalJSONNewFilterArgs(t *testing.T) { // from, to block number var test1 FilterCriteria - vector := fmt.Sprintf(`{"fromBlock":"0x%x","toBlock":"0x%x"}`, fromBlock, toBlock) + vector := fmt.Sprintf(`{"fromBlock":"%#x","toBlock":"%#x"}`, fromBlock, toBlock) if err := json.Unmarshal([]byte(vector), &test1); err != nil { t.Fatal(err) } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 62e0f101ece4..3ef8df9b30e2 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -3333,7 +3333,7 @@ func (api *PublicDebugAPI) SeedHash(ctx context.Context, number uint64) (string, if block == nil { return "", fmt.Errorf("block #%d not found", number) } - return fmt.Sprintf("0x%x", ethash.SeedHash(number)), nil + return fmt.Sprintf("%#x", ethash.SeedHash(number)), nil } // PrivateDebugAPI is the collection of Ethereum APIs exposed over the private diff --git a/mobile/types.go b/mobile/types.go index f577606e97a3..24ba9b4ddfc1 100644 --- a/mobile/types.go +++ b/mobile/types.go @@ -41,7 +41,7 @@ func (n *Nonce) GetBytes() []byte { // GetHex retrieves the hex string representation of the block nonce. func (n *Nonce) GetHex() string { - return fmt.Sprintf("0x%x", n.nonce[:]) + return fmt.Sprintf("%#x", n.nonce[:]) } // Bloom represents a 256 bit bloom filter. @@ -56,7 +56,7 @@ func (b *Bloom) GetBytes() []byte { // GetHex retrieves the hex string representation of the bloom filter. func (b *Bloom) GetHex() string { - return fmt.Sprintf("0x%x", b.bloom[:]) + return fmt.Sprintf("%#x", b.bloom[:]) } // Header represents a block header in the Ethereum blockchain. diff --git a/p2p/discover/database_test.go b/p2p/discover/database_test.go index 6f452a060ceb..d881c653377e 100644 --- a/p2p/discover/database_test.go +++ b/p2p/discover/database_test.go @@ -56,14 +56,14 @@ var nodeDBKeyTests = []struct { func TestNodeDBKeys(t *testing.T) { for i, tt := range nodeDBKeyTests { if key := makeKey(tt.id, tt.field); !bytes.Equal(key, tt.key) { - t.Errorf("make test %d: key mismatch: have 0x%x, want 0x%x", i, key, tt.key) + t.Errorf("make test %d: key mismatch: have %#x, want %#x", i, key, tt.key) } id, field := splitKey(tt.key) if !bytes.Equal(id[:], tt.id[:]) { - t.Errorf("split test %d: id mismatch: have 0x%x, want 0x%x", i, id, tt.id) + t.Errorf("split test %d: id mismatch: have %#x, want %#x", i, id, tt.id) } if field != tt.field { - t.Errorf("split test %d: field mismatch: have 0x%x, want 0x%x", i, field, tt.field) + t.Errorf("split test %d: field mismatch: have %#x, want %#x", i, field, tt.field) } } } diff --git a/p2p/discv5/database_test.go b/p2p/discv5/database_test.go index ff0bc1a174bc..73ee613a34fe 100644 --- a/p2p/discv5/database_test.go +++ b/p2p/discv5/database_test.go @@ -56,14 +56,14 @@ var nodeDBKeyTests = []struct { func TestNodeDBKeys(t *testing.T) { for i, tt := range nodeDBKeyTests { if key := makeKey(tt.id, tt.field); !bytes.Equal(key, tt.key) { - t.Errorf("make test %d: key mismatch: have 0x%x, want 0x%x", i, key, tt.key) + t.Errorf("make test %d: key mismatch: have %#x, want %#x", i, key, tt.key) } id, field := splitKey(tt.key) if !bytes.Equal(id[:], tt.id[:]) { - t.Errorf("split test %d: id mismatch: have 0x%x, want 0x%x", i, id, tt.id) + t.Errorf("split test %d: id mismatch: have %#x, want %#x", i, id, tt.id) } if field != tt.field { - t.Errorf("split test %d: field mismatch: have 0x%x, want 0x%x", i, field, tt.field) + t.Errorf("split test %d: field mismatch: have %#x, want %#x", i, field, tt.field) } } } diff --git a/rlp/rlpgen/gen.go b/rlp/rlpgen/gen.go index ed502c09a7e3..fb9b12e0a18b 100644 --- a/rlp/rlpgen/gen.go +++ b/rlp/rlpgen/gen.go @@ -440,7 +440,7 @@ func (op ptrOp) genWrite(ctx *genContext, v string) string { var b bytes.Buffer fmt.Fprintf(&b, "if %s == nil {\n", v) - fmt.Fprintf(&b, " w.Write([]byte{0x%X})\n", op.nilValue) + fmt.Fprintf(&b, " w.Write([]byte{%#X})\n", op.nilValue) fmt.Fprintf(&b, "} else {\n") fmt.Fprintf(&b, " %s", op.elem.genWrite(ctx, vv)) fmt.Fprintf(&b, "}\n") diff --git a/test.txt b/test.txt new file mode 100644 index 000000000000..f357fafea3ce --- /dev/null +++ b/test.txt @@ -0,0 +1,801 @@ +go run build/ci.go install +>>> /home/me/govm/golang/go-1.21.13/bin/go install -ldflags -X main.gitCommit=5be30c01a972daaea449fc1acb02180546f85557 -v ./... +# gopkg.in/olebedev/go-duktape.v3 +duk_logging.c: In function ‘duk__logger_prototype_log_shared’: +duk_logging.c:184:71: warning: ‘Z’ directive writing 1 byte into a region of size between 0 and 9 [-Wformat-overflow=] + 184 | sprintf((char *) date_buf, "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", + | ^ +In file included from /usr/include/stdio.h:894, + from duk_logging.c:5: +/usr/include/x86_64-linux-gnu/bits/stdio2.h:38:10: note: ‘__builtin___sprintf_chk’ output between 25 and 85 bytes into a destination of size 32 + 38 | return __builtin___sprintf_chk (__s, __USE_FORTIFY_LEVEL - 1, + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 39 | __glibc_objsize (__s), __fmt, + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 40 | __va_arg_pack ()); + | ~~~~~~~~~~~~~~~~~ +github.com/XinFinOrg/XDPoSChain/cmd/abigen +github.com/XinFinOrg/XDPoSChain/cmd/rlpdump +github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/deploy +github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/fee/lending +github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/fee/trading +github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/price +github.com/XinFinOrg/XDPoSChain/cmd/p2psim +github.com/XinFinOrg/XDPoSChain/cmd/XDC +github.com/XinFinOrg/XDPoSChain/cmd/bootnode +github.com/XinFinOrg/XDPoSChain/cmd/ethkey +github.com/XinFinOrg/XDPoSChain/cmd/evm +github.com/XinFinOrg/XDPoSChain/cmd/faucet +github.com/XinFinOrg/XDPoSChain/cmd/gc +github.com/XinFinOrg/XDPoSChain/cmd/puppeth +github.com/XinFinOrg/XDPoSChain/cmd/wnode +github.com/XinFinOrg/XDPoSChain/contracts/XDCx/testnet/deploy +github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer/simulation/deploy +github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer/simulation/test +github.com/XinFinOrg/XDPoSChain/p2p/simulations/examples +github.com/XinFinOrg/XDPoSChain/rlp/rlpgen +go run build/ci.go test +>>> /home/me/govm/golang/go-1.21.13/bin/go test -ldflags -X main.gitCommit=5be30c01a972daaea449fc1acb02180546f85557 -p 1 github.com/XinFinOrg/XDPoSChain github.com/XinFinOrg/XDPoSChain/XDCx github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate github.com/XinFinOrg/XDPoSChain/XDCxDAO github.com/XinFinOrg/XDPoSChain/XDCxlending github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate github.com/XinFinOrg/XDPoSChain/accounts github.com/XinFinOrg/XDPoSChain/accounts/abi github.com/XinFinOrg/XDPoSChain/accounts/abi/bind github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends github.com/XinFinOrg/XDPoSChain/accounts/keystore github.com/XinFinOrg/XDPoSChain/accounts/usbwallet github.com/XinFinOrg/XDPoSChain/accounts/usbwallet/internal/trezor github.com/XinFinOrg/XDPoSChain/bmt github.com/XinFinOrg/XDPoSChain/cmd/XDC github.com/XinFinOrg/XDPoSChain/cmd/abigen github.com/XinFinOrg/XDPoSChain/cmd/bootnode github.com/XinFinOrg/XDPoSChain/cmd/ethkey github.com/XinFinOrg/XDPoSChain/cmd/evm github.com/XinFinOrg/XDPoSChain/cmd/evm/internal/compiler github.com/XinFinOrg/XDPoSChain/cmd/faucet github.com/XinFinOrg/XDPoSChain/cmd/gc github.com/XinFinOrg/XDPoSChain/cmd/internal/browser github.com/XinFinOrg/XDPoSChain/cmd/p2psim github.com/XinFinOrg/XDPoSChain/cmd/puppeth github.com/XinFinOrg/XDPoSChain/cmd/rlpdump github.com/XinFinOrg/XDPoSChain/cmd/utils github.com/XinFinOrg/XDPoSChain/cmd/wnode github.com/XinFinOrg/XDPoSChain/common github.com/XinFinOrg/XDPoSChain/common/bitutil github.com/XinFinOrg/XDPoSChain/common/compiler github.com/XinFinOrg/XDPoSChain/common/countdown github.com/XinFinOrg/XDPoSChain/common/fdlimit github.com/XinFinOrg/XDPoSChain/common/hexutil github.com/XinFinOrg/XDPoSChain/common/lru github.com/XinFinOrg/XDPoSChain/common/math github.com/XinFinOrg/XDPoSChain/common/mclock github.com/XinFinOrg/XDPoSChain/common/number github.com/XinFinOrg/XDPoSChain/common/prque github.com/XinFinOrg/XDPoSChain/common/sort github.com/XinFinOrg/XDPoSChain/compression/rle github.com/XinFinOrg/XDPoSChain/consensus github.com/XinFinOrg/XDPoSChain/consensus/XDPoS github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/engines/engine_v1 github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/engines/engine_v2 github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils github.com/XinFinOrg/XDPoSChain/consensus/clique github.com/XinFinOrg/XDPoSChain/consensus/ethash github.com/XinFinOrg/XDPoSChain/consensus/misc github.com/XinFinOrg/XDPoSChain/consensus/tests github.com/XinFinOrg/XDPoSChain/consensus/tests/engine_v1_tests github.com/XinFinOrg/XDPoSChain/consensus/tests/engine_v2_tests github.com/XinFinOrg/XDPoSChain/console github.com/XinFinOrg/XDPoSChain/contracts github.com/XinFinOrg/XDPoSChain/contracts/XDCx github.com/XinFinOrg/XDPoSChain/contracts/XDCx/contract github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/deploy github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/fee/lending github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/fee/trading github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/price github.com/XinFinOrg/XDPoSChain/contracts/XDCx/testnet github.com/XinFinOrg/XDPoSChain/contracts/XDCx/testnet/deploy github.com/XinFinOrg/XDPoSChain/contracts/blocksigner github.com/XinFinOrg/XDPoSChain/contracts/blocksigner/contract github.com/XinFinOrg/XDPoSChain/contracts/ens github.com/XinFinOrg/XDPoSChain/contracts/ens/contract github.com/XinFinOrg/XDPoSChain/contracts/multisigwallet github.com/XinFinOrg/XDPoSChain/contracts/multisigwallet/contract github.com/XinFinOrg/XDPoSChain/contracts/randomize github.com/XinFinOrg/XDPoSChain/contracts/randomize/contract github.com/XinFinOrg/XDPoSChain/contracts/tests github.com/XinFinOrg/XDPoSChain/contracts/tests/contract github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer/contract github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer/simulation github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer/simulation/deploy github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer/simulation/test github.com/XinFinOrg/XDPoSChain/contracts/validator github.com/XinFinOrg/XDPoSChain/contracts/validator/contract github.com/XinFinOrg/XDPoSChain/core github.com/XinFinOrg/XDPoSChain/core/asm github.com/XinFinOrg/XDPoSChain/core/bloombits github.com/XinFinOrg/XDPoSChain/core/rawdb github.com/XinFinOrg/XDPoSChain/core/state github.com/XinFinOrg/XDPoSChain/core/types github.com/XinFinOrg/XDPoSChain/core/vm github.com/XinFinOrg/XDPoSChain/core/vm/privacy github.com/XinFinOrg/XDPoSChain/core/vm/runtime github.com/XinFinOrg/XDPoSChain/crypto github.com/XinFinOrg/XDPoSChain/crypto/blake2b github.com/XinFinOrg/XDPoSChain/crypto/bn256 github.com/XinFinOrg/XDPoSChain/crypto/bn256/cloudflare github.com/XinFinOrg/XDPoSChain/crypto/bn256/google github.com/XinFinOrg/XDPoSChain/crypto/ecies github.com/XinFinOrg/XDPoSChain/crypto/randentropy github.com/XinFinOrg/XDPoSChain/crypto/secp256k1 github.com/XinFinOrg/XDPoSChain/crypto/sha3 github.com/XinFinOrg/XDPoSChain/eth github.com/XinFinOrg/XDPoSChain/eth/bft github.com/XinFinOrg/XDPoSChain/eth/downloader github.com/XinFinOrg/XDPoSChain/eth/ethconfig github.com/XinFinOrg/XDPoSChain/eth/fetcher github.com/XinFinOrg/XDPoSChain/eth/filters github.com/XinFinOrg/XDPoSChain/eth/gasprice github.com/XinFinOrg/XDPoSChain/eth/hooks github.com/XinFinOrg/XDPoSChain/eth/tracers github.com/XinFinOrg/XDPoSChain/eth/tracers/internal/tracers github.com/XinFinOrg/XDPoSChain/eth/tracers/native github.com/XinFinOrg/XDPoSChain/eth/tracers/testing github.com/XinFinOrg/XDPoSChain/eth/util github.com/XinFinOrg/XDPoSChain/ethclient github.com/XinFinOrg/XDPoSChain/ethdb github.com/XinFinOrg/XDPoSChain/ethdb/dbtest github.com/XinFinOrg/XDPoSChain/ethdb/leveldb github.com/XinFinOrg/XDPoSChain/ethdb/memorydb github.com/XinFinOrg/XDPoSChain/ethstats github.com/XinFinOrg/XDPoSChain/event github.com/XinFinOrg/XDPoSChain/event/filter github.com/XinFinOrg/XDPoSChain/internal/build github.com/XinFinOrg/XDPoSChain/internal/cmdtest github.com/XinFinOrg/XDPoSChain/internal/debug github.com/XinFinOrg/XDPoSChain/internal/ethapi github.com/XinFinOrg/XDPoSChain/internal/guide github.com/XinFinOrg/XDPoSChain/internal/jsre github.com/XinFinOrg/XDPoSChain/internal/jsre/deps github.com/XinFinOrg/XDPoSChain/internal/web3ext github.com/XinFinOrg/XDPoSChain/les github.com/XinFinOrg/XDPoSChain/les/flowcontrol github.com/XinFinOrg/XDPoSChain/light github.com/XinFinOrg/XDPoSChain/log github.com/XinFinOrg/XDPoSChain/log/term github.com/XinFinOrg/XDPoSChain/metrics github.com/XinFinOrg/XDPoSChain/metrics/exp github.com/XinFinOrg/XDPoSChain/metrics/influxdb github.com/XinFinOrg/XDPoSChain/metrics/librato github.com/XinFinOrg/XDPoSChain/miner github.com/XinFinOrg/XDPoSChain/mobile github.com/XinFinOrg/XDPoSChain/node github.com/XinFinOrg/XDPoSChain/p2p github.com/XinFinOrg/XDPoSChain/p2p/discover github.com/XinFinOrg/XDPoSChain/p2p/discv5 github.com/XinFinOrg/XDPoSChain/p2p/enr github.com/XinFinOrg/XDPoSChain/p2p/nat github.com/XinFinOrg/XDPoSChain/p2p/netutil github.com/XinFinOrg/XDPoSChain/p2p/protocols github.com/XinFinOrg/XDPoSChain/p2p/simulations github.com/XinFinOrg/XDPoSChain/p2p/simulations/adapters github.com/XinFinOrg/XDPoSChain/p2p/simulations/examples github.com/XinFinOrg/XDPoSChain/p2p/testing github.com/XinFinOrg/XDPoSChain/params github.com/XinFinOrg/XDPoSChain/rlp github.com/XinFinOrg/XDPoSChain/rlp/internal/rlpstruct github.com/XinFinOrg/XDPoSChain/rlp/rlpgen github.com/XinFinOrg/XDPoSChain/rpc github.com/XinFinOrg/XDPoSChain/tests github.com/XinFinOrg/XDPoSChain/tests/fuzzers/bitutil github.com/XinFinOrg/XDPoSChain/tests/fuzzers/bn256 github.com/XinFinOrg/XDPoSChain/tests/fuzzers/runtime github.com/XinFinOrg/XDPoSChain/trie github.com/XinFinOrg/XDPoSChain/whisper/mailserver github.com/XinFinOrg/XDPoSChain/whisper/shhclient github.com/XinFinOrg/XDPoSChain/whisper/whisperv5 github.com/XinFinOrg/XDPoSChain/whisper/whisperv6 +? github.com/XinFinOrg/XDPoSChain [no test files] +ok github.com/XinFinOrg/XDPoSChain/XDCx (cached) +ok github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate (cached) +? github.com/XinFinOrg/XDPoSChain/XDCxDAO [no test files] +ok github.com/XinFinOrg/XDPoSChain/XDCxlending (cached) +ok github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate (cached) +ok github.com/XinFinOrg/XDPoSChain/accounts (cached) +ok github.com/XinFinOrg/XDPoSChain/accounts/abi (cached) +ok github.com/XinFinOrg/XDPoSChain/accounts/abi/bind 2.409s +? github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends [no test files] +ok github.com/XinFinOrg/XDPoSChain/accounts/keystore 15.031s +? github.com/XinFinOrg/XDPoSChain/accounts/usbwallet [no test files] +? github.com/XinFinOrg/XDPoSChain/accounts/usbwallet/internal/trezor [no test files] +ok github.com/XinFinOrg/XDPoSChain/bmt (cached) +# gopkg.in/olebedev/go-duktape.v3 +duk_logging.c: In function ‘duk__logger_prototype_log_shared’: +duk_logging.c:184:71: warning: ‘Z’ directive writing 1 byte into a region of size between 0 and 9 [-Wformat-overflow=] + 184 | sprintf((char *) date_buf, "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", + | ^ +In file included from /usr/include/stdio.h:894, + from duk_logging.c:5: +/usr/include/x86_64-linux-gnu/bits/stdio2.h:38:10: note: ‘__builtin___sprintf_chk’ output between 25 and 85 bytes into a destination of size 32 + 38 | return __builtin___sprintf_chk (__s, __USE_FORTIFY_LEVEL - 1, + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 39 | __glibc_objsize (__s), __fmt, + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 40 | __va_arg_pack ()); + | ~~~~~~~~~~~~~~~~~ +ok github.com/XinFinOrg/XDPoSChain/cmd/XDC (cached) +? github.com/XinFinOrg/XDPoSChain/cmd/abigen [no test files] +? github.com/XinFinOrg/XDPoSChain/cmd/bootnode [no test files] +ok github.com/XinFinOrg/XDPoSChain/cmd/ethkey (cached) +? github.com/XinFinOrg/XDPoSChain/cmd/evm [no test files] +? github.com/XinFinOrg/XDPoSChain/cmd/evm/internal/compiler [no test files] +? github.com/XinFinOrg/XDPoSChain/cmd/faucet [no test files] +? github.com/XinFinOrg/XDPoSChain/cmd/gc [no test files] +? github.com/XinFinOrg/XDPoSChain/cmd/internal/browser [no test files] +? github.com/XinFinOrg/XDPoSChain/cmd/p2psim [no test files] +? github.com/XinFinOrg/XDPoSChain/cmd/puppeth [no test files] +? github.com/XinFinOrg/XDPoSChain/cmd/rlpdump [no test files] +ok github.com/XinFinOrg/XDPoSChain/cmd/utils (cached) +? github.com/XinFinOrg/XDPoSChain/cmd/wnode [no test files] +ok github.com/XinFinOrg/XDPoSChain/common (cached) +ok github.com/XinFinOrg/XDPoSChain/common/bitutil (cached) +ok github.com/XinFinOrg/XDPoSChain/common/compiler (cached) +ok github.com/XinFinOrg/XDPoSChain/common/countdown (cached) +ok github.com/XinFinOrg/XDPoSChain/common/fdlimit (cached) +ok github.com/XinFinOrg/XDPoSChain/common/hexutil (cached) +ok github.com/XinFinOrg/XDPoSChain/common/lru (cached) +ok github.com/XinFinOrg/XDPoSChain/common/math (cached) +ok github.com/XinFinOrg/XDPoSChain/common/mclock (cached) +ok github.com/XinFinOrg/XDPoSChain/common/number (cached) +ok github.com/XinFinOrg/XDPoSChain/common/prque (cached) +? github.com/XinFinOrg/XDPoSChain/common/sort [no test files] +ok github.com/XinFinOrg/XDPoSChain/compression/rle (cached) +? github.com/XinFinOrg/XDPoSChain/consensus [no test files] +ok github.com/XinFinOrg/XDPoSChain/consensus/XDPoS (cached) +ok github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/engines/engine_v1 (cached) +ok github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/engines/engine_v2 (cached) +ok github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils (cached) +? github.com/XinFinOrg/XDPoSChain/consensus/clique [no test files] +ok github.com/XinFinOrg/XDPoSChain/consensus/ethash (cached) +? github.com/XinFinOrg/XDPoSChain/consensus/misc [no test files] +ok github.com/XinFinOrg/XDPoSChain/consensus/tests (cached) +ok github.com/XinFinOrg/XDPoSChain/consensus/tests/engine_v1_tests (cached) +ok github.com/XinFinOrg/XDPoSChain/consensus/tests/engine_v2_tests (cached) +ok github.com/XinFinOrg/XDPoSChain/console (cached) +ok github.com/XinFinOrg/XDPoSChain/contracts (cached) +? github.com/XinFinOrg/XDPoSChain/contracts/XDCx [no test files] +? github.com/XinFinOrg/XDPoSChain/contracts/XDCx/contract [no test files] +? github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation [no test files] +? github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/deploy [no test files] +? github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/fee/lending [no test files] +? github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/fee/trading [no test files] +? github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/price [no test files] +? github.com/XinFinOrg/XDPoSChain/contracts/XDCx/testnet [no test files] +? github.com/XinFinOrg/XDPoSChain/contracts/XDCx/testnet/deploy [no test files] +ok github.com/XinFinOrg/XDPoSChain/contracts/blocksigner (cached) +? github.com/XinFinOrg/XDPoSChain/contracts/blocksigner/contract [no test files] +ok github.com/XinFinOrg/XDPoSChain/contracts/ens (cached) +? github.com/XinFinOrg/XDPoSChain/contracts/ens/contract [no test files] +? github.com/XinFinOrg/XDPoSChain/contracts/multisigwallet [no test files] +? github.com/XinFinOrg/XDPoSChain/contracts/multisigwallet/contract [no test files] +ok github.com/XinFinOrg/XDPoSChain/contracts/randomize (cached) +? github.com/XinFinOrg/XDPoSChain/contracts/randomize/contract [no test files] +ok github.com/XinFinOrg/XDPoSChain/contracts/tests (cached) +? github.com/XinFinOrg/XDPoSChain/contracts/tests/contract [no test files] +ok github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer (cached) +? github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer/contract [no test files] +? github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer/simulation [no test files] +? github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer/simulation/deploy [no test files] +? github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer/simulation/test [no test files] +ok github.com/XinFinOrg/XDPoSChain/contracts/validator (cached) +? github.com/XinFinOrg/XDPoSChain/contracts/validator/contract [no test files] +ok github.com/XinFinOrg/XDPoSChain/core (cached) +ok github.com/XinFinOrg/XDPoSChain/core/asm (cached) +ok github.com/XinFinOrg/XDPoSChain/core/bloombits (cached) +ok github.com/XinFinOrg/XDPoSChain/core/rawdb (cached) +ok github.com/XinFinOrg/XDPoSChain/core/state (cached) +ok github.com/XinFinOrg/XDPoSChain/core/types (cached) +ok github.com/XinFinOrg/XDPoSChain/core/vm (cached) +ok github.com/XinFinOrg/XDPoSChain/core/vm/privacy (cached) +ok github.com/XinFinOrg/XDPoSChain/core/vm/runtime (cached) +ok github.com/XinFinOrg/XDPoSChain/crypto (cached) +ok github.com/XinFinOrg/XDPoSChain/crypto/blake2b (cached) +? github.com/XinFinOrg/XDPoSChain/crypto/bn256 [no test files] +ok github.com/XinFinOrg/XDPoSChain/crypto/bn256/cloudflare (cached) +ok github.com/XinFinOrg/XDPoSChain/crypto/bn256/google (cached) +ok github.com/XinFinOrg/XDPoSChain/crypto/ecies (cached) +? github.com/XinFinOrg/XDPoSChain/crypto/randentropy [no test files] +ok github.com/XinFinOrg/XDPoSChain/crypto/secp256k1 (cached) +ok github.com/XinFinOrg/XDPoSChain/crypto/sha3 (cached) +ok github.com/XinFinOrg/XDPoSChain/eth (cached) +ok github.com/XinFinOrg/XDPoSChain/eth/bft (cached) +ok github.com/XinFinOrg/XDPoSChain/eth/downloader (cached) +? github.com/XinFinOrg/XDPoSChain/eth/ethconfig [no test files] +ok github.com/XinFinOrg/XDPoSChain/eth/fetcher (cached) +ok github.com/XinFinOrg/XDPoSChain/eth/filters (cached) +? github.com/XinFinOrg/XDPoSChain/eth/gasprice [no test files] +? github.com/XinFinOrg/XDPoSChain/eth/hooks [no test files] +ok github.com/XinFinOrg/XDPoSChain/eth/tracers (cached) +? github.com/XinFinOrg/XDPoSChain/eth/tracers/internal/tracers [no test files] +? github.com/XinFinOrg/XDPoSChain/eth/tracers/native [no test files] +ok github.com/XinFinOrg/XDPoSChain/eth/tracers/testing (cached) +? github.com/XinFinOrg/XDPoSChain/eth/util [no test files] +ok github.com/XinFinOrg/XDPoSChain/ethclient (cached) [no tests to run] +? github.com/XinFinOrg/XDPoSChain/ethdb [no test files] +? github.com/XinFinOrg/XDPoSChain/ethdb/dbtest [no test files] +ok github.com/XinFinOrg/XDPoSChain/ethdb/leveldb (cached) +ok github.com/XinFinOrg/XDPoSChain/ethdb/memorydb (cached) +? github.com/XinFinOrg/XDPoSChain/ethstats [no test files] +ok github.com/XinFinOrg/XDPoSChain/event (cached) +ok github.com/XinFinOrg/XDPoSChain/event/filter (cached) +? github.com/XinFinOrg/XDPoSChain/internal/build [no test files] +? github.com/XinFinOrg/XDPoSChain/internal/cmdtest [no test files] +? github.com/XinFinOrg/XDPoSChain/internal/debug [no test files] +ok github.com/XinFinOrg/XDPoSChain/internal/ethapi (cached) +ok github.com/XinFinOrg/XDPoSChain/internal/guide (cached) +ok github.com/XinFinOrg/XDPoSChain/internal/jsre (cached) +? github.com/XinFinOrg/XDPoSChain/internal/jsre/deps [no test files] +? github.com/XinFinOrg/XDPoSChain/internal/web3ext [no test files] +ok github.com/XinFinOrg/XDPoSChain/les (cached) +? github.com/XinFinOrg/XDPoSChain/les/flowcontrol [no test files] +ok github.com/XinFinOrg/XDPoSChain/light (cached) +? github.com/XinFinOrg/XDPoSChain/log [no test files] +? github.com/XinFinOrg/XDPoSChain/log/term [no test files] +ok github.com/XinFinOrg/XDPoSChain/metrics (cached) +? github.com/XinFinOrg/XDPoSChain/metrics/exp [no test files] +? github.com/XinFinOrg/XDPoSChain/metrics/influxdb [no test files] +? github.com/XinFinOrg/XDPoSChain/metrics/librato [no test files] +ok github.com/XinFinOrg/XDPoSChain/miner (cached) +ok github.com/XinFinOrg/XDPoSChain/mobile (cached) +ok github.com/XinFinOrg/XDPoSChain/node (cached) +ok github.com/XinFinOrg/XDPoSChain/p2p (cached) +ok github.com/XinFinOrg/XDPoSChain/p2p/discover (cached) +ok github.com/XinFinOrg/XDPoSChain/p2p/discv5 (cached) +ok github.com/XinFinOrg/XDPoSChain/p2p/enr (cached) +ok github.com/XinFinOrg/XDPoSChain/p2p/nat (cached) +ok github.com/XinFinOrg/XDPoSChain/p2p/netutil (cached) +ok github.com/XinFinOrg/XDPoSChain/p2p/protocols (cached) +ok github.com/XinFinOrg/XDPoSChain/p2p/simulations (cached) +? github.com/XinFinOrg/XDPoSChain/p2p/simulations/adapters [no test files] +? github.com/XinFinOrg/XDPoSChain/p2p/simulations/examples [no test files] +? github.com/XinFinOrg/XDPoSChain/p2p/testing [no test files] +ok github.com/XinFinOrg/XDPoSChain/params (cached) +ok github.com/XinFinOrg/XDPoSChain/rlp (cached) +? github.com/XinFinOrg/XDPoSChain/rlp/internal/rlpstruct [no test files] +--- FAIL: TestOutput (0.05s) + --- FAIL: TestOutput/nil (0.00s) + gen_test.go:78: output mismatch, want: package test + + import "github.com/XinFinOrg/XDPoSChain/rlp" + import "io" + + func (obj *Test) EncodeRLP(_w io.Writer) error { + w := rlp.NewEncoderBuffer(_w) + _tmp0 := w.List() + if obj.Uint8 == nil { + w.Write([]byte{0x80}) + } else { + w.WriteUint64(uint64((*obj.Uint8))) + } + if obj.Uint8List == nil { + w.Write([]byte{0xC0}) + } else { + w.WriteUint64(uint64((*obj.Uint8List))) + } + if obj.Uint32 == nil { + w.Write([]byte{0x80}) + } else { + w.WriteUint64(uint64((*obj.Uint32))) + } + if obj.Uint32List == nil { + w.Write([]byte{0xC0}) + } else { + w.WriteUint64(uint64((*obj.Uint32List))) + } + if obj.Uint64 == nil { + w.Write([]byte{0x80}) + } else { + w.WriteUint64((*obj.Uint64)) + } + if obj.Uint64List == nil { + w.Write([]byte{0xC0}) + } else { + w.WriteUint64((*obj.Uint64List)) + } + if obj.String == nil { + w.Write([]byte{0x80}) + } else { + w.WriteString((*obj.String)) + } + if obj.StringList == nil { + w.Write([]byte{0xC0}) + } else { + w.WriteString((*obj.StringList)) + } + if obj.ByteArray == nil { + w.Write([]byte{0x80}) + } else { + w.WriteBytes(obj.ByteArray[:]) + } + if obj.ByteArrayList == nil { + w.Write([]byte{0xC0}) + } else { + w.WriteBytes(obj.ByteArrayList[:]) + } + if obj.ByteSlice == nil { + w.Write([]byte{0x80}) + } else { + w.WriteBytes((*obj.ByteSlice)) + } + if obj.ByteSliceList == nil { + w.Write([]byte{0xC0}) + } else { + w.WriteBytes((*obj.ByteSliceList)) + } + if obj.Struct == nil { + w.Write([]byte{0xC0}) + } else { + _tmp1 := w.List() + w.WriteUint64(uint64(obj.Struct.A)) + w.ListEnd(_tmp1) + } + if obj.StructString == nil { + w.Write([]byte{0x80}) + } else { + _tmp2 := w.List() + w.WriteUint64(uint64(obj.StructString.A)) + w.ListEnd(_tmp2) + } + w.ListEnd(_tmp0) + return w.Flush() + } + + func (obj *Test) DecodeRLP(dec *rlp.Stream) error { + var _tmp0 Test + { + if _, err := dec.List(); err != nil { + return err + } + // Uint8: + var _tmp2 *byte + if _tmp3, _tmp4, err := dec.Kind(); err != nil { + return err + } else if _tmp4 != 0 || _tmp3 != rlp.String { + _tmp1, err := dec.Uint8() + if err != nil { + return err + } + _tmp2 = &_tmp1 + } + _tmp0.Uint8 = _tmp2 + // Uint8List: + var _tmp6 *byte + if _tmp7, _tmp8, err := dec.Kind(); err != nil { + return err + } else if _tmp8 != 0 || _tmp7 != rlp.List { + _tmp5, err := dec.Uint8() + if err != nil { + return err + } + _tmp6 = &_tmp5 + } + _tmp0.Uint8List = _tmp6 + // Uint32: + var _tmp10 *uint32 + if _tmp11, _tmp12, err := dec.Kind(); err != nil { + return err + } else if _tmp12 != 0 || _tmp11 != rlp.String { + _tmp9, err := dec.Uint32() + if err != nil { + return err + } + _tmp10 = &_tmp9 + } + _tmp0.Uint32 = _tmp10 + // Uint32List: + var _tmp14 *uint32 + if _tmp15, _tmp16, err := dec.Kind(); err != nil { + return err + } else if _tmp16 != 0 || _tmp15 != rlp.List { + _tmp13, err := dec.Uint32() + if err != nil { + return err + } + _tmp14 = &_tmp13 + } + _tmp0.Uint32List = _tmp14 + // Uint64: + var _tmp18 *uint64 + if _tmp19, _tmp20, err := dec.Kind(); err != nil { + return err + } else if _tmp20 != 0 || _tmp19 != rlp.String { + _tmp17, err := dec.Uint64() + if err != nil { + return err + } + _tmp18 = &_tmp17 + } + _tmp0.Uint64 = _tmp18 + // Uint64List: + var _tmp22 *uint64 + if _tmp23, _tmp24, err := dec.Kind(); err != nil { + return err + } else if _tmp24 != 0 || _tmp23 != rlp.List { + _tmp21, err := dec.Uint64() + if err != nil { + return err + } + _tmp22 = &_tmp21 + } + _tmp0.Uint64List = _tmp22 + // String: + var _tmp26 *string + if _tmp27, _tmp28, err := dec.Kind(); err != nil { + return err + } else if _tmp28 != 0 || _tmp27 != rlp.String { + _tmp25, err := dec.String() + if err != nil { + return err + } + _tmp26 = &_tmp25 + } + _tmp0.String = _tmp26 + // StringList: + var _tmp30 *string + if _tmp31, _tmp32, err := dec.Kind(); err != nil { + return err + } else if _tmp32 != 0 || _tmp31 != rlp.List { + _tmp29, err := dec.String() + if err != nil { + return err + } + _tmp30 = &_tmp29 + } + _tmp0.StringList = _tmp30 + // ByteArray: + var _tmp34 *[3]byte + if _tmp35, _tmp36, err := dec.Kind(); err != nil { + return err + } else if _tmp36 != 0 || _tmp35 != rlp.String { + var _tmp33 [3]byte + if err := dec.ReadBytes(_tmp33[:]); err != nil { + return err + } + _tmp34 = &_tmp33 + } + _tmp0.ByteArray = _tmp34 + // ByteArrayList: + var _tmp38 *[3]byte + if _tmp39, _tmp40, err := dec.Kind(); err != nil { + return err + } else if _tmp40 != 0 || _tmp39 != rlp.List { + var _tmp37 [3]byte + if err := dec.ReadBytes(_tmp37[:]); err != nil { + return err + } + _tmp38 = &_tmp37 + } + _tmp0.ByteArrayList = _tmp38 + // ByteSlice: + var _tmp42 *[]byte + if _tmp43, _tmp44, err := dec.Kind(); err != nil { + return err + } else if _tmp44 != 0 || _tmp43 != rlp.String { + _tmp41, err := dec.Bytes() + if err != nil { + return err + } + _tmp42 = &_tmp41 + } + _tmp0.ByteSlice = _tmp42 + // ByteSliceList: + var _tmp46 *[]byte + if _tmp47, _tmp48, err := dec.Kind(); err != nil { + return err + } else if _tmp48 != 0 || _tmp47 != rlp.List { + _tmp45, err := dec.Bytes() + if err != nil { + return err + } + _tmp46 = &_tmp45 + } + _tmp0.ByteSliceList = _tmp46 + // Struct: + var _tmp51 *Aux + if _tmp52, _tmp53, err := dec.Kind(); err != nil { + return err + } else if _tmp53 != 0 || _tmp52 != rlp.List { + var _tmp49 Aux + { + if _, err := dec.List(); err != nil { + return err + } + // A: + _tmp50, err := dec.Uint32() + if err != nil { + return err + } + _tmp49.A = _tmp50 + if err := dec.ListEnd(); err != nil { + return err + } + } + _tmp51 = &_tmp49 + } + _tmp0.Struct = _tmp51 + // StructString: + var _tmp56 *Aux + if _tmp57, _tmp58, err := dec.Kind(); err != nil { + return err + } else if _tmp58 != 0 || _tmp57 != rlp.String { + var _tmp54 Aux + { + if _, err := dec.List(); err != nil { + return err + } + // A: + _tmp55, err := dec.Uint32() + if err != nil { + return err + } + _tmp54.A = _tmp55 + if err := dec.ListEnd(); err != nil { + return err + } + } + _tmp56 = &_tmp54 + } + _tmp0.StructString = _tmp56 + if err := dec.ListEnd(); err != nil { + return err + } + } + *obj = _tmp0 + return nil + } + got package test + + import "github.com/XinFinOrg/XDPoSChain/rlp" + import "io" + + func (obj *Test) EncodeRLP(_w io.Writer) error { + w := rlp.NewEncoderBuffer(_w) + _tmp0 := w.List() + if obj.Uint8 == nil { + w.Write([]byte{0x80}) + } else { + w.WriteUint64(uint64((*obj.Uint8))) + } + if obj.Uint8List == nil { + w.Write([]byte{0xc0}) + } else { + w.WriteUint64(uint64((*obj.Uint8List))) + } + if obj.Uint32 == nil { + w.Write([]byte{0x80}) + } else { + w.WriteUint64(uint64((*obj.Uint32))) + } + if obj.Uint32List == nil { + w.Write([]byte{0xc0}) + } else { + w.WriteUint64(uint64((*obj.Uint32List))) + } + if obj.Uint64 == nil { + w.Write([]byte{0x80}) + } else { + w.WriteUint64((*obj.Uint64)) + } + if obj.Uint64List == nil { + w.Write([]byte{0xc0}) + } else { + w.WriteUint64((*obj.Uint64List)) + } + if obj.String == nil { + w.Write([]byte{0x80}) + } else { + w.WriteString((*obj.String)) + } + if obj.StringList == nil { + w.Write([]byte{0xc0}) + } else { + w.WriteString((*obj.StringList)) + } + if obj.ByteArray == nil { + w.Write([]byte{0x80}) + } else { + w.WriteBytes(obj.ByteArray[:]) + } + if obj.ByteArrayList == nil { + w.Write([]byte{0xc0}) + } else { + w.WriteBytes(obj.ByteArrayList[:]) + } + if obj.ByteSlice == nil { + w.Write([]byte{0x80}) + } else { + w.WriteBytes((*obj.ByteSlice)) + } + if obj.ByteSliceList == nil { + w.Write([]byte{0xc0}) + } else { + w.WriteBytes((*obj.ByteSliceList)) + } + if obj.Struct == nil { + w.Write([]byte{0xc0}) + } else { + _tmp1 := w.List() + w.WriteUint64(uint64(obj.Struct.A)) + w.ListEnd(_tmp1) + } + if obj.StructString == nil { + w.Write([]byte{0x80}) + } else { + _tmp2 := w.List() + w.WriteUint64(uint64(obj.StructString.A)) + w.ListEnd(_tmp2) + } + w.ListEnd(_tmp0) + return w.Flush() + } + + func (obj *Test) DecodeRLP(dec *rlp.Stream) error { + var _tmp0 Test + { + if _, err := dec.List(); err != nil { + return err + } + // Uint8: + var _tmp2 *byte + if _tmp3, _tmp4, err := dec.Kind(); err != nil { + return err + } else if _tmp4 != 0 || _tmp3 != rlp.String { + _tmp1, err := dec.Uint8() + if err != nil { + return err + } + _tmp2 = &_tmp1 + } + _tmp0.Uint8 = _tmp2 + // Uint8List: + var _tmp6 *byte + if _tmp7, _tmp8, err := dec.Kind(); err != nil { + return err + } else if _tmp8 != 0 || _tmp7 != rlp.List { + _tmp5, err := dec.Uint8() + if err != nil { + return err + } + _tmp6 = &_tmp5 + } + _tmp0.Uint8List = _tmp6 + // Uint32: + var _tmp10 *uint32 + if _tmp11, _tmp12, err := dec.Kind(); err != nil { + return err + } else if _tmp12 != 0 || _tmp11 != rlp.String { + _tmp9, err := dec.Uint32() + if err != nil { + return err + } + _tmp10 = &_tmp9 + } + _tmp0.Uint32 = _tmp10 + // Uint32List: + var _tmp14 *uint32 + if _tmp15, _tmp16, err := dec.Kind(); err != nil { + return err + } else if _tmp16 != 0 || _tmp15 != rlp.List { + _tmp13, err := dec.Uint32() + if err != nil { + return err + } + _tmp14 = &_tmp13 + } + _tmp0.Uint32List = _tmp14 + // Uint64: + var _tmp18 *uint64 + if _tmp19, _tmp20, err := dec.Kind(); err != nil { + return err + } else if _tmp20 != 0 || _tmp19 != rlp.String { + _tmp17, err := dec.Uint64() + if err != nil { + return err + } + _tmp18 = &_tmp17 + } + _tmp0.Uint64 = _tmp18 + // Uint64List: + var _tmp22 *uint64 + if _tmp23, _tmp24, err := dec.Kind(); err != nil { + return err + } else if _tmp24 != 0 || _tmp23 != rlp.List { + _tmp21, err := dec.Uint64() + if err != nil { + return err + } + _tmp22 = &_tmp21 + } + _tmp0.Uint64List = _tmp22 + // String: + var _tmp26 *string + if _tmp27, _tmp28, err := dec.Kind(); err != nil { + return err + } else if _tmp28 != 0 || _tmp27 != rlp.String { + _tmp25, err := dec.String() + if err != nil { + return err + } + _tmp26 = &_tmp25 + } + _tmp0.String = _tmp26 + // StringList: + var _tmp30 *string + if _tmp31, _tmp32, err := dec.Kind(); err != nil { + return err + } else if _tmp32 != 0 || _tmp31 != rlp.List { + _tmp29, err := dec.String() + if err != nil { + return err + } + _tmp30 = &_tmp29 + } + _tmp0.StringList = _tmp30 + // ByteArray: + var _tmp34 *[3]byte + if _tmp35, _tmp36, err := dec.Kind(); err != nil { + return err + } else if _tmp36 != 0 || _tmp35 != rlp.String { + var _tmp33 [3]byte + if err := dec.ReadBytes(_tmp33[:]); err != nil { + return err + } + _tmp34 = &_tmp33 + } + _tmp0.ByteArray = _tmp34 + // ByteArrayList: + var _tmp38 *[3]byte + if _tmp39, _tmp40, err := dec.Kind(); err != nil { + return err + } else if _tmp40 != 0 || _tmp39 != rlp.List { + var _tmp37 [3]byte + if err := dec.ReadBytes(_tmp37[:]); err != nil { + return err + } + _tmp38 = &_tmp37 + } + _tmp0.ByteArrayList = _tmp38 + // ByteSlice: + var _tmp42 *[]byte + if _tmp43, _tmp44, err := dec.Kind(); err != nil { + return err + } else if _tmp44 != 0 || _tmp43 != rlp.String { + _tmp41, err := dec.Bytes() + if err != nil { + return err + } + _tmp42 = &_tmp41 + } + _tmp0.ByteSlice = _tmp42 + // ByteSliceList: + var _tmp46 *[]byte + if _tmp47, _tmp48, err := dec.Kind(); err != nil { + return err + } else if _tmp48 != 0 || _tmp47 != rlp.List { + _tmp45, err := dec.Bytes() + if err != nil { + return err + } + _tmp46 = &_tmp45 + } + _tmp0.ByteSliceList = _tmp46 + // Struct: + var _tmp51 *Aux + if _tmp52, _tmp53, err := dec.Kind(); err != nil { + return err + } else if _tmp53 != 0 || _tmp52 != rlp.List { + var _tmp49 Aux + { + if _, err := dec.List(); err != nil { + return err + } + // A: + _tmp50, err := dec.Uint32() + if err != nil { + return err + } + _tmp49.A = _tmp50 + if err := dec.ListEnd(); err != nil { + return err + } + } + _tmp51 = &_tmp49 + } + _tmp0.Struct = _tmp51 + // StructString: + var _tmp56 *Aux + if _tmp57, _tmp58, err := dec.Kind(); err != nil { + return err + } else if _tmp58 != 0 || _tmp57 != rlp.String { + var _tmp54 Aux + { + if _, err := dec.List(); err != nil { + return err + } + // A: + _tmp55, err := dec.Uint32() + if err != nil { + return err + } + _tmp54.A = _tmp55 + if err := dec.ListEnd(); err != nil { + return err + } + } + _tmp56 = &_tmp54 + } + _tmp0.StructString = _tmp56 + if err := dec.ListEnd(); err != nil { + return err + } + } + *obj = _tmp0 + return nil + } +FAIL +FAIL github.com/XinFinOrg/XDPoSChain/rlp/rlpgen 0.537s +ok github.com/XinFinOrg/XDPoSChain/rpc (cached) +ok github.com/XinFinOrg/XDPoSChain/tests (cached) +? github.com/XinFinOrg/XDPoSChain/tests/fuzzers/bitutil [no test files] +? github.com/XinFinOrg/XDPoSChain/tests/fuzzers/bn256 [no test files] +? github.com/XinFinOrg/XDPoSChain/tests/fuzzers/runtime [no test files] +ok github.com/XinFinOrg/XDPoSChain/trie (cached) +ok github.com/XinFinOrg/XDPoSChain/whisper/mailserver (cached) +? github.com/XinFinOrg/XDPoSChain/whisper/shhclient [no test files] +ok github.com/XinFinOrg/XDPoSChain/whisper/whisperv5 (cached) +ok github.com/XinFinOrg/XDPoSChain/whisper/whisperv6 (cached) +FAIL +util.go:43: exit status 1 +exit status 1 +make: *** [Makefile:48: test] Error 1 diff --git a/tests/state_test.go b/tests/state_test.go index 79a7dc2a1519..5c91a978c520 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -88,6 +88,6 @@ func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) { } else { t.Log("EVM operation log:\n" + buf.String()) } - t.Logf("EVM output: 0x%x", tracer.Output()) + t.Logf("EVM output: %#x", tracer.Output()) t.Logf("EVM error: %v", tracer.Error()) } diff --git a/trie/trie_test.go b/trie/trie_test.go index 202fc76d809e..9311cbae6400 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -422,7 +422,7 @@ func runRandTest(rt randTest) bool { v := tr.Get(step.key) want := values[string(step.key)] if string(v) != want { - rt[i].err = fmt.Errorf("mismatch for key 0x%x, got 0x%x want 0x%x", step.key, v, want) + rt[i].err = fmt.Errorf("mismatch for key %#x, got %#x want %#x", step.key, v, want) } case opCommit: _, rt[i].err = tr.Commit(nil) From 22c54206bdab29c01b87be2087ea9f79d1b036de Mon Sep 17 00:00:00 2001 From: "Mr.P" Date: Wed, 9 Oct 2024 04:28:24 +0300 Subject: [PATCH 012/479] add flag rpc-gascap and set RPCGasCap to 50M (#664) * add gas cap flag * default gas cap to 50m * rpc-gascap --- cmd/XDC/main.go | 1 + cmd/XDC/usage.go | 1 + cmd/utils/flags.go | 5 ++++- eth/ethconfig/config.go | 2 +- eth/peer.go | 2 +- 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go index 525c7bea9343..864e5e1cd087 100644 --- a/cmd/XDC/main.go +++ b/cmd/XDC/main.go @@ -144,6 +144,7 @@ var ( rpcFlags = []cli.Flag{ utils.RPCEnabledFlag, + utils.RPCGlobalGasCapFlag, utils.RPCListenAddrFlag, utils.RPCPortFlag, utils.RPCHttpWriteTimeoutFlag, diff --git a/cmd/XDC/usage.go b/cmd/XDC/usage.go index a49da82bb581..b724155162a5 100644 --- a/cmd/XDC/usage.go +++ b/cmd/XDC/usage.go @@ -144,6 +144,7 @@ var AppHelpFlagGroups = []flagGroup{ Name: "API AND CONSOLE", Flags: []cli.Flag{ utils.RPCEnabledFlag, + utils.RPCGlobalGasCapFlag, utils.RPCListenAddrFlag, utils.RPCPortFlag, utils.RPCHttpWriteTimeoutFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 38ce46222e1f..f96418495472 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -374,7 +374,7 @@ var ( Usage: "Record information useful for VM and contract debugging", } RPCGlobalGasCapFlag = cli.Uint64Flag{ - Name: "rpc.gascap", + Name: "rpc-gascap", Usage: "Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite)", Value: ethconfig.Defaults.RPCGasCap, } @@ -1245,6 +1245,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { if ctx.GlobalIsSet(RPCGlobalTxFeeCap.Name) { cfg.RPCTxFeeCap = ctx.GlobalFloat64(RPCGlobalTxFeeCap.Name) } + if ctx.GlobalIsSet(RPCGlobalGasCapFlag.Name) { + cfg.RPCGasCap = ctx.GlobalUint64(RPCGlobalGasCapFlag.Name) + } if ctx.GlobalIsSet(ExtraDataFlag.Name) { cfg.ExtraData = []byte(ctx.GlobalString(ExtraDataFlag.Name)) } diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 73cc4e7cddbe..efdd2197d92e 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -69,7 +69,7 @@ var Defaults = Config{ GasPrice: big.NewInt(0.25 * params.Shannon), TxPool: core.DefaultTxPoolConfig, - RPCGasCap: 25000000, + RPCGasCap: 50000000, GPO: FullNodeGPO, RPCTxFeeCap: 1, // 1 ether } diff --git a/eth/peer.go b/eth/peer.go index aa846a797e96..8035f2a9f06b 100644 --- a/eth/peer.go +++ b/eth/peer.go @@ -77,7 +77,7 @@ type peer struct { knownVote mapset.Set // Set of BFT Vote known to be known by this peer knownTimeout mapset.Set // Set of BFT timeout known to be known by this peer - knownSyncInfo mapset.Set // Set of BFT Sync Info known to be known by this peer` + knownSyncInfo mapset.Set // Set of BFT Sync Info known to be known by this peer } func newPeer(version int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer { From 693190e5b95a9c5914268199c02ad67e0e11ab9e Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 16 Oct 2024 11:00:58 +0800 Subject: [PATCH 013/479] core: fix wrong blockHash for eth_getLogs (#650) --- core/database_util.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/database_util.go b/core/database_util.go index 9d48ffec6ce2..e65ac0ff8e2c 100644 --- a/core/database_util.go +++ b/core/database_util.go @@ -262,6 +262,10 @@ func GetBlockReceipts(db DatabaseReader, hash common.Hash, number uint64) types. receipts[i].BlockHash = hash receipts[i].BlockNumber = big.NewInt(0).SetUint64(number) receipts[i].TransactionIndex = uint(i) + for _, log := range receipts[i].Logs { + // set BlockHash to fix #650 + log.BlockHash = hash + } } return receipts } From b653dbc60cc9c1c0028e669c30afdb1861f1cf1f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 26 Sep 2024 11:01:54 +0800 Subject: [PATCH 014/479] core/vm, protocol_params: implement eip-2565 modexp repricing (#21607) --- core/vm/contracts.go | 99 +++++++++++--- core/vm/contracts_test.go | 65 +++++++++- core/vm/evm.go | 2 + .../testdata/precompiles/modexp_eip2565.json | 121 ++++++++++++++++++ params/protocol_params.go | 1 - 5 files changed, 263 insertions(+), 25 deletions(-) create mode 100644 core/vm/testdata/precompiles/modexp_eip2565.json diff --git a/core/vm/contracts.go b/core/vm/contracts.go index ba7aa1547b1b..bdf15ab785e3 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -58,7 +58,7 @@ var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{2}): &sha256hash{}, common.BytesToAddress([]byte{3}): &ripemd160hash{}, common.BytesToAddress([]byte{4}): &dataCopy{}, - common.BytesToAddress([]byte{5}): &bigModExp{}, + common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false}, common.BytesToAddress([]byte{6}): &bn256AddByzantium{}, common.BytesToAddress([]byte{7}): &bn256ScalarMulByzantium{}, common.BytesToAddress([]byte{8}): &bn256PairingByzantium{}, @@ -75,7 +75,7 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{2}): &sha256hash{}, common.BytesToAddress([]byte{3}): &ripemd160hash{}, common.BytesToAddress([]byte{4}): &dataCopy{}, - common.BytesToAddress([]byte{5}): &bigModExp{}, + common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false}, common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, @@ -91,7 +91,19 @@ var PrecompiledContractsXDCv2 = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{2}): &sha256hash{}, common.BytesToAddress([]byte{3}): &ripemd160hash{}, common.BytesToAddress([]byte{4}): &dataCopy{}, - common.BytesToAddress([]byte{5}): &bigModExp{}, + common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false}, + common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, + common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, + common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, + common.BytesToAddress([]byte{9}): &blake2F{}, +} + +var PrecompiledContractsEIP1559 = map[common.Address]PrecompiledContract{ + common.BytesToAddress([]byte{1}): &ecrecover{}, + common.BytesToAddress([]byte{2}): &sha256hash{}, + common.BytesToAddress([]byte{3}): &ripemd160hash{}, + common.BytesToAddress([]byte{4}): &dataCopy{}, + common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true}, common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, @@ -99,6 +111,7 @@ var PrecompiledContractsXDCv2 = map[common.Address]PrecompiledContract{ } var ( + PrecompiledAddressesEIP1559 []common.Address PrecompiledAddressesXDCv2 []common.Address PrecompiledAddressesIstanbul []common.Address PrecompiledAddressesByzantium []common.Address @@ -118,11 +131,16 @@ func init() { for k := range PrecompiledContractsXDCv2 { PrecompiledAddressesXDCv2 = append(PrecompiledAddressesXDCv2, k) } + for k := range PrecompiledContractsEIP1559 { + PrecompiledAddressesEIP1559 = append(PrecompiledAddressesEIP1559, k) + } } // ActivePrecompiles returns the precompiles enabled with the current configuration. func ActivePrecompiles(rules params.Rules) []common.Address { switch { + case rules.IsEIP1559: + return PrecompiledAddressesEIP1559 case rules.IsXDCxDisable: return PrecompiledAddressesXDCv2 case rules.IsIstanbul: @@ -244,14 +262,19 @@ func (c *dataCopy) Run(in []byte) ([]byte, error) { } // bigModExp implements a native big integer exponential modular operation. -type bigModExp struct{} +type bigModExp struct { + eip2565 bool +} var ( big0 = big.NewInt(0) big1 = big.NewInt(1) + big3 = big.NewInt(3) big4 = big.NewInt(4) + big7 = big.NewInt(7) big8 = big.NewInt(8) big16 = big.NewInt(16) + big20 = big.NewInt(20) big32 = big.NewInt(32) big64 = big.NewInt(64) big96 = big.NewInt(96) @@ -261,6 +284,35 @@ var ( big199680 = big.NewInt(199680) ) +// modexpMultComplexity implements bigModexp multComplexity formula, as defined in EIP-198 +// +// def mult_complexity(x): +// +// if x <= 64: return x ** 2 +// elif x <= 1024: return x ** 2 // 4 + 96 * x - 3072 +// else: return x ** 2 // 16 + 480 * x - 199680 +// +// where is x is max(length_of_MODULUS, length_of_BASE) +func modexpMultComplexity(x *big.Int) *big.Int { + switch { + case x.Cmp(big64) <= 0: + x.Mul(x, x) // x ** 2 + case x.Cmp(big1024) <= 0: + // (x ** 2 // 4 ) + ( 96 * x - 3072) + x = new(big.Int).Add( + new(big.Int).Div(new(big.Int).Mul(x, x), big4), + new(big.Int).Sub(new(big.Int).Mul(big96, x), big3072), + ) + default: + // (x ** 2 // 16) + (480 * x - 199680) + x = new(big.Int).Add( + new(big.Int).Div(new(big.Int).Mul(x, x), big16), + new(big.Int).Sub(new(big.Int).Mul(big480, x), big199680), + ) + } + return x +} + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bigModExp) RequiredGas(input []byte) uint64 { var ( @@ -295,25 +347,36 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { adjExpLen.Mul(big8, adjExpLen) } adjExpLen.Add(adjExpLen, big.NewInt(int64(msb))) - // Calculate the gas cost of the operation gas := new(big.Int).Set(math.BigMax(modLen, baseLen)) - switch { - case gas.Cmp(big64) <= 0: + if c.eip2565 { + // EIP-2565 has three changes + // 1. Different multComplexity (inlined here) + // in EIP-2565 (https://eips.ethereum.org/EIPS/eip-2565): + // + // def mult_complexity(x): + // ceiling(x/8)^2 + // + // where is x is max(length_of_MODULUS, length_of_BASE) + gas = gas.Add(gas, big7) + gas = gas.Div(gas, big8) gas.Mul(gas, gas) - case gas.Cmp(big1024) <= 0: - gas = new(big.Int).Add( - new(big.Int).Div(new(big.Int).Mul(gas, gas), big4), - new(big.Int).Sub(new(big.Int).Mul(big96, gas), big3072), - ) - default: - gas = new(big.Int).Add( - new(big.Int).Div(new(big.Int).Mul(gas, gas), big16), - new(big.Int).Sub(new(big.Int).Mul(big480, gas), big199680), - ) + + gas.Mul(gas, math.BigMax(adjExpLen, big1)) + // 2. Different divisor (`GQUADDIVISOR`) (3) + gas.Div(gas, big3) + if gas.BitLen() > 64 { + return math.MaxUint64 + } + // 3. Minimum price of 200 gas + if gas.Uint64() < 200 { + return 200 + } + return gas.Uint64() } + gas = modexpMultComplexity(gas) gas.Mul(gas, math.BigMax(adjExpLen, big1)) - gas.Div(gas, new(big.Int).SetUint64(params.ModExpQuadCoeffDiv)) + gas.Div(gas, big20) if gas.BitLen() > 64 { return math.MaxUint64 diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index ed915683c7cb..1a03f93fc223 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -18,8 +18,10 @@ package vm import ( "bytes" + "encoding/json" "fmt" "math/big" + "os" "reflect" "testing" @@ -48,6 +50,25 @@ type precompiledFailureTest struct { name string } +// allPrecompiles does not map to the actual set of precompiles, as it also contains +// repriced versions of precompiles at certain slots +var allPrecompiles = map[common.Address]PrecompiledContract{ + common.BytesToAddress([]byte{1}): &ecrecover{}, + common.BytesToAddress([]byte{2}): &sha256hash{}, + common.BytesToAddress([]byte{3}): &ripemd160hash{}, + common.BytesToAddress([]byte{4}): &dataCopy{}, + common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false}, + common.BytesToAddress([]byte{0xf5}): &bigModExp{eip2565: true}, + common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, + common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, + common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, + common.BytesToAddress([]byte{9}): &blake2F{}, + common.BytesToAddress([]byte{30}): &ringSignatureVerifier{}, + common.BytesToAddress([]byte{40}): &bulletproofVerifier{}, + common.BytesToAddress([]byte{41}): &XDCxLastPrice{}, + common.BytesToAddress([]byte{42}): &XDCxEpochPrice{}, +} + // modexpTests are the test and benchmark data for the modexp precompiled contract. var modexpTests = []precompiledTest{ { @@ -490,7 +511,7 @@ var blake2FTests = []precompiledTest{ } func testPrecompiled(addr string, test precompiledTest, t *testing.T) { - p := PrecompiledContractsIstanbul[common.HexToAddress(addr)] + p := allPrecompiles[common.HexToAddress(addr)] in := common.Hex2Bytes(test.input) gas := p.RequiredGas(in) t.Run(fmt.Sprintf("%s-Gas=%d", test.name, gas), func(t *testing.T) { @@ -509,7 +530,7 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) { func testXDCxPrecompiled(addr string, test precompiledTest, t *testing.T) { contractAddr := common.HexToAddress(addr) - p := PrecompiledContractsByzantium[contractAddr] + p := allPrecompiles[contractAddr] in := common.Hex2Bytes(test.input) gas := p.RequiredGas(in) t.Run(fmt.Sprintf("%s-Gas=%d", test.name, gas), func(t *testing.T) { @@ -523,7 +544,7 @@ func testXDCxPrecompiled(addr string, test precompiledTest, t *testing.T) { func testPrecompiledWithEmptyTradingState(addr string, test precompiledTest, t *testing.T) { contractAddr := common.HexToAddress(addr) - p := PrecompiledContractsByzantium[contractAddr] + p := allPrecompiles[contractAddr] in := common.Hex2Bytes(test.input) gas := p.RequiredGas(in) t.Run(fmt.Sprintf("testPrecompiledWithEmptyTradingState-%s-Gas=%d", test.name, gas), func(t *testing.T) { @@ -536,7 +557,7 @@ func testPrecompiledWithEmptyTradingState(addr string, test precompiledTest, t * } func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) { - p := PrecompiledContractsIstanbul[common.HexToAddress(addr)] + p := allPrecompiles[common.HexToAddress(addr)] in := common.Hex2Bytes(test.input) gas := p.RequiredGas(in) - 1 @@ -554,7 +575,7 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) { } func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing.T) { - p := PrecompiledContractsIstanbul[common.HexToAddress(addr)] + p := allPrecompiles[common.HexToAddress(addr)] in := common.Hex2Bytes(test.input) gas := p.RequiredGas(in) t.Run(test.name, func(t *testing.T) { @@ -574,7 +595,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { if test.noBenchmark { return } - p := PrecompiledContractsIstanbul[common.HexToAddress(addr)] + p := allPrecompiles[common.HexToAddress(addr)] in := common.Hex2Bytes(test.input) reqGas := p.RequiredGas(in) @@ -657,6 +678,9 @@ func BenchmarkPrecompiledModExp(bench *testing.B) { } } +func TestPrecompiledModExpEip2565(t *testing.T) { testJson("modexp_eip2565", "f5", t) } +func BenchmarkPrecompiledModExpEip2565(b *testing.B) { benchJson("modexp_eip2565", "f5", b) } + // Tests the sample inputs from the elliptic curve addition EIP 213. func TestPrecompiledBn256Add(t *testing.T) { for _, test := range bn256AddTests { @@ -800,5 +824,34 @@ func TestPrecompiledEcrecover(t *testing.T) { for _, test := range ecRecoverTests { testPrecompiled("01", test, t) } +} +func testJson(name, addr string, t *testing.T) { + tests, err := loadJson(name) + if err != nil { + t.Fatal(err) + } + for _, test := range tests { + testPrecompiled(addr, test, t) + } +} + +func benchJson(name, addr string, b *testing.B) { + tests, err := loadJson(name) + if err != nil { + b.Fatal(err) + } + for _, test := range tests { + benchmarkPrecompiled(addr, test, b) + } +} + +func loadJson(name string) ([]precompiledTest, error) { + data, err := os.ReadFile(fmt.Sprintf("testdata/precompiles/%v.json", name)) + if err != nil { + return nil, err + } + var testcases []precompiledTest + err = json.Unmarshal(data, &testcases) + return testcases, err } diff --git a/core/vm/evm.go b/core/vm/evm.go index b34fe5cfb99f..c06706a44c8a 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -46,6 +46,8 @@ type ( func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { var precompiles map[common.Address]PrecompiledContract switch { + case evm.chainRules.IsEIP1559: + precompiles = PrecompiledContractsEIP1559 case evm.chainRules.IsXDCxDisable: precompiles = PrecompiledContractsXDCv2 case evm.chainRules.IsIstanbul: diff --git a/core/vm/testdata/precompiles/modexp_eip2565.json b/core/vm/testdata/precompiles/modexp_eip2565.json new file mode 100644 index 000000000000..c55441439ebb --- /dev/null +++ b/core/vm/testdata/precompiles/modexp_eip2565.json @@ -0,0 +1,121 @@ +[ + { + "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002003fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "eip_example1", + "Gas": 1360, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "Expected": "0000000000000000000000000000000000000000000000000000000000000000", + "Name": "eip_example2", + "Gas": 1360, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb502fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", + "Expected": "60008f1614cc01dcfb6bfb09c625cf90b47d4468db81b5f8b7a39d42f332eab9b2da8f2d95311648a8f243f4bb13cfb3d8f7f2a3c014122ebb3ed41b02783adc", + "Name": "nagydani-1-square", + "Gas": 200, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb503fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", + "Expected": "4834a46ba565db27903b1c720c9d593e84e4cbd6ad2e64b31885d944f68cd801f92225a8961c952ddf2797fa4701b330c85c4b363798100b921a1a22a46a7fec", + "Name": "nagydani-1-qube", + "Gas": 200, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb5010001fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", + "Expected": "c36d804180c35d4426b57b50c5bfcca5c01856d104564cd513b461d3c8b8409128a5573e416d0ebe38f5f736766d9dc27143e4da981dfa4d67f7dc474cbee6d2", + "Name": "nagydani-1-pow0x10001", + "Gas": 341, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5102e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + "Expected": "981dd99c3b113fae3e3eaa9435c0dc96779a23c12a53d1084b4f67b0b053a27560f627b873e3f16ad78f28c94f14b6392def26e4d8896c5e3c984e50fa0b3aa44f1da78b913187c6128baa9340b1e9c9a0fd02cb78885e72576da4a8f7e5a113e173a7a2889fde9d407bd9f06eb05bc8fc7b4229377a32941a02bf4edcc06d70", + "Name": "nagydani-2-square", + "Gas": 200, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5103e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + "Expected": "d89ceb68c32da4f6364978d62aaa40d7b09b59ec61eb3c0159c87ec3a91037f7dc6967594e530a69d049b64adfa39c8fa208ea970cfe4b7bcd359d345744405afe1cbf761647e32b3184c7fbe87cee8c6c7ff3b378faba6c68b83b6889cb40f1603ee68c56b4c03d48c595c826c041112dc941878f8c5be828154afd4a16311f", + "Name": "nagydani-2-qube", + "Gas": 200, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf51010001e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + "Expected": "ad85e8ef13fd1dd46eae44af8b91ad1ccae5b7a1c92944f92a19f21b0b658139e0cabe9c1f679507c2de354bf2c91ebd965d1e633978a830d517d2f6f8dd5fd58065d58559de7e2334a878f8ec6992d9b9e77430d4764e863d77c0f87beede8f2f7f2ab2e7222f85cc9d98b8467f4bb72e87ef2882423ebdb6daf02dddac6db2", + "Name": "nagydani-2-pow0x10001", + "Gas": 1365, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb02d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", + "Expected": "affc7507ea6d84751ec6b3f0d7b99dbcc263f33330e450d1b3ff0bc3d0874320bf4edd57debd587306988157958cb3cfd369cc0c9c198706f635c9e0f15d047df5cb44d03e2727f26b083c4ad8485080e1293f171c1ed52aef5993a5815c35108e848c951cf1e334490b4a539a139e57b68f44fee583306f5b85ffa57206b3ee5660458858534e5386b9584af3c7f67806e84c189d695e5eb96e1272d06ec2df5dc5fabc6e94b793718c60c36be0a4d031fc84cd658aa72294b2e16fc240aef70cb9e591248e38bd49c5a554d1afa01f38dab72733092f7555334bbef6c8c430119840492380aa95fa025dcf699f0a39669d812b0c6946b6091e6e235337b6f8", + "Name": "nagydani-3-square", + "Gas": 341, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb03d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", + "Expected": "1b280ecd6a6bf906b806d527c2a831e23b238f89da48449003a88ac3ac7150d6a5e9e6b3be4054c7da11dd1e470ec29a606f5115801b5bf53bc1900271d7c3ff3cd5ed790d1c219a9800437a689f2388ba1a11d68f6a8e5b74e9a3b1fac6ee85fc6afbac599f93c391f5dc82a759e3c6c0ab45ce3f5d25d9b0c1bf94cf701ea6466fc9a478dacc5754e593172b5111eeba88557048bceae401337cd4c1182ad9f700852bc8c99933a193f0b94cf1aedbefc48be3bc93ef5cb276d7c2d5462ac8bb0c8fe8923a1db2afe1c6b90d59c534994a6a633f0ead1d638fdc293486bb634ff2c8ec9e7297c04241a61c37e3ae95b11d53343d4ba2b4cc33d2cfa7eb705e", + "Name": "nagydani-3-qube", + "Gas": 341, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb010001d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", + "Expected": "37843d7c67920b5f177372fa56e2a09117df585f81df8b300fba245b1175f488c99476019857198ed459ed8d9799c377330e49f4180c4bf8e8f66240c64f65ede93d601f957b95b83efdee1e1bfde74169ff77002eaf078c71815a9220c80b2e3b3ff22c2f358111d816ebf83c2999026b6de50bfc711ff68705d2f40b753424aefc9f70f08d908b5a20276ad613b4ab4309a3ea72f0c17ea9df6b3367d44fb3acab11c333909e02e81ea2ed404a712d3ea96bba87461720e2d98723e7acd0520ac1a5212dbedcd8dc0c1abf61d4719e319ff4758a774790b8d463cdfe131d1b2dcfee52d002694e98e720cb6ae7ccea353bc503269ba35f0f63bf8d7b672a76", + "Name": "nagydani-3-pow0x10001", + "Gas": 5461, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8102df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + "Expected": "8a5aea5f50dcc03dc7a7a272b5aeebc040554dbc1ffe36753c4fc75f7ed5f6c2cc0de3a922bf96c78bf0643a73025ad21f45a4a5cadd717612c511ab2bff1190fe5f1ae05ba9f8fe3624de1de2a817da6072ddcdb933b50216811dbe6a9ca79d3a3c6b3a476b079fd0d05f04fb154e2dd3e5cb83b148a006f2bcbf0042efb2ae7b916ea81b27aac25c3bf9a8b6d35440062ad8eae34a83f3ffa2cc7b40346b62174a4422584f72f95316f6b2bee9ff232ba9739301c97c99a9ded26c45d72676eb856ad6ecc81d36a6de36d7f9dafafee11baa43a4b0d5e4ecffa7b9b7dcefd58c397dd373e6db4acd2b2c02717712e6289bed7c813b670c4a0c6735aa7f3b0f1ce556eae9fcc94b501b2c8781ba50a8c6220e8246371c3c7359fe4ef9da786ca7d98256754ca4e496be0a9174bedbecb384bdf470779186d6a833f068d2838a88d90ef3ad48ff963b67c39cc5a3ee123baf7bf3125f64e77af7f30e105d72c4b9b5b237ed251e4c122c6d8c1405e736299c3afd6db16a28c6a9cfa68241e53de4cd388271fe534a6a9b0dbea6171d170db1b89858468885d08fecbd54c8e471c3e25d48e97ba450b96d0d87e00ac732aaa0d3ce4309c1064bd8a4c0808a97e0143e43a24cfa847635125cd41c13e0574487963e9d725c01375db99c31da67b4cf65eff555f0c0ac416c727ff8d438ad7c42030551d68c2e7adda0abb1ca7c10", + "Name": "nagydani-4-square", + "Gas": 1365, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8103df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + "Expected": "5a2664252aba2d6e19d9600da582cdd1f09d7a890ac48e6b8da15ae7c6ff1856fc67a841ac2314d283ffa3ca81a0ecf7c27d89ef91a5a893297928f5da0245c99645676b481b7e20a566ee6a4f2481942bee191deec5544600bb2441fd0fb19e2ee7d801ad8911c6b7750affec367a4b29a22942c0f5f4744a4e77a8b654da2a82571037099e9c6d930794efe5cdca73c7b6c0844e386bdca8ea01b3d7807146bb81365e2cdc6475f8c23e0ff84463126189dc9789f72bbce2e3d2d114d728a272f1345122de23df54c922ec7a16e5c2a8f84da8871482bd258c20a7c09bbcd64c7a96a51029bbfe848736a6ba7bf9d931a9b7de0bcaf3635034d4958b20ae9ab3a95a147b0421dd5f7ebff46c971010ebfc4adbbe0ad94d5498c853e7142c450d8c71de4b2f84edbf8acd2e16d00c8115b150b1c30e553dbb82635e781379fe2a56360420ff7e9f70cc64c00aba7e26ed13c7c19622865ae07248daced36416080f35f8cc157a857ed70ea4f347f17d1bee80fa038abd6e39b1ba06b97264388b21364f7c56e192d4b62d9b161405f32ab1e2594e86243e56fcf2cb30d21adef15b9940f91af681da24328c883d892670c6aa47940867a81830a82b82716895db810df1b834640abefb7db2092dd92912cb9a735175bc447be40a503cf22dfe565b4ed7a3293ca0dfd63a507430b323ee248ec82e843b673c97ad730728cebc", + "Name": "nagydani-4-qube", + "Gas": 1365, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b81010001df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + "Expected": "bed8b970c4a34849fc6926b08e40e20b21c15ed68d18f228904878d4370b56322d0da5789da0318768a374758e6375bfe4641fca5285ec7171828922160f48f5ca7efbfee4d5148612c38ad683ae4e3c3a053d2b7c098cf2b34f2cb19146eadd53c86b2d7ccf3d83b2c370bfb840913ee3879b1057a6b4e07e110b6bcd5e958bc71a14798c91d518cc70abee264b0d25a4110962a764b364ac0b0dd1ee8abc8426d775ec0f22b7e47b32576afaf1b5a48f64573ed1c5c29f50ab412188d9685307323d990802b81dacc06c6e05a1e901830ba9fcc67688dc29c5e27bde0a6e845ca925f5454b6fb3747edfaa2a5820838fb759eadf57f7cb5cec57fc213ddd8a4298fa079c3c0f472b07fb15aa6a7f0a3780bd296ff6a62e58ef443870b02260bd4fd2bbc98255674b8e1f1f9f8d33c7170b0ebbea4523b695911abbf26e41885344823bd0587115fdd83b721a4e8457a31c9a84b3d3520a07e0e35df7f48e5a9d534d0ec7feef1ff74de6a11e7f93eab95175b6ce22c68d78a642ad642837897ec11349205d8593ac19300207572c38d29ca5dfa03bc14cdbc32153c80e5cc3e739403d34c75915e49beb43094cc6dcafb3665b305ddec9286934ae66ec6b777ca528728c851318eb0f207b39f1caaf96db6eeead6b55ed08f451939314577d42bcc9f97c0b52d0234f88fd07e4c1d7780fdebc025cfffcb572cb27a8c33963", + "Name": "nagydani-4-pow0x10001", + "Gas": 21845, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf02e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + "Expected": "d61fe4e3f32ac260915b5b03b78a86d11bfc41d973fce5b0cc59035cf8289a8a2e3878ea15fa46565b0d806e2f85b53873ea20ed653869b688adf83f3ef444535bf91598ff7e80f334fb782539b92f39f55310cc4b35349ab7b278346eda9bc37c0d8acd3557fae38197f412f8d9e57ce6a76b7205c23564cab06e5615be7c6f05c3d05ec690cba91da5e89d55b152ff8dd2157dc5458190025cf94b1ad98f7cbe64e9482faba95e6b33844afc640892872b44a9932096508f4a782a4805323808f23e54b6ff9b841dbfa87db3505ae4f687972c18ea0f0d0af89d36c1c2a5b14560c153c3fee406f5cf15cfd1c0bb45d767426d465f2f14c158495069d0c5955a00150707862ecaae30624ebacdd8ac33e4e6aab3ff90b6ba445a84689386b9e945d01823a65874444316e83767290fcff630d2477f49d5d8ffdd200e08ee1274270f86ed14c687895f6caf5ce528bd970c20d2408a9ba66216324c6a011ac4999098362dbd98a038129a2d40c8da6ab88318aa3046cb660327cc44236d9e5d2163bd0959062195c51ed93d0088b6f92051fc99050ece2538749165976233697ab4b610385366e5ce0b02ad6b61c168ecfbedcdf74278a38de340fd7a5fead8e588e294795f9b011e2e60377a89e25c90e145397cdeabc60fd32444a6b7642a611a83c464d8b8976666351b4865c37b02e6dc21dbcdf5f930341707b618cc0f03c3122646b3385c9df9f2ec730eec9d49e7dfc9153b6e6289da8c4f0ebea9ccc1b751948e3bb7171c9e4d57423b0eeeb79095c030cb52677b3f7e0b45c30f645391f3f9c957afa549c4e0b2465b03c67993cd200b1af01035962edbc4c9e89b31c82ac121987d6529dafdeef67a132dc04b6dc68e77f22862040b75e2ceb9ff16da0fca534e6db7bd12fa7b7f51b6c08c1e23dfcdb7acbd2da0b51c87ffbced065a612e9b1c8bba9b7e2d8d7a2f04fcc4aaf355b60d764879a76b5e16762d5f2f55d585d0c8e82df6940960cddfb72c91dfa71f6b4e1c6ca25dfc39a878e998a663c04fe29d5e83b9586d047b4d7ff70a9f0d44f127e7d741685ca75f11629128d916a0ffef4be586a30c4b70389cc746e84ebf177c01ee8a4511cfbb9d1ecf7f7b33c7dd8177896e10bbc82f838dcd6db7ac67de62bf46b6a640fb580c5d1d2708f3862e3d2b645d0d18e49ef088053e3a220adc0e033c2afcfe61c90e32151152eb3caaf746c5e377d541cafc6cbb0cc0fa48b5caf1728f2e1957f5addfc234f1a9d89e40d49356c9172d0561a695fce6dab1d412321bbf407f63766ffd7b6b3d79bcfa07991c5a9709849c1008689e3b47c50d613980bec239fb64185249d055b30375ccb4354d71fe4d05648fbf6c80634dfc3575f2f24abb714c1e4c95e8896763bf4316e954c7ad19e5780ab7a040ca6fb9271f90a8b22ae738daf6cb", + "Name": "nagydani-5-square", + "Gas": 5461, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf03e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + "Expected": "5f9c70ec884926a89461056ad20ac4c30155e817f807e4d3f5bb743d789c83386762435c3627773fa77da5144451f2a8aad8adba88e0b669f5377c5e9bad70e45c86fe952b613f015a9953b8a5de5eaee4566acf98d41e327d93a35bd5cef4607d025e58951167957df4ff9b1627649d3943805472e5e293d3efb687cfd1e503faafeb2840a3e3b3f85d016051a58e1c9498aab72e63b748d834b31eb05d85dcde65e27834e266b85c75cc4ec0135135e0601cb93eeeb6e0010c8ceb65c4c319623c5e573a2c8c9fbbf7df68a930beb412d3f4dfd146175484f45d7afaa0d2e60684af9b34730f7c8438465ad3e1d0c3237336722f2aa51095bd5759f4b8ab4dda111b684aa3dac62a761722e7ae43495b7709933512c81c4e3c9133a51f7ce9f2b51fcec064f65779666960b4e45df3900f54311f5613e8012dd1b8efd359eda31a778264c72aa8bb419d862734d769076bce2810011989a45374e5c5d8729fec21427f0bf397eacbb4220f603cf463a4b0c94efd858ffd9768cd60d6ce68d755e0fbad007ce5c2223d70c7018345a102e4ab3c60a13a9e7794303156d4c2063e919f2153c13961fb324c80b240742f47773a7a8e25b3e3fb19b00ce839346c6eb3c732fbc6b888df0b1fe0a3d07b053a2e9402c267b2d62f794d8a2840526e3ade15ce2264496ccd7519571dfde47f7a4bb16292241c20b2be59f3f8fb4f6383f232d838c5a22d8c95b6834d9d2ca493f5a505ebe8899503b0e8f9b19e6e2dd81c1628b80016d02097e0134de51054c4e7674824d4d758760fc52377d2cad145e259aa2ffaf54139e1a66b1e0c1c191e32ac59474c6b526f5b3ba07d3e5ec286eddf531fcd5292869be58c9f22ef91026159f7cf9d05ef66b4299f4da48cc1635bf2243051d342d378a22c83390553e873713c0454ce5f3234397111ac3fe3207b86f0ed9fc025c81903e1748103692074f83824fda6341be4f95ff00b0a9a208c267e12fa01825054cc0513629bf3dbb56dc5b90d4316f87654a8be18227978ea0a8a522760cad620d0d14fd38920fb7321314062914275a5f99f677145a6979b156bd82ecd36f23f8e1273cc2759ecc0b2c69d94dad5211d1bed939dd87ed9e07b91d49713a6e16ade0a98aea789f04994e318e4ff2c8a188cd8d43aeb52c6daa3bc29b4af50ea82a247c5cd67b573b34cbadcc0a376d3bbd530d50367b42705d870f2e27a8197ef46070528bfe408360faa2ebb8bf76e9f388572842bcb119f4d84ee34ae31f5cc594f23705a49197b181fb78ed1ec99499c690f843a4d0cf2e226d118e9372271054fbabdcc5c92ae9fefaef0589cd0e722eaf30c1703ec4289c7fd81beaa8a455ccee5298e31e2080c10c366a6fcf56f7d13582ad0bcad037c612b710fc595b70fbefaaca23623b60c6c39b11beb8e5843b6b3dac60f", + "Name": "nagydani-5-qube", + "Gas": 5461, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf010001e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + "Expected": "5a0eb2bdf0ac1cae8e586689fa16cd4b07dfdedaec8a110ea1fdb059dd5253231b6132987598dfc6e11f86780428982d50cf68f67ae452622c3b336b537ef3298ca645e8f89ee39a26758206a5a3f6409afc709582f95274b57b71fae5c6b74619ae6f089a5393c5b79235d9caf699d23d88fb873f78379690ad8405e34c19f5257d596580c7a6a7206a3712825afe630c76b31cdb4a23e7f0632e10f14f4e282c81a66451a26f8df2a352b5b9f607a7198449d1b926e27036810368e691a74b91c61afa73d9d3b99453e7c8b50fd4f09c039a2f2feb5c419206694c31b92df1d9586140cb3417b38d0c503c7b508cc2ed12e813a1c795e9829eb39ee78eeaf360a169b491a1d4e419574e712402de9d48d54c1ae5e03739b7156615e8267e1fb0a897f067afd11fb33f6e24182d7aaaaa18fe5bc1982f20d6b871e5a398f0f6f718181d31ec225cfa9a0a70124ed9a70031bdf0c1c7829f708b6e17d50419ef361cf77d99c85f44607186c8d683106b8bd38a49b5d0fb503b397a83388c5678dcfcc737499d84512690701ed621a6f0172aecf037184ddf0f2453e4053024018e5ab2e30d6d5363b56e8b41509317c99042f517247474ab3abc848e00a07f69c254f46f2a05cf6ed84e5cc906a518fdcfdf2c61ce731f24c5264f1a25fc04934dc28aec112134dd523f70115074ca34e3807aa4cb925147f3a0ce152d323bd8c675ace446d0fd1ae30c4b57f0eb2c23884bc18f0964c0114796c5b6d080c3d89175665fbf63a6381a6a9da39ad070b645c8bb1779506da14439a9f5b5d481954764ea114fac688930bc68534d403cff4210673b6a6ff7ae416b7cd41404c3d3f282fcd193b86d0f54d0006c2a503b40d5c3930da980565b8f9630e9493a79d1c03e74e5f93ac8e4dc1a901ec5e3b3e57049124c7b72ea345aa359e782285d9e6a5c144a378111dd02c40855ff9c2be9b48425cb0b2fd62dc8678fd151121cf26a65e917d65d8e0dacfae108eb5508b601fb8ffa370be1f9a8b749a2d12eeab81f41079de87e2d777994fa4d28188c579ad327f9957fb7bdecec5c680844dd43cb57cf87aeb763c003e65011f73f8c63442df39a92b946a6bd968a1c1e4d5fa7d88476a68bd8e20e5b70a99259c7d3f85fb1b65cd2e93972e6264e74ebf289b8b6979b9b68a85cd5b360c1987f87235c3c845d62489e33acf85d53fa3561fe3a3aee18924588d9c6eba4edb7a4d106b31173e42929f6f0c48c80ce6a72d54eca7c0fe870068b7a7c89c63cdda593f5b32d3cb4ea8a32c39f00ab449155757172d66763ed9527019d6de6c9f2416aa6203f4d11c9ebee1e1d3845099e55504446448027212616167eb36035726daa7698b075286f5379cd3e93cb3e0cf4f9cb8d017facbb5550ed32d5ec5400ae57e47e2bf78d1eaeff9480cc765ceff39db500", + "Name": "nagydani-5-pow0x10001", + "Gas": 87381, + "NoBenchmark": false + } +] \ No newline at end of file diff --git a/params/protocol_params.go b/params/protocol_params.go index b8cd64f72346..d5fba6663eba 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -78,7 +78,6 @@ const ( Ripemd160PerWordGas uint64 = 120 // Per-word price for a RIPEMD160 operation IdentityBaseGas uint64 = 15 // Base price for a data copy operation IdentityPerWordGas uint64 = 3 // Per-work price for a data copy operation - ModExpQuadCoeffDiv uint64 = 20 // Divisor for the quadratic particle of the big int modular exponentiation Bn256AddGas uint64 = 500 // Gas needed for an elliptic curve addition Bn256ScalarMulGas uint64 = 40000 // Gas needed for an elliptic curve scalar multiplication Bn256PairingBaseGas uint64 = 100000 // Base price for an elliptic curve pairing check From c87b7c31359be4c4b0d7c7a5a5a1862973a8aec2 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 26 Sep 2024 13:09:15 +0800 Subject: [PATCH 015/479] common/math: optimized modexp (#25525) --- common/math/modexp.go | 82 +++++++++++++++++++++++++ core/vm/contracts.go | 11 +++- tests/fuzzers/modexp/modexp-fuzzer.go | 86 +++++++++++++++++++++++++++ 3 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 common/math/modexp.go create mode 100644 tests/fuzzers/modexp/modexp-fuzzer.go diff --git a/common/math/modexp.go b/common/math/modexp.go new file mode 100644 index 000000000000..02ef755574c7 --- /dev/null +++ b/common/math/modexp.go @@ -0,0 +1,82 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +import ( + "math/big" + "math/bits" + + "github.com/XinFinOrg/XDPoSChain/common" +) + +// FastExp is semantically equivalent to x.Exp(x,y, m), but is faster for even +// modulus. +func FastExp(x, y, m *big.Int) *big.Int { + // Split m = m1 × m2 where m1 = 2ⁿ + n := m.TrailingZeroBits() + m1 := new(big.Int).Lsh(common.Big1, n) + mask := new(big.Int).Sub(m1, common.Big1) + m2 := new(big.Int).Rsh(m, n) + + // We want z = x**y mod m. + // z1 = x**y mod m1 = (x**y mod m) mod m1 = z mod m1 + // z2 = x**y mod m2 = (x**y mod m) mod m2 = z mod m2 + z1 := fastExpPow2(x, y, mask) + z2 := new(big.Int).Exp(x, y, m2) + + // Reconstruct z from z1, z2 using CRT, using algorithm from paper, + // which uses only a single modInverse. + // p = (z1 - z2) * m2⁻¹ (mod m1) + // z = z2 + p * m2 + z := new(big.Int).Set(z2) + + // Compute (z1 - z2) mod m1 [m1 == 2**n] into z1. + z1 = z1.And(z1, mask) + z2 = z2.And(z2, mask) + z1 = z1.Sub(z1, z2) + if z1.Sign() < 0 { + z1 = z1.Add(z1, m1) + } + + // Reuse z2 for p = z1 * m2inv. + m2inv := new(big.Int).ModInverse(m2, m1) + z2 = z2.Mul(z1, m2inv) + z2 = z2.And(z2, mask) + + // Reuse z1 for m2 * p. + z = z.Add(z, z1.Mul(z2, m2)) + z = z.Rem(z, m) + + return z +} + +func fastExpPow2(x, y *big.Int, mask *big.Int) *big.Int { + z := big.NewInt(1) + if y.Sign() == 0 { + return z + } + p := new(big.Int).Set(x) + p = p.And(p, mask) + if p.Cmp(z) <= 0 { // p <= 1 + return p + } + if y.Cmp(mask) > 0 { + y = new(big.Int).And(y, mask) + } + t := new(big.Int) + + for _, b := range y.Bits() { + for i := 0; i < bits.UintSize; i++ { + if b&1 != 0 { + z, t = t.Mul(z, p), z + z = z.And(z, mask) + } + p, t = t.Mul(p, p), p + p = p.And(p, mask) + b >>= 1 + } + } + return z +} diff --git a/core/vm/contracts.go b/core/vm/contracts.go index bdf15ab785e3..a1ea48fa00ef 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -404,12 +404,19 @@ func (c *bigModExp) Run(input []byte) ([]byte, error) { base = new(big.Int).SetBytes(getData(input, 0, baseLen)) exp = new(big.Int).SetBytes(getData(input, baseLen, expLen)) mod = new(big.Int).SetBytes(getData(input, baseLen+expLen, modLen)) + v []byte ) - if mod.BitLen() == 0 { + switch { + case mod.BitLen() == 0: // Modulo 0 is undefined, return zero return common.LeftPadBytes([]byte{}, int(modLen)), nil + case base.Cmp(common.Big1) == 0: + //If base == 1, then we can just return base % mod (if mod >= 1, which it is) + v = base.Mod(base, mod).Bytes() + default: + v = base.Exp(base, exp, mod).Bytes() } - return common.LeftPadBytes(base.Exp(base, exp, mod).Bytes(), int(modLen)), nil + return common.LeftPadBytes(v, int(modLen)), nil } // newCurvePoint unmarshals a binary blob into a bn256 elliptic curve point, diff --git a/tests/fuzzers/modexp/modexp-fuzzer.go b/tests/fuzzers/modexp/modexp-fuzzer.go new file mode 100644 index 000000000000..38e8487d1bee --- /dev/null +++ b/tests/fuzzers/modexp/modexp-fuzzer.go @@ -0,0 +1,86 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package modexp + +import ( + "fmt" + "math/big" + + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/math" + "github.com/XinFinOrg/XDPoSChain/core/vm" +) + +// The function must return +// 1 if the fuzzer should increase priority of the +// +// given input during subsequent fuzzing (for example, the input is lexically +// correct and was parsed successfully); +// +// -1 if the input must not be added to corpus even if gives new coverage; and +// 0 otherwise +// other values are reserved for future use. +func Fuzz(input []byte) int { + if len(input) <= 96 { + return -1 + } + // Abort on too expensive inputs + precomp := vm.PrecompiledContractsEIP1559[common.BytesToAddress([]byte{5})] + if gas := precomp.RequiredGas(input); gas > 40_000_000 { + return 0 + } + var ( + baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64() + expLen = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64() + modLen = new(big.Int).SetBytes(getData(input, 64, 32)).Uint64() + ) + // Handle a special case when both the base and mod length is zero + if baseLen == 0 && modLen == 0 { + return -1 + } + input = input[96:] + // Retrieve the operands and execute the exponentiation + var ( + base = new(big.Int).SetBytes(getData(input, 0, baseLen)) + exp = new(big.Int).SetBytes(getData(input, baseLen, expLen)) + mod = new(big.Int).SetBytes(getData(input, baseLen+expLen, modLen)) + ) + if mod.BitLen() == 0 { + // Modulo 0 is undefined, return zero + return -1 + } + var a = math.FastExp(new(big.Int).Set(base), new(big.Int).Set(exp), new(big.Int).Set(mod)) + var b = base.Exp(base, exp, mod) + if a.Cmp(b) != 0 { + panic(fmt.Sprintf("Inequality %x != %x", a, b)) + } + return 1 +} + +// getData returns a slice from the data based on the start and size and pads +// up to size with zero's. This function is overflow safe. +func getData(data []byte, start uint64, size uint64) []byte { + length := uint64(len(data)) + if start > length { + start = length + } + end := start + size + if end > length { + end = length + } + return common.RightPadBytes(data[start:end], int(size)) +} From abebda601ccc5f0717c15d9a916bab3591f1d31c Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 26 Sep 2024 13:34:52 +0800 Subject: [PATCH 016/479] common/math, tests/fuzzers: use big.Int clone (#26006) --- common/math/modexp.go | 82 --------------------------- common/math/modexp_test.go | 53 +++++++++++++++++ go.mod | 1 + go.sum | 2 + tests/fuzzers/modexp/debug/main.go | 40 +++++++++++++ tests/fuzzers/modexp/modexp-fuzzer.go | 19 ++++--- 6 files changed, 107 insertions(+), 90 deletions(-) delete mode 100644 common/math/modexp.go create mode 100644 common/math/modexp_test.go create mode 100644 tests/fuzzers/modexp/debug/main.go diff --git a/common/math/modexp.go b/common/math/modexp.go deleted file mode 100644 index 02ef755574c7..000000000000 --- a/common/math/modexp.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package math - -import ( - "math/big" - "math/bits" - - "github.com/XinFinOrg/XDPoSChain/common" -) - -// FastExp is semantically equivalent to x.Exp(x,y, m), but is faster for even -// modulus. -func FastExp(x, y, m *big.Int) *big.Int { - // Split m = m1 × m2 where m1 = 2ⁿ - n := m.TrailingZeroBits() - m1 := new(big.Int).Lsh(common.Big1, n) - mask := new(big.Int).Sub(m1, common.Big1) - m2 := new(big.Int).Rsh(m, n) - - // We want z = x**y mod m. - // z1 = x**y mod m1 = (x**y mod m) mod m1 = z mod m1 - // z2 = x**y mod m2 = (x**y mod m) mod m2 = z mod m2 - z1 := fastExpPow2(x, y, mask) - z2 := new(big.Int).Exp(x, y, m2) - - // Reconstruct z from z1, z2 using CRT, using algorithm from paper, - // which uses only a single modInverse. - // p = (z1 - z2) * m2⁻¹ (mod m1) - // z = z2 + p * m2 - z := new(big.Int).Set(z2) - - // Compute (z1 - z2) mod m1 [m1 == 2**n] into z1. - z1 = z1.And(z1, mask) - z2 = z2.And(z2, mask) - z1 = z1.Sub(z1, z2) - if z1.Sign() < 0 { - z1 = z1.Add(z1, m1) - } - - // Reuse z2 for p = z1 * m2inv. - m2inv := new(big.Int).ModInverse(m2, m1) - z2 = z2.Mul(z1, m2inv) - z2 = z2.And(z2, mask) - - // Reuse z1 for m2 * p. - z = z.Add(z, z1.Mul(z2, m2)) - z = z.Rem(z, m) - - return z -} - -func fastExpPow2(x, y *big.Int, mask *big.Int) *big.Int { - z := big.NewInt(1) - if y.Sign() == 0 { - return z - } - p := new(big.Int).Set(x) - p = p.And(p, mask) - if p.Cmp(z) <= 0 { // p <= 1 - return p - } - if y.Cmp(mask) > 0 { - y = new(big.Int).And(y, mask) - } - t := new(big.Int) - - for _, b := range y.Bits() { - for i := 0; i < bits.UintSize; i++ { - if b&1 != 0 { - z, t = t.Mul(z, p), z - z = z.And(z, mask) - } - p, t = t.Mul(p, p), p - p = p.And(p, mask) - b >>= 1 - } - } - return z -} diff --git a/common/math/modexp_test.go b/common/math/modexp_test.go new file mode 100644 index 000000000000..bd90076f84f6 --- /dev/null +++ b/common/math/modexp_test.go @@ -0,0 +1,53 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package math + +import ( + "math/big" + "testing" + + big2 "github.com/holiman/big" +) + +// TestFastModexp tests some cases found during fuzzing. +func TestFastModexp(t *testing.T) { + for i, tc := range []struct { + base string + exp string + mod string + }{ + {"0xeffffff900002f00", "0x40000000000000", "0x200"}, + {"0xf000", "0x4f900b400080000", "0x400000d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9ffffff005aeffd310000000000000000000000000000000000009f9f9f9f0000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000befffa5a5a5fff900002f000040000000000000000000000000000000029d9d9d000000000000009f9f9f00000000000000009f9f9f000000f3a080ab00000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f0000000000002900009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f000000cf000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f000000000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000befffff0000c0800000000800000000000000000000000000000002000000000000009f9f9f0000000000000000008000ff000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000befffa5a5a5fff900002f000040000000000000000000000000000000029d9d9d000000000000009f9f9f00000000000000009f9f9f000000f3a080ab00000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f0000000000002900009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f000000000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f000000000000000000000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000beffffff900002f0000400000c100000000000000000000000000000000000000006160600000000000000000008000ff0000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f0000"}, + {"5", "1435700818", "72"}, + {"0xffff", "0x300030003000300030003000300030003000302a3000300030003000300030003000300030003000300030003000300030003030623066307f3030783062303430383064303630343036", "0x300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}, + {"0x3133", "0x667f00000000000000000000000000ff002a000000000000000000000000000000000000000000000000000000000000667fff30783362773057ee756a6c266134643831646230313630", "0x3030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}, + } { + var ( + base, _ = new(big.Int).SetString(tc.base, 0) + exp, _ = new(big.Int).SetString(tc.exp, 0) + mod, _ = new(big.Int).SetString(tc.mod, 0) + base2, _ = new(big2.Int).SetString(tc.base, 0) + exp2, _ = new(big2.Int).SetString(tc.exp, 0) + mod2, _ = new(big2.Int).SetString(tc.mod, 0) + ) + var a = new(big2.Int).Exp(base2, exp2, mod2).String() + var b = new(big.Int).Exp(base, exp, mod).String() + if a != b { + t.Errorf("test %d: %#x ^ %#x mod %#x \n have %x\n want %x", i, base, exp, mod, a, b) + } + } +} diff --git a/go.mod b/go.mod index b7baee7256b0..7e9a4f481d5f 100644 --- a/go.mod +++ b/go.mod @@ -61,6 +61,7 @@ require ( github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect github.com/google/uuid v1.3.0 // indirect + github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/maruel/panicparse v0.0.0-20160720141634-ad661195ed0e // indirect diff --git a/go.sum b/go.sum index 98d3a63aed14..36c3740a1f7a 100644 --- a/go.sum +++ b/go.sum @@ -141,6 +141,8 @@ github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1 github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e h1:pIYdhNkDh+YENVNi3gto8n9hAmRxKxoar0iE6BLucjw= +github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e/go.mod h1:j9cQbcqHQujT0oKJ38PylVfqohClLr3CvDC+Qcg+lhU= github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= diff --git a/tests/fuzzers/modexp/debug/main.go b/tests/fuzzers/modexp/debug/main.go new file mode 100644 index 000000000000..008e46b192a5 --- /dev/null +++ b/tests/fuzzers/modexp/debug/main.go @@ -0,0 +1,40 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package main + +import ( + "fmt" + "os" + + "github.com/XinFinOrg/XDPoSChain/tests/fuzzers/modexp" +) + +func main() { + if len(os.Args) != 2 { + fmt.Fprintf(os.Stderr, "Usage: debug \n") + fmt.Fprintf(os.Stderr, "Example\n") + fmt.Fprintf(os.Stderr, " $ debug ../crashers/4bbef6857c733a87ecf6fd8b9e7238f65eb9862a\n") + os.Exit(1) + } + crasher := os.Args[1] + data, err := os.ReadFile(crasher) + if err != nil { + fmt.Fprintf(os.Stderr, "error loading crasher %v: %v", crasher, err) + os.Exit(1) + } + modexp.Fuzz(data) +} diff --git a/tests/fuzzers/modexp/modexp-fuzzer.go b/tests/fuzzers/modexp/modexp-fuzzer.go index 38e8487d1bee..bdbfd86e3006 100644 --- a/tests/fuzzers/modexp/modexp-fuzzer.go +++ b/tests/fuzzers/modexp/modexp-fuzzer.go @@ -21,8 +21,8 @@ import ( "math/big" "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/core/vm" + big2 "github.com/holiman/big" ) // The function must return @@ -55,18 +55,21 @@ func Fuzz(input []byte) int { input = input[96:] // Retrieve the operands and execute the exponentiation var ( - base = new(big.Int).SetBytes(getData(input, 0, baseLen)) - exp = new(big.Int).SetBytes(getData(input, baseLen, expLen)) - mod = new(big.Int).SetBytes(getData(input, baseLen+expLen, modLen)) + base = new(big.Int).SetBytes(getData(input, 0, baseLen)) + exp = new(big.Int).SetBytes(getData(input, baseLen, expLen)) + mod = new(big.Int).SetBytes(getData(input, baseLen+expLen, modLen)) + base2 = new(big2.Int).SetBytes(getData(input, 0, baseLen)) + exp2 = new(big2.Int).SetBytes(getData(input, baseLen, expLen)) + mod2 = new(big2.Int).SetBytes(getData(input, baseLen+expLen, modLen)) ) if mod.BitLen() == 0 { // Modulo 0 is undefined, return zero return -1 } - var a = math.FastExp(new(big.Int).Set(base), new(big.Int).Set(exp), new(big.Int).Set(mod)) - var b = base.Exp(base, exp, mod) - if a.Cmp(b) != 0 { - panic(fmt.Sprintf("Inequality %x != %x", a, b)) + var a = new(big2.Int).Exp(base2, exp2, mod2).String() + var b = new(big.Int).Exp(base, exp, mod).String() + if a != b { + panic(fmt.Sprintf("Inequality %#x ^ %#x mod %#x \n have %s\n want %s", base, exp, mod, a, b)) } return 1 } From 8e15f825cee8b9a4178281475be237fd162a5535 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 26 Sep 2024 13:52:56 +0800 Subject: [PATCH 017/479] core/vm: use optimized bigint (#26021) --- core/vm/contracts.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index a1ea48fa00ef..42cff82bbd05 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -29,6 +29,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/crypto/blake2b" "github.com/XinFinOrg/XDPoSChain/crypto/bn256" "github.com/XinFinOrg/XDPoSChain/params" + big2 "github.com/holiman/big" //lint:ignore SA1019 Needed for precompile "golang.org/x/crypto/ripemd160" @@ -401,17 +402,17 @@ func (c *bigModExp) Run(input []byte) ([]byte, error) { } // Retrieve the operands and execute the exponentiation var ( - base = new(big.Int).SetBytes(getData(input, 0, baseLen)) - exp = new(big.Int).SetBytes(getData(input, baseLen, expLen)) - mod = new(big.Int).SetBytes(getData(input, baseLen+expLen, modLen)) + base = new(big2.Int).SetBytes(getData(input, 0, baseLen)) + exp = new(big2.Int).SetBytes(getData(input, baseLen, expLen)) + mod = new(big2.Int).SetBytes(getData(input, baseLen+expLen, modLen)) v []byte ) switch { case mod.BitLen() == 0: // Modulo 0 is undefined, return zero return common.LeftPadBytes([]byte{}, int(modLen)), nil - case base.Cmp(common.Big1) == 0: - //If base == 1, then we can just return base % mod (if mod >= 1, which it is) + case base.BitLen() == 1: // a bit length of 1 means it's 1 (or -1). + // If base == 1, then we can just return base % mod (if mod >= 1, which it is) v = base.Mod(base, mod).Bytes() default: v = base.Exp(base, exp, mod).Bytes() From 532137c37b8b1fce475fdeb4c7d38c5b985e98d6 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 17 Oct 2024 10:32:24 +0800 Subject: [PATCH 018/479] core/vm: use golang native big.Int (#26834) --- common/math/modexp_test.go | 53 ---------------- core/vm/contracts.go | 7 +-- go.mod | 1 - go.sum | 2 - tests/fuzzers/modexp/debug/main.go | 40 ------------ tests/fuzzers/modexp/modexp-fuzzer.go | 89 --------------------------- 6 files changed, 3 insertions(+), 189 deletions(-) delete mode 100644 common/math/modexp_test.go delete mode 100644 tests/fuzzers/modexp/debug/main.go delete mode 100644 tests/fuzzers/modexp/modexp-fuzzer.go diff --git a/common/math/modexp_test.go b/common/math/modexp_test.go deleted file mode 100644 index bd90076f84f6..000000000000 --- a/common/math/modexp_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2022 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package math - -import ( - "math/big" - "testing" - - big2 "github.com/holiman/big" -) - -// TestFastModexp tests some cases found during fuzzing. -func TestFastModexp(t *testing.T) { - for i, tc := range []struct { - base string - exp string - mod string - }{ - {"0xeffffff900002f00", "0x40000000000000", "0x200"}, - {"0xf000", "0x4f900b400080000", "0x400000d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9ffffff005aeffd310000000000000000000000000000000000009f9f9f9f0000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000befffa5a5a5fff900002f000040000000000000000000000000000000029d9d9d000000000000009f9f9f00000000000000009f9f9f000000f3a080ab00000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f0000000000002900009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f000000cf000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f000000000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000befffff0000c0800000000800000000000000000000000000000002000000000000009f9f9f0000000000000000008000ff000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000befffa5a5a5fff900002f000040000000000000000000000000000000029d9d9d000000000000009f9f9f00000000000000009f9f9f000000f3a080ab00000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f0000000000002900009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f000000000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f000000000000000000000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000beffffff900002f0000400000c100000000000000000000000000000000000000006160600000000000000000008000ff0000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f00000000000000009f9f0000"}, - {"5", "1435700818", "72"}, - {"0xffff", "0x300030003000300030003000300030003000302a3000300030003000300030003000300030003000300030003000300030003030623066307f3030783062303430383064303630343036", "0x300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}, - {"0x3133", "0x667f00000000000000000000000000ff002a000000000000000000000000000000000000000000000000000000000000667fff30783362773057ee756a6c266134643831646230313630", "0x3030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}, - } { - var ( - base, _ = new(big.Int).SetString(tc.base, 0) - exp, _ = new(big.Int).SetString(tc.exp, 0) - mod, _ = new(big.Int).SetString(tc.mod, 0) - base2, _ = new(big2.Int).SetString(tc.base, 0) - exp2, _ = new(big2.Int).SetString(tc.exp, 0) - mod2, _ = new(big2.Int).SetString(tc.mod, 0) - ) - var a = new(big2.Int).Exp(base2, exp2, mod2).String() - var b = new(big.Int).Exp(base, exp, mod).String() - if a != b { - t.Errorf("test %d: %#x ^ %#x mod %#x \n have %x\n want %x", i, base, exp, mod, a, b) - } - } -} diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 42cff82bbd05..b06d0590c7f6 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -29,7 +29,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/crypto/blake2b" "github.com/XinFinOrg/XDPoSChain/crypto/bn256" "github.com/XinFinOrg/XDPoSChain/params" - big2 "github.com/holiman/big" //lint:ignore SA1019 Needed for precompile "golang.org/x/crypto/ripemd160" @@ -402,9 +401,9 @@ func (c *bigModExp) Run(input []byte) ([]byte, error) { } // Retrieve the operands and execute the exponentiation var ( - base = new(big2.Int).SetBytes(getData(input, 0, baseLen)) - exp = new(big2.Int).SetBytes(getData(input, baseLen, expLen)) - mod = new(big2.Int).SetBytes(getData(input, baseLen+expLen, modLen)) + base = new(big.Int).SetBytes(getData(input, 0, baseLen)) + exp = new(big.Int).SetBytes(getData(input, baseLen, expLen)) + mod = new(big.Int).SetBytes(getData(input, baseLen+expLen, modLen)) v []byte ) switch { diff --git a/go.mod b/go.mod index 7e9a4f481d5f..b7baee7256b0 100644 --- a/go.mod +++ b/go.mod @@ -61,7 +61,6 @@ require ( github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/maruel/panicparse v0.0.0-20160720141634-ad661195ed0e // indirect diff --git a/go.sum b/go.sum index 36c3740a1f7a..98d3a63aed14 100644 --- a/go.sum +++ b/go.sum @@ -141,8 +141,6 @@ github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1 github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e h1:pIYdhNkDh+YENVNi3gto8n9hAmRxKxoar0iE6BLucjw= -github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e/go.mod h1:j9cQbcqHQujT0oKJ38PylVfqohClLr3CvDC+Qcg+lhU= github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= diff --git a/tests/fuzzers/modexp/debug/main.go b/tests/fuzzers/modexp/debug/main.go deleted file mode 100644 index 008e46b192a5..000000000000 --- a/tests/fuzzers/modexp/debug/main.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package main - -import ( - "fmt" - "os" - - "github.com/XinFinOrg/XDPoSChain/tests/fuzzers/modexp" -) - -func main() { - if len(os.Args) != 2 { - fmt.Fprintf(os.Stderr, "Usage: debug \n") - fmt.Fprintf(os.Stderr, "Example\n") - fmt.Fprintf(os.Stderr, " $ debug ../crashers/4bbef6857c733a87ecf6fd8b9e7238f65eb9862a\n") - os.Exit(1) - } - crasher := os.Args[1] - data, err := os.ReadFile(crasher) - if err != nil { - fmt.Fprintf(os.Stderr, "error loading crasher %v: %v", crasher, err) - os.Exit(1) - } - modexp.Fuzz(data) -} diff --git a/tests/fuzzers/modexp/modexp-fuzzer.go b/tests/fuzzers/modexp/modexp-fuzzer.go deleted file mode 100644 index bdbfd86e3006..000000000000 --- a/tests/fuzzers/modexp/modexp-fuzzer.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2022 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package modexp - -import ( - "fmt" - "math/big" - - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/core/vm" - big2 "github.com/holiman/big" -) - -// The function must return -// 1 if the fuzzer should increase priority of the -// -// given input during subsequent fuzzing (for example, the input is lexically -// correct and was parsed successfully); -// -// -1 if the input must not be added to corpus even if gives new coverage; and -// 0 otherwise -// other values are reserved for future use. -func Fuzz(input []byte) int { - if len(input) <= 96 { - return -1 - } - // Abort on too expensive inputs - precomp := vm.PrecompiledContractsEIP1559[common.BytesToAddress([]byte{5})] - if gas := precomp.RequiredGas(input); gas > 40_000_000 { - return 0 - } - var ( - baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64() - expLen = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64() - modLen = new(big.Int).SetBytes(getData(input, 64, 32)).Uint64() - ) - // Handle a special case when both the base and mod length is zero - if baseLen == 0 && modLen == 0 { - return -1 - } - input = input[96:] - // Retrieve the operands and execute the exponentiation - var ( - base = new(big.Int).SetBytes(getData(input, 0, baseLen)) - exp = new(big.Int).SetBytes(getData(input, baseLen, expLen)) - mod = new(big.Int).SetBytes(getData(input, baseLen+expLen, modLen)) - base2 = new(big2.Int).SetBytes(getData(input, 0, baseLen)) - exp2 = new(big2.Int).SetBytes(getData(input, baseLen, expLen)) - mod2 = new(big2.Int).SetBytes(getData(input, baseLen+expLen, modLen)) - ) - if mod.BitLen() == 0 { - // Modulo 0 is undefined, return zero - return -1 - } - var a = new(big2.Int).Exp(base2, exp2, mod2).String() - var b = new(big.Int).Exp(base, exp, mod).String() - if a != b { - panic(fmt.Sprintf("Inequality %#x ^ %#x mod %#x \n have %s\n want %s", base, exp, mod, a, b)) - } - return 1 -} - -// getData returns a slice from the data based on the start and size and pads -// up to size with zero's. This function is overflow safe. -func getData(data []byte, start uint64, size uint64) []byte { - length := uint64(len(data)) - if start > length { - start = length - } - end := start + size - if end > length { - end = length - } - return common.RightPadBytes(data[start:end], int(size)) -} From 224caa5a0868ebe0192b706ddb8c04fd951d0a3f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 24 Sep 2024 15:04:08 +0800 Subject: [PATCH 019/479] core/vm: avoid memory expansion check for trivial ops (#24048) --- core/vm/interpreter.go | 54 +++++++++++++++++++----------------------- core/vm/jump_table.go | 44 ++++++++++++++++++++++++---------- 2 files changed, 57 insertions(+), 41 deletions(-) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 7d8351f70278..ae7f1b377f16 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -219,54 +219,50 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // enough stack items available to perform the operation. op = contract.GetOp(pc) operation := in.cfg.JumpTable[op] + cost = operation.constantGas // For tracing // Validate stack if sLen := stack.len(); sLen < operation.minStack { return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack} } else if sLen > operation.maxStack { return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack} } - // Static portion of gas - cost = operation.constantGas // For tracing - if !contract.UseGas(operation.constantGas) { + if !contract.UseGas(cost) { return nil, ErrOutOfGas } - - var memorySize uint64 - // calculate the new memory size and expand the memory to fit - // the operation - // Memory check needs to be done prior to evaluating the dynamic gas portion, - // to detect calculation overflows - if operation.memorySize != nil { - memSize, overflow := operation.memorySize(stack) - if overflow { - return nil, ErrGasUintOverflow - } - // memory is expanded in words of 32 bytes. Gas - // is also calculated in words. - if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { - return nil, ErrGasUintOverflow - } - } - // Dynamic portion of gas - // consume the gas and return an error if not enough gas is available. - // cost is explicitly set so that the capture state defer method can get the proper cost if operation.dynamicGas != nil { + // All ops with a dynamic memory usage also has a dynamic gas cost. + var memorySize uint64 + // calculate the new memory size and expand the memory to fit + // the operation + // Memory check needs to be done prior to evaluating the dynamic gas portion, + // to detect calculation overflows + if operation.memorySize != nil { + memSize, overflow := operation.memorySize(stack) + if overflow { + return nil, ErrGasUintOverflow + } + // memory is expanded in words of 32 bytes. Gas + // is also calculated in words. + if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { + return nil, ErrGasUintOverflow + } + } + // Consume the gas and return an error if not enough gas is available. + // cost is explicitly set so that the capture state defer method can get the proper cost var dynamicCost uint64 dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize) - cost += dynamicCost // total cost, for debug tracing + cost += dynamicCost // for tracing if err != nil || !contract.UseGas(dynamicCost) { return nil, ErrOutOfGas } + if memorySize > 0 { + mem.Resize(memorySize) + } } - if memorySize > 0 { - mem.Resize(memorySize) - } - if in.cfg.Debug { in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) logged = true } - // execute the operation res, err = operation.execute(&pc, in, callContext) if err != nil { diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 4ae7c1e8ce1e..ca46f9166aad 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -17,6 +17,8 @@ package vm import ( + "fmt" + "github.com/XinFinOrg/XDPoSChain/params" ) @@ -60,16 +62,34 @@ var ( // JumpTable contains the EVM opcodes supported at a given fork. type JumpTable [256]*operation +func validate(jt JumpTable) JumpTable { + for i, op := range jt { + if op == nil { + panic(fmt.Sprintf("op %#x is not set", i)) + } + // The interpreter has an assumption that if the memorySize function is + // set, then the dynamicGas function is also set. This is a somewhat + // arbitrary assumption, and can be removed if we need to -- but it + // allows us to avoid a condition check. As long as we have that assumption + // in there, this little sanity check prevents us from merging in a + // change which violates it. + if op.memorySize != nil && op.dynamicGas == nil { + panic(fmt.Sprintf("op %v has dynamic memory but not dynamic gas", OpCode(i).String())) + } + } + return jt +} + func newEip1559InstructionSet() JumpTable { instructionSet := newShanghaiInstructionSet() enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929 - return instructionSet + return validate(instructionSet) } func newShanghaiInstructionSet() JumpTable { instructionSet := newMergeInstructionSet() enable3855(&instructionSet) // PUSH0 instruction - return instructionSet + return validate(instructionSet) } func newMergeInstructionSet() JumpTable { @@ -80,7 +100,7 @@ func newMergeInstructionSet() JumpTable { minStack: minStack(0, 1), maxStack: maxStack(0, 1), } - return instructionSet + return validate(instructionSet) } // newLondonInstructionSet returns the frontier, homestead, byzantium, @@ -89,7 +109,7 @@ func newLondonInstructionSet() JumpTable { instructionSet := newBerlinInstructionSet() // enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529 enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198 - return instructionSet + return validate(instructionSet) } // newBerlinInstructionSet returns the frontier, homestead, byzantium, @@ -97,7 +117,7 @@ func newLondonInstructionSet() JumpTable { func newBerlinInstructionSet() JumpTable { instructionSet := newIstanbulInstructionSet() // enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929 - return instructionSet + return validate(instructionSet) } // newIstanbulInstructionSet returns the frontier, homestead @@ -109,7 +129,7 @@ func newIstanbulInstructionSet() JumpTable { enable1884(&instructionSet) // Reprice reader opcodes - https://eips.ethereum.org/EIPS/eip-1884 enable2200(&instructionSet) // Net metered SSTORE - https://eips.ethereum.org/EIPS/eip-2200 - return instructionSet + return validate(instructionSet) } // newConstantinopleInstructionSet returns the frontier, homestead @@ -148,7 +168,7 @@ func newConstantinopleInstructionSet() JumpTable { maxStack: maxStack(4, 1), memorySize: memoryCreate2, } - return instructionSet + return validate(instructionSet) } // newByzantiumInstructionSet returns the frontier, homestead and @@ -184,14 +204,14 @@ func newByzantiumInstructionSet() JumpTable { maxStack: maxStack(2, 0), memorySize: memoryRevert, } - return instructionSet + return validate(instructionSet) } // EIP 158 a.k.a Spurious Dragon func newSpuriousDragonInstructionSet() JumpTable { instructionSet := newTangerineWhistleInstructionSet() instructionSet[EXP].dynamicGas = gasExpEIP158 - return instructionSet + return validate(instructionSet) } @@ -205,7 +225,7 @@ func newTangerineWhistleInstructionSet() JumpTable { instructionSet[CALL].constantGas = params.CallGasEIP150 instructionSet[CALLCODE].constantGas = params.CallGasEIP150 instructionSet[DELEGATECALL].constantGas = params.CallGasEIP150 - return instructionSet + return validate(instructionSet) } // newHomesteadInstructionSet returns the frontier and homestead @@ -220,7 +240,7 @@ func newHomesteadInstructionSet() JumpTable { maxStack: maxStack(6, 1), memorySize: memoryDelegateCall, } - return instructionSet + return validate(instructionSet) } // newFrontierInstructionSet returns the frontier instructions @@ -1036,5 +1056,5 @@ func newFrontierInstructionSet() JumpTable { } } - return tbl + return validate(tbl) } From 6fa968201f2eecf608cb119550868fcc83792cac Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 24 Sep 2024 15:45:09 +0800 Subject: [PATCH 020/479] core/vm: fix some typos --- core/vm/interpreter.go | 2 +- core/vm/runtime/runtime_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index ae7f1b377f16..1e48dbd0979a 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -186,7 +186,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( logged bool // deferred EVMLogger should ignore already logged steps res []byte // result of the opcode execution function ) - // Don't move this deferrred function, it's placed before the capturestate-deferred method, + // Don't move this deferred function, it's placed before the capturestate-deferred method, // so that it get's executed _after_: the capturestate needs the stacks before // they are returned to the pools defer func() { diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 21f5e1a57529..34031a95e996 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -453,7 +453,7 @@ func BenchmarkSimpleLoop(b *testing.B) { byte(vm.JUMP), } - calllRevertingContractWithInput := []byte{ + callRevertingContractWithInput := []byte{ byte(vm.JUMPDEST), // // push args for the call byte(vm.PUSH1), 0, // out size @@ -481,7 +481,7 @@ func BenchmarkSimpleLoop(b *testing.B) { benchmarkNonModifyingCode(100000000, loopingCode, "loop-100M", b) benchmarkNonModifyingCode(100000000, callInexistant, "call-nonexist-100M", b) benchmarkNonModifyingCode(100000000, callEOA, "call-EOA-100M", b) - benchmarkNonModifyingCode(100000000, calllRevertingContractWithInput, "call-reverting-100M", b) + benchmarkNonModifyingCode(100000000, callRevertingContractWithInput, "call-reverting-100M", b) //benchmarkNonModifyingCode(10000000, staticCallIdentity, "staticcall-identity-10M", b) //benchmarkNonModifyingCode(10000000, loopingCode, "loop-10M", b) From 45c644c43d1498abdaf86a0fe85e74d933d06cbc Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 24 Sep 2024 16:01:34 +0800 Subject: [PATCH 021/479] core/vm: update benchmark to use Errorf instead of Sprintf (#24845) --- core/vm/contracts_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index 1a03f93fc223..22fe467c3f97 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -618,7 +618,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { return } if common.Bytes2Hex(res) != test.expected { - bench.Error(fmt.Sprintf("Expected %v, got %v", test.expected, common.Bytes2Hex(res))) + bench.Errorf("Expected %v, got %v", test.expected, common.Bytes2Hex(res)) return } }) From c78b9e6245a8b382183b28edbdb3fd727a5583c6 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 24 Sep 2024 18:05:53 +0800 Subject: [PATCH 022/479] core/vm: for tracing, do not report post-op memory (#24867) --- core/vm/interpreter.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 1e48dbd0979a..c4b4b4143b80 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -255,11 +255,15 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( if err != nil || !contract.UseGas(dynamicCost) { return nil, ErrOutOfGas } + // Do tracing before memory expansion + if in.cfg.Debug { + in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) + logged = true + } if memorySize > 0 { mem.Resize(memorySize) } - } - if in.cfg.Debug { + } else if in.cfg.Debug { in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) logged = true } From 1fa9e8187037c31a9b3ad8dafa2af991b4332a39 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 24 Sep 2024 18:56:33 +0800 Subject: [PATCH 023/479] core/vm: reduce overhead in instructions-benchmark (#24860) --- core/vm/instructions_test.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 000a22b2d8eb..c095e8b9f3e6 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -291,26 +291,33 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) { var ( env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() + scope = &ScopeContext{nil, stack, nil} evmInterpreter = NewEVMInterpreter(env, env.vmConfig) ) env.interpreter = evmInterpreter // convert args - byteArgs := make([][]byte, len(args)) + intArgs := make([]*uint256.Int, len(args)) for i, arg := range args { - byteArgs[i] = common.Hex2Bytes(arg) + intArgs[i] = new(uint256.Int).SetBytes(common.Hex2Bytes(arg)) } pc := uint64(0) bench.ResetTimer() for i := 0; i < bench.N; i++ { - for _, arg := range byteArgs { - a := new(uint256.Int) - a.SetBytes(arg) - stack.push(a) + for _, arg := range intArgs { + stack.push(arg) } - op(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) + op(&pc, evmInterpreter, scope) stack.pop() } + bench.StopTimer() + + for i, arg := range args { + want := new(uint256.Int).SetBytes(common.Hex2Bytes(arg)) + if have := intArgs[i]; !want.Eq(have) { + bench.Fatalf("input #%d mutated, have %x want %x", i, have, want) + } + } } func BenchmarkOpAdd64(b *testing.B) { From be6631beb06c8496c30646ac801bd91e2aa609ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 3 Jun 2022 10:40:14 +0200 Subject: [PATCH 024/479] core/vm: optimize jumpdest analysis (#23500) core/vm: optimize PUSH opcode discrimination --- core/vm/analysis.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/vm/analysis.go b/core/vm/analysis.go index 3733bab6a7c0..4aa8cfe70f11 100644 --- a/core/vm/analysis.go +++ b/core/vm/analysis.go @@ -76,7 +76,7 @@ func codeBitmapInternal(code, bits bitvec) bitvec { for pc := uint64(0); pc < uint64(len(code)); { op := OpCode(code[pc]) pc++ - if op < PUSH1 || op > PUSH32 { + if int8(op) < int8(PUSH1) { // If not PUSH (the int8(op) > int(PUSH32) is always false). continue } numbits := op - PUSH1 + 1 From 16f2aabc6470997c067bacdec0ef862ad2e2266c Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 25 Sep 2024 10:15:14 +0800 Subject: [PATCH 025/479] core/vm: more linters (#24783) --- core/vm/contracts.go | 2 -- core/vm/instructions_test.go | 48 ++++++++++++++++++------------------ 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index b06d0590c7f6..f7e10794c2b5 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -29,8 +29,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/crypto/blake2b" "github.com/XinFinOrg/XDPoSChain/crypto/bn256" "github.com/XinFinOrg/XDPoSChain/params" - - //lint:ignore SA1019 Needed for precompile "golang.org/x/crypto/ripemd160" ) diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index c095e8b9f3e6..25dceabd5a40 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -230,35 +230,35 @@ func TestAddMod(t *testing.T) { } } -// getResult is a convenience function to generate the expected values -func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase { - var ( - env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) - stack = newstack() - pc = uint64(0) - interpreter = env.interpreter.(*EVMInterpreter) - ) - result := make([]TwoOperandTestcase, len(args)) - for i, param := range args { - x := new(uint256.Int).SetBytes(common.Hex2Bytes(param.x)) - y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y)) - stack.push(x) - stack.push(y) - _, err := opFn(&pc, interpreter, &ScopeContext{nil, stack, nil}) - if err != nil { - log.Fatalln(err) - } - actual := stack.pop() - result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)} - } - return result -} - // utility function to fill the json-file with testcases // Enable this test to generate the 'testcases_xx.json' files func TestWriteExpectedValues(t *testing.T) { t.Skip("Enable this test to create json test cases.") + // getResult is a convenience function to generate the expected values + getResult := func(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase { + var ( + env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) + stack = newstack() + pc = uint64(0) + interpreter = env.interpreter.(*EVMInterpreter) + ) + result := make([]TwoOperandTestcase, len(args)) + for i, param := range args { + x := new(uint256.Int).SetBytes(common.Hex2Bytes(param.x)) + y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y)) + stack.push(x) + stack.push(y) + _, err := opFn(&pc, interpreter, &ScopeContext{nil, stack, nil}) + if err != nil { + log.Fatalln(err) + } + actual := stack.pop() + result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)} + } + return result + } + for name, method := range twoOpMethods { data, err := json.Marshal(getResult(commonParams, method)) if err != nil { From ee5dd8673f672b48fb7810d8914d589e2cec3d59 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 25 Sep 2024 17:14:42 +0800 Subject: [PATCH 026/479] core/vm: remove empty lines --- core/vm/instructions_test.go | 3 --- core/vm/interpreter.go | 1 - core/vm/runtime/runtime_test.go | 1 - 3 files changed, 5 deletions(-) diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 25dceabd5a40..a9ab947c2293 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -47,7 +47,6 @@ var commonParams []*twoOperandParams var twoOpMethods map[string]executionFunc func init() { - // Params is a list of common edgecases that should be used for some common tests params := []string{ "0000000000000000000000000000000000000000000000000000000000000000", // 0 @@ -93,7 +92,6 @@ func init() { } func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) { - var ( env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() @@ -648,7 +646,6 @@ func TestCreate2Addreses(t *testing.T) { expected: "0xE33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0", }, } { - origin := common.BytesToAddress(common.FromHex(tt.origin)) salt := common.BytesToHash(common.FromHex(tt.salt)) code := common.FromHex(tt.code) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index c4b4b4143b80..ebfaa6387ca5 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -145,7 +145,6 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { // considered a revert-and-consume-all-gas operation except for // ErrExecutionReverted which means revert-and-keep-gas-left. func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) { - // Increment the call depth which is restricted to 1024 in.evm.depth++ defer func() { in.evm.depth-- }() diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 34031a95e996..fc986c3a7628 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -374,7 +374,6 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, b *testing. // BenchmarkSimpleLoop test a pretty simple loop which loops until OOG // 55 ms func BenchmarkSimpleLoop(b *testing.B) { - staticCallIdentity := []byte{ byte(vm.JUMPDEST), // [ count ] // push args for the call From 69cb57a0f44967f0ef66b70172825d29d5f7dfd6 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 25 Sep 2024 17:40:04 +0800 Subject: [PATCH 027/479] core/vm: not deep copy return data slice upon call completion (#25183) --- core/vm/instructions.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index eaabc1a76ec6..36401455109d 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -710,7 +710,6 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt } stack.push(&temp) if err == nil || err == ErrExecutionReverted { - ret = common.CopyBytes(ret) scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } scope.Contract.Gas += returnGas @@ -746,7 +745,6 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ } stack.push(&temp) if err == nil || err == ErrExecutionReverted { - ret = common.CopyBytes(ret) scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } scope.Contract.Gas += returnGas @@ -775,7 +773,6 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } stack.push(&temp) if err == nil || err == ErrExecutionReverted { - ret = common.CopyBytes(ret) scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } scope.Contract.Gas += returnGas @@ -804,7 +801,6 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) } stack.push(&temp) if err == nil || err == ErrExecutionReverted { - ret = common.CopyBytes(ret) scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } scope.Contract.Gas += returnGas From 480adaf96092e692eb23fa6af12e79a3ab7eda5a Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 25 Sep 2024 17:53:44 +0800 Subject: [PATCH 028/479] core/vm: minor trivial clean up (#25880) --- core/vm/instructions.go | 4 ++-- core/vm/interpreter.go | 15 +++------------ 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 36401455109d..c9f3fc380d03 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -22,8 +22,8 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/params" + "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/holiman/uint256" - "golang.org/x/crypto/sha3" ) func opAdd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { @@ -238,7 +238,7 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( data := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) if interpreter.hasher == nil { - interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState) + interpreter.hasher = crypto.NewKeccakState() } else { interpreter.hasher.Reset() } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index ebfaa6387ca5..ada646fdb00f 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -17,10 +17,9 @@ package vm import ( - "hash" - "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/math" + "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/log" ) @@ -68,21 +67,13 @@ type ScopeContext struct { Contract *Contract } -// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports -// Read to get a variable amount of data from the hash state. Read is faster than Sum -// because it doesn't copy the internal state, but also modifies the internal state. -type keccakState interface { - hash.Hash - Read([]byte) (int, error) -} - // EVMInterpreter represents an EVM interpreter type EVMInterpreter struct { evm *EVM cfg Config - hasher keccakState // Keccak256 hasher instance shared across opcodes - hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes + hasher crypto.KeccakState // Keccak256 hasher instance shared across opcodes + hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes readOnly bool // Whether to throw on stateful modifications returnData []byte // Last CALL's return data for subsequent reuse From 4482ea4d8472c88c231d916e4e9683ceacddb2e0 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 25 Sep 2024 18:07:10 +0800 Subject: [PATCH 029/479] core/vm: fix docstrings --- core/vm/interface.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/vm/interface.go b/core/vm/interface.go index 903a957a2017..5c695ffed703 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -79,12 +79,12 @@ type StateDB interface { // CallContext provides a basic interface for the EVM calling conventions. The EVM // depends on this context being implemented for doing subcalls and initialising new EVM contracts. type CallContext interface { - // Call another contract + // Call calls another contract. Call(env *EVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) - // Take another's contract code and execute within our own context + // CallCode takes another contracts code and execute within our own context CallCode(env *EVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) - // Same as CallCode except sender and value is propagated from parent to child scope + // DelegateCall is same as CallCode except sender and value is propagated from parent to child scope DelegateCall(env *EVM, me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) - // Create a new contract + // Create creates a new contract Create(env *EVM, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) } From 1c84a31bb6adf1b0d810af8f4e42dc8e136e0f80 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 26 Sep 2024 15:51:24 +0800 Subject: [PATCH 030/479] core/vm: deepcopy jumptable when enabling extra eips (#26137) --- core/vm/interpreter.go | 8 +++++--- core/vm/jump_table.go | 11 +++++++++++ core/vm/jump_table_test.go | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 core/vm/jump_table_test.go diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index ada646fdb00f..9983b02290da 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -110,15 +110,17 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { cfg.JumpTable = &frontierInstructionSet } var extraEips []int + if len(cfg.ExtraEips) > 0 { + // Deep-copy jumptable to prevent modification of opcodes in other tables + cfg.JumpTable = copyJumpTable(cfg.JumpTable) + } for _, eip := range cfg.ExtraEips { - copy := *cfg.JumpTable - if err := EnableEIP(eip, ©); err != nil { + if err := EnableEIP(eip, cfg.JumpTable); err != nil { // Disable it, so caller can check if it's activated or not log.Error("EIP activation failed", "eip", eip, "error", err) } else { extraEips = append(extraEips, eip) } - cfg.JumpTable = © } cfg.ExtraEips = extraEips } diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index ca46f9166aad..46f3dc959d39 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -1058,3 +1058,14 @@ func newFrontierInstructionSet() JumpTable { return validate(tbl) } + +func copyJumpTable(source *JumpTable) *JumpTable { + dest := *source + for i, op := range source { + if op != nil { + opCopy := *op + dest[i] = &opCopy + } + } + return &dest +} diff --git a/core/vm/jump_table_test.go b/core/vm/jump_table_test.go new file mode 100644 index 000000000000..d7c9408bca2c --- /dev/null +++ b/core/vm/jump_table_test.go @@ -0,0 +1,35 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +// TestJumpTableCopy tests that deep copy is necessery to prevent modify shared jump table +func TestJumpTableCopy(t *testing.T) { + tbl := newEip1559InstructionSet() + require.Equal(t, uint64(0), tbl[SLOAD].constantGas) + + // a deep copy won't modify the shared jump table + deepCopy := copyJumpTable(&tbl) + deepCopy[SLOAD].constantGas = 100 + require.Equal(t, uint64(100), deepCopy[SLOAD].constantGas) + require.Equal(t, uint64(0), tbl[SLOAD].constantGas) +} From 8c077345d08e5691c46a4639a1b802b02b1bc157 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 18 Oct 2024 09:24:52 +0800 Subject: [PATCH 031/479] all: removed blockhash from statedb (#23126) --- consensus/tests/engine_v1_tests/helper.go | 2 +- consensus/tests/engine_v2_tests/helper.go | 2 +- core/chain_makers.go | 2 +- core/state/statedb.go | 32 ++--- core/state/statedb_test.go | 4 +- core/state_processor.go | 148 ++++++++++++---------- eth/api_tracer.go | 6 +- miner/worker.go | 4 +- 8 files changed, 102 insertions(+), 98 deletions(-) diff --git a/consensus/tests/engine_v1_tests/helper.go b/consensus/tests/engine_v1_tests/helper.go index 52baa648e00b..a393f457b715 100644 --- a/consensus/tests/engine_v1_tests/helper.go +++ b/consensus/tests/engine_v1_tests/helper.go @@ -382,7 +382,7 @@ func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*ty var gasUsed = new(uint64) var receipts types.Receipts for i, tx := range txs { - statedb.Prepare(tx.Hash(), header.Hash(), i) + statedb.Prepare(tx.Hash(), i) receipt, _, err, _ := ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{}) if err != nil { return nil, fmt.Errorf("%v when applying transaction", err) diff --git a/consensus/tests/engine_v2_tests/helper.go b/consensus/tests/engine_v2_tests/helper.go index 454f482d7172..5ea44d6d480c 100644 --- a/consensus/tests/engine_v2_tests/helper.go +++ b/consensus/tests/engine_v2_tests/helper.go @@ -699,7 +699,7 @@ func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*ty var gasUsed = new(uint64) var receipts types.Receipts for i, tx := range txs { - statedb.Prepare(tx.Hash(), header.Hash(), i) + statedb.Prepare(tx.Hash(), i) receipt, _, err, _ := ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{}) if err != nil { return nil, fmt.Errorf("%v when applying transaction", err) diff --git a/core/chain_makers.go b/core/chain_makers.go index 7a4012945f7d..2df2366fbf15 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -100,7 +100,7 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) { b.SetCoinbase(common.Address{}) } feeCapacity := state.GetTRC21FeeCapacityFromState(b.statedb) - b.statedb.Prepare(tx.Hash(), common.Hash{}, len(b.txs)) + b.statedb.Prepare(tx.Hash(), len(b.txs)) receipt, gas, err, tokenFeeUsed := ApplyTransaction(b.config, feeCapacity, bc, &b.header.Coinbase, b.gasPool, b.statedb, nil, b.header, tx, &b.header.GasUsed, vm.Config{}) if err != nil { panic(err) diff --git a/core/state/statedb.go b/core/state/statedb.go index bac5106f5b96..37bcc85c5a35 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -67,10 +67,10 @@ type StateDB struct { // The refund counter, also used by state transitioning. refund uint64 - thash, bhash common.Hash - txIndex int - logs map[common.Hash][]*types.Log - logSize uint + thash common.Hash + txIndex int + logs map[common.Hash][]*types.Log + logSize uint preimages map[common.Hash][]byte @@ -150,7 +150,6 @@ func (self *StateDB) Reset(root common.Hash) error { self.stateObjects = make(map[common.Address]*stateObject) self.stateObjectsDirty = make(map[common.Address]struct{}) self.thash = common.Hash{} - self.bhash = common.Hash{} self.txIndex = 0 self.logs = make(map[common.Hash][]*types.Log) self.logSize = 0 @@ -164,15 +163,18 @@ func (self *StateDB) AddLog(log *types.Log) { self.journal = append(self.journal, addLogChange{txhash: self.thash}) log.TxHash = self.thash - log.BlockHash = self.bhash log.TxIndex = uint(self.txIndex) log.Index = self.logSize self.logs[self.thash] = append(self.logs[self.thash], log) self.logSize++ } -func (self *StateDB) GetLogs(hash common.Hash) []*types.Log { - return self.logs[hash] +func (s *StateDB) GetLogs(hash common.Hash, blockHash common.Hash) []*types.Log { + logs := s.logs[hash] + for _, l := range logs { + l.BlockHash = blockHash + } + return logs } func (self *StateDB) Logs() []*types.Log { @@ -249,11 +251,6 @@ func (self *StateDB) TxIndex() int { return self.txIndex } -// BlockHash returns the current block hash set by Prepare. -func (self *StateDB) BlockHash() common.Hash { - return self.bhash -} - func (self *StateDB) GetCode(addr common.Address) []byte { stateObject := self.getStateObject(addr) if stateObject != nil { @@ -651,11 +648,10 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { // Prepare sets the current transaction hash and index and block hash which is // used when the EVM emits new state logs. -func (self *StateDB) Prepare(thash, bhash common.Hash, ti int) { - self.thash = thash - self.bhash = bhash - self.txIndex = ti - self.accessList = newAccessList() +func (s *StateDB) Prepare(thash common.Hash, ti int) { + s.thash = thash + s.txIndex = ti + s.accessList = newAccessList() } // DeleteSuicides flags the suicided objects for deletion so that it diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 15533ec5a086..35e7affafda8 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -419,9 +419,9 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error { return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d", state.GetRefund(), checkstate.GetRefund()) } - if !reflect.DeepEqual(state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{})) { + if !reflect.DeepEqual(state.GetLogs(common.Hash{}, common.Hash{}), checkstate.GetLogs(common.Hash{}, common.Hash{})) { return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v", - state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{})) + state.GetLogs(common.Hash{}, common.Hash{}), checkstate.GetLogs(common.Hash{}, common.Hash{})) } return nil } diff --git a/core/state_processor.go b/core/state_processor.go index 292538c39d7d..917b5802a579 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -25,8 +25,6 @@ import ( "sync" "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate" - "github.com/XinFinOrg/XDPoSChain/log" - "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/misc" @@ -34,6 +32,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" ) @@ -69,17 +68,19 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen // transactions failed to execute due to insufficient gas it will return an error. func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tradingState *tradingstate.TradingStateDB, cfg vm.Config, balanceFee map[common.Address]*big.Int) (types.Receipts, []*types.Log, uint64, error) { var ( - receipts types.Receipts - usedGas = new(uint64) - header = block.Header() - allLogs []*types.Log - gp = new(GasPool).AddGas(block.GasLimit()) + receipts types.Receipts + usedGas = new(uint64) + header = block.Header() + blockHash = block.Hash() + blockNumber = block.Number() + allLogs []*types.Log + gp = new(GasPool).AddGas(block.GasLimit()) ) // Mutate the the block and state according to any hard-fork specs if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { misc.ApplyDAOHardFork(statedb) } - if common.TIPSigning.Cmp(header.Number) == 0 { + if common.TIPSigning.Cmp(blockNumber) == 0 { statedb.DeleteAddress(common.BlockSignersBinary) } parentState := statedb.Copy() @@ -88,17 +89,18 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra totalFeeUsed := big.NewInt(0) blockContext := NewEVMBlockContext(header, p.bc, nil) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, tradingState, p.config, cfg) + coinbaseOwner := getCoinbaseOwner(p.bc, statedb, header, nil) // Iterate over and process the individual transactions for i, tx := range block.Transactions() { // check black-list txs after hf if (block.Number().Uint64() >= common.BlackListHFNumber) && !common.IsTestnet { // check if sender is in black list if tx.From() != nil && common.Blacklist[*tx.From()] { - return nil, nil, 0, fmt.Errorf("Block contains transaction with sender in black-list: %v", tx.From().Hex()) + return nil, nil, 0, fmt.Errorf("block contains transaction with sender in black-list: %v", tx.From().Hex()) } // check if receiver is in black list if tx.To() != nil && common.Blacklist[*tx.To()] { - return nil, nil, 0, fmt.Errorf("Block contains transaction with receiver in black-list: %v", tx.To().Hex()) + return nil, nil, 0, fmt.Errorf("block contains transaction with receiver in black-list: %v", tx.To().Hex()) } } // validate minFee slot for XDCZ @@ -115,8 +117,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra return nil, nil, 0, err } } - statedb.Prepare(tx.Hash(), block.Hash(), i) - receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, p.bc, nil, gp, statedb, tradingState, header, tx, usedGas, vmenv) + statedb.Prepare(tx.Hash(), i) + receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, gp, statedb, coinbaseOwner, blockNumber, blockHash, tx, usedGas, vmenv) if err != nil { return nil, nil, 0, err } @@ -138,17 +140,19 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, statedb *state.StateDB, tradingState *tradingstate.TradingStateDB, cfg vm.Config, balanceFee map[common.Address]*big.Int) (types.Receipts, []*types.Log, uint64, error) { block := cBlock.block var ( - receipts types.Receipts - usedGas = new(uint64) - header = block.Header() - allLogs []*types.Log - gp = new(GasPool).AddGas(block.GasLimit()) + receipts types.Receipts + usedGas = new(uint64) + header = block.Header() + blockHash = block.Hash() + blockNumber = block.Number() + allLogs []*types.Log + gp = new(GasPool).AddGas(block.GasLimit()) ) // Mutate the the block and state according to any hard-fork specs if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { misc.ApplyDAOHardFork(statedb) } - if common.TIPSigning.Cmp(header.Number) == 0 { + if common.TIPSigning.Cmp(blockNumber) == 0 { statedb.DeleteAddress(common.BlockSignersBinary) } if cBlock.stop { @@ -164,6 +168,7 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated } blockContext := NewEVMBlockContext(header, p.bc, nil) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, tradingState, p.config, cfg) + coinbaseOwner := getCoinbaseOwner(p.bc, statedb, header, nil) // Iterate over and process the individual transactions receipts = make([]*types.Receipt, block.Transactions().Len()) for i, tx := range block.Transactions() { @@ -171,11 +176,11 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated if (block.Number().Uint64() >= common.BlackListHFNumber) && !common.IsTestnet { // check if sender is in black list if tx.From() != nil && common.Blacklist[*tx.From()] { - return nil, nil, 0, fmt.Errorf("Block contains transaction with sender in black-list: %v", tx.From().Hex()) + return nil, nil, 0, fmt.Errorf("block contains transaction with sender in black-list: %v", tx.From().Hex()) } // check if receiver is in black list if tx.To() != nil && common.Blacklist[*tx.To()] { - return nil, nil, 0, fmt.Errorf("Block contains transaction with receiver in black-list: %v", tx.To().Hex()) + return nil, nil, 0, fmt.Errorf("block contains transaction with receiver in black-list: %v", tx.To().Hex()) } } // validate minFee slot for XDCZ @@ -192,8 +197,8 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated return nil, nil, 0, err } } - statedb.Prepare(tx.Hash(), block.Hash(), i) - receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, p.bc, nil, gp, statedb, tradingState, header, tx, usedGas, vmenv) + statedb.Prepare(tx.Hash(), i) + receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, gp, statedb, coinbaseOwner, blockNumber, blockHash, tx, usedGas, vmenv) if err != nil { return nil, nil, 0, err } @@ -215,23 +220,24 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated return receipts, allLogs, *usedGas, nil } -func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*big.Int, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, XDCxState *tradingstate.TradingStateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, uint64, error, bool) { +func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*big.Int, gp *GasPool, statedb *state.StateDB, coinbaseOwner common.Address, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, uint64, error, bool) { to := tx.To() - if to != nil && *to == common.BlockSignersBinary && config.IsTIPSigning(header.Number) { - return ApplySignTransaction(config, statedb, header, tx, usedGas) - } - if to != nil && *to == common.TradingStateAddrBinary && config.IsTIPXDCXReceiver(header.Number) { - return ApplyEmptyTransaction(config, statedb, header, tx, usedGas) - } - if to != nil && *to == common.XDCXLendingAddressBinary && config.IsTIPXDCXReceiver(header.Number) { - return ApplyEmptyTransaction(config, statedb, header, tx, usedGas) + if to != nil { + if *to == common.BlockSignersBinary && config.IsTIPSigning(blockNumber) { + return ApplySignTransaction(config, statedb, blockNumber, blockHash, tx, usedGas) + } + if *to == common.TradingStateAddrBinary && config.IsTIPXDCXReceiver(blockNumber) { + return ApplyEmptyTransaction(config, statedb, blockNumber, blockHash, tx, usedGas) + } + if *to == common.XDCXLendingAddressBinary && config.IsTIPXDCXReceiver(blockNumber) { + return ApplyEmptyTransaction(config, statedb, blockNumber, blockHash, tx, usedGas) + } } - if tx.IsTradingTransaction() && config.IsTIPXDCXReceiver(header.Number) { - return ApplyEmptyTransaction(config, statedb, header, tx, usedGas) + if tx.IsTradingTransaction() && config.IsTIPXDCXReceiver(blockNumber) { + return ApplyEmptyTransaction(config, statedb, blockNumber, blockHash, tx, usedGas) } - - if tx.IsLendingFinalizedTradeTransaction() && config.IsTIPXDCXReceiver(header.Number) { - return ApplyEmptyTransaction(config, statedb, header, tx, usedGas) + if tx.IsLendingFinalizedTradeTransaction() && config.IsTIPXDCXReceiver(blockNumber) { + return ApplyEmptyTransaction(config, statedb, blockNumber, blockHash, tx, usedGas) } var balanceFee *big.Int @@ -240,7 +246,7 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* balanceFee = value } } - msg, err := tx.AsMessage(types.MakeSigner(config, header.Number), balanceFee, header.Number) + msg, err := tx.AsMessage(types.MakeSigner(config, blockNumber), balanceFee, blockNumber) if err != nil { return nil, 0, err, false } @@ -251,19 +257,9 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* // Update the evm with the new transaction context. evm.Reset(txContext, statedb) - // If we don't have an explicit author (i.e. not mining), extract from the header - var beneficiary common.Address - if author == nil { - beneficiary, _ = bc.Engine().Author(header) // Ignore error, we're past header validation - } else { - beneficiary = *author - } - - coinbaseOwner := statedb.GetOwner(beneficiary) - // Bypass blacklist address maxBlockNumber := new(big.Int).SetInt64(9147459) - if header.Number.Cmp(maxBlockNumber) <= 0 { + if blockNumber.Cmp(maxBlockNumber) <= 0 { addrMap := make(map[string]string) addrMap["0x5248bfb72fd4f234e062d3e9bb76f08643004fcd"] = "29410" addrMap["0x5ac26105b35ea8935be382863a70281ec7a985e9"] = "23551" @@ -391,7 +387,7 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* addrFrom := msg.From().Hex() - currentBlockNumber := header.Number.Int64() + currentBlockNumber := blockNumber.Int64() if addr, ok := blockMap[currentBlockNumber]; ok { if strings.ToLower(addr) == strings.ToLower(addrFrom) { bal := addrMap[addr] @@ -414,10 +410,10 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* // Update the state with pending changes. var root []byte - if config.IsByzantium(header.Number) { + if config.IsByzantium(blockNumber) { statedb.Finalise(true) } else { - root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes() + root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes() } *usedGas += gas @@ -438,10 +434,10 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* } // Set the receipt logs and create the bloom filter. - receipt.Logs = statedb.GetLogs(tx.Hash()) + receipt.Logs = statedb.GetLogs(tx.Hash(), blockHash) receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) - receipt.BlockHash = statedb.BlockHash() - receipt.BlockNumber = header.Number + receipt.BlockHash = blockHash + receipt.BlockNumber = blockNumber receipt.TransactionIndex = uint(statedb.TxIndex()) if balanceFee != nil && failed { state.PayFeeWithTRC21TxFail(statedb, msg.From(), *to) @@ -449,6 +445,17 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* return receipt, gas, err, balanceFee != nil } +func getCoinbaseOwner(bc *BlockChain, statedb *state.StateDB, header *types.Header, author *common.Address) common.Address { + // If we don't have an explicit author (i.e. not mining), extract from the header + var beneficiary common.Address + if author == nil { + beneficiary, _ = bc.Engine().Author(header) // Ignore error, we're past header validation + } else { + beneficiary = *author + } + return statedb.GetOwner(beneficiary) +} + // ApplyTransaction attempts to apply a transaction to the given state database // and uses the input parameters for its environment. It returns the receipt // for the transaction, gas used and an error if the transaction failed, @@ -457,18 +464,19 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* // Create a new context to be used in the EVM environment blockContext := NewEVMBlockContext(header, bc, author) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, XDCxState, config, cfg) - return applyTransaction(config, tokensFee, bc, author, gp, statedb, XDCxState, header, tx , usedGas, vmenv) + coinbaseOwner := getCoinbaseOwner(bc, statedb, header, author) + return applyTransaction(config, tokensFee, gp, statedb, coinbaseOwner, header.Number, header.Hash(), tx, usedGas, vmenv) } -func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, error, bool) { +func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, error, bool) { // Update the state with pending changes var root []byte - if config.IsByzantium(header.Number) { + if config.IsByzantium(blockNumber) { statedb.Finalise(true) } else { - root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes() + root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes() } - from, err := types.Sender(types.MakeSigner(config, header.Number), tx) + from, err := types.Sender(types.MakeSigner(config, blockNumber), tx) if err != nil { return nil, 0, err, false } @@ -488,23 +496,23 @@ func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, he // Set the receipt logs and create a bloom for filtering log := &types.Log{} log.Address = common.BlockSignersBinary - log.BlockNumber = header.Number.Uint64() + log.BlockNumber = blockNumber.Uint64() statedb.AddLog(log) - receipt.Logs = statedb.GetLogs(tx.Hash()) + receipt.Logs = statedb.GetLogs(tx.Hash(), blockHash) receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) - receipt.BlockHash = statedb.BlockHash() - receipt.BlockNumber = header.Number + receipt.BlockHash = blockHash + receipt.BlockNumber = blockNumber receipt.TransactionIndex = uint(statedb.TxIndex()) return receipt, 0, nil, false } -func ApplyEmptyTransaction(config *params.ChainConfig, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, error, bool) { +func ApplyEmptyTransaction(config *params.ChainConfig, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, error, bool) { // Update the state with pending changes var root []byte - if config.IsByzantium(header.Number) { + if config.IsByzantium(blockNumber) { statedb.Finalise(true) } else { - root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes() + root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes() } // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx // based on the eip phase, we're passing wether the root touch-delete accounts. @@ -515,12 +523,12 @@ func ApplyEmptyTransaction(config *params.ChainConfig, statedb *state.StateDB, h // Set the receipt logs and create a bloom for filtering log := &types.Log{} log.Address = *tx.To() - log.BlockNumber = header.Number.Uint64() + log.BlockNumber = blockNumber.Uint64() statedb.AddLog(log) - receipt.Logs = statedb.GetLogs(tx.Hash()) + receipt.Logs = statedb.GetLogs(tx.Hash(), blockHash) receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) - receipt.BlockHash = statedb.BlockHash() - receipt.BlockNumber = header.Number + receipt.BlockHash = blockHash + receipt.BlockNumber = blockNumber receipt.TransactionIndex = uint(statedb.TxIndex()) return receipt, 0, nil, false } diff --git a/eth/api_tracer.go b/eth/api_tracer.go index 3277dd7e0870..1c79c33acf53 100644 --- a/eth/api_tracer.go +++ b/eth/api_tracer.go @@ -520,7 +520,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, // Generate the next state snapshot fast without tracing msg, _ := tx.AsMessage(signer, balacne, block.Number()) txContext := core.NewEVMTxContext(msg) - statedb.Prepare(tx.Hash(), block.Hash(), i) + statedb.Prepare(tx.Hash(), i) vmenv := vm.NewEVM(blockCtx, txContext, statedb, XDCxState, api.config, vm.Config{}) owner := common.Address{} @@ -740,7 +740,7 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, t vmenv := vm.NewEVM(vmctx, txContext, statedb, nil, api.config, vm.Config{Debug: true, Tracer: tracer}) // Call Prepare to clear out the statedb access list - statedb.Prepare(txctx.TxHash, txctx.BlockHash, txctx.TxIndex) + statedb.Prepare(txctx.TxHash, txctx.TxIndex) owner := common.Address{} ret, gas, failed, err, _ := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), owner) @@ -792,7 +792,7 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree usedGas := new(uint64) // Iterate over and process the individual transactions for idx, tx := range block.Transactions() { - statedb.Prepare(tx.Hash(), block.Hash(), idx) + statedb.Prepare(tx.Hash(), idx) if idx == txIndex { var balanceFee *big.Int if tx.To() != nil { diff --git a/miner/worker.go b/miner/worker.go index 0e341636d243..a312d902ba65 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -911,7 +911,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad } } // Start executing the transaction - env.state.Prepare(hash, common.Hash{}, env.tcount) + env.state.Prepare(hash, env.tcount) nonce := env.state.GetNonce(from) if nonce != tx.Nonce() && !tx.IsSkipNonceTransaction() { @@ -1012,7 +1012,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad continue } // Start executing the transaction - env.state.Prepare(hash, common.Hash{}, env.tcount) + env.state.Prepare(hash, env.tcount) nonce := env.state.GetNonce(from) if nonce > tx.Nonce() { // New head notification data race between the transaction pool and miner, shift From 464a89074b6282f9ac449128941187b5bc93e2aa Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 21 Oct 2024 16:27:21 +0800 Subject: [PATCH 032/479] all: use unified emptyRootHash and emptyCodeHash (#26718) --- consensus/ethash/algorithm_test.go | 6 ++--- core/bench_test.go | 6 +++-- core/state/iterator.go | 3 ++- core/state/state_object.go | 9 +++---- core/state/statedb.go | 12 ++------- core/state/sync_test.go | 4 +-- core/types/block.go | 9 ++----- core/types/hashes.go | 42 ++++++++++++++++++++++++++++++ ethclient/ethclient.go | 4 +-- light/lightchain_test.go | 4 +-- trie/trie.go | 2 ++ 11 files changed, 67 insertions(+), 34 deletions(-) create mode 100644 core/types/hashes.go diff --git a/consensus/ethash/algorithm_test.go b/consensus/ethash/algorithm_test.go index 4d05815de00d..6a9d8809ad68 100644 --- a/consensus/ethash/algorithm_test.go +++ b/consensus/ethash/algorithm_test.go @@ -697,11 +697,11 @@ func TestConcurrentDiskCacheGeneration(t *testing.T) { block := types.NewBlockWithHeader(&types.Header{ Number: big.NewInt(3311058), ParentHash: common.HexToHash("0xd783efa4d392943503f28438ad5830b2d5964696ffc285f338585e9fe0a37a05"), - UncleHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"), + UncleHash: types.EmptyUncleHash, Coinbase: common.HexToAddress("0xc0ea08a2d404d3172d2add29a45be56da40e2949"), Root: common.HexToHash("0x77d14e10470b5850332524f8cd6f69ad21f070ce92dca33ab2858300242ef2f1"), - TxHash: common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), - ReceiptHash: common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), + TxHash: types.EmptyTxsHash, + ReceiptHash: types.EmptyReceiptsHash, Difficulty: big.NewInt(167925187834220), GasLimit: 4015682, GasUsed: 0, diff --git a/core/bench_test.go b/core/bench_test.go index 1129142b1af6..5c4b97c97465 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -231,13 +231,15 @@ func makeChainForBench(db ethdb.Database, full bool, count uint64) { ParentHash: hash, Difficulty: big.NewInt(1), UncleHash: types.EmptyUncleHash, - TxHash: types.EmptyRootHash, - ReceiptHash: types.EmptyRootHash, + TxHash: types.EmptyTxsHash, + ReceiptHash: types.EmptyReceiptsHash, } hash = header.Hash() + rawdb.WriteHeader(db, header) rawdb.WriteCanonicalHash(db, hash, n) WriteTd(db, hash, n, big.NewInt(int64(n+1))) + if full || n == 0 { block := types.NewBlockWithHeader(header) rawdb.WriteBody(db, hash, n, block.Body()) diff --git a/core/state/iterator.go b/core/state/iterator.go index 63ae2b08c4d0..1fc639abb7d8 100644 --- a/core/state/iterator.go +++ b/core/state/iterator.go @@ -21,6 +21,7 @@ import ( "fmt" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/rlp" "github.com/XinFinOrg/XDPoSChain/trie" ) @@ -116,7 +117,7 @@ func (it *NodeIterator) step() error { if !it.dataIt.Next(true) { it.dataIt = nil } - if !bytes.Equal(account.CodeHash, emptyCodeHash) { + if !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) { it.codeHash = common.BytesToHash(account.CodeHash) addrHash := common.BytesToHash(it.stateIt.LeafKey()) it.code, err = it.state.db.ContractCode(addrHash, common.BytesToHash(account.CodeHash)) diff --git a/core/state/state_object.go b/core/state/state_object.go index 99763db267e2..ca695efe3b85 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -23,12 +23,11 @@ import ( "math/big" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/rlp" ) -var emptyCodeHash = crypto.Keccak256(nil) - type Code []byte func (c Code) String() string { @@ -93,7 +92,7 @@ type stateObject struct { // empty returns whether the account is considered empty. func (s *stateObject) empty() bool { - return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash) + return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, types.EmptyCodeHash.Bytes()) } // Account is the Ethereum consensus representation of accounts. @@ -111,7 +110,7 @@ func newObject(db *StateDB, address common.Address, data Account, onDirty func(a data.Balance = new(big.Int) } if data.CodeHash == nil { - data.CodeHash = emptyCodeHash + data.CodeHash = types.EmptyCodeHash.Bytes() } return &stateObject{ db: db, @@ -372,7 +371,7 @@ func (s *stateObject) Code(db Database) []byte { if s.code != nil { return s.code } - if bytes.Equal(s.CodeHash(), emptyCodeHash) { + if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) { return nil } code, err := db.ContractCode(s.addrHash, common.BytesToHash(s.CodeHash())) diff --git a/core/state/statedb.go b/core/state/statedb.go index 37bcc85c5a35..8a13f46dcbca 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -36,14 +36,6 @@ type revision struct { journalIndex int } -var ( - // emptyState is the known hash of an empty state trie entry. - emptyState = crypto.Keccak256Hash(nil) - - // emptyCode is the known hash of the empty EVM bytecode. - emptyCode = crypto.Keccak256Hash(nil) -) - // StateDBs within the ethereum protocol are used to store anything // within the merkle trie. StateDBs take care of caching and storing // nested states. It's the general query interface to retrieve: @@ -714,11 +706,11 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) if err := rlp.DecodeBytes(leaf, &account); err != nil { return nil } - if account.Root != emptyState { + if account.Root != types.EmptyRootHash { s.db.TrieDB().Reference(account.Root, parent) } code := common.BytesToHash(account.CodeHash) - if code != emptyCode { + if code != types.EmptyCodeHash { s.db.TrieDB().Reference(code, parent) } return nil diff --git a/core/state/sync_test.go b/core/state/sync_test.go index e38943ab5807..57fa80e7733b 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -23,6 +23,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/rawdb" + "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/ethdb/memorydb" @@ -125,8 +126,7 @@ func checkStateConsistency(db ethdb.Database, root common.Hash) error { // Tests that an empty state is not scheduled for syncing. func TestEmptyStateSync(t *testing.T) { - empty := common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") - if req := NewStateSync(empty, rawdb.NewMemoryDatabase(), trie.NewSyncBloom(1, memorydb.New())).Missing(1); len(req) != 0 { + if req := NewStateSync(types.EmptyRootHash, rawdb.NewMemoryDatabase(), trie.NewSyncBloom(1, memorydb.New())).Missing(1); len(req) != 0 { t.Errorf("content requested for empty state: %v", req) } } diff --git a/core/types/block.go b/core/types/block.go index 071666a801fa..139f641bdba5 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -32,11 +32,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/rlp" ) -var ( - EmptyRootHash = DeriveSha(Transactions{}) - EmptyUncleHash = CalcUncleHash(nil) -) - // A BlockNonce is a 64-bit hash which proves (combined with the // mix-hash) that a sufficient amount of computation has been carried // out on a block. @@ -222,7 +217,7 @@ func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []* // TODO: panic if len(txs) != len(receipts) if len(txs) == 0 { - b.header.TxHash = EmptyRootHash + b.header.TxHash = EmptyTxsHash } else { b.header.TxHash = DeriveSha(Transactions(txs)) b.transactions = make(Transactions, len(txs)) @@ -230,7 +225,7 @@ func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []* } if len(receipts) == 0 { - b.header.ReceiptHash = EmptyRootHash + b.header.ReceiptHash = EmptyReceiptsHash } else { b.header.ReceiptHash = DeriveSha(Receipts(receipts)) b.header.Bloom = CreateBloom(receipts) diff --git a/core/types/hashes.go b/core/types/hashes.go new file mode 100644 index 000000000000..bbcf0446ead8 --- /dev/null +++ b/core/types/hashes.go @@ -0,0 +1,42 @@ +// Copyright 2023 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/crypto" +) + +var ( + // EmptyRootHash is the known root hash of an empty trie. + EmptyRootHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + + // EmptyUncleHash is the known hash of the empty uncle set. + EmptyUncleHash = rlpHash([]*Header(nil)) // 1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347 + + // EmptyCodeHash is the known hash of the empty EVM bytecode. + EmptyCodeHash = crypto.Keccak256Hash(nil) // c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 + + // EmptyTxsHash is the known hash of the empty transaction set. + EmptyTxsHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + + // EmptyReceiptsHash is the known hash of the empty receipt set. + EmptyReceiptsHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + + // EmptyWithdrawalsHash is the known hash of the empty withdrawal set. + EmptyWithdrawalsHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") +) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 7e3a43932752..812e7dae1418 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -110,10 +110,10 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface if head.UncleHash != types.EmptyUncleHash && len(body.UncleHashes) == 0 { return nil, errors.New("server returned empty uncle list but block header indicates uncles") } - if head.TxHash == types.EmptyRootHash && len(body.Transactions) > 0 { + if head.TxHash == types.EmptyTxsHash && len(body.Transactions) > 0 { return nil, errors.New("server returned non-empty transaction list but block header indicates no transactions") } - if head.TxHash != types.EmptyRootHash && len(body.Transactions) == 0 { + if head.TxHash != types.EmptyTxsHash && len(body.Transactions) == 0 { return nil, errors.New("server returned empty transaction list but block header indicates transactions") } // Load uncles because they are not included in the block response. diff --git a/light/lightchain_test.go b/light/lightchain_test.go index 0de612e3b9e6..7d762e2e3f73 100644 --- a/light/lightchain_test.go +++ b/light/lightchain_test.go @@ -250,8 +250,8 @@ func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types. Number: big.NewInt(int64(i + 1)), Difficulty: big.NewInt(int64(difficulty)), UncleHash: types.EmptyUncleHash, - TxHash: types.EmptyRootHash, - ReceiptHash: types.EmptyRootHash, + TxHash: types.EmptyTxsHash, + ReceiptHash: types.EmptyReceiptsHash, } if i == 0 { header.ParentHash = genesis.Hash() diff --git a/trie/trie.go b/trie/trie.go index f163182091e2..834c4d79de72 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -28,6 +28,8 @@ import ( ) var ( + // TODO: remove file core/types/derive_sha.go, then remove emptyRoot and emptyState + // emptyRoot is the known root hash of an empty trie. emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") From a79411fa063cddbc387aba6eca8f5abe9a433eb5 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 22 Oct 2024 19:40:30 +0800 Subject: [PATCH 033/479] all: fix staticcheck warning ST1005: incorrectly formatted error string --- XDCx/XDCx.go | 8 +- XDCx/order_processor.go | 8 +- XDCx/tradingstate/dump.go | 24 ++-- XDCx/tradingstate/relayer_state.go | 4 +- XDCx/tradingstate/statedb.go | 38 ++--- XDCxlending/XDCxlending.go | 2 +- XDCxlending/lendingstate/dump.go | 32 ++--- XDCxlending/lendingstate/relayer.go | 2 +- XDCxlending/lendingstate/statedb.go | 36 ++--- XDCxlending/order_processor.go | 14 +- accounts/abi/type_test.go | 2 +- accounts/keystore/keystore_passphrase.go | 8 +- accounts/keystore/keystore_plain_test.go | 4 +- accounts/usbwallet/ledger.go | 130 +++++++++--------- cmd/faucet/faucet.go | 28 ++-- cmd/faucet/website.go | 16 +-- common/types_test.go | 4 +- consensus/XDPoS/XDPoS.go | 2 +- consensus/XDPoS/engines/engine_v1/engine.go | 6 +- consensus/XDPoS/engines/engine_v1/utils.go | 2 +- consensus/XDPoS/engines/engine_v2/engine.go | 8 +- .../XDPoS/engines/engine_v2/forensics.go | 4 +- consensus/XDPoS/engines/engine_v2/utils.go | 6 +- consensus/XDPoS/engines/engine_v2/vote.go | 6 +- consensus/XDPoS/utils/errors.go | 28 ++-- consensus/errors.go | 4 +- consensus/tests/engine_v1_tests/helper.go | 6 +- consensus/tests/engine_v2_tests/api_test.go | 2 +- consensus/tests/engine_v2_tests/helper.go | 12 +- .../engine_v2_tests/verify_header_test.go | 6 +- consensus/tests/engine_v2_tests/vote_test.go | 2 +- console/bridge.go | 2 +- core/blockchain.go | 16 +-- core/lending_pool.go | 2 +- core/order_pool.go | 4 +- core/tx_pool.go | 4 +- core/vm/contracts.go | 4 +- core/vm/privacy/bulletproof.go | 8 +- core/vm/privacy/ringct.go | 8 +- eth/api_tracer.go | 2 +- eth/backend.go | 6 +- eth/downloader/statesync.go | 4 +- eth/hooks/engine_v1_hooks.go | 2 +- eth/tracers/internal/tracers/assets.go | 16 +-- internal/cmdtest/test_cmd.go | 4 +- internal/ethapi/api.go | 114 +++++++-------- internal/jsre/deps/bindata.go | 16 +-- internal/jsre/jsre.go | 4 +- les/protocol.go | 2 +- les/retrieve.go | 2 +- light/postprocess.go | 9 +- light/txpool.go | 6 +- log/logger.go | 2 +- metrics/disk_nop.go | 3 +- metrics/librato/client.go | 2 +- miner/miner.go | 2 +- miner/remote_agent.go | 2 +- node/node_test.go | 4 +- p2p/peer.go | 2 +- p2p/server.go | 2 +- tests/block_test_util.go | 42 +++--- tests/transaction_test_util.go | 22 +-- whisper/whisperv5/whisper.go | 2 +- whisper/whisperv6/whisper.go | 2 +- 64 files changed, 389 insertions(+), 387 deletions(-) diff --git a/XDCx/XDCx.go b/XDCx/XDCx.go index 3dc3386d92bf..ac3f83d582f4 100644 --- a/XDCx/XDCx.go +++ b/XDCx/XDCx.go @@ -275,11 +275,11 @@ func (XDCx *XDCX) GetAveragePriceLastEpoch(chain consensus.ChainContext, statedb if inversePrice != nil && inversePrice.Sign() > 0 { quoteTokenDecimal, err := XDCx.GetTokenDecimal(chain, statedb, quoteToken) if err != nil || quoteTokenDecimal.Sign() == 0 { - return nil, fmt.Errorf("fail to get tokenDecimal. Token: %v . Err: %v", quoteToken.String(), err) + return nil, fmt.Errorf("fail to get tokenDecimal: Token: %v . Err: %v", quoteToken.String(), err) } baseTokenDecimal, err := XDCx.GetTokenDecimal(chain, statedb, baseToken) if err != nil || baseTokenDecimal.Sign() == 0 { - return nil, fmt.Errorf("fail to get tokenDecimal. Token: %v . Err: %v", baseToken.String(), err) + return nil, fmt.Errorf("fail to get tokenDecimal: Token: %v . Err: %v", baseToken.String(), err) } price = new(big.Int).Mul(baseTokenDecimal, quoteTokenDecimal) price = new(big.Int).Div(price, inversePrice) @@ -302,7 +302,7 @@ func (XDCx *XDCX) ConvertXDCToToken(chain consensus.ChainContext, statedb *state tokenDecimal, err := XDCx.GetTokenDecimal(chain, statedb, token) if err != nil || tokenDecimal.Sign() == 0 { - return common.Big0, common.Big0, fmt.Errorf("fail to get tokenDecimal. Token: %v . Err: %v", token.String(), err) + return common.Big0, common.Big0, fmt.Errorf("fail to get tokenDecimal: Token: %v . Err: %v", token.String(), err) } tokenQuantity := new(big.Int).Mul(quantity, tokenDecimal) tokenQuantity = new(big.Int).Div(tokenQuantity, tokenPriceInXDC) @@ -568,7 +568,7 @@ func (XDCx *XDCX) GetTradingState(block *types.Block, author common.Address) (*t return nil, err } if XDCx.StateCache == nil { - return nil, errors.New("Not initialized XDCx") + return nil, errors.New("not initialized XDCx") } return tradingstate.New(root, XDCx.StateCache) } diff --git a/XDCx/order_processor.go b/XDCx/order_processor.go index 86d156fec881..6418bc028260 100644 --- a/XDCx/order_processor.go +++ b/XDCx/order_processor.go @@ -243,7 +243,7 @@ func (XDCx *XDCX) processOrderList(coinbase common.Address, chain consensus.Chai inversePrice := tradingStateDB.GetLastPrice(tradingstate.GetTradingOrderBookHash(common.XDCNativeAddressBinary, oldestOrder.QuoteToken)) quoteTokenDecimal, err := XDCx.GetTokenDecimal(chain, statedb, oldestOrder.QuoteToken) if err != nil || quoteTokenDecimal.Sign() == 0 { - return nil, nil, nil, fmt.Errorf("Fail to get tokenDecimal. Token: %v . Err: %v", oldestOrder.QuoteToken.String(), err) + return nil, nil, nil, fmt.Errorf("fail to get tokenDecimal: Token: %v . Err: %v", oldestOrder.QuoteToken.String(), err) } log.Debug("TryGet inversePrice XDC/QuoteToken", "inversePrice", inversePrice) if inversePrice != nil && inversePrice.Sign() > 0 { @@ -368,11 +368,11 @@ func (XDCx *XDCX) processOrderList(coinbase common.Address, chain consensus.Chai func (XDCx *XDCX) getTradeQuantity(quotePrice *big.Int, coinbase common.Address, chain consensus.ChainContext, statedb *state.StateDB, takerOrder *tradingstate.OrderItem, makerOrder *tradingstate.OrderItem, quantityToTrade *big.Int) (*big.Int, bool, *tradingstate.SettleBalance, error) { baseTokenDecimal, err := XDCx.GetTokenDecimal(chain, statedb, makerOrder.BaseToken) if err != nil || baseTokenDecimal.Sign() == 0 { - return tradingstate.Zero, false, nil, fmt.Errorf("Fail to get tokenDecimal. Token: %v . Err: %v", makerOrder.BaseToken.String(), err) + return tradingstate.Zero, false, nil, fmt.Errorf("fail to get tokenDecimal: Token: %v . Err: %v", makerOrder.BaseToken.String(), err) } quoteTokenDecimal, err := XDCx.GetTokenDecimal(chain, statedb, makerOrder.QuoteToken) if err != nil || quoteTokenDecimal.Sign() == 0 { - return tradingstate.Zero, false, nil, fmt.Errorf("Fail to get tokenDecimal. Token: %v . Err: %v", makerOrder.QuoteToken.String(), err) + return tradingstate.Zero, false, nil, fmt.Errorf("fail to get tokenDecimal: Token: %v . Err: %v", makerOrder.QuoteToken.String(), err) } if makerOrder.QuoteToken == common.XDCNativeAddressBinary { quotePrice = quoteTokenDecimal @@ -526,7 +526,7 @@ func DoSettleBalance(coinbase common.Address, takerOrder, makerOrder *tradingsta matchingFee = new(big.Int).Add(matchingFee, common.RelayerFee) if common.EmptyHash(takerExOwner.Hash()) || common.EmptyHash(makerExOwner.Hash()) { - return fmt.Errorf("Echange owner empty , Taker: %v , maker : %v ", takerExOwner, makerExOwner) + return fmt.Errorf("empty echange owner: taker: %v , maker : %v", takerExOwner, makerExOwner) } mapBalances := map[common.Address]map[common.Address]*big.Int{} //Checking balance diff --git a/XDCx/tradingstate/dump.go b/XDCx/tradingstate/dump.go index 41281dd45997..33de042c8510 100644 --- a/XDCx/tradingstate/dump.go +++ b/XDCx/tradingstate/dump.go @@ -50,7 +50,7 @@ type DumpOrderBookInfo struct { func (self *TradingStateDB) DumpAskTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) { exhangeObject := self.getStateExchangeObject(orderBook) if exhangeObject == nil { - return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex()) + return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]DumpOrderList{} it := trie.NewIterator(exhangeObject.getAsksTrie(self.db).NodeIterator(nil)) @@ -65,7 +65,7 @@ func (self *TradingStateDB) DumpAskTrie(orderBook common.Hash) (map[*big.Int]Dum } else { var data orderList if err := rlp.DecodeBytes(it.Value, &data); err != nil { - return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,price :%v ", orderBook.Hex(), price) + return nil, fmt.Errorf("fail when decode order iist orderBook: %v , price :%v", orderBook.Hex(), price) } stateOrderList := newStateOrderList(self, Ask, orderBook, priceHash, data, nil) mapResult[price] = stateOrderList.DumpOrderList(self.db) @@ -93,7 +93,7 @@ func (self *TradingStateDB) DumpAskTrie(orderBook common.Hash) (map[*big.Int]Dum func (self *TradingStateDB) DumpBidTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) { exhangeObject := self.getStateExchangeObject(orderBook) if exhangeObject == nil { - return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex()) + return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]DumpOrderList{} it := trie.NewIterator(exhangeObject.getBidsTrie(self.db).NodeIterator(nil)) @@ -108,7 +108,7 @@ func (self *TradingStateDB) DumpBidTrie(orderBook common.Hash) (map[*big.Int]Dum } else { var data orderList if err := rlp.DecodeBytes(it.Value, &data); err != nil { - return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,price :%v ", orderBook.Hex(), price) + return nil, fmt.Errorf("fail when decode order iist orderBook: %v , price :%v", orderBook.Hex(), price) } stateOrderList := newStateOrderList(self, Bid, orderBook, priceHash, data, nil) mapResult[price] = stateOrderList.DumpOrderList(self.db) @@ -136,7 +136,7 @@ func (self *TradingStateDB) DumpBidTrie(orderBook common.Hash) (map[*big.Int]Dum func (self *TradingStateDB) GetBids(orderBook common.Hash) (map[*big.Int]*big.Int, error) { exhangeObject := self.getStateExchangeObject(orderBook) if exhangeObject == nil { - return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex()) + return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]*big.Int{} it := trie.NewIterator(exhangeObject.getBidsTrie(self.db).NodeIterator(nil)) @@ -151,7 +151,7 @@ func (self *TradingStateDB) GetBids(orderBook common.Hash) (map[*big.Int]*big.In } else { var data orderList if err := rlp.DecodeBytes(it.Value, &data); err != nil { - return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,price :%v ", orderBook.Hex(), price) + return nil, fmt.Errorf("fail when decode order iist orderBook: %v , price :%v", orderBook.Hex(), price) } stateOrderList := newStateOrderList(self, Bid, orderBook, priceHash, data, nil) mapResult[price] = stateOrderList.data.Volume @@ -179,7 +179,7 @@ func (self *TradingStateDB) GetBids(orderBook common.Hash) (map[*big.Int]*big.In func (self *TradingStateDB) GetAsks(orderBook common.Hash) (map[*big.Int]*big.Int, error) { exhangeObject := self.getStateExchangeObject(orderBook) if exhangeObject == nil { - return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex()) + return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]*big.Int{} it := trie.NewIterator(exhangeObject.getAsksTrie(self.db).NodeIterator(nil)) @@ -194,7 +194,7 @@ func (self *TradingStateDB) GetAsks(orderBook common.Hash) (map[*big.Int]*big.In } else { var data orderList if err := rlp.DecodeBytes(it.Value, &data); err != nil { - return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,price :%v ", orderBook.Hex(), price) + return nil, fmt.Errorf("fail when decode order iist orderBook: %v , price : %v", orderBook.Hex(), price) } stateOrderList := newStateOrderList(self, Ask, orderBook, priceHash, data, nil) mapResult[price] = stateOrderList.data.Volume @@ -255,7 +255,7 @@ func (self *stateOrderList) DumpOrderList(db Database) DumpOrderList { func (self *TradingStateDB) DumpOrderBookInfo(orderBook common.Hash) (*DumpOrderBookInfo, error) { exhangeObject := self.getStateExchangeObject(orderBook) if exhangeObject == nil { - return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex()) + return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } result := &DumpOrderBookInfo{} result.LastPrice = exhangeObject.data.LastPrice @@ -318,7 +318,7 @@ func (self *liquidationPriceState) DumpLendingBook(db Database) (DumpLendingBook } else { var data orderList if err := rlp.DecodeBytes(it.Value, &data); err != nil { - return result, fmt.Errorf("Failed to decode state lending book orderbook : %s ,liquidation price :%s , lendingBook : %s ,err : %v", self.orderBook, self.liquidationPrice, lendingBook, err) + return result, fmt.Errorf("failed to decode state lending book orderbook: %s , liquidation price : %s , lendingBook : %s , err : %v", self.orderBook, self.liquidationPrice, lendingBook, err) } stateLendingBook := newStateLendingBook(self.orderBook, self.liquidationPrice, lendingBook, data, nil) result.LendingBooks[lendingBook] = stateLendingBook.DumpOrderList(db) @@ -335,7 +335,7 @@ func (self *liquidationPriceState) DumpLendingBook(db Database) (DumpLendingBook func (self *TradingStateDB) DumpLiquidationPriceTrie(orderBook common.Hash) (map[*big.Int]DumpLendingBook, error) { exhangeObject := self.getStateExchangeObject(orderBook) if exhangeObject == nil { - return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex()) + return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]DumpLendingBook{} it := trie.NewIterator(exhangeObject.getLiquidationPriceTrie(self.db).NodeIterator(nil)) @@ -350,7 +350,7 @@ func (self *TradingStateDB) DumpLiquidationPriceTrie(orderBook common.Hash) (map } else { var data orderList if err := rlp.DecodeBytes(it.Value, &data); err != nil { - return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,price :%v ", orderBook.Hex(), price) + return nil, fmt.Errorf("fail when decode order iist orderBook: %v , price : %v", orderBook.Hex(), price) } liquidationPriceState := newLiquidationPriceState(self, orderBook, priceHash, data, nil) dumpLendingBook, err := liquidationPriceState.DumpLendingBook(self.db) diff --git a/XDCx/tradingstate/relayer_state.go b/XDCx/tradingstate/relayer_state.go index 348f2130418e..d5b79bf0c0c4 100644 --- a/XDCx/tradingstate/relayer_state.go +++ b/XDCx/tradingstate/relayer_state.go @@ -106,7 +106,7 @@ func GetAllTradingPairs(statedb *state.StateDB) (map[common.Hash]bool, error) { toTokenSlot := new(big.Int).Add(locBig, RelayerStructMappingSlot["_toTokens"]) toTokenLength := statedb.GetState(common.HexToAddress(common.RelayerRegistrationSMC), common.BigToHash(toTokenSlot)).Big().Uint64() if toTokenLength != fromTokenLength { - return map[common.Hash]bool{}, fmt.Errorf("Invalid length from token & to toke : from :%d , to :%d ", fromTokenLength, toTokenLength) + return map[common.Hash]bool{}, fmt.Errorf("invalid length from token & to token: from :%d , to :%d ", fromTokenLength, toTokenLength) } fromTokens := []common.Address{} fromTokenSlotHash := common.BytesToHash(fromTokenSlot.Bytes()) @@ -279,7 +279,7 @@ func CheckAddTokenBalance(addr common.Address, value *big.Int, token common.Addr newBalance := new(big.Int).Add(balance, value) log.Debug("CheckAddTokenBalance settle balance: ADD TOKEN BALANCE ", "token", token.String(), "address", addr.String(), "balance", balance, "value", value, "newBalance", newBalance) if common.BigToHash(newBalance).Big().Cmp(newBalance) != 0 { - return nil, fmt.Errorf("Overflow when try add token balance , max is 2^256 , balance : %v , value:%v ", balance, value) + return nil, fmt.Errorf("overflow when try add token balance , max is 2^256 , balance : %v , value : %v", balance, value) } else { return newBalance, nil } diff --git a/XDCx/tradingstate/statedb.go b/XDCx/tradingstate/statedb.go index 68d1278d95ec..e5e4f4b06fba 100644 --- a/XDCx/tradingstate/statedb.go +++ b/XDCx/tradingstate/statedb.go @@ -229,7 +229,7 @@ func (self *TradingStateDB) SubAmountOrderItem(orderBook common.Hash, orderId co priceHash := common.BigToHash(price) stateObject := self.GetOrNewStateExchangeObject(orderBook) if stateObject == nil { - return fmt.Errorf("Order book not found : %s ", orderBook.Hex()) + return fmt.Errorf("not found orderBook: %s", orderBook.Hex()) } var stateOrderList *stateOrderList switch side { @@ -238,18 +238,18 @@ func (self *TradingStateDB) SubAmountOrderItem(orderBook common.Hash, orderId co case Bid: stateOrderList = stateObject.getStateBidOrderListObject(self.db, priceHash) default: - return fmt.Errorf("Order type not found : %s ", side) + return fmt.Errorf("not found order type: %s", side) } if stateOrderList == nil || stateOrderList.empty() { - return fmt.Errorf("Order list empty order book : %s , order id : %s , price : %s ", orderBook, orderId.Hex(), priceHash.Hex()) + return fmt.Errorf("empty Orderlist: order book: %s , order id : %s , price : %s", orderBook, orderId.Hex(), priceHash.Hex()) } stateOrderItem := stateObject.getStateOrderObject(self.db, orderId) if stateOrderItem == nil || stateOrderItem.empty() { - return fmt.Errorf("Order item empty order book : %s , order id : %s , price : %s ", orderBook, orderId.Hex(), priceHash.Hex()) + return fmt.Errorf("empty OrderItem: order book: %s , order id : %s , price : %s", orderBook, orderId.Hex(), priceHash.Hex()) } currentAmount := new(big.Int).SetBytes(stateOrderList.GetOrderAmount(self.db, orderId).Bytes()[:]) if currentAmount.Cmp(amount) < 0 { - return fmt.Errorf("Order amount not enough : %s , have : %d , want : %d ", orderId.Hex(), currentAmount, amount) + return fmt.Errorf("not enough order amount: %s , have : %d , want : %d ", orderId.Hex(), currentAmount, amount) } self.journal = append(self.journal, subAmountOrder{ orderBook: orderBook, @@ -282,11 +282,11 @@ func (self *TradingStateDB) CancelOrder(orderBook common.Hash, order *OrderItem) orderIdHash := common.BigToHash(new(big.Int).SetUint64(order.OrderID)) stateObject := self.GetOrNewStateExchangeObject(orderBook) if stateObject == nil { - return fmt.Errorf("Order book not found : %s ", orderBook.Hex()) + return fmt.Errorf("not found orderBook: %s", orderBook.Hex()) } stateOrderItem := stateObject.getStateOrderObject(self.db, orderIdHash) if stateOrderItem == nil || stateOrderItem.empty() { - return fmt.Errorf("Order item empty order book : %s , order id : %s ", orderBook, orderIdHash.Hex()) + return fmt.Errorf("empty OrderItem: order book: %s , order id : %s", orderBook, orderIdHash.Hex()) } priceHash := common.BigToHash(stateOrderItem.data.Price) var stateOrderList *stateOrderList @@ -296,20 +296,20 @@ func (self *TradingStateDB) CancelOrder(orderBook common.Hash, order *OrderItem) case Bid: stateOrderList = stateObject.getStateBidOrderListObject(self.db, priceHash) default: - return fmt.Errorf("Order side not found : %s ", order.Side) + return fmt.Errorf("not found order.Side: %s", order.Side) } if stateOrderList == nil || stateOrderList.empty() { - return fmt.Errorf("Order list empty order book : %s , order id : %s , price : %s ", orderBook, orderIdHash.Hex(), priceHash.Hex()) + return fmt.Errorf("empty OrderList: order book: %s , order id : %s , price : %s", orderBook, orderIdHash.Hex(), priceHash.Hex()) } if stateOrderItem.data.UserAddress != order.UserAddress { - return fmt.Errorf("Error Order User Address mismatch when cancel order book : %s , order id : %s , got : %s , expect : %s ", orderBook, orderIdHash.Hex(), stateOrderItem.data.UserAddress.Hex(), order.UserAddress.Hex()) + return fmt.Errorf("error Order UserAddress mismatch when cancel: order book: %s , order id : %s , got : %s , expect : %s", orderBook, orderIdHash.Hex(), stateOrderItem.data.UserAddress.Hex(), order.UserAddress.Hex()) } if stateOrderItem.data.Hash != order.Hash { - return fmt.Errorf("Invalid order hash : got : %s , expect : %s ", order.Hash.Hex(), stateOrderItem.data.Hash.Hex()) + return fmt.Errorf("invalid order hash: got : %s , expect : %s", order.Hash.Hex(), stateOrderItem.data.Hash.Hex()) } if stateOrderItem.data.ExchangeAddress != order.ExchangeAddress { - return fmt.Errorf("Exchange Address mismatch when cancel. order book : %s , order id : %s , got : %s , expect : %s ", orderBook, orderIdHash.Hex(), order.ExchangeAddress.Hex(), stateOrderItem.data.ExchangeAddress.Hex()) + return fmt.Errorf("mismatch ExchangeAddress when cancel: order book : %s , order id : %s , got : %s , expect : %s", orderBook, orderIdHash.Hex(), order.ExchangeAddress.Hex(), stateOrderItem.data.ExchangeAddress.Hex()) } self.journal = append(self.journal, cancelOrder{ orderBook: orderBook, @@ -396,7 +396,7 @@ func (self *TradingStateDB) GetBestOrderIdAndAmount(orderBook common.Hash, price case Bid: stateOrderList = stateObject.getStateBidOrderListObject(self.db, common.BigToHash(price)) default: - return EmptyHash, Zero, fmt.Errorf("not found side :%s ", side) + return EmptyHash, Zero, fmt.Errorf("not found side: %s", side) } if stateOrderList != nil { key, _, err := stateOrderList.getTrie(self.db).TryGetBestLeftKeyAndValue() @@ -407,9 +407,9 @@ func (self *TradingStateDB) GetBestOrderIdAndAmount(orderBook common.Hash, price amount := stateOrderList.GetOrderAmount(self.db, orderId) return orderId, new(big.Int).SetBytes(amount.Bytes()), nil } - return EmptyHash, Zero, fmt.Errorf("not found order list with orderBook : %s , price : %d , side :%s ", orderBook.Hex(), price, side) + return EmptyHash, Zero, fmt.Errorf("not found order list with orderBook: %s , price : %d , side : %s", orderBook.Hex(), price, side) } - return EmptyHash, Zero, fmt.Errorf("not found orderBook : %s ", orderBook.Hex()) + return EmptyHash, Zero, fmt.Errorf("not found orderBook: %s", orderBook.Hex()) } // updateStateExchangeObject writes the given object to the trie. @@ -699,18 +699,18 @@ func (self *TradingStateDB) RemoveLiquidationPrice(orderBook common.Hash, price priceHash := common.BigToHash(price) orderbookState := self.getStateExchangeObject(orderBook) if orderbookState == nil { - return fmt.Errorf("order book not found : %s ", orderBook.Hex()) + return fmt.Errorf("not found order book: %s", orderBook.Hex()) } liquidationPriceState := orderbookState.getStateLiquidationPrice(self.db, priceHash) if liquidationPriceState == nil { - return fmt.Errorf("liquidation price not found : %s , %s ", orderBook.Hex(), priceHash.Hex()) + return fmt.Errorf("not found liquidation price: %s , %s", orderBook.Hex(), priceHash.Hex()) } lendingBookState := liquidationPriceState.getStateLendingBook(self.db, lendingBook) if lendingBookState == nil { - return fmt.Errorf("lending book not found : %s , %s ,%s ", orderBook.Hex(), priceHash.Hex(), lendingBook.Hex()) + return fmt.Errorf("not found lending book: %s , %s ,%s", orderBook.Hex(), priceHash.Hex(), lendingBook.Hex()) } if !lendingBookState.Exist(self.db, tradeIdHash) { - return fmt.Errorf("trade id not found : %s , %s ,%s , %d ", orderBook.Hex(), priceHash.Hex(), lendingBook.Hex(), tradeId) + return fmt.Errorf("not found trade id: %s , %s ,%s , %d ", orderBook.Hex(), priceHash.Hex(), lendingBook.Hex(), tradeId) } lendingBookState.removeTradingId(self.db, tradeIdHash) lendingBookState.subVolume(One) diff --git a/XDCxlending/XDCxlending.go b/XDCxlending/XDCxlending.go index 48ff54077844..e110e68dfdd1 100644 --- a/XDCxlending/XDCxlending.go +++ b/XDCxlending/XDCxlending.go @@ -665,7 +665,7 @@ func (l *Lending) GetLendingState(block *types.Block, author common.Address) (*l return nil, err } if l.StateCache == nil { - return nil, errors.New("Not initialized XDCx") + return nil, errors.New("not initialized XDCx") } state, err := lendingstate.New(root, l.StateCache) if err != nil { diff --git a/XDCxlending/lendingstate/dump.go b/XDCxlending/lendingstate/dump.go index 10dc070862fb..2b26d43099d3 100644 --- a/XDCxlending/lendingstate/dump.go +++ b/XDCxlending/lendingstate/dump.go @@ -18,11 +18,11 @@ package lendingstate import ( "fmt" - "github.com/XinFinOrg/XDPoSChain/rlp" "math/big" "sort" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/rlp" "github.com/XinFinOrg/XDPoSChain/trie" ) @@ -42,7 +42,7 @@ type DumpOrderBookInfo struct { func (self *LendingStateDB) DumpInvestingTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) { exhangeObject := self.getLendingExchange(orderBook) if exhangeObject == nil { - return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex()) + return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]DumpOrderList{} it := trie.NewIterator(exhangeObject.getInvestingTrie(self.db).NodeIterator(nil)) @@ -57,7 +57,7 @@ func (self *LendingStateDB) DumpInvestingTrie(orderBook common.Hash) (map[*big.I } else { var data itemList if err := rlp.DecodeBytes(it.Value, &data); err != nil { - return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,interest :%v ", orderBook.Hex(), interest) + return nil, fmt.Errorf("fail when decode order iist orderBook: %v , interest : %v", orderBook.Hex(), interest) } stateOrderList := newItemListState(orderBook, interestHash, data, nil) mapResult[interest] = stateOrderList.DumpItemList(self.db) @@ -85,7 +85,7 @@ func (self *LendingStateDB) DumpInvestingTrie(orderBook common.Hash) (map[*big.I func (self *LendingStateDB) DumpBorrowingTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) { exhangeObject := self.getLendingExchange(orderBook) if exhangeObject == nil { - return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex()) + return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]DumpOrderList{} it := trie.NewIterator(exhangeObject.getBorrowingTrie(self.db).NodeIterator(nil)) @@ -100,7 +100,7 @@ func (self *LendingStateDB) DumpBorrowingTrie(orderBook common.Hash) (map[*big.I } else { var data itemList if err := rlp.DecodeBytes(it.Value, &data); err != nil { - return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,interest :%v ", orderBook.Hex(), interest) + return nil, fmt.Errorf("fail when decode order iist orderBook: %v , interest : %v", orderBook.Hex(), interest) } stateOrderList := newItemListState(orderBook, interestHash, data, nil) mapResult[interest] = stateOrderList.DumpItemList(self.db) @@ -128,7 +128,7 @@ func (self *LendingStateDB) DumpBorrowingTrie(orderBook common.Hash) (map[*big.I func (self *LendingStateDB) GetInvestings(orderBook common.Hash) (map[*big.Int]*big.Int, error) { exhangeObject := self.getLendingExchange(orderBook) if exhangeObject == nil { - return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex()) + return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]*big.Int{} it := trie.NewIterator(exhangeObject.getInvestingTrie(self.db).NodeIterator(nil)) @@ -143,7 +143,7 @@ func (self *LendingStateDB) GetInvestings(orderBook common.Hash) (map[*big.Int]* } else { var data itemList if err := rlp.DecodeBytes(it.Value, &data); err != nil { - return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,interest :%v ", orderBook.Hex(), interest) + return nil, fmt.Errorf("fail when decode order iist orderBook: %v , interest : %v", orderBook.Hex(), interest) } stateOrderList := newItemListState(orderBook, interestHash, data, nil) mapResult[interest] = stateOrderList.data.Volume @@ -171,7 +171,7 @@ func (self *LendingStateDB) GetInvestings(orderBook common.Hash) (map[*big.Int]* func (self *LendingStateDB) GetBorrowings(orderBook common.Hash) (map[*big.Int]*big.Int, error) { exhangeObject := self.getLendingExchange(orderBook) if exhangeObject == nil { - return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex()) + return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]*big.Int{} it := trie.NewIterator(exhangeObject.getBorrowingTrie(self.db).NodeIterator(nil)) @@ -186,7 +186,7 @@ func (self *LendingStateDB) GetBorrowings(orderBook common.Hash) (map[*big.Int]* } else { var data itemList if err := rlp.DecodeBytes(it.Value, &data); err != nil { - return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,interest :%v ", orderBook.Hex(), interest) + return nil, fmt.Errorf("fail when decode order iist orderBook: %v , interest : %v", orderBook.Hex(), interest) } stateOrderList := newItemListState(orderBook, interestHash, data, nil) mapResult[interest] = stateOrderList.data.Volume @@ -248,7 +248,7 @@ func (self *itemListState) DumpItemList(db Database) DumpOrderList { func (self *LendingStateDB) DumpOrderBookInfo(orderBook common.Hash) (*DumpOrderBookInfo, error) { exhangeObject := self.getLendingExchange(orderBook) if exhangeObject == nil { - return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex()) + return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } result := &DumpOrderBookInfo{} result.Nonce = exhangeObject.data.Nonce @@ -296,7 +296,7 @@ func (self *liquidationTimeState) DumpItemList(db Database) DumpOrderList { func (self *LendingStateDB) DumpLiquidationTimeTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) { exhangeObject := self.getLendingExchange(orderBook) if exhangeObject == nil { - return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex()) + return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]DumpOrderList{} it := trie.NewIterator(exhangeObject.getLiquidationTimeTrie(self.db).NodeIterator(nil)) @@ -311,7 +311,7 @@ func (self *LendingStateDB) DumpLiquidationTimeTrie(orderBook common.Hash) (map[ } else { var data itemList if err := rlp.DecodeBytes(it.Value, &data); err != nil { - return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,unixTime :%v ", orderBook.Hex(), unixTime) + return nil, fmt.Errorf("fail when decode order iist orderBook: %v , unixTime : %v", orderBook.Hex(), unixTime) } stateOrderList := newLiquidationTimeState(orderBook, unixTimeHash, data, nil) mapResult[unixTime] = stateOrderList.DumpItemList(self.db) @@ -339,7 +339,7 @@ func (self *LendingStateDB) DumpLiquidationTimeTrie(orderBook common.Hash) (map[ func (self *LendingStateDB) DumpLendingOrderTrie(orderBook common.Hash) (map[*big.Int]LendingItem, error) { exhangeObject := self.getLendingExchange(orderBook) if exhangeObject == nil { - return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex()) + return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]LendingItem{} it := trie.NewIterator(exhangeObject.getLendingItemTrie(self.db).NodeIterator(nil)) @@ -354,7 +354,7 @@ func (self *LendingStateDB) DumpLendingOrderTrie(orderBook common.Hash) (map[*bi } else { var data LendingItem if err := rlp.DecodeBytes(it.Value, &data); err != nil { - return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,orderId :%v ", orderBook.Hex(), orderId) + return nil, fmt.Errorf("fail when decode order iist orderBook: %v , orderId : %v", orderBook.Hex(), orderId) } mapResult[orderId] = data } @@ -379,7 +379,7 @@ func (self *LendingStateDB) DumpLendingOrderTrie(orderBook common.Hash) (map[*bi func (self *LendingStateDB) DumpLendingTradeTrie(orderBook common.Hash) (map[*big.Int]LendingTrade, error) { exhangeObject := self.getLendingExchange(orderBook) if exhangeObject == nil { - return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex()) + return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]LendingTrade{} it := trie.NewIterator(exhangeObject.getLendingTradeTrie(self.db).NodeIterator(nil)) @@ -394,7 +394,7 @@ func (self *LendingStateDB) DumpLendingTradeTrie(orderBook common.Hash) (map[*bi } else { var data LendingTrade if err := rlp.DecodeBytes(it.Value, &data); err != nil { - return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,tradeId :%v ", orderBook.Hex(), tradeId) + return nil, fmt.Errorf("fail when decode order iist orderBook: %v , tradeId : %v", orderBook.Hex(), tradeId) } mapResult[tradeId] = data } diff --git a/XDCxlending/lendingstate/relayer.go b/XDCxlending/lendingstate/relayer.go index 536784741bd7..56ee05a4c267 100644 --- a/XDCxlending/lendingstate/relayer.go +++ b/XDCxlending/lendingstate/relayer.go @@ -235,7 +235,7 @@ func CheckAddTokenBalance(addr common.Address, value *big.Int, token common.Addr newBalance := new(big.Int).Add(balance, value) log.Debug("CheckAddTokenBalance settle balance: ADD TOKEN BALANCE ", "token", token.String(), "address", addr.String(), "balance", balance, "value", value, "newBalance", newBalance) if common.BigToHash(newBalance).Big().Cmp(newBalance) != 0 { - return nil, fmt.Errorf("Overflow when try add token balance , max is 2^256 , balance : %v , value:%v ", balance, value) + return nil, fmt.Errorf("overflow when try add token balance , max is 2^256 , balance : %v , value : %v", balance, value) } else { return newBalance, nil } diff --git a/XDCxlending/lendingstate/statedb.go b/XDCxlending/lendingstate/statedb.go index f514f61ba0b6..ee029a862b8c 100644 --- a/XDCxlending/lendingstate/statedb.go +++ b/XDCxlending/lendingstate/statedb.go @@ -241,7 +241,7 @@ func (self *LendingStateDB) SubAmountLendingItem(orderBook common.Hash, orderId priceHash := common.BigToHash(price) lendingExchange := self.GetOrNewLendingExchangeObject(orderBook) if lendingExchange == nil { - return fmt.Errorf("Order book not found : %s ", orderBook.Hex()) + return fmt.Errorf("not found order book: %s", orderBook.Hex()) } var orderList *itemListState switch side { @@ -250,18 +250,18 @@ func (self *LendingStateDB) SubAmountLendingItem(orderBook common.Hash, orderId case Borrowing: orderList = lendingExchange.getBorrowingOrderList(self.db, priceHash) default: - return fmt.Errorf("Order type not found : %s ", side) + return fmt.Errorf("not found order type: %s", side) } if orderList == nil || orderList.empty() { - return fmt.Errorf("Order list empty order book : %s , order id : %s , key : %s ", orderBook, orderId.Hex(), priceHash.Hex()) + return fmt.Errorf("empty orderList: order book : %s , order id : %s , key : %s", orderBook, orderId.Hex(), priceHash.Hex()) } lendingItem := lendingExchange.getLendingItem(self.db, orderId) if lendingItem == nil || lendingItem.empty() { - return fmt.Errorf("Order item empty order book : %s , order id : %s , key : %s ", orderBook, orderId.Hex(), priceHash.Hex()) + return fmt.Errorf("empty order item: order book : %s , order id : %s , key : %s", orderBook, orderId.Hex(), priceHash.Hex()) } currentAmount := new(big.Int).SetBytes(orderList.GetOrderAmount(self.db, orderId).Bytes()[:]) if currentAmount.Cmp(amount) < 0 { - return fmt.Errorf("Order amount not enough : %s , have : %d , want : %d ", orderId.Hex(), currentAmount, amount) + return fmt.Errorf("not enough order amount %s: have : %d , want : %d", orderId.Hex(), currentAmount, amount) } self.journal = append(self.journal, subAmountOrder{ orderBook: orderBook, @@ -295,7 +295,7 @@ func (self *LendingStateDB) CancelLendingOrder(orderBook common.Hash, order *Len orderIdHash := common.BigToHash(new(big.Int).SetUint64(order.LendingId)) stateObject := self.GetOrNewLendingExchangeObject(orderBook) if stateObject == nil { - return fmt.Errorf("Order book not found : %s ", orderBook.Hex()) + return fmt.Errorf("not found order book: %s", orderBook.Hex()) } lendingItem := stateObject.getLendingItem(self.db, orderIdHash) var orderList *itemListState @@ -305,16 +305,16 @@ func (self *LendingStateDB) CancelLendingOrder(orderBook common.Hash, order *Len case Borrowing: orderList = stateObject.getBorrowingOrderList(self.db, interestHash) default: - return fmt.Errorf("Order side not found : %s ", order.Side) + return fmt.Errorf("not found order side: %s", order.Side) } if orderList == nil || orderList.empty() { - return fmt.Errorf("Order list empty order book : %s , order id : %s , key : %s ", orderBook, orderIdHash.Hex(), interestHash.Hex()) + return fmt.Errorf("empty OrderList: order book : %s , order id : %s , key : %s", orderBook, orderIdHash.Hex(), interestHash.Hex()) } if lendingItem == nil || lendingItem.empty() { - return fmt.Errorf("Order item empty order book : %s , order id : %s , key : %s ", orderBook, orderIdHash.Hex(), interestHash.Hex()) + return fmt.Errorf("empty order item: order book : %s , order id : %s , key : %s", orderBook, orderIdHash.Hex(), interestHash.Hex()) } if lendingItem.data.UserAddress != order.UserAddress { - return fmt.Errorf("Error Order User Address mismatch when cancel order book : %s , order id : %s , got : %s , expect : %s ", orderBook, orderIdHash.Hex(), lendingItem.data.UserAddress.Hex(), order.UserAddress.Hex()) + return fmt.Errorf("error Order UserAddress mismatch when cancel order book: %s , order id : %s , got : %s , expect : %s", orderBook, orderIdHash.Hex(), lendingItem.data.UserAddress.Hex(), order.UserAddress.Hex()) } self.journal = append(self.journal, cancelOrder{ orderBook: orderBook, @@ -381,7 +381,7 @@ func (self *LendingStateDB) GetBestLendingIdAndAmount(orderBook common.Hash, pri case Borrowing: stateOrderList = stateObject.getBorrowingOrderList(self.db, common.BigToHash(price)) default: - return EmptyHash, Zero, fmt.Errorf("not found side :%s ", side) + return EmptyHash, Zero, fmt.Errorf("not found side: %s", side) } if stateOrderList != nil { key, _, err := stateOrderList.getTrie(self.db).TryGetBestLeftKeyAndValue() @@ -392,9 +392,9 @@ func (self *LendingStateDB) GetBestLendingIdAndAmount(orderBook common.Hash, pri amount := stateOrderList.GetOrderAmount(self.db, orderId) return orderId, new(big.Int).SetBytes(amount.Bytes()), nil } - return EmptyHash, Zero, fmt.Errorf("not found order list with orderBook : %s , key : %d , side :%s ", orderBook.Hex(), price, side) + return EmptyHash, Zero, fmt.Errorf("not found order list with orderBook: %s , key : %d , side : %s", orderBook.Hex(), price, side) } - return EmptyHash, Zero, fmt.Errorf("not found orderBook : %s ", orderBook.Hex()) + return EmptyHash, Zero, fmt.Errorf("not found orderBook: %s", orderBook.Hex()) } // updateLendingExchange writes the given object to the trie. @@ -621,14 +621,14 @@ func (self *LendingStateDB) RemoveLiquidationTime(lendingBook common.Hash, trade tradeIdHash := common.Uint64ToHash(tradeId) lendingExchangeState := self.getLendingExchange(lendingBook) if lendingExchangeState == nil { - return fmt.Errorf("lending book not found : %s ", lendingBook.Hex()) + return fmt.Errorf("lending book not found: %s", lendingBook.Hex()) } liquidationTime := lendingExchangeState.getLiquidationTimeOrderList(self.db, timeHash) if liquidationTime == nil { - return fmt.Errorf("liquidation time not found : %s , %d ", lendingBook.Hex(), time) + return fmt.Errorf("not found liquidation time: %s , %d", lendingBook.Hex(), time) } if !liquidationTime.Exist(self.db, tradeIdHash) { - return fmt.Errorf("tradeId not exist : %s , %d , %d ", lendingBook.Hex(), time, tradeId) + return fmt.Errorf("not exist tradeId: %s , %d , %d", lendingBook.Hex(), time, tradeId) } liquidationTime.removeTradeId(self.db, tradeIdHash) liquidationTime.subVolume(One) @@ -659,11 +659,11 @@ func (self *LendingStateDB) CancelLendingTrade(orderBook common.Hash, tradeId ui tradeIdHash := common.Uint64ToHash(tradeId) stateObject := self.GetOrNewLendingExchangeObject(orderBook) if stateObject == nil { - return fmt.Errorf("Order book not found : %s ", orderBook.Hex()) + return fmt.Errorf("not found order book: %s", orderBook.Hex()) } lendingTrade := stateObject.getLendingTrade(self.db, tradeIdHash) if lendingTrade == nil || lendingTrade.empty() { - return fmt.Errorf("lending trade empty order book : %s , trade id : %s , trade id hash : %s ", orderBook, tradeIdHash.Hex(), tradeIdHash.Hex()) + return fmt.Errorf("lending trade empty order book: %s , trade id : %s , trade id hash : %s", orderBook, tradeIdHash.Hex(), tradeIdHash.Hex()) } self.journal = append(self.journal, cancelTrading{ orderBook: orderBook, diff --git a/XDCxlending/order_processor.go b/XDCxlending/order_processor.go index 5af2d2771314..40f01d32b00c 100644 --- a/XDCxlending/order_processor.go +++ b/XDCxlending/order_processor.go @@ -438,7 +438,7 @@ func (l *Lending) getLendQuantity( } LendingTokenDecimal, err := l.XDCx.GetTokenDecimal(chain, statedb, makerOrder.LendingToken) if err != nil || LendingTokenDecimal.Sign() == 0 { - return lendingstate.Zero, lendingstate.Zero, false, nil, fmt.Errorf("Fail to get tokenDecimal. Token: %v . Err: %v", makerOrder.LendingToken.String(), err) + return lendingstate.Zero, lendingstate.Zero, false, nil, fmt.Errorf("fail to get tokenDecimal: Token: %v . Err: %v", makerOrder.LendingToken.String(), err) } collateralToken := makerOrder.CollateralToken if takerOrder.Side == lendingstate.Borrowing { @@ -446,7 +446,7 @@ func (l *Lending) getLendQuantity( } collateralTokenDecimal, err := l.XDCx.GetTokenDecimal(chain, statedb, collateralToken) if err != nil || collateralTokenDecimal.Sign() == 0 { - return lendingstate.Zero, lendingstate.Zero, false, nil, fmt.Errorf("fail to get tokenDecimal. Token: %v . Err: %v", collateralToken.String(), err) + return lendingstate.Zero, lendingstate.Zero, false, nil, fmt.Errorf("fail to get tokenDecimal: Token: %v . Err: %v", collateralToken.String(), err) } if takerOrder.Relayer == makerOrder.Relayer { if err := lendingstate.CheckRelayerFee(takerOrder.Relayer, new(big.Int).Mul(common.RelayerLendingFee, big.NewInt(2)), statedb); err != nil { @@ -582,7 +582,7 @@ func DoSettleBalance(coinbase common.Address, takerOrder, makerOrder *lendingsta matchingFee = new(big.Int).Add(matchingFee, common.RelayerLendingFee) if common.EmptyHash(takerExOwner.Hash()) || common.EmptyHash(makerExOwner.Hash()) { - return fmt.Errorf("Echange owner empty , Taker: %v , maker : %v ", takerExOwner, makerExOwner) + return fmt.Errorf("empty echange owner: taker: %v , maker : %v", takerExOwner, makerExOwner) } mapBalances := map[common.Address]map[common.Address]*big.Int{} //Checking balance @@ -971,11 +971,11 @@ func (l *Lending) GetMediumTradePriceBeforeEpoch(chain consensus.ChainContext, s if inversePrice != nil && inversePrice.Sign() > 0 { quoteTokenDecimal, err := l.XDCx.GetTokenDecimal(chain, statedb, quoteToken) if err != nil || quoteTokenDecimal.Sign() == 0 { - return nil, fmt.Errorf("Fail to get tokenDecimal. Token: %v . Err: %v", quoteToken.String(), err) + return nil, fmt.Errorf("fail to get tokenDecimal: Token: %v . Err: %v", quoteToken.String(), err) } baseTokenDecimal, err := l.XDCx.GetTokenDecimal(chain, statedb, baseToken) if err != nil || baseTokenDecimal.Sign() == 0 { - return nil, fmt.Errorf("Fail to get tokenDecimal. Token: %v . Err: %v", baseToken, err) + return nil, fmt.Errorf("fail to get tokenDecimal: Token: %v . Err: %v", baseToken, err) } price = new(big.Int).Mul(baseTokenDecimal, quoteTokenDecimal) price = new(big.Int).Div(price, inversePrice) @@ -1098,7 +1098,7 @@ func (l *Lending) AutoTopUp(statedb *state.StateDB, tradingState *tradingstate.T return nil, fmt.Errorf("process deposit for emptyLendingTrade is not allowed. lendingTradeId: %v", lendingTradeId.Hex()) } if currentPrice.Cmp(lendingTrade.LiquidationPrice) >= 0 { - return nil, fmt.Errorf("CurrentPrice is still higher than or equal to LiquidationPrice. current price: %v , liquidation price : %v ", currentPrice, lendingTrade.LiquidationPrice) + return nil, fmt.Errorf("currentPrice is still higher than or equal to LiquidationPrice. current price: %v , liquidation price : %v ", currentPrice, lendingTrade.LiquidationPrice) } // newLiquidationPrice = currentPrice * 90% newLiquidationPrice := new(big.Int).Mul(currentPrice, common.RateTopUp) @@ -1165,7 +1165,7 @@ func (l *Lending) ProcessRepayLendingTrade(header *types.Header, chain consensus if tokenBalance.Cmp(paymentBalance) < 0 { if lendingTrade.LiquidationTime > time { - return nil, fmt.Errorf("Not enough balance need : %s , have : %s ", paymentBalance, tokenBalance) + return nil, fmt.Errorf("not enough balance need : %s , have : %s", paymentBalance, tokenBalance) } newLendingTrade := &lendingstate.LendingTrade{} var err error diff --git a/accounts/abi/type_test.go b/accounts/abi/type_test.go index 7f6f0b4a0a76..cb9787854c96 100644 --- a/accounts/abi/type_test.go +++ b/accounts/abi/type_test.go @@ -102,7 +102,7 @@ func TestTypeRegexp(t *testing.T) { t.Errorf("type %q: failed to parse type string: %v", tt.blob, err) } if !reflect.DeepEqual(typ, tt.kind) { - t.Errorf("type %q: parsed type mismatch:\nGOT %s\nWANT %s ", tt.blob, spew.Sdump(typeWithoutStringer(typ)), spew.Sdump(typeWithoutStringer(tt.kind))) + t.Errorf("type %q: parsed type mismatch:\nGOT %s\nWANT %s", tt.blob, spew.Sdump(typeWithoutStringer(typ)), spew.Sdump(typeWithoutStringer(tt.kind))) } } } diff --git a/accounts/keystore/keystore_passphrase.go b/accounts/keystore/keystore_passphrase.go index 9e597678dad8..a58d152b1c2d 100644 --- a/accounts/keystore/keystore_passphrase.go +++ b/accounts/keystore/keystore_passphrase.go @@ -201,11 +201,11 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) { func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) { if keyProtected.Version != version { - return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version) + return nil, nil, fmt.Errorf("not supported Version: %v", keyProtected.Version) } if keyProtected.Crypto.Cipher != "aes-128-ctr" { - return nil, nil, fmt.Errorf("Cipher not supported: %v", keyProtected.Crypto.Cipher) + return nil, nil, fmt.Errorf("not supported Cipher: %v", keyProtected.Crypto.Cipher) } keyId = uuid.Parse(keyProtected.Id) @@ -293,13 +293,13 @@ func getKDFKey(cryptoJSON cryptoJSON, auth string) ([]byte, error) { c := ensureInt(cryptoJSON.KDFParams["c"]) prf := cryptoJSON.KDFParams["prf"].(string) if prf != "hmac-sha256" { - return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf) + return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf) } key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New) return key, nil } - return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF) + return nil, fmt.Errorf("unsupported KDF: %s", cryptoJSON.KDF) } // TODO: can we do without this when unmarshalling dynamic JSON? diff --git a/accounts/keystore/keystore_plain_test.go b/accounts/keystore/keystore_plain_test.go index 73a58b507fcc..4a977d2eaca8 100644 --- a/accounts/keystore/keystore_plain_test.go +++ b/accounts/keystore/keystore_plain_test.go @@ -211,7 +211,7 @@ func testDecryptV3(test KeyStoreTestV3, t *testing.T) { } privHex := hex.EncodeToString(privBytes) if test.Priv != privHex { - t.Fatal(fmt.Errorf("Decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex)) + t.Fatal(fmt.Errorf("decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex)) } } @@ -222,7 +222,7 @@ func testDecryptV1(test KeyStoreTestV1, t *testing.T) { } privHex := hex.EncodeToString(privBytes) if test.Priv != privHex { - t.Fatal(fmt.Errorf("Decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex)) + t.Fatal(fmt.Errorf("decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex)) } } diff --git a/accounts/usbwallet/ledger.go b/accounts/usbwallet/ledger.go index d2259eacd01a..a1091add84e0 100644 --- a/accounts/usbwallet/ledger.go +++ b/accounts/usbwallet/ledger.go @@ -164,7 +164,7 @@ func (w *ledgerDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio } // Ensure the wallet is capable of signing the given transaction if chainID != nil && w.version[0] <= 1 && w.version[1] <= 0 && w.version[2] <= 2 { - return common.Address{}, nil, fmt.Errorf("Ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2]) + return common.Address{}, nil, fmt.Errorf("ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2]) } // All infos gathered and metadata checks out, request signing return w.ledgerSign(path, tx, chainID) @@ -175,18 +175,18 @@ func (w *ledgerDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio // // The version retrieval protocol is defined as follows: // -// CLA | INS | P1 | P2 | Lc | Le -// ----+-----+----+----+----+--- -// E0 | 06 | 00 | 00 | 00 | 04 +// CLA | INS | P1 | P2 | Lc | Le +// ----+-----+----+----+----+--- +// E0 | 06 | 00 | 00 | 00 | 04 // // With no input data, and the output data being: // -// Description | Length -// ---------------------------------------------------+-------- -// Flags 01: arbitrary data signature enabled by user | 1 byte -// Application major version | 1 byte -// Application minor version | 1 byte -// Application patch version | 1 byte +// Description | Length +// ---------------------------------------------------+-------- +// Flags 01: arbitrary data signature enabled by user | 1 byte +// Application major version | 1 byte +// Application minor version | 1 byte +// Application patch version | 1 byte func (w *ledgerDriver) ledgerVersion() ([3]byte, error) { // Send the request and wait for the response reply, err := w.ledgerExchange(ledgerOpGetConfiguration, 0, 0, nil) @@ -207,32 +207,32 @@ func (w *ledgerDriver) ledgerVersion() ([3]byte, error) { // // The address derivation protocol is defined as follows: // -// CLA | INS | P1 | P2 | Lc | Le -// ----+-----+----+----+-----+--- -// E0 | 02 | 00 return address -// 01 display address and confirm before returning -// | 00: do not return the chain code -// | 01: return the chain code -// | var | 00 +// CLA | INS | P1 | P2 | Lc | Le +// ----+-----+----+----+-----+--- +// E0 | 02 | 00 return address +// 01 display address and confirm before returning +// | 00: do not return the chain code +// | 01: return the chain code +// | var | 00 // // Where the input data is: // -// Description | Length -// -------------------------------------------------+-------- -// Number of BIP 32 derivations to perform (max 10) | 1 byte -// First derivation index (big endian) | 4 bytes -// ... | 4 bytes -// Last derivation index (big endian) | 4 bytes +// Description | Length +// -------------------------------------------------+-------- +// Number of BIP 32 derivations to perform (max 10) | 1 byte +// First derivation index (big endian) | 4 bytes +// ... | 4 bytes +// Last derivation index (big endian) | 4 bytes // // And the output data is: // -// Description | Length -// ------------------------+------------------- -// Public Key length | 1 byte -// Uncompressed Public Key | arbitrary -// Ethereum address length | 1 byte -// Ethereum address | 40 bytes hex ascii -// Chain code if requested | 32 bytes +// Description | Length +// ------------------------+------------------- +// Public Key length | 1 byte +// Uncompressed Public Key | arbitrary +// Ethereum address length | 1 byte +// Ethereum address | 40 bytes hex ascii +// Chain code if requested | 32 bytes func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, error) { // Flatten the derivation path into the Ledger request path := make([]byte, 1+4*len(derivationPath)) @@ -268,35 +268,35 @@ func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, er // // The transaction signing protocol is defined as follows: // -// CLA | INS | P1 | P2 | Lc | Le -// ----+-----+----+----+-----+--- -// E0 | 04 | 00: first transaction data block -// 80: subsequent transaction data block -// | 00 | variable | variable +// CLA | INS | P1 | P2 | Lc | Le +// ----+-----+----+----+-----+--- +// E0 | 04 | 00: first transaction data block +// 80: subsequent transaction data block +// | 00 | variable | variable // // Where the input for the first transaction block (first 255 bytes) is: // -// Description | Length -// -------------------------------------------------+---------- -// Number of BIP 32 derivations to perform (max 10) | 1 byte -// First derivation index (big endian) | 4 bytes -// ... | 4 bytes -// Last derivation index (big endian) | 4 bytes -// RLP transaction chunk | arbitrary +// Description | Length +// -------------------------------------------------+---------- +// Number of BIP 32 derivations to perform (max 10) | 1 byte +// First derivation index (big endian) | 4 bytes +// ... | 4 bytes +// Last derivation index (big endian) | 4 bytes +// RLP transaction chunk | arbitrary // // And the input for subsequent transaction blocks (first 255 bytes) are: // -// Description | Length -// ----------------------+---------- -// RLP transaction chunk | arbitrary +// Description | Length +// ----------------------+---------- +// RLP transaction chunk | arbitrary // // And the output data is: // -// Description | Length -// ------------+--------- -// signature V | 1 byte -// signature R | 32 bytes -// signature S | 32 bytes +// Description | Length +// ------------+--------- +// signature V | 1 byte +// signature R | 32 bytes +// signature S | 32 bytes func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error) { // Flatten the derivation path into the Ledger request path := make([]byte, 1+4*len(derivationPath)) @@ -370,12 +370,12 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction // // The common transport header is defined as follows: // -// Description | Length -// --------------------------------------+---------- -// Communication channel ID (big endian) | 2 bytes -// Command tag | 1 byte -// Packet sequence index (big endian) | 2 bytes -// Payload | arbitrary +// Description | Length +// --------------------------------------+---------- +// Communication channel ID (big endian) | 2 bytes +// Command tag | 1 byte +// Packet sequence index (big endian) | 2 bytes +// Payload | arbitrary // // The Communication channel ID allows commands multiplexing over the same // physical link. It is not used for the time being, and should be set to 0101 @@ -389,15 +389,15 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction // // APDU Command payloads are encoded as follows: // -// Description | Length -// ----------------------------------- -// APDU length (big endian) | 2 bytes -// APDU CLA | 1 byte -// APDU INS | 1 byte -// APDU P1 | 1 byte -// APDU P2 | 1 byte -// APDU length | 1 byte -// Optional APDU data | arbitrary +// Description | Length +// ----------------------------------- +// APDU length (big endian) | 2 bytes +// APDU CLA | 1 byte +// APDU INS | 1 byte +// APDU P1 | 1 byte +// APDU P2 | 1 byte +// APDU length | 1 byte +// Optional APDU data | arbitrary func (w *ledgerDriver) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 ledgerParam2, data []byte) ([]byte, error) { // Construct the message payload, possibly split into multiple chunks apdu := make([]byte, 2, 7+len(data)) diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go index f61909393d1b..d42713abbd98 100644 --- a/cmd/faucet/faucet.go +++ b/cmd/faucet/faucet.go @@ -396,7 +396,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { continue } if msg.Tier >= uint(*tiersFlag) { - if err = sendError(wsconn, errors.New("Invalid funding tier requested")); err != nil { + if err = sendError(wsconn, errors.New("invalid funding tier requested")); err != nil { log.Warn("Failed to send tier error to client", "err", err) return } @@ -433,7 +433,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { } if !result.Success { log.Warn("Captcha verification failed", "err", string(result.Errors)) - if err = sendError(wsconn, errors.New("Beep-bop, you're a robot!")); err != nil { + if err = sendError(wsconn, errors.New("beep-bop, you're a robot")); err != nil { log.Warn("Failed to send captcha failure to client", "err", err) return } @@ -462,7 +462,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { case *noauthFlag: username, avatar, address, err = authNoAuth(msg.URL) default: - err = errors.New("Something funky happened, please open an issue at https://github.com/XinFinOrg/XDPoSChain/issues") + err = errors.New("something funky happened, please open an issue at https://github.com/XinFinOrg/XDPoSChain/issues") } if err != nil { if err = sendError(wsconn, err); err != nil { @@ -679,7 +679,7 @@ func authGitHub(url string) (string, string, common.Address, error) { return "", "", common.Address{}, err } if gist.Owner.Login == "" { - return "", "", common.Address{}, errors.New("Anonymous Gists not allowed") + return "", "", common.Address{}, errors.New("anonymous gists not allowed") } // Iterate over all the files and look for Ethereum addresses var address common.Address @@ -690,7 +690,7 @@ func authGitHub(url string) (string, string, common.Address, error) { } } if address == (common.Address{}) { - return "", "", common.Address{}, errors.New("No Ethereum address found to fund") + return "", "", common.Address{}, errors.New("no Ethereum address found to fund") } // Validate the user's existence since the API is unhelpful here if res, err = http.Head("https://github.com/" + gist.Owner.Login); err != nil { @@ -699,7 +699,7 @@ func authGitHub(url string) (string, string, common.Address, error) { res.Body.Close() if res.StatusCode != 200 { - return "", "", common.Address{}, errors.New("Invalid user... boom!") + return "", "", common.Address{}, errors.New("invalid user... boom") } // Everything passed validation, return the gathered infos return gist.Owner.Login + "@github", fmt.Sprintf("https://github.com/%s.png?size=64", gist.Owner.Login), address, nil @@ -711,7 +711,7 @@ func authTwitter(url string) (string, string, common.Address, error) { // Ensure the user specified a meaningful URL, no fancy nonsense parts := strings.Split(url, "/") if len(parts) < 4 || parts[len(parts)-2] != "status" { - return "", "", common.Address{}, errors.New("Invalid Twitter status URL") + return "", "", common.Address{}, errors.New("invalid Twitter status URL") } // Twitter's API isn't really friendly with direct links. Still, we don't // want to do ask read permissions from users, so just load the public posts and @@ -725,7 +725,7 @@ func authTwitter(url string) (string, string, common.Address, error) { // Resolve the username from the final redirect, no intermediate junk parts = strings.Split(res.Request.URL.String(), "/") if len(parts) < 4 || parts[len(parts)-2] != "status" { - return "", "", common.Address{}, errors.New("Invalid Twitter status URL") + return "", "", common.Address{}, errors.New("invalid Twitter status URL") } username := parts[len(parts)-3] @@ -735,7 +735,7 @@ func authTwitter(url string) (string, string, common.Address, error) { } address := common.HexToAddress(string(regexp.MustCompile("0x[0-9a-fA-F]{40}").Find(body))) if address == (common.Address{}) { - return "", "", common.Address{}, errors.New("No Ethereum address found to fund") + return "", "", common.Address{}, errors.New("no Ethereum address found to fund") } var avatar string if parts = regexp.MustCompile("src=\"([^\"]+twimg.com/profile_images[^\"]+)\"").FindStringSubmatch(string(body)); len(parts) == 2 { @@ -750,7 +750,7 @@ func authGooglePlus(url string) (string, string, common.Address, error) { // Ensure the user specified a meaningful URL, no fancy nonsense parts := strings.Split(url, "/") if len(parts) < 4 || parts[len(parts)-2] != "posts" { - return "", "", common.Address{}, errors.New("Invalid Google+ post URL") + return "", "", common.Address{}, errors.New("invalid Google+ post URL") } username := parts[len(parts)-3] @@ -769,7 +769,7 @@ func authGooglePlus(url string) (string, string, common.Address, error) { } address := common.HexToAddress(string(regexp.MustCompile("0x[0-9a-fA-F]{40}").Find(body))) if address == (common.Address{}) { - return "", "", common.Address{}, errors.New("No Ethereum address found to fund") + return "", "", common.Address{}, errors.New("no Ethereum address found to fund") } var avatar string if parts = regexp.MustCompile("src=\"([^\"]+googleusercontent.com[^\"]+photo.jpg)\"").FindStringSubmatch(string(body)); len(parts) == 2 { @@ -784,7 +784,7 @@ func authFacebook(url string) (string, string, common.Address, error) { // Ensure the user specified a meaningful URL, no fancy nonsense parts := strings.Split(url, "/") if len(parts) < 4 || parts[len(parts)-2] != "posts" { - return "", "", common.Address{}, errors.New("Invalid Facebook post URL") + return "", "", common.Address{}, errors.New("invalid Facebook post URL") } username := parts[len(parts)-3] @@ -803,7 +803,7 @@ func authFacebook(url string) (string, string, common.Address, error) { } address := common.HexToAddress(string(regexp.MustCompile("0x[0-9a-fA-F]{40}").Find(body))) if address == (common.Address{}) { - return "", "", common.Address{}, errors.New("No Ethereum address found to fund") + return "", "", common.Address{}, errors.New("no Ethereum address found to fund") } var avatar string if parts = regexp.MustCompile("src=\"([^\"]+fbcdn.net[^\"]+)\"").FindStringSubmatch(string(body)); len(parts) == 2 { @@ -818,7 +818,7 @@ func authFacebook(url string) (string, string, common.Address, error) { func authNoAuth(url string) (string, string, common.Address, error) { address := common.HexToAddress(regexp.MustCompile("0x[0-9a-fA-F]{40}").FindString(url)) if address == (common.Address{}) { - return "", "", common.Address{}, errors.New("No Ethereum address found to fund") + return "", "", common.Address{}, errors.New("no Ethereum address found to fund") } return address.Hex() + "@noauth", "", address, nil } diff --git a/cmd/faucet/website.go b/cmd/faucet/website.go index cbdede5df72c..2b204731167a 100644 --- a/cmd/faucet/website.go +++ b/cmd/faucet/website.go @@ -96,11 +96,11 @@ func Asset(name string) ([]byte, error) { if f, ok := _bindata[canonicalName]; ok { a, err := f() if err != nil { - return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + return nil, fmt.Errorf("can't read Asset %s by error: %v", name, err) } return a.bytes, nil } - return nil, fmt.Errorf("Asset %s not found", name) + return nil, fmt.Errorf("not found Asset %s", name) } // AssetString returns the asset contents as a string (instead of a []byte). @@ -134,11 +134,11 @@ func AssetInfo(name string) (os.FileInfo, error) { if f, ok := _bindata[canonicalName]; ok { a, err := f() if err != nil { - return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + return nil, fmt.Errorf("can't read AssetInfo %s by error: %v", name, err) } return a.info, nil } - return nil, fmt.Errorf("AssetInfo %s not found", name) + return nil, fmt.Errorf("not found AssetInfo %s", name) } // AssetDigest returns the digest of the file with the given name. It returns an @@ -148,11 +148,11 @@ func AssetDigest(name string) ([sha256.Size]byte, error) { if f, ok := _bindata[canonicalName]; ok { a, err := f() if err != nil { - return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err) + return [sha256.Size]byte{}, fmt.Errorf("can't read AssetDigest %s by error: %v", name, err) } return a.digest, nil } - return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name) + return [sha256.Size]byte{}, fmt.Errorf("not found AssetDigest %s", name) } // Digests returns a map of all known files and their checksums. @@ -208,12 +208,12 @@ func AssetDir(name string) ([]string, error) { for _, p := range pathList { node = node.Children[p] if node == nil { - return nil, fmt.Errorf("Asset %s not found", name) + return nil, fmt.Errorf("not found Asset %s", name) } } } if node.Func != nil { - return nil, fmt.Errorf("Asset %s not found", name) + return nil, fmt.Errorf("not found Asset %s", name) } rv := make([]string, 0, len(node.Children)) for childName := range node.Children { diff --git a/common/types_test.go b/common/types_test.go index 7621a6e4b8ef..fc52878f1565 100644 --- a/common/types_test.go +++ b/common/types_test.go @@ -190,7 +190,7 @@ func TestBinaryAddressToString(t *testing.T) { have := tt.bin.String() want := tt.str if have != want { - t.Errorf("fail to convert binary address to string address\nwant:%s\nhave:%s", have, want) + t.Errorf("fail to convert binary address to string address\nwant: %s\nhave: %s", have, want) } } } @@ -199,7 +199,7 @@ func TestStringToBinaryAddress(t *testing.T) { want := tt.bin have := HexToAddress(tt.str) if have != want { - t.Errorf("fail to convert string address to binary address\nwant:%s\nhave:%s", have, want) + t.Errorf("fail to convert string address to binary address\nwant: %s\nhave: %s", have, want) } } } diff --git a/consensus/XDPoS/XDPoS.go b/consensus/XDPoS/XDPoS.go index eb976ca60370..d6e532fc129e 100644 --- a/consensus/XDPoS/XDPoS.go +++ b/consensus/XDPoS/XDPoS.go @@ -439,7 +439,7 @@ func (x *XDPoS) CalculateMissingRounds(chain consensus.ChainReader, header *type case params.ConsensusEngineVersion2: return x.EngineV2.CalculateMissingRounds(chain, header) default: // Default "v1" - return nil, errors.New("Not supported in the v1 consensus") + return nil, errors.New("not supported in the v1 consensus") } } diff --git a/consensus/XDPoS/engines/engine_v1/engine.go b/consensus/XDPoS/engines/engine_v1/engine.go index c67dda874494..3df0e29369c0 100644 --- a/consensus/XDPoS/engines/engine_v1/engine.go +++ b/consensus/XDPoS/engines/engine_v1/engine.go @@ -394,7 +394,7 @@ func (x *XDPoS_v1) GetPeriod() uint64 { return x.config.Period } func (x *XDPoS_v1) whoIsCreator(snap *SnapshotV1, header *types.Header) (common.Address, error) { if header.Number.Uint64() == 0 { - return common.Address{}, errors.New("Don't take block 0") + return common.Address{}, errors.New("don't take block 0") } m, err := ecrecover(header, snap.sigcache) if err != nil { @@ -444,7 +444,7 @@ func (x *XDPoS_v1) yourTurn(chain consensus.ChainReader, parent *types.Header, s return 0, -1, -1, false, err } if len(masternodes) == 0 { - return 0, -1, -1, false, errors.New("Masternodes not found") + return 0, -1, -1, false, errors.New("masternodes not found") } pre := common.Address{} // masternode[0] has chance to create block 1 @@ -1029,7 +1029,7 @@ func (x *XDPoS_v1) getSignersFromContract(chain consensus.ChainReader, checkpoin } signers, err := x.HookGetSignersFromContract(startGapBlockHeader.Hash()) if err != nil { - return []common.Address{}, fmt.Errorf("Can't get signers from Smart Contract . Err: %v", err) + return []common.Address{}, fmt.Errorf("can't get signers from Smart Contract . Err: %v", err) } return signers, nil } diff --git a/consensus/XDPoS/engines/engine_v1/utils.go b/consensus/XDPoS/engines/engine_v1/utils.go index 56f1716a8379..a843cfa7a39e 100644 --- a/consensus/XDPoS/engines/engine_v1/utils.go +++ b/consensus/XDPoS/engines/engine_v1/utils.go @@ -26,7 +26,7 @@ func decodeMasternodesFromHeaderExtra(checkpointHeader *types.Header) []common.A // Get m2 list from checkpoint block. func getM1M2FromCheckpointHeader(checkpointHeader *types.Header, currentHeader *types.Header, config *params.ChainConfig) (map[common.Address]common.Address, error) { if checkpointHeader.Number.Uint64()%common.EpocBlockRandomize != 0 { - return nil, errors.New("This block is not checkpoint block epoc.") + return nil, errors.New("this block is not checkpoint block") } // Get signers from this block. masternodes := decodeMasternodesFromHeaderExtra(checkpointHeader) diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index c2235f5d32a9..f2e4a5e4d1a5 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -661,7 +661,7 @@ func (x *XDPoS_v2) VerifyTimeoutMessage(chain consensus.ChainReader, timeoutMsg } if len(snap.NextEpochCandidates) == 0 { log.Error("[VerifyTimeoutMessage] cannot find NextEpochCandidates from snapshot", "messageGapNumber", timeoutMsg.GapNumber) - return false, errors.New("Empty master node lists from snapshot") + return false, errors.New("empty master node lists from snapshot") } verified, signer, err := x.verifyMsgSignature(types.TimeoutSigHash(&types.TimeoutForSign{ @@ -789,7 +789,7 @@ func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert * epochInfo, err := x.getEpochSwitchInfo(blockChainReader, parentHeader, quorumCert.ProposedBlockInfo.Hash) if err != nil { log.Error("[verifyQC] Error when getting epoch switch Info to verify QC", "Error", err) - return errors.New("Fail to verify QC due to failure in getting epoch switch info") + return errors.New("fail to verify QC due to failure in getting epoch switch info") } signatures, duplicates := UniqueSignatures(quorumCert.Signatures) @@ -821,12 +821,12 @@ func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert * }), sig, epochInfo.Masternodes) if err != nil { log.Error("[verifyQC] Error while verfying QC message signatures", "Error", err) - haveError = errors.New("Error while verfying QC message signatures") + haveError = errors.New("error while verfying QC message signatures") return } if !verified { log.Warn("[verifyQC] Signature not verified doing QC verification", "QC", quorumCert) - haveError = errors.New("Fail to verify QC due to signature mis-match") + haveError = errors.New("fail to verify QC due to signature mis-match") return } }(signature) diff --git a/consensus/XDPoS/engines/engine_v2/forensics.go b/consensus/XDPoS/engines/engine_v2/forensics.go index 45f88097d6b3..f6b46320379b 100644 --- a/consensus/XDPoS/engines/engine_v2/forensics.go +++ b/consensus/XDPoS/engines/engine_v2/forensics.go @@ -164,7 +164,7 @@ func (f *Forensics) SendForensicProof(chain consensus.ChainReader, engine *XDPoS if ancestorBlock == nil { log.Error("[SendForensicProof] Unable to find the ancestor block by its hash", "Hash", ancestorHash) - return errors.New("Can't find ancestor block via hash") + return errors.New("can't find ancestor block via hash") } content, err := json.Marshal(&types.ForensicsContent{ @@ -455,7 +455,7 @@ func (f *Forensics) isExtendingFromAncestor(blockChainReader consensus.ChainRead for i := 0; i < blockNumDiff; i++ { parentBlock := blockChainReader.GetHeaderByHash(nextBlockHash) if parentBlock == nil { - return false, fmt.Errorf("Could not find its parent block when checking whether currentBlock %v with hash %v is extending from the ancestorBlock %v", currentBlock.Number, currentBlock.Hash, ancestorBlock.Number) + return false, fmt.Errorf("could not find its parent block when checking whether currentBlock %v with hash %v is extending from the ancestorBlock %v", currentBlock.Number, currentBlock.Hash, ancestorBlock.Number) } else { nextBlockHash = parentBlock.ParentHash } diff --git a/consensus/XDPoS/engines/engine_v2/utils.go b/consensus/XDPoS/engines/engine_v2/utils.go index 20198816280b..92da8d0f7db1 100644 --- a/consensus/XDPoS/engines/engine_v2/utils.go +++ b/consensus/XDPoS/engines/engine_v2/utils.go @@ -98,7 +98,7 @@ func (x *XDPoS_v2) signSignature(signingHash common.Hash) (types.Signature, erro signedHash, err := signFn(accounts.Account{Address: signer}, signingHash.Bytes()) if err != nil { - return nil, fmt.Errorf("Error %v while signing hash", err) + return nil, fmt.Errorf("error %v while signing hash", err) } return signedHash, nil } @@ -106,12 +106,12 @@ func (x *XDPoS_v2) signSignature(signingHash common.Hash) (types.Signature, erro func (x *XDPoS_v2) verifyMsgSignature(signedHashToBeVerified common.Hash, signature types.Signature, masternodes []common.Address) (bool, common.Address, error) { var signerAddress common.Address if len(masternodes) == 0 { - return false, signerAddress, errors.New("Empty masternode list detected when verifying message signatures") + return false, signerAddress, errors.New("empty masternode list detected when verifying message signatures") } // Recover the public key and the Ethereum address pubkey, err := crypto.Ecrecover(signedHashToBeVerified.Bytes(), signature) if err != nil { - return false, signerAddress, fmt.Errorf("Error while verifying message: %v", err) + return false, signerAddress, fmt.Errorf("error while verifying message: %v", err) } copy(signerAddress[:], crypto.Keccak256(pubkey[1:])[12:]) diff --git a/consensus/XDPoS/engines/engine_v2/vote.go b/consensus/XDPoS/engines/engine_v2/vote.go index de79585297af..138e69a7691b 100644 --- a/consensus/XDPoS/engines/engine_v2/vote.go +++ b/consensus/XDPoS/engines/engine_v2/vote.go @@ -79,7 +79,7 @@ func (x *XDPoS_v2) voteHandler(chain consensus.ChainReader, voteMsg *types.Vote) epochInfo, err := x.getEpochSwitchInfo(chain, chain.CurrentHeader(), chain.CurrentHeader().Hash()) if err != nil { log.Error("[voteHandler] Error when getting epoch switch Info", "error", err) - return errors.New("Fail on voteHandler due to failure in getting epoch switch info") + return errors.New("fail on voteHandler due to failure in getting epoch switch info") } certThreshold := x.config.V2.Config(uint64(voteMsg.ProposedBlockInfo.Round)).CertThreshold @@ -178,7 +178,7 @@ func (x *XDPoS_v2) onVotePoolThresholdReached(chain consensus.ChainReader, poole epochInfo, err := x.getEpochSwitchInfo(chain, chain.CurrentHeader(), chain.CurrentHeader().Hash()) if err != nil { log.Error("[voteHandler] Error when getting epoch switch Info", "error", err) - return errors.New("Fail on voteHandler due to failure in getting epoch switch info") + return errors.New("fail on voteHandler due to failure in getting epoch switch info") } // Skip and wait for the next vote to process again if valid votes is less than what we required @@ -249,7 +249,7 @@ func (x *XDPoS_v2) isExtendingFromAncestor(blockChainReader consensus.ChainReade for i := 0; i < blockNumDiff; i++ { parentBlock := blockChainReader.GetHeaderByHash(nextBlockHash) if parentBlock == nil { - return false, fmt.Errorf("Could not find its parent block when checking whether currentBlock %v with hash %v is extending from the ancestorBlock %v", currentBlock.Number, currentBlock.Hash, ancestorBlock.Number) + return false, fmt.Errorf("could not find its parent block when checking whether currentBlock %v with hash %v is extending from the ancestorBlock %v", currentBlock.Number, currentBlock.Hash, ancestorBlock.Number) } else { nextBlockHash = parentBlock.ParentHash } diff --git a/consensus/XDPoS/utils/errors.go b/consensus/XDPoS/utils/errors.go index 4a45d4157ab0..da83ec706474 100644 --- a/consensus/XDPoS/utils/errors.go +++ b/consensus/XDPoS/utils/errors.go @@ -85,20 +85,20 @@ var ( ErrEmptyEpochSwitchValidators = errors.New("empty validators list on epoch switch block") - ErrInvalidV2Extra = errors.New("Invalid v2 extra in the block") - ErrInvalidQC = errors.New("Invalid QC content") - ErrInvalidQCSignatures = errors.New("Invalid QC Signatures") - ErrInvalidTC = errors.New("Invalid TC content") - ErrInvalidTCSignatures = errors.New("Invalid TC Signatures") - ErrEmptyBlockInfoHash = errors.New("BlockInfo hash is empty") - ErrInvalidFieldInNonEpochSwitch = errors.New("Invalid field exist in a non-epoch swtich block") - ErrValidatorNotWithinMasternodes = errors.New("Validator address is not in the master node list") - ErrCoinbaseAndValidatorMismatch = errors.New("Validator and coinbase address in header does not match") - ErrNotItsTurn = errors.New("Not validator's turn to mine this block") - - ErrRoundInvalid = errors.New("Invalid Round, it shall be bigger than QC round") - - ErrAlreadyMined = errors.New("Already mined") + ErrInvalidV2Extra = errors.New("invalid v2 extra in the block") + ErrInvalidQC = errors.New("invalid QC content") + ErrInvalidQCSignatures = errors.New("invalid QC Signatures") + ErrInvalidTC = errors.New("invalid TC content") + ErrInvalidTCSignatures = errors.New("invalid TC Signatures") + ErrEmptyBlockInfoHash = errors.New("blockInfo hash is empty") + ErrInvalidFieldInNonEpochSwitch = errors.New("invalid field exist in a non-epoch swtich block") + ErrValidatorNotWithinMasternodes = errors.New("validator address is not in the master node list") + ErrCoinbaseAndValidatorMismatch = errors.New("validator and coinbase address in header does not match") + ErrNotItsTurn = errors.New("not validator's turn to mine this block") + + ErrRoundInvalid = errors.New("invalid Round, it shall be bigger than QC round") + + ErrAlreadyMined = errors.New("already mined") ) type ErrIncomingMessageRoundNotEqualCurrentRound struct { diff --git a/consensus/errors.go b/consensus/errors.go index c8ed578c2c5f..999916380d36 100644 --- a/consensus/errors.go +++ b/consensus/errors.go @@ -43,7 +43,7 @@ var ( ErrNotReadyToPropose = errors.New("not ready to propose, QC is not ready") - ErrNotReadyToMine = errors.New("Not ready to mine, it's not your turn") + ErrNotReadyToMine = errors.New("not ready to mine, it's not your turn") - ErrCoinbaseMismatch = errors.New("Block Coinbase address does not match its wallte address") + ErrCoinbaseMismatch = errors.New("block Coinbase address does not match its wallte address") ) diff --git a/consensus/tests/engine_v1_tests/helper.go b/consensus/tests/engine_v1_tests/helper.go index a393f457b715..5c27489f408d 100644 --- a/consensus/tests/engine_v1_tests/helper.go +++ b/consensus/tests/engine_v1_tests/helper.go @@ -249,7 +249,7 @@ func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params blockchain.Client = backend if err != nil { - panic(fmt.Errorf("Error while creating simulated wallet for generating singer address and signer fn: %v", err)) + panic(fmt.Errorf("error while creating simulated wallet for generating singer address and signer fn: %v", err)) } blockchain.Engine().(*XDPoS.XDPoS).Authorize(signer, signFn) @@ -322,13 +322,13 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti // Sign all the things for v1 block use v1 sigHash function sighash, err := signFn(accounts.Account{Address: signer}, blockchain.Engine().(*XDPoS.XDPoS).SigHash(header).Bytes()) if err != nil { - panic(errors.New("Error when sign last v1 block hash during test block creation")) + panic(errors.New("error when sign last v1 block hash during test block creation")) } copy(header.Extra[len(header.Extra)-utils.ExtraSeal:], sighash) } block, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, chainConfig) if err != nil { - panic(fmt.Errorf("Fail to create block in test helper, %v", err)) + panic(fmt.Errorf("fail to create block in test helper, %v", err)) } return block } diff --git a/consensus/tests/engine_v2_tests/api_test.go b/consensus/tests/engine_v2_tests/api_test.go index 185f9a40ccd2..aa482c5e7320 100644 --- a/consensus/tests/engine_v2_tests/api_test.go +++ b/consensus/tests/engine_v2_tests/api_test.go @@ -20,7 +20,7 @@ func TestGetMissedRoundsInEpochByBlockNumOnlyForV2Consensus(t *testing.T) { data, err := engine.APIs(bc.GetBlockChain())[0].Service.(*XDPoS.API).GetMissedRoundsInEpochByBlockNum(&blockNum) - assert.EqualError(t, err, "Not supported in the v1 consensus") + assert.EqualError(t, err, "not supported in the v1 consensus") assert.Nil(t, data) } diff --git a/consensus/tests/engine_v2_tests/helper.go b/consensus/tests/engine_v2_tests/helper.go index 5ea44d6d480c..a25019d467d4 100644 --- a/consensus/tests/engine_v2_tests/helper.go +++ b/consensus/tests/engine_v2_tests/helper.go @@ -371,7 +371,7 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon var err error signer, signFn, err := backends.SimulateWalletAddressAndSignFn() if err != nil { - panic(fmt.Errorf("Error while creating simulated wallet for generating singer address and signer fn: %v", err)) + panic(fmt.Errorf("error while creating simulated wallet for generating singer address and signer fn: %v", err)) } backend := getCommonBackend(t, chainConfig) blockchain := backend.GetBlockChain() @@ -634,14 +634,14 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti // Sign all the things for v1 block use v1 sigHash function sighash, err := signFn(accounts.Account{Address: signer}, blockchain.Engine().(*XDPoS.XDPoS).SigHash(header).Bytes()) if err != nil { - panic(errors.New("Error when sign last v1 block hash during test block creation")) + panic(errors.New("error when sign last v1 block hash during test block creation")) } copy(header.Extra[len(header.Extra)-utils.ExtraSeal:], sighash) } } block, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, chainConfig) if err != nil { - panic(fmt.Errorf("Fail to create block in test helper, %v", err)) + panic(fmt.Errorf("fail to create block in test helper, %v", err)) } return block } @@ -758,7 +758,7 @@ func findSignerAndSignFn(bc *BlockChain, header *types.Header, signer common.Add } addressedSignFn = signFn if err != nil { - panic(errors.New("Error trying to use one of the pre-defined private key to sign")) + panic(errors.New("error trying to use one of the pre-defined private key to sign")) } } @@ -806,7 +806,7 @@ func generateV2Extra(roundNumber int64, currentBlock *types.Block, signer common signedHash, err := signFn(accounts.Account{Address: signer}, types.VoteSigHash(voteForSign).Bytes()) if err != nil { - panic(fmt.Errorf("Error generate QC by creating signedHash: %v", err)) + panic(fmt.Errorf("error generate QC by creating signedHash: %v", err)) } var signatures []types.Signature if len(accKeys) == 0 { @@ -831,7 +831,7 @@ func generateV2Extra(roundNumber int64, currentBlock *types.Block, signer common } extraInBytes, err := extra.EncodeToBytes() if err != nil { - panic(fmt.Errorf("Error encode extra into bytes: %v", err)) + panic(fmt.Errorf("error encode extra into bytes: %v", err)) } return extraInBytes } diff --git a/consensus/tests/engine_v2_tests/verify_header_test.go b/consensus/tests/engine_v2_tests/verify_header_test.go index 3518c31008d5..1237c4991469 100644 --- a/consensus/tests/engine_v2_tests/verify_header_test.go +++ b/consensus/tests/engine_v2_tests/verify_header_test.go @@ -119,7 +119,7 @@ func TestShouldVerifyBlock(t *testing.T) { // Genrate QC signedHash, err := signFn(accounts.Account{Address: signer}, types.VoteSigHash(voteForSign).Bytes()) if err != nil { - panic(fmt.Errorf("Error generate QC by creating signedHash: %v", err)) + panic(fmt.Errorf("error generate QC by creating signedHash: %v", err)) } // Sign from acc 1, 2, 3 acc1SignedHash := SignHashByPK(acc1Key, types.VoteSigHash(voteForSign).Bytes()) @@ -139,7 +139,7 @@ func TestShouldVerifyBlock(t *testing.T) { } extraInBytes, err := extra.EncodeToBytes() if err != nil { - panic(fmt.Errorf("Error encode extra into bytes: %v", err)) + panic(fmt.Errorf("error encode extra into bytes: %v", err)) } invalidRoundBlock := blockchain.GetBlockByNumber(902).Header() @@ -398,7 +398,7 @@ func TestShouldFailIfNotEnoughQCSignatures(t *testing.T) { } extraInBytes, err := extra.EncodeToBytes() if err != nil { - panic(fmt.Errorf("Error encode extra into bytes: %v", err)) + panic(fmt.Errorf("error encode extra into bytes: %v", err)) } headerWithDuplicatedSignatures := currentBlock.Header() headerWithDuplicatedSignatures.Extra = extraInBytes diff --git a/consensus/tests/engine_v2_tests/vote_test.go b/consensus/tests/engine_v2_tests/vote_test.go index 53ac60eed1cd..4a58ad08abda 100644 --- a/consensus/tests/engine_v2_tests/vote_test.go +++ b/consensus/tests/engine_v2_tests/vote_test.go @@ -539,7 +539,7 @@ func TestVerifyVoteMsg(t *testing.T) { engineV2.SetNewRoundFaker(blockchain, types.Round(14), false) verified, err = engineV2.VerifyVoteMessage(blockchain, voteMsg) assert.False(t, verified) - assert.Equal(t, "Error while verifying message: invalid signature length", err.Error()) + assert.Equal(t, "error while verifying message: invalid signature length", err.Error()) // Valid vote message from a master node signHash, _ := signFn(accounts.Account{Address: signer}, types.VoteSigHash(voteForSign).Bytes()) diff --git a/console/bridge.go b/console/bridge.go index 80f809533678..34f77dcbfbe4 100644 --- a/console/bridge.go +++ b/console/bridge.go @@ -76,7 +76,7 @@ func (b *bridge) NewAccount(call jsre.Call) (goja.Value, error) { return nil, err } if password != confirm { - return nil, errors.New("passwords don't match!") + return nil, errors.New("passwords don't match") } // A single string password was specified, use that case len(call.Arguments) == 1 && call.Argument(0).ToString() != nil: diff --git a/core/blockchain.go b/core/blockchain.go index fb98f45dab93..d1587ab67245 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1650,7 +1650,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, [] finalizedTrades := map[common.Hash]*lendingstate.LendingTrade{} finalizedTrades, _, _, _, _, err = lendingService.ProcessLiquidationData(block.Header(), bc, statedb, tradingState, lendingState) if err != nil { - return i, events, coalescedLogs, fmt.Errorf("failed to ProcessLiquidationData. Err: %v ", err) + return i, events, coalescedLogs, fmt.Errorf("failed to ProcessLiquidationData. Err: %v", err) } if isSDKNode { finalizedTx := lendingstate.FinalizedResult{} @@ -1678,7 +1678,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, [] expectRoot, _ := lendingService.GetLendingStateRoot(block, author) parentRoot, _ := lendingService.GetLendingStateRoot(parent, parentAuthor) if gotRoot != expectRoot { - err = fmt.Errorf("invalid lending state merke trie got : %s , expect : %s , parent :%s ", gotRoot.Hex(), expectRoot.Hex(), parentRoot.Hex()) + err = fmt.Errorf("invalid lending state merke trie got: %s, expect: %s, parent: %s", gotRoot.Hex(), expectRoot.Hex(), parentRoot.Hex()) bc.reportBlock(block, nil, err) return i, events, coalescedLogs, err } @@ -1930,7 +1930,7 @@ func (bc *BlockChain) getResultBlock(block *types.Block, verifiedM2 bool) (*Resu if block.Number().Uint64()%bc.chainConfig.XDPoS.Epoch == common.LiquidateLendingTradeBlock { finalizedTrades, _, _, _, _, err := lendingService.ProcessLiquidationData(block.Header(), bc, statedb, tradingState, lendingState) if err != nil { - return nil, fmt.Errorf("failed to ProcessLiquidationData. Err: %v ", err) + return nil, fmt.Errorf("failed to ProcessLiquidationData. Err: %v", err) } if isSDKNode { finalizedTx := lendingstate.FinalizedResult{} @@ -1957,7 +1957,7 @@ func (bc *BlockChain) getResultBlock(block *types.Block, verifiedM2 bool) (*Resu expectRoot, _ := lendingService.GetLendingStateRoot(block, author) parentRoot, _ := lendingService.GetLendingStateRoot(parent, parentAuthor) if gotRoot != expectRoot { - err = fmt.Errorf("invalid lending state merke trie got : %s , expect : %s , parent : %s ", gotRoot.Hex(), expectRoot.Hex(), parentRoot.Hex()) + err = fmt.Errorf("invalid lending state merke trie got: %s , expect : %s , parent : %s", gotRoot.Hex(), expectRoot.Hex(), parentRoot.Hex()) bc.reportBlock(block, nil, err) return nil, err } @@ -2177,10 +2177,10 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { } } if oldBlock == nil { - return errors.New("Invalid old chain") + return errors.New("invalid old chain") } if newBlock == nil { - return errors.New("Invalid new chain") + return errors.New("invalid new chain") } for { @@ -2196,10 +2196,10 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { oldBlock, newBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1), bc.GetBlock(newBlock.ParentHash(), newBlock.NumberU64()-1) if oldBlock == nil { - return errors.New("Invalid old chain") + return errors.New("invalid old chain") } if newBlock == nil { - return errors.New("Invalid new chain") + return errors.New("invalid new chain") } } // Ensure XDPoS engine committed block will be not reverted diff --git a/core/lending_pool.go b/core/lending_pool.go index 89aac0665f36..e8e4724dfc8d 100644 --- a/core/lending_pool.go +++ b/core/lending_pool.go @@ -623,7 +623,7 @@ func (pool *LendingPool) validateTx(tx *types.LendingTransaction, local bool) er // check if sender is in black list if tx.From() != nil && common.Blacklist[*tx.From()] { - return fmt.Errorf("Reject transaction with sender in black-list: %v", tx.From().Hex()) + return fmt.Errorf("reject transaction with sender in black-list: %v", tx.From().Hex()) } // Heuristic limit, reject transactions over 32KB to prevent DOS attacks if tx.Size() > 32*1024 { diff --git a/core/order_pool.go b/core/order_pool.go index dfade0ecd448..90ea6fb10388 100644 --- a/core/order_pool.go +++ b/core/order_pool.go @@ -61,7 +61,7 @@ var ( var ( ErrPendingNonceTooLow = errors.New("pending nonce too low") - ErrPoolOverflow = errors.New("Exceed pool size") + ErrPoolOverflow = errors.New("exceed pool size") ) // OrderPoolConfig are the configuration parameters of the order transaction pool. @@ -532,7 +532,7 @@ func (pool *OrderPool) validateTx(tx *types.OrderTransaction, local bool) error // check if sender is in black list if tx.From() != nil && common.Blacklist[*tx.From()] { - return fmt.Errorf("Reject transaction with sender in black-list: %v", tx.From().Hex()) + return fmt.Errorf("reject transaction with sender in black-list: %v", tx.From().Hex()) } // Heuristic limit, reject transactions over 32KB to prevent DOS attacks if tx.Size() > 32*1024 { diff --git a/core/tx_pool.go b/core/tx_pool.go index 7a3aa8cd1efe..4cfdba713abf 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -581,11 +581,11 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { } // check if sender is in black list if tx.From() != nil && common.Blacklist[*tx.From()] { - return fmt.Errorf("Reject transaction with sender in black-list: %v", tx.From().Hex()) + return fmt.Errorf("reject transaction with sender in black-list: %v", tx.From().Hex()) } // check if receiver is in black list if tx.To() != nil && common.Blacklist[*tx.To()] { - return fmt.Errorf("Reject transaction with receiver in black-list: %v", tx.To().Hex()) + return fmt.Errorf("reject transaction with receiver in black-list: %v", tx.To().Hex()) } // Transactions can't be negative. This may never happen using RLP decoded // transactions but may occur if you create a transaction using the RPC. diff --git a/core/vm/contracts.go b/core/vm/contracts.go index f7e10794c2b5..be9df1adfecd 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -575,10 +575,10 @@ func (c *ringSignatureVerifier) RequiredGas(input []byte) uint64 { func (c *ringSignatureVerifier) Run(proof []byte) ([]byte, error) { der, err := privacy.Deserialize(proof) if err != nil { - return []byte{}, errors.New("Fail to deserialize proof") + return []byte{}, errors.New("fail to deserialize proof") } if !privacy.Verify(der, false) { - return []byte{}, errors.New("Fail to verify ring signature") + return []byte{}, errors.New("fail to verify ring signature") } return []byte{}, nil } diff --git a/core/vm/privacy/bulletproof.go b/core/vm/privacy/bulletproof.go index f620d230b2ca..f123bb1005bb 100644 --- a/core/vm/privacy/bulletproof.go +++ b/core/vm/privacy/bulletproof.go @@ -985,7 +985,7 @@ func MRPProve(values []*big.Int) (MultiRangeProof, error) { } if !acceptedInputNumber { - return MultiRangeProof{}, errors.New("Value number is not supported - just 1, 2, 4, 8") + return MultiRangeProof{}, errors.New("value number is not supported - just 1, 2, 4, 8") } EC = genECPrimeGroupKey(m * bitsPerValue) @@ -1002,15 +1002,15 @@ func MRPProve(values []*big.Int) (MultiRangeProof, error) { for j := range values { v := values[j] if v.Cmp(big.NewInt(0)) == -1 { - return MultiRangeProof{}, errors.New("Value is below range! Not proving") + return MultiRangeProof{}, errors.New("value is below range! Not proving") } if v.Cmp(MAX_64_BITS) == 1 { - return MultiRangeProof{}, errors.New("Value is above range! Not proving") + return MultiRangeProof{}, errors.New("value is above range! Not proving") } if v.Cmp(new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(bitsPerValue)), EC.N)) == 1 { - return MultiRangeProof{}, errors.New("Value is above range! Not proving") + return MultiRangeProof{}, errors.New("value is above range! Not proving") } gamma, err := rand.Int(rand.Reader, EC.N) diff --git a/core/vm/privacy/ringct.go b/core/vm/privacy/ringct.go index 93c97c56cee1..91ea3096c14f 100644 --- a/core/vm/privacy/ringct.go +++ b/core/vm/privacy/ringct.go @@ -171,7 +171,7 @@ func (r *RingSignature) Serialize() ([]byte, error) { } if len(sig) != 8+8+32+32+(32+33)*r.NumRing*r.Size+33*r.NumRing { - return []byte{}, errors.New("Could not serialize ring signature") + return []byte{}, errors.New("could not serialize ring signature") } return sig, nil @@ -198,7 +198,7 @@ func computeSignatureSize(numRing int, ringSize int) int { // deserializes the byteified signature into a RingSignature struct func Deserialize(r []byte) (*RingSignature, error) { if len(r) < 16 { - return nil, errors.New("Failed to deserialize ring signature") + return nil, errors.New("failed to deserialize ring signature") } offset := 0 sig := new(RingSignature) @@ -455,7 +455,7 @@ func Sign(m [32]byte, rings []Ring, privkeys []*ecdsa.PrivateKey, s int) (*RingS px, py := curve.ScalarMult(rings[j][idx].X, rings[j][idx].Y, PadTo32Bytes(C[idx].Bytes())) // px, py = c_i*P_i sx, sy := curve.ScalarBaseMult(PadTo32Bytes(S[j][idx].Bytes())) // sx, sy = s[n-1]*G if px == nil || py == nil || sx == nil || sy == nil { - return nil, errors.New("Could not create ring signature") + return nil, errors.New("could not create ring signature") } l_x, l_y := curve.Add(sx, sy, px, py) L[j][idx] = &ecdsa.PublicKey{curve, l_x, l_y} @@ -467,7 +467,7 @@ func Sign(m [32]byte, rings []Ring, privkeys []*ecdsa.PrivateKey, s int) (*RingS hx, hy := HashPoint(rings[j][idx]) sx, sy = curve.ScalarMult(hx, hy, S[j][idx].Bytes()) // sx, sy = s[n-1]*H_p(P_i) if px == nil || py == nil || sx == nil || sy == nil { - return nil, errors.New("Could not create ring signature") + return nil, errors.New("could not create ring signature") } r_x, r_y := curve.Add(sx, sy, px, py) R[j][idx] = &ecdsa.PublicKey{curve, r_x, r_y} diff --git a/eth/api_tracer.go b/eth/api_tracer.go index 1c79c33acf53..dd076156df82 100644 --- a/eth/api_tracer.go +++ b/eth/api_tracer.go @@ -601,7 +601,7 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (* } root := statedb.IntermediateRoot(true) if root != block.Root() { - return nil, nil, fmt.Errorf("invalid merkle root (number :%d got : %x expect: %x)", block.NumberU64(), root.Hex(), block.Root()) + return nil, nil, fmt.Errorf("invalid merkle root (number %d : got : %x expect: %x)", block.NumberU64(), root.Hex(), block.Root()) } // Finalize the state so any modifications are written to the trie root, err = statedb.Commit(true) diff --git a/eth/backend.go b/eth/backend.go index 45e1bd95ec1a..e3a7490207d5 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -154,7 +154,7 @@ func New(ctx *node.ServiceContext, config *ethconfig.Config, XDCXServ *XDCx.XDCX if !config.SkipBcVersionCheck { bcVersion := core.GetBlockChainVersion(chainDb) if bcVersion != core.BlockChainVersion && bcVersion != 0 { - return nil, fmt.Errorf("Blockchain DB version mismatch (%d / %d). Run geth upgradedb.\n", bcVersion, core.BlockChainVersion) + return nil, fmt.Errorf("blockchain DB version mismatch (%d / %d). Run geth upgradedb", bcVersion, core.BlockChainVersion) } core.WriteBlockChainVersion(chainDb, core.BlockChainVersion) } @@ -247,7 +247,7 @@ func New(ctx *node.ServiceContext, config *ethconfig.Config, XDCXServ *XDCx.XDCX } if block.NumberU64()%common.MergeSignRange == 0 || !eth.chainConfig.IsTIP2019(block.Number()) { if err := contracts.CreateTransactionSign(chainConfig, eth.txPool, eth.accountManager, block, chainDb, eb); err != nil { - return fmt.Errorf("Fail to create tx sign for importing block: %v", err) + return fmt.Errorf("fail to create tx sign for importing block: %v", err) } } return nil @@ -478,7 +478,7 @@ func (s *Ethereum) ValidateMasternode() (bool, error) { return false, nil } } else { - return false, errors.New("Only verify masternode permission in XDPoS protocol") + return false, errors.New("only verify masternode permission in XDPoS protocol") } return true, nil } diff --git a/eth/downloader/statesync.go b/eth/downloader/statesync.go index 8a491231fb60..0012c0c67fb1 100644 --- a/eth/downloader/statesync.go +++ b/eth/downloader/statesync.go @@ -18,7 +18,6 @@ package downloader import ( "fmt" - "github.com/XinFinOrg/XDPoSChain/ethdb/memorydb" "hash" "sync" "time" @@ -28,6 +27,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/ethdb" + "github.com/XinFinOrg/XDPoSChain/ethdb/memorydb" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/trie" ) @@ -327,7 +327,7 @@ func (s *stateSync) commit(force bool) error { b := s.d.stateDB.NewBatch() s.sched.Commit(b) if err := b.Write(); err != nil { - return fmt.Errorf("DB write error: %v", err) + return fmt.Errorf("write DB error: %v", err) } s.updateStats(s.numUncommitted, 0, 0, time.Since(start)) s.numUncommitted = 0 diff --git a/eth/hooks/engine_v1_hooks.go b/eth/hooks/engine_v1_hooks.go index 65c84d1c30b0..2661cd764b86 100644 --- a/eth/hooks/engine_v1_hooks.go +++ b/eth/hooks/engine_v1_hooks.go @@ -264,7 +264,7 @@ func AttachConsensusV1Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf foundationWalletAddr := chain.Config().XDPoS.FoudationWalletAddr if foundationWalletAddr == (common.Address{}) { log.Error("Foundation Wallet Address is empty", "error", foundationWalletAddr) - return errors.New("Foundation Wallet Address is empty"), nil + return errors.New("foundation Wallet Address is empty"), nil } rewards := make(map[string]interface{}) if number > 0 && number-rCheckpoint > 0 && foundationWalletAddr != (common.Address{}) { diff --git a/eth/tracers/internal/tracers/assets.go b/eth/tracers/internal/tracers/assets.go index 83772dab0ef5..1f0398e02407 100644 --- a/eth/tracers/internal/tracers/assets.go +++ b/eth/tracers/internal/tracers/assets.go @@ -306,11 +306,11 @@ func Asset(name string) ([]byte, error) { if f, ok := _bindata[canonicalName]; ok { a, err := f() if err != nil { - return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + return nil, fmt.Errorf("can't read Asset %s by error: %v", name, err) } return a.bytes, nil } - return nil, fmt.Errorf("Asset %s not found", name) + return nil, fmt.Errorf("not found Asset %s", name) } // AssetString returns the asset contents as a string (instead of a []byte). @@ -344,11 +344,11 @@ func AssetInfo(name string) (os.FileInfo, error) { if f, ok := _bindata[canonicalName]; ok { a, err := f() if err != nil { - return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + return nil, fmt.Errorf("can't read AssetInfo %s by error: %v", name, err) } return a.info, nil } - return nil, fmt.Errorf("AssetInfo %s not found", name) + return nil, fmt.Errorf("not found AssetInfo %s", name) } // AssetDigest returns the digest of the file with the given name. It returns an @@ -358,11 +358,11 @@ func AssetDigest(name string) ([sha256.Size]byte, error) { if f, ok := _bindata[canonicalName]; ok { a, err := f() if err != nil { - return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err) + return [sha256.Size]byte{}, fmt.Errorf("can't read AssetDigest %s by error: %v", name, err) } return a.digest, nil } - return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name) + return [sha256.Size]byte{}, fmt.Errorf("not found AssetDigest %s", name) } // Digests returns a map of all known files and their checksums. @@ -426,12 +426,12 @@ func AssetDir(name string) ([]string, error) { for _, p := range pathList { node = node.Children[p] if node == nil { - return nil, fmt.Errorf("Asset %s not found", name) + return nil, fmt.Errorf("not found Asset %s", name) } } } if node.Func != nil { - return nil, fmt.Errorf("Asset %s not found", name) + return nil, fmt.Errorf("not found Asset %s", name) } rv := make([]string, 0, len(node.Children)) for childName := range node.Children { diff --git a/internal/cmdtest/test_cmd.go b/internal/cmdtest/test_cmd.go index 40aa87596cb4..45d10897ef4e 100644 --- a/internal/cmdtest/test_cmd.go +++ b/internal/cmdtest/test_cmd.go @@ -123,12 +123,12 @@ func (tt *TestCmd) matchExactOutput(want []byte) error { // Find the mismatch position. for i := 0; i < n; i++ { if want[i] != buf[i] { - return fmt.Errorf("Output mismatch at ◊:\n---------------- (stdout text)\n%s%s\n---------------- (expected text)\n%s", + return fmt.Errorf("output mismatch at ◊:\n---------------- (stdout text)\n%s%s\n---------------- (expected text)\n%s", buf[:i], buf[i:n], want) } } if n < len(want) { - return fmt.Errorf("Not enough output, got until ◊:\n---------------- (stdout text)\n%s\n---------------- (expected text)\n%s◊%s", + return fmt.Errorf("not enough output, got until ◊:\n---------------- (stdout text)\n%s\n---------------- (expected text)\n%s◊%s", buf, want[:n], want[n:]) } } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 3ef8df9b30e2..552043007c80 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2207,7 +2207,7 @@ func (s *PublicTransactionPoolAPI) sign(addr common.Address, tx *types.Transacti // SubmitTransaction is a helper function that submits tx to txPool and logs a message. func SubmitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (common.Hash, error) { if tx.To() != nil && tx.IsSpecialTransaction() { - return common.Hash{}, errors.New("Dont allow transaction sent to BlockSigners & RandomizeSMC smart contract via API") + return common.Hash{}, errors.New("don't allow transaction sent to BlockSigners & RandomizeSMC smart contract via API") } // If the transaction fee cap is already specified, ensure the @@ -2524,11 +2524,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetBestBid(ctx context.Context, baseToken result := PriceVolume{} block := s.b.CurrentBlock() if block == nil { - return result, errors.New("Current block not found") + return result, errors.New("current block not found") } XDCxService := s.b.XDCxService() if XDCxService == nil { - return result, errors.New("XDCX service not found") + return result, errors.New("not find XDCX service") } author, err := s.b.GetEngine().Author(block.Header()) if err != nil { @@ -2540,7 +2540,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetBestBid(ctx context.Context, baseToken } result.Price, result.Volume = XDCxState.GetBestBidPrice(tradingstate.GetTradingOrderBookHash(baseToken, quoteToken)) if result.Price.Sign() == 0 { - return result, errors.New("Bid tree not found") + return result, errors.New("not found bid tree") } return result, nil } @@ -2549,11 +2549,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetBestAsk(ctx context.Context, baseToken result := PriceVolume{} block := s.b.CurrentBlock() if block == nil { - return result, errors.New("Current block not found") + return result, errors.New("not found current block") } XDCxService := s.b.XDCxService() if XDCxService == nil { - return result, errors.New("XDCX service not found") + return result, errors.New("not found XDCX service") } author, err := s.b.GetEngine().Author(block.Header()) if err != nil { @@ -2565,7 +2565,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetBestAsk(ctx context.Context, baseToken } result.Price, result.Volume = XDCxState.GetBestAskPrice(tradingstate.GetTradingOrderBookHash(baseToken, quoteToken)) if result.Price.Sign() == 0 { - return result, errors.New("Ask tree not found") + return result, errors.New("not find ask tree") } return result, nil } @@ -2573,11 +2573,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetBestAsk(ctx context.Context, baseToken func (s *PublicXDCXTransactionPoolAPI) GetBidTree(ctx context.Context, baseToken, quoteToken common.Address) (map[*big.Int]tradingstate.DumpOrderList, error) { block := s.b.CurrentBlock() if block == nil { - return nil, errors.New("Current block not found") + return nil, errors.New("not find current block") } XDCxService := s.b.XDCxService() if XDCxService == nil { - return nil, errors.New("XDCX service not found") + return nil, errors.New("not find XDCX service") } author, err := s.b.GetEngine().Author(block.Header()) if err != nil { @@ -2597,11 +2597,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetBidTree(ctx context.Context, baseToken func (s *PublicXDCXTransactionPoolAPI) GetPrice(ctx context.Context, baseToken, quoteToken common.Address) (*big.Int, error) { block := s.b.CurrentBlock() if block == nil { - return nil, errors.New("Current block not found") + return nil, errors.New("not find current block") } XDCxService := s.b.XDCxService() if XDCxService == nil { - return nil, errors.New("XDCX service not found") + return nil, errors.New("not find XDCX service") } author, err := s.b.GetEngine().Author(block.Header()) if err != nil { @@ -2613,7 +2613,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetPrice(ctx context.Context, baseToken, } price := XDCxState.GetLastPrice(tradingstate.GetTradingOrderBookHash(baseToken, quoteToken)) if price == nil || price.Sign() == 0 { - return common.Big0, errors.New("Order book's price not found") + return common.Big0, errors.New("not find order book's price") } return price, nil } @@ -2621,11 +2621,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetPrice(ctx context.Context, baseToken, func (s *PublicXDCXTransactionPoolAPI) GetLastEpochPrice(ctx context.Context, baseToken, quoteToken common.Address) (*big.Int, error) { block := s.b.CurrentBlock() if block == nil { - return nil, errors.New("Current block not found") + return nil, errors.New("not find current block") } XDCxService := s.b.XDCxService() if XDCxService == nil { - return nil, errors.New("XDCX service not found") + return nil, errors.New("not find XDCX service") } author, err := s.b.GetEngine().Author(block.Header()) if err != nil { @@ -2637,7 +2637,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetLastEpochPrice(ctx context.Context, ba } price := XDCxState.GetMediumPriceBeforeEpoch(tradingstate.GetTradingOrderBookHash(baseToken, quoteToken)) if price == nil || price.Sign() == 0 { - return common.Big0, errors.New("Order book's price not found") + return common.Big0, errors.New("not find order book's price") } return price, nil } @@ -2645,11 +2645,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetLastEpochPrice(ctx context.Context, ba func (s *PublicXDCXTransactionPoolAPI) GetCurrentEpochPrice(ctx context.Context, baseToken, quoteToken common.Address) (*big.Int, error) { block := s.b.CurrentBlock() if block == nil { - return nil, errors.New("Current block not found") + return nil, errors.New("not find current block") } XDCxService := s.b.XDCxService() if XDCxService == nil { - return nil, errors.New("XDCX service not found") + return nil, errors.New("not find XDCX service") } author, err := s.b.GetEngine().Author(block.Header()) if err != nil { @@ -2661,7 +2661,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetCurrentEpochPrice(ctx context.Context, } price, _ := XDCxState.GetMediumPriceAndTotalAmount(tradingstate.GetTradingOrderBookHash(baseToken, quoteToken)) if price == nil || price.Sign() == 0 { - return common.Big0, errors.New("Order book's price not found") + return common.Big0, errors.New("not find order book's price") } return price, nil } @@ -2669,11 +2669,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetCurrentEpochPrice(ctx context.Context, func (s *PublicXDCXTransactionPoolAPI) GetAskTree(ctx context.Context, baseToken, quoteToken common.Address) (map[*big.Int]tradingstate.DumpOrderList, error) { block := s.b.CurrentBlock() if block == nil { - return nil, errors.New("Current block not found") + return nil, errors.New("not find current block") } XDCxService := s.b.XDCxService() if XDCxService == nil { - return nil, errors.New("XDCX service not found") + return nil, errors.New("not find XDCX service") } author, err := s.b.GetEngine().Author(block.Header()) if err != nil { @@ -2693,11 +2693,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetAskTree(ctx context.Context, baseToken func (s *PublicXDCXTransactionPoolAPI) GetOrderById(ctx context.Context, baseToken, quoteToken common.Address, orderId uint64) (interface{}, error) { block := s.b.CurrentBlock() if block == nil { - return nil, errors.New("Current block not found") + return nil, errors.New("not find current block") } XDCxService := s.b.XDCxService() if XDCxService == nil { - return nil, errors.New("XDCX service not found") + return nil, errors.New("not find XDCX service") } author, err := s.b.GetEngine().Author(block.Header()) if err != nil { @@ -2710,7 +2710,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetOrderById(ctx context.Context, baseTok orderIdHash := common.BigToHash(new(big.Int).SetUint64(orderId)) orderitem := XDCxState.GetOrder(tradingstate.GetTradingOrderBookHash(baseToken, quoteToken), orderIdHash) if orderitem.Quantity == nil || orderitem.Quantity.Sign() == 0 { - return nil, errors.New("Order not found") + return nil, errors.New("not found order") } return orderitem, nil } @@ -2718,11 +2718,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetOrderById(ctx context.Context, baseTok func (s *PublicXDCXTransactionPoolAPI) GetTradingOrderBookInfo(ctx context.Context, baseToken, quoteToken common.Address) (*tradingstate.DumpOrderBookInfo, error) { block := s.b.CurrentBlock() if block == nil { - return nil, errors.New("Current block not found") + return nil, errors.New("not find current block") } XDCxService := s.b.XDCxService() if XDCxService == nil { - return nil, errors.New("XDCX service not found") + return nil, errors.New("not find XDCX service") } author, err := s.b.GetEngine().Author(block.Header()) if err != nil { @@ -2742,11 +2742,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetTradingOrderBookInfo(ctx context.Conte func (s *PublicXDCXTransactionPoolAPI) GetLiquidationPriceTree(ctx context.Context, baseToken, quoteToken common.Address) (map[*big.Int]tradingstate.DumpLendingBook, error) { block := s.b.CurrentBlock() if block == nil { - return nil, errors.New("Current block not found") + return nil, errors.New("not find current block") } XDCxService := s.b.XDCxService() if XDCxService == nil { - return nil, errors.New("XDCX service not found") + return nil, errors.New("not find XDCX service") } author, err := s.b.GetEngine().Author(block.Header()) if err != nil { @@ -2766,7 +2766,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetLiquidationPriceTree(ctx context.Conte func (s *PublicXDCXTransactionPoolAPI) GetInvestingTree(ctx context.Context, lendingToken common.Address, term uint64) (map[*big.Int]lendingstate.DumpOrderList, error) { block := s.b.CurrentBlock() if block == nil { - return nil, errors.New("Current block not found") + return nil, errors.New("not find current block") } lendingService := s.b.LendingService() if lendingService == nil { @@ -2790,7 +2790,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetInvestingTree(ctx context.Context, len func (s *PublicXDCXTransactionPoolAPI) GetBorrowingTree(ctx context.Context, lendingToken common.Address, term uint64) (map[*big.Int]lendingstate.DumpOrderList, error) { block := s.b.CurrentBlock() if block == nil { - return nil, errors.New("Current block not found") + return nil, errors.New("not find current block") } lendingService := s.b.LendingService() if lendingService == nil { @@ -2814,7 +2814,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetBorrowingTree(ctx context.Context, len func (s *PublicXDCXTransactionPoolAPI) GetLendingOrderBookInfo(tx context.Context, lendingToken common.Address, term uint64) (*lendingstate.DumpOrderBookInfo, error) { block := s.b.CurrentBlock() if block == nil { - return nil, errors.New("Current block not found") + return nil, errors.New("not find current block") } lendingService := s.b.LendingService() if lendingService == nil { @@ -2838,11 +2838,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetLendingOrderBookInfo(tx context.Contex func (s *PublicXDCXTransactionPoolAPI) getLendingOrderTree(ctx context.Context, lendingToken common.Address, term uint64) (map[*big.Int]lendingstate.LendingItem, error) { block := s.b.CurrentBlock() if block == nil { - return nil, errors.New("Current block not found") + return nil, errors.New("not find current block") } lendingService := s.b.LendingService() if lendingService == nil { - return nil, errors.New("XDCX Lending service not found") + return nil, errors.New("not find XDCX Lending service") } author, err := s.b.GetEngine().Author(block.Header()) if err != nil { @@ -2862,11 +2862,11 @@ func (s *PublicXDCXTransactionPoolAPI) getLendingOrderTree(ctx context.Context, func (s *PublicXDCXTransactionPoolAPI) GetLendingTradeTree(ctx context.Context, lendingToken common.Address, term uint64) (map[*big.Int]lendingstate.LendingTrade, error) { block := s.b.CurrentBlock() if block == nil { - return nil, errors.New("Current block not found") + return nil, errors.New("not find current block") } lendingService := s.b.LendingService() if lendingService == nil { - return nil, errors.New("XDCX Lending service not found") + return nil, errors.New("not find XDCX lending service") } author, err := s.b.GetEngine().Author(block.Header()) if err != nil { @@ -2886,11 +2886,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetLendingTradeTree(ctx context.Context, func (s *PublicXDCXTransactionPoolAPI) GetLiquidationTimeTree(ctx context.Context, lendingToken common.Address, term uint64) (map[*big.Int]lendingstate.DumpOrderList, error) { block := s.b.CurrentBlock() if block == nil { - return nil, errors.New("Current block not found") + return nil, errors.New("not find current block") } lendingService := s.b.LendingService() if lendingService == nil { - return nil, errors.New("XDCX Lending service not found") + return nil, errors.New("not find XDCX Lending service") } author, err := s.b.GetEngine().Author(block.Header()) if err != nil { @@ -2910,11 +2910,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetLiquidationTimeTree(ctx context.Contex func (s *PublicXDCXTransactionPoolAPI) GetLendingOrderCount(ctx context.Context, addr common.Address) (*hexutil.Uint64, error) { block := s.b.CurrentBlock() if block == nil { - return nil, errors.New("Current block not found") + return nil, errors.New("not find current block") } lendingService := s.b.LendingService() if lendingService == nil { - return nil, errors.New("XDCX Lending service not found") + return nil, errors.New("not find XDCX Lending service") } author, err := s.b.GetEngine().Author(block.Header()) if err != nil { @@ -2932,11 +2932,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetBestInvesting(ctx context.Context, len result := InterestVolume{} block := s.b.CurrentBlock() if block == nil { - return result, errors.New("Current block not found") + return result, errors.New("not find current block") } lendingService := s.b.LendingService() if lendingService == nil { - return result, errors.New("XDCX Lending service not found") + return result, errors.New("not find XDCX Lending service") } author, err := s.b.GetEngine().Author(block.Header()) if err != nil { @@ -2954,11 +2954,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetBestBorrowing(ctx context.Context, len result := InterestVolume{} block := s.b.CurrentBlock() if block == nil { - return result, errors.New("Current block not found") + return result, errors.New("not find current block") } lendingService := s.b.LendingService() if lendingService == nil { - return result, errors.New("XDCX Lending service not found") + return result, errors.New("not find XDCX Lending service") } author, err := s.b.GetEngine().Author(block.Header()) if err != nil { @@ -2975,11 +2975,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetBestBorrowing(ctx context.Context, len func (s *PublicXDCXTransactionPoolAPI) GetBids(ctx context.Context, baseToken, quoteToken common.Address) (map[*big.Int]*big.Int, error) { block := s.b.CurrentBlock() if block == nil { - return nil, errors.New("Current block not found") + return nil, errors.New("not find current block") } XDCxService := s.b.XDCxService() if XDCxService == nil { - return nil, errors.New("XDCX service not found") + return nil, errors.New("not find XDCX service") } author, err := s.b.GetEngine().Author(block.Header()) if err != nil { @@ -2999,11 +2999,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetBids(ctx context.Context, baseToken, q func (s *PublicXDCXTransactionPoolAPI) GetAsks(ctx context.Context, baseToken, quoteToken common.Address) (map[*big.Int]*big.Int, error) { block := s.b.CurrentBlock() if block == nil { - return nil, errors.New("Current block not found") + return nil, errors.New("not find current block") } XDCxService := s.b.XDCxService() if XDCxService == nil { - return nil, errors.New("XDCX service not found") + return nil, errors.New("not find XDCX service") } author, err := s.b.GetEngine().Author(block.Header()) if err != nil { @@ -3023,7 +3023,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetAsks(ctx context.Context, baseToken, q func (s *PublicXDCXTransactionPoolAPI) GetInvests(ctx context.Context, lendingToken common.Address, term uint64) (map[*big.Int]*big.Int, error) { block := s.b.CurrentBlock() if block == nil { - return nil, errors.New("Current block not found") + return nil, errors.New("not find current block") } lendingService := s.b.LendingService() if lendingService == nil { @@ -3047,7 +3047,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetInvests(ctx context.Context, lendingTo func (s *PublicXDCXTransactionPoolAPI) GetBorrows(ctx context.Context, lendingToken common.Address, term uint64) (map[*big.Int]*big.Int, error) { block := s.b.CurrentBlock() if block == nil { - return nil, errors.New("Current block not found") + return nil, errors.New("not find current block") } lendingService := s.b.LendingService() if lendingService == nil { @@ -3105,11 +3105,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetLendingOrderById(ctx context.Context, lendingItem := lendingstate.LendingItem{} block := s.b.CurrentBlock() if block == nil { - return lendingItem, errors.New("Current block not found") + return lendingItem, errors.New("not find current block") } lendingService := s.b.LendingService() if lendingService == nil { - return lendingItem, errors.New("XDCX Lending service not found") + return lendingItem, errors.New("not find XDCX lending service") } author, err := s.b.GetEngine().Author(block.Header()) if err != nil { @@ -3123,7 +3123,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetLendingOrderById(ctx context.Context, orderIdHash := common.BigToHash(new(big.Int).SetUint64(orderId)) lendingItem = lendingState.GetLendingOrder(lendingOrderBook, orderIdHash) if lendingItem.LendingId != orderId { - return lendingItem, errors.New("Lending Item not found") + return lendingItem, errors.New("not find lending item") } return lendingItem, nil } @@ -3132,11 +3132,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetLendingTradeById(ctx context.Context, lendingItem := lendingstate.LendingTrade{} block := s.b.CurrentBlock() if block == nil { - return lendingItem, errors.New("Current block not found") + return lendingItem, errors.New("not find current block") } lendingService := s.b.LendingService() if lendingService == nil { - return lendingItem, errors.New("XDCX Lending service not found") + return lendingItem, errors.New("not find XDCX Lending service") } author, err := s.b.GetEngine().Author(block.Header()) if err != nil { @@ -3150,7 +3150,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetLendingTradeById(ctx context.Context, tradeIdHash := common.BigToHash(new(big.Int).SetUint64(tradeId)) lendingItem = lendingState.GetLendingTrade(lendingOrderBook, tradeIdHash) if lendingItem.TradeId != tradeId { - return lendingItem, errors.New("Lending Item not found") + return lendingItem, errors.New("not find lending item") } return lendingItem, nil } @@ -3191,13 +3191,13 @@ type SignTransactionResult struct { // the given from address and it needs to be unlocked. func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) { if args.Gas == nil { - return nil, errors.New("gas not specified") + return nil, errors.New("not specify Gas") } if args.GasPrice == nil { - return nil, errors.New("gasPrice not specified") + return nil, errors.New("not specify GasPrice") } if args.Nonce == nil { - return nil, errors.New("nonce not specified") + return nil, errors.New("not specify Nonce") } if err := args.setDefaults(ctx, s.b); err != nil { return nil, err diff --git a/internal/jsre/deps/bindata.go b/internal/jsre/deps/bindata.go index 5cb51d61f3ba..50b5bbc93d0f 100644 --- a/internal/jsre/deps/bindata.go +++ b/internal/jsre/deps/bindata.go @@ -117,11 +117,11 @@ func Asset(name string) ([]byte, error) { if f, ok := _bindata[canonicalName]; ok { a, err := f() if err != nil { - return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + return nil, fmt.Errorf("can't read Asset %s by error: %v", name, err) } return a.bytes, nil } - return nil, fmt.Errorf("Asset %s not found", name) + return nil, fmt.Errorf("not found Asset %s", name) } // AssetString returns the asset contents as a string (instead of a []byte). @@ -155,11 +155,11 @@ func AssetInfo(name string) (os.FileInfo, error) { if f, ok := _bindata[canonicalName]; ok { a, err := f() if err != nil { - return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + return nil, fmt.Errorf("can't read AssetInfo %s by error: %v", name, err) } return a.info, nil } - return nil, fmt.Errorf("AssetInfo %s not found", name) + return nil, fmt.Errorf("not found AssetInfo %s", name) } // AssetDigest returns the digest of the file with the given name. It returns an @@ -169,11 +169,11 @@ func AssetDigest(name string) ([sha256.Size]byte, error) { if f, ok := _bindata[canonicalName]; ok { a, err := f() if err != nil { - return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err) + return [sha256.Size]byte{}, fmt.Errorf("can't read AssetDigest %s by error: %v", name, err) } return a.digest, nil } - return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name) + return [sha256.Size]byte{}, fmt.Errorf("not found AssetDigest %s", name) } // Digests returns a map of all known files and their checksums. @@ -230,12 +230,12 @@ func AssetDir(name string) ([]string, error) { for _, p := range pathList { node = node.Children[p] if node == nil { - return nil, fmt.Errorf("Asset %s not found", name) + return nil, fmt.Errorf("not found Asset %s", name) } } } if node.Func != nil { - return nil, fmt.Errorf("Asset %s not found", name) + return nil, fmt.Errorf("not found Asset %s", name) } rv := make([]string, 0, len(node.Children)) for childName := range node.Children { diff --git a/internal/jsre/jsre.go b/internal/jsre/jsre.go index 81c3e2217700..14fc602aac65 100644 --- a/internal/jsre/jsre.go +++ b/internal/jsre/jsre.go @@ -294,11 +294,11 @@ func (re *JSRE) loadScript(call Call) (goja.Value, error) { file = common.AbsolutePath(re.assetPath, file) source, err := os.ReadFile(file) if err != nil { - return nil, fmt.Errorf("Could not read file %s: %v", file, err) + return nil, fmt.Errorf("could not read file %s: %v", file, err) } value, err := compileAndRun(re.vm, file, string(source)) if err != nil { - return nil, fmt.Errorf("Error while compiling or running script: %v", err) + return nil, fmt.Errorf("error while compiling or running script: %v", err) } return value, nil } diff --git a/les/protocol.go b/les/protocol.go index 273ccfcce974..1122f13e9bc9 100644 --- a/les/protocol.go +++ b/les/protocol.go @@ -161,7 +161,7 @@ func (a *announceData) checkSignature(pubKey *ecdsa.PublicKey) error { if bytes.Equal(pbytes, recPubkey) { return nil } else { - return errors.New("Wrong signature") + return errors.New("wrong signature") } } diff --git a/les/retrieve.go b/les/retrieve.go index 509b8a3e356c..e7894987bebb 100644 --- a/les/retrieve.go +++ b/les/retrieve.go @@ -119,7 +119,7 @@ func (rm *retrieveManager) retrieve(ctx context.Context, reqID uint64, req *dist case <-ctx.Done(): sentReq.stop(ctx.Err()) case <-shutdown: - sentReq.stop(errors.New("Client is shutting down")) + sentReq.stop(errors.New("client is shutting down")) } return sentReq.getError() } diff --git a/light/postprocess.go b/light/postprocess.go index 5e6e70b8d88b..746c40548fa2 100644 --- a/light/postprocess.go +++ b/light/postprocess.go @@ -19,10 +19,11 @@ package light import ( "encoding/binary" "errors" - "github.com/XinFinOrg/XDPoSChain/core/rawdb" "math/big" "time" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" + "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/bitutil" "github.com/XinFinOrg/XDPoSChain/core" @@ -81,9 +82,9 @@ var trustedCheckpoints = map[common.Hash]trustedCheckpoint{ } var ( - ErrNoTrustedCht = errors.New("No trusted canonical hash trie") - ErrNoTrustedBloomTrie = errors.New("No trusted bloom trie") - ErrNoHeader = errors.New("Header not found") + ErrNoTrustedCht = errors.New("no trusted canonical hash trie") + ErrNoTrustedBloomTrie = errors.New("no trusted bloom trie") + ErrNoHeader = errors.New("header not found") chtPrefix = []byte("chtRoot-") // chtPrefix + chtNum (uint64 big endian) -> trie root hash ChtTablePrefix = "cht-" ) diff --git a/light/txpool.go b/light/txpool.go index 292e91b92dab..ed551e23290f 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -353,11 +353,11 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error // check if sender is in black list if tx.From() != nil && common.Blacklist[*tx.From()] { - return fmt.Errorf("Reject transaction with sender in black-list: %v", tx.From().Hex()) + return fmt.Errorf("reject transaction with sender in black-list: %v", tx.From().Hex()) } // check if receiver is in black list if tx.To() != nil && common.Blacklist[*tx.To()] { - return fmt.Errorf("Reject transaction with receiver in black-list: %v", tx.To().Hex()) + return fmt.Errorf("reject transaction with receiver in black-list: %v", tx.To().Hex()) } // validate minFee slot for XDCZ @@ -423,7 +423,7 @@ func (self *TxPool) add(ctx context.Context, tx *types.Transaction) error { hash := tx.Hash() if self.pending[hash] != nil { - return fmt.Errorf("Known transaction (%x)", hash[:4]) + return fmt.Errorf("known transaction (%x)", hash[:4]) } err := self.validateTx(ctx, tx) if err != nil { diff --git a/log/logger.go b/log/logger.go index 15c83a9b25d1..1a04e4ee9bde 100644 --- a/log/logger.go +++ b/log/logger.go @@ -81,7 +81,7 @@ func LvlFromString(lvlString string) (Lvl, error) { case "crit": return LvlCrit, nil default: - return LvlDebug, fmt.Errorf("Unknown level: %v", lvlString) + return LvlDebug, fmt.Errorf("unknown level: %v", lvlString) } } diff --git a/metrics/disk_nop.go b/metrics/disk_nop.go index 4319f8b277f8..41bbe9adb2d7 100644 --- a/metrics/disk_nop.go +++ b/metrics/disk_nop.go @@ -14,6 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . +//go:build !linux // +build !linux package metrics @@ -22,5 +23,5 @@ import "errors" // ReadDiskStats retrieves the disk IO stats belonging to the current process. func ReadDiskStats(stats *DiskStats) error { - return errors.New("Not implemented") + return errors.New("not implemented") } diff --git a/metrics/librato/client.go b/metrics/librato/client.go index b74fedfec669..75503bb19d58 100644 --- a/metrics/librato/client.go +++ b/metrics/librato/client.go @@ -96,7 +96,7 @@ func (self *LibratoClient) PostMetrics(batch Batch) (err error) { if body, err = io.ReadAll(resp.Body); err != nil { body = []byte(fmt.Sprintf("(could not fetch response body for error: %s)", err)) } - err = fmt.Errorf("Unable to post to Librato: %d %s %s", resp.StatusCode, resp.Status, string(body)) + err = fmt.Errorf("unable to post to Librato: %d %s %s", resp.StatusCode, resp.Status, string(body)) } return } diff --git a/miner/miner.go b/miner/miner.go index 835f0f014b74..8177e45c8e6f 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -158,7 +158,7 @@ func (self *Miner) HashRate() (tot int64) { func (self *Miner) SetExtra(extra []byte) error { if uint64(len(extra)) > params.MaximumExtraDataSize { - return fmt.Errorf("Extra exceeds max length. %d > %v", len(extra), params.MaximumExtraDataSize) + return fmt.Errorf("extra exceeds max length: %d > %v", len(extra), params.MaximumExtraDataSize) } self.worker.setExtra(extra) return nil diff --git a/miner/remote_agent.go b/miner/remote_agent.go index 8b4f9b9a8d49..dd30d81822e6 100644 --- a/miner/remote_agent.go +++ b/miner/remote_agent.go @@ -128,7 +128,7 @@ func (a *RemoteAgent) GetWork() ([3]string, error) { a.work[block.HashNoNonce()] = a.currentWork return res, nil } - return res, errors.New("No work available yet, don't panic.") + return res, errors.New("no work available yet, don't panic") } // SubmitWork tries to inject a pow solution into the remote agent, returning diff --git a/node/node_test.go b/node/node_test.go index 2bff5a68cc1a..40f4e7118aaa 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -56,7 +56,7 @@ func TestNodeLifeCycle(t *testing.T) { t.Fatalf("failed to start node: %v", err) } if err := stack.Start(); err != ErrNodeRunning { - t.Fatalf("start failure mismatch: have %v, want %v ", err, ErrNodeRunning) + t.Fatalf("start failure mismatch: have %v, want %v", err, ErrNodeRunning) } // Ensure that a node can be restarted arbitrarily many times for i := 0; i < 3; i++ { @@ -69,7 +69,7 @@ func TestNodeLifeCycle(t *testing.T) { t.Fatalf("failed to stop node: %v", err) } if err := stack.Stop(); err != ErrNodeStopped { - t.Fatalf("stop failure mismatch: have %v, want %v ", err, ErrNodeStopped) + t.Fatalf("stop failure mismatch: have %v, want %v", err, ErrNodeStopped) } } diff --git a/p2p/peer.go b/p2p/peer.go index 1d1cfc8906f6..e54d70a3b2c8 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -160,7 +160,7 @@ func (p *Peer) Disconnect(reason DiscReason) { // String implements fmt.Stringer. func (p *Peer) String() string { - return fmt.Sprintf("Peer %x %v ", p.rw.id[:8], p.RemoteAddr()) + return fmt.Sprintf("Peer %x %v", p.rw.id[:8], p.RemoteAddr()) } // Inbound returns true if the peer is an inbound connection diff --git a/p2p/server.go b/p2p/server.go index 2ccb4cf17a84..d43451b000dd 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -364,7 +364,7 @@ type sharedUDPConn struct { func (s *sharedUDPConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) { packet, ok := <-s.unhandled if !ok { - return 0, nil, errors.New("Connection was closed") + return 0, nil, errors.New("connection was closed") } l := len(packet.Data) if l > len(b) { diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 72e9f210809a..905c81b0d3e6 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -106,7 +106,7 @@ func (t *BlockTest) Run() error { return err } if gblock.Hash() != t.json.Genesis.Hash { - return fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x\n", gblock.Hash().Bytes(), t.json.Genesis.Hash) + return fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x", gblock.Hash().Bytes(), t.json.Genesis.Hash) } 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(), t.json.Genesis.StateRoot) @@ -174,7 +174,7 @@ func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) if b.BlockHeader == nil { continue // OK - block is supposed to be invalid, continue with next block } else { - return nil, fmt.Errorf("Block RLP decoding failed when expected to succeed: %v", err) + return nil, fmt.Errorf("block RLP decoding failed when expected to succeed: %v", err) } } // RLP decoding worked, try to insert into chain: @@ -184,16 +184,16 @@ func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) if b.BlockHeader == nil { continue // OK - block is supposed to be invalid, continue with next block } else { - return nil, fmt.Errorf("Block #%v insertion into chain failed: %v", blocks[i].Number(), err) + return nil, fmt.Errorf("block #%v insertion into chain failed: %v", blocks[i].Number(), err) } } if b.BlockHeader == nil { - return nil, errors.New("Block insertion should have failed") + return nil, errors.New("block insertion should have failed") } // validate RLP decoding by checking all values against test file JSON if err = validateHeader(b.BlockHeader, cb.Header()); err != nil { - return nil, fmt.Errorf("Deserialised block header validation failed: %v", err) + return nil, fmt.Errorf("deserialised block header validation failed: %v", err) } validBlocks = append(validBlocks, b) } @@ -202,49 +202,49 @@ func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) func validateHeader(h *btHeader, h2 *types.Header) error { if h.Bloom != h2.Bloom { - return fmt.Errorf("Bloom: want: %x have: %x", h.Bloom, h2.Bloom) + return fmt.Errorf("mismatch Bloom: want: %x have: %x", h.Bloom, h2.Bloom) } if h.Coinbase != h2.Coinbase { - return fmt.Errorf("Coinbase: want: %x have: %x", h.Coinbase, h2.Coinbase) + return fmt.Errorf("mismatch Coinbase: want: %x have: %x", h.Coinbase, h2.Coinbase) } if h.MixHash != h2.MixDigest { - return fmt.Errorf("MixHash: want: %x have: %x", h.MixHash, h2.MixDigest) + return fmt.Errorf("mismatch MixHash: want: %x have: %x", h.MixHash, h2.MixDigest) } if h.Nonce != h2.Nonce { - return fmt.Errorf("Nonce: want: %x have: %x", h.Nonce, h2.Nonce) + return fmt.Errorf("mismatch Nonce: want: %x have: %x", h.Nonce, h2.Nonce) } if h.Number.Cmp(h2.Number) != 0 { - return fmt.Errorf("Number: want: %v have: %v", h.Number, h2.Number) + return fmt.Errorf("mismatch Number: want: %v have: %v", h.Number, h2.Number) } if h.ParentHash != h2.ParentHash { - return fmt.Errorf("Parent hash: want: %x have: %x", h.ParentHash, h2.ParentHash) + return fmt.Errorf("mismatch Parent hash: want: %x have: %x", h.ParentHash, h2.ParentHash) } if h.ReceiptTrie != h2.ReceiptHash { - return fmt.Errorf("Receipt hash: want: %x have: %x", h.ReceiptTrie, h2.ReceiptHash) + return fmt.Errorf("mismatch Receipt hash: want: %x have: %x", h.ReceiptTrie, h2.ReceiptHash) } if h.TransactionsTrie != h2.TxHash { - return fmt.Errorf("Tx hash: want: %x have: %x", h.TransactionsTrie, h2.TxHash) + return fmt.Errorf("mismatch tx hash: want: %x have: %x", h.TransactionsTrie, h2.TxHash) } if h.StateRoot != h2.Root { - return fmt.Errorf("State hash: want: %x have: %x", h.StateRoot, h2.Root) + return fmt.Errorf("mismatch state hash: want: %x have: %x", h.StateRoot, h2.Root) } if h.UncleHash != h2.UncleHash { - return fmt.Errorf("Uncle hash: want: %x have: %x", h.UncleHash, h2.UncleHash) + return fmt.Errorf("mismatch UncleHash: want: %x have: %x", h.UncleHash, h2.UncleHash) } if !bytes.Equal(h.ExtraData, h2.Extra) { - return fmt.Errorf("Extra data: want: %x have: %x", h.ExtraData, h2.Extra) + return fmt.Errorf("mismatch ExtraData: want: %x have: %x", h.ExtraData, h2.Extra) } if h.Difficulty.Cmp(h2.Difficulty) != 0 { - return fmt.Errorf("Difficulty: want: %v have: %v", h.Difficulty, h2.Difficulty) + return fmt.Errorf("mismatch difficulty: want: %v have: %v", h.Difficulty, h2.Difficulty) } if h.GasLimit != h2.GasLimit { - return fmt.Errorf("GasLimit: want: %d have: %d", h.GasLimit, h2.GasLimit) + return fmt.Errorf("mismatch GasLimit: want: %d have: %d", h.GasLimit, h2.GasLimit) } if h.GasUsed != h2.GasUsed { - return fmt.Errorf("GasUsed: want: %d have: %d", h.GasUsed, h2.GasUsed) + return fmt.Errorf("mismatch GasUsed: want: %d have: %d", h.GasUsed, h2.GasUsed) } if h.Timestamp.Cmp(h2.Time) != 0 { - return fmt.Errorf("Timestamp: want: %v have: %v", h.Timestamp, h2.Time) + return fmt.Errorf("mismatch Timestamp: want: %v have: %v", h.Timestamp, h2.Time) } return nil } @@ -282,7 +282,7 @@ func (t *BlockTest) validateImportedHeaders(cm *core.BlockChain, validBlocks []b // be part of the longest chain until last block is imported. for b := cm.CurrentBlock(); b != nil && b.NumberU64() != 0; b = cm.GetBlockByHash(b.Header().ParentHash) { if err := validateHeader(bmap[b.Hash()].BlockHeader, b.Header()); err != nil { - return fmt.Errorf("Imported block header validation failed: %v", err) + return fmt.Errorf("imported block header validation failed: %v", err) } } return nil diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go index 803e0f8d53f7..1b889c7e3a97 100644 --- a/tests/transaction_test_util.go +++ b/tests/transaction_test_util.go @@ -83,7 +83,7 @@ func (tt *TransactionTest) Run(config *params.ChainConfig) error { return err } if sender != common.BytesToAddress(tt.json.Sender) { - return fmt.Errorf("Sender mismatch: got %x, want %x", sender, tt.json.Sender) + return fmt.Errorf("mismatch Sender: got %x, want %x", sender, tt.json.Sender) } // Check decoded fields. err = tt.json.Transaction.verify(signer, tx) @@ -98,36 +98,36 @@ func (tt *TransactionTest) Run(config *params.ChainConfig) error { func (tt *ttTransaction) verify(signer types.Signer, tx *types.Transaction) error { if !bytes.Equal(tx.Data(), tt.Data) { - return fmt.Errorf("Tx input data mismatch: got %x want %x", tx.Data(), tt.Data) + return fmt.Errorf("mismatch tx input data: got %x want %x", tx.Data(), tt.Data) } if tx.Gas() != tt.GasLimit { - return fmt.Errorf("GasLimit mismatch: got %d, want %d", tx.Gas(), tt.GasLimit) + return fmt.Errorf("mismatch GasLimit: got %d, want %d", tx.Gas(), tt.GasLimit) } if tx.GasPrice().Cmp(tt.GasPrice) != 0 { - return fmt.Errorf("GasPrice mismatch: got %v, want %v", tx.GasPrice(), tt.GasPrice) + return fmt.Errorf("mismatch GasPrice: got %v, want %v", tx.GasPrice(), tt.GasPrice) } if tx.Nonce() != tt.Nonce { - return fmt.Errorf("Nonce mismatch: got %v, want %v", tx.Nonce(), tt.Nonce) + return fmt.Errorf("mismatch Nonce: got %v, want %v", tx.Nonce(), tt.Nonce) } v, r, s := tx.RawSignatureValues() if r.Cmp(tt.R) != 0 { - return fmt.Errorf("R mismatch: got %v, want %v", r, tt.R) + return fmt.Errorf("mismatch R: got %v, want %v", r, tt.R) } if s.Cmp(tt.S) != 0 { - return fmt.Errorf("S mismatch: got %v, want %v", s, tt.S) + return fmt.Errorf("mismatch S: got %v, want %v", s, tt.S) } if v.Cmp(tt.V) != 0 { - return fmt.Errorf("V mismatch: got %v, want %v", v, tt.V) + return fmt.Errorf("mismatch V: got %v, want %v", v, tt.V) } if tx.To() == nil { if tt.To != (common.Address{}) { - return fmt.Errorf("To mismatch when recipient is nil (contract creation): %x", tt.To) + return fmt.Errorf("mismatch To when recipient is nil (contract creation): %x", tt.To) } } else if *tx.To() != tt.To { - return fmt.Errorf("To mismatch: got %x, want %x", *tx.To(), tt.To) + return fmt.Errorf("mismatch To: got %x, want %x", *tx.To(), tt.To) } if tx.Value().Cmp(tt.Value) != 0 { - return fmt.Errorf("Value mismatch: got %x, want %x", tx.Value(), tt.Value) + return fmt.Errorf("mismatch Value: got %x, want %x", tx.Value(), tt.Value) } return nil } diff --git a/whisper/whisperv5/whisper.go b/whisper/whisperv5/whisper.go index 9bbb1cae01d0..929f24ceaca5 100644 --- a/whisper/whisperv5/whisper.go +++ b/whisper/whisperv5/whisper.go @@ -194,7 +194,7 @@ func (w *Whisper) getPeer(peerID []byte) (*Peer, error) { return p, nil } } - return nil, fmt.Errorf("Could not find peer with ID: %x", peerID) + return nil, fmt.Errorf("could not find peer with ID: %x", peerID) } // AllowP2PMessagesFromPeer marks specific peer trusted, diff --git a/whisper/whisperv6/whisper.go b/whisper/whisperv6/whisper.go index 0b83c89d13d0..f72e5ede37c3 100644 --- a/whisper/whisperv6/whisper.go +++ b/whisper/whisperv6/whisper.go @@ -326,7 +326,7 @@ func (whisper *Whisper) getPeer(peerID []byte) (*Peer, error) { return p, nil } } - return nil, fmt.Errorf("Could not find peer with ID: %x", peerID) + return nil, fmt.Errorf("could not find peer with ID: %x", peerID) } // AllowP2PMessagesFromPeer marks specific peer trusted, From f79763ca88e4bec7f84d36ce00515c1df59d22ae Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 24 Oct 2024 12:49:37 +0800 Subject: [PATCH 034/479] all: fix staticcheck warning S1039: unnecessary use of fmt.Sprintf --- accounts/abi/bind/bind_test.go | 2 +- cmd/puppeth/module_explorer.go | 2 +- cmd/puppeth/module_wallet.go | 4 ++-- p2p/discv5/net.go | 2 +- p2p/discv5/ntp.go | 2 +- whisper/mailserver/mailserver.go | 8 ++++---- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index ce6e36bffd34..ba5668486447 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -821,7 +821,7 @@ func TestBindings(t *testing.T) { } t.Log("Using config", params.TestXDPoSMockChainConfig) // Skip the test if the go-ethereum sources are symlinked (https://github.com/golang/go/issues/14845) - linkTestCode := fmt.Sprintf("package linktest\nfunc CheckSymlinks(){\nfmt.Println(backends.NewSimulatedBackend(nil))\n}") + linkTestCode := "package linktest\nfunc CheckSymlinks(){\nfmt.Println(backends.NewSimulatedBackend(nil))\n}" linkTestDeps, err := imports.Process(os.TempDir(), []byte(linkTestCode), nil) if err != nil { t.Fatalf("failed check for goimports symlink bug: %v", err) diff --git a/cmd/puppeth/module_explorer.go b/cmd/puppeth/module_explorer.go index c6ff5c7767e3..0bed1107ec85 100644 --- a/cmd/puppeth/module_explorer.go +++ b/cmd/puppeth/module_explorer.go @@ -197,7 +197,7 @@ func checkExplorer(client *sshClient, network string) (*explorerInfos, error) { // Run a sanity check to see if the devp2p is reachable nodePort := infos.portmap[infos.envvars["NODE_PORT"]] if err = checkPort(client.server, nodePort); err != nil { - log.Warn(fmt.Sprintf("Explorer devp2p port seems unreachable"), "server", client.server, "port", nodePort, "err", err) + log.Warn("Explorer devp2p port seems unreachable", "server", client.server, "port", nodePort, "err", err) } // Assemble and return the useful infos stats := &explorerInfos{ diff --git a/cmd/puppeth/module_wallet.go b/cmd/puppeth/module_wallet.go index 25c630e24553..6dcbdfbec5db 100644 --- a/cmd/puppeth/module_wallet.go +++ b/cmd/puppeth/module_wallet.go @@ -181,11 +181,11 @@ func checkWallet(client *sshClient, network string) (*walletInfos, error) { // Run a sanity check to see if the devp2p and RPC ports are reachable nodePort := infos.portmap[infos.envvars["NODE_PORT"]] if err = checkPort(client.server, nodePort); err != nil { - log.Warn(fmt.Sprintf("Wallet devp2p port seems unreachable"), "server", client.server, "port", nodePort, "err", err) + log.Warn("Wallet devp2p port seems unreachable", "server", client.server, "port", nodePort, "err", err) } rpcPort := infos.portmap["8545/tcp"] if err = checkPort(client.server, rpcPort); err != nil { - log.Warn(fmt.Sprintf("Wallet RPC port seems unreachable"), "server", client.server, "port", rpcPort, "err", err) + log.Warn("Wallet RPC port seems unreachable", "server", client.server, "port", rpcPort, "err", err) } // Assemble and return the useful infos stats := &walletInfos{ diff --git a/p2p/discv5/net.go b/p2p/discv5/net.go index 097771553d28..9bf529a4f6d6 100644 --- a/p2p/discv5/net.go +++ b/p2p/discv5/net.go @@ -648,7 +648,7 @@ loop: } log.Trace("loop stopped") - log.Debug(fmt.Sprintf("shutting down")) + log.Debug("shutting down") if net.conn != nil { net.conn.Close() } diff --git a/p2p/discv5/ntp.go b/p2p/discv5/ntp.go index 411b4d048511..a6e279a3ccfa 100644 --- a/p2p/discv5/ntp.go +++ b/p2p/discv5/ntp.go @@ -51,7 +51,7 @@ func checkClockDrift() { } if drift < -driftThreshold || drift > driftThreshold { warning := fmt.Sprintf("System clock seems off by %v, which can prevent network connectivity", drift) - howtofix := fmt.Sprintf("Please enable network time synchronisation in system settings") + howtofix := "Please enable network time synchronisation in system settings" separator := strings.Repeat("-", len(warning)) log.Warn(separator) diff --git a/whisper/mailserver/mailserver.go b/whisper/mailserver/mailserver.go index 4b668330e627..49682454e26a 100644 --- a/whisper/mailserver/mailserver.go +++ b/whisper/mailserver/mailserver.go @@ -159,7 +159,7 @@ func (s *WMailServer) validateRequest(peerID []byte, request *whisper.Envelope) f := whisper.Filter{KeySym: s.key} decrypted := request.Open(&f) if decrypted == nil { - log.Warn(fmt.Sprintf("Failed to decrypt p2p request")) + log.Warn("Failed to decrypt p2p request") return false, 0, 0, nil } @@ -171,19 +171,19 @@ func (s *WMailServer) validateRequest(peerID []byte, request *whisper.Envelope) // if you want to check the signature, you can do it here. e.g.: // if !bytes.Equal(peerID, src) { if src == nil { - log.Warn(fmt.Sprintf("Wrong signature of p2p request")) + log.Warn("Wrong signature of p2p request") return false, 0, 0, nil } var bloom []byte payloadSize := len(decrypted.Payload) if payloadSize < 8 { - log.Warn(fmt.Sprintf("Undersized p2p request")) + log.Warn("Undersized p2p request") return false, 0, 0, nil } else if payloadSize == 8 { bloom = whisper.MakeFullNodeBloom() } else if payloadSize < 8+whisper.BloomFilterSize { - log.Warn(fmt.Sprintf("Undersized bloom filter in p2p request")) + log.Warn("Undersized bloom filter in p2p request") return false, 0, 0, nil } else { bloom = decrypted.Payload[8 : 8+whisper.BloomFilterSize] From 3409ada1492f31fad809b1904dc169d5301bff28 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 24 Oct 2024 14:28:16 +0800 Subject: [PATCH 035/479] metrics: fix staticcheck warning ST1017: don't use yoda conditions --- metrics/counter_test.go | 16 +++--- metrics/ewma_test.go | 96 +++++++++++++++++------------------ metrics/gauge_float64_test.go | 2 +- metrics/gauge_test.go | 8 +-- metrics/histogram_test.go | 34 ++++++------- metrics/json_test.go | 2 +- metrics/meter_test.go | 6 +-- metrics/registry_test.go | 14 ++--- metrics/runtime_test.go | 8 +-- metrics/sample.go | 8 +-- metrics/sample_test.go | 56 ++++++++++---------- metrics/timer_test.go | 28 +++++----- 12 files changed, 139 insertions(+), 139 deletions(-) diff --git a/metrics/counter_test.go b/metrics/counter_test.go index dfb03b4e8845..af26ef1548fe 100644 --- a/metrics/counter_test.go +++ b/metrics/counter_test.go @@ -14,7 +14,7 @@ func TestCounterClear(t *testing.T) { c := NewCounter() c.Inc(1) c.Clear() - if count := c.Count(); 0 != count { + if count := c.Count(); count != 0 { t.Errorf("c.Count(): 0 != %v\n", count) } } @@ -22,7 +22,7 @@ func TestCounterClear(t *testing.T) { func TestCounterDec1(t *testing.T) { c := NewCounter() c.Dec(1) - if count := c.Count(); -1 != count { + if count := c.Count(); count != -1 { t.Errorf("c.Count(): -1 != %v\n", count) } } @@ -30,7 +30,7 @@ func TestCounterDec1(t *testing.T) { func TestCounterDec2(t *testing.T) { c := NewCounter() c.Dec(2) - if count := c.Count(); -2 != count { + if count := c.Count(); count != -2 { t.Errorf("c.Count(): -2 != %v\n", count) } } @@ -38,7 +38,7 @@ func TestCounterDec2(t *testing.T) { func TestCounterInc1(t *testing.T) { c := NewCounter() c.Inc(1) - if count := c.Count(); 1 != count { + if count := c.Count(); count != 1 { t.Errorf("c.Count(): 1 != %v\n", count) } } @@ -46,7 +46,7 @@ func TestCounterInc1(t *testing.T) { func TestCounterInc2(t *testing.T) { c := NewCounter() c.Inc(2) - if count := c.Count(); 2 != count { + if count := c.Count(); count != 2 { t.Errorf("c.Count(): 2 != %v\n", count) } } @@ -56,14 +56,14 @@ func TestCounterSnapshot(t *testing.T) { c.Inc(1) snapshot := c.Snapshot() c.Inc(1) - if count := snapshot.Count(); 1 != count { + if count := snapshot.Count(); count != 1 { t.Errorf("c.Count(): 1 != %v\n", count) } } func TestCounterZero(t *testing.T) { c := NewCounter() - if count := c.Count(); 0 != count { + if count := c.Count(); count != 0 { t.Errorf("c.Count(): 0 != %v\n", count) } } @@ -71,7 +71,7 @@ func TestCounterZero(t *testing.T) { func TestGetOrRegisterCounter(t *testing.T) { r := NewRegistry() NewRegisteredCounter("foo", r).Inc(47) - if c := GetOrRegisterCounter("foo", r); 47 != c.Count() { + if c := GetOrRegisterCounter("foo", r); c.Count() != 47 { t.Fatal(c) } } diff --git a/metrics/ewma_test.go b/metrics/ewma_test.go index 0430fbd24725..39e67c605b89 100644 --- a/metrics/ewma_test.go +++ b/metrics/ewma_test.go @@ -15,67 +15,67 @@ func TestEWMA1(t *testing.T) { a := NewEWMA1() a.Update(3) a.Tick() - if rate := a.Rate(); 0.6 != rate { + if rate := a.Rate(); rate != 0.6 { t.Errorf("initial a.Rate(): 0.6 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.22072766470286553 != rate { + if rate := a.Rate(); rate != 0.22072766470286553 { t.Errorf("1 minute a.Rate(): 0.22072766470286553 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.08120116994196772 != rate { + if rate := a.Rate(); rate != 0.08120116994196772 { t.Errorf("2 minute a.Rate(): 0.08120116994196772 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.029872241020718428 != rate { + if rate := a.Rate(); rate != 0.029872241020718428 { t.Errorf("3 minute a.Rate(): 0.029872241020718428 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.01098938333324054 != rate { + if rate := a.Rate(); rate != 0.01098938333324054 { t.Errorf("4 minute a.Rate(): 0.01098938333324054 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.004042768199451294 != rate { + if rate := a.Rate(); rate != 0.004042768199451294 { t.Errorf("5 minute a.Rate(): 0.004042768199451294 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.0014872513059998212 != rate { + if rate := a.Rate(); rate != 0.0014872513059998212 { t.Errorf("6 minute a.Rate(): 0.0014872513059998212 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.0005471291793327122 != rate { + if rate := a.Rate(); rate != 0.0005471291793327122 { t.Errorf("7 minute a.Rate(): 0.0005471291793327122 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.00020127757674150815 != rate { + if rate := a.Rate(); rate != 0.00020127757674150815 { t.Errorf("8 minute a.Rate(): 0.00020127757674150815 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 7.404588245200814e-05 != rate { + if rate := a.Rate(); rate != 7.404588245200814e-05 { t.Errorf("9 minute a.Rate(): 7.404588245200814e-05 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 2.7239957857491083e-05 != rate { + if rate := a.Rate(); rate != 2.7239957857491083e-05 { t.Errorf("10 minute a.Rate(): 2.7239957857491083e-05 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 1.0021020474147462e-05 != rate { + if rate := a.Rate(); rate != 1.0021020474147462e-05 { t.Errorf("11 minute a.Rate(): 1.0021020474147462e-05 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 3.6865274119969525e-06 != rate { + if rate := a.Rate(); rate != 3.6865274119969525e-06 { t.Errorf("12 minute a.Rate(): 3.6865274119969525e-06 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 1.3561976441886433e-06 != rate { + if rate := a.Rate(); rate != 1.3561976441886433e-06 { t.Errorf("13 minute a.Rate(): 1.3561976441886433e-06 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 4.989172314621449e-07 != rate { + if rate := a.Rate(); rate != 4.989172314621449e-07 { t.Errorf("14 minute a.Rate(): 4.989172314621449e-07 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 1.8354139230109722e-07 != rate { + if rate := a.Rate(); rate != 1.8354139230109722e-07 { t.Errorf("15 minute a.Rate(): 1.8354139230109722e-07 != %v\n", rate) } } @@ -84,67 +84,67 @@ func TestEWMA5(t *testing.T) { a := NewEWMA5() a.Update(3) a.Tick() - if rate := a.Rate(); 0.6 != rate { + if rate := a.Rate(); rate != 0.6 { t.Errorf("initial a.Rate(): 0.6 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.49123845184678905 != rate { + if rate := a.Rate(); rate != 0.49123845184678905 { t.Errorf("1 minute a.Rate(): 0.49123845184678905 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.4021920276213837 != rate { + if rate := a.Rate(); rate != 0.4021920276213837 { t.Errorf("2 minute a.Rate(): 0.4021920276213837 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.32928698165641596 != rate { + if rate := a.Rate(); rate != 0.32928698165641596 { t.Errorf("3 minute a.Rate(): 0.32928698165641596 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.269597378470333 != rate { + if rate := a.Rate(); rate != 0.269597378470333 { t.Errorf("4 minute a.Rate(): 0.269597378470333 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.2207276647028654 != rate { + if rate := a.Rate(); rate != 0.2207276647028654 { t.Errorf("5 minute a.Rate(): 0.2207276647028654 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.18071652714732128 != rate { + if rate := a.Rate(); rate != 0.18071652714732128 { t.Errorf("6 minute a.Rate(): 0.18071652714732128 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.14795817836496392 != rate { + if rate := a.Rate(); rate != 0.14795817836496392 { t.Errorf("7 minute a.Rate(): 0.14795817836496392 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.12113791079679326 != rate { + if rate := a.Rate(); rate != 0.12113791079679326 { t.Errorf("8 minute a.Rate(): 0.12113791079679326 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.09917933293295193 != rate { + if rate := a.Rate(); rate != 0.09917933293295193 { t.Errorf("9 minute a.Rate(): 0.09917933293295193 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.08120116994196763 != rate { + if rate := a.Rate(); rate != 0.08120116994196763 { t.Errorf("10 minute a.Rate(): 0.08120116994196763 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.06648189501740036 != rate { + if rate := a.Rate(); rate != 0.06648189501740036 { t.Errorf("11 minute a.Rate(): 0.06648189501740036 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.05443077197364752 != rate { + if rate := a.Rate(); rate != 0.05443077197364752 { t.Errorf("12 minute a.Rate(): 0.05443077197364752 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.04456414692860035 != rate { + if rate := a.Rate(); rate != 0.04456414692860035 { t.Errorf("13 minute a.Rate(): 0.04456414692860035 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.03648603757513079 != rate { + if rate := a.Rate(); rate != 0.03648603757513079 { t.Errorf("14 minute a.Rate(): 0.03648603757513079 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.0298722410207183831020718428 != rate { + if rate := a.Rate(); rate != 0.0298722410207183831020718428 { t.Errorf("15 minute a.Rate(): 0.0298722410207183831020718428 != %v\n", rate) } } @@ -153,67 +153,67 @@ func TestEWMA15(t *testing.T) { a := NewEWMA15() a.Update(3) a.Tick() - if rate := a.Rate(); 0.6 != rate { + if rate := a.Rate(); rate != 0.6 { t.Errorf("initial a.Rate(): 0.6 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.5613041910189706 != rate { + if rate := a.Rate(); rate != 0.5613041910189706 { t.Errorf("1 minute a.Rate(): 0.5613041910189706 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.5251039914257684 != rate { + if rate := a.Rate(); rate != 0.5251039914257684 { t.Errorf("2 minute a.Rate(): 0.5251039914257684 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.4912384518467888184678905 != rate { + if rate := a.Rate(); rate != 0.4912384518467888184678905 { t.Errorf("3 minute a.Rate(): 0.4912384518467888184678905 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.459557003018789 != rate { + if rate := a.Rate(); rate != 0.459557003018789 { t.Errorf("4 minute a.Rate(): 0.459557003018789 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.4299187863442732 != rate { + if rate := a.Rate(); rate != 0.4299187863442732 { t.Errorf("5 minute a.Rate(): 0.4299187863442732 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.4021920276213831 != rate { + if rate := a.Rate(); rate != 0.4021920276213831 { t.Errorf("6 minute a.Rate(): 0.4021920276213831 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.37625345116383313 != rate { + if rate := a.Rate(); rate != 0.37625345116383313 { t.Errorf("7 minute a.Rate(): 0.37625345116383313 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.3519877317060185 != rate { + if rate := a.Rate(); rate != 0.3519877317060185 { t.Errorf("8 minute a.Rate(): 0.3519877317060185 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.3292869816564153165641596 != rate { + if rate := a.Rate(); rate != 0.3292869816564153165641596 { t.Errorf("9 minute a.Rate(): 0.3292869816564153165641596 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.3080502714195546 != rate { + if rate := a.Rate(); rate != 0.3080502714195546 { t.Errorf("10 minute a.Rate(): 0.3080502714195546 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.2881831806538789 != rate { + if rate := a.Rate(); rate != 0.2881831806538789 { t.Errorf("11 minute a.Rate(): 0.2881831806538789 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.26959737847033216 != rate { + if rate := a.Rate(); rate != 0.26959737847033216 { t.Errorf("12 minute a.Rate(): 0.26959737847033216 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.2522102307052083 != rate { + if rate := a.Rate(); rate != 0.2522102307052083 { t.Errorf("13 minute a.Rate(): 0.2522102307052083 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.23594443252115815 != rate { + if rate := a.Rate(); rate != 0.23594443252115815 { t.Errorf("14 minute a.Rate(): 0.23594443252115815 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); 0.2207276647028646247028654470286553 != rate { + if rate := a.Rate(); rate != 0.2207276647028646247028654470286553 { t.Errorf("15 minute a.Rate(): 0.2207276647028646247028654470286553 != %v\n", rate) } } diff --git a/metrics/gauge_float64_test.go b/metrics/gauge_float64_test.go index 99e62a40302f..3ee568e7ba09 100644 --- a/metrics/gauge_float64_test.go +++ b/metrics/gauge_float64_test.go @@ -53,7 +53,7 @@ func TestFunctionalGaugeFloat64(t *testing.T) { func TestGetOrRegisterFunctionalGaugeFloat64(t *testing.T) { r := NewRegistry() NewRegisteredFunctionalGaugeFloat64("foo", r, func() float64 { return 47 }) - if g := GetOrRegisterGaugeFloat64("foo", r); 47 != g.Value() { + if g := GetOrRegisterGaugeFloat64("foo", r); g.Value() != 47 { t.Fatal(g) } } diff --git a/metrics/gauge_test.go b/metrics/gauge_test.go index 1f2603d33985..3aee143455c3 100644 --- a/metrics/gauge_test.go +++ b/metrics/gauge_test.go @@ -16,7 +16,7 @@ func BenchmarkGuage(b *testing.B) { func TestGauge(t *testing.T) { g := NewGauge() g.Update(int64(47)) - if v := g.Value(); 47 != v { + if v := g.Value(); v != 47 { t.Errorf("g.Value(): 47 != %v\n", v) } } @@ -26,7 +26,7 @@ func TestGaugeSnapshot(t *testing.T) { g.Update(int64(47)) snapshot := g.Snapshot() g.Update(int64(0)) - if v := snapshot.Value(); 47 != v { + if v := snapshot.Value(); v != 47 { t.Errorf("g.Value(): 47 != %v\n", v) } } @@ -34,7 +34,7 @@ func TestGaugeSnapshot(t *testing.T) { func TestGetOrRegisterGauge(t *testing.T) { r := NewRegistry() NewRegisteredGauge("foo", r).Update(47) - if g := GetOrRegisterGauge("foo", r); 47 != g.Value() { + if g := GetOrRegisterGauge("foo", r); g.Value() != 47 { t.Fatal(g) } } @@ -55,7 +55,7 @@ func TestFunctionalGauge(t *testing.T) { func TestGetOrRegisterFunctionalGauge(t *testing.T) { r := NewRegistry() NewRegisteredFunctionalGauge("foo", r, func() int64 { return 47 }) - if g := GetOrRegisterGauge("foo", r); 47 != g.Value() { + if g := GetOrRegisterGauge("foo", r); g.Value() != 47 { t.Fatal(g) } } diff --git a/metrics/histogram_test.go b/metrics/histogram_test.go index d7f4f0171cf0..7c9f42fcec96 100644 --- a/metrics/histogram_test.go +++ b/metrics/histogram_test.go @@ -14,7 +14,7 @@ func TestGetOrRegisterHistogram(t *testing.T) { r := NewRegistry() s := NewUniformSample(100) NewRegisteredHistogram("foo", r, s).Update(47) - if h := GetOrRegisterHistogram("foo", r, s); 1 != h.Count() { + if h := GetOrRegisterHistogram("foo", r, s); h.Count() != 1 { t.Fatal(h) } } @@ -29,29 +29,29 @@ func TestHistogram10000(t *testing.T) { func TestHistogramEmpty(t *testing.T) { h := NewHistogram(NewUniformSample(100)) - if count := h.Count(); 0 != count { + if count := h.Count(); count != 0 { t.Errorf("h.Count(): 0 != %v\n", count) } - if min := h.Min(); 0 != min { + if min := h.Min(); min != 0 { t.Errorf("h.Min(): 0 != %v\n", min) } - if max := h.Max(); 0 != max { + if max := h.Max(); max != 0 { t.Errorf("h.Max(): 0 != %v\n", max) } - if mean := h.Mean(); 0.0 != mean { + if mean := h.Mean(); mean != 0.0 { t.Errorf("h.Mean(): 0.0 != %v\n", mean) } - if stdDev := h.StdDev(); 0.0 != stdDev { + if stdDev := h.StdDev(); stdDev != 0.0 { t.Errorf("h.StdDev(): 0.0 != %v\n", stdDev) } ps := h.Percentiles([]float64{0.5, 0.75, 0.99}) - if 0.0 != ps[0] { + if ps[0] != 0.0 { t.Errorf("median: 0.0 != %v\n", ps[0]) } - if 0.0 != ps[1] { + if ps[1] != 0.0 { t.Errorf("75th percentile: 0.0 != %v\n", ps[1]) } - if 0.0 != ps[2] { + if ps[2] != 0.0 { t.Errorf("99th percentile: 0.0 != %v\n", ps[2]) } } @@ -67,29 +67,29 @@ func TestHistogramSnapshot(t *testing.T) { } func testHistogram10000(t *testing.T, h Histogram) { - if count := h.Count(); 10000 != count { + if count := h.Count(); count != 10000 { t.Errorf("h.Count(): 10000 != %v\n", count) } - if min := h.Min(); 1 != min { + if min := h.Min(); min != 1 { t.Errorf("h.Min(): 1 != %v\n", min) } - if max := h.Max(); 10000 != max { + if max := h.Max(); max != 10000 { t.Errorf("h.Max(): 10000 != %v\n", max) } - if mean := h.Mean(); 5000.5 != mean { + if mean := h.Mean(); mean != 5000.5 { t.Errorf("h.Mean(): 5000.5 != %v\n", mean) } - if stdDev := h.StdDev(); 2886.751331514372 != stdDev { + if stdDev := h.StdDev(); stdDev != 2886.751331514372 { t.Errorf("h.StdDev(): 2886.751331514372 != %v\n", stdDev) } ps := h.Percentiles([]float64{0.5, 0.75, 0.99}) - if 5000.5 != ps[0] { + if ps[0] != 5000.5 { t.Errorf("median: 5000.5 != %v\n", ps[0]) } - if 7500.75 != ps[1] { + if ps[1] != 7500.75 { t.Errorf("75th percentile: 7500.75 != %v\n", ps[1]) } - if 9900.99 != ps[2] { + if ps[2] != 9900.99 { t.Errorf("99th percentile: 9900.99 != %v\n", ps[2]) } } diff --git a/metrics/json_test.go b/metrics/json_test.go index cf70051f7a2c..f91fe8cfa54f 100644 --- a/metrics/json_test.go +++ b/metrics/json_test.go @@ -12,7 +12,7 @@ func TestRegistryMarshallJSON(t *testing.T) { r := NewRegistry() r.Register("counter", NewCounter()) enc.Encode(r) - if s := b.String(); "{\"counter\":{\"count\":0}}\n" != s { + if s := b.String(); s != "{\"counter\":{\"count\":0}}\n" { t.Fatalf(s) } } diff --git a/metrics/meter_test.go b/metrics/meter_test.go index e88922260145..0dfce76b7af0 100644 --- a/metrics/meter_test.go +++ b/metrics/meter_test.go @@ -16,7 +16,7 @@ func BenchmarkMeter(b *testing.B) { func TestGetOrRegisterMeter(t *testing.T) { r := NewRegistry() NewRegisteredMeter("foo", r).Mark(47) - if m := GetOrRegisterMeter("foo", r); 47 != m.Count() { + if m := GetOrRegisterMeter("foo", r); m.Count() != 47 { t.Fatal(m) } } @@ -40,7 +40,7 @@ func TestMeterDecay(t *testing.T) { func TestMeterNonzero(t *testing.T) { m := NewMeter() m.Mark(3) - if count := m.Count(); 3 != count { + if count := m.Count(); count != 3 { t.Errorf("m.Count(): 3 != %v\n", count) } } @@ -67,7 +67,7 @@ func TestMeterSnapshot(t *testing.T) { func TestMeterZero(t *testing.T) { m := NewMeter() - if count := m.Count(); 0 != count { + if count := m.Count(); count != 0 { t.Errorf("m.Count(): 0 != %v\n", count) } } diff --git a/metrics/registry_test.go b/metrics/registry_test.go index a63e485fe986..3b4c8def7cbb 100644 --- a/metrics/registry_test.go +++ b/metrics/registry_test.go @@ -19,20 +19,20 @@ func TestRegistry(t *testing.T) { i := 0 r.Each(func(name string, iface interface{}) { i++ - if "foo" != name { + if name != "foo" { t.Fatal(name) } if _, ok := iface.(Counter); !ok { t.Fatal(iface) } }) - if 1 != i { + if i != 1 { t.Fatal(i) } r.Unregister("foo") i = 0 r.Each(func(string, interface{}) { i++ }) - if 0 != i { + if i != 0 { t.Fatal(i) } } @@ -52,7 +52,7 @@ func TestRegistryDuplicate(t *testing.T) { t.Fatal(iface) } }) - if 1 != i { + if i != 1 { t.Fatal(i) } } @@ -60,11 +60,11 @@ func TestRegistryDuplicate(t *testing.T) { func TestRegistryGet(t *testing.T) { r := NewRegistry() r.Register("foo", NewCounter()) - if count := r.Get("foo").(Counter).Count(); 0 != count { + if count := r.Get("foo").(Counter).Count(); count != 0 { t.Fatal(count) } r.Get("foo").(Counter).Inc(1) - if count := r.Get("foo").(Counter).Count(); 1 != count { + if count := r.Get("foo").(Counter).Count(); count != 1 { t.Fatal(count) } } @@ -298,7 +298,7 @@ func TestWalkRegistries(t *testing.T) { Register("bars", c) _, prefix := findPrefix(r2, "") - if "prefix.prefix2." != prefix { + if prefix != "prefix.prefix2." { t.Fatal(prefix) } diff --git a/metrics/runtime_test.go b/metrics/runtime_test.go index ebbfd501a25d..f85f7868f71a 100644 --- a/metrics/runtime_test.go +++ b/metrics/runtime_test.go @@ -22,27 +22,27 @@ func TestRuntimeMemStats(t *testing.T) { zero := runtimeMetrics.MemStats.PauseNs.Count() // Get a "zero" since GC may have run before these tests. runtime.GC() CaptureRuntimeMemStatsOnce(r) - if count := runtimeMetrics.MemStats.PauseNs.Count(); 1 != count-zero { + if count := runtimeMetrics.MemStats.PauseNs.Count(); count-zero != 1 { t.Fatal(count - zero) } runtime.GC() runtime.GC() CaptureRuntimeMemStatsOnce(r) - if count := runtimeMetrics.MemStats.PauseNs.Count(); 3 != count-zero { + if count := runtimeMetrics.MemStats.PauseNs.Count(); count-zero != 3 { t.Fatal(count - zero) } for i := 0; i < 256; i++ { runtime.GC() } CaptureRuntimeMemStatsOnce(r) - if count := runtimeMetrics.MemStats.PauseNs.Count(); 259 != count-zero { + if count := runtimeMetrics.MemStats.PauseNs.Count(); count-zero != 259 { t.Fatal(count - zero) } for i := 0; i < 257; i++ { runtime.GC() } CaptureRuntimeMemStatsOnce(r) - if count := runtimeMetrics.MemStats.PauseNs.Count(); 515 != count-zero { // We lost one because there were too many GCs between captures. + if count := runtimeMetrics.MemStats.PauseNs.Count(); count-zero != 515 { // We lost one because there were too many GCs between captures. t.Fatal(count - zero) } } diff --git a/metrics/sample.go b/metrics/sample.go index 5c4845a4f841..fa2bfb274e39 100644 --- a/metrics/sample.go +++ b/metrics/sample.go @@ -234,7 +234,7 @@ func (NilSample) Variance() float64 { return 0.0 } // SampleMax returns the maximum value of the slice of int64. func SampleMax(values []int64) int64 { - if 0 == len(values) { + if len(values) == 0 { return 0 } var max int64 = math.MinInt64 @@ -248,7 +248,7 @@ func SampleMax(values []int64) int64 { // SampleMean returns the mean value of the slice of int64. func SampleMean(values []int64) float64 { - if 0 == len(values) { + if len(values) == 0 { return 0.0 } return float64(SampleSum(values)) / float64(len(values)) @@ -256,7 +256,7 @@ func SampleMean(values []int64) float64 { // SampleMin returns the minimum value of the slice of int64. func SampleMin(values []int64) int64 { - if 0 == len(values) { + if len(values) == 0 { return 0 } var min int64 = math.MaxInt64 @@ -382,7 +382,7 @@ func SampleSum(values []int64) int64 { // SampleVariance returns the variance of the slice of int64. func SampleVariance(values []int64) float64 { - if 0 == len(values) { + if len(values) == 0 { return 0.0 } m := SampleMean(values) diff --git a/metrics/sample_test.go b/metrics/sample_test.go index d60e99c5bba7..d6e966400b24 100644 --- a/metrics/sample_test.go +++ b/metrics/sample_test.go @@ -84,13 +84,13 @@ func TestExpDecaySample10(t *testing.T) { for i := 0; i < 10; i++ { s.Update(int64(i)) } - if size := s.Count(); 10 != size { + if size := s.Count(); size != 10 { t.Errorf("s.Count(): 10 != %v\n", size) } - if size := s.Size(); 10 != size { + if size := s.Size(); size != 10 { t.Errorf("s.Size(): 10 != %v\n", size) } - if l := len(s.Values()); 10 != l { + if l := len(s.Values()); l != 10 { t.Errorf("len(s.Values()): 10 != %v\n", l) } for _, v := range s.Values() { @@ -106,13 +106,13 @@ func TestExpDecaySample100(t *testing.T) { for i := 0; i < 100; i++ { s.Update(int64(i)) } - if size := s.Count(); 100 != size { + if size := s.Count(); size != 100 { t.Errorf("s.Count(): 100 != %v\n", size) } - if size := s.Size(); 100 != size { + if size := s.Size(); size != 100 { t.Errorf("s.Size(): 100 != %v\n", size) } - if l := len(s.Values()); 100 != l { + if l := len(s.Values()); l != 100 { t.Errorf("len(s.Values()): 100 != %v\n", l) } for _, v := range s.Values() { @@ -128,13 +128,13 @@ func TestExpDecaySample1000(t *testing.T) { for i := 0; i < 1000; i++ { s.Update(int64(i)) } - if size := s.Count(); 1000 != size { + if size := s.Count(); size != 1000 { t.Errorf("s.Count(): 1000 != %v\n", size) } - if size := s.Size(); 100 != size { + if size := s.Size(); size != 100 { t.Errorf("s.Size(): 100 != %v\n", size) } - if l := len(s.Values()); 100 != l { + if l := len(s.Values()); l != 100 { t.Errorf("len(s.Values()): 100 != %v\n", l) } for _, v := range s.Values() { @@ -208,13 +208,13 @@ func TestUniformSample(t *testing.T) { for i := 0; i < 1000; i++ { s.Update(int64(i)) } - if size := s.Count(); 1000 != size { + if size := s.Count(); size != 1000 { t.Errorf("s.Count(): 1000 != %v\n", size) } - if size := s.Size(); 100 != size { + if size := s.Size(); size != 100 { t.Errorf("s.Size(): 100 != %v\n", size) } - if l := len(s.Values()); 100 != l { + if l := len(s.Values()); l != 100 { t.Errorf("len(s.Values()): 100 != %v\n", l) } for _, v := range s.Values() { @@ -276,57 +276,57 @@ func benchmarkSample(b *testing.B, s Sample) { } func testExpDecaySampleStatistics(t *testing.T, s Sample) { - if count := s.Count(); 10000 != count { + if count := s.Count(); count != 10000 { t.Errorf("s.Count(): 10000 != %v\n", count) } - if min := s.Min(); 107 != min { + if min := s.Min(); min != 107 { t.Errorf("s.Min(): 107 != %v\n", min) } - if max := s.Max(); 10000 != max { + if max := s.Max(); max != 10000 { t.Errorf("s.Max(): 10000 != %v\n", max) } - if mean := s.Mean(); 4965.98 != mean { + if mean := s.Mean(); mean != 4965.98 { t.Errorf("s.Mean(): 4965.98 != %v\n", mean) } - if stdDev := s.StdDev(); 2959.825156930727 != stdDev { + if stdDev := s.StdDev(); stdDev != 2959.825156930727 { t.Errorf("s.StdDev(): 2959.825156930727 != %v\n", stdDev) } ps := s.Percentiles([]float64{0.5, 0.75, 0.99}) - if 4615 != ps[0] { + if ps[0] != 4615 { t.Errorf("median: 4615 != %v\n", ps[0]) } - if 7672 != ps[1] { + if ps[1] != 7672 { t.Errorf("75th percentile: 7672 != %v\n", ps[1]) } - if 9998.99 != ps[2] { + if ps[2] != 9998.99 { t.Errorf("99th percentile: 9998.99 != %v\n", ps[2]) } } func testUniformSampleStatistics(t *testing.T, s Sample) { - if count := s.Count(); 10000 != count { + if count := s.Count(); count != 10000 { t.Errorf("s.Count(): 10000 != %v\n", count) } - if min := s.Min(); 37 != min { + if min := s.Min(); min != 37 { t.Errorf("s.Min(): 37 != %v\n", min) } - if max := s.Max(); 9989 != max { + if max := s.Max(); max != 9989 { t.Errorf("s.Max(): 9989 != %v\n", max) } - if mean := s.Mean(); 4748.14 != mean { + if mean := s.Mean(); mean != 4748.14 { t.Errorf("s.Mean(): 4748.14 != %v\n", mean) } - if stdDev := s.StdDev(); 2826.684117548333 != stdDev { + if stdDev := s.StdDev(); stdDev != 2826.684117548333 { t.Errorf("s.StdDev(): 2826.684117548333 != %v\n", stdDev) } ps := s.Percentiles([]float64{0.5, 0.75, 0.99}) - if 4599 != ps[0] { + if ps[0] != 4599 { t.Errorf("median: 4599 != %v\n", ps[0]) } - if 7380.5 != ps[1] { + if ps[1] != 7380.5 { t.Errorf("75th percentile: 7380.5 != %v\n", ps[1]) } - if 9986.429999999998 != ps[2] { + if ps[2] != 9986.429999999998 { t.Errorf("99th percentile: 9986.429999999998 != %v\n", ps[2]) } } diff --git a/metrics/timer_test.go b/metrics/timer_test.go index c1f0ff9388fb..d3750e4f9fa3 100644 --- a/metrics/timer_test.go +++ b/metrics/timer_test.go @@ -18,7 +18,7 @@ func BenchmarkTimer(b *testing.B) { func TestGetOrRegisterTimer(t *testing.T) { r := NewRegistry() NewRegisteredTimer("foo", r).Update(47) - if tm := GetOrRegisterTimer("foo", r); 1 != tm.Count() { + if tm := GetOrRegisterTimer("foo", r); tm.Count() != 1 { t.Fatal(tm) } } @@ -27,7 +27,7 @@ func TestTimerExtremes(t *testing.T) { tm := NewTimer() tm.Update(math.MaxInt64) tm.Update(0) - if stdDev := tm.StdDev(); 4.611686018427388e+18 != stdDev { + if stdDev := tm.StdDev(); stdDev != 4.611686018427388e+18 { t.Errorf("tm.StdDev(): 4.611686018427388e+18 != %v\n", stdDev) } } @@ -54,41 +54,41 @@ func TestTimerFunc(t *testing.T) { func TestTimerZero(t *testing.T) { tm := NewTimer() - if count := tm.Count(); 0 != count { + if count := tm.Count(); count != 0 { t.Errorf("tm.Count(): 0 != %v\n", count) } - if min := tm.Min(); 0 != min { + if min := tm.Min(); min != 0 { t.Errorf("tm.Min(): 0 != %v\n", min) } - if max := tm.Max(); 0 != max { + if max := tm.Max(); max != 0 { t.Errorf("tm.Max(): 0 != %v\n", max) } - if mean := tm.Mean(); 0.0 != mean { + if mean := tm.Mean(); mean != 0.0 { t.Errorf("tm.Mean(): 0.0 != %v\n", mean) } - if stdDev := tm.StdDev(); 0.0 != stdDev { + if stdDev := tm.StdDev(); stdDev != 0.0 { t.Errorf("tm.StdDev(): 0.0 != %v\n", stdDev) } ps := tm.Percentiles([]float64{0.5, 0.75, 0.99}) - if 0.0 != ps[0] { + if ps[0] != 0.0 { t.Errorf("median: 0.0 != %v\n", ps[0]) } - if 0.0 != ps[1] { + if ps[1] != 0.0 { t.Errorf("75th percentile: 0.0 != %v\n", ps[1]) } - if 0.0 != ps[2] { + if ps[2] != 0.0 { t.Errorf("99th percentile: 0.0 != %v\n", ps[2]) } - if rate1 := tm.Rate1(); 0.0 != rate1 { + if rate1 := tm.Rate1(); rate1 != 0.0 { t.Errorf("tm.Rate1(): 0.0 != %v\n", rate1) } - if rate5 := tm.Rate5(); 0.0 != rate5 { + if rate5 := tm.Rate5(); rate5 != 0.0 { t.Errorf("tm.Rate5(): 0.0 != %v\n", rate5) } - if rate15 := tm.Rate15(); 0.0 != rate15 { + if rate15 := tm.Rate15(); rate15 != 0.0 { t.Errorf("tm.Rate15(): 0.0 != %v\n", rate15) } - if rateMean := tm.RateMean(); 0.0 != rateMean { + if rateMean := tm.RateMean(); rateMean != 0.0 { t.Errorf("tm.RateMean(): 0.0 != %v\n", rateMean) } } From c6e4e880ea574cae1a2ac0d0500b29e885e4912d Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 24 Oct 2024 17:32:56 +0800 Subject: [PATCH 036/479] all: fix staticcheck warning SA4006: never used value --- XDCx/order_processor.go | 8 ++-- XDCxlending/XDCxlending.go | 2 +- XDCxlending/lendingstate/lendingitem.go | 8 ++-- XDCxlending/order_processor.go | 7 ++-- bmt/bmt.go | 2 +- contracts/trc21issuer/simulation/test/main.go | 18 ++++----- core/blockchain.go | 9 ++--- core/blockchain_test.go | 2 +- core/lending_pool_test.go | 2 +- core/order_pool_test.go | 3 +- core/vm/privacy/bulletproof.go | 38 +++---------------- core/vm/privacy/ringct_test.go | 12 +++--- crypto/bn256/cloudflare/optate.go | 3 +- metrics/registry_test.go | 4 +- whisper/mailserver/server_test.go | 2 +- whisper/whisperv6/filter_test.go | 4 +- 16 files changed, 46 insertions(+), 78 deletions(-) diff --git a/XDCx/order_processor.go b/XDCx/order_processor.go index 86d156fec881..109831f20c4d 100644 --- a/XDCx/order_processor.go +++ b/XDCx/order_processor.go @@ -656,9 +656,10 @@ func (XDCx *XDCX) ProcessCancelOrder(header *types.Header, tradingStateDB *tradi } log.Debug("ProcessCancelOrder", "baseToken", originOrder.BaseToken, "quoteToken", originOrder.QuoteToken) feeRate := tradingstate.GetExRelayerFee(originOrder.ExchangeAddress, statedb) - tokenCancelFee, tokenPriceInXDC := common.Big0, common.Big0 + var tokenCancelFee, tokenPriceInXDC *big.Int if !chain.Config().IsTIPXDCXCancellationFee(header.Number) { tokenCancelFee = getCancelFeeV1(baseTokenDecimal, feeRate, &originOrder) + tokenPriceInXDC = common.Big0 } else { tokenCancelFee, tokenPriceInXDC = XDCx.getCancelFee(chain, statedb, tradingStateDB, &originOrder, feeRate) } @@ -721,7 +722,7 @@ func (XDCx *XDCX) ProcessCancelOrder(header *types.Header, tradingStateDB *tradi // cancellation fee = 1/10 trading fee // deprecated after hardfork at TIPXDCXCancellationFee func getCancelFeeV1(baseTokenDecimal *big.Int, feeRate *big.Int, order *tradingstate.OrderItem) *big.Int { - cancelFee := big.NewInt(0) + var cancelFee *big.Int if order.Side == tradingstate.Ask { // SELL 1 BTC => XDC ,, // order.Quantity =1 && fee rate =2 @@ -748,8 +749,7 @@ func (XDCx *XDCX) getCancelFee(chain consensus.ChainContext, statedb *state.Stat if feeRate == nil || feeRate.Sign() == 0 { return common.Big0, common.Big0 } - cancelFee := big.NewInt(0) - tokenPriceInXDC := big.NewInt(0) + var cancelFee, tokenPriceInXDC *big.Int var err error if order.Side == tradingstate.Ask { cancelFee, tokenPriceInXDC, err = XDCx.ConvertXDCToToken(chain, statedb, tradingStateDb, order.BaseToken, common.RelayerCancelFee) diff --git a/XDCxlending/XDCxlending.go b/XDCxlending/XDCxlending.go index 48ff54077844..27c5e68cc56a 100644 --- a/XDCxlending/XDCxlending.go +++ b/XDCxlending/XDCxlending.go @@ -311,7 +311,7 @@ func (l *Lending) SyncDataToSDKNode(chain consensus.ChainContext, statedb *state } // maker dirty order makerFilledAmount := big.NewInt(0) - makerOrderHash := common.Hash{} + var makerOrderHash common.Hash if updatedTakerLendingItem.Side == lendingstate.Borrowing { makerOrderHash = tradeRecord.InvestingOrderHash } else { diff --git a/XDCxlending/lendingstate/lendingitem.go b/XDCxlending/lendingstate/lendingitem.go index dd4553ab8793..200ffb920b20 100644 --- a/XDCxlending/lendingstate/lendingitem.go +++ b/XDCxlending/lendingstate/lendingitem.go @@ -411,7 +411,7 @@ func VerifyBalance(isXDCXLendingFork bool, statedb *state.StateDB, lendingStateD if lendTokenXDCPrice != nil && lendTokenXDCPrice.Sign() > 0 { defaultFee := new(big.Int).Mul(quantity, new(big.Int).SetUint64(DefaultFeeRate)) defaultFee = new(big.Int).Div(defaultFee, common.XDCXBaseFee) - defaultFeeInXDC := common.Big0 + var defaultFeeInXDC *big.Int if lendingToken != common.XDCNativeAddressBinary { defaultFeeInXDC = new(big.Int).Mul(defaultFee, lendTokenXDCPrice) defaultFeeInXDC = new(big.Int).Div(defaultFeeInXDC, lendingTokenDecimal) @@ -429,8 +429,7 @@ func VerifyBalance(isXDCXLendingFork bool, statedb *state.StateDB, lendingStateD // make sure actualBalance >= cancel fee lendingBook := GetLendingOrderBookHash(lendingToken, term) item := lendingStateDb.GetLendingOrder(lendingBook, common.BigToHash(new(big.Int).SetUint64(lendingId))) - cancelFee := big.NewInt(0) - cancelFee = new(big.Int).Mul(item.Quantity, borrowingFeeRate) + cancelFee := new(big.Int).Mul(item.Quantity, borrowingFeeRate) cancelFee = new(big.Int).Div(cancelFee, common.XDCXBaseCancelFee) actualBalance := GetTokenBalance(userAddress, lendingToken, statedb) @@ -459,9 +458,8 @@ func VerifyBalance(isXDCXLendingFork bool, statedb *state.StateDB, lendingStateD case LendingStatusCancelled: lendingBook := GetLendingOrderBookHash(lendingToken, term) item := lendingStateDb.GetLendingOrder(lendingBook, common.BigToHash(new(big.Int).SetUint64(lendingId))) - cancelFee := big.NewInt(0) // Fee == quantityToLend/base lend token decimal *price*borrowFee/LendingCancelFee - cancelFee = new(big.Int).Div(item.Quantity, collateralPrice) + cancelFee := new(big.Int).Div(item.Quantity, collateralPrice) cancelFee = new(big.Int).Mul(cancelFee, borrowingFeeRate) cancelFee = new(big.Int).Div(cancelFee, common.XDCXBaseCancelFee) actualBalance := GetTokenBalance(userAddress, collateralToken, statedb) diff --git a/XDCxlending/order_processor.go b/XDCxlending/order_processor.go index 5af2d2771314..9c2edc227df4 100644 --- a/XDCxlending/order_processor.go +++ b/XDCxlending/order_processor.go @@ -727,9 +727,10 @@ func (l *Lending) ProcessCancelOrder(header *types.Header, lendingStateDB *lendi } } feeRate := lendingstate.GetFee(statedb, originOrder.Relayer) - tokenCancelFee, tokenPriceInXDC := common.Big0, common.Big0 + var tokenCancelFee, tokenPriceInXDC *big.Int if !chain.Config().IsTIPXDCXCancellationFee(header.Number) { tokenCancelFee = getCancelFeeV1(collateralTokenDecimal, collateralPrice, feeRate, &originOrder) + tokenPriceInXDC = common.Big0 } else { tokenCancelFee, tokenPriceInXDC = l.getCancelFee(chain, statedb, tradingStateDb, &originOrder, feeRate) } @@ -927,7 +928,7 @@ func (l *Lending) LiquidationTrade(lendingStateDB *lendingstate.LendingStateDB, // cancellation fee = 1/10 borrowing fee // deprecated after hardfork at TIPXDCXCancellationFee func getCancelFeeV1(collateralTokenDecimal *big.Int, collateralPrice, borrowFee *big.Int, order *lendingstate.LendingItem) *big.Int { - cancelFee := big.NewInt(0) + var cancelFee *big.Int if order.Side == lendingstate.Investing { // cancel fee = quantityToLend*borrowFee/LendingCancelFee cancelFee = new(big.Int).Mul(order.Quantity, borrowFee) @@ -947,7 +948,7 @@ func (l *Lending) getCancelFee(chain consensus.ChainContext, statedb *state.Stat if feeRate == nil || feeRate.Sign() == 0 { return common.Big0, common.Big0 } - cancelFee, tokenPriceInXDC := common.Big0, common.Big0 + var cancelFee, tokenPriceInXDC *big.Int var err error if order.Side == lendingstate.Investing { cancelFee, tokenPriceInXDC, err = l.XDCx.ConvertXDCToToken(chain, statedb, tradingStateDb, order.LendingToken, common.RelayerLendingCancelFee) diff --git a/bmt/bmt.go b/bmt/bmt.go index 4b65b1d94aac..bba4f860394e 100644 --- a/bmt/bmt.go +++ b/bmt/bmt.go @@ -394,7 +394,7 @@ func (self *Hasher) ReadFrom(r io.Reader) (m int64, err error) { if err != nil { break } - n, err = self.Write(buf[:n]) + _, err = self.Write(buf[:n]) if err != nil { break } diff --git a/contracts/trc21issuer/simulation/test/main.go b/contracts/trc21issuer/simulation/test/main.go index 1ee2aff60ecf..489a7e24ef05 100644 --- a/contracts/trc21issuer/simulation/test/main.go +++ b/contracts/trc21issuer/simulation/test/main.go @@ -43,7 +43,7 @@ func airDropTokenToAccountNoXDC() { fmt.Println("wait 10s to airdrop success ", tx.Hash().Hex()) time.Sleep(10 * time.Second) - _, receiptRpc, err := client.GetTransactionReceiptResult(context.Background(), tx.Hash()) + _, receiptRpc, _ := client.GetTransactionReceiptResult(context.Background(), tx.Hash()) receipt := map[string]interface{}{} err = json.Unmarshal(receiptRpc, &receipt) if err != nil { @@ -80,8 +80,8 @@ func testTransferTRC21TokenWithAccountNoXDC() { trc21IssuerInstance, _ := trc21issuer.NewTRC21Issuer(airDropAccount, common.TRC21IssuerSMC, client) remainFee, _ := trc21IssuerInstance.GetTokenCapacity(trc21TokenAddr) - airDropBalanceBefore, err := trc21Instance.BalanceOf(simulation.AirdropAddr) - receiverBalanceBefore, err := trc21Instance.BalanceOf(simulation.ReceiverAddr) + airDropBalanceBefore, _ := trc21Instance.BalanceOf(simulation.AirdropAddr) + receiverBalanceBefore, _ := trc21Instance.BalanceOf(simulation.ReceiverAddr) // execute transferAmount trc to other address tx, err := trc21Instance.Transfer(simulation.ReceiverAddr, simulation.TransferAmount) if err != nil { @@ -105,7 +105,7 @@ func testTransferTRC21TokenWithAccountNoXDC() { if err != nil || balance.Cmp(remainAirDrop) != 0 { log.Fatal("check balance after fail transferAmount in tr21: ", err, "get", balance, "wanted", remainAirDrop) } - _, receiptRpc, err := client.GetTransactionReceiptResult(context.Background(), tx.Hash()) + _, receiptRpc, _ := client.GetTransactionReceiptResult(context.Background(), tx.Hash()) receipt := map[string]interface{}{} err = json.Unmarshal(receiptRpc, &receipt) if err != nil { @@ -140,15 +140,15 @@ func testTransferTrc21Fail() { airDropAccount.GasPrice = big.NewInt(0).Mul(common.TRC21GasPrice, big.NewInt(2)) trc21Instance, _ := trc21issuer.NewTRC21(airDropAccount, trc21TokenAddr, client) trc21IssuerInstance, _ := trc21issuer.NewTRC21Issuer(airDropAccount, common.TRC21IssuerSMC, client) - balanceIssuerFee, err := trc21IssuerInstance.GetTokenCapacity(trc21TokenAddr) + balanceIssuerFee, _ := trc21IssuerInstance.GetTokenCapacity(trc21TokenAddr) minFee, err := trc21Instance.MinFee() if err != nil { log.Fatal("can't get minFee of trc21 smart contract:", err) } - ownerBalance, err := trc21Instance.BalanceOf(simulation.MainAddr) - remainFee, err := trc21IssuerInstance.GetTokenCapacity(trc21TokenAddr) - airDropBalanceBefore, err := trc21Instance.BalanceOf(simulation.AirdropAddr) + ownerBalance, _ := trc21Instance.BalanceOf(simulation.MainAddr) + remainFee, _ := trc21IssuerInstance.GetTokenCapacity(trc21TokenAddr) + airDropBalanceBefore, _ := trc21Instance.BalanceOf(simulation.AirdropAddr) tx, err := trc21Instance.Transfer(common.Address{}, big.NewInt(1)) if err != nil { @@ -171,7 +171,7 @@ func testTransferTrc21Fail() { if err != nil || balance.Cmp(ownerBalance) != 0 { log.Fatal("can't get balance token fee in smart contract: ", err, "got", balanceIssuerFee, "wanted", remainFee) } - _, receiptRpc, err := client.GetTransactionReceiptResult(context.Background(), tx.Hash()) + _, receiptRpc, _ := client.GetTransactionReceiptResult(context.Background(), tx.Hash()) receipt := map[string]interface{}{} err = json.Unmarshal(receiptRpc, &receipt) if err != nil { diff --git a/core/blockchain.go b/core/blockchain.go index fb98f45dab93..8fbde7da2399 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1271,8 +1271,6 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types. if current := block.NumberU64(); current > triesInMemory { // Find the next state trie we need to commit chosen := current - triesInMemory - oldTradingRoot := common.Hash{} - oldLendingRoot := common.Hash{} // Only write to disk if we exceeded our memory allowance *and* also have at // least a given number of tries gapped. // @@ -1308,8 +1306,8 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types. if tradingTrieDb != nil && lendingTrieDb != nil { b := bc.GetBlock(header.Hash(), current-triesInMemory) author, _ := bc.Engine().Author(b.Header()) - oldTradingRoot, _ = tradingService.GetTradingStateRoot(b, author) - oldLendingRoot, _ = lendingService.GetLendingStateRoot(b, author) + oldTradingRoot, _ := tradingService.GetTradingStateRoot(b, author) + oldLendingRoot, _ := lendingService.GetLendingStateRoot(b, author) tradingTrieDb.Commit(oldTradingRoot, true) lendingTrieDb.Commit(oldLendingRoot, true) } @@ -1647,8 +1645,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, [] } // liquidate / finalize open lendingTrades if block.Number().Uint64()%bc.chainConfig.XDPoS.Epoch == common.LiquidateLendingTradeBlock { - finalizedTrades := map[common.Hash]*lendingstate.LendingTrade{} - finalizedTrades, _, _, _, _, err = lendingService.ProcessLiquidationData(block.Header(), bc, statedb, tradingState, lendingState) + finalizedTrades, _, _, _, _, err := lendingService.ProcessLiquidationData(block.Header(), bc, statedb, tradingState, lendingState) if err != nil { return i, events, coalescedLogs, fmt.Errorf("failed to ProcessLiquidationData. Err: %v ", err) } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 87b58bf3ad0a..51e2122391d7 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -527,7 +527,7 @@ func testInsertNonceError(t *testing.T, full bool) { blockchain.engine = ethash.NewFakeFailer(failNum) blockchain.hc.engine = blockchain.engine - failRes, err = blockchain.InsertHeaderChain(headers, 1) + failRes, _ = blockchain.InsertHeaderChain(headers, 1) // Check that the returned error indicates the failure. if failRes != failAt { t.Errorf("test %d: failure index mismatch: have %d, want %d", i, failRes, failAt) diff --git a/core/lending_pool_test.go b/core/lending_pool_test.go index 5ffe82d58c34..5a0dab8e3542 100644 --- a/core/lending_pool_test.go +++ b/core/lending_pool_test.go @@ -58,7 +58,7 @@ func getLendingNonce(userAddress common.Address) (uint64, error) { s := result.(string) s = strings.TrimPrefix(s, "0x") n, err := strconv.ParseUint(s, 16, 32) - return uint64(n), nil + return uint64(n), err } func (l *LendingMsg) computeHash() common.Hash { diff --git a/core/order_pool_test.go b/core/order_pool_test.go index 1f03c692eefe..6029e928202e 100644 --- a/core/order_pool_test.go +++ b/core/order_pool_test.go @@ -49,7 +49,6 @@ var ( EOSAddress = common.HexToAddress("0xd9bb01454c85247B2ef35BB5BE57384cC275a8cf") USDAddress = common.HexToAddress("0x45c25041b8e6CBD5c963E7943007187C3673C7c9") _1E18 = new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(100)) - _1E17 = new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(10)) _1E8 = big.NewInt(100000000) _1E7 = big.NewInt(10000000) ) @@ -72,7 +71,7 @@ func getNonce(t *testing.T, userAddress common.Address) (uint64, error) { s := result.(string) s = strings.TrimPrefix(s, "0x") n, err := strconv.ParseUint(s, 16, 32) - return uint64(n), nil + return uint64(n), err } func testSendOrder(t *testing.T, amount, price *big.Int, side string, status string, orderID uint64) { diff --git a/core/vm/privacy/bulletproof.go b/core/vm/privacy/bulletproof.go index f620d230b2ca..37d8522afefc 100644 --- a/core/vm/privacy/bulletproof.go +++ b/core/vm/privacy/bulletproof.go @@ -608,8 +608,6 @@ Delta is a helper function that is used in the range proof */ func Delta(y []*big.Int, z *big.Int) *big.Int { - result := big.NewInt(0) - // (z-z^2)<1^n, y^n> z2 := new(big.Int).Mod(new(big.Int).Mul(z, z), EC.N) t1 := new(big.Int).Mod(new(big.Int).Sub(z, z2), EC.N) @@ -620,21 +618,14 @@ func Delta(y []*big.Int, z *big.Int) *big.Int { po2sum := new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(EC.V)), EC.N), big.NewInt(1)) t3 := new(big.Int).Mod(new(big.Int).Mul(z3, po2sum), EC.N) - result = new(big.Int).Mod(new(big.Int).Sub(t2, t3), EC.N) - - return result + return new(big.Int).Mod(new(big.Int).Sub(t2, t3), EC.N) } // Calculates (aL - z*1^n) + sL*x func CalculateL(aL, sL []*big.Int, z, x *big.Int) []*big.Int { - result := make([]*big.Int, len(aL)) - tmp1 := VectorAddScalar(aL, new(big.Int).Neg(z)) tmp2 := ScalarVectorMul(sL, x) - - result = VectorAdd(tmp1, tmp2) - - return result + return VectorAdd(tmp1, tmp2) } func CalculateR(aR, sR, y, po2 []*big.Int, z, x *big.Int) []*big.Int { @@ -642,29 +633,20 @@ func CalculateR(aR, sR, y, po2 []*big.Int, z, x *big.Int) []*big.Int { log.Info("CalculateR: Arrays not of the same length") } - result := make([]*big.Int, len(aR)) - z2 := new(big.Int).Exp(z, big.NewInt(2), EC.N) tmp11 := VectorAddScalar(aR, z) tmp12 := ScalarVectorMul(sR, x) tmp1 := VectorHadamard(y, VectorAdd(tmp11, tmp12)) tmp2 := ScalarVectorMul(po2, z2) - result = VectorAdd(tmp1, tmp2) - - return result + return VectorAdd(tmp1, tmp2) } // Calculates (aL - z*1^n) + sL*x func CalculateLMRP(aL, sL []*big.Int, z, x *big.Int) []*big.Int { - result := make([]*big.Int, len(aL)) - tmp1 := VectorAddScalar(aL, new(big.Int).Neg(z)) tmp2 := ScalarVectorMul(sL, x) - - result = VectorAdd(tmp1, tmp2) - - return result + return VectorAdd(tmp1, tmp2) } func CalculateRMRP(aR, sR, y, zTimesTwo []*big.Int, z, x *big.Int) []*big.Int { @@ -672,15 +654,11 @@ func CalculateRMRP(aR, sR, y, zTimesTwo []*big.Int, z, x *big.Int) []*big.Int { log.Info("CalculateRMRP: Arrays not of the same length") } - result := make([]*big.Int, len(aR)) - tmp11 := VectorAddScalar(aR, z) tmp12 := ScalarVectorMul(sR, x) tmp1 := VectorHadamard(y, VectorAdd(tmp11, tmp12)) - result = VectorAdd(tmp1, zTimesTwo) - - return result + return VectorAdd(tmp1, zTimesTwo) } /* @@ -689,8 +667,6 @@ DeltaMRP is a helper function that is used in the multi range proof */ func DeltaMRP(y []*big.Int, z *big.Int, m int) *big.Int { - result := big.NewInt(0) - // (z-z^2)<1^n, y^n> z2 := new(big.Int).Mod(new(big.Int).Mul(z, z), EC.N) t1 := new(big.Int).Mod(new(big.Int).Sub(z, z2), EC.N) @@ -709,9 +685,7 @@ func DeltaMRP(y []*big.Int, z *big.Int, m int) *big.Int { t3 = new(big.Int).Mod(new(big.Int).Add(t3, tmp1), EC.N) } - result = new(big.Int).Mod(new(big.Int).Sub(t2, t3), EC.N) - - return result + return new(big.Int).Mod(new(big.Int).Sub(t2, t3), EC.N) } type MultiRangeProof struct { diff --git a/core/vm/privacy/ringct_test.go b/core/vm/privacy/ringct_test.go index 3e5b771c0bce..b3370497a0cf 100644 --- a/core/vm/privacy/ringct_test.go +++ b/core/vm/privacy/ringct_test.go @@ -21,7 +21,7 @@ func TestSign(t *testing.T) { ringSize := 10 s := 9 fmt.Println("Generate random ring parameter ") - rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) + rings, privkeys, m, _ := GenerateMultiRingParams(numRing, ringSize, s) fmt.Println("numRing ", numRing) fmt.Println("ringSize ", ringSize) @@ -57,7 +57,7 @@ func TestDeserialize(t *testing.T) { numRing := 5 ringSize := 10 s := 5 - rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) + rings, privkeys, m, _ := GenerateMultiRingParams(numRing, ringSize, s) ringSignature, err := Sign(m, rings, privkeys, s) if err != nil { @@ -235,7 +235,7 @@ func TestOnCurveVerify(t *testing.T) { numRing := 5 ringSize := 10 s := 5 - rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) + rings, privkeys, m, _ := GenerateMultiRingParams(numRing, ringSize, s) ringSignature, err := Sign(m, rings, privkeys, s) if err != nil { t.Error("Failed to create Ring signature") @@ -259,7 +259,7 @@ func TestOnCurveDeserialize(t *testing.T) { numRing := 5 ringSize := 10 s := 5 - rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) + rings, privkeys, m, _ := GenerateMultiRingParams(numRing, ringSize, s) ringSignature, err := Sign(m, rings, privkeys, s) if err != nil { t.Error("Failed to create Ring signature") @@ -304,7 +304,7 @@ func TestNilPointerDereferencePanic(t *testing.T) { numRing := 5 ringSize := 10 s := 7 - rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) + rings, privkeys, m, _ := GenerateMultiRingParams(numRing, ringSize, s) ringSig, err := Sign(m, rings, privkeys, s) if err != nil { @@ -318,7 +318,7 @@ func TestNilPointerDereferencePanic(t *testing.T) { t.Error("Failed to Serialize input Ring signature") } - _ , err = Deserialize(sig) + _, err = Deserialize(sig) // Should failed to verify Ring signature as the signature is invalid assert.EqualError(t, err, "failed to deserialize, invalid ring signature") } diff --git a/crypto/bn256/cloudflare/optate.go b/crypto/bn256/cloudflare/optate.go index b71e50e3a21c..e8caa7a08656 100644 --- a/crypto/bn256/cloudflare/optate.go +++ b/crypto/bn256/cloudflare/optate.go @@ -199,9 +199,8 @@ func miller(q *twistPoint, p *curvePoint) *gfP12 { r = newR r2.Square(&minusQ2.y) - a, b, c, newR = lineFunctionAdd(r, minusQ2, bAffine, r2) + a, b, c, _ = lineFunctionAdd(r, minusQ2, bAffine, r2) mulLine(ret, a, b, c) - r = newR return ret } diff --git a/metrics/registry_test.go b/metrics/registry_test.go index a63e485fe986..49bebf79f93a 100644 --- a/metrics/registry_test.go +++ b/metrics/registry_test.go @@ -270,7 +270,7 @@ func TestChildPrefixedRegistryOfChildRegister(t *testing.T) { if err != nil { t.Fatal(err.Error()) } - err = r2.Register("baz", NewCounter()) + r2.Register("baz", NewCounter()) c := NewCounter() Register("bars", c) @@ -293,7 +293,7 @@ func TestWalkRegistries(t *testing.T) { if err != nil { t.Fatal(err.Error()) } - err = r2.Register("baz", NewCounter()) + r2.Register("baz", NewCounter()) c := NewCounter() Register("bars", c) diff --git a/whisper/mailserver/server_test.go b/whisper/mailserver/server_test.go index ced1c156c2ea..6925d2999af0 100644 --- a/whisper/mailserver/server_test.go +++ b/whisper/mailserver/server_test.go @@ -169,7 +169,7 @@ func singleRequest(t *testing.T, server *WMailServer, env *whisper.Envelope, p * } src[0]++ - ok, lower, upper, bloom = server.validateRequest(src, request) + ok, lower, upper, _ = server.validateRequest(src, request) if !ok { // request should be valid regardless of signature t.Fatalf("request validation false negative, seed: %d (lower: %d, upper: %d).", seed, lower, upper) diff --git a/whisper/whisperv6/filter_test.go b/whisper/whisperv6/filter_test.go index 03449b88a39e..20ef3c050381 100644 --- a/whisper/whisperv6/filter_test.go +++ b/whisper/whisperv6/filter_test.go @@ -309,7 +309,7 @@ func TestMatchEnvelope(t *testing.T) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err := msg.Wrap(params) + _, err = msg.Wrap(params) if err != nil { t.Fatalf("failed Wrap with seed %d: %s.", seed, err) } @@ -322,7 +322,7 @@ func TestMatchEnvelope(t *testing.T) { if err != nil { t.Fatalf("failed to create new message with seed %d: %s.", seed, err) } - env, err = msg.Wrap(params) + env, err := msg.Wrap(params) if err != nil { t.Fatalf("failed Wrap() with seed %d: %s.", seed, err) } From 2844dbc5a9c39b61cdd5d447325dcec53f5e2ca6 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 25 Oct 2024 11:46:23 +0800 Subject: [PATCH 037/479] rpc: fix staticcheck warning SA1029 by add PeerInfo (#24255) --- rpc/client.go | 5 +++-- rpc/http.go | 30 ++++++++++++++++++------------ rpc/http_test.go | 36 ++++++++++++++++++++++++++++++++++++ rpc/json.go | 5 +++++ rpc/server.go | 35 +++++++++++++++++++++++++++++++++++ rpc/server_test.go | 2 +- rpc/testservice_test.go | 4 ++++ rpc/types.go | 2 ++ rpc/websocket.go | 20 +++++++++++++++++--- rpc/websocket_test.go | 35 +++++++++++++++++++++++++++++++++++ 10 files changed, 156 insertions(+), 18 deletions(-) diff --git a/rpc/client.go b/rpc/client.go index bc0375c5219a..a0fb5ebf43c1 100644 --- a/rpc/client.go +++ b/rpc/client.go @@ -112,6 +112,7 @@ type clientConn struct { func (c *Client) newClientConn(conn ServerCodec) *clientConn { ctx := context.WithValue(context.Background(), clientContextKey{}, c) + ctx = context.WithValue(ctx, peerInfoContextKey{}, conn.peerInfo()) handler := newHandler(ctx, conn, c.idgen, c.services) return &clientConn{conn, handler} } @@ -473,7 +474,7 @@ func (c *Client) Subscribe(ctx context.Context, namespace string, channel interf // Check type of channel first. chanVal := reflect.ValueOf(channel) if chanVal.Kind() != reflect.Chan || chanVal.Type().ChanDir()&reflect.SendDir == 0 { - panic("first argument to Subscribe must be a writable channel") + panic(fmt.Sprintf("channel argument of Subscribe has type %T, need writable channel", channel)) } if chanVal.IsNil() { panic("channel given to Subscribe must not be nil") @@ -532,8 +533,8 @@ func (c *Client) send(ctx context.Context, op *requestOp, msg interface{}) error } func (c *Client) write(ctx context.Context, msg interface{}, retry bool) error { - // The previous write failed. Try to establish a new connection. if c.writeConn == nil { + // The previous write failed. Try to establish a new connection. if err := c.reconnect(ctx); err != nil { return err } diff --git a/rpc/http.go b/rpc/http.go index e7b90acbe339..6f267aa9d66e 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -52,11 +52,18 @@ type httpConn struct { headers http.Header } -// httpConn is treated specially by Client. +// httpConn implements ServerCodec, but it is treated specially by Client +// and some methods don't work. The panic() stubs here exist to ensure +// this special treatment is correct. + func (hc *httpConn) writeJSON(context.Context, interface{}) error { panic("writeJSON called on httpConn") } +func (hc *httpConn) peerInfo() PeerInfo { + panic("peerInfo called on httpConn") +} + func (hc *httpConn) remoteAddr() string { return hc.url } @@ -258,20 +265,19 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { http.Error(w, err.Error(), code) return } + + // Create request-scoped context. + connInfo := PeerInfo{Transport: "http", RemoteAddr: r.RemoteAddr} + connInfo.HTTP.Version = r.Proto + connInfo.HTTP.Host = r.Host + connInfo.HTTP.Origin = r.Header.Get("Origin") + connInfo.HTTP.UserAgent = r.Header.Get("User-Agent") + ctx := r.Context() + ctx = context.WithValue(ctx, peerInfoContextKey{}, connInfo) + // All checks passed, create a codec that reads directly from the request body // until EOF, writes the response to w, and orders the server to process a // single request. - ctx := r.Context() - ctx = context.WithValue(ctx, "remote", r.RemoteAddr) - ctx = context.WithValue(ctx, "scheme", r.Proto) - ctx = context.WithValue(ctx, "local", r.Host) - if ua := r.Header.Get("User-Agent"); ua != "" { - ctx = context.WithValue(ctx, "User-Agent", ua) - } - if origin := r.Header.Get("Origin"); origin != "" { - ctx = context.WithValue(ctx, "Origin", origin) - } - w.Header().Set("content-type", contentType) codec := newHTTPServerConn(r, w) defer codec.close() diff --git a/rpc/http_test.go b/rpc/http_test.go index b75af67c522e..73920b44f09a 100644 --- a/rpc/http_test.go +++ b/rpc/http_test.go @@ -123,3 +123,39 @@ func TestHTTPRespBodyUnlimited(t *testing.T) { t.Fatalf("response has wrong length %d, want %d", len(r), respLength) } } + +func TestHTTPPeerInfo(t *testing.T) { + s := newTestServer() + defer s.Stop() + ts := httptest.NewServer(s) + defer ts.Close() + + c, err := Dial(ts.URL) + if err != nil { + t.Fatal(err) + } + c.SetHeader("user-agent", "ua-testing") + c.SetHeader("origin", "origin.example.com") + + // Request peer information. + var info PeerInfo + if err := c.Call(&info, "test_peerInfo"); err != nil { + t.Fatal(err) + } + + if info.RemoteAddr == "" { + t.Error("RemoteAddr not set") + } + if info.Transport != "http" { + t.Errorf("wrong Transport %q", info.Transport) + } + if info.HTTP.Version != "HTTP/1.1" { + t.Errorf("wrong HTTP.Version %q", info.HTTP.Version) + } + if info.HTTP.UserAgent != "ua-testing" { + t.Errorf("wrong HTTP.UserAgent %q", info.HTTP.UserAgent) + } + if info.HTTP.Origin != "origin.example.com" { + t.Errorf("wrong HTTP.Origin %q", info.HTTP.UserAgent) + } +} diff --git a/rpc/json.go b/rpc/json.go index 1daee3db82af..6024f1e7dc9b 100644 --- a/rpc/json.go +++ b/rpc/json.go @@ -198,6 +198,11 @@ func NewCodec(conn Conn) ServerCodec { return NewFuncCodec(conn, enc.Encode, dec.Decode) } +func (c *jsonCodec) peerInfo() PeerInfo { + // This returns "ipc" because all other built-in transports have a separate codec type. + return PeerInfo{Transport: "ipc", RemoteAddr: c.remote} +} + func (c *jsonCodec) remoteAddr() string { return c.remote } diff --git a/rpc/server.go b/rpc/server.go index 43573a2a415d..a1686bd00882 100644 --- a/rpc/server.go +++ b/rpc/server.go @@ -145,3 +145,38 @@ func (s *RPCService) Modules() map[string]string { } return modules } + +// PeerInfo contains information about the remote end of the network connection. +// +// This is available within RPC method handlers through the context. Call +// PeerInfoFromContext to get information about the client connection related to +// the current method call. +type PeerInfo struct { + // Transport is name of the protocol used by the client. + // This can be "http", "ws" or "ipc". + Transport string + + // Address of client. This will usually contain the IP address and port. + RemoteAddr string + + // Addditional information for HTTP and WebSocket connections. + HTTP struct { + // Protocol version, i.e. "HTTP/1.1". This is not set for WebSocket. + Version string + // Header values sent by the client. + UserAgent string + Origin string + Host string + } +} + +type peerInfoContextKey struct{} + +// PeerInfoFromContext returns information about the client's network connection. +// Use this with the context passed to RPC method handler functions. +// +// The zero value is returned if no connection info is present in ctx. +func PeerInfoFromContext(ctx context.Context) PeerInfo { + info, _ := ctx.Value(peerInfoContextKey{}).(PeerInfo) + return info +} diff --git a/rpc/server_test.go b/rpc/server_test.go index 1af285bc84da..f641603a9698 100644 --- a/rpc/server_test.go +++ b/rpc/server_test.go @@ -45,7 +45,7 @@ func TestServerRegisterName(t *testing.T) { t.Fatalf("Expected service calc to be registered") } - wantCallbacks := 10 + wantCallbacks := 11 if len(svc.callbacks) != wantCallbacks { t.Errorf("Expected %d callbacks for service 'service', got %d", wantCallbacks, len(svc.callbacks)) } diff --git a/rpc/testservice_test.go b/rpc/testservice_test.go index 22bd64d2f639..584473bef09f 100644 --- a/rpc/testservice_test.go +++ b/rpc/testservice_test.go @@ -80,6 +80,10 @@ func (s *testService) EchoWithCtx(ctx context.Context, str string, i int, args * return echoResult{str, i, args} } +func (s *testService) PeerInfo(ctx context.Context) PeerInfo { + return PeerInfoFromContext(ctx) +} + func (s *testService) Sleep(ctx context.Context, duration time.Duration) { time.Sleep(duration) } diff --git a/rpc/types.go b/rpc/types.go index f3b50a259be6..366d6d1e9865 100644 --- a/rpc/types.go +++ b/rpc/types.go @@ -51,8 +51,10 @@ type DataError interface { // a RPC session. Implementations must be go-routine safe since the codec can be called in // multiple go-routines concurrently. type ServerCodec interface { + peerInfo() PeerInfo readBatch() (msgs []*jsonrpcMessage, isBatch bool, err error) close() + jsonWriter } diff --git a/rpc/websocket.go b/rpc/websocket.go index c1c5b1675b61..829271da4f9a 100644 --- a/rpc/websocket.go +++ b/rpc/websocket.go @@ -59,7 +59,7 @@ func (s *Server) WebsocketHandler(allowedOrigins []string) http.Handler { log.Debug("WebSocket upgrade failed", "err", err) return } - codec := newWebsocketCodec(conn) + codec := newWebsocketCodec(conn, r.Host, r.Header) s.ServeCodec(codec, 0) }) } @@ -203,7 +203,7 @@ func DialWebsocketWithDialer(ctx context.Context, endpoint, origin string, diale } return nil, hErr } - return newWebsocketCodec(conn), nil + return newWebsocketCodec(conn, endpoint, header), nil }) } @@ -241,18 +241,28 @@ func wsClientHeaders(endpoint, origin string) (string, http.Header, error) { type websocketCodec struct { *jsonCodec conn *websocket.Conn + info PeerInfo wg sync.WaitGroup pingReset chan struct{} } -func newWebsocketCodec(conn *websocket.Conn) ServerCodec { +func newWebsocketCodec(conn *websocket.Conn, host string, req http.Header) ServerCodec { conn.SetReadLimit(wsMessageSizeLimit) wc := &websocketCodec{ jsonCodec: NewFuncCodec(conn, conn.WriteJSON, conn.ReadJSON).(*jsonCodec), conn: conn, pingReset: make(chan struct{}, 1), + info: PeerInfo{ + Transport: "ws", + RemoteAddr: conn.RemoteAddr().String(), + }, } + // Fill in connection details. + wc.info.HTTP.Host = host + wc.info.HTTP.Origin = req.Get("Origin") + wc.info.HTTP.UserAgent = req.Get("User-Agent") + // Start pinger. wc.wg.Add(1) go wc.pingLoop() return wc @@ -263,6 +273,10 @@ func (wc *websocketCodec) close() { wc.wg.Wait() } +func (wc *websocketCodec) peerInfo() PeerInfo { + return wc.info +} + func (wc *websocketCodec) writeJSON(ctx context.Context, v interface{}) error { err := wc.jsonCodec.writeJSON(ctx, v) if err == nil { diff --git a/rpc/websocket_test.go b/rpc/websocket_test.go index 37ed19476f1a..50b52ba2277b 100644 --- a/rpc/websocket_test.go +++ b/rpc/websocket_test.go @@ -113,6 +113,41 @@ func TestWebsocketLargeCall(t *testing.T) { } } +func TestWebsocketPeerInfo(t *testing.T) { + var ( + s = newTestServer() + ts = httptest.NewServer(s.WebsocketHandler([]string{"origin.example.com"})) + tsurl = "ws:" + strings.TrimPrefix(ts.URL, "http:") + ) + defer s.Stop() + defer ts.Close() + + ctx := context.Background() + c, err := DialWebsocket(ctx, tsurl, "origin.example.com") + if err != nil { + t.Fatal(err) + } + + // Request peer information. + var connInfo PeerInfo + if err := c.Call(&connInfo, "test_peerInfo"); err != nil { + t.Fatal(err) + } + + if connInfo.RemoteAddr == "" { + t.Error("RemoteAddr not set") + } + if connInfo.Transport != "ws" { + t.Errorf("wrong Transport %q", connInfo.Transport) + } + if connInfo.HTTP.UserAgent != "Go-http-client/1.1" { + t.Errorf("wrong HTTP.UserAgent %q", connInfo.HTTP.UserAgent) + } + if connInfo.HTTP.Origin != "origin.example.com" { + t.Errorf("wrong HTTP.Origin %q", connInfo.HTTP.UserAgent) + } +} + // This test checks that client handles WebSocket ping frames correctly. func TestClientWebsocketPing(t *testing.T) { t.Parallel() From 48b1688e846607dbd9bf0c08331bc9a32d0c447d Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 25 Oct 2024 12:23:54 +0800 Subject: [PATCH 038/479] p2p/simulations: fix staticcheck warning SA1029 --- p2p/simulations/http.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/p2p/simulations/http.go b/p2p/simulations/http.go index 7e3aa5c96e1a..84f3af61cd0c 100644 --- a/p2p/simulations/http.go +++ b/p2p/simulations/http.go @@ -721,6 +721,7 @@ func (s *Server) wrapHandler(handler http.HandlerFunc) httprouter.Handle { http.NotFound(w, req) return } + //lint:ignore SA1029 This file will be removed later, reference: #30250 ctx = context.WithValue(ctx, "node", node) } @@ -735,6 +736,7 @@ func (s *Server) wrapHandler(handler http.HandlerFunc) httprouter.Handle { http.NotFound(w, req) return } + //lint:ignore SA1029 This file will be removed later, reference: #30250 ctx = context.WithValue(ctx, "peer", peer) } From dd6822bc3cec972322f9bff9d791d070015000a2 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 25 Oct 2024 18:29:08 +0800 Subject: [PATCH 039/479] internal/ethapi, contracts/trc21issuer: fix staticcheck warning SA1012: pass nil Context --- contracts/trc21issuer/trc21issuer_test.go | 11 ++++++----- internal/ethapi/api.go | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/contracts/trc21issuer/trc21issuer_test.go b/contracts/trc21issuer/trc21issuer_test.go index 698aa3f97d4a..a8658b105be3 100644 --- a/contracts/trc21issuer/trc21issuer_test.go +++ b/contracts/trc21issuer/trc21issuer_test.go @@ -1,6 +1,7 @@ package trc21issuer import ( + "context" "math/big" "testing" @@ -60,7 +61,7 @@ func TestFeeTxWithTRC21Token(t *testing.T) { contractBackend.Commit() //check trc21 SMC balance - balance, err := contractBackend.BalanceAt(nil, trc21IssuerAddr, nil) + balance, err := contractBackend.BalanceAt(context.TODO(), trc21IssuerAddr, nil) if err != nil || balance.Cmp(minApply) != 0 { t.Fatal("can't get balance in trc21Issuer SMC: ", err, "got", balance, "wanted", minApply) } @@ -78,7 +79,7 @@ func TestFeeTxWithTRC21Token(t *testing.T) { t.Fatal("can't execute transfer in tr20: ", err) } contractBackend.Commit() - receipt, err := contractBackend.TransactionReceipt(nil, tx.Hash()) + receipt, err := contractBackend.TransactionReceipt(context.TODO(), tx.Hash()) if err != nil { t.Fatal("can't transaction's receipt ", err, "hash", tx.Hash()) } @@ -100,7 +101,7 @@ func TestFeeTxWithTRC21Token(t *testing.T) { t.Fatal("check balance token fee in smart contract: got", balanceIssuerFee, "wanted", remainFee) } //check trc21 SMC balance - balance, err = contractBackend.BalanceAt(nil, trc21IssuerAddr, nil) + balance, err = contractBackend.BalanceAt(context.TODO(), trc21IssuerAddr, nil) if err != nil || balance.Cmp(remainFee) != 0 { t.Fatal("can't get balance token fee in smart contract: ", err, "got", balanceIssuerFee, "wanted", remainFee) } @@ -130,7 +131,7 @@ func TestFeeTxWithTRC21Token(t *testing.T) { t.Fatal("check balance after fail transfer in tr20: ", err, "get", balance, "wanted", remainAirDrop) } - receipt, err = contractBackend.TransactionReceipt(nil, tx.Hash()) + receipt, err = contractBackend.TransactionReceipt(context.TODO(), tx.Hash()) if err != nil { t.Fatal("can't transaction's receipt ", err, "hash", tx.Hash()) } @@ -142,7 +143,7 @@ func TestFeeTxWithTRC21Token(t *testing.T) { t.Fatal("can't get balance token fee in smart contract: ", err, "got", balanceIssuerFee, "wanted", remainFee) } //check trc21 SMC balance - balance, err = contractBackend.BalanceAt(nil, trc21IssuerAddr, nil) + balance, err = contractBackend.BalanceAt(context.TODO(), trc21IssuerAddr, nil) if err != nil || balance.Cmp(remainFee) != 0 { t.Fatal("can't get balance token fee in smart contract: ", err, "got", balanceIssuerFee, "wanted", remainFee) } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 552043007c80..505e5921d62e 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -3442,14 +3442,14 @@ func GetSignersFromBlocks(b Backend, blockNumber uint64, blockHash common.Hash, limitNumber = currentNumber } for i := blockNumber + 1; i <= limitNumber; i++ { - header, err := b.HeaderByNumber(nil, rpc.BlockNumber(i)) + header, err := b.HeaderByNumber(context.TODO(), rpc.BlockNumber(i)) if err != nil { return addrs, err } if header == nil { return addrs, errors.New("nil header in GetSignersFromBlocks") } - blockData, err := b.BlockByNumber(nil, rpc.BlockNumber(i)) + blockData, err := b.BlockByNumber(context.TODO(), rpc.BlockNumber(i)) if err != nil { return addrs, err } From fbecb8c5a566c74248fa9fede461ea037c8e623f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 25 Oct 2024 21:30:54 +0800 Subject: [PATCH 040/479] all: fix staticcheck warning ST1006: don't use generic name self MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The name of a method’s receiver should be a reflection of its identity; often a one or two letter abbreviation of its type suffices (such as “c” or “cl” for “Client”). Don’t use generic names such as “me”, “this” or “self”, identifiers typical of object-oriented languages that place more emphasis on methods as opposed to functions. The name need not be as descriptive as that of a method argument, as its role is obvious and serves no documentary purpose. It can be very short as it will appear on almost every line of every method of the type; familiarity admits brevity. Be consistent, too: if you call the receiver “c” in one method, don’t call it “cl” in another. --- XDCx/tradingstate/dump.go | 101 ++-- XDCx/tradingstate/state_lendingbook.go | 126 ++-- XDCx/tradingstate/state_liquidationprice.go | 126 ++-- XDCx/tradingstate/state_orderItem.go | 22 +- XDCx/tradingstate/state_orderList.go | 110 ++-- XDCx/tradingstate/state_orderbook.go | 543 ++++++++--------- XDCx/tradingstate/statedb.go | 364 ++++++------ XDCxlending/lendingstate/dump.go | 89 +-- XDCxlending/lendingstate/state_itemList.go | 127 ++-- XDCxlending/lendingstate/state_lendingbook.go | 545 +++++++++--------- XDCxlending/lendingstate/state_lendingitem.go | 26 +- .../lendingstate/state_lendingtrade.go | 47 +- .../lendingstate/state_liquidationtime.go | 135 ++--- XDCxlending/lendingstate/statedb.go | 370 ++++++------ bmt/bmt.go | 164 +++--- cmd/utils/customflags.go | 78 +-- contracts/ens/ens.go | 32 +- core/state/dump.go | 18 +- core/state/statedb.go | 306 +++++----- core/types/block.go | 14 +- eth/backend.go | 188 +++--- eth/handler.go | 40 +- event/filter/filter.go | 38 +- event/filter/generic_filter.go | 16 +- les/flowcontrol/manager.go | 152 ++--- les/handler.go | 12 +- les/txrelay.go | 80 +-- light/lightchain.go | 264 ++++----- light/txpool.go | 226 ++++---- light/txpool_test.go | 12 +- metrics/librato/client.go | 4 +- metrics/librato/librato.go | 46 +- miner/agent.go | 58 +- miner/miner.go | 88 +-- miner/worker.go | 364 ++++++------ p2p/message.go | 62 +- p2p/peer_error.go | 4 +- p2p/simulations/adapters/inproc.go | 132 ++--- p2p/simulations/adapters/state.go | 8 +- p2p/simulations/network.go | 308 +++++----- p2p/testing/peerpool.go | 32 +- p2p/testing/protocolsession.go | 26 +- p2p/testing/protocoltester.go | 54 +- 43 files changed, 2788 insertions(+), 2769 deletions(-) diff --git a/XDCx/tradingstate/dump.go b/XDCx/tradingstate/dump.go index 33de042c8510..58026ed2a158 100644 --- a/XDCx/tradingstate/dump.go +++ b/XDCx/tradingstate/dump.go @@ -47,13 +47,13 @@ type DumpOrderBookInfo struct { LowestLiquidationPrice *big.Int } -func (self *TradingStateDB) DumpAskTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) { - exhangeObject := self.getStateExchangeObject(orderBook) +func (t *TradingStateDB) DumpAskTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) { + exhangeObject := t.getStateExchangeObject(orderBook) if exhangeObject == nil { return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]DumpOrderList{} - it := trie.NewIterator(exhangeObject.getAsksTrie(self.db).NodeIterator(nil)) + it := trie.NewIterator(exhangeObject.getAsksTrie(t.db).NodeIterator(nil)) for it.Next() { priceHash := common.BytesToHash(it.Key) if common.EmptyHash(priceHash) { @@ -67,13 +67,13 @@ func (self *TradingStateDB) DumpAskTrie(orderBook common.Hash) (map[*big.Int]Dum if err := rlp.DecodeBytes(it.Value, &data); err != nil { return nil, fmt.Errorf("fail when decode order iist orderBook: %v , price :%v", orderBook.Hex(), price) } - stateOrderList := newStateOrderList(self, Ask, orderBook, priceHash, data, nil) - mapResult[price] = stateOrderList.DumpOrderList(self.db) + stateOrderList := newStateOrderList(t, Ask, orderBook, priceHash, data, nil) + mapResult[price] = stateOrderList.DumpOrderList(t.db) } } for priceHash, stateOrderList := range exhangeObject.stateAskObjects { if stateOrderList.Volume().Sign() > 0 { - mapResult[new(big.Int).SetBytes(priceHash.Bytes())] = stateOrderList.DumpOrderList(self.db) + mapResult[new(big.Int).SetBytes(priceHash.Bytes())] = stateOrderList.DumpOrderList(t.db) } } listPrice := []*big.Int{} @@ -90,13 +90,13 @@ func (self *TradingStateDB) DumpAskTrie(orderBook common.Hash) (map[*big.Int]Dum return result, nil } -func (self *TradingStateDB) DumpBidTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) { - exhangeObject := self.getStateExchangeObject(orderBook) +func (t *TradingStateDB) DumpBidTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) { + exhangeObject := t.getStateExchangeObject(orderBook) if exhangeObject == nil { return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]DumpOrderList{} - it := trie.NewIterator(exhangeObject.getBidsTrie(self.db).NodeIterator(nil)) + it := trie.NewIterator(exhangeObject.getBidsTrie(t.db).NodeIterator(nil)) for it.Next() { priceHash := common.BytesToHash(it.Key) if common.EmptyHash(priceHash) { @@ -110,13 +110,13 @@ func (self *TradingStateDB) DumpBidTrie(orderBook common.Hash) (map[*big.Int]Dum if err := rlp.DecodeBytes(it.Value, &data); err != nil { return nil, fmt.Errorf("fail when decode order iist orderBook: %v , price :%v", orderBook.Hex(), price) } - stateOrderList := newStateOrderList(self, Bid, orderBook, priceHash, data, nil) - mapResult[price] = stateOrderList.DumpOrderList(self.db) + stateOrderList := newStateOrderList(t, Bid, orderBook, priceHash, data, nil) + mapResult[price] = stateOrderList.DumpOrderList(t.db) } } for priceHash, stateOrderList := range exhangeObject.stateBidObjects { if stateOrderList.Volume().Sign() > 0 { - mapResult[new(big.Int).SetBytes(priceHash.Bytes())] = stateOrderList.DumpOrderList(self.db) + mapResult[new(big.Int).SetBytes(priceHash.Bytes())] = stateOrderList.DumpOrderList(t.db) } } listPrice := []*big.Int{} @@ -133,13 +133,13 @@ func (self *TradingStateDB) DumpBidTrie(orderBook common.Hash) (map[*big.Int]Dum return mapResult, nil } -func (self *TradingStateDB) GetBids(orderBook common.Hash) (map[*big.Int]*big.Int, error) { - exhangeObject := self.getStateExchangeObject(orderBook) +func (t *TradingStateDB) GetBids(orderBook common.Hash) (map[*big.Int]*big.Int, error) { + exhangeObject := t.getStateExchangeObject(orderBook) if exhangeObject == nil { return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]*big.Int{} - it := trie.NewIterator(exhangeObject.getBidsTrie(self.db).NodeIterator(nil)) + it := trie.NewIterator(exhangeObject.getBidsTrie(t.db).NodeIterator(nil)) for it.Next() { priceHash := common.BytesToHash(it.Key) if common.EmptyHash(priceHash) { @@ -153,7 +153,7 @@ func (self *TradingStateDB) GetBids(orderBook common.Hash) (map[*big.Int]*big.In if err := rlp.DecodeBytes(it.Value, &data); err != nil { return nil, fmt.Errorf("fail when decode order iist orderBook: %v , price :%v", orderBook.Hex(), price) } - stateOrderList := newStateOrderList(self, Bid, orderBook, priceHash, data, nil) + stateOrderList := newStateOrderList(t, Bid, orderBook, priceHash, data, nil) mapResult[price] = stateOrderList.data.Volume } } @@ -176,13 +176,13 @@ func (self *TradingStateDB) GetBids(orderBook common.Hash) (map[*big.Int]*big.In return mapResult, nil } -func (self *TradingStateDB) GetAsks(orderBook common.Hash) (map[*big.Int]*big.Int, error) { - exhangeObject := self.getStateExchangeObject(orderBook) +func (t *TradingStateDB) GetAsks(orderBook common.Hash) (map[*big.Int]*big.Int, error) { + exhangeObject := t.getStateExchangeObject(orderBook) if exhangeObject == nil { return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]*big.Int{} - it := trie.NewIterator(exhangeObject.getAsksTrie(self.db).NodeIterator(nil)) + it := trie.NewIterator(exhangeObject.getAsksTrie(t.db).NodeIterator(nil)) for it.Next() { priceHash := common.BytesToHash(it.Key) if common.EmptyHash(priceHash) { @@ -196,7 +196,7 @@ func (self *TradingStateDB) GetAsks(orderBook common.Hash) (map[*big.Int]*big.In if err := rlp.DecodeBytes(it.Value, &data); err != nil { return nil, fmt.Errorf("fail when decode order iist orderBook: %v , price : %v", orderBook.Hex(), price) } - stateOrderList := newStateOrderList(self, Ask, orderBook, priceHash, data, nil) + stateOrderList := newStateOrderList(t, Ask, orderBook, priceHash, data, nil) mapResult[price] = stateOrderList.data.Volume } } @@ -218,22 +218,23 @@ func (self *TradingStateDB) GetAsks(orderBook common.Hash) (map[*big.Int]*big.In } return result, nil } -func (self *stateOrderList) DumpOrderList(db Database) DumpOrderList { - mapResult := DumpOrderList{Volume: self.Volume(), Orders: map[*big.Int]*big.Int{}} - orderListIt := trie.NewIterator(self.getTrie(db).NodeIterator(nil)) + +func (s *stateOrderList) DumpOrderList(db Database) DumpOrderList { + mapResult := DumpOrderList{Volume: s.Volume(), Orders: map[*big.Int]*big.Int{}} + orderListIt := trie.NewIterator(s.getTrie(db).NodeIterator(nil)) for orderListIt.Next() { keyHash := common.BytesToHash(orderListIt.Key) if common.EmptyHash(keyHash) { continue } - if _, exist := self.cachedStorage[keyHash]; exist { + if _, exist := s.cachedStorage[keyHash]; exist { continue } else { _, content, _, _ := rlp.Split(orderListIt.Value) mapResult.Orders[new(big.Int).SetBytes(keyHash.Bytes())] = new(big.Int).SetBytes(content) } } - for key, value := range self.cachedStorage { + for key, value := range s.cachedStorage { if !common.EmptyHash(value) { mapResult.Orders[new(big.Int).SetBytes(key.Bytes())] = new(big.Int).SetBytes(value.Bytes()) } @@ -245,15 +246,15 @@ func (self *stateOrderList) DumpOrderList(db Database) DumpOrderList { sort.Slice(listIds, func(i, j int) bool { return listIds[i].Cmp(listIds[j]) < 0 }) - result := DumpOrderList{Volume: self.Volume(), Orders: map[*big.Int]*big.Int{}} + result := DumpOrderList{Volume: s.Volume(), Orders: map[*big.Int]*big.Int{}} for _, id := range listIds { result.Orders[id] = mapResult.Orders[id] } return mapResult } -func (self *TradingStateDB) DumpOrderBookInfo(orderBook common.Hash) (*DumpOrderBookInfo, error) { - exhangeObject := self.getStateExchangeObject(orderBook) +func (t *TradingStateDB) DumpOrderBookInfo(orderBook common.Hash) (*DumpOrderBookInfo, error) { + exhangeObject := t.getStateExchangeObject(orderBook) if exhangeObject == nil { return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } @@ -264,29 +265,29 @@ func (self *TradingStateDB) DumpOrderBookInfo(orderBook common.Hash) (*DumpOrder result.MediumPriceBeforeEpoch = exhangeObject.data.MediumPriceBeforeEpoch result.Nonce = exhangeObject.data.Nonce result.TotalQuantity = exhangeObject.data.TotalQuantity - result.BestAsk = new(big.Int).SetBytes(exhangeObject.getBestPriceAsksTrie(self.db).Bytes()) - result.BestBid = new(big.Int).SetBytes(exhangeObject.getBestBidsTrie(self.db).Bytes()) - lowestPrice, _ := exhangeObject.getLowestLiquidationPrice(self.db) + result.BestAsk = new(big.Int).SetBytes(exhangeObject.getBestPriceAsksTrie(t.db).Bytes()) + result.BestBid = new(big.Int).SetBytes(exhangeObject.getBestBidsTrie(t.db).Bytes()) + lowestPrice, _ := exhangeObject.getLowestLiquidationPrice(t.db) result.LowestLiquidationPrice = new(big.Int).SetBytes(lowestPrice.Bytes()) return result, nil } -func (self *stateLendingBook) DumpOrderList(db Database) DumpOrderList { - mapResult := DumpOrderList{Volume: self.Volume(), Orders: map[*big.Int]*big.Int{}} - orderListIt := trie.NewIterator(self.getTrie(db).NodeIterator(nil)) +func (s *stateLendingBook) DumpOrderList(db Database) DumpOrderList { + mapResult := DumpOrderList{Volume: s.Volume(), Orders: map[*big.Int]*big.Int{}} + orderListIt := trie.NewIterator(s.getTrie(db).NodeIterator(nil)) for orderListIt.Next() { keyHash := common.BytesToHash(orderListIt.Key) if common.EmptyHash(keyHash) { continue } - if _, exist := self.cachedStorage[keyHash]; exist { + if _, exist := s.cachedStorage[keyHash]; exist { continue } else { _, content, _, _ := rlp.Split(orderListIt.Value) mapResult.Orders[new(big.Int).SetBytes(keyHash.Bytes())] = new(big.Int).SetBytes(content) } } - for key, value := range self.cachedStorage { + for key, value := range s.cachedStorage { if !common.EmptyHash(value) { mapResult.Orders[new(big.Int).SetBytes(key.Bytes())] = new(big.Int).SetBytes(value.Bytes()) } @@ -298,33 +299,33 @@ func (self *stateLendingBook) DumpOrderList(db Database) DumpOrderList { sort.Slice(listIds, func(i, j int) bool { return listIds[i].Cmp(listIds[j]) < 0 }) - result := DumpOrderList{Volume: self.Volume(), Orders: map[*big.Int]*big.Int{}} + result := DumpOrderList{Volume: s.Volume(), Orders: map[*big.Int]*big.Int{}} for _, id := range listIds { result.Orders[id] = mapResult.Orders[id] } return mapResult } -func (self *liquidationPriceState) DumpLendingBook(db Database) (DumpLendingBook, error) { - result := DumpLendingBook{Volume: self.Volume(), LendingBooks: map[common.Hash]DumpOrderList{}} - it := trie.NewIterator(self.getTrie(db).NodeIterator(nil)) +func (l *liquidationPriceState) DumpLendingBook(db Database) (DumpLendingBook, error) { + result := DumpLendingBook{Volume: l.Volume(), LendingBooks: map[common.Hash]DumpOrderList{}} + it := trie.NewIterator(l.getTrie(db).NodeIterator(nil)) for it.Next() { lendingBook := common.BytesToHash(it.Key) if common.EmptyHash(lendingBook) { continue } - if _, exist := self.stateLendingBooks[lendingBook]; exist { + if _, exist := l.stateLendingBooks[lendingBook]; exist { continue } else { var data orderList if err := rlp.DecodeBytes(it.Value, &data); err != nil { - return result, fmt.Errorf("failed to decode state lending book orderbook: %s , liquidation price : %s , lendingBook : %s , err : %v", self.orderBook, self.liquidationPrice, lendingBook, err) + return result, fmt.Errorf("failed to decode state lending book orderbook: %s, liquidation price: %s , lendingBook: %s , err: %v", l.orderBook, l.liquidationPrice, lendingBook, err) } - stateLendingBook := newStateLendingBook(self.orderBook, self.liquidationPrice, lendingBook, data, nil) + stateLendingBook := newStateLendingBook(l.orderBook, l.liquidationPrice, lendingBook, data, nil) result.LendingBooks[lendingBook] = stateLendingBook.DumpOrderList(db) } } - for lendingBook, stateLendingBook := range self.stateLendingBooks { + for lendingBook, stateLendingBook := range l.stateLendingBooks { if !common.EmptyHash(lendingBook) { result.LendingBooks[lendingBook] = stateLendingBook.DumpOrderList(db) } @@ -332,13 +333,13 @@ func (self *liquidationPriceState) DumpLendingBook(db Database) (DumpLendingBook return result, nil } -func (self *TradingStateDB) DumpLiquidationPriceTrie(orderBook common.Hash) (map[*big.Int]DumpLendingBook, error) { - exhangeObject := self.getStateExchangeObject(orderBook) +func (t *TradingStateDB) DumpLiquidationPriceTrie(orderBook common.Hash) (map[*big.Int]DumpLendingBook, error) { + exhangeObject := t.getStateExchangeObject(orderBook) if exhangeObject == nil { return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]DumpLendingBook{} - it := trie.NewIterator(exhangeObject.getLiquidationPriceTrie(self.db).NodeIterator(nil)) + it := trie.NewIterator(exhangeObject.getLiquidationPriceTrie(t.db).NodeIterator(nil)) for it.Next() { priceHash := common.BytesToHash(it.Key) if common.EmptyHash(priceHash) { @@ -352,8 +353,8 @@ func (self *TradingStateDB) DumpLiquidationPriceTrie(orderBook common.Hash) (map if err := rlp.DecodeBytes(it.Value, &data); err != nil { return nil, fmt.Errorf("fail when decode order iist orderBook: %v , price : %v", orderBook.Hex(), price) } - liquidationPriceState := newLiquidationPriceState(self, orderBook, priceHash, data, nil) - dumpLendingBook, err := liquidationPriceState.DumpLendingBook(self.db) + liquidationPriceState := newLiquidationPriceState(t, orderBook, priceHash, data, nil) + dumpLendingBook, err := liquidationPriceState.DumpLendingBook(t.db) if err != nil { return nil, err } @@ -362,7 +363,7 @@ func (self *TradingStateDB) DumpLiquidationPriceTrie(orderBook common.Hash) (map } for priceHash, liquidationPriceState := range exhangeObject.liquidationPriceStates { if liquidationPriceState.Volume().Sign() > 0 { - dumpLendingBook, err := liquidationPriceState.DumpLendingBook(self.db) + dumpLendingBook, err := liquidationPriceState.DumpLendingBook(t.db) if err != nil { return nil, err } diff --git a/XDCx/tradingstate/state_lendingbook.go b/XDCx/tradingstate/state_lendingbook.go index b6af4aded10c..6f8b77695bec 100644 --- a/XDCx/tradingstate/state_lendingbook.go +++ b/XDCx/tradingstate/state_lendingbook.go @@ -65,59 +65,59 @@ func newStateLendingBook(orderBook common.Hash, price common.Hash, lendingBook c } } -func (self *stateLendingBook) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, self.data) +func (s *stateLendingBook) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, s.data) } -func (self *stateLendingBook) setError(err error) { - if self.dbErr == nil { - self.dbErr = err +func (s *stateLendingBook) setError(err error) { + if s.dbErr == nil { + s.dbErr = err } } -func (self *stateLendingBook) getTrie(db Database) Trie { - if self.trie == nil { +func (s *stateLendingBook) getTrie(db Database) Trie { + if s.trie == nil { var err error - self.trie, err = db.OpenStorageTrie(self.lendingBook, self.data.Root) + s.trie, err = db.OpenStorageTrie(s.lendingBook, s.data.Root) if err != nil { - self.trie, _ = db.OpenStorageTrie(self.price, EmptyHash) - self.setError(fmt.Errorf("can't create storage trie: %v", err)) + s.trie, _ = db.OpenStorageTrie(s.price, EmptyHash) + s.setError(fmt.Errorf("can't create storage trie: %v", err)) } } - return self.trie + return s.trie } -func (self *stateLendingBook) Exist(db Database, lendingId common.Hash) bool { - amount, exists := self.cachedStorage[lendingId] +func (s *stateLendingBook) Exist(db Database, lendingId common.Hash) bool { + amount, exists := s.cachedStorage[lendingId] if exists { return true } // Load from DB in case it is missing. - enc, err := self.getTrie(db).TryGet(lendingId[:]) + enc, err := s.getTrie(db).TryGet(lendingId[:]) if err != nil { - self.setError(err) + s.setError(err) return false } if len(enc) > 0 { _, content, _, err := rlp.Split(enc) if err != nil { - self.setError(err) + s.setError(err) } amount.SetBytes(content) } if (amount != common.Hash{}) { - self.cachedStorage[lendingId] = amount + s.cachedStorage[lendingId] = amount } return true } -func (self *stateLendingBook) getAllTradeIds(db Database) []common.Hash { +func (s *stateLendingBook) getAllTradeIds(db Database) []common.Hash { tradeIds := []common.Hash{} - lendingBookTrie := self.getTrie(db) + lendingBookTrie := s.getTrie(db) if lendingBookTrie == nil { return tradeIds } - for id, value := range self.cachedStorage { + for id, value := range s.cachedStorage { if !common.EmptyHash(value) { tradeIds = append(tradeIds, id) } @@ -125,7 +125,7 @@ func (self *stateLendingBook) getAllTradeIds(db Database) []common.Hash { orderListIt := trie.NewIterator(lendingBookTrie.NodeIterator(nil)) for orderListIt.Next() { id := common.BytesToHash(orderListIt.Key) - if _, exist := self.cachedStorage[id]; exist { + if _, exist := s.cachedStorage[id]; exist { continue } tradeIds = append(tradeIds, id) @@ -133,83 +133,83 @@ func (self *stateLendingBook) getAllTradeIds(db Database) []common.Hash { return tradeIds } -func (self *stateLendingBook) insertTradingId(db Database, tradeId common.Hash) { - self.setTradingId(tradeId, tradeId) - self.setError(self.getTrie(db).TryUpdate(tradeId[:], tradeId[:])) +func (s *stateLendingBook) insertTradingId(db Database, tradeId common.Hash) { + s.setTradingId(tradeId, tradeId) + s.setError(s.getTrie(db).TryUpdate(tradeId[:], tradeId[:])) } -func (self *stateLendingBook) removeTradingId(db Database, tradeId common.Hash) { - tr := self.getTrie(db) - self.setError(tr.TryDelete(tradeId[:])) - self.setTradingId(tradeId, EmptyHash) +func (s *stateLendingBook) removeTradingId(db Database, tradeId common.Hash) { + tr := s.getTrie(db) + s.setError(tr.TryDelete(tradeId[:])) + s.setTradingId(tradeId, EmptyHash) } -func (self *stateLendingBook) setTradingId(tradeId common.Hash, value common.Hash) { - self.cachedStorage[tradeId] = value - self.dirtyStorage[tradeId] = value +func (s *stateLendingBook) setTradingId(tradeId common.Hash, value common.Hash) { + s.cachedStorage[tradeId] = value + s.dirtyStorage[tradeId] = value - if self.onDirty != nil { - self.onDirty(self.lendingBook) - self.onDirty = nil + if s.onDirty != nil { + s.onDirty(s.lendingBook) + s.onDirty = nil } } -func (self *stateLendingBook) updateTrie(db Database) Trie { - tr := self.getTrie(db) - for key, value := range self.dirtyStorage { - delete(self.dirtyStorage, key) +func (s *stateLendingBook) updateTrie(db Database) Trie { + tr := s.getTrie(db) + for key, value := range s.dirtyStorage { + delete(s.dirtyStorage, key) if value == EmptyHash { - self.setError(tr.TryDelete(key[:])) + s.setError(tr.TryDelete(key[:])) continue } v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00")) - self.setError(tr.TryUpdate(key[:], v)) + s.setError(tr.TryUpdate(key[:], v)) } return tr } -func (self *stateLendingBook) updateRoot(db Database) error { - self.updateTrie(db) - if self.dbErr != nil { - return self.dbErr +func (s *stateLendingBook) updateRoot(db Database) error { + s.updateTrie(db) + if s.dbErr != nil { + return s.dbErr } - root, err := self.trie.Commit(nil) + root, err := s.trie.Commit(nil) if err == nil { - self.data.Root = root + s.data.Root = root } return err } -func (self *stateLendingBook) deepCopy(db *TradingStateDB, onDirty func(price common.Hash)) *stateLendingBook { - stateLendingBook := newStateLendingBook(self.lendingBook, self.orderBook, self.price, self.data, onDirty) - if self.trie != nil { - stateLendingBook.trie = db.db.CopyTrie(self.trie) +func (s *stateLendingBook) deepCopy(db *TradingStateDB, onDirty func(price common.Hash)) *stateLendingBook { + stateLendingBook := newStateLendingBook(s.lendingBook, s.orderBook, s.price, s.data, onDirty) + if s.trie != nil { + stateLendingBook.trie = db.db.CopyTrie(s.trie) } - for key, value := range self.dirtyStorage { + for key, value := range s.dirtyStorage { stateLendingBook.dirtyStorage[key] = value } - for key, value := range self.cachedStorage { + for key, value := range s.cachedStorage { stateLendingBook.cachedStorage[key] = value } return stateLendingBook } -func (c *stateLendingBook) AddVolume(amount *big.Int) { - c.setVolume(new(big.Int).Add(c.data.Volume, amount)) +func (s *stateLendingBook) AddVolume(amount *big.Int) { + s.setVolume(new(big.Int).Add(s.data.Volume, amount)) } -func (c *stateLendingBook) subVolume(amount *big.Int) { - c.setVolume(new(big.Int).Sub(c.data.Volume, amount)) +func (s *stateLendingBook) subVolume(amount *big.Int) { + s.setVolume(new(big.Int).Sub(s.data.Volume, amount)) } -func (self *stateLendingBook) setVolume(volume *big.Int) { - self.data.Volume = volume - if self.onDirty != nil { - self.onDirty(self.lendingBook) - self.onDirty = nil +func (s *stateLendingBook) setVolume(volume *big.Int) { + s.data.Volume = volume + if s.onDirty != nil { + s.onDirty(s.lendingBook) + s.onDirty = nil } } -func (self *stateLendingBook) Volume() *big.Int { - return self.data.Volume +func (s *stateLendingBook) Volume() *big.Int { + return s.data.Volume } diff --git a/XDCx/tradingstate/state_liquidationprice.go b/XDCx/tradingstate/state_liquidationprice.go index 5a4e4aca631b..d978512dc636 100644 --- a/XDCx/tradingstate/state_liquidationprice.go +++ b/XDCx/tradingstate/state_liquidationprice.go @@ -68,54 +68,54 @@ func newLiquidationPriceState(db *TradingStateDB, orderBook common.Hash, price c } // EncodeRLP implements rlp.Encoder. -func (c *liquidationPriceState) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, c.data) +func (l *liquidationPriceState) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, l.data) } // setError remembers the first non-nil error it is called with. -func (self *liquidationPriceState) setError(err error) { - if self.dbErr == nil { - self.dbErr = err +func (l *liquidationPriceState) setError(err error) { + if l.dbErr == nil { + l.dbErr = err } } -func (self *liquidationPriceState) MarkStateLendingBookDirty(price common.Hash) { - self.stateLendingBooksDirty[price] = struct{}{} - if self.onDirty != nil { - self.onDirty(self.liquidationPrice) - self.onDirty = nil +func (l *liquidationPriceState) MarkStateLendingBookDirty(price common.Hash) { + l.stateLendingBooksDirty[price] = struct{}{} + if l.onDirty != nil { + l.onDirty(l.liquidationPrice) + l.onDirty = nil } } -func (self *liquidationPriceState) createLendingBook(db Database, lendingBook common.Hash) (newobj *stateLendingBook) { - newobj = newStateLendingBook(self.orderBook, self.liquidationPrice, lendingBook, orderList{Volume: Zero}, self.MarkStateLendingBookDirty) - self.stateLendingBooks[lendingBook] = newobj - self.stateLendingBooksDirty[lendingBook] = struct{}{} - if self.onDirty != nil { - self.onDirty(self.liquidationPrice) - self.onDirty = nil +func (l *liquidationPriceState) createLendingBook(db Database, lendingBook common.Hash) (newobj *stateLendingBook) { + newobj = newStateLendingBook(l.orderBook, l.liquidationPrice, lendingBook, orderList{Volume: Zero}, l.MarkStateLendingBookDirty) + l.stateLendingBooks[lendingBook] = newobj + l.stateLendingBooksDirty[lendingBook] = struct{}{} + if l.onDirty != nil { + l.onDirty(l.liquidationPrice) + l.onDirty = nil } return newobj } -func (self *liquidationPriceState) getTrie(db Database) Trie { - if self.trie == nil { +func (l *liquidationPriceState) getTrie(db Database) Trie { + if l.trie == nil { var err error - self.trie, err = db.OpenStorageTrie(self.liquidationPrice, self.data.Root) + l.trie, err = db.OpenStorageTrie(l.liquidationPrice, l.data.Root) if err != nil { - self.trie, _ = db.OpenStorageTrie(self.liquidationPrice, EmptyHash) - self.setError(fmt.Errorf("can't create storage trie: %v", err)) + l.trie, _ = db.OpenStorageTrie(l.liquidationPrice, EmptyHash) + l.setError(fmt.Errorf("can't create storage trie: %v", err)) } } - return self.trie + return l.trie } -func (self *liquidationPriceState) updateTrie(db Database) Trie { - tr := self.getTrie(db) - for lendingId, stateObject := range self.stateLendingBooks { - delete(self.stateLendingBooksDirty, lendingId) +func (l *liquidationPriceState) updateTrie(db Database) Trie { + tr := l.getTrie(db) + for lendingId, stateObject := range l.stateLendingBooks { + delete(l.stateLendingBooksDirty, lendingId) if stateObject.empty() { - self.setError(tr.TryDelete(lendingId[:])) + l.setError(tr.TryDelete(lendingId[:])) continue } err := stateObject.updateRoot(db) @@ -125,17 +125,17 @@ func (self *liquidationPriceState) updateTrie(db Database) Trie { // Encoding []byte cannot fail, ok to ignore the error. v, _ := rlp.EncodeToBytes(stateObject) - self.setError(tr.TryUpdate(lendingId[:], v)) + l.setError(tr.TryUpdate(lendingId[:], v)) } return tr } -func (self *liquidationPriceState) updateRoot(db Database) error { - self.updateTrie(db) - if self.dbErr != nil { - return self.dbErr +func (l *liquidationPriceState) updateRoot(db Database) error { + l.updateTrie(db) + if l.dbErr != nil { + return l.dbErr } - root, err := self.trie.Commit(func(leaf []byte, parent common.Hash) error { + root, err := l.trie.Commit(func(leaf []byte, parent common.Hash) error { var orderList orderList if err := rlp.DecodeBytes(leaf, &orderList); err != nil { return nil @@ -146,57 +146,57 @@ func (self *liquidationPriceState) updateRoot(db Database) error { return nil }) if err == nil { - self.data.Root = root + l.data.Root = root } return err } -func (self *liquidationPriceState) deepCopy(db *TradingStateDB, onDirty func(liquidationPrice common.Hash)) *liquidationPriceState { - stateOrderList := newLiquidationPriceState(db, self.orderBook, self.liquidationPrice, self.data, onDirty) - if self.trie != nil { - stateOrderList.trie = db.db.CopyTrie(self.trie) +func (l *liquidationPriceState) deepCopy(db *TradingStateDB, onDirty func(liquidationPrice common.Hash)) *liquidationPriceState { + stateOrderList := newLiquidationPriceState(db, l.orderBook, l.liquidationPrice, l.data, onDirty) + if l.trie != nil { + stateOrderList.trie = db.db.CopyTrie(l.trie) } - for key, value := range self.stateLendingBooks { - stateOrderList.stateLendingBooks[key] = value.deepCopy(db, self.MarkStateLendingBookDirty) + for key, value := range l.stateLendingBooks { + stateOrderList.stateLendingBooks[key] = value.deepCopy(db, l.MarkStateLendingBookDirty) } - for key, value := range self.stateLendingBooksDirty { + for key, value := range l.stateLendingBooksDirty { stateOrderList.stateLendingBooksDirty[key] = value } return stateOrderList } // Retrieve a state object given my the address. Returns nil if not found. -func (self *liquidationPriceState) getStateLendingBook(db Database, lendingBook common.Hash) (stateObject *stateLendingBook) { +func (l *liquidationPriceState) getStateLendingBook(db Database, lendingBook common.Hash) (stateObject *stateLendingBook) { // Prefer 'live' objects. - if obj := self.stateLendingBooks[lendingBook]; obj != nil { + if obj := l.stateLendingBooks[lendingBook]; obj != nil { return obj } // Load the object from the database. - enc, err := self.getTrie(db).TryGet(lendingBook[:]) + enc, err := l.getTrie(db).TryGet(lendingBook[:]) if len(enc) == 0 { - self.setError(err) + l.setError(err) return nil } var data orderList if err := rlp.DecodeBytes(enc, &data); err != nil { - log.Error("Failed to decode state lending book ", "orderbook", self.orderBook, "liquidation price", self.liquidationPrice, "lendingBook", lendingBook, "err", err) + log.Error("Failed to decode state lending book ", "orderbook", l.orderBook, "liquidation price", l.liquidationPrice, "lendingBook", lendingBook, "err", err) return nil } // Insert into the live set. - obj := newStateLendingBook(self.orderBook, self.liquidationPrice, lendingBook, data, self.MarkStateLendingBookDirty) - self.stateLendingBooks[lendingBook] = obj + obj := newStateLendingBook(l.orderBook, l.liquidationPrice, lendingBook, data, l.MarkStateLendingBookDirty) + l.stateLendingBooks[lendingBook] = obj return obj } -func (self *liquidationPriceState) getAllLiquidationData(db Database) map[common.Hash][]common.Hash { +func (l *liquidationPriceState) getAllLiquidationData(db Database) map[common.Hash][]common.Hash { liquidationData := map[common.Hash][]common.Hash{} - lendingBookTrie := self.getTrie(db) + lendingBookTrie := l.getTrie(db) if lendingBookTrie == nil { return liquidationData } lendingBooks := []common.Hash{} - for id, stateLendingBook := range self.stateLendingBooks { + for id, stateLendingBook := range l.stateLendingBooks { if !stateLendingBook.empty() { lendingBooks = append(lendingBooks, id) } @@ -204,13 +204,13 @@ func (self *liquidationPriceState) getAllLiquidationData(db Database) map[common lendingBookListIt := trie.NewIterator(lendingBookTrie.NodeIterator(nil)) for lendingBookListIt.Next() { id := common.BytesToHash(lendingBookListIt.Key) - if _, exist := self.stateLendingBooks[id]; exist { + if _, exist := l.stateLendingBooks[id]; exist { continue } lendingBooks = append(lendingBooks, id) } for _, lendingBook := range lendingBooks { - stateLendingBook := self.getStateLendingBook(db, lendingBook) + stateLendingBook := l.getStateLendingBook(db, lendingBook) if stateLendingBook != nil { liquidationData[lendingBook] = stateLendingBook.getAllTradeIds(db) } @@ -218,22 +218,22 @@ func (self *liquidationPriceState) getAllLiquidationData(db Database) map[common return liquidationData } -func (c *liquidationPriceState) AddVolume(amount *big.Int) { - c.setVolume(new(big.Int).Add(c.data.Volume, amount)) +func (l *liquidationPriceState) AddVolume(amount *big.Int) { + l.setVolume(new(big.Int).Add(l.data.Volume, amount)) } func (c *liquidationPriceState) subVolume(amount *big.Int) { c.setVolume(new(big.Int).Sub(c.data.Volume, amount)) } -func (self *liquidationPriceState) setVolume(volume *big.Int) { - self.data.Volume = volume - if self.onDirty != nil { - self.onDirty(self.liquidationPrice) - self.onDirty = nil +func (l *liquidationPriceState) setVolume(volume *big.Int) { + l.data.Volume = volume + if l.onDirty != nil { + l.onDirty(l.liquidationPrice) + l.onDirty = nil } } -func (self *liquidationPriceState) Volume() *big.Int { - return self.data.Volume +func (l *liquidationPriceState) Volume() *big.Int { + return l.data.Volume } diff --git a/XDCx/tradingstate/state_orderItem.go b/XDCx/tradingstate/state_orderItem.go index b1e390b50505..758dba829f48 100644 --- a/XDCx/tradingstate/state_orderItem.go +++ b/XDCx/tradingstate/state_orderItem.go @@ -53,23 +53,23 @@ func newStateOrderItem(orderBook common.Hash, orderId common.Hash, data OrderIte } // EncodeRLP implements rlp.Encoder. -func (c *stateOrderItem) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, c.data) +func (s *stateOrderItem) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, s.data) } -func (self *stateOrderItem) deepCopy(onDirty func(orderId common.Hash)) *stateOrderItem { - stateOrderList := newStateOrderItem(self.orderBook, self.orderId, self.data, onDirty) +func (s *stateOrderItem) deepCopy(onDirty func(orderId common.Hash)) *stateOrderItem { + stateOrderList := newStateOrderItem(s.orderBook, s.orderId, s.data, onDirty) return stateOrderList } -func (self *stateOrderItem) setVolume(volume *big.Int) { - self.data.Quantity = volume - if self.onDirty != nil { - self.onDirty(self.orderId) - self.onDirty = nil +func (s *stateOrderItem) setVolume(volume *big.Int) { + s.data.Quantity = volume + if s.onDirty != nil { + s.onDirty(s.orderId) + s.onDirty = nil } } -func (self *stateOrderItem) Quantity() *big.Int { - return self.data.Quantity +func (s *stateOrderItem) Quantity() *big.Int { + return s.data.Quantity } diff --git a/XDCx/tradingstate/state_orderList.go b/XDCx/tradingstate/state_orderList.go index 44de770e9db1..7946ec538fa3 100644 --- a/XDCx/tradingstate/state_orderList.go +++ b/XDCx/tradingstate/state_orderList.go @@ -75,14 +75,14 @@ func newStateOrderList(db *TradingStateDB, orderType string, orderBook common.Ha } // EncodeRLP implements rlp.Encoder. -func (c *stateOrderList) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, c.data) +func (s *stateOrderList) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, s.data) } // setError remembers the first non-nil error it is called with. -func (self *stateOrderList) setError(err error) { - if self.dbErr == nil { - self.dbErr = err +func (s *stateOrderList) setError(err error) { + if s.dbErr == nil { + s.dbErr = err } } @@ -99,90 +99,90 @@ func (c *stateOrderList) getTrie(db Database) Trie { } // GetState returns a value in orderId storage. -func (self *stateOrderList) GetOrderAmount(db Database, orderId common.Hash) common.Hash { - amount, exists := self.cachedStorage[orderId] +func (s *stateOrderList) GetOrderAmount(db Database, orderId common.Hash) common.Hash { + amount, exists := s.cachedStorage[orderId] if exists { return amount } // Load from DB in case it is missing. - enc, err := self.getTrie(db).TryGet(orderId[:]) + enc, err := s.getTrie(db).TryGet(orderId[:]) if err != nil { - self.setError(err) + s.setError(err) return EmptyHash } if len(enc) > 0 { _, content, _, err := rlp.Split(enc) if err != nil { - self.setError(err) + s.setError(err) } amount.SetBytes(content) } if (amount != common.Hash{}) { - self.cachedStorage[orderId] = amount + s.cachedStorage[orderId] = amount } return amount } // SetState updates a value in orderId storage. -func (self *stateOrderList) insertOrderItem(db Database, orderId common.Hash, amount common.Hash) { - self.setOrderItem(orderId, amount) - self.setError(self.getTrie(db).TryUpdate(orderId[:], amount[:])) +func (s *stateOrderList) insertOrderItem(db Database, orderId common.Hash, amount common.Hash) { + s.setOrderItem(orderId, amount) + s.setError(s.getTrie(db).TryUpdate(orderId[:], amount[:])) } // SetState updates a value in orderId storage. -func (self *stateOrderList) removeOrderItem(db Database, orderId common.Hash) { - tr := self.getTrie(db) - self.setError(tr.TryDelete(orderId[:])) - self.setOrderItem(orderId, EmptyHash) +func (s *stateOrderList) removeOrderItem(db Database, orderId common.Hash) { + tr := s.getTrie(db) + s.setError(tr.TryDelete(orderId[:])) + s.setOrderItem(orderId, EmptyHash) } -func (self *stateOrderList) setOrderItem(orderId common.Hash, amount common.Hash) { - self.cachedStorage[orderId] = amount - self.dirtyStorage[orderId] = amount +func (s *stateOrderList) setOrderItem(orderId common.Hash, amount common.Hash) { + s.cachedStorage[orderId] = amount + s.dirtyStorage[orderId] = amount - if self.onDirty != nil { - self.onDirty(self.Price()) - self.onDirty = nil + if s.onDirty != nil { + s.onDirty(s.Price()) + s.onDirty = nil } } // updateAskTrie writes cached storage modifications into the object's storage trie. -func (self *stateOrderList) updateTrie(db Database) Trie { - tr := self.getTrie(db) - for orderId, amount := range self.dirtyStorage { - delete(self.dirtyStorage, orderId) +func (s *stateOrderList) updateTrie(db Database) Trie { + tr := s.getTrie(db) + for orderId, amount := range s.dirtyStorage { + delete(s.dirtyStorage, orderId) if amount == EmptyHash { - self.setError(tr.TryDelete(orderId[:])) + s.setError(tr.TryDelete(orderId[:])) continue } v, _ := rlp.EncodeToBytes(bytes.TrimLeft(amount[:], "\x00")) - self.setError(tr.TryUpdate(orderId[:], v)) + s.setError(tr.TryUpdate(orderId[:], v)) } return tr } // UpdateRoot sets the trie root to the current root orderId of -func (self *stateOrderList) updateRoot(db Database) error { - self.updateTrie(db) - if self.dbErr != nil { - return self.dbErr +func (s *stateOrderList) updateRoot(db Database) error { + s.updateTrie(db) + if s.dbErr != nil { + return s.dbErr } - root, err := self.trie.Commit(nil) + root, err := s.trie.Commit(nil) if err == nil { - self.data.Root = root + s.data.Root = root } return err } -func (self *stateOrderList) deepCopy(db *TradingStateDB, onDirty func(price common.Hash)) *stateOrderList { - stateOrderList := newStateOrderList(db, self.orderType, self.orderBook, self.price, self.data, onDirty) - if self.trie != nil { - stateOrderList.trie = db.db.CopyTrie(self.trie) +func (s *stateOrderList) deepCopy(db *TradingStateDB, onDirty func(price common.Hash)) *stateOrderList { + stateOrderList := newStateOrderList(db, s.orderType, s.orderBook, s.price, s.data, onDirty) + if s.trie != nil { + stateOrderList.trie = db.db.CopyTrie(s.trie) } - for orderId, amount := range self.dirtyStorage { + for orderId, amount := range s.dirtyStorage { stateOrderList.dirtyStorage[orderId] = amount } - for orderId, amount := range self.cachedStorage { + for orderId, amount := range s.cachedStorage { stateOrderList.cachedStorage[orderId] = amount } return stateOrderList @@ -190,29 +190,29 @@ func (self *stateOrderList) deepCopy(db *TradingStateDB, onDirty func(price comm // AddVolume removes amount from c's balance. // It is used to add funds to the destination exchanges of a transfer. -func (c *stateOrderList) AddVolume(amount *big.Int) { - c.setVolume(new(big.Int).Add(c.data.Volume, amount)) +func (s *stateOrderList) AddVolume(amount *big.Int) { + s.setVolume(new(big.Int).Add(s.data.Volume, amount)) } // AddVolume removes amount from c's balance. // It is used to add funds to the destination exchanges of a transfer. -func (c *stateOrderList) subVolume(amount *big.Int) { - c.setVolume(new(big.Int).Sub(c.data.Volume, amount)) +func (s *stateOrderList) subVolume(amount *big.Int) { + s.setVolume(new(big.Int).Sub(s.data.Volume, amount)) } -func (self *stateOrderList) setVolume(volume *big.Int) { - self.data.Volume = volume - if self.onDirty != nil { - self.onDirty(self.price) - self.onDirty = nil +func (s *stateOrderList) setVolume(volume *big.Int) { + s.data.Volume = volume + if s.onDirty != nil { + s.onDirty(s.price) + s.onDirty = nil } } // Returns the address of the contract/orderId -func (c *stateOrderList) Price() common.Hash { - return c.price +func (s *stateOrderList) Price() common.Hash { + return s.price } -func (self *stateOrderList) Volume() *big.Int { - return self.data.Volume +func (s *stateOrderList) Volume() *big.Int { + return s.data.Volume } diff --git a/XDCx/tradingstate/state_orderbook.go b/XDCx/tradingstate/state_orderbook.go index 3bfdd33e0e33..8d74d748e404 100644 --- a/XDCx/tradingstate/state_orderbook.go +++ b/XDCx/tradingstate/state_orderbook.go @@ -66,35 +66,35 @@ type tradingExchanges struct { } // empty returns whether the orderId is considered empty. -func (s *tradingExchanges) empty() bool { - if s.data.Nonce != 0 { +func (te *tradingExchanges) empty() bool { + if te.data.Nonce != 0 { return false } - if s.data.LendingCount != nil && s.data.LendingCount.Sign() > 0 { + if te.data.LendingCount != nil && te.data.LendingCount.Sign() > 0 { return false } - if s.data.LastPrice != nil && s.data.LastPrice.Sign() > 0 { + if te.data.LastPrice != nil && te.data.LastPrice.Sign() > 0 { return false } - if s.data.MediumPrice != nil && s.data.MediumPrice.Sign() > 0 { + if te.data.MediumPrice != nil && te.data.MediumPrice.Sign() > 0 { return false } - if s.data.MediumPriceBeforeEpoch != nil && s.data.MediumPriceBeforeEpoch.Sign() > 0 { + if te.data.MediumPriceBeforeEpoch != nil && te.data.MediumPriceBeforeEpoch.Sign() > 0 { return false } - if s.data.TotalQuantity != nil && s.data.TotalQuantity.Sign() > 0 { + if te.data.TotalQuantity != nil && te.data.TotalQuantity.Sign() > 0 { return false } - if !common.EmptyHash(s.data.AskRoot) { + if !common.EmptyHash(te.data.AskRoot) { return false } - if !common.EmptyHash(s.data.BidRoot) { + if !common.EmptyHash(te.data.BidRoot) { return false } - if !common.EmptyHash(s.data.OrderRoot) { + if !common.EmptyHash(te.data.OrderRoot) { return false } - if !common.EmptyHash(s.data.LiquidationPriceRoot) { + if !common.EmptyHash(te.data.LiquidationPriceRoot) { return false } return true @@ -119,46 +119,46 @@ func newStateExchanges(db *TradingStateDB, hash common.Hash, data tradingExchang } // EncodeRLP implements rlp.Encoder. -func (c *tradingExchanges) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, c.data) +func (te *tradingExchanges) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, te.data) } // setError remembers the first non-nil error it is called with. -func (self *tradingExchanges) setError(err error) { - if self.dbErr == nil { - self.dbErr = err +func (te *tradingExchanges) setError(err error) { + if te.dbErr == nil { + te.dbErr = err } } -func (c *tradingExchanges) getAsksTrie(db Database) Trie { - if c.asksTrie == nil { +func (te *tradingExchanges) getAsksTrie(db Database) Trie { + if te.asksTrie == nil { var err error - c.asksTrie, err = db.OpenStorageTrie(c.orderBookHash, c.data.AskRoot) + te.asksTrie, err = db.OpenStorageTrie(te.orderBookHash, te.data.AskRoot) if err != nil { - c.asksTrie, _ = db.OpenStorageTrie(c.orderBookHash, EmptyHash) - c.setError(fmt.Errorf("can't create asks trie: %v", err)) + te.asksTrie, _ = db.OpenStorageTrie(te.orderBookHash, EmptyHash) + te.setError(fmt.Errorf("can't create asks trie: %v", err)) } } - return c.asksTrie + return te.asksTrie } -func (c *tradingExchanges) getOrdersTrie(db Database) Trie { - if c.ordersTrie == nil { +func (te *tradingExchanges) getOrdersTrie(db Database) Trie { + if te.ordersTrie == nil { var err error - c.ordersTrie, err = db.OpenStorageTrie(c.orderBookHash, c.data.OrderRoot) + te.ordersTrie, err = db.OpenStorageTrie(te.orderBookHash, te.data.OrderRoot) if err != nil { - c.ordersTrie, _ = db.OpenStorageTrie(c.orderBookHash, EmptyHash) - c.setError(fmt.Errorf("can't create asks trie: %v", err)) + te.ordersTrie, _ = db.OpenStorageTrie(te.orderBookHash, EmptyHash) + te.setError(fmt.Errorf("can't create asks trie: %v", err)) } } - return c.ordersTrie + return te.ordersTrie } -func (c *tradingExchanges) getBestPriceAsksTrie(db Database) common.Hash { - trie := c.getAsksTrie(db) +func (te *tradingExchanges) getBestPriceAsksTrie(db Database) common.Hash { + trie := te.getAsksTrie(db) encKey, encValue, err := trie.TryGetBestLeftKeyAndValue() if err != nil { - log.Error("Failed find best price ask trie ", "orderbook", c.orderBookHash.Hex()) + log.Error("Failed find best price ask trie ", "orderbook", te.orderBookHash.Hex()) return EmptyHash } if len(encKey) == 0 || len(encValue) == 0 { @@ -166,23 +166,23 @@ func (c *tradingExchanges) getBestPriceAsksTrie(db Database) common.Hash { return EmptyHash } price := common.BytesToHash(encKey) - if _, exit := c.stateAskObjects[price]; !exit { + if _, exit := te.stateAskObjects[price]; !exit { var data orderList if err := rlp.DecodeBytes(encValue, &data); err != nil { log.Error("Failed to decode state get best ask trie", "err", err) return EmptyHash } - obj := newStateOrderList(c.db, Bid, c.orderBookHash, price, data, c.MarkStateAskObjectDirty) - c.stateAskObjects[price] = obj + obj := newStateOrderList(te.db, Bid, te.orderBookHash, price, data, te.MarkStateAskObjectDirty) + te.stateAskObjects[price] = obj } return common.BytesToHash(encKey) } -func (c *tradingExchanges) getBestBidsTrie(db Database) common.Hash { - trie := c.getBidsTrie(db) +func (te *tradingExchanges) getBestBidsTrie(db Database) common.Hash { + trie := te.getBidsTrie(db) encKey, encValue, err := trie.TryGetBestRightKeyAndValue() if err != nil { - log.Error("Failed find best price bid trie ", "orderbook", c.orderBookHash.Hex()) + log.Error("Failed find best price bid trie ", "orderbook", te.orderBookHash.Hex()) return EmptyHash } if len(encKey) == 0 || len(encValue) == 0 { @@ -190,27 +190,27 @@ func (c *tradingExchanges) getBestBidsTrie(db Database) common.Hash { return EmptyHash } price := common.BytesToHash(encKey) - if _, exit := c.stateBidObjects[price]; !exit { + if _, exit := te.stateBidObjects[price]; !exit { var data orderList if err := rlp.DecodeBytes(encValue, &data); err != nil { log.Error("Failed to decode state get best bid trie", "err", err) return EmptyHash } // Insert into the live set. - obj := newStateOrderList(c.db, Bid, c.orderBookHash, price, data, c.MarkStateBidObjectDirty) - c.stateBidObjects[price] = obj + obj := newStateOrderList(te.db, Bid, te.orderBookHash, price, data, te.MarkStateBidObjectDirty) + te.stateBidObjects[price] = obj } return common.BytesToHash(encKey) } // updateAskTrie writes cached storage modifications into the object's storage trie. -func (self *tradingExchanges) updateAsksTrie(db Database) Trie { - tr := self.getAsksTrie(db) - for price, orderList := range self.stateAskObjects { - if _, isDirty := self.stateAskObjectsDirty[price]; isDirty { - delete(self.stateAskObjectsDirty, price) +func (te *tradingExchanges) updateAsksTrie(db Database) Trie { + tr := te.getAsksTrie(db) + for price, orderList := range te.stateAskObjects { + if _, isDirty := te.stateAskObjectsDirty[price]; isDirty { + delete(te.stateAskObjectsDirty, price) if orderList.empty() { - self.setError(tr.TryDelete(price[:])) + te.setError(tr.TryDelete(price[:])) continue } err := orderList.updateRoot(db) @@ -219,7 +219,7 @@ func (self *tradingExchanges) updateAsksTrie(db Database) Trie { } // Encoding []byte cannot fail, ok to ignore the error. v, _ := rlp.EncodeToBytes(orderList) - self.setError(tr.TryUpdate(price[:], v)) + te.setError(tr.TryUpdate(price[:], v)) } } @@ -228,23 +228,23 @@ func (self *tradingExchanges) updateAsksTrie(db Database) Trie { // CommitAskTrie the storage trie of the object to dwb. // This updates the trie root. -func (self *tradingExchanges) updateAsksRoot(db Database) error { - self.updateAsksTrie(db) - if self.dbErr != nil { - return self.dbErr +func (te *tradingExchanges) updateAsksRoot(db Database) error { + te.updateAsksTrie(db) + if te.dbErr != nil { + return te.dbErr } - self.data.AskRoot = self.asksTrie.Hash() + te.data.AskRoot = te.asksTrie.Hash() return nil } // CommitAskTrie the storage trie of the object to dwb. // This updates the trie root. -func (self *tradingExchanges) CommitAsksTrie(db Database) error { - self.updateAsksTrie(db) - if self.dbErr != nil { - return self.dbErr +func (te *tradingExchanges) CommitAsksTrie(db Database) error { + te.updateAsksTrie(db) + if te.dbErr != nil { + return te.dbErr } - root, err := self.asksTrie.Commit(func(leaf []byte, parent common.Hash) error { + root, err := te.asksTrie.Commit(func(leaf []byte, parent common.Hash) error { var orderList orderList if err := rlp.DecodeBytes(leaf, &orderList); err != nil { return nil @@ -255,31 +255,31 @@ func (self *tradingExchanges) CommitAsksTrie(db Database) error { return nil }) if err == nil { - self.data.AskRoot = root + te.data.AskRoot = root } return err } -func (c *tradingExchanges) getBidsTrie(db Database) Trie { - if c.bidsTrie == nil { +func (te *tradingExchanges) getBidsTrie(db Database) Trie { + if te.bidsTrie == nil { var err error - c.bidsTrie, err = db.OpenStorageTrie(c.orderBookHash, c.data.BidRoot) + te.bidsTrie, err = db.OpenStorageTrie(te.orderBookHash, te.data.BidRoot) if err != nil { - c.bidsTrie, _ = db.OpenStorageTrie(c.orderBookHash, EmptyHash) - c.setError(fmt.Errorf("can't create bids trie: %v", err)) + te.bidsTrie, _ = db.OpenStorageTrie(te.orderBookHash, EmptyHash) + te.setError(fmt.Errorf("can't create bids trie: %v", err)) } } - return c.bidsTrie + return te.bidsTrie } // updateAskTrie writes cached storage modifications into the object's storage trie. -func (self *tradingExchanges) updateBidsTrie(db Database) Trie { - tr := self.getBidsTrie(db) - for price, orderList := range self.stateBidObjects { - if _, isDirty := self.stateBidObjectsDirty[price]; isDirty { - delete(self.stateBidObjectsDirty, price) +func (te *tradingExchanges) updateBidsTrie(db Database) Trie { + tr := te.getBidsTrie(db) + for price, orderList := range te.stateBidObjects { + if _, isDirty := te.stateBidObjectsDirty[price]; isDirty { + delete(te.stateBidObjectsDirty, price) if orderList.empty() { - self.setError(tr.TryDelete(price[:])) + te.setError(tr.TryDelete(price[:])) continue } err := orderList.updateRoot(db) @@ -288,25 +288,25 @@ func (self *tradingExchanges) updateBidsTrie(db Database) Trie { } // Encoding []byte cannot fail, ok to ignore the error. v, _ := rlp.EncodeToBytes(orderList) - self.setError(tr.TryUpdate(price[:], v)) + te.setError(tr.TryUpdate(price[:], v)) } } return tr } -func (self *tradingExchanges) updateBidsRoot(db Database) { - self.updateBidsTrie(db) - self.data.BidRoot = self.bidsTrie.Hash() +func (te *tradingExchanges) updateBidsRoot(db Database) { + te.updateBidsTrie(db) + te.data.BidRoot = te.bidsTrie.Hash() } // CommitAskTrie the storage trie of the object to dwb. // This updates the trie root. -func (self *tradingExchanges) CommitBidsTrie(db Database) error { - self.updateBidsTrie(db) - if self.dbErr != nil { - return self.dbErr +func (te *tradingExchanges) CommitBidsTrie(db Database) error { + te.updateBidsTrie(db) + if te.dbErr != nil { + return te.dbErr } - root, err := self.bidsTrie.Commit(func(leaf []byte, parent common.Hash) error { + root, err := te.bidsTrie.Commit(func(leaf []byte, parent common.Hash) error { var orderList orderList if err := rlp.DecodeBytes(leaf, &orderList); err != nil { return nil @@ -317,116 +317,116 @@ func (self *tradingExchanges) CommitBidsTrie(db Database) error { return nil }) if err == nil { - self.data.BidRoot = root + te.data.BidRoot = root } return err } -func (self *tradingExchanges) deepCopy(db *TradingStateDB, onDirty func(hash common.Hash)) *tradingExchanges { - stateExchanges := newStateExchanges(db, self.orderBookHash, self.data, onDirty) - if self.asksTrie != nil { - stateExchanges.asksTrie = db.db.CopyTrie(self.asksTrie) +func (te *tradingExchanges) deepCopy(db *TradingStateDB, onDirty func(hash common.Hash)) *tradingExchanges { + stateExchanges := newStateExchanges(db, te.orderBookHash, te.data, onDirty) + if te.asksTrie != nil { + stateExchanges.asksTrie = db.db.CopyTrie(te.asksTrie) } - if self.bidsTrie != nil { - stateExchanges.bidsTrie = db.db.CopyTrie(self.bidsTrie) + if te.bidsTrie != nil { + stateExchanges.bidsTrie = db.db.CopyTrie(te.bidsTrie) } - if self.ordersTrie != nil { - stateExchanges.ordersTrie = db.db.CopyTrie(self.ordersTrie) + if te.ordersTrie != nil { + stateExchanges.ordersTrie = db.db.CopyTrie(te.ordersTrie) } - for price, bidObject := range self.stateBidObjects { - stateExchanges.stateBidObjects[price] = bidObject.deepCopy(db, self.MarkStateBidObjectDirty) + for price, bidObject := range te.stateBidObjects { + stateExchanges.stateBidObjects[price] = bidObject.deepCopy(db, te.MarkStateBidObjectDirty) } - for price := range self.stateBidObjectsDirty { + for price := range te.stateBidObjectsDirty { stateExchanges.stateBidObjectsDirty[price] = struct{}{} } - for price, askObject := range self.stateAskObjects { - stateExchanges.stateAskObjects[price] = askObject.deepCopy(db, self.MarkStateAskObjectDirty) + for price, askObject := range te.stateAskObjects { + stateExchanges.stateAskObjects[price] = askObject.deepCopy(db, te.MarkStateAskObjectDirty) } - for price := range self.stateAskObjectsDirty { + for price := range te.stateAskObjectsDirty { stateExchanges.stateAskObjectsDirty[price] = struct{}{} } - for orderId, orderItem := range self.stateOrderObjects { - stateExchanges.stateOrderObjects[orderId] = orderItem.deepCopy(self.MarkStateOrderObjectDirty) + for orderId, orderItem := range te.stateOrderObjects { + stateExchanges.stateOrderObjects[orderId] = orderItem.deepCopy(te.MarkStateOrderObjectDirty) } - for orderId := range self.stateOrderObjectsDirty { + for orderId := range te.stateOrderObjectsDirty { stateExchanges.stateOrderObjectsDirty[orderId] = struct{}{} } - for price, liquidationPrice := range self.liquidationPriceStates { - stateExchanges.liquidationPriceStates[price] = liquidationPrice.deepCopy(db, self.MarkStateLiquidationPriceDirty) + for price, liquidationPrice := range te.liquidationPriceStates { + stateExchanges.liquidationPriceStates[price] = liquidationPrice.deepCopy(db, te.MarkStateLiquidationPriceDirty) } - for price := range self.liquidationPriceStatesDirty { + for price := range te.liquidationPriceStatesDirty { stateExchanges.liquidationPriceStatesDirty[price] = struct{}{} } return stateExchanges } // Returns the address of the contract/orderId -func (c *tradingExchanges) Hash() common.Hash { - return c.orderBookHash +func (te *tradingExchanges) Hash() common.Hash { + return te.orderBookHash } -func (self *tradingExchanges) SetNonce(nonce uint64) { - self.setNonce(nonce) +func (te *tradingExchanges) SetNonce(nonce uint64) { + te.setNonce(nonce) } -func (self *tradingExchanges) setNonce(nonce uint64) { - self.data.Nonce = nonce - if self.onDirty != nil { - self.onDirty(self.Hash()) - self.onDirty = nil +func (te *tradingExchanges) setNonce(nonce uint64) { + te.data.Nonce = nonce + if te.onDirty != nil { + te.onDirty(te.Hash()) + te.onDirty = nil } } -func (self *tradingExchanges) Nonce() uint64 { - return self.data.Nonce +func (te *tradingExchanges) Nonce() uint64 { + return te.data.Nonce } -func (self *tradingExchanges) setLastPrice(price *big.Int) { - self.data.LastPrice = price - if self.onDirty != nil { - self.onDirty(self.Hash()) - self.onDirty = nil +func (te *tradingExchanges) setLastPrice(price *big.Int) { + te.data.LastPrice = price + if te.onDirty != nil { + te.onDirty(te.Hash()) + te.onDirty = nil } } -func (self *tradingExchanges) setMediumPriceBeforeEpoch(price *big.Int) { - self.data.MediumPriceBeforeEpoch = price - if self.onDirty != nil { - self.onDirty(self.Hash()) - self.onDirty = nil +func (te *tradingExchanges) setMediumPriceBeforeEpoch(price *big.Int) { + te.data.MediumPriceBeforeEpoch = price + if te.onDirty != nil { + te.onDirty(te.Hash()) + te.onDirty = nil } } -func (self *tradingExchanges) setMediumPrice(price *big.Int, quantity *big.Int) { - self.data.MediumPrice = price - self.data.TotalQuantity = quantity - if self.onDirty != nil { - self.onDirty(self.Hash()) - self.onDirty = nil +func (te *tradingExchanges) setMediumPrice(price *big.Int, quantity *big.Int) { + te.data.MediumPrice = price + te.data.TotalQuantity = quantity + if te.onDirty != nil { + te.onDirty(te.Hash()) + te.onDirty = nil } } // updateStateExchangeObject writes the given object to the trie. -func (self *tradingExchanges) removeStateOrderListAskObject(db Database, stateOrderList *stateOrderList) { - self.setError(self.asksTrie.TryDelete(stateOrderList.price[:])) +func (te *tradingExchanges) removeStateOrderListAskObject(db Database, stateOrderList *stateOrderList) { + te.setError(te.asksTrie.TryDelete(stateOrderList.price[:])) } // updateStateExchangeObject writes the given object to the trie. -func (self *tradingExchanges) removeStateOrderListBidObject(db Database, stateOrderList *stateOrderList) { - self.setError(self.bidsTrie.TryDelete(stateOrderList.price[:])) +func (te *tradingExchanges) removeStateOrderListBidObject(db Database, stateOrderList *stateOrderList) { + te.setError(te.bidsTrie.TryDelete(stateOrderList.price[:])) } // Retrieve a state object given my the address. Returns nil if not found. -func (self *tradingExchanges) getStateOrderListAskObject(db Database, price common.Hash) (stateOrderList *stateOrderList) { +func (te *tradingExchanges) getStateOrderListAskObject(db Database, price common.Hash) (stateOrderList *stateOrderList) { // Prefer 'live' objects. - if obj := self.stateAskObjects[price]; obj != nil { + if obj := te.stateAskObjects[price]; obj != nil { return obj } // Load the object from the database. - enc, err := self.getAsksTrie(db).TryGet(price[:]) + enc, err := te.getAsksTrie(db).TryGet(price[:]) if len(enc) == 0 { - self.setError(err) + te.setError(err) return nil } var data orderList @@ -435,50 +435,50 @@ func (self *tradingExchanges) getStateOrderListAskObject(db Database, price comm return nil } // Insert into the live set. - obj := newStateOrderList(self.db, Bid, self.orderBookHash, price, data, self.MarkStateAskObjectDirty) - self.stateAskObjects[price] = obj + obj := newStateOrderList(te.db, Bid, te.orderBookHash, price, data, te.MarkStateAskObjectDirty) + te.stateAskObjects[price] = obj return obj } // MarkStateAskObjectDirty adds the specified object to the dirty map to avoid costly // state object cache iteration to find a handful of modified ones. -func (self *tradingExchanges) MarkStateAskObjectDirty(price common.Hash) { - self.stateAskObjectsDirty[price] = struct{}{} - if self.onDirty != nil { - self.onDirty(self.Hash()) - self.onDirty = nil +func (te *tradingExchanges) MarkStateAskObjectDirty(price common.Hash) { + te.stateAskObjectsDirty[price] = struct{}{} + if te.onDirty != nil { + te.onDirty(te.Hash()) + te.onDirty = nil } } // createStateOrderListObject creates a new state object. If there is an existing orderId with // the given address, it is overwritten and returned as the second return value. -func (self *tradingExchanges) createStateOrderListAskObject(db Database, price common.Hash) (newobj *stateOrderList) { - newobj = newStateOrderList(self.db, Ask, self.orderBookHash, price, orderList{Volume: Zero}, self.MarkStateAskObjectDirty) - self.stateAskObjects[price] = newobj - self.stateAskObjectsDirty[price] = struct{}{} +func (te *tradingExchanges) createStateOrderListAskObject(db Database, price common.Hash) (newobj *stateOrderList) { + newobj = newStateOrderList(te.db, Ask, te.orderBookHash, price, orderList{Volume: Zero}, te.MarkStateAskObjectDirty) + te.stateAskObjects[price] = newobj + te.stateAskObjectsDirty[price] = struct{}{} data, err := rlp.EncodeToBytes(newobj) if err != nil { panic(fmt.Errorf("can't encode order list object at %x: %v", price[:], err)) } - self.setError(self.asksTrie.TryUpdate(price[:], data)) - if self.onDirty != nil { - self.onDirty(self.Hash()) - self.onDirty = nil + te.setError(te.asksTrie.TryUpdate(price[:], data)) + if te.onDirty != nil { + te.onDirty(te.Hash()) + te.onDirty = nil } return newobj } // Retrieve a state object given my the address. Returns nil if not found. -func (self *tradingExchanges) getStateBidOrderListObject(db Database, price common.Hash) (stateOrderList *stateOrderList) { +func (te *tradingExchanges) getStateBidOrderListObject(db Database, price common.Hash) (stateOrderList *stateOrderList) { // Prefer 'live' objects. - if obj := self.stateBidObjects[price]; obj != nil { + if obj := te.stateBidObjects[price]; obj != nil { return obj } // Load the object from the database. - enc, err := self.getBidsTrie(db).TryGet(price[:]) + enc, err := te.getBidsTrie(db).TryGet(price[:]) if len(enc) == 0 { - self.setError(err) + te.setError(err) return nil } var data orderList @@ -487,50 +487,50 @@ func (self *tradingExchanges) getStateBidOrderListObject(db Database, price comm return nil } // Insert into the live set. - obj := newStateOrderList(self.db, Bid, self.orderBookHash, price, data, self.MarkStateBidObjectDirty) - self.stateBidObjects[price] = obj + obj := newStateOrderList(te.db, Bid, te.orderBookHash, price, data, te.MarkStateBidObjectDirty) + te.stateBidObjects[price] = obj return obj } // MarkStateAskObjectDirty adds the specified object to the dirty map to avoid costly // state object cache iteration to find a handful of modified ones. -func (self *tradingExchanges) MarkStateBidObjectDirty(price common.Hash) { - self.stateBidObjectsDirty[price] = struct{}{} - if self.onDirty != nil { - self.onDirty(self.Hash()) - self.onDirty = nil +func (te *tradingExchanges) MarkStateBidObjectDirty(price common.Hash) { + te.stateBidObjectsDirty[price] = struct{}{} + if te.onDirty != nil { + te.onDirty(te.Hash()) + te.onDirty = nil } } // createStateOrderListObject creates a new state object. If there is an existing orderId with // the given address, it is overwritten and returned as the second return value. -func (self *tradingExchanges) createStateBidOrderListObject(db Database, price common.Hash) (newobj *stateOrderList) { - newobj = newStateOrderList(self.db, Bid, self.orderBookHash, price, orderList{Volume: Zero}, self.MarkStateBidObjectDirty) - self.stateBidObjects[price] = newobj - self.stateBidObjectsDirty[price] = struct{}{} +func (te *tradingExchanges) createStateBidOrderListObject(db Database, price common.Hash) (newobj *stateOrderList) { + newobj = newStateOrderList(te.db, Bid, te.orderBookHash, price, orderList{Volume: Zero}, te.MarkStateBidObjectDirty) + te.stateBidObjects[price] = newobj + te.stateBidObjectsDirty[price] = struct{}{} data, err := rlp.EncodeToBytes(newobj) if err != nil { panic(fmt.Errorf("can't encode order list object at %x: %v", price[:], err)) } - self.setError(self.bidsTrie.TryUpdate(price[:], data)) - if self.onDirty != nil { - self.onDirty(self.Hash()) - self.onDirty = nil + te.setError(te.bidsTrie.TryUpdate(price[:], data)) + if te.onDirty != nil { + te.onDirty(te.Hash()) + te.onDirty = nil } return newobj } // Retrieve a state object given my the address. Returns nil if not found. -func (self *tradingExchanges) getStateOrderObject(db Database, orderId common.Hash) (stateOrderItem *stateOrderItem) { +func (te *tradingExchanges) getStateOrderObject(db Database, orderId common.Hash) (stateOrderItem *stateOrderItem) { // Prefer 'live' objects. - if obj := self.stateOrderObjects[orderId]; obj != nil { + if obj := te.stateOrderObjects[orderId]; obj != nil { return obj } // Load the object from the database. - enc, err := self.getOrdersTrie(db).TryGet(orderId[:]) + enc, err := te.getOrdersTrie(db).TryGet(orderId[:]) if len(enc) == 0 { - self.setError(err) + te.setError(err) return nil } var data OrderItem @@ -539,48 +539,48 @@ func (self *tradingExchanges) getStateOrderObject(db Database, orderId common.Ha return nil } // Insert into the live set. - obj := newStateOrderItem(self.orderBookHash, orderId, data, self.MarkStateOrderObjectDirty) - self.stateOrderObjects[orderId] = obj + obj := newStateOrderItem(te.orderBookHash, orderId, data, te.MarkStateOrderObjectDirty) + te.stateOrderObjects[orderId] = obj return obj } // MarkStateAskObjectDirty adds the specified object to the dirty map to avoid costly // state object cache iteration to find a handful of modified ones. -func (self *tradingExchanges) MarkStateOrderObjectDirty(orderId common.Hash) { - self.stateOrderObjectsDirty[orderId] = struct{}{} - if self.onDirty != nil { - self.onDirty(self.Hash()) - self.onDirty = nil +func (te *tradingExchanges) MarkStateOrderObjectDirty(orderId common.Hash) { + te.stateOrderObjectsDirty[orderId] = struct{}{} + if te.onDirty != nil { + te.onDirty(te.Hash()) + te.onDirty = nil } } // createStateOrderListObject creates a new state object. If there is an existing orderId with // the given address, it is overwritten and returned as the second return value. -func (self *tradingExchanges) createStateOrderObject(db Database, orderId common.Hash, order OrderItem) (newobj *stateOrderItem) { - newobj = newStateOrderItem(self.orderBookHash, orderId, order, self.MarkStateOrderObjectDirty) +func (t *tradingExchanges) createStateOrderObject(db Database, orderId common.Hash, order OrderItem) (newobj *stateOrderItem) { + newobj = newStateOrderItem(t.orderBookHash, orderId, order, t.MarkStateOrderObjectDirty) orderIdHash := common.BigToHash(new(big.Int).SetUint64(order.OrderID)) - self.stateOrderObjects[orderIdHash] = newobj - self.stateOrderObjectsDirty[orderIdHash] = struct{}{} - if self.onDirty != nil { - self.onDirty(self.orderBookHash) - self.onDirty = nil + t.stateOrderObjects[orderIdHash] = newobj + t.stateOrderObjectsDirty[orderIdHash] = struct{}{} + if t.onDirty != nil { + t.onDirty(t.orderBookHash) + t.onDirty = nil } return newobj } // updateAskTrie writes cached storage modifications into the object's storage trie. -func (self *tradingExchanges) updateOrdersTrie(db Database) Trie { - tr := self.getOrdersTrie(db) - for orderId, orderItem := range self.stateOrderObjects { - if _, isDirty := self.stateOrderObjectsDirty[orderId]; isDirty { - delete(self.stateOrderObjectsDirty, orderId) +func (t *tradingExchanges) updateOrdersTrie(db Database) Trie { + tr := t.getOrdersTrie(db) + for orderId, orderItem := range t.stateOrderObjects { + if _, isDirty := t.stateOrderObjectsDirty[orderId]; isDirty { + delete(t.stateOrderObjectsDirty, orderId) if orderItem.empty() { - self.setError(tr.TryDelete(orderId[:])) + t.setError(tr.TryDelete(orderId[:])) continue } // Encoding []byte cannot fail, ok to ignore the error. v, _ := rlp.EncodeToBytes(orderItem) - self.setError(tr.TryUpdate(orderId[:], v)) + t.setError(tr.TryUpdate(orderId[:], v)) } } return tr @@ -588,71 +588,71 @@ func (self *tradingExchanges) updateOrdersTrie(db Database) Trie { // CommitAskTrie the storage trie of the object to dwb. // This updates the trie root. -func (self *tradingExchanges) updateOrdersRoot(db Database) { - self.updateOrdersTrie(db) - self.data.OrderRoot = self.ordersTrie.Hash() +func (t *tradingExchanges) updateOrdersRoot(db Database) { + t.updateOrdersTrie(db) + t.data.OrderRoot = t.ordersTrie.Hash() } // CommitAskTrie the storage trie of the object to dwb. // This updates the trie root. -func (self *tradingExchanges) CommitOrdersTrie(db Database) error { - self.updateOrdersTrie(db) - if self.dbErr != nil { - return self.dbErr +func (t *tradingExchanges) CommitOrdersTrie(db Database) error { + t.updateOrdersTrie(db) + if t.dbErr != nil { + return t.dbErr } - root, err := self.ordersTrie.Commit(nil) + root, err := t.ordersTrie.Commit(nil) if err == nil { - self.data.OrderRoot = root + t.data.OrderRoot = root } return err } -func (self *tradingExchanges) MarkStateLiquidationPriceDirty(price common.Hash) { - self.liquidationPriceStatesDirty[price] = struct{}{} - if self.onDirty != nil { - self.onDirty(self.Hash()) - self.onDirty = nil +func (t *tradingExchanges) MarkStateLiquidationPriceDirty(price common.Hash) { + t.liquidationPriceStatesDirty[price] = struct{}{} + if t.onDirty != nil { + t.onDirty(t.Hash()) + t.onDirty = nil } } -func (self *tradingExchanges) createStateLiquidationPrice(db Database, liquidationPrice common.Hash) (newobj *liquidationPriceState) { - newobj = newLiquidationPriceState(self.db, self.orderBookHash, liquidationPrice, orderList{Volume: Zero}, self.MarkStateLiquidationPriceDirty) - self.liquidationPriceStates[liquidationPrice] = newobj - self.liquidationPriceStatesDirty[liquidationPrice] = struct{}{} +func (t *tradingExchanges) createStateLiquidationPrice(db Database, liquidationPrice common.Hash) (newobj *liquidationPriceState) { + newobj = newLiquidationPriceState(t.db, t.orderBookHash, liquidationPrice, orderList{Volume: Zero}, t.MarkStateLiquidationPriceDirty) + t.liquidationPriceStates[liquidationPrice] = newobj + t.liquidationPriceStatesDirty[liquidationPrice] = struct{}{} data, err := rlp.EncodeToBytes(newobj) if err != nil { panic(fmt.Errorf("can't encode liquidation price object at %x: %v", liquidationPrice[:], err)) } - self.setError(self.getLiquidationPriceTrie(db).TryUpdate(liquidationPrice[:], data)) - if self.onDirty != nil { - self.onDirty(self.Hash()) - self.onDirty = nil + t.setError(t.getLiquidationPriceTrie(db).TryUpdate(liquidationPrice[:], data)) + if t.onDirty != nil { + t.onDirty(t.Hash()) + t.onDirty = nil } return newobj } -func (self *tradingExchanges) getLiquidationPriceTrie(db Database) Trie { - if self.liquidationPriceTrie == nil { +func (t *tradingExchanges) getLiquidationPriceTrie(db Database) Trie { + if t.liquidationPriceTrie == nil { var err error - self.liquidationPriceTrie, err = db.OpenStorageTrie(self.orderBookHash, self.data.LiquidationPriceRoot) + t.liquidationPriceTrie, err = db.OpenStorageTrie(t.orderBookHash, t.data.LiquidationPriceRoot) if err != nil { - self.liquidationPriceTrie, _ = db.OpenStorageTrie(self.orderBookHash, EmptyHash) - self.setError(fmt.Errorf("can't create liquidation liquidationPrice trie: %v", err)) + t.liquidationPriceTrie, _ = db.OpenStorageTrie(t.orderBookHash, EmptyHash) + t.setError(fmt.Errorf("can't create liquidation liquidationPrice trie: %v", err)) } } - return self.liquidationPriceTrie + return t.liquidationPriceTrie } -func (self *tradingExchanges) getStateLiquidationPrice(db Database, price common.Hash) (stateObject *liquidationPriceState) { +func (t *tradingExchanges) getStateLiquidationPrice(db Database, price common.Hash) (stateObject *liquidationPriceState) { // Prefer 'live' objects. - if obj := self.liquidationPriceStates[price]; obj != nil { + if obj := t.liquidationPriceStates[price]; obj != nil { return obj } // Load the object from the database. - enc, err := self.getLiquidationPriceTrie(db).TryGet(price[:]) + enc, err := t.getLiquidationPriceTrie(db).TryGet(price[:]) if len(enc) == 0 { - self.setError(err) + t.setError(err) return nil } var data orderList @@ -661,16 +661,16 @@ func (self *tradingExchanges) getStateLiquidationPrice(db Database, price common return nil } // Insert into the live set. - obj := newLiquidationPriceState(self.db, self.orderBookHash, price, data, self.MarkStateLiquidationPriceDirty) - self.liquidationPriceStates[price] = obj + obj := newLiquidationPriceState(t.db, t.orderBookHash, price, data, t.MarkStateLiquidationPriceDirty) + t.liquidationPriceStates[price] = obj return obj } -func (self *tradingExchanges) getLowestLiquidationPrice(db Database) (common.Hash, *liquidationPriceState) { - trie := self.getLiquidationPriceTrie(db) +func (t *tradingExchanges) getLowestLiquidationPrice(db Database) (common.Hash, *liquidationPriceState) { + trie := t.getLiquidationPriceTrie(db) encKey, encValue, err := trie.TryGetBestLeftKeyAndValue() if err != nil { - log.Error("Failed find best liquidationPrice ask trie ", "orderbook", self.orderBookHash.Hex()) + log.Error("Failed find best liquidationPrice ask trie ", "orderbook", t.orderBookHash.Hex()) return EmptyHash, nil } if len(encKey) == 0 || len(encValue) == 0 { @@ -678,25 +678,25 @@ func (self *tradingExchanges) getLowestLiquidationPrice(db Database) (common.Has return EmptyHash, nil } price := common.BytesToHash(encKey) - obj := self.liquidationPriceStates[price] + obj := t.liquidationPriceStates[price] if obj == nil { var data orderList if err := rlp.DecodeBytes(encValue, &data); err != nil { log.Error("Failed to decode state get best ask trie", "err", err) return EmptyHash, nil } - obj = newLiquidationPriceState(self.db, self.orderBookHash, price, data, self.MarkStateLiquidationPriceDirty) - self.liquidationPriceStates[price] = obj + obj = newLiquidationPriceState(t.db, t.orderBookHash, price, data, t.MarkStateLiquidationPriceDirty) + t.liquidationPriceStates[price] = obj } return price, obj } -func (self *tradingExchanges) getAllLowerLiquidationPrice(db Database, limit common.Hash) map[common.Hash]*liquidationPriceState { - trie := self.getLiquidationPriceTrie(db) +func (t *tradingExchanges) getAllLowerLiquidationPrice(db Database, limit common.Hash) map[common.Hash]*liquidationPriceState { + trie := t.getLiquidationPriceTrie(db) encKeys, encValues, err := trie.TryGetAllLeftKeyAndValue(limit.Bytes()) result := map[common.Hash]*liquidationPriceState{} if err != nil || len(encKeys) != len(encValues) { - log.Error("Failed get lower liquidation price trie ", "orderbook", self.orderBookHash.Hex(), "encKeys", len(encKeys), "encValues", len(encValues)) + log.Error("Failed get lower liquidation price trie ", "orderbook", t.orderBookHash.Hex(), "encKeys", len(encKeys), "encValues", len(encValues)) return result } if len(encKeys) == 0 || len(encValues) == 0 { @@ -705,15 +705,15 @@ func (self *tradingExchanges) getAllLowerLiquidationPrice(db Database, limit com } for i := range encKeys { price := common.BytesToHash(encKeys[i]) - obj := self.liquidationPriceStates[price] + obj := t.liquidationPriceStates[price] if obj == nil { var data orderList if err := rlp.DecodeBytes(encValues[i], &data); err != nil { log.Error("Failed to decode state get all lower liquidation price trie", "price", price, "encValues", encValues[i], "err", err) return result } - obj = newLiquidationPriceState(self.db, self.orderBookHash, price, data, self.MarkStateLiquidationPriceDirty) - self.liquidationPriceStates[price] = obj + obj = newLiquidationPriceState(t.db, t.orderBookHash, price, data, t.MarkStateLiquidationPriceDirty) + t.liquidationPriceStates[price] = obj } if obj.empty() { continue @@ -723,11 +723,11 @@ func (self *tradingExchanges) getAllLowerLiquidationPrice(db Database, limit com return result } -func (self *tradingExchanges) getHighestLiquidationPrice(db Database) (common.Hash, *liquidationPriceState) { - trie := self.getLiquidationPriceTrie(db) +func (t *tradingExchanges) getHighestLiquidationPrice(db Database) (common.Hash, *liquidationPriceState) { + trie := t.getLiquidationPriceTrie(db) encKey, encValue, err := trie.TryGetBestRightKeyAndValue() if err != nil { - log.Error("Failed find best liquidationPrice ask trie ", "orderbook", self.orderBookHash.Hex()) + log.Error("Failed find best liquidationPrice ask trie ", "orderbook", t.orderBookHash.Hex()) return EmptyHash, nil } if len(encKey) == 0 || len(encValue) == 0 { @@ -735,28 +735,29 @@ func (self *tradingExchanges) getHighestLiquidationPrice(db Database) (common.Ha return EmptyHash, nil } price := common.BytesToHash(encKey) - obj := self.liquidationPriceStates[price] + obj := t.liquidationPriceStates[price] if obj == nil { var data orderList if err := rlp.DecodeBytes(encValue, &data); err != nil { log.Error("Failed to decode state get best ask trie", "err", err) return EmptyHash, nil } - obj = newLiquidationPriceState(self.db, self.orderBookHash, price, data, self.MarkStateLiquidationPriceDirty) - self.liquidationPriceStates[price] = obj + obj = newLiquidationPriceState(t.db, t.orderBookHash, price, data, t.MarkStateLiquidationPriceDirty) + t.liquidationPriceStates[price] = obj } if obj.empty() { return EmptyHash, nil } return price, obj } -func (self *tradingExchanges) updateLiquidationPriceTrie(db Database) Trie { - tr := self.getLiquidationPriceTrie(db) - for price, stateObject := range self.liquidationPriceStates { - if _, isDirty := self.liquidationPriceStatesDirty[price]; isDirty { - delete(self.liquidationPriceStatesDirty, price) + +func (t *tradingExchanges) updateLiquidationPriceTrie(db Database) Trie { + tr := t.getLiquidationPriceTrie(db) + for price, stateObject := range t.liquidationPriceStates { + if _, isDirty := t.liquidationPriceStatesDirty[price]; isDirty { + delete(t.liquidationPriceStatesDirty, price) if stateObject.empty() { - self.setError(tr.TryDelete(price[:])) + t.setError(tr.TryDelete(price[:])) continue } err := stateObject.updateRoot(db) @@ -765,23 +766,23 @@ func (self *tradingExchanges) updateLiquidationPriceTrie(db Database) Trie { } // Encoding []byte cannot fail, ok to ignore the error. v, _ := rlp.EncodeToBytes(stateObject) - self.setError(tr.TryUpdate(price[:], v)) + t.setError(tr.TryUpdate(price[:], v)) } } return tr } -func (self *tradingExchanges) updateLiquidationPriceRoot(db Database) { - self.updateLiquidationPriceTrie(db) - self.data.LiquidationPriceRoot = self.liquidationPriceTrie.Hash() +func (t *tradingExchanges) updateLiquidationPriceRoot(db Database) { + t.updateLiquidationPriceTrie(db) + t.data.LiquidationPriceRoot = t.liquidationPriceTrie.Hash() } -func (self *tradingExchanges) CommitLiquidationPriceTrie(db Database) error { - self.updateLiquidationPriceTrie(db) - if self.dbErr != nil { - return self.dbErr +func (t *tradingExchanges) CommitLiquidationPriceTrie(db Database) error { + t.updateLiquidationPriceTrie(db) + if t.dbErr != nil { + return t.dbErr } - root, err := self.liquidationPriceTrie.Commit(func(leaf []byte, parent common.Hash) error { + root, err := t.liquidationPriceTrie.Commit(func(leaf []byte, parent common.Hash) error { var orderList orderList if err := rlp.DecodeBytes(leaf, &orderList); err != nil { return nil @@ -792,23 +793,23 @@ func (self *tradingExchanges) CommitLiquidationPriceTrie(db Database) error { return nil }) if err == nil { - self.data.LiquidationPriceRoot = root + t.data.LiquidationPriceRoot = root } return err } -func (c *tradingExchanges) addLendingCount(amount *big.Int) { - c.setLendingCount(new(big.Int).Add(c.data.LendingCount, amount)) +func (t *tradingExchanges) addLendingCount(amount *big.Int) { + t.setLendingCount(new(big.Int).Add(t.data.LendingCount, amount)) } -func (c *tradingExchanges) subLendingCount(amount *big.Int) { - c.setLendingCount(new(big.Int).Sub(c.data.LendingCount, amount)) +func (t *tradingExchanges) subLendingCount(amount *big.Int) { + t.setLendingCount(new(big.Int).Sub(t.data.LendingCount, amount)) } -func (self *tradingExchanges) setLendingCount(volume *big.Int) { - self.data.LendingCount = volume - if self.onDirty != nil { - self.onDirty(self.orderBookHash) - self.onDirty = nil +func (t *tradingExchanges) setLendingCount(volume *big.Int) { + t.data.LendingCount = volume + if t.onDirty != nil { + t.onDirty(t.orderBookHash) + t.onDirty = nil } } diff --git a/XDCx/tradingstate/statedb.go b/XDCx/tradingstate/statedb.go index e5e4f4b06fba..036b5cac4cbf 100644 --- a/XDCx/tradingstate/statedb.go +++ b/XDCx/tradingstate/statedb.go @@ -78,55 +78,55 @@ func New(root common.Hash, db Database) (*TradingStateDB, error) { } // setError remembers the first non-nil error it is called with. -func (self *TradingStateDB) setError(err error) { - if self.dbErr == nil { - self.dbErr = err +func (t *TradingStateDB) setError(err error) { + if t.dbErr == nil { + t.dbErr = err } } -func (self *TradingStateDB) Error() error { - return self.dbErr +func (t *TradingStateDB) Error() error { + return t.dbErr } // Exist reports whether the given orderId address exists in the state. // Notably this also returns true for suicided exchanges. -func (self *TradingStateDB) Exist(addr common.Hash) bool { - return self.getStateExchangeObject(addr) != nil +func (t *TradingStateDB) Exist(addr common.Hash) bool { + return t.getStateExchangeObject(addr) != nil } // Empty returns whether the state object is either non-existent // or empty according to the EIP161 specification (balance = nonce = code = 0) -func (self *TradingStateDB) Empty(addr common.Hash) bool { - so := self.getStateExchangeObject(addr) +func (t *TradingStateDB) Empty(addr common.Hash) bool { + so := t.getStateExchangeObject(addr) return so == nil || so.empty() } -func (self *TradingStateDB) GetNonce(addr common.Hash) uint64 { - stateObject := self.getStateExchangeObject(addr) +func (t *TradingStateDB) GetNonce(addr common.Hash) uint64 { + stateObject := t.getStateExchangeObject(addr) if stateObject != nil { return stateObject.Nonce() } return 0 } -func (self *TradingStateDB) GetLastPrice(addr common.Hash) *big.Int { - stateObject := self.getStateExchangeObject(addr) +func (t *TradingStateDB) GetLastPrice(addr common.Hash) *big.Int { + stateObject := t.getStateExchangeObject(addr) if stateObject != nil { return stateObject.data.LastPrice } return nil } -func (self *TradingStateDB) GetMediumPriceBeforeEpoch(addr common.Hash) *big.Int { - stateObject := self.getStateExchangeObject(addr) +func (t *TradingStateDB) GetMediumPriceBeforeEpoch(addr common.Hash) *big.Int { + stateObject := t.getStateExchangeObject(addr) if stateObject != nil { return stateObject.data.MediumPriceBeforeEpoch } return Zero } -func (self *TradingStateDB) GetMediumPriceAndTotalAmount(addr common.Hash) (*big.Int, *big.Int) { - stateObject := self.getStateExchangeObject(addr) +func (t *TradingStateDB) GetMediumPriceAndTotalAmount(addr common.Hash) (*big.Int, *big.Int) { + stateObject := t.getStateExchangeObject(addr) if stateObject != nil { return stateObject.data.MediumPrice, stateObject.data.TotalQuantity } @@ -134,25 +134,25 @@ func (self *TradingStateDB) GetMediumPriceAndTotalAmount(addr common.Hash) (*big } // Database retrieves the low level database supporting the lower level trie ops. -func (self *TradingStateDB) Database() Database { - return self.db +func (t *TradingStateDB) Database() Database { + return t.db } -func (self *TradingStateDB) SetNonce(addr common.Hash, nonce uint64) { - stateObject := self.GetOrNewStateExchangeObject(addr) +func (t *TradingStateDB) SetNonce(addr common.Hash, nonce uint64) { + stateObject := t.GetOrNewStateExchangeObject(addr) if stateObject != nil { - self.journal = append(self.journal, nonceChange{ + t.journal = append(t.journal, nonceChange{ hash: addr, - prev: self.GetNonce(addr), + prev: t.GetNonce(addr), }) stateObject.SetNonce(nonce) } } -func (self *TradingStateDB) SetLastPrice(addr common.Hash, price *big.Int) { - stateObject := self.GetOrNewStateExchangeObject(addr) +func (t *TradingStateDB) SetLastPrice(addr common.Hash, price *big.Int) { + stateObject := t.GetOrNewStateExchangeObject(addr) if stateObject != nil { - self.journal = append(self.journal, lastPriceChange{ + t.journal = append(t.journal, lastPriceChange{ hash: addr, prev: stateObject.data.LastPrice, }) @@ -160,10 +160,10 @@ func (self *TradingStateDB) SetLastPrice(addr common.Hash, price *big.Int) { } } -func (self *TradingStateDB) SetMediumPrice(addr common.Hash, price *big.Int, quantity *big.Int) { - stateObject := self.GetOrNewStateExchangeObject(addr) +func (t *TradingStateDB) SetMediumPrice(addr common.Hash, price *big.Int, quantity *big.Int) { + stateObject := t.GetOrNewStateExchangeObject(addr) if stateObject != nil { - self.journal = append(self.journal, mediumPriceChange{ + t.journal = append(t.journal, mediumPriceChange{ hash: addr, prevPrice: stateObject.data.MediumPrice, prevQuantity: stateObject.data.TotalQuantity, @@ -172,10 +172,10 @@ func (self *TradingStateDB) SetMediumPrice(addr common.Hash, price *big.Int, qua } } -func (self *TradingStateDB) SetMediumPriceBeforeEpoch(addr common.Hash, price *big.Int) { - stateObject := self.GetOrNewStateExchangeObject(addr) +func (t *TradingStateDB) SetMediumPriceBeforeEpoch(addr common.Hash, price *big.Int) { + stateObject := t.GetOrNewStateExchangeObject(addr) if stateObject != nil { - self.journal = append(self.journal, mediumPriceBeforeEpochChange{ + t.journal = append(t.journal, mediumPriceBeforeEpochChange{ hash: addr, prevPrice: stateObject.data.MediumPriceBeforeEpoch, }) @@ -183,78 +183,79 @@ func (self *TradingStateDB) SetMediumPriceBeforeEpoch(addr common.Hash, price *b } } -func (self *TradingStateDB) InsertOrderItem(orderBook common.Hash, orderId common.Hash, order OrderItem) { +func (t *TradingStateDB) InsertOrderItem(orderBook common.Hash, orderId common.Hash, order OrderItem) { priceHash := common.BigToHash(order.Price) - stateExchange := self.getStateExchangeObject(orderBook) + stateExchange := t.getStateExchangeObject(orderBook) if stateExchange == nil { - stateExchange = self.createExchangeObject(orderBook) + stateExchange = t.createExchangeObject(orderBook) } var stateOrderList *stateOrderList switch order.Side { case Ask: - stateOrderList = stateExchange.getStateOrderListAskObject(self.db, priceHash) + stateOrderList = stateExchange.getStateOrderListAskObject(t.db, priceHash) if stateOrderList == nil { - stateOrderList = stateExchange.createStateOrderListAskObject(self.db, priceHash) + stateOrderList = stateExchange.createStateOrderListAskObject(t.db, priceHash) } case Bid: - stateOrderList = stateExchange.getStateBidOrderListObject(self.db, priceHash) + stateOrderList = stateExchange.getStateBidOrderListObject(t.db, priceHash) if stateOrderList == nil { - stateOrderList = stateExchange.createStateBidOrderListObject(self.db, priceHash) + stateOrderList = stateExchange.createStateBidOrderListObject(t.db, priceHash) } default: return } - self.journal = append(self.journal, insertOrder{ + t.journal = append(t.journal, insertOrder{ orderBook: orderBook, orderId: orderId, order: &order, }) - stateExchange.createStateOrderObject(self.db, orderId, order) - stateOrderList.insertOrderItem(self.db, orderId, common.BigToHash(order.Quantity)) + stateExchange.createStateOrderObject(t.db, orderId, order) + stateOrderList.insertOrderItem(t.db, orderId, common.BigToHash(order.Quantity)) stateOrderList.AddVolume(order.Quantity) } -func (self *TradingStateDB) GetOrder(orderBook common.Hash, orderId common.Hash) OrderItem { - stateObject := self.GetOrNewStateExchangeObject(orderBook) +func (t *TradingStateDB) GetOrder(orderBook common.Hash, orderId common.Hash) OrderItem { + stateObject := t.GetOrNewStateExchangeObject(orderBook) if stateObject == nil { return EmptyOrder } - stateOrderItem := stateObject.getStateOrderObject(self.db, orderId) + stateOrderItem := stateObject.getStateOrderObject(t.db, orderId) if stateOrderItem == nil { return EmptyOrder } return stateOrderItem.data } -func (self *TradingStateDB) SubAmountOrderItem(orderBook common.Hash, orderId common.Hash, price *big.Int, amount *big.Int, side string) error { + +func (t *TradingStateDB) SubAmountOrderItem(orderBook common.Hash, orderId common.Hash, price *big.Int, amount *big.Int, side string) error { priceHash := common.BigToHash(price) - stateObject := self.GetOrNewStateExchangeObject(orderBook) + stateObject := t.GetOrNewStateExchangeObject(orderBook) if stateObject == nil { return fmt.Errorf("not found orderBook: %s", orderBook.Hex()) } var stateOrderList *stateOrderList switch side { case Ask: - stateOrderList = stateObject.getStateOrderListAskObject(self.db, priceHash) + stateOrderList = stateObject.getStateOrderListAskObject(t.db, priceHash) case Bid: - stateOrderList = stateObject.getStateBidOrderListObject(self.db, priceHash) + stateOrderList = stateObject.getStateBidOrderListObject(t.db, priceHash) default: return fmt.Errorf("not found order type: %s", side) } if stateOrderList == nil || stateOrderList.empty() { return fmt.Errorf("empty Orderlist: order book: %s , order id : %s , price : %s", orderBook, orderId.Hex(), priceHash.Hex()) } - stateOrderItem := stateObject.getStateOrderObject(self.db, orderId) + stateOrderItem := stateObject.getStateOrderObject(t.db, orderId) if stateOrderItem == nil || stateOrderItem.empty() { return fmt.Errorf("empty OrderItem: order book: %s , order id : %s , price : %s", orderBook, orderId.Hex(), priceHash.Hex()) } - currentAmount := new(big.Int).SetBytes(stateOrderList.GetOrderAmount(self.db, orderId).Bytes()[:]) + currentAmount := new(big.Int).SetBytes(stateOrderList.GetOrderAmount(t.db, orderId).Bytes()[:]) if currentAmount.Cmp(amount) < 0 { return fmt.Errorf("not enough order amount: %s , have : %d , want : %d ", orderId.Hex(), currentAmount, amount) } - self.journal = append(self.journal, subAmountOrder{ + t.journal = append(t.journal, subAmountOrder{ orderBook: orderBook, orderId: orderId, - order: self.GetOrder(orderBook, orderId), + order: t.GetOrder(orderBook, orderId), amount: amount, }) newAmount := new(big.Int).Sub(currentAmount, amount) @@ -262,29 +263,29 @@ func (self *TradingStateDB) SubAmountOrderItem(orderBook common.Hash, orderId co stateOrderList.subVolume(amount) stateOrderItem.setVolume(newAmount) if newAmount.Sign() == 0 { - stateOrderList.removeOrderItem(self.db, orderId) + stateOrderList.removeOrderItem(t.db, orderId) } else { stateOrderList.setOrderItem(orderId, common.BigToHash(newAmount)) } if stateOrderList.empty() { switch side { case Ask: - stateObject.removeStateOrderListAskObject(self.db, stateOrderList) + stateObject.removeStateOrderListAskObject(t.db, stateOrderList) case Bid: - stateObject.removeStateOrderListBidObject(self.db, stateOrderList) + stateObject.removeStateOrderListBidObject(t.db, stateOrderList) default: } } return nil } -func (self *TradingStateDB) CancelOrder(orderBook common.Hash, order *OrderItem) error { +func (t *TradingStateDB) CancelOrder(orderBook common.Hash, order *OrderItem) error { orderIdHash := common.BigToHash(new(big.Int).SetUint64(order.OrderID)) - stateObject := self.GetOrNewStateExchangeObject(orderBook) + stateObject := t.GetOrNewStateExchangeObject(orderBook) if stateObject == nil { return fmt.Errorf("not found orderBook: %s", orderBook.Hex()) } - stateOrderItem := stateObject.getStateOrderObject(self.db, orderIdHash) + stateOrderItem := stateObject.getStateOrderObject(t.db, orderIdHash) if stateOrderItem == nil || stateOrderItem.empty() { return fmt.Errorf("empty OrderItem: order book: %s , order id : %s", orderBook, orderIdHash.Hex()) } @@ -292,9 +293,9 @@ func (self *TradingStateDB) CancelOrder(orderBook common.Hash, order *OrderItem) var stateOrderList *stateOrderList switch stateOrderItem.data.Side { case Ask: - stateOrderList = stateObject.getStateOrderListAskObject(self.db, priceHash) + stateOrderList = stateObject.getStateOrderListAskObject(t.db, priceHash) case Bid: - stateOrderList = stateObject.getStateBidOrderListObject(self.db, priceHash) + stateOrderList = stateObject.getStateBidOrderListObject(t.db, priceHash) default: return fmt.Errorf("not found order.Side: %s", order.Side) } @@ -311,37 +312,37 @@ func (self *TradingStateDB) CancelOrder(orderBook common.Hash, order *OrderItem) if stateOrderItem.data.ExchangeAddress != order.ExchangeAddress { return fmt.Errorf("mismatch ExchangeAddress when cancel: order book : %s , order id : %s , got : %s , expect : %s", orderBook, orderIdHash.Hex(), order.ExchangeAddress.Hex(), stateOrderItem.data.ExchangeAddress.Hex()) } - self.journal = append(self.journal, cancelOrder{ + t.journal = append(t.journal, cancelOrder{ orderBook: orderBook, orderId: orderIdHash, order: stateOrderItem.data, }) - currentAmount := new(big.Int).SetBytes(stateOrderList.GetOrderAmount(self.db, orderIdHash).Bytes()[:]) + currentAmount := new(big.Int).SetBytes(stateOrderList.GetOrderAmount(t.db, orderIdHash).Bytes()[:]) stateOrderItem.setVolume(big.NewInt(0)) stateOrderList.subVolume(currentAmount) - stateOrderList.removeOrderItem(self.db, orderIdHash) + stateOrderList.removeOrderItem(t.db, orderIdHash) if stateOrderList.empty() { switch stateOrderItem.data.Side { case Ask: - stateObject.removeStateOrderListAskObject(self.db, stateOrderList) + stateObject.removeStateOrderListAskObject(t.db, stateOrderList) case Bid: - stateObject.removeStateOrderListBidObject(self.db, stateOrderList) + stateObject.removeStateOrderListBidObject(t.db, stateOrderList) default: } } return nil } -func (self *TradingStateDB) GetVolume(orderBook common.Hash, price *big.Int, orderType string) *big.Int { - stateObject := self.GetOrNewStateExchangeObject(orderBook) +func (t *TradingStateDB) GetVolume(orderBook common.Hash, price *big.Int, orderType string) *big.Int { + stateObject := t.GetOrNewStateExchangeObject(orderBook) var volume *big.Int = nil if stateObject != nil { var stateOrderList *stateOrderList switch orderType { case Ask: - stateOrderList = stateObject.getStateOrderListAskObject(self.db, common.BigToHash(price)) + stateOrderList = stateObject.getStateOrderListAskObject(t.db, common.BigToHash(price)) case Bid: - stateOrderList = stateObject.getStateBidOrderListObject(self.db, common.BigToHash(price)) + stateOrderList = stateObject.getStateBidOrderListObject(t.db, common.BigToHash(price)) default: return Zero } @@ -352,14 +353,15 @@ func (self *TradingStateDB) GetVolume(orderBook common.Hash, price *big.Int, ord } return volume } -func (self *TradingStateDB) GetBestAskPrice(orderBook common.Hash) (*big.Int, *big.Int) { - stateObject := self.getStateExchangeObject(orderBook) + +func (t *TradingStateDB) GetBestAskPrice(orderBook common.Hash) (*big.Int, *big.Int) { + stateObject := t.getStateExchangeObject(orderBook) if stateObject != nil { - priceHash := stateObject.getBestPriceAsksTrie(self.db) + priceHash := stateObject.getBestPriceAsksTrie(t.db) if common.EmptyHash(priceHash) { return Zero, Zero } - orderList := stateObject.getStateOrderListAskObject(self.db, priceHash) + orderList := stateObject.getStateOrderListAskObject(t.db, priceHash) if orderList == nil { log.Error("order list ask not found", "price", priceHash.Hex()) return Zero, Zero @@ -369,14 +371,14 @@ func (self *TradingStateDB) GetBestAskPrice(orderBook common.Hash) (*big.Int, *b return Zero, Zero } -func (self *TradingStateDB) GetBestBidPrice(orderBook common.Hash) (*big.Int, *big.Int) { - stateObject := self.getStateExchangeObject(orderBook) +func (t *TradingStateDB) GetBestBidPrice(orderBook common.Hash) (*big.Int, *big.Int) { + stateObject := t.getStateExchangeObject(orderBook) if stateObject != nil { - priceHash := stateObject.getBestBidsTrie(self.db) + priceHash := stateObject.getBestBidsTrie(t.db) if common.EmptyHash(priceHash) { return Zero, Zero } - orderList := stateObject.getStateBidOrderListObject(self.db, priceHash) + orderList := stateObject.getStateBidOrderListObject(t.db, priceHash) if orderList == nil { log.Error("order list bid not found", "price", priceHash.Hex()) return Zero, Zero @@ -386,25 +388,25 @@ func (self *TradingStateDB) GetBestBidPrice(orderBook common.Hash) (*big.Int, *b return Zero, Zero } -func (self *TradingStateDB) GetBestOrderIdAndAmount(orderBook common.Hash, price *big.Int, side string) (common.Hash, *big.Int, error) { - stateObject := self.GetOrNewStateExchangeObject(orderBook) +func (t *TradingStateDB) GetBestOrderIdAndAmount(orderBook common.Hash, price *big.Int, side string) (common.Hash, *big.Int, error) { + stateObject := t.GetOrNewStateExchangeObject(orderBook) if stateObject != nil { var stateOrderList *stateOrderList switch side { case Ask: - stateOrderList = stateObject.getStateOrderListAskObject(self.db, common.BigToHash(price)) + stateOrderList = stateObject.getStateOrderListAskObject(t.db, common.BigToHash(price)) case Bid: - stateOrderList = stateObject.getStateBidOrderListObject(self.db, common.BigToHash(price)) + stateOrderList = stateObject.getStateBidOrderListObject(t.db, common.BigToHash(price)) default: return EmptyHash, Zero, fmt.Errorf("not found side: %s", side) } if stateOrderList != nil { - key, _, err := stateOrderList.getTrie(self.db).TryGetBestLeftKeyAndValue() + key, _, err := stateOrderList.getTrie(t.db).TryGetBestLeftKeyAndValue() if err != nil { return EmptyHash, Zero, err } orderId := common.BytesToHash(key) - amount := stateOrderList.GetOrderAmount(self.db, orderId) + amount := stateOrderList.GetOrderAmount(t.db, orderId) return orderId, new(big.Int).SetBytes(amount.Bytes()), nil } return EmptyHash, Zero, fmt.Errorf("not found order list with orderBook: %s , price : %d , side : %s", orderBook.Hex(), price, side) @@ -413,25 +415,25 @@ func (self *TradingStateDB) GetBestOrderIdAndAmount(orderBook common.Hash, price } // updateStateExchangeObject writes the given object to the trie. -func (self *TradingStateDB) updateStateExchangeObject(stateObject *tradingExchanges) { +func (t *TradingStateDB) updateStateExchangeObject(stateObject *tradingExchanges) { addr := stateObject.Hash() data, err := rlp.EncodeToBytes(stateObject) if err != nil { panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err)) } - self.setError(self.trie.TryUpdate(addr[:], data)) + t.setError(t.trie.TryUpdate(addr[:], data)) } // Retrieve a state object given my the address. Returns nil if not found. -func (self *TradingStateDB) getStateExchangeObject(addr common.Hash) (stateObject *tradingExchanges) { +func (t *TradingStateDB) getStateExchangeObject(addr common.Hash) (stateObject *tradingExchanges) { // Prefer 'live' objects. - if obj := self.stateExhangeObjects[addr]; obj != nil { + if obj := t.stateExhangeObjects[addr]; obj != nil { return obj } // Load the object from the database. - enc, err := self.trie.TryGet(addr[:]) + enc, err := t.trie.TryGet(addr[:]) if len(enc) == 0 { - self.setError(err) + t.setError(err) return nil } var data tradingExchangeObject @@ -440,169 +442,169 @@ func (self *TradingStateDB) getStateExchangeObject(addr common.Hash) (stateObjec return nil } // Insert into the live set. - obj := newStateExchanges(self, addr, data, self.MarkStateExchangeObjectDirty) - self.stateExhangeObjects[addr] = obj + obj := newStateExchanges(t, addr, data, t.MarkStateExchangeObjectDirty) + t.stateExhangeObjects[addr] = obj return obj } -func (self *TradingStateDB) setStateExchangeObject(object *tradingExchanges) { - self.stateExhangeObjects[object.Hash()] = object - self.stateExhangeObjectsDirty[object.Hash()] = struct{}{} +func (t *TradingStateDB) setStateExchangeObject(object *tradingExchanges) { + t.stateExhangeObjects[object.Hash()] = object + t.stateExhangeObjectsDirty[object.Hash()] = struct{}{} } // Retrieve a state object or create a new state object if nil. -func (self *TradingStateDB) GetOrNewStateExchangeObject(addr common.Hash) *tradingExchanges { - stateExchangeObject := self.getStateExchangeObject(addr) +func (t *TradingStateDB) GetOrNewStateExchangeObject(addr common.Hash) *tradingExchanges { + stateExchangeObject := t.getStateExchangeObject(addr) if stateExchangeObject == nil { - stateExchangeObject = self.createExchangeObject(addr) + stateExchangeObject = t.createExchangeObject(addr) } return stateExchangeObject } // MarkStateAskObjectDirty adds the specified object to the dirty map to avoid costly // state object cache iteration to find a handful of modified ones. -func (self *TradingStateDB) MarkStateExchangeObjectDirty(addr common.Hash) { - self.stateExhangeObjectsDirty[addr] = struct{}{} +func (t *TradingStateDB) MarkStateExchangeObjectDirty(addr common.Hash) { + t.stateExhangeObjectsDirty[addr] = struct{}{} } // createStateOrderListObject creates a new state object. If there is an existing orderId with // the given address, it is overwritten and returned as the second return value. -func (self *TradingStateDB) createExchangeObject(hash common.Hash) (newobj *tradingExchanges) { - newobj = newStateExchanges(self, hash, tradingExchangeObject{LendingCount: Zero, MediumPrice: Zero, MediumPriceBeforeEpoch: Zero, TotalQuantity: Zero}, self.MarkStateExchangeObjectDirty) +func (t *TradingStateDB) createExchangeObject(hash common.Hash) (newobj *tradingExchanges) { + newobj = newStateExchanges(t, hash, tradingExchangeObject{LendingCount: Zero, MediumPrice: Zero, MediumPriceBeforeEpoch: Zero, TotalQuantity: Zero}, t.MarkStateExchangeObjectDirty) newobj.setNonce(0) // sets the object to dirty - self.setStateExchangeObject(newobj) + t.setStateExchangeObject(newobj) return newobj } // Copy creates a deep, independent copy of the state. // Snapshots of the copied state cannot be applied to the copy. -func (self *TradingStateDB) Copy() *TradingStateDB { - self.lock.Lock() - defer self.lock.Unlock() +func (t *TradingStateDB) Copy() *TradingStateDB { + t.lock.Lock() + defer t.lock.Unlock() // Copy all the basic fields, initialize the memory ones state := &TradingStateDB{ - db: self.db, - trie: self.db.CopyTrie(self.trie), - stateExhangeObjects: make(map[common.Hash]*tradingExchanges, len(self.stateExhangeObjectsDirty)), - stateExhangeObjectsDirty: make(map[common.Hash]struct{}, len(self.stateExhangeObjectsDirty)), + db: t.db, + trie: t.db.CopyTrie(t.trie), + stateExhangeObjects: make(map[common.Hash]*tradingExchanges, len(t.stateExhangeObjectsDirty)), + stateExhangeObjectsDirty: make(map[common.Hash]struct{}, len(t.stateExhangeObjectsDirty)), } // Copy the dirty states, logs, and preimages - for addr := range self.stateExhangeObjectsDirty { + for addr := range t.stateExhangeObjectsDirty { state.stateExhangeObjectsDirty[addr] = struct{}{} } - for addr, exchangeObject := range self.stateExhangeObjects { + for addr, exchangeObject := range t.stateExhangeObjects { state.stateExhangeObjects[addr] = exchangeObject.deepCopy(state, state.MarkStateExchangeObjectDirty) } return state } -func (s *TradingStateDB) clearJournalAndRefund() { - s.journal = nil - s.validRevisions = s.validRevisions[:0] +func (t *TradingStateDB) clearJournalAndRefund() { + t.journal = nil + t.validRevisions = t.validRevisions[:0] } // Snapshot returns an identifier for the current revision of the state. -func (self *TradingStateDB) Snapshot() int { - id := self.nextRevisionId - self.nextRevisionId++ - self.validRevisions = append(self.validRevisions, revision{id, len(self.journal)}) +func (t *TradingStateDB) Snapshot() int { + id := t.nextRevisionId + t.nextRevisionId++ + t.validRevisions = append(t.validRevisions, revision{id, len(t.journal)}) return id } // RevertToSnapshot reverts all state changes made since the given revision. -func (self *TradingStateDB) RevertToSnapshot(revid int) { +func (t *TradingStateDB) RevertToSnapshot(revid int) { // Find the snapshot in the stack of valid snapshots. - idx := sort.Search(len(self.validRevisions), func(i int) bool { - return self.validRevisions[i].id >= revid + idx := sort.Search(len(t.validRevisions), func(i int) bool { + return t.validRevisions[i].id >= revid }) - if idx == len(self.validRevisions) || self.validRevisions[idx].id != revid { + if idx == len(t.validRevisions) || t.validRevisions[idx].id != revid { panic(fmt.Errorf("revision id %v cannot be reverted", revid)) } - snapshot := self.validRevisions[idx].journalIndex + snapshot := t.validRevisions[idx].journalIndex // Replay the journal to undo changes. - for i := len(self.journal) - 1; i >= snapshot; i-- { - self.journal[i].undo(self) + for i := len(t.journal) - 1; i >= snapshot; i-- { + t.journal[i].undo(t) } - self.journal = self.journal[:snapshot] + t.journal = t.journal[:snapshot] // Remove invalidated snapshots from the stack. - self.validRevisions = self.validRevisions[:idx] + t.validRevisions = t.validRevisions[:idx] } // Finalise finalises the state by removing the self destructed objects // and clears the journal as well as the refunds. -func (s *TradingStateDB) Finalise() { +func (t *TradingStateDB) Finalise() { // Commit objects to the trie. - for addr, stateObject := range s.stateExhangeObjects { - if _, isDirty := s.stateExhangeObjectsDirty[addr]; isDirty { + for addr, stateObject := range t.stateExhangeObjects { + if _, isDirty := t.stateExhangeObjectsDirty[addr]; isDirty { // Write any storage changes in the state object to its storage trie. - err := stateObject.updateAsksRoot(s.db) + err := stateObject.updateAsksRoot(t.db) if err != nil { log.Warn("Finalise updateAsksRoot", "err", err, "addr", addr, "stateObject", *stateObject) } - stateObject.updateBidsRoot(s.db) - stateObject.updateOrdersRoot(s.db) - stateObject.updateLiquidationPriceRoot(s.db) + stateObject.updateBidsRoot(t.db) + stateObject.updateOrdersRoot(t.db) + stateObject.updateLiquidationPriceRoot(t.db) // Update the object in the main orderId trie. - s.updateStateExchangeObject(stateObject) + t.updateStateExchangeObject(stateObject) //delete(s.stateExhangeObjectsDirty, addr) } } - s.clearJournalAndRefund() + t.clearJournalAndRefund() } // IntermediateRoot computes the current root orderBookHash of the state trie. // It is called in between transactions to get the root orderBookHash that // goes into transaction receipts. -func (s *TradingStateDB) IntermediateRoot() common.Hash { - s.Finalise() - return s.trie.Hash() +func (t *TradingStateDB) IntermediateRoot() common.Hash { + t.Finalise() + return t.trie.Hash() } // Commit writes the state to the underlying in-memory trie database. -func (s *TradingStateDB) Commit() (root common.Hash, err error) { - defer s.clearJournalAndRefund() +func (t *TradingStateDB) Commit() (root common.Hash, err error) { + defer t.clearJournalAndRefund() // Commit objects to the trie. - for addr, stateObject := range s.stateExhangeObjects { - if _, isDirty := s.stateExhangeObjectsDirty[addr]; isDirty { + for addr, stateObject := range t.stateExhangeObjects { + if _, isDirty := t.stateExhangeObjectsDirty[addr]; isDirty { // Write any storage changes in the state object to its storage trie. - if err := stateObject.CommitAsksTrie(s.db); err != nil { + if err := stateObject.CommitAsksTrie(t.db); err != nil { return EmptyHash, err } - if err := stateObject.CommitBidsTrie(s.db); err != nil { + if err := stateObject.CommitBidsTrie(t.db); err != nil { return EmptyHash, err } - if err := stateObject.CommitOrdersTrie(s.db); err != nil { + if err := stateObject.CommitOrdersTrie(t.db); err != nil { return EmptyHash, err } - if err := stateObject.CommitLiquidationPriceTrie(s.db); err != nil { + if err := stateObject.CommitLiquidationPriceTrie(t.db); err != nil { return EmptyHash, err } // Update the object in the main orderId trie. - s.updateStateExchangeObject(stateObject) - delete(s.stateExhangeObjectsDirty, addr) + t.updateStateExchangeObject(stateObject) + delete(t.stateExhangeObjectsDirty, addr) } } // Write trie changes. - root, err = s.trie.Commit(func(leaf []byte, parent common.Hash) error { + root, err = t.trie.Commit(func(leaf []byte, parent common.Hash) error { var exchange tradingExchangeObject if err := rlp.DecodeBytes(leaf, &exchange); err != nil { return nil } if exchange.AskRoot != EmptyRoot { - s.db.TrieDB().Reference(exchange.AskRoot, parent) + t.db.TrieDB().Reference(exchange.AskRoot, parent) } if exchange.BidRoot != EmptyRoot { - s.db.TrieDB().Reference(exchange.BidRoot, parent) + t.db.TrieDB().Reference(exchange.BidRoot, parent) } if exchange.OrderRoot != EmptyRoot { - s.db.TrieDB().Reference(exchange.OrderRoot, parent) + t.db.TrieDB().Reference(exchange.OrderRoot, parent) } if exchange.LiquidationPriceRoot != EmptyRoot { - s.db.TrieDB().Reference(exchange.LiquidationPriceRoot, parent) + t.db.TrieDB().Reference(exchange.LiquidationPriceRoot, parent) } return nil }) @@ -610,19 +612,19 @@ func (s *TradingStateDB) Commit() (root common.Hash, err error) { return root, err } -func (self *TradingStateDB) GetAllLowerLiquidationPriceData(orderBook common.Hash, limit *big.Int) map[*big.Int]map[common.Hash][]common.Hash { +func (t *TradingStateDB) GetAllLowerLiquidationPriceData(orderBook common.Hash, limit *big.Int) map[*big.Int]map[common.Hash][]common.Hash { result := map[*big.Int]map[common.Hash][]common.Hash{} - orderbookState := self.getStateExchangeObject(orderBook) + orderbookState := t.getStateExchangeObject(orderBook) if orderbookState == nil { return result } - mapPrices := orderbookState.getAllLowerLiquidationPrice(self.db, common.BigToHash(limit)) + mapPrices := orderbookState.getAllLowerLiquidationPrice(t.db, common.BigToHash(limit)) for priceHash, liquidationState := range mapPrices { price := new(big.Int).SetBytes(priceHash[:]) log.Debug("GetAllLowerLiquidationPriceData", "price", price, "limit", limit) if liquidationState != nil && price.Sign() > 0 && price.Cmp(limit) < 0 { liquidationData := map[common.Hash][]common.Hash{} - priceLiquidationData := liquidationState.getAllLiquidationData(self.db) + priceLiquidationData := liquidationState.getAllLiquidationData(t.db) for lendingBook, data := range priceLiquidationData { if len(data) == 0 { continue @@ -641,16 +643,16 @@ func (self *TradingStateDB) GetAllLowerLiquidationPriceData(orderBook common.Has return result } -func (self *TradingStateDB) GetHighestLiquidationPriceData(orderBook common.Hash, price *big.Int) (*big.Int, map[common.Hash][]common.Hash) { +func (t *TradingStateDB) GetHighestLiquidationPriceData(orderBook common.Hash, price *big.Int) (*big.Int, map[common.Hash][]common.Hash) { liquidationData := map[common.Hash][]common.Hash{} - orderbookState := self.getStateExchangeObject(orderBook) + orderbookState := t.getStateExchangeObject(orderBook) if orderbookState == nil { return common.Big0, liquidationData } - highestPriceHash, liquidationState := orderbookState.getHighestLiquidationPrice(self.db) + highestPriceHash, liquidationState := orderbookState.getHighestLiquidationPrice(t.db) highestPrice := new(big.Int).SetBytes(highestPriceHash[:]) if liquidationState != nil && highestPrice.Sign() > 0 && price.Cmp(highestPrice) < 0 { - priceLiquidationData := liquidationState.getAllLiquidationData(self.db) + priceLiquidationData := liquidationState.getAllLiquidationData(t.db) for lendingBook, data := range priceLiquidationData { if len(data) == 0 { continue @@ -667,26 +669,26 @@ func (self *TradingStateDB) GetHighestLiquidationPriceData(orderBook common.Hash return highestPrice, liquidationData } -func (self *TradingStateDB) InsertLiquidationPrice(orderBook common.Hash, price *big.Int, lendingBook common.Hash, tradeId uint64) { +func (t *TradingStateDB) InsertLiquidationPrice(orderBook common.Hash, price *big.Int, lendingBook common.Hash, tradeId uint64) { tradIdHash := common.Uint64ToHash(tradeId) priceHash := common.BigToHash(price) - orderBookState := self.getStateExchangeObject(orderBook) + orderBookState := t.getStateExchangeObject(orderBook) if orderBookState == nil { - orderBookState = self.createExchangeObject(orderBook) + orderBookState = t.createExchangeObject(orderBook) } - liquidationPriceState := orderBookState.getStateLiquidationPrice(self.db, priceHash) + liquidationPriceState := orderBookState.getStateLiquidationPrice(t.db, priceHash) if liquidationPriceState == nil { - liquidationPriceState = orderBookState.createStateLiquidationPrice(self.db, priceHash) + liquidationPriceState = orderBookState.createStateLiquidationPrice(t.db, priceHash) } - lendingBookState := liquidationPriceState.getStateLendingBook(self.db, lendingBook) + lendingBookState := liquidationPriceState.getStateLendingBook(t.db, lendingBook) if lendingBookState == nil { - lendingBookState = liquidationPriceState.createLendingBook(self.db, lendingBook) + lendingBookState = liquidationPriceState.createLendingBook(t.db, lendingBook) } - lendingBookState.insertTradingId(self.db, tradIdHash) + lendingBookState.insertTradingId(t.db, tradIdHash) lendingBookState.AddVolume(One) liquidationPriceState.AddVolume(One) orderBookState.addLendingCount(One) - self.journal = append(self.journal, insertLiquidationPrice{ + t.journal = append(t.journal, insertLiquidationPrice{ orderBook: orderBook, price: price, lendingBook: lendingBook, @@ -694,35 +696,35 @@ func (self *TradingStateDB) InsertLiquidationPrice(orderBook common.Hash, price }) } -func (self *TradingStateDB) RemoveLiquidationPrice(orderBook common.Hash, price *big.Int, lendingBook common.Hash, tradeId uint64) error { +func (t *TradingStateDB) RemoveLiquidationPrice(orderBook common.Hash, price *big.Int, lendingBook common.Hash, tradeId uint64) error { tradeIdHash := common.Uint64ToHash(tradeId) priceHash := common.BigToHash(price) - orderbookState := self.getStateExchangeObject(orderBook) + orderbookState := t.getStateExchangeObject(orderBook) if orderbookState == nil { return fmt.Errorf("not found order book: %s", orderBook.Hex()) } - liquidationPriceState := orderbookState.getStateLiquidationPrice(self.db, priceHash) + liquidationPriceState := orderbookState.getStateLiquidationPrice(t.db, priceHash) if liquidationPriceState == nil { return fmt.Errorf("not found liquidation price: %s , %s", orderBook.Hex(), priceHash.Hex()) } - lendingBookState := liquidationPriceState.getStateLendingBook(self.db, lendingBook) + lendingBookState := liquidationPriceState.getStateLendingBook(t.db, lendingBook) if lendingBookState == nil { return fmt.Errorf("not found lending book: %s , %s ,%s", orderBook.Hex(), priceHash.Hex(), lendingBook.Hex()) } - if !lendingBookState.Exist(self.db, tradeIdHash) { - return fmt.Errorf("not found trade id: %s , %s ,%s , %d ", orderBook.Hex(), priceHash.Hex(), lendingBook.Hex(), tradeId) + if !lendingBookState.Exist(t.db, tradeIdHash) { + return fmt.Errorf("not found trade id: %s, %s ,%s , %d", orderBook.Hex(), priceHash.Hex(), lendingBook.Hex(), tradeId) } - lendingBookState.removeTradingId(self.db, tradeIdHash) + lendingBookState.removeTradingId(t.db, tradeIdHash) lendingBookState.subVolume(One) liquidationPriceState.subVolume(One) if liquidationPriceState.Volume().Sign() == 0 { - err := orderbookState.getLiquidationPriceTrie(self.db).TryDelete(priceHash[:]) + err := orderbookState.getLiquidationPriceTrie(t.db).TryDelete(priceHash[:]) if err != nil { log.Warn("RemoveLiquidationPrice getLiquidationPriceTrie.TryDelete", "err", err, "priceHash", priceHash[:]) } } orderbookState.subLendingCount(One) - self.journal = append(self.journal, removeLiquidationPrice{ + t.journal = append(t.journal, removeLiquidationPrice{ orderBook: orderBook, price: price, lendingBook: lendingBook, diff --git a/XDCxlending/lendingstate/dump.go b/XDCxlending/lendingstate/dump.go index 2b26d43099d3..f3fe43742e48 100644 --- a/XDCxlending/lendingstate/dump.go +++ b/XDCxlending/lendingstate/dump.go @@ -39,13 +39,13 @@ type DumpOrderBookInfo struct { LowestLiquidationTime *big.Int } -func (self *LendingStateDB) DumpInvestingTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) { - exhangeObject := self.getLendingExchange(orderBook) +func (ls *LendingStateDB) DumpInvestingTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) { + exhangeObject := ls.getLendingExchange(orderBook) if exhangeObject == nil { return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]DumpOrderList{} - it := trie.NewIterator(exhangeObject.getInvestingTrie(self.db).NodeIterator(nil)) + it := trie.NewIterator(exhangeObject.getInvestingTrie(ls.db).NodeIterator(nil)) for it.Next() { interestHash := common.BytesToHash(it.Key) if common.EmptyHash(interestHash) { @@ -60,12 +60,12 @@ func (self *LendingStateDB) DumpInvestingTrie(orderBook common.Hash) (map[*big.I return nil, fmt.Errorf("fail when decode order iist orderBook: %v , interest : %v", orderBook.Hex(), interest) } stateOrderList := newItemListState(orderBook, interestHash, data, nil) - mapResult[interest] = stateOrderList.DumpItemList(self.db) + mapResult[interest] = stateOrderList.DumpItemList(ls.db) } } for interestHash, itemList := range exhangeObject.investingStates { if itemList.Volume().Sign() > 0 { - mapResult[new(big.Int).SetBytes(interestHash.Bytes())] = itemList.DumpItemList(self.db) + mapResult[new(big.Int).SetBytes(interestHash.Bytes())] = itemList.DumpItemList(ls.db) } } listInterest := []*big.Int{} @@ -82,13 +82,13 @@ func (self *LendingStateDB) DumpInvestingTrie(orderBook common.Hash) (map[*big.I return result, nil } -func (self *LendingStateDB) DumpBorrowingTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) { - exhangeObject := self.getLendingExchange(orderBook) +func (ls *LendingStateDB) DumpBorrowingTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) { + exhangeObject := ls.getLendingExchange(orderBook) if exhangeObject == nil { return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]DumpOrderList{} - it := trie.NewIterator(exhangeObject.getBorrowingTrie(self.db).NodeIterator(nil)) + it := trie.NewIterator(exhangeObject.getBorrowingTrie(ls.db).NodeIterator(nil)) for it.Next() { interestHash := common.BytesToHash(it.Key) if common.EmptyHash(interestHash) { @@ -103,12 +103,12 @@ func (self *LendingStateDB) DumpBorrowingTrie(orderBook common.Hash) (map[*big.I return nil, fmt.Errorf("fail when decode order iist orderBook: %v , interest : %v", orderBook.Hex(), interest) } stateOrderList := newItemListState(orderBook, interestHash, data, nil) - mapResult[interest] = stateOrderList.DumpItemList(self.db) + mapResult[interest] = stateOrderList.DumpItemList(ls.db) } } for interestHash, itemList := range exhangeObject.borrowingStates { if itemList.Volume().Sign() > 0 { - mapResult[new(big.Int).SetBytes(interestHash.Bytes())] = itemList.DumpItemList(self.db) + mapResult[new(big.Int).SetBytes(interestHash.Bytes())] = itemList.DumpItemList(ls.db) } } listInterest := []*big.Int{} @@ -125,13 +125,13 @@ func (self *LendingStateDB) DumpBorrowingTrie(orderBook common.Hash) (map[*big.I return result, nil } -func (self *LendingStateDB) GetInvestings(orderBook common.Hash) (map[*big.Int]*big.Int, error) { - exhangeObject := self.getLendingExchange(orderBook) +func (ls *LendingStateDB) GetInvestings(orderBook common.Hash) (map[*big.Int]*big.Int, error) { + exhangeObject := ls.getLendingExchange(orderBook) if exhangeObject == nil { return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]*big.Int{} - it := trie.NewIterator(exhangeObject.getInvestingTrie(self.db).NodeIterator(nil)) + it := trie.NewIterator(exhangeObject.getInvestingTrie(ls.db).NodeIterator(nil)) for it.Next() { interestHash := common.BytesToHash(it.Key) if common.EmptyHash(interestHash) { @@ -168,13 +168,13 @@ func (self *LendingStateDB) GetInvestings(orderBook common.Hash) (map[*big.Int]* return result, nil } -func (self *LendingStateDB) GetBorrowings(orderBook common.Hash) (map[*big.Int]*big.Int, error) { - exhangeObject := self.getLendingExchange(orderBook) +func (ls *LendingStateDB) GetBorrowings(orderBook common.Hash) (map[*big.Int]*big.Int, error) { + exhangeObject := ls.getLendingExchange(orderBook) if exhangeObject == nil { return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]*big.Int{} - it := trie.NewIterator(exhangeObject.getBorrowingTrie(self.db).NodeIterator(nil)) + it := trie.NewIterator(exhangeObject.getBorrowingTrie(ls.db).NodeIterator(nil)) for it.Next() { interestHash := common.BytesToHash(it.Key) if common.EmptyHash(interestHash) { @@ -211,22 +211,22 @@ func (self *LendingStateDB) GetBorrowings(orderBook common.Hash) (map[*big.Int]* return result, nil } -func (self *itemListState) DumpItemList(db Database) DumpOrderList { - mapResult := DumpOrderList{Volume: self.Volume(), Orders: map[*big.Int]*big.Int{}} - orderListIt := trie.NewIterator(self.getTrie(db).NodeIterator(nil)) +func (il *itemListState) DumpItemList(db Database) DumpOrderList { + mapResult := DumpOrderList{Volume: il.Volume(), Orders: map[*big.Int]*big.Int{}} + orderListIt := trie.NewIterator(il.getTrie(db).NodeIterator(nil)) for orderListIt.Next() { keyHash := common.BytesToHash(orderListIt.Key) if common.EmptyHash(keyHash) { continue } - if _, exist := self.cachedStorage[keyHash]; exist { + if _, exist := il.cachedStorage[keyHash]; exist { continue } else { _, content, _, _ := rlp.Split(orderListIt.Value) mapResult.Orders[new(big.Int).SetBytes(keyHash.Bytes())] = new(big.Int).SetBytes(content) } } - for key, value := range self.cachedStorage { + for key, value := range il.cachedStorage { if !common.EmptyHash(value) { mapResult.Orders[new(big.Int).SetBytes(key.Bytes())] = new(big.Int).SetBytes(value.Bytes()) } @@ -238,44 +238,44 @@ func (self *itemListState) DumpItemList(db Database) DumpOrderList { sort.Slice(listIds, func(i, j int) bool { return listIds[i].Cmp(listIds[j]) < 0 }) - result := DumpOrderList{Volume: self.Volume(), Orders: map[*big.Int]*big.Int{}} + result := DumpOrderList{Volume: il.Volume(), Orders: map[*big.Int]*big.Int{}} for _, id := range listIds { result.Orders[id] = mapResult.Orders[id] } return result } -func (self *LendingStateDB) DumpOrderBookInfo(orderBook common.Hash) (*DumpOrderBookInfo, error) { - exhangeObject := self.getLendingExchange(orderBook) +func (ls *LendingStateDB) DumpOrderBookInfo(orderBook common.Hash) (*DumpOrderBookInfo, error) { + exhangeObject := ls.getLendingExchange(orderBook) if exhangeObject == nil { return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } result := &DumpOrderBookInfo{} result.Nonce = exhangeObject.data.Nonce result.TradeNonce = exhangeObject.data.TradeNonce - result.BestInvesting = new(big.Int).SetBytes(exhangeObject.getBestInvestingInterest(self.db).Bytes()) - result.BestBorrowing = new(big.Int).SetBytes(exhangeObject.getBestBorrowingInterest(self.db).Bytes()) - lowestLiquidationTime, _ := exhangeObject.getLowestLiquidationTime(self.db) + result.BestInvesting = new(big.Int).SetBytes(exhangeObject.getBestInvestingInterest(ls.db).Bytes()) + result.BestBorrowing = new(big.Int).SetBytes(exhangeObject.getBestBorrowingInterest(ls.db).Bytes()) + lowestLiquidationTime, _ := exhangeObject.getLowestLiquidationTime(ls.db) result.LowestLiquidationTime = new(big.Int).SetBytes(lowestLiquidationTime.Bytes()) return result, nil } -func (self *liquidationTimeState) DumpItemList(db Database) DumpOrderList { - mapResult := DumpOrderList{Volume: self.Volume(), Orders: map[*big.Int]*big.Int{}} - orderListIt := trie.NewIterator(self.getTrie(db).NodeIterator(nil)) +func (lts *liquidationTimeState) DumpItemList(db Database) DumpOrderList { + mapResult := DumpOrderList{Volume: lts.Volume(), Orders: map[*big.Int]*big.Int{}} + orderListIt := trie.NewIterator(lts.getTrie(db).NodeIterator(nil)) for orderListIt.Next() { keyHash := common.BytesToHash(orderListIt.Key) if common.EmptyHash(keyHash) { continue } - if _, exist := self.cachedStorage[keyHash]; exist { + if _, exist := lts.cachedStorage[keyHash]; exist { continue } else { _, content, _, _ := rlp.Split(orderListIt.Value) mapResult.Orders[new(big.Int).SetBytes(keyHash.Bytes())] = new(big.Int).SetBytes(content) } } - for key, value := range self.cachedStorage { + for key, value := range lts.cachedStorage { if !common.EmptyHash(value) { mapResult.Orders[new(big.Int).SetBytes(key.Bytes())] = new(big.Int).SetBytes(value.Bytes()) } @@ -287,19 +287,20 @@ func (self *liquidationTimeState) DumpItemList(db Database) DumpOrderList { sort.Slice(listIds, func(i, j int) bool { return listIds[i].Cmp(listIds[j]) < 0 }) - result := DumpOrderList{Volume: self.Volume(), Orders: map[*big.Int]*big.Int{}} + result := DumpOrderList{Volume: lts.Volume(), Orders: map[*big.Int]*big.Int{}} for _, id := range listIds { result.Orders[id] = mapResult.Orders[id] } return mapResult } -func (self *LendingStateDB) DumpLiquidationTimeTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) { - exhangeObject := self.getLendingExchange(orderBook) + +func (ls *LendingStateDB) DumpLiquidationTimeTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) { + exhangeObject := ls.getLendingExchange(orderBook) if exhangeObject == nil { return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]DumpOrderList{} - it := trie.NewIterator(exhangeObject.getLiquidationTimeTrie(self.db).NodeIterator(nil)) + it := trie.NewIterator(exhangeObject.getLiquidationTimeTrie(ls.db).NodeIterator(nil)) for it.Next() { unixTimeHash := common.BytesToHash(it.Key) if common.EmptyHash(unixTimeHash) { @@ -314,12 +315,12 @@ func (self *LendingStateDB) DumpLiquidationTimeTrie(orderBook common.Hash) (map[ return nil, fmt.Errorf("fail when decode order iist orderBook: %v , unixTime : %v", orderBook.Hex(), unixTime) } stateOrderList := newLiquidationTimeState(orderBook, unixTimeHash, data, nil) - mapResult[unixTime] = stateOrderList.DumpItemList(self.db) + mapResult[unixTime] = stateOrderList.DumpItemList(ls.db) } } for unixTimeHash, itemList := range exhangeObject.liquidationTimeStates { if itemList.Volume().Sign() > 0 { - mapResult[new(big.Int).SetBytes(unixTimeHash.Bytes())] = itemList.DumpItemList(self.db) + mapResult[new(big.Int).SetBytes(unixTimeHash.Bytes())] = itemList.DumpItemList(ls.db) } } listUnixTime := []*big.Int{} @@ -336,13 +337,13 @@ func (self *LendingStateDB) DumpLiquidationTimeTrie(orderBook common.Hash) (map[ return result, nil } -func (self *LendingStateDB) DumpLendingOrderTrie(orderBook common.Hash) (map[*big.Int]LendingItem, error) { - exhangeObject := self.getLendingExchange(orderBook) +func (ls *LendingStateDB) DumpLendingOrderTrie(orderBook common.Hash) (map[*big.Int]LendingItem, error) { + exhangeObject := ls.getLendingExchange(orderBook) if exhangeObject == nil { return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]LendingItem{} - it := trie.NewIterator(exhangeObject.getLendingItemTrie(self.db).NodeIterator(nil)) + it := trie.NewIterator(exhangeObject.getLendingItemTrie(ls.db).NodeIterator(nil)) for it.Next() { orderIdHash := common.BytesToHash(it.Key) if common.EmptyHash(orderIdHash) { @@ -376,13 +377,13 @@ func (self *LendingStateDB) DumpLendingOrderTrie(orderBook common.Hash) (map[*bi return result, nil } -func (self *LendingStateDB) DumpLendingTradeTrie(orderBook common.Hash) (map[*big.Int]LendingTrade, error) { - exhangeObject := self.getLendingExchange(orderBook) +func (ls *LendingStateDB) DumpLendingTradeTrie(orderBook common.Hash) (map[*big.Int]LendingTrade, error) { + exhangeObject := ls.getLendingExchange(orderBook) if exhangeObject == nil { return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex()) } mapResult := map[*big.Int]LendingTrade{} - it := trie.NewIterator(exhangeObject.getLendingTradeTrie(self.db).NodeIterator(nil)) + it := trie.NewIterator(exhangeObject.getLendingTradeTrie(ls.db).NodeIterator(nil)) for it.Next() { tradeIdHash := common.BytesToHash(it.Key) if common.EmptyHash(tradeIdHash) { diff --git a/XDCxlending/lendingstate/state_itemList.go b/XDCxlending/lendingstate/state_itemList.go index ad48c8c857ec..7737e5b159a6 100644 --- a/XDCxlending/lendingstate/state_itemList.go +++ b/XDCxlending/lendingstate/state_itemList.go @@ -19,10 +19,11 @@ package lendingstate import ( "bytes" "fmt" - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/rlp" "io" "math/big" + + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/rlp" ) type itemListState struct { @@ -46,8 +47,8 @@ type itemListState struct { onDirty func(price common.Hash) // Callback method to mark a state object newly dirty } -func (s *itemListState) empty() bool { - return s.data.Volume == nil || s.data.Volume.Sign() == 0 +func (il *itemListState) empty() bool { + return il.data.Volume == nil || il.data.Volume.Sign() == 0 } func newItemListState(lendingBook common.Hash, key common.Hash, data itemList, onDirty func(price common.Hash)) *itemListState { @@ -62,132 +63,132 @@ func newItemListState(lendingBook common.Hash, key common.Hash, data itemList, o } // EncodeRLP implements rlp.Encoder. -func (c *itemListState) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, c.data) +func (il *itemListState) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, il.data) } // setError remembers the first non-nil error it is called with. -func (self *itemListState) setError(err error) { - if self.dbErr == nil { - self.dbErr = err +func (il *itemListState) setError(err error) { + if il.dbErr == nil { + il.dbErr = err } } -func (c *itemListState) getTrie(db Database) Trie { - if c.trie == nil { +func (il *itemListState) getTrie(db Database) Trie { + if il.trie == nil { var err error - c.trie, err = db.OpenStorageTrie(c.key, c.data.Root) + il.trie, err = db.OpenStorageTrie(il.key, il.data.Root) if err != nil { - c.trie, _ = db.OpenStorageTrie(c.key, EmptyHash) - c.setError(fmt.Errorf("can't create storage trie: %v", err)) + il.trie, _ = db.OpenStorageTrie(il.key, EmptyHash) + il.setError(fmt.Errorf("can't create storage trie: %v", err)) } } - return c.trie + return il.trie } -func (self *itemListState) GetOrderAmount(db Database, orderId common.Hash) common.Hash { - amount, exists := self.cachedStorage[orderId] +func (il *itemListState) GetOrderAmount(db Database, orderId common.Hash) common.Hash { + amount, exists := il.cachedStorage[orderId] if exists { return amount } // Load from DB in case it is missing. - enc, err := self.getTrie(db).TryGet(orderId[:]) + enc, err := il.getTrie(db).TryGet(orderId[:]) if err != nil { - self.setError(err) + il.setError(err) return EmptyHash } if len(enc) > 0 { _, content, _, err := rlp.Split(enc) if err != nil { - self.setError(err) + il.setError(err) } amount.SetBytes(content) } if (amount != common.Hash{}) { - self.cachedStorage[orderId] = amount + il.cachedStorage[orderId] = amount } return amount } -func (self *itemListState) insertLendingItem(db Database, orderId common.Hash, amount common.Hash) { - self.setOrderItem(orderId, amount) - self.setError(self.getTrie(db).TryUpdate(orderId[:], amount[:])) +func (il *itemListState) insertLendingItem(db Database, orderId common.Hash, amount common.Hash) { + il.setOrderItem(orderId, amount) + il.setError(il.getTrie(db).TryUpdate(orderId[:], amount[:])) } -func (self *itemListState) removeOrderItem(db Database, orderId common.Hash) { - tr := self.getTrie(db) - self.setError(tr.TryDelete(orderId[:])) - self.setOrderItem(orderId, EmptyHash) +func (il *itemListState) removeOrderItem(db Database, orderId common.Hash) { + tr := il.getTrie(db) + il.setError(tr.TryDelete(orderId[:])) + il.setOrderItem(orderId, EmptyHash) } -func (self *itemListState) setOrderItem(orderId common.Hash, amount common.Hash) { - self.cachedStorage[orderId] = amount - self.dirtyStorage[orderId] = amount +func (il *itemListState) setOrderItem(orderId common.Hash, amount common.Hash) { + il.cachedStorage[orderId] = amount + il.dirtyStorage[orderId] = amount - if self.onDirty != nil { - self.onDirty(self.key) - self.onDirty = nil + if il.onDirty != nil { + il.onDirty(il.key) + il.onDirty = nil } } // updateAskTrie writes cached storage modifications into the object's storage trie. -func (self *itemListState) updateTrie(db Database) Trie { - tr := self.getTrie(db) - for orderId, amount := range self.dirtyStorage { - delete(self.dirtyStorage, orderId) +func (il *itemListState) updateTrie(db Database) Trie { + tr := il.getTrie(db) + for orderId, amount := range il.dirtyStorage { + delete(il.dirtyStorage, orderId) if amount == EmptyHash { - self.setError(tr.TryDelete(orderId[:])) + il.setError(tr.TryDelete(orderId[:])) continue } v, _ := rlp.EncodeToBytes(bytes.TrimLeft(amount[:], "\x00")) - self.setError(tr.TryUpdate(orderId[:], v)) + il.setError(tr.TryUpdate(orderId[:], v)) } return tr } // UpdateRoot sets the trie root to the current root tradeId of -func (self *itemListState) updateRoot(db Database) error { - self.updateTrie(db) - if self.dbErr != nil { - return self.dbErr +func (il *itemListState) updateRoot(db Database) error { + il.updateTrie(db) + if il.dbErr != nil { + return il.dbErr } - root, err := self.trie.Commit(nil) + root, err := il.trie.Commit(nil) if err == nil { - self.data.Root = root + il.data.Root = root } return err } -func (self *itemListState) deepCopy(db *LendingStateDB, onDirty func(price common.Hash)) *itemListState { - stateOrderList := newItemListState(self.lendingBook, self.key, self.data, onDirty) - if self.trie != nil { - stateOrderList.trie = db.db.CopyTrie(self.trie) +func (il *itemListState) deepCopy(db *LendingStateDB, onDirty func(price common.Hash)) *itemListState { + stateOrderList := newItemListState(il.lendingBook, il.key, il.data, onDirty) + if il.trie != nil { + stateOrderList.trie = db.db.CopyTrie(il.trie) } - for orderId, amount := range self.dirtyStorage { + for orderId, amount := range il.dirtyStorage { stateOrderList.dirtyStorage[orderId] = amount } - for orderId, amount := range self.cachedStorage { + for orderId, amount := range il.cachedStorage { stateOrderList.cachedStorage[orderId] = amount } return stateOrderList } -func (c *itemListState) AddVolume(amount *big.Int) { - c.setVolume(new(big.Int).Add(c.data.Volume, amount)) +func (il *itemListState) AddVolume(amount *big.Int) { + il.setVolume(new(big.Int).Add(il.data.Volume, amount)) } -func (c *itemListState) subVolume(amount *big.Int) { - c.setVolume(new(big.Int).Sub(c.data.Volume, amount)) +func (il *itemListState) subVolume(amount *big.Int) { + il.setVolume(new(big.Int).Sub(il.data.Volume, amount)) } -func (self *itemListState) setVolume(volume *big.Int) { - self.data.Volume = volume - if self.onDirty != nil { - self.onDirty(self.key) - self.onDirty = nil +func (il *itemListState) setVolume(volume *big.Int) { + il.data.Volume = volume + if il.onDirty != nil { + il.onDirty(il.key) + il.onDirty = nil } } -func (self *itemListState) Volume() *big.Int { - return self.data.Volume +func (il *itemListState) Volume() *big.Int { + return il.data.Volume } diff --git a/XDCxlending/lendingstate/state_lendingbook.go b/XDCxlending/lendingstate/state_lendingbook.go index c063f23f7fb5..1cc3fda0778a 100644 --- a/XDCxlending/lendingstate/state_lendingbook.go +++ b/XDCxlending/lendingstate/state_lendingbook.go @@ -108,14 +108,14 @@ func newStateExchanges(db *LendingStateDB, hash common.Hash, data lendingObject, } // EncodeRLP implements rlp.Encoder. -func (self *lendingExchangeState) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, self.data) +func (le *lendingExchangeState) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, le.data) } // setError remembers the first non-nil error it is called with. -func (self *lendingExchangeState) setError(err error) { - if self.dbErr == nil { - self.dbErr = err +func (le *lendingExchangeState) setError(err error) { + if le.dbErr == nil { + le.dbErr = err } } @@ -123,63 +123,64 @@ func (self *lendingExchangeState) setError(err error) { Get Trie */ -func (self *lendingExchangeState) getLendingItemTrie(db Database) Trie { - if self.lendingItemTrie == nil { +func (le *lendingExchangeState) getLendingItemTrie(db Database) Trie { + if le.lendingItemTrie == nil { var err error - self.lendingItemTrie, err = db.OpenStorageTrie(self.lendingBook, self.data.LendingItemRoot) + le.lendingItemTrie, err = db.OpenStorageTrie(le.lendingBook, le.data.LendingItemRoot) if err != nil { - self.lendingItemTrie, _ = db.OpenStorageTrie(self.lendingBook, EmptyHash) - self.setError(fmt.Errorf("can't create Lendings trie: %v", err)) + le.lendingItemTrie, _ = db.OpenStorageTrie(le.lendingBook, EmptyHash) + le.setError(fmt.Errorf("can't create Lendings trie: %v", err)) } } - return self.lendingItemTrie + return le.lendingItemTrie } -func (self *lendingExchangeState) getLendingTradeTrie(db Database) Trie { - if self.lendingTradeTrie == nil { +func (le *lendingExchangeState) getLendingTradeTrie(db Database) Trie { + if le.lendingTradeTrie == nil { var err error - self.lendingTradeTrie, err = db.OpenStorageTrie(self.lendingBook, self.data.LendingTradeRoot) + le.lendingTradeTrie, err = db.OpenStorageTrie(le.lendingBook, le.data.LendingTradeRoot) if err != nil { - self.lendingTradeTrie, _ = db.OpenStorageTrie(self.lendingBook, EmptyHash) - self.setError(fmt.Errorf("can't create Lendings trie: %v", err)) + le.lendingTradeTrie, _ = db.OpenStorageTrie(le.lendingBook, EmptyHash) + le.setError(fmt.Errorf("can't create Lendings trie: %v", err)) } } - return self.lendingTradeTrie + return le.lendingTradeTrie } -func (self *lendingExchangeState) getInvestingTrie(db Database) Trie { - if self.investingTrie == nil { + +func (le *lendingExchangeState) getInvestingTrie(db Database) Trie { + if le.investingTrie == nil { var err error - self.investingTrie, err = db.OpenStorageTrie(self.lendingBook, self.data.InvestingRoot) + le.investingTrie, err = db.OpenStorageTrie(le.lendingBook, le.data.InvestingRoot) if err != nil { - self.investingTrie, _ = db.OpenStorageTrie(self.lendingBook, EmptyHash) - self.setError(fmt.Errorf("can't create Lendings trie: %v", err)) + le.investingTrie, _ = db.OpenStorageTrie(le.lendingBook, EmptyHash) + le.setError(fmt.Errorf("can't create Lendings trie: %v", err)) } } - return self.investingTrie + return le.investingTrie } -func (self *lendingExchangeState) getBorrowingTrie(db Database) Trie { - if self.borrowingTrie == nil { +func (le *lendingExchangeState) getBorrowingTrie(db Database) Trie { + if le.borrowingTrie == nil { var err error - self.borrowingTrie, err = db.OpenStorageTrie(self.lendingBook, self.data.BorrowingRoot) + le.borrowingTrie, err = db.OpenStorageTrie(le.lendingBook, le.data.BorrowingRoot) if err != nil { - self.borrowingTrie, _ = db.OpenStorageTrie(self.lendingBook, EmptyHash) - self.setError(fmt.Errorf("can't create bids trie: %v", err)) + le.borrowingTrie, _ = db.OpenStorageTrie(le.lendingBook, EmptyHash) + le.setError(fmt.Errorf("can't create bids trie: %v", err)) } } - return self.borrowingTrie + return le.borrowingTrie } -func (self *lendingExchangeState) getLiquidationTimeTrie(db Database) Trie { - if self.liquidationTimeTrie == nil { +func (le *lendingExchangeState) getLiquidationTimeTrie(db Database) Trie { + if le.liquidationTimeTrie == nil { var err error - self.liquidationTimeTrie, err = db.OpenStorageTrie(self.lendingBook, self.data.LiquidationTimeRoot) + le.liquidationTimeTrie, err = db.OpenStorageTrie(le.lendingBook, le.data.LiquidationTimeRoot) if err != nil { - self.liquidationTimeTrie, _ = db.OpenStorageTrie(self.lendingBook, EmptyHash) - self.setError(fmt.Errorf("can't create bids trie: %v", err)) + le.liquidationTimeTrie, _ = db.OpenStorageTrie(le.lendingBook, EmptyHash) + le.setError(fmt.Errorf("can't create bids trie: %v", err)) } } - return self.liquidationTimeTrie + return le.liquidationTimeTrie } /* @@ -187,16 +188,16 @@ func (self *lendingExchangeState) getLiquidationTimeTrie(db Database) Trie { Get State */ -func (self *lendingExchangeState) getBorrowingOrderList(db Database, rate common.Hash) (stateOrderList *itemListState) { +func (le *lendingExchangeState) getBorrowingOrderList(db Database, rate common.Hash) (stateOrderList *itemListState) { // Prefer 'live' objects. - if obj := self.borrowingStates[rate]; obj != nil { + if obj := le.borrowingStates[rate]; obj != nil { return obj } // Load the object from the database. - enc, err := self.getBorrowingTrie(db).TryGet(rate[:]) + enc, err := le.getBorrowingTrie(db).TryGet(rate[:]) if len(enc) == 0 { - self.setError(err) + le.setError(err) return nil } var data itemList @@ -205,21 +206,21 @@ func (self *lendingExchangeState) getBorrowingOrderList(db Database, rate common return nil } // Insert into the live set. - obj := newItemListState(self.lendingBook, rate, data, self.MarkBorrowingDirty) - self.borrowingStates[rate] = obj + obj := newItemListState(le.lendingBook, rate, data, le.MarkBorrowingDirty) + le.borrowingStates[rate] = obj return obj } -func (self *lendingExchangeState) getInvestingOrderList(db Database, rate common.Hash) (stateOrderList *itemListState) { +func (le *lendingExchangeState) getInvestingOrderList(db Database, rate common.Hash) (stateOrderList *itemListState) { // Prefer 'live' objects. - if obj := self.investingStates[rate]; obj != nil { + if obj := le.investingStates[rate]; obj != nil { return obj } // Load the object from the database. - enc, err := self.getInvestingTrie(db).TryGet(rate[:]) + enc, err := le.getInvestingTrie(db).TryGet(rate[:]) if len(enc) == 0 { - self.setError(err) + le.setError(err) return nil } var data itemList @@ -228,21 +229,21 @@ func (self *lendingExchangeState) getInvestingOrderList(db Database, rate common return nil } // Insert into the live set. - obj := newItemListState(self.lendingBook, rate, data, self.MarkInvestingDirty) - self.investingStates[rate] = obj + obj := newItemListState(le.lendingBook, rate, data, le.MarkInvestingDirty) + le.investingStates[rate] = obj return obj } -func (self *lendingExchangeState) getLiquidationTimeOrderList(db Database, time common.Hash) (stateObject *liquidationTimeState) { +func (le *lendingExchangeState) getLiquidationTimeOrderList(db Database, time common.Hash) (stateObject *liquidationTimeState) { // Prefer 'live' objects. - if obj := self.liquidationTimeStates[time]; obj != nil { + if obj := le.liquidationTimeStates[time]; obj != nil { return obj } // Load the object from the database. - enc, err := self.getLiquidationTimeTrie(db).TryGet(time[:]) + enc, err := le.getLiquidationTimeTrie(db).TryGet(time[:]) if len(enc) == 0 { - self.setError(err) + le.setError(err) return nil } var data itemList @@ -251,21 +252,21 @@ func (self *lendingExchangeState) getLiquidationTimeOrderList(db Database, time return nil } // Insert into the live set. - obj := newLiquidationTimeState(self.lendingBook, time, data, self.MarkLiquidationTimeDirty) - self.liquidationTimeStates[time] = obj + obj := newLiquidationTimeState(le.lendingBook, time, data, le.MarkLiquidationTimeDirty) + le.liquidationTimeStates[time] = obj return obj } -func (self *lendingExchangeState) getLendingItem(db Database, lendingId common.Hash) (stateObject *lendingItemState) { +func (le *lendingExchangeState) getLendingItem(db Database, lendingId common.Hash) (stateObject *lendingItemState) { // Prefer 'live' objects. - if obj := self.lendingItemStates[lendingId]; obj != nil { + if obj := le.lendingItemStates[lendingId]; obj != nil { return obj } // Load the object from the database. - enc, err := self.getLendingItemTrie(db).TryGet(lendingId[:]) + enc, err := le.getLendingItemTrie(db).TryGet(lendingId[:]) if len(enc) == 0 { - self.setError(err) + le.setError(err) return nil } var data LendingItem @@ -274,21 +275,21 @@ func (self *lendingExchangeState) getLendingItem(db Database, lendingId common.H return nil } // Insert into the live set. - obj := newLendinItemState(self.lendingBook, lendingId, data, self.MarkLendingItemDirty) - self.lendingItemStates[lendingId] = obj + obj := newLendinItemState(le.lendingBook, lendingId, data, le.MarkLendingItemDirty) + le.lendingItemStates[lendingId] = obj return obj } -func (self *lendingExchangeState) getLendingTrade(db Database, tradeId common.Hash) (stateObject *lendingTradeState) { +func (le *lendingExchangeState) getLendingTrade(db Database, tradeId common.Hash) (stateObject *lendingTradeState) { // Prefer 'live' objects. - if obj := self.lendingTradeStates[tradeId]; obj != nil { + if obj := le.lendingTradeStates[tradeId]; obj != nil { return obj } // Load the object from the database. - enc, err := self.getLendingTradeTrie(db).TryGet(tradeId[:]) + enc, err := le.getLendingTradeTrie(db).TryGet(tradeId[:]) if len(enc) == 0 { - self.setError(err) + le.setError(err) return nil } var data LendingTrade @@ -297,8 +298,8 @@ func (self *lendingExchangeState) getLendingTrade(db Database, tradeId common.Ha return nil } // Insert into the live set. - obj := newLendingTradeState(self.lendingBook, tradeId, data, self.MarkLendingTradeDirty) - self.lendingTradeStates[tradeId] = obj + obj := newLendingTradeState(le.lendingBook, tradeId, data, le.MarkLendingTradeDirty) + le.lendingTradeStates[tradeId] = obj return obj } @@ -307,46 +308,47 @@ func (self *lendingExchangeState) getLendingTrade(db Database, tradeId common.Ha Update Trie */ -func (self *lendingExchangeState) updateLendingTimeTrie(db Database) Trie { - tr := self.getLendingItemTrie(db) - for lendingId, lendingItem := range self.lendingItemStates { - if _, isDirty := self.lendingItemStatesDirty[lendingId]; isDirty { - delete(self.lendingItemStatesDirty, lendingId) +func (le *lendingExchangeState) updateLendingTimeTrie(db Database) Trie { + tr := le.getLendingItemTrie(db) + for lendingId, lendingItem := range le.lendingItemStates { + if _, isDirty := le.lendingItemStatesDirty[lendingId]; isDirty { + delete(le.lendingItemStatesDirty, lendingId) if lendingItem.empty() { - self.setError(tr.TryDelete(lendingId[:])) + le.setError(tr.TryDelete(lendingId[:])) continue } // Encoding []byte cannot fail, ok to ignore the error. v, _ := rlp.EncodeToBytes(lendingItem) - self.setError(tr.TryUpdate(lendingId[:], v)) + le.setError(tr.TryUpdate(lendingId[:], v)) } } return tr } -func (self *lendingExchangeState) updateLendingTradeTrie(db Database) Trie { - tr := self.getLendingTradeTrie(db) - for tradeId, lendingTradeItem := range self.lendingTradeStates { - if _, isDirty := self.lendingTradeStatesDirty[tradeId]; isDirty { - delete(self.lendingTradeStatesDirty, tradeId) +func (le *lendingExchangeState) updateLendingTradeTrie(db Database) Trie { + tr := le.getLendingTradeTrie(db) + for tradeId, lendingTradeItem := range le.lendingTradeStates { + if _, isDirty := le.lendingTradeStatesDirty[tradeId]; isDirty { + delete(le.lendingTradeStatesDirty, tradeId) if lendingTradeItem.empty() { - self.setError(tr.TryDelete(tradeId[:])) + le.setError(tr.TryDelete(tradeId[:])) continue } // Encoding []byte cannot fail, ok to ignore the error. v, _ := rlp.EncodeToBytes(lendingTradeItem) - self.setError(tr.TryUpdate(tradeId[:], v)) + le.setError(tr.TryUpdate(tradeId[:], v)) } } return tr } -func (self *lendingExchangeState) updateBorrowingTrie(db Database) Trie { - tr := self.getBorrowingTrie(db) - for rate, orderList := range self.borrowingStates { - if _, isDirty := self.borrowingStatesDirty[rate]; isDirty { - delete(self.borrowingStatesDirty, rate) + +func (le *lendingExchangeState) updateBorrowingTrie(db Database) Trie { + tr := le.getBorrowingTrie(db) + for rate, orderList := range le.borrowingStates { + if _, isDirty := le.borrowingStatesDirty[rate]; isDirty { + delete(le.borrowingStatesDirty, rate) if orderList.empty() { - self.setError(tr.TryDelete(rate[:])) + le.setError(tr.TryDelete(rate[:])) continue } err := orderList.updateRoot(db) @@ -355,19 +357,19 @@ func (self *lendingExchangeState) updateBorrowingTrie(db Database) Trie { } // Encoding []byte cannot fail, ok to ignore the error. v, _ := rlp.EncodeToBytes(orderList) - self.setError(tr.TryUpdate(rate[:], v)) + le.setError(tr.TryUpdate(rate[:], v)) } } return tr } -func (self *lendingExchangeState) updateInvestingTrie(db Database) Trie { - tr := self.getInvestingTrie(db) - for rate, orderList := range self.investingStates { - if _, isDirty := self.investingStatesDirty[rate]; isDirty { - delete(self.investingStatesDirty, rate) +func (le *lendingExchangeState) updateInvestingTrie(db Database) Trie { + tr := le.getInvestingTrie(db) + for rate, orderList := range le.investingStates { + if _, isDirty := le.investingStatesDirty[rate]; isDirty { + delete(le.investingStatesDirty, rate) if orderList.empty() { - self.setError(tr.TryDelete(rate[:])) + le.setError(tr.TryDelete(rate[:])) continue } err := orderList.updateRoot(db) @@ -376,19 +378,19 @@ func (self *lendingExchangeState) updateInvestingTrie(db Database) Trie { } // Encoding []byte cannot fail, ok to ignore the error. v, _ := rlp.EncodeToBytes(orderList) - self.setError(tr.TryUpdate(rate[:], v)) + le.setError(tr.TryUpdate(rate[:], v)) } } return tr } -func (self *lendingExchangeState) updateLiquidationTimeTrie(db Database) Trie { - tr := self.getLiquidationTimeTrie(db) - for time, itemList := range self.liquidationTimeStates { - if _, isDirty := self.liquidationTimestatesDirty[time]; isDirty { - delete(self.liquidationTimestatesDirty, time) +func (le *lendingExchangeState) updateLiquidationTimeTrie(db Database) Trie { + tr := le.getLiquidationTimeTrie(db) + for time, itemList := range le.liquidationTimeStates { + if _, isDirty := le.liquidationTimestatesDirty[time]; isDirty { + delete(le.liquidationTimestatesDirty, time) if itemList.empty() { - self.setError(tr.TryDelete(time[:])) + le.setError(tr.TryDelete(time[:])) continue } err := itemList.updateRoot(db) @@ -397,7 +399,7 @@ func (self *lendingExchangeState) updateLiquidationTimeTrie(db Database) Trie { } // Encoding []byte cannot fail, ok to ignore the error. v, _ := rlp.EncodeToBytes(itemList) - self.setError(tr.TryUpdate(time[:], v)) + le.setError(tr.TryUpdate(time[:], v)) } } return tr @@ -407,69 +409,69 @@ func (self *lendingExchangeState) updateLiquidationTimeTrie(db Database) Trie { Update Root */ -func (self *lendingExchangeState) updateOrderRoot(db Database) { - self.updateLendingTimeTrie(db) - self.data.LendingItemRoot = self.lendingItemTrie.Hash() +func (le *lendingExchangeState) updateOrderRoot(db Database) { + le.updateLendingTimeTrie(db) + le.data.LendingItemRoot = le.lendingItemTrie.Hash() } -func (self *lendingExchangeState) updateInvestingRoot(db Database) error { - self.updateInvestingTrie(db) - if self.dbErr != nil { - return self.dbErr +func (le *lendingExchangeState) updateInvestingRoot(db Database) error { + le.updateInvestingTrie(db) + if le.dbErr != nil { + return le.dbErr } - self.data.InvestingRoot = self.investingTrie.Hash() + le.data.InvestingRoot = le.investingTrie.Hash() return nil } -func (self *lendingExchangeState) updateBorrowingRoot(db Database) { - self.updateBorrowingTrie(db) - self.data.BorrowingRoot = self.borrowingTrie.Hash() +func (le *lendingExchangeState) updateBorrowingRoot(db Database) { + le.updateBorrowingTrie(db) + le.data.BorrowingRoot = le.borrowingTrie.Hash() } -func (self *lendingExchangeState) updateLiquidationTimeRoot(db Database) { - self.updateLiquidationTimeTrie(db) - self.data.LiquidationTimeRoot = self.liquidationTimeTrie.Hash() +func (le *lendingExchangeState) updateLiquidationTimeRoot(db Database) { + le.updateLiquidationTimeTrie(db) + le.data.LiquidationTimeRoot = le.liquidationTimeTrie.Hash() } -func (self *lendingExchangeState) updateLendingTradeRoot(db Database) { - self.updateLendingTradeTrie(db) - self.data.LendingTradeRoot = self.lendingTradeTrie.Hash() +func (le *lendingExchangeState) updateLendingTradeRoot(db Database) { + le.updateLendingTradeTrie(db) + le.data.LendingTradeRoot = le.lendingTradeTrie.Hash() } /** Commit Trie */ -func (self *lendingExchangeState) CommitLendingItemTrie(db Database) error { - self.updateLendingTimeTrie(db) - if self.dbErr != nil { - return self.dbErr +func (le *lendingExchangeState) CommitLendingItemTrie(db Database) error { + le.updateLendingTimeTrie(db) + if le.dbErr != nil { + return le.dbErr } - root, err := self.lendingItemTrie.Commit(nil) + root, err := le.lendingItemTrie.Commit(nil) if err == nil { - self.data.LendingItemRoot = root + le.data.LendingItemRoot = root } return err } -func (self *lendingExchangeState) CommitLendingTradeTrie(db Database) error { - self.updateLendingTradeTrie(db) - if self.dbErr != nil { - return self.dbErr +func (le *lendingExchangeState) CommitLendingTradeTrie(db Database) error { + le.updateLendingTradeTrie(db) + if le.dbErr != nil { + return le.dbErr } - root, err := self.lendingTradeTrie.Commit(nil) + root, err := le.lendingTradeTrie.Commit(nil) if err == nil { - self.data.LendingTradeRoot = root + le.data.LendingTradeRoot = root } return err } -func (self *lendingExchangeState) CommitInvestingTrie(db Database) error { - self.updateInvestingTrie(db) - if self.dbErr != nil { - return self.dbErr +func (le *lendingExchangeState) CommitInvestingTrie(db Database) error { + le.updateInvestingTrie(db) + if le.dbErr != nil { + return le.dbErr } - root, err := self.investingTrie.Commit(func(leaf []byte, parent common.Hash) error { + root, err := le.investingTrie.Commit(func(leaf []byte, parent common.Hash) error { var orderList itemList if err := rlp.DecodeBytes(leaf, &orderList); err != nil { return nil @@ -480,17 +482,17 @@ func (self *lendingExchangeState) CommitInvestingTrie(db Database) error { return nil }) if err == nil { - self.data.InvestingRoot = root + le.data.InvestingRoot = root } return err } -func (self *lendingExchangeState) CommitBorrowingTrie(db Database) error { - self.updateBorrowingTrie(db) - if self.dbErr != nil { - return self.dbErr +func (le *lendingExchangeState) CommitBorrowingTrie(db Database) error { + le.updateBorrowingTrie(db) + if le.dbErr != nil { + return le.dbErr } - root, err := self.borrowingTrie.Commit(func(leaf []byte, parent common.Hash) error { + root, err := le.borrowingTrie.Commit(func(leaf []byte, parent common.Hash) error { var orderList itemList if err := rlp.DecodeBytes(leaf, &orderList); err != nil { return nil @@ -501,17 +503,17 @@ func (self *lendingExchangeState) CommitBorrowingTrie(db Database) error { return nil }) if err == nil { - self.data.BorrowingRoot = root + le.data.BorrowingRoot = root } return err } -func (self *lendingExchangeState) CommitLiquidationTimeTrie(db Database) error { - self.updateLiquidationTimeTrie(db) - if self.dbErr != nil { - return self.dbErr +func (le *lendingExchangeState) CommitLiquidationTimeTrie(db Database) error { + le.updateLiquidationTimeTrie(db) + if le.dbErr != nil { + return le.dbErr } - root, err := self.liquidationTimeTrie.Commit(func(leaf []byte, parent common.Hash) error { + root, err := le.liquidationTimeTrie.Commit(func(leaf []byte, parent common.Hash) error { var orderList itemList if err := rlp.DecodeBytes(leaf, &orderList); err != nil { return nil @@ -522,7 +524,7 @@ func (self *lendingExchangeState) CommitLiquidationTimeTrie(db Database) error { return nil }) if err == nil { - self.data.LiquidationTimeRoot = root + le.data.LiquidationTimeRoot = root } return err } @@ -532,11 +534,11 @@ func (self *lendingExchangeState) CommitLiquidationTimeTrie(db Database) error { Get Trie Data */ -func (self *lendingExchangeState) getBestInvestingInterest(db Database) common.Hash { - trie := self.getInvestingTrie(db) +func (le *lendingExchangeState) getBestInvestingInterest(db Database) common.Hash { + trie := le.getInvestingTrie(db) encKey, encValue, err := trie.TryGetBestLeftKeyAndValue() if err != nil { - log.Error("Failed find best investing rate", "orderbook", self.lendingBook.Hex()) + log.Error("Failed find best investing rate", "orderbook", le.lendingBook.Hex()) return EmptyHash } if len(encKey) == 0 || len(encValue) == 0 { @@ -545,23 +547,23 @@ func (self *lendingExchangeState) getBestInvestingInterest(db Database) common.H } // Insert into the live set. interest := common.BytesToHash(encKey) - if _, exist := self.investingStates[interest]; !exist { + if _, exist := le.investingStates[interest]; !exist { var data itemList if err := rlp.DecodeBytes(encValue, &data); err != nil { log.Error("Failed to decode state get best investing rate", "err", err) return EmptyHash } - obj := newItemListState(self.lendingBook, interest, data, self.MarkInvestingDirty) - self.investingStates[interest] = obj + obj := newItemListState(le.lendingBook, interest, data, le.MarkInvestingDirty) + le.investingStates[interest] = obj } return interest } -func (self *lendingExchangeState) getBestBorrowingInterest(db Database) common.Hash { - trie := self.getBorrowingTrie(db) +func (le *lendingExchangeState) getBestBorrowingInterest(db Database) common.Hash { + trie := le.getBorrowingTrie(db) encKey, encValue, err := trie.TryGetBestRightKeyAndValue() if err != nil { - log.Error("Failed find best key bid trie ", "orderbook", self.lendingBook.Hex()) + log.Error("Failed find best key bid trie ", "orderbook", le.lendingBook.Hex()) return EmptyHash } if len(encKey) == 0 || len(encValue) == 0 { @@ -570,23 +572,23 @@ func (self *lendingExchangeState) getBestBorrowingInterest(db Database) common.H } // Insert into the live set. interest := common.BytesToHash(encKey) - if _, exist := self.borrowingStates[interest]; !exist { + if _, exist := le.borrowingStates[interest]; !exist { var data itemList if err := rlp.DecodeBytes(encValue, &data); err != nil { log.Error("Failed to decode state get best bid trie", "err", err) return EmptyHash } - obj := newItemListState(self.lendingBook, interest, data, self.MarkBorrowingDirty) - self.borrowingStates[interest] = obj + obj := newItemListState(le.lendingBook, interest, data, le.MarkBorrowingDirty) + le.borrowingStates[interest] = obj } return interest } -func (self *lendingExchangeState) getLowestLiquidationTime(db Database) (common.Hash, *liquidationTimeState) { - trie := self.getLiquidationTimeTrie(db) +func (le *lendingExchangeState) getLowestLiquidationTime(db Database) (common.Hash, *liquidationTimeState) { + trie := le.getLiquidationTimeTrie(db) encKey, encValue, err := trie.TryGetBestLeftKeyAndValue() if err != nil { - log.Error("Failed find best liquidation time trie ", "orderBook", self.lendingBook.Hex()) + log.Error("Failed find best liquidation time trie ", "orderBook", le.lendingBook.Hex()) return EmptyHash, nil } if len(encKey) == 0 || len(encValue) == 0 { @@ -594,15 +596,15 @@ func (self *lendingExchangeState) getLowestLiquidationTime(db Database) (common. return EmptyHash, nil } price := common.BytesToHash(encKey) - obj, exist := self.liquidationTimeStates[price] + obj, exist := le.liquidationTimeStates[price] if !exist { var data itemList if err := rlp.DecodeBytes(encValue, &data); err != nil { log.Error("Failed to decode state get liquidation time trie", "err", err) return EmptyHash, nil } - obj = newLiquidationTimeState(self.lendingBook, price, data, self.MarkLiquidationTimeDirty) - self.liquidationTimeStates[price] = obj + obj = newLiquidationTimeState(le.lendingBook, price, data, le.MarkLiquidationTimeDirty) + le.liquidationTimeStates[price] = obj } if obj.empty() { return EmptyHash, nil @@ -610,193 +612,194 @@ func (self *lendingExchangeState) getLowestLiquidationTime(db Database) (common. return price, obj } -func (self *lendingExchangeState) deepCopy(db *LendingStateDB, onDirty func(hash common.Hash)) *lendingExchangeState { - stateExchanges := newStateExchanges(db, self.lendingBook, self.data, onDirty) - if self.investingTrie != nil { - stateExchanges.investingTrie = db.db.CopyTrie(self.investingTrie) +func (le *lendingExchangeState) deepCopy(db *LendingStateDB, onDirty func(hash common.Hash)) *lendingExchangeState { + stateExchanges := newStateExchanges(db, le.lendingBook, le.data, onDirty) + if le.investingTrie != nil { + stateExchanges.investingTrie = db.db.CopyTrie(le.investingTrie) } - if self.borrowingTrie != nil { - stateExchanges.borrowingTrie = db.db.CopyTrie(self.borrowingTrie) + if le.borrowingTrie != nil { + stateExchanges.borrowingTrie = db.db.CopyTrie(le.borrowingTrie) } - if self.lendingItemTrie != nil { - stateExchanges.lendingItemTrie = db.db.CopyTrie(self.lendingItemTrie) + if le.lendingItemTrie != nil { + stateExchanges.lendingItemTrie = db.db.CopyTrie(le.lendingItemTrie) } - for key, value := range self.borrowingStates { - stateExchanges.borrowingStates[key] = value.deepCopy(db, self.MarkBorrowingDirty) + for key, value := range le.borrowingStates { + stateExchanges.borrowingStates[key] = value.deepCopy(db, le.MarkBorrowingDirty) } - for key := range self.borrowingStatesDirty { + for key := range le.borrowingStatesDirty { stateExchanges.borrowingStatesDirty[key] = struct{}{} } - for key, value := range self.investingStates { - stateExchanges.investingStates[key] = value.deepCopy(db, self.MarkInvestingDirty) + for key, value := range le.investingStates { + stateExchanges.investingStates[key] = value.deepCopy(db, le.MarkInvestingDirty) } - for key := range self.investingStatesDirty { + for key := range le.investingStatesDirty { stateExchanges.investingStatesDirty[key] = struct{}{} } - for key, value := range self.lendingItemStates { - stateExchanges.lendingItemStates[key] = value.deepCopy(self.MarkLendingItemDirty) + for key, value := range le.lendingItemStates { + stateExchanges.lendingItemStates[key] = value.deepCopy(le.MarkLendingItemDirty) } - for orderId := range self.lendingItemStatesDirty { + for orderId := range le.lendingItemStatesDirty { stateExchanges.lendingItemStatesDirty[orderId] = struct{}{} } - for key, value := range self.lendingTradeStates { - stateExchanges.lendingTradeStates[key] = value.deepCopy(self.MarkLendingTradeDirty) + for key, value := range le.lendingTradeStates { + stateExchanges.lendingTradeStates[key] = value.deepCopy(le.MarkLendingTradeDirty) } - for orderId := range self.lendingTradeStatesDirty { + for orderId := range le.lendingTradeStatesDirty { stateExchanges.lendingTradeStatesDirty[orderId] = struct{}{} } - for time, orderList := range self.liquidationTimeStates { - stateExchanges.liquidationTimeStates[time] = orderList.deepCopy(db, self.MarkLiquidationTimeDirty) + for time, orderList := range le.liquidationTimeStates { + stateExchanges.liquidationTimeStates[time] = orderList.deepCopy(db, le.MarkLiquidationTimeDirty) } - for time := range self.liquidationTimestatesDirty { + for time := range le.liquidationTimestatesDirty { stateExchanges.liquidationTimestatesDirty[time] = struct{}{} } return stateExchanges } // Returns the address of the contract/tradeId -func (self *lendingExchangeState) Hash() common.Hash { - return self.lendingBook +func (le *lendingExchangeState) Hash() common.Hash { + return le.lendingBook } -func (self *lendingExchangeState) setNonce(nonce uint64) { - self.data.Nonce = nonce - if self.onDirty != nil { - self.onDirty(self.Hash()) - self.onDirty = nil +func (le *lendingExchangeState) setNonce(nonce uint64) { + le.data.Nonce = nonce + if le.onDirty != nil { + le.onDirty(le.Hash()) + le.onDirty = nil } } -func (self *lendingExchangeState) Nonce() uint64 { - return self.data.Nonce +func (le *lendingExchangeState) Nonce() uint64 { + return le.data.Nonce } -func (self *lendingExchangeState) setTradeNonce(nonce uint64) { - self.data.TradeNonce = nonce - if self.onDirty != nil { - self.onDirty(self.Hash()) - self.onDirty = nil +func (le *lendingExchangeState) setTradeNonce(nonce uint64) { + le.data.TradeNonce = nonce + if le.onDirty != nil { + le.onDirty(le.Hash()) + le.onDirty = nil } } -func (self *lendingExchangeState) TradeNonce() uint64 { - return self.data.TradeNonce + +func (le *lendingExchangeState) TradeNonce() uint64 { + return le.data.TradeNonce } -func (self *lendingExchangeState) removeInvestingOrderList(db Database, stateOrderList *itemListState) { - self.setError(self.investingTrie.TryDelete(stateOrderList.key[:])) +func (le *lendingExchangeState) removeInvestingOrderList(db Database, stateOrderList *itemListState) { + le.setError(le.investingTrie.TryDelete(stateOrderList.key[:])) } -func (self *lendingExchangeState) removeBorrowingOrderList(db Database, stateOrderList *itemListState) { - self.setError(self.borrowingTrie.TryDelete(stateOrderList.key[:])) +func (le *lendingExchangeState) removeBorrowingOrderList(db Database, stateOrderList *itemListState) { + le.setError(le.borrowingTrie.TryDelete(stateOrderList.key[:])) } -func (self *lendingExchangeState) createInvestingOrderList(db Database, price common.Hash) (newobj *itemListState) { - newobj = newItemListState(self.lendingBook, price, itemList{Volume: Zero}, self.MarkInvestingDirty) - self.investingStates[price] = newobj - self.investingStatesDirty[price] = struct{}{} +func (le *lendingExchangeState) createInvestingOrderList(db Database, price common.Hash) (newobj *itemListState) { + newobj = newItemListState(le.lendingBook, price, itemList{Volume: Zero}, le.MarkInvestingDirty) + le.investingStates[price] = newobj + le.investingStatesDirty[price] = struct{}{} data, err := rlp.EncodeToBytes(newobj) if err != nil { panic(fmt.Errorf("can't encode order list object at %x: %v", price[:], err)) } - self.setError(self.getInvestingTrie(db).TryUpdate(price[:], data)) - if self.onDirty != nil { - self.onDirty(self.Hash()) - self.onDirty = nil + le.setError(le.getInvestingTrie(db).TryUpdate(price[:], data)) + if le.onDirty != nil { + le.onDirty(le.Hash()) + le.onDirty = nil } return newobj } -func (self *lendingExchangeState) MarkBorrowingDirty(price common.Hash) { - self.borrowingStatesDirty[price] = struct{}{} - if self.onDirty != nil { - self.onDirty(self.Hash()) - self.onDirty = nil +func (le *lendingExchangeState) MarkBorrowingDirty(price common.Hash) { + le.borrowingStatesDirty[price] = struct{}{} + if le.onDirty != nil { + le.onDirty(le.Hash()) + le.onDirty = nil } } -func (self *lendingExchangeState) MarkInvestingDirty(price common.Hash) { - self.investingStatesDirty[price] = struct{}{} - if self.onDirty != nil { - self.onDirty(self.Hash()) - self.onDirty = nil +func (le *lendingExchangeState) MarkInvestingDirty(price common.Hash) { + le.investingStatesDirty[price] = struct{}{} + if le.onDirty != nil { + le.onDirty(le.Hash()) + le.onDirty = nil } } -func (self *lendingExchangeState) MarkLendingItemDirty(lending common.Hash) { - self.lendingItemStatesDirty[lending] = struct{}{} - if self.onDirty != nil { - self.onDirty(self.Hash()) - self.onDirty = nil +func (le *lendingExchangeState) MarkLendingItemDirty(lending common.Hash) { + le.lendingItemStatesDirty[lending] = struct{}{} + if le.onDirty != nil { + le.onDirty(le.Hash()) + le.onDirty = nil } } -func (self *lendingExchangeState) MarkLendingTradeDirty(tradeId common.Hash) { - self.lendingTradeStatesDirty[tradeId] = struct{}{} - if self.onDirty != nil { - self.onDirty(self.Hash()) - self.onDirty = nil +func (le *lendingExchangeState) MarkLendingTradeDirty(tradeId common.Hash) { + le.lendingTradeStatesDirty[tradeId] = struct{}{} + if le.onDirty != nil { + le.onDirty(le.Hash()) + le.onDirty = nil } } -func (self *lendingExchangeState) MarkLiquidationTimeDirty(orderId common.Hash) { - self.liquidationTimestatesDirty[orderId] = struct{}{} - if self.onDirty != nil { - self.onDirty(self.Hash()) - self.onDirty = nil +func (le *lendingExchangeState) MarkLiquidationTimeDirty(orderId common.Hash) { + le.liquidationTimestatesDirty[orderId] = struct{}{} + if le.onDirty != nil { + le.onDirty(le.Hash()) + le.onDirty = nil } } -func (self *lendingExchangeState) createBorrowingOrderList(db Database, price common.Hash) (newobj *itemListState) { - newobj = newItemListState(self.lendingBook, price, itemList{Volume: Zero}, self.MarkBorrowingDirty) - self.borrowingStates[price] = newobj - self.borrowingStatesDirty[price] = struct{}{} +func (le *lendingExchangeState) createBorrowingOrderList(db Database, price common.Hash) (newobj *itemListState) { + newobj = newItemListState(le.lendingBook, price, itemList{Volume: Zero}, le.MarkBorrowingDirty) + le.borrowingStates[price] = newobj + le.borrowingStatesDirty[price] = struct{}{} data, err := rlp.EncodeToBytes(newobj) if err != nil { panic(fmt.Errorf("can't encode order list object at %x: %v", price[:], err)) } - self.setError(self.getBorrowingTrie(db).TryUpdate(price[:], data)) - if self.onDirty != nil { - self.onDirty(self.Hash()) - self.onDirty = nil + le.setError(le.getBorrowingTrie(db).TryUpdate(price[:], data)) + if le.onDirty != nil { + le.onDirty(le.Hash()) + le.onDirty = nil } return newobj } -func (self *lendingExchangeState) createLendingItem(db Database, orderId common.Hash, order LendingItem) (newobj *lendingItemState) { - newobj = newLendinItemState(self.lendingBook, orderId, order, self.MarkLendingItemDirty) +func (le *lendingExchangeState) createLendingItem(db Database, orderId common.Hash, order LendingItem) (newobj *lendingItemState) { + newobj = newLendinItemState(le.lendingBook, orderId, order, le.MarkLendingItemDirty) orderIdHash := common.BigToHash(new(big.Int).SetUint64(order.LendingId)) - self.lendingItemStates[orderIdHash] = newobj - self.lendingItemStatesDirty[orderIdHash] = struct{}{} - if self.onDirty != nil { - self.onDirty(self.lendingBook) - self.onDirty = nil + le.lendingItemStates[orderIdHash] = newobj + le.lendingItemStatesDirty[orderIdHash] = struct{}{} + if le.onDirty != nil { + le.onDirty(le.lendingBook) + le.onDirty = nil } return newobj } -func (self *lendingExchangeState) createLiquidationTime(db Database, time common.Hash) (newobj *liquidationTimeState) { - newobj = newLiquidationTimeState(time, self.lendingBook, itemList{Volume: Zero}, self.MarkLiquidationTimeDirty) - self.liquidationTimeStates[time] = newobj - self.liquidationTimestatesDirty[time] = struct{}{} +func (le *lendingExchangeState) createLiquidationTime(db Database, time common.Hash) (newobj *liquidationTimeState) { + newobj = newLiquidationTimeState(time, le.lendingBook, itemList{Volume: Zero}, le.MarkLiquidationTimeDirty) + le.liquidationTimeStates[time] = newobj + le.liquidationTimestatesDirty[time] = struct{}{} data, err := rlp.EncodeToBytes(newobj) if err != nil { panic(fmt.Errorf("can't encode liquidation time at %x: %v", time[:], err)) } - self.setError(self.getLiquidationTimeTrie(db).TryUpdate(time[:], data)) - if self.onDirty != nil { - self.onDirty(self.lendingBook) - self.onDirty = nil + le.setError(le.getLiquidationTimeTrie(db).TryUpdate(time[:], data)) + if le.onDirty != nil { + le.onDirty(le.lendingBook) + le.onDirty = nil } return newobj } -func (self *lendingExchangeState) insertLendingTrade(tradeId common.Hash, order LendingTrade) (newobj *lendingTradeState) { - newobj = newLendingTradeState(self.lendingBook, tradeId, order, self.MarkLendingTradeDirty) - self.lendingTradeStates[tradeId] = newobj - self.lendingTradeStatesDirty[tradeId] = struct{}{} - if self.onDirty != nil { - self.onDirty(self.lendingBook) - self.onDirty = nil +func (le *lendingExchangeState) insertLendingTrade(tradeId common.Hash, order LendingTrade) (newobj *lendingTradeState) { + newobj = newLendingTradeState(le.lendingBook, tradeId, order, le.MarkLendingTradeDirty) + le.lendingTradeStates[tradeId] = newobj + le.lendingTradeStatesDirty[tradeId] = struct{}{} + if le.onDirty != nil { + le.onDirty(le.lendingBook) + le.onDirty = nil } return newobj } diff --git a/XDCxlending/lendingstate/state_lendingitem.go b/XDCxlending/lendingstate/state_lendingitem.go index 10d95ef68b21..ef9d193653d4 100644 --- a/XDCxlending/lendingstate/state_lendingitem.go +++ b/XDCxlending/lendingstate/state_lendingitem.go @@ -31,8 +31,8 @@ type lendingItemState struct { onDirty func(orderId common.Hash) // Callback method to mark a state object newly dirty } -func (s *lendingItemState) empty() bool { - return s.data.Quantity == nil || s.data.Quantity.Cmp(Zero) == 0 +func (li *lendingItemState) empty() bool { + return li.data.Quantity == nil || li.data.Quantity.Cmp(Zero) == 0 } func newLendinItemState(orderBook common.Hash, orderId common.Hash, data LendingItem, onDirty func(orderId common.Hash)) *lendingItemState { @@ -45,23 +45,23 @@ func newLendinItemState(orderBook common.Hash, orderId common.Hash, data Lending } // EncodeRLP implements rlp.Encoder. -func (c *lendingItemState) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, c.data) +func (li *lendingItemState) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, li.data) } -func (self *lendingItemState) deepCopy(onDirty func(orderId common.Hash)) *lendingItemState { - stateOrderList := newLendinItemState(self.orderBook, self.orderId, self.data, onDirty) +func (li *lendingItemState) deepCopy(onDirty func(orderId common.Hash)) *lendingItemState { + stateOrderList := newLendinItemState(li.orderBook, li.orderId, li.data, onDirty) return stateOrderList } -func (self *lendingItemState) setVolume(volume *big.Int) { - self.data.Quantity = volume - if self.onDirty != nil { - self.onDirty(self.orderId) - self.onDirty = nil +func (li *lendingItemState) setVolume(volume *big.Int) { + li.data.Quantity = volume + if li.onDirty != nil { + li.onDirty(li.orderId) + li.onDirty = nil } } -func (self *lendingItemState) Quantity() *big.Int { - return self.data.Quantity +func (li *lendingItemState) Quantity() *big.Int { + return li.data.Quantity } diff --git a/XDCxlending/lendingstate/state_lendingtrade.go b/XDCxlending/lendingstate/state_lendingtrade.go index 893a6063ebd0..ab3ce84e2d25 100644 --- a/XDCxlending/lendingstate/state_lendingtrade.go +++ b/XDCxlending/lendingstate/state_lendingtrade.go @@ -17,10 +17,11 @@ package lendingstate import ( - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/rlp" "io" "math/big" + + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/rlp" ) type lendingTradeState struct { @@ -30,8 +31,8 @@ type lendingTradeState struct { onDirty func(orderId common.Hash) // Callback method to mark a state object newly dirty } -func (s *lendingTradeState) empty() bool { - return s.data.Amount.Sign() == 0 +func (lt *lendingTradeState) empty() bool { + return lt.data.Amount.Sign() == 0 } func newLendingTradeState(orderBook common.Hash, tradeId common.Hash, data LendingTrade, onDirty func(orderId common.Hash)) *lendingTradeState { @@ -44,35 +45,35 @@ func newLendingTradeState(orderBook common.Hash, tradeId common.Hash, data Lendi } // EncodeRLP implements rlp.Encoder. -func (c *lendingTradeState) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, c.data) +func (lt *lendingTradeState) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, lt.data) } -func (self *lendingTradeState) deepCopy(onDirty func(orderId common.Hash)) *lendingTradeState { - stateOrderList := newLendingTradeState(self.orderBook, self.tradeId, self.data, onDirty) +func (lt *lendingTradeState) deepCopy(onDirty func(orderId common.Hash)) *lendingTradeState { + stateOrderList := newLendingTradeState(lt.orderBook, lt.tradeId, lt.data, onDirty) return stateOrderList } -func (self *lendingTradeState) SetCollateralLockedAmount(amount *big.Int) { - self.data.CollateralLockedAmount = amount - if self.onDirty != nil { - self.onDirty(self.tradeId) - self.onDirty = nil +func (lt *lendingTradeState) SetCollateralLockedAmount(amount *big.Int) { + lt.data.CollateralLockedAmount = amount + if lt.onDirty != nil { + lt.onDirty(lt.tradeId) + lt.onDirty = nil } } -func (self *lendingTradeState) SetLiquidationPrice(price *big.Int) { - self.data.LiquidationPrice = price - if self.onDirty != nil { - self.onDirty(self.tradeId) - self.onDirty = nil +func (lt *lendingTradeState) SetLiquidationPrice(price *big.Int) { + lt.data.LiquidationPrice = price + if lt.onDirty != nil { + lt.onDirty(lt.tradeId) + lt.onDirty = nil } } -func (self *lendingTradeState) SetAmount(amount *big.Int) { - self.data.Amount = amount - if self.onDirty != nil { - self.onDirty(self.tradeId) - self.onDirty = nil +func (lt *lendingTradeState) SetAmount(amount *big.Int) { + lt.data.Amount = amount + if lt.onDirty != nil { + lt.onDirty(lt.tradeId) + lt.onDirty = nil } } diff --git a/XDCxlending/lendingstate/state_liquidationtime.go b/XDCxlending/lendingstate/state_liquidationtime.go index 3661b4628764..558650989a97 100644 --- a/XDCxlending/lendingstate/state_liquidationtime.go +++ b/XDCxlending/lendingstate/state_liquidationtime.go @@ -19,11 +19,12 @@ package lendingstate import ( "bytes" "fmt" + "io" + "math/big" + "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/rlp" "github.com/XinFinOrg/XDPoSChain/trie" - "io" - "math/big" ) type liquidationTimeState struct { @@ -47,8 +48,8 @@ type liquidationTimeState struct { onDirty func(time common.Hash) // Callback method to mark a state object newly dirty } -func (s *liquidationTimeState) empty() bool { - return s.data.Volume == nil || s.data.Volume.Sign() == 0 +func (lt *liquidationTimeState) empty() bool { + return lt.data.Volume == nil || lt.data.Volume.Sign() == 0 } func newLiquidationTimeState(time common.Hash, lendingBook common.Hash, data itemList, onDirty func(time common.Hash)) *liquidationTimeState { @@ -62,59 +63,59 @@ func newLiquidationTimeState(time common.Hash, lendingBook common.Hash, data ite } } -func (self *liquidationTimeState) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, self.data) +func (lt *liquidationTimeState) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, lt.data) } -func (self *liquidationTimeState) setError(err error) { - if self.dbErr == nil { - self.dbErr = err +func (lt *liquidationTimeState) setError(err error) { + if lt.dbErr == nil { + lt.dbErr = err } } -func (self *liquidationTimeState) getTrie(db Database) Trie { - if self.trie == nil { +func (lt *liquidationTimeState) getTrie(db Database) Trie { + if lt.trie == nil { var err error - self.trie, err = db.OpenStorageTrie(self.lendingBook, self.data.Root) + lt.trie, err = db.OpenStorageTrie(lt.lendingBook, lt.data.Root) if err != nil { - self.trie, _ = db.OpenStorageTrie(self.time, EmptyHash) - self.setError(fmt.Errorf("can't create storage trie: %v", err)) + lt.trie, _ = db.OpenStorageTrie(lt.time, EmptyHash) + lt.setError(fmt.Errorf("can't create storage trie: %v", err)) } } - return self.trie + return lt.trie } -func (self *liquidationTimeState) Exist(db Database, tradeId common.Hash) bool { - amount, exists := self.cachedStorage[tradeId] +func (lt *liquidationTimeState) Exist(db Database, tradeId common.Hash) bool { + amount, exists := lt.cachedStorage[tradeId] if exists { return true } // Load from DB in case it is missing. - enc, err := self.getTrie(db).TryGet(tradeId[:]) + enc, err := lt.getTrie(db).TryGet(tradeId[:]) if err != nil { - self.setError(err) + lt.setError(err) return false } if len(enc) > 0 { _, content, _, err := rlp.Split(enc) if err != nil { - self.setError(err) + lt.setError(err) } amount.SetBytes(content) } if (amount != common.Hash{}) { - self.cachedStorage[tradeId] = amount + lt.cachedStorage[tradeId] = amount } return true } -func (self *liquidationTimeState) getAllTradeIds(db Database) []common.Hash { +func (lt *liquidationTimeState) getAllTradeIds(db Database) []common.Hash { tradeIds := []common.Hash{} - lendingBookTrie := self.getTrie(db) + lendingBookTrie := lt.getTrie(db) if lendingBookTrie == nil { return tradeIds } - for id, value := range self.cachedStorage { + for id, value := range lt.cachedStorage { if !common.EmptyHash(value) { tradeIds = append(tradeIds, id) } @@ -122,7 +123,7 @@ func (self *liquidationTimeState) getAllTradeIds(db Database) []common.Hash { orderListIt := trie.NewIterator(lendingBookTrie.NodeIterator(nil)) for orderListIt.Next() { id := common.BytesToHash(orderListIt.Key) - if _, exist := self.cachedStorage[id]; exist { + if _, exist := lt.cachedStorage[id]; exist { continue } tradeIds = append(tradeIds, id) @@ -130,83 +131,83 @@ func (self *liquidationTimeState) getAllTradeIds(db Database) []common.Hash { return tradeIds } -func (self *liquidationTimeState) insertTradeId(db Database, tradeId common.Hash) { - self.setTradeId(tradeId, tradeId) - self.setError(self.getTrie(db).TryUpdate(tradeId[:], tradeId[:])) +func (lt *liquidationTimeState) insertTradeId(db Database, tradeId common.Hash) { + lt.setTradeId(tradeId, tradeId) + lt.setError(lt.getTrie(db).TryUpdate(tradeId[:], tradeId[:])) } -func (self *liquidationTimeState) removeTradeId(db Database, tradeId common.Hash) { - tr := self.getTrie(db) - self.setError(tr.TryDelete(tradeId[:])) - self.setTradeId(tradeId, EmptyHash) +func (lt *liquidationTimeState) removeTradeId(db Database, tradeId common.Hash) { + tr := lt.getTrie(db) + lt.setError(tr.TryDelete(tradeId[:])) + lt.setTradeId(tradeId, EmptyHash) } -func (self *liquidationTimeState) setTradeId(tradeId common.Hash, value common.Hash) { - self.cachedStorage[tradeId] = value - self.dirtyStorage[tradeId] = value +func (lt *liquidationTimeState) setTradeId(tradeId common.Hash, value common.Hash) { + lt.cachedStorage[tradeId] = value + lt.dirtyStorage[tradeId] = value - if self.onDirty != nil { - self.onDirty(self.lendingBook) - self.onDirty = nil + if lt.onDirty != nil { + lt.onDirty(lt.lendingBook) + lt.onDirty = nil } } -func (self *liquidationTimeState) updateTrie(db Database) Trie { - tr := self.getTrie(db) - for key, value := range self.dirtyStorage { - delete(self.dirtyStorage, key) +func (lt *liquidationTimeState) updateTrie(db Database) Trie { + tr := lt.getTrie(db) + for key, value := range lt.dirtyStorage { + delete(lt.dirtyStorage, key) if value == EmptyHash { - self.setError(tr.TryDelete(key[:])) + lt.setError(tr.TryDelete(key[:])) continue } v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00")) - self.setError(tr.TryUpdate(key[:], v)) + lt.setError(tr.TryUpdate(key[:], v)) } return tr } -func (self *liquidationTimeState) updateRoot(db Database) error { - self.updateTrie(db) - if self.dbErr != nil { - return self.dbErr +func (lt *liquidationTimeState) updateRoot(db Database) error { + lt.updateTrie(db) + if lt.dbErr != nil { + return lt.dbErr } - root, err := self.trie.Commit(nil) + root, err := lt.trie.Commit(nil) if err == nil { - self.data.Root = root + lt.data.Root = root } return err } -func (self *liquidationTimeState) deepCopy(db *LendingStateDB, onDirty func(time common.Hash)) *liquidationTimeState { - stateLendingBook := newLiquidationTimeState(self.lendingBook, self.time, self.data, onDirty) - if self.trie != nil { - stateLendingBook.trie = db.db.CopyTrie(self.trie) +func (lt *liquidationTimeState) deepCopy(db *LendingStateDB, onDirty func(time common.Hash)) *liquidationTimeState { + stateLendingBook := newLiquidationTimeState(lt.lendingBook, lt.time, lt.data, onDirty) + if lt.trie != nil { + stateLendingBook.trie = db.db.CopyTrie(lt.trie) } - for key, value := range self.dirtyStorage { + for key, value := range lt.dirtyStorage { stateLendingBook.dirtyStorage[key] = value } - for key, value := range self.cachedStorage { + for key, value := range lt.cachedStorage { stateLendingBook.cachedStorage[key] = value } return stateLendingBook } -func (c *liquidationTimeState) AddVolume(amount *big.Int) { - c.setVolume(new(big.Int).Add(c.data.Volume, amount)) +func (lt *liquidationTimeState) AddVolume(amount *big.Int) { + lt.setVolume(new(big.Int).Add(lt.data.Volume, amount)) } -func (c *liquidationTimeState) subVolume(amount *big.Int) { - c.setVolume(new(big.Int).Sub(c.data.Volume, amount)) +func (lt *liquidationTimeState) subVolume(amount *big.Int) { + lt.setVolume(new(big.Int).Sub(lt.data.Volume, amount)) } -func (self *liquidationTimeState) setVolume(volume *big.Int) { - self.data.Volume = volume - if self.onDirty != nil { - self.onDirty(self.lendingBook) - self.onDirty = nil +func (lt *liquidationTimeState) setVolume(volume *big.Int) { + lt.data.Volume = volume + if lt.onDirty != nil { + lt.onDirty(lt.lendingBook) + lt.onDirty = nil } } -func (self *liquidationTimeState) Volume() *big.Int { - return self.data.Volume +func (lt *liquidationTimeState) Volume() *big.Int { + return lt.data.Volume } diff --git a/XDCxlending/lendingstate/statedb.go b/XDCxlending/lendingstate/statedb.go index ee029a862b8c..e5a5cbf6ede6 100644 --- a/XDCxlending/lendingstate/statedb.go +++ b/XDCxlending/lendingstate/statedb.go @@ -73,39 +73,39 @@ func New(root common.Hash, db Database) (*LendingStateDB, error) { } // setError remembers the first non-nil error it is called with. -func (self *LendingStateDB) setError(err error) { - if self.dbErr == nil { - self.dbErr = err +func (ls *LendingStateDB) setError(err error) { + if ls.dbErr == nil { + ls.dbErr = err } } -func (self *LendingStateDB) Error() error { - return self.dbErr +func (ls *LendingStateDB) Error() error { + return ls.dbErr } // Exist reports whether the given tradeId address exists in the state. // Notably this also returns true for suicided lenddinges. -func (self *LendingStateDB) Exist(addr common.Hash) bool { - return self.getLendingExchange(addr) != nil +func (ls *LendingStateDB) Exist(addr common.Hash) bool { + return ls.getLendingExchange(addr) != nil } // Empty returns whether the state object is either non-existent // or empty according to the EIP161 specification (balance = nonce = code = 0) -func (self *LendingStateDB) Empty(addr common.Hash) bool { - so := self.getLendingExchange(addr) +func (ls *LendingStateDB) Empty(addr common.Hash) bool { + so := ls.getLendingExchange(addr) return so == nil || so.empty() } -func (self *LendingStateDB) GetNonce(addr common.Hash) uint64 { - stateObject := self.getLendingExchange(addr) +func (ls *LendingStateDB) GetNonce(addr common.Hash) uint64 { + stateObject := ls.getLendingExchange(addr) if stateObject != nil { return stateObject.Nonce() } return 0 } -func (self *LendingStateDB) GetTradeNonce(addr common.Hash) uint64 { - stateObject := self.getLendingExchange(addr) +func (ls *LendingStateDB) GetTradeNonce(addr common.Hash) uint64 { + stateObject := ls.getLendingExchange(addr) if stateObject != nil { return stateObject.TradeNonce() } @@ -113,14 +113,14 @@ func (self *LendingStateDB) GetTradeNonce(addr common.Hash) uint64 { } // Database retrieves the low level database supporting the lower level trie ops. -func (self *LendingStateDB) Database() Database { - return self.db +func (ls *LendingStateDB) Database() Database { + return ls.db } -func (self *LendingStateDB) SetNonce(addr common.Hash, nonce uint64) { - stateObject := self.GetOrNewLendingExchangeObject(addr) +func (ls *LendingStateDB) SetNonce(addr common.Hash, nonce uint64) { + stateObject := ls.GetOrNewLendingExchangeObject(addr) if stateObject != nil { - self.journal = append(self.journal, nonceChange{ + ls.journal = append(ls.journal, nonceChange{ hash: addr, prev: stateObject.Nonce(), }) @@ -128,10 +128,10 @@ func (self *LendingStateDB) SetNonce(addr common.Hash, nonce uint64) { } } -func (self *LendingStateDB) SetTradeNonce(addr common.Hash, nonce uint64) { - stateObject := self.GetOrNewLendingExchangeObject(addr) +func (ls *LendingStateDB) SetTradeNonce(addr common.Hash, nonce uint64) { + stateObject := ls.GetOrNewLendingExchangeObject(addr) if stateObject != nil { - self.journal = append(self.journal, tradeNonceChange{ + ls.journal = append(ls.journal, tradeNonceChange{ hash: addr, prev: stateObject.TradeNonce(), }) @@ -139,45 +139,45 @@ func (self *LendingStateDB) SetTradeNonce(addr common.Hash, nonce uint64) { } } -func (self *LendingStateDB) InsertLendingItem(orderBook common.Hash, orderId common.Hash, order LendingItem) { +func (ls *LendingStateDB) InsertLendingItem(orderBook common.Hash, orderId common.Hash, order LendingItem) { interestHash := common.BigToHash(order.Interest) - stateExchange := self.getLendingExchange(orderBook) + stateExchange := ls.getLendingExchange(orderBook) if stateExchange == nil { - stateExchange = self.createLendingExchangeObject(orderBook) + stateExchange = ls.createLendingExchangeObject(orderBook) } var stateOrderList *itemListState switch order.Side { case Investing: - stateOrderList = stateExchange.getInvestingOrderList(self.db, interestHash) + stateOrderList = stateExchange.getInvestingOrderList(ls.db, interestHash) if stateOrderList == nil { - stateOrderList = stateExchange.createInvestingOrderList(self.db, interestHash) + stateOrderList = stateExchange.createInvestingOrderList(ls.db, interestHash) } case Borrowing: - stateOrderList = stateExchange.getBorrowingOrderList(self.db, interestHash) + stateOrderList = stateExchange.getBorrowingOrderList(ls.db, interestHash) if stateOrderList == nil { - stateOrderList = stateExchange.createBorrowingOrderList(self.db, interestHash) + stateOrderList = stateExchange.createBorrowingOrderList(ls.db, interestHash) } default: return } - self.journal = append(self.journal, insertOrder{ + ls.journal = append(ls.journal, insertOrder{ orderBook: orderBook, orderId: orderId, order: &order, }) - stateExchange.createLendingItem(self.db, orderId, order) - stateOrderList.insertLendingItem(self.db, orderId, common.BigToHash(order.Quantity)) + stateExchange.createLendingItem(ls.db, orderId, order) + stateOrderList.insertLendingItem(ls.db, orderId, common.BigToHash(order.Quantity)) stateOrderList.AddVolume(order.Quantity) } -func (self *LendingStateDB) InsertTradingItem(orderBook common.Hash, tradeId uint64, order LendingTrade) { +func (ls *LendingStateDB) InsertTradingItem(orderBook common.Hash, tradeId uint64, order LendingTrade) { tradeIdHash := common.Uint64ToHash(tradeId) - stateExchange := self.getLendingExchange(orderBook) + stateExchange := ls.getLendingExchange(orderBook) if stateExchange == nil { - stateExchange = self.createLendingExchangeObject(orderBook) + stateExchange = ls.createLendingExchangeObject(orderBook) } - prvTrade := self.GetLendingTrade(orderBook, tradeIdHash) - self.journal = append(self.journal, insertTrading{ + prvTrade := ls.GetLendingTrade(orderBook, tradeIdHash) + ls.journal = append(ls.journal, insertTrading{ orderBook: orderBook, tradeId: tradeId, prvTrade: &prvTrade, @@ -185,88 +185,90 @@ func (self *LendingStateDB) InsertTradingItem(orderBook common.Hash, tradeId uin stateExchange.insertLendingTrade(tradeIdHash, order) } -func (self *LendingStateDB) UpdateLiquidationPrice(orderBook common.Hash, tradeId uint64, price *big.Int) { +func (ls *LendingStateDB) UpdateLiquidationPrice(orderBook common.Hash, tradeId uint64, price *big.Int) { tradeIdHash := common.Uint64ToHash(tradeId) - stateExchange := self.getLendingExchange(orderBook) + stateExchange := ls.getLendingExchange(orderBook) if stateExchange == nil { - stateExchange = self.createLendingExchangeObject(orderBook) + stateExchange = ls.createLendingExchangeObject(orderBook) } - stateLendingTrade := stateExchange.getLendingTrade(self.db, tradeIdHash) - self.journal = append(self.journal, liquidationPriceChange{ + stateLendingTrade := stateExchange.getLendingTrade(ls.db, tradeIdHash) + ls.journal = append(ls.journal, liquidationPriceChange{ orderBook: orderBook, tradeId: tradeIdHash, prev: stateLendingTrade.data.LiquidationPrice, }) stateLendingTrade.SetLiquidationPrice(price) } -func (self *LendingStateDB) UpdateCollateralLockedAmount(orderBook common.Hash, tradeId uint64, amount *big.Int) { + +func (ls *LendingStateDB) UpdateCollateralLockedAmount(orderBook common.Hash, tradeId uint64, amount *big.Int) { tradeIdHash := common.Uint64ToHash(tradeId) - stateExchange := self.getLendingExchange(orderBook) + stateExchange := ls.getLendingExchange(orderBook) if stateExchange == nil { - stateExchange = self.createLendingExchangeObject(orderBook) + stateExchange = ls.createLendingExchangeObject(orderBook) } - stateLendingTrade := stateExchange.getLendingTrade(self.db, tradeIdHash) - self.journal = append(self.journal, collateralLockedAmount{ + stateLendingTrade := stateExchange.getLendingTrade(ls.db, tradeIdHash) + ls.journal = append(ls.journal, collateralLockedAmount{ orderBook: orderBook, tradeId: tradeIdHash, prev: stateLendingTrade.data.CollateralLockedAmount, }) stateLendingTrade.SetCollateralLockedAmount(amount) } -func (self *LendingStateDB) GetLendingOrder(orderBook common.Hash, orderId common.Hash) LendingItem { - stateObject := self.GetOrNewLendingExchangeObject(orderBook) + +func (ls *LendingStateDB) GetLendingOrder(orderBook common.Hash, orderId common.Hash) LendingItem { + stateObject := ls.GetOrNewLendingExchangeObject(orderBook) if stateObject == nil { return EmptyLendingOrder } - stateOrderItem := stateObject.getLendingItem(self.db, orderId) + stateOrderItem := stateObject.getLendingItem(ls.db, orderId) if stateOrderItem == nil { return EmptyLendingOrder } return stateOrderItem.data } -func (self *LendingStateDB) GetLendingTrade(orderBook common.Hash, tradeId common.Hash) LendingTrade { - stateObject := self.GetOrNewLendingExchangeObject(orderBook) +func (ls *LendingStateDB) GetLendingTrade(orderBook common.Hash, tradeId common.Hash) LendingTrade { + stateObject := ls.GetOrNewLendingExchangeObject(orderBook) if stateObject == nil { return EmptyLendingTrade } - stateOrderItem := stateObject.getLendingTrade(self.db, tradeId) + stateOrderItem := stateObject.getLendingTrade(ls.db, tradeId) if stateOrderItem == nil || stateOrderItem.empty() { return EmptyLendingTrade } return stateOrderItem.data } -func (self *LendingStateDB) SubAmountLendingItem(orderBook common.Hash, orderId common.Hash, price *big.Int, amount *big.Int, side string) error { +func (ls *LendingStateDB) SubAmountLendingItem(orderBook common.Hash, orderId common.Hash, price *big.Int, amount *big.Int, side string) error { priceHash := common.BigToHash(price) - lendingExchange := self.GetOrNewLendingExchangeObject(orderBook) + lendingExchange := ls.GetOrNewLendingExchangeObject(orderBook) if lendingExchange == nil { return fmt.Errorf("not found order book: %s", orderBook.Hex()) } var orderList *itemListState switch side { case Investing: - orderList = lendingExchange.getInvestingOrderList(self.db, priceHash) + orderList = lendingExchange.getInvestingOrderList(ls.db, priceHash) case Borrowing: - orderList = lendingExchange.getBorrowingOrderList(self.db, priceHash) + orderList = lendingExchange.getBorrowingOrderList(ls.db, priceHash) default: return fmt.Errorf("not found order type: %s", side) } if orderList == nil || orderList.empty() { return fmt.Errorf("empty orderList: order book : %s , order id : %s , key : %s", orderBook, orderId.Hex(), priceHash.Hex()) } - lendingItem := lendingExchange.getLendingItem(self.db, orderId) + lendingItem := lendingExchange.getLendingItem(ls.db, orderId) if lendingItem == nil || lendingItem.empty() { return fmt.Errorf("empty order item: order book : %s , order id : %s , key : %s", orderBook, orderId.Hex(), priceHash.Hex()) } - currentAmount := new(big.Int).SetBytes(orderList.GetOrderAmount(self.db, orderId).Bytes()[:]) + currentAmount := new(big.Int).SetBytes(orderList.GetOrderAmount(ls.db, orderId).Bytes()[:]) if currentAmount.Cmp(amount) < 0 { return fmt.Errorf("not enough order amount %s: have : %d , want : %d", orderId.Hex(), currentAmount, amount) } - self.journal = append(self.journal, subAmountOrder{ + ls.journal = append(ls.journal, subAmountOrder{ orderBook: orderBook, orderId: orderId, - order: self.GetLendingOrder(orderBook, orderId), + order: ls.GetLendingOrder(orderBook, orderId), amount: amount, }) newAmount := new(big.Int).Sub(currentAmount, amount) @@ -274,36 +276,36 @@ func (self *LendingStateDB) SubAmountLendingItem(orderBook common.Hash, orderId log.Debug("SubAmountOrderItem", "tradeId", orderId.Hex(), "side", side, "key", price.Uint64(), "amount", amount.Uint64(), "new amount", newAmount.Uint64()) orderList.subVolume(amount) if newAmount.Sign() == 0 { - orderList.removeOrderItem(self.db, orderId) + orderList.removeOrderItem(ls.db, orderId) } else { orderList.setOrderItem(orderId, common.BigToHash(newAmount)) } if orderList.empty() { switch side { case Investing: - lendingExchange.removeInvestingOrderList(self.db, orderList) + lendingExchange.removeInvestingOrderList(ls.db, orderList) case Borrowing: - lendingExchange.removeBorrowingOrderList(self.db, orderList) + lendingExchange.removeBorrowingOrderList(ls.db, orderList) default: } } return nil } -func (self *LendingStateDB) CancelLendingOrder(orderBook common.Hash, order *LendingItem) error { +func (ls *LendingStateDB) CancelLendingOrder(orderBook common.Hash, order *LendingItem) error { interestHash := common.BigToHash(order.Interest) orderIdHash := common.BigToHash(new(big.Int).SetUint64(order.LendingId)) - stateObject := self.GetOrNewLendingExchangeObject(orderBook) + stateObject := ls.GetOrNewLendingExchangeObject(orderBook) if stateObject == nil { return fmt.Errorf("not found order book: %s", orderBook.Hex()) } - lendingItem := stateObject.getLendingItem(self.db, orderIdHash) + lendingItem := stateObject.getLendingItem(ls.db, orderIdHash) var orderList *itemListState switch lendingItem.data.Side { case Investing: - orderList = stateObject.getInvestingOrderList(self.db, interestHash) + orderList = stateObject.getInvestingOrderList(ls.db, interestHash) case Borrowing: - orderList = stateObject.getBorrowingOrderList(self.db, interestHash) + orderList = stateObject.getBorrowingOrderList(ls.db, interestHash) default: return fmt.Errorf("not found order side: %s", order.Side) } @@ -316,35 +318,35 @@ func (self *LendingStateDB) CancelLendingOrder(orderBook common.Hash, order *Len if lendingItem.data.UserAddress != order.UserAddress { return fmt.Errorf("error Order UserAddress mismatch when cancel order book: %s , order id : %s , got : %s , expect : %s", orderBook, orderIdHash.Hex(), lendingItem.data.UserAddress.Hex(), order.UserAddress.Hex()) } - self.journal = append(self.journal, cancelOrder{ + ls.journal = append(ls.journal, cancelOrder{ orderBook: orderBook, orderId: orderIdHash, - order: self.GetLendingOrder(orderBook, orderIdHash), + order: ls.GetLendingOrder(orderBook, orderIdHash), }) lendingItem.setVolume(big.NewInt(0)) - currentAmount := new(big.Int).SetBytes(orderList.GetOrderAmount(self.db, orderIdHash).Bytes()[:]) + currentAmount := new(big.Int).SetBytes(orderList.GetOrderAmount(ls.db, orderIdHash).Bytes()[:]) orderList.subVolume(currentAmount) - orderList.removeOrderItem(self.db, orderIdHash) + orderList.removeOrderItem(ls.db, orderIdHash) if orderList.empty() { switch order.Side { case Investing: - stateObject.removeInvestingOrderList(self.db, orderList) + stateObject.removeInvestingOrderList(ls.db, orderList) case Borrowing: - stateObject.removeBorrowingOrderList(self.db, orderList) + stateObject.removeBorrowingOrderList(ls.db, orderList) default: } } return nil } -func (self *LendingStateDB) GetBestInvestingRate(orderBook common.Hash) (*big.Int, *big.Int) { - stateObject := self.getLendingExchange(orderBook) +func (ls *LendingStateDB) GetBestInvestingRate(orderBook common.Hash) (*big.Int, *big.Int) { + stateObject := ls.getLendingExchange(orderBook) if stateObject != nil { - investingHash := stateObject.getBestInvestingInterest(self.db) + investingHash := stateObject.getBestInvestingInterest(ls.db) if common.EmptyHash(investingHash) { return Zero, Zero } - orderList := stateObject.getInvestingOrderList(self.db, investingHash) + orderList := stateObject.getInvestingOrderList(ls.db, investingHash) if orderList == nil { log.Error("order list investing not found", "key", investingHash.Hex()) return Zero, Zero @@ -354,14 +356,14 @@ func (self *LendingStateDB) GetBestInvestingRate(orderBook common.Hash) (*big.In return Zero, Zero } -func (self *LendingStateDB) GetBestBorrowRate(orderBook common.Hash) (*big.Int, *big.Int) { - stateObject := self.getLendingExchange(orderBook) +func (ls *LendingStateDB) GetBestBorrowRate(orderBook common.Hash) (*big.Int, *big.Int) { + stateObject := ls.getLendingExchange(orderBook) if stateObject != nil { - priceHash := stateObject.getBestBorrowingInterest(self.db) + priceHash := stateObject.getBestBorrowingInterest(ls.db) if common.EmptyHash(priceHash) { return Zero, Zero } - orderList := stateObject.getBorrowingOrderList(self.db, priceHash) + orderList := stateObject.getBorrowingOrderList(ls.db, priceHash) if orderList == nil { log.Error("order list ask not found", "key", priceHash.Hex()) return Zero, Zero @@ -371,25 +373,25 @@ func (self *LendingStateDB) GetBestBorrowRate(orderBook common.Hash) (*big.Int, return Zero, Zero } -func (self *LendingStateDB) GetBestLendingIdAndAmount(orderBook common.Hash, price *big.Int, side string) (common.Hash, *big.Int, error) { - stateObject := self.GetOrNewLendingExchangeObject(orderBook) +func (ls *LendingStateDB) GetBestLendingIdAndAmount(orderBook common.Hash, price *big.Int, side string) (common.Hash, *big.Int, error) { + stateObject := ls.GetOrNewLendingExchangeObject(orderBook) if stateObject != nil { var stateOrderList *itemListState switch side { case Investing: - stateOrderList = stateObject.getInvestingOrderList(self.db, common.BigToHash(price)) + stateOrderList = stateObject.getInvestingOrderList(ls.db, common.BigToHash(price)) case Borrowing: - stateOrderList = stateObject.getBorrowingOrderList(self.db, common.BigToHash(price)) + stateOrderList = stateObject.getBorrowingOrderList(ls.db, common.BigToHash(price)) default: return EmptyHash, Zero, fmt.Errorf("not found side: %s", side) } if stateOrderList != nil { - key, _, err := stateOrderList.getTrie(self.db).TryGetBestLeftKeyAndValue() + key, _, err := stateOrderList.getTrie(ls.db).TryGetBestLeftKeyAndValue() if err != nil { return EmptyHash, Zero, err } orderId := common.BytesToHash(key) - amount := stateOrderList.GetOrderAmount(self.db, orderId) + amount := stateOrderList.GetOrderAmount(ls.db, orderId) return orderId, new(big.Int).SetBytes(amount.Bytes()), nil } return EmptyHash, Zero, fmt.Errorf("not found order list with orderBook: %s , key : %d , side : %s", orderBook.Hex(), price, side) @@ -398,25 +400,25 @@ func (self *LendingStateDB) GetBestLendingIdAndAmount(orderBook common.Hash, pri } // updateLendingExchange writes the given object to the trie. -func (self *LendingStateDB) updateLendingExchange(stateObject *lendingExchangeState) { +func (ls *LendingStateDB) updateLendingExchange(stateObject *lendingExchangeState) { addr := stateObject.Hash() data, err := rlp.EncodeToBytes(stateObject) if err != nil { panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err)) } - self.setError(self.trie.TryUpdate(addr[:], data)) + ls.setError(ls.trie.TryUpdate(addr[:], data)) } // Retrieve a state object given my the address. Returns nil if not found. -func (self *LendingStateDB) getLendingExchange(addr common.Hash) (stateObject *lendingExchangeState) { +func (ls *LendingStateDB) getLendingExchange(addr common.Hash) (stateObject *lendingExchangeState) { // Prefer 'live' objects. - if obj := self.lendingExchangeStates[addr]; obj != nil { + if obj := ls.lendingExchangeStates[addr]; obj != nil { return obj } // Load the object from the database. - enc, err := self.trie.TryGet(addr[:]) + enc, err := ls.trie.TryGet(addr[:]) if len(enc) == 0 { - self.setError(err) + ls.setError(err) return nil } var data lendingObject @@ -425,176 +427,176 @@ func (self *LendingStateDB) getLendingExchange(addr common.Hash) (stateObject *l return nil } // Insert into the live set. - obj := newStateExchanges(self, addr, data, self.MarkLendingExchangeObjectDirty) - self.lendingExchangeStates[addr] = obj + obj := newStateExchanges(ls, addr, data, ls.MarkLendingExchangeObjectDirty) + ls.lendingExchangeStates[addr] = obj return obj } -func (self *LendingStateDB) setLendingExchangeObject(object *lendingExchangeState) { - self.lendingExchangeStates[object.Hash()] = object - self.lendingExchangeStatesDirty[object.Hash()] = struct{}{} +func (ls *LendingStateDB) setLendingExchangeObject(object *lendingExchangeState) { + ls.lendingExchangeStates[object.Hash()] = object + ls.lendingExchangeStatesDirty[object.Hash()] = struct{}{} } // Retrieve a state object or create a new state object if nil. -func (self *LendingStateDB) GetOrNewLendingExchangeObject(addr common.Hash) *lendingExchangeState { - stateExchangeObject := self.getLendingExchange(addr) +func (ls *LendingStateDB) GetOrNewLendingExchangeObject(addr common.Hash) *lendingExchangeState { + stateExchangeObject := ls.getLendingExchange(addr) if stateExchangeObject == nil { - stateExchangeObject = self.createLendingExchangeObject(addr) + stateExchangeObject = ls.createLendingExchangeObject(addr) } return stateExchangeObject } // MarkStateLendObjectDirty adds the specified object to the dirty map to avoid costly // state object cache iteration to find a handful of modified ones. -func (self *LendingStateDB) MarkLendingExchangeObjectDirty(addr common.Hash) { - self.lendingExchangeStatesDirty[addr] = struct{}{} +func (ls *LendingStateDB) MarkLendingExchangeObjectDirty(addr common.Hash) { + ls.lendingExchangeStatesDirty[addr] = struct{}{} } // createStateOrderListObject creates a new state object. If there is an existing tradeId with // the given address, it is overwritten and returned as the second return value. -func (self *LendingStateDB) createLendingExchangeObject(hash common.Hash) (newobj *lendingExchangeState) { - newobj = newStateExchanges(self, hash, lendingObject{}, self.MarkLendingExchangeObjectDirty) +func (ls *LendingStateDB) createLendingExchangeObject(hash common.Hash) (newobj *lendingExchangeState) { + newobj = newStateExchanges(ls, hash, lendingObject{}, ls.MarkLendingExchangeObjectDirty) newobj.setNonce(0) // sets the object to dirty - self.setLendingExchangeObject(newobj) + ls.setLendingExchangeObject(newobj) return newobj } // Copy creates a deep, independent copy of the state. // Snapshots of the copied state cannot be applied to the copy. -func (self *LendingStateDB) Copy() *LendingStateDB { - self.lock.Lock() - defer self.lock.Unlock() +func (ls *LendingStateDB) Copy() *LendingStateDB { + ls.lock.Lock() + defer ls.lock.Unlock() // Copy all the basic fields, initialize the memory ones state := &LendingStateDB{ - db: self.db, - trie: self.db.CopyTrie(self.trie), - lendingExchangeStates: make(map[common.Hash]*lendingExchangeState, len(self.lendingExchangeStatesDirty)), - lendingExchangeStatesDirty: make(map[common.Hash]struct{}, len(self.lendingExchangeStatesDirty)), + db: ls.db, + trie: ls.db.CopyTrie(ls.trie), + lendingExchangeStates: make(map[common.Hash]*lendingExchangeState, len(ls.lendingExchangeStatesDirty)), + lendingExchangeStatesDirty: make(map[common.Hash]struct{}, len(ls.lendingExchangeStatesDirty)), } // Copy the dirty states, logs, and preimages - for addr := range self.lendingExchangeStatesDirty { + for addr := range ls.lendingExchangeStatesDirty { state.lendingExchangeStatesDirty[addr] = struct{}{} } - for addr, exchangeObject := range self.lendingExchangeStates { + for addr, exchangeObject := range ls.lendingExchangeStates { state.lendingExchangeStates[addr] = exchangeObject.deepCopy(state, state.MarkLendingExchangeObjectDirty) } return state } -func (s *LendingStateDB) clearJournalAndRefund() { - s.journal = nil - s.validRevisions = s.validRevisions[:0] +func (ls *LendingStateDB) clearJournalAndRefund() { + ls.journal = nil + ls.validRevisions = ls.validRevisions[:0] } // Snapshot returns an identifier for the current revision of the state. -func (self *LendingStateDB) Snapshot() int { - id := self.nextRevisionId - self.nextRevisionId++ - self.validRevisions = append(self.validRevisions, revision{id, len(self.journal)}) +func (ls *LendingStateDB) Snapshot() int { + id := ls.nextRevisionId + ls.nextRevisionId++ + ls.validRevisions = append(ls.validRevisions, revision{id, len(ls.journal)}) return id } // RevertToSnapshot reverts all state changes made since the given revision. -func (self *LendingStateDB) RevertToSnapshot(revid int) { +func (ls *LendingStateDB) RevertToSnapshot(revid int) { // Find the snapshot in the stack of valid snapshots. - idx := sort.Search(len(self.validRevisions), func(i int) bool { - return self.validRevisions[i].id >= revid + idx := sort.Search(len(ls.validRevisions), func(i int) bool { + return ls.validRevisions[i].id >= revid }) - if idx == len(self.validRevisions) || self.validRevisions[idx].id != revid { + if idx == len(ls.validRevisions) || ls.validRevisions[idx].id != revid { panic(fmt.Errorf("revision id %v cannot be reverted", revid)) } - snapshot := self.validRevisions[idx].journalIndex + snapshot := ls.validRevisions[idx].journalIndex // Replay the journal to undo changes. - for i := len(self.journal) - 1; i >= snapshot; i-- { - self.journal[i].undo(self) + for i := len(ls.journal) - 1; i >= snapshot; i-- { + ls.journal[i].undo(ls) } - self.journal = self.journal[:snapshot] + ls.journal = ls.journal[:snapshot] // Remove invalidated snapshots from the stack. - self.validRevisions = self.validRevisions[:idx] + ls.validRevisions = ls.validRevisions[:idx] } // Finalise finalises the state by removing the self destructed objects // and clears the journal as well as the refunds. -func (s *LendingStateDB) Finalise() { +func (ls *LendingStateDB) Finalise() { // Commit objects to the trie. - for addr, stateObject := range s.lendingExchangeStates { - if _, isDirty := s.lendingExchangeStatesDirty[addr]; isDirty { + for addr, stateObject := range ls.lendingExchangeStates { + if _, isDirty := ls.lendingExchangeStatesDirty[addr]; isDirty { // Write any storage changes in the state object to its storage trie. - err := stateObject.updateInvestingRoot(s.db) + err := stateObject.updateInvestingRoot(ls.db) if err != nil { log.Warn("Finalise updateInvestingRoot", "err", err, "addr", addr, "stateObject", *stateObject) } - stateObject.updateBorrowingRoot(s.db) - stateObject.updateOrderRoot(s.db) - stateObject.updateLendingTradeRoot(s.db) - stateObject.updateLiquidationTimeRoot(s.db) + stateObject.updateBorrowingRoot(ls.db) + stateObject.updateOrderRoot(ls.db) + stateObject.updateLendingTradeRoot(ls.db) + stateObject.updateLiquidationTimeRoot(ls.db) // Update the object in the main tradeId trie. - s.updateLendingExchange(stateObject) + ls.updateLendingExchange(stateObject) //delete(s.investingStatesDirty, addr) } } - s.clearJournalAndRefund() + ls.clearJournalAndRefund() } // IntermediateRoot computes the current root orderBook of the state trie. // It is called in between transactions to get the root orderBook that // goes into transaction receipts. -func (s *LendingStateDB) IntermediateRoot() common.Hash { - s.Finalise() - return s.trie.Hash() +func (ls *LendingStateDB) IntermediateRoot() common.Hash { + ls.Finalise() + return ls.trie.Hash() } // Commit writes the state to the underlying in-memory trie database. -func (s *LendingStateDB) Commit() (root common.Hash, err error) { - defer s.clearJournalAndRefund() +func (ls *LendingStateDB) Commit() (root common.Hash, err error) { + defer ls.clearJournalAndRefund() // Commit objects to the trie. - for addr, stateObject := range s.lendingExchangeStates { - if _, isDirty := s.lendingExchangeStatesDirty[addr]; isDirty { + for addr, stateObject := range ls.lendingExchangeStates { + if _, isDirty := ls.lendingExchangeStatesDirty[addr]; isDirty { // Write any storage changes in the state object to its storage trie. - if err := stateObject.CommitInvestingTrie(s.db); err != nil { + if err := stateObject.CommitInvestingTrie(ls.db); err != nil { return EmptyHash, err } - if err := stateObject.CommitBorrowingTrie(s.db); err != nil { + if err := stateObject.CommitBorrowingTrie(ls.db); err != nil { return EmptyHash, err } - if err := stateObject.CommitLendingItemTrie(s.db); err != nil { + if err := stateObject.CommitLendingItemTrie(ls.db); err != nil { return EmptyHash, err } - if err := stateObject.CommitLendingTradeTrie(s.db); err != nil { + if err := stateObject.CommitLendingTradeTrie(ls.db); err != nil { return EmptyHash, err } - if err := stateObject.CommitLiquidationTimeTrie(s.db); err != nil { + if err := stateObject.CommitLiquidationTimeTrie(ls.db); err != nil { return EmptyHash, err } // Update the object in the main tradeId trie. - s.updateLendingExchange(stateObject) - delete(s.lendingExchangeStatesDirty, addr) + ls.updateLendingExchange(stateObject) + delete(ls.lendingExchangeStatesDirty, addr) } } // Write trie changes. - root, err = s.trie.Commit(func(leaf []byte, parent common.Hash) error { + root, err = ls.trie.Commit(func(leaf []byte, parent common.Hash) error { var exchange lendingObject if err := rlp.DecodeBytes(leaf, &exchange); err != nil { return nil } if exchange.InvestingRoot != EmptyRoot { - s.db.TrieDB().Reference(exchange.InvestingRoot, parent) + ls.db.TrieDB().Reference(exchange.InvestingRoot, parent) } if exchange.BorrowingRoot != EmptyRoot { - s.db.TrieDB().Reference(exchange.BorrowingRoot, parent) + ls.db.TrieDB().Reference(exchange.BorrowingRoot, parent) } if exchange.LendingItemRoot != EmptyRoot { - s.db.TrieDB().Reference(exchange.LendingItemRoot, parent) + ls.db.TrieDB().Reference(exchange.LendingItemRoot, parent) } if exchange.LendingTradeRoot != EmptyRoot { - s.db.TrieDB().Reference(exchange.LendingTradeRoot, parent) + ls.db.TrieDB().Reference(exchange.LendingTradeRoot, parent) } if exchange.LiquidationTimeRoot != EmptyRoot { - s.db.TrieDB().Reference(exchange.LiquidationTimeRoot, parent) + ls.db.TrieDB().Reference(exchange.LiquidationTimeRoot, parent) } return nil }) @@ -602,38 +604,38 @@ func (s *LendingStateDB) Commit() (root common.Hash, err error) { return root, err } -func (self *LendingStateDB) InsertLiquidationTime(lendingBook common.Hash, time *big.Int, tradeId uint64) { +func (ls *LendingStateDB) InsertLiquidationTime(lendingBook common.Hash, time *big.Int, tradeId uint64) { timeHash := common.BigToHash(time) - lendingExchangeState := self.getLendingExchange(lendingBook) + lendingExchangeState := ls.getLendingExchange(lendingBook) if lendingExchangeState == nil { - lendingExchangeState = self.createLendingExchangeObject(lendingBook) + lendingExchangeState = ls.createLendingExchangeObject(lendingBook) } - liquidationTime := lendingExchangeState.getLiquidationTimeOrderList(self.db, timeHash) + liquidationTime := lendingExchangeState.getLiquidationTimeOrderList(ls.db, timeHash) if liquidationTime == nil { - liquidationTime = lendingExchangeState.createLiquidationTime(self.db, timeHash) + liquidationTime = lendingExchangeState.createLiquidationTime(ls.db, timeHash) } - liquidationTime.insertTradeId(self.db, common.Uint64ToHash(tradeId)) + liquidationTime.insertTradeId(ls.db, common.Uint64ToHash(tradeId)) liquidationTime.AddVolume(One) } -func (self *LendingStateDB) RemoveLiquidationTime(lendingBook common.Hash, tradeId uint64, time uint64) error { +func (ls *LendingStateDB) RemoveLiquidationTime(lendingBook common.Hash, tradeId uint64, time uint64) error { timeHash := common.Uint64ToHash(time) tradeIdHash := common.Uint64ToHash(tradeId) - lendingExchangeState := self.getLendingExchange(lendingBook) + lendingExchangeState := ls.getLendingExchange(lendingBook) if lendingExchangeState == nil { return fmt.Errorf("lending book not found: %s", lendingBook.Hex()) } - liquidationTime := lendingExchangeState.getLiquidationTimeOrderList(self.db, timeHash) + liquidationTime := lendingExchangeState.getLiquidationTimeOrderList(ls.db, timeHash) if liquidationTime == nil { return fmt.Errorf("not found liquidation time: %s , %d", lendingBook.Hex(), time) } - if !liquidationTime.Exist(self.db, tradeIdHash) { - return fmt.Errorf("not exist tradeId: %s , %d , %d", lendingBook.Hex(), time, tradeId) + if !liquidationTime.Exist(ls.db, tradeIdHash) { + return fmt.Errorf("not exist tradeId: %s, %d, %d", lendingBook.Hex(), time, tradeId) } - liquidationTime.removeTradeId(self.db, tradeIdHash) + liquidationTime.removeTradeId(ls.db, tradeIdHash) liquidationTime.subVolume(One) if liquidationTime.Volume().Sign() == 0 { - err := lendingExchangeState.getLiquidationTimeTrie(self.db).TryDelete(timeHash[:]) + err := lendingExchangeState.getLiquidationTimeTrie(ls.db).TryDelete(timeHash[:]) if err != nil { log.Warn("RemoveLiquidationTime getLiquidationTimeTrie.TryDelete", "err", err, "timeHash[:]", timeHash[:]) } @@ -641,33 +643,33 @@ func (self *LendingStateDB) RemoveLiquidationTime(lendingBook common.Hash, trade return nil } -func (self *LendingStateDB) GetLowestLiquidationTime(lendingBook common.Hash, time *big.Int) (*big.Int, []common.Hash) { +func (ls *LendingStateDB) GetLowestLiquidationTime(lendingBook common.Hash, time *big.Int) (*big.Int, []common.Hash) { liquidationData := []common.Hash{} - lendingExchangeState := self.getLendingExchange(lendingBook) + lendingExchangeState := ls.getLendingExchange(lendingBook) if lendingExchangeState == nil { return common.Big0, liquidationData } - lowestPriceHash, liquidationState := lendingExchangeState.getLowestLiquidationTime(self.db) + lowestPriceHash, liquidationState := lendingExchangeState.getLowestLiquidationTime(ls.db) lowestTime := new(big.Int).SetBytes(lowestPriceHash[:]) if liquidationState != nil && lowestTime.Sign() > 0 && lowestTime.Cmp(time) <= 0 { - liquidationData = liquidationState.getAllTradeIds(self.db) + liquidationData = liquidationState.getAllTradeIds(ls.db) } return lowestTime, liquidationData } -func (self *LendingStateDB) CancelLendingTrade(orderBook common.Hash, tradeId uint64) error { +func (ls *LendingStateDB) CancelLendingTrade(orderBook common.Hash, tradeId uint64) error { tradeIdHash := common.Uint64ToHash(tradeId) - stateObject := self.GetOrNewLendingExchangeObject(orderBook) + stateObject := ls.GetOrNewLendingExchangeObject(orderBook) if stateObject == nil { return fmt.Errorf("not found order book: %s", orderBook.Hex()) } - lendingTrade := stateObject.getLendingTrade(self.db, tradeIdHash) + lendingTrade := stateObject.getLendingTrade(ls.db, tradeIdHash) if lendingTrade == nil || lendingTrade.empty() { return fmt.Errorf("lending trade empty order book: %s , trade id : %s , trade id hash : %s", orderBook, tradeIdHash.Hex(), tradeIdHash.Hex()) } - self.journal = append(self.journal, cancelTrading{ + ls.journal = append(ls.journal, cancelTrading{ orderBook: orderBook, - order: self.GetLendingTrade(orderBook, tradeIdHash), + order: ls.GetLendingTrade(orderBook, tradeIdHash), }) lendingTrade.SetAmount(Zero) return nil diff --git a/bmt/bmt.go b/bmt/bmt.go index bba4f860394e..d03bc72d4e10 100644 --- a/bmt/bmt.go +++ b/bmt/bmt.go @@ -150,29 +150,29 @@ func NewTreePool(hasher BaseHasher, segmentCount, capacity int) *TreePool { } // Drain drains the pool uptil it has no more than n resources -func (self *TreePool) Drain(n int) { - self.lock.Lock() - defer self.lock.Unlock() - for len(self.c) > n { - <-self.c - self.count-- +func (tp *TreePool) Drain(n int) { + tp.lock.Lock() + defer tp.lock.Unlock() + for len(tp.c) > n { + <-tp.c + tp.count-- } } // Reserve is blocking until it returns an available Tree // it reuses free Trees or creates a new one if size is not reached -func (self *TreePool) Reserve() *Tree { - self.lock.Lock() - defer self.lock.Unlock() +func (tp *TreePool) Reserve() *Tree { + tp.lock.Lock() + defer tp.lock.Unlock() var t *Tree - if self.count == self.Capacity { - return <-self.c + if tp.count == tp.Capacity { + return <-tp.c } select { - case t = <-self.c: + case t = <-tp.c: default: - t = NewTree(self.hasher, self.SegmentSize, self.SegmentCount) - self.count++ + t = NewTree(tp.hasher, tp.SegmentSize, tp.SegmentCount) + tp.count++ } return t } @@ -180,8 +180,8 @@ func (self *TreePool) Reserve() *Tree { // Release gives back a Tree to the pool. // This Tree is guaranteed to be in reusable state // does not need locking -func (self *TreePool) Release(t *Tree) { - self.c <- t // can never fail but... +func (tp *TreePool) Release(t *Tree) { + tp.c <- t // can never fail but... } // Tree is a reusable control structure representing a BMT @@ -193,17 +193,17 @@ type Tree struct { } // Draw draws the BMT (badly) -func (self *Tree) Draw(hash []byte, d int) string { +func (t *Tree) Draw(hash []byte, d int) string { var left, right []string var anc []*Node - for i, n := range self.leaves { + for i, n := range t.leaves { left = append(left, fmt.Sprintf("%v", hashstr(n.left))) if i%2 == 0 { anc = append(anc, n.parent) } right = append(right, fmt.Sprintf("%v", hashstr(n.right))) } - anc = self.leaves + anc = t.leaves var hashes [][]string for l := 0; len(anc) > 0; l++ { var nodes []*Node @@ -277,42 +277,42 @@ func NewTree(hasher BaseHasher, segmentSize, segmentCount int) *Tree { // methods needed by hash.Hash // Size returns the size -func (self *Hasher) Size() int { - return self.size +func (ha *Hasher) Size() int { + return ha.size } // BlockSize returns the block size -func (self *Hasher) BlockSize() int { - return self.blocksize +func (ha *Hasher) BlockSize() int { + return ha.blocksize } // Sum returns the hash of the buffer // hash.Hash interface Sum method appends the byte slice to the underlying // data before it calculates and returns the hash of the chunk -func (self *Hasher) Sum(b []byte) (r []byte) { - t := self.bmt - i := self.cur +func (ha *Hasher) Sum(b []byte) (r []byte) { + t := ha.bmt + i := ha.cur n := t.leaves[i] j := i // must run strictly before all nodes calculate // datanodes are guaranteed to have a parent - if len(self.segment) > self.size && i > 0 && n.parent != nil { + if len(ha.segment) > ha.size && i > 0 && n.parent != nil { n = n.parent } else { i *= 2 } - d := self.finalise(n, i) - self.writeSegment(j, self.segment, d) - c := <-self.result - self.releaseTree() + d := ha.finalise(n, i) + ha.writeSegment(j, ha.segment, d) + c := <-ha.result + ha.releaseTree() // sha3(length + BMT(pure_chunk)) - if self.blockLength == nil { + if ha.blockLength == nil { return c } - res := self.pool.hasher() + res := ha.pool.hasher() res.Reset() - res.Write(self.blockLength) + res.Write(ha.blockLength) res.Write(c) return res.Sum(nil) } @@ -321,8 +321,8 @@ func (self *Hasher) Sum(b []byte) (r []byte) { // Hash waits for the hasher result and returns it // caller must call this on a BMT Hasher being written to -func (self *Hasher) Hash() []byte { - return <-self.result +func (ha *Hasher) Hash() []byte { + return <-ha.result } // Hasher implements the io.Writer interface @@ -330,16 +330,16 @@ func (self *Hasher) Hash() []byte { // Write fills the buffer to hash // with every full segment complete launches a hasher go routine // that shoots up the BMT -func (self *Hasher) Write(b []byte) (int, error) { +func (ha *Hasher) Write(b []byte) (int, error) { l := len(b) if l <= 0 { return 0, nil } - s := self.segment - i := self.cur - count := (self.count + 1) / 2 - need := self.count*self.size - self.cur*2*self.size - size := self.size + s := ha.segment + i := ha.cur + count := (ha.count + 1) / 2 + need := ha.count*ha.size - ha.cur*2*ha.size + size := ha.size if need > size { size *= 2 } @@ -356,7 +356,7 @@ func (self *Hasher) Write(b []byte) (int, error) { // read full segments and the last possibly partial segment for need > 0 && i < count-1 { // push all finished chunks we read - self.writeSegment(i, s, self.depth) + ha.writeSegment(i, s, ha.depth) need -= size if need < 0 { size += need @@ -365,8 +365,8 @@ func (self *Hasher) Write(b []byte) (int, error) { rest += size i++ } - self.segment = s - self.cur = i + ha.segment = s + ha.cur = i // otherwise, we can assume len(s) == 0, so all buffer is read and chunk is not yet full return l, nil } @@ -376,8 +376,8 @@ func (self *Hasher) Write(b []byte) (int, error) { // ReadFrom reads from io.Reader and appends to the data to hash using Write // it reads so that chunk to hash is maximum length or reader reaches EOF // caller must Reset the hasher prior to call -func (self *Hasher) ReadFrom(r io.Reader) (m int64, err error) { - bufsize := self.size*self.count - self.size*self.cur - len(self.segment) +func (ha *Hasher) ReadFrom(r io.Reader) (m int64, err error) { + bufsize := ha.size*ha.count - ha.size*ha.cur - len(ha.segment) buf := make([]byte, bufsize) var read int for { @@ -385,7 +385,7 @@ func (self *Hasher) ReadFrom(r io.Reader) (m int64, err error) { n, err = r.Read(buf) read += n if err == io.EOF || read == len(buf) { - hash := self.Sum(buf[:n]) + hash := ha.Sum(buf[:n]) if read == len(buf) { err = NewEOC(hash) } @@ -394,7 +394,7 @@ func (self *Hasher) ReadFrom(r io.Reader) (m int64, err error) { if err != nil { break } - _, err = self.Write(buf[:n]) + _, err = ha.Write(buf[:n]) if err != nil { break } @@ -403,9 +403,9 @@ func (self *Hasher) ReadFrom(r io.Reader) (m int64, err error) { } // Reset needs to be called before writing to the hasher -func (self *Hasher) Reset() { - self.getTree() - self.blockLength = nil +func (ha *Hasher) Reset() { + ha.getTree() + ha.blockLength = nil } // Hasher implements the SwarmHash interface @@ -413,53 +413,53 @@ func (self *Hasher) Reset() { // ResetWithLength needs to be called before writing to the hasher // the argument is supposed to be the byte slice binary representation of // the legth of the data subsumed under the hash -func (self *Hasher) ResetWithLength(l []byte) { - self.Reset() - self.blockLength = l +func (ha *Hasher) ResetWithLength(l []byte) { + ha.Reset() + ha.blockLength = l } // Release gives back the Tree to the pool whereby it unlocks // it resets tree, segment and index -func (self *Hasher) releaseTree() { - if self.bmt != nil { - n := self.bmt.leaves[self.cur] +func (ha *Hasher) releaseTree() { + if ha.bmt != nil { + n := ha.bmt.leaves[ha.cur] for ; n != nil; n = n.parent { n.unbalanced = false if n.parent != nil { n.root = false } } - self.pool.Release(self.bmt) - self.bmt = nil + ha.pool.Release(ha.bmt) + ha.bmt = nil } - self.cur = 0 - self.segment = nil + ha.cur = 0 + ha.segment = nil } -func (self *Hasher) writeSegment(i int, s []byte, d int) { - h := self.pool.hasher() - n := self.bmt.leaves[i] +func (ha *Hasher) writeSegment(i int, s []byte, d int) { + h := ha.pool.hasher() + n := ha.bmt.leaves[i] - if len(s) > self.size && n.parent != nil { + if len(s) > ha.size && n.parent != nil { go func() { h.Reset() h.Write(s) s = h.Sum(nil) if n.root { - self.result <- s + ha.result <- s return } - self.run(n.parent, h, d, n.index, s) + ha.run(n.parent, h, d, n.index, s) }() return } - go self.run(n, h, d, i*2, s) + go ha.run(n, h, d, i*2, s) } -func (self *Hasher) run(n *Node, h hash.Hash, d int, i int, s []byte) { +func (ha *Hasher) run(n *Node, h hash.Hash, d int, i int, s []byte) { isLeft := i%2 == 0 for { if isLeft { @@ -480,9 +480,9 @@ func (self *Hasher) run(n *Node, h hash.Hash, d int, i int, s []byte) { s = append(n.left, n.right...) } - self.hash = s + ha.hash = s if n.root { - self.result <- s + ha.result <- s return } @@ -493,20 +493,20 @@ func (self *Hasher) run(n *Node, h hash.Hash, d int, i int, s []byte) { } // getTree obtains a BMT resource by reserving one from the pool -func (self *Hasher) getTree() *Tree { - if self.bmt != nil { - return self.bmt +func (ha *Hasher) getTree() *Tree { + if ha.bmt != nil { + return ha.bmt } - t := self.pool.Reserve() - self.bmt = t + t := ha.pool.Reserve() + ha.bmt = t return t } // atomic bool toggle implementing a concurrent reusable 2-state object // atomic addint with %2 implements atomic bool toggle // it returns true if the toggler just put it in the active/waiting state -func (self *Node) toggle() bool { - return atomic.AddInt32(&self.state, 1)%2 == 1 +func (n *Node) toggle() bool { + return atomic.AddInt32(&n.state, 1)%2 == 1 } func hashstr(b []byte) string { @@ -526,7 +526,7 @@ func depth(n int) (d int) { // finalise is following the zigzags on the tree belonging // to the final datasegment -func (self *Hasher) finalise(n *Node, i int) (d int) { +func (ha *Hasher) finalise(n *Node, i int) (d int) { isLeft := i%2 == 0 for { // when the final segment's path is going via left segments @@ -551,8 +551,8 @@ type EOC struct { } // Error returns the error string -func (self *EOC) Error() string { - return fmt.Sprintf("hasher limit reached, chunk hash: %x", self.Hash) +func (e *EOC) Error() string { + return fmt.Sprintf("hasher limit reached, chunk hash: %x", e.Hash) } // NewEOC creates new end of chunk error with the hash diff --git a/cmd/utils/customflags.go b/cmd/utils/customflags.go index 5f7833daf992..2304678344f0 100644 --- a/cmd/utils/customflags.go +++ b/cmd/utils/customflags.go @@ -38,12 +38,12 @@ type DirectoryString struct { Value string } -func (self *DirectoryString) String() string { - return self.Value +func (ds *DirectoryString) String() string { + return ds.Value } -func (self *DirectoryString) Set(value string) error { - self.Value = expandPath(value) +func (ds *DirectoryString) Set(value string) error { + ds.Value = expandPath(value) return nil } @@ -55,12 +55,12 @@ type DirectoryFlag struct { Usage string } -func (self DirectoryFlag) String() string { +func (df DirectoryFlag) String() string { fmtString := "%s %v\t%v" - if len(self.Value.Value) > 0 { + if len(df.Value.Value) > 0 { fmtString = "%s \"%v\"\t%v" } - return fmt.Sprintf(fmtString, prefixedNames(self.Name), self.Value.Value, self.Usage) + return fmt.Sprintf(fmtString, prefixedNames(df.Name), df.Value.Value, df.Usage) } func eachName(longName string, fn func(string)) { @@ -73,9 +73,9 @@ func eachName(longName string, fn func(string)) { // called by cli library, grabs variable from environment (if in env) // and adds variable to flag set for parsing. -func (self DirectoryFlag) Apply(set *flag.FlagSet) { - eachName(self.Name, func(name string) { - set.Var(&self.Value, self.Name, self.Usage) +func (df DirectoryFlag) Apply(set *flag.FlagSet) { + eachName(df.Name, func(name string) { + set.Var(&df.Value, df.Name, df.Usage) }) } @@ -89,16 +89,16 @@ type textMarshalerVal struct { v TextMarshaler } -func (v textMarshalerVal) String() string { - if v.v == nil { +func (t textMarshalerVal) String() string { + if t.v == nil { return "" } - text, _ := v.v.MarshalText() + text, _ := t.v.MarshalText() return string(text) } -func (v textMarshalerVal) Set(s string) error { - return v.v.UnmarshalText([]byte(s)) +func (t textMarshalerVal) Set(s string) error { + return t.v.UnmarshalText([]byte(s)) } // TextMarshalerFlag wraps a TextMarshaler value. @@ -108,17 +108,17 @@ type TextMarshalerFlag struct { Usage string } -func (f TextMarshalerFlag) GetName() string { - return f.Name +func (t TextMarshalerFlag) GetName() string { + return t.Name } -func (f TextMarshalerFlag) String() string { - return fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage) +func (t TextMarshalerFlag) String() string { + return fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(t.Name), t.Value, t.Usage) } -func (f TextMarshalerFlag) Apply(set *flag.FlagSet) { - eachName(f.Name, func(name string) { - set.Var(textMarshalerVal{f.Value}, f.Name, f.Usage) +func (t TextMarshalerFlag) Apply(set *flag.FlagSet) { + eachName(t.Name, func(name string) { + set.Var(textMarshalerVal{t.Value}, t.Name, t.Usage) }) } @@ -142,37 +142,37 @@ type BigFlag struct { // bigValue turns *big.Int into a flag.Value type bigValue big.Int -func (b *bigValue) String() string { - if b == nil { +func (bv *bigValue) String() string { + if bv == nil { return "" } - return (*big.Int)(b).String() + return (*big.Int)(bv).String() } -func (b *bigValue) Set(s string) error { +func (bv *bigValue) Set(s string) error { int, ok := math.ParseBig256(s) if !ok { return errors.New("invalid integer syntax") } - *b = (bigValue)(*int) + *bv = (bigValue)(*int) return nil } -func (f BigFlag) GetName() string { - return f.Name +func (bf BigFlag) GetName() string { + return bf.Name } -func (f BigFlag) String() string { +func (bf BigFlag) String() string { fmtString := "%s %v\t%v" - if f.Value != nil { + if bf.Value != nil { fmtString = "%s \"%v\"\t%v" } - return fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage) + return fmt.Sprintf(fmtString, prefixedNames(bf.Name), bf.Value, bf.Usage) } -func (f BigFlag) Apply(set *flag.FlagSet) { - eachName(f.Name, func(name string) { - set.Var((*bigValue)(f.Value), f.Name, f.Usage) +func (bf BigFlag) Apply(set *flag.FlagSet) { + eachName(bf.Name, func(name string) { + set.Var((*bigValue)(bf.Value), bf.Name, bf.Usage) }) } @@ -207,12 +207,12 @@ func prefixedNames(fullName string) (prefixed string) { return } -func (self DirectoryFlag) GetName() string { - return self.Name +func (df DirectoryFlag) GetName() string { + return df.Name } -func (self *DirectoryFlag) Set(value string) { - self.Value.Value = value +func (df *DirectoryFlag) Set(value string) { + df.Value.Value = value } // Expands a file path diff --git a/contracts/ens/ens.go b/contracts/ens/ens.go index d12efa690c39..7c47865097b8 100644 --- a/contracts/ens/ens.go +++ b/contracts/ens/ens.go @@ -100,45 +100,45 @@ func ensNode(name string) common.Hash { return crypto.Keccak256Hash(parentNode[:], parentLabel[:]) } -func (self *ENS) getResolver(node [32]byte) (*contract.PublicResolverSession, error) { - resolverAddr, err := self.Resolver(node) +func (e *ENS) getResolver(node [32]byte) (*contract.PublicResolverSession, error) { + resolverAddr, err := e.Resolver(node) if err != nil { return nil, err } - resolver, err := contract.NewPublicResolver(resolverAddr, self.contractBackend) + resolver, err := contract.NewPublicResolver(resolverAddr, e.contractBackend) if err != nil { return nil, err } return &contract.PublicResolverSession{ Contract: resolver, - TransactOpts: self.TransactOpts, + TransactOpts: e.TransactOpts, }, nil } -func (self *ENS) getRegistrar(node [32]byte) (*contract.FIFSRegistrarSession, error) { - registrarAddr, err := self.Owner(node) +func (e *ENS) getRegistrar(node [32]byte) (*contract.FIFSRegistrarSession, error) { + registrarAddr, err := e.Owner(node) if err != nil { return nil, err } - registrar, err := contract.NewFIFSRegistrar(registrarAddr, self.contractBackend) + registrar, err := contract.NewFIFSRegistrar(registrarAddr, e.contractBackend) if err != nil { return nil, err } return &contract.FIFSRegistrarSession{ Contract: registrar, - TransactOpts: self.TransactOpts, + TransactOpts: e.TransactOpts, }, nil } // Resolve is a non-transactional call that returns the content hash associated with a name. -func (self *ENS) Resolve(name string) (common.Hash, error) { +func (e *ENS) Resolve(name string) (common.Hash, error) { node := ensNode(name) - resolver, err := self.getResolver(node) + resolver, err := e.getResolver(node) if err != nil { return common.Hash{}, err } @@ -153,26 +153,26 @@ func (self *ENS) Resolve(name string) (common.Hash, error) { // Register registers a new domain name for the caller, making them the owner of the new name. // Only works if the registrar for the parent domain implements the FIFS registrar protocol. -func (self *ENS) Register(name string) (*types.Transaction, error) { +func (e *ENS) Register(name string) (*types.Transaction, error) { parentNode, label := ensParentNode(name) - registrar, err := self.getRegistrar(parentNode) + registrar, err := e.getRegistrar(parentNode) if err != nil { return nil, err } - return registrar.Contract.Register(&self.TransactOpts, label, self.TransactOpts.From) + return registrar.Contract.Register(&e.TransactOpts, label, e.TransactOpts.From) } // SetContentHash sets the content hash associated with a name. Only works if the caller // owns the name, and the associated resolver implements a `setContent` function. -func (self *ENS) SetContentHash(name string, hash common.Hash) (*types.Transaction, error) { +func (e *ENS) SetContentHash(name string, hash common.Hash) (*types.Transaction, error) { node := ensNode(name) - resolver, err := self.getResolver(node) + resolver, err := e.getResolver(node) if err != nil { return nil, err } - opts := self.TransactOpts + opts := e.TransactOpts opts.GasLimit = 200000 return resolver.Contract.SetContent(&opts, node, hash) } diff --git a/core/state/dump.go b/core/state/dump.go index d99b580bc4d7..ba4bfee0e19f 100644 --- a/core/state/dump.go +++ b/core/state/dump.go @@ -39,15 +39,15 @@ type Dump struct { Accounts map[string]DumpAccount `json:"accounts"` } -func (self *StateDB) RawDump() Dump { +func (s *StateDB) RawDump() Dump { dump := Dump{ - Root: fmt.Sprintf("%x", self.trie.Hash()), + Root: fmt.Sprintf("%x", s.trie.Hash()), Accounts: make(map[string]DumpAccount), } - it := trie.NewIterator(self.trie.NodeIterator(nil)) + it := trie.NewIterator(s.trie.NodeIterator(nil)) for it.Next() { - addr := self.trie.GetKey(it.Key) + addr := s.trie.GetKey(it.Key) var data Account if err := rlp.DecodeBytes(it.Value, &data); err != nil { panic(err) @@ -59,20 +59,20 @@ func (self *StateDB) RawDump() Dump { Nonce: data.Nonce, Root: common.Bytes2Hex(data.Root[:]), CodeHash: common.Bytes2Hex(data.CodeHash), - Code: common.Bytes2Hex(obj.Code(self.db)), + Code: common.Bytes2Hex(obj.Code(s.db)), Storage: make(map[string]string), } - storageIt := trie.NewIterator(obj.getTrie(self.db).NodeIterator(nil)) + storageIt := trie.NewIterator(obj.getTrie(s.db).NodeIterator(nil)) for storageIt.Next() { - account.Storage[common.Bytes2Hex(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value) + account.Storage[common.Bytes2Hex(s.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value) } dump.Accounts[common.Bytes2Hex(addr)] = account } return dump } -func (self *StateDB) Dump() []byte { - json, err := json.MarshalIndent(self.RawDump(), "", " ") +func (s *StateDB) Dump() []byte { + json, err := json.MarshalIndent(s.RawDump(), "", " ") if err != nil { fmt.Println("dump err", err) } diff --git a/core/state/statedb.go b/core/state/statedb.go index 8a13f46dcbca..aa2495cf401e 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -86,19 +86,19 @@ type AccountInfo struct { StorageHash common.Hash } -func (self *StateDB) SubRefund(gas uint64) { - self.journal = append(self.journal, refundChange{ - prev: self.refund}) - if gas > self.refund { - panic(fmt.Sprintf("Refund counter below zero (gas: %d > refund: %d)", gas, self.refund)) +func (s *StateDB) SubRefund(gas uint64) { + s.journal = append(s.journal, refundChange{ + prev: s.refund}) + if gas > s.refund { + panic(fmt.Sprintf("Refund counter below zero (gas: %d > refund: %d)", gas, s.refund)) } - self.refund -= gas + s.refund -= gas } -func (self *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash { - stateObject := self.getStateObject(addr) +func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash { + stateObject := s.getStateObject(addr) if stateObject != nil { - return stateObject.GetCommittedState(self.db, hash) + return stateObject.GetCommittedState(s.db, hash) } return common.Hash{} } @@ -121,44 +121,44 @@ func New(root common.Hash, db Database) (*StateDB, error) { } // setError remembers the first non-nil error it is called with. -func (self *StateDB) setError(err error) { - if self.dbErr == nil { - self.dbErr = err +func (s *StateDB) setError(err error) { + if s.dbErr == nil { + s.dbErr = err } } -func (self *StateDB) Error() error { - return self.dbErr +func (s *StateDB) Error() error { + return s.dbErr } // Reset clears out all ephemeral state objects from the state db, but keeps // the underlying state trie to avoid reloading data for the next operations. -func (self *StateDB) Reset(root common.Hash) error { - tr, err := self.db.OpenTrie(root) +func (s *StateDB) Reset(root common.Hash) error { + tr, err := s.db.OpenTrie(root) if err != nil { return err } - self.trie = tr - self.stateObjects = make(map[common.Address]*stateObject) - self.stateObjectsDirty = make(map[common.Address]struct{}) - self.thash = common.Hash{} - self.txIndex = 0 - self.logs = make(map[common.Hash][]*types.Log) - self.logSize = 0 - self.preimages = make(map[common.Hash][]byte) - self.clearJournalAndRefund() - self.accessList = newAccessList() + s.trie = tr + s.stateObjects = make(map[common.Address]*stateObject) + s.stateObjectsDirty = make(map[common.Address]struct{}) + s.thash = common.Hash{} + s.txIndex = 0 + s.logs = make(map[common.Hash][]*types.Log) + s.logSize = 0 + s.preimages = make(map[common.Hash][]byte) + s.clearJournalAndRefund() + s.accessList = newAccessList() return nil } -func (self *StateDB) AddLog(log *types.Log) { - self.journal = append(self.journal, addLogChange{txhash: self.thash}) +func (s *StateDB) AddLog(log *types.Log) { + s.journal = append(s.journal, addLogChange{txhash: s.thash}) - log.TxHash = self.thash - log.TxIndex = uint(self.txIndex) - log.Index = self.logSize - self.logs[self.thash] = append(self.logs[self.thash], log) - self.logSize++ + log.TxHash = s.thash + log.TxIndex = uint(s.txIndex) + log.Index = s.logSize + s.logs[s.thash] = append(s.logs[s.thash], log) + s.logSize++ } func (s *StateDB) GetLogs(hash common.Hash, blockHash common.Hash) []*types.Log { @@ -169,58 +169,58 @@ func (s *StateDB) GetLogs(hash common.Hash, blockHash common.Hash) []*types.Log return logs } -func (self *StateDB) Logs() []*types.Log { +func (s *StateDB) Logs() []*types.Log { var logs []*types.Log - for _, lgs := range self.logs { + for _, lgs := range s.logs { logs = append(logs, lgs...) } return logs } // AddPreimage records a SHA3 preimage seen by the VM. -func (self *StateDB) AddPreimage(hash common.Hash, preimage []byte) { - if _, ok := self.preimages[hash]; !ok { - self.journal = append(self.journal, addPreimageChange{hash: hash}) +func (s *StateDB) AddPreimage(hash common.Hash, preimage []byte) { + if _, ok := s.preimages[hash]; !ok { + s.journal = append(s.journal, addPreimageChange{hash: hash}) pi := make([]byte, len(preimage)) copy(pi, preimage) - self.preimages[hash] = pi + s.preimages[hash] = pi } } // Preimages returns a list of SHA3 preimages that have been submitted. -func (self *StateDB) Preimages() map[common.Hash][]byte { - return self.preimages +func (s *StateDB) Preimages() map[common.Hash][]byte { + return s.preimages } -func (self *StateDB) AddRefund(gas uint64) { - self.journal = append(self.journal, refundChange{prev: self.refund}) - self.refund += gas +func (s *StateDB) AddRefund(gas uint64) { + s.journal = append(s.journal, refundChange{prev: s.refund}) + s.refund += gas } // Exist reports whether the given account address exists in the state. // Notably this also returns true for suicided accounts. -func (self *StateDB) Exist(addr common.Address) bool { - return self.getStateObject(addr) != nil +func (s *StateDB) Exist(addr common.Address) bool { + return s.getStateObject(addr) != nil } // Empty returns whether the state object is either non-existent // or empty according to the EIP161 specification (balance = nonce = code = 0) -func (self *StateDB) Empty(addr common.Address) bool { - so := self.getStateObject(addr) +func (s *StateDB) Empty(addr common.Address) bool { + so := s.getStateObject(addr) return so == nil || so.empty() } // Retrieve the balance from the given address or 0 if object not found -func (self *StateDB) GetBalance(addr common.Address) *big.Int { - stateObject := self.getStateObject(addr) +func (s *StateDB) GetBalance(addr common.Address) *big.Int { + stateObject := s.getStateObject(addr) if stateObject != nil { return stateObject.Balance() } return common.Big0 } -func (self *StateDB) GetNonce(addr common.Address) uint64 { - stateObject := self.getStateObject(addr) +func (s *StateDB) GetNonce(addr common.Address) uint64 { + stateObject := s.getStateObject(addr) if stateObject != nil { return stateObject.Nonce() } @@ -230,8 +230,8 @@ func (self *StateDB) GetNonce(addr common.Address) uint64 { // GetStorageRoot retrieves the storage root from the given address or empty // if object not found. -func (self *StateDB) GetStorageRoot(addr common.Address) common.Hash { - stateObject := self.getStateObject(addr) +func (s *StateDB) GetStorageRoot(addr common.Address) common.Hash { + stateObject := s.getStateObject(addr) if stateObject != nil { return stateObject.Root() } @@ -239,45 +239,45 @@ func (self *StateDB) GetStorageRoot(addr common.Address) common.Hash { } // TxIndex returns the current transaction index set by Prepare. -func (self *StateDB) TxIndex() int { - return self.txIndex +func (s *StateDB) TxIndex() int { + return s.txIndex } -func (self *StateDB) GetCode(addr common.Address) []byte { - stateObject := self.getStateObject(addr) +func (s *StateDB) GetCode(addr common.Address) []byte { + stateObject := s.getStateObject(addr) if stateObject != nil { - return stateObject.Code(self.db) + return stateObject.Code(s.db) } return nil } -func (self *StateDB) GetCodeSize(addr common.Address) int { - stateObject := self.getStateObject(addr) +func (s *StateDB) GetCodeSize(addr common.Address) int { + stateObject := s.getStateObject(addr) if stateObject == nil { return 0 } if stateObject.code != nil { return len(stateObject.code) } - size, err := self.db.ContractCodeSize(stateObject.addrHash, common.BytesToHash(stateObject.CodeHash())) + size, err := s.db.ContractCodeSize(stateObject.addrHash, common.BytesToHash(stateObject.CodeHash())) if err != nil { - self.setError(err) + s.setError(err) } return size } -func (self *StateDB) GetCodeHash(addr common.Address) common.Hash { - stateObject := self.getStateObject(addr) +func (s *StateDB) GetCodeHash(addr common.Address) common.Hash { + stateObject := s.getStateObject(addr) if stateObject == nil { return common.Hash{} } return common.BytesToHash(stateObject.CodeHash()) } -func (self *StateDB) GetAccountInfo(addr common.Address) *AccountInfo { +func (s *StateDB) GetAccountInfo(addr common.Address) *AccountInfo { result := AccountInfo{} - stateObject := self.getStateObject(addr) + stateObject := s.getStateObject(addr) if stateObject == nil { result.Balance = common.Big0 return &result @@ -286,7 +286,7 @@ func (self *StateDB) GetAccountInfo(addr common.Address) *AccountInfo { if stateObject.code != nil { result.CodeSize = len(stateObject.code) } else { - result.CodeSize, _ = self.db.ContractCodeSize(stateObject.addrHash, common.BytesToHash(stateObject.CodeHash())) + result.CodeSize, _ = s.db.ContractCodeSize(stateObject.addrHash, common.BytesToHash(stateObject.CodeHash())) } result.Nonce = stateObject.Nonce() result.Balance = stateObject.Balance() @@ -296,32 +296,32 @@ func (self *StateDB) GetAccountInfo(addr common.Address) *AccountInfo { return &result } -func (self *StateDB) GetState(addr common.Address, bhash common.Hash) common.Hash { - stateObject := self.getStateObject(addr) +func (s *StateDB) GetState(addr common.Address, bhash common.Hash) common.Hash { + stateObject := s.getStateObject(addr) if stateObject != nil { - return stateObject.GetState(self.db, bhash) + return stateObject.GetState(s.db, bhash) } return common.Hash{} } // Database retrieves the low level database supporting the lower level trie ops. -func (self *StateDB) Database() Database { - return self.db +func (s *StateDB) Database() Database { + return s.db } // StorageTrie returns the storage trie of an account. // The return value is a copy and is nil for non-existent accounts. -func (self *StateDB) StorageTrie(addr common.Address) Trie { - stateObject := self.getStateObject(addr) +func (s *StateDB) StorageTrie(addr common.Address) Trie { + stateObject := s.getStateObject(addr) if stateObject == nil { return nil } - cpy := stateObject.deepCopy(self, nil) - return cpy.updateTrie(self.db) + cpy := stateObject.deepCopy(s, nil) + return cpy.updateTrie(s.db) } -func (self *StateDB) HasSuicided(addr common.Address) bool { - stateObject := self.getStateObject(addr) +func (s *StateDB) HasSuicided(addr common.Address) bool { + stateObject := s.getStateObject(addr) if stateObject != nil { return stateObject.suicided } @@ -333,46 +333,46 @@ func (self *StateDB) HasSuicided(addr common.Address) bool { */ // AddBalance adds amount to the account associated with addr. -func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) { - stateObject := self.GetOrNewStateObject(addr) +func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) { + stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { stateObject.AddBalance(amount) } } // SubBalance subtracts amount from the account associated with addr. -func (self *StateDB) SubBalance(addr common.Address, amount *big.Int) { - stateObject := self.GetOrNewStateObject(addr) +func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) { + stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { stateObject.SubBalance(amount) } } -func (self *StateDB) SetBalance(addr common.Address, amount *big.Int) { - stateObject := self.GetOrNewStateObject(addr) +func (s *StateDB) SetBalance(addr common.Address, amount *big.Int) { + stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { stateObject.SetBalance(amount) } } -func (self *StateDB) SetNonce(addr common.Address, nonce uint64) { - stateObject := self.GetOrNewStateObject(addr) +func (s *StateDB) SetNonce(addr common.Address, nonce uint64) { + stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { stateObject.SetNonce(nonce) } } -func (self *StateDB) SetCode(addr common.Address, code []byte) { - stateObject := self.GetOrNewStateObject(addr) +func (s *StateDB) SetCode(addr common.Address, code []byte) { + stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { stateObject.SetCode(crypto.Keccak256Hash(code), code) } } -func (self *StateDB) SetState(addr common.Address, key, value common.Hash) { - stateObject := self.GetOrNewStateObject(addr) +func (s *StateDB) SetState(addr common.Address, key, value common.Hash) { + stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { - stateObject.SetState(self.db, key, value) + stateObject.SetState(s.db, key, value) } } @@ -390,12 +390,12 @@ func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common // // The account's state object is still available until the state is committed, // getStateObject will return a non-nil account after Suicide. -func (self *StateDB) Suicide(addr common.Address) bool { - stateObject := self.getStateObject(addr) +func (s *StateDB) Suicide(addr common.Address) bool { + stateObject := s.getStateObject(addr) if stateObject == nil { return false } - self.journal = append(self.journal, suicideChange{ + s.journal = append(s.journal, suicideChange{ account: &addr, prev: stateObject.suicided, prevbalance: new(big.Int).Set(stateObject.Balance()), @@ -411,34 +411,34 @@ func (self *StateDB) Suicide(addr common.Address) bool { // // updateStateObject writes the given object to the trie. -func (self *StateDB) updateStateObject(stateObject *stateObject) { +func (s *StateDB) updateStateObject(stateObject *stateObject) { addr := stateObject.Address() data, err := rlp.EncodeToBytes(stateObject) if err != nil { panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err)) } - self.setError(self.trie.TryUpdate(addr[:], data)) + s.setError(s.trie.TryUpdate(addr[:], data)) } // deleteStateObject removes the given object from the state trie. -func (self *StateDB) deleteStateObject(stateObject *stateObject) { +func (s *StateDB) deleteStateObject(stateObject *stateObject) { stateObject.deleted = true addr := stateObject.Address() - self.setError(self.trie.TryDelete(addr[:])) + s.setError(s.trie.TryDelete(addr[:])) } // DeleteAddress removes the address from the state trie. -func (self *StateDB) DeleteAddress(addr common.Address) { - stateObject := self.getStateObject(addr) +func (s *StateDB) DeleteAddress(addr common.Address) { + stateObject := s.getStateObject(addr) if stateObject != nil && !stateObject.deleted { - self.deleteStateObject(stateObject) + s.deleteStateObject(stateObject) } } // Retrieve a state object given my the address. Returns nil if not found. -func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) { +func (s *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) { // Prefer 'live' objects. - if obj := self.stateObjects[addr]; obj != nil { + if obj := s.stateObjects[addr]; obj != nil { if obj.deleted { return nil } @@ -446,9 +446,9 @@ func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObje } // Load the object from the database. - enc, err := self.trie.TryGet(addr[:]) + enc, err := s.trie.TryGet(addr[:]) if len(enc) == 0 { - self.setError(err) + s.setError(err) return nil } var data Account @@ -457,42 +457,42 @@ func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObje return nil } // Insert into the live set. - obj := newObject(self, addr, data, self.MarkStateObjectDirty) - self.setStateObject(obj) + obj := newObject(s, addr, data, s.MarkStateObjectDirty) + s.setStateObject(obj) return obj } -func (self *StateDB) setStateObject(object *stateObject) { - self.stateObjects[object.Address()] = object +func (s *StateDB) setStateObject(object *stateObject) { + s.stateObjects[object.Address()] = object } // Retrieve a state object or create a new state object if nil. -func (self *StateDB) GetOrNewStateObject(addr common.Address) *stateObject { - stateObject := self.getStateObject(addr) +func (s *StateDB) GetOrNewStateObject(addr common.Address) *stateObject { + stateObject := s.getStateObject(addr) if stateObject == nil || stateObject.deleted { - stateObject, _ = self.createObject(addr) + stateObject, _ = s.createObject(addr) } return stateObject } // MarkStateObjectDirty adds the specified object to the dirty map to avoid costly // state object cache iteration to find a handful of modified ones. -func (self *StateDB) MarkStateObjectDirty(addr common.Address) { - self.stateObjectsDirty[addr] = struct{}{} +func (s *StateDB) MarkStateObjectDirty(addr common.Address) { + s.stateObjectsDirty[addr] = struct{}{} } // createObject creates a new state object. If there is an existing account with // the given address, it is overwritten and returned as the second return value. -func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) { - prev = self.getStateObject(addr) - newobj = newObject(self, addr, Account{}, self.MarkStateObjectDirty) +func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) { + prev = s.getStateObject(addr) + newobj = newObject(s, addr, Account{}, s.MarkStateObjectDirty) newobj.setNonce(0) // sets the object to dirty if prev == nil { - self.journal = append(self.journal, createObjectChange{account: &addr}) + s.journal = append(s.journal, createObjectChange{account: &addr}) } else { - self.journal = append(self.journal, resetObjectChange{prev: prev}) + s.journal = append(s.journal, resetObjectChange{prev: prev}) } - self.setStateObject(newobj) + s.setStateObject(newobj) return newobj, prev } @@ -506,8 +506,8 @@ func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObjec // 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1) // // Carrying over the balance ensures that Ether doesn't disappear. -func (self *StateDB) CreateAccount(addr common.Address) { - new, prev := self.createObject(addr) +func (s *StateDB) CreateAccount(addr common.Address) { + new, prev := s.createObject(addr) if prev != nil { new.setBalance(prev.data.Balance) } @@ -537,29 +537,29 @@ func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common // Copy creates a deep, independent copy of the state. // Snapshots of the copied state cannot be applied to the copy. -func (self *StateDB) Copy() *StateDB { - self.lock.Lock() - defer self.lock.Unlock() +func (s *StateDB) Copy() *StateDB { + s.lock.Lock() + defer s.lock.Unlock() // Copy all the basic fields, initialize the memory ones state := &StateDB{ - db: self.db, - trie: self.db.CopyTrie(self.trie), - stateObjects: make(map[common.Address]*stateObject, len(self.stateObjectsDirty)), - stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)), - refund: self.refund, - logs: make(map[common.Hash][]*types.Log, len(self.logs)), - logSize: self.logSize, + db: s.db, + trie: s.db.CopyTrie(s.trie), + stateObjects: make(map[common.Address]*stateObject, len(s.stateObjectsDirty)), + stateObjectsDirty: make(map[common.Address]struct{}, len(s.stateObjectsDirty)), + refund: s.refund, + logs: make(map[common.Hash][]*types.Log, len(s.logs)), + logSize: s.logSize, preimages: make(map[common.Hash][]byte), } // Copy the dirty states, logs, and preimages - for addr := range self.stateObjectsDirty { - state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state, state.MarkStateObjectDirty) + for addr := range s.stateObjectsDirty { + state.stateObjects[addr] = s.stateObjects[addr].deepCopy(state, state.MarkStateObjectDirty) state.stateObjectsDirty[addr] = struct{}{} } // Deep copy the logs occurred in the scope of block - for hash, logs := range self.logs { + for hash, logs := range s.logs { cpy := make([]*types.Log, len(logs)) for i, l := range logs { cpy[i] = new(types.Log) @@ -568,7 +568,7 @@ func (self *StateDB) Copy() *StateDB { state.logs[hash] = cpy } - for hash, preimage := range self.preimages { + for hash, preimage := range s.preimages { state.preimages[hash] = preimage } // Do we need to copy the access list? In practice: No. At the start of a @@ -576,42 +576,42 @@ func (self *StateDB) Copy() *StateDB { // _between_ transactions/blocks, never in the middle of a transaction. // However, it doesn't cost us much to copy an empty list, so we do it anyway // to not blow up if we ever decide copy it in the middle of a transaction - state.accessList = self.accessList.Copy() + state.accessList = s.accessList.Copy() return state } // Snapshot returns an identifier for the current revision of the state. -func (self *StateDB) Snapshot() int { - id := self.nextRevisionId - self.nextRevisionId++ - self.validRevisions = append(self.validRevisions, revision{id, len(self.journal)}) +func (s *StateDB) Snapshot() int { + id := s.nextRevisionId + s.nextRevisionId++ + s.validRevisions = append(s.validRevisions, revision{id, len(s.journal)}) return id } // RevertToSnapshot reverts all state changes made since the given revision. -func (self *StateDB) RevertToSnapshot(revid int) { +func (s *StateDB) RevertToSnapshot(revid int) { // Find the snapshot in the stack of valid snapshots. - idx := sort.Search(len(self.validRevisions), func(i int) bool { - return self.validRevisions[i].id >= revid + idx := sort.Search(len(s.validRevisions), func(i int) bool { + return s.validRevisions[i].id >= revid }) - if idx == len(self.validRevisions) || self.validRevisions[idx].id != revid { + if idx == len(s.validRevisions) || s.validRevisions[idx].id != revid { panic(fmt.Errorf("revision id %v cannot be reverted", revid)) } - snapshot := self.validRevisions[idx].journalIndex + snapshot := s.validRevisions[idx].journalIndex // Replay the journal to undo changes. - for i := len(self.journal) - 1; i >= snapshot; i-- { - self.journal[i].undo(self) + for i := len(s.journal) - 1; i >= snapshot; i-- { + s.journal[i].undo(s) } - self.journal = self.journal[:snapshot] + s.journal = s.journal[:snapshot] // Remove invalidated snapshots from the stack. - self.validRevisions = self.validRevisions[:idx] + s.validRevisions = s.validRevisions[:idx] } // GetRefund returns the current value of the refund counter. -func (self *StateDB) GetRefund() uint64 { - return self.refund +func (s *StateDB) GetRefund() uint64 { + return s.refund } // Finalise finalises the state by removing the self destructed objects diff --git a/core/types/block.go b/core/types/block.go index 139f641bdba5..4f973e79c308 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -450,10 +450,10 @@ type Blocks []*Block type BlockBy func(b1, b2 *Block) bool -func (self BlockBy) Sort(blocks Blocks) { +func (bb BlockBy) Sort(blocks Blocks) { bs := blockSorter{ blocks: blocks, - by: self, + by: bb, } sort.Sort(bs) } @@ -463,10 +463,12 @@ type blockSorter struct { by func(b1, b2 *Block) bool } -func (self blockSorter) Len() int { return len(self.blocks) } -func (self blockSorter) Swap(i, j int) { - self.blocks[i], self.blocks[j] = self.blocks[j], self.blocks[i] +func (bs blockSorter) Len() int { return len(bs.blocks) } + +func (bs blockSorter) Swap(i, j int) { + bs.blocks[i], bs.blocks[j] = bs.blocks[j], bs.blocks[i] } -func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) } + +func (bs blockSorter) Less(i, j int) bool { return bs.by(bs.blocks[i], bs.blocks[j]) } func Number(b1, b2 *Block) bool { return b1.header.Number.Cmp(b2.header.Number) < 0 } diff --git a/eth/backend.go b/eth/backend.go index e3a7490207d5..3b6138bb9633 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -103,9 +103,9 @@ type Ethereum struct { Lending *XDCxlending.Lending } -func (s *Ethereum) AddLesServer(ls LesServer) { - s.lesServer = ls - ls.SetBloomBitsIndexer(s.bloomIndexer) +func (e *Ethereum) AddLesServer(ls LesServer) { + e.lesServer = ls + ls.SetBloomBitsIndexer(e.bloomIndexer) } // New creates a new Ethereum object (including the @@ -371,80 +371,80 @@ func CreateConsensusEngine(ctx *node.ServiceContext, config *ethash.Config, chai // APIs returns the collection of RPC services the ethereum package offers. // NOTE, some of these services probably need to be moved to somewhere else. -func (s *Ethereum) APIs() []rpc.API { - apis := ethapi.GetAPIs(s.ApiBackend, s.BlockChain()) +func (e *Ethereum) APIs() []rpc.API { + apis := ethapi.GetAPIs(e.ApiBackend, e.BlockChain()) // Append any APIs exposed explicitly by the consensus engine - apis = append(apis, s.engine.APIs(s.BlockChain())...) + apis = append(apis, e.engine.APIs(e.BlockChain())...) // Append all the local APIs and return return append(apis, []rpc.API{ { Namespace: "eth", Version: "1.0", - Service: NewPublicEthereumAPI(s), + Service: NewPublicEthereumAPI(e), Public: true, }, { Namespace: "eth", Version: "1.0", - Service: NewPublicMinerAPI(s), + Service: NewPublicMinerAPI(e), Public: true, }, { Namespace: "eth", Version: "1.0", - Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), + Service: downloader.NewPublicDownloaderAPI(e.protocolManager.downloader, e.eventMux), Public: true, }, { Namespace: "miner", Version: "1.0", - Service: NewPrivateMinerAPI(s), + Service: NewPrivateMinerAPI(e), Public: false, }, { Namespace: "eth", Version: "1.0", - Service: filters.NewFilterAPI(filters.NewFilterSystem(s.ApiBackend, filters.Config{LogCacheSize: s.config.FilterLogCacheSize}), false), + Service: filters.NewFilterAPI(filters.NewFilterSystem(e.ApiBackend, filters.Config{LogCacheSize: e.config.FilterLogCacheSize}), false), Public: true, }, { Namespace: "admin", Version: "1.0", - Service: NewPrivateAdminAPI(s), + Service: NewPrivateAdminAPI(e), }, { Namespace: "debug", Version: "1.0", - Service: NewPublicDebugAPI(s), + Service: NewPublicDebugAPI(e), Public: true, }, { Namespace: "debug", Version: "1.0", - Service: NewPrivateDebugAPI(s.chainConfig, s), + Service: NewPrivateDebugAPI(e.chainConfig, e), }, { Namespace: "net", Version: "1.0", - Service: s.netRPCService, + Service: e.netRPCService, Public: true, }, }...) } -func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) { - s.blockchain.ResetWithGenesisBlock(gb) +func (e *Ethereum) ResetWithGenesisBlock(gb *types.Block) { + e.blockchain.ResetWithGenesisBlock(gb) } -func (s *Ethereum) Etherbase() (eb common.Address, err error) { - s.lock.RLock() - etherbase := s.etherbase - s.lock.RUnlock() +func (e *Ethereum) Etherbase() (eb common.Address, err error) { + e.lock.RLock() + etherbase := e.etherbase + e.lock.RUnlock() if etherbase != (common.Address{}) { return etherbase, nil } - if wallets := s.AccountManager().Wallets(); len(wallets) > 0 { + if wallets := e.AccountManager().Wallets(); len(wallets) > 0 { if accounts := wallets[0].Accounts(); len(accounts) > 0 { etherbase := accounts[0].Address - s.lock.Lock() - s.etherbase = etherbase - s.lock.Unlock() + e.lock.Lock() + e.etherbase = etherbase + e.lock.Unlock() log.Info("Etherbase automatically configured", "address", etherbase) return etherbase, nil @@ -454,25 +454,25 @@ func (s *Ethereum) Etherbase() (eb common.Address, err error) { } // set in js console via admin interface or wrapper from cli flags -func (self *Ethereum) SetEtherbase(etherbase common.Address) { - self.lock.Lock() - self.etherbase = etherbase - self.lock.Unlock() +func (e *Ethereum) SetEtherbase(etherbase common.Address) { + e.lock.Lock() + e.etherbase = etherbase + e.lock.Unlock() - self.miner.SetEtherbase(etherbase) + e.miner.SetEtherbase(etherbase) } // ValidateMasternode checks if node's address is in set of masternodes -func (s *Ethereum) ValidateMasternode() (bool, error) { - eb, err := s.Etherbase() +func (e *Ethereum) ValidateMasternode() (bool, error) { + eb, err := e.Etherbase() if err != nil { return false, err } - if s.chainConfig.XDPoS != nil { + if e.chainConfig.XDPoS != nil { //check if miner's wallet is in set of validators - c := s.engine.(*XDPoS.XDPoS) + c := e.engine.(*XDPoS.XDPoS) - authorized := c.IsAuthorisedAddress(s.blockchain, s.blockchain.CurrentHeader(), eb) + authorized := c.IsAuthorisedAddress(e.blockchain, e.blockchain.CurrentHeader(), eb) if !authorized { //This miner doesn't belong to set of validators return false, nil @@ -483,14 +483,14 @@ func (s *Ethereum) ValidateMasternode() (bool, error) { return true, nil } -func (s *Ethereum) StartStaking(local bool) error { - eb, err := s.Etherbase() +func (e *Ethereum) StartStaking(local bool) error { + eb, err := e.Etherbase() if err != nil { log.Error("Cannot start mining without etherbase", "err", err) return fmt.Errorf("etherbase missing: %v", err) } - if XDPoS, ok := s.engine.(*XDPoS.XDPoS); ok { - wallet, err := s.accountManager.Find(accounts.Account{Address: eb}) + if XDPoS, ok := e.engine.(*XDPoS.XDPoS); ok { + wallet, err := e.accountManager.Find(accounts.Account{Address: eb}) if wallet == nil || err != nil { log.Error("Etherbase account unavailable locally", "address", eb, "err", err) return fmt.Errorf("signer missing: %v", err) @@ -502,102 +502,104 @@ func (s *Ethereum) StartStaking(local bool) error { // mechanism introduced to speed sync times. CPU mining on mainnet is ludicrous // so noone will ever hit this path, whereas marking sync done on CPU mining // will ensure that private networks work in single miner mode too. - atomic.StoreUint32(&s.protocolManager.acceptTxs, 1) + atomic.StoreUint32(&e.protocolManager.acceptTxs, 1) } - go s.miner.Start(eb) + go e.miner.Start(eb) return nil } -func (s *Ethereum) StopStaking() { - s.miner.Stop() +func (e *Ethereum) StopStaking() { + e.miner.Stop() } -func (s *Ethereum) IsStaking() bool { return s.miner.Mining() } -func (s *Ethereum) Miner() *miner.Miner { return s.miner } - -func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager } -func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain } -func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } -func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } -func (s *Ethereum) Engine() consensus.Engine { return s.engine } -func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb } -func (s *Ethereum) IsListening() bool { return true } // Always listening -func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } -func (s *Ethereum) NetVersion() uint64 { return s.networkId } -func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } + +func (e *Ethereum) IsStaking() bool { return e.miner.Mining() } +func (e *Ethereum) Miner() *miner.Miner { return e.miner } + +func (e *Ethereum) AccountManager() *accounts.Manager { return e.accountManager } +func (e *Ethereum) BlockChain() *core.BlockChain { return e.blockchain } +func (e *Ethereum) TxPool() *core.TxPool { return e.txPool } +func (e *Ethereum) EventMux() *event.TypeMux { return e.eventMux } +func (e *Ethereum) Engine() consensus.Engine { return e.engine } +func (e *Ethereum) ChainDb() ethdb.Database { return e.chainDb } +func (e *Ethereum) IsListening() bool { return true } // Always listening +func (e *Ethereum) EthVersion() int { return int(e.protocolManager.SubProtocols[0].Version) } +func (e *Ethereum) NetVersion() uint64 { return e.networkId } +func (e *Ethereum) Downloader() *downloader.Downloader { return e.protocolManager.downloader } // Protocols implements node.Service, returning all the currently configured // network protocols to start. -func (s *Ethereum) Protocols() []p2p.Protocol { - if s.lesServer == nil { - return s.protocolManager.SubProtocols +func (e *Ethereum) Protocols() []p2p.Protocol { + if e.lesServer == nil { + return e.protocolManager.SubProtocols } - return append(s.protocolManager.SubProtocols, s.lesServer.Protocols()...) + return append(e.protocolManager.SubProtocols, e.lesServer.Protocols()...) } // Start implements node.Service, starting all internal goroutines needed by the // Ethereum protocol implementation. -func (s *Ethereum) Start(srvr *p2p.Server) error { +func (e *Ethereum) Start(srvr *p2p.Server) error { // Start the bloom bits servicing goroutines - s.startBloomHandlers() + e.startBloomHandlers() // Start the RPC service - s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion()) + e.netRPCService = ethapi.NewPublicNetAPI(srvr, e.NetVersion()) // Figure out a max peers count based on the server limits maxPeers := srvr.MaxPeers - if s.config.LightServ > 0 { - if s.config.LightPeers >= srvr.MaxPeers { - return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", s.config.LightPeers, srvr.MaxPeers) + if e.config.LightServ > 0 { + if e.config.LightPeers >= srvr.MaxPeers { + return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", e.config.LightPeers, srvr.MaxPeers) } - maxPeers -= s.config.LightPeers + maxPeers -= e.config.LightPeers } // Start the networking layer and the light server if requested - s.protocolManager.Start(maxPeers) - if s.lesServer != nil { - s.lesServer.Start(srvr) + e.protocolManager.Start(maxPeers) + if e.lesServer != nil { + e.lesServer.Start(srvr) } return nil } -func (s *Ethereum) SaveData() { - s.blockchain.SaveData() + +func (e *Ethereum) SaveData() { + e.blockchain.SaveData() } // Stop implements node.Service, terminating all internal goroutines used by the // Ethereum protocol. -func (s *Ethereum) Stop() error { - s.bloomIndexer.Close() - s.blockchain.Stop() - s.protocolManager.Stop() - if s.lesServer != nil { - s.lesServer.Stop() +func (e *Ethereum) Stop() error { + e.bloomIndexer.Close() + e.blockchain.Stop() + e.protocolManager.Stop() + if e.lesServer != nil { + e.lesServer.Stop() } - s.txPool.Stop() - s.miner.Stop() - s.eventMux.Stop() + e.txPool.Stop() + e.miner.Stop() + e.eventMux.Stop() - s.chainDb.Close() - close(s.shutdownChan) + e.chainDb.Close() + close(e.shutdownChan) return nil } -func (s *Ethereum) GetPeer() int { - return len(s.protocolManager.peers.peers) +func (e *Ethereum) GetPeer() int { + return len(e.protocolManager.peers.peers) } -func (s *Ethereum) GetXDCX() *XDCx.XDCX { - return s.XDCX +func (e *Ethereum) GetXDCX() *XDCx.XDCX { + return e.XDCX } -func (s *Ethereum) OrderPool() *core.OrderPool { - return s.orderPool +func (e *Ethereum) OrderPool() *core.OrderPool { + return e.orderPool } -func (s *Ethereum) GetXDCXLending() *XDCxlending.Lending { - return s.Lending +func (e *Ethereum) GetXDCXLending() *XDCxlending.Lending { + return e.Lending } // LendingPool geth eth lending pool -func (s *Ethereum) LendingPool() *core.LendingPool { - return s.lendingPool +func (e *Ethereum) LendingPool() *core.LendingPool { + return e.lendingPool } diff --git a/eth/handler.go b/eth/handler.go index 76733fb8d863..913d7f07b395 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -266,9 +266,11 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne func (pm *ProtocolManager) addOrderPoolProtocol(orderpool orderPool) { pm.orderpool = orderpool } + func (pm *ProtocolManager) addLendingPoolProtocol(lendingpool lendingPool) { pm.lendingpool = lendingpool } + func (pm *ProtocolManager) removePeer(id string) { // Short circuit if the peer was already removed peer := pm.peers.Peer(id) @@ -1037,12 +1039,12 @@ func (pm *ProtocolManager) LendingBroadcastTx(hash common.Hash, tx *types.Lendin } // minedBroadcastLoop broadcast loop -func (self *ProtocolManager) minedBroadcastLoop() { +func (pm *ProtocolManager) minedBroadcastLoop() { // automatically stops if unsubscribe - for obj := range self.minedBlockSub.Chan() { + for obj := range pm.minedBlockSub.Chan() { switch ev := obj.Data.(type) { case core.NewMinedBlockEvent: - self.BroadcastBlock(ev.Block, true) // First propagate block to peers + pm.BroadcastBlock(ev.Block, true) // First propagate block to peers //self.BroadcastBlock(ev.Block, false) // Only then announce to the rest } } @@ -1062,34 +1064,34 @@ func (pm *ProtocolManager) txBroadcastLoop() { } // orderTxBroadcastLoop broadcast order -func (self *ProtocolManager) orderTxBroadcastLoop() { - if self.orderTxSub == nil { +func (pm *ProtocolManager) orderTxBroadcastLoop() { + if pm.orderTxSub == nil { return } for { select { - case event := <-self.orderTxCh: - self.OrderBroadcastTx(event.Tx.Hash(), event.Tx) + case event := <-pm.orderTxCh: + pm.OrderBroadcastTx(event.Tx.Hash(), event.Tx) // Err() channel will be closed when unsubscribing. - case <-self.orderTxSub.Err(): + case <-pm.orderTxSub.Err(): return } } } // lendingTxBroadcastLoop broadcast order -func (self *ProtocolManager) lendingTxBroadcastLoop() { - if self.lendingTxSub == nil { +func (pm *ProtocolManager) lendingTxBroadcastLoop() { + if pm.lendingTxSub == nil { return } for { select { - case event := <-self.lendingTxCh: - self.LendingBroadcastTx(event.Tx.Hash(), event.Tx) + case event := <-pm.lendingTxCh: + pm.LendingBroadcastTx(event.Tx.Hash(), event.Tx) // Err() channel will be closed when unsubscribing. - case <-self.lendingTxSub.Err(): + case <-pm.lendingTxSub.Err(): return } } @@ -1106,13 +1108,13 @@ type NodeInfo struct { } // NodeInfo retrieves some protocol metadata about the running host node. -func (self *ProtocolManager) NodeInfo() *NodeInfo { - currentBlock := self.blockchain.CurrentBlock() +func (pm *ProtocolManager) NodeInfo() *NodeInfo { + currentBlock := pm.blockchain.CurrentBlock() return &NodeInfo{ - Network: self.networkId, - Difficulty: self.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()), - Genesis: self.blockchain.Genesis().Hash(), - Config: self.blockchain.Config(), + Network: pm.networkId, + Difficulty: pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()), + Genesis: pm.blockchain.Genesis().Hash(), + Config: pm.blockchain.Config(), Head: currentBlock.Hash(), } } diff --git a/event/filter/filter.go b/event/filter/filter.go index b1fbf30ee4b2..a6fe46d6a060 100644 --- a/event/filter/filter.go +++ b/event/filter/filter.go @@ -45,37 +45,37 @@ func New() *Filters { } } -func (self *Filters) Start() { - go self.loop() +func (f *Filters) Start() { + go f.loop() } -func (self *Filters) Stop() { - close(self.quit) +func (f *Filters) Stop() { + close(f.quit) } -func (self *Filters) Notify(filter Filter, data interface{}) { - self.ch <- FilterEvent{filter, data} +func (f *Filters) Notify(filter Filter, data interface{}) { + f.ch <- FilterEvent{filter, data} } -func (self *Filters) Install(watcher Filter) int { - self.watchers[self.id] = watcher - self.id++ +func (f *Filters) Install(watcher Filter) int { + f.watchers[f.id] = watcher + f.id++ - return self.id - 1 + return f.id - 1 } -func (self *Filters) Uninstall(id int) { - delete(self.watchers, id) +func (f *Filters) Uninstall(id int) { + delete(f.watchers, id) } -func (self *Filters) loop() { +func (f *Filters) loop() { out: for { select { - case <-self.quit: + case <-f.quit: break out - case event := <-self.ch: - for _, watcher := range self.watchers { + case event := <-f.ch: + for _, watcher := range f.watchers { if reflect.TypeOf(watcher) == reflect.TypeOf(event.filter) { if watcher.Compare(event.filter) { watcher.Trigger(event.data) @@ -86,10 +86,10 @@ out: } } -func (self *Filters) Match(a, b Filter) bool { +func (f *Filters) Match(a, b Filter) bool { return reflect.TypeOf(a) == reflect.TypeOf(b) && a.Compare(b) } -func (self *Filters) Get(i int) Filter { - return self.watchers[i] +func (f *Filters) Get(i int) Filter { + return f.watchers[i] } diff --git a/event/filter/generic_filter.go b/event/filter/generic_filter.go index d679b8bfa89e..b641f5f070d2 100644 --- a/event/filter/generic_filter.go +++ b/event/filter/generic_filter.go @@ -23,18 +23,18 @@ type Generic struct { Fn func(data interface{}) } -// self = registered, f = incoming -func (self Generic) Compare(f Filter) bool { +// g = registered, f = incoming +func (g Generic) Compare(f Filter) bool { var strMatch, dataMatch = true, true filter := f.(Generic) - if (len(self.Str1) > 0 && filter.Str1 != self.Str1) || - (len(self.Str2) > 0 && filter.Str2 != self.Str2) || - (len(self.Str3) > 0 && filter.Str3 != self.Str3) { + if (len(g.Str1) > 0 && filter.Str1 != g.Str1) || + (len(g.Str2) > 0 && filter.Str2 != g.Str2) || + (len(g.Str3) > 0 && filter.Str3 != g.Str3) { strMatch = false } - for k := range self.Data { + for k := range g.Data { if _, ok := filter.Data[k]; !ok { return false } @@ -43,6 +43,6 @@ func (self Generic) Compare(f Filter) bool { return strMatch && dataMatch } -func (self Generic) Trigger(data interface{}) { - self.Fn(data) +func (g Generic) Trigger(data interface{}) { + g.Fn(data) } diff --git a/les/flowcontrol/manager.go b/les/flowcontrol/manager.go index 571b87bdb901..e22cd5ae92b6 100644 --- a/les/flowcontrol/manager.go +++ b/les/flowcontrol/manager.go @@ -35,35 +35,35 @@ type cmNode struct { finishRecharge mclock.AbsTime } -func (node *cmNode) update(time mclock.AbsTime) { - dt := int64(time - node.lastUpdate) - node.rcValue += node.rcDelta * dt / rcConst - node.lastUpdate = time - if node.recharging && time >= node.finishRecharge { - node.recharging = false - node.rcDelta = 0 - node.rcValue = 0 +func (n *cmNode) update(time mclock.AbsTime) { + dt := int64(time - n.lastUpdate) + n.rcValue += n.rcDelta * dt / rcConst + n.lastUpdate = time + if n.recharging && time >= n.finishRecharge { + n.recharging = false + n.rcDelta = 0 + n.rcValue = 0 } } -func (node *cmNode) set(serving bool, simReqCnt, sumWeight uint64) { - if node.serving && !serving { - node.recharging = true - sumWeight += node.rcWeight +func (n *cmNode) set(serving bool, simReqCnt, sumWeight uint64) { + if n.serving && !serving { + n.recharging = true + sumWeight += n.rcWeight } - node.serving = serving - if node.recharging && serving { - node.recharging = false - sumWeight -= node.rcWeight + n.serving = serving + if n.recharging && serving { + n.recharging = false + sumWeight -= n.rcWeight } - node.rcDelta = 0 + n.rcDelta = 0 if serving { - node.rcDelta = int64(rcConst / simReqCnt) + n.rcDelta = int64(rcConst / simReqCnt) } - if node.recharging { - node.rcDelta = -int64(node.node.cm.rcRecharge * node.rcWeight / sumWeight) - node.finishRecharge = node.lastUpdate + mclock.AbsTime(node.rcValue*rcConst/(-node.rcDelta)) + if n.recharging { + n.rcDelta = -int64(n.node.cm.rcRecharge * n.rcWeight / sumWeight) + n.finishRecharge = n.lastUpdate + mclock.AbsTime(n.rcValue*rcConst/(-n.rcDelta)) } } @@ -89,16 +89,16 @@ func NewClientManager(rcTarget, maxSimReq, maxRcSum uint64) *ClientManager { return cm } -func (self *ClientManager) Stop() { - self.lock.Lock() - defer self.lock.Unlock() +func (cm *ClientManager) Stop() { + cm.lock.Lock() + defer cm.lock.Unlock() // signal any waiting accept routines to return false - self.nodes = make(map[*cmNode]struct{}) - close(self.resumeQueue) + cm.nodes = make(map[*cmNode]struct{}) + close(cm.resumeQueue) } -func (self *ClientManager) addNode(cnode *ClientNode) *cmNode { +func (cm *ClientManager) addNode(cnode *ClientNode) *cmNode { time := mclock.Now() node := &cmNode{ node: cnode, @@ -106,28 +106,28 @@ func (self *ClientManager) addNode(cnode *ClientNode) *cmNode { finishRecharge: time, rcWeight: 1, } - self.lock.Lock() - defer self.lock.Unlock() + cm.lock.Lock() + defer cm.lock.Unlock() - self.nodes[node] = struct{}{} - self.update(mclock.Now()) + cm.nodes[node] = struct{}{} + cm.update(mclock.Now()) return node } -func (self *ClientManager) removeNode(node *cmNode) { - self.lock.Lock() - defer self.lock.Unlock() +func (cm *ClientManager) removeNode(node *cmNode) { + cm.lock.Lock() + defer cm.lock.Unlock() time := mclock.Now() - self.stop(node, time) - delete(self.nodes, node) - self.update(time) + cm.stop(node, time) + delete(cm.nodes, node) + cm.update(time) } // recalc sumWeight -func (self *ClientManager) updateNodes(time mclock.AbsTime) (rce bool) { +func (cm *ClientManager) updateNodes(time mclock.AbsTime) (rce bool) { var sumWeight, rcSum uint64 - for node := range self.nodes { + for node := range cm.nodes { rc := node.recharging node.update(time) if rc && !node.recharging { @@ -138,44 +138,44 @@ func (self *ClientManager) updateNodes(time mclock.AbsTime) (rce bool) { } rcSum += uint64(node.rcValue) } - self.sumWeight = sumWeight - self.rcSumValue = rcSum + cm.sumWeight = sumWeight + cm.rcSumValue = rcSum return } -func (self *ClientManager) update(time mclock.AbsTime) { +func (cm *ClientManager) update(time mclock.AbsTime) { for { firstTime := time - for node := range self.nodes { + for node := range cm.nodes { if node.recharging && node.finishRecharge < firstTime { firstTime = node.finishRecharge } } - if self.updateNodes(firstTime) { - for node := range self.nodes { + if cm.updateNodes(firstTime) { + for node := range cm.nodes { if node.recharging { - node.set(node.serving, self.simReqCnt, self.sumWeight) + node.set(node.serving, cm.simReqCnt, cm.sumWeight) } } } else { - self.time = time + cm.time = time return } } } -func (self *ClientManager) canStartReq() bool { - return self.simReqCnt < self.maxSimReq && self.rcSumValue < self.maxRcSum +func (cm *ClientManager) canStartReq() bool { + return cm.simReqCnt < cm.maxSimReq && cm.rcSumValue < cm.maxRcSum } -func (self *ClientManager) queueProc() { - for rc := range self.resumeQueue { +func (cm *ClientManager) queueProc() { + for rc := range cm.resumeQueue { for { time.Sleep(time.Millisecond * 10) - self.lock.Lock() - self.update(mclock.Now()) - cs := self.canStartReq() - self.lock.Unlock() + cm.lock.Lock() + cm.update(mclock.Now()) + cs := cm.canStartReq() + cm.lock.Unlock() if cs { break } @@ -184,41 +184,41 @@ func (self *ClientManager) queueProc() { } } -func (self *ClientManager) accept(node *cmNode, time mclock.AbsTime) bool { - self.lock.Lock() - defer self.lock.Unlock() +func (cm *ClientManager) accept(node *cmNode, time mclock.AbsTime) bool { + cm.lock.Lock() + defer cm.lock.Unlock() - self.update(time) - if !self.canStartReq() { + cm.update(time) + if !cm.canStartReq() { resume := make(chan bool) - self.lock.Unlock() - self.resumeQueue <- resume + cm.lock.Unlock() + cm.resumeQueue <- resume <-resume - self.lock.Lock() - if _, ok := self.nodes[node]; !ok { + cm.lock.Lock() + if _, ok := cm.nodes[node]; !ok { return false // reject if node has been removed or manager has been stopped } } - self.simReqCnt++ - node.set(true, self.simReqCnt, self.sumWeight) + cm.simReqCnt++ + node.set(true, cm.simReqCnt, cm.sumWeight) node.startValue = node.rcValue - self.update(self.time) + cm.update(cm.time) return true } -func (self *ClientManager) stop(node *cmNode, time mclock.AbsTime) { +func (cm *ClientManager) stop(node *cmNode, time mclock.AbsTime) { if node.serving { - self.update(time) - self.simReqCnt-- - node.set(false, self.simReqCnt, self.sumWeight) - self.update(time) + cm.update(time) + cm.simReqCnt-- + node.set(false, cm.simReqCnt, cm.sumWeight) + cm.update(time) } } -func (self *ClientManager) processed(node *cmNode, time mclock.AbsTime) (rcValue, rcCost uint64) { - self.lock.Lock() - defer self.lock.Unlock() +func (cm *ClientManager) processed(node *cmNode, time mclock.AbsTime) (rcValue, rcCost uint64) { + cm.lock.Lock() + defer cm.lock.Unlock() - self.stop(node, time) + cm.stop(node, time) return uint64(node.rcValue), uint64(node.rcValue - node.startValue) } diff --git a/les/handler.go b/les/handler.go index 6a4ba688ea3b..bb8c89f1566b 100644 --- a/les/handler.go +++ b/les/handler.go @@ -1181,15 +1181,15 @@ type NodeInfo struct { } // NodeInfo retrieves some protocol metadata about the running host node. -func (self *ProtocolManager) NodeInfo() *NodeInfo { - head := self.blockchain.CurrentHeader() +func (pm *ProtocolManager) NodeInfo() *NodeInfo { + head := pm.blockchain.CurrentHeader() hash := head.Hash() return &NodeInfo{ - Network: self.networkId, - Difficulty: self.blockchain.GetTd(hash, head.Number.Uint64()), - Genesis: self.blockchain.Genesis().Hash(), - Config: self.blockchain.Config(), + Network: pm.networkId, + Difficulty: pm.blockchain.GetTd(hash, head.Number.Uint64()), + Genesis: pm.blockchain.Genesis().Hash(), + Config: pm.blockchain.Config(), Head: hash, } } diff --git a/les/txrelay.go b/les/txrelay.go index a78d9f497460..b5488a8ad538 100644 --- a/les/txrelay.go +++ b/les/txrelay.go @@ -50,47 +50,47 @@ func NewLesTxRelay(ps *peerSet, reqDist *requestDistributor) *LesTxRelay { return r } -func (self *LesTxRelay) registerPeer(p *peer) { - self.lock.Lock() - defer self.lock.Unlock() +func (l *LesTxRelay) registerPeer(p *peer) { + l.lock.Lock() + defer l.lock.Unlock() - self.peerList = self.ps.AllPeers() + l.peerList = l.ps.AllPeers() } -func (self *LesTxRelay) unregisterPeer(p *peer) { - self.lock.Lock() - defer self.lock.Unlock() +func (l *LesTxRelay) unregisterPeer(p *peer) { + l.lock.Lock() + defer l.lock.Unlock() - self.peerList = self.ps.AllPeers() + l.peerList = l.ps.AllPeers() } // send sends a list of transactions to at most a given number of peers at // once, never resending any particular transaction to the same peer twice -func (self *LesTxRelay) send(txs types.Transactions, count int) { +func (l *LesTxRelay) send(txs types.Transactions, count int) { sendTo := make(map[*peer]types.Transactions) - self.peerStartPos++ // rotate the starting position of the peer list - if self.peerStartPos >= len(self.peerList) { - self.peerStartPos = 0 + l.peerStartPos++ // rotate the starting position of the peer list + if l.peerStartPos >= len(l.peerList) { + l.peerStartPos = 0 } for _, tx := range txs { hash := tx.Hash() - ltr, ok := self.txSent[hash] + ltr, ok := l.txSent[hash] if !ok { ltr = <rInfo{ tx: tx, sentTo: make(map[*peer]struct{}), } - self.txSent[hash] = ltr - self.txPending[hash] = struct{}{} + l.txSent[hash] = ltr + l.txPending[hash] = struct{}{} } - if len(self.peerList) > 0 { + if len(l.peerList) > 0 { cnt := count - pos := self.peerStartPos + pos := l.peerStartPos for { - peer := self.peerList[pos] + peer := l.peerList[pos] if _, ok := ltr.sentTo[peer]; !ok { sendTo[peer] = append(sendTo[peer], tx) ltr.sentTo[peer] = struct{}{} @@ -100,10 +100,10 @@ func (self *LesTxRelay) send(txs types.Transactions, count int) { break // sent it to the desired number of peers } pos++ - if pos == len(self.peerList) { + if pos == len(l.peerList) { pos = 0 } - if pos == self.peerStartPos { + if pos == l.peerStartPos { break // tried all available peers } } @@ -130,46 +130,46 @@ func (self *LesTxRelay) send(txs types.Transactions, count int) { return func() { peer.SendTxs(reqID, cost, ll) } }, } - self.reqDist.queue(rq) + l.reqDist.queue(rq) } } -func (self *LesTxRelay) Send(txs types.Transactions) { - self.lock.Lock() - defer self.lock.Unlock() +func (l *LesTxRelay) Send(txs types.Transactions) { + l.lock.Lock() + defer l.lock.Unlock() - self.send(txs, 3) + l.send(txs, 3) } -func (self *LesTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) { - self.lock.Lock() - defer self.lock.Unlock() +func (l *LesTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) { + l.lock.Lock() + defer l.lock.Unlock() for _, hash := range mined { - delete(self.txPending, hash) + delete(l.txPending, hash) } for _, hash := range rollback { - self.txPending[hash] = struct{}{} + l.txPending[hash] = struct{}{} } - if len(self.txPending) > 0 { - txs := make(types.Transactions, len(self.txPending)) + if len(l.txPending) > 0 { + txs := make(types.Transactions, len(l.txPending)) i := 0 - for hash := range self.txPending { - txs[i] = self.txSent[hash].tx + for hash := range l.txPending { + txs[i] = l.txSent[hash].tx i++ } - self.send(txs, 1) + l.send(txs, 1) } } -func (self *LesTxRelay) Discard(hashes []common.Hash) { - self.lock.Lock() - defer self.lock.Unlock() +func (l *LesTxRelay) Discard(hashes []common.Hash) { + l.lock.Lock() + defer l.lock.Unlock() for _, hash := range hashes { - delete(self.txSent, hash) - delete(self.txPending, hash) + delete(l.txSent, hash) + delete(l.txPending, hash) } } diff --git a/light/lightchain.go b/light/lightchain.go index dd3e57f624dd..236d1ed85bf9 100644 --- a/light/lightchain.go +++ b/light/lightchain.go @@ -116,45 +116,45 @@ func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus. } // addTrustedCheckpoint adds a trusted checkpoint to the blockchain -func (self *LightChain) addTrustedCheckpoint(cp trustedCheckpoint) { - if self.odr.ChtIndexer() != nil { - StoreChtRoot(self.chainDb, cp.sectionIdx, cp.sectionHead, cp.chtRoot) - self.odr.ChtIndexer().AddKnownSectionHead(cp.sectionIdx, cp.sectionHead) +func (lc *LightChain) addTrustedCheckpoint(cp trustedCheckpoint) { + if lc.odr.ChtIndexer() != nil { + StoreChtRoot(lc.chainDb, cp.sectionIdx, cp.sectionHead, cp.chtRoot) + lc.odr.ChtIndexer().AddKnownSectionHead(cp.sectionIdx, cp.sectionHead) } - if self.odr.BloomTrieIndexer() != nil { - StoreBloomTrieRoot(self.chainDb, cp.sectionIdx, cp.sectionHead, cp.bloomTrieRoot) - self.odr.BloomTrieIndexer().AddKnownSectionHead(cp.sectionIdx, cp.sectionHead) + if lc.odr.BloomTrieIndexer() != nil { + StoreBloomTrieRoot(lc.chainDb, cp.sectionIdx, cp.sectionHead, cp.bloomTrieRoot) + lc.odr.BloomTrieIndexer().AddKnownSectionHead(cp.sectionIdx, cp.sectionHead) } - if self.odr.BloomIndexer() != nil { - self.odr.BloomIndexer().AddKnownSectionHead(cp.sectionIdx, cp.sectionHead) + if lc.odr.BloomIndexer() != nil { + lc.odr.BloomIndexer().AddKnownSectionHead(cp.sectionIdx, cp.sectionHead) } log.Info("Added trusted checkpoint", "chain", cp.name, "block", (cp.sectionIdx+1)*CHTFrequencyClient-1, "hash", cp.sectionHead) } -func (self *LightChain) getProcInterrupt() bool { - return atomic.LoadInt32(&self.procInterrupt) == 1 +func (lc *LightChain) getProcInterrupt() bool { + return atomic.LoadInt32(&lc.procInterrupt) == 1 } // Odr returns the ODR backend of the chain -func (self *LightChain) Odr() OdrBackend { - return self.odr +func (lc *LightChain) Odr() OdrBackend { + return lc.odr } // loadLastState loads the last known chain state from the database. This method // assumes that the chain manager mutex is held. -func (self *LightChain) loadLastState() error { - if head := core.GetHeadHeaderHash(self.chainDb); head == (common.Hash{}) { +func (lc *LightChain) loadLastState() error { + if head := core.GetHeadHeaderHash(lc.chainDb); head == (common.Hash{}) { // Corrupt or empty database, init from scratch - self.Reset() + lc.Reset() } else { - if header := self.GetHeaderByHash(head); header != nil { - self.hc.SetCurrentHeader(header) + if header := lc.GetHeaderByHash(head); header != nil { + lc.hc.SetCurrentHeader(header) } } // Issue a status log and return - header := self.hc.CurrentHeader() - headerTd := self.GetTd(header.Hash(), header.Number.Uint64()) + header := lc.hc.CurrentHeader() + headerTd := lc.GetTd(header.Hash(), header.Number.Uint64()) log.Info("Loaded most recent local header", "number", header.Number, "hash", header.Hash(), "td", headerTd) return nil @@ -162,128 +162,128 @@ func (self *LightChain) loadLastState() error { // SetHead rewinds the local chain to a new head. Everything above the new // head will be deleted and the new one set. -func (bc *LightChain) SetHead(head uint64) { - bc.mu.Lock() - defer bc.mu.Unlock() +func (lc *LightChain) SetHead(head uint64) { + lc.mu.Lock() + defer lc.mu.Unlock() - bc.hc.SetHead(head, nil) - bc.loadLastState() + lc.hc.SetHead(head, nil) + lc.loadLastState() } // GasLimit returns the gas limit of the current HEAD block. -func (self *LightChain) GasLimit() uint64 { - return self.hc.CurrentHeader().GasLimit +func (lc *LightChain) GasLimit() uint64 { + return lc.hc.CurrentHeader().GasLimit } // Reset purges the entire blockchain, restoring it to its genesis state. -func (bc *LightChain) Reset() { - bc.ResetWithGenesisBlock(bc.genesisBlock) +func (lc *LightChain) Reset() { + lc.ResetWithGenesisBlock(lc.genesisBlock) } // ResetWithGenesisBlock purges the entire blockchain, restoring it to the // specified genesis state. -func (bc *LightChain) ResetWithGenesisBlock(genesis *types.Block) { +func (lc *LightChain) ResetWithGenesisBlock(genesis *types.Block) { // Dump the entire block chain and purge the caches - bc.SetHead(0) + lc.SetHead(0) - bc.mu.Lock() - defer bc.mu.Unlock() + lc.mu.Lock() + defer lc.mu.Unlock() // Prepare the genesis block and reinitialise the chain - if err := core.WriteTd(bc.chainDb, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil { + if err := core.WriteTd(lc.chainDb, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil { log.Crit("Failed to write genesis block TD", "err", err) } - rawdb.WriteBlock(bc.chainDb, genesis) - bc.genesisBlock = genesis - bc.hc.SetGenesis(bc.genesisBlock.Header()) - bc.hc.SetCurrentHeader(bc.genesisBlock.Header()) + rawdb.WriteBlock(lc.chainDb, genesis) + lc.genesisBlock = genesis + lc.hc.SetGenesis(lc.genesisBlock.Header()) + lc.hc.SetCurrentHeader(lc.genesisBlock.Header()) } // Accessors // Engine retrieves the light chain's consensus engine. -func (bc *LightChain) Engine() consensus.Engine { return bc.engine } +func (lc *LightChain) Engine() consensus.Engine { return lc.engine } // Genesis returns the genesis block -func (bc *LightChain) Genesis() *types.Block { - return bc.genesisBlock +func (lc *LightChain) Genesis() *types.Block { + return lc.genesisBlock } // State returns a new mutable state based on the current HEAD block. -func (bc *LightChain) State() (*state.StateDB, error) { +func (lc *LightChain) State() (*state.StateDB, error) { return nil, errors.New("not implemented, needs client/server interface split") } // GetBody retrieves a block body (transactions and uncles) from the database // or ODR service by hash, caching it if found. -func (self *LightChain) GetBody(ctx context.Context, hash common.Hash) (*types.Body, error) { +func (lc *LightChain) GetBody(ctx context.Context, hash common.Hash) (*types.Body, error) { // Short circuit if the body's already in the cache, retrieve otherwise - if cached, ok := self.bodyCache.Get(hash); ok { + if cached, ok := lc.bodyCache.Get(hash); ok { body := cached.(*types.Body) return body, nil } - body, err := GetBody(ctx, self.odr, hash, self.hc.GetBlockNumber(hash)) + body, err := GetBody(ctx, lc.odr, hash, lc.hc.GetBlockNumber(hash)) if err != nil { return nil, err } // Cache the found body for next time and return - self.bodyCache.Add(hash, body) + lc.bodyCache.Add(hash, body) return body, nil } // GetBodyRLP retrieves a block body in RLP encoding from the database or // ODR service by hash, caching it if found. -func (self *LightChain) GetBodyRLP(ctx context.Context, hash common.Hash) (rlp.RawValue, error) { +func (lc *LightChain) GetBodyRLP(ctx context.Context, hash common.Hash) (rlp.RawValue, error) { // Short circuit if the body's already in the cache, retrieve otherwise - if cached, ok := self.bodyRLPCache.Get(hash); ok { + if cached, ok := lc.bodyRLPCache.Get(hash); ok { return cached.(rlp.RawValue), nil } - body, err := GetBodyRLP(ctx, self.odr, hash, self.hc.GetBlockNumber(hash)) + body, err := GetBodyRLP(ctx, lc.odr, hash, lc.hc.GetBlockNumber(hash)) if err != nil { return nil, err } // Cache the found body for next time and return - self.bodyRLPCache.Add(hash, body) + lc.bodyRLPCache.Add(hash, body) return body, nil } // HasBlock checks if a block is fully present in the database or not, caching // it if present. -func (bc *LightChain) HasBlock(hash common.Hash, number uint64) bool { - blk, _ := bc.GetBlock(NoOdr, hash, number) +func (lc *LightChain) HasBlock(hash common.Hash, number uint64) bool { + blk, _ := lc.GetBlock(NoOdr, hash, number) return blk != nil } // GetBlock retrieves a block from the database or ODR service by hash and number, // caching it if found. -func (self *LightChain) GetBlock(ctx context.Context, hash common.Hash, number uint64) (*types.Block, error) { +func (lc *LightChain) GetBlock(ctx context.Context, hash common.Hash, number uint64) (*types.Block, error) { // Short circuit if the block's already in the cache, retrieve otherwise - if block, ok := self.blockCache.Get(hash); ok { + if block, ok := lc.blockCache.Get(hash); ok { return block.(*types.Block), nil } - block, err := GetBlock(ctx, self.odr, hash, number) + block, err := GetBlock(ctx, lc.odr, hash, number) if err != nil { return nil, err } // Cache the found block for next time and return - self.blockCache.Add(block.Hash(), block) + lc.blockCache.Add(block.Hash(), block) return block, nil } // GetBlockByHash retrieves a block from the database or ODR service by hash, // caching it if found. -func (self *LightChain) GetBlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { - return self.GetBlock(ctx, hash, self.hc.GetBlockNumber(hash)) +func (lc *LightChain) GetBlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { + return lc.GetBlock(ctx, hash, lc.hc.GetBlockNumber(hash)) } // GetBlockByNumber retrieves a block from the database or ODR service by // number, caching it (associated with its hash) if found. -func (self *LightChain) GetBlockByNumber(ctx context.Context, number uint64) (*types.Block, error) { - hash, err := GetCanonicalHash(ctx, self.odr, number) +func (lc *LightChain) GetBlockByNumber(ctx context.Context, number uint64) (*types.Block, error) { + hash, err := GetCanonicalHash(ctx, lc.odr, number) if hash == (common.Hash{}) || err != nil { return nil, err } - return self.GetBlock(ctx, hash, number) + return lc.GetBlock(ctx, hash, number) } func (bc *LightChain) SaveData() { @@ -291,44 +291,44 @@ func (bc *LightChain) SaveData() { // Stop stops the blockchain service. If any imports are currently in progress // it will abort them using the procInterrupt. -func (bc *LightChain) Stop() { - if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) { +func (lc *LightChain) Stop() { + if !atomic.CompareAndSwapInt32(&lc.running, 0, 1) { return } - close(bc.quit) - atomic.StoreInt32(&bc.procInterrupt, 1) + close(lc.quit) + atomic.StoreInt32(&lc.procInterrupt, 1) - bc.wg.Wait() + lc.wg.Wait() log.Info("Blockchain manager stopped") } // Rollback is designed to remove a chain of links from the database that aren't // certain enough to be valid. -func (self *LightChain) Rollback(chain []common.Hash) { - self.mu.Lock() - defer self.mu.Unlock() +func (lc *LightChain) Rollback(chain []common.Hash) { + lc.mu.Lock() + defer lc.mu.Unlock() for i := len(chain) - 1; i >= 0; i-- { hash := chain[i] - if head := self.hc.CurrentHeader(); head.Hash() == hash { - self.hc.SetCurrentHeader(self.GetHeader(head.ParentHash, head.Number.Uint64()-1)) + if head := lc.hc.CurrentHeader(); head.Hash() == hash { + lc.hc.SetCurrentHeader(lc.GetHeader(head.ParentHash, head.Number.Uint64()-1)) } } } // postChainEvents iterates over the events generated by a chain insertion and // posts them into the event feed. -func (self *LightChain) postChainEvents(events []interface{}) { +func (lc *LightChain) postChainEvents(events []interface{}) { for _, event := range events { switch ev := event.(type) { case core.ChainEvent: - if self.CurrentHeader().Hash() == ev.Hash { - self.chainHeadFeed.Send(core.ChainHeadEvent{Block: ev.Block}) + if lc.CurrentHeader().Hash() == ev.Hash { + lc.chainHeadFeed.Send(core.ChainHeadEvent{Block: ev.Block}) } - self.chainFeed.Send(ev) + lc.chainFeed.Send(ev) case core.ChainSideEvent: - self.chainSideFeed.Send(ev) + lc.chainSideFeed.Send(ev) } } } @@ -344,28 +344,28 @@ func (self *LightChain) postChainEvents(events []interface{}) { // // In the case of a light chain, InsertHeaderChain also creates and posts light // chain events when necessary. -func (self *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) { +func (lc *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) { start := time.Now() - if i, err := self.hc.ValidateHeaderChain(chain, checkFreq); err != nil { + if i, err := lc.hc.ValidateHeaderChain(chain, checkFreq); err != nil { return i, err } // Make sure only one thread manipulates the chain at once - self.chainmu.Lock() + lc.chainmu.Lock() defer func() { - self.chainmu.Unlock() + lc.chainmu.Unlock() time.Sleep(time.Millisecond * 10) // ugly hack; do not hog chain lock in case syncing is CPU-limited by validation }() - self.wg.Add(1) - defer self.wg.Done() + lc.wg.Add(1) + defer lc.wg.Done() var events []interface{} whFunc := func(header *types.Header) error { - self.mu.Lock() - defer self.mu.Unlock() + lc.mu.Lock() + defer lc.mu.Unlock() - status, err := self.hc.WriteHeader(header) + status, err := lc.hc.WriteHeader(header) switch status { case core.CanonStatTy: @@ -378,91 +378,91 @@ func (self *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int) } return err } - i, err := self.hc.InsertHeaderChain(chain, whFunc, start) - self.postChainEvents(events) + i, err := lc.hc.InsertHeaderChain(chain, whFunc, start) + lc.postChainEvents(events) return i, err } // CurrentHeader retrieves the current head header of the canonical chain. The // header is retrieved from the HeaderChain's internal cache. -func (self *LightChain) CurrentHeader() *types.Header { - return self.hc.CurrentHeader() +func (lc *LightChain) CurrentHeader() *types.Header { + return lc.hc.CurrentHeader() } // GetTd retrieves a block's total difficulty in the canonical chain from the // database by hash and number, caching it if found. -func (self *LightChain) GetTd(hash common.Hash, number uint64) *big.Int { - return self.hc.GetTd(hash, number) +func (lc *LightChain) GetTd(hash common.Hash, number uint64) *big.Int { + return lc.hc.GetTd(hash, number) } // GetTdByHash retrieves a block's total difficulty in the canonical chain from the // database by hash, caching it if found. -func (self *LightChain) GetTdByHash(hash common.Hash) *big.Int { - return self.hc.GetTdByHash(hash) +func (lc *LightChain) GetTdByHash(hash common.Hash) *big.Int { + return lc.hc.GetTdByHash(hash) } // GetHeader retrieves a block header from the database by hash and number, // caching it if found. -func (self *LightChain) GetHeader(hash common.Hash, number uint64) *types.Header { - return self.hc.GetHeader(hash, number) +func (lc *LightChain) GetHeader(hash common.Hash, number uint64) *types.Header { + return lc.hc.GetHeader(hash, number) } // GetHeaderByHash retrieves a block header from the database by hash, caching it if // found. -func (self *LightChain) GetHeaderByHash(hash common.Hash) *types.Header { - return self.hc.GetHeaderByHash(hash) +func (lc *LightChain) GetHeaderByHash(hash common.Hash) *types.Header { + return lc.hc.GetHeaderByHash(hash) } // HasHeader checks if a block header is present in the database or not, caching // it if present. -func (bc *LightChain) HasHeader(hash common.Hash, number uint64) bool { - return bc.hc.HasHeader(hash, number) +func (lc *LightChain) HasHeader(hash common.Hash, number uint64) bool { + return lc.hc.HasHeader(hash, number) } // GetCanonicalHash returns the canonical hash for a given block number -func (bc *LightChain) GetCanonicalHash(number uint64) common.Hash { - return bc.hc.GetCanonicalHash(number) +func (lc *LightChain) GetCanonicalHash(number uint64) common.Hash { + return lc.hc.GetCanonicalHash(number) } // GetBlockHashesFromHash retrieves a number of block hashes starting at a given // hash, fetching towards the genesis block. -func (self *LightChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash { - return self.hc.GetBlockHashesFromHash(hash, max) +func (lc *LightChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash { + return lc.hc.GetBlockHashesFromHash(hash, max) } // GetHeaderByNumber retrieves a block header from the database by number, // caching it (associated with its hash) if found. -func (self *LightChain) GetHeaderByNumber(number uint64) *types.Header { - return self.hc.GetHeaderByNumber(number) +func (lc *LightChain) GetHeaderByNumber(number uint64) *types.Header { + return lc.hc.GetHeaderByNumber(number) } // GetHeaderByNumberOdr retrieves a block header from the database or network // by number, caching it (associated with its hash) if found. -func (self *LightChain) GetHeaderByNumberOdr(ctx context.Context, number uint64) (*types.Header, error) { - if header := self.hc.GetHeaderByNumber(number); header != nil { +func (lc *LightChain) GetHeaderByNumberOdr(ctx context.Context, number uint64) (*types.Header, error) { + if header := lc.hc.GetHeaderByNumber(number); header != nil { return header, nil } - return GetHeaderByNumber(ctx, self.odr, number) + return GetHeaderByNumber(ctx, lc.odr, number) } // Config retrieves the header chain's chain configuration. -func (self *LightChain) Config() *params.ChainConfig { return self.hc.Config() } +func (lc *LightChain) Config() *params.ChainConfig { return lc.hc.Config() } -func (self *LightChain) SyncCht(ctx context.Context) bool { - if self.odr.ChtIndexer() == nil { +func (lc *LightChain) SyncCht(ctx context.Context) bool { + if lc.odr.ChtIndexer() == nil { return false } - headNum := self.CurrentHeader().Number.Uint64() - chtCount, _, _ := self.odr.ChtIndexer().Sections() + headNum := lc.CurrentHeader().Number.Uint64() + chtCount, _, _ := lc.odr.ChtIndexer().Sections() if headNum+1 < chtCount*CHTFrequencyClient { num := chtCount*CHTFrequencyClient - 1 - header, err := GetHeaderByNumber(ctx, self.odr, num) + header, err := GetHeaderByNumber(ctx, lc.odr, num) if header != nil && err == nil { - self.mu.Lock() - if self.hc.CurrentHeader().Number.Uint64() < header.Number.Uint64() { - self.hc.SetCurrentHeader(header) + lc.mu.Lock() + if lc.hc.CurrentHeader().Number.Uint64() < header.Number.Uint64() { + lc.hc.SetCurrentHeader(header) } - self.mu.Unlock() + lc.mu.Unlock() return true } } @@ -471,38 +471,38 @@ func (self *LightChain) SyncCht(ctx context.Context) bool { // LockChain locks the chain mutex for reading so that multiple canonical hashes can be // retrieved while it is guaranteed that they belong to the same version of the chain -func (self *LightChain) LockChain() { - self.chainmu.RLock() +func (lc *LightChain) LockChain() { + lc.chainmu.RLock() } // UnlockChain unlocks the chain mutex -func (self *LightChain) UnlockChain() { - self.chainmu.RUnlock() +func (lc *LightChain) UnlockChain() { + lc.chainmu.RUnlock() } // SubscribeChainEvent registers a subscription of ChainEvent. -func (self *LightChain) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { - return self.scope.Track(self.chainFeed.Subscribe(ch)) +func (lc *LightChain) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { + return lc.scope.Track(lc.chainFeed.Subscribe(ch)) } // SubscribeChainHeadEvent registers a subscription of ChainHeadEvent. -func (self *LightChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { - return self.scope.Track(self.chainHeadFeed.Subscribe(ch)) +func (lc *LightChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { + return lc.scope.Track(lc.chainHeadFeed.Subscribe(ch)) } // SubscribeChainSideEvent registers a subscription of ChainSideEvent. -func (self *LightChain) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription { - return self.scope.Track(self.chainSideFeed.Subscribe(ch)) +func (lc *LightChain) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription { + return lc.scope.Track(lc.chainSideFeed.Subscribe(ch)) } // SubscribeLogsEvent implements the interface of filters.Backend // LightChain does not send logs events, so return an empty subscription. -func (self *LightChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { - return self.scope.Track(new(event.Feed).Subscribe(ch)) +func (lc *LightChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { + return lc.scope.Track(new(event.Feed).Subscribe(ch)) } // SubscribeRemovedLogsEvent implements the interface of filters.Backend // LightChain does not send core.RemovedLogsEvent, so return an empty subscription. -func (self *LightChain) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { - return self.scope.Track(new(event.Feed).Subscribe(ch)) +func (lc *LightChain) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { + return lc.scope.Track(new(event.Feed).Subscribe(ch)) } diff --git a/light/txpool.go b/light/txpool.go index ed551e23290f..92e42101a733 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -113,25 +113,25 @@ func NewTxPool(config *params.ChainConfig, chain *LightChain, relay TxRelayBacke } // currentState returns the light state of the current head header -func (pool *TxPool) currentState(ctx context.Context) *state.StateDB { - return NewState(ctx, pool.chain.CurrentHeader(), pool.odr) +func (p *TxPool) currentState(ctx context.Context) *state.StateDB { + return NewState(ctx, p.chain.CurrentHeader(), p.odr) } // GetNonce returns the "pending" nonce of a given address. It always queries // the nonce belonging to the latest header too in order to detect if another // client using the same key sent a transaction. -func (pool *TxPool) GetNonce(ctx context.Context, addr common.Address) (uint64, error) { - state := pool.currentState(ctx) +func (p *TxPool) GetNonce(ctx context.Context, addr common.Address) (uint64, error) { + state := p.currentState(ctx) nonce := state.GetNonce(addr) if state.Error() != nil { return 0, state.Error() } - sn, ok := pool.nonce[addr] + sn, ok := p.nonce[addr] if ok && sn > nonce { nonce = sn } if !ok || sn < nonce { - pool.nonce[addr] = nonce + p.nonce[addr] = nonce } return nonce, nil } @@ -165,52 +165,52 @@ func (txc txStateChanges) getLists() (mined []common.Hash, rollback []common.Has // checkMinedTxs checks newly added blocks for the currently pending transactions // and marks them as mined if necessary. It also stores block position in the db // and adds them to the received txStateChanges map. -func (pool *TxPool) checkMinedTxs(ctx context.Context, hash common.Hash, number uint64, txc txStateChanges) error { +func (p *TxPool) checkMinedTxs(ctx context.Context, hash common.Hash, number uint64, txc txStateChanges) error { // If no transactions are pending, we don't care about anything - if len(pool.pending) == 0 { + if len(p.pending) == 0 { return nil } - block, err := GetBlock(ctx, pool.odr, hash, number) + block, err := GetBlock(ctx, p.odr, hash, number) if err != nil { return err } // Gather all the local transaction mined in this block - list := pool.mined[hash] + list := p.mined[hash] for _, tx := range block.Transactions() { - if _, ok := pool.pending[tx.Hash()]; ok { + if _, ok := p.pending[tx.Hash()]; ok { list = append(list, tx) } } // If some transactions have been mined, write the needed data to disk and update if list != nil { // Retrieve all the receipts belonging to this block and write the lookup table - if _, err := GetBlockReceipts(ctx, pool.odr, hash, number); err != nil { // ODR caches, ignore results + if _, err := GetBlockReceipts(ctx, p.odr, hash, number); err != nil { // ODR caches, ignore results return err } - if err := core.WriteTxLookupEntries(pool.chainDb, block); err != nil { + if err := core.WriteTxLookupEntries(p.chainDb, block); err != nil { return err } // Update the transaction pool's state for _, tx := range list { - delete(pool.pending, tx.Hash()) + delete(p.pending, tx.Hash()) txc.setState(tx.Hash(), true) } - pool.mined[hash] = list + p.mined[hash] = list } return nil } // rollbackTxs marks the transactions contained in recently rolled back blocks // as rolled back. It also removes any positional lookup entries. -func (pool *TxPool) rollbackTxs(hash common.Hash, txc txStateChanges) { - if list, ok := pool.mined[hash]; ok { +func (p *TxPool) rollbackTxs(hash common.Hash, txc txStateChanges) { + if list, ok := p.mined[hash]; ok { for _, tx := range list { txHash := tx.Hash() - core.DeleteTxLookupEntry(pool.chainDb, txHash) - pool.pending[txHash] = tx + core.DeleteTxLookupEntry(p.chainDb, txHash) + p.pending[txHash] = tx txc.setState(txHash, false) } - delete(pool.mined, hash) + delete(p.mined, hash) } } @@ -220,60 +220,60 @@ func (pool *TxPool) rollbackTxs(hash common.Hash, txc txStateChanges) { // timeout) occurs during checking new blocks, it leaves the locally known head // at the latest checked block and still returns a valid txStateChanges, making it // possible to continue checking the missing blocks at the next chain head event -func (pool *TxPool) reorgOnNewHead(ctx context.Context, newHeader *types.Header) (txStateChanges, error) { +func (p *TxPool) reorgOnNewHead(ctx context.Context, newHeader *types.Header) (txStateChanges, error) { txc := make(txStateChanges) - oldh := pool.chain.GetHeaderByHash(pool.head) + oldh := p.chain.GetHeaderByHash(p.head) newh := newHeader // find common ancestor, create list of rolled back and new block hashes var oldHashes, newHashes []common.Hash for oldh.Hash() != newh.Hash() { if oldh.Number.Uint64() >= newh.Number.Uint64() { oldHashes = append(oldHashes, oldh.Hash()) - oldh = pool.chain.GetHeader(oldh.ParentHash, oldh.Number.Uint64()-1) + oldh = p.chain.GetHeader(oldh.ParentHash, oldh.Number.Uint64()-1) } if oldh.Number.Uint64() < newh.Number.Uint64() { newHashes = append(newHashes, newh.Hash()) - newh = pool.chain.GetHeader(newh.ParentHash, newh.Number.Uint64()-1) + newh = p.chain.GetHeader(newh.ParentHash, newh.Number.Uint64()-1) if newh == nil { // happens when CHT syncing, nothing to do newh = oldh } } } - if oldh.Number.Uint64() < pool.clearIdx { - pool.clearIdx = oldh.Number.Uint64() + if oldh.Number.Uint64() < p.clearIdx { + p.clearIdx = oldh.Number.Uint64() } // roll back old blocks for _, hash := range oldHashes { - pool.rollbackTxs(hash, txc) + p.rollbackTxs(hash, txc) } - pool.head = oldh.Hash() + p.head = oldh.Hash() // check mined txs of new blocks (array is in reversed order) for i := len(newHashes) - 1; i >= 0; i-- { hash := newHashes[i] - if err := pool.checkMinedTxs(ctx, hash, newHeader.Number.Uint64()-uint64(i), txc); err != nil { + if err := p.checkMinedTxs(ctx, hash, newHeader.Number.Uint64()-uint64(i), txc); err != nil { return txc, err } - pool.head = hash + p.head = hash } // clear old mined tx entries of old blocks - if idx := newHeader.Number.Uint64(); idx > pool.clearIdx+txPermanent { + if idx := newHeader.Number.Uint64(); idx > p.clearIdx+txPermanent { idx2 := idx - txPermanent - if len(pool.mined) > 0 { - for i := pool.clearIdx; i < idx2; i++ { - hash := core.GetCanonicalHash(pool.chainDb, i) - if list, ok := pool.mined[hash]; ok { + if len(p.mined) > 0 { + for i := p.clearIdx; i < idx2; i++ { + hash := core.GetCanonicalHash(p.chainDb, i) + if list, ok := p.mined[hash]; ok { hashes := make([]common.Hash, len(list)) for i, tx := range list { hashes[i] = tx.Hash() } - pool.relay.Discard(hashes) - delete(pool.mined, hash) + p.relay.Discard(hashes) + delete(p.mined, hash) } } } - pool.clearIdx = idx2 + p.clearIdx = idx2 } return txc, nil @@ -285,66 +285,66 @@ const blockCheckTimeout = time.Second * 3 // eventLoop processes chain head events and also notifies the tx relay backend // about the new head hash and tx state changes -func (pool *TxPool) eventLoop() { +func (p *TxPool) eventLoop() { for { select { - case ev := <-pool.chainHeadCh: - pool.setNewHead(ev.Block.Header()) + case ev := <-p.chainHeadCh: + p.setNewHead(ev.Block.Header()) // hack in order to avoid hogging the lock; this part will // be replaced by a subsequent PR. time.Sleep(time.Millisecond) // System stopped - case <-pool.chainHeadSub.Err(): + case <-p.chainHeadSub.Err(): return } } } -func (pool *TxPool) setNewHead(head *types.Header) { - pool.mu.Lock() - defer pool.mu.Unlock() +func (p *TxPool) setNewHead(head *types.Header) { + p.mu.Lock() + defer p.mu.Unlock() ctx, cancel := context.WithTimeout(context.Background(), blockCheckTimeout) defer cancel() - txc, _ := pool.reorgOnNewHead(ctx, head) + txc, _ := p.reorgOnNewHead(ctx, head) m, r := txc.getLists() - pool.relay.NewHead(pool.head, m, r) + p.relay.NewHead(p.head, m, r) // Update fork indicator by next pending block number next := new(big.Int).Add(head.Number, big.NewInt(1)) - pool.homestead = pool.config.IsHomestead(head.Number) - pool.eip2718 = pool.config.IsEIP1559(next) + p.homestead = p.config.IsHomestead(head.Number) + p.eip2718 = p.config.IsEIP1559(next) } // Stop stops the light transaction pool -func (pool *TxPool) Stop() { +func (p *TxPool) Stop() { // Unsubscribe all subscriptions registered from txpool - pool.scope.Close() + p.scope.Close() // Unsubscribe subscriptions registered from blockchain - pool.chainHeadSub.Unsubscribe() - close(pool.quit) + p.chainHeadSub.Unsubscribe() + close(p.quit) log.Info("Transaction pool stopped") } // SubscribeNewTxsEvent registers a subscription of core.NewTxsEvent and // starts sending event to the given channel. -func (pool *TxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription { - return pool.scope.Track(pool.txFeed.Subscribe(ch)) +func (p *TxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription { + return p.scope.Track(p.txFeed.Subscribe(ch)) } // Stats returns the number of currently pending (locally created) transactions -func (pool *TxPool) Stats() (pending int) { - pool.mu.RLock() - defer pool.mu.RUnlock() +func (p *TxPool) Stats() (pending int) { + p.mu.RLock() + defer p.mu.RUnlock() - pending = len(pool.pending) + pending = len(p.pending) return } // validateTx checks whether a transaction is valid according to the consensus rules. -func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error { +func (p *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error { // Validate sender var ( from common.Address @@ -362,33 +362,33 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error // validate minFee slot for XDCZ if tx.IsXDCZApplyTransaction() { - copyState := pool.currentState(ctx).Copy() - if err := core.ValidateXDCZApplyTransaction(pool.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:])); err != nil { + copyState := p.currentState(ctx).Copy() + if err := core.ValidateXDCZApplyTransaction(p.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:])); err != nil { return err } } // validate balance slot, token decimal for XDCX if tx.IsXDCXApplyTransaction() { - copyState := pool.currentState(ctx).Copy() - if err := core.ValidateXDCXApplyTransaction(pool.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:])); err != nil { + copyState := p.currentState(ctx).Copy() + if err := core.ValidateXDCXApplyTransaction(p.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:])); err != nil { return err } } // Validate the transaction sender and it's sig. Throw // if the from fields is invalid. - if from, err = types.Sender(pool.signer, tx); err != nil { + if from, err = types.Sender(p.signer, tx); err != nil { return core.ErrInvalidSender } // Last but not least check for nonce errors - currentState := pool.currentState(ctx) + currentState := p.currentState(ctx) if n := currentState.GetNonce(from); n > tx.Nonce() { return core.ErrNonceTooLow } // Check the transaction doesn't exceed the current // block limit gas. - header := pool.chain.GetHeaderByHash(pool.head) + header := p.chain.GetHeaderByHash(p.head) if header.GasLimit < tx.Gas() { return core.ErrGasLimit } @@ -407,7 +407,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error } // Should supply enough intrinsic gas - gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, pool.homestead) + gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, p.homestead) if err != nil { return err } @@ -419,80 +419,80 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error // add validates a new transaction and sets its state pending if processable. // It also updates the locally stored nonce if necessary. -func (self *TxPool) add(ctx context.Context, tx *types.Transaction) error { +func (p *TxPool) add(ctx context.Context, tx *types.Transaction) error { hash := tx.Hash() - if self.pending[hash] != nil { + if p.pending[hash] != nil { return fmt.Errorf("known transaction (%x)", hash[:4]) } - err := self.validateTx(ctx, tx) + err := p.validateTx(ctx, tx) if err != nil { return err } - if _, ok := self.pending[hash]; !ok { - self.pending[hash] = tx + if _, ok := p.pending[hash]; !ok { + p.pending[hash] = tx nonce := tx.Nonce() + 1 - addr, _ := types.Sender(self.signer, tx) - if nonce > self.nonce[addr] { - self.nonce[addr] = nonce + addr, _ := types.Sender(p.signer, tx) + if nonce > p.nonce[addr] { + p.nonce[addr] = nonce } // Notify the subscribers. This event is posted in a goroutine // because it's possible that somewhere during the post "Remove transaction" // gets called which will then wait for the global tx pool lock and deadlock. - go self.txFeed.Send(core.NewTxsEvent{Txs: types.Transactions{tx}}) + go p.txFeed.Send(core.NewTxsEvent{Txs: types.Transactions{tx}}) } // Print a log message if low enough level is set - log.Debug("Pooled new transaction", "hash", hash, "from", log.Lazy{Fn: func() common.Address { from, _ := types.Sender(self.signer, tx); return from }}, "to", tx.To()) + log.Debug("Pooled new transaction", "hash", hash, "from", log.Lazy{Fn: func() common.Address { from, _ := types.Sender(p.signer, tx); return from }}, "to", tx.To()) return nil } // Add adds a transaction to the pool if valid and passes it to the tx relay // backend -func (self *TxPool) Add(ctx context.Context, tx *types.Transaction) error { - self.mu.Lock() - defer self.mu.Unlock() +func (p *TxPool) Add(ctx context.Context, tx *types.Transaction) error { + p.mu.Lock() + defer p.mu.Unlock() data, err := tx.MarshalBinary() if err != nil { return err } - if err := self.add(ctx, tx); err != nil { + if err := p.add(ctx, tx); err != nil { return err } //fmt.Println("Send", tx.Hash()) - self.relay.Send(types.Transactions{tx}) + p.relay.Send(types.Transactions{tx}) - self.chainDb.Put(tx.Hash().Bytes(), data) + p.chainDb.Put(tx.Hash().Bytes(), data) return nil } // AddTransactions adds all valid transactions to the pool and passes them to // the tx relay backend -func (self *TxPool) AddBatch(ctx context.Context, txs []*types.Transaction) { - self.mu.Lock() - defer self.mu.Unlock() +func (p *TxPool) AddBatch(ctx context.Context, txs []*types.Transaction) { + p.mu.Lock() + defer p.mu.Unlock() var sendTx types.Transactions for _, tx := range txs { - if err := self.add(ctx, tx); err == nil { + if err := p.add(ctx, tx); err == nil { sendTx = append(sendTx, tx) } } if len(sendTx) > 0 { - self.relay.Send(sendTx) + p.relay.Send(sendTx) } } // GetTransaction returns a transaction if it is contained in the pool // and nil otherwise. -func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction { +func (p *TxPool) GetTransaction(hash common.Hash) *types.Transaction { // check the txs first - if tx, ok := tp.pending[hash]; ok { + if tx, ok := p.pending[hash]; ok { return tx } return nil @@ -500,13 +500,13 @@ func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction { // GetTransactions returns all currently processable transactions. // The returned slice may be modified by the caller. -func (self *TxPool) GetTransactions() (txs types.Transactions, err error) { - self.mu.RLock() - defer self.mu.RUnlock() +func (p *TxPool) GetTransactions() (txs types.Transactions, err error) { + p.mu.RLock() + defer p.mu.RUnlock() - txs = make(types.Transactions, len(self.pending)) + txs = make(types.Transactions, len(p.pending)) i := 0 - for _, tx := range self.pending { + for _, tx := range p.pending { txs[i] = tx i++ } @@ -515,14 +515,14 @@ func (self *TxPool) GetTransactions() (txs types.Transactions, err error) { // Content retrieves the data content of the transaction pool, returning all the // pending as well as queued transactions, grouped by account and nonce. -func (self *TxPool) Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { - self.mu.RLock() - defer self.mu.RUnlock() +func (p *TxPool) Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { + p.mu.RLock() + defer p.mu.RUnlock() // Retrieve all the pending transactions and sort by account and by nonce pending := make(map[common.Address]types.Transactions) - for _, tx := range self.pending { - account, _ := types.Sender(self.signer, tx) + for _, tx := range p.pending { + account, _ := types.Sender(p.signer, tx) pending[account] = append(pending[account], tx) } // There are no queued transactions in a light pool, just return an empty map @@ -531,26 +531,26 @@ func (self *TxPool) Content() (map[common.Address]types.Transactions, map[common } // RemoveTransactions removes all given transactions from the pool. -func (self *TxPool) RemoveTransactions(txs types.Transactions) { - self.mu.Lock() - defer self.mu.Unlock() +func (p *TxPool) RemoveTransactions(txs types.Transactions) { + p.mu.Lock() + defer p.mu.Unlock() var hashes []common.Hash for _, tx := range txs { //self.RemoveTx(tx.Hash()) hash := tx.Hash() - delete(self.pending, hash) - self.chainDb.Delete(hash[:]) + delete(p.pending, hash) + p.chainDb.Delete(hash[:]) hashes = append(hashes, hash) } - self.relay.Discard(hashes) + p.relay.Discard(hashes) } // RemoveTx removes the transaction with the given hash from the pool. -func (pool *TxPool) RemoveTx(hash common.Hash) { - pool.mu.Lock() - defer pool.mu.Unlock() +func (p *TxPool) RemoveTx(hash common.Hash) { + p.mu.Lock() + defer p.mu.Unlock() // delete from pending pool - delete(pool.pending, hash) - pool.chainDb.Delete(hash[:]) - pool.relay.Discard([]common.Hash{hash}) + delete(p.pending, hash) + p.chainDb.Delete(hash[:]) + p.relay.Discard([]common.Hash{hash}) } diff --git a/light/txpool_test.go b/light/txpool_test.go index 4ada980daf33..467efb9cd0b0 100644 --- a/light/txpool_test.go +++ b/light/txpool_test.go @@ -36,19 +36,19 @@ type testTxRelay struct { send, discard, mined chan int } -func (self *testTxRelay) Send(txs types.Transactions) { - self.send <- len(txs) +func (r *testTxRelay) Send(txs types.Transactions) { + r.send <- len(txs) } -func (self *testTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) { +func (r *testTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) { m := len(mined) if m != 0 { - self.mined <- m + r.mined <- m } } -func (self *testTxRelay) Discard(hashes []common.Hash) { - self.discard <- len(hashes) +func (r *testTxRelay) Discard(hashes []common.Hash) { + r.discard <- len(hashes) } const poolTestTxs = 1000 diff --git a/metrics/librato/client.go b/metrics/librato/client.go index 75503bb19d58..a807c392af01 100644 --- a/metrics/librato/client.go +++ b/metrics/librato/client.go @@ -65,7 +65,7 @@ type Batch struct { Source string `json:"source"` } -func (self *LibratoClient) PostMetrics(batch Batch) (err error) { +func (lc *LibratoClient) PostMetrics(batch Batch) (err error) { var ( js []byte req *http.Request @@ -85,7 +85,7 @@ func (self *LibratoClient) PostMetrics(batch Batch) (err error) { } req.Header.Set("Content-Type", "application/json") - req.SetBasicAuth(self.Email, self.Token) + req.SetBasicAuth(lc.Email, lc.Token) if resp, err = http.DefaultClient.Do(req); err != nil { return diff --git a/metrics/librato/librato.go b/metrics/librato/librato.go index 58ced3bf9a29..45bf6cf297a7 100644 --- a/metrics/librato/librato.go +++ b/metrics/librato/librato.go @@ -40,14 +40,14 @@ func Librato(r metrics.Registry, d time.Duration, e string, t string, s string, NewReporter(r, d, e, t, s, p, u).Run() } -func (self *Reporter) Run() { +func (re *Reporter) Run() { log.Printf("WARNING: This client has been DEPRECATED! It has been moved to https://github.com/mihasya/go-metrics-librato and will be removed from rcrowley/go-metrics on August 5th 2015") - ticker := time.Tick(self.Interval) - metricsApi := &LibratoClient{self.Email, self.Token} + ticker := time.Tick(re.Interval) + metricsApi := &LibratoClient{re.Email, re.Token} for now := range ticker { var metrics Batch var err error - if metrics, err = self.BuildRequest(now, self.Registry); err != nil { + if metrics, err = re.BuildRequest(now, re.Registry); err != nil { log.Printf("ERROR constructing librato request body %s", err) continue } @@ -79,21 +79,21 @@ func sumSquaresTimer(t metrics.Timer) float64 { return sumSquares } -func (self *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Batch, err error) { +func (re *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Batch, err error) { snapshot = Batch{ // coerce timestamps to a stepping fn so that they line up in Librato graphs - MeasureTime: (now.Unix() / self.intervalSec) * self.intervalSec, - Source: self.Source, + MeasureTime: (now.Unix() / re.intervalSec) * re.intervalSec, + Source: re.Source, } snapshot.Gauges = make([]Measurement, 0) snapshot.Counters = make([]Measurement, 0) - histogramGaugeCount := 1 + len(self.Percentiles) + histogramGaugeCount := 1 + len(re.Percentiles) r.Each(func(name string, metric interface{}) { - if self.Namespace != "" { - name = fmt.Sprintf("%s.%s", self.Namespace, name) + if re.Namespace != "" { + name = fmt.Sprintf("%s.%s", re.Namespace, name) } measurement := Measurement{} - measurement[Period] = self.Interval.Seconds() + measurement[Period] = re.Interval.Seconds() switch m := metric.(type) { case metrics.Counter: if m.Count() > 0 { @@ -125,7 +125,7 @@ func (self *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot measurement[Sum] = float64(s.Sum()) measurement[SumSquares] = sumSquares(s) gauges[0] = measurement - for i, p := range self.Percentiles { + for i, p := range re.Percentiles { gauges[i+1] = Measurement{ Name: fmt.Sprintf("%s.%.2f", measurement[Name], p), Value: s.Percentile(p), @@ -142,7 +142,7 @@ func (self *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Measurement{ Name: fmt.Sprintf("%s.%s", name, "1min"), Value: m.Rate1(), - Period: int64(self.Interval.Seconds()), + Period: int64(re.Interval.Seconds()), Attributes: map[string]interface{}{ DisplayUnitsLong: Operations, DisplayUnitsShort: OperationsShort, @@ -152,7 +152,7 @@ func (self *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Measurement{ Name: fmt.Sprintf("%s.%s", name, "5min"), Value: m.Rate5(), - Period: int64(self.Interval.Seconds()), + Period: int64(re.Interval.Seconds()), Attributes: map[string]interface{}{ DisplayUnitsLong: Operations, DisplayUnitsShort: OperationsShort, @@ -162,7 +162,7 @@ func (self *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Measurement{ Name: fmt.Sprintf("%s.%s", name, "15min"), Value: m.Rate15(), - Period: int64(self.Interval.Seconds()), + Period: int64(re.Interval.Seconds()), Attributes: map[string]interface{}{ DisplayUnitsLong: Operations, DisplayUnitsShort: OperationsShort, @@ -184,15 +184,15 @@ func (self *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Max: float64(m.Max()), Min: float64(m.Min()), SumSquares: sumSquaresTimer(m), - Period: int64(self.Interval.Seconds()), - Attributes: self.TimerAttributes, + Period: int64(re.Interval.Seconds()), + Attributes: re.TimerAttributes, } - for i, p := range self.Percentiles { + for i, p := range re.Percentiles { gauges[i+1] = Measurement{ Name: fmt.Sprintf("%s.timer.%2.0f", name, p*100), Value: m.Percentile(p), - Period: int64(self.Interval.Seconds()), - Attributes: self.TimerAttributes, + Period: int64(re.Interval.Seconds()), + Attributes: re.TimerAttributes, } } snapshot.Gauges = append(snapshot.Gauges, gauges...) @@ -200,7 +200,7 @@ func (self *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Measurement{ Name: fmt.Sprintf("%s.%s", name, "rate.1min"), Value: m.Rate1(), - Period: int64(self.Interval.Seconds()), + Period: int64(re.Interval.Seconds()), Attributes: map[string]interface{}{ DisplayUnitsLong: Operations, DisplayUnitsShort: OperationsShort, @@ -210,7 +210,7 @@ func (self *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Measurement{ Name: fmt.Sprintf("%s.%s", name, "rate.5min"), Value: m.Rate5(), - Period: int64(self.Interval.Seconds()), + Period: int64(re.Interval.Seconds()), Attributes: map[string]interface{}{ DisplayUnitsLong: Operations, DisplayUnitsShort: OperationsShort, @@ -220,7 +220,7 @@ func (self *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Measurement{ Name: fmt.Sprintf("%s.%s", name, "rate.15min"), Value: m.Rate15(), - Period: int64(self.Interval.Seconds()), + Period: int64(re.Interval.Seconds()), Attributes: map[string]interface{}{ DisplayUnitsLong: Operations, DisplayUnitsShort: OperationsShort, diff --git a/miner/agent.go b/miner/agent.go index 5ea0a17ddd5d..4616c2c5bcbb 100644 --- a/miner/agent.go +++ b/miner/agent.go @@ -49,70 +49,70 @@ func NewCpuAgent(chain consensus.ChainReader, engine consensus.Engine) *CpuAgent return miner } -func (self *CpuAgent) Work() chan<- *Work { return self.workCh } -func (self *CpuAgent) SetReturnCh(ch chan<- *Result) { self.returnCh = ch } +func (ca *CpuAgent) Work() chan<- *Work { return ca.workCh } +func (ca *CpuAgent) SetReturnCh(ch chan<- *Result) { ca.returnCh = ch } -func (self *CpuAgent) Stop() { - if !atomic.CompareAndSwapInt32(&self.isMining, 1, 0) { +func (ca *CpuAgent) Stop() { + if !atomic.CompareAndSwapInt32(&ca.isMining, 1, 0) { return // agent already stopped } - self.stop <- struct{}{} + ca.stop <- struct{}{} done: // Empty work channel for { select { - case <-self.workCh: + case <-ca.workCh: default: break done } } } -func (self *CpuAgent) Start() { - if !atomic.CompareAndSwapInt32(&self.isMining, 0, 1) { +func (ca *CpuAgent) Start() { + if !atomic.CompareAndSwapInt32(&ca.isMining, 0, 1) { return // agent already started } - go self.update() + go ca.update() } -func (self *CpuAgent) update() { +func (ca *CpuAgent) update() { out: for { select { - case work := <-self.workCh: - self.mu.Lock() - if self.quitCurrentOp != nil { - close(self.quitCurrentOp) + case work := <-ca.workCh: + ca.mu.Lock() + if ca.quitCurrentOp != nil { + close(ca.quitCurrentOp) } - self.quitCurrentOp = make(chan struct{}) - go self.mine(work, self.quitCurrentOp) - self.mu.Unlock() - case <-self.stop: - self.mu.Lock() - if self.quitCurrentOp != nil { - close(self.quitCurrentOp) - self.quitCurrentOp = nil + ca.quitCurrentOp = make(chan struct{}) + go ca.mine(work, ca.quitCurrentOp) + ca.mu.Unlock() + case <-ca.stop: + ca.mu.Lock() + if ca.quitCurrentOp != nil { + close(ca.quitCurrentOp) + ca.quitCurrentOp = nil } - self.mu.Unlock() + ca.mu.Unlock() break out } } } -func (self *CpuAgent) mine(work *Work, stop <-chan struct{}) { - if result, err := self.engine.Seal(self.chain, work.Block, stop); result != nil { +func (ca *CpuAgent) mine(work *Work, stop <-chan struct{}) { + if result, err := ca.engine.Seal(ca.chain, work.Block, stop); result != nil { log.Info("Successfully sealed new block", "number", result.Number(), "hash", result.Hash()) - self.returnCh <- &Result{work, result} + ca.returnCh <- &Result{work, result} } else { if err != nil { log.Warn("Block sealing failed", "err", err) } - self.returnCh <- nil + ca.returnCh <- nil } } -func (self *CpuAgent) GetHashRate() int64 { - if pow, ok := self.engine.(consensus.PoW); ok { +func (ca *CpuAgent) GetHashRate() int64 { + if pow, ok := ca.engine.(consensus.PoW); ok { return int64(pow.Hashrate()) } return 0 diff --git a/miner/miner.go b/miner/miner.go index 8177e45c8e6f..0b4354c4d8db 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -82,73 +82,73 @@ func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine con // It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and // the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks // and halt your mining operation for as long as the DOS continues. -func (self *Miner) update() { - events := self.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{}) +func (m *Miner) update() { + events := m.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{}) for ev := range events.Chan() { switch ev.Data.(type) { case downloader.StartEvent: - atomic.StoreInt32(&self.canStart, 0) - if self.Mining() { - self.Stop() - atomic.StoreInt32(&self.shouldStart, 1) + atomic.StoreInt32(&m.canStart, 0) + if m.Mining() { + m.Stop() + atomic.StoreInt32(&m.shouldStart, 1) log.Info("Mining aborted due to sync") } case downloader.DoneEvent, downloader.FailedEvent: - shouldStart := atomic.LoadInt32(&self.shouldStart) == 1 + shouldStart := atomic.LoadInt32(&m.shouldStart) == 1 - atomic.StoreInt32(&self.canStart, 1) - atomic.StoreInt32(&self.shouldStart, 0) + atomic.StoreInt32(&m.canStart, 1) + atomic.StoreInt32(&m.shouldStart, 0) if shouldStart { - self.Start(self.coinbase) + m.Start(m.coinbase) } } } } -func (self *Miner) Start(coinbase common.Address) { - atomic.StoreInt32(&self.shouldStart, 1) - self.SetEtherbase(coinbase) +func (m *Miner) Start(coinbase common.Address) { + atomic.StoreInt32(&m.shouldStart, 1) + m.SetEtherbase(coinbase) - if atomic.LoadInt32(&self.canStart) == 0 { + if atomic.LoadInt32(&m.canStart) == 0 { log.Info("Network syncing, will start miner afterwards") return } - atomic.StoreInt32(&self.mining, 1) + atomic.StoreInt32(&m.mining, 1) log.Info("Starting mining operation") - self.worker.start() - self.worker.commitNewWork() + m.worker.start() + m.worker.commitNewWork() } -func (self *Miner) Stop() { - self.worker.stop() - atomic.StoreInt32(&self.mining, 0) - atomic.StoreInt32(&self.shouldStart, 0) +func (m *Miner) Stop() { + m.worker.stop() + atomic.StoreInt32(&m.mining, 0) + atomic.StoreInt32(&m.shouldStart, 0) } -func (self *Miner) Register(agent Agent) { - if self.Mining() { +func (m *Miner) Register(agent Agent) { + if m.Mining() { agent.Start() } - self.worker.register(agent) + m.worker.register(agent) } -func (self *Miner) Unregister(agent Agent) { - self.worker.unregister(agent) +func (m *Miner) Unregister(agent Agent) { + m.worker.unregister(agent) } -func (self *Miner) Mining() bool { - return atomic.LoadInt32(&self.mining) > 0 +func (m *Miner) Mining() bool { + return atomic.LoadInt32(&m.mining) > 0 } -func (self *Miner) HashRate() (tot int64) { - if pow, ok := self.engine.(consensus.PoW); ok { +func (m *Miner) HashRate() (tot int64) { + if pow, ok := m.engine.(consensus.PoW); ok { tot += int64(pow.Hashrate()) } // do we care this might race? is it worth we're rewriting some // aspects of the worker/locking up agents so we can get an accurate // hashrate? - for agent := range self.worker.agents { + for agent := range m.worker.agents { if _, ok := agent.(*CpuAgent); !ok { tot += agent.GetHashRate() } @@ -156,17 +156,17 @@ func (self *Miner) HashRate() (tot int64) { return } -func (self *Miner) SetExtra(extra []byte) error { +func (m *Miner) SetExtra(extra []byte) error { if uint64(len(extra)) > params.MaximumExtraDataSize { return fmt.Errorf("extra exceeds max length: %d > %v", len(extra), params.MaximumExtraDataSize) } - self.worker.setExtra(extra) + m.worker.setExtra(extra) return nil } // Pending returns the currently pending block and associated state. -func (self *Miner) Pending() (*types.Block, *state.StateDB) { - return self.worker.pending() +func (m *Miner) Pending() (*types.Block, *state.StateDB) { + return m.worker.pending() } // PendingBlock returns the currently pending block. @@ -174,22 +174,22 @@ func (self *Miner) Pending() (*types.Block, *state.StateDB) { // Note, to access both the pending block and the pending state // simultaneously, please use Pending(), as the pending state can // change between multiple method calls -func (self *Miner) PendingBlock() *types.Block { - return self.worker.pendingBlock() +func (m *Miner) PendingBlock() *types.Block { + return m.worker.pendingBlock() } // PendingBlockAndReceipts returns the currently pending block and corresponding receipts. -func (miner *Miner) PendingBlockAndReceipts() (*types.Block, types.Receipts) { - return miner.worker.pendingBlockAndReceipts() +func (m *Miner) PendingBlockAndReceipts() (*types.Block, types.Receipts) { + return m.worker.pendingBlockAndReceipts() } -func (self *Miner) SetEtherbase(addr common.Address) { - self.coinbase = addr - self.worker.setEtherbase(addr) +func (m *Miner) SetEtherbase(addr common.Address) { + m.coinbase = addr + m.worker.setEtherbase(addr) } // SubscribePendingLogs starts delivering logs from pending transactions // to the given channel. -func (self *Miner) SubscribePendingLogs(ch chan<- []*types.Log) event.Subscription { - return self.worker.pendingLogsFeed.Subscribe(ch) +func (m *Miner) SubscribePendingLogs(ch chan<- []*types.Log) event.Subscription { + return m.worker.pendingLogsFeed.Subscribe(ch) } diff --git a/miner/worker.go b/miner/worker.go index a312d902ba65..38e576a32322 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -182,16 +182,16 @@ func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase com return worker } -func (self *worker) setEtherbase(addr common.Address) { - self.mu.Lock() - defer self.mu.Unlock() - self.coinbase = addr +func (w *worker) setEtherbase(addr common.Address) { + w.mu.Lock() + defer w.mu.Unlock() + w.coinbase = addr } -func (self *worker) setExtra(extra []byte) { - self.mu.Lock() - defer self.mu.Unlock() - self.extra = extra +func (w *worker) setExtra(extra []byte) { + w.mu.Lock() + defer w.mu.Unlock() + w.extra = extra } // pending returns the pending state and corresponding block. The returned @@ -221,56 +221,56 @@ func (w *worker) pendingBlockAndReceipts() (*types.Block, types.Receipts) { return w.snapshotBlock, w.snapshotReceipts } -func (self *worker) start() { - self.mu.Lock() - defer self.mu.Unlock() +func (w *worker) start() { + w.mu.Lock() + defer w.mu.Unlock() - atomic.StoreInt32(&self.mining, 1) + atomic.StoreInt32(&w.mining, 1) // spin up agents - for agent := range self.agents { + for agent := range w.agents { agent.Start() } } -func (self *worker) stop() { - self.wg.Wait() +func (w *worker) stop() { + w.wg.Wait() - self.mu.Lock() - defer self.mu.Unlock() - if atomic.LoadInt32(&self.mining) == 1 { - for agent := range self.agents { + w.mu.Lock() + defer w.mu.Unlock() + if atomic.LoadInt32(&w.mining) == 1 { + for agent := range w.agents { agent.Stop() } } - atomic.StoreInt32(&self.mining, 0) - atomic.StoreInt32(&self.atWork, 0) + atomic.StoreInt32(&w.mining, 0) + atomic.StoreInt32(&w.atWork, 0) } -func (self *worker) register(agent Agent) { - self.mu.Lock() - defer self.mu.Unlock() - self.agents[agent] = struct{}{} - agent.SetReturnCh(self.recv) +func (w *worker) register(agent Agent) { + w.mu.Lock() + defer w.mu.Unlock() + w.agents[agent] = struct{}{} + agent.SetReturnCh(w.recv) } -func (self *worker) unregister(agent Agent) { - self.mu.Lock() - defer self.mu.Unlock() - delete(self.agents, agent) +func (w *worker) unregister(agent Agent) { + w.mu.Lock() + defer w.mu.Unlock() + delete(w.agents, agent) agent.Stop() } -func (self *worker) update() { - if self.announceTxs { - defer self.txsSub.Unsubscribe() +func (w *worker) update() { + if w.announceTxs { + defer w.txsSub.Unsubscribe() } - defer self.chainHeadSub.Unsubscribe() - defer self.chainSideSub.Unsubscribe() + defer w.chainHeadSub.Unsubscribe() + defer w.chainSideSub.Unsubscribe() // timeout waiting for v1 inital value minePeriod := 2 - MinePeriodCh := self.engine.(*XDPoS.XDPoS).MinePeriodCh + MinePeriodCh := w.engine.(*XDPoS.XDPoS).MinePeriodCh defer close(MinePeriodCh) timeout := time.NewTimer(time.Duration(minePeriod) * time.Second) @@ -298,73 +298,73 @@ func (self *worker) update() { timeout.Reset(time.Duration(minePeriod) * time.Second) case <-c: - if atomic.LoadInt32(&self.mining) == 1 { - self.commitNewWork() + if atomic.LoadInt32(&w.mining) == 1 { + w.commitNewWork() } timeout.Reset(time.Duration(minePeriod) * time.Second) // Handle ChainHeadEvent - case <-self.chainHeadCh: - self.commitNewWork() + case <-w.chainHeadCh: + w.commitNewWork() timeout.Reset(time.Duration(minePeriod) * time.Second) // Handle ChainSideEvent - case <-self.chainSideCh: + case <-w.chainSideCh: // Handle NewTxsEvent - case ev := <-self.txsCh: + case ev := <-w.txsCh: // Apply transactions to the pending state if we're not mining. // // Note all transactions received may not be continuous with transactions // already included in the current mining block. These transactions will // be automatically eliminated. - if atomic.LoadInt32(&self.mining) == 0 { - self.currentMu.Lock() + if atomic.LoadInt32(&w.mining) == 0 { + w.currentMu.Lock() txs := make(map[common.Address]types.Transactions) for _, tx := range ev.Txs { - acc, _ := types.Sender(self.current.signer, tx) + acc, _ := types.Sender(w.current.signer, tx) txs[acc] = append(txs[acc], tx) } - feeCapacity := state.GetTRC21FeeCapacityFromState(self.current.state) - txset, specialTxs := types.NewTransactionsByPriceAndNonce(self.current.signer, txs, nil, feeCapacity) + feeCapacity := state.GetTRC21FeeCapacityFromState(w.current.state) + txset, specialTxs := types.NewTransactionsByPriceAndNonce(w.current.signer, txs, nil, feeCapacity) - tcount := self.current.tcount - self.current.commitTransactions(self.mux, feeCapacity, txset, specialTxs, self.chain, self.coinbase, &self.pendingLogsFeed) + tcount := w.current.tcount + w.current.commitTransactions(w.mux, feeCapacity, txset, specialTxs, w.chain, w.coinbase, &w.pendingLogsFeed) // Only update the snapshot if any new transactions were added // to the pending block - if tcount != self.current.tcount { - self.updateSnapshot() + if tcount != w.current.tcount { + w.updateSnapshot() } - self.currentMu.Unlock() + w.currentMu.Unlock() } else { // If we're mining, but nothing is being processed, wake on new transactions - if self.config.XDPoS != nil && self.config.XDPoS.Period == 0 { - self.commitNewWork() + if w.config.XDPoS != nil && w.config.XDPoS.Period == 0 { + w.commitNewWork() } } - case <-self.chainHeadSub.Err(): + case <-w.chainHeadSub.Err(): return - case <-self.chainSideSub.Err(): + case <-w.chainSideSub.Err(): return } } } -func (self *worker) wait() { +func (w *worker) wait() { for { mustCommitNewWork := true - for result := range self.recv { - atomic.AddInt32(&self.atWork, -1) + for result := range w.recv { + atomic.AddInt32(&w.atWork, -1) if result == nil { continue } block := result.Block - if self.config.XDPoS != nil && block.NumberU64() >= self.config.XDPoS.Epoch && len(block.Validator()) == 0 { - self.mux.Post(core.NewMinedBlockEvent{Block: block}) + if w.config.XDPoS != nil && block.NumberU64() >= w.config.XDPoS.Epoch && len(block.Validator()) == 0 { + w.mux.Post(core.NewMinedBlockEvent{Block: block}) continue } work := result.Work @@ -387,9 +387,9 @@ func (self *worker) wait() { log.BlockHash = hash } // Commit block and state to database. - self.currentMu.Lock() - stat, err := self.chain.WriteBlockWithState(block, receipts, work.state, work.tradingState, work.lendingState) - self.currentMu.Unlock() + w.currentMu.Lock() + stat, err := w.chain.WriteBlockWithState(block, receipts, work.state, work.tradingState, work.lendingState) + w.currentMu.Unlock() if err != nil { log.Error("Failed writing block to chain", "err", err) continue @@ -400,7 +400,7 @@ func (self *worker) wait() { mustCommitNewWork = false } // Broadcast the block and announce chain insertion event - self.mux.Post(core.NewMinedBlockEvent{Block: block}) + w.mux.Post(core.NewMinedBlockEvent{Block: block}) var ( events []interface{} logs = work.state.Logs() @@ -411,7 +411,7 @@ func (self *worker) wait() { } if work.config.XDPoS != nil { // epoch block - isEpochSwitchBlock, _, err := self.engine.(*XDPoS.XDPoS).IsEpochSwitch(block.Header()) + isEpochSwitchBlock, _, err := w.engine.(*XDPoS.XDPoS).IsEpochSwitch(block.Header()) if err != nil { log.Error("[wait] fail to check if block is epoch switch block when worker waiting", "BlockNum", block.Number(), "Hash", block.Hash()) } @@ -419,29 +419,29 @@ func (self *worker) wait() { core.CheckpointCh <- 1 } } - self.chain.UpdateBlocksHashCache(block) - self.chain.PostChainEvents(events, logs) + w.chain.UpdateBlocksHashCache(block) + w.chain.PostChainEvents(events, logs) // Insert the block into the set of pending ones to wait for confirmations - self.unconfirmed.Insert(block.NumberU64(), block.Hash()) + w.unconfirmed.Insert(block.NumberU64(), block.Hash()) if mustCommitNewWork { - self.commitNewWork() + w.commitNewWork() } - if self.config.XDPoS != nil { - c := self.engine.(*XDPoS.XDPoS) - err = c.HandleProposedBlock(self.chain, block.Header()) + if w.config.XDPoS != nil { + c := w.engine.(*XDPoS.XDPoS) + err = c.HandleProposedBlock(w.chain, block.Header()) if err != nil { log.Warn("[wait] Unable to handle new proposed block", "err", err, "number", block.Number(), "hash", block.Hash()) } - authorized := c.IsAuthorisedAddress(self.chain, block.Header(), self.coinbase) + authorized := c.IsAuthorisedAddress(w.chain, block.Header(), w.coinbase) if !authorized { valid := false - masternodes := c.GetMasternodes(self.chain, block.Header()) + masternodes := c.GetMasternodes(w.chain, block.Header()) for _, m := range masternodes { - if m == self.coinbase { + if m == w.coinbase { valid = true break } @@ -452,8 +452,8 @@ func (self *worker) wait() { } } // Send tx sign to smart contract blockSigners. - if block.NumberU64()%common.MergeSignRange == 0 || !self.config.IsTIP2019(block.Number()) { - if err := contracts.CreateTransactionSign(self.config, self.eth.TxPool(), self.eth.AccountManager(), block, self.chainDb, self.coinbase); err != nil { + if block.NumberU64()%common.MergeSignRange == 0 || !w.config.IsTIP2019(block.Number()) { + if err := contracts.CreateTransactionSign(w.config, w.eth.TxPool(), w.eth.AccountManager(), block, w.chainDb, w.coinbase); err != nil { log.Error("Fail to create tx sign for signer", "error", err) } } @@ -463,12 +463,12 @@ func (self *worker) wait() { } // push sends a new work task to currently live miner agents. -func (self *worker) push(work *Work) { - if atomic.LoadInt32(&self.mining) != 1 { +func (w *worker) push(work *Work) { + if atomic.LoadInt32(&w.mining) != 1 { return } - for agent := range self.agents { - atomic.AddInt32(&self.atWork, 1) + for agent := range w.agents { + atomic.AddInt32(&w.atWork, 1) if ch := agent.Work(); ch != nil { ch <- work } @@ -502,25 +502,25 @@ func (w *worker) updateSnapshot() { } // makeCurrent creates a new environment for the current cycle. -func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error { +func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error { // Retrieve the parent state to execute on top and start a prefetcher for // the miner to speed block sealing up a bit - state, err := self.chain.StateAt(parent.Root()) + state, err := w.chain.StateAt(parent.Root()) if err != nil { return err } - author, _ := self.chain.Engine().Author(parent.Header()) + author, _ := w.chain.Engine().Author(parent.Header()) var XDCxState *tradingstate.TradingStateDB var lendingState *lendingstate.LendingStateDB - if self.config.XDPoS != nil { - XDCX := self.eth.GetXDCX() + if w.config.XDPoS != nil { + XDCX := w.eth.GetXDCX() XDCxState, err = XDCX.GetTradingState(parent, author) if err != nil { log.Error("Failed to get XDCx state ", "number", parent.Number(), "err", err) return err } - lending := self.eth.GetXDCXLending() + lending := w.eth.GetXDCXLending() lendingState, err = lending.GetLendingState(parent, author) if err != nil { log.Error("Failed to get lending state ", "number", parent.Number(), "err", err) @@ -529,8 +529,8 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error } work := &Work{ - config: self.config, - signer: types.MakeSigner(self.config, header.Number), + config: w.config, + signer: types.MakeSigner(w.config, header.Number), state: state, parentState: state.Copy(), tradingState: XDCxState, @@ -544,7 +544,7 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error // Keep track of transactions which return errors so they can be removed work.tcount = 0 - self.current = work + w.current = work return nil } @@ -555,37 +555,37 @@ func abs(x int64) int64 { return x } -func (self *worker) commitNewWork() { - self.mu.Lock() - defer self.mu.Unlock() - self.uncleMu.Lock() - defer self.uncleMu.Unlock() - self.currentMu.Lock() - defer self.currentMu.Unlock() +func (w *worker) commitNewWork() { + w.mu.Lock() + defer w.mu.Unlock() + w.uncleMu.Lock() + defer w.uncleMu.Unlock() + w.currentMu.Lock() + defer w.currentMu.Unlock() tstart := time.Now() - c := self.engine.(*XDPoS.XDPoS) + c := w.engine.(*XDPoS.XDPoS) var parent *types.Block if c != nil { - parent = c.FindParentBlockToAssign(self.chain, self.chain.CurrentBlock()) + parent = c.FindParentBlockToAssign(w.chain, w.chain.CurrentBlock()) } else { - parent = self.chain.CurrentBlock() + parent = w.chain.CurrentBlock() } var signers map[common.Address]struct{} - if parent.Hash().Hex() == self.lastParentBlockCommit { + if parent.Hash().Hex() == w.lastParentBlockCommit { return } - if !self.announceTxs && atomic.LoadInt32(&self.mining) == 0 { + if !w.announceTxs && atomic.LoadInt32(&w.mining) == 0 { return } // Only try to commit new work if we are mining - if atomic.LoadInt32(&self.mining) == 1 { + if atomic.LoadInt32(&w.mining) == 1 { // check if we are right after parent's coinbase in the list - if self.config.XDPoS != nil { - ok, err := c.YourTurn(self.chain, parent.Header(), self.coinbase) + if w.config.XDPoS != nil { + ok, err := c.YourTurn(w.chain, parent.Header(), w.coinbase) if err != nil { log.Warn("Failed when trying to commit new work", "err", err) return @@ -612,15 +612,15 @@ func (self *worker) commitNewWork() { ParentHash: parent.Hash(), Number: num.Add(num, common.Big1), GasLimit: params.TargetGasLimit, - Extra: self.extra, + Extra: w.extra, Time: big.NewInt(tstamp), } // Only set the coinbase if we are mining (avoid spurious block rewards) - if atomic.LoadInt32(&self.mining) == 1 { - header.Coinbase = self.coinbase + if atomic.LoadInt32(&w.mining) == 1 { + header.Coinbase = w.coinbase } - if err := self.engine.Prepare(self.chain, header); err != nil { + if err := w.engine.Prepare(w.chain, header); err != nil { if err == consensus.ErrNotReadyToPropose { log.Info("Waiting...", "err", err) return @@ -629,12 +629,12 @@ func (self *worker) commitNewWork() { return } // If we are care about TheDAO hard-fork check whether to override the extra-data or not - if daoBlock := self.config.DAOForkBlock; daoBlock != nil { + if daoBlock := w.config.DAOForkBlock; daoBlock != nil { // Check whether the block is among the fork extra-override range limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange) if header.Number.Cmp(daoBlock) >= 0 && header.Number.Cmp(limit) < 0 { // Depending whether we support or oppose the fork, override differently - if self.config.DAOForkSupport { + if w.config.DAOForkSupport { header.Extra = common.CopyBytes(params.DAOForkBlockExtra) } else if bytes.Equal(header.Extra, params.DAOForkBlockExtra) { header.Extra = []byte{} // If miner opposes, don't let it use the reserved extra-data @@ -642,14 +642,14 @@ func (self *worker) commitNewWork() { } } // Could potentially happen if starting to mine in an odd state. - err := self.makeCurrent(parent, header) + err := w.makeCurrent(parent, header) if err != nil { log.Error("Failed to create mining context", "err", err) return } // Create the current work task and check any fork transitions needed - work := self.current - if self.config.DAOForkSupport && self.config.DAOForkBlock != nil && self.config.DAOForkBlock.Cmp(header.Number) == 0 { + work := w.current + if w.config.DAOForkSupport && w.config.DAOForkBlock != nil && w.config.DAOForkBlock.Cmp(header.Number) == 0 { misc.ApplyDAOHardFork(work.state) } if common.TIPSigning.Cmp(header.Number) == 0 { @@ -670,31 +670,31 @@ func (self *worker) commitNewWork() { lendingFinalizedTradeTransaction *types.Transaction ) feeCapacity := state.GetTRC21FeeCapacityFromStateWithCache(parent.Root(), work.state) - if self.config.XDPoS != nil { - isEpochSwitchBlock, _, err := self.engine.(*XDPoS.XDPoS).IsEpochSwitch(header) + if w.config.XDPoS != nil { + isEpochSwitchBlock, _, err := w.engine.(*XDPoS.XDPoS).IsEpochSwitch(header) if err != nil { log.Error("[commitNewWork] fail to check if block is epoch switch block when fetching pending transactions", "BlockNum", header.Number, "Hash", header.Hash()) } if !isEpochSwitchBlock { - pending, err := self.eth.TxPool().Pending() + pending, err := w.eth.TxPool().Pending() if err != nil { log.Error("Failed to fetch pending transactions", "err", err) return } - txs, specialTxs = types.NewTransactionsByPriceAndNonce(self.current.signer, pending, signers, feeCapacity) + txs, specialTxs = types.NewTransactionsByPriceAndNonce(w.current.signer, pending, signers, feeCapacity) } } - if atomic.LoadInt32(&self.mining) == 1 { - wallet, err := self.eth.AccountManager().Find(accounts.Account{Address: self.coinbase}) + if atomic.LoadInt32(&w.mining) == 1 { + wallet, err := w.eth.AccountManager().Find(accounts.Account{Address: w.coinbase}) if err != nil { - log.Warn("Can't find coinbase account wallet", "coinbase", self.coinbase, "err", err) + log.Warn("Can't find coinbase account wallet", "coinbase", w.coinbase, "err", err) return } - if self.config.XDPoS != nil && self.chain.Config().IsTIPXDCXMiner(header.Number) { - XDCX := self.eth.GetXDCX() - XDCXLending := self.eth.GetXDCXLending() - if XDCX != nil && header.Number.Uint64() > self.config.XDPoS.Epoch { - isEpochSwitchBlock, epochNumber, err := self.engine.(*XDPoS.XDPoS).IsEpochSwitch(header) + if w.config.XDPoS != nil && w.chain.Config().IsTIPXDCXMiner(header.Number) { + XDCX := w.eth.GetXDCX() + XDCXLending := w.eth.GetXDCXLending() + if XDCX != nil && header.Number.Uint64() > w.config.XDPoS.Epoch { + isEpochSwitchBlock, epochNumber, err := w.engine.(*XDPoS.XDPoS).IsEpochSwitch(header) if err != nil { log.Error("[commitNewWork] fail to check if block is epoch switch block when performing XDCX and XDCXLending operations", "BlockNum", header.Number, "Hash", header.Hash()) } @@ -709,16 +709,16 @@ func (self *worker) commitNewWork() { // won't grasp tx at checkpoint //https://github.com/XinFinOrg/XDPoSChain-v1/pull/416 log.Debug("Start processing order pending") - tradingOrderPending, _ := self.eth.OrderPool().Pending() + tradingOrderPending, _ := w.eth.OrderPool().Pending() log.Debug("Start processing order pending", "len", len(tradingOrderPending)) - tradingTxMatches, tradingMatchingResults = XDCX.ProcessOrderPending(header, self.coinbase, self.chain, tradingOrderPending, work.state, work.tradingState) + tradingTxMatches, tradingMatchingResults = XDCX.ProcessOrderPending(header, w.coinbase, w.chain, tradingOrderPending, work.state, work.tradingState) log.Debug("trading transaction matches found", "tradingTxMatches", len(tradingTxMatches)) - lendingOrderPending, _ := self.eth.LendingPool().Pending() - lendingInput, lendingMatchingResults = XDCXLending.ProcessOrderPending(header, self.coinbase, self.chain, lendingOrderPending, work.state, work.lendingState, work.tradingState) + lendingOrderPending, _ := w.eth.LendingPool().Pending() + lendingInput, lendingMatchingResults = XDCXLending.ProcessOrderPending(header, w.coinbase, w.chain, lendingOrderPending, work.state, work.lendingState, work.tradingState) log.Debug("lending transaction matches found", "lendingInput", len(lendingInput), "lendingMatchingResults", len(lendingMatchingResults)) - if header.Number.Uint64()%self.config.XDPoS.Epoch == common.LiquidateLendingTradeBlock { - updatedTrades, liquidatedTrades, autoRepayTrades, autoTopUpTrades, autoRecallTrades, err = XDCXLending.ProcessLiquidationData(header, self.chain, work.state, work.tradingState, work.lendingState) + if header.Number.Uint64()%w.config.XDPoS.Epoch == common.LiquidateLendingTradeBlock { + updatedTrades, liquidatedTrades, autoRepayTrades, autoTopUpTrades, autoRecallTrades, err = XDCXLending.ProcessLiquidationData(header, w.chain, work.state, work.tradingState, work.lendingState) if err != nil { log.Error("Fail when process lending liquidation data ", "error", err) return @@ -737,16 +737,16 @@ func (self *worker) commitNewWork() { log.Error("Fail to marshal txMatch", "error", err) return } - nonce := work.state.GetNonce(self.coinbase) + nonce := work.state.GetNonce(w.coinbase) tx := types.NewTransaction(nonce, common.XDCXAddrBinary, big.NewInt(0), txMatchGasLimit, big.NewInt(0), txMatchBytes) - txM, err := wallet.SignTx(accounts.Account{Address: self.coinbase}, tx, self.config.ChainId) + txM, err := wallet.SignTx(accounts.Account{Address: w.coinbase}, tx, w.config.ChainId) if err != nil { log.Error("Fail to create tx matches", "error", err) return } else { tradingTransaction = txM if XDCX.IsSDKNode() { - self.chain.AddMatchingResult(tradingTransaction.Hash(), tradingMatchingResults) + w.chain.AddMatchingResult(tradingTransaction.Hash(), tradingMatchingResults) } // force adding trading, lending transaction to this block if tradingTransaction != nil { @@ -767,16 +767,16 @@ func (self *worker) commitNewWork() { log.Error("Fail to marshal lendingData", "error", err) return } - nonce := work.state.GetNonce(self.coinbase) + nonce := work.state.GetNonce(w.coinbase) lendingTx := types.NewTransaction(nonce, common.XDCXLendingAddressBinary, big.NewInt(0), txMatchGasLimit, big.NewInt(0), lendingDataBytes) - signedLendingTx, err := wallet.SignTx(accounts.Account{Address: self.coinbase}, lendingTx, self.config.ChainId) + signedLendingTx, err := wallet.SignTx(accounts.Account{Address: w.coinbase}, lendingTx, w.config.ChainId) if err != nil { log.Error("Fail to create lending tx", "error", err) return } else { lendingTransaction = signedLendingTx if XDCX.IsSDKNode() { - self.chain.AddLendingResult(lendingTransaction.Hash(), lendingMatchingResults) + w.chain.AddLendingResult(lendingTransaction.Hash(), lendingMatchingResults) } if lendingTransaction != nil { specialTxs = append(specialTxs, lendingTransaction) @@ -791,16 +791,16 @@ func (self *worker) commitNewWork() { log.Error("Fail to marshal lendingData", "error", err) return } - nonce := work.state.GetNonce(self.coinbase) + nonce := work.state.GetNonce(w.coinbase) finalizedTx := types.NewTransaction(nonce, common.XDCXLendingFinalizedTradeAddressBinary, big.NewInt(0), txMatchGasLimit, big.NewInt(0), finalizedTradeData) - signedFinalizedTx, err := wallet.SignTx(accounts.Account{Address: self.coinbase}, finalizedTx, self.config.ChainId) + signedFinalizedTx, err := wallet.SignTx(accounts.Account{Address: w.coinbase}, finalizedTx, w.config.ChainId) if err != nil { log.Error("Fail to create lending tx", "error", err) return } else { lendingFinalizedTradeTransaction = signedFinalizedTx if XDCX.IsSDKNode() { - self.chain.AddFinalizedTrades(lendingFinalizedTradeTransaction.Hash(), updatedTrades) + w.chain.AddFinalizedTrades(lendingFinalizedTradeTransaction.Hash(), updatedTrades) } if lendingFinalizedTradeTransaction != nil { specialTxs = append(specialTxs, lendingFinalizedTradeTransaction) @@ -811,8 +811,8 @@ func (self *worker) commitNewWork() { XDCxStateRoot := work.tradingState.IntermediateRoot() LendingStateRoot := work.lendingState.IntermediateRoot() txData := append(XDCxStateRoot.Bytes(), LendingStateRoot.Bytes()...) - tx := types.NewTransaction(work.state.GetNonce(self.coinbase), common.TradingStateAddrBinary, big.NewInt(0), txMatchGasLimit, big.NewInt(0), txData) - txStateRoot, err := wallet.SignTx(accounts.Account{Address: self.coinbase}, tx, self.config.ChainId) + tx := types.NewTransaction(work.state.GetNonce(w.coinbase), common.TradingStateAddrBinary, big.NewInt(0), txMatchGasLimit, big.NewInt(0), txData) + txStateRoot, err := wallet.SignTx(accounts.Account{Address: w.coinbase}, tx, w.config.ChainId) if err != nil { log.Error("Fail to create tx state root", "error", err) return @@ -820,29 +820,29 @@ func (self *worker) commitNewWork() { specialTxs = append(specialTxs, txStateRoot) } } - work.commitTransactions(self.mux, feeCapacity, txs, specialTxs, self.chain, self.coinbase, &self.pendingLogsFeed) + work.commitTransactions(w.mux, feeCapacity, txs, specialTxs, w.chain, w.coinbase, &w.pendingLogsFeed) // compute uncles for the new block. var ( uncles []*types.Header ) // Create the new block to seal with the consensus engine - if work.Block, err = self.engine.Finalize(self.chain, header, work.state, work.parentState, work.txs, uncles, work.receipts); err != nil { + if work.Block, err = w.engine.Finalize(w.chain, header, work.state, work.parentState, work.txs, uncles, work.receipts); err != nil { log.Error("Failed to finalize block for sealing", "err", err) return } - if atomic.LoadInt32(&self.mining) == 1 { + if atomic.LoadInt32(&w.mining) == 1 { log.Info("Committing new block", "number", work.Block.Number(), "txs", work.tcount, "special-txs", len(specialTxs), "uncles", len(uncles), "elapsed", common.PrettyDuration(time.Since(tstart))) - self.unconfirmed.Shift(work.Block.NumberU64() - 1) - self.lastParentBlockCommit = parent.Hash().Hex() + w.unconfirmed.Shift(work.Block.NumberU64() - 1) + w.lastParentBlockCommit = parent.Hash().Hex() } - self.push(work) - self.updateSnapshot() + w.push(work) + w.updateSnapshot() } -func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Address]*big.Int, txs *types.TransactionsByPriceAndNonce, specialTxs types.Transactions, bc *core.BlockChain, coinbase common.Address, pendingLogsFeed *event.Feed) { - gp := new(core.GasPool).AddGas(env.header.GasLimit) +func (w *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Address]*big.Int, txs *types.TransactionsByPriceAndNonce, specialTxs types.Transactions, bc *core.BlockChain, coinbase common.Address, pendingLogsFeed *event.Feed) { + gp := new(core.GasPool).AddGas(w.header.GasLimit) balanceUpdated := map[common.Address]*big.Int{} totalFeeUsed := big.NewInt(0) var coalescedLogs []*types.Log @@ -850,7 +850,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad for _, tx := range specialTxs { to := tx.To() //HF number for black-list - if (env.header.Number.Uint64() >= common.BlackListHFNumber) && !common.IsTestnet { + if (w.header.Number.Uint64() >= common.BlackListHFNumber) && !common.IsTestnet { from := tx.From() // check if sender is in black list if from != nil && common.Blacklist[*from] { @@ -891,12 +891,12 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad // during transaction acceptance is the transaction pool. // // We use the eip155 signer regardless of the current hf. - from, _ := types.Sender(env.signer, tx) + from, _ := types.Sender(w.signer, tx) // Check whether the tx is replay protected. If we're not in the EIP155 hf // phase, start ignoring the sender until we do. hash := tx.Hash() - if tx.Protected() && !env.config.IsEIP155(env.header.Number) { - log.Trace("Ignoring reply protected special transaction", "hash", hash, "eip155", env.config.EIP155Block) + if tx.Protected() && !w.config.IsEIP155(w.header.Number) { + log.Trace("Ignoring reply protected special transaction", "hash", hash, "eip155", w.config.EIP155Block) continue } if *to == common.BlockSignersBinary { @@ -905,20 +905,20 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad continue } blkNumber := binary.BigEndian.Uint64(data[8:40]) - if blkNumber >= env.header.Number.Uint64() || blkNumber <= env.header.Number.Uint64()-env.config.XDPoS.Epoch*2 { - log.Trace("Data special transaction invalid number", "hash", hash, "blkNumber", blkNumber, "miner", env.header.Number) + if blkNumber >= w.header.Number.Uint64() || blkNumber <= w.header.Number.Uint64()-w.config.XDPoS.Epoch*2 { + log.Trace("Data special transaction invalid number", "hash", hash, "blkNumber", blkNumber, "miner", w.header.Number) continue } } // Start executing the transaction - env.state.Prepare(hash, env.tcount) + w.state.Prepare(hash, w.tcount) - nonce := env.state.GetNonce(from) + nonce := w.state.GetNonce(from) if nonce != tx.Nonce() && !tx.IsSkipNonceTransaction() { log.Trace("Skipping account with special transaction invalid nonce", "sender", from, "nonce", nonce, "tx nonce ", tx.Nonce(), "to", to) continue } - err, logs, tokenFeeUsed, gas := env.commitTransaction(balanceFee, tx, bc, coinbase, gp) + err, logs, tokenFeeUsed, gas := w.commitTransaction(balanceFee, tx, bc, coinbase, gp) switch err { case core.ErrNonceTooLow: // New head notification data race between the transaction pool and miner, shift @@ -930,7 +930,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad case nil: // Everything ok, collect the logs and shift in the next transaction from the same account coalescedLogs = append(coalescedLogs, logs...) - env.tcount++ + w.tcount++ default: // Strange error, discard the transaction and get the next in line (note, the @@ -938,7 +938,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad log.Debug("Add Special Transaction failed, account skipped", "hash", hash, "sender", from, "nonce", tx.Nonce(), "to", to, "err", err) } if tokenFeeUsed { - fee := common.GetGasFee(env.header.Number.Uint64(), gas) + fee := common.GetGasFee(w.header.Number.Uint64(), gas) balanceFee[*to] = new(big.Int).Sub(balanceFee[*to], fee) balanceUpdated[*to] = balanceFee[*to] totalFeeUsed = totalFeeUsed.Add(totalFeeUsed, fee) @@ -963,7 +963,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad //HF number for black-list to := tx.To() - if (env.header.Number.Uint64() >= common.BlackListHFNumber) && !common.IsTestnet { + if (w.header.Number.Uint64() >= common.BlackListHFNumber) && !common.IsTestnet { from := tx.From() // check if sender is in black list if from != nil && common.Blacklist[*from] { @@ -1002,18 +1002,18 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad // during transaction acceptance is the transaction pool. // // We use the eip155 signer regardless of the current hf. - from, _ := types.Sender(env.signer, tx) + from, _ := types.Sender(w.signer, tx) hash := tx.Hash() // Check whether the tx is replay protected. If we're not in the EIP155 hf // phase, start ignoring the sender until we do. - if tx.Protected() && !env.config.IsEIP155(env.header.Number) { - log.Trace("Ignoring reply protected transaction", "hash", hash, "eip155", env.config.EIP155Block) + if tx.Protected() && !w.config.IsEIP155(w.header.Number) { + log.Trace("Ignoring reply protected transaction", "hash", hash, "eip155", w.config.EIP155Block) txs.Pop() continue } // Start executing the transaction - env.state.Prepare(hash, env.tcount) - nonce := env.state.GetNonce(from) + w.state.Prepare(hash, w.tcount) + nonce := w.state.GetNonce(from) if nonce > tx.Nonce() { // New head notification data race between the transaction pool and miner, shift log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) @@ -1026,7 +1026,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad txs.Pop() continue } - err, logs, tokenFeeUsed, gas := env.commitTransaction(balanceFee, tx, bc, coinbase, gp) + err, logs, tokenFeeUsed, gas := w.commitTransaction(balanceFee, tx, bc, coinbase, gp) switch { case errors.Is(err, core.ErrGasLimitReached): // Pop the current out-of-gas transaction without shifting in the next from the account @@ -1046,7 +1046,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad case errors.Is(err, nil): // Everything ok, collect the logs and shift in the next transaction from the same account coalescedLogs = append(coalescedLogs, logs...) - env.tcount++ + w.tcount++ txs.Shift() case errors.Is(err, core.ErrTxTypeNotSupported): @@ -1061,13 +1061,13 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad txs.Shift() } if tokenFeeUsed { - fee := common.GetGasFee(env.header.Number.Uint64(), gas) + fee := common.GetGasFee(w.header.Number.Uint64(), gas) balanceFee[*to] = new(big.Int).Sub(balanceFee[*to], fee) balanceUpdated[*to] = balanceFee[*to] totalFeeUsed = totalFeeUsed.Add(totalFeeUsed, fee) } } - state.UpdateTRC21Fee(env.state, balanceUpdated, totalFeeUsed) + state.UpdateTRC21Fee(w.state, balanceUpdated, totalFeeUsed) // make a copy, the state caches the logs and these logs get "upgraded" from pending to mined // logs by filling in the block hash when the block was mined by the local miner. This can // cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed. @@ -1079,27 +1079,27 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad } pendingLogsFeed.Send(cpy) } - if env.tcount > 0 { + if w.tcount > 0 { go func(tcount int) { err := mux.Post(core.PendingStateEvent{}) if err != nil { log.Warn("[commitTransactions] Error when sending PendingStateEvent", "tcount", tcount) } - }(env.tcount) + }(w.tcount) } } -func (env *Work) commitTransaction(balanceFee map[common.Address]*big.Int, tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) (error, []*types.Log, bool, uint64) { - snap := env.state.Snapshot() +func (w *Work) commitTransaction(balanceFee map[common.Address]*big.Int, tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) (error, []*types.Log, bool, uint64) { + snap := w.state.Snapshot() - receipt, gas, err, tokenFeeUsed := core.ApplyTransaction(env.config, balanceFee, bc, &coinbase, gp, env.state, env.tradingState, env.header, tx, &env.header.GasUsed, vm.Config{}) + receipt, gas, err, tokenFeeUsed := core.ApplyTransaction(w.config, balanceFee, bc, &coinbase, gp, w.state, w.tradingState, w.header, tx, &w.header.GasUsed, vm.Config{}) if err != nil { - env.state.RevertToSnapshot(snap) + w.state.RevertToSnapshot(snap) return err, nil, false, 0 } - env.txs = append(env.txs, tx) - env.receipts = append(env.receipts, receipt) + w.txs = append(w.txs, tx) + w.receipts = append(w.receipts, receipt) return nil, receipt.Logs, tokenFeeUsed, gas } diff --git a/p2p/message.go b/p2p/message.go index d39bcb31f6b6..58c159ee7988 100644 --- a/p2p/message.go +++ b/p2p/message.go @@ -47,21 +47,21 @@ type Msg struct { // the given value, which must be a pointer. // // For the decoding rules, please see package rlp. -func (msg Msg) Decode(val interface{}) error { - s := rlp.NewStream(msg.Payload, uint64(msg.Size)) +func (m Msg) Decode(val interface{}) error { + s := rlp.NewStream(m.Payload, uint64(m.Size)) if err := s.Decode(val); err != nil { - return newPeerError(errInvalidMsg, "(code %x) (size %d) %v", msg.Code, msg.Size, err) + return newPeerError(errInvalidMsg, "(code %x) (size %d) %v", m.Code, m.Size, err) } return nil } -func (msg Msg) String() string { - return fmt.Sprintf("msg #%v (%v bytes)", msg.Code, msg.Size) +func (m Msg) String() string { + return fmt.Sprintf("msg #%v (%v bytes)", m.Code, m.Size) } // Discard reads any remaining payload data into a black hole. -func (msg Msg) Discard() error { - _, err := io.Copy(io.Discard, msg.Payload) +func (m Msg) Discard() error { + _, err := io.Copy(io.Discard, m.Payload) return err } @@ -119,24 +119,24 @@ type eofSignal struct { // note: when using eofSignal to detect whether a message payload // has been read, Read might not be called for zero sized messages. -func (r *eofSignal) Read(buf []byte) (int, error) { - if r.count == 0 { - if r.eof != nil { - r.eof <- struct{}{} - r.eof = nil +func (s *eofSignal) Read(buf []byte) (int, error) { + if s.count == 0 { + if s.eof != nil { + s.eof <- struct{}{} + s.eof = nil } return 0, io.EOF } max := len(buf) - if int(r.count) < len(buf) { - max = int(r.count) + if int(s.count) < len(buf) { + max = int(s.count) } - n, err := r.wrapped.Read(buf[:max]) - r.count -= uint32(n) - if (err != nil || r.count == 0) && r.eof != nil { - r.eof <- struct{}{} // tell Peer that msg has been consumed - r.eof = nil + n, err := s.wrapped.Read(buf[:max]) + s.count -= uint32(n) + if (err != nil || s.count == 0) && s.eof != nil { + s.eof <- struct{}{} // tell Peer that msg has been consumed + s.eof = nil } return n, err } @@ -269,15 +269,15 @@ func newMsgEventer(rw MsgReadWriter, feed *event.Feed, peerID discover.NodeID, p // ReadMsg reads a message from the underlying MsgReadWriter and emits a // "message received" event -func (self *msgEventer) ReadMsg() (Msg, error) { - msg, err := self.MsgReadWriter.ReadMsg() +func (e *msgEventer) ReadMsg() (Msg, error) { + msg, err := e.MsgReadWriter.ReadMsg() if err != nil { return msg, err } - self.feed.Send(&PeerEvent{ + e.feed.Send(&PeerEvent{ Type: PeerEventTypeMsgRecv, - Peer: self.peerID, - Protocol: self.Protocol, + Peer: e.peerID, + Protocol: e.Protocol, MsgCode: &msg.Code, MsgSize: &msg.Size, }) @@ -286,15 +286,15 @@ func (self *msgEventer) ReadMsg() (Msg, error) { // WriteMsg writes a message to the underlying MsgReadWriter and emits a // "message sent" event -func (self *msgEventer) WriteMsg(msg Msg) error { - err := self.MsgReadWriter.WriteMsg(msg) +func (e *msgEventer) WriteMsg(msg Msg) error { + err := e.MsgReadWriter.WriteMsg(msg) if err != nil { return err } - self.feed.Send(&PeerEvent{ + e.feed.Send(&PeerEvent{ Type: PeerEventTypeMsgSend, - Peer: self.peerID, - Protocol: self.Protocol, + Peer: e.peerID, + Protocol: e.Protocol, MsgCode: &msg.Code, MsgSize: &msg.Size, }) @@ -303,8 +303,8 @@ func (self *msgEventer) WriteMsg(msg Msg) error { // Close closes the underlying MsgReadWriter if it implements the io.Closer // interface -func (self *msgEventer) Close() error { - if v, ok := self.MsgReadWriter.(io.Closer); ok { +func (e *msgEventer) Close() error { + if v, ok := e.MsgReadWriter.(io.Closer); ok { return v.Close() } return nil diff --git a/p2p/peer_error.go b/p2p/peer_error.go index 64530116e6d7..3ee1e1ab2562 100644 --- a/p2p/peer_error.go +++ b/p2p/peer_error.go @@ -48,8 +48,8 @@ func newPeerError(code int, format string, v ...interface{}) *peerError { return err } -func (self *peerError) Error() string { - return self.message +func (e *peerError) Error() string { + return e.message } var errProtocolReturned = errors.New("protocol returned") diff --git a/p2p/simulations/adapters/inproc.go b/p2p/simulations/adapters/inproc.go index fce627d90605..1f64941dfa96 100644 --- a/p2p/simulations/adapters/inproc.go +++ b/p2p/simulations/adapters/inproc.go @@ -51,18 +51,18 @@ func NewSimAdapter(services map[string]ServiceFunc) *SimAdapter { } // Name returns the name of the adapter for logging purposes -func (s *SimAdapter) Name() string { +func (sa *SimAdapter) Name() string { return "sim-adapter" } // NewNode returns a new SimNode using the given config -func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) { - s.mtx.Lock() - defer s.mtx.Unlock() +func (sa *SimAdapter) NewNode(config *NodeConfig) (Node, error) { + sa.mtx.Lock() + defer sa.mtx.Unlock() // check a node with the ID doesn't already exist id := config.ID - if _, exists := s.nodes[id]; exists { + if _, exists := sa.nodes[id]; exists { return nil, fmt.Errorf("node already exists: %s", id) } @@ -71,7 +71,7 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) { return nil, errors.New("node must have at least one service") } for _, service := range config.Services { - if _, exists := s.services[service]; !exists { + if _, exists := sa.services[service]; !exists { return nil, fmt.Errorf("unknown node service %q", service) } } @@ -81,7 +81,7 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) { PrivateKey: config.PrivateKey, MaxPeers: math.MaxInt32, NoDiscovery: true, - Dialer: s, + Dialer: sa, EnableMsgEvents: true, }, NoUSB: true, @@ -95,18 +95,18 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) { ID: id, config: config, node: n, - adapter: s, + adapter: sa, running: make(map[string]node.Service), connected: make(map[discover.NodeID]bool), } - s.nodes[id] = simNode + sa.nodes[id] = simNode return simNode, nil } // Dial implements the p2p.NodeDialer interface by connecting to the node using // an in-memory net.Pipe connection -func (s *SimAdapter) Dial(dest *discover.Node) (conn net.Conn, err error) { - node, ok := s.GetNode(dest.ID) +func (sa *SimAdapter) Dial(dest *discover.Node) (conn net.Conn, err error) { + node, ok := sa.GetNode(dest.ID) if !ok { return nil, fmt.Errorf("unknown node: %s", dest.ID) } @@ -125,8 +125,8 @@ func (s *SimAdapter) Dial(dest *discover.Node) (conn net.Conn, err error) { // DialRPC implements the RPCDialer interface by creating an in-memory RPC // client of the given node -func (s *SimAdapter) DialRPC(id discover.NodeID) (*rpc.Client, error) { - node, ok := s.GetNode(id) +func (sa *SimAdapter) DialRPC(id discover.NodeID) (*rpc.Client, error) { + node, ok := sa.GetNode(id) if !ok { return nil, fmt.Errorf("unknown node: %s", id) } @@ -138,10 +138,10 @@ func (s *SimAdapter) DialRPC(id discover.NodeID) (*rpc.Client, error) { } // GetNode returns the node with the given ID if it exists -func (s *SimAdapter) GetNode(id discover.NodeID) (*SimNode, bool) { - s.mtx.RLock() - defer s.mtx.RUnlock() - node, ok := s.nodes[id] +func (sa *SimAdapter) GetNode(id discover.NodeID) (*SimNode, bool) { + sa.mtx.RLock() + defer sa.mtx.RUnlock() + node, ok := sa.nodes[id] return node, ok } @@ -161,30 +161,30 @@ type SimNode struct { } // Addr returns the node's discovery address -func (self *SimNode) Addr() []byte { - return []byte(self.Node().String()) +func (sn *SimNode) Addr() []byte { + return []byte(sn.Node().String()) } // Node returns a discover.Node representing the SimNode -func (self *SimNode) Node() *discover.Node { - return discover.NewNode(self.ID, net.IP{127, 0, 0, 1}, 30303, 30303) +func (sn *SimNode) Node() *discover.Node { + return discover.NewNode(sn.ID, net.IP{127, 0, 0, 1}, 30303, 30303) } // Client returns an rpc.Client which can be used to communicate with the // underlying services (it is set once the node has started) -func (self *SimNode) Client() (*rpc.Client, error) { - self.lock.RLock() - defer self.lock.RUnlock() - if self.client == nil { +func (sn *SimNode) Client() (*rpc.Client, error) { + sn.lock.RLock() + defer sn.lock.RUnlock() + if sn.client == nil { return nil, errors.New("node not started") } - return self.client, nil + return sn.client, nil } // ServeRPC serves RPC requests over the given connection by creating an // in-memory client to the node's RPC server -func (self *SimNode) ServeRPC(conn *websocket.Conn) error { - handler, err := self.node.RPCHandler() +func (sn *SimNode) ServeRPC(conn *websocket.Conn) error { + handler, err := sn.node.RPCHandler() if err != nil { return err } @@ -195,13 +195,13 @@ func (self *SimNode) ServeRPC(conn *websocket.Conn) error { // Snapshots creates snapshots of the services by calling the // simulation_snapshot RPC method -func (self *SimNode) Snapshots() (map[string][]byte, error) { - self.lock.RLock() - services := make(map[string]node.Service, len(self.running)) - for name, service := range self.running { +func (sn *SimNode) Snapshots() (map[string][]byte, error) { + sn.lock.RLock() + services := make(map[string]node.Service, len(sn.running)) + for name, service := range sn.running { services[name] = service } - self.lock.RUnlock() + sn.lock.RUnlock() if len(services) == 0 { return nil, errors.New("no running services") } @@ -221,23 +221,23 @@ func (self *SimNode) Snapshots() (map[string][]byte, error) { } // Start registers the services and starts the underlying devp2p node -func (self *SimNode) Start(snapshots map[string][]byte) error { +func (sn *SimNode) Start(snapshots map[string][]byte) error { newService := func(name string) func(ctx *node.ServiceContext) (node.Service, error) { return func(nodeCtx *node.ServiceContext) (node.Service, error) { ctx := &ServiceContext{ - RPCDialer: self.adapter, + RPCDialer: sn.adapter, NodeContext: nodeCtx, - Config: self.config, + Config: sn.config, } if snapshots != nil { ctx.Snapshot = snapshots[name] } - serviceFunc := self.adapter.services[name] + serviceFunc := sn.adapter.services[name] service, err := serviceFunc(ctx) if err != nil { return nil, err } - self.running[name] = service + sn.running[name] = service return service, nil } } @@ -245,9 +245,9 @@ func (self *SimNode) Start(snapshots map[string][]byte) error { // ensure we only register the services once in the case of the node // being stopped and then started again var regErr error - self.registerOnce.Do(func() { - for _, name := range self.config.Services { - if err := self.node.Register(newService(name)); err != nil { + sn.registerOnce.Do(func() { + for _, name := range sn.config.Services { + if err := sn.node.Register(newService(name)); err != nil { regErr = err return } @@ -257,54 +257,54 @@ func (self *SimNode) Start(snapshots map[string][]byte) error { return regErr } - if err := self.node.Start(); err != nil { + if err := sn.node.Start(); err != nil { return err } // create an in-process RPC client - handler, err := self.node.RPCHandler() + handler, err := sn.node.RPCHandler() if err != nil { return err } - self.lock.Lock() - self.client = rpc.DialInProc(handler) - self.lock.Unlock() + sn.lock.Lock() + sn.client = rpc.DialInProc(handler) + sn.lock.Unlock() return nil } // Stop closes the RPC client and stops the underlying devp2p node -func (self *SimNode) Stop() error { - self.lock.Lock() - if self.client != nil { - self.client.Close() - self.client = nil +func (sn *SimNode) Stop() error { + sn.lock.Lock() + if sn.client != nil { + sn.client.Close() + sn.client = nil } - self.lock.Unlock() - return self.node.Stop() + sn.lock.Unlock() + return sn.node.Stop() } // Services returns a copy of the underlying services -func (self *SimNode) Services() []node.Service { - self.lock.RLock() - defer self.lock.RUnlock() - services := make([]node.Service, 0, len(self.running)) - for _, service := range self.running { +func (sn *SimNode) Services() []node.Service { + sn.lock.RLock() + defer sn.lock.RUnlock() + services := make([]node.Service, 0, len(sn.running)) + for _, service := range sn.running { services = append(services, service) } return services } // Server returns the underlying p2p.Server -func (self *SimNode) Server() *p2p.Server { - return self.node.Server() +func (sn *SimNode) Server() *p2p.Server { + return sn.node.Server() } // SubscribeEvents subscribes the given channel to peer events from the // underlying p2p.Server -func (self *SimNode) SubscribeEvents(ch chan *p2p.PeerEvent) event.Subscription { - srv := self.Server() +func (sn *SimNode) SubscribeEvents(ch chan *p2p.PeerEvent) event.Subscription { + srv := sn.Server() if srv == nil { panic("node not running") } @@ -312,12 +312,12 @@ func (self *SimNode) SubscribeEvents(ch chan *p2p.PeerEvent) event.Subscription } // NodeInfo returns information about the node -func (self *SimNode) NodeInfo() *p2p.NodeInfo { - server := self.Server() +func (sn *SimNode) NodeInfo() *p2p.NodeInfo { + server := sn.Server() if server == nil { return &p2p.NodeInfo{ - ID: self.ID.String(), - Enode: self.Node().String(), + ID: sn.ID.String(), + Enode: sn.Node().String(), } } return server.NodeInfo() diff --git a/p2p/simulations/adapters/state.go b/p2p/simulations/adapters/state.go index 0d4ecfb0ffbe..416c504d2a05 100644 --- a/p2p/simulations/adapters/state.go +++ b/p2p/simulations/adapters/state.go @@ -20,12 +20,12 @@ type SimStateStore struct { m map[string][]byte } -func (self *SimStateStore) Load(s string) ([]byte, error) { - return self.m[s], nil +func (sss *SimStateStore) Load(s string) ([]byte, error) { + return sss.m[s], nil } -func (self *SimStateStore) Save(s string, data []byte) error { - self.m[s] = data +func (sss *SimStateStore) Save(s string, data []byte) error { + sss.m[s] = data return nil } diff --git a/p2p/simulations/network.go b/p2p/simulations/network.go index 5be5697da332..4c992fd1d5ed 100644 --- a/p2p/simulations/network.go +++ b/p2p/simulations/network.go @@ -74,22 +74,22 @@ func NewNetwork(nodeAdapter adapters.NodeAdapter, conf *NetworkConfig) *Network } // Events returns the output event feed of the Network. -func (self *Network) Events() *event.Feed { - return &self.events +func (net *Network) Events() *event.Feed { + return &net.events } // NewNode adds a new node to the network with a random ID -func (self *Network) NewNode() (*Node, error) { +func (net *Network) NewNode() (*Node, error) { conf := adapters.RandomNodeConfig() - conf.Services = []string{self.DefaultService} - return self.NewNodeWithConfig(conf) + conf.Services = []string{net.DefaultService} + return net.NewNodeWithConfig(conf) } // NewNodeWithConfig adds a new node to the network with the given config, // returning an error if a node with the same ID or name already exists -func (self *Network) NewNodeWithConfig(conf *adapters.NodeConfig) (*Node, error) { - self.lock.Lock() - defer self.lock.Unlock() +func (net *Network) NewNodeWithConfig(conf *adapters.NodeConfig) (*Node, error) { + net.lock.Lock() + defer net.lock.Unlock() // create a random ID and PrivateKey if not set if conf.ID == (discover.NodeID{}) { @@ -100,31 +100,31 @@ func (self *Network) NewNodeWithConfig(conf *adapters.NodeConfig) (*Node, error) id := conf.ID if conf.Reachable == nil { conf.Reachable = func(otherID discover.NodeID) bool { - _, err := self.InitConn(conf.ID, otherID) + _, err := net.InitConn(conf.ID, otherID) return err == nil } } // assign a name to the node if not set if conf.Name == "" { - conf.Name = fmt.Sprintf("node%02d", len(self.Nodes)+1) + conf.Name = fmt.Sprintf("node%02d", len(net.Nodes)+1) } // check the node doesn't already exist - if node := self.getNode(id); node != nil { + if node := net.getNode(id); node != nil { return nil, fmt.Errorf("node with ID %q already exists", id) } - if node := self.getNodeByName(conf.Name); node != nil { + if node := net.getNodeByName(conf.Name); node != nil { return nil, fmt.Errorf("node with name %q already exists", conf.Name) } // if no services are configured, use the default service if len(conf.Services) == 0 { - conf.Services = []string{self.DefaultService} + conf.Services = []string{net.DefaultService} } // use the NodeAdapter to create the node - adapterNode, err := self.nodeAdapter.NewNode(conf) + adapterNode, err := net.nodeAdapter.NewNode(conf) if err != nil { return nil, err } @@ -133,27 +133,27 @@ func (self *Network) NewNodeWithConfig(conf *adapters.NodeConfig) (*Node, error) Config: conf, } log.Trace(fmt.Sprintf("node %v created", id)) - self.nodeMap[id] = len(self.Nodes) - self.Nodes = append(self.Nodes, node) + net.nodeMap[id] = len(net.Nodes) + net.Nodes = append(net.Nodes, node) // emit a "control" event - self.events.Send(ControlEvent(node)) + net.events.Send(ControlEvent(node)) return node, nil } // Config returns the network configuration -func (self *Network) Config() *NetworkConfig { - return &self.NetworkConfig +func (net *Network) Config() *NetworkConfig { + return &net.NetworkConfig } // StartAll starts all nodes in the network -func (self *Network) StartAll() error { - for _, node := range self.Nodes { +func (net *Network) StartAll() error { + for _, node := range net.Nodes { if node.Up { continue } - if err := self.Start(node.ID()); err != nil { + if err := net.Start(node.ID()); err != nil { return err } } @@ -161,12 +161,12 @@ func (self *Network) StartAll() error { } // StopAll stops all nodes in the network -func (self *Network) StopAll() error { - for _, node := range self.Nodes { +func (net *Network) StopAll() error { + for _, node := range net.Nodes { if !node.Up { continue } - if err := self.Stop(node.ID()); err != nil { + if err := net.Stop(node.ID()); err != nil { return err } } @@ -174,21 +174,21 @@ func (self *Network) StopAll() error { } // Start starts the node with the given ID -func (self *Network) Start(id discover.NodeID) error { - return self.startWithSnapshots(id, nil) +func (net *Network) Start(id discover.NodeID) error { + return net.startWithSnapshots(id, nil) } // startWithSnapshots starts the node with the given ID using the give // snapshots -func (self *Network) startWithSnapshots(id discover.NodeID, snapshots map[string][]byte) error { - node := self.GetNode(id) +func (net *Network) startWithSnapshots(id discover.NodeID, snapshots map[string][]byte) error { + node := net.GetNode(id) if node == nil { return fmt.Errorf("node %v does not exist", id) } if node.Up { return fmt.Errorf("node %v already up", id) } - log.Trace(fmt.Sprintf("starting node %v: %v using %v", id, node.Up, self.nodeAdapter.Name())) + log.Trace(fmt.Sprintf("starting node %v: %v using %v", id, node.Up, net.nodeAdapter.Name())) if err := node.Start(snapshots); err != nil { log.Warn(fmt.Sprintf("start up failed: %v", err)) return err @@ -196,7 +196,7 @@ func (self *Network) startWithSnapshots(id discover.NodeID, snapshots map[string node.Up = true log.Info(fmt.Sprintf("started node %v: %v", id, node.Up)) - self.events.Send(NewEvent(node)) + net.events.Send(NewEvent(node)) // subscribe to peer events client, err := node.Client() @@ -208,22 +208,22 @@ func (self *Network) startWithSnapshots(id discover.NodeID, snapshots map[string if err != nil { return fmt.Errorf("error getting peer events for node %v: %s", id, err) } - go self.watchPeerEvents(id, events, sub) + go net.watchPeerEvents(id, events, sub) return nil } // watchPeerEvents reads peer events from the given channel and emits // corresponding network events -func (self *Network) watchPeerEvents(id discover.NodeID, events chan *p2p.PeerEvent, sub event.Subscription) { +func (net *Network) watchPeerEvents(id discover.NodeID, events chan *p2p.PeerEvent, sub event.Subscription) { defer func() { sub.Unsubscribe() // assume the node is now down - self.lock.Lock() - node := self.getNode(id) + net.lock.Lock() + node := net.getNode(id) node.Up = false - self.lock.Unlock() - self.events.Send(NewEvent(node)) + net.lock.Unlock() + net.events.Send(NewEvent(node)) }() for { select { @@ -235,16 +235,16 @@ func (self *Network) watchPeerEvents(id discover.NodeID, events chan *p2p.PeerEv switch event.Type { case p2p.PeerEventTypeAdd: - self.DidConnect(id, peer) + net.DidConnect(id, peer) case p2p.PeerEventTypeDrop: - self.DidDisconnect(id, peer) + net.DidDisconnect(id, peer) case p2p.PeerEventTypeMsgSend: - self.DidSend(id, peer, event.Protocol, *event.MsgCode) + net.DidSend(id, peer, event.Protocol, *event.MsgCode) case p2p.PeerEventTypeMsgRecv: - self.DidReceive(peer, id, event.Protocol, *event.MsgCode) + net.DidReceive(peer, id, event.Protocol, *event.MsgCode) } @@ -258,8 +258,8 @@ func (self *Network) watchPeerEvents(id discover.NodeID, events chan *p2p.PeerEv } // Stop stops the node with the given ID -func (self *Network) Stop(id discover.NodeID) error { - node := self.GetNode(id) +func (net *Network) Stop(id discover.NodeID) error { + node := net.GetNode(id) if node == nil { return fmt.Errorf("node %v does not exist", id) } @@ -272,15 +272,15 @@ func (self *Network) Stop(id discover.NodeID) error { node.Up = false log.Info(fmt.Sprintf("stop node %v: %v", id, node.Up)) - self.events.Send(ControlEvent(node)) + net.events.Send(ControlEvent(node)) return nil } // Connect connects two nodes together by calling the "admin_addPeer" RPC // method on the "one" node so that it connects to the "other" node -func (self *Network) Connect(oneID, otherID discover.NodeID) error { +func (net *Network) Connect(oneID, otherID discover.NodeID) error { log.Debug(fmt.Sprintf("connecting %s to %s", oneID, otherID)) - conn, err := self.InitConn(oneID, otherID) + conn, err := net.InitConn(oneID, otherID) if err != nil { return err } @@ -288,14 +288,14 @@ func (self *Network) Connect(oneID, otherID discover.NodeID) error { if err != nil { return err } - self.events.Send(ControlEvent(conn)) + net.events.Send(ControlEvent(conn)) return client.Call(nil, "admin_addPeer", string(conn.other.Addr())) } // Disconnect disconnects two nodes by calling the "admin_removePeer" RPC // method on the "one" node so that it disconnects from the "other" node -func (self *Network) Disconnect(oneID, otherID discover.NodeID) error { - conn := self.GetConn(oneID, otherID) +func (net *Network) Disconnect(oneID, otherID discover.NodeID) error { + conn := net.GetConn(oneID, otherID) if conn == nil { return fmt.Errorf("connection between %v and %v does not exist", oneID, otherID) } @@ -306,13 +306,13 @@ func (self *Network) Disconnect(oneID, otherID discover.NodeID) error { if err != nil { return err } - self.events.Send(ControlEvent(conn)) + net.events.Send(ControlEvent(conn)) return client.Call(nil, "admin_removePeer", string(conn.other.Addr())) } // DidConnect tracks the fact that the "one" node connected to the "other" node -func (self *Network) DidConnect(one, other discover.NodeID) error { - conn, err := self.GetOrCreateConn(one, other) +func (net *Network) DidConnect(one, other discover.NodeID) error { + conn, err := net.GetOrCreateConn(one, other) if err != nil { return fmt.Errorf("connection between %v and %v does not exist", one, other) } @@ -320,14 +320,14 @@ func (self *Network) DidConnect(one, other discover.NodeID) error { return fmt.Errorf("%v and %v already connected", one, other) } conn.Up = true - self.events.Send(NewEvent(conn)) + net.events.Send(NewEvent(conn)) return nil } // DidDisconnect tracks the fact that the "one" node disconnected from the // "other" node -func (self *Network) DidDisconnect(one, other discover.NodeID) error { - conn := self.GetConn(one, other) +func (net *Network) DidDisconnect(one, other discover.NodeID) error { + conn := net.GetConn(one, other) if conn == nil { return fmt.Errorf("connection between %v and %v does not exist", one, other) } @@ -336,12 +336,12 @@ func (self *Network) DidDisconnect(one, other discover.NodeID) error { } conn.Up = false conn.initiated = time.Now().Add(-dialBanTimeout) - self.events.Send(NewEvent(conn)) + net.events.Send(NewEvent(conn)) return nil } // DidSend tracks the fact that "sender" sent a message to "receiver" -func (self *Network) DidSend(sender, receiver discover.NodeID, proto string, code uint64) error { +func (net *Network) DidSend(sender, receiver discover.NodeID, proto string, code uint64) error { msg := &Msg{ One: sender, Other: receiver, @@ -349,12 +349,12 @@ func (self *Network) DidSend(sender, receiver discover.NodeID, proto string, cod Code: code, Received: false, } - self.events.Send(NewEvent(msg)) + net.events.Send(NewEvent(msg)) return nil } // DidReceive tracks the fact that "receiver" received a message from "sender" -func (self *Network) DidReceive(sender, receiver discover.NodeID, proto string, code uint64) error { +func (net *Network) DidReceive(sender, receiver discover.NodeID, proto string, code uint64) error { msg := &Msg{ One: sender, Other: receiver, @@ -362,36 +362,36 @@ func (self *Network) DidReceive(sender, receiver discover.NodeID, proto string, Code: code, Received: true, } - self.events.Send(NewEvent(msg)) + net.events.Send(NewEvent(msg)) return nil } // GetNode gets the node with the given ID, returning nil if the node does not // exist -func (self *Network) GetNode(id discover.NodeID) *Node { - self.lock.Lock() - defer self.lock.Unlock() - return self.getNode(id) +func (net *Network) GetNode(id discover.NodeID) *Node { + net.lock.Lock() + defer net.lock.Unlock() + return net.getNode(id) } // GetNode gets the node with the given name, returning nil if the node does // not exist -func (self *Network) GetNodeByName(name string) *Node { - self.lock.Lock() - defer self.lock.Unlock() - return self.getNodeByName(name) +func (net *Network) GetNodeByName(name string) *Node { + net.lock.Lock() + defer net.lock.Unlock() + return net.getNodeByName(name) } -func (self *Network) getNode(id discover.NodeID) *Node { - i, found := self.nodeMap[id] +func (net *Network) getNode(id discover.NodeID) *Node { + i, found := net.nodeMap[id] if !found { return nil } - return self.Nodes[i] + return net.Nodes[i] } -func (self *Network) getNodeByName(name string) *Node { - for _, node := range self.Nodes { +func (net *Network) getNodeByName(name string) *Node { + for _, node := range net.Nodes { if node.Config.Name == name { return node } @@ -400,40 +400,40 @@ func (self *Network) getNodeByName(name string) *Node { } // GetNodes returns the existing nodes -func (self *Network) GetNodes() (nodes []*Node) { - self.lock.Lock() - defer self.lock.Unlock() +func (net *Network) GetNodes() (nodes []*Node) { + net.lock.Lock() + defer net.lock.Unlock() - nodes = append(nodes, self.Nodes...) + nodes = append(nodes, net.Nodes...) return nodes } // GetConn returns the connection which exists between "one" and "other" // regardless of which node initiated the connection -func (self *Network) GetConn(oneID, otherID discover.NodeID) *Conn { - self.lock.Lock() - defer self.lock.Unlock() - return self.getConn(oneID, otherID) +func (net *Network) GetConn(oneID, otherID discover.NodeID) *Conn { + net.lock.Lock() + defer net.lock.Unlock() + return net.getConn(oneID, otherID) } // GetOrCreateConn is like GetConn but creates the connection if it doesn't // already exist -func (self *Network) GetOrCreateConn(oneID, otherID discover.NodeID) (*Conn, error) { - self.lock.Lock() - defer self.lock.Unlock() - return self.getOrCreateConn(oneID, otherID) +func (net *Network) GetOrCreateConn(oneID, otherID discover.NodeID) (*Conn, error) { + net.lock.Lock() + defer net.lock.Unlock() + return net.getOrCreateConn(oneID, otherID) } -func (self *Network) getOrCreateConn(oneID, otherID discover.NodeID) (*Conn, error) { - if conn := self.getConn(oneID, otherID); conn != nil { +func (net *Network) getOrCreateConn(oneID, otherID discover.NodeID) (*Conn, error) { + if conn := net.getConn(oneID, otherID); conn != nil { return conn, nil } - one := self.getNode(oneID) + one := net.getNode(oneID) if one == nil { return nil, fmt.Errorf("node %v does not exist", oneID) } - other := self.getNode(otherID) + other := net.getNode(otherID) if other == nil { return nil, fmt.Errorf("node %v does not exist", otherID) } @@ -444,18 +444,18 @@ func (self *Network) getOrCreateConn(oneID, otherID discover.NodeID) (*Conn, err other: other, } label := ConnLabel(oneID, otherID) - self.connMap[label] = len(self.Conns) - self.Conns = append(self.Conns, conn) + net.connMap[label] = len(net.Conns) + net.Conns = append(net.Conns, conn) return conn, nil } -func (self *Network) getConn(oneID, otherID discover.NodeID) *Conn { +func (net *Network) getConn(oneID, otherID discover.NodeID) *Conn { label := ConnLabel(oneID, otherID) - i, found := self.connMap[label] + i, found := net.connMap[label] if !found { return nil } - return self.Conns[i] + return net.Conns[i] } // InitConn(one, other) retrieves the connectiton model for the connection between @@ -466,13 +466,13 @@ func (self *Network) getConn(oneID, otherID discover.NodeID) *Conn { // it also checks whether there has been recent attempt to connect the peers // this is cheating as the simulation is used as an oracle and know about // remote peers attempt to connect to a node which will then not initiate the connection -func (self *Network) InitConn(oneID, otherID discover.NodeID) (*Conn, error) { - self.lock.Lock() - defer self.lock.Unlock() +func (net *Network) InitConn(oneID, otherID discover.NodeID) (*Conn, error) { + net.lock.Lock() + defer net.lock.Unlock() if oneID == otherID { return nil, fmt.Errorf("refusing to connect to self %v", oneID) } - conn, err := self.getOrCreateConn(oneID, otherID) + conn, err := net.getOrCreateConn(oneID, otherID) if err != nil { return nil, err } @@ -491,28 +491,28 @@ func (self *Network) InitConn(oneID, otherID discover.NodeID) (*Conn, error) { } // Shutdown stops all nodes in the network and closes the quit channel -func (self *Network) Shutdown() { - for _, node := range self.Nodes { +func (net *Network) Shutdown() { + for _, node := range net.Nodes { log.Debug(fmt.Sprintf("stopping node %s", node.ID().TerminalString())) if err := node.Stop(); err != nil { log.Warn(fmt.Sprintf("error stopping node %s", node.ID().TerminalString()), "err", err) } } - close(self.quitc) + close(net.quitc) } //Reset resets all network properties: //emtpies the nodes and the connection list -func (self *Network) Reset() { - self.lock.Lock() - defer self.lock.Unlock() +func (net *Network) Reset() { + net.lock.Lock() + defer net.lock.Unlock() //re-initialize the maps - self.connMap = make(map[string]int) - self.nodeMap = make(map[discover.NodeID]int) + net.connMap = make(map[string]int) + net.nodeMap = make(map[discover.NodeID]int) - self.Nodes = nil - self.Conns = nil + net.Nodes = nil + net.Conns = nil } // Node is a wrapper around adapters.Node which is used to track the status @@ -528,37 +528,37 @@ type Node struct { } // ID returns the ID of the node -func (self *Node) ID() discover.NodeID { - return self.Config.ID +func (n *Node) ID() discover.NodeID { + return n.Config.ID } // String returns a log-friendly string -func (self *Node) String() string { - return fmt.Sprintf("Node %v", self.ID().TerminalString()) +func (n *Node) String() string { + return fmt.Sprintf("Node %v", n.ID().TerminalString()) } // NodeInfo returns information about the node -func (self *Node) NodeInfo() *p2p.NodeInfo { +func (n *Node) NodeInfo() *p2p.NodeInfo { // avoid a panic if the node is not started yet - if self.Node == nil { + if n.Node == nil { return nil } - info := self.Node.NodeInfo() - info.Name = self.Config.Name + info := n.Node.NodeInfo() + info.Name = n.Config.Name return info } // MarshalJSON implements the json.Marshaler interface so that the encoded // JSON includes the NodeInfo -func (self *Node) MarshalJSON() ([]byte, error) { +func (n *Node) MarshalJSON() ([]byte, error) { return json.Marshal(struct { Info *p2p.NodeInfo `json:"info,omitempty"` Config *adapters.NodeConfig `json:"config,omitempty"` Up bool `json:"up"` }{ - Info: self.NodeInfo(), - Config: self.Config, - Up: self.Up, + Info: n.NodeInfo(), + Config: n.Config, + Up: n.Up, }) } @@ -580,19 +580,19 @@ type Conn struct { } // nodesUp returns whether both nodes are currently up -func (self *Conn) nodesUp() error { - if !self.one.Up { - return fmt.Errorf("one %v is not up", self.One) +func (c *Conn) nodesUp() error { + if !c.one.Up { + return fmt.Errorf("one %v is not up", c.One) } - if !self.other.Up { - return fmt.Errorf("other %v is not up", self.Other) + if !c.other.Up { + return fmt.Errorf("other %v is not up", c.Other) } return nil } // String returns a log-friendly string -func (self *Conn) String() string { - return fmt.Sprintf("Conn %v->%v", self.One.TerminalString(), self.Other.TerminalString()) +func (c *Conn) String() string { + return fmt.Sprintf("Conn %v->%v", c.One.TerminalString(), c.Other.TerminalString()) } // Msg represents a p2p message sent between two nodes in the network @@ -605,8 +605,8 @@ type Msg struct { } // String returns a log-friendly string -func (self *Msg) String() string { - return fmt.Sprintf("Msg(%d) %v->%v", self.Code, self.One.TerminalString(), self.Other.TerminalString()) +func (m *Msg) String() string { + return fmt.Sprintf("Msg(%d) %v->%v", m.Code, m.One.TerminalString(), m.Other.TerminalString()) } // ConnLabel generates a deterministic string which represents a connection @@ -640,14 +640,14 @@ type NodeSnapshot struct { } // Snapshot creates a network snapshot -func (self *Network) Snapshot() (*Snapshot, error) { - self.lock.Lock() - defer self.lock.Unlock() +func (net *Network) Snapshot() (*Snapshot, error) { + net.lock.Lock() + defer net.lock.Unlock() snap := &Snapshot{ - Nodes: make([]NodeSnapshot, len(self.Nodes)), - Conns: make([]Conn, len(self.Conns)), + Nodes: make([]NodeSnapshot, len(net.Nodes)), + Conns: make([]Conn, len(net.Conns)), } - for i, node := range self.Nodes { + for i, node := range net.Nodes { snap.Nodes[i] = NodeSnapshot{Node: *node} if !node.Up { continue @@ -658,33 +658,33 @@ func (self *Network) Snapshot() (*Snapshot, error) { } snap.Nodes[i].Snapshots = snapshots } - for i, conn := range self.Conns { + for i, conn := range net.Conns { snap.Conns[i] = *conn } return snap, nil } // Load loads a network snapshot -func (self *Network) Load(snap *Snapshot) error { +func (net *Network) Load(snap *Snapshot) error { for _, n := range snap.Nodes { - if _, err := self.NewNodeWithConfig(n.Node.Config); err != nil { + if _, err := net.NewNodeWithConfig(n.Node.Config); err != nil { return err } if !n.Node.Up { continue } - if err := self.startWithSnapshots(n.Node.Config.ID, n.Snapshots); err != nil { + if err := net.startWithSnapshots(n.Node.Config.ID, n.Snapshots); err != nil { return err } } for _, conn := range snap.Conns { - if !self.GetNode(conn.One).Up || !self.GetNode(conn.Other).Up { + if !net.GetNode(conn.One).Up || !net.GetNode(conn.Other).Up { //in this case, at least one of the nodes of a connection is not up, //so it would result in the snapshot `Load` to fail continue } - if err := self.Connect(conn.One, conn.Other); err != nil { + if err := net.Connect(conn.One, conn.Other); err != nil { return err } } @@ -692,7 +692,7 @@ func (self *Network) Load(snap *Snapshot) error { } // Subscribe reads control events from a channel and executes them -func (self *Network) Subscribe(events chan *Event) { +func (net *Network) Subscribe(events chan *Event) { for { select { case event, ok := <-events: @@ -700,23 +700,23 @@ func (self *Network) Subscribe(events chan *Event) { return } if event.Control { - self.executeControlEvent(event) + net.executeControlEvent(event) } - case <-self.quitc: + case <-net.quitc: return } } } -func (self *Network) executeControlEvent(event *Event) { +func (net *Network) executeControlEvent(event *Event) { log.Trace("execute control event", "type", event.Type, "event", event) switch event.Type { case EventTypeNode: - if err := self.executeNodeEvent(event); err != nil { + if err := net.executeNodeEvent(event); err != nil { log.Error("error executing node event", "event", event, "err", err) } case EventTypeConn: - if err := self.executeConnEvent(event); err != nil { + if err := net.executeConnEvent(event); err != nil { log.Error("error executing conn event", "event", event, "err", err) } case EventTypeMsg: @@ -724,21 +724,21 @@ func (self *Network) executeControlEvent(event *Event) { } } -func (self *Network) executeNodeEvent(e *Event) error { +func (net *Network) executeNodeEvent(e *Event) error { if !e.Node.Up { - return self.Stop(e.Node.ID()) + return net.Stop(e.Node.ID()) } - if _, err := self.NewNodeWithConfig(e.Node.Config); err != nil { + if _, err := net.NewNodeWithConfig(e.Node.Config); err != nil { return err } - return self.Start(e.Node.ID()) + return net.Start(e.Node.ID()) } -func (self *Network) executeConnEvent(e *Event) error { +func (net *Network) executeConnEvent(e *Event) error { if e.Conn.Up { - return self.Connect(e.Conn.One, e.Conn.Other) + return net.Connect(e.Conn.One, e.Conn.Other) } else { - return self.Disconnect(e.Conn.One, e.Conn.Other) + return net.Disconnect(e.Conn.One, e.Conn.Other) } } diff --git a/p2p/testing/peerpool.go b/p2p/testing/peerpool.go index 6f8a5d7a5228..d34b4c07809c 100644 --- a/p2p/testing/peerpool.go +++ b/p2p/testing/peerpool.go @@ -39,29 +39,29 @@ func NewTestPeerPool() *TestPeerPool { return &TestPeerPool{peers: make(map[discover.NodeID]TestPeer)} } -func (self *TestPeerPool) Add(p TestPeer) { - self.lock.Lock() - defer self.lock.Unlock() +func (pp *TestPeerPool) Add(p TestPeer) { + pp.lock.Lock() + defer pp.lock.Unlock() log.Trace(fmt.Sprintf("pp add peer %v", p.ID())) - self.peers[p.ID()] = p + pp.peers[p.ID()] = p } -func (self *TestPeerPool) Remove(p TestPeer) { - self.lock.Lock() - defer self.lock.Unlock() - delete(self.peers, p.ID()) +func (pp *TestPeerPool) Remove(p TestPeer) { + pp.lock.Lock() + defer pp.lock.Unlock() + delete(pp.peers, p.ID()) } -func (self *TestPeerPool) Has(id discover.NodeID) bool { - self.lock.Lock() - defer self.lock.Unlock() - _, ok := self.peers[id] +func (pp *TestPeerPool) Has(id discover.NodeID) bool { + pp.lock.Lock() + defer pp.lock.Unlock() + _, ok := pp.peers[id] return ok } -func (self *TestPeerPool) Get(id discover.NodeID) TestPeer { - self.lock.Lock() - defer self.lock.Unlock() - return self.peers[id] +func (pp *TestPeerPool) Get(id discover.NodeID) TestPeer { + pp.lock.Lock() + defer pp.lock.Unlock() + return pp.peers[id] } diff --git a/p2p/testing/protocolsession.go b/p2p/testing/protocolsession.go index 2c0133b111b1..39ccc70bd0fd 100644 --- a/p2p/testing/protocolsession.go +++ b/p2p/testing/protocolsession.go @@ -78,10 +78,10 @@ type Disconnect struct { } // trigger sends messages from peers -func (self *ProtocolSession) trigger(trig Trigger) error { - simNode, ok := self.adapter.GetNode(trig.Peer) +func (ps *ProtocolSession) trigger(trig Trigger) error { + simNode, ok := ps.adapter.GetNode(trig.Peer) if !ok { - return fmt.Errorf("trigger: peer %v does not exist (1- %v)", trig.Peer, len(self.IDs)) + return fmt.Errorf("trigger: peer %v does not exist (1- %v)", trig.Peer, len(ps.IDs)) } mockNode, ok := simNode.Services()[0].(*mockNode) if !ok { @@ -107,7 +107,7 @@ func (self *ProtocolSession) trigger(trig Trigger) error { } // expect checks an expectation of a message sent out by the pivot node -func (self *ProtocolSession) expect(exps []Expect) error { +func (ps *ProtocolSession) expect(exps []Expect) error { // construct a map of expectations for each node peerExpects := make(map[discover.NodeID][]Expect) for _, exp := range exps { @@ -120,9 +120,9 @@ func (self *ProtocolSession) expect(exps []Expect) error { // construct a map of mockNodes for each node mockNodes := make(map[discover.NodeID]*mockNode) for nodeID := range peerExpects { - simNode, ok := self.adapter.GetNode(nodeID) + simNode, ok := ps.adapter.GetNode(nodeID) if !ok { - return fmt.Errorf("trigger: peer %v does not exist (1- %v)", nodeID, len(self.IDs)) + return fmt.Errorf("trigger: peer %v does not exist (1- %v)", nodeID, len(ps.IDs)) } mockNode, ok := simNode.Services()[0].(*mockNode) if !ok { @@ -202,9 +202,9 @@ func (self *ProtocolSession) expect(exps []Expect) error { } // TestExchanges tests a series of exchanges against the session -func (self *ProtocolSession) TestExchanges(exchanges ...Exchange) error { +func (ps *ProtocolSession) TestExchanges(exchanges ...Exchange) error { for i, e := range exchanges { - if err := self.testExchange(e); err != nil { + if err := ps.testExchange(e); err != nil { return fmt.Errorf("exchange #%d %q: %v", i, e.Label, err) } log.Trace(fmt.Sprintf("exchange #%d %q: run successfully", i, e.Label)) @@ -214,14 +214,14 @@ func (self *ProtocolSession) TestExchanges(exchanges ...Exchange) error { // testExchange tests a single Exchange. // Default timeout value is 2 seconds. -func (self *ProtocolSession) testExchange(e Exchange) error { +func (ps *ProtocolSession) testExchange(e Exchange) error { errc := make(chan error) done := make(chan struct{}) defer close(done) go func() { for _, trig := range e.Triggers { - err := self.trigger(trig) + err := ps.trigger(trig) if err != nil { errc <- err return @@ -229,7 +229,7 @@ func (self *ProtocolSession) testExchange(e Exchange) error { } select { - case errc <- self.expect(e.Expects): + case errc <- ps.expect(e.Expects): case <-done: } }() @@ -250,7 +250,7 @@ func (self *ProtocolSession) testExchange(e Exchange) error { // TestDisconnected tests the disconnections given as arguments // the disconnect structs describe what disconnect error is expected on which peer -func (self *ProtocolSession) TestDisconnected(disconnects ...*Disconnect) error { +func (ps *ProtocolSession) TestDisconnected(disconnects ...*Disconnect) error { expects := make(map[discover.NodeID]error) for _, disconnect := range disconnects { expects[disconnect.Peer] = disconnect.Error @@ -259,7 +259,7 @@ func (self *ProtocolSession) TestDisconnected(disconnects ...*Disconnect) error timeout := time.After(time.Second) for len(expects) > 0 { select { - case event := <-self.events: + case event := <-ps.events: if event.Type != p2p.PeerEventTypeDrop { continue } diff --git a/p2p/testing/protocoltester.go b/p2p/testing/protocoltester.go index 21a57fd09cf9..622d63bfe324 100644 --- a/p2p/testing/protocoltester.go +++ b/p2p/testing/protocoltester.go @@ -100,24 +100,24 @@ func NewProtocolTester(t *testing.T, id discover.NodeID, n int, run func(*p2p.Pe } // Stop stops the p2p server -func (self *ProtocolTester) Stop() error { - self.Server.Stop() +func (pt *ProtocolTester) Stop() error { + pt.Server.Stop() return nil } // Connect brings up the remote peer node and connects it using the // p2p/simulations network connection with the in memory network adapter -func (self *ProtocolTester) Connect(selfID discover.NodeID, peers ...*adapters.NodeConfig) { +func (pt *ProtocolTester) Connect(selfID discover.NodeID, peers ...*adapters.NodeConfig) { for _, peer := range peers { log.Trace(fmt.Sprintf("start node %v", peer.ID)) - if _, err := self.network.NewNodeWithConfig(peer); err != nil { + if _, err := pt.network.NewNodeWithConfig(peer); err != nil { panic(fmt.Sprintf("error starting peer %v: %v", peer.ID, err)) } - if err := self.network.Start(peer.ID); err != nil { + if err := pt.network.Start(peer.ID); err != nil { panic(fmt.Sprintf("error starting peer %v: %v", peer.ID, err)) } log.Trace(fmt.Sprintf("connect to %v", peer.ID)) - if err := self.network.Connect(selfID, peer.ID); err != nil { + if err := pt.network.Connect(selfID, peer.ID); err != nil { panic(fmt.Sprintf("error connecting to peer %v: %v", peer.ID, err)) } } @@ -130,25 +130,25 @@ type testNode struct { run func(*p2p.Peer, p2p.MsgReadWriter) error } -func (t *testNode) Protocols() []p2p.Protocol { +func (tn *testNode) Protocols() []p2p.Protocol { return []p2p.Protocol{{ Length: 100, - Run: t.run, + Run: tn.run, }} } -func (t *testNode) APIs() []rpc.API { +func (tn *testNode) APIs() []rpc.API { return nil } -func (t *testNode) Start(server *p2p.Server) error { +func (tn *testNode) Start(server *p2p.Server) error { return nil } -func (t *testNode) SaveData() { +func (tn *testNode) SaveData() { } -func (t *testNode) Stop() error { +func (tn *testNode) Stop() error { return nil } @@ -178,34 +178,34 @@ func newMockNode() *mockNode { // Run is a protocol run function which just loops waiting for tests to // instruct it to either trigger or expect a message from the peer -func (m *mockNode) Run(peer *p2p.Peer, rw p2p.MsgReadWriter) error { +func (mn *mockNode) Run(peer *p2p.Peer, rw p2p.MsgReadWriter) error { for { select { - case trig := <-m.trigger: - m.err <- p2p.Send(rw, trig.Code, trig.Msg) - case exps := <-m.expect: - m.err <- expectMsgs(rw, exps) - case <-m.stop: + case trig := <-mn.trigger: + mn.err <- p2p.Send(rw, trig.Code, trig.Msg) + case exps := <-mn.expect: + mn.err <- expectMsgs(rw, exps) + case <-mn.stop: return nil } } } -func (m *mockNode) Trigger(trig *Trigger) error { - m.trigger <- trig - return <-m.err +func (mn *mockNode) Trigger(trig *Trigger) error { + mn.trigger <- trig + return <-mn.err } -func (m *mockNode) Expect(exp ...Expect) error { - m.expect <- exp - return <-m.err +func (mn *mockNode) Expect(exp ...Expect) error { + mn.expect <- exp + return <-mn.err } -func (m *mockNode) SaveData() { +func (mn *mockNode) SaveData() { } -func (m *mockNode) Stop() error { - m.stopOnce.Do(func() { close(m.stop) }) +func (mn *mockNode) Stop() error { + mn.stopOnce.Do(func() { close(mn.stop) }) return nil } From 4709ca3f139b769decd292207fc64776e78692f3 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 28 Oct 2024 15:13:21 +0800 Subject: [PATCH 041/479] all: fix staticcheck warning SA1006 --- accounts/abi/bind/backends/simulated.go | 4 ++-- consensus/XDPoS/engines/engine_v2/forensics_test.go | 4 ++-- consensus/tests/engine_v2_tests/helper.go | 4 ++-- metrics/json_test.go | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index ba62d58a700a..e43cae43775c 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -86,10 +86,10 @@ func SimulateWalletAddressAndSignFn() (common.Address, func(account accounts.Acc pass := "" // not used but required by API a1, err := ks.NewAccount(pass) if err != nil { - return common.Address{}, nil, fmt.Errorf(err.Error()) + return common.Address{}, nil, err } if err := ks.Unlock(a1, ""); err != nil { - return a1.Address, nil, fmt.Errorf(err.Error()) + return a1.Address, nil, err } return a1.Address, ks.SignHash, nil } diff --git a/consensus/XDPoS/engines/engine_v2/forensics_test.go b/consensus/XDPoS/engines/engine_v2/forensics_test.go index 3597bfb9367d..3b6dc1b252d2 100644 --- a/consensus/XDPoS/engines/engine_v2/forensics_test.go +++ b/consensus/XDPoS/engines/engine_v2/forensics_test.go @@ -58,10 +58,10 @@ func getSignerAndSignFn(pk *ecdsa.PrivateKey) (common.Address, func(account acco pass := "" // not used but required by API a1, err := ks.ImportECDSA(pk, pass) if err != nil { - return common.Address{}, nil, fmt.Errorf(err.Error()) + return common.Address{}, nil, err } if err := ks.Unlock(a1, ""); err != nil { - return a1.Address, nil, fmt.Errorf(err.Error()) + return a1.Address, nil, err } return a1.Address, ks.SignHash, nil } diff --git a/consensus/tests/engine_v2_tests/helper.go b/consensus/tests/engine_v2_tests/helper.go index a25019d467d4..14eceb6005f6 100644 --- a/consensus/tests/engine_v2_tests/helper.go +++ b/consensus/tests/engine_v2_tests/helper.go @@ -88,10 +88,10 @@ func getSignerAndSignFn(pk *ecdsa.PrivateKey) (common.Address, func(account acco pass := "" // not used but required by API a1, err := ks.ImportECDSA(pk, pass) if err != nil { - return common.Address{}, nil, fmt.Errorf(err.Error()) + return common.Address{}, nil, err } if err := ks.Unlock(a1, ""); err != nil { - return a1.Address, nil, fmt.Errorf(err.Error()) + return a1.Address, nil, err } return a1.Address, ks.SignHash, nil } diff --git a/metrics/json_test.go b/metrics/json_test.go index f91fe8cfa54f..811bc29f11ec 100644 --- a/metrics/json_test.go +++ b/metrics/json_test.go @@ -13,7 +13,7 @@ func TestRegistryMarshallJSON(t *testing.T) { r.Register("counter", NewCounter()) enc.Encode(r) if s := b.String(); s != "{\"counter\":{\"count\":0}}\n" { - t.Fatalf(s) + t.Fatal(s) } } From 71b9005f3434488c9a3330f74ce91bdaed29d6fe Mon Sep 17 00:00:00 2001 From: wgr523 Date: Mon, 28 Oct 2024 15:14:30 +0800 Subject: [PATCH 042/479] feat: add api xdpos_getBlockInfoByEpochNum (#674) * feat: add api xdpos_getBlockInfoByEpochNum * feat: add cache round2epochBlockInfo * fix: round2epochBlockInfo contains round now * feat: binary search in GetBlockByEpochNumber * fix: change some code back, refine style --- consensus/XDPoS/api.go | 16 +++ consensus/XDPoS/engines/engine_v2/engine.go | 7 + .../XDPoS/engines/engine_v2/epochSwitch.go | 13 +- consensus/XDPoS/engines/engine_v2/utils.go | 122 ++++++++++++++++++ consensus/XDPoS/utils/constants.go | 2 + consensus/XDPoS/utils/types.go | 7 + consensus/tests/engine_v2_tests/api_test.go | 59 +++++++++ internal/web3ext/web3ext.go | 5 + 8 files changed, 230 insertions(+), 1 deletion(-) diff --git a/consensus/XDPoS/api.go b/consensus/XDPoS/api.go index fd06703128b3..287e37b7f413 100644 --- a/consensus/XDPoS/api.go +++ b/consensus/XDPoS/api.go @@ -344,3 +344,19 @@ func (api *API) GetEpochNumbersBetween(begin, end *rpc.BlockNumber) ([]uint64, e } return epochSwitchNumbers, nil } + +/* +An API exclusively for V2 consensus, designed to assist in getting rewards of the epoch number. +Given the epoch number, search the epoch switch block. +*/ +func (api *API) GetBlockInfoByEpochNum(epochNumber uint64) (*utils.EpochNumInfo, error) { + result, err := api.XDPoS.EngineV2.GetBlockByEpochNumber(api.chain, epochNumber) + if err != nil { + return nil, err + } + return &utils.EpochNumInfo{ + EpochBlockHash: result.Hash, + EpochRound: result.Round, + EpochBlockNumber: result.Number, + }, nil +} diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index f2e4a5e4d1a5..dafc87fbd8ea 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -37,6 +37,10 @@ type XDPoS_v2 struct { epochSwitches *lru.ARCCache // infos of epoch: master nodes, epoch switch block info, parent of that info verifiedHeaders *lru.ARCCache + // only contains epoch switch block info + // input: round, output: infos of epoch switch block and next epoch switch block info + round2epochBlockInfo *lru.ARCCache + signer common.Address // Ethereum address of the signing key signFn clique.SignerFn // Signer function to authorize hashes with lock sync.RWMutex // Protects the signer fields @@ -77,6 +81,7 @@ func New(chainConfig *params.ChainConfig, db ethdb.Database, minePeriodCh chan i signatures, _ := lru.NewARC(utils.InmemorySnapshots) epochSwitches, _ := lru.NewARC(int(utils.InmemoryEpochs)) verifiedHeaders, _ := lru.NewARC(utils.InmemorySnapshots) + round2epochBlockInfo, _ := lru.NewARC(utils.InmemoryRound2Epochs) timeoutPool := utils.NewPool() votePool := utils.NewPool() @@ -96,6 +101,8 @@ func New(chainConfig *params.ChainConfig, db ethdb.Database, minePeriodCh chan i BroadcastCh: make(chan interface{}), minePeriodCh: minePeriodCh, + round2epochBlockInfo: round2epochBlockInfo, + timeoutPool: timeoutPool, votePool: votePool, diff --git a/consensus/XDPoS/engines/engine_v2/epochSwitch.go b/consensus/XDPoS/engines/engine_v2/epochSwitch.go index abb35bde0926..e9d76daa620a 100644 --- a/consensus/XDPoS/engines/engine_v2/epochSwitch.go +++ b/consensus/XDPoS/engines/engine_v2/epochSwitch.go @@ -56,7 +56,6 @@ func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types log.Error("[getEpochSwitchInfo] get extra field", "err", err, "number", h.Number.Uint64()) return nil, err } - snap, err := x.getSnapshot(chain, h.Number.Uint64(), false) if err != nil { log.Error("[getEpochSwitchInfo] Adaptor v2 getSnapshot has error", "err", err) @@ -155,6 +154,14 @@ func (x *XDPoS_v2) IsEpochSwitch(header *types.Header) (bool, uint64, error) { return true, epochNum, nil } log.Debug("[IsEpochSwitch]", "is", parentRound < epochStartRound, "parentRound", parentRound, "round", round, "number", header.Number.Uint64(), "epochNum", epochNum, "hash", header.Hash()) + // if isEpochSwitch, add to cache + if parentRound < epochStartRound { + x.round2epochBlockInfo.Add(round, &types.BlockInfo{ + Hash: header.Hash(), + Number: header.Number, + Round: round, + }) + } return parentRound < epochStartRound, epochNum, nil } @@ -175,6 +182,10 @@ func (x *XDPoS_v2) GetEpochSwitchInfoBetween(chain consensus.ChainReader, begin, return nil, err } iteratorHeader = nil + // V2 switch epoch switch info has nil parent + if epochSwitchInfo.EpochSwitchParentBlockInfo == nil { + break + } iteratorHash = epochSwitchInfo.EpochSwitchParentBlockInfo.Hash iteratorNum = epochSwitchInfo.EpochSwitchBlockInfo.Number if iteratorNum.Cmp(begin.Number) >= 0 { diff --git a/consensus/XDPoS/engines/engine_v2/utils.go b/consensus/XDPoS/engines/engine_v2/utils.go index 92da8d0f7db1..2e3486156de4 100644 --- a/consensus/XDPoS/engines/engine_v2/utils.go +++ b/consensus/XDPoS/engines/engine_v2/utils.go @@ -3,6 +3,7 @@ package engine_v2 import ( "errors" "fmt" + "math/big" "github.com/XinFinOrg/XDPoSChain/accounts" "github.com/XinFinOrg/XDPoSChain/common" @@ -218,3 +219,124 @@ func (x *XDPoS_v2) CalculateMissingRounds(chain consensus.ChainReader, header *t return missedRoundsMetadata, nil } + +func (x *XDPoS_v2) getBlockByEpochNumberInCache(chain consensus.ChainReader, estRound types.Round) *types.BlockInfo { + epochSwitchInCache := make([]*types.BlockInfo, 0) + for r := estRound; r < estRound+types.Round(x.config.Epoch); r++ { + info, ok := x.round2epochBlockInfo.Get(r) + if ok { + blockInfo := info.(*types.BlockInfo) + epochSwitchInCache = append(epochSwitchInCache, blockInfo) + } + } + if len(epochSwitchInCache) == 1 { + return epochSwitchInCache[0] + } else if len(epochSwitchInCache) == 0 { + return nil + } + // when multiple cache hits, need to find the one in main chain + for _, blockInfo := range epochSwitchInCache { + header := chain.GetHeaderByNumber(blockInfo.Number.Uint64()) + if header == nil { + continue + } + if header.Hash() == blockInfo.Hash { + return blockInfo + } + } + return nil +} + +func (x *XDPoS_v2) binarySearchBlockByEpochNumber(chain consensus.ChainReader, targetEpochNum uint64, start, end uint64) (*types.BlockInfo, error) { + // `end` must be larger than the target and `start` could be the target + for start < end { + header := chain.GetHeaderByNumber((start + end) / 2) + if header == nil { + return nil, errors.New("header nil in binary search") + } + isEpochSwitch, epochNum, err := x.IsEpochSwitch(header) + if err != nil { + return nil, err + } + if epochNum == targetEpochNum { + _, round, _, err := x.getExtraFields(header) + if err != nil { + return nil, err + } + if isEpochSwitch { + return &types.BlockInfo{ + Hash: header.Hash(), + Round: round, + Number: header.Number, + }, nil + } else { + end = header.Number.Uint64() + // trick to shorten the search + estStart := end - uint64(round)%x.config.Epoch + if start < estStart { + start = estStart + } + } + } else if epochNum > targetEpochNum { + end = header.Number.Uint64() + } else if epochNum < targetEpochNum { + // if start keeps the same, means no result and the search is over + nextStart := header.Number.Uint64() + if nextStart == start { + break + } + start = nextStart + } + } + return nil, errors.New("no epoch switch header in binary search (all rounds in this epoch are missed, which is very rare)") +} + +func (x *XDPoS_v2) GetBlockByEpochNumber(chain consensus.ChainReader, targetEpochNum uint64) (*types.BlockInfo, error) { + currentHeader := chain.CurrentHeader() + epochSwitchInfo, err := x.getEpochSwitchInfo(chain, currentHeader, currentHeader.Hash()) + if err != nil { + return nil, err + } + epochNum := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(epochSwitchInfo.EpochSwitchBlockInfo.Round)/x.config.Epoch + // if current epoch is this epoch, we early return the result + if targetEpochNum == epochNum { + return epochSwitchInfo.EpochSwitchBlockInfo, nil + } + if targetEpochNum > epochNum { + return nil, errors.New("input epoch number > current epoch number") + } + if targetEpochNum < x.config.V2.SwitchBlock.Uint64()/x.config.Epoch { + return nil, errors.New("input epoch number < v2 begin epoch number") + } + // the block's round should be in [estRound,estRound+Epoch-1] + estRound := types.Round((targetEpochNum - x.config.V2.SwitchBlock.Uint64()/x.config.Epoch) * x.config.Epoch) + // check the round2epochBlockInfo cache + blockInfo := x.getBlockByEpochNumberInCache(chain, estRound) + if blockInfo != nil { + return blockInfo, nil + } + // if cache miss, we do search + epoch := big.NewInt(int64(x.config.Epoch)) + estblockNumDiff := new(big.Int).Mul(epoch, big.NewInt(int64(epochNum-targetEpochNum))) + estBlockNum := new(big.Int).Sub(epochSwitchInfo.EpochSwitchBlockInfo.Number, estblockNumDiff) + if estBlockNum.Cmp(x.config.V2.SwitchBlock) == -1 { + estBlockNum.Set(x.config.V2.SwitchBlock) + } + // if the targrt is close, we search brute-forcily + closeEpochNum := uint64(2) + if closeEpochNum >= epochNum-targetEpochNum { + estBlockHeader := chain.GetHeaderByNumber(estBlockNum.Uint64()) + epochSwitchInfos, err := x.GetEpochSwitchInfoBetween(chain, estBlockHeader, currentHeader) + if err != nil { + return nil, err + } + for _, info := range epochSwitchInfos { + epochNum := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(info.EpochSwitchBlockInfo.Round)/x.config.Epoch + if epochNum == targetEpochNum { + return info.EpochSwitchBlockInfo, nil + } + } + } + // else, we use binary search + return x.binarySearchBlockByEpochNumber(chain, targetEpochNum, estBlockNum.Uint64(), epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64()) +} diff --git a/consensus/XDPoS/utils/constants.go b/consensus/XDPoS/utils/constants.go index c8df06cf4664..6cef0034a062 100644 --- a/consensus/XDPoS/utils/constants.go +++ b/consensus/XDPoS/utils/constants.go @@ -17,6 +17,8 @@ var ( UncleHash = types.CalcUncleHash(nil) // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW. InmemoryEpochs = 5 * EpochLength // Number of mapping from block to epoch switch infos to keep in memory + + InmemoryRound2Epochs = 65536 // Number of mapping of epoch switch blocks for quickly locating epoch switch block. One epoch ~ 0.5hours, so 65536 epochs ~ 3.7 years. And it uses ~ 10MB memory. ) const ( diff --git a/consensus/XDPoS/utils/types.go b/consensus/XDPoS/utils/types.go index 897e984b4811..96817c294e3d 100644 --- a/consensus/XDPoS/utils/types.go +++ b/consensus/XDPoS/utils/types.go @@ -71,3 +71,10 @@ type PublicApiMissedRoundsMetadata struct { EpochBlockNumber *big.Int MissedRounds []MissedRoundInfo } + +// Given an epoch number, this struct records the epoch switch block (first block in epoch) infos such as block number +type EpochNumInfo struct { + EpochBlockHash common.Hash `json:"hash"` + EpochRound types.Round `json:"round"` + EpochBlockNumber *big.Int `json:"number"` +} diff --git a/consensus/tests/engine_v2_tests/api_test.go b/consensus/tests/engine_v2_tests/api_test.go index aa482c5e7320..0b1258d2a4aa 100644 --- a/consensus/tests/engine_v2_tests/api_test.go +++ b/consensus/tests/engine_v2_tests/api_test.go @@ -168,3 +168,62 @@ func TestGetEpochNumbersBetween(t *testing.T) { assert.Nil(t, numbers) assert.EqualError(t, err, "illegal begin block number") } +func TestGetBlockByEpochNumber(t *testing.T) { + blockchain, _, currentBlock, signer, signFn := PrepareXDCTestBlockChainWithPenaltyForV2Engine(t, 1802, params.TestXDPoSMockChainConfig) + + blockCoinBase := "0x111000000000000000000000000000000123" + largeRound := int64(1802) + newBlock := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, int(currentBlock.NumberU64())+1, largeRound, blockCoinBase, signer, signFn, nil, nil, currentBlock.Header().Root.Hex()) + err := blockchain.InsertBlock(newBlock) + assert.Nil(t, err) + largeRound2 := int64(3603) + newBlock2 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, newBlock, int(newBlock.NumberU64())+1, largeRound2, blockCoinBase, signer, signFn, nil, nil, newBlock.Header().Root.Hex()) + err = blockchain.InsertBlock(newBlock2) + assert.Nil(t, err) + + // block num, round, epoch is as follows + // 900,0,1 (v2 switch block, not v2 epoch switch block) + // 901,1,1 (1st epoch switch block) + // 902,2,1 + // ... + // 1800,900,2 (2nd epoch switch block) + // 1801,901,2 + // 1802,902,2 + // 1803,1802,3 (epoch switch) + // epoch 4 has no block + // 1804,3603,5 (epoch switch) + engine := blockchain.Engine().(*XDPoS.XDPoS) + + // init the snapshot, otherwise getEpochSwitchInfo would return error + checkpointHeader := blockchain.GetHeaderByNumber(blockchain.Config().XDPoS.V2.SwitchBlock.Uint64() + 1) + err = engine.Initial(blockchain, checkpointHeader) + assert.Nil(t, err) + + info, err := engine.APIs(blockchain)[0].Service.(*XDPoS.API).GetBlockInfoByEpochNum(0) + assert.NotNil(t, err) + assert.Nil(t, info) + + info, err = engine.APIs(blockchain)[0].Service.(*XDPoS.API).GetBlockInfoByEpochNum(1) + assert.Equal(t, info.EpochRound, types.Round(1)) + assert.Nil(t, err) + + info, err = engine.APIs(blockchain)[0].Service.(*XDPoS.API).GetBlockInfoByEpochNum(2) + assert.Equal(t, info.EpochRound, types.Round(900)) + assert.Nil(t, err) + + info, err = engine.APIs(blockchain)[0].Service.(*XDPoS.API).GetBlockInfoByEpochNum(3) + assert.Equal(t, info.EpochRound, types.Round(largeRound)) + assert.Nil(t, err) + + info, err = engine.APIs(blockchain)[0].Service.(*XDPoS.API).GetBlockInfoByEpochNum(4) + assert.NotNil(t, err) + assert.Nil(t, info) + + info, err = engine.APIs(blockchain)[0].Service.(*XDPoS.API).GetBlockInfoByEpochNum(5) + assert.Equal(t, info.EpochRound, types.Round(largeRound2)) + assert.Nil(t, err) + + info, err = engine.APIs(blockchain)[0].Service.(*XDPoS.API).GetBlockInfoByEpochNum(6) + assert.NotNil(t, err) + assert.Nil(t, info) +} diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 688f8df33af1..1921ad609961 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -168,6 +168,11 @@ web3._extend({ params: 2, inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter, web3._extend.formatters.inputBlockNumberFormatter] }), + new web3._extend.Method({ + name: 'getBlockInfoByEpochNum', + call: 'XDPoS_getBlockInfoByEpochNum', + params: 1, + }), ], properties: [ new web3._extend.Property({ From 9c1492189d02f643b9e3576ea410a9f3feee163d Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 28 Oct 2024 11:41:57 +0800 Subject: [PATCH 043/479] XDCxDAO: fix staticcheck warning S1034: use result of type assertion to simplify cases --- XDCxDAO/mongodb.go | 68 ++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 39 deletions(-) diff --git a/XDCxDAO/mongodb.go b/XDCxDAO/mongodb.go index 3d2efef31715..566632d67d83 100644 --- a/XDCxDAO/mongodb.go +++ b/XDCxDAO/mongodb.go @@ -101,7 +101,7 @@ func (db *MongoDatabase) HasObject(hash common.Hash, val interface{}) (bool, err err error ) query := bson.M{"hash": hash.Hex()} - switch val.(type) { + switch item := val.(type) { case *tradingstate.OrderItem: // Find key in ordersCollection collection count, err = sc.DB(db.dbName).C(ordersCollection).Find(query).Limit(1).Count() @@ -126,7 +126,6 @@ func (db *MongoDatabase) HasObject(hash common.Hash, val interface{}) (bool, err } case *lendingstate.LendingItem: // Find key in lendingItemsCollection collection - item := val.(*lendingstate.LendingItem) switch item.Type { case lendingstate.Repay: count, err = sc.DB(db.dbName).C(lendingRepayCollection).Find(query).Limit(1).Count() @@ -176,7 +175,7 @@ func (db *MongoDatabase) GetObject(hash common.Hash, val interface{}) (interface query := bson.M{"hash": hash.Hex()} - switch val.(type) { + switch item := val.(type) { case *tradingstate.OrderItem: var oi *tradingstate.OrderItem err := sc.DB(db.dbName).C(ordersCollection).Find(query).One(&oi) @@ -196,7 +195,6 @@ func (db *MongoDatabase) GetObject(hash common.Hash, val interface{}) (interface case *lendingstate.LendingItem: var li *lendingstate.LendingItem var err error - item := val.(*lendingstate.LendingItem) switch item.Type { case lendingstate.Repay: err = sc.DB(db.dbName).C(lendingRepayCollection).Find(query).One(&li) @@ -230,62 +228,58 @@ func (db *MongoDatabase) PutObject(hash common.Hash, val interface{}) error { cacheKey := db.getCacheKey(hash.Bytes()) db.cacheItems.Add(cacheKey, val) - switch val.(type) { + switch item := val.(type) { case *tradingstate.Trade: // PutObject trade into tradesCollection collection - db.tradeBulk.Insert(val.(*tradingstate.Trade)) + db.tradeBulk.Insert(item) case *tradingstate.OrderItem: // PutObject order into ordersCollection collection - o := val.(*tradingstate.OrderItem) - if o.Status == tradingstate.OrderStatusOpen { - db.orderBulk.Insert(o) + if item.Status == tradingstate.OrderStatusOpen { + db.orderBulk.Insert(item) } else { - query := bson.M{"hash": o.Hash.Hex()} - db.orderBulk.Upsert(query, o) + query := bson.M{"hash": item.Hash.Hex()} + db.orderBulk.Upsert(query, item) } return nil case *tradingstate.EpochPriceItem: - item := val.(*tradingstate.EpochPriceItem) query := bson.M{"hash": item.Hash.Hex()} db.epochPriceBulk.Upsert(query, item) return nil case *lendingstate.LendingTrade: - lt := val.(*lendingstate.LendingTrade) // PutObject LendingTrade into tradesCollection collection if existed, err := db.HasObject(hash, val); err == nil && existed { - query := bson.M{"hash": lt.Hash.Hex()} - db.lendingTradeBulk.Upsert(query, lt) + query := bson.M{"hash": item.Hash.Hex()} + db.lendingTradeBulk.Upsert(query, item) } else { - db.lendingTradeBulk.Insert(lt) + db.lendingTradeBulk.Insert(item) } case *lendingstate.LendingItem: // PutObject order into ordersCollection collection - li := val.(*lendingstate.LendingItem) - switch li.Type { + switch item.Type { case lendingstate.Repay: - if li.Status != lendingstate.LendingStatusReject { - li.Status = lendingstate.Repay + if item.Status != lendingstate.LendingStatusReject { + item.Status = lendingstate.Repay } - db.repayBulk.Insert(li) + db.repayBulk.Insert(item) return nil case lendingstate.TopUp: - if li.Status != lendingstate.LendingStatusReject { - li.Status = lendingstate.TopUp + if item.Status != lendingstate.LendingStatusReject { + item.Status = lendingstate.TopUp } - db.topUpBulk.Insert(li) + db.topUpBulk.Insert(item) return nil case lendingstate.Recall: - if li.Status != lendingstate.LendingStatusReject { - li.Status = lendingstate.Recall + if item.Status != lendingstate.LendingStatusReject { + item.Status = lendingstate.Recall } - db.recallBulk.Insert(li) + db.recallBulk.Insert(item) return nil default: - if li.Status == lendingstate.LendingStatusOpen { - db.lendingItemBulk.Insert(li) + if item.Status == lendingstate.LendingStatusOpen { + db.lendingItemBulk.Insert(item) } else { - query := bson.M{"hash": li.Hash.Hex()} - db.lendingItemBulk.Upsert(query, li) + query := bson.M{"hash": item.Hash.Hex()} + db.lendingItemBulk.Upsert(query, item) } return nil } @@ -313,7 +307,7 @@ func (db *MongoDatabase) DeleteObject(hash common.Hash, val interface{}) error { if found { var err error - switch val.(type) { + switch item := val.(type) { case *tradingstate.OrderItem: err = sc.DB(db.dbName).C(ordersCollection).Remove(query) if err != nil && err != mgo.ErrNotFound { @@ -325,7 +319,6 @@ func (db *MongoDatabase) DeleteObject(hash common.Hash, val interface{}) error { return fmt.Errorf("failed to delete XDCx trade. Err: %v", err) } case *lendingstate.LendingItem: - item := val.(*lendingstate.LendingItem) switch item.Type { case lendingstate.Repay: err = sc.DB(db.dbName).C(lendingRepayCollection).Remove(query) @@ -424,7 +417,7 @@ func (db *MongoDatabase) DeleteItemByTxHash(txhash common.Hash, val interface{}) defer sc.Close() query := bson.M{"txHash": txhash.Hex()} - switch val.(type) { + switch item := val.(type) { case *tradingstate.OrderItem: if err := sc.DB(db.dbName).C(ordersCollection).Remove(query); err != nil && err != mgo.ErrNotFound { log.Error("DeleteItemByTxHash: failed to delete order", "txhash", txhash, "err", err) @@ -434,7 +427,6 @@ func (db *MongoDatabase) DeleteItemByTxHash(txhash common.Hash, val interface{}) log.Error("DeleteItemByTxHash: failed to delete trade", "txhash", txhash, "err", err) } case *lendingstate.LendingItem: - item := val.(*lendingstate.LendingItem) switch item.Type { case lendingstate.Repay: if err := sc.DB(db.dbName).C(lendingRepayCollection).Remove(query); err != nil && err != mgo.ErrNotFound { @@ -473,7 +465,7 @@ func (db *MongoDatabase) GetListItemByTxHash(txhash common.Hash, val interface{} defer sc.Close() query := bson.M{"txHash": txhash.Hex()} - switch val.(type) { + switch item := val.(type) { case *tradingstate.OrderItem: result := []*tradingstate.OrderItem{} if err := sc.DB(db.dbName).C(ordersCollection).Find(query).All(&result); err != nil && err != mgo.ErrNotFound { @@ -487,7 +479,6 @@ func (db *MongoDatabase) GetListItemByTxHash(txhash common.Hash, val interface{} } return result case *lendingstate.LendingItem: - item := val.(*lendingstate.LendingItem) result := []*lendingstate.LendingItem{} switch item.Type { case lendingstate.Repay: @@ -529,7 +520,7 @@ func (db *MongoDatabase) GetListItemByHashes(hashes []string, val interface{}) i query := bson.M{"hash": bson.M{"$in": hashes}} - switch val.(type) { + switch item := val.(type) { case *tradingstate.OrderItem: result := []*tradingstate.OrderItem{} if err := sc.DB(db.dbName).C(ordersCollection).Find(query).All(&result); err != nil && err != mgo.ErrNotFound { @@ -543,7 +534,6 @@ func (db *MongoDatabase) GetListItemByHashes(hashes []string, val interface{}) i } return result case *lendingstate.LendingItem: - item := val.(*lendingstate.LendingItem) result := []*lendingstate.LendingItem{} switch item.Type { case lendingstate.Repay: From a5bc0baba97f665ba7acdac30f25b83967e77126 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 28 Oct 2024 17:45:19 +0800 Subject: [PATCH 044/479] all: fix staticcheck warning ST1019: import package twice --- consensus/tests/engine_v1_tests/helper.go | 15 +++++++------- consensus/tests/engine_v2_tests/helper.go | 23 +++++++++++----------- contracts/utils.go | 18 ++++++++--------- eth/api_backend.go | 24 ++++++++++------------- 4 files changed, 36 insertions(+), 44 deletions(-) diff --git a/consensus/tests/engine_v1_tests/helper.go b/consensus/tests/engine_v1_tests/helper.go index 5c27489f408d..7904ea32ca42 100644 --- a/consensus/tests/engine_v1_tests/helper.go +++ b/consensus/tests/engine_v1_tests/helper.go @@ -20,7 +20,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract" "github.com/XinFinOrg/XDPoSChain/core" - . "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/crypto" @@ -192,12 +191,12 @@ func voteTX(gasLimit uint64, nonce uint64, addr string) (*types.Transaction, err return signedTX, nil } -func UpdateSigner(bc *BlockChain) error { +func UpdateSigner(bc *core.BlockChain) error { err := bc.UpdateM1() return err } -func GetSnapshotSigner(bc *BlockChain, header *types.Header) (signersList, error) { +func GetSnapshotSigner(bc *core.BlockChain, header *types.Header) (signersList, error) { engine := bc.Engine().(*XDPoS.XDPoS) snap, err := engine.GetSnapshot(bc, header) if err != nil { @@ -238,7 +237,7 @@ func GetCandidateFromCurrentSmartContract(backend bind.ContractBackend, t *testi } // V1 consensus engine -func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig) (*BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error)) { +func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig) (*core.BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error)) { // Preparation var err error // Authorise @@ -291,7 +290,7 @@ func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params return blockchain, backend, currentBlock, signer, signFn } -func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, startingBlock *types.Block, blockNumber int, roundNumber int64, blockCoinBase string, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), penalties []byte) *types.Block { +func CreateBlock(blockchain *core.BlockChain, chainConfig *params.ChainConfig, startingBlock *types.Block, blockNumber int, roundNumber int64, blockCoinBase string, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), penalties []byte) *types.Block { currentBlock := startingBlock merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" @@ -333,7 +332,7 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti return block } -func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*types.Transaction, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), config *params.ChainConfig) (*types.Block, error) { +func createBlockFromHeader(bc *core.BlockChain, customHeader *types.Header, txs []*types.Transaction, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), config *params.ChainConfig) (*types.Block, error) { if customHeader.Extra == nil { extraSubstring := "d7830100018358444388676f312e31342e31856c696e75780000000000000000b185dc0d0e917d18e5dbf0746be6597d3331dd27ea0554e6db433feb2e81730b20b2807d33a1527bf43cd3bc057aa7f641609c2551ebe2fd575f4db704fbf38101" // Grabbed from existing mainnet block, it does not have any meaning except for the length validation customHeader.Extra, _ = hex.DecodeString(extraSubstring) @@ -377,13 +376,13 @@ func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*ty if err != nil { return nil, fmt.Errorf("%v when get state", err) } - gp := new(GasPool).AddGas(header.GasLimit) + gp := new(core.GasPool).AddGas(header.GasLimit) var gasUsed = new(uint64) var receipts types.Receipts for i, tx := range txs { statedb.Prepare(tx.Hash(), i) - receipt, _, err, _ := ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{}) + receipt, _, err, _ := core.ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{}) if err != nil { return nil, fmt.Errorf("%v when applying transaction", err) } diff --git a/consensus/tests/engine_v2_tests/helper.go b/consensus/tests/engine_v2_tests/helper.go index 14eceb6005f6..15e3ee77cd69 100644 --- a/consensus/tests/engine_v2_tests/helper.go +++ b/consensus/tests/engine_v2_tests/helper.go @@ -23,7 +23,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/contracts" contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract" "github.com/XinFinOrg/XDPoSChain/core" - . "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/crypto" @@ -314,12 +313,12 @@ func signingTxWithSignerFn(header *types.Header, nonce uint64, signer common.Add return signedTx, nil } -func UpdateSigner(bc *BlockChain) error { +func UpdateSigner(bc *core.BlockChain) error { err := bc.UpdateM1() return err } -func GetSnapshotSigner(bc *BlockChain, header *types.Header) (signersList, error) { +func GetSnapshotSigner(bc *core.BlockChain, header *types.Header) (signersList, error) { engine := bc.Engine().(*XDPoS.XDPoS) snap, err := engine.GetSnapshot(bc, header) if err != nil { @@ -366,7 +365,7 @@ type ForkedBlockOptions struct { } // V2 concensus engine -func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig, forkedBlockOptions *ForkedBlockOptions) (*BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error), *types.Block) { +func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig, forkedBlockOptions *ForkedBlockOptions) (*core.BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error), *types.Block) { // Preparation var err error signer, signFn, err := backends.SimulateWalletAddressAndSignFn() @@ -456,7 +455,7 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon } // V2 concensus engine, compared to PrepareXDCTestBlockChainForV2Engine: (1) no forking (2) add penalty -func PrepareXDCTestBlockChainWithPenaltyForV2Engine(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig) (*BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error)) { +func PrepareXDCTestBlockChainWithPenaltyForV2Engine(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig) (*core.BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error)) { // Preparation var err error signer, signFn, err := backends.SimulateWalletAddressAndSignFn() @@ -507,7 +506,7 @@ func PrepareXDCTestBlockChainWithPenaltyForV2Engine(t *testing.T, numOfBlocks in } // V2 concensus engine, compared to PrepareXDCTestBlockChainForV2Engine: (1) no forking (2) 128 masternode candidates -func PrepareXDCTestBlockChainWith128Candidates(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig) (*BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error)) { +func PrepareXDCTestBlockChainWith128Candidates(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig) (*core.BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error)) { // Preparation var err error signer, signFn, err := backends.SimulateWalletAddressAndSignFn() @@ -569,7 +568,7 @@ func PrepareXDCTestBlockChainWith128Candidates(t *testing.T, numOfBlocks int, ch return blockchain, backend, currentBlock, signer, signFn } -func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, startingBlock *types.Block, blockNumber int, roundNumber int64, blockCoinBase string, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), penalties []byte, signersKey []*ecdsa.PrivateKey, merkleRoot string) *types.Block { +func CreateBlock(blockchain *core.BlockChain, chainConfig *params.ChainConfig, startingBlock *types.Block, blockNumber int, roundNumber int64, blockCoinBase string, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), penalties []byte, signersKey []*ecdsa.PrivateKey, merkleRoot string) *types.Block { currentBlock := startingBlock if len(merkleRoot) == 0 { merkleRoot = "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" @@ -646,7 +645,7 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti return block } -func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*types.Transaction, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), config *params.ChainConfig) (*types.Block, error) { +func createBlockFromHeader(bc *core.BlockChain, customHeader *types.Header, txs []*types.Transaction, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), config *params.ChainConfig) (*types.Block, error) { if customHeader.Extra == nil { extraSubstring := "d7830100018358444388676f312e31342e31856c696e75780000000000000000b185dc0d0e917d18e5dbf0746be6597d3331dd27ea0554e6db433feb2e81730b20b2807d33a1527bf43cd3bc057aa7f641609c2551ebe2fd575f4db704fbf38101" // Grabbed from existing mainnet block, it does not have any meaning except for the length validation customHeader.Extra, _ = hex.DecodeString(extraSubstring) @@ -694,13 +693,13 @@ func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*ty if err != nil { return nil, fmt.Errorf("%v when get state", err) } - gp := new(GasPool).AddGas(header.GasLimit) + gp := new(core.GasPool).AddGas(header.GasLimit) var gasUsed = new(uint64) var receipts types.Receipts for i, tx := range txs { statedb.Prepare(tx.Hash(), i) - receipt, _, err, _ := ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{}) + receipt, _, err, _ := core.ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{}) if err != nil { return nil, fmt.Errorf("%v when applying transaction", err) } @@ -729,7 +728,7 @@ func decodeMasternodesFromHeaderExtra(checkpointHeader *types.Header) []common.A return masternodes } -func findSignerAndSignFn(bc *BlockChain, header *types.Header, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), config *params.ChainConfig) (common.Address, func(account accounts.Account, hash []byte) ([]byte, error)) { +func findSignerAndSignFn(bc *core.BlockChain, header *types.Header, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), config *params.ChainConfig) (common.Address, func(account accounts.Account, hash []byte) ([]byte, error)) { addressToSign := signer addressedSignFn := signFn @@ -765,7 +764,7 @@ func findSignerAndSignFn(bc *BlockChain, header *types.Header, signer common.Add return addressToSign, addressedSignFn } -func sealHeader(bc *BlockChain, header *types.Header, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error)) { +func sealHeader(bc *core.BlockChain, header *types.Header, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error)) { // Sign all the things and seal it signedBlockHeader := bc.Engine().(*XDPoS.XDPoS).SigHash(header) diff --git a/contracts/utils.go b/contracts/utils.go index 9af44e052af7..79841b0b2119 100644 --- a/contracts/utils.go +++ b/contracts/utils.go @@ -35,14 +35,12 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/hexutil" "github.com/XinFinOrg/XDPoSChain/consensus" - "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" "github.com/XinFinOrg/XDPoSChain/contracts/blocksigner/contract" randomizeContract "github.com/XinFinOrg/XDPoSChain/contracts/randomize/contract" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/state" - stateDatabase "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/log" @@ -210,8 +208,8 @@ func BuildTxOpeningRandomize(nonce uint64, randomizeAddr common.Address, randomi } // Get signers signed for blockNumber from blockSigner contract. -func GetSignersFromContract(state *stateDatabase.StateDB, block *types.Block) ([]common.Address, error) { - return stateDatabase.GetSigners(state, block), nil +func GetSignersFromContract(statedb *state.StateDB, block *types.Block) ([]common.Address, error) { + return state.GetSigners(statedb, block), nil } // Get signers signed for blockNumber from blockSigner contract. @@ -414,8 +412,8 @@ func CalculateRewardForSigner(chainReward *big.Int, signers map[common.Address]* } // Get candidate owner by address. -func GetCandidatesOwnerBySigner(state *state.StateDB, signerAddr common.Address) common.Address { - owner := stateDatabase.GetCandidateOwner(state, signerAddr) +func GetCandidatesOwnerBySigner(statedb *state.StateDB, signerAddr common.Address) common.Address { + owner := state.GetCandidateOwner(statedb, signerAddr) return owner } @@ -427,14 +425,14 @@ func CalculateRewardForHolders(foundationWalletAddr common.Address, state *state return nil, rewards } -func GetRewardBalancesRate(foundationWalletAddr common.Address, state *state.StateDB, masterAddr common.Address, totalReward *big.Int, blockNumber uint64) (map[common.Address]*big.Int, error) { - owner := GetCandidatesOwnerBySigner(state, masterAddr) +func GetRewardBalancesRate(foundationWalletAddr common.Address, statedb *state.StateDB, masterAddr common.Address, totalReward *big.Int, blockNumber uint64) (map[common.Address]*big.Int, error) { + owner := GetCandidatesOwnerBySigner(statedb, masterAddr) balances := make(map[common.Address]*big.Int) rewardMaster := new(big.Int).Mul(totalReward, new(big.Int).SetInt64(common.RewardMasterPercent)) rewardMaster = new(big.Int).Div(rewardMaster, new(big.Int).SetInt64(100)) balances[owner] = rewardMaster // Get voters for masternode. - voters := stateDatabase.GetVoters(state, masterAddr) + voters := state.GetVoters(statedb, masterAddr) if len(voters) > 0 { totalVoterReward := new(big.Int).Mul(totalReward, new(big.Int).SetUint64(common.RewardVoterPercent)) @@ -446,7 +444,7 @@ func GetRewardBalancesRate(foundationWalletAddr common.Address, state *state.Sta if _, ok := voterCaps[voteAddr]; ok && common.TIP2019Block.Uint64() <= blockNumber { continue } - voterCap := stateDatabase.GetVoterCap(state, masterAddr, voteAddr) + voterCap := state.GetVoterCap(statedb, masterAddr, voteAddr) totalCap.Add(totalCap, voterCap) voterCaps[voteAddr] = voterCap } diff --git a/eth/api_backend.go b/eth/api_backend.go index 98220fd1118f..d5294649684c 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -24,24 +24,20 @@ import ( "os" "path/filepath" + "github.com/XinFinOrg/XDPoSChain/XDCx" "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate" "github.com/XinFinOrg/XDPoSChain/XDCxlending" - "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" - - "github.com/XinFinOrg/XDPoSChain/XDCx" - - "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" - "github.com/XinFinOrg/XDPoSChain/accounts" + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/consensus" + "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" "github.com/XinFinOrg/XDPoSChain/contracts" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/bloombits" "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/state" - stateDatabase "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/eth/downloader" @@ -516,20 +512,20 @@ func (b *EthApiBackend) GetVotersRewards(masternodeAddr common.Address) map[comm func (b *EthApiBackend) GetVotersCap(checkpoint *big.Int, masterAddr common.Address, voters []common.Address) map[common.Address]*big.Int { chain := b.eth.blockchain checkpointBlock := chain.GetBlockByNumber(checkpoint.Uint64()) - state, err := chain.StateAt(checkpointBlock.Root()) + statedb, err := chain.StateAt(checkpointBlock.Root()) if err != nil { log.Error("fail to get state in GetVotersCap", "checkpoint", checkpoint, "err", err) return nil } - if state != nil { + if statedb != nil { log.Error("fail to get state in GetVotersCap", "checkpoint", checkpoint) return nil } voterCaps := make(map[common.Address]*big.Int) for _, voteAddr := range voters { - voterCap := stateDatabase.GetVoterCap(state, masterAddr, voteAddr) + voterCap := state.GetVoterCap(statedb, masterAddr, voteAddr) voterCaps[voteAddr] = voterCap } return voterCaps @@ -552,21 +548,21 @@ func (b *EthApiBackend) GetEpochDuration() *big.Int { // GetMasternodesCap return a cap of all masternode at a checkpoint func (b *EthApiBackend) GetMasternodesCap(checkpoint uint64) map[common.Address]*big.Int { checkpointBlock := b.eth.blockchain.GetBlockByNumber(checkpoint) - state, err := b.eth.blockchain.StateAt(checkpointBlock.Root()) + statedb, err := b.eth.blockchain.StateAt(checkpointBlock.Root()) if err != nil { log.Error("fail to get state in GetMasternodesCap", "checkpoint", checkpoint, "err", err) return nil } - if state == nil { + if statedb == nil { log.Error("fail to get state in GetMasternodesCap", "checkpoint", checkpoint) return nil } - candicates := stateDatabase.GetCandidates(state) + candicates := state.GetCandidates(statedb) masternodesCap := map[common.Address]*big.Int{} for _, candicate := range candicates { - masternodesCap[candicate] = stateDatabase.GetCandidateCap(state, candicate) + masternodesCap[candicate] = state.GetCandidateCap(statedb, candicate) } return masternodesCap From 181b23a767eba1f15d7f7365bbf5cdcb04a423c0 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 30 Oct 2024 21:02:41 +0800 Subject: [PATCH 045/479] core, XDCxlending/lendingstate: fix staticcheck warning S1002: omit comparison to bool constant --- XDCxlending/lendingstate/lendingitem.go | 2 +- core/lending_pool.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/XDCxlending/lendingstate/lendingitem.go b/XDCxlending/lendingstate/lendingitem.go index 200ffb920b20..3ce001f83216 100644 --- a/XDCxlending/lendingstate/lendingitem.go +++ b/XDCxlending/lendingstate/lendingitem.go @@ -207,7 +207,7 @@ func (l *LendingItem) VerifyLendingItem(state *state.StateDB) error { if err := l.VerifyLendingStatus(); err != nil { return err } - if valid, _ := IsValidPair(state, l.Relayer, l.LendingToken, l.Term); valid == false { + if valid, _ := IsValidPair(state, l.Relayer, l.LendingToken, l.Term); !valid { return fmt.Errorf("invalid pair . LendToken %s . Term: %v", l.LendingToken.Hex(), l.Term) } if l.Status == LendingStatusNew { diff --git a/core/lending_pool.go b/core/lending_pool.go index e8e4724dfc8d..c5197a3754a5 100644 --- a/core/lending_pool.go +++ b/core/lending_pool.go @@ -598,7 +598,7 @@ func (pool *LendingPool) validateLending(tx *types.LendingTransaction) error { if !lendingstate.IsValidRelayer(cloneStateDb, tx.RelayerAddress()) { return fmt.Errorf("invalid lending relayer. ExchangeAddress: %s", tx.RelayerAddress().Hex()) } - if valid, _ := lendingstate.IsValidPair(cloneStateDb, tx.RelayerAddress(), tx.LendingToken(), tx.Term()); valid == false { + if valid, _ := lendingstate.IsValidPair(cloneStateDb, tx.RelayerAddress(), tx.LendingToken(), tx.Term()); !valid { return fmt.Errorf("invalid pair. Relayer: %s. LendingToken: %s. Term: %d", tx.RelayerAddress().Hex(), tx.LendingToken().Hex(), tx.Term()) } if tx.IsCreatedLending() { From 5b0b5b52c7670908ea67556286b1cb17f4c54343 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 30 Oct 2024 10:25:52 +0800 Subject: [PATCH 046/479] common: fix staticcheck warning S1001: replace loop with copy --- common/types.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/common/types.go b/common/types.go index c7abab289b9a..2f621a4a5f40 100644 --- a/common/types.go +++ b/common/types.go @@ -143,9 +143,7 @@ func (h *Hash) SetString(s string) { h.SetBytes([]byte(s)) } // Sets h to other func (h *Hash) Set(other Hash) { - for i, v := range other { - h[i] = v - } + copy(h[:], other[:]) } // Generate implements testing/quick.Generator. @@ -262,9 +260,7 @@ func (a *Address) SetString(s string) { a.SetBytes([]byte(s)) } // Sets a to other func (a *Address) Set(other Address) { - for i, v := range other { - a[i] = v - } + copy(a[:], other[:]) } // MarshalText returns the hex representation of a. From 1329edfe5f52983b03dfebffd0c49399822dd0e4 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 30 Oct 2024 11:09:33 +0800 Subject: [PATCH 047/479] XDPoSChain, ethclient: fix staticcheck warning ST1012: rename NotFound to ErrNotFound --- ethclient/ethclient.go | 16 ++++++++-------- interfaces.go | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 812e7dae1418..1e76ec6d5d43 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -75,7 +75,7 @@ func (ec *Client) BlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumb var r []*types.Receipt err := ec.c.CallContext(ctx, &r, "eth_getBlockReceipts", blockNrOrHash) if err == nil && r == nil { - return nil, ethereum.NotFound + return nil, ethereum.ErrNotFound } return r, err } @@ -92,7 +92,7 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface if err != nil { return nil, err } else if len(raw) == 0 { - return nil, ethereum.NotFound + return nil, ethereum.ErrNotFound } // Decode header and transactions. var head *types.Header @@ -154,7 +154,7 @@ func (ec *Client) HeaderByHash(ctx context.Context, hash common.Hash) (*types.He var head *types.Header err := ec.c.CallContext(ctx, &head, "eth_getBlockByHash", hash, false) if err == nil && head == nil { - err = ethereum.NotFound + err = ethereum.ErrNotFound } return head, err } @@ -165,7 +165,7 @@ func (ec *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.H var head *types.Header err := ec.c.CallContext(ctx, &head, "eth_getBlockByNumber", toBlockNumArg(number), false) if err == nil && head == nil { - err = ethereum.NotFound + err = ethereum.ErrNotFound } return head, err } @@ -195,7 +195,7 @@ func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (tx * if err != nil { return nil, false, err } else if json == nil { - return nil, false, ethereum.NotFound + return nil, false, ethereum.ErrNotFound } else if _, r, _ := json.tx.RawSignatureValues(); r == nil { return nil, false, errors.New("server returned transaction without signature") } @@ -241,7 +241,7 @@ func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash, err := ec.c.CallContext(ctx, &json, "eth_getTransactionByBlockHashAndIndex", blockHash, hexutil.Uint64(index)) if err == nil { if json == nil { - return nil, ethereum.NotFound + return nil, ethereum.ErrNotFound } else if _, r, _ := json.tx.RawSignatureValues(); r == nil { return nil, errors.New("server returned transaction without signature") } @@ -257,7 +257,7 @@ func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (* err := ec.c.CallContext(ctx, &r, "eth_getTransactionReceipt", txHash) if err == nil { if r == nil { - return nil, ethereum.NotFound + return nil, ethereum.ErrNotFound } } return r, err @@ -268,7 +268,7 @@ func (ec *Client) GetTransactionReceiptResult(ctx context.Context, txHash common result, err := ec.c.GetResultCallContext(ctx, &r, "eth_getTransactionReceipt", txHash) if err == nil { if r == nil { - return nil, nil, ethereum.NotFound + return nil, nil, ethereum.ErrNotFound } } return r, result, err diff --git a/interfaces.go b/interfaces.go index dfd96b217a1a..6815b37c66a7 100644 --- a/interfaces.go +++ b/interfaces.go @@ -26,8 +26,8 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/types" ) -// NotFound is returned by API methods if the requested item does not exist. -var NotFound = errors.New("not found") +// ErrNotFound is returned by API methods if the requested item does not exist. +var ErrNotFound = errors.New("not found") // TODO: move subscription to package event From 95f8abb76548b306487859ea7dffb3dad9dc523a Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 30 Oct 2024 13:43:19 +0800 Subject: [PATCH 048/479] crypto/bn256: fix staticcheck warning SA9009: ineffectual compiler directive --- crypto/bn256/cloudflare/gfp_decl.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crypto/bn256/cloudflare/gfp_decl.go b/crypto/bn256/cloudflare/gfp_decl.go index fdea5c11a5be..1954d14a4a5a 100644 --- a/crypto/bn256/cloudflare/gfp_decl.go +++ b/crypto/bn256/cloudflare/gfp_decl.go @@ -1,3 +1,4 @@ +//go:build (amd64 && !generic) || (arm64 && !generic) // +build amd64,!generic arm64,!generic package bn256 @@ -9,10 +10,10 @@ import ( "golang.org/x/sys/cpu" ) -//nolint:varcheck +//nolint:varcheck,unused,deadcode var hasBMI2 = cpu.X86.HasBMI2 -// go:noescape +//go:noescape func gfpNeg(c, a *gfP) //go:noescape From 29c9c5808a87cf3a11b220085b8d28f4777e7188 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 30 Oct 2024 14:09:03 +0800 Subject: [PATCH 049/479] compression/rle: fix staticcheck warning SA9004: constant has no explicit type --- compression/rle/read_write.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compression/rle/read_write.go b/compression/rle/read_write.go index d5aadf8c1d92..93922981f811 100644 --- a/compression/rle/read_write.go +++ b/compression/rle/read_write.go @@ -26,9 +26,9 @@ import ( const ( token byte = 0xfe - emptyShaToken = 0xfd - emptyListShaToken = 0xfe - tokenToken = 0xff + emptyShaToken byte = 0xfd + emptyListShaToken byte = 0xfe + tokenToken byte = 0xff ) var empty = crypto.Keccak256([]byte("")) From 212daafe177444f350e7049f13b7594bfa4132c7 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 30 Oct 2024 15:09:51 +0800 Subject: [PATCH 050/479] core: fix staticcheck warning SA6005: should use strings.EqualFold --- core/state_processor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/state_processor.go b/core/state_processor.go index 917b5802a579..f1995a79c71a 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -389,7 +389,7 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* currentBlockNumber := blockNumber.Int64() if addr, ok := blockMap[currentBlockNumber]; ok { - if strings.ToLower(addr) == strings.ToLower(addrFrom) { + if strings.EqualFold(addr, addrFrom) { // case insensitive bal := addrMap[addr] hBalance := new(big.Int) hBalance.SetString(bal+"000000000000000000", 10) From 381226c506fc80d29b53ea562957c7ec39a2dabc Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 30 Oct 2024 15:40:21 +0800 Subject: [PATCH 051/479] XDCxDAO: fix staticcheck warning SA5007: infinite recursive call --- XDCxDAO/mongodb.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/XDCxDAO/mongodb.go b/XDCxDAO/mongodb.go index 566632d67d83..5c0352db7a59 100644 --- a/XDCxDAO/mongodb.go +++ b/XDCxDAO/mongodb.go @@ -825,7 +825,10 @@ func (db *MongoDatabase) EnsureIndexes() error { } func (db *MongoDatabase) Close() error { - return db.Close() + if db.Session != nil { + db.Session.Close() + } + return nil } // HasAncient returns an error as we don't have a backing chain freezer. From 6ed8d399c20b3985eefb167d8bda809808b3e709 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 29 Oct 2024 09:14:23 +0800 Subject: [PATCH 052/479] fix nil dereference in field selection --- consensus/XDPoS/engines/engine_v2/forensics.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/XDPoS/engines/engine_v2/forensics.go b/consensus/XDPoS/engines/engine_v2/forensics.go index c2b6aa0541f9..adec35cfd134 100644 --- a/consensus/XDPoS/engines/engine_v2/forensics.go +++ b/consensus/XDPoS/engines/engine_v2/forensics.go @@ -210,7 +210,7 @@ func (f *Forensics) findAncestorQCs(chain consensus.ChainReader, currentQc types parentHash := quorumCertificate.ProposedBlockInfo.Hash parentHeader := chain.GetHeaderByHash(parentHash) if parentHeader == nil { - log.Error("[findAncestorQCs] Forensics findAncestorQCs unable to find its parent block header", "BlockNum", parentHeader.Number.Int64(), "ParentHash", parentHash.Hex()) + log.Error("[findAncestorQCs] Forensics findAncestorQCs unable to find its parent block header", "ParentHash", parentHash.Hex()) return nil, errors.New("unable to find parent block header in forensics") } var decodedExtraField types.ExtraFields_v2 From 2306ceafb23239ff860ae59c887dad4383b6196d Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 29 Oct 2024 09:14:23 +0800 Subject: [PATCH 053/479] fix impossible condition: nil != nil --- accounts/abi/bind/base.go | 7 ++----- core/order_pool_test.go | 4 ---- whisper/whisperv6/whisper.go | 3 --- 3 files changed, 2 insertions(+), 12 deletions(-) diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index 4d5390d15bc2..0477d8c59e9e 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -280,7 +280,7 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int if err != nil { return nil, nil, err } - sub, err := event.NewSubscription(func(quit <-chan struct{}) error { + sub := event.NewSubscription(func(quit <-chan struct{}) error { for _, log := range buff { select { case logs <- log: @@ -289,11 +289,8 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int } } return nil - }), nil + }) - if err != nil { - return nil, nil, err - } return logs, sub, nil } diff --git a/core/order_pool_test.go b/core/order_pool_test.go index 6029e928202e..c12c167d1c9c 100644 --- a/core/order_pool_test.go +++ b/core/order_pool_test.go @@ -60,10 +60,6 @@ func getNonce(t *testing.T, userAddress common.Address) (uint64, error) { return 0, err } var result interface{} - if err != nil { - - return 0, err - } err = rpcClient.Call(&result, "XDCx_getOrderCount", userAddress) if err != nil { return 0, err diff --git a/whisper/whisperv6/whisper.go b/whisper/whisperv6/whisper.go index f72e5ede37c3..28fed9f676ac 100644 --- a/whisper/whisperv6/whisper.go +++ b/whisper/whisperv6/whisper.go @@ -501,9 +501,6 @@ func (whisper *Whisper) AddSymKeyFromPassword(password string) (string, error) { // kdf should run no less than 0.1 seconds on an average computer, // because it's an once in a session experience derived := pbkdf2.Key([]byte(password), nil, 65356, aesKeyLength, sha256.New) - if err != nil { - return "", err - } whisper.keyMu.Lock() defer whisper.keyMu.Unlock() From c688f4b24cb65b2459394956847f78f2441bafd9 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 29 Oct 2024 09:14:23 +0800 Subject: [PATCH 054/479] fix panic with nil value --- les/backend.go | 2 +- les/odr_requests.go | 4 ++-- les/peer.go | 6 +++--- les/randselect.go | 4 ++-- les/retrieve.go | 2 +- light/postprocess.go | 5 ++--- p2p/discv5/ticket.go | 4 ++-- 7 files changed, 13 insertions(+), 14 deletions(-) diff --git a/les/backend.go b/les/backend.go index cc6fdf962df4..ac496a7ecf6d 100644 --- a/les/backend.go +++ b/les/backend.go @@ -147,7 +147,7 @@ func lesTopic(genesisHash common.Hash, protocolVersion uint) discv5.Topic { case lpv2: name = "LES2" default: - panic(nil) + panic("lesTopic") } return discv5.Topic(name + "@" + common.Bytes2Hex(genesisHash.Bytes()[0:8])) } diff --git a/les/odr_requests.go b/les/odr_requests.go index 1ad2d106ba05..38914156c2c1 100644 --- a/les/odr_requests.go +++ b/les/odr_requests.go @@ -196,7 +196,7 @@ func (r *TrieRequest) GetCost(peer *peer) uint64 { case lpv2: return peer.GetRequestCost(GetProofsV2Msg, 1) default: - panic(nil) + panic("TrieRequest GetCost") } } @@ -356,7 +356,7 @@ func (r *ChtRequest) GetCost(peer *peer) uint64 { case lpv2: return peer.GetRequestCost(GetHelperTrieProofsMsg, 1) default: - panic(nil) + panic("ChtRequest GetCost") } } diff --git a/les/peer.go b/les/peer.go index cbc7b9957020..277750c365c3 100644 --- a/les/peer.go +++ b/les/peer.go @@ -279,7 +279,7 @@ func (p *peer) RequestProofs(reqID, cost uint64, reqs []ProofReq) error { case lpv2: return sendRequest(p.rw, GetProofsV2Msg, reqID, cost, reqs) default: - panic(nil) + panic("peer RequestProofs") } } @@ -301,7 +301,7 @@ func (p *peer) RequestHelperTrieProofs(reqID, cost uint64, reqs []HelperTrieReq) case lpv2: return sendRequest(p.rw, GetHelperTrieProofsMsg, reqID, cost, reqs) default: - panic(nil) + panic("peer RequestHelperTrieProofs") } } @@ -320,7 +320,7 @@ func (p *peer) SendTxs(reqID, cost uint64, txs types.Transactions) error { case lpv2: return sendRequest(p.rw, SendTxV2Msg, reqID, cost, txs) default: - panic(nil) + panic("peer SendTxs") } } diff --git a/les/randselect.go b/les/randselect.go index 1a9d0695bdcf..6150c3a263c5 100644 --- a/les/randselect.go +++ b/les/randselect.go @@ -109,7 +109,7 @@ func (n *wrsNode) insert(item wrsItem, weight int64) int { for n.items[branch] != nil && (n.level == 0 || n.items[branch].(*wrsNode).itemCnt == n.items[branch].(*wrsNode).maxItems) { branch++ if branch == wrsBranches { - panic(nil) + panic("wrsNode insert: branch == wrsBranches") } } n.itemCnt++ @@ -169,5 +169,5 @@ func (n *wrsNode) choose(val int64) (wrsItem, int64) { val -= w } } - panic(nil) + panic("wrsNode choose") } diff --git a/les/retrieve.go b/les/retrieve.go index e7894987bebb..f73f4fe6c93e 100644 --- a/les/retrieve.go +++ b/les/retrieve.go @@ -306,7 +306,7 @@ func (r *sentReq) tryRequest() { s, ok := r.sentTo[p] r.lock.RUnlock() if !ok { - panic(nil) + panic("sentReq tryRequest: !ok") } defer func() { diff --git a/light/postprocess.go b/light/postprocess.go index 746c40548fa2..f407c85d145e 100644 --- a/light/postprocess.go +++ b/light/postprocess.go @@ -22,11 +22,10 @@ import ( "math/big" "time" - "github.com/XinFinOrg/XDPoSChain/core/rawdb" - "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/bitutil" "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/log" @@ -165,7 +164,7 @@ func (c *ChtIndexerBackend) Process(header *types.Header) { td := core.GetTd(c.diskdb, hash, num) if td == nil { - panic(nil) + panic("ChtIndexerBackend Process: td == nil") } var encNumber [8]byte binary.BigEndian.PutUint64(encNumber[:], num) diff --git a/p2p/discv5/ticket.go b/p2p/discv5/ticket.go index 2a6d40e244e1..4780e8b8661e 100644 --- a/p2p/discv5/ticket.go +++ b/p2p/discv5/ticket.go @@ -441,7 +441,7 @@ func (s *ticketStore) removeTicketRef(ref ticketRef) { } } if idx == -1 { - panic(nil) + panic("ticketStore removeTicketRef: idx == -1") } list = append(list[:idx], list[idx+1:]...) if len(list) != 0 { @@ -802,7 +802,7 @@ func (r *topicRadius) chooseLookupBucket(a, b int) int { rnd-- } } - panic(nil) // should never happen + panic("topicRadius chooseLookupBucket") // should never happen } func (r *topicRadius) needMoreLookups(a, b int, maxValue float64) bool { From 371c3b6874f81ca771a0917007126e3a4741ce28 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 29 Oct 2024 09:14:23 +0800 Subject: [PATCH 055/479] fix tautological condition: non-nil != nil --- cmd/gc/main.go | 6 +++--- core/blockchain.go | 4 ++-- eth/handler.go | 4 +--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/cmd/gc/main.go b/cmd/gc/main.go index d0532e3f0d1e..4d1ba3a14137 100644 --- a/cmd/gc/main.go +++ b/cmd/gc/main.go @@ -12,6 +12,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/cmd/utils" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/lru" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/state" @@ -20,7 +21,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/ethdb/leveldb" "github.com/XinFinOrg/XDPoSChain/rlp" "github.com/XinFinOrg/XDPoSChain/trie" - "github.com/XinFinOrg/XDPoSChain/common/lru" ) var ( @@ -170,7 +170,7 @@ func getAllChilds(n StateNode, db *leveldb.Database) ([17]*StateNode, error) { } if err == nil { childs[i] = &StateNode{node: childNode, path: append(n.path, byte(i))} - } else if err != nil { + } else { _, ok := err.(*trie.MissingNodeError) if !ok { return childs, err @@ -187,7 +187,7 @@ func getAllChilds(n StateNode, db *leveldb.Database) ([17]*StateNode, error) { } if err == nil { childs[0] = &StateNode{node: childNode, path: append(n.path, node.Key...)} - } else if err != nil { + } else { _, ok := err.(*trie.MissingNodeError) if !ok { return childs, err diff --git a/core/blockchain.go b/core/blockchain.go index 8d9dbf573d3f..48a7e69b5e3b 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1659,7 +1659,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, [] } } //check - if tradingState != nil && tradingService != nil { + if tradingState != nil { gotRoot := tradingState.IntermediateRoot() expectRoot, _ := tradingService.GetTradingStateRoot(block, author) parentRoot, _ := tradingService.GetTradingStateRoot(parent, parentAuthor) @@ -1938,7 +1938,7 @@ func (bc *BlockChain) getResultBlock(block *types.Block, verifiedM2 bool) (*Resu } } } - if tradingState != nil && tradingService != nil { + if tradingState != nil { gotRoot := tradingState.IntermediateRoot() expectRoot, _ := tradingService.GetTradingStateRoot(block, author) parentRoot, _ := tradingService.GetTradingStateRoot(parent, parentAuthor) diff --git a/eth/handler.go b/eth/handler.go index 913d7f07b395..5cdb4876a878 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -285,9 +285,7 @@ func (pm *ProtocolManager) removePeer(id string) { log.Debug("Peer removal failed", "peer", id, "err", err) } // Hard disconnect at the networking layer - if peer != nil { - peer.Peer.Disconnect(p2p.DiscUselessPeer) - } + peer.Peer.Disconnect(p2p.DiscUselessPeer) } func (pm *ProtocolManager) Start(maxPeers int) { From 5f66fb5de017e176ffaecb03f0239624f051d31e Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 29 Oct 2024 09:14:23 +0800 Subject: [PATCH 056/479] fix tautological condition: nil == nil --- consensus/XDPoS/engines/engine_v1/engine.go | 2 +- consensus/clique/clique.go | 2 +- trie/trie.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/consensus/XDPoS/engines/engine_v1/engine.go b/consensus/XDPoS/engines/engine_v1/engine.go index 3df0e29369c0..c913a80773fc 100644 --- a/consensus/XDPoS/engines/engine_v1/engine.go +++ b/consensus/XDPoS/engines/engine_v1/engine.go @@ -476,7 +476,7 @@ func (x *XDPoS_v1) snapshot(chain consensus.ChainReader, number uint64, hash com headers []*types.Header snap *SnapshotV1 ) - for snap == nil { + for { // If an in-memory SnapshotV1 was found, use that if s, ok := x.recents.Get(hash); ok { snap = s.(*SnapshotV1) diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 9bae8fd7439c..bb1e9bb4b436 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -390,7 +390,7 @@ func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash commo headers []*types.Header snap *Snapshot ) - for snap == nil { + for { // If an in-memory snapshot was found, use that if s, ok := c.recents.Get(hash); ok { snap = s.(*Snapshot) diff --git a/trie/trie.go b/trie/trie.go index 834c4d79de72..dea4ddbf1ce0 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -243,7 +243,7 @@ func (t *Trie) tryGetAllLeftKeyAndValue(origNode Node, prefix []byte, limit []by if err != nil { return nil, nil, n, false, err } - if err == nil && didResolve { + if didResolve { n = n.copy() n.Children[i] = newnode } From 60fedfed8383d328a4f860b6c36a55c58553e478 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 29 Oct 2024 09:14:23 +0800 Subject: [PATCH 057/479] fix staticcheck SA5011: possible nil pointer dereference --- cmd/puppeth/wizard_netstats.go | 3 ++- mobile/ethereum.go | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd/puppeth/wizard_netstats.go b/cmd/puppeth/wizard_netstats.go index b8bdd19fb0ca..37bdb0e81290 100644 --- a/cmd/puppeth/wizard_netstats.go +++ b/cmd/puppeth/wizard_netstats.go @@ -82,7 +82,6 @@ func (w *wizard) gatherStats(server string, pubkey []byte, client *sshClient) *s logger.Info("Starting remote server health-check") stat := &serverStat{ - address: client.address, services: make(map[string]map[string]string), } if client == nil { @@ -94,6 +93,8 @@ func (w *wizard) gatherStats(server string, pubkey []byte, client *sshClient) *s } client = conn } + stat.address = client.address + // Client connected one way or another, run health-checks logger.Debug("Checking for nginx availability") if infos, err := checkNginx(client, w.network); err != nil { diff --git a/mobile/ethereum.go b/mobile/ethereum.go index f7904b7c329e..ac21afdd6b56 100644 --- a/mobile/ethereum.go +++ b/mobile/ethereum.go @@ -64,11 +64,13 @@ func (msg *CallMsg) SetGas(gas int64) { msg.msg.Gas = uint64(gas) } func (msg *CallMsg) SetGasPrice(price *BigInt) { msg.msg.GasPrice = price.bigint } func (msg *CallMsg) SetValue(value *BigInt) { msg.msg.Value = value.bigint } func (msg *CallMsg) SetData(data []byte) { msg.msg.Data = common.CopyBytes(data) } + func (msg *CallMsg) SetTo(address *Address) { if address == nil { msg.msg.To = nil + } else { + msg.msg.To = &address.address } - msg.msg.To = &address.address } // SyncProgress gives progress indications when the node is synchronising with From c36642a0e1e3b4698baa8332627a859237142779 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 29 Oct 2024 09:17:52 +0800 Subject: [PATCH 058/479] all: fix staticcheck warning ST1008: error should be last return value --- XDCxlending/XDCxlending.go | 2 +- XDCxlending/order_processor.go | 34 ++++++++++----------- consensus/XDPoS/engines/engine_v1/engine.go | 4 +-- contracts/utils.go | 6 ++-- eth/api_backend.go | 2 +- eth/hooks/engine_v1_hooks.go | 8 ++--- eth/hooks/engine_v2_hooks.go | 2 +- miner/worker.go | 10 +++--- 8 files changed, 34 insertions(+), 34 deletions(-) diff --git a/XDCxlending/XDCxlending.go b/XDCxlending/XDCxlending.go index 30ad610da53e..3816ab758a40 100644 --- a/XDCxlending/XDCxlending.go +++ b/XDCxlending/XDCxlending.go @@ -926,7 +926,7 @@ func (l *Lending) ProcessLiquidationData(header *types.Header, chain consensus.C trade := lendingState.GetLendingTrade(lendingBook, tradingIdHash) log.Debug("TestRecall", "borrower", trade.Borrower.Hex(), "lendingToken", trade.LendingToken.Hex(), "collateral", trade.CollateralToken.Hex(), "price", price, "tradingIdHash", tradingIdHash.Hex()) if trade.AutoTopUp { - err, _, newTrade := l.ProcessRecallLendingTrade(lendingState, statedb, tradingState, lendingBook, tradingIdHash, newLiquidatePrice) + _, newTrade, err := l.ProcessRecallLendingTrade(lendingState, statedb, tradingState, lendingBook, tradingIdHash, newLiquidatePrice) if err != nil { log.Error("ProcessRecallLendingTrade", "lendingBook", lendingBook.Hex(), "tradingIdHash", tradingIdHash.Hex(), "newLiquidatePrice", newLiquidatePrice, "err", err) return updatedTrades, liquidatedTrades, autoRepayTrades, autoTopUpTrades, autoRecallTrades, err diff --git a/XDCxlending/order_processor.go b/XDCxlending/order_processor.go index 6a275590c5a8..0a41347acdb8 100644 --- a/XDCxlending/order_processor.go +++ b/XDCxlending/order_processor.go @@ -66,7 +66,7 @@ func (l *Lending) ApplyOrder(header *types.Header, coinbase common.Address, chai switch order.Type { case lendingstate.TopUp: - err, reject, newLendingTrade := l.ProcessTopUp(lendingStateDB, statedb, tradingStateDb, order) + reject, newLendingTrade, err := l.ProcessTopUp(lendingStateDB, statedb, tradingStateDb, order) if err != nil || reject { rejects = append(rejects, order) } @@ -784,22 +784,22 @@ func (l *Lending) ProcessCancelOrder(header *types.Header, lendingStateDB *lendi return nil, false } -func (l *Lending) ProcessTopUp(lendingStateDB *lendingstate.LendingStateDB, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, order *lendingstate.LendingItem) (error, bool, *lendingstate.LendingTrade) { +func (l *Lending) ProcessTopUp(lendingStateDB *lendingstate.LendingStateDB, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, order *lendingstate.LendingItem) (bool, *lendingstate.LendingTrade, error) { lendingTradeId := common.Uint64ToHash(order.LendingTradeId) lendingBook := lendingstate.GetLendingOrderBookHash(order.LendingToken, order.Term) lendingTrade := lendingStateDB.GetLendingTrade(lendingBook, lendingTradeId) if lendingTrade == lendingstate.EmptyLendingTrade { - return fmt.Errorf("process deposit for emptyLendingTrade is not allowed. lendingTradeId: %v", lendingTradeId.Hex()), true, nil + return true, nil, fmt.Errorf("process deposit for emptyLendingTrade is not allowed. lendingTradeId: %v", lendingTradeId.Hex()) } if order.UserAddress != lendingTrade.Borrower { - return fmt.Errorf("ProcessTopUp: invalid userAddress . UserAddress: %s . Borrower: %s", order.UserAddress.Hex(), lendingTrade.Borrower.Hex()), true, nil + return true, nil, fmt.Errorf("ProcessTopUp: invalid userAddress . UserAddress: %s . Borrower: %s", order.UserAddress.Hex(), lendingTrade.Borrower.Hex()) } if order.Relayer != lendingTrade.BorrowingRelayer { - return fmt.Errorf("ProcessTopUp: invalid relayerAddress . Got: %s . Expect: %s", order.Relayer.Hex(), lendingTrade.BorrowingRelayer.Hex()), true, nil + return true, nil, fmt.Errorf("ProcessTopUp: invalid relayerAddress . Got: %s . Expect: %s", order.Relayer.Hex(), lendingTrade.BorrowingRelayer.Hex()) } if order.Quantity.Sign() <= 0 || lendingTrade.TradeId != lendingTradeId.Big().Uint64() { log.Debug("ProcessTopUp: invalid quantity", "Quantity", order.Quantity, "lendingTradeId", lendingTradeId.Hex()) - return nil, true, nil + return true, nil, nil } return l.ProcessTopUpLendingTrade(lendingStateDB, statedb, tradingStateDb, lendingTradeId, lendingBook, order.Quantity) } @@ -1113,23 +1113,23 @@ func (l *Lending) AutoTopUp(statedb *state.StateDB, tradingState *tradingstate.T if tokenBalance.Cmp(requiredDepositAmount) < 0 { return nil, fmt.Errorf("not enough balance to AutoTopUp. requiredDepositAmount: %v . tokenBalance: %v . Token: %s", requiredDepositAmount, tokenBalance, lendingTrade.CollateralToken.Hex()) } - err, _, newTrade := l.ProcessTopUpLendingTrade(lendingState, statedb, tradingState, lendingTradeId, lendingBook, requiredDepositAmount) + _, newTrade, err := l.ProcessTopUpLendingTrade(lendingState, statedb, tradingState, lendingTradeId, lendingBook, requiredDepositAmount) return newTrade, err } -func (l *Lending) ProcessTopUpLendingTrade(lendingStateDB *lendingstate.LendingStateDB, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, lendingTradeId common.Hash, lendingBook common.Hash, quantity *big.Int) (error, bool, *lendingstate.LendingTrade) { +func (l *Lending) ProcessTopUpLendingTrade(lendingStateDB *lendingstate.LendingStateDB, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, lendingTradeId common.Hash, lendingBook common.Hash, quantity *big.Int) (bool, *lendingstate.LendingTrade, error) { lendingTrade := lendingStateDB.GetLendingTrade(lendingBook, lendingTradeId) if lendingTrade == lendingstate.EmptyLendingTrade { - return fmt.Errorf("process deposit for emptyLendingTrade is not allowed. lendingTradeId: %v", lendingTradeId.Hex()), true, nil + return true, nil, fmt.Errorf("process deposit for emptyLendingTrade is not allowed. lendingTradeId: %v", lendingTradeId.Hex()) } tokenBalance := lendingstate.GetTokenBalance(lendingTrade.Borrower, lendingTrade.CollateralToken, statedb) if tokenBalance.Cmp(quantity) < 0 { log.Debug("not enough balance deposit", "Quantity", quantity, "tokenBalance", tokenBalance) - return fmt.Errorf("not enough balance deposit. lendingTradeId: %v , Quantity : %v , tokenBalance : %v", lendingTradeId.Hex(), quantity, tokenBalance), true, nil + return true, nil, fmt.Errorf("not enough balance deposit. lendingTradeId: %v , Quantity : %v , tokenBalance : %v", lendingTradeId.Hex(), quantity, tokenBalance) } err := tradingStateDb.RemoveLiquidationPrice(tradingstate.GetTradingOrderBookHash(lendingTrade.CollateralToken, lendingTrade.LendingToken), lendingTrade.LiquidationPrice, lendingBook, lendingTrade.TradeId) if err != nil { - return err, true, nil + return true, nil, err } err = lendingstate.SubTokenBalance(lendingTrade.Borrower, quantity, lendingTrade.CollateralToken, statedb) if err != nil { @@ -1150,7 +1150,7 @@ func (l *Lending) ProcessTopUpLendingTrade(lendingStateDB *lendingstate.LendingS newLendingTrade.LiquidationPrice = newLiquidationPrice newLendingTrade.CollateralLockedAmount = newLockedAmount log.Debug("ProcessTopUp successfully", "price", newLiquidationPrice, "lockAmount", newLockedAmount) - return nil, false, &newLendingTrade + return false, &newLendingTrade, nil } func (l *Lending) ProcessRepayLendingTrade(header *types.Header, chain consensus.ChainContext, lendingStateDB *lendingstate.LendingStateDB, statedb *state.StateDB, tradingstateDB *tradingstate.TradingStateDB, lendingBook common.Hash, lendingTradeId uint64) (trade *lendingstate.LendingTrade, err error) { @@ -1236,14 +1236,14 @@ func (l *Lending) ProcessRepayLendingTrade(header *types.Header, chain consensus return &lendingTrade, nil } -func (l *Lending) ProcessRecallLendingTrade(lendingStateDB *lendingstate.LendingStateDB, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, lendingBook common.Hash, lendingTradeId common.Hash, newLiquidationPrice *big.Int) (error, bool, *lendingstate.LendingTrade) { +func (l *Lending) ProcessRecallLendingTrade(lendingStateDB *lendingstate.LendingStateDB, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, lendingBook common.Hash, lendingTradeId common.Hash, newLiquidationPrice *big.Int) (bool, *lendingstate.LendingTrade, error) { log.Debug("ProcessRecallLendingTrade", "lendingTradeId", lendingTradeId.Hex(), "lendingBook", lendingBook.Hex(), "newLiquidationPrice", newLiquidationPrice) lendingTrade := lendingStateDB.GetLendingTrade(lendingBook, lendingTradeId) if lendingTrade == lendingstate.EmptyLendingTrade { - return fmt.Errorf("process recall for emptyLendingTrade is not allowed. lendingTradeId: %v", lendingTradeId.Hex()), true, nil + return true, nil, fmt.Errorf("process recall for emptyLendingTrade is not allowed. lendingTradeId: %v", lendingTradeId.Hex()) } if newLiquidationPrice.Cmp(lendingTrade.LiquidationPrice) <= 0 { - return fmt.Errorf("New liquidation price must higher than old liquidation price. current liquidation price: %v , new liquidation price : %v ", lendingTrade.LiquidationPrice, newLiquidationPrice), true, nil + return true, nil, fmt.Errorf("New liquidation price must higher than old liquidation price. current liquidation price: %v , new liquidation price : %v ", lendingTrade.LiquidationPrice, newLiquidationPrice) } newLockedAmount := new(big.Int).Mul(lendingTrade.CollateralLockedAmount, lendingTrade.LiquidationPrice) newLockedAmount = new(big.Int).Div(newLockedAmount, newLiquidationPrice) @@ -1251,7 +1251,7 @@ func (l *Lending) ProcessRecallLendingTrade(lendingStateDB *lendingstate.Lending log.Debug("ProcessRecallLendingTrade", "newLockedAmount", newLockedAmount, "recallAmount", recallAmount, "oldLiquidationPrice", lendingTrade.LiquidationPrice, "newLiquidationPrice", newLiquidationPrice) err := tradingStateDb.RemoveLiquidationPrice(tradingstate.GetTradingOrderBookHash(lendingTrade.CollateralToken, lendingTrade.LendingToken), lendingTrade.LiquidationPrice, lendingBook, lendingTrade.TradeId) if err != nil { - return err, true, nil + return true, nil, err } err = lendingstate.AddTokenBalance(lendingTrade.Borrower, recallAmount, lendingTrade.CollateralToken, statedb) if err != nil { @@ -1269,5 +1269,5 @@ func (l *Lending) ProcessRecallLendingTrade(lendingStateDB *lendingstate.Lending newLendingTrade.LiquidationPrice = newLiquidationPrice newLendingTrade.CollateralLockedAmount = newLockedAmount log.Debug("ProcessRecall", "price", newLiquidationPrice, "lockAmount", newLockedAmount, "recall amount", recallAmount) - return nil, false, &newLendingTrade + return false, &newLendingTrade, nil } diff --git a/consensus/XDPoS/engines/engine_v1/engine.go b/consensus/XDPoS/engines/engine_v1/engine.go index c913a80773fc..ce968c76af34 100644 --- a/consensus/XDPoS/engines/engine_v1/engine.go +++ b/consensus/XDPoS/engines/engine_v1/engine.go @@ -53,7 +53,7 @@ type XDPoS_v1 struct { signFn clique.SignerFn // Signer function to authorize hashes with lock sync.RWMutex // Protects the signer fields - HookReward func(chain consensus.ChainReader, state *state.StateDB, parentState *state.StateDB, header *types.Header) (error, map[string]interface{}) + HookReward func(chain consensus.ChainReader, state *state.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) HookPenalty func(chain consensus.ChainReader, blockNumberEpoc uint64) ([]common.Address, error) HookPenaltyTIPSigning func(chain consensus.ChainReader, header *types.Header, candidate []common.Address) ([]common.Address, error) HookValidator func(header *types.Header, signers []common.Address) ([]byte, error) @@ -834,7 +834,7 @@ func (x *XDPoS_v1) Finalize(chain consensus.ChainReader, header *types.Header, s // _ = c.CacheData(header, txs, receipts) if x.HookReward != nil && number%rCheckpoint == 0 { - err, rewards := x.HookReward(chain, state, parentState, header) + rewards, err := x.HookReward(chain, state, parentState, header) if err != nil { return nil, err } diff --git a/contracts/utils.go b/contracts/utils.go index 79841b0b2119..961ea1a61a9f 100644 --- a/contracts/utils.go +++ b/contracts/utils.go @@ -417,12 +417,12 @@ func GetCandidatesOwnerBySigner(statedb *state.StateDB, signerAddr common.Addres return owner } -func CalculateRewardForHolders(foundationWalletAddr common.Address, state *state.StateDB, signer common.Address, calcReward *big.Int, blockNumber uint64) (error, map[common.Address]*big.Int) { +func CalculateRewardForHolders(foundationWalletAddr common.Address, state *state.StateDB, signer common.Address, calcReward *big.Int, blockNumber uint64) (map[common.Address]*big.Int, error) { rewards, err := GetRewardBalancesRate(foundationWalletAddr, state, signer, calcReward, blockNumber) if err != nil { - return err, nil + return nil, err } - return nil, rewards + return rewards, nil } func GetRewardBalancesRate(foundationWalletAddr common.Address, statedb *state.StateDB, masterAddr common.Address, totalReward *big.Int, blockNumber uint64) (map[common.Address]*big.Int, error) { diff --git a/eth/api_backend.go b/eth/api_backend.go index 1a38e5c5686b..4141402ea818 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -494,7 +494,7 @@ func (b *EthApiBackend) GetVotersRewards(masternodeAddr common.Address) map[comm var voterResults map[common.Address]*big.Int for signer, calcReward := range rewardSigners { if signer == masternodeAddr { - err, rewards := contracts.CalculateRewardForHolders(foundationWalletAddr, state, masternodeAddr, calcReward, number) + rewards, err := contracts.CalculateRewardForHolders(foundationWalletAddr, state, masternodeAddr, calcReward, number) if err != nil { log.Crit("Fail to calculate reward for holders.", "error", err) return nil diff --git a/eth/hooks/engine_v1_hooks.go b/eth/hooks/engine_v1_hooks.go index 2661cd764b86..c08aebd75c30 100644 --- a/eth/hooks/engine_v1_hooks.go +++ b/eth/hooks/engine_v1_hooks.go @@ -258,13 +258,13 @@ func AttachConsensusV1Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf } // Hook calculates reward for masternodes - adaptor.EngineV1.HookReward = func(chain consensus.ChainReader, stateBlock *state.StateDB, parentState *state.StateDB, header *types.Header) (error, map[string]interface{}) { + adaptor.EngineV1.HookReward = func(chain consensus.ChainReader, stateBlock *state.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) { number := header.Number.Uint64() rCheckpoint := chain.Config().XDPoS.RewardCheckpoint foundationWalletAddr := chain.Config().XDPoS.FoudationWalletAddr if foundationWalletAddr == (common.Address{}) { log.Error("Foundation Wallet Address is empty", "error", foundationWalletAddr) - return errors.New("foundation Wallet Address is empty"), nil + return nil, errors.New("foundation Wallet Address is empty") } rewards := make(map[string]interface{}) if number > 0 && number-rCheckpoint > 0 && foundationWalletAddr != (common.Address{}) { @@ -290,7 +290,7 @@ func AttachConsensusV1Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf voterResults := make(map[common.Address]interface{}) if len(signers) > 0 { for signer, calcReward := range rewardSigners { - err, rewards := contracts.CalculateRewardForHolders(foundationWalletAddr, parentState, signer, calcReward, number) + rewards, err := contracts.CalculateRewardForHolders(foundationWalletAddr, parentState, signer, calcReward, number) if err != nil { log.Crit("Fail to calculate reward for holders.", "error", err) } @@ -305,7 +305,7 @@ func AttachConsensusV1Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf rewards["rewards"] = voterResults log.Debug("Time Calculated HookReward ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start))) } - return nil, rewards + return rewards, nil } } diff --git a/eth/hooks/engine_v2_hooks.go b/eth/hooks/engine_v2_hooks.go index f356af27a74c..8cb27cdd49c4 100644 --- a/eth/hooks/engine_v2_hooks.go +++ b/eth/hooks/engine_v2_hooks.go @@ -196,7 +196,7 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf voterResults := make(map[common.Address]interface{}) if len(signers) > 0 { for signer, calcReward := range rewardSigners { - err, rewards := contracts.CalculateRewardForHolders(foundationWalletAddr, parentState, signer, calcReward, number) + rewards, err := contracts.CalculateRewardForHolders(foundationWalletAddr, parentState, signer, calcReward, number) if err != nil { log.Error("[HookReward] Fail to calculate reward for holders.", "error", err) return nil, err diff --git a/miner/worker.go b/miner/worker.go index 38e576a32322..d698b2f66a79 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -918,7 +918,7 @@ func (w *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Addr log.Trace("Skipping account with special transaction invalid nonce", "sender", from, "nonce", nonce, "tx nonce ", tx.Nonce(), "to", to) continue } - err, logs, tokenFeeUsed, gas := w.commitTransaction(balanceFee, tx, bc, coinbase, gp) + logs, tokenFeeUsed, gas, err := w.commitTransaction(balanceFee, tx, bc, coinbase, gp) switch err { case core.ErrNonceTooLow: // New head notification data race between the transaction pool and miner, shift @@ -1026,7 +1026,7 @@ func (w *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Addr txs.Pop() continue } - err, logs, tokenFeeUsed, gas := w.commitTransaction(balanceFee, tx, bc, coinbase, gp) + logs, tokenFeeUsed, gas, err := w.commitTransaction(balanceFee, tx, bc, coinbase, gp) switch { case errors.Is(err, core.ErrGasLimitReached): // Pop the current out-of-gas transaction without shifting in the next from the account @@ -1090,16 +1090,16 @@ func (w *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Addr } } -func (w *Work) commitTransaction(balanceFee map[common.Address]*big.Int, tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) (error, []*types.Log, bool, uint64) { +func (w *Work) commitTransaction(balanceFee map[common.Address]*big.Int, tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) ([]*types.Log, bool, uint64, error) { snap := w.state.Snapshot() receipt, gas, err, tokenFeeUsed := core.ApplyTransaction(w.config, balanceFee, bc, &coinbase, gp, w.state, w.tradingState, w.header, tx, &w.header.GasUsed, vm.Config{}) if err != nil { w.state.RevertToSnapshot(snap) - return err, nil, false, 0 + return nil, false, 0, err } w.txs = append(w.txs, tx) w.receipts = append(w.receipts, receipt) - return nil, receipt.Logs, tokenFeeUsed, gas + return receipt.Logs, tokenFeeUsed, gas, nil } From a14e9416faccc1e3c3ab4db048c95b4cf2f4e9db Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 29 Oct 2024 09:16:23 +0800 Subject: [PATCH 059/479] all: fix staticcheck warning SA4010: append result never used --- XDCxlending/lendingstate/statedb_test.go | 7 +++---- bmt/bmt.go | 5 +---- eth/handler.go | 15 +++------------ 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/XDCxlending/lendingstate/statedb_test.go b/XDCxlending/lendingstate/statedb_test.go index 0ea231e5ac9b..07207d5704b0 100644 --- a/XDCxlending/lendingstate/statedb_test.go +++ b/XDCxlending/lendingstate/statedb_test.go @@ -18,10 +18,11 @@ package lendingstate import ( "fmt" - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/core/rawdb" "math/big" "testing" + + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" ) func TestEchangeStates(t *testing.T) { @@ -225,9 +226,7 @@ func TestDumpStates(t *testing.T) { orderBook := common.StringToHash("BTC/XDC") numberOrder := 20 orderItems := []LendingItem{} - relayers := []common.Hash{} for i := 0; i < numberOrder; i++ { - relayers = append(relayers, common.BigToHash(big.NewInt(int64(i)))) id := new(big.Int).SetUint64(uint64(i) + 1) orderItems = append(orderItems, LendingItem{LendingId: id.Uint64(), Quantity: big.NewInt(int64(2*i + 1)), Interest: big.NewInt(1), Side: Investing, Signature: &Signature{V: 1, R: common.HexToHash("111111"), S: common.HexToHash("222222222222")}}) orderItems = append(orderItems, LendingItem{LendingId: id.Uint64(), Quantity: big.NewInt(int64(2*i + 1)), Interest: big.NewInt(1), Side: Borrowing, Signature: &Signature{V: 1, R: common.HexToHash("3333333333"), S: common.HexToHash("22222222222222222")}}) diff --git a/bmt/bmt.go b/bmt/bmt.go index d03bc72d4e10..aa368857694e 100644 --- a/bmt/bmt.go +++ b/bmt/bmt.go @@ -196,11 +196,8 @@ type Tree struct { func (t *Tree) Draw(hash []byte, d int) string { var left, right []string var anc []*Node - for i, n := range t.leaves { + for _, n := range t.leaves { left = append(left, fmt.Sprintf("%v", hashstr(n.left))) - if i%2 == 0 { - anc = append(anc, n.parent) - } right = append(right, fmt.Sprintf("%v", hashstr(n.right))) } anc = t.leaves diff --git a/eth/handler.go b/eth/handler.go index 5cdb4876a878..3bb563ddcdaa 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -769,7 +769,6 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if err := msg.Decode(&txs); err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } - var unkownTxs []*types.Transaction for i, tx := range txs { // Validate and mark the remote transaction @@ -778,9 +777,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } p.MarkTransaction(tx.Hash()) exist, _ := pm.knownTxs.ContainsOrAdd(tx.Hash(), true) - if !exist { - unkownTxs = append(unkownTxs, tx) - } else { + if exist { log.Trace("Discard known tx", "hash", tx.Hash(), "nonce", tx.Nonce(), "to", tx.To()) } @@ -797,7 +794,6 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if err := msg.Decode(&txs); err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } - var unkownOrderTxs []*types.OrderTransaction for i, tx := range txs { // Validate and mark the remote transaction @@ -806,9 +802,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } p.MarkOrderTransaction(tx.Hash()) exist, _ := pm.knowOrderTxs.ContainsOrAdd(tx.Hash(), true) - if !exist { - unkownOrderTxs = append(unkownOrderTxs, tx) - } else { + if exist { log.Trace("Discard known tx", "hash", tx.Hash(), "nonce", tx.Nonce()) } @@ -828,7 +822,6 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if err := msg.Decode(&txs); err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } - var unkownLendingTxs []*types.LendingTransaction for i, tx := range txs { // Validate and mark the remote transaction @@ -837,9 +830,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } p.MarkLendingTransaction(tx.Hash()) exist, _ := pm.knowLendingTxs.ContainsOrAdd(tx.Hash(), true) - if !exist { - unkownLendingTxs = append(unkownLendingTxs, tx) - } else { + if exist { log.Trace("Discard known tx", "hash", tx.Hash(), "nonce", tx.Nonce()) } From f9960875cc9e002b9f9df07b4a7caefc5d7f252b Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 29 Oct 2024 11:29:13 +0800 Subject: [PATCH 060/479] fix: staticcheck warning SA4003: every value of uint64 >= 0 --- eth/hooks/engine_v1_hooks.go | 218 ++++++++++++++++----------------- p2p/discv5/net.go | 2 +- p2p/discv5/nodeevent_string.go | 2 +- 3 files changed, 110 insertions(+), 112 deletions(-) diff --git a/eth/hooks/engine_v1_hooks.go b/eth/hooks/engine_v1_hooks.go index c08aebd75c30..553ffc591671 100644 --- a/eth/hooks/engine_v1_hooks.go +++ b/eth/hooks/engine_v1_hooks.go @@ -30,43 +30,42 @@ func AttachConsensusV1Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf log.Crit("Can't get state at head of canonical chain", "head number", bc.CurrentHeader().Number.Uint64(), "err", err) } prevEpoc := blockNumberEpoc - chain.Config().XDPoS.Epoch - if prevEpoc >= 0 { - start := time.Now() - prevHeader := chain.GetHeaderByNumber(prevEpoc) - penSigners := adaptor.GetMasternodes(chain, prevHeader) - if len(penSigners) > 0 { - // Loop for each block to check missing sign. - for i := prevEpoc; i < blockNumberEpoc; i++ { - if i%common.MergeSignRange == 0 || !chainConfig.IsTIP2019(big.NewInt(int64(i))) { - bheader := chain.GetHeaderByNumber(i) - bhash := bheader.Hash() - block := chain.GetBlock(bhash, i) - if len(penSigners) > 0 { - signedMasternodes, err := contracts.GetSignersFromContract(canonicalState, block) - if err != nil { - return nil, err - } - if len(signedMasternodes) > 0 { - // Check signer signed? - for _, signed := range signedMasternodes { - for j, addr := range penSigners { - if signed == addr { - // Remove it from dupSigners. - penSigners = append(penSigners[:j], penSigners[j+1:]...) - } + + start := time.Now() + prevHeader := chain.GetHeaderByNumber(prevEpoc) + penSigners := adaptor.GetMasternodes(chain, prevHeader) + if len(penSigners) > 0 { + // Loop for each block to check missing sign. + for i := prevEpoc; i < blockNumberEpoc; i++ { + if i%common.MergeSignRange == 0 || !chainConfig.IsTIP2019(big.NewInt(int64(i))) { + bheader := chain.GetHeaderByNumber(i) + bhash := bheader.Hash() + block := chain.GetBlock(bhash, i) + if len(penSigners) > 0 { + signedMasternodes, err := contracts.GetSignersFromContract(canonicalState, block) + if err != nil { + return nil, err + } + if len(signedMasternodes) > 0 { + // Check signer signed? + for _, signed := range signedMasternodes { + for j, addr := range penSigners { + if signed == addr { + // Remove it from dupSigners. + penSigners = append(penSigners[:j], penSigners[j+1:]...) } } } - } else { - break } + } else { + break } } } - log.Debug("Time Calculated HookPenalty ", "block", blockNumberEpoc, "time", common.PrettyDuration(time.Since(start))) - return penSigners, nil } - return []common.Address{}, nil + log.Debug("Time Calculated HookPenalty ", "block", blockNumberEpoc, "time", common.PrettyDuration(time.Since(start))) + return penSigners, nil + } // Hook scans for bad masternodes and decide to penalty them @@ -77,105 +76,104 @@ func AttachConsensusV1Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf if header.Number.Uint64() > comebackLength { combackEpoch = header.Number.Uint64() - comebackLength } - if prevEpoc >= 0 { - start := time.Now() - listBlockHash := make([]common.Hash, chain.Config().XDPoS.Epoch) + start := time.Now() - // get list block hash & stats total created block - statMiners := make(map[common.Address]int) - listBlockHash[0] = header.ParentHash - parentnumber := header.Number.Uint64() - 1 - parentHash := header.ParentHash - for i := uint64(1); i < chain.Config().XDPoS.Epoch; i++ { - parentHeader := chain.GetHeader(parentHash, parentnumber) - miner, _ := adaptor.RecoverSigner(parentHeader) - value, exist := statMiners[miner] - if exist { - value = value + 1 - } else { - value = 1 - } - statMiners[miner] = value - parentHash = parentHeader.ParentHash - parentnumber-- - listBlockHash[i] = parentHash + listBlockHash := make([]common.Hash, chain.Config().XDPoS.Epoch) + + // get list block hash & stats total created block + statMiners := make(map[common.Address]int) + listBlockHash[0] = header.ParentHash + parentnumber := header.Number.Uint64() - 1 + parentHash := header.ParentHash + for i := uint64(1); i < chain.Config().XDPoS.Epoch; i++ { + parentHeader := chain.GetHeader(parentHash, parentnumber) + miner, _ := adaptor.RecoverSigner(parentHeader) + value, exist := statMiners[miner] + if exist { + value = value + 1 + } else { + value = 1 } + statMiners[miner] = value + parentHash = parentHeader.ParentHash + parentnumber-- + listBlockHash[i] = parentHash + } - // add list not miner to penalties - prevHeader := chain.GetHeaderByNumber(prevEpoc) - preMasternodes := adaptor.GetMasternodes(chain, prevHeader) - penalties := []common.Address{} - for miner, total := range statMiners { - if total < common.MinimunMinerBlockPerEpoch { - log.Debug("Find a node not enough requirement create block", "addr", miner.Hex(), "total", total) - penalties = append(penalties, miner) - } + // add list not miner to penalties + prevHeader := chain.GetHeaderByNumber(prevEpoc) + preMasternodes := adaptor.GetMasternodes(chain, prevHeader) + penalties := []common.Address{} + for miner, total := range statMiners { + if total < common.MinimunMinerBlockPerEpoch { + log.Debug("Find a node not enough requirement create block", "addr", miner.Hex(), "total", total) + penalties = append(penalties, miner) } - for _, addr := range preMasternodes { - if _, exist := statMiners[addr]; !exist { - log.Debug("Find a node don't create block", "addr", addr.Hex()) - penalties = append(penalties, addr) - } + } + for _, addr := range preMasternodes { + if _, exist := statMiners[addr]; !exist { + log.Debug("Find a node don't create block", "addr", addr.Hex()) + penalties = append(penalties, addr) } + } - // get list check penalties signing block & list master nodes wil comeback - penComebacks := []common.Address{} - if combackEpoch > 0 { - combackHeader := chain.GetHeaderByNumber(combackEpoch) - penalties := common.ExtractAddressFromBytes(combackHeader.Penalties) - for _, penaltie := range penalties { - for _, addr := range candidates { - if penaltie == addr { - penComebacks = append(penComebacks, penaltie) - } + // get list check penalties signing block & list master nodes wil comeback + penComebacks := []common.Address{} + if combackEpoch > 0 { + combackHeader := chain.GetHeaderByNumber(combackEpoch) + penalties := common.ExtractAddressFromBytes(combackHeader.Penalties) + for _, penaltie := range penalties { + for _, addr := range candidates { + if penaltie == addr { + penComebacks = append(penComebacks, penaltie) } } } + } - // Loop for each block to check missing sign. with comeback nodes - mapBlockHash := map[common.Hash]bool{} - for i := common.RangeReturnSigner - 1; i >= 0; i-- { - if len(penComebacks) > 0 { - blockNumber := header.Number.Uint64() - uint64(i) - 1 - bhash := listBlockHash[i] - if blockNumber%common.MergeSignRange == 0 { - mapBlockHash[bhash] = true - } - signData, ok := adaptor.GetCachedSigningTxs(bhash) - if !ok { - block := chain.GetBlock(bhash, blockNumber) - txs := block.Transactions() - signData = adaptor.CacheSigningTxs(bhash, txs) - } - txs := signData.([]*types.Transaction) - // Check signer signed? - for _, tx := range txs { - blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:]) - from := *tx.From() - if mapBlockHash[blkHash] { - for j, addr := range penComebacks { - if from == addr { - // Remove it from dupSigners. - penComebacks = append(penComebacks[:j], penComebacks[j+1:]...) - break - } + // Loop for each block to check missing sign. with comeback nodes + mapBlockHash := map[common.Hash]bool{} + for i := common.RangeReturnSigner - 1; i >= 0; i-- { + if len(penComebacks) > 0 { + blockNumber := header.Number.Uint64() - uint64(i) - 1 + bhash := listBlockHash[i] + if blockNumber%common.MergeSignRange == 0 { + mapBlockHash[bhash] = true + } + signData, ok := adaptor.GetCachedSigningTxs(bhash) + if !ok { + block := chain.GetBlock(bhash, blockNumber) + txs := block.Transactions() + signData = adaptor.CacheSigningTxs(bhash, txs) + } + txs := signData.([]*types.Transaction) + // Check signer signed? + for _, tx := range txs { + blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:]) + from := *tx.From() + if mapBlockHash[blkHash] { + for j, addr := range penComebacks { + if from == addr { + // Remove it from dupSigners. + penComebacks = append(penComebacks[:j], penComebacks[j+1:]...) + break } } } - } else { - break } + } else { + break } + } - log.Debug("Time Calculated HookPenaltyTIPSigning ", "block", header.Number, "hash", header.Hash().Hex(), "pen comeback nodes", len(penComebacks), "not enough miner", len(penalties), "time", common.PrettyDuration(time.Since(start))) - penalties = append(penalties, penComebacks...) - if chain.Config().IsTIPRandomize(header.Number) { - return penalties, nil - } - return penComebacks, nil + log.Debug("Time Calculated HookPenaltyTIPSigning ", "block", header.Number, "hash", header.Hash().Hex(), "pen comeback nodes", len(penComebacks), "not enough miner", len(penalties), "time", common.PrettyDuration(time.Since(start))) + penalties = append(penalties, penComebacks...) + if chain.Config().IsTIPRandomize(header.Number) { + return penalties, nil } - return []common.Address{}, nil + + return penComebacks, nil } // Hook prepares validators M2 for the current epoch at checkpoint block diff --git a/p2p/discv5/net.go b/p2p/discv5/net.go index 9bf529a4f6d6..54981a4f7b12 100644 --- a/p2p/discv5/net.go +++ b/p2p/discv5/net.go @@ -1230,7 +1230,7 @@ func (net *Network) checkTopicRegister(data *topicRegister) (*pong, error) { if rlpHash(data.Topics) != pongpkt.data.(*pong).TopicHash { return nil, errors.New("topic hash mismatch") } - if data.Idx < 0 || int(data.Idx) >= len(data.Topics) { + if int(data.Idx) >= len(data.Topics) { return nil, errors.New("topic index out of range") } return pongpkt.data.(*pong), nil diff --git a/p2p/discv5/nodeevent_string.go b/p2p/discv5/nodeevent_string.go index eb696fb8beef..d7c883aeb09c 100644 --- a/p2p/discv5/nodeevent_string.go +++ b/p2p/discv5/nodeevent_string.go @@ -16,7 +16,7 @@ var ( func (i nodeEvent) String() string { switch { - case 0 <= i && i <= 8: + case i <= 8: return _nodeEvent_name_0[_nodeEvent_index_0[i]:_nodeEvent_index_0[i+1]] case 265 <= i && i <= 267: i -= 265 From 6b6f0bd8915b16f8665333f2ec83a85f8f578fb3 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 29 Oct 2024 12:01:45 +0800 Subject: [PATCH 061/479] core, XDCxlending/lendingstate: fix staticcheck warning SA5001: must check error before rpcClient.Close() --- XDCxlending/lendingstate/lendingitem_test.go | 2 +- core/lending_pool_test.go | 2 +- core/order_pool_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/XDCxlending/lendingstate/lendingitem_test.go b/XDCxlending/lendingstate/lendingitem_test.go index 692dff1c2fd6..2fcd615012c3 100644 --- a/XDCxlending/lendingstate/lendingitem_test.go +++ b/XDCxlending/lendingstate/lendingitem_test.go @@ -515,11 +515,11 @@ func Test_CreateOrder(t *testing.T) { func sendOrder(nonce uint64) { rpcClient, err := rpc.DialHTTP("http://localhost:8501") - defer rpcClient.Close() if err != nil { fmt.Println("rpc.DialHTTP failed", "err", err) os.Exit(1) } + defer rpcClient.Close() rand.Seed(time.Now().UTC().UnixNano()) item := &LendingOrderMsg{ AccountNonce: nonce, diff --git a/core/lending_pool_test.go b/core/lending_pool_test.go index 5a0dab8e3542..76c50b4ca6dd 100644 --- a/core/lending_pool_test.go +++ b/core/lending_pool_test.go @@ -46,10 +46,10 @@ type LendingMsg struct { func getLendingNonce(userAddress common.Address) (uint64, error) { rpcClient, err := rpc.DialHTTP("http://127.0.0.1:8501") - defer rpcClient.Close() if err != nil { return 0, err } + defer rpcClient.Close() var result interface{} err = rpcClient.Call(&result, "XDCx_getLendingOrderCount", userAddress) if err != nil { diff --git a/core/order_pool_test.go b/core/order_pool_test.go index c12c167d1c9c..87cbfdad1ba9 100644 --- a/core/order_pool_test.go +++ b/core/order_pool_test.go @@ -55,10 +55,10 @@ var ( func getNonce(t *testing.T, userAddress common.Address) (uint64, error) { rpcClient, err := rpc.DialHTTP("http://127.0.0.1:8501") - defer rpcClient.Close() if err != nil { return 0, err } + defer rpcClient.Close() var result interface{} err = rpcClient.Call(&result, "XDCx_getOrderCount", userAddress) if err != nil { From 11285be8305b2e7a7bc51dab971bdd939edb1029 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 29 Oct 2024 12:55:45 +0800 Subject: [PATCH 062/479] all: fix staticcheck warning SA2002: must call T.Fatalf in same goroutine --- core/blockchain_test.go | 9 ++++++--- p2p/simulations/mocker_test.go | 19 +++++++++---------- whisper/whisperv6/peer_test.go | 24 +----------------------- 3 files changed, 16 insertions(+), 36 deletions(-) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 51e2122391d7..43192e4a0fae 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -986,14 +986,17 @@ func TestCanonicalBlockRetrieval(t *testing.T) { continue // busy wait for canonical hash to be written } if ch != block.Hash() { - t.Fatalf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex()) + t.Errorf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex()) + return } fb := GetBlock(blockchain.db, ch, block.NumberU64()) if fb == nil { - t.Fatalf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex()) + t.Errorf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex()) + return } if fb.Hash() != block.Hash() { - t.Fatalf("invalid block hash for block %d, want %s, got %s", block.NumberU64(), block.Hash().Hex(), fb.Hash().Hex()) + t.Errorf("invalid block hash for block %d, want %s, got %s", block.NumberU64(), block.Hash().Hex(), fb.Hash().Hex()) + return } return } diff --git a/p2p/simulations/mocker_test.go b/p2p/simulations/mocker_test.go index becde298354c..32b566ecee7e 100644 --- a/p2p/simulations/mocker_test.go +++ b/p2p/simulations/mocker_test.go @@ -80,14 +80,17 @@ func TestMocker(t *testing.T) { var opts SubscribeOpts sub, err := client.SubscribeNetwork(events, opts) defer sub.Unsubscribe() - //wait until all nodes are started and connected - //store every node up event in a map (value is irrelevant, mimic Set datatype) + + // wait until all nodes are started and connected + // store every node up event in a map (value is irrelevant, mimic Set datatype) nodemap := make(map[discover.NodeID]bool) - wg.Add(1) nodesComplete := false connCount := 0 + wg.Add(1) go func() { - for { + defer wg.Done() + + for connCount < (nodeCount-1)*2 { select { case event := <-events: //if the event is a node Up event only @@ -102,14 +105,10 @@ func TestMocker(t *testing.T) { } } else if event.Conn != nil && nodesComplete { connCount += 1 - if connCount == (nodeCount-1)*2 { - wg.Done() - return - } } case <-time.After(30 * time.Second): - wg.Done() - t.Fatalf("Timeout waiting for nodes being started up!") + t.Errorf("Timeout waiting for nodes being started up!") + return } } }() diff --git a/whisper/whisperv6/peer_test.go b/whisper/whisperv6/peer_test.go index d5869e180c04..8c8249748109 100644 --- a/whisper/whisperv6/peer_test.go +++ b/whisper/whisperv6/peer_test.go @@ -23,7 +23,6 @@ import ( mrand "math/rand" "net" "sync" - "sync/atomic" "testing" "time" @@ -72,7 +71,6 @@ var keys = []string{ } type TestData struct { - started int64 counter [NumNodes]int mutex sync.RWMutex } @@ -226,14 +224,9 @@ func initialize(t *testing.T) { }, } + startServer(t, node.server) nodes[i] = &node } - - for i := 0; i < NumNodes; i++ { - go startServer(t, nodes[i].server) - } - - waitForServersToStart(t) } func startServer(t *testing.T, s *p2p.Server) { @@ -241,8 +234,6 @@ func startServer(t *testing.T, s *p2p.Server) { if err != nil { t.Fatalf("failed to start the fisrt server.") } - - atomic.AddInt64(&result.started, 1) } func stopServers() { @@ -500,16 +491,3 @@ func checkBloomFilterExchange(t *testing.T) { time.Sleep(50 * time.Millisecond) } } - -func waitForServersToStart(t *testing.T) { - const iterations = 200 - var started int64 - for j := 0; j < iterations; j++ { - time.Sleep(50 * time.Millisecond) - started = atomic.LoadInt64(&result.started) - if started == NumNodes { - return - } - } - t.Fatalf("Failed to start all the servers, running: %d", started) -} From edce9ccb27a82996ab4c9a3eb6d0bd8e57b403ad Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 29 Oct 2024 14:07:10 +0800 Subject: [PATCH 063/479] all: fix staticcheck warning S1024: not use x.Sub(time.Now()) --- cmd/faucet/faucet.go | 2 +- consensus/clique/clique.go | 2 +- eth/fetcher/fetcher.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go index d42713abbd98..050fc7f80bc9 100644 --- a/cmd/faucet/faucet.go +++ b/cmd/faucet/faucet.go @@ -517,7 +517,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) { // Send an error if too frequent funding, othewise a success if !fund { - if err = sendError(wsconn, fmt.Errorf("%s left until next allowance", common.PrettyDuration(timeout.Sub(time.Now())))); err != nil { // nolint: gosimple + if err = sendError(wsconn, fmt.Errorf("%s left until next allowance", common.PrettyDuration(time.Until(timeout)))); err != nil { log.Warn("Failed to send funding error to client", "err", err) return } diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index bb1e9bb4b436..e1bdcc16e957 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -644,7 +644,7 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, stop <-ch } } // Sweet, the protocol permits us to sign the block, wait for our time - delay := time.Unix(header.Time.Int64(), 0).Sub(time.Now()) // nolint: gosimple + delay := time.Until(time.Unix(header.Time.Int64(), 0)) if header.Difficulty.Cmp(diffNoTurn) == 0 { // It's not our turn explicitly to sign, delay it a bit wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime diff --git a/eth/fetcher/fetcher.go b/eth/fetcher/fetcher.go index 7d1e15fd4dea..ed362f027635 100644 --- a/eth/fetcher/fetcher.go +++ b/eth/fetcher/fetcher.go @@ -679,7 +679,7 @@ func (f *Fetcher) insert(peer string, block *types.Block) { go f.broadcastBlock(block, true) } case consensus.ErrFutureBlock: - delay := time.Unix(block.Time().Int64(), 0).Sub(time.Now()) // nolint: gosimple + delay := time.Until(time.Unix(block.Time().Int64(), 0)) log.Info("Receive future block", "number", block.NumberU64(), "hash", block.Hash().Hex(), "delay", delay) time.Sleep(delay) goto again From b9323b73e09eb634b2eadede4e75020d85be8dd8 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 29 Oct 2024 16:26:18 +0800 Subject: [PATCH 064/479] metrics/influxdb: fix staticcheck warning SA1015: replace time.Tick with time.NewTicker --- metrics/influxdb/influxdb.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/metrics/influxdb/influxdb.go b/metrics/influxdb/influxdb.go index 90df2e18a757..5c00c1a4c320 100644 --- a/metrics/influxdb/influxdb.go +++ b/metrics/influxdb/influxdb.go @@ -69,16 +69,19 @@ func (r *reporter) makeClient() (err error) { } func (r *reporter) run() { - intervalTicker := time.Tick(r.interval) - pingTicker := time.Tick(time.Second * 5) + intervalTicker := time.NewTicker(r.interval) + pingTicker := time.NewTicker(time.Second * 5) + + defer intervalTicker.Stop() + defer pingTicker.Stop() for { select { - case <-intervalTicker: + case <-intervalTicker.C: if err := r.send(); err != nil { log.Printf("unable to send to InfluxDB. err=%v", err) } - case <-pingTicker: + case <-pingTicker.C: _, _, err := r.client.Ping() if err != nil { log.Printf("got error while sending a ping to InfluxDB, trying to recreate client. err=%v", err) From 2db2f9d1f731b57f12c6d9e389e9fe3e01948819 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 30 Oct 2024 09:19:21 +0800 Subject: [PATCH 065/479] XDCxlending: fix staticcheck warning S1008: simplify returning boolean expression --- XDCxlending/XDCxlending.go | 5 +---- XDCxlending/lendingstate/relayer.go | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/XDCxlending/XDCxlending.go b/XDCxlending/XDCxlending.go index 3816ab758a40..28e9321aff24 100644 --- a/XDCxlending/XDCxlending.go +++ b/XDCxlending/XDCxlending.go @@ -684,10 +684,7 @@ func (l *Lending) HasLendingState(block *types.Block, author common.Address) boo return false } _, err = l.StateCache.OpenTrie(root) - if err != nil { - return false - } - return true + return err == nil } func (l *Lending) GetTriegc() *prque.Prque { diff --git a/XDCxlending/lendingstate/relayer.go b/XDCxlending/lendingstate/relayer.go index 56ee05a4c267..e7ca282d1b17 100644 --- a/XDCxlending/lendingstate/relayer.go +++ b/XDCxlending/lendingstate/relayer.go @@ -41,10 +41,7 @@ func IsResignedRelayer(relayer common.Address, statedb *state.StateDB) bool { slot := RelayerMappingSlot["RESIGN_REQUESTS"] locBig := GetLocMappingAtKey(relayer.Hash(), slot) locHash := common.BigToHash(locBig) - if statedb.GetState(common.HexToAddress(common.RelayerRegistrationSMC), locHash) != (common.Hash{}) { - return true - } - return false + return statedb.GetState(common.HexToAddress(common.RelayerRegistrationSMC), locHash) != (common.Hash{}) } func GetBaseTokenLength(relayer common.Address, statedb *state.StateDB) uint64 { From b7154dd450c2b68a67f218cd25023b23203a47ba Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 31 Oct 2024 16:45:30 +0800 Subject: [PATCH 066/479] core: fix staticcheck warning S1006: use for {} for infinite loops --- core/lending_pool_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lending_pool_test.go b/core/lending_pool_test.go index 76c50b4ca6dd..555c53ebc5cf 100644 --- a/core/lending_pool_test.go +++ b/core/lending_pool_test.go @@ -181,7 +181,7 @@ func TestSendLending(t *testing.T) { t.FailNow() } - for true { + for { // 10% interestRate := 10 * common.BaseLendingInterest.Uint64() // lendToken: USD, collateral: BTC From af40f2e19494e84d4917f8a1c3e2efb1c216f2df Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 31 Oct 2024 15:31:48 +0800 Subject: [PATCH 067/479] eth/downloader: fix staticcheck warning S1033: unnecessary guard for delete --- eth/downloader/statesync.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/eth/downloader/statesync.go b/eth/downloader/statesync.go index 0012c0c67fb1..d27092e02fce 100644 --- a/eth/downloader/statesync.go +++ b/eth/downloader/statesync.go @@ -419,9 +419,7 @@ func (s *stateSync) process(req *stateReq) error { default: return fmt.Errorf("invalid state node %s: %v", hash.TerminalString(), err) } - if _, ok := req.tasks[hash]; ok { - delete(req.tasks, hash) - } + delete(req.tasks, hash) } // Put unfulfilled tasks back into the retry queue npeers := s.d.peers.Len() From 5132d7f7e00cf0835aee92382c3dc2ba29d3af11 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 31 Oct 2024 16:16:56 +0800 Subject: [PATCH 068/479] contracts: fix staticcheck warning S1025: unnecessary fmt.Sprintf("%s", x) --- contracts/utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/utils.go b/contracts/utils.go index 961ea1a61a9f..17e108ee6466 100644 --- a/contracts/utils.go +++ b/contracts/utils.go @@ -556,7 +556,7 @@ func Decrypt(key []byte, cryptoText string) string { // XORKeyStream can work in-place if the two arguments are the same. stream.XORKeyStream(ciphertext, ciphertext) - return fmt.Sprintf("%s", ciphertext) + return string(ciphertext[:]) } // Generate random string. From 363d9784bf9868c5fc6e09a4c7cd5d681cef2695 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 31 Oct 2024 16:31:26 +0800 Subject: [PATCH 069/479] engines/engine_v2: fix staticcheck warning S1009: should omit nil check --- consensus/XDPoS/engines/engine_v2/verifyHeader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/XDPoS/engines/engine_v2/verifyHeader.go b/consensus/XDPoS/engines/engine_v2/verifyHeader.go index 7ba672bccbaa..ca9aa20cefe9 100644 --- a/consensus/XDPoS/engines/engine_v2/verifyHeader.go +++ b/consensus/XDPoS/engines/engine_v2/verifyHeader.go @@ -109,7 +109,7 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade if !bytes.Equal(header.Nonce[:], utils.NonceDropVote) { return utils.ErrInvalidCheckpointVote } - if header.Validators == nil || len(header.Validators) == 0 { + if len(header.Validators) == 0 { return utils.ErrEmptyEpochSwitchValidators } if len(header.Validators)%common.AddressLength != 0 { From f9ece4f0416bea9a8b0c9a50ce2c66f7dc1d9efe Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 31 Oct 2024 13:31:51 +0800 Subject: [PATCH 070/479] p2p: fix staticcheck warning SA4030: rand.Intn(1) always returns 0 --- p2p/peer_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2p/peer_test.go b/p2p/peer_test.go index a3e1c74fd876..20d020f186d8 100644 --- a/p2p/peer_test.go +++ b/p2p/peer_test.go @@ -150,7 +150,7 @@ func TestPeerDisconnect(t *testing.T) { // This test is supposed to verify that Peer can reliably handle // multiple causes of disconnection occurring at the same time. func TestPeerDisconnectRace(t *testing.T) { - maybe := func() bool { return rand.Intn(1) == 1 } + maybe := func() bool { return rand.Intn(2) == 1 } for i := 0; i < 1000; i++ { protoclose := make(chan error) From 9e9992835743c1d25912779bc4bc5e9e939cb21e Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 31 Oct 2024 13:51:26 +0800 Subject: [PATCH 071/479] p2p: fix staticcheck warning SA4009: overwrite argument before use --- p2p/rlpx.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/p2p/rlpx.go b/p2p/rlpx.go index 5ceb897eae09..a76454528372 100644 --- a/p2p/rlpx.go +++ b/p2p/rlpx.go @@ -183,7 +183,7 @@ func (t *rlpx) doEncHandshake(prv *ecdsa.PrivateKey, dial *discover.Node) (disco if dial == nil { sec, err = receiverEncHandshake(t.fd, prv, nil) } else { - sec, err = initiatorEncHandshake(t.fd, prv, dial.ID, nil) + sec, err = initiatorEncHandshake(t.fd, prv, dial.ID) } if err != nil { return discover.NodeID{}, err @@ -280,9 +280,9 @@ func (h *encHandshake) staticSharedSecret(prv *ecdsa.PrivateKey) ([]byte, error) // it should be called on the dialing side of the connection. // // prv is the local client's private key. -func initiatorEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, remoteID discover.NodeID, token []byte) (s secrets, err error) { +func initiatorEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, remoteID discover.NodeID) (s secrets, err error) { h := &encHandshake{initiator: true, remoteID: remoteID} - authMsg, err := h.makeAuthMsg(prv, token) + authMsg, err := h.makeAuthMsg(prv) if err != nil { return s, err } @@ -306,7 +306,7 @@ func initiatorEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, remoteID d } // makeAuthMsg creates the initiator handshake message. -func (h *encHandshake) makeAuthMsg(prv *ecdsa.PrivateKey, token []byte) (*authMsgV4, error) { +func (h *encHandshake) makeAuthMsg(prv *ecdsa.PrivateKey) (*authMsgV4, error) { rpub, err := h.remoteID.Pubkey() if err != nil { return nil, fmt.Errorf("bad remoteID: %v", err) @@ -324,7 +324,7 @@ func (h *encHandshake) makeAuthMsg(prv *ecdsa.PrivateKey, token []byte) (*authMs } // Sign known message: static-shared-secret ^ nonce - token, err = h.staticSharedSecret(prv) + token, err := h.staticSharedSecret(prv) if err != nil { return nil, err } From 36211ef5f15afdbd848a887335808bb1fbbd4c02 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 31 Oct 2024 14:55:09 +0800 Subject: [PATCH 072/479] p2p/netutil: fix staticcheck warning SA1021: not use bytes.Equal to compare net.IPs --- p2p/netutil/net.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2p/netutil/net.go b/p2p/netutil/net.go index 656abb6825b2..d5da3c694f8e 100644 --- a/p2p/netutil/net.go +++ b/p2p/netutil/net.go @@ -212,7 +212,7 @@ func sameNet(bits uint, ip, other net.IP) bool { if mask != 0 && nb < len(ip) && ip[nb]&mask != other[nb]&mask { return false } - return nb <= len(ip) && bytes.Equal(ip[:nb], other[:nb]) + return nb <= len(ip) && ip[:nb].Equal(other[:nb]) } // DistinctNetSet tracks IPs, ensuring that at most N of them From 089a589a810efec059ecce4a5eb15ef8b80d9e78 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 7 Aug 2024 10:55:44 +0800 Subject: [PATCH 073/479] eth: fix typo balacne --- eth/api_tracer.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/eth/api_tracer.go b/eth/api_tracer.go index dd076156df82..3b9f0e56ab8e 100644 --- a/eth/api_tracer.go +++ b/eth/api_tracer.go @@ -236,13 +236,13 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl feeCapacity := state.GetTRC21FeeCapacityFromState(task.statedb) // Trace all the transactions contained within for i, tx := range task.block.Transactions() { - var balacne *big.Int + var balance *big.Int if tx.To() != nil { if value, ok := feeCapacity[*tx.To()]; ok { - balacne = value + balance = value } } - msg, _ := tx.AsMessage(signer, balacne, task.block.Number()) + msg, _ := tx.AsMessage(signer, balance, task.block.Number()) txctx := &tracers.Context{ BlockHash: task.block.Hash(), TxIndex: i, @@ -480,13 +480,13 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, // Fetch and execute the next transaction trace tasks for task := range jobs { feeCapacity := state.GetTRC21FeeCapacityFromState(task.statedb) - var balacne *big.Int + var balance *big.Int if txs[task.index].To() != nil { if value, ok := feeCapacity[*txs[task.index].To()]; ok { - balacne = value + balance = value } } - msg, _ := txs[task.index].AsMessage(signer, balacne, block.Number()) + msg, _ := txs[task.index].AsMessage(signer, balance, block.Number()) txctx := &tracers.Context{ BlockHash: blockHash, TxIndex: task.index, @@ -507,18 +507,18 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, for i, tx := range txs { // Send the trace task over for execution jobs <- &txTraceTask{statedb: statedb.Copy(), index: i} - var balacne *big.Int + var balance *big.Int if tx.To() != nil { // Bypass the validation for trading and lending transactions as their nonce are not incremented if tx.IsSkipNonceTransaction() { continue } if value, ok := feeCapacity[*tx.To()]; ok { - balacne = value + balance = value } } // Generate the next state snapshot fast without tracing - msg, _ := tx.AsMessage(signer, balacne, block.Number()) + msg, _ := tx.AsMessage(signer, balance, block.Number()) txContext := core.NewEVMTxContext(msg) statedb.Prepare(tx.Hash(), i) From e561f2842ed0297909a89dd560cba72c2b7228fa Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 25 Jun 2024 23:19:02 +0800 Subject: [PATCH 074/479] core/types: drop type v4StoredReceiptRLP --- core/types/receipt.go | 49 +++++--------------------------------- core/types/receipt_test.go | 21 +--------------- 2 files changed, 7 insertions(+), 63 deletions(-) diff --git a/core/types/receipt.go b/core/types/receipt.go index 76507b521064..9ac2616fe20d 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -90,18 +90,8 @@ type receiptRLP struct { Logs []*Log } -// v4StoredReceiptRLP is the storage encoding of a receipt used in database version 4. -type v4StoredReceiptRLP struct { - PostStateOrStatus []byte - CumulativeGasUsed uint64 - TxHash common.Hash - ContractAddress common.Address - Logs []*LogForStorage - GasUsed uint64 -} - -// v3StoredReceiptRLP is the original storage encoding of a receipt including some unnecessary fields. -type v3StoredReceiptRLP struct { +// receiptStorageRLP is the original storage encoding of a receipt including some unnecessary fields. +type receiptStorageRLP struct { PostStateOrStatus []byte CumulativeGasUsed uint64 Bloom Bloom @@ -241,7 +231,7 @@ type ReceiptForStorage Receipt // EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt // into an RLP stream. func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error { - enc := &v3StoredReceiptRLP{ + enc := &receiptStorageRLP{ PostStateOrStatus: (*Receipt)(r).statusEncoding(), CumulativeGasUsed: r.CumulativeGasUsed, Bloom: r.Bloom, @@ -264,17 +254,11 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { if err != nil { return err } - // Try decoding from the newest format for future proofness, then the older one - // for old nodes that just upgraded. V4 was an intermediate unreleased format so - // we do need to decode it, but it's not common (try last). - if err := decodeV3StoredReceiptRLP(r, blob); err == nil { - return nil - } - return decodeV4StoredReceiptRLP(r, blob) + return decodeStoredReceiptRLP(r, blob) } -func decodeV3StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error { - var stored v3StoredReceiptRLP +func decodeStoredReceiptRLP(r *ReceiptForStorage, blob []byte) error { + var stored receiptStorageRLP if err := rlp.DecodeBytes(blob, &stored); err != nil { return err } @@ -295,27 +279,6 @@ func decodeV3StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error { return nil } -func decodeV4StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error { - var stored v4StoredReceiptRLP - if err := rlp.DecodeBytes(blob, &stored); err != nil { - return err - } - if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil { - return err - } - r.CumulativeGasUsed = stored.CumulativeGasUsed - r.TxHash = stored.TxHash - r.ContractAddress = stored.ContractAddress - r.GasUsed = stored.GasUsed - r.Logs = make([]*Log, len(stored.Logs)) - for i, log := range stored.Logs { - r.Logs[i] = (*Log)(log) - } - r.Bloom = CreateBloom(Receipts{(*Receipt)(r)}) - - return nil -} - // Receipts implements DerivableList for receipts. type Receipts []*Receipt diff --git a/core/types/receipt_test.go b/core/types/receipt_test.go index 1ca5a2864774..ac6b6b89ea08 100644 --- a/core/types/receipt_test.go +++ b/core/types/receipt_test.go @@ -43,10 +43,6 @@ func TestLegacyReceiptDecoding(t *testing.T) { name string encode func(*Receipt) ([]byte, error) }{ - { - "V4StoredReceiptRLP", - encodeAsV4StoredReceiptRLP, - }, { "V3StoredReceiptRLP", encodeAsV3StoredReceiptRLP, @@ -113,23 +109,8 @@ func TestLegacyReceiptDecoding(t *testing.T) { } } -func encodeAsV4StoredReceiptRLP(want *Receipt) ([]byte, error) { - stored := &v4StoredReceiptRLP{ - PostStateOrStatus: want.statusEncoding(), - CumulativeGasUsed: want.CumulativeGasUsed, - TxHash: want.TxHash, - ContractAddress: want.ContractAddress, - Logs: make([]*LogForStorage, len(want.Logs)), - GasUsed: want.GasUsed, - } - for i, log := range want.Logs { - stored.Logs[i] = (*LogForStorage)(log) - } - return rlp.EncodeToBytes(stored) -} - func encodeAsV3StoredReceiptRLP(want *Receipt) ([]byte, error) { - stored := &v3StoredReceiptRLP{ + stored := &receiptStorageRLP{ PostStateOrStatus: want.statusEncoding(), CumulativeGasUsed: want.CumulativeGasUsed, Bloom: want.Bloom, From 6c77d63dc252998bffcec39fe9437d7239879183 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 26 Jun 2024 08:52:59 +0800 Subject: [PATCH 075/479] core/types: replace core.SetReceiptsData with receipts.DeriveFields --- core/blockchain.go | 56 ++++++-------------------------------------- light/odr_util.go | 19 +++++++++++++-- light/postprocess.go | 2 +- 3 files changed, 25 insertions(+), 52 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 48a7e69b5e3b..247319a91e43 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1029,49 +1029,6 @@ func (bc *BlockChain) Rollback(chain []common.Hash) { } } -// SetReceiptsData computes all the non-consensus fields of the receipts -func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts types.Receipts) error { - signer := types.MakeSigner(config, block.Number()) - - transactions, logIndex := block.Transactions(), uint(0) - if len(transactions) != len(receipts) { - return errors.New("transaction and receipt count mismatch") - } - - for j := 0; j < len(receipts); j++ { - // The transaction hash can be retrieved from the transaction itself - receipts[j].TxHash = transactions[j].Hash() - - // block location fields - receipts[j].BlockHash = block.Hash() - receipts[j].BlockNumber = block.Number() - receipts[j].TransactionIndex = uint(j) - - // The contract address can be derived from the transaction itself - if transactions[j].To() == nil { - // Deriving the signer is expensive, only do if it's actually needed - from, _ := types.Sender(signer, transactions[j]) - receipts[j].ContractAddress = crypto.CreateAddress(from, transactions[j].Nonce()) - } - // The used gas can be calculated based on previous receipts - if j == 0 { - receipts[j].GasUsed = receipts[j].CumulativeGasUsed - } else { - receipts[j].GasUsed = receipts[j].CumulativeGasUsed - receipts[j-1].CumulativeGasUsed - } - // The derived log fields can simply be set from the block and transaction - for k := 0; k < len(receipts[j].Logs); k++ { - receipts[j].Logs[k].BlockNumber = block.NumberU64() - receipts[j].Logs[k].BlockHash = block.Hash() - receipts[j].Logs[k].TxHash = receipts[j].TxHash - receipts[j].Logs[k].TxIndex = uint(j) - receipts[j].Logs[k].Index = logIndex - logIndex++ - } - } - return nil -} - // InsertReceiptChain attempts to complete an already existing header chain with // transaction and receipt data. func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) { @@ -1100,22 +1057,23 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ if atomic.LoadInt32(&bc.procInterrupt) == 1 { return 0, nil } + blockHash, blockNumber := block.Hash(), block.NumberU64() // Short circuit if the owner header is unknown - if !bc.HasHeader(block.Hash(), block.NumberU64()) { - return i, fmt.Errorf("containing header #%d [%x…] unknown", block.Number(), block.Hash().Bytes()[:4]) + if !bc.HasHeader(blockHash, blockNumber) { + return i, fmt.Errorf("containing header #%d [%x…] unknown", blockNumber, blockHash.Bytes()[:4]) } // Skip if the entire data is already known - if bc.HasBlock(block.Hash(), block.NumberU64()) { + if bc.HasBlock(blockHash, blockNumber) { stats.ignored++ continue } // Compute all the non-consensus fields of the receipts - if err := SetReceiptsData(bc.chainConfig, block, receipts); err != nil { + if err := receipts.DeriveFields(bc.chainConfig, blockHash, blockNumber, block.Transactions()); err != nil { return i, fmt.Errorf("failed to set receipts data: %v", err) } // Write all the data out into the database - rawdb.WriteBody(batch, block.Hash(), block.NumberU64(), block.Body()) - if err := WriteBlockReceipts(batch, block.Hash(), block.NumberU64(), receipts); err != nil { + rawdb.WriteBody(batch, blockHash, blockNumber, block.Body()) + if err := WriteBlockReceipts(batch, blockHash, blockNumber, receipts); err != nil { return i, fmt.Errorf("failed to write block receipts: %v", err) } if err := WriteTxLookupEntries(batch, block); err != nil { diff --git a/light/odr_util.go b/light/odr_util.go index d7ebd6739f75..11ddda877ae4 100644 --- a/light/odr_util.go +++ b/light/odr_util.go @@ -19,6 +19,7 @@ package light import ( "bytes" "context" + "errors" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core" @@ -29,6 +30,13 @@ import ( var sha3_nil = crypto.Keccak256Hash(nil) +// errNonCanonicalHash is returned if the requested chain data doesn't belong +// to the canonical chain. ODR can only retrieve the canonical chain data covered +// by the CHT or Bloom trie for verification. +var errNonCanonicalHash = errors.New("hash is not currently canonical") + +// GetHeaderByNumber retrieves the canonical block header corresponding to the +// given number. The returned header is proven by local CHT. func GetHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64) (*types.Header, error) { db := odr.Database() hash := core.GetCanonicalHash(db, number) @@ -113,7 +121,7 @@ func GetBlock(ctx context.Context, odr OdrBackend, hash common.Hash, number uint // Retrieve the block header and body contents header := core.GetHeader(odr.Database(), hash, number) if header == nil { - return nil, ErrNoHeader + return nil, errNoHeader } body, err := GetBody(ctx, odr, hash, number) if err != nil { @@ -129,6 +137,13 @@ func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, num // Retrieve the potentially incomplete receipts from disk or network receipts := core.GetBlockReceipts(odr.Database(), hash, number) if receipts == nil { + header, err := GetHeaderByNumber(ctx, odr, number) + if err != nil { + return nil, errNoHeader + } + if header.Hash() != hash { + return nil, errNonCanonicalHash + } r := &ReceiptsRequest{Hash: hash, Number: number} if err := odr.Retrieve(ctx, r); err != nil { return nil, err @@ -144,7 +159,7 @@ func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, num genesis := core.GetCanonicalHash(odr.Database(), 0) config, _ := core.GetChainConfig(odr.Database(), genesis) - if err := core.SetReceiptsData(config, block, receipts); err != nil { + if err := receipts.DeriveFields(config, hash, number, block.Transactions()); err != nil { return nil, err } core.WriteBlockReceipts(odr.Database(), hash, number, receipts) diff --git a/light/postprocess.go b/light/postprocess.go index f407c85d145e..5f6799cf7afb 100644 --- a/light/postprocess.go +++ b/light/postprocess.go @@ -83,7 +83,7 @@ var trustedCheckpoints = map[common.Hash]trustedCheckpoint{ var ( ErrNoTrustedCht = errors.New("no trusted canonical hash trie") ErrNoTrustedBloomTrie = errors.New("no trusted bloom trie") - ErrNoHeader = errors.New("header not found") + errNoHeader = errors.New("header not found") chtPrefix = []byte("chtRoot-") // chtPrefix + chtNum (uint64 big endian) -> trie root hash ChtTablePrefix = "cht-" ) From b5fe81f0938f20ee77d596354c50a893793769a8 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 27 Apr 2021 13:21:41 +0200 Subject: [PATCH 076/479] core/vm: make gas cost reporting to tracers correct (#22702) Previously, the makeCallVariantGasCallEIP2929 charged the cold account access cost directly, leading to an incorrect gas cost passed to the tracer from the main execution loop. This change still temporarily charges the cost (to allow for an accurate calculation of the available gas for the call), but then afterwards refunds it and instead returns the correct total gas cost to be then properly charged in the main loop. --- core/vm/operations_acl.go | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index ba811cdaa865..c0d8ba67876e 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -177,10 +177,15 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc { return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { addr := common.Address(stack.Back(1).Bytes20()) // Check slot presence in the access list - if !evm.StateDB.AddressInAccessList(addr) { + warmAccess := evm.StateDB.AddressInAccessList(addr) + // The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so + // the cost to charge for cold access, if any, is Cold - Warm + coldCost := ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929 + if !warmAccess { evm.StateDB.AddAddressToAccessList(addr) - // The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost - if !contract.UseGas(ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929) { + // Charge the remaining difference here already, to correctly calculate available + // gas for call + if !contract.UseGas(coldCost) { return 0, ErrOutOfGas } } @@ -189,7 +194,16 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc { // - transfer value // - memory expansion // - 63/64ths rule - return oldCalculator(evm, contract, stack, mem, memorySize) + gas, err := oldCalculator(evm, contract, stack, mem, memorySize) + if warmAccess || err != nil { + return gas, err + } + // In case of a cold access, we temporarily add the cold charge back, and also + // add it to the returned gas. By adding it to the return, it will be charged + // outside of this function, as part of the dynamic gas, and that will make it + // also become correctly reported to tracers. + contract.Gas += coldCost + return gas + coldCost, nil } } From 6a3b818069272e714dba058b6dbc51ccf7bd2815 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 22 Aug 2024 16:35:50 +0800 Subject: [PATCH 077/479] core, params: implement EIP-3529 (#22733) --- core/blockchain_test.go | 3 +- core/state_transition.go | 15 ++- core/vm/eips.go | 26 +++-- core/vm/jump_table.go | 2 +- core/vm/operations_acl.go | 209 ++++++++++++++++++++------------------ params/protocol_params.go | 25 ++++- 6 files changed, 161 insertions(+), 119 deletions(-) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 43192e4a0fae..7d6b676ed64d 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -1499,7 +1499,8 @@ func TestEIP2718Transition(t *testing.T) { block := chain.GetBlockByNumber(1) // Expected gas is intrinsic + 2 * pc + hot load + cold load, since only one load is in the access list - expected := params.TxGas + params.TxAccessListAddressGas + params.TxAccessListStorageKeyGas + vm.GasQuickStep*2 + vm.WarmStorageReadCostEIP2929 + vm.ColdSloadCostEIP2929 + expected := params.TxGas + params.TxAccessListAddressGas + params.TxAccessListStorageKeyGas + + vm.GasQuickStep*2 + params.WarmStorageReadCostEIP2929 + params.ColdSloadCostEIP2929 if block.GasUsed() != expected { t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expected, block.GasUsed()) } diff --git a/core/state_transition.go b/core/state_transition.go index 591f2b423a02..92bbfcd41297 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -247,6 +247,7 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG sender := st.from() // err checked in preCheck homestead := st.evm.ChainConfig().IsHomestead(st.evm.Context.BlockNumber) + eip3529 := st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber) contractCreation := msg.To() == nil // Pay intrinsic gas @@ -293,7 +294,13 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG return nil, 0, false, vmerr, nil } } - st.refundGas() + if !eip3529 { + // Before EIP-3529: refunds were capped to gasUsed / 2 + st.refundGas(params.RefundQuotient) + } else { + // After EIP-3529: refunds are capped to gasUsed / 5 + st.refundGas(params.RefundQuotientEIP3529) + } if st.evm.Context.BlockNumber.Cmp(common.TIPTRC21Fee) > 0 { if (owner != common.Address{}) { @@ -306,9 +313,9 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG return ret, st.gasUsed(), vmerr != nil, nil, vmerr } -func (st *StateTransition) refundGas() { - // Apply refund counter, capped to half of the used gas. - refund := st.gasUsed() / 2 +func (st *StateTransition) refundGas(refundQuotient uint64) { + // Apply refund counter, capped to a refund quotient + refund := st.gasUsed() / refundQuotient if refund > st.state.GetRefund() { refund = st.state.GetRefund() } diff --git a/core/vm/eips.go b/core/vm/eips.go index 0d7cc71f119f..19ea9a8980ff 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -26,6 +26,7 @@ import ( var activators = map[int]func(*JumpTable){ 3855: enable3855, + 3529: enable3529, 3198: enable3198, 2929: enable2929, 2200: enable2200, @@ -104,28 +105,28 @@ func enable2929(jt *JumpTable) { jt[SLOAD].constantGas = 0 jt[SLOAD].dynamicGas = gasSLoadEIP2929 - jt[EXTCODECOPY].constantGas = WarmStorageReadCostEIP2929 + jt[EXTCODECOPY].constantGas = params.WarmStorageReadCostEIP2929 jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP2929 - jt[EXTCODESIZE].constantGas = WarmStorageReadCostEIP2929 + jt[EXTCODESIZE].constantGas = params.WarmStorageReadCostEIP2929 jt[EXTCODESIZE].dynamicGas = gasEip2929AccountCheck - jt[EXTCODEHASH].constantGas = WarmStorageReadCostEIP2929 + jt[EXTCODEHASH].constantGas = params.WarmStorageReadCostEIP2929 jt[EXTCODEHASH].dynamicGas = gasEip2929AccountCheck - jt[BALANCE].constantGas = WarmStorageReadCostEIP2929 + jt[BALANCE].constantGas = params.WarmStorageReadCostEIP2929 jt[BALANCE].dynamicGas = gasEip2929AccountCheck - jt[CALL].constantGas = WarmStorageReadCostEIP2929 + jt[CALL].constantGas = params.WarmStorageReadCostEIP2929 jt[CALL].dynamicGas = gasCallEIP2929 - jt[CALLCODE].constantGas = WarmStorageReadCostEIP2929 + jt[CALLCODE].constantGas = params.WarmStorageReadCostEIP2929 jt[CALLCODE].dynamicGas = gasCallCodeEIP2929 - jt[STATICCALL].constantGas = WarmStorageReadCostEIP2929 + jt[STATICCALL].constantGas = params.WarmStorageReadCostEIP2929 jt[STATICCALL].dynamicGas = gasStaticCallEIP2929 - jt[DELEGATECALL].constantGas = WarmStorageReadCostEIP2929 + jt[DELEGATECALL].constantGas = params.WarmStorageReadCostEIP2929 jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP2929 // This was previously part of the dynamic cost, but we're using it as a constantGas @@ -134,6 +135,15 @@ func enable2929(jt *JumpTable) { jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP2929 } +// enable3529 enabled "EIP-3529: Reduction in refunds": +// - Removes refunds for selfdestructs +// - Reduces refunds for SSTORE +// - Reduces max refunds to 20% gas +func enable3529(jt *JumpTable) { + jt[SSTORE].dynamicGas = gasSStoreEIP3529 + jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP3529 +} + // enable3198 applies EIP-3198 (BASEFEE Opcode) // - Adds an opcode that returns the current block's base fee. func enable3198(jt *JumpTable) { diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 46f3dc959d39..56f5dee91d3a 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -83,6 +83,7 @@ func validate(jt JumpTable) JumpTable { func newEip1559InstructionSet() JumpTable { instructionSet := newShanghaiInstructionSet() enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929 + enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529 return validate(instructionSet) } @@ -107,7 +108,6 @@ func newMergeInstructionSet() JumpTable { // constantinople, istanbul, petersburg, berlin and london instructions. func newLondonInstructionSet() JumpTable { instructionSet := newBerlinInstructionSet() - // enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529 enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198 return validate(instructionSet) } diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index c0d8ba67876e..dfef06285602 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -24,91 +24,75 @@ import ( "github.com/XinFinOrg/XDPoSChain/params" ) -const ( - ColdAccountAccessCostEIP2929 = uint64(2600) // COLD_ACCOUNT_ACCESS_COST - ColdSloadCostEIP2929 = uint64(2100) // COLD_SLOAD_COST - WarmStorageReadCostEIP2929 = uint64(100) // WARM_STORAGE_READ_COST -) - -// gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929" -// -// When calling SSTORE, check if the (address, storage_key) pair is in accessed_storage_keys. -// If it is not, charge an additional COLD_SLOAD_COST gas, and add the pair to accessed_storage_keys. -// Additionally, modify the parameters defined in EIP 2200 as follows: -// -// Parameter Old value New value -// SLOAD_GAS 800 = WARM_STORAGE_READ_COST -// SSTORE_RESET_GAS 5000 5000 - COLD_SLOAD_COST -// -// The other parameters defined in EIP 2200 are unchanged. -// see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified -func gasSStoreEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - // If we fail the minimum gas availability invariant, fail (0) - if contract.Gas <= params.SstoreSentryGasEIP2200 { - return 0, errors.New("not enough gas for reentrancy sentry") - } - // Gas sentry honoured, do the actual gas calculation based on the stored value - var ( - y, x = stack.Back(1), stack.peek() - slot = common.Hash(x.Bytes32()) - current = evm.StateDB.GetState(contract.Address(), slot) - cost = uint64(0) - ) - // Check slot presence in the access list - if addrPresent, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent { - cost = ColdSloadCostEIP2929 - // If the caller cannot afford the cost, this change will be rolled back - evm.StateDB.AddSlotToAccessList(contract.Address(), slot) - if !addrPresent { - // Once we're done with YOLOv2 and schedule this for mainnet, might - // be good to remove this panic here, which is just really a - // canary to have during testing - panic("impossible case: address was not present in access list during sstore op") +func makeGasSStoreFunc(clearingRefund uint64) gasFunc { + return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + // If we fail the minimum gas availability invariant, fail (0) + if contract.Gas <= params.SstoreSentryGasEIP2200 { + return 0, errors.New("not enough gas for reentrancy sentry") } - } - value := common.Hash(y.Bytes32()) + // Gas sentry honoured, do the actual gas calculation based on the stored value + var ( + y, x = stack.Back(1), stack.peek() + slot = common.Hash(x.Bytes32()) + current = evm.StateDB.GetState(contract.Address(), slot) + cost = uint64(0) + ) + // Check slot presence in the access list + if addrPresent, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent { + cost = params.ColdSloadCostEIP2929 + // If the caller cannot afford the cost, this change will be rolled back + evm.StateDB.AddSlotToAccessList(contract.Address(), slot) + if !addrPresent { + // Once we're done with YOLOv2 and schedule this for mainnet, might + // be good to remove this panic here, which is just really a + // canary to have during testing + panic("impossible case: address was not present in access list during sstore op") + } + } + value := common.Hash(y.Bytes32()) - if current == value { // noop (1) - // EIP 2200 original clause: - // return params.SloadGasEIP2200, nil - return cost + WarmStorageReadCostEIP2929, nil // SLOAD_GAS - } - original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32()) - if original == current { - if original == (common.Hash{}) { // create slot (2.1.1) - return cost + params.SstoreSetGasEIP2200, nil + if current == value { // noop (1) + // EIP 2200 original clause: + // return params.SloadGasEIP2200, nil + return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS } - if value == (common.Hash{}) { // delete slot (2.1.2b) - evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200) + original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32()) + if original == current { + if original == (common.Hash{}) { // create slot (2.1.1) + return cost + params.SstoreSetGasEIP2200, nil + } + if value == (common.Hash{}) { // delete slot (2.1.2b) + evm.StateDB.AddRefund(clearingRefund) + } + // EIP-2200 original clause: + // return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2) + return cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929), nil // write existing slot (2.1.2) } - // EIP-2200 original clause: - // return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2) - return cost + (params.SstoreResetGasEIP2200 - ColdSloadCostEIP2929), nil // write existing slot (2.1.2) - } - if original != (common.Hash{}) { - if current == (common.Hash{}) { // recreate slot (2.2.1.1) - evm.StateDB.SubRefund(params.SstoreClearsScheduleRefundEIP2200) - } else if value == (common.Hash{}) { // delete slot (2.2.1.2) - evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200) + if original != (common.Hash{}) { + if current == (common.Hash{}) { // recreate slot (2.2.1.1) + evm.StateDB.SubRefund(clearingRefund) + } else if value == (common.Hash{}) { // delete slot (2.2.1.2) + evm.StateDB.AddRefund(clearingRefund) + } } - } - if original == value { - if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1) - // EIP 2200 Original clause: - //evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200) - evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - WarmStorageReadCostEIP2929) - } else { // reset to original existing slot (2.2.2.2) - // EIP 2200 Original clause: - // evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200) - // - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST) - // - SLOAD_GAS redefined as WARM_STORAGE_READ_COST - // Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST - evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - ColdSloadCostEIP2929) - WarmStorageReadCostEIP2929) + if original == value { + if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1) + // EIP 2200 Original clause: + //evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200) + evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.WarmStorageReadCostEIP2929) + } else { // reset to original existing slot (2.2.2.2) + // EIP 2200 Original clause: + // evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200) + // - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST) + // - SLOAD_GAS redefined as WARM_STORAGE_READ_COST + // Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST + evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.WarmStorageReadCostEIP2929) + } } + // EIP-2200 original clause: + //return params.SloadGasEIP2200, nil // dirty update (2.2) + return cost + params.WarmStorageReadCostEIP2929, nil // dirty update (2.2) } - // EIP-2200 original clause: - //return params.SloadGasEIP2200, nil // dirty update (2.2) - return cost + WarmStorageReadCostEIP2929, nil // dirty update (2.2) } // gasSLoadEIP2929 calculates dynamic gas for SLOAD according to EIP-2929 @@ -124,9 +108,9 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me // If the caller cannot afford the cost, this change will be rolled back // If he does afford it, we can skip checking the same thing later on, during execution evm.StateDB.AddSlotToAccessList(contract.Address(), slot) - return ColdSloadCostEIP2929, nil + return params.ColdSloadCostEIP2929, nil } - return WarmStorageReadCostEIP2929, nil + return params.WarmStorageReadCostEIP2929, nil } // gasExtCodeCopyEIP2929 implements extcodecopy according to EIP-2929 @@ -146,7 +130,7 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo evm.StateDB.AddAddressToAccessList(addr) var overflow bool // We charge (cold-warm), since 'warm' is already charged as constantGas - if gas, overflow = math.SafeAdd(gas, ColdAccountAccessCostEIP2929-WarmStorageReadCostEIP2929); overflow { + if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow { return 0, ErrGasUintOverflow } return gas, nil @@ -168,7 +152,7 @@ func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Mem // If the caller cannot afford the cost, this change will be rolled back evm.StateDB.AddAddressToAccessList(addr) // The warm storage read cost is already charged as constantGas - return ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929, nil + return params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929, nil } return 0, nil } @@ -180,7 +164,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc { warmAccess := evm.StateDB.AddressInAccessList(addr) // The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so // the cost to charge for cold access, if any, is Cold - Warm - coldCost := ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929 + coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929 if !warmAccess { evm.StateDB.AddAddressToAccessList(addr) // Charge the remaining difference here already, to correctly calculate available @@ -212,24 +196,49 @@ var ( gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall) gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall) gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode) + gasSelfdestructEIP2929 = makeSelfdestructGasFn(true) + // gasSelfdestructEIP3529 implements the changes in EIP-3529 (no refunds) + gasSelfdestructEIP3529 = makeSelfdestructGasFn(false) + + // gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929 + // + // When calling SSTORE, check if the (address, storage_key) pair is in accessed_storage_keys. + // If it is not, charge an additional COLD_SLOAD_COST gas, and add the pair to accessed_storage_keys. + // Additionally, modify the parameters defined in EIP 2200 as follows: + // + // Parameter Old value New value + // SLOAD_GAS 800 = WARM_STORAGE_READ_COST + // SSTORE_RESET_GAS 5000 5000 - COLD_SLOAD_COST + // + //The other parameters defined in EIP 2200 are unchanged. + // see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified + gasSStoreEIP2929 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP2200) + + // gasSStoreEIP3529 implements gas cost for SSTORE according to EIP-3529 + // Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800) + gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529) ) -func gasSelfdestructEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - var ( - gas uint64 - address = common.Address(stack.peek().Bytes20()) - ) - if !evm.StateDB.AddressInAccessList(address) { - // If the caller cannot afford the cost, this change will be rolled back - evm.StateDB.AddAddressToAccessList(address) - gas = ColdAccountAccessCostEIP2929 - } - // if empty and transfers value - if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 { - gas += params.CreateBySelfdestructGas - } - if !evm.StateDB.HasSuicided(contract.Address()) { - evm.StateDB.AddRefund(params.SelfdestructRefundGas) +// makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-2539 +func makeSelfdestructGasFn(refundsEnabled bool) gasFunc { + gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + var ( + gas uint64 + address = common.Address(stack.peek().Bytes20()) + ) + if !evm.StateDB.AddressInAccessList(address) { + // If the caller cannot afford the cost, this change will be rolled back + evm.StateDB.AddAddressToAccessList(address) + gas = params.ColdAccountAccessCostEIP2929 + } + // if empty and transfers value + if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 { + gas += params.CreateBySelfdestructGas + } + if refundsEnabled && !evm.StateDB.HasSuicided(contract.Address()) { + evm.StateDB.AddRefund(params.SelfdestructRefundGas) + } + return gas, nil } - return gas, nil + return gasFunc } diff --git a/params/protocol_params.go b/params/protocol_params.go index d5fba6663eba..cb7080d37b4b 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -23,10 +23,10 @@ var ( ) const ( - GasLimitBoundDivisor uint64 = 1024 // The bound divisor of the gas limit, used in update calculations. - MinGasLimit uint64 = 5000 // Minimum the gas limit may ever be. + GasLimitBoundDivisor uint64 = 1024 // The bound divisor of the gas limit, used in update calculations. + MinGasLimit uint64 = 5000 // Minimum the gas limit may ever be. MaxGasLimit uint64 = 0x7fffffffffffffff // Maximum the gas limit (2^63-1). - GenesisGasLimit uint64 = 4712388 // Gas limit of the Genesis block. + GenesisGasLimit uint64 = 4712388 // Gas limit of the Genesis block. XDCGenesisGasLimit uint64 = 84000000 MaximumExtraDataSize uint64 = 32 // Maximum size extra data may be after Genesis. @@ -64,8 +64,8 @@ const ( TxAccessListAddressGas uint64 = 2400 // Per address specified in EIP 2930 access list TxAccessListStorageKeyGas uint64 = 1900 // Per storage key specified in EIP 2930 access list - - TxDataNonZeroGas uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. + + TxDataNonZeroGas uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. MaxCodeSize = 24576 // Maximum bytecode to permit for a contract @@ -83,6 +83,11 @@ const ( Bn256PairingBaseGas uint64 = 100000 // Base price for an elliptic curve pairing check Bn256PairingPerPointGas uint64 = 80000 // Per-point price for an elliptic curve pairing check XDCXPriceGas uint64 = 1 + + // The Refund Quotient is the cap on how much of the used gas can be refunded. Before EIP-3529, + // up to half the consumed gas could be refunded. Redefined as 1/5th in EIP-3529 + RefundQuotient uint64 = 2 + RefundQuotientEIP3529 uint64 = 5 ) var ( @@ -107,6 +112,16 @@ const ( SstoreResetGasEIP2200 uint64 = 5000 // Once per SSTORE operation from clean non-zero to something else SstoreClearsScheduleRefundEIP2200 uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot + ColdAccountAccessCostEIP2929 = uint64(2600) // COLD_ACCOUNT_ACCESS_COST + ColdSloadCostEIP2929 = uint64(2100) // COLD_SLOAD_COST + WarmStorageReadCostEIP2929 = uint64(100) // WARM_STORAGE_READ_COST + + // In EIP-2200: SstoreResetGas was 5000. + // In EIP-2929: SstoreResetGas was changed to '5000 - COLD_SLOAD_COST'. + // In EIP-3529: SSTORE_CLEARS_SCHEDULE is defined as SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST + // Which becomes: 5000 - 2100 + 1900 = 4800 + SstoreClearsScheduleRefundEIP3529 uint64 = SstoreResetGasEIP2200 - ColdSloadCostEIP2929 + TxAccessListStorageKeyGas + Create2Gas uint64 = 32000 // Once per CREATE2 operation SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation. TxDataNonZeroGasFrontier uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. From e18553b855d937c8808fccf59ad23822edd68dc7 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 25 Apr 2024 15:50:21 +0800 Subject: [PATCH 078/479] all: implement eip-1559 (#22837) --- accounts/abi/bind/backends/simulated.go | 2 + common/gas.go | 1 + consensus/XDPoS/engines/engine_v1/engine.go | 4 + consensus/XDPoS/engines/engine_v1/utils.go | 10 +- consensus/XDPoS/engines/engine_v2/utils.go | 9 +- .../XDPoS/engines/engine_v2/verifyHeader.go | 5 +- consensus/clique/clique.go | 8 +- consensus/ethash/consensus.go | 4 + consensus/misc/eip1559.go | 62 +++++++++++ core/blockchain_test.go | 7 +- core/chain_makers.go | 6 +- core/error.go | 4 + core/evm.go | 5 + core/genesis.go | 3 + core/state_processor.go | 11 +- core/state_transition.go | 38 +++++-- core/token_validator.go | 2 + core/types/access_list_tx.go | 2 + core/types/block.go | 14 +++ core/types/dynamic_fee_tx.go | 104 ++++++++++++++++++ core/types/legacy_tx.go | 2 + core/types/receipt.go | 6 +- core/types/transaction.go | 48 ++++++-- core/types/transaction_marshalling.go | 104 ++++++++++++++++-- core/types/transaction_signing.go | 76 ++++++++++++- core/vm/eips.go | 2 +- core/vm/evm.go | 1 + eth/api_tracer.go | 13 ++- eth/tracers/testing/calltrace_test.go | 4 +- eth/tracers/tracers_test.go | 6 +- interfaces.go | 6 +- internal/ethapi/api.go | 4 +- internal/ethapi/transaction_args.go | 2 +- les/odr_test.go | 4 +- light/odr_test.go | 2 +- params/protocol_params.go | 2 + tests/state_test_util.go | 2 +- 37 files changed, 507 insertions(+), 78 deletions(-) create mode 100644 consensus/misc/eip1559.go create mode 100644 core/types/dynamic_fee_tx.go diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index e43cae43775c..deb9023f52d4 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -525,6 +525,8 @@ func (m callMsg) Nonce() uint64 { return 0 } func (m callMsg) CheckNonce() bool { return false } func (m callMsg) To() *common.Address { return m.CallMsg.To } func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } +func (m callMsg) FeeCap() *big.Int { return m.CallMsg.FeeCap } +func (m callMsg) Tip() *big.Int { return m.CallMsg.Tip } func (m callMsg) Gas() uint64 { return m.CallMsg.Gas } func (m callMsg) Value() *big.Int { return m.CallMsg.Value } func (m callMsg) Data() []byte { return m.CallMsg.Data } diff --git a/common/gas.go b/common/gas.go index a5b5690a7691..1e40721b145a 100644 --- a/common/gas.go +++ b/common/gas.go @@ -4,6 +4,7 @@ import "math/big" var MinGasPrice50x = big.NewInt(12500000000) var GasPrice50x = big.NewInt(12500000000) +var BaseFee = big.NewInt(12500000000) func GetGasFee(blockNumber, gas uint64) *big.Int { fee := new(big.Int).SetUint64(gas) diff --git a/consensus/XDPoS/engines/engine_v1/engine.go b/consensus/XDPoS/engines/engine_v1/engine.go index ce968c76af34..61fca6fdb870 100644 --- a/consensus/XDPoS/engines/engine_v1/engine.go +++ b/consensus/XDPoS/engines/engine_v1/engine.go @@ -241,6 +241,10 @@ func (x *XDPoS_v1) verifyCascadingFields(chain consensus.ChainReader, header *ty if parent.Time.Uint64()+x.config.Period > header.Time.Uint64() { return utils.ErrInvalidTimestamp } + // Verify the header's EIP-1559 attributes. + if err := misc.VerifyEip1559Header(chain.Config(), header); err != nil { + return err + } if number%x.config.Epoch != 0 { return x.verifySeal(chain, header, parents, fullVerify) diff --git a/consensus/XDPoS/engines/engine_v1/utils.go b/consensus/XDPoS/engines/engine_v1/utils.go index a843cfa7a39e..13334fb7e16c 100644 --- a/consensus/XDPoS/engines/engine_v1/utils.go +++ b/consensus/XDPoS/engines/engine_v1/utils.go @@ -8,7 +8,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/crypto/sha3" - "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rlp" lru "github.com/hashicorp/golang-lru" @@ -62,7 +61,7 @@ func getM1M2(masternodes []common.Address, validators []int64, currentHeader *ty func sigHash(header *types.Header) (hash common.Hash) { hasher := sha3.NewKeccak256() - err := rlp.Encode(hasher, []interface{}{ + enc := []interface{}{ header.ParentHash, header.UncleHash, header.Coinbase, @@ -78,10 +77,11 @@ func sigHash(header *types.Header) (hash common.Hash) { header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short header.MixDigest, header.Nonce, - }) - if err != nil { - log.Debug("Fail to encode", err) } + if header.BaseFee != nil { + enc = append(enc, header.BaseFee) + } + rlp.Encode(hasher, enc) hasher.Sum(hash[:0]) return hash } diff --git a/consensus/XDPoS/engines/engine_v2/utils.go b/consensus/XDPoS/engines/engine_v2/utils.go index 2e3486156de4..9c99b636a9d4 100644 --- a/consensus/XDPoS/engines/engine_v2/utils.go +++ b/consensus/XDPoS/engines/engine_v2/utils.go @@ -20,7 +20,7 @@ import ( func sigHash(header *types.Header) (hash common.Hash) { hasher := sha3.NewKeccak256() - err := rlp.Encode(hasher, []interface{}{ + enc := []interface{}{ header.ParentHash, header.UncleHash, header.Coinbase, @@ -38,10 +38,11 @@ func sigHash(header *types.Header) (hash common.Hash) { header.Nonce, header.Validators, header.Penalties, - }) - if err != nil { - log.Debug("Fail to encode", err) } + if header.BaseFee != nil { + enc = append(enc, header.BaseFee) + } + rlp.Encode(hasher, enc) hasher.Sum(hash[:0]) return hash } diff --git a/consensus/XDPoS/engines/engine_v2/verifyHeader.go b/consensus/XDPoS/engines/engine_v2/verifyHeader.go index ca9aa20cefe9..becbe8c5c17e 100644 --- a/consensus/XDPoS/engines/engine_v2/verifyHeader.go +++ b/consensus/XDPoS/engines/engine_v2/verifyHeader.go @@ -94,7 +94,10 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade if header.UncleHash != utils.UncleHash { return utils.ErrInvalidUncleHash } - + // Verify the header's EIP-1559 attributes. + if err := misc.VerifyEip1559Header(chain.Config(), header); err != nil { + return err + } if header.Difficulty.Cmp(big.NewInt(1)) != 0 { return utils.ErrInvalidDifficulty } diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index e1bdcc16e957..fd3f56a958dd 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -147,7 +147,7 @@ type SignerFn func(accounts.Account, []byte) ([]byte, error) func sigHash(header *types.Header) (hash common.Hash) { hasher := sha3.NewKeccak256() - rlp.Encode(hasher, []interface{}{ + enc := []interface{}{ header.ParentHash, header.UncleHash, header.Coinbase, @@ -163,7 +163,11 @@ func sigHash(header *types.Header) (hash common.Hash) { header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short header.MixDigest, header.Nonce, - }) + } + if header.BaseFee != nil { + enc = append(enc, header.BaseFee) + } + rlp.Encode(hasher, enc) hasher.Sum(hash[:0]) return hash } diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index faa4c5230250..7b978a6da1c3 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -253,6 +253,10 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent * if header.GasUsed > header.GasLimit { return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit) } + // Verify the header's EIP-1559 attributes. + if err := misc.VerifyEip1559Header(chain.Config(), header); err != nil { + return err + } // Verify that the gas limit remains within allowed bounds diff := int64(parent.GasLimit) - int64(header.GasLimit) diff --git a/consensus/misc/eip1559.go b/consensus/misc/eip1559.go new file mode 100644 index 000000000000..c993d97062a2 --- /dev/null +++ b/consensus/misc/eip1559.go @@ -0,0 +1,62 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package misc + +import ( + "fmt" + "math/big" + + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/params" +) + +// VerifyEip1559Header verifies some header attributes which were changed in EIP-1559, +// - gas limit check +// - basefee check +func VerifyEip1559Header(config *params.ChainConfig, header *types.Header) error { + if !config.IsEIP1559(header.Number) { + if header.BaseFee != nil { + return fmt.Errorf("invalid baseFee: have %s, want ", + header.BaseFee) + } + return nil + } + + // Verify the header is not malformed + if header.BaseFee == nil { + return fmt.Errorf("header is missing baseFee") + } + + // Verify the baseFee is correct based on the current header. + expectedBaseFee := CalcBaseFee(config, header) + if header.BaseFee.Cmp(expectedBaseFee) != 0 { + return fmt.Errorf("invalid baseFee: have %s, want %s", + header.BaseFee, expectedBaseFee) + } + return nil +} + +// CalcBaseFee calculates the basefee of the header. +func CalcBaseFee(config *params.ChainConfig, header *types.Header) *big.Int { + // If the current block is the first EIP-1559 block, return the InitialBaseFee. + if config.IsEIP1559(header.Number) { + return new(big.Int).Set(common.BaseFee) + } else { + return nil + } +} diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 7d6b676ed64d..75903c4b6469 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -18,6 +18,7 @@ package core import ( "fmt" + "math" "math/big" "math/rand" "sync" @@ -1431,7 +1432,7 @@ func TestEIP2718Transition(t *testing.T) { // A sender who makes transactions, has some funds key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") address = crypto.PubkeyToAddress(key.PublicKey) - funds = big.NewInt(1000000000) + funds = big.NewInt(math.MaxInt64) gspec = &Genesis{ Config: ¶ms.ChainConfig{ ChainId: new(big.Int).SetBytes([]byte("eip1559")), @@ -1458,7 +1459,7 @@ func TestEIP2718Transition(t *testing.T) { byte(vm.SLOAD), }, Nonce: 0, - Balance: big.NewInt(0), + Balance: big.NewInt(50000000000), }, }, } @@ -1475,7 +1476,7 @@ func TestEIP2718Transition(t *testing.T) { Nonce: 0, To: &aa, Gas: 30000, - GasPrice: big.NewInt(1), + GasPrice: new(big.Int).Set(common.BaseFee), AccessList: types.AccessList{{ Address: aa, StorageKeys: []common.Hash{{0}}, diff --git a/core/chain_makers.go b/core/chain_makers.go index 2df2366fbf15..cd8ff5443dce 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -265,7 +265,7 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S time = new(big.Int).Add(parent.Time(), big.NewInt(10)) // block time is fixed at 10 seconds } - return &types.Header{ + header := &types.Header{ Root: state.IntermediateRoot(chain.Config().IsEIP158(parent.Number())), ParentHash: parent.Hash(), Coinbase: parent.Coinbase(), @@ -279,6 +279,10 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S Number: new(big.Int).Add(parent.Number(), common.Big1), Time: time, } + + header.BaseFee = misc.CalcBaseFee(chain.Config(), header) + + return header } // newCanonical creates a chain database, and injects a deterministic canonical diff --git a/core/error.go b/core/error.go index 5503a6e6f2ef..fd45fdc73d3a 100644 --- a/core/error.go +++ b/core/error.go @@ -53,4 +53,8 @@ var ( // ErrGasUintOverflow is returned when calculating gas usage. ErrGasUintOverflow = errors.New("gas uint64 overflow") + + // ErrFeeCapTooLow is returned if the transaction fee cap is less than the + // the base fee of the block. + ErrFeeCapTooLow = errors.New("fee cap less than block base fee") ) diff --git a/core/evm.go b/core/evm.go index 48d0aca12239..9847a8d790b1 100644 --- a/core/evm.go +++ b/core/evm.go @@ -31,6 +31,7 @@ func NewEVMBlockContext(header *types.Header, chain consensus.ChainContext, auth // If we don't have an explicit author (i.e. not mining), extract from the header var ( beneficiary common.Address + baseFee *big.Int random common.Hash ) if author == nil { @@ -38,6 +39,9 @@ func NewEVMBlockContext(header *types.Header, chain consensus.ChainContext, auth } else { beneficiary = *author } + if header.BaseFee != nil { + baseFee = new(big.Int).Set(header.BaseFee) + } // since xdpos chain do not use difficulty and mixdigest, we use hash of the block number as random random = crypto.Keccak256Hash(header.Number.Bytes()) return vm.BlockContext{ @@ -48,6 +52,7 @@ func NewEVMBlockContext(header *types.Header, chain consensus.ChainContext, auth BlockNumber: new(big.Int).Set(header.Number), Time: new(big.Int).Set(header.Time), Difficulty: new(big.Int).Set(header.Difficulty), + BaseFee: baseFee, GasLimit: header.GasLimit, Random: &random, } diff --git a/core/genesis.go b/core/genesis.go index 2156ddc7259d..c45ec967a99c 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -264,6 +264,9 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { if g.Difficulty == nil { head.Difficulty = params.GenesisDifficulty } + if g.Config != nil && g.Config.IsEIP1559(common.Big0) { + head.BaseFee = new(big.Int).SetUint64(params.InitialBaseFee) + } statedb.Commit(false) statedb.Database().TrieDB().Commit(root, true) diff --git a/core/state_processor.go b/core/state_processor.go index f1995a79c71a..3779b13c8079 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -118,7 +118,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra } } statedb.Prepare(tx.Hash(), i) - receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, gp, statedb, coinbaseOwner, blockNumber, blockHash, tx, usedGas, vmenv) + receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, gp, statedb, coinbaseOwner, blockNumber, header.BaseFee, blockHash, tx, usedGas, vmenv) if err != nil { return nil, nil, 0, err } @@ -198,7 +198,7 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated } } statedb.Prepare(tx.Hash(), i) - receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, gp, statedb, coinbaseOwner, blockNumber, blockHash, tx, usedGas, vmenv) + receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, gp, statedb, coinbaseOwner, blockNumber, header.BaseFee, blockHash, tx, usedGas, vmenv) if err != nil { return nil, nil, 0, err } @@ -220,7 +220,7 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated return receipts, allLogs, *usedGas, nil } -func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*big.Int, gp *GasPool, statedb *state.StateDB, coinbaseOwner common.Address, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, uint64, error, bool) { +func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*big.Int, gp *GasPool, statedb *state.StateDB, coinbaseOwner common.Address, blockNumber, baseFee *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, uint64, error, bool) { to := tx.To() if to != nil { if *to == common.BlockSignersBinary && config.IsTIPSigning(blockNumber) { @@ -246,7 +246,8 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* balanceFee = value } } - msg, err := tx.AsMessage(types.MakeSigner(config, blockNumber), balanceFee, blockNumber) + // msg, err := tx.AsMessage(types.MakeSigner(config, blockNumber), balanceFee, blockNumber) + msg, err := tx.AsMessage(types.MakeSigner(config, blockNumber), balanceFee, blockNumber, baseFee) if err != nil { return nil, 0, err, false } @@ -465,7 +466,7 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* blockContext := NewEVMBlockContext(header, bc, author) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, XDCxState, config, cfg) coinbaseOwner := getCoinbaseOwner(bc, statedb, header, author) - return applyTransaction(config, tokensFee, gp, statedb, coinbaseOwner, header.Number, header.Hash(), tx, usedGas, vmenv) + return applyTransaction(config, tokensFee, gp, statedb, coinbaseOwner, header.Number, header.BaseFee, header.Hash(), tx, usedGas, vmenv) } func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, error, bool) { diff --git a/core/state_transition.go b/core/state_transition.go index 92bbfcd41297..d1a1db534c4f 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -23,6 +23,7 @@ import ( "math/big" "github.com/XinFinOrg/XDPoSChain/common" + cmath "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/log" @@ -57,6 +58,8 @@ type StateTransition struct { msg Message gas uint64 gasPrice *big.Int + feeCap *big.Int + tip *big.Int initialGas uint64 value *big.Int data []byte @@ -71,6 +74,8 @@ type Message interface { To() *common.Address GasPrice() *big.Int + FeeCap() *big.Int + Tip() *big.Int Gas() uint64 Value() *big.Int @@ -125,6 +130,8 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition evm: evm, msg: msg, gasPrice: msg.GasPrice(), + feeCap: msg.FeeCap(), + tip: msg.Tip(), value: msg.Value(), data: msg.Data(), state: evm.StateDB, @@ -197,19 +204,28 @@ func (st *StateTransition) buyGas() error { } func (st *StateTransition) preCheck() error { - // Make sure this transaction's nonce is correct - if st.msg.CheckNonce() { - // Make sure this transaction's nonce is correct. - stNonce := st.state.GetNonce(st.from().Address()) - if msgNonce := st.msg.Nonce(); stNonce < msgNonce { + // Make sure this transaction's nonce is correct. + msg := st.msg + if msg.CheckNonce() { + stNonce := st.state.GetNonce(msg.From()) + if msgNonce := msg.Nonce(); stNonce < msgNonce { return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooHigh, - st.msg.From().Hex(), msgNonce, stNonce) + msg.From().Hex(), msgNonce, stNonce) } else if stNonce > msgNonce { return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooLow, - st.msg.From().Hex(), msgNonce, stNonce) + msg.From().Hex(), msgNonce, stNonce) } else if stNonce+1 < stNonce { return fmt.Errorf("%w: address %v, nonce: %d", ErrNonceMax, - st.msg.From().Hex(), stNonce) + msg.From().Hex(), stNonce) + } + } + // Make sure that transaction feeCap is greater than the baseFee (post london) + if st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber) { + // This will panic if baseFee is nil, but basefee presence is verified + // as part of header validation. + if st.feeCap.Cmp(st.evm.Context.BaseFee) < 0 { + return fmt.Errorf("%w: address %v, feeCap: %s baseFee: %s", ErrFeeCapTooLow, + msg.From().Hex(), st.feeCap, st.evm.Context.BaseFee) } } return st.buyGas() @@ -307,7 +323,11 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG st.state.AddBalance(owner, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice)) } } else { - st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice)) + effectiveTip := st.gasPrice + if st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber) { + effectiveTip = cmath.BigMin(st.tip, new(big.Int).Sub(st.feeCap, st.evm.Context.BaseFee)) + } + st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip)) } return ret, st.gasUsed(), vmerr != nil, nil, vmerr diff --git a/core/token_validator.go b/core/token_validator.go index 59a4faa5a432..dae490946553 100644 --- a/core/token_validator.go +++ b/core/token_validator.go @@ -48,6 +48,8 @@ func (m callMsg) Nonce() uint64 { return 0 } func (m callMsg) CheckNonce() bool { return false } func (m callMsg) To() *common.Address { return m.CallMsg.To } func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } +func (m callMsg) FeeCap() *big.Int { return m.CallMsg.FeeCap } +func (m callMsg) Tip() *big.Int { return m.CallMsg.Tip } func (m callMsg) Gas() uint64 { return m.CallMsg.Gas } func (m callMsg) Value() *big.Int { return m.CallMsg.Value } func (m callMsg) Data() []byte { return m.CallMsg.Data } diff --git a/core/types/access_list_tx.go b/core/types/access_list_tx.go index f80044e108fa..2131a743f113 100644 --- a/core/types/access_list_tx.go +++ b/core/types/access_list_tx.go @@ -102,6 +102,8 @@ func (tx *AccessListTx) accessList() AccessList { return tx.AccessList } func (tx *AccessListTx) data() []byte { return tx.Data } func (tx *AccessListTx) gas() uint64 { return tx.Gas } func (tx *AccessListTx) gasPrice() *big.Int { return tx.GasPrice } +func (tx *AccessListTx) tip() *big.Int { return tx.GasPrice } +func (tx *AccessListTx) feeCap() *big.Int { return tx.GasPrice } func (tx *AccessListTx) value() *big.Int { return tx.Value } func (tx *AccessListTx) nonce() uint64 { return tx.Nonce } func (tx *AccessListTx) to() *common.Address { return tx.To } diff --git a/core/types/block.go b/core/types/block.go index 4f973e79c308..aad990880fbd 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -81,6 +81,9 @@ type Header struct { Validators []byte `json:"validators" gencodec:"required"` Validator []byte `json:"validator" gencodec:"required"` Penalties []byte `json:"penalties" gencodec:"required"` + + // BaseFee was added by EIP-1559 and is ignored in legacy headers. + BaseFee *big.Int `json:"baseFee" rlp:"optional"` } // field type overrides for gencodec @@ -91,6 +94,7 @@ type headerMarshaling struct { GasUsed hexutil.Uint64 Time *hexutil.Big Extra hexutil.Bytes + BaseFee *hexutil.Big Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON } @@ -264,6 +268,9 @@ func CopyHeader(h *Header) *Header { if cpy.Number = new(big.Int); h.Number != nil { cpy.Number.Set(h.Number) } + if h.BaseFee != nil { + cpy.BaseFee = new(big.Int).Set(h.BaseFee) + } if len(h.Extra) > 0 { cpy.Extra = make([]byte, len(h.Extra)) copy(cpy.Extra, h.Extra) @@ -340,6 +347,13 @@ func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Ext func (b *Block) Penalties() []byte { return common.CopyBytes(b.header.Penalties) } func (b *Block) Validator() []byte { return common.CopyBytes(b.header.Validator) } +func (b *Block) BaseFee() *big.Int { + if b.header.BaseFee == nil { + return nil + } + return new(big.Int).Set(b.header.BaseFee) +} + func (b *Block) Header() *Header { return CopyHeader(b.header) } // Body returns the non-header content of the block. diff --git a/core/types/dynamic_fee_tx.go b/core/types/dynamic_fee_tx.go new file mode 100644 index 000000000000..ef4c66f2f58c --- /dev/null +++ b/core/types/dynamic_fee_tx.go @@ -0,0 +1,104 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "math/big" + + "github.com/XinFinOrg/XDPoSChain/common" +) + +type DynamicFeeTx struct { + ChainID *big.Int + Nonce uint64 + Tip *big.Int + FeeCap *big.Int + Gas uint64 + To *common.Address `rlp:"nil"` // nil means contract creation + Value *big.Int + Data []byte + AccessList AccessList + + // Signature values + V *big.Int `json:"v" gencodec:"required"` + R *big.Int `json:"r" gencodec:"required"` + S *big.Int `json:"s" gencodec:"required"` +} + +// copy creates a deep copy of the transaction data and initializes all fields. +func (tx *DynamicFeeTx) copy() TxData { + cpy := &DynamicFeeTx{ + Nonce: tx.Nonce, + To: tx.To, // TODO: copy pointed-to address + Data: common.CopyBytes(tx.Data), + Gas: tx.Gas, + // These are copied below. + AccessList: make(AccessList, len(tx.AccessList)), + Value: new(big.Int), + ChainID: new(big.Int), + Tip: new(big.Int), + FeeCap: new(big.Int), + V: new(big.Int), + R: new(big.Int), + S: new(big.Int), + } + copy(cpy.AccessList, tx.AccessList) + if tx.Value != nil { + cpy.Value.Set(tx.Value) + } + if tx.ChainID != nil { + cpy.ChainID.Set(tx.ChainID) + } + if tx.Tip != nil { + cpy.Tip.Set(tx.Tip) + } + if tx.FeeCap != nil { + cpy.FeeCap.Set(tx.FeeCap) + } + if tx.V != nil { + cpy.V.Set(tx.V) + } + if tx.R != nil { + cpy.R.Set(tx.R) + } + if tx.S != nil { + cpy.S.Set(tx.S) + } + return cpy +} + +// accessors for innerTx. +func (tx *DynamicFeeTx) txType() byte { return DynamicFeeTxType } +func (tx *DynamicFeeTx) chainID() *big.Int { return tx.ChainID } +func (tx *DynamicFeeTx) protected() bool { return true } +func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList } +func (tx *DynamicFeeTx) data() []byte { return tx.Data } +func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas } +func (tx *DynamicFeeTx) feeCap() *big.Int { return tx.FeeCap } +func (tx *DynamicFeeTx) tip() *big.Int { return tx.Tip } +func (tx *DynamicFeeTx) gasPrice() *big.Int { return tx.FeeCap } +func (tx *DynamicFeeTx) value() *big.Int { return tx.Value } +func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce } +func (tx *DynamicFeeTx) to() *common.Address { return tx.To } + +func (tx *DynamicFeeTx) rawSignatureValues() (v, r, s *big.Int) { + return tx.V, tx.R, tx.S +} + +func (tx *DynamicFeeTx) setSignatureValues(chainID, v, r, s *big.Int) { + tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s +} diff --git a/core/types/legacy_tx.go b/core/types/legacy_tx.go index 146a1e2877e9..5593f87b3100 100644 --- a/core/types/legacy_tx.go +++ b/core/types/legacy_tx.go @@ -98,6 +98,8 @@ func (tx *LegacyTx) accessList() AccessList { return nil } func (tx *LegacyTx) data() []byte { return tx.Data } func (tx *LegacyTx) gas() uint64 { return tx.Gas } func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice } +func (tx *LegacyTx) tip() *big.Int { return tx.GasPrice } +func (tx *LegacyTx) feeCap() *big.Int { return tx.GasPrice } func (tx *LegacyTx) value() *big.Int { return tx.Value } func (tx *LegacyTx) nonce() uint64 { return tx.Nonce } func (tx *LegacyTx) to() *common.Address { return tx.To } diff --git a/core/types/receipt.go b/core/types/receipt.go index 9ac2616fe20d..ce6af8c2f68e 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -124,10 +124,6 @@ func (r *Receipt) EncodeRLP(w io.Writer) error { if r.Type == LegacyTxType { return rlp.Encode(w, data) } - // It's an EIP-2718 typed TX receipt. - if r.Type != AccessListTxType { - return ErrTxTypeNotSupported - } buf := encodeBufferPool.Get().(*bytes.Buffer) defer encodeBufferPool.Put(buf) buf.Reset() @@ -163,7 +159,7 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error { return errEmptyTypedReceipt } r.Type = b[0] - if r.Type == AccessListTxType { + if r.Type == AccessListTxType || r.Type == DynamicFeeTxType { var dec receiptRLP if err := rlp.DecodeBytes(b[1:], &dec); err != nil { return err diff --git a/core/types/transaction.go b/core/types/transaction.go index 9549af34027d..c5f73db9ae11 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -27,6 +27,7 @@ import ( "time" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/rlp" ) @@ -56,6 +57,7 @@ var ( const ( LegacyTxType = iota AccessListTxType + DynamicFeeTxType ) // Transaction is an Ethereum transaction. @@ -88,6 +90,8 @@ type TxData interface { data() []byte gas() uint64 gasPrice() *big.Int + tip() *big.Int + feeCap() *big.Int value() *big.Int nonce() uint64 to() *common.Address @@ -191,6 +195,10 @@ func (tx *Transaction) decodeTyped(b []byte) (TxData, error) { var inner AccessListTx err := rlp.DecodeBytes(b[1:], &inner) return &inner, err + case DynamicFeeTxType: + var inner DynamicFeeTx + err := rlp.DecodeBytes(b[1:], &inner) + return &inner, err default: return nil, ErrTxTypeNotSupported } @@ -274,6 +282,12 @@ func (tx *Transaction) Gas() uint64 { return tx.inner.gas() } // GasPrice returns the gas price of the transaction. func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.inner.gasPrice()) } +// Tip returns the tip per gas of the transaction. +func (tx *Transaction) Tip() *big.Int { return new(big.Int).Set(tx.inner.tip()) } + +// FeeCap returns the fee cap per gas of the transaction. +func (tx *Transaction) FeeCap() *big.Int { return new(big.Int).Set(tx.inner.feeCap()) } + // Value returns the ether amount of the transaction. func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.inner.value()) } @@ -351,11 +365,13 @@ func (tx *Transaction) Size() common.StorageSize { } // AsMessage returns the transaction as a core.Message. -func (tx *Transaction) AsMessage(s Signer, balanceFee *big.Int, number *big.Int) (Message, error) { +func (tx *Transaction) AsMessage(s Signer, balanceFee, blockNumber, baseFee *big.Int) (Message, error) { msg := Message{ nonce: tx.Nonce(), gasLimit: tx.Gas(), gasPrice: new(big.Int).Set(tx.GasPrice()), + feeCap: new(big.Int).Set(tx.FeeCap()), + tip: new(big.Int).Set(tx.Tip()), to: tx.To(), amount: tx.Value(), data: tx.Data(), @@ -364,17 +380,23 @@ func (tx *Transaction) AsMessage(s Signer, balanceFee *big.Int, number *big.Int) balanceTokenFee: balanceFee, } - var err error - msg.from, err = Sender(s, tx) if balanceFee != nil { - if number.Cmp(common.BlockNumberGas50x) >= 0 { - msg.gasPrice = common.GasPrice50x - } else if number.Cmp(common.TIPTRC21Fee) > 0 { - msg.gasPrice = common.TRC21GasPrice - } else { - msg.gasPrice = common.TRC21GasPriceBefore + if blockNumber != nil { + if blockNumber.Cmp(common.BlockNumberGas50x) >= 0 { + msg.gasPrice = common.GasPrice50x + } else if blockNumber.Cmp(common.TIPTRC21Fee) > 0 { + msg.gasPrice = common.TRC21GasPrice + } else { + msg.gasPrice = common.TRC21GasPriceBefore + } } + } else if baseFee != nil { + // If baseFee provided, set gasPrice to effectiveGasPrice. + msg.gasPrice = math.BigMin(msg.gasPrice.Add(msg.tip, baseFee), msg.feeCap) } + + var err error + msg.from, err = Sender(s, tx) return msg, err } @@ -742,13 +764,15 @@ type Message struct { amount *big.Int gasLimit uint64 gasPrice *big.Int + feeCap *big.Int + tip *big.Int data []byte accessList AccessList checkNonce bool balanceTokenFee *big.Int } -func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, accessList AccessList, checkNonce bool, balanceTokenFee *big.Int, number *big.Int) Message { +func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice, feeCap, tip *big.Int, data []byte, accessList AccessList, checkNonce bool, balanceTokenFee *big.Int, number *big.Int) Message { if balanceTokenFee != nil { gasPrice = common.GetGasPrice(number) } @@ -759,6 +783,8 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *b amount: amount, gasLimit: gasLimit, gasPrice: gasPrice, + feeCap: feeCap, + tip: tip, data: data, accessList: accessList, checkNonce: checkNonce, @@ -770,6 +796,8 @@ func (m Message) From() common.Address { return m.from } func (m Message) BalanceTokenFee() *big.Int { return m.balanceTokenFee } func (m Message) To() *common.Address { return m.to } func (m Message) GasPrice() *big.Int { return m.gasPrice } +func (m Message) FeeCap() *big.Int { return m.feeCap } +func (m Message) Tip() *big.Int { return m.tip } func (m Message) Value() *big.Int { return m.amount } func (m Message) Gas() uint64 { return m.gasLimit } func (m Message) Nonce() uint64 { return m.nonce } diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go index 91403994bf7e..f4a47c7ee640 100644 --- a/core/types/transaction_marshalling.go +++ b/core/types/transaction_marshalling.go @@ -14,15 +14,19 @@ type txJSON struct { Type hexutil.Uint64 `json:"type"` // Common transaction fields: - Nonce *hexutil.Uint64 `json:"nonce"` - GasPrice *hexutil.Big `json:"gasPrice"` - Gas *hexutil.Uint64 `json:"gas"` - Value *hexutil.Big `json:"value"` - Data *hexutil.Bytes `json:"input"` - V *hexutil.Big `json:"v"` - R *hexutil.Big `json:"r"` - S *hexutil.Big `json:"s"` - To *common.Address `json:"to"` + Nonce *hexutil.Uint64 `json:"nonce"` + GasPrice *hexutil.Big `json:"gasPrice"` + FeeCap *hexutil.Big `json:"feeCap"` + Tip *hexutil.Big `json:"tip"` + MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"` + MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"` + Gas *hexutil.Uint64 `json:"gas"` + Value *hexutil.Big `json:"value"` + Data *hexutil.Bytes `json:"input"` + V *hexutil.Big `json:"v"` + R *hexutil.Big `json:"r"` + S *hexutil.Big `json:"s"` + To *common.Address `json:"to"` // Access list transaction fields: ChainID *hexutil.Big `json:"chainId,omitempty"` @@ -63,6 +67,19 @@ func (t *Transaction) MarshalJSON() ([]byte, error) { enc.V = (*hexutil.Big)(tx.V) enc.R = (*hexutil.Big)(tx.R) enc.S = (*hexutil.Big)(tx.S) + case *DynamicFeeTx: + enc.ChainID = (*hexutil.Big)(tx.ChainID) + enc.AccessList = &tx.AccessList + enc.Nonce = (*hexutil.Uint64)(&tx.Nonce) + enc.Gas = (*hexutil.Uint64)(&tx.Gas) + enc.FeeCap = (*hexutil.Big)(tx.FeeCap) + enc.Tip = (*hexutil.Big)(tx.Tip) + enc.Value = (*hexutil.Big)(tx.Value) + enc.Data = (*hexutil.Bytes)(&tx.Data) + enc.To = t.To() + enc.V = (*hexutil.Big)(tx.V) + enc.R = (*hexutil.Big)(tx.R) + enc.S = (*hexutil.Big)(tx.S) } return json.Marshal(&enc) } @@ -175,6 +192,75 @@ func (t *Transaction) UnmarshalJSON(input []byte) error { } } + case DynamicFeeTxType: + var itx DynamicFeeTx + inner = &itx + // Access list is optional for now. + if dec.AccessList != nil { + itx.AccessList = *dec.AccessList + } + if dec.ChainID == nil { + return errors.New("missing required field 'chainId' in transaction") + } + itx.ChainID = (*big.Int)(dec.ChainID) + if dec.To != nil { + itx.To = dec.To + } + if dec.Nonce == nil { + return errors.New("missing required field 'nonce' in transaction") + } + itx.Nonce = uint64(*dec.Nonce) + switch { + case dec.Tip == nil && dec.MaxPriorityFeePerGas == nil: + return errors.New("at least one of 'tip' or 'maxPriorityFeePerGas' must be defined") + case dec.Tip != nil && dec.MaxPriorityFeePerGas != nil: + return errors.New("only one of 'tip' or 'maxPriorityFeePerGas' may be defined") + case dec.Tip != nil && dec.MaxPriorityFeePerGas == nil: + itx.Tip = (*big.Int)(dec.Tip) + case dec.Tip == nil && dec.MaxPriorityFeePerGas != nil: + itx.Tip = (*big.Int)(dec.MaxPriorityFeePerGas) + } + switch { + case dec.FeeCap == nil && dec.MaxFeePerGas == nil: + return errors.New("at least one of 'feeCap' or 'maxFeePerGas' must be defined") + case dec.FeeCap != nil && dec.MaxFeePerGas != nil: + return errors.New("only one of 'feeCap' or 'maxFeePerGas' may be defined") + case dec.FeeCap != nil && dec.MaxFeePerGas == nil: + itx.FeeCap = (*big.Int)(dec.FeeCap) + case dec.FeeCap == nil && dec.MaxFeePerGas != nil: + itx.FeeCap = (*big.Int)(dec.MaxFeePerGas) + } + if dec.Gas == nil { + return errors.New("missing required field 'gas' for txdata") + } + itx.Gas = uint64(*dec.Gas) + if dec.Value == nil { + return errors.New("missing required field 'value' in transaction") + } + itx.Value = (*big.Int)(dec.Value) + if dec.Data == nil { + return errors.New("missing required field 'input' in transaction") + } + itx.Data = *dec.Data + if dec.V == nil { + return errors.New("missing required field 'v' in transaction") + } + itx.V = (*big.Int)(dec.V) + if dec.R == nil { + return errors.New("missing required field 'r' in transaction") + } + itx.R = (*big.Int)(dec.R) + if dec.S == nil { + return errors.New("missing required field 's' in transaction") + } + itx.S = (*big.Int)(dec.S) + withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0 + if withSignature { + if err := sanityCheckSignature(itx.V, itx.R, itx.S, false); err != nil { + return err + } + } + default: return ErrTxTypeNotSupported } diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index f4174dae4858..2715e718f98d 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -42,7 +42,7 @@ func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer { var signer Signer switch { case config.IsEIP1559(blockNumber): - signer = NewEIP2930Signer(config.ChainId) + signer = NewLondonSigner(config.ChainId) case config.IsEIP155(blockNumber): signer = NewEIP155Signer(config.ChainId) case config.IsHomestead(blockNumber): @@ -63,7 +63,7 @@ func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer { func LatestSigner(config *params.ChainConfig) Signer { if config.ChainId != nil { if common.Eip1559Block.Uint64() != 9999999999 || config.Eip1559Block != nil { - return NewEIP2930Signer(config.ChainId) + return NewLondonSigner(config.ChainId) } if config.EIP155Block != nil { return NewEIP155Signer(config.ChainId) @@ -83,7 +83,7 @@ func LatestSignerForChainID(chainID *big.Int) Signer { if chainID == nil { return HomesteadSigner{} } - return NewEIP2930Signer(chainID) + return NewLondonSigner(chainID) } // SignTx signs the transaction using the given signer and private key. @@ -170,6 +170,72 @@ type Signer interface { Equal(Signer) bool } +type londonSigner struct{ eip2930Signer } + +// NewLondonSigner returns a signer that accepts +// - EIP-1559 dynamic fee transactions +// - EIP-2930 access list transactions, +// - EIP-155 replay protected transactions, and +// - legacy Homestead transactions. +func NewLondonSigner(chainId *big.Int) Signer { + return londonSigner{eip2930Signer{NewEIP155Signer(chainId)}} +} + +func (s londonSigner) Sender(tx *Transaction) (common.Address, error) { + if tx.Type() != DynamicFeeTxType { + return s.eip2930Signer.Sender(tx) + } + V, R, S := tx.RawSignatureValues() + // DynamicFee txs are defined to use 0 and 1 as their recovery + // id, add 27 to become equivalent to unprotected Homestead signatures. + V = new(big.Int).Add(V, big.NewInt(27)) + if tx.ChainId().Cmp(s.chainId) != 0 { + return common.Address{}, ErrInvalidChainId + } + return recoverPlain(s.Hash(tx), R, S, V, true) +} + +func (s londonSigner) Equal(s2 Signer) bool { + x, ok := s2.(londonSigner) + return ok && x.chainId.Cmp(s.chainId) == 0 +} + +func (s londonSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) { + txdata, ok := tx.inner.(*DynamicFeeTx) + if !ok { + return s.eip2930Signer.SignatureValues(tx, sig) + } + // Check that chain ID of tx matches the signer. We also accept ID zero here, + // because it indicates that the chain ID was not specified in the tx. + if txdata.ChainID.Sign() != 0 && txdata.ChainID.Cmp(s.chainId) != 0 { + return nil, nil, nil, ErrInvalidChainId + } + R, S, _ = decodeSignature(sig) + V = big.NewInt(int64(sig[64])) + return R, S, V, nil +} + +// Hash returns the hash to be signed by the sender. +// It does not uniquely identify the transaction. +func (s londonSigner) Hash(tx *Transaction) common.Hash { + if tx.Type() != DynamicFeeTxType { + return s.eip2930Signer.Hash(tx) + } + return prefixedRlpHash( + tx.Type(), + []interface{}{ + s.chainId, + tx.Nonce(), + tx.Tip(), + tx.FeeCap(), + tx.Gas(), + tx.To(), + tx.Value(), + tx.Data(), + tx.AccessList(), + }) +} + type eip2930Signer struct{ EIP155Signer } // NewEIP2930Signer returns a signer that accepts EIP-2930 access list transactions, @@ -193,8 +259,8 @@ func (s eip2930Signer) Sender(tx *Transaction) (common.Address, error) { case LegacyTxType: return s.EIP155Signer.Sender(tx) case AccessListTxType: - // ACL txs are defined to use 0 and 1 as their recovery id, add - // 27 to become equivalent to unprotected Homestead signatures. + // AL txs are defined to use 0 and 1 as their recovery + // id, add 27 to become equivalent to unprotected Homestead signatures. V = new(big.Int).Add(V, big.NewInt(27)) default: return common.Address{}, ErrTxTypeNotSupported diff --git a/core/vm/eips.go b/core/vm/eips.go index 19ea9a8980ff..2c4641d699d3 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -158,7 +158,7 @@ func enable3198(jt *JumpTable) { // opBaseFee implements BASEFEE opcode func opBaseFee(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) { - baseFee, _ := uint256.FromBig(common.MinGasPrice50x) + baseFee, _ := uint256.FromBig(common.BaseFee) callContext.Stack.push(baseFee) return nil, nil } diff --git a/core/vm/evm.go b/core/vm/evm.go index c06706a44c8a..c14ce7c03eac 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -101,6 +101,7 @@ type BlockContext struct { BlockNumber *big.Int // Provides information for NUMBER Time *big.Int // Provides information for TIME Difficulty *big.Int // Provides information for DIFFICULTY + BaseFee *big.Int // Provides information for BASEFEE Random *common.Hash // Provides information for PREVRANDAO } diff --git a/eth/api_tracer.go b/eth/api_tracer.go index 3b9f0e56ab8e..bdb3ed823c6f 100644 --- a/eth/api_tracer.go +++ b/eth/api_tracer.go @@ -28,7 +28,6 @@ import ( "time" "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate" - "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/hexutil" "github.com/XinFinOrg/XDPoSChain/core" @@ -242,7 +241,8 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl balance = value } } - msg, _ := tx.AsMessage(signer, balance, task.block.Number()) + header := task.block.Header() + msg, _ := tx.AsMessage(signer, balance, header.Number, header.BaseFee) txctx := &tracers.Context{ BlockHash: task.block.Hash(), TxIndex: i, @@ -486,7 +486,8 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, balance = value } } - msg, _ := txs[task.index].AsMessage(signer, balance, block.Number()) + header := block.Header() + msg, _ := txs[task.index].AsMessage(signer, balance, header.Number, header.BaseFee) txctx := &tracers.Context{ BlockHash: blockHash, TxIndex: task.index, @@ -518,7 +519,8 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, } } // Generate the next state snapshot fast without tracing - msg, _ := tx.AsMessage(signer, balance, block.Number()) + header := block.Header() + msg, _ := tx.AsMessage(signer, balance, header.Number, header.BaseFee) txContext := core.NewEVMTxContext(msg) statedb.Prepare(tx.Hash(), i) @@ -800,7 +802,8 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree balanceFee = value } } - msg, err := tx.AsMessage(types.MakeSigner(api.config, block.Header().Number), balanceFee, block.Number()) + header := block.Header() + msg, err := tx.AsMessage(types.MakeSigner(api.config, header.Number), balanceFee, header.Number, header.BaseFee) if err != nil { return nil, vm.BlockContext{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err) } diff --git a/eth/tracers/testing/calltrace_test.go b/eth/tracers/testing/calltrace_test.go index 8542b17313bf..aaea4f375d41 100644 --- a/eth/tracers/testing/calltrace_test.go +++ b/eth/tracers/testing/calltrace_test.go @@ -115,7 +115,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { t.Fatalf("failed to create call tracer: %v", err) } evm := vm.NewEVM(context, txContext, statedb, nil, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) - msg, err := tx.AsMessage(signer, nil, nil) + msg, err := tx.AsMessage(signer, nil, nil, nil) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } @@ -201,7 +201,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { b.Fatalf("failed to parse testcase input: %v", err) } signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number))) - msg, err := tx.AsMessage(signer, nil, nil) + msg, err := tx.AsMessage(signer, nil, nil, nil) if err != nil { b.Fatalf("failed to prepare transaction for tracing: %v", err) } diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index a0e17e7c538f..9505387fce35 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -153,7 +153,7 @@ func TestZeroValueToNotExitCall(t *testing.T) { t.Fatalf("failed to create call tracer: %v", err) } evm := vm.NewEVM(context, txContext, statedb, nil, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}) - msg, err := tx.AsMessage(signer, nil, nil) + msg, err := tx.AsMessage(signer, nil, nil, nil) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } @@ -239,7 +239,7 @@ func TestPrestateTracerCreate2(t *testing.T) { } evm := vm.NewEVM(context, txContext, statedb, nil, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}) - msg, err := tx.AsMessage(signer, nil, nil) + msg, err := tx.AsMessage(signer, nil, nil, nil) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } @@ -336,7 +336,7 @@ func BenchmarkTransactionTrace(b *testing.B) { //EnableReturnData: false, }) evm := vm.NewEVM(context, txContext, statedb, nil, params.AllEthashProtocolChanges, vm.Config{Debug: true, Tracer: tracer}) - msg, err := tx.AsMessage(signer, nil, nil) + msg, err := tx.AsMessage(signer, nil, nil, nil) if err != nil { b.Fatalf("failed to prepare transaction for tracing: %v", err) } diff --git a/interfaces.go b/interfaces.go index 6815b37c66a7..79caec666089 100644 --- a/interfaces.go +++ b/interfaces.go @@ -120,7 +120,11 @@ type CallMsg struct { Value *big.Int // amount of wei sent along with the call Data []byte // input data, usually an ABI-encoded contract method invocation BalanceTokenFee *big.Int - AccessList types.AccessList // EIP-2930 access list. + + FeeCap *big.Int // EIP-1559 fee cap per gas. + Tip *big.Int // EIP-1559 tip per gas. + + AccessList types.AccessList // EIP-2930 access list. } // A ContractCaller provides contract calls, essentially transactions that are executed by diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 1f7b9f5601e2..7915c6dd7628 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1822,7 +1822,7 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber)) result.TransactionIndex = (*hexutil.Uint64)(&index) } - if tx.Type() == types.AccessListTxType { + if tx.Type() != types.LegacyTxType { al := tx.AccessList() result.Accesses = &al result.ChainID = (*hexutil.Big)(tx.ChainId()) @@ -1960,7 +1960,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH if value, ok := feeCapacity[to]; ok { balanceTokenFee = value } - msg := types.NewMessage(args.from(), args.To, uint64(*args.Nonce), args.Value.ToInt(), uint64(*args.Gas), args.GasPrice.ToInt(), args.data(), accessList, false, balanceTokenFee, header.Number) + msg := types.NewMessage(args.from(), args.To, uint64(*args.Nonce), args.Value.ToInt(), uint64(*args.Gas), args.GasPrice.ToInt(), nil, nil, args.data(), accessList, false, balanceTokenFee, header.Number) // Apply the transaction with the access list tracer tracer := vm.NewAccessListTracer(accessList, args.from(), to, precompiles) diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 48374f8fa9d8..f2df0a5b64e8 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -165,7 +165,7 @@ func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap } // Create new call message - msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, accessList, false, nil, number) + msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, nil, nil, data, accessList, false, nil, number) return msg } diff --git a/les/odr_test.go b/les/odr_test.go index 1495398379a0..7d9431dcf302 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -133,7 +133,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai if value, ok := feeCapacity[testContractAddr]; ok { balanceTokenFee = value } - msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, nil, false, balanceTokenFee, header.Number)} + msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, new(big.Int), nil, nil, data, nil, false, balanceTokenFee, header.Number)} context := core.NewEVMBlockContext(header, bc, nil) txContext := core.NewEVMTxContext(msg) @@ -154,7 +154,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai if value, ok := feeCapacity[testContractAddr]; ok { balanceTokenFee = value } - msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, nil, false, balanceTokenFee, header.Number)} + msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 100000, new(big.Int), nil, nil, data, nil, false, balanceTokenFee, header.Number)} context := core.NewEVMBlockContext(header, lc, nil) txContext := core.NewEVMTxContext(msg) vmenv := vm.NewEVM(context, txContext, statedb, nil, config, vm.Config{}) diff --git a/light/odr_test.go b/light/odr_test.go index c2c22d265712..7be95dd96975 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -184,7 +184,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain if value, ok := feeCapacity[testContractAddr]; ok { balanceTokenFee = value } - msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, new(big.Int), data, nil, false, balanceTokenFee, header.Number)} + msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, new(big.Int), nil, nil, data, nil, false, balanceTokenFee, header.Number)} txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(header, chain, nil) vmenv := vm.NewEVM(context, txContext, st, nil, config, vm.Config{}) diff --git a/params/protocol_params.go b/params/protocol_params.go index cb7080d37b4b..73cf17a8a38b 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -67,6 +67,8 @@ const ( TxDataNonZeroGas uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. + InitialBaseFee = 12500000000 // Initial base fee for EIP-1559 blocks. + MaxCodeSize = 24576 // Maximum bytecode to permit for a contract // Precompiled contract gas prices diff --git a/tests/state_test_util.go b/tests/state_test_util.go index afb35c9d02db..cc1e79223237 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -241,7 +241,7 @@ func (tx *stTransaction) toMessage(ps stPostState, number *big.Int) (core.Messag if err != nil { return nil, fmt.Errorf("invalid tx data %q", dataHex) } - msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, data, nil, true, nil, number) + msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, nil, nil, data, nil, true, nil, number) return msg, nil } From 4e7fd897d83f5b00927c50dc24528ff26124d2f7 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 28 May 2024 18:31:53 +0800 Subject: [PATCH 079/479] core: fix failing tests (#22888) --- core/blockchain_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 75903c4b6469..87f031b834a9 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -17,6 +17,7 @@ package core import ( + "errors" "fmt" "math" "math/big" @@ -1108,8 +1109,8 @@ func TestEIP155Transition(t *testing.T) { } }) _, err := blockchain.InsertChain(blocks) - if err != types.ErrInvalidChainId { - t.Error("expected error:", types.ErrInvalidChainId) + if have, want := err, types.ErrInvalidChainId; !errors.Is(have, want) { + t.Errorf("have %v, want %v", have, want) } } From 684afd0b18cb83a53e6ebcf7b1fb219a3e9d96f7 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 26 Apr 2024 17:43:05 +0800 Subject: [PATCH 080/479] EIP-1559: miner changes (#22896) --- core/types/transaction.go | 14 ++++++++++++++ miner/worker.go | 3 +++ 2 files changed, 17 insertions(+) diff --git a/core/types/transaction.go b/core/types/transaction.go index c5f73db9ae11..e038ab0da69c 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -43,6 +43,7 @@ var ( errInvalidYParity = errors.New("'yParity' field must be 0 or 1") errVYParityMismatch = errors.New("'v' and 'yParity' fields do not match") errVYParityMissing = errors.New("missing 'yParity' or 'v' field in transaction") + ErrFeeCapTooLow = errors.New("fee cap less than base fee") errEmptyTypedTx = errors.New("empty typed transaction bytes") errNoSigner = errors.New("missing signing methods") skipNonceDestinationAddress = map[common.Address]bool{ @@ -320,6 +321,19 @@ func (tx *Transaction) From() *common.Address { return &from } +// EffectiveTip returns the effective miner tip for the given base fee. +// Returns error in case of a negative effective miner tip. +func (tx *Transaction) EffectiveTip(baseFee *big.Int) (*big.Int, error) { + if baseFee == nil { + return tx.Tip(), nil + } + feeCap := tx.FeeCap() + if feeCap.Cmp(baseFee) == -1 { + return nil, ErrFeeCapTooLow + } + return math.BigMin(tx.Tip(), feeCap.Sub(feeCap, baseFee)), nil +} + // RawSignatureValues returns the V, R, S signature values of the transaction. // The return values should not be modified by the caller. func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) { diff --git a/miner/worker.go b/miner/worker.go index d698b2f66a79..3cd8fae9406b 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -615,6 +615,9 @@ func (w *worker) commitNewWork() { Extra: w.extra, Time: big.NewInt(tstamp), } + // Set baseFee if we are on an EIP-1559 chain + header.BaseFee = misc.CalcBaseFee(self.config, header) + // Only set the coinbase if we are mining (avoid spurious block rewards) if atomic.LoadInt32(&w.mining) == 1 { header.Coinbase = w.coinbase From d88d2670e5fa53e2e12612590e9ed9396d2fd1a7 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 16 May 2024 17:48:56 +0800 Subject: [PATCH 081/479] core: make txpool free space calculation more accurate (#22933) --- core/tx_pool.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index 4cfdba713abf..e7b2686ef08d 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -711,7 +711,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e return pool.promoteSpecialTx(from, tx, isLocal) } // If the transaction pool is full, discard underpriced transactions - if uint64(pool.all.Count()) >= pool.config.GlobalSlots+pool.config.GlobalQueue { + if uint64(pool.all.Slots()+numSlots(tx)) > pool.config.GlobalSlots+pool.config.GlobalQueue { log.Debug("Add transaction to pool full", "hash", hash, "nonce", tx.Nonce()) // If the new transaction is underpriced, don't accept it if !isLocal && pool.priced.Underpriced(tx) { From f6a4769084c0dd8f8632428316af833bcff0a4d0 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 17 May 2024 14:33:44 +0800 Subject: [PATCH 082/479] EIP-1559 tx pool support (#22898) --- core/tx_list.go | 204 +++++++++----- core/tx_pool.go | 61 +++- core/tx_pool_test.go | 578 ++++++++++++++++++++++++++++++++++---- core/types/transaction.go | 71 +++-- eth/gasprice/gasprice.go | 2 +- 5 files changed, 759 insertions(+), 157 deletions(-) diff --git a/core/tx_list.go b/core/tx_list.go index 3e746ff3518e..fd2985af7a95 100644 --- a/core/tx_list.go +++ b/core/tx_list.go @@ -23,6 +23,7 @@ import ( "sort" "sync" "sync/atomic" + "time" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/types" @@ -284,15 +285,23 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran return false, nil } if old != nil { - // threshold = oldGP * (100 + priceBump) / 100 + if old.FeeCapCmp(tx) >= 0 || old.TipCmp(tx) >= 0 { + return false, nil + } + // thresholdFeeCap = oldFC * (100 + priceBump) / 100 a := big.NewInt(100 + int64(priceBump)) - a = a.Mul(a, old.GasPrice()) + aFeeCap := new(big.Int).Mul(a, old.FeeCap()) + aTip := a.Mul(a, old.Tip()) + + // thresholdTip = oldTip * (100 + priceBump) / 100 b := big.NewInt(100) - threshold := a.Div(a, b) - // Have to ensure that the new gas price is higher than the old gas - // price as well as checking the percentage threshold to ensure that + thresholdFeeCap := aFeeCap.Div(aFeeCap, b) + thresholdTip := aTip.Div(aTip, b) + + // Have to ensure that either the new fee cap or tip is higher than the + // old ones as well as checking the percentage threshold to ensure that // this is accurate for low (Wei-level) gas price replacements - if old.GasPriceCmp(tx) >= 0 || tx.GasPriceIntCmp(threshold) < 0 { + if tx.FeeCapIntCmp(thresholdFeeCap) < 0 || tx.TipIntCmp(thresholdTip) < 0 { return false, nil } } @@ -417,33 +426,54 @@ func (l *txList) LastElement() *types.Transaction { } // priceHeap is a heap.Interface implementation over transactions for retrieving -// price-sorted transactions to discard when the pool fills up. -type priceHeap []*types.Transaction +// price-sorted transactions to discard when the pool fills up. If baseFee is set +// then the heap is sorted based on the effective tip based on the given base fee. +// If baseFee is nil then the sorting is based on feeCap. +type priceHeap struct { + baseFee *big.Int // heap should always be re-sorted after baseFee is changed + list []*types.Transaction +} -func (h priceHeap) Len() int { return len(h) } -func (h priceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } +func (h *priceHeap) Len() int { return len(h.list) } +func (h *priceHeap) Swap(i, j int) { h.list[i], h.list[j] = h.list[j], h.list[i] } -func (h priceHeap) Less(i, j int) bool { - // Sort primarily by price, returning the cheaper one - switch h[i].GasPriceCmp(h[j]) { +func (h *priceHeap) Less(i, j int) bool { + switch h.cmp(h.list[i], h.list[j]) { case -1: return true case 1: return false + default: + return h.list[i].Nonce() > h.list[j].Nonce() + } +} + +func (h *priceHeap) cmp(a, b *types.Transaction) int { + if h.baseFee != nil { + // Compare effective tips if baseFee is specified + if c := a.EffectiveTipCmp(b, h.baseFee); c != 0 { + return c + } + } + // Compare fee caps if baseFee is not specified or effective tips are equal + if c := a.FeeCapCmp(b); c != 0 { + return c } - // If the prices match, stabilize via nonces (high nonce is worse) - return h[i].Nonce() > h[j].Nonce() + // Compare tips if effective tips and fee caps are equal + return a.TipCmp(b) } func (h *priceHeap) Push(x interface{}) { - *h = append(*h, x.(*types.Transaction)) + tx := x.(*types.Transaction) + h.list = append(h.list, tx) } func (h *priceHeap) Pop() interface{} { - old := *h + old := h.list n := len(old) x := old[n-1] - *h = old[0 : n-1] + old[n-1] = nil + h.list = old[0 : n-1] return x } @@ -451,18 +481,30 @@ func (h *priceHeap) Pop() interface{} { // contents in a price-incrementing way. It's built opon the all transactions // in txpool but only interested in the remote part. It means only remote transactions // will be considered for tracking, sorting, eviction, etc. +// +// Two heaps are used for sorting: the urgent heap (based on effective tip in the next +// block) and the floating heap (based on feeCap). Always the bigger heap is chosen for +// eviction. Transactions evicted from the urgent heap are first demoted into the floating heap. +// In some cases (during a congestion, when blocks are full) the urgent heap can provide +// better candidates for inclusion while in other cases (at the top of the baseFee peak) +// the floating heap is better. When baseFee is decreasing they behave similarly. type txPricedList struct { - all *txLookup // Pointer to the map of all transactions - remotes *priceHeap // Heap of prices of all the stored **remote** transactions - stales int64 // Number of stale price points to (re-heap trigger) - reheapMu sync.Mutex // Mutex asserts that only one routine is reheaping the list + all *txLookup // Pointer to the map of all transactions + urgent, floating priceHeap // Heaps of prices of all the stored **remote** transactions + stales int64 // Number of stale price points to (re-heap trigger) + reheapMu sync.Mutex // Mutex asserts that only one routine is reheaping the list } +const ( + // urgentRatio : floatingRatio is the capacity ratio of the two queues + urgentRatio = 4 + floatingRatio = 1 +) + // newTxPricedList creates a new price-sorted transaction heap. func newTxPricedList(all *txLookup) *txPricedList { return &txPricedList{ - all: all, - remotes: new(priceHeap), + all: all, } } @@ -471,7 +513,8 @@ func (l *txPricedList) Put(tx *types.Transaction, local bool) { if local { return } - heap.Push(l.remotes, tx) + // Insert every new transaction to the urgent heap first; Discard will balance the heaps + heap.Push(&l.urgent, tx) } // Removed notifies the prices transaction list that an old transaction dropped @@ -480,58 +523,43 @@ func (l *txPricedList) Put(tx *types.Transaction, local bool) { func (l *txPricedList) Removed(count int) { // Bump the stale counter, but exit if still too low (< 25%) stales := atomic.AddInt64(&l.stales, int64(count)) - if int(stales) <= len(*l.remotes)/4 { + if int(stales) <= (len(l.urgent.list)+len(l.floating.list))/4 { return } // Seems we've reached a critical number of stale transactions, reheap l.Reheap() } -// Cap finds all the transactions below the given price threshold, drops them -// from the priced list and returns them for further removal from the entire pool. -// -// Note: only remote transactions will be considered for eviction. -func (l *txPricedList) Cap(threshold *big.Int) types.Transactions { - drop := make(types.Transactions, 0, 128) // Remote underpriced transactions to drop - for len(*l.remotes) > 0 { - // Discard stale transactions if found during cleanup - cheapest := (*l.remotes)[0] - if l.all.GetRemote(cheapest.Hash()) == nil { // Removed or migrated - heap.Pop(l.remotes) - l.stales-- - continue - } - // Stop the discards if we've reached the threshold - if cheapest.GasPriceIntCmp(threshold) >= 0 { - break - } - heap.Pop(l.remotes) - drop = append(drop, cheapest) - } - return drop -} - // Underpriced checks whether a transaction is cheaper than (or as cheap as) the // lowest priced (remote) transaction currently being tracked. func (l *txPricedList) Underpriced(tx *types.Transaction) bool { + // Note: with two queues, being underpriced is defined as being worse than the worst item + // in all non-empty queues if there is any. If both queues are empty then nothing is underpriced. + return (l.underpricedFor(&l.urgent, tx) || len(l.urgent.list) == 0) && + (l.underpricedFor(&l.floating, tx) || len(l.floating.list) == 0) && + (len(l.urgent.list) != 0 || len(l.floating.list) != 0) +} + +// underpricedFor checks whether a transaction is cheaper than (or as cheap as) the +// lowest priced (remote) transaction in the given heap. +func (l *txPricedList) underpricedFor(h *priceHeap, tx *types.Transaction) bool { // Discard stale price points if found at the heap start - for len(*l.remotes) > 0 { - head := []*types.Transaction(*l.remotes)[0] + for len(h.list) > 0 { + head := h.list[0] if l.all.GetRemote(head.Hash()) == nil { // Removed or migrated atomic.AddInt64(&l.stales, -1) - heap.Pop(l.remotes) + heap.Pop(h) continue } break } // Check if the transaction is underpriced or not - if len(*l.remotes) == 0 { + if len(h.list) == 0 { return false // There is no remote transaction at all. } // If the remote transaction is even cheaper than the // cheapest one tracked locally, reject it. - cheapest := []*types.Transaction(*l.remotes)[0] - return cheapest.GasPriceCmp(tx) >= 0 + return h.cmp(h.list[0], tx) >= 0 } // Discard finds a number of most underpriced transactions, removes them from the @@ -540,21 +568,36 @@ func (l *txPricedList) Underpriced(tx *types.Transaction) bool { // Note local transaction won't be considered for eviction. func (l *txPricedList) Discard(slots int, force bool) (types.Transactions, bool) { drop := make(types.Transactions, 0, slots) // Remote underpriced transactions to drop - for len(*l.remotes) > 0 && slots > 0 { - // Discard stale transactions if found during cleanup - tx := heap.Pop(l.remotes).(*types.Transaction) - if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated - atomic.AddInt64(&l.stales, -1) - continue + for slots > 0 { + if len(l.urgent.list)*floatingRatio > len(l.floating.list)*urgentRatio || floatingRatio == 0 { + // Discard stale transactions if found during cleanup + tx := heap.Pop(&l.urgent).(*types.Transaction) + if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated + atomic.AddInt64(&l.stales, -1) + continue + } + // Non stale transaction found, move to floating heap + heap.Push(&l.floating, tx) + } else { + if len(l.floating.list) == 0 { + // Stop if both heaps are empty + break + } + // Discard stale transactions if found during cleanup + tx := heap.Pop(&l.floating).(*types.Transaction) + if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated + atomic.AddInt64(&l.stales, -1) + continue + } + // Non stale transaction found, discard it + drop = append(drop, tx) + slots -= numSlots(tx) } - // Non stale transaction found, discard it - drop = append(drop, tx) - slots -= numSlots(tx) } // If we still can't make enough room for the new transaction if slots > 0 && !force { for _, tx := range drop { - heap.Push(l.remotes, tx) + heap.Push(&l.urgent, tx) } return nil, false } @@ -565,13 +608,32 @@ func (l *txPricedList) Discard(slots int, force bool) (types.Transactions, bool) func (l *txPricedList) Reheap() { l.reheapMu.Lock() defer l.reheapMu.Unlock() - reheap := make(priceHeap, 0, l.all.RemoteCount()) - + start := time.Now() atomic.StoreInt64(&l.stales, 0) - l.remotes = &reheap + l.urgent.list = make([]*types.Transaction, 0, l.all.RemoteCount()) l.all.Range(func(hash common.Hash, tx *types.Transaction, local bool) bool { - *l.remotes = append(*l.remotes, tx) + l.urgent.list = append(l.urgent.list, tx) return true }, false, true) // Only iterate remotes - heap.Init(l.remotes) + heap.Init(&l.urgent) + + // balance out the two heaps by moving the worse half of transactions into the + // floating heap + // Note: Discard would also do this before the first eviction but Reheap can do + // is more efficiently. Also, Underpriced would work suboptimally the first time + // if the floating queue was empty. + floatingCount := len(l.urgent.list) * floatingRatio / (urgentRatio + floatingRatio) + l.floating.list = make([]*types.Transaction, floatingCount) + for i := 0; i < floatingCount; i++ { + l.floating.list[i] = heap.Pop(&l.urgent).(*types.Transaction) + } + heap.Init(&l.floating) + reheapTimer.Update(time.Since(start)) +} + +// SetBaseFee updates the base fee and triggers a re-heap. Note that Removed is not +// necessary to call right before SetBaseFee when processing a new block. +func (l *txPricedList) SetBaseFee(baseFee *big.Int) { + l.urgent.baseFee = baseFee + l.Reheap() } diff --git a/core/tx_pool.go b/core/tx_pool.go index e7b2686ef08d..8d1530e121ae 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -106,6 +106,10 @@ var ( ErrDuplicateSpecialTransaction = errors.New("duplicate a special transaction") ErrMinDeploySMC = errors.New("smart contract creation cost is under allowance") + + // ErrTipAboveFeeCap is a sanity error to ensure no one is able to specify a + // transaction with a tip higher than the total fee cap. + ErrTipAboveFeeCap = errors.New("tip higher than fee cap") ) var ( @@ -138,6 +142,8 @@ var ( queuedGauge = metrics.NewRegisteredGauge("txpool/queued", nil) localGauge = metrics.NewRegisteredGauge("txpool/local", nil) slotsGauge = metrics.NewRegisteredGauge("txpool/slots", nil) + + reheapTimer = metrics.NewRegisteredTimer("txpool/reheap", nil) ) // TxStatus is the current status of a transaction as seen by the pool. @@ -199,7 +205,7 @@ var DefaultTxPoolConfig = TxPoolConfig{ PriceBump: 10, AccountSlots: 16, - GlobalSlots: 4096, + GlobalSlots: 4096 + 1024, // urgent + floating queue capacity with 4:1 ratio AccountQueue: 64, GlobalQueue: 1024, @@ -262,6 +268,9 @@ type TxPool struct { signer types.Signer mu sync.RWMutex + eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions. + eip1559 bool // Fork indicator whether we are using EIP-1559 type transactions. + currentState *state.StateDB // Current state in the blockchain head pendingNonces *txNoncer // Pending state tracking virtual nonces currentMaxGas uint64 // Current gas limit for transaction caps @@ -285,7 +294,6 @@ type TxPool struct { wg sync.WaitGroup // tracks loop, scheduleReorgLoop initDoneCh chan struct{} // is closed once the pool is initialized (for tests) - eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions. IsSigner func(address common.Address) bool trc21FeeCapacity map[common.Address]*big.Int } @@ -466,10 +474,18 @@ func (pool *TxPool) SetGasPrice(price *big.Int) { pool.mu.Lock() defer pool.mu.Unlock() + old := pool.gasPrice pool.gasPrice = price - for _, tx := range pool.priced.Cap(price) { - pool.removeTx(tx.Hash(), false) + // if the min miner fee increased, remove transactions below the new threshold + if price.Cmp(old) > 0 { + // pool.priced is sorted by FeeCap, so we have to iterate through pool.all instead + drop := pool.all.RemotesBelowTip(price) + for _, tx := range drop { + pool.removeTx(tx.Hash(), false) + } + pool.priced.Removed(len(drop)) } + log.Info("Transaction pool price threshold updated", "price", price) } @@ -575,6 +591,10 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { if !pool.eip2718 && tx.Type() != types.LegacyTxType { return ErrTxTypeNotSupported } + // Reject dynamic fee transactions until EIP-1559 activates. + if !pool.eip1559 && tx.Type() == types.DynamicFeeTxType { + return ErrTxTypeNotSupported + } // Reject transactions over defined size to prevent DOS attacks if uint64(tx.Size()) > txMaxSize { return ErrOversizedData @@ -596,13 +616,17 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { if pool.currentMaxGas < tx.Gas() { return ErrGasLimit } + // Ensure feeCap is less than or equal to tip. + if tx.FeeCapIntCmp(tx.Tip()) < 0 { + return ErrTipAboveFeeCap + } // Make sure the transaction is signed properly. from, err := types.Sender(pool.signer, tx) if err != nil { return ErrInvalidSender } - // Drop non-local transactions under our own minimal accepted gas price - if !local && tx.GasPriceIntCmp(pool.gasPrice) < 0 { + // Drop non-local transactions under our own minimal accepted gas price or tip + if !local && tx.TipIntCmp(pool.gasPrice) < 0 { if !tx.IsSpecialTransaction() || (pool.IsSigner != nil && !pool.IsSigner(from)) { return ErrUnderpriced } @@ -712,10 +736,9 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e } // If the transaction pool is full, discard underpriced transactions if uint64(pool.all.Slots()+numSlots(tx)) > pool.config.GlobalSlots+pool.config.GlobalQueue { - log.Debug("Add transaction to pool full", "hash", hash, "nonce", tx.Nonce()) // If the new transaction is underpriced, don't accept it if !isLocal && pool.priced.Underpriced(tx) { - log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice()) + log.Trace("Discarding underpriced transaction", "hash", hash, "tip", tx.Tip(), "feeCap", tx.FeeCap()) underpricedTxMeter.Mark(1) return false, ErrUnderpriced } @@ -732,7 +755,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e } // Kick out the underpriced remote transactions. for _, tx := range drop { - log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "price", tx.GasPrice()) + log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "tip", tx.Tip(), "feeCap", tx.FeeCap()) underpricedTxMeter.Mark(1) pool.removeTx(tx.Hash(), false) } @@ -1243,6 +1266,9 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt // because of another transaction (e.g. higher gas price). if reset != nil { pool.demoteUnexecutables() + if reset.newHead != nil { + pool.priced.SetBaseFee(reset.newHead.BaseFee) + } } // Ensure pool.queue and pool.pending sizes stay within the configured limits. pool.truncatePending() @@ -1360,6 +1386,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { // Update all fork indicator by next pending block number. next := new(big.Int).Add(newHead.Number, big.NewInt(1)) pool.eip2718 = pool.chainconfig.IsEIP1559(next) + pool.eip1559 = pool.chainconfig.IsEIP1559(next) } // promoteExecutables moves transactions that have become processable from the @@ -1572,6 +1599,10 @@ func (pool *TxPool) truncateQueue() { // demoteUnexecutables removes invalid and processed transactions from the pools // executable/pending queue and any subsequent transactions that become unexecutable // are moved back into the future queue. +// +// Note: transactions are not marked as removed in the priced list because re-heaping +// is always explicitly triggered by SetBaseFee and it would be unnecessary and wasteful +// to trigger a re-heap is this function func (pool *TxPool) demoteUnexecutables() { // Iterate over all accounts and demote any non-executable transactions for addr, list := range pool.pending { @@ -1877,6 +1908,18 @@ func (t *txLookup) RemoteToLocals(locals *accountSet) int { return migrated } +// RemotesBelowTip finds all remote transactions below the given tip threshold. +func (t *txLookup) RemotesBelowTip(threshold *big.Int) types.Transactions { + found := make(types.Transactions, 0, 128) + t.Range(func(hash common.Hash, tx *types.Transaction, local bool) bool { + if tx.TipIntCmp(threshold) < 0 { + found = append(found, tx) + } + return true + }, false, true) // Only iterate remotes + return found +} + // numSlots calculates the number of slots needed for a single transaction. func numSlots(tx *types.Transaction) int { return int((tx.Size() + txSlotSize - 1) / txSlotSize) diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index 62e1d70a61da..f73d6f92bc8c 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -36,13 +36,23 @@ import ( "github.com/XinFinOrg/XDPoSChain/params" ) -// testTxPoolConfig is a transaction pool configuration without stateful disk -// sideeffects used during testing. -var testTxPoolConfig TxPoolConfig +var ( + // testTxPoolConfig is a transaction pool configuration without stateful disk + // sideeffects used during testing. + testTxPoolConfig TxPoolConfig + + // eip1559Config is a chain config with EIP-1559 enabled at block 0. + eip1559Config *params.ChainConfig +) func init() { testTxPoolConfig = DefaultTxPoolConfig testTxPoolConfig.Journal = "" + + cpy := *params.TestChainConfig + eip1559Config = &cpy + eip1559Config.BerlinBlock = common.Big0 + eip1559Config.Eip1559Block = common.Big0 } type testBlockChain struct { @@ -102,13 +112,32 @@ func pricedDataTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key return tx } +func dynamicFeeTx(nonce uint64, gaslimit uint64, gasFee *big.Int, tip *big.Int, key *ecdsa.PrivateKey) *types.Transaction { + tx, _ := types.SignNewTx(key, types.LatestSignerForChainID(params.TestChainConfig.ChainId), &types.DynamicFeeTx{ + ChainID: params.TestChainConfig.ChainId, + Nonce: nonce, + Tip: tip, + FeeCap: gasFee, + Gas: gaslimit, + To: &common.Address{}, + Value: big.NewInt(100), + Data: nil, + AccessList: nil, + }) + return tx +} + func setupTxPool() (*TxPool, *ecdsa.PrivateKey) { + return setupTxPoolWithConfig(params.TestChainConfig) +} + +func setupTxPoolWithConfig(config *params.ChainConfig) (*TxPool, *ecdsa.PrivateKey) { diskdb := rawdb.NewMemoryDatabase() statedb, _ := state.New(common.Hash{}, state.NewDatabase(diskdb)) blockchain := &testBlockChain{statedb, 10000000, new(event.Feed)} key, _ := crypto.GenerateKey() - pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain) + pool := NewTxPool(testTxPoolConfig, config, blockchain) // wait for the pool to initialize <-pool.initDoneCh @@ -126,7 +155,7 @@ func validateTxPoolInternals(pool *TxPool) error { return fmt.Errorf("total transaction count %d != %d pending + %d queued", total, pending, queued) } pool.priced.Reheap() - priced, remote := pool.priced.remotes.Len(), pool.all.RemoteCount() + priced, remote := pool.priced.urgent.Len()+pool.priced.floating.Len(), pool.all.RemoteCount() if priced != remote { return fmt.Errorf("total priced transaction count %d != %d", priced, remote) } @@ -253,6 +282,18 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) { } } +func testAddBalance(pool *TxPool, addr common.Address, amount *big.Int) { + pool.mu.Lock() + pool.currentState.AddBalance(addr, amount) + pool.mu.Unlock() +} + +func testSetNonce(pool *TxPool, addr common.Address, nonce uint64) { + pool.mu.Lock() + pool.currentState.SetNonce(addr, nonce) + pool.mu.Unlock() +} + func TestInvalidTransactions(t *testing.T) { t.Parallel() @@ -262,19 +303,19 @@ func TestInvalidTransactions(t *testing.T) { tx := transaction(0, 100, key) from, _ := deriveSender(tx) - pool.currentState.AddBalance(from, big.NewInt(1)) + testAddBalance(pool, from, big.NewInt(1)) if err := pool.AddRemote(tx); err != ErrInsufficientFunds { t.Error("expected", ErrInsufficientFunds) } balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(new(big.Int).SetUint64(tx.Gas()), tx.GasPrice())) - pool.currentState.AddBalance(from, balance) + testAddBalance(pool, from, balance) if err := pool.AddRemote(tx); err != ErrIntrinsicGas { t.Error("expected", ErrIntrinsicGas, "got", err) } - pool.currentState.SetNonce(from, 1) - pool.currentState.AddBalance(from, big.NewInt(0xffffffffffffff)) + testSetNonce(pool, from, 1) + testAddBalance(pool, from, big.NewInt(0xffffffffffffff)) tx = transaction(0, 100000, key) if err := pool.AddRemote(tx); err != ErrNonceTooLow { t.Error("expected", ErrNonceTooLow) @@ -298,7 +339,7 @@ func TestTransactionQueue(t *testing.T) { tx := transaction(0, 100, key) from, _ := deriveSender(tx) - pool.currentState.AddBalance(from, big.NewInt(1000)) + testAddBalance(pool, from, big.NewInt(1000)) <-pool.requestReset(nil, nil) pool.enqueueTx(tx.Hash(), tx, false, true) @@ -309,7 +350,7 @@ func TestTransactionQueue(t *testing.T) { tx = transaction(1, 100, key) from, _ = deriveSender(tx) - pool.currentState.SetNonce(from, 2) + testSetNonce(pool, from, 2) pool.enqueueTx(tx.Hash(), tx, false, true) <-pool.requestPromoteExecutables(newAccountSet(pool.signer, from)) @@ -332,7 +373,7 @@ func TestTransactionQueue2(t *testing.T) { tx2 := transaction(10, 100, key) tx3 := transaction(11, 100, key) from, _ := deriveSender(tx1) - pool.currentState.AddBalance(from, big.NewInt(1000)) + testAddBalance(pool, from, big.NewInt(1000)) pool.reset(nil, nil) pool.enqueueTx(tx1.Hash(), tx1, false, true) @@ -356,12 +397,25 @@ func TestTransactionNegativeValue(t *testing.T) { tx, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(-1), 100, big.NewInt(1), nil), types.HomesteadSigner{}, key) from, _ := deriveSender(tx) - pool.currentState.AddBalance(from, big.NewInt(1)) + testAddBalance(pool, from, big.NewInt(1)) if err := pool.AddRemote(tx); err != ErrNegativeValue { t.Error("expected", ErrNegativeValue, "got", err) } } +func TestTransactionTipAboveFeeCap(t *testing.T) { + t.Parallel() + + pool, key := setupTxPoolWithConfig(eip1559Config) + defer pool.Stop() + + tx := dynamicFeeTx(0, 100, big.NewInt(1), big.NewInt(2), key) + + if err := pool.AddRemote(tx); err != ErrTipAboveFeeCap { + t.Error("expected", ErrTipAboveFeeCap, "got", err) + } +} + func TestTransactionChainFork(t *testing.T) { t.Parallel() @@ -451,7 +505,7 @@ func TestTransactionMissingNonce(t *testing.T) { defer pool.Stop() addr := crypto.PubkeyToAddress(key.PublicKey) - pool.currentState.AddBalance(addr, big.NewInt(100000000000000)) + testAddBalance(pool, addr, big.NewInt(100000000000000)) tx := transaction(1, 100000, key) if _, err := pool.add(tx, false); err != nil { t.Error("didn't expect error", err) @@ -475,8 +529,8 @@ func TestTransactionNonceRecovery(t *testing.T) { defer pool.Stop() addr := crypto.PubkeyToAddress(key.PublicKey) - pool.currentState.SetNonce(addr, n) - pool.currentState.AddBalance(addr, big.NewInt(100000000000000)) + testSetNonce(pool, addr, n) + testAddBalance(pool, addr, big.NewInt(100000000000000)) <-pool.requestReset(nil, nil) tx := transaction(n, 100000, key) @@ -484,7 +538,7 @@ func TestTransactionNonceRecovery(t *testing.T) { t.Error(err) } // simulate some weird re-order of transactions and missing nonce(s) - pool.currentState.SetNonce(addr, n-1) + testSetNonce(pool, addr, n-1) <-pool.requestReset(nil, nil) if fn := pool.Nonce(addr); fn != n-1 { t.Errorf("expected nonce to be %d, got %d", n-1, fn) @@ -501,7 +555,7 @@ func TestTransactionDropping(t *testing.T) { defer pool.Stop() account := crypto.PubkeyToAddress(key.PublicKey) - pool.currentState.AddBalance(account, big.NewInt(1000)) + testAddBalance(pool, account, big.NewInt(1000)) // Add some pending and some queued transactions var ( @@ -549,7 +603,7 @@ func TestTransactionDropping(t *testing.T) { t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), 6) } // Reduce the balance of the account, and check that invalidated transactions are dropped - pool.currentState.AddBalance(account, big.NewInt(-650)) + testAddBalance(pool, account, big.NewInt(-650)) <-pool.requestReset(nil, nil) if _, ok := pool.pending[account].txs.items[tx0.Nonce()]; !ok { @@ -616,7 +670,7 @@ func TestTransactionPostponing(t *testing.T) { keys[i], _ = crypto.GenerateKey() accs[i] = crypto.PubkeyToAddress(keys[i].PublicKey) - pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(50100)) + testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(50100)) } // Add a batch consecutive pending transactions for validation txs := []*types.Transaction{} @@ -659,7 +713,7 @@ func TestTransactionPostponing(t *testing.T) { } // Reduce the balance of the account, and check that transactions are reorganised for _, addr := range accs { - pool.currentState.AddBalance(addr, big.NewInt(-1)) + testAddBalance(pool, addr, big.NewInt(-1)) } <-pool.requestReset(nil, nil) @@ -720,7 +774,7 @@ func TestTransactionGapFilling(t *testing.T) { defer pool.Stop() account := crypto.PubkeyToAddress(key.PublicKey) - pool.currentState.AddBalance(account, big.NewInt(1000000)) + testAddBalance(pool, account, big.NewInt(1000000)) // Keep track of transaction events to ensure all executables get announced events := make(chan NewTxsEvent, testTxPoolConfig.AccountQueue+5) @@ -774,7 +828,7 @@ func TestTransactionQueueAccountLimiting(t *testing.T) { defer pool.Stop() account := crypto.PubkeyToAddress(key.PublicKey) - pool.currentState.AddBalance(account, big.NewInt(1000000)) + testAddBalance(pool, account, big.NewInt(1000000)) testTxPoolConfig.AccountQueue = 10 // Keep queuing up transactions and make sure all above a limit are dropped for i := uint64(1); i <= testTxPoolConfig.AccountQueue; i++ { @@ -831,7 +885,7 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) { keys := make([]*ecdsa.PrivateKey, 5) for i := 0; i < len(keys); i++ { keys[i], _ = crypto.GenerateKey() - pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) + testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) } local := keys[len(keys)-1] @@ -923,8 +977,8 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) { local, _ := crypto.GenerateKey() remote, _ := crypto.GenerateKey() - pool.currentState.AddBalance(crypto.PubkeyToAddress(local.PublicKey), big.NewInt(1000000000)) - pool.currentState.AddBalance(crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(1000000000)) + testAddBalance(pool, crypto.PubkeyToAddress(local.PublicKey), big.NewInt(1000000000)) + testAddBalance(pool, crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(1000000000)) // Add the two transactions and ensure they both are queued up if err := pool.AddLocal(pricedTransaction(1, 100000, big.NewInt(1), local)); err != nil { @@ -1057,7 +1111,7 @@ func TestTransactionPendingLimiting(t *testing.T) { defer pool.Stop() account := crypto.PubkeyToAddress(key.PublicKey) - pool.currentState.AddBalance(account, big.NewInt(1000000)) + testAddBalance(pool, account, big.NewInt(1000000)) testTxPoolConfig.AccountQueue = 10 // Keep track of transaction events to ensure all executables get announced events := make(chan NewTxsEvent, testTxPoolConfig.AccountQueue) @@ -1108,7 +1162,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) { keys := make([]*ecdsa.PrivateKey, 5) for i := 0; i < len(keys); i++ { keys[i], _ = crypto.GenerateKey() - pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) + testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) } // Generate and queue a batch of transactions nonces := make(map[common.Address]uint64) @@ -1147,7 +1201,7 @@ func TestTransactionAllowedTxSize(t *testing.T) { defer pool.Stop() account := crypto.PubkeyToAddress(key.PublicKey) - pool.currentState.AddBalance(account, big.NewInt(1000000000)) + testAddBalance(pool, account, big.NewInt(1000000000)) // Compute maximal data size for transactions (lower bound). // @@ -1212,7 +1266,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) { // Create a number of test accounts and fund them key, _ := crypto.GenerateKey() addr := crypto.PubkeyToAddress(key.PublicKey) - pool.currentState.AddBalance(addr, big.NewInt(1000000)) + testAddBalance(pool, addr, big.NewInt(1000000)) txs := types.Transactions{} for j := 0; j < int(config.GlobalSlots)*2; j++ { @@ -1246,7 +1300,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) { keys := make([]*ecdsa.PrivateKey, 5) for i := 0; i < len(keys); i++ { keys[i], _ = crypto.GenerateKey() - pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) + testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) } // Generate and queue a batch of transactions nonces := make(map[common.Address]uint64) @@ -1297,7 +1351,7 @@ func TestTransactionPoolRepricing(t *testing.T) { keys := make([]*ecdsa.PrivateKey, 4) for i := 0; i < len(keys); i++ { keys[i], _ = crypto.GenerateKey() - pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) + testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) } // Generate and queue a batch of transactions, both pending and queued txs := types.Transactions{} @@ -1397,8 +1451,135 @@ func TestTransactionPoolRepricing(t *testing.T) { } } +// Tests that setting the transaction pool gas price to a higher value correctly +// discards everything cheaper (legacy & dynamic fee) than that and moves any +// gapped transactions back from the pending pool to the queue. +// +// Note, local transactions are never allowed to be dropped. +func TestTransactionPoolRepricingDynamicFee(t *testing.T) { + t.Parallel() + + // Create the pool to test the pricing enforcement with + pool, _ := setupTxPoolWithConfig(eip1559Config) + defer pool.Stop() + + // Keep track of transaction events to ensure all executables get announced + events := make(chan NewTxsEvent, 32) + sub := pool.txFeed.Subscribe(events) + defer sub.Unsubscribe() + + // Create a number of test accounts and fund them + keys := make([]*ecdsa.PrivateKey, 4) + for i := 0; i < len(keys); i++ { + keys[i], _ = crypto.GenerateKey() + testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) + } + // Generate and queue a batch of transactions, both pending and queued + txs := types.Transactions{} + + txs = append(txs, pricedTransaction(0, 100000, big.NewInt(2), keys[0])) + txs = append(txs, pricedTransaction(1, 100000, big.NewInt(1), keys[0])) + txs = append(txs, pricedTransaction(2, 100000, big.NewInt(2), keys[0])) + + txs = append(txs, dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[1])) + txs = append(txs, dynamicFeeTx(1, 100000, big.NewInt(3), big.NewInt(2), keys[1])) + txs = append(txs, dynamicFeeTx(2, 100000, big.NewInt(3), big.NewInt(2), keys[1])) + + txs = append(txs, dynamicFeeTx(1, 100000, big.NewInt(2), big.NewInt(2), keys[2])) + txs = append(txs, dynamicFeeTx(2, 100000, big.NewInt(1), big.NewInt(1), keys[2])) + txs = append(txs, dynamicFeeTx(3, 100000, big.NewInt(2), big.NewInt(2), keys[2])) + + ltx := dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[3]) + + // Import the batch and that both pending and queued transactions match up + pool.AddRemotesSync(txs) + pool.AddLocal(ltx) + + pending, queued := pool.Stats() + if pending != 7 { + t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 7) + } + if queued != 3 { + t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 3) + } + if err := validateEvents(events, 7); err != nil { + t.Fatalf("original event firing failed: %v", err) + } + if err := validateTxPoolInternals(pool); err != nil { + t.Fatalf("pool internal state corrupted: %v", err) + } + // Reprice the pool and check that underpriced transactions get dropped + pool.SetGasPrice(big.NewInt(2)) + + pending, queued = pool.Stats() + if pending != 2 { + t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) + } + if queued != 5 { + t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 5) + } + if err := validateEvents(events, 0); err != nil { + t.Fatalf("reprice event firing failed: %v", err) + } + if err := validateTxPoolInternals(pool); err != nil { + t.Fatalf("pool internal state corrupted: %v", err) + } + // Check that we can't add the old transactions back + tx := pricedTransaction(1, 100000, big.NewInt(1), keys[0]) + if err := pool.AddRemote(tx); err != ErrUnderpriced { + t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced) + } + tx = dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[1]) + if err := pool.AddRemote(tx); err != ErrUnderpriced { + t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced) + } + tx = dynamicFeeTx(2, 100000, big.NewInt(1), big.NewInt(1), keys[2]) + if err := pool.AddRemote(tx); err != ErrUnderpriced { + t.Fatalf("adding underpriced queued transaction error mismatch: have %v, want %v", err, ErrUnderpriced) + } + if err := validateEvents(events, 0); err != nil { + t.Fatalf("post-reprice event firing failed: %v", err) + } + if err := validateTxPoolInternals(pool); err != nil { + t.Fatalf("pool internal state corrupted: %v", err) + } + // However we can add local underpriced transactions + tx = dynamicFeeTx(1, 100000, big.NewInt(1), big.NewInt(1), keys[3]) + if err := pool.AddLocal(tx); err != nil { + t.Fatalf("failed to add underpriced local transaction: %v", err) + } + if pending, _ = pool.Stats(); pending != 3 { + t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3) + } + if err := validateEvents(events, 1); err != nil { + t.Fatalf("post-reprice local event firing failed: %v", err) + } + if err := validateTxPoolInternals(pool); err != nil { + t.Fatalf("pool internal state corrupted: %v", err) + } + // And we can fill gaps with properly priced transactions + tx = pricedTransaction(1, 100000, big.NewInt(2), keys[0]) + if err := pool.AddRemote(tx); err != nil { + t.Fatalf("failed to add pending transaction: %v", err) + } + tx = dynamicFeeTx(0, 100000, big.NewInt(3), big.NewInt(2), keys[1]) + if err := pool.AddRemote(tx); err != nil { + t.Fatalf("failed to add pending transaction: %v", err) + } + tx = dynamicFeeTx(2, 100000, big.NewInt(2), big.NewInt(2), keys[2]) + if err := pool.AddRemote(tx); err != nil { + t.Fatalf("failed to add queued transaction: %v", err) + } + if err := validateEvents(events, 5); err != nil { + t.Fatalf("post-reprice event firing failed: %v", err) + } + if err := validateTxPoolInternals(pool); err != nil { + t.Fatalf("pool internal state corrupted: %v", err) + } +} + // Tests that setting the transaction pool gas price to a higher value does not -// remove local transactions. +// remove local transactions (legacy & dynamic fee). func TestTransactionPoolRepricingKeepsLocals(t *testing.T) { t.Parallel() @@ -1407,30 +1588,42 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) { statedb, _ := state.New(common.Hash{}, state.NewDatabase(db)) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} - pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain) + pool := NewTxPool(testTxPoolConfig, eip1559Config, blockchain) defer pool.Stop() // Create a number of test accounts and fund them keys := make([]*ecdsa.PrivateKey, 3) for i := 0; i < len(keys); i++ { keys[i], _ = crypto.GenerateKey() - pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000*1000000)) + testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000*1000000)) } // Create transaction (both pending and queued) with a linearly growing gasprice + // common.LimitThresholdNonceInQueue = 10 for i := uint64(0); i < 5; i++ { - // Add pending - p_tx := pricedTransaction(i, 100000, big.NewInt(int64(i+1)), keys[2]) - if err := pool.AddLocal(p_tx); err != nil { + // Add pending transaction. + pendingTx := pricedTransaction(i, 100000, big.NewInt(int64(i+1)), keys[2]) + if err := pool.AddLocal(pendingTx); err != nil { t.Fatal(err) } - // Add queued - q_tx := pricedTransaction(i+6, 100000, big.NewInt(int64(i+1)), keys[2]) - if err := pool.AddLocal(q_tx); err != nil { + // Add queued transaction. + queuedTx := pricedTransaction(i+6, 100000, big.NewInt(int64(i+1)), keys[2]) + if err := pool.AddLocal(queuedTx); err != nil { + t.Fatal(err) + } + + // Add pending dynamic fee transaction. + pendingTx = dynamicFeeTx(i, 100000, big.NewInt(int64(i)+1), big.NewInt(int64(i)), keys[1]) + if err := pool.AddLocal(pendingTx); err != nil { + t.Fatal(err) + } + // Add queued dynamic fee transaction. + queuedTx = dynamicFeeTx(i+6, 100000, big.NewInt(int64(i)+1), big.NewInt(int64(i)), keys[1]) + if err := pool.AddLocal(queuedTx); err != nil { t.Fatal(err) } } pending, queued := pool.Stats() - expPending, expQueued := 5, 5 + expPending, expQueued := 10, 10 validate := func() { pending, queued = pool.Stats() if pending != expPending { @@ -1486,7 +1679,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) { keys := make([]*ecdsa.PrivateKey, 4) for i := 0; i < len(keys); i++ { keys[i], _ = crypto.GenerateKey() - pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) + testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) } // Generate and queue a batch of transactions, both pending and queued txs := types.Transactions{} @@ -1594,7 +1787,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) { keys := make([]*ecdsa.PrivateKey, 2) for i := 0; i < len(keys); i++ { keys[i], _ = crypto.GenerateKey() - pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) + testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) } // Fill up the entire queue with the same transaction price points txs := types.Transactions{} @@ -1635,6 +1828,173 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) { } } +// Tests that when the pool reaches its global transaction limit, underpriced +// transactions (legacy & dynamic fee) are gradually shifted out for more +// expensive ones and any gapped pending transactions are moved into the queue. +// +// Note, local transactions are never allowed to be dropped. +func TestTransactionPoolUnderpricingDynamicFee(t *testing.T) { + t.Parallel() + + pool, _ := setupTxPoolWithConfig(eip1559Config) + defer pool.Stop() + + pool.config.GlobalSlots = 2 + pool.config.GlobalQueue = 2 + + // Keep track of transaction events to ensure all executables get announced + events := make(chan NewTxsEvent, 32) + sub := pool.txFeed.Subscribe(events) + defer sub.Unsubscribe() + + // Create a number of test accounts and fund them + keys := make([]*ecdsa.PrivateKey, 4) + for i := 0; i < len(keys); i++ { + keys[i], _ = crypto.GenerateKey() + testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) + } + + // Generate and queue a batch of transactions, both pending and queued + txs := types.Transactions{} + + txs = append(txs, dynamicFeeTx(0, 100000, big.NewInt(3), big.NewInt(2), keys[0])) + txs = append(txs, pricedTransaction(1, 100000, big.NewInt(2), keys[0])) + txs = append(txs, dynamicFeeTx(1, 100000, big.NewInt(2), big.NewInt(1), keys[1])) + + ltx := dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[2]) + + // Import the batch and that both pending and queued transactions match up + pool.AddRemotes(txs) // Pend K0:0, K0:1; Que K1:1 + pool.AddLocal(ltx) // +K2:0 => Pend K0:0, K0:1, K2:0; Que K1:1 + + pending, queued := pool.Stats() + if pending != 3 { + t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3) + } + if queued != 1 { + t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1) + } + if err := validateEvents(events, 3); err != nil { + t.Fatalf("original event firing failed: %v", err) + } + if err := validateTxPoolInternals(pool); err != nil { + t.Fatalf("pool internal state corrupted: %v", err) + } + + // Ensure that adding an underpriced transaction fails + tx := dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[1]) + if err := pool.AddRemote(tx); err != ErrUnderpriced { // Pend K0:0, K0:1, K2:0; Que K1:1 + t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced) + } + + // Ensure that adding high priced transactions drops cheap ones, but not own + tx = pricedTransaction(0, 100000, big.NewInt(2), keys[1]) + if err := pool.AddRemote(tx); err != nil { // +K1:0, -K1:1 => Pend K0:0, K0:1, K1:0, K2:0; Que - + t.Fatalf("failed to add well priced transaction: %v", err) + } + + tx = pricedTransaction(2, 100000, big.NewInt(3), keys[1]) + if err := pool.AddRemote(tx); err != nil { // +K1:2, -K0:1 => Pend K0:0 K1:0, K2:0; Que K1:2 + t.Fatalf("failed to add well priced transaction: %v", err) + } + tx = dynamicFeeTx(3, 100000, big.NewInt(4), big.NewInt(1), keys[1]) + if err := pool.AddRemote(tx); err != nil { // +K1:3, -K1:0 => Pend K0:0 K2:0; Que K1:2 K1:3 + t.Fatalf("failed to add well priced transaction: %v", err) + } + pending, queued = pool.Stats() + if pending != 2 { + t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) + } + if queued != 2 { + t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2) + } + if err := validateEvents(events, 1); err != nil { + t.Fatalf("additional event firing failed: %v", err) + } + if err := validateTxPoolInternals(pool); err != nil { + t.Fatalf("pool internal state corrupted: %v", err) + } + // Ensure that adding local transactions can push out even higher priced ones + ltx = dynamicFeeTx(1, 100000, big.NewInt(1), big.NewInt(0), keys[2]) + if err := pool.AddLocal(ltx); err != nil { + t.Fatalf("failed to append underpriced local transaction: %v", err) + } + ltx = dynamicFeeTx(0, 100000, big.NewInt(1), big.NewInt(0), keys[3]) + if err := pool.AddLocal(ltx); err != nil { + t.Fatalf("failed to add new underpriced local transaction: %v", err) + } + pending, queued = pool.Stats() + if pending != 3 { + t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3) + } + if queued != 1 { + t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1) + } + if err := validateEvents(events, 2); err != nil { + t.Fatalf("local event firing failed: %v", err) + } + if err := validateTxPoolInternals(pool); err != nil { + t.Fatalf("pool internal state corrupted: %v", err) + } +} + +// Tests whether highest fee cap transaction is retained after a batch of high effective +// tip transactions are added and vice versa +func TestDualHeapEviction(t *testing.T) { + t.Parallel() + + pool, _ := setupTxPoolWithConfig(eip1559Config) + defer pool.Stop() + + pool.config.GlobalSlots = 10 + pool.config.GlobalQueue = 10 + + var ( + highTip, highCap *types.Transaction + baseFee int + ) + + check := func(tx *types.Transaction, name string) { + if pool.all.GetRemote(tx.Hash()) == nil { + t.Fatalf("highest %s transaction evicted from the pool", name) + } + } + + add := func(urgent bool) { + txs := make([]*types.Transaction, 20) + for i := range txs { + // Create a test accounts and fund it + key, _ := crypto.GenerateKey() + testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000000)) + if urgent { + txs[i] = dynamicFeeTx(0, 100000, big.NewInt(int64(baseFee+1+i)), big.NewInt(int64(1+i)), key) + highTip = txs[i] + } else { + txs[i] = dynamicFeeTx(0, 100000, big.NewInt(int64(baseFee+200+i)), big.NewInt(1), key) + highCap = txs[i] + } + } + pool.AddRemotes(txs) + pending, queued := pool.Stats() + if pending+queued != 20 { + t.Fatalf("transaction count mismatch: have %d, want %d", pending+queued, 20) + } + } + + add(false) + for baseFee = 0; baseFee <= 1000; baseFee += 100 { + pool.priced.SetBaseFee(big.NewInt(int64(baseFee))) + add(true) + check(highCap, "fee cap") + add(false) + check(highTip, "effective tip") + } + + if err := validateTxPoolInternals(pool); err != nil { + t.Fatalf("pool internal state corrupted: %v", err) + } +} + // Tests that the pool rejects duplicate transactions. func TestTransactionDeduplication(t *testing.T) { t.Parallel() @@ -1648,7 +2008,7 @@ func TestTransactionDeduplication(t *testing.T) { // Create a test account to add transactions with key, _ := crypto.GenerateKey() - pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000)) + testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000)) // Create a batch of transactions and add a few of them txs := make([]*types.Transaction, common.LimitThresholdNonceInQueue) @@ -1720,7 +2080,7 @@ func TestTransactionReplacement(t *testing.T) { // Create a test account to add transactions with key, _ := crypto.GenerateKey() - pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000)) + testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000)) // Add pending transactions, ensuring the minimum price bump is enforced for replacement (for ultra low prices too) price := int64(100) @@ -1781,6 +2141,116 @@ func TestTransactionReplacement(t *testing.T) { } } +// Tests that the pool rejects replacement dynamic fee transactions that don't +// meet the minimum price bump required. +func TestTransactionReplacementDynamicFee(t *testing.T) { + t.Parallel() + + // Create the pool to test the pricing enforcement with + pool, key := setupTxPoolWithConfig(eip1559Config) + defer pool.Stop() + testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000)) + + // Keep track of transaction events to ensure all executables get announced + events := make(chan NewTxsEvent, 32) + sub := pool.txFeed.Subscribe(events) + defer sub.Unsubscribe() + + // Add pending transactions, ensuring the minimum price bump is enforced for replacement (for ultra low prices too) + feeCap := int64(100) + feeCapThreshold := (feeCap * (100 + int64(testTxPoolConfig.PriceBump))) / 100 + tip := int64(60) + tipThreshold := (tip * (100 + int64(testTxPoolConfig.PriceBump))) / 100 + + // Run the following identical checks for both the pending and queue pools: + // 1. Send initial tx => accept + // 2. Don't bump tip or fee cap => discard + // 3. Bump both more than min => accept + // 4. Check events match expected (2 new executable txs during pending, 0 during queue) + // 5. Send new tx with larger tip and feeCap => accept + // 6. Bump tip max allowed so it's still underpriced => discard + // 7. Bump fee cap max allowed so it's still underpriced => discard + // 8. Bump tip min for acceptance => discard + // 9. Bump feecap min for acceptance => discard + // 10. Bump feecap and tip min for acceptance => accept + // 11. Check events match expected (2 new executable txs during pending, 0 during queue) + stages := []string{"pending", "queued"} + for _, stage := range stages { + // Since state is empty, 0 nonce txs are "executable" and can go + // into pending immediately. 2 nonce txs are "happed + nonce := uint64(0) + if stage == "queued" { + nonce = 2 + } + + // 1. Send initial tx => accept + tx := dynamicFeeTx(nonce, 100000, big.NewInt(2), big.NewInt(1), key) + if err := pool.addRemoteSync(tx); err != nil { + t.Fatalf("failed to add original cheap %s transaction: %v", stage, err) + } + // 2. Don't bump tip or feecap => discard + tx = dynamicFeeTx(nonce, 100001, big.NewInt(2), big.NewInt(1), key) + if err := pool.AddRemote(tx); err != ErrReplaceUnderpriced { + t.Fatalf("original cheap %s transaction replacement error mismatch: have %v, want %v", stage, err, ErrReplaceUnderpriced) + } + // 3. Bump both more than min => accept + tx = dynamicFeeTx(nonce, 100000, big.NewInt(3), big.NewInt(2), key) + if err := pool.AddRemote(tx); err != nil { + t.Fatalf("failed to replace original cheap %s transaction: %v", stage, err) + } + // 4. Check events match expected (2 new executable txs during pending, 0 during queue) + count := 2 + if stage == "queued" { + count = 0 + } + if err := validateEvents(events, count); err != nil { + t.Fatalf("cheap %s replacement event firing failed: %v", stage, err) + } + // 5. Send new tx with larger tip and feeCap => accept + tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCap), big.NewInt(tip), key) + if err := pool.addRemoteSync(tx); err != nil { + t.Fatalf("failed to add original proper %s transaction: %v", stage, err) + } + // 6. Bump tip max allowed so it's still underpriced => discard + tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCap), big.NewInt(tipThreshold-1), key) + if err := pool.AddRemote(tx); err != ErrReplaceUnderpriced { + t.Fatalf("original proper %s transaction replacement error mismatch: have %v, want %v", stage, err, ErrReplaceUnderpriced) + } + // 7. Bump fee cap max allowed so it's still underpriced => discard + tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCapThreshold-1), big.NewInt(tip), key) + if err := pool.AddRemote(tx); err != ErrReplaceUnderpriced { + t.Fatalf("original proper %s transaction replacement error mismatch: have %v, want %v", stage, err, ErrReplaceUnderpriced) + } + // 8. Bump tip min for acceptance => accept + tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCap), big.NewInt(tipThreshold), key) + if err := pool.AddRemote(tx); err != ErrReplaceUnderpriced { + t.Fatalf("original proper %s transaction replacement error mismatch: have %v, want %v", stage, err, ErrReplaceUnderpriced) + } + // 9. Bump fee cap min for acceptance => accept + tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCapThreshold), big.NewInt(tip), key) + if err := pool.AddRemote(tx); err != ErrReplaceUnderpriced { + t.Fatalf("original proper %s transaction replacement error mismatch: have %v, want %v", stage, err, ErrReplaceUnderpriced) + } + // 10. Check events match expected (3 new executable txs during pending, 0 during queue) + tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCapThreshold), big.NewInt(tipThreshold), key) + if err := pool.AddRemote(tx); err != nil { + t.Fatalf("failed to replace original cheap %s transaction: %v", stage, err) + } + // 11. Check events match expected (3 new executable txs during pending, 0 during queue) + count = 2 + if stage == "queued" { + count = 0 + } + if err := validateEvents(events, count); err != nil { + t.Fatalf("replacement %s event firing failed: %v", stage, err) + } + } + + if err := validateTxPoolInternals(pool); err != nil { + t.Fatalf("pool internal state corrupted: %v", err) + } +} + // Tests that local transactions are journaled to disk, but remote transactions // get discarded between restarts. func TestTransactionJournaling(t *testing.T) { testTransactionJournaling(t, false) } @@ -1817,8 +2287,8 @@ func testTransactionJournaling(t *testing.T, nolocals bool) { local, _ := crypto.GenerateKey() remote, _ := crypto.GenerateKey() - pool.currentState.AddBalance(crypto.PubkeyToAddress(local.PublicKey), big.NewInt(1000000000)) - pool.currentState.AddBalance(crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(1000000000)) + testAddBalance(pool, crypto.PubkeyToAddress(local.PublicKey), big.NewInt(1000000000)) + testAddBalance(pool, crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(1000000000)) // Add three local and a remote transactions and ensure they are queued up if err := pool.AddLocal(pricedTransaction(0, 100000, big.NewInt(1), local)); err != nil { @@ -1912,7 +2382,7 @@ func TestTransactionStatusCheck(t *testing.T) { keys := make([]*ecdsa.PrivateKey, 3) for i := 0; i < len(keys); i++ { keys[i], _ = crypto.GenerateKey() - pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) + testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) } // Generate and queue a batch of transactions, both pending and queued txs := types.Transactions{} @@ -1982,7 +2452,7 @@ func benchmarkPendingDemotion(b *testing.B, size int) { defer pool.Stop() account := crypto.PubkeyToAddress(key.PublicKey) - pool.currentState.AddBalance(account, big.NewInt(1000000)) + testAddBalance(pool, account, big.NewInt(1000000)) for i := 0; i < size; i++ { tx := transaction(uint64(i), 100000, key) @@ -2007,7 +2477,7 @@ func benchmarkFuturePromotion(b *testing.B, size int) { defer pool.Stop() account := crypto.PubkeyToAddress(key.PublicKey) - pool.currentState.AddBalance(account, big.NewInt(1000000)) + testAddBalance(pool, account, big.NewInt(1000000)) for i := 0; i < size; i++ { tx := transaction(uint64(1+i), 100000, key) @@ -2035,7 +2505,7 @@ func benchmarkPoolBatchInsert(b *testing.B, size int, local bool) { defer pool.Stop() account := crypto.PubkeyToAddress(key.PublicKey) - pool.currentState.AddBalance(account, big.NewInt(1000000)) + testAddBalance(pool, account, big.NewInt(1000000)) batches := make([]types.Transactions, b.N) for i := 0; i < b.N; i++ { @@ -2076,13 +2546,13 @@ func BenchmarkInsertRemoteWithAllLocals(b *testing.B) { for i := 0; i < b.N; i++ { b.StopTimer() pool, _ := setupTxPool() - pool.currentState.AddBalance(account, big.NewInt(100000000)) + testAddBalance(pool, account, big.NewInt(100000000)) for _, local := range locals { pool.AddLocal(local) } b.StartTimer() // Assign a high enough balance for testing - pool.currentState.AddBalance(remoteAddr, big.NewInt(100000000)) + testAddBalance(pool, remoteAddr, big.NewInt(100000000)) for i := 0; i < len(remotes); i++ { pool.AddRemotes([]*types.Transaction{remotes[i]}) } diff --git a/core/types/transaction.go b/core/types/transaction.go index e038ab0da69c..453d0ba43c86 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -321,33 +321,67 @@ func (tx *Transaction) From() *common.Address { return &from } +// Cost returns gas * gasPrice + value. +func (tx *Transaction) Cost() *big.Int { + total := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas())) + total.Add(total, tx.Value()) + return total +} + +// RawSignatureValues returns the V, R, S signature values of the transaction. +// The return values should not be modified by the caller. +func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) { + return tx.inner.rawSignatureValues() +} + +// FeeCapCmp compares the fee cap of two transactions. +func (tx *Transaction) FeeCapCmp(other *Transaction) int { + return tx.inner.feeCap().Cmp(other.inner.feeCap()) +} + +// FeeCapIntCmp compares the fee cap of the transaction against the given fee cap. +func (tx *Transaction) FeeCapIntCmp(other *big.Int) int { + return tx.inner.feeCap().Cmp(other) +} + +// TipCmp compares the tip of two transactions. +func (tx *Transaction) TipCmp(other *Transaction) int { + return tx.inner.tip().Cmp(other.inner.tip()) +} + +// TipIntCmp compares the tip of the transaction against the given tip. +func (tx *Transaction) TipIntCmp(other *big.Int) int { + return tx.inner.tip().Cmp(other) +} + // EffectiveTip returns the effective miner tip for the given base fee. -// Returns error in case of a negative effective miner tip. +// Note: if the effective tip is negative, this method returns both error +// the actual negative value, _and_ ErrFeeCapTooLow func (tx *Transaction) EffectiveTip(baseFee *big.Int) (*big.Int, error) { if baseFee == nil { return tx.Tip(), nil } + var err error feeCap := tx.FeeCap() if feeCap.Cmp(baseFee) == -1 { - return nil, ErrFeeCapTooLow + err = ErrFeeCapTooLow } - return math.BigMin(tx.Tip(), feeCap.Sub(feeCap, baseFee)), nil -} - -// RawSignatureValues returns the V, R, S signature values of the transaction. -// The return values should not be modified by the caller. -func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) { - return tx.inner.rawSignatureValues() + return math.BigMin(tx.Tip(), feeCap.Sub(feeCap, baseFee)), err } -// GasPriceCmp compares the gas prices of two transactions. -func (tx *Transaction) GasPriceCmp(other *Transaction) int { - return tx.inner.gasPrice().Cmp(other.inner.gasPrice()) +// EffectiveTipValue is identical to EffectiveTip, but does not return an +// error in case the effective tip is negative +func (tx *Transaction) EffectiveTipValue(baseFee *big.Int) *big.Int { + effectiveTip, _ := tx.EffectiveTip(baseFee) + return effectiveTip } -// GasPriceIntCmp compares the gas price of the transaction against the given price. -func (tx *Transaction) GasPriceIntCmp(other *big.Int) int { - return tx.inner.gasPrice().Cmp(other) +// EffectiveTipCmp compares the effective tip of two transactions assuming the given base fee. +func (tx *Transaction) EffectiveTipCmp(other *Transaction, baseFee *big.Int) int { + if baseFee == nil { + return tx.TipCmp(other) + } + return tx.EffectiveTipValue(baseFee).Cmp(other.EffectiveTipValue(baseFee)) } // Hash returns the transaction hash. @@ -426,13 +460,6 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e return &Transaction{inner: cpy, time: tx.time}, nil } -// Cost returns gas * gasPrice + value. -func (tx *Transaction) Cost() *big.Int { - total := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas())) - total.Add(total, tx.Value()) - return total -} - // TxCost returns gas * gasPrice + value. func (tx *Transaction) TxCost(number *big.Int) *big.Int { total := new(big.Int).Mul(common.GetGasPrice(number), new(big.Int).SetUint64(tx.Gas())) diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index fe3a4b511126..e860e9a69f91 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -194,7 +194,7 @@ type transactionsByGasPrice []*types.Transaction func (t transactionsByGasPrice) Len() int { return len(t) } func (t transactionsByGasPrice) Swap(i, j int) { t[i], t[j] = t[j], t[i] } -func (t transactionsByGasPrice) Less(i, j int) bool { return t[i].GasPriceCmp(t[j]) < 0 } +func (t transactionsByGasPrice) Less(i, j int) bool { return t[i].FeeCapCmp(t[j]) < 0 } // getBlockPrices calculates the lowest transaction gas price in a given block // and sends it to the result channel. If the block is empty or all transactions From 4f0317cb1fa0a664852fddad5c5717f3cc74b98c Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 17 May 2024 17:49:58 +0800 Subject: [PATCH 083/479] core: add new eip-1559 tx constraints (#22970) --- core/chain_makers.go | 15 ++ core/error.go | 12 ++ core/state_processor_test.go | 290 +++++++++++++++++++++++++++++++++++ core/state_transition.go | 34 ++-- core/tx_pool.go | 11 +- core/tx_pool_test.go | 20 +++ 6 files changed, 368 insertions(+), 14 deletions(-) create mode 100644 core/state_processor_test.go diff --git a/core/chain_makers.go b/core/chain_makers.go index cd8ff5443dce..e5cab11ecb0e 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -328,3 +328,18 @@ func makeBlockChain(parent *types.Block, n int, engine consensus.Engine, db ethd }) return blocks } + +type fakeChainReader struct { + config *params.ChainConfig +} + +// Config returns the chain configuration. +func (cr *fakeChainReader) Config() *params.ChainConfig { + return cr.config +} + +func (cr *fakeChainReader) CurrentHeader() *types.Header { return nil } +func (cr *fakeChainReader) GetHeaderByNumber(number uint64) *types.Header { return nil } +func (cr *fakeChainReader) GetHeaderByHash(hash common.Hash) *types.Header { return nil } +func (cr *fakeChainReader) GetHeader(hash common.Hash, number uint64) *types.Header { return nil } +func (cr *fakeChainReader) GetBlock(hash common.Hash, number uint64) *types.Block { return nil } diff --git a/core/error.go b/core/error.go index fd45fdc73d3a..192d41778f15 100644 --- a/core/error.go +++ b/core/error.go @@ -54,6 +54,18 @@ var ( // ErrGasUintOverflow is returned when calculating gas usage. ErrGasUintOverflow = errors.New("gas uint64 overflow") + // ErrTipAboveFeeCap is a sanity error to ensure no one is able to specify a + // transaction with a tip higher than the total fee cap. + ErrTipAboveFeeCap = errors.New("tip higher than fee cap") + + // ErrTipVeryHigh is a sanity error to avoid extremely big numbers specified + // in the tip field. + ErrTipVeryHigh = errors.New("tip higher than 2^256-1") + + // ErrFeeCapVeryHigh is a sanity error to avoid extremely big numbers specified + // in the fee cap field. + ErrFeeCapVeryHigh = errors.New("fee cap higher than 2^256-1") + // ErrFeeCapTooLow is returned if the transaction fee cap is less than the // the base fee of the block. ErrFeeCapTooLow = errors.New("fee cap less than block base fee") diff --git a/core/state_processor_test.go b/core/state_processor_test.go new file mode 100644 index 000000000000..1327471c6a90 --- /dev/null +++ b/core/state_processor_test.go @@ -0,0 +1,290 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "math/big" + "testing" + + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/consensus" + "github.com/XinFinOrg/XDPoSChain/consensus/ethash" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" + "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/core/vm" + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/params" + "golang.org/x/crypto/sha3" +) + +// TestStateProcessorErrors tests the output from the 'core' errors +// as defined in core/error.go. These errors are generated when the +// blockchain imports bad blocks, meaning blocks which have valid headers but +// contain invalid transactions +func TestStateProcessorErrors(t *testing.T) { + var ( + config = ¶ms.ChainConfig{ + 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), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + Eip1559Block: big.NewInt(0), + Ethash: new(params.EthashConfig), + } + signer = types.LatestSigner(config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + ) + var makeTx = func(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *types.Transaction { + tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data) + signedTx, err := types.SignTx(tx, signer, testKey) + if err != nil { + t.Fatalf("fail to sign tx: %v, err: %v", tx, err) + } + return signedTx + } + var mkDynamicTx = func(nonce uint64, to common.Address, gasLimit uint64, tip, feeCap *big.Int) *types.Transaction { + tx, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{ + Nonce: nonce, + Tip: tip, + FeeCap: feeCap, + Gas: gasLimit, + To: &to, + Value: big.NewInt(0), + }), signer, testKey) + return tx + } + { // Tests against a 'recent' chain definition + var ( + db = rawdb.NewMemoryDatabase() + gspec = &Genesis{ + Config: config, + Alloc: GenesisAlloc{ + common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + }, + } + genesis = gspec.MustCommit(db) + blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}) + ) + defer blockchain.Stop() + bigNumber := new(big.Int).SetBytes(common.FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) + tooBigNumber := new(big.Int).Set(bigNumber) + tooBigNumber.Add(tooBigNumber, common.Big1) + for i, tt := range []struct { + txs []*types.Transaction + want string + }{ + { // ErrNonceTooLow + txs: []*types.Transaction{ + makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil), + makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil), + }, + want: "nonce too low: address xdc71562b71999873DB5b286dF957af199Ec94617F7, tx: 0 state: 1", + }, + { // ErrNonceTooHigh + txs: []*types.Transaction{ + makeTx(100, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil), + }, + want: "nonce too high: address xdc71562b71999873DB5b286dF957af199Ec94617F7, tx: 100 state: 0", + }, + { // ErrGasLimitReached + txs: []*types.Transaction{ + makeTx(0, common.Address{}, big.NewInt(0), 21000000, big.NewInt(875000000), nil), + }, + want: "gas limit reached", + }, + { // ErrInsufficientFundsForTransfer + txs: []*types.Transaction{ + makeTx(0, common.Address{}, big.NewInt(1000000000000000000), params.TxGas, big.NewInt(875000000), nil), + }, + want: "insufficient funds for transfer: address xdc71562b71999873DB5b286dF957af199Ec94617F7", + }, + { // ErrInsufficientFunds + txs: []*types.Transaction{ + makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(900000000000000000), nil), + }, + want: "insufficient funds for gas * price + value: address xdc71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 18900000000000000000000", + }, + // ErrGasUintOverflow + // One missing 'core' error is ErrGasUintOverflow: "gas uint64 overflow", + // In order to trigger that one, we'd have to allocate a _huge_ chunk of data, such that the + // multiplication len(data) +gas_per_byte overflows uint64. Not testable at the moment + { // ErrIntrinsicGas + txs: []*types.Transaction{ + makeTx(0, common.Address{}, big.NewInt(0), params.TxGas-1000, big.NewInt(875000000), nil), + }, + want: "intrinsic gas too low: have 20000, want 21000", + }, + { // ErrGasLimitReached + txs: []*types.Transaction{ + makeTx(0, common.Address{}, big.NewInt(0), params.TxGas*1000, big.NewInt(875000000), nil), + }, + want: "gas limit reached", + }, + { // ErrFeeCapTooLow + txs: []*types.Transaction{ + mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(0), big.NewInt(0)), + }, + want: "fee cap less than block base fee: address xdc71562b71999873DB5b286dF957af199Ec94617F7, feeCap: 0 baseFee: 875000000", + }, + { // ErrTipVeryHigh + txs: []*types.Transaction{ + mkDynamicTx(0, common.Address{}, params.TxGas, tooBigNumber, big.NewInt(1)), + }, + want: "tip higher than 2^256-1: address xdc71562b71999873DB5b286dF957af199Ec94617F7, tip bit length: 257", + }, + { // ErrFeeCapVeryHigh + txs: []*types.Transaction{ + mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(1), tooBigNumber), + }, + want: "fee cap higher than 2^256-1: address xdc71562b71999873DB5b286dF957af199Ec94617F7, feeCap bit length: 257", + }, + { // ErrTipAboveFeeCap + txs: []*types.Transaction{ + mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(2), big.NewInt(1)), + }, + want: "tip higher than fee cap: address xdc71562b71999873DB5b286dF957af199Ec94617F7, tip: 1, feeCap: 2", + }, + { // ErrInsufficientFunds + // Available balance: 1000000000000000000 + // Effective cost: 18375000021000 + // FeeCap * gas: 1050000000000000000 + // This test is designed to have the effective cost be covered by the balance, but + // the extended requirement on FeeCap*gas < balance to fail + txs: []*types.Transaction{ + mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(1), big.NewInt(50000000000000)), + }, + want: "insufficient funds for gas * price + value: address xdc71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 1050000000000000000", + }, + { // Another ErrInsufficientFunds, this one to ensure that feecap/tip of max u256 is allowed + txs: []*types.Transaction{ + mkDynamicTx(0, common.Address{}, params.TxGas, bigNumber, bigNumber), + }, + want: "insufficient funds for gas * price + value: address xdc71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 2431633873983640103894990685182446064918669677978451844828609264166175722438635000", + }, + }[8:] { + block := GenerateBadBlock(t, genesis, ethash.NewFaker(), tt.txs, gspec.Config) + _, err := blockchain.InsertChain(types.Blocks{block}) + if err == nil { + t.Fatal("block imported without errors") + } + if have, want := err.Error(), tt.want; have != want { + t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want) + } + } + } + + // One final error is ErrTxTypeNotSupported. For this, we need an older chain + { + var ( + db = rawdb.NewMemoryDatabase() + gspec = &Genesis{ + Config: ¶ms.ChainConfig{ + 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), + }, + Alloc: GenesisAlloc{ + common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + }, + } + genesis = gspec.MustCommit(db) + blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}) + ) + defer blockchain.Stop() + for i, tt := range []struct { + txs []*types.Transaction + want string + }{ + { // ErrTxTypeNotSupported + txs: []*types.Transaction{ + mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)), + }, + want: "transaction type not supported", + }, + } { + block := GenerateBadBlock(t, genesis, ethash.NewFaker(), tt.txs, gspec.Config) + _, err := blockchain.InsertChain(types.Blocks{block}) + if err == nil { + t.Fatal("block imported without errors") + } + if have, want := err.Error(), tt.want; have != want { + t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want) + } + } + } +} + +// GenerateBadBlock constructs a "block" which contains the transactions. The transactions are not expected to be +// valid, and no proper post-state can be made. But from the perspective of the blockchain, the block is sufficiently +// valid to be considered for import: +// - valid pow (fake), ancestry, difficulty, gaslimit etc +func GenerateBadBlock(t *testing.T, parent *types.Block, engine consensus.Engine, txs types.Transactions, config *params.ChainConfig) *types.Block { + header := &types.Header{ + ParentHash: parent.Hash(), + Coinbase: parent.Coinbase(), + Difficulty: engine.CalcDifficulty(&fakeChainReader{config}, parent.Time().Uint64()+10, &types.Header{ + Number: parent.Number(), + Time: parent.Time(), + Difficulty: parent.Difficulty(), + UncleHash: parent.UncleHash(), + }), + GasLimit: parent.GasLimit(), + Number: new(big.Int).Add(parent.Number(), common.Big1), + Time: new(big.Int).SetUint64(parent.Time().Uint64() + 10), + UncleHash: types.EmptyUncleHash, + } + if config.IsEIP1559(header.Number) { + header.BaseFee = common.BaseFee + } + var receipts []*types.Receipt + // The post-state result doesn't need to be correct (this is a bad block), but we do need something there + // Preferably something unique. So let's use a combo of blocknum + txhash + hasher := sha3.NewLegacyKeccak256() + hasher.Write(header.Number.Bytes()) + var cumulativeGas uint64 + for _, tx := range txs { + txh := tx.Hash() + hasher.Write(txh[:]) + receipt := types.NewReceipt(nil, false, cumulativeGas+tx.Gas()) + receipt.TxHash = tx.Hash() + receipt.GasUsed = tx.Gas() + receipts = append(receipts, receipt) + cumulativeGas += tx.Gas() + } + header.Root = common.BytesToHash(hasher.Sum(nil)) + // Assemble and return the final block for sealing + return types.NewBlock(header, txs, nil, receipts) +} diff --git a/core/state_transition.go b/core/state_transition.go index d1a1db534c4f..04b8bcdf28a8 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -178,15 +178,17 @@ func (st *StateTransition) to() vm.AccountRef { } func (st *StateTransition) buyGas() error { - var ( - state = st.state - balanceTokenFee = st.balanceTokenFee() - from = st.from() - ) - mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice) + mgval := new(big.Int).SetUint64(st.msg.Gas()) + mgval = mgval.Mul(mgval, st.gasPrice) + balanceTokenFee := st.balanceTokenFee() if balanceTokenFee == nil { - if state.GetBalance(from.Address()).Cmp(mgval) < 0 { - return errInsufficientBalanceForGas + balanceCheck := mgval + if st.feeCap != nil { + balanceCheck = new(big.Int).SetUint64(st.msg.Gas()) + balanceCheck = balanceCheck.Mul(balanceCheck, st.feeCap) + } + if have, want := st.state.GetBalance(st.msg.From()), balanceCheck; have.Cmp(want) < 0 { + return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), have, want) } } else if balanceTokenFee.Cmp(mgval) < 0 { return errInsufficientBalanceForGas @@ -198,7 +200,7 @@ func (st *StateTransition) buyGas() error { st.initialGas = st.msg.Gas() if balanceTokenFee == nil { - state.SubBalance(from.Address(), mgval) + st.state.SubBalance(st.msg.From(), mgval) } return nil } @@ -221,6 +223,18 @@ func (st *StateTransition) preCheck() error { } // Make sure that transaction feeCap is greater than the baseFee (post london) if st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber) { + if l := st.feeCap.BitLen(); l > 256 { + return fmt.Errorf("%w: address %v, feeCap bit length: %d", ErrFeeCapVeryHigh, + msg.From().Hex(), l) + } + if l := st.tip.BitLen(); l > 256 { + return fmt.Errorf("%w: address %v, tip bit length: %d", ErrTipVeryHigh, + msg.From().Hex(), l) + } + if st.feeCap.Cmp(st.tip) < 0 { + return fmt.Errorf("%w: address %v, tip: %s, feeCap: %s", ErrTipAboveFeeCap, + msg.From().Hex(), st.feeCap, st.tip) + } // This will panic if baseFee is nil, but basefee presence is verified // as part of header validation. if st.feeCap.Cmp(st.evm.Context.BaseFee) < 0 { @@ -257,7 +271,7 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG // Check clauses 1-3, buy gas if everything is correct if err = st.preCheck(); err != nil { - return + return nil, 0, false, err, nil } msg := st.msg sender := st.from() // err checked in preCheck diff --git a/core/tx_pool.go b/core/tx_pool.go index 8d1530e121ae..6b010c292af2 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -106,10 +106,6 @@ var ( ErrDuplicateSpecialTransaction = errors.New("duplicate a special transaction") ErrMinDeploySMC = errors.New("smart contract creation cost is under allowance") - - // ErrTipAboveFeeCap is a sanity error to ensure no one is able to specify a - // transaction with a tip higher than the total fee cap. - ErrTipAboveFeeCap = errors.New("tip higher than fee cap") ) var ( @@ -616,6 +612,13 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { if pool.currentMaxGas < tx.Gas() { return ErrGasLimit } + // Sanity check for extremely large numbers + if tx.FeeCap().BitLen() > 256 { + return ErrFeeCapVeryHigh + } + if tx.Tip().BitLen() > 256 { + return ErrTipVeryHigh + } // Ensure feeCap is less than or equal to tip. if tx.FeeCapIntCmp(tx.Tip()) < 0 { return ErrTipAboveFeeCap diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index f73d6f92bc8c..3a3766c26cbf 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -416,6 +416,26 @@ func TestTransactionTipAboveFeeCap(t *testing.T) { } } +func TestTransactionVeryHighValues(t *testing.T) { + t.Parallel() + + pool, key := setupTxPoolWithConfig(eip1559Config) + defer pool.Stop() + + veryBigNumber := big.NewInt(1) + veryBigNumber.Lsh(veryBigNumber, 300) + + tx := dynamicFeeTx(0, 100, big.NewInt(1), veryBigNumber, key) + if err := pool.AddRemote(tx); err != ErrTipVeryHigh { + t.Error("expected", ErrTipVeryHigh, "got", err) + } + + tx2 := dynamicFeeTx(0, 100, veryBigNumber, big.NewInt(1), key) + if err := pool.AddRemote(tx2); err != ErrFeeCapVeryHigh { + t.Error("expected", ErrFeeCapVeryHigh, "got", err) + } +} + func TestTransactionChainFork(t *testing.T) { t.Parallel() From bfd1c0c9e075064ae1b0a5b79403c904b0424e47 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 21 May 2024 19:21:50 +0800 Subject: [PATCH 084/479] core, eth, internal, les: RPC methods and fields for EIP 1559 (#22964) --- core/types/block.go | 2 +- core/types/gen_header_json.go | 8 ++ core/types/transaction_marshalling.go | 48 ++++---- eth/api_backend.go | 8 +- eth/api_tracer.go | 5 +- eth/gasprice/gasprice.go | 108 +++++++++++------- eth/gasprice/gasprice_test.go | 157 ++++++++++++++++++++++++++ ethstats/ethstats.go | 7 +- internal/ethapi/api.go | 113 +++++++++++++----- internal/ethapi/backend.go | 3 +- internal/ethapi/transaction_args.go | 130 +++++++++++++++++---- internal/jsre/deps/bindata.go | 117 ++++++++----------- internal/jsre/deps/web3.js | 11 +- internal/web3ext/web3ext.go | 5 + les/api_backend.go | 8 +- 15 files changed, 534 insertions(+), 196 deletions(-) create mode 100644 eth/gasprice/gasprice_test.go diff --git a/core/types/block.go b/core/types/block.go index aad990880fbd..57c470baa16c 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -83,7 +83,7 @@ type Header struct { Penalties []byte `json:"penalties" gencodec:"required"` // BaseFee was added by EIP-1559 and is ignored in legacy headers. - BaseFee *big.Int `json:"baseFee" rlp:"optional"` + BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"` } // field type overrides for gencodec diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go index dc48332426dc..ebe660dba9c3 100644 --- a/core/types/gen_header_json.go +++ b/core/types/gen_header_json.go @@ -13,6 +13,7 @@ import ( var _ = (*headerMarshaling)(nil) +// MarshalJSON marshals as JSON. func (h Header) MarshalJSON() ([]byte, error) { type Header struct { ParentHash common.Hash `json:"parentHash" gencodec:"required"` @@ -30,6 +31,7 @@ func (h Header) MarshalJSON() ([]byte, error) { Extra hexutil.Bytes `json:"extraData" gencodec:"required"` MixDigest common.Hash `json:"mixHash" gencodec:"required"` Nonce BlockNonce `json:"nonce" gencodec:"required"` + BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` Hash common.Hash `json:"hash"` } var enc Header @@ -48,10 +50,12 @@ func (h Header) MarshalJSON() ([]byte, error) { enc.Extra = h.Extra enc.MixDigest = h.MixDigest enc.Nonce = h.Nonce + enc.BaseFee = (*hexutil.Big)(h.BaseFee) enc.Hash = h.Hash() return json.Marshal(&enc) } +// UnmarshalJSON unmarshals from JSON. func (h *Header) UnmarshalJSON(input []byte) error { type Header struct { ParentHash *common.Hash `json:"parentHash" gencodec:"required"` @@ -69,6 +73,7 @@ func (h *Header) UnmarshalJSON(input []byte) error { Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` MixDigest *common.Hash `json:"mixHash" gencodec:"required"` Nonce *BlockNonce `json:"nonce" gencodec:"required"` + BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` } var dec Header if err := json.Unmarshal(input, &dec); err != nil { @@ -134,5 +139,8 @@ func (h *Header) UnmarshalJSON(input []byte) error { return errors.New("missing required field 'nonce' for Header") } h.Nonce = *dec.Nonce + if dec.BaseFee != nil { + h.BaseFee = (*big.Int)(dec.BaseFee) + } return nil } diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go index f4a47c7ee640..80b74d4b41ef 100644 --- a/core/types/transaction_marshalling.go +++ b/core/types/transaction_marshalling.go @@ -1,3 +1,19 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + package types import ( @@ -16,8 +32,6 @@ type txJSON struct { // Common transaction fields: Nonce *hexutil.Uint64 `json:"nonce"` GasPrice *hexutil.Big `json:"gasPrice"` - FeeCap *hexutil.Big `json:"feeCap"` - Tip *hexutil.Big `json:"tip"` MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"` MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"` Gas *hexutil.Uint64 `json:"gas"` @@ -72,8 +86,8 @@ func (t *Transaction) MarshalJSON() ([]byte, error) { enc.AccessList = &tx.AccessList enc.Nonce = (*hexutil.Uint64)(&tx.Nonce) enc.Gas = (*hexutil.Uint64)(&tx.Gas) - enc.FeeCap = (*hexutil.Big)(tx.FeeCap) - enc.Tip = (*hexutil.Big)(tx.Tip) + enc.MaxFeePerGas = (*hexutil.Big)(tx.FeeCap) + enc.MaxPriorityFeePerGas = (*hexutil.Big)(tx.Tip) enc.Value = (*hexutil.Big)(tx.Value) enc.Data = (*hexutil.Bytes)(&tx.Data) enc.To = t.To() @@ -210,26 +224,14 @@ func (t *Transaction) UnmarshalJSON(input []byte) error { return errors.New("missing required field 'nonce' in transaction") } itx.Nonce = uint64(*dec.Nonce) - switch { - case dec.Tip == nil && dec.MaxPriorityFeePerGas == nil: - return errors.New("at least one of 'tip' or 'maxPriorityFeePerGas' must be defined") - case dec.Tip != nil && dec.MaxPriorityFeePerGas != nil: - return errors.New("only one of 'tip' or 'maxPriorityFeePerGas' may be defined") - case dec.Tip != nil && dec.MaxPriorityFeePerGas == nil: - itx.Tip = (*big.Int)(dec.Tip) - case dec.Tip == nil && dec.MaxPriorityFeePerGas != nil: - itx.Tip = (*big.Int)(dec.MaxPriorityFeePerGas) - } - switch { - case dec.FeeCap == nil && dec.MaxFeePerGas == nil: - return errors.New("at least one of 'feeCap' or 'maxFeePerGas' must be defined") - case dec.FeeCap != nil && dec.MaxFeePerGas != nil: - return errors.New("only one of 'feeCap' or 'maxFeePerGas' may be defined") - case dec.FeeCap != nil && dec.MaxFeePerGas == nil: - itx.FeeCap = (*big.Int)(dec.FeeCap) - case dec.FeeCap == nil && dec.MaxFeePerGas != nil: - itx.FeeCap = (*big.Int)(dec.MaxFeePerGas) + if dec.MaxPriorityFeePerGas == nil { + return errors.New("missing required field 'maxPriorityFeePerGas' for txdata") + } + itx.Tip = (*big.Int)(dec.MaxPriorityFeePerGas) + if dec.MaxFeePerGas == nil { + return errors.New("missing required field 'maxFeePerGas' for txdata") } + itx.FeeCap = (*big.Int)(dec.MaxFeePerGas) if dec.Gas == nil { return errors.New("missing required field 'gas' for txdata") } diff --git a/eth/api_backend.go b/eth/api_backend.go index 4141402ea818..fb3cefd45572 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -344,8 +344,8 @@ func (b *EthApiBackend) ProtocolVersion() int { return b.eth.EthVersion() } -func (b *EthApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { - return b.gpo.SuggestPrice(ctx) +func (b *EthApiBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { + return b.gpo.SuggestTipCap(ctx) } func (b *EthApiBackend) ChainDb() ethdb.Database { @@ -393,6 +393,10 @@ func (b *EthApiBackend) GetEngine() consensus.Engine { return b.eth.engine } +func (b *EthApiBackend) CurrentHeader() *types.Header { + return b.eth.blockchain.CurrentHeader() +} + func (b *EthApiBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (*state.StateDB, error) { return b.eth.stateAtBlock(block, reexec, base, checkLive) } diff --git a/eth/api_tracer.go b/eth/api_tracer.go index bdb3ed823c6f..6b7d346eabe4 100644 --- a/eth/api_tracer.go +++ b/eth/api_tracer.go @@ -691,7 +691,10 @@ func (api *PrivateDebugAPI) TraceCall(ctx context.Context, args ethapi.Transacti } } // Execute the trace - msg := args.ToMessage(api.eth.ApiBackend, block.Number(), api.eth.ApiBackend.RPCGasCap()) + msg, err := args.ToMessage(api.eth.ApiBackend, block.Number(), api.eth.ApiBackend.RPCGasCap(), block.BaseFee()) + if err != nil { + return nil, err + } vmctx := core.NewEVMBlockContext(block.Header(), api.eth.blockchain, nil) var traceConfig *TraceConfig if config != nil { diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index e860e9a69f91..e868e6fdcf0a 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -31,8 +31,10 @@ import ( const sampleNumber = 3 // Number of transactions sampled in a block -var DefaultMaxPrice = big.NewInt(500 * params.GWei) -var DefaultIgnorePrice = big.NewInt(2 * params.Wei) +var ( + DefaultMaxPrice = big.NewInt(500 * params.GWei) + DefaultIgnorePrice = big.NewInt(2 * params.Wei) +) type Config struct { Blocks int @@ -103,8 +105,13 @@ func NewOracle(backend OracleBackend, params Config) *Oracle { } } -// SuggestPrice returns the recommended gas price. -func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) { +// SuggestTipCap returns a tip cap so that newly created transaction can have a +// very high chance to be included in the following blocks. +// +// Note, for legacy transactions and the legacy eth_gasPrice RPC call, it will be +// necessary to add the basefee to the returned number to fall back to the legacy +// behavior. +func (gpo *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) { head, _ := gpo.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) headHash := head.Hash() @@ -113,7 +120,7 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) { lastHead, lastPrice := gpo.lastHead, gpo.lastPrice gpo.cacheLock.RUnlock() if headHash == lastHead { - return lastPrice, nil + return new(big.Int).Set(lastPrice), nil } gpo.fetchLock.Lock() defer gpo.fetchLock.Unlock() @@ -123,17 +130,17 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) { lastHead, lastPrice = gpo.lastHead, gpo.lastPrice gpo.cacheLock.RUnlock() if headHash == lastHead { - return lastPrice, nil + return new(big.Int).Set(lastPrice), nil } var ( sent, exp int number = head.Number.Uint64() - result = make(chan getBlockPricesResult, gpo.checkBlocks) + result = make(chan results, gpo.checkBlocks) quit = make(chan struct{}) - txPrices []*big.Int + results []*big.Int ) for sent < gpo.checkBlocks && number > 0 { - go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, gpo.ignorePrice, result, quit) + go gpo.getBlockValues(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, gpo.ignorePrice, result, quit) sent++ exp++ number-- @@ -142,93 +149,116 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) { res := <-result if res.err != nil { close(quit) - return lastPrice, res.err + return new(big.Int).Set(lastPrice), res.err } exp-- // Nothing returned. There are two special cases here: // - The block is empty // - All the transactions included are sent by the miner itself. // In these cases, use the latest calculated price for samping. - if len(res.prices) == 0 { - res.prices = []*big.Int{lastPrice} + if len(res.values) == 0 { + res.values = []*big.Int{lastPrice} } // Besides, in order to collect enough data for sampling, if nothing // meaningful returned, try to query more blocks. But the maximum // is 2*checkBlocks. - if len(res.prices) == 1 && len(txPrices)+1+exp < gpo.checkBlocks*2 && number > 0 { - go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, gpo.ignorePrice, result, quit) + if len(res.values) == 1 && len(results)+1+exp < gpo.checkBlocks*2 && number > 0 { + go gpo.getBlockValues(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, gpo.ignorePrice, result, quit) sent++ exp++ number-- } - txPrices = append(txPrices, res.prices...) + results = append(results, res.values...) } price := lastPrice - if len(txPrices) > 0 { - sort.Sort(bigIntArray(txPrices)) - price = txPrices[(len(txPrices)-1)*gpo.percentile/100] + if len(results) > 0 { + sort.Sort(bigIntArray(results)) + price = results[(len(results)-1)*gpo.percentile/100] } if price.Cmp(gpo.maxPrice) > 0 { price = new(big.Int).Set(gpo.maxPrice) } - // Check gas price min. - minGasPrice := common.GetMinGasPrice(head.Number) - if price.Cmp(minGasPrice) < 0 { - price = new(big.Int).Set(minGasPrice) + // Check min gas price for non-eip1559 block + if head.BaseFee == nil { + minGasPrice := common.GetMinGasPrice(head.Number) + if price.Cmp(minGasPrice) < 0 { + price = new(big.Int).Set(minGasPrice) + } } gpo.cacheLock.Lock() gpo.lastHead = headHash gpo.lastPrice = price gpo.cacheLock.Unlock() - return price, nil + + return new(big.Int).Set(price), nil } -type getBlockPricesResult struct { - prices []*big.Int +type results struct { + values []*big.Int err error } -type transactionsByGasPrice []*types.Transaction +type txSorter struct { + txs []*types.Transaction + baseFee *big.Int +} + +func newSorter(txs []*types.Transaction, baseFee *big.Int) *txSorter { + return &txSorter{ + txs: txs, + baseFee: baseFee, + } +} -func (t transactionsByGasPrice) Len() int { return len(t) } -func (t transactionsByGasPrice) Swap(i, j int) { t[i], t[j] = t[j], t[i] } -func (t transactionsByGasPrice) Less(i, j int) bool { return t[i].FeeCapCmp(t[j]) < 0 } +func (s *txSorter) Len() int { return len(s.txs) } +func (s *txSorter) Swap(i, j int) { + s.txs[i], s.txs[j] = s.txs[j], s.txs[i] +} +func (s *txSorter) Less(i, j int) bool { + // It's okay to discard the error because a tx would never be + // accepted into a block with an invalid effective tip. + tip1, _ := s.txs[i].EffectiveTip(s.baseFee) + tip2, _ := s.txs[j].EffectiveTip(s.baseFee) + return tip1.Cmp(tip2) < 0 +} // getBlockPrices calculates the lowest transaction gas price in a given block // and sends it to the result channel. If the block is empty or all transactions // are sent by the miner itself(it doesn't make any sense to include this kind of // transaction prices for sampling), nil gasprice is returned. -func (gpo *Oracle) getBlockPrices(ctx context.Context, signer types.Signer, blockNum uint64, limit int, ignoreUnder *big.Int, result chan getBlockPricesResult, quit chan struct{}) { +func (gpo *Oracle) getBlockValues(ctx context.Context, signer types.Signer, blockNum uint64, limit int, ignoreUnder *big.Int, result chan results, quit chan struct{}) { block, err := gpo.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum)) if block == nil { select { - case result <- getBlockPricesResult{nil, err}: + case result <- results{nil, err}: case <-quit: } return } - blockTxs := block.Transactions() - txs := make([]*types.Transaction, len(blockTxs)) - copy(txs, blockTxs) - sort.Sort(transactionsByGasPrice(txs)) + // Sort the transaction by effective tip in ascending sort. + txs := make([]*types.Transaction, len(block.Transactions())) + copy(txs, block.Transactions()) + sorter := newSorter(txs, block.BaseFee()) + sort.Sort(sorter) var prices []*big.Int - for _, tx := range txs { - if ignoreUnder != nil && tx.GasPrice().Cmp(ignoreUnder) == -1 { + for _, tx := range sorter.txs { + tip, _ := tx.EffectiveTip(block.BaseFee()) + if ignoreUnder != nil && tip.Cmp(ignoreUnder) == -1 { continue } sender, err := types.Sender(signer, tx) if err == nil && sender != block.Coinbase() { - prices = append(prices, tx.GasPrice()) + prices = append(prices, tip) if len(prices) >= limit { break } } } select { - case result <- getBlockPricesResult{prices, nil}: + case result <- results{prices, nil}: case <-quit: } } diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go new file mode 100644 index 000000000000..0c200e095bcb --- /dev/null +++ b/eth/gasprice/gasprice_test.go @@ -0,0 +1,157 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package gasprice + +import ( + "context" + "math" + "math/big" + "testing" + + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/consensus/ethash" + "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" + "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/core/vm" + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/params" + "github.com/XinFinOrg/XDPoSChain/rpc" +) + +type testBackend struct { + chain *core.BlockChain +} + +func (b *testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { + if number == rpc.LatestBlockNumber { + return b.chain.CurrentBlock().Header(), nil + } + return b.chain.GetHeaderByNumber(uint64(number)), nil +} + +func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { + if number == rpc.LatestBlockNumber { + return b.chain.CurrentBlock(), nil + } + return b.chain.GetBlockByNumber(uint64(number)), nil +} + +func (b *testBackend) ChainConfig() *params.ChainConfig { + return b.chain.Config() +} + +func newTestBackend(t *testing.T, eip1559Block *big.Int) *testBackend { + var ( + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr = crypto.PubkeyToAddress(key.PublicKey) + gspec = &core.Genesis{ + Config: params.TestChainConfig, + Alloc: core.GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}}, + } + signer = types.LatestSigner(gspec.Config) + ) + if eip1559Block != nil { + gspec.Config.Eip1559Block = eip1559Block + signer = types.LatestSigner(gspec.Config) + } + engine := ethash.NewFaker() + db := rawdb.NewMemoryDatabase() + genesis, _ := gspec.Commit(db) + + // Generate testing blocks + blocks, _ := core.GenerateChain(gspec.Config, genesis, engine, db, 32, func(i int, b *core.BlockGen) { + b.SetCoinbase(common.Address{1}) + + var tx *types.Transaction + if eip1559Block != nil && b.Number().Cmp(eip1559Block) >= 0 { + txdata := &types.DynamicFeeTx{ + ChainID: gspec.Config.ChainId, + Nonce: b.TxNonce(addr), + To: &common.Address{}, + Gas: 30000, + FeeCap: big.NewInt(100 * params.GWei), + Tip: big.NewInt(int64(i+1) * params.GWei), + Data: []byte{}, + } + tx = types.NewTx(txdata) + } else { + txdata := &types.LegacyTx{ + Nonce: b.TxNonce(addr), + To: &common.Address{}, + Gas: 21000, + GasPrice: big.NewInt(int64(i+1) * params.GWei), + Value: big.NewInt(100), + Data: []byte{}, + } + tx = types.NewTx(txdata) + } + tx, err := types.SignTx(tx, signer, key) + if err != nil { + t.Fatalf("failed to create tx: %v", err) + } + b.AddTx(tx) + }) + // Construct testing chain + diskdb := rawdb.NewMemoryDatabase() + gspec.Commit(diskdb) + chain, err := core.NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}) + if err != nil { + t.Fatalf("Failed to create local chain, %v", err) + } + chain.InsertChain(blocks) + return &testBackend{chain: chain} +} + +func (b *testBackend) CurrentHeader() *types.Header { + return b.chain.CurrentHeader() +} + +func (b *testBackend) GetBlockByNumber(number uint64) *types.Block { + return b.chain.GetBlockByNumber(number) +} + +func TestSuggestTipCap(t *testing.T) { + config := Config{ + Blocks: 3, + Percentile: 60, + Default: big.NewInt(params.GWei), + } + var cases = []struct { + fork *big.Int // Eip1559 fork number + expect *big.Int // Expected gasprice suggestion + }{ + {nil, big.NewInt(params.GWei * int64(30))}, + {big.NewInt(0), big.NewInt(params.GWei * int64(30))}, // Fork point in genesis + {big.NewInt(1), big.NewInt(params.GWei * int64(30))}, // Fork point in first block + {big.NewInt(32), big.NewInt(params.GWei * int64(30))}, // Fork point in last block + {big.NewInt(33), big.NewInt(params.GWei * int64(30))}, // Fork point in the future + } + for _, c := range cases { + backend := newTestBackend(t, c.fork) + oracle := NewOracle(backend, config) + + // The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G + got, err := oracle.SuggestTipCap(context.Background()) + if err != nil { + t.Fatalf("Failed to retrieve recommended gas price: %v", err) + } + if got.Cmp(c.expect) != 0 { + t.Fatalf("Gas price mismatch, want %d, got %d", c.expect, got) + } + } +} diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go index 57f26188d228..0fe575546ab5 100644 --- a/ethstats/ethstats.go +++ b/ethstats/ethstats.go @@ -816,8 +816,11 @@ func (s *Service) reportStats(conn *connWrapper) error { sync := s.eth.Downloader().Progress() syncing = s.eth.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock - price, _ := s.eth.ApiBackend.SuggestPrice(context.Background()) - gasprice = int(price.Uint64()) + tipcap, _ := s.eth.ApiBackend.SuggestGasTipCap(context.Background()) + if head := s.eth.ApiBackend.CurrentHeader(); head.BaseFee != nil { + tipcap.Add(tipcap, head.BaseFee) + } + gasprice = int(tipcap.Uint64()) } else { sync := s.les.Downloader().Progress() syncing = s.les.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 7915c6dd7628..211d2332a7e7 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -39,6 +39,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" "github.com/XinFinOrg/XDPoSChain/consensus/ethash" + "github.com/XinFinOrg/XDPoSChain/consensus/misc" contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/state" @@ -80,10 +81,25 @@ func NewPublicEthereumAPI(b Backend) *PublicEthereumAPI { return &PublicEthereumAPI{b} } -// GasPrice returns a suggestion for a gas price. +// GasPrice returns a suggestion for a gas price for legacy transactions. func (s *PublicEthereumAPI) GasPrice(ctx context.Context) (*hexutil.Big, error) { - price, err := s.b.SuggestPrice(ctx) - return (*hexutil.Big)(price), err + tipcap, err := s.b.SuggestGasTipCap(ctx) + if err != nil { + return nil, err + } + if head := s.b.CurrentHeader(); head.BaseFee != nil { + tipcap.Add(tipcap, head.BaseFee) + } + return (*hexutil.Big)(tipcap), err +} + +// MaxPriorityFeePerGas returns a suggestion for a gas tip cap for dynamic transactions. +func (s *PublicEthereumAPI) MaxPriorityFeePerGas(ctx context.Context) (*hexutil.Big, error) { + tipcap, err := s.b.SuggestGasTipCap(ctx) + if err != nil { + return nil, err + } + return (*hexutil.Big)(tipcap), err } // ProtocolVersion returns the current Ethereum protocol version this node supports @@ -132,12 +148,12 @@ func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string]*RPCTransac "queued": make(map[string]map[string]*RPCTransaction), } pending, queue := s.b.TxPoolContent() - + curHeader := s.b.CurrentHeader() // Flatten the pending transactions for account, txs := range pending { dump := make(map[string]*RPCTransaction) for _, tx := range txs { - dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx) + dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()) } content["pending"][account.Hex()] = dump } @@ -145,7 +161,7 @@ func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string]*RPCTransac for account, txs := range queue { dump := make(map[string]*RPCTransaction) for _, tx := range txs { - dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx) + dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()) } content["queued"][account.Hex()] = dump } @@ -1261,7 +1277,10 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash return nil, 0, false, err, nil } - msg := args.ToMessage(b, header.Number, globalGasCap) + msg, err := args.ToMessage(b, header.Number, globalGasCap, header.BaseFee) + if err != nil { + return nil, 0, false, err, nil + } msg.SetBalanceTokenFeeForCall() // Setup context so it may be cancelled the call has completed @@ -1558,14 +1577,11 @@ func FormatLogs(logs []vm.StructLog) []StructLogRes { return formatted } -// rpcOutputBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are -// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain -// transaction hashes. -func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool, ctx context.Context) (map[string]interface{}, error) { - head := b.Header() // copies the header once - fields := map[string]interface{}{ +// RPCMarshalHeader converts the given header to the RPC output . +func RPCMarshalHeader(head *types.Header) map[string]interface{} { + result := map[string]interface{}{ "number": (*hexutil.Big)(head.Number), - "hash": b.Hash(), + "hash": head.Hash(), "parentHash": head.ParentHash, "nonce": head.Nonce, "mixHash": head.MixDigest, @@ -1574,9 +1590,8 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx "stateRoot": head.Root, "miner": head.Coinbase, "difficulty": (*hexutil.Big)(head.Difficulty), - "totalDifficulty": (*hexutil.Big)(s.b.GetTd(b.Hash())), "extraData": hexutil.Bytes(head.Extra), - "size": hexutil.Uint64(b.Size()), + "size": hexutil.Uint64(head.Size()), "gasLimit": hexutil.Uint64(head.GasLimit), "gasUsed": hexutil.Uint64(head.GasUsed), "timestamp": (*hexutil.Big)(head.Time), @@ -1587,6 +1602,21 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx "penalties": hexutil.Bytes(head.Penalties), } + if head.BaseFee != nil { + result["baseFeePerGas"] = (*hexutil.Big)(head.BaseFee) + } + + return result +} + +// rpcOutputBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are +// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain +// transaction hashes. +func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool, ctx context.Context) (map[string]interface{}, error) { + fields := RPCMarshalHeader(b.Header()) + fields["size"] = hexutil.Uint64(b.Size()) + fields["totalDifficulty"] = (*hexutil.Big)(s.b.GetTd(b.Hash())) + if inclTx { formatTx := func(tx *types.Transaction) (interface{}, error) { return tx.Hash(), nil @@ -1773,6 +1803,8 @@ type RPCTransaction struct { From common.Address `json:"from"` Gas hexutil.Uint64 `json:"gas"` GasPrice *hexutil.Big `json:"gasPrice"` + FeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"` + Tip *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"` Hash common.Hash `json:"hash"` Input hexutil.Bytes `json:"input"` Nonce hexutil.Uint64 `json:"nonce"` @@ -1789,7 +1821,7 @@ type RPCTransaction struct { // newRPCTransaction returns a transaction that will serialize to the RPC // representation, with the given location metadata set (if available). -func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64) *RPCTransaction { +func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64, baseFee *big.Int) *RPCTransaction { // Determine the signer. For replay-protected transactions, use the most permissive // signer, because we assume that signers are backwards-compatible with old // transactions. For non-protected transactions, the homestead signer signer is used @@ -1800,7 +1832,6 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber } else { signer = types.HomesteadSigner{} } - from, _ := types.Sender(signer, tx) v, r, s := tx.RawSignatureValues() result := &RPCTransaction{ @@ -1822,17 +1853,36 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber)) result.TransactionIndex = (*hexutil.Uint64)(&index) } - if tx.Type() != types.LegacyTxType { + switch tx.Type() { + case types.AccessListTxType: al := tx.AccessList() result.Accesses = &al result.ChainID = (*hexutil.Big)(tx.ChainId()) + case types.DynamicFeeTxType: + al := tx.AccessList() + result.Accesses = &al + result.ChainID = (*hexutil.Big)(tx.ChainId()) + result.FeeCap = (*hexutil.Big)(tx.FeeCap()) + result.Tip = (*hexutil.Big)(tx.Tip()) + // if the transaction has been mined, compute the effective gas price + if baseFee != nil && blockHash != (common.Hash{}) { + // price = min(tip, feeCap - baseFee) + baseFee = min(tip + baseFee, feeCap) + price := math.BigMin(new(big.Int).Add(tx.Tip(), baseFee), tx.FeeCap()) + result.GasPrice = (*hexutil.Big)(price) + } else { + result.GasPrice = nil + } } return result } // newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation -func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction { - return newRPCTransaction(tx, common.Hash{}, 0, 0) +func newRPCPendingTransaction(tx *types.Transaction, current *types.Header, config *params.ChainConfig) *RPCTransaction { + var baseFee *big.Int + if current != nil { + baseFee = misc.CalcBaseFee(config, current) + } + return newRPCTransaction(tx, common.Hash{}, 0, 0, baseFee) } // newRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation. @@ -1841,7 +1891,7 @@ func newRPCTransactionFromBlockIndex(b *types.Block, index uint64) *RPCTransacti if index >= uint64(len(txs)) { return nil } - return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index) + return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index, b.BaseFee()) } // newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index. @@ -2077,17 +2127,23 @@ func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, addr } // GetTransactionByHash returns the transaction for the given hash -func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) *RPCTransaction { +func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) { // Try to return an already finalized transaction - if tx, blockHash, blockNumber, index := core.GetTransaction(s.b.ChainDb(), hash); tx != nil { - return newRPCTransaction(tx, blockHash, blockNumber, index) + tx, blockHash, blockNumber, index := core.GetTransaction(s.b.ChainDb(), hash) + if tx != nil { + header, err := s.b.HeaderByHash(ctx, blockHash) + if err != nil { + return nil, err + } + return newRPCTransaction(tx, blockHash, blockNumber, index, header.BaseFee), nil } // No finalized transaction, try to retrieve it from the pool if tx := s.b.GetPoolTransaction(hash); tx != nil { - return newRPCPendingTransaction(tx) + return newRPCPendingTransaction(tx, s.b.CurrentHeader(), s.b.ChainConfig()), nil } + // Transaction unknown, return as such - return nil + return nil, nil } // GetRawTransactionByHash returns the bytes of the transaction for the given hash. @@ -3236,11 +3292,12 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, err accounts[account.Address] = struct{}{} } } + curHeader := s.b.CurrentHeader() transactions := make([]*RPCTransaction, 0, len(pending)) for _, tx := range pending { from, _ := types.Sender(s.signer, tx) if _, exists := accounts[from]; exists { - transactions = append(transactions, newRPCPendingTransaction(tx)) + transactions = append(transactions, newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())) } } return transactions, nil diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index a365e5278020..39d732542992 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -48,7 +48,7 @@ type Backend interface { // General Ethereum API Downloader() *downloader.Downloader ProtocolVersion() int - SuggestPrice(ctx context.Context) (*big.Int, error) + SuggestGasTipCap(ctx context.Context) (*big.Int, error) ChainDb() ethdb.Database AccountManager() *accounts.Manager RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection @@ -61,6 +61,7 @@ type Backend interface { HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) + CurrentHeader() *types.Header BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index f2df0a5b64e8..d9838ea577f8 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -20,6 +20,7 @@ import ( "bytes" "context" "errors" + "fmt" "math/big" "github.com/XinFinOrg/XDPoSChain/common" @@ -37,6 +38,8 @@ type TransactionArgs struct { To *common.Address `json:"to"` Gas *hexutil.Uint64 `json:"gas"` GasPrice *hexutil.Big `json:"gasPrice"` + FeeCap *hexutil.Big `json:"maxFeePerGas"` + Tip *hexutil.Big `json:"maxPriorityFeePerGas"` Value *hexutil.Big `json:"value"` Nonce *hexutil.Uint64 `json:"nonce"` @@ -72,12 +75,43 @@ func (arg *TransactionArgs) data() []byte { // setDefaults fills in default values for unspecified tx fields. func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { - if args.GasPrice == nil { - price, err := b.SuggestPrice(ctx) - if err != nil { - return err + if args.GasPrice != nil && (args.FeeCap != nil || args.Tip != nil) { + return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") + } + // After london, default to 1559 unless gasPrice is set + head := b.CurrentHeader() + if b.ChainConfig().IsEIP1559(head.Number) && args.GasPrice == nil { + if args.Tip == nil { + tip, err := b.SuggestGasTipCap(ctx) + if err != nil { + return err + } + args.Tip = (*hexutil.Big)(tip) + } + if args.FeeCap == nil { + feeCap := new(big.Int).Add( + (*big.Int)(args.Tip), + new(big.Int).Mul(head.BaseFee, big.NewInt(2)), + ) + args.FeeCap = (*hexutil.Big)(feeCap) + } + if args.FeeCap.ToInt().Cmp(args.Tip.ToInt()) < 0 { + return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.FeeCap, args.Tip) + } + } else { + if args.FeeCap != nil || args.Tip != nil { + return errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") + } + if args.GasPrice == nil { + price, err := b.SuggestGasTipCap(ctx) + if err != nil { + return err + } + if b.ChainConfig().IsEIP1559(head.Number) { + price.Add(price, head.BaseFee) + } + args.GasPrice = (*hexutil.Big)(price) } - args.GasPrice = (*hexutil.Big)(price) } if args.Value == nil { args.Value = new(hexutil.Big) @@ -104,6 +138,8 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { From: args.From, To: args.To, GasPrice: args.GasPrice, + FeeCap: args.FeeCap, + Tip: args.Tip, Value: args.Value, Data: (*hexutil.Bytes)(&data), AccessList: args.AccessList, @@ -124,7 +160,12 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { } // ToMessage converts TransactionArgs to the Message type used by the core evm -func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap uint64) types.Message { +func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap uint64, baseFee *big.Int) (types.Message, error) { + // Reject invalid combinations of pre- and post-1559 fee styles + if args.GasPrice != nil && (args.FeeCap != nil || args.Tip != nil) { + return types.Message{}, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") + } + // Set sender address or use zero address if none specified. addr := args.from() if addr == (common.Address{}) { @@ -147,12 +188,38 @@ func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap log.Warn("Caller gas above allowance, capping", "requested", gas, "cap", globalGasCap) gas = globalGasCap } - gasPrice := new(big.Int) - if args.GasPrice != nil { - gasPrice = args.GasPrice.ToInt() - } - if gasPrice.Sign() <= 0 { - gasPrice = new(big.Int).SetUint64(defaultGasPrice) + + var ( + gasPrice *big.Int + feeCap *big.Int + tip *big.Int + ) + if baseFee == nil { + // If there's no basefee, then it must be a non-1559 execution + gasPrice = new(big.Int) + if args.GasPrice != nil { + gasPrice = args.GasPrice.ToInt() + } + if gasPrice.Sign() <= 0 { + gasPrice = new(big.Int).SetUint64(defaultGasPrice) + } + feeCap, tip = gasPrice, gasPrice + } else { + // A basefee is provided, necessitating 1559-type execution + if args.GasPrice != nil { + gasPrice = args.GasPrice.ToInt() + feeCap, tip = gasPrice, gasPrice + } else { + feeCap = new(big.Int) + if args.FeeCap != nil { + feeCap = args.FeeCap.ToInt() + } + tip = new(big.Int) + if args.Tip != nil { + tip = args.Tip.ToInt() + } + gasPrice = math.BigMin(new(big.Int).Add(tip, baseFee), feeCap) + } } value := new(big.Int) if args.Value != nil { @@ -165,24 +232,32 @@ func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap } // Create new call message - msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, nil, nil, data, accessList, false, nil, number) - return msg + msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, feeCap, tip, data, accessList, false, nil, number) + return msg, nil } // toTransaction converts the arguments to a transaction. // This assumes that setDefaults has been called. func (args *TransactionArgs) toTransaction() *types.Transaction { var data types.TxData - if args.AccessList == nil { - data = &types.LegacyTx{ - To: args.To, - Nonce: uint64(*args.Nonce), - Gas: uint64(*args.Gas), - GasPrice: (*big.Int)(args.GasPrice), - Value: (*big.Int)(args.Value), - Data: args.data(), + switch { + case args.FeeCap != nil: + al := types.AccessList{} + if args.AccessList != nil { + al = *args.AccessList } - } else { + data = &types.DynamicFeeTx{ + To: args.To, + ChainID: (*big.Int)(args.ChainID), + Nonce: uint64(*args.Nonce), + Gas: uint64(*args.Gas), + FeeCap: (*big.Int)(args.FeeCap), + Tip: (*big.Int)(args.Tip), + Value: (*big.Int)(args.Value), + Data: args.data(), + AccessList: al, + } + case args.AccessList != nil: data = &types.AccessListTx{ To: args.To, ChainID: (*big.Int)(args.ChainID), @@ -193,6 +268,15 @@ func (args *TransactionArgs) toTransaction() *types.Transaction { Data: args.data(), AccessList: *args.AccessList, } + default: + data = &types.LegacyTx{ + To: args.To, + Nonce: uint64(*args.Nonce), + Gas: uint64(*args.Gas), + GasPrice: (*big.Int)(args.GasPrice), + Value: (*big.Int)(args.Value), + Data: args.data(), + } } return types.NewTx(data) } diff --git a/internal/jsre/deps/bindata.go b/internal/jsre/deps/bindata.go index 50b5bbc93d0f..7b17817f126a 100644 --- a/internal/jsre/deps/bindata.go +++ b/internal/jsre/deps/bindata.go @@ -1,16 +1,15 @@ -// Code generated by go-bindata. DO NOT EDIT. +// Code generated for package deps by go-bindata DO NOT EDIT. (@generated) // sources: -// bignumber.js (17.314kB) -// web3.js (403.868kB) - +// bignumber.js +// web3.js package deps import ( "bytes" "compress/gzip" - "crypto/sha256" "fmt" "io" + "io/ioutil" "os" "path/filepath" "strings" @@ -20,7 +19,7 @@ import ( func bindataRead(data []byte, name string) ([]byte, error) { gz, err := gzip.NewReader(bytes.NewBuffer(data)) if err != nil { - return nil, fmt.Errorf("read %q: %w", name, err) + return nil, fmt.Errorf("read %q: %v", name, err) } var buf bytes.Buffer @@ -28,7 +27,7 @@ func bindataRead(data []byte, name string) ([]byte, error) { clErr := gz.Close() if err != nil { - return nil, fmt.Errorf("read %q: %w", name, err) + return nil, fmt.Errorf("read %q: %v", name, err) } if clErr != nil { return nil, err @@ -38,9 +37,8 @@ func bindataRead(data []byte, name string) ([]byte, error) { } type asset struct { - bytes []byte - info os.FileInfo - digest [sha256.Size]byte + bytes []byte + info os.FileInfo } type bindataFileInfo struct { @@ -50,21 +48,32 @@ type bindataFileInfo struct { modTime time.Time } +// Name return file name func (fi bindataFileInfo) Name() string { return fi.name } + +// Size return file size func (fi bindataFileInfo) Size() int64 { return fi.size } + +// Mode return file mode func (fi bindataFileInfo) Mode() os.FileMode { return fi.mode } + +// Mode return file modify time func (fi bindataFileInfo) ModTime() time.Time { return fi.modTime } + +// IsDir return file whether a directory func (fi bindataFileInfo) IsDir() bool { - return false + return fi.mode&os.ModeDir != 0 } + +// Sys return file is sys mode func (fi bindataFileInfo) Sys() interface{} { return nil } @@ -85,11 +94,11 @@ func bignumberJs() (*asset, error) { } info := bindataFileInfo{name: "bignumber.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x5b, 0x75, 0xfc, 0x15, 0x5e, 0x7d, 0x27, 0x1a, 0x9a, 0xb5, 0xfb, 0x16, 0x90, 0xf4, 0x93, 0xac, 0xcb, 0x6c, 0x9c, 0xcd, 0x68, 0xe6, 0xd0, 0x3a, 0xcf, 0xa3, 0x83, 0x5c, 0x20, 0x34, 0x66, 0x45}} + a := &asset{bytes: bytes, info: info} return a, nil } -var _web3Js = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xfd\x6b\x7b\x13\x39\xd2\x38\x0e\xbf\xcf\xa7\x50\xfc\xdc\x0f\xb6\x89\xb1\x9d\x84\x61\x18\x67\x32\x6c\x08\xcc\x90\xbd\x81\x70\x01\xd9\xd9\xbd\xb3\x59\xae\x8e\x5b\xb6\x7b\x68\x77\xfb\xd7\xdd\xce\x61\x48\xbe\xfb\xff\x52\xe9\x54\x3a\xf4\xc1\x49\x98\xd3\x26\x2f\xc0\x2d\x95\x4e\xa5\x52\xa9\x54\x2a\x55\x65\xf4\xff\x2d\xa3\x8c\xee\x76\x26\xcb\x64\x5c\x44\x69\x42\x68\xa7\xe8\x25\xbd\xac\xfb\x45\xa5\xe4\x9d\xb4\xb7\xec\x7e\x89\x26\x9d\xf5\xe4\x38\x3d\xe1\xbf\x0a\xf8\x75\x16\x64\x24\xd8\x2d\x2e\x17\x34\x9d\x10\x59\xd7\x6e\x4b\x16\x6d\x3d\x78\x20\x12\x77\x58\x99\xe5\x83\x07\x41\x37\xa3\xc5\x32\x4b\x48\xd0\x49\x7b\xeb\xc3\x2e\x4b\x8f\x64\x5a\x24\xd2\x58\xad\x93\xdd\x84\x9e\x93\x97\x59\x96\x66\x9d\xd6\x7e\x90\x24\x69\x41\x26\x51\x12\x92\x79\x1a\x2e\x63\x4a\xda\xad\x8d\x74\xa3\xd5\x6e\x75\x77\x8a\x59\x96\x9e\x93\x49\x7f\x9c\x86\x74\xb7\xf5\xe6\xf0\xc5\xd1\xeb\x97\x9f\xde\x1e\x7e\xfc\xf4\xe3\xe1\xd1\xdb\x17\xad\xde\xe4\x9a\xd5\x17\xef\xb2\xbe\xef\x7e\xa1\x17\x8b\x34\x2b\xf2\xd1\x97\xeb\xeb\x1d\x36\x86\xe3\xe1\x49\x7f\x1c\xc4\x71\x27\xee\x8b\xac\x9e\xec\x7d\x87\xf2\x01\x26\xbb\x00\xb8\x79\x72\x4c\x4f\x76\x44\x57\xf3\x4e\xf2\x2c\x19\xd1\xee\x75\x2f\xee\xe9\x92\xb4\xc7\x71\x77\x2d\xa0\x58\x93\x32\x13\x7a\x11\x35\xc2\xd5\x24\xcd\x3a\x0c\x3a\xdd\x1d\xee\xa4\xdf\x67\xfd\x98\x26\xd3\x62\xb6\x93\x6e\x6c\x74\xf3\x4e\xc6\x10\xaf\xba\x71\xdd\xed\x7c\xd9\x1c\x1d\xab\x2e\x8b\x2a\x7a\x1c\x4b\x3d\xd1\x76\xf7\xcb\x1a\x4f\x90\x9d\xd9\x3d\x5e\x23\xe4\xcb\x1a\x21\x84\xb4\xc6\x69\x92\x17\x41\x52\xb4\x46\xa4\xc8\x96\xb4\xc7\x53\xa3\x64\xb1\x2c\xf2\xd6\x88\x1c\xc3\xb7\x84\x86\xbc\x24\x98\xd3\xd6\x88\xb4\x3e\xa5\xe7\x09\xcd\x5a\x3d\x9d\xc3\x46\xc7\x72\x82\x30\xcc\x68\x9e\xb7\x44\xce\x35\xfc\x7f\x22\xaa\x96\xc5\xe1\x7f\x91\x96\x2e\x8b\xfa\xf6\xd2\x4f\xa8\x88\xd1\xde\xe9\x65\x41\xf3\xed\x2d\x7f\x7b\x12\x48\x61\x7a\x8d\x90\xeb\xde\x9d\x20\xe0\x46\xfd\x51\xc3\x41\xd8\x6b\x86\x80\x95\x51\xfd\x47\x1d\xfa\x38\x4d\x0a\x9a\x14\xb7\x1e\xfc\x9f\x72\xde\xd9\x8c\xfd\x61\xa6\x7d\x12\xc4\xf9\x6f\x37\xf4\x8c\xe6\x34\x3b\xf3\xad\xfa\x3f\xfa\xa4\xe5\xcb\xd3\xf7\x74\x1a\xe5\x45\x16\xfc\x17\x4c\x5e\xaf\xaa\x0e\x7a\x7e\x78\x2b\xbe\x5f\x64\x41\x92\x4f\xbc\xac\xef\xcf\x82\x83\xcc\x22\x85\xd5\x91\x90\xd3\xe2\x43\x35\x49\xdd\x19\x2e\xec\xa6\x7f\x93\x46\xbf\xf2\x04\x04\x4d\x10\x5f\x55\xc1\x22\x8b\xe6\x41\x76\xe9\xed\x47\x9a\xc6\xb5\x93\xb7\x27\xda\xfa\xf3\xa2\xd0\xdc\x83\x2b\xab\x29\x43\xc2\x7e\xe9\x36\xfe\x47\x42\x82\xb7\xf7\x61\x94\xa7\xe7\xc9\x2d\x7a\x1e\x24\x69\x72\x39\x4f\x97\xf9\x0a\x5d\x8f\x92\x90\x5e\xd0\xd0\xd8\xbb\xee\x6c\x62\x75\xe5\xa8\x3b\x66\xed\xe7\x51\x72\x1b\xc6\xbd\xb7\x04\x4c\xbc\x4c\x42\x1a\xb6\x2c\x34\xd1\x33\x46\x08\x7f\x01\x1c\x9d\x46\x61\xd8\x0c\x47\x37\xab\xff\x2c\x88\x97\xde\xee\x2f\xa3\xa4\xd8\xfa\xe6\x49\xf5\x14\xbc\xa5\xe7\xcf\xa3\xdf\x11\xf9\xb7\x5a\x73\xfb\xb3\x20\x99\xfe\x9e\xa4\x73\x27\x94\x53\x52\x37\x92\xea\x2b\xa9\xc6\x8b\x99\x77\x7c\x37\xaa\x45\xd0\xda\xc9\xda\xda\x75\xef\xcb\xf5\x49\x6f\xeb\x77\x3b\xf4\xff\x85\xce\xbc\xbf\x93\xec\x38\x59\x26\xe1\x8d\x49\xe5\xd6\x1b\xd7\xfd\xb1\xf7\xcf\x7d\xec\xbd\x3f\xf4\xfd\x91\xcf\x1c\xde\xc1\x8b\xf3\xc2\x1f\x4d\xda\xfc\xba\x9b\xb9\xde\xab\xb6\xef\x6c\xaf\x5a\x75\xde\x27\x59\x3a\xbf\xe5\xb4\x17\xe9\x2d\x8f\x9a\xb7\x13\xf8\x7e\xdf\x75\xf3\x47\xc0\x5f\x94\x84\x51\x46\xc7\xc5\x81\x77\xcf\x5c\xa1\x27\xb7\x9b\x88\x68\x1c\x2c\x3e\xfe\xae\x93\xe1\xc7\x64\xb3\xd3\x2e\x5d\xa4\x79\x54\x75\x50\x5f\x04\x97\xc1\x69\x4c\x4d\xa1\xe0\x77\xe1\x4a\x65\x34\x77\x27\xc7\xaf\xdb\xd1\xc0\x9e\x1c\xef\x0b\x13\x9f\xbf\xfd\x49\xe6\x4e\x90\x54\x52\x77\x33\x3a\xfb\x1d\xd0\xff\x87\xc5\xfa\x5d\x9c\x1f\x6f\xcc\x27\xbf\x36\xd6\x6d\xa6\x77\x8f\xf6\x86\x68\xbf\xf5\xc6\xf5\xb5\x67\xf6\xc0\xb3\xa5\x55\xc9\x71\x8f\x9b\xc8\x71\x60\xbc\x41\x76\xa5\x85\x43\xa7\xdd\x1f\x4c\xd2\x6c\x1e\x14\x05\xcd\xf2\x76\x77\x07\x00\x3e\xa4\x71\x14\x46\xc5\xe5\xc7\xcb\x05\x35\x61\x59\xfb\x0c\x6a\x6d\xf0\xf0\xe1\x1a\x79\x68\x40\x0a\x9d\x3b\x89\x72\x12\x90\x45\x96\xa6\x0c\x98\x14\xb3\xa0\x20\x19\x5d\xb0\x43\x56\x52\xe4\x44\xcc\x1d\x61\x99\xac\x86\x83\x82\xcc\x83\x62\x3c\xa3\xf9\x88\x7d\x8a\x6c\xf4\xf3\xf8\x04\x7f\x3c\x36\xbe\x4e\xcc\xcc\x6d\xeb\xfb\xe4\xf8\xc9\xc9\xf1\x49\x8f\xf4\xfb\xfd\x35\xf2\x70\xe0\x8c\x4d\xf6\x78\x97\x28\x6b\x9a\x4e\x57\x4c\x71\x31\x8b\xf2\xfe\x27\x58\x18\x3f\x4a\x04\x31\xc0\x3e\x47\xd7\x01\xcb\x38\x48\x8a\x1d\x04\xcc\xf7\x6d\x1f\xf4\x21\xe4\x88\xe6\x76\xd6\xae\x77\xd6\xd6\x3c\xfd\xe8\x2f\xb2\xb4\xe0\x58\xdb\x25\x09\x3d\x37\xfa\xda\xf9\x72\xdd\xdd\xa9\x2e\xd5\x07\xe9\x25\x5b\x8e\x8b\x94\x35\xee\x81\xad\x6b\xb7\x1f\xe5\x62\xce\x35\x42\x18\x39\x4a\xa4\x08\xbb\x96\xf5\x75\x96\xd8\x87\x79\xeb\x0c\x04\xb6\x3b\xff\x3e\xee\x1c\x0f\x1f\x7d\x77\xf2\xb0\xfb\xef\x93\xee\xb3\x41\x97\x8f\xd3\x3c\x38\x94\x76\xeb\xba\xf7\xa5\x85\x49\xb1\x35\xfa\xae\xd7\xe2\xf4\xd6\x1a\x6d\x3e\xbe\x3e\xe9\x7d\xf3\x3b\x93\xf7\xf3\x34\x8d\x6b\x68\xfb\x94\x81\x94\x10\x36\xcb\x93\xff\x73\x2a\x85\x5f\x8f\xf5\xcf\x13\x94\xbc\x8d\x3f\xea\xc8\x18\x7a\x76\x53\x1a\x66\x85\x57\x21\x62\x0e\x6f\x53\x30\x4b\x5d\x91\x7c\xcd\x22\x15\xb4\xcb\x5b\xac\x2a\x7b\x13\xaa\xfd\x0f\x43\xad\x49\xb3\x0f\xff\xa7\x11\xd1\x8a\xfe\xd4\x53\xec\x93\xdf\x9b\x62\xd9\x1e\xa6\x48\xb6\xf0\xd3\x6c\x31\xa3\x04\x36\x3b\x20\xdc\xbe\x8f\x72\x59\xae\xfa\x21\xe8\x12\x7e\x3e\x46\xbf\x4f\x70\xc6\xb6\xf1\x65\xd2\x2f\x11\x5b\xab\xfa\xf9\xd4\xa8\x47\x14\xf5\x50\x39\x74\xf2\xc6\x64\xce\x4a\xaf\x44\xe7\xbc\x80\x43\xe8\x2c\x79\x55\x4a\x37\xcb\x54\x91\x3a\x6f\xb4\xb2\xf4\xcd\x88\x9d\x55\xc2\x49\xfd\xcb\x66\xef\xba\x7b\x33\xc2\x17\xbd\xab\xa7\xfc\x6f\x9b\x50\xfe\xe0\x21\x74\xf8\xe3\x2c\xca\xc9\x24\x8a\x29\xa3\xd4\x45\x90\x15\x24\x9d\x90\x73\x7a\xba\xdd\xff\x25\xef\xaf\x01\x88\xf8\x62\x00\x93\x8c\x52\x92\xa7\x93\xe2\x3c\xc8\xe8\x88\x5c\xa6\x4b\x32\x0e\x12\x92\xd1\x30\xca\x8b\x2c\x3a\x5d\x16\x94\x44\x05\x09\x92\x70\x90\x66\x64\x9e\x86\xd1\xe4\x12\xea\x88\x0a\xb2\x4c\x42\x9a\x01\xc1\x17\x34\x9b\xe7\xac\x1d\xf6\xf1\xd3\xdb\x23\xf2\x9a\xe6\x39\xcd\xc8\x4f\x34\xa1\x59\x10\x93\x77\xcb\xd3\x38\x1a\x93\xd7\xd1\x98\x26\x39\x25\x41\x4e\x16\x2c\x25\x9f\xd1\x90\x9c\x5e\x0a\x2a\xa2\xe4\x47\xd6\x99\x0f\xa2\x33\xe4\xc7\x74\x99\x84\x01\x1b\x73\x8f\xd0\xa8\x98\xd1\x8c\x9c\xd1\x2c\x67\x33\xb4\x2d\xdb\x12\x35\xf6\x48\x9a\x41\x2d\x9d\xa0\x60\x63\xc8\x48\xba\x60\x05\xbb\x24\x48\x2e\x49\x1c\x14\xba\xac\x8b\x02\x3d\xd2\x90\x44\x09\x54\x3b\x4b\xe5\xca\x8e\x0a\x72\x1e\xc5\x31\x39\xa5\x64\x99\xd3\xc9\x32\xe6\x82\xe3\xe9\xb2\x20\x3f\x1f\x7c\x7c\x75\x78\xf4\x91\xec\xbd\xfd\x17\xf9\x79\xef\xfd\xfb\xbd\xb7\x1f\xff\xb5\x43\xce\xa3\x62\x96\x2e\x0b\xc2\x24\x4a\xa8\x2b\x9a\x2f\xe2\x88\x86\xe4\x3c\xc8\xb2\x20\x29\x2e\x49\x3a\x81\x2a\xde\xbc\x7c\xbf\xff\x6a\xef\xed\xc7\xbd\xe7\x07\xaf\x0f\x3e\xfe\x8b\xa4\x19\xf9\xf1\xe0\xe3\xdb\x97\x1f\x3e\x90\x1f\x0f\xdf\x93\x3d\xf2\x6e\xef\xfd\xc7\x83\xfd\xa3\xd7\x7b\xef\xc9\xbb\xa3\xf7\xef\x0e\x3f\xbc\xec\x13\xf2\x81\xb2\x8e\x51\xa8\xa1\x1e\xd1\x13\x98\xb3\x8c\x92\x90\x16\x41\x14\xcb\xf9\xff\x57\xba\x24\xf9\x2c\x5d\xc6\x21\x99\x05\x67\x94\x64\x74\x4c\xa3\x33\x1a\x92\x80\x8c\xd3\xc5\x65\xe3\x89\x84\xca\x82\x38\x4d\xa6\x30\x6c\x45\x65\x84\x1c\x4c\x48\x92\x16\x3d\x92\x53\x4a\xbe\x9f\x15\xc5\x62\x34\x18\x9c\x9f\x9f\xf7\xa7\xc9\xb2\x9f\x66\xd3\x41\xcc\x2b\xc8\x07\x3f\xf4\xd7\x1e\x0e\x24\xb3\xfd\x1b\x90\xed\x38\x0d\x69\xd6\xff\x05\x58\xe4\xdf\x82\x65\x31\x4b\x33\xf2\x26\xc8\xe8\x67\xf2\xbf\x69\x41\xcf\xa3\xf1\xaf\xe4\xfb\x39\xfb\xfe\x1b\x2d\x66\x21\x3d\xeb\x8f\xd3\xf9\x0f\x00\x1c\x06\x05\x25\x5b\xc3\xcd\x6f\x80\xe1\xd5\x6f\x05\x15\x02\x2c\x2a\x23\xe4\x31\xdf\xde\x21\x24\x05\x04\xcc\x76\x41\x1f\xe4\x41\x52\x98\x80\x51\x52\xf8\xe0\x8e\x1c\xc0\x65\x09\xe4\x8b\xcb\x24\x98\x47\x63\xc9\xc6\x51\x89\x90\xe7\x00\x8f\xf2\x95\xfc\x50\x64\x51\x32\x35\xcb\xe4\x90\xe6\x83\x7e\x4f\x03\x6b\x8c\x19\x0d\xbc\x63\x3c\x72\x41\x97\x65\xb0\x9e\x6e\xab\xfe\x02\x70\x94\x8b\x01\x1a\x9c\x39\x47\x55\xf4\x60\x87\x15\x7c\x5a\x5a\x88\xa3\xfc\xbe\xaa\x02\xb6\x11\x0e\x7c\x75\xa5\x4e\x8f\xa4\x04\x7a\x2f\xcb\x82\x4b\x0e\xce\x99\xb8\x25\x0a\xec\x33\xfa\x44\x12\x80\x58\x49\x9c\x43\x84\xa4\x48\x09\x4d\x18\x0d\x0f\x42\xca\xfe\x53\xad\x30\x66\x1c\x70\x36\xc9\xb8\x92\x90\x6b\xcd\x8d\x99\xd7\x8d\x47\xcc\xc0\x72\x73\x67\x86\x24\xb2\x0b\x35\xe4\x46\x17\x81\xf7\xcf\x69\x31\x4b\x43\x4f\xb7\xb8\x72\x3d\xcd\xe6\x84\x4b\x2e\xa9\x31\x23\x6b\x84\xaf\x41\x51\xfc\x93\x98\x19\x91\x45\xfe\x06\xbd\x27\x5f\x38\xf1\x5c\x2b\xb1\xfc\x6f\x1c\xf3\x39\xf9\x82\x2b\xbb\x86\x2c\x78\xab\x90\x93\x2f\xf0\xae\xe1\x9a\x88\xcf\x88\xf1\x06\x2e\x11\x31\x32\x84\xbe\xb0\x9d\x88\xb1\x7b\x40\x88\x81\x0c\xb4\x53\xe3\x2e\x39\x38\x92\x28\x62\xd8\xcc\x4d\xf1\x0e\x61\xad\x3f\x89\xe2\x82\x66\x1d\x54\xb6\x8b\x74\x10\x82\x8a\x0a\x21\x14\x48\x22\x00\x9d\x42\xf7\x78\x78\xb2\xc3\xf9\x67\x34\x21\x9d\x75\xdc\x08\xae\x83\x3f\xd0\xe0\x4f\x39\xda\x51\x72\x16\xc4\x51\xa8\x69\x80\xd5\xb8\x3e\x22\x6d\xb2\x41\x70\xe5\x6b\x58\xd6\xc0\x35\x9b\x14\x58\x42\x69\x64\x11\x07\x51\xc2\xe9\xcb\x9a\x46\x0e\xf0\x4e\xe4\x94\xcf\xa2\x48\x3f\x3c\xfd\x85\x8e\x8b\x6b\xab\x42\x39\xc9\xba\x1c\xaf\x36\xb4\xe0\xca\xa7\x0e\x75\xc3\x99\xb9\x1e\x2f\x6f\x09\x5c\x30\x69\xa8\x58\xde\x39\x66\xc0\x27\x3d\x72\x0c\xe0\x27\xdd\x66\xa8\x89\xa3\x1c\x24\x20\xbe\xf8\xca\xb1\x93\x63\x34\x00\x0b\xe0\xd8\xf1\xa5\x2f\x74\x81\x32\xc4\x38\xcd\x36\xc2\x4d\xee\x2e\x7d\x81\x9d\xbc\x8c\xbe\x73\x49\xe0\x53\x5a\xe0\x15\x98\x0b\xce\x21\x48\x96\x15\x13\x7d\x63\x25\x8c\x1a\xfa\xf3\x60\xd1\x29\xe3\xb1\xa0\x95\xf3\xac\x11\x83\x77\xf2\x9a\x3b\xbc\xa7\xc7\x50\xe4\x84\xb3\x67\xf9\xa5\x56\x11\xea\x8f\xd8\xa7\x0e\x27\x93\x9c\x16\x4e\xa7\x32\x1a\x2e\xc7\x14\xf5\x2b\x18\x8f\x7b\xa4\xa6\x73\x80\x9d\x22\x28\xa2\xf1\xbb\x20\x2b\x5e\xc3\x4b\x22\xab\xe6\xbe\x9d\xdf\xf1\xf4\x53\xd6\x95\x31\xa6\x44\xc3\x0f\x6e\x95\x6f\x82\x62\xd6\x9f\xc4\x69\x9a\x75\x3a\x4e\x8b\x1b\x64\x7b\xb3\x4b\x06\x64\x7b\xab\x4b\x1e\x92\xed\x2d\x31\x68\x84\xbe\x60\x3c\x26\x1b\xa4\xa3\x36\x1d\x03\xeb\x25\x28\x24\xcf\xd0\xde\x45\xc8\xf6\x16\x19\x19\x09\x25\x9d\x95\xa8\xef\x91\x21\xc6\x7e\x46\xf3\x65\x5c\x48\xea\xe1\x33\xf8\x66\x19\x17\xd1\xcf\x51\x31\xe3\x73\x22\x29\xd0\xe8\x5b\x4f\xd1\x51\xcf\x9c\x41\x59\xb9\x18\x21\xaf\xdf\x3c\xf1\xf9\x49\xdf\x6a\xd5\xb7\x06\x1a\xf6\x00\xad\x11\x35\xbc\x56\x6b\x47\x2f\x1c\x1a\x4f\xc4\x88\x45\x67\xc5\xae\x90\x66\x2f\x83\xf1\xac\x63\x33\xa6\x08\xd3\x16\xe3\xfa\xa5\xf3\xa5\xe7\xea\xa4\x8b\x0b\x71\x84\x40\x57\x36\x5c\x6d\x67\xc7\xec\xbe\x5c\x47\x88\x08\xd5\xda\x65\x54\x4c\xe3\x89\x00\xb1\xe7\x08\x3a\xe0\x76\x49\xe2\x09\x3e\xec\xc9\xc2\x4d\x98\x4b\x71\x63\x97\x50\xf1\x0c\x8f\x0c\xc8\x96\x06\xbd\x26\x34\xce\xa9\x35\xbc\xc1\x80\x84\x69\xd2\x2e\x48\x10\x86\x44\x94\x2a\x52\xb3\xca\x3e\x89\x8a\x76\x4e\x82\x38\xa3\x41\x78\x49\xc6\xe9\x32\x29\x68\x58\x82\xa5\xaf\x34\xce\x6b\xbd\x08\x07\x03\xf2\xf1\xf0\xc5\xe1\x88\x4c\xa2\xe9\x32\xa3\x84\x1d\xd8\x12\x9a\xb3\x13\x20\x3b\xa5\x5d\xe6\x26\xb3\xfa\x2d\x88\xe4\x8f\x33\xc9\xe6\x64\x50\x8c\x40\x89\x95\x92\x65\xae\xd0\x9a\xd1\x49\x00\xea\x98\xf3\x59\x1a\x53\xde\xc3\x28\x99\xae\xd7\x30\x82\x0a\x1e\x60\x73\x7e\x31\xe8\x1e\x49\x9d\x95\x6f\x2c\x72\x39\x27\xb5\xa2\xbe\x67\x8b\xeb\xb8\xaa\x31\x44\x40\xbc\x61\x72\x1e\x68\xb2\xce\x69\xe1\xcc\x29\x27\xab\xb7\xc1\x9c\xda\xfb\x90\xce\xc1\x72\xa6\x5b\xd6\xb3\xf9\x54\xef\x67\xba\x62\x4f\x9d\x8a\x2f\x0a\x0c\x6a\xa9\x56\xfe\x55\x0c\x5b\x56\xb2\xc8\xe8\x59\x94\x2e\x73\xd5\xa1\xad\x1d\x86\x92\x28\x21\x51\x52\x38\x25\xea\xf0\x8f\xfa\xeb\x6b\x90\xfd\x4d\xd2\x8c\xc0\x23\xe1\x88\xec\x92\xcd\x1d\x12\x91\xef\xe5\x00\xe4\x7b\x61\x12\x6d\x6c\x94\x15\x67\x7f\x56\x9f\x37\x76\xc9\x46\x47\xe2\x20\x22\x8f\xc8\xe6\x09\x93\xf0\xc9\xd5\x15\x19\xee\x94\x56\x52\xc1\xca\x05\x3d\x6c\x90\x88\x3c\x2c\x9b\xb9\x0d\xbb\x17\x4c\x38\x28\x63\xfb\xf2\xef\xda\x49\x35\x53\xae\xbb\x9d\xae\x35\x85\x83\x01\x99\x44\x59\x5e\x10\x1a\xd3\x39\x4d\x0a\x76\xbe\xe2\x68\xea\x91\xfc\x73\xb4\x20\x51\xb1\xca\x94\x1b\xd8\x1f\xfa\xb0\xcf\xf0\x57\x39\x03\xf0\x74\x3e\x0c\x23\xd6\x48\x10\xab\x45\x2e\xf0\xe9\xf0\x1f\x17\xdf\x7e\xbe\xa8\x49\xa7\x84\x41\x1c\x47\x64\x83\x6c\x9e\x48\x3e\x41\x36\x88\xd3\x0d\x0f\xda\x6b\x11\x6c\x31\x3f\x0f\xa4\xd8\x2a\x3d\xb4\xcf\xa9\xe2\xc6\xac\xe7\x0f\xcd\x54\x98\xb0\x65\x62\xea\x96\x8b\xbf\x86\x32\x49\x19\x43\x1a\x56\x31\x24\xd2\x88\xa6\x6b\x39\xca\x60\x40\xc6\x41\x3c\x5e\xc6\x41\x41\xa5\xe0\xc3\x8e\x7c\xa2\x2f\x24\x2a\xe8\xfc\x16\xec\x88\xb1\xa2\xe3\x3f\x11\x53\xea\xda\xb0\xd7\x2b\xed\x2b\xb7\x9c\x90\xdf\x8f\xc1\x60\xe6\xf2\xd5\x79\x0b\x71\xb4\x45\xa2\x1f\x35\xda\x10\xa1\x8b\x14\x37\x93\x69\x85\xc6\x88\x43\x36\xd6\x18\xc9\x74\x75\xab\xa9\x54\x22\x7e\x5d\x52\xb9\x1e\x04\x35\xec\x11\xff\xa0\x7e\x9f\x8e\x08\x15\xd3\x3a\x22\x0e\x0d\xb2\x4d\x13\xb4\x54\x2a\x89\x4a\x10\x52\xa6\x23\x2a\x47\x88\x28\x01\x27\x0c\x68\x4d\x23\xa6\x5a\x43\x84\x87\xe8\x3b\x1d\x1b\xb8\x59\x5d\x41\x24\x4b\x71\x2a\xc6\xf0\x9c\x88\x73\xef\x29\xdc\x3a\xee\xdf\xb1\x46\x89\x0f\xb9\x03\x23\x93\xeb\x4b\xab\x45\x0c\xbd\x88\xac\x51\x6b\x98\xaa\x54\x0e\x7a\x54\xb5\x7a\x06\x8c\x51\xce\x81\x58\x99\xbb\x1e\x69\x13\x75\x94\x3a\x89\xfa\xe4\x60\xd1\xb5\x52\x26\x39\x18\x90\x7c\x39\xe7\x37\x74\x9e\x5d\x4a\x88\x88\x0a\x5e\x54\x77\x1c\x9d\x30\xae\xa8\xbe\x60\x4b\xf2\xf1\x1f\xd9\xbc\x89\x08\x29\x6d\x3a\x28\x18\x0c\x48\x46\xe7\xe9\x19\x5c\x63\x92\xf1\x32\xcb\x98\x7c\xaa\x84\xd3\x14\x92\x45\x37\xa3\x1c\x7a\xee\xe9\x6d\xbe\x8a\xc6\x4f\x22\xb3\xb1\xe6\xcf\x18\x19\x79\xe4\xd4\xdf\x98\xd2\x3e\x58\xeb\xb0\xe4\x5a\xc7\x7b\x6a\x95\x3c\xce\x43\x65\x85\x75\xe5\x20\xc9\x8a\xed\x60\xf8\x92\xc4\xbc\xbf\xe0\xbd\x65\x6d\x8d\xc5\x2d\x13\x36\xb5\x80\xde\x77\xb8\xbd\xaa\x6d\x82\x21\xae\x45\x3b\xdd\x9e\x37\xfb\x79\x9a\xc6\x65\x79\x4c\x08\x29\xc9\x3a\xaa\xc8\xc3\x97\x9b\xa5\xcd\x56\x65\x72\x2e\x5c\x96\xfb\x9e\x06\xa5\x3d\x3e\xe2\x99\x6b\x8c\x20\x5c\xfb\x0d\x40\x9d\xb2\xd9\x90\x86\xb3\xa3\xc7\xbd\x16\xbf\xfb\x6d\x8d\xbe\x81\x9f\xac\x6f\xad\xd1\x13\xf6\x1b\x5f\xc7\xb6\x46\x4f\x7b\x3e\x5b\x8f\x28\x29\x5a\xa3\xcd\x21\xfb\x99\xd1\x20\x6e\x8d\x36\xb7\xd8\x6f\x7e\x2b\xdb\x1a\x6d\x6e\xb3\xaf\x25\x87\x82\x06\x96\x02\xec\xc9\xf5\x49\xef\xe9\x6f\x69\x17\x55\x73\x0d\x7d\x33\x6b\x22\x5c\xc9\x2a\x46\x45\x66\x39\xdb\xb6\x08\xe7\xae\x68\x62\xe4\x2f\x5a\x61\x69\x64\xf6\xa4\x49\x5d\xb7\xb0\x3b\x2a\x31\x36\x6a\xd4\x28\xba\x12\xf7\x4e\x97\x64\x3b\xd9\x92\x36\x30\x61\xb2\x86\x5d\x6f\xc9\xf4\xdd\xbd\x25\xd3\xbd\x25\xd3\x7f\x8b\x25\x93\x5e\x08\x77\x65\xce\xf4\x3c\x9a\xbe\x5d\xce\x4f\x81\x15\x2a\xee\x7c\x1a\x4d\x13\x48\xec\xff\xa2\x38\xf9\xb2\x88\x62\xd3\xbe\xa6\x3f\x80\x34\xfe\xaf\x04\x1b\x7b\x41\xc6\x69\x32\x89\x1c\x63\x20\x79\x32\x43\xbb\x02\x9c\x5d\x60\x5b\x90\x03\xe7\xbc\x3a\x27\xc0\xef\x09\x3c\xd8\x60\xe7\x2c\xc6\xb7\xb4\x95\x2c\x2c\x05\x36\x37\xa0\x9c\x79\xc8\x70\xcc\x21\xa3\x9c\x24\x74\x1a\x14\xd1\x19\xed\x49\x4e\x04\x17\x47\xc5\x79\xda\xce\xc9\x38\x9d\x2f\xa4\xb4\x0a\xa5\xd8\xdc\xaa\x92\x93\x38\x0d\x8a\x28\x99\x92\x45\x1a\x25\x45\x8f\x5f\x87\x32\xb2\x0f\xd3\xf3\xc4\x3a\xd3\x99\x6a\x12\xf7\xf8\x76\xc5\xb1\x7c\xa5\xf0\x7d\x2d\xc7\xc2\x96\x52\x42\x69\x08\xa7\xe8\x53\x3d\xc7\xa1\xdf\x18\x06\x90\x76\xad\xec\x7c\xcc\x76\x0d\x06\x0c\xf5\x4b\x2e\xac\xda\xed\xf3\xb9\xe8\x8c\xfb\x2f\x3f\xbe\xfa\xf4\xfc\xe0\xa7\xb7\x47\x6f\x9e\xbf\x7c\xff\xe9\xfd\xe1\xd1\xdb\x17\x07\x6f\x7f\xfa\xf4\xe6\xf0\xc5\x4b\x74\x86\x53\x9a\x38\x98\xc9\xfe\x22\x08\x5f\xd3\x49\xd1\xe1\x5f\x45\xfa\xf1\x3c\xcd\xf7\x15\x16\x45\x9b\xfd\x22\x15\xe2\xd2\xe6\x93\x6e\x8f\x3c\x79\x6c\xde\xf0\xe0\xdd\x12\x86\xd3\xe1\x8d\x98\x06\x18\xe6\xc4\xcb\xc3\x6f\x09\xce\x9f\xab\xb3\xb1\x79\x68\x5e\x15\x87\xae\xd4\x61\x60\xd1\x83\x90\x22\x7d\x45\x2f\xe4\xb8\xf3\xe5\x69\x5e\x64\x9d\x2d\x84\xbf\xd8\xba\xda\xe7\xc5\xa5\x96\x7b\x83\x3c\xd9\xee\x92\x01\x46\x91\x8d\xee\xf7\xd1\x74\x56\x88\x62\x3d\x12\x93\x87\x5f\x19\x9f\x62\x07\xbe\x53\xb4\x96\xca\x74\xb7\xc6\xae\x3c\x9e\x99\x68\x55\xda\xb9\xdf\x6d\x06\x2c\xb5\x29\x6f\xac\xdb\xe7\x6b\x7e\x83\xd4\x4f\x50\x1d\xa7\xe3\x92\x7c\xf9\x8a\xf8\x20\xf3\x6f\x3b\x77\xca\xb8\xb3\xf9\xac\x4d\xb2\x74\x7e\x54\x4c\x9e\xde\x4f\x9c\x67\xe2\xc4\x3b\xa3\x32\x46\x26\x5e\x21\xc9\x49\x63\xdf\x34\x48\x56\x67\x64\xf6\x93\xa3\xf2\x39\x6b\x0f\x6f\xf7\xd7\x26\x1b\xa2\x7a\xf2\x8c\x90\xf6\x66\x9b\x8c\x48\x7b\xd8\xbe\x3d\x8f\xaa\xc3\x24\x3b\xb1\xb2\x52\xff\x60\x70\x39\x61\x82\xf1\x7c\x19\x17\x11\x17\x2a\x4f\x2f\xc9\xd6\x7f\xe6\x4c\x3c\x57\x36\x74\x01\xab\xb9\xa0\x53\x9a\x55\x6c\x25\xef\x45\xad\x75\xfb\xf7\xaa\x33\x22\x6c\x99\x4b\x66\x44\xa0\xc9\xa2\x3e\x86\x35\xd5\xa2\xda\x5c\xa3\x39\xcd\xad\xac\xad\x6e\x7f\x91\x9e\x77\x36\xb7\x9e\x76\xbb\x26\x4a\xf7\x67\x74\xfc\x99\x44\x13\x03\xa7\x48\x2c\xb2\x10\x91\x47\xd3\x84\x86\x07\xf9\x5b\x9d\xed\x28\xa2\x55\x1d\x33\x7a\x21\x7a\x6c\x22\x43\x12\x2d\x1c\xfa\xa0\xed\xc2\x94\xc4\x52\x76\x64\x39\x8f\x98\x18\x1e\xc4\xb9\xb6\x5a\xb6\x5b\xaf\xc5\x97\x0f\x43\x92\xdd\x0c\x7b\x64\xb3\xdb\x23\x9b\x4f\x90\x3c\xb2\xd5\x35\x72\xbb\x64\x77\x77\x97\x91\xac\x97\x0a\x33\xc6\x3e\x1e\x05\x31\x74\x8a\x70\xd5\x81\xbe\xf0\xe0\xa2\xa6\x4b\x44\x5c\x91\x60\x0b\x81\x06\x79\x38\x76\xb0\x0c\x67\x5a\x30\xac\x68\x57\x09\x87\xb0\x2c\xa2\x29\xe1\x72\xba\x45\x6f\xaa\x0b\x06\xfe\x0c\xa3\x58\x06\xcc\xe7\x71\x97\xf7\x06\xe9\x32\x3b\x5d\x72\x75\x45\x5a\xc3\x96\xd0\x11\x0f\x06\x64\xac\xa8\x88\x09\xcf\x72\x22\x55\xeb\x1c\x28\x2a\xf8\x44\x2b\x49\xdb\x15\xb2\xe5\xfd\xad\x35\xcf\x62\x6e\x3d\x2a\x48\xcf\xfc\xf2\x29\x9d\x47\xc9\xd2\x5e\x05\xed\xc9\x2d\xff\xda\x50\xb7\xac\x7c\x53\x5d\x8f\x35\xe8\xd0\x0d\x28\x68\x59\x4d\x42\x47\x95\x34\xe4\xa3\x1e\xba\x12\xf9\x88\xe6\x5d\xc2\x39\xba\x0b\xca\xf9\x3a\x28\x13\x2c\xbf\x0c\x65\x0e\xef\xae\x45\x19\x60\x0c\x89\xc4\x26\x8a\x44\x73\x2e\x8a\x1c\x66\xee\xb3\x38\xb7\x16\xa3\x80\xe9\x87\xd1\x59\x14\xd2\xf0\xf9\x65\x05\x0f\xbf\x09\x35\xd5\xe0\xe6\xe8\xae\x91\xb3\x2c\xc5\xce\xd1\xca\xe8\x39\xba\x0d\x7e\xdc\x5b\x58\x5e\xb5\x42\x51\x99\xc4\xa5\x1f\x4c\x37\xc6\x8b\xdc\xd9\xcc\xb9\x28\xc5\x91\x68\xda\x45\x91\x23\x9f\xf9\x30\xe4\x59\x5e\xb0\x5f\xdd\x52\x60\xdb\x6c\x93\x67\x7c\x6b\x16\x9e\x31\x56\xc3\x66\xe9\xc9\x11\xbd\xcb\xad\xd8\xfb\x62\x3a\xd1\x88\x63\x12\x44\xc5\xd9\xc6\x11\x3d\x92\x60\x4e\xf9\x03\x1f\xf6\xcb\x12\xc1\x04\x0c\xab\x53\xd5\xe0\xc1\xbc\x73\x08\x85\x36\x7a\x04\x2b\xcb\x59\x21\xf1\xc4\x9a\xec\x92\xb2\x97\xba\x0f\xbb\x03\x74\xa4\xc9\xa3\x5f\x05\x4f\xcc\xe1\x96\x4a\x94\x3f\xde\x3c\x31\x45\xe1\xf6\xf0\x82\x89\xcc\xee\xe4\xf6\xf3\x38\x1a\x53\x26\x99\x6c\x91\x87\x50\xdd\x8a\x74\x5e\x33\x33\xf8\x14\x7e\x67\x13\xb4\x2a\xfa\x4b\x55\x01\xce\x26\xa3\x8e\x88\x16\x1f\xe0\x88\x13\x97\x60\x36\xe6\x9e\x3c\xee\x8a\x3d\xbc\x48\x05\x7c\x97\x3c\x94\xa7\x4a\xdf\x0c\x58\x15\x71\xe9\xf0\xc9\xe3\x9e\x68\x7f\xb5\x29\xa8\x38\x95\xf3\xe1\x7b\x8e\xe5\x77\x8a\xfd\x20\x1f\x47\x51\x15\xfe\x3d\xc7\xf9\xdf\x10\xf3\x52\xab\x03\xda\x81\x66\xf8\x5f\x6d\x02\xb4\x7b\x9a\xb2\x19\xd8\xd3\x0e\x6c\x4a\xa6\xa0\x94\xb7\x97\xa0\x5c\x55\xe8\x62\xdb\xe7\xc0\x66\x05\x69\xca\xc0\x5d\x6b\x78\xd1\x22\x1b\x44\x9c\x71\x00\xed\xfc\xb7\x32\x2b\x78\x3c\xec\x11\x9c\x54\xe6\x33\xe0\x8b\x34\xfd\x40\x67\xcd\x91\xf5\xdd\xb3\x61\x60\xc5\x8e\x9c\x14\x07\x0e\x2f\xf0\x51\x59\x86\x53\x8a\x23\x73\xe4\x26\xb9\xfd\x48\xd3\x78\x64\x27\x38\x50\x4c\x02\x19\xd9\x09\x18\x4a\x89\x65\x23\x3b\xc1\x85\x3a\x72\xc0\x8e\xbc\x70\xb8\x51\x9d\xe2\xa9\xcf\x05\x3c\xf2\x43\xe2\xc1\xea\x14\x0f\x1c\xc6\x36\x4a\x72\x21\x7d\xd3\xe3\xe6\xb8\xe5\xcc\x09\xc2\x69\x2e\xac\xa0\xfa\x91\x77\xdd\x5d\xcb\x6b\x5d\xf3\x72\xa8\x35\xda\x7c\xda\x6b\x99\x97\x4a\xad\xd1\x16\x58\x30\xc0\xc2\x68\x8d\x36\x37\x7b\x2d\x7c\x35\xd5\x1a\x99\x9f\xd7\x27\xbd\xcd\xe1\xef\xec\xd2\xe5\x80\xdb\xc6\x57\xf8\x20\x8a\x92\xa2\xcc\x05\x91\xb8\xbd\x8a\x92\x82\x7b\x67\x61\x3f\x1e\xab\x5f\x27\x3a\x71\x1b\xfd\xb6\x9c\xb7\x44\x49\xc1\x5d\xb7\x44\x49\xf1\xe4\xb1\x02\x7b\xaa\x2b\xda\xfa\xe6\x49\x49\x5d\x0c\xbe\xc6\x95\x91\x7d\x34\xfc\x8a\xde\xb8\x00\xdc\x36\x43\x38\x48\x8a\x15\x2d\x2f\x8c\x12\x15\x06\x17\xd0\x5c\x45\xc9\x1b\x99\x57\x44\x49\x21\x45\xc5\x67\x37\x72\xe9\xc2\x7b\x55\x6f\x06\xb1\xd9\x28\x8a\xdd\xbd\x1d\xc4\xbd\x1d\xc4\x9f\xd7\x0e\x82\x68\x43\x08\x2e\x2a\xdd\x91\x0d\x44\x03\xd3\x06\x9b\xd5\x73\xd3\x85\x14\x0c\xd2\xb5\xe7\x8e\xbe\x47\x42\x3d\x9f\xd1\x44\xbd\x57\xec\x71\xdb\x6f\x26\x80\x2b\x07\x0e\x52\xb2\x1c\x78\x6d\x23\x2c\xf5\xb7\xfd\x3c\x11\x38\xa9\x94\x1f\xf9\xff\x57\x57\xa4\xdd\x46\x7c\x36\x95\x2f\x17\xf8\x8f\x1d\xf4\xd4\x30\x4a\x44\xeb\x8d\x3d\x7e\x4c\x69\x81\x4d\x7e\xc1\x80\xbc\x9d\xcb\x87\xa0\xc0\x4b\x58\x25\x86\xb5\xbb\x96\xef\xb9\xb1\xab\x29\x45\x4b\x35\x93\xae\x15\x57\x46\x3a\xb2\x8f\x5d\xc3\xa0\x1d\xd0\x83\x0d\xda\xed\x46\x2a\x4d\xd1\xc0\xca\xdf\x38\x76\xe0\xeb\xc7\xc6\xc8\x18\x67\x94\x11\x93\x5c\x0f\xa6\x5b\x16\x4e\xee\x61\x34\x99\x50\x30\x48\xe6\x28\xb7\xce\x25\xe7\xea\x5d\x08\x3e\x8e\x48\x94\x88\x59\x92\xb6\xcb\x89\xf7\x10\x62\x1e\x5d\xd8\x76\xe8\xeb\x47\xb0\xe0\x1c\x46\xf5\xa2\x1c\x95\xe7\xfe\x37\xb3\x26\xdd\x95\xde\xea\x69\x82\x54\xa4\xba\x0a\x46\xd3\xf9\x69\x94\xb8\x1e\x6e\x8a\x74\x4a\x19\x77\x67\x35\xd0\x69\x9f\x2f\xaa\x60\xb1\xa0\x09\xac\xa5\x20\xe1\x6f\x20\x2c\xec\x8a\xda\xea\xee\x61\x04\x63\x9a\x45\x63\xc6\x9e\x64\xaf\xea\x0b\x8b\x0b\xd4\x74\x22\x60\x61\x1f\xaa\x44\xad\x1c\x5e\x9d\xde\xaf\x0a\xad\x4a\x6f\xc1\xaf\x4c\x76\x48\x3d\x76\xc7\x41\x1c\x0b\xfc\xca\x6b\x1c\x3e\xa2\x59\xa0\x97\x6e\x1e\xfd\x2a\x9c\x0b\xc2\x75\xdd\x2c\xc8\x7b\xec\x7f\x49\x68\xe0\xfe\xd7\x73\x6f\x87\xf1\xad\x6c\x41\xfd\x3a\xd3\x4a\xd4\xf8\xbd\x33\xf9\x16\xae\x58\x15\xeb\xbb\xbb\x20\x5d\x4c\xa2\xc4\x7a\xab\x54\x87\x04\xed\xb5\x48\x54\x25\x6e\x98\x6d\xa5\x01\xcf\xdd\xcb\x9f\x97\x1f\xfd\xb9\xc6\xd7\xd5\xd0\x34\x58\x66\x46\xed\x55\x83\x5e\x87\x51\x6b\x17\x00\x5d\xf2\x8c\xb4\xdb\x64\xd4\xcc\x20\x0b\xa1\xcc\x6b\x96\xb5\x02\xde\x18\xef\xe7\xca\x09\x25\x33\xfa\x9e\x7b\x69\xfd\x85\x1f\x67\x72\xef\x91\xb7\xc2\x01\x66\xf8\xc1\x1c\x13\x19\x90\x78\x25\x16\x75\x63\x5e\x14\x82\x5f\x25\x1b\x7f\x3e\xff\x4c\x6a\x79\xed\x10\x7e\xe5\x47\x4a\xe8\x4e\x4c\x58\x67\x75\xd4\x19\xdb\x5a\x09\xee\xd0\xa6\xe4\x47\x9e\x4c\x08\xe4\x25\x7c\x03\x2c\xd2\xf9\xa2\xb8\xc4\x2a\xc1\x06\x9b\x68\xed\x2a\x34\xe9\x11\xb1\xa7\x11\x48\x1f\x2b\xe0\x46\x7a\x9c\x2a\xf5\x35\xe5\xc5\x44\xe5\x40\x44\x95\x75\x63\x30\x2e\x56\x36\x3c\x62\xc1\x4d\xc6\xa1\x1f\xe3\x95\xfb\x87\x7a\x1d\xe5\x85\xf3\xf2\xef\xd8\x18\xcd\x89\xc7\x29\x54\xe5\xe8\x75\xcd\xee\xf6\xa2\xde\x05\xc9\x9b\xfa\xe5\x22\xe4\x96\xad\xe2\x1d\x9c\x52\x45\x16\x69\x81\xde\xba\xf2\xc2\x52\x38\xe2\x7e\x87\x88\xf1\xb6\x4f\x3d\x21\x14\xa0\xe6\xb3\x22\x63\x6f\x53\xeb\x91\x6f\x5f\x25\x0b\xd2\xbe\xfd\xb2\x9d\x85\x98\xcd\x93\x5d\xdc\x63\x0d\x8b\x87\xb1\xb1\xeb\x2a\xfa\xc5\x6b\x2d\xf7\x85\x16\x87\xd4\x22\x50\x27\xc5\xaf\x6e\xd5\xab\xb9\xc1\x40\x4e\x37\x3d\xa3\xd9\x65\x31\x03\x5f\x24\xa8\x1e\x8c\x1d\xd7\xf1\x94\xb4\x48\x73\xf0\x63\xbc\xd4\xf5\xdf\x50\x28\xdf\x4b\x77\xda\x84\xab\x74\xbe\xee\x91\x76\x5b\x2a\xdf\x2b\x94\x14\xef\xf8\x2c\x59\x3a\x3d\xa5\xbe\xbb\x3e\xe9\x6d\x36\x8a\xb5\xf7\x15\x75\x72\x70\x1b\x5d\xad\x94\xcb\x18\x48\x89\x56\x4e\x9a\x99\xb1\xff\xb9\xaa\x0c\x7e\x3d\xd6\x3f\x4f\x50\xf2\x36\xfe\xb0\x74\x73\x2c\x8d\x2b\xe7\xd8\x2f\xa9\x9d\x63\xbf\x9f\xa2\xea\x90\x7e\xce\xa9\xb1\x81\x86\xce\xb9\x7b\x5f\x45\x45\xc7\x0a\xaf\xa2\xa3\xe3\xf0\xb6\x92\x8e\xa5\xae\xa8\xa5\x33\x8b\x54\xa8\xe9\x78\x8b\x55\x65\x6f\xa2\xa8\x63\xb8\x2d\x51\xd4\x35\x73\x94\x2f\xba\xd5\x40\x51\xd7\x28\x9a\xd7\xd7\x7a\x5c\xe7\xb9\xfd\x5b\x85\x3c\x78\xf1\x55\x08\x44\x96\xb0\x49\x84\xa7\xaf\x48\x24\x76\xa1\x0a\x32\x91\xed\x56\x97\xbf\x91\x4e\x97\x4b\x52\x4d\xde\xcc\x79\xda\xbb\xdb\xd7\x72\x6a\x94\x0d\xe8\xee\xee\xa3\x8f\x54\xbe\xdf\xf1\xf0\x61\xe4\xe2\x36\xca\x9b\xfb\xb6\x1d\xd3\xac\x08\xa2\xc4\xef\xdf\xd6\x41\x24\xbf\x4d\xaa\x21\x6a\x0e\xd4\x37\xd3\xab\xc9\x5a\x14\xb1\x32\x6a\xbd\x41\x14\x34\x9b\xb3\x23\x7f\x34\x81\x9a\xcd\x7e\x87\xc2\x6b\x2d\x99\x46\x67\x34\x91\x26\x2d\xe6\x91\xba\xcc\x5d\xae\x65\xff\xc2\x8f\xd9\xda\xe2\x16\xb0\xcc\x2b\x77\xda\xf5\xdb\xdf\x62\x88\xe6\x4b\x84\x3b\xa7\x6d\x15\x5e\xe1\x38\x3d\xa3\x59\x76\x9e\x45\x45\x41\xc1\xdc\x8b\xf7\xaa\x45\x36\xa0\xf7\x8d\x71\x77\x0e\x5a\xf6\x1c\x3f\xe4\x07\x2b\x08\x7d\x14\x8d\x12\x81\xc2\xc2\xf5\x3b\x6c\xbf\xb5\x6f\x84\x4c\x57\x2b\x69\x35\xa7\xb5\xb6\x25\x78\xf3\xb8\x10\xf0\x63\x70\x30\x00\x55\x78\x30\x67\xab\x02\xbc\x1e\x0a\x6d\x16\x1b\x2f\xe3\x04\x94\xdf\x31\xc4\xd1\x67\x4a\x02\x92\x47\xc9\x34\xa6\xca\x0f\x17\x40\xf6\x0d\x93\x68\xa0\x60\xee\x66\x86\xbb\xe5\xe0\xad\x5d\x5d\x91\xe3\xf6\xf1\xe6\x49\xfb\xa4\xab\x84\xc1\x1a\x37\x00\xa2\x7b\x26\xde\xd9\x17\x76\x6d\x58\x22\xba\x73\x1b\x28\x8e\x0a\xb0\x55\xd8\xec\x91\x47\x60\x8f\x3d\x84\xbe\x6c\x62\x47\x34\xba\x43\x8e\x20\x2b\x1d\x35\xf4\xa4\x6b\x87\xb2\xd3\x82\x74\xe8\xf0\x50\x02\xea\x06\x06\x03\x12\xc4\x31\x39\x0d\xf2\x68\xcc\xfd\x1f\xc0\x63\x81\xed\x2d\xa1\xc0\x89\x53\x76\x32\x96\xbd\xe9\x91\xed\xad\x3a\xa3\x13\x73\x61\x0b\x8e\x26\x4f\xe0\x52\x17\x49\xe8\x14\x04\x48\x08\x0a\x75\x7c\xd2\x22\xbb\x3f\xc0\xfa\xd4\x69\x8f\x79\x62\xa5\x32\x6d\x4f\xd6\xb6\x2a\x07\x98\xd1\xd2\x9e\x55\xac\x76\xdc\x6a\x29\xcd\x6a\xb7\x5f\x86\x43\x18\x87\xe8\x76\xac\x6d\x14\x15\x79\xf0\x80\xe0\xef\x63\xf4\x1b\xb9\x80\x3b\x91\xbb\xae\x8a\x8c\x31\x98\xde\x68\x6e\xc4\xf2\xad\x9a\x1a\x39\x0b\xe6\xdc\x88\x09\x33\xa7\x06\x79\x5c\xbb\xe5\xcc\x58\xfd\xaa\x98\x18\xd4\xe6\xd7\x9e\x97\xbb\x9c\x18\xd3\xf5\x89\x66\xa4\x68\x26\xe0\x6c\xd4\x02\x5b\x84\x2d\x8e\x74\x7e\x48\x6a\x09\x63\x85\x4d\x31\x15\x9b\x8f\x15\xe0\xd6\xc9\xf1\xb6\x00\x95\x69\x1c\x44\x41\x6c\x9e\x58\x09\xfa\xdb\xdd\x1d\x00\xab\x37\xd8\x1e\xf0\x58\xc4\x10\xeb\xf7\x04\xd4\xd8\x1d\x4d\x64\x34\x21\x1d\x94\x85\x38\xa4\xcd\x8f\x6f\x38\xb1\xc0\xb0\x7d\xaf\x21\x36\x2b\xa6\x5c\x6c\x12\xf2\x54\xed\x9b\x67\x98\x37\xdf\x54\xb7\x54\xfc\x3d\x67\xc2\xc5\x67\xcb\x98\x77\xa3\xa2\x63\xb3\x72\x3c\xdd\xda\xfb\x5a\xa3\x79\x56\x19\x7c\x28\x22\xbf\x74\x7e\x0d\x2f\x8a\xa5\xbb\xbd\xf0\x56\x14\x07\x79\x41\x8e\x4f\x98\x30\xc1\xeb\xbd\xd1\xb4\xaf\xfb\xe7\x5d\xcd\x01\xc8\x59\xc4\xf1\xb1\x04\x07\x1a\xfd\x12\x0a\x3e\x15\x0d\x34\x21\x92\x0a\xe3\x58\x74\x84\x51\x1c\xd8\xbe\x69\x22\xa7\x97\x24\xa4\x93\x60\x19\x83\x22\x34\x5f\x32\x39\x55\x6d\xcc\x2d\xe1\xa6\xa6\x27\xc2\x3c\xda\xb3\x68\x1c\xa3\x6e\xc0\x80\xf5\x8e\xb8\xa2\x28\xdc\xf0\xf4\x56\x6a\x54\x2f\x7d\xb5\x4b\x1d\x31\x5a\x22\xb9\xbd\x46\x80\xe2\x05\x29\x1f\xb7\x18\xc5\xf7\x48\x8b\x2d\x02\xf6\xdf\x49\xeb\x44\x53\xbb\x80\x40\x69\x50\x28\x59\xc6\xf6\xb3\x07\x34\x9b\x8d\xd0\x66\x3b\x98\xb3\xfa\x5b\xb3\x10\x5c\x27\x55\xce\x4a\xe0\x7b\x83\x70\x96\xc7\x67\x3d\x87\x1b\x5e\x36\x1c\x63\xbc\xec\x5f\x58\xf5\x16\x11\x0b\x6e\xd5\xf9\xf7\x31\x3f\x8d\xff\xfb\xa4\x5b\x2f\x22\x08\xe5\xad\xf2\xf6\x50\x7e\xef\x60\x85\xb1\x90\xd0\xcd\x59\x87\x7c\x7b\xea\xde\x65\x59\x38\xf3\x5c\x5a\x88\x7b\x74\x7b\x63\xf0\xfa\xa3\x36\x6f\x65\x84\x2b\x54\xe9\x04\xd5\x66\x0b\x35\xde\x60\x95\xfd\x37\x36\x26\xde\x21\xa5\x7f\x7e\xc7\xa8\xae\x5f\x59\x1a\x4f\xb0\x3f\x59\xc1\xca\x9c\x42\xea\x65\xf2\xf1\x89\xcf\x89\x78\x7f\xb1\xcc\x67\x1d\xc7\x33\xa9\x7c\xa9\x2d\xdd\x8c\xba\x35\xb3\xb1\xb8\x3e\xd7\xcf\x7c\x0e\x40\x71\x4b\xc8\x8f\x67\xe7\xac\x47\xb0\x7f\x59\xcb\x3d\xe9\xad\x9c\xfa\x8a\x09\xc4\xce\x7c\x6f\x3d\x7f\xd0\x75\x47\xea\x10\x88\xff\xed\xe7\xcf\xe7\x91\xb5\xc6\x13\x6b\xe9\x44\xb0\xd9\x04\x57\xa9\x15\xf3\xb1\xf2\x6c\xac\x39\x77\x84\x96\xee\xc8\x58\x92\xc8\xa3\x6d\x13\x9f\xa0\xfc\x7e\x74\x92\xa5\x73\xaf\xb9\x01\x87\xf2\xf1\x96\x53\xfb\xc1\x8e\x65\x20\x64\x58\x06\xad\xf0\x60\x4a\x32\x35\xde\x72\x03\x16\x25\x06\x82\x59\x94\xe1\x4f\xb3\x86\x55\x7d\x15\x5e\x05\x7b\x13\xbe\xb1\xe4\x82\xae\x78\xe2\x03\xdd\x93\x82\x8e\x40\xd7\x43\xb2\x05\xc6\x0f\x5d\xe9\xd1\x59\x20\xaf\x6c\x11\x55\xd6\x89\x9b\x77\x2a\xf6\xad\x28\x28\xf0\xa1\xe0\x77\xec\xb8\xf4\x06\xd9\xe6\x4e\xef\xf9\x6e\x9b\x33\x90\x9c\x04\x93\x82\x66\x6a\x91\xe0\xfe\xde\x68\xad\xfa\xcb\xf8\x7c\x77\x6b\xce\x51\xe2\xb3\x9b\x54\x62\x4f\x84\x8e\x79\x5b\x56\x3f\xf6\xeb\x51\xea\x46\xda\x8e\x79\x53\xc9\x68\x1a\x72\x1a\xf2\xb0\xba\x6f\x0c\x76\x63\xb7\x1a\xa6\x11\xa3\x32\x1d\xce\xa2\x69\xdf\x20\xd1\xdd\x72\xad\x3f\xc4\x1e\x82\xff\x1a\x52\xbf\x34\x48\x6d\xf8\xf7\x87\x22\xfe\x7b\xda\x47\x7f\xbf\x0b\xed\x13\x2f\xe9\xe3\x00\x8d\x37\x25\x7d\x3b\x8c\xd8\x8a\x9b\x8a\x43\xac\x76\xfd\xcd\x76\x16\xb3\x17\xab\xd4\x2f\xe6\xcf\x4b\x6f\xb1\x43\x5f\xfe\xf5\x57\xbe\x84\x17\xe2\xd6\xcf\x35\x52\xad\xeb\x7e\x87\x6c\x92\x0d\xb3\x77\x5d\xee\x93\x89\x47\x12\xf3\x4c\x3d\xf7\x40\x6c\x5d\xba\x19\x0f\xb6\x2b\xfc\xd9\x1b\xb8\xb6\x2c\xbe\x0c\x2e\xb6\xb6\xe2\xd8\xf0\x9c\xab\x95\xb5\xd5\x35\xd5\xaa\xde\x8b\x44\xab\xeb\xb5\x17\xbc\xe5\x57\xbb\xea\x4d\xdc\xf5\x49\x6f\xf3\xf7\x0e\xbd\x7f\x54\xff\xec\x6d\x59\xf1\xee\x4d\x78\x22\x81\xff\xb9\xad\xcb\x52\x3f\x7d\x5b\xa2\xb7\x6f\x4b\xfc\x60\x6d\xe9\x79\xfd\xb6\x54\xcf\xdf\x96\xe8\xfd\xdb\x12\x3d\x80\x5b\x9a\x2f\xe0\x9c\x1a\x1b\x58\xd8\x38\xfe\x51\xbe\xe2\x23\xb8\x23\xef\x2b\xb8\xa3\xd5\x9f\xc1\x1d\x35\x7d\x07\x77\xe4\x3e\x84\x3b\xba\x83\x97\x70\xcb\x5b\x3f\x85\x3b\x6a\xfc\x16\xee\xf7\x8e\xeb\x7f\xd4\xc0\xe2\x6c\x59\x65\x72\x26\x5d\xab\xf0\x1f\x82\x38\x91\xd5\xd9\x12\x9b\x9d\x2d\x0d\x2b\xb1\xa5\xcf\xf0\x6c\xa9\x2d\xcf\x96\xd8\xf4\x6c\x89\x6d\xcf\x96\x96\xf1\x99\xa7\xde\x26\x8b\xe3\x37\xb5\x3f\x3b\xf2\x1b\xa0\x1d\xdd\xc0\x02\xed\xa8\xb1\x09\xda\x91\xc7\x06\xcd\x2e\x7d\xb3\x35\x52\x61\x86\xd6\x74\x91\x34\x37\x44\xfb\xb6\xc9\x2a\x69\x2f\x73\x0a\x8a\xd9\x71\xd1\xe6\x01\xf9\xa6\x29\xa1\xc9\x19\x09\x53\x0a\xd6\x0a\xf0\x3a\x30\x48\x42\xf0\x61\x4b\xfe\xf9\xe6\xf5\xab\xa2\x58\xbc\xa7\xff\x6f\x49\xf3\x62\x0d\x04\xb3\xcb\x05\x4d\x27\x56\x0e\xf7\x63\xa3\xde\x6f\xb4\x25\x5e\x44\xc3\x7d\x1b\x9a\x7c\xb9\xde\x59\x33\x82\x45\x96\x42\x9a\x09\x20\xa9\xff\x92\xcf\xd8\xee\x13\x4d\x93\x34\xa3\xa3\x38\x4a\xe8\xda\x35\xb7\x58\x65\x78\x68\xe4\xed\xfe\xfe\xe5\xec\xfd\xcb\xd9\x3f\xf1\xcb\x59\xfe\x6a\x56\xd8\xb0\x19\xcf\x66\xf9\x86\x43\x6e\xf6\x7a\x56\xec\x7d\x47\x45\x14\x43\x9d\x5c\x9f\x09\x6b\x87\x3f\x4f\x72\xc0\xa2\xe2\x52\xb1\x44\x5d\x64\x1c\x07\x79\x4e\x8e\xa1\xc8\x89\xe8\x26\xcf\xd0\x4c\x98\x57\xb5\x36\x80\x7b\x23\x58\xa5\x42\xb9\xca\x38\x08\xa9\x70\x66\xdd\xdc\xcf\x39\x40\xb2\x9a\x8e\xde\x1e\x7c\xfc\xc0\xce\xd6\x30\x09\xed\x73\x1a\xb5\x39\x69\xb6\x3f\xa3\xdf\x6f\xd0\xef\x9f\xd0\xef\xfc\xd7\xe0\x34\x95\x1f\x93\x28\x49\xe8\xa5\xfa\xa2\xf3\x22\x85\xa7\x8c\x32\x65\x11\x8d\xcd\x84\x24\x48\xcc\x84\x79\x34\xce\xec\x94\x38\x8e\x9c\x42\x06\xbc\x01\x2a\x3f\x8c\x22\xd3\x2c\x48\x42\x35\x14\x23\xeb\x27\xe3\xeb\xa3\xf1\xf5\xce\xf8\x7a\x69\x7c\xfd\x9f\xf1\xf5\x2f\xe3\xeb\xad\xf1\xf5\xc2\xf8\xfa\x87\xf1\x75\xc4\xbf\xd6\x4e\xca\x5d\xd7\xb0\x39\x7a\xb7\xf7\x82\x4d\xf1\x88\x6c\x6f\xf5\x54\xe2\x87\x83\x9f\xde\xee\x7d\x3c\x7a\xff\xf2\xd3\xeb\x97\x6f\x7f\xfa\xf8\x6a\x44\x1e\xeb\x4c\x98\xd5\x91\xfe\xa9\x73\x4a\x28\x67\x44\xbe\x10\x2b\x41\xfb\x51\x87\x8c\x4f\x2f\x0e\x7f\x7e\x4b\xae\x75\x4d\xef\x0e\x5f\xbf\x66\xd0\x1f\x0f\xde\xbc\x3c\x3c\xfa\x38\x22\x9b\xc3\xe1\x70\x20\x7a\x28\x6e\xbc\x9f\xc7\xe9\xf8\xf3\x88\xb4\x19\xeb\xcc\x8b\xb6\x91\xb7\x37\x86\x50\xc6\x23\xfd\xb6\x91\x3f\xc0\x60\xfb\x79\x9d\xef\x93\xfb\x50\x18\xf7\x1b\xd9\x5f\x7d\x23\x5b\x53\x2e\x20\xf2\x59\xb0\x7d\x57\x1e\x20\xf6\xb3\xcb\x45\x91\xfe\xfd\x03\xde\x1c\xc6\x90\xf6\x48\x47\xc0\x60\x0d\x7a\x01\x06\x2c\xa7\xed\x8d\xee\xe4\xba\x6f\x00\x8a\xcb\xf1\x03\x55\x91\x44\x1e\x3c\x90\xb9\x7d\xe9\x2f\x82\x8b\xc9\x33\x7a\xd1\xb6\x5f\xd1\x19\x9e\xbf\x7e\x20\x5b\xac\xb4\xed\xfd\x78\x4b\xba\x8b\x34\x8b\x13\x79\x19\xae\x2e\xf8\x2d\xff\xec\xc4\x7a\x6d\xc7\x41\x25\x8e\x58\xe7\xfa\xaf\xe8\x45\x1f\xb4\x97\xc2\x73\xaf\xcf\xc6\x88\x61\x45\x0e\x5b\xb7\xce\x4f\x74\x5c\xfd\x36\x22\x5b\xdf\x3c\xe1\x25\xd1\xe3\x64\xf9\xe6\x8c\xb1\x3c\x85\xe3\xd6\xe8\x9b\xef\x7a\x2d\x13\xe5\xad\xd1\xd3\xe1\xf5\x49\x6f\xab\x91\xcf\xa7\x7b\xbe\x77\xcf\xf7\xfe\xbc\x7c\x4f\xb3\x3d\xfe\xce\xff\x0e\xf8\x9e\x25\xbb\xaf\x2e\xba\x7b\x24\x77\x59\xd0\x27\xb8\xaf\x14\x6d\xc8\xe6\xb5\xfd\x81\x60\xf7\x3a\x1c\xd1\xe4\x29\x06\x60\xdf\x4a\x84\x5f\x26\x51\xf1\x26\x58\x28\x71\xb1\x2d\x25\xea\x11\xe7\x41\xed\xa1\x94\x35\x99\xd4\x3e\xd2\x6c\xb1\xbd\x69\xc8\xf9\x23\x94\x31\x1c\xaa\x42\xff\x5b\x91\x77\x1a\x9c\x9e\x06\x53\xaa\x5a\xc2\x79\x48\xf8\x1f\xd9\x79\x73\x4f\x9d\x28\xfb\x4d\x75\x76\x9c\x9e\xd1\x38\x18\xcb\x66\xed\x6c\x7d\xc6\x18\xf9\xb2\xa7\xfe\xca\x11\xc4\x4f\xb5\x10\xf9\x2c\x48\x92\x34\x31\xc6\x6d\x42\xe8\x73\xcd\xa8\x02\xa2\xa6\x15\x38\x59\x8d\x3c\x10\x18\x95\xfa\xbc\x34\xaa\x06\xaa\xab\x49\x9c\xdd\x46\x5e\x20\xa3\x32\x75\x1e\xb3\xc7\xe6\x01\xf4\x0f\xd1\x04\x34\xc8\xd5\x03\x87\x40\x3f\x9b\xb0\x3e\x50\x3c\xd7\x70\xea\xab\xac\x18\xf7\xb7\x51\xdd\xb8\xfa\xa6\x05\x50\x99\x62\x85\x32\xac\x98\xdf\xd8\x4a\x3b\x62\x58\x04\xa1\x30\x25\x05\x53\xcf\x8b\x05\x1d\xb3\xcd\x4b\x99\xe7\x63\xa3\x2b\xe1\x3d\xc5\x67\x39\xa5\xab\x38\xa5\x0c\x2e\x14\x11\xb9\x2c\x1b\xac\xf1\x2c\xc8\x82\x71\x41\xb3\x5c\xaa\xf8\xe1\x5e\x5e\x94\x46\xfb\x88\xb7\x8d\x68\x9a\xf4\x90\x2d\x34\x19\xae\xf9\xdd\x7e\x44\xd3\x59\x41\xa4\x47\x5a\xcb\xbb\xaf\x18\x83\x21\x6d\x72\x90\x1e\xf4\x2e\xef\x41\x3b\x1e\x1f\x43\xdc\x42\x04\x60\x20\x28\x2d\xbc\x56\x55\x37\xc4\x9b\xdd\xfe\x2f\x69\x94\x40\xb0\x06\xf2\x0c\xea\x20\x23\xd2\x1a\xb6\xba\x64\x43\x00\x97\x18\xbe\xdd\x78\x2e\x20\x60\xcf\x9f\x7d\x32\x60\x10\x2b\xce\x86\xe8\xe1\x06\xf7\xb8\x7c\xd3\x79\x29\x33\x44\x34\x1d\xd1\xc0\xd6\x09\x66\x88\x10\xcc\xc3\xf5\x31\x6d\xcd\x0b\xf7\xd6\x5c\x31\x2b\x51\xc2\x2a\xf1\x23\x0b\xfb\xa3\xf6\x38\x4a\x62\x8d\x6b\xb3\x43\xee\x81\xe4\x88\x6f\xed\x4a\xa4\x9f\xf1\x78\xcf\x83\x01\xf9\x31\x4a\x42\xc2\x1f\x77\x89\x8e\xaa\x78\xcd\x4c\xa2\x68\xb5\xf4\x4d\x3e\xd8\xbe\xf4\x20\x84\xd4\x8c\x5e\x48\x13\x66\x75\xe6\x62\x69\xfc\xd4\xc3\x4e\x1c\xe5\x67\x25\x56\xcd\x16\x7e\xf7\x02\xc6\x35\xc2\xa6\x66\x87\x44\x1b\xbb\x5b\x18\x5c\xc6\x42\xc6\xb6\x1d\xba\xa9\x4e\xc4\xda\x11\xa1\x2f\x54\x0b\x13\xd2\xe1\x45\x76\x77\xc9\xb0\x6b\x9c\xd2\x4e\x33\x1a\x7c\xd6\xa0\x6c\x94\x1b\xbb\x44\xbc\x2a\x67\x33\xb8\x3f\x0b\xb2\xfd\x34\xa4\x50\x83\xf7\x10\xc6\x26\x5b\x9a\xe3\xe4\x45\xd6\x8c\x42\xf8\xa4\xad\x44\x22\x7b\xac\xc8\x6f\x47\x23\xd0\xdc\x7f\x0f\x91\xdc\x64\xe6\xf3\xa2\xec\x75\xba\x39\xd9\x1e\x1f\xf3\x9d\x45\x46\x27\xd1\x05\x0f\xa2\x35\xbc\xe8\xb2\x59\x00\xae\xe1\x77\x6f\x2f\xa2\xbd\x95\xcf\xbe\xd7\x76\x19\x8e\xa0\x41\x0c\xdc\xbc\x32\x98\x80\x2f\xca\xa7\xe1\x6b\x5f\xb8\x5d\x17\xdd\xc0\x54\xc1\x28\x5e\x60\x9e\xcf\x3e\x2c\x07\x61\xb6\xcd\x97\x83\x9c\x11\xd6\x92\xa6\x8e\x49\x9a\xd9\x26\x74\x79\x91\x95\x45\xc4\x47\x33\xca\xa0\xc6\x62\x6e\xf6\x8a\x4e\x74\xb3\x95\x0e\xd6\x89\x22\x38\xb8\xe1\xb5\x4d\x83\xb0\xfe\x6e\xec\x92\x44\xee\x0b\xdf\x93\x2d\xf2\x8c\x9d\x6c\xc8\x06\x61\xfb\x41\xe2\xa3\x09\xe1\x42\x7e\x46\x2f\xee\x92\x34\xac\x98\x03\x36\x6d\xd4\xb0\x86\xdf\x8c\x38\x1c\x9e\x81\xa8\xe3\xb7\xa1\x80\xdf\x6d\x5a\x2d\x8f\xa5\x93\x65\x1c\x2b\x34\x0c\xe8\x19\x4d\x0a\xfe\x50\x00\x58\xfe\x2f\x79\x9a\x90\xe0\x34\xb2\x79\xbc\x74\x9b\xf8\x31\xfd\x71\x19\xc7\xf6\x1b\x4a\xf9\x98\x80\x95\x7e\xc4\x4b\xbb\x8f\xa1\x78\xc3\x4e\xbb\x9a\xb1\xbb\x6d\x18\x82\x14\xab\x1c\xab\x4e\xd9\x77\x1f\x4c\x28\xa2\x24\xa4\x17\x87\x93\x4e\xbb\xd3\xee\x82\x6f\xc8\x47\x9b\x9e\xe7\x90\x0a\xde\xb1\x13\x2c\x2e\x17\x54\x34\x07\x40\x40\x45\xa6\x3f\xb3\x4e\xd4\xfd\x22\x43\x08\xf7\x19\xfc\x0e\xb9\x16\xa2\x98\x69\xf9\xa7\x5a\x21\x1b\xa4\xdd\x61\x33\xa7\x6a\xdf\x20\xed\x6e\xbb\xd1\xda\x0b\xa3\x7c\x11\x07\x97\x7c\x5e\xc0\xc7\x68\x52\x30\xd9\x56\x61\xc3\x7e\xb3\x76\x01\xd9\x2f\x78\xb1\xaa\x17\xae\xac\x36\x73\xf2\xfd\xcb\xcb\xe8\x01\xdb\xd2\x2c\x8a\xa1\xd3\xbe\x8c\xb7\x78\xd9\x11\x66\x75\x5d\xf2\xe8\x07\x95\xa8\xa6\xd5\xed\x5b\xe5\xc3\x67\x65\xb3\xe9\xcc\xac\x81\x66\x01\xc6\x27\x9b\x3c\xb3\xdf\xb4\x8a\xf7\x60\x6c\xcd\x68\x67\x23\x83\x81\x1e\x68\x7a\x46\xb3\x38\x0d\x42\x1a\x2a\x45\xb0\x67\x4d\xe0\x01\x7c\xd4\x44\x52\xf6\xa6\x71\x40\x3e\x1e\xbe\x38\x1c\x91\x79\xf0\x19\x54\xc3\x51\x72\xb6\x8c\x13\x9a\x05\xa7\x31\xbd\xcb\x01\xea\xd3\x80\xfd\x7a\x77\x93\x3c\x22\x28\xbb\xdb\xed\x67\x74\x11\x07\x63\xda\x69\x93\x36\x38\x75\x63\xa7\x85\x96\x19\x24\x32\x4d\xce\x68\x56\xe4\x3a\xe4\x26\xc8\x7d\x21\x1d\x47\xf3\x20\xb6\x99\x6c\x94\xf8\x99\x7d\x91\xbe\xe0\x05\x5c\xca\xab\x0c\x9f\x69\xba\x35\xe4\x02\x9e\xa8\xa9\x36\x00\x64\x91\xba\xf1\x31\x55\xf8\x99\x26\x63\xac\x95\x6d\x19\x4f\xbc\xab\x71\xa1\xba\xaa\x83\xb3\x26\x52\x4b\xea\x8e\xcf\x13\x9a\x5b\xa8\x4f\xcd\x1d\xc5\x38\xec\x73\x80\x98\xe6\xf9\xc7\x59\x90\x74\x86\xe0\x44\xf6\x11\xb7\x3a\x17\xd6\xfb\x82\xb0\x36\xbb\x10\xbe\x15\xe5\x18\x58\xdc\x5b\x82\x9b\x66\x81\xca\x20\xb9\x14\x8e\x77\x84\x3b\xd2\xa4\x1c\xad\x7d\x81\xd7\xbd\x24\xe4\xea\x7f\x4e\x43\xd1\xe4\x32\x17\x8e\xd4\x73\x72\x4a\x27\x69\x46\xfb\x0e\x5d\xbd\x12\x47\x87\x6a\xdc\x5f\x89\x3d\xa8\x86\xb4\x5e\xc1\x3e\x6f\x20\x5f\xad\xdf\x87\xc2\x54\x6c\x1e\x5c\xf0\xb0\x95\x17\x51\x71\x39\x22\x4f\x41\x85\x2d\x77\x9d\x28\x17\x2e\x8d\xa1\x68\xd7\xde\x64\xd0\x24\x77\x36\x18\xc4\x8e\x51\x14\x4f\x67\x75\x61\xab\xac\x30\xa4\x3b\x63\xb4\xc3\x4e\x21\x1c\x69\x6d\x6f\x15\x10\x5f\xe9\xef\x1f\x0e\xdf\xf6\x15\x96\x79\x7b\xda\x81\x25\xb8\x8e\xcd\x49\x60\x47\xf3\xec\x91\x45\x90\xe7\x8c\x77\x15\xb3\x2c\x5d\x4e\x67\xe6\x0a\x50\x03\x11\xb4\x06\xb5\xba\x97\x93\x9a\xab\x3d\x82\xd3\x92\x47\xe6\x2d\x1d\xb1\x04\x10\x6f\x3b\xcc\xea\x6a\x6a\x3b\x93\xf6\xa3\xa8\x02\xd2\x59\x8f\xf2\x1f\xa3\x24\x2a\xa8\x85\x74\xab\x1b\x20\x21\xa2\x4e\x98\x52\x96\xdb\x51\xb4\x2e\xde\x8b\x4d\x85\xaf\x03\x76\x5e\x4a\x80\xfb\x93\x9f\xa9\x2d\x48\x4d\x69\x01\x11\x8b\x0f\x27\x47\x49\xe4\xd5\x76\x41\xd9\x62\x46\xc5\x0f\xb5\xe0\x48\x91\xf6\x94\x76\x4a\x39\x44\xf7\x46\x6d\x54\xfd\x50\xd5\x74\x78\x67\xba\x50\x04\xdc\x76\xe5\x84\x66\x59\x9a\x49\x97\x34\xbc\xc7\x39\x49\xd2\x82\x8c\xd3\x2c\xa3\xe3\x62\x74\xae\xd6\x8d\xd9\x6b\x63\x01\xb1\x82\x92\x04\x96\x3c\x13\xfe\x7b\x06\xff\xf5\x8b\xf4\x75\x7a\x4e\xb3\xfd\x20\xa7\x1d\x60\x2e\x5c\xdf\xab\xf9\x18\x83\xfa\x87\xb8\x65\x16\x57\x37\xc7\xec\xff\x13\x7d\x14\x47\x20\xd8\xef\x37\x26\x3c\xee\x89\x2c\xa1\xe7\xe4\x25\x1b\x55\xa7\x0d\x57\xbd\xd0\x11\xb0\x55\xfd\x77\xbb\x20\xf4\x22\xca\x8b\xbc\x47\x16\x31\x0d\x72\x10\x8b\x61\xe4\x69\xa2\x50\x35\x49\xe3\x38\x3d\x8f\x92\x29\x94\xcc\x19\x17\xb4\x96\x91\xe8\x61\x0f\xfc\x2b\xf4\xf4\xb3\x8f\x8a\x28\xb1\xaa\xf7\xe0\xfd\xca\xf4\x2a\x1c\x7c\xa6\xb0\x08\x39\xc3\x87\xcb\xe8\x08\xec\x69\x15\x93\xe5\x24\xc0\x58\x2d\xf8\xaa\xe0\x13\xcf\x51\x2b\x28\xeb\x5d\x9a\xe7\xd1\x69\xcc\xa7\x10\x5c\x68\x08\xa3\xbe\x0f\x07\x4c\xbe\xcc\x0a\xfe\x93\x89\xd4\x12\x5b\x2f\x27\x93\x68\x7a\x29\x3e\x0e\x25\x29\x3d\x22\x9f\x59\xf3\xfc\x4f\x5f\x57\xc1\xa7\xb8\xd9\xe2\x60\x73\x0d\xa6\x2e\x97\xf8\xa7\xbc\x8a\xe2\x70\x53\x0d\xa7\xee\x7f\xf8\xa7\xb8\x30\xd2\x79\xbc\xc0\xa3\x47\x6a\x61\xea\x7b\x1c\x5e\xe0\xd7\xe0\x34\x35\xf2\x3c\x25\xe4\x3d\x0c\x1f\x00\x5c\xdf\xe0\x3c\x5e\x02\xf5\x02\x15\xe6\x9f\x02\x0b\x08\x84\x58\x10\xe8\x03\x2e\x53\x04\x42\xa8\xc6\xe1\x14\xfd\x2e\xe4\x6f\x5b\xa4\xe0\x7c\xc1\x3a\xf9\x5e\x29\x39\x9d\x93\xc3\x38\x48\xd8\xc9\x20\x50\xac\x59\xa4\x0b\x5d\x59\x9a\x91\x80\xbc\x7a\xf9\x4f\x38\x84\x4b\x69\xed\xce\x18\x8a\xda\x67\xe5\xd1\xee\xe7\x19\x95\x7e\xf6\x02\x74\x95\x2b\xa2\xa0\xa0\x60\x01\x6c\x3d\x05\x39\x39\xa7\x6c\x81\x68\x07\x2b\x72\x18\x6b\x48\x1a\xfa\x99\x1a\x47\x72\x39\x4e\xcc\x52\xb8\xa8\xc3\x6a\x96\x4c\x02\x0b\x45\xbc\x04\x8e\x1a\x6b\x72\x2a\xce\x9d\x2c\x79\x08\x6f\xc3\xa2\x02\xf2\xcc\x68\x64\x84\xbf\x90\x64\x55\xbb\x7c\x03\x8e\x63\xcf\x0a\x3e\xa7\xd1\xfd\x82\xfd\x6f\x59\xe2\x45\x5a\xb5\xc0\xd1\x79\xe1\x37\x5b\xea\x6c\xb5\xfd\x8e\x8b\x1d\x10\x72\x37\x4b\xbd\x88\xe6\x34\xff\x3d\x96\x79\x22\x94\x8b\x6c\x71\x2b\x55\x55\xce\x8f\xf9\xb0\x45\x13\x65\xcb\xe2\x90\x83\xea\x49\x23\xa2\xd0\x64\x20\xef\x0e\xd9\xdc\x6b\x5a\x30\x6b\x53\x5e\xae\x74\x05\x1a\x40\xe1\x1f\x1b\xdf\x58\xb3\x50\x73\xfe\xf9\x86\x09\x81\xb0\xec\x65\x79\xf1\xe3\xea\x8a\x0c\x77\xbc\x87\x1b\x51\xaf\x73\x38\xe1\xe9\xc6\x89\x48\xe0\x5c\xf6\xe4\xc1\x03\x22\x7e\xfb\x84\x7e\xd6\xa4\x9d\x8b\x4f\x18\x3e\x1f\x68\x86\x2c\x26\x0a\x2b\x9d\xc8\xf0\xa2\xdd\x6b\xb7\xf1\x85\x8b\xe5\x29\xcd\x57\x1a\x13\x4a\xa9\x4c\x97\xc8\xd8\xb1\x1e\x52\x51\x74\xc2\xc1\x64\x14\x0f\x75\x14\x13\x66\x93\x00\x5b\x9c\xa7\xed\x9c\x8c\x55\x4c\x17\x87\xb4\xcc\x90\x2f\x4d\xe8\xab\x84\x6a\xd0\x21\xd9\xac\xd3\x54\x78\x19\x24\xc3\xc0\x4f\x11\x65\xf9\x16\x2c\x3c\xf9\xee\x20\xaf\x75\xaa\x00\x56\x49\xd4\x4e\x5d\x6b\x72\xcb\xbf\x16\xcc\x72\x7f\x11\x2f\x73\xdd\x05\xf1\xed\x75\x6f\xa8\x80\x4c\x4d\xd2\x8c\x8e\x3f\xe7\xf2\xd8\xc4\x79\xa4\xbc\xe6\xcc\xc5\x63\xb9\xf8\x12\xfc\xf8\x7a\xa3\x11\x73\x92\x1f\x7b\x23\x11\x9b\x31\x85\x51\x03\x6c\xfd\x07\x1a\x1e\x3b\xb6\x83\xe0\x4a\x62\xe6\xac\xba\x8d\x89\x13\x95\x5a\x1a\xb4\xc1\x7f\x86\x17\xc7\xc3\x47\xdf\x05\x8f\x26\x27\x5f\x1e\x0f\xaf\xff\x67\x10\xf5\x0b\x9a\x17\x0a\x7c\x85\xb1\x57\x0c\xf9\xeb\x0c\xb6\xc1\x30\xe1\xfc\x3f\xf8\x4f\x67\x78\xd1\x7d\x56\x39\x4e\x4c\x7f\x83\x81\x8e\x95\xc5\xa3\x61\x41\xef\xb8\x07\x61\x61\x74\x38\x87\x77\xbc\x6c\x3f\x46\xa3\x36\xe9\x57\x38\x02\x24\xa6\xab\x0a\x6f\x67\xcc\xbe\x30\x36\x87\xc0\xf6\x1e\xfd\xe8\x05\xb3\xba\x0c\xa1\xbb\xda\x39\x38\x3b\xce\xe7\xec\xdf\x71\xb0\xc8\x41\x76\x88\x63\x22\xbf\x7b\xd8\x43\xa3\xdd\x63\xee\x78\x1e\x75\xd8\x68\xe0\x50\x6d\xef\x1c\x3b\x34\x18\xcf\xc8\x38\xc8\x9d\x6a\xa2\x9c\x13\xca\x72\x2e\x66\x08\x51\x13\x5f\x65\xcd\x69\x8a\xb7\x95\x2f\xe7\x73\x1a\x96\x92\x97\xd5\xdc\x1d\x93\x99\x55\x7b\x15\xb9\x0d\x06\x7c\x3c\x16\x6e\x02\x55\x52\xfc\x72\x76\x20\xad\x0f\x11\x10\xaf\x82\x1c\x9c\xd1\xcc\x82\x6d\xd9\x88\xa9\x4b\x91\xd2\x8e\xcf\xe1\xcb\xe3\x21\xdc\x51\x12\x8b\x42\xc0\x79\x77\x31\x23\x31\x85\xe7\xd4\x28\x02\xdf\x62\x41\x33\xd6\x5b\x39\x0d\x09\x44\x2f\x9c\x46\x3c\xc0\x5d\x90\xd3\x79\xb0\x60\xd3\xb1\x69\x68\xfa\x3a\xca\x82\x01\x75\x1a\xdc\xb2\x6d\x3e\xe9\x92\x1f\xc8\xb7\x6c\x3b\x17\x59\xc7\xd1\x49\xbf\x48\x8f\x58\x43\x42\x17\xb4\xbe\xbb\x8b\x32\x81\xe8\xab\x2b\xfc\x7e\xd7\x53\x23\xd6\x2e\x59\x35\x96\xf8\x0a\x47\xcb\x52\xb3\x7c\x83\xf1\xeb\xf8\x0b\x8a\x4a\xdf\x88\xa3\x9e\xa4\xc6\x12\x52\x2c\xd2\xbb\x24\x45\xa9\xbd\x56\xfb\xf2\x0a\x94\x88\x74\xc6\x8a\xfa\xec\x57\xd7\xa2\x9d\x76\x5b\x90\x92\x4b\xa6\x06\x7e\x6f\x44\xb4\x08\x68\xec\xf4\x9e\x55\x54\x41\xc6\xb2\x17\xe8\xda\xdd\x26\x69\x60\x7a\x33\x6d\xfa\xc7\x88\xf4\x3b\x76\xf0\x99\x70\x07\xfa\xf2\x26\x4e\x51\xb8\x41\xc0\x75\xf4\x6b\x52\x90\xdd\xff\x8d\xdd\x52\xe2\x46\xe4\x65\x33\xd2\xda\x9a\x2a\x49\xd3\x2a\x69\x4a\x9e\x5a\xd2\x34\xd8\x68\x91\x32\x89\x32\x0a\xc9\xd6\x90\xfb\x0c\x7a\x24\x2e\x08\x79\x9b\xfc\x7d\xc2\xf0\x82\x70\xe3\x0e\xd7\xb8\xab\x96\x92\xfd\xb7\xfd\xc2\xfb\x00\xe6\xda\xca\x80\xab\x19\xfd\x5a\xe2\x8c\x77\xe3\x93\x4e\x75\x25\x3e\x90\x0c\xcf\x77\xdb\xaa\x8d\xd6\x53\x91\xb8\xfc\xf2\xd5\x67\x42\xc8\xd0\x8b\x70\xa5\xa4\x6a\xd4\xaf\xa9\x7a\xe4\xf1\xd0\x7f\x4b\x20\x1d\x11\xcb\xd3\x74\xae\xa5\xdc\xfa\x20\x9b\xde\x93\xa4\xef\xea\xcb\x08\xbc\xc9\x37\x32\xdf\x19\x90\x74\x78\x37\x2c\xb9\x50\xf6\x2d\xc9\x8b\x20\x19\x33\x2e\xa2\x0b\x5f\x5d\x29\xa4\x89\xc2\xf0\x7a\x0d\x7e\x19\x8e\x33\xbc\xa9\xdc\x36\x02\x78\x91\xaa\xb2\xdd\x14\x51\xf2\x3c\x5c\x87\xa5\x0f\x8e\x71\x51\x43\x14\x79\x42\x24\x79\xf1\x23\x58\xab\xe8\x19\x8c\x86\xf7\xad\x7d\x77\xe8\xe1\x7d\x69\x8c\x1b\xd9\xe3\x7a\xec\xfc\xa8\x8d\x48\x56\xc5\x8f\x2c\x7a\x23\x0c\xc9\x12\xed\x86\x23\x62\x7d\x2a\xea\x87\xc3\xbb\x7e\x83\xc1\x1c\x8a\xbe\x35\x5c\x0c\x4c\xbc\x48\x96\x71\x0c\x51\x12\x3a\xee\x0a\x01\xc3\x6d\x50\x61\x78\xc6\x2e\xee\x6b\x1b\x8e\xfc\x94\x77\xb6\x01\x3b\xe0\x80\x37\x61\x06\x3c\xe9\x46\x13\x29\xba\xd7\x74\x34\xe0\x02\xb0\x7e\x2c\x4e\x44\x8d\x86\x23\x71\xa3\x62\x34\x64\x69\x50\xb0\x72\x0c\xf6\x71\x84\xef\xa3\x60\x23\x97\x4a\xaa\x33\x07\xf1\xf7\xdc\x5c\x57\xda\x02\xa1\x72\x0c\xac\x98\xfd\x6a\x40\xb9\x4e\xca\x2e\xdd\x7d\x6a\x7d\x1d\x6e\x26\xf9\x33\x5c\x6d\xcc\x7a\x4d\xc6\x10\xf6\xa9\x43\x3d\x7b\x1b\x3e\x90\xae\x32\xea\x40\x8c\xfb\x25\x9b\x40\xba\x9c\x93\xd3\x38\x1d\x7f\x26\x33\x1a\x84\x34\x63\x1f\xe9\xdc\xb6\xda\x88\xf2\xe7\x2c\xd9\x27\x34\xcc\xe8\x85\xf2\x8b\x0e\x65\xc9\x24\x8a\x0b\x5b\x99\xe9\x21\x58\x80\x35\xdc\x0f\xb3\x94\xca\x93\xfe\x37\x9b\x5b\xfa\xa8\xcf\xc1\x6b\xf0\x52\x7e\x50\xe7\x75\xe1\xaa\x7c\xe7\x74\x17\xca\x17\x71\x58\x9f\xb3\xd7\xdc\x7e\xdc\x60\x66\xe2\x94\x89\x79\x8b\x68\xec\xce\xc3\x47\x96\x5c\x37\x0f\x85\x02\xaa\x98\x00\xa8\xc9\x98\x00\x28\x56\x39\x01\x4f\x1e\x6b\xfc\x73\xe8\x1b\xe3\x1f\xaa\xc2\x35\xf9\xd0\xef\x00\xdd\x08\xfb\x25\x8e\x47\x84\xc8\x37\x92\x3f\x7a\x32\x15\x1e\xfd\x8c\xd4\x2f\x9e\x0e\x82\xe1\x88\xff\x27\x53\x84\x05\xc9\x48\xff\xe4\x39\xc8\xba\x64\x84\x3f\x64\xb9\xa3\x62\xf2\x74\x24\xfe\x97\x69\x60\xaf\x32\x92\x3f\x74\x3d\x1c\x56\xfe\xd2\xe9\x02\x5e\xfd\x14\xf5\xb8\x46\xb7\x23\x5f\x22\x87\x76\x6d\x39\x47\x9e\x34\x03\x56\x9a\x4d\x8e\xec\x04\x39\x8e\x9f\x29\x8c\xe2\x67\x8a\xc6\x00\x69\xe2\x87\x84\x53\xd2\xe2\x08\x7f\xc8\x5c\x53\x65\x3d\x72\x52\x14\xd6\xb8\xa0\x3e\xd2\x3f\x79\x0e\x92\x8e\x47\xf8\x43\xe6\x1a\x27\x91\x91\x9d\x20\xa1\x50\xbe\x95\x63\x1d\xdd\x47\x6e\x92\xec\xa1\x03\xe9\x24\xc9\x3a\xa5\x30\x36\x42\xbf\x71\x7f\x93\xe9\x48\xfd\x92\xe9\x7c\x4f\x1d\xa9\x5f\x6a\xf4\x7c\xbd\x8f\xf4\x4f\x35\x26\xb6\x4b\x8e\xe4\x0f\x99\xca\x36\xac\x91\xf8\x5f\xd5\xc1\xf8\xdd\x48\xfe\x90\xa9\xc0\x36\x46\xf2\x47\x0f\x16\x18\x77\x50\x27\x5e\x75\xb7\x46\x9b\xdf\xf5\x2a\xfd\xdb\xf4\x5a\xcb\x62\xf2\xb4\x35\x7a\xfa\xcd\xf5\x49\x6f\x6b\xb3\x89\xc7\x07\x73\x09\xef\xf2\x05\xdc\x12\x8e\x0e\x5a\x23\xd2\x1a\xf6\xb7\x86\xfd\xcd\xd6\xda\xb5\x74\x05\xb7\xd5\x28\x52\xf1\xbd\x27\x89\x7b\x4f\x12\x7f\x05\x4f\x12\xa2\x96\x35\xd7\x17\xdc\xdf\xe9\x64\x92\xd1\x4b\xf2\x73\x14\x8f\x3f\x53\xf2\xfd\x2f\x74\x32\xb1\xdd\x49\x34\xf4\x18\x07\x60\x51\x90\x90\x43\x26\x71\x07\x00\x15\x05\x89\x0b\xf6\x63\x70\xca\xc0\xfe\x91\x4e\x69\x9c\x17\x34\x8e\x69\x46\xbe\x9f\x40\xa2\x0b\xfc\x53\x70\x46\x7e\x4e\xd3\x90\x7c\x3f\x2d\x75\x73\xf1\x58\xbb\xf7\x11\xbe\x20\xdf\x04\x49\x30\x35\x7d\x4f\xf4\x07\x0c\x0b\x83\x8c\x03\xcc\x39\x80\xf4\x31\x71\x70\x0a\x87\x23\x1b\x38\x3a\x0d\x12\x09\xf2\x12\xcc\xf8\x6d\x08\x2e\x79\xe5\x03\x5a\xcc\x24\xe0\x8b\xe7\x15\x70\xe1\xa9\xf2\x37\x3b\xab\xaa\x2f\x9f\xa9\xfa\xde\x82\x67\xf2\x32\xc0\x84\x16\x12\xf0\x1d\xcd\x72\x78\x4a\x55\x0e\xbd\x10\x20\xaa\x13\xe7\x41\x36\xaf\xea\x06\xcb\x57\xc0\xb4\x28\x20\x6a\x93\x0b\x9f\x8b\x2c\x09\x2a\xb9\x8a\x01\x29\xd9\x05\x3b\x51\x69\xe7\x1e\x51\x6c\x55\x88\xc2\xca\x97\xfb\x08\xe1\x40\xd2\x1b\x93\x78\xb8\x41\x93\xd0\xd3\x37\x9e\x21\xc1\x9e\xc3\x89\xc9\x85\x3a\x65\xe9\x0a\x93\x59\xba\xa0\x59\x71\xe9\x81\x5b\x88\x2c\x09\xfa\xaa\x28\x16\xef\xb2\xf4\x2c\x0a\xbd\xe4\xc6\x16\xea\x42\x64\x2b\x62\x5b\x8c\x2b\x4a\x44\x8b\xb1\x5d\xa0\x99\x47\xc3\xb5\x35\x25\xab\xff\x4c\x4f\xb7\x49\x47\x56\x63\x7a\xe5\xcd\xec\x15\x92\xd0\x73\x6b\xd9\xe8\x92\xc8\x41\xaf\x08\xb5\x8a\x7a\x2e\xa1\x10\x10\xe5\x6f\x5d\xe8\x39\x5b\x2e\xe0\xa8\x1f\x57\x11\x9e\x8a\xcc\x17\xcf\x9d\xbc\x7c\x26\x4b\x7e\x98\xb9\x25\x13\x58\x03\x2c\xf7\x2d\x2d\x9c\xdc\x85\x26\x7c\x06\x22\xd7\x81\x03\x77\xfa\xeb\xaf\xb2\x0d\x46\xd7\x6e\x1f\x34\x81\x03\x90\xf8\xec\x60\x18\x4d\xd9\xfa\xa8\x11\x2c\xa2\x91\xda\x0c\xc5\xff\xfc\xc8\x81\x3b\x29\xb0\x95\x1b\x45\x31\xf9\x8c\x8c\xaf\x9e\x82\x41\xf4\x32\xc2\x1f\x4e\x13\x9f\xd4\x1a\xe0\x3f\x9c\x01\x0a\x80\x8e\x6e\x5f\x90\x73\x44\xf3\x11\xfa\xdd\xe1\xc6\x3c\xd7\xdd\x1d\x26\x31\x0d\x06\xe0\x82\x37\xa7\x44\x8f\x21\xe5\x3b\x31\xf8\x04\x5a\x63\xe4\xe6\x19\x5f\xdd\xd8\x4a\xc7\xc5\x84\x46\x59\xa7\x8c\xa7\x49\x31\xe5\xe1\x98\xc1\xf5\x34\x8e\x0b\xaf\x4c\xda\x9e\xbe\x64\x94\x07\x8b\xd0\xbd\xf8\x4c\xe9\xe2\x20\xff\x70\x99\x8c\xa3\x64\x5a\xd9\x15\x28\x6b\xc1\x37\xa3\x40\x4f\x47\x30\x5f\x78\xae\xed\x57\x2c\x28\xf9\x0c\x86\xbb\x93\x82\x2f\x0f\x8c\x7c\x32\x2b\xa1\xe0\xdb\x03\x27\xde\x5d\x4b\x30\xf6\xe9\x40\xe1\x27\xb8\x1c\x50\xa5\x78\x61\x8d\x3a\x65\x82\xa7\x6d\xfd\x9e\x4a\x36\x2f\x52\xbc\xb5\xda\xd0\x28\xcd\x53\x37\xc6\xa5\xac\xbd\x0a\xa7\xdc\xc4\x51\x42\xfe\x4c\xfd\x23\xc3\x50\xe2\xdb\x81\xc3\xa6\x2d\x1c\x52\xa5\x78\x60\xdd\x5b\x61\x59\x66\xdf\xbe\x2d\x74\xfa\x5c\x56\xd6\xc9\xf1\xb4\x7b\xf0\x7c\xef\x2d\x6a\x8c\x7d\x3a\x50\xda\x3d\x0d\x07\x13\xdf\x3e\x38\xe9\x39\x45\x01\x42\x02\xdb\xc5\xec\x85\xcf\xb7\x7e\xfc\x92\x9b\x5f\x0a\x99\xde\x15\xcd\xeb\x3a\xb8\x93\xb6\x21\xcb\xae\x4f\xc3\x28\x03\x55\xf1\x38\x58\xc0\xeb\x0b\x74\x81\xe9\x99\xd1\x83\xfd\xbd\x77\xc6\xda\x67\xe5\xb0\x85\x5c\xc4\x45\x49\xb6\x7c\x99\x54\xc9\xf3\x8d\xc7\x9e\x0c\xa2\x2f\x9a\x91\x2b\x1b\x1c\xca\x28\xfe\x5b\x15\x71\xf4\x58\xf1\x6e\xd8\xeb\x84\x38\xd2\x31\xef\x9c\x13\xd0\xc1\xb4\xe5\x9e\x94\xa4\x21\x6d\xf7\x0c\x88\x29\x98\x85\x8c\x48\x9b\x09\x1d\x9f\xc6\x71\x44\x93\xe2\x1f\x1c\xbc\xad\xef\xa4\xbb\xbd\x9b\xb4\x46\x8b\xf3\x34\xfb\x5c\xd6\x60\x42\x8b\x4f\x02\xd4\x02\x31\x03\x06\x8c\xec\x55\x7e\xcb\x6e\x51\xa1\xd0\x2e\xeb\x17\x2d\x66\x9f\x60\xae\xc7\x69\xfc\x8f\xdf\xa1\x7f\xe7\xb3\x28\x5f\x28\xdf\xc8\x4e\xf7\xf2\xd9\xec\xd6\x68\x83\x9f\x27\xde\xbd\x24\xca\xf7\xd3\x24\xe1\x3e\x9b\xd0\x72\xeb\x1a\xb4\xd7\xf1\x6e\x97\x0f\x1e\x78\xb7\x51\x5c\x65\xa7\xeb\xdf\xc1\xb8\x97\x02\x29\x93\x97\xd2\x3c\x18\x87\x42\xe4\x04\x21\xd1\x78\xf5\xb6\xac\x6e\xe9\x4d\x14\x9f\x10\xb8\xca\xc9\x38\x58\xb4\x46\x5b\x43\x96\x84\x8f\x24\xad\xd1\xd6\x26\x4b\xd3\xc7\x81\xd6\x68\xeb\xb1\x4a\xe1\xa2\x53\x6b\xb4\xf5\x54\x25\x61\xe1\xbe\x35\xda\xde\x52\x19\x6c\x85\xb7\x46\xdb\xdb\x3a\x41\x0b\xf5\xad\xd1\xb6\xae\x54\x1f\x0b\x5b\xa3\xed\x6f\x9d\x64\x5a\xcc\x5a\xa3\xed\xa7\x4e\x7a\x42\x8b\xd6\x68\xfb\x3b\x27\x5d\x0a\xc2\xad\xd1\xe3\xa1\x93\x99\xcf\x66\xad\xd1\xe3\x4d\x37\x9d\xc9\xc2\xad\xd1\x63\xdd\x7d\x79\xc6\x69\x8d\x1e\x7f\xa3\x12\xcd\x83\x73\x6b\xf4\xf8\x89\xca\x92\x52\x4b\x6b\xf4\xf8\xdb\x6a\xdd\xde\xf5\x49\x6f\x6b\xfb\x5e\xf3\x76\xaf\x79\xfb\x6f\xd1\xbc\x05\x71\x0c\x0e\x26\x6e\xe7\xc7\x15\x29\xb8\x1c\x55\x88\x4f\x17\x22\xc3\xc4\xbc\x3c\xe3\x16\xfd\x48\xc7\x00\xbd\x91\x70\x3a\x68\x4c\x5d\x74\x24\x57\x4f\xe3\x55\xd4\xfc\x08\x97\xbb\x56\x65\x90\x26\x21\xce\x79\xec\x23\x13\x44\xb2\x22\x91\xa9\xbc\xbb\xee\xc5\xb1\x31\x14\x53\x30\x32\x8f\x56\x3d\xb8\xa9\xef\x11\xcb\xb4\xac\x44\xe9\x61\x26\xe0\x23\xf2\x2f\xfc\x72\x9e\xfd\x87\x93\x1d\x73\x49\xbe\x09\x39\x3d\xac\x0e\xf3\x6d\x49\xad\xd2\x1f\xf8\xae\xfa\x75\x75\x05\xf1\x6f\x88\xed\xf7\x81\x25\x42\xea\x71\x9b\x49\xa1\x10\x57\xa0\xdd\x23\xed\x22\xe5\x3f\x4f\xfa\x1c\xcd\x28\xde\xe1\xc4\x73\x1b\x2a\x9a\x39\x9e\x9c\x80\x81\x8b\xb2\x0f\x15\x37\xa4\x5d\x4f\xd0\x6c\xab\x1a\xd6\x1f\x56\x7c\x17\x11\x0f\x77\xa1\x03\x1d\xe1\xe7\x25\x1d\x04\x4f\x37\x28\x6d\x16\xf4\xc3\x2d\xf0\x45\xa1\xf1\x6a\xe0\xd9\x7c\xdd\x85\xbd\x53\x54\x61\xdc\x13\xb5\x38\x0c\x8a\x40\x8e\x80\xfd\xee\xb3\x7f\xc8\x2e\xfa\x7d\x75\x05\x46\xb1\x0a\x00\xae\x92\x73\x09\x22\xbe\xae\xae\x74\xf4\x4d\xd0\x36\xb2\xa6\xe5\x1d\x39\x02\x3c\x1e\x9e\xf4\x73\xc6\x10\x94\x8b\x75\x06\x3d\x17\x02\x8e\xa6\x30\x77\xba\x7e\xf1\x4c\x17\x6e\x65\x57\x98\xda\x0a\xe9\xce\xbd\xb4\xed\xfc\xa2\xde\xa7\x77\x8f\x87\x27\xe8\xe1\xd5\x3a\xb4\xdf\x25\x5f\xe0\xb1\x43\x90\x24\x69\x41\x26\x51\x12\xf2\x7e\x45\xc9\x94\x37\xf4\x4c\x35\x3f\x4e\x93\x3c\x8d\x69\xff\x3c\xc8\x92\x4e\x1b\x97\xe0\xde\x72\x18\x2b\x8e\xd3\x69\x1b\x99\xbe\x8a\x1e\x33\x54\x38\x1e\x97\xa8\x60\x43\x38\x32\x17\xcc\x5d\xc7\xb7\x3a\x7b\xbc\x5b\x3d\x93\x20\xcc\x23\x14\xd4\x28\x9d\x1d\xc2\x14\x37\x58\x8e\x17\x74\xcc\x24\x00\xcf\x7a\xec\x81\x47\xa6\xd3\x60\xfc\x59\xc5\x10\x05\x57\x04\xe2\xb0\x2b\xaf\x5b\x3b\x41\x36\x5d\xc2\x5b\x90\x63\xf5\x0b\x79\xe3\x31\x8d\xd0\x65\x8d\x10\xfb\xb9\xb2\x18\xf6\x1b\xd7\x71\x20\xd8\xc4\x6f\x9a\x7e\x2c\x34\xdb\x48\x96\x71\xec\xa0\x3b\x95\x94\x26\xbc\xdf\xe9\x03\xb0\x84\x98\xa0\x28\x6b\x5c\x33\x0b\x98\xec\x9f\x46\xa6\xd2\x10\x89\xdf\x9c\xb3\x77\xd2\x1e\x1c\x94\xda\x3d\x2f\x63\xed\x49\xf6\xce\x0e\x5b\x9d\x6e\x4f\x37\x84\x30\x5c\x3f\x53\x41\x51\x04\xe3\xd9\xc7\x74\x5f\x3a\xc2\xc2\x53\x26\xbd\x63\xe1\x33\xb7\x9e\x5a\x3e\x6e\xfe\xe9\x0c\x47\x16\xed\x07\x71\xac\xf6\x13\x01\x5c\x72\xa6\x70\xba\xa9\x0e\x18\x9e\x13\x86\xf7\x88\x01\xa4\xda\x1a\x6d\x81\x74\xcf\x57\x7d\x6b\xb4\x05\xb2\x3b\x8e\xd9\xb6\x0d\xc0\xd6\x46\xd8\x1a\x3d\xde\x66\x22\xf3\xe3\x7b\x91\xf9\x5e\x64\xfe\x6b\x8b\xcc\x28\xdc\x0b\x9c\xbd\xef\x2a\xde\xcb\xdf\xf3\x34\xc9\x16\x63\x53\xde\xfc\x85\x27\xaa\xab\xc3\x2c\x4b\x6d\x11\x98\xa7\x29\x49\xd4\x55\x51\xb0\xc1\x1a\x42\xa6\x23\x63\x02\x3a\x3e\x95\x4a\x9a\x22\x23\x17\x81\xbd\x6b\x1c\x05\x06\x61\x28\x7d\x3a\x32\x76\x2c\x0a\x83\x9b\x6c\xe8\x9a\x48\xb0\x2c\x02\x83\x30\xf4\xd8\xd8\x12\x31\x7e\x5e\xa8\xd0\xd6\xad\x83\x35\x18\x27\x66\xc5\x61\xe8\x93\xb9\x7d\x03\xcf\x79\x54\x70\x09\x51\x3b\x22\xc9\xb4\xab\xfa\x2f\x60\xbc\x5d\xf3\xed\xe7\xa6\x77\x01\x85\x5f\xa3\x9b\xee\x14\xe8\x7b\xa2\x24\xe4\x6a\x26\x09\xdb\x43\x75\xd3\x2c\xeb\x09\x49\x34\x77\x65\x62\x4e\x3e\xfc\x97\x10\x16\x35\x80\xc0\x0f\x76\x31\xa9\x50\xd9\x23\xf0\xba\xbd\xe4\xfd\x9a\xa8\xf2\x18\x60\x4e\xf0\xf1\xa0\x54\x60\xe7\x45\x4a\xaa\x65\x62\x8d\xec\x8f\xa8\xb4\xef\xc8\x3e\x76\x81\x75\xb1\x88\xfa\x51\xfe\x8f\x20\x8e\xc2\xf7\x34\x5f\xa4\x49\x4e\x45\x53\xce\xdb\x3b\x67\x0c\xfe\xf6\x3a\x7c\x8d\xf5\x0f\x92\x33\x6f\xad\x3b\x4e\xa5\xd7\x6e\xff\x4a\x2b\xe7\x3e\x9b\x9c\xc1\xf2\x3d\x17\x7c\x43\xf8\x32\x44\xe3\x7d\xd1\x07\xf0\x1a\x81\x13\x9c\x28\xf6\x7a\x2a\xd4\xf9\x86\xf8\x45\x09\xa0\x2c\xad\x9f\xe4\x83\x6f\x8d\xb6\x40\x8f\x26\x56\x64\x6b\xb4\x0d\x56\x6f\x8d\xa2\x7c\xdf\x6f\xf8\xf7\x1b\xfe\x9f\x77\xc3\xd7\xfb\xbd\x12\xcb\xef\x48\x45\xd6\x50\x57\xc5\x4e\x3c\x99\x05\x96\x0b\x59\x7f\x00\x99\xab\xaa\xd3\x24\x1c\x7a\x37\x85\xf5\x60\xf2\x41\x94\x80\xde\x43\x87\x10\x04\xa6\x34\x86\x46\xc8\x71\xdf\xfe\xc9\xd5\x4b\xf8\x91\x19\x6c\xf3\xf6\x33\x65\x0e\xb7\xaf\xc1\xde\x49\x28\x25\x17\x80\xb1\xef\x35\x91\xbe\x9c\xcd\x54\x6f\x03\xc2\xdb\xaf\xbf\x6a\xf3\xa9\xe7\x69\xd4\x13\xe5\xac\x5b\x9d\xe0\x34\xf2\xa8\x41\x90\xdf\x67\x62\x39\x5a\xe6\x01\xbe\x77\x77\x49\x1b\xf5\xa9\x4d\x1e\x3c\x30\x1c\x39\xa3\x73\x33\x6f\xd6\xf0\xf6\x7f\xdd\xb5\xb6\xe1\xaa\x06\x3d\xae\xa1\x49\x07\x12\x4b\xb6\x6b\xc8\xe3\x1e\xa3\x3d\x3b\x83\x55\x11\x03\xcb\x3d\x4d\x03\xed\x89\xc3\x3b\x47\x28\x07\x55\x68\x44\x5a\x1e\xa9\xbd\x6a\x20\x3d\xaa\x80\x5e\xc2\x55\x14\x3f\x5a\x7b\x5f\x36\x05\x61\x28\x69\x38\xd7\xc7\x70\x4c\x1b\x32\xed\x5a\xd5\x54\x4a\x4f\x9c\x54\xfc\x55\x56\x9e\xec\xf5\x71\xfd\xe6\x84\x82\x5e\x21\xae\x32\xfb\x58\x53\xa5\xb4\x3f\xaa\x3f\x9f\x68\x31\x93\xea\x66\xdd\x49\xd3\xeb\x45\xad\x2a\x75\xe2\xa8\x39\x34\x02\xb4\xaa\xb4\xc1\xbc\x72\x6e\xd1\x68\x52\x39\xbf\xb9\xbb\x19\xb5\xeb\xab\x57\xd4\x48\x86\x77\x17\x73\xcb\x79\xaf\xa5\x56\x16\x9c\x55\x68\x1b\x15\x8f\x35\x27\xcf\xd5\x5b\xf1\x8e\x95\x4e\xe7\x5e\x1c\x57\x4e\x17\x00\x89\x8b\x9e\x95\x09\x8c\xab\x42\x6b\x3a\xb8\x3a\xb5\x19\x8f\x02\x5d\xa5\x5a\x19\xb5\x55\x91\x9b\x72\x94\x03\xb6\x7f\x72\xd2\xa7\xb4\xc8\x85\xf1\x4a\x7c\x49\x42\xba\x88\xd3\x4b\x1a\x4a\x13\x41\x78\x3e\x38\x9e\x05\x51\x62\x3f\x57\x83\xda\x7e\x4c\x33\xd9\x23\x8f\xef\x01\x79\x60\xf5\x91\xa4\x5c\x97\xd7\x4a\xb5\xb8\x66\xb8\xc8\x3d\x92\x97\x1b\xfa\x59\x5b\x49\x8b\xd8\xe0\x41\xb6\x84\x14\x96\x9a\x7c\x21\x60\x33\x44\x92\x71\xd4\xbc\x3f\x42\x94\xf2\x5d\xf9\xb0\x0c\xf2\x07\x03\x72\x1e\x44\x5c\x5d\x0e\x22\xd7\xa2\xd0\x2a\x58\x79\x53\x66\xce\xbb\x58\x0a\x2a\x60\xb4\xee\x18\xed\x9a\x9e\x97\xd7\x29\x3c\x4d\x36\xda\xb7\x77\x25\xe8\xef\xc6\xc6\x8e\x79\x6c\x1a\x0c\x48\x5e\xa4\x0b\xae\xab\x8d\x92\x29\x09\x26\xac\x2b\xdf\x0c\xf9\x5c\xe5\xa4\x53\x44\x73\x9a\x2e\x8b\xae\x73\x74\xe4\x08\xf8\x81\x7c\x33\xf4\x1e\x16\x79\xef\xfb\xac\xf6\x9f\x45\xe5\x3a\xa6\x42\x97\x7c\xb9\xf6\x9c\xe9\x6c\x04\xf2\x07\x7b\xde\x73\xa8\x9a\x11\xef\x69\x53\x9f\xfc\xb4\x63\x60\xc5\x98\xe0\xbe\x24\xe0\x2b\x63\xcc\x08\x1b\x9c\x04\x9f\x32\x89\x79\x99\x84\x36\x06\xda\xbe\xc3\x27\x8d\x91\x43\x11\xfc\xe7\xb8\x23\xbe\x71\xab\x6c\xf9\xe1\x9a\x95\x3f\x11\x17\x6b\x06\xd5\x4c\x69\xf1\x51\x37\xf5\x9e\x93\x9a\xe6\x28\xa8\x1b\xaf\x82\x7c\x86\x89\xaa\x27\x09\xb3\xeb\x3f\xc2\x47\x93\x8e\x00\xf0\x53\x9b\xb7\x90\xb7\x83\x10\xc2\x48\xd4\xd5\x1f\x9b\x0b\xd0\xec\x11\xc4\x39\xf2\x77\x47\xfe\x95\x79\x6f\x7f\xa2\xbc\xb7\x97\xfd\x45\x93\x8e\x49\x71\x57\x57\x64\x1d\x5a\xac\x2c\x46\x14\xeb\xf6\xd0\x26\xfe\xbb\xc9\x12\xc0\x7f\x0d\x97\x83\x3d\xa4\x34\x44\x21\xa2\xb7\x2b\x67\x46\xfe\x0d\x06\xea\x9e\x2f\x4e\xa7\x88\x6a\xe1\x58\x21\xd9\xf8\x7a\xbb\x5b\xd3\x3c\x31\x44\x35\xc5\x51\x4b\xa6\xba\x41\x65\x83\x01\xe1\x9b\x95\x14\x17\x82\x24\x24\xe2\x66\x84\x04\xd3\x20\x4a\xc4\xca\x39\xa7\x22\xc2\x5f\xcd\x9f\x5f\xf6\xb4\x37\xc0\x9a\x1a\x6c\x59\xc7\xd9\xfe\x6b\x86\x34\xe6\x4e\xd9\xc4\xa5\x20\xdb\x12\xd8\xee\x98\xd3\x71\x9a\x84\x84\x31\xdc\xda\x4a\x10\xe9\xd6\x13\x2b\x31\x38\x22\xe8\xc2\x9a\x76\xd8\xeb\xc5\xe8\x8e\x3b\x84\x7d\xb7\x23\x51\x42\x9c\x68\x11\xa7\xcc\x8b\x34\xa3\xa1\xf2\xe3\xce\x25\x10\xd0\xf8\x4c\x83\x9c\x04\x73\xb6\x21\xf5\xbd\xfc\xda\xfe\x2b\xe5\xdf\xf6\x9f\xc7\xbd\xfc\x5d\x74\xb1\xba\x87\xd7\xa5\xb9\x65\x1c\xc3\x2d\x61\x43\x22\xed\x64\xd3\x03\x05\xba\x62\x90\x84\xfe\x63\xc0\x8e\xd9\x97\xca\x97\x86\x25\xc5\x59\x60\x35\x87\x06\xbb\x52\x7c\x60\x80\x53\x55\x70\x1a\x19\x97\x0b\xfc\x45\x11\x95\xc7\x77\x48\x0b\x4e\x23\xb2\xcb\x20\xa5\x9c\xf5\x90\x6b\x42\xeb\xc7\xa4\x4f\x48\x09\x09\x90\x68\x2a\x8a\xcb\x5a\xe4\xd8\x12\x7a\xae\x92\xe4\x98\x92\xcb\x6b\x4c\x0c\x96\x6e\x64\x53\xda\x14\x04\x71\x77\xc5\xa2\x5b\x15\x45\x6d\x39\xd8\x90\x2c\x84\xaf\x13\xa9\x28\x0e\x9d\xd2\x3e\x49\x59\x40\x28\x69\x59\x1f\xff\x64\x92\x6a\x4b\x4f\x3c\x14\x1a\xe8\x89\x60\x28\xf5\x5d\xbf\x90\x8a\x2d\xfa\x5b\x59\x03\xfb\x53\x3f\xb8\x74\xad\x4e\x91\x98\xfe\x3a\x92\x0e\x7a\x6a\xf6\x31\x07\x1b\x0c\x78\x6c\x45\x6d\x65\x61\x54\xaa\x6d\x25\xbe\x5c\xef\x30\x60\x89\xa5\x75\xb3\x6d\x81\x18\x54\x31\x9c\x71\x33\x78\x8b\x03\x84\x8c\x1f\x25\xc4\xd1\x98\xc2\x55\x83\xb6\xd7\xb0\xc2\xff\xf9\x6c\x47\xc0\xfe\xa3\xdc\x62\x84\x38\x56\x23\x79\x7f\x91\x2e\x0c\x07\x73\x66\xf7\xe2\x20\x2f\x04\xa4\x53\xb5\xbf\x3b\x9c\x90\x3a\xac\x20\x38\x2f\x5a\x57\x2f\x4e\x20\x10\x2d\xa4\xdb\x7d\xd2\x28\xac\xe9\x12\x6b\x48\x00\xf7\x79\x54\x92\x1f\xc8\xd0\xae\x4d\xcc\xb4\xa4\xfd\x3d\xb9\x96\xeb\xb5\x00\xf2\xef\x56\x2a\x41\x84\x26\x8b\x59\x4a\x75\x9a\x32\xb5\xc3\xc3\x5a\x37\xbb\xdc\x5f\x04\x97\xc1\x69\x4c\x7d\xdd\x73\x8f\x03\xdc\x7e\x2a\xa7\x49\xa8\x23\x52\x25\x69\xf2\x48\x54\x82\xd1\x61\x6f\x13\xd7\x65\x53\x0f\xbe\xfd\x18\x67\xf4\xab\x60\x3b\x72\xa9\xf4\x60\xc4\xa8\x56\x39\x41\x60\xfb\xb6\xb1\xcb\x2b\xda\x31\x27\xb1\xf4\x46\x10\x9f\x68\x0d\x1d\x80\x94\xfb\x82\x30\xb1\xb5\x04\x21\x25\xe7\x41\xae\x04\xca\x35\x13\x57\x7c\x69\xc3\xd5\x2b\x3a\xc2\x68\xc3\x2c\xeb\xfe\x75\x16\xe4\x33\x1f\xd2\x59\xaf\x69\x96\x95\xdd\x44\xe2\x2b\x47\xdf\xbd\x62\x95\xc4\xc3\xc4\xd1\x30\xe4\xd7\x5e\x88\xeb\xb2\x9e\xf8\xdb\x2a\x39\x76\x91\x5d\x28\x53\x22\x7c\x95\x4a\x88\x93\x28\xcb\x8b\x72\x01\x71\x45\x19\xaf\x44\x03\xe2\x53\x7b\xf8\xae\x5f\x8d\xaf\x3a\xc7\x97\x10\x69\x93\x0f\xbc\x6e\x9e\xad\xc6\x9a\xa2\xbc\x16\xd5\xab\x0c\xdd\xcf\xd3\x94\x4e\x9e\x03\x09\x5d\x99\xc0\xae\xdc\x04\xd9\xf9\xf6\x05\xb7\x2b\x85\x24\xf1\x69\x18\xa0\xdd\x58\xf0\xb2\xb5\x66\x75\xda\x59\xcf\xa6\x2e\x6a\xba\x36\x65\xa0\x89\xaa\x7f\xb0\x36\x18\x58\x3b\xb0\x71\x81\xa3\x3d\x1e\x23\xf5\xa5\x55\x79\x87\xef\xcb\x83\x81\xe1\x4a\xb7\x34\xee\xf4\x78\x0c\x5e\x71\x53\x1e\xa8\x29\x4a\xa6\x15\xb2\x99\xa9\xc6\x36\x47\xce\x27\xf1\xda\xe5\x44\x58\x1c\xaa\x12\x85\xc8\x17\x24\x75\x35\x95\x88\x26\x24\x49\x75\x0d\x8c\xbd\x2d\x82\x3c\xa7\x61\x8f\x55\xa1\x5d\xdf\x31\x88\x1c\x2d\x69\x93\x97\x29\xc2\x83\x19\xb0\xd0\x69\x98\x43\xfa\x7c\xa7\x9a\x36\xab\x64\x65\x19\x4a\x5b\xca\x6b\x6d\x65\x31\x43\xae\x25\x21\x58\x0d\x84\x08\x93\x46\x05\xaa\x4b\x3d\x59\xe0\x94\x8e\x83\x65\x4e\xd9\x49\x3c\x4c\x93\x82\x9c\x07\x09\xd8\x24\xe5\x8b\x34\x8a\xf9\x6d\x78\x52\xd0\x6c\x12\x8c\x95\x6f\xec\x06\x27\xf1\x26\xa7\x6d\x7b\x9b\xaa\xe7\x87\xc4\x71\xaf\xab\xd6\x34\x5a\x9b\x3f\xd1\x82\x3b\x6b\x66\xfb\x63\x8f\x9c\xcf\xa2\xf1\x0c\x8c\x06\xd8\xf2\x2e\x52\xb1\x8d\x91\x45\xbc\xcc\xeb\xaf\x5e\x05\x1f\xa8\x99\x5f\xcd\x3c\xfc\x86\x4c\x35\x22\xec\xea\x72\xaa\x2a\x56\x2f\x3f\xde\x46\x76\x2c\x97\x1b\x91\xb1\xf2\x8d\xe4\x98\x2a\x19\xc6\x7c\xe9\xd0\xe7\x06\xe9\xcd\x99\xaf\xe7\xd4\xe3\x3d\xee\x36\xb8\x3e\x2f\x63\x4d\xce\x61\xd8\x7b\x0a\x2e\x79\xc9\xe2\x3b\x0f\xbb\xbb\x9f\xb6\x0b\xe7\xf8\x73\x1f\xaf\x10\xcf\x61\xda\x6b\xb6\x64\xd1\xed\x8e\x32\x7f\x36\x6d\x25\x5a\xa3\x6f\xcb\x2c\xa0\x95\x45\x43\x6b\xb4\xb5\xed\x9a\x44\x8b\x91\xb7\x46\xdb\x9b\xd7\x27\xbd\xad\x27\xf7\xa6\x4f\xf7\xa6\x4f\x7f\x6d\xd3\x27\x64\xeb\x2c\x4c\x20\xef\xc0\xd8\xb9\xc4\x8d\xa5\x30\xae\xe4\xef\xb2\x0e\x27\xf2\xce\x79\x2f\x9b\xe6\xa3\x12\xcd\x0d\x92\xf1\xc4\x09\x56\x54\x82\x63\xdf\xc9\xed\x84\xb1\x4f\x59\x29\xc1\x26\x4e\xc0\xe7\x7b\xbe\x3e\xbc\x7f\xb7\xcf\x99\xfb\x6d\x3a\xc0\xe3\x2d\x01\xab\xa5\xf0\x80\xb1\x48\xc9\xfb\x77\xfb\xe2\x9e\xc0\xdf\x01\xf1\x1c\x1d\x9c\x28\xea\x96\x67\x69\x8e\x6f\xbf\xdc\xc6\xf7\x0f\xdf\xbe\x7d\xb9\xff\xf1\xe0\xf0\x2d\x79\xf9\xfe\xfd\xe1\xfb\x11\xd9\x57\xea\xdf\x31\xaf\x92\x9f\xe8\x43\x4a\xda\x1b\x84\xd5\x47\x36\xda\x7d\x7f\x1f\xb4\xc7\x9b\xa6\x63\x57\xef\xec\xb9\x12\xa1\x60\xab\x27\xe2\x95\xf9\x9b\x90\x86\xb4\x23\x62\x1b\x05\xa3\x61\xc2\xb3\x34\x9a\xe7\xc1\x94\x92\x5d\xb2\xbe\x2e\x5e\x1a\xb2\x6d\x5d\xfc\xee\xf3\x90\xb1\x4e\x4a\x5f\x16\x7b\x46\xbc\xc9\x23\xa2\xa6\xeb\xef\x1f\x0e\xdf\xc2\xac\x64\xaa\x4b\x9e\x30\xab\xa2\x6f\xce\x5b\x32\x8d\x03\x51\xb5\x39\x5a\x3d\x9b\x1f\xf9\x75\x35\x1e\xef\x3c\x6f\x3a\xa5\x1f\x0f\xde\xbc\x3c\x3c\xfa\x38\x22\xe2\xd2\x9b\x11\x17\xeb\xe4\x3c\x27\x1b\xa4\xcd\xfe\x0b\xc6\x33\xc6\x31\xda\x46\x40\x1b\xe1\x46\xf2\xdb\xfb\xdd\xea\x7e\xb7\xfa\x6b\xef\x56\x68\xb3\x82\x57\x97\x7f\x54\x2b\xdd\xe6\x8f\xd9\x1b\xbd\xa1\xbf\xc3\xa7\xec\xd2\xe7\x10\x5b\xff\xea\x70\x86\x23\x32\xe5\xc6\x31\x44\xbc\xb1\x85\xb6\xf4\x61\xc1\x36\x42\xfe\xda\xef\xe0\x17\xd2\x94\x17\x29\xd2\x71\x3e\x0f\x5d\x41\x2a\x9e\x23\xe7\x69\xd2\xad\x79\x42\x8f\x32\x93\x34\xb9\x9c\xa7\x4b\xd5\xa2\x4a\x28\x39\xbd\x49\xa4\x4d\xa9\xc4\x15\x0d\xb9\x3c\x00\x41\x0c\x9c\x60\x4d\x22\x4d\x1d\xcf\x9e\xa7\x69\x7c\x0d\xe1\x55\x43\x70\x41\xce\x37\x09\xca\x21\x43\x34\x3b\xf0\x3e\x84\x86\x86\xc3\x74\x79\xe2\x83\x60\x04\x6c\x51\x8a\xda\x07\x6b\xc6\x34\x61\xef\x5b\x0c\xc2\x74\x1c\xc5\xeb\xb5\x03\x30\x20\xe4\xbb\x57\x22\x91\x47\x54\x88\xfa\xa2\x26\xb8\xdf\x10\xbf\x4b\xcc\x5d\xfd\xe5\xb5\xbd\x72\xe9\x0d\x31\xc6\x36\xa7\xcf\x90\xbb\x00\x07\x2f\x46\x16\xae\x43\xed\x1d\xdc\x1b\x2d\xc8\x5b\x41\x39\xea\x50\x75\x55\x4e\x82\x38\x25\xba\x0e\xca\x3b\x9a\x5e\x9b\x8f\x0e\x56\xa8\x67\x68\x85\xf0\x67\x5e\x31\x2e\x5c\xb4\x9a\x1e\x56\x1a\x91\xf4\xa4\x7e\xa3\xe1\xe4\xd1\x34\x09\x8a\x65\x66\x0f\x07\xa7\x97\x8d\x07\xc3\x94\x8f\x47\x41\x55\x0d\x08\x1c\x18\x34\xef\xbf\x78\xe1\x20\xc9\x5b\x70\xa4\x20\x09\x95\x6a\xa9\x48\x21\x28\xf1\x24\x4a\x82\xd8\x6f\xf5\xcc\xeb\xf0\xd9\x94\xe2\x75\x6d\x65\x89\xea\x0d\xa4\xc8\x3c\x7a\x46\xb3\xcb\x62\xc6\x35\xd6\xf3\xd3\x08\x58\x46\xca\xa3\x44\x43\xdf\x44\x98\x85\x4a\x6c\x79\x5c\x83\x88\xee\x38\x9e\xed\xd4\xe2\x56\xbf\xd0\x23\xc0\x7b\x07\x22\xda\x5d\x87\xf2\xcf\x51\xe7\x59\x44\xea\x35\xd7\xad\x9d\xc7\xed\xa7\xa8\x9c\x3f\x6c\x15\xbe\x05\xb9\x9f\x4e\x49\xed\x9d\xae\xab\xd2\x14\xf3\xf4\x51\x76\xec\xb6\x2c\x1d\x85\xb0\xa8\xe4\xe7\xe0\x78\x59\x04\xd3\x16\xe5\x8f\x23\x08\x31\x65\x19\x03\x08\x20\x3c\x7f\x8c\x6e\x74\x72\xb2\x8c\xe3\x92\x17\x2e\x5a\xb3\x48\xdc\xcb\x7f\x53\x21\x0c\xf5\x95\x05\x66\x84\x4c\x6b\x34\x67\x15\xb7\xfd\x02\xfb\xce\xdb\x98\x0e\xdf\xbe\x7a\xe4\xcc\xbe\x39\xef\xda\xb1\xf5\x56\xaa\x0d\xfa\x5e\x43\x71\x26\x91\x8c\xd3\x64\x1c\x14\x1d\x63\xf6\xbb\xe5\x7e\x6c\x4a\xb9\x9e\x70\x62\x53\xce\xf5\xec\xdd\x96\x96\x71\xb8\x90\xdf\x3d\xb8\x3c\x4c\x70\x05\x61\x38\x04\x27\x04\x5e\x4b\xa8\x9a\x7d\xf0\x00\xf4\x0d\x66\x2f\xaa\xb7\xe9\x72\xe7\x3b\x80\x83\x3b\xf4\xbe\x13\x64\x53\x6b\x75\x69\xf1\xf1\x99\x51\x72\x84\xbf\x84\x67\x9e\x4d\xe4\x09\x45\x8c\x4f\xdc\xbf\xa8\x7a\xed\x97\x5a\x7c\x32\xc9\x17\x25\xa5\xe1\xfa\xb6\xba\x3b\x6c\x65\xfe\x92\x46\x49\xa7\xd5\x72\x2b\x57\x8f\xe2\x38\xb9\x71\x3c\xe1\xeb\x0d\x90\x0d\x3b\x6c\x99\x77\x7b\xb8\x47\xf8\xaa\x26\x49\x8b\x03\xa3\xaf\x0a\x85\x1e\x7f\x43\x1a\xb8\x61\xdb\xf0\x6a\xa1\xdb\xb3\x5a\xc1\xed\xab\x8d\x04\x71\xed\x74\x59\x2c\x96\xc5\xeb\x74\xaa\xd9\xb5\xf0\xc5\x83\x56\x8b\x74\xfe\xc3\xfd\xcc\x20\xb1\xcc\x04\xd3\xdc\x1a\xc6\x64\xbb\x81\xe2\x30\xfc\x96\xcb\xe0\xa7\x19\x0d\x97\x63\x8a\xe6\x2a\x18\x8f\x7b\x44\xb8\xa2\xc4\xfc\x24\x18\x8f\x8f\x45\x32\xe7\x89\x0c\x29\xe2\x5b\x52\xf9\x33\x73\xca\xfa\xf9\x2c\x9a\x14\x9d\x2e\x19\x39\x18\x95\x59\x8e\xd2\x2a\x18\x8f\xa5\x96\x8a\x1b\x7b\x73\xd2\xa6\x31\x2d\xa8\x1c\x87\x76\x92\x64\xa6\x73\xaa\xba\x01\xcb\x40\xf7\x57\xe2\x5d\x89\x58\xda\x6c\xab\xe7\x62\x5c\xa9\x63\x85\xbb\x92\x8b\x8c\x86\xab\x85\x1f\x8f\xe3\x06\x5b\xfa\xf9\xa3\x7b\x64\xda\xaa\xf7\xc8\x54\x55\x7c\xb3\xdc\xc6\xce\xac\x80\x18\x12\xa0\xe1\xfb\xc1\x16\x3b\x6c\xb7\x4f\x8e\x40\xf9\x87\xf2\xff\x54\x4a\xcb\xd8\xf4\xbf\xc1\xa3\x46\xeb\x55\x9b\xf7\x45\x63\x25\x35\x7e\x2d\x67\x53\x0c\xd4\x3c\xb9\x96\x71\x40\x69\x5f\x08\x2d\x1d\x23\x80\x13\x83\x7a\x7d\x00\xd8\x7f\x95\x26\x0a\x2f\xe8\xb1\x62\xf7\xbc\xed\x93\xd2\x01\x18\x56\x13\xde\x3b\x61\x03\x97\xc8\x23\x56\xd5\x95\x70\x9d\x9f\xac\x1b\xba\xc6\x7a\xda\x44\x01\x7f\x5b\x5f\x97\x03\xbf\x6e\xf2\x0d\xa7\x41\x8f\xfe\xaf\x3a\x90\x08\x8e\x21\xb2\x36\x18\x90\x8f\x87\x2f\x0e\x47\x24\xa3\xdc\x20\xab\x47\xf2\x54\x98\xce\xa8\x2b\x2e\x6d\x8b\x13\x70\x4d\x57\x9f\x95\x8b\x8a\x76\x4e\x12\x3a\xa6\x79\x1e\x64\x97\x6c\xb1\x40\x04\xec\x9c\x91\x5b\x1b\xfc\x15\x83\xb7\x68\x72\x9e\x66\x9f\xb9\x94\x37\x5f\xc6\x45\xb4\x88\x51\x24\x07\x33\x76\x8a\xdf\xbd\xd1\xe0\x21\xf1\xda\x72\x7f\x23\x4d\xb9\x79\x1d\xa6\x19\x83\x6c\xde\xb0\x21\xd5\x8d\xd1\x90\x6f\x1c\xe6\xc9\x44\x95\xea\x4b\x1c\xf9\x1c\xd8\xac\xb3\xce\x1d\xbb\xb0\x27\xbe\xf3\x43\x19\xac\xc5\x4e\x89\x63\xdf\x68\xf6\x53\xf8\x73\xf2\xd5\x54\x63\x06\xe9\xad\xa7\xf4\x08\xa5\xeb\x17\x04\x6f\x8f\xc9\x01\xf0\x1c\xb9\x79\x8e\x0f\x1b\x3c\x47\x31\x3d\x61\xd2\x63\x76\xd1\x63\xf9\x29\x8a\xe5\xb4\xb0\x22\xc5\xf8\x7c\x5c\x55\x1e\xc4\xaa\xa7\x3b\xa2\x15\xe3\xd5\x30\x9e\x21\x97\xd1\x0b\xd1\x41\x4e\x2e\x57\x1e\xb6\x2a\x78\x07\x03\x27\xc8\x6e\x94\x5e\xf4\x0d\x76\xa4\x3f\x76\x88\x04\x90\x5c\x08\xfe\xdf\x91\xa9\x8a\xe5\xf0\x1f\x2a\x1d\x31\x1a\xf9\xd3\x94\x23\xe9\x85\x78\xde\xed\x72\x73\x8e\x06\xed\x99\xa8\x84\x3f\x97\x70\xe4\xd6\x68\x1b\x3c\x18\x61\xa7\xe1\x8c\x31\x7f\x77\x7f\x33\x7a\x7f\x33\xfa\xd7\xbe\x19\x15\xd7\xa2\xe2\xc9\xef\x7f\x45\x7c\xbd\x3b\xf5\x18\x0e\x87\x80\x87\x64\x3f\x4d\xce\x28\x63\x45\x81\x08\x79\x0c\xe7\x60\x38\x0b\x40\xdc\x62\x19\xc8\x85\x11\x70\x10\xe7\x29\x09\xe2\x38\x3d\xcf\x79\x78\x76\x50\xd4\xe5\xfd\x35\x56\x91\x14\xfc\xdf\x44\x17\x34\xbc\xe6\x59\x6b\xee\xbd\xc6\x9a\xb8\x51\x2d\x52\x3b\xc8\xb1\x50\x59\xaa\x03\x67\xc7\x54\x89\x92\xab\x2b\x19\x20\x5d\x67\xb4\x95\x0e\xb5\xdd\xb5\x95\x01\xfc\x2c\x27\x44\x24\xae\x98\xe5\x7d\xe8\x48\xfd\xa2\xd1\x10\xd7\x43\x1c\x4e\x40\xd5\xdc\x85\xda\x87\x4e\x9d\x00\x29\xf8\x3e\x7e\xd1\x6a\xdc\x19\xc9\x20\x4a\xaa\x1d\x38\x72\x31\x51\x93\x71\x5a\x79\xf9\x63\x5b\xc2\xa6\x4a\xbf\x2f\x0e\x5b\x3d\x36\x09\x67\x34\x8b\x26\xe0\xd7\x23\xa3\xe3\x80\x71\x1c\x14\xa8\xe6\xc1\x03\x12\x07\xbf\x5e\x92\x38\x0d\x42\x12\x5e\x26\xc1\x3c\x1a\x93\x34\xa1\x39\xb4\x26\x26\x44\x37\x24\x82\x59\xa7\x4a\x4f\x00\x50\xd2\xbe\x5e\x36\xee\x40\xb1\xd9\x9a\xd2\xe2\x50\x1d\x92\x3d\x1e\x9c\xd9\xc4\x68\x81\xb5\xce\x3d\x00\x56\x26\x88\x29\x91\xc7\xe4\xf2\x5b\x0f\x43\xd3\x5f\x7a\xf5\xc2\xb3\xf3\xf3\x08\xe2\x95\xa0\x5e\x11\xd0\x41\xe4\x94\x9f\xa0\x47\xce\xcb\x2a\x2e\xbc\x2f\x33\x2a\xd4\x8b\x3d\xb8\xc0\x1b\xf3\xd5\xc1\x0f\xc7\x33\x7a\xe1\x53\x1b\x68\xad\xa9\x95\x60\x79\xa2\x6c\x50\xc4\xd0\x7c\x8a\xb0\xda\xa5\x4a\x79\x4b\xe1\x2f\x83\x70\x3f\x13\xe1\xc9\x59\x55\x62\x91\x75\xc9\x48\xae\x37\x01\xe6\xca\x4a\xbe\x6b\x02\xcf\xf3\x3a\xe8\xe6\xc8\xea\x76\xcf\x81\x63\x4b\x40\x43\xb1\x2f\x17\xa6\x48\x71\x3d\x6e\x7e\x20\xa3\x32\x4b\xa0\x00\xc7\x64\xb6\x5b\x83\xfb\xab\xd1\x4a\xd7\x5a\x7d\x55\xae\xeb\xeb\xdd\x4d\x6a\x14\xa5\x4c\xfd\x14\x3a\xe8\x70\x0a\xcc\x67\x8c\x02\x3d\x08\xb7\x48\x5d\xaa\x6a\xf6\xc2\x90\x3f\x8b\x50\x4a\xb4\x20\x09\x49\x4e\x8b\x9c\x2c\x17\x90\x21\x4e\x23\xc0\x32\xa2\x82\x66\x6c\xef\x48\xcf\x84\xb0\x25\xdc\x98\xf6\xd7\xd6\xd0\xd3\x88\xd7\xe9\x34\xdf\x2b\x3e\x14\x41\x56\xac\xd9\x9a\xc6\x9c\xc6\x13\x95\x38\x71\xdf\x2f\x0b\x16\x6e\xd6\x62\xc4\x09\xa3\xf1\xc4\xf1\xe1\x23\x1f\xd9\x4d\x69\xc1\xf5\x59\xac\xb0\xf5\xd2\x0e\xf4\x0b\x7a\x98\x39\x74\x8f\xc8\x93\xa7\xc5\x33\x58\x2b\x7d\x1f\xe3\x80\x8c\x29\x2d\x3a\xd6\x9b\x1f\x61\xc9\xe8\x9c\x72\x06\x03\x12\xa6\x49\x5b\xbc\x12\x65\x7d\x14\x68\x03\xb3\x49\xb8\xe8\x96\x89\xd2\xec\x08\x3c\x61\xf4\xfb\x7d\xf2\xcb\x92\x3b\x02\x66\x6d\x32\xde\xeb\x9c\x97\x4b\x1e\x46\x56\x3c\x8a\xbc\xb6\x5f\xc0\x5a\x2b\x5d\x0d\xc3\x7f\xc6\xe4\x99\xde\x83\x29\x37\xe4\xac\x7b\xa6\xc9\x1f\xef\x98\x66\x9f\x46\xff\xea\xfd\xb0\x7e\x3d\xd2\x5d\xa4\x71\xcc\xc9\xc7\x4f\xb6\x82\x36\x35\x98\x4d\x97\x4a\x25\x02\x6a\xdb\xe4\x8d\x32\xc3\x35\x88\x25\x2d\x21\x17\x31\xa3\xa9\x33\xa7\xd2\xc8\x82\x91\x9e\x1c\xab\x6f\x12\x7c\xcf\xa6\x7c\x34\x91\x36\x3e\xc9\x37\xa5\x8e\x9b\x51\x86\x36\x53\x86\xa1\x69\xe5\xf5\x33\x2b\x41\x57\x32\x92\x85\x5c\xd2\xb9\x15\x7a\x6e\x47\xa4\xa5\xfa\x00\xe8\x93\xed\x8c\x9a\x31\x9e\x77\x69\x1c\x33\x3e\xa3\x7b\xc2\x69\x70\xc4\x8b\xb0\x73\x1a\x9d\xd3\xa4\x80\x23\x67\x9f\x51\x1c\x0c\x4d\xef\x25\x0b\x61\x68\x7f\xcc\x31\x05\xe4\x78\x10\x9e\xf4\xe4\x15\x95\x91\xdc\xd3\xc4\x28\x72\xb0\x1b\x23\xae\x20\x06\xfa\x65\x9b\xb5\x8c\x5a\xe8\x90\xb8\x25\x93\xf5\x88\x13\xde\x43\x2e\x37\xcf\xed\x40\x4f\x9c\xa6\xf6\x33\x0a\x63\x02\x7b\xed\x7d\xcf\x43\x47\x60\x76\x5c\x83\x8d\x2e\x5c\x0d\x7c\x20\x0d\xdf\x2a\xaa\xb2\x52\x5d\x57\xa9\xb2\xc7\xaf\x54\x33\x3b\x83\x6c\x09\x48\xa9\xc7\xf8\x52\x6b\x4c\x2d\x6c\x6a\x31\xd8\x12\x7d\x11\xb4\x83\x06\x33\x01\x41\xca\x99\x77\x9f\x8c\xa9\x15\x22\x2c\x6b\x54\x86\xd8\x72\xf7\xcb\xf2\x35\xdb\x73\xb2\xf0\xb5\x93\xfa\x5d\xda\xef\x7e\x42\xcf\xc5\xad\x13\xc6\x01\xf6\x15\xc6\x99\x64\x14\x1a\xae\xf1\xfc\xcc\xb1\x66\xd9\x77\xc6\xa7\x1e\x31\x77\x7c\x5a\xcb\x07\x89\xe0\xc8\xe2\x5c\x58\x41\xbd\x96\x43\x52\x97\xbd\x54\x94\xf5\x77\xa3\x5a\xef\x6c\x2c\x6d\x46\x04\xa1\xeb\x07\x10\xbb\x6a\xc8\x28\x5c\x32\xb0\x33\xc7\x82\x26\x21\x18\xb8\xa9\x49\x0e\x72\x50\xb4\x24\x39\xa3\x50\xe5\x0b\x46\x57\x94\x4e\x00\x98\x15\x62\x52\x4f\x97\x2b\x57\x54\xeb\xcb\x24\xc8\xf3\x68\x9a\xd0\xb0\xef\xf6\xd1\xa6\x28\x1f\x4f\xf6\xcd\x8e\x92\xb1\xc6\xa7\x35\x13\xe4\x6d\x06\x9b\x8c\xa1\x91\x68\x7b\x62\x12\x63\xe9\x30\x88\x33\x1a\x84\x97\xfa\xbd\xba\x16\x14\xf3\xdb\x53\x9a\x29\xc8\x4a\xe9\xb5\x6e\x5c\xd1\xa4\x63\xb5\xa6\x7c\xc0\x0d\x5d\x8f\x5c\x7a\x65\x72\x2e\xee\x73\x0b\xc9\xa4\xe8\x22\x15\x63\x8b\xe6\x73\x1a\x46\x41\x41\xe3\x4b\xbb\x59\x41\xee\xe3\xa6\xb4\x6d\x4a\x27\x50\x7d\xa7\xc4\xd3\x84\xcf\x6b\x15\xd6\x64\x73\x96\xcf\xb6\x1f\x3e\x18\x74\x97\x7b\xee\x44\xe9\xb0\x37\x73\x93\xb7\x71\xc3\x3e\xd4\x0f\xa9\x8e\x31\x98\x23\x1e\x8d\x35\x4f\xe2\xba\xd4\x1d\x08\xc2\x35\xba\x13\xbe\x6e\x3a\x10\xbc\xef\xd6\x8f\xc7\x91\x1c\xd2\x85\x14\x1c\xcc\x81\xd4\xf0\x77\x78\x5a\x3e\x4f\xcf\xa4\x4a\x93\x04\xf9\x65\x32\x56\x87\x1f\x9f\x60\xe4\xe3\xdb\xcb\x04\xde\x4e\x1b\x08\x40\x32\x86\x85\x2d\x87\x77\x61\x43\xf8\x55\x6a\x36\x04\x7f\x07\xa3\x53\x2b\x64\xbb\xcf\x79\x82\x23\x53\x78\x4d\x4e\x54\x49\x5b\x28\xb7\x76\xd4\x12\x3b\xca\xc1\x80\x1c\x4c\x34\x67\x8c\x72\xf5\xae\xef\x92\x0a\xf7\x2b\x24\x2a\x88\xf6\xd2\xa5\xcb\x9d\xcf\x28\x18\x63\x88\xd1\x77\x09\x67\xaa\x39\x89\x0a\x93\xad\x7a\x37\x6a\x87\xd8\xd5\x32\xf3\xed\x1e\x3e\xf4\x8b\x1a\xed\x09\xc5\xfb\x31\x44\x48\xf1\xf0\xb7\xaf\xe8\x9f\xc7\x92\xc7\x33\x6a\x5b\xef\xc5\xe9\xb4\xac\x5d\x62\x31\xa6\x8a\xb3\x05\xd4\x32\x62\x7b\x42\x89\x3b\x3e\x7f\xc0\x12\x13\xc4\x39\x00\xd8\x03\x6b\x4e\x47\x8e\x9b\x29\x21\x88\x1f\xbc\xe0\x09\x23\x41\x63\x9d\x6e\x9f\xef\xc8\xe3\x40\x3a\x2c\x04\xb7\x2a\x34\x24\x6c\x75\xcf\xb2\x34\x49\x97\xb9\xf2\x5e\x28\x0c\x03\xd8\x6e\x6f\x7b\x22\xe2\xd5\x08\x61\xb7\xed\x35\xaf\x05\xa7\x12\xa9\xb6\xd2\x6b\x42\x40\xae\x0d\x1d\xab\xa1\x7e\x0e\x6f\x31\x6f\xd7\x35\xfc\xd8\xb9\x22\xe5\xb8\x75\x62\xbf\x55\x5c\x90\x5e\x9f\xf4\xb6\x87\x4d\xae\x40\xdb\xcb\x9c\xeb\xc5\xc7\x45\x7b\xed\xfe\x42\xf4\xfe\x42\xf4\x4f\x7c\x21\xaa\x9f\x8a\x22\x95\xf5\x4d\xde\x8b\x0a\xe0\x15\x6e\x32\x7d\xb1\xdf\x1a\x3f\x31\x4d\x26\xd1\xd4\x0b\xc7\xb3\x24\xe0\xc1\x69\x60\xc5\x74\x89\x4e\x83\xc4\x13\xa7\x05\xb4\xc9\x3c\xd0\x14\xb7\x91\xe6\x97\x99\xa7\xd1\x54\x78\x30\xb0\xac\x18\x39\xd0\xf3\x68\x6a\x29\xf5\xb1\x35\x23\xd7\x38\x5f\x71\x88\x2b\x05\x7b\x6d\x3a\xad\xd2\xe9\xd8\x12\x17\xf4\x8c\x25\x6d\x18\x52\x11\xef\x9d\xf7\x19\x5a\x91\xaa\xb2\x12\x6c\x47\x29\x81\xa2\xfc\x5d\x46\xc5\x35\x28\xba\x9d\x30\xea\x3e\xd5\xe9\x56\x03\xc7\xca\xdd\x7d\x5b\x9c\x3c\xdb\xbd\x36\x0d\xb2\x38\xe2\x89\xe3\x74\x3e\x8f\x8a\x82\x86\xed\x13\x75\x45\x6a\xd4\xf6\xc3\x2e\x19\xa2\xce\x24\x8b\x65\xf1\x82\x4e\x82\x65\xec\xbd\x2a\xa9\xeb\x15\xdb\x83\x4f\xf1\x20\xf0\x43\x19\x6f\xbc\x16\x46\x24\xfd\x10\xb5\xe8\xf1\x36\x55\x7e\x73\x83\xbb\x60\x8d\xe2\xb7\xe8\xbe\xfd\x86\x8b\x8b\x24\xac\x96\x92\x59\x35\x1a\xf5\x54\x88\xb2\x3d\x78\x90\xd4\xf4\x8a\x5e\x54\x8d\xfc\xe5\x22\x1d\xcf\xaa\x46\x4e\x35\x00\xef\x03\x88\x98\x3a\xd1\x7d\xdf\x64\x67\xb6\x38\xd5\xb5\x2c\x6a\x94\xc9\xac\xef\xac\xe7\x9e\x7e\xe3\xb6\x0d\x73\x66\xde\xd5\x1c\x99\x6f\xa6\x13\x12\x18\x4e\x0c\x83\x24\x94\x77\xba\x39\xdc\xe9\x70\x0b\x06\xc6\x21\x5e\xbd\xfc\xa7\xc5\x18\xa0\x0e\x26\xc1\x7b\x59\x82\xbc\x75\x30\x9c\x01\x3b\x26\xfa\xf2\x32\x5f\xde\x4b\xb8\x75\x7a\x43\x94\x7f\x31\xae\xb9\xe1\xa2\x12\x5d\x16\xc3\xe7\xd5\x95\x45\xfb\x7b\x63\x08\x10\x81\x5c\xb4\x61\x78\x8f\x6f\x30\x59\x2d\xf4\x49\x38\xcc\xf2\x5f\x92\x9a\x12\x1b\xae\xba\x48\x45\x64\xeb\xa8\x20\xf3\x68\x3a\xe3\x22\xae\x72\xb3\x2c\xd4\x69\x4e\xcb\x45\x5a\xdb\x6e\x91\x9a\xad\x1e\xb7\xa7\x41\xfe\x2e\x8b\xc6\xb4\xdd\x23\xec\x37\xfb\x0f\xa6\x8f\xfd\x48\xd2\x64\x4c\x7d\xef\x28\x3f\xd3\xcb\x8a\x97\x94\x9f\xe9\x65\xd3\xb7\x94\x50\x93\x83\x43\x5e\xc3\x2e\xb2\xfc\x78\x41\xc7\xd1\x3c\x88\x3b\x18\xc0\x7d\xcb\x66\x5e\xf7\x7e\x6d\x22\x46\x4e\x3f\xef\x9a\x96\x7d\x55\xdf\x3d\x49\xdf\x94\x6a\xef\xe9\xf5\xb7\xa4\x57\x21\x6e\x39\x04\x0b\x37\xbb\x32\x4a\x91\xa0\x56\xaf\x10\xd6\x98\x4e\x2f\x4c\xc1\x4b\xa4\xaf\x19\xd2\x56\x2d\x65\x16\x17\xdd\x2f\x4a\xe7\x78\xd1\xc7\xdb\xf6\xba\x3c\xf7\x6b\x5d\x9b\x09\xa0\x7c\x6f\xa4\x12\x7f\x26\x80\x7a\x5d\xc2\xd2\x11\x2e\xe0\x1d\x9b\xbf\x7a\x07\xca\xdb\x86\x0d\x25\xd5\x8a\x17\x7d\x20\x29\x7f\x21\xc8\xd2\x90\xd3\x20\xf7\xc3\x4d\x83\xdc\x80\x02\xf2\x45\xa0\x5a\x08\x45\xf9\xba\x84\x34\xb3\xf3\x82\xe3\x47\xbd\xf2\xcc\x7f\xb1\x32\x29\xc9\x78\x38\x37\x21\x29\x11\x9a\xa7\x92\xb2\x54\xa4\xa8\x55\xc8\xcb\xae\xd8\x72\x10\x83\x43\xfc\xe8\x90\x3e\x35\xf4\xe6\x83\x72\xe7\xcc\x03\xa5\x29\x4f\x66\x36\x20\xbf\x52\xd0\xf2\x26\x4b\x08\x51\x45\x9e\x59\xce\x97\x71\x50\x44\x67\xf4\xa7\x20\x3f\xca\xe1\x05\x5d\x59\x55\x0e\xac\x55\xd7\xb4\xb6\x86\xa9\x2a\x27\x07\x6f\x1a\x45\x48\xb8\x38\x9d\xda\x36\x86\x3a\x03\xc5\xcd\x71\x94\x60\xa0\xc9\xf2\xaa\xc0\x3c\xef\x70\x19\x6c\x9d\xbe\x4b\xb4\xd4\x68\x01\xc0\xec\x36\x27\x79\x38\x2d\x54\x52\x39\x54\xd8\x80\xc6\xcd\x9a\xb0\x25\x0d\xd4\xa0\x4c\x69\x06\x03\xa2\x9c\x10\x81\x37\x3e\x71\xce\x26\x84\x37\xc5\xe6\xe7\x75\x34\x8f\x0a\xcf\x14\x9a\x00\x02\x57\x2a\xb1\x64\xde\x8d\x7c\xa3\x4c\x1e\xfd\xea\x63\x82\x3a\xd3\x80\x2e\xa2\x39\xcd\x8b\x60\xbe\x28\x2d\xa2\x20\xf4\xba\xe2\x19\x49\xd9\xca\x35\xb2\xcb\xaa\x55\x87\x78\xd4\x99\x30\x9a\x4c\xa2\xf1\x32\x86\x87\x24\x2e\x0f\xb5\x81\xcc\x81\xa4\x45\x10\xbf\x68\x52\x81\x05\x89\x85\x24\x73\xcd\x08\x70\xbd\xcc\xcd\x95\xe3\x66\xbb\x22\x48\x54\xd0\x79\xd7\x7e\x42\xe6\xd8\xf1\x01\x94\x7b\x63\x6a\xac\x2f\xdf\x66\xce\x0b\xd6\x2d\xb4\x53\xae\x12\xb8\xde\x69\xb4\xca\x3e\x44\xd3\x84\x66\x24\x8e\x9c\x68\xf8\x2b\xad\x2d\x5e\x4d\xee\x5f\x62\xc4\x5d\x63\x02\xbe\x7c\xa9\x09\x00\x7d\xd8\xf6\xcc\x95\x84\x91\xb3\x84\x13\x6b\xe6\xa6\x7e\x56\x04\x36\xb9\x62\xed\x10\x7a\x2e\x7d\xf8\xa3\x69\xe0\x53\x80\x0e\xee\xb8\x0f\x8d\x78\x5d\x9c\x4e\xbd\x88\xc7\x0c\xd6\x87\xf6\x38\x9d\x6a\xa5\x9b\x8b\x74\xa8\xd7\xc0\x3b\xae\x10\xa3\x1b\x5d\x76\x44\x13\xf6\x65\x6c\xae\x0a\x1f\x56\x86\x67\xa1\xdb\x45\x77\x70\x9d\xce\xee\x69\x54\xdc\x60\x1b\xf6\x56\x62\x34\x11\xa7\x53\x4f\xd5\x32\xb5\xa4\x4a\x55\xc8\x94\xfc\xe1\x02\xa7\xfe\x94\x7a\x3e\x8b\x72\xb6\x39\x2d\xd2\xbc\xb8\xc1\x31\xf5\x5d\x9a\x57\x4b\x67\x6e\xe0\xa5\xca\x4d\xcc\xad\x14\x4f\x34\xeb\x24\xde\xc1\xd8\x77\x7f\x11\x5c\xc2\x6b\x8a\x5d\x43\xe5\x84\xb3\x04\x92\x21\xa9\x28\x62\xef\x59\x4a\x66\x62\xd8\xf3\x34\xfb\xfc\x31\x7d\x97\xa5\x67\xb4\xbc\x0c\x02\xc2\x65\x17\x59\x94\x66\x11\x62\xeb\x4e\x41\x09\x81\xe2\x09\x4c\x70\xb8\x29\xc3\x7e\x9a\xf3\x0c\xde\x49\xee\x5c\x05\x33\x76\x94\x4e\x76\x8d\xaf\x67\xe4\x18\x7d\x9e\x90\x91\x32\x5e\xb8\xd6\xad\x72\xcd\x3b\x57\xc2\xc7\x71\x7a\x0e\x8f\x49\xa4\x2e\xa1\xaa\xfa\xea\xc7\x0f\x3c\x60\x22\x23\x26\x92\x26\xf1\x25\x8f\x02\x51\x18\x6f\x32\xe4\xbb\x08\xfe\xfe\xc1\xf7\x9c\x47\x3e\x8e\x20\x23\xfb\xa9\x0e\x7e\x16\x61\x1f\x7b\x59\x1f\x1b\xf1\x2e\x75\x0b\x04\xf4\x2f\x6c\x53\xbd\xdc\xac\x8e\xd2\x9b\x6c\x1c\xd5\x84\x2d\xe8\x1a\xf0\x4b\x2f\x16\x51\x76\xe9\x59\xf1\x28\x17\x93\x5b\xce\x9d\xc6\x78\xa1\x59\x5e\xd9\x12\xb0\x40\x3d\x0b\x00\x28\xdb\x27\xd0\x59\x10\xdd\x1d\xdf\xaa\x7c\x1f\x9c\x4b\x92\x11\x29\x5e\x30\x54\xfd\x5e\x3e\x8e\x22\x7b\xf9\xca\x32\x78\x1b\xfd\x7b\x2e\x10\xa7\xe0\x74\xd8\x15\xbd\x2a\x74\x03\xe0\x45\x19\x42\x9f\xf9\x98\xc3\x60\xb0\xca\x8a\x80\xb5\x89\x57\x63\xe9\x62\xd4\xcb\xed\x16\x2b\xc9\x52\xa9\x73\x14\x35\xa3\x7f\xc5\x54\x6d\x2d\x98\x2f\x46\x0a\xbe\x12\x11\x49\xfd\x7c\x79\xca\xdf\x97\x75\x86\xbd\x6d\xbe\x2a\x5b\x17\xe1\xb8\x65\xf8\x0a\x52\xce\x88\x5a\xc3\x8b\x16\xd9\x20\x6e\xe1\x6d\x23\xa2\x08\xf4\x8a\xdf\x0e\x26\xf4\x1c\x2e\x0a\x3b\x66\x88\x6e\xb8\x4f\x39\x0d\x92\x7e\x94\xff\x23\x88\xa3\xb0\x03\x21\x34\x44\xca\x8b\x28\xa3\xe3\xa2\xe3\xbb\x4c\x11\x9e\xca\x00\x50\xd4\xd8\xe9\x3a\x37\x35\x58\x70\xd2\x91\x8d\x64\x0f\x3c\xd5\x1a\xce\xf0\x3c\x15\x35\xa8\x42\xf4\xcc\xac\x89\xeb\x61\x6c\xd3\x14\xe1\xae\x5c\xc2\xb6\x65\xa8\x71\xcd\x49\x3e\x5c\x26\xe3\x28\xf1\x8b\x43\xc2\x3f\x38\x9a\xcb\x75\x33\x89\xb8\xee\x92\x0c\x21\x1c\x9c\x2b\x81\x6d\x63\x94\x4c\x41\xd8\xf5\x9e\xe3\x5d\x30\xd3\x45\x95\xf0\x16\x55\x53\x01\x86\x32\xcb\xcf\xa2\xe9\x8c\xe6\x75\xe5\x31\x14\xa2\x1d\x91\xfb\x39\x49\xcf\x93\x0f\x45\x50\x50\x9f\xbb\x42\x94\x5b\xde\x00\xae\x62\xc7\xae\x61\xb1\x8c\x63\x1a\xd6\x55\x81\xa1\x4a\x54\x0b\xda\x6b\x55\x49\x60\x82\xba\x5b\xda\x51\x2d\x44\x4f\xd7\x53\x51\x41\x4d\x49\xdf\x3d\xe3\xa8\x3c\x0b\x95\x34\xae\xd0\x46\x9e\x34\x04\xeb\x3b\x3b\x8e\xca\xb3\x50\x49\x9b\xcd\x8d\xfc\xc9\xa8\x84\xb1\x29\x8f\x3c\x69\x1c\xb6\xcc\x1e\x60\x54\x9a\x83\xcb\xf9\x07\x54\x9e\x57\x52\xd6\x56\x5b\x7a\xaa\xb0\x41\x8c\xde\x1b\x47\xe1\x91\x37\xd5\x81\xb7\x0f\xba\xa3\xaa\x4c\x5c\x1a\x1f\xd7\x46\x9e\x34\x0c\x6b\x4d\x82\x27\x11\x43\xdb\xdc\x6f\x54\x92\xce\xb9\xa6\x61\x82\xc6\x6f\xab\x5a\xa3\xcd\xa7\x65\x8e\x95\xd8\xd6\xd1\x1a\x6d\x6f\x5f\x9f\xf4\xb6\x37\xef\x9d\x72\xdc\xdb\xa0\xfd\xd7\xd8\xa0\x09\x4a\xbf\x8b\xe8\x3a\xab\x85\x22\x68\x68\x78\xc6\x83\xff\x98\x16\x65\x3c\xed\x2b\xc4\x34\x68\x1e\x85\x20\x88\xe3\x81\x15\xa7\x13\x9e\x17\xdb\x51\x7e\xdc\xd8\x04\xd2\x46\xde\x0d\x68\x56\x11\x93\xc0\x17\xd1\xec\x13\xdf\x1a\x85\xcf\x7c\x1c\xca\x77\x75\x7f\xf6\xba\x52\xb1\xb7\xe0\x5a\x79\xd2\xed\xaa\x85\x30\x80\x01\x9c\x57\xa1\x4e\xf9\x8d\x61\x64\xa8\x5f\x01\x22\x3e\x31\xc4\x9d\xc4\x53\x60\xfb\x83\x3d\x19\x86\xe7\x4d\x30\x32\xd0\x2f\xd2\xf0\x91\x29\x9b\x1a\xe7\xa5\x1b\x04\xb0\x96\x67\x0b\x1d\xed\x0f\xdc\x5a\x00\xaf\xe7\x6f\xa8\xb2\x69\xce\x83\x26\xac\x0b\xa1\xb1\x59\x87\xb1\x10\x58\xd9\x69\xdc\xbd\x1f\x1c\x52\x92\x39\x38\x76\xa1\x78\xac\xe9\x0e\xce\x3f\x36\xdb\x15\x43\x85\x78\xda\xd1\x78\x68\x88\x88\xaa\x08\x85\x38\xa6\xb2\x2f\x0c\x57\x94\x93\x71\x9a\x65\xae\x87\x4c\x38\x79\x05\x05\xdd\xcb\xa6\xb9\x2f\x68\xa1\x8e\x9a\xfe\x90\xfc\x0d\x4e\x6e\x39\xf9\x02\xe7\xb6\x6b\xd6\x5e\x54\x88\x27\x2a\x86\x13\x4d\xcf\x54\xe1\x76\x4a\xe7\x48\x9f\xde\x39\x14\xa0\xc8\x31\x40\x09\x34\xe2\x07\x03\xf9\x16\x09\x34\x5d\x86\x77\x1a\xd8\x3c\xc1\x27\xa2\x0e\x46\xc6\xb6\xda\x00\x5e\x32\x66\xc1\xa5\x7c\x97\x27\xe6\x6e\xbd\xe3\xc4\xb2\x0c\xba\xca\xc3\x3a\x3b\x8f\x3b\x17\x40\xd6\x1d\x87\x00\xe7\xde\x92\x2b\xe1\xf5\x95\x97\x51\xc6\x2a\x60\x3d\x0a\x06\x1d\x81\xc4\x8e\x24\xc4\xf5\xdd\xdd\x32\x42\x36\x1f\x62\xb1\x33\xb7\x08\x27\x57\x11\xf4\xad\xe3\xf8\x47\xa8\xf2\x28\x2c\xb5\x4d\x60\xf0\x84\x49\xc5\x88\x89\x91\xbe\xe3\x60\x1e\xf2\x72\x36\x0d\xed\x49\xbc\xc4\xbb\x70\x10\xab\x56\xb5\xfd\x57\x25\xe5\xa9\xf6\x2b\xc9\xce\x88\xa2\xba\x3a\xc3\x58\x95\x5f\x98\xc1\x4f\x4b\xa2\xab\x5e\x6b\x6e\x8e\x97\x4f\xc7\x13\xea\xb4\x48\xfd\x81\x0c\x8c\x50\xa8\xbb\xa4\x24\x48\x81\xcf\xd7\xbd\x78\x86\x83\x86\x6b\xc4\x58\xad\x30\xb8\x2a\x09\xc2\x23\x51\x7f\xb3\x60\x23\xde\xe2\x95\xf3\x7e\xa3\x90\x23\xc2\x5b\xfa\xb0\x47\x9e\x4a\x2d\x54\x45\x13\xcb\x64\x11\x8c\x3f\xf3\xbb\x46\xd3\xa4\x10\x92\x0c\x9d\x94\x99\xa4\xbb\x60\x7a\x8d\x92\x55\xf1\x1f\x8a\xf4\x76\xc9\x16\x79\x26\x13\xa5\x43\x77\x22\xcf\x81\xda\xc3\x81\x72\xc3\x5e\xe6\xcf\x1d\x0b\x39\x3d\x51\xdc\x9c\x51\xa1\xc3\xc1\xde\xa8\x55\x28\xbf\xe3\xe1\x09\x19\xf9\x7c\x8e\xef\x43\x24\xeb\x00\x05\x0f\x97\xc8\xb2\xc3\x93\x07\x71\x8c\x17\x77\xbf\xdf\x97\xeb\x7b\xdf\x2e\x6b\x6d\x3e\x8e\xb7\x9f\x03\xbe\xdd\x41\x94\x62\x09\xca\x76\xa3\x40\xd5\xd0\xe3\x8e\x5d\xec\x8a\xb9\x6b\x3b\x78\x4a\x29\x0f\x5d\x81\xf1\xd8\x2d\x48\x42\xd3\x25\x8c\x04\xe3\x61\xbc\xf9\xc9\x88\xd5\xc1\x63\x20\x32\x70\x81\x36\x2f\xed\x8a\x59\x85\x30\xc4\x75\x54\x0b\xbd\x2a\x0b\xf5\xbc\x4a\x1c\x67\xff\xbe\x29\x65\x30\xcb\x44\x53\xed\x31\x70\x90\xd1\xf2\x9f\xf0\x00\x6d\x88\x85\x98\xfd\x80\x17\x6b\x53\xfa\xc2\x45\xb0\xf8\x63\x17\xd3\x37\x15\xdc\xf3\xb4\xe4\xd2\x12\x4e\x1b\x7d\xac\xfb\x5e\x16\x6b\xdd\xb0\x62\x7c\xb4\x98\x71\x24\x88\xaa\x7b\x46\xd7\xdc\x77\x84\x50\x0a\x2f\xe1\x8e\xb1\x1e\x90\xef\x74\xe7\xe5\x6f\x93\x06\x7b\xae\xbf\x1e\x97\x07\x20\x6f\x3d\xf2\xb9\x88\xe1\xd3\xa1\xc7\x4d\x77\x76\x4c\x1f\xc9\xbc\xd3\x34\x74\xfc\xc1\x17\xd9\xa5\xf5\x0c\x12\x81\xc2\xcb\xc7\xf2\xf1\x12\xe3\xa9\xe6\x18\xde\xca\x77\x1c\x8f\x3b\x9c\xe2\x77\x09\xf5\xfa\xfa\xb1\x3b\x2f\x5b\x47\x92\x4c\xe5\x46\xd1\xe4\x5c\x69\x6f\x1b\x66\x91\xda\x5d\xc1\x6a\xe1\x4f\xb5\xd4\x6a\xd7\x8c\x24\x29\x01\x28\xec\x5d\x7f\x20\x43\x38\xd4\x18\x67\x4d\x57\x3a\xc4\x01\x50\x83\x84\x3f\x3b\x4f\x42\xe1\x9a\x12\x22\xd8\x26\x8f\xe4\x41\xd5\x09\xe5\x5b\xb3\x5c\x8d\xf0\x75\x6c\xdd\x58\xf3\xd0\x31\xaf\x27\x45\x75\xb5\xe0\xcd\xe3\x06\xd0\xbc\x88\xe6\x41\x41\x7f\x0a\x40\x81\x58\x47\x55\x08\xbc\x8e\xa2\x70\xcd\x77\x41\x4d\x5f\x9f\x3a\x9a\xcd\x10\x1a\x57\xdd\xec\x78\x40\xcb\x66\xe6\xbd\x6c\x86\xca\x48\x68\x10\xd1\x45\xea\x02\x85\x7c\x80\xa7\x62\x4a\x8b\x17\x76\xa4\x22\xb9\xb3\xda\xd5\xd4\xcd\x95\xa8\xeb\x8e\xe7\xa9\x11\xe2\xe5\x55\xb5\x58\x99\x3c\xc8\x4b\x73\xa9\xf9\x16\xf1\x14\x71\x51\x89\x67\x44\xf6\x95\x08\xfb\x6d\x83\x2b\xaa\xfa\x6f\x14\x5f\x51\x15\x5a\x75\x90\x5f\x33\xd8\xa2\xd6\xd1\xb0\x01\x66\x8b\xb1\xf4\xe2\x95\xf3\x53\x73\x1d\x23\x12\xd0\xe5\xe6\x36\x15\xe3\x12\x65\xff\xd8\x5c\x89\x18\x41\x67\x24\x18\x16\x53\x8c\xd8\x29\x78\x4e\x5c\xb7\x77\x96\xc6\xf5\x19\xf8\xd2\xfd\xc4\x7a\xdc\x26\x23\xfe\x61\xed\x24\xed\x9e\x23\xbc\x8c\xb4\xbb\x39\x95\xa7\x1c\xe5\x89\xe1\x9c\xe8\x2c\xde\x71\xe9\x86\x95\x33\xc8\x5a\x62\x90\x51\x62\xca\xb6\x1f\x15\x7f\xa9\x7a\xeb\xf1\x84\x4a\xc2\x13\x5c\x18\x82\xce\xba\x89\x1d\x6d\x66\x04\xdb\x7c\x81\x65\x28\xe9\x6a\x44\xa7\x95\x6d\x15\x16\x3a\xfb\xc1\x62\x11\x5f\x0a\xc7\x47\x8d\x08\xab\x6b\xdb\xe7\xf1\x2d\xc0\x6a\x86\x25\xde\xa8\xee\x9a\x79\x10\xe1\x84\x34\xe3\xd1\x11\x85\x6e\x1d\x4a\xc8\x33\x61\x5f\x2b\x9a\x90\x4c\xd7\x2b\x1e\x7b\x7e\x2a\x05\x17\x87\x4d\x8d\xe1\x32\x40\x57\x6a\xf6\x4e\x7e\x59\x71\x53\x44\xe2\x23\xd1\x49\xa5\xc5\xf4\x6e\x2d\x3d\x16\xb1\xcf\x3f\x65\x28\x25\x59\x16\x08\x3c\xca\xc6\xcb\x38\xc8\xd6\xd7\xd7\xd7\xab\x03\x28\x49\x0a\xda\xb9\x93\x10\x4a\x5c\xfb\xdb\x1a\x6d\x3d\xf1\xfb\xa3\xd9\xba\xbf\xfd\xbf\xbf\xfd\xff\x6b\xdf\xfe\x8b\xab\x7f\x06\x2b\x43\x5c\xf9\x03\x73\xfc\x6e\x21\x37\x7c\x96\x05\xd5\x86\x00\x6b\x83\x01\x84\xf0\x0a\x32\x46\xca\x6c\x07\x5b\xe6\xe6\x10\x19\xc1\x85\xd1\x64\x42\x33\x9a\x14\x84\x26\x67\x39\x14\x3a\xcd\xd2\xf3\x9c\x66\x6b\xc8\x3f\xe9\x79\x94\x84\xe9\x39\x68\x2c\x50\xe0\x0a\xf2\xe0\x81\xc8\xe9\xff\xf3\xcd\xeb\x57\x45\xb1\x10\xae\x6f\x39\xd7\x34\xd3\xc8\xae\x1f\x16\x58\x9f\x88\xbb\x10\x4d\x93\x94\x31\x82\x38\x4a\x28\xeb\x49\x92\x86\x74\x0d\x39\x3b\x73\x6a\x54\x03\xbf\x98\xc7\x6c\x64\x62\x63\x6b\x77\x9b\x36\x72\xcd\x31\xf9\xcf\x57\xef\xb7\x8c\xea\x66\xd9\x56\xbb\x5b\x5a\x4a\x4a\x0e\xac\x85\x77\x12\x99\xae\x49\x04\xc8\x4f\x4c\xb4\x07\x6f\x9f\xdc\x37\x38\xeb\xa5\x32\x80\x30\xca\xe3\x2d\x7f\x96\xe6\x45\x8f\x14\xd1\x9c\xa6\xcb\xa2\xc7\x2a\xcc\x7a\xa0\x64\x3e\x4f\x33\xe1\x8c\x05\x36\x13\x06\x47\x76\x09\xfc\x77\x75\x45\xda\x82\xd8\xe3\x74\x1c\xc4\x2c\x71\xf4\xf4\x9b\xc7\xdf\x40\x9c\x5c\xbe\xf7\xf0\x0a\xd9\x4e\x28\x7e\x5d\x5d\x91\xa1\xca\x66\xcd\x90\x5d\x68\x4d\xa5\xc9\x46\xc9\xae\x6a\xbf\x56\x78\x5a\x64\x74\x01\x81\xe7\xe8\xb9\x35\x65\x96\xec\x24\x00\xdf\xa3\xb3\x8c\x90\x9c\x9e\xa7\x69\x4c\x83\xe4\x1a\xee\x58\xd9\xfe\x2c\x25\x18\x8d\x65\xe1\x65\x12\x1d\xf8\xcc\xb6\x0c\x57\x46\x18\xd3\x48\xee\x32\x3b\x60\x5e\x04\xb2\xea\x39\xaa\xf9\x0d\x0a\x27\xa4\x35\xf1\x8a\x0d\x65\x13\xa2\xc5\x2b\x18\xf2\xab\xf7\x5b\x3a\x4c\x2d\x97\xb4\x10\xe6\xd1\x44\xc0\x93\x33\xec\xcb\xcf\xaa\xc8\x18\x4f\x47\xbc\x50\x5b\xd3\xb5\xa6\x0b\x9a\x74\xda\xef\x0e\x3f\x7c\x94\x91\x35\x39\xe1\xf0\xce\xed\xac\x21\xc7\x80\x30\xb7\x0f\x1e\x98\x93\x6a\x1c\xfa\x96\x60\x50\xd3\x7e\x1e\xe4\xd1\x98\xb4\xc9\x06\x74\xe1\xf9\x92\xb1\x07\x54\xc5\x06\x69\x8f\xd4\x55\xa1\xaa\xa7\x5f\xa4\xe2\xf1\x5d\xfb\x34\xc8\xe9\x93\xc7\x6d\x6b\xfc\xda\x2d\xf6\x2b\x1a\x84\x34\xeb\xb4\xf7\x80\xaf\x46\xbf\x06\xfc\xb4\x05\xed\xf3\x11\x56\x14\x62\xf2\x31\x4d\x8a\x47\xec\xa0\xdd\xee\x91\x36\x93\xfc\xa3\x31\x54\x31\xf8\x25\x97\x6a\x47\x75\x63\x25\xa6\xac\x86\x5c\x79\x00\x95\xcb\x64\x8c\x0e\xd5\xb6\x26\xd9\x77\xf1\xbc\x40\xd7\xd7\xfe\x50\xd9\x55\xa4\x97\xdb\xa1\x13\xa5\x2e\xcd\x26\x39\x49\x33\x26\xad\x8a\xd8\xcb\x40\x8f\x5a\xbb\xaf\x31\x97\x84\x1d\x78\xe9\xc1\xdf\x1d\x44\x93\x4b\x55\xbf\x40\xb2\x54\xe4\x63\xaf\xd7\x3e\x6b\x80\xfd\x34\x49\xa8\x78\x8f\x21\x29\x4c\x53\xa2\x71\xb9\x28\x5b\x97\xf1\x27\x3e\xd2\x8b\xc2\xe9\xa0\x80\x45\xcf\x50\x84\x55\xbe\xd9\xad\xaa\x2e\xbd\x17\xf5\x77\x7c\x0d\xe2\x55\xd2\x3c\x14\x32\xd0\x40\x50\x43\x04\x7b\x8a\xe3\x54\x50\x82\xc8\xfa\xd1\x09\x3e\x42\x8a\x2c\x9a\x4e\x69\xc6\x23\x26\xb1\xd9\x07\xb1\x45\xb9\x3f\x65\x38\xa8\x23\x18\xe8\x81\x8f\x6a\xcc\xc0\xc7\x4d\xe8\x07\x8c\x57\x76\x0c\x6e\x92\x80\xab\xea\xbc\x08\x0a\x3a\x9e\x05\xc9\xd4\xaf\x40\xe0\xcf\x0a\x24\xe2\x83\xf0\x12\x0c\xfa\xe1\x46\xf8\x31\xe3\x30\x36\xcb\x5b\x37\x03\x17\x37\xa0\x18\x0d\x28\x6f\x95\x50\x44\x2c\xfb\x32\xab\x86\xa2\xe0\x4c\xe6\xbd\xb5\x52\x37\x56\x2b\xd2\x16\xc1\x57\x5b\xf6\xc5\x96\xd1\x32\x3b\x0b\x5e\x5b\x28\xd6\x1b\x81\x8b\x59\xb3\xb2\xbc\xaf\x97\xde\x47\x5e\xaa\x83\x37\x0f\xb1\x90\xef\x96\x03\xd8\x5d\xa8\x62\x02\x62\xa5\xe1\x75\xa5\x2f\xcb\xe3\x4b\x46\xef\xfc\xd1\x2c\x2c\x2e\x46\xd5\x25\x6b\x2b\xca\x45\xfd\xd4\x64\xa6\x4a\x08\x90\x0a\x4e\x5b\x18\x60\xe7\x87\xa4\x5d\x90\x49\x10\xc5\x34\xec\x93\x43\x76\x4e\x3b\x8f\xd8\xd9\x23\x80\x20\x67\xe5\xab\x09\xb5\xe9\x99\x0b\x8d\x4f\xa5\xcf\x50\xc1\x34\xa2\x70\x44\xbe\x53\x7f\x52\xdf\xc7\x76\x9f\x6c\x31\x1e\x91\xf6\x56\x7f\xa8\x94\x87\x52\xff\xd8\x4e\x68\xf1\x29\x8e\xf2\x82\x26\xe0\xa6\x70\xcd\xd2\x1e\x9e\x18\x06\x5d\x52\xc1\x95\xf1\x88\x6d\x2e\xf9\x4a\xab\x42\x36\x48\x3d\x09\x8e\xba\x00\x0f\x5d\xaa\x0a\x8c\xd3\x3e\x13\x73\x5b\xa3\xa7\xec\x97\x21\x3f\xb7\x46\x9b\xdf\xb2\x93\xff\xf6\xfd\xc9\xff\xfe\xe4\xff\x17\x3f\xf9\x6b\xc3\x7f\x78\x2c\x79\x47\x46\xff\xca\x90\x13\x9f\x2a\x4f\xa3\x29\xb7\xc1\xed\xff\xc2\x4f\xe8\xfc\x1e\x24\x7c\x4d\x27\xe6\x86\xa0\x42\x57\x5e\xa2\x07\x7b\xc6\xc6\xc9\x21\x38\xbb\x38\x9f\xb1\xde\x77\x4c\x03\xad\xef\x79\x61\xf2\x90\x6c\xb9\x2f\xfe\xc0\xe2\x8f\x49\xf1\xe6\xbb\x47\xe2\x7f\x89\x27\x98\xfb\x3b\x71\xaa\x0b\x12\x72\xf0\x7c\xef\xad\x98\xe4\x90\x7c\xf7\x2d\x19\xa7\xf3\xc5\x52\x84\x8d\x39\xbd\x24\xf3\xf4\x2c\x4a\xa6\x28\x38\xda\x63\x32\x9e\x05\x19\xec\x05\xfc\x66\x36\xe4\xa6\x54\xd2\x5c\x5d\x42\xc7\x94\x3f\x5a\x28\x52\xd6\x20\xc7\x55\x4e\x3a\x7b\x64\x97\x6c\x0e\x7b\xe4\x39\xfb\x7f\xb3\x47\xfa\xfd\x7e\x8f\xfc\x1f\xd9\x25\xdb\xdf\x74\xd9\x61\x87\xe4\x0b\x3a\x8e\x26\x11\x5f\x48\x07\x1f\x0e\x37\xb7\x9f\x6c\x3e\xb1\x4d\xcc\xa2\x3c\x85\x74\x31\x0e\xd7\x49\xee\x35\x7f\x8b\xcb\x3a\xc2\x06\x68\x5e\xad\xe1\x9b\x65\x21\x49\x85\x12\x4c\xf8\x6c\x30\xeb\x37\x26\x94\x55\x8c\xe7\x91\x8d\xa8\xbd\xd7\xee\x33\xb4\xec\xa7\x21\xdd\x2b\x3a\x43\xa4\xb5\x66\x63\x6b\xff\x9f\x93\xcd\x19\x20\x7f\x2f\x0c\xc4\x5a\xa4\x47\x8b\x05\xcd\xf6\x83\x5c\xab\xb2\x51\x36\x7f\x76\xdc\x79\xdc\x95\x2f\x81\x45\xc2\xb0\xf7\xd8\xba\x31\xe3\xb9\x8b\x38\x2a\x3a\xed\x76\xd7\x7c\x85\x9d\x74\x4d\xeb\xaa\x71\x1a\xb2\xc1\x25\xbe\xce\x4b\xf9\x10\x60\x7e\xd8\x25\x7b\x4c\x20\x84\x8f\xef\x77\xc9\xff\x75\x9d\x90\x06\x9e\x99\x15\x13\x6b\x40\x2a\x8f\xb9\x21\x25\x8f\xc8\x1e\xd9\x20\x9b\x43\x64\x67\xe4\x73\xf3\x2f\x43\xa9\xda\x36\x4c\xd7\xdd\xfe\x2f\x69\x94\xb0\x61\xda\x96\x8a\xe3\x25\xb8\x70\x85\x29\x7e\x73\xf8\x82\x11\xf6\xe6\x50\x32\x25\x61\xe1\x07\x94\xef\xa1\xb8\x6f\x87\x4f\x1e\xdb\x04\x37\x4f\xc3\xef\xbe\xdd\x1c\x96\x11\x9a\x49\x5f\xda\x2d\x33\xa7\x26\x51\xb8\x92\x8a\x32\x3a\x0f\xa2\x84\xeb\x8e\x58\x9e\xbe\x7b\x14\xae\x83\x4c\xf6\x20\x80\xb5\xdd\xf2\x56\xd7\x72\x8a\x04\xcc\x4a\x82\x29\x8b\xd7\xef\x0c\x13\x39\xdd\x24\xc8\xda\x07\x49\xc1\x7d\xf8\xf4\xc8\xe6\xb0\x4b\xfe\xff\x0c\x6b\x1b\x4e\x2d\xdc\xe5\x92\x30\x3f\xf7\xbd\xfc\x55\x75\xa9\x92\xba\x3e\x63\x9e\xea\xdf\x21\x71\x13\x74\x58\x07\xc2\xe0\x1f\x2e\xd4\x21\x41\xbc\x75\x10\xec\x53\xce\x97\x7f\x72\x06\xd8\x79\xb7\x7f\x12\x84\x25\xb4\x5e\x72\x6e\x57\x9d\xa0\xb9\x75\xfd\xa4\x10\x53\x69\x39\x97\xaf\x73\x2c\xa2\x62\x30\x7b\x2a\xc7\xe9\x7b\x80\xb2\xa4\x18\xcd\x86\x70\xad\xd8\x1a\xd6\x8a\xb1\x9c\x3e\xaa\xb1\xca\x19\x02\xe8\x88\xf2\xe7\xd2\x57\x01\x7a\xa9\x20\x82\x9b\x92\xcd\x27\x88\x85\x9d\x06\x39\xdd\x7e\x42\x76\xa1\x8c\x56\x0f\x6d\x3f\x31\x4c\x00\xc2\x90\x72\xcd\x22\xec\x81\x1d\x5e\xa8\x47\x36\xbf\x31\x25\x61\xd5\xcf\xe7\xa7\x41\xd2\xe1\xc5\x4c\xe6\x67\x2d\x66\xe1\x6f\x05\x2d\xdc\xe7\x6c\xe8\x45\x6a\xec\x5e\x6c\xfa\x08\xf8\x69\xcd\x2e\xe5\x8a\xe6\xca\x24\xb0\xd7\x7d\xc7\x43\x5b\x24\x69\x21\x84\xb2\xef\xa3\x1f\x5a\x53\x90\x48\xb8\x1f\x9f\x89\x46\x6a\x3e\x0b\xb8\xb4\x06\xfb\xdb\xc5\x38\x5e\xe6\xd1\x99\x8a\xc4\x19\x9d\x46\x71\x54\x28\x01\xe7\x34\x48\x3e\x0f\x4e\xb3\x20\x19\xcf\x48\x4e\xb3\xb3\x68\x2c\x37\xc0\x80\xbb\x8d\x6d\x7d\x3f\x88\x7e\xe8\xdb\x34\xa4\xa2\x62\xe4\x72\x17\x9a\xd0\x8c\x6d\x43\x41\x3c\x4d\xb3\xa8\x98\xcd\x49\x48\xf3\x71\x16\x9d\x72\xb6\x24\xe4\x1f\x9a\xf4\xcf\xa3\xcf\xd1\x82\x86\x51\x00\x42\x10\xfb\x1a\x1c\x24\x05\xcd\x92\x80\x3f\x9d\xf8\xf4\x3c\x48\x3e\x7f\x12\x3e\x6b\x3f\xf1\x79\xfd\xff\xfd\x24\x46\x9a\x4c\x3f\xb1\x21\x7e\x82\xb7\x44\x9f\xc2\x68\x1a\x39\x4f\x39\xe4\xd4\xf8\x28\xf2\x54\xee\xa9\x72\x06\xa4\x33\x9c\x22\xf5\x6c\xb3\x0d\x68\xf5\xb9\xbd\x22\x4f\x2d\xb6\x28\x66\x74\x9f\xef\x53\xed\x7f\xbe\x6c\xef\xac\x79\x79\xa6\xe0\xb1\x1d\x6b\xe7\xee\xe0\x0a\x36\x48\x7b\x08\xa2\x12\xb4\x82\xcd\x5d\x18\x3a\x5e\x30\x6c\x90\x5d\xd2\xe1\xe2\x54\xe7\xbb\xa7\xe4\x91\x6e\xa2\x2b\x9f\x0d\x3c\xda\xb2\xf6\x5b\xe5\xed\xc3\x6c\x0a\xd5\x29\x1a\xac\x51\x5b\x09\x26\x82\x70\x05\x84\xcd\xe3\xa1\x47\x49\x5e\x44\xc5\xb2\x90\x9e\x97\xa3\x90\x26\x05\xdb\xb4\xec\x38\x02\xbc\x96\x83\x24\x8c\x32\x6a\x1a\x30\x98\x6f\x6c\xf2\x9e\x94\x65\xd5\x23\x1b\x78\x35\xd5\x42\x2d\xb5\xa0\xa9\x96\x6e\xab\xb5\x0a\x2f\x32\x7b\xe2\xf5\xc6\x6c\x1e\x81\x4d\xce\xd0\x7e\xf9\xf1\x15\x9b\x07\xf9\xba\x05\x63\x00\xa5\xaa\xbe\x75\x2d\x7e\x9d\x56\xf1\x6b\xf9\x94\x8e\x23\x57\x04\x1b\x8f\x72\xfe\x52\x0e\xf3\x71\x47\xee\x04\xcf\x2d\xa5\xf2\xa6\xda\x8b\x3c\x8a\x0f\xa9\xf0\xe0\xcf\xe9\x78\x4b\x4a\xe8\x3c\x40\x7e\x61\x2a\xe5\x84\x08\xfb\x97\x89\x38\x59\x61\xe1\x4f\x3b\x97\xa9\xd5\x95\x2b\x2c\x40\xd7\x4b\x5f\x0f\xe2\x31\xeb\x20\x13\xde\x51\xf5\x48\xea\xd1\xda\xc0\xd8\xb0\xb6\xc6\x1d\xa5\x45\x09\x83\xff\xfc\xf3\xe5\xf1\xf0\xd1\x77\x27\x5f\xb6\xae\x3b\x2f\x3f\xbe\x62\xbf\xf7\x1e\xfd\xdf\xc9\x97\xcd\xed\xeb\x2b\xf5\xb1\x3d\xec\x6d\x6f\x5e\x77\xff\x67\xd0\x2f\x40\x09\xaa\x36\x70\xe3\x5d\x5e\x19\x63\x40\xe0\xfc\x79\xde\xe6\x8a\x08\x13\x4f\x30\xe1\xf4\xef\x45\xdb\x0b\xbd\x04\xef\x06\x6f\x2f\xdc\x95\x64\x21\x4e\x0f\x0a\x3f\xee\xd9\x7e\x4c\xae\xae\xca\xf2\xbe\xb9\xe1\xb0\x27\x24\x4a\x4a\x06\x6e\x70\x9f\xbb\x19\xba\x97\x8d\x34\x1a\xfc\xd6\xb0\x91\xd5\x26\x17\x29\xd9\x48\xf3\xe5\x9c\x01\x1e\xe5\xe2\xf8\x30\x4f\xc3\x47\xdf\x7d\xfb\x68\x73\xa8\xb2\xe1\x8c\x0b\xbd\x1b\xa7\x31\xe9\x1c\x7c\x38\x1c\x1c\xbc\xdc\x27\xec\xdc\x30\xda\x1a\x0e\xb7\xbb\x36\x4f\x46\xd5\xba\xa7\x50\x94\xeb\x0c\x5c\xe6\x35\x1c\xb6\x38\x13\x6e\xf5\xc8\x56\x33\x5b\x55\xcc\x54\x8d\x2d\x85\xd0\x69\x9f\xfc\xf3\xfd\xcb\x9f\x1c\x0f\x89\xaa\x80\x7f\x34\xa5\x35\xba\x93\x8a\x20\xeb\x86\xa7\x09\xa0\x03\xee\xf3\x9c\x21\x7f\xdb\x23\x8f\xbb\x64\x44\xda\xed\x46\xe3\x1e\xc7\x11\x3c\x24\x53\x1d\x04\xe5\x53\x94\xd8\xe3\x63\x58\xf8\x69\xef\x1f\x87\x3f\xfe\xeb\xf0\xfd\xff\xda\xb3\x0a\x75\x94\xcc\xa9\x5d\xbf\x77\x72\x39\xd0\xad\xc7\xbe\xb9\xb9\xfa\xc8\xc5\x6a\xf2\x9f\x4b\xdc\x83\x87\x3b\x34\xa7\x02\x67\x78\x81\xe7\x1c\x82\xef\x9d\xc4\xe0\x7c\x8e\xcf\x8c\x43\x87\x3b\xe0\xc7\xe8\x10\x5b\x7a\x94\x91\xe7\x0f\x75\x4a\x31\x4e\xa8\xfc\x8c\x62\x9e\x67\x36\x9f\x74\x7b\x64\x6b\xa8\x5c\xab\x19\x52\x9e\x44\xaf\x35\x48\x59\xb8\xd9\x02\x2d\xf1\x86\x75\x00\x59\x5c\xa9\x8f\xf5\x8a\xad\x91\xf9\x79\x7d\xd2\xdb\x7e\x7c\xaf\xc6\xbf\x57\xe3\xff\xc5\xd5\xf8\x42\x85\xbf\x18\x57\xdb\xef\xdd\xc2\xe2\xae\xa5\x23\x2e\xb6\x76\x56\x8a\x14\x57\x63\xa7\xc7\xf5\x4c\x8b\xb1\xd7\x12\x6c\x11\x14\xb3\x1e\x49\xa8\x61\xfd\xfd\x09\x34\x17\xce\xc3\x53\x79\x55\x8d\x63\x55\x4b\xaf\x05\xc2\x5e\x07\x6c\x7c\xd8\x7f\x3c\x55\x67\x8d\xd5\x0d\x2f\x70\xc5\x42\x26\x74\xbe\x30\xe8\x91\x2e\xaf\x7c\x6c\x5a\xc5\xfa\x69\xd2\x69\xc3\xa8\xda\x38\xb6\x6b\xd7\xb0\x9f\xce\x53\xc6\xc4\xf8\x5b\xc2\x83\x77\xfb\x44\xdf\x2b\xf3\x17\x86\xed\x1e\xa1\x88\xf5\x7e\xe2\x6c\x50\x5c\x78\x77\x6c\x2f\x9f\xde\x1e\x24\x21\x6e\x1f\x35\x5f\x5a\x19\x59\x53\x6f\x0c\x5e\x1f\x7c\xf8\xf8\xf2\x2d\xac\xa0\xfd\xc3\xb7\x6f\x5f\xee\x7f\x3c\x38\x7c\x4b\xde\xbf\xfc\xf0\xee\xf0\xed\x87\x97\x1f\x4a\x5b\x0d\x83\x22\xc0\xcd\xb2\x6f\xbc\x39\x0d\x1e\x0a\x33\xc2\x79\x70\x31\x4e\xe7\x8b\x98\x5e\x44\xc5\xe5\x88\x3c\x01\xca\xb2\x7a\x08\xba\x50\x65\x87\xc0\xaa\xd2\xfb\x4d\xd7\x13\x2e\x47\xd8\x1c\x7c\x31\xe3\x72\xc3\xc1\x2f\xb4\x6d\x27\x44\x77\x78\xbc\x72\xe0\x2f\x21\x39\x9f\x45\xe3\x19\x99\x07\xc5\x78\x26\xc4\x57\xbe\x09\x31\x86\x16\x1a\xe5\x3c\xd1\x29\xa0\x69\x7f\xe0\x6e\xb8\x8e\x72\x7a\x0b\x16\x08\xfe\xb0\xba\xd1\xa4\xf3\xc9\x4f\xc8\xc7\xf0\x36\x2e\x0a\x4f\x5c\x67\xfb\xaa\x30\x1b\xab\x00\xdb\x71\xa0\xec\x10\xe8\xa5\xa1\x81\xa1\x1a\xd1\x77\xbb\xa2\x6b\x07\x8b\x93\x28\xa3\x86\x47\x00\x1b\x5d\x65\xe3\x61\x43\xf1\xb4\x5e\x01\xae\xe3\x14\x63\xd3\x16\xfd\x17\xd2\x98\x16\xb4\xaa\x06\x7b\x30\x36\x6e\xf0\x2b\xec\x9f\xd9\xae\x05\x84\x28\x08\x82\xd7\x07\xca\x1d\x6e\x2b\x95\x70\x67\x39\x24\xe5\x3e\xa4\xa3\xa2\xbf\xb6\x26\x85\x41\x93\x84\xd7\x6c\xb5\x07\xbc\xc8\x64\xc2\x9f\xe6\x79\x48\x3c\x32\x0b\x63\x8f\xae\x78\x55\xd9\x6c\xb0\x67\xc9\x6b\xff\xe0\x2e\xdb\xb5\xe7\x61\xb9\xc4\x5f\xbc\x7c\xb4\xff\xea\xe8\xed\xff\xbe\x7c\xaf\xea\x09\xe9\x78\xb6\x4c\x3e\xd3\x50\xbc\x2a\xe1\x2f\x46\xc5\x5f\x3f\xa3\x8b\x38\x18\xd3\xce\xe0\xdf\xd7\xc7\xff\x4e\xfe\x9d\x9d\x3c\xfb\xf7\x97\xc1\xb4\xd7\xbe\xbe\x7a\xf4\xe8\xea\x4b\xbb\x0b\x3e\x93\xbf\x78\xe1\xff\x7d\x22\x4b\x1c\x8b\x32\x27\xac\xd0\xb1\x2c\x75\x72\xec\x2f\x67\x97\x32\x0a\x95\x94\xd1\x6d\xa1\x96\x54\x43\xa8\x8c\xb8\xe6\x63\xd9\x6d\xc9\x49\x0d\x0c\xb8\x6b\x16\x10\x8f\xf8\xcb\x60\x00\x77\xa0\x54\xb8\xc3\x00\x4f\x1b\x50\xc1\x9a\x43\xfa\x2c\x6f\x9f\x65\x99\x2b\x57\xf8\x9d\xb1\x60\xc8\x06\xe1\xef\x5f\x0d\x51\x5d\xdd\x59\x5b\x9c\xcc\x75\x6a\xe0\xb3\x05\x83\xbe\xa3\x52\xc2\x9a\x86\x1b\xd3\xac\xb9\x8b\x4f\x77\x66\xd7\xee\x8c\x18\x3a\xf8\xfa\x55\x16\xd4\xe0\xfa\x2e\x19\xd3\x18\x22\x05\xc8\x47\x9c\x46\x99\x71\x4c\x83\x4c\x9a\x70\x59\xad\x88\x64\x6b\x41\xfb\x81\xc0\x57\x43\x21\x2b\xf2\xed\x71\x66\x79\x7b\xaf\xc3\x7f\x95\x76\x95\x02\x67\x18\xfe\xba\x47\x36\x87\xc3\x21\x79\xc8\x2f\x67\x3c\x77\xad\x5e\xc7\x0f\xf0\x6e\x0f\xb0\x23\xf1\xc5\x38\x48\x4e\x05\xbd\xf0\x90\x3b\xe2\x5d\xdf\xea\xa8\x72\x67\xcc\x22\x11\x88\x25\x25\x2c\x2b\x9d\x0e\x73\x16\xc1\xe3\xdb\x9b\x76\x7b\x96\xb6\x1e\x83\x0b\xe7\x3f\x8c\x47\xfe\x24\xb6\xd0\x20\x0c\x73\x1c\xf8\x5c\x58\x39\xb8\xd2\x18\x57\x0f\xf7\xd6\xf8\x86\x2b\x0f\x06\xe2\xac\x1d\x71\x3f\xfc\x82\xeb\xc1\x6e\x2c\x6f\x85\x54\xea\x41\xc8\x4b\x05\x59\x16\x9d\x51\xcc\x70\x83\x50\xcd\x9e\x6c\xaf\x82\xc3\x7a\xa0\x0d\x37\xfc\x7e\x9b\x52\x24\x53\xc8\xd7\xea\x11\x44\x48\x15\x5f\xc7\xc3\x13\xb5\x65\xc2\x15\x36\xef\x9b\x86\x16\x09\x66\x09\x9e\x88\x25\x3a\xef\xe6\x45\x76\x55\x6f\xaa\x24\x5e\x06\xda\x57\x0d\xcb\xba\xe5\xae\x26\xd7\x11\x5e\xa9\xe4\x7c\x46\xa5\xdf\x81\x90\x8b\xe5\x70\xfa\x02\x8d\x3b\xdb\xdf\x43\x84\x66\x41\xc4\x15\xa8\x75\xed\x3b\xd5\xd1\x7e\x92\x66\x1d\x86\x97\xcf\xf4\x92\x9f\x14\x7d\x03\x30\x9d\xc0\x74\xfc\x40\xfd\x59\x90\x1f\x9e\x27\xef\x20\xa0\x56\x71\x09\x01\x13\x2d\x2e\x50\x82\x9e\xcf\xf4\xf2\xa4\xdc\xb6\xb3\x9d\x26\xe4\xe0\xdd\x7e\xbb\x6b\x2d\x7e\x21\x5b\x54\xd4\xe9\x98\x59\xe8\x65\xb2\x8f\x7d\x10\x0a\x37\xe7\x04\x1d\x37\xa2\x9c\xe4\x45\xc4\xa3\xac\x44\x21\x22\x6a\x6c\x16\x5a\x8a\x70\xbf\x1d\x67\xa7\xfc\xb4\x24\xe5\x00\xb6\x7b\x64\x54\xf4\xa3\xc7\xa9\xc0\xec\xd5\x34\x4d\xa8\xd0\x3c\x75\xd6\x3f\xd9\x62\xff\x79\x16\x15\xe0\x2f\xc5\xe2\x46\x08\xc4\x3a\x42\x7d\x72\xcf\x50\xd2\xc5\xe0\x7a\x59\xed\x42\x81\xe4\x1d\x7a\xd5\x0b\x82\x35\x4c\x3f\x56\xbd\xf4\x03\x7a\xba\x42\x8c\x4d\x76\xc7\xe0\xdc\x2b\xa0\x48\xa2\xa9\x1e\x4b\xc4\x73\x84\xaa\x3d\x6b\xca\x5e\x86\xe8\xd9\xaf\x6f\x54\x15\x16\xcf\x37\x13\x1b\x14\x55\x63\xa9\xc1\x1c\x4a\xed\x3e\x4a\xac\x3f\xdf\x3e\x69\x99\xdd\x09\x6d\xa2\x75\x46\x71\xdc\xf1\xfc\x2b\x5d\x82\x95\xb5\x7e\x6d\xd6\x6a\x6f\xd8\xec\x76\xa3\xdd\x22\x39\x36\xcc\xee\x63\x3b\x6d\xcd\x07\xe1\xc5\x56\x5a\x90\x7c\xb9\x58\xa4\x59\x01\xba\x35\x7e\x53\xfb\x6e\x9f\x28\xad\x4a\xdb\x70\x04\x59\x4e\x98\x8d\x5f\x2a\xdc\x64\x31\xd6\x53\xd9\x4a\x14\xe6\x3d\xd6\x03\x4d\x55\x5a\xd0\x23\x87\xba\xf6\x6e\x5a\xea\xed\xc6\xd5\xe3\x6a\x0c\x3a\x4e\xda\x4b\x5e\x69\x5f\x9f\xf4\xb6\xbf\xb9\x57\xe9\xde\xab\x74\xff\x2b\x54\xba\xe2\x61\xc5\xad\x9e\x63\xef\x05\x59\x9a\x90\xff\x5d\xce\x83\xb3\x28\x27\xdf\x07\xec\xf3\x6f\x9f\xf9\x67\x7f\x4e\xbd\xea\xde\xc1\x80\x1c\x24\x51\x11\x05\x71\xf4\x2b\x25\x7f\xe7\xbd\x60\x84\x1a\x90\x1c\x2c\xb1\xa4\xc1\x0d\x0c\x94\x2d\x55\xc3\xc9\x79\x1f\xb4\xba\xb2\x98\x8c\x5e\x22\x22\x6b\x1d\x84\x23\x32\xac\xbb\x79\xe3\xd6\x1e\x6c\xf8\xb6\x5b\x5d\xaf\x99\x89\xd7\x9d\xae\x7e\x85\x26\x83\x78\x4d\x24\x42\xa1\x25\x6d\xd0\xe3\x71\xc2\xcb\x5f\xa7\xf4\x90\xaa\x67\x22\xab\x91\x59\xd2\xf7\xae\xd7\x0d\x11\x1a\x01\x6b\xcf\xe9\xfd\x60\x4d\xa0\xa7\xc4\x15\x2f\x6f\xab\x27\x1a\x33\x9c\xa6\xf2\xac\x6e\x99\x6a\x59\x36\xe9\x18\xf3\x28\xb3\xdd\xf5\x36\x0a\xa7\x15\x84\x67\xec\x8c\x2a\x67\x87\x1c\xbc\x80\x1c\xd9\x3b\x35\x69\x1b\x1b\x65\x7e\x86\xfc\xaf\x7f\xf8\x5b\x21\xa7\x1a\x9d\x2d\x9f\x07\x89\x91\xaa\x74\xf9\x2e\x88\xff\xcf\x0e\x4c\xf2\x85\x50\x73\xc3\x0b\x89\x03\x75\x78\x94\x06\x44\x7e\x53\x1d\xa5\xac\xab\x0b\xe9\xe6\x79\x99\x6d\x35\xe0\x37\xcf\x90\x68\xb0\xda\xb3\x22\x4e\xf3\x44\xeb\x32\x94\xfb\xf4\x41\x3a\x67\x01\xf4\x4c\xb5\xdd\xa7\x67\x34\xbb\xec\x48\x6f\xc8\x1f\xa2\x64\x1a\xd3\x37\x1c\xe1\x5d\x32\x22\xde\x0c\x5d\x93\x98\x56\xd5\x11\x3f\xb8\x98\x40\x75\xd0\x52\xc2\xbb\xa4\x1b\x64\x41\x24\xd3\x38\x45\x1a\xb6\x45\x22\x43\xce\xcf\xee\xee\x2e\xa7\x1a\x0c\x24\xdc\x2e\x48\x58\x76\xe6\x66\x60\xfc\x5a\xb7\xed\xab\x4e\xc8\xb0\x96\x4f\xc9\xc1\x80\xc7\x1c\x54\x49\xc2\x2b\x3b\x66\x2e\x72\x3d\x36\xf2\x27\xcf\x19\xd1\x29\xbc\x47\xab\x61\x47\xcf\x19\x50\xb9\x8b\x6f\xd1\x71\x8b\xbf\xf0\xba\x72\xce\x54\x45\x55\x52\xc0\x09\xbb\xa0\x3c\x12\x8b\xa2\x23\x79\x4f\x97\x4c\x22\x1a\x87\x96\xe9\x81\x68\xc5\xe8\xa9\xc5\x73\x70\x07\x2d\xc6\xc3\xbb\x66\x91\xa1\x4c\xb6\xa2\x3e\x48\xb2\x70\x1d\x61\x39\xec\x4d\xc2\xf6\x25\x6b\x93\xdf\x82\xc5\x99\x7a\x78\x47\x56\x14\xf5\x09\x39\x91\x89\x81\x4f\xee\xc5\xc0\x7b\x31\xf0\xaf\x2d\x06\xea\xf7\x79\x7c\xd1\xdc\xd5\x0b\xbd\xbb\xb9\xbb\x67\x20\x6f\xa4\xba\xb1\xd4\x58\x19\xce\x89\x22\x52\x8b\xb4\x42\x66\x9f\xe8\x14\x29\x5c\xae\xc9\x5c\xf6\x69\x5c\xdc\x03\xcf\xd3\xf9\x5a\x32\x18\x22\x30\xf0\xc9\x8f\x83\x21\x6a\x43\x68\x9c\x81\x4a\x70\x4f\xcf\xbe\x22\x56\x8e\xa1\x74\x05\x8d\xc1\x9b\x20\x09\xa6\x54\xbf\xce\x67\x2c\x8b\xa3\xc2\x50\x05\x48\x17\x1e\x1a\x1c\xed\xf7\x73\x03\x43\x4e\xc5\xd9\xbc\xc6\xfe\x3d\xa4\x8c\xc3\x44\x89\xe9\xdf\xd3\x12\xff\x4e\x83\x9c\xfb\x5c\x28\x8b\x44\x31\xa5\xe0\xa5\xd2\xb3\x49\x99\x9e\xe6\x6d\xc7\xa2\xb2\x4d\xb3\x3d\x20\x31\x07\x11\xa2\x8d\xd2\x58\x13\x86\x3b\x51\x14\x3e\x47\x11\x87\xb2\xe3\x93\xbe\x0c\x73\x26\xd8\xa8\x94\x3a\x37\xc7\xdc\x19\xa7\xbe\xa4\x10\xa1\x39\xc4\xb6\xab\xc6\xd9\x27\x6f\x18\x2b\x8f\x68\x2e\xa2\x63\x03\x3e\x1c\x2f\x94\x86\x67\xcf\xc6\x78\x93\x83\xba\x7a\xbb\x8c\x63\xed\x18\xa3\xc7\xa4\x48\x7a\x11\xc1\xb5\x99\x0f\x77\x7f\xcc\xf8\x43\x77\x16\x76\x87\xac\x7d\xad\xb8\x3b\x0e\x26\x1b\x45\xdb\xb1\x03\x9c\xa8\x50\x32\xe6\x41\x8c\xd4\x84\x8f\x79\xff\x6e\x5f\x44\x98\xa8\x8e\x1d\xa3\xd1\x26\x5c\xbd\x72\xc2\x03\xa4\xab\x13\xa7\x8d\x26\x0e\x7a\xc0\x20\x5d\x2c\x19\x44\xa7\x92\x3c\xe8\x40\xb5\x54\x62\x63\xdd\xc3\x5d\x4b\x28\xc8\xf7\xb8\xd1\x53\xda\x92\x21\x95\xd3\xc5\x1e\x81\xe8\xdf\x55\x21\xa4\xc8\x33\xfd\x9b\x53\x37\x14\x39\x61\xec\x00\x7d\xd6\x78\xd6\x77\xb0\xce\xf9\xbd\x8a\x9a\x8b\x31\xef\x22\x9e\x3b\xe0\xad\x3e\x2b\x9a\xee\x88\x4b\x70\xef\x89\x91\x62\x06\xe9\xc5\x28\xb4\x37\x2b\x70\x36\x03\xc7\x9e\x67\x5e\x00\x55\x95\x37\x36\x89\xc0\x85\x2f\x64\x91\x7c\x3f\x25\xe9\x70\x85\xc8\x45\x81\x5c\xb7\x8d\x90\xd0\x2c\x06\x11\x76\xc7\x2a\xf6\x11\xdb\x4b\xf2\xca\xce\x97\x85\x3c\x01\xc0\x68\x19\x60\x40\xc8\x33\x02\x0c\xa9\x63\x8a\x5f\x0b\x22\xd5\x19\xa0\x59\x2a\x51\x66\x54\xb9\x55\xc6\x2a\x0e\x07\x55\xd2\x45\x2e\xc7\xa7\x29\x6d\x9d\xfe\x82\xd1\xc5\x32\xe4\xd0\x4e\x97\x51\x1c\x02\xc2\xc4\xa0\x58\xa6\xe3\xdf\x16\x18\xfe\xc7\xc3\x17\x87\xeb\xeb\xeb\x20\xde\xb7\x73\xb2\x9c\xc6\x97\x7d\x11\x45\x8c\x1d\x08\x96\x39\xdb\x13\x0b\xd5\x4a\x82\x5c\xca\xb2\xdf\xd2\xae\x46\xdd\x90\x30\xc6\x01\x19\xea\xbd\xf5\xa6\x11\xe9\xe9\xf4\x97\x63\x96\x7d\x3c\x3c\x39\x61\x62\x17\xfe\xbc\xba\x52\x76\x9b\x36\x28\xff\xb1\x09\x65\xd8\x58\x76\xfc\x57\x45\x56\xed\x00\x49\x10\x17\x76\xd0\xab\x10\x55\x76\x8b\xaa\x2e\xd5\xb5\xd1\x29\x0f\x81\x92\xf8\x9f\x65\x11\xc7\xcf\xb7\x90\xdf\xf5\x69\x78\x15\x3f\xd0\xc4\x8a\x60\xe1\x0b\x55\x60\x9c\xd5\xa1\x2d\x53\xa2\xd4\x17\x53\xfa\x7e\xc6\x88\xc5\xa2\xcc\xeb\x3c\xa6\x79\x76\xc3\x1c\x5e\xb4\x83\x99\x99\x32\x8a\xb4\x0c\x68\xbc\xe1\x54\xcc\xee\x1a\xd5\x94\x0f\xc1\xbe\x86\x12\xa4\xc2\xb2\x9a\x7a\x7a\x96\x61\xae\x68\x52\xef\xce\x51\x72\xc8\x65\x46\xe1\x86\xf4\xfd\xbb\x7d\xe5\x81\x89\x9b\xb2\x8c\x83\x44\x09\x9b\x51\x22\x94\x2e\x7e\x5f\x4f\x99\xeb\xeb\xb1\xdf\xef\x5f\xe3\xf8\x6e\xb6\x2f\x3d\xad\xc9\x94\x45\x3d\x9c\xb4\xce\xa7\x7d\xa9\xbb\xf9\x55\x88\x50\xd2\x80\xe9\x93\x1e\xcf\x5a\x19\xa2\x45\xc9\x12\xc5\xce\x1b\x69\x03\xd3\xf4\xfa\xef\xdb\x7b\xbd\xcf\xbd\xde\xe7\xaf\xad\xf7\x11\x4a\x9f\xf0\xf4\x16\x37\x7f\x3e\xbd\x8f\xd2\xd6\x60\xc5\x0f\x67\x4e\x4a\xa3\xf3\xe2\xb9\xc1\x47\xd8\x30\x4c\x97\x1f\x8e\xa6\x02\x46\x6a\x25\xef\x54\x04\x0a\x5b\xd3\xf2\x52\xde\xf1\xd8\xf4\x8b\x0b\x2e\xf2\x85\x58\xd2\x95\x25\x07\x75\x58\xcd\x68\x67\x11\x40\x8e\xda\xa5\xe3\xeb\xa0\xa5\x6f\xd6\xbb\x7c\x79\xc0\xa2\xc5\xb2\x50\x8f\xd7\x12\x7a\x2e\xb0\xd9\xd1\xdb\x25\x13\x3a\x46\xa4\xad\xe0\xac\x38\x1a\x23\xd2\x0e\x4f\x3f\xf9\x72\xa5\x98\xb8\xad\xfa\xa4\x1a\x9d\xd2\x66\x8d\x2a\x38\x6f\xa3\xbe\x5c\xd9\xe8\x96\xdb\xe8\x62\x59\xbc\xa2\x17\xf5\xc3\x7c\x45\x2f\xca\xc6\x68\x66\x55\x0f\xb0\xbe\x2d\x0e\x54\x36\x34\x7f\x5b\xd6\xb8\xc4\x66\x74\xac\xe1\xe4\x44\xf4\x34\x92\x7b\x62\xe8\x3d\xd1\x2d\x00\x3e\x29\xd9\xb9\x5e\x3c\xd7\xbb\x16\xa7\x9d\xd6\x68\x1b\xb6\xa8\xa7\xf7\x5b\xd4\xfd\x16\xf5\xd7\xde\xa2\xf4\xd5\x04\x2d\x66\x37\xba\x97\x10\xc0\x77\xfb\x2a\xb1\x24\xfa\xbf\x2f\xfc\xbf\xef\x12\xc4\x7f\x0f\x52\xb3\x6d\x32\x10\x69\x8e\x6c\x01\x2d\x44\xb2\x04\x1b\x97\xb5\x37\x4e\x93\x49\x34\x95\x60\x28\x14\x0e\x86\x96\x91\x55\x24\xd8\xb9\x78\xb6\x66\x5c\xd0\x88\x44\x09\xf3\x23\x0f\x05\x6e\x21\x03\x12\x25\xc8\x41\xfe\xe1\x32\x19\xf3\x2d\x06\x43\xe5\x3c\x55\x82\x31\x56\x9c\x51\x1b\x48\xa4\xaa\xba\xb8\x83\x22\x0c\x11\x9d\x06\x89\xcc\xe6\x5e\x0f\x9d\xfe\xc8\x64\x25\x84\x80\xcf\xb4\x26\x77\x06\x4a\xe7\x2d\xde\x08\x82\x12\x70\x78\xd2\x25\x0f\x1e\x10\xf1\xbb\x0f\x3a\xc1\xc3\x49\xa7\x3d\xbc\x68\x73\xd7\x25\xc3\x2e\x79\x46\x5a\xb4\x98\xb1\xdd\x03\x02\x93\x3e\xbf\x7c\x15\xe4\xb3\x16\x19\xd9\xc9\x5c\xa3\xdb\xd2\x52\x02\x74\xed\x43\x34\x4d\x68\x96\x57\xf4\xf0\x8e\xfb\x27\x1a\x2c\xe9\xa6\xca\x75\x7a\x9b\x17\xc1\x67\x9a\xbd\x3f\x3c\xa8\xef\xea\xcd\x7a\x8a\x3a\xfa\x41\xb6\xf5\x26\xc8\x0b\x9a\x25\x69\x48\x71\x4f\x55\xb6\x8d\xcc\x1f\xa3\x24\x88\xa3\xe2\xf2\xb7\xc3\xa6\x6c\xb1\x04\x9d\x3a\xdb\xc1\x27\x8a\xfe\xf5\x63\x96\xce\x9f\xff\x06\x74\xda\x16\x5d\x43\x41\xa5\x9e\x5f\x42\xc3\xac\xf3\x7b\x49\x78\xc0\xca\xa9\x58\x6e\x5e\x48\x3e\x0e\x05\xab\xc7\xb3\x4c\xc6\x31\xfd\x8d\x06\x70\xc4\xda\xaa\xe9\x3a\x86\x29\xed\xb4\x9c\x27\x34\xce\xfd\x74\x99\x34\xba\x64\xbc\x83\x71\x78\xdb\xe6\xa4\x84\x87\x52\x02\xc6\x47\xe5\x4c\xc1\x6f\xd8\xff\x23\xd5\x20\x9a\x0c\x67\x12\x30\x80\xd1\x67\xd5\xbd\x97\xc5\xec\xae\x8f\x87\x8d\x8f\x86\x77\x74\x32\x84\xf0\xcf\xe5\x27\x43\xae\xf8\xe2\x7b\x78\x44\xbd\x3d\x5a\xe0\xce\x2c\x6a\xfa\xb1\xb8\x41\x17\x90\x85\x03\xdf\x5b\xb9\xf7\x13\x82\xfd\xb3\x1f\x3c\xdf\x7b\x6b\x85\xa2\x13\x3b\x2a\xd7\xc9\xf1\xe7\xd3\x42\x33\x77\xbd\xb6\xc6\x7b\xd7\xe7\x76\x71\xea\x25\xd5\xcb\x62\xa6\x75\x81\x3d\xd2\xc6\x81\xbb\xdb\x3d\x31\xcc\x29\x2d\x46\x25\x1a\x6f\xe9\xa9\xb6\x8f\x0b\x8a\x91\xf4\x84\x96\xd6\x28\x7c\x16\xc4\x46\x8c\xb9\xbe\x15\x36\xfd\x2c\x88\x1d\x57\x34\x2a\xed\x7a\x0d\xd0\xb3\xd2\x50\x84\x97\xc7\x9b\x0c\x46\x14\xbd\xc9\x70\x44\xd1\x86\x03\x6a\xa2\x89\x60\xdc\x25\x88\xc1\x6e\xb7\xf6\xdc\x2c\x00\xdd\xb3\xb3\x64\x53\x4e\xbe\x3a\x40\x23\x5b\x5e\xe3\x02\x77\x44\x8e\xb5\x38\xcd\x2f\x77\x85\x13\xd5\x1f\xf5\x5d\xae\x0d\x81\xe3\xde\x73\x7e\xa2\x80\x51\xe0\x50\xeb\x16\x73\x84\xab\xe1\x79\xca\x63\x91\x02\x2a\x51\x9a\xa4\x59\x30\xa5\x7b\x45\x13\xbd\x89\x00\x2d\xc5\x91\x0f\x42\xa9\x34\x2a\xb0\xc4\xd7\x1d\xe7\xd8\x45\x0a\x7a\x85\x55\xd0\xe2\x1d\x98\x70\xed\x59\x33\x26\x06\x55\x3a\x1c\x2b\xf3\xb7\x9f\x6f\xef\xc0\xe4\xaa\xaf\xa3\x67\xce\x8e\xac\xa1\xa9\x03\xc3\xed\x86\xe5\xeb\x6d\xcf\x59\xe2\xda\xfa\x99\x2d\x5e\x72\xbd\x1a\xfd\x82\x84\x59\x49\xbb\x58\xa6\xf7\x23\xc4\x42\x87\x80\x55\x58\x41\x38\x41\x27\x15\x3b\xf2\xc6\xa6\x4c\xb8\x11\x5a\xd4\xa0\x1b\x0e\x59\x74\xa4\x6e\xd5\x8a\x33\x42\x93\x55\x2b\x40\x1d\x5a\x30\xce\x3c\x2e\x3d\x6c\x56\xd1\x83\xdc\xd6\x2d\x5e\x4e\x0c\x7e\x8d\xad\x50\x4a\xd6\x03\xaf\x60\x64\x3e\xf0\x6f\x4a\x28\xf8\x52\xed\x3d\x0d\xe2\x72\x22\x91\x27\x95\x46\x54\x22\x81\x7d\x64\x82\xcf\x60\xbf\x03\x9d\xe0\x11\x1f\x24\x85\x77\xc0\x20\x95\xd6\xd3\x05\x80\x39\x34\xa1\xce\x39\x5f\x83\x3f\x20\x06\x7f\x0b\x56\xd0\x5b\x2b\xe1\xf7\xf3\x45\x14\x97\x72\x02\x93\xe9\x0b\xd0\x0a\xce\xef\x42\x48\x3c\x0c\xcb\xc9\xcc\x3e\xc5\x34\xe4\xd2\x76\x31\xa7\x5b\x55\x07\xb9\x15\x17\xee\x2a\x94\xe8\x99\x1b\x39\x85\x2f\xe8\x38\x9a\x57\xad\x38\x7d\x36\x6a\x88\x04\x5d\xa0\x84\x28\xff\xb8\x03\x36\x8f\x14\x35\x83\x2d\x8f\x1f\x5f\xa2\x98\x80\x53\x67\xe5\xa0\xeb\x57\x10\xaa\xb0\x7a\x63\xf9\xe8\xd1\xdb\xac\x34\x26\x55\xca\x19\x5c\x99\x4a\xe8\x8f\xc4\x69\x6e\x82\xa7\xf7\x74\x4c\xa3\x45\x03\x32\x77\xcb\x34\x21\x00\x17\xf4\xb6\x14\x20\x6a\x6c\x3c\xc0\x86\xab\xb8\x96\x8b\x79\x06\x67\x03\x36\xa1\x00\x7e\x58\xb8\xa3\x63\x53\xd9\xf2\x26\xf2\xfe\x5d\xda\xaf\xbd\x0f\xce\x9b\x2f\x73\xb7\x80\x1f\x19\x95\x70\x4d\xb8\x1b\xc3\x85\xe7\x94\xe0\x86\xdc\xaf\xeb\x6d\xa3\xae\xde\xbc\x9f\xf6\x6c\xf9\xd6\x99\x6f\x1c\xd1\x34\x59\x61\x1c\x26\x74\xc9\x38\x4a\x81\xbe\xf2\x38\x1a\x74\xbe\xbc\xc7\x77\x7e\x0a\x2d\x21\x1c\x61\xf4\x5a\xd5\x51\x06\xe2\xef\xa8\x95\x73\x93\x8e\xb2\xfd\xe0\xce\xce\xca\x34\x2f\xa2\x79\x50\xd0\x9f\x82\x3a\x99\x10\x41\xfa\x87\xe6\x07\xb8\x09\xc5\x18\x23\xbc\x95\xe0\x31\xe6\x32\xea\x87\x34\x8e\xc2\xd2\xa3\x8d\x9e\x36\x0e\xdd\xcf\x05\x78\xc9\x14\x9a\x75\xfa\xc6\x5a\xda\x91\xd7\xaf\x5f\x37\xec\x43\x5c\x4a\x41\xaa\xa6\x95\x5a\xfe\x40\xb3\x05\xad\xdd\xa2\x14\x06\x38\x74\x35\x02\x1c\x98\x8a\x5e\xe4\xcb\xd3\x79\x54\xfc\x9c\x66\x75\x92\x92\x06\x2c\x59\xe9\xbe\xfc\x6a\x93\xa0\x06\xad\x0a\xa8\xd2\xed\xb8\xa4\x3d\xff\x31\x67\x3f\x48\x42\xfe\xec\xbd\x08\x8a\x65\xad\xd6\xc5\x02\xb7\x4e\xd4\xea\xb4\x55\x02\xe5\x70\x90\x3b\x50\xb7\xbd\x5c\xa4\xe3\x59\x29\xef\xf0\x8d\xb4\xc9\x81\x52\xc1\x96\x9f\x28\x7d\x20\x37\x61\x20\xe5\x03\x70\x82\xc3\x6a\x1b\x2e\xad\xbf\xee\xe1\x34\xa5\xaf\x35\x52\xf7\xd3\xd0\x04\x03\x7e\xeb\xa6\x08\x4d\x95\x55\xa3\xd0\x29\xb9\xe0\x52\x75\x62\xb6\xa5\xd0\xe2\x4f\xe6\x14\x61\xe4\xc1\x41\xd1\xea\xb0\x38\xad\xbb\x8d\xda\xb2\xa6\x0b\xa1\xcf\x9d\x46\x1e\x2a\x58\x96\xae\x4e\x31\x65\x00\x42\xd0\x2e\xcb\xb6\x1a\x35\x5f\xc4\xa0\x1d\x47\x27\xba\xf2\x22\xca\x33\x05\x1b\xb3\x50\x69\x09\xd4\xbc\xc9\xfa\x9d\x8c\xd7\xaf\x5f\xbb\xc0\xff\x1f\x7b\xef\xbe\xd6\x46\xae\x2c\x8e\xfe\x9d\x3c\x85\x26\xbf\xb3\x06\x3b\x34\xc6\x77\x88\x13\x66\x6f\x62\x20\xb0\x12\x02\x3f\x20\x33\xb3\x36\x1f\xc3\xd7\xb6\x65\xdc\x89\xdd\xed\xd5\xdd\xe6\x32\x13\xf6\xfb\x9c\xe7\x38\x2f\x76\x3e\x95\x2e\xad\x6b\xbb\xcd\x25\x93\x99\x05\x6b\xef\x89\xbb\x5b\x2a\x95\xa4\x52\xa9\x54\xaa\x0b\x65\x93\x12\x48\xc1\xc1\x94\x4e\x93\x17\xf0\xcc\x4c\x04\x69\x52\x53\x71\x5f\x98\x97\xe4\x20\xa3\x61\xb2\x06\xc5\xc5\x95\xaa\x72\x14\x1c\x3e\x08\x7b\x7e\x22\x6b\xb5\x18\x02\xb0\x94\x18\x83\x67\x65\x44\x91\xdb\xb2\xb7\x40\x1b\x93\x20\x54\xcd\x43\x8d\x16\x58\x89\x3b\xc2\x1f\xf9\xc9\x28\xf6\xd3\xdc\x3e\x38\xca\x14\x12\x21\x16\xc7\x88\x9b\x37\xe5\x20\x64\x2f\x32\xff\x50\xca\xec\xa9\xd4\x93\xe8\xe2\x18\x5e\xf8\xc9\x61\x1c\xf4\x73\xc7\xcc\x51\xe6\xce\xf7\x68\x8b\x63\xc9\xf2\xf6\x25\x79\x58\x8a\x32\x77\x6c\xa3\x27\x59\x21\xe4\x34\xe3\x2e\xf6\x48\x34\xc4\x93\x1a\xfd\x4c\x8d\x55\xf3\x70\xd3\x8b\x4a\x2d\xca\x2c\x44\xb9\xb9\xae\xf4\x33\x43\x40\xc9\x2a\xa4\x17\x28\x2e\x7f\x7e\x3f\x8d\x62\x2e\x27\x73\xd3\x41\xf0\xc3\xf1\x10\x29\xab\xec\x9d\xac\xb4\xad\xb1\x21\x37\x15\x34\x62\x39\x78\x92\xbf\x3a\x2d\xd5\x8d\x31\x98\xfa\x82\xf7\x75\x57\xf3\x47\x93\x12\xfd\x51\xf3\xc3\x0c\x0e\x19\x8a\x25\xcf\x6a\x2a\xe2\x71\x7b\xc6\x0a\x4e\x47\xa5\xb2\x67\x92\xec\x87\xe8\x42\x92\x80\x8a\xa1\x64\xeb\x68\x66\xc9\x98\x9f\x83\x86\x8f\x7e\x89\x55\xa8\x5c\x8c\xa3\x9e\x3f\xae\x90\x41\xad\xf8\xe6\x6b\x96\x34\xd4\xd6\x64\xd0\xf7\xa7\x1f\xef\xda\x2c\xa9\x6c\x34\x4a\x5f\xe6\x35\x29\x99\x75\x66\x0d\xea\xbe\x83\x72\x52\x46\x5e\xa1\x64\x9f\x9e\x79\xe1\x1c\xb7\xd3\x51\x66\x10\xaf\x59\xb6\xbe\xe8\xd4\xd6\xbd\x17\x86\x85\x2d\xf3\xf0\xca\x4c\x5b\x5f\x74\xea\x2d\x78\x41\xe7\xf4\x45\xa7\xfe\x8a\x3e\x0a\x5a\x78\xd1\x69\xd0\x2a\x41\xcf\x0f\x5f\x74\x1a\x0d\x4f\xb5\xbf\x87\x47\x36\x48\x2f\x3a\xcd\x26\x3c\x73\x3b\xdc\x17\x9d\x26\x05\xcf\x38\xfb\x8b\x4e\x93\xa2\xc5\xed\x65\x5e\x74\x9a\xa4\x41\x6e\x45\xfb\xa2\xd3\x6c\xdc\x9e\x79\x8d\x57\x4f\x06\xfd\x4f\x06\xfd\x7f\x6f\x83\x7e\x97\x35\xff\xbd\x9d\xce\x8a\xdb\xd9\x17\x30\xa2\x87\x72\x1f\x71\xfa\x98\x3e\x6a\xf0\x76\xbe\xd5\x5f\xe6\x9d\x76\x17\xb3\xbf\x02\x3e\x69\xab\xab\xab\x59\x50\x37\x5b\xa0\x38\x96\xf1\x98\xb0\x78\x00\x87\xd3\x11\xf2\xa7\x81\x84\xfb\x23\x1d\x48\xc6\x41\x92\xe2\xbc\xf3\x42\x88\xd3\xf3\xac\xd0\x5d\x85\x2b\x8c\x63\xfd\x22\xc5\x68\xc5\x55\x68\x01\x81\x4f\x16\xbf\x8c\x4d\xed\x23\x4e\x2d\x9b\x9a\xba\x79\xc9\xbb\xcb\xed\x99\xd7\xac\x3e\xed\x16\x4f\xbb\xc5\xdf\x7b\xb7\xf8\x4e\xdd\xbf\x1e\xce\x53\xab\xa0\x23\x59\x66\x0d\x7f\x88\xe3\x24\x0a\xfd\xf1\x93\x49\xfc\x63\x9b\xc4\xdf\x16\x33\x92\x0e\xf1\x55\x66\x79\x9d\xa7\xe8\xce\x0a\x9a\x5a\xee\x29\x9b\xd5\x73\x6b\xa1\x7b\x5c\x65\x07\x13\xb2\x11\x1c\xf9\x57\xef\xf1\xbc\x2b\x2e\xb9\xe8\x92\xf7\xfc\xd9\x33\x1d\x37\xa3\x40\x8e\x6b\x77\xf1\x2b\x5b\xb3\x1d\xf1\x41\xb2\x7d\x7e\xf6\xac\xa0\x21\x43\xe1\xbb\x5a\xdc\x3f\xc2\xfd\xe8\x92\x46\x57\xcc\xbb\xdc\xe4\xe5\xac\xb8\xaa\x5f\x73\x06\x64\x16\x8e\xa3\xfe\x97\x62\x94\xa2\x94\xcd\x21\x16\x57\xb9\x22\x36\xe3\xc5\xc6\xcd\x39\x7a\x0f\x6c\x22\x91\xcd\xfd\x5c\x3b\x89\x45\xee\xc3\x6d\xf6\x05\xce\x2e\x15\x9f\x9f\x62\xb3\x93\x3f\x37\x8b\xdc\x59\xe9\x73\xa3\x21\x6f\x93\xac\x59\xc3\x52\x23\xd2\xe2\xcd\xde\x2a\x14\x24\xdd\x9e\x70\xaa\x76\xdd\x76\x38\x2f\x45\x24\x70\xb2\xbc\xfb\x78\xe7\x83\xcd\x39\x6a\xe1\x6c\x3a\xe4\xc2\x0e\xb1\xdc\x94\xcb\xf9\x76\x9b\x09\xe7\x16\x15\x91\xa6\x15\xd2\xe5\xf4\xda\x93\x9c\xfe\x24\xa7\xff\xbd\xe5\x74\x26\xa4\x27\x23\x87\x56\x67\x8e\xf8\x8d\x63\x3c\x9b\x10\xd0\x3f\xcd\x51\x02\xf5\xa3\x18\x57\x82\x48\x95\xd3\xd7\x0a\x47\x1e\x2a\x18\xa9\x60\x5e\xc0\x03\x28\x74\x3c\x1a\x3d\xba\x76\xe8\xfb\x91\xc7\x09\x77\x3c\x1e\x29\xb7\x1b\xf8\x8a\x65\x6d\xd8\xf9\x16\x17\x3a\xc9\x68\xfe\x85\x4e\x32\x82\x0b\x1d\x2a\xb8\x2c\x72\x6f\x93\x27\xe7\xbb\x37\x27\x43\x3c\x90\xb6\xa6\x4b\xeb\x4d\x1d\x13\x11\x92\xd1\xe8\xdc\x5e\x40\xb5\x1e\x42\x16\x5d\x56\x5e\xa3\x41\x38\x8c\xdc\x2d\x5a\xbe\xde\xaf\xb9\x04\xa7\xfb\xfe\x35\x23\x82\xe3\xe0\x77\xfd\x72\x58\x6a\x7b\x5e\x51\xd5\x3c\xec\x2e\x88\x04\xe1\x61\xf4\x4b\x3e\x02\xb6\x22\xf7\x6b\x78\xe2\xc7\x5f\x4e\xe2\x59\x92\xe2\xc1\x21\x36\x2e\x83\xa5\xe6\xf3\x0b\xde\x0f\x89\x10\x13\x99\xee\xd0\x0f\x72\xda\x77\x96\xb9\x1f\x05\xf8\x83\xc1\x61\x1c\x5c\xfa\x29\xa6\x47\x42\x47\xeb\x79\xc5\xee\xd7\x77\x9a\x35\x73\x6e\xf7\xf3\x8a\xdd\x0f\x81\x91\x9f\xcc\x6d\xdd\x59\xe6\x7e\x4d\x5f\xe0\x94\x6e\xe8\xb9\x63\x9f\x53\xea\xfe\xcd\x17\x98\xfb\xbc\x62\xf7\xa6\xfb\xe3\x9b\x49\x6e\xe3\xae\x22\xf7\xa6\xfa\x79\x0d\xbb\x8a\xdc\x77\xc8\x89\x1c\x97\x62\x0a\x7a\x27\x8e\x26\x87\x7e\x92\x5c\x45\xf1\x20\x6f\xfc\x0b\xd6\xb9\xf7\x3a\x98\x37\x26\xae\x22\xf7\x26\xc3\x79\x0d\xbb\x8a\x3c\x04\xeb\x99\xd7\x76\x4e\x29\x7b\xf3\xe2\x61\x75\x15\x25\xb3\x1e\xdc\xbc\x61\x48\xca\x34\x0b\xb3\xe7\x49\x90\x24\x41\x78\xf1\xbc\x30\xb6\xd3\x28\xd1\xaf\xae\x24\x2c\x2d\x5f\x2d\x7a\x0a\x94\xaf\x77\x44\xf3\x6f\xb9\x8e\x47\x23\x29\x03\xa7\x66\x7b\xa1\x9c\xa2\x35\xcb\x88\x66\xfd\xe9\x0c\xfd\x74\x86\xfe\x7b\x9f\xa1\xb3\xbb\xae\xde\xef\xbf\x6b\x77\x5d\x9b\x63\x7c\x8d\xde\xe2\x18\x5f\x24\xbf\xfb\xc9\xef\x01\x7a\xe3\x8f\xf1\xf5\x7f\xc7\xe9\x30\xa9\x8c\x66\xea\x71\xb8\xcd\xc2\x81\x1f\xe1\x21\x8e\x71\xd8\xc7\x1d\x44\xda\x4f\x3a\xab\xab\x17\x41\x3a\x9a\xf5\x2a\xfd\x68\xb2\xfa\x6b\x10\xee\x04\xe1\x41\x7c\xb1\xfa\xeb\xd6\x61\x74\xdc\x1d\xf9\x41\xb8\xda\x1b\x47\xbd\xd5\xe4\xca\x8f\x27\xab\x41\x98\xe2\x38\xf4\xc7\xab\xa4\x4b\xf8\x3a\xe5\xff\x56\x2e\xa2\xff\xf3\xa1\xd1\x78\xe4\xab\xb1\xec\xbe\xeb\x98\x60\xf3\x37\x3f\x5c\xc3\x8f\xbf\xc4\x65\x17\xb5\x7c\xc5\xe9\x55\x14\x7f\x39\xc2\x10\xeb\x3d\x4f\x51\xae\x17\x37\xb5\xe5\xbd\xdf\x7f\x3f\xcf\x29\x75\x1f\x27\xce\x9b\xb0\xbf\x1d\xfa\xbd\x31\x9e\x87\xa5\x54\xd2\x8e\xa0\xbd\xc0\x7d\x70\xbb\xf2\xa7\x05\x71\xcb\x4a\x3a\x70\xb3\x16\xb8\x07\x6e\x83\xe8\x2a\x64\x61\xfc\xf3\x10\xe3\xc5\xec\x58\x59\xbe\x16\xf7\x4d\x76\x20\x36\x9b\x16\x40\x8b\x16\xb2\x23\x65\x7c\xbb\x37\x4a\x31\x4e\xe3\x00\x5f\xce\x0b\x17\xc2\x8b\xd9\xd1\xb2\x7c\xbd\x0f\x69\xa5\x64\xb7\x9b\x43\x54\xa4\x8c\x83\x9c\xb4\x4f\xf7\x1e\xa2\x0b\x5c\xc0\xf7\xdd\x8e\x8b\xfa\xe1\x1e\x63\x42\xd3\x1f\xcd\x09\x32\x6e\xc7\x41\xfd\x70\xef\xd1\x60\x19\xcf\xf2\x91\xa1\x85\xec\xf8\x18\xdf\x38\x4a\xcd\x42\x28\xe5\xdc\xea\x1a\x2a\x4e\x9d\x2d\x4b\xb7\x7f\x19\x3f\x94\x5e\x66\x8c\x28\x7b\xc9\xf9\x80\x74\xe3\x38\x55\x9f\x39\xf5\x4b\x80\x08\x09\x66\x8f\x17\x58\xba\x98\x9c\xce\xa4\x07\x49\x16\x7f\xd4\x6b\xc6\x51\x70\xe9\xf4\x8d\x21\x73\x02\xdf\x9d\x67\xc8\x7c\xd8\x16\xa5\xac\x02\x1b\xbe\x3b\x8e\x57\x96\xf3\x15\x11\x96\x6c\xd1\xe2\xad\xf7\x92\x8d\xa7\x33\xd5\xd3\x99\xea\xef\x7d\xa6\x62\x07\x2a\x7e\x41\xf4\x6d\xd3\x9c\xdc\xc5\xb0\x9a\x7b\x47\xf9\xd3\x80\x0b\xe3\x34\x47\x6e\x3a\xca\xb3\x40\xa3\xd7\x65\xb9\x81\x7d\x79\xe9\xf4\x66\x4a\xe4\x03\x16\xc4\xf7\xf5\x73\x89\x81\x07\x69\x7f\x54\x22\xdf\xf5\x98\x74\x7d\x3f\xc1\x68\x89\x50\x7c\x92\x2e\x75\x94\x4f\x30\x59\xf1\x45\x52\x49\x46\xc1\x30\x2d\x69\x19\xb9\x90\x91\x5d\xb7\x6a\x16\x60\x2c\x19\xdc\xd7\x42\x7c\xc5\xbc\x9d\xe1\x42\xf6\xb5\x05\x8d\x29\x0e\x07\x41\x78\xf1\xe8\x78\x1c\xd2\x76\x64\x1b\x22\x1b\x52\x2c\xfa\xaa\x89\x8d\x06\xce\xa8\x4c\x33\x94\xdd\x4a\xd2\x81\x28\x35\xdf\x92\x90\x41\xd3\x65\x04\x85\x14\x2c\xb2\x93\x45\xaa\x0e\x83\x30\x49\xfd\xf1\xb8\x50\xcb\x5a\x69\xbb\xbb\xbe\xbb\x50\x0e\x1e\x17\x38\xfd\x10\x5d\x14\x08\x16\x40\x4a\x39\xc3\x04\xd0\x16\xb5\x22\x39\xad\x4e\xa3\xb9\x01\x5b\x48\x91\x39\xed\x75\x47\x7e\x78\x61\x8f\x4c\x30\x47\xc6\x12\xf3\x25\x9b\x64\x29\xa3\xa7\x08\x42\xa4\x63\x52\x23\x11\x0b\xfa\x78\x76\x47\x47\x8e\x64\x34\xaa\x00\x6b\x34\xd8\x4d\x32\x32\xd9\x8d\x5b\x7c\x9a\x73\x4b\x63\x90\x01\x32\x6e\x69\x14\x4b\x82\x07\x55\xd3\xbb\x89\x11\xd9\x34\xf5\x8f\x87\x88\x49\xba\xc8\xb8\xa6\xa0\xcd\x32\x1c\xf4\xa2\xf7\x6b\x5e\x23\xe3\x07\x68\x5b\x26\x3d\x43\x12\xa5\x38\xe0\x74\xd4\x21\xff\xa1\xc0\x92\xd1\xa8\x43\xfe\xe3\x51\xe9\xd5\x96\xd2\xa8\xd9\x7c\x92\x49\x9f\x64\xd2\xbf\xb9\x4c\x9a\x29\xfa\xb9\x93\xf5\x5d\x1c\x5b\x2c\x02\x29\x75\x10\x3f\xc2\x17\x64\x9e\xfd\x78\xb3\x17\x38\x32\xfb\x24\xab\xef\xd4\xa2\x95\xcf\x49\x24\xb2\xe7\x04\x7d\x7f\x2a\x03\x71\xc1\xd8\xeb\x6e\x1e\x9a\x10\x24\x4c\x98\x27\x3a\x33\x5f\x46\x1b\x68\xa9\x7a\xdd\x6f\x0f\x5e\x0d\xea\xfd\x41\xb3\xf9\xca\x5f\x6b\x35\xfb\xcd\x57\xcd\x7a\xbb\x89\x6b\xeb\xd5\x57\xfd\x56\x15\x37\x9a\x83\x76\xb3\xd5\xae\xf7\x96\x32\x5c\x6c\x60\xfc\x9a\x5f\xab\xd5\x7a\xfd\xea\x5a\xb3\xff\xaa\x3f\xf4\xd7\xd6\x6b\xc3\x6a\xbf\xb1\x8e\xdb\x8d\xde\xa0\x55\xeb\xbf\xaa\xf5\xd6\xfd\x61\xb5\xba\xe4\x66\x4e\x14\xc7\x8e\x24\xea\xfa\xbd\xa0\x63\x19\xc4\x8c\x15\x32\x3f\xf8\x8e\xb5\x7f\x74\xab\xa7\x85\x09\xda\x06\x64\x7d\x5c\x2d\x70\xcd\xee\x52\xa8\x0a\xc7\xcc\x9f\xc5\x17\x9d\x9a\xf7\x62\xce\x3c\xbd\xe8\xd4\x09\xb3\x6d\x3d\x31\xdb\x27\x66\xfb\xf7\x66\xb6\x19\xaf\xe5\xda\x2f\x8d\xd9\xe6\x59\x26\x0f\xe3\xe8\x77\x3c\xf1\xc3\xca\x00\xff\xf4\x20\x0c\xda\xe6\xa3\xae\x39\xa8\xeb\x37\xa4\x86\x4d\xad\x72\x0d\xca\x32\xa4\xb3\x4f\xf0\x28\xe5\x6c\xa1\x9a\x44\xe9\x3b\x7d\xa1\x64\x75\xd1\x4a\x24\x7a\x09\xcd\xc1\x59\x2a\xaa\x7d\x91\xea\xa8\x0a\x68\xa9\x8a\xfa\x41\xaa\x61\xdc\xe6\x86\xb3\xf1\x98\x8a\x96\x7c\x2c\xe4\xfc\xd1\xfa\xed\xa6\x32\x4e\xf1\x44\x19\x22\x03\x74\x3c\x99\x9b\x8e\x9b\x25\x9f\x06\x74\x41\xab\x40\x68\x97\x0a\xaa\x5a\xae\x6d\x29\xaf\xbf\x9c\x6f\x1b\x32\x5e\xdf\x2a\xa9\xb6\xc5\xab\x55\x5b\x97\x24\x38\xba\x06\xc7\x16\xbb\x45\x1b\xe1\xff\xb2\xbd\xa5\x75\x3b\x04\xff\xa2\x1d\x8e\x94\xec\xea\x73\x3a\x4d\xc3\xe8\xcb\xbd\x66\xd9\xc4\x6d\x19\xc6\xf3\xfb\x4d\x41\xa9\xb3\xa8\x64\x89\x97\xfb\xae\x53\xe4\x8f\x3f\xb2\x94\xf2\xe8\x87\x0d\x4a\x38\xda\x2b\xb2\xa1\x0c\x83\x10\x0f\xf8\x38\x69\x10\x44\x5b\x1d\x56\xcb\x31\x5c\x90\x7b\x3d\x8d\x10\xbe\xa6\xc1\x92\xb8\x85\x39\x1a\xc6\xd1\x24\x3b\x6d\x8b\x94\xe6\x15\xb4\x4f\x36\xb6\x00\x27\x8c\x92\x60\x98\xb4\xb1\x64\xc0\xb8\x41\xba\x49\x44\x19\x3c\x65\x5c\x77\xd8\x48\x7d\xfd\x38\x1b\x8f\x6f\x25\x6b\xf7\x60\x88\xf0\x75\x90\x40\x71\xeb\x90\x6b\x2d\x3a\x15\x86\xc1\x30\xcb\x02\xc6\x5b\xa3\x79\xc0\x40\xcf\x36\xc6\xe1\x45\x3a\x42\x2b\xa8\x76\x56\xb6\x64\x34\x82\x32\xd3\x68\x5a\x2a\xbf\x46\xab\xab\xfc\xe2\x8b\xf0\x7f\x58\x4f\x30\x5a\x3f\xc8\xc2\x8d\x3a\xdc\xd4\xc0\x21\xc3\x2c\x8d\xec\xa4\xa8\x1a\x42\xb8\x88\x91\xbd\xe2\xbd\x70\x52\xa3\x0a\x4d\xe5\xbe\xbd\xcf\x4a\x92\x66\x52\x47\x88\x92\x88\x27\x79\x02\xf2\xea\xcd\x82\xf1\xe0\x1d\x4e\x4b\xd2\xf1\x1c\x87\xb3\x09\x8e\xfd\xde\x18\x77\x50\x1a\xcf\xb0\xa9\xfb\xf3\x27\x70\x63\x25\xd8\x7a\x25\x99\x8e\x83\xb4\xb4\x54\x59\x92\x02\x6b\x32\x7e\x0f\x85\x41\x7b\xcb\x27\x0a\xde\xf0\x39\xf9\x09\xd5\xe4\x19\x89\x7a\x9f\x4f\x79\x8d\x33\xc2\x8d\x95\xe7\xaf\x5f\xd1\x1f\xb7\xaf\xe5\xc2\x7a\x91\xd7\x8a\x4a\x4c\x34\x5f\x3b\xe3\x09\xa5\xe0\x1f\x7b\x86\xac\xa8\xf7\xd9\x83\xf2\x1e\x1d\x32\xd6\x17\x02\xdf\x4f\x6e\xc2\xfe\x3b\xd8\x6f\x88\xc8\x0b\x5d\x28\x9f\xf1\x21\x80\x41\xdc\x64\x45\x4a\x92\x9f\x86\x56\x4d\x99\x24\x00\xa1\xb2\x0c\xb8\x5e\x46\xcb\x80\x43\xa5\x3f\xf2\xe3\xcd\xb4\x54\x2d\x57\xd2\xe8\xd3\x74\x8a\xe3\xae\x9f\xe0\x52\x99\x7f\x4e\x88\xfc\x50\xaa\x95\x9d\x1b\x0f\x9f\x59\x77\xee\xee\x6c\xe3\xce\x12\x91\xf3\x90\x68\xbc\xc6\x05\xe9\x90\xb9\x62\x84\x80\x22\xf3\xc4\x92\x78\xab\xee\x63\x90\x8f\x4d\xd3\xf4\xd0\x25\xd1\xc9\x00\xd1\xed\x5e\x52\xd9\x70\x83\x9f\xfc\x0e\xf2\x51\x5f\xac\x97\xd9\x65\xbf\x3b\x0a\x18\xca\xec\x9c\xac\x1d\x82\x96\x17\xed\x95\x9c\x38\x09\xc7\xb1\x87\xd4\xad\x83\xff\x71\x5c\x68\x19\xfb\x60\xb3\x9a\xd2\xe5\xc1\x6d\x36\x64\x6c\x91\x73\xbc\x39\xa1\xb2\x47\x9a\x01\x8f\xe5\xbe\x93\x66\xf5\x02\xbb\xb6\x93\x6c\xf7\xed\xc7\x98\xc8\x8a\xd3\x59\x8c\xd1\x3f\x8f\x0f\x3e\x1e\x1d\x76\x11\x6f\xe5\x6a\x14\xf4\x47\x70\x78\xe2\x3b\x50\x10\xa2\x1e\x28\x6d\x59\x11\x8d\x23\x66\x6f\x05\xdf\xab\x54\x2a\xb7\x4c\x85\x67\xdb\x9b\x11\x39\x12\xc6\xd3\xbe\x54\xd5\xca\x1d\xb3\x8e\x3b\xc8\xc2\xbf\x61\x16\x3a\xba\xdd\x5c\x47\x96\x47\x4d\x2d\xf9\xe9\x99\xaa\x5f\x27\xd3\xc4\xaa\x68\x9b\x55\x09\xf6\x44\x59\x14\x24\x4b\xb6\x42\x2a\x95\xc4\x3e\x59\x2e\xcb\x53\xc6\xb0\x62\x13\xcd\x67\x4d\x9e\x76\xd7\xd4\xb1\x9a\x0e\x0d\x27\x1f\x20\xe9\x60\xae\x05\xed\x21\x47\xec\xf6\xd3\x11\xfb\xe9\x88\xfd\xf7\x3e\x62\x4b\xfa\x4c\xc6\x21\x26\x8c\xa5\xab\x27\xed\x7f\xe2\xe1\x30\xc6\x37\xe8\x97\x60\xdc\xff\x82\xd1\x9b\xcf\x78\x38\x74\x85\xeb\x59\x28\xb6\xcf\xbe\x1f\x93\x23\xfc\x81\x1f\xf6\xb1\x0f\x65\x6d\x51\x7d\xee\x10\x08\x88\x55\x79\xe7\x5f\xa2\x5f\xa2\x68\x80\xde\x5c\x38\x0f\xf9\xcd\xec\x90\xff\x4f\xc6\x4d\x15\xef\x61\xc6\x62\xf3\x92\xc2\x5b\x22\xd5\xe9\x79\xdc\x6d\x49\xdc\x71\x1c\x47\x5a\xf4\xa0\x55\xfa\x8e\x1a\x21\xd0\x6d\x67\x2f\x5d\x4a\xc8\xc6\x38\x8d\xc2\x24\xe8\x8d\x29\x81\x4d\x7d\xf0\x22\x41\x13\x76\xe9\x43\xf6\xa2\x69\x1c\x5d\x06\x03\x1c\x27\xa2\x96\x3f\x4e\x22\xb3\x6a\x34\x1e\x93\xaa\x84\xda\xb8\x03\x37\x0a\xa3\x01\xfd\x1a\x84\xfd\x68\x22\x43\x26\xc0\x58\xf6\x09\x7a\xe7\x9a\x06\x13\x4c\x16\x5b\x90\xa0\x1a\x4a\x70\x3f\x0a\x07\xb0\x3b\x06\xe1\xc5\x18\xa7\x51\x08\xc3\x49\xba\x97\x73\xd0\xe7\xa8\x2a\xc7\x7d\xfe\x12\x6d\x88\xae\x48\x7a\x06\xd2\x36\x68\x80\x6f\xa5\x97\x1c\x17\x59\xeb\xe0\x3c\xfc\x11\x09\x65\x14\x47\x61\x34\x4b\xc6\x37\x10\x07\xc3\xb1\x0f\x93\x4f\x96\xf3\x08\x1a\xf8\xa9\xef\x3c\x21\xab\xbd\x55\x54\x1e\xe1\x40\xe9\x3c\x01\x23\x9f\xd4\x7e\x50\x7a\xaf\x24\x88\x8d\xc2\x24\x22\x5b\x17\x21\x8a\x12\x25\x8d\xca\x5e\x78\xe9\x8f\x83\xc1\x21\x2b\x5f\x92\x65\x1e\xee\x86\x0d\x83\x21\x49\xf8\xea\x1e\xcf\xc8\xbc\x92\x46\x87\xf4\x1d\xa0\x54\xa1\xbd\xf7\xa0\x9b\xcc\xda\x42\x3a\xbf\xb0\x53\xf9\x86\x3a\x57\x54\x98\x65\xa0\xf9\x5d\x39\x74\x8a\x37\x12\x24\x3f\x13\x74\x8f\x28\x15\x62\x21\xa8\x49\xdd\x4c\x47\x71\x74\x85\xd4\xee\xe9\xe5\x95\xee\xb0\x6e\xd2\x4f\x95\x42\x27\x7f\x7f\xa1\xd9\x07\x69\x36\x97\x04\xf4\x73\xa9\x90\x7e\xe6\x13\x03\x00\x37\x28\x42\x8a\x9e\x5b\x88\x36\x78\xfa\x61\x49\x36\xce\xa3\x8e\x87\x21\x04\x73\xee\xa9\xdc\xcf\x40\x16\x90\xe7\x49\xa7\x70\x1c\x3b\x52\x67\xca\xbd\x29\xeb\xf6\x36\x88\x67\xa6\xba\x0b\x8d\xcd\x1f\x32\xa3\xb6\xdc\xbe\x21\xe4\xb2\x8c\xd9\x0a\x09\xea\xd1\x39\xdd\xc7\x06\x1b\x35\xe6\x9d\x0c\x48\x81\xb7\xe4\xbb\x45\xc9\x44\xeb\x3d\x04\x61\x42\x0b\xdf\x19\x61\x02\x4e\x32\x75\x72\x26\x73\x37\x52\x4c\x1e\x80\x16\x55\x1a\xe4\x7a\x36\x98\x8d\x12\x6f\xe5\x5e\xa4\x97\xcc\xa3\x3d\xa5\x43\x82\xe8\xd0\x9c\xed\x0f\xa7\x62\x5f\x25\xd2\x26\x3f\x13\x32\x91\xcf\xa0\xb8\x94\x4f\x95\x5d\x35\x97\x4b\x4b\xa2\xae\xba\xeb\x3b\xb7\xfb\x79\x3b\x77\x4a\x8e\x54\x4c\x70\xd1\x11\x25\xdf\x0e\xc5\xa7\xb9\x1c\x9b\x06\xff\xbf\x05\x68\x7b\x83\xb9\x4b\xc6\xf2\x55\xd8\x25\x71\x4c\xd2\x68\x10\xa1\xfe\x18\xfb\xe1\x6c\x8a\x42\x80\x4f\x06\x58\x1c\xdb\xf3\x86\x4a\xc2\xde\xb2\xf2\x28\x92\x72\x44\x14\xd1\xb8\x3a\x96\x44\x38\x3a\xa5\xa5\xcf\x88\x90\x44\xaa\x77\x10\x05\x12\x0c\x3a\x06\xa0\x8e\x0d\x64\x27\xfb\x09\x7a\x5d\xc4\x7c\x99\xd5\xd1\x57\x18\x00\x13\xc0\xd4\xdd\x9c\x21\x54\x12\x2b\x7c\xce\xe4\x46\x53\x21\x94\x12\x11\x94\xd9\xd1\xc2\xe9\xe6\x22\x20\x47\xba\x40\xd7\x1d\x93\x3a\x96\x39\x37\xe6\x36\x77\xe4\x05\x08\x95\x48\xa1\x2e\xef\x10\x35\x2d\xb3\x0c\xf2\x6b\x69\x78\x32\xfc\xd9\xe8\x94\x98\x46\xf5\x0b\xbe\x49\x4a\x59\xdd\x32\xd7\xf2\x6e\x6c\x6c\xa0\x2a\xfa\xf1\x47\xe4\x1a\x43\x42\x4c\xf1\x09\x7d\x5f\x52\x0a\xbd\x56\xc7\x59\x17\x80\x73\xc6\x3b\xdb\x7d\x62\x4c\x78\x01\x91\xff\xf9\xb0\x4f\x70\x7f\xe4\x87\x41\x32\xe1\xc7\xd0\x7c\xe6\x00\x00\xf2\x87\x97\xb6\x21\x0f\xec\x17\x8c\xa7\x22\x81\x00\xef\xec\xea\xcb\xcf\xc9\x28\x08\x49\x43\xd7\xfd\x68\x32\x1d\xe3\xeb\x20\xbd\xe9\xb4\xe0\x48\x46\x0a\x10\x82\x28\x91\xcd\xe1\x0b\xbe\xa1\x9a\x02\x31\x9a\xd2\x78\xad\xae\xa2\x18\x4f\xa2\x4b\x8c\xfc\xf1\x18\x7a\x95\x78\x08\x5f\xf7\xf1\x34\x05\xb1\x9f\xbd\x92\xcb\xa7\x23\x7c\x83\x42\x4c\x47\xa4\x87\x59\xfd\x01\xe9\xf1\xcc\x1f\x8f\x6f\x50\xef\x06\x86\x8c\x0c\x0f\xcb\x05\x00\x34\xf3\x0b\xd9\x90\x82\xf0\xa2\x54\x96\xf6\x81\xd2\x0f\x4a\xef\xd0\xd7\xaf\x04\xdf\x4a\x10\x0e\xf0\xf5\xc1\xb0\x04\x7e\x8a\x84\xd8\xce\x97\xca\x30\xf9\x2b\x35\x7d\x83\x90\x28\xec\x0b\xbe\x39\xab\x88\x95\xa8\xdb\x43\x9b\x14\x49\xca\x1b\xb6\xc9\x7f\x61\xf2\x84\x53\x26\x99\xf7\x3e\x35\xce\x45\x51\x58\x84\x27\x50\x9b\xda\x3c\x9a\x64\x26\xc3\xa6\x0a\xd4\x41\x85\xa8\x4d\xc0\x59\x3a\x93\xe0\x54\xe9\x3d\x01\x2c\xa9\x22\x3d\xd4\xaf\x6c\x9f\xec\x9e\x1f\x1e\x7c\xf8\xb0\xf7\xf1\xdd\xf9\xc9\xde\xfe\xf6\xc1\xa7\x13\xf9\x78\x54\x64\x06\x4c\xa1\x4a\x91\x98\x1e\xe5\xe8\x68\xca\x64\x04\xaf\x2d\x3f\xf5\xd1\x06\x3a\x3d\x7b\xad\xbe\xdf\x03\x7f\x63\xfe\xba\xd8\x52\x15\x00\x2b\xd3\x59\x32\x2a\xe9\x74\xcf\x44\x3c\xa5\xf4\xde\x20\xa1\x85\xbf\xe0\x9b\xb2\x31\x06\x19\xc0\x05\x06\xaf\x90\xb8\x29\x20\xcb\x69\x71\x57\x57\xd1\xc4\x9f\x2a\x4c\x32\x00\xb2\x05\x86\x02\x24\x46\x48\x53\x1d\xa6\x7d\x7f\x2a\xa9\x2e\x24\xbd\xb6\xea\x2a\x4e\x05\x57\xe0\x1a\xe5\x3f\xf4\x31\xd8\xf7\xa7\xa7\x50\x2d\x80\x2d\x9e\x8f\xcc\x29\x14\x3f\x93\x5c\xd2\x45\xe3\x8a\xe3\x3c\x5a\x58\x66\x8e\x54\xa9\x59\x89\x6f\x72\x72\xb0\x75\xd0\xe1\x44\x86\xc6\xd1\xc5\x7f\xe9\x52\x75\xe4\x90\xab\xef\x2b\x49\x17\x50\x16\x24\xd6\xa3\x23\xfb\x56\x99\xf8\xd3\x92\xcb\x58\x81\xff\x81\xfd\xe2\x20\x1b\x65\x32\xf6\xec\xa8\x17\x0c\x64\xcf\x1b\x41\x11\x5f\x30\x4a\x66\x31\xe8\x89\x39\xb3\x0a\x12\x94\xa4\x01\xa1\x07\xca\xc9\xf1\x00\xf9\x43\xf0\x10\x8a\xe3\xe0\xd2\x1f\x6b\x7b\xad\x02\x93\x0c\x08\xf8\xfd\xd3\xa5\x11\x0c\xce\x74\x14\xb3\x2e\x55\xfa\x99\x3d\x80\x5a\x47\x7c\x71\x7a\xcc\x70\xdd\x89\xfc\xe9\x16\xe1\x31\xd3\x33\x5b\x6a\x0c\xfd\x71\x82\xe5\x5b\x36\xe6\xf7\x34\x77\x4c\x59\xfd\x1f\x7e\x60\x6d\xa2\x3b\xc0\x20\xf3\x02\x33\x2e\x2d\x5a\xc7\xe1\xff\xb5\x31\x9e\x3f\x40\xcd\x02\xe3\x58\x5c\x31\x80\x14\x0a\x93\x7a\x09\x15\xd5\x51\xd2\x16\xbb\x7b\x98\x54\x5c\xdc\x7a\x06\x24\x5f\x72\xba\x52\x2e\x1d\xe9\x51\x35\xd4\x1b\x2f\x2d\xf7\x92\x99\xbb\x82\x29\xa4\x5f\x74\xea\x10\xdb\x87\x29\xc3\x5f\x74\x1a\xe0\x87\xba\x56\xe4\x8e\x8c\x05\xdd\xc4\x69\x1a\x84\x17\x76\xd7\x5e\x60\x4c\x03\x29\xc7\x31\xda\x10\x4e\x6b\xaf\x8d\x12\x59\xa8\x67\x61\x1f\xe4\x8a\x5a\xc4\x1a\x65\xfd\x26\x28\xaf\x3f\x5d\xeb\x3d\x5d\xeb\xfd\xcd\xaf\xf5\x58\x48\x5f\x76\x6a\xb9\x4b\x58\xdf\x79\xe6\xb0\x8e\xe4\x17\x5a\xee\x8b\x45\x0c\x67\xf9\x92\xae\xb1\xc3\xc1\xe6\x60\x90\xc0\xd0\x89\xdd\xcd\x0f\x41\x2d\x95\xa0\x19\x15\xbf\x98\xd7\x9b\x47\x84\xaf\x20\x85\x50\x79\x08\xb2\x02\xd0\x4d\x95\xee\xf6\xcf\x9f\xcb\xe7\x03\x76\x3e\x7b\xae\x2b\x89\xc8\xb6\xf9\x9c\x5d\x5b\x49\xe5\x24\x5e\x45\x03\xf5\x70\x5f\x3a\x52\x2e\x0a\x99\xc7\x95\xc2\xd1\x98\xdc\x44\xc6\xde\xa2\x6a\x74\x09\x45\x74\xdf\xe6\x3d\x4d\x2c\x9b\x85\xcd\x1e\x87\xff\xa9\xfb\x96\xbe\x3d\xb9\x74\x97\xc2\x42\x90\x47\x22\x02\x94\x7f\xfc\x11\x70\xa7\x8a\xa9\x20\xbc\x00\x6e\x5c\x56\x20\xf2\xeb\x8b\x79\x39\x4d\x29\x44\xd9\x4d\xf9\xae\x9d\x14\xd2\xd0\xd8\x4f\xa0\x99\xe3\x94\x4c\xf6\x0f\x1b\x1b\xc6\x40\xf3\x3f\xe3\xc5\xea\x2a\xcd\xf1\xaf\x90\x14\x2c\xb5\x34\x9e\x11\x99\x2d\x4e\x52\x94\x44\xd4\xce\x71\x3a\x05\xd6\x0d\x67\x67\x3f\xbc\x49\xc9\x81\xdf\x43\x3d\x3c\x24\x0c\x80\x2e\x71\x7e\x85\x0a\xa3\x41\x95\x8c\xda\x5f\x30\x2c\xfd\x60\xc1\xfa\xc7\x1f\x91\x6d\xe4\xcb\x46\x7d\x64\x5e\x37\x10\x54\x2d\xfe\xd1\xce\xce\x46\x94\x6f\x86\xf8\x3a\x45\xdd\xc3\x4f\xa8\x7f\xd3\x1f\x63\x4f\x74\x13\x86\x5d\x6c\x36\xd0\x13\xe8\x32\xb3\x59\x9a\xc6\x51\x9f\xf0\xac\x84\x8e\x8e\xd1\x8a\x74\x0c\x16\xcb\xc4\x36\x17\x96\x8e\x30\xd2\xd0\x4b\xdd\x7a\xa8\x5a\xa4\x7f\x96\x61\xa5\xa4\xe0\x12\xcd\x24\x63\xb0\xe7\x02\x80\x6e\xc6\x26\xe9\x62\x4b\xa6\x1d\x94\x23\xdf\xaf\x6e\x09\x75\xeb\x65\x42\xf8\xde\xc0\xcb\xd8\x04\x7b\x2f\xeb\x90\xa8\xce\x00\x38\x0b\x59\x27\xdc\x4e\x72\xcf\x9a\x97\xd3\x99\x6c\x33\xdf\x64\x5e\x93\xff\x90\xac\x6b\xda\x23\x72\xb4\xa4\x9c\x5a\xa6\x5c\x78\x79\x59\x2a\x27\xd6\xab\x74\xd2\x87\x0f\xfe\x60\x20\x6c\xbb\xa4\xc4\x9f\xe2\xbb\x3e\x3d\xd2\xc1\x41\x62\xb1\xdc\x78\x0b\xde\x4b\xb6\xe2\x54\xa0\x13\x23\x21\x5b\xfa\x66\xed\xe6\x5a\x2c\x06\xc3\xec\x95\xaa\x95\xca\x58\x10\x68\x15\x34\xe4\x0b\x21\x21\xcf\xa2\x5b\xa2\x35\x08\x4c\xa8\x9c\x4b\xd2\x1c\x94\x73\x46\xdb\x2a\xd5\x0a\x84\xdc\x06\x6c\x44\x56\x57\xd3\x5d\x10\xd9\xf7\x29\x49\xe9\x93\xec\xfb\x77\x97\x7d\x33\x93\x36\x9e\xb1\xf7\xa1\x7c\x74\xf7\x7a\x7e\xa8\x4a\xbb\x41\xcf\x17\xae\xb7\xf8\x9a\xaa\xab\xf3\x5c\x77\x8f\x27\x7e\x9c\x6e\xb3\x82\x99\xdb\xad\xf3\x6a\x0c\xd4\x4a\xd0\x2c\xef\x8b\xa6\xf3\x96\x5e\x8b\x4b\xb0\xe3\x34\x0e\xc2\x8b\x5b\x70\x6d\xb1\xbd\x27\xd2\x72\xcf\x0f\xe5\x4f\x3f\xfb\xe3\x19\xbe\x45\x97\xe4\x1f\x76\x1d\x42\x20\x0f\x71\x8c\xe7\xdc\x90\x7a\xaa\x79\x01\x44\xa9\x61\x38\xa9\x62\x71\x3a\xf2\x00\x23\x22\xad\x7b\xb4\x25\x73\x0b\x03\xb5\x1b\x1d\x65\x48\x37\xdd\xf3\xc3\x52\x1a\x95\x99\xaa\x08\x74\x38\xe4\x33\x57\xf9\x94\x2c\x56\x44\xa4\x1e\xe4\x89\x28\x2d\x05\x54\x7d\x43\x21\x32\x3f\xdd\x25\x53\x7f\xcc\x20\x6e\x05\x31\x91\xc5\x6c\x0e\x31\xbc\x47\x27\x11\xf3\xec\x95\xbb\x03\xd5\x19\xf4\x52\xd9\xec\x1a\x6f\x4f\xc8\x31\xd0\x0d\x9b\xa4\x0b\x2e\x12\xc2\x53\x1a\xa7\x23\x39\x27\x78\xa9\x0c\x8d\x30\x6c\xc3\x24\x0d\xd2\x19\x15\xb8\x4c\xf3\xaf\x01\x9e\x46\x49\x90\xca\x58\x32\xb8\x02\x3d\x00\xd3\x1f\x07\x38\x4c\x75\x4b\x8c\xc2\x0d\x1b\x26\x16\x3c\xd7\xb8\x39\x82\x8b\x62\x64\x8e\x1f\x57\xc1\xe7\x5e\x25\x0b\xd2\x1b\xce\xc2\x01\xd8\x44\xf6\x71\x9c\xfa\x81\x98\x7e\xc7\xf2\x11\x13\xbb\xd8\x3a\x7a\xf4\x25\x24\xf0\xba\xc3\x5a\x62\x23\x4f\x66\x53\x4b\xf9\x25\xc9\xb6\xc2\x7b\x3d\x8d\x32\x89\x96\x80\xee\xd0\x06\x24\xda\x1c\xcf\x70\x87\xfe\xc3\xc5\x5c\x2d\xdb\xbb\x73\x56\xd8\xe4\x67\x93\x02\x81\xed\x83\x3e\xe2\x9c\x10\x71\x0e\x89\x4a\x93\x59\x92\xc2\x56\x87\x27\x38\x4c\x05\xdd\xf4\x6e\x52\x9c\x34\xea\x65\x26\x8c\xff\x50\xd6\x26\x92\x95\x7b\xf0\xe9\x4b\x8c\xf9\xe3\xd5\x29\xa5\xa2\x59\x18\xfc\x7b\x86\x51\x30\xc0\x61\x1a\x0c\x03\x95\x13\x17\x9a\x6b\x3e\x3a\x05\x66\x18\x9a\xb4\x73\x4d\x1f\x76\x1d\x69\x0f\x7a\xad\x13\x01\x1f\xe3\x92\xdf\x0b\xca\x15\x3f\x25\x8c\xb5\xc2\xc7\x97\x83\xfe\xe3\xbe\x44\x60\xc8\xaa\x7c\x14\xad\x41\x10\xcc\xfd\xf0\x45\xa7\x41\x44\x57\x9e\xb9\xff\xf6\xcc\x6b\x15\xca\x95\xcc\xb4\xbb\xad\x42\x09\xdb\x5e\xcb\x4a\xf8\x88\xc8\x17\x43\xbf\x9f\x46\xf1\x8d\x47\x15\xca\x64\x60\x9f\x11\x36\x4d\x44\xfd\x68\x88\x44\x6f\x36\x36\xd0\x0b\x1a\x91\xe9\x05\x94\x79\xb6\xba\x8a\xba\xd1\x64\x12\x85\xff\x3c\x7e\xfe\xec\x99\xd1\xf9\xec\x17\x6b\x80\xe3\x54\x7a\x41\x86\x21\xc6\x2f\xca\x1e\x92\x5e\xe1\xb0\xbf\xd2\xf3\x13\xdc\x6e\x6a\x1f\x26\x83\x96\x5e\xf4\x72\xfa\x65\x30\xd4\x5e\xf6\x83\xe9\x08\xc7\x2b\x14\x72\xf9\xf5\xf3\x67\xb7\xcf\x9f\xe1\x71\x82\x91\xd4\x19\xaa\x30\xa7\x7d\xe1\xc3\xf0\x02\xfd\xf8\x23\xfb\x50\xf1\x27\x03\xd1\xb7\xcd\xfd\xad\xe7\xcf\x9e\xd1\x0f\xa5\x53\x8e\xb3\x87\x54\x54\xe1\x99\x60\x48\x3f\x50\xc4\xe0\xb7\x8c\xcf\x99\x18\x65\x19\x31\xd6\x10\x8d\x86\x81\x4a\xbd\x38\xba\x4a\x70\x5c\x7e\xfe\xec\x99\x18\xb1\x28\x4a\x2b\xdd\xf8\x66\x9a\x46\xff\x3c\xa6\x55\x6f\xe1\xf4\x24\x6f\x3f\xe2\x3b\xfa\xe3\xf9\xf3\x67\x25\xf5\x38\xf6\x0c\x51\x8d\xc8\xf1\x28\x8a\xd3\xfe\x2c\x4d\xe8\x1b\xb2\x6c\xba\x68\x03\xf1\xba\xaf\xa5\xd7\xe7\xe3\xa0\x47\x3e\x55\xc6\x41\x4f\x7a\x0f\xca\xb0\x2e\x74\x8a\x7c\x25\xa5\x2a\xd2\x3b\x05\x82\x3f\xbe\x88\x00\x04\xf9\xf1\xfa\xb9\xc0\xe2\x43\x14\x7d\x99\x4d\x51\xea\xf7\xc6\x58\xc2\xe4\xf8\xed\xc1\xaf\xec\xcc\x27\xde\xed\x7d\xfc\xf9\xdc\xf6\xfe\xf8\xd3\xdb\xf3\xfd\xbd\x5f\xcf\xab\xae\x0f\x35\xd7\x87\xba\xeb\x43\xc3\xda\xb6\xab\x1d\xf9\xa3\xd1\x96\xfc\xd1\x68\x4f\xfe\xc8\xdb\x14\x43\xd3\x8d\x26\x53\x72\x50\x1c\x9b\x43\x64\x9b\x52\xad\xd6\x20\x9a\xf5\x88\xd4\x4f\x6a\x65\x05\x80\xc5\xca\x58\x20\xd9\x52\x21\x80\x70\x82\x28\x40\x6f\x50\xbd\xd5\x7e\x8d\x82\xe5\x65\x05\xbc\x90\x11\xd1\x1b\x54\xab\xaf\x1b\xdf\xc8\xdf\xe0\x34\x38\x43\x1b\x04\xc6\x1b\x54\x7b\xad\x7e\xa7\x57\xa9\x39\xb5\x4a\xb4\x5a\x19\xfd\x86\xaa\xd7\xb5\x5a\x4f\xaf\x9f\x3d\xde\x3e\x57\x7a\xfd\x8b\x3f\xfe\x82\xde\xed\x94\xea\xbf\xad\x97\xd5\xde\x5e\xd3\x10\x89\xea\xbb\x40\x7b\xb9\xd0\x08\x48\x83\x9c\xf4\xa2\x6b\xf5\x23\x18\x1a\x90\x36\xaf\x03\xf4\x1b\x2a\x5d\x67\x1d\x62\xbf\xeb\xd2\xef\x86\xf4\xbb\x59\xd6\x3a\x0b\x50\x4a\xc9\x35\xfa\xe9\xa7\x9f\xd0\x3a\x94\x4c\xae\xd1\x8f\xa8\x7a\x3d\x1c\xd2\x01\x6a\x37\xb4\x2a\x64\x75\x9c\x5e\x93\x81\x4c\xae\xb5\x4f\x7c\xf1\x9c\x26\xf0\xfd\xfa\xf5\x73\x67\xa7\x26\xb3\x71\x1a\x4c\xc7\x41\x1f\xb4\x04\x66\xf7\xae\x09\x19\x0f\x4e\xaf\xcf\x5e\x5b\xbe\x35\xe9\xb7\xba\xf5\xe3\x3a\xfd\xd8\x3c\xcb\x69\x3d\x99\xf5\x10\xc8\x37\x1e\x9a\x04\xd7\xa8\x1f\x8d\x67\x93\x30\x51\xa8\x5f\x86\x49\x24\x85\xd2\x00\x7a\xf5\x92\xd0\x4c\xb5\xc6\x47\x8a\x3d\x56\x6b\xd5\xaa\x3e\xb4\x62\x25\xd3\xc1\x2a\xa5\x30\x31\xcd\x32\xfa\x4a\x7e\xd3\xf1\x76\x54\xa9\xc9\x55\x6a\x6d\xa9\x4a\xad\xed\xaa\x53\x97\xeb\xac\x97\x51\x56\xa7\x6e\xcc\xba\xe0\x06\xb4\x4e\x9a\x33\x52\x41\x78\x29\x8f\x16\x79\x2c\x3c\x62\xd7\xeb\xd2\xf8\x30\xf2\x6c\xb2\x57\x55\xfe\xa2\xae\x0c\x69\xee\x88\x2a\xfc\x91\xd1\x58\x91\x61\x55\x58\xa7\x52\x6f\xce\xd8\x2a\x6c\x55\xa9\x38\x67\x80\x15\x96\xcb\x2a\xe6\x8d\x32\x5c\x16\x80\x1e\x18\xc7\x26\x27\xfc\xe1\xda\xca\x04\x19\x03\xd8\x58\x80\x03\x42\x95\x3a\xfa\x0d\x0d\x4e\xc9\xff\xae\xd7\xd1\x6f\xe8\xba\x7e\x76\xa6\x2f\x24\x28\x1b\xa0\xdf\x36\xa0\xe0\x75\x60\x14\x50\x98\x24\xfc\xbc\x85\x33\xad\xd8\x57\x0e\x63\xdc\xa7\x9d\x1b\xa0\xa3\x7e\x14\xb2\x0d\x26\xdb\x95\x8e\xba\x07\x1f\xc9\x1e\x51\xbd\xae\x56\x3d\x54\xbd\xae\xd6\xe0\xbf\x75\xf8\x6f\x13\xfe\xbb\xee\x01\x2d\x90\xff\xd6\xe1\xbf\x4d\xf8\xef\x3a\xfc\xb7\xd6\x23\xff\x6d\xb4\xb3\xcd\xec\xe5\x4b\x86\xd4\x4b\xb4\xb9\x7d\x4c\x03\xb2\x23\x2a\x0e\x21\x22\x10\xc4\x41\x3a\x9a\x54\x78\x99\xd5\x0c\x15\x52\x7a\x83\x89\x0f\x15\xfa\x20\x49\x18\x15\x7c\x9d\xd2\xe8\x01\xa2\xcb\xe7\x83\xe8\x08\x27\x38\xed\x20\xc7\x16\xc9\x06\xe1\xf8\x4b\x30\x65\x96\xbf\xd1\x10\x85\x47\x11\x9c\xc6\x46\x7e\x82\x7a\x18\x87\xe0\x1d\xc0\xee\xb7\xfc\x70\x00\x26\x7c\x83\x60\x80\xc2\x28\x65\x66\x98\x26\x29\xd0\x6c\x2e\x1c\x12\x37\x17\x3d\xff\x82\x6f\x0e\xe3\x20\x8a\x8f\xa8\x05\xf0\xc6\x46\xf6\xde\x4a\x3a\xdc\x2c\x4c\x9b\x53\xb3\x03\xaa\xf8\xc6\xff\xb8\xc1\xe1\x86\xbd\xf9\xec\xad\x85\x3f\x7f\xc1\x37\xbf\x44\x31\x18\x31\x7e\xc1\x37\x95\x2b\xf2\xdb\x5e\xec\x38\xf8\x1d\xb3\x52\x49\x70\xf1\x96\x30\x20\xb4\x8a\x9a\x79\xcb\x48\xf8\x01\xc4\x30\x40\x26\x58\x3e\x72\x1c\xc7\xec\x99\x37\xb8\x8c\xda\x85\x5a\x20\xfd\x4f\xfa\x23\x4c\x8e\x1f\x88\x88\xd0\x96\x3e\x24\x47\xd1\x15\x81\x5d\xe2\xcd\x2c\x93\x5d\xfa\x65\x6e\x1f\x64\xb8\xf6\x61\xe1\x8d\x4a\xe3\x2c\xbd\x3b\xd5\x97\x6a\x66\x22\x4a\xd0\xa1\xa2\x07\xfd\xf9\x86\x61\xc8\x9e\x2d\x52\x08\x62\x64\x27\xca\xd3\x41\xb2\x96\x23\x7f\x12\x2a\xa7\x50\xe7\x8c\x8e\x2c\xcc\x38\x7b\x63\x61\x35\x6e\x86\x85\xa4\xfd\xc4\x00\x0e\xd1\x74\xf4\xa1\x94\xd1\xfe\x81\x21\xfe\x0f\x81\xb8\x13\x73\x36\x0b\x47\x51\x8a\x08\x49\xba\x0b\xa5\xf2\x1e\xa0\x6e\x01\xb9\x90\x8f\x67\xbd\x22\x90\x41\x7c\xe2\x30\xcf\xa4\xbd\x0d\x3e\x64\x3b\x15\x93\xd1\xce\xa4\x5d\x4c\x2e\xb1\xae\x14\x00\x4c\x19\x64\xf6\x7a\x0e\xb6\xfb\xc1\x35\xb0\xed\x3c\x6c\x7f\xdb\x00\x26\x7e\xca\x06\x79\x35\xa3\x8e\xaf\xa8\xca\x50\xb7\x4c\x36\xca\x26\x1c\x48\x8b\xad\xbb\x9f\x50\x9b\xf0\x33\x6d\xc2\xd0\xc6\x06\x6a\xce\x9b\xb4\xef\x6e\x68\xed\x7d\x76\x8c\xb8\x6b\xcd\x18\xb4\xce\x86\xe4\x0c\xfd\x46\x64\x09\x73\x11\xcd\xe5\xe6\xb2\x4c\x97\xcf\x66\x82\xf0\xf2\xbd\x85\xd3\x18\xaf\xdd\xcc\x86\x14\xcd\xf8\x8d\x78\xca\x58\x0e\x7f\xe5\xe0\x3a\x32\xc3\x62\x7c\x74\x45\xd4\xb1\x11\x2f\x1c\x19\x79\x33\xff\xc8\x21\x1a\x27\x3b\x79\x58\xce\xd4\xb4\x82\x9b\x87\xf8\x1b\xd4\x04\x47\x16\xfa\x90\x47\xfb\xea\x5c\x9c\x72\x08\x4c\xd2\x5c\xb0\x23\x39\xc0\x54\xa1\x5b\x5d\x43\x84\x14\x55\xe1\xda\xb1\x94\xce\xd0\x6f\xee\xc5\xe9\xf8\x53\x85\x6f\xfb\x0a\xd4\x11\x68\x9c\xaa\x4b\xd1\x3e\x07\x4e\x49\xd6\x93\xa6\x07\x87\xfd\xf8\x66\x4a\x2d\x63\x65\x39\x6f\xdf\x43\xd1\x70\x98\xe0\xd4\x98\x19\xba\x46\x06\x51\x57\xd4\xcb\x0a\x7b\xe6\x5e\xed\x65\x27\xc4\xec\x67\x2d\xfb\x59\xcf\x7e\x36\x3c\x60\x31\xf2\x29\x43\xc1\x75\x80\x17\xc5\x95\x70\xcd\x2b\x7f\x8a\xea\xe1\x00\x64\xcf\x66\x3a\x72\x08\x31\x84\xbe\xf7\x4f\x29\x18\x22\xbf\xe8\x43\xaa\x7c\x53\xcb\x36\x72\xca\x36\xac\x47\xa2\x22\x43\xa8\xd2\xaa\xa7\x12\xa8\xfa\x58\x53\x1f\xeb\xea\x63\xc3\x13\x0a\x0b\x63\xf3\x5e\x5d\x45\x7b\xe4\xe4\xfb\x5d\x8c\x91\x7d\xd2\x95\x61\xb2\xce\xba\x87\xee\x47\x6e\x36\xa2\x61\x07\x82\xc2\x92\xb5\x65\x60\xdf\x61\x16\x2b\x14\x2e\x24\xa9\xa8\x4e\x30\xb5\xe8\xb8\xaa\xd2\x60\x9d\xc1\xeb\xdf\x14\x66\x5b\xb5\x69\x80\x92\x9a\x3e\x1d\x5a\x2d\x63\x7e\xa0\x56\x5d\xad\x55\xd7\x6b\x59\xb5\x4d\x49\x43\x9f\x4e\xad\x56\xc3\xa6\x86\x7a\xaf\x9d\x1d\xec\x47\x7f\x79\x0b\xb4\x9d\x18\x8e\x2c\x67\x1c\xb1\xff\xd2\x51\xdd\x40\xb5\xd7\xec\xe7\x1b\x3e\x43\xec\x85\x63\xdf\x85\x39\x0e\x86\x29\x50\xba\xe7\x50\x94\xe5\x4e\x1c\x47\x3d\x25\x93\x27\xa9\x6b\xaa\x42\xf2\xfa\x4d\x52\x74\x95\x92\x9a\x21\x77\xfd\x26\x29\xb5\x4a\x49\x5d\x97\xba\x7e\x93\xf4\x57\x49\x43\x7a\x6d\x6c\xc3\xcb\xcb\xb6\x0d\x00\x90\xab\xa9\xc8\xd5\x1c\xc8\xd5\xe7\x20\xd7\xc8\x45\xae\x7a\x47\xe4\xea\x2a\x72\x75\x07\x72\x8d\x39\xc8\x55\x73\x91\xab\xdd\x11\xb9\x86\x8a\x5c\xc3\x81\x5c\x75\x0e\x72\xb5\x5c\xe4\xea\x73\x91\xb3\x92\xee\xa7\x29\xd8\x10\x25\xa9\x9f\x62\xb3\x00\xb0\x93\xb4\x6a\xe9\x18\xb0\x8c\x54\xd7\xa3\xc1\x17\x32\x17\x69\xdd\xf6\x85\x0c\x44\xaa\x6b\xc7\xad\x4a\x14\xeb\x7a\x9a\xc3\xfb\x60\xf9\x94\xe8\xc9\x43\x5a\x3b\xfa\xa9\xc5\xb2\x7c\xf4\x63\x8b\xb9\x82\x94\x73\x4b\xb6\x84\xca\xc5\x28\x41\xac\x1f\x8e\x5d\xcd\x8d\x9d\xb9\x7e\x0c\xec\x8c\x25\xa4\x62\x57\xbd\x0b\x76\x75\x09\xbb\xba\x1b\x3b\x73\x01\x19\xd8\x19\x6b\x48\xc5\xae\x76\x17\xec\x1a\x12\x76\x0d\x37\x76\xe6\x0a\x32\xb0\x33\x16\x91\x8a\x5d\x7d\x3e\x76\x26\xb5\x62\x1e\xd8\xda\x2e\x97\xd0\x6d\xd8\xb2\x8e\x74\x21\xc7\x58\x4e\xea\xe6\x6a\x59\x55\x86\xe8\xd3\x70\xc9\x3e\xec\x28\xdc\x41\xf5\x56\x7b\xb5\x51\x67\x1a\xe8\xb2\x4d\x15\xcc\x25\x16\x21\x20\x25\xcc\x71\x98\xa9\x86\x97\x12\x96\xf0\x09\x41\x0e\xef\xa1\xdf\xc7\x42\x47\x2c\x80\xfc\x37\xbe\xf6\x27\x53\x71\x52\xce\x3e\xf0\x39\xa5\xb0\x52\x7c\x9d\x4a\xb7\xdb\x95\xcd\xed\xe3\x0a\x3b\x47\x94\x26\xdc\x22\xfd\x0b\xbe\xf1\x50\x7f\x78\x21\xa4\xf9\x0c\xca\x74\xec\x13\x24\xae\x53\xa4\x43\x61\x12\x7e\x29\x6b\xc7\x06\x88\xe9\xb4\xbb\x16\x25\xf6\x39\x8d\x9a\xba\x8b\xc7\x53\x1c\x97\x36\xb7\xe9\xb5\x3e\xd5\xd9\x3f\x7f\xc6\x6c\x56\xe4\x26\x5f\x3f\x7f\x0e\x11\x70\xc1\x80\x44\xb1\x2a\xe8\xb4\xea\x1e\xb7\x4b\xe8\xb4\xc0\x76\x44\xb2\x4c\xe8\xb4\x9a\x5e\x66\x92\xd0\x69\x81\x0b\xe3\x64\xd0\x7a\xd1\x69\xd7\x6e\xcf\xbc\x56\xfd\x5e\xd6\x22\xdf\xd2\x4c\xe4\xd1\x8c\x39\xbe\xa1\x59\x06\x5d\x09\x2f\x11\x33\xa0\x20\xcd\xa3\x7e\x34\x99\x46\x21\x84\x5c\x27\xdf\x56\x9f\x3f\x13\xf3\x3e\x0e\x7a\x15\x56\xf4\xeb\x57\xd9\x00\x40\x38\x7d\x3e\xb0\x71\x87\x9f\xe0\xcc\xaa\xc3\x4f\xb0\xf4\xed\x97\x28\x1e\x80\x5b\xba\x28\x20\xde\xc8\x10\x66\x43\xb0\x17\x03\x5a\xdf\xe4\xb7\x3c\x19\x4c\xeb\x67\x05\x33\x0c\x9e\x55\x5d\xb2\x50\xa5\xf7\x9f\xd2\xe1\x3a\x40\xc1\x61\xbf\x42\x1e\x34\xac\xdb\x4d\xf1\x95\x3e\xe6\x19\xa2\x88\x2f\xdb\x97\xd3\xf7\x5b\x3b\xd9\x65\x13\x7d\xb6\xde\x60\xf5\x12\x6a\x9e\x47\x96\x15\xbf\xc5\x4a\xf1\x64\x3a\xf6\x53\x1b\x83\x12\x41\xa6\xff\x08\x59\x40\x1e\xae\x41\x05\xa7\x02\xc1\xeb\x40\xef\x17\xfc\x8e\x2b\x3c\xc0\x64\x07\x35\x51\xa9\x56\x5f\x47\xbd\x20\x4d\xca\x79\x00\x83\x4b\x0b\xbc\xbd\x9f\xef\x0a\xee\x7c\xfb\x63\xf7\xfc\xd7\x9d\x83\xa3\xfd\xf3\xfd\x83\xad\x6d\xb4\x09\xa1\x0d\x52\x3f\x4c\x51\x8c\xa7\x31\x4e\x70\x98\x06\xe1\x05\x57\xc4\x10\x32\x9c\x44\x83\xac\xef\x56\x98\x5b\xdb\x85\x60\x32\x76\x6a\xc0\x94\x2e\x05\x35\x93\x23\xf1\x68\xa7\x28\xcb\x25\x61\x36\x9b\x14\xdd\x2e\xb8\x7d\xcf\x62\x30\x78\x10\x39\x3e\xe4\x22\x4a\x71\xa9\x77\x82\xee\xc9\x1c\xa0\x93\x11\x26\xa3\x9e\x46\x68\xc6\xdc\x04\x08\x0b\x40\xa4\x30\x80\x56\x40\xae\x66\x0f\xfd\xe1\x45\x07\x48\x97\xe3\x5a\x96\x77\x54\x03\x5b\xd8\x2e\x12\x0a\x9b\x91\x5f\x10\xba\x26\xc3\x86\x3e\xb5\xc7\x94\x70\x27\xa4\x47\x90\xff\x82\x6f\x2a\xd6\xb2\xdc\x33\xb4\x3f\xbc\x40\xa5\x03\x68\xc5\x1f\x97\xa1\x4e\xdf\x36\x78\x05\xc7\x40\x6d\x8b\xc7\x11\xa5\x13\x7a\x4b\x48\x84\xf7\x8e\x10\x4a\x3f\xaf\x4f\xe4\x5c\x11\xf4\xdd\xdf\x55\x29\xc1\x2c\x80\x14\x69\x41\xde\xe3\xf9\xd5\x73\x85\x6e\xd3\xdb\x74\x98\xa3\xb8\xc4\x2e\xcf\x60\x08\x3d\xf4\x07\x0a\x2e\x3b\x28\xb8\xcc\x78\xe3\xad\x62\x7a\xa0\xcc\xb7\x0a\xa9\xa3\x84\x85\x62\x92\x83\xae\x01\x90\x13\x87\xd0\xfa\xec\xc6\x59\x5d\xab\x16\xd9\x43\x97\xd0\x0a\xd2\x93\x63\x21\x3e\xd1\xd3\xc3\xd2\xd3\x16\x7e\x28\x7a\x12\x90\xee\x47\x4f\x2a\x9f\xbe\x03\x3d\xed\x85\x41\x1a\xf8\xe3\xe0\x77\x9c\x20\x1f\x85\xf8\x6a\x7c\xc3\x30\x1c\xb0\xe1\x98\x4f\x4b\x7c\xd7\xb8\x1e\x46\xf1\x64\x3f\x1a\x60\xb4\x4d\x7d\xd5\x20\x4c\x73\xc6\xe9\xa2\x58\xa6\x53\xb0\xae\x06\x37\x3f\x4e\xb5\x62\x93\xb1\x93\xe1\x77\x47\xb2\x0f\x46\x56\x25\xf3\x83\x8d\x53\xdc\x91\xe0\x82\x30\x50\x2c\x6c\xc4\x34\x49\xe4\x62\x51\x51\x6f\x4e\xa7\x84\x16\x60\xb4\x78\xba\xe9\xc4\x72\xcd\x40\x86\x78\x43\xfc\xe4\x9b\x22\xa5\x41\xf3\x54\x9c\x12\xc9\x99\x1a\xd6\x47\xf1\x84\x4e\xbb\x6f\xd3\xdd\x50\xfa\xce\x48\x6a\x23\x23\xaf\xd7\xb6\x92\xd4\x8e\x06\x6c\x65\xac\x67\xf1\x80\x12\x3a\xf5\x00\xb0\xf5\x03\xec\x8b\x4a\x85\x17\x0e\xd8\xe8\xa8\x7c\x18\x62\x39\x24\xa2\x25\xd0\x9e\xdd\x93\x7c\xd8\x12\x34\x71\x53\x66\x38\x2e\x62\x44\x45\x8d\x8a\x06\x7e\xea\xa3\x1e\xc8\x5e\x6a\x09\x87\x3c\x06\xa0\x69\xa6\x0b\xee\xed\xac\x03\x3e\xc4\x31\xcc\x65\x3f\x0a\xfb\x31\x4e\xf1\x0a\x1b\x8e\x71\x74\xa1\x30\x65\xe9\x5e\xea\x68\xb1\xb1\x86\x78\x1a\x80\x39\x75\x6f\x61\x3c\x05\x0f\x24\x96\x82\x07\x0b\x6c\x7a\x5f\x13\xe6\x0a\x43\x80\x32\x65\x27\xe1\x0d\xbc\x0d\xd6\x80\x04\xbe\xc0\xce\x25\xf1\x27\x01\x8b\x06\xcd\x62\xc1\x08\x82\xf0\xe2\x01\xb8\x49\xd6\xf9\x0d\x4e\x1e\x0c\x7e\x69\x89\xb4\xb9\xa4\x92\x49\x91\x7a\x57\x1c\x73\x27\x85\xb1\x92\x1d\x2d\xca\x2b\x1d\x3a\x07\xf7\xc0\xe1\xc0\x36\xfb\x3e\x7c\x91\xab\xdb\x68\x8a\xb6\x87\xfc\x4b\x3f\x18\xfb\xbd\x31\xa6\x66\x88\x89\x7b\x5b\x3c\xe7\x9d\x29\x4c\x55\x3b\x41\xc8\x36\xbe\xdc\x7d\x8a\xc1\x55\xf7\x99\x8f\x51\xca\xbc\xa3\x69\xd0\x34\x0a\x29\xdb\x35\x50\x90\x20\x3c\x1c\xe2\x7e\x1a\x5c\xe2\xf1\x0d\xf2\xd1\x00\x27\x69\x3c\x83\x67\x0f\xc5\xd8\x1f\xac\x44\x61\x1f\x17\xda\x67\x8a\x52\x2f\xa0\xf1\x58\x34\x4c\x81\x3f\x36\x25\xf3\x91\x2c\x15\x27\x62\x51\x65\x51\xea\x17\x15\xe7\x93\x3f\x2f\x5a\x9c\xfe\x77\xb2\xb9\x98\x41\x21\xb5\x44\x30\xcc\x05\x80\x72\x57\x8b\x52\xd4\x72\x51\xb2\x00\x43\x86\x78\x48\x04\x55\xb6\xe0\xf0\x80\xc5\xcb\xe4\x9c\x7a\x47\x9a\x10\xeb\xe2\x33\x6b\xcf\x55\x36\xd7\xea\xeb\xab\x8d\xba\xfc\x89\xaa\x44\x6c\x5f\x34\x39\xa8\x83\x6a\xca\x57\x55\xfe\xed\xa0\x7a\x91\xb3\x53\x62\x55\x65\xfb\xf3\x15\xd9\xc8\xb9\x36\xf9\xa9\x85\x8d\xf4\xc9\x08\x4b\x42\x01\x4b\xb4\xe5\xa3\x11\x68\x8d\x89\x90\x59\x60\x29\x72\x11\x76\x33\xe4\xf8\x40\x80\x01\xbe\xac\x89\xd0\xc4\xd6\xb5\xa5\x43\xdf\xe0\xb0\xc4\xac\xbd\x4d\x95\xa7\xa6\x23\x37\x64\x5b\xe7\x2a\x53\xea\x75\x9c\x7e\x53\xe4\x4f\x7c\x4a\xf0\x18\xf7\x53\xda\xf0\x71\x1a\xfb\x29\xbe\xb8\x29\xb9\xcc\xb5\x25\xed\x33\x88\x8b\x1b\x68\x89\xb2\xd2\x25\xa7\x79\x18\x9b\x8d\x43\x3f\x49\x08\x9b\x78\xeb\x27\x78\xa0\x78\xcc\xc9\x7f\xf9\xc6\x61\x0c\xd4\x31\x8e\xe1\xc0\x45\x76\x35\x37\xa4\xfc\x45\xae\xe7\xf6\x63\xf7\x19\x39\x36\xea\x2e\xa4\x18\x39\xc9\x8c\xcd\xbc\x61\xc9\xb3\x1b\xcd\x82\x80\xd9\xe7\x41\x5c\xdc\x50\x14\x3d\xe4\xbe\xc0\xd1\xc7\xc0\x73\x58\x7a\x32\xb2\xef\x18\xfd\xd7\xee\x73\xee\x85\xb6\x7a\x53\xe4\xa1\xdc\x1b\x23\x1d\x73\xcb\x84\xea\x6c\x5b\xe6\x92\xa5\x32\xd3\xf0\xda\xaf\xde\x54\x1d\x76\x92\xc6\xd8\x9f\xdc\x49\x95\x0d\x32\x14\x53\x3e\xcb\x36\xf8\x8d\xfa\x4a\x2f\xa0\x06\xdb\xea\x89\x86\x4a\x27\x10\xc6\x5a\xd2\x4c\xd7\x50\xa9\x51\x57\x15\xd3\x92\xc2\xf7\x18\xf0\xd3\xd4\xbe\xfa\xcb\x1c\x8f\x90\x1d\xcb\x5e\x6b\xdb\x61\xb9\x88\x38\xf5\x63\x38\x6e\xd9\x04\x44\x73\x7b\x83\xe3\x4d\x66\x5d\xc5\x85\xc6\x1f\x7e\x58\x1a\x8e\x67\xc9\x68\xa9\xd8\x36\x47\xa1\xb8\x36\x3a\x31\xcc\x1d\x54\xcb\x9b\x57\x38\xd7\x42\x56\xd3\xa9\x7c\x5b\x2a\x2b\xcf\xcf\x27\xf4\xec\xdb\xad\xb0\x1f\x7f\xdc\xce\xa7\x10\xc5\x63\x07\xea\x19\x54\x22\xb5\x21\xdd\x6e\xb2\x83\xb6\xe1\x1c\xcc\xde\xcb\x4a\xef\x3c\x05\xbd\xac\xa2\x9c\xf0\xe4\x5c\x99\x7c\xbd\xf0\x6e\xba\xa9\xf6\xc8\xaa\x10\xd4\x33\xcb\x64\x0a\x7e\xa0\xea\x6f\xb0\x1f\xf2\x99\xe2\xdb\x1d\xe8\x61\xbb\x6f\xbb\x86\x2a\x9a\x73\x94\xe0\x92\x7a\xed\xdc\x45\xf3\x9c\xc1\xc8\xd5\x15\x8a\xba\x5c\xd1\x24\xd5\xbb\x93\xc6\x59\x4c\x67\x76\x40\xfa\xcf\x9c\xce\x4c\x13\xbc\xe0\x74\x5a\x15\xbf\x05\xa7\x53\xd4\xbd\xc7\x74\xe6\x29\x7c\x8b\x5d\x1d\x7c\xd3\xe9\xbc\xf7\x74\xe5\x2c\x81\x39\xf3\xa5\xeb\x4d\x73\x26\x89\x6e\x26\x42\xcf\xdb\xb7\x89\x75\xcc\xea\xfa\x12\x6d\xa0\xe0\x52\x9e\xad\xbc\x2d\x82\xed\x98\x34\xae\x74\x77\xe4\x07\x21\xa4\x3c\x71\xdd\xb5\xbe\x05\xbb\x81\x73\xde\x79\xb4\xe1\x0e\x3e\xa0\xab\xd8\x94\x1d\x84\xd4\x35\x88\x41\x1a\x9a\xac\x31\x6d\x97\x10\x77\xa2\xaf\xf3\x38\xca\xdb\x2e\xdf\x0e\xb4\x93\x90\xd4\x84\x32\x77\xa4\x57\x6f\xbb\x96\xbd\xc7\x04\x4f\x9b\x38\x14\xe1\x3f\x53\xae\xc6\xa0\x54\xea\xa7\xcc\xa8\xbb\xa2\xd7\x31\x60\x68\x34\x4b\xa5\x23\xa1\x15\x61\xc2\x52\xc4\x65\x24\xa4\x72\x42\x64\xbd\x21\x61\x76\x59\x04\x08\xfb\x79\x35\xc2\x2c\xf2\x3e\xc5\x0f\x02\x79\x26\x05\x90\x33\x17\x86\xbd\x20\xf9\x83\xa9\x64\xa2\x0e\xf5\x06\x80\xf4\x78\xd0\x05\xe1\xda\xa0\xcb\xb2\xf2\x64\xa0\x4c\x05\x68\x98\xc9\xab\x50\x9c\xb6\xd0\x56\x07\x58\xa4\xdf\x90\xc8\x0b\xc9\x61\x38\x9b\x09\xb1\x42\x93\x23\x5e\x39\xcc\x59\x7f\x3d\x38\x82\xf3\x32\x23\x3a\xb3\xcc\x75\x14\x43\xbf\x32\x45\xb7\x87\x94\x7e\x79\x59\xb3\x36\xa1\x9f\xe1\x21\xfb\xba\x94\xf4\xd1\xb5\x62\x76\x84\x27\x18\xa4\x70\xd8\x5d\x29\x09\xb0\xab\x28\x38\xed\x83\x43\x3b\xbc\x36\xab\x73\x09\x16\x5f\xf2\xb0\xf3\x94\x99\xd2\x7c\xf2\x1c\x6f\x61\x0a\xe8\xec\x80\xec\xb9\x33\x77\xdd\x0e\x70\x81\x75\x2b\xf6\xa9\xa7\x75\xfb\xb4\x6e\xd1\xdd\xd7\xed\x7d\x56\x07\x58\x08\x8f\x82\x64\xe1\xb5\x61\xc5\x84\x51\x34\x70\x91\x5f\x0f\x8e\x9c\x1c\x40\xf6\x20\x33\x38\xc0\x7d\xd9\x8e\x15\xb3\x93\x6c\x68\x7a\xb8\x1f\x4d\xd8\xd2\x21\x6c\x21\x88\x66\x49\x71\xe6\x21\x06\xab\x28\x7b\x10\xa4\xc4\xbb\x51\x72\xe2\xbe\x90\x07\x14\x88\x48\x5c\x5a\xb2\x79\xf8\x8f\xa2\x28\xc1\x68\x12\x5c\x13\x59\xc8\xd2\x3f\xf0\x04\x35\x85\x34\x24\x13\x22\x93\xc2\x5c\x64\x17\x5d\x82\x74\x4a\x4e\x3a\xc9\xac\x97\xe0\x7f\xcf\x70\x98\x5a\x55\x0c\x48\x15\xed\xa4\xac\x1e\xea\x28\x3a\x55\x83\x32\x4a\xda\xac\xcc\x57\xf5\x93\x9d\xcd\x86\x95\x2d\x46\x52\xb6\xda\xac\x91\x92\xc8\x1f\x4c\x60\x66\x3d\x1e\x9c\xa1\xdf\x36\x68\xbd\xd3\x20\x37\x74\x49\xf6\x9b\x9b\x40\xbf\xed\xb2\xf2\x4a\x40\x13\x49\xb4\x3d\xf4\x07\x03\x32\x81\x73\x14\x20\x53\xc8\x72\xd5\xad\xd0\x7f\xed\xea\x8f\xc3\xf7\xdd\x63\xf4\x7f\x5a\xab\x6b\x68\xca\x80\x26\x4c\x97\x67\x83\x79\xf8\xa5\x9f\xac\x81\x9c\x3c\xf5\x07\x15\xfe\x94\x23\x1b\x1f\xfa\xfc\xfa\x79\x96\xf0\xd0\xf9\x22\x10\x0a\x33\x57\x86\xb8\xc9\x02\x8f\x85\xec\xaf\x00\xb2\x7c\xfb\x4c\xd0\xb2\x56\xb2\xeb\xf1\x58\x08\x28\xe9\x3e\x12\x00\x25\x22\x98\x25\x19\x14\x08\x67\xf9\xc8\xc7\x66\x71\xf8\x12\xe3\x4a\x7e\x65\xd7\x6b\x9e\x16\x37\x4b\xb9\x60\xf6\x07\xfa\xe5\xda\x9d\x19\x88\xa8\x46\x63\x9d\x6c\x48\xe3\xe5\x8a\x19\x32\x0b\x53\x41\x3b\xe0\x57\x64\x42\x0d\x19\xc1\x1a\x40\xe9\x8b\x15\x9a\x72\x5a\x44\x58\xf9\x87\x56\xc0\xd6\x2c\xbd\x17\xe2\xed\x9a\xa1\x17\x68\xa6\x37\xf8\x4a\xe8\x05\x22\xa0\x28\x58\x64\xbe\x2e\xc6\x7b\xe6\xe0\x62\xbc\x07\xb7\x16\xe5\xed\x5c\xcc\x72\x91\x4a\xf2\xc3\x17\x64\xec\x47\x6d\x13\x05\x68\xd9\xe5\x96\x2f\x43\xa7\x61\xee\xa5\x37\x39\xd2\xab\x86\x1d\xda\xc8\x6c\xdf\xf9\xe1\x5f\x06\xed\xa9\x28\xd9\xcc\x10\x36\x07\x03\xfb\x20\xc0\x5c\xf7\xa3\xb0\xef\xa7\x1c\x66\x61\x0d\xcc\xa7\x70\x2a\x18\x0a\x2c\xd9\x91\x3f\xa0\x81\x8c\xd8\x42\xfd\x36\x5c\x66\x16\xea\x7c\xe6\x9b\x70\x04\x68\xb6\xc0\x95\x3b\x94\xd3\x59\x82\x8d\x0f\xbc\xc3\xa9\x92\xb8\x58\x5a\xc4\x10\x03\x16\x8d\xfd\x24\x85\xe7\xf9\x6b\x3a\x13\xaf\x4f\x4b\xea\x72\x5e\x41\xb5\x32\x75\x31\x3b\x63\xce\x60\x36\x4f\x62\x2a\x38\xb8\x29\x26\x03\xb7\xa1\xaf\x41\x69\x33\xa5\xdb\xe6\x82\x7a\xfe\x3f\xe3\x22\xc8\xe6\xa2\x60\xbf\x59\xb0\xdd\x2a\xe4\xdd\x03\x3d\x9c\xd1\xff\x7e\x34\xc0\xb7\x54\x3d\x78\x22\x4e\x6b\xf4\x52\x04\x4e\x12\x52\x77\xba\x6f\xbb\x2e\x28\x6c\xae\x6e\x05\x7d\x11\x58\xba\xb0\x61\x42\x04\x92\x77\x10\x38\xf8\x11\xb0\x01\x90\x0c\x27\x35\x02\x27\x98\x02\x66\x9e\x76\xaa\xa3\x6d\x1b\x4d\xdc\x2a\xde\x08\x0b\x18\x06\xd2\x89\x56\x3f\x76\x25\xeb\xc3\x7c\x1b\xc0\x9c\x00\x67\xaa\x7d\xa8\xc5\x8f\x13\xe4\x66\x32\x02\x8a\x5a\x14\xa9\x8a\x5d\xf2\x7d\x02\xb6\x9f\x0e\xfc\xb3\x89\x35\x0f\x03\x86\x2d\x29\x97\xb4\x55\xe3\x12\xe7\x89\x81\x40\x85\x2d\x11\x34\x1a\x70\x2a\xd7\xee\x66\xec\xd2\xfe\xea\xcb\xfc\xe6\x55\xeb\x95\x32\x7a\xb9\xba\x30\x06\x42\xd5\xe2\x38\xcb\xbc\xc7\x78\x8a\xfc\x14\x8d\x31\xe1\x82\x51\xc8\x57\x00\xcb\xf2\x41\x2d\x41\x61\xbf\x06\x86\x6b\xf2\x2d\x24\xce\x37\x93\x20\xa4\x46\xa2\xec\x10\x6f\x84\x4b\x54\x1f\x59\x25\x3a\x7d\x12\xfe\x94\x90\x26\x60\x7f\x4c\x8f\xbc\xc1\x25\xfa\xf1\x47\xab\x3e\x5e\x0f\xd4\x71\x78\x27\x5d\x46\x86\x89\xaa\x4c\x71\x9e\xcf\xf5\x66\x8b\x5e\x49\xbb\x45\xd2\x4c\x24\x11\x86\xd2\xec\x95\x85\xa0\x79\x73\x0f\x4b\xc8\xab\xab\xe4\x20\x43\xd3\x7d\xb9\x44\x2e\x90\xd7\x99\xe9\x17\x48\xe0\xf0\x7b\xae\x0e\x82\x5f\xc5\x53\x1b\x41\xd7\x29\xf9\x4e\x97\xf1\x8f\xb7\xac\x1e\x17\x6f\x6b\x7b\x20\xf9\xcd\x99\x01\x2a\x1f\xd9\xda\x9b\x67\xf9\x77\x4f\x4b\x05\x30\xbd\x63\xb2\x87\xdd\x0c\x05\xf5\xa3\xf1\x18\x53\xfa\x8f\x86\x5c\x34\x00\x51\x13\x43\x2e\xbd\x3c\xd1\x43\x12\x45\x25\x27\x6f\xb2\x8d\xc6\xfe\x95\xf4\xca\xea\x97\x68\x77\xfd\xa0\x0e\xe8\x42\x48\x29\x52\x3b\xbb\x78\x84\x0c\x0f\x8c\x0b\xd2\xfa\x64\x7d\x1a\xe6\xb8\x2e\x40\x89\x3f\xa6\xd8\xc3\x0f\x00\x06\x2a\x49\x9f\x86\x1f\xc5\x71\x70\x49\x65\x15\xce\x31\xac\x00\xf9\x55\x6a\x26\xe7\x4b\x96\x83\x66\xac\xd5\x62\x72\xcd\x5d\x7a\x96\x2f\xdf\xf4\x47\x78\x72\x37\xb8\x76\x81\x93\xa9\xcc\xc1\x62\x7a\x28\xc1\xb3\x82\xa0\x39\x19\x6f\xb3\x9c\x8d\xf4\x14\x43\x45\x2c\xfe\x56\x17\xc3\xfa\x51\x78\x89\xe3\x54\x91\x61\x69\xb6\x3b\x6e\x4c\x09\x16\x9f\xd4\xfa\xcf\xed\xb6\x7a\x48\xab\xa8\xce\xab\xe2\x65\x41\x7b\x98\xf9\x2e\x56\x2a\x6a\xf3\x8f\x75\xc2\xbb\x49\xc6\x47\xb3\x13\xf5\x43\x91\xc4\x6a\x1a\x25\x49\xd0\x1b\x63\xf7\x8a\xb5\x34\xb5\x98\x73\x53\x36\x50\xa6\x3d\x28\xfd\xc6\x4f\xe0\x7f\x18\x50\x90\x50\x9f\x93\x15\xdc\x91\x7e\x67\x0e\x4f\xd6\x4a\x5f\xf0\x4d\x47\xf5\x8b\xb2\x16\xd3\x3c\xa5\xec\x85\xc8\x32\xee\xc0\x7f\xe7\x14\x14\xab\xb2\x63\xba\x73\xd9\x6b\x30\x11\x5e\xb7\x4c\xb0\x17\x16\x72\xbd\x7a\x74\x7e\xdf\x3d\x5e\xb3\x57\x90\x58\x78\xd3\x5e\x42\x2c\x1c\x09\x28\x7d\x57\x39\x98\xe2\xf0\xf8\xf8\x83\x51\xad\xb8\x33\x99\x3c\xfd\x76\xc1\x6b\x12\x5c\xef\x85\x6a\xb9\xc2\xa6\x47\x74\x15\x27\x8b\x2d\x63\xe4\x5c\x37\x26\x2b\xd1\x7c\x03\x1d\xdc\x84\x1c\xea\xdc\xc0\xb9\x81\x2d\xf7\xca\x80\x5d\x01\x7e\x07\xc3\x40\x5f\xe3\x39\x70\x20\x09\x58\x42\x33\x80\x41\xf6\x38\x9c\x79\x51\x66\x18\x87\x11\x7d\xa3\x31\x40\x96\xb3\x1f\xe7\x71\x8f\xa2\x4b\x9a\x22\x2f\xae\xe9\xd8\xda\x5e\x46\x4b\x4b\x76\xdf\x0a\x6b\xf9\x4a\x1a\xd1\x7c\x43\x2e\x57\x8e\x39\xb5\x1c\xa4\xea\x24\x4c\x5e\x51\x26\x4e\x31\x36\x2e\xab\xaa\xac\x04\xfa\xfa\x95\x92\x6b\x56\xa7\xc2\x27\xf1\x86\x1f\x7b\x0d\x1d\x8d\x55\x4e\xa2\x54\x36\xef\x5e\x83\xb6\x03\x57\x1b\xe2\xa7\xfd\x76\x83\xf5\xdc\x46\x9c\x36\xd0\xac\xb8\x48\x65\x0c\xbb\x97\x3a\x88\xf9\xd7\x1d\x62\xd5\xf9\xee\x25\x17\xf2\x66\x56\xfa\xd1\x64\xea\xa7\xb0\xbd\x14\x5d\x86\xf2\xb6\xa0\x6d\x62\x92\xf8\x53\x74\x4f\xb4\x2d\xbf\xbb\x20\xf7\x50\x86\x83\x11\x6d\xfb\x98\x93\xb7\x83\x90\x25\xea\x72\xf1\x46\x85\xbe\x45\xf1\xc2\xdc\x77\x8e\x5a\x46\x8e\xb4\xa4\x2c\xc1\xec\x8b\x2d\x50\x23\x11\x77\xb5\x0a\xe4\x9d\xed\x18\x0b\xfd\x35\x0f\xb1\xa4\xb8\x53\xd5\x72\x25\x45\xab\x31\xb4\xf7\xa7\xd5\xeb\x56\xa3\x5d\x6b\xf7\xd7\x20\xb1\x41\xbb\xd5\x6e\xb6\x86\xad\xe1\x59\x99\xab\xe2\x01\x34\x7f\xc8\xfa\xe1\x38\x47\x16\x40\xc1\x39\x16\x8e\xc3\x97\xa8\x9b\x31\x32\x1a\xd6\x66\xf1\x3d\x2f\x6f\x8d\xc9\xfe\x4a\x8b\x0a\x8f\x7c\x9d\x64\x74\x7a\xe7\x25\xa3\xc6\x6c\xe0\x0b\xfa\x0e\x6b\xf8\x61\x03\x38\x98\xc2\xa8\xb6\xf4\xa6\x7e\x9c\xe0\x92\xb2\x50\x73\x2e\x26\xe3\x44\x51\xfc\x64\xd5\xac\x5e\x09\xa4\x38\xa2\x31\xbc\xe6\x2c\x3a\x4a\x18\x06\x32\x79\xea\xd5\x3c\x88\xfc\x32\x4e\x3a\x0c\xb3\xa4\x10\x06\xb8\x13\x9c\xa4\xd4\xb6\xc1\x1f\x5b\x16\xa8\x06\xf3\xb4\x7a\x86\x36\x36\x50\xb6\xf6\xd0\x8f\x3f\xea\xed\x9e\xd6\x58\x19\xbe\x26\x5d\x2a\xa8\xed\x6b\x7a\x81\x61\xb6\x8c\x54\x0e\x63\x2c\x7e\xad\x45\x66\xca\x53\xf7\x50\xb3\x9c\x63\x5d\x17\x5d\xb2\x23\x3a\x5c\x05\x65\x30\xcc\xf2\x06\xfc\x29\x34\x50\xd5\x6f\xad\x8d\xe2\xca\xad\x4e\xad\x5d\x8c\x51\x58\x8f\x46\x8e\x63\x90\x27\x9d\x4e\x54\xd1\x3c\xf7\xae\x88\x2f\xc2\xab\xd8\x9f\x4e\x41\x8e\xf4\x53\xd6\xbc\xac\x32\x41\x3e\xd9\xe9\x13\xc9\x2b\x2d\x77\xf5\x2a\xae\x3e\x86\x2b\x5b\xe6\xf0\x63\xfb\x54\xd4\x81\xe4\xce\x97\x3d\x42\xe8\xe1\x32\x7e\x9e\x54\xcf\x75\x04\x72\x6f\x59\x67\xa9\x43\x68\x38\xa0\x54\x23\x0e\x18\xd9\xc5\x8e\xe5\xe0\x94\x17\x22\x4a\xf7\x5e\x04\x84\x3a\x86\xa8\x26\x4d\x6c\x6e\x50\x29\x76\xed\x40\xe6\x8d\x79\xd3\xdd\xc7\x43\x35\x53\x3e\x59\x8e\x3a\x39\xde\xe7\xac\x69\x6a\x83\xc2\x7e\x67\x7e\xe7\x7f\x91\x18\x2e\xf6\x2d\x6c\xf3\xcf\xdd\xc0\xc8\xb2\xb4\x6b\x54\xcc\x65\x25\xfc\x2b\x4d\x6d\x84\xe2\x6a\xe9\x38\x85\x3d\x5e\x83\x59\x90\x1a\x5d\x9d\xf0\x4d\x1b\xf7\xc4\x6a\x73\x48\x03\x39\xca\x0e\x8b\x73\xac\xdb\x8b\xf5\x6e\x21\x74\x16\x8a\x9e\xb3\x6d\xb3\x5f\x97\xa2\x1b\x44\x99\xf3\x89\x2d\x00\x9a\xd5\x67\xd5\x10\x4b\x32\xcf\x0c\x11\x20\x81\x75\xf6\x2e\x92\x49\x17\xfa\x97\xc1\x84\x2b\x60\x03\x0a\xb3\x37\x22\x1c\x57\x38\xe6\xba\xf6\xa3\xe2\xdb\x69\xde\xa6\xad\xec\xaf\x66\x41\xae\x5a\xb4\x7c\x22\x64\x25\xfa\x56\x09\x2e\x2d\x45\x24\x1d\x21\xa3\x17\xb3\x0c\xd5\x0a\x66\x80\xe0\x42\xd4\x2c\x26\xf4\x81\x59\x49\xf6\xca\x52\x58\xd2\x05\xea\x16\xd6\x96\xd2\x92\x5e\x90\x90\xde\xd0\x72\x5c\xbb\x2d\x7c\x6c\x61\xf7\xd0\x89\x98\x38\xa1\xf8\x92\xaf\x65\xd0\xa3\x6d\x4f\x32\x01\x88\x1d\x4a\xbb\x68\x92\x1e\x21\xb5\xf7\x5f\x71\x9f\xd2\x02\xb4\x88\x48\xc7\xdf\x60\x6f\xca\xa2\x2a\xcf\x67\xd3\xdc\x7b\xde\xc2\xa6\x39\xd9\xb1\x30\x0a\x92\x47\xfd\x9d\x59\xf6\x43\xa3\xa8\xef\x4b\x0f\xb8\xa5\x38\x63\x17\x38\x22\x0c\x7c\x83\x5d\x85\x69\x1c\x24\xd5\x82\xbc\x98\x34\xc0\xf2\x4e\xc1\x6e\xbf\xe1\xfc\x2a\x23\x9f\x71\x13\x5b\x73\x8c\x53\x98\x1b\x86\x3c\x79\xca\x26\xa6\x44\x5d\xa4\xc3\x92\xed\x4d\x12\x93\x51\x14\x3e\xd6\x6d\x42\x34\xb1\xb0\x36\xc6\xca\xd6\xf4\xb1\x52\xef\x5f\x40\xc7\xe4\x27\xc9\x6c\x82\x07\xea\x7d\xa2\x3f\x8e\xb1\x3f\xb8\x91\xf6\x3b\xe5\x40\x36\x0b\x69\xda\xca\x02\x11\xcd\x16\x63\x7b\x76\xfe\xb5\xd0\xa1\x89\x30\x2e\x30\x51\x8f\x13\xbc\x30\xaf\x77\xeb\x8b\x66\xe1\xa2\xb0\xfe\x44\x89\xdb\x20\x79\xaa\x42\x3a\xe0\x54\x80\x04\xf1\xdb\x79\xc0\xb9\xa1\x53\x92\x57\x0f\xab\x6c\x4b\xe5\xcd\x62\xd7\xc8\x8b\x70\x4e\x08\x1b\x6e\x13\x42\xd9\x93\xb9\x54\xf5\x8b\x0d\x94\xab\x1d\x65\xd0\x72\x94\xa2\x86\x66\xc2\x7a\x43\xf2\xde\x6e\x22\x31\xef\xca\xe4\xcb\x60\x08\xf7\x25\xf4\xdf\xfc\xcb\x92\x79\x56\x18\xe6\x85\xc9\x7b\x0a\x9d\xb4\x52\xec\x9e\x64\x8b\x80\x87\x3b\x7d\xd2\x18\x59\xcb\x7b\x3f\x73\x85\xc1\x94\xc5\x0b\x2a\xae\x8e\xe5\x35\x98\xe5\x05\x7b\x00\x39\x85\x34\x03\x80\xf3\xbd\x42\xb2\x40\xe5\x98\xda\x56\x04\x21\xb3\xe4\x65\x76\x00\xcc\x64\xe6\x02\x87\x60\xcc\x9b\x0f\x4d\x44\x29\x77\x00\xa3\xa1\xb3\xf3\x61\x99\x3a\x03\x50\x61\x49\x42\xd2\x26\x6a\x37\xc1\xe4\x18\x3e\x70\xfb\xd9\xbd\x21\x8a\x26\x01\x91\x11\x3c\xe4\xd3\x4f\x57\xc1\x78\x8c\x7a\x58\x34\x38\x40\xb1\x1f\x0e\xa2\xc9\xf8\xe6\x81\x0e\xf7\xd4\x6a\x82\x0d\x93\x87\xf6\x7e\xf6\x60\x4a\x49\xe3\xdf\x80\x0b\xd1\x49\x1e\x98\x2c\x48\xa2\xc6\x0a\xbe\xc6\xfd\x59\x8a\x4b\x4b\x3c\x1a\xd5\x92\xc7\x12\x77\x78\xcc\x7c\xcb\x21\x16\x3d\x10\x74\x0f\x2d\x91\xe1\x20\xff\xbf\xe4\x3e\x33\x53\x30\x32\x77\xe3\xd4\xec\x71\x12\xf5\x18\x75\x51\xc5\xa6\xdd\xa8\x9f\x4e\x33\x9b\x65\x87\xa2\xfa\x07\xe7\x55\x92\xa1\x44\xa6\x70\x4a\xed\xe6\xaa\x91\xd6\xdc\xe2\x56\x47\x97\xb6\xb4\xae\x4d\x69\x85\xc6\x9b\xa5\x89\x07\x32\x05\xae\x88\x71\x97\xa5\x41\x66\x0b\xe9\xb6\x5c\x61\x89\xbc\xa5\xf1\x00\xfc\xad\x01\x6b\x09\x6d\xa6\xf9\x18\x80\xdd\xb4\xa1\x26\x17\xc9\xa0\x99\x82\x9c\x27\x93\xe5\x63\x8e\x5e\x9a\xfa\x6c\x25\x35\x74\x96\xc2\xd9\xee\x2c\x75\xc4\x44\xa9\x05\x0f\xe3\xd9\x91\x5a\x48\xd1\x77\xd3\x6a\xdb\x34\x03\x8a\x8a\x7b\xc0\xf8\x32\x67\x79\x1a\x4b\xf6\x04\x2c\x87\xf8\x75\x77\x7d\xb8\x25\x4a\x9c\x50\x88\xdb\xbf\xd9\x34\x5c\x8f\xa8\x1f\x7f\xbf\xb5\x73\x8b\xc8\xf6\xc9\x2d\x28\x6d\xbb\x70\x26\xe5\x71\x66\x9b\xbf\xc5\x2d\xa4\x15\xb7\x74\xd8\xed\xfc\xf0\x65\x30\xec\x48\xdb\xb3\x44\x21\x0b\xaa\xc7\x99\x4b\xd5\x22\xfb\xf2\xf7\xa1\x2f\xcf\x95\x0e\xbe\x03\x75\xc4\x5f\x44\x6d\x6e\x59\x7c\x85\x34\xc9\x4b\x7c\xa8\x5d\x61\x65\x1f\xbf\x61\x0f\xfd\xf1\xc8\x1a\xec\x6c\x3b\xfa\x46\x0a\x07\x6d\x77\x8d\x52\x97\x72\xd7\x26\xbb\x10\xf0\x44\x6c\xe1\xe2\x8a\x84\x3d\x1d\x5e\x21\x63\xb0\x67\xba\xed\xb9\xbc\x3b\xa9\x18\x4b\xfb\x66\x74\xa9\x02\x5b\xac\x82\x41\xc5\x1a\x92\xc0\xa9\x98\x57\xf4\x25\xee\xeb\x0c\x39\x00\x84\x31\x3f\x6a\xfb\x92\x1e\xdf\x40\x63\x3f\xb8\xa6\xc9\x40\xa0\x82\x75\x48\xa5\xb3\x35\x35\xcc\x54\xa0\xbb\xf4\x26\xd6\x13\xdf\x3d\xf4\xc1\x7f\x02\x3f\x7e\x60\x05\xf1\xf7\xce\x98\xbf\x47\x3d\xb1\x8d\x19\x2e\xaa\x28\xbe\x17\x63\x7c\x70\x14\x4d\x45\xf1\x43\x31\xee\x82\x7a\xe2\x6f\xce\xbb\xbf\xb9\xb2\xf8\xdb\x6f\x15\x9e\x62\xdb\xe3\x38\xa1\x3d\xdc\xde\x51\x48\x1f\xee\xbe\xbf\xb0\x6d\x1d\xf2\xf8\x16\xdc\x3d\xf2\x14\xe4\x99\x2a\x4f\x64\xba\x94\x53\x5a\xb2\xfc\x95\xb7\x67\x5e\xab\xf1\xbd\x26\xa5\x7c\xf0\x1c\x94\x8b\xe6\x9e\x54\x72\x4e\x1a\x88\x99\xe9\x27\xb5\xb4\x93\xbc\xa2\x23\xf1\x24\xe8\x47\x33\xe0\xe2\xa7\x9a\x7c\x72\xdf\x4f\x47\x1e\xb2\xa4\xa0\xcc\x8e\xd7\x1f\xa2\xbe\x3f\x46\xd3\x68\x7c\x33\x0c\xc6\x28\x1a\x22\xba\x69\xb1\x53\xbc\xe5\xc8\xcb\x62\xdb\x6f\xa8\x05\xb5\x86\x15\xc6\x24\x5e\xef\x90\xf7\xb7\xaf\xcd\xd8\x41\x92\xad\x65\xef\xb3\xc1\xd4\xc0\x46\x70\xd6\x23\x33\xa8\x13\xf1\x4e\x65\x1a\x47\x69\x44\x3e\xa1\x0d\x72\xfa\xd0\x0b\xb0\x7a\x68\x03\x85\xf8\x8a\x20\x90\x0f\x21\x9c\x8d\xc7\x8e\x85\x22\x30\xc8\x96\x89\x14\xef\xc8\x16\xc9\x93\xcf\x49\xbe\x92\xdb\xa9\xd8\xfe\x10\xf4\x62\x3f\xbe\x99\xa7\x23\x97\xf2\x83\x3a\x41\x41\xb6\x50\xa6\xf5\x24\xc2\x05\xef\xb2\x3f\x46\x41\x38\xc2\x71\xa0\x04\x70\x55\x22\x3a\xe8\x79\x46\xcd\x08\xa3\xe6\x74\x16\x08\xfb\xc7\x63\x0c\x83\x7b\x9c\xf0\x33\x18\xf9\x29\x47\x88\x85\xf2\xa0\x62\x90\x71\xaa\x44\x28\x2f\x0e\x20\x97\xbb\xa2\x4b\x1c\xc7\xc1\x00\x27\xe8\x90\x2a\x44\x02\x9c\x50\x06\x3e\xbd\x41\x41\xc8\xb2\x19\x67\x08\x14\x68\x41\xcf\xd5\x70\xb2\x28\x00\x43\xe6\x72\x94\x5b\x24\x6a\x20\x99\xa8\xfd\x9b\x13\x4a\xc2\x8a\x74\x93\x63\x92\x28\xfb\x8b\x05\x78\x3c\xe8\xa0\x25\xc8\x94\xb5\xa4\x1b\x8e\xd8\xdb\x24\x7f\x13\x9c\x8e\xa2\x41\xae\x8f\xbc\x54\x5a\x8f\x91\x6f\x73\x3c\x43\xc8\x0c\x67\x48\xd1\x57\x0c\xb2\xf9\xbc\x3a\x83\x18\x4e\xfd\xab\xd0\xfc\x22\x31\x12\x22\x2c\x64\x69\xf5\x5c\xe6\xc4\x9b\xb3\x8b\x09\x0e\x2d\xa6\xc3\x64\x47\xc9\xc7\x02\x65\xcc\x87\x9d\xbb\xb2\xf2\xd6\xf4\x0f\x56\x04\x98\x99\x14\x77\xfd\x0a\x84\x63\x69\x6c\xc7\xe9\x07\xde\xe4\xc8\x4f\x0e\xae\x42\x46\xf6\x37\xa5\x25\x52\x73\xa9\x2c\x7c\x9e\xc8\x23\x6c\x82\xbc\x3c\x79\x31\xb7\x1f\xb4\x56\xee\x74\x5b\x6a\xfd\x3f\xc9\x6c\x4a\x44\xad\x30\x48\x2b\x3e\x11\x4e\xd9\xd6\xe7\xc7\x17\x33\x32\xba\xd6\xf1\x40\x96\x0c\x0a\x39\xe3\x94\x79\xdc\xc6\x4b\x09\xca\x38\x7a\x40\x95\xc2\x7c\xd2\xe9\x2a\x35\x21\xc8\x1d\x54\xf6\x03\xc7\xb6\x83\xb8\x62\x7c\x88\x63\x1c\xf6\x49\x03\x30\xce\x53\x7d\xbd\x1a\xc3\xc0\xe4\x62\x1b\x40\xe7\x3e\x83\x6c\xa9\x31\x6c\x4c\x75\x1b\x56\x4a\x22\x33\x4d\xaa\xf2\x9e\x85\x74\x1c\x60\x02\xe9\xaa\x35\x43\xa0\x6e\xf2\xf9\xc8\x32\xd8\x94\xca\xe2\x1a\x8e\x88\xd2\x10\x52\x0e\x80\x94\xca\x7f\x65\x5e\xc9\x23\x96\xa3\x0d\xc6\x36\xf9\x9d\xc5\x5c\x5e\x44\xcb\xe5\x73\x3c\xb3\x11\x58\x72\x59\x9c\x6c\x73\xe5\xf2\x08\xea\xd2\x1a\xe1\xef\xd4\x75\xe2\xa4\x1a\x5e\xfc\x2e\x64\x93\xe7\xae\xee\x98\x2b\x74\xc0\x98\x19\x4b\x12\x00\x24\x05\x26\xf4\x83\x01\x4a\xa2\x09\xa6\xa9\xa7\xd0\xd5\x08\x87\xe8\x26\x9a\xc5\xc2\xcc\xde\x27\xe2\x2c\x05\xfe\xc0\xb1\x73\xef\xbb\x0b\xea\x8e\xce\x79\x7b\x19\xa2\x0c\xa0\x52\x31\x47\x46\x0c\xfd\x1d\xb7\xbb\xb9\x68\x14\x9a\xd3\x6e\x34\x25\xc2\xce\x34\x93\x7b\x98\xbc\x73\x0f\x71\x4a\x02\x06\x1a\x26\x45\xa6\x9a\x80\x26\xf2\x81\xa7\x94\xad\x4e\xba\x7f\x16\x95\x5f\xee\x38\xee\xd0\x88\x72\x89\x2d\xfa\x67\x5d\xe3\x22\xe2\x21\xbf\x6c\xfb\xe8\x4f\xc0\x68\x62\x4e\x3d\xc4\xb6\xea\xac\x98\xbe\x59\xcb\x00\xcb\xb9\x5b\x2c\x99\xce\x53\xb9\xf8\x19\xda\x90\xda\x57\x3f\x2d\x90\xba\xc8\xb1\xc9\x6e\xa3\xab\x28\x5c\x4a\xa9\xfc\xcc\xdd\x1d\xa5\xe0\x85\xe3\x28\x9a\x22\xbf\x17\x5d\x5a\xb6\xc1\xfc\x2e\x2f\x71\x68\x4b\xee\x0e\x03\x17\x15\xad\xca\xfd\x14\x6f\x0b\xe4\xd5\x2a\xb4\x78\xc4\xe1\x04\x7a\x0a\xf6\x2f\x8b\xac\x1b\xdb\xc6\xd7\x1f\x47\x21\x7e\x04\x8e\x07\x70\xd1\x46\xb6\x87\xc0\x8b\x02\x3b\x19\x29\x36\x77\x23\x93\x73\x91\xa8\xc2\x11\xe7\xa7\x56\x7b\x32\xfb\x19\xd9\x7a\xbb\x1f\x22\x1f\x3c\x6f\xb5\x58\x84\xb9\x91\x85\x8c\x38\xef\xf9\x20\x6c\xe1\x69\x84\xf1\x83\x1a\x0e\x31\x09\x2e\xc2\x60\x18\xf4\xfd\x30\x65\x01\x25\x03\xda\x7b\x00\x49\xdb\xb1\x1d\x93\x7f\x91\x3c\x88\xe9\x59\x59\x7e\xf3\x00\x61\x63\xcc\xe6\x75\xb2\x70\x84\xc1\x97\x4d\xaf\xe6\x8c\x35\xb2\x9a\x85\x89\x91\xd2\x6e\x30\xe6\x0e\x1a\x7e\xb0\x54\x2f\xb2\x7f\xb6\xb2\xb1\x1b\xb6\x30\x0e\xed\x7f\x71\x00\xa7\xd5\xeb\x6a\xb5\x5a\xab\xd6\xab\x0d\x0f\x55\xaf\xab\xcd\x6a\xab\xda\xae\xae\x9d\x3d\x1a\x60\x0f\xb5\x0b\x87\x5e\x61\xe1\xeb\xf8\x8c\x18\x2b\xf6\x8a\x39\x04\xc3\x72\xe5\x0f\xf4\xdf\xaf\x5f\x21\x66\xaf\x26\x6a\x0c\x51\x49\x4c\xef\x0f\x1b\x16\x45\xa1\xfc\x07\x50\x25\xa3\x21\xfe\xb3\xb0\x31\xa9\x0e\x80\x92\xc7\x18\x87\x17\xe9\x88\x9a\x1e\x39\xb9\x48\xf1\x98\x31\xd9\x42\x59\x2c\x52\xcc\x76\xd8\x8f\x06\x84\xde\x31\xfd\xa1\x93\x3b\xbc\xce\x8f\xfd\x29\x08\x00\x87\xfd\xca\x2e\xbe\x76\xb7\x39\x2f\x80\x4c\xa1\xd5\xbe\x70\x70\x97\x8c\x58\x0b\x44\x76\xb1\xc4\x35\x98\x17\xd6\xc5\x52\x45\x19\x92\x4f\xe9\x70\x7d\xa1\x68\x2e\x6c\x2a\x9c\xb1\x5c\xf8\x54\x7d\xfd\x8a\x76\xf1\x75\x6e\xf8\x96\x39\x04\xd4\xf7\x53\x1c\xb2\x3d\x5f\xa5\x20\x07\xf3\x77\x13\x92\x74\x0f\x9b\x0d\xf8\x09\xe3\x86\x12\x65\x42\x9a\xdf\x45\xef\x75\x8b\xe2\x52\x84\x36\x04\x76\x35\x1e\x3f\x43\xbc\xa9\xbb\x53\x9a\x41\x49\x9d\x29\xd1\xc0\xce\x8b\x85\x23\x21\x03\xfb\x8b\xc1\xb0\x2c\xbe\x8a\xe9\xc8\x17\xa1\x0e\x32\x12\x73\x97\x0e\x92\xe3\x8c\xc7\x28\x3c\xc7\x01\xfc\x58\x65\x49\x14\x7e\x56\xc7\xe8\x54\x77\xec\x4f\xa6\x08\x5f\x43\x24\xc9\x5e\xa0\x77\x8e\xde\xab\x92\x32\xe6\x6d\x03\xbd\x4f\xed\xdb\x82\xa4\x28\x88\xff\xc3\x11\x28\x1d\xea\x13\x91\x34\xc4\xb0\xd5\x22\x3f\x45\x3e\x4a\x83\x89\x45\xe2\xb6\x85\x64\x97\xbb\xeb\x4e\x0a\x21\x0f\x0e\x29\x8a\x36\x08\x7a\x6c\x16\x4e\x03\x1e\x15\x9b\xfc\x53\xaa\x37\xd1\x0a\x2a\x05\x14\xe3\x97\x68\xbd\x5c\x16\xd1\xb2\x9d\x52\x3c\x85\xa3\xf6\x78\x19\x05\x22\xdc\xf6\xd7\x8d\xac\xe9\x37\x6f\x78\x1b\x96\xf2\xa2\xd1\x02\x82\xbf\x73\x5b\x92\xc7\x94\x2e\xae\x7b\x8d\xa9\x3b\xca\x7d\xd1\xee\x6f\x20\x73\xb0\x8b\x64\x0c\x36\xa9\x50\x6c\xb6\xcb\x1b\x2a\x9a\xb6\x1c\x2b\x7e\x10\xfa\x3d\xfd\xe4\x21\x1d\x00\x8a\xb2\x53\x1a\x83\x83\x08\x81\x8a\x60\x18\xa4\xf7\x15\x05\xb3\xc5\x29\x56\x97\x83\x49\x91\xcf\x45\x43\xf7\x5a\x58\x93\x29\x47\xd9\xe2\x22\x39\x99\x8c\x9d\x61\x58\x44\xb5\x53\x01\x83\xc7\x99\xdf\x80\xa5\x43\xff\x80\xf4\x1b\x75\x42\xfa\x89\xc2\x17\x2c\x04\xaf\x88\x52\x1b\x68\xdf\x4f\x47\x95\x3e\x0e\xc6\x59\xcd\x55\xb4\x40\x44\x22\xfb\xf9\xb7\xd0\xce\xe3\x30\x47\x32\x8e\xbf\x77\xb5\xfb\x64\xc7\x5d\x99\x16\x8c\xf3\xae\x4a\x0b\xf3\xce\xb9\x32\x58\x38\xa9\x51\x5c\xe5\xe8\xe7\xe6\xc9\xb9\x62\xd2\x08\x33\xbf\xaf\x3a\x4d\xea\x48\xbd\xc5\xa7\x40\x12\x1b\x86\xc1\x78\xcc\xc3\xce\x32\x37\x09\x38\x6f\xcd\x17\x4a\xf8\x61\x2e\xb4\x1d\x7a\x65\x50\x4e\x17\x9f\x42\xb3\xcc\x20\x15\x22\x94\x87\x32\x3e\x2b\x70\x04\x63\xae\x20\x35\xf7\x49\x8b\x96\x90\xc9\x24\xb4\x1f\xb1\x64\xf6\x60\x1e\xa8\xc8\xd7\x58\xbd\x21\x9f\x9c\x5f\xb9\xa3\xcc\x9f\x5f\xa1\x0d\xf2\x5f\x47\x02\xb5\xc9\xf9\xef\x64\x9b\xb9\x6e\xf8\x03\xdc\x5e\xef\xe9\xe1\xd7\x45\x31\x3f\xf9\x82\x64\xce\x91\x73\x4f\x50\xe0\xee\x8e\xb6\x5a\xaa\x5e\xbf\xaa\xb6\x5f\xa1\x97\xa4\x0b\xbf\xc3\x9e\xbe\xb3\xb3\xb3\x53\x46\xcb\xf4\xc5\x4f\x3f\xa1\xea\x75\xad\x0a\xdb\x3d\x41\xc0\xb1\xdd\xd3\x2e\x96\xaa\xd7\xcd\x76\xab\x4a\x81\x5d\xe9\xc0\xae\x8a\x02\x83\xe1\xc5\xc9\x0c\x3c\x7d\x4a\x80\xc6\x9b\x37\xb4\x26\x5a\x46\x30\xd2\xb9\xf5\x59\xdd\xd5\x0d\xa8\xc3\xfe\xf2\xcb\x2e\x6f\xa0\x6a\xa5\xe5\x2c\x03\x63\xca\x8a\xbe\xa4\xf6\x36\x9c\xda\xca\xe8\x27\x54\x69\xa1\xff\x42\x35\xd4\x41\x2b\xb5\x22\x22\x8a\xc1\x39\x54\x71\xc3\x43\x71\xdf\xef\x8f\x30\xcb\xae\x33\x5f\xe0\x20\x35\xcf\x09\x3d\xc6\xa5\x12\xad\x4a\x8e\x4a\x0a\x92\x64\x37\x91\x06\xc3\x7e\xc5\x44\xab\x6e\xa0\xf3\xb8\x44\xcb\x03\x41\xae\xf5\xd6\x2c\x7d\xba\xca\x72\xf8\x94\x44\xf9\x0c\x3e\xfa\x8a\xaa\x05\xc3\x9a\x87\xf8\x4a\x72\x76\x82\x5b\x47\xa6\x00\x09\x79\xfa\x9e\x67\xda\x48\xda\x9d\x4f\xd9\xd1\x7e\x9e\x21\x0d\x0e\xfb\x60\x48\x43\xff\xb5\x1b\xd2\xec\xe2\x6b\x53\x13\x60\x03\x47\x0a\x6e\x50\xa0\x15\xfa\xbb\x58\xfc\x4d\x5d\x7d\x31\xc2\xd7\x85\x55\x18\x05\x4e\x9e\x0b\x46\xd5\x2c\xd4\xfa\x43\x31\xf2\x11\xbe\x36\x43\x68\xb2\xf1\x93\x8e\xf6\xf3\x13\x09\x59\x03\x67\xde\xf5\x98\x7a\x55\xf8\xe4\x99\x2c\x7a\x8c\xa4\xb3\x6e\x02\x1a\xe1\xeb\xee\xc8\x8f\x0b\xe7\xd9\x4a\xe6\x1e\xe8\x20\x47\x5a\x40\x0f\x72\x57\xf7\x3c\xc4\x71\xec\xd8\x1a\x07\xb0\x04\x48\xb3\x9c\xa9\x7d\x6a\xed\xb2\x8d\xdf\xd9\xaa\x92\x76\xaa\xc3\xfc\xba\x0e\x06\x21\xc0\x7d\x8e\x82\xb0\xb4\xb4\x74\x87\x88\x9b\x12\x85\xd3\xf5\xb6\x88\xa6\x87\xaf\x14\x4a\xb8\xc5\x17\x8c\x43\x78\xfa\xf3\xa5\x26\xbe\xd8\xa8\xcd\xb6\x58\x8f\xc5\x23\x65\xd2\x2a\x8b\x25\x4a\xa1\x75\x3e\xf0\xa3\x0b\x7d\x64\x47\x99\x45\x56\xcd\xd5\x22\xa9\xe9\xe4\x46\xd9\x16\x5a\xcf\xc9\x8f\x49\x57\x4b\x03\x34\x13\xd0\xe9\xbd\x30\x65\x9d\xad\x24\xb3\x5e\x92\xc6\xa5\xc0\x43\xf5\xb2\x07\x49\xf8\x32\x95\x05\x59\x51\xeb\x65\x9b\x03\xee\xc2\x7b\x9e\x32\x4c\xab\xa8\x5e\xd4\x7d\xf6\x83\x9f\x06\x61\xad\xd8\xa6\xc5\xca\xf2\x7d\x4b\x3c\xde\x6d\xeb\x62\xd5\xff\xbc\xdd\xab\x28\x02\x0f\xb5\xa6\xc6\xd0\x9e\x7d\x0f\xa3\xb8\xfc\x47\x6d\x63\x74\x38\xbe\xe3\x9d\x4c\x42\x90\xee\x48\x74\xea\x2a\xc3\x38\x9a\x90\xb7\xdd\x68\x80\x61\x93\x2a\xba\x21\xc9\x00\xef\xb1\x27\x29\x74\x7b\xf7\x6d\x49\x90\xe3\x42\x8b\xe1\xbb\xde\x9c\xd8\x2a\xa2\xfb\x93\xbc\xdc\x8a\x6f\x51\xa2\xd6\x62\xbb\x94\xa8\x26\x36\x2a\xf1\xe6\xb1\xf7\x2a\xad\xe9\x79\xb9\x9c\x03\x49\x8b\x9e\xf5\xb6\xd2\x67\x04\xbd\x99\x96\x02\xbe\x26\xf4\xad\xca\xae\x5b\x5c\x78\xab\xd2\x10\x2e\xba\x53\x7d\x3a\xd9\x59\x59\x2f\xb6\x51\x7d\x4a\x87\xeb\x62\x9b\x62\x0f\x77\xdb\xa4\x68\xa3\x7f\xde\x1e\x55\xb0\xfd\x87\x5a\x59\xb3\x74\xb8\x6e\xdf\xa0\xc8\x28\x3e\xe6\xf6\x94\xc6\x37\x39\x06\x46\x03\x4c\x8e\xe8\x9f\x8e\xf6\xba\xdc\xd3\xa9\x84\x93\xbe\x3f\xc5\xa5\x9c\x8d\xd3\x64\xcb\xa8\xef\xa7\xfd\x11\x2a\x99\xe9\xa3\x01\x85\x51\x1c\x5d\x01\xdd\x42\xc6\x95\xd2\xd2\xbe\x3f\x1e\x46\xf1\x04\x0f\xd8\x34\x0c\xfc\xd4\x37\x53\xd0\x2d\xce\xc0\xe5\x49\xbd\x3b\xff\x66\x73\xb5\x08\x99\x7c\xd7\xcc\x1b\x28\x8c\xb2\xee\x8c\x0c\x8b\x33\x6e\x56\xc7\x65\x0c\xa0\x6c\x0d\xb3\x90\x51\x0f\xb5\x10\x50\xe8\x8a\xc3\x29\x17\x0e\x40\x23\x52\xf0\x42\x2e\x4c\x3c\x60\xd9\xcc\x24\x2f\x74\x67\x26\x5e\xc9\x4e\xf6\x46\x4a\x89\x36\x99\x25\x29\xea\x61\x14\x90\x11\x9d\xe0\x30\xa5\x79\xd6\x7c\xb8\x5e\x8f\x71\x2a\x3c\x16\x0a\xe5\xf6\xd5\xf2\x74\xaa\xca\x7d\x9a\xe3\x90\xba\x56\x65\x09\xe2\xbf\xe0\x69\x8a\x66\xe1\x94\x27\x0d\x54\xb3\x83\x4a\x36\x2d\x55\x0b\xf7\x7d\xcb\xc6\x01\x32\x0d\x6e\x8a\x51\x10\x5e\x62\xae\xcf\x05\xcd\xe0\x20\xbb\x2b\xb3\xe6\xd1\x46\x7a\x89\x25\xd1\x66\x49\x4c\xd3\x08\x05\x69\xc2\xbd\x62\x10\xa1\xe0\xfb\xde\x31\xf5\xac\xc8\xd3\x84\xb8\xee\x4b\xa6\x42\x59\x77\x99\x79\x1f\x02\x2b\x65\x9b\xcd\x00\x64\xe0\x64\x9e\x8a\xda\xce\xaa\x33\x25\x5a\x3e\xdc\xf2\x53\x9f\x0b\xeb\xd5\xa2\x92\xe6\xe6\x60\x90\x40\x1b\x3c\x2f\xb8\x63\xa4\x19\x2d\x14\xdf\x14\x45\x90\x05\x23\xf3\x38\x33\x76\x41\x74\xcd\x33\x27\x00\xca\x2f\xa9\x4f\x89\x2f\x59\x50\x52\x7b\x62\xe0\x78\x8f\x33\x99\xe7\x14\x9d\xd2\x92\xc9\xef\x0b\xd5\x9b\xbf\x37\xb2\x92\x45\x92\x99\x9b\xee\xf5\x59\x3a\x3a\x39\xa0\xa8\x34\x40\x2c\x98\xa8\x0a\x4a\xf6\x71\x06\x32\x9a\x13\x27\x92\xd1\x9a\xc4\x94\x01\xc3\xf9\x91\xd2\x36\xa1\x6b\x2e\xf2\xe5\xa6\x44\x36\x60\x06\xd1\x2e\x6f\xa8\x49\xd2\x8b\x52\x30\xcf\x75\x9a\x20\xff\xd2\x0f\xc6\x10\xb1\x8b\xf2\x05\x60\x76\x6e\xaa\x39\x91\x9c\x55\x82\xf0\x32\xfa\x82\x13\x3d\xc9\x70\x89\x25\x07\xf6\xd0\xd5\x28\xe8\x8f\xac\xac\xba\x77\x93\xc3\xaa\xcd\x56\xf9\x42\xe9\x45\xd1\x18\xfb\xe1\x2d\x1a\x44\x3b\xe3\x59\x32\x42\xbf\x8c\x70\x4a\xe3\x99\xf0\x5c\xb4\xe0\xae\x35\xf5\x63\x60\x14\xec\x55\xc6\xb5\x05\xbb\xbe\x43\x38\x10\xc1\xe9\x61\xc4\xef\xbf\xcd\x0b\x80\x5b\x94\x90\x5c\x6b\x86\xa7\xca\x75\xc5\xe5\x58\x10\x8c\x3d\x53\xb0\x1a\x6b\x95\x16\x55\x16\x1f\x1d\xf0\x05\x75\x26\x6c\x89\x64\xc4\x6d\xd1\x96\x90\xd7\xdc\x38\x0d\x46\xd6\xa5\x56\x21\x1f\x25\x43\x33\x17\xdd\xf3\xe2\x99\xac\xb0\xa1\xa5\x64\xce\x2b\xcc\xa1\x67\xb5\xed\x11\xfd\xba\xd1\x2c\x4c\x39\x7d\x59\x98\x09\x01\x1a\xd2\x44\xc2\x47\x10\xb7\x78\x43\xc5\x7f\x55\x6b\xf2\xb5\xc9\x8b\x5c\x43\xce\x30\x38\x8a\x66\xe1\x00\xcd\xa6\xd4\xa1\xb0\x3f\x9e\x0d\xb0\x46\xf7\x66\x35\x0d\xa3\xcc\xc8\x45\xfe\x50\x3c\xb6\xad\xc0\x62\x10\x5d\x85\x32\x1e\x51\x38\xbe\x41\xc3\x99\x58\x94\x96\x48\xfa\xab\xab\x68\x8c\x13\xea\x54\x69\x97\xb5\x80\x6f\xc4\x78\xe2\x07\xa1\x2a\x5c\x15\xeb\xd7\xc4\xbf\x2e\x29\xfd\x82\x8b\x53\xb4\x62\xcb\xcc\xee\xcd\xbf\x52\x15\x73\x4e\x35\x0f\xae\x29\x07\x4a\xe6\x78\x28\xad\xbf\x44\x12\x01\xba\xe8\x09\x68\xc3\x49\x4e\xe4\xab\xda\xc7\x20\x2c\xc9\x4d\xbe\x44\x4d\x4f\xa1\x33\x9b\xf9\x24\xcf\xe0\x6d\x23\x12\x42\x77\x12\xc0\x7c\xb7\x2d\xca\xe7\xa9\x9a\x85\xfd\x7e\x23\x8f\x80\x78\xbb\x2c\xad\x27\xa7\xd1\x04\xc1\x0c\xc7\xe4\x34\x29\x36\x86\x95\xec\x80\x00\xce\x90\xf6\x8a\x8c\xbb\xa8\x7b\x90\xe0\x2a\xb6\x5c\xf5\xae\x39\x46\x4a\x0a\xac\x8c\xe1\xc3\x94\x9b\x45\x15\xee\x2b\xb3\x30\x3d\x19\x96\x3c\xa2\x16\x34\x14\x4e\x86\x56\x36\xe4\x99\x9e\x4f\x95\x3c\xb6\x68\x1e\xb6\x6e\x85\x93\x8a\xbf\x27\x37\x7d\x5f\x63\xb7\xc2\x59\x28\x73\x9d\xbc\xee\x69\xe5\xe6\xd8\x0d\xff\x24\x93\xb7\x73\x63\x43\xcc\x30\xb1\xce\x58\xae\xc5\x9b\xca\xc3\xc4\x49\xd3\x91\x89\x9e\x9f\xc1\x47\x7e\x02\x19\x72\x9d\x27\xee\xb9\xa9\xc8\x33\x76\x2d\xfb\x40\xd1\x49\x67\xd0\x69\xd8\x35\x9c\xa0\x28\x94\x8e\xc2\xb5\x36\x2a\xb5\x6a\x75\xb0\x64\x2d\x5b\x8e\xc5\xbb\xb4\x32\x3f\x06\x8b\x47\xfb\x79\xf8\x41\xa2\xbe\xe6\x65\x20\xcb\x0d\x98\x9a\xe7\x6a\x46\x07\x61\x81\x9c\xe4\x77\x8d\x6e\x47\x1a\x42\x34\x44\xf2\xbc\x20\x77\x85\x6d\x48\xc4\x1c\x28\xa1\xdb\x8e\x77\x37\xeb\xad\xb6\xdd\x49\x2c\x2f\xd5\xf5\x9d\x23\xac\xf1\xd8\x6a\xc5\xc3\xac\x1d\x63\x11\xde\xc3\xad\x21\x30\xd5\x10\x73\x2c\xb1\x33\x4d\x0a\x5f\x38\x0f\xaf\x32\x61\xf4\xf2\x10\x2a\x12\x40\x58\x56\xf1\xa8\x25\x1c\x2b\x09\x40\x2b\xcc\xcb\x94\x1a\xf4\xbd\x99\x0d\x87\x65\x63\xe6\x1b\xf2\xd1\x62\x63\xfd\x69\x3a\x00\x96\x21\x0f\x36\x4d\xcb\x5f\x3c\x63\x9f\x33\x82\x30\x05\xae\xc7\x11\x2e\xec\x42\x44\x59\x11\xf3\x1f\x9a\xbb\xbc\x17\x98\xf3\x19\xe0\x55\x5a\x62\x48\xd9\x74\x29\x6a\xc9\xf9\xaa\x13\x5a\x50\x26\x14\x65\x0c\x1c\xeb\xd1\xa1\x91\x60\x0a\x1b\x15\x82\x85\x3c\xd8\xf8\x12\x21\x9d\xe0\x6b\x03\x25\x9d\x63\x4d\xf1\xf7\xc1\x7c\x27\x76\x58\x92\x9b\x44\xe0\xe2\x64\x90\xe8\x63\x04\x28\xfb\x29\xcd\x17\xcf\x6a\x66\x31\x43\x51\x90\x20\x3c\x1c\xe2\x7e\x1a\x5c\xe2\xf1\x0d\xf2\xd1\x00\x27\x69\x3c\x83\x67\x0f\xe4\xf4\x95\x28\xec\xe3\x42\x51\x46\x0b\x52\xa8\x92\xe8\x01\x50\xca\x02\x72\x43\x89\xc5\x35\x17\x64\x10\x1e\x68\x67\x40\x1b\x9c\x1c\x45\x32\x21\x87\x5a\xc2\x51\x3a\x8f\xd0\x73\xaa\xcd\xa7\x7a\x5e\x74\x21\xba\xdf\xb1\x8c\xaf\x79\x20\xca\x07\x83\xe6\xad\x95\x79\x02\xfc\x02\x9c\x55\x1a\x21\xce\x64\x77\xa4\x79\xb0\x2e\x1e\x52\xde\xb5\x78\xa4\xe4\x77\xad\x5a\x7d\xb5\x51\x2f\x26\xe6\x27\x4c\xe3\xa3\xc4\xbf\xf7\xd9\xa4\x2d\x89\xc0\x49\x41\x98\xe2\x78\x28\x59\x0b\x23\xe7\xaa\xe0\xfc\x95\x75\x9d\x53\x2d\xdd\x6e\x59\x7c\x44\x1f\x8d\xf0\x78\x8a\x63\x22\xfe\x14\x58\x04\x3b\x0c\x37\xe6\x1b\xac\xa3\xfc\x0d\xee\xf1\xa8\xcc\xa4\x3b\x55\xd0\xae\x56\xce\x69\xaf\x76\xa1\x4b\x25\x9b\xb0\xe5\xd6\xcf\xc9\x55\x15\xe3\x41\x00\xed\xba\xdf\x33\xd6\x85\x3d\x00\x2e\x52\xcf\x8b\x6c\x25\xc2\x61\x51\xcd\x22\x96\x65\xb8\x54\x29\x7c\xf1\x63\xa3\x95\x9e\x08\x4b\xde\xdd\xdf\xec\x3e\x3c\x3d\x11\x11\x9a\x07\xa5\x20\x2d\x30\xba\xfa\x4b\xd0\xd4\xee\xc4\xef\x17\xa2\xab\x89\xdf\xbf\x0f\x6d\x89\xea\xf7\xa2\xaf\x2f\xd8\xae\x42\x92\xe8\xab\x7b\x0e\x68\x91\x79\xa0\x44\x46\x1b\xa1\x75\x17\x23\xb6\xdc\xe3\xaf\xd0\x24\xcd\xf1\x61\x20\xd8\x80\x13\x03\xfb\x91\x79\x31\xf0\x4c\x2d\x10\xd2\x77\xdf\x4f\x47\x34\xac\xef\x33\xfe\x9e\x0d\xf3\xeb\x2c\xd2\xef\xed\x99\xd7\x6a\x7e\xaf\xe1\x7d\x19\x32\x25\x1e\x8e\xb8\xfc\xe0\xf1\x7e\x39\xe4\x45\xe3\xfe\x0a\x0c\xe5\xf8\xbf\xae\xa0\xbf\xe2\x3b\x04\xff\xb5\x05\xd0\x35\xaf\x28\x78\xd4\xd8\x6c\xca\x24\x02\x90\xa2\xc1\x4a\xef\x73\xc2\xd3\x28\xb5\x25\x17\x18\x57\x18\xd9\x76\xb3\x98\x89\x16\x2b\xcb\x8d\xb4\xc4\xe3\xdd\xcc\xb4\x58\xf5\x3f\xcf\x4e\xab\x28\x02\x0f\xc5\x29\x7b\xd0\x9e\xdd\x54\x8b\xe2\xf2\x37\xb0\x25\x36\xca\x4f\xfc\xa9\x10\x0e\x27\xfe\x74\xf1\xd8\x0b\x16\x17\x71\x13\x84\xcb\x2a\x93\x8e\xf9\x5d\x0d\x96\xd1\xf2\x06\x6a\xb8\x6d\x96\x6f\x52\x5c\xb3\x18\x2d\xd3\x3f\x97\xe9\x32\xfd\x73\x1a\x30\x73\xc0\xf5\x0c\x70\x29\x40\xcb\xa8\x56\xb6\xd8\x44\xf3\x2f\x45\x2c\xa3\x39\xe0\x86\x06\xb8\xee\x04\x5c\xb7\x02\xb6\x43\x4e\xe3\x60\x3a\x86\xab\x97\x12\x1d\x96\x37\x6f\xc0\x6f\xe2\x2b\x7d\xae\x93\xe7\x75\xf2\x08\x28\xd8\xa0\x88\xa9\xf8\x4c\xa7\xa2\xf4\x19\xbd\x21\xad\xff\xf8\x23\x02\x6c\x3e\xa3\x97\xa8\x5a\x59\x6b\x49\x33\x54\x7e\x8d\x3e\xe7\x84\xbb\x90\xe6\x9e\xda\x82\x4f\xfc\x29\xd8\xcc\x6e\xa6\xa5\x12\x47\x18\x3a\xdd\x46\x2f\x51\xa9\x81\x56\xd0\xe7\x32\xeb\x69\x63\x68\xf5\x76\x32\xe2\x33\x98\x8a\x8b\xc1\x80\xa7\xfb\x36\xa9\x91\x7d\x20\x28\xa1\x0d\x24\xa1\xd3\x36\x9c\x49\x20\xb6\x5e\x56\xdc\x6e\x1c\x3c\x0a\xc6\x18\x95\xe4\x7e\xb2\x70\x01\xae\x58\x23\xd6\x61\x91\x9b\x59\xbc\xcf\x8c\xb3\xca\x50\xef\x61\x27\xaf\xf0\xe4\xbb\xdb\x59\x0a\x56\xbb\x10\xa3\xff\xae\x4d\x2d\xd9\x0e\x41\xed\x7a\xe4\xad\xa4\xb8\xb9\xa5\xa8\xb5\xe0\xe6\x20\xea\x09\x43\x79\xf1\x46\x18\xca\xcf\xe7\xfb\x46\x89\x18\x5f\xe2\x38\xc1\xfb\x52\xc1\xec\x95\x2d\xae\xd9\x0f\xd9\x67\x27\x75\xe7\x02\xb5\x6d\x01\xfc\x4f\xe7\x3f\x84\xfd\x90\x15\xca\x3a\x98\xcb\x69\xd4\x86\x4f\xf9\xc2\x66\xb6\xf9\x9f\xcb\x67\x68\x03\x7d\x2e\x16\xab\xd3\xc2\x52\xf6\x2e\xc2\x28\xc6\xdf\x8c\xab\x48\x20\xf7\xc2\x01\xf8\x39\x67\xd3\x1d\x90\x37\x07\xc3\x79\x3c\x43\x6a\x87\xc2\xf8\x61\x63\x03\xad\xd4\xe6\xf0\x24\x99\xc2\xe4\xda\x77\x62\xc4\x56\x91\x20\x16\x69\x2f\x13\xfc\x21\x8a\xa6\xd9\x92\xf0\x74\x1c\x3c\x69\x46\x15\x91\x43\xbb\xf1\xf4\xa7\x1d\xb4\xb4\xf9\xb6\xbb\xb5\xbd\xf3\x6e\x77\xef\x9f\xef\x3f\xec\x7f\x3c\x38\xfc\xbf\x47\xc7\x27\x9f\x7e\xfe\xe5\xd7\x7f\xfd\x8f\xdf\xeb\x0f\xf0\xf0\x62\x14\x7c\xfe\x32\x9e\x84\xd1\xf4\xdf\x71\x92\xce\x2e\xaf\xae\x6f\x7e\xaf\xd6\xea\x8d\x66\xab\xbd\xb6\xfe\x6a\x79\x75\x83\x45\xb8\x15\x47\x3b\xb1\x68\x17\x46\x35\x1b\x62\x87\x57\x4a\x66\xb9\xa1\x58\x98\xda\x44\x21\xad\x1d\x9b\x9b\x0a\x99\xe9\xc0\xb1\xdf\x30\xc7\xae\x84\x08\x49\xd2\xf2\xc8\xa8\x49\x76\x60\x41\x2b\xa8\x56\x3e\x03\xef\x95\x4c\x60\xaa\x9b\xc4\xc5\x81\xd6\x8b\x00\x2d\x9f\xf1\x0d\x5e\x16\xc3\x2c\x50\xa9\x40\x14\x2a\x91\x7b\xbe\x12\x61\x06\xd0\xff\x4a\x5b\x94\x7d\x6b\xc2\xfc\xe0\x3d\x88\x0d\xf1\xf2\xb2\xf2\x41\x90\xad\xf8\xc1\x28\xd2\x88\x2d\x69\x0d\x8b\x70\x9b\xe5\xee\xd1\x0f\xf9\xd2\x1e\xf1\xda\x99\xd9\xa7\xf5\x74\xf4\x7f\x3a\xfa\x8b\xa3\xff\xa7\x93\x9d\x95\x5a\x1b\xbd\xdd\x2e\xec\xa0\x55\x6b\xbf\xdd\x96\x7d\xb4\x6a\x6d\xf5\x09\xbe\xde\xdd\x69\x8b\x22\xf3\xe7\x3a\x6e\x15\xc4\xe1\x01\x9d\xb7\x6a\x6d\xa7\xf7\x56\xad\xfd\x37\xd0\x08\x14\x3f\xac\xc3\x60\xdc\xe7\xac\x6e\xf7\xf7\x07\xcb\xa8\x68\x80\x0f\xa3\x20\x4c\x5d\x4e\xc6\xb5\xb6\xc3\xc9\xd8\x7a\x98\xce\x30\x75\x7b\x19\x8b\x26\x8b\xba\x1a\x4b\x40\xef\x71\x82\xd2\x89\xf8\x5e\xce\x6a\x40\x9b\x8b\xae\x8d\xef\xfa\x18\x45\x57\x95\x70\x59\xe3\x8b\x6f\x21\x9f\x35\xa8\xb4\x98\xaf\x31\xaf\x25\xe4\x5b\xfe\xe2\xb1\x3d\x8d\xd5\x86\x8b\x39\x1a\xd7\x40\xf6\x11\x18\xaa\x6e\xc6\x44\x04\xca\x16\x4b\x9d\x2c\x16\x2d\x08\x9b\x9b\xc2\x5d\x52\x8e\x36\x3a\x2f\x8b\x87\xc2\x60\x64\xf9\xa1\xc0\x1e\x26\xed\x53\x1f\xee\xbd\x4f\x7d\xf8\x0e\xf6\xa9\x22\x38\x3c\xf4\x3e\x65\x5d\x4e\x1f\xb6\x9f\xb6\x29\xf1\xf7\x60\xdb\x54\x72\xe5\x4f\xb7\xc3\x41\xe0\x87\xa5\x45\x77\x2c\xdb\x91\xfc\xfb\xdf\xb2\x3e\x3c\xce\x96\x55\x64\x99\x7c\xff\x5b\xd6\x87\x6d\x6d\xd3\x7a\xda\xb1\x8c\x1d\x4b\x5a\x31\x0b\x6d\x5e\xdf\x74\xf7\x12\xf3\x22\x61\x4b\x00\x29\x7d\xe4\xd1\xf0\xe1\x0b\xbb\x3b\xa1\x8b\xbb\x5a\x25\xff\x0f\x17\x2b\xf4\x23\xe9\x3e\xfb\x4a\xbf\x65\xcb\x7f\x9e\xba\x00\x08\xcb\xad\x2d\x68\xdf\x4b\x5b\xc0\x72\xd4\x7e\x4b\xa5\x81\x87\xa4\x57\xc9\xc8\xaf\x69\xaf\x46\x13\xbf\xff\x88\xaa\x05\x0f\xf1\x66\xe1\x17\xb4\xf6\x77\x50\x37\x18\xf9\x62\xef\xa0\x8a\x50\x8c\x58\xa4\x2f\xfb\x5b\x2d\xa8\x09\x26\x37\xfb\x5b\x2d\x9b\x8c\x07\x26\xce\x5f\xf0\x0d\xcd\x82\x4d\xed\x60\x45\x5f\xc1\xf9\xd7\x0f\x53\x9e\xc4\x3b\x8a\x27\xd4\x46\x7b\xfb\xe7\xc3\x73\xd8\x74\x4f\xa2\xf7\x38\x13\x06\xd1\xd5\xd5\x55\x25\x9a\xe2\x30\x49\xc6\x95\x28\xbe\x58\x1d\x44\xfd\x64\x15\x92\x70\x47\xab\x5a\x9d\x51\x3a\x19\x5b\x14\x21\xdb\x97\xd3\xf7\x5b\x3b\x19\xda\xe2\xb9\x60\x30\x84\xf9\x3e\x20\xda\x1e\x67\x78\xbf\xb0\x94\xe7\xb0\x47\x91\x81\x49\xc8\x43\x10\x72\xb7\x17\x29\xdc\x73\xe6\xea\xd2\x44\xa5\x5a\x7d\x5d\xf1\x74\x31\xe0\x3b\x8c\xd4\xe4\xb0\x18\x7a\x82\x94\xfd\xad\xd6\x3c\x6c\x83\x94\xd9\x22\xeb\x41\xaa\xa5\x0f\x69\x84\xa6\xd4\xea\x54\xf6\xce\x71\xec\x70\x86\x5f\x8c\xb6\x3b\xb0\xe1\xe9\xa0\x5a\x7d\x1d\x4c\x48\x95\xaf\xb4\x73\x80\xb9\xf6\x25\xc3\x47\x69\xfb\xf6\xce\x6e\x37\x0e\xa2\x7d\x6c\x3f\x1c\x2c\x35\xfa\x00\x66\xd6\x5f\x06\x43\xc3\xfb\x86\xd2\xfc\x9c\x14\x4d\xf3\x2b\xfe\x91\xcd\xd5\xba\x96\xcf\xef\xae\x60\x3c\x75\x1a\xab\xd5\xaa\x0e\x78\x41\xef\xa0\xb9\x7e\x3f\xc5\xe4\xdd\x2d\x48\xe1\x4f\x68\x84\x50\x05\x24\xc2\xf6\x21\x03\x2b\x59\xb4\x77\xb1\xd2\xe7\x75\x69\x2c\x00\x1b\xa0\x9c\xca\x89\x3f\x4e\xd1\x26\xfc\xb3\xb8\x58\x0c\xd4\x45\xc9\xfb\x21\xc8\x0b\x93\xcd\xe3\xcb\x60\x58\xa1\x6e\x11\xb8\xc4\x3b\xe3\x01\x7e\x39\x79\x6b\xa0\xb8\x92\xdf\x51\xad\xb9\x90\xc0\xab\x4e\xb1\x45\xbc\x25\x2b\x9d\x71\x0f\xb3\xb6\xf0\x52\x23\xe4\xc1\x4c\x94\xb3\xd5\x61\x85\xe5\x72\x0b\x83\xd0\x02\x74\x88\xdf\xc3\xd8\xd8\x52\xa2\x2d\x72\x46\xce\x80\x09\x9f\x60\xf1\xc6\x79\x5c\xe6\x7b\x0c\xed\x11\x7b\xb2\x94\x93\x98\x38\x2d\x9a\xbd\xb0\x60\xf9\x8e\x6d\x4c\x04\xbc\xfa\x91\x19\xb3\x68\xb8\x72\x83\x96\x37\x1c\x1f\xeb\x51\x80\x88\x71\xe0\x39\xe0\xbc\x60\x56\x5d\x96\x68\xd9\xf9\xd7\xca\x48\x0e\xc6\x90\x39\x81\x30\x28\x9c\xd8\x24\xa3\x60\x83\x5e\xd5\xe6\x85\x3f\x9d\x59\x82\xd0\x84\x18\x38\xf3\xb3\x72\x50\xaa\xd1\x83\x92\x34\xd0\xb9\x69\x7f\x34\xec\x05\xb2\xce\x51\xb0\x61\x6c\x19\x2a\xf3\x9d\x44\x56\x2c\x66\x8c\xb5\x0d\x6d\x94\xa5\x5a\x92\x8e\x86\xd3\x9f\x25\xda\x85\x08\x30\xc7\xeb\x15\xb5\xb9\x2e\xc4\x83\x65\xbf\xe3\x3b\xf1\xde\x05\xf9\xee\x03\x7a\xdf\x5a\xfc\xca\xa4\xde\x14\xe7\xe6\x52\x25\x45\xbb\x21\xbd\x57\xb9\x7b\xf6\x01\x29\x5c\x5d\x6c\xda\x74\xbf\x76\x71\xf6\xc5\xaa\x79\xc8\x21\x36\xdc\x07\x4c\xae\xd8\x20\x54\xc8\x99\xac\xef\xda\x73\x4c\x17\x16\x36\xec\xaa\xc4\x02\x8e\x2b\xf9\xfb\xdd\xed\xeb\x9c\xe3\x3b\x85\x66\x3f\xbb\x7b\xfc\xf0\xd9\x69\xad\x7b\xfc\x48\xda\x59\x5b\x23\x67\xfa\xb5\xbf\xf4\x99\xbe\x1f\x4c\x47\x38\x5e\x79\x64\x13\x01\x38\xbd\xcb\x4d\xfd\x39\x87\x78\x33\x73\xe7\x83\x9c\xe6\xbb\xd0\xb1\x43\xc2\x71\x12\x71\x68\x97\x5f\xba\x4d\x08\xc4\x7b\x2d\x13\x86\x52\x83\x9c\xe1\xfc\x14\x2a\xd1\x9f\x9c\x11\xb3\x8a\x3b\xf0\x32\x65\x51\x15\x68\x91\x05\xd2\x69\x90\xd3\x0d\x9d\x9b\x14\x5f\xa7\xe4\x14\xe9\xb3\x67\x34\xa5\x7d\x62\xbe\x59\x3c\xd5\x86\x3f\xc0\xfd\x60\xe2\x8f\xc7\x37\x2c\x0d\xe8\xa0\xf0\xcd\x8d\x3c\x2a\xb7\xac\x15\x36\x70\x27\x02\x0d\xb5\xd9\xc5\x93\x71\xdc\x05\xbf\x47\x4d\xcf\x91\x4d\x89\x74\xab\x23\x77\x7e\xb1\x8b\x1d\xa5\xa6\xc3\x51\x4b\x2e\x53\xc9\x66\x37\x4b\x20\xb1\x8b\xaf\xef\x98\x09\xc2\x32\xbc\x12\xf9\xc8\xf7\x0d\x0b\x4e\xa7\x76\xf3\x10\x84\xd3\x59\x7a\x9f\x39\xe5\xe4\xa1\x12\xdd\x1d\xe8\xec\xa1\x88\xa3\xaf\x31\x0a\x0b\x7d\xdc\x39\xa9\x04\x8c\x96\x3d\x84\x4d\x36\x39\x1b\x28\x6b\x83\x56\x78\x6d\xa5\x9e\xae\x42\x3d\x5c\x23\x90\x01\xea\xc8\x40\x6f\xed\xba\x79\xf7\x4e\x9b\x75\x57\xdb\x6d\xa5\x0d\xa2\xd3\xaa\x7b\x9a\xf2\x7c\xfd\xc9\xd4\xee\xef\xae\xfb\x76\xed\x8e\x46\x24\xf3\x3c\x4d\xb8\x79\x48\x01\x07\x60\xa1\x71\xb5\x26\xa2\x22\x25\x36\x64\x47\xd5\x87\x49\x48\x0f\x2e\xaf\x73\x39\x5e\x61\x25\x71\x41\x55\x14\x91\xd5\xc1\x79\x19\xf7\x63\x9c\x3e\x90\x52\x89\xc8\xbf\xbb\xf6\xc0\x41\xd0\x4b\xc6\x26\x6c\x9e\xc8\xd4\xd1\xb7\xa8\xc6\x50\x76\x0e\x76\x04\x08\xb6\xea\x8c\x84\xbe\x88\xfa\x28\x88\x47\xdd\xc3\x3d\xc7\xdb\xed\x21\xe3\xcb\xc2\x81\x69\x4e\x78\x59\x7a\xa8\x92\xa2\xcb\xea\xe3\x64\x37\xc4\xcf\x51\x4c\xd1\x8e\xbe\x95\xe2\x62\xb2\xae\xe7\x45\xc6\xd4\x2a\x71\x7d\x81\x0e\xcb\x1e\x25\x73\x73\x3c\x8e\xae\x90\x1f\xf7\x82\x34\xf6\xe3\x1b\xc4\xd4\x4b\x5f\xf0\x8d\x25\xee\xe0\x17\x59\x23\xf1\x93\xb5\xe1\x9c\x81\xd2\xd5\x2d\xc5\x46\x6b\x8e\x33\x24\x41\x29\xc7\x0d\x12\xe2\xbf\x81\x6e\x23\x8a\x51\x10\x86\x38\x86\xe8\xb3\xd1\x2c\x05\x01\x42\x8f\xc2\x07\x31\x13\xa9\x8e\x91\x92\x21\x7b\xa0\xad\x18\x01\xe9\xb8\xc6\x4f\xae\x11\x58\x6a\x2c\x42\x02\x91\xa4\x95\x8c\xf2\xf4\x91\x81\x54\x30\x90\x0a\x1a\x8d\xfd\x7a\x70\x04\xf3\x49\xaf\x01\xa7\xfe\x00\xf5\xa3\x30\x49\xfd\x50\x6f\xde\x9a\x44\x4a\x9d\x63\xb7\x62\x4d\xe0\x7d\x1a\x9c\xa1\xdf\x36\x50\xf5\xba\xd5\xa7\xff\xb3\xb9\xc3\x18\x85\x1b\x6d\xfa\xbf\x7c\xcd\x58\xa4\xe9\xc4\x02\xed\xd9\x46\x91\x7f\x42\x1c\x32\xd8\x81\x1e\x23\x0a\x99\x60\xe2\x0f\x12\x89\x2c\x27\x5f\x99\x8d\x19\x5b\x06\x12\x3a\x6d\xe3\xe3\x0e\x3d\xa9\xaa\x2f\xce\x16\xcc\xdd\x22\x90\xc1\x30\x7f\x37\xf1\xc7\xf6\x37\xbb\x2c\xfa\x18\xe0\x15\xc0\x12\xcb\x8d\x84\xb2\xe0\x94\x17\x09\x44\x66\x94\x7e\xf8\x60\x64\x32\x49\xf0\x56\xe6\x06\x1f\x7b\xac\xe8\x61\x30\xd4\xff\xe9\xd1\xc3\xe6\x88\xa9\x8b\x88\x88\x84\x87\x66\x34\x34\x37\x82\x98\xbb\xc6\xdc\x28\x62\xee\xaa\x8f\x14\x49\xec\xfe\xdc\xae\x4b\xd5\xd3\x30\xde\x96\xfd\x98\x48\x17\xbb\xf6\xe0\x68\xb9\x01\xc7\x72\x39\xa6\x3c\x56\x1a\xd0\x4c\x42\xe1\x92\x06\xbf\x64\x12\xa8\x94\x9d\x21\xc7\x26\x7e\xdf\x7e\x49\x24\x0e\xfe\x0e\x23\xb8\x57\x7f\x69\x85\xf9\x75\xbb\xb9\x62\x79\x3d\x0e\x7a\x2b\x04\x95\x01\xd8\xb6\x26\xda\x57\x1c\xf6\x57\xc0\xa6\xd1\xf2\x9e\xba\x59\x6a\x1f\x26\x83\xd6\x7c\xe3\xbb\x64\xe4\xd7\x5b\x3a\x48\xf2\xb2\xae\x83\x4b\x46\x7e\xab\x56\x37\x5f\x36\xd6\x2d\x25\x1b\xda\xab\x38\x98\xe2\xc9\xa0\xd6\xae\x5a\x6d\xff\x94\x57\xd3\xde\x97\xc1\x50\x6f\x07\x5f\x4e\xbf\x0c\x86\x79\xf7\x0e\x6a\xd7\xa3\x01\x5e\xe9\x0f\x7b\xd6\xd7\x69\xec\x78\xbd\x72\x31\xf6\x07\x13\x3f\xb4\x7d\x8e\xec\xc0\x70\x5f\x7f\x3d\xf5\x07\x2b\x7e\x98\x04\xd7\xaf\xea\xfa\x20\x90\x4f\x41\x12\xd5\xaa\xb5\xba\x3e\xe2\xec\xd3\xab\xb5\x57\x6b\xfa\x0c\x91\x4f\xbf\xe3\x38\x62\xae\xd7\x96\xaf\xa1\xe3\x1b\xd5\x91\xad\x8c\xf0\xb5\xf6\xc1\xc7\x3a\x71\xd1\xb8\x1b\x03\xe3\x7d\xdc\xd7\x27\x37\xf6\x7b\xbd\x20\xb5\xbe\x5c\x19\xe3\x0b\xbf\x7f\xf3\xd8\x77\x40\x62\xf5\xc0\x93\xbe\x68\xe0\x65\xb6\x56\xc4\x23\x5b\x22\xf0\x4c\x56\x86\x66\x16\xca\xd6\x81\xf8\x5d\x6f\x8a\xdf\x84\xea\xf9\x6f\x42\xec\xe2\x37\xfd\x95\x91\x76\x66\x5f\x0a\xbf\x18\x21\x53\x0c\x28\xfd\x1a\x77\x58\x14\x1d\x4e\xad\xd2\x53\x1a\xab\x4f\x82\x36\xb3\xb7\x91\x52\x83\x50\x22\x6d\x56\x26\x40\xf1\x46\xd0\x9d\xfc\x86\x92\x9b\x78\x23\x53\x99\x78\x19\xaa\xaf\x24\x9a\x82\x67\x42\x4a\xf0\x23\xa3\x20\x3a\x2a\x7d\x36\x50\x8c\x5e\xa4\xdf\x9c\x4c\x16\x55\x44\x2a\x0a\x48\x99\xd7\x2e\xae\x98\x74\x87\x62\x63\x5d\xea\xb4\x6a\x5e\xbe\x36\xd9\x53\xe9\xaa\xd3\x6a\x7a\x0a\xe1\x75\x5a\x2d\x2f\x9b\xf8\x4e\xab\xed\xa9\xa3\xd7\x69\xad\xe9\x37\xc2\x3a\x29\x77\xda\x55\x8f\x51\x6b\xa7\x0d\xf8\x08\x4a\xe9\xb4\xeb\x9e\x4c\x2b\x9d\x76\xd3\xb3\x51\x4b\xa7\xdd\xf0\x64\x0a\xe9\xb4\x5b\x9e\x4c\x3f\x9d\x36\xe0\xa5\xd0\x4c\xa7\xbd\xe6\xe9\x54\xd3\x69\xaf\x7b\x3a\xdd\x74\xda\xaf\x3c\x83\x48\x3a\x6b\x55\xcf\x42\x4e\x9d\x35\xc0\x9f\x2d\x89\xce\x1a\x60\xcf\x48\xa3\xb3\xd6\xf4\x0c\xe2\xe8\xac\x01\xe2\x84\x8c\x3a\x6b\x80\x73\xb6\xce\x3a\x6b\x6d\xf9\x02\xdd\xcb\x96\x6c\x67\x8d\x5f\xad\x93\xc5\xdc\x59\x7b\xe5\xf1\xa5\xda\x59\xaf\x7a\xd9\x12\xee\xac\xd7\xbc\x6c\x71\x77\xd6\x01\x9d\x8c\x82\x3b\xeb\xd0\xb8\x60\x34\x9d\xf5\xe6\xed\x99\xd7\xae\x3e\x5d\x1e\xfc\xf9\x97\x07\xdd\x11\xee\x7f\x21\x9d\x82\x95\x42\xdd\x80\x68\x9a\xb3\x64\x36\x25\x03\x83\x59\x7c\x6a\xa9\xdf\x20\xc7\xd3\x90\xe6\xe8\x87\x0d\xb4\xc4\x21\x2f\x59\x2c\x42\x84\x93\xc6\x03\x5e\x57\xe4\x9a\xe3\x8b\x76\x8e\xf0\x10\xc7\x18\x0e\x7a\x71\x70\x01\x67\xb2\x20\x0c\xd2\x0c\x4c\x32\x9b\xe2\x18\x54\xd7\x1b\x5a\x7a\x0e\x09\xca\xe6\xec\x62\x82\xc3\x54\x2b\x80\xd2\x08\x8d\xfc\x70\x30\xc6\xca\xb8\xc9\xb0\x7b\x56\xc8\x8a\x4d\x0d\x54\x35\xdd\x01\x25\xdd\x37\x8d\x25\x4f\x4d\xa0\x82\x30\x5d\x97\x34\xf4\x43\xb9\xbe\x50\x4c\xa8\xb3\x63\x1e\xf3\xb3\x1a\x54\x09\xff\x89\x40\x85\x17\x32\x36\xca\x21\xc2\x8a\x58\x44\xd3\x7f\x01\xa4\xcb\x00\x5f\xb9\x50\x74\x36\x2f\x21\xbc\xc7\x51\x40\x5f\xbf\xaa\xe5\x39\xc1\x01\x96\xa0\x33\xe6\xd5\x7f\x20\x6b\x4e\xd8\x8e\xc0\xa2\xb3\x03\x37\xaa\x96\x8d\x56\x9c\x58\xd5\xda\x76\xb4\xdc\x2d\x2d\x56\x63\x2f\x4c\x1b\xf5\x45\x9b\x58\xac\xc6\xce\x38\xf2\xef\x52\xa5\xdd\x84\xf7\x59\xf9\x3b\x92\x52\x85\x52\xb0\x87\xe4\x57\x37\x29\x3e\x80\xe4\x40\xc6\x6b\x5b\xde\x65\x85\xfe\x76\xe9\xa2\xcb\xda\x2a\xb2\x22\xb2\xd2\x8b\xa9\x10\x32\x68\x6f\x05\x6e\x68\xc3\x8e\xb3\x45\xb3\xb0\x7d\xcd\xb2\xaf\xde\xa4\x36\xe3\xe7\x85\xdc\x05\x6d\xa8\x2c\x92\x4f\x3b\xab\x7f\x1a\x9c\xdd\x29\x79\x76\x66\xce\x1d\xfc\x8e\xa9\xaa\x36\x73\x1c\x55\x8b\x0a\xc6\x9a\xa5\xb6\xf0\x10\x73\x23\xb4\x75\x44\x99\x6f\x6b\xd6\x33\x32\x9a\xe4\x35\x81\x87\x42\x22\xf5\xc9\xcc\xdc\x6c\xd7\x9f\x4e\xc7\x37\xac\x61\x3f\xbe\x98\x11\x16\x9e\xe4\xf9\x2b\x32\x7e\x5d\x99\xc6\x51\x1a\x11\x1c\x65\xce\x9d\x67\x38\xa1\xef\x3e\x76\x05\x4b\xbb\xf6\x24\xeb\xfc\x39\xb2\x0e\x04\x8c\xfe\x13\xe2\x12\x59\x73\x2a\x15\x30\x91\x80\x2d\x96\xde\xe3\xa1\x34\xd3\xad\x93\x2a\x27\x8c\x59\x48\x25\xa9\xea\x52\xbb\xf9\xb3\x49\x7a\x2e\xbe\xd2\x6e\xda\xb9\xc8\x09\x61\x13\x1b\x74\xf8\x2a\x7e\x2f\xa1\x3f\x92\x20\x64\xc1\x58\x09\xcb\xa8\x5e\xd7\xaa\xec\xaf\x8c\xbe\xaa\x69\x7c\xd9\xf2\x2a\x95\xad\x16\xea\xfb\x5b\x2d\xcd\x9a\xc2\x66\x00\xa2\x7b\x4d\xa2\x0d\x36\xaa\x16\x03\x10\x9e\xf6\x26\xf7\x76\x2c\xd3\x04\xdb\x73\x15\x9f\x9a\x9c\xb4\x7a\xdd\x5e\x6b\xb6\xea\x8d\x6a\xcd\x43\xd5\x6b\x3c\xec\x0f\xfc\xde\xfa\x2b\x4b\x5e\xc5\xea\xf5\xab\xf5\x9e\x3f\xe8\x0f\xb1\x07\x03\xd3\xa8\xb7\x9a\x6b\x6d\xb5\xdc\x99\xf3\x46\x4c\x4b\xa3\x27\xf7\x62\x5f\x64\xd2\xb3\xed\x5d\x57\xfe\x14\x61\x70\xaf\x9e\xbf\x87\xd4\xda\xee\x1d\xc3\x7d\x7d\xcd\x67\x83\x22\x71\x4e\xe0\xf1\xf4\x82\x28\x70\x44\xe0\xdd\x3f\x97\x4a\xef\x9f\xf2\x87\x33\x9b\x4b\x88\xf4\x99\x10\x9c\x59\x80\xfc\x95\x4a\x25\x09\x26\xf5\x14\x47\x5f\x91\xfc\x12\xf6\xba\x66\x59\xf3\x11\x47\x5f\x0b\x02\xac\x37\xcb\x16\x80\x10\xca\x58\x71\x49\x37\xc1\xdd\xcf\x38\x64\x57\xb9\xa1\xb0\x5f\xf7\x2b\x43\x5a\x45\xd2\x98\xa2\x65\x54\xd5\xc5\x07\xa5\x74\x4d\x2b\x5d\xcb\x2d\x5d\xd7\x4a\xd7\x73\x4b\x37\xb4\xd2\x8d\xdc\xd2\x4d\xad\x74\x33\xb7\x74\x4b\x2b\xdd\xca\x2d\xdd\xd6\x4a\xb7\x73\x4b\xaf\x69\xa5\xd7\x72\x4b\xaf\x6b\xa5\xd7\x73\x4b\xbf\xd2\x4a\xbf\xca\x9f\x9d\xaa\x36\x3b\x73\x26\xb3\xa6\x15\xcf\x9f\xcd\x5a\x5d\x2b\x9e\x3f\x9d\xb5\x86\x56\x3c\x7f\x3e\x6b\x4d\xad\x78\xfe\x84\xd6\x5a\x5a\xf1\x96\xc1\x0d\x56\x57\x09\x43\xfe\x12\x84\x17\xa4\x6a\xe0\x8f\x7b\x36\xb1\xd9\x27\xdb\xc0\xa9\x75\xa0\x7a\xf0\xc9\x3a\x28\x7d\xf8\x64\x1d\x80\x01\x7c\x6a\xd8\xd0\xe9\x66\x77\xd0\xea\x37\x82\xc4\xce\x4e\xc9\xf7\x50\xcf\x43\x7d\x0f\x0d\x3c\x69\x81\x7a\x08\xad\x79\x64\x0b\xad\x9e\xe9\xbc\x61\x40\xeb\x0d\x3c\x24\xaa\x66\x23\xe4\x21\x54\xab\x7b\xe8\xe4\xb4\x66\xd4\xeb\xd3\x7a\xb4\x25\x5a\x35\x5b\xb4\xa4\xde\x1a\xa9\x57\x37\xea\xf5\x68\x3d\x81\xa4\x2f\xd5\x6b\x78\x08\xd5\xa1\xbd\x86\x51\x2f\xaf\x7f\x4d\xd1\xbf\xe6\x42\xfd\x6b\x89\xfe\xb5\x16\xea\x5f\x5b\xf4\xaf\xbd\x50\xff\xd6\x44\xff\xd6\x16\xea\xdf\xba\xe8\xdf\xfa\x42\xfd\x7b\x25\xfa\xf7\x6a\xa1\xfe\xd5\xaa\x1e\xeb\x5f\xcd\x24\x98\xbc\x0e\xd6\x6a\x1e\xeb\x60\xcd\xa4\x98\xbc\x1e\x12\x2c\x69\x0f\x6b\x26\xc9\xe4\x92\x68\xc3\xe3\x24\x6a\xd2\x4c\x6e\x1f\x9b\xa2\x8f\x26\xd1\xe4\xf6\xb1\x25\xfa\x08\x54\x63\x76\xf2\xdd\x3b\x47\x27\x3d\x84\x5a\xb4\x93\x26\xdd\x0c\x68\x45\x6b\x27\x09\xbd\xbd\xa2\x15\x4d\xc2\xe9\xd3\x8a\xf6\x4e\xd6\x3c\x44\x3a\x7a\x72\x5a\x33\x29\xa7\x47\x2b\x5a\x3b\x49\x38\x46\xbd\x0a\x15\x4d\xd2\xc9\xeb\x63\x4b\xf4\xb1\x6e\xe7\x35\xae\x3e\x12\x9a\xa3\x7d\xac\xdb\x99\x8d\xb3\x8f\x2d\xde\xc7\xba\x9d\xdb\xb8\xfa\xd8\x14\x7d\xac\xdb\xd9\x8d\xab\x8f\xaf\xb2\x3e\xda\xf9\x8d\xb3\x8f\x4d\xd1\x47\x3b\xc3\x71\xf5\x91\x30\x46\xd6\x47\x3b\xc7\x71\xf5\x71\x3d\xeb\xa3\x9d\xe5\x38\x69\xb5\xe1\xf1\x3e\xda\x79\x8e\xab\x8f\x75\x41\xab\x75\x3b\xd3\x71\xf5\x71\x4d\xf4\xb1\x61\x67\x3a\xae\x3e\x92\xe5\x4f\xfb\xd8\xa8\xd9\x17\xe4\xee\xae\x9b\x58\x9b\x80\x6b\xc3\xce\x75\x76\x77\xed\x9d\x24\xc3\x4a\xd6\xd6\xc9\x69\xc3\xce\x75\x76\x77\x73\x16\x64\x1b\x2a\xda\xb9\xce\xee\xae\xa3\x93\x4d\x0f\xd5\x1b\x50\xd1\x24\x9d\xbc\x3e\xd6\xb2\x3e\xda\x99\x8e\xab\x8f\xcd\xac\x8f\x76\xa6\xe3\xea\x23\x4c\x24\xed\xa3\x9d\xe9\x38\xfb\x58\x15\x7d\xb4\x33\x1d\x67\x1f\x1b\x1e\xeb\x63\xd3\xce\x74\x5c\x7d\xac\x8a\x3e\x36\xed\x4c\xc7\xd5\xc7\x86\xe8\x63\xd3\xce\x74\x5c\x7d\x24\xac\x9c\xf6\xb1\x69\x67\x3a\xae\x3e\xbe\x12\xf3\xd8\xb4\x33\x1d\x57\x1f\xc9\xf2\x60\x7d\xb4\x33\x1d\x27\xad\xb6\x38\xad\x36\xed\x4c\xc7\xd5\xc7\x7a\xd6\xc7\x35\xfb\x82\xdc\xdb\x73\x0b\xaa\x6d\xda\x49\x3b\xd7\xd9\xdb\xb3\x77\x12\x68\x0e\x78\x40\xd3\xce\x75\xf6\xf6\x72\xc4\x80\x16\x88\x80\x76\xae\xb3\xb7\x67\xef\x24\xe1\x1d\x75\x18\xd6\x96\x5d\xd4\x71\xf5\x91\xcc\x07\xed\x63\xcb\xce\x74\x5c\x7d\x6c\x88\x3e\xb6\xec\x4c\xc7\xd9\xc7\xaa\xe8\xa3\x9d\xe9\xb8\xfa\x58\xcb\xfa\x68\x67\x3a\xae\x3e\xae\x8b\x79\x6c\xd9\x99\x8e\xab\x8f\x40\x73\xb4\x8f\x76\xa6\xe3\xea\x23\x88\xe4\xb4\x8f\x76\xa6\xe3\xec\x63\xc3\xe3\x7d\xb4\x33\x1d\x57\x1f\x9b\xa2\x8f\x6d\x3b\xd3\x71\xf6\xb1\xc6\xfb\xd8\xb6\x33\x1d\x57\x1f\xeb\xa2\x8f\x6d\x3b\xd3\x71\xf5\xf1\x95\x98\xc7\x76\xc3\x5c\x90\x70\x8d\x92\xe2\x78\x82\x07\x81\x9f\x32\xa7\x32\x70\x57\x50\xcb\x91\x23\x2e\xda\x40\x25\xf8\x77\x19\xf9\xba\x86\x95\x96\xa9\xb1\x32\x35\x52\xa6\x67\x2f\x53\x67\x65\xea\xa4\x4c\xdf\x5e\xa6\xc1\xca\x34\x48\x99\x81\xa1\xcd\xd5\x54\x95\x3b\x16\x4b\xdd\x05\x03\xda\x42\xa6\x74\x91\x4d\xd7\x4f\x7d\xdb\xc1\xdc\x4f\x7d\x11\xca\xc7\x4f\x7d\xb7\x72\x2c\x7c\x1b\xa4\xc9\x49\x94\xfa\x63\x01\x33\xdc\xf2\x53\x9f\x7a\x90\xbc\x44\xeb\x16\xe8\x50\xe7\x03\x1e\xa6\x1c\xba\xf0\x38\x81\xf2\x46\x67\x9c\x29\xaf\x04\x9a\xa7\x19\xc8\x9f\x7e\xfa\x09\xb5\xe0\xe2\xad\x7a\xbd\x5e\xcd\xee\xdb\xb2\x12\xff\x40\x8d\xba\x41\x1c\x6a\x5f\x76\xd1\x06\x02\xb5\xfb\x70\x1c\x45\x71\x49\xea\xe4\xaa\xa2\x7b\x77\x75\x0e\xca\x7e\x40\x1b\xd2\x93\xbe\x70\x04\xea\xa5\x52\x29\xc3\x6d\x19\xb5\x9b\x34\x5f\xda\x2b\x08\x26\xda\x2c\x53\x85\x8d\x5d\x3f\xcb\xab\x32\x9c\x33\xe5\xac\xfc\xb6\xb8\x76\xd6\x04\xc7\x54\xb3\x3a\xb8\x79\xba\x59\x83\x4b\x2c\xd2\xd9\x66\x91\xce\x7e\xb0\x76\xf6\xc3\x5d\x3b\xfb\xc1\xda\xd9\x0f\x45\x3b\x6b\xf6\x56\x76\xa2\x2a\x89\xee\xf3\x60\x53\x90\x53\xcf\xee\x3f\x08\x06\xef\xd4\x8d\x01\x7c\x14\x6d\x9e\x54\xb9\x79\xe5\xe7\x78\x43\x2a\x3a\x6f\x0b\xf9\xee\x32\xc3\x78\xa7\xf7\xdb\x42\xf7\x1e\x8e\x2b\x2e\x94\x77\xfd\x2f\x30\x81\x2b\x8c\xdd\x53\xfb\xdd\xc5\x2e\xbb\x25\x2b\x95\x76\x95\x6b\x89\xdd\x85\xef\x23\x28\x2d\xec\x2a\x77\x11\xbb\xce\x4b\x88\xf9\x37\x0e\x47\x2c\x37\x30\xcc\x21\x8b\xc0\x33\x80\x31\x55\x8b\x16\x48\x56\x0e\x6e\x08\xb9\xac\x1e\x14\xac\xe0\x94\x29\x6e\xe8\xe0\x31\xbb\xfe\x37\x36\x5e\xf8\x7c\x6e\xd0\x82\xcb\xbb\x92\x47\xd0\x20\x5f\xed\x1e\x0e\xf4\x97\x40\x52\x53\x7d\x5d\x7b\x28\xf1\x90\x7a\x85\x06\x7c\x12\x6d\x20\x1f\x2d\xa3\x52\xa9\x87\x7e\xa4\x9b\x63\xe9\x7f\xc9\xcf\x41\x99\xb0\x81\x6b\xb4\x8c\x52\xa9\x3d\x11\xb0\x38\x24\xd3\x94\xd0\x95\x4a\xe3\x94\x37\xea\x68\x05\x25\x65\xa8\xd6\xd3\x8c\xde\x04\x56\xda\xf9\xbf\x18\x56\xb0\x1d\x97\xfa\xe8\x47\xf4\xbf\x8f\x83\x95\x76\x08\x9a\x8b\x55\x0f\xfd\x86\xfa\xe8\x37\x82\xd8\xc3\x23\xa3\x09\x80\x73\x91\x21\x88\x94\x7a\xe8\xeb\x03\x0f\x8e\x7c\x5b\x7d\xec\x4a\x93\x3e\x37\xf1\x7e\x91\x20\x6b\xdc\x4f\x4c\x73\x51\x84\xd5\x60\x82\x71\x38\x8b\x39\x4a\xdf\x35\xac\x19\x5b\x97\xc2\xc8\x65\x7f\xab\x65\xf1\xfd\xca\x2f\x6f\x3a\x7c\x65\xf1\xc5\x94\xcb\x7c\x35\x23\xff\xfe\x56\xcb\x6a\x32\xe0\x9c\x84\x39\xb9\xea\x1f\x6a\x0a\xee\x14\xda\x61\xfe\xc4\xc9\x5e\x7e\x0f\x31\x71\xd4\xa9\x4c\x4c\xc4\xee\xc4\xef\x93\xc9\x50\x32\xc3\x9b\xf3\xc1\x8a\x99\x73\x92\x65\xb3\xa7\xf3\x92\x9b\x81\x9d\x45\xb6\x76\x58\x40\xd5\xff\xd2\x2e\x66\x7f\xff\x98\x6c\x74\xb1\xbd\x64\x71\x86\xd0\x0e\xc6\x83\x9e\xdf\xff\xc2\xe2\x6a\x4e\xa2\x01\x2c\x29\x42\x33\x62\xbe\xe1\x65\x77\xe7\x2d\x11\x81\x2c\xe2\x01\x98\x39\xc1\x57\xc5\x5a\x0e\x2c\x5c\x68\x2b\xfb\x04\x00\x33\xe6\x11\xab\xbe\xbb\xf3\xb6\xb2\x1d\xd2\x58\xe5\x60\x40\xb5\xf3\xd6\x62\xf0\x33\x75\x98\xcb\x30\x33\xc3\x1c\x93\x19\xb7\x68\xca\x42\x50\x71\x81\x84\x3e\xda\xee\x99\xa5\x50\x1e\xb4\x90\x1c\xca\x43\x2d\xcf\x63\x94\xbf\xc7\x37\x49\x1a\x63\x7f\xb2\x19\x0e\x58\xef\x2c\xd6\x91\x11\x33\x8b\x15\xe0\x3c\xd6\x80\x4d\xc8\x3e\xc2\x13\x0c\x41\xc6\xc1\x18\x93\xce\x13\x8b\x95\x09\xfe\xf3\x21\xbe\x4e\xe9\x6b\xbb\xf8\x8e\x2f\xdf\xb2\x98\xa9\xd0\x7a\x25\x19\x07\x7d\x5c\xe2\x28\x88\x9b\x7a\x81\x8b\xcd\x7e\x52\x99\xb5\x2d\xfc\x77\x99\xb5\x7b\x8c\x2e\x18\x0e\x8f\x82\x64\xe1\xb1\xfd\x66\x74\x73\x92\x75\xa8\x87\xfb\xd1\x84\x79\xdd\x13\x82\x08\xa2\x59\x52\x8c\x64\x44\x17\x0b\x89\xe3\x39\xbd\x29\xcd\xed\x82\xe6\x1b\x61\x1e\xd8\xe0\xbc\x77\x99\x05\x6b\xb9\x7c\xad\x1a\x8d\xcb\xe1\x98\x69\xf3\xd9\x67\xc8\xec\x7a\x69\x3d\xd2\x88\xd2\x68\x03\x05\x97\x6c\x0a\xab\x8e\x95\x18\x5d\x62\xb4\xf7\x33\x9c\x3f\x93\x59\x2f\xc1\xff\x9e\xe1\x30\xcd\x39\x3d\x03\xbe\xc2\x81\x61\xae\x01\xb4\x8e\x8f\x36\x21\xe6\x24\x90\x3f\x46\xe5\x98\x0e\x34\x14\x2c\x09\x20\x1e\x52\xbb\xb2\xba\x8a\xd8\x8c\x64\xef\xac\xd9\x72\xf3\xa3\xc6\x50\xd3\xf3\xcc\x42\x10\x22\xc1\x88\x46\xe1\x1c\x6d\xd0\x0b\xc3\x82\x8b\x13\x3b\x6f\xf3\x0c\xae\xf9\xa6\xb3\x48\x9c\xba\x76\xe3\x49\xf8\xf8\xde\x85\x0f\xf4\xdf\xd3\x18\x27\x38\xbe\xc4\x54\x0c\x89\x66\x44\x94\x97\xc4\x0f\x50\x63\xf8\x69\xd0\x1b\x33\x0e\x8c\xb6\x62\xf4\x36\x0e\xfc\x10\xbd\xa3\xee\x99\x68\x18\x8c\x31\x0e\xfb\x95\x3e\x80\xe0\x21\x9f\x21\x02\xb6\x46\x3f\x27\x47\x50\xe4\x9f\x7e\x88\x76\xe3\x59\xef\x06\x7d\x1e\x91\x7f\x2a\x57\xb8\xf7\xdf\x17\x13\x3f\x18\x57\xfa\xd1\xc4\x2e\xef\x9c\x1c\xf1\xe6\x72\xc4\x1e\xb9\x50\x61\xe9\xe7\x59\x96\xef\x25\xec\x93\x83\x02\x4d\x99\xf4\xfc\xd9\x33\x32\xe8\x40\x7a\x22\x1d\x12\x28\x89\xa8\x52\xa8\x0c\xb3\x4e\x7f\xfd\x81\x56\x57\xa3\x4b\x1c\x0f\xc7\xd1\x15\xa9\x03\x1b\x5f\x8d\xa7\x03\x25\xf5\x6a\xed\xf2\x8f\xa4\xec\x6b\xf1\xb9\x2e\x7f\x5e\xd7\xbf\x36\xd8\x1e\xc6\x1a\x03\x3c\x01\x15\x02\x56\xb4\xbb\xba\x8a\x78\xb3\xa8\x57\x23\x45\x00\x65\x68\xba\xfa\x5a\x54\xa9\x67\x55\x44\x99\x67\x80\x00\x2d\x44\x4b\x35\xd4\x52\xac\xd8\x33\x40\x85\x95\xbb\x85\xff\x12\x82\x94\x4b\x2c\x2f\xf7\x1a\xd2\x77\xf8\x0f\x2f\x43\x8b\x2c\x2f\xf7\xea\xaf\x9f\xbb\x0b\x2c\x2f\xf7\x6a\xec\x3b\xf9\x2f\x74\x9c\x37\x0a\x0f\xcb\x1b\xd0\xf3\x37\x6f\x58\x3e\x48\xf9\x75\x9d\xaa\x00\x95\xb7\x0c\x21\xb3\x25\x51\xad\x7a\x5d\xad\x31\xad\x5f\x56\x94\x71\x3d\x52\x88\xbc\xbc\xd5\xa9\x83\x2d\x8f\x52\x9f\xfe\xab\xd2\x08\x7b\x49\x6f\x90\x38\x29\x65\x2f\xcb\x8c\x60\xa4\x29\x58\x5d\x45\x64\x97\x80\x9b\x18\x14\x48\x0b\x89\x2e\x1e\x63\xa5\x2d\x25\x08\xe0\x25\x28\x0a\xc7\x37\x74\x39\x6e\xfd\x72\x70\xb4\x85\x3e\xa3\x37\x68\x1d\x60\xf2\x06\x6b\x36\x2c\xe8\x5d\x9c\xda\x59\xf6\x8d\xf7\x97\xaf\x25\xe5\x2c\x20\xd6\x55\xc5\xf1\xfa\x4f\x94\x39\x17\x15\x39\x8d\xe2\x9a\x0c\x63\xb6\xca\x78\xa2\x68\x96\x0f\x98\x81\x7a\x9e\xc4\x83\xdc\x52\x0f\x08\x0d\xf6\x46\xf2\x65\x20\x74\x07\x39\x08\xcd\x97\x85\xb8\x74\x40\x08\xdb\xa4\x79\xca\x8a\x9e\xe9\xa2\x11\xfb\x2c\xe1\xaa\xaa\x9e\x17\x11\x8a\x90\x43\x30\x42\x77\x13\x8e\xd0\x82\x02\x12\x52\xe5\x39\xf3\xd0\x95\xd1\xbd\x7c\xf6\x12\x4b\xe3\xb5\x26\x59\x89\xe2\x92\x80\xe5\x14\xb1\xa4\xc2\x0b\x48\x5a\xcd\x27\x49\xeb\x7b\x97\xb4\x1c\xf2\x95\x43\xbd\x73\x72\x94\x2f\xe7\x2c\xaa\xde\xb1\xb0\x74\x9d\x97\x3f\x31\xf1\xbf\x1f\x13\xcf\x3d\xcd\x3e\x02\xcb\xde\x0b\xfb\x31\x86\xc8\x0d\x0c\xb8\x06\x92\xc9\x21\xd9\xe4\xae\x20\x6a\x4c\xe3\xf8\x02\xb7\xe5\x5f\x51\xf5\x2f\xb5\x39\x14\xdd\x15\xe6\x9f\xb7\x49\x99\x05\x76\x81\xd6\xd3\x2e\xf0\x97\xd8\x05\xb6\xc7\xb8\x9f\xc6\x51\x18\xf4\x51\x37\x1a\xe0\x5e\x14\xcd\x57\xf8\x6f\x77\xf3\x14\xfe\xf4\xeb\x42\x3b\xc2\x76\x57\x55\xf8\x93\xe7\x87\xda\x01\x64\xd6\xae\x32\x10\xb5\x5e\x9e\x16\x93\xe0\xa3\x2c\xa4\xc7\xc2\x6f\x80\xef\x84\x1f\x4f\xbd\xd4\x9d\xaf\x37\x83\x32\x0b\xac\xe3\xbf\x76\x72\xe4\xff\x9c\x75\x7c\x30\x4b\xa7\xb3\xb4\xf8\xa5\xdd\x41\xee\xa5\xdd\xc1\xe2\x97\x76\xba\x54\x77\xa0\x5d\xe2\x1d\xfc\xb9\xd7\x41\x8f\x2e\xd5\x99\xba\x79\xf1\xe6\x61\x25\xbb\x9c\x86\xbe\x17\xe9\xee\xef\x74\xc2\x3e\xd0\xae\x35\x5d\x42\xd4\x41\x81\x4b\x8b\x83\x05\x2f\x2d\x9e\xb2\xd8\xfd\x35\x98\xef\xe6\xc7\xe3\x3d\xf4\x6b\xe5\x55\xbd\xc1\x0d\xc4\x51\x92\x92\xe5\x7d\x71\x63\x70\xdf\xa9\x3f\xa8\x6c\x86\x49\xf0\x2b\x29\x2d\x72\xc1\x4d\xfd\x81\xcc\xfe\x06\x7e\xea\x4b\x17\xa1\xae\x0b\xd0\x44\xbd\x01\x25\xb5\x8e\x33\x83\x5f\xc5\x00\xf8\xb5\x5a\xb4\xa7\xa7\x15\xe9\xb9\x12\x8a\x00\x51\xcc\xc2\x54\xf4\x4c\x0b\x66\x05\xb6\x78\x87\xf4\x9b\x01\x8c\xbe\x58\x51\x31\xfb\x87\xf6\xdd\x68\x8d\xc6\xb4\x19\xfb\x09\x8d\x9c\x85\xa6\x51\x12\xa8\x1e\xf8\xa4\x51\xf2\x9d\xd4\x3f\x8c\x78\x67\x45\x0b\xcb\x1a\x46\x2b\xa8\xa6\x35\x72\xe8\x0f\xb2\x67\x18\x28\x91\x6d\x44\x7d\x4d\x59\x89\xdc\x56\x16\x52\x4b\x6d\x24\x0b\xa9\x25\x97\xb6\x05\xd7\x52\x2d\xb3\x97\x35\x40\xdc\x0e\x91\x5b\xe0\xce\x42\x0b\x71\xe8\x14\xf1\x0e\xa7\x52\xc2\x79\x65\xaa\xa8\x02\x5f\x8c\x66\xfe\xcc\x49\x7d\x2e\xa9\x68\xae\x90\xe3\x2f\xeb\x7b\x76\x11\x24\xa1\xc0\xf6\x15\xc3\x43\x42\x03\xe3\xe8\xed\xf3\x67\xb7\x56\xbe\xc9\x97\xcb\xf5\xab\x7a\x63\x21\xde\x79\xbf\xc4\x64\x4f\xbc\xf3\x5b\xf1\xce\xbd\xe3\x03\x04\x21\x71\x8b\xb1\xce\x3d\x16\x40\xf7\xbe\xac\xf3\x4f\x67\x87\xd9\x92\x98\xc3\x0f\x2d\xac\x8a\xa6\x03\xb0\x47\xa0\xab\xc4\x7e\x38\x88\x26\x25\x83\x03\x96\xcb\x15\x4d\x52\xca\x87\xc3\x52\x87\x9d\x1a\x5c\xae\xde\x3c\xf3\x08\xb8\x27\x46\xa5\x33\x2a\x4e\x9c\x0b\x31\xaa\xbf\x76\xe6\x85\xff\x28\x46\xb5\xba\xb7\xdd\x45\xaf\xd6\x5e\xad\xad\xd4\x10\xa3\x0d\xb4\x8f\xd3\x51\x34\x40\x75\x17\xb7\x82\xd0\xde\x77\xe5\x56\x9b\x83\x01\xf5\x1f\x54\x17\x44\x01\x2e\xc0\x57\x2f\xa9\x4d\xff\xf8\xa2\x55\x1a\xf8\x1f\x1c\x47\x90\x3b\x2c\x1d\x61\x14\xe3\x44\xe2\x8b\x4a\x47\x48\x39\xd6\x63\xf2\x6c\xe0\x7d\x27\x5e\xc0\x16\xe2\xef\x0c\x07\x75\x35\x3a\x9b\x07\xd0\x14\x9e\x7d\x61\x47\x21\x46\x93\x28\xc6\x54\x78\x5c\x59\x81\xbe\xb9\x46\x91\xaf\xf7\x95\x95\x82\x0b\x1c\xe6\x73\x91\x05\xbe\x76\xbf\x28\xe7\x4f\x0b\xfc\x9b\x9d\xe2\x50\x18\x45\xd3\x62\x62\xc8\x47\x4e\x8e\xce\x95\x2d\x88\xdd\xbd\x26\xb2\x22\x79\x34\x27\x9a\x5a\x88\xe8\xee\x17\x6e\xf6\x89\xe8\xbe\x15\xd1\xfd\x8f\xc4\xfc\xf2\x49\x4e\xe2\x81\x7f\xa2\xf0\x5b\xf8\xe0\x2c\x9f\x6f\x0d\x01\xb8\x54\xca\x17\x81\xcb\xe8\xeb\x57\xfd\xd5\x9d\xb6\x18\x7b\x8f\xe7\xc7\x15\x58\x5d\x45\x9f\x08\x7c\xb5\x5e\x60\x44\x0a\x00\xcd\x82\x28\x73\x35\x0a\xc6\x18\x95\x7e\x28\x65\xbe\xd6\x59\x0c\x6e\xf0\x38\x34\x62\x6e\x0b\x13\x4e\x43\x91\x19\x88\x2d\x09\xa9\x2a\x4a\xdd\xb1\x1b\xe2\xf1\x16\xd9\xbd\x24\x0a\x5a\x88\x97\xfc\xb5\x1d\xb7\x2c\x39\xba\x68\x92\xac\xc7\xe5\x2b\x59\x26\x24\x68\xed\xcf\xcf\xf3\xf1\xb8\x49\xc2\x8b\xc5\xc4\x36\x62\x5e\x8b\x2f\xc7\xbb\x9b\xb5\x2c\xd6\x33\x79\x92\x3e\x9a\x89\xc0\x6d\x0e\xa2\x87\x7e\x92\x90\x85\xbc\x42\x50\x1b\xa0\xf7\xf8\x06\x6d\xe1\x38\xb8\xa4\x39\x21\x77\xf8\xa0\xd4\xf3\x63\x4e\x1f\xbe\x7d\xbf\xb5\x53\xcf\x5a\x13\xcf\x05\x13\x8f\x77\xa3\x70\x18\x5c\xcc\x58\x26\xca\x08\xb2\x42\x26\x79\xf9\x25\xe3\x68\x8a\xe3\xf4\x06\xfd\x41\x8f\xc5\xe0\x4d\x0a\xcc\xf7\x64\x44\x73\x1c\x27\xe4\x21\x08\x59\xba\x80\x34\x12\xbe\x34\x15\xb4\x85\x87\xfe\x6c\x9c\x76\x50\x13\x95\x6a\xf5\x75\x48\xa4\x5c\x76\xc1\x77\x24\x34\xc7\x31\x4f\x64\x9e\x81\x23\xe3\x3f\x0f\xcd\x20\x65\xc9\x33\x13\x00\x95\x1d\xea\xa5\x0f\x69\x84\xa6\x38\x1e\x46\xf1\x44\x02\xae\x40\x96\xd2\x3f\xf6\x87\x17\x1d\xd7\x28\x23\x7a\xf1\x75\x0c\x31\x67\x6a\xf5\xf5\xd5\x46\x5d\x0b\xc1\x4d\xbb\x42\x51\xd7\x3e\x65\x08\x29\x8d\xdf\x96\xf3\x12\x92\xe6\x25\x90\x27\xb3\x32\xc8\x48\x8b\xaf\xb7\xf9\x59\x44\x0f\x80\xcf\xdd\x92\xae\xca\x19\x43\xc9\xf8\xf5\x6d\x74\xc3\xfd\xcd\x86\x51\x0c\xa7\x98\xac\xd1\x07\x48\x0c\xfa\x65\x30\x34\x92\xc6\x53\x6a\xe7\xa7\x47\xc5\x0c\x6b\x91\x8a\x7f\x64\x93\xb5\x4e\xd3\x4f\xde\x1b\x8c\xa7\x4e\x63\xb5\x5a\xd5\x01\xe7\x64\xaf\xef\x0f\x2f\xec\x86\x17\x64\x22\x36\xc4\x4f\x4e\x78\xa4\xb8\x2b\x18\x86\xb9\xde\xe1\xba\x82\x7a\xd0\x15\x65\x41\x77\xc9\x37\x3b\x65\xb0\x81\x5a\xf8\x43\xa5\x60\xe5\xc4\x1f\xa7\x68\x13\xfe\x59\x3c\x11\x2d\x77\xa3\x91\xfc\xda\xef\x43\x76\x34\x91\xfa\x60\x58\x61\x51\x49\x4a\xbc\x33\x1e\xe0\xe7\x9c\x54\x56\x5c\x9e\x57\xad\xe6\x42\xb9\x5d\xd4\xa9\xb7\x1a\x10\x06\xa9\x23\x29\x2c\xf3\xb2\x07\xdf\x7d\x46\xab\x84\x7c\x28\x0f\xf2\xc4\xec\xd8\xcd\x12\xdd\x09\xca\x41\x36\xa5\x83\x4d\xd3\xcd\x1b\xfa\x1c\x5b\xa8\x27\x90\x93\xf7\xc2\x01\xbe\xb6\xd5\x38\xad\x5e\x33\x05\x90\x25\x5a\xe7\x9c\x10\x5d\x02\x15\x21\x2c\x8b\x37\xce\xfc\xf5\x19\x36\xbc\x52\xf6\xc6\x59\x89\x6f\x79\x1b\x64\x56\x2a\xec\xc9\x66\x84\x91\x6d\x2d\xb4\x68\xf6\x62\x8e\x91\x85\xfa\x91\x09\xea\x5a\x07\x79\x5c\xa4\x37\x1c\x1f\xab\x71\x81\xe8\x24\xcb\x73\xcc\x93\x65\x03\x05\x66\x69\x7c\xb3\x5e\xeb\x73\x86\x58\x46\xef\x2c\x35\xb0\xf9\x7d\x7e\x36\x06\x80\xaf\x0c\xb1\x75\x74\xcd\xe2\x22\x8b\x51\xf6\x8a\x75\xdc\x81\xc8\x9e\x18\x63\x3b\xe8\x40\x8e\x66\xc7\xc0\x5a\xb0\x50\x6c\x39\x6a\xd4\x96\x43\x9a\x3e\xa7\x31\x07\x02\x7e\xae\x34\x01\xa3\x27\x46\x5a\xfe\x68\x1b\xeb\x22\xe3\x8d\xe6\x85\x82\xb2\x75\x96\x8f\xbe\xfc\xce\x1e\xb0\x4a\x6a\xe2\xd7\x83\x23\xb5\x3b\xe0\x3a\x65\xf1\xb8\x36\xc6\xed\x33\xb5\x81\xf9\xcc\x6d\x60\xa4\xd9\x7c\x8d\x3e\xe7\x8c\x1e\xf9\xcb\x6a\x9c\x7e\x06\x73\x18\xa3\x23\xa7\x9f\x75\xb3\x18\xfe\x77\x6b\xbe\xd6\x03\x4e\x91\x3f\x89\x39\x30\xdd\x34\x34\x6a\x9b\x12\x8d\x49\x9c\x56\xcf\x96\x97\xf3\x4d\x8a\x24\xe0\xd2\xd1\x97\xf3\x0d\x4b\x10\x33\xb6\x97\x65\xf5\xf2\x0c\x28\xe5\x63\xc4\xbd\x36\xf4\x22\xc1\x66\x72\x37\xf2\x05\x37\xf1\x87\x12\x2d\x83\xc4\x96\x6e\x7f\x7e\xf4\x1a\x8b\x68\xf0\x00\x41\x6c\xa8\x88\x20\x24\x43\x2a\x14\xba\xc4\x84\xc5\xaa\x79\xc8\x21\x9b\xde\x07\x4c\xae\x6c\x9a\x05\xd9\x11\x47\x49\x97\x00\xe3\x21\x5d\x50\x65\xc3\xae\x8a\xc5\xa4\xd0\x1c\xe1\xe9\x36\xcf\x16\x8d\x42\xb3\x07\xea\xd1\x53\xe8\xf2\x9c\xb0\xb7\x67\xde\xda\x5f\xdb\x87\x7e\x81\xb4\xee\xf3\x93\xa3\x3f\xae\xee\xc8\x99\x5e\xdb\x95\xf5\xfa\xef\xa0\x5d\x3a\x06\xe3\xcc\x2e\x37\xde\xa5\x4a\x24\xf9\x65\x9e\x1e\x49\xe0\x71\x84\x67\x89\xdf\x1b\x63\x16\x0e\x4c\x42\xe7\x18\xc9\xa9\x16\x29\x14\xfd\xcd\x3b\xa4\x66\x58\x93\xb6\x85\x23\xc8\xa6\x8c\x98\xa1\x2d\xb3\x31\x36\x35\x49\xa2\x3c\xc4\x58\x09\x12\xe4\x23\x9a\x80\x19\x5d\xe2\x38\x81\xa8\x65\x23\x3f\x45\x21\xbe\x18\xe3\x7e\x8a\x07\x84\x0d\xf7\x59\x4a\xd5\x94\x29\x7c\xd2\x08\x8d\x83\x34\x1d\xe3\x15\x1a\xe0\xb2\xa2\x02\xc5\x71\x1c\xc5\x68\x10\xe1\x24\x5c\x4a\x91\x3f\x1c\xe2\x3e\xad\x4b\x91\x5a\x4a\x50\x82\xfb\xb3\x38\x48\x6f\x3c\x51\xb1\x37\x4b\x51\x90\x42\x25\x5e\x23\x48\x13\x11\x50\x21\x18\x07\x29\x73\xe2\xa6\x79\x5d\x03\xc2\x9f\x27\x38\xa4\xfb\x41\x62\x53\x94\xd1\x01\xf9\x40\x3b\x27\xd4\x65\xda\x5b\x79\xfe\xee\x9a\xb4\x2d\xff\x90\xf2\x5e\x36\x83\x76\x1e\x30\x32\xeb\x6d\x38\x35\x5c\xe6\x9d\x16\x02\x76\x42\x23\xbb\x17\x76\x9e\xd3\x7e\x15\xed\x92\x5f\x96\xc4\x71\xef\x4f\xab\x67\x1e\x2a\xbd\x3f\x6d\x9c\xb1\x60\x01\xe8\x2b\x79\x64\x57\x01\xb5\x76\xd9\x92\x44\xee\xfd\x69\x8d\x56\xaa\xaa\x95\x1a\xf9\x95\xea\xb4\x52\x4d\xad\x54\xcd\xaf\xd4\xa0\x95\xea\x6a\xa5\x9a\xa8\xa4\xd6\xb1\x65\x47\x32\x86\x8c\x7b\x19\xba\x06\xad\x2b\x06\xad\x6b\x1f\x34\x13\x1f\x69\xb8\x58\x9f\xe8\x85\xc9\x70\xc8\xd3\x0e\x52\xa4\x69\x90\xd5\x6a\x95\x7c\xb1\xf5\xd7\x9c\x88\x86\x0a\xb9\x66\x85\x5c\x2f\x04\xb9\xea\x1c\x78\x09\x86\x06\xb9\x51\x08\x72\xcd\x35\x3b\x9e\x04\x43\x83\x5c\xd5\x20\xcf\x9f\xc8\xae\x1f\xc7\x37\xa8\xa7\xa7\x53\xa5\x53\xd5\xa3\xf1\x2f\x4c\x4d\x46\x4a\x27\x9f\xb0\x9e\xe4\x26\x49\xf1\x04\x0d\xa3\x59\x8c\xd2\x60\xa2\xcf\xfd\x82\x41\x79\x43\x7c\x9d\x1e\x93\xd5\xe7\x8e\x1f\x6b\x89\x78\xbb\x1f\x0d\x82\xe1\x0d\xe5\x84\x94\x0e\x0b\x60\xb1\xee\xc6\xa2\x7b\x4a\x1d\x07\x7e\x3d\x85\x94\x97\x10\x6d\xc5\xc8\x14\x67\x4b\x92\xfb\x33\x4a\x70\x3a\x9b\xaa\x1f\x72\x3c\x3a\xe6\x1f\xf6\xf7\x7e\xa6\xae\x1d\x79\x27\xfc\xbd\x9f\xcf\xab\x68\x03\xed\xfd\x6c\xa6\x46\x93\x8a\xd4\x68\x91\x9a\x35\x9a\xb1\xbc\xa4\x61\x2a\x93\x59\xef\x12\x13\x51\xc1\x75\xf4\xaf\xd2\xe0\xc7\xd0\x36\x8d\x7e\xfc\x15\xd1\x27\x57\xf4\x63\xb9\x38\x0b\x73\x2c\xca\x67\xd7\xa1\xf6\x30\xc7\xa2\xd9\xba\x68\xb6\xa6\x34\x5b\x9b\xd7\x6c\x4d\x6d\xb6\xb6\x58\xb3\x10\x46\x27\xa8\xf2\x25\x48\x80\x04\x75\x75\x05\xba\xaa\x36\xa0\x6a\x9d\x2f\x66\xa8\x5a\x55\x97\xa9\x63\x46\x18\x59\xe7\xb1\x56\x04\xd4\x5a\xa5\xe7\x7a\x3d\xb6\x3f\xfd\x58\xa3\x1f\x6b\xd6\x8f\x75\xfa\xb1\x6e\xfd\xd8\xa0\x1f\x1b\xd6\x8f\xcd\xbc\x36\x5b\x79\x6d\xb6\xf3\xda\x5c\x13\x6d\xe6\x68\xa4\x0a\x71\x1e\xb4\x38\xf7\x41\xc5\x38\x10\x32\x95\x14\xb2\x1f\xd1\x83\x24\x77\x75\x2a\xaf\x25\xe9\xa3\x10\x67\x56\x8b\xd8\x7b\xe7\xde\xde\x61\x70\x33\x2f\x33\xe0\x42\x6a\xe9\x63\x1a\x6a\xe8\x57\x20\x42\x54\xfa\x95\xcc\x3d\x5f\x25\xf0\x2c\xf6\xde\xd7\x7a\xc5\x1a\xad\x58\x67\x15\xd7\xb4\x8a\x2d\x67\xc5\x3a\xad\xd8\x64\x15\x6b\x5a\xc5\x35\x67\xc5\x06\xad\xd8\x3e\x13\xa8\x29\x15\x6b\x59\xc5\x7b\xed\x62\x79\x51\xea\x29\x22\x3c\x76\xfc\x31\x4b\xc9\xce\x82\xc7\xc3\xe3\x5d\xa2\xc7\x73\x38\x8c\xc1\x09\x38\xb6\xf8\xf1\x56\x7c\xad\x4e\x78\x48\xca\xd1\x2b\xbc\xe9\x8e\xf3\xbd\xe8\x64\xea\x17\x76\x3c\xd9\xcd\x6d\xf6\x31\xb8\xa4\x5f\xda\xcd\xd5\x46\x5d\x57\xcb\x89\x65\x22\x08\xb6\x54\xd0\x15\x4a\x59\x1f\xca\x17\x49\x04\xd5\x0c\x7e\x8e\xfd\x4b\x8c\xa2\xf1\xc0\xc9\x6a\x17\x90\x1f\xba\xe7\x74\x72\xbb\x7a\xbc\x43\xa5\xc5\xae\x3f\xee\xcf\xc6\x64\x85\x85\xf8\xca\xd9\x6c\x97\x25\x82\xe9\xd2\x44\x30\xd5\xeb\xe6\xa0\x01\xff\x87\x96\xb9\x84\xa6\xe7\x6b\xe9\xb2\xbc\x30\x5d\x9a\x17\xa6\x7a\xcd\x6a\x34\x20\xa6\x7c\x97\x0b\xa8\xd5\x32\x7a\x83\x4a\xdd\x73\xe9\xf9\xbf\x50\x0d\x75\x50\xb5\x6c\x42\xac\x33\x88\x75\x0a\x91\x01\x6c\x32\x88\x35\x0d\x62\xad\x00\xc4\x06\x83\xd8\x30\xba\x55\xa2\xed\x28\x10\xeb\x05\x20\x36\x19\xc4\xa6\xb5\xd7\x0d\x0d\x62\xa3\x00\xc4\x16\x83\xd8\xb2\xf6\xba\xa9\x41\x6c\x16\x80\xd8\x66\x10\xdb\xd6\x5e\xb7\x34\x88\xad\x02\x10\xd7\x18\xc4\x35\x6b\xaf\xdb\x1a\xc4\xf6\x5c\x88\x99\xd8\x4f\x81\x2a\xd5\xd7\xf4\xea\xba\x77\x8c\xa0\x69\xb2\xfb\x5c\xac\xdc\x63\x11\x91\x52\x17\xd7\xc0\xab\x03\xd2\xb5\xae\x25\x09\x07\x4f\x97\x1f\xcf\xfa\x29\x1a\x05\x17\x23\xe4\x87\x03\x34\x8e\xae\x90\x1f\x5f\xcc\x20\xfc\x0b\xb8\x39\xff\x7b\xe6\xc7\x46\xe2\x1e\x68\xc0\x47\x1b\xa4\x15\x2e\xc5\x59\x94\x07\x17\x3d\x5a\x84\xee\x12\xd6\xe3\x13\xef\xb3\x82\x41\x8c\x93\xd9\x38\x45\xd1\x30\xaf\xf9\x11\xdd\x02\x4a\x17\x3e\x7a\x89\x2e\x7c\xea\xba\x52\x5b\x2b\xa3\x65\x44\x5f\xf5\xd8\xab\x16\xbc\xea\xc1\x2b\x1b\x92\x63\x0a\x48\xea\x0a\x3d\x12\xbe\x44\x17\xd7\x30\xc3\x65\x20\x08\x5e\x40\x88\x9d\x52\x01\x5b\x22\x18\xd2\xa1\x5f\x0f\x8e\x10\x84\x93\x94\x3f\xbe\xa3\x1c\xee\x62\x84\x7e\x43\x17\xe3\xa2\x4c\xce\xae\x54\xf9\x95\xb1\xb8\x77\x94\xc5\x95\x4a\xef\xb2\xed\x9b\xec\x64\xef\x24\xb1\xa0\xcc\x0a\xb4\xd5\x02\xed\xac\x80\x4e\xcf\xbf\x32\x6e\xf8\x8e\x72\xc3\x12\x6d\x26\xdb\x6f\xdf\x71\xfe\x07\xfb\xed\x32\x22\xad\x99\x30\xea\x0c\x46\x9d\xc3\xa8\xa9\x08\xd4\x0c\x0c\xab\x6a\x81\x6a\x1e\x86\x0d\x06\xbd\xc1\xa1\xd7\x55\x0c\xeb\x1a\x86\x35\x0b\x86\x4d\x06\xa3\xc9\x61\x34\x54\x04\x1a\x06\x86\x75\xb5\x40\x3d\x0f\xc3\x16\x83\xde\xe2\xd0\x9b\x2a\x86\x4d\x0d\xc3\x86\x05\xc3\x36\x83\xd1\xe6\x30\x5a\x2a\x02\x2d\x03\xc3\xa6\x5a\xa0\x99\x87\xe1\x1a\x83\xbe\x76\xa6\x90\x88\xc0\xb0\xad\x61\xd8\x52\x30\x2c\x94\xf8\x23\xe1\x49\x27\x84\xae\xb5\x40\xda\x89\x79\xd7\x5d\x14\x56\x8a\xaf\x53\xf9\xde\x49\xd6\xa4\xf2\x50\x0a\x4a\x1a\x07\x7a\x5b\x64\xde\x5f\x4d\xc7\x3e\xc1\xe6\x3a\x45\x4e\x70\x2c\xce\x4c\x29\x6b\xd9\x06\x51\x5c\x5c\xe5\x29\x75\xd5\xe4\x1d\x72\xc9\x72\xde\x1d\x94\x5c\xb0\xb0\x31\xb2\xa7\xde\x8d\x74\x5a\x4d\x2f\xbb\x14\xe9\xb4\xda\x1e\xbb\x2b\xe9\xb4\x6b\xb7\x67\xde\xda\x5f\x3b\x12\xe1\xd3\x7d\xd5\xd3\x7d\xd5\xa3\xdd\x57\x69\x4b\x3c\xbb\xcf\xd1\x6f\x72\xfe\x5a\x77\x38\x0f\x95\x15\xee\xbd\x38\x9a\xbf\x57\x8f\xe6\xef\xef\x7a\x34\x7f\xaf\x1e\xcd\xdf\xe7\x1d\xcd\xe7\x29\x98\x9f\x6e\xaa\x9e\x6e\xaa\x9e\x6e\xaa\x94\x2f\x4f\x37\x55\x4f\x37\x55\x4f\x37\x55\x59\xb3\x4f\x37\x55\xfa\xc7\xa7\x9b\x2a\xc7\xe3\xd3\x4d\xd5\xd3\x4d\xd5\xd3\x4d\x15\xfc\x3d\xdd\x54\x15\x53\xe2\x3e\xdd\x54\x3d\xdd\x54\x3d\xdd\x54\x49\x7f\x4f\x37\x55\x4f\x37\x55\x4f\x37\x55\x4f\x37\x55\xff\xc9\x37\x55\x0f\x76\x47\x75\xb7\xdb\xa9\x22\xf7\x52\x05\x6e\xa4\x1e\xeb\x2e\xea\xaf\x9d\x0f\xe5\xe9\x2e\xea\xef\x7f\x17\x25\xdf\x1d\x75\x9b\x73\x1d\x9d\xe4\x9b\xa3\x6e\x53\xba\x36\x82\x87\xc7\xbf\x33\xa2\x5e\x9a\xe2\xd6\xc8\x1e\x54\x80\x7b\x68\xe7\x5d\x2b\x81\x1b\xa7\xec\x51\x2c\xc5\x4c\x37\xf5\x15\x61\x90\xa2\xa4\x17\x5d\x9b\x70\x8e\x05\x3a\xc7\xf2\x35\x1d\xff\xb3\x49\x93\xf5\x56\xdb\x7d\x28\x67\x87\xee\x60\xbe\x1a\xf7\x3d\xbe\xb1\xe9\x71\xd5\x16\x3d\xee\x3f\x3e\xb7\x61\x36\x28\x64\x08\x78\x54\x89\x00\xfd\x43\x1e\x27\x87\xea\x90\x55\x22\x5b\x1b\x1f\xfb\x53\x05\x90\x19\x09\x4d\xf9\x6c\x04\x45\xb3\x9d\xfd\x49\x2f\x4a\x9f\xd1\x32\x1d\x9f\x65\xde\x68\x19\xfd\x03\x7a\xe5\x88\xa5\x70\xe5\x4f\xed\x38\xc3\xbe\x61\x6a\x08\xa4\x09\x38\xb6\x3b\xc6\x93\xd7\x64\xc6\xe7\x4f\x4f\xd7\xaa\xe2\x67\x59\x35\x04\xd1\x7c\x66\x59\x66\x05\xa0\x7b\xab\xe5\xb8\x26\x04\xb4\x20\x46\xfe\x75\x32\x3d\x76\x95\xa1\xd2\xb2\x70\x72\xae\xb7\xda\x0e\x85\x48\xd5\xa9\x0c\xb1\x36\x5a\x54\x31\x22\xad\x27\x4d\x31\x92\x0d\x5a\xa0\x7d\xf9\x9c\x0d\xe7\xdc\x0c\xf0\xa0\x1c\x54\xab\x7f\x91\xf1\xd4\xe6\x43\xac\xa6\x90\x2e\xa3\x90\xaa\xd4\x42\xcb\x22\x0a\x40\x83\x4e\x13\xc6\x31\xaa\x54\xbe\x2b\x24\xec\x20\x5c\x2b\xd1\xe6\x10\xac\x9b\x58\x33\x42\x55\xdf\xab\x9d\xfd\x4a\xea\x96\xd8\x9a\x22\x55\x18\x5e\x67\x59\x5e\x83\x50\xcf\x63\xa0\x1d\x9f\x3e\x41\x1c\x14\xcb\x8d\x56\x46\xea\x81\x71\x76\x27\x63\xa1\xcc\x15\x13\xcb\x14\xec\xbe\x57\xb9\xb7\xdb\x7c\x08\xa1\xb7\xdb\x5c\x58\xe2\x35\xf7\x58\x4d\xdc\xed\x36\xad\xb1\x2d\xe0\x86\x26\xc0\x83\x3b\xec\xf0\x5b\x71\x34\x55\x76\x79\xf6\x02\x06\xe1\x1b\x44\xc5\x1b\x90\xe6\xd4\x40\x73\x9a\x9e\x9f\x4c\x3c\x29\x25\x42\xcd\xa1\xda\xab\xba\x0c\x56\x8f\x35\x47\x50\x97\xa2\x7e\x69\xab\x98\x80\xea\xa8\x20\xd4\x88\x71\x85\x84\x18\xd2\x06\x2f\x98\x7f\x87\x41\xc6\x33\x67\x03\x17\x86\x2f\x04\x2f\xb2\x8b\xff\x04\x9b\xf9\xca\x8a\x75\x0f\x5f\x80\xdd\xa3\x39\x09\x90\xbe\xa3\xd5\x46\x86\xe8\x61\x56\x1c\x40\x5a\x7c\xd5\x31\x9a\xcf\x5f\x79\xa4\x50\xfe\x49\xb3\xdb\x7c\xac\x63\xe6\xfd\xd2\xf5\x7d\xcb\xf3\xe5\xa3\x9d\x02\xbf\x6d\x10\x67\xc2\xaa\x70\x82\xe3\x4b\xfc\xfc\x59\xa9\x5f\x46\xf5\x6a\xad\x8e\x7a\x37\xa8\xfb\xff\xfd\xbf\x83\x38\xe8\xa3\x7d\x9c\x84\xc1\xb8\x82\x36\xc7\x63\x14\x07\x17\xa3\x34\x41\xac\xfc\xa0\xf2\xfc\xf9\xb3\x23\x3c\x08\x92\x34\x0e\x7a\x33\x80\xef\x87\x03\x08\xca\x13\x84\x28\x89\x66\x71\x1f\xc3\x9b\x5e\x10\xfa\xf1\x0d\x61\x07\x93\xc4\x63\x51\x1a\x62\xf8\x37\x9a\xa5\x68\x02\x3c\xbd\x0f\x9c\xd5\x43\x7e\x8c\xd1\x14\xc7\x93\x20\x4d\xf1\x00\x4d\xe3\xe8\x32\x18\xe0\x01\x0d\x3a\x41\xd6\xe9\x30\x1a\x8f\xa3\xab\x20\xbc\x40\xfd\x28\x1c\x04\x74\x0d\x93\x4a\x13\x9c\x76\xd8\x8a\x5f\x41\x2a\x5a\x09\x28\x86\x29\x3e\xfd\x68\x80\xd1\x64\x96\xa4\x64\xa3\xf6\x83\x10\x80\xfa\xbd\xe8\x92\x7c\x9a\xde\x40\x17\x51\x18\xa5\x41\x1f\x7b\x34\xae\xd0\x38\x48\x40\xb3\x2c\xb7\x17\x0e\x34\x64\x06\x41\xd2\x1f\xfb\xc1\x04\xc7\x15\x17\x0e\x41\x28\x0f\x04\xc7\x61\x1a\x47\x83\x59\x1f\x3f\x38\x1a\x88\x75\x6d\x10\xf5\x67\x22\x0e\x06\xa9\xb1\x1a\xc5\x2c\x46\xc6\xc4\x4f\x71\x1c\xf8\xe3\x24\x1b\x66\x98\x1b\xa8\x26\xa1\x4e\xe6\xf9\x64\x77\xef\x18\x1d\x1f\xec\x9c\xfc\xb2\x79\xb4\x8d\xf6\x8e\xd1\xe1\xd1\xc1\xcf\x7b\x5b\xdb\x5b\xe8\xed\xbf\xd0\xc9\xee\x36\xea\x1e\x1c\xfe\xeb\x68\xef\xdd\xee\x09\xda\x3d\xf8\xb0\xb5\x7d\x74\x8c\x36\x3f\x6e\xa1\xee\xc1\xc7\x93\xa3\xbd\xb7\x9f\x4e\x0e\x8e\x8e\xd1\x8b\xcd\x63\xb4\x77\xfc\x02\x3e\x6c\x7e\xfc\x17\xda\xfe\xf5\xf0\x68\xfb\xf8\x18\x1d\x1c\xa1\xbd\xfd\xc3\x0f\x7b\xdb\x5b\xe8\x97\xcd\xa3\xa3\xcd\x8f\x27\x7b\xdb\xc7\x1e\xda\xfb\xd8\xfd\xf0\x69\x6b\xef\xe3\x3b\x0f\xbd\xfd\x74\x82\x3e\x1e\x9c\xa0\x0f\x7b\xfb\x7b\x27\xdb\x5b\xe8\xe4\xc0\x83\x46\xcd\x6a\xe8\x60\x07\xed\x6f\x1f\x75\x77\x37\x3f\x9e\x6c\xbe\xdd\xfb\xb0\x77\xf2\x2f\x68\x6f\x67\xef\xe4\x23\x69\x6b\xe7\xe0\x08\x6d\xa2\xc3\xcd\xa3\x93\xbd\xee\xa7\x0f\x9b\x47\xe8\xf0\xd3\xd1\xe1\xc1\xf1\x36\x22\xdd\xda\xda\x3b\xee\x7e\xd8\xdc\xdb\xdf\xde\xaa\xa0\xbd\x8f\xe8\xe3\x01\xda\xfe\x79\xfb\xe3\x09\x3a\xde\xdd\xfc\xf0\xc1\xda\x4b\x82\xbb\xd2\xc7\xb7\xdb\xe8\xc3\xde\xe6\xdb\x0f\xdb\xb4\xa5\x8f\xff\x42\x5b\x7b\x47\xdb\xdd\x13\xd2\x9d\xec\x57\x77\x6f\x6b\xfb\xe3\xc9\xe6\x07\x0f\x1d\x1f\x6e\x77\xf7\xc8\x8f\xed\x5f\xb7\xf7\x0f\x3f\x6c\x1e\xfd\xcb\x63\x30\x8f\xb7\xff\xef\xa7\xed\x8f\x27\x7b\x9b\x1f\xd0\xd6\xe6\xfe\xe6\xbb\xed\x63\x54\x9a\x33\x24\x87\x47\x07\xdd\x4f\x47\xdb\xfb\x04\xe7\x83\x1d\x74\xfc\xe9\xed\xf1\xc9\xde\xc9\xa7\x93\x6d\xf4\xee\xe0\x60\x0b\x06\xfa\x78\xfb\xe8\xe7\xbd\xee\xf6\xf1\x6b\xf4\xe1\xe0\x18\x46\xeb\xd3\xf1\xb6\x87\xb6\x36\x4f\x36\xa1\xe1\xc3\xa3\x83\x9d\xbd\x93\xe3\xd7\xe4\xf7\xdb\x4f\xc7\x7b\x30\x68\x7b\x1f\x4f\xb6\x8f\x8e\x3e\x1d\x9e\xec\x1d\x7c\x2c\xa3\xdd\x83\x5f\xb6\x7f\xde\x3e\x42\xdd\xcd\x4f\xc7\xdb\x5b\x30\xba\x07\x1f\xa1\xab\x27\xbb\xdb\x07\x47\xff\x22\x40\xc9\x18\xc0\xe0\x7b\xe8\x97\xdd\xed\x93\xdd\xed\x23\x32\xa0\x30\x52\x9b\x64\x08\x8e\x4f\x8e\xf6\xba\x27\x72\xb1\x83\x23\x74\x72\x70\x74\x22\xf5\x11\x7d\xdc\x7e\xf7\x61\xef\xdd\xf6\xc7\xee\x36\xf9\x7a\x40\xa0\xfc\xb2\x77\xbc\x5d\x46\x9b\x47\x7b\xc7\xa4\xc0\x1e\x6d\xf6\x97\xcd\x7f\xa1\x83\x4f\xd0\x65\x32\x47\x9f\x8e\xb7\xe9\x4f\x89\x62\x3d\x98\x49\xb4\xb7\x83\x36\xb7\x7e\xde\x23\x68\xb3\xc2\x87\x07\xc7\xc7\x7b\x8c\x4e\x60\xc8\xba\xbb\x6c\xb8\x2b\xcf\x9f\xbd\x5c\x55\x75\x5e\xfb\x7e\x3a\x7a\x58\xbd\x57\xb1\xa8\xd3\x34\xf0\xb1\x28\x42\x1f\x0b\x59\x67\xc3\x85\x9d\x1f\xa6\x09\x4a\xfd\x1e\x97\x58\x48\x95\xf3\xdf\xc7\xd6\x60\x9b\x99\x1c\x55\xf5\x10\xaa\x79\x08\xd5\x3d\x84\x1a\x1e\x42\x4d\x0f\xa1\x96\x87\x50\xdb\x43\x68\xcd\x43\x68\xdd\x43\xe8\x95\x87\x6a\x55\x0f\xd5\x6a\x1e\xaa\xd5\x3d\x54\x6b\x78\xa8\xd6\xf4\x50\xad\x25\x59\x58\xae\xd1\xba\xe4\x1b\x81\x47\xca\x13\x18\xb5\x16\x85\x4b\xea\x41\x5b\xaf\x18\xfc\x3a\x83\x51\x83\x36\x32\x38\x0d\xd6\x56\x93\xe1\xf2\x8a\xc1\x58\x97\xf0\x5c\x63\xb0\xda\x0c\x97\x1a\x85\x59\x93\x63\x2d\xd7\x58\x5d\x8e\x4b\x95\xc2\x00\x3c\x38\x9e\x0d\x0a\x8b\xc0\xaf\xc9\xfd\x96\xe1\x34\x59\xdd\x16\xc3\x7d\x8d\xc1\xa8\x4b\x78\xd6\x18\xac\x75\x86\x0b\xeb\x77\xad\x71\x56\x7e\x2d\xcf\x45\x3c\x67\x2e\x38\x1e\x6b\xd2\x58\xd5\x19\x4c\x8e\x73\x5b\x1d\x0f\xe8\x5b\x43\xeb\x7b\x9b\xd5\x69\x64\xb0\xa0\x6e\x2b\xc3\x99\xc3\xe0\xe3\x01\x6d\xd5\xb4\xbe\x43\xa1\x96\xd4\xc1\x35\x86\x60\x3b\x1b\x5c\x01\xa4\x2e\x0d\x34\x45\x36\x03\xb4\xce\xea\x48\x83\x05\x13\xd3\xca\x06\x57\xc0\x68\x48\x03\x4d\x91\x95\x10\xaa\xb3\x91\xad\x4a\xc0\xf8\x68\xac\x89\xd9\x13\x14\x8a\xd8\xe8\x50\x64\xd5\xd9\x48\xe6\xad\x0c\x8a\x22\x1b\x2b\x40\x4f\x6e\x89\xd3\x56\x43\x1a\xcf\x76\xf6\x4d\xa1\xe9\x35\x0f\x3e\xc1\x50\x71\x7a\x7d\x95\xd1\x1e\xa7\xa9\x5a\x4b\x1a\xd6\x35\x56\x56\x99\x8f\x5a\x46\x04\x62\x2e\x5e\xb1\x82\x9c\x78\xd6\xa5\x32\x1c\xf1\x35\xf8\x2d\x9f\xa5\xc4\x5a\x6e\x66\x55\x79\xfb\x62\xcd\xcb\x6b\x62\x5d\x01\x99\x81\xe2\xeb\xb3\x95\xd1\xbe\xe8\x67\x3d\x43\x41\x8c\x13\x23\x19\x0a\x17\x69\x53\x32\x6f\x81\x30\xc4\x94\xc1\x6f\x65\x08\x40\x3f\xd7\xb2\x85\x08\x0d\x36\x19\x22\x6d\x0d\xe9\x86\x3a\xf8\xa2\xd3\xb5\x0c\x8e\x18\x3b\xb1\xa0\xe1\xbb\x02\x47\x30\x90\x9a\x34\x48\xed\xac\x5d\xb1\xf0\xd8\x02\xae\x35\x2c\xf3\x21\x3a\xa0\x21\xce\x01\x89\x05\x57\x97\xfe\x6d\x89\x55\xac\x0e\x50\xcb\x52\xae\xa9\xce\x8c\x98\xc9\xac\x53\xa8\x56\x43\x67\x4a\x96\xec\xf3\x11\x59\x21\x96\xf9\x40\x22\x54\x73\xd5\x43\xd5\xeb\xd6\xe6\x7a\x7d\xed\xd5\xab\x57\xe4\x77\x7b\x7b\xeb\xd5\xf6\xdb\xcd\x1a\xf9\xbd\xbe\x53\x7b\xfb\xb6\xbb\xd5\x25\xbf\x37\x5f\xb5\x1a\x3b\x5b\xcd\x6d\x75\xbe\x47\xb1\xb3\x81\x56\x75\xb3\xbe\xfe\x76\xbb\x0d\x0d\x74\x9b\x5b\x5b\xb5\x7a\x13\x1a\xd8\x5a\xab\x36\xb6\x77\x1a\xe4\xf7\xda\x66\x7b\x6b\xad\xbd\x0d\x0d\x73\x84\xce\xac\xfa\x80\xa3\xbd\xc3\xed\xfd\xad\x5a\xbb\x0a\xe1\xf7\xe7\xe8\x90\x44\xd9\x4c\x8b\x24\xbd\xa2\xbb\xf2\x5d\xef\x8a\xa8\x32\x11\x90\x70\x04\xc1\x6e\xaf\x35\x5b\xf5\x46\x15\x46\x70\x7b\xa7\xbb\xb5\xf9\x76\x1d\x3a\xf8\x6a\xfd\xed\xe6\x56\x77\x67\x9b\xfc\xae\x55\x1b\xf5\x56\x73\x0d\x06\xa7\xdb\xd8\xaa\x6f\xd7\x76\xaa\x67\x4e\xd5\x78\x51\xa5\xbc\x55\xb1\x5b\xd8\x4b\xa9\x96\x73\x53\x33\xdf\x1c\x9f\x62\x01\xba\xd7\xcc\x2c\xd2\x71\x7d\xb3\x7f\x2e\x95\xe6\x97\x07\xe7\xa6\x21\x13\xca\xbb\x53\x91\xea\xa1\x0d\x54\x32\x0b\x20\x6a\x00\x2a\x35\x96\x19\x3e\x48\x2f\x17\x33\x2a\x35\x00\x32\xbb\x52\x0d\xa0\x69\x5d\x6a\x82\xcb\x51\x8d\xa1\x79\xb6\xce\xbb\x48\xdc\x3f\x10\x52\x74\x5e\x39\x02\x03\x38\x1f\x8d\xdd\x05\x62\x28\x10\x3b\x0b\x80\xf8\x79\xfe\xbb\x1b\x02\xc8\x44\xe7\xbf\xbb\x21\xc0\x36\x7d\x9e\xb8\x21\xc0\xa6\x71\x9e\xc4\xf6\x88\xd6\xab\xab\x64\x95\x7d\x21\x87\xe6\x4b\x3f\x0e\x88\x74\x6c\xb9\xa4\xf5\xc7\x1e\xea\x8d\x3d\xd4\x1f\x7b\x68\x30\xf6\x10\x1e\x5b\x1a\xf2\x63\x0f\xf5\x62\x0f\xf5\x63\x0f\x0d\x62\x0f\xe1\x58\x6f\xcc\x27\xa8\xf8\x04\xe1\x5d\xd3\x65\xa4\x17\x43\xd0\x71\xf8\x58\xd3\x3f\xf6\xc9\xc7\x3e\xfd\x58\xff\xff\xd9\x7b\xfb\x2d\xa9\x6d\x6c\x51\xfc\xef\xf0\x14\x9a\xf9\xad\x81\x6a\xba\xe8\xb6\xe4\x2f\x19\xe8\xfc\x2e\x21\x70\x3a\x37\x10\x58\xc0\xdc\x70\x16\x0b\x32\xb2\x2d\x77\x39\x54\x57\xf5\xa9\x72\xd3\xd5\x49\xc8\xba\xaf\x71\x5f\xef\x3e\xc9\x5d\xda\x92\x6d\xd9\x96\xe4\xaa\xa6\xc9\x99\xcc\xd0\xb3\x86\x54\x95\xa4\xbd\xb7\xf6\x97\xb6\xbe\xb6\xfa\x85\xb9\x28\xcc\x65\xa1\xdf\x2f\x84\x09\x03\x97\x85\x41\xbf\xb0\x79\xa6\x9a\x75\xdf\xa5\xae\xbb\xd4\xdf\x15\x34\x1e\x25\x84\xff\xee\x1f\x21\x6c\xb4\xed\x4a\x98\x0f\x9b\xa3\xfd\xd6\xa6\xf6\x7f\x99\xbf\x29\xdf\xbe\xdd\xfb\xcd\x74\x89\x01\x6e\xed\xdc\xc7\xd1\xde\xaf\x37\xbe\xea\xba\x46\x81\x03\x15\x78\x92\xce\xa7\xd9\x7c\x9a\xcf\xf7\xd0\x3e\x9a\xcd\xcd\x77\x6f\x3e\xa2\x66\x41\xae\xbc\xef\x13\xb9\xd4\x66\x80\x46\xfa\xd0\x06\x9c\x1f\x40\x0b\xa8\x15\x9a\xdf\x87\x36\x10\xd5\x00\x5a\x14\x58\xa1\x05\x7d\x68\x03\xd9\x6a\xd0\x7e\x3d\x3c\x54\x10\xa9\x67\x85\x18\xf6\x21\x0e\x14\x02\x99\xd3\xa4\x0b\x21\x56\x46\x71\x89\x12\xb4\x5a\x56\xf3\x49\x35\x5d\x0b\xb1\x9a\x2e\x6d\x80\x0e\x54\xfb\x7c\x6e\x16\x39\x58\xc4\xc0\xa4\xc4\x1f\xe8\x6d\x6e\x2a\x01\x75\x07\xbc\xc2\x26\xb1\xf1\x1a\x10\xd8\x4b\x6a\x6a\x0d\x66\x36\xd8\x49\x6c\x48\x65\x2b\xb4\xaf\x69\xeb\xea\xea\xda\x1a\x4e\xd2\xd5\x34\x5b\x4d\xf3\x15\x70\x7c\xf5\x69\xda\x1a\xf4\xa1\x7d\xaa\xb6\x76\xa1\x7d\x92\xb6\x92\x3e\xb4\x4f\xd6\x56\xdc\x87\x78\xcd\xda\xba\x82\x5d\x6b\x87\xba\xae\x2c\xea\x0a\x1e\x75\x65\x52\x57\x70\xc4\xa6\x12\x70\xd1\x52\x5d\x57\x56\x75\x85\x01\xc0\xd4\x1a\x86\x86\xe1\x09\x8d\xbe\x2b\xff\x4e\x7f\x8e\x01\x62\x48\x38\xf5\xdb\x8b\x30\xc5\x3f\x47\x68\x72\x2c\x8f\xe6\x66\xc2\x33\xe7\x86\x9e\x1e\xab\x23\xbc\xc7\xf2\xf8\x6d\x2e\xea\x99\x38\x72\xac\x8e\xe9\x1e\xcb\x83\xb4\x5c\xd4\x63\xc6\x7a\xbe\xaa\x07\x87\x65\x61\x44\x48\x8d\xf5\x02\x55\x0f\x0e\x26\xa7\xa2\x5e\x66\xac\x07\x07\x98\x3b\x6c\xe9\x87\xb5\x8f\xd5\xd3\x1a\x9f\x70\x3c\x2b\x67\x15\x6b\x82\x21\xf1\xc5\x30\xf0\x8f\x3f\xc3\x58\xd7\x5c\x7c\x53\x56\xeb\x57\xcb\x0a\x3c\x9e\x84\xb9\xf8\x96\x55\x4c\x9e\xda\xba\x8d\xa8\x01\x3a\xb4\x79\xc2\x8b\x6a\xf0\x68\x23\xd4\x1f\x74\xe6\x41\x9e\x0f\x5f\x21\x46\xea\xbd\x45\x79\x98\xa9\x05\x29\xa2\xc9\xf0\x2d\xfa\xed\x48\x3e\x2c\xdc\x9e\x91\x68\x6a\xfc\x0d\xf9\xa4\xaf\xad\x2d\xa4\xc9\x64\xd2\x56\xdd\x47\xc2\x3f\x08\x90\xc9\x9e\x00\x15\x08\xbb\xc5\x81\x25\x80\xae\x9b\x4a\x76\xb4\xc1\xb3\xf6\xe3\xf6\xc1\xf3\x00\x98\x0a\x9c\x7b\xc0\xc6\x02\x67\x53\x47\xf5\x77\x3a\xda\xf7\x30\xeb\x37\x76\xe0\x70\x8c\xe1\xd9\x8e\xc3\x43\x98\x09\x22\x78\xdd\x45\x5e\xc8\x32\x1e\x9c\x3a\x93\x33\xaf\xe1\x6b\x2e\x6e\xb5\x04\xeb\xd6\x63\x74\x83\xe2\x1c\xa3\x23\xa4\x87\xef\x9f\x36\x7f\x0b\xb7\x9a\xbe\x99\x67\x64\xc7\x30\x15\x3b\x36\x5c\x26\x41\xae\x39\xd8\x71\x73\x5d\xef\xb8\x33\xbd\x3a\xde\x79\x5e\x25\x35\xe4\xb8\x33\xa7\x3a\xb6\x4e\xa6\xc6\x8f\xc2\xbd\x90\x3b\xe1\x52\xb8\xea\x05\x8b\x1c\x98\xdd\xad\xaa\x76\xcc\x7b\x02\xea\xb8\xa9\x6c\xbe\x5c\xb8\x1d\x14\x1c\x25\x10\xb5\xda\xd5\x05\xf8\x6a\x3f\x06\x21\x8b\x7f\x1a\x28\x89\x6c\x37\xd4\x35\x45\x26\x94\x76\xce\x45\xc1\xc7\x8f\x72\xf7\x1f\xe9\x27\xe2\x0a\x3c\xd9\x4c\xd1\xe5\x14\xfd\x62\x7a\xe6\x63\x32\xd9\xc0\xcd\xce\x4b\xf8\xf7\x97\xf6\xb5\xf6\x8f\x03\x38\xc4\x0d\x67\xb2\xd9\xbb\x39\xb9\xdc\x93\xd7\xc9\x7f\x17\x5f\x7e\xd9\xdb\xdb\xbb\x67\x83\xe6\x8f\x42\x13\x80\x7e\x17\x10\x5b\xd2\x2c\xb0\x82\x71\x58\x37\x01\x02\xd0\x76\xb9\x77\x73\xf2\x3b\x10\x67\x87\x18\x6e\xc3\x33\xc1\xb4\xdf\x5a\x50\x16\x58\x10\x4a\x6c\xa6\x0b\x23\xa4\xcd\xfd\xfb\x0b\xa0\x6a\xf3\xf5\xd7\x5f\x4f\x7c\x72\x67\xa1\x13\x25\x3f\x38\x4f\xc3\xd4\x87\x61\xe4\x3b\x70\xdb\x1d\x86\xb1\xbe\xf6\xa3\xce\xb7\xc0\x99\xa7\xfa\x73\xb5\x94\x9e\x69\x08\xc6\xf2\x3e\x8f\xa5\xf6\x55\x1f\xe6\x51\x96\xd1\x9e\x64\xa9\x17\xf0\x26\xb7\x14\x89\xb7\x0c\xa7\x70\xec\xad\x2e\x6a\x6a\x4d\xc7\x6d\x86\x8b\x83\xbd\xa3\x36\x75\x85\xed\x8e\x2a\xd5\xc2\x39\x7e\xfa\xe0\xe1\x1f\x20\x1a\x47\xf3\xf7\xfc\x12\x9a\xae\x79\xb6\xe2\x95\xe5\xed\x24\x8b\x40\xe1\xc9\xc1\x6b\x14\xa8\x7c\xc8\xb0\x11\xcd\xf1\x29\xcb\x5a\xf1\xe8\x47\xac\x0c\x12\xea\x54\x1e\x4a\xe9\x94\x65\x06\x49\x7d\xf5\x51\xee\x03\x5b\x8e\x46\xd5\x35\xcd\xaf\x13\x7d\x7c\x3b\x8d\xe3\x2f\x47\x9c\xfe\x15\xae\xac\x7c\xee\xad\xfb\x5e\x62\x35\x0d\xb1\x35\x65\xda\xcb\xe3\x07\x77\xf0\x16\x3b\x19\xc3\xb7\xaa\xaf\x73\xff\xe2\x08\x6e\x9f\xb6\x5b\x18\xe5\xa2\xac\x26\x86\x04\x54\xdd\x2d\x0d\x5e\x64\x39\x4b\x69\x62\xc8\xcd\xe4\x6d\x12\x9a\xb2\x3c\x2b\x78\x67\x8f\xc3\x54\x31\xf3\x73\xc2\x71\xe1\x75\xcb\x3e\x7d\x0b\xc4\x16\xa1\x9b\x83\xef\xe1\x0a\xfa\x00\xc0\x36\x6b\xcf\xe6\xe5\x62\x51\x94\x9a\x17\x8b\x21\x60\x34\x2f\x15\xc3\x74\xd5\xbc\x50\x2c\x8a\x78\xb3\x4c\x3c\xa0\xd4\xba\x4e\x6c\x5d\x13\xb6\xcc\x16\x60\xdd\x07\xc9\x1b\xa6\x96\x5c\x30\x3f\xca\xc0\xbf\x9b\x02\xa3\x7b\xf7\xb4\xfe\xab\x17\x94\xcc\x80\xea\x7b\x0e\x3f\xbe\x29\xd1\x1d\xe4\xbf\x45\xef\xd4\x47\xda\x7e\xc4\x81\xf6\x39\xb2\xbd\x1d\xa9\x48\x9a\x2c\xe0\x72\xac\x9c\x5b\xc2\xf4\xc1\xc7\xe6\x34\x35\xe6\x99\x10\x2c\x2d\x4d\x98\x00\x12\x02\x10\x26\x67\x32\x31\x5c\x90\xe5\x68\x1f\x10\xd9\x16\x1a\xd1\x7d\x44\x3c\x2b\xd7\x60\xd9\x6c\x32\x49\xd1\x4d\x94\xc9\x38\x57\x7c\xcc\x01\xb2\xb7\x09\x99\xdc\x85\x1d\x59\xe2\x43\xf7\x51\x30\x86\x22\x45\xef\x50\x86\xde\xa1\x5c\x42\x8e\x78\x9e\xf0\x94\x99\x92\x0e\xf5\x20\x47\x3b\x10\x2f\x69\x17\x9f\x32\xd5\x8b\x3b\xc8\xdb\xc4\x1e\x0f\x02\x9f\x04\x76\x5c\x87\xb7\x1b\x74\xd4\xdb\x43\xb7\x0f\xb7\xee\x8b\x80\xef\x87\x49\xee\x73\xd2\x5f\xe5\x41\x16\x91\x0a\x7b\xc9\x4d\xcb\x7d\xe8\x08\x65\xa6\x25\x3e\x04\x28\xef\xdf\x47\xbe\xa7\x7a\x09\xe2\x37\xbe\x2d\x8a\x8e\x90\x89\x0e\xb6\xdd\x6d\xad\xad\x16\x03\xd5\x22\x5a\xbd\xd8\xc6\xfa\x37\xbc\x51\x67\x21\x10\x16\x0c\x07\x99\x4f\x50\x67\x11\x10\x16\x0b\x33\x73\x1d\x5f\x5f\x28\xcc\xcd\x75\x02\x7d\x91\x90\xf7\xeb\x7c\x59\xe0\xfb\x67\x5d\xe0\x13\xb1\xf0\x41\x31\x5f\x2e\x57\xfa\x9a\xdb\x21\x0c\xd4\xea\xef\x93\x90\x40\x2e\x84\x16\xf2\xc8\x3a\xdd\x60\x99\xee\x33\xad\xd0\xed\xb8\x0e\x64\x5c\xae\xfb\x33\xae\x06\x7d\x59\x42\x18\x2c\x06\x88\xf0\x79\xa7\xd5\x03\x68\xe0\x5a\x38\xe8\x06\xe4\xdd\x35\x03\x51\xf6\x65\xb9\xe0\x5a\x97\x0b\x40\x1e\x5b\xac\x14\x98\xc5\xd2\x2e\x12\x28\xd1\xd8\xaf\x4d\x89\x0a\xf6\x65\x01\xfa\xa7\x4e\xb0\xb1\x9e\x31\x12\x46\x9f\x3b\x37\x86\xc2\xf2\xef\xb3\x7c\x30\x58\x1e\xd0\xe7\xf0\x24\x8c\x3a\xb3\x78\xed\x16\x76\x7f\x55\x80\x90\x60\xbb\x75\x01\x51\xb1\x03\x13\xbe\x4b\xe0\x7f\xe8\xda\x40\x86\xbd\x30\xe1\x39\x15\x53\x7e\x3f\x8a\xb3\x3c\xf4\x62\xf8\xec\xc5\x5e\x9e\x63\xf8\x5c\xc4\x1e\x0f\x13\xdf\xbc\x66\x50\x14\x99\xe7\xa5\x3e\x2c\x2e\x44\x34\xa4\x38\xc4\xf2\x73\x50\x24\xb4\x60\x00\x20\xe5\x05\x0b\x0a\x16\xec\xb0\x5c\xb0\x55\xe4\xa9\xb9\x7d\xc5\x3a\xad\xa5\xe3\x16\x2d\x78\xd4\x26\x9c\xb9\x73\x34\x0c\x5e\x2c\x1b\x4b\x5f\x86\xe8\x91\x11\x97\x90\x60\xd7\x41\x5a\x34\x19\x19\xa6\x3b\xd6\x31\x18\xa8\x09\x31\x5f\x62\xff\x32\x54\x7f\xc2\x50\x2d\xa4\xb2\xdd\x60\x6d\x14\x4e\x67\xb8\x96\x02\x72\x0e\xd8\x84\xf4\xaf\x3a\x6b\xf7\x9a\xd5\x70\x74\x37\x4e\xc4\x00\x9e\x7c\x59\xd7\xff\xef\x19\x98\xff\x7c\xd7\xf2\xbe\x93\x8f\x38\x94\xbf\x34\xb7\x72\xd1\x6a\x79\xbe\xc8\x51\xd6\xbd\xaf\xa7\xf5\xe0\xb8\xff\x74\xca\xf7\xdd\x6d\x80\x7a\xa1\x96\xb7\x30\x64\x89\x29\x82\x41\xfa\x96\x72\xb9\x7e\xbe\x2a\x4f\xf9\x64\x61\x1c\xc6\xd6\xff\xb5\xaa\x7e\xa8\xe7\xf9\xe2\xcb\x64\xd1\x9f\x67\x36\x0b\xc1\x52\x9c\xe8\x08\x91\x7b\xf5\xe7\xfb\x47\x12\x42\xfd\x83\x63\x6d\xf8\x2f\x93\x05\xfa\x9b\xaa\xb6\x67\x5d\x2f\x54\x36\x5a\xb0\xf9\x9a\x8f\x9f\x0a\xec\xaf\x8f\xd5\xf3\xf1\xd5\x79\x77\x86\x6b\x60\xcb\x09\xaf\x1e\xaf\x18\x7c\x66\xf3\x6f\xca\x6a\x6d\x60\x50\xb3\x85\xbf\x40\x77\xd0\x64\x01\x99\x3d\xf7\xd0\xed\xce\xe2\x47\x7f\x25\x4b\xc3\x55\xaf\x52\xeb\x99\xd9\xe1\x37\x10\x48\x2f\x7f\xcf\xc5\xac\x9c\x73\x34\x51\x65\xf7\x91\x3a\x92\xd9\xe7\x62\x2b\x4d\x2b\xa3\x1b\x10\xd4\xca\xe5\xe3\x37\xb2\x12\xa4\x1d\x1d\x30\x02\x74\xe1\x6c\x79\x31\x59\x4c\x11\x46\x87\x88\xec\x6d\x91\xb1\x1d\xc1\x4b\x28\xbb\x80\xf5\xf7\x8c\xc9\xb3\x25\x88\xfd\xfd\x91\xa5\xd0\x45\xa7\x46\x1d\x21\x4d\x5a\x98\x57\xdf\x63\x13\x81\xf7\x76\xd1\xf4\x30\x42\xff\xec\x3b\x6d\xc7\x07\xeb\x79\x99\xf1\x89\xb7\xf7\x65\xd7\x6b\xeb\x5d\xaf\x41\x51\x01\x45\xa1\xa9\xe8\x04\x8a\x06\x1b\x46\x10\xb3\x40\x51\xfc\xc9\xdb\x68\x91\x23\xd7\xfd\x1f\xbd\x8d\x76\xc2\x4e\x4f\x99\xb7\x69\x36\xd3\xf0\x80\x29\xc3\xda\x70\xd0\x78\x52\xb7\xbc\x7f\x1f\x11\xb9\xe9\x55\xff\xf2\xf5\xd7\x5f\xa3\x78\x6f\x0f\xa1\x77\x66\x48\xdd\xbf\x0e\x24\x1c\x0c\x20\x61\xba\xb7\xb7\x1d\xa4\x6e\x3b\xdf\xe8\x5e\x3a\x3d\xc1\x6d\xbf\x8d\x87\xe4\xbb\x95\xb5\x6e\x63\x49\xac\xd6\x6d\xbc\xa9\xf3\x4d\x6f\x49\x6c\x17\x92\x3f\x84\x94\xec\xd8\xed\xba\x9d\xf9\x4d\x02\xd4\x2a\x8e\x12\xe2\xbe\xea\x39\x24\xf9\x55\x3d\xdc\x77\x6e\x98\xda\x76\x3f\x33\xb8\xd5\x38\xe1\xe8\x26\x2a\xe0\xb0\xdb\xef\xe2\xe3\x89\xed\x09\x97\x53\x06\x19\xe6\x18\xba\x89\x52\xa8\xce\xe4\xee\xe0\x3b\xa4\xf6\x09\x4d\xf4\x43\xb0\x52\x9e\x08\xc2\x9b\xad\x56\xb5\xd9\xa6\xf6\x5a\xe5\xd1\x3f\x59\x82\x13\xad\x04\xfb\x9d\xa2\x4e\x23\xf3\xd8\xd6\x20\x83\x77\x6a\x26\x1c\x74\x5c\x66\x4e\xe6\xd0\x2e\x52\x10\x65\x09\xd6\x4a\x30\xd6\x8b\x62\x79\xb2\x55\x16\x91\xd0\x3c\xe2\xc1\x06\xb2\xc0\x34\x43\xfb\x35\xda\x7d\xc1\xd4\x7d\xf9\xd0\x9b\x75\xf3\x18\x1a\x12\x74\x54\x33\x66\x5f\xb0\xd6\x84\x41\x38\xae\x13\x03\x00\xe1\xeb\xfa\x79\xda\xc5\x9f\x70\x8f\xa6\xf0\x0b\x72\x67\xc2\x6b\x09\xd8\xb4\xcd\x87\x46\xb6\x48\xfb\xd9\xd6\xd1\xc8\x76\xe8\xa4\x12\x8c\xa8\x88\x09\xd7\xbf\xcb\xd6\xa8\xac\x13\xaa\x3a\x90\x32\xbc\x30\xd7\x89\x54\x1d\x48\x09\x7e\x62\xae\x13\xab\x3a\x60\xf3\xb3\x2f\xdb\xb0\x5f\xb6\x61\xbf\x6c\xc3\x0e\xa3\xcd\x2f\xdb\xb0\xff\x94\x6b\xbc\x61\xb4\xf3\x1a\x6f\x18\x8d\xae\xf1\xea\x73\xb6\xe1\x1a\x6f\x18\x7d\x59\xe3\xbd\xf6\x35\xde\x30\xda\x76\x8d\xd7\x24\x9c\xee\x1a\x2f\x08\xc8\x7d\x68\xbb\xd9\x3b\x33\x6f\xcd\x52\xef\x4f\xbd\x35\xbb\x89\x82\x3f\xe4\xe1\x82\x06\xcf\x97\x55\xe0\xee\x2a\xf0\x26\x82\x3d\xd5\x83\x4d\x14\x68\xbf\xbf\x8e\x02\x95\xa5\x1b\x6a\x1c\x68\x79\xa2\x77\xca\xe9\xa6\xf5\xef\xc5\xf1\xb3\x9f\x9e\x3d\x7e\xfc\xf2\xd1\xab\x97\xfd\xd5\xe2\xe7\xdf\xfd\xf4\xdd\x0f\xdf\x3e\x7a\xfd\x68\xf8\x2a\xf7\x8b\x67\x7f\xff\xe1\xdb\x9f\x1e\x3e\xfb\xe1\xe5\xab\x07\x3f\x34\x2d\x35\x74\x72\x59\xf9\xe1\x76\xcb\xca\x5a\x8b\xd5\x6c\x59\x27\x6d\xe9\xad\x49\xd7\xa8\xc5\xec\x1a\x4f\xd1\xa5\x2d\x55\x79\x25\x97\x44\x2a\x74\x1f\x91\xe0\x1e\xaa\x0c\x4b\x22\x5a\x9f\xdf\x6c\xd0\x3e\x0a\xd1\x6d\x74\x29\x6f\x0f\x56\xf5\x25\x4d\xf8\x44\xf6\x60\xa5\x12\xfd\x0d\x45\x83\x58\x04\xc2\x40\x7e\xf1\x1a\x1d\xa1\x4b\xf4\x37\x14\x9a\xa2\x44\x7e\xf1\x9f\x02\x2a\x41\xb7\x91\xc0\xe3\x0b\x3c\x7b\x86\xca\x1b\xb9\x2c\xf7\xba\xf7\xf3\xa5\xfc\xf9\x3f\x2d\x4b\xc1\x1a\xdb\xce\x4a\x54\xc2\x73\x02\x06\xa6\x35\x9c\xd9\x48\xce\x6c\xe4\x05\xcd\x8d\x81\x31\x4d\x55\xc9\x5d\x74\x29\xab\x5e\x5a\x96\x95\x5a\x05\xe9\xb2\xf1\x12\x1e\xf8\x19\xf6\x5a\xf0\xb5\xdf\xf5\x8f\xa3\x7d\xeb\xed\x72\x74\xb5\xe1\xc9\xe3\x97\x2f\x04\xad\x1b\x0f\x9b\x94\x41\x7f\x77\xc2\xb2\x3e\x26\xaa\x01\x8a\x5a\x59\x9f\xae\x2f\x7a\xba\x65\xac\xf6\xa4\xae\x66\x61\xa1\x7a\x79\xe2\x67\x74\x1f\xc5\xf7\xd0\xcf\x8e\x95\x39\xe8\x03\x5c\x4d\x35\x67\x45\xa9\xd1\xa7\x65\xf5\x7c\xb9\x86\x3c\xae\x42\xab\xe0\xb1\xdc\x9f\xf7\xd0\x1d\x64\x3a\x4d\x5d\x03\xd7\x1b\xdd\x47\x2a\x5f\x84\xa9\xb2\xf8\x1b\x74\xf0\xdd\x11\x02\x34\x1a\x14\x0b\xae\xee\x89\x6a\x1d\xeb\xd7\x47\x80\xd6\x7e\xb8\x7a\x80\xf9\xa9\x86\xb9\x03\xea\x8e\x61\xde\xd3\x10\xb0\xdd\xd2\x92\xa6\x58\x0b\xbe\xa9\x40\x81\x46\xc4\x42\xed\x27\xd1\x0f\x0f\xd1\xf3\x55\x79\x5a\x56\xe5\x07\x8e\xce\x96\xf3\xcb\xc5\xf2\xb4\x64\x73\xb4\xfc\xc0\x57\xe8\x3f\x1e\x4f\xc8\xde\x5d\xb4\x79\x47\xd1\x3e\xda\xbc\x8b\xe0\xdf\x10\xfe\x0d\x84\x9b\x31\x83\x54\x1a\x2d\xd1\xcb\xfb\x03\xef\x90\xb7\x89\x1d\x47\xe6\x2d\xc4\x29\x08\x47\x46\xfd\x18\xd9\xf4\xea\x39\x78\xb9\xc6\xa7\x86\x9f\x3a\xc1\x58\x5f\x66\xd3\x81\xfe\xec\xed\xba\x9b\xb2\x06\xfb\xa9\xf8\xe9\xd9\x72\xc5\x56\x97\x9d\x97\xe8\x84\x09\xbc\xd2\x07\x22\xeb\x2e\xa5\xf1\xd5\x19\xb3\xf5\xbf\x32\xf6\x6c\x8c\xee\xde\xde\x8e\xbf\xdd\xce\x8e\xdf\xd9\xd7\xf1\x5d\xbb\x3a\xd7\xff\x94\xc0\xf2\xbc\x3a\x3b\xaf\x9e\xc0\xd4\xba\x53\x17\x41\x90\x9e\xf3\x75\xb9\xe2\xb9\xf6\xd0\x40\x5a\x56\xeb\x3a\x21\xb4\x6c\xdc\x99\x2d\xd4\x8d\x9f\x2d\xe6\xb5\x98\xb4\x1c\xdc\x6c\xc5\xef\x22\x42\x82\x29\x22\x61\x34\x45\x3e\x0d\xa6\x28\xc4\xa4\xdf\x58\xbd\x59\x70\x57\x94\xe9\x45\xfd\x47\x0b\xea\x49\xb3\xf5\xdd\x02\xbd\x77\x3d\x68\x57\x78\xbf\x00\x56\x6a\xe1\x25\xc4\x7a\xee\x5d\x7f\x7b\xf3\xd6\xe2\xed\xb7\x50\x35\xf1\x07\x70\xa4\xca\x2d\xf8\x45\xa3\x76\xb0\x09\x37\x96\x4a\x00\x28\x69\x5e\xeb\x85\x11\x20\xf2\x3c\x74\x07\x89\x81\xb6\x79\x29\x41\xe7\x84\x88\x5e\x7c\xf2\xb9\x76\xf4\x0c\x0b\x73\x06\xa6\x19\x17\xcf\xea\x4e\x3c\x61\x0b\x58\xfb\xe9\x75\xed\x10\x11\xd3\x1a\x5a\xba\x5e\xae\xd2\x71\xfe\xf7\xc0\x7f\x4a\x26\xc1\xa7\xa4\x44\xdd\x4d\x31\xc1\x6b\xeb\xb2\xf9\x53\x02\x6f\xd0\xf7\xab\x0b\x5f\xef\x4a\x66\x61\x7d\x82\x5a\xa0\x77\xe6\x13\x24\x9d\x44\x82\xe4\x2a\x19\x04\x49\x27\x75\x20\xb9\x7a\xce\x40\x45\x30\x1e\xa3\x18\x77\x49\xc6\x57\xa2\x19\x77\x89\xc6\xbb\x50\x6d\x94\x83\x54\xae\x66\x69\xa4\x5c\x54\x4b\xa9\xcd\x66\x49\xcf\x19\x2c\xe6\xd5\xe6\x6c\x60\x85\xa8\x71\x00\xef\xcd\xbe\x3b\x02\xbe\xd8\xea\xcc\x97\x17\x48\xd5\x19\xdf\x8d\x78\x21\x06\xd8\xb5\xc5\x06\x64\xa0\x0c\x76\x20\x3f\xca\xa0\x17\x3e\xdb\x4d\xe0\xd5\x8c\x57\x6c\x58\xb2\xc3\xac\x41\x03\xf6\xb4\x14\x53\x90\xf9\xf9\xe9\x02\x3a\x67\x30\xab\x9a\x83\x75\x98\x3d\x45\x6d\x24\x6d\xac\xbc\xe3\x9c\x44\xc7\xd1\x91\x52\x3b\x43\xb1\x20\x12\x7f\x75\xe8\xd9\x48\xcf\x55\xf7\x89\x56\x77\xbe\xbc\xb0\xc6\xa5\x56\x6e\xbd\x32\xc6\x39\xa6\x9e\xbc\x12\x52\x78\xf5\x66\x63\xa3\xfd\xd5\x46\xea\xda\x11\xf4\xc0\x5e\x09\x94\xed\x08\x48\xdf\xee\xf4\xcd\xd5\xd4\xc0\xe1\x56\xdb\x1e\x05\xd0\xa5\x89\x90\x4b\x00\xd3\x43\xd7\x66\xf9\xab\x0d\x6e\xab\xe3\x6d\xaa\x4b\xfd\x7a\xb5\xc1\x2e\x39\xaa\xba\x4f\x9a\xba\x20\x47\xa7\x7a\xaf\xcf\x57\x60\x51\xf2\x39\x11\xa1\xea\xe3\x5a\xfe\x6a\x13\x28\x5f\x80\x26\x13\x45\x5b\x73\x35\x58\xe1\x57\xf7\x83\x6d\xd3\x1b\x80\xf6\xa4\x81\x26\xbd\x86\x84\xf6\xa4\x07\xed\xe9\x38\xb4\x3f\xd4\xa8\x3a\xae\xd0\xa1\x9f\xa8\xef\x12\x2d\x6a\x8a\x76\x9a\xed\xbd\x98\x2d\xd1\xf3\xd2\xa1\xd9\x02\x65\xfd\xe6\x23\xbe\xa7\x7d\x95\xa1\x5c\xf3\xfd\x93\x55\xbe\xc3\xb9\x06\xac\x4b\x8d\x45\x25\xa9\x41\x63\x0e\xa9\xae\xfd\xa4\xad\x6d\x77\x49\x30\x58\xcc\x96\xcf\x64\x94\x72\xd4\x59\x0f\xd3\xe9\xb2\x76\xf6\xc5\x12\x02\x3d\x87\x8b\x17\x13\xe8\x16\xc5\xe8\xc2\x83\x66\x2b\x93\xba\xd3\xf7\xef\xb7\x44\x82\x6a\xd7\xfd\x83\xa7\x34\x7d\x82\xee\x68\xe5\x36\x45\x47\x5d\xd3\x69\x60\x18\x81\x3f\xdd\x11\x78\x77\xcd\xa3\xed\xee\x56\x2b\x1e\xfd\x2e\x2b\xaa\x34\x30\xb0\xda\x31\x24\x2e\x0a\xae\xdc\xf3\xa7\x23\x38\x9e\xec\x88\xc3\x35\xb6\xad\xd8\x62\x7d\xb6\x5c\x3b\xb5\x04\xdc\xef\xf3\xf2\x89\x34\x8c\x57\x6f\xb4\x05\xc5\x56\x0f\xad\x63\x9e\x6c\xb8\xcd\xc0\xa7\x6a\x8e\x8d\x7e\x56\xff\x71\x56\x22\x56\xc1\x10\x08\xfe\xd2\x1c\x13\xbe\xf2\xa0\x0f\xc6\xa4\xad\xcd\xe4\xc8\x6b\x1c\x80\xb1\xde\x2b\xaf\xee\x8e\xac\x6d\x33\xf9\x57\x5e\xdd\x19\x55\xcf\x32\x6e\x1d\x1e\xa2\x87\x33\x97\xf3\xdb\x7e\x58\xbf\xe2\x90\x31\xee\x1a\x91\xe6\xbe\x6a\x3f\xdc\x8c\x2b\x23\xca\xbd\x9b\x4b\xad\x5b\xbd\x6a\x14\x6e\xfb\x26\x1b\xdc\x34\x9a\x68\x41\xc8\xde\x36\x03\xa0\x04\x40\x7a\x00\xc8\x00\x80\x93\x8b\x22\xf6\x58\x2d\x2f\x1c\x4c\x9c\x6b\xd6\xf0\xaa\x35\x8d\x77\x68\xf2\xbb\x22\x5f\xfe\x70\xb3\x26\x06\xbe\xba\xfc\xc7\x5c\xb3\x9a\x57\xad\x09\xe9\x10\xe1\x87\x16\xe2\x7c\x79\xf1\xe9\x0b\xb4\xdf\x2d\x4d\x33\x92\x81\xbc\xad\x96\xd6\x59\x86\x14\xe3\x5b\x6f\x31\x13\xca\x47\x27\x6d\x1d\x28\x36\x43\xec\xc4\x2b\xdd\x16\xc2\x24\x1d\x9b\x1d\xff\x5c\xc7\xa2\x0c\x8b\x34\xd7\x7e\x2a\x6a\x50\xbf\x59\xf1\x11\xed\x86\xcb\x40\xb7\x61\xf1\x6a\xb8\x0e\x74\xd5\xb3\x54\xf8\x2a\x47\xa9\xe0\x90\x54\xc6\xcb\x79\xf7\xbc\x13\xde\x43\x87\x5d\xfa\xf7\xd0\xed\xfe\x0f\x80\x1c\x36\x68\x9a\xd3\x5c\xff\x24\x87\xa0\x3e\x79\x0d\x4f\x5f\x66\xac\x89\x37\xae\x41\xa2\x43\xa3\xe8\xf5\x2a\xf5\x2a\xe0\x10\xe6\xa1\xf1\x30\xdd\xcb\xff\x3a\xe7\xfc\x17\x3e\x04\x3a\x63\xeb\x59\xad\xdc\x5b\xbd\x45\x3f\xa0\xe2\x53\x16\x0b\xc7\xd7\x84\xb6\x0f\xe9\x6d\xe1\xfc\xee\x6b\x88\x2d\x3e\xfb\xaa\x9c\x16\x1a\xaa\x85\x39\x3d\xe0\xdc\x69\x6d\x4e\x03\xa5\x96\xe7\x74\x50\x57\x5d\x57\x6c\x59\xe1\xee\xc4\x93\x41\x27\x9e\x5c\xb5\x13\x4f\x06\x9d\x78\xb2\x5b\x27\xcc\xa2\x92\xaa\xab\x8c\xac\x5a\xa2\x15\xaf\x56\x25\xff\xc0\x0d\x07\x10\x91\xba\xdc\x2d\xfd\xc1\xd9\xf9\x7a\x56\x93\x61\x62\x91\xa1\xe6\xd3\x61\xcd\x4f\x4f\x4f\x6c\xb8\x3d\xd4\xa0\x9e\x0e\x4d\xd8\x7a\x9f\xe8\x9a\x4e\x4d\xda\xfd\x97\x3a\x42\x69\x70\x67\xcd\x65\xa7\x2d\x3c\xc4\x96\x9b\x39\xf5\xc7\xf6\x7c\xa6\x93\xed\x5f\x8e\x6b\x5e\xf1\xb8\xa6\xbf\xeb\x61\x4d\x7f\xec\xa8\xa6\xef\x38\xa8\xe9\x7f\x39\xa6\x79\xdd\xc7\x34\xfd\x2d\x0f\x69\x1a\xc4\xd2\x39\xa2\xe9\x6f\x73\x40\xd3\xb7\x5f\xc3\x6f\x0e\x1e\xde\xa5\xc1\xc7\xb7\x53\x8a\xff\x45\x8e\x6b\xf6\x13\xec\x84\x98\xfc\x61\x67\x38\xeb\x74\x3b\x02\xe7\x9f\x2b\xdd\xce\x95\x4e\x5b\xaa\xe2\xf6\xb4\x67\x5d\x67\xa7\x84\x3c\x21\x26\x9d\x63\x21\x21\x26\xd6\x63\x26\x74\xcb\x84\x3c\xa2\x62\xe7\xa8\x09\x55\x59\x2d\x42\x4c\xae\xed\x0a\xb1\xde\x7d\x6b\x4e\x9e\xc1\x21\x07\x6f\x93\xa5\x69\x9a\xe4\x61\x3e\xd5\x12\xf6\xec\x4d\x4d\x35\x23\x92\x30\x92\x10\xa6\xa7\xf3\xd9\x33\xe4\xed\x31\x34\x4d\x70\x98\x78\x38\x64\x7a\xf6\x1f\x33\x12\x1c\x92\x82\x67\x32\x67\x50\x9d\x1b\x68\x4b\x24\x51\xec\xfb\x24\x8a\x64\x5a\x21\x95\x39\xc8\x8c\x84\xf2\x34\x08\x18\x8d\xf5\xbc\x42\x5b\x22\xc9\x53\x2f\x23\xdc\xcb\xf5\x34\x44\x66\x24\x41\x9c\x86\x01\xc5\xb9\x9e\xa4\xa8\x17\x9a\x5e\x77\x96\x22\xa1\x4f\x57\xcc\x52\x84\xa3\x2f\x69\x8a\xae\x29\x26\xa2\x3b\xa7\x29\x12\x4d\xc6\xe2\x22\xdd\x67\x0c\x23\x23\xfa\x25\x4d\xd1\xf5\xc7\x46\x74\xdb\x34\x45\x46\xe1\x74\xe3\x23\x3a\x9a\xa6\xc8\xa7\xee\x34\x45\x62\x18\xbf\x4b\x89\x29\x5a\x22\xff\x22\xd1\xd2\xbf\xf4\xe5\x96\xeb\xbd\xd8\xf2\x99\xae\xac\x5c\x3d\x88\x92\x45\x4d\x77\x15\xa0\x9f\xea\x13\xbc\x86\xb7\x6e\xba\x87\x7c\x0f\xd8\xd9\xd9\xfc\x72\xa2\x7e\x9c\x22\xb6\x3a\x39\x3f\xe5\x8b\x6a\xdd\x7f\x93\x47\xbf\x3e\xd3\xd2\x03\xa9\x94\x5a\x14\x3d\xf4\xde\x26\x20\x94\x91\x22\x81\xb8\x22\x8f\x09\x65\x9c\x90\xbd\xe9\xb0\x5e\x8c\xfd\x38\x08\x12\x48\x33\x48\x7c\x5e\x44\x61\x96\xeb\xa1\xc1\xa0\x41\x1a\x66\x5e\x91\x66\x05\x3c\x80\x90\x05\xb9\x9f\x92\xc2\x04\x98\x27\x69\x98\xa7\x2c\x84\xd7\xb3\x31\x4d\xf2\x34\xcd\x9c\x80\xfd\x24\x8c\x32\x12\xa6\x10\xce\xf8\x01\x4d\x43\x9f\x9a\x00\x87\x49\x81\x31\x2e\x80\xe2\x34\xf2\xc2\xdc\xc3\x89\x13\x70\x42\xfc\x82\x12\x06\x4f\x6e\xb3\x02\x27\x41\x91\xa4\x26\xc0\x2c\xc5\x59\xc8\x73\xa0\x38\x67\x51\x4e\x31\xa6\x4e\xc0\x39\xf5\x62\xc6\x24\x8f\x99\xef\xf9\x1e\x09\x8c\x3c\xc6\x84\xfa\x61\x2a\xdf\x8c\x08\xc2\xd8\x8b\x8a\x94\x3b\x01\x93\xc0\xc7\x34\x4c\xe1\xed\x88\x80\xf3\x20\x25\x34\x33\xb2\x22\xf4\xb2\x38\xcf\xe0\x01\xf1\x3c\x2c\x8a\x34\xe0\xc4\x09\x38\x26\x29\x0f\xf3\x18\x58\x51\x90\x38\xa5\x49\x64\x14\x1e\xf5\x72\x9e\x62\xf9\x78\x85\x9f\xe2\x28\x89\x52\xec\xe6\x71\x9a\x67\x5e\x24\x33\x54\x92\x30\x8b\x31\xf1\x43\x13\xe0\x0c\x27\x69\x81\x25\x01\x59\x11\x25\x24\x4a\x02\x27\x60\x1e\x24\x69\x94\x64\xc0\xbb\x84\x17\x38\x60\xb9\x91\xc7\xbc\x48\x79\x10\x53\x78\x46\xdc\xa7\x41\x41\x42\xee\x3b\x01\x7b\x45\x86\x93\x3c\x83\x06\x34\xa5\x59\x1e\xa6\x46\x8a\x49\xe0\x65\x0c\x67\x19\x3c\xd2\x1e\xb3\x2c\xc9\xa2\xd0\x2d\xbc\x9c\x27\x24\x8b\xc0\x40\xc2\x84\xa4\x1e\x89\x8d\x80\x03\x16\x07\x34\x60\x30\x47\x88\x38\x8b\x78\x40\xdd\x14\x87\x59\xea\xb1\x24\x07\x4a\xd2\x3c\xc0\x45\x9a\x07\x46\x93\x8e\x8a\x84\xd2\x1c\x00\x53\x1f\xe3\xd0\x4f\xdd\x14\x27\xd4\xe7\x21\x0e\x09\x98\x34\x8f\xa2\xbc\x60\x66\x03\xa1\x3e\xce\xa2\x08\x22\x7c\x92\xa7\x81\x4f\xb0\xe7\xf6\x15\x9e\xe7\x93\x38\xa3\xf2\xcd\xf7\x22\x25\xd8\x37\xaa\x5b\x5a\x84\x49\x5c\x64\x2a\xbf\x29\x2f\x3c\xce\xdd\x5a\x91\x45\xdc\xf3\xd2\x02\x14\xdf\xcf\x19\xa5\x45\x66\xd4\x8a\x3c\x64\x71\x82\x03\x00\x9c\xf8\x1e\x63\x31\x71\xb3\xc2\x8b\x32\x16\xf9\xa1\x7c\xde\xc5\xf3\x7c\x4a\xcc\x06\x82\x03\x92\x90\x44\xce\xbd\x3c\xe6\xf1\x88\xc7\x6e\x56\x90\x38\x8d\x3d\x46\xc1\xb9\x04\x51\x4e\x48\x51\x18\x4d\x9a\x70\x2c\xd8\x04\x2c\x0b\x33\x12\x65\x09\x89\x9c\x80\x83\x9c\x64\x51\x5e\x80\x56\x84\x2c\x0b\x08\xe3\xb9\xd1\x57\xf8\x3e\xf5\x72\x0c\x2c\x4b\xf2\x24\x4c\xfd\xbc\x70\x02\x8e\x42\x8f\xc5\x7e\x18\x48\x03\x61\x45\xe4\xe7\xdc\xac\x6e\x11\xf3\x58\x0a\x7e\xdb\xcf\xe2\x38\x25\xcc\xed\x36\x29\xce\x48\x96\x10\xe9\xdd\x62\x9e\x33\xce\x23\x13\xe0\x84\xc4\x84\x64\x92\x65\x38\xa0\xc4\x0f\xfd\xd4\x09\x98\x91\xb4\xe0\x94\x49\x3f\x9b\x15\xd8\xf3\x23\xa3\x81\x30\x8a\x59\x14\x05\x40\x71\x9a\x05\xc4\xf7\x3c\xb7\x77\xcb\x48\x90\xd2\x34\xf6\xc0\xcf\x7a\x05\x4d\xe2\x04\x1b\xbd\x5b\x1c\x65\x21\x66\xc0\x63\x2f\x0a\x83\x94\xfb\x6e\xad\xc8\x71\x42\x38\xc5\x09\x00\x8e\x78\x11\x12\x6c\x1c\xf3\xf2\x28\x49\xbc\x88\x80\x2c\xc2\x30\x0a\x59\x32\x62\x79\x45\xe0\x71\x3f\x94\xbc\x0b\xe3\x18\x13\x8f\x30\xa3\x1e\x7b\x11\x63\x9e\xec\x99\x4f\xd2\x34\xc7\xa9\x5b\x78\x38\x61\x41\x86\x31\xb8\xcd\x94\xe6\x24\xf7\x32\x23\xc5\x98\xfb\x71\x94\x79\x52\x8f\x71\x80\x59\x1a\xba\xbd\x1b\x89\x03\x1a\xc7\x01\xe8\x71\x5e\x50\xce\xd3\x24\x31\x01\xf6\x83\xd4\x4b\xb3\x14\x7a\xc6\x71\x92\x06\x74\x44\xdd\xfc\x04\x67\x5e\x96\x82\x50\xb2\x30\x4b\x42\x16\xf9\x46\x7f\xcc\x73\xca\x58\x00\x6e\x93\xfb\x01\xa6\x2c\x73\xab\x5b\x98\x26\x59\xc6\x82\x42\x8e\x0c\x91\xcf\xfd\xd8\x08\x38\xa2\x84\x47\x85\x74\x56\x79\x94\x92\x94\x32\x37\x2b\xe2\x80\x16\x94\x70\x30\x90\x30\xe7\x45\x4a\xcc\xbe\x22\xa6\x2c\x8c\x7c\x39\xd2\x04\x3e\x8e\x49\x11\xb9\xb5\x82\x06\x19\x8d\x29\x96\x91\x10\x2e\x3c\x96\xc6\x46\xb7\x49\xb3\x2c\xf6\x88\x14\x1e\x66\x51\xe0\x27\xdc\x1d\xbb\x25\x5e\xca\x8b\xa2\x60\x32\x8a\x8c\x7c\xcc\x89\x51\x2b\x58\x10\x7a\x51\xc6\xc1\xf2\x72\x4e\x49\x9a\x73\x77\xec\x96\xf2\x22\x61\x7e\x21\x47\x06\x92\x45\x71\x82\xcd\x71\x45\x14\xe3\x98\x16\x72\x08\xf3\x63\x12\xfa\xc4\x2d\xbc\x8c\x91\xd8\xe7\x19\xf0\x98\x33\x12\x45\x38\x31\xf2\x38\xc7\x34\x4a\xa9\x1c\x9a\x88\x50\x24\xd2\x5d\x04\x1c\x06\x22\x2c\x67\x71\x9e\x83\x81\x64\x39\xf7\x78\x8a\x8d\x6e\xb3\x08\xe3\x3c\x28\xe2\x42\x0d\xba\x3c\xc7\xb1\x5b\x8f\xbd\xa8\xf0\xa2\x58\xc6\x0b\x31\xc1\x71\x54\xa4\x46\x93\xf6\x58\xe4\xc7\x79\x06\x06\xc2\x48\x46\x13\xca\xdc\x23\x08\xc6\x7e\x91\x50\x2f\x50\x0b\x77\x89\x97\x33\x23\xc5\x38\x8d\xb1\x97\xfa\xd2\x1f\xfb\x38\x0b\x62\xec\xe6\x31\xa1\x79\x1a\xc7\x45\x28\xb5\xc2\x0b\xe2\x9c\x1a\xfd\xb1\x4f\x32\xc6\xd2\x18\xb4\x22\xf0\xb2\x98\x04\x89\xdb\x40\xfc\x2c\xe1\x29\xf7\x80\x15\x38\xcc\x92\x94\xa7\x46\xe1\x05\x3e\xce\xa3\x38\x83\x9e\x25\x19\xf6\xbc\x3c\x70\xeb\x71\x90\x65\x61\x1e\xc8\xc0\x3b\x4b\x7d\x1e\x90\xd4\x38\x34\x89\x70\x85\x24\x09\x38\xab\x22\x8b\xc2\x98\x0b\xf7\xea\xf2\x15\x45\x96\x46\x05\x93\x83\x24\xcb\xa3\x82\x71\x23\xc5\x51\x16\x04\x38\xa1\x00\x38\x60\x41\x1c\x52\x1c\xab\x45\xd4\xb7\x8e\x6b\xab\xed\xbc\xf0\xc7\xab\xde\x50\xb5\x3d\x83\xf6\x63\xe7\x86\xea\x4f\x57\xbb\xa1\x1a\x62\xb2\xdd\xd6\x81\x61\x3b\xe2\xfa\xb3\x8f\x5e\x75\xeb\x20\x62\x5e\xc2\xeb\x05\x77\x3f\xcd\xb2\xc4\xb3\x6c\x1d\xa4\x69\x14\x33\x2e\x87\x5f\x1a\x64\x8c\xc5\xdd\xd0\xc5\x81\xc4\xcf\x22\x5e\xf8\x31\x78\xb2\x82\x27\x41\x41\x85\x27\x33\xd5\x64\x61\x50\x14\xa1\x0f\x56\x10\x16\x38\xf7\xa3\x62\xdb\x55\xfd\x10\x7b\x3c\x24\xd2\xf9\xb0\x9c\x47\x94\xe4\x96\xad\x83\x24\xf5\xc2\x88\x4a\x85\x24\xa9\xcf\xa3\x0c\x17\x5b\x22\xc1\x05\xf5\xf3\x44\xea\x7c\x91\x06\x38\xcd\x23\x4b\x4f\xc2\x94\x7b\x59\x2e\xc3\x20\xec\xc7\x9c\xe0\x38\xd9\x65\xeb\xe0\xba\xef\x91\x6e\x93\x1a\x16\xea\x79\xf6\xcc\xaf\xc7\xd8\x9e\xfa\xf5\x98\xd8\x73\xbf\x1e\xfb\xf6\xe4\xaf\xc7\x81\x3d\xfb\xeb\x71\x68\x4f\xff\x7a\x1c\xd9\xf3\xbf\x1e\xc7\x96\x04\xb0\xb2\x83\x90\x1e\xd6\x78\x0e\x5c\x96\xcf\x65\xf9\xf0\xb2\x87\xe4\x01\x34\x37\x5e\x81\x92\xe5\x73\x59\x6e\x69\x4e\xa0\x39\xb1\x36\x27\x73\x59\x6e\x69\xee\x43\x73\xdf\xda\xdc\x9f\xcb\x72\x4b\xf3\x00\x9a\x07\xd6\xe6\xc1\x5c\x96\x5b\x9a\x87\xd0\x3c\xb4\x36\x0f\xe7\xb2\xdc\xd2\x3c\x82\xe6\x91\xb5\x79\x34\x97\xe5\x96\xe6\x31\x34\x8f\xad\xcd\xe3\xb9\x2c\x37\x1c\xeb\xdb\x32\xe9\xb1\xd4\x0c\x13\x70\x26\x95\xa2\x9f\x71\x0f\x8e\xdc\x4a\x85\x30\xb5\x4a\xa5\x2e\x98\x5a\x65\x52\x0f\x4c\xad\x32\xa9\x02\xa6\x56\xb9\x14\xbf\xa9\x55\x2e\x25\x6f\x6a\xc5\xa5\xd4\x4d\xad\xb8\x14\xb8\xa9\x55\x21\x85\x6d\x6a\x55\x48\x39\x9b\x5a\x9d\x48\x19\x9b\x5a\x9d\x48\xf1\x9a\x5a\xcd\xa4\x68\x4d\xad\x66\x52\xaa\x73\x53\xde\x41\xd7\xd5\xdd\x2d\x9f\x43\xb5\xe6\xd3\xae\xf1\xff\x58\xca\xdc\xc3\xb6\xeb\xe6\x8f\x60\x04\xaf\xb7\xcf\x86\x55\xb6\x48\x14\x2d\xd1\x08\x16\xfc\x58\xd6\xb7\x0d\xf4\xac\xd1\xe8\x36\x22\x6f\xa1\xa6\x39\x97\x6b\x0b\x63\x2e\x61\xa8\xfb\x05\x7d\x18\x70\x6b\xfe\x4a\x19\xa8\x0f\x0f\xd1\x7f\x40\x36\x62\x3b\xf2\x3a\xa5\xf3\x4e\x19\xaa\x37\xb3\x26\xcf\xf1\x66\xec\x2e\x9e\xaa\x36\xd7\x5a\xb8\xef\xe3\xc9\x5a\xb3\x4e\x16\xec\x99\x4c\xfe\xab\x27\xaf\x9e\x43\x8a\xe2\x3a\x1d\x70\xa7\x1e\x1d\xd4\x83\x43\xaf\xef\x50\xb7\x5a\xec\xba\x61\x2a\x6b\xce\x3b\x54\xcc\x87\x54\xcc\x4c\x54\xcc\x87\x54\xcc\x74\x2a\xba\xf5\xe2\x61\x3d\x4b\x26\x63\x5d\xa4\x96\x9c\x39\x1f\xb4\xdc\xdb\xbb\x24\xdf\x6e\x25\x8a\xb7\x93\x28\x6e\x25\x8a\xb7\x92\x28\x9e\x75\x12\x7c\xcf\xea\x2c\xdc\x5a\x62\xee\xb9\xca\xd5\xad\x31\x09\x2b\x0e\x77\xab\xc1\x39\xe6\x44\x13\x69\x0d\x2f\x1a\x15\x29\x9e\x77\xc8\x98\x1b\xc8\x98\x99\xc8\x98\x0f\xc8\x98\x75\xc8\xe8\x02\x8c\x06\xf0\x48\xe4\x94\xe9\x4e\xb9\xc3\x5d\xae\x24\x6e\xc5\x1e\xbb\xc4\xfe\x63\x19\x4b\xcf\x65\x1c\x98\x7b\x35\xe7\xaa\xa6\xe3\x4e\xb8\xac\x89\x23\xcd\x91\x58\x5f\x85\xae\xeb\x4a\x02\xb0\x31\xb2\xe8\xd7\x9d\xd7\x75\x47\x69\x68\x3d\xcd\x5c\x30\xad\x8c\xfb\x23\x57\xb7\x7a\xeb\xca\x66\xb2\xfa\x0c\x72\xb6\x09\x38\x42\x92\xde\x1e\xba\x5f\x5b\x67\xf3\xcb\xff\x8f\x30\xba\x8b\x06\xc7\xa6\x87\x74\x88\x7f\x6b\x09\x8e\x93\x21\xfe\xdd\x6f\xac\xc5\x42\x05\xbe\x2a\x15\xc0\xc5\x2d\x69\x90\xd2\x19\x52\x20\x25\x31\xc0\x6f\x06\xda\x8e\x8a\x3f\x96\x36\xf1\xb6\xa3\xde\x8f\xa5\x89\x38\x7b\x4e\x7c\x95\x14\x7f\x86\x6e\xa2\x62\xa6\xd2\xe2\x8b\x2f\xe6\x7b\x7c\xb2\x8d\xb4\x7d\x3e\x17\x6d\xe6\xaa\x8d\xf8\x72\x32\x77\x24\xd3\x9f\x41\x36\x7d\x01\x3a\x95\x78\xe0\x73\x26\x3f\xa7\xea\xb3\xbd\xf9\x1c\x9a\x0b\x2c\xa9\x44\x09\x9f\x33\xf9\x39\x55\x9f\xdd\x29\xf9\x67\x32\x27\xbf\x72\x38\x72\x5c\x61\x73\x99\x5e\x7a\x4f\x26\x3f\x60\xb3\x3a\x63\xbf\x2a\xec\xe4\xec\x9f\x69\xaf\x48\xb0\x7a\xd4\x71\x66\xe6\x87\xd9\xd4\xa4\x01\xa4\x70\xce\xba\x38\xe7\x1d\x9c\xb3\x2e\xce\xb9\x8e\x73\xb6\x0d\x4e\x2c\xfb\xc9\xd5\xd0\x20\xef\x9b\x70\x39\x28\xd0\x3a\xed\xff\xac\x7e\xb4\x42\x2b\x0c\xda\x42\x81\xd3\xaf\xcb\x64\x1a\x6e\x37\x4e\xd9\x4f\x55\xb9\xc6\x39\xeb\xe2\x9c\x77\x70\xce\xba\x38\xe7\x3a\xce\x59\x8b\xd3\x18\x75\x8e\xbf\x43\x60\xa6\xf5\x7b\xc8\xbe\xf4\xbd\xfd\x32\xd5\xf7\x60\xbc\xdf\x97\xae\x6b\x54\xdf\x83\x33\xf8\xbe\xb4\xb9\xd0\x0f\xf0\x50\x82\xa8\x33\x9b\x37\x24\x9a\x8c\x52\x56\x14\x08\x67\x6d\x5f\xa4\xbb\xa8\xb0\xee\x2e\x66\xdb\xf8\xaa\x16\xad\xf8\x57\x70\xc4\x8d\xb3\x02\x54\xd9\xcc\x84\x30\xbb\x12\xc6\xef\x8d\xae\xa7\x8f\xf1\xfb\xd2\x84\xf1\xfb\xf2\x2a\x18\xcd\xce\xae\x8f\xf1\x47\x23\xc6\x1f\x4d\x18\xcd\xda\xd6\x7f\xbc\xc2\x82\x12\x16\x2f\x6a\xb3\x87\x8a\x56\xea\x60\x1d\xa4\xf6\x4a\xfb\xd2\x3d\x02\x89\x44\x27\xb1\x86\xb5\x1d\x99\x7f\x3f\xcb\x59\xc5\xd1\x85\x7b\xa6\x2f\xfe\x60\xbe\x69\xd4\x6f\x98\x6e\x9e\x98\xc8\x86\x01\xa8\x30\xb5\x81\x89\x6d\x61\x6a\x03\x73\x68\x6e\x6a\x03\x53\x68\x6e\x6a\x03\x53\xf2\x49\x3e\x87\xe7\x3b\xe6\xb6\xf7\x3b\x60\x4e\x3f\xc9\x67\x50\x4b\xb2\x8e\xeb\x9c\xcb\x07\x4c\xb3\xbe\x04\x22\x20\x65\x26\x1a\x61\x49\x21\x33\xd1\x08\xab\x17\xa9\xa9\x0d\x2c\x5e\xa4\xa6\x36\xb0\x4e\xc2\x4c\x6d\x60\x99\x64\xf0\x9a\x81\xf8\x83\x65\x97\x89\x54\xf5\x8a\x58\x99\x01\x0b\x37\x13\xc9\x07\xa1\x59\xfb\xed\x88\x23\xb9\x51\x0d\x83\x9d\x6b\x7d\xac\x44\x5b\x33\x84\xc8\xe0\x18\xf4\x9f\x0d\xa2\x81\xe3\x26\x19\xc5\xe4\x18\xf4\x9e\x49\x62\x8f\x3d\x9d\x5a\x36\x24\xb6\x0f\x47\x5b\x65\x94\x08\x81\x45\xe9\x10\x21\x6e\x11\x02\x7b\x52\x85\xb0\xe3\x09\xd2\x71\x84\xda\xba\xa4\x44\x48\xc0\xc5\x0e\x11\x92\x16\x21\x99\xd5\xe3\xd2\x04\xea\x6b\xee\x75\x1c\xa1\xb6\x92\x29\x11\xfa\x02\x61\x3e\x44\xe8\xb7\x08\x7d\x81\x2b\x57\x08\xfd\x11\x73\xe8\xc3\xd1\xd6\x3e\x25\xc2\x40\x20\xe4\x43\x84\x41\x8b\x30\x10\xb8\xb8\x42\x18\xe8\x08\xf9\x38\x42\x6d\xb5\x54\x22\x0c\x05\xc2\x62\x88\x30\x6c\x11\x86\x02\x57\xa1\x10\x86\x3a\xc2\x62\x1c\xa1\xb6\xbe\x2a\x11\x46\x30\xa9\x18\x22\x8c\x5a\x84\x10\xbd\x9f\x28\x84\x51\x67\x12\x31\x8e\x50\x5b\x91\x95\x08\x63\x81\x70\x36\x44\x18\xb7\x08\x61\xda\xa4\xc6\x64\x51\xdf\x15\x04\x7c\xf2\xdd\x8b\x2f\x8f\xe2\x5c\xdf\xa3\x38\x58\x04\xf7\xea\x65\x33\x01\x0c\xf2\xb0\xf8\xde\x75\x3f\x8b\x63\x46\x83\xff\x29\x1f\xc6\x79\xb8\x5c\x7c\xe0\x2b\x99\xe5\x17\x55\x4b\xe4\x93\x3b\x69\x59\x89\x00\x25\x47\x0c\xce\x67\xa7\xbc\x58\xae\xb8\x3a\x4e\x3d\x90\x9a\x76\xd7\x44\xdb\xbb\xab\x96\xaf\x7d\x72\x1d\x0f\xf1\xfc\x59\x9f\xe0\xd1\xe9\x6c\xf2\x83\xdc\x45\xd8\x23\xc1\xa1\xaf\xf2\x14\x7f\xb9\xdd\x64\xbd\xaa\x14\x62\xb2\xeb\xed\x26\xd1\x64\xe4\x76\x53\xe7\x58\xc3\xe0\x76\x53\x88\xc9\x97\xdb\x4d\xd7\x7d\xbb\x49\x48\x65\xbb\xdb\x4d\x46\xe1\x74\x6e\x37\x49\x01\x39\x6f\x37\xc9\x7b\xb4\x5b\xde\xfe\xf6\xff\xd4\xf7\x99\xf8\x22\xbb\x93\xb2\x35\x8f\x82\x5e\xc1\x69\x1e\xf6\xab\x7e\x38\x7b\x9f\x17\xbd\x1f\xb3\xf2\x6c\xc6\x57\x7f\xc8\x95\x28\x8d\x54\xf8\x2e\x28\x94\x05\x92\x30\xf8\xac\xd3\xf3\xaf\x70\x75\xea\xc7\xad\xde\x04\x82\xc3\x33\x0f\xa1\xeb\x4d\x3d\xed\xb7\xf1\xab\x50\x87\x87\xe8\x39\x5f\x9d\xc2\x28\xfa\x70\xb6\x2c\x33\x8e\x70\xff\xd9\x14\xd1\xfc\xf9\x43\xdc\xbd\xbb\x14\xc6\x53\x14\x24\x53\x14\xe0\x29\xf2\xfd\x29\x22\xe1\x14\xe1\x78\x8a\x92\x29\x42\x58\x3b\x6a\x14\xd2\x29\x0a\xbd\x29\x0a\xc8\x14\xf9\xc1\x14\x91\x68\x8a\x30\x9d\x22\xec\x4d\x11\xd1\xeb\x25\x53\x14\xe2\x29\x0a\xfc\x29\xf2\xc3\x29\x22\xf1\x14\xe1\x64\x8a\xb0\x80\xaf\xd5\x8b\xbc\x29\x0a\xc9\x14\x05\xc1\x14\xf9\xd1\x14\x45\xfe\x14\x85\xe1\x14\x05\xf1\x14\xf9\x89\x56\xd1\xc7\x53\x44\xfc\x29\xc2\xe1\x14\xc5\x53\x84\x22\x32\x45\x61\x30\x45\x01\x3c\x2d\xa0\x57\x14\x94\x90\x29\xc2\xc1\x14\x45\xa2\x22\x9e\xa2\xd0\x9f\xa2\x20\x9c\x22\x3f\xd6\x2a\x92\x64\x8a\x08\x9e\x22\x2c\x50\x4e\x11\x22\x74\x8a\x88\x37\x45\x58\x90\x23\xab\xbd\x75\xf0\x95\x98\xf9\x4a\xba\x7c\x15\x54\x08\x3e\x8a\x7e\x13\xf1\x79\x8a\x50\xa8\x53\xab\x10\x8b\x6e\x09\x6a\x81\x20\x4f\xa7\xd2\x57\x8c\x13\x54\x89\x0a\xd1\x14\xe9\xdd\xc5\x91\xe4\x87\x60\x30\x50\xef\x77\x05\x21\x04\x2a\x18\x2c\xf8\xe7\xc7\x92\xb1\x61\xd8\xe3\x57\xe0\x29\x69\x85\x52\xfa\x81\x8e\x41\x88\x46\xa8\x86\x2f\x44\x1a\x49\xb1\x87\xba\x0c\x85\x08\x84\x3e\x08\xbd\x10\x32\x14\x8c\xad\xa3\x9a\xce\x8b\x50\xe7\xa7\xe7\x73\x06\xcf\xa4\x88\xa0\x72\x3d\x2b\x8b\xc1\x0b\x4f\x60\x05\xdf\xbd\xfa\xe9\xe5\xf1\x77\x8f\xe5\x9b\x52\x82\x63\x64\x8a\xa0\xf3\x82\x43\x54\x68\xa4\x12\x13\x70\x57\x69\x2a\x56\xe2\x24\x4a\x7b\x81\x21\x54\xc7\xff\xf2\x9b\x67\xaf\xf9\x1a\xb1\x45\xae\x72\xa3\x9f\x81\x48\xe5\x7b\x1a\x06\x3a\x44\xfd\x9f\x9e\x77\xe5\xd9\x0b\x29\xbd\x8d\x77\x17\x26\x23\x94\x78\xde\xb4\x5f\x56\xcf\x15\x64\x15\x43\x05\xd2\xa9\x40\x3d\x8f\x0c\xaa\xf8\x5a\x95\x61\x69\xa0\x97\x1a\x10\x84\x5d\x04\xc4\x80\x20\xea\x12\x69\xaa\x12\xf7\xfa\x61\x40\x44\x3b\x84\x0c\x41\x24\x7d\x2c\x43\x10\x4c\xaf\x62\xaa\x90\xf6\xb9\x35\xac\x92\xf5\xd0\x0c\x2a\xe4\xfd\xae\x0c\xab\x70\xad\xca\x10\x43\xd1\xa5\x72\xd8\x9c\xba\x5a\x63\x3a\x2a\x0f\x42\x47\x10\xf8\x74\x44\xab\x82\x3e\x12\x83\x5e\x50\xb7\xde\x44\x74\x54\x31\x63\xea\x52\x4c\x4a\x47\xe5\x9d\xd0\x11\x79\xb3\x3e\x11\x06\x95\xe8\xa3\x19\x52\x92\xd1\x51\x89\xe7\x74\x44\x6b\x38\x75\x6b\x77\xd1\xc7\x61\x90\xbc\x55\x5c\xca\x4b\x60\x33\x23\x89\x56\x6a\x11\xa6\xdf\xa9\x62\xc4\x1e\x74\xa1\x98\xfa\x18\xea\x55\x8c\x3a\xa1\xd3\x69\x28\x8f\xbb\x64\x38\x6c\x03\x3b\xd4\x3f\xe9\x53\x6a\x75\x14\xd8\x21\xd1\xb4\xdb\x19\x83\x56\x74\x3a\x63\xf5\x13\xd8\xa1\xbf\xbc\x57\xc5\xe6\x2a\xb0\xd9\x15\xd0\x51\x56\x60\x3a\xca\x0a\x42\x47\x45\xef\x53\xb7\xd8\x82\x1e\x08\x9b\xaf\x70\xb1\x3b\xa2\x2e\x15\x8e\xe9\x88\x30\x28\x1d\xe1\x64\x42\x47\x55\x8b\x51\xb7\x40\xd3\x3e\xbf\x0d\x83\x47\x1f\xcb\xb0\x4a\x4e\x5d\x22\xe5\x74\xc4\x84\x8a\xbe\x44\xf5\x37\xaa\xa6\x63\x51\x46\xe0\x79\x34\xf0\xb0\xd5\x83\xa8\x3a\xd6\x30\xa3\x11\xa0\xcd\x83\xd4\x48\x3c\x13\x92\xa0\x8b\xc4\x58\x27\xec\xc2\x31\x12\x13\x75\xe1\x18\xeb\xc4\x6d\x1d\x03\x16\xdd\xd9\x1a\x9b\x27\x7d\x14\x06\x20\xac\xdf\x1d\x7b\xc0\xa1\x10\x19\x80\x64\x1d\xc6\x1a\x2a\xe4\x6d\x05\xab\x03\x91\x24\x18\x1a\x17\x7d\xa9\x58\xe3\x2e\x27\x33\x31\x1d\xe9\x05\xa1\x2e\x6e\xfb\x7d\x14\x26\xdd\xa0\x3d\xb9\x9b\x74\x83\x8e\x33\x3c\xa2\x23\x8a\x1a\xd3\x71\x45\xa5\x74\x44\x28\x09\x75\x08\x85\x51\xb7\x2d\xa5\x7d\x0a\xec\x8e\xc4\x69\x2a\x39\x1d\x51\x62\xde\xe7\xa9\xdd\x9f\x58\x35\x48\x9f\x80\x18\x4a\xf1\x16\x66\x8f\xc9\x16\xc6\x84\xfd\x2d\x0c\x1f\x07\x5b\xe8\x33\x0e\x9d\xa6\x8f\xa3\x31\x93\xc4\xf1\x88\x33\xd4\x43\x70\x33\x84\x64\xcc\x5d\x62\x36\x66\xf7\x38\xdd\xc2\x5b\xe2\x6c\xcc\x91\xe1\x7c\x0b\x67\x89\xf9\x16\xae\x0c\x17\x7d\x09\x19\xd5\x65\xcc\x55\x60\x3c\x66\xa1\x98\x6c\x61\x20\xd8\x1f\xb1\x32\x1c\x6c\xe3\xd8\xc2\x2d\xdc\x0e\x8e\x9c\xde\x0d\xc7\x5b\xb8\x25\x4c\xb7\xb0\x45\x9c\x6c\x61\xf5\x98\x6d\xe1\x4d\x71\x3a\xe6\xc1\x70\xe6\x72\x61\x38\x1f\x73\x0b\x7c\x0b\x37\x8a\x8b\x9e\x87\xda\x25\x54\xc1\x5e\x60\x71\x46\x66\x92\x49\x87\x2b\xd8\x1a\xa2\x48\xd8\x26\xe8\x81\x56\xee\x19\xca\xc3\x9e\x70\x86\x35\xa2\x0e\xd3\x4c\x38\xe2\x4e\x8d\xf1\xe1\xd8\x1e\x9b\xb4\x58\x6c\x91\x49\xdd\x53\x5b\x54\xd2\x52\x31\xa4\x33\xeb\x71\x73\x58\x23\xef\x70\xcb\x16\x9a\x00\x04\x4b\x58\xa2\xda\x9a\x39\xe0\xea\x1e\xa6\x63\xe4\x13\x6a\x57\x14\x9f\x8e\x29\x4a\x40\xc7\x04\x1d\x52\x77\xe7\x23\xea\x56\xa5\x58\x2b\x1f\x96\x52\x6a\x67\x5d\x42\x5d\xac\x63\x74\x4c\xbd\x52\xea\x36\x82\x8c\xba\x55\x27\xa7\x63\x8a\xc1\xe9\x98\x11\x14\x74\x4c\xc5\x3b\x61\x85\x45\x09\xf0\x88\xb9\x62\x32\xa2\xa1\xd8\x1f\x75\x19\x38\x70\x6a\x2a\x0e\x47\x0d\x1e\x47\xa3\x5e\x03\xc7\x2e\x4f\x4c\x47\x2d\x11\x27\xa3\x2e\x03\x33\x87\x35\xe2\x74\xc4\x5d\xe0\x6c\xd4\x6b\x61\xdd\x1d\x18\x50\xf0\x11\xdf\x8b\x8b\x51\x97\xa4\x42\x0b\x67\x37\xb1\xd3\xae\x30\x19\x77\x2d\xbe\xc3\x73\xe0\x60\xc4\xac\x71\x38\xea\x5b\x70\xe4\x34\x60\x1c\x8f\xfa\x36\x4c\x47\x9c\x0f\x4e\x46\x2d\x10\xb3\x11\x37\x80\xd3\x51\x1f\x88\xb3\x51\x57\x80\xf3\x51\x7f\x84\xb9\xc3\xd9\xe1\xa2\xeb\x8d\x76\x89\x1f\xa8\x27\x51\x9a\x7d\x4b\x1d\x7d\x62\x2f\xb0\x84\x12\x35\xd1\x86\x72\xbf\x85\x10\x98\x15\x31\xb0\x2b\x51\xd8\xe5\x88\x39\x86\x68\x82\x63\x13\xfa\xd8\xeb\x84\x7f\xf6\xf1\xb3\xde\x51\x31\x47\x10\xad\x6c\xcd\xf1\x83\x2c\x37\xc7\x0e\x2d\xfb\x6c\x3b\x28\x2d\x7b\x0c\x30\x72\xcd\x4a\x2d\x91\x43\xad\xde\xe6\xd8\xa1\x15\xb0\xa5\xff\x4e\xf9\x62\x6a\xef\x1e\xa1\x63\xc4\xfb\x74\x8c\x01\x01\x75\x8b\x38\xa4\x63\x5d\x88\xa8\x55\x7f\x62\x3a\xa6\x7c\x94\xba\xf8\x97\x74\x91\xdb\x82\x08\x87\x76\xa4\xd4\x25\xbd\x8c\x8e\x69\x5f\x4e\xdd\xfa\xcb\xa9\xdb\xfc\x0a\x3a\x66\x21\xd8\x1b\x31\x11\x8c\x47\xac\x10\x93\x51\x33\xc4\xbe\x6b\xa4\x70\x6a\x38\x0e\x47\x4d\x04\x47\xde\x98\x9c\x70\x3c\xea\xc9\x30\x1d\xb5\x16\x9c\x8c\xba\x0b\xcc\x46\x1d\x1e\x4e\x47\x7c\x26\xce\x46\xfd\x06\xce\x47\xdc\x12\xe6\x0e\xbf\x84\x0b\xa7\xdb\x90\xd1\x83\xbb\x0f\x78\xd4\x2e\x31\xb1\x1b\x26\xf6\x47\xcc\x1e\x07\x23\x8a\x8f\xc3\x51\xdb\xc1\xd1\xb8\x77\x8b\x1d\xee\x0d\xd3\x71\xe3\x49\x9c\xfe\x03\xb3\x51\xff\x87\xd3\x51\x27\x8a\x33\xa7\x13\xc1\xf9\xa8\x97\xc2\x7c\xc4\x4d\xe1\xa2\xeb\x47\x76\x0b\x1e\x8c\x3e\xa5\xa6\xd7\xb6\x43\xd2\x50\x63\x0c\x19\xee\x6a\xc7\x35\x8c\x11\x83\xaa\x00\xeb\x29\xc6\xb8\xa1\x89\xf9\x0c\xe5\x51\x0d\xc0\x56\x21\x6e\x09\x34\x94\xea\x32\xb7\x85\x0c\x2d\x7d\x96\x98\xa1\xed\xa1\x01\x43\xda\x12\x68\x26\x21\xeb\x54\x30\x0d\x1c\x56\xdb\xe3\xba\x70\x0c\xa0\x8b\x0e\x73\xcc\x6b\x0e\xae\xf6\x98\x8e\x30\x97\x50\xcf\xa6\x38\x3e\x75\x2b\x4e\x40\x5d\x8a\x13\xd2\x11\xbd\x88\xe8\x08\xd7\x62\x3a\xa2\x7a\x94\x8e\x88\x36\xa1\x36\xbe\x33\x3a\x22\xd3\x94\xba\xb5\x36\xa3\x23\x5a\x93\xd3\x11\xc9\x71\xea\x56\xdc\x82\xba\xd4\x1e\x7b\x4e\xb3\xc5\xd8\xb3\xca\x15\x93\x31\x9b\xc6\xfe\x98\x4d\xe2\x60\xc4\xaa\x71\x38\x66\x14\x38\x1a\xf3\x1c\x38\x1e\xb1\xed\x66\xdc\xb3\x8a\x11\x27\x63\x06\x84\xd9\x88\x7f\xc4\xe9\x98\x07\xc1\x99\xd3\x43\xe1\x7c\xcc\xc3\x60\x6e\x1f\x9c\x8b\x11\x0f\x01\xf1\x81\x5b\x56\x78\x44\xd3\x30\x19\xb1\x74\xec\x8f\x19\x33\x0e\xc6\x8c\x15\x87\x63\xae\x2a\xb2\xbb\x22\x1c\x8f\x39\x0b\x4c\xdd\xe6\x92\x8c\x19\x3c\x66\x56\x67\x81\xd3\x31\x5b\xc6\xd9\x88\xbb\xc0\xb9\xd3\x59\x62\x3e\xe6\xca\x70\xd1\x73\x38\xbb\x44\x05\x8a\x6c\x6a\xf2\x22\x35\x4c\x53\x5c\x20\xdb\x12\x73\x9f\xfd\xb6\x9c\x98\x60\x07\x2d\x47\x8c\xf0\x43\xbd\x3f\xa6\xa8\xa0\x29\x1d\xc2\x8e\x3b\x0a\x6d\x1d\x15\x8d\xd1\x80\x46\xd4\x10\x30\xab\xd1\x1a\x49\x4e\x95\x82\x9a\x22\x00\x8d\x57\xc3\xf2\x5c\x03\x3b\x2c\xe5\x4d\x5f\x87\x65\x45\x87\xcb\xa6\x9e\x3a\x85\x84\xa9\x5b\x48\x84\x5a\x7a\xe4\x53\x97\x74\x02\xea\xea\x4f\x48\xdd\x5a\x17\x51\xb7\x66\xc4\xd4\xce\x0f\x4a\x5d\x7a\x91\x50\xbb\x3e\x33\xea\x16\x7d\x4a\xdd\x32\xcc\xa8\x45\xa7\x72\xea\x16\x11\xa7\x2e\x9d\x2a\xa8\x5b\x95\xb1\x37\x62\x47\x18\x8f\x28\x1f\x26\x23\x96\x8a\x7d\x87\x02\xe2\xc0\x69\xa7\x38\x1c\x31\x45\x1c\x79\x23\x3e\x28\x76\xda\x5c\x13\xc1\x5a\x68\x4f\xac\x5e\x9b\xd9\xac\x15\xa7\x23\xae\x0d\x67\x0e\xbf\x88\xf3\x11\x1f\x82\xf9\x88\xcd\xe2\xc2\xe9\xdc\xc4\x88\x6e\x21\x1c\x3b\x55\x09\x13\xa7\xd1\x62\x7f\xc4\x2e\x71\x30\x62\x98\x38\x74\x58\x26\x8e\x46\x7c\x0d\x8e\x47\x9d\xd5\x88\x25\xe1\x64\xc4\x46\x31\x73\x38\x00\x9c\x3a\xbd\x16\xce\x9c\xae\x05\xe7\x36\xfb\xc7\x7c\xcc\x84\x8b\xae\xeb\xd9\x7d\xe8\x36\xe8\x48\x4d\x6a\xe0\x61\xc3\xd0\xad\x42\x0d\xc3\xa0\xad\x80\x9a\x9a\x05\x4d\x90\x63\x2a\x0d\x2d\xdd\x8f\x24\x48\xc3\x18\xdd\x86\x4c\xc3\x52\xaa\x75\xc0\x34\x4c\x37\x7d\x1f\x36\x65\x9a\x92\x0f\x4b\x53\xad\x13\xa6\xa9\xba\x16\xc7\x19\x86\x69\xc9\xb7\x21\x54\xde\xf2\xcd\x34\x49\xd7\x22\xdf\x61\x4f\x5d\x6c\xc0\xd4\xcc\x54\x42\x5d\xf2\xf5\xa9\xab\x8f\x01\x75\x28\x4e\x48\x5d\xcc\x8b\xa8\xab\x27\x31\xb5\xb1\x87\x52\x87\x5a\x25\xd4\x25\x6a\x46\x5d\x12\x49\xa9\x43\x11\x32\x6a\x53\xf3\x9c\xba\x34\x99\x53\xb3\xc6\x16\xd4\x21\x64\xec\x39\xa5\x8c\xb1\xd3\x5c\x89\xd3\x5e\xb1\xef\xb4\x15\x1c\xb8\xcc\x01\x87\x4e\x53\xc2\x91\xd3\x20\x70\xec\xf2\x08\x6a\xbc\x31\x16\x25\x4e\x6f\x81\x99\xcb\x62\x70\x6a\x71\x1a\x38\xb3\x39\xd9\xdc\x69\xb9\x98\x3b\x9d\x02\x2e\xac\x1e\x11\x7b\x4e\xa9\x63\xa7\x21\x62\xe2\xb6\x6e\xdf\xa2\x69\x38\x70\x1a\x1a\x0e\x5d\x26\x8c\x23\xab\x1d\xe2\xd8\xe9\x19\x30\x75\x5a\x3f\x4e\x9c\xb6\x88\x99\xc5\x59\xe1\xd4\x69\x6e\x38\x73\x79\x07\x9c\x5b\xad\x18\x73\xa7\xe7\xc0\x85\xe6\x1c\x76\x19\x53\xa9\x18\xe0\x89\x01\x60\xc3\x9c\xa1\x3f\xbe\xdb\x6e\x6e\x0c\xdd\xb1\x6c\x37\x74\xc4\x0a\x9e\xa1\x28\x94\xf0\x88\x91\x8e\xa8\x29\x34\x39\x61\x45\x89\x79\x9c\xa1\x9e\x99\xfe\xa4\xe9\xb7\xc9\x05\x4b\x3a\x4d\x45\x69\x03\xd4\x40\x67\x76\x57\x5e\xf6\x18\xba\x5f\xb3\x9e\xf0\x86\x89\x86\x36\x85\x22\xc2\x50\x54\x6f\x2a\x59\x7b\x2e\x8b\xb1\x8b\xa7\xaa\x0e\x71\xc9\x5f\xd5\xf1\x5d\xb2\x56\xbf\x07\x2e\x66\xab\x3a\xa1\x9d\xad\xaa\x46\x34\xda\xe7\xd8\xa2\x5a\xaa\x98\xba\x38\xaa\xea\x24\x36\x29\xa9\x72\x66\xd7\x52\x55\x23\x75\xe9\xa3\xaa\x93\x99\x45\xae\x4a\x73\x97\x1a\xa9\x3a\xdc\xa5\xa2\xaa\x4e\x61\xb7\xd0\x3a\x22\x36\x1a\x36\x76\xf5\x00\x13\x0b\x93\xb1\x6f\xd3\x38\x1c\xb8\x88\xc5\xa1\x4b\x2c\x38\x72\x31\x03\xc7\x8e\x2e\xda\xfc\x6f\x62\x17\x21\x66\x2e\x4d\xc5\xa9\xd3\x1f\x66\x2e\x8b\xc2\xb9\x5d\xbf\x31\xb7\x29\x1d\x2e\xc6\xad\xab\x9d\xdc\x58\x6b\x60\xb7\x2f\xc0\x64\x5c\xe1\xb0\x3f\x66\x7d\x38\x70\x5a\x1f\x0e\xc7\x9d\x40\x2d\x6c\x67\x77\xe3\x71\xa7\x84\xe9\xb8\x73\xc3\xc9\xb8\x37\xa8\xd5\xc1\x65\x65\x52\x29\xac\xa5\xd9\x98\x5b\x93\x8a\xe1\xa0\x93\x8f\x79\x9c\x5a\x49\x00\x8b\x36\xb2\xcb\x8f\x7a\x5e\x83\xa7\x6c\xfd\x7e\x8d\xaa\x19\xab\xd0\x9a\xcf\x79\x56\x41\x3e\xa2\x97\xdf\x3c\x7b\x8d\xca\xc5\x59\xfd\x4c\x44\x93\xd1\xe0\xe9\x83\x97\xbd\x87\x8b\xdb\x8b\x89\x53\xd4\x1e\xfc\x87\x07\x14\xd5\x17\xf8\xac\xbe\x4c\xf5\x86\x9e\xfa\x55\x56\x90\x5f\xea\xcf\xe2\xcb\x54\xeb\x4f\x9f\x72\x2d\xab\xd2\xb7\x8f\x5e\xca\xc4\x58\x48\x26\x7e\x71\xbf\x51\x25\x6a\x37\x0f\x54\xc9\x2f\x5a\x96\x94\xab\x3e\x51\xe5\x4e\xad\xf7\x9e\x5f\x36\x29\xc0\xde\xf3\x4b\x43\xea\xbb\xf7\xfc\xb2\xce\xab\xf7\x9e\x5f\x9a\xd3\xea\x09\x1c\x52\x44\x61\x84\xd2\xb2\x5a\x23\x96\x65\xcb\x55\x5e\x2e\x4e\x50\xb5\x44\xcf\x1f\x62\x23\xdc\x6f\x4a\x48\x05\xf4\xa6\x9f\x03\xd9\xf4\x76\x48\x18\xd9\xdf\x0e\x69\xc1\x3d\x5f\x0a\x80\xcf\x1f\xe2\x37\xe5\x5b\x74\x07\x61\x43\x8e\x52\x85\x57\xa6\xe7\x9f\xd4\xbd\x7b\xd3\xb6\x57\xe9\xf8\xc4\x7f\x26\x3e\x46\x77\x34\xd0\x90\x87\x6f\x0f\xdd\x1c\x00\x36\x24\x2c\x7d\xb0\x5e\xf3\xd3\x74\xce\x11\x8e\xd0\xfa\x3c\x7d\xcf\x2f\x0d\xec\x5f\x9f\xa7\xdf\xf3\xcb\x75\x23\x82\xf6\xbb\x9d\x29\x8b\x97\x50\x49\xb2\xa6\xfe\x72\x1f\xe1\xa8\xf9\x66\x7f\x62\xe5\x21\x64\x9c\x52\xf4\x98\x19\xb9\xae\xa1\x2b\x5a\xde\x28\xa0\x6f\x15\x51\x46\xb8\xee\xa7\x5b\xd2\xb2\x7a\x09\x59\x51\x8e\xb4\x24\x28\x0d\x5c\x1b\x48\xa9\x50\x01\x35\x2a\x14\x19\xb6\x31\x69\x0d\x09\xec\x5a\xd3\xc5\x53\xac\x96\xa7\xe0\x60\xe6\xbc\xa8\x10\xa1\x60\x19\x02\xb3\xb9\xa1\x64\xce\x9b\x49\x89\x0e\xe5\xdb\x10\x1e\x24\x70\xac\x95\x6b\x32\x79\xfe\x90\x28\x1d\xdc\x43\xfb\x0d\x07\xf6\xd0\xdf\x10\xa1\x6f\x21\xc7\x23\xe8\x56\x89\xfe\x06\x6f\x5c\x6c\x4d\xde\xaa\x3c\x99\x6d\x4f\x5f\x00\xe9\x3b\x5b\x22\xf7\x3a\x54\x12\x0a\xc5\x92\x56\xb4\x8f\x48\x60\x21\x78\xcf\x40\xf1\x00\xad\x29\xb3\xbf\xe8\x40\xb9\xc8\x38\xe2\x2c\x9b\x29\xb5\x43\xe5\x1a\xb1\xb3\xb3\x79\xc9\x73\x21\x4b\xb6\x40\x7c\x73\xc6\x16\x39\xcf\xeb\xbc\x8c\xe0\xde\xa7\x46\x68\x82\x05\x0a\x4c\xc6\x16\x28\xe5\x28\x5d\x2d\xdf\xf3\x05\x2a\x17\xd5\x12\x51\x99\x14\x78\x8d\xd6\x19\x9b\x4b\xf0\x12\xe4\xda\x0c\xed\x62\x56\x66\x33\xc4\xe6\xf3\xe5\xc5\x1a\x40\x0b\xb8\xd5\x52\x80\x3d\x5f\xf3\x1c\x5d\x94\xd5\x6c\x79\x5e\x49\x02\xd7\xe5\x72\x31\x84\xa2\x18\x0d\xe9\x35\x27\xed\x97\xfb\xf7\xd5\xb3\x32\xed\x4f\xc2\xa1\xf8\xd8\xc4\xb9\x8e\xe6\x62\xa9\xb9\xb1\x5b\x71\x15\x58\x70\x62\xed\x67\xf0\x59\x93\x52\x0a\xf1\x36\x12\xd2\xf7\xcd\xa2\xb2\xf5\x23\xd6\xfb\x11\xbf\x55\x89\x3d\x7f\xd3\x7f\x82\x47\x01\x06\x4f\xed\x18\x3c\xe0\x43\x99\xf8\x12\x95\x8b\x0f\x7c\xb5\xe6\x76\x2f\x58\x2e\x3e\xbc\xec\x39\xc2\xce\x4f\x5b\x0d\x10\xd8\x31\x40\xb4\xd0\x74\x8e\xad\xdf\xe0\x50\x28\x74\x1f\xfa\xc7\xce\x82\x43\xfb\x85\x2f\xb2\xd5\xe5\x59\xb5\xc3\x53\x80\x2a\x63\xed\xf2\x61\xd3\xae\xad\x3c\xed\xba\x7c\x6b\x0a\xdd\x9c\x7f\x0e\xac\x2d\x47\x5c\xb9\x7b\x1f\xba\x31\x4f\x6b\x46\x9a\x82\x8e\xff\xe0\x95\x1e\xa7\x75\x89\x9b\x03\x50\xed\x69\xac\xbe\x0c\x64\xb5\x55\xbf\x1a\xbc\x9c\x65\x88\x3e\xbe\x5b\x94\x55\xc9\xe6\x7a\xea\xab\x6e\x1d\xbe\xc9\x66\x6c\x71\xc2\x9f\xbc\x68\xd3\xa2\xca\xcc\x63\xde\xc6\x2b\xe4\xff\xfa\x2a\x6d\x6e\x23\xdf\xa7\x86\x19\x6b\x51\x58\xdb\xbc\x78\xa2\xb7\x21\x80\xc7\x57\x7f\xdb\xb5\xa1\x92\x36\xaf\x28\xc4\xff\xb7\xa4\x0d\xda\x84\xea\xcf\x98\x99\xd6\xf5\x54\x9b\x4c\x1f\x06\x16\x25\x3f\x4a\xab\x82\xcf\xe3\xcf\xb6\x19\x46\x22\x63\x3c\x01\xe0\x6c\xcf\x5e\x34\x8a\xa1\xeb\x89\xa5\xee\xaa\x5b\x77\xa5\xea\x1a\x89\x7c\xcc\xcb\x75\xc5\xe7\x8d\x16\x9b\x21\x16\xd0\xf9\xed\x42\x0b\xea\x76\xd0\x85\x18\x68\x65\xaa\xb5\x37\xe5\xdb\x37\x93\x89\xa2\xf6\x5d\xeb\xae\x45\x20\xd9\x4c\x5d\xe0\x3b\xa4\xd5\x36\xb1\xc6\xe0\xb0\x7b\x86\xb4\xb2\x71\xaa\x67\x49\xf3\x9a\x8c\x62\xdc\x81\xff\x7d\x91\x2f\xd1\xfa\x82\x9d\xc9\xf0\x63\xce\xd6\x95\x54\x86\xa1\x0b\xaf\xdc\x22\xeb\x11\xdb\x15\x98\xcb\xf0\x2b\x83\x0e\x43\x46\xf1\x5d\x4d\x7d\x60\x1a\xd7\x66\x82\x57\x31\xf5\xab\xb8\x94\x11\xd7\x65\x98\x91\x55\x68\x79\x5e\x0d\x3c\x70\xe3\x72\xdd\x22\xeb\xb8\x5c\xbb\xcc\x3a\x43\xc6\x7b\x7e\x29\x53\x40\x47\xc1\xa1\x4f\xf4\x92\xf2\x83\xa5\x40\xcb\x1b\x1d\x19\xb3\x46\x1f\xa2\x97\x42\x03\xd5\x24\x60\xb5\x5c\xaf\xdb\x30\x1d\x72\x1e\x42\x40\x0c\xd3\x52\xd9\xa2\x19\xa8\x5a\xc6\x4d\xea\xf1\xea\x94\xad\xdf\x77\x4c\xb6\xd6\xdd\xc9\xa4\xa3\xa2\xc2\x10\xeb\xd1\xf5\x5d\xa7\xeb\xc2\x68\x05\x14\x8d\x05\x1d\x95\x7d\x07\x3a\xfb\x95\x51\xf1\x45\x99\x88\xa8\x24\x64\x55\xab\xb6\xbb\x01\xd9\x2f\x9e\x6c\x4f\xf6\xca\x4e\xf6\xdc\x4d\xf6\xdc\x41\xf6\x6a\x0b\xb2\x9d\x49\xa4\xd7\x75\x16\x69\xb9\xfc\xb1\x5d\x1e\xe9\xb1\x24\xcc\x12\x56\xc5\x37\x95\x9e\x8a\xf9\xdb\x47\x2f\x0f\x54\x80\xd6\xc9\xc5\x3c\x45\x59\x71\x62\x48\xae\x7d\x36\x67\x82\x88\x4d\x85\xfa\x50\x54\xc0\x35\x69\xf1\x98\x00\x35\x99\x9d\x87\x0b\x35\xdd\xa4\xdb\xdf\x3e\x7a\x69\xcc\xb8\xfd\x6a\x55\x9e\xcd\xf9\x9d\xdd\x96\x88\x64\xa3\xce\x42\x91\xfe\xd3\x9f\x67\xb9\x48\x2d\x44\x08\xb2\x4b\xc8\x50\x9a\xf5\x9f\x07\x52\x51\x2c\x5f\x63\x74\x24\xea\x1d\x48\xae\x3e\x92\x32\x5e\xae\x26\xed\x3b\xeb\xea\xe1\xf8\x1a\xf5\xc1\x7a\x5e\x66\x7c\xe2\x4d\x11\xd9\x1b\xbc\x85\xd1\x80\x25\x57\x04\x4b\xa6\x28\x70\x80\xf5\xaf\x08\x36\x98\xa2\x68\xcf\xfe\x90\xc6\x95\xe7\x1e\x7c\x8d\x0f\xf4\xc6\x5a\x0b\x2b\x67\x0e\xf4\x39\xc7\x16\x0d\xfc\x2d\x30\x5c\xcf\x9c\x46\xe0\xda\x91\x38\xb2\x6b\xf7\xf1\x16\x18\xcc\xa3\x1e\x4e\xc8\xb5\x0d\x7b\xff\x24\x6e\xb5\xf1\x2e\xd7\xe0\x5c\x5b\x58\x3b\xba\x58\x9b\x8b\xeb\x3a\xda\xa6\x96\x33\x7f\x7e\x53\xab\x97\x42\x5f\x4b\xcc\x7e\x37\x24\xd3\x5e\x56\x7d\x2d\xb9\xfb\xdd\x30\x98\xb6\x59\xdd\xef\x86\xd1\x54\x25\x7b\xbf\x1b\xe1\x8f\x6f\xa7\x34\xf8\xa4\x84\xfb\x7f\x64\xa6\xfd\xcf\x96\x0f\xff\xbf\x27\xb3\x3d\xbc\x54\x50\x2e\x78\x7e\xbd\x29\xee\xbf\x61\x6b\xde\x66\xad\x67\x6b\xae\x95\xbd\xf6\x89\x33\x03\xfe\xd0\x96\x37\x51\x80\x16\xec\x94\xaf\xcf\x74\x2b\x3d\xd4\xc9\x10\x55\x04\x19\xf2\xbf\xbf\x7e\x34\x81\x79\x80\xa2\xa0\x79\xc2\xc6\x04\xe6\x75\x14\x08\x3a\x80\xa8\x4d\x14\x1c\xa8\x2f\x82\x7e\x43\x64\xd0\x82\x96\xe0\xd5\x72\x4a\xf9\x0b\x5f\x23\x86\x16\xfc\x62\x7e\x89\xa4\xad\xe5\x26\xc4\xba\x43\x41\x9d\xd7\x3c\x16\xe7\xa7\x29\x5f\x7d\x44\xf0\xaa\x14\xbc\xaa\x22\x3e\xf8\x04\xc2\xf9\x03\x67\x93\xf9\xf2\x02\x5a\x88\xff\x9a\x1a\x74\x1b\x77\xbd\xdb\xb0\x42\xcd\x97\x4d\xcb\x97\xda\x23\xd4\xec\xa9\x07\x66\xb9\xfb\xe7\x11\xcf\x87\x59\x59\xe0\x85\x5e\xe4\x75\xd7\x3b\x6b\x4e\x83\x8b\x5f\x94\x9d\x88\x4a\xf4\x70\x2a\xa8\x36\x8f\x61\xea\x7d\x2d\xc3\xab\x9e\x50\x2c\x7a\x7b\x84\xba\xaf\x6f\xeb\x33\xf3\xbe\xa4\xbe\x29\xab\x8b\x72\xcd\xd1\x0f\xcf\x5e\xad\x01\xc2\x98\x60\xea\x87\x52\x94\x82\x7c\x44\x0f\x84\x7c\x05\x5f\xee\x00\x63\xd4\x48\xc2\x8a\x8a\xaf\xd0\x82\x9f\xb0\xaa\x5c\x9c\x5c\x03\xe3\x01\x14\x17\x8c\x57\x22\x38\x58\x2c\xab\x89\x95\xab\x87\x87\x68\xb1\x1c\x8d\x54\xe1\x4d\x16\xc9\xd0\xdf\x1b\xee\xde\x33\x56\x93\x8c\xfd\xbd\x66\xb2\x21\x24\x55\x9c\x51\x8c\xa9\xb5\xa1\x15\xe7\xbd\x0e\x75\x9d\x08\xc0\x26\x95\x07\x3f\x7c\xab\x49\x05\xb6\x13\x60\xdc\x3e\x63\x6b\xd8\x5e\xd8\xca\x86\x1a\x49\x01\x0c\x61\x12\x8d\xb0\xaa\xa5\x40\x51\xc3\xbd\x66\xe1\x3f\xf8\xe1\xdb\xeb\x11\xbd\xdc\xdb\x69\x05\xcf\x16\xf9\x84\x2d\x96\xd5\x8c\xaf\x14\x21\x2e\x35\x60\x8b\x5c\x57\x03\xd1\xc3\x11\x55\x68\xed\xec\xa6\x64\xc8\x98\x56\x34\x96\xa7\xea\xff\x61\xfa\xf1\xec\xc5\xe7\x56\x8f\x67\x2f\x3e\x93\x76\x3c\x7b\x71\x3d\xca\xb1\x5c\x75\x74\x63\xb9\xda\x41\x35\x96\xab\x2b\x6b\xc6\x6f\x3b\x6a\xc6\x6f\x7f\xb0\x66\xbc\xfe\xfc\xaa\xf1\xfa\xb3\xe9\xc6\xeb\xeb\x52\x8e\x4d\x4f\x3b\x36\x3b\xa9\xc7\xe6\x13\xf4\xe3\xdd\x8e\xfa\xf1\xee\x0f\xd2\x0f\xd8\x94\xd7\x35\x63\x21\x57\x46\xd5\x84\x70\xce\x8b\x6a\xfb\xa8\x6c\x01\x3a\x21\xbf\xa1\x65\xd1\x40\x82\x27\x6c\xae\x4b\x19\x00\xd8\xf5\xa8\x03\x80\xea\x28\x04\xfc\xf2\x64\x42\x42\x97\x1e\xc8\x4a\xba\x2a\x2c\x4c\x7a\x20\xa6\x40\x0b\x74\x1f\xf9\xc4\xb6\xd3\xa5\x69\xca\xa4\x55\x95\xfb\xf7\xd1\x02\xb6\xc8\x1b\x65\x90\x47\x87\x08\xba\x83\x16\xc6\xc7\xea\xcd\x2a\x24\xe0\x0c\x75\xed\x23\xaa\x27\x4f\x6e\x82\x74\x30\x93\x05\xba\x63\x78\x31\x74\x80\xba\xbf\xd5\x25\xd0\xfd\x77\x6a\x2f\x2c\xe5\xff\xdb\xa9\xef\x8b\x89\x7d\x72\x51\x6b\xef\x8b\x6b\xd2\x5e\x29\xf7\xae\xa6\x6a\xca\x5b\xeb\xf3\x16\xca\x3b\xf0\x98\x00\xea\x0a\xfa\xab\x59\x41\x03\x67\x5c\x81\x15\xfa\x3f\x5c\x83\x5f\x2c\x2b\x56\xf1\xcf\xed\x80\x57\x80\xe5\xba\x54\x18\xa0\x5d\x8f\x0a\x4b\xc2\x74\x15\x5e\x2d\x47\xfd\xaf\xa8\x32\xaa\xbf\xaa\x47\xa0\x07\xca\xab\x2f\xf6\x44\x38\xd8\xfe\xf2\x62\x12\x05\x03\xb5\xfc\x54\x81\x5d\x93\xcf\xf9\x73\x49\x6c\xc4\xe5\x88\x1a\xbb\x0b\xec\xc5\x40\x60\x4f\xae\x22\xb0\x07\x79\xfe\xb9\x23\x5f\x96\xe7\x9f\x29\xf2\x95\x4f\x7e\x5f\xc7\x9c\x39\xef\xcd\x99\xf3\x9d\xe6\xcc\xf9\xd6\x73\xe6\xfe\x88\xb0\xdf\x04\xb2\x70\x60\xd4\x1c\xfc\x66\x6c\xb5\xba\x14\xcd\xea\x31\x44\x3e\x0c\xdf\x19\x56\xda\xe7\xe1\xcd\x30\x86\x81\xd4\x7e\x1b\x73\xa3\x7d\x89\x43\xd1\xf0\xa9\x1e\x5d\x7e\x33\xef\xae\x3c\x58\xa8\x27\xc0\x97\x85\xbe\xb6\xb9\x36\xbd\x70\xbc\x5a\x9e\xf1\x55\x75\x89\x7e\x55\x4f\x0c\x43\x45\x50\xaf\x06\xc4\x60\x59\x51\x29\xc8\xfa\xc0\x04\xa7\x76\x2b\xcd\x9b\xe8\x5d\xef\xb2\x2e\x4f\x16\x65\x51\x66\x6c\x51\xa1\x14\xca\xcb\x85\x66\x1b\x80\xd4\xb1\xfa\xdb\xae\x4b\xd7\xc4\xd4\xbf\x5c\xc3\x3a\xf0\x90\x02\xbb\x39\x76\xd8\x35\x79\x76\x26\xd4\x92\xcd\xf7\x3a\xbc\x1f\x65\x1c\x32\x3a\xe4\x86\x73\x1a\xd8\xad\x98\xc8\xbb\x62\xfe\x04\x5b\xbd\xd0\x59\xdd\xef\x45\x67\xcf\xb7\x6b\xb3\x9f\x08\xec\xcd\xa0\xbd\xf8\xdb\x75\x59\x7b\xba\x2b\x14\x4c\x71\x82\x19\x4e\xe1\x4e\x4d\x86\x73\xcc\x71\xb1\x37\x00\xf2\xf6\xdf\xa8\xab\x53\x84\xbd\xad\xb7\x07\x40\xe9\xa6\x8d\xda\x0e\xdc\xf2\x85\x3a\x3c\x01\x6e\xb1\xfe\x22\xff\xfb\xdb\x6f\x86\x0b\x18\x22\xee\x6f\x6c\xe0\x2f\x47\x68\xb8\x0b\xa6\xff\xc9\xb1\xb9\xae\x7e\xd4\x90\xd1\x3f\x0b\x68\x0d\xda\xfb\x00\xa4\x0d\xcd\xf9\xe2\xa4\x9a\xa1\xdb\x88\x6e\x79\x94\xba\xef\x68\x1e\x2e\x17\x1f\xf8\xaa\x9e\x1a\x6a\x6e\x58\xf9\x07\x31\x68\xd7\xb7\x03\xb6\x72\x3c\xf5\xa8\xdd\x48\xb7\xb3\x33\xf7\x11\xbd\xea\x3a\xd1\x5b\x6b\x94\xb3\x8a\x21\xb6\xde\x11\xcf\xd6\x2b\x59\xdd\x9d\xc2\x8d\xe6\xa0\x0f\xaa\xe5\x6b\x9f\xd8\xb7\x42\xa0\xf8\x13\xce\xec\x28\x5c\x5d\xa5\x32\x9c\xdc\xa9\xeb\x3d\x91\xc2\x6c\x88\xac\xc5\x6b\x3a\xc5\x23\xc5\x66\x80\x25\xbb\xbb\xf5\xe1\xfd\x2e\x6e\xf7\x4d\xaf\x76\x0b\xaf\x6e\xf5\x66\x70\x84\x5f\xfc\xd5\x34\x1c\x9c\x9d\xaf\x67\x93\x3a\x90\x12\x31\x82\x69\x5e\x69\xae\xdd\x8b\x25\x90\xe1\x9c\x6c\x1d\x8a\x68\x02\xae\x3d\x48\x0d\x73\xda\x35\x1b\xeb\x41\x92\x81\x55\x00\x18\xa1\x92\xd9\xf2\x0c\x06\x49\xcb\xd8\x8f\x46\xc3\xd6\x46\xed\x39\xca\xe6\xcb\x85\x6b\xa6\xb2\xad\x4a\x03\x9c\xbe\x2e\xc3\x8f\x76\x5d\x86\x62\xa7\x2e\xeb\x90\x21\x4a\x91\xe4\x36\x27\x5f\x4d\x27\x5d\x1f\x42\xfd\xbf\x82\x62\xff\x55\x72\x66\x08\xb4\xf6\xa5\x12\xde\xd0\xcd\xd6\xa7\xc6\xec\x08\xe0\x0e\x53\xbd\xb1\x2e\x83\x13\x0b\x9a\xc6\x84\x2e\x3a\xf6\x33\x6a\x06\x17\xdb\xd8\xc0\x85\x52\xf9\x1a\xfc\x9b\xf2\xad\x89\xed\x76\x55\x85\xca\x9d\xfd\xe5\x26\x3c\xb6\x9e\x9b\xe9\x9d\x96\x51\x47\x63\x3e\xbe\x9d\xd2\x70\x9b\xf3\x2e\x87\xb7\xff\x82\x66\x55\x75\xb6\xbe\x7b\x78\x78\x5a\xcd\xd6\x07\x29\x3f\x3c\xaf\x0a\xfa\xf3\x1a\x7d\x20\x07\xf8\x80\xa0\xf4\x12\xfd\x8f\x53\x56\xcd\x4a\xb6\x16\x1a\xd3\x1e\x90\x81\x53\x21\xf2\xb0\xc7\xe1\x21\xfa\x96\x57\xf2\x3a\x1c\xe7\x82\xdd\x25\x4b\xe7\x7c\x8d\xfe\xa1\x30\xfd\xe3\xc6\x57\x70\x8c\x7f\xc5\xf9\xa3\xe6\xfc\xcb\xe0\x24\x0d\xba\x25\x85\x77\x0b\xdd\xbc\x59\xff\x7c\xcf\x0e\x1e\xfd\x43\x76\x47\x03\xfe\x14\x7e\x68\x61\x9f\xaa\xef\x5d\xd0\xea\xd7\x9b\x37\x0d\xe7\x73\x8e\x3a\x44\x36\x95\x9d\x64\x9c\xc0\xc9\x99\x7f\x4c\xe5\x69\xfc\x1f\x96\x39\x3f\xf8\x79\x8d\x96\x2b\xf4\x8d\x3c\x4a\x53\x16\x25\xcf\x51\xb6\xcc\xf9\x14\xa0\xb0\x45\x8e\xce\xd7\x1c\x95\x95\x18\xd7\xfe\x21\xf8\xa8\xf5\x41\x9d\xc3\x69\xfa\x70\xa2\xbe\x77\xfb\x20\x7f\xbd\x27\xcf\x24\xb5\xcd\x0e\x9a\xda\x47\x3a\xb0\xdf\x7e\xd3\xbe\x1d\x5c\x94\x8b\x5c\xcc\x2e\x3b\x75\xe4\xd1\x21\x41\x0b\xd2\x7f\x86\xc3\x3e\x37\xbe\x3a\xbc\x7d\xe7\xda\xfe\x6e\x1f\xde\x90\xbd\x5d\x57\xab\x72\x71\xf2\x78\xb5\x3c\x7d\x38\x63\xab\x87\xcb\x5c\x48\xee\x25\xfc\x78\x50\x68\xbf\x2a\xe6\xbf\x62\xef\xf9\x42\xf2\xb8\xaf\xb2\x67\xe7\x8b\x4b\xc1\xdf\x1b\x5f\x35\x1e\xec\x3c\x5b\x93\x9c\x8b\x1f\x27\x12\x8f\xec\x20\x6c\x6d\xc2\xe1\xfb\x7a\x08\x84\x9f\xb2\xe5\xf9\xa2\xe2\x2b\xb5\x72\x09\x3f\xcd\x6b\x5f\x21\x9b\xb7\xce\x02\x4a\xe1\x3e\x63\xfd\x85\x6f\xaa\x15\x13\x5f\x2e\x66\xe5\x9c\xa3\x49\x0d\xed\xbe\x02\x22\x51\x7f\x05\x6d\x5a\x80\x99\xea\xde\x83\xaa\x6e\xb0\xbf\x2f\x4c\xfd\x2b\x90\xa9\xac\xfc\xf5\x11\xf2\x36\xdf\x52\xcf\x13\x32\x97\x3f\xdd\x87\x9f\xbe\x79\xfc\x58\xfc\x64\xc1\x24\xd8\x05\xd3\xf5\xf5\xf9\x6a\xb5\x3c\x61\x15\x9f\x82\xd6\x55\x33\xbe\xe2\x70\xcf\x13\x2d\xf8\xa6\x42\x82\x04\x96\x55\x7c\x05\x8d\xa0\x1b\xdb\xd0\x07\x04\x4e\x64\xf5\x9b\xc8\xdb\x3c\x7e\xe8\x79\x7b\x42\x43\xbd\xcd\xb7\xf0\xf1\x57\xe1\x9c\xe7\xcb\x8b\x16\x3f\x34\xfb\x4a\x72\x5e\x0e\xe5\x13\xd5\x45\x01\xc0\x7f\xfc\x78\x0f\xae\x66\x7a\x7b\x68\x1f\x69\x90\xa1\x60\xbf\xce\x38\xa4\xb0\xb7\x51\xb0\xea\xea\xf9\xe2\x94\x55\xd9\x8c\xe7\x2d\xbe\x7b\x68\xb9\x98\x5f\x22\x76\x76\xc6\xa1\xdf\xe5\x1a\x0c\x10\x9d\x2f\xca\x6a\x2a\x26\x9a\x19\x5b\x73\x98\x6d\x0a\x46\x34\x90\x9a\x3a\x82\x49\x55\x7d\x2e\xaa\x81\x2a\x86\x7a\xa6\x7d\x3d\x63\xe5\x6a\xd8\x33\xe8\x97\xa2\xf5\x2b\xc5\xba\x3b\x77\x14\xed\x37\xfa\x1d\xb0\xb4\x14\x15\xc5\xff\x95\xbf\x97\xb5\x6a\x6b\xbc\x8a\x31\xf0\x05\x18\x03\x8c\xc2\xad\x2d\x34\x5a\x2e\xe3\x96\xae\x92\x97\x8b\x9c\x6f\xd0\x11\xba\x83\x8d\x6a\xdf\xd8\xd1\xad\x5b\x9a\xf2\xef\xef\xcb\x66\x16\xe5\x07\x3c\x6f\xa0\xca\xdb\xbe\xb2\x0b\x55\x7a\x2c\x24\x2e\x39\x23\x7f\xbd\x73\x54\x8b\xff\x9e\xc6\x2f\xb4\x7f\x64\xf0\x1f\x35\xa0\xaf\xbf\x46\xd8\xab\x15\x08\xfd\xa6\x6c\x48\x89\xa4\xa6\x44\x2a\x2b\xfa\x0d\x75\xf4\xb0\x61\xfe\x16\x88\x00\xa0\x4d\x48\x0d\xf3\xb3\x19\xcf\xde\xbf\xcc\xd8\x9c\xad\xfe\x97\x68\x35\x11\x72\x78\xbe\x2c\x17\xf2\x34\x35\x30\xa0\xf9\xa9\x6b\xf1\xed\xcf\xd2\xea\x5b\xe6\x54\xb3\xd5\xf2\x02\x3d\x5a\xad\x96\xab\x09\xf4\xea\xd6\x13\x11\x0a\xb5\xaa\xf9\xf7\xfd\x5b\x68\xbf\x05\x70\x50\x2d\xa5\x67\x9d\xe0\x68\xef\xa0\x5a\xfe\xfd\xec\x8c\xaf\x1e\xb2\x35\x9f\xec\xa1\x7d\x09\x40\xa8\xfc\x62\x59\x09\x05\x07\x62\x25\x5f\x6e\x89\xc2\xba\xa3\x1f\x3f\xc3\x48\xd0\xf2\x09\xa2\x6a\x11\x89\xb7\xec\x98\xca\x6d\x36\x35\x38\x49\x2e\x1b\xa4\x31\xd1\x19\xf8\x75\xdd\x46\x4a\x14\x96\x2a\x37\xd4\xdb\xeb\xcb\x45\x1a\xc4\xc3\xba\xa1\x49\x2c\x1a\xd8\x9b\x4a\x39\x1f\x3f\xa6\xca\xd7\x29\x37\x87\xef\xa4\x97\x15\x47\x6b\xfe\x5f\xe7\x7c\x91\x81\xa3\xb3\x13\xda\xe2\xa8\x55\x07\x06\xc2\xcb\xd3\x74\x39\x6f\x0c\xc9\x86\x99\x7a\x5d\xcc\x64\x88\xb9\x81\x34\xce\xa4\x48\x32\x08\x2b\x06\x3d\xf4\x1a\x92\x9a\x83\xc7\x06\x22\xc0\x0d\xeb\x44\xf8\x43\x22\x1c\x0a\x7f\x6f\x47\x22\x31\x91\x54\x7a\x8a\xca\x47\x5e\x07\xc4\xfe\x91\x45\x6b\xa2\x2d\x3a\xf3\xc8\x1b\x74\x26\xf8\x24\x8e\x62\xaa\x88\x8d\x25\xb1\x8f\xb7\x24\x16\x93\x5d\x3b\xd5\xd6\x34\x51\xd5\xed\x68\xd7\x02\x1a\xdd\x04\x08\x7d\x93\x10\xa1\xbf\x1a\x27\xfa\x41\x53\x03\x54\x84\xee\xc3\xe0\x6a\x10\x35\xb5\xf5\x47\x07\x95\xa6\x6a\xfd\x83\x10\x82\xf4\x56\x5b\x0e\x2e\x6d\x8f\x75\xc4\xfa\x28\xa3\x81\xdc\x3f\x72\x98\x7e\xcf\xa3\xb7\xcd\x3e\x57\x20\xdc\xf0\x7e\xc5\x59\xfe\x70\xb9\xa8\xca\xc5\x39\x5c\x9e\x05\xe9\xb7\xae\x48\x50\xf2\x1d\xf4\xfd\xeb\x23\x20\xeb\xa1\x08\x2c\x0c\xa3\xc1\xad\xef\x16\x1f\xd8\xbc\xcc\xa1\x92\xe4\xf6\x2d\xd5\xad\x86\xdf\x5d\x2c\x48\x02\x84\x85\x82\x37\x0d\x9e\xb7\xca\x4c\x44\xd3\xe6\xc7\xfd\x7d\x11\x8c\xd7\x1e\xaa\x07\xe6\xa6\x74\x23\x32\x10\x14\x5e\xf2\x57\xcd\x19\x1a\x6b\xfb\x8f\x1b\xc2\x0e\x0f\xd1\x77\x05\xba\xe0\x48\xc4\x6b\xe7\x67\x48\x44\xaa\x53\x54\x56\xff\xf7\x7f\xff\x9f\x7a\x58\xd2\x41\x00\xc5\x37\x2c\x3d\x1f\x54\xbc\x35\x70\xfe\x52\x7b\x5f\x82\x15\x4c\x5a\x2d\x17\x95\xb1\xae\x86\x44\xff\xe2\xeb\x5f\x02\x83\xfa\x0e\x65\xf5\x09\xa2\xea\x42\x3a\x1a\x4a\x5d\x71\xb6\x60\x73\xb8\xfc\xd0\xf0\xf1\x05\x67\x39\x2a\xca\xd5\xba\xaa\xb9\x04\xdd\xda\x5d\xcc\xc3\xd1\x0d\x4d\x16\xcb\x21\x7b\xd7\x7b\xb5\x4e\x48\x44\x37\x95\xfc\x95\x67\xd5\x68\x6d\xf8\x5b\xd3\x3a\x1c\xc3\x7a\x70\x1e\xd5\x0a\xf5\xb0\x06\x05\x62\x41\x47\x16\x83\xb9\xd7\xf7\x07\x3a\x30\x2c\xa7\x19\x90\x73\xa7\x91\xae\x29\x00\x6b\xb4\xb7\x55\x5f\xcd\x47\x75\x03\xf8\x1d\x54\xb0\x0e\xeb\x65\xdf\xfd\x3e\x6f\x4f\xd9\x25\x2a\x17\xd9\xfc\x1c\x26\x21\x62\x72\xa1\x4f\x69\x4c\x5c\x7e\x5c\x73\xe7\xd1\x0e\xdc\x01\x55\xbe\x1a\x03\x3d\x35\x4f\x23\x70\x36\x49\xe2\xd2\x19\xea\xdb\x18\xea\x41\xf0\x22\x19\x36\x16\x1f\x7c\x4e\x9e\x0f\x47\xf8\x3e\x47\xa9\xe2\xe8\xe3\xeb\xe5\x28\xb8\x8c\x2b\x32\x3d\x06\xa6\x7b\x9b\x3e\xdb\xbd\x8d\xf7\x70\x0f\xfd\x06\x1c\x99\x48\x1a\xe4\xaf\x8d\x3c\x02\xab\x3c\x60\x46\x65\x98\x63\x60\x4f\x9f\x82\x99\x25\x51\xf3\xd3\x28\x85\xbf\xbf\x7a\x7c\x87\xa2\x1c\x56\xca\x78\xde\x78\xde\xda\x6d\xaa\x1b\x58\xcd\x77\x70\x68\xda\x77\xf0\x3f\xf7\x7a\x31\x89\x8a\x35\xda\xd1\x58\xd2\xd7\xc0\xeb\x86\x24\x5a\xb5\xda\xab\x01\x16\xdd\x01\x6a\x41\x89\xe6\x63\xdb\xd5\x9f\x4e\xb8\xd3\xae\x13\x55\xa7\x67\x5a\x34\x32\xa9\x4e\xcf\xd0\x51\x6f\x2c\xd9\x43\x7f\x39\x3a\x92\x4e\xb9\x1f\x9d\xa8\x4d\x8c\xea\xf4\xac\x1f\x67\x68\x13\xf4\xb6\xf6\xde\xe7\x5c\x7c\x13\x6c\x45\x47\x40\xe0\xad\x0f\x7c\xb5\x2e\x97\x8b\x5b\x77\xd1\x2d\x58\xf4\xbd\x35\x15\xbf\x4a\x7a\x6e\xdd\xd5\xa2\x42\xf8\x5d\x76\x57\xfd\x2e\xbf\xdc\xf8\xea\xa3\x5a\xa4\x7b\xb9\x3c\xe5\xe8\xc1\xd3\x6f\x51\x7a\x5e\xce\x73\xb4\x3c\xab\xca\xd3\xf2\x17\xbe\x5a\x4f\xd1\xbc\x7c\xcf\xd1\xea\xe0\xe7\xf5\x54\x4e\x89\x61\xa5\x7d\x7d\xc6\xb3\xb2\x28\x33\x61\xbc\x79\x09\x02\x3f\x63\x55\xc5\x57\x8b\x35\xc0\x83\x46\xd5\x8c\xa3\x62\x39\x9f\x2f\x2f\xca\xc5\xc9\x5d\xb9\xe6\x29\xd4\xaf\x77\x2f\x12\xdd\xaa\x95\xe6\x96\x5c\xdc\xed\x54\x38\x60\xa7\x79\x6f\x15\xb5\xb9\x22\x29\xca\x6e\x7c\x25\xc5\xa5\x2e\x4d\x36\xcb\xdc\xdd\x01\x4c\xf4\x19\x64\x07\xc2\x69\x67\x17\xbd\x55\xe3\xbf\x68\xdf\x0f\x16\xcb\x9c\xbf\xba\x3c\xe3\x6d\x30\xd7\xae\x55\xab\x89\x47\xb9\xd0\xd7\x8d\x5f\x94\x8b\x93\xe5\xff\x7c\x89\x3e\x78\x07\xf4\xc0\x83\xe9\x79\xdb\x42\xbb\x4b\xda\x10\xa3\x5c\x63\x0d\x89\xad\x2e\x66\x6c\xde\x83\x14\x1f\x78\x77\xe4\x42\xcc\xaa\x3e\x1b\x25\x6f\x31\xaa\xdf\x66\x6c\xfd\xec\x62\xf1\xbc\x3e\x02\x73\xa4\x2a\x1d\x74\x7f\x87\xea\xcd\x16\x09\x64\x8d\x93\x4c\xa9\x3d\x46\xb7\xba\xdc\x1f\x12\xe5\x70\x91\x78\x4f\xf0\x46\xe7\xd5\x9b\xf7\x32\x81\xa1\xa8\x01\x9f\x3b\x8b\x5f\xbd\x7e\xbd\x98\x95\x8b\xa5\xe8\x15\x43\x17\x3c\x45\xea\xa2\xaa\x5a\xb5\x3e\x50\x0a\xad\x78\xf2\xf1\x86\xba\xa2\x0a\xdb\x26\x1f\xa7\xbf\x7e\x7c\x3b\xa5\xd1\x36\x5b\x22\x83\x1b\xbb\xaf\x9f\x3e\x39\xae\xaa\xb3\x17\x62\xc8\x58\x57\x0d\xb4\xbf\xa6\xe5\x89\x3c\xcc\x72\xf0\xf3\xfa\xaf\xdb\x40\xbe\x75\xbe\xe6\x30\x61\xcb\xaa\x5b\xf7\x6e\x0c\x11\x7d\x53\x9e\xfc\x00\x00\xef\x89\x0e\xff\xbc\x9e\x09\xa7\x5c\x9e\x2c\x96\x2b\x7e\x77\x5e\x2e\xf8\x8d\x06\xf5\x05\x4f\xfd\xad\x50\x0a\x21\xfd\xc8\x53\x39\x36\xc9\x6b\xc6\xb7\x0e\x0e\xe7\x65\x7a\x28\x40\x08\xe7\x7c\xe3\xf0\x10\xe5\xcb\x45\x85\x96\x1f\xf8\x6a\x55\xe6\xbc\xde\x70\xa8\xf7\x37\x6e\x68\x57\x90\xd5\xce\x81\x70\x70\xb7\x9a\x03\x0d\xb0\x1f\xd1\xa9\x70\x20\x51\x76\x6b\x09\x05\x81\x6d\x32\xbd\x0a\x10\x77\xef\xc6\x47\x03\x37\x64\x89\xda\xd8\xaa\x29\xfe\xeb\x5d\x42\x3e\xbe\x15\x5c\x98\xbe\x91\x5c\x78\xbb\x77\xe3\xf0\xf0\xff\x43\xeb\xe5\xf9\x2a\xe3\x4f\xd9\xd9\x59\xb9\x38\xf9\xfb\x8b\x27\x47\xa2\xf0\xce\x1c\x0e\x91\xfe\xbc\x3e\x38\x65\x67\x37\xfe\x5f\x00\x00\x00\xff\xff\x72\x02\x45\xd0\x9c\x29\x06\x00") +var _web3Js = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xfd\x6b\x7b\x13\x39\xd2\x38\x0e\xbf\xcf\xa7\x50\xfc\xdc\x0f\xb6\x89\xb1\x9d\x84\x61\x18\x67\x32\x6c\x08\x30\x64\x6f\x20\x5c\x40\x76\x76\xef\x6c\x96\xab\xe3\x96\xed\x1e\xda\xdd\xfe\x75\xb7\x73\x18\xc8\x77\xff\x5f\x2a\x9d\x4a\x87\x3e\x38\x09\xc3\xcc\x6c\xf2\x02\xdc\x52\xe9\x54\x2a\x95\x4a\xa5\x52\x55\x46\xff\xdf\x32\xca\xe8\x6e\x67\xb2\x4c\xc6\x45\x94\x26\x84\x76\x8a\x5e\xd2\xcb\xba\x9f\x55\x4a\xde\x49\x7b\xcb\xee\xe7\x68\xd2\x59\x4f\x8e\xd3\x13\xfe\xab\x80\x5f\x67\x41\x46\x82\xdd\xe2\x72\x41\xd3\x09\x91\x75\xed\xb6\x64\xd1\xd6\xbd\x7b\x22\x71\x87\x95\x59\xde\xbb\x17\x74\x33\x5a\x2c\xb3\x84\x04\x9d\xb4\xb7\x3e\xec\xb2\xf4\x48\xa6\x45\x22\x8d\xd5\x3a\xd9\x4d\xe8\x39\x79\x9e\x65\x69\xd6\x69\xed\x07\x49\x92\x16\x64\x12\x25\x21\x99\xa7\xe1\x32\xa6\xa4\xdd\xda\x48\x37\x5a\xed\x56\x77\xa7\x98\x65\xe9\x39\x99\xf4\xc7\x69\x48\x77\x5b\xaf\x0f\x9f\x1d\xbd\x7a\xfe\xf1\xcd\xe1\x87\x8f\x2f\x0e\x8f\xde\x3c\x6b\xf5\x26\x57\xac\xbe\x78\x97\xf5\x7d\xf7\x33\xbd\x58\xa4\x59\x91\x8f\x3e\x5f\x5d\xed\xb0\x31\x1c\x0f\x4f\xfa\xe3\x20\x8e\x3b\x71\x5f\x64\xf5\x64\xef\x3b\x94\x0f\x30\xd9\x05\xc0\xcd\x93\x63\x7a\xb2\x23\xba\x9a\x77\x92\x27\xc9\x88\x76\xaf\x7a\x71\x4f\x97\xa4\x3d\x8e\xbb\x2b\x01\xc5\x9a\x94\x99\xd0\x8b\xa8\x11\xae\x26\x69\xd6\x61\xd0\xe9\xee\x70\x27\xfd\x31\xeb\xc7\x34\x99\x16\xb3\x9d\x74\x63\xa3\x9b\x77\x32\x86\x78\xd5\x8d\xab\x6e\xe7\xf3\xe6\xe8\x58\x75\x59\x54\xd1\xe3\x58\xea\x89\xb6\xbb\x9f\xd7\x78\x82\xec\xcc\xee\xf1\x1a\x21\x9f\xd7\x08\x21\xa4\x35\x4e\x93\xbc\x08\x92\xa2\x35\x22\x45\xb6\xa4\x3d\x9e\x1a\x25\x8b\x65\x91\xb7\x46\xe4\x18\xbe\x25\x34\xe4\x25\xc1\x9c\xb6\x46\xa4\xf5\x31\x3d\x4f\x68\xd6\xea\xe9\x1c\x36\x3a\x96\x13\x84\x61\x46\xf3\xbc\x25\x72\xae\xe0\xff\x13\x51\xb5\x2c\x0e\xff\x8b\xb4\x74\x59\xd4\xb7\x97\x7e\x44\x45\x8c\xf6\x4e\x2f\x0b\x9a\x6f\x6f\xf9\xdb\x93\x40\x0a\xd3\x6b\x84\x5c\xf5\x6e\x05\x01\xd7\xea\x8f\x1a\x0e\xc2\x5e\x33\x04\xac\x8c\xea\x3f\xea\xd0\xc7\x69\x52\xd0\xa4\xb8\xf1\xe0\xff\x94\xf3\xce\x66\xec\x0f\x33\xed\x93\x20\xce\x7f\xbf\xa1\x67\x34\xa7\xd9\x99\x6f\xd5\xff\xd1\x27\x2d\x5f\x9e\xbe\xa3\xd3\x28\x2f\xb2\xe0\xbf\x60\xf2\x7a\x55\x75\xd0\xf3\xc3\x1b\xf1\xfd\x22\x0b\x92\x7c\xe2\x65\x7d\x7f\x16\x1c\x64\x16\x29\xac\x8e\x84\x9c\x16\xef\xab\x49\xea\xd6\x70\x61\x37\xfd\xbb\x34\xfa\x95\x27\x20\x68\x82\xf8\xaa\x0a\x16\x59\x34\x0f\xb2\x4b\x6f\x3f\xd2\x34\xae\x9d\xbc\x3d\xd1\xd6\x9f\x17\x85\xe6\x1e\x5c\x59\x4d\x19\x12\xf6\x4b\xb7\xf1\x3f\x12\x12\xbc\xbd\x0f\xa3\x3c\x3d\x4f\x6e\xd0\xf3\x20\x49\x93\xcb\x79\xba\xcc\x57\xe8\x7a\x94\x84\xf4\x82\x86\xc6\xde\x75\x6b\x13\xab\x2b\x47\xdd\x31\x6b\x3f\x8f\x92\x9b\x30\xee\xbd\x25\x60\xe2\x79\x12\xd2\xb0\x65\xa1\x89\x9e\x31\x42\xf8\x0b\xe0\xe8\x34\x0a\xc3\x66\x38\xba\x5e\xfd\x67\x41\xbc\xf4\x76\x7f\x19\x25\xc5\xd6\x77\x8f\xaa\xa7\xe0\x0d\x3d\x7f\x1a\x7d\x43\xe4\xdf\x68\xcd\xed\xcf\x82\x64\xfa\x2d\x49\xe7\x56\x28\xa7\xa4\x6e\x24\xd5\x57\x52\x8d\x17\x33\x6f\xf9\x6e\x54\x8b\xa0\xb5\x93\xb5\xb5\xab\xde\xe7\xab\x93\xde\xd6\x37\x3b\xf4\xff\x85\xce\xbc\xdf\x48\x76\x9c\x2c\x93\xf0\xda\xa4\x72\xe3\x8d\xeb\xee\xd8\xfb\xe7\x3e\xf6\xde\x1d\xfa\xfe\xc8\x67\x0e\xef\xe0\xc5\x79\xe1\x8f\x26\x6d\x7e\xdd\xcd\x5c\xef\x55\xdb\xb7\xb6\x57\xad\x3a\xef\x93\x2c\x9d\xdf\x70\xda\x8b\xf4\x86\x47\xcd\x9b\x09\x7c\xdf\x76\xdd\xfc\x11\xf0\x17\x25\x61\x94\xd1\x71\x71\xe0\xdd\x33\x57\xe8\xc9\xcd\x26\x22\x1a\x07\x8b\x0f\xdf\x74\x32\xfc\x98\x6c\x76\xda\xa5\x8b\x34\x8f\xaa\x0e\xea\x8b\xe0\x32\x38\x8d\xa9\x29\x14\x7c\x13\xae\x54\x46\x73\xb7\x72\xfc\xba\x19\x0d\xec\xc9\xf1\x3e\x33\xf1\xf9\xfb\x9f\x64\x6e\x05\x49\x25\x75\x37\xa3\xb3\x6f\x80\xfe\x3f\x2c\xd6\x6f\xe3\xfc\x78\x6d\x3e\xf9\xb5\xb1\x6e\x33\xbd\x3b\xb4\x37\x44\xfb\x8d\x37\xae\xaf\x3d\xb3\x07\x9e\x2d\xad\x4a\x8e\x7b\xd8\x44\x8e\x03\xe3\x0d\xb2\x2b\x2d\x1c\x3a\xed\xfe\x60\x92\x66\xf3\xa0\x28\x68\x96\xb7\xbb\x3b\x00\xf0\x3e\x8d\xa3\x30\x2a\x2e\x3f\x5c\x2e\xa8\x09\xcb\xda\x67\x50\x6b\x83\xfb\xf7\xd7\xc8\x7d\x03\x52\xe8\xdc\x49\x94\x93\x80\x2c\xb2\x34\x65\xc0\xa4\x98\x05\x05\xc9\xe8\x82\x1d\xb2\x92\x22\x27\x62\xee\x08\xcb\x64\x35\x1c\x14\x64\x1e\x14\xe3\x19\xcd\x47\xec\x53\x64\xa3\x9f\xc7\x27\xf8\xe3\xa1\xf1\x75\x62\x66\x6e\x5b\xdf\x27\xc7\x8f\x4e\x8e\x4f\x7a\xa4\xdf\xef\xaf\x91\xfb\x03\x67\x6c\xb2\xc7\xbb\x44\x59\xd3\x74\xba\x62\x8a\x8b\x59\x94\xf7\x3f\xc2\xc2\x78\x21\x11\xc4\x00\xfb\x1c\x5d\x07\x2c\xe3\x20\x29\x76\x10\x30\xdf\xb7\x7d\xd0\x87\x90\x23\x9a\xdb\x59\xbb\xda\x59\x5b\xf3\xf4\xa3\xbf\xc8\xd2\x82\x63\x6d\x97\x24\xf4\xdc\xe8\x6b\xe7\xf3\x55\x77\xa7\xba\x54\x1f\xa4\x97\x6c\x39\x2e\x52\xd6\xb8\x07\xb6\xae\xdd\x7e\x94\x8b\x39\xd7\x08\x61\xe4\x28\x91\x22\xec\x5a\xd6\xd7\x59\x62\x1f\xe6\xad\x33\x10\xd8\xee\xfc\xfb\xb8\x73\x3c\x7c\xf0\xc3\xc9\xfd\xee\xbf\x4f\xba\x4f\x06\x5d\x3e\x4e\xf3\xe0\x50\xda\xad\xab\xde\xe7\x16\x26\xc5\xd6\xe8\x87\x5e\x8b\xd3\x5b\x6b\xb4\xf9\xf0\xea\xa4\xf7\xdd\x37\x26\xef\xa7\x69\x1a\xd7\xd0\xf6\x29\x03\x29\x21\x6c\x96\x27\xff\xe7\x54\x0a\xbf\x1e\xea\x9f\x27\x28\x79\x1b\x7f\xd4\x91\x31\xf4\xec\xba\x34\xcc\x0a\xaf\x42\xc4\x1c\xde\xa6\x60\x96\xba\x22\xf9\x9a\x45\x2a\x68\x97\xb7\x58\x55\xf6\x3a\x54\xfb\x1f\x86\x5a\x93\x66\xef\xff\x4f\x23\xa2\x15\xfd\xa9\xa7\xd8\x47\xdf\x9a\x62\xd9\x1e\xa6\x48\xb6\xf0\xd3\x6c\x31\xa3\x04\x36\x3b\x20\xdc\xbe\x8f\x72\x59\xae\xfa\x21\xe8\x12\x7e\x3e\x44\xbf\x4f\x70\xc6\xb6\xf1\x65\xd2\x2f\x11\x5b\xab\xfa\xf9\xd8\xa8\x47\x14\xf5\x50\x39\x74\xf2\xda\x64\xce\x4a\xaf\x44\xe7\xbc\x80\x43\xe8\x2c\x79\x55\x4a\x37\xcb\x54\x91\x3a\x6f\xb4\xb2\xf4\xf5\x88\x9d\x55\xc2\x49\xfd\xf3\x66\xef\xaa\x7b\x3d\xc2\x17\xbd\xab\xa7\xfc\xef\x9b\x50\xfe\xe0\x3e\x74\xf8\xc3\x2c\xca\xc9\x24\x8a\x29\xa3\xd4\x45\x90\x15\x24\x9d\x90\x73\x7a\xba\xdd\xff\x35\xef\xaf\x01\x88\xf8\x62\x00\x93\x8c\x52\x92\xa7\x93\xe2\x3c\xc8\xe8\x88\x5c\xa6\x4b\x32\x0e\x12\x92\xd1\x30\xca\x8b\x2c\x3a\x5d\x16\x94\x44\x05\x09\x92\x70\x90\x66\x64\x9e\x86\xd1\xe4\x12\xea\x88\x0a\xb2\x4c\x42\x9a\x01\xc1\x17\x34\x9b\xe7\xac\x1d\xf6\xf1\xf3\x9b\x23\xf2\x8a\xe6\x39\xcd\xc8\xcf\x34\xa1\x59\x10\x93\xb7\xcb\xd3\x38\x1a\x93\x57\xd1\x98\x26\x39\x25\x41\x4e\x16\x2c\x25\x9f\xd1\x90\x9c\x5e\x0a\x2a\xa2\xe4\x05\xeb\xcc\x7b\xd1\x19\xf2\x22\x5d\x26\x61\xc0\xc6\xdc\x23\x34\x2a\x66\x34\x23\x67\x34\xcb\xd9\x0c\x6d\xcb\xb6\x44\x8d\x3d\x92\x66\x50\x4b\x27\x28\xd8\x18\x32\x92\x2e\x58\xc1\x2e\x09\x92\x4b\x12\x07\x85\x2e\xeb\xa2\x40\x8f\x34\x24\x51\x02\xd5\xce\x52\xb9\xb2\xa3\x82\x9c\x47\x71\x4c\x4e\x29\x59\xe6\x74\xb2\x8c\xb9\xe0\x78\xba\x2c\xc8\x2f\x07\x1f\x5e\x1e\x1e\x7d\x20\x7b\x6f\xfe\x45\x7e\xd9\x7b\xf7\x6e\xef\xcd\x87\x7f\xed\x90\xf3\xa8\x98\xa5\xcb\x82\x30\x89\x12\xea\x8a\xe6\x8b\x38\xa2\x21\x39\x0f\xb2\x2c\x48\x8a\x4b\x92\x4e\xa0\x8a\xd7\xcf\xdf\xed\xbf\xdc\x7b\xf3\x61\xef\xe9\xc1\xab\x83\x0f\xff\x22\x69\x46\x5e\x1c\x7c\x78\xf3\xfc\xfd\x7b\xf2\xe2\xf0\x1d\xd9\x23\x6f\xf7\xde\x7d\x38\xd8\x3f\x7a\xb5\xf7\x8e\xbc\x3d\x7a\xf7\xf6\xf0\xfd\xf3\x3e\x21\xef\x29\xeb\x18\x85\x1a\xea\x11\x3d\x81\x39\xcb\x28\x09\x69\x11\x44\xb1\x9c\xff\x7f\xa5\x4b\x92\xcf\xd2\x65\x1c\x92\x59\x70\x46\x49\x46\xc7\x34\x3a\xa3\x21\x09\xc8\x38\x5d\x5c\x36\x9e\x48\xa8\x2c\x88\xd3\x64\x0a\xc3\x56\x54\x46\xc8\xc1\x84\x24\x69\xd1\x23\x39\xa5\xe4\xc7\x59\x51\x2c\x46\x83\xc1\xf9\xf9\x79\x7f\x9a\x2c\xfb\x69\x36\x1d\xc4\xbc\x82\x7c\xf0\x53\x7f\xed\xfe\x40\x32\xdb\xbf\x01\xd9\x8e\xd3\x90\x66\xfd\x5f\x81\x45\xfe\x2d\x58\x16\xb3\x34\x23\xaf\x83\x8c\x7e\x22\xff\x9b\x16\xf4\x3c\x1a\xff\x46\x7e\x9c\xb3\xef\xbf\xd1\x62\x16\xd2\xb3\xfe\x38\x9d\xff\x04\xc0\x61\x50\x50\xb2\x35\xdc\xfc\x0e\x18\x5e\xfd\x56\x50\x21\xc0\xa2\x32\x42\x1e\xf3\xed\x1d\x42\x52\x40\xc0\x6c\x17\xf4\x41\x1e\x24\x85\x09\x18\x25\x85\x0f\xee\xc8\x01\x5c\x96\x40\x3e\xbb\x4c\x82\x79\x34\x96\x6c\x1c\x95\x08\x79\x0e\xf0\x28\x5f\xc9\xf7\x45\x16\x25\x53\xb3\x4c\x0e\x69\x3e\xe8\x77\x34\xb0\xc6\x98\xd1\xc0\x3b\xc6\x23\x17\x74\x59\x06\xeb\xe9\xb6\xea\x2f\x00\x47\xb9\x18\xa0\xc1\x99\x73\x54\x45\x0f\x76\x58\xc1\xa7\xa5\x85\x38\xca\xef\xab\x2a\x60\x1b\xe1\xc0\x5f\xbe\xa8\xd3\x23\x29\x81\xde\xcb\xb2\xe0\x92\x83\x73\x26\x6e\x89\x02\xfb\x8c\x3e\x91\x04\x20\x56\x12\xe7\x10\x21\x29\x52\x42\x13\x46\xc3\x83\x90\xb2\xff\x54\x2b\x8c\x19\x07\x9c\x4d\x32\xae\x24\xe4\x5a\x73\x63\xe6\x75\xe3\x11\x33\xb0\xdc\xdc\x99\x21\x89\xec\x42\x0d\xb9\xd1\x45\xe0\xfd\x73\x5a\xcc\xd2\xd0\xd3\x2d\xae\x5c\x4f\xb3\x39\xe1\x92\x4b\x6a\xcc\xc8\x1a\xe1\x6b\x50\x14\xff\x28\x66\x46\x64\x91\xbf\x41\xef\xc9\x67\x4e\x3c\x57\x4a\x2c\xff\x1b\xc7\x7c\x4e\x3e\xe3\xca\xae\x20\x0b\xde\x2a\xe4\xe4\x33\xbc\x6b\xb8\x22\xe2\x33\x62\xbc\x81\x4b\x44\x8c\x0c\xa1\x2f\x6c\x27\x62\xec\x1e\x10\x62\x20\x03\xed\xd4\xb8\x4b\x0e\x8e\x24\x8a\x18\x36\x73\x53\xbc\x43\x58\xeb\x4f\xa2\xb8\xa0\x59\x07\x95\xed\x22\x1d\x84\xa0\xa2\x42\x08\x05\x92\x08\x40\xa7\xd0\x3d\x1e\x9e\xec\x70\xfe\x19\x4d\x48\x67\x1d\x37\x82\xeb\xe0\x0f\x34\xf8\x53\x8e\x76\x94\x9c\x05\x71\x14\x6a\x1a\x60\x35\xae\x8f\x48\x9b\x6c\x10\x5c\xf9\x1a\x96\x35\x70\xcd\x26\x05\x96\x50\x1a\x59\xc4\x41\x94\x70\xfa\xb2\xa6\x91\x03\xbc\x15\x39\xe5\xb3\x28\xd2\x0f\x4f\x7f\xa5\xe3\xe2\xca\xaa\x50\x4e\xb2\x2e\xc7\xab\x0d\x2d\xb8\xf2\xa9\x43\xdd\x70\x66\xae\xc7\xcb\x5b\x02\x17\x4c\x1a\x2a\x96\x77\x8e\x19\xf0\x49\x8f\x1c\x03\xf8\x49\xb7\x19\x6a\xe2\x28\x07\x09\x88\x2f\xbe\x72\xec\xe4\x18\x0d\xc0\x02\x38\x76\x7c\xe9\x0b\x5d\xa0\x0c\x31\x4e\xb3\x8d\x70\x93\xbb\x4b\x5f\x60\x27\x2f\xa3\xef\x5c\x12\xf8\x94\x16\x78\x05\xe6\x82\x73\x08\x92\x65\xc5\x44\xdf\x58\x09\xa3\x86\xfe\x3c\x58\x74\xca\x78\x2c\x68\xe5\x3c\x6b\xc4\xe0\x9d\xbc\xe6\x0e\xef\xe9\x31\x14\x39\xe1\xec\x59\x7e\xa9\x55\x84\xfa\x23\xf6\xa9\xc3\xc9\x24\xa7\x85\xd3\xa9\x8c\x86\xcb\x31\x45\xfd\x0a\xc6\xe3\x1e\xa9\xe9\x1c\x60\xa7\x08\x8a\x68\xfc\x36\xc8\x8a\x57\xf0\x92\xc8\xaa\xb9\x6f\xe7\x77\x3c\xfd\x94\x75\x65\x8c\x29\xd1\xf0\xbd\x5b\xe5\xeb\xa0\x98\xf5\x27\x71\x9a\x66\x9d\x8e\xd3\xe2\x06\xd9\xde\xec\x92\x01\xd9\xde\xea\x92\xfb\x64\x7b\x4b\x0c\x1a\xa1\x2f\x18\x8f\xc9\x06\xe9\xa8\x4d\xc7\xc0\x7a\x09\x0a\xc9\x13\xb4\x77\x11\xb2\xbd\x45\x46\x46\x42\x49\x67\x25\xea\x7b\x64\x88\xb1\x9f\xd1\x7c\x19\x17\x92\x7a\xf8\x0c\xbe\x5e\xc6\x45\xf4\x4b\x54\xcc\xf8\x9c\x48\x0a\x34\xfa\xd6\x53\x74\xd4\x33\x67\x50\x56\x2e\x46\xc8\xeb\x37\x4f\x7c\x7e\xd2\xb7\x5a\xf5\xad\x81\x86\x3d\x40\x6b\x44\x0d\xaf\xd5\xda\xd1\x0b\x87\xc6\x13\x31\x62\xd1\x59\xb1\x2b\xa4\xd9\xf3\x60\x3c\xeb\xd8\x8c\x29\xc2\xb4\xc5\xb8\x7e\xe9\x7c\xe9\xb9\x3a\xe9\xe2\x42\x1c\x21\xd0\x95\x0d\x57\xdb\xd9\x31\xbb\x2f\xd7\x11\x22\x42\xb5\x76\x19\x15\xd3\x78\x22\x40\xec\x39\x82\x0e\xb8\x5d\x92\x78\x82\x0f\x7b\xb2\x70\x13\xe6\x52\xdc\xd8\x25\x54\x3c\xc3\x23\x03\xb2\xa5\x41\xaf\x08\x8d\x73\x6a\x0d\x6f\x30\x20\x61\x9a\xb4\x0b\x12\x84\x21\x11\xa5\x8a\xd4\xac\xb2\x4f\xa2\xa2\x9d\x93\x20\xce\x68\x10\x5e\x92\x71\xba\x4c\x0a\x1a\x96\x60\xe9\x2b\x8d\xf3\x4a\x2f\xc2\xc1\x80\x7c\x38\x7c\x76\x38\x22\x93\x68\xba\xcc\x28\x61\x07\xb6\x84\xe6\xec\x04\xc8\x4e\x69\x97\xb9\xc9\xac\x7e\x0f\x22\xf9\xe3\x4c\xb2\x39\x19\x14\x23\x50\x62\xa5\x64\x99\x2b\xb4\x66\x74\x12\x80\x3a\xe6\x7c\x96\xc6\x94\xf7\x30\x4a\xa6\xeb\x35\x8c\xa0\x82\x07\xd8\x9c\x5f\x0c\xba\x47\x52\x67\xe5\x1b\x8b\x5c\xce\x49\xad\xa8\xef\xd9\xe2\x3a\xae\x6a\x0c\x11\x10\x6f\x98\x9c\x07\x9a\xac\x73\x5a\x38\x73\xca\xc9\xea\x4d\x30\xa7\xf6\x3e\xa4\x73\xb0\x9c\xe9\x96\xf5\x6c\x3e\xd5\xfb\x99\xae\xd8\x53\xa7\xe2\x8b\x02\x83\x5a\xaa\x95\x7f\x15\xc3\x96\x95\x2c\x32\x7a\x16\xa5\xcb\x5c\x75\x68\x6b\x87\xa1\x24\x4a\x48\x94\x14\x4e\x89\x3a\xfc\xa3\xfe\xfa\x1a\x64\x7f\x93\x34\x23\xf0\x48\x38\x22\xbb\x64\x73\x87\x44\xe4\x47\x39\x00\xf9\x5e\x98\x44\x1b\x1b\x65\xc5\xd9\x9f\xd5\xe7\x8d\x5d\xb2\xd1\x91\x38\x88\xc8\x03\xb2\x79\xc2\x24\x7c\xf2\xe5\x0b\x19\xee\x94\x56\x52\xc1\xca\x05\x3d\x6c\x90\x88\xdc\x2f\x9b\xb9\x0d\xbb\x17\x4c\x38\x28\x63\xfb\xf2\xef\xca\x49\x35\x53\xae\xba\x9d\xae\x35\x85\x83\x01\x99\x44\x59\x5e\x10\x1a\xd3\x39\x4d\x0a\x76\xbe\xe2\x68\xea\x91\xfc\x53\xb4\x20\x51\xb1\xca\x94\x1b\xd8\x1f\xfa\xb0\xcf\xf0\x57\x39\x03\xf0\x74\x3e\x0c\x23\xd6\x48\x10\xab\x45\x2e\xf0\xe9\xf0\x1f\x17\xdf\x7e\xbe\xa8\x49\xa7\x84\x41\x1c\x47\x64\x83\x6c\x9e\x48\x3e\x41\x36\x88\xd3\x0d\x0f\xda\x6b\x11\x6c\x31\x3f\x0f\xa4\xd8\x2a\x3d\xb4\xcf\xa9\xe2\xda\xac\xe7\x0f\xcd\x54\x98\xb0\x65\x62\xea\x86\x8b\xbf\x86\x32\x49\x19\x43\x1a\x56\x31\x24\xd2\x88\xa6\x6b\x39\xca\x60\x40\xc6\x41\x3c\x5e\xc6\x41\x41\xa5\xe0\xc3\x8e\x7c\xa2\x2f\x24\x2a\xe8\xfc\x06\xec\x88\xb1\xa2\xe3\x3f\x11\x53\xea\xda\xb0\x57\x2b\xed\x2b\x37\x9c\x90\x6f\xc7\x60\x30\x73\xf9\xea\xbc\x85\x38\xda\x22\xd1\x8f\x1a\x6d\x88\xd0\x45\x8a\x9b\xc9\xb4\x42\x63\xc4\x21\x1b\x6b\x8c\x64\xba\xba\xd5\x54\x2a\x11\xbf\x2e\xa9\x5c\x0f\x82\x1a\xf6\x88\x7f\x50\xbf\x4f\x47\x84\x8a\x69\x1d\x11\x87\x06\xd9\xa6\x09\x5a\x2a\x95\x44\x25\x08\x29\xd3\x11\x95\x23\x44\x94\x80\x13\x06\xb4\xa6\x11\x53\xad\x21\xc2\x43\xf4\x9d\x8e\x0d\xdc\xac\xae\x20\x92\xa5\x38\x15\x63\x78\x4e\xc4\xb9\xf7\x14\x6e\x1d\xf7\x6f\x59\xa3\xc4\x87\xdc\x81\x91\xc9\xf5\xa5\xd5\x22\x86\x5e\x44\xd6\xa8\x35\x4c\x55\x2a\x07\x3d\xaa\x5a\x3d\x03\xc6\x28\xe7\x40\xac\xcc\x6d\x8f\xb4\x89\x3a\x4a\x9d\x44\x7d\x72\xb0\xe8\x5a\x29\x93\x1c\x0c\x48\xbe\x9c\xf3\x1b\x3a\xcf\x2e\x25\x44\x44\x05\x2f\xaa\x3b\x8e\x4e\x18\x57\x54\x5f\xb0\x25\xf9\xf8\x8f\x6c\xde\x44\x84\x94\x36\x1d\x14\x0c\x06\x24\xa3\xf3\xf4\x0c\xae\x31\xc9\x78\x99\x65\x4c\x3e\x55\xc2\x69\x0a\xc9\xa2\x9b\x51\x0e\x3d\xf7\xf4\x36\x5f\x45\xe3\x27\x91\xd9\x58\xf3\x67\x8c\x8c\x3c\x70\xea\x6f\x4c\x69\xef\xad\x75\x58\x72\xad\xe3\x3d\xb5\x4a\x1e\xe7\xa1\xb2\xc2\xba\x72\x90\x64\xc5\x76\x30\x7c\x49\x62\xde\x5f\xf0\xde\xb2\xb6\xc6\xe2\x96\x09\x9b\x5a\x40\xef\x3b\xdc\x5e\xd5\x36\xc1\x10\xd7\xa2\x9d\x6e\xcf\x9b\xfd\x34\x4d\xe3\xb2\x3c\x26\x84\x94\x64\x1d\x55\xe4\xe1\xcb\xcd\xd2\x66\xab\x32\x39\x17\x2e\xcb\x7d\x47\x83\xd2\x1e\x1f\xf1\xcc\x35\x46\x10\xae\xfd\x06\xa0\x4e\xd9\x6c\x48\xc3\xd9\xd1\xc3\x5e\x8b\xdf\xfd\xb6\x46\xdf\xc1\x4f\xd6\xb7\xd6\xe8\x11\xfb\x8d\xaf\x63\x5b\xa3\xc7\x3d\x9f\xad\x47\x94\x14\xad\xd1\xe6\x90\xfd\xcc\x68\x10\xb7\x46\x9b\x5b\xec\x37\xbf\x95\x6d\x8d\x36\xb7\xd9\xd7\x92\x43\x41\x03\x4b\x01\xf6\xe8\xea\xa4\xf7\xf8\xf7\xb4\x8b\xaa\xb9\x86\xbe\x9e\x35\x11\xae\x64\x15\xa3\x22\xb3\x9c\x6d\x5b\x84\x73\x57\x34\x31\xf2\x17\xad\xb0\x34\x32\x7b\xd2\xa4\xae\x1b\xd8\x1d\x95\x18\x1b\x35\x6a\x14\x5d\x89\x7b\xa7\x4b\xb2\x9d\x6c\x49\x1b\x98\x30\x59\xc3\xae\xb7\x64\xfa\xe1\xce\x92\xe9\xce\x92\xe9\xbf\xc5\x92\x49\x2f\x84\xdb\x32\x67\x7a\x1a\x4d\xdf\x2c\xe7\xa7\xc0\x0a\x15\x77\x3e\x8d\xa6\x09\x24\xf6\x7f\x55\x9c\x7c\x59\x44\xb1\x69\x5f\xd3\x1f\x40\x1a\xff\x57\x82\x8d\xbd\x20\xe3\x34\x99\x44\x8e\x31\x90\x3c\x99\xa1\x5d\x01\xce\x2e\xb0\x2d\xc8\x81\x73\x5e\x9d\x13\xe0\xf7\x04\x1e\x6c\xb0\x73\x16\xe3\x5b\xda\x4a\x16\x96\x02\x9b\x1b\x50\xce\xdc\x67\x38\xe6\x90\x51\x4e\x12\x3a\x0d\x8a\xe8\x8c\xf6\x24\x27\x82\x8b\xa3\xe2\x3c\x6d\xe7\x64\x9c\xce\x17\x52\x5a\x85\x52\x6c\x6e\x55\xc9\x49\x9c\x06\x45\x94\x4c\xc9\x22\x8d\x92\xa2\xc7\xaf\x43\x19\xd9\x87\xe9\x79\x62\x9d\xe9\x4c\x35\x89\x7b\x7c\xfb\xc2\xb1\xfc\x45\xe1\xfb\x4a\x8e\x85\x2d\xa5\x84\xd2\x10\x4e\xd1\xa7\x7a\x8e\x43\xbf\x31\x0c\x20\xed\x4a\xd9\xf9\x98\xed\x1a\x0c\x18\xea\x97\x5c\x58\xb5\xdb\xe7\x73\xd1\x19\xf7\x9f\x7f\x78\xf9\xf1\xe9\xc1\xcf\x6f\x8e\x5e\x3f\x7d\xfe\xee\xe3\xbb\xc3\xa3\x37\xcf\x0e\xde\xfc\xfc\xf1\xf5\xe1\xb3\xe7\xe8\x0c\xa7\x34\x71\x30\x93\xfd\x45\x10\xbe\xa2\x93\xa2\xc3\xbf\x8a\xf4\xc3\x79\x9a\xef\x2b\x2c\x8a\x36\xfb\x45\x2a\xc4\xa5\xcd\x47\xdd\x1e\x79\xf4\xd0\xbc\xe1\xc1\xbb\x25\x0c\xa7\xc3\x1b\x31\x0d\x30\xcc\x89\x97\x87\xdf\x12\x9c\x3f\x55\x67\x63\xf3\xd0\xbc\x2a\x0e\x5d\xa9\xc3\xc0\xa2\x07\x21\x45\xfa\x92\x5e\xc8\x71\xe7\xcb\xd3\xbc\xc8\x3a\x5b\x08\x7f\xb1\x75\xb5\xcf\x8b\x4b\x2d\xf7\x06\x79\xb4\xdd\x25\x03\x8c\x22\x1b\xdd\xef\xa2\xe9\xac\x10\xc5\x7a\x24\x26\xf7\xbf\x32\x3e\xc5\x0e\x7c\xab\x68\x2d\x95\xe9\x6e\x8c\x5d\x79\x3c\x33\xd1\xaa\xb4\x73\xdf\x6c\x06\x2c\xb5\x29\x6f\xac\xdb\xe7\x6b\x7e\x83\xd4\x4f\x50\x1d\xa7\xe3\x92\x7c\xf9\x8a\x78\x2f\xf3\x6f\x3a\x77\xca\xb8\xb3\xf9\xac\x4d\xb2\x74\x7e\x54\x4c\x1e\xdf\x4d\x9c\x67\xe2\xc4\x3b\xa3\x32\x46\x26\x5e\x21\xc9\x49\x63\xdf\x34\x48\x56\x67\x64\xf6\x93\xa3\xf2\x39\x6b\x0f\x6f\xf6\xd7\x26\x1b\xa2\x7a\xf2\x84\x90\xf6\x66\x9b\x8c\x48\x7b\xd8\xbe\x39\x8f\xaa\xc3\x24\x3b\xb1\xb2\x52\xff\x60\x70\x39\x61\x82\xf1\x7c\x19\x17\x11\x17\x2a\x4f\x2f\xc9\xd6\x7f\xe6\x4c\x3c\x57\x36\x74\x01\xab\xb9\xa0\x53\x9a\x55\x6c\x25\xef\x44\xad\x75\xfb\xf7\xaa\x33\x22\x6c\x99\x4b\x66\x44\xa0\xc9\xa2\x3e\x86\x35\xd5\xa2\xda\x5c\xa3\x39\xcd\xad\xac\xad\x6e\x7f\x91\x9e\x77\x36\xb7\x1e\x77\xbb\x26\x4a\xf7\x67\x74\xfc\x89\x44\x13\x03\xa7\x48\x2c\xb2\x10\x91\x47\xd3\x84\x86\x07\xf9\x1b\x9d\xed\x28\xa2\x55\x1d\x33\x7a\x21\x7a\x6c\x22\x43\x12\x2d\x1c\xfa\xa0\xed\xc2\x94\xc4\x52\x76\x64\x39\x8f\x98\x18\x1e\xc4\xb9\xb6\x5a\xb6\x5b\xaf\xc5\x97\x0f\x43\x92\xdd\x0c\x7b\x64\xb3\xdb\x23\x9b\x8f\x90\x3c\xb2\xd5\x35\x72\xbb\x64\x77\x77\x97\x91\xac\x97\x0a\x33\xc6\x3e\x1e\x04\x31\x74\x8a\x70\xd5\x81\xbe\xf0\xe0\xa2\xa6\x4b\x44\x5c\x91\x60\x0b\x81\x06\x79\x38\x76\xb0\x0c\x67\x5a\x30\xac\x68\x57\x09\x87\xb0\x2c\xa2\x29\xe1\x72\xba\x45\x6f\xaa\x0b\x06\xfe\x0c\xa3\x58\x06\xcc\xe7\x71\x97\xf7\x06\xe9\x32\x3b\x5d\xf2\xe5\x0b\x69\x0d\x5b\x42\x47\x3c\x18\x90\xb1\xa2\x22\x26\x3c\xcb\x89\x54\xad\x73\xa0\xa8\xe0\x13\xad\x24\x6d\x57\xc8\x96\xf7\xb7\xd6\x3c\x8b\xb9\xf5\xa8\x20\x3d\xf3\xcb\xa7\x74\x1e\x25\x4b\x7b\x15\xb4\x27\x37\xfc\x6b\x43\xdd\xb2\xf2\x4d\x75\x3d\xd6\xa0\x43\xd7\xa0\xa0\x65\x35\x09\x1d\x55\xd2\x90\x8f\x7a\xe8\x4a\xe4\x23\x9a\x77\x09\xe7\xe8\x36\x28\xe7\xeb\xa0\x4c\xb0\xfc\x32\x94\x39\xbc\xbb\x16\x65\x80\x31\x24\x12\x9b\x28\x12\xcd\xb9\x28\x72\x98\xb9\xcf\xe2\xdc\x5a\x8c\x02\xa6\x1f\x46\x67\x51\x48\xc3\xa7\x97\x15\x3c\xfc\x3a\xd4\x54\x83\x9b\xa3\xdb\x46\xce\xb2\x14\x3b\x47\x2b\xa3\xe7\xe8\x26\xf8\x71\x6f\x61\x79\xd5\x0a\x45\x65\x12\x97\x7e\x30\xdd\x18\x2f\x72\x67\x33\xe7\xa2\x14\x47\xa2\x69\x17\x45\x8e\x7c\xe6\xc3\x90\x67\x79\xc1\x7e\x75\x43\x81\x6d\xb3\x4d\x9e\xf0\xad\x59\x78\xc6\x58\x0d\x9b\xa5\x27\x47\xf4\x2e\xb7\x62\xef\x8b\xe9\x44\x23\x8e\x49\x10\x15\x67\x1b\x47\xf4\x48\x82\x39\xe5\x0f\x7c\xd8\x2f\x4b\x04\x13\x30\xac\x4e\x55\x83\x07\xf3\xce\x21\x14\xda\xe8\x11\xac\x2c\x67\x85\xc4\x13\x6b\xb2\x4b\xca\x5e\xea\xde\xef\x0e\xd0\x91\x26\x8f\x7e\x13\x3c\x31\x87\x5b\x2a\x51\xfe\x78\xf3\xc4\x14\x85\xdb\xc3\x0b\x26\x32\xbb\x93\xdb\xcf\xe3\x68\x4c\x99\x64\xb2\x45\xee\x43\x75\x2b\xd2\x79\xcd\xcc\xe0\x53\xf8\xad\x4d\xd0\xaa\xe8\x2f\x55\x05\x38\x9b\x8c\x3a\x22\x5a\x7c\x80\x23\x4e\x5c\x82\xd9\x98\x7b\xf4\xb0\x2b\xf6\xf0\x22\x15\xf0\x5d\x72\x5f\x9e\x2a\x7d\x33\x60\x55\xc4\xa5\xc3\x47\x0f\x7b\xa2\xfd\xd5\xa6\xa0\xe2\x54\xce\x87\xef\x39\x96\xdf\x2a\xf6\x83\x7c\x1c\x45\x55\xf8\xf7\x1c\xe7\x7f\x47\xcc\x4b\xad\x0e\x68\x07\x9a\xe1\x7f\xb5\x09\xd0\xee\x69\xca\x66\x60\x4f\x3b\xb0\x29\x99\x82\x52\xde\x5e\x82\x72\x55\xa1\x8b\x6d\x9f\x03\x9b\x15\xa4\x29\x03\x77\xad\xe1\x45\x8b\x6c\x10\x71\xc6\x01\xb4\xf3\xdf\xca\xac\xe0\xe1\xb0\x47\x70\x52\x99\xcf\x80\xcf\xd2\xf4\x03\x9d\x35\x47\xd6\x77\xcf\x86\x81\x15\x3b\x72\x52\x1c\x38\xbc\xc0\x47\x65\x19\x4e\x29\x8e\xcc\x91\x9b\xe4\xf6\x23\x4d\xe3\x91\x9d\xe0\x40\x31\x09\x64\x64\x27\x60\x28\x25\x96\x8d\xec\x04\x17\xea\xc8\x01\x3b\xf2\xc2\xe1\x46\x75\x8a\xa7\x3e\x17\xf0\xc8\x0f\x89\x07\xab\x53\x3c\x70\x18\xdb\x28\xc9\x85\xf4\x4d\x8f\x9b\xe3\x96\x33\x27\x08\xa7\xb9\xb0\x82\xea\x47\xde\x75\x77\x25\xaf\x75\xcd\xcb\xa1\xd6\x68\xf3\x71\xaf\x65\x5e\x2a\xb5\x46\x5b\x60\xc1\x00\x0b\xa3\x35\xda\xdc\xec\xb5\xf0\xd5\x54\x6b\x64\x7e\x5e\x9d\xf4\x36\x87\xdf\xd8\xa5\xcb\x01\xb7\x8d\xaf\xf0\x41\x14\x25\x45\x99\x0b\x22\x71\x7b\x15\x25\x05\xf7\xce\xc2\x7e\x3c\x54\xbf\x4e\x74\xe2\x36\xfa\x6d\x39\x6f\x89\x92\x82\xbb\x6e\x89\x92\xe2\xd1\x43\x05\xf6\x58\x57\xb4\xf5\xdd\xa3\x92\xba\x18\x7c\x8d\x2b\x23\xfb\x68\xf8\x15\xbd\x71\x01\xb8\x6d\x86\x70\x90\x14\x2b\x5a\x5e\x18\x25\x2a\x0c\x2e\xa0\xb9\x8a\x92\xd7\x32\xaf\x88\x92\x42\x8a\x8a\x4f\xae\xe5\xd2\x85\xf7\xaa\xde\x0c\x62\xb3\x51\x14\xbb\x3b\x3b\x88\x3b\x3b\x88\x3f\xaf\x1d\x04\xd1\x86\x10\x5c\x54\xba\x25\x1b\x88\x06\xa6\x0d\x36\xab\xe7\xa6\x0b\x29\x18\xa4\x6b\xcf\x1d\x7d\x8f\x84\x7a\x3e\xa3\x89\x7a\xaf\xd8\xe3\xb6\xdf\x4c\x00\x57\x0e\x1c\xa4\x64\x39\xf0\xda\x46\x58\xea\x6f\xfb\x79\x22\x70\x52\x29\x3f\xf2\xff\xbf\x7c\x21\xed\x36\xe2\xb3\xa9\x7c\xb9\xc0\x7f\xec\xa0\xa7\x86\x51\x22\x5a\x6f\xec\xf1\x63\x4a\x0b\x6c\xf2\x0b\x06\xe4\xed\x5c\x3e\x04\x05\x5e\xc2\x2a\x31\xac\xdd\xb5\x7c\xcf\x8d\x5d\x4d\x29\x5a\xaa\x99\x74\xad\xb8\x32\xd2\x91\x7d\xec\x1a\x06\xed\x80\x1e\x6c\xd0\x6e\x37\x52\x69\x8a\x06\x56\xfe\xc6\xb1\x03\x5f\x3f\x36\x46\xc6\x38\xa3\x8c\x98\xe4\x7a\x30\xdd\xb2\x70\x72\x0f\xa3\xc9\x84\x82\x41\x32\x47\xb9\x75\x2e\x39\x57\xef\x42\xf0\x71\x44\xa2\x44\xcc\x92\xb4\x5d\x4e\xbc\x87\x10\xf3\xe8\xc2\xb6\x43\x5f\x3f\x82\x05\xe7\x30\xaa\x17\xe5\xa8\x3c\xf7\xbf\x99\x35\xe9\xae\xf4\x56\x4f\x13\xa4\x22\xd5\x55\x30\x9a\xce\x4f\xa3\xc4\xf5\x70\x53\xa4\x53\xca\xb8\x3b\xab\x81\x4e\xfb\x7c\x51\x05\x8b\x05\x4d\x60\x2d\x05\x09\x7f\x03\x61\x61\x57\xd4\x56\x77\x0f\x23\x18\xd3\x2c\x1a\x33\xf6\x24\x7b\x55\x5f\x58\x5c\xa0\xa6\x13\x01\x0b\xfb\x50\x25\x6a\xe5\xf0\xea\xf4\x7e\x55\x68\x55\x7a\x0b\x7e\x65\xb2\x43\xea\xb1\x3b\x0e\xe2\x58\xe0\x57\x5e\xe3\xf0\x11\xcd\x02\xbd\x74\xf3\xe8\x37\xe1\x5c\x10\xae\xeb\x66\x41\xde\x63\xff\x4b\x42\x03\xf7\xbf\x9e\x7b\x3b\x8c\x6f\x65\x0b\xea\xd7\x99\x56\xa2\xc6\xef\x9d\xc9\xb7\x70\xc5\xaa\x58\xdf\xdd\x05\xe9\x62\x12\x25\xd6\x5b\xa5\x3a\x24\x68\xaf\x45\xa2\x2a\x71\xc3\x6c\x2b\x0d\x78\xee\x5e\xfe\xb4\xfc\xe8\xcf\x35\xbe\xae\x86\xa6\xc1\x32\x33\x6a\xaf\x1a\xf4\x3a\x8c\x5a\xbb\x00\xe8\x92\x27\xa4\xdd\x26\xa3\x66\x06\x59\x08\x65\x5e\xb3\xac\x15\xf0\xc6\x78\x3f\x57\x4e\x28\x99\xd1\xf7\xdc\x4b\xeb\x2f\xfc\x38\x93\x7b\x8f\xbc\x15\x0e\x30\xc3\x0f\xe6\x98\xc8\x80\xc4\x2b\xb1\xa8\x1b\xf3\xa2\x10\xfc\x2a\xd9\xf8\xf3\xf9\x67\x52\xcb\x6b\x87\xf0\x2b\x3f\x52\x42\x77\x62\xc2\x3a\xab\xa3\xce\xd8\xd6\x4a\x70\x87\x36\x25\x3f\xf2\x64\x42\x20\x2f\xe1\x1b\x60\x91\xce\x17\xc5\x25\x56\x09\x36\xd8\x44\x6b\x57\xa1\x49\x8f\x88\x3d\x8d\x40\xfa\x58\x01\x37\xd2\xe3\x54\xa9\xaf\x29\x2f\x26\x2a\x07\x22\xaa\xac\x1b\x83\x71\xb1\xb2\xe1\x11\x0b\xae\x33\x0e\xfd\x18\xaf\xdc\x3f\xd4\xab\x28\x2f\x9c\x97\x7f\xc7\xc6\x68\x4e\x3c\x4e\xa1\x2a\x47\xaf\x6b\x76\xb7\x17\xf5\x2e\x48\xde\xd4\x2f\x17\x21\xb7\x6c\x15\xef\xe0\x94\x2a\xb2\x48\x0b\xf4\xd6\x95\x17\x96\xc2\x11\xf7\x3b\x44\x8c\xb7\x7d\xea\x09\xa1\x00\x35\x9f\x15\x19\x7b\x9b\x5a\x8f\x7c\xfb\x2a\x59\x90\xf6\xed\x97\xed\x2c\xc4\x6c\x9e\xec\xe2\x1e\x6b\x58\x3c\x8c\x8d\x5d\x57\xd1\x2f\x5e\x6b\xb9\x2f\xb4\x38\xa4\x16\x81\x3a\x29\x7e\x75\xab\x5e\xcd\x0d\x06\x72\xba\xe9\x19\xcd\x2e\x8b\x19\xf8\x22\x41\xf5\x60\xec\xb8\x8e\xa7\xa4\x45\x9a\x83\x1f\xe3\xa5\xae\xff\x86\x42\xf9\x5e\xba\xd5\x26\x5c\xa5\xf3\x55\x8f\xb4\xdb\x52\xf9\x5e\xa1\xa4\x78\xcb\x67\xc9\xd2\xe9\x29\xf5\xdd\xd5\x49\x6f\xb3\x51\xac\xbd\xaf\xa8\x93\x83\xdb\xe8\x6a\xa5\x5c\xc6\x40\x4a\xb4\x72\xd2\xcc\x8c\xfd\xcf\x55\x65\xf0\xeb\xa1\xfe\x79\x82\x92\xb7\xf1\x87\xa5\x9b\x63\x69\x5c\x39\xc7\x7e\x49\xed\x1c\xfb\xfd\x18\x55\x87\xf4\x73\x4e\x8d\x0d\x34\x74\xce\xdd\xfb\x2a\x2a\x3a\x56\x78\x15\x1d\x1d\x87\xb7\x95\x74\x2c\x75\x45\x2d\x9d\x59\xa4\x42\x4d\xc7\x5b\xac\x2a\x7b\x1d\x45\x1d\xc3\x6d\x89\xa2\xae\x99\xa3\x7c\xd1\xad\x06\x8a\xba\x46\xd1\xbc\xbe\xd6\xe3\x3a\xcf\xed\xdf\x2a\xe4\xc1\x8b\xaf\x42\x20\xb2\x84\x4d\x22\x3c\x7d\x45\x22\xb1\x0b\x55\x90\x89\x6c\xb7\xba\xfc\xb5\x74\xba\x5c\x92\x6a\xf2\x66\xce\xd3\xde\xed\xbe\x96\x53\xa3\x6c\x40\x77\xb7\x1f\x7d\xa4\xf2\xfd\x8e\x87\x0f\x23\x17\xb7\x51\xde\xdc\xb7\xed\x98\x66\x45\x10\x25\x7e\xff\xb6\x0e\x22\xf9\x6d\x52\x0d\x51\x73\xa0\xbe\x99\x5e\x4d\xd6\xa2\x88\x95\x51\xeb\x0d\xa2\xa0\xd9\x9c\x1d\xf9\xa3\x09\xd4\x6c\xf6\x3b\x14\x5e\x6b\xc9\x34\x3a\xa3\x89\x34\x69\x31\x8f\xd4\x65\xee\x72\x2d\xfb\x17\x7e\xcc\xd6\x16\xb7\x80\x65\x5e\xb9\xd3\xae\xdf\xfe\x16\x43\x34\x5f\x22\xdc\x39\x6d\xab\xf0\x0a\xc7\xe9\x19\xcd\xb2\xf3\x2c\x2a\x0a\x0a\xe6\x5e\xbc\x57\x2d\xb2\x01\xbd\x6f\x8c\xbb\x73\xd0\xb2\xe7\xf8\x21\x3f\x58\x41\xe8\xa3\x68\x94\x08\x14\x16\xae\xdf\x61\xfb\xad\x7d\x23\x64\xba\x5a\x49\xab\x39\xad\xb5\x2d\xc1\x9b\xc7\x85\x80\x1f\x83\x83\x01\xa8\xc2\x83\x39\x5b\x15\xe0\xf5\x50\x68\xb3\xd8\x78\x19\x27\xa0\xfc\x8e\x21\x8e\x3e\x51\x12\x90\x3c\x4a\xa6\x31\x55\x7e\xb8\x00\xb2\x6f\x98\x44\x03\x05\x73\x37\x33\xdc\x2d\x07\x6f\xed\xcb\x17\x72\xdc\x3e\xde\x3c\x69\x9f\x74\x95\x30\x58\xe3\x06\x40\x74\xcf\xc4\x3b\xfb\xc2\xae\x0d\x4b\x44\x77\x6e\x03\xc5\x51\x01\xb6\x0a\x9b\x3d\xf2\x00\xec\xb1\x87\xd0\x97\x4d\xec\x88\x46\x77\xc8\x11\x64\xa5\xa3\x86\x9e\x74\xed\x50\x76\x5a\x90\x0e\x1d\xee\x4b\x40\xdd\xc0\x60\x40\x82\x38\x26\xa7\x41\x1e\x8d\xb9\xff\x03\x78\x2c\xb0\xbd\x25\x14\x38\x71\xca\x4e\xc6\xb2\x37\x3d\xb2\xbd\x55\x67\x74\x62\x2e\x6c\xc1\xd1\xe4\x09\x5c\xea\x22\x09\x9d\x82\x00\x09\x41\xa1\x8e\x4f\x5a\x64\xf7\x27\x58\x9f\x3a\xed\x21\x4f\xac\x54\xa6\xed\xc9\xda\x56\xe5\x00\x33\x5a\xda\xb3\x8a\xd5\x8e\x5b\x2d\xa5\x59\xed\xf6\xcb\x70\x08\xe3\x10\xdd\x8e\xb5\x8d\xa2\x22\xf7\xee\x11\xfc\x7d\x8c\x7e\x23\x17\x70\x27\x72\xd7\x55\x91\x31\x06\xd3\x6b\xcd\x8d\x58\xbe\x55\x53\x23\x67\xc1\x9c\x1b\x31\x61\xe6\xd4\x20\x8f\x6b\x37\x9c\x19\xab\x5f\x15\x13\x83\xda\xfc\xda\xf3\x72\x9b\x13\x63\xba\x3e\xd1\x8c\x14\xcd\x04\x9c\x8d\x5a\x60\x8b\xb0\xc5\x91\xce\x0f\x49\x2d\x61\xac\xb0\x29\xa6\x62\xf3\xa1\x02\xdc\x3a\x39\xde\x16\xa0\x32\x8d\x83\x28\x88\xcd\x13\x2b\x41\x7f\xbb\xbb\x03\x60\xf5\x1a\xdb\x03\x1e\x8b\x18\x62\xfd\x9e\x80\x1a\xbb\xa5\x89\x8c\x26\xa4\x83\xb2\x10\x87\xb4\xf9\xf1\x35\x27\x16\x18\xb6\xef\x35\xc4\x66\xc5\x94\x8b\x4d\x42\x9e\xaa\x7d\xf3\x0c\xf3\xe6\x9b\xea\x96\x8a\xbf\xe7\x4c\xb8\xf8\x6c\x19\xf3\x6e\x54\x74\x6c\x56\x8e\xa7\x5b\x7b\x5f\x6b\x34\xcf\x2a\x83\x0f\x45\xe4\x97\xce\xaf\xe1\x45\xb1\x74\xb7\x17\xde\x8a\xe2\x20\x2f\xc8\xf1\x09\x13\x26\x78\xbd\xd7\x9a\xf6\x75\xff\xbc\xab\x39\x00\x39\x8b\x38\x3e\x96\xe0\x40\xa3\x5f\x42\xc1\xa7\xa2\x81\x26\x44\x52\x61\x1c\x8b\x8e\x30\x8a\x03\xdb\x37\x4d\xe4\xf4\x92\x84\x74\x12\x2c\x63\x50\x84\xe6\x4b\x26\xa7\xaa\x8d\xb9\x25\xdc\xd4\xf4\x44\x98\x47\x7b\x16\x8d\x63\xd4\x35\x18\xb0\xde\x11\x57\x14\x85\x1b\x9e\xde\x4a\x8d\xea\xa5\xaf\x76\xa9\x23\x46\x4b\x24\xb7\xd7\x08\x50\xbc\x20\xe5\xe3\x16\xa3\xf8\x1e\x69\xb1\x45\xc0\xfe\x3b\x69\x9d\x68\x6a\x17\x10\x28\x0d\x0a\x25\xcb\xd8\x7e\xf6\x80\x66\xb3\x11\xda\x6c\x07\x73\x56\x7f\x6b\x16\x82\xeb\xa4\xca\x59\x09\x7c\x6f\x10\xce\xf2\xf8\xac\xe7\x70\xc3\xcb\x86\x63\x8c\x97\xfd\x0b\xab\xde\x22\x62\xc1\xad\x3a\xff\x3e\xe6\xa7\xf1\x7f\x9f\x74\xeb\x45\x04\xa1\xbc\x55\xde\x1e\xca\xef\x1d\xac\x30\x16\x12\xba\x39\xeb\x90\x6f\x4f\xdd\xbb\x2c\x0b\x67\x9e\x4b\x0b\x71\x8f\x6e\x6f\x0c\x5e\x7f\xd4\xe6\xad\x8c\x70\x85\x2a\x9d\xa0\xda\x6c\xa1\xc6\x1b\xac\xb2\xff\xc6\xc6\xc4\x3b\xa4\xf4\xcf\xef\x18\xd5\xf5\x2b\x4b\xe3\x09\xf6\x27\x2b\x58\x99\x53\x48\xbd\x4c\x3e\x3e\xf1\x39\x11\xef\x2f\x96\xf9\xac\xe3\x78\x26\x95\x2f\xb5\xa5\x9b\x51\xb7\x66\x36\x16\xd7\xe7\xfa\x99\xcf\x01\x28\x6e\x09\xf9\xf1\xec\x9c\xf5\x08\xf6\x2f\x6b\xb9\x27\xbd\x91\x53\x5f\x31\x81\xd8\x99\xef\x8d\xe7\x0f\xba\xee\x48\x1d\x02\xf1\xbf\xff\xfc\xf9\x3c\xb2\xd6\x78\x62\x2d\x9d\x08\x36\x9b\xe0\x2a\xb5\x62\x3e\x56\x9e\x8d\x35\xe7\x8e\xd0\xd2\x1d\x19\x4b\x12\x79\xb4\x6d\xe2\x13\x94\xdf\x8f\x4e\xb2\x74\xee\x35\x37\xe0\x50\x3e\xde\x72\x6a\x3f\xd8\xb1\x0c\x84\x0c\xcb\xa0\x15\x1e\x4c\x49\xa6\xc6\x5b\x6e\xc0\xa2\xc4\x40\x30\x8b\x32\xfc\x69\xd6\xb0\xaa\xaf\xc2\xab\x60\x6f\xc2\x37\x96\x5c\xd0\x15\x4f\x7c\xa0\x7b\x52\xd0\x11\xe8\xba\x4f\xb6\xc0\xf8\xa1\x2b\x3d\x3a\x0b\xe4\x95\x2d\xa2\xca\x3a\x71\xf3\x4e\xc5\xbe\x15\x05\x05\xde\x17\xfc\x8e\x1d\x97\xde\x20\xdb\xdc\xe9\x3d\xdf\x6d\x73\x06\x92\x93\x60\x52\xd0\x4c\x2d\x12\xdc\xdf\x6b\xad\x55\x7f\x19\x9f\xef\x6e\xcd\x39\x4a\x7c\x76\x93\x4a\xec\x89\xd0\x31\x6f\xca\xea\xc7\x7e\x3d\x4a\xdd\x48\xdb\x31\x6f\x2a\x19\x4d\x43\x4e\x43\xee\x57\xf7\x8d\xc1\x6e\xec\x56\xc3\x34\x62\x54\xa6\xc3\x59\x34\xed\x1b\x24\xba\x5d\xae\xf5\x87\xd8\x43\xf0\x5f\x43\xea\x97\x06\xa9\x0d\xff\xfe\x50\xc4\x7f\x47\xfb\xe8\xef\x9b\xd0\x3e\xf1\x92\x3e\x0e\xd0\x78\x5d\xd2\xb7\xc3\x88\xad\xb8\xa9\x38\xc4\x6a\xd7\xdf\x6c\x67\x31\x7b\xb1\x4a\xfd\x62\xfe\xbc\xf4\x16\x3b\xf4\xe5\x5f\x7f\xe5\x4b\x78\x21\x6e\xfd\x5c\x23\xd5\xba\xee\x77\xc8\x26\xd9\x30\x7b\xd7\xe5\x3e\x99\x78\x24\x31\xcf\xd4\x73\x0f\xc4\xd6\xa5\x9b\xf1\x60\xbb\xc2\x9f\xbd\x81\x6b\xcb\xe2\xcb\xe0\x62\x6b\x2b\x8e\x0d\xcf\xb9\x5a\x59\x5b\x5d\x53\xad\xea\xbd\x48\xb4\xba\x5e\x7b\xc1\x5b\x7e\xb5\xab\xde\xc4\x5d\x9d\xf4\x36\xbf\x75\xe8\xfd\xa3\xfa\x67\x6f\xcb\x8a\x77\x6f\xc2\x13\x09\xfc\xcf\x6d\x5d\x96\xfa\xe9\xdb\x12\xbd\x7d\x5b\xe2\x07\x6b\x4b\xcf\xeb\xb7\xa5\x7a\xfe\xb6\x44\xef\xdf\x96\xe8\x01\xdc\xd2\x7c\x01\xe7\xd4\xd8\xc0\xc2\xc6\xf1\x8f\xf2\x15\x1f\xc1\x1d\x79\x5f\xc1\x1d\xad\xfe\x0c\xee\xa8\xe9\x3b\xb8\x23\xf7\x21\xdc\xd1\x2d\xbc\x84\x5b\xde\xf8\x29\xdc\x51\xe3\xb7\x70\xdf\x3a\xae\xff\x51\x03\x8b\xb3\x65\x95\xc9\x99\x74\xad\xc2\x7f\x08\xe2\x44\x56\x67\x4b\x6c\x76\xb6\x34\xac\xc4\x96\x3e\xc3\xb3\xa5\xb6\x3c\x5b\x62\xd3\xb3\x25\xb6\x3d\x5b\x5a\xc6\x67\x9e\x7a\x9b\x2c\x8e\xdf\xd5\xfe\xec\xc8\x6f\x80\x76\x74\x0d\x0b\xb4\xa3\xc6\x26\x68\x47\x1e\x1b\x34\xbb\xf4\xf5\xd6\x48\x85\x19\x5a\xd3\x45\xd2\xdc\x10\xed\xfb\x26\xab\xa4\xbd\xcc\x29\x28\x66\xc7\x45\x9b\x07\xe4\x9b\xa6\x84\x26\x67\x24\x4c\x29\x58\x2b\xc0\xeb\xc0\x20\x09\xc1\x87\x2d\xf9\xe7\xeb\x57\x2f\x8b\x62\xf1\x8e\xfe\xbf\x25\xcd\x8b\x35\x10\xcc\x2e\x17\x34\x9d\x58\x39\xdc\x8f\x8d\x7a\xbf\xd1\x96\x78\x11\x0d\xf7\x6d\x68\xf2\xf9\x6a\x67\xcd\x08\x16\x59\x0a\x69\x26\x80\xa4\xfe\x6b\x3e\x63\xbb\x4f\x34\x4d\xd2\x8c\x8e\xe2\x28\xa1\x6b\x57\xdc\x62\x95\xe1\xa1\x91\xb7\xfb\xbb\x97\xb3\x77\x2f\x67\xff\xc4\x2f\x67\xf9\xab\x59\x61\xc3\x66\x3c\x9b\xe5\x1b\x0e\xb9\xde\xeb\x59\xb1\xf7\x1d\x15\x51\x0c\x75\x72\x7d\x26\xac\x1d\xfe\x3c\xc9\x01\x8b\x8a\x4b\xc5\x12\x75\x91\x71\x1c\xe4\x39\x39\x86\x22\x27\xa2\x9b\x3c\x43\x33\x61\x5e\xd5\xda\x00\xee\x8d\x60\x95\x0a\xe5\x2a\xe3\x20\xa4\xc2\x99\x75\x73\x3f\xe7\x00\xc9\x6a\x3a\x7a\x73\xf0\xe1\x3d\x3b\x5b\xc3\x24\xb4\xcf\x69\xd4\xe6\xa4\xd9\xfe\x84\x7e\xbf\x46\xbf\x7f\x46\xbf\xf3\xdf\x82\xd3\x54\x7e\x4c\xa2\x24\xa1\x97\xea\x8b\xce\x8b\x14\x9e\x32\xca\x94\x45\x34\x36\x13\x92\x20\x31\x13\xe6\xd1\x38\xb3\x53\xe2\x38\x72\x0a\x19\xf0\x06\xa8\xfc\x30\x8a\x4c\xb3\x20\x09\xd5\x50\x8c\xac\x9f\x8d\xaf\x0f\xc6\xd7\x5b\xe3\xeb\xb9\xf1\xf5\x7f\xc6\xd7\xbf\x8c\xaf\x37\xc6\xd7\x33\xe3\xeb\x1f\xc6\xd7\x11\xff\x5a\x3b\x29\x77\x5d\xc3\xe6\xe8\xed\xde\x33\x36\xc5\x23\xb2\xbd\xd5\x53\x89\xef\x0f\x7e\x7e\xb3\xf7\xe1\xe8\xdd\xf3\x8f\xaf\x9e\xbf\xf9\xf9\xc3\xcb\x11\x79\xa8\x33\x61\x56\x47\xfa\xa7\xce\x29\xa1\x9c\x11\xf9\x4c\xac\x04\xed\x47\x1d\x32\x3e\x3e\x3b\xfc\xe5\x0d\xb9\xd2\x35\xbd\x3d\x7c\xf5\x8a\x41\x7f\x38\x78\xfd\xfc\xf0\xe8\xc3\x88\x6c\x0e\x87\xc3\x81\xe8\xa1\xb8\xf1\x7e\x1a\xa7\xe3\x4f\x23\xd2\x66\xac\x33\x2f\xda\x46\xde\xde\x18\x42\x19\x8f\xf4\xdb\x46\xfe\x00\x83\xed\xe7\x75\xbe\x4f\xee\x42\x61\xdc\x6d\x64\x7f\xf5\x8d\x6c\x4d\xb9\x80\xc8\x67\xc1\xf6\x6d\x79\x80\xd8\xcf\x2e\x17\x45\xfa\xf7\xf7\x78\x73\x18\x43\xda\x03\x1d\x01\x83\x35\xe8\x05\x18\xb0\x9c\xb6\x37\xba\x93\xeb\xbe\x01\x28\x2e\xc7\x0f\x54\x45\x12\xb9\x77\x4f\xe6\xf6\xa5\xbf\x08\x2e\x26\xcf\xe8\x45\xdb\x7e\x45\x67\x78\xfe\xfa\x89\x6c\xb1\xd2\xb6\xf7\xe3\x2d\xe9\x2e\xd2\x2c\x4e\xe4\x65\xb8\xba\xe0\xb7\xfc\xb3\x13\xeb\xb5\x1d\x07\x95\x38\x62\x9d\xeb\xbf\xa4\x17\x7d\xd0\x5e\x0a\xcf\xbd\x3e\x1b\x23\x86\x15\x39\x6c\xdd\x3a\x3f\xd1\x71\xf5\xdb\x88\x6c\x7d\xf7\x88\x97\x44\x8f\x93\xe5\x9b\x33\xc6\xf2\x14\x8e\x5b\xa3\xef\x7e\xe8\xb5\x4c\x94\xb7\x46\x8f\x87\x57\x27\xbd\xad\x46\x3e\x9f\xee\xf8\xde\x1d\xdf\xfb\xf3\xf2\x3d\xcd\xf6\xf8\x3b\xff\x5b\xe0\x7b\x96\xec\xbe\xba\xe8\xee\x91\xdc\x65\x41\x9f\xe0\xbe\x52\xb4\x21\x9b\xd7\xf6\x07\x82\xdd\xeb\x70\x44\x93\xc7\x18\x80\x7d\x2b\x11\x7e\x99\x44\xc5\xeb\x60\xa1\xc4\xc5\xb6\x94\xa8\x47\x9c\x07\xb5\x87\x52\xd6\x64\x52\xfb\x48\xb3\xc5\xf6\xa6\x21\xe7\x8f\x50\xc6\x70\xa8\x0a\xfd\x6f\x45\xde\x69\x70\x7a\x1a\x4c\xa9\x6a\x09\xe7\x21\xe1\x7f\x64\xe7\xcd\x3d\x75\xa2\xec\xd7\xd5\xd9\x71\x7a\x46\xe3\x60\x2c\x9b\xb5\xb3\xf5\x19\x63\xe4\xcb\x9e\xfa\x2b\x47\x10\x3f\xd7\x42\xe4\xb3\x20\x49\xd2\xc4\x18\xb7\x09\xa1\xcf\x35\xa3\x0a\x88\x9a\x56\xe0\x64\x35\xf2\x40\x60\x54\xea\xf3\xd2\xa8\x1a\xa8\xae\x26\x71\x76\x1b\x79\x81\x8c\xca\xd4\x79\xcc\x1e\x9b\x07\xd0\x3f\x44\x13\xd0\x20\x57\x0f\x1c\x02\xfd\x64\xc2\xfa\x40\xf1\x5c\xc3\xa9\xaf\xb2\x62\xdc\xdf\x46\x75\xe3\xea\x9b\x16\x40\x65\x8a\x15\xca\xb0\x62\x7e\x63\x2b\xed\x88\x61\x11\x84\xc2\x94\x14\x4c\x3d\x2f\x16\x74\xcc\x36\x2f\x65\x9e\x8f\x8d\xae\x84\xf7\x14\x9f\xe5\x94\xae\xe2\x94\x32\xb8\x50\x44\xe4\xb2\x6c\xb0\xc6\xb3\x20\x0b\xc6\x05\xcd\x72\xa9\xe2\x87\x7b\x79\x51\x1a\xed\x23\xde\x36\xa2\x69\xd2\x43\xb6\xd0\x64\xb8\xe6\x77\xfb\x11\x4d\x67\x05\x91\x1e\x69\x2d\xef\xbe\x62\x0c\x86\xb4\xc9\x41\x7a\xd0\xbb\xbc\x07\xed\x78\x7c\x0c\x71\x0b\x11\x80\x81\xa0\xb4\xf0\x5a\x55\xdd\x10\x6f\x76\xfb\xbf\xa6\x51\x02\xc1\x1a\xc8\x13\xa8\x83\x8c\x48\x6b\xd8\xea\x92\x0d\x01\x5c\x62\xf8\x76\xed\xb9\x80\x80\x3d\x7f\xf6\xc9\x80\x41\xac\x38\x1b\xa2\x87\x1b\xdc\xe3\xf2\x75\xe7\xa5\xcc\x10\xd1\x74\x44\x03\x5b\x27\x98\x21\x42\x30\x0f\xd7\xc7\xb4\x35\x2f\xdc\x5b\x73\xc5\xac\x44\x09\xab\xc4\x8f\x2c\xec\x8f\xda\xe3\x28\x89\x35\xae\xcd\x0e\xb9\x07\x92\x23\xbe\xb5\x2b\x91\x7e\xc6\xe3\x3d\x0f\x06\xe4\x45\x94\x84\x84\x3f\xee\x12\x1d\x55\xf1\x9a\x99\x44\xd1\x6a\xe9\x9b\x7c\xb0\x7d\xe9\x41\x08\xa9\x19\xbd\x90\x26\xcc\xea\xcc\xc5\xd2\xf8\xa9\x87\x9d\x38\xca\xcf\x4a\xac\x9a\x2d\xfc\xee\x05\x8c\x6b\x84\x4d\xcd\x0e\x89\x36\x76\xb7\x30\xb8\x8c\x85\x8c\x6d\x3b\x74\x53\x9d\x88\xb5\x23\x42\x5f\xa8\x16\x26\xa4\xc3\x8b\xec\xee\x92\x61\xd7\x38\xa5\x9d\x66\x34\xf8\xa4\x41\xd9\x28\x37\x76\x89\x78\x55\xce\x66\x70\x7f\x16\x64\xfb\x69\x48\xa1\x06\xef\x21\x8c\x4d\xb6\x34\xc7\xc9\x8b\xac\x19\x85\xf0\x49\x5b\x89\x44\xf6\x58\x91\xdf\x8f\x46\xa0\xb9\xff\x1e\x22\xb9\xce\xcc\xe7\x45\xd9\xeb\x74\x73\xb2\x3d\x3e\xe6\x3b\x8b\x8c\x4e\xa2\x0b\x1e\x44\x6b\x78\xd1\x65\xb3\x00\x5c\xc3\xef\xde\x5e\x44\x7b\x2b\x9f\x7d\xaf\xed\x32\x1c\x41\x83\x18\xb8\x79\x65\x30\x01\x5f\x94\x4f\xc3\xd7\xbe\x70\xbb\x2e\xba\x81\xa9\x82\x51\xbc\xc0\x3c\x9f\x7d\x58\x0e\xc2\x6c\x9b\x2f\x07\x39\x23\xac\x25\x4d\x1d\x93\x34\xb3\x4d\xe8\xf2\x22\x2b\x8b\x88\x8f\x66\x94\x41\x8d\xc5\xdc\xec\x15\x9d\xe8\x7a\x2b\x1d\xac\x13\x45\x70\x70\xc3\x6b\x9b\x06\x61\xfd\xdd\xd8\x25\x89\xdc\x17\x7e\x24\x5b\xe4\x09\x3b\xd9\x90\x0d\xc2\xf6\x83\xc4\x47\x13\xc2\x85\xfc\x8c\x5e\xdc\x26\x69\x58\x31\x07\x6c\xda\xa8\x61\x0d\xbf\x1b\x71\x38\x3c\x03\x51\xc7\xef\x43\x01\xdf\x6c\x5a\x2d\x8f\xa5\x93\x65\x1c\x2b\x34\x0c\xe8\x19\x4d\x0a\xfe\x50\x00\x58\xfe\xaf\x79\x9a\x90\xe0\x34\xb2\x79\xbc\x74\x9b\xf8\x21\x7d\xb1\x8c\x63\xfb\x0d\xa5\x7c\x4c\xc0\x4a\x3f\xe0\xa5\xdd\xc7\x50\xbc\x61\xa7\x5d\xcd\xd8\xdd\x36\x0c\x41\x8a\x55\x8e\x55\xa7\xec\xbb\x0f\x26\x14\x51\x12\xd2\x8b\xc3\x49\xa7\xdd\x69\x77\xc1\x37\xe4\x83\x4d\xcf\x73\x48\x05\xef\xd8\x09\x16\x97\x0b\x2a\x9a\x03\x20\xa0\x22\xd3\x9f\x59\x27\xea\x7e\x96\x21\x84\xfb\x0c\x7e\x87\x5c\x09\x51\xcc\xb4\xfc\x53\xad\x90\x0d\xd2\xee\xb0\x99\x53\xb5\x6f\x90\x76\xb7\xdd\x68\xed\x85\x51\xbe\x88\x83\x4b\x3e\x2f\xe0\x63\x34\x29\x98\x6c\xab\xb0\x61\xbf\x59\xbb\x80\xec\x67\xbc\x58\xd5\x0b\x57\x56\x9b\x39\xf9\xfe\xe5\x65\xf4\x80\x6d\x69\x16\xc5\xd0\x69\x5f\xc6\x5b\xbc\xec\x08\xb3\xba\x2e\x79\xf0\x93\x4a\x54\xd3\xea\xf6\xad\xf2\xe1\xb3\xb2\xd9\x74\x66\xd6\x40\xb3\x00\xe3\x93\x4d\x9e\xd8\x6f\x5a\xc5\x7b\x30\xb6\x66\xb4\xb3\x91\xc1\x40\x0f\x34\x3d\xa3\x59\x9c\x06\x21\x0d\x95\x22\xd8\xb3\x26\xf0\x00\x3e\x68\x22\x29\x7b\xd3\x38\x20\x1f\x0e\x9f\x1d\x8e\xc8\x3c\xf8\x04\xaa\xe1\x28\x39\x5b\xc6\x09\xcd\x82\xd3\x98\xde\xe6\x00\xf5\x69\xc0\x7e\xbd\xbb\x49\x1e\x10\x94\xdd\xed\xf6\x33\xba\x88\x83\x31\xed\xb4\x49\x1b\x9c\xba\xb1\xd3\x42\xcb\x0c\x12\x99\x26\x67\x34\x2b\x72\x1d\x72\x13\xe4\xbe\x90\x8e\xa3\x79\x10\xdb\x4c\x36\x4a\xfc\xcc\xbe\x48\x9f\xf1\x02\x2e\xe5\x55\x86\xcf\x34\xdd\x1a\x72\x01\x4f\xd4\x54\x1b\x00\xb2\x48\xdd\xf8\x98\x2a\xfc\x4c\x93\x31\xd6\xca\xb6\x8c\x27\xde\xd6\xb8\x50\x5d\xd5\xc1\x59\x13\xa9\x25\x75\xc7\xe7\x09\xcd\x2d\xd4\xa7\xe6\x8e\x62\x1c\xf6\x39\x40\x4c\xf3\xfc\xc3\x2c\x48\x3a\x43\x70\x22\xfb\x80\x5b\x9d\x0b\xeb\x7d\x41\x58\x9b\x5d\x08\xdf\x8a\x72\x0c\x2c\xee\x2d\xc1\x4d\xb3\x40\x65\x90\x5c\x0a\xc7\x3b\xc2\x1d\x69\x52\x8e\xd6\xbe\xc0\xeb\x5e\x12\x72\xf5\x3f\xa7\xa1\x68\x72\x99\x0b\x47\xea\x39\x39\xa5\x93\x34\xa3\x7d\x87\xae\x5e\x8a\xa3\x43\x35\xee\xbf\x88\x3d\xa8\x86\xb4\x5e\xc2\x3e\x6f\x20\x5f\xad\xdf\xfb\xc2\x54\x6c\x1e\x5c\xf0\xb0\x95\x17\x51\x71\x39\x22\x8f\x41\x85\x2d\x77\x9d\x28\x17\x2e\x8d\xa1\x68\xd7\xde\x64\xd0\x24\x77\x36\x18\xc4\x8e\x51\x14\x4f\x67\x75\x61\xab\xac\x30\xa4\x3b\x63\xb4\xc3\x4e\x21\x1c\x69\x6d\x6f\x15\x10\x5f\xe9\xef\xef\x0f\xdf\xf4\x15\x96\x79\x7b\xda\x81\x25\xb8\x8e\xcd\x49\x60\x47\xf3\xec\x91\x45\x90\xe7\x8c\x77\x15\xb3\x2c\x5d\x4e\x67\xe6\x0a\x50\x03\x11\xb4\x06\xb5\xba\x97\x93\x9a\xab\x3d\x80\xd3\x92\x47\xe6\x2d\x1d\xb1\x04\x10\x6f\x3b\xcc\xea\x6a\x6a\x3b\x93\xf6\xa3\xa8\x02\xd2\x59\x8f\xf2\x17\x51\x12\x15\xd4\x42\xba\xd5\x0d\x90\x10\x51\x27\x4c\x29\xcb\xed\x28\x5a\x17\xef\xc4\xa6\xc2\xd7\x01\x3b\x2f\x25\xc0\xfd\xc9\x2f\xd4\x16\xa4\xa6\xb4\x80\x88\xc5\x87\x93\xa3\x24\xf2\x6a\xbb\xa0\x6c\x31\xa3\xe2\x87\x5a\x70\xa4\x48\x7b\x4a\x3b\xa5\x1c\xa2\x7b\xa3\x36\xaa\x7e\xa8\x6a\x3a\xbc\x33\x5d\x28\x02\x6e\xbb\x72\x42\xb3\x2c\xcd\xa4\x4b\x1a\xde\xe3\x9c\x24\x69\x41\xc6\x69\x96\xd1\x71\x31\x3a\x57\xeb\xc6\xec\xb5\xb1\x80\x58\x41\x49\x02\x4b\x9e\x09\xff\x3d\x81\xff\xfa\x45\xfa\x2a\x3d\xa7\xd9\x7e\x90\xd3\x0e\x30\x17\xae\xef\xd5\x7c\x8c\x41\xfd\x43\xdc\x32\x8b\xab\x9b\x63\xf6\xff\x89\x3e\x8a\x23\x10\xec\xf7\x1b\x13\x1e\xf7\x44\x96\xd0\x73\xf2\x9c\x8d\xaa\xd3\x86\xab\x5e\xe8\x08\xd8\xaa\xfe\xbb\x5d\x10\x7a\x11\xe5\x45\xde\x23\x8b\x98\x06\x39\x88\xc5\x30\xf2\x34\x51\xa8\x9a\xa4\x71\x9c\x9e\x47\xc9\x14\x4a\xe6\x8c\x0b\x5a\xcb\x48\xf4\xb0\x07\xfe\x15\x7a\xfa\xd9\x47\x45\x94\x58\xd5\x7b\xf0\x7e\x65\x7a\x15\x0e\x3e\x51\x58\x84\x9c\xe1\xc3\x65\x74\x04\xf6\xb4\x8a\xc9\x72\x12\x60\xac\x16\x7c\x55\xf0\x89\xe7\xa8\x15\x94\xf5\x36\xcd\xf3\xe8\x34\xe6\x53\x08\x2e\x34\x84\x51\xdf\xfb\x03\x26\x5f\x66\x05\xff\xc9\x44\x6a\x89\xad\xe7\x93\x49\x34\xbd\x14\x1f\x87\x92\x94\x1e\x90\x4f\xac\x79\xfe\xa7\xaf\xab\xe0\x53\xdc\x6c\x71\xb0\xb9\x06\x53\x97\x4b\xfc\x53\x5e\x45\x71\xb8\xa9\x86\x53\xf7\x3f\xfc\x53\x5c\x18\xe9\x3c\x5e\xe0\xc1\x03\xb5\x30\xf5\x3d\x0e\x2f\xf0\x5b\x70\x9a\x1a\x79\x9e\x12\xf2\x1e\x86\x0f\x00\xae\x6f\x70\x1e\x2f\x81\x7a\x81\x0a\xf3\x4f\x81\x05\x04\x42\x2c\x08\xf4\x01\x97\x29\x02\x21\x54\xe3\x70\x8a\x7e\x17\xf2\xb7\x2d\x52\x70\xbe\x60\x9d\x7c\xbf\x28\x39\x9d\x93\xc3\x38\x48\xd8\xc9\x20\x50\xac\x59\xa4\x0b\x5d\x59\x9a\x91\x80\xbc\x7c\xfe\x4f\x38\x84\x4b\x69\xed\xd6\x18\x8a\xda\x67\xe5\xd1\xee\x97\x19\x95\x7e\xf6\x02\x74\x95\x2b\xa2\xa0\xa0\x60\x01\x6c\x3d\x05\x39\x39\xa7\x6c\x81\x68\x07\x2b\x72\x18\x6b\x48\x1a\xfa\x85\x1a\x47\x72\x39\x4e\xcc\x52\xb8\xa8\xc3\x6a\x96\x4c\x02\x0b\x45\xbc\x04\x8e\x1a\x6b\x72\x2a\xce\x9d\x2c\x79\x08\x6f\xc3\xa2\x02\xf2\xc4\x68\x64\x84\xbf\x90\x64\x55\xbb\x7c\x03\x8e\x63\xcf\x0a\x3e\xa7\xd1\xdd\x82\xfd\x6f\x59\xe2\x45\x5a\xb5\xc0\xd1\x79\xe1\x77\x5b\xea\x6c\xb5\x7d\xc3\xc5\x0e\x08\xb9\x9d\xa5\x5e\x44\x73\x9a\x7f\x8b\x65\x9e\x08\xe5\x22\x5b\xdc\x4a\x55\x95\xf3\x63\x3e\x6c\xd1\x44\xd9\xb2\x38\xe4\xa0\x7a\xd2\x88\x28\x34\x19\xc8\xbb\x43\x36\xf7\x9a\x16\xcc\xda\x94\x97\x2b\x5d\x81\x06\x50\xf8\xc7\xc6\x37\xd6\x2c\xd4\x9c\x7f\xbe\x63\x42\x20\x2c\x7b\x59\x5e\xfc\xf8\xf2\x85\x0c\x77\xbc\x87\x1b\x51\xaf\x73\x38\xe1\xe9\xc6\x89\x48\xe0\x5c\xf6\xe4\xde\x3d\x22\x7e\xfb\x84\x7e\xd6\xa4\x9d\x8b\x4f\x18\x3e\x1f\x68\x86\x2c\x26\x0a\x2b\x9d\xc8\xf0\xa2\xdd\x6b\xb7\xf1\x85\x8b\xe5\x29\xcd\x57\x1a\x13\x4a\xa9\x4c\x97\xc8\xd8\xb1\x1e\x52\x51\x74\xc2\xc1\x64\x14\x0f\x75\x14\x13\x66\x93\x00\x5b\x9c\xa7\xed\x9c\x8c\x55\x4c\x17\x87\xb4\xcc\x90\x2f\x4d\xe8\xab\x84\x6a\xd0\x21\xd9\xac\xd3\x54\x78\x19\x24\xc3\xc0\x4f\x11\x65\xf9\x16\x2c\x3c\xf9\xee\x20\xaf\x75\xaa\x00\x56\x49\xd4\x4e\x5d\x6b\x72\xc3\xbf\x16\xcc\x72\x7f\x11\x2f\x73\xdd\x05\xf1\xed\x75\x6f\xa8\x80\x4c\x4d\xd2\x8c\x8e\x3f\xe5\xf2\xd8\xc4\x79\xa4\xbc\xe6\xcc\xc5\x63\xb9\xf8\x12\xfc\xf8\x7a\xa3\x11\x73\x92\x1f\x7b\x23\x11\x9b\x31\x85\x51\x03\x6c\xfd\x07\x1a\x1e\x3b\xb6\x83\xe0\x4a\x62\xe6\xac\xba\x8d\x89\x13\x95\x5a\x1a\xb4\xc1\x7f\x86\x17\xc7\xc3\x07\x3f\x04\x0f\x26\x27\x9f\x1f\x0e\xaf\xfe\x67\x10\xf5\x0b\x9a\x17\x0a\x7c\x85\xb1\x57\x0c\xf9\xeb\x0c\xb6\xc1\x30\xe1\xfc\x3f\xf8\x4f\x67\x78\xd1\x7d\x52\x39\x4e\x4c\x7f\x83\x81\x8e\x95\xc5\xa3\x61\x41\xef\xb8\x07\x61\x61\x74\x38\x87\x77\xbc\x6c\x3f\x46\xa3\x36\xe9\x57\x38\x02\x24\xa6\xab\x0a\x6f\x67\xcc\xbe\x30\x36\x87\xc0\xf6\x1e\xbc\xf0\x82\x59\x5d\x86\xd0\x5d\xed\x1c\x9c\x1d\xe7\x73\xf6\xef\x38\x58\xe4\x20\x3b\xc4\x31\x91\xdf\x3d\xec\xa1\xd1\xee\x31\x77\x3c\x8f\x3a\x6c\x34\x70\xa8\xb6\x77\x8e\x1d\x1a\x8c\x67\x64\x1c\xe4\x4e\x35\x51\xce\x09\x65\x39\x17\x33\x84\xa8\x89\xaf\xb2\xe6\x34\xc5\xdb\xca\x97\xf3\x39\x0d\x4b\xc9\xcb\x6a\xee\x96\xc9\xcc\xaa\xbd\x8a\xdc\x06\x03\x3e\x1e\x0b\x37\x81\x2a\x29\x7e\x39\x3b\x90\xd6\x87\x08\x88\x97\x41\x0e\xce\x68\x66\xc1\xb6\x6c\xc4\xd4\xa5\x48\x69\xc7\xe7\xf0\xe5\xe1\x10\xee\x28\x89\x45\x21\xe0\xbc\xbb\x98\x91\x98\xc2\x73\x6a\x14\x81\x6f\xb1\xa0\x19\xeb\xad\x9c\x86\x04\xa2\x17\x4e\x23\x1e\xe0\x2e\xc8\xe9\x3c\x58\xb0\xe9\xd8\x34\x34\x7d\x1d\x65\xc1\x80\x3a\x0d\x6e\xd9\x36\x1f\x75\xc9\x4f\xe4\x7b\xb6\x9d\x8b\xac\xe3\xe8\xa4\x5f\xa4\x47\xac\x21\xa1\x0b\x5a\xdf\xdd\x45\x99\x40\xf4\xd5\x15\xfe\xb8\xeb\xa9\x11\x6b\x97\xac\x1a\x4b\x7c\x85\xa3\x65\xa9\x59\xbe\xc1\xf8\x75\xfc\x05\x45\xa5\xaf\xc5\x51\x4f\x52\x63\x09\x29\x16\xe9\x6d\x92\xa2\xd4\x5e\xab\x7d\x79\x05\x4a\x44\x3a\x63\x45\x7d\xf6\xab\x6b\xd1\x4e\xbb\x2d\x48\xc9\x25\x53\x03\xbf\xd7\x22\x5a\x04\x34\x76\x7a\xcf\x2a\xaa\x20\x63\xd9\x0b\x74\xed\x6e\x93\x34\x30\xbd\x99\x36\xfd\x63\x44\xfa\x03\x3b\xf8\x4c\xb8\x03\x7d\x79\x13\xa7\x28\xdc\x20\xe0\x3a\xfa\x35\x29\xc8\xee\xff\xc6\x6e\x29\x71\x23\xf2\xb2\x19\x69\x6d\x4d\x95\xa4\x69\x95\x34\x25\x4f\x2d\x69\x1a\x6c\xb4\x48\x99\x44\x19\x85\x64\x6b\xc8\x7d\x06\x3d\x10\x17\x84\xbc\x4d\xfe\x3e\x61\x78\x41\xb8\x71\x87\x6b\xdc\x55\x4b\xc9\xfe\xdb\x7e\xe1\x7d\x00\x73\x6d\x65\xc0\xd5\x8c\x7e\x2d\x71\xc6\xbb\xf1\x49\xa7\xba\x12\x1f\x48\x86\xe7\xbb\x6d\xd5\x46\xeb\xa9\x48\x5c\x7e\xf9\xea\x33\x21\x64\xe8\x45\xb8\x52\x52\x35\xea\xd7\x54\x3d\xf2\x70\xe8\xbf\x25\x90\x8e\x88\xe5\x69\x3a\xd7\x52\x6e\x7d\x90\x4d\xef\x49\xd2\x77\xf5\x65\x04\xde\xe4\x1b\x99\xef\x0c\x48\x3a\xbc\x1b\x96\x5c\x28\xfb\x96\xe4\x45\x90\x8c\x19\x17\xd1\x85\xbf\x7c\x51\x48\x13\x85\xe1\xf5\x1a\xfc\x32\x1c\x67\x78\x53\xb9\x6d\x04\xf0\x22\x55\x65\xbb\x29\xa2\xe4\x79\xb8\x0e\x4b\xef\x1d\xe3\xa2\x86\x28\xf2\x84\x48\xf2\xe2\x47\xb0\x56\xd1\x33\x18\x0d\xef\x5b\xfb\xf6\xd0\xc3\xfb\xd2\x18\x37\xb2\xc7\xf5\xd8\x79\xa1\x8d\x48\x56\xc5\x8f\x2c\x7a\x2d\x0c\xc9\x12\xed\x86\x23\x62\x7d\x2a\xea\x87\xc3\xbb\x7e\x8d\xc1\x1c\x8a\xbe\x35\x5c\x0c\x4c\xbc\x48\x96\x71\x0c\x51\x12\x3a\xee\x0a\x01\xc3\x6d\x50\x61\x78\xc6\x2e\xee\x6b\x1b\x8e\xfc\x94\x77\xb6\x01\x3b\xe0\x80\xd7\x61\x06\x3c\xe9\x5a\x13\x29\xba\xd7\x74\x34\xe0\x02\xb0\x7e\x2c\x4e\x44\x8d\x86\x23\x71\xa3\x62\x34\x64\x69\x50\xb0\x72\x0c\xf6\x71\x84\xef\xa3\x60\x23\x97\x4a\xaa\x33\x07\xf1\xf7\xdc\x5c\x57\xda\x02\xa1\x72\x0c\xac\x98\xfd\x6a\x40\xb9\x4e\xca\x2e\xdd\x7d\x6a\x7d\x1d\x6e\x26\xf9\x33\x5c\x6d\xcc\x7a\x45\xc6\x10\xf6\xa9\x43\x3d\x7b\x1b\x3e\x90\xae\x32\xea\x40\x8c\xfb\x39\x9b\x40\xba\x9c\x93\xd3\x38\x1d\x7f\x22\x33\x1a\x84\x34\x63\x1f\xe9\xdc\xb6\xda\x88\xf2\xa7\x2c\xd9\x27\x34\xcc\xe8\x85\xf2\x8b\x0e\x65\xc9\x24\x8a\x0b\x5b\x99\xe9\x21\x58\x80\x35\xdc\x0f\xb3\x94\xca\x93\xfe\x77\x9b\x5b\xfa\xa8\xcf\xc1\x6b\xf0\x52\x7e\x50\xe7\x75\xe1\xaa\x7c\xe7\x74\x17\xca\x17\x71\x58\x9f\xb3\xd7\xdc\x7e\x5c\x63\x66\xe2\x94\x89\x79\x8b\x68\xec\xce\xc3\x07\x96\x5c\x37\x0f\x85\x02\xaa\x98\x00\xa8\xc9\x98\x00\x28\x56\x39\x01\x8f\x1e\x6a\xfc\x73\xe8\x6b\xe3\x1f\xaa\xc2\x35\xf9\xd0\xef\x00\x5d\x0b\xfb\x25\x8e\x47\x84\xc8\x37\x92\x3f\x7a\x32\x15\x1e\xfd\x8c\xd4\x2f\x9e\x0e\x82\xe1\x88\xff\x27\x53\x84\x05\xc9\x48\xff\xe4\x39\xc8\xba\x64\x84\x3f\x64\xb9\xa3\x62\xf2\x78\x24\xfe\x97\x69\x60\xaf\x32\x92\x3f\x74\x3d\x1c\x56\xfe\xd2\xe9\x02\x5e\xfd\x14\xf5\xb8\x46\xb7\x23\x5f\x22\x87\x76\x6d\x39\x47\x9e\x34\x03\x56\x9a\x4d\x8e\xec\x04\x39\x8e\x5f\x28\x8c\xe2\x17\x8a\xc6\x00\x69\xe2\x87\x84\x53\xd2\xe2\x08\x7f\xc8\x5c\x53\x65\x3d\x72\x52\x14\xd6\xb8\xa0\x3e\xd2\x3f\x79\x0e\x92\x8e\x47\xf8\x43\xe6\x1a\x27\x91\x91\x9d\x20\xa1\x50\xbe\x95\x63\x1d\xdd\x47\x6e\x92\xec\xa1\x03\xe9\x24\xc9\x3a\xa5\x30\x36\x42\xbf\x71\x7f\x93\xe9\x48\xfd\x92\xe9\x7c\x4f\x1d\xa9\x5f\x6a\xf4\x7c\xbd\x8f\xf4\x4f\x35\x26\xb6\x4b\x8e\xe4\x0f\x99\xca\x36\xac\x91\xf8\x5f\xd5\xc1\xf8\xdd\x48\xfe\x90\xa9\xc0\x36\x46\xf2\x47\x0f\x16\x18\x77\x50\x27\x5e\x75\xb7\x46\x9b\x3f\xf4\x2a\xfd\xdb\xf4\x5a\xcb\x62\xf2\xb8\x35\x7a\xfc\xdd\xd5\x49\x6f\x6b\xb3\x89\xc7\x07\x73\x09\xef\xf2\x05\xdc\x12\x8e\x0e\x5a\x23\xd2\x1a\xf6\xb7\x86\xfd\xcd\xd6\xda\x95\x74\x05\xb7\xd5\x28\x52\xf1\x9d\x27\x89\x3b\x4f\x12\x7f\x05\x4f\x12\xa2\x96\x35\xd7\x17\xdc\xdf\xe9\x64\x92\xd1\x4b\xf2\x4b\x14\x8f\x3f\x51\xf2\xe3\xaf\x74\x32\xb1\xdd\x49\x34\xf4\x18\x07\x60\x51\x90\x90\x43\x26\x71\x07\x00\x15\x05\x89\x0b\xf6\x22\x38\x65\x60\xff\x48\xa7\x34\xce\x0b\x1a\xc7\x34\x23\x3f\x4e\x20\xd1\x05\xfe\x39\x38\x23\xbf\xa4\x69\x48\x7e\x9c\x96\xba\xb9\x78\xa8\xdd\xfb\x08\x5f\x90\xaf\x83\x24\x98\x9a\xbe\x27\xfa\x03\x86\x85\x41\xc6\x01\xe6\x1c\x40\xfa\x98\x38\x38\x85\xc3\x91\x0d\x1c\x9d\x06\x89\x04\x79\x0e\x66\xfc\x36\x04\x97\xbc\xf2\x01\x2d\x66\x12\xf0\xd9\xd3\x0a\xb8\xf0\x54\xf9\x9b\x9d\x55\xd5\x97\xcf\x54\x7d\x6f\xc0\x33\x79\x19\x60\x42\x0b\x09\xf8\x96\x66\x39\x3c\xa5\x2a\x87\x5e\x08\x10\xd5\x89\xf3\x20\x9b\x57\x75\x83\xe5\x2b\x60\x5a\x14\x10\xb5\xc9\x85\xcf\x45\x96\x04\x95\x5c\xc5\x80\x94\xec\x82\x9d\xa8\xb4\x73\x8f\x28\xb6\x2a\x44\x61\xe5\xcb\x7d\x84\x70\x20\xe9\x8d\x49\x3c\xdc\xa0\x49\xe8\xe9\x1b\xcf\x90\x60\x4f\xe1\xc4\xe4\x42\x9d\xb2\x74\x85\xc9\x2c\x5d\xd0\xac\xb8\xf4\xc0\x2d\x44\x96\x04\x7d\x59\x14\x8b\xb7\x59\x7a\x16\x85\x5e\x72\x63\x0b\x75\x21\xb2\x15\xb1\x2d\xc6\x15\x25\xa2\xc5\xd8\x2e\xd0\xcc\xa3\xe1\xda\x9a\x92\xd5\x7f\xa1\xa7\xdb\xa4\x23\xab\x31\xbd\xf2\x66\xf6\x0a\x49\xe8\xb9\xb5\x6c\x74\x49\xe4\xa0\x57\x84\x5a\x45\x3d\x97\x50\x08\x88\xf2\xb7\x2e\xf4\x9c\x2d\x17\x70\xd4\x8f\xab\x08\x4f\x45\xe6\xb3\xa7\x4e\x5e\x3e\x93\x25\xdf\xcf\xdc\x92\x09\xac\x01\x96\xfb\x86\x16\x4e\xee\x42\x13\x3e\x03\x91\xeb\xc0\x81\x3b\xfd\xed\x37\xd9\x06\xa3\x6b\xb7\x0f\x9a\xc0\x01\x48\x7c\x76\x30\x8c\xa6\x6c\x7d\xd4\x08\x16\xd1\x48\x6d\x86\xe2\x7f\x7e\xe4\xc0\x9d\x14\xd8\xca\x8d\xa2\x98\x7c\x46\xc6\x57\x4f\xc1\x20\x7a\x19\xe1\x0f\xa7\x89\x8f\x6a\x0d\xf0\x1f\xce\x00\x05\x40\x47\xb7\x2f\xc8\x39\xa2\xf9\x08\xfd\xee\x70\x63\x9e\xab\xee\x0e\x93\x98\x06\x03\x70\xc1\x9b\x53\xa2\xc7\x90\xf2\x9d\x18\x7c\x02\xad\x31\x72\xf3\x8c\xaf\x6e\x6c\xa5\xe3\x62\x42\xa3\xac\x53\xc6\xd3\xa4\x98\xf2\x70\xcc\xe0\x7a\x1a\xc7\x85\x57\x26\x6d\x4f\x5f\x32\xca\x83\x45\xe8\x5e\x7c\xa2\x74\x71\x90\xbf\xbf\x4c\xc6\x51\x32\xad\xec\x0a\x94\xb5\xe0\x9b\x51\xa0\xa7\x23\x98\x2f\x3c\xd5\xf6\x2b\x16\x94\x7c\x06\xc3\xdd\x49\xc1\x97\x07\x46\x3e\x99\x95\x50\xf0\xed\x81\x13\xef\xae\x25\x18\xfb\x74\xa0\xf0\x13\x5c\x0e\xa8\x52\xbc\xb0\x46\x9d\x32\xc1\xd3\xb6\x7e\x4f\x25\x9b\x17\x29\xde\x5a\x6d\x68\x94\xe6\xa9\x1b\xe3\x52\xd6\x5e\x85\x53\x6e\xe2\x28\x21\x7f\xa1\xfe\x91\x61\x28\xf1\xed\xc0\x61\xd3\x16\x0e\xa9\x52\x3c\xb0\xee\xad\xb0\x2c\xb3\x6f\xdf\x16\x3a\x7d\x2e\x2b\xeb\xe4\x78\xda\x3d\x78\xba\xf7\x06\x35\xc6\x3e\x1d\x28\xed\x9e\x86\x83\x89\x6f\x1f\x9c\xf4\x9c\xa2\x00\x21\x81\xed\x62\xf6\xc2\xe7\x5b\x3f\x7e\xc9\xcd\x2f\x85\x4c\xef\x8a\xe6\x75\x1d\xdc\x49\xdb\x90\x65\xd7\xa7\x61\x94\x81\xaa\x78\x1c\x2c\xe0\xf5\x05\xba\xc0\xf4\xcc\xe8\xc1\xfe\xde\x5b\x63\xed\xb3\x72\xd8\x42\x2e\xe2\xa2\x24\x5b\xbe\x4c\xaa\xe4\xf9\xc6\x63\x4f\x06\xd1\x17\xcd\xc8\x95\x0d\x0e\x65\x14\xff\xad\x8a\x38\x7a\xac\x78\x37\xec\x75\x42\x1c\xe9\x98\x77\xce\x09\xe8\x60\xda\x72\x4f\x4a\xd2\x90\xb6\x7b\x06\xc4\x14\xcc\x42\x46\xa4\xcd\x84\x8e\x8f\xe3\x38\xa2\x49\xf1\x0f\x0e\xde\xd6\x77\xd2\xdd\xde\x75\x5a\xa3\xc5\x79\x9a\x7d\x2a\x6b\x30\xa1\xc5\x47\x01\x6a\x81\x98\x01\x03\x46\xf6\x2a\xbf\x61\xb7\xa8\x50\x68\x97\xf5\x8b\x16\xb3\x8f\x30\xd7\xe3\x34\xfe\xc7\x37\xe8\xdf\xf9\x2c\xca\x17\xca\x37\xb2\xd3\xbd\x7c\x36\xbb\x31\xda\xe0\xe7\x89\x77\x2f\x89\xf2\xfd\x34\x49\xb8\xcf\x26\xb4\xdc\xba\x06\xed\x75\xbc\xdb\xe5\xbd\x7b\xde\x6d\x14\x57\xd9\xe9\xfa\x77\x30\xee\xa5\x40\xca\xe4\xa5\x34\x0f\xc6\xa1\x10\x39\x41\x48\x34\x5e\xbd\x2d\xab\x5b\x7a\x13\xc5\x27\x04\xae\x72\x32\x0e\x16\xad\xd1\xd6\x90\x25\xe1\x23\x49\x6b\xb4\xb5\xc9\xd2\xf4\x71\xa0\x35\xda\x7a\xa8\x52\xb8\xe8\xd4\x1a\x6d\x3d\x56\x49\x58\xb8\x6f\x8d\xb6\xb7\x54\x06\x5b\xe1\xad\xd1\xf6\xb6\x4e\xd0\x42\x7d\x6b\xb4\xad\x2b\xd5\xc7\xc2\xd6\x68\xfb\x7b\x27\x99\x16\xb3\xd6\x68\xfb\xb1\x93\x9e\xd0\xa2\x35\xda\xfe\xc1\x49\x97\x82\x70\x6b\xf4\x70\xe8\x64\xe6\xb3\x59\x6b\xf4\x70\xd3\x4d\x67\xb2\x70\x6b\xf4\x50\x77\x5f\x9e\x71\x5a\xa3\x87\xdf\xa9\x44\xf3\xe0\xdc\x1a\x3d\x7c\xa4\xb2\xa4\xd4\xd2\x1a\x3d\xfc\xbe\x5a\xb7\x77\x75\xd2\xdb\xda\xbe\xd3\xbc\xdd\x69\xde\xfe\x5b\x34\x6f\x41\x1c\x83\x83\x89\x9b\xf9\x71\x45\x0a\x2e\x47\x15\xe2\xd3\x85\xc8\x30\x31\xcf\xcf\xb8\x45\x3f\xd2\x31\x40\x6f\x24\x9c\x0e\x1a\x53\x17\x1d\xc9\xd5\xd3\x78\x15\x35\x2f\xe0\x72\xd7\xaa\x0c\xd2\x24\xc4\x39\x8f\x7d\x64\x82\x48\x56\x24\x32\x95\x77\xd7\xbd\x38\x36\x86\x62\x0a\x46\xe6\xd1\xaa\x07\x37\xf5\x3d\x62\x99\x96\x95\x28\x3d\xcc\x04\x7c\x44\xfe\x95\x5f\xce\xb3\xff\x70\xb2\x63\x2e\xc9\x37\x21\xa7\x87\xd5\x61\xbe\x2d\xa9\x55\xfa\x03\xdf\x55\xbf\xbe\x7c\x81\xf8\x37\xc4\xf6\xfb\xc0\x12\x21\xf5\xb8\xcd\xa4\x50\x88\x2b\xd0\xee\x91\x76\x91\xf2\x9f\x27\x7d\x8e\x66\x14\xef\x70\xe2\xb9\x0d\x15\xcd\x1c\x4f\x4e\xc0\xc0\x45\xd9\x87\x8a\x1b\xd2\xae\x27\x68\xb6\x55\x0d\xeb\x0f\x2b\xbe\x8b\x88\x87\xbb\xd0\x81\x8e\xf0\xf3\x92\x0e\x82\xa7\x1b\x94\x36\x0b\xfa\xe1\x16\xf8\xa2\xd0\x78\x35\xf0\x6c\xbe\xee\xc2\xde\x29\xaa\x30\xee\x89\x5a\x1c\x06\x45\x20\x47\xc0\x7e\xf7\xd9\x3f\x64\x17\xfd\xfe\xf2\x05\x8c\x62\x15\x00\x5c\x25\xe7\x12\x44\x7c\x7d\xf9\xa2\xa3\x6f\x82\xb6\x91\x35\x2d\xef\xc8\x11\xe0\xf1\xf0\xa4\x9f\x33\x86\xa0\x5c\xac\x33\xe8\xb9\x10\x70\x34\x85\xb9\xd3\xf5\xab\x67\xba\x70\x2b\xbb\xc2\xd4\x56\x48\x77\xee\xa5\x6d\xe7\x57\xf5\x3e\xbd\x7b\x3c\x3c\x41\x0f\xaf\xd6\xa1\xfd\x2e\xf9\x0c\x8f\x1d\x82\x24\x49\x0b\x32\x89\x92\x90\xf7\x2b\x4a\xa6\xbc\xa1\x27\xaa\xf9\x71\x9a\xe4\x69\x4c\xfb\xe7\x41\x96\x74\xda\xb8\x04\xf7\x96\xc3\x58\x71\x9c\x4e\xdb\xc8\xf4\x55\xf4\x98\xa1\xc2\xf1\xb8\x44\x05\x1b\xc2\x91\xb9\x60\xee\x3a\xbe\xd5\xd9\xe3\xdd\xea\x99\x04\x61\x1e\xa1\xa0\x46\xe9\xec\x10\xa6\xb8\xc1\x72\xbc\xa0\x63\x26\x01\x78\xd6\x63\x0f\x3c\x32\x9d\x06\xe3\x4f\x2a\x86\x28\xb8\x22\x10\x87\x5d\x79\xdd\xda\x09\xb2\xe9\x12\xde\x82\x1c\xab\x5f\xc8\x1b\x8f\x69\x84\x2e\x6b\x84\xd8\xcf\x95\xc5\xb0\xdf\xb8\x8e\x03\xc1\x26\x7e\xd3\xf4\x63\xa1\xd9\x46\xb2\x8c\x63\x07\xdd\xa9\xa4\x34\xe1\xfd\x4e\x1f\x80\x25\xc4\x04\x45\x59\xe3\x9a\x59\xc0\x64\xff\x34\x32\x95\x86\x48\xfc\xe6\x9c\xbd\x93\xf6\xe0\xa0\xd4\xee\x79\x19\x6b\x4f\xb2\x77\x76\xd8\xea\x74\x7b\xba\x21\x84\xe1\xfa\x99\x0a\x8a\x22\x18\xcf\x3e\xa4\xfb\xd2\x11\x16\x9e\x32\xe9\x1d\x0b\x9f\xb9\xf5\xd4\xf2\x71\xf3\x4f\x67\x38\xb2\x68\x3f\x88\x63\xb5\x9f\x08\xe0\x92\x33\x85\xd3\x4d\x75\xc0\xf0\x9c\x30\xbc\x47\x0c\x20\xd5\xd6\x68\x0b\xa4\x7b\xbe\xea\x5b\xa3\x2d\x90\xdd\x71\xcc\xb6\x6d\x00\xb6\x36\xc2\xd6\xe8\xe1\x36\x13\x99\x1f\xde\x89\xcc\x77\x22\xf3\x5f\x5b\x64\x46\xe1\x5e\xe0\xec\x7d\x5b\xf1\x5e\xfe\x9e\xa7\x49\xb6\x18\x9b\xf2\xe6\xaf\x3c\x51\x5d\x1d\x66\x59\x6a\x8b\xc0\x3c\x4d\x49\xa2\xae\x8a\x82\x0d\xd6\x10\x32\x1d\x19\x13\xd0\xf1\xb1\x54\xd2\x14\x19\xb9\x08\xec\x5d\xe3\x28\x30\x08\x43\xe9\xd3\x91\xb1\x63\x51\x18\xdc\x64\x43\xd7\x44\x82\x65\x11\x18\x84\xa1\xc7\xc6\x96\x88\xf1\xf3\x42\x85\xb6\x6e\x1d\xac\xc1\x38\x31\x2b\x0e\x43\x9f\xcc\xed\x1b\x78\xce\xa3\x82\x4b\x88\xda\x11\x49\xa6\x5d\xd5\x7f\x01\xe3\xed\x9a\x6f\x3f\x37\xbd\x0b\x28\xfc\x1a\xdd\x74\xa7\x40\xdf\x13\x25\x21\x57\x33\x49\xd8\x1e\xaa\x9b\x66\x59\x4f\x48\xa2\xb9\x2b\x13\x73\xf2\xe1\xbf\x84\xb0\xa8\x01\x04\x7e\xb0\x8b\x49\x85\xca\x1e\x81\xd7\xed\x25\xef\xd7\x44\x95\xc7\x00\x73\x82\x8f\x07\xa5\x02\x3b\x2f\x52\x52\x2d\x13\x6b\x64\x7f\x44\xa5\x7d\x47\xf6\xb1\x0b\xac\x8b\x45\xd4\x8f\xf2\x7f\x04\x71\x14\xbe\xa3\xf9\x22\x4d\x72\x2a\x9a\x72\xde\xde\x39\x63\xf0\xb7\xd7\xe1\x6b\xac\x7f\x90\x9c\x79\x6b\xdd\x71\x2a\xbd\x72\xfb\x57\x5a\x39\xf7\xd9\xe4\x0c\x96\xef\xb9\xe0\x1b\xc2\x97\x21\x1a\xef\x8b\x3e\x80\xd7\x08\x9c\xe0\x44\xb1\xd7\x53\xa1\xce\x37\xc4\x2f\x4a\x00\x65\x69\xfd\x24\x1f\x7c\x6b\xb4\x05\x7a\x34\xb1\x22\x5b\xa3\x6d\xb0\x7a\x6b\x14\xe5\xfb\x6e\xc3\xbf\xdb\xf0\xff\xbc\x1b\xbe\xde\xef\x95\x58\x7e\x4b\x2a\xb2\x86\xba\x2a\x76\xe2\xc9\x2c\xb0\x5c\xc8\xfa\x03\xc8\x5c\x55\x9d\x26\xe1\xd0\xbb\x29\xac\x07\x93\x0f\xa2\x04\xf4\x1e\x3a\x84\x20\x30\xa5\x31\x34\x42\x8e\xfb\xf6\x4f\xae\x5e\xc2\x8f\xcc\x60\x9b\xb7\x9f\x29\x73\xb8\x7d\x0d\xf6\x56\x42\x29\xb9\x00\x8c\x7d\xaf\x88\xf4\xe5\x6c\xa6\x7a\x1b\x10\xde\x7e\xfd\x55\x9b\x4f\x3d\x4f\xa3\x9e\x28\x67\xdd\xea\x04\xa7\x91\x47\x0d\x82\xfc\x3e\x13\xcb\xd1\x32\x0f\xf0\xbd\xbb\x4b\xda\xa8\x4f\x6d\x72\xef\x9e\xe1\xc8\x19\x9d\x9b\x79\xb3\x86\xb7\xff\xab\xae\xb5\x0d\x57\x35\xe8\x71\x0d\x4d\x3a\x90\x58\xb2\x5d\x43\x1e\xf7\x18\xed\xd9\x19\xac\x8a\x18\x58\xee\x69\x1a\x68\x4f\x1c\xde\x39\x42\x39\xa8\x42\x23\xd2\xf2\x48\xed\x55\x03\xe9\x51\x05\xf4\x12\xae\xa2\xf8\xd1\xda\xfb\xb2\x29\x08\x43\x49\xc3\xb9\x3e\x86\x63\xda\x90\x69\x57\xaa\xa6\x52\x7a\xe2\xa4\xe2\xaf\xb2\xf2\x64\xaf\x8f\xeb\xd7\x27\x14\xf4\x0a\x71\x95\xd9\xc7\x9a\x2a\xa5\xfd\x51\xfd\xf9\x48\x8b\x99\x54\x37\xeb\x4e\x9a\x5e\x2f\x6a\x55\xa9\x13\x47\xcd\xa1\x11\xa0\x55\xa5\x0d\xe6\x95\x73\x8b\x46\x93\xca\xf9\xcd\xed\xcd\xa8\x5d\x5f\xbd\xa2\x46\x32\xbc\xdb\x98\x5b\xce\x7b\x2d\xb5\xb2\xe0\xac\x42\xdb\xa8\x78\xac\x39\x79\xae\xde\x8a\x77\xac\x74\x3a\xf7\xe2\xb8\x72\xba\x00\x48\x5c\xf4\xac\x4c\x60\x5c\x15\x5a\xd3\xc1\xd5\xa9\xcd\x78\x14\xe8\x2a\xd5\xca\xa8\xad\x8a\xdc\x94\xa3\x1c\xb0\xfd\x93\x93\x3e\xa5\x45\x2e\x8c\x57\xe2\x4b\x12\xd2\x45\x9c\x5e\xd2\x50\x9a\x08\xc2\xf3\xc1\xf1\x2c\x88\x12\xfb\xb9\x1a\xd4\xf6\x22\xcd\x64\x8f\x3c\xbe\x07\xe4\x81\xd5\x47\x92\x72\x5d\x5e\x29\xd5\xe2\x9a\xe1\x22\xf7\x48\x5e\x6e\xe8\x67\x6d\x25\x2d\x62\x83\x07\xd9\x12\x52\x58\x6a\xf2\x85\x80\xcd\x10\x49\xc6\x51\xf3\xbe\x80\x28\xe5\xbb\xf2\x61\x19\xe4\x0f\x06\xe4\x3c\x88\xb8\xba\x1c\x44\xae\x45\xa1\x55\xb0\xf2\xa6\xcc\x9c\x77\xb1\x14\x54\xc0\x68\xdd\x31\xda\x35\x3d\x2f\xaf\x53\x78\x9a\x6c\xb4\x6f\xef\x4a\xd0\xdf\x8d\x8d\x1d\xf3\xd8\x34\x18\x90\xbc\x48\x17\x5c\x57\x1b\x25\x53\x12\x4c\x58\x57\xbe\x1b\xf2\xb9\xca\x49\xa7\x88\xe6\x34\x5d\x16\x5d\xe7\xe8\xc8\x11\xf0\x13\xf9\x6e\xe8\x3d\x2c\xf2\xde\xf7\x59\xed\xbf\x88\xca\x75\x4c\x85\x2e\xf9\x7c\xe5\x39\xd3\xd9\x08\xe4\x0f\xf6\xbc\xe7\x50\x35\x23\xde\xd3\xa6\x3e\xf9\x69\xc7\xc0\x8a\x31\xc1\x7d\x49\xc0\x57\xc6\x98\x11\x36\x38\x09\x3e\x65\x12\xf3\x32\x09\x6d\x0c\xb4\x7d\x87\x4f\x1a\x23\x87\x22\xf8\xcf\x71\x47\x7c\xed\x56\xd9\xf2\xc3\x35\x2b\x7f\x22\x2e\xd6\x0c\xaa\x99\xd2\xe2\x83\x6e\xea\x1d\x27\x35\xcd\x51\x50\x37\x5e\x06\xf9\x0c\x13\x55\x4f\x12\x66\xd7\x7f\x84\x8f\x26\x1d\x01\xe0\xa7\x36\x6f\x21\x6f\x07\x21\x84\x91\xa8\xab\x3f\x36\x17\xa0\xd9\x23\x88\x73\xe4\xef\x8e\xfc\x2b\xf3\xde\xfe\x48\x79\x6f\x2f\xfb\x8b\x26\x1d\x93\xe2\xbe\x7c\x21\xeb\xd0\x62\x65\x31\xa2\x58\xb7\x87\x36\xf1\xdf\x75\x96\x00\xfe\x6b\xb8\x1c\xec\x21\xa5\x21\x0a\x11\xbd\x5d\x39\x33\xf2\x6f\x30\x50\xf7\x7c\x71\x3a\x45\x54\x0b\xc7\x0a\xc9\xc6\xd7\xdb\xdd\x9a\xe6\x89\x21\xaa\x29\x8e\x5a\x32\xd5\x0d\x2a\x1b\x0c\x08\xdf\xac\xa4\xb8\x10\x24\x21\x11\x37\x23\x24\x98\x06\x51\x22\x56\xce\x39\x15\x11\xfe\x6a\xfe\xfc\xb2\xa7\xbd\x01\xd6\xd4\x60\xcb\x3a\xce\xf6\x5f\x33\xa4\x31\x77\xca\x26\x2e\x05\xd9\x96\xc0\x76\xc7\x9c\x8e\xd3\x24\x24\x8c\xe1\xd6\x56\x82\x48\xb7\x9e\x58\x89\xc1\x11\x41\x17\xd6\xb4\xc3\x5e\x2f\x46\xb7\xdc\x21\xec\xbb\x1d\x89\x12\xe2\x44\x8b\x38\x65\x5e\xa4\x19\x0d\x95\x1f\x77\x2e\x81\x80\xc6\x67\x1a\xe4\x24\x98\xb3\x0d\xa9\xef\xe5\xd7\xf6\x5f\x29\xff\xb6\xff\x3c\xee\xe5\x6f\xa3\x8b\xd5\x3d\xbc\x2a\xcd\x2d\xe3\x18\x6e\x09\x1b\x12\x69\x27\x9b\x1e\x28\xd0\x15\x83\x24\xf4\x17\x01\x3b\x66\x5f\x2a\x5f\x1a\x96\x14\x67\x81\xd5\x1c\x1a\xec\x4a\xf1\x81\x01\x4e\x55\xc1\x69\x64\x5c\x2e\xf0\x17\x45\x54\x1e\xdf\x21\x2d\x38\x8d\xc8\x2e\x83\x94\x72\xd6\x7d\xae\x09\xad\x1f\x93\x3e\x21\x25\x24\x40\xa2\xa9\x28\x2e\x6b\x91\x63\x4b\xe8\xb9\x4a\x92\x63\x4a\x2e\xaf\x30\x31\x58\xba\x91\x4d\x69\x53\x10\xc4\xdd\x15\x8b\x6e\x55\x14\xb5\xe5\x60\x43\xb2\x10\xbe\x4e\xa4\xa2\x38\x74\x4a\xfb\x24\x65\x01\xa1\xa4\x65\x7d\xfc\x93\x49\xaa\x2d\x3d\xf1\x50\x68\xa0\x27\x82\xa1\xd4\x77\xfd\x42\x2a\xb6\xe8\xef\x65\x0d\xec\x4f\xfd\xe0\xd2\xb5\x3a\x45\x62\xfa\xeb\x48\x3a\xe8\xa9\xd9\xc7\x1c\x6c\x30\xe0\xb1\x15\xb5\x95\x85\x51\xa9\xb6\x95\xf8\x7c\xb5\xc3\x80\x25\x96\xd6\xcd\xb6\x05\x62\x50\xc5\x70\xc6\xcd\xe0\x2d\x0e\x10\x32\x7e\x94\x10\x47\x63\x0a\x57\x0d\xda\x5e\xc3\x0a\xff\xe7\xb3\x1d\x01\xfb\x8f\x72\x8b\x11\xe2\x58\x8d\xe4\xfd\x45\xba\x30\x1c\xcc\x99\xdd\x8b\x83\xbc\x10\x90\x4e\xd5\xfe\xee\x70\x42\xea\xb0\x82\xe0\xbc\x68\x5d\xbd\x38\x81\x40\xb4\x90\x6e\xf7\x49\xa3\xb0\xa6\x4b\xac\x21\x01\xdc\xe7\x51\x49\x7e\x22\x43\xbb\x36\x31\xd3\x92\xf6\xf7\xe4\x5a\xae\xd7\x02\xc8\xbf\x1b\xa9\x04\x11\x9a\x2c\x66\x29\xd5\x69\xca\xd4\x0e\x0f\x6b\xdd\xec\x72\x7f\x11\x5c\x06\xa7\x31\xf5\x75\xcf\x3d\x0e\x70\xfb\xa9\x9c\x26\xa1\x8e\x48\x95\xa4\xc9\x03\x51\x09\x46\x87\xbd\x4d\x5c\x95\x4d\x3d\xf8\xf6\x63\x9c\xd1\xaf\x82\xed\xc8\xa5\xd2\x83\x11\xa3\x5a\xe5\x04\x81\xed\xdb\xc6\x2e\xaf\x68\xc7\x9c\xc4\xd2\x1b\x41\x7c\xa2\x35\x74\x00\x52\xee\x0b\xc2\xc4\xd6\x12\x84\x94\x9c\x07\xb9\x12\x28\xd7\x4c\x5c\xf1\xa5\x0d\x57\xaf\xe8\x08\xa3\x0d\xb3\xac\xfb\xd7\x59\x90\xcf\x7c\x48\x67\xbd\xa6\x59\x56\x76\x13\x89\xaf\x1c\x7d\xf7\x8a\x55\x12\x0f\x13\x47\xc3\x90\x5f\x7b\x21\xae\xcb\x7a\xe2\x6f\xab\xe4\xd8\x45\x76\xa1\x4c\x89\xf0\x55\x2a\x21\x4e\xa2\x2c\x2f\xca\x05\xc4\x15\x65\xbc\x12\x0d\x88\x4f\xed\xe1\xbb\x7e\x35\xbe\xea\x1c\x5f\x42\xa4\x4d\x3e\xf0\xba\x79\xb6\x1a\x6b\x8a\xf2\x5a\x54\xaf\x32\x74\x3f\x4f\x53\x3a\x79\x0e\x24\x74\x65\x02\xbb\x72\x13\x64\xe7\xdb\x67\xdc\xae\x14\x92\xc4\xa7\x61\x80\x76\x6d\xc1\xcb\xd6\x9a\xd5\x69\x67\x3d\x9b\xba\xa8\xe9\xca\x94\x81\x26\xaa\xfe\xc1\xda\x60\x60\xed\xc0\xc6\x05\x8e\xf6\x78\x8c\xd4\x97\x56\xe5\x1d\xbe\x2f\x0f\x06\x86\x2b\xdd\xd2\xb8\xd3\xe3\x31\x78\xc5\x4d\x79\xa0\xa6\x28\x99\x56\xc8\x66\xa6\x1a\xdb\x1c\x39\x9f\xc4\x2b\x97\x13\x61\x71\xa8\x4a\x14\x22\x9f\x91\xd4\xd5\x54\x22\x9a\x90\x24\xd5\x35\x30\xf6\xb6\x08\xf2\x9c\x86\x3d\x56\x85\x76\x7d\xc7\x20\x72\xb4\xa4\x4d\x5e\xa6\x08\x0f\x66\xc0\x42\xa7\x61\x0e\xe9\xf3\x9d\x6a\xda\xac\x92\x95\x65\x28\x6d\x29\xaf\xb5\x95\xc5\x0c\xb9\x96\x84\x60\x35\x10\x22\x4c\x1a\x15\xa8\x2e\xf5\x64\x81\x53\x3a\x0e\x96\x39\x65\x27\xf1\x30\x4d\x0a\x72\x1e\x24\x60\x93\x94\x2f\xd2\x28\xe6\xb7\xe1\x49\x41\xb3\x49\x30\x56\xbe\xb1\x1b\x9c\xc4\x9b\x9c\xb6\xed\x6d\xaa\x9e\x1f\x12\xc7\xbd\xae\x5a\xd3\x68\x6d\xfe\x4c\x0b\xee\xac\x99\xed\x8f\x3d\x72\x3e\x8b\xc6\x33\x30\x1a\x60\xcb\xbb\x48\xc5\x36\x46\x16\xf1\x32\xaf\xbf\x7a\x15\x7c\xa0\x66\x7e\x35\xf3\xf0\x1b\x32\xd5\x88\xb0\xab\xcb\xa9\xaa\x58\xbd\xfc\x78\x13\xd9\xb1\x5c\x6e\x44\xc6\xca\xd7\x92\x63\xaa\x64\x18\xf3\xa5\x43\x9f\x1b\xa4\x37\x67\xbe\x9e\x53\x8f\xf7\xb8\xdb\xe0\xfa\xbc\x8c\x35\x39\x87\x61\xef\x29\xb8\xe4\x25\x8b\xef\x3c\xec\xee\x7e\xda\x2e\x9c\xe3\xcf\x7d\xbc\x42\x3c\x87\x69\xaf\xd9\x92\x45\xb7\x3b\xca\xfc\xd9\xb4\x95\x68\x8d\xbe\x2f\xb3\x80\x56\x16\x0d\xad\xd1\xd6\xb6\x6b\x12\x2d\x46\xde\x1a\x6d\x6f\x5e\x9d\xf4\xb6\x1e\xdd\x99\x3e\xdd\x99\x3e\xfd\xb5\x4d\x9f\x90\xad\xb3\x30\x81\xbc\x05\x63\xe7\x12\x37\x96\xc2\xb8\x92\xbf\xcb\x3a\x9c\xc8\x3b\xe7\xbd\x6c\x9a\x8f\x4a\x34\x37\x48\xc6\x13\x27\x58\x51\x09\x8e\x7d\x27\xb7\x13\xc6\x3e\x65\xa5\x04\x9b\x38\x01\x9f\xef\xf9\xfa\xf0\xee\xed\x3e\x67\xee\x37\xe9\x00\x8f\xb7\x04\xac\x96\xc2\x03\xc6\x22\x25\xef\xde\xee\x8b\x7b\x02\x7f\x07\xc4\x73\x74\x70\xa2\xa8\x5b\x9e\xa5\x39\xbe\xfd\x72\x1b\xdf\x3f\x7c\xf3\xe6\xf9\xfe\x87\x83\xc3\x37\xe4\xf9\xbb\x77\x87\xef\x46\x64\x5f\xa9\x7f\xc7\xbc\x4a\x7e\xa2\x0f\x29\x69\x6f\x10\x56\x1f\xd9\x68\xf7\xfd\x7d\xd0\x1e\x6f\x9a\x8e\x5d\xbd\xb3\xe7\x4a\x84\x82\xad\x9e\x88\x57\xe6\x6f\x42\x1a\xd2\x8e\x88\x6d\x14\x8c\x86\x09\xcf\xd2\x68\x9e\x07\x53\x4a\x76\xc9\xfa\xba\x78\x69\xc8\xb6\x75\xf1\xbb\xcf\x43\xc6\x3a\x29\x7d\x59\xec\x09\xf1\x26\x8f\x88\x9a\xae\xbf\xbf\x3f\x7c\x03\xb3\x92\xa9\x2e\x79\xc2\xac\x8a\xbe\x39\x6f\xc9\x34\x0e\x44\xd5\xe6\x68\xf5\x6c\x7e\xe0\xd7\xd5\x78\xbc\xf3\xbc\xe9\x94\x7e\x38\x78\xfd\xfc\xf0\xe8\xc3\x88\x88\x4b\x6f\x46\x5c\xac\x93\xf3\x9c\x6c\x90\x36\xfb\x2f\x18\xcf\x18\xc7\x68\x1b\x01\x6d\x84\x1b\xc9\xef\xef\x76\xab\xbb\xdd\xea\xaf\xbd\x5b\xa1\xcd\x0a\x5e\x5d\xfe\x51\xad\x74\x9b\x3f\x66\x6f\xf4\x86\xfe\x16\x9f\xb2\x4b\x9f\x43\x6c\xfd\xab\xc3\x19\x8e\xc8\x94\x1b\xc7\x10\xf1\xc6\x16\xda\xd2\x87\x05\xdb\x08\xf9\x6b\xbf\x83\x5f\x48\x53\x5e\xa4\x48\xc7\xf9\x3c\x74\x05\xa9\x78\x8e\x9c\xa7\x49\xb7\xe6\x09\x3d\xca\x4c\xd2\xe4\x72\x9e\x2e\x55\x8b\x2a\xa1\xe4\xf4\x26\x91\x36\xa5\x12\x57\x34\xe4\xf2\x00\x04\x31\x70\x82\x35\x89\x34\x75\x3c\x7b\x9a\xa6\xf1\x15\x84\x57\x0d\xc1\x05\x39\xdf\x24\x28\x87\x0c\xd1\xec\xc0\xfb\x10\x1a\x1a\x0e\xd3\xe5\x89\x0f\x82\x11\xb0\x45\x29\x6a\x1f\xac\x19\xd3\x84\xbd\x6f\x31\x08\xd3\x71\x14\xaf\xd7\x0e\xc0\x80\x90\xef\x5e\x89\x44\x1e\x51\x21\xea\x8b\x9a\xe0\x7e\x43\xfc\x2e\x31\x77\xf5\x97\xd7\xf6\xca\xa5\x37\xc4\x18\xdb\x9c\x3e\x43\xee\x02\x1c\xbc\x18\x59\xb8\x0e\xb5\x77\x70\x6f\xb4\x20\x6f\x05\xe5\xa8\x43\xd5\x55\x39\x09\xe2\x94\xe8\x3a\x28\xef\x68\x7a\x6d\x3e\x3a\x58\xa1\x9e\xa1\x15\xc2\x9f\x79\xc5\xb8\x70\xd1\x6a\x7a\x58\x69\x44\xd2\x93\xfa\xb5\x86\x93\x47\xd3\x24\x28\x96\x99\x3d\x1c\x9c\x5e\x36\x1e\x0c\x53\x3e\x1e\x05\x55\x35\x20\x70\x60\xd0\xbc\xff\xe2\x85\x83\x24\x6f\xc1\x91\x82\x24\x54\xaa\xa5\x22\x85\xa0\xc4\x93\x28\x09\x62\xbf\xd5\x33\xaf\xc3\x67\x53\x8a\xd7\xb5\x95\x25\xaa\x37\x90\x22\xf3\xe8\x19\xcd\x2e\x8b\x19\xd7\x58\xcf\x4f\x23\x60\x19\x29\x8f\x12\x0d\x7d\x13\x61\x16\x2a\xb1\xe5\x71\x0d\x22\xba\xe3\x78\xb6\x53\x8b\x5b\xfd\x42\x8f\x00\xef\x1c\x88\x68\x77\x1d\xca\x3f\x47\x9d\x67\x11\xa9\xd7\x5c\xb7\x76\x1e\xb7\x9f\xa2\x72\xfe\xb0\x55\xf8\x16\xe4\x7e\x3a\x25\xb5\x77\xba\xae\x4a\x53\xcc\xd3\x07\xd9\xb1\x9b\xb2\x74\x14\xc2\xa2\x92\x9f\x83\xe3\x65\x11\x4c\x5b\x94\x3f\x8e\x20\xc4\x94\x65\x0c\x20\x80\xf0\xfc\x31\xba\xd1\xc9\xc9\x32\x8e\x4b\x5e\xb8\x68\xcd\x22\x71\x2f\xff\x4d\x85\x30\xd4\x57\x16\x98\x11\x32\xad\xd1\x9c\x55\xdc\xf6\x0b\xec\x3b\x6f\x63\x3a\x7c\xfb\xea\x91\x33\xfb\xe6\xbc\x6b\xc7\xd6\x5b\xa9\x36\xe8\x7b\x0d\xc5\x99\x44\x32\x4e\x93\x71\x50\x74\x8c\xd9\xef\x96\xfb\xb1\x29\xe5\x7a\xc2\x89\x4d\x39\xd7\xb3\x77\x5b\x5a\xc6\xe1\x42\x7e\xf7\xe0\xf2\x30\xc1\x15\x84\xe1\x10\x9c\x10\x78\x2d\xa1\x6a\xf6\xde\x3d\xd0\x37\x98\xbd\xa8\xde\xa6\xcb\x9d\xef\x00\x0e\x6e\xd1\xfb\x4e\x90\x4d\xad\xd5\xa5\xc5\xc7\x27\x46\xc9\x11\xfe\x12\x9e\x79\x36\x91\x27\x14\x31\x3e\x71\xff\xa2\xea\xb5\x5f\x6a\xf1\xc9\x24\x9f\x95\x94\x86\xeb\xdb\xea\xee\xb0\x95\xf9\x6b\x1a\x25\x9d\x56\xcb\xad\x5c\x3d\x8a\xe3\xe4\xc6\xf1\x84\xaf\x37\x40\x36\xec\xb0\x65\xde\xed\xe1\x1e\xe1\xab\x9a\x24\x2d\x0e\x8c\xbe\x2a\x14\x7a\xfc\x0d\x69\xe0\x86\x6d\xc3\xab\x85\x6e\xcf\x6a\x05\xb7\xaf\x36\x12\xc4\xb5\xd3\x65\xb1\x58\x16\xaf\xd2\xa9\x66\xd7\xc2\x17\x0f\x5a\x2d\xd2\xf9\x0f\xf7\x33\x83\xc4\x32\x13\x4c\x73\x6b\x18\x93\xed\x06\x8a\xc3\xf0\x5b\x2e\x83\x9f\x66\x34\x5c\x8e\x29\x9a\xab\x60\x3c\xee\x11\xe1\x8a\x12\xf3\x93\x60\x3c\x3e\x16\xc9\x9c\x27\x32\xa4\x88\x6f\x49\xe5\x4f\xcc\x29\xeb\xe7\xb3\x68\x52\x74\xba\x64\xe4\x60\x54\x66\x39\x4a\xab\x60\x3c\x96\x5a\x2a\x6e\xec\xcd\x49\x9b\xc6\xb4\xa0\x72\x1c\xda\x49\x92\x99\xce\xa9\xea\x1a\x2c\x03\xdd\x5f\x89\x77\x25\x62\x69\xb3\xad\x9e\x8b\x71\xa5\x8e\x15\x6e\x4b\x2e\x32\x1a\xae\x16\x7e\x3c\x8e\x1b\x6c\xe9\xe7\x8f\xee\x91\x69\xab\xde\x23\x53\x55\xf1\xcd\x72\x1b\x3b\xb3\x02\x62\x48\x80\x86\xef\x07\x5b\xec\xb0\xdd\x3e\x39\x02\xe5\x1f\xca\xff\x53\x29\x2d\x63\xd3\xff\x06\x8f\x1a\xad\x57\x6d\xde\x17\x8d\x95\xd4\xf8\xb5\x9c\x4d\x31\x50\xf3\xe4\x5a\xc6\x01\xa5\x7d\x21\xb4\x74\x8c\x00\x4e\x0c\xea\xf5\x01\x60\xff\x55\x9a\x28\xbc\xa0\xc7\x8a\xdd\xf3\xb6\x4f\x4a\x07\x60\x58\x4d\x78\xef\x84\x0d\x5c\x22\x8f\x58\x55\x57\xc2\x75\x7e\xb2\xae\xe9\x1a\xeb\x71\x13\x05\xfc\x4d\x7d\x5d\x0e\xfc\xba\xc9\xd7\x9c\x06\x3d\xfa\xbf\xea\x40\x22\x38\x86\xc8\xda\x60\x40\x3e\x1c\x3e\x3b\x1c\x91\x8c\x72\x83\xac\x1e\xc9\x53\x61\x3a\xa3\xae\xb8\xb4\x2d\x4e\xc0\x35\x5d\x7d\x56\x2e\x2a\xda\x39\x49\xe8\x98\xe6\x79\x90\x5d\xb2\xc5\x02\x11\xb0\x73\x46\x6e\x6d\xf0\x57\x0c\xde\xa2\xc9\x79\x9a\x7d\xe2\x52\xde\x7c\x19\x17\xd1\x22\x46\x91\x1c\xcc\xd8\x29\x7e\xf7\x46\x83\xfb\xc4\x6b\xcb\xfd\x9d\x34\xe5\xe6\x75\x98\x66\x0c\xb2\x79\xc3\x86\x54\x37\x46\x43\xbe\x71\x98\x27\x13\x55\xaa\x2f\x71\xe4\x73\x60\xb3\xce\x3a\x77\xec\xc2\x9e\xf8\xce\x0f\x65\xb0\x16\x3b\x25\x8e\x7d\xa3\xd9\x4f\xe1\xcf\xc9\x57\x53\x8d\x19\xa4\xb7\x9e\xd2\x23\x94\xae\x5f\x10\xbc\x3d\x26\x07\xc0\x73\xe4\xe6\x39\x3e\x6c\xf0\x1c\xc5\xf4\x84\x49\x8f\xd9\x45\x8f\xe5\xa7\x28\x96\xd3\xc2\x8a\x14\xe3\xf3\x71\x55\x79\x10\xab\x9e\xee\x88\x56\x8c\x57\xc3\x78\x86\x5c\x46\x2f\x44\x07\x39\xb9\x5c\x79\xd8\xaa\xe0\x2d\x0c\x9c\x20\xbb\x51\x7a\xd1\x37\xd8\x91\xfe\xd8\x21\x12\x40\x72\x21\xf8\x7f\x47\xa6\x2a\x96\xc3\x7f\xa8\x74\xc4\x68\xe4\x4f\x53\x8e\xa4\x17\xe2\x79\xb7\xcb\xcd\x39\x1a\xb4\x67\xa2\x12\xfe\x5c\xc2\x91\x5b\xa3\x6d\xf0\x60\x84\x9d\x86\x33\xc6\xfc\xc3\xdd\xcd\xe8\xdd\xcd\xe8\x5f\xfb\x66\x54\x5c\x8b\x8a\x27\xbf\xff\x15\xf1\xf5\x6e\xd5\x63\x38\x1c\x02\xee\x93\xfd\x34\x39\xa3\x8c\x15\x05\x22\xe4\x31\x9c\x83\xe1\x2c\x00\x71\x8b\x65\x20\x17\x46\xc0\x41\x9c\xa7\x24\x88\xe3\xf4\x3c\xe7\xe1\xd9\x41\x51\x97\xf7\xd7\x58\x45\x52\xf0\x7f\x1d\x5d\xd0\xf0\x8a\x67\xad\xb9\xf7\x1a\x6b\xe2\x46\xb5\x48\xed\x20\xc7\x42\x65\xa9\x0e\x9c\x1d\x53\x25\x4a\xbe\x7c\x91\x01\xd2\x75\x46\x5b\xe9\x50\xdb\x5d\x5b\x19\xc0\xcf\x72\x42\x44\xe2\x8a\x59\xde\x87\x8e\xd4\x2f\x1a\x0d\x71\x3d\xc4\xe1\x04\x54\xcd\x5d\xa8\x7d\xe8\xd4\x09\x90\x82\xef\xe3\x17\xad\xc6\x9d\x91\x0c\xa2\xa4\xda\x81\x23\x17\x13\x35\x19\xa7\x95\x97\x3f\xb6\x25\x6c\xaa\xf4\xfb\xe2\xb0\xd5\x63\x93\x70\x46\xb3\x68\x02\x7e\x3d\x32\x3a\x0e\x18\xc7\x41\x81\x6a\xee\xdd\x23\x71\xf0\xdb\x25\x89\xd3\x20\x24\xe1\x65\x12\xcc\xa3\x31\x49\x13\x9a\x43\x6b\x62\x42\x74\x43\x22\x98\x75\xaa\xf4\x04\x00\x25\xed\xeb\x65\xe3\x0e\x14\x9b\xad\x29\x2d\x0e\xd5\x21\xd9\xe3\xc1\x99\x4d\x8c\x16\x58\xeb\xdc\x03\x60\x65\x82\x98\x12\x79\x4c\x2e\xbf\xf5\x30\x34\xfd\xa5\x57\x2f\x3c\x3b\x3f\x8f\x20\x5e\x09\xea\x15\x01\x1d\x44\x4e\xf9\x09\x7a\xe4\xbc\xac\xe2\xc2\xfb\x32\xa3\x42\xbd\xd8\x83\x0b\xbc\x31\x5f\x1d\xfc\x70\x3c\xa3\x17\x3e\xb5\x81\xd6\x9a\x5a\x09\x96\x27\xca\x06\x45\x0c\xcd\xa7\x08\xab\x5d\xaa\x94\xb7\x14\xfe\x32\x08\xf7\x13\x11\x9e\x9c\x55\x25\x16\x59\x97\x8c\xe4\x7a\x13\x60\xae\xac\xe4\xbb\x26\xf0\x3c\xaf\x83\x6e\x8e\xac\x6e\xf7\x1c\x38\xb6\x04\x34\x14\xfb\x72\x61\x8a\x14\xd7\xe3\xe6\x07\x32\x2a\xb3\x04\x0a\x70\x4c\x66\xbb\x35\xb8\xbf\x1a\xad\x74\xad\xd5\x57\xe5\xba\xbe\xde\x5d\xa7\x46\x51\xca\xd4\x4f\xa1\x83\x0e\xa7\xc0\x7c\xc6\x28\xd0\x83\x70\x8b\xd4\xa5\xaa\x66\x2f\x0c\xf9\xb3\x08\xa5\x44\x0b\x92\x90\xe4\xb4\xc8\xc9\x72\x01\x19\xe2\x34\x02\x2c\x23\x2a\x68\xc6\xf6\x8e\xf4\x4c\x08\x5b\xc2\x8d\x69\x7f\x6d\x0d\x3d\x8d\x78\x95\x4e\xf3\xbd\xe2\x7d\x11\x64\xc5\x9a\xad\x69\xcc\x69\x3c\x51\x89\x13\xf7\xfd\xb2\x60\xe1\x66\x2d\x46\x9c\x30\x1a\x4f\x1c\x1f\x3e\xf2\x91\xdd\x94\x16\x5c\x9f\xc5\x0a\x5b\x2f\xed\x40\xbf\xa0\x87\x99\x43\xf7\x88\x3c\x79\x5a\x3c\x83\xb5\xd2\xf7\x31\x0e\xc8\x98\xd2\xa2\x63\xbd\xf9\x11\x96\x8c\xce\x29\x67\x30\x20\x61\x9a\xb4\xc5\x2b\x51\xd6\x47\x81\x36\x30\x9b\x84\x8b\x6e\x99\x28\xcd\x8e\xc0\x13\x46\xbf\xdf\x27\xbf\x2e\xb9\x23\x60\xd6\x26\xe3\xbd\xce\x79\xb9\xe4\x61\x64\xc5\xa3\xc8\x2b\xfb\x05\xac\xb5\xd2\xd5\x30\xfc\x67\x4c\x9e\xe9\x3d\x98\x72\x43\xce\xba\x67\x9a\xfc\xf1\x8e\x69\xf6\x69\xf4\xaf\xde\x0f\xeb\xd7\x23\xdd\x45\x1a\xc7\x9c\x7c\xfc\x64\x2b\x68\x53\x83\xd9\x74\xa9\x54\x22\xa0\xb6\x4d\x5e\x2b\x33\x5c\x83\x58\xd2\x12\x72\x11\x33\x9a\x3a\x73\x2a\x8d\x2c\x18\xe9\xc9\xb1\xfa\x26\xc1\xf7\x6c\xca\x47\x13\x69\xe3\x93\x7c\x53\xea\xb8\x1e\x65\x68\x33\x65\x18\x9a\x56\x5e\x3f\xb1\x12\x74\x25\x23\x59\xc8\x25\x9d\x1b\xa1\xe7\x66\x44\x5a\xaa\x0f\x80\x3e\xd9\xce\xa8\x19\xe3\x79\x9b\xc6\x31\xe3\x33\xba\x27\x9c\x06\x47\xbc\x08\x3b\xa7\xd1\x39\x4d\x0a\x38\x72\xf6\x19\xc5\xc1\xd0\xf4\x5e\xb2\x10\x86\xf6\xc7\x1c\x53\x40\x8e\x07\xe1\x49\x4f\x5e\x51\x19\xc9\x3d\x4d\x8c\x22\x07\xbb\x31\xe2\x0a\x62\xa0\x5f\xb6\x59\xcb\xa8\x85\x0e\x89\x5b\x32\x59\x8f\x38\xe1\x3d\xe4\x72\xf3\xdc\x0e\xf4\xc4\x69\x6a\x3f\xa3\x30\x26\xb0\xd7\xde\xf7\x3c\x74\x04\x66\xc7\x35\xd8\xe8\xc2\xd5\xc0\x07\xd2\xf0\xad\xa2\x2a\x2b\xd5\x75\x95\x2a\x7b\xfc\x4a\x35\xb3\x33\xc8\x96\x80\x94\x7a\x8c\x2f\xb5\xc6\xd4\xc2\xa6\x16\x83\x2d\xd1\x17\x41\x3b\x68\x30\x13\x10\xa4\x9c\x79\xf7\xc9\x98\x5a\x21\xc2\xb2\x46\x65\x88\x2d\x77\xbf\x2c\x5f\xb3\x3d\x27\x0b\x5f\x3b\xa9\xdf\xa5\xfd\xee\x27\xf4\x5c\xdc\x3a\x61\x1c\x60\x5f\x61\x9c\x49\x46\xa1\xe1\x1a\xcf\xcf\x1c\x6b\x96\x7d\x67\x7c\xea\x11\x73\xc7\xa7\xb5\x7c\x90\x08\x8e\x2c\xce\x85\x15\xd4\x6b\x39\x24\x75\xd9\x4b\x45\x59\x7f\x37\xaa\xf5\xce\xc6\xd2\x66\x44\x10\xba\x7e\x00\xb1\xab\x86\x8c\xc2\x25\x03\x3b\x73\x2c\x68\x12\x82\x81\x9b\x9a\xe4\x20\x07\x45\x4b\x92\x33\x0a\x55\xbe\x60\x74\x45\xe9\x04\x80\x59\x21\x26\xf5\x74\xb9\x72\x45\xb5\xbe\x4c\x82\x3c\x8f\xa6\x09\x0d\xfb\x6e\x1f\x6d\x8a\xf2\xf1\x64\xdf\xec\x28\x19\x6b\x7c\x5a\x33\x41\xde\x66\xb0\xc9\x18\x1a\x89\xb6\x27\x26\x31\x96\x0e\x83\x38\xa3\x41\x78\xa9\xdf\xab\x6b\x41\x31\xbf\x39\xa5\x99\x82\xac\x94\x5e\xeb\xc6\x15\x4d\x3a\x56\x6b\xca\x07\xdc\xd0\xf5\xc8\xa5\x57\x26\xe7\xe2\x3e\xb7\x90\x4c\x8a\x2e\x52\x31\xb6\x68\x3e\xa7\x61\x14\x14\x34\xbe\xb4\x9b\x15\xe4\x3e\x6e\x4a\xdb\xa6\x74\x02\xd5\x77\x4a\x3c\x4d\xf8\xbc\x56\x61\x4d\x36\x67\xf9\x6c\xfb\xe1\x83\x41\x77\xb9\xe7\x4e\x94\x0e\x7b\x33\x37\x79\x1b\x37\xec\x43\xfd\x90\xea\x18\x83\x39\xe2\xd1\x58\xf3\x24\xae\x4b\xdd\x81\x20\x5c\xa3\x3b\xe1\xab\xa6\x03\xc1\xfb\x6e\xfd\x78\x1c\xc9\x21\x5d\x48\xc1\xc1\x1c\x48\x0d\x7f\x87\xa7\xe5\xf3\xf4\x4c\xaa\x34\x49\x90\x5f\x26\x63\x75\xf8\xf1\x09\x46\x3e\xbe\xbd\x4c\xe0\xed\xb4\x81\x00\x24\x63\x58\xd8\x72\x78\x17\x36\x84\x5f\xa5\x66\x43\xf0\x77\x30\x3a\xb5\x42\xb6\xfb\x9c\x27\x38\x32\x85\xd7\xe4\x44\x95\xb4\x85\x72\x6b\x47\x2d\xb1\xa3\x1c\x0c\xc8\xc1\x44\x73\xc6\x28\x57\xef\xfa\x2e\xa9\x70\xbf\x42\xa2\x82\x68\x2f\x5d\xba\xdc\xf9\x8c\x82\x31\x86\x18\x7d\x97\x70\xa6\x9a\x93\xa8\x30\xd9\xaa\x77\xa3\x76\x88\x5d\x2d\x33\xdf\xee\xe1\x43\xbf\xa8\xd1\x9e\x50\xbc\x1f\x43\x84\x14\x0f\x7f\xfb\x8a\xfe\x79\x2c\x79\x3c\xa3\xb6\xf5\x5e\x9c\x4e\xcb\xda\x25\x16\x63\xaa\x38\x5b\x40\x2d\x23\xb6\x27\x94\xb8\xe3\xf3\x07\x2c\x31\x41\x9c\x03\x80\x3d\xb0\xe6\x74\xe4\xb8\x99\x12\x82\xf8\xc1\x33\x9e\x30\x12\x34\xd6\xe9\xf6\xf9\x8e\x3c\x0e\xa4\xc3\x42\x70\xab\x42\x43\xc2\x56\xf7\x2c\x4b\x93\x74\x99\x2b\xef\x85\xc2\x30\x80\xed\xf6\xb6\x27\x22\x5e\x8d\x10\x76\xdb\x5e\xf3\x5a\x70\x2a\x91\x6a\x2b\xbd\x26\x04\xe4\xda\xd0\xb1\x1a\xea\xe7\xf0\x06\xf3\x76\x55\xc3\x8f\x9d\x2b\x52\x8e\x5b\x27\xf6\x5b\xc5\x05\xe9\xd5\x49\x6f\x7b\xd8\xe4\x0a\xb4\xbd\xcc\xb9\x5e\x7c\x5c\xb4\xd7\xee\x2e\x44\xef\x2e\x44\xff\xc4\x17\xa2\xfa\xa9\x28\x52\x59\x5f\xe7\xbd\xa8\x00\x5e\xe1\x26\xd3\x17\xfb\xad\xf1\x13\xd3\x64\x12\x4d\xbd\x70\x3c\x4b\x02\x1e\x9c\x06\x56\x4c\x97\xe8\x34\x48\x3c\x71\x5a\x40\x9b\xcc\x03\x4d\x71\x1b\x69\x7e\x99\x79\x1a\x4d\x85\x07\x03\xcb\x8a\x91\x03\x3d\x8d\xa6\x96\x52\x1f\x5b\x33\x72\x8d\xf3\x17\x0e\xf1\x45\xc1\x5e\x99\x4e\xab\x74\x3a\xb6\xc4\x05\x3d\x63\x49\x1b\x86\x54\xc4\x7b\xe7\x7d\x86\x56\xa4\xaa\xac\x04\xdb\x51\x4a\xa0\x28\x7f\x9b\x51\x71\x0d\x8a\x6e\x27\x8c\xba\x4f\x75\xba\xd5\xc0\xb1\x72\x77\xdf\x16\x27\xcf\x76\xaf\x4d\x83\x2c\x8e\x78\xe2\x38\x9d\xcf\xa3\xa2\xa0\x61\xfb\x44\x5d\x91\x1a\xb5\xfd\xb4\x4b\x86\xa8\x33\xc9\x62\x59\x3c\xa3\x93\x60\x19\x7b\xaf\x4a\xea\x7a\xc5\xf6\xe0\x53\x3c\x08\xfc\x50\xc6\x1b\xaf\x85\x11\x49\x3f\x44\x2d\x7a\xbc\x4d\x95\xdf\xdc\xe0\x2e\x58\xa3\xf8\x3d\xba\x6f\xbf\xe1\xe2\x22\x09\xab\xa5\x64\x56\x8d\x46\x3d\x15\xa2\x6c\x0f\x1e\x24\x35\xbd\xa4\x17\x55\x23\x7f\xbe\x48\xc7\xb3\xaa\x91\x53\x0d\xc0\xfb\x00\x22\xa6\x4e\x74\xdf\x37\xd9\x99\x2d\x4e\x75\x2d\x8b\x1a\x65\x32\xeb\x3b\xeb\xb9\xa7\xdf\xb8\x6d\xc3\x9c\x99\x77\x35\x47\xe6\x9b\xe9\x84\x04\x86\x13\xc3\x20\x09\xe5\x9d\x6e\x0e\x77\x3a\xdc\x82\x81\x71\x88\x97\xcf\xff\x69\x31\x06\xa8\x83\x49\xf0\x5e\x96\x20\x6f\x1d\x0c\x67\xc0\x8e\x89\xbe\xbc\xcc\x97\xf7\x12\x6e\x9d\xde\x10\xe5\x9f\x8d\x6b\x6e\xb8\xa8\x44\x97\xc5\xf0\xf9\xe5\x8b\x45\xfb\x7b\x63\x08\x10\x81\x5c\xb4\x61\x78\x8f\x6f\x30\x59\x2d\xf4\x49\x38\xcc\xf2\x5f\x92\x9a\x12\x1b\xae\xba\x48\x45\x64\xeb\xa8\x20\xf3\x68\x3a\xe3\x22\xae\x72\xb3\x2c\xd4\x69\x4e\xcb\x45\x5a\xdb\x6e\x91\x9a\xad\x1e\xb7\xa7\x41\xfe\x36\x8b\xc6\xb4\xdd\x23\xec\x37\xfb\x0f\xa6\x8f\xfd\x48\xd2\x64\x4c\x7d\xef\x28\x3f\xd1\xcb\x8a\x97\x94\x9f\xe8\x65\xd3\xb7\x94\x50\x93\x83\x43\x5e\xc3\x2e\xb2\xfc\x78\x46\xc7\xd1\x3c\x88\x3b\x18\xc0\x7d\xcb\x66\x5e\xf7\x7e\x6d\x22\x46\x4e\x3f\x6f\x9b\x96\x7d\x55\xdf\x3e\x49\x5f\x97\x6a\xbf\x29\xbd\xce\x83\x8b\x17\x94\xbe\xa5\xd9\xcf\x9c\x58\xe7\xc1\xc5\xdb\x2c\x4a\xb3\xa8\xb8\x34\xd2\xff\xeb\xe8\x5a\x88\x65\x0e\x61\xc3\x0d\xb0\x8c\x66\x24\xa8\xda\x2b\xac\x35\xa6\xe7\x0b\x53\x40\x13\xe9\x6b\x86\x54\x56\x4b\xc1\xc5\x45\xf7\xb3\xd2\x4d\x5e\xf4\xf1\xf6\xbe\x2e\xf5\x03\x5a\x27\x67\x02\x28\x1f\x1d\xa9\xc4\x9f\x09\xa0\x5e\xa1\xb0\x74\x84\x0b\x78\xef\xe6\xaf\xde\x81\xf2\xb6\x61\x43\x49\xf5\xe3\x45\x1f\x48\xca\x5f\x08\xb2\x34\xe4\x34\xc8\xfd\x70\xd3\x20\x37\xa0\x80\x7c\x11\xa8\x16\x56\x51\xbe\x31\x54\xbc\x36\x4c\x42\x35\x14\x9c\x16\x60\x49\x0b\x18\xc6\xf0\x35\xaa\xda\x72\xd6\x5d\x5d\x9b\x6e\x81\xf2\xb6\x1d\x58\xa3\x0f\xc5\x45\x5f\x9a\x1f\x7a\x2b\xc0\x8f\x9d\xa5\x2e\xe4\x62\xe5\xa5\x23\xe3\x04\x5d\x67\x09\x89\x90\x45\x95\x2b\x49\x45\xd0\x5a\x65\x39\xd9\x15\x5b\x8e\x73\x70\xe8\x23\x1d\xea\xa8\x66\x7d\xf9\xa0\x5c\x1a\xf5\x40\x69\xf2\x93\x99\x0d\x96\x5b\x29\x68\x79\x93\x25\x0b\x4f\x45\xe4\x59\xce\x97\x71\x50\x44\x67\xf4\xe7\x20\x3f\xca\xe1\x65\x61\x59\x55\x0e\xac\x55\xd7\xb4\xb6\x86\xa9\x2a\x27\x07\x6f\x1a\x8b\x48\xb8\x38\x9d\xda\xb6\x97\x3a\x03\xc5\x13\x72\x94\x83\xa0\xe1\xf3\xaa\x06\x3d\xef\x93\x19\x6c\x9d\x1e\x50\xb4\xd4\x68\x01\xc0\xec\x36\x27\x79\x38\x45\x55\x52\x39\x54\xd8\x80\xc6\xcd\x9a\xb0\x85\x11\xd4\xa0\x4c\x8c\x06\x03\xa2\x9c\x33\x81\x97\x42\xa1\x7f\xc0\x07\xc5\xfe\x69\x90\xd3\x06\x7c\xc9\x07\xec\x63\x29\x1e\x38\x83\x1f\xf1\xfc\x69\x90\xbf\x8a\xe6\x51\xe1\xa1\x1d\x13\x40\x94\x55\x89\x25\x04\x67\xe4\x1b\x65\xf2\xe8\x37\xdf\x6e\xa3\x33\x0d\xe8\x22\x9a\xd3\xbc\x08\xe6\x8b\xd2\x22\x0a\x42\x2f\x68\x9e\x91\x94\xb1\x0c\x23\xbb\xac\x5a\xa5\x55\x41\x9d\x09\xa3\xc9\x24\x1a\x2f\x63\x78\xd9\x53\x86\x69\x0d\x64\x0e\x24\x2d\x82\xf8\x59\x93\x0a\x2c\x48\x2c\xb5\x9a\x8b\x55\x80\x6b\xfe\x62\x2e\x59\x37\xdb\x95\xf5\xa2\x82\xce\xbb\xf6\x9b\x3e\xc7\xb0\x12\xa0\xdc\x2b\x6c\x63\x61\xfb\xa4\x26\x5e\xb0\x6e\x85\x9f\x72\x1d\xcd\xd5\x4e\xa3\xe5\xfd\x3e\x9a\x26\x34\x23\x71\x94\xdb\xaf\x8f\x57\x5a\xd4\xbc\x9a\xdc\xbf\xb6\x89\xbb\xb8\x05\x7c\xf9\x1a\x17\x00\x5a\xfb\xe1\x99\x2b\x09\x23\x67\x09\x27\xd6\xcc\x4d\xfd\xac\x08\x6c\x72\x4d\xe7\x21\xf4\x5c\x06\x55\x40\xd3\xc0\xa7\x00\x69\x52\x70\x1f\x1a\x31\xd9\x38\x9d\x7a\x11\x8f\x39\xbb\x0f\xed\x71\x3a\xd5\x5a\x50\x17\xe9\x50\xaf\x81\x77\x5c\x21\x46\x37\xba\x7d\x8a\x26\xec\xcb\xd8\xd5\x15\x3e\xac\x0c\xcf\x42\xb7\x8b\xee\xe0\x3a\x9d\x6d\xdb\xa8\xb8\xc1\xfe\xef\xad\xc4\x68\x22\x4e\xa7\x9e\xaa\x65\x6a\x49\x95\xaa\x90\x79\xc4\x82\x1b\xb5\x7a\xb5\xc1\xf9\x2c\xca\xd9\xae\xb8\x48\xf3\xe2\x1a\x7a\x83\xb7\x69\x5e\x2d\x16\xba\x91\xb0\x2a\x77\x4f\xb7\x52\x3c\xd1\xac\x93\x78\xeb\x64\xdf\xfd\x45\x70\x09\xcf\x5b\x76\x0d\x1d\x20\xce\x12\x48\x86\xa4\xa2\x88\xbd\x87\x56\x99\x89\x61\xcf\xd3\xec\xd3\x87\xf4\x6d\x96\x9e\xd1\xf2\x32\x08\x08\x97\x5d\x08\x91\xbf\xbc\xa0\x84\x40\x01\x1e\x26\x38\xfe\x97\x61\xd0\xce\x79\x06\xef\x24\xf7\x76\x83\x19\x3b\x4a\x27\xbb\xc6\xd7\x13\x72\x8c\x3e\x4f\xc8\x48\x59\x93\x5c\xe9\x56\xf9\x55\x08\xbf\x15\x89\xe3\xf4\x1c\x5e\xf7\x48\xe5\x4e\x55\xf5\xd5\xaf\x51\x78\x04\x4b\x46\x4c\x24\x4d\xe2\x4b\x1e\x96\xa3\x30\x1e\xc9\xc8\x87\x2a\xfc\x41\x8a\xef\x7d\x95\x7c\xad\x42\x46\xf6\xdb\x29\xfc\x4e\xc5\xd6\x2f\xb0\x3e\x36\xe2\x5d\xea\x5a\x0e\xe8\x5f\x18\x0b\x7b\xb9\x59\x1d\xa5\x37\xd9\x38\xaa\x09\x5b\xd0\x35\xe0\x97\x5e\x2c\xa2\xec\xd2\xb3\xe2\x51\x2e\x26\xb7\x9c\x7b\xf1\xf1\x42\xb3\xbc\xb2\x25\x60\x81\x7a\x16\x00\x50\xb6\x4f\xa0\xb3\x20\xba\x3b\xbe\x55\xf9\x2e\x38\x97\x24\x23\x52\xbc\x60\xa8\xfa\xbd\x7c\x1c\x45\xf6\xf2\x95\x65\xf0\x36\xfa\xf7\x5c\x20\x4e\xc1\xe9\x38\x38\x7a\x55\xe8\x06\xc0\xad\x35\xc4\xa2\xf3\x31\x87\xc1\x60\x95\x15\x01\x6b\x13\xaf\xc6\xd2\xc5\xa8\x97\xdb\x0d\x56\x92\x75\xc7\xc1\x51\xd4\x8c\xfe\x15\x53\xb5\xd5\x92\xbe\xa0\x35\xf8\x8e\x4a\x24\xf5\xf3\xe5\x29\x7f\xf0\xd7\x19\xf6\xb6\xf9\xaa\x6c\x5d\x84\xe3\x96\xe1\xbc\x49\x79\x87\x6a\x0d\x2f\x5a\x64\x83\xb8\x85\xb7\x8d\x23\x06\xf4\x8a\x5f\xd7\x26\xf4\x1c\x6e\x6e\x3b\x66\xcc\x74\xb8\xe0\x3a\x0d\x92\x7e\x94\xff\x23\x88\xa3\xb0\x03\x31\x4d\x44\xca\xb3\x28\xa3\xe3\xa2\xe3\xbb\xdd\x12\xae\xe3\x00\x50\xd4\xd8\xe9\x3a\x57\x67\x58\x70\xd2\xa1\xa6\x64\x0f\x3c\xd5\x1a\xde\x09\x3d\x15\x35\xa8\x42\xf4\xcc\xac\x89\x2b\x80\x6c\x5b\x21\xe1\x3f\x5e\xc2\xb6\x65\xec\x77\xcd\x49\xde\x5f\x26\xe3\x28\xf1\x8b\x43\xc2\x61\x3b\x9a\xcb\x75\x33\x89\xb8\xfe\xab\x0c\x21\x1c\xbc\x5d\x81\xb1\x69\x94\x4c\x41\xd8\xf5\x2a\x10\x5c\x30\xd3\x67\x98\x70\xdf\x55\x53\x01\x86\x32\xcb\xcf\xa2\xe9\x8c\xe6\x75\xe5\x31\x14\xa2\x1d\x91\xfb\x29\x49\xcf\x93\xf7\x45\x50\x50\x9f\xff\x48\x94\x5b\xde\x00\xae\x62\xc7\xae\x61\xb1\x8c\x63\x1a\xd6\x55\x81\xa1\x4a\x74\x1a\xda\x8d\x58\x49\xa4\x88\xba\x6b\xf3\x51\x2d\x44\x4f\xd7\x53\x51\x41\x4d\x49\xdf\xc5\xef\xa8\x3c\x0b\x95\x34\xee\x34\x47\x9e\x34\x04\xeb\x3b\x3b\x8e\xca\xb3\x50\x49\x9b\xcd\x8d\xfc\xc9\xa8\x84\xb1\x29\x8f\x3c\x69\x1c\xb6\xcc\x40\x63\x54\x9a\x83\xcb\xf9\x07\x54\x9e\x57\x52\xd6\xd6\x97\x7a\xaa\xb0\x41\x8c\xde\x1b\x47\xe1\x91\x37\xd5\x81\xb7\x0f\xba\xa3\xaa\x4c\x5c\x1a\x1f\xd7\x46\x9e\x34\x0c\x6b\x4d\x82\x27\x11\x43\xdb\xdc\x6f\x54\x92\xce\xb9\xa6\x61\x13\xc8\xaf\x0f\x5b\xa3\xcd\xc7\x65\x9e\xae\xd8\xd6\xd1\x1a\x6d\x6f\x5f\x9d\xf4\xb6\x37\xef\xbc\xa4\xdc\x19\x05\xfe\xd7\x18\x05\x0a\x4a\xbf\x8d\x70\x47\xab\xc5\x86\x68\x68\x09\xc8\xa3\x31\x99\x26\x7e\x3c\xed\x2b\x04\x99\x68\x1e\x16\x22\x88\xe3\x81\x15\x38\x15\xde\x7b\xdb\x61\x97\xdc\x60\x11\xf2\xd1\x82\x1b\x61\xae\x22\x48\x84\x2f\xc4\xdc\x47\xbe\x35\x8a\x20\x06\x38\xb6\xf2\xea\x01\x06\x74\xa5\x62\x6f\xc1\xb5\xf2\xa4\x9b\x55\x0b\x71\x19\x03\x38\xaf\x42\x9d\xf2\x1b\xc3\xc8\xd8\xcb\x02\x44\x7c\x62\x88\x5b\x09\x70\xc1\xf6\x07\x7b\x32\x0c\x57\xa8\x60\xf5\xa1\x9f\x08\xe2\x23\x53\x36\x35\xce\x4b\xd7\x88\x28\x2e\xcf\x16\x3a\xfc\x22\xf8\x19\x01\x5e\xcf\x1f\xb5\x65\xd3\x9c\x47\xb1\x58\x17\x42\x63\xb3\x0e\x63\x21\xb0\xb2\xd3\xb8\x7b\x3f\x39\xa4\x24\x73\x70\x30\x49\xf1\x7a\xd6\x1d\x9c\x7f\x6c\xb6\x6f\x8c\x0a\xf1\xb4\xa3\xf1\xd0\x10\x11\x55\x21\x23\x71\x90\x6b\x5f\x5c\xb4\x28\x27\xe3\x34\xcb\x5c\x97\xa5\x70\xf2\x0a\x0a\xba\x97\x4d\x73\x5f\x14\x49\x1d\xc6\xfe\x3e\xf9\x1b\x9c\xdc\x72\xf2\x19\xce\x6d\x57\xac\xbd\xa8\x10\x6f\x86\x0c\xaf\xa6\x9e\xa9\xc2\xed\x94\xce\x91\x3e\xbd\x73\x28\x40\x91\x63\xe9\x13\x68\xc4\x0f\x06\xf2\x71\x18\x68\xba\x0c\x77\x41\xb0\x79\x82\x93\x4a\x1d\x1d\x8e\x6d\xb5\x01\x3c\x2d\xcd\x82\x4b\xf9\x50\x52\xcc\xdd\x7a\xc7\x09\x2e\x1a\x74\x95\xcb\x7b\x76\x1e\x77\x2e\x80\xac\x3b\x0e\x01\xce\xdd\x57\x57\xc2\xeb\x2b\x2f\xa3\x8c\x55\xc0\x7a\xa5\x0d\x3a\x02\x89\x1d\x49\x88\xeb\xbb\xbb\x65\x84\x6c\xbe\x8c\x63\x67\x6e\x11\xdf\xaf\x22\x0a\x5f\xc7\x71\x58\x51\xe5\xe2\x59\x6a\x9b\xc0\x02\x0d\x93\x8a\x11\xa4\x24\x7d\xcb\xc1\x3c\xe4\xe5\x6c\x1a\xda\xb5\x7b\x89\xbb\xe7\x20\x56\xad\x6a\x83\xbc\x4a\xca\x53\xed\x57\x92\x9d\x11\xd6\x76\x75\x86\xb1\x2a\xbf\x30\xa3\xd1\x96\x84\xbb\xbd\xd2\xdc\x1c\x2f\x9f\x8e\x27\xf6\x6c\x91\xfa\x23\x4b\x18\xb1\x69\x77\x49\x49\xd4\x08\x5f\xf0\x01\xf1\x2e\x0a\x0d\xd7\x08\x7a\x5b\x61\xd9\x56\x12\x15\x49\xa2\xfe\x7a\xd1\x5f\xbc\xc5\x2b\xe7\xfd\x5a\x31\x60\x84\xfb\xfa\x61\x8f\x3c\x96\x5a\xa8\x8a\x26\x96\xc9\x22\x18\x7f\xe2\x77\x8d\xa6\x8d\x27\x24\x19\x3a\x29\x33\x49\x77\xc1\xd0\x8f\xa4\xb2\x2a\xfe\x43\x91\xde\x2e\xd9\x22\x4f\x64\xa2\xf4\xb0\x4f\xe4\x39\x50\xbb\x9c\x50\x7e\xf1\xcb\x1c\xec\x63\x21\xa7\x27\x8a\x9b\x33\x2a\x74\x38\xd8\x3d\xb8\x8a\xad\x78\x3c\x3c\x21\x23\x9f\x13\xf8\x7d\x08\x2d\x1e\xa0\x68\xee\x12\x59\x76\xbc\xf8\x20\x8e\xf1\xe2\xee\xf7\xfb\x72\x7d\xef\xdb\x65\xad\xcd\xc7\x71\xbf\x74\xc0\xb7\x3b\x08\x1b\x2d\x41\xd9\x6e\x14\xa8\x1a\x7a\xdc\xd3\x8e\x5d\x31\xf7\x35\x08\x6f\x5b\xe5\xa1\x2b\x30\x5e\x1f\x06\x49\x68\xfa\xe8\x91\x60\x3c\xae\x3a\x3f\x19\xb1\x3a\x78\x50\x4a\x06\x2e\xd0\xe6\xa5\x5d\x31\xab\x10\x17\xba\x8e\x6a\xa1\x57\x65\xb1\xb7\x57\x09\xac\xed\xdf\x37\xa5\x0c\x66\xd9\xc2\xaa\x3d\x06\x0e\x32\x5a\xfe\x13\x2e\xb9\x0d\xb1\x10\xb3\x1f\x70\x2b\x6e\x4a\x5f\xb8\x08\x16\x7f\xec\x62\xfa\xa6\x82\xbb\x02\x97\x5c\x5a\xc2\x69\xa3\x8f\x75\xdf\x53\x6f\xad\x1b\x56\x8c\x8f\x16\x33\x8e\x04\x51\x75\xcf\xe8\x9a\xfb\xb0\x13\x4a\xe1\x25\xdc\x31\xd6\x03\x72\x66\xef\x3c\xc5\x6e\xd2\x60\xcf\x75\xa0\xe4\xf2\x00\xe4\x3e\x49\xbe\xdf\x31\x9c\x6c\xf4\xb8\xe9\xce\x8e\xe9\xb4\x9a\x77\x9a\x86\x8e\x83\xfe\x22\xbb\xb4\xde\xa5\x22\x50\x78\x8a\x5a\x3e\x5e\x62\xbc\x9d\x1d\x83\xf3\x82\x8e\xe3\x02\x89\x53\xfc\x2e\xa1\xb8\x10\x2a\x65\x76\x5e\xb6\x8e\x24\x99\xca\x8d\xa2\xc9\xb9\xd2\xde\x36\xcc\x22\xb5\xbb\x82\xd5\xc2\x9f\x6a\xa9\xd5\xae\x19\x49\x52\x02\x50\x18\xda\xfe\x44\x86\x70\xa8\x31\xce\x9a\xae\x74\x88\x23\xd2\x06\x09\xf7\x03\x90\x84\xc2\x57\x28\x84\x14\x4e\x1e\xc8\x83\xaa\x13\x5b\xb9\x66\xb9\x1a\xf1\x04\xd9\xba\xb1\xe6\xa1\x63\x5e\x4f\x8a\xea\x6a\xc1\x9b\x07\x72\xa0\x79\x11\xcd\x83\x82\xfe\x1c\x80\x02\xb1\x8e\xaa\x10\x78\x1d\x45\xe1\x9a\x6f\x83\x9a\xbe\x3e\x75\x34\x9b\x21\x34\xae\xba\xd9\xf1\x80\x96\xcd\xcc\x3b\xd9\x0c\x95\xa1\xe9\x20\xc4\x8e\xd4\x05\x0a\xf9\x00\x4f\xc5\x94\x16\xcf\xec\xd0\x51\x72\x67\xb5\xab\xa9\x9b\x2b\x51\xd7\x2d\xcf\x53\x23\xc4\xcb\xab\x6a\xb1\x32\x79\xd4\x9d\xe6\x52\xf3\x0d\x02\x5c\xe2\xa2\x12\xcf\x88\xec\x2b\x11\xf6\xfb\x46\xbb\x54\xf5\x5f\x2b\xe0\xa5\x2a\xb4\xea\x20\xbf\x66\xf4\x4b\xad\xa3\x61\x03\xcc\x16\x63\xe9\x56\x2d\xe7\xa7\xe6\x3a\x46\x24\xa0\xcb\xcd\x6d\x2a\xc6\x25\xca\xfe\xb1\xb9\x12\x31\xa2\x00\x49\x30\x2c\xa6\x18\xc1\x6c\xf0\x9c\xb8\x7e\x08\x2d\x8d\xeb\x13\x70\x6e\xfc\x91\xf5\xb8\x4d\x46\xfc\xc3\xda\x49\xda\x3d\x47\x78\x19\x69\xff\x7f\x2a\x4f\x79\x2e\x14\xc3\x39\xd1\x59\xbc\xe3\xd2\x2f\x2e\x67\x90\xb5\xc4\x20\xc3\xf6\x94\x6d\x3f\x2a\x20\x56\xf5\xd6\xe3\x89\x5d\x85\x27\xb8\x30\x04\x9d\x75\x13\x3b\xda\xcc\x08\xb6\xf9\x02\xcb\x50\xd2\xf7\x8b\x4e\x2b\xdb\x2a\x2c\x74\xf6\x83\xc5\x22\xbe\x14\x9e\xa8\x1a\x11\x56\xd7\xb6\xcf\xe3\x5b\x80\xd5\x0c\x4b\xbc\x56\xdd\x35\xf3\x20\xe2\x3b\x69\xc6\xa3\x43\x3c\xdd\x38\xb6\x93\x67\xc2\xbe\x56\x78\x27\x99\xae\x57\x3c\x76\xc5\x55\x0a\x2e\x0e\x9b\x1a\xc3\x65\x80\xae\xd4\xec\x9d\xfc\xb2\xe2\xa6\x88\xc4\x47\xa2\x93\x4a\x8b\xe9\xdd\x5a\xba\x90\x62\x9f\x7f\xca\xd8\x56\xb2\x2c\x10\x78\x94\x8d\x97\x71\x90\xad\xaf\xaf\xaf\x57\x47\xb4\x92\x14\xb4\x73\x2b\x31\xad\xb8\xf6\xb7\x35\xda\x7a\xe4\x77\x10\xb4\x75\x77\xfb\x7f\x77\xfb\xff\xd7\xbe\xfd\x17\x57\xff\x0c\x56\xc6\x1c\xf3\x47\x4a\xf9\x66\x31\x50\x7c\x96\x05\xd5\x86\x00\x6b\x83\x01\xc4\x54\x0b\x32\x46\xca\x6c\x07\x5b\xe6\xe6\x10\x19\xc1\x85\xd1\x64\x42\x33\x9a\x14\x84\x26\x67\x39\x14\x3a\xcd\xd2\xf3\x9c\x66\x6b\xc8\x61\xec\x79\x94\x84\xe9\x39\x68\x2c\x50\x24\x11\x72\xef\x9e\xc8\xe9\xff\xf3\xf5\xab\x97\x45\xb1\x10\xbe\x88\x39\xd7\x34\xd3\xc8\xae\x1f\x16\x58\x9f\x08\x84\x11\x4d\x93\x94\x31\x82\x38\x4a\x28\xeb\x49\x92\x86\x74\x0d\x79\x9f\x73\x6a\x54\x03\xbf\x98\xc7\x6c\x64\x62\x63\x6b\x77\x9b\x36\x72\xc5\x31\xf9\xcf\x97\xef\xb6\x8c\xea\x66\xd9\x56\xbb\x5b\x5a\x4a\x4a\x0e\xac\x85\xb7\x12\x99\xae\x49\x04\xc8\x4f\x4c\xb4\x07\xf7\xab\xdc\x59\x3b\xeb\xa5\x32\x80\x30\xca\xe3\x2d\x7f\x96\xe6\x45\x8f\x14\xd1\x9c\xa6\xcb\xa2\xc7\x2a\xcc\x7a\xa0\x64\x3e\x4f\x33\xf1\xd8\x11\x36\x13\x06\x47\x76\x09\xfc\xf7\xe5\x0b\x69\x0b\x62\x8f\xd3\x71\x10\xb3\xc4\xd1\xe3\xef\x1e\x7e\x07\x81\x8b\xf9\xde\xc3\x2b\x64\x3b\xa1\xf8\xf5\xe5\x0b\x19\xaa\x6c\xd6\x0c\xd9\x85\xd6\x54\x9a\x6c\x94\xec\xaa\xf6\x6b\x85\xa7\x45\x46\x17\x10\x09\x90\x9e\x5b\x53\x66\xc9\x4e\x02\xf0\x1d\x3a\xcb\x08\xc9\xe9\x69\x9a\xc6\x34\x48\xae\xe0\x8e\x95\xed\xcf\x52\x82\xd1\x58\x16\x6e\x3f\xd1\x81\xcf\x6c\xcb\xf0\x2d\x85\x31\x8d\xe4\x2e\xb3\x03\xe6\x45\x20\xab\x9e\xa3\x9a\xdf\xa0\x70\x42\x5a\x13\xaf\xd8\x50\x36\x21\x5a\xbc\x82\x21\xbf\x7c\xb7\xa5\xe3\x06\x73\x49\x0b\x61\x1e\x4d\x04\x3c\x39\xc3\xce\x15\xad\x8a\x8c\xf1\x74\xc4\x0b\xb5\x35\x5d\x6b\xba\xa0\x49\xa7\xfd\xf6\xf0\xfd\x07\x19\xea\x94\x13\x0e\xef\xdc\xce\x1a\xf2\xd4\x08\x73\x7b\xef\x9e\x39\xa9\xc6\xa1\x6f\x09\x06\x35\xed\xa7\x41\x1e\x8d\x49\x9b\x6c\x40\x17\x9e\x2e\x19\x7b\x40\x55\x6c\x90\xf6\x48\x5d\x15\xaa\x7a\xfa\x45\x2a\x1e\xdf\xb5\x4f\x83\x9c\x3e\x7a\xd8\xb6\xc6\xaf\xfd\x94\xbf\xa4\x41\x48\xb3\x4e\x7b\x0f\xf8\x6a\xf4\x5b\xc0\x4f\x5b\xd0\x3e\x1f\x61\x45\x21\x26\x1f\xd3\xa4\x78\xc0\x0e\xda\xed\x1e\x69\x33\xc9\x3f\x1a\x43\x15\x83\x5f\x73\xa9\x76\x54\x37\x56\x62\xca\x6a\xc8\x95\x47\xb4\xb9\x4c\xc6\xe8\x50\x6d\x6b\x92\x7d\x17\xcf\x0b\x74\x7d\xed\x8f\x5d\x5e\x45\x7a\xb9\x1d\xcb\x52\xea\xd2\x6c\x92\x93\x34\x63\xd2\xaa\x08\x86\x0d\xf4\xa8\xb5\xfb\x1a\x73\x49\xd8\x81\x97\x1e\xfc\xdd\x41\x34\xb9\x54\xf5\x0b\x24\x4b\x45\x3e\x76\x43\xee\xb3\x06\xd8\x4f\x93\x84\x8a\xf7\x18\x92\xc2\x34\x25\x1a\x97\x8b\xb2\x75\x19\x10\xe4\x03\xbd\x28\x9c\x0e\x0a\x58\xf4\x0c\x45\x58\xe5\x9b\xdd\xaa\xea\xd2\x3b\x51\x7f\xc7\xd7\x20\x5e\x25\xcd\x63\x53\x03\x0d\x04\x35\x44\xb0\xa7\x38\x4e\x05\x25\x88\xac\x17\x4e\x34\x18\x52\x64\xd1\x74\x4a\x33\x1e\xc2\x8a\xcd\x3e\x88\x2d\xca\x1f\x2d\xc3\x41\x1d\xc1\x40\x0f\x7c\x54\x63\x46\xa2\x6e\x42\x3f\x60\xbc\xb2\x63\x70\x93\x04\x7c\x87\xe7\x45\x50\xd0\xf1\x2c\x48\xa6\x7e\x05\x02\x7f\x56\x20\x11\x1f\x84\x97\x60\xd0\x0f\x37\xc2\x0f\x19\x87\xb1\x59\xde\xba\x19\x49\xba\x01\xc5\x68\x40\x79\xab\x84\x42\x94\xd9\x97\x59\x35\x14\x05\x67\x32\xef\xad\x95\xba\xb1\x5a\x91\xb6\x08\xbe\xda\xb2\x2f\xb6\x8c\x96\xd9\x59\xf0\xca\x42\xb1\xde\x08\x5c\xcc\x9a\x95\xe5\x7d\xbd\xf4\x3e\xf0\x52\x1d\xbc\x79\x88\x85\x7c\xbb\x1c\xc0\xee\x42\x15\x13\x10\x2b\x0d\xaf\x2b\x7d\x59\x1e\x5f\x32\x7a\xe7\x8f\x66\x61\x71\x31\xaa\x2e\x59\x5b\x51\x2e\xea\xa7\x26\x33\x55\x42\x80\x54\x70\xda\xc2\x00\x3b\x3f\x24\xed\x82\x4c\x82\x28\xa6\x61\x9f\x1c\xb2\x73\xda\x79\xc4\xce\x1e\x01\x44\x9d\x2b\x5f\x4d\xa8\x4d\xcf\x5c\x68\x7c\x2a\x7d\x86\x8a\x6e\x12\x85\x23\xf2\x83\xfa\x93\xfa\x3e\xb6\xfb\x64\x8b\xf1\x88\xb4\xb7\xfa\x43\xa5\x3c\x94\xfa\xc7\x76\x42\x8b\x8f\x71\x94\x17\x34\x01\xbf\x91\x6b\x96\xf6\xf0\xc4\x30\xe8\x92\x0a\xae\x8c\x87\xd0\x73\xc9\x57\x5a\x15\xb2\x41\xea\x49\x70\xd4\x05\x78\xe8\x52\x55\x60\x9c\xf6\x99\x98\xdb\x1a\x3d\x66\xbf\x0c\xf9\xb9\x35\xda\xfc\x9e\x9d\xfc\xb7\xef\x4e\xfe\x77\x27\xff\xbf\xf8\xc9\x5f\x1b\xfe\xc3\x63\xc9\x5b\x32\xfa\x57\x86\x9c\xf8\x54\x79\x1a\x4d\xb9\x0d\x6e\xff\x57\x7e\x42\xe7\xf7\x20\xe1\x2b\x3a\x31\x37\x04\x15\x4b\xf4\x12\x3d\xd8\x33\x36\x4e\x0e\xc1\xd9\xc5\xf9\x8c\xf5\xbe\x63\x1a\x68\xfd\xc8\x0b\x93\xfb\x64\xcb\x7d\xf1\x07\x16\x7f\x4c\x8a\x37\xdf\x3d\x12\xff\x4b\x3c\xc1\xdc\xdf\x8a\x53\x5d\x90\x90\x83\xa7\x7b\x6f\xc4\x24\x87\xe4\x87\xef\xc9\x38\x9d\x2f\x96\x22\x8e\xcf\xe9\x25\x99\xa7\x67\x51\x32\x45\xd1\xea\x1e\x92\xf1\x2c\xc8\x60\x2f\xe0\x37\xb3\x21\x37\xa5\x92\xe6\xea\x12\x3a\xa6\xfc\xd1\x42\x91\xb2\x06\x39\xae\x72\xd2\xd9\x23\xbb\x64\x73\xd8\x23\x4f\xd9\xff\x9b\x3d\xd2\xef\xf7\x7b\xe4\xff\xc8\x2e\xd9\xfe\xae\xcb\x0e\x3b\x24\x5f\xd0\x71\x34\x89\xf8\x42\x3a\x78\x7f\xb8\xb9\xfd\x68\xf3\x91\x6d\x62\x16\xe5\x29\xa4\x8b\x71\xb8\x5e\x8b\xaf\xf8\x5b\x5c\xd6\x11\x36\x40\xf3\x6a\x0d\xdf\x2c\x0b\x49\x2a\x94\x60\xc2\x67\x83\x59\xbf\x31\xa1\xac\x62\x3c\x8f\x6c\x44\xed\xbd\x76\x9f\xa1\x65\x3f\x0d\xe9\x5e\xd1\x19\x22\xad\x35\x1b\x5b\xfb\xff\x9c\x6c\xce\x00\xf9\x7b\x61\x20\xd6\x22\x3d\x5a\x2c\x68\xb6\x1f\xe4\x5a\x95\x8d\xb2\xf9\xb3\xe3\xce\xc3\xae\x7c\x09\x2c\x12\x86\xbd\x87\xd6\x8d\x19\xcf\x5d\xc4\x51\xd1\x69\xb7\xbb\xe6\x2b\xec\xa4\x6b\x5a\x57\x8d\xd3\x90\x0d\x2e\xf1\x75\x5e\xca\x87\x00\xf3\xd3\x2e\xd9\x63\x02\x21\x7c\xfc\xb8\x4b\xfe\xaf\xeb\xc4\x98\xf0\xcc\xac\x98\x58\x03\x52\xb9\x30\x0e\x29\x79\x40\xf6\xc8\x06\xd9\x1c\x22\x3b\x23\x5f\xdc\x05\x19\xdb\xd6\xb6\x61\xba\xea\xf6\x7f\x4d\xa3\x84\x0d\xd3\xb6\x54\x1c\x2f\xc1\xa7\x2e\x4c\xf1\xeb\xc3\x67\x8c\xb0\x37\x87\x92\x29\x09\x0b\x3f\xa0\x7c\x0f\xc5\x7d\x3f\x7c\xf4\xd0\x26\xb8\x79\x1a\xfe\xf0\xfd\xe6\xb0\x8c\xd0\x4c\xfa\xd2\x7e\xb2\x39\x35\x89\xc2\x95\x54\x94\xd1\x79\x10\x25\x5c\x77\xc4\xf2\xf4\xdd\xa3\x70\x1d\x64\xb2\x07\x01\xac\xed\x96\xb7\xba\x96\x53\x24\x60\x56\x12\x4c\x59\xbc\xfe\x60\x98\xc8\xe9\x26\x41\xd6\x3e\x48\x0a\xee\xc3\xa7\x47\x36\x87\x5d\xf2\xff\x67\x58\xdb\x70\x6a\xe1\x2e\x97\x84\xf9\xb9\xef\xe5\xaf\xaa\x4b\x95\xd4\xf5\x19\xf3\x54\xff\x0e\x89\x9b\xa0\xc3\x3a\x10\x06\xff\x70\xa1\x0e\x09\xe2\xad\x83\x60\x9f\x72\xbe\xfc\x93\x33\xc0\xde\xd4\xfd\x93\x20\x2c\xa1\xf5\x92\x73\xbb\xea\x44\x31\xae\xeb\x27\x85\x20\x57\xcb\xb9\x7c\x9d\x63\x11\x15\x83\xd9\x53\x39\x4e\xdf\x03\x94\x25\xc5\x68\x36\x84\x2b\xc5\xd6\xb0\x56\x8c\xe5\xf4\x51\x8d\x55\xce\x10\x40\x47\x94\x3f\x95\xbe\x0a\xd0\x4b\x05\x11\x6d\x96\x6c\x3e\x42\x2c\xec\x34\xc8\xe9\xf6\x23\xb2\x0b\x65\xb4\x7a\x68\xfb\x91\x61\x02\x10\x86\x94\x6b\x16\x61\x0f\xec\xf0\x42\x3d\xb2\xf9\x9d\x29\x09\xab\x7e\x3e\x3d\x0d\x92\x0e\x2f\x66\x32\x3f\x6b\x31\x0b\x7f\x2b\x68\xe1\x3e\x65\x43\x2f\x52\x63\xf7\x62\xd3\x47\xc0\x71\x6e\x76\x29\x57\x34\x57\x26\x81\xbd\xee\x5b\x1e\x6b\x24\x49\x0b\x21\x94\xfd\x18\xfd\xd4\x9a\x82\x44\xc2\xfd\xf8\x4c\x34\x52\xf3\x59\xc0\xa5\x35\xd8\xdf\x2e\xc6\xf1\x32\x8f\xce\x54\x68\xd4\xe8\x34\x8a\xa3\x42\x09\x38\xa7\x41\xf2\x69\x70\x9a\x05\xc9\x78\x46\x72\x9a\x9d\x45\x63\xb9\x01\x06\xdc\x8f\x6f\xeb\xc7\x41\xf4\x53\xdf\xa6\x21\x15\xa6\x24\x97\xbb\xd0\x84\x66\x6c\x1b\x0a\xe2\x69\x9a\x45\xc5\x6c\x4e\x42\x9a\x8f\xb3\xe8\x94\xb3\x25\x21\xff\xd0\xa4\x7f\x1e\x7d\x8a\x16\x34\x8c\x02\x10\x82\xd8\xd7\xe0\x20\x29\x68\x96\x04\xfc\xe9\xc4\xc7\xa7\x41\xf2\xe9\xa3\x70\x22\xfc\x91\xcf\xeb\xff\xef\x67\x31\xd2\x64\xfa\x91\x0d\xf1\x23\xbc\x25\xfa\x18\x46\xd3\xc8\x79\xca\x21\xa7\xc6\x47\x91\xa7\x72\x4f\x95\x33\x20\x9d\xe1\x14\xa9\x67\x9b\x6d\x40\xab\x4f\xed\x15\x79\x6a\xb1\x45\x31\xa3\xfb\x7c\x9f\x6a\xff\xf3\x79\x7b\x67\xcd\xcb\x33\x05\x8f\xed\x58\x3b\x77\x07\x57\xb0\x41\xda\x43\x10\x95\xa0\x15\x6c\xee\xc2\xd0\xf1\x8c\x61\x83\xec\x92\x0e\x17\xa7\x3a\x3f\x3c\x26\x0f\x74\x13\x5d\xf9\x6c\xe0\xc1\x96\xb5\xdf\x2a\x6f\x1f\x66\x53\xa8\x4e\xd1\x60\x8d\xda\x4a\x30\x11\x84\x2b\x20\x6c\x1e\xa0\x3e\x4a\xf2\x22\x2a\x96\x85\x74\x85\x1d\x85\x34\x29\xd8\xa6\x65\x07\x76\xe0\xb5\x1c\x24\x61\x94\x51\xd3\x80\xc1\x7c\x63\x93\xf7\xa4\x2c\xab\x1e\xd9\xc0\xab\xa9\x16\x6a\xa9\x05\x4d\xb5\x74\x5b\xad\x55\x78\x91\xd9\x13\xaf\x7b\x6c\xf3\x08\x6c\x72\x86\xf6\xf3\x0f\x2f\xd9\x3c\xc8\xd7\x2d\x18\x03\x28\x55\xf5\xad\x6b\xf1\xeb\xb4\x8a\x5f\xcb\xa7\x74\x1c\xb9\x22\xfa\x7b\x94\xf3\x97\x72\x98\x8f\x3b\x72\x27\x78\x6e\x29\x95\x37\xd5\x5e\xe4\x51\x7c\x48\x85\x07\x7f\x4e\xc7\x5b\x52\x42\xe7\x01\xf2\x0b\x53\x29\x27\x44\xd8\xbf\x4c\xc4\xc9\x0a\x0b\x7f\xda\xb9\x4c\xad\xae\x5c\x61\x01\xba\x5e\xfa\x7a\x10\x8f\x59\x47\xfd\xf0\x8e\xaa\x47\x52\x8f\xd6\x06\xc6\x86\xb5\x35\xee\x28\x2d\x4a\x18\xfc\xe7\x9f\xcf\x8f\x87\x0f\x7e\x38\xf9\xbc\x75\xd5\x79\xfe\xe1\x25\xfb\xbd\xf7\xe0\xff\x4e\x3e\x6f\x6e\x5f\x7d\x51\x1f\xdb\xc3\xde\xf6\xe6\x55\xf7\x7f\x06\xfd\x02\x94\xa0\x6a\x03\x37\xde\xe5\x95\x31\x06\x04\xce\x9f\xe7\x6d\xae\x88\x30\xf1\x04\x13\x4e\xff\x5e\xb4\x3d\xd3\x4b\xf0\x76\xf0\xf6\xcc\x5d\x49\x16\xe2\xf4\xa0\xf0\xe3\x9e\xed\x87\xe4\xcb\x97\xb2\xbc\xef\xae\x39\xec\x09\x89\x92\x92\x81\x1b\xdc\xe7\x76\x86\xee\x65\x23\x8d\x06\xbf\x35\x6c\x64\xb5\xc9\x45\x4a\x36\xd2\x7c\x39\x67\x80\x47\xb9\x38\x3e\xcc\xd3\xf0\xc1\x0f\xdf\x3f\xd8\x1c\xaa\x6c\x38\xe3\x42\xef\xc6\x69\x4c\x3a\x07\xef\x0f\x07\x07\xcf\xf7\x09\x3b\x37\x8c\xb6\x86\xc3\xed\xae\xcd\x93\x51\xb5\xee\x29\x14\xe5\x3a\x03\x97\x79\x0d\x87\x2d\xce\x84\x5b\x3d\xb2\xd5\xcc\x56\x15\x33\x55\x63\x4b\x21\x74\xda\x27\xff\x7c\xf7\xfc\x67\xc7\x43\xa2\x2a\xe0\x1f\x4d\x69\x8d\xee\xa4\x22\xc8\xba\xe1\x69\x02\xe8\x80\xfb\x3c\x67\xc8\xdf\xf7\xc8\xc3\x2e\x19\x91\x76\xbb\xd1\xb8\xc7\x71\x04\x0f\xc9\x54\x07\x41\xf9\x14\x25\xf6\xf8\x18\x16\x7e\xde\xfb\xc7\xe1\x8b\x7f\x1d\xbe\xfb\x5f\x7b\x56\xa1\x8e\x92\x39\xb5\xeb\xf7\x4e\x2e\x07\xba\xf1\xd8\x37\x37\x57\x1f\xb9\x58\x4d\xfe\x73\x89\x7b\xf0\x70\x87\xe6\x54\xe0\x0c\x2f\xf0\x9c\x43\xf0\xbd\x93\x18\x9c\xcf\xf1\x99\x71\xe8\x70\x07\xfc\x10\x1d\x62\x4b\x8f\x32\xf2\xfc\xa1\x4e\x29\xc6\x09\x95\x9f\x51\xcc\xf3\xcc\xe6\xa3\x6e\x8f\x6c\x0d\x95\x6b\x35\x43\xca\x93\xe8\xb5\x06\x29\x0b\x37\x5b\xa0\x25\xde\xb0\x0e\x20\x8b\x2b\xf5\xb1\x5e\xb1\x35\x32\x3f\xaf\x4e\x7a\xdb\x0f\xef\xd4\xf8\x77\x6a\xfc\xbf\xb8\x1a\x5f\xa8\xf0\x17\xe3\x6a\xfb\xbd\x1b\x58\xdc\xb5\x74\x08\xcc\xd6\xce\x4a\xa1\xfb\x6a\xec\xf4\xb8\x9e\x69\x31\xf6\x5a\x82\x2d\x82\x62\xd6\x23\x09\x35\xac\xbf\x3f\x82\xe6\xc2\x79\x78\x2a\xaf\xaa\x71\xf0\x70\xe9\xb5\x40\xd8\xeb\x80\x8d\x0f\xfb\x8f\xa7\xea\xac\xb1\xba\xe1\x05\xae\x58\xc8\x84\xce\x67\x06\x3d\xd2\xe5\x95\x8f\x4d\xab\x58\x3f\x4d\x3a\x6d\x18\x55\x1b\x07\xdb\xed\x1a\xf6\xd3\x79\xca\x98\x18\x7f\x4b\x78\xf0\x76\x9f\xe8\x7b\x65\xfe\xc2\xb0\xdd\x23\x14\xb1\xde\x8f\x9c\x0d\x8a\x0b\xef\x8e\xed\xe5\xd3\xdb\x83\x24\xc4\xed\xa3\xe6\x4b\x2b\x23\x6b\xea\x8d\xc1\xab\x83\xf7\x1f\x9e\xbf\x81\x15\xb4\x7f\xf8\xe6\xcd\xf3\xfd\x0f\x07\x87\x6f\xc8\xbb\xe7\xef\xdf\x1e\xbe\x79\xff\xfc\x7d\x69\xab\x61\x50\x04\xb8\x59\xf6\x8d\x37\xa7\xc1\x7d\x61\x46\x38\x0f\x2e\xc6\xe9\x7c\x11\xd3\x8b\xa8\xb8\x1c\x91\x47\x40\x59\x56\x0f\x41\x17\xaa\xec\x10\x58\x55\x7a\xbf\xe9\x7a\xe2\x12\x09\x9b\x83\xcf\x66\xa0\x74\x38\xf8\x85\xb6\xed\x84\xe8\x0e\x0f\x20\x0f\xfc\x25\x24\xe7\xb3\x68\x3c\x23\xf3\xa0\x18\xcf\x84\xf8\xca\x37\x21\xc6\xd0\x42\xa3\x9c\x27\x2c\x06\x34\xed\x8f\xa4\x0e\xd7\x51\x4e\x6f\xc1\x02\xc1\x05\x17\xd5\x7f\xf4\x13\xf2\x31\xbc\x8d\x8b\xc2\x13\xd7\xd9\xbe\x2a\xcc\xc6\x2a\xc0\x76\x1c\x28\x3b\x26\x7d\x69\xac\x66\xa8\x46\xf4\xdd\xae\xe8\xca\xc1\xe2\x24\xca\xa8\xe1\x11\xc0\x46\x57\xd9\x78\xd8\x50\x3c\xad\x57\x80\xeb\xc0\xd1\xd8\xb4\x45\xff\x85\x34\xa6\x05\xad\xaa\xc1\x1e\x8c\x8d\x1b\xfc\x0a\xfb\x17\xb6\x6b\x01\x21\x0a\x82\xe0\xf5\x81\x72\x87\xdb\x4a\x25\xdc\x59\x0e\x49\xb9\x0f\xe9\xa8\xe8\xaf\xad\x49\x61\xd0\x24\xe1\x35\x5b\xed\x01\x2f\x32\x99\xf0\xa7\x79\x1e\x12\x8f\xcc\xc2\xd8\xa3\x2b\x5e\x55\x36\x1b\xec\x59\xf2\xda\x3f\xb8\xcb\x76\xed\x79\x58\x2e\xf1\x67\xcf\x1f\xec\xbf\x3c\x7a\xf3\xbf\xcf\xdf\xa9\x7a\x42\x3a\x9e\x2d\x93\x4f\x34\x14\xaf\x4a\xf8\x8b\x51\xf1\xd7\xcf\xe8\x22\x0e\xc6\xb4\x33\xf8\xf7\xd5\xf1\xbf\x93\x7f\x67\x27\x4f\xfe\xfd\x79\x30\xed\xb5\xaf\xbe\x3c\x78\xf0\xe5\x73\xbb\x0b\x3e\x93\x3f\x7b\xe1\xff\x7d\x22\x4b\x1c\x8b\x32\x27\xac\xd0\xb1\x2c\x75\x72\xec\x2f\x67\x97\x32\x0a\x95\x94\xd1\x6d\xa1\x96\x54\x43\xa8\x8c\xb8\xe6\x63\xd9\x6d\xc9\x49\x0d\x0c\xb8\x6b\x16\x10\x8f\xf8\xcb\x60\x00\x77\xa0\x54\xb8\xc3\x00\x4f\x1b\x50\xc1\x9a\x43\xfa\x2c\x6f\x9f\x65\x99\x2b\x57\xf8\x9d\xb1\x60\xc8\x06\xe1\xef\x5f\x0d\x51\x5d\xdd\x59\x5b\x9c\xcc\x75\x6a\xe0\xb3\x05\x83\xbe\xa3\x52\xc2\x9a\x86\x1b\xd3\xac\xb9\x8b\x4f\x77\x66\xd7\xee\x8c\x18\x3a\xf8\xfa\x55\x16\xd4\xe0\xfa\x2e\x19\xd3\x18\x22\x05\xc8\x47\x9c\x46\x99\x71\x4c\x83\x4c\x9a\x70\x59\xad\x88\x64\x6b\x41\xfb\x81\xc0\x57\x43\x21\x2b\xf2\xed\x71\x66\x79\x7b\xaf\xc3\x7f\x95\x76\x95\x02\x67\x18\xfe\xaa\x47\x36\x87\xc3\x21\xb9\xcf\x2f\x67\x3c\x77\xad\x5e\xc7\x0f\xf0\x6e\x0f\xb0\x23\xf1\xc5\x38\x48\x4e\x05\xbd\xf0\x58\x3f\xe2\x5d\xdf\xea\xa8\x72\x67\xcc\x22\x11\x08\x62\x25\x2c\x2b\x9d\x0e\x73\x16\xd1\x5f\x2c\xf3\x99\x69\x31\x68\xbb\x11\xc7\xe0\xc2\xf9\x0f\xe3\x91\x3f\x8b\x2d\x34\x08\xc3\x1c\x47\xa2\x17\x56\x0e\xae\x34\xc6\xd5\xc3\xbd\x35\xbe\xe1\xca\x83\x81\x38\x6b\x47\xdc\x0f\xbf\xe0\x7a\xb0\x1b\xcb\x5b\x21\x95\x7a\x10\xf2\x52\x41\x96\x45\x67\x14\x33\xdc\x20\x54\xb3\x27\xdb\xab\xe0\xb0\x1e\x68\xc3\x0d\xbf\xdf\xa6\x14\xc9\x14\xf2\xb5\x7a\x04\x21\x6b\xc5\xd7\xf1\xf0\x44\x6d\x99\x70\x85\xcd\xfb\xa6\xa1\x45\x82\x59\x82\x27\x62\x89\xce\xbb\x79\x91\x5d\xd5\x9b\x2a\x89\x97\x81\xf6\x55\xc3\xb2\x6e\xb9\xab\xc9\x75\x84\x57\x2a\x39\x9f\x51\xe9\x77\x20\xe4\x62\x39\x9c\xbe\x40\xe3\xce\xf6\xf7\x10\xa1\x59\x10\x71\x05\x6a\x5d\xfb\x4e\x75\xb4\x9f\xa4\x59\x87\xe1\xe5\x13\xbd\xe4\x27\x45\xdf\x00\x4c\x27\x30\x1d\x3f\x50\x7f\x16\xe4\x87\xe7\xc9\x5b\x88\xe4\x55\x5c\x42\x64\x4a\x8b\x0b\x94\xa0\xe7\x13\xbd\x3c\x29\xb7\xed\x6c\xa7\x09\x39\x78\xbb\xdf\xee\x5a\x8b\x5f\xc8\x16\x15\x75\x3a\x66\x16\x7a\x99\xec\x63\x1f\x84\xc2\xcd\x39\x41\xc7\x8d\x28\x27\x79\x11\xf1\x28\x2b\x51\x88\x88\x1a\x9b\x85\x96\x22\xdc\x6f\xc7\xd9\x29\x3f\x2d\x49\x39\x80\xed\x1e\x19\x15\xfd\xe8\x71\x2a\x30\x7b\x35\x4d\x13\x2a\x34\x4f\x9d\xf5\x8f\xb6\xd8\x7f\x9e\x45\x05\xf8\x4b\xb1\xb8\x11\x02\xb1\x8e\x50\x1f\xdd\x33\x94\x74\x31\xb8\x5e\x56\xbb\x50\x20\x79\x87\x5e\xf5\x82\x60\x0d\xd3\x8f\x55\x2f\x7d\x8f\x9e\xae\x10\x63\x93\xdd\x31\x38\xf7\x0a\x28\x92\x68\xaa\xc7\x12\xf1\x1c\xa1\x6a\xcf\x9a\xb2\x97\x21\x7a\xf6\xeb\x1b\x55\x85\xc5\xf3\xf5\xc4\x06\x45\xd5\x58\x6a\x30\x87\x52\xbb\x8f\x12\xeb\xcf\xb7\x4f\x5a\x66\x77\x42\x9b\x68\x9d\x51\x1c\x77\x3c\xff\x4a\x97\x60\x65\xad\x5f\x9b\xb5\xda\x1b\x36\xbb\xdd\x68\xb7\x48\x8e\x0d\xb3\xfb\xd8\x4e\x5b\xf3\x41\x78\xb1\x95\x16\x24\x5f\x2e\x16\x69\x56\x80\x6e\x8d\xdf\xd4\xbe\xdd\x27\x4a\xab\xd2\x36\x1c\x41\x96\x13\x66\xe3\x97\x0a\xd7\x59\x8c\xf5\x54\xb6\x12\x85\x79\x8f\xf5\x40\x53\x95\x16\xf4\xc8\xa1\xae\xbd\x9b\x96\x7a\xbb\x71\xf5\xb8\x1a\x83\x8e\x93\xf6\x92\x57\xda\x57\x27\xbd\xed\xef\xee\x54\xba\x77\x2a\xdd\xff\x0a\x95\xae\x78\x58\x71\xa3\xe7\xd8\x7b\x41\x96\x26\xe4\x7f\x97\xf3\xe0\x2c\xca\xc9\x8f\x01\xfb\xfc\xdb\x27\xfe\xd9\x9f\x53\xaf\xba\x77\x30\x20\x07\x49\x54\x44\x41\x1c\xfd\x46\xc9\xdf\x79\x2f\x18\xa1\x06\x24\x07\x4b\x2c\x69\x70\x03\x03\x65\x4b\xd5\x70\x72\xde\x07\xad\xae\x2c\x26\xa3\x97\x88\xc8\x5a\x07\xe1\x88\x0c\xeb\x6e\xde\xb8\xb5\x07\x1b\xbe\xed\x56\xd7\x6b\x66\xe2\x75\xa7\xab\x5f\xa1\xc9\x20\x5e\x13\x89\x50\x68\x49\x1b\xf4\x78\x9c\xf0\xf2\xd7\x29\x3d\xa4\xea\x99\xc8\x6a\x64\x96\xf4\xbd\xeb\x75\x43\x84\x46\xc0\xda\x73\x7a\x3f\x58\x13\xe8\x29\x71\xc5\xcb\xdb\xea\x89\xc6\x0c\xa7\xa9\x3c\xab\x5b\xa6\x5a\x96\x4d\x3a\xc6\x3c\xca\x6c\x77\xbd\x8d\xc2\x69\x05\xe1\x19\x3b\xa3\xca\xd9\x21\x07\xcf\x20\x47\xf6\x4e\x4d\xda\xc6\x46\x99\x9f\x21\xff\xeb\x1f\xfe\x56\xc8\xa9\x46\x67\xcb\xe7\x41\x62\xa4\x2a\x5d\xbe\x0b\xe2\xff\xb3\x03\x93\x7c\x21\xd4\xdc\xf0\x42\xe2\x40\x1d\x1e\xa5\x01\x91\xdf\x54\x47\x29\xeb\xea\x42\xba\x79\x5e\x66\x5b\x0d\xf8\xcd\x33\x24\x1a\xac\xf6\xac\x50\xd7\x3c\xd1\xba\x0c\xe5\x3e\x7d\x90\xce\x59\x00\x3d\x51\x6d\xf7\xe9\x19\xcd\x2e\x3b\xd2\x1b\xf2\xfb\x28\x99\xc6\xf4\x35\x47\x78\x97\x8c\x88\x37\x43\xd7\x24\xa6\x55\x75\xc4\x0f\x2e\x26\x50\x1d\xb4\x94\xf0\x2e\xe9\x06\x59\x10\xc9\x34\x4e\x91\x86\x6d\x91\xc8\x90\xf3\xb3\xbb\xbb\xcb\xa9\x06\x03\x09\xb7\x0b\x12\x96\x9d\xb9\x19\x18\xbf\xd6\x6d\xfb\xaa\x13\x32\xac\xe5\x53\x72\x30\xe0\x31\x07\x55\x92\xf0\xca\x8e\x99\x8b\x5c\x8f\x8d\xfc\xc9\x73\x46\x74\x0a\xef\xd1\x6a\xd8\xd1\x53\x06\x54\xee\xe2\x5b\x74\xdc\xe2\x2f\xbc\xae\x9c\x33\x55\x51\x95\x14\x70\xc2\x2e\x28\x8f\xc4\xa2\xe8\x48\xde\xd3\x25\x93\x88\xc6\xa1\x65\x7a\x20\x5a\x31\x7a\x6a\xf1\x1c\xdc\x41\x8b\xf1\xf0\xae\x59\x64\x28\x93\xad\xa8\x0f\x92\x2c\x5c\x47\x58\x0e\x7b\x93\xb0\x7d\xc9\xda\xe4\xb7\x60\x71\xa6\x1e\xde\x91\x15\x45\x7d\x42\x4e\x64\x62\xe0\xa3\x3b\x31\xf0\x4e\x0c\xfc\x6b\x8b\x81\xfa\x7d\x1e\x5f\x34\xb7\xf5\x42\xef\x76\xee\xee\x19\xc8\x6b\xa9\x6e\x2c\x35\x56\x86\x73\xa2\x88\xd4\x22\xad\x90\xd9\x27\x3a\x45\x0a\x97\x6b\x32\x97\x7d\x1a\x17\xf7\xc0\xf3\x74\xbe\x96\x0c\x86\x08\x0c\x7c\xf2\xe3\x60\x88\xda\x10\x1a\x67\xa0\x12\xdc\xd3\xb3\xaf\x88\x95\x63\x28\x5d\x41\x63\xf0\x3a\x48\x82\x29\xd5\xaf\xf3\x19\xcb\xe2\xa8\x30\x54\x01\xd2\x85\x87\x06\x47\xfb\xfd\xdc\xc0\x90\x53\x71\x36\xaf\xb1\x7f\x0f\x29\xe3\x30\x51\x62\xfa\xf7\xb4\xc4\xbf\xd3\x20\xe7\x3e\x17\xca\x22\x51\x4c\x29\x78\xa9\xf4\x6c\x52\xa6\xa7\x79\xdb\xb1\xa8\x6c\xd3\x6c\x0f\x48\xcc\x41\x84\x68\xa3\x34\xd6\x84\xe1\x4e\x14\x85\xcf\x51\xc4\xa1\xec\xf8\xa4\x2f\xc3\x9c\x09\x36\x2a\xa5\xce\xcd\x31\x77\xc6\xa9\x2f\x29\x44\x68\x0e\xb1\xed\xaa\x71\xf6\xc9\x6b\xc6\xca\x23\x9a\x8b\xe8\xd8\x80\x0f\xc7\x0b\xa5\xe1\xd9\xb3\x31\xde\xe4\xa0\xbe\xbc\x59\xc6\xb1\x76\x8c\xd1\x63\x52\x24\xbd\x88\xe0\xda\xcc\x87\xbb\x3f\x66\xfc\xa1\x5b\x0b\xbb\x43\xd6\xbe\x56\xdc\x1d\x07\x93\x8d\xa2\xed\xd8\x01\x4e\x54\x28\x19\xf3\x20\x46\x6a\xc2\xc7\xbc\x7b\xbb\x2f\x22\x4c\x54\xc7\x8e\xd1\x68\x13\xae\x5e\x39\xe1\x01\xd2\xd5\x89\xd3\x46\x13\x07\x3d\x60\x90\x2e\x96\x0c\xa2\x53\x49\x1e\x74\xa0\x5a\x2a\xb1\xb1\xee\xe1\xae\x25\x14\xe4\x7b\xdc\xe8\x29\x6d\xc9\x90\xca\xe9\x62\x8f\x40\xf4\xef\xaa\x10\x52\xe4\x89\xfe\xcd\xa9\x1b\x8a\x9c\x30\x76\x80\x3e\x6b\x3c\xeb\x3b\x58\xe7\xfc\x5e\x45\xcd\xc5\x98\x77\x11\xcf\x1d\xf0\x56\x9f\x15\x4d\x77\xc4\x25\xb8\xf7\xc4\x48\x31\x83\xf4\x62\x14\xda\x9b\x15\x38\x9b\x81\x63\xcf\x13\x2f\x80\xaa\xca\x1b\x9b\x44\xe0\xc2\x17\xb2\x48\xbe\x9f\x92\x74\xb8\x42\xe4\xa2\x40\xae\xdb\x46\x48\x68\x16\x83\x08\xbb\x63\x15\xfb\x88\xed\x25\x79\x65\xe7\xcb\x42\x9e\x00\x60\xb4\x0c\x30\x20\xe4\x19\x01\x86\xd4\x31\xc5\xaf\x05\x91\xea\x0c\xd0\x2c\x95\x28\x33\xaa\xdc\x2a\x63\x15\x87\x83\x2a\xe9\x22\x97\xe3\xd3\x94\xb6\x4e\x7f\xc5\xe8\x62\x19\x72\x68\xa7\xcb\x28\x0e\x01\x61\x62\x50\x2c\xd3\xf1\x6f\x0b\x0c\xff\xc3\xe1\xb3\xc3\xf5\xf5\x75\x10\xef\xdb\x39\x59\x4e\xe3\xcb\xbe\x88\x22\xc6\x0e\x04\xcb\x9c\xed\x89\x85\x6a\x25\x41\x2e\x65\xd9\x6f\x69\x57\xa3\x6e\x48\x18\xe3\x80\x0c\xf5\xde\x7a\xd3\x88\xf4\x74\xfa\xeb\x31\xcb\x3e\x1e\x9e\x9c\x30\xb1\x0b\x7f\x7e\xf9\xa2\xec\x36\x6d\x50\xfe\x63\x13\xca\xb0\xb1\xec\xf8\xaf\x8a\xac\xda\x01\x92\x20\x2e\xec\xa0\x57\x21\xaa\xec\x16\x55\x5d\xaa\x6b\xa3\x53\x1e\x02\x25\xf1\x3f\xcb\x22\x8e\x9f\x6f\x21\xbf\xeb\xd3\xf0\x2a\x7e\xa0\x89\x15\xc1\xc2\x17\xaa\xc0\x38\xab\x43\x5b\xa6\x44\xa9\x2f\xa6\xf4\xfd\x8c\x11\x8b\x45\x99\xd7\x79\x4c\xf3\xec\x86\x39\xbc\x68\x07\x33\x33\x65\x14\x69\x19\xd0\x78\xc3\xa9\x98\xdd\x35\xaa\x29\x1f\x82\x7d\x0d\x25\x48\x85\x65\x35\xf5\xf4\x2c\xc3\x5c\xd1\xa4\xde\x9d\xa3\xe4\x90\xcb\x8c\xc2\x0d\xe9\xbb\xb7\xfb\xca\x03\x13\x37\x65\x19\x07\x89\x12\x36\xa3\x44\x28\x5d\xfc\xbe\x9e\x32\xd7\xd7\x63\xbf\xdf\xbf\xc2\xf1\xdd\x6c\x5f\x7a\x5a\x93\x29\x8b\x7a\x38\x69\x9d\x4f\xfb\x52\x77\xf3\xab\x10\xa1\xa4\x01\xd3\x27\x3d\x9e\xb5\x32\x44\x8b\x92\x25\x8a\x9d\xd7\xd2\x06\xa6\xe9\xf5\xdf\xf7\x77\x7a\x9f\x3b\xbd\xcf\x5f\x5b\xef\x23\x94\x3e\xe1\xe9\x0d\x6e\xfe\x7c\x7a\x1f\xa5\xad\xc1\x8a\x1f\xce\x9c\x94\x46\xe7\xd9\x53\x83\x8f\xb0\x61\x98\x2e\x3f\x1c\x4d\x05\x8c\xd4\x4a\xde\xa9\x08\x14\xb6\xa6\xe5\xa5\xbc\xe3\xb1\xe9\x17\x17\x5c\xe4\x33\xb1\xa4\x2b\x4b\x0e\xea\xb0\x9a\xd1\xce\x22\x80\x1c\xb5\x4b\xc7\xd7\x41\x4b\xdf\xac\x77\xf9\xf2\x80\x45\x8b\x65\xa1\x1e\xaf\x25\xf4\x5c\x60\xb3\xa3\xb7\x4b\x26\x74\x8c\x48\x5b\xc1\x59\x71\x34\x46\xa4\x1d\x9e\x7e\xf4\xe5\x4a\x31\x71\x5b\xf5\x49\x35\x3a\xa5\xcd\x1a\x55\x70\xde\x46\x7d\xb9\xb2\xd1\x2d\xb7\xd1\xc5\xb2\x78\x49\x2f\xea\x87\xf9\x92\x5e\x94\x8d\xd1\xcc\xaa\x1e\x60\x7d\x5b\x1c\xa8\x6c\x68\xfe\xb6\xac\x71\x89\xcd\xe8\x58\xc3\xc9\x89\xe8\x69\x24\xf7\xc4\xd0\x7b\xa2\x5b\x00\x7c\x52\xb2\x73\x3d\x7b\xaa\x77\x2d\x4e\x3b\xad\xd1\x36\x6c\x51\x8f\xef\xb6\xa8\xbb\x2d\xea\xaf\xbd\x45\xe9\xab\x09\x5a\xcc\xae\x75\x2f\x21\x80\x6f\xf7\x55\x62\x49\xf4\x7f\x5f\xf8\x7f\xdf\x25\x88\xff\x1e\xa4\x66\xdb\x64\x20\xd2\x1c\xd9\x02\x5a\x88\x64\x09\x36\x2e\x6b\x6f\x9c\x26\x93\x68\x2a\xc1\x50\x28\x1c\x0c\x2d\x23\xab\x48\xb0\x73\xf1\x6c\xcd\xb8\xa0\x11\x89\x12\xe6\x05\x0f\x05\x6e\x21\x03\x12\x25\xc8\x41\xfe\xfe\x32\x19\xf3\x2d\x06\x43\xe5\x3c\x55\x82\x31\x56\x9c\x51\x1b\x48\xa4\xaa\xba\xb8\x83\x22\x0c\x11\x9d\x06\x89\xcc\xe6\x5e\x0f\x9d\xfe\xc8\x64\x25\x84\x80\xcf\xb4\x26\x77\x06\x4a\xe7\x2d\xde\x08\x82\x12\x70\x78\xd2\x25\xf7\xee\x11\xf1\xbb\x0f\x3a\xc1\xc3\x49\xa7\x3d\xbc\x68\x73\xd7\x25\xc3\x2e\x79\x42\x5a\xb4\x98\xb1\xdd\x03\x02\x93\x3e\xbd\x7c\x19\xe4\xb3\x16\x19\xd9\xc9\x5c\xa3\xdb\xd2\x52\x02\x74\xed\x7d\x34\x4d\x68\x96\x57\xf4\xf0\x96\xfb\x27\x1a\x2c\xe9\xa6\xca\x75\x7a\x9b\x17\xc1\x27\x9a\xbd\x3b\x3c\xa8\xef\xea\xf5\x7a\x8a\x3a\xfa\x5e\xb6\xf5\x3a\xc8\x0b\x9a\x25\x69\x48\x71\x4f\x55\xb6\x8d\xcc\x17\x51\x12\xc4\x51\x71\xf9\xfb\x61\x53\xb6\x58\x82\x4e\x9d\xed\xe0\x13\x45\xff\x7a\x91\xa5\xf3\xa7\xbf\x03\x9d\xb6\x45\xd7\x50\x50\xa9\xa7\x97\xd0\x30\xeb\xfc\x5e\x12\x1e\xb0\x72\x2a\x96\x9b\x17\x92\x8f\x43\xc1\xea\xf1\x2c\x93\x71\x4c\x7f\xa7\x01\x1c\xb1\xb6\x6a\xba\x8e\x61\x4a\x3b\x2d\xe7\x09\x8d\x73\x3f\x5d\x26\x8d\x2e\x19\x6f\x61\x1c\xde\xb6\x39\x29\xe1\xa1\x94\x80\xf1\x51\x39\x53\xf0\x3b\xf6\xff\x48\x35\x88\x26\xc3\x99\x04\x0c\x60\xf4\x59\x75\xef\x79\x31\xbb\xed\xe3\x61\xe3\xa3\xe1\x2d\x9d\x0c\x21\xfc\x73\xf9\xc9\x90\x2b\xbe\xf8\x1e\x1e\x51\x6f\x8f\x16\xb8\x33\x8b\x9a\x7e\x2c\xae\xd1\x05\x64\xe1\xc0\xf7\x56\xee\xfd\x84\x60\xff\xec\x07\x4f\xf7\xde\x58\xa1\xe8\xc4\x8e\xca\x75\x72\xfc\xf9\xb4\xd0\xcc\x5d\xad\xad\xf1\xde\xf5\xb9\x5d\x9c\x7a\x49\xf5\xbc\x98\x69\x5d\x60\x8f\xb4\x71\xe0\xee\x76\x4f\x0c\x73\x4a\x8b\x51\x89\xc6\x5b\x7a\xaa\xed\xe3\x82\x62\x24\x3d\xa1\xa5\x35\x0a\x9f\x05\xb1\x11\x63\xae\x6f\x85\x4d\x3f\x0b\x62\xc7\x15\x8d\x4a\xbb\x5a\x03\xf4\xac\x34\x14\xe1\xe5\xf1\x3a\x83\x11\x45\xaf\x33\x1c\x51\xb4\xe1\x80\x9a\x68\x22\x18\x77\x09\x62\xb0\xdb\xad\x3d\x37\x0b\x40\xf7\xec\x2c\xd9\x94\x93\xaf\x0e\xd0\xc8\x96\xd7\xb8\xc0\x1d\x91\x63\x2d\x4e\xf3\xcb\x5d\xe1\x44\xf5\x85\xbe\xcb\xb5\x21\x70\xdc\x7b\xce\x4f\x14\x30\x0a\x1c\x6a\xdd\x62\x8e\x70\x35\x3c\x4f\x79\x2c\x52\x40\x25\x4a\x93\x34\x0b\xa6\x74\xaf\x68\xa2\x37\x11\xa0\xa5\x38\xf2\x41\x28\x95\x46\x05\x96\xf8\xba\xe3\x1c\xbb\x48\x41\xaf\xb0\x0a\x5a\xbc\x03\x13\xae\x3d\x6b\xc6\xc4\xa0\x4a\x87\x63\x65\xfe\xfe\xf3\xed\x1d\x98\x58\x26\x07\xc9\x24\xad\x1f\x1f\x02\x2e\x1d\xa6\x1f\xe6\x0f\x32\x5a\xc9\xe3\xea\x56\x2f\x67\xbe\xd6\x08\xd5\xf1\xe8\x66\xc3\xf2\xf5\xb6\xe7\x30\x34\x6d\xeb\xcd\x58\x15\xb9\x5a\x6d\xb5\x82\x3c\x5d\xb9\x52\xf1\x09\xc6\x8f\x10\x0b\x1d\x02\x56\x61\x05\xe1\x04\x9d\xcb\xec\x38\x23\x9b\x32\xe1\x5a\x68\x51\x83\x6e\x38\x64\xd1\x91\x3a\x1e\x25\x4e\x44\x4d\x78\x94\x00\x75\x68\xc1\x38\xe1\xb9\xf4\xb0\x59\x45\x0f\x52\x88\xb1\x76\x2e\x62\xec\x4e\xd8\xe6\xa6\x64\x3d\xf0\x0a\x46\xa6\x3b\x83\xa6\x84\x82\xaf\x10\xdf\xd1\x20\x2e\x27\x12\x79\x2e\x6b\x44\x25\x12\xd8\x47\x26\xf8\xc4\xf9\x0d\xe8\x04\x8f\xf8\x20\x29\xbc\x03\x06\x19\xbc\x9e\x2e\x00\xcc\xa1\x09\x75\xaa\xfb\x1a\xfc\x01\x6d\x67\x37\x60\x05\xbd\xb5\x92\xdd\x6d\xbe\x88\xe2\x52\x4e\x60\x6e\x71\x02\xb4\x62\x9f\x73\x21\x24\x1e\x86\xe5\x64\x66\x9f\xd9\x1a\x72\x69\xbb\x98\xd3\xad\xaa\x63\xeb\x8a\x0b\x77\x15\x4a\xf4\xcc\x8d\x9c\xc2\x67\x74\x1c\xcd\xab\x56\x9c\x3e\x09\x36\x44\x82\x2e\x50\x42\x94\x7f\xdc\x01\x9b\x07\xa8\x9a\xc1\x96\x47\xcb\x2f\x51\xc3\xc0\x19\xbb\x72\xd0\xf5\x2b\x08\x55\x58\xbd\xb1\x7c\xf0\x68\xa9\x56\x1a\x93\x2a\xe5\x0c\xae\x4c\x01\xf6\x47\xe2\x34\xd7\xc1\xd3\x3b\x3a\xa6\xd1\xa2\x01\x99\xbb\x65\x9a\x10\x80\x0b\x7a\x53\x0a\x10\x35\x36\x1e\x60\xc3\x55\x5c\xcb\xc5\x3c\x83\xb3\x01\x9b\x50\x00\x3f\x1a\xdd\xd2\x21\xb1\x6c\x79\x13\x69\x6d\x20\xad\xf5\xde\x05\xe7\xcd\x97\xb9\x5b\xc0\x8f\x8c\x4a\xb8\x26\xdc\x8d\xe1\xc2\x73\x4a\x60\xf5\xae\xd6\xdb\x46\x5d\xbd\x7e\x3f\xed\xd9\xf2\xad\x33\xdf\x38\xa2\x69\xb2\xc2\x38\x4c\xe8\x92\x71\x94\x02\x7d\xe5\x71\x34\xe8\x7c\x79\x8f\x6f\xfd\x14\x5a\x42\x38\xc2\xc4\xb7\xaa\xa3\x0c\xc4\xdf\x51\x2b\xe7\x3a\x1d\x65\xfb\xc1\xad\x9d\x95\x69\x5e\x44\xf3\xa0\xa0\x3f\x07\x75\x32\x21\x82\xf4\x0f\xcd\x0f\x70\x1d\x8a\x31\x46\x78\x23\xc1\x63\xcc\x65\xd4\xf7\x69\x1c\x85\xa5\x47\x1b\x3d\x6d\x1c\xba\x9f\x0b\xf0\x92\x29\x34\xeb\xf4\x8d\xb5\xb4\x23\xaf\x5e\xbd\x6a\xd8\x87\xb8\x94\x82\x54\x4d\x2b\xb5\xfc\x9e\x66\x0b\x5a\xbb\x45\x29\x0c\x70\xe8\x6a\x04\x38\x30\x15\xbd\xc8\x97\xa7\xf3\xa8\xf8\x25\xcd\xea\x24\x25\x0d\x58\xb2\xd2\x7d\xf9\xd5\x06\x50\x0d\x5a\x15\x50\xa5\xdb\x71\x49\x7b\xfe\x63\xce\x7e\x90\x84\xfc\x91\x7f\x11\x14\xcb\x5a\xad\x8b\x05\x6e\x9d\xa8\xd5\x69\xab\x04\xca\xe1\x20\xb7\xa0\x6e\x7b\xbe\x48\xc7\xb3\xff\x8f\xbd\x77\x5d\x6f\xe3\x56\x12\x45\x7f\xdb\x4f\x81\x78\x9f\x15\x91\x31\x4d\xf1\x2e\x99\xb6\x32\x23\x53\x92\xa5\xb1\x65\x69\x4b\x72\x92\xb5\xf5\x29\xfe\x9a\x24\x28\xb6\x4d\x76\x73\xba\x9b\xba\x24\xd6\x7e\x9f\xf3\x1c\xe7\xc5\xce\x87\xc2\xfd\xd6\x6c\xea\xe2\x38\x19\x69\xcd\xc4\xec\x6e\xa0\x50\x00\x0a\x85\x42\xa1\x2e\x5e\xde\xe1\xea\x69\x91\x03\xa5\x28\xeb\x3f\x51\xba\x8a\xdc\x86\x81\xf8\x3b\x60\xa5\xc2\x95\x16\x6b\x52\x5b\x5f\x51\xdf\x09\xed\xb4\xf6\xb6\x17\x0f\xf5\x62\x8a\x3a\x54\x7b\x0f\x7c\xd8\x7e\xc3\x34\x58\x46\x4b\x4c\xd7\x64\x17\xe7\x2a\x15\x1d\x07\x31\x5c\xee\xd7\x94\x52\xb4\x6f\x70\x80\x34\x3a\xc2\x4e\xf1\x76\xa3\xa6\x0c\x6a\x97\x90\xe7\x51\xed\x9b\x52\xd1\xf7\x5e\x9c\x6e\x7c\x05\x98\x00\xee\xfb\x6c\x34\xaa\xfb\x05\x29\x3b\x91\x7c\x69\xcb\x91\xca\x37\x5d\xe0\xd1\x2b\x79\x6b\x28\xcd\xeb\x5b\x82\xf5\xe1\xfd\xfb\xf7\x76\x61\xca\x3e\x15\x90\x82\xb3\x69\x9d\x26\x2f\xe0\x99\x19\x4a\xd2\xd4\xae\xe2\xd6\x34\x2f\xd5\x83\xa4\x6d\xb2\x36\xc5\xf5\x9d\xae\x8a\x14\x9c\x3f\x8c\xfa\x41\xaa\x6a\xbb\x18\x02\xb0\xc4\x18\xe3\x67\x65\x44\x91\x9b\x72\x65\x89\x36\xa6\x61\xa4\x1b\xc9\x5a\x2d\xb0\x12\xb7\x84\x3f\x0e\xd2\x71\x12\x64\xb9\x7d\xf0\x94\x29\x24\x5a\x2c\x8f\x11\x37\xf2\xca\x41\xc8\x5d\x64\xf1\x61\x95\x59\x95\xe9\x27\xd4\xe5\x31\x3c\x0f\xd2\xc3\x24\x1c\xe4\x8e\x99\xa7\xcc\xad\x6f\x13\x97\xc7\x92\x65\x2f\x4c\xf3\xb0\x14\x65\x6e\xd9\x46\x5f\xb1\xc5\xc8\x69\xc6\x5f\xec\x81\x68\x88\xa7\x76\xfa\x85\x9a\xec\xe6\xe1\x66\x16\x55\x5a\x54\x59\x88\x76\x7f\x5f\x1d\x48\x73\x48\xc5\x36\xa6\x1f\x6a\x8e\x8f\xc1\x20\x8b\x13\x2e\x3f\x73\x03\x4a\xf0\x46\xaa\x20\x52\x56\xdb\x53\x59\x69\x57\x63\x23\x6e\x30\x69\x45\xb4\xa8\x28\x5e\xfb\xb4\x54\x2f\xc1\x60\xf0\x0c\x3e\xe8\x3d\xc3\x2b\x4f\x49\x77\x48\x8d\x30\x25\x1c\x32\x14\x2b\x15\xa7\xc1\x4c\x85\x5b\x75\x56\x71\x36\x2e\x95\x2b\x36\xc9\xbe\x8f\xcf\x15\xc9\xa8\x18\x4a\xae\x8e\x4a\x7b\xce\xfc\x4c\x3c\x7c\xf4\x4b\xac\x42\xf5\x7c\x12\xf7\x83\x49\x95\x0c\x6a\x35\xb0\x5f\xb3\xd4\xa9\xae\x26\xc3\x41\x30\xfb\x70\xdb\x66\x49\x65\xab\x51\xfa\x32\xaf\x49\xc5\xb8\x55\x36\x68\x7a\x50\xaa\xa9\x29\x79\x85\x92\x7b\x7a\x16\x05\xb5\xdc\xce\xc6\xd2\x2d\xc0\xb0\xef\x7d\xd6\xad\xaf\x57\x9e\x59\x76\xc6\xcc\xcf\x4d\x1a\xf8\x3e\xeb\x36\xda\xf0\x82\xce\xe9\xb3\x6e\xe3\x25\x7d\x14\xb4\xf0\xac\xdb\xa4\x55\xc2\x7e\x10\x3d\xeb\x36\x9b\x15\xdd\x0b\x01\x1e\xd9\x20\x3d\xeb\xb6\x5a\xf0\xcc\xad\x91\x9f\x75\x5b\x14\x3c\xe3\xec\xcf\xba\x2d\x8a\x16\xb7\x1a\x7a\xd6\x6d\x91\x06\xb9\x2d\xf1\xb3\x6e\xab\x79\x73\x56\x69\xbe\x7c\x74\x6b\x78\x74\x6b\xf8\x67\xbb\x35\xf8\x7c\x1a\xee\xec\x7a\x57\xdc\xdb\xa0\x80\x2b\x01\x94\xfb\x80\xb3\x87\xf4\xd4\x83\xb7\x8b\x6d\x1f\xa5\x8f\xde\x6d\x8c\x1f\x0b\x78\xe6\xad\xae\xae\xca\xd0\x76\xae\x70\x79\x2c\xef\x33\x61\xf1\x00\x0e\x67\x63\x14\xcc\x42\x05\xf7\x07\x3a\x90\x4c\xc2\x34\xc3\x79\xe7\x85\x08\x67\x9f\x64\xa1\xdb\x0a\x57\x18\x27\xe6\x05\x8b\xd5\x8a\xaf\xd0\x12\x02\x9f\x2a\x7e\x59\x9b\xda\x07\x9c\x39\x36\x35\x7d\xf3\x52\x77\x97\x9b\xb3\x4a\xab\xf6\xb8\x5b\x3c\xee\x16\xff\xec\xdd\xe2\x3b\x75\x82\xbb\x3f\x7f\xb5\x82\xee\x74\xd2\x27\xe0\x10\x27\x69\x1c\x05\x93\x47\xc7\x80\x87\x76\x0c\xb8\x29\x66\x2a\x1e\xe1\x4b\x69\x7f\x9e\xa7\x00\x97\x05\x6d\xed\xf7\x8c\xcd\xea\x27\x67\xa1\x3b\x5c\x71\x87\x53\xb2\x11\x1c\x05\x97\xef\xf0\xa2\xab\x2f\xb5\xe8\x4a\xe5\xe9\x93\x27\x26\x6e\x56\x81\x1c\x07\xf7\xe2\x57\xb9\x76\x3b\xe2\x83\x62\x01\xfe\xe4\x49\x41\x03\x87\xc2\x77\xb8\x78\x70\x84\x07\xf1\x05\x8d\x31\x99\x77\xe9\xc9\xcb\x39\x71\xd5\xbf\xe6\x0c\xc8\x3c\x9a\xc4\x83\x2f\xc5\x28\x45\x2b\x9b\x43\x2c\xbe\x72\x45\x2c\xe7\x8b\x8d\x9b\x77\xf4\xee\xd9\x74\x42\xce\xfd\x42\xfb\x89\x65\xee\xc9\x5d\x76\x07\xde\x2e\x15\x9f\x9f\x62\xb3\x93\x3f\x37\xcb\xdc\x65\x99\x73\x63\x20\xef\x92\xac\x59\xc3\x4a\x23\xca\xe2\x95\x6f\x35\x0a\x52\x6e\x4f\x38\x55\xfb\x6e\x3b\xbc\x97\x22\x0a\x38\x55\xde\x7d\xb8\xf3\xc1\xe6\x02\xb5\xb0\x9c\x0e\xb5\xb0\x47\x2c\xb7\xe5\x72\xbe\xdd\x4a\xe1\xdc\xa1\x22\x32\xb4\x42\xa6\x9c\x5e\x7f\x94\xd3\x1f\xe5\xf4\x7f\xb6\x9c\xce\x84\xf4\x74\xec\xd1\xea\x2c\x10\xbf\x71\x82\xe7\x53\x02\xfa\xe7\x05\x4a\xa0\x41\x9c\xe0\x6a\x18\xeb\x72\xfa\x5a\xe1\xf8\x4b\x05\xe3\x35\x2c\x0a\xfb\x00\x85\x8e\xc7\xe3\x07\xd7\x0e\x7d\x3f\xf2\x38\xe1\x8e\xc7\x63\xed\x76\x03\x5f\xb2\xdc\x15\x3b\xdf\xe2\x42\x27\x1d\x2f\xbe\xd0\x49\xc7\x70\xa1\x43\x05\x97\x65\xee\x6d\xf2\xe4\x7c\xff\xe6\x64\x89\x07\xca\xd6\x74\xe1\xbc\xa9\x63\x22\x42\x3a\x1e\x7f\x72\x17\xd0\xad\x8a\x90\x43\x97\x95\xd7\x68\xa8\x3b\xe2\x19\x2d\x3a\xbe\xde\xad\xb9\x14\x67\xfb\xc1\x15\x23\x82\xe3\xf0\x0f\xf3\x72\x58\x69\x7b\x51\x51\xdd\x6c\xec\x36\x88\x84\xd1\x61\xfc\x6b\x3e\x02\xae\x22\x77\x6b\x78\x1a\x24\x5f\x4e\x92\x79\x9a\xe1\xe1\x21\xb6\x2e\x83\x95\xe6\xf3\x0b\xde\x0d\x89\x08\x13\x99\xee\x30\x08\x73\xda\xf7\x96\xb9\x1b\x05\x04\xc3\xe1\x61\x12\x5e\x04\x19\xa6\x47\x42\x4f\xeb\x79\xc5\xee\xd6\x77\x9a\x3b\x74\x61\xf7\xf3\x8a\xdd\x0d\x81\x71\x90\x2e\x6c\xdd\x5b\xe6\x6e\x4d\x9f\xe3\x8c\x6e\xe8\xb9\x63\x9f\x53\xea\xee\xcd\x17\x98\xfb\xbc\x62\x77\xa6\xfb\xe3\xeb\x69\x6e\xe3\xbe\x22\x77\xa6\xfa\x45\x0d\xfb\x8a\xdc\x75\xc8\x89\x1c\x97\x61\x0a\x7a\x27\x89\xa7\x87\x41\x9a\x5e\xc6\xc9\x30\x6f\xfc\x0b\xd6\xb9\xf3\x3a\x58\x34\x26\xbe\x22\x77\x26\xc3\x45\x0d\xfb\x8a\xdc\x07\xeb\x59\xd4\x76\x4e\x29\x77\xf3\xe2\x61\x75\x15\xa5\xf3\x3e\xdc\xbc\x61\x48\x4d\x35\x8f\xe4\xf3\x34\x4c\xd3\x30\x3a\x7f\x5a\x18\xdb\x59\x9c\x9a\x57\x57\x0a\x96\x8e\xaf\x0e\x3d\x05\xca\xd7\x3b\xa2\xc5\xb7\x5c\xc7\xe3\xb1\x92\x87\xd4\xb0\xbd\xd0\x4e\xd1\x86\x65\x44\xab\xf1\x78\x86\x7e\x3c\x43\xff\xb3\xcf\xd0\xf2\xae\xab\xff\xc7\x1f\xc6\x5d\xd7\xe6\x04\x5f\xa1\x37\x38\xc1\xe7\xe9\x1f\x41\xfa\x47\x88\x5e\x07\x13\x7c\xf5\x9f\x49\x36\x4a\xab\xe3\xb9\x7e\x1c\xee\xb0\xa0\xe8\x47\x78\x84\x13\x1c\x0d\x70\x17\x91\xf6\xd3\xee\xea\xea\x79\x98\x8d\xe7\xfd\xea\x20\x9e\xae\xfe\x16\x46\x3b\x61\x74\x90\x9c\xaf\xfe\xb6\x75\x18\x1f\xf7\xc6\x41\x18\xad\xf6\x27\x71\x7f\x35\xbd\x0c\x92\xe9\x6a\x18\x65\x38\x89\x82\xc9\x2a\xe9\x12\xbe\xca\xf8\xbf\xd5\xf3\xf8\x7f\xbd\x6f\x36\x1f\xf8\x6a\x4c\xde\x77\x1d\x13\x6c\xfe\xe1\x87\x6b\xf8\xf1\xb7\xb8\xec\xa2\x96\xaf\x38\xbb\x8c\x93\x2f\x47\x18\x22\xde\xe7\x29\xca\xcd\xe2\xb6\xb6\xbc\xff\xc7\x1f\x9f\x72\x4a\xdd\xc5\xb9\xf3\x3a\x1a\x6c\x47\x41\x7f\x82\x17\x61\xa9\x94\x74\x23\xe8\x2e\x70\x17\xdc\x2e\x83\x59\x41\xdc\x64\x49\x0f\x6e\xce\x02\x77\xc0\x6d\x18\x5f\x46\x2c\x99\x41\x1e\x62\xbc\x98\x1b\x2b\xc7\xd7\xe2\x3e\xcb\x1e\xc4\xe6\xb3\x02\x68\xd1\x42\x6e\xa4\xac\x6f\x77\x46\x29\xc1\x59\x12\xe2\x8b\x45\x61\x44\x78\x31\x37\x5a\x8e\xaf\x77\x21\xad\x8c\xec\x76\x0b\x88\x8a\x94\xf1\x90\x93\xf1\xe9\xce\x43\x74\x8e\x0b\xf8\xc4\xbb\x71\xd1\x3f\xdc\x61\x4c\x68\x12\xa8\x05\xa1\xd6\xdd\x38\xe8\x1f\xee\x3c\x1a\x2c\xef\x5b\x3e\x32\xb4\x90\x1b\x1f\xeb\x1b\x47\xa9\x55\x08\xa5\x9c\x5b\x5d\x4b\xc5\x69\xb2\x65\xe5\xf6\x4f\xf2\x43\xe5\xa5\x64\x44\xf2\x25\xe7\x03\xca\x8d\xe3\x4c\x7f\xe6\xd4\xaf\x00\x22\x24\x28\x1f\xcf\xb1\x72\x31\x39\x9b\x2b\x0f\x8a\x2c\xfe\xa0\xd7\x8c\xe3\xf0\xc2\xeb\x1b\x43\xe6\x04\xbe\x7b\xcf\x90\xf9\xb0\x1d\x4a\x59\x0d\x36\x7c\xf7\x1c\xaf\x1c\xe7\x2b\x22\x2c\xb9\x62\xe6\x3b\xef\x25\x9b\x8f\x67\xaa\xc7\x33\xd5\x3f\xfb\x4c\xc5\x0e\x54\xfc\x82\xe8\xdb\x26\x7b\xb9\x8d\x61\x35\xf7\x8e\x0a\x66\x21\x17\xc6\x69\xa6\xe0\x6c\x9c\x67\x81\x46\xaf\xcb\x72\xc3\x1b\xf3\xd2\xd9\xf5\x8c\xc8\x07\x2c\x94\xf1\xab\xa7\x0a\x03\x0f\xb3\xc1\xb8\x44\xbe\x9b\xb1\xea\x06\x41\x8a\xd1\x0a\xa1\xf8\x34\x5b\xe9\x6a\x9f\x60\xb2\x92\xf3\xb4\x9a\x8e\xc3\x51\x56\x32\xf2\x92\x21\x2b\xc7\x70\xcd\x2e\xc0\x58\x32\xb8\xaf\x45\xf8\x92\x79\x3b\xc3\x85\xec\x2b\x07\x1a\x33\x1c\x0d\xc3\xe8\xfc\xc1\xf1\x38\xa4\xed\xa8\x36\x44\x2e\xa4\x58\x0c\x5a\x1b\x1b\x03\x9c\x55\x99\xe6\x69\xbb\x51\xa4\x03\x51\x6a\xb1\x25\x21\x83\x66\xca\x08\x1a\x29\x38\x64\x27\x87\x54\x1d\x85\x51\x9a\x05\x93\x49\xa1\x96\x8d\xd2\x6e\x37\x7e\x7f\xa1\x1c\x3c\xce\x71\xf6\x3e\x3e\x2f\x10\x44\x80\x94\xf2\x86\x0f\xa0\x2d\x1a\x45\x72\x5a\x9d\xc5\x0b\x03\xb9\x90\x22\x0b\xda\xeb\x8d\x83\xe8\xdc\x1d\xb1\x60\x81\x8c\x25\xe6\x4b\x35\xc9\xd2\x46\x4f\x13\x84\x48\xc7\x94\x46\x62\x16\x0c\xf2\xec\x96\x8e\x1c\xe9\x78\x5c\x05\xd6\x68\xb1\x9b\x74\x6c\xb3\x1b\xbf\xf8\xb4\xe0\x96\xc6\x22\x03\x64\xdd\xd2\x68\x96\x04\xf7\xaa\xa6\xf7\x13\x23\x72\x69\xea\x1f\x0e\x11\x9b\x74\x91\x75\x4d\x41\x9b\x65\x38\x98\x45\xef\xd6\xbc\x41\xc6\xf7\xd0\xb6\x4a\x7a\x96\x24\x4a\x71\xc0\xd9\xb8\x4b\xfe\x43\x81\xa5\xe3\x71\x97\xfc\xa7\x42\xa5\x57\x57\x62\xa7\x56\xeb\x51\x26\x7d\x94\x49\xff\xe1\x32\xa9\x54\xf4\x73\x27\xeb\xdb\x38\xb6\x38\x04\x52\xea\x20\x7e\x84\xcf\xc9\x3c\x07\xc9\x66\x3f\xf4\xe4\x37\x4a\x57\xdf\xea\x45\xab\x9f\xd3\x58\xe4\x10\x0a\x07\xc1\x4c\x05\xe2\x83\xb1\xd7\xdb\x3c\xb4\x21\x28\x98\x30\x4f\x74\x66\xbe\x8c\x36\xd0\x4a\xed\x6a\xd0\x19\xbe\x1c\x36\x06\xc3\x56\xeb\x65\xb0\xd6\x6e\x0d\x5a\x2f\x5b\x8d\x4e\x0b\xd7\xd7\x6b\x2f\x07\xed\x1a\x6e\xb6\x86\x9d\x56\xbb\xd3\xe8\xaf\x48\x5c\x5c\x60\x82\x7a\x50\xaf\xd7\xfb\x83\xda\x5a\x6b\xf0\x72\x30\x0a\xd6\xd6\xeb\xa3\xda\xa0\xb9\x8e\x3b\xcd\xfe\xb0\x5d\x1f\xbc\xac\xf7\xd7\x83\x51\xad\xb6\xe2\x67\x4e\x14\xc7\xae\x22\xea\x06\xfd\xb0\xeb\x18\x44\xc9\x0a\x99\x1f\x7c\xd7\xd9\x3f\xba\xd5\xd3\xc2\x04\x6d\x0b\xb2\x39\xae\x0e\xb8\x76\x77\x29\x54\x8d\x63\xe6\xcf\xe2\xb3\x6e\xbd\xf2\x6c\xc1\x3c\x3d\xeb\x36\x08\xb3\x6d\x3f\x32\xdb\x47\x66\xfb\xcf\x66\xb6\x92\xd7\x72\xed\x97\xc1\x6c\xf3\x2c\x93\x47\x49\xfc\x07\x9e\x06\x51\x75\x88\x7f\xbe\x17\x06\xed\xf2\x51\x37\x1c\xd4\xcd\x1b\x52\xcb\xa6\x56\xbb\x06\x65\x79\xe2\xd9\x27\x78\x54\x32\xd7\x50\x4d\xa2\xf2\x9d\xbe\xd0\x72\xdb\x18\x25\x52\xb3\x84\xe1\xe0\xac\x14\x35\xbe\x28\x75\x74\x05\xb4\x52\x45\xff\xa0\xd4\xb0\x6e\x73\xa3\xf9\x64\x42\x45\x4b\x3e\x16\x6a\x16\x6d\xf3\x76\x53\x1b\xa7\x64\xaa\x0d\x91\x05\x3a\x99\x2e\x4c\x4a\xce\x52\x70\x03\xba\xa0\x55\x20\xb4\x4b\x05\x55\x23\xe3\x38\x2d\xb9\x47\x0a\xaa\x59\xc7\x21\xef\xf7\x8d\x96\x70\x5c\xbc\x5a\x75\x75\x49\x81\x63\x6a\x70\x5c\xb1\x5b\x8c\x11\xfe\x0f\xd7\x5b\x5a\xb7\x4b\xf0\x2f\xda\xe1\x58\xcb\x31\xbf\xa0\xd3\x34\xbc\xbe\xda\x6b\x96\x53\xdd\x95\x67\x3d\xbf\xdf\x14\x94\x3e\x8b\x5a\xae\x7c\xb5\xef\x26\x45\xfe\xf8\x23\x4b\xac\x8f\x7e\xd8\xa0\x84\x63\xbc\x22\x1b\xca\x28\x8c\xf0\x90\x8f\x93\x01\x41\xb4\xd5\x65\xb5\x3c\xc3\x05\x19\xe8\xb3\x18\xe1\x2b\x1a\x2c\x89\x5b\x98\xa3\x51\x12\x4f\xe5\x69\x5b\x24\x76\xaf\xa2\x7d\xb2\xb1\x85\x38\x65\x94\x04\xc3\x64\x8c\x25\x03\xc6\x0d\xd2\x6d\x22\x92\xf0\xb4\x71\xdd\x61\x23\xf5\xf5\xc3\x7c\x32\xb9\x51\xac\xdd\xc3\x11\xc2\x57\x61\x0a\xc5\x9d\x43\x6e\xb4\xe8\x55\x18\x86\x23\x99\x0b\x8d\xb7\x46\xb3\xa1\x81\x9e\x6d\x82\xa3\xf3\x6c\x8c\x5e\xa0\xfa\x59\xd9\x91\xd7\x09\xca\xcc\xe2\x59\xa9\xfc\x0a\xad\xae\xf2\x8b\x2f\xc2\xff\x61\x3d\xc1\x68\xfd\xa0\x0a\x37\xfa\x70\x53\x03\x07\x89\x59\x16\xbb\x49\x51\x37\x84\xf0\x11\x23\x7b\xc5\x7b\xe1\xa5\x46\x1d\x9a\xce\x7d\xfb\x9f\xb5\x54\xd5\xa4\x8e\x10\x25\x11\x4f\x75\x05\xe4\xd5\x9f\x87\x93\xe1\x5b\x9c\x95\x94\xe3\x39\x8e\xe6\x53\x9c\x04\xfd\x09\xee\xa2\x2c\x99\x63\x5b\xf7\x17\x4c\xe1\xc6\x4a\xb0\xf5\x6a\x3a\x9b\x84\x59\x69\xa5\xba\xa2\x04\xdc\x64\xfc\x1e\x0a\x83\xf6\x96\x4f\x14\xbc\xe1\x73\xf2\x33\xaa\xab\x33\x12\xf7\x3f\x9f\xf2\x1a\x67\x84\x1b\x6b\xcf\x5f\xbf\xa2\x3f\x6f\x5e\xa9\x85\xcd\x22\xaf\x34\x95\x98\x68\xbe\x7e\xc6\xd3\x6a\xc1\x3f\xee\x3c\x61\x71\xff\x73\x05\xca\x57\xe8\x90\xb1\xbe\x10\xf8\x41\x7a\x1d\x0d\xde\xc2\x7e\x43\x44\x5e\xe8\x42\xf9\x8c\x0f\x01\x0c\xe2\x26\x2b\x52\x52\xfc\x34\x8c\x6a\xda\x24\x01\x08\x9d\x65\xc0\xf5\x32\x7a\x0e\x38\x54\x07\xe3\x20\xd9\xcc\x4a\xb5\x72\x35\x8b\x3f\xce\x66\x38\xe9\x05\x29\x2e\x95\xf9\xe7\x94\xc8\x0f\xa5\x7a\xd9\xbb\xf1\xf0\x99\xf5\x67\x30\x97\x1b\xb7\x4c\xc7\xce\x43\xa2\xf1\x1a\xe7\xa4\x43\xf6\x8a\x11\x02\x8a\xca\x13\x4b\xe2\xad\xbe\x8f\x41\x56\x3a\x43\xd3\x43\x97\x44\x57\x02\xa2\xdb\xbd\xa2\xb2\xe1\x06\x3f\xf9\x1d\xe4\xa3\xbe\x5c\x2f\xe5\x65\xbf\x3f\x0a\x18\x92\x76\x4e\xce\x0e\x41\xcb\xcb\xf6\x4a\x4d\xa8\x84\x93\xa4\x82\xf4\xad\x83\xff\x71\x5c\x68\x19\xf7\x60\xb3\x9a\xca\xe5\xc1\x8d\x1c\x32\xb6\xc8\x39\xde\x9c\x50\xd9\x23\xcd\x03\xc8\x32\x00\x2a\xb3\x7a\x8e\x7d\xdb\x89\xdc\x7d\x07\x09\x26\xb2\xe2\x6c\x9e\x60\xf4\x5f\xc7\x07\x1f\x8e\x0e\x7b\x88\xb7\x72\x39\x0e\x07\x63\x38\x3c\xf1\x1d\x28\x8c\x50\x1f\x94\xb6\xac\x88\xc1\x11\xe5\x5b\xc1\xf7\xaa\xd5\xea\x0d\x53\xe1\xb9\xf6\x66\x44\x8e\x84\xc9\x6c\xa0\x54\x75\x72\x47\xd9\x71\x0f\x59\x04\xd7\xcc\x42\xc7\xb4\x9b\xeb\xaa\xf2\xa8\xad\x25\x3f\x3d\xd3\xf5\xeb\x64\x9a\x58\x15\x63\xb3\x2a\xc1\x9e\xa8\x8a\x82\x64\xc9\x56\x49\xa5\x92\xd8\x27\xcb\x65\x75\xca\x18\x56\x6c\xa2\xf9\xac\xa9\xd3\xee\x9b\x3a\x56\xd3\xa3\xe1\xe4\x03\xa4\x1c\xcc\x8d\xa0\x3d\xe4\x88\xdd\x79\x3c\x62\x3f\x1e\xb1\xff\xd9\x47\x6c\x45\x9f\xc9\x38\xc4\x94\xb1\x74\xfd\xa4\xfd\x5f\x78\x34\x4a\xf0\x35\xfa\x35\x9c\x0c\xbe\x60\xf4\xfa\x33\x1e\x8d\x7c\xe1\x7a\x96\x8a\xed\xb3\x1f\x24\xe4\x08\x7f\x10\x44\x03\x1c\x40\x59\x57\x54\x9f\x5b\x04\x02\x62\x55\xde\x06\x17\xe8\xd7\x38\x1e\xa2\xd7\xe7\xde\x43\x7e\x4b\x1e\xf2\xff\x8b\x71\x53\xcd\x7b\x98\xb1\xd8\xbc\xd4\xf8\x8e\x48\x75\x66\x36\x7b\x57\x2a\x7b\x9c\x24\xb1\x11\x3d\x68\x95\xbe\xa3\x46\x08\x74\xdb\xd9\xcb\x56\x52\xb2\x31\xce\xe2\x28\x0d\xfb\x13\x4a\x60\xb3\x00\xbc\x48\xd0\x94\x5d\xfa\x90\xbd\x68\x96\xc4\x17\xe1\x10\x27\xa9\xa8\x15\x4c\xd2\xd8\xae\x1a\x4f\x26\xa4\x2a\xa1\x36\xee\xc0\x8d\xa2\x78\x48\xbf\x86\xd1\x20\x9e\xaa\x90\x09\x30\x96\x95\x82\xde\xb9\x66\xe1\x14\x93\xc5\x16\xa6\xa8\x8e\x52\x3c\x88\xa3\x21\xec\x8e\x61\x74\x3e\xc1\x59\x1c\xc1\x70\x92\xee\xe5\x1c\xf4\x39\xaa\xda\x71\x9f\xbf\x44\x1b\xa2\x2b\x8a\x9e\x81\xb4\x0d\x1a\xe0\x1b\xe5\x25\xc7\x45\xd5\x3a\x78\x0f\x7f\x44\x42\x19\x27\x71\x14\xcf\xd3\xc9\x35\xc4\xc1\xf0\xec\xc3\xe4\x93\xe3\x3c\x82\x86\x41\x16\x78\x4f\xc8\x7a\x6f\x35\x95\x47\x34\xd4\x3a\x4f\xc0\xa8\x27\xb5\x1f\xb4\xde\x6b\x69\x72\xe3\x28\x8d\xc9\xd6\x45\x88\xa2\x44\x49\xa3\xba\x17\x5d\x04\x93\x70\x78\xc8\xca\x97\x54\x99\x87\xbb\x61\xc3\x60\x28\x12\xbe\xbe\xc7\x33\x32\xaf\x66\xf1\x21\x7d\x07\x28\x55\x69\xef\x2b\xd0\x4d\x66\x6d\xa1\x9c\x5f\xd8\xa9\x7c\x43\x9f\x2b\x2a\xcc\x32\xd0\xfc\xae\x1c\x3a\xc5\x1b\x09\xd3\x5f\x08\xba\x47\x94\x0a\xb1\x10\xd4\x94\x6e\x66\xe3\x24\xbe\x44\x7a\xf7\xcc\xf2\x5a\x77\x58\x37\xe9\xa7\x6a\xa1\x93\x7f\xb0\xd4\xec\x83\x34\x9b\x4b\x02\xe6\xb9\x54\x48\x3f\x8b\x89\x01\x80\x5b\x14\xa1\x44\xcf\x2d\x44\x1b\x3c\x09\xb3\x22\x1b\xe7\x51\xc7\xfd\x10\x82\x3d\xf7\x54\xee\x67\x20\x0b\xc8\xf3\xa4\x53\x38\x49\x3c\x29\x35\xd5\xde\x94\x4d\x7b\x1b\xc4\x33\x56\xdd\x86\xc6\x16\x0f\x99\x55\x5b\x6d\xdf\x12\x72\x59\xde\x70\x8d\x04\xcd\xe8\x9c\xfe\x63\x83\x8b\x1a\xf3\x4e\x06\xa4\xc0\x1b\xf2\xdd\xa1\x64\xa2\xf5\xee\x83\x30\xa1\x85\xef\x8c\x30\x01\x27\x95\x3a\x39\x93\xb9\x1d\x29\xa6\xf7\x40\x8b\x3a\x0d\x72\x3d\x1b\xcc\x46\x89\xb7\x72\x27\xd2\x4b\x17\xd1\x9e\xd6\x21\x41\x74\x68\xc1\xf6\x87\x33\xb1\xaf\x12\x69\x93\x9f\x09\x99\xc8\x67\x51\x5c\xc6\xa7\xca\xad\x9a\xcb\xa5\x25\x51\x57\xdf\xf5\xbd\xdb\xfd\xa2\x9d\x3b\x23\x47\x2a\x26\xb8\x98\x88\x92\x6f\x87\xe2\xd3\x42\x8e\x4d\x83\xff\xdf\x00\xb4\xbd\xe1\xc2\x25\xe3\xf8\x2a\xec\x92\x38\x26\x59\x3c\x8c\xd1\x60\x82\x83\x68\x3e\x43\x11\xc0\x27\x03\x2c\x8e\xed\x79\x43\xa5\x60\xef\x58\x79\x14\x49\x35\x22\x8a\x68\x5c\x1f\x4b\x22\x1c\x9d\xd2\xd2\x67\x44\x48\x22\xd5\xbb\x88\x02\x09\x87\x5d\x0b\x50\xd7\x05\xb2\x2b\x7f\x82\x5e\x17\x31\x5f\x66\x7d\xf4\x35\x06\xc0\x04\x30\x7d\x37\x67\x08\x95\xc4\x0a\x5f\x30\xb9\xf1\x4c\x08\xa5\x44\x04\x65\x76\xb4\x70\xba\x39\x0f\xc9\x91\x2e\x34\x75\xc7\xa4\x8e\x63\xce\xad\xb9\xcd\x1d\x79\x01\x42\x27\x52\xa8\xcb\x3b\x44\x4d\xcb\x1c\x83\xfc\x4a\x19\x1e\x89\x3f\x1b\x9d\x12\xd3\xa8\x7e\xc1\xd7\x69\x49\xd6\x2d\x73\x2d\xef\xc6\xc6\x06\xaa\xa1\x1f\x7f\x44\xbe\x31\x24\xc4\x94\x9c\xd0\xf7\x25\xad\xd0\x2b\x7d\x9c\x4d\x01\x38\x67\xbc\xe5\xee\x93\x60\xc2\x0b\x88\xfc\xcf\x87\x7d\x8a\x07\xe3\x20\x0a\xd3\x29\x3f\x86\xe6\x33\x07\x00\x90\x3f\xbc\xb4\x0d\x75\x60\xbf\x60\x3c\x13\x09\x04\x78\x67\x57\x7f\xfa\x9c\x8e\xc3\x88\x34\x74\x35\x88\xa7\xb3\x09\xbe\x0a\xb3\xeb\x6e\x1b\x8e\x64\xa4\x00\x21\x88\x12\xd9\x1c\xbe\xe0\x6b\xaa\x29\x10\xa3\xa9\x8c\xd7\xea\x2a\x4a\xf0\x34\xbe\xc0\x28\x98\x4c\xa0\x57\x69\x05\xe1\xab\x01\x9e\x65\x20\xf6\xb3\x57\x6a\xf9\x6c\x8c\xaf\x51\x84\xe9\x88\xf4\x31\xab\x3f\x24\x3d\x9e\x07\x93\xc9\x35\xea\x5f\xc3\x90\x91\xe1\x61\xb9\x00\x80\x66\x7e\x25\x1b\x52\x18\x9d\x97\xca\xca\x3e\x50\xfa\x41\xeb\x1d\xfa\xfa\x95\xe0\x5b\x0d\xa3\x21\xbe\x3a\x18\x95\xc0\x4f\x91\x10\xdb\xa7\x95\x32\x4c\xfe\x8b\xba\xb9\x41\x28\x14\xf6\x05\x5f\x9f\x55\xc5\x4a\x34\xed\xa1\x6d\x8a\x24\xe5\x2d\xdb\xe4\xbf\x31\x79\xc2\x29\x93\xcc\xfb\x80\x1a\xe7\xa2\x38\x2a\xc2\x13\xa8\x4d\x6d\x1e\x4d\x32\x93\x61\x5b\x05\xea\xa1\x42\xd4\x21\xe0\x1c\x9d\x49\x71\xa6\xf5\x9e\x00\x56\x54\x91\x15\x34\xa8\x6e\x9f\xec\x7e\x3a\x3c\x78\xff\x7e\xef\xc3\xdb\x4f\x27\x7b\xfb\xdb\x07\x1f\x4f\xd4\xe3\x51\x91\x19\xb0\x85\x2a\x4d\x62\x7a\x90\xa3\xa3\x2d\x93\x11\xbc\xb6\x82\x2c\x40\x1b\xe8\xf4\xec\x95\xfe\x7e\x0f\xfc\x8d\xf9\xeb\x62\x4b\x55\x00\xac\xce\xe6\xe9\xb8\x64\xd2\x3d\x13\xf1\xb4\xd2\x7b\xc3\x94\x16\xfe\x82\xaf\xcb\xd6\x18\x48\x80\x4b\x0c\x5e\x21\x71\x53\x40\x56\xd3\xe5\xae\xae\xa2\x69\x30\xd3\x98\x64\x08\x64\x0b\x0c\x05\x48\x8c\x90\xa6\x3e\x4c\xfb\xc1\x4c\x51\x5d\x28\x7a\x6d\xdd\x55\x9c\x0a\xae\xc0\x35\xca\x7f\x9a\x63\xb0\x1f\xcc\x4e\xa1\x5a\x08\x5b\x3c\x1f\x99\x53\x28\x7e\xa6\xb8\xa4\x8b\xc6\x35\xc7\x79\xb4\xb4\xcc\x1c\xeb\x52\xb3\x16\xdf\xe4\xe4\x60\xeb\xa0\xcb\x89\x0c\x4d\xe2\xf3\xff\x30\xa5\xea\xd8\x23\x57\xdf\x55\x92\x2e\xa0\x2c\x48\x9d\x47\x47\xf6\xad\x3a\x0d\x66\x25\x9f\xb1\x02\xff\x03\xfb\xc5\xa1\x1c\x65\x32\xf6\xec\xa8\x17\x0e\x55\xcf\x1b\x41\x11\x5f\x30\x4a\xe7\x09\xe8\x89\x39\xb3\x0a\x53\x94\x66\x21\xa1\x07\xca\xc9\xf1\x10\x05\x23\xf0\x10\x4a\x92\xf0\x22\x98\x18\x7b\xad\x06\x93\x0c\x08\xf8\xfd\xd3\xa5\x11\x0e\xcf\x4c\x14\x65\x97\xaa\x03\x69\x0f\xa0\xd7\x11\x5f\xbc\x1e\x33\x5c\x77\xa2\x7e\xba\x41\x78\xc2\xf4\xcc\x8e\x1a\xa3\x60\x92\x62\xf5\x96\x8d\xf9\x3d\x2d\x1c\x53\x56\xff\x87\x1f\x58\x9b\xe8\x16\x30\xc8\xbc\xc0\x8c\x2b\x8b\xd6\x73\xf8\x7f\x65\x8d\xe7\x0f\x50\xb3\xc0\x38\x16\x57\x0c\x20\x8d\xc2\x94\x5e\x42\x45\x7d\x94\x8c\xc5\xee\x1f\x26\x1d\x17\xbf\x9e\x01\xa9\x97\x9c\xbe\x94\x4b\x47\x66\x54\x0d\xfd\xc6\xcb\xc8\xbd\x64\xe7\xae\x60\x0a\xe9\x67\xdd\x06\xc4\xf6\x61\xca\xf0\x67\xdd\x26\xf8\xa1\xae\x15\xb9\x23\x63\x41\x37\x71\x96\x85\xd1\xb9\xdb\xb5\x17\x18\xd3\x50\xc9\x7d\x8c\x36\x84\xd3\xda\x2b\xab\x84\x0c\xf5\x2c\xec\x83\x7c\x51\x8b\x58\xa3\xac\xdf\x04\xe5\xf5\xc7\x6b\xbd\xc7\x6b\xbd\x7f\xf8\xb5\x1e\x0b\xe9\xcb\x4e\x2d\xb7\x09\xeb\xbb\xc8\x1c\xd6\x93\xfc\xc2\xc8\x7d\xb1\x8c\xe1\x2c\x5f\xd2\x75\x76\x38\xd8\x1c\x0e\x53\x18\x3a\xb1\xbb\x05\x11\xa8\xa5\x52\x34\xa7\xe2\x17\xf3\x7a\xab\x10\xe1\x2b\xcc\x20\x54\x1e\x82\xac\x00\x74\x53\xa5\xbb\xfd\xd3\xa7\xea\xf9\x80\x9d\xcf\x9e\x9a\x4a\x22\xb2\x6d\x3e\x65\xd7\x56\x4a\x39\x85\x57\xd1\x40\x3d\xdc\x97\x8e\x94\x8b\x23\xe6\x71\xa5\x71\x34\x26\x37\x91\xb1\x77\xa8\x1a\x7d\x42\x11\xdd\xb7\x79\x4f\x53\xc7\x66\xe1\xb2\xc7\xe1\x7f\xfa\xbe\x65\x6e\x4f\x3e\xdd\xa5\xb0\x10\xe4\x91\x88\x00\xe5\x1f\x7f\x04\xdc\xa9\x62\x2a\x8c\xce\x81\x1b\x97\x35\x88\xfc\xfa\x62\x51\x4e\x53\x0a\x51\x75\x53\xbe\x6d\x27\x85\x34\x34\x09\x52\x68\xe6\x38\x23\x93\xfd\xc3\xc6\x86\x35\xd0\xfc\xcf\x7a\xb1\xba\x4a\x73\xff\x6b\x24\x05\x4b\x2d\x4b\xe6\x44\x66\x4b\xd2\x0c\xa5\x31\xb5\x73\x9c\xcd\x80\x75\xc3\xd9\x39\x88\xae\x33\x72\xe0\xaf\xa0\x3e\x1e\x11\x06\x40\x97\x38\xbf\x42\x85\xd1\xa0\x4a\x46\xe3\x2f\x1c\x95\x7e\x70\x60\xfd\xe3\x8f\xc8\x35\xf2\x65\xab\x3e\xb2\xaf\x1b\x08\xaa\x0e\xff\x68\x6f\x67\x63\xca\x37\x23\x7c\x95\xa1\xde\xe1\x47\x34\xb8\x1e\x4c\x70\x45\x74\x13\x86\x5d\x6c\x36\xd0\x13\xe8\x32\xb3\x59\x9a\x25\xf1\x80\xf0\xac\x94\x8e\x8e\xd5\x8a\x72\x0c\x16\xcb\xc4\x35\x17\x8e\x8e\x30\xd2\x30\x4b\xdd\x54\x50\xad\x48\xff\x1c\xc3\x4a\x49\xc1\x27\x9a\x29\xc6\x60\x4f\x05\x00\xd3\x8c\x4d\xd1\xc5\x96\x6c\x3b\x28\x4f\xbe\x5f\xd3\x12\xea\xa6\x22\x85\xf0\xbd\x61\x45\xb2\x09\xf6\x5e\xd5\x21\x51\x9d\x01\x70\x16\xb2\x4e\xb8\x9d\xe4\x9e\x33\x2f\xa7\x37\xd9\x66\xbe\xc9\xbc\x21\xff\x21\x55\xd7\xb4\x47\xe4\x68\x45\x39\xf5\x9c\x72\xe1\xe7\xcf\x95\x72\x62\xbd\x2a\x27\x7d\xf8\x10\x0c\x87\xc2\xb6\x4b\x49\xfc\x29\xbe\x9b\xd3\xa3\x1c\x1c\x14\x16\xcb\x8d\xb7\xe0\xbd\x62\x2b\x4e\x05\x3a\x31\x12\xaa\xa5\xaf\x6c\x37\xd7\x62\x31\x1c\xc9\x57\xba\x56\x4a\xb2\x20\xd0\x2a\x18\xc8\x17\x42\x42\x9d\x45\xbf\x44\x6b\x11\x98\x50\x39\x97\x94\x39\x28\xe7\x8c\xb6\x53\xaa\x15\x08\xf9\x0d\xd8\x88\xac\xae\xa7\xbb\x20\xb2\xef\x63\x92\xd2\x47\xd9\xf7\x9f\x2e\xfb\x4a\x93\x36\x9e\xb1\xf7\xbe\x7c\x74\xf7\xfa\x41\xa4\x4b\xbb\x61\x3f\x10\xae\xb7\xf8\x8a\xaa\xab\xf3\x5c\x77\x8f\xa7\x41\x92\x6d\xb3\x82\xd2\xed\xd6\x7b\x35\x06\x6a\x25\x68\x96\xf7\xc5\xd0\x79\x2b\xaf\xc5\x25\xd8\x71\x96\x84\xd1\xf9\x0d\xb8\xb6\xb8\xde\x13\x69\xb9\x1f\x44\xea\xa7\x5f\x82\xc9\x1c\xdf\xa0\x0b\xf2\x0f\xbb\x0e\x21\x90\x47\x38\xc1\x0b\x6e\x48\x2b\xba\x79\x01\x44\xa9\x61\x38\xe9\x62\x71\x36\xae\x00\x46\x44\x5a\xaf\xd0\x96\xec\x2d\x0c\xd4\x6e\x74\x94\x21\xdd\x74\x3f\x88\x4a\x59\x5c\x66\xaa\x22\xd0\xe1\x90\xcf\x5c\xe5\x53\x72\x58\x11\x91\x7a\x90\x27\xa2\xb4\x12\x52\xf5\x0d\x85\xc8\xfc\x74\x57\x6c\xfd\x31\x83\xb8\x15\x26\x44\x16\x73\x39\xc4\xf0\x1e\x9d\xc4\xcc\xb3\x57\xed\x0e\x54\x67\xd0\x4b\x65\xbb\x6b\xbc\x3d\x21\xc7\x40\x37\x5c\x92\x2e\xb8\x48\x08\x4f\x69\x9c\x8d\xd5\x9c\xe0\xa5\x32\x34\xc2\xb0\x8d\xd2\x2c\xcc\xe6\x54\xe0\xb2\xcd\xbf\x86\x78\x16\xa7\x61\xa6\x62\xc9\xe0\x0a\xf4\x00\xcc\x60\x12\xe2\x28\x33\x2d\x31\x0a\x37\x6c\x99\x58\xf0\x5c\xe3\xf6\x08\x2e\x8b\x91\x3d\x7e\x5c\x05\x9f\x7b\x95\x2c\x48\x6f\x34\x8f\x86\x60\x13\x39\xc0\x49\x16\x84\x62\xfa\x3d\xcb\x47\x4c\xec\x72\xeb\xe8\xc1\x97\x90\xc0\xeb\x16\x6b\x89\x8d\x3c\x99\x4d\x23\xe5\x97\x22\xdb\x0a\xef\xf5\x2c\x96\x12\x2d\x01\xdd\xa5\x0d\x28\xb4\x39\x99\xe3\x2e\xfd\x87\x8b\xb9\x46\xb6\x77\xef\xac\xb0\xc9\x97\x93\x02\x81\xed\xc3\x01\xe2\x9c\x10\x71\x0e\x89\x4a\xd3\x79\x9a\xc1\x56\x87\xa7\x38\xca\x04\xdd\xf4\xaf\x33\x9c\x36\x1b\x65\x26\x8c\xff\x50\x36\x26\x92\x95\xbb\xf7\xe9\x4b\xad\xf9\xe3\xd5\x29\xa5\xa2\x79\x14\xfe\xf7\x1c\xa3\x70\x88\xa3\x2c\x1c\x85\x3a\x27\x2e\x34\xd7\x7c\x74\x0a\xcc\x30\x34\xe9\xe6\x9a\x01\xec\x3a\xca\x1e\xf4\xca\x24\x02\x3e\xc6\xa5\xa0\x1f\x96\xab\x41\x46\x18\x6b\x95\x8f\x2f\x07\xfd\xe7\x5d\x89\xc0\x92\x55\xf9\x28\x3a\x83\x20\xd8\xfb\xe1\xb3\x6e\x93\x88\xae\x3c\x73\xff\xcd\x59\xa5\x5d\x28\x57\x32\xd3\xee\xb6\x0b\x25\x6c\x7b\xa5\x2a\xe1\x63\x22\x5f\x8c\x82\x41\x16\x27\xd7\x15\xaa\x50\x26\x03\xfb\x84\xb0\x69\x22\xea\xc7\x23\x24\x7a\xb3\xb1\x81\x9e\xd1\x88\x4c\xcf\xa0\xcc\x93\xd5\x55\xd4\x8b\xa7\xd3\x38\xfa\xaf\xe3\xa7\x4f\x9e\x58\x9d\x97\xbf\x58\x03\x1c\xa7\xd2\x33\x32\x0c\x09\x7e\x56\xae\x20\xe5\x15\x8e\x06\x2f\xfa\x41\x8a\x3b\x2d\xe3\xc3\x74\xd8\x36\x8b\x5e\xcc\xbe\x0c\x47\xc6\xcb\x41\x38\x1b\xe3\xe4\x05\x85\x5c\x7e\xf5\xf4\xc9\xcd\xd3\x27\x78\x92\x62\xa4\x74\x86\x2a\xcc\x69\x5f\xf8\x30\x3c\x43\x3f\xfe\xc8\x3e\x54\x83\xe9\x50\xf4\x6d\x73\x7f\xeb\xe9\x93\x27\xf4\x43\xe9\x94\xe3\x5c\x41\x3a\xaa\xf0\x4c\x30\xa4\x1f\x28\x62\xf0\x5b\xc5\xe7\x4c\x8c\xb2\x8a\x18\x6b\x88\x46\xc3\x40\xa5\x7e\x12\x5f\xa6\x38\x29\x3f\x7d\xf2\x44\x8c\x58\x1c\x67\xd5\x5e\x72\x3d\xcb\xe2\xff\x3a\xa6\x55\x6f\xe0\xf4\xa4\x6e\x3f\xe2\x3b\xfa\xf3\xe9\xd3\x27\x25\xfd\x38\xf6\x04\x51\x8d\xc8\xf1\x38\x4e\xb2\xc1\x3c\x4b\xe9\x1b\xb2\x6c\x7a\x68\x03\xf1\xba\xaf\x94\xd7\x9f\x26\x61\x9f\x7c\xaa\x4e\xc2\xbe\xf2\x1e\x94\x61\x3d\xe8\x14\xf9\x4a\x4a\x55\x95\x77\x1a\x84\x60\x72\x1e\x03\x08\xf2\xe3\xd5\x53\x81\xc5\xfb\x38\xfe\x32\x9f\xa1\x2c\xe8\x4f\xb0\x82\xc9\xf1\x9b\x83\xdf\xd8\x99\x4f\xbc\xdb\xfb\xf0\xcb\x27\xd7\xfb\xe3\x8f\x6f\x3e\xed\xef\xfd\xf6\xa9\xe6\xfb\x50\xf7\x7d\x68\xf8\x3e\x34\x9d\x6d\xfb\xda\x51\x3f\x5a\x6d\xa9\x1f\xad\xf6\xd4\x8f\xbc\x4d\x31\x34\xbd\x78\x3a\x23\x07\xc5\x89\x3d\x44\xae\x29\x35\x6a\x0d\xe3\x79\x9f\x48\xfd\xa4\x96\x2c\x00\x2c\x56\xc5\x02\xa9\x96\x0a\x21\x84\x13\x44\x21\x7a\x8d\x1a\xed\xce\x2b\x14\x3e\x7f\xae\x81\x17\x32\x22\x7a\x8d\xea\x8d\x75\xeb\x1b\xf9\x1b\x9e\x86\x67\x68\x83\xc0\x78\x8d\xea\xaf\xf4\xef\xf4\x2a\x35\xa7\x56\x89\x56\x2b\xa3\xdf\x51\xed\xaa\x5e\xef\x9b\xf5\xe5\xe3\xcd\x53\xad\xd7\xbf\x06\x93\x2f\xe8\xed\x4e\xa9\xf1\xfb\x7a\x59\xef\xed\x15\x0d\x91\xa8\xbf\x0b\x8d\x97\x4b\x8d\x80\x32\xc8\x69\x3f\xbe\xd2\x3f\x82\xa1\x01\x69\xf3\x2a\x44\xbf\xa3\xd2\x95\xec\x10\xfb\xdd\x50\x7e\x37\x95\xdf\xad\xb2\xd1\x59\x80\x52\x4a\xaf\xd0\xcf\x3f\xff\x8c\xd6\xa1\x64\x7a\x85\x7e\x44\xb5\xab\xd1\x88\x0e\x50\xa7\x69\x54\x21\xab\xe3\xf4\x8a\x0c\x64\x7a\x65\x7c\xe2\x8b\xe7\x34\x85\xef\x57\xaf\x9e\x7a\x3b\x35\x9d\x4f\xb2\x70\x36\x09\x07\xa0\x25\xb0\xbb\x77\x45\xc8\x78\x78\x7a\x75\xf6\xca\xf1\xad\x45\xbf\x35\x9c\x1f\xd7\xe9\xc7\xd6\x59\x4e\xeb\xe9\xbc\x8f\x40\xbe\xa9\xa0\x69\x78\x85\x06\xf1\x64\x3e\x8d\x52\x8d\xfa\x55\x98\x44\x52\x28\x0d\xa1\x57\x3f\x11\x9a\xa9\xd5\xf9\x48\xb1\xc7\x5a\xbd\x56\x33\x87\x56\xac\x64\x3a\x58\xa5\x0c\x26\xa6\x55\x46\x5f\xc9\x6f\x3a\xde\x9e\x2a\x75\xb5\x4a\xbd\xa3\x54\xa9\x77\x7c\x75\x1a\x6a\x9d\xf5\x32\x92\x75\x1a\xd6\xac\x0b\x6e\x40\xeb\x64\x39\x23\x15\x46\x17\xea\x68\x91\xc7\xc2\x23\x76\xb5\xae\x8c\x0f\x23\xcf\x16\x7b\x55\xe3\x2f\x1a\xda\x90\xe6\x8e\xa8\xc6\x1f\x19\x8d\x15\x19\x56\x8d\x75\x6a\xf5\x16\x8c\xad\xc6\x56\xb5\x8a\x0b\x06\x58\x63\xb9\xac\x62\xde\x28\xc3\x65\x01\xe8\x81\x71\x62\x73\xc2\x1f\xae\x9c\x4c\x90\x31\x80\x8d\x25\x38\x20\x54\x69\xa0\xdf\xd1\xf0\x94\xfc\xef\x6a\x1d\xfd\x8e\xae\x1a\x67\x67\xe6\x42\x82\xb2\x21\xfa\x7d\x03\x0a\x5e\x85\x56\x01\x8d\x49\xc2\xcf\x1b\x38\xd3\x8a\x7d\xe5\x30\xc1\x03\xda\xb9\x21\x3a\x1a\xc4\x11\xdb\x60\xe4\xae\x74\xd4\x3b\xf8\x40\xf6\x88\xda\x55\xad\x56\x41\xb5\xab\x5a\x1d\xfe\xdb\x80\xff\xb6\xe0\xbf\xeb\x15\xa0\x05\xf2\xdf\x06\xfc\xb7\x05\xff\x5d\x87\xff\xd6\xfb\xe4\xbf\xcd\x8e\xdc\xcc\x7e\xfa\x89\x21\xf5\x13\xda\xdc\x3e\xa6\x01\xd9\x11\x15\x87\x10\x11\x08\x92\x30\x1b\x4f\xab\xbc\xcc\xaa\x44\x85\x94\xde\x60\xe2\x43\x95\x3e\x28\x12\x46\x15\x5f\x65\x34\x7a\x80\xe8\xf2\xa7\x61\x7c\x84\x53\x9c\x75\x91\x67\x8b\x64\x83\x70\xfc\x25\x9c\x31\xcb\xdf\x78\x84\xa2\xa3\x18\x4e\x63\xe3\x20\x45\x7d\x8c\x23\xf0\x0e\x60\xf7\x5b\x41\x34\x04\x13\xbe\x61\x38\x44\x51\x9c\x31\x33\x4c\x9b\x14\x68\x36\x17\x0e\x89\x9b\x8b\x7e\xfa\x82\xaf\x0f\x93\x30\x4e\x8e\xa8\x05\xf0\xc6\x86\x7c\xef\x24\x1d\x6e\x16\x66\xcc\xa9\xdd\x01\x5d\x7c\xe3\x7f\xdc\xe0\x70\xc3\xdd\xbc\x7c\xeb\xe0\xcf\x5f\xf0\xf5\xaf\x71\x02\x46\x8c\x5f\xf0\x75\xf5\x92\xfc\x76\x17\x3b\x0e\xff\xc0\xac\x54\x1a\x9e\xbf\x21\x0c\x08\xad\xa2\x56\xde\x32\x12\x7e\x00\x09\x0c\x90\x0d\x96\x8f\x1c\xc7\x51\x3e\xf3\x06\x9f\xa3\x4e\xa1\x16\x48\xff\xd3\xc1\x18\x93\xe3\x07\x22\x22\xb4\xa3\x0f\xe9\x51\x7c\x49\x60\x97\x78\x33\xcf\xc9\x2e\xfd\x53\x6e\x1f\x54\xb8\xee\x61\xe1\x8d\x2a\xe3\xac\xbc\x3b\x35\x97\xaa\x34\x11\x25\xe8\x50\xd1\x83\xfe\x7c\xcd\x30\x64\xcf\x0e\x29\x04\x31\xb2\x13\xe5\xe9\x20\x39\xcb\x91\x3f\x05\x95\x53\xa8\x73\x46\x47\x16\x66\x9c\xbd\x71\xb0\x1a\x3f\xc3\x42\xca\x7e\x62\x01\x87\x68\x3a\xe6\x50\xaa\x68\xff\xc0\x10\xff\x97\x40\xdc\x8b\x39\x9b\x85\xa3\x38\x43\x84\x24\xfd\x85\x32\x75\x0f\xd0\xb7\x80\x5c\xc8\xc7\xf3\x7e\x11\xc8\x20\x3e\x71\x98\x67\xca\xde\x06\x1f\xe4\x4e\xc5\x64\xb4\x33\x65\x17\x53\x4b\xac\x6b\x05\x00\x53\x06\x99\xbd\x5e\x80\xed\x7e\x78\x05\x6c\x3b\x0f\xdb\xdf\x37\x80\x89\x9f\xb2\x41\x5e\x95\xd4\xf1\x15\xd5\x18\xea\x8e\xc9\x46\x72\xc2\x81\xb4\xd8\xba\xfb\x19\x75\x08\x3f\x33\x26\x0c\x6d\x6c\xa0\xd6\xa2\x49\xfb\xee\x86\xd6\xdd\x67\xcf\x88\xfb\xd6\x8c\x45\xeb\x6c\x48\xce\xd0\xef\x44\x96\xb0\x17\xd1\x42\x6e\xae\xca\x74\xf9\x6c\x26\x8c\x2e\xde\x39\x38\x8d\xf5\xda\xcf\x6c\x48\x51\xc9\x6f\xc4\x93\x64\x39\xfc\x95\x87\xeb\xa8\x0c\x8b\xf1\xd1\x17\xa2\x8e\x8b\x78\xe1\xc8\xc8\x9b\xf9\x57\x0e\xd1\x78\xd9\xc9\xfd\x72\xa6\x96\x13\xdc\x22\xc4\x5f\xa3\x16\x38\xb2\xd0\x87\x3c\xda\xd7\xe7\xe2\x94\x43\x60\x92\xe6\x92\x1d\xc9\x01\xa6\x0b\xdd\xfa\x1a\x22\xa4\xa8\x0b\xd7\x9e\xa5\x74\x86\x7e\xf7\x2f\x4e\xcf\x9f\x2e\x7c\xbb\x57\xa0\x89\x40\xf3\x54\x5f\x8a\xee\x39\xf0\x4a\xb2\x15\x65\x7a\x70\x34\x48\xae\x67\xd4\x32\x56\x95\xf3\xf6\x2b\x28\x1e\x8d\x52\x9c\x59\x33\x43\xd7\xc8\x30\xee\x89\x7a\xb2\x70\xc5\xde\xab\x2b\xf2\x84\x28\x7f\xd6\xe5\xcf\x86\xfc\xd9\xac\x00\x8b\x51\x4f\x19\x1a\xae\x43\xbc\x2c\xae\x84\x6b\x5e\x06\x33\xd4\x88\x86\x20\x7b\xb6\xb2\xb1\x47\x88\x21\xf4\xbd\x7f\x4a\xc1\x10\xf9\xc5\x1c\x52\xed\x9b\x5e\xb6\x99\x53\xb6\xe9\x3c\x12\x15\x19\x42\x9d\x56\x2b\x3a\x81\xea\x8f\x75\xfd\xb1\xa1\x3f\x36\x2b\x42\x61\x61\x6d\xde\xab\xab\x68\x8f\x9c\x7c\xbf\x8b\x31\x72\x4f\xba\x36\x4c\xce\x59\xaf\xa0\xbb\x91\x9b\x8b\x68\xd8\x81\xa0\xb0\x64\xed\x18\xd8\xb7\x98\xc5\x0a\x85\x0b\x49\x2a\xaa\x13\x4c\x1d\x3a\xae\x9a\x32\x58\x67\xf0\xfa\x77\x8d\xd9\xd6\x5c\x1a\xa0\xb4\x6e\x4e\x87\x51\xcb\x9a\x1f\xa8\xd5\xd0\x6b\x35\xcc\x5a\x4e\x6d\x53\xda\x34\xa7\xd3\xa8\xd5\x74\xa9\xa1\xde\x19\x67\x07\xf7\xd1\x5f\xdd\x02\x5d\x27\x86\x23\xc7\x19\x47\xec\xbf\x74\x54\x37\x50\xfd\x15\xfb\xf9\x9a\xcf\x10\x7b\xe1\xd9\x77\x61\x8e\xc3\x51\x06\x94\x5e\xf1\x28\xca\x72\x27\x8e\xa3\x9e\x91\xc9\x53\xd4\x35\x35\x21\x79\xfd\xae\x28\xba\x4a\x69\xdd\x92\xbb\x7e\x57\x94\x5a\xa5\xb4\x61\x4a\x5d\xbf\x2b\xfa\xab\xb4\xa9\xbc\xb6\xb6\xe1\xe7\xcf\x5d\x1b\x00\x20\x57\xd7\x91\xab\x7b\x90\x6b\x2c\x40\xae\x99\x8b\x5c\xed\x96\xc8\x35\x74\xe4\x1a\x1e\xe4\x9a\x0b\x90\xab\xe5\x22\x57\xbf\x25\x72\x4d\x1d\xb9\xa6\x07\xb9\xda\x02\xe4\xea\xb9\xc8\x35\x16\x22\xe7\x24\xdd\x8f\x33\xb0\x21\x4a\xb3\x20\xc3\x76\x01\x60\x27\x59\xcd\xd1\x31\x60\x19\x99\xa9\x47\x83\x2f\x64\x2e\xb2\x86\xeb\x0b\x19\x88\xcc\xd4\x8e\x3b\x95\x28\xce\xf5\xb4\x80\xf7\xc1\xf2\x29\xd1\x93\x87\xb2\x76\xcc\x53\x8b\x63\xf9\x98\xc7\x16\x7b\x05\x69\xe7\x16\xb9\x84\xca\xc5\x28\x41\xac\x1f\x8e\x5d\xdd\x8f\x9d\xbd\x7e\x2c\xec\xac\x25\xa4\x63\x57\xbb\x0d\x76\x0d\x05\xbb\x86\x1f\x3b\x7b\x01\x59\xd8\x59\x6b\x48\xc7\xae\x7e\x1b\xec\x9a\x0a\x76\x4d\x3f\x76\xf6\x0a\xb2\xb0\xb3\x16\x91\x8e\x5d\x63\x31\x76\x36\xb5\x62\x1e\xd8\xda\x2d\x97\xd0\x6d\xd8\xb1\x8e\x4c\x21\xc7\x5a\x4e\xfa\xe6\xea\x58\x55\x96\xe8\xd3\xf4\xc9\x3e\xec\x28\xdc\x45\x8d\x76\x67\xb5\xd9\x60\x1a\xe8\xb2\x4b\x15\xcc\x25\x16\x21\x20\xa5\xcc\x71\x98\xa9\x86\x57\x52\x96\xf0\x09\x41\x0e\xef\x51\x30\xc0\x42\x47\x2c\x80\xfc\x27\xbe\x0a\xa6\x33\x71\x52\x96\x1f\xf8\x9c\x52\x58\x19\xbe\xca\x94\xdb\xed\xea\xe6\xf6\x71\x95\x9d\x23\x4a\x53\x6e\x91\xfe\x05\x5f\x57\xd0\x60\x74\x2e\xa4\x79\x09\x65\x36\x09\x08\x12\x57\x19\x32\xa1\x30\x09\xbf\x24\xdb\x71\x01\x62\x3a\xed\x9e\x43\x89\xfd\x89\x46\x4d\xdd\xc5\x93\x19\x4e\x4a\x9b\xdb\xf4\x5a\x9f\xea\xec\x9f\x3e\x61\x36\x2b\x6a\x93\xaf\x9e\x3e\x85\x08\xb8\x60\x40\xa2\x59\x15\x74\xdb\x8d\x0a\xb7\x4b\xe8\xb6\xc1\x76\x44\xb1\x4c\xe8\xb6\x5b\x15\x69\x92\xd0\x6d\x83\x0b\xe3\x74\xd8\x7e\xd6\xed\xd4\x6f\xce\x2a\xed\xc6\x9d\xac\x45\xbe\xa5\x99\xc8\x83\x19\x73\x7c\x43\xb3\x0c\xba\x12\x7e\x42\xcc\x80\x82\x34\x8f\x06\xf1\x74\x16\x47\x10\x72\x9d\x7c\x5b\x7d\xfa\x44\xcc\xfb\x24\xec\x57\x59\xd1\xaf\x5f\x55\x03\x00\xe1\xf4\x79\xcf\xc6\x1d\x41\x8a\xa5\x55\x47\x90\x62\xe5\xdb\xaf\x71\x32\x04\xb7\x74\x51\x40\xbc\x51\x21\xcc\x47\x60\x2f\x06\xb4\xbe\xc9\x6f\x79\x24\x4c\xe7\x67\x0d\x33\x0c\x9e\x55\x3d\xb2\x50\x95\xf7\x1f\xb3\xd1\x3a\x40\xc1\xd1\xa0\x4a\x1e\x0c\xac\x3b\x2d\xf1\x95\x3e\xe6\x19\xa2\x88\x2f\xdb\x17\xb3\x77\x5b\x3b\xf2\xb2\x89\x3e\x3b\x6f\xb0\xfa\x29\x35\xcf\x23\xcb\x8a\xdf\x62\x65\x78\x3a\x9b\x04\x99\x8b\x41\x89\x20\xd3\x7f\x46\x2c\x20\x0f\xd7\xa0\x82\x53\x81\xe0\x75\xa0\xf7\x0b\xff\xc0\x55\x1e\x60\xb2\x8b\x5a\xa8\x54\x6f\xac\xa3\x7e\x98\xa5\xe5\x3c\x80\xe1\x85\x03\xde\xde\x2f\xb7\x05\xf7\x69\xfb\x43\xef\xd3\x6f\x3b\x07\x47\xfb\x9f\xf6\x0f\xb6\xb6\xd1\x26\x84\x36\xc8\x82\x28\x43\x09\x9e\x25\x38\xc5\x51\x16\x46\xe7\x5c\x11\x43\xc8\x70\x1a\x0f\x65\xdf\x9d\x30\xb7\xb6\x0b\xc1\x64\xec\xd4\x82\xa9\x5c\x0a\x1a\x26\x47\xe2\xd1\x4d\x51\x8e\x4b\x42\x39\x9b\x14\xdd\x1e\xb8\x7d\xcf\x13\x30\x78\x10\x39\x3e\xd4\x22\x5a\x71\xa5\x77\x82\xee\xc9\x1c\xa0\x93\x31\x26\xa3\x9e\xc5\x68\xce\xdc\x04\x08\x0b\x40\xa4\x30\x80\xd6\x40\xae\xca\x87\xc1\xe8\xbc\x0b\xa4\xcb\x71\x2d\xab\x3b\xaa\x85\x2d\x6c\x17\x29\x85\xcd\xc8\x2f\x8c\x7c\x93\xe1\x42\x9f\xda\x63\x2a\xb8\x13\xd2\x23\xc8\x7f\xc1\xd7\x55\x67\x59\xee\x19\x3a\x18\x9d\xa3\xd2\x01\xb4\x12\x4c\xca\x50\x67\xe0\x1a\xbc\x82\x63\xa0\xb7\xc5\xe3\x88\xd2\x09\xbd\x21\x24\xc2\x7b\x47\x08\x65\x90\xd7\x27\x72\xae\x08\x07\xfe\xef\xba\x94\x60\x17\x40\x9a\xb4\xa0\xee\xf1\xfc\xea\xb9\x4a\xb7\xe9\x6d\x3a\xcc\x71\x52\x62\x97\x67\x30\x84\x15\xf4\x27\x0a\x2f\xba\x28\xbc\x90\xbc\xf1\x46\x33\x3d\xd0\xe6\x5b\x87\xd4\xd5\xc2\x42\x31\xc9\xc1\xd4\x00\xa8\x89\x43\x68\x7d\x76\xe3\xac\xaf\x55\x87\xec\x61\x4a\x68\x05\xe9\xc9\xb3\x10\x1f\xe9\xe9\x7e\xe9\x69\x0b\xdf\x17\x3d\x09\x48\x77\xa3\x27\x9d\x4f\xdf\x82\x9e\xf6\xa2\x30\x0b\x83\x49\xf8\x07\x4e\x51\x80\x22\x7c\x39\xb9\x66\x18\x0e\xd9\x70\x2c\xa6\x25\xbe\x6b\x5c\x8d\xe2\x64\xba\x1f\x0f\x31\xda\xa6\xbe\x6a\x10\xa6\x59\x72\xba\x38\x51\xe9\x14\xac\xab\xc1\xcd\x8f\x53\xad\xd8\x64\xdc\x64\xf8\xdd\x91\xec\xbd\x91\x55\xc9\xfe\xe0\xe2\x14\xb7\x24\xb8\x30\x0a\x35\x0b\x1b\x31\x4d\x0a\xb9\x38\x54\xd4\x9b\xb3\x19\xa1\x05\x18\x2d\x9e\x6e\x3a\x75\x5c\x33\x90\x21\xde\x10\x3f\xf9\xa6\x48\x69\xd0\x3e\x15\x67\x44\x72\xa6\x86\xf5\x71\x32\xa5\xd3\x1e\xb8\x74\x37\x94\xbe\x25\x49\x6d\x48\xf2\x7a\xe5\x2a\x49\xed\x68\xc0\x56\xc6\x79\x16\x0f\x29\xa1\x53\x0f\x00\x57\x3f\xc0\xbe\xa8\x54\x78\xe1\x80\x8d\x8e\xce\x87\x21\x96\x43\x2a\x5a\x02\xed\xd9\x1d\xc9\x87\x2d\x41\x1b\x37\x6d\x86\x93\x22\x46\x54\xd4\xa8\x68\x18\x64\x01\xea\x83\xec\xa5\x97\xf0\xc8\x63\x00\x9a\x66\xba\xe0\xde\xce\x26\xe0\x43\x9c\xc0\x5c\x0e\xe2\x68\x90\xe0\x0c\xbf\x60\xc3\x31\x89\xcf\x35\xa6\xac\xdc\x4b\x1d\x2d\x37\xd6\x10\x4f\x03\x30\xa7\xee\x2d\x8c\xa7\xe0\xa1\xc2\x52\xf0\x70\x89\x4d\xef\x6b\xca\x5c\x61\x08\x50\xa6\xec\x24\xbc\x81\xb7\xc1\x1a\x50\xc0\x17\xd8\xb9\x14\xfe\x24\x60\xd1\xa0\x59\x2c\x18\x41\x18\x9d\xdf\x03\x37\x91\x9d\xdf\xe0\xe4\xc1\xe0\x97\x56\x48\x9b\x2b\x3a\x99\x14\xa9\x77\xc9\x31\xf7\x52\x18\x2b\xd9\x35\xa2\xbc\xd2\xa1\xf3\x70\x0f\x1c\x0d\x5d\xb3\x1f\xc0\x17\xb5\xba\x8b\xa6\x68\x7b\x28\xb8\x08\xc2\x49\xd0\x9f\x60\x6a\x86\x98\xfa\xb7\xc5\x4f\xbc\x33\x85\xa9\x6a\x27\x8c\xd8\xc6\x97\xbb\x4f\x31\xb8\xfa\x3e\xf3\x21\xce\x98\x77\x34\x0d\x9a\x46\x21\xc9\x5d\x03\x85\x29\xc2\xa3\x11\x1e\x64\xe1\x05\x9e\x5c\xa3\x00\x0d\x71\x9a\x25\x73\x78\xae\xa0\x04\x07\xc3\x17\x71\x34\xc0\x85\xf6\x99\xa2\xd4\x0b\x68\x3c\x14\x0d\x53\xe0\x0f\x4d\xc9\x7c\x24\x4b\xc5\x89\x58\x54\x59\x96\xfa\x45\xc5\xc5\xe4\xcf\x8b\x16\xa7\xff\x1d\x39\x17\x73\x28\xa4\x97\x08\x47\xb9\x00\x50\xee\x6a\xd1\x8a\x3a\x2e\x4a\x96\x60\xc8\x10\x0f\x89\xa0\xca\x16\x1c\x1e\xb2\x78\x99\x9c\x53\xef\x28\x13\xe2\x5c\x7c\x76\xed\x85\xca\xe6\x7a\x63\x7d\xb5\xd9\x50\x3f\x51\x95\x88\xeb\x8b\x21\x07\x75\x51\x5d\xfb\xaa\xcb\xbf\x5d\xd4\x28\x72\x76\x4a\x9d\xaa\xec\x60\xb1\x22\x1b\x79\xd7\x26\x3f\xb5\xb0\x91\x3e\x19\x63\x45\x28\x60\x89\xb6\x02\x34\x06\xad\x31\x11\x32\x0b\x2c\x45\x2e\xc2\x6e\x46\x1c\x1f\x08\x30\xc0\x97\x35\x11\x9a\xd8\xba\x76\x74\xe8\x1b\x1c\x96\x98\xb5\xb7\xad\xf2\x34\x74\xe4\x96\x6c\xeb\x5d\x65\x5a\xbd\xae\xd7\x6f\x8a\xfc\x89\x4f\x29\x9e\xe0\x41\x46\x1b\x3e\xce\x92\x20\xc3\xe7\xd7\x25\x9f\xb9\xb6\xa2\x7d\x06\x71\x71\x03\xad\x50\x56\xba\xe2\x35\x0f\x63\xb3\x71\x18\xa4\x29\x61\x13\x6f\x82\x14\x0f\x35\x8f\x39\xf5\x2f\xdf\x38\x8c\x81\x3a\xc6\x09\x1c\xb8\xc8\xae\xe6\x87\x94\xbf\xc8\xcd\xdc\x7e\xec\x3e\x23\xc7\x46\xdd\x87\x14\x23\x27\x95\xb1\xd9\x37\x2c\x79\x76\xa3\x32\x08\x98\x7b\x1e\xc4\xc5\x0d\x45\xb1\x82\xfc\x17\x38\xe6\x18\x54\x3c\x96\x9e\x8c\xec\xbb\x56\xff\x8d\xfb\x9c\x3b\xa1\xad\xdf\x14\x55\x50\xee\x8d\x91\x89\xb9\x63\x42\x4d\xb6\xad\x72\xc9\x52\x99\x69\x78\xdd\x57\x6f\xba\x0e\x3b\xcd\x12\x1c\x4c\x6f\xa5\xca\x06\x19\x8a\x29\x9f\x55\x1b\xfc\x66\xe3\x45\x3f\xa4\x06\xdb\xfa\x89\x86\x4a\x27\x10\xc6\x5a\xd1\x4c\xd7\x51\xa9\xd9\xd0\x15\xd3\x8a\xc2\xf7\x18\xf0\x33\xd4\xbe\xe6\xcb\x1c\x8f\x90\x1d\xc7\x5e\xeb\xda\x61\xb9\x88\x38\x0b\x12\x38\x6e\xb9\x04\x44\x7b\x7b\x83\xe3\x8d\xb4\xae\xe2\x42\xe3\x0f\x3f\xac\x8c\x26\xf3\x74\xbc\x52\x6c\x9b\xa3\x50\x7c\x1b\x9d\x18\xe6\x2e\xaa\xe7\xcd\x2b\x9c\x6b\x21\xab\xe9\x4c\xbd\x2d\x55\x95\xe7\x9f\xa6\xf4\xec\xdb\xab\xb2\x1f\x7f\xde\x2c\xa6\x10\xcd\x63\x07\xea\x59\x54\xa2\xb4\xa1\xdc\x6e\xb2\x83\xb6\xe5\x1c\xcc\xde\xab\x4a\xef\x3c\x05\xbd\xaa\xa2\x9c\xf2\xe4\x5c\x52\xbe\x5e\x7a\x37\xdd\xd4\x7b\xe4\x54\x08\x9a\x99\x65\xa4\x82\x1f\xa8\xfa\x1b\xec\x87\x7c\xa6\xf8\x76\x07\x7a\xd8\xde\x9b\x9e\xa5\x8a\xe6\x1c\x25\xbc\xa0\x5e\x3b\xb7\xd1\x3c\x4b\x18\xb9\xba\x42\x51\x97\x2b\x9a\x94\x7a\xb7\xd2\x38\x8b\xe9\x94\x07\xa4\xff\x99\xd3\x29\x35\xc1\x4b\x4e\xa7\x53\xf1\x5b\x70\x3a\x45\xdd\x3b\x4c\x67\x9e\xc2\xb7\xd8\xd5\xc1\x37\x9d\xce\x3b\x4f\x57\xce\x12\x58\x30\x5f\xa6\xde\x34\x67\x92\xe8\x66\x22\xf4\xbc\x03\x97\x58\xc7\xac\xae\x2f\xd0\x06\x0a\x2f\xd4\xd9\xca\xdb\x22\xd8\x8e\x49\xe3\x4a\xf7\xc6\x41\x18\x41\xca\x13\xdf\x5d\xeb\x1b\xb0\x1b\xf8\xc4\x3b\x8f\x36\xfc\xc1\x07\x4c\x15\x9b\xb6\x83\x90\xba\x16\x31\x28\x43\x23\x1b\x33\x76\x09\x71\x27\xfa\x2a\x8f\xa3\xbc\xe9\xf1\xed\xc0\x38\x09\x29\x4d\x68\x73\x47\x7a\xf5\xa6\xe7\xd8\x7b\x6c\xf0\xb4\x89\x43\x11\xfe\x33\xe3\x6a\x0c\x4a\xa5\x41\xc6\x8c\xba\xab\x66\x1d\x0b\x86\x41\xb3\x54\x3a\x12\x5a\x11\x26\x2c\xc5\x5c\x46\x42\x3a\x27\x44\xce\x1b\x12\x66\x97\x45\x80\xb0\x9f\x97\x63\xcc\x22\xef\x53\xfc\x20\x90\x67\x5a\x00\x39\x7b\x61\xb8\x0b\x92\x3f\x98\x4a\x26\xea\x50\x6f\x00\x48\x8f\x07\x5d\x10\xae\x0d\xa6\x2c\xab\x4e\x06\x92\x2a\x40\xcb\x4c\x5e\x87\xe2\xb5\x85\x76\x3a\xc0\x22\xf3\x86\x44\x5d\x48\x1e\xc3\x59\x29\xc4\x0a\x4d\x8e\x78\xe5\x31\x67\xfd\xed\xe0\x08\xce\xcb\x8c\xe8\xec\x32\x57\x71\x02\xfd\x92\x8a\xee\x0a\xd2\xfa\x55\x91\xcd\xba\x84\x7e\x86\x87\xea\xeb\x52\x32\x47\xd7\x89\xd9\x11\x9e\x62\x90\xc2\x61\x77\xa5\x24\xc0\xae\xa2\xe0\xb4\x0f\x0e\xed\xf0\xda\xae\xce\x25\x58\x7c\xc1\xc3\xce\x53\x66\x4a\xf3\xc9\x73\xbc\x85\x29\xa0\xb7\x03\xaa\xe7\xce\xc2\x75\x3b\xc4\x05\xd6\xad\xd8\xa7\x1e\xd7\xed\xe3\xba\x45\xb7\x5f\xb7\x77\x59\x1d\x60\x21\x3c\x0e\xd3\xa5\xd7\x86\x13\x13\x46\xd1\xc0\x45\x7e\x3b\x38\xf2\x72\x00\xd5\x83\xcc\xe2\x00\x77\x65\x3b\x4e\xcc\x4e\xe4\xd0\xf4\xf1\x20\x9e\xb2\xa5\x43\xd8\x42\x18\xcf\xd3\xe2\xcc\x43\x0c\x56\x51\xf6\x20\x48\x89\x77\xa3\xe4\xc5\x7d\x29\x0f\x28\x10\x91\xb8\xb4\xe4\xf2\xf0\x1f\xc7\x71\x8a\xd1\x34\xbc\x22\xb2\x90\xa3\x7f\xe0\x09\x6a\x0b\x69\x48\x25\x44\x26\x85\xf9\xc8\x2e\xbe\x00\xe9\x94\x9c\x74\xd2\x79\x3f\xc5\xff\x3d\xc7\x51\xe6\x54\x31\x20\x5d\xb4\x53\xb2\x7a\xe8\xa3\xe8\x55\x0d\xaa\x28\x19\xb3\xb2\x58\xd5\x4f\x76\x36\x17\x56\xae\x18\x49\x72\xb5\x39\x23\x25\x91\x3f\x98\x40\x69\x3d\x1e\x9e\xa1\xdf\x37\x68\xbd\xd3\x30\x37\x74\x89\xfc\xcd\x4d\xa0\xdf\xf4\x58\x79\x2d\xa0\x89\x22\xda\x1e\x06\xc3\x21\x99\xc0\x05\x0a\x90\x19\x64\xb9\xea\x55\xe9\xbf\x6e\xf5\xc7\xe1\xbb\xde\x31\xfa\x5f\xed\xd5\x35\x34\x63\x40\x53\xa6\xcb\x73\xc1\x3c\xfc\x32\x48\xd7\x40\x4e\x9e\x05\xc3\x2a\x7f\xca\x91\x8d\x0f\x03\x7e\xfd\x3c\x4f\x79\xe8\x7c\x11\x08\x85\x99\x2b\x43\xdc\x64\x81\xc7\x52\xf6\x57\x00\x59\xbd\x7d\x26\x68\x39\x2b\xb9\xf5\x78\x2c\x04\x94\x72\x1f\x09\x80\x52\x11\xcc\x92\x0c\x0a\x84\xb3\x7c\xe0\x63\xb3\x38\x7c\x89\x71\x25\xbf\xe4\xf5\x5a\xc5\x88\x9b\xa5\x5d\x30\x07\x43\xf3\x72\xed\xd6\x0c\x44\x54\xa3\xb1\x4e\x36\x94\xf1\xf2\xc5\x0c\x99\x47\x99\xa0\x1d\xf0\x2b\xb2\xa1\x46\x8c\x60\x2d\xa0\xf4\xc5\x0b\x9a\x72\x5a\x44\x58\xf9\x97\x51\xc0\xd5\x2c\xbd\x17\xe2\xed\xda\xa1\x17\x68\xa6\x37\xf8\x4a\xe8\x05\x22\xa0\x68\x58\x48\x5f\x17\xeb\x3d\x73\x70\xb1\xde\x83\x5b\x8b\xf6\x76\x21\x66\xb9\x48\xa5\xf9\xe1\x0b\x24\xfb\xd1\xdb\x44\x21\x7a\xee\x73\xcb\x57\xa1\xd3\x30\xf7\xca\x9b\x1c\xe9\xd5\xc0\x0e\x6d\x48\xdb\x77\x7e\xf8\x57\x41\x57\x74\x94\x5c\x66\x08\x9b\xc3\xa1\x7b\x10\x60\xae\x07\x71\x34\x08\x32\x0e\xb3\xb0\x06\xe6\x63\x34\x13\x0c\x05\x96\xec\x38\x18\xd2\x40\x46\x6c\xa1\x7e\x1b\x2e\x33\x8f\x4c\x3e\xf3\x4d\x38\x02\x34\x5b\xe0\xca\x1d\xca\x99\x2c\xc1\xc5\x07\xde\xe2\x4c\x4b\x5c\xac\x2c\x62\x88\x01\x8b\x26\x41\x9a\xc1\xf3\xe2\x35\x2d\xc5\xeb\xd3\x92\xbe\x9c\x5f\xa0\x7a\x99\xba\x98\x9d\x31\x67\x30\x97\x27\x31\x15\x1c\xfc\x14\x23\xc1\x6d\x98\x6b\x50\xd9\x4c\xe9\xb6\xb9\xa4\x9e\xff\xaf\xb8\x08\x72\xb9\x28\xb8\x6f\x16\x5c\xb7\x0a\x79\xf7\x40\xf7\x67\xf4\xbf\x1f\x0f\xf1\x0d\x55\x0f\x9e\x88\xd3\x1a\xbd\x14\x81\x93\x84\xd2\x9d\xde\x9b\x9e\x0f\x0a\x9b\xab\x1b\x41\x5f\x04\x96\x29\x6c\xd8\x10\x81\xe4\x3d\x04\x0e\x7e\x04\x6c\x00\x14\xc3\x49\x83\xc0\x09\xa6\x80\x59\xc5\x38\xd5\xd1\xb6\xad\x26\x6e\x34\x6f\x84\x25\x0c\x03\xe9\x44\xeb\x1f\x7b\x8a\xf5\x61\xbe\x0d\x60\x4e\x80\x33\xdd\x3e\xd4\xe1\xc7\x09\x72\x33\x19\x01\x4d\x2d\x8a\x74\xc5\x2e\xf9\x3e\x05\xdb\x4f\x0f\xfe\x72\x62\xed\xc3\x80\x65\x4b\xca\x25\x6d\xdd\xb8\xc4\x7b\x62\x20\x50\x61\x4b\x04\x8d\x06\x9c\xca\x8d\xbb\x19\xb7\xb4\xbf\xfa\x53\x7e\xf3\xba\xf5\x4a\x19\xfd\xb4\xba\x34\x06\x42\xd5\xe2\x39\xcb\xbc\xc3\x78\x86\x82\x0c\x4d\x30\xe1\x82\x71\xc4\x57\x00\xcb\xf2\x41\x2d\x41\x61\xbf\x06\x86\x6b\xf3\x2d\x24\xce\x37\xd3\x30\xa2\x46\xa2\xec\x10\x6f\x85\x4b\xd4\x1f\x59\x25\x3a\x7d\x0a\xfe\x94\x90\xa6\x60\x7f\x4c\x8f\xbc\xe1\x05\xfa\xf1\x47\xa7\x3e\xde\x0c\xd4\x71\x78\x2b\x5d\x86\xc4\x44\x57\xa6\x78\xcf\xe7\x66\xb3\x45\xaf\xa4\xfd\x22\xa9\x14\x49\x84\xa1\x34\x7b\xe5\x20\x68\xde\xdc\xfd\x12\xf2\xea\x2a\x39\xc8\xd0\x74\x5f\x3e\x91\x0b\xe4\x75\x66\xfa\x05\x12\x38\xfc\x5e\xa8\x83\xe0\x57\xf1\xd4\x46\xd0\x77\x4a\xbe\xd5\x65\xfc\xc3\x2d\xab\x87\xc5\xdb\xd9\x1e\x48\x7e\x0b\x66\x80\xca\x47\xae\xf6\x16\x59\xfe\xdd\xd1\x52\x01\x4c\xef\x98\xec\xe1\x36\x43\x41\x83\x78\x32\xc1\x94\xfe\xe3\x11\x17\x0d\x40\xd4\xc4\x90\x4b\x2f\x4f\xf4\x50\x44\x51\xc5\xc9\x9b\x6c\xa3\x49\x70\xa9\xbc\x72\xfa\x25\xba\x5d\x3f\xa8\x03\xba\x10\x52\x8a\xd4\x96\x17\x8f\x90\xe1\x81\x71\x41\x5a\x9f\xac\x4f\xcb\x1c\xd7\x07\x28\x0d\x26\x14\x7b\xf8\x01\xc0\x40\x25\x19\xd0\xf0\xa3\x38\x09\x2f\xa8\xac\xc2\x39\x86\x13\x20\xbf\x4a\x95\x72\xbe\x62\x39\x68\xc7\x5a\x2d\x26\xd7\xdc\xa6\x67\xf9\xf2\xcd\x60\x8c\xa7\xb7\x83\xeb\x16\x38\x99\xca\x1c\x2c\xa6\x47\x0a\x3c\x27\x08\x9a\x93\xf1\x46\xe6\x6c\xa4\xa7\x18\x2a\x62\xf1\xb7\xa6\x18\x36\x88\xa3\x0b\x9c\x64\x9a\x0c\x4b\xb3\xdd\x71\x63\x4a\xb0\xf8\xa4\xd6\x7f\x7e\xb7\xd5\x43\x5a\x45\x77\x5e\x15\x2f\x0b\xda\xc3\x2c\x76\xb1\xd2\x51\x5b\x7c\xac\x13\xde\x4d\x2a\x3e\x86\x9d\x68\x10\x89\x24\x56\xb3\x38\x4d\xc3\xfe\x04\xfb\x57\xac\xa3\xa9\xe5\x9c\x9b\xe4\x40\xd9\xf6\xa0\xf4\x1b\x3f\x81\xff\x69\x41\x41\x42\x7d\x4e\x56\x70\x57\xf9\x2d\x1d\x9e\x9c\x95\xbe\xe0\xeb\xae\xee\x17\xe5\x2c\x66\x78\x4a\xb9\x0b\x91\x65\xdc\x85\xff\x2e\x28\x28\x56\x65\xd7\x76\xe7\x72\xd7\x60\x22\xbc\x69\x99\xe0\x2e\x2c\xe4\x7a\xfd\xe8\xfc\xae\x77\xbc\xe6\xae\xa0\xb0\xf0\x96\xbb\x84\x58\x38\x0a\x50\xfa\xae\x7a\x30\xc3\xd1\xf1\xf1\x7b\xab\x5a\x71\x67\x32\x75\xfa\xdd\x82\xd7\x34\xbc\xda\x8b\xf4\x72\x85\x4d\x8f\xe8\x2a\x4e\x97\x5b\xc6\xc8\xbb\x6e\x6c\x56\x62\xf8\x06\x7a\xb8\x09\x39\xd4\xf9\x81\x73\x03\x5b\xee\x95\x01\xbb\x02\xfc\x0e\x47\xa1\xb9\xc6\x73\xe0\x40\x12\xb0\x94\x66\x00\x83\xec\x71\x58\x7a\x51\x4a\x8c\xa3\x98\xbe\x31\x18\x20\xcb\xd9\x8f\xf3\xb8\x47\xd1\x25\x4d\x91\x17\xd7\x74\x6c\x6d\x3f\x47\x2b\x2b\x6e\xdf\x0a\x67\xf9\x6a\x16\xd3\x7c\x43\x3e\x57\x8e\x05\xb5\x3c\xa4\xea\x25\x4c\x5e\x51\x25\x4e\x31\x36\x3e\xab\x2a\x59\x02\x7d\xfd\x4a\xc9\x55\xd6\xa9\xf2\x49\xbc\xe6\xc7\x5e\x4b\x47\xe3\x94\x93\x28\x95\x2d\xba\xd7\xa0\xed\xc0\xd5\x86\xf8\xe9\xbe\xdd\x60\x3d\x77\x11\xa7\x0b\x34\x2b\x2e\x52\x19\xc3\xee\xa5\x0f\x62\xfe\x75\x87\x58\x75\x81\x7f\xc9\x45\xbc\x99\x17\x83\x78\x3a\x0b\x32\xd8\x5e\x8a\x2e\x43\x75\x5b\x30\x36\x31\x45\xfc\x29\xba\x27\xba\x96\xdf\x6d\x90\xbb\x2f\xc3\xc1\x98\xb6\x7d\xcc\xc9\xdb\x43\xc8\x0a\x75\xf9\x78\xa3\x46\xdf\xa2\x78\x61\xee\xbb\x40\x2d\xa3\x46\x5a\xd2\x96\xa0\xfc\xe2\x0a\xd4\x48\xc4\x5d\xa3\x02\x79\xe7\x3a\xc6\x42\x7f\xed\x43\x2c\x29\xee\x55\xb5\x5c\x2a\xd1\x6a\x2c\xed\xfd\x69\xed\xaa\xdd\xec\xd4\x3b\x83\x35\x48\x6c\xd0\x69\x77\x5a\xed\x51\x7b\x74\x56\xe6\xaa\x78\x00\xcd\x1f\x64\x3f\x3c\xe7\xc8\x02\x28\x78\xc7\xc2\x73\xf8\x12\x75\x25\x23\xa3\x61\x6d\x96\xdf\xf3\xf2\xd6\x98\xea\xaf\xb4\xac\xf0\xc8\xd7\x89\xa4\xd3\x5b\x2f\x19\x3d\x66\x03\x5f\xd0\xb7\x58\xc3\xf7\x1b\xc0\xc1\x16\x46\x8d\xa5\x37\x0b\x92\x14\x97\xb4\x85\x9a\x73\x31\x99\xa4\x9a\xe2\x47\x56\x73\x7a\x25\x90\xe2\x88\xc6\xf0\x5a\xb0\xe8\x28\x61\x58\xc8\xe4\xa9\x57\xf3\x20\xf2\xcb\x38\xe5\x30\xcc\x92\x42\x58\xe0\x4e\x70\x9a\x51\xdb\x86\x60\xe2\x58\xa0\x06\xcc\xd3\xda\x19\xda\xd8\x40\x72\xed\xa1\x1f\x7f\x34\xdb\x3d\xad\xb3\x32\x7c\x4d\xfa\x54\x50\xdb\x57\xf4\x02\xc3\x6e\x19\xe9\x1c\xc6\x5a\xfc\x46\x8b\xcc\x94\xa7\x51\x41\xad\x72\x8e\x75\x5d\x7c\xc1\x8e\xe8\x70\x15\x24\x61\xd8\xe5\x2d\xf8\x33\x68\xa0\x66\xde\x5a\x5b\xc5\xb5\x5b\x9d\x7a\xa7\x18\xa3\x70\x1e\x8d\x3c\xc7\xa0\x8a\x72\x3a\xd1\x45\xf3\xdc\xbb\x22\xbe\x08\x2f\x93\x60\x36\x03\x39\x32\xc8\x58\xf3\xaa\xca\x04\x05\x64\xa7\x4f\x15\xaf\xb4\xdc\xd5\xab\xb9\xfa\x58\xae\x6c\xd2\xe1\xc7\xf5\xa9\xa8\x03\xc9\xad\x2f\x7b\x84\xd0\xc3\x65\xfc\x3c\xa9\x9e\xeb\x08\xd4\xde\xb2\xce\x52\x87\xd0\x68\x48\xa9\x46\x1c\x30\xe4\xc5\x8e\xe3\xe0\x94\x17\x22\xca\xf4\x5e\x04\x84\xba\x96\xa8\xa6\x4c\x6c\x6e\x50\x29\x76\xed\x40\xe6\x8d\x79\xd3\xdd\xc5\x43\x55\x2a\x9f\x1c\x47\x9d\x1c\xef\x73\xd6\x34\xb5\x41\x61\xbf\xa5\xdf\xf9\xdf\x24\x86\x8b\x7b\x0b\xdb\xfc\x6b\x37\x30\xb2\x2c\xdd\x1a\x15\x7b\x59\x09\xff\x4a\x5b\x1b\xa1\xb9\x5a\x7a\x4e\x61\x0f\xd7\xa0\x0c\x52\x63\xaa\x13\xbe\x69\xe3\x15\xb1\xda\x3c\xd2\x40\x8e\xb2\xc3\xe1\x1c\xeb\xf7\x62\xbd\x5d\x08\x9d\xa5\xa2\xe7\x6c\xbb\xec\xd7\x95\xe8\x06\xb1\x74\x3e\x71\x05\x40\x73\xfa\xac\x5a\x62\x89\xf4\xcc\x10\x01\x12\x58\x67\x6f\x23\x99\xf4\xa0\x7f\x12\x26\x5c\x01\x5b\x50\x98\xbd\x11\xe1\xb8\xc2\x31\xd7\xb7\x1f\x15\xdf\x4e\xf3\x36\x6d\x6d\x7f\xb5\x0b\x72\xd5\xa2\xe3\x13\x21\x2b\xd1\xb7\x6a\x78\xe1\x28\xa2\xe8\x08\x19\xbd\xd8\x65\xa8\x56\x50\x02\x82\x0b\x51\xbb\x98\xd0\x07\xca\x92\xec\x95\xa3\xb0\xa2\x0b\x34\x2d\xac\x1d\xa5\x15\xbd\x20\x21\xbd\x91\xe3\xb8\x76\x53\xf8\xd8\xc2\xee\xa1\x53\x31\x71\x42\xf1\xa5\x5e\xcb\xa0\x07\xdb\x9e\x54\x02\x10\x3b\x94\x71\xd1\xa4\x3c\x42\x6a\xef\xbf\xe3\x3e\x65\x04\x68\x11\x91\x8e\xbf\xc1\xde\x24\xa3\x2a\x2f\x66\xd3\xdc\x7b\xde\xc1\xa6\x39\xd9\xb1\x30\x0a\x8a\x47\xfd\xad\x59\xf6\x7d\xa3\x68\xee\x4b\xf7\xb8\xa5\x78\x63\x17\x78\x22\x0c\x7c\x83\x5d\x85\x69\x1c\x14\xd5\x82\xba\x98\x0c\xc0\xea\x4e\xc1\x6e\xbf\xe1\xfc\xaa\x22\x2f\xb9\x89\xab\x39\xc6\x29\xec\x0d\x43\x9d\x3c\x6d\x13\xd3\xa2\x2e\xd2\x61\x91\x7b\x93\xc2\x64\x34\x85\x8f\x73\x9b\x10\x4d\x2c\xad\x8d\x71\xb2\x35\x73\xac\xf4\xfb\x17\xd0\x31\x05\x69\x3a\x9f\xe2\xa1\x7e\x9f\x18\x4c\x12\x1c\x0c\xaf\x95\xfd\x4e\x3b\x90\xcd\x23\x9a\xb6\xb2\x40\x44\xb3\xe5\xd8\x9e\x9b\x7f\x2d\x75\x68\x22\x8c\x0b\x4c\xd4\x93\x14\x2f\xcd\xeb\xfd\xfa\xa2\x79\xb4\x2c\xac\xbf\x50\xe2\xb6\x48\x9e\xaa\x90\x0e\x38\x15\x20\x41\xfc\x6e\x1e\xf0\xc9\xd2\x29\xa9\xab\x87\x55\x76\xa5\xf2\x66\xb1\x6b\xd4\x45\xb8\x20\x84\x0d\xb7\x09\xa1\xec\xc9\x5e\xaa\xe6\xc5\x06\xca\xd5\x8e\x32\x68\x39\x4a\x51\x4b\x33\xe1\xbc\x21\x79\xe7\x36\x91\x58\x74\x65\xf2\x65\x38\x82\xfb\x12\xfa\x6f\xfe\x65\xc9\x22\x2b\x0c\xfb\xc2\xe4\x1d\x85\x4e\x5a\x29\x76\x4f\xb2\x45\xc0\xc3\x9d\x3e\x69\x8c\xac\xe5\xbd\x5f\xb8\xc2\x60\xc6\xe2\x05\x15\x57\xc7\xf2\x1a\xcc\xf2\x82\x3d\x80\x9c\x42\x9a\x01\xc0\xf9\x5e\x21\x32\x50\x39\xa6\xb6\x15\x61\xc4\x2c\x79\x99\x1d\x00\x33\x99\x39\xc7\x11\x18\xf3\xe6\x43\x13\x51\xca\x3d\xc0\x68\xe8\xec\x7c\x58\xb6\xce\x00\x54\x58\x8a\x90\xb4\x89\x3a\x2d\x30\x39\x86\x0f\xdc\x7e\x76\x6f\x84\xe2\x69\x48\x64\x84\x0a\x0a\xe8\xa7\xcb\x70\x32\x41\x7d\x2c\x1a\x1c\xa2\x24\x88\x86\xf1\x74\x72\x7d\x4f\x87\x7b\x6a\x35\xc1\x86\xa9\x82\xf6\x7e\xa9\xc0\x94\x92\xc6\xbf\x01\x17\xa2\x93\x3c\xb4\x59\x90\x42\x8d\x55\x7c\x85\x07\xf3\x0c\x97\x56\x78\x34\xaa\x95\x0a\x4b\xdc\x51\x61\xe6\x5b\x1e\xb1\xe8\x9e\xa0\x57\xd0\x0a\x19\x0e\xf2\xff\x2b\xfe\x33\x33\x05\xa3\x72\x37\x4e\xcd\x15\x4e\xa2\x15\x46\x5d\x54\xb1\xe9\x36\xea\xa7\xd3\xcc\x66\xd9\xa3\xa8\xfe\xc1\x7b\x95\x64\x29\x91\x29\x9c\x52\xa7\xb5\x6a\xa5\x35\x77\xb8\xd5\xd1\xa5\xad\xac\x6b\x5b\x5a\xa1\xf1\x66\x69\xe2\x01\xa9\xc0\x15\x31\xee\x64\x1a\x64\xb6\x90\x6e\xca\x55\x96\xc8\x5b\x19\x0f\xc0\xdf\x19\xb0\x96\xd0\x66\x96\x8f\x01\xd8\x4d\x5b\x6a\x72\x91\x0c\x9a\x29\xc8\x79\x32\x59\x3e\xe6\xe8\x27\x5b\x9f\xad\xa5\x86\x96\x29\x9c\xdd\xce\x52\x47\x4c\x94\x5a\xf2\x30\x2e\x8f\xd4\x42\x8a\xbe\x9d\x56\xdb\xa5\x19\xd0\x54\xdc\x43\xc6\x97\x39\xcb\x33\x58\x72\x45\xc0\xf2\x88\x5f\xb7\xd7\x87\x3b\xa2\xc4\x09\x85\xb8\xfb\x9b\x4b\xc3\xf5\x80\xfa\xf1\x77\x5b\x3b\x37\x88\x6c\x9f\xdc\x82\xd2\xb5\x0b\x4b\x29\x8f\x33\xdb\xfc\x2d\x6e\x29\xad\xb8\xa3\xc3\x7e\xe7\x87\x2f\xc3\x51\x57\xd9\x9e\x15\x0a\x59\x52\x3d\xce\x5c\xaa\x96\xd9\x97\xbf\x0f\x7d\x79\xae\x74\xf0\x1d\xa8\x23\xfe\x26\x6a\x73\xc7\xe2\x2b\xa4\x49\x5e\xe1\x43\xed\x0b\x2b\xfb\xf0\x0d\x57\xd0\x9f\x0f\xac\xc1\x96\xdb\xd1\x37\x52\x38\x18\xbb\x6b\x9c\xf9\x94\xbb\x2e\xd9\x85\x80\x27\x62\x0b\x17\x57\x14\xec\xe9\xf0\x0a\x19\x83\x3d\xd3\x6d\xcf\xe7\xdd\x49\xc5\x58\xda\x37\xab\x4b\x55\xd8\x62\x35\x0c\xaa\xce\x90\x04\x5e\xc5\xbc\xa6\x2f\xf1\x5f\x67\xa8\x01\x20\xac\xf9\xd1\xdb\x57\xf4\xf8\x16\x1a\xfb\xe1\x15\x4d\x06\x02\x15\x9c\x43\xaa\x9c\xad\xa9\x61\xa6\x06\xdd\xa7\x37\x71\x9e\xf8\xee\xa0\x0f\xfe\x0b\xf8\xf1\x3d\x2b\x88\xbf\x77\xc6\xfc\x3d\xea\x89\x5d\xcc\x70\x59\x45\xf1\x9d\x18\xe3\xbd\xa3\x68\x2b\x8a\xef\x8b\x71\x17\xd4\x13\x7f\x73\xde\xfd\xcd\x95\xc5\xdf\x7e\xab\xa8\x68\xb6\x3d\x9e\x13\xda\xfd\xed\x1d\x85\xf4\xe1\xfe\xfb\x0b\xd7\xd6\xa1\x8e\x6f\xc1\xdd\x23\x4f\x41\x2e\x55\x79\x22\xd3\xa5\x9a\xd2\x92\xe5\xaf\xbc\x39\xab\xb4\x9b\xdf\x6b\x52\xca\x7b\xcf\x41\xb9\x6c\xee\x49\x2d\xe7\xa4\x85\x98\x9d\x7e\xd2\x48\x3b\xc9\x2b\x7a\x12\x4f\x82\x7e\x54\x02\x17\x3f\xf5\xe4\x93\xfb\x41\x36\xae\x20\x47\x0a\x4a\x79\xbc\x7e\x1f\x0f\x82\x09\x9a\xc5\x93\xeb\x51\x38\x41\xf1\x08\xd1\x4d\x8b\x9d\xe2\x1d\x47\x5e\x16\xdb\x7e\x43\x2f\x68\x34\xac\x31\x26\xf1\x7a\x87\xbc\xbf\x79\x65\xc7\x0e\x52\x6c\x2d\xfb\x9f\x2d\xa6\x06\x36\x82\xf3\x3e\x99\x41\x93\x88\x77\xaa\xb3\x24\xce\x62\xf2\x09\x6d\x90\xd3\x87\x59\x80\xd5\x43\x1b\x28\xc2\x97\x04\x81\x7c\x08\xd1\x7c\x32\xf1\x2c\x14\x81\x81\x5c\x26\x4a\xbc\x23\x57\x24\x4f\x3e\x27\xf9\x4a\x6e\xaf\x62\xfb\x7d\xd8\x4f\x82\xe4\x7a\x91\x8e\x5c\xc9\x0f\xea\x05\x05\xd9\x42\x99\xd6\x93\x08\x17\xbc\xcb\xc1\x04\x85\xd1\x18\x27\xa1\x16\xc0\x55\x8b\xe8\x60\xe6\x19\xb5\x23\x8c\xda\xd3\x59\x20\xec\x1f\x8f\x31\x0c\xee\x71\xc2\xcf\x60\x1c\x64\x1c\x21\x16\xca\x83\x8a\x41\xd6\xa9\x12\xa1\xbc\x38\x80\x5c\xee\x8a\x2f\x70\x92\x84\x43\x9c\xa2\x43\xaa\x10\x09\x71\x4a\x19\xf8\xec\x1a\x85\x11\xcb\x66\x2c\x11\x28\xd0\x82\x99\xab\xe1\x64\x59\x00\x96\xcc\xe5\x29\xb7\x4c\xd4\x40\x32\x51\xfb\xd7\x27\x94\x84\x35\xe9\x26\xc7\x24\x51\xf5\x17\x0b\xf1\x64\xd8\x45\x2b\x90\x29\x6b\xc5\x34\x1c\x71\xb7\x49\xfe\xa6\x38\x1b\xc7\xc3\x5c\x1f\x79\xa5\xb4\x19\x23\xdf\xe5\x78\x86\x90\x1d\xce\x90\xa2\xaf\x19\x64\xf3\x79\xf5\x06\x31\x9c\x05\x97\x91\xfd\x45\x61\x24\x44\x58\x90\x69\xf5\x7c\xe6\xc4\x9b\xf3\xf3\x29\x8e\x1c\xa6\xc3\x64\x47\xc9\xc7\x02\x49\xe6\xc3\xce\x5d\xb2\xbc\x33\xfd\x83\x13\x01\x66\x26\xc5\x5d\xbf\x42\xe1\x58\x9a\xb8\x71\xfa\x81\x37\x39\x0e\xd2\x83\xcb\x88\x91\xfd\x75\x69\x85\xd4\x5c\x29\x0b\x9f\x27\xf2\x08\x9b\x20\x2f\x4f\x5e\x2c\xec\x07\xad\x95\x3b\xdd\x8e\x5a\xff\x4f\x3a\x9f\x11\x51\x2b\x0a\xb3\x6a\x40\x84\x53\xb6\xf5\x05\xc9\xf9\x9c\x8c\xae\x73\x3c\x90\x23\x83\x42\xce\x38\x49\x8f\xdb\x64\x25\x45\x92\xa3\x87\x54\x29\xcc\x27\x9d\xae\x52\x1b\x82\xda\x41\x6d\x3f\xf0\x6c\x3b\x88\x2b\xc6\x47\x38\xc1\xd1\x80\x34\x00\xe3\x3c\x33\xd7\xab\x35\x0c\x4c\x2e\x76\x01\xf4\xee\x33\xc8\x95\x1a\xc3\xc5\x54\xb7\x61\xa5\xa4\x2a\xd3\xa4\x2a\xef\x79\x44\xc7\x01\x26\x90\xae\x5a\x3b\x04\xea\x26\x9f\x0f\x99\xc1\xa6\x54\x16\xd7\x70\x44\x94\x86\x90\x72\x00\xa4\x54\xfe\x3b\xf3\x4a\x1e\xb1\x1c\x6d\x30\xb6\xc9\xef\x2c\x16\xf2\x22\x5a\x2e\x9f\xe3\xd9\x8d\xc0\x92\x93\x71\xb2\xed\x95\xcb\x23\xa8\x2b\x6b\x84\xbf\xd3\xd7\x89\x97\x6a\x78\xf1\xdb\x90\x4d\x9e\xbb\xba\x67\xae\xd0\x01\x63\x66\x2c\x49\x00\x90\x14\x98\xd0\x0f\x87\x28\x8d\xa7\x98\xa6\x9e\x42\x97\x63\x1c\xa1\xeb\x78\x9e\x08\x33\xfb\x80\x88\xb3\x14\xf8\x3d\xc7\xce\xbd\xeb\x2e\x68\x3a\x3a\xe7\xed\x65\x88\x32\x80\x6a\xd5\x1e\x19\x31\xf4\xb7\xdc\xee\x16\xa2\x51\x68\x4e\x7b\xf1\x8c\x08\x3b\x33\x29\xf7\x30\x79\xe7\x0e\xe2\x94\x02\x0c\x34\x4c\x9a\x4c\x35\x05\x4d\xe4\x3d\x4f\x29\x5b\x9d\x74\xff\x2c\x2a\xbf\xdc\x72\xdc\xa1\x11\xed\x12\x5b\xf4\xcf\xb9\xc6\x45\xc4\x43\x7e\xd9\xf6\x21\x98\x82\xd1\xc4\x82\x7a\x88\x6d\xd5\xb2\x98\xb9\x59\xab\x00\xcb\xb9\x5b\x2c\x99\xce\x53\xb5\xf8\x19\xda\x50\xda\xd7\x3f\x2d\x91\xba\xc8\xb3\xc9\x6e\xa3\xcb\x38\x5a\xc9\xa8\xfc\xcc\xdd\x1d\x95\xe0\x85\x93\x38\x9e\xa1\xa0\x1f\x5f\x38\xb6\xc1\xfc\x2e\xaf\x70\x68\x2b\xfe\x0e\x03\x17\x15\xad\xaa\xfd\x14\x6f\x0b\xe4\xd5\x2a\xb4\x78\xc4\xe1\x04\x7a\x0a\xf6\x2f\xcb\xac\x1b\xd7\xc6\x37\x98\xc4\x11\x7e\x00\x8e\x07\x70\xd1\x86\xdc\x43\xe0\x45\x81\x9d\x8c\x14\x5b\xb8\x91\xa9\xb9\x48\x74\xe1\x88\xf3\x53\xa7\x3d\x99\xfb\x8c\xec\xbc\xdd\x8f\x50\x00\x9e\xb7\x46\x2c\xc2\xdc\xc8\x42\x56\x9c\xf7\x7c\x10\xae\xf0\x34\xc2\xf8\x41\x0f\x87\x98\x86\xe7\x51\x38\x0a\x07\x41\x94\xb1\x80\x92\x21\xed\x3d\x80\xa4\xed\xb8\x8e\xc9\xbf\x2a\x1e\xc4\xf4\xac\xac\xbe\xb9\x87\xb0\x31\x76\xf3\x26\x59\x78\xc2\xe0\xab\xa6\x57\x0b\xc6\x1a\x39\xcd\xc2\xc4\x48\x19\x37\x18\x0b\x07\x0d\xdf\x5b\xaa\x17\xd5\x3f\x5b\xdb\xd8\x2d\x5b\x18\x8f\xf6\xbf\x38\x80\xd3\xda\x55\xad\x56\xab\xd7\x1a\xb5\x66\x05\xd5\xae\x6a\xad\x5a\xbb\xd6\xa9\xad\x9d\x3d\x18\xe0\x0a\xea\x14\x0e\xbd\xc2\xc2\xd7\xf1\x19\xb1\x56\xec\x25\x73\x08\x86\xe5\xca\x1f\xe8\xbf\x5f\xbf\x42\xcc\x5e\x43\xd4\x18\xa1\x92\x98\xde\x1f\x36\x1c\x8a\x42\xf5\x0f\xa0\x2a\x46\x43\xfc\x67\x61\x63\x52\x13\x00\x25\x8f\x09\x8e\xce\xb3\x31\x35\x3d\xf2\x72\x91\xe2\x31\x63\xe4\x42\x59\x2e\x52\xcc\x76\x34\x88\x87\x84\xde\x31\xfd\x61\x92\x3b\xbc\xce\x8f\xfd\x29\x08\x00\x47\x83\xea\x2e\xbe\xf2\xb7\xb9\x28\x80\x4c\xa1\xd5\xbe\x74\x70\x17\x49\xac\x05\x22\xbb\x38\xe2\x1a\x2c\x0a\xeb\xe2\xa8\xa2\x0d\xc9\xc7\x6c\xb4\xbe\x54\x34\x17\x36\x15\xde\x58\x2e\x7c\xaa\xbe\x7e\x45\xbb\xf8\x2a\x37\x7c\xcb\x02\x02\x1a\x04\x19\x8e\xd8\x9e\xaf\x53\x90\x87\xf9\xfb\x09\x49\xb9\x87\x95\x03\x7e\xc2\xb8\xa1\x42\x99\x90\xe6\x77\xd9\x7b\xdd\xa2\xb8\x14\xa1\x0d\x81\x5d\x9d\xc7\xcf\x10\x6f\x1a\xfe\x94\x66\x50\xd2\x64\x4a\x34\xb0\xf3\x72\xe1\x48\xc8\xc0\xfe\x6a\x31\x2c\x87\xaf\x62\x36\x0e\x44\xa8\x03\x49\x62\xfe\xd2\x61\x7a\x2c\x79\x8c\xc6\x73\x3c\xc0\x8f\x75\x96\x44\xe1\xcb\x3a\x56\xa7\x7a\x93\x60\x3a\x43\xf8\x0a\x22\x49\xf6\x43\xb3\x73\xf4\x5e\x95\x94\xb1\x6f\x1b\xe8\x7d\xea\xc0\x15\x24\x45\x43\xfc\x5f\x9e\x40\xe9\x50\x9f\x88\xa4\x11\x86\xad\x16\x05\x19\x0a\x50\x16\x4e\x1d\x12\xb7\x2b\x24\xbb\xda\x5d\x7f\x52\x08\x75\x70\x48\x51\xb4\x41\xd0\x63\xb3\x70\x1a\xf2\xa8\xd8\xe4\x9f\x52\xa3\x85\x5e\xa0\x52\x48\x31\xfe\x09\xad\x97\xcb\x22\x5a\xb6\x57\x8a\xa7\x70\xf4\x1e\x3f\x47\xa1\x08\xb7\xfd\x75\x43\x36\xfd\xfa\x35\x6f\xc3\x51\x5e\x34\x5a\x40\xf0\xf7\x6e\x4b\xea\x98\xd2\xc5\x75\xa7\x31\xf5\x47\xb9\x2f\xda\xfd\x0d\x64\x0f\x76\x91\x8c\xc1\x36\x15\x8a\xcd\xf6\xf9\x86\x8e\xa6\x2b\xc7\x4a\x10\x46\x41\xdf\x3c\x79\x28\x07\x80\xa2\xec\x94\xc6\xe0\x20\x42\xa0\x26\x18\x86\xd9\x5d\x45\x41\xb9\x38\xc5\xea\xf2\x30\x29\xf2\xb9\x68\xe8\x5e\x07\x6b\xb2\xe5\x28\x57\x5c\x24\x2f\x93\x71\x33\x0c\x87\xa8\x76\x2a\x60\xf0\x38\xf3\x1b\xb0\x74\xe8\x1f\x90\x7e\xb3\x41\x48\x3f\xd5\xf8\x82\x83\xe0\x35\x51\x6a\x03\xed\x07\xd9\xb8\x3a\xc0\xe1\x44\xd6\x5c\x45\x4b\x44\x24\x72\x9f\x7f\x0b\xed\x3c\x1e\x73\x24\xeb\xf8\x7b\x5b\xbb\x4f\x76\xdc\x55\x69\xc1\x3a\xef\xea\xb4\xb0\xe8\x9c\xab\x82\x85\x93\x1a\xc5\x55\x8d\x7e\x6e\x9f\x9c\xab\x36\x8d\x30\xf3\xfb\x9a\xd7\xa4\x8e\xd4\x5b\x7e\x0a\x14\xb1\x61\x14\x4e\x26\x3c\xec\x2c\x73\x93\x80\xf3\xd6\x62\xa1\x84\x1f\xe6\x22\xd7\xa1\x57\x05\xe5\x75\xf1\x29\x34\xcb\x0c\x52\x21\x42\xb9\x2f\xe3\xb3\x02\x47\x30\xe6\x0a\x52\xf7\x9f\xb4\x68\x09\x95\x4c\x22\xf7\x11\x4b\x65\x0f\xf6\x81\x8a\x7c\x4d\xf4\x1b\xf2\xe9\xa7\x4b\x7f\x94\xf9\x4f\x97\x68\x83\xfc\xd7\x93\x40\x6d\xfa\xe9\x0f\xb2\xcd\x5c\x35\x83\x21\xee\xac\xf7\xcd\xf0\xeb\xa2\x58\x90\x7e\x41\x2a\xe7\xc8\xb9\x27\x28\x70\x77\x47\x5b\x2d\xd5\xae\x5e\xd6\x3a\x2f\xd1\x4f\xa4\x0b\x7f\xc0\x9e\xbe\xb3\xb3\xb3\x53\x46\xcf\xe9\x8b\x9f\x7f\x46\xb5\xab\x7a\x0d\xb6\x7b\x82\x80\x67\xbb\xa7\x5d\x2c\xd5\xae\x5a\x9d\x76\x8d\x02\xbb\x34\x81\x5d\x16\x05\x06\xc3\x8b\xd3\x39\x78\xfa\x94\x00\x8d\xd7\xaf\x69\x4d\xf4\x1c\xc1\x48\xe7\xd6\x67\x75\x57\x37\xa0\x0e\xfb\xcb\x2f\xfb\x7c\x03\xd5\xaa\x6d\x6f\x19\x18\x53\x56\xf4\x27\x6a\x6f\xc3\xa9\xad\x8c\x7e\x46\xd5\x36\xfa\x0f\x54\x47\x5d\xf4\xa2\x5e\x44\x44\xb1\x38\x87\x2e\x6e\x54\x50\x32\x08\x06\x63\xcc\xb2\xeb\x2c\x16\x38\x48\xcd\x4f\x84\x1e\x93\x52\x89\x56\x25\x47\x25\x0d\x49\xb2\x9b\x28\x83\xe1\xbe\x62\xa2\x55\x37\xd0\xa7\xa4\x44\xcb\x03\x41\xae\xf5\xd7\x1c\x7d\xba\x94\x39\x7c\x4a\xa2\xbc\x84\x8f\xbe\xa2\x5a\xc1\xb0\xe6\x11\xbe\x54\x9c\x9d\xe0\xd6\x91\x29\x40\x22\x9e\xbe\xe7\x89\x31\x92\x6e\xe7\x53\x76\xb4\x5f\x64\x48\x83\xa3\x01\x18\xd2\xd0\x7f\xdd\x86\x34\xbb\xf8\xca\xd6\x04\xb8\xc0\x91\x82\x1b\x14\x68\x95\xfe\x2e\x16\x7f\xd3\x54\x5f\x8c\xf1\x55\x61\x15\x46\x81\x93\xe7\x92\x51\x35\x0b\xb5\x7e\x5f\x8c\x7c\x8c\xaf\xec\x10\x9a\x6c\xfc\x94\xa3\xfd\xe2\x44\x42\xce\xc0\x99\xb7\x3d\xa6\x5e\x16\x3e\x79\xa6\xcb\x1e\x23\xe9\xac\xdb\x80\xc6\xf8\xaa\x37\x0e\x92\xc2\x79\xb6\xd2\x85\x07\x3a\xc8\x91\x16\xd2\x83\xdc\xe5\x1d\x0f\x71\x1c\x3b\xb6\xc6\x01\x2c\x01\xd2\x2a\x4b\xb5\x4f\xbd\x53\x76\xf1\x3b\x57\x55\xd2\x4e\x6d\x94\x5f\xd7\xc3\x20\x04\xb8\xcf\x71\x18\x95\x56\x56\x6e\x11\x71\x53\xa1\x70\xba\xde\x96\xd1\xf4\xf0\x95\x42\x09\xb7\xf8\x82\xf1\x08\x4f\x7f\xbd\xd4\xc4\x17\x1b\xb5\xd9\x16\xeb\xb1\x78\xa4\x4c\x5a\x65\xb9\x44\x29\xb4\xce\x7b\x7e\x74\xa1\x8f\xec\x28\xb3\xcc\xaa\xb9\x5c\x26\x35\x9d\xda\x28\xdb\x42\x1b\x39\xf9\x31\xe9\x6a\x69\x82\x66\x02\x3a\xbd\x17\x65\xac\xb3\xd5\x74\xde\x4f\xb3\xa4\x14\x56\x50\xa3\x5c\x81\x24\x7c\x52\x65\x41\x56\xd4\x7a\xd9\xe5\x80\xbb\xf4\x9e\xa7\x0d\xd3\x2a\x6a\x14\x75\x9f\x7d\x1f\x64\x61\x54\x2f\xb6\x69\xb1\xb2\x7c\xdf\x12\x8f\xb7\xdb\xba\x58\xf5\xbf\x6e\xf7\x2a\x8a\xc0\x7d\xad\xa9\x09\xb4\xe7\xde\xc3\x28\x2e\xff\xa3\xb6\x31\x3a\x1c\xdf\xf1\x4e\xa6\x20\x48\x77\x24\x3a\x75\xd5\x51\x12\x4f\xc9\xdb\x5e\x3c\xc4\xb0\x49\x15\xdd\x90\x54\x80\x77\xd8\x93\x34\xba\xbd\xfd\xb6\x24\xc8\x71\xa9\xc5\xf0\x5d\x6f\x4e\x6c\x15\xd1\xfd\x49\x5d\x6e\xc5\xb7\x28\x51\x6b\xb9\x5d\x4a\x54\x13\x1b\x95\x78\xf3\xd0\x7b\x95\xd1\xf4\xa2\x5c\xce\xa1\xa2\x45\x97\xbd\xad\x0e\x18\x41\x6f\x66\xa5\x90\xaf\x09\x73\xab\x72\xeb\x16\x97\xde\xaa\x0c\x84\x8b\xee\x54\x1f\x4f\x76\x5e\xac\x17\xdb\xa8\x3e\x66\xa3\x75\xb1\x4d\xb1\x87\xdb\x6d\x52\xb4\xd1\xbf\x6e\x8f\x2a\xd8\xfe\x7d\xad\xac\x79\x36\x5a\x77\x6f\x50\x64\x14\x1f\x72\x7b\xca\x92\xeb\x1c\x03\xa3\x21\x26\x47\xf4\x8f\x47\x7b\x3d\xee\xe9\x54\xc2\xe9\x20\x98\xe1\x52\xce\xc6\x69\xb3\x65\x34\x08\xb2\xc1\x18\x95\xec\xf4\xd1\x80\xc2\x38\x89\x2f\x81\x6e\x21\xe3\x4a\x69\x65\x3f\x98\x8c\xe2\x64\x8a\x87\x6c\x1a\x86\x41\x16\xd8\x29\xe8\x96\x67\xe0\xea\xa4\xde\x9e\x7f\xb3\xb9\x5a\x86\x4c\xbe\x6b\xe6\x0d\x14\x46\x59\xb7\x24\xc3\xe2\x8c\x9b\xd5\xf1\x19\x03\x68\x5b\xc3\x3c\x62\xd4\x43\x2d\x04\x34\xba\xe2\x70\xca\x85\x03\xd0\x88\x14\xbc\x90\x0b\x13\x0f\x59\x36\x33\xc5\x0b\xdd\x9b\x89\x57\xb1\x93\xbd\x56\x52\xa2\x4d\xe7\x69\x86\xfa\x18\x85\x64\x44\xa7\x38\xca\x68\x9e\xb5\x00\xae\xd7\x13\x9c\x09\x8f\x85\x42\xb9\x7d\x8d\x3c\x9d\xba\x72\x9f\xe6\x38\xa4\xae\x55\x32\x41\xfc\x17\x3c\xcb\xd0\x3c\x9a\xf1\xa4\x81\x7a\x76\x50\xc5\xa6\xa5\xe6\xe0\xbe\x6f\xd8\x38\x40\xa6\xc1\x4d\x31\x0a\xc2\x4b\xcc\xf7\xb9\xa0\x19\x1c\x64\x77\x65\xd6\x3c\xc6\x48\xaf\xb0\x24\xda\x2c\x89\x69\x16\xa3\x30\x4b\xb9\x57\x0c\x22\x14\x7c\xd7\x3b\xa6\xbe\x13\x79\x9a\x10\xd7\x7f\xc9\x54\x28\xeb\x2e\x33\xef\x43\x60\xa5\xec\xb2\x19\x80\x0c\x9c\xcc\x53\xd1\xd8\x59\x4d\xa6\x44\xcb\x47\x5b\x41\x16\x70\x61\xbd\x56\x54\xd2\xdc\x1c\x0e\x53\x68\x83\xe7\x05\xf7\x8c\x34\xa3\x85\xe2\x9b\xa2\x08\xb2\x60\x65\x1e\x67\xc6\x2e\x88\xae\x79\xe6\x04\x40\xf9\x25\xf5\x29\x09\x14\x0b\x4a\x6a\x4f\x0c\x1c\xef\x61\x26\xf3\x13\x45\xa7\xb4\x62\xf3\xfb\x42\xf5\x16\xef\x8d\xac\x64\x91\x64\xe6\xb6\x7b\xbd\x4c\x47\xa7\x06\x14\x55\x06\x88\x05\x13\xd5\x41\xa9\x3e\xce\x40\x46\x0b\xe2\x44\x32\x5a\x53\x98\x32\x60\xb8\x38\x52\xda\x26\x74\xcd\x47\xbe\xdc\x94\xc8\x05\xcc\x22\xda\xe7\x1b\x7a\x92\xf4\xa2\x14\xcc\x73\x9d\xa6\x28\xb8\x08\xc2\x09\x44\xec\xa2\x7c\x01\x98\x9d\x9f\x6a\x4e\x14\x67\x95\x30\xba\x88\xbf\xe0\xd4\x4c\x32\x5c\x62\xc9\x81\x2b\xe8\x72\x1c\x0e\xc6\x4e\x56\xdd\xbf\xce\x61\xd5\x76\xab\x7c\xa1\xf4\xe3\x78\x82\x83\xe8\x06\x0d\xe3\x9d\xc9\x3c\x1d\xa3\x5f\xc7\x38\xa3\xf1\x4c\x78\x2e\x5a\x70\xd7\x9a\x05\x09\x30\x0a\xf6\x4a\x72\x6d\xc1\xae\x6f\x11\x0e\x44\x70\x7a\x18\xf1\xbb\x6f\xf3\x02\xe0\x16\x25\x24\xdf\x9a\xe1\xa9\x72\x7d\x71\x39\x96\x04\xe3\xce\x14\xac\xc7\x5a\xa5\x45\xb5\xc5\x47\x07\x7c\x49\x9d\x09\x5b\x22\x92\xb8\x1d\xda\x12\xf2\x9a\x1b\xa7\xc1\xc8\xfa\xd4\x2a\xe4\xa3\x62\x68\xe6\xa3\x7b\x5e\x5c\xca\x0a\x1b\x46\x4a\xe6\xbc\xc2\x1c\xba\xac\xed\x8e\xe8\xd7\x8b\xe7\x51\xc6\xe9\xcb\xc1\x4c\x08\xd0\x88\x26\x12\x3e\x82\xb8\xc5\x1b\x3a\xfe\xab\x46\x93\xaf\x6c\x5e\xe4\x1b\x72\x86\xc1\x51\x3c\x8f\x86\x68\x3e\xa3\x0e\x85\x83\xc9\x7c\x88\x0d\xba\xb7\xab\x19\x18\x49\x23\x17\xf5\x43\xf1\xd8\xb6\x02\x8b\x61\x7c\x19\xa9\x78\xc4\xd1\xe4\x1a\x8d\xe6\x62\x51\x3a\x22\xe9\xaf\xae\xa2\x09\x4e\xa9\x53\xa5\x5b\xd6\x02\xbe\x91\xe0\x69\x10\x46\xba\x70\x55\xac\x5f\xd3\xe0\xaa\xa4\xf5\x0b\x2e\x4e\xd1\x0b\x57\x66\xf6\xca\xe2\x2b\x55\x31\xe7\x54\xf3\xe0\x9b\x72\xa0\x64\x8e\x87\xd6\xfa\x4f\x48\x21\x40\x1f\x3d\x01\x6d\x78\xc9\x89\x7c\xd5\xfb\x18\x46\x25\xb5\xc9\x9f\x50\xab\xa2\xd1\x99\xcb\x7c\x92\x67\xf0\x76\x11\x09\xa1\x3b\x05\x60\xbe\xdb\x16\xe5\xf3\x54\xcd\xc2\x7e\xbf\x56\x47\x40\xbc\x7d\xae\xac\x27\xaf\xd1\x04\xc1\x0c\x27\xe4\x34\x29\x36\x86\x17\xf2\x80\x00\xce\x90\xee\x8a\x8c\xbb\xe8\x7b\x90\xe0\x2a\xae\x5c\xf5\xbe\x39\x46\x5a\x0a\x2c\xc9\xf0\x61\xca\xed\xa2\x1a\xf7\x55\x59\x98\x99\x0c\x4b\x1d\x51\x07\x1a\x1a\x27\x43\x2f\x36\xd4\x99\x5e\x4c\x95\x3c\xb6\x68\x1e\xb6\x7e\x85\x93\x8e\x7f\x45\x6d\xfa\xae\xc6\x6e\x85\xb3\x50\xe6\x3a\x79\xdd\xd1\xca\xcd\xb3\x1b\xfe\x45\x26\x6f\x9f\xac\x0d\x51\x62\xe2\x9c\xb1\x5c\x8b\x37\x9d\x87\x89\x93\xa6\x27\x13\x3d\x3f\x83\x8f\x83\x14\x32\xe4\x7a\x4f\xdc\x0b\x53\x91\x4b\x76\xad\xfa\x40\xd1\x49\x67\xd0\x69\xd8\x35\x9c\xa2\x38\x52\x8e\xc2\xf5\x0e\x2a\xb5\xeb\x0d\xb0\x64\x2d\x3b\x8e\xc5\xbb\xb4\x32\x3f\x06\x8b\x47\xf7\x79\xf8\x5e\xa2\xbe\xe6\x65\x20\xcb\x0d\x98\x9a\xe7\x6a\x46\x07\x61\x89\x9c\xe4\xb7\x8d\x6e\x47\x1a\x42\x34\x44\xf2\xa2\x20\x77\x85\x6d\x48\xc4\x1c\x68\xa1\xdb\x8e\x77\x37\x1b\xed\x8e\xdb\x49\x2c\x2f\xd5\xf5\xad\x23\xac\xf1\xd8\x6a\xc5\xc3\xac\x1d\x63\x11\xde\xc3\xaf\x21\xb0\xd5\x10\x0b\x2c\xb1\xa5\x26\x85\x2f\x9c\xfb\x57\x99\x30\x7a\xb9\x0f\x15\x09\x20\xac\xaa\x78\xf4\x12\x9e\x95\x04\xa0\x35\xe6\x65\x4b\x0d\xe6\xde\xcc\x86\xc3\xb1\x31\xf3\x0d\xf9\x68\xb9\xb1\xfe\x38\x1b\x02\xcb\x50\x07\x9b\xa6\xe5\x2f\x9e\xb1\xcf\x1b\x41\x98\x02\x37\xe3\x08\x17\x76\x21\xa2\xac\x88\xf9\x0f\x2d\x5c\xde\x4b\xcc\xf9\x1c\xf0\x2a\xad\x30\xa4\x5c\xba\x14\xbd\xe4\x62\xd5\x09\x2d\xa8\x12\x8a\x36\x06\x9e\xf5\xe8\xd1\x48\x30\x85\x8d\x0e\xc1\x41\x1e\x6c\x7c\x89\x90\x4e\xf0\x75\x81\x52\xce\xb1\xb6\xf8\x7b\x6f\xbe\x13\x3b\x2c\xc9\x4d\x2a\x70\xf1\x32\x48\xf4\x21\x06\x94\x83\x8c\xe6\x8b\x67\x35\x65\xcc\x50\x14\xa6\x08\x8f\x46\x78\x90\x85\x17\x78\x72\x8d\x02\x34\xc4\x69\x96\xcc\xe1\xb9\x02\x72\xfa\x8b\x38\x1a\xe0\x42\x51\x46\x0b\x52\xa8\x96\xe8\x01\x50\x92\x01\xb9\xa1\xc4\xf2\x9a\x0b\x32\x08\xf7\xb4\x33\xa0\x0d\x4e\x8e\x22\x99\x90\x47\x2d\xe1\x29\x9d\x47\xe8\x39\xd5\x16\x53\x3d\x2f\xba\x14\xdd\xef\x38\xc6\xd7\x3e\x10\xe5\x83\x41\x8b\xd6\xca\x22\x01\x7e\x09\xce\xaa\x8c\x10\x67\xb2\x3b\xca\x3c\x38\x17\x0f\x29\xef\x5b\x3c\x4a\xf2\xbb\x76\xbd\xb1\xda\x6c\x14\x13\xf3\x53\xa6\xf1\xd1\xe2\xdf\x07\x6c\xd2\x56\x44\xe0\xa4\x30\xca\x70\x32\x52\xac\x85\x91\x77\x55\x70\xfe\xca\xba\xce\xa9\x96\x6e\xb7\x2c\x3e\x62\x80\xc6\x78\x32\xc3\x09\x11\x7f\x0a\x2c\x82\x1d\x86\x1b\xf3\x0d\x36\x51\xfe\x06\xf7\x78\x54\x66\x32\x9d\x2a\x68\x57\xab\x9f\x68\xaf\x76\xa1\x4b\x25\x97\xb0\xe5\xd7\xcf\xa9\x55\x35\xe3\x41\x00\xed\xbb\xdf\xb3\xd6\x85\x3b\x00\x2e\xd2\xcf\x8b\x6c\x25\xc2\x61\x51\xcf\x22\x26\x33\x5c\xea\x14\xbe\xfc\xb1\xd1\x49\x4f\x84\x25\xef\xee\x6f\xf6\xee\x9f\x9e\x88\x08\xcd\x83\x52\x90\x16\x18\x5d\xfd\x2d\x68\x6a\x77\x1a\x0c\x0a\xd1\xd5\x34\x18\xdc\x85\xb6\x44\xf5\x3b\xd1\xd7\x17\xec\x56\x21\x29\xf4\xd5\xfb\x04\x68\x91\x79\xa0\x44\x46\x1b\xa1\x75\x97\x23\xb6\xdc\xe3\xaf\xd0\x24\x2d\xf0\x61\x20\xd8\x80\x13\x03\xfb\x21\xbd\x18\x78\xa6\x16\x08\xe9\xbb\x1f\x64\x63\x1a\xd6\xf7\x09\x7f\xcf\x86\xf9\x95\x8c\xf4\x7b\x73\x56\x69\xb7\xbe\xd7\xf0\xbe\x0c\x99\x12\x0f\x47\x5c\xbe\xf7\x78\xbf\x1c\xf2\xb2\x71\x7f\x05\x86\x6a\xfc\x5f\x5f\xd0\x5f\xf1\x1d\x82\xff\xba\x02\xe8\xda\x57\x14\x3c\x6a\xac\x9c\x32\x85\x00\x94\x68\xb0\xca\xfb\x9c\xf0\x34\x5a\x6d\xc5\x05\xc6\x17\x46\xb6\xd3\x2a\x66\xa2\xc5\xca\x72\x23\x2d\xf1\x78\x3b\x33\x2d\x56\xfd\xaf\xb3\xd3\x2a\x8a\xc0\x7d\x71\xca\x3e\xb4\xe7\x36\xd5\xa2\xb8\xfc\x03\x6c\x89\xad\xf2\xd3\x60\x26\x84\xc3\x69\x30\x5b\x3e\xf6\x82\xc3\x45\xdc\x06\xe1\xb3\xca\xa4\x63\x7e\x5b\x83\x65\xf4\x7c\x03\x35\xfd\x36\xcb\xd7\x19\xae\x3b\x8c\x96\xe9\x9f\xcf\x74\x99\xfe\x79\x0d\x98\x39\xe0\x86\x04\x5c\x0a\xd1\x73\x54\x2f\x3b\x6c\xa2\xf9\x97\x22\x96\xd1\x1c\x70\xd3\x00\xdc\xf0\x02\x6e\x38\x01\xbb\x21\x67\x49\x38\x9b\xc0\xd5\x4b\x89\x0e\xcb\xeb\xd7\xe0\x37\xf1\x95\x3e\x37\xc8\xf3\x3a\x79\x04\x14\x5c\x50\xc4\x54\x7c\xa6\x53\x51\xfa\x8c\x5e\x93\xd6\x7f\xfc\x11\x01\x36\x9f\xd1\x4f\xa8\x56\x5d\x6b\x2b\x33\x54\x7e\x85\x3e\xe7\x84\xbb\x50\xe6\x9e\xda\x82\x4f\x83\x19\xd8\xcc\x6e\x66\xa5\x12\x47\x18\x3a\xdd\x41\x3f\xa1\x52\x13\xbd\x40\x9f\xcb\xac\xa7\xcd\x91\xd3\xdb\xc9\x8a\xcf\x60\x2b\x2e\x86\x43\x9e\xee\xdb\xa6\x46\xf6\x81\xa0\x84\x36\x90\x82\x4e\xc7\x72\x26\x81\xd8\x7a\xb2\xb8\xdb\x38\x78\x1c\x4e\x30\x2a\xa9\xfd\x64\xe1\x02\x7c\xb1\x46\x9c\xc3\xa2\x36\xb3\x7c\x9f\x19\x67\x55\xa1\xde\xc1\x4e\x5e\xe3\xc9\xb7\xb7\xb3\x14\xac\x76\x29\x46\xff\x5d\x9b\x5a\xb2\x1d\x82\xda\xf5\xa8\x5b\x49\x71\x73\x4b\x51\x6b\xc9\xcd\x41\xd4\x13\x86\xf2\xe2\x8d\x30\x94\x5f\xcc\xf7\xad\x12\x09\xbe\xc0\x49\x8a\xf7\x95\x82\xf2\x95\x2b\xae\xd9\x0f\xf2\xb3\x97\xba\x73\x81\xba\xb6\x00\xfe\x67\xf2\x1f\xc2\x7e\xc8\x0a\x65\x1d\xcc\xe5\x34\x7a\xc3\xa7\x7c\x61\x33\xdb\xfc\xcf\xe5\x33\xb4\x81\x3e\x17\x8b\xd5\xe9\x60\x29\x7b\xe7\x51\x9c\xe0\x6f\xc6\x55\x14\x90\x7b\xd1\x10\xfc\x9c\xe5\x74\x87\xe4\xcd\xc1\x68\x11\xcf\x50\xda\xa1\x30\x7e\xd8\xd8\x40\x2f\xea\x0b\x78\x92\x4a\x61\x6a\xed\x5b\x31\x62\xa7\x48\x90\x88\xb4\x97\x29\x7e\x1f\xc7\x33\xb9\x24\x2a\x26\x0e\x15\x65\x46\x35\x91\xc3\xb8\xf1\x0c\x66\x5d\xb4\xb2\xf9\xa6\xb7\xb5\xbd\xf3\x76\x77\xef\xbf\xde\xbd\xdf\xff\x70\x70\xf8\xbf\x8f\x8e\x4f\x3e\xfe\xf2\xeb\x6f\xff\xfe\x3f\x41\x7f\x30\xc4\xa3\xf3\x71\xf8\xf9\xcb\x64\x1a\xc5\xb3\xff\x4e\xd2\x6c\x7e\x71\x79\x75\xfd\x47\xad\xde\x68\xb6\xda\x9d\xb5\xf5\x97\xcf\x57\x37\x58\x84\x5b\x71\xb4\x13\x8b\x76\x69\x54\xe5\x10\x7b\xbc\x52\xa4\xe5\x86\x66\x61\xea\x12\x85\x8c\x76\x5c\x6e\x2a\x64\xa6\x43\xcf\x7e\xc3\x1c\xbb\x52\x22\x24\x29\xcb\x43\x52\x93\xea\xc0\x82\x5e\xa0\x7a\xf9\x0c\xbc\x57\xa4\xc0\xd4\xb0\x89\x8b\x03\x6d\x14\x01\x5a\x3e\xe3\x1b\xbc\x2a\x86\x39\xa0\x52\x81\x28\xd2\x22\xf7\x7c\x25\xc2\x0c\xa0\xff\x95\xb6\xa8\xfa\xd6\x44\xf9\xc1\x7b\x10\x1b\xe2\xe7\xcf\xb5\x0f\x82\x6c\xc5\x0f\x46\x91\x56\x6c\x49\x67\x58\x84\x1b\x99\xbb\xc7\x3c\xe4\x2b\x7b\xc4\x2b\x6f\x66\x9f\xf6\xe3\xd1\xff\xf1\xe8\x2f\x8e\xfe\x1f\x4f\x76\x5e\xd4\x3b\xe8\xcd\x76\x61\x07\xad\x7a\xe7\xcd\xb6\xea\xa3\x55\xef\xe8\x4f\xf0\xf5\xf6\x4e\x5b\x14\x99\xbf\xd6\x71\xab\x20\x0e\xf7\xe8\xbc\x55\xef\x78\xbd\xb7\xea\x9d\x7f\x80\x46\xa0\xf8\x61\x1d\x06\xe3\x2e\x67\x75\xb7\xbf\x3f\x58\x46\xc5\x43\x7c\x18\x87\x51\xe6\x73\x32\xae\x77\x3c\x4e\xc6\xce\xc3\xb4\xc4\xd4\xef\x65\x2c\x9a\x2c\xea\x6a\xac\x00\xbd\xc3\x09\xca\x24\xe2\x3b\x39\xab\x01\x6d\x2e\xbb\x36\xbe\xeb\x63\x14\x5d\x55\xc2\x65\x8d\x2f\xbe\xa5\x7c\xd6\xa0\xd2\x72\xbe\xc6\xbc\x96\x90\x6f\xf9\x8b\x87\xf6\x34\xd6\x1b\x2e\xe6\x68\x5c\x07\xd9\x47\x60\xa8\xbb\x19\x13\x11\x48\x2e\x96\x06\x59\x2c\x46\x10\x36\x3f\x85\xfb\xa4\x1c\x63\x74\x7e\x2a\x1e\x0a\x83\x91\xe5\xfb\x02\x7b\x98\xb2\x4f\xbd\xbf\xf3\x3e\xf5\xfe\x3b\xd8\xa7\x8a\xe0\x70\xdf\xfb\x94\x73\x39\xbd\xdf\x7e\xdc\xa6\xc4\xdf\xbd\x6d\x53\xe9\x65\x30\xdb\x8e\x86\x61\x10\x95\x96\xdd\xb1\x5c\x47\xf2\xef\x7f\xcb\x7a\xff\x30\x5b\x56\x91\x65\xf2\xfd\x6f\x59\xef\xb7\x8d\x4d\xeb\x71\xc7\xb2\x76\x2c\x65\xc5\x2c\xb5\x79\x7d\xd3\xdd\x4b\xcc\x8b\x82\x2d\x01\xa4\xf5\x91\x47\xc3\x87\x2f\xec\xee\x84\x2e\xee\x5a\x8d\xfc\x3f\x5c\xac\xd0\x8f\xa4\xfb\xec\x2b\xfd\x26\x97\xff\x22\x75\x01\x10\x96\x5f\x5b\xd0\xb9\x93\xb6\x80\xe5\xa8\xfd\x96\x4a\x83\x0a\x52\x5e\xa5\xe3\xa0\x6e\xbc\x1a\x4f\x83\xc1\x03\xaa\x16\x2a\x88\x37\x0b\xbf\xa0\xb5\x7f\x82\xba\xc1\xca\x17\x7b\x0b\x55\x84\x66\xc4\xa2\x7c\xd9\xdf\x6a\x43\x4d\x30\xb9\xd9\xdf\x6a\xbb\x64\x3c\x30\x71\xfe\x82\xaf\x69\x16\x6c\x6a\x07\x2b\xfa\x0a\xce\xbf\x41\x94\xf1\x24\xde\x71\x32\xa5\x36\xda\xdb\xbf\x1c\x7e\x82\x4d\xf7\x24\x7e\x87\xa5\x30\x88\x2e\x2f\x2f\xab\xf1\x0c\x47\x69\x3a\xa9\xc6\xc9\xf9\xea\x30\x1e\xa4\xab\x90\x84\x3b\x5e\x35\xea\x8c\xb3\xe9\xc4\xa1\x08\xd9\xbe\x98\xbd\xdb\xda\x91\x68\x8b\xe7\x82\xc1\x10\x16\xfb\x80\x18\x7b\x9c\xe5\xfd\xc2\x52\x9e\xc3\x1e\x45\x06\x26\x25\x0f\x61\xc4\xdd\x5e\x94\x70\xcf\xd2\xd5\xa5\x85\x4a\xf5\xc6\xba\xe6\xe9\x62\xc1\xf7\x18\xa9\xa9\x61\x31\xcc\x04\x29\xfb\x5b\xed\x45\xd8\x86\x19\xb3\x45\x36\x83\x54\x2b\x1f\xb2\x18\xcd\xa8\xd5\xa9\xea\x9d\xe3\xd9\xe1\x2c\xbf\x18\x63\x77\x60\xc3\xd3\x45\xf5\xc6\x3a\x98\x90\x6a\x5f\x69\xe7\x00\x73\xe3\x8b\xc4\x47\x6b\xfb\xe6\xd6\x6e\x37\x1e\xa2\x7d\x68\x3f\x1c\xac\x34\x7a\x0f\x66\xd6\x5f\x86\x23\xcb\xfb\x86\xd2\xfc\x82\x14\x4d\x8b\x2b\xfe\x29\xe7\x6a\xdd\xc8\xe7\x77\x5b\x30\x15\x7d\x1a\x6b\xb5\x9a\x09\x78\x49\xef\xa0\x85\x7e\x3f\xc5\xe4\xdd\x2d\x48\xe1\x4f\x68\x84\x50\x05\x24\xc2\x0e\x20\x03\x2b\x59\xb4\xb7\xb1\xd2\xe7\x75\x69\x2c\x00\x17\xa0\x9c\xca\x69\x30\xc9\xd0\x26\xfc\xb3\xbc\x58\x0c\xd4\x45\xc9\xfb\x3e\xc8\x0b\x93\xcd\xe3\xcb\x70\x54\xa5\x6e\x11\xb8\xc4\x3b\x53\x01\xfc\x72\xf2\xd6\x40\x71\x2d\xbf\xa3\x5e\x73\x29\x81\x57\x9f\x62\x87\x78\x4b\x56\x3a\xe3\x1e\x76\x6d\xe1\xa5\x46\xc8\x83\x99\x28\xcb\xd5\xe1\x84\xe5\x73\x0b\x83\xd0\x02\x74\x88\xdf\xc1\xd8\xb8\x52\xa2\x2d\x73\x46\x96\xc0\x84\x4f\xb0\x78\xe3\x3d\x2e\xf3\x3d\x86\xf6\x88\x3d\x39\xca\x29\x4c\x9c\x16\x95\x2f\x1c\x58\xbe\x65\x1b\x13\x01\xaf\x7f\x64\xc6\x2c\x06\xae\xdc\xa0\xe5\x35\xc7\xc7\x79\x14\x20\x62\x1c\x78\x0e\x78\x2f\x98\x75\x97\x25\x5a\x76\xf1\xb5\x32\x52\x83\x31\x48\x27\x10\x06\x85\x13\x9b\x62\x14\x6c\xd1\xab\xde\xbc\xf0\xa7\xb3\x4b\x10\x9a\x10\x03\x67\x7f\xd6\x0e\x4a\x75\x7a\x50\x52\x06\x3a\x37\xed\x8f\x81\xbd\x40\xd6\x3b\x0a\x2e\x8c\x1d\x43\x65\xbf\x53\xc8\x8a\xc5\x8c\x71\xb6\x61\x8c\xb2\x52\x4b\xd1\xd1\x70\xfa\x73\x44\xbb\x10\x01\xe6\x78\xbd\xa2\x36\xd7\x85\x78\xb0\xea\x77\x7c\x2b\xde\xbb\x24\xdf\xbd\x47\xef\x5b\x87\x5f\x99\xd2\x9b\xe2\xdc\x5c\xa9\xa4\x69\x37\x94\xf7\x3a\x77\x97\x1f\x90\xc6\xd5\xc5\xa6\x4d\xf7\x6b\x1f\x67\x5f\xae\x5a\x05\x79\xc4\x86\xbb\x80\xc9\x15\x1b\x84\x0a\x59\xca\xfa\xbe\x3d\xc7\x76\x61\x61\xc3\xae\x4b\x2c\xe0\xb8\x92\xbf\xdf\xdd\xbc\xca\x39\xbe\x53\x68\xee\xb3\x7b\x85\x1f\x3e\xbb\xed\xf5\x0a\x3f\x92\x76\xd7\xd6\xc8\x99\x7e\xed\x6f\x7d\xa6\x1f\x84\xb3\x31\x4e\x5e\x3c\xb0\x89\x00\x9c\xde\xd5\xa6\xfe\x9a\x43\xbc\x9d\xb9\xf3\x5e\x4e\xf3\x3d\xe8\xd8\x21\xe1\x38\xa9\x38\xb4\xab\x2f\xfd\x26\x04\xe2\xbd\x91\x09\x43\xab\x41\xce\x70\x41\x06\x95\xe8\x4f\xce\x88\x59\xc5\x1d\x78\x99\xb1\xa8\x0a\xb4\xc8\x12\xe9\x34\xc8\xe9\x86\xce\x4d\x86\xaf\x32\x72\x8a\x0c\xd8\x33\x9a\xd1\x3e\x31\xdf\x2c\x9e\x6a\x23\x18\xe2\x41\x38\x0d\x26\x93\x6b\x96\x06\x74\x58\xf8\xe6\x46\x1d\x95\x1b\xd6\x0a\x1b\xb8\x13\x81\x86\xde\xec\xf2\xc9\x38\x6e\x83\xdf\x83\xa6\xe7\x90\x53\xa2\xdc\xea\xa8\x9d\x5f\xee\x62\x47\xab\xe9\x71\xd4\x52\xcb\x54\xe5\xec\xca\x04\x12\xbb\xf8\xea\x96\x99\x20\x1c\xc3\xab\x90\x8f\x7a\xdf\xb0\xe4\x74\x1a\x37\x0f\x61\x34\x9b\x67\x77\x99\x53\x4e\x1e\x3a\xd1\xdd\x82\xce\xee\x8b\x38\x06\x06\xa3\x70\xd0\xc7\xad\x93\x4a\xc0\x68\xb9\x43\xd8\xc8\xc9\xd9\x40\xb2\x0d\x5a\xe1\x95\x93\x7a\x7a\x1a\xf5\x70\x8d\x80\x04\xd4\x55\x81\xde\xb8\x75\xf3\xfe\x9d\x56\x76\xd7\xd8\x6d\x95\x0d\xa2\xdb\x6e\x54\x0c\xe5\xf9\xfa\xa3\xa9\xdd\x3f\x5d\xf7\xed\xdb\x1d\xad\x48\xe6\x79\x9a\x70\xfb\x90\x02\x0e\xc0\x42\xe3\xea\x4c\x44\x45\x4a\x6c\xa8\x8e\xaa\xf7\x93\x90\x1e\x5c\x5e\x17\x72\xbc\xc2\x4a\xe2\x82\xaa\x28\x22\xab\x83\xf3\x32\x1e\x24\x38\xbb\x27\xa5\x12\x91\x7f\x77\xdd\x81\x83\xa0\x97\x8c\x4d\xb8\x3c\x91\xa9\xa3\x6f\x51\x8d\xa1\xea\x1c\xec\x09\x10\xec\xd4\x19\x09\x7d\x11\xf5\x51\x10\x8f\xa6\x87\x7b\x8e\xb7\xdb\x7d\xc6\x97\x85\x03\xd3\x82\xf0\xb2\xf4\x50\xa5\x44\x97\x35\xc7\xc9\x6d\x88\x9f\xa3\x98\xa2\x1d\x7d\xa3\xc4\xc5\x64\x5d\xcf\x8b\x8c\x69\x54\xe2\xfa\x02\x13\x96\x3b\x4a\xe6\xe6\x64\x12\x5f\xa2\x20\xe9\x87\x59\x12\x24\xd7\x88\xa9\x97\xbe\xe0\x6b\x47\xdc\xc1\x2f\xaa\x46\xe2\x67\x67\xc3\x39\x03\x65\xaa\x5b\x8a\x8d\xd6\x02\x67\x48\x82\x52\x8e\x1b\x24\xc4\x7f\x03\xdd\x46\x9c\xa0\x30\x8a\x70\x02\xd1\x67\xe3\x79\x06\x02\x84\x19\x85\x0f\x62\x26\x52\x1d\x23\x25\x43\xf6\x40\x5b\xb1\x02\xd2\x71\x8d\x9f\x5a\x23\x74\xd4\x58\x86\x04\x62\x45\x2b\x19\xe7\xe9\x23\x43\xa5\x60\xa8\x14\xb4\x1a\xfb\xed\xe0\x08\xe6\x93\x5e\x03\xce\x82\x21\x1a\xc4\x51\x9a\x05\x91\xd9\xbc\x33\x89\x94\x3e\xc7\x7e\xc5\x9a\xc0\xfb\x34\x3c\x43\xbf\x6f\xa0\xda\x55\x7b\x40\xff\xe7\x72\x87\xb1\x0a\x37\x3b\xf4\x7f\xf9\x9a\xb1\xd8\xd0\x89\x85\xc6\xb3\x8b\x22\xff\x82\x38\x64\xb0\x03\x3d\x44\x14\x32\xc1\xc4\xef\x25\x12\x59\x4e\xbe\x32\x17\x33\x76\x0c\x24\x74\xda\xc5\xc7\x3d\x7a\x52\x5d\x5f\x2c\x17\xcc\xed\x22\x90\xc1\x30\x7f\x37\xf1\xc7\xf6\x37\x7b\x2c\xfa\x18\xe0\x15\xc2\x12\xcb\x8d\x84\xb2\xe4\x94\x17\x09\x44\x66\x95\xbe\xff\x60\x64\x2a\x49\xf0\x56\x16\x06\x1f\x7b\xa8\xe8\x61\x30\xd4\xff\xd3\xa3\x87\x2d\x10\x53\x97\x11\x11\x09\x0f\x95\x34\xb4\x30\x82\x98\xbf\xc6\xc2\x28\x62\xfe\xaa\x0f\x14\x49\xec\xee\xdc\xae\x47\xd5\xd3\x30\xde\x8e\xfd\x98\x48\x17\xbb\xee\xe0\x68\xb9\x01\xc7\x72\x39\xa6\x3a\x56\x06\x50\x29\xa1\x70\x49\x83\x5f\x32\x09\x54\xca\xde\x90\x63\xd3\x60\xe0\xbe\x24\x12\x07\x7f\x8f\x11\xdc\xcb\xbf\xb5\xc2\xfc\xaa\xd3\x7a\xe1\x78\x3d\x09\xfb\x2f\x08\x2a\x43\xb0\x6d\x4d\x8d\xaf\x38\x1a\xbc\x00\x9b\x46\xc7\x7b\xea\x66\x69\x7c\x98\x0e\xdb\x8b\x8d\xef\xd2\x71\xd0\x68\x9b\x20\xc9\xcb\x86\x09\x2e\x1d\x07\xed\x7a\xc3\x7e\xd9\x5c\x77\x94\x6c\x1a\xaf\x92\x70\x86\xa7\xc3\x7a\xa7\xe6\xb4\xfd\xd3\x5e\xcd\xfa\x5f\x86\x23\xb3\x1d\x7c\x31\xfb\x32\x1c\xe5\xdd\x3b\xe8\x5d\x8f\x87\xf8\xc5\x60\xd4\x77\xbe\xce\x12\xcf\xeb\x17\xe7\x93\x60\x38\x0d\x22\xd7\xe7\xd8\x0d\x0c\x0f\xcc\xd7\xb3\x60\xf8\x22\x88\xd2\xf0\xea\x65\xc3\x1c\x04\xf2\x29\x4c\xe3\x7a\xad\xde\x30\x47\x9c\x7d\x7a\xb9\xf6\x72\xcd\x9c\x21\xf2\xe9\x0f\x9c\xc4\xcc\xf5\xda\xf1\x35\xf2\x7c\xa3\x3a\xb2\x17\x63\x7c\x65\x7c\x08\xb0\x49\x5c\x34\xee\xc6\xd0\x7a\x9f\x0c\xcc\xc9\x4d\x82\x7e\x3f\xcc\x9c\x2f\x5f\x4c\xf0\x79\x30\xb8\x7e\xe8\x3b\x20\xb1\x7a\xe0\xc9\x5c\x34\xf0\x52\xae\x15\xf1\xc8\x96\x08\x3c\x93\x95\x61\x98\x85\xb2\x75\x20\x7e\x37\x5a\xe2\x37\xa1\x7a\xfe\x9b\x10\xbb\xf8\x4d\x7f\x49\xd2\x96\xf6\xa5\xf0\x8b\x11\x32\xc5\x80\xd2\xaf\x75\x87\x45\xd1\xe1\xd4\xaa\x3c\x65\x89\xfe\x24\x68\x53\xbe\x8d\xb5\x1a\x84\x12\x69\xb3\x2a\x01\x8a\x37\x82\xee\xd4\x37\x94\xdc\xc4\x1b\x95\xca\xc4\xcb\x48\x7f\xa5\xd0\x14\x3c\x13\x52\x82\x1f\x92\x82\xe8\xa8\x0c\xd8\x40\x31\x7a\x51\x7e\x73\x32\x59\x56\x11\xa9\x29\x20\x55\x5e\xbb\xbc\x62\xd2\x1f\x8a\x8d\x75\xa9\xdb\xae\x57\xf2\xb5\xc9\x15\x9d\xae\xba\xed\x56\x45\x23\xbc\x6e\xbb\x5d\x91\x13\xdf\x6d\x77\x2a\xfa\xe8\x75\xdb\x6b\xe6\x8d\xb0\x49\xca\xdd\x4e\xad\xc2\xa8\xb5\xdb\x01\x7c\x04\xa5\x74\x3b\x8d\x8a\x4a\x2b\xdd\x4e\xab\xe2\xa2\x96\x6e\xa7\x59\x51\x29\xa4\xdb\x69\x57\x54\xfa\xe9\x76\x00\x2f\x8d\x66\xba\x9d\xb5\x8a\x49\x35\xdd\xce\x7a\xc5\xa4\x9b\x6e\xe7\x65\xc5\x22\x92\xee\x5a\xad\xe2\x20\xa7\xee\x1a\xe0\xcf\x96\x44\x77\x0d\xb0\x67\xa4\xd1\x5d\x6b\x55\x2c\xe2\xe8\xae\x01\xe2\x84\x8c\xba\x6b\x80\xb3\x5c\x67\xdd\xb5\x8e\x7a\x81\x5e\x91\x4b\xb6\xbb\xc6\xaf\xd6\xc9\x62\xee\xae\xbd\xac\xf0\xa5\xda\x5d\xaf\x55\xe4\x12\xee\xae\xd7\x2b\x72\x71\x77\xd7\x01\x1d\x49\xc1\xdd\x75\x68\x5c\x30\x9a\xee\x7a\xeb\xe6\xac\xd2\xa9\x3d\x5e\x1e\xfc\xf5\x97\x07\xbd\x31\x1e\x7c\x21\x9d\x82\x95\x42\xdd\x80\x68\x9a\xb3\x74\x3e\x23\x03\x83\x59\x7c\x6a\xa5\xdf\x20\xc7\xd3\x90\xe6\xe8\x87\x0d\xb4\xc2\x21\xaf\x38\x2c\x42\x84\x93\xc6\x3d\x5e\x57\xe4\x9a\xe3\x8b\x76\x8e\xf0\x08\x27\x18\x0e\x7a\x49\x78\x0e\x67\xb2\x30\x0a\x33\x09\x26\x9d\xcf\x70\x02\xaa\xeb\x0d\x23\x3d\x87\x02\x65\x73\x7e\x3e\xc5\x51\x66\x14\x40\x59\x8c\xc6\x41\x34\x9c\x60\x6d\xdc\x54\xd8\x7d\x27\x64\xcd\xa6\x06\xaa\xda\xee\x80\x8a\xee\x9b\xc6\x92\xa7\x26\x50\x61\x94\xad\x2b\x1a\xfa\x91\x5a\x5f\x28\x26\xf4\xd9\xb1\x8f\xf9\xb2\x06\x55\xc2\x7f\x24\x50\xe1\x85\x8a\x8d\x76\x88\x70\x22\x16\xd3\xf4\x5f\x00\xe9\x22\xc4\x97\x3e\x14\xbd\xcd\x2b\x08\xef\x71\x14\xd0\xd7\xaf\x7a\x79\x4e\x70\x80\x25\xe8\x8c\x79\xf5\x1f\xc8\x9a\x13\xb6\x23\xb0\xe8\xdc\xc0\xad\xaa\x65\xab\x15\x2f\x56\xf5\x8e\x1b\x2d\x7f\x4b\xcb\xd5\xd8\x8b\xb2\x66\x63\xd9\x26\x96\xab\xb1\x33\x89\x83\xdb\x54\xe9\xb4\xe0\xbd\x2c\x7f\x4b\x52\xaa\x52\x0a\xae\x20\xf5\xd5\x75\x86\x0f\x20\x39\x90\xf5\xda\x95\x77\x59\xa3\xbf\x5d\xba\xe8\x64\x5b\x45\x56\x84\x2c\xbd\x9c\x0a\x41\x42\x7b\x23\x70\x43\x1b\x6e\x9c\x1d\x9a\x85\xed\x2b\x96\x7d\xf5\x3a\x73\x19\x3f\x2f\xe5\x2e\xe8\x42\x65\x99\x7c\xda\xb2\xfe\x69\x78\x76\xab\xe4\xd9\xd2\x9c\x3b\xfc\x03\x53\x55\xad\x74\x1c\xd5\x8b\x0a\xc6\x2a\x53\x5b\x54\x10\x73\x23\x74\x75\x44\x9b\x6f\x67\xd6\x33\x32\x9a\xe4\x35\x81\x87\x22\x22\xf5\xa9\xcc\xdc\x6e\x37\x98\xcd\x26\xd7\xac\xe1\x20\x39\x9f\x13\x16\x9e\xe6\xf9\x2b\x32\x7e\x5d\x9d\x25\x71\x16\x13\x1c\x55\xce\x9d\x67\x38\x61\xee\x3e\x6e\x05\x4b\xa7\xfe\x28\xeb\xfc\x35\xb2\x0e\x04\x8c\xfe\x0b\xe2\x12\x39\x73\x2a\x15\x30\x91\x80\x2d\x96\xde\xe3\xa1\x4c\xea\xd6\x49\x95\x13\xc6\x2c\x94\x92\x54\x75\x69\xdc\xfc\xb9\x24\x3d\x1f\x5f\xe9\xb4\xdc\x5c\xe4\x84\xb0\x89\x0d\x3a\x7c\xd5\xa0\x9f\xd2\x1f\x69\x18\xb1\x60\xac\x84\x65\xd4\xae\xea\x35\xf6\x57\x46\x5f\xf5\x34\xbe\x6c\x79\x95\xca\x4e\x0b\xf5\xfd\xad\xb6\x61\x4d\xe1\x32\x00\x31\xbd\x26\xd1\x06\x1b\x55\x87\x01\x08\x4f\x7b\x93\x7b\x3b\x26\x35\xc1\xee\x5c\xc5\xa7\x36\x27\xad\x5d\x75\xd6\x5a\xed\x46\xb3\x56\xaf\xa0\xda\x15\x1e\x0d\x86\x41\x7f\xfd\xa5\x23\xaf\x62\xed\xea\xe5\x7a\x3f\x18\x0e\x46\xb8\x02\x03\xd3\x6c\xb4\x5b\x6b\x1d\xbd\xdc\x99\xf7\x46\xcc\x48\xa3\xa7\xf6\x62\x5f\x64\xd2\x73\xed\x5d\x97\xc1\x0c\x61\x70\xaf\x5e\xbc\x87\xd4\x3b\xfe\x1d\xc3\x7f\x7d\xcd\x67\x83\x22\xf1\x89\xc0\xe3\xe9\x05\x51\xe8\x89\xc0\xbb\xff\x49\x29\xbd\x7f\xca\x1f\xce\x5c\x2e\x21\xca\x67\x42\x70\x76\x01\xf2\x57\x2a\x95\x14\x98\xd4\x53\x1c\x7d\x45\xea\x4b\xd8\xeb\x5a\x65\xc3\x47\x1c\x7d\x2d\x08\xb0\xd1\x2a\x3b\x00\x42\x28\x63\xcd\x25\xdd\x06\x77\x37\xe3\x90\x5d\xed\x86\xc2\x7d\xdd\xaf\x0d\x69\x0d\x29\x63\x8a\x9e\xa3\x9a\x29\x3e\x68\xa5\xeb\x46\xe9\x7a\x6e\xe9\x86\x51\xba\x91\x5b\xba\x69\x94\x6e\xe6\x96\x6e\x19\xa5\x5b\xb9\xa5\xdb\x46\xe9\x76\x6e\xe9\x8e\x51\xba\x93\x5b\x7a\xcd\x28\xbd\x96\x5b\x7a\xdd\x28\xbd\x9e\x5b\xfa\xa5\x51\xfa\x65\xfe\xec\xd4\x8c\xd9\x59\x30\x99\x75\xa3\x78\xfe\x6c\xd6\x1b\x46\xf1\xfc\xe9\xac\x37\x8d\xe2\xf9\xf3\x59\x6f\x19\xc5\xf3\x27\xb4\xde\x36\x8a\xb7\x2d\x6e\xb0\xba\x4a\x18\xf2\x97\x30\x3a\x27\x55\xc3\x60\xd2\x77\x89\xcd\x01\xd9\x06\x4e\x9d\x03\xd5\x87\x4f\xce\x41\x19\xc0\x27\xe7\x00\x0c\xe1\x53\xd3\x85\x4e\x4f\xde\x41\xeb\xdf\x08\x12\x3b\x3b\xa5\xa0\x82\xfa\x15\x34\xa8\xa0\x61\x45\x59\xa0\x15\x84\xd6\x2a\x64\x0b\xad\x9d\x99\xbc\x61\x48\xeb\x0d\x2b\x48\x54\x95\x23\x54\x41\xa8\xde\xa8\xa0\x93\xd3\xba\x55\x6f\x40\xeb\xd1\x96\x68\x55\xb9\x68\x49\xbd\x35\x52\xaf\x61\xd5\xeb\xd3\x7a\x02\xc9\x40\xa9\xd7\xac\x20\xd4\x80\xf6\x9a\x56\xbd\xbc\xfe\xb5\x44\xff\x5a\x4b\xf5\xaf\x2d\xfa\xd7\x5e\xaa\x7f\x1d\xd1\xbf\xce\x52\xfd\x5b\x13\xfd\x5b\x5b\xaa\x7f\xeb\xa2\x7f\xeb\x4b\xf5\xef\xa5\xe8\xdf\xcb\xa5\xfa\x57\xaf\x55\x58\xff\xea\x36\xc1\xe4\x75\xb0\x5e\xaf\xb0\x0e\xd6\x6d\x8a\xc9\xeb\x21\xc1\x92\xf6\xb0\x6e\x93\x4c\x2e\x89\x36\x2b\x9c\x44\x6d\x9a\xc9\xed\x63\x4b\xf4\xd1\x26\x9a\xdc\x3e\xb6\x45\x1f\x81\x6a\xec\x4e\xbe\x7d\xeb\xe9\x64\x05\xa1\x36\xed\xa4\x4d\x37\x43\x5a\xd1\xd9\x49\x42\x6f\x2f\x69\x45\x9b\x70\x06\xb4\xa2\xbb\x93\xf5\x0a\x22\x1d\x3d\x39\xad\xdb\x94\xd3\xa7\x15\x9d\x9d\x24\x1c\xa3\x51\x83\x8a\x36\xe9\xe4\xf5\xb1\x2d\xfa\xd8\x70\xf3\x1a\x5f\x1f\x09\xcd\xd1\x3e\x36\xdc\xcc\xc6\xdb\xc7\x36\xef\x63\xc3\xcd\x6d\x7c\x7d\x6c\x89\x3e\x36\xdc\xec\xc6\xd7\xc7\x97\xb2\x8f\x6e\x7e\xe3\xed\x63\x4b\xf4\xd1\xcd\x70\x7c\x7d\x24\x8c\x91\xf5\xd1\xcd\x71\x7c\x7d\x5c\x97\x7d\x74\xb3\x1c\x2f\xad\x36\x2b\xbc\x8f\x6e\x9e\xe3\xeb\x63\x43\xd0\x6a\xc3\xcd\x74\x7c\x7d\x5c\x13\x7d\x6c\xba\x99\x8e\xaf\x8f\x64\xf9\xd3\x3e\x36\xeb\xee\x05\xb9\xbb\xeb\x27\xd6\x16\xe0\xda\x74\x73\x9d\xdd\x5d\x77\x27\xc9\xb0\x92\xb5\x75\x72\xda\x74\x73\x9d\xdd\xdd\x9c\x05\xd9\x81\x8a\x6e\xae\xb3\xbb\xeb\xe9\x64\xab\x82\x1a\x4d\xa8\x68\x93\x4e\x5e\x1f\xeb\xb2\x8f\x6e\xa6\xe3\xeb\x63\x4b\xf6\xd1\xcd\x74\x7c\x7d\x84\x89\xa4\x7d\x74\x33\x1d\x6f\x1f\x6b\xa2\x8f\x6e\xa6\xe3\xed\x63\xb3\xc2\xfa\xd8\x72\x33\x1d\x5f\x1f\x6b\xa2\x8f\x2d\x37\xd3\xf1\xf5\xb1\x29\xfa\xd8\x72\x33\x1d\x5f\x1f\x09\x2b\xa7\x7d\x6c\xb9\x99\x8e\xaf\x8f\x2f\xc5\x3c\xb6\xdc\x4c\xc7\xd7\x47\xb2\x3c\x58\x1f\xdd\x4c\xc7\x4b\xab\x6d\x4e\xab\x2d\x37\xd3\xf1\xf5\xb1\x21\xfb\xb8\xe6\x5e\x90\x7b\x7b\x7e\x41\xb5\x43\x3b\xe9\xe6\x3a\x7b\x7b\xee\x4e\x02\xcd\x01\x0f\x68\xb9\xb9\xce\xde\x5e\x8e\x18\xd0\x06\x11\xd0\xcd\x75\xf6\xf6\xdc\x9d\x24\xbc\xa3\x01\xc3\xda\x76\x8b\x3a\xbe\x3e\x92\xf9\xa0\x7d\x6c\xbb\x99\x8e\xaf\x8f\x4d\xd1\xc7\xb6\x9b\xe9\x78\xfb\x58\x13\x7d\x74\x33\x1d\x5f\x1f\xeb\xb2\x8f\x6e\xa6\xe3\xeb\xe3\xba\x98\xc7\xb6\x9b\xe9\xf8\xfa\x08\x34\x47\xfb\xe8\x66\x3a\xbe\x3e\x82\x48\x4e\xfb\xe8\x66\x3a\xde\x3e\x36\x2b\xbc\x8f\x6e\xa6\xe3\xeb\x63\x4b\xf4\xb1\xe3\x66\x3a\xde\x3e\xd6\x79\x1f\x3b\x6e\xa6\xe3\xeb\x63\x43\xf4\xb1\xe3\x66\x3a\xbe\x3e\xbe\x14\xf3\xd8\x69\xda\x0b\x12\xae\x51\x32\x9c\x4c\xf1\x30\x0c\x32\xe6\x54\x06\xee\x0a\x7a\x39\x72\xc4\x45\x1b\xa8\x04\xff\x3e\x47\x81\xa9\x61\xa5\x65\xea\xac\x4c\x9d\x94\xe9\xbb\xcb\x34\x58\x99\x06\x29\x33\x70\x97\x69\xb2\x32\x4d\x52\x66\x68\x69\x73\x0d\x55\xe5\x8e\xc3\x52\x77\xc9\x80\xb6\x90\x29\x5d\x64\xd3\x0d\xb2\xc0\x75\x30\x0f\xb2\x40\x84\xf2\x09\xb2\xc0\xaf\x1c\x8b\xde\x84\x59\x7a\x12\x67\xc1\x44\xc0\x8c\xb6\x82\x2c\xa0\x1e\x24\x3f\xa1\x75\x07\x74\xa8\xf3\x1e\x8f\x32\x0e\x5d\x78\x9c\x40\x79\xab\x33\xde\x94\x57\x02\xcd\x53\x09\xf2\xe7\x9f\x7f\x46\x6d\xb8\x78\xab\x5d\xad\xd7\xe4\x7d\x9b\x2c\xf1\x2f\xd4\x6c\x58\xc4\xa1\xf7\x65\x17\x6d\x20\x50\xbb\x8f\x26\x71\x9c\x94\x94\x4e\xae\x6a\xba\x77\x5f\xe7\xa0\xec\x7b\xb4\xa1\x3c\x99\x0b\x47\xa0\x5e\x2a\x95\x24\x6e\xcf\x51\xa7\x45\xf3\xa5\xbd\x84\x60\xa2\xad\x32\x55\xd8\xb8\xf5\xb3\xbc\x2a\xc3\x59\x2a\x67\xd5\xb7\xc5\xb5\xb3\x36\x38\xa6\x9a\x35\xc1\x2d\xd2\xcd\x5a\x5c\x62\x99\xce\xb6\x8a\x74\xf6\xbd\xb3\xb3\xef\x6f\xdb\xd9\xf7\xce\xce\xbe\x2f\xda\x59\xbb\xb7\xaa\x13\x55\x49\x74\x9f\x07\x9b\x82\x9c\x7a\x6e\xff\x41\x30\x78\xa7\x6e\x0c\xe0\xa3\xe8\xf2\xa4\xca\xcd\x2b\xbf\xc0\x1b\x52\xd3\x79\x3b\xc8\x77\x97\x19\xc6\x7b\xbd\xdf\x96\xba\xf7\xf0\x5c\x71\xa1\xbc\xeb\x7f\x81\x09\x5c\x61\xec\x9e\xba\xef\x2e\x76\xd9\x2d\x59\xa9\xb4\xab\x5d\x4b\xec\x2e\x7d\x1f\x41\x69\x61\x57\xbb\x8b\xd8\xf5\x5e\x42\x2c\xbe\x71\x38\x62\xb9\x81\x61\x0e\x59\x04\x9e\x21\x8c\xa9\x5e\xb4\x40\xb2\x72\x70\x43\xc8\x65\xf5\xa0\x60\x05\xa7\x4c\x71\x43\x07\x8f\xf2\xfa\xdf\xda\x78\xe1\xf3\x27\x8b\x16\x7c\xde\x95\x3c\x82\x06\xf9\xea\xf6\x70\xa0\xbf\x04\x92\x86\xea\xeb\xaa\x82\xd2\x0a\xd2\xaf\xd0\x80\x4f\xa2\x0d\x14\xa0\xe7\xa8\x54\xea\xa3\x1f\xe9\xe6\x58\xfa\xbf\xe4\xe7\xb0\x4c\xd8\xc0\x15\x7a\x8e\x32\xa5\x3d\x11\xb0\x38\x22\xd3\x94\xd2\x95\x4a\xe3\x94\x37\x1b\xe8\x05\x4a\xcb\x50\xad\x6f\x18\xbd\x09\xac\x8c\xf3\x7f\x31\xac\x60\x3b\x2e\x0d\xd0\x8f\xe8\xff\x3e\x0c\x56\xc6\x21\x68\x21\x56\x7d\xf4\x3b\x1a\xa0\xdf\x09\x62\xf7\x8f\x8c\x21\x00\x2e\x44\x86\x20\x52\xea\xa3\xaf\xf7\x3c\x38\xea\x6d\xf5\xb1\x2f\x4d\xfa\xc2\xc4\xfb\x45\x82\xac\x71\x3f\x31\xc3\x45\x11\x56\x83\x0d\xc6\xe3\x2c\xe6\x29\x7d\xdb\xb0\x66\x6c\x5d\x0a\x23\x97\xfd\xad\xb6\xc3\xf7\x2b\xbf\xbc\xed\xf0\x25\xe3\x8b\x69\x97\xf9\x7a\x46\xfe\xfd\xad\xb6\xd3\x64\xc0\x3b\x09\x0b\x72\xd5\xdf\xd7\x14\xdc\x2a\xb4\xc3\xe2\x89\x53\xbd\xfc\xee\x63\xe2\xa8\x53\x99\x98\x88\xdd\x69\x30\x20\x93\xa1\x65\x86\xb7\xe7\x83\x15\xb3\xe7\x44\x66\xb3\xa7\xf3\x92\x9b\x81\x9d\x45\xb6\xf6\x58\x40\x35\xfe\xd6\x2e\x66\xff\xfc\x98\x6c\x74\xb1\xfd\xc4\xe2\x0c\xa1\x1d\x8c\x87\xfd\x60\xf0\x85\xc5\xd5\x9c\xc6\x43\x58\x52\x84\x66\xc4\x7c\xc3\xcb\xde\xce\x1b\x22\x02\x39\xc4\x03\x30\x73\x82\xaf\x9a\xb5\x1c\x58\xb8\xd0\x56\xf6\x09\x00\x66\xcc\x23\x56\x7d\x6f\xe7\x4d\x75\x3b\xa2\xb1\xca\xc1\x80\x6a\xe7\x8d\xc3\xe0\x67\xe6\x31\x97\x61\x66\x86\x39\x26\x33\x7e\xd1\x94\x85\xa0\xe2\x02\x09\x7d\x74\xdd\x33\x2b\xa1\x3c\x68\x21\x35\x94\x87\x5e\x9e\xc7\x28\x7f\x87\xaf\xd3\x2c\xc1\xc1\x74\x33\x1a\xb2\xde\x39\xac\x23\x63\x66\x16\x2b\xc0\x55\x58\x03\x2e\x21\xfb\x08\x4f\x31\x04\x19\x07\x63\x4c\x3a\x4f\x2c\x56\x26\xf8\xcf\x47\xf8\x2a\xa3\xaf\xdd\xe2\x3b\xbe\x78\xc3\x62\xa6\x42\xeb\xd5\x74\x12\x0e\x70\x89\xa3\x20\x6e\xea\x05\x2e\x2e\xfb\x49\x6d\xd6\xb6\xf0\x3f\x65\xd6\xee\x30\xba\x60\x38\x3c\x0e\xd3\xa5\xc7\xf6\x9b\xd1\xcd\x89\xec\x50\x1f\x0f\xe2\x29\xf3\xba\x27\x04\x11\xc6\xf3\xb4\x18\xc9\x88\x2e\x16\x12\xc7\x73\x7a\x53\x5a\xd8\x05\xc3\x37\xc2\x3e\xb0\xc1\x79\xef\x42\x06\x6b\xb9\x78\xa5\x1b\x8d\xab\xe1\x98\x69\xf3\xf2\x33\x64\x76\xbd\x70\x1e\x69\x44\x69\xb4\x81\xc2\x0b\x36\x85\x35\xcf\x4a\x8c\x2f\x30\xda\xfb\x05\xce\x9f\xe9\xbc\x9f\xe2\xff\x9e\xe3\x28\xcb\x39\x3d\x03\xbe\xc2\x81\x61\xa1\x01\xb4\x89\x8f\x31\x21\xf6\x24\x90\x3f\x46\xe5\x98\x0e\x34\x14\x2c\x09\x20\x15\xa4\x77\x65\x75\x15\xb1\x19\x91\xef\x9c\xd9\x72\xf3\xa3\xc6\x50\xd3\x73\x69\x21\x08\x91\x60\x44\xa3\x70\x8e\xb6\xe8\x85\x61\xc1\xc5\x89\x9d\x37\x79\x06\xd7\x7c\xd3\x59\x26\x4e\x5d\xa7\xf9\x28\x7c\x7c\xef\xc2\x07\xfa\xcf\x59\x82\x53\x9c\x5c\x60\x2a\x86\xc4\x73\x22\xca\x2b\xe2\x07\xa8\x31\x82\x2c\xec\x4f\x18\x07\x46\x5b\x09\x7a\x93\x84\x41\x84\xde\x52\xf7\x4c\x34\x0a\x27\x18\x47\x83\xea\x00\x40\xf0\x90\xcf\x10\x01\xdb\xa0\x9f\x93\x23\x28\xf2\x5f\x41\x84\x76\x93\x79\xff\x1a\x7d\x1e\x93\x7f\xaa\x97\xb8\xff\x9f\xe7\xd3\x20\x9c\x54\x07\xf1\xd4\x2d\xef\x9c\x1c\xf1\xe6\x72\xc4\x1e\xb5\x50\x61\xe9\xe7\x89\xcc\xf7\x12\x0d\xc8\x41\x81\xa6\x4c\x7a\xfa\xe4\x09\x19\x74\x20\x3d\x91\x0e\x09\x94\x44\x54\x29\x54\x86\x59\xa7\xbf\xfe\x44\xab\xab\xf1\x05\x4e\x46\x93\xf8\x92\xd4\x81\x8d\xaf\xce\xd3\x81\x92\x7a\xf5\x4e\xf9\x47\x52\xf6\x95\xf8\xdc\x50\x3f\xaf\x9b\x5f\x9b\x6c\x0f\x63\x8d\x01\x9e\x80\x0a\x01\x2b\xda\x5d\x5d\x45\xbc\x59\xd4\xaf\x93\x22\x80\x32\x34\x5d\x7b\x25\xaa\x34\x64\x15\x51\xe6\x09\x20\x40\x0b\xd1\x52\x4d\xbd\x14\x2b\xf6\x04\x50\x61\xe5\x6e\xe0\xbf\x84\x20\xd5\x12\xcf\x9f\xf7\x9b\xca\x77\xf8\x0f\x2f\x43\x8b\x3c\x7f\xde\x6f\xbc\x7a\xea\x2f\xf0\xfc\x79\xbf\xce\xbe\x93\xff\x42\xc7\x79\xa3\xf0\xf0\x7c\x03\x7a\xfe\xfa\x35\xcb\x07\xa9\xbe\x6e\x50\x15\xa0\xf6\x96\x21\x64\xb7\x24\xaa\xd5\xae\x6a\x75\xa6\xf5\x93\x45\x19\xd7\x23\x85\xc8\xcb\x1b\x93\x3a\xd8\xf2\x28\x0d\xe8\xbf\x3a\x8d\xb0\x97\xf4\x06\x89\x93\x92\x7c\x59\x66\x04\xa3\x4c\xc1\xea\x2a\x22\xbb\x04\xdc\xc4\xa0\x50\x59\x48\x74\xf1\x58\x2b\x6d\x25\x45\x00\x2f\x45\x71\x34\xb9\xa6\xcb\x71\xeb\xd7\x83\xa3\x2d\xf4\x19\xbd\x46\xeb\x00\x93\x37\x58\x77\x61\x41\xef\xe2\xf4\xce\xb2\x6f\xbc\xbf\x7c\x2d\x69\x67\x01\xb1\xae\xaa\x9e\xd7\x7f\xa1\xcc\xb9\xac\xc8\x69\x15\x37\x64\x18\xbb\x55\xc6\x13\x45\xb3\x7c\xc0\x2c\xd4\xf3\x24\x1e\xe4\x97\x7a\x40\x68\x70\x37\x92\x2f\x03\xa1\x5b\xc8\x41\x68\xb1\x2c\xc4\xa5\x03\x42\xd8\x36\xcd\x53\x56\xf4\xc4\x14\x8d\xd8\x67\x05\x57\x5d\xf5\xbc\x8c\x50\x84\x3c\x82\x11\xba\x9d\x70\x84\x96\x14\x90\x90\x2e\xcf\xd9\x87\x2e\x49\xf7\xea\xd9\x4b\x2c\x8d\x57\x86\x64\x25\x8a\x2b\x02\x96\x57\xc4\x52\x0a\x2f\x21\x69\xb5\x1e\x25\xad\xef\x5d\xd2\xf2\xc8\x57\x1e\xf5\xce\xc9\x51\xbe\x9c\xb3\xac\x7a\xc7\xc1\xd2\x4d\x5e\xfe\xc8\xc4\xff\x79\x4c\x3c\xf7\x34\xfb\x00\x2c\x7b\x2f\x1a\x24\x18\x22\x37\x30\xe0\x06\x48\x26\x87\xc8\xc9\x7d\x81\xa8\x31\x8d\xe7\x0b\xdc\x96\x7f\x45\xb5\xbf\xd5\xe6\x50\x74\x57\x58\x7c\xde\x26\x65\x96\xd8\x05\xda\x8f\xbb\xc0\xdf\x62\x17\xd8\x9e\xe0\x41\x96\xc4\x51\x38\x40\xbd\x78\x88\xfb\x71\xbc\x58\xe1\xbf\xdd\xcb\x53\xf8\xd3\xaf\x4b\xed\x08\xdb\x3d\x5d\xe1\x4f\x9e\xef\x6b\x07\x50\x59\xbb\xce\x40\xf4\x7a\x79\x5a\x4c\x82\x8f\xb6\x90\x1e\x0a\xbf\x21\xbe\x15\x7e\x3c\xf5\x52\x6f\xb1\xde\x0c\xca\x2c\xb1\x8e\xff\xde\xc9\x91\xff\xe7\xac\xe3\x83\x79\x36\x9b\x67\xc5\x2f\xed\x0e\x72\x2f\xed\x0e\x96\xbf\xb4\x33\xa5\xba\x03\xe3\x12\xef\xe0\xaf\xbd\x0e\x7a\x70\xa9\xce\xd6\xcd\x8b\x37\xf7\x2b\xd9\xe5\x34\xf4\xbd\x48\x77\xff\xa4\x13\xf6\x81\x71\xad\xe9\x13\xa2\x0e\x0a\x5c\x5a\x1c\x2c\x79\x69\xf1\x98\xc5\xee\xef\xc1\x7c\x37\x3f\x1c\xef\xa1\xdf\xaa\x2f\x1b\x4d\x6e\x20\x8e\xd2\x8c\x2c\xef\xf3\x6b\x8b\xfb\xce\x82\x61\x75\x33\x4a\xc3\xdf\x48\x69\x91\x0b\x6e\x16\x0c\x55\xf6\x37\x0c\xb2\x40\xb9\x08\xf5\x5d\x80\xa6\xfa\x0d\x28\xa9\x75\x2c\x0d\x7e\x35\x03\xe0\x57\x7a\xd1\xbe\x99\x56\xa4\xef\x4b\x28\x02\x44\x31\x8f\x32\xd1\x33\x23\x98\x15\xd8\xe2\x1d\xd2\x6f\x16\x30\xfa\xe2\x85\x8e\xd9\xbf\x8c\xef\x56\x6b\x34\xa6\xcd\x24\x48\x69\xe4\x2c\x34\x8b\xd3\x50\xf7\xc0\x27\x8d\x92\xef\xa4\xfe\x61\xcc\x3b\x2b\x5a\x78\x6e\x60\xf4\x02\xd5\x8d\x46\x0e\x83\xa1\x7c\x86\x81\x12\xd9\x46\xf4\xd7\x94\x95\xa8\x6d\xc9\x90\x5a\x7a\x23\x32\xa4\x96\x5a\xda\x15\x5c\x4b\xb7\xcc\x7e\x6e\x00\xe2\x76\x88\xdc\x02\x77\x1e\x39\x88\xc3\xa4\x88\xb7\x38\x53\x12\xce\x6b\x53\x45\x15\xf8\x62\x34\xf3\x67\x4e\xe9\x73\x49\x47\xf3\x05\x39\xfe\xb2\xbe\xcb\x8b\x20\x05\x05\xb6\xaf\x58\x1e\x12\x06\x18\x4f\x6f\x9f\x3e\xb9\x71\xf2\x4d\xbe\x5c\xae\x5e\x36\x9a\x4b\xf1\xce\xbb\x25\x26\x7b\xe4\x9d\xdf\x8a\x77\xee\x1d\x1f\x20\x08\x89\x5b\x8c\x75\xee\xb1\x00\xba\x77\x65\x9d\x7f\x39\x3b\x94\x4b\x62\x01\x3f\x74\xb0\x2a\x9a\x0e\xc0\x1d\x81\xae\x9a\x04\xd1\x30\x9e\x96\x2c\x0e\x58\x2e\x57\x0d\x49\x29\x1f\x0e\x4b\x1d\x76\x6a\x71\xb9\x46\xeb\xac\x42\xc0\x3d\x32\x2a\x93\x51\x71\xe2\x5c\x8a\x51\xfd\xbd\x33\x2f\xfc\x8f\x62\x54\xab\x7b\xdb\x3d\xf4\x72\xed\xe5\xda\x8b\x3a\x62\xb4\x81\xf6\x71\x36\x8e\x87\xa8\xe1\xe3\x56\x10\xda\xfb\xb6\xdc\x6a\x73\x38\xa4\xfe\x83\xfa\x82\x28\xc0\x05\xf8\xea\x25\xb5\xe9\x1f\x5f\xb4\x5a\x03\xff\x07\x27\x31\xe4\x0e\xcb\xc6\x18\x25\x38\x55\xf8\xa2\xd6\x11\x52\x8e\xf5\x98\x3c\x5b\x78\xdf\x8a\x17\xb0\x85\xf8\x07\xc3\x41\x5f\x8d\xde\xe6\x01\x34\x85\xe7\x5e\xd8\x71\x84\xd1\x34\x4e\x30\x15\x1e\x5f\xbc\x80\xbe\xf9\x46\x91\xaf\xf7\x17\x2f\x0a\x2e\x70\x98\xcf\x65\x16\xf8\xda\xdd\xa2\x9c\x3f\x2e\xf0\x6f\x76\x8a\x43\x51\x1c\xcf\x8a\x89\x21\x1f\x38\x39\x7a\x57\xb6\x20\x76\xff\x9a\x90\x45\xf2\x68\x4e\x34\xb5\x14\xd1\xdd\x2d\xdc\xec\x23\xd1\x7d\x2b\xa2\xfb\x3f\x0a\xf3\xcb\x27\x39\x85\x07\xfe\x85\xc2\x6f\xe1\x83\xb3\x7a\xbe\xb5\x04\xe0\x52\x29\x5f\x04\x2e\xa3\xaf\x5f\xcd\x57\xb7\xda\x62\xdc\x3d\x5e\x1c\x57\x60\x75\x15\x7d\x24\xf0\xf5\x7a\xa1\x15\x29\x00\x34\x0b\xa2\xcc\xe5\x38\x9c\x60\x54\xfa\xa1\x24\x7d\xad\x65\x0c\x6e\xf0\x38\xb4\x62\x6e\x0b\x13\x4e\x4b\x91\x19\x8a\x2d\x09\xe9\x2a\x4a\xd3\xb1\x1b\xe2\xf1\x16\xd9\xbd\x14\x0a\x5a\x8a\x97\xfc\xbd\x1d\xb7\x1c\x39\xba\x68\x92\xac\x87\xe5\x2b\x32\x13\x12\xb4\xf6\xd7\xe7\xf9\x78\xd8\x24\xe1\xc5\x62\x62\x5b\x31\xaf\xc5\x97\xe3\xdd\xcd\xba\x8c\xf5\x4c\x9e\x94\x8f\x76\x22\x70\x97\x83\xe8\x61\x90\xa6\x64\x21\xbf\x20\xa8\x0d\xd1\x3b\x7c\x8d\xb6\x70\x12\x5e\xd0\x9c\x90\x3b\x7c\x50\x1a\xf9\x31\xa7\x0f\xdf\xbc\xdb\xda\x69\xc8\xd6\xc4\x73\xc1\xc4\xe3\xbd\x38\x1a\x85\xe7\x73\x96\x89\x32\x86\xac\x90\x69\x5e\x7e\xc9\x24\x9e\xe1\x24\xbb\x46\x7f\xd2\x63\x31\x78\x93\x02\xf3\x3d\x19\xd3\x1c\xc7\x29\x79\x08\x23\x96\x2e\x20\x8b\x85\x2f\x4d\x15\x6d\xe1\x51\x30\x9f\x64\x5d\xd4\x42\xa5\x7a\x63\x1d\x12\x29\x97\x7d\xf0\x3d\x09\xcd\x71\xc2\x13\x99\x4b\x70\x64\xfc\x17\xa1\x19\x66\x2c\x79\x66\x0a\xa0\xe4\xa1\x5e\xf9\x90\xc5\x68\x86\x93\x51\x9c\x4c\x15\xe0\x1a\x64\x25\xfd\xe3\x60\x74\xde\xf5\x8d\x32\xa2\x17\x5f\xc7\x10\x73\xa6\xde\x58\x5f\x6d\x36\x8c\x10\xdc\xb4\x2b\x14\x75\xe3\x93\x44\x48\x6b\xfc\xa6\x9c\x97\x90\x34\x2f\x81\x3c\x99\x95\xa1\x24\x2d\xbe\xde\x16\x67\x11\x3d\x00\x3e\x77\x43\xba\xaa\x66\x0c\x25\xe3\x37\x70\xd1\x0d\xf7\x37\x1b\xc5\x09\x9c\x62\x64\xa3\xf7\x90\x18\xf4\xcb\x70\x64\x25\x8d\xa7\xd4\xce\x4f\x8f\x9a\x19\xd6\x32\x15\xff\x94\x93\xb5\x4e\xd3\x4f\xde\x19\x4c\x45\x9f\xc6\x5a\xad\x66\x02\xce\xc9\x5e\x3f\x18\x9d\xbb\x0d\x2f\xc8\x44\x6c\x88\x9f\x9c\xf0\x48\x71\x5f\x30\x0c\x7b\xbd\xc3\x75\x05\xf5\xa0\x2b\xca\x82\x6e\x93\x6f\x76\xc6\x60\x03\xb5\xf0\x87\x6a\xc1\xca\x69\x30\xc9\xd0\x26\xfc\xb3\x7c\x22\x5a\xee\x46\xa3\xf8\xb5\xdf\x85\xec\x68\x22\xf5\xe1\xa8\xca\xa2\x92\x94\x78\x67\x2a\x80\x9f\x77\x52\x59\x71\x75\x5e\x8d\x9a\x4b\xe5\x76\xd1\xa7\xde\x69\x40\x18\x66\x9e\xa4\xb0\xcc\xcb\x1e\x7c\xf7\x19\xad\x12\xf2\xa1\x3c\xa8\x22\x66\xc7\x6d\x96\xe8\x4f\x50\x0e\xb2\x29\x1d\x6c\x9a\x6e\xde\xd2\xe7\xb8\x42\x3d\x81\x9c\xbc\x17\x0d\xf1\x95\xab\xc6\x69\xed\x8a\x29\x80\x1c\xd1\x3a\x17\x84\xe8\x12\xa8\x08\x61\x59\xbc\xf1\xe6\xaf\x97\xd8\xf0\x4a\xf2\x8d\xb7\x12\xdf\xf2\x36\xc8\xac\x54\xd9\x93\xcb\x08\x43\x6e\x2d\xb4\xa8\x7c\xb1\xc0\xc8\x42\xff\xc8\x04\x75\xa3\x83\x3c\x2e\xd2\x6b\x8e\x8f\xd3\xb8\x40\x74\x92\xe5\x39\xe6\xc9\xb2\x81\x02\x65\x1a\x5f\xd9\x6b\x73\xce\x10\xcb\xe8\x2d\x53\x03\xdb\xdf\x17\x67\x63\x00\xf8\xda\x10\x3b\x47\xd7\x2e\x2e\xb2\x18\xc9\x57\xac\xe3\x1e\x44\xf6\xc4\x18\xbb\x41\x87\x6a\x34\x3b\x06\xd6\x81\x85\x66\xcb\x51\xa7\xb6\x1c\xca\xf4\x79\x8d\x39\x10\xf0\x73\xad\x09\x18\x3d\x31\xd2\xea\x47\xd7\x58\x17\x19\x6f\xb4\x28\x14\x94\xab\xb3\x7c\xf4\xd5\x77\xee\x80\x55\x4a\x13\xbf\x1d\x1c\xe9\xdd\x01\xd7\x29\x87\xc7\xb5\x35\x6e\x9f\xa9\x0d\xcc\x67\x6e\x03\xa3\xcc\xe6\x2b\xf4\x39\x67\xf4\xc8\x9f\xac\x71\xfa\x19\xcc\x61\xac\x8e\x9c\x7e\x36\xcd\x62\xf8\xdf\x8d\xfd\xda\x0c\x38\x45\xfe\x14\xe6\xc0\x74\xd3\xd0\xa8\x6b\x4a\x0c\x26\x71\x5a\x3b\x7b\xfe\x3c\xdf\xa4\x48\x01\xae\x1c\x7d\x39\xdf\x70\x04\x31\x63\x7b\x99\xac\x97\x67\x40\xa9\x1e\x23\xee\xb4\xa1\x17\x09\x36\x93\xbb\x91\x2f\xb9\x89\xdf\x97\x68\x19\xa6\xae\x74\xfb\x8b\xa3\xd7\x38\x44\x83\x7b\x08\x62\x43\x45\x04\x21\x19\x52\xa1\xd0\x27\x26\x2c\x57\xad\x82\x3c\xb2\xe9\x5d\xc0\xe4\xca\xa6\x32\xc8\x8e\x38\x4a\xfa\x04\x98\x0a\x32\x05\x55\x36\xec\xba\x58\x4c\x0a\x2d\x10\x9e\x6e\xf2\x6c\xd1\x28\x34\x77\xa0\x1e\x33\x85\x2e\xcf\x09\x7b\x73\x56\x59\xfb\x7b\xfb\xd0\x2f\x91\xd6\x7d\x71\x72\xf4\x87\xd5\x1d\x79\xd3\x6b\xfb\xb2\x5e\xff\x13\xb4\x4b\xc7\x60\x9c\xd9\xe3\xc6\xbb\x54\x89\xa4\xbe\xcc\xd3\x23\x09\x3c\x8e\xf0\x3c\x0d\xfa\x13\xcc\xc2\x81\x29\xe8\x1c\x23\x35\xd5\x22\x85\x62\xbe\x79\x8b\xf4\x0c\x6b\xca\xb6\x70\x04\xd9\x94\x11\x33\xb4\x65\x36\xc6\xb6\x26\x49\x94\x87\x18\x2b\x61\x8a\x02\x44\x13\x30\xa3\x0b\x9c\xa4\x10\xb5\x6c\x1c\x64\x28\xc2\xe7\x13\x3c\xc8\xf0\x90\xb0\xe1\x01\x4b\xa9\x9a\x31\x85\x4f\x16\xa3\x49\x98\x65\x13\xfc\x82\x06\xb8\xac\xea\x40\x71\x92\xc4\x09\x1a\xc6\x38\x8d\x56\x32\x14\x8c\x46\x78\x40\xeb\x52\xa4\x56\x52\x94\xe2\xc1\x3c\x09\xb3\xeb\x8a\xa8\xd8\x9f\x67\x28\xcc\xa0\x12\xaf\x11\x66\xa9\x08\xa8\x10\x4e\xc2\x8c\x39\x71\xd3\xbc\xae\x21\xe1\xcf\x53\x1c\xd1\xfd\x20\x75\x29\xca\xe8\x80\xbc\xa7\x9d\x13\xea\x32\xe3\xad\x3a\x7f\xb7\x4d\xda\x96\x7f\x48\x79\xa7\x9a\x41\x7b\x0f\x18\xd2\x7a\x1b\x4e\x0d\x17\x79\xa7\x85\x90\x9d\xd0\xc8\xee\x85\xbd\xe7\xb4\xdf\x44\xbb\xe4\x97\x23\x71\xdc\xbb\xd3\xda\x59\x05\x95\xde\x9d\x36\xcf\x58\xb0\x00\xf4\x95\x3c\xb2\xab\x80\x7a\xa7\xec\x48\x22\xf7\xee\xb4\x4e\x2b\xd5\xf4\x4a\xcd\xfc\x4a\x0d\x5a\xa9\xae\x57\xaa\xe5\x57\x6a\xd2\x4a\x0d\xbd\x52\x5d\x54\xd2\xeb\xb8\xb2\x23\x59\x43\xc6\xbd\x0c\x7d\x83\xd6\x13\x83\xd6\x73\x0f\x9a\x8d\x8f\x32\x5c\xac\x4f\xf4\xc2\x64\x34\xe2\x69\x07\x29\xd2\x34\xc8\x6a\xad\x46\xbe\xb8\xfa\x6b\x4f\x44\x53\x87\x5c\x77\x42\x6e\x14\x82\x5c\xf3\x0e\xbc\x02\xc3\x80\xdc\x2c\x04\xb9\xee\x9b\x9d\x8a\x02\xc3\x80\x5c\x33\x20\x2f\x9e\xc8\x5e\x90\x24\xd7\xa8\x6f\xa6\x53\xa5\x53\xd5\xa7\xf1\x2f\x6c\x4d\x46\x46\x27\x9f\xb0\x9e\xf4\x3a\xcd\xf0\x14\x8d\xe2\x79\x82\xb2\x70\x6a\xce\xfd\x92\x41\x79\x23\x7c\x95\x1d\x93\xd5\xe7\x8f\x1f\xeb\x88\x78\xbb\x1f\x0f\xc3\xd1\x35\xe5\x84\x94\x0e\x0b\x60\xb1\xee\xc7\xa2\x77\x4a\x1d\x07\x7e\x3b\x85\x94\x97\x10\x6d\xc5\xca\x14\xe7\x4a\x92\xfb\x0b\x4a\x71\x36\x9f\xe9\x1f\x72\x3c\x3a\x16\x1f\xf6\xf7\x7e\xa1\xae\x1d\x79\x27\xfc\xbd\x5f\x3e\xd5\xd0\x06\xda\xfb\xc5\x4e\x8d\xa6\x14\xa9\xd3\x22\x75\x67\x34\x63\x75\x49\xc3\x54\xa6\xf3\xfe\x05\x26\xa2\x82\xef\xe8\x5f\xa3\xc1\x8f\xa1\x6d\x1a\xfd\xf8\x2b\xa2\x4f\xbe\xe8\xc7\x6a\x71\x16\xe6\x58\x94\x97\xd7\xa1\xee\x30\xc7\xa2\xd9\x86\x68\xb6\xae\x35\x5b\x5f\xd4\x6c\x5d\x6f\xb6\xbe\x5c\xb3\x10\x46\x27\xac\xf1\x25\x48\x80\x84\x0d\x7d\x05\xfa\xaa\x36\xa1\x6a\x83\x2f\x66\xa8\x5a\xd3\x97\xa9\x67\x46\x18\x59\xe7\xb1\x56\x04\xd4\x5a\xa3\xe7\x7a\x33\xb6\x3f\xfd\x58\xa7\x1f\xeb\xce\x8f\x0d\xfa\xb1\xe1\xfc\xd8\xa4\x1f\x9b\xce\x8f\xad\xbc\x36\xdb\x79\x6d\x76\xf2\xda\x5c\x13\x6d\xe6\x68\xa4\x0a\x71\x1e\xb4\x3c\xf7\x41\xc5\x38\x10\xb2\x95\x14\xaa\x1f\xd1\xbd\x24\x77\xf5\x2a\xaf\x15\xe9\xa3\x10\x67\xd6\x8b\xb8\x7b\xe7\xdf\xde\x61\x70\xa5\x97\x19\x70\x21\xbd\xf4\x31\x0d\x35\xf4\x1b\x10\x21\x2a\xfd\x46\xe6\x9e\xaf\x12\x78\x16\x7b\xef\x2b\xb3\x62\x9d\x56\x6c\xb0\x8a\x6b\x46\xc5\xb6\xb7\x62\x83\x56\x6c\xb1\x8a\x75\xa3\xe2\x9a\xb7\x62\x93\x56\xec\x9c\x09\xd4\xb4\x8a\x75\x59\xf1\x4e\xbb\x58\x5e\x94\x7a\x8a\x08\x8f\x1d\x7f\xcc\x52\xb2\xb3\xe0\xf1\xf0\x78\x9b\xe8\xf1\x1c\x0e\x63\x70\x02\x8e\x2b\x7e\xbc\x13\x5f\xa7\x13\x1e\x52\x72\xf4\x0a\x6f\xba\xe3\x7c\x2f\x3a\x95\xfa\x85\x1d\x8f\xbc\xb9\x95\x1f\xc3\x0b\xfa\xa5\xd3\x5a\x6d\x36\x4c\xb5\x9c\x58\x26\x82\x60\x4b\x05\x5d\xa1\xb4\xf5\xa1\x7d\x51\x44\x50\xc3\xe0\xe7\x38\xb8\xc0\x28\x9e\x0c\xbd\xac\x76\x09\xf9\xa1\xf7\x89\x4e\x6e\xcf\x8c\x77\xa8\xb5\xd8\x0b\x26\x83\xf9\x84\xac\xb0\x08\x5f\x7a\x9b\xed\xb1\x44\x30\x3d\x9a\x08\xa6\x76\xd5\x1a\x36\xe1\xff\xd0\x73\x2e\xa1\x99\xf9\x5a\x7a\x2c\x2f\x4c\x8f\xe6\x85\xa9\x5d\xb1\x1a\x4d\x88\x29\xdf\xe3\x02\x6a\xad\x8c\x5e\xa3\x52\xef\x93\xf2\xfc\x1f\xa8\x8e\xba\xa8\x56\xb6\x21\x36\x18\xc4\x06\x85\xc8\x00\xb6\x18\xc4\xba\x01\xb1\x5e\x00\x62\x93\x41\x6c\x5a\xdd\x2a\xd1\x76\x34\x88\x8d\x02\x10\x5b\x0c\x62\xcb\xd9\xeb\xa6\x01\xb1\x59\x00\x62\x9b\x41\x6c\x3b\x7b\xdd\x32\x20\xb6\x0a\x40\xec\x30\x88\x1d\x67\xaf\xdb\x06\xc4\x76\x01\x88\x6b\x0c\xe2\x9a\xb3\xd7\x1d\x03\x62\x67\x21\x44\x29\xf6\x53\xa0\x5a\xf5\x35\xb3\xba\xe9\x1d\x23\x68\x9a\xec\x3e\xe7\x2f\xee\xb0\x88\x48\xa9\xf3\x2b\xe0\xd5\x21\xe9\x5a\xcf\x91\x84\x83\xa7\xcb\x4f\xe6\x83\x0c\x8d\xc3\xf3\x31\x0a\xa2\x21\x9a\xc4\x97\x28\x48\xce\xe7\x10\xfe\x05\xdc\x9c\xff\x7b\x1e\x24\x56\xe2\x1e\x68\x20\x40\x1b\xa4\x15\x2e\xc5\x39\x94\x07\xe7\x7d\x5a\x84\xee\x12\xce\xe3\x13\xef\xb3\x86\x41\x82\xd3\xf9\x24\x43\xf1\x28\xaf\xf9\x31\xdd\x02\x4a\xe7\x01\xfa\x09\x9d\x07\xd4\x75\xa5\xbe\x56\x46\xcf\x11\x7d\xd5\x67\xaf\xda\xf0\xaa\x0f\xaf\x5c\x48\x4e\x28\x20\xa5\x2b\xf4\x48\xf8\x13\x3a\xbf\x82\x19\x2e\x03\x41\xf0\x02\x42\xec\x54\x0a\xb8\x12\xc1\x90\x0e\xfd\x76\x70\x84\x20\x9c\xa4\xfa\xf1\x2d\xe5\x70\xe7\x63\xf4\x3b\x3a\x9f\x14\x65\x72\x6e\xa5\xca\x6f\x8c\xc5\xbd\xa5\x2c\xae\x54\x7a\x2b\xb7\x6f\xb2\x93\xbd\x55\xc4\x82\x32\x2b\xd0\xd1\x0b\x74\x64\x01\x93\x9e\x7f\x63\xdc\xf0\x2d\xe5\x86\x25\xda\x8c\xdc\x6f\xdf\x72\xfe\x07\xfb\xed\x73\x44\x5a\xb3\x61\x34\x18\x8c\x06\x87\x51\xd7\x11\xa8\x5b\x18\xd6\xf4\x02\xb5\x3c\x0c\x9b\x0c\x7a\x93\x43\x6f\xe8\x18\x36\x0c\x0c\xeb\x0e\x0c\x5b\x0c\x46\x8b\xc3\x68\xea\x08\x34\x2d\x0c\x1b\x7a\x81\x46\x1e\x86\x6d\x06\xbd\xcd\xa1\xb7\x74\x0c\x5b\x06\x86\x4d\x07\x86\x1d\x06\xa3\xc3\x61\xb4\x75\x04\xda\x16\x86\x2d\xbd\x40\x2b\x0f\xc3\x35\x06\x7d\xed\x4c\x23\x11\x81\x61\xc7\xc0\xb0\xad\x61\x58\x28\xf1\x47\xca\x93\x4e\x08\x5d\x6b\x81\xb4\x13\x8b\xae\xbb\x28\xac\x0c\x5f\x65\xea\xbd\x93\xaa\x49\xe5\xa1\x14\xb4\x34\x0e\xf4\xb6\xc8\xbe\xbf\x9a\x4d\x02\x82\xcd\x55\x86\xbc\xe0\x58\x9c\x99\x92\x6c\xd9\x05\x51\x5c\x5c\xe5\x29\x75\xf5\xe4\x1d\x6a\xc9\x72\xde\x1d\x94\x5a\xb0\xb0\x31\x72\x45\xbf\x1b\xe9\xb6\x5b\x15\x79\x29\xd2\x6d\x77\x2a\xec\xae\xa4\xdb\xa9\xdf\x9c\x55\xd6\xfe\xde\x91\x08\x1f\xef\xab\x1e\xef\xab\x1e\xec\xbe\xca\x58\xe2\xf2\x3e\xc7\xbc\xc9\xf9\x7b\xdd\xe1\xdc\x57\x56\xb8\x77\xe2\x68\xfe\x4e\x3f\x9a\xbf\xbb\xed\xd1\xfc\x9d\x7e\x34\x7f\x97\x77\x34\x5f\xa4\x60\x7e\xbc\xa9\x7a\xbc\xa9\x7a\xbc\xa9\xd2\xbe\x3c\xde\x54\x3d\xde\x54\x3d\xde\x54\xc9\x66\x1f\x6f\xaa\xcc\x8f\x8f\x37\x55\x9e\xc7\xc7\x9b\xaa\xc7\x9b\xaa\xc7\x9b\x2a\xf8\x7b\xbc\xa9\x2a\xa6\xc4\x7d\xbc\xa9\x7a\xbc\xa9\x7a\xbc\xa9\x52\xfe\x1e\x6f\xaa\x1e\x6f\xaa\x1e\x6f\xaa\x1e\x6f\xaa\xfe\x27\xdf\x54\xdd\xdb\x1d\xd5\xed\x6e\xa7\x8a\xdc\x4b\x15\xb8\x91\x7a\xa8\xbb\xa8\xbf\x77\x3e\x94\xc7\xbb\xa8\x7f\xfe\x5d\x94\x7a\x77\xd4\x6b\x2d\x74\x74\x52\x6f\x8e\x7a\x2d\xe5\xda\x08\x1e\x1e\xfe\xce\x88\x7a\x69\x8a\x5b\x23\x77\x50\x01\xee\xa1\x9d\x77\xad\x04\x6e\x9c\xaa\x47\xb1\x12\x33\xdd\xd6\x57\x44\x61\x86\xd2\x7e\x7c\x65\xc3\x39\x16\xe8\x1c\xab\xd7\x74\xfc\xcf\x25\x4d\x36\xda\x1d\xff\xa1\x9c\x1d\xba\xc3\xc5\x6a\xdc\x77\xf8\xda\xa5\xc7\xd5\x5b\xac\x70\xff\xf1\x85\x0d\xb3\x41\x21\x43\xc0\xa3\x4a\x84\xe8\x5f\xea\x38\x79\x54\x87\xac\x12\xd9\xda\xf8\xd8\x9f\x6a\x80\xec\x48\x68\xda\x67\x2b\x28\x9a\xeb\xec\x4f\x7a\x51\xfa\x8c\x9e\xd3\xf1\x79\xce\x1b\x2d\xa3\x7f\x41\xaf\x3c\xb1\x14\x2e\x83\x99\x1b\x67\xd8\x37\x6c\x0d\x81\x32\x01\xc7\x6e\xc7\x78\xf2\x9a\xcc\xf8\xe2\xe9\xe9\x39\x55\xfc\x2c\xab\x86\x20\x9a\xcf\x2c\xcb\xac\x00\x74\x67\xb5\x1c\xd7\x84\x80\x16\xc4\xca\xbf\x4e\xa6\xc7\xad\x32\xd4\x5a\x16\x4e\xce\x8d\x76\xc7\xa3\x10\xa9\x79\x95\x21\xce\x46\x8b\x2a\x46\x94\xf5\x64\x28\x46\xe4\xa0\x85\xc6\x97\xcf\x72\x38\x17\x66\x80\x07\xe5\xa0\x5e\xfd\x8b\x8a\xa7\x31\x1f\x62\x35\x45\x74\x19\x45\x54\xa5\x16\x39\x16\x51\x08\x1a\x74\x9a\x30\x8e\x51\xa5\xf6\x5d\x23\x61\x0f\xe1\x3a\x89\x36\x87\x60\xfd\xc4\x2a\x09\x55\x7f\xaf\x77\xf6\x2b\xa9\x5b\x62\x6b\x8a\x54\x61\x78\x9d\xc9\xbc\x06\x91\x99\xc7\xc0\x38\x3e\x7d\x84\x38\x28\x8e\x1b\x2d\x49\xea\xa1\x75\x76\x27\x63\xa1\xcd\x15\x13\xcb\x34\xec\xbe\x57\xb9\xb7\xd7\xba\x0f\xa1\xb7\xd7\x5a\x5a\xe2\xb5\xf7\x58\x43\xdc\xed\xb5\x9c\xb1\x2d\xe0\x86\x26\xc4\xc3\x5b\xec\xf0\x5b\x49\x3c\xd3\x76\x79\xf6\x02\x06\xe1\x1b\x44\xc5\x1b\x92\xe6\xf4\x40\x73\x86\x9e\x9f\x4c\x3c\x29\x25\x42\xcd\xa1\xfa\xcb\x86\x0a\xd6\x8c\x35\x47\x50\x57\xa2\x7e\x19\xab\x98\x80\xea\xea\x20\xf4\x88\x71\x85\x84\x18\xd2\x06\x2f\x98\x7f\x87\x41\xc6\x33\x67\x03\x17\x86\x2f\x04\x2f\xb2\x8b\xff\x0c\x9b\xf9\x8b\x17\xce\x3d\x7c\x09\x76\x8f\x16\x24\x40\xfa\x8e\x56\x1b\x19\xa2\xfb\x59\x71\x00\x69\xf9\x55\xc7\x68\x3e\x7f\xe5\x91\x42\xf9\x27\xcd\x5e\xeb\xa1\x8e\x99\x77\x4b\xd7\xf7\x2d\xcf\x97\x0f\x76\x0a\xfc\xb6\x41\x9c\x09\xab\xc2\x29\x4e\x2e\xf0\xd3\x27\xa5\x41\x19\x35\x6a\xf5\x06\xea\x5f\xa3\xde\xff\xf7\xff\x0e\x93\x70\x80\xf6\x71\x1a\x85\x93\x2a\xda\x9c\x4c\x50\x12\x9e\x8f\xb3\x14\xb1\xf2\xc3\xea\xd3\xa7\x4f\x8e\xf0\x30\x4c\xb3\x24\xec\xcf\x01\x7e\x10\x0d\x21\x28\x4f\x18\xa1\x34\x9e\x27\x03\x0c\x6f\xfa\x61\x14\x24\xd7\x84\x1d\x4c\xd3\x0a\x8b\xd2\x90\xc0\xbf\xf1\x3c\x43\x53\xe0\xe9\x03\xe0\xac\x15\x14\x24\x18\xcd\x70\x32\x0d\xb3\x0c\x0f\xd1\x2c\x89\x2f\xc2\x21\x1e\xd2\xa0\x13\x64\x9d\x8e\xe2\xc9\x24\xbe\x0c\xa3\x73\x34\x88\xa3\x61\x48\xd7\x30\xa9\x34\xc5\x59\x97\xad\xf8\x17\x48\x47\x2b\x05\xc5\x30\xc5\x67\x10\x0f\x31\x9a\xce\xd3\x8c\x6c\xd4\x41\x18\x01\xd0\xa0\x1f\x5f\x90\x4f\xb3\x6b\xe8\x22\x8a\xe2\x2c\x1c\xe0\x0a\x8d\x2b\x34\x09\x53\xd0\x2c\xab\xed\x45\x43\x03\x99\x61\x98\x0e\x26\x41\x38\xc5\x49\xd5\x87\x43\x18\xa9\x03\xc1\x71\x98\x25\xf1\x70\x3e\xc0\xf7\x8e\x06\x62\x5d\x1b\xc6\x83\xb9\x88\x83\x41\x6a\xac\xc6\x09\x8b\x91\x31\x0d\x32\x9c\x84\xc1\x24\x95\xc3\x0c\x73\x03\xd5\x14\xd4\xc9\x3c\x9f\xec\xee\x1d\xa3\xe3\x83\x9d\x93\x5f\x37\x8f\xb6\xd1\xde\x31\x3a\x3c\x3a\xf8\x65\x6f\x6b\x7b\x0b\xbd\xf9\x37\x3a\xd9\xdd\x46\xbd\x83\xc3\x7f\x1f\xed\xbd\xdd\x3d\x41\xbb\x07\xef\xb7\xb6\x8f\x8e\xd1\xe6\x87\x2d\xd4\x3b\xf8\x70\x72\xb4\xf7\xe6\xe3\xc9\xc1\xd1\x31\x7a\xb6\x79\x8c\xf6\x8e\x9f\xc1\x87\xcd\x0f\xff\x46\xdb\xbf\x1d\x1e\x6d\x1f\x1f\xa3\x83\x23\xb4\xb7\x7f\xf8\x7e\x6f\x7b\x0b\xfd\xba\x79\x74\xb4\xf9\xe1\x64\x6f\xfb\xb8\x82\xf6\x3e\xf4\xde\x7f\xdc\xda\xfb\xf0\xb6\x82\xde\x7c\x3c\x41\x1f\x0e\x4e\xd0\xfb\xbd\xfd\xbd\x93\xed\x2d\x74\x72\x50\x81\x46\xed\x6a\xe8\x60\x07\xed\x6f\x1f\xf5\x76\x37\x3f\x9c\x6c\xbe\xd9\x7b\xbf\x77\xf2\x6f\x68\x6f\x67\xef\xe4\x03\x69\x6b\xe7\xe0\x08\x6d\xa2\xc3\xcd\xa3\x93\xbd\xde\xc7\xf7\x9b\x47\xe8\xf0\xe3\xd1\xe1\xc1\xf1\x36\x22\xdd\xda\xda\x3b\xee\xbd\xdf\xdc\xdb\xdf\xde\xaa\xa2\xbd\x0f\xe8\xc3\x01\xda\xfe\x65\xfb\xc3\x09\x3a\xde\xdd\x7c\xff\xde\xd9\x4b\x82\xbb\xd6\xc7\x37\xdb\xe8\xfd\xde\xe6\x9b\xf7\xdb\xb4\xa5\x0f\xff\x46\x5b\x7b\x47\xdb\xbd\x13\xd2\x1d\xf9\xab\xb7\xb7\xb5\xfd\xe1\x64\xf3\x7d\x05\x1d\x1f\x6e\xf7\xf6\xc8\x8f\xed\xdf\xb6\xf7\x0f\xdf\x6f\x1e\xfd\xbb\xc2\x60\x1e\x6f\xff\xef\x8f\xdb\x1f\x4e\xf6\x36\xdf\xa3\xad\xcd\xfd\xcd\xb7\xdb\xc7\xa8\xb4\x60\x48\x0e\x8f\x0e\x7a\x1f\x8f\xb6\xf7\x09\xce\x07\x3b\xe8\xf8\xe3\x9b\xe3\x93\xbd\x93\x8f\x27\xdb\xe8\xed\xc1\xc1\x16\x0c\xf4\xf1\xf6\xd1\x2f\x7b\xbd\xed\xe3\x57\xe8\xfd\xc1\x31\x8c\xd6\xc7\xe3\xed\x0a\xda\xda\x3c\xd9\x84\x86\x0f\x8f\x0e\x76\xf6\x4e\x8e\x5f\x91\xdf\x6f\x3e\x1e\xef\xc1\xa0\xed\x7d\x38\xd9\x3e\x3a\xfa\x78\x78\xb2\x77\xf0\xa1\x8c\x76\x0f\x7e\xdd\xfe\x65\xfb\x08\xf5\x36\x3f\x1e\x6f\x6f\xc1\xe8\x1e\x7c\x80\xae\x9e\xec\x6e\x1f\x1c\xfd\x9b\x00\x25\x63\x00\x83\x5f\x41\xbf\xee\x6e\x9f\xec\x6e\x1f\x91\x01\x85\x91\xda\x24\x43\x70\x7c\x72\xb4\xd7\x3b\x51\x8b\x1d\x1c\xa1\x93\x83\xa3\x13\xa5\x8f\xe8\xc3\xf6\xdb\xf7\x7b\x6f\xb7\x3f\xf4\xb6\xc9\xd7\x03\x02\xe5\xd7\xbd\xe3\xed\x32\xda\x3c\xda\x3b\x26\x05\xf6\x68\xb3\xbf\x6e\xfe\x1b\x1d\x7c\x84\x2e\x93\x39\xfa\x78\xbc\x4d\x7f\x2a\x14\x5b\x81\x99\x44\x7b\x3b\x68\x73\xeb\x97\x3d\x82\x36\x2b\x7c\x78\x70\x7c\xbc\xc7\xe8\x04\x86\xac\xb7\xcb\x86\xbb\xfa\xf4\xc9\x4f\xab\xba\xce\x6b\x3f\xc8\xc6\xf7\xab\xf7\x2a\x16\x75\x9a\x06\x3e\x16\x45\xe8\x63\x21\xeb\x6c\xb8\xb0\x0b\xa2\x2c\x45\x59\xd0\xe7\x12\x0b\xa9\xf2\xe9\x8f\x89\x33\xd8\xa6\x94\xa3\x6a\x15\x84\xea\x15\x84\x1a\x15\x84\x9a\x15\x84\x5a\x15\x84\xda\x15\x84\x3a\x15\x84\xd6\x2a\x08\xad\x57\x10\x7a\x59\x41\xf5\x5a\x05\xd5\xeb\x15\x54\x6f\x54\x50\xbd\x59\x41\xf5\x56\x05\xd5\xdb\x8a\x85\xe5\x1a\xad\x4b\xbe\x11\x78\xa4\x3c\x81\x51\x6f\x53\xb8\xa4\x1e\xb4\xf5\x92\xc1\x6f\x30\x18\x75\x68\x43\xc2\x69\xb2\xb6\x5a\x0c\x97\x97\x0c\xc6\xba\x82\xe7\x1a\x83\xd5\x61\xb8\xd4\x29\xcc\xba\x1a\x6b\xb9\xce\xea\x72\x5c\x6a\x14\x06\xe0\xc1\xf1\x6c\x52\x58\x04\x7e\x5d\xed\xb7\x0a\xa7\xc5\xea\xb6\x19\xee\x6b\x0c\x46\x43\xc1\xb3\xce\x60\xad\x33\x5c\x58\xbf\xeb\xcd\xb3\xf2\x2b\x75\x2e\x92\x05\x73\xc1\xf1\x58\x53\xc6\xaa\xc1\x60\x72\x9c\x3b\xfa\x78\x40\xdf\x9a\x46\xdf\x3b\xac\x4e\x53\xc2\x82\xba\x6d\x89\x33\x87\xc1\xc7\x03\xda\xaa\x1b\x7d\x87\x42\x6d\xa5\x83\x6b\x0c\xc1\x8e\x1c\x5c\x01\xa4\xa1\x0c\x34\x45\x56\x02\x5a\x67\x75\x94\xc1\x82\x89\x69\xcb\xc1\x15\x30\x9a\xca\x40\x53\x64\x15\x84\x1a\x6c\x64\x6b\x0a\x30\x3e\x1a\x6b\x62\xf6\x04\x85\x22\x36\x3a\x14\x59\x7d\x36\xd2\x45\x2b\x83\xa2\xc8\xc6\x0a\xd0\x53\x5b\xe2\xb4\xd5\x54\xc6\xb3\x23\xbf\x69\x34\xbd\x56\x81\x4f\x30\x54\x9c\x5e\x5f\x4a\xda\xe3\x34\x55\x6f\x2b\xc3\xba\xc6\xca\x6a\xf3\x51\x97\x44\x20\xe6\xe2\x25\x2b\xc8\x89\x67\x5d\x29\xc3\x11\x5f\x83\xdf\xea\x59\x4a\xac\xe5\x96\xac\xca\xdb\x17\x6b\x5e\x5d\x13\xeb\x1a\x48\x09\x8a\xaf\xcf\xb6\xa4\x7d\xd1\xcf\x86\x44\x41\x8c\x13\x23\x19\x0a\x17\x19\x53\xb2\x68\x81\x30\xc4\xb4\xc1\x6f\x4b\x04\xa0\x9f\x6b\x72\x21\x42\x83\x2d\x86\x48\xc7\x40\xba\xa9\x0f\xbe\xe8\x74\x5d\xc2\x11\x63\x27\x16\x34\x7c\xd7\xe0\x08\x06\x52\x57\x06\xa9\x23\xdb\x15\x0b\x8f\x2d\xe0\x7a\xd3\x31\x1f\xa2\x03\x06\xe2\x1c\x90\x58\x70\x0d\xe5\xdf\xb6\x58\xc5\xfa\x00\xb5\x1d\xe5\x5a\xfa\xcc\x88\x99\x94\x9d\x42\xf5\x3a\x3a\xd3\xb2\x64\x7f\x1a\x93\x15\xe2\x98\x0f\x24\x42\x35\xd7\x2a\xa8\x76\xd5\xde\x5c\x6f\xac\xbd\x7c\xf9\x92\xfc\xee\x6c\x6f\xbd\xdc\x7e\xb3\x59\x27\xbf\xd7\x77\xea\x6f\xde\xf4\xb6\x7a\xe4\xf7\xe6\xcb\x76\x73\x67\xab\xb5\xad\xcf\xf7\x38\xf1\x36\xd0\xae\x6d\x36\xd6\xdf\x6c\x77\xa0\x81\x5e\x6b\x6b\xab\xde\x68\x41\x03\x5b\x6b\xb5\xe6\xf6\x4e\x93\xfc\x5e\xdb\xec\x6c\xad\x75\xb6\xa1\x61\x8e\xd0\x99\x53\x1f\x70\xb4\x77\xb8\xbd\xbf\x55\xef\xd4\x20\xfc\xfe\x02\x1d\x92\x28\x2b\xb5\x48\xca\x2b\xba\x2b\xdf\xf6\xae\x88\x2a\x13\x01\x09\x4f\x10\xec\xce\x5a\xab\xdd\x68\xd6\x60\x04\xb7\x77\x7a\x5b\x9b\x6f\xd6\xa1\x83\x2f\xd7\xdf\x6c\x6e\xf5\x76\xb6\xc9\xef\x7a\xad\xd9\x68\xb7\xd6\x60\x70\x7a\xcd\xad\xc6\x76\x7d\xa7\x76\xe6\x55\x8d\x17\x55\xca\x3b\x15\xbb\x85\xbd\x94\xea\x39\x37\x35\x8b\xcd\xf1\x29\x16\xa0\x7b\x95\x66\x91\x9e\xeb\x9b\xfd\x4f\x4a\x69\x7e\x79\xf0\xc9\x36\x64\x42\x79\x77\x2a\x4a\x3d\xb4\x81\x4a\x76\x01\x44\x0d\x40\x95\xc6\xa4\xe1\x83\xf2\x72\x39\xa3\x52\x0b\x20\xb3\x2b\x35\x00\xda\xd6\xa5\x36\xb8\x1c\xd5\x18\x5a\x64\xeb\xbc\x8b\xc4\xfd\x03\x21\x45\xef\x95\x23\x30\x80\x4f\xe3\x89\xbf\x40\x02\x05\x12\x6f\x01\x10\x3f\x3f\xfd\xe1\x87\x00\x32\xd1\xa7\x3f\xfc\x10\x60\x9b\xfe\x94\xfa\x21\xc0\xa6\xf1\x29\x4d\xdc\x11\xad\x57\x57\xc9\x2a\xfb\xff\xd9\x7b\xfb\x2d\xa9\x6d\x6c\x51\xfc\xef\xf0\x14\x9a\xf9\xad\x81\x6a\xba\xe8\xb6\xe4\x2f\x19\xe8\xfc\x2e\x21\x70\x3a\x37\x10\x58\xc0\xdc\x70\x16\x0b\x32\xb2\x2d\x77\x39\x54\x57\xf5\xa9\x72\xd3\xd5\x49\xc8\xba\xaf\x71\x5f\xef\x3e\xc9\x5d\xda\x92\x6d\xd9\x96\xe4\xaa\xa6\xc9\x99\xcc\xd0\xb3\x86\x54\x95\xa4\xbd\xb7\xf6\x97\xb6\xbe\xb6\xde\x8b\x49\xf3\x07\xb6\x2a\x45\x74\x6c\xd8\xa4\x65\xf3\x29\x4a\xe7\x53\x94\xcd\xa7\x28\x9f\x4f\x11\x9f\x1b\x10\xb1\xd5\x14\xa5\xab\x29\xca\x56\x53\x94\xaf\xa6\x88\xaf\xfa\xc8\x98\x20\x85\x09\x82\x8f\x87\x57\x46\xd2\x15\x24\x1d\x87\x42\xdc\x2f\xcc\x44\x61\x26\x0b\x49\xbf\x30\x17\x85\xb9\x2c\xf4\xfb\x85\x30\x61\xe0\xb2\x30\xe8\x17\x36\xcf\x54\xb3\xee\xbb\xd4\x75\x97\xfa\xbb\x82\xc6\xa3\x84\xf0\xdf\xfd\x23\x84\x8d\xb6\x5d\x09\xf3\x61\x73\xb4\xdf\xda\xd4\xfe\x2f\xf3\x37\xe5\xdb\xb7\x7b\xbf\x99\x2e\x31\xc0\xad\x9d\xfb\x38\xda\xfb\xf5\xc6\x57\x5d\xd7\x28\x70\xa0\x02\x4f\xd2\xf9\x34\x9b\x4f\xf3\xf9\x1e\xda\x47\xb3\xb9\xf9\xee\xcd\x47\xd4\x2c\xc8\x95\xf7\x7d\x22\x97\xda\x0c\xd0\x48\x1f\xda\x80\xf3\x03\x68\x01\xb5\x42\xf3\xfb\xd0\x06\xa2\x1a\x40\x8b\x02\x2b\xb4\xa0\x0f\x6d\x20\x5b\x0d\xda\xaf\x87\x87\x0a\x22\xf5\xac\x10\xc3\x3e\xc4\x81\x42\x20\x73\x9a\x74\x21\xc4\xca\x28\x2e\x51\x82\x56\xcb\x6a\x3e\xa9\xa6\x6b\x21\x56\xd3\xa5\x0d\xd0\x81\x6a\x9f\xcf\xcd\x22\x07\x8b\x18\x98\x94\xf8\x03\xbd\xcd\x4d\x25\xa0\xee\x80\x57\xd8\x24\x36\x5e\x03\x02\x7b\x49\x4d\xad\xc1\xcc\x06\x3b\x89\x0d\xa9\x6c\x85\xf6\x35\x6d\x5d\x5d\x5d\x5b\xc3\x49\xba\x9a\x66\xab\x69\xbe\x02\x8e\xaf\x3e\x4d\x5b\x83\x3e\xb4\x4f\xd5\xd6\x2e\xb4\x4f\xd2\x56\xd2\x87\xf6\xc9\xda\x8a\xfb\x10\xaf\x59\x5b\x57\xb0\x6b\xed\x50\xd7\x95\x45\x5d\xc1\xa3\xae\x4c\xea\x0a\x8e\xd8\x54\x02\x2e\x5a\xaa\xeb\xca\xaa\xae\x30\x00\x98\x5a\xc3\xd0\x30\x3c\xa1\xd1\x77\xe5\xdf\xe9\xcf\x31\x40\x0c\x09\xa7\x7e\x7b\x11\xa6\xf8\xe7\x08\x4d\x8e\xe5\xd1\xdc\x4c\x78\xe6\xdc\xd0\xd3\x63\x75\x84\xf7\x58\x1e\xbf\xcd\x45\x3d\x13\x47\x8e\xd5\x31\xdd\x63\x79\x90\x96\x8b\x7a\xcc\x58\xcf\x57\xf5\xe0\xb0\x2c\x8c\x08\xa9\xb1\x5e\xa0\xea\xc1\xc1\xe4\x54\xd4\xcb\x8c\xf5\xe0\x00\x73\x87\x2d\xfd\xb0\xf6\xb1\x7a\x5a\xe3\x13\x8e\x67\xe5\xac\x62\x4d\x30\x24\xbe\x18\x06\xfe\xf1\x67\x18\xeb\x9a\x8b\x6f\xca\x6a\xfd\x6a\x59\x81\xc7\x93\x30\x17\xdf\xb2\x8a\xc9\x53\x5b\xb7\x11\x35\x40\x87\x36\x4f\x78\x51\x0d\x1e\x6d\x84\xfa\x83\xce\x3c\xc8\xf3\xe1\x2b\xc4\x48\xbd\xb7\x28\x0f\x33\xb5\x20\x45\x34\x19\xbe\x45\xbf\x1d\xc9\x87\x85\xdb\x33\x12\x4d\x8d\xbf\x21\x9f\xf4\xb5\xb5\x85\x34\x99\x4c\xda\xaa\xfb\x48\xf8\x07\x01\x32\xd9\x13\xa0\x02\x61\xb7\x38\xb0\x04\xd0\x75\x53\xc9\x8e\x36\x78\xd6\x7e\xdc\x3e\x78\x1e\x00\x53\x81\x73\x0f\xd8\x58\xe0\x6c\xea\xa8\xfe\x4e\x47\xfb\x1e\x66\xfd\xc6\x0e\x1c\x8e\x31\x3c\xdb\x71\x78\x08\x33\x41\x04\xaf\xbb\xc8\x0b\x59\xc6\x83\x53\x67\x72\xe6\x35\x7c\xcd\xc5\xad\x96\x60\xdd\x7a\x8c\x6e\x50\x9c\x63\x74\x84\xf4\xf0\xfd\xd3\xe6\x6f\xe1\x56\xd3\x37\xf3\x8c\xec\x18\xa6\x62\xc7\x86\xcb\x24\xc8\x35\x07\x3b\x6e\xae\xeb\x1d\x77\xa6\x57\xc7\x3b\xcf\xab\xa4\x86\x1c\x77\xe6\x54\xc7\xd6\xc9\xd4\xf8\x51\xb8\x17\x72\x27\x5c\x0a\x57\xbd\x60\x91\x03\xb3\xbb\x55\xd5\x8e\x79\x4f\x40\x1d\x37\x95\xcd\x97\x0b\xb7\x83\x82\xa3\x04\xa2\x56\xbb\xba\x00\x5f\xed\xc7\x20\x64\xf1\x4f\x03\x25\x91\xed\x86\xba\xa6\xc8\x84\xd2\xce\xb9\x28\xf8\xf8\x51\xee\xfe\x23\xfd\x44\x5c\x81\x27\x9b\x29\xba\x9c\xa2\x5f\x4c\xcf\x7c\x4c\x26\x1b\xb8\xd9\x79\x09\xff\xfe\xd2\xbe\xd6\xfe\x71\x00\x87\xb8\xe1\x4c\x36\x7b\x37\x27\x97\x7b\xf2\x3a\xf9\xef\xe2\xcb\x2f\x7b\x7b\x7b\xf7\x6c\xd0\xfc\x51\x68\x02\xd0\xef\x02\x62\x4b\x9a\x05\x56\x30\x0e\xeb\x26\x40\x00\xda\x2e\xf7\x6e\x4e\x7e\x07\xe2\xec\x10\xc3\x6d\x78\x26\x98\xf6\x5b\x0b\xca\x02\x0b\x42\x89\xcd\x74\x61\x84\xb4\xb9\x7f\x7f\x01\x54\x6d\xbe\xfe\xfa\xeb\x89\x4f\xee\x2c\x74\xa2\xe4\x07\xe7\x69\x98\xfa\x30\x8c\x7c\x07\x6e\xbb\xc3\x30\xd6\xd7\x7e\xd4\xf9\x16\x38\xf3\x54\x7f\xae\x96\xd2\x33\x0d\xc1\x58\xde\xe7\xb1\xd4\xbe\xea\xc3\x3c\xca\x32\xda\x93\x2c\xf5\x02\xde\xe4\x96\x22\xf1\x96\xe1\x14\x8e\xbd\xd5\x45\x4d\xad\xe9\xb8\xcd\x70\x71\xb0\x77\xd4\xa6\xae\xb0\xdd\x51\xa5\x5a\x38\xc7\x4f\x1f\x3c\xfc\x03\x44\xe3\x68\xfe\x9e\x5f\x42\xd3\x35\xcf\x56\xbc\xb2\xbc\x9d\x64\x11\x28\x3c\x39\x78\x8d\x02\x95\x0f\x19\x36\xa2\x39\x3e\x65\x59\x2b\x1e\xfd\x88\x95\x41\x42\x9d\xca\x43\x29\x9d\xb2\xcc\x20\xa9\xaf\x3e\xca\x7d\x60\xcb\xd1\xa8\xba\xa6\xf9\x75\xa2\x8f\x6f\xa7\x71\xfc\xe5\x88\xd3\xbf\xc2\x95\x95\xcf\xbd\x75\xdf\x4b\xac\xa6\x21\xb6\xa6\x4c\x7b\x79\xfc\xe0\x0e\xde\x62\x27\x63\xf8\x56\xf5\x75\xee\x5f\x1c\xc1\xed\xd3\x76\x0b\xa3\x5c\x94\xd5\xc4\x90\x80\xaa\xbb\xa5\xc1\x8b\x2c\x67\x29\x4d\x0c\xb9\x99\xbc\x4d\x42\x53\x96\x67\x05\xef\xec\x71\x98\x2a\x66\x7e\x4e\x38\x2e\xbc\x6e\xd9\xa7\x6f\x81\xd8\x22\x74\x73\xf0\x3d\x5c\x41\x1f\x00\xd8\x66\xed\xd9\xbc\x5c\x2c\x8a\x52\xf3\x62\x31\x04\x8c\xe6\xa5\x62\x98\xae\x9a\x17\x8a\x45\x11\x6f\x96\x89\x07\x94\x5a\xd7\x89\xad\x6b\xc2\x96\xd9\x02\xac\xfb\x20\x79\xc3\xd4\x92\x0b\xe6\x47\x19\xf8\x77\x53\x60\x74\xef\x9e\xd6\x7f\xf5\x82\x92\x19\x50\x7d\xcf\xe1\xc7\x37\x25\xba\x83\xfc\xb7\xe8\x9d\xfa\x48\xdb\x8f\x38\xd0\x3e\x47\xb6\xb7\x23\x15\x49\x93\x05\x5c\x8e\x95\x73\x4b\x98\x3e\xf8\xd8\x9c\xa6\xc6\x3c\x13\x82\xa5\xa5\x09\x13\x40\x42\x00\xc2\xe4\x4c\x26\x86\x0b\xb2\x1c\xed\x03\x22\xdb\x42\x23\xba\x8f\x88\x67\xe5\x1a\x2c\x9b\x4d\x26\x29\xba\x89\x32\x19\xe7\x8a\x8f\x39\x40\xf6\x36\x21\x93\xbb\xb0\x23\x4b\x7c\xe8\x3e\x0a\xc6\x50\xa4\xe8\x1d\xca\xd0\x3b\x94\x4b\xc8\x11\xcf\x13\x9e\x32\x53\xd2\xa1\x1e\xe4\x68\x07\xe2\x25\xed\xe2\x53\xa6\x7a\x71\x07\x79\x9b\xd8\xe3\x41\xe0\x93\xc0\x8e\xeb\xf0\x76\x83\x8e\x7a\x7b\xe8\xf6\xe1\xd6\x7d\x11\xf0\xfd\x30\xc9\x7d\x4e\xfa\xab\x3c\xc8\x22\x52\x61\x2f\xb9\x69\xb9\x0f\x1d\xa1\xcc\xb4\xc4\x87\x00\xe5\xfd\xfb\xc8\xf7\x54\x2f\x41\xfc\xc6\xb7\x45\xd1\x11\x32\xd1\xc1\xb6\xbb\xad\xb5\xd5\x62\xa0\x5a\x44\xab\x17\xdb\x58\xff\x86\x37\xea\x2c\x04\xc2\x82\xe1\x20\xf3\x09\xea\x2c\x02\xc2\x62\x61\x66\xae\xe3\xeb\x0b\x85\xb9\xb9\x4e\xa0\x2f\x12\xf2\x7e\x9d\x2f\x0b\x7c\xff\xac\x0b\x7c\x22\x16\x3e\x28\xe6\xcb\xe5\x4a\x5f\x73\x3b\x84\x81\x5a\xfd\x7d\x12\x12\xc8\x85\xd0\x42\x1e\x59\xa7\x1b\x2c\xd3\x7d\xa6\x15\xba\x1d\xd7\x81\x8c\xcb\x75\x7f\xc6\xd5\xa0\x2f\x4b\x08\x83\xc5\x00\x11\x3e\xef\xb4\x7a\x00\x0d\x5c\x0b\x07\xdd\x80\xbc\xbb\x66\x20\xca\xbe\x2c\x17\x5c\xeb\x72\x01\xc8\x63\x8b\x95\x02\xb3\x58\xda\x45\x02\x25\x1a\xfb\xb5\x29\x51\xc1\xbe\x2c\x40\xff\xd4\x09\x36\xd6\x33\x46\xc2\xe8\x73\xe7\xc6\x50\x58\xfe\x7d\x96\x0f\x06\xcb\x03\xfa\x1c\x9e\x84\x51\x67\x16\xaf\xdd\xc2\xee\xaf\x0a\x10\x12\x6c\xb7\x2e\x20\x2a\x76\x60\xc2\x77\x09\xfc\x0f\x5d\x1b\xc8\xb0\x17\x26\x3c\xa7\x62\xca\xef\x47\x71\x96\x87\x5e\x0c\x9f\xbd\xd8\xcb\x73\x0c\x9f\x8b\xd8\xe3\x61\xe2\x9b\xd7\x0c\x8a\x22\xf3\xbc\xd4\x87\xc5\x85\x88\x86\x14\x87\x58\x7e\x0e\x8a\x84\x16\x0c\x00\xa4\xbc\x60\x41\xc1\x82\x1d\x96\x0b\xb6\x8a\x3c\x35\xb7\xaf\x58\xa7\xb5\x74\xdc\xa2\x05\x8f\xda\x84\x33\x77\x8e\x86\xc1\x8b\x65\x63\xe9\xcb\x10\x3d\x32\xe2\x12\x12\xec\x3a\x48\x8b\x26\x23\xc3\x74\xc7\x3a\x06\x03\x35\x21\xe6\x4b\xec\x5f\x86\xea\x4f\x18\xaa\x85\x54\xb6\x1b\xac\x8d\xc2\xe9\x0c\xd7\x52\x40\xce\x01\x9b\x90\xfe\x55\x67\xed\x5e\xb3\x1a\x8e\xee\xc6\x89\x18\xc0\x93\x2f\xeb\xfa\xff\x3d\x03\xf3\x9f\xef\x5a\xde\x77\xf2\x11\x87\xf2\x97\xe6\x56\x2e\x5a\x2d\xcf\x17\x39\xca\xba\xf7\xf5\xb4\x1e\x1c\xf7\x9f\x4e\xf9\xbe\xbb\x0d\x50\x2f\xd4\xf2\x16\x86\x2c\x31\x45\x30\x48\xdf\x52\x2e\xd7\xcf\x57\xe5\x29\x9f\x2c\x8c\xc3\xd8\xfa\xbf\x56\xd5\x0f\xf5\x3c\x5f\x7c\x99\x2c\xfa\xf3\xcc\x66\x21\x58\x8a\x13\x1d\x21\x72\xaf\xfe\x7c\xff\x48\x42\xa8\x7f\x70\xac\x0d\xff\x65\xb2\x40\x7f\x53\xd5\xf6\xac\xeb\x85\xca\x46\x0b\x36\x5f\xf3\xf1\x53\x81\xfd\xf5\xb1\x7a\x3e\xbe\x3a\xef\xce\x70\x0d\x6c\x39\xe1\xd5\xe3\x15\x83\xcf\x6c\xfe\x4d\x59\xad\x0d\x0c\x6a\xb6\xf0\x17\xe8\x0e\x9a\x2c\x20\xb3\xe7\x1e\xba\xdd\x59\xfc\xe8\xaf\x64\x69\xb8\xea\x55\x6a\x3d\x33\x3b\xfc\x06\x02\xe9\xe5\xef\xb9\x98\x95\x73\x8e\x26\xaa\xec\x3e\x52\x47\x32\xfb\x5c\x6c\xa5\x69\x65\x74\x03\x82\x5a\xb9\x7c\xfc\x46\x56\x82\xb4\xa3\x03\x46\x80\x2e\x9c\x2d\x2f\x26\x8b\x29\xc2\xe8\x10\x91\xbd\x2d\x32\xb6\x23\x78\x09\x65\x17\xb0\xfe\x9e\x31\x79\xb6\x04\xb1\xbf\x3f\xb2\x14\xba\xe8\xd4\xa8\x23\xa4\x49\x0b\xf3\xea\x7b\x6c\x22\xf0\xde\x2e\x9a\x1e\x46\xe8\x9f\x7d\xa7\xed\xf8\x60\x3d\x2f\x33\x3e\xf1\xf6\xbe\xec\x7a\x6d\xbd\xeb\x35\x28\x2a\xa0\x28\x34\x15\x9d\x40\xd1\x60\xc3\x08\x62\x16\x28\x8a\x3f\x79\x1b\x2d\x72\xe4\xba\xff\xa3\xb7\xd1\x4e\xd8\xe9\x29\xf3\x36\xcd\x66\x1a\x1e\x30\x65\x58\x1b\x0e\x1a\x4f\xea\x96\xf7\xef\x23\x22\x37\xbd\xea\x5f\xbe\xfe\xfa\x6b\x14\xef\xed\x21\xf4\xce\x0c\xa9\xfb\xd7\x81\x84\x83\x01\x24\x4c\xf7\xf6\xb6\x83\xd4\x6d\xe7\x1b\xdd\x4b\xa7\x27\xb8\xed\xb7\xf1\x90\x7c\xb7\xb2\xd6\x6d\x2c\x89\xd5\xba\x8d\x37\x75\xbe\xe9\x2d\x89\xed\x42\xf2\x87\x90\x92\x1d\xbb\x5d\xb7\x33\xbf\x49\x80\x5a\xc5\x51\x42\xdc\x57\x3d\x87\x24\xbf\xaa\x87\xfb\xce\x0d\x53\xdb\xee\x67\x06\xb7\x1a\x27\x1c\xdd\x44\x05\x1c\x76\xfb\x5d\x7c\x3c\xb1\x3d\xe1\x72\xca\x20\xc3\x1c\x43\x37\x51\x0a\xd5\x99\xdc\x1d\x7c\x87\xd4\x3e\xa1\x89\x7e\x08\x56\xca\x13\x41\x78\xb3\xd5\xaa\x36\xdb\xd4\x5e\xab\x3c\xfa\x27\x4b\x70\xa2\x95\x60\xbf\x53\xd4\x69\x64\x1e\xdb\x1a\x64\xf0\x4e\xcd\x84\x83\x8e\xcb\xcc\xc9\x1c\xda\x45\x0a\xa2\x2c\xc1\x5a\x09\xc6\x7a\x51\x2c\x4f\xb6\xca\x22\x12\x9a\x47\x3c\xd8\x40\x16\x98\x66\x68\xbf\x46\xbb\x2f\x98\xba\x2f\x1f\x7a\xb3\x6e\x1e\x43\x43\x82\x8e\x6a\xc6\xec\x0b\xd6\x9a\x30\x08\xc7\x75\x62\x00\x20\x7c\x5d\x3f\x4f\xbb\xf8\x13\xee\xd1\x14\x7e\x41\xee\x4c\x78\x2d\x01\x9b\xb6\xf9\xd0\xc8\x16\x69\x3f\xdb\x3a\x1a\xd9\x0e\x9d\x54\x82\x11\x15\x31\xe1\xfa\x77\xd9\x1a\x95\x75\x42\x55\x07\x52\x86\x17\xe6\x3a\x91\xaa\x03\x29\xc1\x4f\xcc\x75\x62\x55\x07\x6c\x7e\xf6\x65\x1b\xf6\xcb\x36\xec\x97\x6d\xd8\x61\xb4\xf9\x65\x1b\xf6\x9f\x72\x8d\x37\x8c\x76\x5e\xe3\x0d\xa3\xd1\x35\x5e\x7d\xce\x36\x5c\xe3\x0d\xa3\x2f\x6b\xbc\xd7\xbe\xc6\x1b\x46\xdb\xae\xf1\x9a\x84\xd3\x5d\xe3\x05\x01\xb9\x0f\x6d\x37\x7b\x67\xe6\xad\x59\xea\xfd\xa9\xb7\x66\x37\x51\xf0\x87\x3c\x5c\xd0\xe0\xf9\xb2\x0a\xdc\x5d\x05\xde\x44\xb0\xa7\x7a\xb0\x89\x02\xed\xf7\xd7\x51\xa0\xb2\x74\x43\x8d\x03\x2d\x4f\xf4\x4e\x39\xdd\xb4\xfe\xbd\x38\x7e\xf6\xd3\xb3\xc7\x8f\x5f\x3e\x7a\xf5\xb2\xbf\x5a\xfc\xfc\xbb\x9f\xbe\xfb\xe1\xdb\x47\xaf\x1f\x0d\x5f\xe5\x7e\xf1\xec\xef\x3f\x7c\xfb\xd3\xc3\x67\x3f\xbc\x7c\xf5\xe0\x87\xa6\xa5\x86\x4e\x2e\x2b\x3f\xdc\x6e\x59\x59\x6b\xb1\x9a\x2d\xeb\xa4\x2d\xbd\x35\xe9\x1a\xb5\x98\x5d\xe3\x29\xba\xb4\xa5\x2a\xaf\xe4\x92\x48\x85\xee\x23\x12\xdc\x43\x95\x61\x49\x44\xeb\xf3\x9b\x0d\xda\x47\x21\xba\x8d\x2e\xe5\xed\xc1\xaa\xbe\xa4\x09\x9f\xc8\x1e\xac\x54\xa2\xbf\xa1\x68\x10\x8b\x40\x18\xc8\x2f\x5e\xa3\x23\x74\x89\xfe\x86\x42\x53\x94\xc8\x2f\xfe\x53\x40\x25\xe8\x36\x12\x78\x7c\x81\x67\xcf\x50\x79\x23\x97\xe5\x5e\xf7\x7e\xbe\x94\x3f\xff\xa7\x65\x29\x58\x63\xdb\x59\x89\x4a\x78\x4e\xc0\xc0\xb4\x86\x33\x1b\xc9\x99\x8d\xbc\xa0\xb9\x31\x30\xa6\xa9\x2a\xb9\x8b\x2e\x65\xd5\x4b\xcb\xb2\x52\xab\x20\x5d\x36\x5e\xc2\x03\x3f\xc3\x5e\x0b\xbe\xf6\xbb\xfe\x71\xb4\x6f\xbd\x5d\x8e\xae\x36\x3c\x79\xfc\xf2\x85\xa0\x75\xe3\x61\x93\x32\xe8\xef\x4e\x58\xd6\xc7\x44\x35\x40\x51\x2b\xeb\xd3\xf5\x45\x4f\xb7\x8c\xd5\x9e\xd4\xd5\x2c\x2c\x54\x2f\x4f\xfc\x8c\xee\xa3\xf8\x1e\xfa\xd9\xb1\x32\x07\x7d\x80\xab\xa9\xe6\xac\x28\x35\xfa\xb4\xac\x9e\x2f\xd7\x90\xc7\x55\x68\x15\x3c\x96\xfb\xf3\x1e\xba\x83\x4c\xa7\xa9\x6b\xe0\x7a\xa3\xfb\x48\xe5\x8b\x30\x55\x16\x7f\x83\x0e\xbe\x3b\x42\x80\x46\x83\x62\xc1\xd5\x3d\x51\xad\x63\xfd\xfa\x08\xd0\xda\x0f\x57\x0f\x30\x3f\xd5\x30\x77\x40\xdd\x31\xcc\x7b\x1a\x02\xb6\x5b\x5a\xd2\x14\x6b\xc1\x37\x15\x28\xd0\x88\x58\xa8\xfd\x24\xfa\xe1\x21\x7a\xbe\x2a\x4f\xcb\xaa\xfc\xc0\xd1\xd9\x72\x7e\xb9\x58\x9e\x96\x6c\x8e\x96\x1f\xf8\x0a\xfd\xc7\xe3\x09\xd9\xbb\x8b\x36\xef\x28\xda\x47\x9b\x77\x11\xfc\x1b\xc2\xbf\x81\x70\x33\x66\x90\x4a\xa3\x25\x7a\x79\x7f\xe0\x1d\xf2\x36\xb1\xe3\xc8\xbc\x85\x38\x05\xe1\xc8\xa8\x1f\x23\x9b\x5e\x3d\x07\x2f\xd7\xf8\xd4\xf0\x53\x27\x18\xeb\xcb\x6c\x3a\xd0\x9f\xbd\x5d\x77\x53\xd6\x60\x3f\x15\x3f\x3d\x5b\xae\xd8\xea\xb2\xf3\x12\x9d\x30\x81\x57\xfa\x40\x64\xdd\xa5\x34\xbe\x3a\x63\xb6\xfe\x57\xc6\x9e\x8d\xd1\xdd\xdb\xdb\xf1\xb7\xdb\xd9\xf1\x3b\xfb\x3a\xbe\x6b\x57\xe7\xfa\x9f\x12\x58\x9e\x57\x67\xe7\xd5\x13\x98\x5a\x77\xea\x22\x08\xd2\x73\xbe\x2e\x57\x3c\xd7\x1e\x1a\x48\xcb\x6a\x5d\x27\x84\x96\x8d\x3b\xb3\x85\xba\xf1\xb3\xc5\xbc\x16\x93\x96\x83\x9b\xad\xf8\x5d\x44\x48\x30\x45\x24\x8c\xa6\xc8\xa7\xc1\x14\x85\x98\xf4\x1b\xab\x37\x0b\xee\x8a\x32\xbd\xa8\xff\x68\x41\x3d\x69\xb6\xbe\x5b\xa0\xf7\xae\x07\xed\x0a\xef\x17\xc0\x4a\x2d\xbc\x84\x58\xcf\xbd\xeb\x6f\x6f\xde\x5a\xbc\xfd\x16\xaa\x26\xfe\x00\x8e\x54\xb9\x05\xbf\x68\xd4\x0e\x36\xe1\xc6\x52\x09\x00\x25\xcd\x6b\xbd\x30\x02\x44\x9e\x87\xee\x20\x31\xd0\x36\x2f\x25\xe8\x9c\x10\xd1\x8b\x4f\x3e\xd7\x8e\x9e\x61\x61\xce\xc0\x34\xe3\xe2\x59\xdd\x89\x27\x6c\x01\x6b\x3f\xbd\xae\x1d\x22\x62\x5a\x43\x4b\xd7\xcb\x55\x3a\xce\xff\x1e\xf8\x4f\xc9\x24\xf8\x94\x94\xa8\xbb\x29\x26\x78\x6d\x5d\x36\x7f\x4a\xe0\x0d\xfa\x7e\x75\xe1\xeb\x5d\xc9\x2c\xac\x4f\x50\x0b\xf4\xce\x7c\x82\xa4\x93\x48\x90\x5c\x25\x83\x20\xe9\xa4\x0e\x24\x57\xcf\x19\xa8\x08\xc6\x63\x14\xe3\x2e\xc9\xf8\x4a\x34\xe3\x2e\xd1\x78\x17\xaa\x8d\x72\x90\xca\xd5\x2c\x8d\x94\x8b\x6a\x29\xb5\xd9\x2c\xe9\x39\x83\xc5\xbc\xda\x9c\x0d\xac\x10\x35\x0e\xe0\xbd\xd9\x77\x47\xc0\x17\x5b\x9d\xf9\xf2\x02\xa9\x3a\xe3\xbb\x11\x2f\xc4\x00\xbb\xb6\xd8\x80\x0c\x94\xc1\x0e\xe4\x47\x19\xf4\xc2\x67\xbb\x09\xbc\x9a\xf1\x8a\x0d\x4b\x76\x98\x35\x68\xc0\x9e\x96\x62\x0a\x32\x3f\x3f\x5d\x40\xe7\x0c\x66\x55\x73\xb0\x0e\xb3\xa7\xa8\x8d\xa4\x8d\x95\x77\x9c\x93\xe8\x38\x3a\x52\x6a\x67\x28\x16\x44\xe2\xaf\x0e\x3d\x1b\xe9\xb9\xea\x3e\xd1\xea\xce\x97\x17\xd6\xb8\xd4\xca\xad\x57\xc6\x38\xc7\xd4\x93\x57\x42\x0a\xaf\xde\x6c\x6c\xb4\xbf\xda\x48\x5d\x3b\x82\x1e\xd8\x2b\x81\xb2\x1d\x01\xe9\xdb\x9d\xbe\xb9\x9a\x1a\x38\xdc\x6a\xdb\xa3\x00\xba\x34\x11\x72\x09\x60\x7a\xe8\xda\x2c\x7f\xb5\xc1\x6d\x75\xbc\x4d\x75\xa9\x5f\xaf\x36\xd8\x25\x47\x55\xf7\x49\x53\x17\xe4\xe8\x54\xef\xf5\xf9\x0a\x2c\x4a\x3e\x27\x22\x54\x7d\x5c\xcb\x5f\x6d\x02\xe5\x0b\xd0\x64\xa2\x68\x6b\xae\x06\x2b\xfc\xea\x7e\xb0\x6d\x7a\x03\xd0\x9e\x34\xd0\xa4\xd7\x90\xd0\x9e\xf4\xa0\x3d\x1d\x87\xf6\x87\x1a\x55\xc7\x15\x3a\xf4\x13\xf5\x5d\xa2\x45\x4d\xd1\x4e\xb3\xbd\x17\xb3\x25\x7a\x5e\x3a\x34\x5b\xa0\xac\xdf\x7c\xc4\xf7\xb4\xaf\x32\x94\x6b\xbe\x7f\xb2\xca\x77\x38\xd7\x80\x75\xa9\xb1\xa8\x24\x35\x68\xcc\x21\xd5\xb5\x9f\xb4\xb5\xed\x2e\x09\x06\x8b\xd9\xf2\x99\x8c\x52\x8e\x3a\xeb\x61\x3a\x5d\xd6\xce\xbe\x58\x42\xa0\xe7\x70\xf1\x62\x02\xdd\xa2\x18\x5d\x78\xd0\x6c\x65\x52\x77\xfa\xfe\xfd\x96\x48\x50\xed\xba\x7f\xf0\x94\xa6\x4f\xd0\x1d\xad\xdc\xa6\xe8\xa8\x6b\x3a\x0d\x0c\x23\xf0\xa7\x3b\x02\xef\xae\x79\xb4\xdd\xdd\x6a\xc5\xa3\xdf\x65\x45\x95\x06\x06\x56\x3b\x86\xc4\x45\xc1\x95\x7b\xfe\x74\x04\xc7\x93\x1d\x71\xb8\xc6\xb6\x15\x5b\xac\xcf\x96\x6b\xa7\x96\x80\xfb\x7d\x5e\x3e\x91\x86\xf1\xea\x8d\xb6\xa0\xd8\xea\xa1\x75\xcc\x93\x0d\xb7\x19\xf8\x54\xcd\xb1\xd1\xcf\xea\x3f\xce\x4a\xc4\x2a\x18\x02\xc1\x5f\x9a\x63\xc2\x57\x1e\xf4\xc1\x98\xb4\xb5\x99\x1c\x79\x8d\x03\x30\xd6\x7b\xe5\xd5\xdd\x91\xb5\x6d\x26\xff\xca\xab\x3b\xa3\xea\x59\xc6\xad\xc3\x43\xf4\x70\xe6\x72\x7e\xdb\x0f\xeb\x57\x1c\x32\xc6\x5d\x23\xd2\xdc\x57\xed\x87\x9b\x71\x65\x44\xb9\x77\x73\xa9\x75\xab\x57\x8d\xc2\x6d\xdf\x64\x83\x9b\x46\x13\x2d\x08\xd9\xdb\x66\x00\x94\x00\x48\x0f\x00\x19\x00\x70\x72\x51\xc4\x1e\xab\xe5\x85\x83\x89\x73\xcd\x1a\x5e\xb5\xa6\xf1\x0e\x4d\x7e\x57\xe4\xcb\x1f\x6e\xd6\xc4\xc0\x57\x97\xff\x98\x6b\x56\xf3\xaa\x35\x21\x1d\x22\xfc\xd0\x42\x9c\x2f\x2f\x3e\x7d\x81\xf6\xbb\xa5\x69\x46\x32\x90\xb7\xd5\xd2\x3a\xcb\x90\x62\x7c\xeb\x2d\x66\x42\xf9\xe8\xa4\xad\x03\xc5\x66\x88\x9d\x78\xa5\xdb\x42\x98\xa4\x63\xb3\xe3\x9f\xeb\x58\x94\x61\x91\xe6\xda\x4f\x45\x0d\xea\x37\x2b\x3e\xa2\xdd\x70\x19\xe8\x36\x2c\x5e\x0d\xd7\x81\xae\x7a\x96\x0a\x5f\xe5\x28\x15\x1c\x92\xca\x78\x39\xef\x9e\x77\xc2\x7b\xe8\xb0\x4b\xff\x1e\xba\xdd\xff\x01\x90\xc3\x06\x4d\x73\x9a\xeb\x9f\xe4\x10\xd4\x27\xaf\xe1\xe9\xcb\x8c\x35\xf1\xc6\x35\x48\x74\x68\x14\xbd\x5e\xa5\x5e\x05\x1c\xc2\x3c\x34\x1e\xa6\x7b\xf9\x5f\xe7\x9c\xff\xc2\x87\x40\x67\x6c\x3d\xab\x95\x7b\xab\xb7\xe8\x07\x54\x7c\xca\x62\xe1\xf8\x9a\xd0\xf6\x21\xbd\x2d\x9c\xdf\x7d\x0d\xb1\xc5\x67\x5f\x95\xd3\x42\x43\xb5\x30\xa7\x07\x9c\x3b\xad\xcd\x69\xa0\xd4\xf2\x9c\x0e\xea\xaa\xeb\x8a\x2d\x2b\xdc\x9d\x78\x32\xe8\xc4\x93\xab\x76\xe2\xc9\xa0\x13\x4f\x76\xeb\x84\x59\x54\x52\x75\x95\x91\x55\x4b\xb4\xe2\xd5\xaa\xe4\x1f\xb8\xe1\x00\x22\x52\x97\xbb\xa5\x3f\x38\x3b\x5f\xcf\x6a\x32\x4c\x2c\x32\xd4\x7c\x3a\xac\xf9\xe9\xe9\x89\x0d\xb7\x87\x1a\xd4\xd3\xa1\x09\x5b\xef\x13\x5d\xd3\xa9\x49\xbb\xff\x52\x47\x28\x0d\xee\xac\xb9\xec\xb4\x85\x87\xd8\x72\x33\xa7\xfe\xd8\x9e\xcf\x74\xb2\xfd\xcb\x71\xcd\x2b\x1e\xd7\xf4\x77\x3d\xac\xe9\x8f\x1d\xd5\xf4\x1d\x07\x35\xfd\x2f\xc7\x34\xaf\xfb\x98\xa6\xbf\xe5\x21\x4d\x83\x58\x3a\x47\x34\xfd\x6d\x0e\x68\xfa\xf6\x6b\xf8\xcd\xc1\xc3\xbb\x34\xf8\xf8\x76\x4a\xf1\xbf\xc8\x71\xcd\x7e\x82\x9d\x10\x93\x3f\xec\x0c\x67\x9d\x6e\x47\xe0\xfc\x73\xa5\xdb\xb9\xd2\x69\x4b\x55\xdc\x9e\xf6\xac\xeb\xec\x94\x90\x27\xc4\xa4\x73\x2c\x24\xc4\xc4\x7a\xcc\x84\x6e\x99\x90\x47\x54\xec\x1c\x35\xa1\x2a\xab\x45\x88\xc9\xb5\x5d\x21\xd6\xbb\x6f\xcd\xc9\x33\x38\xe4\xe0\x6d\xb2\x34\x4d\x93\x3c\xcc\xa7\x5a\xc2\x9e\xbd\xa9\xa9\x66\x44\x12\x46\x12\xc2\xf4\x74\x3e\x7b\x86\xbc\x3d\x86\xa6\x09\x0e\x13\x0f\x87\x4c\xcf\xfe\x63\x46\x82\x43\x52\xf0\x4c\xe6\x0c\xaa\x73\x03\x6d\x89\x24\x8a\x7d\x9f\x44\x91\x4c\x2b\xa4\x32\x07\x99\x91\x50\x9e\x06\x01\xa3\xb1\x9e\x57\x68\x4b\x24\x79\xea\x65\x84\x7b\xb9\x9e\x86\xc8\x8c\x24\x88\xd3\x30\xa0\x38\xd7\x93\x14\xf5\x42\xd3\xeb\xce\x52\x24\xf4\xe9\x8a\x59\x8a\x70\xf4\x25\x4d\xd1\x35\xc5\x44\x74\xe7\x34\x45\xa2\xc9\x58\x5c\xa4\xfb\x8c\x61\x64\x44\xbf\xa4\x29\xba\xfe\xd8\x88\x6e\x9b\xa6\xc8\x28\x9c\x6e\x7c\x44\x47\xd3\x14\xf9\xd4\x9d\xa6\x48\x0c\xe3\x77\x29\x31\x45\x4b\xe4\x5f\x24\x5a\xfa\x97\xbe\xdc\x72\xbd\x17\x5b\x3e\xd3\x95\x95\xab\x07\x51\xb2\xa8\xe9\xae\x02\xf4\x53\x7d\x82\xd7\xf0\xd6\x4d\xf7\x90\xef\x01\x3b\x3b\x9b\x5f\x4e\xd4\x8f\x53\xc4\x56\x27\xe7\xa7\x7c\x51\xad\xfb\x6f\xf2\xe8\xd7\x67\x5a\x7a\x20\x95\x52\x8b\xa2\x87\xde\xdb\x04\x84\x32\x52\x24\x10\x57\xe4\x31\xa1\x8c\x13\xb2\x37\x1d\xd6\x8b\xb1\x1f\x07\x41\x02\x69\x06\x89\xcf\x8b\x28\xcc\x72\x3d\x34\x18\x34\x48\xc3\xcc\x2b\xd2\xac\x80\x07\x10\xb2\x20\xf7\x53\x52\x98\x00\xf3\x24\x0d\xf3\x94\x85\xf0\x7a\x36\xa6\x49\x9e\xa6\x99\x13\xb0\x9f\x84\x51\x46\xc2\x14\xc2\x19\x3f\xa0\x69\xe8\x53\x13\xe0\x30\x29\x30\xc6\x05\x50\x9c\x46\x5e\x98\x7b\x38\x71\x02\x4e\x88\x5f\x50\xc2\xe0\xc9\x6d\x56\xe0\x24\x28\x92\xd4\x04\x98\xa5\x38\x0b\x79\x0e\x14\xe7\x2c\xca\x29\xc6\xd4\x09\x38\xa7\x5e\xcc\x98\xe4\x31\xf3\x3d\xdf\x23\x81\x91\xc7\x98\x50\x3f\x4c\xe5\x9b\x11\x41\x18\x7b\x51\x91\x72\x27\x60\x12\xf8\x98\x86\x29\xbc\x1d\x11\x70\x1e\xa4\x84\x66\x46\x56\x84\x5e\x16\xe7\x19\x3c\x20\x9e\x87\x45\x91\x06\x9c\x38\x01\xc7\x24\xe5\x61\x1e\x03\x2b\x0a\x12\xa7\x34\x89\x8c\xc2\xa3\x5e\xce\x53\x2c\x1f\xaf\xf0\x53\x1c\x25\x51\x8a\xdd\x3c\x4e\xf3\xcc\x8b\x64\x86\x4a\x12\x66\x31\x26\x7e\x68\x02\x9c\xe1\x24\x2d\xb0\x24\x20\x2b\xa2\x84\x44\x49\xe0\x04\xcc\x83\x24\x8d\x92\x0c\x78\x97\xf0\x02\x07\x2c\x37\xf2\x98\x17\x29\x0f\x62\x0a\xcf\x88\xfb\x34\x28\x48\xc8\x7d\x27\x60\xaf\xc8\x70\x92\x67\xd0\x80\xa6\x34\xcb\xc3\xd4\x48\x31\x09\xbc\x8c\xe1\x2c\x83\x47\xda\x63\x96\x25\x59\x14\xba\x85\x97\xf3\x84\x64\x11\x18\x48\x98\x90\xd4\x23\xb1\x11\x70\xc0\xe2\x80\x06\x0c\xe6\x08\x11\x67\x11\x0f\xa8\x9b\xe2\x30\x4b\x3d\x96\xe4\x40\x49\x9a\x07\xb8\x48\xf3\xc0\x68\xd2\x51\x91\x50\x9a\x03\x60\xea\x63\x1c\xfa\xa9\x9b\xe2\x84\xfa\x3c\xc4\x21\x01\x93\xe6\x51\x94\x17\xcc\x6c\x20\xd4\xc7\x59\x14\x41\x84\x4f\xf2\x34\xf0\x09\xf6\xdc\xbe\xc2\xf3\x7c\x12\x67\x54\xbe\xf9\x5e\xa4\x04\xfb\x46\x75\x4b\x8b\x30\x89\x8b\x4c\xe5\x37\xe5\x85\xc7\xb9\x5b\x2b\xb2\x88\x7b\x5e\x5a\x80\xe2\xfb\x39\xa3\xb4\xc8\x8c\x5a\x91\x87\x2c\x4e\x70\x00\x80\x13\xdf\x63\x2c\x26\x6e\x56\x78\x51\xc6\x22\x3f\x94\xcf\xbb\x78\x9e\x4f\x89\xd9\x40\x70\x40\x12\x92\xc8\xb9\x97\xc7\x3c\x1e\xf1\xd8\xcd\x0a\x12\xa7\xb1\xc7\x28\x38\x97\x20\xca\x09\x29\x0a\xa3\x49\x13\x8e\x05\x9b\x80\x65\x61\x46\xa2\x2c\x21\x91\x13\x70\x90\x93\x2c\xca\x0b\xd0\x8a\x90\x65\x01\x61\x3c\x37\xfa\x0a\xdf\xa7\x5e\x8e\x81\x65\x49\x9e\x84\xa9\x9f\x17\x4e\xc0\x51\xe8\xb1\xd8\x0f\x03\x69\x20\xac\x88\xfc\x9c\x9b\xd5\x2d\x62\x1e\x4b\xc1\x6f\xfb\x59\x1c\xa7\x84\xb9\xdd\x26\xc5\x19\xc9\x12\x22\xbd\x5b\xcc\x73\xc6\x79\x64\x02\x9c\x90\x98\x90\x4c\xb2\x0c\x07\x94\xf8\xa1\x9f\x3a\x01\x33\x92\x16\x9c\x32\xe9\x67\xb3\x02\x7b\x7e\x64\x34\x10\x46\x31\x8b\xa2\x00\x28\x4e\xb3\x80\xf8\x9e\xe7\xf6\x6e\x19\x09\x52\x9a\xc6\x1e\xf8\x59\xaf\xa0\x49\x9c\x60\xa3\x77\x8b\xa3\x2c\xc4\x0c\x78\xec\x45\x61\x90\x72\xdf\xad\x15\x39\x4e\x08\xa7\x38\x01\xc0\x11\x2f\x42\x82\x8d\x63\x5e\x1e\x25\x89\x17\x11\x90\x45\x18\x46\x21\x4b\x46\x2c\xaf\x08\x3c\xee\x87\x92\x77\x61\x1c\x63\xe2\x11\x66\xd4\x63\x2f\x62\xcc\x93\x3d\xf3\x49\x9a\xe6\x38\x75\x0b\x0f\x27\x2c\xc8\x30\x06\xb7\x99\xd2\x9c\xe4\x5e\x66\xa4\x18\x73\x3f\x8e\x32\x4f\xea\x31\x0e\x30\x4b\x43\xb7\x77\x23\x71\x40\xe3\x38\x00\x3d\xce\x0b\xca\x79\x9a\x24\x26\xc0\x7e\x90\x7a\x69\x96\x42\xcf\x38\x4e\xd2\x80\x8e\xa8\x9b\x9f\xe0\xcc\xcb\x52\x10\x4a\x16\x66\x49\xc8\x22\xdf\xe8\x8f\x79\x4e\x19\x0b\xc0\x6d\x72\x3f\xc0\x94\x65\x6e\x75\x0b\xd3\x24\xcb\x58\x50\xc8\x91\x21\xf2\xb9\x1f\x1b\x01\x47\x94\xf0\xa8\x90\xce\x2a\x8f\x52\x92\x52\xe6\x66\x45\x1c\xd0\x82\x12\x0e\x06\x12\xe6\xbc\x48\x89\xd9\x57\xc4\x94\x85\x91\x2f\x47\x9a\xc0\xc7\x31\x29\x22\xb7\x56\xd0\x20\xa3\x31\xc5\x32\x12\xc2\x85\xc7\xd2\xd8\xe8\x36\x69\x96\xc5\x1e\x91\xc2\xc3\x2c\x0a\xfc\x84\xbb\x63\xb7\xc4\x4b\x79\x51\x14\x4c\x46\x91\x91\x8f\x39\x31\x6a\x05\x0b\x42\x2f\xca\x38\x58\x5e\xce\x29\x49\x73\xee\x8e\xdd\x52\x5e\x24\xcc\x2f\xe4\xc8\x40\xb2\x28\x4e\xb0\x39\xae\x88\x62\x1c\xd3\x42\x0e\x61\x7e\x4c\x42\x9f\xb8\x85\x97\x31\x12\xfb\x3c\x03\x1e\x73\x46\xa2\x08\x27\x46\x1e\xe7\x98\x46\x29\x95\x43\x13\x11\x8a\x44\xba\x8b\x80\xc3\x40\x84\xe5\x2c\xce\x73\x30\x90\x2c\xe7\x1e\x4f\xb1\xd1\x6d\x16\x61\x9c\x07\x45\x5c\xa8\x41\x97\xe7\x38\x76\xeb\xb1\x17\x15\x5e\x14\xcb\x78\x21\x26\x38\x8e\x8a\xd4\x68\xd2\x1e\x8b\xfc\x38\xcf\xc0\x40\x18\xc9\x68\x42\x99\x7b\x04\xc1\xd8\x2f\x12\xea\x05\x6a\xe1\x2e\xf1\x72\x66\xa4\x18\xa7\x31\xf6\x52\x5f\xfa\x63\x1f\x67\x41\x8c\xdd\x3c\x26\x34\x4f\xe3\xb8\x08\xa5\x56\x78\x41\x9c\x53\xa3\x3f\xf6\x49\xc6\x58\x1a\x83\x56\x04\x5e\x16\x93\x20\x71\x1b\x88\x9f\x25\x3c\xe5\x1e\xb0\x02\x87\x59\x92\xf2\xd4\x28\xbc\xc0\xc7\x79\x14\x67\xd0\xb3\x24\xc3\x9e\x97\x07\x6e\x3d\x0e\xb2\x2c\xcc\x03\x19\x78\x67\xa9\xcf\x03\x92\x1a\x87\x26\x11\xae\x90\x24\x01\x67\x55\x64\x51\x18\x73\xe1\x5e\x5d\xbe\xa2\xc8\xd2\xa8\x60\x72\x90\x64\x79\x54\x30\x6e\xa4\x38\xca\x82\x00\x27\x14\x00\x07\x2c\x88\x43\x8a\x63\xb5\x88\xfa\xd6\x71\x6d\xb5\x9d\x17\xfe\x78\xd5\x1b\xaa\xb6\x67\xd0\x7e\xec\xdc\x50\xfd\xe9\x6a\x37\x54\x43\x4c\xb6\xdb\x3a\x30\x6c\x47\x5c\x7f\xf6\xd1\xab\x6e\x1d\x44\xcc\x4b\x78\xbd\xe0\xee\xa7\x59\x96\x78\x96\xad\x83\x34\x8d\x62\xc6\xe5\xf0\x4b\x83\x8c\xb1\xb8\x1b\xba\x38\x90\xf8\x59\xc4\x0b\x3f\x06\x4f\x56\xf0\x24\x28\xa8\xf0\x64\xa6\x9a\x2c\x0c\x8a\x22\xf4\xc1\x0a\xc2\x02\xe7\x7e\x54\x6c\xbb\xaa\x1f\x62\x8f\x87\x44\x3a\x1f\x96\xf3\x88\x92\xdc\xb2\x75\x90\xa4\x5e\x18\x51\xa9\x90\x24\xf5\x79\x94\xe1\x62\x4b\x24\xb8\xa0\x7e\x9e\x48\x9d\x2f\xd2\x00\xa7\x79\x64\xe9\x49\x98\x72\x2f\xcb\x65\x18\x84\xfd\x98\x13\x1c\x27\xbb\x6c\x1d\x5c\xf7\x3d\xd2\x6d\x52\xc3\x42\x3d\xcf\x9e\xf9\xf5\x18\xdb\x53\xbf\x1e\x13\x7b\xee\xd7\x63\xdf\x9e\xfc\xf5\x38\xb0\x67\x7f\x3d\x0e\xed\xe9\x5f\x8f\x23\x7b\xfe\xd7\xe3\xd8\x92\x00\x56\x76\x10\xd2\xc3\x1a\xcf\x81\xcb\xf2\xb9\x2c\x1f\x5e\xf6\x90\x3c\x80\xe6\xc6\x2b\x50\xb2\x7c\x2e\xcb\x2d\xcd\x09\x34\x27\xd6\xe6\x64\x2e\xcb\x2d\xcd\x7d\x68\xee\x5b\x9b\xfb\x73\x59\x6e\x69\x1e\x40\xf3\xc0\xda\x3c\x98\xcb\x72\x4b\xf3\x10\x9a\x87\xd6\xe6\xe1\x5c\x96\x5b\x9a\x47\xd0\x3c\xb2\x36\x8f\xe6\xb2\xdc\xd2\x3c\x86\xe6\xb1\xb5\x79\x3c\x97\xe5\x86\x63\x7d\x5b\x26\x3d\x96\x9a\x61\x02\xce\xa4\x52\xf4\x33\xee\xc1\x91\x5b\xa9\x10\xa6\x56\xa9\xd4\x05\x53\xab\x4c\xea\x81\xa9\x55\x26\x55\xc0\xd4\x2a\x97\xe2\x37\xb5\xca\xa5\xe4\x4d\xad\xb8\x94\xba\xa9\x15\x97\x02\x37\xb5\x2a\xa4\xb0\x4d\xad\x0a\x29\x67\x53\xab\x13\x29\x63\x53\xab\x13\x29\x5e\x53\xab\x99\x14\xad\xa9\xd5\x4c\x4a\x75\x6e\xca\x3b\xe8\xba\xba\xbb\xe5\x73\xa8\xd6\x7c\xda\x35\xfe\x1f\x4b\x99\x7b\xd8\x76\xdd\xfc\x11\x8c\xe0\xf5\xf6\xd9\xb0\xca\x16\x89\xa2\x25\x1a\xc1\x82\x1f\xcb\xfa\xb6\x81\x9e\x35\x1a\xdd\x46\xe4\x2d\xd4\x34\xe7\x72\x6d\x61\xcc\x25\x0c\x75\xbf\xa0\x0f\x03\x6e\xcd\x5f\x29\x03\xf5\xe1\x21\xfa\x0f\xc8\x46\x6c\x47\x5e\xa7\x74\xde\x29\x43\xf5\x66\xd6\xe4\x39\xde\x8c\xdd\xc5\x53\xd5\xe6\x5a\x0b\xf7\x7d\x3c\x59\x6b\xd6\xc9\x82\x3d\x93\xc9\x7f\xf5\xe4\xd5\x73\x48\x51\x5c\xa7\x03\xee\xd4\xa3\x83\x7a\x70\xe8\xf5\x1d\xea\x56\x8b\x5d\x37\x4c\x65\xcd\x79\x87\x8a\xf9\x90\x8a\x99\x89\x8a\xf9\x90\x8a\x99\x4e\x45\xb7\x5e\x3c\xac\x67\xc9\x64\xac\x8b\xd4\x92\x33\xe7\x83\x96\x7b\x7b\x97\xe4\xdb\xad\x44\xf1\x76\x12\xc5\xad\x44\xf1\x56\x12\xc5\xb3\x4e\x82\xef\x59\x9d\x85\x5b\x4b\xcc\x3d\x57\xb9\xba\x35\x26\x61\xc5\xe1\x6e\x35\x38\xc7\x9c\x68\x22\xad\xe1\x45\xa3\x22\xc5\xf3\x0e\x19\x73\x03\x19\x33\x13\x19\xf3\x01\x19\xb3\x0e\x19\x5d\x80\xd1\x00\x1e\x89\x9c\x32\xdd\x29\x77\xb8\xcb\x95\xc4\xad\xd8\x63\x97\xd8\x7f\x2c\x63\xe9\xb9\x8c\x03\x73\xaf\xe6\x5c\xd5\x74\xdc\x09\x97\x35\x71\xa4\x39\x12\xeb\xab\xd0\x75\x5d\x49\x00\x36\x46\x16\xfd\xba\xf3\xba\xee\x28\x0d\xad\xa7\x99\x0b\xa6\x95\x71\x7f\xe4\xea\x56\x6f\x5d\xd9\x4c\x56\x9f\x41\xce\x36\x01\x47\x48\xd2\xdb\x43\xf7\x6b\xeb\x6c\x7e\xf9\xff\x11\x46\x77\xd1\xe0\xd8\xf4\x90\x0e\xf1\x6f\x2d\xc1\x71\x32\xc4\xbf\xfb\x8d\xb5\x58\xa8\xc0\x57\xa5\x02\xb8\xb8\x25\x0d\x52\x3a\x43\x0a\xa4\x24\x06\xf8\xcd\x40\xdb\x51\xf1\xc7\xd2\x26\xde\x76\xd4\xfb\xb1\x34\x11\x67\xcf\x89\xaf\x92\xe2\xcf\xd0\x4d\x54\xcc\x54\x5a\x7c\xf1\xc5\x7c\x8f\x4f\xb6\x91\xb6\xcf\xe7\xa2\xcd\x5c\xb5\x11\x5f\x4e\xe6\x8e\x64\xfa\x33\xc8\xa6\x2f\x40\xa7\x12\x0f\x7c\xce\xe4\xe7\x54\x7d\xb6\x37\x9f\x43\x73\x81\x25\x95\x28\xe1\x73\x26\x3f\xa7\xea\xb3\x3b\x25\xff\x4c\xe6\xe4\x57\x0e\x47\x8e\x2b\x6c\x2e\xd3\x4b\xef\xc9\xe4\x07\x6c\x56\x67\xec\x57\x85\x9d\x9c\xfd\x33\xed\x15\x09\x56\x8f\x3a\xce\xcc\xfc\x30\x9b\x9a\x34\x80\x14\xce\x59\x17\xe7\xbc\x83\x73\xd6\xc5\x39\xd7\x71\xce\xb6\xc1\x89\x65\x3f\xb9\x1a\x1a\xe4\x7d\x13\x2e\x07\x05\x5a\xa7\xfd\x9f\xd5\x8f\x56\x68\x85\x41\x5b\x28\x70\xfa\x75\x99\x4c\xc3\xed\xc6\x29\xfb\xa9\x2a\xd7\x38\x67\x5d\x9c\xf3\x0e\xce\x59\x17\xe7\x5c\xc7\x39\x6b\x71\x1a\xa3\xce\xf1\x77\x08\xcc\xb4\x7e\x0f\xd9\x97\xbe\xb7\x5f\xa6\xfa\x1e\x8c\xf7\xfb\xd2\x75\x8d\xea\x7b\x70\x06\xdf\x97\x36\x17\xfa\x01\x1e\x4a\x10\x75\x66\xf3\x86\x44\x93\x51\xca\x8a\x02\xe1\xac\xed\x8b\x74\x17\x15\xd6\xdd\xc5\x6c\x1b\x5f\xd5\xa2\x15\xff\x0a\x8e\xb8\x71\x56\x80\x2a\x9b\x99\x10\x66\x57\xc2\xf8\xbd\xd1\xf5\xf4\x31\x7e\x5f\x9a\x30\x7e\x5f\x5e\x05\xa3\xd9\xd9\xf5\x31\xfe\x68\xc4\xf8\xa3\x09\xa3\x59\xdb\xfa\x8f\x57\x58\x50\xc2\xe2\x45\x6d\xf6\x50\xd1\x4a\x1d\xac\x83\xd4\x5e\x69\x5f\xba\x47\x20\x91\xe8\x24\xd6\xb0\xb6\x23\xf3\xef\x67\x39\xab\x38\xba\x70\xcf\xf4\xc5\x1f\xcc\x37\x8d\xfa\x0d\xd3\xcd\x13\x13\xd9\x30\x00\x15\xa6\x36\x30\xb1\x2d\x4c\x6d\x60\x0e\xcd\x4d\x6d\x60\x0a\xcd\x4d\x6d\x60\x4a\x3e\xc9\xe7\xf0\x7c\xc7\xdc\xf6\x7e\x07\xcc\xe9\x27\xf9\x0c\x6a\x49\xd6\x71\x9d\x73\xf9\x80\x69\xd6\x97\x40\x04\xa4\xcc\x44\x23\x2c\x29\x64\x26\x1a\x61\xf5\x22\x35\xb5\x81\xc5\x8b\xd4\xd4\x06\xd6\x49\x98\xa9\x0d\x2c\x93\x0c\x5e\x33\x10\x7f\xb0\xec\x32\x91\xaa\x5e\x11\x2b\x33\x60\xe1\x66\x22\xf9\x20\x34\x6b\xbf\x1d\x71\x24\x37\xaa\x61\xb0\x73\xad\x8f\x95\x68\x6b\x86\x10\x19\x1c\x83\xfe\xb3\x41\x34\x70\xdc\x24\xa3\x98\x1c\x83\xde\x33\x49\xec\xb1\xa7\x53\xcb\x86\xc4\xf6\xe1\x68\xab\x8c\x12\x21\xb0\x28\x1d\x22\xc4\x2d\x42\x60\x4f\xaa\x10\x76\x3c\x41\x3a\x8e\x50\x5b\x97\x94\x08\x09\xb8\xd8\x21\x42\xd2\x22\x24\xb3\x7a\x5c\x9a\x40\x7d\xcd\xbd\x8e\x23\xd4\x56\x32\x25\x42\x5f\x20\xcc\x87\x08\xfd\x16\xa1\x2f\x70\xe5\x0a\xa1\x3f\x62\x0e\x7d\x38\xda\xda\xa7\x44\x18\x08\x84\x7c\x88\x30\x68\x11\x06\x02\x17\x57\x08\x03\x1d\x21\x1f\x47\xa8\xad\x96\x4a\x84\xa1\x40\x58\x0c\x11\x86\x2d\xc2\x50\xe0\x2a\x14\xc2\x50\x47\x58\x8c\x23\xd4\xd6\x57\x25\xc2\x08\x26\x15\x43\x84\x51\x8b\x10\xa2\xf7\x13\x85\x30\xea\x4c\x22\xc6\x11\x6a\x2b\xb2\x12\x61\x2c\x10\xce\x86\x08\xe3\x16\x21\x4c\x9b\xd4\x98\x2c\xea\xbb\x82\x80\x4f\xbe\x7b\xf1\xe5\x51\x9c\xeb\x7b\x14\x07\x8b\xe0\x5e\xbd\x6c\x26\x80\x41\x1e\x16\xdf\xbb\xee\x67\x71\xcc\x68\xf0\x3f\xe5\xc3\x38\x0f\x97\x8b\x0f\x7c\x25\xb3\xfc\xa2\x6a\x89\x7c\x72\x27\x2d\x2b\x11\xa0\xe4\x88\xc1\xf9\xec\x94\x17\xcb\x15\x57\xc7\xa9\x07\x52\xd3\xee\x9a\x68\x7b\x77\xd5\xf2\xb5\x4f\xae\xe3\x21\x9e\x3f\xeb\x13\x3c\x3a\x9d\x4d\x7e\x90\xbb\x08\x7b\x24\x38\xf4\x55\x9e\xe2\x2f\xb7\x9b\xac\x57\x95\x42\x4c\x76\xbd\xdd\x24\x9a\x8c\xdc\x6e\xea\x1c\x6b\x18\xdc\x6e\x0a\x31\xf9\x72\xbb\xe9\xba\x6f\x37\x09\xa9\x6c\x77\xbb\xc9\x28\x9c\xce\xed\x26\x29\x20\xe7\xed\x26\x79\x8f\x76\xcb\xdb\xdf\xfe\x9f\xfa\x3e\x13\x5f\x64\x77\x52\xb6\xe6\x51\xd0\x2b\x38\xcd\xc3\x7e\xd5\x0f\x67\xef\xf3\xa2\xf7\x63\x56\x9e\xcd\xf8\xea\x0f\xb9\x12\xa5\x91\x0a\xdf\x05\x85\xb2\x40\x12\x06\x9f\x75\x7a\xfe\x15\xae\x4e\xfd\xb8\xd5\x9b\x40\x70\x78\xe6\x21\x74\xbd\xa9\xa7\xfd\x36\x7e\x15\xea\xf0\x10\x3d\xe7\xab\x53\x18\x45\x1f\xce\x96\x65\xc6\x11\xee\x3f\x9b\x22\x9a\x3f\x7f\x88\xbb\x77\x97\xc2\x78\x8a\x82\x64\x8a\x02\x3c\x45\xbe\x3f\x45\x24\x9c\x22\x1c\x4f\x51\x32\x45\x08\x6b\x47\x8d\x42\x3a\x45\xa1\x37\x45\x01\x99\x22\x3f\x98\x22\x12\x4d\x11\xa6\x53\x84\xbd\x29\x22\x7a\xbd\x64\x8a\x42\x3c\x45\x81\x3f\x45\x7e\x38\x45\x24\x9e\x22\x9c\x4c\x11\x16\xf0\xb5\x7a\x91\x37\x45\x21\x99\xa2\x20\x98\x22\x3f\x9a\xa2\xc8\x9f\xa2\x30\x9c\xa2\x20\x9e\x22\x3f\xd1\x2a\xfa\x78\x8a\x88\x3f\x45\x38\x9c\xa2\x78\x8a\x50\x44\xa6\x28\x0c\xa6\x28\x80\xa7\x05\xf4\x8a\x82\x12\x32\x45\x38\x98\xa2\x48\x54\xc4\x53\x14\xfa\x53\x14\x84\x53\xe4\xc7\x5a\x45\x92\x4c\x11\xc1\x53\x84\x05\xca\x29\x42\x84\x4e\x11\xf1\xa6\x08\x0b\x72\x64\xb5\xb7\x0e\xbe\x12\x33\x5f\x49\x97\xaf\x82\x0a\xc1\x47\xd1\x6f\x22\x3e\x4f\x11\x0a\x75\x6a\x15\x62\xd1\x2d\x41\x2d\x10\xe4\xe9\x54\xfa\x8a\x71\x82\x2a\x51\x21\x9a\x22\xbd\xbb\x38\x92\xfc\x10\x0c\x06\xea\xfd\xae\x20\x84\x40\x05\x83\x05\xff\xfc\x58\x32\x36\x0c\x7b\xfc\x0a\x3c\x25\xad\x50\x4a\x3f\xd0\x31\x08\xd1\x08\xd5\xf0\x85\x48\x23\x29\xf6\x50\x97\xa1\x10\x81\xd0\x07\xa1\x17\x42\x86\x82\xb1\x75\x54\xd3\x79\x11\xea\xfc\xf4\x7c\xce\xe0\x99\x14\x11\x54\xae\x67\x65\x31\x78\xe1\x09\xac\xe0\xbb\x57\x3f\xbd\x3c\xfe\xee\xb1\x7c\x53\x4a\x70\x8c\x4c\x11\x74\x5e\x70\x88\x0a\x8d\x54\x62\x02\xee\x2a\x4d\xc5\x4a\x9c\x44\x69\x2f\x30\x84\xea\xf8\x5f\x7e\xf3\xec\x35\x5f\x23\xb6\xc8\x55\x6e\xf4\x33\x10\xa9\x7c\x4f\xc3\x40\x87\xa8\xff\xd3\xf3\xae\x3c\x7b\x21\xa5\xb7\xf1\xee\xc2\x64\x84\x12\xcf\x9b\xf6\xcb\xea\xb9\x82\xac\x62\xa8\x40\x3a\x15\xa8\xe7\x91\x41\x15\x5f\xab\x32\x2c\x0d\xf4\x52\x03\x82\xb0\x8b\x80\x18\x10\x44\x5d\x22\x4d\x55\xe2\x5e\x3f\x0c\x88\x68\x87\x90\x21\x88\xa4\x8f\x65\x08\x82\xe9\x55\x4c\x15\xd2\x3e\xb7\x86\x55\xb2\x1e\x9a\x41\x85\xbc\xdf\x95\x61\x15\xae\x55\x19\x62\x28\xba\x54\x0e\x9b\x53\x57\x6b\x4c\x47\xe5\x41\xe8\x08\x02\x9f\x8e\x68\x55\xd0\x47\x62\xd0\x0b\xea\xd6\x9b\x88\x8e\x2a\x66\x4c\x5d\x8a\x49\xe9\xa8\xbc\x13\x3a\x22\x6f\xd6\x27\xc2\xa0\x12\x7d\x34\x43\x4a\x32\x3a\x2a\xf1\x9c\x8e\x68\x0d\xa7\x6e\xed\x2e\xfa\x38\x0c\x92\xb7\x8a\x4b\x79\x09\x6c\x66\x24\xd1\x4a\x2d\xc2\xf4\x3b\x55\x8c\xd8\x83\x2e\x14\x53\x1f\x43\xbd\x8a\x51\x27\x74\x3a\x0d\xe5\x71\x97\x0c\x87\x6d\x60\x87\xfa\x27\x7d\x4a\xad\x8e\x02\x3b\x24\x9a\x76\x3b\x63\xd0\x8a\x4e\x67\xac\x7e\x02\x3b\xf4\x97\xf7\xaa\xd8\x5c\x05\x36\xbb\x02\x3a\xca\x0a\x4c\x47\x59\x41\xe8\xa8\xe8\x7d\xea\x16\x5b\xd0\x03\x61\xf3\x15\x2e\x76\x47\xd4\xa5\xc2\x31\x1d\x11\x06\xa5\x23\x9c\x4c\xe8\xa8\x6a\x31\xea\x16\x68\xda\xe7\xb7\x61\xf0\xe8\x63\x19\x56\xc9\xa9\x4b\xa4\x9c\x8e\x98\x50\xd1\x97\xa8\xfe\x46\xd5\x74\x2c\xca\x08\x3c\x8f\x06\x1e\xb6\x7a\x10\x55\xc7\x1a\x66\x34\x02\xb4\x79\x90\x1a\x89\x67\x42\x12\x74\x91\x18\xeb\x84\x5d\x38\x46\x62\xa2\x2e\x1c\x63\x9d\xb8\xad\x63\xc0\xa2\x3b\x5b\x63\xf3\xa4\x8f\xc2\x00\x84\xf5\xbb\x63\x0f\x38\x14\x22\x03\x90\xac\xc3\x58\x43\x85\xbc\xad\x60\x75\x20\x92\x04\x43\xe3\xa2\x2f\x15\x6b\xdc\xe5\x64\x26\xa6\x23\xbd\x20\xd4\xc5\x6d\xbf\x8f\xc2\xa4\x1b\xb4\x27\x77\x93\x6e\xd0\x71\x86\x47\x74\x44\x51\x63\x3a\xae\xa8\x94\x8e\x08\x25\xa1\x0e\xa1\x30\xea\xb6\xa5\xb4\x4f\x81\xdd\x91\x38\x4d\x25\xa7\x23\x4a\xcc\xfb\x3c\xb5\xfb\x13\xab\x06\xe9\x13\x10\x43\x29\xde\xc2\xec\x31\xd9\xc2\x98\xb0\xbf\x85\xe1\xe3\x60\x0b\x7d\xc6\xa1\xd3\xf4\x71\x34\x66\x92\x38\x1e\x71\x86\x7a\x08\x6e\x86\x90\x8c\xb9\x4b\xcc\xc6\xec\x1e\xa7\x5b\x78\x4b\x9c\x8d\x39\x32\x9c\x6f\xe1\x2c\x31\xdf\xc2\x95\xe1\xa2\x2f\x21\xa3\xba\x8c\xb9\x0a\x8c\xc7\x2c\x14\x93\x2d\x0c\x04\xfb\x23\x56\x86\x83\x6d\x1c\x5b\xb8\x85\xdb\xc1\x91\xd3\xbb\xe1\x78\x0b\xb7\x84\xe9\x16\xb6\x88\x93\x2d\xac\x1e\xb3\x2d\xbc\x29\x4e\xc7\x3c\x18\xce\x5c\x2e\x0c\xe7\x63\x6e\x81\x6f\xe1\x46\x71\xd1\xf3\x50\xbb\x84\x2a\xd8\x0b\x2c\xce\xc8\x4c\x32\xe9\x70\x05\x5b\x43\x14\x09\xdb\x04\x3d\xd0\xca\x3d\x43\x79\xd8\x13\xce\xb0\x46\xd4\x61\x9a\x09\x47\xdc\xa9\x31\x3e\x1c\xdb\x63\x93\x16\x8b\x2d\x32\xa9\x7b\x6a\x8b\x4a\x5a\x2a\x86\x74\x66\x3d\x6e\x0e\x6b\xe4\x1d\x6e\xd9\x42\x13\x80\x60\x09\x4b\x54\x5b\x33\x07\x5c\xdd\xc3\x74\x8c\x7c\x42\xed\x8a\xe2\xd3\x31\x45\x09\xe8\x98\xa0\x43\xea\xee\x7c\x44\xdd\xaa\x14\x6b\xe5\xc3\x52\x4a\xed\xac\x4b\xa8\x8b\x75\x8c\x8e\xa9\x57\x4a\xdd\x46\x90\x51\xb7\xea\xe4\x74\x4c\x31\x38\x1d\x33\x82\x82\x8e\xa9\x78\x27\xac\xb0\x28\x01\x1e\x31\x57\x4c\x46\x34\x14\xfb\xa3\x2e\x03\x07\x4e\x4d\xc5\xe1\xa8\xc1\xe3\x68\xd4\x6b\xe0\xd8\xe5\x89\xe9\xa8\x25\xe2\x64\xd4\x65\x60\xe6\xb0\x46\x9c\x8e\xb8\x0b\x9c\x8d\x7a\x2d\xac\xbb\x03\x03\x0a\x3e\xe2\x7b\x71\x31\xea\x92\x54\x68\xe1\xec\x26\x76\xda\x15\x26\xe3\xae\xc5\x77\x78\x0e\x1c\x8c\x98\x35\x0e\x47\x7d\x0b\x8e\x9c\x06\x8c\xe3\x51\xdf\x86\xe9\x88\xf3\xc1\xc9\xa8\x05\x62\x36\xe2\x06\x70\x3a\xea\x03\x71\x36\xea\x0a\x70\x3e\xea\x8f\x30\x77\x38\x3b\x5c\x74\xbd\xd1\x2e\xf1\x03\xf5\x24\x4a\xb3\x6f\xa9\xa3\x4f\xec\x05\x96\x50\xa2\x26\xda\x50\xee\xb7\x10\x02\xb3\x22\x06\x76\x25\x0a\xbb\x1c\x31\xc7\x10\x4d\x70\x6c\x42\x1f\x7b\x9d\xf0\xcf\x3e\x7e\xd6\x3b\x2a\xe6\x08\xa2\x95\xad\x39\x7e\x90\xe5\xe6\xd8\xa1\x65\x9f\x6d\x07\xa5\x65\x8f\x01\x46\xae\x59\xa9\x25\x72\xa8\xd5\xdb\x1c\x3b\xb4\x02\xb6\xf4\xdf\x29\x5f\x4c\xed\xdd\x23\x74\x8c\x78\x9f\x8e\x31\x20\xa0\x6e\x11\x87\x74\xac\x0b\x11\xb5\xea\x4f\x4c\xc7\x94\x8f\x52\x17\xff\x92\x2e\x72\x5b\x10\xe1\xd0\x8e\x94\xba\xa4\x97\xd1\x31\xed\xcb\xa9\x5b\x7f\x39\x75\x9b\x5f\x41\xc7\x2c\x04\x7b\x23\x26\x82\xf1\x88\x15\x62\x32\x6a\x86\xd8\x77\x8d\x14\x4e\x0d\xc7\xe1\xa8\x89\xe0\xc8\x1b\x93\x13\x8e\x47\x3d\x19\xa6\xa3\xd6\x82\x93\x51\x77\x81\xd9\xa8\xc3\xc3\xe9\x88\xcf\xc4\xd9\xa8\xdf\xc0\xf9\x88\x5b\xc2\xdc\xe1\x97\x70\xe1\x74\x1b\x32\x7a\x70\xf7\x01\x8f\xda\x25\x26\x76\xc3\xc4\xfe\x88\xd9\xe3\x60\x44\xf1\x71\x38\x6a\x3b\x38\x1a\xf7\x6e\xb1\xc3\xbd\x61\x3a\x6e\x3c\x89\xd3\x7f\x60\x36\xea\xff\x70\x3a\xea\x44\x71\xe6\x74\x22\x38\x1f\xf5\x52\x98\x8f\xb8\x29\x5c\x74\xfd\xc8\x6e\xc1\x83\xd1\xa7\xd4\xf4\xda\x76\x48\x1a\x6a\x8c\x21\xc3\x5d\xed\xb8\x86\x31\x62\x50\x15\x60\x3d\xc5\x18\x37\x34\x31\x9f\xa1\x3c\xaa\x01\xd8\x2a\xc4\x2d\x81\x86\x52\x5d\xe6\xb6\x90\xa1\xa5\xcf\x12\x33\xb4\x3d\x34\x60\x48\x5b\x02\xcd\x24\x64\x9d\x0a\xa6\x81\xc3\x6a\x7b\x5c\x17\x8e\x01\x74\xd1\x61\x8e\x79\xcd\xc1\xd5\x1e\xd3\x11\xe6\x12\xea\xd9\x14\xc7\xa7\x6e\xc5\x09\xa8\x4b\x71\x42\x3a\xa2\x17\x11\x1d\xe1\x5a\x4c\x47\x54\x8f\xd2\x11\xd1\x26\xd4\xc6\x77\x46\x47\x64\x9a\x52\xb7\xd6\x66\x74\x44\x6b\x72\x3a\x22\x39\x4e\xdd\x8a\x5b\x50\x97\xda\x63\xcf\x69\xb6\x18\x7b\x56\xb9\x62\x32\x66\xd3\xd8\x1f\xb3\x49\x1c\x8c\x58\x35\x0e\xc7\x8c\x02\x47\x63\x9e\x03\xc7\x23\xb6\xdd\x8c\x7b\x56\x31\xe2\x64\xcc\x80\x30\x1b\xf1\x8f\x38\x1d\xf3\x20\x38\x73\x7a\x28\x9c\x8f\x79\x18\xcc\xed\x83\x73\x31\xe2\x21\x20\x3e\x70\xcb\x0a\x8f\x68\x1a\x26\x23\x96\x8e\xfd\x31\x63\xc6\xc1\x98\xb1\xe2\x70\xcc\x55\x45\x76\x57\x84\xe3\x31\x67\x81\xa9\xdb\x5c\x92\x31\x83\xc7\xcc\xea\x2c\x70\x3a\x66\xcb\x38\x1b\x71\x17\x38\x77\x3a\x4b\xcc\xc7\x5c\x19\x2e\x7a\x0e\x67\x97\xa8\x40\x91\x4d\x4d\x5e\xa4\x86\x69\x8a\x0b\x64\x5b\x62\xee\xb3\xdf\x96\x13\x13\xec\xa0\xe5\x88\x11\x7e\xa8\xf7\xc7\x14\x15\x34\xa5\x43\xd8\x71\x47\xa1\xad\xa3\xa2\x31\x1a\xd0\x88\x1a\x02\x66\x35\x5a\x23\xc9\xa9\x52\x50\x53\x04\xa0\xf1\x6a\x58\x9e\x6b\x60\x87\xa5\xbc\xe9\xeb\xb0\xac\xe8\x70\xd9\xd4\x53\xa7\x90\x30\x75\x0b\x89\x50\x4b\x8f\x7c\xea\x92\x4e\x40\x5d\xfd\x09\xa9\x5b\xeb\x22\xea\xd6\x8c\x98\xda\xf9\x41\xa9\x4b\x2f\x12\x6a\xd7\x67\x46\xdd\xa2\x4f\xa9\x5b\x86\x19\xb5\xe8\x54\x4e\xdd\x22\xe2\xd4\xa5\x53\x05\x75\xab\x32\xf6\x46\xec\x08\xe3\x11\xe5\xc3\x64\xc4\x52\xb1\xef\x50\x40\x1c\x38\xed\x14\x87\x23\xa6\x88\x23\x6f\xc4\x07\xc5\x4e\x9b\x6b\x22\x58\x0b\xed\x89\xd5\x6b\x33\x9b\xb5\xe2\x74\xc4\xb5\xe1\xcc\xe1\x17\x71\x3e\xe2\x43\x30\x1f\xb1\x59\x5c\x38\x9d\x9b\x18\xd1\x2d\x84\x63\xa7\x2a\x61\xe2\x34\x5a\xec\x8f\xd8\x25\x0e\x46\x0c\x13\x87\x0e\xcb\xc4\xd1\x88\xaf\xc1\xf1\xa8\xb3\x1a\xb1\x24\x9c\x8c\xd8\x28\x66\x0e\x07\x80\x53\xa7\xd7\xc2\x99\xd3\xb5\xe0\xdc\x66\xff\x98\x8f\x99\x70\xd1\x75\x3d\xbb\x0f\xdd\x06\x1d\xa9\x49\x0d\x3c\x6c\x18\xba\x55\xa8\x61\x18\xb4\x15\x50\x53\xb3\xa0\x09\x72\x4c\xa5\xa1\xa5\xfb\x91\x04\x69\x18\xa3\xdb\x90\x69\x58\x4a\xb5\x0e\x98\x86\xe9\xa6\xef\xc3\xa6\x4c\x53\xf2\x61\x69\xaa\x75\xc2\x34\x55\xd7\xe2\x38\xc3\x30\x2d\xf9\x36\x84\xca\x5b\xbe\x99\x26\xe9\x5a\xe4\x3b\xec\xa9\x8b\x0d\x98\x9a\x99\x4a\xa8\x4b\xbe\x3e\x75\xf5\x31\xa0\x0e\xc5\x09\xa9\x8b\x79\x11\x75\xf5\x24\xa6\x36\xf6\x50\xea\x50\xab\x84\xba\x44\xcd\xa8\x4b\x22\x29\x75\x28\x42\x46\x6d\x6a\x9e\x53\x97\x26\x73\x6a\xd6\xd8\x82\x3a\x84\x8c\x3d\xa7\x94\x31\x76\x9a\x2b\x71\xda\x2b\xf6\x9d\xb6\x82\x03\x97\x39\xe0\xd0\x69\x4a\x38\x72\x1a\x04\x8e\x5d\x1e\x41\x8d\x37\xc6\xa2\xc4\xe9\x2d\x30\x73\x59\x0c\x4e\x2d\x4e\x03\x67\x36\x27\x9b\x3b\x2d\x17\x73\xa7\x53\xc0\x85\xd5\x23\x62\xcf\x29\x75\xec\x34\x44\x4c\xdc\xd6\xed\x5b\x34\x0d\x07\x4e\x43\xc3\xa1\xcb\x84\x71\x64\xb5\x43\x1c\x3b\x3d\x03\xa6\x4e\xeb\xc7\x89\xd3\x16\x31\xb3\x38\x2b\x9c\x3a\xcd\x0d\x67\x2e\xef\x80\x73\xab\x15\x63\xee\xf4\x1c\xb8\xd0\x9c\xc3\x2e\x63\x2a\x15\x03\x3c\x31\x00\x6c\x98\x33\xf4\xc7\x77\xdb\xcd\x8d\xa1\x3b\x96\xed\x86\x8e\x58\xc1\x33\x14\x85\x12\x1e\x31\xd2\x11\x35\x85\x26\x27\xac\x28\x31\x8f\x33\xd4\x33\xd3\x9f\x34\xfd\x36\xb9\x60\x49\xa7\xa9\x28\x6d\x80\x1a\xe8\xcc\xee\xca\xcb\x1e\x43\xf7\x6b\xd6\x13\xde\x30\xd1\xd0\xa6\x50\x44\x18\x8a\xea\x4d\x25\x6b\xcf\x65\x31\x76\xf1\x54\xd5\x21\x2e\xf9\xab\x3a\xbe\x4b\xd6\xea\xf7\xc0\xc5\x6c\x55\x27\xb4\xb3\x55\xd5\x88\x46\xfb\x1c\x5b\x54\x4b\x15\x53\x17\x47\x55\x9d\xc4\x26\x25\x55\xce\xec\x5a\xaa\x6a\xa4\x2e\x7d\x54\x75\x32\xb3\xc8\x55\x69\xee\x52\x23\x55\x87\xbb\x54\x54\xd5\x29\xec\x16\x5a\x47\xc4\x46\xc3\xc6\xae\x1e\x60\x62\x61\x32\xf6\x6d\x1a\x87\x03\x17\xb1\x38\x74\x89\x05\x47\x2e\x66\xe0\xd8\xd1\x45\x9b\xff\x4d\xec\x22\xc4\xcc\xa5\xa9\x38\x75\xfa\xc3\xcc\x65\x51\x38\xb7\xeb\x37\xe6\x36\xa5\xc3\xc5\xb8\x75\xb5\x93\x1b\x6b\x0d\xec\xf6\x05\x98\x8c\x2b\x1c\xf6\xc7\xac\x0f\x07\x4e\xeb\xc3\xe1\xb8\x13\xa8\x85\xed\xec\x6e\x3c\xee\x94\x30\x1d\x77\x6e\x38\x19\xf7\x06\xb5\x3a\xb8\xac\x4c\x2a\x85\xb5\x34\x1b\x73\x6b\x52\x31\x1c\x74\xf2\x31\x8f\x53\x2b\x09\x60\xd1\x46\x76\xf9\x51\xcf\x6b\xf0\x94\xad\xdf\xaf\x51\x35\x63\x15\x5a\xf3\x39\xcf\x2a\xc8\x47\xf4\xf2\x9b\x67\xaf\x51\xb9\x38\xab\x9f\x89\x68\x32\x1a\x3c\x7d\xf0\xb2\xf7\x70\x71\x7b\x31\x71\x8a\xda\x83\xff\xf0\x80\xa2\xfa\x02\x9f\xd5\x97\xa9\xde\xd0\x53\xbf\xca\x0a\xf2\x4b\xfd\x59\x7c\x99\x6a\xfd\xe9\x53\xae\x65\x55\xfa\xf6\xd1\x4b\x99\x18\x0b\xc9\xc4\x2f\xee\x37\xaa\x44\xed\xe6\x81\x2a\xf9\x45\xcb\x92\x72\xd5\x27\xaa\xdc\xa9\xf5\xde\xf3\xcb\x26\x05\xd8\x7b\x7e\x69\x48\x7d\xf7\x9e\x5f\xd6\x79\xf5\xde\xf3\x4b\x73\x5a\x3d\x81\x43\x8a\x28\x8c\x50\x5a\x56\x6b\xc4\xb2\x6c\xb9\xca\xcb\xc5\x09\xaa\x96\xe8\xf9\x43\x6c\x84\xfb\x4d\x09\xa9\x80\xde\xf4\x73\x20\x9b\xde\x0e\x09\x23\xfb\xdb\x21\x2d\xb8\xe7\x4b\x01\xf0\xf9\x43\xfc\xa6\x7c\x8b\xee\x20\x6c\xc8\x51\xaa\xf0\xca\xf4\xfc\x93\xba\x77\x6f\xda\xf6\x2a\x1d\x9f\xf8\xcf\xc4\xc7\xe8\x8e\x06\x1a\xf2\xf0\xed\xa1\x9b\x03\xc0\x86\x84\xa5\x0f\xd6\x6b\x7e\x9a\xce\x39\xc2\x11\x5a\x9f\xa7\xef\xf9\xa5\x81\xfd\xeb\xf3\xf4\x7b\x7e\xb9\x6e\x44\xd0\x7e\xb7\x33\x65\xf1\x12\x2a\x49\xd6\xd4\x5f\xee\x23\x1c\x35\xdf\xec\x4f\xac\x3c\x84\x8c\x53\x8a\x1e\x33\x23\xd7\x35\x74\x45\xcb\x1b\x05\xf4\xad\x22\xca\x08\xd7\xfd\x74\x4b\x5a\x56\x2f\x21\x2b\xca\x91\x96\x04\xa5\x81\x6b\x03\x29\x15\x2a\xa0\x46\x85\x22\xc3\x36\x26\xad\x21\x81\x5d\x6b\xba\x78\x8a\xd5\xf2\x14\x1c\xcc\x9c\x17\x15\x22\x14\x2c\x43\x60\x36\x37\x94\xcc\x79\x33\x29\xd1\xa1\x7c\x1b\xc2\x83\x04\x8e\xb5\x72\x4d\x26\xcf\x1f\x12\xa5\x83\x7b\x68\xbf\xe1\xc0\x1e\xfa\x1b\x22\xf4\x2d\xe4\x78\x04\xdd\x2a\xd1\xdf\xe0\x8d\x8b\xad\xc9\x5b\x95\x27\xb3\xed\xe9\x0b\x20\x7d\x67\x4b\xe4\x5e\x87\x4a\x42\xa1\x58\xd2\x8a\xf6\x11\x09\x2c\x04\xef\x19\x28\x1e\xa0\x35\x65\xf6\x17\x1d\x28\x17\x19\x47\x9c\x65\x33\xa5\x76\xa8\x5c\x23\x76\x76\x36\x2f\x79\x2e\x64\xc9\x16\x88\x6f\xce\xd8\x22\xe7\x79\x9d\x97\x11\xdc\xfb\xd4\x08\x4d\xb0\x40\x81\xc9\xd8\x02\xa5\x1c\xa5\xab\xe5\x7b\xbe\x40\xe5\xa2\x5a\x22\x2a\x93\x02\xaf\xd1\x3a\x63\x73\x09\x5e\x82\x5c\x9b\xa1\x5d\xcc\xca\x6c\x86\xd8\x7c\xbe\xbc\x58\x03\x68\x01\xb7\x5a\x0a\xb0\xe7\x6b\x9e\xa3\x8b\xb2\x9a\x2d\xcf\x2b\x49\xe0\xba\x5c\x2e\x86\x50\x14\xa3\x21\xbd\xe6\xa4\xfd\x72\xff\xbe\x7a\x56\xa6\xfd\x49\x38\x14\x1f\x9b\x38\xd7\xd1\x5c\x2c\x35\x37\x76\x2b\xae\x02\x0b\x4e\xac\xfd\x0c\x3e\x6b\x52\x4a\x21\xde\x46\x42\xfa\xbe\x59\x54\xb6\x7e\xc4\x7a\x3f\xe2\xb7\x2a\xb1\xe7\x6f\xfa\x4f\xf0\x28\xc0\xe0\xa9\x1d\x83\x07\x7c\x28\x13\x5f\xa2\x72\xf1\x81\xaf\xd6\xdc\xee\x05\xcb\xc5\x87\x97\x3d\x47\xd8\xf9\x69\xab\x01\x02\x3b\x06\x88\x16\x9a\xce\xb1\xf5\x1b\x1c\x0a\x85\xee\x43\xff\xd8\x59\x70\x68\xbf\xf0\x45\xb6\xba\x3c\xab\x76\x78\x0a\x50\x65\xac\x5d\x3e\x6c\xda\xb5\x95\xa7\x5d\x97\x6f\x4d\xa1\x9b\xf3\xcf\x81\xb5\xe5\x88\x2b\x77\xef\x43\x37\xe6\x69\xcd\x48\x53\xd0\xf1\x1f\xbc\xd2\xe3\xb4\x2e\x71\x73\x00\xaa\x3d\x8d\xd5\x97\x81\xac\xb6\xea\x57\x83\x97\xb3\x0c\xd1\xc7\x77\x8b\xb2\x2a\xd9\x5c\x4f\x7d\xd5\xad\xc3\x37\xd9\x8c\x2d\x4e\xf8\x93\x17\x6d\x5a\x54\x99\x79\xcc\xdb\x78\x85\xfc\x5f\x5f\xa5\xcd\x6d\xe4\xfb\xd4\x30\x63\x2d\x0a\x6b\x9b\x17\x4f\xf4\x36\x04\xf0\xf8\xea\x6f\xbb\x36\x54\xd2\xe6\x15\x85\xf8\xff\x96\xb4\x41\x9b\x50\xfd\x19\x33\xd3\xba\x9e\x6a\x93\xe9\xc3\xc0\xa2\xe4\x47\x69\x55\xf0\x79\xfc\xd9\x36\xc3\x48\x64\x8c\x27\x00\x9c\xed\xd9\x8b\x46\x31\x74\x3d\xb1\xd4\x5d\x75\xeb\xae\x54\x5d\x23\x91\x8f\x79\xb9\xae\xf8\xbc\xd1\x62\x33\xc4\x02\x3a\xbf\x5d\x68\x41\xdd\x0e\xba\x10\x03\xad\x4c\xb5\xf6\xa6\x7c\xfb\x66\x32\x51\xd4\xbe\x6b\xdd\xb5\x08\x24\x9b\xa9\x0b\x7c\x87\xb4\xda\x26\xd6\x18\x1c\x76\xcf\x90\x56\x36\x4e\xf5\x2c\x69\x5e\x93\x51\x8c\x3b\xf0\xbf\x2f\xf2\x25\x5a\x5f\xb0\x33\x19\x7e\xcc\xd9\xba\x92\xca\x30\x74\xe1\x95\x5b\x64\x3d\x62\xbb\x02\x73\x19\x7e\x65\xd0\x61\xc8\x28\xbe\xab\xa9\x0f\x4c\xe3\xda\x4c\xf0\x2a\xa6\x7e\x15\x97\x32\xe2\xba\x0c\x33\xb2\x0a\x2d\xcf\xab\x81\x07\x6e\x5c\xae\x5b\x64\x1d\x97\x6b\x97\x59\x67\xc8\x78\xcf\x2f\x65\x0a\xe8\x28\x38\xf4\x89\x5e\x52\x7e\xb0\x14\x68\x79\xa3\x23\x63\xd6\xe8\x43\xf4\x52\x68\xa0\x9a\x04\xac\x96\xeb\x75\x1b\xa6\x43\xce\x43\x08\x88\x61\x5a\x2a\x5b\x34\x03\x55\xcb\xb8\x49\x3d\x5e\x9d\xb2\xf5\xfb\x8e\xc9\xd6\xba\x3b\x99\x74\x54\x54\x18\x62\x3d\xba\xbe\xeb\x74\x5d\x18\xad\x80\xa2\xb1\xa0\xa3\xb2\xef\x40\x67\xbf\x32\x2a\xbe\x28\x13\x11\x95\x84\xac\x6a\xd5\x76\x37\x20\xfb\xc5\x93\xed\xc9\x5e\xd9\xc9\x9e\xbb\xc9\x9e\x3b\xc8\x5e\x6d\x41\xb6\x33\x89\xf4\xba\xce\x22\x2d\x97\x3f\xb6\xcb\x23\x3d\x96\x84\x59\xc2\xaa\xf8\xa6\xd2\x53\x31\x7f\xfb\xe8\xe5\x81\x0a\xd0\x3a\xb9\x98\xa7\x28\x2b\x4e\x0c\xc9\xb5\xcf\xe6\x4c\x10\xb1\xa9\x50\x1f\x8a\x0a\xb8\x26\x2d\x1e\x13\xa0\x26\xb3\xf3\x70\xa1\xa6\x9b\x74\xfb\xdb\x47\x2f\x8d\x19\xb7\x5f\xad\xca\xb3\x39\xbf\xb3\xdb\x12\x91\x6c\xd4\x59\x28\xd2\x7f\xfa\xf3\x2c\x17\xa9\x85\x08\x41\x76\x09\x19\x4a\xb3\xfe\xf3\x40\x2a\x8a\xe5\x6b\x8c\x8e\x44\xbd\x03\xc9\xd5\x47\x52\xc6\xcb\xd5\xa4\x7d\x67\x5d\x3d\x1c\x5f\xa3\x3e\x58\xcf\xcb\x8c\x4f\xbc\x29\x22\x7b\x83\xb7\x30\x1a\xb0\xe4\x8a\x60\xc9\x14\x05\x0e\xb0\xfe\x15\xc1\x06\x53\x14\xed\xd9\x1f\xd2\xb8\xf2\xdc\x83\xaf\xf1\x81\xde\x58\x6b\x61\xe5\xcc\x81\x3e\xe7\xd8\xa2\x81\xbf\x05\x86\xeb\x99\xd3\x08\x5c\x3b\x12\x47\x76\xed\x3e\xde\x02\x83\x79\xd4\xc3\x09\xb9\xb6\x61\xef\x9f\xc4\xad\x36\xde\xe5\x1a\x9c\x6b\x0b\x6b\x47\x17\x6b\x73\x71\x5d\x47\xdb\xd4\x72\xe6\xcf\x6f\x6a\xf5\x52\xe8\x6b\x89\xd9\xef\x86\x64\xda\xcb\xaa\xaf\x25\x77\xbf\x1b\x06\xd3\x36\xab\xfb\xdd\x30\x9a\xaa\x64\xef\x77\x23\xfc\xf1\xed\x94\x06\x9f\x94\x70\xff\x8f\xcc\xb4\xff\xd9\xf2\xe1\xff\xf7\x64\xb6\x87\x97\x0a\xca\x05\xcf\xaf\x37\xc5\xfd\x37\x6c\xcd\xdb\xac\xf5\x6c\xcd\xb5\xb2\xd7\x3e\x71\x66\xc0\x1f\xda\xf2\x26\x0a\xd0\x82\x9d\xf2\xf5\x99\x6e\xa5\x87\x3a\x19\xa2\x8a\x20\x43\xfe\xf7\xd7\x8f\x26\x30\x0f\x50\x14\x34\x4f\xd8\x98\xc0\xbc\x8e\x02\x41\x07\x10\xb5\x89\x82\x03\xf5\x45\xd0\x6f\x88\x0c\x5a\xd0\x12\xbc\x5a\x4e\x29\x7f\xe1\x6b\xc4\xd0\x82\x5f\xcc\x2f\x91\xb4\xb5\xdc\x84\x58\x77\x28\xa8\xf3\x9a\xc7\xe2\xfc\x34\xe5\xab\x8f\x08\x5e\x95\x82\x57\x55\xc4\x07\x9f\x40\x38\x7f\xe0\x6c\x32\x5f\x5e\x40\x0b\xf1\x5f\x53\x83\x6e\xe3\xae\x77\x1b\x56\xa8\xf9\xb2\x69\xf9\x52\x7b\x84\x9a\x3d\xf5\xc0\x2c\x77\xff\x3c\xe2\xf9\x30\x2b\x0b\xbc\xd0\x8b\xbc\xee\x7a\x67\xcd\x69\x70\xf1\x8b\xb2\x13\x51\x89\x1e\x4e\x05\xd5\xe6\x31\x4c\xbd\xaf\x65\x78\xd5\x13\x8a\x45\x6f\x8f\x50\xf7\xf5\x6d\x7d\x66\xde\x97\xd4\x37\x65\x75\x51\xae\x39\xfa\xe1\xd9\xab\x35\x40\x18\x13\x4c\xfd\x50\x8a\x52\x90\x8f\xe8\x81\x90\xaf\xe0\xcb\x1d\x60\x8c\x1a\x49\x58\x51\xf1\x15\x5a\xf0\x13\x56\x95\x8b\x93\x6b\x60\x3c\x80\xe2\x82\xf1\x4a\x04\x07\x8b\x65\x35\xb1\x72\xf5\xf0\x10\x2d\x96\xa3\x91\x2a\xbc\xc9\x22\x19\xfa\x7b\xc3\xdd\x7b\xc6\x6a\x92\xb1\xbf\xd7\x4c\x36\x84\xa4\x8a\x33\x8a\x31\xb5\x36\xb4\xe2\xbc\xd7\xa1\xae\x13\x01\xd8\xa4\xf2\xe0\x87\x6f\x35\xa9\xc0\x76\x02\x8c\xdb\x67\x6c\x0d\xdb\x0b\x5b\xd9\x50\x23\x29\x80\x21\x4c\xa2\x11\x56\xb5\x14\x28\x6a\xb8\xd7\x2c\xfc\x07\x3f\x7c\x7b\x3d\xa2\x97\x7b\x3b\xad\xe0\xd9\x22\x9f\xb0\xc5\xb2\x9a\xf1\x95\x22\xc4\xa5\x06\x6c\x91\xeb\x6a\x20\x7a\x38\xa2\x0a\xad\x9d\xdd\x94\x0c\x19\xd3\x8a\xc6\xf2\x54\xfd\x3f\x4c\x3f\x9e\xbd\xf8\xdc\xea\xf1\xec\xc5\x67\xd2\x8e\x67\x2f\xae\x47\x39\x96\xab\x8e\x6e\x2c\x57\x3b\xa8\xc6\x72\x75\x65\xcd\xf8\x6d\x47\xcd\xf8\xed\x0f\xd6\x8c\xd7\x9f\x5f\x35\x5e\x7f\x36\xdd\x78\x7d\x5d\xca\xb1\xe9\x69\xc7\x66\x27\xf5\xd8\x7c\x82\x7e\xbc\xdb\x51\x3f\xde\xfd\x41\xfa\x01\x9b\xf2\xba\x66\x2c\xe4\xca\xa8\x9a\x10\xce\x79\x51\x6d\x1f\x95\x2d\x40\x27\xe4\x37\xb4\x2c\x1a\x48\xf0\x84\xcd\x75\x29\x03\x00\xbb\x1e\x75\x00\x50\x1d\x85\x80\x5f\x9e\x4c\x48\xe8\xd2\x03\x59\x49\x57\x85\x85\x49\x0f\xc4\x14\x68\x81\xee\x23\x9f\xd8\x76\xba\x34\x4d\x99\xb4\xaa\x72\xff\x3e\x5a\xc0\x16\x79\xa3\x0c\xf2\xe8\x10\x41\x77\xd0\xc2\xf8\x58\xbd\x59\x85\x04\x9c\xa1\xae\x7d\x44\xf5\xe4\xc9\x4d\x90\x0e\x66\xb2\x40\x77\x0c\x2f\x86\x0e\x50\xf7\xb7\xba\x04\xba\xff\x4e\xed\x85\xa5\xfc\x7f\x3b\xf5\x7d\x31\xb1\x4f\x2e\x6a\xed\x7d\x71\x4d\xda\x2b\xe5\xde\xd5\x54\x4d\x79\x6b\x7d\xde\x42\x79\x07\x1e\x13\x40\x5d\x41\x7f\x35\x2b\x68\xe0\x8c\x2b\xb0\x42\xff\x87\x6b\xf0\x8b\x65\xc5\x2a\xfe\xb9\x1d\xf0\x0a\xb0\x5c\x97\x0a\x03\xb4\xeb\x51\x61\x49\x98\xae\xc2\xab\xe5\xa8\xff\x15\x55\x46\xf5\x57\xf5\x08\xf4\x40\x79\xf5\xc5\x9e\x08\x07\xdb\x5f\x5e\x4c\xa2\x60\xa0\x96\x9f\x2a\xb0\x6b\xf2\x39\x7f\x2e\x89\x8d\xb8\x1c\x51\x63\x77\x81\xbd\x18\x08\xec\xc9\x55\x04\xf6\x20\xcf\x3f\x77\xe4\xcb\xf2\xfc\x33\x45\xbe\xf2\xc9\xef\xeb\x98\x33\xe7\xbd\x39\x73\xbe\xd3\x9c\x39\xdf\x7a\xce\xdc\x1f\x11\xf6\x9b\x40\x16\x0e\x8c\x9a\x83\xdf\x8c\xad\x56\x97\xa2\x59\x3d\x86\xc8\x87\xe1\x3b\xc3\x4a\xfb\x3c\xbc\x19\xc6\x30\x90\xda\x6f\x63\x6e\xb4\x2f\x71\x28\x1a\x3e\xd5\xa3\xcb\x6f\xe6\xdd\x95\x07\x0b\xf5\x04\xf8\xb2\xd0\xd7\x36\xd7\xa6\x17\x8e\x57\xcb\x33\xbe\xaa\x2e\xd1\xaf\xea\x89\x61\xa8\x08\xea\xd5\x80\x18\x2c\x2b\x2a\x05\x59\x1f\x98\xe0\xd4\x6e\xa5\x79\x13\xbd\xeb\x5d\xd6\xe5\xc9\xa2\x2c\xca\x8c\x2d\x2a\x94\x42\x79\xb9\xd0\x6c\x03\x90\x3a\x56\x7f\xdb\x75\xe9\x9a\x98\xfa\x97\x6b\x58\x07\x1e\x52\x60\x37\xc7\x0e\xbb\x26\xcf\xce\x84\x5a\xb2\xf9\x5e\x87\xf7\xa3\x8c\x43\x46\x87\xdc\x70\x4e\x03\xbb\x15\x13\x79\x57\xcc\x9f\x60\xab\x17\x3a\xab\xfb\xbd\xe8\xec\xf9\x76\x6d\xf6\x13\x81\xbd\x19\xb4\x17\x7f\xbb\x2e\x6b\x4f\x77\x85\x82\x29\x4e\x30\xc3\x29\xdc\xa9\xc9\x70\x8e\x39\x2e\xf6\x06\x40\xde\xfe\x1b\x75\x75\x8a\xb0\xb7\xf5\xf6\x00\x28\xdd\xb4\x51\xdb\x81\x5b\xbe\x50\x87\x27\xc0\x2d\xd6\x5f\xe4\x7f\x7f\xfb\xcd\x70\x01\x43\xc4\xfd\x8d\x0d\xfc\xe5\x08\x0d\x77\xc1\xf4\x3f\x39\x36\xd7\xd5\x8f\x1a\x32\xfa\x67\x01\xad\x41\x7b\x1f\x80\xb4\xa1\x39\x5f\x9c\x54\x33\x74\x1b\xd1\x2d\x8f\x52\xf7\x1d\xcd\xc3\xe5\xe2\x03\x5f\xd5\x53\x43\xcd\x0d\x2b\xff\x20\x06\xed\xfa\x76\xc0\x56\x8e\xa7\x1e\xb5\x1b\xe9\x76\x76\xe6\x3e\xa2\x57\x5d\x27\x7a\x6b\x8d\x72\x56\x31\xc4\xd6\x3b\xe2\xd9\x7a\x25\xab\xbb\x53\xb8\xd1\x1c\xf4\x41\xb5\x7c\xed\x13\xfb\x56\x08\x14\x7f\xc2\x99\x1d\x85\xab\xab\x54\x86\x93\x3b\x75\xbd\x27\x52\x98\x0d\x91\xb5\x78\x4d\xa7\x78\xa4\xd8\x0c\xb0\x64\x77\xb7\x3e\xbc\xdf\xc5\xed\xbe\xe9\xd5\x6e\xe1\xd5\xad\xde\x0c\x8e\xf0\x8b\xbf\x9a\x86\x83\xb3\xf3\xf5\x6c\x52\x07\x52\x22\x46\x30\xcd\x2b\xcd\xb5\x7b\xb1\x04\x32\x9c\x93\xad\x43\x11\x4d\xc0\xb5\x07\xa9\x61\x4e\xbb\x66\x63\x3d\x48\x32\xb0\x0a\x00\x23\x54\x32\x5b\x9e\xc1\x20\x69\x19\xfb\xd1\x68\xd8\xda\xa8\x3d\x47\xd9\x7c\xb9\x70\xcd\x54\xb6\x55\x69\x80\xd3\xd7\x65\xf8\xd1\xae\xcb\x50\xec\xd4\x65\x1d\x32\x44\x29\x92\xdc\xe6\xe4\xab\xe9\xa4\xeb\x43\xa8\xff\x57\x50\xec\xbf\x4a\xce\x0c\x81\xd6\xbe\x54\xc2\x1b\xba\xd9\xfa\xd4\x98\x1d\x01\xdc\x61\xaa\x37\xd6\x65\x70\x62\x41\xd3\x98\xd0\x45\xc7\x7e\x46\xcd\xe0\x62\x1b\x1b\xb8\x50\x2a\x5f\x83\x7f\x53\xbe\x35\xb1\xdd\xae\xaa\x50\xb9\xb3\xbf\xdc\x84\xc7\xd6\x73\x33\xbd\xd3\x32\xea\x68\xcc\xc7\xb7\x53\x1a\x6e\x73\xde\xe5\xf0\xf6\x5f\xd0\xac\xaa\xce\xd6\x77\x0f\x0f\x4f\xab\xd9\xfa\x20\xe5\x87\xe7\x55\x41\x7f\x5e\xa3\x0f\xe4\x00\x1f\x10\x94\x5e\xa2\xff\x71\xca\xaa\x59\xc9\xd6\x42\x63\xda\x03\x32\x70\x2a\x44\x1e\xf6\x38\x3c\x44\xdf\xf2\x4a\x5e\x87\xe3\x5c\xb0\xbb\x64\xe9\x9c\xaf\xd1\x3f\x14\xa6\x7f\xdc\xf8\x0a\x8e\xf1\xaf\x38\x7f\xd4\x9c\x7f\x19\x9c\xa4\x41\xb7\xa4\xf0\x6e\xa1\x9b\x37\xeb\x9f\xef\xd9\xc1\xa3\x7f\xc8\xee\x68\xc0\x9f\xc2\x0f\x2d\xec\x53\xf5\xbd\x0b\x5a\xfd\x7a\xf3\xa6\xe1\x7c\xce\x51\x87\xc8\xa6\xb2\x93\x8c\x13\x38\x39\xf3\x8f\xa9\x3c\x8d\xff\xc3\x32\xe7\x07\x3f\xaf\xd1\x72\x85\xbe\x91\x47\x69\xca\xa2\xe4\x39\xca\x96\x39\x9f\x02\x14\xb6\xc8\xd1\xf9\x9a\xa3\xb2\x12\xe3\xda\x3f\x04\x1f\xb5\x3e\xa8\x73\x38\x4d\x1f\x4e\xd4\xf7\x6e\x1f\xe4\xaf\xf7\xe4\x99\xa4\xb6\xd9\x41\x53\xfb\x48\x07\xf6\xdb\x6f\xda\xb7\x83\x8b\x72\x91\x8b\xd9\x65\xa7\x8e\x3c\x3a\x24\x68\x41\xfa\xcf\x70\xd8\xe7\xc6\x57\x87\xb7\xef\x5c\xdb\xdf\xed\xc3\x1b\xb2\xb7\xeb\x6a\x55\x2e\x4e\x1e\xaf\x96\xa7\x0f\x67\x6c\xf5\x70\x99\x0b\xc9\xbd\x84\x1f\x0f\x0a\xed\x57\xc5\xfc\x57\xec\x3d\x5f\x48\x1e\xf7\x55\xf6\xec\x7c\x71\x29\xf8\x7b\xe3\xab\xc6\x83\x9d\x67\x6b\x92\x73\xf1\xe3\x44\xe2\x91\x1d\x84\xad\x4d\x38\x7c\x5f\x0f\x81\xf0\x53\xb6\x3c\x5f\x54\x7c\xa5\x56\x2e\xe1\xa7\x79\xed\x2b\x64\xf3\xd6\x59\x40\x29\xdc\x67\xac\xbf\xf0\x4d\xb5\x62\xe2\xcb\xc5\xac\x9c\x73\x34\xa9\xa1\xdd\x57\x40\x24\xea\xaf\xa0\x4d\x0b\x30\x53\xdd\x7b\x50\xd5\x0d\xf6\xf7\x85\xa9\x7f\x05\x32\x95\x95\xbf\x3e\x42\xde\xe6\x5b\xea\x79\x42\xe6\xf2\xa7\xfb\xf0\xd3\x37\x8f\x1f\x8b\x9f\x2c\x98\x04\xbb\x60\xba\xbe\x3e\x5f\xad\x96\x27\xac\xe2\x53\xd0\xba\x6a\xc6\x57\x1c\xee\x79\xa2\x05\xdf\x54\x48\x90\xc0\xb2\x8a\xaf\xa0\x11\x74\x63\x1b\xfa\x80\xc0\x89\xac\x7e\x13\x79\x9b\xc7\x0f\x3d\x6f\x4f\x68\xa8\xb7\xf9\x16\x3e\xfe\x2a\x9c\xf3\x7c\x79\xd1\xe2\x87\x66\x5f\x49\xce\xcb\xa1\x7c\xa2\xba\x28\x00\xf8\x8f\x1f\xef\xc1\xd5\x4c\x6f\x0f\xed\x23\x0d\x32\x14\xec\xd7\x19\x87\x14\xf6\x36\x0a\x56\x5d\x3d\x5f\x9c\xb2\x2a\x9b\xf1\xbc\xc5\x77\x0f\x2d\x17\xf3\x4b\xc4\xce\xce\x38\xf4\xbb\x5c\x83\x01\xa2\xf3\x45\x59\x4d\xc5\x44\x33\x63\x6b\x0e\xb3\x4d\xc1\x88\x06\x52\x53\x47\x30\xa9\xaa\xcf\x45\x35\x50\xc5\x50\xcf\xb4\xaf\x67\xac\x5c\x0d\x7b\x06\xfd\x52\xb4\x7e\xa5\x58\x77\xe7\x8e\xa2\xfd\x46\xbf\x03\x96\x96\xa2\xa2\xf8\xbf\xf2\xf7\xb2\x56\x6d\x8d\x57\x31\x06\xbe\x00\x63\x80\x51\xb8\xb5\x85\x46\xcb\x65\xdc\xd2\x55\xf2\x72\x91\xf3\x0d\x3a\x42\x77\xb0\x51\xed\x1b\x3b\xba\x75\x4b\x53\xfe\xfd\x7d\xd9\xcc\xa2\xfc\x80\xe7\x0d\x54\x79\xdb\x57\x76\xa1\x4a\x8f\x85\xc4\x25\x67\xe4\xaf\x77\x8e\x6a\xf1\xdf\xd3\xf8\x85\xf6\x8f\x0c\xfe\xa3\x06\xf4\xf5\xd7\x08\x7b\xb5\x02\xa1\xdf\x94\x0d\x29\x91\xd4\x94\x48\x65\x45\xbf\xa1\x8e\x1e\x36\xcc\xdf\x02\x11\x00\xb4\x09\xa9\x61\x7e\x36\xe3\xd9\xfb\x97\x19\x9b\xb3\xd5\xff\x12\xad\x26\x42\x0e\xcf\x97\xe5\x42\x9e\xa6\x06\x06\x34\x3f\x75\x2d\xbe\xfd\x59\x5a\x7d\xcb\x9c\x6a\xb6\x5a\x5e\xa0\x47\xab\xd5\x72\x35\x81\x5e\xdd\x7a\x22\x42\xa1\x56\x35\xff\xbe\x7f\x0b\xed\xb7\x00\x0e\xaa\xa5\xf4\xac\x13\x1c\xed\x1d\x54\xcb\xbf\x9f\x9d\xf1\xd5\x43\xb6\xe6\x93\x3d\xb4\x2f\x01\x08\x95\x5f\x2c\x2b\xa1\xe0\x40\xac\xe4\xcb\x2d\x51\x58\x77\xf4\xe3\x67\x18\x09\x5a\x3e\x41\x54\x2d\x22\xf1\x96\x1d\x53\xb9\xcd\xa6\x06\x27\xc9\x65\x83\x34\x26\x3a\x03\xbf\xae\xdb\x48\x89\xc2\x52\xe5\x86\x7a\x7b\x7d\xb9\x48\x83\x78\x58\x37\x34\x89\x45\x03\x7b\x53\x29\xe7\xe3\xc7\x54\xf9\x3a\xe5\xe6\xf0\x9d\xf4\xb2\xe2\x68\xcd\xff\xeb\x9c\x2f\x32\x70\x74\x76\x42\x5b\x1c\xb5\xea\xc0\x40\x78\x79\x9a\x2e\xe7\x8d\x21\xd9\x30\x53\xaf\x8b\x99\x0c\x31\x37\x90\xc6\x99\x14\x49\x06\x61\xc5\xa0\x87\x5e\x43\x52\x73\xf0\xd8\x40\x04\xb8\x61\x9d\x08\x7f\x48\x84\x43\xe1\xef\xed\x48\x24\x26\x92\x4a\x4f\x51\xf9\xc8\xeb\x80\xd8\x3f\xb2\x68\x4d\xb4\x45\x67\x1e\x79\x83\xce\x04\x9f\xc4\x51\x4c\x15\xb1\xb1\x24\xf6\xf1\x96\xc4\x62\xb2\x6b\xa7\xda\x9a\x26\xaa\xba\x1d\xed\x5a\x40\xa3\x9b\x00\xa1\x6f\x12\x22\xf4\x57\xe3\x44\x3f\x68\x6a\x80\x8a\xd0\x7d\x18\x5c\x0d\xa2\xa6\xb6\xfe\xe8\xa0\xd2\x54\xad\x7f\x10\x42\x90\xde\x6a\xcb\xc1\xa5\xed\xb1\x8e\x58\x1f\x65\x34\x90\xfb\x47\x0e\xd3\xef\x79\xf4\xb6\xd9\xe7\x0a\x84\x1b\xde\xaf\x38\xcb\x1f\x2e\x17\x55\xb9\x38\x87\xcb\xb3\x20\xfd\xd6\x15\x09\x4a\xbe\x83\xbe\x7f\x7d\x04\x64\x3d\x14\x81\x85\x61\x34\xb8\xf5\xdd\xe2\x03\x9b\x97\x39\x54\x92\xdc\xbe\xa5\xba\xd5\xf0\xbb\x8b\x05\x49\x80\xb0\x50\xf0\xa6\xc1\xf3\x56\x99\x89\x68\xda\xfc\xb8\xbf\x2f\x82\xf1\xda\x43\xf5\xc0\xdc\x94\x6e\x44\x06\x82\xc2\x4b\xfe\xaa\x39\x43\x63\x6d\xff\x71\x43\xd8\xe1\x21\xfa\xae\x40\x17\x1c\x89\x78\xed\xfc\x0c\x89\x48\x75\x8a\xca\xea\xff\xfe\xef\xff\x53\x0f\x4b\x3a\x08\xa0\xf8\x86\xa5\xe7\x83\x8a\xb7\x06\xce\x5f\x6a\xef\x4b\xb0\x82\x49\xab\xe5\xa2\x32\xd6\xd5\x90\xe8\x5f\x7c\xfd\x4b\x60\x50\xdf\xa1\xac\x3e\x41\x54\x5d\x48\x47\x43\xa9\x2b\xce\x16\x6c\x0e\x97\x1f\x1a\x3e\xbe\xe0\x2c\x47\x45\xb9\x5a\x57\x35\x97\xa0\x5b\xbb\x8b\x79\x38\xba\xa1\xc9\x62\x39\x64\xef\x7a\xaf\xd6\x09\x89\xe8\xa6\x92\xbf\xf2\xac\x1a\xad\x0d\x7f\x6b\x5a\x87\x63\x58\x0f\xce\xa3\x5a\xa1\x1e\xd6\xa0\x40\x2c\xe8\xc8\x62\x30\xf7\xfa\xfe\x40\x07\x86\xe5\x34\x03\x72\xee\x34\xd2\x35\x05\x60\x8d\xf6\xb6\xea\xab\xf9\xa8\x6e\x00\xbf\x83\x0a\xd6\x61\xbd\xec\xbb\xdf\xe7\xed\x29\xbb\x44\xe5\x22\x9b\x9f\xc3\x24\x44\x4c\x2e\xf4\x29\x8d\x89\xcb\x8f\x6b\xee\x3c\xda\x81\x3b\xa0\xca\x57\x63\xa0\xa7\xe6\x69\x04\xce\x26\x49\x5c\x3a\x43\x7d\x1b\x43\x3d\x08\x5e\x24\xc3\xc6\xe2\x83\xcf\xc9\xf3\xe1\x08\xdf\xe7\x28\x55\x1c\x7d\x7c\xbd\x1c\x05\x97\x71\x45\xa6\xc7\xc0\x74\x6f\xd3\x67\xbb\xb7\xf1\x1e\xee\xa1\xdf\x80\x23\x13\x49\x83\xfc\xb5\x91\x47\x60\x95\x07\xcc\xa8\x0c\x73\x0c\xec\xe9\x53\x30\xb3\x24\x6a\x7e\x1a\xa5\xf0\xf7\x57\x8f\xef\x50\x94\xc3\x4a\x19\xcf\x1b\xcf\x5b\xbb\x4d\x75\x03\xab\xf9\x0e\x0e\x4d\xfb\x0e\xfe\xe7\x5e\x2f\x26\x51\xb1\x46\x3b\x1a\x4b\xfa\x1a\x78\xdd\x90\x44\xab\x56\x7b\x35\xc0\xa2\x3b\x40\x2d\x28\xd1\x7c\x6c\xbb\xfa\xd3\x09\x77\xda\x75\xa2\xea\xf4\x4c\x8b\x46\x26\xd5\xe9\x19\x3a\xea\x8d\x25\x7b\xe8\x2f\x47\x47\xd2\x29\xf7\xa3\x13\xb5\x89\x51\x9d\x9e\xf5\xe3\x0c\x6d\x82\xde\xd6\xde\xfb\x9c\x8b\x6f\x82\xad\xe8\x08\x08\xbc\xf5\x81\xaf\xd6\xe5\x72\x71\xeb\x2e\xba\x05\x8b\xbe\xb7\xa6\xe2\x57\x49\xcf\xad\xbb\x5a\x54\x08\xbf\xcb\xee\xaa\xdf\xe5\x97\x1b\x5f\x7d\x54\x8b\x74\x2f\x97\xa7\x1c\x3d\x78\xfa\x2d\x4a\xcf\xcb\x79\x8e\x96\x67\x55\x79\x5a\xfe\xc2\x57\xeb\x29\x9a\x97\xef\x39\x5a\x1d\xfc\xbc\x9e\xca\x29\x31\xac\xb4\xaf\xcf\x78\x56\x16\x65\x26\x8c\x37\x2f\x41\xe0\x67\xac\xaa\xf8\x6a\xb1\x06\x78\xd0\xa8\x9a\x71\x54\x2c\xe7\xf3\xe5\x45\xb9\x38\xb9\x2b\xd7\x3c\x85\xfa\xf5\xee\x45\xa2\x5b\xb5\xd2\xdc\x92\x8b\xbb\x9d\x0a\x07\xec\x34\xef\xad\xa2\x36\x57\x24\x45\xd9\x8d\xaf\xa4\xb8\xd4\xa5\xc9\x66\x99\xbb\x3b\x80\x89\x3e\x83\xec\x40\x38\xed\xec\xa2\xb7\x6a\xfc\x17\xed\xfb\xc1\x62\x99\xf3\x57\x97\x67\xbc\x0d\xe6\xda\xb5\x6a\x35\xf1\x28\x17\xfa\xba\xf1\x8b\x72\x71\xb2\xfc\x9f\x2f\xd1\x07\xef\x80\x1e\x78\x30\x3d\x6f\x5b\x68\x77\x49\x1b\x62\x94\x6b\xac\x21\xb1\xd5\xc5\x8c\xcd\x7b\x90\xe2\x03\xef\x8e\x5c\x88\x59\xd5\x67\xa3\xe4\x2d\x46\xf5\xdb\x8c\xad\x9f\x5d\x2c\x9e\xd7\x47\x60\x8e\x54\xa5\x83\xee\xef\x50\xbd\xd9\x22\x81\xac\x71\x92\x29\xb5\xc7\xe8\x56\x97\xfb\x43\xa2\x1c\x2e\x12\xef\x09\xde\xe8\xbc\x7a\xf3\x5e\x26\x30\x14\x35\xe0\x73\x67\xf1\xab\xd7\xaf\x17\xb3\x72\xb1\x14\xbd\x62\xe8\x82\xa7\x48\x5d\x54\x55\xab\xd6\x07\x4a\xa1\x15\x4f\x3e\xde\x50\x57\x54\x61\xdb\xe4\xe3\xf4\xd7\x8f\x6f\xa7\x34\xda\x66\x4b\x64\x70\x63\xf7\xf5\xd3\x27\xc7\x55\x75\xf6\x42\x0c\x19\xeb\xaa\x81\xf6\xd7\xb4\x3c\x91\x87\x59\x0e\x7e\x5e\xff\x75\x1b\xc8\xb7\xce\xd7\x1c\x26\x6c\x59\x75\xeb\xde\x8d\x21\xa2\x6f\xca\x93\x1f\x00\xe0\x3d\xd1\xe1\x9f\xd7\x33\xe1\x94\xcb\x93\xc5\x72\xc5\xef\xce\xcb\x05\xbf\xd1\xa0\xbe\xe0\xa9\xbf\x15\x4a\x21\xa4\x1f\x79\x2a\xc7\x26\x79\xcd\xf8\xd6\xc1\xe1\xbc\x4c\x0f\x05\x08\xe1\x9c\x6f\x1c\x1e\xa2\x7c\xb9\xa8\xd0\xf2\x03\x5f\xad\xca\x9c\xd7\x1b\x0e\xf5\xfe\xc6\x0d\xed\x0a\xb2\xda\x39\x10\x0e\xee\x56\x73\xa0\x01\xf6\x23\x3a\x15\x0e\x24\xca\x6e\x2d\xa1\x20\xb0\x4d\xa6\x57\x01\xe2\xee\xdd\xf8\x68\xe0\x86\x2c\x51\x1b\x5b\x35\xc5\x7f\xbd\x4b\xc8\xc7\xb7\x82\x0b\xd3\x37\x92\x0b\x6f\xf7\x6e\x1c\x1e\xfe\x7f\x68\xbd\x3c\x5f\x65\xfc\x29\x3b\x3b\x2b\x17\x27\x7f\x7f\xf1\xe4\x48\x14\xde\x99\xc3\x21\xd2\x9f\xd7\x07\xa7\xec\xec\xc6\xff\x0b\x00\x00\xff\xff\x8e\xc8\x22\x88\x33\x2c\x06\x00") func web3JsBytes() ([]byte, error) { return bindataRead( @@ -105,7 +114,7 @@ func web3Js() (*asset, error) { } info := bindataFileInfo{name: "web3.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe1, 0x52, 0x59, 0xdc, 0x62, 0x13, 0x62, 0xd3, 0x4c, 0x5, 0x90, 0x78, 0x59, 0x0, 0xbc, 0xf7, 0x73, 0xa4, 0x6d, 0x49, 0xdd, 0x77, 0x5a, 0x40, 0x5d, 0x5f, 0xe0, 0xc1, 0xb7, 0x0, 0xeb, 0x1d}} + a := &asset{bytes: bytes, info: info} return a, nil } @@ -113,8 +122,8 @@ func web3Js() (*asset, error) { // It returns an error if the asset could not be found or // could not be loaded. func Asset(name string) ([]byte, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[canonicalName]; ok { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { a, err := f() if err != nil { return nil, fmt.Errorf("can't read Asset %s by error: %v", name, err) @@ -124,12 +133,6 @@ func Asset(name string) ([]byte, error) { return nil, fmt.Errorf("not found Asset %s", name) } -// AssetString returns the asset contents as a string (instead of a []byte). -func AssetString(name string) (string, error) { - data, err := Asset(name) - return string(data), err -} - // MustAsset is like Asset but panics when Asset would return an error. // It simplifies safe initialization of global variables. func MustAsset(name string) []byte { @@ -141,18 +144,12 @@ func MustAsset(name string) []byte { return a } -// MustAssetString is like AssetString but panics when Asset would return an -// error. It simplifies safe initialization of global variables. -func MustAssetString(name string) string { - return string(MustAsset(name)) -} - // AssetInfo loads and returns the asset info for the given name. // It returns an error if the asset could not be found or // could not be loaded. func AssetInfo(name string) (os.FileInfo, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[canonicalName]; ok { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { a, err := f() if err != nil { return nil, fmt.Errorf("can't read AssetInfo %s by error: %v", name, err) @@ -162,33 +159,6 @@ func AssetInfo(name string) (os.FileInfo, error) { return nil, fmt.Errorf("not found AssetInfo %s", name) } -// AssetDigest returns the digest of the file with the given name. It returns an -// error if the asset could not be found or the digest could not be loaded. -func AssetDigest(name string) ([sha256.Size]byte, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[canonicalName]; ok { - a, err := f() - if err != nil { - return [sha256.Size]byte{}, fmt.Errorf("can't read AssetDigest %s by error: %v", name, err) - } - return a.digest, nil - } - return [sha256.Size]byte{}, fmt.Errorf("not found AssetDigest %s", name) -} - -// Digests returns a map of all known files and their checksums. -func Digests() (map[string][sha256.Size]byte, error) { - mp := make(map[string][sha256.Size]byte, len(_bindata)) - for name := range _bindata { - a, err := _bindata[name]() - if err != nil { - return nil, err - } - mp[name] = a.digest - } - return mp, nil -} - // AssetNames returns the names of the assets. func AssetNames() []string { names := make([]string, 0, len(_bindata)) @@ -204,9 +174,6 @@ var _bindata = map[string]func() (*asset, error){ "web3.js": web3Js, } -// AssetDebug is true if the assets were built with the debug flag enabled. -const AssetDebug = false - // AssetDir returns the file names below a certain // directory embedded in the file by go-bindata. // For example if you run go-bindata on data/... and data contains the @@ -218,24 +185,24 @@ const AssetDebug = false // a.png // b.png // -// then AssetDir("data") would return []string{"foo.txt", "img"}, -// AssetDir("data/img") would return []string{"a.png", "b.png"}, -// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and +// then AssetDir("data") would return []string{"foo.txt", "img"} +// AssetDir("data/img") would return []string{"a.png", "b.png"} +// AssetDir("foo.txt") and AssetDir("notexist") would return an error // AssetDir("") will return []string{"data"}. func AssetDir(name string) ([]string, error) { node := _bintree if len(name) != 0 { - canonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(canonicalName, "/") + cannonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(cannonicalName, "/") for _, p := range pathList { node = node.Children[p] if node == nil { - return nil, fmt.Errorf("not found Asset %s", name) + return nil, fmt.Errorf("Asset %s not found", name) } } } if node.Func != nil { - return nil, fmt.Errorf("not found Asset %s", name) + return nil, fmt.Errorf("Asset %s not found", name) } rv := make([]string, 0, len(node.Children)) for childName := range node.Children { @@ -254,7 +221,7 @@ var _bintree = &bintree{nil, map[string]*bintree{ "web3.js": {web3Js, map[string]*bintree{}}, }} -// RestoreAsset restores an asset under the given directory. +// RestoreAsset restores an asset under the given directory func RestoreAsset(dir, name string) error { data, err := Asset(name) if err != nil { @@ -268,14 +235,18 @@ func RestoreAsset(dir, name string) error { if err != nil { return err } - err = os.WriteFile(_filePath(dir, name), data, info.Mode()) + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) if err != nil { return err } - return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) + err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) + if err != nil { + return err + } + return nil } -// RestoreAssets restores an asset under the given directory recursively. +// RestoreAssets restores an asset under the given directory recursively func RestoreAssets(dir, name string) error { children, err := AssetDir(name) // File @@ -293,6 +264,6 @@ func RestoreAssets(dir, name string) error { } func _filePath(dir, name string) string { - canonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...) + cannonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) } diff --git a/internal/jsre/deps/web3.js b/internal/jsre/deps/web3.js index 5c7236ed1442..b6a646aede5e 100644 --- a/internal/jsre/deps/web3.js +++ b/internal/jsre/deps/web3.js @@ -3766,7 +3766,7 @@ var inputTransactionFormatter = function (options){ options.to = inputAddressFormatter(options.to); } - ['gasPrice', 'gas', 'value', 'nonce'].filter(function (key) { + ['maxFeePerGas', 'maxPriorityFeePerGas', 'gasPrice', 'gas', 'value', 'nonce'].filter(function (key) { return options[key] !== undefined; }).forEach(function(key){ options[key] = utils.fromDecimal(options[key]); @@ -3790,6 +3790,12 @@ var outputTransactionFormatter = function (tx){ tx.nonce = utils.toDecimal(tx.nonce); tx.gas = utils.toDecimal(tx.gas); tx.gasPrice = utils.toBigNumber(tx.gasPrice); + if(tx.maxFeePerGas !== undefined) { + tx.maxFeePerGas = utils.toBigNumber(tx.maxFeePerGas); + } + if(tx.maxPriorityFeePerGas !== undefined) { + tx.maxPriorityFeePerGas = utils.toBigNumber(tx.maxPriorityFeePerGas); + } tx.value = utils.toBigNumber(tx.value); return tx; }; @@ -3828,6 +3834,9 @@ var outputTransactionReceiptFormatter = function (receipt){ var outputBlockFormatter = function(block) { // transform to number + if (block.baseFeePerGas !== undefined) { + block.baseFeePerGas = utils.toBigNumber(block.baseFeePerGas); + } block.gasLimit = utils.toDecimal(block.gasLimit); block.gasUsed = utils.toDecimal(block.gasUsed); block.size = utils.toDecimal(block.size); diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 1921ad609961..d0350ff8a7ad 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -557,6 +557,11 @@ web3._extend({ return formatted; } }), + new web3._extend.Property({ + name: 'maxPriorityFeePerGas', + getter: 'eth_maxPriorityFeePerGas', + outputFormatter: web3._extend.utils.toBigNumber + }), ] }); ` diff --git a/les/api_backend.go b/les/api_backend.go index c5873dc5dbfe..a7a3e8e38004 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -269,8 +269,8 @@ func (b *LesApiBackend) ProtocolVersion() int { return b.eth.LesVersion() + 10000 } -func (b *LesApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { - return b.gpo.SuggestPrice(ctx) +func (b *LesApiBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { + return b.gpo.SuggestTipCap(ctx) } func (b *LesApiBackend) ChainDb() ethdb.Database { @@ -303,6 +303,10 @@ func (b *LesApiBackend) ServiceFilter(ctx context.Context, session *bloombits.Ma } } +func (b *LesApiBackend) CurrentHeader() *types.Header { + return b.eth.blockchain.CurrentHeader() +} + // func (b *LesApiBackend) GetIPCClient() (*ethclient.Client, error) { func (b *LesApiBackend) GetIPCClient() (bind.ContractBackend, error) { // func (b *LesApiBackend) GetIPCClient() (bind.ContractBackend, error) { From 64f7389c652913af270506c94f158adbc26b674f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 22 May 2024 17:45:22 +0800 Subject: [PATCH 085/479] params: print right Eip1559Block for ChainConfig --- params/config.go | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/params/config.go b/params/config.go index a7ac5ff3a4dc..2b28c9159149 100644 --- a/params/config.go +++ b/params/config.go @@ -546,6 +546,26 @@ func (c *ChainConfig) String() string { default: engine = "unknown" } + berlinBlock := common.BerlinBlock + if c.BerlinBlock != nil { + berlinBlock = c.BerlinBlock + } + londonBlock := common.LondonBlock + if c.LondonBlock != nil { + londonBlock = c.LondonBlock + } + mergeBlock := common.MergeBlock + if c.MergeBlock != nil { + mergeBlock = c.MergeBlock + } + shanghaiBlock := common.ShanghaiBlock + if c.ShanghaiBlock != nil { + shanghaiBlock = c.ShanghaiBlock + } + eip1559Block := common.Eip1559Block + if c.Eip1559Block != nil { + eip1559Block = c.Eip1559Block + } return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Istanbul: %v BerlinBlock: %v LondonBlock: %v MergeBlock: %v ShanghaiBlock: %v Eip1559Block: %v Engine: %v}", c.ChainId, c.HomesteadBlock, @@ -557,11 +577,11 @@ func (c *ChainConfig) String() string { c.ByzantiumBlock, c.ConstantinopleBlock, common.TIPXDCXCancellationFee, - common.BerlinBlock, - common.LondonBlock, - common.MergeBlock, - common.ShanghaiBlock, - common.Eip1559Block, + berlinBlock, + londonBlock, + mergeBlock, + shanghaiBlock, + eip1559Block, engine, ) } From 5a31888b19aab7096106138ba27d4c3ac67eb592 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 23 May 2024 16:33:59 +0800 Subject: [PATCH 086/479] core, eth, miner: enforce configured mining reward post 1559 too (#22995) --- core/tx_pool.go | 29 ++++++++++++++++++++++++----- core/tx_pool_test.go | 2 +- core/types/transaction.go | 8 ++++++++ eth/api_backend.go | 2 +- eth/helper_test.go | 2 +- eth/protocol.go | 2 +- eth/sync.go | 2 +- miner/worker.go | 4 ++-- 8 files changed, 39 insertions(+), 12 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index 6b010c292af2..16b5c60a9b9d 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -29,6 +29,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/prque" "github.com/XinFinOrg/XDPoSChain/consensus" + "github.com/XinFinOrg/XDPoSChain/consensus/misc" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/event" @@ -537,13 +538,30 @@ func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common // Pending retrieves all currently processable transactions, grouped by origin // account and sorted by nonce. The returned transaction set is a copy and can be // freely modified by calling code. -func (pool *TxPool) Pending() (map[common.Address]types.Transactions, error) { +// +// The enforceTips parameter can be used to do an extra filtering on the pending +// transactions and only return those whose **effective** tip is large enough in +// the next pending execution environment. +func (pool *TxPool) Pending(enforceTips bool) (map[common.Address]types.Transactions, error) { pool.mu.Lock() defer pool.mu.Unlock() pending := make(map[common.Address]types.Transactions) for addr, list := range pool.pending { - pending[addr] = list.Flatten() + txs := list.Flatten() + + // If the miner requests tip enforcement, cap the lists now + if enforceTips && !pool.locals.contains(addr) { + for i, tx := range txs { + if tx.EffectiveTipIntCmp(pool.gasPrice, pool.priced.urgent.baseFee) < 0 { + txs = txs[:i] + break + } + } + } + if len(txs) > 0 { + pending[addr] = txs + } } return pending, nil } @@ -619,7 +637,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { if tx.Tip().BitLen() > 256 { return ErrTipVeryHigh } - // Ensure feeCap is less than or equal to tip. + // Ensure feeCap is greater than or equal to tip. if tx.FeeCapIntCmp(tx.Tip()) < 0 { return ErrTipAboveFeeCap } @@ -1269,8 +1287,9 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt // because of another transaction (e.g. higher gas price). if reset != nil { pool.demoteUnexecutables() - if reset.newHead != nil { - pool.priced.SetBaseFee(reset.newHead.BaseFee) + if reset.newHead != nil && pool.chainconfig.IsEIP1559(new(big.Int).Add(reset.newHead.Number, big.NewInt(1))) { + pendingBaseFee := misc.CalcBaseFee(pool.chainconfig, reset.newHead) + pool.priced.SetBaseFee(pendingBaseFee) } } // Ensure pool.queue and pool.pending sizes stay within the configured limits. diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index 3a3766c26cbf..45b615689035 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -272,7 +272,7 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) { trigger = true <-pool.requestReset(nil, nil) - _, err := pool.Pending() + _, err := pool.Pending(false) if err != nil { t.Fatalf("Could not fetch pending transactions: %v", err) } diff --git a/core/types/transaction.go b/core/types/transaction.go index 453d0ba43c86..68d67ea5f66a 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -384,6 +384,14 @@ func (tx *Transaction) EffectiveTipCmp(other *Transaction, baseFee *big.Int) int return tx.EffectiveTipValue(baseFee).Cmp(other.EffectiveTipValue(baseFee)) } +// EffectiveTipIntCmp compares the effective tip of a transaction to the given tip. +func (tx *Transaction) EffectiveTipIntCmp(other *big.Int, baseFee *big.Int) int { + if baseFee == nil { + return tx.TipIntCmp(other) + } + return tx.EffectiveTipValue(baseFee).Cmp(other) +} + // Hash returns the transaction hash. func (tx *Transaction) Hash() common.Hash { if hash := tx.hash.Load(); hash != nil { diff --git a/eth/api_backend.go b/eth/api_backend.go index fb3cefd45572..cd395b364f39 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -298,7 +298,7 @@ func (b *EthApiBackend) SendLendingTx(ctx context.Context, signedTx *types.Lendi } func (b *EthApiBackend) GetPoolTransactions() (types.Transactions, error) { - pending, err := b.eth.txPool.Pending() + pending, err := b.eth.txPool.Pending(false) if err != nil { return nil, err } diff --git a/eth/helper_test.go b/eth/helper_test.go index 4963093c1aa0..de175bbcee3d 100644 --- a/eth/helper_test.go +++ b/eth/helper_test.go @@ -111,7 +111,7 @@ func (p *testTxPool) AddRemotes(txs []*types.Transaction) []error { } // Pending returns all the transactions known to the pool -func (p *testTxPool) Pending() (map[common.Address]types.Transactions, error) { +func (p *testTxPool) Pending(enforceTips bool) (map[common.Address]types.Transactions, error) { p.lock.RLock() defer p.lock.RUnlock() diff --git a/eth/protocol.go b/eth/protocol.go index eb7297a28c10..3876133e830e 100644 --- a/eth/protocol.go +++ b/eth/protocol.go @@ -108,7 +108,7 @@ type txPool interface { // Pending should return pending transactions. // The slice should be modifiable by the caller. - Pending() (map[common.Address]types.Transactions, error) + Pending(enforceTips bool) (map[common.Address]types.Transactions, error) // SubscribeNewTxsEvent should return an event subscription of // NewTxsEvent and send events to the given channel. diff --git a/eth/sync.go b/eth/sync.go index cbe6d421c8bb..76edb614fbdb 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -45,7 +45,7 @@ type txsync struct { // syncTransactions starts sending all currently pending transactions to the given peer. func (pm *ProtocolManager) syncTransactions(p *peer) { var txs types.Transactions - pending, _ := pm.txpool.Pending() + pending, _ := pm.txpool.Pending(false) for _, batch := range pending { txs = append(txs, batch...) } diff --git a/miner/worker.go b/miner/worker.go index 3cd8fae9406b..c076518a60d9 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -616,7 +616,7 @@ func (w *worker) commitNewWork() { Time: big.NewInt(tstamp), } // Set baseFee if we are on an EIP-1559 chain - header.BaseFee = misc.CalcBaseFee(self.config, header) + header.BaseFee = misc.CalcBaseFee(w.config, header) // Only set the coinbase if we are mining (avoid spurious block rewards) if atomic.LoadInt32(&w.mining) == 1 { @@ -679,7 +679,7 @@ func (w *worker) commitNewWork() { log.Error("[commitNewWork] fail to check if block is epoch switch block when fetching pending transactions", "BlockNum", header.Number, "Hash", header.Hash()) } if !isEpochSwitchBlock { - pending, err := w.eth.TxPool().Pending() + pending, err := w.eth.TxPool().Pending(true) if err != nil { log.Error("Failed to fetch pending transactions", "err", err) return From 4c096de9b08b283167bc9b372f505c060fc2d0b2 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 24 May 2024 12:03:03 +0800 Subject: [PATCH 087/479] all: rename internal 1559 gas fields (#23010) --- accounts/abi/bind/backends/simulated.go | 4 +- core/error.go | 8 +- core/state_processor_test.go | 22 ++--- core/state_transition.go | 57 ++++++----- core/token_validator.go | 4 +- core/tx_list.go | 18 ++-- core/tx_pool.go | 18 ++-- core/tx_pool_test.go | 24 ++--- core/types/access_list_tx.go | 5 +- core/types/dynamic_fee_tx.go | 22 ++--- core/types/legacy_tx.go | 4 +- core/types/transaction.go | 121 ++++++++++++------------ core/types/transaction_marshalling.go | 8 +- core/types/transaction_signing.go | 4 +- eth/gasprice/gasprice.go | 6 +- eth/gasprice/gasprice_test.go | 14 +-- interfaces.go | 5 +- internal/ethapi/api.go | 12 +-- internal/ethapi/transaction_args.go | 87 +++++++++-------- 19 files changed, 220 insertions(+), 223 deletions(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index deb9023f52d4..58e73976122c 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -525,8 +525,8 @@ func (m callMsg) Nonce() uint64 { return 0 } func (m callMsg) CheckNonce() bool { return false } func (m callMsg) To() *common.Address { return m.CallMsg.To } func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } -func (m callMsg) FeeCap() *big.Int { return m.CallMsg.FeeCap } -func (m callMsg) Tip() *big.Int { return m.CallMsg.Tip } +func (m callMsg) GasFeeCap() *big.Int { return m.CallMsg.GasFeeCap } +func (m callMsg) GasTipCap() *big.Int { return m.CallMsg.GasTipCap } func (m callMsg) Gas() uint64 { return m.CallMsg.Gas } func (m callMsg) Value() *big.Int { return m.CallMsg.Value } func (m callMsg) Data() []byte { return m.CallMsg.Data } diff --git a/core/error.go b/core/error.go index 192d41778f15..327e68278b97 100644 --- a/core/error.go +++ b/core/error.go @@ -56,17 +56,17 @@ var ( // ErrTipAboveFeeCap is a sanity error to ensure no one is able to specify a // transaction with a tip higher than the total fee cap. - ErrTipAboveFeeCap = errors.New("tip higher than fee cap") + ErrTipAboveFeeCap = errors.New("max priority fee per gas higher than max fee per gas") // ErrTipVeryHigh is a sanity error to avoid extremely big numbers specified // in the tip field. - ErrTipVeryHigh = errors.New("tip higher than 2^256-1") + ErrTipVeryHigh = errors.New("max priority fee per gas higher than 2^256-1") // ErrFeeCapVeryHigh is a sanity error to avoid extremely big numbers specified // in the fee cap field. - ErrFeeCapVeryHigh = errors.New("fee cap higher than 2^256-1") + ErrFeeCapVeryHigh = errors.New("max fee per gas higher than 2^256-1") // ErrFeeCapTooLow is returned if the transaction fee cap is less than the // the base fee of the block. - ErrFeeCapTooLow = errors.New("fee cap less than block base fee") + ErrFeeCapTooLow = errors.New("max fee per gas less than block base fee") ) diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 1327471c6a90..c07d3c50656d 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -63,14 +63,14 @@ func TestStateProcessorErrors(t *testing.T) { } return signedTx } - var mkDynamicTx = func(nonce uint64, to common.Address, gasLimit uint64, tip, feeCap *big.Int) *types.Transaction { + var mkDynamicTx = func(nonce uint64, to common.Address, gasLimit uint64, gasTipCap, gasFeeCap *big.Int) *types.Transaction { tx, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{ - Nonce: nonce, - Tip: tip, - FeeCap: feeCap, - Gas: gasLimit, - To: &to, - Value: big.NewInt(0), + Nonce: nonce, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Gas: gasLimit, + To: &to, + Value: big.NewInt(0), }), signer, testKey) return tx } @@ -148,25 +148,25 @@ func TestStateProcessorErrors(t *testing.T) { txs: []*types.Transaction{ mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(0), big.NewInt(0)), }, - want: "fee cap less than block base fee: address xdc71562b71999873DB5b286dF957af199Ec94617F7, feeCap: 0 baseFee: 875000000", + want: "fee cap less than block base fee: address xdc71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas: 0 baseFee: 875000000", }, { // ErrTipVeryHigh txs: []*types.Transaction{ mkDynamicTx(0, common.Address{}, params.TxGas, tooBigNumber, big.NewInt(1)), }, - want: "tip higher than 2^256-1: address xdc71562b71999873DB5b286dF957af199Ec94617F7, tip bit length: 257", + want: "max priority fee per gas higher than 2^256-1: address xdc71562b71999873DB5b286dF957af199Ec94617F7, maxPriorityFeePerGas bit length: 257", }, { // ErrFeeCapVeryHigh txs: []*types.Transaction{ mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(1), tooBigNumber), }, - want: "fee cap higher than 2^256-1: address xdc71562b71999873DB5b286dF957af199Ec94617F7, feeCap bit length: 257", + want: "max fee per gas higher than 2^256-1: address xdc71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas bit length: 257", }, { // ErrTipAboveFeeCap txs: []*types.Transaction{ mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(2), big.NewInt(1)), }, - want: "tip higher than fee cap: address xdc71562b71999873DB5b286dF957af199Ec94617F7, tip: 1, feeCap: 2", + want: "max priority fee per gas higher than max fee per gas: address xdc71562b71999873DB5b286dF957af199Ec94617F7, maxPriorityFeePerGas: 2, maxFeePerGas: 1", }, { // ErrInsufficientFunds // Available balance: 1000000000000000000 diff --git a/core/state_transition.go b/core/state_transition.go index 04b8bcdf28a8..f64af1c750ac 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -58,8 +58,8 @@ type StateTransition struct { msg Message gas uint64 gasPrice *big.Int - feeCap *big.Int - tip *big.Int + gasFeeCap *big.Int + gasTipCap *big.Int initialGas uint64 value *big.Int data []byte @@ -74,8 +74,8 @@ type Message interface { To() *common.Address GasPrice() *big.Int - FeeCap() *big.Int - Tip() *big.Int + GasFeeCap() *big.Int + GasTipCap() *big.Int Gas() uint64 Value() *big.Int @@ -126,15 +126,15 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, // NewStateTransition initialises and returns a new state transition object. func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition { return &StateTransition{ - gp: gp, - evm: evm, - msg: msg, - gasPrice: msg.GasPrice(), - feeCap: msg.FeeCap(), - tip: msg.Tip(), - value: msg.Value(), - data: msg.Data(), - state: evm.StateDB, + gp: gp, + evm: evm, + msg: msg, + gasPrice: msg.GasPrice(), + gasFeeCap: msg.GasFeeCap(), + gasTipCap: msg.GasTipCap(), + value: msg.Value(), + data: msg.Data(), + state: evm.StateDB, } } @@ -183,9 +183,9 @@ func (st *StateTransition) buyGas() error { balanceTokenFee := st.balanceTokenFee() if balanceTokenFee == nil { balanceCheck := mgval - if st.feeCap != nil { + if st.gasFeeCap != nil { balanceCheck = new(big.Int).SetUint64(st.msg.Gas()) - balanceCheck = balanceCheck.Mul(balanceCheck, st.feeCap) + balanceCheck = balanceCheck.Mul(balanceCheck, st.gasFeeCap) } if have, want := st.state.GetBalance(st.msg.From()), balanceCheck; have.Cmp(want) < 0 { return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), have, want) @@ -221,25 +221,25 @@ func (st *StateTransition) preCheck() error { msg.From().Hex(), stNonce) } } - // Make sure that transaction feeCap is greater than the baseFee (post london) + // Make sure that transaction gasFeeCap is greater than the baseFee (post london) if st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber) { - if l := st.feeCap.BitLen(); l > 256 { - return fmt.Errorf("%w: address %v, feeCap bit length: %d", ErrFeeCapVeryHigh, + if l := st.gasFeeCap.BitLen(); l > 256 { + return fmt.Errorf("%w: address %v, maxFeePerGas bit length: %d", ErrFeeCapVeryHigh, msg.From().Hex(), l) } - if l := st.tip.BitLen(); l > 256 { - return fmt.Errorf("%w: address %v, tip bit length: %d", ErrTipVeryHigh, + if l := st.gasTipCap.BitLen(); l > 256 { + return fmt.Errorf("%w: address %v, maxPriorityFeePerGas bit length: %d", ErrTipVeryHigh, msg.From().Hex(), l) } - if st.feeCap.Cmp(st.tip) < 0 { - return fmt.Errorf("%w: address %v, tip: %s, feeCap: %s", ErrTipAboveFeeCap, - msg.From().Hex(), st.feeCap, st.tip) + if st.gasFeeCap.Cmp(st.gasTipCap) < 0 { + return fmt.Errorf("%w: address %v, maxPriorityFeePerGas: %s, maxFeePerGas: %s", ErrTipAboveFeeCap, + msg.From().Hex(), st.gasTipCap, st.gasFeeCap) } // This will panic if baseFee is nil, but basefee presence is verified // as part of header validation. - if st.feeCap.Cmp(st.evm.Context.BaseFee) < 0 { - return fmt.Errorf("%w: address %v, feeCap: %s baseFee: %s", ErrFeeCapTooLow, - msg.From().Hex(), st.feeCap, st.evm.Context.BaseFee) + if st.gasFeeCap.Cmp(st.evm.Context.BaseFee) < 0 { + return fmt.Errorf("%w: address %v, maxFeePerGas: %s baseFee: %s", ErrFeeCapTooLow, + msg.From().Hex(), st.gasFeeCap, st.evm.Context.BaseFee) } } return st.buyGas() @@ -275,12 +275,11 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG } msg := st.msg sender := st.from() // err checked in preCheck - homestead := st.evm.ChainConfig().IsHomestead(st.evm.Context.BlockNumber) eip3529 := st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber) contractCreation := msg.To() == nil - // Pay intrinsic gas + // Check clauses 4-5, subtract intrinsic gas if everything is correct gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead) if err != nil { return nil, 0, false, err, nil @@ -339,7 +338,7 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG } else { effectiveTip := st.gasPrice if st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber) { - effectiveTip = cmath.BigMin(st.tip, new(big.Int).Sub(st.feeCap, st.evm.Context.BaseFee)) + effectiveTip = cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee)) } st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip)) } diff --git a/core/token_validator.go b/core/token_validator.go index dae490946553..c8c3ba4ad806 100644 --- a/core/token_validator.go +++ b/core/token_validator.go @@ -48,8 +48,8 @@ func (m callMsg) Nonce() uint64 { return 0 } func (m callMsg) CheckNonce() bool { return false } func (m callMsg) To() *common.Address { return m.CallMsg.To } func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } -func (m callMsg) FeeCap() *big.Int { return m.CallMsg.FeeCap } -func (m callMsg) Tip() *big.Int { return m.CallMsg.Tip } +func (m callMsg) GasFeeCap() *big.Int { return m.CallMsg.GasFeeCap } +func (m callMsg) GasTipCap() *big.Int { return m.CallMsg.GasTipCap } func (m callMsg) Gas() uint64 { return m.CallMsg.Gas } func (m callMsg) Value() *big.Int { return m.CallMsg.Value } func (m callMsg) Data() []byte { return m.CallMsg.Data } diff --git a/core/tx_list.go b/core/tx_list.go index fd2985af7a95..8e92debe4934 100644 --- a/core/tx_list.go +++ b/core/tx_list.go @@ -285,13 +285,13 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran return false, nil } if old != nil { - if old.FeeCapCmp(tx) >= 0 || old.TipCmp(tx) >= 0 { + if old.GasFeeCapCmp(tx) >= 0 || old.GasTipCapCmp(tx) >= 0 { return false, nil } // thresholdFeeCap = oldFC * (100 + priceBump) / 100 a := big.NewInt(100 + int64(priceBump)) - aFeeCap := new(big.Int).Mul(a, old.FeeCap()) - aTip := a.Mul(a, old.Tip()) + aFeeCap := new(big.Int).Mul(a, old.GasFeeCap()) + aTip := a.Mul(a, old.GasTipCap()) // thresholdTip = oldTip * (100 + priceBump) / 100 b := big.NewInt(100) @@ -301,7 +301,7 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran // Have to ensure that either the new fee cap or tip is higher than the // old ones as well as checking the percentage threshold to ensure that // this is accurate for low (Wei-level) gas price replacements - if tx.FeeCapIntCmp(thresholdFeeCap) < 0 || tx.TipIntCmp(thresholdTip) < 0 { + if tx.GasFeeCapIntCmp(thresholdFeeCap) < 0 || tx.GasTipCapIntCmp(thresholdTip) < 0 { return false, nil } } @@ -428,7 +428,7 @@ func (l *txList) LastElement() *types.Transaction { // priceHeap is a heap.Interface implementation over transactions for retrieving // price-sorted transactions to discard when the pool fills up. If baseFee is set // then the heap is sorted based on the effective tip based on the given base fee. -// If baseFee is nil then the sorting is based on feeCap. +// If baseFee is nil then the sorting is based on gasFeeCap. type priceHeap struct { baseFee *big.Int // heap should always be re-sorted after baseFee is changed list []*types.Transaction @@ -451,16 +451,16 @@ func (h *priceHeap) Less(i, j int) bool { func (h *priceHeap) cmp(a, b *types.Transaction) int { if h.baseFee != nil { // Compare effective tips if baseFee is specified - if c := a.EffectiveTipCmp(b, h.baseFee); c != 0 { + if c := a.EffectiveGasTipCmp(b, h.baseFee); c != 0 { return c } } // Compare fee caps if baseFee is not specified or effective tips are equal - if c := a.FeeCapCmp(b); c != 0 { + if c := a.GasFeeCapCmp(b); c != 0 { return c } // Compare tips if effective tips and fee caps are equal - return a.TipCmp(b) + return a.GasTipCapCmp(b) } func (h *priceHeap) Push(x interface{}) { @@ -483,7 +483,7 @@ func (h *priceHeap) Pop() interface{} { // will be considered for tracking, sorting, eviction, etc. // // Two heaps are used for sorting: the urgent heap (based on effective tip in the next -// block) and the floating heap (based on feeCap). Always the bigger heap is chosen for +// block) and the floating heap (based on gasFeeCap). Always the bigger heap is chosen for // eviction. Transactions evicted from the urgent heap are first demoted into the floating heap. // In some cases (during a congestion, when blocks are full) the urgent heap can provide // better candidates for inclusion while in other cases (at the top of the baseFee peak) diff --git a/core/tx_pool.go b/core/tx_pool.go index 16b5c60a9b9d..cef3c78542bc 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -475,7 +475,7 @@ func (pool *TxPool) SetGasPrice(price *big.Int) { pool.gasPrice = price // if the min miner fee increased, remove transactions below the new threshold if price.Cmp(old) > 0 { - // pool.priced is sorted by FeeCap, so we have to iterate through pool.all instead + // pool.priced is sorted by GasFeeCap, so we have to iterate through pool.all instead drop := pool.all.RemotesBelowTip(price) for _, tx := range drop { pool.removeTx(tx.Hash(), false) @@ -631,14 +631,14 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { return ErrGasLimit } // Sanity check for extremely large numbers - if tx.FeeCap().BitLen() > 256 { + if tx.GasFeeCap().BitLen() > 256 { return ErrFeeCapVeryHigh } - if tx.Tip().BitLen() > 256 { + if tx.GasTipCap().BitLen() > 256 { return ErrTipVeryHigh } - // Ensure feeCap is greater than or equal to tip. - if tx.FeeCapIntCmp(tx.Tip()) < 0 { + // Ensure gasFeeCap is greater than or equal to gasTipCap. + if tx.GasFeeCapIntCmp(tx.GasTipCap()) < 0 { return ErrTipAboveFeeCap } // Make sure the transaction is signed properly. @@ -647,7 +647,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { return ErrInvalidSender } // Drop non-local transactions under our own minimal accepted gas price or tip - if !local && tx.TipIntCmp(pool.gasPrice) < 0 { + if !local && tx.GasTipCapIntCmp(pool.gasPrice) < 0 { if !tx.IsSpecialTransaction() || (pool.IsSigner != nil && !pool.IsSigner(from)) { return ErrUnderpriced } @@ -759,7 +759,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e if uint64(pool.all.Slots()+numSlots(tx)) > pool.config.GlobalSlots+pool.config.GlobalQueue { // If the new transaction is underpriced, don't accept it if !isLocal && pool.priced.Underpriced(tx) { - log.Trace("Discarding underpriced transaction", "hash", hash, "tip", tx.Tip(), "feeCap", tx.FeeCap()) + log.Trace("Discarding underpriced transaction", "hash", hash, "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap()) underpricedTxMeter.Mark(1) return false, ErrUnderpriced } @@ -776,7 +776,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e } // Kick out the underpriced remote transactions. for _, tx := range drop { - log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "tip", tx.Tip(), "feeCap", tx.FeeCap()) + log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap()) underpricedTxMeter.Mark(1) pool.removeTx(tx.Hash(), false) } @@ -1934,7 +1934,7 @@ func (t *txLookup) RemoteToLocals(locals *accountSet) int { func (t *txLookup) RemotesBelowTip(threshold *big.Int) types.Transactions { found := make(types.Transactions, 0, 128) t.Range(func(hash common.Hash, tx *types.Transaction, local bool) bool { - if tx.TipIntCmp(threshold) < 0 { + if tx.GasTipCapIntCmp(threshold) < 0 { found = append(found, tx) } return true diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index 45b615689035..3e0bcb7b055a 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -116,8 +116,8 @@ func dynamicFeeTx(nonce uint64, gaslimit uint64, gasFee *big.Int, tip *big.Int, tx, _ := types.SignNewTx(key, types.LatestSignerForChainID(params.TestChainConfig.ChainId), &types.DynamicFeeTx{ ChainID: params.TestChainConfig.ChainId, Nonce: nonce, - Tip: tip, - FeeCap: gasFee, + GasTipCap: tip, + GasFeeCap: gasFee, Gas: gaslimit, To: &common.Address{}, Value: big.NewInt(100), @@ -2177,17 +2177,17 @@ func TestTransactionReplacementDynamicFee(t *testing.T) { defer sub.Unsubscribe() // Add pending transactions, ensuring the minimum price bump is enforced for replacement (for ultra low prices too) - feeCap := int64(100) - feeCapThreshold := (feeCap * (100 + int64(testTxPoolConfig.PriceBump))) / 100 - tip := int64(60) - tipThreshold := (tip * (100 + int64(testTxPoolConfig.PriceBump))) / 100 + gasFeeCap := int64(100) + feeCapThreshold := (gasFeeCap * (100 + int64(testTxPoolConfig.PriceBump))) / 100 + gasTipCap := int64(60) + tipThreshold := (gasTipCap * (100 + int64(testTxPoolConfig.PriceBump))) / 100 // Run the following identical checks for both the pending and queue pools: // 1. Send initial tx => accept // 2. Don't bump tip or fee cap => discard // 3. Bump both more than min => accept // 4. Check events match expected (2 new executable txs during pending, 0 during queue) - // 5. Send new tx with larger tip and feeCap => accept + // 5. Send new tx with larger tip and gasFeeCap => accept // 6. Bump tip max allowed so it's still underpriced => discard // 7. Bump fee cap max allowed so it's still underpriced => discard // 8. Bump tip min for acceptance => discard @@ -2227,27 +2227,27 @@ func TestTransactionReplacementDynamicFee(t *testing.T) { t.Fatalf("cheap %s replacement event firing failed: %v", stage, err) } // 5. Send new tx with larger tip and feeCap => accept - tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCap), big.NewInt(tip), key) + tx = dynamicFeeTx(nonce, 100000, big.NewInt(gasFeeCap), big.NewInt(gasTipCap), key) if err := pool.addRemoteSync(tx); err != nil { t.Fatalf("failed to add original proper %s transaction: %v", stage, err) } // 6. Bump tip max allowed so it's still underpriced => discard - tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCap), big.NewInt(tipThreshold-1), key) + tx = dynamicFeeTx(nonce, 100000, big.NewInt(gasFeeCap), big.NewInt(tipThreshold-1), key) if err := pool.AddRemote(tx); err != ErrReplaceUnderpriced { t.Fatalf("original proper %s transaction replacement error mismatch: have %v, want %v", stage, err, ErrReplaceUnderpriced) } // 7. Bump fee cap max allowed so it's still underpriced => discard - tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCapThreshold-1), big.NewInt(tip), key) + tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCapThreshold-1), big.NewInt(gasTipCap), key) if err := pool.AddRemote(tx); err != ErrReplaceUnderpriced { t.Fatalf("original proper %s transaction replacement error mismatch: have %v, want %v", stage, err, ErrReplaceUnderpriced) } // 8. Bump tip min for acceptance => accept - tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCap), big.NewInt(tipThreshold), key) + tx = dynamicFeeTx(nonce, 100000, big.NewInt(gasFeeCap), big.NewInt(tipThreshold), key) if err := pool.AddRemote(tx); err != ErrReplaceUnderpriced { t.Fatalf("original proper %s transaction replacement error mismatch: have %v, want %v", stage, err, ErrReplaceUnderpriced) } // 9. Bump fee cap min for acceptance => accept - tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCapThreshold), big.NewInt(tip), key) + tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCapThreshold), big.NewInt(gasTipCap), key) if err := pool.AddRemote(tx); err != ErrReplaceUnderpriced { t.Fatalf("original proper %s transaction replacement error mismatch: have %v, want %v", stage, err, ErrReplaceUnderpriced) } diff --git a/core/types/access_list_tx.go b/core/types/access_list_tx.go index 2131a743f113..265fd7545812 100644 --- a/core/types/access_list_tx.go +++ b/core/types/access_list_tx.go @@ -94,7 +94,6 @@ func (tx *AccessListTx) copy() TxData { } // accessors for innerTx. - func (tx *AccessListTx) txType() byte { return AccessListTxType } func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID } func (tx *AccessListTx) protected() bool { return true } @@ -102,8 +101,8 @@ func (tx *AccessListTx) accessList() AccessList { return tx.AccessList } func (tx *AccessListTx) data() []byte { return tx.Data } func (tx *AccessListTx) gas() uint64 { return tx.Gas } func (tx *AccessListTx) gasPrice() *big.Int { return tx.GasPrice } -func (tx *AccessListTx) tip() *big.Int { return tx.GasPrice } -func (tx *AccessListTx) feeCap() *big.Int { return tx.GasPrice } +func (tx *AccessListTx) gasTipCap() *big.Int { return tx.GasPrice } +func (tx *AccessListTx) gasFeeCap() *big.Int { return tx.GasPrice } func (tx *AccessListTx) value() *big.Int { return tx.Value } func (tx *AccessListTx) nonce() uint64 { return tx.Nonce } func (tx *AccessListTx) to() *common.Address { return tx.To } diff --git a/core/types/dynamic_fee_tx.go b/core/types/dynamic_fee_tx.go index ef4c66f2f58c..7dfb2ad9f37a 100644 --- a/core/types/dynamic_fee_tx.go +++ b/core/types/dynamic_fee_tx.go @@ -25,8 +25,8 @@ import ( type DynamicFeeTx struct { ChainID *big.Int Nonce uint64 - Tip *big.Int - FeeCap *big.Int + GasTipCap *big.Int + GasFeeCap *big.Int Gas uint64 To *common.Address `rlp:"nil"` // nil means contract creation Value *big.Int @@ -50,8 +50,8 @@ func (tx *DynamicFeeTx) copy() TxData { AccessList: make(AccessList, len(tx.AccessList)), Value: new(big.Int), ChainID: new(big.Int), - Tip: new(big.Int), - FeeCap: new(big.Int), + GasTipCap: new(big.Int), + GasFeeCap: new(big.Int), V: new(big.Int), R: new(big.Int), S: new(big.Int), @@ -63,11 +63,11 @@ func (tx *DynamicFeeTx) copy() TxData { if tx.ChainID != nil { cpy.ChainID.Set(tx.ChainID) } - if tx.Tip != nil { - cpy.Tip.Set(tx.Tip) + if tx.GasTipCap != nil { + cpy.GasTipCap.Set(tx.GasTipCap) } - if tx.FeeCap != nil { - cpy.FeeCap.Set(tx.FeeCap) + if tx.GasFeeCap != nil { + cpy.GasFeeCap.Set(tx.GasFeeCap) } if tx.V != nil { cpy.V.Set(tx.V) @@ -88,9 +88,9 @@ func (tx *DynamicFeeTx) protected() bool { return true } func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList } func (tx *DynamicFeeTx) data() []byte { return tx.Data } func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas } -func (tx *DynamicFeeTx) feeCap() *big.Int { return tx.FeeCap } -func (tx *DynamicFeeTx) tip() *big.Int { return tx.Tip } -func (tx *DynamicFeeTx) gasPrice() *big.Int { return tx.FeeCap } +func (tx *DynamicFeeTx) gasFeeCap() *big.Int { return tx.GasFeeCap } +func (tx *DynamicFeeTx) gasTipCap() *big.Int { return tx.GasTipCap } +func (tx *DynamicFeeTx) gasPrice() *big.Int { return tx.GasFeeCap } func (tx *DynamicFeeTx) value() *big.Int { return tx.Value } func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce } func (tx *DynamicFeeTx) to() *common.Address { return tx.To } diff --git a/core/types/legacy_tx.go b/core/types/legacy_tx.go index 5593f87b3100..819909a2a375 100644 --- a/core/types/legacy_tx.go +++ b/core/types/legacy_tx.go @@ -98,8 +98,8 @@ func (tx *LegacyTx) accessList() AccessList { return nil } func (tx *LegacyTx) data() []byte { return tx.Data } func (tx *LegacyTx) gas() uint64 { return tx.Gas } func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice } -func (tx *LegacyTx) tip() *big.Int { return tx.GasPrice } -func (tx *LegacyTx) feeCap() *big.Int { return tx.GasPrice } +func (tx *LegacyTx) gasTipCap() *big.Int { return tx.GasPrice } +func (tx *LegacyTx) gasFeeCap() *big.Int { return tx.GasPrice } func (tx *LegacyTx) value() *big.Int { return tx.Value } func (tx *LegacyTx) nonce() uint64 { return tx.Nonce } func (tx *LegacyTx) to() *common.Address { return tx.To } diff --git a/core/types/transaction.go b/core/types/transaction.go index 68d67ea5f66a..f39a96c734e3 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -34,18 +34,19 @@ import ( //go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go var ( - ErrInvalidSig = errors.New("invalid transaction v, r, s values") - ErrUnexpectedProtection = errors.New("transaction type does not supported EIP-155 protected signatures") - ErrInvalidTxType = errors.New("transaction type not valid in this context") - ErrTxTypeNotSupported = errors.New("transaction type not supported") - ErrGasFeeCapTooLow = errors.New("fee cap less than base fee") - errShortTypedTx = errors.New("typed transaction too short") - errInvalidYParity = errors.New("'yParity' field must be 0 or 1") - errVYParityMismatch = errors.New("'v' and 'yParity' fields do not match") - errVYParityMissing = errors.New("missing 'yParity' or 'v' field in transaction") - ErrFeeCapTooLow = errors.New("fee cap less than base fee") - errEmptyTypedTx = errors.New("empty typed transaction bytes") - errNoSigner = errors.New("missing signing methods") + ErrInvalidSig = errors.New("invalid transaction v, r, s values") + ErrUnexpectedProtection = errors.New("transaction type does not supported EIP-155 protected signatures") + ErrInvalidTxType = errors.New("transaction type not valid in this context") + ErrTxTypeNotSupported = errors.New("transaction type not supported") + ErrGasFeeCapTooLow = errors.New("fee cap less than base fee") + errShortTypedTx = errors.New("typed transaction too short") + errInvalidYParity = errors.New("'yParity' field must be 0 or 1") + errVYParityMismatch = errors.New("'v' and 'yParity' fields do not match") + errVYParityMissing = errors.New("missing 'yParity' or 'v' field in transaction") + errEmptyTypedTx = errors.New("empty typed transaction bytes") + errNoSigner = errors.New("missing signing methods") + ErrFeeCapTooLow = errors.New("fee cap less than base fee") + skipNonceDestinationAddress = map[common.Address]bool{ common.XDCXAddrBinary: true, common.TradingStateAddrBinary: true, @@ -91,8 +92,8 @@ type TxData interface { data() []byte gas() uint64 gasPrice() *big.Int - tip() *big.Int - feeCap() *big.Int + gasTipCap() *big.Int + gasFeeCap() *big.Int value() *big.Int nonce() uint64 to() *common.Address @@ -283,11 +284,11 @@ func (tx *Transaction) Gas() uint64 { return tx.inner.gas() } // GasPrice returns the gas price of the transaction. func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.inner.gasPrice()) } -// Tip returns the tip per gas of the transaction. -func (tx *Transaction) Tip() *big.Int { return new(big.Int).Set(tx.inner.tip()) } +// GasTipCap returns the gasTipCap per gas of the transaction. +func (tx *Transaction) GasTipCap() *big.Int { return new(big.Int).Set(tx.inner.gasTipCap()) } -// FeeCap returns the fee cap per gas of the transaction. -func (tx *Transaction) FeeCap() *big.Int { return new(big.Int).Set(tx.inner.feeCap()) } +// GasFeeCap returns the fee cap per gas of the transaction. +func (tx *Transaction) GasFeeCap() *big.Int { return new(big.Int).Set(tx.inner.gasFeeCap()) } // Value returns the ether amount of the transaction. func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.inner.value()) } @@ -334,62 +335,62 @@ func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) { return tx.inner.rawSignatureValues() } -// FeeCapCmp compares the fee cap of two transactions. -func (tx *Transaction) FeeCapCmp(other *Transaction) int { - return tx.inner.feeCap().Cmp(other.inner.feeCap()) +// GasFeeCapCmp compares the fee cap of two transactions. +func (tx *Transaction) GasFeeCapCmp(other *Transaction) int { + return tx.inner.gasFeeCap().Cmp(other.inner.gasFeeCap()) } -// FeeCapIntCmp compares the fee cap of the transaction against the given fee cap. -func (tx *Transaction) FeeCapIntCmp(other *big.Int) int { - return tx.inner.feeCap().Cmp(other) +// GasFeeCapIntCmp compares the fee cap of the transaction against the given fee cap. +func (tx *Transaction) GasFeeCapIntCmp(other *big.Int) int { + return tx.inner.gasFeeCap().Cmp(other) } -// TipCmp compares the tip of two transactions. -func (tx *Transaction) TipCmp(other *Transaction) int { - return tx.inner.tip().Cmp(other.inner.tip()) +// GasTipCapCmp compares the gasTipCap of two transactions. +func (tx *Transaction) GasTipCapCmp(other *Transaction) int { + return tx.inner.gasTipCap().Cmp(other.inner.gasTipCap()) } -// TipIntCmp compares the tip of the transaction against the given tip. -func (tx *Transaction) TipIntCmp(other *big.Int) int { - return tx.inner.tip().Cmp(other) +// GasTipCapIntCmp compares the gasTipCap of the transaction against the given gasTipCap. +func (tx *Transaction) GasTipCapIntCmp(other *big.Int) int { + return tx.inner.gasTipCap().Cmp(other) } -// EffectiveTip returns the effective miner tip for the given base fee. -// Note: if the effective tip is negative, this method returns both error -// the actual negative value, _and_ ErrFeeCapTooLow -func (tx *Transaction) EffectiveTip(baseFee *big.Int) (*big.Int, error) { +// EffectiveGasTip returns the effective miner gasTipCap for the given base fee. +// Note: if the effective gasTipCap is negative, this method returns both error +// the actual negative value, _and_ ErrGasFeeCapTooLow +func (tx *Transaction) EffectiveGasTip(baseFee *big.Int) (*big.Int, error) { if baseFee == nil { - return tx.Tip(), nil + return tx.GasTipCap(), nil } var err error - feeCap := tx.FeeCap() - if feeCap.Cmp(baseFee) == -1 { - err = ErrFeeCapTooLow + gasFeeCap := tx.GasFeeCap() + if gasFeeCap.Cmp(baseFee) == -1 { + err = ErrGasFeeCapTooLow } - return math.BigMin(tx.Tip(), feeCap.Sub(feeCap, baseFee)), err + return math.BigMin(tx.GasTipCap(), gasFeeCap.Sub(gasFeeCap, baseFee)), err } -// EffectiveTipValue is identical to EffectiveTip, but does not return an -// error in case the effective tip is negative -func (tx *Transaction) EffectiveTipValue(baseFee *big.Int) *big.Int { - effectiveTip, _ := tx.EffectiveTip(baseFee) +// EffectiveGasTipValue is identical to EffectiveGasTip, but does not return an +// error in case the effective gasTipCap is negative +func (tx *Transaction) EffectiveGasTipValue(baseFee *big.Int) *big.Int { + effectiveTip, _ := tx.EffectiveGasTip(baseFee) return effectiveTip } -// EffectiveTipCmp compares the effective tip of two transactions assuming the given base fee. -func (tx *Transaction) EffectiveTipCmp(other *Transaction, baseFee *big.Int) int { +// EffectiveGasTipCmp compares the effective gasTipCap of two transactions assuming the given base fee. +func (tx *Transaction) EffectiveGasTipCmp(other *Transaction, baseFee *big.Int) int { if baseFee == nil { - return tx.TipCmp(other) + return tx.GasTipCapCmp(other) } - return tx.EffectiveTipValue(baseFee).Cmp(other.EffectiveTipValue(baseFee)) + return tx.EffectiveGasTipValue(baseFee).Cmp(other.EffectiveGasTipValue(baseFee)) } -// EffectiveTipIntCmp compares the effective tip of a transaction to the given tip. +// EffectiveTipIntCmp compares the effective gasTipCap of a transaction to the given gasTipCap. func (tx *Transaction) EffectiveTipIntCmp(other *big.Int, baseFee *big.Int) int { if baseFee == nil { - return tx.TipIntCmp(other) + return tx.GasTipCapIntCmp(other) } - return tx.EffectiveTipValue(baseFee).Cmp(other) + return tx.EffectiveGasTipValue(baseFee).Cmp(other) } // Hash returns the transaction hash. @@ -426,8 +427,8 @@ func (tx *Transaction) AsMessage(s Signer, balanceFee, blockNumber, baseFee *big nonce: tx.Nonce(), gasLimit: tx.Gas(), gasPrice: new(big.Int).Set(tx.GasPrice()), - feeCap: new(big.Int).Set(tx.FeeCap()), - tip: new(big.Int).Set(tx.Tip()), + gasFeeCap: new(big.Int).Set(tx.GasFeeCap()), + gasTipCap: new(big.Int).Set(tx.GasTipCap()), to: tx.To(), amount: tx.Value(), data: tx.Data(), @@ -448,7 +449,7 @@ func (tx *Transaction) AsMessage(s Signer, balanceFee, blockNumber, baseFee *big } } else if baseFee != nil { // If baseFee provided, set gasPrice to effectiveGasPrice. - msg.gasPrice = math.BigMin(msg.gasPrice.Add(msg.tip, baseFee), msg.feeCap) + msg.gasPrice = math.BigMin(msg.gasPrice.Add(msg.gasTipCap, baseFee), msg.gasFeeCap) } var err error @@ -813,15 +814,15 @@ type Message struct { amount *big.Int gasLimit uint64 gasPrice *big.Int - feeCap *big.Int - tip *big.Int + gasFeeCap *big.Int + gasTipCap *big.Int data []byte accessList AccessList checkNonce bool balanceTokenFee *big.Int } -func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice, feeCap, tip *big.Int, data []byte, accessList AccessList, checkNonce bool, balanceTokenFee *big.Int, number *big.Int) Message { +func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice, gasFeeCap, gasTipCap *big.Int, data []byte, accessList AccessList, checkNonce bool, balanceTokenFee *big.Int, number *big.Int) Message { if balanceTokenFee != nil { gasPrice = common.GetGasPrice(number) } @@ -832,8 +833,8 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *b amount: amount, gasLimit: gasLimit, gasPrice: gasPrice, - feeCap: feeCap, - tip: tip, + gasFeeCap: gasFeeCap, + gasTipCap: gasTipCap, data: data, accessList: accessList, checkNonce: checkNonce, @@ -845,8 +846,8 @@ func (m Message) From() common.Address { return m.from } func (m Message) BalanceTokenFee() *big.Int { return m.balanceTokenFee } func (m Message) To() *common.Address { return m.to } func (m Message) GasPrice() *big.Int { return m.gasPrice } -func (m Message) FeeCap() *big.Int { return m.feeCap } -func (m Message) Tip() *big.Int { return m.tip } +func (m Message) GasFeeCap() *big.Int { return m.gasFeeCap } +func (m Message) GasTipCap() *big.Int { return m.gasTipCap } func (m Message) Value() *big.Int { return m.amount } func (m Message) Gas() uint64 { return m.gasLimit } func (m Message) Nonce() uint64 { return m.nonce } diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go index 80b74d4b41ef..569350784d05 100644 --- a/core/types/transaction_marshalling.go +++ b/core/types/transaction_marshalling.go @@ -86,8 +86,8 @@ func (t *Transaction) MarshalJSON() ([]byte, error) { enc.AccessList = &tx.AccessList enc.Nonce = (*hexutil.Uint64)(&tx.Nonce) enc.Gas = (*hexutil.Uint64)(&tx.Gas) - enc.MaxFeePerGas = (*hexutil.Big)(tx.FeeCap) - enc.MaxPriorityFeePerGas = (*hexutil.Big)(tx.Tip) + enc.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap) + enc.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap) enc.Value = (*hexutil.Big)(tx.Value) enc.Data = (*hexutil.Bytes)(&tx.Data) enc.To = t.To() @@ -227,11 +227,11 @@ func (t *Transaction) UnmarshalJSON(input []byte) error { if dec.MaxPriorityFeePerGas == nil { return errors.New("missing required field 'maxPriorityFeePerGas' for txdata") } - itx.Tip = (*big.Int)(dec.MaxPriorityFeePerGas) + itx.GasTipCap = (*big.Int)(dec.MaxPriorityFeePerGas) if dec.MaxFeePerGas == nil { return errors.New("missing required field 'maxFeePerGas' for txdata") } - itx.FeeCap = (*big.Int)(dec.MaxFeePerGas) + itx.GasFeeCap = (*big.Int)(dec.MaxFeePerGas) if dec.Gas == nil { return errors.New("missing required field 'gas' for txdata") } diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index 2715e718f98d..b835e45af1ba 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -226,8 +226,8 @@ func (s londonSigner) Hash(tx *Transaction) common.Hash { []interface{}{ s.chainId, tx.Nonce(), - tx.Tip(), - tx.FeeCap(), + tx.GasTipCap(), + tx.GasFeeCap(), tx.Gas(), tx.To(), tx.Value(), diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index e868e6fdcf0a..4b58d2109125 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -219,8 +219,8 @@ func (s *txSorter) Swap(i, j int) { func (s *txSorter) Less(i, j int) bool { // It's okay to discard the error because a tx would never be // accepted into a block with an invalid effective tip. - tip1, _ := s.txs[i].EffectiveTip(s.baseFee) - tip2, _ := s.txs[j].EffectiveTip(s.baseFee) + tip1, _ := s.txs[i].EffectiveGasTip(s.baseFee) + tip2, _ := s.txs[j].EffectiveGasTip(s.baseFee) return tip1.Cmp(tip2) < 0 } @@ -245,7 +245,7 @@ func (gpo *Oracle) getBlockValues(ctx context.Context, signer types.Signer, bloc var prices []*big.Int for _, tx := range sorter.txs { - tip, _ := tx.EffectiveTip(block.BaseFee()) + tip, _ := tx.EffectiveGasTip(block.BaseFee()) if ignoreUnder != nil && tip.Cmp(ignoreUnder) == -1 { continue } diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index 0c200e095bcb..bdd04b2fa209 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -80,13 +80,13 @@ func newTestBackend(t *testing.T, eip1559Block *big.Int) *testBackend { var tx *types.Transaction if eip1559Block != nil && b.Number().Cmp(eip1559Block) >= 0 { txdata := &types.DynamicFeeTx{ - ChainID: gspec.Config.ChainId, - Nonce: b.TxNonce(addr), - To: &common.Address{}, - Gas: 30000, - FeeCap: big.NewInt(100 * params.GWei), - Tip: big.NewInt(int64(i+1) * params.GWei), - Data: []byte{}, + ChainID: gspec.Config.ChainId, + Nonce: b.TxNonce(addr), + To: &common.Address{}, + Gas: 30000, + GasFeeCap: big.NewInt(100 * params.GWei), + GasTipCap: big.NewInt(int64(i+1) * params.GWei), + Data: []byte{}, } tx = types.NewTx(txdata) } else { diff --git a/interfaces.go b/interfaces.go index 79caec666089..233385fa52dc 100644 --- a/interfaces.go +++ b/interfaces.go @@ -117,13 +117,12 @@ type CallMsg struct { To *common.Address // the destination contract (nil for contract creation) Gas uint64 // if 0, the call executes with near-infinite gas GasPrice *big.Int // wei <-> gas exchange ratio + GasFeeCap *big.Int // EIP-1559 fee cap per gas. + GasTipCap *big.Int // EIP-1559 tip per gas. Value *big.Int // amount of wei sent along with the call Data []byte // input data, usually an ABI-encoded contract method invocation BalanceTokenFee *big.Int - FeeCap *big.Int // EIP-1559 fee cap per gas. - Tip *big.Int // EIP-1559 tip per gas. - AccessList types.AccessList // EIP-2930 access list. } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 211d2332a7e7..f05a68fda81c 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1803,8 +1803,8 @@ type RPCTransaction struct { From common.Address `json:"from"` Gas hexutil.Uint64 `json:"gas"` GasPrice *hexutil.Big `json:"gasPrice"` - FeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"` - Tip *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"` + GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"` + GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"` Hash common.Hash `json:"hash"` Input hexutil.Bytes `json:"input"` Nonce hexutil.Uint64 `json:"nonce"` @@ -1862,12 +1862,12 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber al := tx.AccessList() result.Accesses = &al result.ChainID = (*hexutil.Big)(tx.ChainId()) - result.FeeCap = (*hexutil.Big)(tx.FeeCap()) - result.Tip = (*hexutil.Big)(tx.Tip()) + result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap()) + result.GasTipCap = (*hexutil.Big)(tx.GasTipCap()) // if the transaction has been mined, compute the effective gas price if baseFee != nil && blockHash != (common.Hash{}) { - // price = min(tip, feeCap - baseFee) + baseFee = min(tip + baseFee, feeCap) - price := math.BigMin(new(big.Int).Add(tx.Tip(), baseFee), tx.FeeCap()) + // price = min(tip, gasFeeCap - baseFee) + baseFee + price := math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap()) result.GasPrice = (*hexutil.Big)(price) } else { result.GasPrice = nil diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index d9838ea577f8..80832054fb1c 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -34,14 +34,14 @@ import ( // TransactionArgs represents the arguments to construct a new transaction // or a message call. type TransactionArgs struct { - From *common.Address `json:"from"` - To *common.Address `json:"to"` - Gas *hexutil.Uint64 `json:"gas"` - GasPrice *hexutil.Big `json:"gasPrice"` - FeeCap *hexutil.Big `json:"maxFeePerGas"` - Tip *hexutil.Big `json:"maxPriorityFeePerGas"` - Value *hexutil.Big `json:"value"` - Nonce *hexutil.Uint64 `json:"nonce"` + From *common.Address `json:"from"` + To *common.Address `json:"to"` + Gas *hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"` + MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"` + Value *hexutil.Big `json:"value"` + Nonce *hexutil.Uint64 `json:"nonce"` // We accept "data" and "input" for backwards-compatibility reasons. // "input" is the newer name and should be preferred by clients. @@ -75,31 +75,31 @@ func (arg *TransactionArgs) data() []byte { // setDefaults fills in default values for unspecified tx fields. func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { - if args.GasPrice != nil && (args.FeeCap != nil || args.Tip != nil) { + if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") } // After london, default to 1559 unless gasPrice is set head := b.CurrentHeader() if b.ChainConfig().IsEIP1559(head.Number) && args.GasPrice == nil { - if args.Tip == nil { + if args.MaxPriorityFeePerGas == nil { tip, err := b.SuggestGasTipCap(ctx) if err != nil { return err } - args.Tip = (*hexutil.Big)(tip) + args.MaxPriorityFeePerGas = (*hexutil.Big)(tip) } - if args.FeeCap == nil { - feeCap := new(big.Int).Add( - (*big.Int)(args.Tip), + if args.MaxFeePerGas == nil { + gasFeeCap := new(big.Int).Add( + (*big.Int)(args.MaxPriorityFeePerGas), new(big.Int).Mul(head.BaseFee, big.NewInt(2)), ) - args.FeeCap = (*hexutil.Big)(feeCap) + args.MaxFeePerGas = (*hexutil.Big)(gasFeeCap) } - if args.FeeCap.ToInt().Cmp(args.Tip.ToInt()) < 0 { - return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.FeeCap, args.Tip) + if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { + return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) } } else { - if args.FeeCap != nil || args.Tip != nil { + if args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil { return errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") } if args.GasPrice == nil { @@ -135,14 +135,14 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { // pass the pointer directly. data := args.data() callArgs := TransactionArgs{ - From: args.From, - To: args.To, - GasPrice: args.GasPrice, - FeeCap: args.FeeCap, - Tip: args.Tip, - Value: args.Value, - Data: (*hexutil.Bytes)(&data), - AccessList: args.AccessList, + From: args.From, + To: args.To, + GasPrice: args.GasPrice, + MaxFeePerGas: args.MaxFeePerGas, + MaxPriorityFeePerGas: args.MaxPriorityFeePerGas, + Value: args.Value, + Data: (*hexutil.Bytes)(&data), + AccessList: args.AccessList, } pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) estimated, err := DoEstimateGas(ctx, b, callArgs, pendingBlockNr, nil, b.RPCGasCap()) @@ -162,7 +162,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { // ToMessage converts TransactionArgs to the Message type used by the core evm func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap uint64, baseFee *big.Int) (types.Message, error) { // Reject invalid combinations of pre- and post-1559 fee styles - if args.GasPrice != nil && (args.FeeCap != nil || args.Tip != nil) { + if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { return types.Message{}, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") } @@ -190,9 +190,9 @@ func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap } var ( - gasPrice *big.Int - feeCap *big.Int - tip *big.Int + gasPrice *big.Int + gasFeeCap *big.Int + gasTipCap *big.Int ) if baseFee == nil { // If there's no basefee, then it must be a non-1559 execution @@ -203,22 +203,22 @@ func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap if gasPrice.Sign() <= 0 { gasPrice = new(big.Int).SetUint64(defaultGasPrice) } - feeCap, tip = gasPrice, gasPrice + gasFeeCap, gasTipCap = gasPrice, gasPrice } else { // A basefee is provided, necessitating 1559-type execution if args.GasPrice != nil { gasPrice = args.GasPrice.ToInt() - feeCap, tip = gasPrice, gasPrice + gasFeeCap, gasTipCap = gasPrice, gasPrice } else { - feeCap = new(big.Int) - if args.FeeCap != nil { - feeCap = args.FeeCap.ToInt() + gasFeeCap = new(big.Int) + if args.MaxFeePerGas != nil { + gasFeeCap = args.MaxFeePerGas.ToInt() } - tip = new(big.Int) - if args.Tip != nil { - tip = args.Tip.ToInt() + gasTipCap = new(big.Int) + if args.MaxPriorityFeePerGas != nil { + gasTipCap = args.MaxPriorityFeePerGas.ToInt() } - gasPrice = math.BigMin(new(big.Int).Add(tip, baseFee), feeCap) + gasPrice = math.BigMin(new(big.Int).Add(gasTipCap, baseFee), gasFeeCap) } } value := new(big.Int) @@ -231,8 +231,7 @@ func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap accessList = *args.AccessList } - // Create new call message - msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, feeCap, tip, data, accessList, false, nil, number) + msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, gasFeeCap, gasTipCap, data, accessList, false, nil, number) return msg, nil } @@ -241,7 +240,7 @@ func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap func (args *TransactionArgs) toTransaction() *types.Transaction { var data types.TxData switch { - case args.FeeCap != nil: + case args.MaxFeePerGas != nil: al := types.AccessList{} if args.AccessList != nil { al = *args.AccessList @@ -251,8 +250,8 @@ func (args *TransactionArgs) toTransaction() *types.Transaction { ChainID: (*big.Int)(args.ChainID), Nonce: uint64(*args.Nonce), Gas: uint64(*args.Gas), - FeeCap: (*big.Int)(args.FeeCap), - Tip: (*big.Int)(args.Tip), + GasFeeCap: (*big.Int)(args.MaxFeePerGas), + GasTipCap: (*big.Int)(args.MaxPriorityFeePerGas), Value: (*big.Int)(args.Value), Data: args.data(), AccessList: al, From ef1dbd0772664ebedbeab56c1c9432e6606c50b8 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 24 May 2024 14:10:43 +0800 Subject: [PATCH 088/479] internal/ethapi: support for eip-1559 txs in clef (#22966) --- internal/ethapi/transaction_args.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 80832054fb1c..86b77bd553ab 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -279,3 +279,9 @@ func (args *TransactionArgs) toTransaction() *types.Transaction { } return types.NewTx(data) } + +// ToTransaction converts the arguments to a transaction. +// This assumes that setDefaults has been called. +func (args *TransactionArgs) ToTransaction() *types.Transaction { + return args.toTransaction() +} From 18bc355e89e010ca8270592d75824c4f49ecaa07 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 24 May 2024 14:59:09 +0800 Subject: [PATCH 089/479] core, internal: support various eth_call invocations post 1559 (#23027) --- core/state_transition.go | 37 +++++++++--------- core/vm/evm.go | 58 ++++++++++++++--------------- core/vm/instructions.go | 2 +- core/vm/instructions_test.go | 12 +++--- core/vm/interpreter.go | 1 + internal/ethapi/api.go | 8 ++-- internal/ethapi/transaction_args.go | 12 +++++- internal/jsre/deps/bindata.go | 2 +- internal/jsre/deps/web3.js | 2 +- 9 files changed, 73 insertions(+), 61 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index f64af1c750ac..cf4e5f26606c 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -223,23 +223,26 @@ func (st *StateTransition) preCheck() error { } // Make sure that transaction gasFeeCap is greater than the baseFee (post london) if st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber) { - if l := st.gasFeeCap.BitLen(); l > 256 { - return fmt.Errorf("%w: address %v, maxFeePerGas bit length: %d", ErrFeeCapVeryHigh, - msg.From().Hex(), l) - } - if l := st.gasTipCap.BitLen(); l > 256 { - return fmt.Errorf("%w: address %v, maxPriorityFeePerGas bit length: %d", ErrTipVeryHigh, - msg.From().Hex(), l) - } - if st.gasFeeCap.Cmp(st.gasTipCap) < 0 { - return fmt.Errorf("%w: address %v, maxPriorityFeePerGas: %s, maxFeePerGas: %s", ErrTipAboveFeeCap, - msg.From().Hex(), st.gasTipCap, st.gasFeeCap) - } - // This will panic if baseFee is nil, but basefee presence is verified - // as part of header validation. - if st.gasFeeCap.Cmp(st.evm.Context.BaseFee) < 0 { - return fmt.Errorf("%w: address %v, maxFeePerGas: %s baseFee: %s", ErrFeeCapTooLow, - msg.From().Hex(), st.gasFeeCap, st.evm.Context.BaseFee) + // Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call) + if !st.evm.Config.NoBaseFee || st.gasFeeCap.BitLen() > 0 || st.gasTipCap.BitLen() > 0 { + if l := st.gasFeeCap.BitLen(); l > 256 { + return fmt.Errorf("%w: address %v, maxFeePerGas bit length: %d", ErrFeeCapVeryHigh, + msg.From().Hex(), l) + } + if l := st.gasTipCap.BitLen(); l > 256 { + return fmt.Errorf("%w: address %v, maxPriorityFeePerGas bit length: %d", ErrTipVeryHigh, + msg.From().Hex(), l) + } + if st.gasFeeCap.Cmp(st.gasTipCap) < 0 { + return fmt.Errorf("%w: address %v, maxPriorityFeePerGas: %s, maxFeePerGas: %s", ErrTipAboveFeeCap, + msg.From().Hex(), st.gasTipCap, st.gasFeeCap) + } + // This will panic if baseFee is nil, but basefee presence is verified + // as part of header validation. + if st.gasFeeCap.Cmp(st.evm.Context.BaseFee) < 0 { + return fmt.Errorf("%w: address %v, maxFeePerGas: %s baseFee: %s", ErrFeeCapTooLow, + msg.From().Hex(), st.gasFeeCap, st.evm.Context.BaseFee) + } } } return st.buyGas() diff --git a/core/vm/evm.go b/core/vm/evm.go index c14ce7c03eac..be90bece714c 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -140,7 +140,7 @@ type EVM struct { chainRules params.Rules // virtual machine configuration options used to initialise the // evm. - vmConfig Config + Config Config // global (to this context) ethereum virtual machine // used throughout the execution of the tx. interpreters []Interpreter @@ -156,13 +156,13 @@ type EVM struct { // NewEVM returns a new EVM. The returned EVM is not thread safe and should // only ever be used *once*. -func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, tradingStateDB *tradingstate.TradingStateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM { +func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, tradingStateDB *tradingstate.TradingStateDB, chainConfig *params.ChainConfig, config Config) *EVM { evm := &EVM{ Context: blockCtx, TxContext: txCtx, StateDB: statedb, tradingStateDB: tradingStateDB, - vmConfig: vmConfig, + Config: config, chainConfig: chainConfig, chainRules: chainConfig.Rules(blockCtx.BlockNumber), interpreters: make([]Interpreter, 0, 1), @@ -170,7 +170,7 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, tradingStat // vmConfig.EVMInterpreter will be used by EVM-C, it won't be checked here // as we always want to have the built-in EVM as the failover option. - evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig)) + evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, config)) evm.interpreter = evm.interpreters[0] return evm @@ -218,13 +218,13 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas if !evm.StateDB.Exist(addr) { if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 { // Calling a non existing account, don't do anything, but ping the tracer - if evm.vmConfig.Debug { + if evm.Config.Debug { if evm.depth == 0 { - evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value) - evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil) + evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value) + evm.Config.Tracer.CaptureEnd(ret, 0, 0, nil) } else { - evm.vmConfig.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value) - evm.vmConfig.Tracer.CaptureExit(ret, 0, nil) + evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value) + evm.Config.Tracer.CaptureExit(ret, 0, nil) } } return nil, gas, nil @@ -234,18 +234,18 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value) // Capture the tracer start/end events in debug mode - if evm.vmConfig.Debug { + if evm.Config.Debug { if evm.depth == 0 { - evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value) + evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value) defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters - evm.vmConfig.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err) + evm.Config.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err) }(gas, time.Now()) } else { // Handle tracer events for entering and exiting a call frame - evm.vmConfig.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value) + evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value) defer func(startGas uint64) { - evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err) + evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } } @@ -305,10 +305,10 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, var snapshot = evm.StateDB.Snapshot() // Invoke tracer hooks that signal entering/exiting a call frame - if evm.vmConfig.Debug { - evm.vmConfig.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value) + if evm.Config.Debug { + evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value) defer func(startGas uint64) { - evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err) + evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -346,10 +346,10 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by var snapshot = evm.StateDB.Snapshot() // Invoke tracer hooks that signal entering/exiting a call frame - if evm.vmConfig.Debug { - evm.vmConfig.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil) + if evm.Config.Debug { + evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil) defer func(startGas uint64) { - evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err) + evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -396,10 +396,10 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte evm.StateDB.AddBalance(addr, big0) // Invoke tracer hooks that signal entering/exiting a call frame - if evm.vmConfig.Debug { - evm.vmConfig.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil) + if evm.Config.Debug { + evm.Config.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil) defer func(startGas uint64) { - evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err) + evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -480,11 +480,11 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, contract := NewContract(caller, AccountRef(address), value, gas) contract.SetCodeOptionalHash(&address, codeAndHash) - if evm.vmConfig.Debug { + if evm.Config.Debug { if evm.depth == 0 { - evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value) + evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value) } else { - evm.vmConfig.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value) + evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value) } } start := time.Now() @@ -524,11 +524,11 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, } } - if evm.vmConfig.Debug { + if evm.Config.Debug { if evm.depth == 0 { - evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err) + evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err) } else { - evm.vmConfig.Tracer.CaptureExit(ret, gas-contract.Gas, err) + evm.Config.Tracer.CaptureExit(ret, gas-contract.Gas, err) } } return ret, address, contract.Gas, err diff --git a/core/vm/instructions.go b/core/vm/instructions.go index c9f3fc380d03..4f2d04639475 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -246,7 +246,7 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( interpreter.hasher.Read(interpreter.hasherBuf[:]) evm := interpreter.evm - if evm.vmConfig.EnablePreimageRecording { + if evm.Config.EnablePreimageRecording { evm.StateDB.AddPreimage(interpreter.hasherBuf, data) } diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index a9ab947c2293..73c1139152c8 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -194,7 +194,7 @@ func TestAddMod(t *testing.T) { var ( env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() - evmInterpreter = NewEVMInterpreter(env, env.vmConfig) + evmInterpreter = NewEVMInterpreter(env, env.Config) pc = uint64(0) ) tests := []struct { @@ -290,7 +290,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) { env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() scope = &ScopeContext{nil, stack, nil} - evmInterpreter = NewEVMInterpreter(env, env.vmConfig) + evmInterpreter = NewEVMInterpreter(env, env.Config) ) env.interpreter = evmInterpreter @@ -531,7 +531,7 @@ func TestOpMstore(t *testing.T) { env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env, env.vmConfig) + evmInterpreter = NewEVMInterpreter(env, env.Config) ) env.interpreter = evmInterpreter @@ -557,7 +557,7 @@ func BenchmarkOpMstore(bench *testing.B) { env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env, env.vmConfig) + evmInterpreter = NewEVMInterpreter(env, env.Config) ) env.interpreter = evmInterpreter @@ -579,7 +579,7 @@ func BenchmarkOpKeccak256(bench *testing.B) { env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env, env.vmConfig) + evmInterpreter = NewEVMInterpreter(env, env.Config) ) env.interpreter = evmInterpreter mem.Resize(32) @@ -683,7 +683,7 @@ func TestRandom(t *testing.T) { env = NewEVM(BlockContext{Random: &tt.random}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) - evmInterpreter = NewEVMInterpreter(env, env.vmConfig) + evmInterpreter = NewEVMInterpreter(env, env.Config) ) opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) if len(stack.data) != 1 { diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 9983b02290da..0df6c427e41c 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -27,6 +27,7 @@ import ( type Config struct { Debug bool // Enables debugging Tracer EVMLogger // Opcode logger + NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls) EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages JumpTable *JumpTable // EVM instruction table, automatically populated if unset diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index f05a68fda81c..2b636a77181f 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1263,7 +1263,7 @@ func (s *PublicBlockChainAPI) getCandidatesFromSmartContract() ([]utils.Masterno return candidatesWithStakeInfo, nil } -func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, vmCfg vm.Config, timeout time.Duration, globalGasCap uint64) ([]byte, uint64, bool, error, error) { +func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) ([]byte, uint64, bool, error, error) { defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now()) statedb, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) @@ -1312,7 +1312,7 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash } // Get a new instance of the EVM. - evm, vmError, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &vmCfg) + evm, vmError, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &vm.Config{NoBaseFee: true}) if err != nil { return nil, 0, false, err, nil } @@ -1382,7 +1382,7 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, bl if args.To != nil && *args.To == common.MasternodeVotingSMCBinary { timeout = 0 } - result, _, failed, err, vmErr := DoCall(ctx, s.b, args, *blockNrOrHash, overrides, vm.Config{}, timeout, s.b.RPCGasCap()) + result, _, failed, err, vmErr := DoCall(ctx, s.b, args, *blockNrOrHash, overrides, timeout, s.b.RPCGasCap()) if err != nil { return nil, err } @@ -1438,7 +1438,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr executable := func(gas uint64) (bool, []byte, error, error) { args.Gas = (*hexutil.Uint64)(&gas) - res, _, failed, err, vmErr := DoCall(ctx, b, args, blockNrOrHash, nil, vm.Config{}, 0, gasCap) + res, _, failed, err, vmErr := DoCall(ctx, b, args, blockNrOrHash, nil, 0, gasCap) if err != nil { if errors.Is(err, vm.ErrOutOfGas) || errors.Is(err, core.ErrIntrinsicGas) { return false, nil, nil, nil // Special case, raise gas limit diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 86b77bd553ab..926122df06f1 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -159,7 +159,9 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { return nil } -// ToMessage converts TransactionArgs to the Message type used by the core evm +// ToMessage converts th transaction arguments to the Message type used by the +// core evm. This method is used in calls and traces that do not require a real +// live transaction. func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap uint64, baseFee *big.Int) (types.Message, error) { // Reject invalid combinations of pre- and post-1559 fee styles if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { @@ -207,9 +209,11 @@ func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap } else { // A basefee is provided, necessitating 1559-type execution if args.GasPrice != nil { + // User specified the legacy gas field, convert to 1559 gas typing gasPrice = args.GasPrice.ToInt() gasFeeCap, gasTipCap = gasPrice, gasPrice } else { + // User specified 1559 gas feilds (or none), use those gasFeeCap = new(big.Int) if args.MaxFeePerGas != nil { gasFeeCap = args.MaxFeePerGas.ToInt() @@ -218,7 +222,11 @@ func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap if args.MaxPriorityFeePerGas != nil { gasTipCap = args.MaxPriorityFeePerGas.ToInt() } - gasPrice = math.BigMin(new(big.Int).Add(gasTipCap, baseFee), gasFeeCap) + // Backfill the legacy gasPrice for EVM execution, unless we're all zeroes + gasPrice = new(big.Int) + if gasFeeCap.BitLen() > 0 || gasTipCap.BitLen() > 0 { + gasPrice = math.BigMin(new(big.Int).Add(gasTipCap, baseFee), gasFeeCap) + } } } value := new(big.Int) diff --git a/internal/jsre/deps/bindata.go b/internal/jsre/deps/bindata.go index 7b17817f126a..fc97d9751a71 100644 --- a/internal/jsre/deps/bindata.go +++ b/internal/jsre/deps/bindata.go @@ -98,7 +98,7 @@ func bignumberJs() (*asset, error) { return a, nil } -var _web3Js = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xfd\x6b\x7b\x13\x39\xd2\x38\x0e\xbf\xcf\xa7\x50\xfc\xdc\x0f\xb6\x89\xb1\x9d\x84\x61\x18\x67\x32\x6c\x08\x30\x64\x6f\x20\x5c\x40\x76\x76\xef\x6c\x96\xab\xe3\x96\xed\x1e\xda\xdd\xfe\x75\xb7\x73\x18\xc8\x77\xff\x5f\x2a\x9d\x4a\x87\x3e\x38\x09\xc3\xcc\x6c\xf2\x02\xdc\x52\xe9\x54\x2a\x95\x4a\xa5\x52\x55\x46\xff\xdf\x32\xca\xe8\x6e\x67\xb2\x4c\xc6\x45\x94\x26\x84\x76\x8a\x5e\xd2\xcb\xba\x9f\x55\x4a\xde\x49\x7b\xcb\xee\xe7\x68\xd2\x59\x4f\x8e\xd3\x13\xfe\xab\x80\x5f\x67\x41\x46\x82\xdd\xe2\x72\x41\xd3\x09\x91\x75\xed\xb6\x64\xd1\xd6\xbd\x7b\x22\x71\x87\x95\x59\xde\xbb\x17\x74\x33\x5a\x2c\xb3\x84\x04\x9d\xb4\xb7\x3e\xec\xb2\xf4\x48\xa6\x45\x22\x8d\xd5\x3a\xd9\x4d\xe8\x39\x79\x9e\x65\x69\xd6\x69\xed\x07\x49\x92\x16\x64\x12\x25\x21\x99\xa7\xe1\x32\xa6\xa4\xdd\xda\x48\x37\x5a\xed\x56\x77\xa7\x98\x65\xe9\x39\x99\xf4\xc7\x69\x48\x77\x5b\xaf\x0f\x9f\x1d\xbd\x7a\xfe\xf1\xcd\xe1\x87\x8f\x2f\x0e\x8f\xde\x3c\x6b\xf5\x26\x57\xac\xbe\x78\x97\xf5\x7d\xf7\x33\xbd\x58\xa4\x59\x91\x8f\x3e\x5f\x5d\xed\xb0\x31\x1c\x0f\x4f\xfa\xe3\x20\x8e\x3b\x71\x5f\x64\xf5\x64\xef\x3b\x94\x0f\x30\xd9\x05\xc0\xcd\x93\x63\x7a\xb2\x23\xba\x9a\x77\x92\x27\xc9\x88\x76\xaf\x7a\x71\x4f\x97\xa4\x3d\x8e\xbb\x2b\x01\xc5\x9a\x94\x99\xd0\x8b\xa8\x11\xae\x26\x69\xd6\x61\xd0\xe9\xee\x70\x27\xfd\x31\xeb\xc7\x34\x99\x16\xb3\x9d\x74\x63\xa3\x9b\x77\x32\x86\x78\xd5\x8d\xab\x6e\xe7\xf3\xe6\xe8\x58\x75\x59\x54\xd1\xe3\x58\xea\x89\xb6\xbb\x9f\xd7\x78\x82\xec\xcc\xee\xf1\x1a\x21\x9f\xd7\x08\x21\xa4\x35\x4e\x93\xbc\x08\x92\xa2\x35\x22\x45\xb6\xa4\x3d\x9e\x1a\x25\x8b\x65\x91\xb7\x46\xe4\x18\xbe\x25\x34\xe4\x25\xc1\x9c\xb6\x46\xa4\xf5\x31\x3d\x4f\x68\xd6\xea\xe9\x1c\x36\x3a\x96\x13\x84\x61\x46\xf3\xbc\x25\x72\xae\xe0\xff\x13\x51\xb5\x2c\x0e\xff\x8b\xb4\x74\x59\xd4\xb7\x97\x7e\x44\x45\x8c\xf6\x4e\x2f\x0b\x9a\x6f\x6f\xf9\xdb\x93\x40\x0a\xd3\x6b\x84\x5c\xf5\x6e\x05\x01\xd7\xea\x8f\x1a\x0e\xc2\x5e\x33\x04\xac\x8c\xea\x3f\xea\xd0\xc7\x69\x52\xd0\xa4\xb8\xf1\xe0\xff\x94\xf3\xce\x66\xec\x0f\x33\xed\x93\x20\xce\x7f\xbf\xa1\x67\x34\xa7\xd9\x99\x6f\xd5\xff\xd1\x27\x2d\x5f\x9e\xbe\xa3\xd3\x28\x2f\xb2\xe0\xbf\x60\xf2\x7a\x55\x75\xd0\xf3\xc3\x1b\xf1\xfd\x22\x0b\x92\x7c\xe2\x65\x7d\x7f\x16\x1c\x64\x16\x29\xac\x8e\x84\x9c\x16\xef\xab\x49\xea\xd6\x70\x61\x37\xfd\xbb\x34\xfa\x95\x27\x20\x68\x82\xf8\xaa\x0a\x16\x59\x34\x0f\xb2\x4b\x6f\x3f\xd2\x34\xae\x9d\xbc\x3d\xd1\xd6\x9f\x17\x85\xe6\x1e\x5c\x59\x4d\x19\x12\xf6\x4b\xb7\xf1\x3f\x12\x12\xbc\xbd\x0f\xa3\x3c\x3d\x4f\x6e\xd0\xf3\x20\x49\x93\xcb\x79\xba\xcc\x57\xe8\x7a\x94\x84\xf4\x82\x86\xc6\xde\x75\x6b\x13\xab\x2b\x47\xdd\x31\x6b\x3f\x8f\x92\x9b\x30\xee\xbd\x25\x60\xe2\x79\x12\xd2\xb0\x65\xa1\x89\x9e\x31\x42\xf8\x0b\xe0\xe8\x34\x0a\xc3\x66\x38\xba\x5e\xfd\x67\x41\xbc\xf4\x76\x7f\x19\x25\xc5\xd6\x77\x8f\xaa\xa7\xe0\x0d\x3d\x7f\x1a\x7d\x43\xe4\xdf\x68\xcd\xed\xcf\x82\x64\xfa\x2d\x49\xe7\x56\x28\xa7\xa4\x6e\x24\xd5\x57\x52\x8d\x17\x33\x6f\xf9\x6e\x54\x8b\xa0\xb5\x93\xb5\xb5\xab\xde\xe7\xab\x93\xde\xd6\x37\x3b\xf4\xff\x85\xce\xbc\xdf\x48\x76\x9c\x2c\x93\xf0\xda\xa4\x72\xe3\x8d\xeb\xee\xd8\xfb\xe7\x3e\xf6\xde\x1d\xfa\xfe\xc8\x67\x0e\xef\xe0\xc5\x79\xe1\x8f\x26\x6d\x7e\xdd\xcd\x5c\xef\x55\xdb\xb7\xb6\x57\xad\x3a\xef\x93\x2c\x9d\xdf\x70\xda\x8b\xf4\x86\x47\xcd\x9b\x09\x7c\xdf\x76\xdd\xfc\x11\xf0\x17\x25\x61\x94\xd1\x71\x71\xe0\xdd\x33\x57\xe8\xc9\xcd\x26\x22\x1a\x07\x8b\x0f\xdf\x74\x32\xfc\x98\x6c\x76\xda\xa5\x8b\x34\x8f\xaa\x0e\xea\x8b\xe0\x32\x38\x8d\xa9\x29\x14\x7c\x13\xae\x54\x46\x73\xb7\x72\xfc\xba\x19\x0d\xec\xc9\xf1\x3e\x33\xf1\xf9\xfb\x9f\x64\x6e\x05\x49\x25\x75\x37\xa3\xb3\x6f\x80\xfe\x3f\x2c\xd6\x6f\xe3\xfc\x78\x6d\x3e\xf9\xb5\xb1\x6e\x33\xbd\x3b\xb4\x37\x44\xfb\x8d\x37\xae\xaf\x3d\xb3\x07\x9e\x2d\xad\x4a\x8e\x7b\xd8\x44\x8e\x03\xe3\x0d\xb2\x2b\x2d\x1c\x3a\xed\xfe\x60\x92\x66\xf3\xa0\x28\x68\x96\xb7\xbb\x3b\x00\xf0\x3e\x8d\xa3\x30\x2a\x2e\x3f\x5c\x2e\xa8\x09\xcb\xda\x67\x50\x6b\x83\xfb\xf7\xd7\xc8\x7d\x03\x52\xe8\xdc\x49\x94\x93\x80\x2c\xb2\x34\x65\xc0\xa4\x98\x05\x05\xc9\xe8\x82\x1d\xb2\x92\x22\x27\x62\xee\x08\xcb\x64\x35\x1c\x14\x64\x1e\x14\xe3\x19\xcd\x47\xec\x53\x64\xa3\x9f\xc7\x27\xf8\xe3\xa1\xf1\x75\x62\x66\x6e\x5b\xdf\x27\xc7\x8f\x4e\x8e\x4f\x7a\xa4\xdf\xef\xaf\x91\xfb\x03\x67\x6c\xb2\xc7\xbb\x44\x59\xd3\x74\xba\x62\x8a\x8b\x59\x94\xf7\x3f\xc2\xc2\x78\x21\x11\xc4\x00\xfb\x1c\x5d\x07\x2c\xe3\x20\x29\x76\x10\x30\xdf\xb7\x7d\xd0\x87\x90\x23\x9a\xdb\x59\xbb\xda\x59\x5b\xf3\xf4\xa3\xbf\xc8\xd2\x82\x63\x6d\x97\x24\xf4\xdc\xe8\x6b\xe7\xf3\x55\x77\xa7\xba\x54\x1f\xa4\x97\x6c\x39\x2e\x52\xd6\xb8\x07\xb6\xae\xdd\x7e\x94\x8b\x39\xd7\x08\x61\xe4\x28\x91\x22\xec\x5a\xd6\xd7\x59\x62\x1f\xe6\xad\x33\x10\xd8\xee\xfc\xfb\xb8\x73\x3c\x7c\xf0\xc3\xc9\xfd\xee\xbf\x4f\xba\x4f\x06\x5d\x3e\x4e\xf3\xe0\x50\xda\xad\xab\xde\xe7\x16\x26\xc5\xd6\xe8\x87\x5e\x8b\xd3\x5b\x6b\xb4\xf9\xf0\xea\xa4\xf7\xdd\x37\x26\xef\xa7\x69\x1a\xd7\xd0\xf6\x29\x03\x29\x21\x6c\x96\x27\xff\xe7\x54\x0a\xbf\x1e\xea\x9f\x27\x28\x79\x1b\x7f\xd4\x91\x31\xf4\xec\xba\x34\xcc\x0a\xaf\x42\xc4\x1c\xde\xa6\x60\x96\xba\x22\xf9\x9a\x45\x2a\x68\x97\xb7\x58\x55\xf6\x3a\x54\xfb\x1f\x86\x5a\x93\x66\xef\xff\x4f\x23\xa2\x15\xfd\xa9\xa7\xd8\x47\xdf\x9a\x62\xd9\x1e\xa6\x48\xb6\xf0\xd3\x6c\x31\xa3\x04\x36\x3b\x20\xdc\xbe\x8f\x72\x59\xae\xfa\x21\xe8\x12\x7e\x3e\x44\xbf\x4f\x70\xc6\xb6\xf1\x65\xd2\x2f\x11\x5b\xab\xfa\xf9\xd8\xa8\x47\x14\xf5\x50\x39\x74\xf2\xda\x64\xce\x4a\xaf\x44\xe7\xbc\x80\x43\xe8\x2c\x79\x55\x4a\x37\xcb\x54\x91\x3a\x6f\xb4\xb2\xf4\xf5\x88\x9d\x55\xc2\x49\xfd\xf3\x66\xef\xaa\x7b\x3d\xc2\x17\xbd\xab\xa7\xfc\xef\x9b\x50\xfe\xe0\x3e\x74\xf8\xc3\x2c\xca\xc9\x24\x8a\x29\xa3\xd4\x45\x90\x15\x24\x9d\x90\x73\x7a\xba\xdd\xff\x35\xef\xaf\x01\x88\xf8\x62\x00\x93\x8c\x52\x92\xa7\x93\xe2\x3c\xc8\xe8\x88\x5c\xa6\x4b\x32\x0e\x12\x92\xd1\x30\xca\x8b\x2c\x3a\x5d\x16\x94\x44\x05\x09\x92\x70\x90\x66\x64\x9e\x86\xd1\xe4\x12\xea\x88\x0a\xb2\x4c\x42\x9a\x01\xc1\x17\x34\x9b\xe7\xac\x1d\xf6\xf1\xf3\x9b\x23\xf2\x8a\xe6\x39\xcd\xc8\xcf\x34\xa1\x59\x10\x93\xb7\xcb\xd3\x38\x1a\x93\x57\xd1\x98\x26\x39\x25\x41\x4e\x16\x2c\x25\x9f\xd1\x90\x9c\x5e\x0a\x2a\xa2\xe4\x05\xeb\xcc\x7b\xd1\x19\xf2\x22\x5d\x26\x61\xc0\xc6\xdc\x23\x34\x2a\x66\x34\x23\x67\x34\xcb\xd9\x0c\x6d\xcb\xb6\x44\x8d\x3d\x92\x66\x50\x4b\x27\x28\xd8\x18\x32\x92\x2e\x58\xc1\x2e\x09\x92\x4b\x12\x07\x85\x2e\xeb\xa2\x40\x8f\x34\x24\x51\x02\xd5\xce\x52\xb9\xb2\xa3\x82\x9c\x47\x71\x4c\x4e\x29\x59\xe6\x74\xb2\x8c\xb9\xe0\x78\xba\x2c\xc8\x2f\x07\x1f\x5e\x1e\x1e\x7d\x20\x7b\x6f\xfe\x45\x7e\xd9\x7b\xf7\x6e\xef\xcd\x87\x7f\xed\x90\xf3\xa8\x98\xa5\xcb\x82\x30\x89\x12\xea\x8a\xe6\x8b\x38\xa2\x21\x39\x0f\xb2\x2c\x48\x8a\x4b\x92\x4e\xa0\x8a\xd7\xcf\xdf\xed\xbf\xdc\x7b\xf3\x61\xef\xe9\xc1\xab\x83\x0f\xff\x22\x69\x46\x5e\x1c\x7c\x78\xf3\xfc\xfd\x7b\xf2\xe2\xf0\x1d\xd9\x23\x6f\xf7\xde\x7d\x38\xd8\x3f\x7a\xb5\xf7\x8e\xbc\x3d\x7a\xf7\xf6\xf0\xfd\xf3\x3e\x21\xef\x29\xeb\x18\x85\x1a\xea\x11\x3d\x81\x39\xcb\x28\x09\x69\x11\x44\xb1\x9c\xff\x7f\xa5\x4b\x92\xcf\xd2\x65\x1c\x92\x59\x70\x46\x49\x46\xc7\x34\x3a\xa3\x21\x09\xc8\x38\x5d\x5c\x36\x9e\x48\xa8\x2c\x88\xd3\x64\x0a\xc3\x56\x54\x46\xc8\xc1\x84\x24\x69\xd1\x23\x39\xa5\xe4\xc7\x59\x51\x2c\x46\x83\xc1\xf9\xf9\x79\x7f\x9a\x2c\xfb\x69\x36\x1d\xc4\xbc\x82\x7c\xf0\x53\x7f\xed\xfe\x40\x32\xdb\xbf\x01\xd9\x8e\xd3\x90\x66\xfd\x5f\x81\x45\xfe\x2d\x58\x16\xb3\x34\x23\xaf\x83\x8c\x7e\x22\xff\x9b\x16\xf4\x3c\x1a\xff\x46\x7e\x9c\xb3\xef\xbf\xd1\x62\x16\xd2\xb3\xfe\x38\x9d\xff\x04\xc0\x61\x50\x50\xb2\x35\xdc\xfc\x0e\x18\x5e\xfd\x56\x50\x21\xc0\xa2\x32\x42\x1e\xf3\xed\x1d\x42\x52\x40\xc0\x6c\x17\xf4\x41\x1e\x24\x85\x09\x18\x25\x85\x0f\xee\xc8\x01\x5c\x96\x40\x3e\xbb\x4c\x82\x79\x34\x96\x6c\x1c\x95\x08\x79\x0e\xf0\x28\x5f\xc9\xf7\x45\x16\x25\x53\xb3\x4c\x0e\x69\x3e\xe8\x77\x34\xb0\xc6\x98\xd1\xc0\x3b\xc6\x23\x17\x74\x59\x06\xeb\xe9\xb6\xea\x2f\x00\x47\xb9\x18\xa0\xc1\x99\x73\x54\x45\x0f\x76\x58\xc1\xa7\xa5\x85\x38\xca\xef\xab\x2a\x60\x1b\xe1\xc0\x5f\xbe\xa8\xd3\x23\x29\x81\xde\xcb\xb2\xe0\x92\x83\x73\x26\x6e\x89\x02\xfb\x8c\x3e\x91\x04\x20\x56\x12\xe7\x10\x21\x29\x52\x42\x13\x46\xc3\x83\x90\xb2\xff\x54\x2b\x8c\x19\x07\x9c\x4d\x32\xae\x24\xe4\x5a\x73\x63\xe6\x75\xe3\x11\x33\xb0\xdc\xdc\x99\x21\x89\xec\x42\x0d\xb9\xd1\x45\xe0\xfd\x73\x5a\xcc\xd2\xd0\xd3\x2d\xae\x5c\x4f\xb3\x39\xe1\x92\x4b\x6a\xcc\xc8\x1a\xe1\x6b\x50\x14\xff\x28\x66\x46\x64\x91\xbf\x41\xef\xc9\x67\x4e\x3c\x57\x4a\x2c\xff\x1b\xc7\x7c\x4e\x3e\xe3\xca\xae\x20\x0b\xde\x2a\xe4\xe4\x33\xbc\x6b\xb8\x22\xe2\x33\x62\xbc\x81\x4b\x44\x8c\x0c\xa1\x2f\x6c\x27\x62\xec\x1e\x10\x62\x20\x03\xed\xd4\xb8\x4b\x0e\x8e\x24\x8a\x18\x36\x73\x53\xbc\x43\x58\xeb\x4f\xa2\xb8\xa0\x59\x07\x95\xed\x22\x1d\x84\xa0\xa2\x42\x08\x05\x92\x08\x40\xa7\xd0\x3d\x1e\x9e\xec\x70\xfe\x19\x4d\x48\x67\x1d\x37\x82\xeb\xe0\x0f\x34\xf8\x53\x8e\x76\x94\x9c\x05\x71\x14\x6a\x1a\x60\x35\xae\x8f\x48\x9b\x6c\x10\x5c\xf9\x1a\x96\x35\x70\xcd\x26\x05\x96\x50\x1a\x59\xc4\x41\x94\x70\xfa\xb2\xa6\x91\x03\xbc\x15\x39\xe5\xb3\x28\xd2\x0f\x4f\x7f\xa5\xe3\xe2\xca\xaa\x50\x4e\xb2\x2e\xc7\xab\x0d\x2d\xb8\xf2\xa9\x43\xdd\x70\x66\xae\xc7\xcb\x5b\x02\x17\x4c\x1a\x2a\x96\x77\x8e\x19\xf0\x49\x8f\x1c\x03\xf8\x49\xb7\x19\x6a\xe2\x28\x07\x09\x88\x2f\xbe\x72\xec\xe4\x18\x0d\xc0\x02\x38\x76\x7c\xe9\x0b\x5d\xa0\x0c\x31\x4e\xb3\x8d\x70\x93\xbb\x4b\x5f\x60\x27\x2f\xa3\xef\x5c\x12\xf8\x94\x16\x78\x05\xe6\x82\x73\x08\x92\x65\xc5\x44\xdf\x58\x09\xa3\x86\xfe\x3c\x58\x74\xca\x78\x2c\x68\xe5\x3c\x6b\xc4\xe0\x9d\xbc\xe6\x0e\xef\xe9\x31\x14\x39\xe1\xec\x59\x7e\xa9\x55\x84\xfa\x23\xf6\xa9\xc3\xc9\x24\xa7\x85\xd3\xa9\x8c\x86\xcb\x31\x45\xfd\x0a\xc6\xe3\x1e\xa9\xe9\x1c\x60\xa7\x08\x8a\x68\xfc\x36\xc8\x8a\x57\xf0\x92\xc8\xaa\xb9\x6f\xe7\x77\x3c\xfd\x94\x75\x65\x8c\x29\xd1\xf0\xbd\x5b\xe5\xeb\xa0\x98\xf5\x27\x71\x9a\x66\x9d\x8e\xd3\xe2\x06\xd9\xde\xec\x92\x01\xd9\xde\xea\x92\xfb\x64\x7b\x4b\x0c\x1a\xa1\x2f\x18\x8f\xc9\x06\xe9\xa8\x4d\xc7\xc0\x7a\x09\x0a\xc9\x13\xb4\x77\x11\xb2\xbd\x45\x46\x46\x42\x49\x67\x25\xea\x7b\x64\x88\xb1\x9f\xd1\x7c\x19\x17\x92\x7a\xf8\x0c\xbe\x5e\xc6\x45\xf4\x4b\x54\xcc\xf8\x9c\x48\x0a\x34\xfa\xd6\x53\x74\xd4\x33\x67\x50\x56\x2e\x46\xc8\xeb\x37\x4f\x7c\x7e\xd2\xb7\x5a\xf5\xad\x81\x86\x3d\x40\x6b\x44\x0d\xaf\xd5\xda\xd1\x0b\x87\xc6\x13\x31\x62\xd1\x59\xb1\x2b\xa4\xd9\xf3\x60\x3c\xeb\xd8\x8c\x29\xc2\xb4\xc5\xb8\x7e\xe9\x7c\xe9\xb9\x3a\xe9\xe2\x42\x1c\x21\xd0\x95\x0d\x57\xdb\xd9\x31\xbb\x2f\xd7\x11\x22\x42\xb5\x76\x19\x15\xd3\x78\x22\x40\xec\x39\x82\x0e\xb8\x5d\x92\x78\x82\x0f\x7b\xb2\x70\x13\xe6\x52\xdc\xd8\x25\x54\x3c\xc3\x23\x03\xb2\xa5\x41\xaf\x08\x8d\x73\x6a\x0d\x6f\x30\x20\x61\x9a\xb4\x0b\x12\x84\x21\x11\xa5\x8a\xd4\xac\xb2\x4f\xa2\xa2\x9d\x93\x20\xce\x68\x10\x5e\x92\x71\xba\x4c\x0a\x1a\x96\x60\xe9\x2b\x8d\xf3\x4a\x2f\xc2\xc1\x80\x7c\x38\x7c\x76\x38\x22\x93\x68\xba\xcc\x28\x61\x07\xb6\x84\xe6\xec\x04\xc8\x4e\x69\x97\xb9\xc9\xac\x7e\x0f\x22\xf9\xe3\x4c\xb2\x39\x19\x14\x23\x50\x62\xa5\x64\x99\x2b\xb4\x66\x74\x12\x80\x3a\xe6\x7c\x96\xc6\x94\xf7\x30\x4a\xa6\xeb\x35\x8c\xa0\x82\x07\xd8\x9c\x5f\x0c\xba\x47\x52\x67\xe5\x1b\x8b\x5c\xce\x49\xad\xa8\xef\xd9\xe2\x3a\xae\x6a\x0c\x11\x10\x6f\x98\x9c\x07\x9a\xac\x73\x5a\x38\x73\xca\xc9\xea\x4d\x30\xa7\xf6\x3e\xa4\x73\xb0\x9c\xe9\x96\xf5\x6c\x3e\xd5\xfb\x99\xae\xd8\x53\xa7\xe2\x8b\x02\x83\x5a\xaa\x95\x7f\x15\xc3\x96\x95\x2c\x32\x7a\x16\xa5\xcb\x5c\x75\x68\x6b\x87\xa1\x24\x4a\x48\x94\x14\x4e\x89\x3a\xfc\xa3\xfe\xfa\x1a\x64\x7f\x93\x34\x23\xf0\x48\x38\x22\xbb\x64\x73\x87\x44\xe4\x47\x39\x00\xf9\x5e\x98\x44\x1b\x1b\x65\xc5\xd9\x9f\xd5\xe7\x8d\x5d\xb2\xd1\x91\x38\x88\xc8\x03\xb2\x79\xc2\x24\x7c\xf2\xe5\x0b\x19\xee\x94\x56\x52\xc1\xca\x05\x3d\x6c\x90\x88\xdc\x2f\x9b\xb9\x0d\xbb\x17\x4c\x38\x28\x63\xfb\xf2\xef\xca\x49\x35\x53\xae\xba\x9d\xae\x35\x85\x83\x01\x99\x44\x59\x5e\x10\x1a\xd3\x39\x4d\x0a\x76\xbe\xe2\x68\xea\x91\xfc\x53\xb4\x20\x51\xb1\xca\x94\x1b\xd8\x1f\xfa\xb0\xcf\xf0\x57\x39\x03\xf0\x74\x3e\x0c\x23\xd6\x48\x10\xab\x45\x2e\xf0\xe9\xf0\x1f\x17\xdf\x7e\xbe\xa8\x49\xa7\x84\x41\x1c\x47\x64\x83\x6c\x9e\x48\x3e\x41\x36\x88\xd3\x0d\x0f\xda\x6b\x11\x6c\x31\x3f\x0f\xa4\xd8\x2a\x3d\xb4\xcf\xa9\xe2\xda\xac\xe7\x0f\xcd\x54\x98\xb0\x65\x62\xea\x86\x8b\xbf\x86\x32\x49\x19\x43\x1a\x56\x31\x24\xd2\x88\xa6\x6b\x39\xca\x60\x40\xc6\x41\x3c\x5e\xc6\x41\x41\xa5\xe0\xc3\x8e\x7c\xa2\x2f\x24\x2a\xe8\xfc\x06\xec\x88\xb1\xa2\xe3\x3f\x11\x53\xea\xda\xb0\x57\x2b\xed\x2b\x37\x9c\x90\x6f\xc7\x60\x30\x73\xf9\xea\xbc\x85\x38\xda\x22\xd1\x8f\x1a\x6d\x88\xd0\x45\x8a\x9b\xc9\xb4\x42\x63\xc4\x21\x1b\x6b\x8c\x64\xba\xba\xd5\x54\x2a\x11\xbf\x2e\xa9\x5c\x0f\x82\x1a\xf6\x88\x7f\x50\xbf\x4f\x47\x84\x8a\x69\x1d\x11\x87\x06\xd9\xa6\x09\x5a\x2a\x95\x44\x25\x08\x29\xd3\x11\x95\x23\x44\x94\x80\x13\x06\xb4\xa6\x11\x53\xad\x21\xc2\x43\xf4\x9d\x8e\x0d\xdc\xac\xae\x20\x92\xa5\x38\x15\x63\x78\x4e\xc4\xb9\xf7\x14\x6e\x1d\xf7\x6f\x59\xa3\xc4\x87\xdc\x81\x91\xc9\xf5\xa5\xd5\x22\x86\x5e\x44\xd6\xa8\x35\x4c\x55\x2a\x07\x3d\xaa\x5a\x3d\x03\xc6\x28\xe7\x40\xac\xcc\x6d\x8f\xb4\x89\x3a\x4a\x9d\x44\x7d\x72\xb0\xe8\x5a\x29\x93\x1c\x0c\x48\xbe\x9c\xf3\x1b\x3a\xcf\x2e\x25\x44\x44\x05\x2f\xaa\x3b\x8e\x4e\x18\x57\x54\x5f\xb0\x25\xf9\xf8\x8f\x6c\xde\x44\x84\x94\x36\x1d\x14\x0c\x06\x24\xa3\xf3\xf4\x0c\xae\x31\xc9\x78\x99\x65\x4c\x3e\x55\xc2\x69\x0a\xc9\xa2\x9b\x51\x0e\x3d\xf7\xf4\x36\x5f\x45\xe3\x27\x91\xd9\x58\xf3\x67\x8c\x8c\x3c\x70\xea\x6f\x4c\x69\xef\xad\x75\x58\x72\xad\xe3\x3d\xb5\x4a\x1e\xe7\xa1\xb2\xc2\xba\x72\x90\x64\xc5\x76\x30\x7c\x49\x62\xde\x5f\xf0\xde\xb2\xb6\xc6\xe2\x96\x09\x9b\x5a\x40\xef\x3b\xdc\x5e\xd5\x36\xc1\x10\xd7\xa2\x9d\x6e\xcf\x9b\xfd\x34\x4d\xe3\xb2\x3c\x26\x84\x94\x64\x1d\x55\xe4\xe1\xcb\xcd\xd2\x66\xab\x32\x39\x17\x2e\xcb\x7d\x47\x83\xd2\x1e\x1f\xf1\xcc\x35\x46\x10\xae\xfd\x06\xa0\x4e\xd9\x6c\x48\xc3\xd9\xd1\xc3\x5e\x8b\xdf\xfd\xb6\x46\xdf\xc1\x4f\xd6\xb7\xd6\xe8\x11\xfb\x8d\xaf\x63\x5b\xa3\xc7\x3d\x9f\xad\x47\x94\x14\xad\xd1\xe6\x90\xfd\xcc\x68\x10\xb7\x46\x9b\x5b\xec\x37\xbf\x95\x6d\x8d\x36\xb7\xd9\xd7\x92\x43\x41\x03\x4b\x01\xf6\xe8\xea\xa4\xf7\xf8\xf7\xb4\x8b\xaa\xb9\x86\xbe\x9e\x35\x11\xae\x64\x15\xa3\x22\xb3\x9c\x6d\x5b\x84\x73\x57\x34\x31\xf2\x17\xad\xb0\x34\x32\x7b\xd2\xa4\xae\x1b\xd8\x1d\x95\x18\x1b\x35\x6a\x14\x5d\x89\x7b\xa7\x4b\xb2\x9d\x6c\x49\x1b\x98\x30\x59\xc3\xae\xb7\x64\xfa\xe1\xce\x92\xe9\xce\x92\xe9\xbf\xc5\x92\x49\x2f\x84\xdb\x32\x67\x7a\x1a\x4d\xdf\x2c\xe7\xa7\xc0\x0a\x15\x77\x3e\x8d\xa6\x09\x24\xf6\x7f\x55\x9c\x7c\x59\x44\xb1\x69\x5f\xd3\x1f\x40\x1a\xff\x57\x82\x8d\xbd\x20\xe3\x34\x99\x44\x8e\x31\x90\x3c\x99\xa1\x5d\x01\xce\x2e\xb0\x2d\xc8\x81\x73\x5e\x9d\x13\xe0\xf7\x04\x1e\x6c\xb0\x73\x16\xe3\x5b\xda\x4a\x16\x96\x02\x9b\x1b\x50\xce\xdc\x67\x38\xe6\x90\x51\x4e\x12\x3a\x0d\x8a\xe8\x8c\xf6\x24\x27\x82\x8b\xa3\xe2\x3c\x6d\xe7\x64\x9c\xce\x17\x52\x5a\x85\x52\x6c\x6e\x55\xc9\x49\x9c\x06\x45\x94\x4c\xc9\x22\x8d\x92\xa2\xc7\xaf\x43\x19\xd9\x87\xe9\x79\x62\x9d\xe9\x4c\x35\x89\x7b\x7c\xfb\xc2\xb1\xfc\x45\xe1\xfb\x4a\x8e\x85\x2d\xa5\x84\xd2\x10\x4e\xd1\xa7\x7a\x8e\x43\xbf\x31\x0c\x20\xed\x4a\xd9\xf9\x98\xed\x1a\x0c\x18\xea\x97\x5c\x58\xb5\xdb\xe7\x73\xd1\x19\xf7\x9f\x7f\x78\xf9\xf1\xe9\xc1\xcf\x6f\x8e\x5e\x3f\x7d\xfe\xee\xe3\xbb\xc3\xa3\x37\xcf\x0e\xde\xfc\xfc\xf1\xf5\xe1\xb3\xe7\xe8\x0c\xa7\x34\x71\x30\x93\xfd\x45\x10\xbe\xa2\x93\xa2\xc3\xbf\x8a\xf4\xc3\x79\x9a\xef\x2b\x2c\x8a\x36\xfb\x45\x2a\xc4\xa5\xcd\x47\xdd\x1e\x79\xf4\xd0\xbc\xe1\xc1\xbb\x25\x0c\xa7\xc3\x1b\x31\x0d\x30\xcc\x89\x97\x87\xdf\x12\x9c\x3f\x55\x67\x63\xf3\xd0\xbc\x2a\x0e\x5d\xa9\xc3\xc0\xa2\x07\x21\x45\xfa\x92\x5e\xc8\x71\xe7\xcb\xd3\xbc\xc8\x3a\x5b\x08\x7f\xb1\x75\xb5\xcf\x8b\x4b\x2d\xf7\x06\x79\xb4\xdd\x25\x03\x8c\x22\x1b\xdd\xef\xa2\xe9\xac\x10\xc5\x7a\x24\x26\xf7\xbf\x32\x3e\xc5\x0e\x7c\xab\x68\x2d\x95\xe9\x6e\x8c\x5d\x79\x3c\x33\xd1\xaa\xb4\x73\xdf\x6c\x06\x2c\xb5\x29\x6f\xac\xdb\xe7\x6b\x7e\x83\xd4\x4f\x50\x1d\xa7\xe3\x92\x7c\xf9\x8a\x78\x2f\xf3\x6f\x3a\x77\xca\xb8\xb3\xf9\xac\x4d\xb2\x74\x7e\x54\x4c\x1e\xdf\x4d\x9c\x67\xe2\xc4\x3b\xa3\x32\x46\x26\x5e\x21\xc9\x49\x63\xdf\x34\x48\x56\x67\x64\xf6\x93\xa3\xf2\x39\x6b\x0f\x6f\xf6\xd7\x26\x1b\xa2\x7a\xf2\x84\x90\xf6\x66\x9b\x8c\x48\x7b\xd8\xbe\x39\x8f\xaa\xc3\x24\x3b\xb1\xb2\x52\xff\x60\x70\x39\x61\x82\xf1\x7c\x19\x17\x11\x17\x2a\x4f\x2f\xc9\xd6\x7f\xe6\x4c\x3c\x57\x36\x74\x01\xab\xb9\xa0\x53\x9a\x55\x6c\x25\xef\x44\xad\x75\xfb\xf7\xaa\x33\x22\x6c\x99\x4b\x66\x44\xa0\xc9\xa2\x3e\x86\x35\xd5\xa2\xda\x5c\xa3\x39\xcd\xad\xac\xad\x6e\x7f\x91\x9e\x77\x36\xb7\x1e\x77\xbb\x26\x4a\xf7\x67\x74\xfc\x89\x44\x13\x03\xa7\x48\x2c\xb2\x10\x91\x47\xd3\x84\x86\x07\xf9\x1b\x9d\xed\x28\xa2\x55\x1d\x33\x7a\x21\x7a\x6c\x22\x43\x12\x2d\x1c\xfa\xa0\xed\xc2\x94\xc4\x52\x76\x64\x39\x8f\x98\x18\x1e\xc4\xb9\xb6\x5a\xb6\x5b\xaf\xc5\x97\x0f\x43\x92\xdd\x0c\x7b\x64\xb3\xdb\x23\x9b\x8f\x90\x3c\xb2\xd5\x35\x72\xbb\x64\x77\x77\x97\x91\xac\x97\x0a\x33\xc6\x3e\x1e\x04\x31\x74\x8a\x70\xd5\x81\xbe\xf0\xe0\xa2\xa6\x4b\x44\x5c\x91\x60\x0b\x81\x06\x79\x38\x76\xb0\x0c\x67\x5a\x30\xac\x68\x57\x09\x87\xb0\x2c\xa2\x29\xe1\x72\xba\x45\x6f\xaa\x0b\x06\xfe\x0c\xa3\x58\x06\xcc\xe7\x71\x97\xf7\x06\xe9\x32\x3b\x5d\xf2\xe5\x0b\x69\x0d\x5b\x42\x47\x3c\x18\x90\xb1\xa2\x22\x26\x3c\xcb\x89\x54\xad\x73\xa0\xa8\xe0\x13\xad\x24\x6d\x57\xc8\x96\xf7\xb7\xd6\x3c\x8b\xb9\xf5\xa8\x20\x3d\xf3\xcb\xa7\x74\x1e\x25\x4b\x7b\x15\xb4\x27\x37\xfc\x6b\x43\xdd\xb2\xf2\x4d\x75\x3d\xd6\xa0\x43\xd7\xa0\xa0\x65\x35\x09\x1d\x55\xd2\x90\x8f\x7a\xe8\x4a\xe4\x23\x9a\x77\x09\xe7\xe8\x36\x28\xe7\xeb\xa0\x4c\xb0\xfc\x32\x94\x39\xbc\xbb\x16\x65\x80\x31\x24\x12\x9b\x28\x12\xcd\xb9\x28\x72\x98\xb9\xcf\xe2\xdc\x5a\x8c\x02\xa6\x1f\x46\x67\x51\x48\xc3\xa7\x97\x15\x3c\xfc\x3a\xd4\x54\x83\x9b\xa3\xdb\x46\xce\xb2\x14\x3b\x47\x2b\xa3\xe7\xe8\x26\xf8\x71\x6f\x61\x79\xd5\x0a\x45\x65\x12\x97\x7e\x30\xdd\x18\x2f\x72\x67\x33\xe7\xa2\x14\x47\xa2\x69\x17\x45\x8e\x7c\xe6\xc3\x90\x67\x79\xc1\x7e\x75\x43\x81\x6d\xb3\x4d\x9e\xf0\xad\x59\x78\xc6\x58\x0d\x9b\xa5\x27\x47\xf4\x2e\xb7\x62\xef\x8b\xe9\x44\x23\x8e\x49\x10\x15\x67\x1b\x47\xf4\x48\x82\x39\xe5\x0f\x7c\xd8\x2f\x4b\x04\x13\x30\xac\x4e\x55\x83\x07\xf3\xce\x21\x14\xda\xe8\x11\xac\x2c\x67\x85\xc4\x13\x6b\xb2\x4b\xca\x5e\xea\xde\xef\x0e\xd0\x91\x26\x8f\x7e\x13\x3c\x31\x87\x5b\x2a\x51\xfe\x78\xf3\xc4\x14\x85\xdb\xc3\x0b\x26\x32\xbb\x93\xdb\xcf\xe3\x68\x4c\x99\x64\xb2\x45\xee\x43\x75\x2b\xd2\x79\xcd\xcc\xe0\x53\xf8\xad\x4d\xd0\xaa\xe8\x2f\x55\x05\x38\x9b\x8c\x3a\x22\x5a\x7c\x80\x23\x4e\x5c\x82\xd9\x98\x7b\xf4\xb0\x2b\xf6\xf0\x22\x15\xf0\x5d\x72\x5f\x9e\x2a\x7d\x33\x60\x55\xc4\xa5\xc3\x47\x0f\x7b\xa2\xfd\xd5\xa6\xa0\xe2\x54\xce\x87\xef\x39\x96\xdf\x2a\xf6\x83\x7c\x1c\x45\x55\xf8\xf7\x1c\xe7\x7f\x47\xcc\x4b\xad\x0e\x68\x07\x9a\xe1\x7f\xb5\x09\xd0\xee\x69\xca\x66\x60\x4f\x3b\xb0\x29\x99\x82\x52\xde\x5e\x82\x72\x55\xa1\x8b\x6d\x9f\x03\x9b\x15\xa4\x29\x03\x77\xad\xe1\x45\x8b\x6c\x10\x71\xc6\x01\xb4\xf3\xdf\xca\xac\xe0\xe1\xb0\x47\x70\x52\x99\xcf\x80\xcf\xd2\xf4\x03\x9d\x35\x47\xd6\x77\xcf\x86\x81\x15\x3b\x72\x52\x1c\x38\xbc\xc0\x47\x65\x19\x4e\x29\x8e\xcc\x91\x9b\xe4\xf6\x23\x4d\xe3\x91\x9d\xe0\x40\x31\x09\x64\x64\x27\x60\x28\x25\x96\x8d\xec\x04\x17\xea\xc8\x01\x3b\xf2\xc2\xe1\x46\x75\x8a\xa7\x3e\x17\xf0\xc8\x0f\x89\x07\xab\x53\x3c\x70\x18\xdb\x28\xc9\x85\xf4\x4d\x8f\x9b\xe3\x96\x33\x27\x08\xa7\xb9\xb0\x82\xea\x47\xde\x75\x77\x25\xaf\x75\xcd\xcb\xa1\xd6\x68\xf3\x71\xaf\x65\x5e\x2a\xb5\x46\x5b\x60\xc1\x00\x0b\xa3\x35\xda\xdc\xec\xb5\xf0\xd5\x54\x6b\x64\x7e\x5e\x9d\xf4\x36\x87\xdf\xd8\xa5\xcb\x01\xb7\x8d\xaf\xf0\x41\x14\x25\x45\x99\x0b\x22\x71\x7b\x15\x25\x05\xf7\xce\xc2\x7e\x3c\x54\xbf\x4e\x74\xe2\x36\xfa\x6d\x39\x6f\x89\x92\x82\xbb\x6e\x89\x92\xe2\xd1\x43\x05\xf6\x58\x57\xb4\xf5\xdd\xa3\x92\xba\x18\x7c\x8d\x2b\x23\xfb\x68\xf8\x15\xbd\x71\x01\xb8\x6d\x86\x70\x90\x14\x2b\x5a\x5e\x18\x25\x2a\x0c\x2e\xa0\xb9\x8a\x92\xd7\x32\xaf\x88\x92\x42\x8a\x8a\x4f\xae\xe5\xd2\x85\xf7\xaa\xde\x0c\x62\xb3\x51\x14\xbb\x3b\x3b\x88\x3b\x3b\x88\x3f\xaf\x1d\x04\xd1\x86\x10\x5c\x54\xba\x25\x1b\x88\x06\xa6\x0d\x36\xab\xe7\xa6\x0b\x29\x18\xa4\x6b\xcf\x1d\x7d\x8f\x84\x7a\x3e\xa3\x89\x7a\xaf\xd8\xe3\xb6\xdf\x4c\x00\x57\x0e\x1c\xa4\x64\x39\xf0\xda\x46\x58\xea\x6f\xfb\x79\x22\x70\x52\x29\x3f\xf2\xff\xbf\x7c\x21\xed\x36\xe2\xb3\xa9\x7c\xb9\xc0\x7f\xec\xa0\xa7\x86\x51\x22\x5a\x6f\xec\xf1\x63\x4a\x0b\x6c\xf2\x0b\x06\xe4\xed\x5c\x3e\x04\x05\x5e\xc2\x2a\x31\xac\xdd\xb5\x7c\xcf\x8d\x5d\x4d\x29\x5a\xaa\x99\x74\xad\xb8\x32\xd2\x91\x7d\xec\x1a\x06\xed\x80\x1e\x6c\xd0\x6e\x37\x52\x69\x8a\x06\x56\xfe\xc6\xb1\x03\x5f\x3f\x36\x46\xc6\x38\xa3\x8c\x98\xe4\x7a\x30\xdd\xb2\x70\x72\x0f\xa3\xc9\x84\x82\x41\x32\x47\xb9\x75\x2e\x39\x57\xef\x42\xf0\x71\x44\xa2\x44\xcc\x92\xb4\x5d\x4e\xbc\x87\x10\xf3\xe8\xc2\xb6\x43\x5f\x3f\x82\x05\xe7\x30\xaa\x17\xe5\xa8\x3c\xf7\xbf\x99\x35\xe9\xae\xf4\x56\x4f\x13\xa4\x22\xd5\x55\x30\x9a\xce\x4f\xa3\xc4\xf5\x70\x53\xa4\x53\xca\xb8\x3b\xab\x81\x4e\xfb\x7c\x51\x05\x8b\x05\x4d\x60\x2d\x05\x09\x7f\x03\x61\x61\x57\xd4\x56\x77\x0f\x23\x18\xd3\x2c\x1a\x33\xf6\x24\x7b\x55\x5f\x58\x5c\xa0\xa6\x13\x01\x0b\xfb\x50\x25\x6a\xe5\xf0\xea\xf4\x7e\x55\x68\x55\x7a\x0b\x7e\x65\xb2\x43\xea\xb1\x3b\x0e\xe2\x58\xe0\x57\x5e\xe3\xf0\x11\xcd\x02\xbd\x74\xf3\xe8\x37\xe1\x5c\x10\xae\xeb\x66\x41\xde\x63\xff\x4b\x42\x03\xf7\xbf\x9e\x7b\x3b\x8c\x6f\x65\x0b\xea\xd7\x99\x56\xa2\xc6\xef\x9d\xc9\xb7\x70\xc5\xaa\x58\xdf\xdd\x05\xe9\x62\x12\x25\xd6\x5b\xa5\x3a\x24\x68\xaf\x45\xa2\x2a\x71\xc3\x6c\x2b\x0d\x78\xee\x5e\xfe\xb4\xfc\xe8\xcf\x35\xbe\xae\x86\xa6\xc1\x32\x33\x6a\xaf\x1a\xf4\x3a\x8c\x5a\xbb\x00\xe8\x92\x27\xa4\xdd\x26\xa3\x66\x06\x59\x08\x65\x5e\xb3\xac\x15\xf0\xc6\x78\x3f\x57\x4e\x28\x99\xd1\xf7\xdc\x4b\xeb\x2f\xfc\x38\x93\x7b\x8f\xbc\x15\x0e\x30\xc3\x0f\xe6\x98\xc8\x80\xc4\x2b\xb1\xa8\x1b\xf3\xa2\x10\xfc\x2a\xd9\xf8\xf3\xf9\x67\x52\xcb\x6b\x87\xf0\x2b\x3f\x52\x42\x77\x62\xc2\x3a\xab\xa3\xce\xd8\xd6\x4a\x70\x87\x36\x25\x3f\xf2\x64\x42\x20\x2f\xe1\x1b\x60\x91\xce\x17\xc5\x25\x56\x09\x36\xd8\x44\x6b\x57\xa1\x49\x8f\x88\x3d\x8d\x40\xfa\x58\x01\x37\xd2\xe3\x54\xa9\xaf\x29\x2f\x26\x2a\x07\x22\xaa\xac\x1b\x83\x71\xb1\xb2\xe1\x11\x0b\xae\x33\x0e\xfd\x18\xaf\xdc\x3f\xd4\xab\x28\x2f\x9c\x97\x7f\xc7\xc6\x68\x4e\x3c\x4e\xa1\x2a\x47\xaf\x6b\x76\xb7\x17\xf5\x2e\x48\xde\xd4\x2f\x17\x21\xb7\x6c\x15\xef\xe0\x94\x2a\xb2\x48\x0b\xf4\xd6\x95\x17\x96\xc2\x11\xf7\x3b\x44\x8c\xb7\x7d\xea\x09\xa1\x00\x35\x9f\x15\x19\x7b\x9b\x5a\x8f\x7c\xfb\x2a\x59\x90\xf6\xed\x97\xed\x2c\xc4\x6c\x9e\xec\xe2\x1e\x6b\x58\x3c\x8c\x8d\x5d\x57\xd1\x2f\x5e\x6b\xb9\x2f\xb4\x38\xa4\x16\x81\x3a\x29\x7e\x75\xab\x5e\xcd\x0d\x06\x72\xba\xe9\x19\xcd\x2e\x8b\x19\xf8\x22\x41\xf5\x60\xec\xb8\x8e\xa7\xa4\x45\x9a\x83\x1f\xe3\xa5\xae\xff\x86\x42\xf9\x5e\xba\xd5\x26\x5c\xa5\xf3\x55\x8f\xb4\xdb\x52\xf9\x5e\xa1\xa4\x78\xcb\x67\xc9\xd2\xe9\x29\xf5\xdd\xd5\x49\x6f\xb3\x51\xac\xbd\xaf\xa8\x93\x83\xdb\xe8\x6a\xa5\x5c\xc6\x40\x4a\xb4\x72\xd2\xcc\x8c\xfd\xcf\x55\x65\xf0\xeb\xa1\xfe\x79\x82\x92\xb7\xf1\x87\xa5\x9b\x63\x69\x5c\x39\xc7\x7e\x49\xed\x1c\xfb\xfd\x18\x55\x87\xf4\x73\x4e\x8d\x0d\x34\x74\xce\xdd\xfb\x2a\x2a\x3a\x56\x78\x15\x1d\x1d\x87\xb7\x95\x74\x2c\x75\x45\x2d\x9d\x59\xa4\x42\x4d\xc7\x5b\xac\x2a\x7b\x1d\x45\x1d\xc3\x6d\x89\xa2\xae\x99\xa3\x7c\xd1\xad\x06\x8a\xba\x46\xd1\xbc\xbe\xd6\xe3\x3a\xcf\xed\xdf\x2a\xe4\xc1\x8b\xaf\x42\x20\xb2\x84\x4d\x22\x3c\x7d\x45\x22\xb1\x0b\x55\x90\x89\x6c\xb7\xba\xfc\xb5\x74\xba\x5c\x92\x6a\xf2\x66\xce\xd3\xde\xed\xbe\x96\x53\xa3\x6c\x40\x77\xb7\x1f\x7d\xa4\xf2\xfd\x8e\x87\x0f\x23\x17\xb7\x51\xde\xdc\xb7\xed\x98\x66\x45\x10\x25\x7e\xff\xb6\x0e\x22\xf9\x6d\x52\x0d\x51\x73\xa0\xbe\x99\x5e\x4d\xd6\xa2\x88\x95\x51\xeb\x0d\xa2\xa0\xd9\x9c\x1d\xf9\xa3\x09\xd4\x6c\xf6\x3b\x14\x5e\x6b\xc9\x34\x3a\xa3\x89\x34\x69\x31\x8f\xd4\x65\xee\x72\x2d\xfb\x17\x7e\xcc\xd6\x16\xb7\x80\x65\x5e\xb9\xd3\xae\xdf\xfe\x16\x43\x34\x5f\x22\xdc\x39\x6d\xab\xf0\x0a\xc7\xe9\x19\xcd\xb2\xf3\x2c\x2a\x0a\x0a\xe6\x5e\xbc\x57\x2d\xb2\x01\xbd\x6f\x8c\xbb\x73\xd0\xb2\xe7\xf8\x21\x3f\x58\x41\xe8\xa3\x68\x94\x08\x14\x16\xae\xdf\x61\xfb\xad\x7d\x23\x64\xba\x5a\x49\xab\x39\xad\xb5\x2d\xc1\x9b\xc7\x85\x80\x1f\x83\x83\x01\xa8\xc2\x83\x39\x5b\x15\xe0\xf5\x50\x68\xb3\xd8\x78\x19\x27\xa0\xfc\x8e\x21\x8e\x3e\x51\x12\x90\x3c\x4a\xa6\x31\x55\x7e\xb8\x00\xb2\x6f\x98\x44\x03\x05\x73\x37\x33\xdc\x2d\x07\x6f\xed\xcb\x17\x72\xdc\x3e\xde\x3c\x69\x9f\x74\x95\x30\x58\xe3\x06\x40\x74\xcf\xc4\x3b\xfb\xc2\xae\x0d\x4b\x44\x77\x6e\x03\xc5\x51\x01\xb6\x0a\x9b\x3d\xf2\x00\xec\xb1\x87\xd0\x97\x4d\xec\x88\x46\x77\xc8\x11\x64\xa5\xa3\x86\x9e\x74\xed\x50\x76\x5a\x90\x0e\x1d\xee\x4b\x40\xdd\xc0\x60\x40\x82\x38\x26\xa7\x41\x1e\x8d\xb9\xff\x03\x78\x2c\xb0\xbd\x25\x14\x38\x71\xca\x4e\xc6\xb2\x37\x3d\xb2\xbd\x55\x67\x74\x62\x2e\x6c\xc1\xd1\xe4\x09\x5c\xea\x22\x09\x9d\x82\x00\x09\x41\xa1\x8e\x4f\x5a\x64\xf7\x27\x58\x9f\x3a\xed\x21\x4f\xac\x54\xa6\xed\xc9\xda\x56\xe5\x00\x33\x5a\xda\xb3\x8a\xd5\x8e\x5b\x2d\xa5\x59\xed\xf6\xcb\x70\x08\xe3\x10\xdd\x8e\xb5\x8d\xa2\x22\xf7\xee\x11\xfc\x7d\x8c\x7e\x23\x17\x70\x27\x72\xd7\x55\x91\x31\x06\xd3\x6b\xcd\x8d\x58\xbe\x55\x53\x23\x67\xc1\x9c\x1b\x31\x61\xe6\xd4\x20\x8f\x6b\x37\x9c\x19\xab\x5f\x15\x13\x83\xda\xfc\xda\xf3\x72\x9b\x13\x63\xba\x3e\xd1\x8c\x14\xcd\x04\x9c\x8d\x5a\x60\x8b\xb0\xc5\x91\xce\x0f\x49\x2d\x61\xac\xb0\x29\xa6\x62\xf3\xa1\x02\xdc\x3a\x39\xde\x16\xa0\x32\x8d\x83\x28\x88\xcd\x13\x2b\x41\x7f\xbb\xbb\x03\x60\xf5\x1a\xdb\x03\x1e\x8b\x18\x62\xfd\x9e\x80\x1a\xbb\xa5\x89\x8c\x26\xa4\x83\xb2\x10\x87\xb4\xf9\xf1\x35\x27\x16\x18\xb6\xef\x35\xc4\x66\xc5\x94\x8b\x4d\x42\x9e\xaa\x7d\xf3\x0c\xf3\xe6\x9b\xea\x96\x8a\xbf\xe7\x4c\xb8\xf8\x6c\x19\xf3\x6e\x54\x74\x6c\x56\x8e\xa7\x5b\x7b\x5f\x6b\x34\xcf\x2a\x83\x0f\x45\xe4\x97\xce\xaf\xe1\x45\xb1\x74\xb7\x17\xde\x8a\xe2\x20\x2f\xc8\xf1\x09\x13\x26\x78\xbd\xd7\x9a\xf6\x75\xff\xbc\xab\x39\x00\x39\x8b\x38\x3e\x96\xe0\x40\xa3\x5f\x42\xc1\xa7\xa2\x81\x26\x44\x52\x61\x1c\x8b\x8e\x30\x8a\x03\xdb\x37\x4d\xe4\xf4\x92\x84\x74\x12\x2c\x63\x50\x84\xe6\x4b\x26\xa7\xaa\x8d\xb9\x25\xdc\xd4\xf4\x44\x98\x47\x7b\x16\x8d\x63\xd4\x35\x18\xb0\xde\x11\x57\x14\x85\x1b\x9e\xde\x4a\x8d\xea\xa5\xaf\x76\xa9\x23\x46\x4b\x24\xb7\xd7\x08\x50\xbc\x20\xe5\xe3\x16\xa3\xf8\x1e\x69\xb1\x45\xc0\xfe\x3b\x69\x9d\x68\x6a\x17\x10\x28\x0d\x0a\x25\xcb\xd8\x7e\xf6\x80\x66\xb3\x11\xda\x6c\x07\x73\x56\x7f\x6b\x16\x82\xeb\xa4\xca\x59\x09\x7c\x6f\x10\xce\xf2\xf8\xac\xe7\x70\xc3\xcb\x86\x63\x8c\x97\xfd\x0b\xab\xde\x22\x62\xc1\xad\x3a\xff\x3e\xe6\xa7\xf1\x7f\x9f\x74\xeb\x45\x04\xa1\xbc\x55\xde\x1e\xca\xef\x1d\xac\x30\x16\x12\xba\x39\xeb\x90\x6f\x4f\xdd\xbb\x2c\x0b\x67\x9e\x4b\x0b\x71\x8f\x6e\x6f\x0c\x5e\x7f\xd4\xe6\xad\x8c\x70\x85\x2a\x9d\xa0\xda\x6c\xa1\xc6\x1b\xac\xb2\xff\xc6\xc6\xc4\x3b\xa4\xf4\xcf\xef\x18\xd5\xf5\x2b\x4b\xe3\x09\xf6\x27\x2b\x58\x99\x53\x48\xbd\x4c\x3e\x3e\xf1\x39\x11\xef\x2f\x96\xf9\xac\xe3\x78\x26\x95\x2f\xb5\xa5\x9b\x51\xb7\x66\x36\x16\xd7\xe7\xfa\x99\xcf\x01\x28\x6e\x09\xf9\xf1\xec\x9c\xf5\x08\xf6\x2f\x6b\xb9\x27\xbd\x91\x53\x5f\x31\x81\xd8\x99\xef\x8d\xe7\x0f\xba\xee\x48\x1d\x02\xf1\xbf\xff\xfc\xf9\x3c\xb2\xd6\x78\x62\x2d\x9d\x08\x36\x9b\xe0\x2a\xb5\x62\x3e\x56\x9e\x8d\x35\xe7\x8e\xd0\xd2\x1d\x19\x4b\x12\x79\xb4\x6d\xe2\x13\x94\xdf\x8f\x4e\xb2\x74\xee\x35\x37\xe0\x50\x3e\xde\x72\x6a\x3f\xd8\xb1\x0c\x84\x0c\xcb\xa0\x15\x1e\x4c\x49\xa6\xc6\x5b\x6e\xc0\xa2\xc4\x40\x30\x8b\x32\xfc\x69\xd6\xb0\xaa\xaf\xc2\xab\x60\x6f\xc2\x37\x96\x5c\xd0\x15\x4f\x7c\xa0\x7b\x52\xd0\x11\xe8\xba\x4f\xb6\xc0\xf8\xa1\x2b\x3d\x3a\x0b\xe4\x95\x2d\xa2\xca\x3a\x71\xf3\x4e\xc5\xbe\x15\x05\x05\xde\x17\xfc\x8e\x1d\x97\xde\x20\xdb\xdc\xe9\x3d\xdf\x6d\x73\x06\x92\x93\x60\x52\xd0\x4c\x2d\x12\xdc\xdf\x6b\xad\x55\x7f\x19\x9f\xef\x6e\xcd\x39\x4a\x7c\x76\x93\x4a\xec\x89\xd0\x31\x6f\xca\xea\xc7\x7e\x3d\x4a\xdd\x48\xdb\x31\x6f\x2a\x19\x4d\x43\x4e\x43\xee\x57\xf7\x8d\xc1\x6e\xec\x56\xc3\x34\x62\x54\xa6\xc3\x59\x34\xed\x1b\x24\xba\x5d\xae\xf5\x87\xd8\x43\xf0\x5f\x43\xea\x97\x06\xa9\x0d\xff\xfe\x50\xc4\x7f\x47\xfb\xe8\xef\x9b\xd0\x3e\xf1\x92\x3e\x0e\xd0\x78\x5d\xd2\xb7\xc3\x88\xad\xb8\xa9\x38\xc4\x6a\xd7\xdf\x6c\x67\x31\x7b\xb1\x4a\xfd\x62\xfe\xbc\xf4\x16\x3b\xf4\xe5\x5f\x7f\xe5\x4b\x78\x21\x6e\xfd\x5c\x23\xd5\xba\xee\x77\xc8\x26\xd9\x30\x7b\xd7\xe5\x3e\x99\x78\x24\x31\xcf\xd4\x73\x0f\xc4\xd6\xa5\x9b\xf1\x60\xbb\xc2\x9f\xbd\x81\x6b\xcb\xe2\xcb\xe0\x62\x6b\x2b\x8e\x0d\xcf\xb9\x5a\x59\x5b\x5d\x53\xad\xea\xbd\x48\xb4\xba\x5e\x7b\xc1\x5b\x7e\xb5\xab\xde\xc4\x5d\x9d\xf4\x36\xbf\x75\xe8\xfd\xa3\xfa\x67\x6f\xcb\x8a\x77\x6f\xc2\x13\x09\xfc\xcf\x6d\x5d\x96\xfa\xe9\xdb\x12\xbd\x7d\x5b\xe2\x07\x6b\x4b\xcf\xeb\xb7\xa5\x7a\xfe\xb6\x44\xef\xdf\x96\xe8\x01\xdc\xd2\x7c\x01\xe7\xd4\xd8\xc0\xc2\xc6\xf1\x8f\xf2\x15\x1f\xc1\x1d\x79\x5f\xc1\x1d\xad\xfe\x0c\xee\xa8\xe9\x3b\xb8\x23\xf7\x21\xdc\xd1\x2d\xbc\x84\x5b\xde\xf8\x29\xdc\x51\xe3\xb7\x70\xdf\x3a\xae\xff\x51\x03\x8b\xb3\x65\x95\xc9\x99\x74\xad\xc2\x7f\x08\xe2\x44\x56\x67\x4b\x6c\x76\xb6\x34\xac\xc4\x96\x3e\xc3\xb3\xa5\xb6\x3c\x5b\x62\xd3\xb3\x25\xb6\x3d\x5b\x5a\xc6\x67\x9e\x7a\x9b\x2c\x8e\xdf\xd5\xfe\xec\xc8\x6f\x80\x76\x74\x0d\x0b\xb4\xa3\xc6\x26\x68\x47\x1e\x1b\x34\xbb\xf4\xf5\xd6\x48\x85\x19\x5a\xd3\x45\xd2\xdc\x10\xed\xfb\x26\xab\xa4\xbd\xcc\x29\x28\x66\xc7\x45\x9b\x07\xe4\x9b\xa6\x84\x26\x67\x24\x4c\x29\x58\x2b\xc0\xeb\xc0\x20\x09\xc1\x87\x2d\xf9\xe7\xeb\x57\x2f\x8b\x62\xf1\x8e\xfe\xbf\x25\xcd\x8b\x35\x10\xcc\x2e\x17\x34\x9d\x58\x39\xdc\x8f\x8d\x7a\xbf\xd1\x96\x78\x11\x0d\xf7\x6d\x68\xf2\xf9\x6a\x67\xcd\x08\x16\x59\x0a\x69\x26\x80\xa4\xfe\x6b\x3e\x63\xbb\x4f\x34\x4d\xd2\x8c\x8e\xe2\x28\xa1\x6b\x57\xdc\x62\x95\xe1\xa1\x91\xb7\xfb\xbb\x97\xb3\x77\x2f\x67\xff\xc4\x2f\x67\xf9\xab\x59\x61\xc3\x66\x3c\x9b\xe5\x1b\x0e\xb9\xde\xeb\x59\xb1\xf7\x1d\x15\x51\x0c\x75\x72\x7d\x26\xac\x1d\xfe\x3c\xc9\x01\x8b\x8a\x4b\xc5\x12\x75\x91\x71\x1c\xe4\x39\x39\x86\x22\x27\xa2\x9b\x3c\x43\x33\x61\x5e\xd5\xda\x00\xee\x8d\x60\x95\x0a\xe5\x2a\xe3\x20\xa4\xc2\x99\x75\x73\x3f\xe7\x00\xc9\x6a\x3a\x7a\x73\xf0\xe1\x3d\x3b\x5b\xc3\x24\xb4\xcf\x69\xd4\xe6\xa4\xd9\xfe\x84\x7e\xbf\x46\xbf\x7f\x46\xbf\xf3\xdf\x82\xd3\x54\x7e\x4c\xa2\x24\xa1\x97\xea\x8b\xce\x8b\x14\x9e\x32\xca\x94\x45\x34\x36\x13\x92\x20\x31\x13\xe6\xd1\x38\xb3\x53\xe2\x38\x72\x0a\x19\xf0\x06\xa8\xfc\x30\x8a\x4c\xb3\x20\x09\xd5\x50\x8c\xac\x9f\x8d\xaf\x0f\xc6\xd7\x5b\xe3\xeb\xb9\xf1\xf5\x7f\xc6\xd7\xbf\x8c\xaf\x37\xc6\xd7\x33\xe3\xeb\x1f\xc6\xd7\x11\xff\x5a\x3b\x29\x77\x5d\xc3\xe6\xe8\xed\xde\x33\x36\xc5\x23\xb2\xbd\xd5\x53\x89\xef\x0f\x7e\x7e\xb3\xf7\xe1\xe8\xdd\xf3\x8f\xaf\x9e\xbf\xf9\xf9\xc3\xcb\x11\x79\xa8\x33\x61\x56\x47\xfa\xa7\xce\x29\xa1\x9c\x11\xf9\x4c\xac\x04\xed\x47\x1d\x32\x3e\x3e\x3b\xfc\xe5\x0d\xb9\xd2\x35\xbd\x3d\x7c\xf5\x8a\x41\x7f\x38\x78\xfd\xfc\xf0\xe8\xc3\x88\x6c\x0e\x87\xc3\x81\xe8\xa1\xb8\xf1\x7e\x1a\xa7\xe3\x4f\x23\xd2\x66\xac\x33\x2f\xda\x46\xde\xde\x18\x42\x19\x8f\xf4\xdb\x46\xfe\x00\x83\xed\xe7\x75\xbe\x4f\xee\x42\x61\xdc\x6d\x64\x7f\xf5\x8d\x6c\x4d\xb9\x80\xc8\x67\xc1\xf6\x6d\x79\x80\xd8\xcf\x2e\x17\x45\xfa\xf7\xf7\x78\x73\x18\x43\xda\x03\x1d\x01\x83\x35\xe8\x05\x18\xb0\x9c\xb6\x37\xba\x93\xeb\xbe\x01\x28\x2e\xc7\x0f\x54\x45\x12\xb9\x77\x4f\xe6\xf6\xa5\xbf\x08\x2e\x26\xcf\xe8\x45\xdb\x7e\x45\x67\x78\xfe\xfa\x89\x6c\xb1\xd2\xb6\xf7\xe3\x2d\xe9\x2e\xd2\x2c\x4e\xe4\x65\xb8\xba\xe0\xb7\xfc\xb3\x13\xeb\xb5\x1d\x07\x95\x38\x62\x9d\xeb\xbf\xa4\x17\x7d\xd0\x5e\x0a\xcf\xbd\x3e\x1b\x23\x86\x15\x39\x6c\xdd\x3a\x3f\xd1\x71\xf5\xdb\x88\x6c\x7d\xf7\x88\x97\x44\x8f\x93\xe5\x9b\x33\xc6\xf2\x14\x8e\x5b\xa3\xef\x7e\xe8\xb5\x4c\x94\xb7\x46\x8f\x87\x57\x27\xbd\xad\x46\x3e\x9f\xee\xf8\xde\x1d\xdf\xfb\xf3\xf2\x3d\xcd\xf6\xf8\x3b\xff\x5b\xe0\x7b\x96\xec\xbe\xba\xe8\xee\x91\xdc\x65\x41\x9f\xe0\xbe\x52\xb4\x21\x9b\xd7\xf6\x07\x82\xdd\xeb\x70\x44\x93\xc7\x18\x80\x7d\x2b\x11\x7e\x99\x44\xc5\xeb\x60\xa1\xc4\xc5\xb6\x94\xa8\x47\x9c\x07\xb5\x87\x52\xd6\x64\x52\xfb\x48\xb3\xc5\xf6\xa6\x21\xe7\x8f\x50\xc6\x70\xa8\x0a\xfd\x6f\x45\xde\x69\x70\x7a\x1a\x4c\xa9\x6a\x09\xe7\x21\xe1\x7f\x64\xe7\xcd\x3d\x75\xa2\xec\xd7\xd5\xd9\x71\x7a\x46\xe3\x60\x2c\x9b\xb5\xb3\xf5\x19\x63\xe4\xcb\x9e\xfa\x2b\x47\x10\x3f\xd7\x42\xe4\xb3\x20\x49\xd2\xc4\x18\xb7\x09\xa1\xcf\x35\xa3\x0a\x88\x9a\x56\xe0\x64\x35\xf2\x40\x60\x54\xea\xf3\xd2\xa8\x1a\xa8\xae\x26\x71\x76\x1b\x79\x81\x8c\xca\xd4\x79\xcc\x1e\x9b\x07\xd0\x3f\x44\x13\xd0\x20\x57\x0f\x1c\x02\xfd\x64\xc2\xfa\x40\xf1\x5c\xc3\xa9\xaf\xb2\x62\xdc\xdf\x46\x75\xe3\xea\x9b\x16\x40\x65\x8a\x15\xca\xb0\x62\x7e\x63\x2b\xed\x88\x61\x11\x84\xc2\x94\x14\x4c\x3d\x2f\x16\x74\xcc\x36\x2f\x65\x9e\x8f\x8d\xae\x84\xf7\x14\x9f\xe5\x94\xae\xe2\x94\x32\xb8\x50\x44\xe4\xb2\x6c\xb0\xc6\xb3\x20\x0b\xc6\x05\xcd\x72\xa9\xe2\x87\x7b\x79\x51\x1a\xed\x23\xde\x36\xa2\x69\xd2\x43\xb6\xd0\x64\xb8\xe6\x77\xfb\x11\x4d\x67\x05\x91\x1e\x69\x2d\xef\xbe\x62\x0c\x86\xb4\xc9\x41\x7a\xd0\xbb\xbc\x07\xed\x78\x7c\x0c\x71\x0b\x11\x80\x81\xa0\xb4\xf0\x5a\x55\xdd\x10\x6f\x76\xfb\xbf\xa6\x51\x02\xc1\x1a\xc8\x13\xa8\x83\x8c\x48\x6b\xd8\xea\x92\x0d\x01\x5c\x62\xf8\x76\xed\xb9\x80\x80\x3d\x7f\xf6\xc9\x80\x41\xac\x38\x1b\xa2\x87\x1b\xdc\xe3\xf2\x75\xe7\xa5\xcc\x10\xd1\x74\x44\x03\x5b\x27\x98\x21\x42\x30\x0f\xd7\xc7\xb4\x35\x2f\xdc\x5b\x73\xc5\xac\x44\x09\xab\xc4\x8f\x2c\xec\x8f\xda\xe3\x28\x89\x35\xae\xcd\x0e\xb9\x07\x92\x23\xbe\xb5\x2b\x91\x7e\xc6\xe3\x3d\x0f\x06\xe4\x45\x94\x84\x84\x3f\xee\x12\x1d\x55\xf1\x9a\x99\x44\xd1\x6a\xe9\x9b\x7c\xb0\x7d\xe9\x41\x08\xa9\x19\xbd\x90\x26\xcc\xea\xcc\xc5\xd2\xf8\xa9\x87\x9d\x38\xca\xcf\x4a\xac\x9a\x2d\xfc\xee\x05\x8c\x6b\x84\x4d\xcd\x0e\x89\x36\x76\xb7\x30\xb8\x8c\x85\x8c\x6d\x3b\x74\x53\x9d\x88\xb5\x23\x42\x5f\xa8\x16\x26\xa4\xc3\x8b\xec\xee\x92\x61\xd7\x38\xa5\x9d\x66\x34\xf8\xa4\x41\xd9\x28\x37\x76\x89\x78\x55\xce\x66\x70\x7f\x16\x64\xfb\x69\x48\xa1\x06\xef\x21\x8c\x4d\xb6\x34\xc7\xc9\x8b\xac\x19\x85\xf0\x49\x5b\x89\x44\xf6\x58\x91\xdf\x8f\x46\xa0\xb9\xff\x1e\x22\xb9\xce\xcc\xe7\x45\xd9\xeb\x74\x73\xb2\x3d\x3e\xe6\x3b\x8b\x8c\x4e\xa2\x0b\x1e\x44\x6b\x78\xd1\x65\xb3\x00\x5c\xc3\xef\xde\x5e\x44\x7b\x2b\x9f\x7d\xaf\xed\x32\x1c\x41\x83\x18\xb8\x79\x65\x30\x01\x5f\x94\x4f\xc3\xd7\xbe\x70\xbb\x2e\xba\x81\xa9\x82\x51\xbc\xc0\x3c\x9f\x7d\x58\x0e\xc2\x6c\x9b\x2f\x07\x39\x23\xac\x25\x4d\x1d\x93\x34\xb3\x4d\xe8\xf2\x22\x2b\x8b\x88\x8f\x66\x94\x41\x8d\xc5\xdc\xec\x15\x9d\xe8\x7a\x2b\x1d\xac\x13\x45\x70\x70\xc3\x6b\x9b\x06\x61\xfd\xdd\xd8\x25\x89\xdc\x17\x7e\x24\x5b\xe4\x09\x3b\xd9\x90\x0d\xc2\xf6\x83\xc4\x47\x13\xc2\x85\xfc\x8c\x5e\xdc\x26\x69\x58\x31\x07\x6c\xda\xa8\x61\x0d\xbf\x1b\x71\x38\x3c\x03\x51\xc7\xef\x43\x01\xdf\x6c\x5a\x2d\x8f\xa5\x93\x65\x1c\x2b\x34\x0c\xe8\x19\x4d\x0a\xfe\x50\x00\x58\xfe\xaf\x79\x9a\x90\xe0\x34\xb2\x79\xbc\x74\x9b\xf8\x21\x7d\xb1\x8c\x63\xfb\x0d\xa5\x7c\x4c\xc0\x4a\x3f\xe0\xa5\xdd\xc7\x50\xbc\x61\xa7\x5d\xcd\xd8\xdd\x36\x0c\x41\x8a\x55\x8e\x55\xa7\xec\xbb\x0f\x26\x14\x51\x12\xd2\x8b\xc3\x49\xa7\xdd\x69\x77\xc1\x37\xe4\x83\x4d\xcf\x73\x48\x05\xef\xd8\x09\x16\x97\x0b\x2a\x9a\x03\x20\xa0\x22\xd3\x9f\x59\x27\xea\x7e\x96\x21\x84\xfb\x0c\x7e\x87\x5c\x09\x51\xcc\xb4\xfc\x53\xad\x90\x0d\xd2\xee\xb0\x99\x53\xb5\x6f\x90\x76\xb7\xdd\x68\xed\x85\x51\xbe\x88\x83\x4b\x3e\x2f\xe0\x63\x34\x29\x98\x6c\xab\xb0\x61\xbf\x59\xbb\x80\xec\x67\xbc\x58\xd5\x0b\x57\x56\x9b\x39\xf9\xfe\xe5\x65\xf4\x80\x6d\x69\x16\xc5\xd0\x69\x5f\xc6\x5b\xbc\xec\x08\xb3\xba\x2e\x79\xf0\x93\x4a\x54\xd3\xea\xf6\xad\xf2\xe1\xb3\xb2\xd9\x74\x66\xd6\x40\xb3\x00\xe3\x93\x4d\x9e\xd8\x6f\x5a\xc5\x7b\x30\xb6\x66\xb4\xb3\x91\xc1\x40\x0f\x34\x3d\xa3\x59\x9c\x06\x21\x0d\x95\x22\xd8\xb3\x26\xf0\x00\x3e\x68\x22\x29\x7b\xd3\x38\x20\x1f\x0e\x9f\x1d\x8e\xc8\x3c\xf8\x04\xaa\xe1\x28\x39\x5b\xc6\x09\xcd\x82\xd3\x98\xde\xe6\x00\xf5\x69\xc0\x7e\xbd\xbb\x49\x1e\x10\x94\xdd\xed\xf6\x33\xba\x88\x83\x31\xed\xb4\x49\x1b\x9c\xba\xb1\xd3\x42\xcb\x0c\x12\x99\x26\x67\x34\x2b\x72\x1d\x72\x13\xe4\xbe\x90\x8e\xa3\x79\x10\xdb\x4c\x36\x4a\xfc\xcc\xbe\x48\x9f\xf1\x02\x2e\xe5\x55\x86\xcf\x34\xdd\x1a\x72\x01\x4f\xd4\x54\x1b\x00\xb2\x48\xdd\xf8\x98\x2a\xfc\x4c\x93\x31\xd6\xca\xb6\x8c\x27\xde\xd6\xb8\x50\x5d\xd5\xc1\x59\x13\xa9\x25\x75\xc7\xe7\x09\xcd\x2d\xd4\xa7\xe6\x8e\x62\x1c\xf6\x39\x40\x4c\xf3\xfc\xc3\x2c\x48\x3a\x43\x70\x22\xfb\x80\x5b\x9d\x0b\xeb\x7d\x41\x58\x9b\x5d\x08\xdf\x8a\x72\x0c\x2c\xee\x2d\xc1\x4d\xb3\x40\x65\x90\x5c\x0a\xc7\x3b\xc2\x1d\x69\x52\x8e\xd6\xbe\xc0\xeb\x5e\x12\x72\xf5\x3f\xa7\xa1\x68\x72\x99\x0b\x47\xea\x39\x39\xa5\x93\x34\xa3\x7d\x87\xae\x5e\x8a\xa3\x43\x35\xee\xbf\x88\x3d\xa8\x86\xb4\x5e\xc2\x3e\x6f\x20\x5f\xad\xdf\xfb\xc2\x54\x6c\x1e\x5c\xf0\xb0\x95\x17\x51\x71\x39\x22\x8f\x41\x85\x2d\x77\x9d\x28\x17\x2e\x8d\xa1\x68\xd7\xde\x64\xd0\x24\x77\x36\x18\xc4\x8e\x51\x14\x4f\x67\x75\x61\xab\xac\x30\xa4\x3b\x63\xb4\xc3\x4e\x21\x1c\x69\x6d\x6f\x15\x10\x5f\xe9\xef\xef\x0f\xdf\xf4\x15\x96\x79\x7b\xda\x81\x25\xb8\x8e\xcd\x49\x60\x47\xf3\xec\x91\x45\x90\xe7\x8c\x77\x15\xb3\x2c\x5d\x4e\x67\xe6\x0a\x50\x03\x11\xb4\x06\xb5\xba\x97\x93\x9a\xab\x3d\x80\xd3\x92\x47\xe6\x2d\x1d\xb1\x04\x10\x6f\x3b\xcc\xea\x6a\x6a\x3b\x93\xf6\xa3\xa8\x02\xd2\x59\x8f\xf2\x17\x51\x12\x15\xd4\x42\xba\xd5\x0d\x90\x10\x51\x27\x4c\x29\xcb\xed\x28\x5a\x17\xef\xc4\xa6\xc2\xd7\x01\x3b\x2f\x25\xc0\xfd\xc9\x2f\xd4\x16\xa4\xa6\xb4\x80\x88\xc5\x87\x93\xa3\x24\xf2\x6a\xbb\xa0\x6c\x31\xa3\xe2\x87\x5a\x70\xa4\x48\x7b\x4a\x3b\xa5\x1c\xa2\x7b\xa3\x36\xaa\x7e\xa8\x6a\x3a\xbc\x33\x5d\x28\x02\x6e\xbb\x72\x42\xb3\x2c\xcd\xa4\x4b\x1a\xde\xe3\x9c\x24\x69\x41\xc6\x69\x96\xd1\x71\x31\x3a\x57\xeb\xc6\xec\xb5\xb1\x80\x58\x41\x49\x02\x4b\x9e\x09\xff\x3d\x81\xff\xfa\x45\xfa\x2a\x3d\xa7\xd9\x7e\x90\xd3\x0e\x30\x17\xae\xef\xd5\x7c\x8c\x41\xfd\x43\xdc\x32\x8b\xab\x9b\x63\xf6\xff\x89\x3e\x8a\x23\x10\xec\xf7\x1b\x13\x1e\xf7\x44\x96\xd0\x73\xf2\x9c\x8d\xaa\xd3\x86\xab\x5e\xe8\x08\xd8\xaa\xfe\xbb\x5d\x10\x7a\x11\xe5\x45\xde\x23\x8b\x98\x06\x39\x88\xc5\x30\xf2\x34\x51\xa8\x9a\xa4\x71\x9c\x9e\x47\xc9\x14\x4a\xe6\x8c\x0b\x5a\xcb\x48\xf4\xb0\x07\xfe\x15\x7a\xfa\xd9\x47\x45\x94\x58\xd5\x7b\xf0\x7e\x65\x7a\x15\x0e\x3e\x51\x58\x84\x9c\xe1\xc3\x65\x74\x04\xf6\xb4\x8a\xc9\x72\x12\x60\xac\x16\x7c\x55\xf0\x89\xe7\xa8\x15\x94\xf5\x36\xcd\xf3\xe8\x34\xe6\x53\x08\x2e\x34\x84\x51\xdf\xfb\x03\x26\x5f\x66\x05\xff\xc9\x44\x6a\x89\xad\xe7\x93\x49\x34\xbd\x14\x1f\x87\x92\x94\x1e\x90\x4f\xac\x79\xfe\xa7\xaf\xab\xe0\x53\xdc\x6c\x71\xb0\xb9\x06\x53\x97\x4b\xfc\x53\x5e\x45\x71\xb8\xa9\x86\x53\xf7\x3f\xfc\x53\x5c\x18\xe9\x3c\x5e\xe0\xc1\x03\xb5\x30\xf5\x3d\x0e\x2f\xf0\x5b\x70\x9a\x1a\x79\x9e\x12\xf2\x1e\x86\x0f\x00\xae\x6f\x70\x1e\x2f\x81\x7a\x81\x0a\xf3\x4f\x81\x05\x04\x42\x2c\x08\xf4\x01\x97\x29\x02\x21\x54\xe3\x70\x8a\x7e\x17\xf2\xb7\x2d\x52\x70\xbe\x60\x9d\x7c\xbf\x28\x39\x9d\x93\xc3\x38\x48\xd8\xc9\x20\x50\xac\x59\xa4\x0b\x5d\x59\x9a\x91\x80\xbc\x7c\xfe\x4f\x38\x84\x4b\x69\xed\xd6\x18\x8a\xda\x67\xe5\xd1\xee\x97\x19\x95\x7e\xf6\x02\x74\x95\x2b\xa2\xa0\xa0\x60\x01\x6c\x3d\x05\x39\x39\xa7\x6c\x81\x68\x07\x2b\x72\x18\x6b\x48\x1a\xfa\x85\x1a\x47\x72\x39\x4e\xcc\x52\xb8\xa8\xc3\x6a\x96\x4c\x02\x0b\x45\xbc\x04\x8e\x1a\x6b\x72\x2a\xce\x9d\x2c\x79\x08\x6f\xc3\xa2\x02\xf2\xc4\x68\x64\x84\xbf\x90\x64\x55\xbb\x7c\x03\x8e\x63\xcf\x0a\x3e\xa7\xd1\xdd\x82\xfd\x6f\x59\xe2\x45\x5a\xb5\xc0\xd1\x79\xe1\x77\x5b\xea\x6c\xb5\x7d\xc3\xc5\x0e\x08\xb9\x9d\xa5\x5e\x44\x73\x9a\x7f\x8b\x65\x9e\x08\xe5\x22\x5b\xdc\x4a\x55\x95\xf3\x63\x3e\x6c\xd1\x44\xd9\xb2\x38\xe4\xa0\x7a\xd2\x88\x28\x34\x19\xc8\xbb\x43\x36\xf7\x9a\x16\xcc\xda\x94\x97\x2b\x5d\x81\x06\x50\xf8\xc7\xc6\x37\xd6\x2c\xd4\x9c\x7f\xbe\x63\x42\x20\x2c\x7b\x59\x5e\xfc\xf8\xf2\x85\x0c\x77\xbc\x87\x1b\x51\xaf\x73\x38\xe1\xe9\xc6\x89\x48\xe0\x5c\xf6\xe4\xde\x3d\x22\x7e\xfb\x84\x7e\xd6\xa4\x9d\x8b\x4f\x18\x3e\x1f\x68\x86\x2c\x26\x0a\x2b\x9d\xc8\xf0\xa2\xdd\x6b\xb7\xf1\x85\x8b\xe5\x29\xcd\x57\x1a\x13\x4a\xa9\x4c\x97\xc8\xd8\xb1\x1e\x52\x51\x74\xc2\xc1\x64\x14\x0f\x75\x14\x13\x66\x93\x00\x5b\x9c\xa7\xed\x9c\x8c\x55\x4c\x17\x87\xb4\xcc\x90\x2f\x4d\xe8\xab\x84\x6a\xd0\x21\xd9\xac\xd3\x54\x78\x19\x24\xc3\xc0\x4f\x11\x65\xf9\x16\x2c\x3c\xf9\xee\x20\xaf\x75\xaa\x00\x56\x49\xd4\x4e\x5d\x6b\x72\xc3\xbf\x16\xcc\x72\x7f\x11\x2f\x73\xdd\x05\xf1\xed\x75\x6f\xa8\x80\x4c\x4d\xd2\x8c\x8e\x3f\xe5\xf2\xd8\xc4\x79\xa4\xbc\xe6\xcc\xc5\x63\xb9\xf8\x12\xfc\xf8\x7a\xa3\x11\x73\x92\x1f\x7b\x23\x11\x9b\x31\x85\x51\x03\x6c\xfd\x07\x1a\x1e\x3b\xb6\x83\xe0\x4a\x62\xe6\xac\xba\x8d\x89\x13\x95\x5a\x1a\xb4\xc1\x7f\x86\x17\xc7\xc3\x07\x3f\x04\x0f\x26\x27\x9f\x1f\x0e\xaf\xfe\x67\x10\xf5\x0b\x9a\x17\x0a\x7c\x85\xb1\x57\x0c\xf9\xeb\x0c\xb6\xc1\x30\xe1\xfc\x3f\xf8\x4f\x67\x78\xd1\x7d\x52\x39\x4e\x4c\x7f\x83\x81\x8e\x95\xc5\xa3\x61\x41\xef\xb8\x07\x61\x61\x74\x38\x87\x77\xbc\x6c\x3f\x46\xa3\x36\xe9\x57\x38\x02\x24\xa6\xab\x0a\x6f\x67\xcc\xbe\x30\x36\x87\xc0\xf6\x1e\xbc\xf0\x82\x59\x5d\x86\xd0\x5d\xed\x1c\x9c\x1d\xe7\x73\xf6\xef\x38\x58\xe4\x20\x3b\xc4\x31\x91\xdf\x3d\xec\xa1\xd1\xee\x31\x77\x3c\x8f\x3a\x6c\x34\x70\xa8\xb6\x77\x8e\x1d\x1a\x8c\x67\x64\x1c\xe4\x4e\x35\x51\xce\x09\x65\x39\x17\x33\x84\xa8\x89\xaf\xb2\xe6\x34\xc5\xdb\xca\x97\xf3\x39\x0d\x4b\xc9\xcb\x6a\xee\x96\xc9\xcc\xaa\xbd\x8a\xdc\x06\x03\x3e\x1e\x0b\x37\x81\x2a\x29\x7e\x39\x3b\x90\xd6\x87\x08\x88\x97\x41\x0e\xce\x68\x66\xc1\xb6\x6c\xc4\xd4\xa5\x48\x69\xc7\xe7\xf0\xe5\xe1\x10\xee\x28\x89\x45\x21\xe0\xbc\xbb\x98\x91\x98\xc2\x73\x6a\x14\x81\x6f\xb1\xa0\x19\xeb\xad\x9c\x86\x04\xa2\x17\x4e\x23\x1e\xe0\x2e\xc8\xe9\x3c\x58\xb0\xe9\xd8\x34\x34\x7d\x1d\x65\xc1\x80\x3a\x0d\x6e\xd9\x36\x1f\x75\xc9\x4f\xe4\x7b\xb6\x9d\x8b\xac\xe3\xe8\xa4\x5f\xa4\x47\xac\x21\xa1\x0b\x5a\xdf\xdd\x45\x99\x40\xf4\xd5\x15\xfe\xb8\xeb\xa9\x11\x6b\x97\xac\x1a\x4b\x7c\x85\xa3\x65\xa9\x59\xbe\xc1\xf8\x75\xfc\x05\x45\xa5\xaf\xc5\x51\x4f\x52\x63\x09\x29\x16\xe9\x6d\x92\xa2\xd4\x5e\xab\x7d\x79\x05\x4a\x44\x3a\x63\x45\x7d\xf6\xab\x6b\xd1\x4e\xbb\x2d\x48\xc9\x25\x53\x03\xbf\xd7\x22\x5a\x04\x34\x76\x7a\xcf\x2a\xaa\x20\x63\xd9\x0b\x74\xed\x6e\x93\x34\x30\xbd\x99\x36\xfd\x63\x44\xfa\x03\x3b\xf8\x4c\xb8\x03\x7d\x79\x13\xa7\x28\xdc\x20\xe0\x3a\xfa\x35\x29\xc8\xee\xff\xc6\x6e\x29\x71\x23\xf2\xb2\x19\x69\x6d\x4d\x95\xa4\x69\x95\x34\x25\x4f\x2d\x69\x1a\x6c\xb4\x48\x99\x44\x19\x85\x64\x6b\xc8\x7d\x06\x3d\x10\x17\x84\xbc\x4d\xfe\x3e\x61\x78\x41\xb8\x71\x87\x6b\xdc\x55\x4b\xc9\xfe\xdb\x7e\xe1\x7d\x00\x73\x6d\x65\xc0\xd5\x8c\x7e\x2d\x71\xc6\xbb\xf1\x49\xa7\xba\x12\x1f\x48\x86\xe7\xbb\x6d\xd5\x46\xeb\xa9\x48\x5c\x7e\xf9\xea\x33\x21\x64\xe8\x45\xb8\x52\x52\x35\xea\xd7\x54\x3d\xf2\x70\xe8\xbf\x25\x90\x8e\x88\xe5\x69\x3a\xd7\x52\x6e\x7d\x90\x4d\xef\x49\xd2\x77\xf5\x65\x04\xde\xe4\x1b\x99\xef\x0c\x48\x3a\xbc\x1b\x96\x5c\x28\xfb\x96\xe4\x45\x90\x8c\x19\x17\xd1\x85\xbf\x7c\x51\x48\x13\x85\xe1\xf5\x1a\xfc\x32\x1c\x67\x78\x53\xb9\x6d\x04\xf0\x22\x55\x65\xbb\x29\xa2\xe4\x79\xb8\x0e\x4b\xef\x1d\xe3\xa2\x86\x28\xf2\x84\x48\xf2\xe2\x47\xb0\x56\xd1\x33\x18\x0d\xef\x5b\xfb\xf6\xd0\xc3\xfb\xd2\x18\x37\xb2\xc7\xf5\xd8\x79\xa1\x8d\x48\x56\xc5\x8f\x2c\x7a\x2d\x0c\xc9\x12\xed\x86\x23\x62\x7d\x2a\xea\x87\xc3\xbb\x7e\x8d\xc1\x1c\x8a\xbe\x35\x5c\x0c\x4c\xbc\x48\x96\x71\x0c\x51\x12\x3a\xee\x0a\x01\xc3\x6d\x50\x61\x78\xc6\x2e\xee\x6b\x1b\x8e\xfc\x94\x77\xb6\x01\x3b\xe0\x80\xd7\x61\x06\x3c\xe9\x5a\x13\x29\xba\xd7\x74\x34\xe0\x02\xb0\x7e\x2c\x4e\x44\x8d\x86\x23\x71\xa3\x62\x34\x64\x69\x50\xb0\x72\x0c\xf6\x71\x84\xef\xa3\x60\x23\x97\x4a\xaa\x33\x07\xf1\xf7\xdc\x5c\x57\xda\x02\xa1\x72\x0c\xac\x98\xfd\x6a\x40\xb9\x4e\xca\x2e\xdd\x7d\x6a\x7d\x1d\x6e\x26\xf9\x33\x5c\x6d\xcc\x7a\x45\xc6\x10\xf6\xa9\x43\x3d\x7b\x1b\x3e\x90\xae\x32\xea\x40\x8c\xfb\x39\x9b\x40\xba\x9c\x93\xd3\x38\x1d\x7f\x22\x33\x1a\x84\x34\x63\x1f\xe9\xdc\xb6\xda\x88\xf2\xa7\x2c\xd9\x27\x34\xcc\xe8\x85\xf2\x8b\x0e\x65\xc9\x24\x8a\x0b\x5b\x99\xe9\x21\x58\x80\x35\xdc\x0f\xb3\x94\xca\x93\xfe\x77\x9b\x5b\xfa\xa8\xcf\xc1\x6b\xf0\x52\x7e\x50\xe7\x75\xe1\xaa\x7c\xe7\x74\x17\xca\x17\x71\x58\x9f\xb3\xd7\xdc\x7e\x5c\x63\x66\xe2\x94\x89\x79\x8b\x68\xec\xce\xc3\x07\x96\x5c\x37\x0f\x85\x02\xaa\x98\x00\xa8\xc9\x98\x00\x28\x56\x39\x01\x8f\x1e\x6a\xfc\x73\xe8\x6b\xe3\x1f\xaa\xc2\x35\xf9\xd0\xef\x00\x5d\x0b\xfb\x25\x8e\x47\x84\xc8\x37\x92\x3f\x7a\x32\x15\x1e\xfd\x8c\xd4\x2f\x9e\x0e\x82\xe1\x88\xff\x27\x53\x84\x05\xc9\x48\xff\xe4\x39\xc8\xba\x64\x84\x3f\x64\xb9\xa3\x62\xf2\x78\x24\xfe\x97\x69\x60\xaf\x32\x92\x3f\x74\x3d\x1c\x56\xfe\xd2\xe9\x02\x5e\xfd\x14\xf5\xb8\x46\xb7\x23\x5f\x22\x87\x76\x6d\x39\x47\x9e\x34\x03\x56\x9a\x4d\x8e\xec\x04\x39\x8e\x5f\x28\x8c\xe2\x17\x8a\xc6\x00\x69\xe2\x87\x84\x53\xd2\xe2\x08\x7f\xc8\x5c\x53\x65\x3d\x72\x52\x14\xd6\xb8\xa0\x3e\xd2\x3f\x79\x0e\x92\x8e\x47\xf8\x43\xe6\x1a\x27\x91\x91\x9d\x20\xa1\x50\xbe\x95\x63\x1d\xdd\x47\x6e\x92\xec\xa1\x03\xe9\x24\xc9\x3a\xa5\x30\x36\x42\xbf\x71\x7f\x93\xe9\x48\xfd\x92\xe9\x7c\x4f\x1d\xa9\x5f\x6a\xf4\x7c\xbd\x8f\xf4\x4f\x35\x26\xb6\x4b\x8e\xe4\x0f\x99\xca\x36\xac\x91\xf8\x5f\xd5\xc1\xf8\xdd\x48\xfe\x90\xa9\xc0\x36\x46\xf2\x47\x0f\x16\x18\x77\x50\x27\x5e\x75\xb7\x46\x9b\x3f\xf4\x2a\xfd\xdb\xf4\x5a\xcb\x62\xf2\xb8\x35\x7a\xfc\xdd\xd5\x49\x6f\x6b\xb3\x89\xc7\x07\x73\x09\xef\xf2\x05\xdc\x12\x8e\x0e\x5a\x23\xd2\x1a\xf6\xb7\x86\xfd\xcd\xd6\xda\x95\x74\x05\xb7\xd5\x28\x52\xf1\x9d\x27\x89\x3b\x4f\x12\x7f\x05\x4f\x12\xa2\x96\x35\xd7\x17\xdc\xdf\xe9\x64\x92\xd1\x4b\xf2\x4b\x14\x8f\x3f\x51\xf2\xe3\xaf\x74\x32\xb1\xdd\x49\x34\xf4\x18\x07\x60\x51\x90\x90\x43\x26\x71\x07\x00\x15\x05\x89\x0b\xf6\x22\x38\x65\x60\xff\x48\xa7\x34\xce\x0b\x1a\xc7\x34\x23\x3f\x4e\x20\xd1\x05\xfe\x39\x38\x23\xbf\xa4\x69\x48\x7e\x9c\x96\xba\xb9\x78\xa8\xdd\xfb\x08\x5f\x90\xaf\x83\x24\x98\x9a\xbe\x27\xfa\x03\x86\x85\x41\xc6\x01\xe6\x1c\x40\xfa\x98\x38\x38\x85\xc3\x91\x0d\x1c\x9d\x06\x89\x04\x79\x0e\x66\xfc\x36\x04\x97\xbc\xf2\x01\x2d\x66\x12\xf0\xd9\xd3\x0a\xb8\xf0\x54\xf9\x9b\x9d\x55\xd5\x97\xcf\x54\x7d\x6f\xc0\x33\x79\x19\x60\x42\x0b\x09\xf8\x96\x66\x39\x3c\xa5\x2a\x87\x5e\x08\x10\xd5\x89\xf3\x20\x9b\x57\x75\x83\xe5\x2b\x60\x5a\x14\x10\xb5\xc9\x85\xcf\x45\x96\x04\x95\x5c\xc5\x80\x94\xec\x82\x9d\xa8\xb4\x73\x8f\x28\xb6\x2a\x44\x61\xe5\xcb\x7d\x84\x70\x20\xe9\x8d\x49\x3c\xdc\xa0\x49\xe8\xe9\x1b\xcf\x90\x60\x4f\xe1\xc4\xe4\x42\x9d\xb2\x74\x85\xc9\x2c\x5d\xd0\xac\xb8\xf4\xc0\x2d\x44\x96\x04\x7d\x59\x14\x8b\xb7\x59\x7a\x16\x85\x5e\x72\x63\x0b\x75\x21\xb2\x15\xb1\x2d\xc6\x15\x25\xa2\xc5\xd8\x2e\xd0\xcc\xa3\xe1\xda\x9a\x92\xd5\x7f\xa1\xa7\xdb\xa4\x23\xab\x31\xbd\xf2\x66\xf6\x0a\x49\xe8\xb9\xb5\x6c\x74\x49\xe4\xa0\x57\x84\x5a\x45\x3d\x97\x50\x08\x88\xf2\xb7\x2e\xf4\x9c\x2d\x17\x70\xd4\x8f\xab\x08\x4f\x45\xe6\xb3\xa7\x4e\x5e\x3e\x93\x25\xdf\xcf\xdc\x92\x09\xac\x01\x96\xfb\x86\x16\x4e\xee\x42\x13\x3e\x03\x91\xeb\xc0\x81\x3b\xfd\xed\x37\xd9\x06\xa3\x6b\xb7\x0f\x9a\xc0\x01\x48\x7c\x76\x30\x8c\xa6\x6c\x7d\xd4\x08\x16\xd1\x48\x6d\x86\xe2\x7f\x7e\xe4\xc0\x9d\x14\xd8\xca\x8d\xa2\x98\x7c\x46\xc6\x57\x4f\xc1\x20\x7a\x19\xe1\x0f\xa7\x89\x8f\x6a\x0d\xf0\x1f\xce\x00\x05\x40\x47\xb7\x2f\xc8\x39\xa2\xf9\x08\xfd\xee\x70\x63\x9e\xab\xee\x0e\x93\x98\x06\x03\x70\xc1\x9b\x53\xa2\xc7\x90\xf2\x9d\x18\x7c\x02\xad\x31\x72\xf3\x8c\xaf\x6e\x6c\xa5\xe3\x62\x42\xa3\xac\x53\xc6\xd3\xa4\x98\xf2\x70\xcc\xe0\x7a\x1a\xc7\x85\x57\x26\x6d\x4f\x5f\x32\xca\x83\x45\xe8\x5e\x7c\xa2\x74\x71\x90\xbf\xbf\x4c\xc6\x51\x32\xad\xec\x0a\x94\xb5\xe0\x9b\x51\xa0\xa7\x23\x98\x2f\x3c\xd5\xf6\x2b\x16\x94\x7c\x06\xc3\xdd\x49\xc1\x97\x07\x46\x3e\x99\x95\x50\xf0\xed\x81\x13\xef\xae\x25\x18\xfb\x74\xa0\xf0\x13\x5c\x0e\xa8\x52\xbc\xb0\x46\x9d\x32\xc1\xd3\xb6\x7e\x4f\x25\x9b\x17\x29\xde\x5a\x6d\x68\x94\xe6\xa9\x1b\xe3\x52\xd6\x5e\x85\x53\x6e\xe2\x28\x21\x7f\xa1\xfe\x91\x61\x28\xf1\xed\xc0\x61\xd3\x16\x0e\xa9\x52\x3c\xb0\xee\xad\xb0\x2c\xb3\x6f\xdf\x16\x3a\x7d\x2e\x2b\xeb\xe4\x78\xda\x3d\x78\xba\xf7\x06\x35\xc6\x3e\x1d\x28\xed\x9e\x86\x83\x89\x6f\x1f\x9c\xf4\x9c\xa2\x00\x21\x81\xed\x62\xf6\xc2\xe7\x5b\x3f\x7e\xc9\xcd\x2f\x85\x4c\xef\x8a\xe6\x75\x1d\xdc\x49\xdb\x90\x65\xd7\xa7\x61\x94\x81\xaa\x78\x1c\x2c\xe0\xf5\x05\xba\xc0\xf4\xcc\xe8\xc1\xfe\xde\x5b\x63\xed\xb3\x72\xd8\x42\x2e\xe2\xa2\x24\x5b\xbe\x4c\xaa\xe4\xf9\xc6\x63\x4f\x06\xd1\x17\xcd\xc8\x95\x0d\x0e\x65\x14\xff\xad\x8a\x38\x7a\xac\x78\x37\xec\x75\x42\x1c\xe9\x98\x77\xce\x09\xe8\x60\xda\x72\x4f\x4a\xd2\x90\xb6\x7b\x06\xc4\x14\xcc\x42\x46\xa4\xcd\x84\x8e\x8f\xe3\x38\xa2\x49\xf1\x0f\x0e\xde\xd6\x77\xd2\xdd\xde\x75\x5a\xa3\xc5\x79\x9a\x7d\x2a\x6b\x30\xa1\xc5\x47\x01\x6a\x81\x98\x01\x03\x46\xf6\x2a\xbf\x61\xb7\xa8\x50\x68\x97\xf5\x8b\x16\xb3\x8f\x30\xd7\xe3\x34\xfe\xc7\x37\xe8\xdf\xf9\x2c\xca\x17\xca\x37\xb2\xd3\xbd\x7c\x36\xbb\x31\xda\xe0\xe7\x89\x77\x2f\x89\xf2\xfd\x34\x49\xb8\xcf\x26\xb4\xdc\xba\x06\xed\x75\xbc\xdb\xe5\xbd\x7b\xde\x6d\x14\x57\xd9\xe9\xfa\x77\x30\xee\xa5\x40\xca\xe4\xa5\x34\x0f\xc6\xa1\x10\x39\x41\x48\x34\x5e\xbd\x2d\xab\x5b\x7a\x13\xc5\x27\x04\xae\x72\x32\x0e\x16\xad\xd1\xd6\x90\x25\xe1\x23\x49\x6b\xb4\xb5\xc9\xd2\xf4\x71\xa0\x35\xda\x7a\xa8\x52\xb8\xe8\xd4\x1a\x6d\x3d\x56\x49\x58\xb8\x6f\x8d\xb6\xb7\x54\x06\x5b\xe1\xad\xd1\xf6\xb6\x4e\xd0\x42\x7d\x6b\xb4\xad\x2b\xd5\xc7\xc2\xd6\x68\xfb\x7b\x27\x99\x16\xb3\xd6\x68\xfb\xb1\x93\x9e\xd0\xa2\x35\xda\xfe\xc1\x49\x97\x82\x70\x6b\xf4\x70\xe8\x64\xe6\xb3\x59\x6b\xf4\x70\xd3\x4d\x67\xb2\x70\x6b\xf4\x50\x77\x5f\x9e\x71\x5a\xa3\x87\xdf\xa9\x44\xf3\xe0\xdc\x1a\x3d\x7c\xa4\xb2\xa4\xd4\xd2\x1a\x3d\xfc\xbe\x5a\xb7\x77\x75\xd2\xdb\xda\xbe\xd3\xbc\xdd\x69\xde\xfe\x5b\x34\x6f\x41\x1c\x83\x83\x89\x9b\xf9\x71\x45\x0a\x2e\x47\x15\xe2\xd3\x85\xc8\x30\x31\xcf\xcf\xb8\x45\x3f\xd2\x31\x40\x6f\x24\x9c\x0e\x1a\x53\x17\x1d\xc9\xd5\xd3\x78\x15\x35\x2f\xe0\x72\xd7\xaa\x0c\xd2\x24\xc4\x39\x8f\x7d\x64\x82\x48\x56\x24\x32\x95\x77\xd7\xbd\x38\x36\x86\x62\x0a\x46\xe6\xd1\xaa\x07\x37\xf5\x3d\x62\x99\x96\x95\x28\x3d\xcc\x04\x7c\x44\xfe\x95\x5f\xce\xb3\xff\x70\xb2\x63\x2e\xc9\x37\x21\xa7\x87\xd5\x61\xbe\x2d\xa9\x55\xfa\x03\xdf\x55\xbf\xbe\x7c\x81\xf8\x37\xc4\xf6\xfb\xc0\x12\x21\xf5\xb8\xcd\xa4\x50\x88\x2b\xd0\xee\x91\x76\x91\xf2\x9f\x27\x7d\x8e\x66\x14\xef\x70\xe2\xb9\x0d\x15\xcd\x1c\x4f\x4e\xc0\xc0\x45\xd9\x87\x8a\x1b\xd2\xae\x27\x68\xb6\x55\x0d\xeb\x0f\x2b\xbe\x8b\x88\x87\xbb\xd0\x81\x8e\xf0\xf3\x92\x0e\x82\xa7\x1b\x94\x36\x0b\xfa\xe1\x16\xf8\xa2\xd0\x78\x35\xf0\x6c\xbe\xee\xc2\xde\x29\xaa\x30\xee\x89\x5a\x1c\x06\x45\x20\x47\xc0\x7e\xf7\xd9\x3f\x64\x17\xfd\xfe\xf2\x05\x8c\x62\x15\x00\x5c\x25\xe7\x12\x44\x7c\x7d\xf9\xa2\xa3\x6f\x82\xb6\x91\x35\x2d\xef\xc8\x11\xe0\xf1\xf0\xa4\x9f\x33\x86\xa0\x5c\xac\x33\xe8\xb9\x10\x70\x34\x85\xb9\xd3\xf5\xab\x67\xba\x70\x2b\xbb\xc2\xd4\x56\x48\x77\xee\xa5\x6d\xe7\x57\xf5\x3e\xbd\x7b\x3c\x3c\x41\x0f\xaf\xd6\xa1\xfd\x2e\xf9\x0c\x8f\x1d\x82\x24\x49\x0b\x32\x89\x92\x90\xf7\x2b\x4a\xa6\xbc\xa1\x27\xaa\xf9\x71\x9a\xe4\x69\x4c\xfb\xe7\x41\x96\x74\xda\xb8\x04\xf7\x96\xc3\x58\x71\x9c\x4e\xdb\xc8\xf4\x55\xf4\x98\xa1\xc2\xf1\xb8\x44\x05\x1b\xc2\x91\xb9\x60\xee\x3a\xbe\xd5\xd9\xe3\xdd\xea\x99\x04\x61\x1e\xa1\xa0\x46\xe9\xec\x10\xa6\xb8\xc1\x72\xbc\xa0\x63\x26\x01\x78\xd6\x63\x0f\x3c\x32\x9d\x06\xe3\x4f\x2a\x86\x28\xb8\x22\x10\x87\x5d\x79\xdd\xda\x09\xb2\xe9\x12\xde\x82\x1c\xab\x5f\xc8\x1b\x8f\x69\x84\x2e\x6b\x84\xd8\xcf\x95\xc5\xb0\xdf\xb8\x8e\x03\xc1\x26\x7e\xd3\xf4\x63\xa1\xd9\x46\xb2\x8c\x63\x07\xdd\xa9\xa4\x34\xe1\xfd\x4e\x1f\x80\x25\xc4\x04\x45\x59\xe3\x9a\x59\xc0\x64\xff\x34\x32\x95\x86\x48\xfc\xe6\x9c\xbd\x93\xf6\xe0\xa0\xd4\xee\x79\x19\x6b\x4f\xb2\x77\x76\xd8\xea\x74\x7b\xba\x21\x84\xe1\xfa\x99\x0a\x8a\x22\x18\xcf\x3e\xa4\xfb\xd2\x11\x16\x9e\x32\xe9\x1d\x0b\x9f\xb9\xf5\xd4\xf2\x71\xf3\x4f\x67\x38\xb2\x68\x3f\x88\x63\xb5\x9f\x08\xe0\x92\x33\x85\xd3\x4d\x75\xc0\xf0\x9c\x30\xbc\x47\x0c\x20\xd5\xd6\x68\x0b\xa4\x7b\xbe\xea\x5b\xa3\x2d\x90\xdd\x71\xcc\xb6\x6d\x00\xb6\x36\xc2\xd6\xe8\xe1\x36\x13\x99\x1f\xde\x89\xcc\x77\x22\xf3\x5f\x5b\x64\x46\xe1\x5e\xe0\xec\x7d\x5b\xf1\x5e\xfe\x9e\xa7\x49\xb6\x18\x9b\xf2\xe6\xaf\x3c\x51\x5d\x1d\x66\x59\x6a\x8b\xc0\x3c\x4d\x49\xa2\xae\x8a\x82\x0d\xd6\x10\x32\x1d\x19\x13\xd0\xf1\xb1\x54\xd2\x14\x19\xb9\x08\xec\x5d\xe3\x28\x30\x08\x43\xe9\xd3\x91\xb1\x63\x51\x18\xdc\x64\x43\xd7\x44\x82\x65\x11\x18\x84\xa1\xc7\xc6\x96\x88\xf1\xf3\x42\x85\xb6\x6e\x1d\xac\xc1\x38\x31\x2b\x0e\x43\x9f\xcc\xed\x1b\x78\xce\xa3\x82\x4b\x88\xda\x11\x49\xa6\x5d\xd5\x7f\x01\xe3\xed\x9a\x6f\x3f\x37\xbd\x0b\x28\xfc\x1a\xdd\x74\xa7\x40\xdf\x13\x25\x21\x57\x33\x49\xd8\x1e\xaa\x9b\x66\x59\x4f\x48\xa2\xb9\x2b\x13\x73\xf2\xe1\xbf\x84\xb0\xa8\x01\x04\x7e\xb0\x8b\x49\x85\xca\x1e\x81\xd7\xed\x25\xef\xd7\x44\x95\xc7\x00\x73\x82\x8f\x07\xa5\x02\x3b\x2f\x52\x52\x2d\x13\x6b\x64\x7f\x44\xa5\x7d\x47\xf6\xb1\x0b\xac\x8b\x45\xd4\x8f\xf2\x7f\x04\x71\x14\xbe\xa3\xf9\x22\x4d\x72\x2a\x9a\x72\xde\xde\x39\x63\xf0\xb7\xd7\xe1\x6b\xac\x7f\x90\x9c\x79\x6b\xdd\x71\x2a\xbd\x72\xfb\x57\x5a\x39\xf7\xd9\xe4\x0c\x96\xef\xb9\xe0\x1b\xc2\x97\x21\x1a\xef\x8b\x3e\x80\xd7\x08\x9c\xe0\x44\xb1\xd7\x53\xa1\xce\x37\xc4\x2f\x4a\x00\x65\x69\xfd\x24\x1f\x7c\x6b\xb4\x05\x7a\x34\xb1\x22\x5b\xa3\x6d\xb0\x7a\x6b\x14\xe5\xfb\x6e\xc3\xbf\xdb\xf0\xff\xbc\x1b\xbe\xde\xef\x95\x58\x7e\x4b\x2a\xb2\x86\xba\x2a\x76\xe2\xc9\x2c\xb0\x5c\xc8\xfa\x03\xc8\x5c\x55\x9d\x26\xe1\xd0\xbb\x29\xac\x07\x93\x0f\xa2\x04\xf4\x1e\x3a\x84\x20\x30\xa5\x31\x34\x42\x8e\xfb\xf6\x4f\xae\x5e\xc2\x8f\xcc\x60\x9b\xb7\x9f\x29\x73\xb8\x7d\x0d\xf6\x56\x42\x29\xb9\x00\x8c\x7d\xaf\x88\xf4\xe5\x6c\xa6\x7a\x1b\x10\xde\x7e\xfd\x55\x9b\x4f\x3d\x4f\xa3\x9e\x28\x67\xdd\xea\x04\xa7\x91\x47\x0d\x82\xfc\x3e\x13\xcb\xd1\x32\x0f\xf0\xbd\xbb\x4b\xda\xa8\x4f\x6d\x72\xef\x9e\xe1\xc8\x19\x9d\x9b\x79\xb3\x86\xb7\xff\xab\xae\xb5\x0d\x57\x35\xe8\x71\x0d\x4d\x3a\x90\x58\xb2\x5d\x43\x1e\xf7\x18\xed\xd9\x19\xac\x8a\x18\x58\xee\x69\x1a\x68\x4f\x1c\xde\x39\x42\x39\xa8\x42\x23\xd2\xf2\x48\xed\x55\x03\xe9\x51\x05\xf4\x12\xae\xa2\xf8\xd1\xda\xfb\xb2\x29\x08\x43\x49\xc3\xb9\x3e\x86\x63\xda\x90\x69\x57\xaa\xa6\x52\x7a\xe2\xa4\xe2\xaf\xb2\xf2\x64\xaf\x8f\xeb\xd7\x27\x14\xf4\x0a\x71\x95\xd9\xc7\x9a\x2a\xa5\xfd\x51\xfd\xf9\x48\x8b\x99\x54\x37\xeb\x4e\x9a\x5e\x2f\x6a\x55\xa9\x13\x47\xcd\xa1\x11\xa0\x55\xa5\x0d\xe6\x95\x73\x8b\x46\x93\xca\xf9\xcd\xed\xcd\xa8\x5d\x5f\xbd\xa2\x46\x32\xbc\xdb\x98\x5b\xce\x7b\x2d\xb5\xb2\xe0\xac\x42\xdb\xa8\x78\xac\x39\x79\xae\xde\x8a\x77\xac\x74\x3a\xf7\xe2\xb8\x72\xba\x00\x48\x5c\xf4\xac\x4c\x60\x5c\x15\x5a\xd3\xc1\xd5\xa9\xcd\x78\x14\xe8\x2a\xd5\xca\xa8\xad\x8a\xdc\x94\xa3\x1c\xb0\xfd\x93\x93\x3e\xa5\x45\x2e\x8c\x57\xe2\x4b\x12\xd2\x45\x9c\x5e\xd2\x50\x9a\x08\xc2\xf3\xc1\xf1\x2c\x88\x12\xfb\xb9\x1a\xd4\xf6\x22\xcd\x64\x8f\x3c\xbe\x07\xe4\x81\xd5\x47\x92\x72\x5d\x5e\x29\xd5\xe2\x9a\xe1\x22\xf7\x48\x5e\x6e\xe8\x67\x6d\x25\x2d\x62\x83\x07\xd9\x12\x52\x58\x6a\xf2\x85\x80\xcd\x10\x49\xc6\x51\xf3\xbe\x80\x28\xe5\xbb\xf2\x61\x19\xe4\x0f\x06\xe4\x3c\x88\xb8\xba\x1c\x44\xae\x45\xa1\x55\xb0\xf2\xa6\xcc\x9c\x77\xb1\x14\x54\xc0\x68\xdd\x31\xda\x35\x3d\x2f\xaf\x53\x78\x9a\x6c\xb4\x6f\xef\x4a\xd0\xdf\x8d\x8d\x1d\xf3\xd8\x34\x18\x90\xbc\x48\x17\x5c\x57\x1b\x25\x53\x12\x4c\x58\x57\xbe\x1b\xf2\xb9\xca\x49\xa7\x88\xe6\x34\x5d\x16\x5d\xe7\xe8\xc8\x11\xf0\x13\xf9\x6e\xe8\x3d\x2c\xf2\xde\xf7\x59\xed\xbf\x88\xca\x75\x4c\x85\x2e\xf9\x7c\xe5\x39\xd3\xd9\x08\xe4\x0f\xf6\xbc\xe7\x50\x35\x23\xde\xd3\xa6\x3e\xf9\x69\xc7\xc0\x8a\x31\xc1\x7d\x49\xc0\x57\xc6\x98\x11\x36\x38\x09\x3e\x65\x12\xf3\x32\x09\x6d\x0c\xb4\x7d\x87\x4f\x1a\x23\x87\x22\xf8\xcf\x71\x47\x7c\xed\x56\xd9\xf2\xc3\x35\x2b\x7f\x22\x2e\xd6\x0c\xaa\x99\xd2\xe2\x83\x6e\xea\x1d\x27\x35\xcd\x51\x50\x37\x5e\x06\xf9\x0c\x13\x55\x4f\x12\x66\xd7\x7f\x84\x8f\x26\x1d\x01\xe0\xa7\x36\x6f\x21\x6f\x07\x21\x84\x91\xa8\xab\x3f\x36\x17\xa0\xd9\x23\x88\x73\xe4\xef\x8e\xfc\x2b\xf3\xde\xfe\x48\x79\x6f\x2f\xfb\x8b\x26\x1d\x93\xe2\xbe\x7c\x21\xeb\xd0\x62\x65\x31\xa2\x58\xb7\x87\x36\xf1\xdf\x75\x96\x00\xfe\x6b\xb8\x1c\xec\x21\xa5\x21\x0a\x11\xbd\x5d\x39\x33\xf2\x6f\x30\x50\xf7\x7c\x71\x3a\x45\x54\x0b\xc7\x0a\xc9\xc6\xd7\xdb\xdd\x9a\xe6\x89\x21\xaa\x29\x8e\x5a\x32\xd5\x0d\x2a\x1b\x0c\x08\xdf\xac\xa4\xb8\x10\x24\x21\x11\x37\x23\x24\x98\x06\x51\x22\x56\xce\x39\x15\x11\xfe\x6a\xfe\xfc\xb2\xa7\xbd\x01\xd6\xd4\x60\xcb\x3a\xce\xf6\x5f\x33\xa4\x31\x77\xca\x26\x2e\x05\xd9\x96\xc0\x76\xc7\x9c\x8e\xd3\x24\x24\x8c\xe1\xd6\x56\x82\x48\xb7\x9e\x58\x89\xc1\x11\x41\x17\xd6\xb4\xc3\x5e\x2f\x46\xb7\xdc\x21\xec\xbb\x1d\x89\x12\xe2\x44\x8b\x38\x65\x5e\xa4\x19\x0d\x95\x1f\x77\x2e\x81\x80\xc6\x67\x1a\xe4\x24\x98\xb3\x0d\xa9\xef\xe5\xd7\xf6\x5f\x29\xff\xb6\xff\x3c\xee\xe5\x6f\xa3\x8b\xd5\x3d\xbc\x2a\xcd\x2d\xe3\x18\x6e\x09\x1b\x12\x69\x27\x9b\x1e\x28\xd0\x15\x83\x24\xf4\x17\x01\x3b\x66\x5f\x2a\x5f\x1a\x96\x14\x67\x81\xd5\x1c\x1a\xec\x4a\xf1\x81\x01\x4e\x55\xc1\x69\x64\x5c\x2e\xf0\x17\x45\x54\x1e\xdf\x21\x2d\x38\x8d\xc8\x2e\x83\x94\x72\xd6\x7d\xae\x09\xad\x1f\x93\x3e\x21\x25\x24\x40\xa2\xa9\x28\x2e\x6b\x91\x63\x4b\xe8\xb9\x4a\x92\x63\x4a\x2e\xaf\x30\x31\x58\xba\x91\x4d\x69\x53\x10\xc4\xdd\x15\x8b\x6e\x55\x14\xb5\xe5\x60\x43\xb2\x10\xbe\x4e\xa4\xa2\x38\x74\x4a\xfb\x24\x65\x01\xa1\xa4\x65\x7d\xfc\x93\x49\xaa\x2d\x3d\xf1\x50\x68\xa0\x27\x82\xa1\xd4\x77\xfd\x42\x2a\xb6\xe8\xef\x65\x0d\xec\x4f\xfd\xe0\xd2\xb5\x3a\x45\x62\xfa\xeb\x48\x3a\xe8\xa9\xd9\xc7\x1c\x6c\x30\xe0\xb1\x15\xb5\x95\x85\x51\xa9\xb6\x95\xf8\x7c\xb5\xc3\x80\x25\x96\xd6\xcd\xb6\x05\x62\x50\xc5\x70\xc6\xcd\xe0\x2d\x0e\x10\x32\x7e\x94\x10\x47\x63\x0a\x57\x0d\xda\x5e\xc3\x0a\xff\xe7\xb3\x1d\x01\xfb\x8f\x72\x8b\x11\xe2\x58\x8d\xe4\xfd\x45\xba\x30\x1c\xcc\x99\xdd\x8b\x83\xbc\x10\x90\x4e\xd5\xfe\xee\x70\x42\xea\xb0\x82\xe0\xbc\x68\x5d\xbd\x38\x81\x40\xb4\x90\x6e\xf7\x49\xa3\xb0\xa6\x4b\xac\x21\x01\xdc\xe7\x51\x49\x7e\x22\x43\xbb\x36\x31\xd3\x92\xf6\xf7\xe4\x5a\xae\xd7\x02\xc8\xbf\x1b\xa9\x04\x11\x9a\x2c\x66\x29\xd5\x69\xca\xd4\x0e\x0f\x6b\xdd\xec\x72\x7f\x11\x5c\x06\xa7\x31\xf5\x75\xcf\x3d\x0e\x70\xfb\xa9\x9c\x26\xa1\x8e\x48\x95\xa4\xc9\x03\x51\x09\x46\x87\xbd\x4d\x5c\x95\x4d\x3d\xf8\xf6\x63\x9c\xd1\xaf\x82\xed\xc8\xa5\xd2\x83\x11\xa3\x5a\xe5\x04\x81\xed\xdb\xc6\x2e\xaf\x68\xc7\x9c\xc4\xd2\x1b\x41\x7c\xa2\x35\x74\x00\x52\xee\x0b\xc2\xc4\xd6\x12\x84\x94\x9c\x07\xb9\x12\x28\xd7\x4c\x5c\xf1\xa5\x0d\x57\xaf\xe8\x08\xa3\x0d\xb3\xac\xfb\xd7\x59\x90\xcf\x7c\x48\x67\xbd\xa6\x59\x56\x76\x13\x89\xaf\x1c\x7d\xf7\x8a\x55\x12\x0f\x13\x47\xc3\x90\x5f\x7b\x21\xae\xcb\x7a\xe2\x6f\xab\xe4\xd8\x45\x76\xa1\x4c\x89\xf0\x55\x2a\x21\x4e\xa2\x2c\x2f\xca\x05\xc4\x15\x65\xbc\x12\x0d\x88\x4f\xed\xe1\xbb\x7e\x35\xbe\xea\x1c\x5f\x42\xa4\x4d\x3e\xf0\xba\x79\xb6\x1a\x6b\x8a\xf2\x5a\x54\xaf\x32\x74\x3f\x4f\x53\x3a\x79\x0e\x24\x74\x65\x02\xbb\x72\x13\x64\xe7\xdb\x67\xdc\xae\x14\x92\xc4\xa7\x61\x80\x76\x6d\xc1\xcb\xd6\x9a\xd5\x69\x67\x3d\x9b\xba\xa8\xe9\xca\x94\x81\x26\xaa\xfe\xc1\xda\x60\x60\xed\xc0\xc6\x05\x8e\xf6\x78\x8c\xd4\x97\x56\xe5\x1d\xbe\x2f\x0f\x06\x86\x2b\xdd\xd2\xb8\xd3\xe3\x31\x78\xc5\x4d\x79\xa0\xa6\x28\x99\x56\xc8\x66\xa6\x1a\xdb\x1c\x39\x9f\xc4\x2b\x97\x13\x61\x71\xa8\x4a\x14\x22\x9f\x91\xd4\xd5\x54\x22\x9a\x90\x24\xd5\x35\x30\xf6\xb6\x08\xf2\x9c\x86\x3d\x56\x85\x76\x7d\xc7\x20\x72\xb4\xa4\x4d\x5e\xa6\x08\x0f\x66\xc0\x42\xa7\x61\x0e\xe9\xf3\x9d\x6a\xda\xac\x92\x95\x65\x28\x6d\x29\xaf\xb5\x95\xc5\x0c\xb9\x96\x84\x60\x35\x10\x22\x4c\x1a\x15\xa8\x2e\xf5\x64\x81\x53\x3a\x0e\x96\x39\x65\x27\xf1\x30\x4d\x0a\x72\x1e\x24\x60\x93\x94\x2f\xd2\x28\xe6\xb7\xe1\x49\x41\xb3\x49\x30\x56\xbe\xb1\x1b\x9c\xc4\x9b\x9c\xb6\xed\x6d\xaa\x9e\x1f\x12\xc7\xbd\xae\x5a\xd3\x68\x6d\xfe\x4c\x0b\xee\xac\x99\xed\x8f\x3d\x72\x3e\x8b\xc6\x33\x30\x1a\x60\xcb\xbb\x48\xc5\x36\x46\x16\xf1\x32\xaf\xbf\x7a\x15\x7c\xa0\x66\x7e\x35\xf3\xf0\x1b\x32\xd5\x88\xb0\xab\xcb\xa9\xaa\x58\xbd\xfc\x78\x13\xd9\xb1\x5c\x6e\x44\xc6\xca\xd7\x92\x63\xaa\x64\x18\xf3\xa5\x43\x9f\x1b\xa4\x37\x67\xbe\x9e\x53\x8f\xf7\xb8\xdb\xe0\xfa\xbc\x8c\x35\x39\x87\x61\xef\x29\xb8\xe4\x25\x8b\xef\x3c\xec\xee\x7e\xda\x2e\x9c\xe3\xcf\x7d\xbc\x42\x3c\x87\x69\xaf\xd9\x92\x45\xb7\x3b\xca\xfc\xd9\xb4\x95\x68\x8d\xbe\x2f\xb3\x80\x56\x16\x0d\xad\xd1\xd6\xb6\x6b\x12\x2d\x46\xde\x1a\x6d\x6f\x5e\x9d\xf4\xb6\x1e\xdd\x99\x3e\xdd\x99\x3e\xfd\xb5\x4d\x9f\x90\xad\xb3\x30\x81\xbc\x05\x63\xe7\x12\x37\x96\xc2\xb8\x92\xbf\xcb\x3a\x9c\xc8\x3b\xe7\xbd\x6c\x9a\x8f\x4a\x34\x37\x48\xc6\x13\x27\x58\x51\x09\x8e\x7d\x27\xb7\x13\xc6\x3e\x65\xa5\x04\x9b\x38\x01\x9f\xef\xf9\xfa\xf0\xee\xed\x3e\x67\xee\x37\xe9\x00\x8f\xb7\x04\xac\x96\xc2\x03\xc6\x22\x25\xef\xde\xee\x8b\x7b\x02\x7f\x07\xc4\x73\x74\x70\xa2\xa8\x5b\x9e\xa5\x39\xbe\xfd\x72\x1b\xdf\x3f\x7c\xf3\xe6\xf9\xfe\x87\x83\xc3\x37\xe4\xf9\xbb\x77\x87\xef\x46\x64\x5f\xa9\x7f\xc7\xbc\x4a\x7e\xa2\x0f\x29\x69\x6f\x10\x56\x1f\xd9\x68\xf7\xfd\x7d\xd0\x1e\x6f\x9a\x8e\x5d\xbd\xb3\xe7\x4a\x84\x82\xad\x9e\x88\x57\xe6\x6f\x42\x1a\xd2\x8e\x88\x6d\x14\x8c\x86\x09\xcf\xd2\x68\x9e\x07\x53\x4a\x76\xc9\xfa\xba\x78\x69\xc8\xb6\x75\xf1\xbb\xcf\x43\xc6\x3a\x29\x7d\x59\xec\x09\xf1\x26\x8f\x88\x9a\xae\xbf\xbf\x3f\x7c\x03\xb3\x92\xa9\x2e\x79\xc2\xac\x8a\xbe\x39\x6f\xc9\x34\x0e\x44\xd5\xe6\x68\xf5\x6c\x7e\xe0\xd7\xd5\x78\xbc\xf3\xbc\xe9\x94\x7e\x38\x78\xfd\xfc\xf0\xe8\xc3\x88\x88\x4b\x6f\x46\x5c\xac\x93\xf3\x9c\x6c\x90\x36\xfb\x2f\x18\xcf\x18\xc7\x68\x1b\x01\x6d\x84\x1b\xc9\xef\xef\x76\xab\xbb\xdd\xea\xaf\xbd\x5b\xa1\xcd\x0a\x5e\x5d\xfe\x51\xad\x74\x9b\x3f\x66\x6f\xf4\x86\xfe\x16\x9f\xb2\x4b\x9f\x43\x6c\xfd\xab\xc3\x19\x8e\xc8\x94\x1b\xc7\x10\xf1\xc6\x16\xda\xd2\x87\x05\xdb\x08\xf9\x6b\xbf\x83\x5f\x48\x53\x5e\xa4\x48\xc7\xf9\x3c\x74\x05\xa9\x78\x8e\x9c\xa7\x49\xb7\xe6\x09\x3d\xca\x4c\xd2\xe4\x72\x9e\x2e\x55\x8b\x2a\xa1\xe4\xf4\x26\x91\x36\xa5\x12\x57\x34\xe4\xf2\x00\x04\x31\x70\x82\x35\x89\x34\x75\x3c\x7b\x9a\xa6\xf1\x15\x84\x57\x0d\xc1\x05\x39\xdf\x24\x28\x87\x0c\xd1\xec\xc0\xfb\x10\x1a\x1a\x0e\xd3\xe5\x89\x0f\x82\x11\xb0\x45\x29\x6a\x1f\xac\x19\xd3\x84\xbd\x6f\x31\x08\xd3\x71\x14\xaf\xd7\x0e\xc0\x80\x90\xef\x5e\x89\x44\x1e\x51\x21\xea\x8b\x9a\xe0\x7e\x43\xfc\x2e\x31\x77\xf5\x97\xd7\xf6\xca\xa5\x37\xc4\x18\xdb\x9c\x3e\x43\xee\x02\x1c\xbc\x18\x59\xb8\x0e\xb5\x77\x70\x6f\xb4\x20\x6f\x05\xe5\xa8\x43\xd5\x55\x39\x09\xe2\x94\xe8\x3a\x28\xef\x68\x7a\x6d\x3e\x3a\x58\xa1\x9e\xa1\x15\xc2\x9f\x79\xc5\xb8\x70\xd1\x6a\x7a\x58\x69\x44\xd2\x93\xfa\xb5\x86\x93\x47\xd3\x24\x28\x96\x99\x3d\x1c\x9c\x5e\x36\x1e\x0c\x53\x3e\x1e\x05\x55\x35\x20\x70\x60\xd0\xbc\xff\xe2\x85\x83\x24\x6f\xc1\x91\x82\x24\x54\xaa\xa5\x22\x85\xa0\xc4\x93\x28\x09\x62\xbf\xd5\x33\xaf\xc3\x67\x53\x8a\xd7\xb5\x95\x25\xaa\x37\x90\x22\xf3\xe8\x19\xcd\x2e\x8b\x19\xd7\x58\xcf\x4f\x23\x60\x19\x29\x8f\x12\x0d\x7d\x13\x61\x16\x2a\xb1\xe5\x71\x0d\x22\xba\xe3\x78\xb6\x53\x8b\x5b\xfd\x42\x8f\x00\xef\x1c\x88\x68\x77\x1d\xca\x3f\x47\x9d\x67\x11\xa9\xd7\x5c\xb7\x76\x1e\xb7\x9f\xa2\x72\xfe\xb0\x55\xf8\x16\xe4\x7e\x3a\x25\xb5\x77\xba\xae\x4a\x53\xcc\xd3\x07\xd9\xb1\x9b\xb2\x74\x14\xc2\xa2\x92\x9f\x83\xe3\x65\x11\x4c\x5b\x94\x3f\x8e\x20\xc4\x94\x65\x0c\x20\x80\xf0\xfc\x31\xba\xd1\xc9\xc9\x32\x8e\x4b\x5e\xb8\x68\xcd\x22\x71\x2f\xff\x4d\x85\x30\xd4\x57\x16\x98\x11\x32\xad\xd1\x9c\x55\xdc\xf6\x0b\xec\x3b\x6f\x63\x3a\x7c\xfb\xea\x91\x33\xfb\xe6\xbc\x6b\xc7\xd6\x5b\xa9\x36\xe8\x7b\x0d\xc5\x99\x44\x32\x4e\x93\x71\x50\x74\x8c\xd9\xef\x96\xfb\xb1\x29\xe5\x7a\xc2\x89\x4d\x39\xd7\xb3\x77\x5b\x5a\xc6\xe1\x42\x7e\xf7\xe0\xf2\x30\xc1\x15\x84\xe1\x10\x9c\x10\x78\x2d\xa1\x6a\xf6\xde\x3d\xd0\x37\x98\xbd\xa8\xde\xa6\xcb\x9d\xef\x00\x0e\x6e\xd1\xfb\x4e\x90\x4d\xad\xd5\xa5\xc5\xc7\x27\x46\xc9\x11\xfe\x12\x9e\x79\x36\x91\x27\x14\x31\x3e\x71\xff\xa2\xea\xb5\x5f\x6a\xf1\xc9\x24\x9f\x95\x94\x86\xeb\xdb\xea\xee\xb0\x95\xf9\x6b\x1a\x25\x9d\x56\xcb\xad\x5c\x3d\x8a\xe3\xe4\xc6\xf1\x84\xaf\x37\x40\x36\xec\xb0\x65\xde\xed\xe1\x1e\xe1\xab\x9a\x24\x2d\x0e\x8c\xbe\x2a\x14\x7a\xfc\x0d\x69\xe0\x86\x6d\xc3\xab\x85\x6e\xcf\x6a\x05\xb7\xaf\x36\x12\xc4\xb5\xd3\x65\xb1\x58\x16\xaf\xd2\xa9\x66\xd7\xc2\x17\x0f\x5a\x2d\xd2\xf9\x0f\xf7\x33\x83\xc4\x32\x13\x4c\x73\x6b\x18\x93\xed\x06\x8a\xc3\xf0\x5b\x2e\x83\x9f\x66\x34\x5c\x8e\x29\x9a\xab\x60\x3c\xee\x11\xe1\x8a\x12\xf3\x93\x60\x3c\x3e\x16\xc9\x9c\x27\x32\xa4\x88\x6f\x49\xe5\x4f\xcc\x29\xeb\xe7\xb3\x68\x52\x74\xba\x64\xe4\x60\x54\x66\x39\x4a\xab\x60\x3c\x96\x5a\x2a\x6e\xec\xcd\x49\x9b\xc6\xb4\xa0\x72\x1c\xda\x49\x92\x99\xce\xa9\xea\x1a\x2c\x03\xdd\x5f\x89\x77\x25\x62\x69\xb3\xad\x9e\x8b\x71\xa5\x8e\x15\x6e\x4b\x2e\x32\x1a\xae\x16\x7e\x3c\x8e\x1b\x6c\xe9\xe7\x8f\xee\x91\x69\xab\xde\x23\x53\x55\xf1\xcd\x72\x1b\x3b\xb3\x02\x62\x48\x80\x86\xef\x07\x5b\xec\xb0\xdd\x3e\x39\x02\xe5\x1f\xca\xff\x53\x29\x2d\x63\xd3\xff\x06\x8f\x1a\xad\x57\x6d\xde\x17\x8d\x95\xd4\xf8\xb5\x9c\x4d\x31\x50\xf3\xe4\x5a\xc6\x01\xa5\x7d\x21\xb4\x74\x8c\x00\x4e\x0c\xea\xf5\x01\x60\xff\x55\x9a\x28\xbc\xa0\xc7\x8a\xdd\xf3\xb6\x4f\x4a\x07\x60\x58\x4d\x78\xef\x84\x0d\x5c\x22\x8f\x58\x55\x57\xc2\x75\x7e\xb2\xae\xe9\x1a\xeb\x71\x13\x05\xfc\x4d\x7d\x5d\x0e\xfc\xba\xc9\xd7\x9c\x06\x3d\xfa\xbf\xea\x40\x22\x38\x86\xc8\xda\x60\x40\x3e\x1c\x3e\x3b\x1c\x91\x8c\x72\x83\xac\x1e\xc9\x53\x61\x3a\xa3\xae\xb8\xb4\x2d\x4e\xc0\x35\x5d\x7d\x56\x2e\x2a\xda\x39\x49\xe8\x98\xe6\x79\x90\x5d\xb2\xc5\x02\x11\xb0\x73\x46\x6e\x6d\xf0\x57\x0c\xde\xa2\xc9\x79\x9a\x7d\xe2\x52\xde\x7c\x19\x17\xd1\x22\x46\x91\x1c\xcc\xd8\x29\x7e\xf7\x46\x83\xfb\xc4\x6b\xcb\xfd\x9d\x34\xe5\xe6\x75\x98\x66\x0c\xb2\x79\xc3\x86\x54\x37\x46\x43\xbe\x71\x98\x27\x13\x55\xaa\x2f\x71\xe4\x73\x60\xb3\xce\x3a\x77\xec\xc2\x9e\xf8\xce\x0f\x65\xb0\x16\x3b\x25\x8e\x7d\xa3\xd9\x4f\xe1\xcf\xc9\x57\x53\x8d\x19\xa4\xb7\x9e\xd2\x23\x94\xae\x5f\x10\xbc\x3d\x26\x07\xc0\x73\xe4\xe6\x39\x3e\x6c\xf0\x1c\xc5\xf4\x84\x49\x8f\xd9\x45\x8f\xe5\xa7\x28\x96\xd3\xc2\x8a\x14\xe3\xf3\x71\x55\x79\x10\xab\x9e\xee\x88\x56\x8c\x57\xc3\x78\x86\x5c\x46\x2f\x44\x07\x39\xb9\x5c\x79\xd8\xaa\xe0\x2d\x0c\x9c\x20\xbb\x51\x7a\xd1\x37\xd8\x91\xfe\xd8\x21\x12\x40\x72\x21\xf8\x7f\x47\xa6\x2a\x96\xc3\x7f\xa8\x74\xc4\x68\xe4\x4f\x53\x8e\xa4\x17\xe2\x79\xb7\xcb\xcd\x39\x1a\xb4\x67\xa2\x12\xfe\x5c\xc2\x91\x5b\xa3\x6d\xf0\x60\x84\x9d\x86\x33\xc6\xfc\xc3\xdd\xcd\xe8\xdd\xcd\xe8\x5f\xfb\x66\x54\x5c\x8b\x8a\x27\xbf\xff\x15\xf1\xf5\x6e\xd5\x63\x38\x1c\x02\xee\x93\xfd\x34\x39\xa3\x8c\x15\x05\x22\xe4\x31\x9c\x83\xe1\x2c\x00\x71\x8b\x65\x20\x17\x46\xc0\x41\x9c\xa7\x24\x88\xe3\xf4\x3c\xe7\xe1\xd9\x41\x51\x97\xf7\xd7\x58\x45\x52\xf0\x7f\x1d\x5d\xd0\xf0\x8a\x67\xad\xb9\xf7\x1a\x6b\xe2\x46\xb5\x48\xed\x20\xc7\x42\x65\xa9\x0e\x9c\x1d\x53\x25\x4a\xbe\x7c\x91\x01\xd2\x75\x46\x5b\xe9\x50\xdb\x5d\x5b\x19\xc0\xcf\x72\x42\x44\xe2\x8a\x59\xde\x87\x8e\xd4\x2f\x1a\x0d\x71\x3d\xc4\xe1\x04\x54\xcd\x5d\xa8\x7d\xe8\xd4\x09\x90\x82\xef\xe3\x17\xad\xc6\x9d\x91\x0c\xa2\xa4\xda\x81\x23\x17\x13\x35\x19\xa7\x95\x97\x3f\xb6\x25\x6c\xaa\xf4\xfb\xe2\xb0\xd5\x63\x93\x70\x46\xb3\x68\x02\x7e\x3d\x32\x3a\x0e\x18\xc7\x41\x81\x6a\xee\xdd\x23\x71\xf0\xdb\x25\x89\xd3\x20\x24\xe1\x65\x12\xcc\xa3\x31\x49\x13\x9a\x43\x6b\x62\x42\x74\x43\x22\x98\x75\xaa\xf4\x04\x00\x25\xed\xeb\x65\xe3\x0e\x14\x9b\xad\x29\x2d\x0e\xd5\x21\xd9\xe3\xc1\x99\x4d\x8c\x16\x58\xeb\xdc\x03\x60\x65\x82\x98\x12\x79\x4c\x2e\xbf\xf5\x30\x34\xfd\xa5\x57\x2f\x3c\x3b\x3f\x8f\x20\x5e\x09\xea\x15\x01\x1d\x44\x4e\xf9\x09\x7a\xe4\xbc\xac\xe2\xc2\xfb\x32\xa3\x42\xbd\xd8\x83\x0b\xbc\x31\x5f\x1d\xfc\x70\x3c\xa3\x17\x3e\xb5\x81\xd6\x9a\x5a\x09\x96\x27\xca\x06\x45\x0c\xcd\xa7\x08\xab\x5d\xaa\x94\xb7\x14\xfe\x32\x08\xf7\x13\x11\x9e\x9c\x55\x25\x16\x59\x97\x8c\xe4\x7a\x13\x60\xae\xac\xe4\xbb\x26\xf0\x3c\xaf\x83\x6e\x8e\xac\x6e\xf7\x1c\x38\xb6\x04\x34\x14\xfb\x72\x61\x8a\x14\xd7\xe3\xe6\x07\x32\x2a\xb3\x04\x0a\x70\x4c\x66\xbb\x35\xb8\xbf\x1a\xad\x74\xad\xd5\x57\xe5\xba\xbe\xde\x5d\xa7\x46\x51\xca\xd4\x4f\xa1\x83\x0e\xa7\xc0\x7c\xc6\x28\xd0\x83\x70\x8b\xd4\xa5\xaa\x66\x2f\x0c\xf9\xb3\x08\xa5\x44\x0b\x92\x90\xe4\xb4\xc8\xc9\x72\x01\x19\xe2\x34\x02\x2c\x23\x2a\x68\xc6\xf6\x8e\xf4\x4c\x08\x5b\xc2\x8d\x69\x7f\x6d\x0d\x3d\x8d\x78\x95\x4e\xf3\xbd\xe2\x7d\x11\x64\xc5\x9a\xad\x69\xcc\x69\x3c\x51\x89\x13\xf7\xfd\xb2\x60\xe1\x66\x2d\x46\x9c\x30\x1a\x4f\x1c\x1f\x3e\xf2\x91\xdd\x94\x16\x5c\x9f\xc5\x0a\x5b\x2f\xed\x40\xbf\xa0\x87\x99\x43\xf7\x88\x3c\x79\x5a\x3c\x83\xb5\xd2\xf7\x31\x0e\xc8\x98\xd2\xa2\x63\xbd\xf9\x11\x96\x8c\xce\x29\x67\x30\x20\x61\x9a\xb4\xc5\x2b\x51\xd6\x47\x81\x36\x30\x9b\x84\x8b\x6e\x99\x28\xcd\x8e\xc0\x13\x46\xbf\xdf\x27\xbf\x2e\xb9\x23\x60\xd6\x26\xe3\xbd\xce\x79\xb9\xe4\x61\x64\xc5\xa3\xc8\x2b\xfb\x05\xac\xb5\xd2\xd5\x30\xfc\x67\x4c\x9e\xe9\x3d\x98\x72\x43\xce\xba\x67\x9a\xfc\xf1\x8e\x69\xf6\x69\xf4\xaf\xde\x0f\xeb\xd7\x23\xdd\x45\x1a\xc7\x9c\x7c\xfc\x64\x2b\x68\x53\x83\xd9\x74\xa9\x54\x22\xa0\xb6\x4d\x5e\x2b\x33\x5c\x83\x58\xd2\x12\x72\x11\x33\x9a\x3a\x73\x2a\x8d\x2c\x18\xe9\xc9\xb1\xfa\x26\xc1\xf7\x6c\xca\x47\x13\x69\xe3\x93\x7c\x53\xea\xb8\x1e\x65\x68\x33\x65\x18\x9a\x56\x5e\x3f\xb1\x12\x74\x25\x23\x59\xc8\x25\x9d\x1b\xa1\xe7\x66\x44\x5a\xaa\x0f\x80\x3e\xd9\xce\xa8\x19\xe3\x79\x9b\xc6\x31\xe3\x33\xba\x27\x9c\x06\x47\xbc\x08\x3b\xa7\xd1\x39\x4d\x0a\x38\x72\xf6\x19\xc5\xc1\xd0\xf4\x5e\xb2\x10\x86\xf6\xc7\x1c\x53\x40\x8e\x07\xe1\x49\x4f\x5e\x51\x19\xc9\x3d\x4d\x8c\x22\x07\xbb\x31\xe2\x0a\x62\xa0\x5f\xb6\x59\xcb\xa8\x85\x0e\x89\x5b\x32\x59\x8f\x38\xe1\x3d\xe4\x72\xf3\xdc\x0e\xf4\xc4\x69\x6a\x3f\xa3\x30\x26\xb0\xd7\xde\xf7\x3c\x74\x04\x66\xc7\x35\xd8\xe8\xc2\xd5\xc0\x07\xd2\xf0\xad\xa2\x2a\x2b\xd5\x75\x95\x2a\x7b\xfc\x4a\x35\xb3\x33\xc8\x96\x80\x94\x7a\x8c\x2f\xb5\xc6\xd4\xc2\xa6\x16\x83\x2d\xd1\x17\x41\x3b\x68\x30\x13\x10\xa4\x9c\x79\xf7\xc9\x98\x5a\x21\xc2\xb2\x46\x65\x88\x2d\x77\xbf\x2c\x5f\xb3\x3d\x27\x0b\x5f\x3b\xa9\xdf\xa5\xfd\xee\x27\xf4\x5c\xdc\x3a\x61\x1c\x60\x5f\x61\x9c\x49\x46\xa1\xe1\x1a\xcf\xcf\x1c\x6b\x96\x7d\x67\x7c\xea\x11\x73\xc7\xa7\xb5\x7c\x90\x08\x8e\x2c\xce\x85\x15\xd4\x6b\x39\x24\x75\xd9\x4b\x45\x59\x7f\x37\xaa\xf5\xce\xc6\xd2\x66\x44\x10\xba\x7e\x00\xb1\xab\x86\x8c\xc2\x25\x03\x3b\x73\x2c\x68\x12\x82\x81\x9b\x9a\xe4\x20\x07\x45\x4b\x92\x33\x0a\x55\xbe\x60\x74\x45\xe9\x04\x80\x59\x21\x26\xf5\x74\xb9\x72\x45\xb5\xbe\x4c\x82\x3c\x8f\xa6\x09\x0d\xfb\x6e\x1f\x6d\x8a\xf2\xf1\x64\xdf\xec\x28\x19\x6b\x7c\x5a\x33\x41\xde\x66\xb0\xc9\x18\x1a\x89\xb6\x27\x26\x31\x96\x0e\x83\x38\xa3\x41\x78\xa9\xdf\xab\x6b\x41\x31\xbf\x39\xa5\x99\x82\xac\x94\x5e\xeb\xc6\x15\x4d\x3a\x56\x6b\xca\x07\xdc\xd0\xf5\xc8\xa5\x57\x26\xe7\xe2\x3e\xb7\x90\x4c\x8a\x2e\x52\x31\xb6\x68\x3e\xa7\x61\x14\x14\x34\xbe\xb4\x9b\x15\xe4\x3e\x6e\x4a\xdb\xa6\x74\x02\xd5\x77\x4a\x3c\x4d\xf8\xbc\x56\x61\x4d\x36\x67\xf9\x6c\xfb\xe1\x83\x41\x77\xb9\xe7\x4e\x94\x0e\x7b\x33\x37\x79\x1b\x37\xec\x43\xfd\x90\xea\x18\x83\x39\xe2\xd1\x58\xf3\x24\xae\x4b\xdd\x81\x20\x5c\xa3\x3b\xe1\xab\xa6\x03\xc1\xfb\x6e\xfd\x78\x1c\xc9\x21\x5d\x48\xc1\xc1\x1c\x48\x0d\x7f\x87\xa7\xe5\xf3\xf4\x4c\xaa\x34\x49\x90\x5f\x26\x63\x75\xf8\xf1\x09\x46\x3e\xbe\xbd\x4c\xe0\xed\xb4\x81\x00\x24\x63\x58\xd8\x72\x78\x17\x36\x84\x5f\xa5\x66\x43\xf0\x77\x30\x3a\xb5\x42\xb6\xfb\x9c\x27\x38\x32\x85\xd7\xe4\x44\x95\xb4\x85\x72\x6b\x47\x2d\xb1\xa3\x1c\x0c\xc8\xc1\x44\x73\xc6\x28\x57\xef\xfa\x2e\xa9\x70\xbf\x42\xa2\x82\x68\x2f\x5d\xba\xdc\xf9\x8c\x82\x31\x86\x18\x7d\x97\x70\xa6\x9a\x93\xa8\x30\xd9\xaa\x77\xa3\x76\x88\x5d\x2d\x33\xdf\xee\xe1\x43\xbf\xa8\xd1\x9e\x50\xbc\x1f\x43\x84\x14\x0f\x7f\xfb\x8a\xfe\x79\x2c\x79\x3c\xa3\xb6\xf5\x5e\x9c\x4e\xcb\xda\x25\x16\x63\xaa\x38\x5b\x40\x2d\x23\xb6\x27\x94\xb8\xe3\xf3\x07\x2c\x31\x41\x9c\x03\x80\x3d\xb0\xe6\x74\xe4\xb8\x99\x12\x82\xf8\xc1\x33\x9e\x30\x12\x34\xd6\xe9\xf6\xf9\x8e\x3c\x0e\xa4\xc3\x42\x70\xab\x42\x43\xc2\x56\xf7\x2c\x4b\x93\x74\x99\x2b\xef\x85\xc2\x30\x80\xed\xf6\xb6\x27\x22\x5e\x8d\x10\x76\xdb\x5e\xf3\x5a\x70\x2a\x91\x6a\x2b\xbd\x26\x04\xe4\xda\xd0\xb1\x1a\xea\xe7\xf0\x06\xf3\x76\x55\xc3\x8f\x9d\x2b\x52\x8e\x5b\x27\xf6\x5b\xc5\x05\xe9\xd5\x49\x6f\x7b\xd8\xe4\x0a\xb4\xbd\xcc\xb9\x5e\x7c\x5c\xb4\xd7\xee\x2e\x44\xef\x2e\x44\xff\xc4\x17\xa2\xfa\xa9\x28\x52\x59\x5f\xe7\xbd\xa8\x00\x5e\xe1\x26\xd3\x17\xfb\xad\xf1\x13\xd3\x64\x12\x4d\xbd\x70\x3c\x4b\x02\x1e\x9c\x06\x56\x4c\x97\xe8\x34\x48\x3c\x71\x5a\x40\x9b\xcc\x03\x4d\x71\x1b\x69\x7e\x99\x79\x1a\x4d\x85\x07\x03\xcb\x8a\x91\x03\x3d\x8d\xa6\x96\x52\x1f\x5b\x33\x72\x8d\xf3\x17\x0e\xf1\x45\xc1\x5e\x99\x4e\xab\x74\x3a\xb6\xc4\x05\x3d\x63\x49\x1b\x86\x54\xc4\x7b\xe7\x7d\x86\x56\xa4\xaa\xac\x04\xdb\x51\x4a\xa0\x28\x7f\x9b\x51\x71\x0d\x8a\x6e\x27\x8c\xba\x4f\x75\xba\xd5\xc0\xb1\x72\x77\xdf\x16\x27\xcf\x76\xaf\x4d\x83\x2c\x8e\x78\xe2\x38\x9d\xcf\xa3\xa2\xa0\x61\xfb\x44\x5d\x91\x1a\xb5\xfd\xb4\x4b\x86\xa8\x33\xc9\x62\x59\x3c\xa3\x93\x60\x19\x7b\xaf\x4a\xea\x7a\xc5\xf6\xe0\x53\x3c\x08\xfc\x50\xc6\x1b\xaf\x85\x11\x49\x3f\x44\x2d\x7a\xbc\x4d\x95\xdf\xdc\xe0\x2e\x58\xa3\xf8\x3d\xba\x6f\xbf\xe1\xe2\x22\x09\xab\xa5\x64\x56\x8d\x46\x3d\x15\xa2\x6c\x0f\x1e\x24\x35\xbd\xa4\x17\x55\x23\x7f\xbe\x48\xc7\xb3\xaa\x91\x53\x0d\xc0\xfb\x00\x22\xa6\x4e\x74\xdf\x37\xd9\x99\x2d\x4e\x75\x2d\x8b\x1a\x65\x32\xeb\x3b\xeb\xb9\xa7\xdf\xb8\x6d\xc3\x9c\x99\x77\x35\x47\xe6\x9b\xe9\x84\x04\x86\x13\xc3\x20\x09\xe5\x9d\x6e\x0e\x77\x3a\xdc\x82\x81\x71\x88\x97\xcf\xff\x69\x31\x06\xa8\x83\x49\xf0\x5e\x96\x20\x6f\x1d\x0c\x67\xc0\x8e\x89\xbe\xbc\xcc\x97\xf7\x12\x6e\x9d\xde\x10\xe5\x9f\x8d\x6b\x6e\xb8\xa8\x44\x97\xc5\xf0\xf9\xe5\x8b\x45\xfb\x7b\x63\x08\x10\x81\x5c\xb4\x61\x78\x8f\x6f\x30\x59\x2d\xf4\x49\x38\xcc\xf2\x5f\x92\x9a\x12\x1b\xae\xba\x48\x45\x64\xeb\xa8\x20\xf3\x68\x3a\xe3\x22\xae\x72\xb3\x2c\xd4\x69\x4e\xcb\x45\x5a\xdb\x6e\x91\x9a\xad\x1e\xb7\xa7\x41\xfe\x36\x8b\xc6\xb4\xdd\x23\xec\x37\xfb\x0f\xa6\x8f\xfd\x48\xd2\x64\x4c\x7d\xef\x28\x3f\xd1\xcb\x8a\x97\x94\x9f\xe8\x65\xd3\xb7\x94\x50\x93\x83\x43\x5e\xc3\x2e\xb2\xfc\x78\x46\xc7\xd1\x3c\x88\x3b\x18\xc0\x7d\xcb\x66\x5e\xf7\x7e\x6d\x22\x46\x4e\x3f\x6f\x9b\x96\x7d\x55\xdf\x3e\x49\x5f\x97\x6a\xbf\x29\xbd\xce\x83\x8b\x17\x94\xbe\xa5\xd9\xcf\x9c\x58\xe7\xc1\xc5\xdb\x2c\x4a\xb3\xa8\xb8\x34\xd2\xff\xeb\xe8\x5a\x88\x65\x0e\x61\xc3\x0d\xb0\x8c\x66\x24\xa8\xda\x2b\xac\x35\xa6\xe7\x0b\x53\x40\x13\xe9\x6b\x86\x54\x56\x4b\xc1\xc5\x45\xf7\xb3\xd2\x4d\x5e\xf4\xf1\xf6\xbe\x2e\xf5\x03\x5a\x27\x67\x02\x28\x1f\x1d\xa9\xc4\x9f\x09\xa0\x5e\xa1\xb0\x74\x84\x0b\x78\xef\xe6\xaf\xde\x81\xf2\xb6\x61\x43\x49\xf5\xe3\x45\x1f\x48\xca\x5f\x08\xb2\x34\xe4\x34\xc8\xfd\x70\xd3\x20\x37\xa0\x80\x7c\x11\xa8\x16\x56\x51\xbe\x31\x54\xbc\x36\x4c\x42\x35\x14\x9c\x16\x60\x49\x0b\x18\xc6\xf0\x35\xaa\xda\x72\xd6\x5d\x5d\x9b\x6e\x81\xf2\xb6\x1d\x58\xa3\x0f\xc5\x45\x5f\x9a\x1f\x7a\x2b\xc0\x8f\x9d\xa5\x2e\xe4\x62\xe5\xa5\x23\xe3\x04\x5d\x67\x09\x89\x90\x45\x95\x2b\x49\x45\xd0\x5a\x65\x39\xd9\x15\x5b\x8e\x73\x70\xe8\x23\x1d\xea\xa8\x66\x7d\xf9\xa0\x5c\x1a\xf5\x40\x69\xf2\x93\x99\x0d\x96\x5b\x29\x68\x79\x93\x25\x0b\x4f\x45\xe4\x59\xce\x97\x71\x50\x44\x67\xf4\xe7\x20\x3f\xca\xe1\x65\x61\x59\x55\x0e\xac\x55\xd7\xb4\xb6\x86\xa9\x2a\x27\x07\x6f\x1a\x8b\x48\xb8\x38\x9d\xda\xb6\x97\x3a\x03\xc5\x13\x72\x94\x83\xa0\xe1\xf3\xaa\x06\x3d\xef\x93\x19\x6c\x9d\x1e\x50\xb4\xd4\x68\x01\xc0\xec\x36\x27\x79\x38\x45\x55\x52\x39\x54\xd8\x80\xc6\xcd\x9a\xb0\x85\x11\xd4\xa0\x4c\x8c\x06\x03\xa2\x9c\x33\x81\x97\x42\xa1\x7f\xc0\x07\xc5\xfe\x69\x90\xd3\x06\x7c\xc9\x07\xec\x63\x29\x1e\x38\x83\x1f\xf1\xfc\x69\x90\xbf\x8a\xe6\x51\xe1\xa1\x1d\x13\x40\x94\x55\x89\x25\x04\x67\xe4\x1b\x65\xf2\xe8\x37\xdf\x6e\xa3\x33\x0d\xe8\x22\x9a\xd3\xbc\x08\xe6\x8b\xd2\x22\x0a\x42\x2f\x68\x9e\x91\x94\xb1\x0c\x23\xbb\xac\x5a\xa5\x55\x41\x9d\x09\xa3\xc9\x24\x1a\x2f\x63\x78\xd9\x53\x86\x69\x0d\x64\x0e\x24\x2d\x82\xf8\x59\x93\x0a\x2c\x48\x2c\xb5\x9a\x8b\x55\x80\x6b\xfe\x62\x2e\x59\x37\xdb\x95\xf5\xa2\x82\xce\xbb\xf6\x9b\x3e\xc7\xb0\x12\xa0\xdc\x2b\x6c\x63\x61\xfb\xa4\x26\x5e\xb0\x6e\x85\x9f\x72\x1d\xcd\xd5\x4e\xa3\xe5\xfd\x3e\x9a\x26\x34\x23\x71\x94\xdb\xaf\x8f\x57\x5a\xd4\xbc\x9a\xdc\xbf\xb6\x89\xbb\xb8\x05\x7c\xf9\x1a\x17\x00\x5a\xfb\xe1\x99\x2b\x09\x23\x67\x09\x27\xd6\xcc\x4d\xfd\xac\x08\x6c\x72\x4d\xe7\x21\xf4\x5c\x06\x55\x40\xd3\xc0\xa7\x00\x69\x52\x70\x1f\x1a\x31\xd9\x38\x9d\x7a\x11\x8f\x39\xbb\x0f\xed\x71\x3a\xd5\x5a\x50\x17\xe9\x50\xaf\x81\x77\x5c\x21\x46\x37\xba\x7d\x8a\x26\xec\xcb\xd8\xd5\x15\x3e\xac\x0c\xcf\x42\xb7\x8b\xee\xe0\x3a\x9d\x6d\xdb\xa8\xb8\xc1\xfe\xef\xad\xc4\x68\x22\x4e\xa7\x9e\xaa\x65\x6a\x49\x95\xaa\x90\x79\xc4\x82\x1b\xb5\x7a\xb5\xc1\xf9\x2c\xca\xd9\xae\xb8\x48\xf3\xe2\x1a\x7a\x83\xb7\x69\x5e\x2d\x16\xba\x91\xb0\x2a\x77\x4f\xb7\x52\x3c\xd1\xac\x93\x78\xeb\x64\xdf\xfd\x45\x70\x09\xcf\x5b\x76\x0d\x1d\x20\xce\x12\x48\x86\xa4\xa2\x88\xbd\x87\x56\x99\x89\x61\xcf\xd3\xec\xd3\x87\xf4\x6d\x96\x9e\xd1\xf2\x32\x08\x08\x97\x5d\x08\x91\xbf\xbc\xa0\x84\x40\x01\x1e\x26\x38\xfe\x97\x61\xd0\xce\x79\x06\xef\x24\xf7\x76\x83\x19\x3b\x4a\x27\xbb\xc6\xd7\x13\x72\x8c\x3e\x4f\xc8\x48\x59\x93\x5c\xe9\x56\xf9\x55\x08\xbf\x15\x89\xe3\xf4\x1c\x5e\xf7\x48\xe5\x4e\x55\xf5\xd5\xaf\x51\x78\x04\x4b\x46\x4c\x24\x4d\xe2\x4b\x1e\x96\xa3\x30\x1e\xc9\xc8\x87\x2a\xfc\x41\x8a\xef\x7d\x95\x7c\xad\x42\x46\xf6\xdb\x29\xfc\x4e\xc5\xd6\x2f\xb0\x3e\x36\xe2\x5d\xea\x5a\x0e\xe8\x5f\x18\x0b\x7b\xb9\x59\x1d\xa5\x37\xd9\x38\xaa\x09\x5b\xd0\x35\xe0\x97\x5e\x2c\xa2\xec\xd2\xb3\xe2\x51\x2e\x26\xb7\x9c\x7b\xf1\xf1\x42\xb3\xbc\xb2\x25\x60\x81\x7a\x16\x00\x50\xb6\x4f\xa0\xb3\x20\xba\x3b\xbe\x55\xf9\x2e\x38\x97\x24\x23\x52\xbc\x60\xa8\xfa\xbd\x7c\x1c\x45\xf6\xf2\x95\x65\xf0\x36\xfa\xf7\x5c\x20\x4e\xc1\xe9\x38\x38\x7a\x55\xe8\x06\xc0\xad\x35\xc4\xa2\xf3\x31\x87\xc1\x60\x95\x15\x01\x6b\x13\xaf\xc6\xd2\xc5\xa8\x97\xdb\x0d\x56\x92\x75\xc7\xc1\x51\xd4\x8c\xfe\x15\x53\xb5\xd5\x92\xbe\xa0\x35\xf8\x8e\x4a\x24\xf5\xf3\xe5\x29\x7f\xf0\xd7\x19\xf6\xb6\xf9\xaa\x6c\x5d\x84\xe3\x96\xe1\xbc\x49\x79\x87\x6a\x0d\x2f\x5a\x64\x83\xb8\x85\xb7\x8d\x23\x06\xf4\x8a\x5f\xd7\x26\xf4\x1c\x6e\x6e\x3b\x66\xcc\x74\xb8\xe0\x3a\x0d\x92\x7e\x94\xff\x23\x88\xa3\xb0\x03\x31\x4d\x44\xca\xb3\x28\xa3\xe3\xa2\xe3\xbb\xdd\x12\xae\xe3\x00\x50\xd4\xd8\xe9\x3a\x57\x67\x58\x70\xd2\xa1\xa6\x64\x0f\x3c\xd5\x1a\xde\x09\x3d\x15\x35\xa8\x42\xf4\xcc\xac\x89\x2b\x80\x6c\x5b\x21\xe1\x3f\x5e\xc2\xb6\x65\xec\x77\xcd\x49\xde\x5f\x26\xe3\x28\xf1\x8b\x43\xc2\x61\x3b\x9a\xcb\x75\x33\x89\xb8\xfe\xab\x0c\x21\x1c\xbc\x5d\x81\xb1\x69\x94\x4c\x41\xd8\xf5\x2a\x10\x5c\x30\xd3\x67\x98\x70\xdf\x55\x53\x01\x86\x32\xcb\xcf\xa2\xe9\x8c\xe6\x75\xe5\x31\x14\xa2\x1d\x91\xfb\x29\x49\xcf\x93\xf7\x45\x50\x50\x9f\xff\x48\x94\x5b\xde\x00\xae\x62\xc7\xae\x61\xb1\x8c\x63\x1a\xd6\x55\x81\xa1\x4a\x74\x1a\xda\x8d\x58\x49\xa4\x88\xba\x6b\xf3\x51\x2d\x44\x4f\xd7\x53\x51\x41\x4d\x49\xdf\xc5\xef\xa8\x3c\x0b\x95\x34\xee\x34\x47\x9e\x34\x04\xeb\x3b\x3b\x8e\xca\xb3\x50\x49\x9b\xcd\x8d\xfc\xc9\xa8\x84\xb1\x29\x8f\x3c\x69\x1c\xb6\xcc\x40\x63\x54\x9a\x83\xcb\xf9\x07\x54\x9e\x57\x52\xd6\xd6\x97\x7a\xaa\xb0\x41\x8c\xde\x1b\x47\xe1\x91\x37\xd5\x81\xb7\x0f\xba\xa3\xaa\x4c\x5c\x1a\x1f\xd7\x46\x9e\x34\x0c\x6b\x4d\x82\x27\x11\x43\xdb\xdc\x6f\x54\x92\xce\xb9\xa6\x61\x13\xc8\xaf\x0f\x5b\xa3\xcd\xc7\x65\x9e\xae\xd8\xd6\xd1\x1a\x6d\x6f\x5f\x9d\xf4\xb6\x37\xef\xbc\xa4\xdc\x19\x05\xfe\xd7\x18\x05\x0a\x4a\xbf\x8d\x70\x47\xab\xc5\x86\x68\x68\x09\xc8\xa3\x31\x99\x26\x7e\x3c\xed\x2b\x04\x99\x68\x1e\x16\x22\x88\xe3\x81\x15\x38\x15\xde\x7b\xdb\x61\x97\xdc\x60\x11\xf2\xd1\x82\x1b\x61\xae\x22\x48\x84\x2f\xc4\xdc\x47\xbe\x35\x8a\x20\x06\x38\xb6\xf2\xea\x01\x06\x74\xa5\x62\x6f\xc1\xb5\xf2\xa4\x9b\x55\x0b\x71\x19\x03\x38\xaf\x42\x9d\xf2\x1b\xc3\xc8\xd8\xcb\x02\x44\x7c\x62\x88\x5b\x09\x70\xc1\xf6\x07\x7b\x32\x0c\x57\xa8\x60\xf5\xa1\x9f\x08\xe2\x23\x53\x36\x35\xce\x4b\xd7\x88\x28\x2e\xcf\x16\x3a\xfc\x22\xf8\x19\x01\x5e\xcf\x1f\xb5\x65\xd3\x9c\x47\xb1\x58\x17\x42\x63\xb3\x0e\x63\x21\xb0\xb2\xd3\xb8\x7b\x3f\x39\xa4\x24\x73\x70\x30\x49\xf1\x7a\xd6\x1d\x9c\x7f\x6c\xb6\x6f\x8c\x0a\xf1\xb4\xa3\xf1\xd0\x10\x11\x55\x21\x23\x71\x90\x6b\x5f\x5c\xb4\x28\x27\xe3\x34\xcb\x5c\x97\xa5\x70\xf2\x0a\x0a\xba\x97\x4d\x73\x5f\x14\x49\x1d\xc6\xfe\x3e\xf9\x1b\x9c\xdc\x72\xf2\x19\xce\x6d\x57\xac\xbd\xa8\x10\x6f\x86\x0c\xaf\xa6\x9e\xa9\xc2\xed\x94\xce\x91\x3e\xbd\x73\x28\x40\x91\x63\xe9\x13\x68\xc4\x0f\x06\xf2\x71\x18\x68\xba\x0c\x77\x41\xb0\x79\x82\x93\x4a\x1d\x1d\x8e\x6d\xb5\x01\x3c\x2d\xcd\x82\x4b\xf9\x50\x52\xcc\xdd\x7a\xc7\x09\x2e\x1a\x74\x95\xcb\x7b\x76\x1e\x77\x2e\x80\xac\x3b\x0e\x01\xce\xdd\x57\x57\xc2\xeb\x2b\x2f\xa3\x8c\x55\xc0\x7a\xa5\x0d\x3a\x02\x89\x1d\x49\x88\xeb\xbb\xbb\x65\x84\x6c\xbe\x8c\x63\x67\x6e\x11\xdf\xaf\x22\x0a\x5f\xc7\x71\x58\x51\xe5\xe2\x59\x6a\x9b\xc0\x02\x0d\x93\x8a\x11\xa4\x24\x7d\xcb\xc1\x3c\xe4\xe5\x6c\x1a\xda\xb5\x7b\x89\xbb\xe7\x20\x56\xad\x6a\x83\xbc\x4a\xca\x53\xed\x57\x92\x9d\x11\xd6\x76\x75\x86\xb1\x2a\xbf\x30\xa3\xd1\x96\x84\xbb\xbd\xd2\xdc\x1c\x2f\x9f\x8e\x27\xf6\x6c\x91\xfa\x23\x4b\x18\xb1\x69\x77\x49\x49\xd4\x08\x5f\xf0\x01\xf1\x2e\x0a\x0d\xd7\x08\x7a\x5b\x61\xd9\x56\x12\x15\x49\xa2\xfe\x7a\xd1\x5f\xbc\xc5\x2b\xe7\xfd\x5a\x31\x60\x84\xfb\xfa\x61\x8f\x3c\x96\x5a\xa8\x8a\x26\x96\xc9\x22\x18\x7f\xe2\x77\x8d\xa6\x8d\x27\x24\x19\x3a\x29\x33\x49\x77\xc1\xd0\x8f\xa4\xb2\x2a\xfe\x43\x91\xde\x2e\xd9\x22\x4f\x64\xa2\xf4\xb0\x4f\xe4\x39\x50\xbb\x9c\x50\x7e\xf1\xcb\x1c\xec\x63\x21\xa7\x27\x8a\x9b\x33\x2a\x74\x38\xd8\x3d\xb8\x8a\xad\x78\x3c\x3c\x21\x23\x9f\x13\xf8\x7d\x08\x2d\x1e\xa0\x68\xee\x12\x59\x76\xbc\xf8\x20\x8e\xf1\xe2\xee\xf7\xfb\x72\x7d\xef\xdb\x65\xad\xcd\xc7\x71\xbf\x74\xc0\xb7\x3b\x08\x1b\x2d\x41\xd9\x6e\x14\xa8\x1a\x7a\xdc\xd3\x8e\x5d\x31\xf7\x35\x08\x6f\x5b\xe5\xa1\x2b\x30\x5e\x1f\x06\x49\x68\xfa\xe8\x91\x60\x3c\xae\x3a\x3f\x19\xb1\x3a\x78\x50\x4a\x06\x2e\xd0\xe6\xa5\x5d\x31\xab\x10\x17\xba\x8e\x6a\xa1\x57\x65\xb1\xb7\x57\x09\xac\xed\xdf\x37\xa5\x0c\x66\xd9\xc2\xaa\x3d\x06\x0e\x32\x5a\xfe\x13\x2e\xb9\x0d\xb1\x10\xb3\x1f\x70\x2b\x6e\x4a\x5f\xb8\x08\x16\x7f\xec\x62\xfa\xa6\x82\xbb\x02\x97\x5c\x5a\xc2\x69\xa3\x8f\x75\xdf\x53\x6f\xad\x1b\x56\x8c\x8f\x16\x33\x8e\x04\x51\x75\xcf\xe8\x9a\xfb\xb0\x13\x4a\xe1\x25\xdc\x31\xd6\x03\x72\x66\xef\x3c\xc5\x6e\xd2\x60\xcf\x75\xa0\xe4\xf2\x00\xe4\x3e\x49\xbe\xdf\x31\x9c\x6c\xf4\xb8\xe9\xce\x8e\xe9\xb4\x9a\x77\x9a\x86\x8e\x83\xfe\x22\xbb\xb4\xde\xa5\x22\x50\x78\x8a\x5a\x3e\x5e\x62\xbc\x9d\x1d\x83\xf3\x82\x8e\xe3\x02\x89\x53\xfc\x2e\xa1\xb8\x10\x2a\x65\x76\x5e\xb6\x8e\x24\x99\xca\x8d\xa2\xc9\xb9\xd2\xde\x36\xcc\x22\xb5\xbb\x82\xd5\xc2\x9f\x6a\xa9\xd5\xae\x19\x49\x52\x02\x50\x18\xda\xfe\x44\x86\x70\xa8\x31\xce\x9a\xae\x74\x88\x23\xd2\x06\x09\xf7\x03\x90\x84\xc2\x57\x28\x84\x14\x4e\x1e\xc8\x83\xaa\x13\x5b\xb9\x66\xb9\x1a\xf1\x04\xd9\xba\xb1\xe6\xa1\x63\x5e\x4f\x8a\xea\x6a\xc1\x9b\x07\x72\xa0\x79\x11\xcd\x83\x82\xfe\x1c\x80\x02\xb1\x8e\xaa\x10\x78\x1d\x45\xe1\x9a\x6f\x83\x9a\xbe\x3e\x75\x34\x9b\x21\x34\xae\xba\xd9\xf1\x80\x96\xcd\xcc\x3b\xd9\x0c\x95\xa1\xe9\x20\xc4\x8e\xd4\x05\x0a\xf9\x00\x4f\xc5\x94\x16\xcf\xec\xd0\x51\x72\x67\xb5\xab\xa9\x9b\x2b\x51\xd7\x2d\xcf\x53\x23\xc4\xcb\xab\x6a\xb1\x32\x79\xd4\x9d\xe6\x52\xf3\x0d\x02\x5c\xe2\xa2\x12\xcf\x88\xec\x2b\x11\xf6\xfb\x46\xbb\x54\xf5\x5f\x2b\xe0\xa5\x2a\xb4\xea\x20\xbf\x66\xf4\x4b\xad\xa3\x61\x03\xcc\x16\x63\xe9\x56\x2d\xe7\xa7\xe6\x3a\x46\x24\xa0\xcb\xcd\x6d\x2a\xc6\x25\xca\xfe\xb1\xb9\x12\x31\xa2\x00\x49\x30\x2c\xa6\x18\xc1\x6c\xf0\x9c\xb8\x7e\x08\x2d\x8d\xeb\x13\x70\x6e\xfc\x91\xf5\xb8\x4d\x46\xfc\xc3\xda\x49\xda\x3d\x47\x78\x19\x69\xff\x7f\x2a\x4f\x79\x2e\x14\xc3\x39\xd1\x59\xbc\xe3\xd2\x2f\x2e\x67\x90\xb5\xc4\x20\xc3\xf6\x94\x6d\x3f\x2a\x20\x56\xf5\xd6\xe3\x89\x5d\x85\x27\xb8\x30\x04\x9d\x75\x13\x3b\xda\xcc\x08\xb6\xf9\x02\xcb\x50\xd2\xf7\x8b\x4e\x2b\xdb\x2a\x2c\x74\xf6\x83\xc5\x22\xbe\x14\x9e\xa8\x1a\x11\x56\xd7\xb6\xcf\xe3\x5b\x80\xd5\x0c\x4b\xbc\x56\xdd\x35\xf3\x20\xe2\x3b\x69\xc6\xa3\x43\x3c\xdd\x38\xb6\x93\x67\xc2\xbe\x56\x78\x27\x99\xae\x57\x3c\x76\xc5\x55\x0a\x2e\x0e\x9b\x1a\xc3\x65\x80\xae\xd4\xec\x9d\xfc\xb2\xe2\xa6\x88\xc4\x47\xa2\x93\x4a\x8b\xe9\xdd\x5a\xba\x90\x62\x9f\x7f\xca\xd8\x56\xb2\x2c\x10\x78\x94\x8d\x97\x71\x90\xad\xaf\xaf\xaf\x57\x47\xb4\x92\x14\xb4\x73\x2b\x31\xad\xb8\xf6\xb7\x35\xda\x7a\xe4\x77\x10\xb4\x75\x77\xfb\x7f\x77\xfb\xff\xd7\xbe\xfd\x17\x57\xff\x0c\x56\xc6\x1c\xf3\x47\x4a\xf9\x66\x31\x50\x7c\x96\x05\xd5\x86\x00\x6b\x83\x01\xc4\x54\x0b\x32\x46\xca\x6c\x07\x5b\xe6\xe6\x10\x19\xc1\x85\xd1\x64\x42\x33\x9a\x14\x84\x26\x67\x39\x14\x3a\xcd\xd2\xf3\x9c\x66\x6b\xc8\x61\xec\x79\x94\x84\xe9\x39\x68\x2c\x50\x24\x11\x72\xef\x9e\xc8\xe9\xff\xf3\xf5\xab\x97\x45\xb1\x10\xbe\x88\x39\xd7\x34\xd3\xc8\xae\x1f\x16\x58\x9f\x08\x84\x11\x4d\x93\x94\x31\x82\x38\x4a\x28\xeb\x49\x92\x86\x74\x0d\x79\x9f\x73\x6a\x54\x03\xbf\x98\xc7\x6c\x64\x62\x63\x6b\x77\x9b\x36\x72\xc5\x31\xf9\xcf\x97\xef\xb6\x8c\xea\x66\xd9\x56\xbb\x5b\x5a\x4a\x4a\x0e\xac\x85\xb7\x12\x99\xae\x49\x04\xc8\x4f\x4c\xb4\x07\xf7\xab\xdc\x59\x3b\xeb\xa5\x32\x80\x30\xca\xe3\x2d\x7f\x96\xe6\x45\x8f\x14\xd1\x9c\xa6\xcb\xa2\xc7\x2a\xcc\x7a\xa0\x64\x3e\x4f\x33\xf1\xd8\x11\x36\x13\x06\x47\x76\x09\xfc\xf7\xe5\x0b\x69\x0b\x62\x8f\xd3\x71\x10\xb3\xc4\xd1\xe3\xef\x1e\x7e\x07\x81\x8b\xf9\xde\xc3\x2b\x64\x3b\xa1\xf8\xf5\xe5\x0b\x19\xaa\x6c\xd6\x0c\xd9\x85\xd6\x54\x9a\x6c\x94\xec\xaa\xf6\x6b\x85\xa7\x45\x46\x17\x10\x09\x90\x9e\x5b\x53\x66\xc9\x4e\x02\xf0\x1d\x3a\xcb\x08\xc9\xe9\x69\x9a\xc6\x34\x48\xae\xe0\x8e\x95\xed\xcf\x52\x82\xd1\x58\x16\x6e\x3f\xd1\x81\xcf\x6c\xcb\xf0\x2d\x85\x31\x8d\xe4\x2e\xb3\x03\xe6\x45\x20\xab\x9e\xa3\x9a\xdf\xa0\x70\x42\x5a\x13\xaf\xd8\x50\x36\x21\x5a\xbc\x82\x21\xbf\x7c\xb7\xa5\xe3\x06\x73\x49\x0b\x61\x1e\x4d\x04\x3c\x39\xc3\xce\x15\xad\x8a\x8c\xf1\x74\xc4\x0b\xb5\x35\x5d\x6b\xba\xa0\x49\xa7\xfd\xf6\xf0\xfd\x07\x19\xea\x94\x13\x0e\xef\xdc\xce\x1a\xf2\xd4\x08\x73\x7b\xef\x9e\x39\xa9\xc6\xa1\x6f\x09\x06\x35\xed\xa7\x41\x1e\x8d\x49\x9b\x6c\x40\x17\x9e\x2e\x19\x7b\x40\x55\x6c\x90\xf6\x48\x5d\x15\xaa\x7a\xfa\x45\x2a\x1e\xdf\xb5\x4f\x83\x9c\x3e\x7a\xd8\xb6\xc6\xaf\xfd\x94\xbf\xa4\x41\x48\xb3\x4e\x7b\x0f\xf8\x6a\xf4\x5b\xc0\x4f\x5b\xd0\x3e\x1f\x61\x45\x21\x26\x1f\xd3\xa4\x78\xc0\x0e\xda\xed\x1e\x69\x33\xc9\x3f\x1a\x43\x15\x83\x5f\x73\xa9\x76\x54\x37\x56\x62\xca\x6a\xc8\x95\x47\xb4\xb9\x4c\xc6\xe8\x50\x6d\x6b\x92\x7d\x17\xcf\x0b\x74\x7d\xed\x8f\x5d\x5e\x45\x7a\xb9\x1d\xcb\x52\xea\xd2\x6c\x92\x93\x34\x63\xd2\xaa\x08\x86\x0d\xf4\xa8\xb5\xfb\x1a\x73\x49\xd8\x81\x97\x1e\xfc\xdd\x41\x34\xb9\x54\xf5\x0b\x24\x4b\x45\x3e\x76\x43\xee\xb3\x06\xd8\x4f\x93\x84\x8a\xf7\x18\x92\xc2\x34\x25\x1a\x97\x8b\xb2\x75\x19\x10\xe4\x03\xbd\x28\x9c\x0e\x0a\x58\xf4\x0c\x45\x58\xe5\x9b\xdd\xaa\xea\xd2\x3b\x51\x7f\xc7\xd7\x20\x5e\x25\xcd\x63\x53\x03\x0d\x04\x35\x44\xb0\xa7\x38\x4e\x05\x25\x88\xac\x17\x4e\x34\x18\x52\x64\xd1\x74\x4a\x33\x1e\xc2\x8a\xcd\x3e\x88\x2d\xca\x1f\x2d\xc3\x41\x1d\xc1\x40\x0f\x7c\x54\x63\x46\xa2\x6e\x42\x3f\x60\xbc\xb2\x63\x70\x93\x04\x7c\x87\xe7\x45\x50\xd0\xf1\x2c\x48\xa6\x7e\x05\x02\x7f\x56\x20\x11\x1f\x84\x97\x60\xd0\x0f\x37\xc2\x0f\x19\x87\xb1\x59\xde\xba\x19\x49\xba\x01\xc5\x68\x40\x79\xab\x84\x42\x94\xd9\x97\x59\x35\x14\x05\x67\x32\xef\xad\x95\xba\xb1\x5a\x91\xb6\x08\xbe\xda\xb2\x2f\xb6\x8c\x96\xd9\x59\xf0\xca\x42\xb1\xde\x08\x5c\xcc\x9a\x95\xe5\x7d\xbd\xf4\x3e\xf0\x52\x1d\xbc\x79\x88\x85\x7c\xbb\x1c\xc0\xee\x42\x15\x13\x10\x2b\x0d\xaf\x2b\x7d\x59\x1e\x5f\x32\x7a\xe7\x8f\x66\x61\x71\x31\xaa\x2e\x59\x5b\x51\x2e\xea\xa7\x26\x33\x55\x42\x80\x54\x70\xda\xc2\x00\x3b\x3f\x24\xed\x82\x4c\x82\x28\xa6\x61\x9f\x1c\xb2\x73\xda\x79\xc4\xce\x1e\x01\x44\x9d\x2b\x5f\x4d\xa8\x4d\xcf\x5c\x68\x7c\x2a\x7d\x86\x8a\x6e\x12\x85\x23\xf2\x83\xfa\x93\xfa\x3e\xb6\xfb\x64\x8b\xf1\x88\xb4\xb7\xfa\x43\xa5\x3c\x94\xfa\xc7\x76\x42\x8b\x8f\x71\x94\x17\x34\x01\xbf\x91\x6b\x96\xf6\xf0\xc4\x30\xe8\x92\x0a\xae\x8c\x87\xd0\x73\xc9\x57\x5a\x15\xb2\x41\xea\x49\x70\xd4\x05\x78\xe8\x52\x55\x60\x9c\xf6\x99\x98\xdb\x1a\x3d\x66\xbf\x0c\xf9\xb9\x35\xda\xfc\x9e\x9d\xfc\xb7\xef\x4e\xfe\x77\x27\xff\xbf\xf8\xc9\x5f\x1b\xfe\xc3\x63\xc9\x5b\x32\xfa\x57\x86\x9c\xf8\x54\x79\x1a\x4d\xb9\x0d\x6e\xff\x57\x7e\x42\xe7\xf7\x20\xe1\x2b\x3a\x31\x37\x04\x15\x4b\xf4\x12\x3d\xd8\x33\x36\x4e\x0e\xc1\xd9\xc5\xf9\x8c\xf5\xbe\x63\x1a\x68\xfd\xc8\x0b\x93\xfb\x64\xcb\x7d\xf1\x07\x16\x7f\x4c\x8a\x37\xdf\x3d\x12\xff\x4b\x3c\xc1\xdc\xdf\x8a\x53\x5d\x90\x90\x83\xa7\x7b\x6f\xc4\x24\x87\xe4\x87\xef\xc9\x38\x9d\x2f\x96\x22\x8e\xcf\xe9\x25\x99\xa7\x67\x51\x32\x45\xd1\xea\x1e\x92\xf1\x2c\xc8\x60\x2f\xe0\x37\xb3\x21\x37\xa5\x92\xe6\xea\x12\x3a\xa6\xfc\xd1\x42\x91\xb2\x06\x39\xae\x72\xd2\xd9\x23\xbb\x64\x73\xd8\x23\x4f\xd9\xff\x9b\x3d\xd2\xef\xf7\x7b\xe4\xff\xc8\x2e\xd9\xfe\xae\xcb\x0e\x3b\x24\x5f\xd0\x71\x34\x89\xf8\x42\x3a\x78\x7f\xb8\xb9\xfd\x68\xf3\x91\x6d\x62\x16\xe5\x29\xa4\x8b\x71\xb8\x5e\x8b\xaf\xf8\x5b\x5c\xd6\x11\x36\x40\xf3\x6a\x0d\xdf\x2c\x0b\x49\x2a\x94\x60\xc2\x67\x83\x59\xbf\x31\xa1\xac\x62\x3c\x8f\x6c\x44\xed\xbd\x76\x9f\xa1\x65\x3f\x0d\xe9\x5e\xd1\x19\x22\xad\x35\x1b\x5b\xfb\xff\x9c\x6c\xce\x00\xf9\x7b\x61\x20\xd6\x22\x3d\x5a\x2c\x68\xb6\x1f\xe4\x5a\x95\x8d\xb2\xf9\xb3\xe3\xce\xc3\xae\x7c\x09\x2c\x12\x86\xbd\x87\xd6\x8d\x19\xcf\x5d\xc4\x51\xd1\x69\xb7\xbb\xe6\x2b\xec\xa4\x6b\x5a\x57\x8d\xd3\x90\x0d\x2e\xf1\x75\x5e\xca\x87\x00\xf3\xd3\x2e\xd9\x63\x02\x21\x7c\xfc\xb8\x4b\xfe\xaf\xeb\xc4\x98\xf0\xcc\xac\x98\x58\x03\x52\xb9\x30\x0e\x29\x79\x40\xf6\xc8\x06\xd9\x1c\x22\x3b\x23\x5f\xdc\x05\x19\xdb\xd6\xb6\x61\xba\xea\xf6\x7f\x4d\xa3\x84\x0d\xd3\xb6\x54\x1c\x2f\xc1\xa7\x2e\x4c\xf1\xeb\xc3\x67\x8c\xb0\x37\x87\x92\x29\x09\x0b\x3f\xa0\x7c\x0f\xc5\x7d\x3f\x7c\xf4\xd0\x26\xb8\x79\x1a\xfe\xf0\xfd\xe6\xb0\x8c\xd0\x4c\xfa\xd2\x7e\xb2\x39\x35\x89\xc2\x95\x54\x94\xd1\x79\x10\x25\x5c\x77\xc4\xf2\xf4\xdd\xa3\x70\x1d\x64\xb2\x07\x01\xac\xed\x96\xb7\xba\x96\x53\x24\x60\x56\x12\x4c\x59\xbc\xfe\x60\x98\xc8\xe9\x26\x41\xd6\x3e\x48\x0a\xee\xc3\xa7\x47\x36\x87\x5d\xf2\xff\x67\x58\xdb\x70\x6a\xe1\x2e\x97\x84\xf9\xb9\xef\xe5\xaf\xaa\x4b\x95\xd4\xf5\x19\xf3\x54\xff\x0e\x89\x9b\xa0\xc3\x3a\x10\x06\xff\x70\xa1\x0e\x09\xe2\xad\x83\x60\x9f\x72\xbe\xfc\x93\x33\xc0\xde\xd4\xfd\x93\x20\x2c\xa1\xf5\x92\x73\xbb\xea\x44\x31\xae\xeb\x27\x85\x20\x57\xcb\xb9\x7c\x9d\x63\x11\x15\x83\xd9\x53\x39\x4e\xdf\x03\x94\x25\xc5\x68\x36\x84\x2b\xc5\xd6\xb0\x56\x8c\xe5\xf4\x51\x8d\x55\xce\x10\x40\x47\x94\x3f\x95\xbe\x0a\xd0\x4b\x05\x11\x6d\x96\x6c\x3e\x42\x2c\xec\x34\xc8\xe9\xf6\x23\xb2\x0b\x65\xb4\x7a\x68\xfb\x91\x61\x02\x10\x86\x94\x6b\x16\x61\x0f\xec\xf0\x42\x3d\xb2\xf9\x9d\x29\x09\xab\x7e\x3e\x3d\x0d\x92\x0e\x2f\x66\x32\x3f\x6b\x31\x0b\x7f\x2b\x68\xe1\x3e\x65\x43\x2f\x52\x63\xf7\x62\xd3\x47\xc0\x71\x6e\x76\x29\x57\x34\x57\x26\x81\xbd\xee\x5b\x1e\x6b\x24\x49\x0b\x21\x94\xfd\x18\xfd\xd4\x9a\x82\x44\xc2\xfd\xf8\x4c\x34\x52\xf3\x59\xc0\xa5\x35\xd8\xdf\x2e\xc6\xf1\x32\x8f\xce\x54\x68\xd4\xe8\x34\x8a\xa3\x42\x09\x38\xa7\x41\xf2\x69\x70\x9a\x05\xc9\x78\x46\x72\x9a\x9d\x45\x63\xb9\x01\x06\xdc\x8f\x6f\xeb\xc7\x41\xf4\x53\xdf\xa6\x21\x15\xa6\x24\x97\xbb\xd0\x84\x66\x6c\x1b\x0a\xe2\x69\x9a\x45\xc5\x6c\x4e\x42\x9a\x8f\xb3\xe8\x94\xb3\x25\x21\xff\xd0\xa4\x7f\x1e\x7d\x8a\x16\x34\x8c\x02\x10\x82\xd8\xd7\xe0\x20\x29\x68\x96\x04\xfc\xe9\xc4\xc7\xa7\x41\xf2\xe9\xa3\x70\x22\xfc\x91\xcf\xeb\xff\xef\x67\x31\xd2\x64\xfa\x91\x0d\xf1\x23\xbc\x25\xfa\x18\x46\xd3\xc8\x79\xca\x21\xa7\xc6\x47\x91\xa7\x72\x4f\x95\x33\x20\x9d\xe1\x14\xa9\x67\x9b\x6d\x40\xab\x4f\xed\x15\x79\x6a\xb1\x45\x31\xa3\xfb\x7c\x9f\x6a\xff\xf3\x79\x7b\x67\xcd\xcb\x33\x05\x8f\xed\x58\x3b\x77\x07\x57\xb0\x41\xda\x43\x10\x95\xa0\x15\x6c\xee\xc2\xd0\xf1\x8c\x61\x83\xec\x92\x0e\x17\xa7\x3a\x3f\x3c\x26\x0f\x74\x13\x5d\xf9\x6c\xe0\xc1\x96\xb5\xdf\x2a\x6f\x1f\x66\x53\xa8\x4e\xd1\x60\x8d\xda\x4a\x30\x11\x84\x2b\x20\x6c\x1e\xa0\x3e\x4a\xf2\x22\x2a\x96\x85\x74\x85\x1d\x85\x34\x29\xd8\xa6\x65\x07\x76\xe0\xb5\x1c\x24\x61\x94\x51\xd3\x80\xc1\x7c\x63\x93\xf7\xa4\x2c\xab\x1e\xd9\xc0\xab\xa9\x16\x6a\xa9\x05\x4d\xb5\x74\x5b\xad\x55\x78\x91\xd9\x13\xaf\x7b\x6c\xf3\x08\x6c\x72\x86\xf6\xf3\x0f\x2f\xd9\x3c\xc8\xd7\x2d\x18\x03\x28\x55\xf5\xad\x6b\xf1\xeb\xb4\x8a\x5f\xcb\xa7\x74\x1c\xb9\x22\xfa\x7b\x94\xf3\x97\x72\x98\x8f\x3b\x72\x27\x78\x6e\x29\x95\x37\xd5\x5e\xe4\x51\x7c\x48\x85\x07\x7f\x4e\xc7\x5b\x52\x42\xe7\x01\xf2\x0b\x53\x29\x27\x44\xd8\xbf\x4c\xc4\xc9\x0a\x0b\x7f\xda\xb9\x4c\xad\xae\x5c\x61\x01\xba\x5e\xfa\x7a\x10\x8f\x59\x47\xfd\xf0\x8e\xaa\x47\x52\x8f\xd6\x06\xc6\x86\xb5\x35\xee\x28\x2d\x4a\x18\xfc\xe7\x9f\xcf\x8f\x87\x0f\x7e\x38\xf9\xbc\x75\xd5\x79\xfe\xe1\x25\xfb\xbd\xf7\xe0\xff\x4e\x3e\x6f\x6e\x5f\x7d\x51\x1f\xdb\xc3\xde\xf6\xe6\x55\xf7\x7f\x06\xfd\x02\x94\xa0\x6a\x03\x37\xde\xe5\x95\x31\x06\x04\xce\x9f\xe7\x6d\xae\x88\x30\xf1\x04\x13\x4e\xff\x5e\xb4\x3d\xd3\x4b\xf0\x76\xf0\xf6\xcc\x5d\x49\x16\xe2\xf4\xa0\xf0\xe3\x9e\xed\x87\xe4\xcb\x97\xb2\xbc\xef\xae\x39\xec\x09\x89\x92\x92\x81\x1b\xdc\xe7\x76\x86\xee\x65\x23\x8d\x06\xbf\x35\x6c\x64\xb5\xc9\x45\x4a\x36\xd2\x7c\x39\x67\x80\x47\xb9\x38\x3e\xcc\xd3\xf0\xc1\x0f\xdf\x3f\xd8\x1c\xaa\x6c\x38\xe3\x42\xef\xc6\x69\x4c\x3a\x07\xef\x0f\x07\x07\xcf\xf7\x09\x3b\x37\x8c\xb6\x86\xc3\xed\xae\xcd\x93\x51\xb5\xee\x29\x14\xe5\x3a\x03\x97\x79\x0d\x87\x2d\xce\x84\x5b\x3d\xb2\xd5\xcc\x56\x15\x33\x55\x63\x4b\x21\x74\xda\x27\xff\x7c\xf7\xfc\x67\xc7\x43\xa2\x2a\xe0\x1f\x4d\x69\x8d\xee\xa4\x22\xc8\xba\xe1\x69\x02\xe8\x80\xfb\x3c\x67\xc8\xdf\xf7\xc8\xc3\x2e\x19\x91\x76\xbb\xd1\xb8\xc7\x71\x04\x0f\xc9\x54\x07\x41\xf9\x14\x25\xf6\xf8\x18\x16\x7e\xde\xfb\xc7\xe1\x8b\x7f\x1d\xbe\xfb\x5f\x7b\x56\xa1\x8e\x92\x39\xb5\xeb\xf7\x4e\x2e\x07\xba\xf1\xd8\x37\x37\x57\x1f\xb9\x58\x4d\xfe\x73\x89\x7b\xf0\x70\x87\xe6\x54\xe0\x0c\x2f\xf0\x9c\x43\xf0\xbd\x93\x18\x9c\xcf\xf1\x99\x71\xe8\x70\x07\xfc\x10\x1d\x62\x4b\x8f\x32\xf2\xfc\xa1\x4e\x29\xc6\x09\x95\x9f\x51\xcc\xf3\xcc\xe6\xa3\x6e\x8f\x6c\x0d\x95\x6b\x35\x43\xca\x93\xe8\xb5\x06\x29\x0b\x37\x5b\xa0\x25\xde\xb0\x0e\x20\x8b\x2b\xf5\xb1\x5e\xb1\x35\x32\x3f\xaf\x4e\x7a\xdb\x0f\xef\xd4\xf8\x77\x6a\xfc\xbf\xb8\x1a\x5f\xa8\xf0\x17\xe3\x6a\xfb\xbd\x1b\x58\xdc\xb5\x74\x08\xcc\xd6\xce\x4a\xa1\xfb\x6a\xec\xf4\xb8\x9e\x69\x31\xf6\x5a\x82\x2d\x82\x62\xd6\x23\x09\x35\xac\xbf\x3f\x82\xe6\xc2\x79\x78\x2a\xaf\xaa\x71\xf0\x70\xe9\xb5\x40\xd8\xeb\x80\x8d\x0f\xfb\x8f\xa7\xea\xac\xb1\xba\xe1\x05\xae\x58\xc8\x84\xce\x67\x06\x3d\xd2\xe5\x95\x8f\x4d\xab\x58\x3f\x4d\x3a\x6d\x18\x55\x1b\x07\xdb\xed\x1a\xf6\xd3\x79\xca\x98\x18\x7f\x4b\x78\xf0\x76\x9f\xe8\x7b\x65\xfe\xc2\xb0\xdd\x23\x14\xb1\xde\x8f\x9c\x0d\x8a\x0b\xef\x8e\xed\xe5\xd3\xdb\x83\x24\xc4\xed\xa3\xe6\x4b\x2b\x23\x6b\xea\x8d\xc1\xab\x83\xf7\x1f\x9e\xbf\x81\x15\xb4\x7f\xf8\xe6\xcd\xf3\xfd\x0f\x07\x87\x6f\xc8\xbb\xe7\xef\xdf\x1e\xbe\x79\xff\xfc\x7d\x69\xab\x61\x50\x04\xb8\x59\xf6\x8d\x37\xa7\xc1\x7d\x61\x46\x38\x0f\x2e\xc6\xe9\x7c\x11\xd3\x8b\xa8\xb8\x1c\x91\x47\x40\x59\x56\x0f\x41\x17\xaa\xec\x10\x58\x55\x7a\xbf\xe9\x7a\xe2\x12\x09\x9b\x83\xcf\x66\xa0\x74\x38\xf8\x85\xb6\xed\x84\xe8\x0e\x0f\x20\x0f\xfc\x25\x24\xe7\xb3\x68\x3c\x23\xf3\xa0\x18\xcf\x84\xf8\xca\x37\x21\xc6\xd0\x42\xa3\x9c\x27\x2c\x06\x34\xed\x8f\xa4\x0e\xd7\x51\x4e\x6f\xc1\x02\xc1\x05\x17\xd5\x7f\xf4\x13\xf2\x31\xbc\x8d\x8b\xc2\x13\xd7\xd9\xbe\x2a\xcc\xc6\x2a\xc0\x76\x1c\x28\x3b\x26\x7d\x69\xac\x66\xa8\x46\xf4\xdd\xae\xe8\xca\xc1\xe2\x24\xca\xa8\xe1\x11\xc0\x46\x57\xd9\x78\xd8\x50\x3c\xad\x57\x80\xeb\xc0\xd1\xd8\xb4\x45\xff\x85\x34\xa6\x05\xad\xaa\xc1\x1e\x8c\x8d\x1b\xfc\x0a\xfb\x17\xb6\x6b\x01\x21\x0a\x82\xe0\xf5\x81\x72\x87\xdb\x4a\x25\xdc\x59\x0e\x49\xb9\x0f\xe9\xa8\xe8\xaf\xad\x49\x61\xd0\x24\xe1\x35\x5b\xed\x01\x2f\x32\x99\xf0\xa7\x79\x1e\x12\x8f\xcc\xc2\xd8\xa3\x2b\x5e\x55\x36\x1b\xec\x59\xf2\xda\x3f\xb8\xcb\x76\xed\x79\x58\x2e\xf1\x67\xcf\x1f\xec\xbf\x3c\x7a\xf3\xbf\xcf\xdf\xa9\x7a\x42\x3a\x9e\x2d\x93\x4f\x34\x14\xaf\x4a\xf8\x8b\x51\xf1\xd7\xcf\xe8\x22\x0e\xc6\xb4\x33\xf8\xf7\xd5\xf1\xbf\x93\x7f\x67\x27\x4f\xfe\xfd\x79\x30\xed\xb5\xaf\xbe\x3c\x78\xf0\xe5\x73\xbb\x0b\x3e\x93\x3f\x7b\xe1\xff\x7d\x22\x4b\x1c\x8b\x32\x27\xac\xd0\xb1\x2c\x75\x72\xec\x2f\x67\x97\x32\x0a\x95\x94\xd1\x6d\xa1\x96\x54\x43\xa8\x8c\xb8\xe6\x63\xd9\x6d\xc9\x49\x0d\x0c\xb8\x6b\x16\x10\x8f\xf8\xcb\x60\x00\x77\xa0\x54\xb8\xc3\x00\x4f\x1b\x50\xc1\x9a\x43\xfa\x2c\x6f\x9f\x65\x99\x2b\x57\xf8\x9d\xb1\x60\xc8\x06\xe1\xef\x5f\x0d\x51\x5d\xdd\x59\x5b\x9c\xcc\x75\x6a\xe0\xb3\x05\x83\xbe\xa3\x52\xc2\x9a\x86\x1b\xd3\xac\xb9\x8b\x4f\x77\x66\xd7\xee\x8c\x18\x3a\xf8\xfa\x55\x16\xd4\xe0\xfa\x2e\x19\xd3\x18\x22\x05\xc8\x47\x9c\x46\x99\x71\x4c\x83\x4c\x9a\x70\x59\xad\x88\x64\x6b\x41\xfb\x81\xc0\x57\x43\x21\x2b\xf2\xed\x71\x66\x79\x7b\xaf\xc3\x7f\x95\x76\x95\x02\x67\x18\xfe\xaa\x47\x36\x87\xc3\x21\xb9\xcf\x2f\x67\x3c\x77\xad\x5e\xc7\x0f\xf0\x6e\x0f\xb0\x23\xf1\xc5\x38\x48\x4e\x05\xbd\xf0\x58\x3f\xe2\x5d\xdf\xea\xa8\x72\x67\xcc\x22\x11\x08\x62\x25\x2c\x2b\x9d\x0e\x73\x16\xd1\x5f\x2c\xf3\x99\x69\x31\x68\xbb\x11\xc7\xe0\xc2\xf9\x0f\xe3\x91\x3f\x8b\x2d\x34\x08\xc3\x1c\x47\xa2\x17\x56\x0e\xae\x34\xc6\xd5\xc3\xbd\x35\xbe\xe1\xca\x83\x81\x38\x6b\x47\xdc\x0f\xbf\xe0\x7a\xb0\x1b\xcb\x5b\x21\x95\x7a\x10\xf2\x52\x41\x96\x45\x67\x14\x33\xdc\x20\x54\xb3\x27\xdb\xab\xe0\xb0\x1e\x68\xc3\x0d\xbf\xdf\xa6\x14\xc9\x14\xf2\xb5\x7a\x04\x21\x6b\xc5\xd7\xf1\xf0\x44\x6d\x99\x70\x85\xcd\xfb\xa6\xa1\x45\x82\x59\x82\x27\x62\x89\xce\xbb\x79\x91\x5d\xd5\x9b\x2a\x89\x97\x81\xf6\x55\xc3\xb2\x6e\xb9\xab\xc9\x75\x84\x57\x2a\x39\x9f\x51\xe9\x77\x20\xe4\x62\x39\x9c\xbe\x40\xe3\xce\xf6\xf7\x10\xa1\x59\x10\x71\x05\x6a\x5d\xfb\x4e\x75\xb4\x9f\xa4\x59\x87\xe1\xe5\x13\xbd\xe4\x27\x45\xdf\x00\x4c\x27\x30\x1d\x3f\x50\x7f\x16\xe4\x87\xe7\xc9\x5b\x88\xe4\x55\x5c\x42\x64\x4a\x8b\x0b\x94\xa0\xe7\x13\xbd\x3c\x29\xb7\xed\x6c\xa7\x09\x39\x78\xbb\xdf\xee\x5a\x8b\x5f\xc8\x16\x15\x75\x3a\x66\x16\x7a\x99\xec\x63\x1f\x84\xc2\xcd\x39\x41\xc7\x8d\x28\x27\x79\x11\xf1\x28\x2b\x51\x88\x88\x1a\x9b\x85\x96\x22\xdc\x6f\xc7\xd9\x29\x3f\x2d\x49\x39\x80\xed\x1e\x19\x15\xfd\xe8\x71\x2a\x30\x7b\x35\x4d\x13\x2a\x34\x4f\x9d\xf5\x8f\xb6\xd8\x7f\x9e\x45\x05\xf8\x4b\xb1\xb8\x11\x02\xb1\x8e\x50\x1f\xdd\x33\x94\x74\x31\xb8\x5e\x56\xbb\x50\x20\x79\x87\x5e\xf5\x82\x60\x0d\xd3\x8f\x55\x2f\x7d\x8f\x9e\xae\x10\x63\x93\xdd\x31\x38\xf7\x0a\x28\x92\x68\xaa\xc7\x12\xf1\x1c\xa1\x6a\xcf\x9a\xb2\x97\x21\x7a\xf6\xeb\x1b\x55\x85\xc5\xf3\xf5\xc4\x06\x45\xd5\x58\x6a\x30\x87\x52\xbb\x8f\x12\xeb\xcf\xb7\x4f\x5a\x66\x77\x42\x9b\x68\x9d\x51\x1c\x77\x3c\xff\x4a\x97\x60\x65\xad\x5f\x9b\xb5\xda\x1b\x36\xbb\xdd\x68\xb7\x48\x8e\x0d\xb3\xfb\xd8\x4e\x5b\xf3\x41\x78\xb1\x95\x16\x24\x5f\x2e\x16\x69\x56\x80\x6e\x8d\xdf\xd4\xbe\xdd\x27\x4a\xab\xd2\x36\x1c\x41\x96\x13\x66\xe3\x97\x0a\xd7\x59\x8c\xf5\x54\xb6\x12\x85\x79\x8f\xf5\x40\x53\x95\x16\xf4\xc8\xa1\xae\xbd\x9b\x96\x7a\xbb\x71\xf5\xb8\x1a\x83\x8e\x93\xf6\x92\x57\xda\x57\x27\xbd\xed\xef\xee\x54\xba\x77\x2a\xdd\xff\x0a\x95\xae\x78\x58\x71\xa3\xe7\xd8\x7b\x41\x96\x26\xe4\x7f\x97\xf3\xe0\x2c\xca\xc9\x8f\x01\xfb\xfc\xdb\x27\xfe\xd9\x9f\x53\xaf\xba\x77\x30\x20\x07\x49\x54\x44\x41\x1c\xfd\x46\xc9\xdf\x79\x2f\x18\xa1\x06\x24\x07\x4b\x2c\x69\x70\x03\x03\x65\x4b\xd5\x70\x72\xde\x07\xad\xae\x2c\x26\xa3\x97\x88\xc8\x5a\x07\xe1\x88\x0c\xeb\x6e\xde\xb8\xb5\x07\x1b\xbe\xed\x56\xd7\x6b\x66\xe2\x75\xa7\xab\x5f\xa1\xc9\x20\x5e\x13\x89\x50\x68\x49\x1b\xf4\x78\x9c\xf0\xf2\xd7\x29\x3d\xa4\xea\x99\xc8\x6a\x64\x96\xf4\xbd\xeb\x75\x43\x84\x46\xc0\xda\x73\x7a\x3f\x58\x13\xe8\x29\x71\xc5\xcb\xdb\xea\x89\xc6\x0c\xa7\xa9\x3c\xab\x5b\xa6\x5a\x96\x4d\x3a\xc6\x3c\xca\x6c\x77\xbd\x8d\xc2\x69\x05\xe1\x19\x3b\xa3\xca\xd9\x21\x07\xcf\x20\x47\xf6\x4e\x4d\xda\xc6\x46\x99\x9f\x21\xff\xeb\x1f\xfe\x56\xc8\xa9\x46\x67\xcb\xe7\x41\x62\xa4\x2a\x5d\xbe\x0b\xe2\xff\xb3\x03\x93\x7c\x21\xd4\xdc\xf0\x42\xe2\x40\x1d\x1e\xa5\x01\x91\xdf\x54\x47\x29\xeb\xea\x42\xba\x79\x5e\x66\x5b\x0d\xf8\xcd\x33\x24\x1a\xac\xf6\xac\x50\xd7\x3c\xd1\xba\x0c\xe5\x3e\x7d\x90\xce\x59\x00\x3d\x51\x6d\xf7\xe9\x19\xcd\x2e\x3b\xd2\x1b\xf2\xfb\x28\x99\xc6\xf4\x35\x47\x78\x97\x8c\x88\x37\x43\xd7\x24\xa6\x55\x75\xc4\x0f\x2e\x26\x50\x1d\xb4\x94\xf0\x2e\xe9\x06\x59\x10\xc9\x34\x4e\x91\x86\x6d\x91\xc8\x90\xf3\xb3\xbb\xbb\xcb\xa9\x06\x03\x09\xb7\x0b\x12\x96\x9d\xb9\x19\x18\xbf\xd6\x6d\xfb\xaa\x13\x32\xac\xe5\x53\x72\x30\xe0\x31\x07\x55\x92\xf0\xca\x8e\x99\x8b\x5c\x8f\x8d\xfc\xc9\x73\x46\x74\x0a\xef\xd1\x6a\xd8\xd1\x53\x06\x54\xee\xe2\x5b\x74\xdc\xe2\x2f\xbc\xae\x9c\x33\x55\x51\x95\x14\x70\xc2\x2e\x28\x8f\xc4\xa2\xe8\x48\xde\xd3\x25\x93\x88\xc6\xa1\x65\x7a\x20\x5a\x31\x7a\x6a\xf1\x1c\xdc\x41\x8b\xf1\xf0\xae\x59\x64\x28\x93\xad\xa8\x0f\x92\x2c\x5c\x47\x58\x0e\x7b\x93\xb0\x7d\xc9\xda\xe4\xb7\x60\x71\xa6\x1e\xde\x91\x15\x45\x7d\x42\x4e\x64\x62\xe0\xa3\x3b\x31\xf0\x4e\x0c\xfc\x6b\x8b\x81\xfa\x7d\x1e\x5f\x34\xb7\xf5\x42\xef\x76\xee\xee\x19\xc8\x6b\xa9\x6e\x2c\x35\x56\x86\x73\xa2\x88\xd4\x22\xad\x90\xd9\x27\x3a\x45\x0a\x97\x6b\x32\x97\x7d\x1a\x17\xf7\xc0\xf3\x74\xbe\x96\x0c\x86\x08\x0c\x7c\xf2\xe3\x60\x88\xda\x10\x1a\x67\xa0\x12\xdc\xd3\xb3\xaf\x88\x95\x63\x28\x5d\x41\x63\xf0\x3a\x48\x82\x29\xd5\xaf\xf3\x19\xcb\xe2\xa8\x30\x54\x01\xd2\x85\x87\x06\x47\xfb\xfd\xdc\xc0\x90\x53\x71\x36\xaf\xb1\x7f\x0f\x29\xe3\x30\x51\x62\xfa\xf7\xb4\xc4\xbf\xd3\x20\xe7\x3e\x17\xca\x22\x51\x4c\x29\x78\xa9\xf4\x6c\x52\xa6\xa7\x79\xdb\xb1\xa8\x6c\xd3\x6c\x0f\x48\xcc\x41\x84\x68\xa3\x34\xd6\x84\xe1\x4e\x14\x85\xcf\x51\xc4\xa1\xec\xf8\xa4\x2f\xc3\x9c\x09\x36\x2a\xa5\xce\xcd\x31\x77\xc6\xa9\x2f\x29\x44\x68\x0e\xb1\xed\xaa\x71\xf6\xc9\x6b\xc6\xca\x23\x9a\x8b\xe8\xd8\x80\x0f\xc7\x0b\xa5\xe1\xd9\xb3\x31\xde\xe4\xa0\xbe\xbc\x59\xc6\xb1\x76\x8c\xd1\x63\x52\x24\xbd\x88\xe0\xda\xcc\x87\xbb\x3f\x66\xfc\xa1\x5b\x0b\xbb\x43\xd6\xbe\x56\xdc\x1d\x07\x93\x8d\xa2\xed\xd8\x01\x4e\x54\x28\x19\xf3\x20\x46\x6a\xc2\xc7\xbc\x7b\xbb\x2f\x22\x4c\x54\xc7\x8e\xd1\x68\x13\xae\x5e\x39\xe1\x01\xd2\xd5\x89\xd3\x46\x13\x07\x3d\x60\x90\x2e\x96\x0c\xa2\x53\x49\x1e\x74\xa0\x5a\x2a\xb1\xb1\xee\xe1\xae\x25\x14\xe4\x7b\xdc\xe8\x29\x6d\xc9\x90\xca\xe9\x62\x8f\x40\xf4\xef\xaa\x10\x52\xe4\x89\xfe\xcd\xa9\x1b\x8a\x9c\x30\x76\x80\x3e\x6b\x3c\xeb\x3b\x58\xe7\xfc\x5e\x45\xcd\xc5\x98\x77\x11\xcf\x1d\xf0\x56\x9f\x15\x4d\x77\xc4\x25\xb8\xf7\xc4\x48\x31\x83\xf4\x62\x14\xda\x9b\x15\x38\x9b\x81\x63\xcf\x13\x2f\x80\xaa\xca\x1b\x9b\x44\xe0\xc2\x17\xb2\x48\xbe\x9f\x92\x74\xb8\x42\xe4\xa2\x40\xae\xdb\x46\x48\x68\x16\x83\x08\xbb\x63\x15\xfb\x88\xed\x25\x79\x65\xe7\xcb\x42\x9e\x00\x60\xb4\x0c\x30\x20\xe4\x19\x01\x86\xd4\x31\xc5\xaf\x05\x91\xea\x0c\xd0\x2c\x95\x28\x33\xaa\xdc\x2a\x63\x15\x87\x83\x2a\xe9\x22\x97\xe3\xd3\x94\xb6\x4e\x7f\xc5\xe8\x62\x19\x72\x68\xa7\xcb\x28\x0e\x01\x61\x62\x50\x2c\xd3\xf1\x6f\x0b\x0c\xff\xc3\xe1\xb3\xc3\xf5\xf5\x75\x10\xef\xdb\x39\x59\x4e\xe3\xcb\xbe\x88\x22\xc6\x0e\x04\xcb\x9c\xed\x89\x85\x6a\x25\x41\x2e\x65\xd9\x6f\x69\x57\xa3\x6e\x48\x18\xe3\x80\x0c\xf5\xde\x7a\xd3\x88\xf4\x74\xfa\xeb\x31\xcb\x3e\x1e\x9e\x9c\x30\xb1\x0b\x7f\x7e\xf9\xa2\xec\x36\x6d\x50\xfe\x63\x13\xca\xb0\xb1\xec\xf8\xaf\x8a\xac\xda\x01\x92\x20\x2e\xec\xa0\x57\x21\xaa\xec\x16\x55\x5d\xaa\x6b\xa3\x53\x1e\x02\x25\xf1\x3f\xcb\x22\x8e\x9f\x6f\x21\xbf\xeb\xd3\xf0\x2a\x7e\xa0\x89\x15\xc1\xc2\x17\xaa\xc0\x38\xab\x43\x5b\xa6\x44\xa9\x2f\xa6\xf4\xfd\x8c\x11\x8b\x45\x99\xd7\x79\x4c\xf3\xec\x86\x39\xbc\x68\x07\x33\x33\x65\x14\x69\x19\xd0\x78\xc3\xa9\x98\xdd\x35\xaa\x29\x1f\x82\x7d\x0d\x25\x48\x85\x65\x35\xf5\xf4\x2c\xc3\x5c\xd1\xa4\xde\x9d\xa3\xe4\x90\xcb\x8c\xc2\x0d\xe9\xbb\xb7\xfb\xca\x03\x13\x37\x65\x19\x07\x89\x12\x36\xa3\x44\x28\x5d\xfc\xbe\x9e\x32\xd7\xd7\x63\xbf\xdf\xbf\xc2\xf1\xdd\x6c\x5f\x7a\x5a\x93\x29\x8b\x7a\x38\x69\x9d\x4f\xfb\x52\x77\xf3\xab\x10\xa1\xa4\x01\xd3\x27\x3d\x9e\xb5\x32\x44\x8b\x92\x25\x8a\x9d\xd7\xd2\x06\xa6\xe9\xf5\xdf\xf7\x77\x7a\x9f\x3b\xbd\xcf\x5f\x5b\xef\x23\x94\x3e\xe1\xe9\x0d\x6e\xfe\x7c\x7a\x1f\xa5\xad\xc1\x8a\x1f\xce\x9c\x94\x46\xe7\xd9\x53\x83\x8f\xb0\x61\x98\x2e\x3f\x1c\x4d\x05\x8c\xd4\x4a\xde\xa9\x08\x14\xb6\xa6\xe5\xa5\xbc\xe3\xb1\xe9\x17\x17\x5c\xe4\x33\xb1\xa4\x2b\x4b\x0e\xea\xb0\x9a\xd1\xce\x22\x80\x1c\xb5\x4b\xc7\xd7\x41\x4b\xdf\xac\x77\xf9\xf2\x80\x45\x8b\x65\xa1\x1e\xaf\x25\xf4\x5c\x60\xb3\xa3\xb7\x4b\x26\x74\x8c\x48\x5b\xc1\x59\x71\x34\x46\xa4\x1d\x9e\x7e\xf4\xe5\x4a\x31\x71\x5b\xf5\x49\x35\x3a\xa5\xcd\x1a\x55\x70\xde\x46\x7d\xb9\xb2\xd1\x2d\xb7\xd1\xc5\xb2\x78\x49\x2f\xea\x87\xf9\x92\x5e\x94\x8d\xd1\xcc\xaa\x1e\x60\x7d\x5b\x1c\xa8\x6c\x68\xfe\xb6\xac\x71\x89\xcd\xe8\x58\xc3\xc9\x89\xe8\x69\x24\xf7\xc4\xd0\x7b\xa2\x5b\x00\x7c\x52\xb2\x73\x3d\x7b\xaa\x77\x2d\x4e\x3b\xad\xd1\x36\x6c\x51\x8f\xef\xb6\xa8\xbb\x2d\xea\xaf\xbd\x45\xe9\xab\x09\x5a\xcc\xae\x75\x2f\x21\x80\x6f\xf7\x55\x62\x49\xf4\x7f\x5f\xf8\x7f\xdf\x25\x88\xff\x1e\xa4\x66\xdb\x64\x20\xd2\x1c\xd9\x02\x5a\x88\x64\x09\x36\x2e\x6b\x6f\x9c\x26\x93\x68\x2a\xc1\x50\x28\x1c\x0c\x2d\x23\xab\x48\xb0\x73\xf1\x6c\xcd\xb8\xa0\x11\x89\x12\xe6\x05\x0f\x05\x6e\x21\x03\x12\x25\xc8\x41\xfe\xfe\x32\x19\xf3\x2d\x06\x43\xe5\x3c\x55\x82\x31\x56\x9c\x51\x1b\x48\xa4\xaa\xba\xb8\x83\x22\x0c\x11\x9d\x06\x89\xcc\xe6\x5e\x0f\x9d\xfe\xc8\x64\x25\x84\x80\xcf\xb4\x26\x77\x06\x4a\xe7\x2d\xde\x08\x82\x12\x70\x78\xd2\x25\xf7\xee\x11\xf1\xbb\x0f\x3a\xc1\xc3\x49\xa7\x3d\xbc\x68\x73\xd7\x25\xc3\x2e\x79\x42\x5a\xb4\x98\xb1\xdd\x03\x02\x93\x3e\xbd\x7c\x19\xe4\xb3\x16\x19\xd9\xc9\x5c\xa3\xdb\xd2\x52\x02\x74\xed\x7d\x34\x4d\x68\x96\x57\xf4\xf0\x96\xfb\x27\x1a\x2c\xe9\xa6\xca\x75\x7a\x9b\x17\xc1\x27\x9a\xbd\x3b\x3c\xa8\xef\xea\xf5\x7a\x8a\x3a\xfa\x5e\xb6\xf5\x3a\xc8\x0b\x9a\x25\x69\x48\x71\x4f\x55\xb6\x8d\xcc\x17\x51\x12\xc4\x51\x71\xf9\xfb\x61\x53\xb6\x58\x82\x4e\x9d\xed\xe0\x13\x45\xff\x7a\x91\xa5\xf3\xa7\xbf\x03\x9d\xb6\x45\xd7\x50\x50\xa9\xa7\x97\xd0\x30\xeb\xfc\x5e\x12\x1e\xb0\x72\x2a\x96\x9b\x17\x92\x8f\x43\xc1\xea\xf1\x2c\x93\x71\x4c\x7f\xa7\x01\x1c\xb1\xb6\x6a\xba\x8e\x61\x4a\x3b\x2d\xe7\x09\x8d\x73\x3f\x5d\x26\x8d\x2e\x19\x6f\x61\x1c\xde\xb6\x39\x29\xe1\xa1\x94\x80\xf1\x51\x39\x53\xf0\x3b\xf6\xff\x48\x35\x88\x26\xc3\x99\x04\x0c\x60\xf4\x59\x75\xef\x79\x31\xbb\xed\xe3\x61\xe3\xa3\xe1\x2d\x9d\x0c\x21\xfc\x73\xf9\xc9\x90\x2b\xbe\xf8\x1e\x1e\x51\x6f\x8f\x16\xb8\x33\x8b\x9a\x7e\x2c\xae\xd1\x05\x64\xe1\xc0\xf7\x56\xee\xfd\x84\x60\xff\xec\x07\x4f\xf7\xde\x58\xa1\xe8\xc4\x8e\xca\x75\x72\xfc\xf9\xb4\xd0\xcc\x5d\xad\xad\xf1\xde\xf5\xb9\x5d\x9c\x7a\x49\xf5\xbc\x98\x69\x5d\x60\x8f\xb4\x71\xe0\xee\x76\x4f\x0c\x73\x4a\x8b\x51\x89\xc6\x5b\x7a\xaa\xed\xe3\x82\x62\x24\x3d\xa1\xa5\x35\x0a\x9f\x05\xb1\x11\x63\xae\x6f\x85\x4d\x3f\x0b\x62\xc7\x15\x8d\x4a\xbb\x5a\x03\xf4\xac\x34\x14\xe1\xe5\xf1\x3a\x83\x11\x45\xaf\x33\x1c\x51\xb4\xe1\x80\x9a\x68\x22\x18\x77\x09\x62\xb0\xdb\xad\x3d\x37\x0b\x40\xf7\xec\x2c\xd9\x94\x93\xaf\x0e\xd0\xc8\x96\xd7\xb8\xc0\x1d\x91\x63\x2d\x4e\xf3\xcb\x5d\xe1\x44\xf5\x85\xbe\xcb\xb5\x21\x70\xdc\x7b\xce\x4f\x14\x30\x0a\x1c\x6a\xdd\x62\x8e\x70\x35\x3c\x4f\x79\x2c\x52\x40\x25\x4a\x93\x34\x0b\xa6\x74\xaf\x68\xa2\x37\x11\xa0\xa5\x38\xf2\x41\x28\x95\x46\x05\x96\xf8\xba\xe3\x1c\xbb\x48\x41\xaf\xb0\x0a\x5a\xbc\x03\x13\xae\x3d\x6b\xc6\xc4\xa0\x4a\x87\x63\x65\xfe\xfe\xf3\xed\x1d\x98\x58\x26\x07\xc9\x24\xad\x1f\x1f\x02\x2e\x1d\xa6\x1f\xe6\x0f\x32\x5a\xc9\xe3\xea\x56\x2f\x67\xbe\xd6\x08\xd5\xf1\xe8\x66\xc3\xf2\xf5\xb6\xe7\x30\x34\x6d\xeb\xcd\x58\x15\xb9\x5a\x6d\xb5\x82\x3c\x5d\xb9\x52\xf1\x09\xc6\x8f\x10\x0b\x1d\x02\x56\x61\x05\xe1\x04\x9d\xcb\xec\x38\x23\x9b\x32\xe1\x5a\x68\x51\x83\x6e\x38\x64\xd1\x91\x3a\x1e\x25\x4e\x44\x4d\x78\x94\x00\x75\x68\xc1\x38\xe1\xb9\xf4\xb0\x59\x45\x0f\x52\x88\xb1\x76\x2e\x62\xec\x4e\xd8\xe6\xa6\x64\x3d\xf0\x0a\x46\xa6\x3b\x83\xa6\x84\x82\xaf\x10\xdf\xd1\x20\x2e\x27\x12\x79\x2e\x6b\x44\x25\x12\xd8\x47\x26\xf8\xc4\xf9\x0d\xe8\x04\x8f\xf8\x20\x29\xbc\x03\x06\x19\xbc\x9e\x2e\x00\xcc\xa1\x09\x75\xaa\xfb\x1a\xfc\x01\x6d\x67\x37\x60\x05\xbd\xb5\x92\xdd\x6d\xbe\x88\xe2\x52\x4e\x60\x6e\x71\x02\xb4\x62\x9f\x73\x21\x24\x1e\x86\xe5\x64\x66\x9f\xd9\x1a\x72\x69\xbb\x98\xd3\xad\xaa\x63\xeb\x8a\x0b\x77\x15\x4a\xf4\xcc\x8d\x9c\xc2\x67\x74\x1c\xcd\xab\x56\x9c\x3e\x09\x36\x44\x82\x2e\x50\x42\x94\x7f\xdc\x01\x9b\x07\xa8\x9a\xc1\x96\x47\xcb\x2f\x51\xc3\xc0\x19\xbb\x72\xd0\xf5\x2b\x08\x55\x58\xbd\xb1\x7c\xf0\x68\xa9\x56\x1a\x93\x2a\xe5\x0c\xae\x4c\x01\xf6\x47\xe2\x34\xd7\xc1\xd3\x3b\x3a\xa6\xd1\xa2\x01\x99\xbb\x65\x9a\x10\x80\x0b\x7a\x53\x0a\x10\x35\x36\x1e\x60\xc3\x55\x5c\xcb\xc5\x3c\x83\xb3\x01\x9b\x50\x00\x3f\x1a\xdd\xd2\x21\xb1\x6c\x79\x13\x69\x6d\x20\xad\xf5\xde\x05\xe7\xcd\x97\xb9\x5b\xc0\x8f\x8c\x4a\xb8\x26\xdc\x8d\xe1\xc2\x73\x4a\x60\xf5\xae\xd6\xdb\x46\x5d\xbd\x7e\x3f\xed\xd9\xf2\xad\x33\xdf\x38\xa2\x69\xb2\xc2\x38\x4c\xe8\x92\x71\x94\x02\x7d\xe5\x71\x34\xe8\x7c\x79\x8f\x6f\xfd\x14\x5a\x42\x38\xc2\xc4\xb7\xaa\xa3\x0c\xc4\xdf\x51\x2b\xe7\x3a\x1d\x65\xfb\xc1\xad\x9d\x95\x69\x5e\x44\xf3\xa0\xa0\x3f\x07\x75\x32\x21\x82\xf4\x0f\xcd\x0f\x70\x1d\x8a\x31\x46\x78\x23\xc1\x63\xcc\x65\xd4\xf7\x69\x1c\x85\xa5\x47\x1b\x3d\x6d\x1c\xba\x9f\x0b\xf0\x92\x29\x34\xeb\xf4\x8d\xb5\xb4\x23\xaf\x5e\xbd\x6a\xd8\x87\xb8\x94\x82\x54\x4d\x2b\xb5\xfc\x9e\x66\x0b\x5a\xbb\x45\x29\x0c\x70\xe8\x6a\x04\x38\x30\x15\xbd\xc8\x97\xa7\xf3\xa8\xf8\x25\xcd\xea\x24\x25\x0d\x58\xb2\xd2\x7d\xf9\xd5\x06\x50\x0d\x5a\x15\x50\xa5\xdb\x71\x49\x7b\xfe\x63\xce\x7e\x90\x84\xfc\x91\x7f\x11\x14\xcb\x5a\xad\x8b\x05\x6e\x9d\xa8\xd5\x69\xab\x04\xca\xe1\x20\xb7\xa0\x6e\x7b\xbe\x48\xc7\xb3\xff\x8f\xbd\x77\x5d\x6f\xe3\x56\x12\x45\x7f\xdb\x4f\x81\x78\x9f\x15\x91\x31\x4d\xf1\x2e\x99\xb6\x32\x23\x53\x92\xa5\xb1\x65\x69\x4b\x72\x92\xb5\xf5\x29\xfe\x9a\x24\x28\xb6\x4d\x76\x73\xba\x9b\xba\x24\xd6\x7e\x9f\xf3\x1c\xe7\xc5\xce\x87\xc2\xfd\xd6\x6c\xea\xe2\x38\x19\x69\xcd\xc4\xec\x6e\xa0\x50\x00\x0a\x85\x42\xa1\x2e\x5e\xde\xe1\xea\x69\x91\x03\xa5\x28\xeb\x3f\x51\xba\x8a\xdc\x86\x81\xf8\x3b\x60\xa5\xc2\x95\x16\x6b\x52\x5b\x5f\x51\xdf\x09\xed\xb4\xf6\xb6\x17\x0f\xf5\x62\x8a\x3a\x54\x7b\x0f\x7c\xd8\x7e\xc3\x34\x58\x46\x4b\x4c\xd7\x64\x17\xe7\x2a\x15\x1d\x07\x31\x5c\xee\xd7\x94\x52\xb4\x6f\x70\x80\x34\x3a\xc2\x4e\xf1\x76\xa3\xa6\x0c\x6a\x97\x90\xe7\x51\xed\x9b\x52\xd1\xf7\x5e\x9c\x6e\x7c\x05\x98\x00\xee\xfb\x6c\x34\xaa\xfb\x05\x29\x3b\x91\x7c\x69\xcb\x91\xca\x37\x5d\xe0\xd1\x2b\x79\x6b\x28\xcd\xeb\x5b\x82\xf5\xe1\xfd\xfb\xf7\x76\x61\xca\x3e\x15\x90\x82\xb3\x69\x9d\x26\x2f\xe0\x99\x19\x4a\xd2\xd4\xae\xe2\xd6\x34\x2f\xd5\x83\xa4\x6d\xb2\x36\xc5\xf5\x9d\xae\x8a\x14\x9c\x3f\x8c\xfa\x41\xaa\x6a\xbb\x18\x02\xb0\xc4\x18\xe3\x67\x65\x44\x91\x9b\x72\x65\x89\x36\xa6\x61\xa4\x1b\xc9\x5a\x2d\xb0\x12\xb7\x84\x3f\x0e\xd2\x71\x12\x64\xb9\x7d\xf0\x94\x29\x24\x5a\x2c\x8f\x11\x37\xf2\xca\x41\xc8\x5d\x64\xf1\x61\x95\x59\x95\xe9\x27\xd4\xe5\x31\x3c\x0f\xd2\xc3\x24\x1c\xe4\x8e\x99\xa7\xcc\xad\x6f\x13\x97\xc7\x92\x65\x2f\x4c\xf3\xb0\x14\x65\x6e\xd9\x46\x5f\xb1\xc5\xc8\x69\xc6\x5f\xec\x81\x68\x88\xa7\x76\xfa\x85\x9a\xec\xe6\xe1\x66\x16\x55\x5a\x54\x59\x88\x76\x7f\x5f\x1d\x48\x73\x48\xc5\x36\xa6\x1f\x6a\x8e\x8f\xc1\x20\x8b\x13\x2e\x3f\x73\x03\x4a\xf0\x46\xaa\x20\x52\x56\xdb\x53\x59\x69\x57\x63\x23\x6e\x30\x69\x45\xb4\xa8\x28\x5e\xfb\xb4\x54\x2f\xc1\x60\xf0\x0c\x3e\xe8\x3d\xc3\x2b\x4f\x49\x77\x48\x8d\x30\x25\x1c\x32\x14\x2b\x15\xa7\xc1\x4c\x85\x5b\x75\x56\x71\x36\x2e\x95\x2b\x36\xc9\xbe\x8f\xcf\x15\xc9\xa8\x18\x4a\xae\x8e\x4a\x7b\xce\xfc\x4c\x3c\x7c\xf4\x4b\xac\x42\xf5\x7c\x12\xf7\x83\x49\x95\x0c\x6a\x35\xb0\x5f\xb3\xd4\xa9\xae\x26\xc3\x41\x30\xfb\x70\xdb\x66\x49\x65\xab\x51\xfa\x32\xaf\x49\xc5\xb8\x55\x36\x68\x7a\x50\xaa\xa9\x29\x79\x85\x92\x7b\x7a\x16\x05\xb5\xdc\xce\xc6\xd2\x2d\xc0\xb0\xef\x7d\xd6\xad\xaf\x57\x9e\x59\x76\xc6\xcc\xcf\x4d\x1a\xf8\x3e\xeb\x36\xda\xf0\x82\xce\xe9\xb3\x6e\xe3\x25\x7d\x14\xb4\xf0\xac\xdb\xa4\x55\xc2\x7e\x10\x3d\xeb\x36\x9b\x15\xdd\x0b\x01\x1e\xd9\x20\x3d\xeb\xb6\x5a\xf0\xcc\xad\x91\x9f\x75\x5b\x14\x3c\xe3\xec\xcf\xba\x2d\x8a\x16\xb7\x1a\x7a\xd6\x6d\x91\x06\xb9\x2d\xf1\xb3\x6e\xab\x79\x73\x56\x69\xbe\x7c\x74\x6b\x78\x74\x6b\xf8\x67\xbb\x35\xf8\x7c\x1a\xee\xec\x7a\x57\xdc\xdb\xa0\x80\x2b\x01\x94\xfb\x80\xb3\x87\xf4\xd4\x83\xb7\x8b\x6d\x1f\xa5\x8f\xde\x6d\x8c\x1f\x0b\x78\xe6\xad\xae\xae\xca\xd0\x76\xae\x70\x79\x2c\xef\x33\x61\xf1\x00\x0e\x67\x63\x14\xcc\x42\x05\xf7\x07\x3a\x90\x4c\xc2\x34\xc3\x79\xe7\x85\x08\x67\x9f\x64\xa1\xdb\x0a\x57\x18\x27\xe6\x05\x8b\xd5\x8a\xaf\xd0\x12\x02\x9f\x2a\x7e\x59\x9b\xda\x07\x9c\x39\x36\x35\x7d\xf3\x52\x77\x97\x9b\xb3\x4a\xab\xf6\xb8\x5b\x3c\xee\x16\xff\xec\xdd\xe2\x3b\x75\x82\xbb\x3f\x7f\xb5\x82\xee\x74\xd2\x27\xe0\x10\x27\x69\x1c\x05\x93\x47\xc7\x80\x87\x76\x0c\xb8\x29\x66\x2a\x1e\xe1\x4b\x69\x7f\x9e\xa7\x00\x97\x05\x6d\xed\xf7\x8c\xcd\xea\x27\x67\xa1\x3b\x5c\x71\x87\x53\xb2\x11\x1c\x05\x97\xef\xf0\xa2\xab\x2f\xb5\xe8\x4a\xe5\xe9\x93\x27\x26\x6e\x56\x81\x1c\x07\xf7\xe2\x57\xb9\x76\x3b\xe2\x83\x62\x01\xfe\xe4\x49\x41\x03\x87\xc2\x77\xb8\x78\x70\x84\x07\xf1\x05\x8d\x31\x99\x77\xe9\xc9\xcb\x39\x71\xd5\xbf\xe6\x0c\xc8\x3c\x9a\xc4\x83\x2f\xc5\x28\x45\x2b\x9b\x43\x2c\xbe\x72\x45\x2c\xe7\x8b\x8d\x9b\x77\xf4\xee\xd9\x74\x42\xce\xfd\x42\xfb\x89\x65\xee\xc9\x5d\x76\x07\xde\x2e\x15\x9f\x9f\x62\xb3\x93\x3f\x37\xcb\xdc\x65\x99\x73\x63\x20\xef\x92\xac\x59\xc3\x4a\x23\xca\xe2\x95\x6f\x35\x0a\x52\x6e\x4f\x38\x55\xfb\x6e\x3b\xbc\x97\x22\x0a\x38\x55\xde\x7d\xb8\xf3\xc1\xe6\x02\xb5\xb0\x9c\x0e\xb5\xb0\x47\x2c\xb7\xe5\x72\xbe\xdd\x4a\xe1\xdc\xa1\x22\x32\xb4\x42\xa6\x9c\x5e\x7f\x94\xd3\x1f\xe5\xf4\x7f\xb6\x9c\xce\x84\xf4\x74\xec\xd1\xea\x2c\x10\xbf\x71\x82\xe7\x53\x02\xfa\xe7\x05\x4a\xa0\x41\x9c\xe0\x6a\x18\xeb\x72\xfa\x5a\xe1\xf8\x4b\x05\xe3\x35\x2c\x0a\xfb\x00\x85\x8e\xc7\xe3\x07\xd7\x0e\x7d\x3f\xf2\x38\xe1\x8e\xc7\x63\xed\x76\x03\x5f\xb2\xdc\x15\x3b\xdf\xe2\x42\x27\x1d\x2f\xbe\xd0\x49\xc7\x70\xa1\x43\x05\x97\x65\xee\x6d\xf2\xe4\x7c\xff\xe6\x64\x89\x07\xca\xd6\x74\xe1\xbc\xa9\x63\x22\x42\x3a\x1e\x7f\x72\x17\xd0\xad\x8a\x90\x43\x97\x95\xd7\x68\xa8\x3b\xe2\x19\x2d\x3a\xbe\xde\xad\xb9\x14\x67\xfb\xc1\x15\x23\x82\xe3\xf0\x0f\xf3\x72\x58\x69\x7b\x51\x51\xdd\x6c\xec\x36\x88\x84\xd1\x61\xfc\x6b\x3e\x02\xae\x22\x77\x6b\x78\x1a\x24\x5f\x4e\x92\x79\x9a\xe1\xe1\x21\xb6\x2e\x83\x95\xe6\xf3\x0b\xde\x0d\x89\x08\x13\x99\xee\x30\x08\x73\xda\xf7\x96\xb9\x1b\x05\x04\xc3\xe1\x61\x12\x5e\x04\x19\xa6\x47\x42\x4f\xeb\x79\xc5\xee\xd6\x77\x9a\x3b\x74\x61\xf7\xf3\x8a\xdd\x0d\x81\x71\x90\x2e\x6c\xdd\x5b\xe6\x6e\x4d\x9f\xe3\x8c\x6e\xe8\xb9\x63\x9f\x53\xea\xee\xcd\x17\x98\xfb\xbc\x62\x77\xa6\xfb\xe3\xeb\x69\x6e\xe3\xbe\x22\x77\xa6\xfa\x45\x0d\xfb\x8a\xdc\x75\xc8\x89\x1c\x97\x61\x0a\x7a\x27\x89\xa7\x87\x41\x9a\x5e\xc6\xc9\x30\x6f\xfc\x0b\xd6\xb9\xf3\x3a\x58\x34\x26\xbe\x22\x77\x26\xc3\x45\x0d\xfb\x8a\xdc\x07\xeb\x59\xd4\x76\x4e\x29\x77\xf3\xe2\x61\x75\x15\xa5\xf3\x3e\xdc\xbc\x61\x48\x4d\x35\x8f\xe4\xf3\x34\x4c\xd3\x30\x3a\x7f\x5a\x18\xdb\x59\x9c\x9a\x57\x57\x0a\x96\x8e\xaf\x0e\x3d\x05\xca\xd7\x3b\xa2\xc5\xb7\x5c\xc7\xe3\xb1\x92\x87\xd4\xb0\xbd\xd0\x4e\xd1\x86\x65\x44\xab\xf1\x78\x86\x7e\x3c\x43\xff\xb3\xcf\xd0\xf2\xae\xab\xff\xc7\x1f\xc6\x5d\xd7\xe6\x04\x5f\xa1\x37\x38\xc1\xe7\xe9\x1f\x41\xfa\x47\x88\x5e\x07\x13\x7c\xf5\x9f\x49\x36\x4a\xab\xe3\xb9\x7e\x1c\xee\xb0\xa0\xe8\x47\x78\x84\x13\x1c\x0d\x70\x17\x91\xf6\xd3\xee\xea\xea\x79\x98\x8d\xe7\xfd\xea\x20\x9e\xae\xfe\x16\x46\x3b\x61\x74\x90\x9c\xaf\xfe\xb6\x75\x18\x1f\xf7\xc6\x41\x18\xad\xf6\x27\x71\x7f\x35\xbd\x0c\x92\xe9\x6a\x18\x65\x38\x89\x82\xc9\x2a\xe9\x12\xbe\xca\xf8\xbf\xd5\xf3\xf8\x7f\xbd\x6f\x36\x1f\xf8\x6a\x4c\xde\x77\x1d\x13\x6c\xfe\xe1\x87\x6b\xf8\xf1\xb7\xb8\xec\xa2\x96\xaf\x38\xbb\x8c\x93\x2f\x47\x18\x22\xde\xe7\x29\xca\xcd\xe2\xb6\xb6\xbc\xff\xc7\x1f\x9f\x72\x4a\xdd\xc5\xb9\xf3\x3a\x1a\x6c\x47\x41\x7f\x82\x17\x61\xa9\x94\x74\x23\xe8\x2e\x70\x17\xdc\x2e\x83\x59\x41\xdc\x64\x49\x0f\x6e\xce\x02\x77\xc0\x6d\x18\x5f\x46\x2c\x99\x41\x1e\x62\xbc\x98\x1b\x2b\xc7\xd7\xe2\x3e\xcb\x1e\xc4\xe6\xb3\x02\x68\xd1\x42\x6e\xa4\xac\x6f\x77\x46\x29\xc1\x59\x12\xe2\x8b\x45\x61\x44\x78\x31\x37\x5a\x8e\xaf\x77\x21\xad\x8c\xec\x76\x0b\x88\x8a\x94\xf1\x90\x93\xf1\xe9\xce\x43\x74\x8e\x0b\xf8\xc4\xbb\x71\xd1\x3f\xdc\x61\x4c\x68\x12\xa8\x05\xa1\xd6\xdd\x38\xe8\x1f\xee\x3c\x1a\x2c\xef\x5b\x3e\x32\xb4\x90\x1b\x1f\xeb\x1b\x47\xa9\x55\x08\xa5\x9c\x5b\x5d\x4b\xc5\x69\xb2\x65\xe5\xf6\x4f\xf2\x43\xe5\xa5\x64\x44\xf2\x25\xe7\x03\xca\x8d\xe3\x4c\x7f\xe6\xd4\xaf\x00\x22\x24\x28\x1f\xcf\xb1\x72\x31\x39\x9b\x2b\x0f\x8a\x2c\xfe\xa0\xd7\x8c\xe3\xf0\xc2\xeb\x1b\x43\xe6\x04\xbe\x7b\xcf\x90\xf9\xb0\x1d\x4a\x59\x0d\x36\x7c\xf7\x1c\xaf\x1c\xe7\x2b\x22\x2c\xb9\x62\xe6\x3b\xef\x25\x9b\x8f\x67\xaa\xc7\x33\xd5\x3f\xfb\x4c\xc5\x0e\x54\xfc\x82\xe8\xdb\x26\x7b\xb9\x8d\x61\x35\xf7\x8e\x0a\x66\x21\x17\xc6\x69\xa6\xe0\x6c\x9c\x67\x81\x46\xaf\xcb\x72\xc3\x1b\xf3\xd2\xd9\xf5\x8c\xc8\x07\x2c\x94\xf1\xab\xa7\x0a\x03\x0f\xb3\xc1\xb8\x44\xbe\x9b\xb1\xea\x06\x41\x8a\xd1\x0a\xa1\xf8\x34\x5b\xe9\x6a\x9f\x60\xb2\x92\xf3\xb4\x9a\x8e\xc3\x51\x56\x32\xf2\x92\x21\x2b\xc7\x70\xcd\x2e\xc0\x58\x32\xb8\xaf\x45\xf8\x92\x79\x3b\xc3\x85\xec\x2b\x07\x1a\x33\x1c\x0d\xc3\xe8\xfc\xc1\xf1\x38\xa4\xed\xa8\x36\x44\x2e\xa4\x58\x0c\x5a\x1b\x1b\x03\x9c\x55\x99\xe6\x69\xbb\x51\xa4\x03\x51\x6a\xb1\x25\x21\x83\x66\xca\x08\x1a\x29\x38\x64\x27\x87\x54\x1d\x85\x51\x9a\x05\x93\x49\xa1\x96\x8d\xd2\x6e\x37\x7e\x7f\xa1\x1c\x3c\xce\x71\xf6\x3e\x3e\x2f\x10\x44\x80\x94\xf2\x86\x0f\xa0\x2d\x1a\x45\x72\x5a\x9d\xc5\x0b\x03\xb9\x90\x22\x0b\xda\xeb\x8d\x83\xe8\xdc\x1d\xb1\x60\x81\x8c\x25\xe6\x4b\x35\xc9\xd2\x46\x4f\x13\x84\x48\xc7\x94\x46\x62\x16\x0c\xf2\xec\x96\x8e\x1c\xe9\x78\x5c\x05\xd6\x68\xb1\x9b\x74\x6c\xb3\x1b\xbf\xf8\xb4\xe0\x96\xc6\x22\x03\x64\xdd\xd2\x68\x96\x04\xf7\xaa\xa6\xf7\x13\x23\x72\x69\xea\x1f\x0e\x11\x9b\x74\x91\x75\x4d\x41\x9b\x65\x38\x98\x45\xef\xd6\xbc\x41\xc6\xf7\xd0\xb6\x4a\x7a\x96\x24\x4a\x71\xc0\xd9\xb8\x4b\xfe\x43\x81\xa5\xe3\x71\x97\xfc\xa7\x42\xa5\x57\x57\x62\xa7\x56\xeb\x51\x26\x7d\x94\x49\xff\xe1\x32\xa9\x54\xf4\x73\x27\xeb\xdb\x38\xb6\x38\x04\x52\xea\x20\x7e\x84\xcf\xc9\x3c\x07\xc9\x66\x3f\xf4\xe4\x37\x4a\x57\xdf\xea\x45\xab\x9f\xd3\x58\xe4\x10\x0a\x07\xc1\x4c\x05\xe2\x83\xb1\xd7\xdb\x3c\xb4\x21\x28\x98\x30\x4f\x74\x66\xbe\x8c\x36\xd0\x4a\xed\x6a\xd0\x19\xbe\x1c\x36\x06\xc3\x56\xeb\x65\xb0\xd6\x6e\x0d\x5a\x2f\x5b\x8d\x4e\x0b\xd7\xd7\x6b\x2f\x07\xed\x1a\x6e\xb6\x86\x9d\x56\xbb\xd3\xe8\xaf\x48\x5c\x5c\x60\x82\x7a\x50\xaf\xd7\xfb\x83\xda\x5a\x6b\xf0\x72\x30\x0a\xd6\xd6\xeb\xa3\xda\xa0\xb9\x8e\x3b\xcd\xfe\xb0\x5d\x1f\xbc\xac\xf7\xd7\x83\x51\xad\xb6\xe2\x67\x4e\x14\xc7\xae\x22\xea\x06\xfd\xb0\xeb\x18\x44\xc9\x0a\x99\x1f\x7c\xd7\xd9\x3f\xba\xd5\xd3\xc2\x04\x6d\x0b\xb2\x39\xae\x0e\xb8\x76\x77\x29\x54\x8d\x63\xe6\xcf\xe2\xb3\x6e\xbd\xf2\x6c\xc1\x3c\x3d\xeb\x36\x08\xb3\x6d\x3f\x32\xdb\x47\x66\xfb\xcf\x66\xb6\x92\xd7\x72\xed\x97\xc1\x6c\xf3\x2c\x93\x47\x49\xfc\x07\x9e\x06\x51\x75\x88\x7f\xbe\x17\x06\xed\xf2\x51\x37\x1c\xd4\xcd\x1b\x52\xcb\xa6\x56\xbb\x06\x65\x79\xe2\xd9\x27\x78\x54\x32\xd7\x50\x4d\xa2\xf2\x9d\xbe\xd0\x72\xdb\x18\x25\x52\xb3\x84\xe1\xe0\xac\x14\x35\xbe\x28\x75\x74\x05\xb4\x52\x45\xff\xa0\xd4\xb0\x6e\x73\xa3\xf9\x64\x42\x45\x4b\x3e\x16\x6a\x16\x6d\xf3\x76\x53\x1b\xa7\x64\xaa\x0d\x91\x05\x3a\x99\x2e\x4c\x4a\xce\x52\x70\x03\xba\xa0\x55\x20\xb4\x4b\x05\x55\x23\xe3\x38\x2d\xb9\x47\x0a\xaa\x59\xc7\x21\xef\xf7\x8d\x96\x70\x5c\xbc\x5a\x75\x75\x49\x81\x63\x6a\x70\x5c\xb1\x5b\x8c\x11\xfe\x0f\xd7\x5b\x5a\xb7\x4b\xf0\x2f\xda\xe1\x58\xcb\x31\xbf\xa0\xd3\x34\xbc\xbe\xda\x6b\x96\x53\xdd\x95\x67\x3d\xbf\xdf\x14\x94\x3e\x8b\x5a\xae\x7c\xb5\xef\x26\x45\xfe\xf8\x23\x4b\xac\x8f\x7e\xd8\xa0\x84\x63\xbc\x22\x1b\xca\x28\x8c\xf0\x90\x8f\x93\x01\x41\xb4\xd5\x65\xb5\x3c\xc3\x05\x19\xe8\xb3\x18\xe1\x2b\x1a\x2c\x89\x5b\x98\xa3\x51\x12\x4f\xe5\x69\x5b\x24\x76\xaf\xa2\x7d\xb2\xb1\x85\x38\x65\x94\x04\xc3\x64\x8c\x25\x03\xc6\x0d\xd2\x6d\x22\x92\xf0\xb4\x71\xdd\x61\x23\xf5\xf5\xc3\x7c\x32\xb9\x51\xac\xdd\xc3\x11\xc2\x57\x61\x0a\xc5\x9d\x43\x6e\xb4\xe8\x55\x18\x86\x23\x99\x0b\x8d\xb7\x46\xb3\xa1\x81\x9e\x6d\x82\xa3\xf3\x6c\x8c\x5e\xa0\xfa\x59\xd9\x91\xd7\x09\xca\xcc\xe2\x59\xa9\xfc\x0a\xad\xae\xf2\x8b\x2f\xc2\xff\x61\x3d\xc1\x68\xfd\xa0\x0a\x37\xfa\x70\x53\x03\x07\x89\x59\x16\xbb\x49\x51\x37\x84\xf0\x11\x23\x7b\xc5\x7b\xe1\xa5\x46\x1d\x9a\xce\x7d\xfb\x9f\xb5\x54\xd5\xa4\x8e\x10\x25\x11\x4f\x75\x05\xe4\xd5\x9f\x87\x93\xe1\x5b\x9c\x95\x94\xe3\x39\x8e\xe6\x53\x9c\x04\xfd\x09\xee\xa2\x2c\x99\x63\x5b\xf7\x17\x4c\xe1\xc6\x4a\xb0\xf5\x6a\x3a\x9b\x84\x59\x69\xa5\xba\xa2\x04\xdc\x64\xfc\x1e\x0a\x83\xf6\x96\x4f\x14\xbc\xe1\x73\xf2\x33\xaa\xab\x33\x12\xf7\x3f\x9f\xf2\x1a\x67\x84\x1b\x6b\xcf\x5f\xbf\xa2\x3f\x6f\x5e\xa9\x85\xcd\x22\xaf\x34\x95\x98\x68\xbe\x7e\xc6\xd3\x6a\xc1\x3f\xee\x3c\x61\x71\xff\x73\x05\xca\x57\xe8\x90\xb1\xbe\x10\xf8\x41\x7a\x1d\x0d\xde\xc2\x7e\x43\x44\x5e\xe8\x42\xf9\x8c\x0f\x01\x0c\xe2\x26\x2b\x52\x52\xfc\x34\x8c\x6a\xda\x24\x01\x08\x9d\x65\xc0\xf5\x32\x7a\x0e\x38\x54\x07\xe3\x20\xd9\xcc\x4a\xb5\x72\x35\x8b\x3f\xce\x66\x38\xe9\x05\x29\x2e\x95\xf9\xe7\x94\xc8\x0f\xa5\x7a\xd9\xbb\xf1\xf0\x99\xf5\x67\x30\x97\x1b\xb7\x4c\xc7\xce\x43\xa2\xf1\x1a\xe7\xa4\x43\xf6\x8a\x11\x02\x8a\xca\x13\x4b\xe2\xad\xbe\x8f\x41\x56\x3a\x43\xd3\x43\x97\x44\x57\x02\xa2\xdb\xbd\xa2\xb2\xe1\x06\x3f\xf9\x1d\xe4\xa3\xbe\x5c\x2f\xe5\x65\xbf\x3f\x0a\x18\x92\x76\x4e\xce\x0e\x41\xcb\xcb\xf6\x4a\x4d\xa8\x84\x93\xa4\x82\xf4\xad\x83\xff\x71\x5c\x68\x19\xf7\x60\xb3\x9a\xca\xe5\xc1\x8d\x1c\x32\xb6\xc8\x39\xde\x9c\x50\xd9\x23\xcd\x03\xc8\x32\x00\x2a\xb3\x7a\x8e\x7d\xdb\x89\xdc\x7d\x07\x09\x26\xb2\xe2\x6c\x9e\x60\xf4\x5f\xc7\x07\x1f\x8e\x0e\x7b\x88\xb7\x72\x39\x0e\x07\x63\x38\x3c\xf1\x1d\x28\x8c\x50\x1f\x94\xb6\xac\x88\xc1\x11\xe5\x5b\xc1\xf7\xaa\xd5\xea\x0d\x53\xe1\xb9\xf6\x66\x44\x8e\x84\xc9\x6c\xa0\x54\x75\x72\x47\xd9\x71\x0f\x59\x04\xd7\xcc\x42\xc7\xb4\x9b\xeb\xaa\xf2\xa8\xad\x25\x3f\x3d\xd3\xf5\xeb\x64\x9a\x58\x15\x63\xb3\x2a\xc1\x9e\xa8\x8a\x82\x64\xc9\x56\x49\xa5\x92\xd8\x27\xcb\x65\x75\xca\x18\x56\x6c\xa2\xf9\xac\xa9\xd3\xee\x9b\x3a\x56\xd3\xa3\xe1\xe4\x03\xa4\x1c\xcc\x8d\xa0\x3d\xe4\x88\xdd\x79\x3c\x62\x3f\x1e\xb1\xff\xd9\x47\x6c\x45\x9f\xc9\x38\xc4\x94\xb1\x74\xfd\xa4\xfd\x5f\x78\x34\x4a\xf0\x35\xfa\x35\x9c\x0c\xbe\x60\xf4\xfa\x33\x1e\x8d\x7c\xe1\x7a\x96\x8a\xed\xb3\x1f\x24\xe4\x08\x7f\x10\x44\x03\x1c\x40\x59\x57\x54\x9f\x5b\x04\x02\x62\x55\xde\x06\x17\xe8\xd7\x38\x1e\xa2\xd7\xe7\xde\x43\x7e\x4b\x1e\xf2\xff\x8b\x71\x53\xcd\x7b\x98\xb1\xd8\xbc\xd4\xf8\x8e\x48\x75\x66\x36\x7b\x57\x2a\x7b\x9c\x24\xb1\x11\x3d\x68\x95\xbe\xa3\x46\x08\x74\xdb\xd9\xcb\x56\x52\xb2\x31\xce\xe2\x28\x0d\xfb\x13\x4a\x60\xb3\x00\xbc\x48\xd0\x94\x5d\xfa\x90\xbd\x68\x96\xc4\x17\xe1\x10\x27\xa9\xa8\x15\x4c\xd2\xd8\xae\x1a\x4f\x26\xa4\x2a\xa1\x36\xee\xc0\x8d\xa2\x78\x48\xbf\x86\xd1\x20\x9e\xaa\x90\x09\x30\x96\x95\x82\xde\xb9\x66\xe1\x14\x93\xc5\x16\xa6\xa8\x8e\x52\x3c\x88\xa3\x21\xec\x8e\x61\x74\x3e\xc1\x59\x1c\xc1\x70\x92\xee\xe5\x1c\xf4\x39\xaa\xda\x71\x9f\xbf\x44\x1b\xa2\x2b\x8a\x9e\x81\xb4\x0d\x1a\xe0\x1b\xe5\x25\xc7\x45\xd5\x3a\x78\x0f\x7f\x44\x42\x19\x27\x71\x14\xcf\xd3\xc9\x35\xc4\xc1\xf0\xec\xc3\xe4\x93\xe3\x3c\x82\x86\x41\x16\x78\x4f\xc8\x7a\x6f\x35\x95\x47\x34\xd4\x3a\x4f\xc0\xa8\x27\xb5\x1f\xb4\xde\x6b\x69\x72\xe3\x28\x8d\xc9\xd6\x45\x88\xa2\x44\x49\xa3\xba\x17\x5d\x04\x93\x70\x78\xc8\xca\x97\x54\x99\x87\xbb\x61\xc3\x60\x28\x12\xbe\xbe\xc7\x33\x32\xaf\x66\xf1\x21\x7d\x07\x28\x55\x69\xef\x2b\xd0\x4d\x66\x6d\xa1\x9c\x5f\xd8\xa9\x7c\x43\x9f\x2b\x2a\xcc\x32\xd0\xfc\xae\x1c\x3a\xc5\x1b\x09\xd3\x5f\x08\xba\x47\x94\x0a\xb1\x10\xd4\x94\x6e\x66\xe3\x24\xbe\x44\x7a\xf7\xcc\xf2\x5a\x77\x58\x37\xe9\xa7\x6a\xa1\x93\x7f\xb0\xd4\xec\x83\x34\x9b\x4b\x02\xe6\xb9\x54\x48\x3f\x8b\x89\x01\x80\x5b\x14\xa1\x44\xcf\x2d\x44\x1b\x3c\x09\xb3\x22\x1b\xe7\x51\xc7\xfd\x10\x82\x3d\xf7\x54\xee\x67\x20\x0b\xc8\xf3\xa4\x53\x38\x49\x3c\x29\x35\xd5\xde\x94\x4d\x7b\x1b\xc4\x33\x56\xdd\x86\xc6\x16\x0f\x99\x55\x5b\x6d\xdf\x12\x72\x59\xde\x70\x8d\x04\xcd\xe8\x9c\xfe\x63\x83\x8b\x1a\xf3\x4e\x06\xa4\xc0\x1b\xf2\xdd\xa1\x64\xa2\xf5\xee\x83\x30\xa1\x85\xef\x8c\x30\x01\x27\x95\x3a\x39\x93\xb9\x1d\x29\xa6\xf7\x40\x8b\x3a\x0d\x72\x3d\x1b\xcc\x46\x89\xb7\x72\x27\xd2\x4b\x17\xd1\x9e\xd6\x21\x41\x74\x68\xc1\xf6\x87\x33\xb1\xaf\x12\x69\x93\x9f\x09\x99\xc8\x67\x51\x5c\xc6\xa7\xca\xad\x9a\xcb\xa5\x25\x51\x57\xdf\xf5\xbd\xdb\xfd\xa2\x9d\x3b\x23\x47\x2a\x26\xb8\x98\x88\x92\x6f\x87\xe2\xd3\x42\x8e\x4d\x83\xff\xdf\x00\xb4\xbd\xe1\xc2\x25\xe3\xf8\x2a\xec\x92\x38\x26\x59\x3c\x8c\xd1\x60\x82\x83\x68\x3e\x43\x11\xc0\x27\x03\x2c\x8e\xed\x79\x43\xa5\x60\xef\x58\x79\x14\x49\x35\x22\x8a\x68\x5c\x1f\x4b\x22\x1c\x9d\xd2\xd2\x67\x44\x48\x22\xd5\xbb\x88\x02\x09\x87\x5d\x0b\x50\xd7\x05\xb2\x2b\x7f\x82\x5e\x17\x31\x5f\x66\x7d\xf4\x35\x06\xc0\x04\x30\x7d\x37\x67\x08\x95\xc4\x0a\x5f\x30\xb9\xf1\x4c\x08\xa5\x44\x04\x65\x76\xb4\x70\xba\x39\x0f\xc9\x91\x2e\x34\x75\xc7\xa4\x8e\x63\xce\xad\xb9\xcd\x1d\x79\x01\x42\x27\x52\xa8\xcb\x3b\x44\x4d\xcb\x1c\x83\xfc\x4a\x19\x1e\x89\x3f\x1b\x9d\x12\xd3\xa8\x7e\xc1\xd7\x69\x49\xd6\x2d\x73\x2d\xef\xc6\xc6\x06\xaa\xa1\x1f\x7f\x44\xbe\x31\x24\xc4\x94\x9c\xd0\xf7\x25\xad\xd0\x2b\x7d\x9c\x4d\x01\x38\x67\xbc\xe5\xee\x93\x60\xc2\x0b\x88\xfc\xcf\x87\x7d\x8a\x07\xe3\x20\x0a\xd3\x29\x3f\x86\xe6\x33\x07\x00\x90\x3f\xbc\xb4\x0d\x75\x60\xbf\x60\x3c\x13\x09\x04\x78\x67\x57\x7f\xfa\x9c\x8e\xc3\x88\x34\x74\x35\x88\xa7\xb3\x09\xbe\x0a\xb3\xeb\x6e\x1b\x8e\x64\xa4\x00\x21\x88\x12\xd9\x1c\xbe\xe0\x6b\xaa\x29\x10\xa3\xa9\x8c\xd7\xea\x2a\x4a\xf0\x34\xbe\xc0\x28\x98\x4c\xa0\x57\x69\x05\xe1\xab\x01\x9e\x65\x20\xf6\xb3\x57\x6a\xf9\x6c\x8c\xaf\x51\x84\xe9\x88\xf4\x31\xab\x3f\x24\x3d\x9e\x07\x93\xc9\x35\xea\x5f\xc3\x90\x91\xe1\x61\xb9\x00\x80\x66\x7e\x25\x1b\x52\x18\x9d\x97\xca\xca\x3e\x50\xfa\x41\xeb\x1d\xfa\xfa\x95\xe0\x5b\x0d\xa3\x21\xbe\x3a\x18\x95\xc0\x4f\x91\x10\xdb\xa7\x95\x32\x4c\xfe\x8b\xba\xb9\x41\x28\x14\xf6\x05\x5f\x9f\x55\xc5\x4a\x34\xed\xa1\x6d\x8a\x24\xe5\x2d\xdb\xe4\xbf\x31\x79\xc2\x29\x93\xcc\xfb\x80\x1a\xe7\xa2\x38\x2a\xc2\x13\xa8\x4d\x6d\x1e\x4d\x32\x93\x61\x5b\x05\xea\xa1\x42\xd4\x21\xe0\x1c\x9d\x49\x71\xa6\xf5\x9e\x00\x56\x54\x91\x15\x34\xa8\x6e\x9f\xec\x7e\x3a\x3c\x78\xff\x7e\xef\xc3\xdb\x4f\x27\x7b\xfb\xdb\x07\x1f\x4f\xd4\xe3\x51\x91\x19\xb0\x85\x2a\x4d\x62\x7a\x90\xa3\xa3\x2d\x93\x11\xbc\xb6\x82\x2c\x40\x1b\xe8\xf4\xec\x95\xfe\x7e\x0f\xfc\x8d\xf9\xeb\x62\x4b\x55\x00\xac\xce\xe6\xe9\xb8\x64\xd2\x3d\x13\xf1\xb4\xd2\x7b\xc3\x94\x16\xfe\x82\xaf\xcb\xd6\x18\x48\x80\x4b\x0c\x5e\x21\x71\x53\x40\x56\xd3\xe5\xae\xae\xa2\x69\x30\xd3\x98\x64\x08\x64\x0b\x0c\x05\x48\x8c\x90\xa6\x3e\x4c\xfb\xc1\x4c\x51\x5d\x28\x7a\x6d\xdd\x55\x9c\x0a\xae\xc0\x35\xca\x7f\x9a\x63\xb0\x1f\xcc\x4e\xa1\x5a\x08\x5b\x3c\x1f\x99\x53\x28\x7e\xa6\xb8\xa4\x8b\xc6\x35\xc7\x79\xb4\xb4\xcc\x1c\xeb\x52\xb3\x16\xdf\xe4\xe4\x60\xeb\xa0\xcb\x89\x0c\x4d\xe2\xf3\xff\x30\xa5\xea\xd8\x23\x57\xdf\x55\x92\x2e\xa0\x2c\x48\x9d\x47\x47\xf6\xad\x3a\x0d\x66\x25\x9f\xb1\x02\xff\x03\xfb\xc5\xa1\x1c\x65\x32\xf6\xec\xa8\x17\x0e\x55\xcf\x1b\x41\x11\x5f\x30\x4a\xe7\x09\xe8\x89\x39\xb3\x0a\x53\x94\x66\x21\xa1\x07\xca\xc9\xf1\x10\x05\x23\xf0\x10\x4a\x92\xf0\x22\x98\x18\x7b\xad\x06\x93\x0c\x08\xf8\xfd\xd3\xa5\x11\x0e\xcf\x4c\x14\x65\x97\xaa\x03\x69\x0f\xa0\xd7\x11\x5f\xbc\x1e\x33\x5c\x77\xa2\x7e\xba\x41\x78\xc2\xf4\xcc\x8e\x1a\xa3\x60\x92\x62\xf5\x96\x8d\xf9\x3d\x2d\x1c\x53\x56\xff\x87\x1f\x58\x9b\xe8\x16\x30\xc8\xbc\xc0\x8c\x2b\x8b\xd6\x73\xf8\x7f\x65\x8d\xe7\x0f\x50\xb3\xc0\x38\x16\x57\x0c\x20\x8d\xc2\x94\x5e\x42\x45\x7d\x94\x8c\xc5\xee\x1f\x26\x1d\x17\xbf\x9e\x01\xa9\x97\x9c\xbe\x94\x4b\x47\x66\x54\x0d\xfd\xc6\xcb\xc8\xbd\x64\xe7\xae\x60\x0a\xe9\x67\xdd\x06\xc4\xf6\x61\xca\xf0\x67\xdd\x26\xf8\xa1\xae\x15\xb9\x23\x63\x41\x37\x71\x96\x85\xd1\xb9\xdb\xb5\x17\x18\xd3\x50\xc9\x7d\x8c\x36\x84\xd3\xda\x2b\xab\x84\x0c\xf5\x2c\xec\x83\x7c\x51\x8b\x58\xa3\xac\xdf\x04\xe5\xf5\xc7\x6b\xbd\xc7\x6b\xbd\x7f\xf8\xb5\x1e\x0b\xe9\xcb\x4e\x2d\xb7\x09\xeb\xbb\xc8\x1c\xd6\x93\xfc\xc2\xc8\x7d\xb1\x8c\xe1\x2c\x5f\xd2\x75\x76\x38\xd8\x1c\x0e\x53\x18\x3a\xb1\xbb\x05\x11\xa8\xa5\x52\x34\xa7\xe2\x17\xf3\x7a\xab\x10\xe1\x2b\xcc\x20\x54\x1e\x82\xac\x00\x74\x53\xa5\xbb\xfd\xd3\xa7\xea\xf9\x80\x9d\xcf\x9e\x9a\x4a\x22\xb2\x6d\x3e\x65\xd7\x56\x4a\x39\x85\x57\xd1\x40\x3d\xdc\x97\x8e\x94\x8b\x23\xe6\x71\xa5\x71\x34\x26\x37\x91\xb1\x77\xa8\x1a\x7d\x42\x11\xdd\xb7\x79\x4f\x53\xc7\x66\xe1\xb2\xc7\xe1\x7f\xfa\xbe\x65\x6e\x4f\x3e\xdd\xa5\xb0\x10\xe4\x91\x88\x00\xe5\x1f\x7f\x04\xdc\xa9\x62\x2a\x8c\xce\x81\x1b\x97\x35\x88\xfc\xfa\x62\x51\x4e\x53\x0a\x51\x75\x53\xbe\x6d\x27\x85\x34\x34\x09\x52\x68\xe6\x38\x23\x93\xfd\xc3\xc6\x86\x35\xd0\xfc\xcf\x7a\xb1\xba\x4a\x73\xff\x6b\x24\x05\x4b\x2d\x4b\xe6\x44\x66\x4b\xd2\x0c\xa5\x31\xb5\x73\x9c\xcd\x80\x75\xc3\xd9\x39\x88\xae\x33\x72\xe0\xaf\xa0\x3e\x1e\x11\x06\x40\x97\x38\xbf\x42\x85\xd1\xa0\x4a\x46\xe3\x2f\x1c\x95\x7e\x70\x60\xfd\xe3\x8f\xc8\x35\xf2\x65\xab\x3e\xb2\xaf\x1b\x08\xaa\x0e\xff\x68\x6f\x67\x63\xca\x37\x23\x7c\x95\xa1\xde\xe1\x47\x34\xb8\x1e\x4c\x70\x45\x74\x13\x86\x5d\x6c\x36\xd0\x13\xe8\x32\xb3\x59\x9a\x25\xf1\x80\xf0\xac\x94\x8e\x8e\xd5\x8a\x72\x0c\x16\xcb\xc4\x35\x17\x8e\x8e\x30\xd2\x30\x4b\xdd\x54\x50\xad\x48\xff\x1c\xc3\x4a\x49\xc1\x27\x9a\x29\xc6\x60\x4f\x05\x00\xd3\x8c\x4d\xd1\xc5\x96\x6c\x3b\x28\x4f\xbe\x5f\xd3\x12\xea\xa6\x22\x85\xf0\xbd\x61\x45\xb2\x09\xf6\x5e\xd5\x21\x51\x9d\x01\x70\x16\xb2\x4e\xb8\x9d\xe4\x9e\x33\x2f\xa7\x37\xd9\x66\xbe\xc9\xbc\x21\xff\x21\x55\xd7\xb4\x47\xe4\x68\x45\x39\xf5\x9c\x72\xe1\xe7\xcf\x95\x72\x62\xbd\x2a\x27\x7d\xf8\x10\x0c\x87\xc2\xb6\x4b\x49\xfc\x29\xbe\x9b\xd3\xa3\x1c\x1c\x14\x16\xcb\x8d\xb7\xe0\xbd\x62\x2b\x4e\x05\x3a\x31\x12\xaa\xa5\xaf\x6c\x37\xd7\x62\x31\x1c\xc9\x57\xba\x56\x4a\xb2\x20\xd0\x2a\x18\xc8\x17\x42\x42\x9d\x45\xbf\x44\x6b\x11\x98\x50\x39\x97\x94\x39\x28\xe7\x8c\xb6\x53\xaa\x15\x08\xf9\x0d\xd8\x88\xac\xae\xa7\xbb\x20\xb2\xef\x63\x92\xd2\x47\xd9\xf7\x9f\x2e\xfb\x4a\x93\x36\x9e\xb1\xf7\xbe\x7c\x74\xf7\xfa\x41\xa4\x4b\xbb\x61\x3f\x10\xae\xb7\xf8\x8a\xaa\xab\xf3\x5c\x77\x8f\xa7\x41\x92\x6d\xb3\x82\xd2\xed\xd6\x7b\x35\x06\x6a\x25\x68\x96\xf7\xc5\xd0\x79\x2b\xaf\xc5\x25\xd8\x71\x96\x84\xd1\xf9\x0d\xb8\xb6\xb8\xde\x13\x69\xb9\x1f\x44\xea\xa7\x5f\x82\xc9\x1c\xdf\xa0\x0b\xf2\x0f\xbb\x0e\x21\x90\x47\x38\xc1\x0b\x6e\x48\x2b\xba\x79\x01\x44\xa9\x61\x38\xe9\x62\x71\x36\xae\x00\x46\x44\x5a\xaf\xd0\x96\xec\x2d\x0c\xd4\x6e\x74\x94\x21\xdd\x74\x3f\x88\x4a\x59\x5c\x66\xaa\x22\xd0\xe1\x90\xcf\x5c\xe5\x53\x72\x58\x11\x91\x7a\x90\x27\xa2\xb4\x12\x52\xf5\x0d\x85\xc8\xfc\x74\x57\x6c\xfd\x31\x83\xb8\x15\x26\x44\x16\x73\x39\xc4\xf0\x1e\x9d\xc4\xcc\xb3\x57\xed\x0e\x54\x67\xd0\x4b\x65\xbb\x6b\xbc\x3d\x21\xc7\x40\x37\x5c\x92\x2e\xb8\x48\x08\x4f\x69\x9c\x8d\xd5\x9c\xe0\xa5\x32\x34\xc2\xb0\x8d\xd2\x2c\xcc\xe6\x54\xe0\xb2\xcd\xbf\x86\x78\x16\xa7\x61\xa6\x62\xc9\xe0\x0a\xf4\x00\xcc\x60\x12\xe2\x28\x33\x2d\x31\x0a\x37\x6c\x99\x58\xf0\x5c\xe3\xf6\x08\x2e\x8b\x91\x3d\x7e\x5c\x05\x9f\x7b\x95\x2c\x48\x6f\x34\x8f\x86\x60\x13\x39\xc0\x49\x16\x84\x62\xfa\x3d\xcb\x47\x4c\xec\x72\xeb\xe8\xc1\x97\x90\xc0\xeb\x16\x6b\x89\x8d\x3c\x99\x4d\x23\xe5\x97\x22\xdb\x0a\xef\xf5\x2c\x96\x12\x2d\x01\xdd\xa5\x0d\x28\xb4\x39\x99\xe3\x2e\xfd\x87\x8b\xb9\x46\xb6\x77\xef\xac\xb0\xc9\x97\x93\x02\x81\xed\xc3\x01\xe2\x9c\x10\x71\x0e\x89\x4a\xd3\x79\x9a\xc1\x56\x87\xa7\x38\xca\x04\xdd\xf4\xaf\x33\x9c\x36\x1b\x65\x26\x8c\xff\x50\x36\x26\x92\x95\xbb\xf7\xe9\x4b\xad\xf9\xe3\xd5\x29\xa5\xa2\x79\x14\xfe\xf7\x1c\xa3\x70\x88\xa3\x2c\x1c\x85\x3a\x27\x2e\x34\xd7\x7c\x74\x0a\xcc\x30\x34\xe9\xe6\x9a\x01\xec\x3a\xca\x1e\xf4\xca\x24\x02\x3e\xc6\xa5\xa0\x1f\x96\xab\x41\x46\x18\x6b\x95\x8f\x2f\x07\xfd\xe7\x5d\x89\xc0\x92\x55\xf9\x28\x3a\x83\x20\xd8\xfb\xe1\xb3\x6e\x93\x88\xae\x3c\x73\xff\xcd\x59\xa5\x5d\x28\x57\x32\xd3\xee\xb6\x0b\x25\x6c\x7b\xa5\x2a\xe1\x63\x22\x5f\x8c\x82\x41\x16\x27\xd7\x15\xaa\x50\x26\x03\xfb\x84\xb0\x69\x22\xea\xc7\x23\x24\x7a\xb3\xb1\x81\x9e\xd1\x88\x4c\xcf\xa0\xcc\x93\xd5\x55\xd4\x8b\xa7\xd3\x38\xfa\xaf\xe3\xa7\x4f\x9e\x58\x9d\x97\xbf\x58\x03\x1c\xa7\xd2\x33\x32\x0c\x09\x7e\x56\xae\x20\xe5\x15\x8e\x06\x2f\xfa\x41\x8a\x3b\x2d\xe3\xc3\x74\xd8\x36\x8b\x5e\xcc\xbe\x0c\x47\xc6\xcb\x41\x38\x1b\xe3\xe4\x05\x85\x5c\x7e\xf5\xf4\xc9\xcd\xd3\x27\x78\x92\x62\xa4\x74\x86\x2a\xcc\x69\x5f\xf8\x30\x3c\x43\x3f\xfe\xc8\x3e\x54\x83\xe9\x50\xf4\x6d\x73\x7f\xeb\xe9\x93\x27\xf4\x43\xe9\x94\xe3\x5c\x41\x3a\xaa\xf0\x4c\x30\xa4\x1f\x28\x62\xf0\x5b\xc5\xe7\x4c\x8c\xb2\x8a\x18\x6b\x88\x46\xc3\x40\xa5\x7e\x12\x5f\xa6\x38\x29\x3f\x7d\xf2\x44\x8c\x58\x1c\x67\xd5\x5e\x72\x3d\xcb\xe2\xff\x3a\xa6\x55\x6f\xe0\xf4\xa4\x6e\x3f\xe2\x3b\xfa\xf3\xe9\xd3\x27\x25\xfd\x38\xf6\x04\x51\x8d\xc8\xf1\x38\x4e\xb2\xc1\x3c\x4b\xe9\x1b\xb2\x6c\x7a\x68\x03\xf1\xba\xaf\x94\xd7\x9f\x26\x61\x9f\x7c\xaa\x4e\xc2\xbe\xf2\x1e\x94\x61\x3d\xe8\x14\xf9\x4a\x4a\x55\x95\x77\x1a\x84\x60\x72\x1e\x03\x08\xf2\xe3\xd5\x53\x81\xc5\xfb\x38\xfe\x32\x9f\xa1\x2c\xe8\x4f\xb0\x82\xc9\xf1\x9b\x83\xdf\xd8\x99\x4f\xbc\xdb\xfb\xf0\xcb\x27\xd7\xfb\xe3\x8f\x6f\x3e\xed\xef\xfd\xf6\xa9\xe6\xfb\x50\xf7\x7d\x68\xf8\x3e\x34\x9d\x6d\xfb\xda\x51\x3f\x5a\x6d\xa9\x1f\xad\xf6\xd4\x8f\xbc\x4d\x31\x34\xbd\x78\x3a\x23\x07\xc5\x89\x3d\x44\xae\x29\x35\x6a\x0d\xe3\x79\x9f\x48\xfd\xa4\x96\x2c\x00\x2c\x56\xc5\x02\xa9\x96\x0a\x21\x84\x13\x44\x21\x7a\x8d\x1a\xed\xce\x2b\x14\x3e\x7f\xae\x81\x17\x32\x22\x7a\x8d\xea\x8d\x75\xeb\x1b\xf9\x1b\x9e\x86\x67\x68\x83\xc0\x78\x8d\xea\xaf\xf4\xef\xf4\x2a\x35\xa7\x56\x89\x56\x2b\xa3\xdf\x51\xed\xaa\x5e\xef\x9b\xf5\xe5\xe3\xcd\x53\xad\xd7\xbf\x06\x93\x2f\xe8\xed\x4e\xa9\xf1\xfb\x7a\x59\xef\xed\x15\x0d\x91\xa8\xbf\x0b\x8d\x97\x4b\x8d\x80\x32\xc8\x69\x3f\xbe\xd2\x3f\x82\xa1\x01\x69\xf3\x2a\x44\xbf\xa3\xd2\x95\xec\x10\xfb\xdd\x50\x7e\x37\x95\xdf\xad\xb2\xd1\x59\x80\x52\x4a\xaf\xd0\xcf\x3f\xff\x8c\xd6\xa1\x64\x7a\x85\x7e\x44\xb5\xab\xd1\x88\x0e\x50\xa7\x69\x54\x21\xab\xe3\xf4\x8a\x0c\x64\x7a\x65\x7c\xe2\x8b\xe7\x34\x85\xef\x57\xaf\x9e\x7a\x3b\x35\x9d\x4f\xb2\x70\x36\x09\x07\xa0\x25\xb0\xbb\x77\x45\xc8\x78\x78\x7a\x75\xf6\xca\xf1\xad\x45\xbf\x35\x9c\x1f\xd7\xe9\xc7\xd6\x59\x4e\xeb\xe9\xbc\x8f\x40\xbe\xa9\xa0\x69\x78\x85\x06\xf1\x64\x3e\x8d\x52\x8d\xfa\x55\x98\x44\x52\x28\x0d\xa1\x57\x3f\x11\x9a\xa9\xd5\xf9\x48\xb1\xc7\x5a\xbd\x56\x33\x87\x56\xac\x64\x3a\x58\xa5\x0c\x26\xa6\x55\x46\x5f\xc9\x6f\x3a\xde\x9e\x2a\x75\xb5\x4a\xbd\xa3\x54\xa9\x77\x7c\x75\x1a\x6a\x9d\xf5\x32\x92\x75\x1a\xd6\xac\x0b\x6e\x40\xeb\x64\x39\x23\x15\x46\x17\xea\x68\x91\xc7\xc2\x23\x76\xb5\xae\x8c\x0f\x23\xcf\x16\x7b\x55\xe3\x2f\x1a\xda\x90\xe6\x8e\xa8\xc6\x1f\x19\x8d\x15\x19\x56\x8d\x75\x6a\xf5\x16\x8c\xad\xc6\x56\xb5\x8a\x0b\x06\x58\x63\xb9\xac\x62\xde\x28\xc3\x65\x01\xe8\x81\x71\x62\x73\xc2\x1f\xae\x9c\x4c\x90\x31\x80\x8d\x25\x38\x20\x54\x69\xa0\xdf\xd1\xf0\x94\xfc\xef\x6a\x1d\xfd\x8e\xae\x1a\x67\x67\xe6\x42\x82\xb2\x21\xfa\x7d\x03\x0a\x5e\x85\x56\x01\x8d\x49\xc2\xcf\x1b\x38\xd3\x8a\x7d\xe5\x30\xc1\x03\xda\xb9\x21\x3a\x1a\xc4\x11\xdb\x60\xe4\xae\x74\xd4\x3b\xf8\x40\xf6\x88\xda\x55\xad\x56\x41\xb5\xab\x5a\x1d\xfe\xdb\x80\xff\xb6\xe0\xbf\xeb\x15\xa0\x05\xf2\xdf\x06\xfc\xb7\x05\xff\x5d\x87\xff\xd6\xfb\xe4\xbf\xcd\x8e\xdc\xcc\x7e\xfa\x89\x21\xf5\x13\xda\xdc\x3e\xa6\x01\xd9\x11\x15\x87\x10\x11\x08\x92\x30\x1b\x4f\xab\xbc\xcc\xaa\x44\x85\x94\xde\x60\xe2\x43\x95\x3e\x28\x12\x46\x15\x5f\x65\x34\x7a\x80\xe8\xf2\xa7\x61\x7c\x84\x53\x9c\x75\x91\x67\x8b\x64\x83\x70\xfc\x25\x9c\x31\xcb\xdf\x78\x84\xa2\xa3\x18\x4e\x63\xe3\x20\x45\x7d\x8c\x23\xf0\x0e\x60\xf7\x5b\x41\x34\x04\x13\xbe\x61\x38\x44\x51\x9c\x31\x33\x4c\x9b\x14\x68\x36\x17\x0e\x89\x9b\x8b\x7e\xfa\x82\xaf\x0f\x93\x30\x4e\x8e\xa8\x05\xf0\xc6\x86\x7c\xef\x24\x1d\x6e\x16\x66\xcc\xa9\xdd\x01\x5d\x7c\xe3\x7f\xdc\xe0\x70\xc3\xdd\xbc\x7c\xeb\xe0\xcf\x5f\xf0\xf5\xaf\x71\x02\x46\x8c\x5f\xf0\x75\xf5\x92\xfc\x76\x17\x3b\x0e\xff\xc0\xac\x54\x1a\x9e\xbf\x21\x0c\x08\xad\xa2\x56\xde\x32\x12\x7e\x00\x09\x0c\x90\x0d\x96\x8f\x1c\xc7\x51\x3e\xf3\x06\x9f\xa3\x4e\xa1\x16\x48\xff\xd3\xc1\x18\x93\xe3\x07\x22\x22\xb4\xa3\x0f\xe9\x51\x7c\x49\x60\x97\x78\x33\xcf\xc9\x2e\xfd\x53\x6e\x1f\x54\xb8\xee\x61\xe1\x8d\x2a\xe3\xac\xbc\x3b\x35\x97\xaa\x34\x11\x25\xe8\x50\xd1\x83\xfe\x7c\xcd\x30\x64\xcf\x0e\x29\x04\x31\xb2\x13\xe5\xe9\x20\x39\xcb\x91\x3f\x05\x95\x53\xa8\x73\x46\x47\x16\x66\x9c\xbd\x71\xb0\x1a\x3f\xc3\x42\xca\x7e\x62\x01\x87\x68\x3a\xe6\x50\xaa\x68\xff\xc0\x10\xff\x97\x40\xdc\x8b\x39\x9b\x85\xa3\x38\x43\x84\x24\xfd\x85\x32\x75\x0f\xd0\xb7\x80\x5c\xc8\xc7\xf3\x7e\x11\xc8\x20\x3e\x71\x98\x67\xca\xde\x06\x1f\xe4\x4e\xc5\x64\xb4\x33\x65\x17\x53\x4b\xac\x6b\x05\x00\x53\x06\x99\xbd\x5e\x80\xed\x7e\x78\x05\x6c\x3b\x0f\xdb\xdf\x37\x80\x89\x9f\xb2\x41\x5e\x95\xd4\xf1\x15\xd5\x18\xea\x8e\xc9\x46\x72\xc2\x81\xb4\xd8\xba\xfb\x19\x75\x08\x3f\x33\x26\x0c\x6d\x6c\xa0\xd6\xa2\x49\xfb\xee\x86\xd6\xdd\x67\xcf\x88\xfb\xd6\x8c\x45\xeb\x6c\x48\xce\xd0\xef\x44\x96\xb0\x17\xd1\x42\x6e\xae\xca\x74\xf9\x6c\x26\x8c\x2e\xde\x39\x38\x8d\xf5\xda\xcf\x6c\x48\x51\xc9\x6f\xc4\x93\x64\x39\xfc\x95\x87\xeb\xa8\x0c\x8b\xf1\xd1\x17\xa2\x8e\x8b\x78\xe1\xc8\xc8\x9b\xf9\x57\x0e\xd1\x78\xd9\xc9\xfd\x72\xa6\x96\x13\xdc\x22\xc4\x5f\xa3\x16\x38\xb2\xd0\x87\x3c\xda\xd7\xe7\xe2\x94\x43\x60\x92\xe6\x92\x1d\xc9\x01\xa6\x0b\xdd\xfa\x1a\x22\xa4\xa8\x0b\xd7\x9e\xa5\x74\x86\x7e\xf7\x2f\x4e\xcf\x9f\x2e\x7c\xbb\x57\xa0\x89\x40\xf3\x54\x5f\x8a\xee\x39\xf0\x4a\xb2\x15\x65\x7a\x70\x34\x48\xae\x67\xd4\x32\x56\x95\xf3\xf6\x2b\x28\x1e\x8d\x52\x9c\x59\x33\x43\xd7\xc8\x30\xee\x89\x7a\xb2\x70\xc5\xde\xab\x2b\xf2\x84\x28\x7f\xd6\xe5\xcf\x86\xfc\xd9\xac\x00\x8b\x51\x4f\x19\x1a\xae\x43\xbc\x2c\xae\x84\x6b\x5e\x06\x33\xd4\x88\x86\x20\x7b\xb6\xb2\xb1\x47\x88\x21\xf4\xbd\x7f\x4a\xc1\x10\xf9\xc5\x1c\x52\xed\x9b\x5e\xb6\x99\x53\xb6\xe9\x3c\x12\x15\x19\x42\x9d\x56\x2b\x3a\x81\xea\x8f\x75\xfd\xb1\xa1\x3f\x36\x2b\x42\x61\x61\x6d\xde\xab\xab\x68\x8f\x9c\x7c\xbf\x8b\x31\x72\x4f\xba\x36\x4c\xce\x59\xaf\xa0\xbb\x91\x9b\x8b\x68\xd8\x81\xa0\xb0\x64\xed\x18\xd8\xb7\x98\xc5\x0a\x85\x0b\x49\x2a\xaa\x13\x4c\x1d\x3a\xae\x9a\x32\x58\x67\xf0\xfa\x77\x8d\xd9\xd6\x5c\x1a\xa0\xb4\x6e\x4e\x87\x51\xcb\x9a\x1f\xa8\xd5\xd0\x6b\x35\xcc\x5a\x4e\x6d\x53\xda\x34\xa7\xd3\xa8\xd5\x74\xa9\xa1\xde\x19\x67\x07\xf7\xd1\x5f\xdd\x02\x5d\x27\x86\x23\xc7\x19\x47\xec\xbf\x74\x54\x37\x50\xfd\x15\xfb\xf9\x9a\xcf\x10\x7b\xe1\xd9\x77\x61\x8e\xc3\x51\x06\x94\x5e\xf1\x28\xca\x72\x27\x8e\xa3\x9e\x91\xc9\x53\xd4\x35\x35\x21\x79\xfd\xae\x28\xba\x4a\x69\xdd\x92\xbb\x7e\x57\x94\x5a\xa5\xb4\x61\x4a\x5d\xbf\x2b\xfa\xab\xb4\xa9\xbc\xb6\xb6\xe1\xe7\xcf\x5d\x1b\x00\x20\x57\xd7\x91\xab\x7b\x90\x6b\x2c\x40\xae\x99\x8b\x5c\xed\x96\xc8\x35\x74\xe4\x1a\x1e\xe4\x9a\x0b\x90\xab\xe5\x22\x57\xbf\x25\x72\x4d\x1d\xb9\xa6\x07\xb9\xda\x02\xe4\xea\xb9\xc8\x35\x16\x22\xe7\x24\xdd\x8f\x33\xb0\x21\x4a\xb3\x20\xc3\x76\x01\x60\x27\x59\xcd\xd1\x31\x60\x19\x99\xa9\x47\x83\x2f\x64\x2e\xb2\x86\xeb\x0b\x19\x88\xcc\xd4\x8e\x3b\x95\x28\xce\xf5\xb4\x80\xf7\xc1\xf2\x29\xd1\x93\x87\xb2\x76\xcc\x53\x8b\x63\xf9\x98\xc7\x16\x7b\x05\x69\xe7\x16\xb9\x84\xca\xc5\x28\x41\xac\x1f\x8e\x5d\xdd\x8f\x9d\xbd\x7e\x2c\xec\xac\x25\xa4\x63\x57\xbb\x0d\x76\x0d\x05\xbb\x86\x1f\x3b\x7b\x01\x59\xd8\x59\x6b\x48\xc7\xae\x7e\x1b\xec\x9a\x0a\x76\x4d\x3f\x76\xf6\x0a\xb2\xb0\xb3\x16\x91\x8e\x5d\x63\x31\x76\x36\xb5\x62\x1e\xd8\xda\x2d\x97\xd0\x6d\xd8\xb1\x8e\x4c\x21\xc7\x5a\x4e\xfa\xe6\xea\x58\x55\x96\xe8\xd3\xf4\xc9\x3e\xec\x28\xdc\x45\x8d\x76\x67\xb5\xd9\x60\x1a\xe8\xb2\x4b\x15\xcc\x25\x16\x21\x20\xa5\xcc\x71\x98\xa9\x86\x57\x52\x96\xf0\x09\x41\x0e\xef\x51\x30\xc0\x42\x47\x2c\x80\xfc\x27\xbe\x0a\xa6\x33\x71\x52\x96\x1f\xf8\x9c\x52\x58\x19\xbe\xca\x94\xdb\xed\xea\xe6\xf6\x71\x95\x9d\x23\x4a\x53\x6e\x91\xfe\x05\x5f\x57\xd0\x60\x74\x2e\xa4\x79\x09\x65\x36\x09\x08\x12\x57\x19\x32\xa1\x30\x09\xbf\x24\xdb\x71\x01\x62\x3a\xed\x9e\x43\x89\xfd\x89\x46\x4d\xdd\xc5\x93\x19\x4e\x4a\x9b\xdb\xf4\x5a\x9f\xea\xec\x9f\x3e\x61\x36\x2b\x6a\x93\xaf\x9e\x3e\x85\x08\xb8\x60\x40\xa2\x59\x15\x74\xdb\x8d\x0a\xb7\x4b\xe8\xb6\xc1\x76\x44\xb1\x4c\xe8\xb6\x5b\x15\x69\x92\xd0\x6d\x83\x0b\xe3\x74\xd8\x7e\xd6\xed\xd4\x6f\xce\x2a\xed\xc6\x9d\xac\x45\xbe\xa5\x99\xc8\x83\x19\x73\x7c\x43\xb3\x0c\xba\x12\x7e\x42\xcc\x80\x82\x34\x8f\x06\xf1\x74\x16\x47\x10\x72\x9d\x7c\x5b\x7d\xfa\x44\xcc\xfb\x24\xec\x57\x59\xd1\xaf\x5f\x55\x03\x00\xe1\xf4\x79\xcf\xc6\x1d\x41\x8a\xa5\x55\x47\x90\x62\xe5\xdb\xaf\x71\x32\x04\xb7\x74\x51\x40\xbc\x51\x21\xcc\x47\x60\x2f\x06\xb4\xbe\xc9\x6f\x79\x24\x4c\xe7\x67\x0d\x33\x0c\x9e\x55\x3d\xb2\x50\x95\xf7\x1f\xb3\xd1\x3a\x40\xc1\xd1\xa0\x4a\x1e\x0c\xac\x3b\x2d\xf1\x95\x3e\xe6\x19\xa2\x88\x2f\xdb\x17\xb3\x77\x5b\x3b\xf2\xb2\x89\x3e\x3b\x6f\xb0\xfa\x29\x35\xcf\x23\xcb\x8a\xdf\x62\x65\x78\x3a\x9b\x04\x99\x8b\x41\x89\x20\xd3\x7f\x46\x2c\x20\x0f\xd7\xa0\x82\x53\x81\xe0\x75\xa0\xf7\x0b\xff\xc0\x55\x1e\x60\xb2\x8b\x5a\xa8\x54\x6f\xac\xa3\x7e\x98\xa5\xe5\x3c\x80\xe1\x85\x03\xde\xde\x2f\xb7\x05\xf7\x69\xfb\x43\xef\xd3\x6f\x3b\x07\x47\xfb\x9f\xf6\x0f\xb6\xb6\xd1\x26\x84\x36\xc8\x82\x28\x43\x09\x9e\x25\x38\xc5\x51\x16\x46\xe7\x5c\x11\x43\xc8\x70\x1a\x0f\x65\xdf\x9d\x30\xb7\xb6\x0b\xc1\x64\xec\xd4\x82\xa9\x5c\x0a\x1a\x26\x47\xe2\xd1\x4d\x51\x8e\x4b\x42\x39\x9b\x14\xdd\x1e\xb8\x7d\xcf\x13\x30\x78\x10\x39\x3e\xd4\x22\x5a\x71\xa5\x77\x82\xee\xc9\x1c\xa0\x93\x31\x26\xa3\x9e\xc5\x68\xce\xdc\x04\x08\x0b\x40\xa4\x30\x80\xd6\x40\xae\xca\x87\xc1\xe8\xbc\x0b\xa4\xcb\x71\x2d\xab\x3b\xaa\x85\x2d\x6c\x17\x29\x85\xcd\xc8\x2f\x8c\x7c\x93\xe1\x42\x9f\xda\x63\x2a\xb8\x13\xd2\x23\xc8\x7f\xc1\xd7\x55\x67\x59\xee\x19\x3a\x18\x9d\xa3\xd2\x01\xb4\x12\x4c\xca\x50\x67\xe0\x1a\xbc\x82\x63\xa0\xb7\xc5\xe3\x88\xd2\x09\xbd\x21\x24\xc2\x7b\x47\x08\x65\x90\xd7\x27\x72\xae\x08\x07\xfe\xef\xba\x94\x60\x17\x40\x9a\xb4\xa0\xee\xf1\xfc\xea\xb9\x4a\xb7\xe9\x6d\x3a\xcc\x71\x52\x62\x97\x67\x30\x84\x15\xf4\x27\x0a\x2f\xba\x28\xbc\x90\xbc\xf1\x46\x33\x3d\xd0\xe6\x5b\x87\xd4\xd5\xc2\x42\x31\xc9\xc1\xd4\x00\xa8\x89\x43\x68\x7d\x76\xe3\xac\xaf\x55\x87\xec\x61\x4a\x68\x05\xe9\xc9\xb3\x10\x1f\xe9\xe9\x7e\xe9\x69\x0b\xdf\x17\x3d\x09\x48\x77\xa3\x27\x9d\x4f\xdf\x82\x9e\xf6\xa2\x30\x0b\x83\x49\xf8\x07\x4e\x51\x80\x22\x7c\x39\xb9\x66\x18\x0e\xd9\x70\x2c\xa6\x25\xbe\x6b\x5c\x8d\xe2\x64\xba\x1f\x0f\x31\xda\xa6\xbe\x6a\x10\xa6\x59\x72\xba\x38\x51\xe9\x14\xac\xab\xc1\xcd\x8f\x53\xad\xd8\x64\xdc\x64\xf8\xdd\x91\xec\xbd\x91\x55\xc9\xfe\xe0\xe2\x14\xb7\x24\xb8\x30\x0a\x35\x0b\x1b\x31\x4d\x0a\xb9\x38\x54\xd4\x9b\xb3\x19\xa1\x05\x18\x2d\x9e\x6e\x3a\x75\x5c\x33\x90\x21\xde\x10\x3f\xf9\xa6\x48\x69\xd0\x3e\x15\x67\x44\x72\xa6\x86\xf5\x71\x32\xa5\xd3\x1e\xb8\x74\x37\x94\xbe\x25\x49\x6d\x48\xf2\x7a\xe5\x2a\x49\xed\x68\xc0\x56\xc6\x79\x16\x0f\x29\xa1\x53\x0f\x00\x57\x3f\xc0\xbe\xa8\x54\x78\xe1\x80\x8d\x8e\xce\x87\x21\x96\x43\x2a\x5a\x02\xed\xd9\x1d\xc9\x87\x2d\x41\x1b\x37\x6d\x86\x93\x22\x46\x54\xd4\xa8\x68\x18\x64\x01\xea\x83\xec\xa5\x97\xf0\xc8\x63\x00\x9a\x66\xba\xe0\xde\xce\x26\xe0\x43\x9c\xc0\x5c\x0e\xe2\x68\x90\xe0\x0c\xbf\x60\xc3\x31\x89\xcf\x35\xa6\xac\xdc\x4b\x1d\x2d\x37\xd6\x10\x4f\x03\x30\xa7\xee\x2d\x8c\xa7\xe0\xa1\xc2\x52\xf0\x70\x89\x4d\xef\x6b\xca\x5c\x61\x08\x50\xa6\xec\x24\xbc\x81\xb7\xc1\x1a\x50\xc0\x17\xd8\xb9\x14\xfe\x24\x60\xd1\xa0\x59\x2c\x18\x41\x18\x9d\xdf\x03\x37\x91\x9d\xdf\xe0\xe4\xc1\xe0\x97\x56\x48\x9b\x2b\x3a\x99\x14\xa9\x77\xc9\x31\xf7\x52\x18\x2b\xd9\x35\xa2\xbc\xd2\xa1\xf3\x70\x0f\x1c\x0d\x5d\xb3\x1f\xc0\x17\xb5\xba\x8b\xa6\x68\x7b\x28\xb8\x08\xc2\x49\xd0\x9f\x60\x6a\x86\x98\xfa\xb7\xc5\x4f\xbc\x33\x85\xa9\x6a\x27\x8c\xd8\xc6\x97\xbb\x4f\x31\xb8\xfa\x3e\xf3\x21\xce\x98\x77\x34\x0d\x9a\x46\x21\xc9\x5d\x03\x85\x29\xc2\xa3\x11\x1e\x64\xe1\x05\x9e\x5c\xa3\x00\x0d\x71\x9a\x25\x73\x78\xae\xa0\x04\x07\xc3\x17\x71\x34\xc0\x85\xf6\x99\xa2\xd4\x0b\x68\x3c\x14\x0d\x53\xe0\x0f\x4d\xc9\x7c\x24\x4b\xc5\x89\x58\x54\x59\x96\xfa\x45\xc5\xc5\xe4\xcf\x8b\x16\xa7\xff\x1d\x39\x17\x73\x28\xa4\x97\x08\x47\xb9\x00\x50\xee\x6a\xd1\x8a\x3a\x2e\x4a\x96\x60\xc8\x10\x0f\x89\xa0\xca\x16\x1c\x1e\xb2\x78\x99\x9c\x53\xef\x28\x13\xe2\x5c\x7c\x76\xed\x85\xca\xe6\x7a\x63\x7d\xb5\xd9\x50\x3f\x51\x95\x88\xeb\x8b\x21\x07\x75\x51\x5d\xfb\xaa\xcb\xbf\x5d\xd4\x28\x72\x76\x4a\x9d\xaa\xec\x60\xb1\x22\x1b\x79\xd7\x26\x3f\xb5\xb0\x91\x3e\x19\x63\x45\x28\x60\x89\xb6\x02\x34\x06\xad\x31\x11\x32\x0b\x2c\x45\x2e\xc2\x6e\x46\x1c\x1f\x08\x30\xc0\x97\x35\x11\x9a\xd8\xba\x76\x74\xe8\x1b\x1c\x96\x98\xb5\xb7\xad\xf2\x34\x74\xe4\x96\x6c\xeb\x5d\x65\x5a\xbd\xae\xd7\x6f\x8a\xfc\x89\x4f\x29\x9e\xe0\x41\x46\x1b\x3e\xce\x92\x20\xc3\xe7\xd7\x25\x9f\xb9\xb6\xa2\x7d\x06\x71\x71\x03\xad\x50\x56\xba\xe2\x35\x0f\x63\xb3\x71\x18\xa4\x29\x61\x13\x6f\x82\x14\x0f\x35\x8f\x39\xf5\x2f\xdf\x38\x8c\x81\x3a\xc6\x09\x1c\xb8\xc8\xae\xe6\x87\x94\xbf\xc8\xcd\xdc\x7e\xec\x3e\x23\xc7\x46\xdd\x87\x14\x23\x27\x95\xb1\xd9\x37\x2c\x79\x76\xa3\x32\x08\x98\x7b\x1e\xc4\xc5\x0d\x45\xb1\x82\xfc\x17\x38\xe6\x18\x54\x3c\x96\x9e\x8c\xec\xbb\x56\xff\x8d\xfb\x9c\x3b\xa1\xad\xdf\x14\x55\x50\xee\x8d\x91\x89\xb9\x63\x42\x4d\xb6\xad\x72\xc9\x52\x99\x69\x78\xdd\x57\x6f\xba\x0e\x3b\xcd\x12\x1c\x4c\x6f\xa5\xca\x06\x19\x8a\x29\x9f\x55\x1b\xfc\x66\xe3\x45\x3f\xa4\x06\xdb\xfa\x89\x86\x4a\x27\x10\xc6\x5a\xd1\x4c\xd7\x51\xa9\xd9\xd0\x15\xd3\x8a\xc2\xf7\x18\xf0\x33\xd4\xbe\xe6\xcb\x1c\x8f\x90\x1d\xc7\x5e\xeb\xda\x61\xb9\x88\x38\x0b\x12\x38\x6e\xb9\x04\x44\x7b\x7b\x83\xe3\x8d\xb4\xae\xe2\x42\xe3\x0f\x3f\xac\x8c\x26\xf3\x74\xbc\x52\x6c\x9b\xa3\x50\x7c\x1b\x9d\x18\xe6\x2e\xaa\xe7\xcd\x2b\x9c\x6b\x21\xab\xe9\x4c\xbd\x2d\x55\x95\xe7\x9f\xa6\xf4\xec\xdb\xab\xb2\x1f\x7f\xde\x2c\xa6\x10\xcd\x63\x07\xea\x59\x54\xa2\xb4\xa1\xdc\x6e\xb2\x83\xb6\xe5\x1c\xcc\xde\xab\x4a\xef\x3c\x05\xbd\xaa\xa2\x9c\xf2\xe4\x5c\x52\xbe\x5e\x7a\x37\xdd\xd4\x7b\xe4\x54\x08\x9a\x99\x65\xa4\x82\x1f\xa8\xfa\x1b\xec\x87\x7c\xa6\xf8\x76\x07\x7a\xd8\xde\x9b\x9e\xa5\x8a\xe6\x1c\x25\xbc\xa0\x5e\x3b\xb7\xd1\x3c\x4b\x18\xb9\xba\x42\x51\x97\x2b\x9a\x94\x7a\xb7\xd2\x38\x8b\xe9\x94\x07\xa4\xff\x99\xd3\x29\x35\xc1\x4b\x4e\xa7\x53\xf1\x5b\x70\x3a\x45\xdd\x3b\x4c\x67\x9e\xc2\xb7\xd8\xd5\xc1\x37\x9d\xce\x3b\x4f\x57\xce\x12\x58\x30\x5f\xa6\xde\x34\x67\x92\xe8\x66\x22\xf4\xbc\x03\x97\x58\xc7\xac\xae\x2f\xd0\x06\x0a\x2f\xd4\xd9\xca\xdb\x22\xd8\x8e\x49\xe3\x4a\xf7\xc6\x41\x18\x41\xca\x13\xdf\x5d\xeb\x1b\xb0\x1b\xf8\xc4\x3b\x8f\x36\xfc\xc1\x07\x4c\x15\x9b\xb6\x83\x90\xba\x16\x31\x28\x43\x23\x1b\x33\x76\x09\x71\x27\xfa\x2a\x8f\xa3\xbc\xe9\xf1\xed\xc0\x38\x09\x29\x4d\x68\x73\x47\x7a\xf5\xa6\xe7\xd8\x7b\x6c\xf0\xb4\x89\x43\x11\xfe\x33\xe3\x6a\x0c\x4a\xa5\x41\xc6\x8c\xba\xab\x66\x1d\x0b\x86\x41\xb3\x54\x3a\x12\x5a\x11\x26\x2c\xc5\x5c\x46\x42\x3a\x27\x44\xce\x1b\x12\x66\x97\x45\x80\xb0\x9f\x97\x63\xcc\x22\xef\x53\xfc\x20\x90\x67\x5a\x00\x39\x7b\x61\xb8\x0b\x92\x3f\x98\x4a\x26\xea\x50\x6f\x00\x48\x8f\x07\x5d\x10\xae\x0d\xa6\x2c\xab\x4e\x06\x92\x2a\x40\xcb\x4c\x5e\x87\xe2\xb5\x85\x76\x3a\xc0\x22\xf3\x86\x44\x5d\x48\x1e\xc3\x59\x29\xc4\x0a\x4d\x8e\x78\xe5\x31\x67\xfd\xed\xe0\x08\xce\xcb\x8c\xe8\xec\x32\x57\x71\x02\xfd\x92\x8a\xee\x0a\xd2\xfa\x55\x91\xcd\xba\x84\x7e\x86\x87\xea\xeb\x52\x32\x47\xd7\x89\xd9\x11\x9e\x62\x90\xc2\x61\x77\xa5\x24\xc0\xae\xa2\xe0\xb4\x0f\x0e\xed\xf0\xda\xae\xce\x25\x58\x7c\xc1\xc3\xce\x53\x66\x4a\xf3\xc9\x73\xbc\x85\x29\xa0\xb7\x03\xaa\xe7\xce\xc2\x75\x3b\xc4\x05\xd6\xad\xd8\xa7\x1e\xd7\xed\xe3\xba\x45\xb7\x5f\xb7\x77\x59\x1d\x60\x21\x3c\x0e\xd3\xa5\xd7\x86\x13\x13\x46\xd1\xc0\x45\x7e\x3b\x38\xf2\x72\x00\xd5\x83\xcc\xe2\x00\x77\x65\x3b\x4e\xcc\x4e\xe4\xd0\xf4\xf1\x20\x9e\xb2\xa5\x43\xd8\x42\x18\xcf\xd3\xe2\xcc\x43\x0c\x56\x51\xf6\x20\x48\x89\x77\xa3\xe4\xc5\x7d\x29\x0f\x28\x10\x91\xb8\xb4\xe4\xf2\xf0\x1f\xc7\x71\x8a\xd1\x34\xbc\x22\xb2\x90\xa3\x7f\xe0\x09\x6a\x0b\x69\x48\x25\x44\x26\x85\xf9\xc8\x2e\xbe\x00\xe9\x94\x9c\x74\xd2\x79\x3f\xc5\xff\x3d\xc7\x51\xe6\x54\x31\x20\x5d\xb4\x53\xb2\x7a\xe8\xa3\xe8\x55\x0d\xaa\x28\x19\xb3\xb2\x58\xd5\x4f\x76\x36\x17\x56\xae\x18\x49\x72\xb5\x39\x23\x25\x91\x3f\x98\x40\x69\x3d\x1e\x9e\xa1\xdf\x37\x68\xbd\xd3\x30\x37\x74\x89\xfc\xcd\x4d\xa0\xdf\xf4\x58\x79\x2d\xa0\x89\x22\xda\x1e\x06\xc3\x21\x99\xc0\x05\x0a\x90\x19\x64\xb9\xea\x55\xe9\xbf\x6e\xf5\xc7\xe1\xbb\xde\x31\xfa\x5f\xed\xd5\x35\x34\x63\x40\x53\xa6\xcb\x73\xc1\x3c\xfc\x32\x48\xd7\x40\x4e\x9e\x05\xc3\x2a\x7f\xca\x91\x8d\x0f\x03\x7e\xfd\x3c\x4f\x79\xe8\x7c\x11\x08\x85\x99\x2b\x43\xdc\x64\x81\xc7\x52\xf6\x57\x00\x59\xbd\x7d\x26\x68\x39\x2b\xb9\xf5\x78\x2c\x04\x94\x72\x1f\x09\x80\x52\x11\xcc\x92\x0c\x0a\x84\xb3\x7c\xe0\x63\xb3\x38\x7c\x89\x71\x25\xbf\xe4\xf5\x5a\xc5\x88\x9b\xa5\x5d\x30\x07\x43\xf3\x72\xed\xd6\x0c\x44\x54\xa3\xb1\x4e\x36\x94\xf1\xf2\xc5\x0c\x99\x47\x99\xa0\x1d\xf0\x2b\xb2\xa1\x46\x8c\x60\x2d\xa0\xf4\xc5\x0b\x9a\x72\x5a\x44\x58\xf9\x97\x51\xc0\xd5\x2c\xbd\x17\xe2\xed\xda\xa1\x17\x68\xa6\x37\xf8\x4a\xe8\x05\x22\xa0\x68\x58\x48\x5f\x17\xeb\x3d\x73\x70\xb1\xde\x83\x5b\x8b\xf6\x76\x21\x66\xb9\x48\xa5\xf9\xe1\x0b\x24\xfb\xd1\xdb\x44\x21\x7a\xee\x73\xcb\x57\xa1\xd3\x30\xf7\xca\x9b\x1c\xe9\xd5\xc0\x0e\x6d\x48\xdb\x77\x7e\xf8\x57\x41\x57\x74\x94\x5c\x66\x08\x9b\xc3\xa1\x7b\x10\x60\xae\x07\x71\x34\x08\x32\x0e\xb3\xb0\x06\xe6\x63\x34\x13\x0c\x05\x96\xec\x38\x18\xd2\x40\x46\x6c\xa1\x7e\x1b\x2e\x33\x8f\x4c\x3e\xf3\x4d\x38\x02\x34\x5b\xe0\xca\x1d\xca\x99\x2c\xc1\xc5\x07\xde\xe2\x4c\x4b\x5c\xac\x2c\x62\x88\x01\x8b\x26\x41\x9a\xc1\xf3\xe2\x35\x2d\xc5\xeb\xd3\x92\xbe\x9c\x5f\xa0\x7a\x99\xba\x98\x9d\x31\x67\x30\x97\x27\x31\x15\x1c\xfc\x14\x23\xc1\x6d\x98\x6b\x50\xd9\x4c\xe9\xb6\xb9\xa4\x9e\xff\xaf\xb8\x08\x72\xb9\x28\xb8\x6f\x16\x5c\xb7\x0a\x79\xf7\x40\xf7\x67\xf4\xbf\x1f\x0f\xf1\x0d\x55\x0f\x9e\x88\xd3\x1a\xbd\x14\x81\x93\x84\xd2\x9d\xde\x9b\x9e\x0f\x0a\x9b\xab\x1b\x41\x5f\x04\x96\x29\x6c\xd8\x10\x81\xe4\x3d\x04\x0e\x7e\x04\x6c\x00\x14\xc3\x49\x83\xc0\x09\xa6\x80\x59\xc5\x38\xd5\xd1\xb6\xad\x26\x6e\x34\x6f\x84\x25\x0c\x03\xe9\x44\xeb\x1f\x7b\x8a\xf5\x61\xbe\x0d\x60\x4e\x80\x33\xdd\x3e\xd4\xe1\xc7\x09\x72\x33\x19\x01\x4d\x2d\x8a\x74\xc5\x2e\xf9\x3e\x05\xdb\x4f\x0f\xfe\x72\x62\xed\xc3\x80\x65\x4b\xca\x25\x6d\xdd\xb8\xc4\x7b\x62\x20\x50\x61\x4b\x04\x8d\x06\x9c\xca\x8d\xbb\x19\xb7\xb4\xbf\xfa\x53\x7e\xf3\xba\xf5\x4a\x19\xfd\xb4\xba\x34\x06\x42\xd5\xe2\x39\xcb\xbc\xc3\x78\x86\x82\x0c\x4d\x30\xe1\x82\x71\xc4\x57\x00\xcb\xf2\x41\x2d\x41\x61\xbf\x06\x86\x6b\xf3\x2d\x24\xce\x37\xd3\x30\xa2\x46\xa2\xec\x10\x6f\x85\x4b\xd4\x1f\x59\x25\x3a\x7d\x0a\xfe\x94\x90\xa6\x60\x7f\x4c\x8f\xbc\xe1\x05\xfa\xf1\x47\xa7\x3e\xde\x0c\xd4\x71\x78\x2b\x5d\x86\xc4\x44\x57\xa6\x78\xcf\xe7\x66\xb3\x45\xaf\xa4\xfd\x22\xa9\x14\x49\x84\xa1\x34\x7b\xe5\x20\x68\xde\xdc\xfd\x12\xf2\xea\x2a\x39\xc8\xd0\x74\x5f\x3e\x91\x0b\xe4\x75\x66\xfa\x05\x12\x38\xfc\x5e\xa8\x83\xe0\x57\xf1\xd4\x46\xd0\x77\x4a\xbe\xd5\x65\xfc\xc3\x2d\xab\x87\xc5\xdb\xd9\x1e\x48\x7e\x0b\x66\x80\xca\x47\xae\xf6\x16\x59\xfe\xdd\xd1\x52\x01\x4c\xef\x98\xec\xe1\x36\x43\x41\x83\x78\x32\xc1\x94\xfe\xe3\x11\x17\x0d\x40\xd4\xc4\x90\x4b\x2f\x4f\xf4\x50\x44\x51\xc5\xc9\x9b\x6c\xa3\x49\x70\xa9\xbc\x72\xfa\x25\xba\x5d\x3f\xa8\x03\xba\x10\x52\x8a\xd4\x96\x17\x8f\x90\xe1\x81\x71\x41\x5a\x9f\xac\x4f\xcb\x1c\xd7\x07\x28\x0d\x26\x14\x7b\xf8\x01\xc0\x40\x25\x19\xd0\xf0\xa3\x38\x09\x2f\xa8\xac\xc2\x39\x86\x13\x20\xbf\x4a\x95\x72\xbe\x62\x39\x68\xc7\x5a\x2d\x26\xd7\xdc\xa6\x67\xf9\xf2\xcd\x60\x8c\xa7\xb7\x83\xeb\x16\x38\x99\xca\x1c\x2c\xa6\x47\x0a\x3c\x27\x08\x9a\x93\xf1\x46\xe6\x6c\xa4\xa7\x18\x2a\x62\xf1\xb7\xa6\x18\x36\x88\xa3\x0b\x9c\x64\x9a\x0c\x4b\xb3\xdd\x71\x63\x4a\xb0\xf8\xa4\xd6\x7f\x7e\xb7\xd5\x43\x5a\x45\x77\x5e\x15\x2f\x0b\xda\xc3\x2c\x76\xb1\xd2\x51\x5b\x7c\xac\x13\xde\x4d\x2a\x3e\x86\x9d\x68\x10\x89\x24\x56\xb3\x38\x4d\xc3\xfe\x04\xfb\x57\xac\xa3\xa9\xe5\x9c\x9b\xe4\x40\xd9\xf6\xa0\xf4\x1b\x3f\x81\xff\x69\x41\x41\x42\x7d\x4e\x56\x70\x57\xf9\x2d\x1d\x9e\x9c\x95\xbe\xe0\xeb\xae\xee\x17\xe5\x2c\x66\x78\x4a\xb9\x0b\x91\x65\xdc\x85\xff\x2e\x28\x28\x56\x65\xd7\x76\xe7\x72\xd7\x60\x22\xbc\x69\x99\xe0\x2e\x2c\xe4\x7a\xfd\xe8\xfc\xae\x77\xbc\xe6\xae\xa0\xb0\xf0\x96\xbb\x84\x58\x38\x0a\x50\xfa\xae\x7a\x30\xc3\xd1\xf1\xf1\x7b\xab\x5a\x71\x67\x32\x75\xfa\xdd\x82\xd7\x34\xbc\xda\x8b\xf4\x72\x85\x4d\x8f\xe8\x2a\x4e\x97\x5b\xc6\xc8\xbb\x6e\x6c\x56\x62\xf8\x06\x7a\xb8\x09\x39\xd4\xf9\x81\x73\x03\x5b\xee\x95\x01\xbb\x02\xfc\x0e\x47\xa1\xb9\xc6\x73\xe0\x40\x12\xb0\x94\x66\x00\x83\xec\x71\x58\x7a\x51\x4a\x8c\xa3\x98\xbe\x31\x18\x20\xcb\xd9\x8f\xf3\xb8\x47\xd1\x25\x4d\x91\x17\xd7\x74\x6c\x6d\x3f\x47\x2b\x2b\x6e\xdf\x0a\x67\xf9\x6a\x16\xd3\x7c\x43\x3e\x57\x8e\x05\xb5\x3c\xa4\xea\x25\x4c\x5e\x51\x25\x4e\x31\x36\x3e\xab\x2a\x59\x02\x7d\xfd\x4a\xc9\x55\xd6\xa9\xf2\x49\xbc\xe6\xc7\x5e\x4b\x47\xe3\x94\x93\x28\x95\x2d\xba\xd7\xa0\xed\xc0\xd5\x86\xf8\xe9\xbe\xdd\x60\x3d\x77\x11\xa7\x0b\x34\x2b\x2e\x52\x19\xc3\xee\xa5\x0f\x62\xfe\x75\x87\x58\x75\x81\x7f\xc9\x45\xbc\x99\x17\x83\x78\x3a\x0b\x32\xd8\x5e\x8a\x2e\x43\x75\x5b\x30\x36\x31\x45\xfc\x29\xba\x27\xba\x96\xdf\x6d\x90\xbb\x2f\xc3\xc1\x98\xb6\x7d\xcc\xc9\xdb\x43\xc8\x0a\x75\xf9\x78\xa3\x46\xdf\xa2\x78\x61\xee\xbb\x40\x2d\xa3\x46\x5a\xd2\x96\xa0\xfc\xe2\x0a\xd4\x48\xc4\x5d\xa3\x02\x79\xe7\x3a\xc6\x42\x7f\xed\x43\x2c\x29\xee\x55\xb5\x5c\x2a\xd1\x6a\x2c\xed\xfd\x69\xed\xaa\xdd\xec\xd4\x3b\x83\x35\x48\x6c\xd0\x69\x77\x5a\xed\x51\x7b\x74\x56\xe6\xaa\x78\x00\xcd\x1f\x64\x3f\x3c\xe7\xc8\x02\x28\x78\xc7\xc2\x73\xf8\x12\x75\x25\x23\xa3\x61\x6d\x96\xdf\xf3\xf2\xd6\x98\xea\xaf\xb4\xac\xf0\xc8\xd7\x89\xa4\xd3\x5b\x2f\x19\x3d\x66\x03\x5f\xd0\xb7\x58\xc3\xf7\x1b\xc0\xc1\x16\x46\x8d\xa5\x37\x0b\x92\x14\x97\xb4\x85\x9a\x73\x31\x99\xa4\x9a\xe2\x47\x56\x73\x7a\x25\x90\xe2\x88\xc6\xf0\x5a\xb0\xe8\x28\x61\x58\xc8\xe4\xa9\x57\xf3\x20\xf2\xcb\x38\xe5\x30\xcc\x92\x42\x58\xe0\x4e\x70\x9a\x51\xdb\x86\x60\xe2\x58\xa0\x06\xcc\xd3\xda\x19\xda\xd8\x40\x72\xed\xa1\x1f\x7f\x34\xdb\x3d\xad\xb3\x32\x7c\x4d\xfa\x54\x50\xdb\x57\xf4\x02\xc3\x6e\x19\xe9\x1c\xc6\x5a\xfc\x46\x8b\xcc\x94\xa7\x51\x41\xad\x72\x8e\x75\x5d\x7c\xc1\x8e\xe8\x70\x15\x24\x61\xd8\xe5\x2d\xf8\x33\x68\xa0\x66\xde\x5a\x5b\xc5\xb5\x5b\x9d\x7a\xa7\x18\xa3\x70\x1e\x8d\x3c\xc7\xa0\x8a\x72\x3a\xd1\x45\xf3\xdc\xbb\x22\xbe\x08\x2f\x93\x60\x36\x03\x39\x32\xc8\x58\xf3\xaa\xca\x04\x05\x64\xa7\x4f\x15\xaf\xb4\xdc\xd5\xab\xb9\xfa\x58\xae\x6c\xd2\xe1\xc7\xf5\xa9\xa8\x03\xc9\xad\x2f\x7b\x84\xd0\xc3\x65\xfc\x3c\xa9\x9e\xeb\x08\xd4\xde\xb2\xce\x52\x87\xd0\x68\x48\xa9\x46\x1c\x30\xe4\xc5\x8e\xe3\xe0\x94\x17\x22\xca\xf4\x5e\x04\x84\xba\x96\xa8\xa6\x4c\x6c\x6e\x50\x29\x76\xed\x40\xe6\x8d\x79\xd3\xdd\xc5\x43\x55\x2a\x9f\x1c\x47\x9d\x1c\xef\x73\xd6\x34\xb5\x41\x61\xbf\xa5\xdf\xf9\xdf\x24\x86\x8b\x7b\x0b\xdb\xfc\x6b\x37\x30\xb2\x2c\xdd\x1a\x15\x7b\x59\x09\xff\x4a\x5b\x1b\xa1\xb9\x5a\x7a\x4e\x61\x0f\xd7\xa0\x0c\x52\x63\xaa\x13\xbe\x69\xe3\x15\xb1\xda\x3c\xd2\x40\x8e\xb2\xc3\xe1\x1c\xeb\xf7\x62\xbd\x5d\x08\x9d\xa5\xa2\xe7\x6c\xbb\xec\xd7\x95\xe8\x06\xb1\x74\x3e\x71\x05\x40\x73\xfa\xac\x5a\x62\x89\xf4\xcc\x10\x01\x12\x58\x67\x6f\x23\x99\xf4\xa0\x7f\x12\x26\x5c\x01\x5b\x50\x98\xbd\x11\xe1\xb8\xc2\x31\xd7\xb7\x1f\x15\xdf\x4e\xf3\x36\x6d\x6d\x7f\xb5\x0b\x72\xd5\xa2\xe3\x13\x21\x2b\xd1\xb7\x6a\x78\xe1\x28\xa2\xe8\x08\x19\xbd\xd8\x65\xa8\x56\x50\x02\x82\x0b\x51\xbb\x98\xd0\x07\xca\x92\xec\x95\xa3\xb0\xa2\x0b\x34\x2d\xac\x1d\xa5\x15\xbd\x20\x21\xbd\x91\xe3\xb8\x76\x53\xf8\xd8\xc2\xee\xa1\x53\x31\x71\x42\xf1\xa5\x5e\xcb\xa0\x07\xdb\x9e\x54\x02\x10\x3b\x94\x71\xd1\xa4\x3c\x42\x6a\xef\xbf\xe3\x3e\x65\x04\x68\x11\x91\x8e\xbf\xc1\xde\x24\xa3\x2a\x2f\x66\xd3\xdc\x7b\xde\xc1\xa6\x39\xd9\xb1\x30\x0a\x8a\x47\xfd\xad\x59\xf6\x7d\xa3\x68\xee\x4b\xf7\xb8\xa5\x78\x63\x17\x78\x22\x0c\x7c\x83\x5d\x85\x69\x1c\x14\xd5\x82\xba\x98\x0c\xc0\xea\x4e\xc1\x6e\xbf\xe1\xfc\xaa\x22\x2f\xb9\x89\xab\x39\xc6\x29\xec\x0d\x43\x9d\x3c\x6d\x13\xd3\xa2\x2e\xd2\x61\x91\x7b\x93\xc2\x64\x34\x85\x8f\x73\x9b\x10\x4d\x2c\xad\x8d\x71\xb2\x35\x73\xac\xf4\xfb\x17\xd0\x31\x05\x69\x3a\x9f\xe2\xa1\x7e\x9f\x18\x4c\x12\x1c\x0c\xaf\x95\xfd\x4e\x3b\x90\xcd\x23\x9a\xb6\xb2\x40\x44\xb3\xe5\xd8\x9e\x9b\x7f\x2d\x75\x68\x22\x8c\x0b\x4c\xd4\x93\x14\x2f\xcd\xeb\xfd\xfa\xa2\x79\xb4\x2c\xac\xbf\x50\xe2\xb6\x48\x9e\xaa\x90\x0e\x38\x15\x20\x41\xfc\x6e\x1e\xf0\xc9\xd2\x29\xa9\xab\x87\x55\x76\xa5\xf2\x66\xb1\x6b\xd4\x45\xb8\x20\x84\x0d\xb7\x09\xa1\xec\xc9\x5e\xaa\xe6\xc5\x06\xca\xd5\x8e\x32\x68\x39\x4a\x51\x4b\x33\xe1\xbc\x21\x79\xe7\x36\x91\x58\x74\x65\xf2\x65\x38\x82\xfb\x12\xfa\x6f\xfe\x65\xc9\x22\x2b\x0c\xfb\xc2\xe4\x1d\x85\x4e\x5a\x29\x76\x4f\xb2\x45\xc0\xc3\x9d\x3e\x69\x8c\xac\xe5\xbd\x5f\xb8\xc2\x60\xc6\xe2\x05\x15\x57\xc7\xf2\x1a\xcc\xf2\x82\x3d\x80\x9c\x42\x9a\x01\xc0\xf9\x5e\x21\x32\x50\x39\xa6\xb6\x15\x61\xc4\x2c\x79\x99\x1d\x00\x33\x99\x39\xc7\x11\x18\xf3\xe6\x43\x13\x51\xca\x3d\xc0\x68\xe8\xec\x7c\x58\xb6\xce\x00\x54\x58\x8a\x90\xb4\x89\x3a\x2d\x30\x39\x86\x0f\xdc\x7e\x76\x6f\x84\xe2\x69\x48\x64\x84\x0a\x0a\xe8\xa7\xcb\x70\x32\x41\x7d\x2c\x1a\x1c\xa2\x24\x88\x86\xf1\x74\x72\x7d\x4f\x87\x7b\x6a\x35\xc1\x86\xa9\x82\xf6\x7e\xa9\xc0\x94\x92\xc6\xbf\x01\x17\xa2\x93\x3c\xb4\x59\x90\x42\x8d\x55\x7c\x85\x07\xf3\x0c\x97\x56\x78\x34\xaa\x95\x0a\x4b\xdc\x51\x61\xe6\x5b\x1e\xb1\xe8\x9e\xa0\x57\xd0\x0a\x19\x0e\xf2\xff\x2b\xfe\x33\x33\x05\xa3\x72\x37\x4e\xcd\x15\x4e\xa2\x15\x46\x5d\x54\xb1\xe9\x36\xea\xa7\xd3\xcc\x66\xd9\xa3\xa8\xfe\xc1\x7b\x95\x64\x29\x91\x29\x9c\x52\xa7\xb5\x6a\xa5\x35\x77\xb8\xd5\xd1\xa5\xad\xac\x6b\x5b\x5a\xa1\xf1\x66\x69\xe2\x01\xa9\xc0\x15\x31\xee\x64\x1a\x64\xb6\x90\x6e\xca\x55\x96\xc8\x5b\x19\x0f\xc0\xdf\x19\xb0\x96\xd0\x66\x96\x8f\x01\xd8\x4d\x5b\x6a\x72\x91\x0c\x9a\x29\xc8\x79\x32\x59\x3e\xe6\xe8\x27\x5b\x9f\xad\xa5\x86\x96\x29\x9c\xdd\xce\x52\x47\x4c\x94\x5a\xf2\x30\x2e\x8f\xd4\x42\x8a\xbe\x9d\x56\xdb\xa5\x19\xd0\x54\xdc\x43\xc6\x97\x39\xcb\x33\x58\x72\x45\xc0\xf2\x88\x5f\xb7\xd7\x87\x3b\xa2\xc4\x09\x85\xb8\xfb\x9b\x4b\xc3\xf5\x80\xfa\xf1\x77\x5b\x3b\x37\x88\x6c\x9f\xdc\x82\xd2\xb5\x0b\x4b\x29\x8f\x33\xdb\xfc\x2d\x6e\x29\xad\xb8\xa3\xc3\x7e\xe7\x87\x2f\xc3\x51\x57\xd9\x9e\x15\x0a\x59\x52\x3d\xce\x5c\xaa\x96\xd9\x97\xbf\x0f\x7d\x79\xae\x74\xf0\x1d\xa8\x23\xfe\x26\x6a\x73\xc7\xe2\x2b\xa4\x49\x5e\xe1\x43\xed\x0b\x2b\xfb\xf0\x0d\x57\xd0\x9f\x0f\xac\xc1\x96\xdb\xd1\x37\x52\x38\x18\xbb\x6b\x9c\xf9\x94\xbb\x2e\xd9\x85\x80\x27\x62\x0b\x17\x57\x14\xec\xe9\xf0\x0a\x19\x83\x3d\xd3\x6d\xcf\xe7\xdd\x49\xc5\x58\xda\x37\xab\x4b\x55\xd8\x62\x35\x0c\xaa\xce\x90\x04\x5e\xc5\xbc\xa6\x2f\xf1\x5f\x67\xa8\x01\x20\xac\xf9\xd1\xdb\x57\xf4\xf8\x16\x1a\xfb\xe1\x15\x4d\x06\x02\x15\x9c\x43\xaa\x9c\xad\xa9\x61\xa6\x06\xdd\xa7\x37\x71\x9e\xf8\xee\xa0\x0f\xfe\x0b\xf8\xf1\x3d\x2b\x88\xbf\x77\xc6\xfc\x3d\xea\x89\x5d\xcc\x70\x59\x45\xf1\x9d\x18\xe3\xbd\xa3\x68\x2b\x8a\xef\x8b\x71\x17\xd4\x13\x7f\x73\xde\xfd\xcd\x95\xc5\xdf\x7e\xab\xa8\x68\xb6\x3d\x9e\x13\xda\xfd\xed\x1d\x85\xf4\xe1\xfe\xfb\x0b\xd7\xd6\xa1\x8e\x6f\xc1\xdd\x23\x4f\x41\x2e\x55\x79\x22\xd3\xa5\x9a\xd2\x92\xe5\xaf\xbc\x39\xab\xb4\x9b\xdf\x6b\x52\xca\x7b\xcf\x41\xb9\x6c\xee\x49\x2d\xe7\xa4\x85\x98\x9d\x7e\xd2\x48\x3b\xc9\x2b\x7a\x12\x4f\x82\x7e\x54\x02\x17\x3f\xf5\xe4\x93\xfb\x41\x36\xae\x20\x47\x0a\x4a\x79\xbc\x7e\x1f\x0f\x82\x09\x9a\xc5\x93\xeb\x51\x38\x41\xf1\x08\xd1\x4d\x8b\x9d\xe2\x1d\x47\x5e\x16\xdb\x7e\x43\x2f\x68\x34\xac\x31\x26\xf1\x7a\x87\xbc\xbf\x79\x65\xc7\x0e\x52\x6c\x2d\xfb\x9f\x2d\xa6\x06\x36\x82\xf3\x3e\x99\x41\x93\x88\x77\xaa\xb3\x24\xce\x62\xf2\x09\x6d\x90\xd3\x87\x59\x80\xd5\x43\x1b\x28\xc2\x97\x04\x81\x7c\x08\xd1\x7c\x32\xf1\x2c\x14\x81\x81\x5c\x26\x4a\xbc\x23\x57\x24\x4f\x3e\x27\xf9\x4a\x6e\xaf\x62\xfb\x7d\xd8\x4f\x82\xe4\x7a\x91\x8e\x5c\xc9\x0f\xea\x05\x05\xd9\x42\x99\xd6\x93\x08\x17\xbc\xcb\xc1\x04\x85\xd1\x18\x27\xa1\x16\xc0\x55\x8b\xe8\x60\xe6\x19\xb5\x23\x8c\xda\xd3\x59\x20\xec\x1f\x8f\x31\x0c\xee\x71\xc2\xcf\x60\x1c\x64\x1c\x21\x16\xca\x83\x8a\x41\xd6\xa9\x12\xa1\xbc\x38\x80\x5c\xee\x8a\x2f\x70\x92\x84\x43\x9c\xa2\x43\xaa\x10\x09\x71\x4a\x19\xf8\xec\x1a\x85\x11\xcb\x66\x2c\x11\x28\xd0\x82\x99\xab\xe1\x64\x59\x00\x96\xcc\xe5\x29\xb7\x4c\xd4\x40\x32\x51\xfb\xd7\x27\x94\x84\x35\xe9\x26\xc7\x24\x51\xf5\x17\x0b\xf1\x64\xd8\x45\x2b\x90\x29\x6b\xc5\x34\x1c\x71\xb7\x49\xfe\xa6\x38\x1b\xc7\xc3\x5c\x1f\x79\xa5\xb4\x19\x23\xdf\xe5\x78\x86\x90\x1d\xce\x90\xa2\xaf\x19\x64\xf3\x79\xf5\x06\x31\x9c\x05\x97\x91\xfd\x45\x61\x24\x44\x58\x90\x69\xf5\x7c\xe6\xc4\x9b\xf3\xf3\x29\x8e\x1c\xa6\xc3\x64\x47\xc9\xc7\x02\x49\xe6\xc3\xce\x5d\xb2\xbc\x33\xfd\x83\x13\x01\x66\x26\xc5\x5d\xbf\x42\xe1\x58\x9a\xb8\x71\xfa\x81\x37\x39\x0e\xd2\x83\xcb\x88\x91\xfd\x75\x69\x85\xd4\x5c\x29\x0b\x9f\x27\xf2\x08\x9b\x20\x2f\x4f\x5e\x2c\xec\x07\xad\x95\x3b\xdd\x8e\x5a\xff\x4f\x3a\x9f\x11\x51\x2b\x0a\xb3\x6a\x40\x84\x53\xb6\xf5\x05\xc9\xf9\x9c\x8c\xae\x73\x3c\x90\x23\x83\x42\xce\x38\x49\x8f\xdb\x64\x25\x45\x92\xa3\x87\x54\x29\xcc\x27\x9d\xae\x52\x1b\x82\xda\x41\x6d\x3f\xf0\x6c\x3b\x88\x2b\xc6\x47\x38\xc1\xd1\x80\x34\x00\xe3\x3c\x33\xd7\xab\x35\x0c\x4c\x2e\x76\x01\xf4\xee\x33\xc8\x95\x1a\xc3\xc5\x54\xb7\x61\xa5\xa4\x2a\xd3\xa4\x2a\xef\x79\x44\xc7\x01\x26\x90\xae\x5a\x3b\x04\xea\x26\x9f\x0f\x99\xc1\xa6\x54\x16\xd7\x70\x44\x94\x86\x90\x72\x00\xa4\x54\xfe\x3b\xf3\x4a\x1e\xb1\x1c\x6d\x30\xb6\xc9\xef\x2c\x16\xf2\x22\x5a\x2e\x9f\xe3\xd9\x8d\xc0\x92\x93\x71\xb2\xed\x95\xcb\x23\xa8\x2b\x6b\x84\xbf\xd3\xd7\x89\x97\x6a\x78\xf1\xdb\x90\x4d\x9e\xbb\xba\x67\xae\xd0\x01\x63\x66\x2c\x49\x00\x90\x14\x98\xd0\x0f\x87\x28\x8d\xa7\x98\xa6\x9e\x42\x97\x63\x1c\xa1\xeb\x78\x9e\x08\x33\xfb\x80\x88\xb3\x14\xf8\x3d\xc7\xce\xbd\xeb\x2e\x68\x3a\x3a\xe7\xed\x65\x88\x32\x80\x6a\xd5\x1e\x19\x31\xf4\xb7\xdc\xee\x16\xa2\x51\x68\x4e\x7b\xf1\x8c\x08\x3b\x33\x29\xf7\x30\x79\xe7\x0e\xe2\x94\x02\x0c\x34\x4c\x9a\x4c\x35\x05\x4d\xe4\x3d\x4f\x29\x5b\x9d\x74\xff\x2c\x2a\xbf\xdc\x72\xdc\xa1\x11\xed\x12\x5b\xf4\xcf\xb9\xc6\x45\xc4\x43\x7e\xd9\xf6\x21\x98\x82\xd1\xc4\x82\x7a\x88\x6d\xd5\xb2\x98\xb9\x59\xab\x00\xcb\xb9\x5b\x2c\x99\xce\x53\xb5\xf8\x19\xda\x50\xda\xd7\x3f\x2d\x91\xba\xc8\xb3\xc9\x6e\xa3\xcb\x38\x5a\xc9\xa8\xfc\xcc\xdd\x1d\x95\xe0\x85\x93\x38\x9e\xa1\xa0\x1f\x5f\x38\xb6\xc1\xfc\x2e\xaf\x70\x68\x2b\xfe\x0e\x03\x17\x15\xad\xaa\xfd\x14\x6f\x0b\xe4\xd5\x2a\xb4\x78\xc4\xe1\x04\x7a\x0a\xf6\x2f\xcb\xac\x1b\xd7\xc6\x37\x98\xc4\x11\x7e\x00\x8e\x07\x70\xd1\x86\xdc\x43\xe0\x45\x81\x9d\x8c\x14\x5b\xb8\x91\xa9\xb9\x48\x74\xe1\x88\xf3\x53\xa7\x3d\x99\xfb\x8c\xec\xbc\xdd\x8f\x50\x00\x9e\xb7\x46\x2c\xc2\xdc\xc8\x42\x56\x9c\xf7\x7c\x10\xae\xf0\x34\xc2\xf8\x41\x0f\x87\x98\x86\xe7\x51\x38\x0a\x07\x41\x94\xb1\x80\x92\x21\xed\x3d\x80\xa4\xed\xb8\x8e\xc9\xbf\x2a\x1e\xc4\xf4\xac\xac\xbe\xb9\x87\xb0\x31\x76\xf3\x26\x59\x78\xc2\xe0\xab\xa6\x57\x0b\xc6\x1a\x39\xcd\xc2\xc4\x48\x19\x37\x18\x0b\x07\x0d\xdf\x5b\xaa\x17\xd5\x3f\x5b\xdb\xd8\x2d\x5b\x18\x8f\xf6\xbf\x38\x80\xd3\xda\x55\xad\x56\xab\xd7\x1a\xb5\x66\x05\xd5\xae\x6a\xad\x5a\xbb\xd6\xa9\xad\x9d\x3d\x18\xe0\x0a\xea\x14\x0e\xbd\xc2\xc2\xd7\xf1\x19\xb1\x56\xec\x25\x73\x08\x86\xe5\xca\x1f\xe8\xbf\x5f\xbf\x42\xcc\x5e\x43\xd4\x18\xa1\x92\x98\xde\x1f\x36\x1c\x8a\x42\xf5\x0f\xa0\x2a\x46\x43\xfc\x67\x61\x63\x52\x13\x00\x25\x8f\x09\x8e\xce\xb3\x31\x35\x3d\xf2\x72\x91\xe2\x31\x63\xe4\x42\x59\x2e\x52\xcc\x76\x34\x88\x87\x84\xde\x31\xfd\x61\x92\x3b\xbc\xce\x8f\xfd\x29\x08\x00\x47\x83\xea\x2e\xbe\xf2\xb7\xb9\x28\x80\x4c\xa1\xd5\xbe\x74\x70\x17\x49\xac\x05\x22\xbb\x38\xe2\x1a\x2c\x0a\xeb\xe2\xa8\xa2\x0d\xc9\xc7\x6c\xb4\xbe\x54\x34\x17\x36\x15\xde\x58\x2e\x7c\xaa\xbe\x7e\x45\xbb\xf8\x2a\x37\x7c\xcb\x02\x02\x1a\x04\x19\x8e\xd8\x9e\xaf\x53\x90\x87\xf9\xfb\x09\x49\xb9\x87\x95\x03\x7e\xc2\xb8\xa1\x42\x99\x90\xe6\x77\xd9\x7b\xdd\xa2\xb8\x14\xa1\x0d\x81\x5d\x9d\xc7\xcf\x10\x6f\x1a\xfe\x94\x66\x50\xd2\x64\x4a\x34\xb0\xf3\x72\xe1\x48\xc8\xc0\xfe\x6a\x31\x2c\x87\xaf\x62\x36\x0e\x44\xa8\x03\x49\x62\xfe\xd2\x61\x7a\x2c\x79\x8c\xc6\x73\x3c\xc0\x8f\x75\x96\x44\xe1\xcb\x3a\x56\xa7\x7a\x93\x60\x3a\x43\xf8\x0a\x22\x49\xf6\x43\xb3\x73\xf4\x5e\x95\x94\xb1\x6f\x1b\xe8\x7d\xea\xc0\x15\x24\x45\x43\xfc\x5f\x9e\x40\xe9\x50\x9f\x88\xa4\x11\x86\xad\x16\x05\x19\x0a\x50\x16\x4e\x1d\x12\xb7\x2b\x24\xbb\xda\x5d\x7f\x52\x08\x75\x70\x48\x51\xb4\x41\xd0\x63\xb3\x70\x1a\xf2\xa8\xd8\xe4\x9f\x52\xa3\x85\x5e\xa0\x52\x48\x31\xfe\x09\xad\x97\xcb\x22\x5a\xb6\x57\x8a\xa7\x70\xf4\x1e\x3f\x47\xa1\x08\xb7\xfd\x75\x43\x36\xfd\xfa\x35\x6f\xc3\x51\x5e\x34\x5a\x40\xf0\xf7\x6e\x4b\xea\x98\xd2\xc5\x75\xa7\x31\xf5\x47\xb9\x2f\xda\xfd\x0d\x64\x0f\x76\x91\x8c\xc1\x36\x15\x8a\xcd\xf6\xf9\x86\x8e\xa6\x2b\xc7\x4a\x10\x46\x41\xdf\x3c\x79\x28\x07\x80\xa2\xec\x94\xc6\xe0\x20\x42\xa0\x26\x18\x86\xd9\x5d\x45\x41\xb9\x38\xc5\xea\xf2\x30\x29\xf2\xb9\x68\xe8\x5e\x07\x6b\xb2\xe5\x28\x57\x5c\x24\x2f\x93\x71\x33\x0c\x87\xa8\x76\x2a\x60\xf0\x38\xf3\x1b\xb0\x74\xe8\x1f\x90\x7e\xb3\x41\x48\x3f\xd5\xf8\x82\x83\xe0\x35\x51\x6a\x03\xed\x07\xd9\xb8\x3a\xc0\xe1\x44\xd6\x5c\x45\x4b\x44\x24\x72\x9f\x7f\x0b\xed\x3c\x1e\x73\x24\xeb\xf8\x7b\x5b\xbb\x4f\x76\xdc\x55\x69\xc1\x3a\xef\xea\xb4\xb0\xe8\x9c\xab\x82\x85\x93\x1a\xc5\x55\x8d\x7e\x6e\x9f\x9c\xab\x36\x8d\x30\xf3\xfb\x9a\xd7\xa4\x8e\xd4\x5b\x7e\x0a\x14\xb1\x61\x14\x4e\x26\x3c\xec\x2c\x73\x93\x80\xf3\xd6\x62\xa1\x84\x1f\xe6\x22\xd7\xa1\x57\x05\xe5\x75\xf1\x29\x34\xcb\x0c\x52\x21\x42\xb9\x2f\xe3\xb3\x02\x47\x30\xe6\x0a\x52\xf7\x9f\xb4\x68\x09\x95\x4c\x22\xf7\x11\x4b\x65\x0f\xf6\x81\x8a\x7c\x4d\xf4\x1b\xf2\xe9\xa7\x4b\x7f\x94\xf9\x4f\x97\x68\x83\xfc\xd7\x93\x40\x6d\xfa\xe9\x0f\xb2\xcd\x5c\x35\x83\x21\xee\xac\xf7\xcd\xf0\xeb\xa2\x58\x90\x7e\x41\x2a\xe7\xc8\xb9\x27\x28\x70\x77\x47\x5b\x2d\xd5\xae\x5e\xd6\x3a\x2f\xd1\x4f\xa4\x0b\x7f\xc0\x9e\xbe\xb3\xb3\xb3\x53\x46\xcf\xe9\x8b\x9f\x7f\x46\xb5\xab\x7a\x0d\xb6\x7b\x82\x80\x67\xbb\xa7\x5d\x2c\xd5\xae\x5a\x9d\x76\x8d\x02\xbb\x34\x81\x5d\x16\x05\x06\xc3\x8b\xd3\x39\x78\xfa\x94\x00\x8d\xd7\xaf\x69\x4d\xf4\x1c\xc1\x48\xe7\xd6\x67\x75\x57\x37\xa0\x0e\xfb\xcb\x2f\xfb\x7c\x03\xd5\xaa\x6d\x6f\x19\x18\x53\x56\xf4\x27\x6a\x6f\xc3\xa9\xad\x8c\x7e\x46\xd5\x36\xfa\x0f\x54\x47\x5d\xf4\xa2\x5e\x44\x44\xb1\x38\x87\x2e\x6e\x54\x50\x32\x08\x06\x63\xcc\xb2\xeb\x2c\x16\x38\x48\xcd\x4f\x84\x1e\x93\x52\x89\x56\x25\x47\x25\x0d\x49\xb2\x9b\x28\x83\xe1\xbe\x62\xa2\x55\x37\xd0\xa7\xa4\x44\xcb\x03\x41\xae\xf5\xd7\x1c\x7d\xba\x94\x39\x7c\x4a\xa2\xbc\x84\x8f\xbe\xa2\x5a\xc1\xb0\xe6\x11\xbe\x54\x9c\x9d\xe0\xd6\x91\x29\x40\x22\x9e\xbe\xe7\x89\x31\x92\x6e\xe7\x53\x76\xb4\x5f\x64\x48\x83\xa3\x01\x18\xd2\xd0\x7f\xdd\x86\x34\xbb\xf8\xca\xd6\x04\xb8\xc0\x91\x82\x1b\x14\x68\x95\xfe\x2e\x16\x7f\xd3\x54\x5f\x8c\xf1\x55\x61\x15\x46\x81\x93\xe7\x92\x51\x35\x0b\xb5\x7e\x5f\x8c\x7c\x8c\xaf\xec\x10\x9a\x6c\xfc\x94\xa3\xfd\xe2\x44\x42\xce\xc0\x99\xb7\x3d\xa6\x5e\x16\x3e\x79\xa6\xcb\x1e\x23\xe9\xac\xdb\x80\xc6\xf8\xaa\x37\x0e\x92\xc2\x79\xb6\xd2\x85\x07\x3a\xc8\x91\x16\xd2\x83\xdc\xe5\x1d\x0f\x71\x1c\x3b\xb6\xc6\x01\x2c\x01\xd2\x2a\x4b\xb5\x4f\xbd\x53\x76\xf1\x3b\x57\x55\xd2\x4e\x6d\x94\x5f\xd7\xc3\x20\x04\xb8\xcf\x71\x18\x95\x56\x56\x6e\x11\x71\x53\xa1\x70\xba\xde\x96\xd1\xf4\xf0\x95\x42\x09\xb7\xf8\x82\xf1\x08\x4f\x7f\xbd\xd4\xc4\x17\x1b\xb5\xd9\x16\xeb\xb1\x78\xa4\x4c\x5a\x65\xb9\x44\x29\xb4\xce\x7b\x7e\x74\xa1\x8f\xec\x28\xb3\xcc\xaa\xb9\x5c\x26\x35\x9d\xda\x28\xdb\x42\x1b\x39\xf9\x31\xe9\x6a\x69\x82\x66\x02\x3a\xbd\x17\x65\xac\xb3\xd5\x74\xde\x4f\xb3\xa4\x14\x56\x50\xa3\x5c\x81\x24\x7c\x52\x65\x41\x56\xd4\x7a\xd9\xe5\x80\xbb\xf4\x9e\xa7\x0d\xd3\x2a\x6a\x14\x75\x9f\x7d\x1f\x64\x61\x54\x2f\xb6\x69\xb1\xb2\x7c\xdf\x12\x8f\xb7\xdb\xba\x58\xf5\xbf\x6e\xf7\x2a\x8a\xc0\x7d\xad\xa9\x09\xb4\xe7\xde\xc3\x28\x2e\xff\xa3\xb6\x31\x3a\x1c\xdf\xf1\x4e\xa6\x20\x48\x77\x24\x3a\x75\xd5\x51\x12\x4f\xc9\xdb\x5e\x3c\xc4\xb0\x49\x15\xdd\x90\x54\x80\x77\xd8\x93\x34\xba\xbd\xfd\xb6\x24\xc8\x71\xa9\xc5\xf0\x5d\x6f\x4e\x6c\x15\xd1\xfd\x49\x5d\x6e\xc5\xb7\x28\x51\x6b\xb9\x5d\x4a\x54\x13\x1b\x95\x78\xf3\xd0\x7b\x95\xd1\xf4\xa2\x5c\xce\xa1\xa2\x45\x97\xbd\xad\x0e\x18\x41\x6f\x66\xa5\x90\xaf\x09\x73\xab\x72\xeb\x16\x97\xde\xaa\x0c\x84\x8b\xee\x54\x1f\x4f\x76\x5e\xac\x17\xdb\xa8\x3e\x66\xa3\x75\xb1\x4d\xb1\x87\xdb\x6d\x52\xb4\xd1\xbf\x6e\x8f\x2a\xd8\xfe\x7d\xad\xac\x79\x36\x5a\x77\x6f\x50\x64\x14\x1f\x72\x7b\xca\x92\xeb\x1c\x03\xa3\x21\x26\x47\xf4\x8f\x47\x7b\x3d\xee\xe9\x54\xc2\xe9\x20\x98\xe1\x52\xce\xc6\x69\xb3\x65\x34\x08\xb2\xc1\x18\x95\xec\xf4\xd1\x80\xc2\x38\x89\x2f\x81\x6e\x21\xe3\x4a\x69\x65\x3f\x98\x8c\xe2\x64\x8a\x87\x6c\x1a\x86\x41\x16\xd8\x29\xe8\x96\x67\xe0\xea\xa4\xde\x9e\x7f\xb3\xb9\x5a\x86\x4c\xbe\x6b\xe6\x0d\x14\x46\x59\xb7\x24\xc3\xe2\x8c\x9b\xd5\xf1\x19\x03\x68\x5b\xc3\x3c\x62\xd4\x43\x2d\x04\x34\xba\xe2\x70\xca\x85\x03\xd0\x88\x14\xbc\x90\x0b\x13\x0f\x59\x36\x33\xc5\x0b\xdd\x9b\x89\x57\xb1\x93\xbd\x56\x52\xa2\x4d\xe7\x69\x86\xfa\x18\x85\x64\x44\xa7\x38\xca\x68\x9e\xb5\x00\xae\xd7\x13\x9c\x09\x8f\x85\x42\xb9\x7d\x8d\x3c\x9d\xba\x72\x9f\xe6\x38\xa4\xae\x55\x32\x41\xfc\x17\x3c\xcb\xd0\x3c\x9a\xf1\xa4\x81\x7a\x76\x50\xc5\xa6\xa5\xe6\xe0\xbe\x6f\xd8\x38\x40\xa6\xc1\x4d\x31\x0a\xc2\x4b\xcc\xf7\xb9\xa0\x19\x1c\x64\x77\x65\xd6\x3c\xc6\x48\xaf\xb0\x24\xda\x2c\x89\x69\x16\xa3\x30\x4b\xb9\x57\x0c\x22\x14\x7c\xd7\x3b\xa6\xbe\x13\x79\x9a\x10\xd7\x7f\xc9\x54\x28\xeb\x2e\x33\xef\x43\x60\xa5\xec\xb2\x19\x80\x0c\x9c\xcc\x53\xd1\xd8\x59\x4d\xa6\x44\xcb\x47\x5b\x41\x16\x70\x61\xbd\x56\x54\xd2\xdc\x1c\x0e\x53\x68\x83\xe7\x05\xf7\x8c\x34\xa3\x85\xe2\x9b\xa2\x08\xb2\x60\x65\x1e\x67\xc6\x2e\x88\xae\x79\xe6\x04\x40\xf9\x25\xf5\x29\x09\x14\x0b\x4a\x6a\x4f\x0c\x1c\xef\x61\x26\xf3\x13\x45\xa7\xb4\x62\xf3\xfb\x42\xf5\x16\xef\x8d\xac\x64\x91\x64\xe6\xb6\x7b\xbd\x4c\x47\xa7\x06\x14\x55\x06\x88\x05\x13\xd5\x41\xa9\x3e\xce\x40\x46\x0b\xe2\x44\x32\x5a\x53\x98\x32\x60\xb8\x38\x52\xda\x26\x74\xcd\x47\xbe\xdc\x94\xc8\x05\xcc\x22\xda\xe7\x1b\x7a\x92\xf4\xa2\x14\xcc\x73\x9d\xa6\x28\xb8\x08\xc2\x09\x44\xec\xa2\x7c\x01\x98\x9d\x9f\x6a\x4e\x14\x67\x95\x30\xba\x88\xbf\xe0\xd4\x4c\x32\x5c\x62\xc9\x81\x2b\xe8\x72\x1c\x0e\xc6\x4e\x56\xdd\xbf\xce\x61\xd5\x76\xab\x7c\xa1\xf4\xe3\x78\x82\x83\xe8\x06\x0d\xe3\x9d\xc9\x3c\x1d\xa3\x5f\xc7\x38\xa3\xf1\x4c\x78\x2e\x5a\x70\xd7\x9a\x05\x09\x30\x0a\xf6\x4a\x72\x6d\xc1\xae\x6f\x11\x0e\x44\x70\x7a\x18\xf1\xbb\x6f\xf3\x02\xe0\x16\x25\x24\xdf\x9a\xe1\xa9\x72\x7d\x71\x39\x96\x04\xe3\xce\x14\xac\xc7\x5a\xa5\x45\xb5\xc5\x47\x07\x7c\x49\x9d\x09\x5b\x22\x92\xb8\x1d\xda\x12\xf2\x9a\x1b\xa7\xc1\xc8\xfa\xd4\x2a\xe4\xa3\x62\x68\xe6\xa3\x7b\x5e\x5c\xca\x0a\x1b\x46\x4a\xe6\xbc\xc2\x1c\xba\xac\xed\x8e\xe8\xd7\x8b\xe7\x51\xc6\xe9\xcb\xc1\x4c\x08\xd0\x88\x26\x12\x3e\x82\xb8\xc5\x1b\x3a\xfe\xab\x46\x93\xaf\x6c\x5e\xe4\x1b\x72\x86\xc1\x51\x3c\x8f\x86\x68\x3e\xa3\x0e\x85\x83\xc9\x7c\x88\x0d\xba\xb7\xab\x19\x18\x49\x23\x17\xf5\x43\xf1\xd8\xb6\x02\x8b\x61\x7c\x19\xa9\x78\xc4\xd1\xe4\x1a\x8d\xe6\x62\x51\x3a\x22\xe9\xaf\xae\xa2\x09\x4e\xa9\x53\xa5\x5b\xd6\x02\xbe\x91\xe0\x69\x10\x46\xba\x70\x55\xac\x5f\xd3\xe0\xaa\xa4\xf5\x0b\x2e\x4e\xd1\x0b\x57\x66\xf6\xca\xe2\x2b\x55\x31\xe7\x54\xf3\xe0\x9b\x72\xa0\x64\x8e\x87\xd6\xfa\x4f\x48\x21\x40\x1f\x3d\x01\x6d\x78\xc9\x89\x7c\xd5\xfb\x18\x46\x25\xb5\xc9\x9f\x50\xab\xa2\xd1\x99\xcb\x7c\x92\x67\xf0\x76\x11\x09\xa1\x3b\x05\x60\xbe\xdb\x16\xe5\xf3\x54\xcd\xc2\x7e\xbf\x56\x47\x40\xbc\x7d\xae\xac\x27\xaf\xd1\x04\xc1\x0c\x27\xe4\x34\x29\x36\x86\x17\xf2\x80\x00\xce\x90\xee\x8a\x8c\xbb\xe8\x7b\x90\xe0\x2a\xae\x5c\xf5\xbe\x39\x46\x5a\x0a\x2c\xc9\xf0\x61\xca\xed\xa2\x1a\xf7\x55\x59\x98\x99\x0c\x4b\x1d\x51\x07\x1a\x1a\x27\x43\x2f\x36\xd4\x99\x5e\x4c\x95\x3c\xb6\x68\x1e\xb6\x7e\x85\x93\x8e\x7f\x45\x6d\xfa\xae\xc6\x6e\x85\xb3\x50\xe6\x3a\x79\xdd\xd1\xca\xcd\xb3\x1b\xfe\x45\x26\x6f\x9f\xac\x0d\x51\x62\xe2\x9c\xb1\x5c\x8b\x37\x9d\x87\x89\x93\xa6\x27\x13\x3d\x3f\x83\x8f\x83\x14\x32\xe4\x7a\x4f\xdc\x0b\x53\x91\x4b\x76\xad\xfa\x40\xd1\x49\x67\xd0\x69\xd8\x35\x9c\xa2\x38\x52\x8e\xc2\xf5\x0e\x2a\xb5\xeb\x0d\xb0\x64\x2d\x3b\x8e\xc5\xbb\xb4\x32\x3f\x06\x8b\x47\xf7\x79\xf8\x5e\xa2\xbe\xe6\x65\x20\xcb\x0d\x98\x9a\xe7\x6a\x46\x07\x61\x89\x9c\xe4\xb7\x8d\x6e\x47\x1a\x42\x34\x44\xf2\xa2\x20\x77\x85\x6d\x48\xc4\x1c\x68\xa1\xdb\x8e\x77\x37\x1b\xed\x8e\xdb\x49\x2c\x2f\xd5\xf5\xad\x23\xac\xf1\xd8\x6a\xc5\xc3\xac\x1d\x63\x11\xde\xc3\xaf\x21\xb0\xd5\x10\x0b\x2c\xb1\xa5\x26\x85\x2f\x9c\xfb\x57\x99\x30\x7a\xb9\x0f\x15\x09\x20\xac\xaa\x78\xf4\x12\x9e\x95\x04\xa0\x35\xe6\x65\x4b\x0d\xe6\xde\xcc\x86\xc3\xb1\x31\xf3\x0d\xf9\x68\xb9\xb1\xfe\x38\x1b\x02\xcb\x50\x07\x9b\xa6\xe5\x2f\x9e\xb1\xcf\x1b\x41\x98\x02\x37\xe3\x08\x17\x76\x21\xa2\xac\x88\xf9\x0f\x2d\x5c\xde\x4b\xcc\xf9\x1c\xf0\x2a\xad\x30\xa4\x5c\xba\x14\xbd\xe4\x62\xd5\x09\x2d\xa8\x12\x8a\x36\x06\x9e\xf5\xe8\xd1\x48\x30\x85\x8d\x0e\xc1\x41\x1e\x6c\x7c\x89\x90\x4e\xf0\x75\x81\x52\xce\xb1\xb6\xf8\x7b\x6f\xbe\x13\x3b\x2c\xc9\x4d\x2a\x70\xf1\x32\x48\xf4\x21\x06\x94\x83\x8c\xe6\x8b\x67\x35\x65\xcc\x50\x14\xa6\x08\x8f\x46\x78\x90\x85\x17\x78\x72\x8d\x02\x34\xc4\x69\x96\xcc\xe1\xb9\x02\x72\xfa\x8b\x38\x1a\xe0\x42\x51\x46\x0b\x52\xa8\x96\xe8\x01\x50\x92\x01\xb9\xa1\xc4\xf2\x9a\x0b\x32\x08\xf7\xb4\x33\xa0\x0d\x4e\x8e\x22\x99\x90\x47\x2d\xe1\x29\x9d\x47\xe8\x39\xd5\x16\x53\x3d\x2f\xba\x14\xdd\xef\x38\xc6\xd7\x3e\x10\xe5\x83\x41\x8b\xd6\xca\x22\x01\x7e\x09\xce\xaa\x8c\x10\x67\xb2\x3b\xca\x3c\x38\x17\x0f\x29\xef\x5b\x3c\x4a\xf2\xbb\x76\xbd\xb1\xda\x6c\x14\x13\xf3\x53\xa6\xf1\xd1\xe2\xdf\x07\x6c\xd2\x56\x44\xe0\xa4\x30\xca\x70\x32\x52\xac\x85\x91\x77\x55\x70\xfe\xca\xba\xce\xa9\x96\x6e\xb7\x2c\x3e\x62\x80\xc6\x78\x32\xc3\x09\x11\x7f\x0a\x2c\x82\x1d\x86\x1b\xf3\x0d\x36\x51\xfe\x06\xf7\x78\x54\x66\x32\x9d\x2a\x68\x57\xab\x9f\x68\xaf\x76\xa1\x4b\x25\x97\xb0\xe5\xd7\xcf\xa9\x55\x35\xe3\x41\x00\xed\xbb\xdf\xb3\xd6\x85\x3b\x00\x2e\xd2\xcf\x8b\x6c\x25\xc2\x61\x51\xcf\x22\x26\x33\x5c\xea\x14\xbe\xfc\xb1\xd1\x49\x4f\x84\x25\xef\xee\x6f\xf6\xee\x9f\x9e\x88\x08\xcd\x83\x52\x90\x16\x18\x5d\xfd\x2d\x68\x6a\x77\x1a\x0c\x0a\xd1\xd5\x34\x18\xdc\x85\xb6\x44\xf5\x3b\xd1\xd7\x17\xec\x56\x21\x29\xf4\xd5\xfb\x04\x68\x91\x79\xa0\x44\x46\x1b\xa1\x75\x97\x23\xb6\xdc\xe3\xaf\xd0\x24\x2d\xf0\x61\x20\xd8\x80\x13\x03\xfb\x21\xbd\x18\x78\xa6\x16\x08\xe9\xbb\x1f\x64\x63\x1a\xd6\xf7\x09\x7f\xcf\x86\xf9\x95\x8c\xf4\x7b\x73\x56\x69\xb7\xbe\xd7\xf0\xbe\x0c\x99\x12\x0f\x47\x5c\xbe\xf7\x78\xbf\x1c\xf2\xb2\x71\x7f\x05\x86\x6a\xfc\x5f\x5f\xd0\x5f\xf1\x1d\x82\xff\xba\x02\xe8\xda\x57\x14\x3c\x6a\xac\x9c\x32\x85\x00\x94\x68\xb0\xca\xfb\x9c\xf0\x34\x5a\x6d\xc5\x05\xc6\x17\x46\xb6\xd3\x2a\x66\xa2\xc5\xca\x72\x23\x2d\xf1\x78\x3b\x33\x2d\x56\xfd\xaf\xb3\xd3\x2a\x8a\xc0\x7d\x71\xca\x3e\xb4\xe7\x36\xd5\xa2\xb8\xfc\x03\x6c\x89\xad\xf2\xd3\x60\x26\x84\xc3\x69\x30\x5b\x3e\xf6\x82\xc3\x45\xdc\x06\xe1\xb3\xca\xa4\x63\x7e\x5b\x83\x65\xf4\x7c\x03\x35\xfd\x36\xcb\xd7\x19\xae\x3b\x8c\x96\xe9\x9f\xcf\x74\x99\xfe\x79\x0d\x98\x39\xe0\x86\x04\x5c\x0a\xd1\x73\x54\x2f\x3b\x6c\xa2\xf9\x97\x22\x96\xd1\x1c\x70\xd3\x00\xdc\xf0\x02\x6e\x38\x01\xbb\x21\x67\x49\x38\x9b\xc0\xd5\x4b\x89\x0e\xcb\xeb\xd7\xe0\x37\xf1\x95\x3e\x37\xc8\xf3\x3a\x79\x04\x14\x5c\x50\xc4\x54\x7c\xa6\x53\x51\xfa\x8c\x5e\x93\xd6\x7f\xfc\x11\x01\x36\x9f\xd1\x4f\xa8\x56\x5d\x6b\x2b\x33\x54\x7e\x85\x3e\xe7\x84\xbb\x50\xe6\x9e\xda\x82\x4f\x83\x19\xd8\xcc\x6e\x66\xa5\x12\x47\x18\x3a\xdd\x41\x3f\xa1\x52\x13\xbd\x40\x9f\xcb\xac\xa7\xcd\x91\xd3\xdb\xc9\x8a\xcf\x60\x2b\x2e\x86\x43\x9e\xee\xdb\xa6\x46\xf6\x81\xa0\x84\x36\x90\x82\x4e\xc7\x72\x26\x81\xd8\x7a\xb2\xb8\xdb\x38\x78\x1c\x4e\x30\x2a\xa9\xfd\x64\xe1\x02\x7c\xb1\x46\x9c\xc3\xa2\x36\xb3\x7c\x9f\x19\x67\x55\xa1\xde\xc1\x4e\x5e\xe3\xc9\xb7\xb7\xb3\x14\xac\x76\x29\x46\xff\x5d\x9b\x5a\xb2\x1d\x82\xda\xf5\xa8\x5b\x49\x71\x73\x4b\x51\x6b\xc9\xcd\x41\xd4\x13\x86\xf2\xe2\x8d\x30\x94\x5f\xcc\xf7\xad\x12\x09\xbe\xc0\x49\x8a\xf7\x95\x82\xf2\x95\x2b\xae\xd9\x0f\xf2\xb3\x97\xba\x73\x81\xba\xb6\x00\xfe\x67\xf2\x1f\xc2\x7e\xc8\x0a\x65\x1d\xcc\xe5\x34\x7a\xc3\xa7\x7c\x61\x33\xdb\xfc\xcf\xe5\x33\xb4\x81\x3e\x17\x8b\xd5\xe9\x60\x29\x7b\xe7\x51\x9c\xe0\x6f\xc6\x55\x14\x90\x7b\xd1\x10\xfc\x9c\xe5\x74\x87\xe4\xcd\xc1\x68\x11\xcf\x50\xda\xa1\x30\x7e\xd8\xd8\x40\x2f\xea\x0b\x78\x92\x4a\x61\x6a\xed\x5b\x31\x62\xa7\x48\x90\x88\xb4\x97\x29\x7e\x1f\xc7\x33\xb9\x24\x2a\x26\x0e\x15\x65\x46\x35\x91\xc3\xb8\xf1\x0c\x66\x5d\xb4\xb2\xf9\xa6\xb7\xb5\xbd\xf3\x76\x77\xef\xbf\xde\xbd\xdf\xff\x70\x70\xf8\xbf\x8f\x8e\x4f\x3e\xfe\xf2\xeb\x6f\xff\xfe\x3f\x41\x7f\x30\xc4\xa3\xf3\x71\xf8\xf9\xcb\x64\x1a\xc5\xb3\xff\x4e\xd2\x6c\x7e\x71\x79\x75\xfd\x47\xad\xde\x68\xb6\xda\x9d\xb5\xf5\x97\xcf\x57\x37\x58\x84\x5b\x71\xb4\x13\x8b\x76\x69\x54\xe5\x10\x7b\xbc\x52\xa4\xe5\x86\x66\x61\xea\x12\x85\x8c\x76\x5c\x6e\x2a\x64\xa6\x43\xcf\x7e\xc3\x1c\xbb\x52\x22\x24\x29\xcb\x43\x52\x93\xea\xc0\x82\x5e\xa0\x7a\xf9\x0c\xbc\x57\xa4\xc0\xd4\xb0\x89\x8b\x03\x6d\x14\x01\x5a\x3e\xe3\x1b\xbc\x2a\x86\x39\xa0\x52\x81\x28\xd2\x22\xf7\x7c\x25\xc2\x0c\xa0\xff\x95\xb6\xa8\xfa\xd6\x44\xf9\xc1\x7b\x10\x1b\xe2\xe7\xcf\xb5\x0f\x82\x6c\xc5\x0f\x46\x91\x56\x6c\x49\x67\x58\x84\x1b\x99\xbb\xc7\x3c\xe4\x2b\x7b\xc4\x2b\x6f\x66\x9f\xf6\xe3\xd1\xff\xf1\xe8\x2f\x8e\xfe\x1f\x4f\x76\x5e\xd4\x3b\xe8\xcd\x76\x61\x07\xad\x7a\xe7\xcd\xb6\xea\xa3\x55\xef\xe8\x4f\xf0\xf5\xf6\x4e\x5b\x14\x99\xbf\xd6\x71\xab\x20\x0e\xf7\xe8\xbc\x55\xef\x78\xbd\xb7\xea\x9d\x7f\x80\x46\xa0\xf8\x61\x1d\x06\xe3\x2e\x67\x75\xb7\xbf\x3f\x58\x46\xc5\x43\x7c\x18\x87\x51\xe6\x73\x32\xae\x77\x3c\x4e\xc6\xce\xc3\xb4\xc4\xd4\xef\x65\x2c\x9a\x2c\xea\x6a\xac\x00\xbd\xc3\x09\xca\x24\xe2\x3b\x39\xab\x01\x6d\x2e\xbb\x36\xbe\xeb\x63\x14\x5d\x55\xc2\x65\x8d\x2f\xbe\xa5\x7c\xd6\xa0\xd2\x72\xbe\xc6\xbc\x96\x90\x6f\xf9\x8b\x87\xf6\x34\xd6\x1b\x2e\xe6\x68\x5c\x07\xd9\x47\x60\xa8\xbb\x19\x13\x11\x48\x2e\x96\x06\x59\x2c\x46\x10\x36\x3f\x85\xfb\xa4\x1c\x63\x74\x7e\x2a\x1e\x0a\x83\x91\xe5\xfb\x02\x7b\x98\xb2\x4f\xbd\xbf\xf3\x3e\xf5\xfe\x3b\xd8\xa7\x8a\xe0\x70\xdf\xfb\x94\x73\x39\xbd\xdf\x7e\xdc\xa6\xc4\xdf\xbd\x6d\x53\xe9\x65\x30\xdb\x8e\x86\x61\x10\x95\x96\xdd\xb1\x5c\x47\xf2\xef\x7f\xcb\x7a\xff\x30\x5b\x56\x91\x65\xf2\xfd\x6f\x59\xef\xb7\x8d\x4d\xeb\x71\xc7\xb2\x76\x2c\x65\xc5\x2c\xb5\x79\x7d\xd3\xdd\x4b\xcc\x8b\x82\x2d\x01\xa4\xf5\x91\x47\xc3\x87\x2f\xec\xee\x84\x2e\xee\x5a\x8d\xfc\x3f\x5c\xac\xd0\x8f\xa4\xfb\xec\x2b\xfd\x26\x97\xff\x22\x75\x01\x10\x96\x5f\x5b\xd0\xb9\x93\xb6\x80\xe5\xa8\xfd\x96\x4a\x83\x0a\x52\x5e\xa5\xe3\xa0\x6e\xbc\x1a\x4f\x83\xc1\x03\xaa\x16\x2a\x88\x37\x0b\xbf\xa0\xb5\x7f\x82\xba\xc1\xca\x17\x7b\x0b\x55\x84\x66\xc4\xa2\x7c\xd9\xdf\x6a\x43\x4d\x30\xb9\xd9\xdf\x6a\xbb\x64\x3c\x30\x71\xfe\x82\xaf\x69\x16\x6c\x6a\x07\x2b\xfa\x0a\xce\xbf\x41\x94\xf1\x24\xde\x71\x32\xa5\x36\xda\xdb\xbf\x1c\x7e\x82\x4d\xf7\x24\x7e\x87\xa5\x30\x88\x2e\x2f\x2f\xab\xf1\x0c\x47\x69\x3a\xa9\xc6\xc9\xf9\xea\x30\x1e\xa4\xab\x90\x84\x3b\x5e\x35\xea\x8c\xb3\xe9\xc4\xa1\x08\xd9\xbe\x98\xbd\xdb\xda\x91\x68\x8b\xe7\x82\xc1\x10\x16\xfb\x80\x18\x7b\x9c\xe5\xfd\xc2\x52\x9e\xc3\x1e\x45\x06\x26\x25\x0f\x61\xc4\xdd\x5e\x94\x70\xcf\xd2\xd5\xa5\x85\x4a\xf5\xc6\xba\xe6\xe9\x62\xc1\xf7\x18\xa9\xa9\x61\x31\xcc\x04\x29\xfb\x5b\xed\x45\xd8\x86\x19\xb3\x45\x36\x83\x54\x2b\x1f\xb2\x18\xcd\xa8\xd5\xa9\xea\x9d\xe3\xd9\xe1\x2c\xbf\x18\x63\x77\x60\xc3\xd3\x45\xf5\xc6\x3a\x98\x90\x6a\x5f\x69\xe7\x00\x73\xe3\x8b\xc4\x47\x6b\xfb\xe6\xd6\x6e\x37\x1e\xa2\x7d\x68\x3f\x1c\xac\x34\x7a\x0f\x66\xd6\x5f\x86\x23\xcb\xfb\x86\xd2\xfc\x82\x14\x4d\x8b\x2b\xfe\x29\xe7\x6a\xdd\xc8\xe7\x77\x5b\x30\x15\x7d\x1a\x6b\xb5\x9a\x09\x78\x49\xef\xa0\x85\x7e\x3f\xc5\xe4\xdd\x2d\x48\xe1\x4f\x68\x84\x50\x05\x24\xc2\x0e\x20\x03\x2b\x59\xb4\xb7\xb1\xd2\xe7\x75\x69\x2c\x00\x17\xa0\x9c\xca\x69\x30\xc9\xd0\x26\xfc\xb3\xbc\x58\x0c\xd4\x45\xc9\xfb\x3e\xc8\x0b\x93\xcd\xe3\xcb\x70\x54\xa5\x6e\x11\xb8\xc4\x3b\x53\x01\xfc\x72\xf2\xd6\x40\x71\x2d\xbf\xa3\x5e\x73\x29\x81\x57\x9f\x62\x87\x78\x4b\x56\x3a\xe3\x1e\x76\x6d\xe1\xa5\x46\xc8\x83\x99\x28\xcb\xd5\xe1\x84\xe5\x73\x0b\x83\xd0\x02\x74\x88\xdf\xc1\xd8\xb8\x52\xa2\x2d\x73\x46\x96\xc0\x84\x4f\xb0\x78\xe3\x3d\x2e\xf3\x3d\x86\xf6\x88\x3d\x39\xca\x29\x4c\x9c\x16\x95\x2f\x1c\x58\xbe\x65\x1b\x13\x01\xaf\x7f\x64\xc6\x2c\x06\xae\xdc\xa0\xe5\x35\xc7\xc7\x79\x14\x20\x62\x1c\x78\x0e\x78\x2f\x98\x75\x97\x25\x5a\x76\xf1\xb5\x32\x52\x83\x31\x48\x27\x10\x06\x85\x13\x9b\x62\x14\x6c\xd1\xab\xde\xbc\xf0\xa7\xb3\x4b\x10\x9a\x10\x03\x67\x7f\xd6\x0e\x4a\x75\x7a\x50\x52\x06\x3a\x37\xed\x8f\x81\xbd\x40\xd6\x3b\x0a\x2e\x8c\x1d\x43\x65\xbf\x53\xc8\x8a\xc5\x8c\x71\xb6\x61\x8c\xb2\x52\x4b\xd1\xd1\x70\xfa\x73\x44\xbb\x10\x01\xe6\x78\xbd\xa2\x36\xd7\x85\x78\xb0\xea\x77\x7c\x2b\xde\xbb\x24\xdf\xbd\x47\xef\x5b\x87\x5f\x99\xd2\x9b\xe2\xdc\x5c\xa9\xa4\x69\x37\x94\xf7\x3a\x77\x97\x1f\x90\xc6\xd5\xc5\xa6\x4d\xf7\x6b\x1f\x67\x5f\xae\x5a\x05\x79\xc4\x86\xbb\x80\xc9\x15\x1b\x84\x0a\x59\xca\xfa\xbe\x3d\xc7\x76\x61\x61\xc3\xae\x4b\x2c\xe0\xb8\x92\xbf\xdf\xdd\xbc\xca\x39\xbe\x53\x68\xee\xb3\x7b\x85\x1f\x3e\xbb\xed\xf5\x0a\x3f\x92\x76\xd7\xd6\xc8\x99\x7e\xed\x6f\x7d\xa6\x1f\x84\xb3\x31\x4e\x5e\x3c\xb0\x89\x00\x9c\xde\xd5\xa6\xfe\x9a\x43\xbc\x9d\xb9\xf3\x5e\x4e\xf3\x3d\xe8\xd8\x21\xe1\x38\xa9\x38\xb4\xab\x2f\xfd\x26\x04\xe2\xbd\x91\x09\x43\xab\x41\xce\x70\x41\x06\x95\xe8\x4f\xce\x88\x59\xc5\x1d\x78\x99\xb1\xa8\x0a\xb4\xc8\x12\xe9\x34\xc8\xe9\x86\xce\x4d\x86\xaf\x32\x72\x8a\x0c\xd8\x33\x9a\xd1\x3e\x31\xdf\x2c\x9e\x6a\x23\x18\xe2\x41\x38\x0d\x26\x93\x6b\x96\x06\x74\x58\xf8\xe6\x46\x1d\x95\x1b\xd6\x0a\x1b\xb8\x13\x81\x86\xde\xec\xf2\xc9\x38\x6e\x83\xdf\x83\xa6\xe7\x90\x53\xa2\xdc\xea\xa8\x9d\x5f\xee\x62\x47\xab\xe9\x71\xd4\x52\xcb\x54\xe5\xec\xca\x04\x12\xbb\xf8\xea\x96\x99\x20\x1c\xc3\xab\x90\x8f\x7a\xdf\xb0\xe4\x74\x1a\x37\x0f\x61\x34\x9b\x67\x77\x99\x53\x4e\x1e\x3a\xd1\xdd\x82\xce\xee\x8b\x38\x06\x06\xa3\x70\xd0\xc7\xad\x93\x4a\xc0\x68\xb9\x43\xd8\xc8\xc9\xd9\x40\xb2\x0d\x5a\xe1\x95\x93\x7a\x7a\x1a\xf5\x70\x8d\x80\x04\xd4\x55\x81\xde\xb8\x75\xf3\xfe\x9d\x56\x76\xd7\xd8\x6d\x95\x0d\xa2\xdb\x6e\x54\x0c\xe5\xf9\xfa\xa3\xa9\xdd\x3f\x5d\xf7\xed\xdb\x1d\xad\x48\xe6\x79\x9a\x70\xfb\x90\x02\x0e\xc0\x42\xe3\xea\x4c\x44\x45\x4a\x6c\xa8\x8e\xaa\xf7\x93\x90\x1e\x5c\x5e\x17\x72\xbc\xc2\x4a\xe2\x82\xaa\x28\x22\xab\x83\xf3\x32\x1e\x24\x38\xbb\x27\xa5\x12\x91\x7f\x77\xdd\x81\x83\xa0\x97\x8c\x4d\xb8\x3c\x91\xa9\xa3\x6f\x51\x8d\xa1\xea\x1c\xec\x09\x10\xec\xd4\x19\x09\x7d\x11\xf5\x51\x10\x8f\xa6\x87\x7b\x8e\xb7\xdb\x7d\xc6\x97\x85\x03\xd3\x82\xf0\xb2\xf4\x50\xa5\x44\x97\x35\xc7\xc9\x6d\x88\x9f\xa3\x98\xa2\x1d\x7d\xa3\xc4\xc5\x64\x5d\xcf\x8b\x8c\x69\x54\xe2\xfa\x02\x13\x96\x3b\x4a\xe6\xe6\x64\x12\x5f\xa2\x20\xe9\x87\x59\x12\x24\xd7\x88\xa9\x97\xbe\xe0\x6b\x47\xdc\xc1\x2f\xaa\x46\xe2\x67\x67\xc3\x39\x03\x65\xaa\x5b\x8a\x8d\xd6\x02\x67\x48\x82\x52\x8e\x1b\x24\xc4\x7f\x03\xdd\x46\x9c\xa0\x30\x8a\x70\x02\xd1\x67\xe3\x79\x06\x02\x84\x19\x85\x0f\x62\x26\x52\x1d\x23\x25\x43\xf6\x40\x5b\xb1\x02\xd2\x71\x8d\x9f\x5a\x23\x74\xd4\x58\x86\x04\x62\x45\x2b\x19\xe7\xe9\x23\x43\xa5\x60\xa8\x14\xb4\x1a\xfb\xed\xe0\x08\xe6\x93\x5e\x03\xce\x82\x21\x1a\xc4\x51\x9a\x05\x91\xd9\xbc\x33\x89\x94\x3e\xc7\x7e\xc5\x9a\xc0\xfb\x34\x3c\x43\xbf\x6f\xa0\xda\x55\x7b\x40\xff\xe7\x72\x87\xb1\x0a\x37\x3b\xf4\x7f\xf9\x9a\xb1\xd8\xd0\x89\x85\xc6\xb3\x8b\x22\xff\x82\x38\x64\xb0\x03\x3d\x44\x14\x32\xc1\xc4\xef\x25\x12\x59\x4e\xbe\x32\x17\x33\x76\x0c\x24\x74\xda\xc5\xc7\x3d\x7a\x52\x5d\x5f\x2c\x17\xcc\xed\x22\x90\xc1\x30\x7f\x37\xf1\xc7\xf6\x37\x7b\x2c\xfa\x18\xe0\x15\xc2\x12\xcb\x8d\x84\xb2\xe4\x94\x17\x09\x44\x66\x95\xbe\xff\x60\x64\x2a\x49\xf0\x56\x16\x06\x1f\x7b\xa8\xe8\x61\x30\xd4\xff\xd3\xa3\x87\x2d\x10\x53\x97\x11\x11\x09\x0f\x95\x34\xb4\x30\x82\x98\xbf\xc6\xc2\x28\x62\xfe\xaa\x0f\x14\x49\xec\xee\xdc\xae\x47\xd5\xd3\x30\xde\x8e\xfd\x98\x48\x17\xbb\xee\xe0\x68\xb9\x01\xc7\x72\x39\xa6\x3a\x56\x06\x50\x29\xa1\x70\x49\x83\x5f\x32\x09\x54\xca\xde\x90\x63\xd3\x60\xe0\xbe\x24\x12\x07\x7f\x8f\x11\xdc\xcb\xbf\xb5\xc2\xfc\xaa\xd3\x7a\xe1\x78\x3d\x09\xfb\x2f\x08\x2a\x43\xb0\x6d\x4d\x8d\xaf\x38\x1a\xbc\x00\x9b\x46\xc7\x7b\xea\x66\x69\x7c\x98\x0e\xdb\x8b\x8d\xef\xd2\x71\xd0\x68\x9b\x20\xc9\xcb\x86\x09\x2e\x1d\x07\xed\x7a\xc3\x7e\xd9\x5c\x77\x94\x6c\x1a\xaf\x92\x70\x86\xa7\xc3\x7a\xa7\xe6\xb4\xfd\xd3\x5e\xcd\xfa\x5f\x86\x23\xb3\x1d\x7c\x31\xfb\x32\x1c\xe5\xdd\x3b\xe8\x5d\x8f\x87\xf8\xc5\x60\xd4\x77\xbe\xce\x12\xcf\xeb\x17\xe7\x93\x60\x38\x0d\x22\xd7\xe7\xd8\x0d\x0c\x0f\xcc\xd7\xb3\x60\xf8\x22\x88\xd2\xf0\xea\x65\xc3\x1c\x04\xf2\x29\x4c\xe3\x7a\xad\xde\x30\x47\x9c\x7d\x7a\xb9\xf6\x72\xcd\x9c\x21\xf2\xe9\x0f\x9c\xc4\xcc\xf5\xda\xf1\x35\xf2\x7c\xa3\x3a\xb2\x17\x63\x7c\x65\x7c\x08\xb0\x49\x5c\x34\xee\xc6\xd0\x7a\x9f\x0c\xcc\xc9\x4d\x82\x7e\x3f\xcc\x9c\x2f\x5f\x4c\xf0\x79\x30\xb8\x7e\xe8\x3b\x20\xb1\x7a\xe0\xc9\x5c\x34\xf0\x52\xae\x15\xf1\xc8\x96\x08\x3c\x93\x95\x61\x98\x85\xb2\x75\x20\x7e\x37\x5a\xe2\x37\xa1\x7a\xfe\x9b\x10\xbb\xf8\x4d\x7f\x49\xd2\x96\xf6\xa5\xf0\x8b\x11\x32\xc5\x80\xd2\xaf\x75\x87\x45\xd1\xe1\xd4\xaa\x3c\x65\x89\xfe\x24\x68\x53\xbe\x8d\xb5\x1a\x84\x12\x69\xb3\x2a\x01\x8a\x37\x82\xee\xd4\x37\x94\xdc\xc4\x1b\x95\xca\xc4\xcb\x48\x7f\xa5\xd0\x14\x3c\x13\x52\x82\x1f\x92\x82\xe8\xa8\x0c\xd8\x40\x31\x7a\x51\x7e\x73\x32\x59\x56\x11\xa9\x29\x20\x55\x5e\xbb\xbc\x62\xd2\x1f\x8a\x8d\x75\xa9\xdb\xae\x57\xf2\xb5\xc9\x15\x9d\xae\xba\xed\x56\x45\x23\xbc\x6e\xbb\x5d\x91\x13\xdf\x6d\x77\x2a\xfa\xe8\x75\xdb\x6b\xe6\x8d\xb0\x49\xca\xdd\x4e\xad\xc2\xa8\xb5\xdb\x01\x7c\x04\xa5\x74\x3b\x8d\x8a\x4a\x2b\xdd\x4e\xab\xe2\xa2\x96\x6e\xa7\x59\x51\x29\xa4\xdb\x69\x57\x54\xfa\xe9\x76\x00\x2f\x8d\x66\xba\x9d\xb5\x8a\x49\x35\xdd\xce\x7a\xc5\xa4\x9b\x6e\xe7\x65\xc5\x22\x92\xee\x5a\xad\xe2\x20\xa7\xee\x1a\xe0\xcf\x96\x44\x77\x0d\xb0\x67\xa4\xd1\x5d\x6b\x55\x2c\xe2\xe8\xae\x01\xe2\x84\x8c\xba\x6b\x80\xb3\x5c\x67\xdd\xb5\x8e\x7a\x81\x5e\x91\x4b\xb6\xbb\xc6\xaf\xd6\xc9\x62\xee\xae\xbd\xac\xf0\xa5\xda\x5d\xaf\x55\xe4\x12\xee\xae\xd7\x2b\x72\x71\x77\xd7\x01\x1d\x49\xc1\xdd\x75\x68\x5c\x30\x9a\xee\x7a\xeb\xe6\xac\xd2\xa9\x3d\x5e\x1e\xfc\xf5\x97\x07\xbd\x31\x1e\x7c\x21\x9d\x82\x95\x42\xdd\x80\x68\x9a\xb3\x74\x3e\x23\x03\x83\x59\x7c\x6a\xa5\xdf\x20\xc7\xd3\x90\xe6\xe8\x87\x0d\xb4\xc2\x21\xaf\x38\x2c\x42\x84\x93\xc6\x3d\x5e\x57\xe4\x9a\xe3\x8b\x76\x8e\xf0\x08\x27\x18\x0e\x7a\x49\x78\x0e\x67\xb2\x30\x0a\x33\x09\x26\x9d\xcf\x70\x02\xaa\xeb\x0d\x23\x3d\x87\x02\x65\x73\x7e\x3e\xc5\x51\x66\x14\x40\x59\x8c\xc6\x41\x34\x9c\x60\x6d\xdc\x54\xd8\x7d\x27\x64\xcd\xa6\x06\xaa\xda\xee\x80\x8a\xee\x9b\xc6\x92\xa7\x26\x50\x61\x94\xad\x2b\x1a\xfa\x91\x5a\x5f\x28\x26\xf4\xd9\xb1\x8f\xf9\xb2\x06\x55\xc2\x7f\x24\x50\xe1\x85\x8a\x8d\x76\x88\x70\x22\x16\xd3\xf4\x5f\x00\xe9\x22\xc4\x97\x3e\x14\xbd\xcd\x2b\x08\xef\x71\x14\xd0\xd7\xaf\x7a\x79\x4e\x70\x80\x25\xe8\x8c\x79\xf5\x1f\xc8\x9a\x13\xb6\x23\xb0\xe8\xdc\xc0\xad\xaa\x65\xab\x15\x2f\x56\xf5\x8e\x1b\x2d\x7f\x4b\xcb\xd5\xd8\x8b\xb2\x66\x63\xd9\x26\x96\xab\xb1\x33\x89\x83\xdb\x54\xe9\xb4\xe0\xbd\x2c\x7f\x4b\x52\xaa\x52\x0a\xae\x20\xf5\xd5\x75\x86\x0f\x20\x39\x90\xf5\xda\x95\x77\x59\xa3\xbf\x5d\xba\xe8\x64\x5b\x45\x56\x84\x2c\xbd\x9c\x0a\x41\x42\x7b\x23\x70\x43\x1b\x6e\x9c\x1d\x9a\x85\xed\x2b\x96\x7d\xf5\x3a\x73\x19\x3f\x2f\xe5\x2e\xe8\x42\x65\x99\x7c\xda\xb2\xfe\x69\x78\x76\xab\xe4\xd9\xd2\x9c\x3b\xfc\x03\x53\x55\xad\x74\x1c\xd5\x8b\x0a\xc6\x2a\x53\x5b\x54\x10\x73\x23\x74\x75\x44\x9b\x6f\x67\xd6\x33\x32\x9a\xe4\x35\x81\x87\x22\x22\xf5\xa9\xcc\xdc\x6e\x37\x98\xcd\x26\xd7\xac\xe1\x20\x39\x9f\x13\x16\x9e\xe6\xf9\x2b\x32\x7e\x5d\x9d\x25\x71\x16\x13\x1c\x55\xce\x9d\x67\x38\x61\xee\x3e\x6e\x05\x4b\xa7\xfe\x28\xeb\xfc\x35\xb2\x0e\x04\x8c\xfe\x0b\xe2\x12\x39\x73\x2a\x15\x30\x91\x80\x2d\x96\xde\xe3\xa1\x4c\xea\xd6\x49\x95\x13\xc6\x2c\x94\x92\x54\x75\x69\xdc\xfc\xb9\x24\x3d\x1f\x5f\xe9\xb4\xdc\x5c\xe4\x84\xb0\x89\x0d\x3a\x7c\xd5\xa0\x9f\xd2\x1f\x69\x18\xb1\x60\xac\x84\x65\xd4\xae\xea\x35\xf6\x57\x46\x5f\xf5\x34\xbe\x6c\x79\x95\xca\x4e\x0b\xf5\xfd\xad\xb6\x61\x4d\xe1\x32\x00\x31\xbd\x26\xd1\x06\x1b\x55\x87\x01\x08\x4f\x7b\x93\x7b\x3b\x26\x35\xc1\xee\x5c\xc5\xa7\x36\x27\xad\x5d\x75\xd6\x5a\xed\x46\xb3\x56\xaf\xa0\xda\x15\x1e\x0d\x86\x41\x7f\xfd\xa5\x23\xaf\x62\xed\xea\xe5\x7a\x3f\x18\x0e\x46\xb8\x02\x03\xd3\x6c\xb4\x5b\x6b\x1d\xbd\xdc\x99\xf7\x46\xcc\x48\xa3\xa7\xf6\x62\x5f\x64\xd2\x73\xed\x5d\x97\xc1\x0c\x61\x70\xaf\x5e\xbc\x87\xd4\x3b\xfe\x1d\xc3\x7f\x7d\xcd\x67\x83\x22\xf1\x89\xc0\xe3\xe9\x05\x51\xe8\x89\xc0\xbb\xff\x49\x29\xbd\x7f\xca\x1f\xce\x5c\x2e\x21\xca\x67\x42\x70\x76\x01\xf2\x57\x2a\x95\x14\x98\xd4\x53\x1c\x7d\x45\xea\x4b\xd8\xeb\x5a\x65\xc3\x47\x1c\x7d\x2d\x08\xb0\xd1\x2a\x3b\x00\x42\x28\x63\xcd\x25\xdd\x06\x77\x37\xe3\x90\x5d\xed\x86\xc2\x7d\xdd\xaf\x0d\x69\x0d\x29\x63\x8a\x9e\xa3\x9a\x29\x3e\x68\xa5\xeb\x46\xe9\x7a\x6e\xe9\x86\x51\xba\x91\x5b\xba\x69\x94\x6e\xe6\x96\x6e\x19\xa5\x5b\xb9\xa5\xdb\x46\xe9\x76\x6e\xe9\x8e\x51\xba\x93\x5b\x7a\xcd\x28\xbd\x96\x5b\x7a\xdd\x28\xbd\x9e\x5b\xfa\xa5\x51\xfa\x65\xfe\xec\xd4\x8c\xd9\x59\x30\x99\x75\xa3\x78\xfe\x6c\xd6\x1b\x46\xf1\xfc\xe9\xac\x37\x8d\xe2\xf9\xf3\x59\x6f\x19\xc5\xf3\x27\xb4\xde\x36\x8a\xb7\x2d\x6e\xb0\xba\x4a\x18\xf2\x97\x30\x3a\x27\x55\xc3\x60\xd2\x77\x89\xcd\x01\xd9\x06\x4e\x9d\x03\xd5\x87\x4f\xce\x41\x19\xc0\x27\xe7\x00\x0c\xe1\x53\xd3\x85\x4e\x4f\xde\x41\xeb\xdf\x08\x12\x3b\x3b\xa5\xa0\x82\xfa\x15\x34\xa8\xa0\x61\x45\x59\xa0\x15\x84\xd6\x2a\x64\x0b\xad\x9d\x99\xbc\x61\x48\xeb\x0d\x2b\x48\x54\x95\x23\x54\x41\xa8\xde\xa8\xa0\x93\xd3\xba\x55\x6f\x40\xeb\xd1\x96\x68\x55\xb9\x68\x49\xbd\x35\x52\xaf\x61\xd5\xeb\xd3\x7a\x02\xc9\x40\xa9\xd7\xac\x20\xd4\x80\xf6\x9a\x56\xbd\xbc\xfe\xb5\x44\xff\x5a\x4b\xf5\xaf\x2d\xfa\xd7\x5e\xaa\x7f\x1d\xd1\xbf\xce\x52\xfd\x5b\x13\xfd\x5b\x5b\xaa\x7f\xeb\xa2\x7f\xeb\x4b\xf5\xef\xa5\xe8\xdf\xcb\xa5\xfa\x57\xaf\x55\x58\xff\xea\x36\xc1\xe4\x75\xb0\x5e\xaf\xb0\x0e\xd6\x6d\x8a\xc9\xeb\x21\xc1\x92\xf6\xb0\x6e\x93\x4c\x2e\x89\x36\x2b\x9c\x44\x6d\x9a\xc9\xed\x63\x4b\xf4\xd1\x26\x9a\xdc\x3e\xb6\x45\x1f\x81\x6a\xec\x4e\xbe\x7d\xeb\xe9\x64\x05\xa1\x36\xed\xa4\x4d\x37\x43\x5a\xd1\xd9\x49\x42\x6f\x2f\x69\x45\x9b\x70\x06\xb4\xa2\xbb\x93\xf5\x0a\x22\x1d\x3d\x39\xad\xdb\x94\xd3\xa7\x15\x9d\x9d\x24\x1c\xa3\x51\x83\x8a\x36\xe9\xe4\xf5\xb1\x2d\xfa\xd8\x70\xf3\x1a\x5f\x1f\x09\xcd\xd1\x3e\x36\xdc\xcc\xc6\xdb\xc7\x36\xef\x63\xc3\xcd\x6d\x7c\x7d\x6c\x89\x3e\x36\xdc\xec\xc6\xd7\xc7\x97\xb2\x8f\x6e\x7e\xe3\xed\x63\x4b\xf4\xd1\xcd\x70\x7c\x7d\x24\x8c\x91\xf5\xd1\xcd\x71\x7c\x7d\x5c\x97\x7d\x74\xb3\x1c\x2f\xad\x36\x2b\xbc\x8f\x6e\x9e\xe3\xeb\x63\x43\xd0\x6a\xc3\xcd\x74\x7c\x7d\x5c\x13\x7d\x6c\xba\x99\x8e\xaf\x8f\x64\xf9\xd3\x3e\x36\xeb\xee\x05\xb9\xbb\xeb\x27\xd6\x16\xe0\xda\x74\x73\x9d\xdd\x5d\x77\x27\xc9\xb0\x92\xb5\x75\x72\xda\x74\x73\x9d\xdd\xdd\x9c\x05\xd9\x81\x8a\x6e\xae\xb3\xbb\xeb\xe9\x64\xab\x82\x1a\x4d\xa8\x68\x93\x4e\x5e\x1f\xeb\xb2\x8f\x6e\xa6\xe3\xeb\x63\x4b\xf6\xd1\xcd\x74\x7c\x7d\x84\x89\xa4\x7d\x74\x33\x1d\x6f\x1f\x6b\xa2\x8f\x6e\xa6\xe3\xed\x63\xb3\xc2\xfa\xd8\x72\x33\x1d\x5f\x1f\x6b\xa2\x8f\x2d\x37\xd3\xf1\xf5\xb1\x29\xfa\xd8\x72\x33\x1d\x5f\x1f\x09\x2b\xa7\x7d\x6c\xb9\x99\x8e\xaf\x8f\x2f\xc5\x3c\xb6\xdc\x4c\xc7\xd7\x47\xb2\x3c\x58\x1f\xdd\x4c\xc7\x4b\xab\x6d\x4e\xab\x2d\x37\xd3\xf1\xf5\xb1\x21\xfb\xb8\xe6\x5e\x90\x7b\x7b\x7e\x41\xb5\x43\x3b\xe9\xe6\x3a\x7b\x7b\xee\x4e\x02\xcd\x01\x0f\x68\xb9\xb9\xce\xde\x5e\x8e\x18\xd0\x06\x11\xd0\xcd\x75\xf6\xf6\xdc\x9d\x24\xbc\xa3\x01\xc3\xda\x76\x8b\x3a\xbe\x3e\x92\xf9\xa0\x7d\x6c\xbb\x99\x8e\xaf\x8f\x4d\xd1\xc7\xb6\x9b\xe9\x78\xfb\x58\x13\x7d\x74\x33\x1d\x5f\x1f\xeb\xb2\x8f\x6e\xa6\xe3\xeb\xe3\xba\x98\xc7\xb6\x9b\xe9\xf8\xfa\x08\x34\x47\xfb\xe8\x66\x3a\xbe\x3e\x82\x48\x4e\xfb\xe8\x66\x3a\xde\x3e\x36\x2b\xbc\x8f\x6e\xa6\xe3\xeb\x63\x4b\xf4\xb1\xe3\x66\x3a\xde\x3e\xd6\x79\x1f\x3b\x6e\xa6\xe3\xeb\x63\x43\xf4\xb1\xe3\x66\x3a\xbe\x3e\xbe\x14\xf3\xd8\x69\xda\x0b\x12\xae\x51\x32\x9c\x4c\xf1\x30\x0c\x32\xe6\x54\x06\xee\x0a\x7a\x39\x72\xc4\x45\x1b\xa8\x04\xff\x3e\x47\x81\xa9\x61\xa5\x65\xea\xac\x4c\x9d\x94\xe9\xbb\xcb\x34\x58\x99\x06\x29\x33\x70\x97\x69\xb2\x32\x4d\x52\x66\x68\x69\x73\x0d\x55\xe5\x8e\xc3\x52\x77\xc9\x80\xb6\x90\x29\x5d\x64\xd3\x0d\xb2\xc0\x75\x30\x0f\xb2\x40\x84\xf2\x09\xb2\xc0\xaf\x1c\x8b\xde\x84\x59\x7a\x12\x67\xc1\x44\xc0\x8c\xb6\x82\x2c\xa0\x1e\x24\x3f\xa1\x75\x07\x74\xa8\xf3\x1e\x8f\x32\x0e\x5d\x78\x9c\x40\x79\xab\x33\xde\x94\x57\x02\xcd\x53\x09\xf2\xe7\x9f\x7f\x46\x6d\xb8\x78\xab\x5d\xad\xd7\xe4\x7d\x9b\x2c\xf1\x2f\xd4\x6c\x58\xc4\xa1\xf7\x65\x17\x6d\x20\x50\xbb\x8f\x26\x71\x9c\x94\x94\x4e\xae\x6a\xba\x77\x5f\xe7\xa0\xec\x7b\xb4\xa1\x3c\x99\x0b\x47\xa0\x5e\x2a\x95\x24\x6e\xcf\x51\xa7\x45\xf3\xa5\xbd\x84\x60\xa2\xad\x32\x55\xd8\xb8\xf5\xb3\xbc\x2a\xc3\x59\x2a\x67\xd5\xb7\xc5\xb5\xb3\x36\x38\xa6\x9a\x35\xc1\x2d\xd2\xcd\x5a\x5c\x62\x99\xce\xb6\x8a\x74\xf6\xbd\xb3\xb3\xef\x6f\xdb\xd9\xf7\xce\xce\xbe\x2f\xda\x59\xbb\xb7\xaa\x13\x55\x49\x74\x9f\x07\x9b\x82\x9c\x7a\x6e\xff\x41\x30\x78\xa7\x6e\x0c\xe0\xa3\xe8\xf2\xa4\xca\xcd\x2b\xbf\xc0\x1b\x52\xd3\x79\x3b\xc8\x77\x97\x19\xc6\x7b\xbd\xdf\x96\xba\xf7\xf0\x5c\x71\xa1\xbc\xeb\x7f\x81\x09\x5c\x61\xec\x9e\xba\xef\x2e\x76\xd9\x2d\x59\xa9\xb4\xab\x5d\x4b\xec\x2e\x7d\x1f\x41\x69\x61\x57\xbb\x8b\xd8\xf5\x5e\x42\x2c\xbe\x71\x38\x62\xb9\x81\x61\x0e\x59\x04\x9e\x21\x8c\xa9\x5e\xb4\x40\xb2\x72\x70\x43\xc8\x65\xf5\xa0\x60\x05\xa7\x4c\x71\x43\x07\x8f\xf2\xfa\xdf\xda\x78\xe1\xf3\x27\x8b\x16\x7c\xde\x95\x3c\x82\x06\xf9\xea\xf6\x70\xa0\xbf\x04\x92\x86\xea\xeb\xaa\x82\xd2\x0a\xd2\xaf\xd0\x80\x4f\xa2\x0d\x14\xa0\xe7\xa8\x54\xea\xa3\x1f\xe9\xe6\x58\xfa\xbf\xe4\xe7\xb0\x4c\xd8\xc0\x15\x7a\x8e\x32\xa5\x3d\x11\xb0\x38\x22\xd3\x94\xd2\x95\x4a\xe3\x94\x37\x1b\xe8\x05\x4a\xcb\x50\xad\x6f\x18\xbd\x09\xac\x8c\xf3\x7f\x31\xac\x60\x3b\x2e\x0d\xd0\x8f\xe8\xff\x3e\x0c\x56\xc6\x21\x68\x21\x56\x7d\xf4\x3b\x1a\xa0\xdf\x09\x62\xf7\x8f\x8c\x21\x00\x2e\x44\x86\x20\x52\xea\xa3\xaf\xf7\x3c\x38\xea\x6d\xf5\xb1\x2f\x4d\xfa\xc2\xc4\xfb\x45\x82\xac\x71\x3f\x31\xc3\x45\x11\x56\x83\x0d\xc6\xe3\x2c\xe6\x29\x7d\xdb\xb0\x66\x6c\x5d\x0a\x23\x97\xfd\xad\xb6\xc3\xf7\x2b\xbf\xbc\xed\xf0\x25\xe3\x8b\x69\x97\xf9\x7a\x46\xfe\xfd\xad\xb6\xd3\x64\xc0\x3b\x09\x0b\x72\xd5\xdf\xd7\x14\xdc\x2a\xb4\xc3\xe2\x89\x53\xbd\xfc\xee\x63\xe2\xa8\x53\x99\x98\x88\xdd\x69\x30\x20\x93\xa1\x65\x86\xb7\xe7\x83\x15\xb3\xe7\x44\x66\xb3\xa7\xf3\x92\x9b\x81\x9d\x45\xb6\xf6\x58\x40\x35\xfe\xd6\x2e\x66\xff\xfc\x98\x6c\x74\xb1\xfd\xc4\xe2\x0c\xa1\x1d\x8c\x87\xfd\x60\xf0\x85\xc5\xd5\x9c\xc6\x43\x58\x52\x84\x66\xc4\x7c\xc3\xcb\xde\xce\x1b\x22\x02\x39\xc4\x03\x30\x73\x82\xaf\x9a\xb5\x1c\x58\xb8\xd0\x56\xf6\x09\x00\x66\xcc\x23\x56\x7d\x6f\xe7\x4d\x75\x3b\xa2\xb1\xca\xc1\x80\x6a\xe7\x8d\xc3\xe0\x67\xe6\x31\x97\x61\x66\x86\x39\x26\x33\x7e\xd1\x94\x85\xa0\xe2\x02\x09\x7d\x74\xdd\x33\x2b\xa1\x3c\x68\x21\x35\x94\x87\x5e\x9e\xc7\x28\x7f\x87\xaf\xd3\x2c\xc1\xc1\x74\x33\x1a\xb2\xde\x39\xac\x23\x63\x66\x16\x2b\xc0\x55\x58\x03\x2e\x21\xfb\x08\x4f\x31\x04\x19\x07\x63\x4c\x3a\x4f\x2c\x56\x26\xf8\xcf\x47\xf8\x2a\xa3\xaf\xdd\xe2\x3b\xbe\x78\xc3\x62\xa6\x42\xeb\xd5\x74\x12\x0e\x70\x89\xa3\x20\x6e\xea\x05\x2e\x2e\xfb\x49\x6d\xd6\xb6\xf0\x3f\x65\xd6\xee\x30\xba\x60\x38\x3c\x0e\xd3\xa5\xc7\xf6\x9b\xd1\xcd\x89\xec\x50\x1f\x0f\xe2\x29\xf3\xba\x27\x04\x11\xc6\xf3\xb4\x18\xc9\x88\x2e\x16\x12\xc7\x73\x7a\x53\x5a\xd8\x05\xc3\x37\xc2\x3e\xb0\xc1\x79\xef\x42\x06\x6b\xb9\x78\xa5\x1b\x8d\xab\xe1\x98\x69\xf3\xf2\x33\x64\x76\xbd\x70\x1e\x69\x44\x69\xb4\x81\xc2\x0b\x36\x85\x35\xcf\x4a\x8c\x2f\x30\xda\xfb\x05\xce\x9f\xe9\xbc\x9f\xe2\xff\x9e\xe3\x28\xcb\x39\x3d\x03\xbe\xc2\x81\x61\xa1\x01\xb4\x89\x8f\x31\x21\xf6\x24\x90\x3f\x46\xe5\x98\x0e\x34\x14\x2c\x09\x20\x15\xa4\x77\x65\x75\x15\xb1\x19\x91\xef\x9c\xd9\x72\xf3\xa3\xc6\x50\xd3\x73\x69\x21\x08\x91\x60\x44\xa3\x70\x8e\xb6\xe8\x85\x61\xc1\xc5\x89\x9d\x37\x79\x06\xd7\x7c\xd3\x59\x26\x4e\x5d\xa7\xf9\x28\x7c\x7c\xef\xc2\x07\xfa\xcf\x59\x82\x53\x9c\x5c\x60\x2a\x86\xc4\x73\x22\xca\x2b\xe2\x07\xa8\x31\x82\x2c\xec\x4f\x18\x07\x46\x5b\x09\x7a\x93\x84\x41\x84\xde\x52\xf7\x4c\x34\x0a\x27\x18\x47\x83\xea\x00\x40\xf0\x90\xcf\x10\x01\xdb\xa0\x9f\x93\x23\x28\xf2\x5f\x41\x84\x76\x93\x79\xff\x1a\x7d\x1e\x93\x7f\xaa\x97\xb8\xff\x9f\xe7\xd3\x20\x9c\x54\x07\xf1\xd4\x2d\xef\x9c\x1c\xf1\xe6\x72\xc4\x1e\xb5\x50\x61\xe9\xe7\x89\xcc\xf7\x12\x0d\xc8\x41\x81\xa6\x4c\x7a\xfa\xe4\x09\x19\x74\x20\x3d\x91\x0e\x09\x94\x44\x54\x29\x54\x86\x59\xa7\xbf\xfe\x44\xab\xab\xf1\x05\x4e\x46\x93\xf8\x92\xd4\x81\x8d\xaf\xce\xd3\x81\x92\x7a\xf5\x4e\xf9\x47\x52\xf6\x95\xf8\xdc\x50\x3f\xaf\x9b\x5f\x9b\x6c\x0f\x63\x8d\x01\x9e\x80\x0a\x01\x2b\xda\x5d\x5d\x45\xbc\x59\xd4\xaf\x93\x22\x80\x32\x34\x5d\x7b\x25\xaa\x34\x64\x15\x51\xe6\x09\x20\x40\x0b\xd1\x52\x4d\xbd\x14\x2b\xf6\x04\x50\x61\xe5\x6e\xe0\xbf\x84\x20\xd5\x12\xcf\x9f\xf7\x9b\xca\x77\xf8\x0f\x2f\x43\x8b\x3c\x7f\xde\x6f\xbc\x7a\xea\x2f\xf0\xfc\x79\xbf\xce\xbe\x93\xff\x42\xc7\x79\xa3\xf0\xf0\x7c\x03\x7a\xfe\xfa\x35\xcb\x07\xa9\xbe\x6e\x50\x15\xa0\xf6\x96\x21\x64\xb7\x24\xaa\xd5\xae\x6a\x75\xa6\xf5\x93\x45\x19\xd7\x23\x85\xc8\xcb\x1b\x93\x3a\xd8\xf2\x28\x0d\xe8\xbf\x3a\x8d\xb0\x97\xf4\x06\x89\x93\x92\x7c\x59\x66\x04\xa3\x4c\xc1\xea\x2a\x22\xbb\x04\xdc\xc4\xa0\x50\x59\x48\x74\xf1\x58\x2b\x6d\x25\x45\x00\x2f\x45\x71\x34\xb9\xa6\xcb\x71\xeb\xd7\x83\xa3\x2d\xf4\x19\xbd\x46\xeb\x00\x93\x37\x58\x77\x61\x41\xef\xe2\xf4\xce\xb2\x6f\xbc\xbf\x7c\x2d\x69\x67\x01\xb1\xae\xaa\x9e\xd7\x7f\xa1\xcc\xb9\xac\xc8\x69\x15\x37\x64\x18\xbb\x55\xc6\x13\x45\xb3\x7c\xc0\x2c\xd4\xf3\x24\x1e\xe4\x97\x7a\x40\x68\x70\x37\x92\x2f\x03\xa1\x5b\xc8\x41\x68\xb1\x2c\xc4\xa5\x03\x42\xd8\x36\xcd\x53\x56\xf4\xc4\x14\x8d\xd8\x67\x05\x57\x5d\xf5\xbc\x8c\x50\x84\x3c\x82\x11\xba\x9d\x70\x84\x96\x14\x90\x90\x2e\xcf\xd9\x87\x2e\x49\xf7\xea\xd9\x4b\x2c\x8d\x57\x86\x64\x25\x8a\x2b\x02\x96\x57\xc4\x52\x0a\x2f\x21\x69\xb5\x1e\x25\xad\xef\x5d\xd2\xf2\xc8\x57\x1e\xf5\xce\xc9\x51\xbe\x9c\xb3\xac\x7a\xc7\xc1\xd2\x4d\x5e\xfe\xc8\xc4\xff\x79\x4c\x3c\xf7\x34\xfb\x00\x2c\x7b\x2f\x1a\x24\x18\x22\x37\x30\xe0\x06\x48\x26\x87\xc8\xc9\x7d\x81\xa8\x31\x8d\xe7\x0b\xdc\x96\x7f\x45\xb5\xbf\xd5\xe6\x50\x74\x57\x58\x7c\xde\x26\x65\x96\xd8\x05\xda\x8f\xbb\xc0\xdf\x62\x17\xd8\x9e\xe0\x41\x96\xc4\x51\x38\x40\xbd\x78\x88\xfb\x71\xbc\x58\xe1\xbf\xdd\xcb\x53\xf8\xd3\xaf\x4b\xed\x08\xdb\x3d\x5d\xe1\x4f\x9e\xef\x6b\x07\x50\x59\xbb\xce\x40\xf4\x7a\x79\x5a\x4c\x82\x8f\xb6\x90\x1e\x0a\xbf\x21\xbe\x15\x7e\x3c\xf5\x52\x6f\xb1\xde\x0c\xca\x2c\xb1\x8e\xff\xde\xc9\x91\xff\xe7\xac\xe3\x83\x79\x36\x9b\x67\xc5\x2f\xed\x0e\x72\x2f\xed\x0e\x96\xbf\xb4\x33\xa5\xba\x03\xe3\x12\xef\xe0\xaf\xbd\x0e\x7a\x70\xa9\xce\xd6\xcd\x8b\x37\xf7\x2b\xd9\xe5\x34\xf4\xbd\x48\x77\xff\xa4\x13\xf6\x81\x71\xad\xe9\x13\xa2\x0e\x0a\x5c\x5a\x1c\x2c\x79\x69\xf1\x98\xc5\xee\xef\xc1\x7c\x37\x3f\x1c\xef\xa1\xdf\xaa\x2f\x1b\x4d\x6e\x20\x8e\xd2\x8c\x2c\xef\xf3\x6b\x8b\xfb\xce\x82\x61\x75\x33\x4a\xc3\xdf\x48\x69\x91\x0b\x6e\x16\x0c\x55\xf6\x37\x0c\xb2\x40\xb9\x08\xf5\x5d\x80\xa6\xfa\x0d\x28\xa9\x75\x2c\x0d\x7e\x35\x03\xe0\x57\x7a\xd1\xbe\x99\x56\xa4\xef\x4b\x28\x02\x44\x31\x8f\x32\xd1\x33\x23\x98\x15\xd8\xe2\x1d\xd2\x6f\x16\x30\xfa\xe2\x85\x8e\xd9\xbf\x8c\xef\x56\x6b\x34\xa6\xcd\x24\x48\x69\xe4\x2c\x34\x8b\xd3\x50\xf7\xc0\x27\x8d\x92\xef\xa4\xfe\x61\xcc\x3b\x2b\x5a\x78\x6e\x60\xf4\x02\xd5\x8d\x46\x0e\x83\xa1\x7c\x86\x81\x12\xd9\x46\xf4\xd7\x94\x95\xa8\x6d\xc9\x90\x5a\x7a\x23\x32\xa4\x96\x5a\xda\x15\x5c\x4b\xb7\xcc\x7e\x6e\x00\xe2\x76\x88\xdc\x02\x77\x1e\x39\x88\xc3\xa4\x88\xb7\x38\x53\x12\xce\x6b\x53\x45\x15\xf8\x62\x34\xf3\x67\x4e\xe9\x73\x49\x47\xf3\x05\x39\xfe\xb2\xbe\xcb\x8b\x20\x05\x05\xb6\xaf\x58\x1e\x12\x06\x18\x4f\x6f\x9f\x3e\xb9\x71\xf2\x4d\xbe\x5c\xae\x5e\x36\x9a\x4b\xf1\xce\xbb\x25\x26\x7b\xe4\x9d\xdf\x8a\x77\xee\x1d\x1f\x20\x08\x89\x5b\x8c\x75\xee\xb1\x00\xba\x77\x65\x9d\x7f\x39\x3b\x94\x4b\x62\x01\x3f\x74\xb0\x2a\x9a\x0e\xc0\x1d\x81\xae\x9a\x04\xd1\x30\x9e\x96\x2c\x0e\x58\x2e\x57\x0d\x49\x29\x1f\x0e\x4b\x1d\x76\x6a\x71\xb9\x46\xeb\xac\x42\xc0\x3d\x32\x2a\x93\x51\x71\xe2\x5c\x8a\x51\xfd\xbd\x33\x2f\xfc\x8f\x62\x54\xab\x7b\xdb\x3d\xf4\x72\xed\xe5\xda\x8b\x3a\x62\xb4\x81\xf6\x71\x36\x8e\x87\xa8\xe1\xe3\x56\x10\xda\xfb\xb6\xdc\x6a\x73\x38\xa4\xfe\x83\xfa\x82\x28\xc0\x05\xf8\xea\x25\xb5\xe9\x1f\x5f\xb4\x5a\x03\xff\x07\x27\x31\xe4\x0e\xcb\xc6\x18\x25\x38\x55\xf8\xa2\xd6\x11\x52\x8e\xf5\x98\x3c\x5b\x78\xdf\x8a\x17\xb0\x85\xf8\x07\xc3\x41\x5f\x8d\xde\xe6\x01\x34\x85\xe7\x5e\xd8\x71\x84\xd1\x34\x4e\x30\x15\x1e\x5f\xbc\x80\xbe\xf9\x46\x91\xaf\xf7\x17\x2f\x0a\x2e\x70\x98\xcf\x65\x16\xf8\xda\xdd\xa2\x9c\x3f\x2e\xf0\x6f\x76\x8a\x43\x51\x1c\xcf\x8a\x89\x21\x1f\x38\x39\x7a\x57\xb6\x20\x76\xff\x9a\x90\x45\xf2\x68\x4e\x34\xb5\x14\xd1\xdd\x2d\xdc\xec\x23\xd1\x7d\x2b\xa2\xfb\x3f\x0a\xf3\xcb\x27\x39\x85\x07\xfe\x85\xc2\x6f\xe1\x83\xb3\x7a\xbe\xb5\x04\xe0\x52\x29\x5f\x04\x2e\xa3\xaf\x5f\xcd\x57\xb7\xda\x62\xdc\x3d\x5e\x1c\x57\x60\x75\x15\x7d\x24\xf0\xf5\x7a\xa1\x15\x29\x00\x34\x0b\xa2\xcc\xe5\x38\x9c\x60\x54\xfa\xa1\x24\x7d\xad\x65\x0c\x6e\xf0\x38\xb4\x62\x6e\x0b\x13\x4e\x4b\x91\x19\x8a\x2d\x09\xe9\x2a\x4a\xd3\xb1\x1b\xe2\xf1\x16\xd9\xbd\x14\x0a\x5a\x8a\x97\xfc\xbd\x1d\xb7\x1c\x39\xba\x68\x92\xac\x87\xe5\x2b\x32\x13\x12\xb4\xf6\xd7\xe7\xf9\x78\xd8\x24\xe1\xc5\x62\x62\x5b\x31\xaf\xc5\x97\xe3\xdd\xcd\xba\x8c\xf5\x4c\x9e\x94\x8f\x76\x22\x70\x97\x83\xe8\x61\x90\xa6\x64\x21\xbf\x20\xa8\x0d\xd1\x3b\x7c\x8d\xb6\x70\x12\x5e\xd0\x9c\x90\x3b\x7c\x50\x1a\xf9\x31\xa7\x0f\xdf\xbc\xdb\xda\x69\xc8\xd6\xc4\x73\xc1\xc4\xe3\xbd\x38\x1a\x85\xe7\x73\x96\x89\x32\x86\xac\x90\x69\x5e\x7e\xc9\x24\x9e\xe1\x24\xbb\x46\x7f\xd2\x63\x31\x78\x93\x02\xf3\x3d\x19\xd3\x1c\xc7\x29\x79\x08\x23\x96\x2e\x20\x8b\x85\x2f\x4d\x15\x6d\xe1\x51\x30\x9f\x64\x5d\xd4\x42\xa5\x7a\x63\x1d\x12\x29\x97\x7d\xf0\x3d\x09\xcd\x71\xc2\x13\x99\x4b\x70\x64\xfc\x17\xa1\x19\x66\x2c\x79\x66\x0a\xa0\xe4\xa1\x5e\xf9\x90\xc5\x68\x86\x93\x51\x9c\x4c\x15\xe0\x1a\x64\x25\xfd\xe3\x60\x74\xde\xf5\x8d\x32\xa2\x17\x5f\xc7\x10\x73\xa6\xde\x58\x5f\x6d\x36\x8c\x10\xdc\xb4\x2b\x14\x75\xe3\x93\x44\x48\x6b\xfc\xa6\x9c\x97\x90\x34\x2f\x81\x3c\x99\x95\xa1\x24\x2d\xbe\xde\x16\x67\x11\x3d\x00\x3e\x77\x43\xba\xaa\x66\x0c\x25\xe3\x37\x70\xd1\x0d\xf7\x37\x1b\xc5\x09\x9c\x62\x64\xa3\xf7\x90\x18\xf4\xcb\x70\x64\x25\x8d\xa7\xd4\xce\x4f\x8f\x9a\x19\xd6\x32\x15\xff\x94\x93\xb5\x4e\xd3\x4f\xde\x19\x4c\x45\x9f\xc6\x5a\xad\x66\x02\xce\xc9\x5e\x3f\x18\x9d\xbb\x0d\x2f\xc8\x44\x6c\x88\x9f\x9c\xf0\x48\x71\x5f\x30\x0c\x7b\xbd\xc3\x75\x05\xf5\xa0\x2b\xca\x82\x6e\x93\x6f\x76\xc6\x60\x03\xb5\xf0\x87\x6a\xc1\xca\x69\x30\xc9\xd0\x26\xfc\xb3\x7c\x22\x5a\xee\x46\xa3\xf8\xb5\xdf\x85\xec\x68\x22\xf5\xe1\xa8\xca\xa2\x92\x94\x78\x67\x2a\x80\x9f\x77\x52\x59\x71\x75\x5e\x8d\x9a\x4b\xe5\x76\xd1\xa7\xde\x69\x40\x18\x66\x9e\xa4\xb0\xcc\xcb\x1e\x7c\xf7\x19\xad\x12\xf2\xa1\x3c\xa8\x22\x66\xc7\x6d\x96\xe8\x4f\x50\x0e\xb2\x29\x1d\x6c\x9a\x6e\xde\xd2\xe7\xb8\x42\x3d\x81\x9c\xbc\x17\x0d\xf1\x95\xab\xc6\x69\xed\x8a\x29\x80\x1c\xd1\x3a\x17\x84\xe8\x12\xa8\x08\x61\x59\xbc\xf1\xe6\xaf\x97\xd8\xf0\x4a\xf2\x8d\xb7\x12\xdf\xf2\x36\xc8\xac\x54\xd9\x93\xcb\x08\x43\x6e\x2d\xb4\xa8\x7c\xb1\xc0\xc8\x42\xff\xc8\x04\x75\xa3\x83\x3c\x2e\xd2\x6b\x8e\x8f\xd3\xb8\x40\x74\x92\xe5\x39\xe6\xc9\xb2\x81\x02\x65\x1a\x5f\xd9\x6b\x73\xce\x10\xcb\xe8\x2d\x53\x03\xdb\xdf\x17\x67\x63\x00\xf8\xda\x10\x3b\x47\xd7\x2e\x2e\xb2\x18\xc9\x57\xac\xe3\x1e\x44\xf6\xc4\x18\xbb\x41\x87\x6a\x34\x3b\x06\xd6\x81\x85\x66\xcb\x51\xa7\xb6\x1c\xca\xf4\x79\x8d\x39\x10\xf0\x73\xad\x09\x18\x3d\x31\xd2\xea\x47\xd7\x58\x17\x19\x6f\xb4\x28\x14\x94\xab\xb3\x7c\xf4\xd5\x77\xee\x80\x55\x4a\x13\xbf\x1d\x1c\xe9\xdd\x01\xd7\x29\x87\xc7\xb5\x35\x6e\x9f\xa9\x0d\xcc\x67\x6e\x03\xa3\xcc\xe6\x2b\xf4\x39\x67\xf4\xc8\x9f\xac\x71\xfa\x19\xcc\x61\xac\x8e\x9c\x7e\x36\xcd\x62\xf8\xdf\x8d\xfd\xda\x0c\x38\x45\xfe\x14\xe6\xc0\x74\xd3\xd0\xa8\x6b\x4a\x0c\x26\x71\x5a\x3b\x7b\xfe\x3c\xdf\xa4\x48\x01\xae\x1c\x7d\x39\xdf\x70\x04\x31\x63\x7b\x99\xac\x97\x67\x40\xa9\x1e\x23\xee\xb4\xa1\x17\x09\x36\x93\xbb\x91\x2f\xb9\x89\xdf\x97\x68\x19\xa6\xae\x74\xfb\x8b\xa3\xd7\x38\x44\x83\x7b\x08\x62\x43\x45\x04\x21\x19\x52\xa1\xd0\x27\x26\x2c\x57\xad\x82\x3c\xb2\xe9\x5d\xc0\xe4\xca\xa6\x32\xc8\x8e\x38\x4a\xfa\x04\x98\x0a\x32\x05\x55\x36\xec\xba\x58\x4c\x0a\x2d\x10\x9e\x6e\xf2\x6c\xd1\x28\x34\x77\xa0\x1e\x33\x85\x2e\xcf\x09\x7b\x73\x56\x59\xfb\x7b\xfb\xd0\x2f\x91\xd6\x7d\x71\x72\xf4\x87\xd5\x1d\x79\xd3\x6b\xfb\xb2\x5e\xff\x13\xb4\x4b\xc7\x60\x9c\xd9\xe3\xc6\xbb\x54\x89\xa4\xbe\xcc\xd3\x23\x09\x3c\x8e\xf0\x3c\x0d\xfa\x13\xcc\xc2\x81\x29\xe8\x1c\x23\x35\xd5\x22\x85\x62\xbe\x79\x8b\xf4\x0c\x6b\xca\xb6\x70\x04\xd9\x94\x11\x33\xb4\x65\x36\xc6\xb6\x26\x49\x94\x87\x18\x2b\x61\x8a\x02\x44\x13\x30\xa3\x0b\x9c\xa4\x10\xb5\x6c\x1c\x64\x28\xc2\xe7\x13\x3c\xc8\xf0\x90\xb0\xe1\x01\x4b\xa9\x9a\x31\x85\x4f\x16\xa3\x49\x98\x65\x13\xfc\x82\x06\xb8\xac\xea\x40\x71\x92\xc4\x09\x1a\xc6\x38\x8d\x56\x32\x14\x8c\x46\x78\x40\xeb\x52\xa4\x56\x52\x94\xe2\xc1\x3c\x09\xb3\xeb\x8a\xa8\xd8\x9f\x67\x28\xcc\xa0\x12\xaf\x11\x66\xa9\x08\xa8\x10\x4e\xc2\x8c\x39\x71\xd3\xbc\xae\x21\xe1\xcf\x53\x1c\xd1\xfd\x20\x75\x29\xca\xe8\x80\xbc\xa7\x9d\x13\xea\x32\xe3\xad\x3a\x7f\xb7\x4d\xda\x96\x7f\x48\x79\xa7\x9a\x41\x7b\x0f\x18\xd2\x7a\x1b\x4e\x0d\x17\x79\xa7\x85\x90\x9d\xd0\xc8\xee\x85\xbd\xe7\xb4\xdf\x44\xbb\xe4\x97\x23\x71\xdc\xbb\xd3\xda\x59\x05\x95\xde\x9d\x36\xcf\x58\xb0\x00\xf4\x95\x3c\xb2\xab\x80\x7a\xa7\xec\x48\x22\xf7\xee\xb4\x4e\x2b\xd5\xf4\x4a\xcd\xfc\x4a\x0d\x5a\xa9\xae\x57\xaa\xe5\x57\x6a\xd2\x4a\x0d\xbd\x52\x5d\x54\xd2\xeb\xb8\xb2\x23\x59\x43\xc6\xbd\x0c\x7d\x83\xd6\x13\x83\xd6\x73\x0f\x9a\x8d\x8f\x32\x5c\xac\x4f\xf4\xc2\x64\x34\xe2\x69\x07\x29\xd2\x34\xc8\x6a\xad\x46\xbe\xb8\xfa\x6b\x4f\x44\x53\x87\x5c\x77\x42\x6e\x14\x82\x5c\xf3\x0e\xbc\x02\xc3\x80\xdc\x2c\x04\xb9\xee\x9b\x9d\x8a\x02\xc3\x80\x5c\x33\x20\x2f\x9e\xc8\x5e\x90\x24\xd7\xa8\x6f\xa6\x53\xa5\x53\xd5\xa7\xf1\x2f\x6c\x4d\x46\x46\x27\x9f\xb0\x9e\xf4\x3a\xcd\xf0\x14\x8d\xe2\x79\x82\xb2\x70\x6a\xce\xfd\x92\x41\x79\x23\x7c\x95\x1d\x93\xd5\xe7\x8f\x1f\xeb\x88\x78\xbb\x1f\x0f\xc3\xd1\x35\xe5\x84\x94\x0e\x0b\x60\xb1\xee\xc7\xa2\x77\x4a\x1d\x07\x7e\x3b\x85\x94\x97\x10\x6d\xc5\xca\x14\xe7\x4a\x92\xfb\x0b\x4a\x71\x36\x9f\xe9\x1f\x72\x3c\x3a\x16\x1f\xf6\xf7\x7e\xa1\xae\x1d\x79\x27\xfc\xbd\x5f\x3e\xd5\xd0\x06\xda\xfb\xc5\x4e\x8d\xa6\x14\xa9\xd3\x22\x75\x67\x34\x63\x75\x49\xc3\x54\xa6\xf3\xfe\x05\x26\xa2\x82\xef\xe8\x5f\xa3\xc1\x8f\xa1\x6d\x1a\xfd\xf8\x2b\xa2\x4f\xbe\xe8\xc7\x6a\x71\x16\xe6\x58\x94\x97\xd7\xa1\xee\x30\xc7\xa2\xd9\x86\x68\xb6\xae\x35\x5b\x5f\xd4\x6c\x5d\x6f\xb6\xbe\x5c\xb3\x10\x46\x27\xac\xf1\x25\x48\x80\x84\x0d\x7d\x05\xfa\xaa\x36\xa1\x6a\x83\x2f\x66\xa8\x5a\xd3\x97\xa9\x67\x46\x18\x59\xe7\xb1\x56\x04\xd4\x5a\xa3\xe7\x7a\x33\xb6\x3f\xfd\x58\xa7\x1f\xeb\xce\x8f\x0d\xfa\xb1\xe1\xfc\xd8\xa4\x1f\x9b\xce\x8f\xad\xbc\x36\xdb\x79\x6d\x76\xf2\xda\x5c\x13\x6d\xe6\x68\xa4\x0a\x71\x1e\xb4\x3c\xf7\x41\xc5\x38\x10\xb2\x95\x14\xaa\x1f\xd1\xbd\x24\x77\xf5\x2a\xaf\x15\xe9\xa3\x10\x67\xd6\x8b\xb8\x7b\xe7\xdf\xde\x61\x70\xa5\x97\x19\x70\x21\xbd\xf4\x31\x0d\x35\xf4\x1b\x10\x21\x2a\xfd\x46\xe6\x9e\xaf\x12\x78\x16\x7b\xef\x2b\xb3\x62\x9d\x56\x6c\xb0\x8a\x6b\x46\xc5\xb6\xb7\x62\x83\x56\x6c\xb1\x8a\x75\xa3\xe2\x9a\xb7\x62\x93\x56\xec\x9c\x09\xd4\xb4\x8a\x75\x59\xf1\x4e\xbb\x58\x5e\x94\x7a\x8a\x08\x8f\x1d\x7f\xcc\x52\xb2\xb3\xe0\xf1\xf0\x78\x9b\xe8\xf1\x1c\x0e\x63\x70\x02\x8e\x2b\x7e\xbc\x13\x5f\xa7\x13\x1e\x52\x72\xf4\x0a\x6f\xba\xe3\x7c\x2f\x3a\x95\xfa\x85\x1d\x8f\xbc\xb9\x95\x1f\xc3\x0b\xfa\xa5\xd3\x5a\x6d\x36\x4c\xb5\x9c\x58\x26\x82\x60\x4b\x05\x5d\xa1\xb4\xf5\xa1\x7d\x51\x44\x50\xc3\xe0\xe7\x38\xb8\xc0\x28\x9e\x0c\xbd\xac\x76\x09\xf9\xa1\xf7\x89\x4e\x6e\xcf\x8c\x77\xa8\xb5\xd8\x0b\x26\x83\xf9\x84\xac\xb0\x08\x5f\x7a\x9b\xed\xb1\x44\x30\x3d\x9a\x08\xa6\x76\xd5\x1a\x36\xe1\xff\xd0\x73\x2e\xa1\x99\xf9\x5a\x7a\x2c\x2f\x4c\x8f\xe6\x85\xa9\x5d\xb1\x1a\x4d\x88\x29\xdf\xe3\x02\x6a\xad\x8c\x5e\xa3\x52\xef\x93\xf2\xfc\x1f\xa8\x8e\xba\xa8\x56\xb6\x21\x36\x18\xc4\x06\x85\xc8\x00\xb6\x18\xc4\xba\x01\xb1\x5e\x00\x62\x93\x41\x6c\x5a\xdd\x2a\xd1\x76\x34\x88\x8d\x02\x10\x5b\x0c\x62\xcb\xd9\xeb\xa6\x01\xb1\x59\x00\x62\x9b\x41\x6c\x3b\x7b\xdd\x32\x20\xb6\x0a\x40\xec\x30\x88\x1d\x67\xaf\xdb\x06\xc4\x76\x01\x88\x6b\x0c\xe2\x9a\xb3\xd7\x1d\x03\x62\x67\x21\x44\x29\xf6\x53\xa0\x5a\xf5\x35\xb3\xba\xe9\x1d\x23\x68\x9a\xec\x3e\xe7\x2f\xee\xb0\x88\x48\xa9\xf3\x2b\xe0\xd5\x21\xe9\x5a\xcf\x91\x84\x83\xa7\xcb\x4f\xe6\x83\x0c\x8d\xc3\xf3\x31\x0a\xa2\x21\x9a\xc4\x97\x28\x48\xce\xe7\x10\xfe\x05\xdc\x9c\xff\x7b\x1e\x24\x56\xe2\x1e\x68\x20\x40\x1b\xa4\x15\x2e\xc5\x39\x94\x07\xe7\x7d\x5a\x84\xee\x12\xce\xe3\x13\xef\xb3\x86\x41\x82\xd3\xf9\x24\x43\xf1\x28\xaf\xf9\x31\xdd\x02\x4a\xe7\x01\xfa\x09\x9d\x07\xd4\x75\xa5\xbe\x56\x46\xcf\x11\x7d\xd5\x67\xaf\xda\xf0\xaa\x0f\xaf\x5c\x48\x4e\x28\x20\xa5\x2b\xf4\x48\xf8\x13\x3a\xbf\x82\x19\x2e\x03\x41\xf0\x02\x42\xec\x54\x0a\xb8\x12\xc1\x90\x0e\xfd\x76\x70\x84\x20\x9c\xa4\xfa\xf1\x2d\xe5\x70\xe7\x63\xf4\x3b\x3a\x9f\x14\x65\x72\x6e\xa5\xca\x6f\x8c\xc5\xbd\xa5\x2c\xae\x54\x7a\x2b\xb7\x6f\xb2\x93\xbd\x55\xc4\x82\x32\x2b\xd0\xd1\x0b\x74\x64\x01\x93\x9e\x7f\x63\xdc\xf0\x2d\xe5\x86\x25\xda\x8c\xdc\x6f\xdf\x72\xfe\x07\xfb\xed\x73\x44\x5a\xb3\x61\x34\x18\x8c\x06\x87\x51\xd7\x11\xa8\x5b\x18\xd6\xf4\x02\xb5\x3c\x0c\x9b\x0c\x7a\x93\x43\x6f\xe8\x18\x36\x0c\x0c\xeb\x0e\x0c\x5b\x0c\x46\x8b\xc3\x68\xea\x08\x34\x2d\x0c\x1b\x7a\x81\x46\x1e\x86\x6d\x06\xbd\xcd\xa1\xb7\x74\x0c\x5b\x06\x86\x4d\x07\x86\x1d\x06\xa3\xc3\x61\xb4\x75\x04\xda\x16\x86\x2d\xbd\x40\x2b\x0f\xc3\x35\x06\x7d\xed\x4c\x23\x11\x81\x61\xc7\xc0\xb0\xad\x61\x58\x28\xf1\x47\xca\x93\x4e\x08\x5d\x6b\x81\xb4\x13\x8b\xae\xbb\x28\xac\x0c\x5f\x65\xea\xbd\x93\xaa\x49\xe5\xa1\x14\xb4\x34\x0e\xf4\xb6\xc8\xbe\xbf\x9a\x4d\x02\x82\xcd\x55\x86\xbc\xe0\x58\x9c\x99\x92\x6c\xd9\x05\x51\x5c\x5c\xe5\x29\x75\xf5\xe4\x1d\x6a\xc9\x72\xde\x1d\x94\x5a\xb0\xb0\x31\x72\x45\xbf\x1b\xe9\xb6\x5b\x15\x79\x29\xd2\x6d\x77\x2a\xec\xae\xa4\xdb\xa9\xdf\x9c\x55\xd6\xfe\xde\x91\x08\x1f\xef\xab\x1e\xef\xab\x1e\xec\xbe\xca\x58\xe2\xf2\x3e\xc7\xbc\xc9\xf9\x7b\xdd\xe1\xdc\x57\x56\xb8\x77\xe2\x68\xfe\x4e\x3f\x9a\xbf\xbb\xed\xd1\xfc\x9d\x7e\x34\x7f\x97\x77\x34\x5f\xa4\x60\x7e\xbc\xa9\x7a\xbc\xa9\x7a\xbc\xa9\xd2\xbe\x3c\xde\x54\x3d\xde\x54\x3d\xde\x54\xc9\x66\x1f\x6f\xaa\xcc\x8f\x8f\x37\x55\x9e\xc7\xc7\x9b\xaa\xc7\x9b\xaa\xc7\x9b\x2a\xf8\x7b\xbc\xa9\x2a\xa6\xc4\x7d\xbc\xa9\x7a\xbc\xa9\x7a\xbc\xa9\x52\xfe\x1e\x6f\xaa\x1e\x6f\xaa\x1e\x6f\xaa\x1e\x6f\xaa\xfe\x27\xdf\x54\xdd\xdb\x1d\xd5\xed\x6e\xa7\x8a\xdc\x4b\x15\xb8\x91\x7a\xa8\xbb\xa8\xbf\x77\x3e\x94\xc7\xbb\xa8\x7f\xfe\x5d\x94\x7a\x77\xd4\x6b\x2d\x74\x74\x52\x6f\x8e\x7a\x2d\xe5\xda\x08\x1e\x1e\xfe\xce\x88\x7a\x69\x8a\x5b\x23\x77\x50\x01\xee\xa1\x9d\x77\xad\x04\x6e\x9c\xaa\x47\xb1\x12\x33\xdd\xd6\x57\x44\x61\x86\xd2\x7e\x7c\x65\xc3\x39\x16\xe8\x1c\xab\xd7\x74\xfc\xcf\x25\x4d\x36\xda\x1d\xff\xa1\x9c\x1d\xba\xc3\xc5\x6a\xdc\x77\xf8\xda\xa5\xc7\xd5\x5b\xac\x70\xff\xf1\x85\x0d\xb3\x41\x21\x43\xc0\xa3\x4a\x84\xe8\x5f\xea\x38\x79\x54\x87\xac\x12\xd9\xda\xf8\xd8\x9f\x6a\x80\xec\x48\x68\xda\x67\x2b\x28\x9a\xeb\xec\x4f\x7a\x51\xfa\x8c\x9e\xd3\xf1\x79\xce\x1b\x2d\xa3\x7f\x41\xaf\x3c\xb1\x14\x2e\x83\x99\x1b\x67\xd8\x37\x6c\x0d\x81\x32\x01\xc7\x6e\xc7\x78\xf2\x9a\xcc\xf8\xe2\xe9\xe9\x39\x55\xfc\x2c\xab\x86\x20\x9a\xcf\x2c\xcb\xac\x00\x74\x67\xb5\x1c\xd7\x84\x80\x16\xc4\xca\xbf\x4e\xa6\xc7\xad\x32\xd4\x5a\x16\x4e\xce\x8d\x76\xc7\xa3\x10\xa9\x79\x95\x21\xce\x46\x8b\x2a\x46\x94\xf5\x64\x28\x46\xe4\xa0\x85\xc6\x97\xcf\x72\x38\x17\x66\x80\x07\xe5\xa0\x5e\xfd\x8b\x8a\xa7\x31\x1f\x62\x35\x45\x74\x19\x45\x54\xa5\x16\x39\x16\x51\x08\x1a\x74\x9a\x30\x8e\x51\xa5\xf6\x5d\x23\x61\x0f\xe1\x3a\x89\x36\x87\x60\xfd\xc4\x2a\x09\x55\x7f\xaf\x77\xf6\x2b\xa9\x5b\x62\x6b\x8a\x54\x61\x78\x9d\xc9\xbc\x06\x91\x99\xc7\xc0\x38\x3e\x7d\x84\x38\x28\x8e\x1b\x2d\x49\xea\xa1\x75\x76\x27\x63\xa1\xcd\x15\x13\xcb\x34\xec\xbe\x57\xb9\xb7\xd7\xba\x0f\xa1\xb7\xd7\x5a\x5a\xe2\xb5\xf7\x58\x43\xdc\xed\xb5\x9c\xb1\x2d\xe0\x86\x26\xc4\xc3\x5b\xec\xf0\x5b\x49\x3c\xd3\x76\x79\xf6\x02\x06\xe1\x1b\x44\xc5\x1b\x92\xe6\xf4\x40\x73\x86\x9e\x9f\x4c\x3c\x29\x25\x42\xcd\xa1\xfa\xcb\x86\x0a\xd6\x8c\x35\x47\x50\x57\xa2\x7e\x19\xab\x98\x80\xea\xea\x20\xf4\x88\x71\x85\x84\x18\xd2\x06\x2f\x98\x7f\x87\x41\xc6\x33\x67\x03\x17\x86\x2f\x04\x2f\xb2\x8b\xff\x0c\x9b\xf9\x8b\x17\xce\x3d\x7c\x09\x76\x8f\x16\x24\x40\xfa\x8e\x56\x1b\x19\xa2\xfb\x59\x71\x00\x69\xf9\x55\xc7\x68\x3e\x7f\xe5\x91\x42\xf9\x27\xcd\x5e\xeb\xa1\x8e\x99\x77\x4b\xd7\xf7\x2d\xcf\x97\x0f\x76\x0a\xfc\xb6\x41\x9c\x09\xab\xc2\x29\x4e\x2e\xf0\xd3\x27\xa5\x41\x19\x35\x6a\xf5\x06\xea\x5f\xa3\xde\xff\xf7\xff\x0e\x93\x70\x80\xf6\x71\x1a\x85\x93\x2a\xda\x9c\x4c\x50\x12\x9e\x8f\xb3\x14\xb1\xf2\xc3\xea\xd3\xa7\x4f\x8e\xf0\x30\x4c\xb3\x24\xec\xcf\x01\x7e\x10\x0d\x21\x28\x4f\x18\xa1\x34\x9e\x27\x03\x0c\x6f\xfa\x61\x14\x24\xd7\x84\x1d\x4c\xd3\x0a\x8b\xd2\x90\xc0\xbf\xf1\x3c\x43\x53\xe0\xe9\x03\xe0\xac\x15\x14\x24\x18\xcd\x70\x32\x0d\xb3\x0c\x0f\xd1\x2c\x89\x2f\xc2\x21\x1e\xd2\xa0\x13\x64\x9d\x8e\xe2\xc9\x24\xbe\x0c\xa3\x73\x34\x88\xa3\x61\x48\xd7\x30\xa9\x34\xc5\x59\x97\xad\xf8\x17\x48\x47\x2b\x05\xc5\x30\xc5\x67\x10\x0f\x31\x9a\xce\xd3\x8c\x6c\xd4\x41\x18\x01\xd0\xa0\x1f\x5f\x90\x4f\xb3\x6b\xe8\x22\x8a\xe2\x2c\x1c\xe0\x0a\x8d\x2b\x34\x09\x53\xd0\x2c\xab\xed\x45\x43\x03\x99\x61\x98\x0e\x26\x41\x38\xc5\x49\xd5\x87\x43\x18\xa9\x03\xc1\x71\x98\x25\xf1\x70\x3e\xc0\xf7\x8e\x06\x62\x5d\x1b\xc6\x83\xb9\x88\x83\x41\x6a\xac\xc6\x09\x8b\x91\x31\x0d\x32\x9c\x84\xc1\x24\x95\xc3\x0c\x73\x03\xd5\x14\xd4\xc9\x3c\x9f\xec\xee\x1d\xa3\xe3\x83\x9d\x93\x5f\x37\x8f\xb6\xd1\xde\x31\x3a\x3c\x3a\xf8\x65\x6f\x6b\x7b\x0b\xbd\xf9\x37\x3a\xd9\xdd\x46\xbd\x83\xc3\x7f\x1f\xed\xbd\xdd\x3d\x41\xbb\x07\xef\xb7\xb6\x8f\x8e\xd1\xe6\x87\x2d\xd4\x3b\xf8\x70\x72\xb4\xf7\xe6\xe3\xc9\xc1\xd1\x31\x7a\xb6\x79\x8c\xf6\x8e\x9f\xc1\x87\xcd\x0f\xff\x46\xdb\xbf\x1d\x1e\x6d\x1f\x1f\xa3\x83\x23\xb4\xb7\x7f\xf8\x7e\x6f\x7b\x0b\xfd\xba\x79\x74\xb4\xf9\xe1\x64\x6f\xfb\xb8\x82\xf6\x3e\xf4\xde\x7f\xdc\xda\xfb\xf0\xb6\x82\xde\x7c\x3c\x41\x1f\x0e\x4e\xd0\xfb\xbd\xfd\xbd\x93\xed\x2d\x74\x72\x50\x81\x46\xed\x6a\xe8\x60\x07\xed\x6f\x1f\xf5\x76\x37\x3f\x9c\x6c\xbe\xd9\x7b\xbf\x77\xf2\x6f\x68\x6f\x67\xef\xe4\x03\x69\x6b\xe7\xe0\x08\x6d\xa2\xc3\xcd\xa3\x93\xbd\xde\xc7\xf7\x9b\x47\xe8\xf0\xe3\xd1\xe1\xc1\xf1\x36\x22\xdd\xda\xda\x3b\xee\xbd\xdf\xdc\xdb\xdf\xde\xaa\xa2\xbd\x0f\xe8\xc3\x01\xda\xfe\x65\xfb\xc3\x09\x3a\xde\xdd\x7c\xff\xde\xd9\x4b\x82\xbb\xd6\xc7\x37\xdb\xe8\xfd\xde\xe6\x9b\xf7\xdb\xb4\xa5\x0f\xff\x46\x5b\x7b\x47\xdb\xbd\x13\xd2\x1d\xf9\xab\xb7\xb7\xb5\xfd\xe1\x64\xf3\x7d\x05\x1d\x1f\x6e\xf7\xf6\xc8\x8f\xed\xdf\xb6\xf7\x0f\xdf\x6f\x1e\xfd\xbb\xc2\x60\x1e\x6f\xff\xef\x8f\xdb\x1f\x4e\xf6\x36\xdf\xa3\xad\xcd\xfd\xcd\xb7\xdb\xc7\xa8\xb4\x60\x48\x0e\x8f\x0e\x7a\x1f\x8f\xb6\xf7\x09\xce\x07\x3b\xe8\xf8\xe3\x9b\xe3\x93\xbd\x93\x8f\x27\xdb\xe8\xed\xc1\xc1\x16\x0c\xf4\xf1\xf6\xd1\x2f\x7b\xbd\xed\xe3\x57\xe8\xfd\xc1\x31\x8c\xd6\xc7\xe3\xed\x0a\xda\xda\x3c\xd9\x84\x86\x0f\x8f\x0e\x76\xf6\x4e\x8e\x5f\x91\xdf\x6f\x3e\x1e\xef\xc1\xa0\xed\x7d\x38\xd9\x3e\x3a\xfa\x78\x78\xb2\x77\xf0\xa1\x8c\x76\x0f\x7e\xdd\xfe\x65\xfb\x08\xf5\x36\x3f\x1e\x6f\x6f\xc1\xe8\x1e\x7c\x80\xae\x9e\xec\x6e\x1f\x1c\xfd\x9b\x00\x25\x63\x00\x83\x5f\x41\xbf\xee\x6e\x9f\xec\x6e\x1f\x91\x01\x85\x91\xda\x24\x43\x70\x7c\x72\xb4\xd7\x3b\x51\x8b\x1d\x1c\xa1\x93\x83\xa3\x13\xa5\x8f\xe8\xc3\xf6\xdb\xf7\x7b\x6f\xb7\x3f\xf4\xb6\xc9\xd7\x03\x02\xe5\xd7\xbd\xe3\xed\x32\xda\x3c\xda\x3b\x26\x05\xf6\x68\xb3\xbf\x6e\xfe\x1b\x1d\x7c\x84\x2e\x93\x39\xfa\x78\xbc\x4d\x7f\x2a\x14\x5b\x81\x99\x44\x7b\x3b\x68\x73\xeb\x97\x3d\x82\x36\x2b\x7c\x78\x70\x7c\xbc\xc7\xe8\x04\x86\xac\xb7\xcb\x86\xbb\xfa\xf4\xc9\x4f\xab\xba\xce\x6b\x3f\xc8\xc6\xf7\xab\xf7\x2a\x16\x75\x9a\x06\x3e\x16\x45\xe8\x63\x21\xeb\x6c\xb8\xb0\x0b\xa2\x2c\x45\x59\xd0\xe7\x12\x0b\xa9\xf2\xe9\x8f\x89\x33\xd8\xa6\x94\xa3\x6a\x15\x84\xea\x15\x84\x1a\x15\x84\x9a\x15\x84\x5a\x15\x84\xda\x15\x84\x3a\x15\x84\xd6\x2a\x08\xad\x57\x10\x7a\x59\x41\xf5\x5a\x05\xd5\xeb\x15\x54\x6f\x54\x50\xbd\x59\x41\xf5\x56\x05\xd5\xdb\x8a\x85\xe5\x1a\xad\x4b\xbe\x11\x78\xa4\x3c\x81\x51\x6f\x53\xb8\xa4\x1e\xb4\xf5\x92\xc1\x6f\x30\x18\x75\x68\x43\xc2\x69\xb2\xb6\x5a\x0c\x97\x97\x0c\xc6\xba\x82\xe7\x1a\x83\xd5\x61\xb8\xd4\x29\xcc\xba\x1a\x6b\xb9\xce\xea\x72\x5c\x6a\x14\x06\xe0\xc1\xf1\x6c\x52\x58\x04\x7e\x5d\xed\xb7\x0a\xa7\xc5\xea\xb6\x19\xee\x6b\x0c\x46\x43\xc1\xb3\xce\x60\xad\x33\x5c\x58\xbf\xeb\xcd\xb3\xf2\x2b\x75\x2e\x92\x05\x73\xc1\xf1\x58\x53\xc6\xaa\xc1\x60\x72\x9c\x3b\xfa\x78\x40\xdf\x9a\x46\xdf\x3b\xac\x4e\x53\xc2\x82\xba\x6d\x89\x33\x87\xc1\xc7\x03\xda\xaa\x1b\x7d\x87\x42\x6d\xa5\x83\x6b\x0c\xc1\x8e\x1c\x5c\x01\xa4\xa1\x0c\x34\x45\x56\x02\x5a\x67\x75\x94\xc1\x82\x89\x69\xcb\xc1\x15\x30\x9a\xca\x40\x53\x64\x15\x84\x1a\x6c\x64\x6b\x0a\x30\x3e\x1a\x6b\x62\xf6\x04\x85\x22\x36\x3a\x14\x59\x7d\x36\xd2\x45\x2b\x83\xa2\xc8\xc6\x0a\xd0\x53\x5b\xe2\xb4\xd5\x54\xc6\xb3\x23\xbf\x69\x34\xbd\x56\x81\x4f\x30\x54\x9c\x5e\x5f\x4a\xda\xe3\x34\x55\x6f\x2b\xc3\xba\xc6\xca\x6a\xf3\x51\x97\x44\x20\xe6\xe2\x25\x2b\xc8\x89\x67\x5d\x29\xc3\x11\x5f\x83\xdf\xea\x59\x4a\xac\xe5\x96\xac\xca\xdb\x17\x6b\x5e\x5d\x13\xeb\x1a\x48\x09\x8a\xaf\xcf\xb6\xa4\x7d\xd1\xcf\x86\x44\x41\x8c\x13\x23\x19\x0a\x17\x19\x53\xb2\x68\x81\x30\xc4\xb4\xc1\x6f\x4b\x04\xa0\x9f\x6b\x72\x21\x42\x83\x2d\x86\x48\xc7\x40\xba\xa9\x0f\xbe\xe8\x74\x5d\xc2\x11\x63\x27\x16\x34\x7c\xd7\xe0\x08\x06\x52\x57\x06\xa9\x23\xdb\x15\x0b\x8f\x2d\xe0\x7a\xd3\x31\x1f\xa2\x03\x06\xe2\x1c\x90\x58\x70\x0d\xe5\xdf\xb6\x58\xc5\xfa\x00\xb5\x1d\xe5\x5a\xfa\xcc\x88\x99\x94\x9d\x42\xf5\x3a\x3a\xd3\xb2\x64\x7f\x1a\x93\x15\xe2\x98\x0f\x24\x42\x35\xd7\x2a\xa8\x76\xd5\xde\x5c\x6f\xac\xbd\x7c\xf9\x92\xfc\xee\x6c\x6f\xbd\xdc\x7e\xb3\x59\x27\xbf\xd7\x77\xea\x6f\xde\xf4\xb6\x7a\xe4\xf7\xe6\xcb\x76\x73\x67\xab\xb5\xad\xcf\xf7\x38\xf1\x36\xd0\xae\x6d\x36\xd6\xdf\x6c\x77\xa0\x81\x5e\x6b\x6b\xab\xde\x68\x41\x03\x5b\x6b\xb5\xe6\xf6\x4e\x93\xfc\x5e\xdb\xec\x6c\xad\x75\xb6\xa1\x61\x8e\xd0\x99\x53\x1f\x70\xb4\x77\xb8\xbd\xbf\x55\xef\xd4\x20\xfc\xfe\x02\x1d\x92\x28\x2b\xb5\x48\xca\x2b\xba\x2b\xdf\xf6\xae\x88\x2a\x13\x01\x09\x4f\x10\xec\xce\x5a\xab\xdd\x68\xd6\x60\x04\xb7\x77\x7a\x5b\x9b\x6f\xd6\xa1\x83\x2f\xd7\xdf\x6c\x6e\xf5\x76\xb6\xc9\xef\x7a\xad\xd9\x68\xb7\xd6\x60\x70\x7a\xcd\xad\xc6\x76\x7d\xa7\x76\xe6\x55\x8d\x17\x55\xca\x3b\x15\xbb\x85\xbd\x94\xea\x39\x37\x35\x8b\xcd\xf1\x29\x16\xa0\x7b\x95\x66\x91\x9e\xeb\x9b\xfd\x4f\x4a\x69\x7e\x79\xf0\xc9\x36\x64\x42\x79\x77\x2a\x4a\x3d\xb4\x81\x4a\x76\x01\x44\x0d\x40\x95\xc6\xa4\xe1\x83\xf2\x72\x39\xa3\x52\x0b\x20\xb3\x2b\x35\x00\xda\xd6\xa5\x36\xb8\x1c\xd5\x18\x5a\x64\xeb\xbc\x8b\xc4\xfd\x03\x21\x45\xef\x95\x23\x30\x80\x4f\xe3\x89\xbf\x40\x02\x05\x12\x6f\x01\x10\x3f\x3f\xfd\xe1\x87\x00\x32\xd1\xa7\x3f\xfc\x10\x60\x9b\xfe\x94\xfa\x21\xc0\xa6\xf1\x29\x4d\xdc\x11\xad\x57\x57\xc9\x2a\xfb\xff\xd9\x7b\xfb\x2d\xa9\x6d\x6c\x51\xfc\xef\xf0\x14\x9a\xf9\xad\x81\x6a\xba\xe8\xb6\xe4\x2f\x19\xe8\xfc\x2e\x21\x70\x3a\x37\x10\x58\xc0\xdc\x70\x16\x0b\x32\xb2\x2d\x77\x39\x54\x57\xf5\xa9\x72\xd3\xd5\x49\xc8\xba\xaf\x71\x5f\xef\x3e\xc9\x5d\xda\x92\x6d\xd9\x96\xe4\xaa\xa6\xc9\x99\xcc\xd0\xb3\x86\x54\x95\xa4\xbd\xb7\xf6\x97\xb6\xbe\xb6\xde\x8b\x49\xf3\x07\xb6\x2a\x45\x74\x6c\xd8\xa4\x65\xf3\x29\x4a\xe7\x53\x94\xcd\xa7\x28\x9f\x4f\x11\x9f\x1b\x10\xb1\xd5\x14\xa5\xab\x29\xca\x56\x53\x94\xaf\xa6\x88\xaf\xfa\xc8\x98\x20\x85\x09\x82\x8f\x87\x57\x46\xd2\x15\x24\x1d\x87\x42\xdc\x2f\xcc\x44\x61\x26\x0b\x49\xbf\x30\x17\x85\xb9\x2c\xf4\xfb\x85\x30\x61\xe0\xb2\x30\xe8\x17\x36\xcf\x54\xb3\xee\xbb\xd4\x75\x97\xfa\xbb\x82\xc6\xa3\x84\xf0\xdf\xfd\x23\x84\x8d\xb6\x5d\x09\xf3\x61\x73\xb4\xdf\xda\xd4\xfe\x2f\xf3\x37\xe5\xdb\xb7\x7b\xbf\x99\x2e\x31\xc0\xad\x9d\xfb\x38\xda\xfb\xf5\xc6\x57\x5d\xd7\x28\x70\xa0\x02\x4f\xd2\xf9\x34\x9b\x4f\xf3\xf9\x1e\xda\x47\xb3\xb9\xf9\xee\xcd\x47\xd4\x2c\xc8\x95\xf7\x7d\x22\x97\xda\x0c\xd0\x48\x1f\xda\x80\xf3\x03\x68\x01\xb5\x42\xf3\xfb\xd0\x06\xa2\x1a\x40\x8b\x02\x2b\xb4\xa0\x0f\x6d\x20\x5b\x0d\xda\xaf\x87\x87\x0a\x22\xf5\xac\x10\xc3\x3e\xc4\x81\x42\x20\x73\x9a\x74\x21\xc4\xca\x28\x2e\x51\x82\x56\xcb\x6a\x3e\xa9\xa6\x6b\x21\x56\xd3\xa5\x0d\xd0\x81\x6a\x9f\xcf\xcd\x22\x07\x8b\x18\x98\x94\xf8\x03\xbd\xcd\x4d\x25\xa0\xee\x80\x57\xd8\x24\x36\x5e\x03\x02\x7b\x49\x4d\xad\xc1\xcc\x06\x3b\x89\x0d\xa9\x6c\x85\xf6\x35\x6d\x5d\x5d\x5d\x5b\xc3\x49\xba\x9a\x66\xab\x69\xbe\x02\x8e\xaf\x3e\x4d\x5b\x83\x3e\xb4\x4f\xd5\xd6\x2e\xb4\x4f\xd2\x56\xd2\x87\xf6\xc9\xda\x8a\xfb\x10\xaf\x59\x5b\x57\xb0\x6b\xed\x50\xd7\x95\x45\x5d\xc1\xa3\xae\x4c\xea\x0a\x8e\xd8\x54\x02\x2e\x5a\xaa\xeb\xca\xaa\xae\x30\x00\x98\x5a\xc3\xd0\x30\x3c\xa1\xd1\x77\xe5\xdf\xe9\xcf\x31\x40\x0c\x09\xa7\x7e\x7b\x11\xa6\xf8\xe7\x08\x4d\x8e\xe5\xd1\xdc\x4c\x78\xe6\xdc\xd0\xd3\x63\x75\x84\xf7\x58\x1e\xbf\xcd\x45\x3d\x13\x47\x8e\xd5\x31\xdd\x63\x79\x90\x96\x8b\x7a\xcc\x58\xcf\x57\xf5\xe0\xb0\x2c\x8c\x08\xa9\xb1\x5e\xa0\xea\xc1\xc1\xe4\x54\xd4\xcb\x8c\xf5\xe0\x00\x73\x87\x2d\xfd\xb0\xf6\xb1\x7a\x5a\xe3\x13\x8e\x67\xe5\xac\x62\x4d\x30\x24\xbe\x18\x06\xfe\xf1\x67\x18\xeb\x9a\x8b\x6f\xca\x6a\xfd\x6a\x59\x81\xc7\x93\x30\x17\xdf\xb2\x8a\xc9\x53\x5b\xb7\x11\x35\x40\x87\x36\x4f\x78\x51\x0d\x1e\x6d\x84\xfa\x83\xce\x3c\xc8\xf3\xe1\x2b\xc4\x48\xbd\xb7\x28\x0f\x33\xb5\x20\x45\x34\x19\xbe\x45\xbf\x1d\xc9\x87\x85\xdb\x33\x12\x4d\x8d\xbf\x21\x9f\xf4\xb5\xb5\x85\x34\x99\x4c\xda\xaa\xfb\x48\xf8\x07\x01\x32\xd9\x13\xa0\x02\x61\xb7\x38\xb0\x04\xd0\x75\x53\xc9\x8e\x36\x78\xd6\x7e\xdc\x3e\x78\x1e\x00\x53\x81\x73\x0f\xd8\x58\xe0\x6c\xea\xa8\xfe\x4e\x47\xfb\x1e\x66\xfd\xc6\x0e\x1c\x8e\x31\x3c\xdb\x71\x78\x08\x33\x41\x04\xaf\xbb\xc8\x0b\x59\xc6\x83\x53\x67\x72\xe6\x35\x7c\xcd\xc5\xad\x96\x60\xdd\x7a\x8c\x6e\x50\x9c\x63\x74\x84\xf4\xf0\xfd\xd3\xe6\x6f\xe1\x56\xd3\x37\xf3\x8c\xec\x18\xa6\x62\xc7\x86\xcb\x24\xc8\x35\x07\x3b\x6e\xae\xeb\x1d\x77\xa6\x57\xc7\x3b\xcf\xab\xa4\x86\x1c\x77\xe6\x54\xc7\xd6\xc9\xd4\xf8\x51\xb8\x17\x72\x27\x5c\x0a\x57\xbd\x60\x91\x03\xb3\xbb\x55\xd5\x8e\x79\x4f\x40\x1d\x37\x95\xcd\x97\x0b\xb7\x83\x82\xa3\x04\xa2\x56\xbb\xba\x00\x5f\xed\xc7\x20\x64\xf1\x4f\x03\x25\x91\xed\x86\xba\xa6\xc8\x84\xd2\xce\xb9\x28\xf8\xf8\x51\xee\xfe\x23\xfd\x44\x5c\x81\x27\x9b\x29\xba\x9c\xa2\x5f\x4c\xcf\x7c\x4c\x26\x1b\xb8\xd9\x79\x09\xff\xfe\xd2\xbe\xd6\xfe\x71\x00\x87\xb8\xe1\x4c\x36\x7b\x37\x27\x97\x7b\xf2\x3a\xf9\xef\xe2\xcb\x2f\x7b\x7b\x7b\xf7\x6c\xd0\xfc\x51\x68\x02\xd0\xef\x02\x62\x4b\x9a\x05\x56\x30\x0e\xeb\x26\x40\x00\xda\x2e\xf7\x6e\x4e\x7e\x07\xe2\xec\x10\xc3\x6d\x78\x26\x98\xf6\x5b\x0b\xca\x02\x0b\x42\x89\xcd\x74\x61\x84\xb4\xb9\x7f\x7f\x01\x54\x6d\xbe\xfe\xfa\xeb\x89\x4f\xee\x2c\x74\xa2\xe4\x07\xe7\x69\x98\xfa\x30\x8c\x7c\x07\x6e\xbb\xc3\x30\xd6\xd7\x7e\xd4\xf9\x16\x38\xf3\x54\x7f\xae\x96\xd2\x33\x0d\xc1\x58\xde\xe7\xb1\xd4\xbe\xea\xc3\x3c\xca\x32\xda\x93\x2c\xf5\x02\xde\xe4\x96\x22\xf1\x96\xe1\x14\x8e\xbd\xd5\x45\x4d\xad\xe9\xb8\xcd\x70\x71\xb0\x77\xd4\xa6\xae\xb0\xdd\x51\xa5\x5a\x38\xc7\x4f\x1f\x3c\xfc\x03\x44\xe3\x68\xfe\x9e\x5f\x42\xd3\x35\xcf\x56\xbc\xb2\xbc\x9d\x64\x11\x28\x3c\x39\x78\x8d\x02\x95\x0f\x19\x36\xa2\x39\x3e\x65\x59\x2b\x1e\xfd\x88\x95\x41\x42\x9d\xca\x43\x29\x9d\xb2\xcc\x20\xa9\xaf\x3e\xca\x7d\x60\xcb\xd1\xa8\xba\xa6\xf9\x75\xa2\x8f\x6f\xa7\x71\xfc\xe5\x88\xd3\xbf\xc2\x95\x95\xcf\xbd\x75\xdf\x4b\xac\xa6\x21\xb6\xa6\x4c\x7b\x79\xfc\xe0\x0e\xde\x62\x27\x63\xf8\x56\xf5\x75\xee\x5f\x1c\xc1\xed\xd3\x76\x0b\xa3\x5c\x94\xd5\xc4\x90\x80\xaa\xbb\xa5\xc1\x8b\x2c\x67\x29\x4d\x0c\xb9\x99\xbc\x4d\x42\x53\x96\x67\x05\xef\xec\x71\x98\x2a\x66\x7e\x4e\x38\x2e\xbc\x6e\xd9\xa7\x6f\x81\xd8\x22\x74\x73\xf0\x3d\x5c\x41\x1f\x00\xd8\x66\xed\xd9\xbc\x5c\x2c\x8a\x52\xf3\x62\x31\x04\x8c\xe6\xa5\x62\x98\xae\x9a\x17\x8a\x45\x11\x6f\x96\x89\x07\x94\x5a\xd7\x89\xad\x6b\xc2\x96\xd9\x02\xac\xfb\x20\x79\xc3\xd4\x92\x0b\xe6\x47\x19\xf8\x77\x53\x60\x74\xef\x9e\xd6\x7f\xf5\x82\x92\x19\x50\x7d\xcf\xe1\xc7\x37\x25\xba\x83\xfc\xb7\xe8\x9d\xfa\x48\xdb\x8f\x38\xd0\x3e\x47\xb6\xb7\x23\x15\x49\x93\x05\x5c\x8e\x95\x73\x4b\x98\x3e\xf8\xd8\x9c\xa6\xc6\x3c\x13\x82\xa5\xa5\x09\x13\x40\x42\x00\xc2\xe4\x4c\x26\x86\x0b\xb2\x1c\xed\x03\x22\xdb\x42\x23\xba\x8f\x88\x67\xe5\x1a\x2c\x9b\x4d\x26\x29\xba\x89\x32\x19\xe7\x8a\x8f\x39\x40\xf6\x36\x21\x93\xbb\xb0\x23\x4b\x7c\xe8\x3e\x0a\xc6\x50\xa4\xe8\x1d\xca\xd0\x3b\x94\x4b\xc8\x11\xcf\x13\x9e\x32\x53\xd2\xa1\x1e\xe4\x68\x07\xe2\x25\xed\xe2\x53\xa6\x7a\x71\x07\x79\x9b\xd8\xe3\x41\xe0\x93\xc0\x8e\xeb\xf0\x76\x83\x8e\x7a\x7b\xe8\xf6\xe1\xd6\x7d\x11\xf0\xfd\x30\xc9\x7d\x4e\xfa\xab\x3c\xc8\x22\x52\x61\x2f\xb9\x69\xb9\x0f\x1d\xa1\xcc\xb4\xc4\x87\x00\xe5\xfd\xfb\xc8\xf7\x54\x2f\x41\xfc\xc6\xb7\x45\xd1\x11\x32\xd1\xc1\xb6\xbb\xad\xb5\xd5\x62\xa0\x5a\x44\xab\x17\xdb\x58\xff\x86\x37\xea\x2c\x04\xc2\x82\xe1\x20\xf3\x09\xea\x2c\x02\xc2\x62\x61\x66\xae\xe3\xeb\x0b\x85\xb9\xb9\x4e\xa0\x2f\x12\xf2\x7e\x9d\x2f\x0b\x7c\xff\xac\x0b\x7c\x22\x16\x3e\x28\xe6\xcb\xe5\x4a\x5f\x73\x3b\x84\x81\x5a\xfd\x7d\x12\x12\xc8\x85\xd0\x42\x1e\x59\xa7\x1b\x2c\xd3\x7d\xa6\x15\xba\x1d\xd7\x81\x8c\xcb\x75\x7f\xc6\xd5\xa0\x2f\x4b\x08\x83\xc5\x00\x11\x3e\xef\xb4\x7a\x00\x0d\x5c\x0b\x07\xdd\x80\xbc\xbb\x66\x20\xca\xbe\x2c\x17\x5c\xeb\x72\x01\xc8\x63\x8b\x95\x02\xb3\x58\xda\x45\x02\x25\x1a\xfb\xb5\x29\x51\xc1\xbe\x2c\x40\xff\xd4\x09\x36\xd6\x33\x46\xc2\xe8\x73\xe7\xc6\x50\x58\xfe\x7d\x96\x0f\x06\xcb\x03\xfa\x1c\x9e\x84\x51\x67\x16\xaf\xdd\xc2\xee\xaf\x0a\x10\x12\x6c\xb7\x2e\x20\x2a\x76\x60\xc2\x77\x09\xfc\x0f\x5d\x1b\xc8\xb0\x17\x26\x3c\xa7\x62\xca\xef\x47\x71\x96\x87\x5e\x0c\x9f\xbd\xd8\xcb\x73\x0c\x9f\x8b\xd8\xe3\x61\xe2\x9b\xd7\x0c\x8a\x22\xf3\xbc\xd4\x87\xc5\x85\x88\x86\x14\x87\x58\x7e\x0e\x8a\x84\x16\x0c\x00\xa4\xbc\x60\x41\xc1\x82\x1d\x96\x0b\xb6\x8a\x3c\x35\xb7\xaf\x58\xa7\xb5\x74\xdc\xa2\x05\x8f\xda\x84\x33\x77\x8e\x86\xc1\x8b\x65\x63\xe9\xcb\x10\x3d\x32\xe2\x12\x12\xec\x3a\x48\x8b\x26\x23\xc3\x74\xc7\x3a\x06\x03\x35\x21\xe6\x4b\xec\x5f\x86\xea\x4f\x18\xaa\x85\x54\xb6\x1b\xac\x8d\xc2\xe9\x0c\xd7\x52\x40\xce\x01\x9b\x90\xfe\x55\x67\xed\x5e\xb3\x1a\x8e\xee\xc6\x89\x18\xc0\x93\x2f\xeb\xfa\xff\x3d\x03\xf3\x9f\xef\x5a\xde\x77\xf2\x11\x87\xf2\x97\xe6\x56\x2e\x5a\x2d\xcf\x17\x39\xca\xba\xf7\xf5\xb4\x1e\x1c\xf7\x9f\x4e\xf9\xbe\xbb\x0d\x50\x2f\xd4\xf2\x16\x86\x2c\x31\x45\x30\x48\xdf\x52\x2e\xd7\xcf\x57\xe5\x29\x9f\x2c\x8c\xc3\xd8\xfa\xbf\x56\xd5\x0f\xf5\x3c\x5f\x7c\x99\x2c\xfa\xf3\xcc\x66\x21\x58\x8a\x13\x1d\x21\x72\xaf\xfe\x7c\xff\x48\x42\xa8\x7f\x70\xac\x0d\xff\x65\xb2\x40\x7f\x53\xd5\xf6\xac\xeb\x85\xca\x46\x0b\x36\x5f\xf3\xf1\x53\x81\xfd\xf5\xb1\x7a\x3e\xbe\x3a\xef\xce\x70\x0d\x6c\x39\xe1\xd5\xe3\x15\x83\xcf\x6c\xfe\x4d\x59\xad\x0d\x0c\x6a\xb6\xf0\x17\xe8\x0e\x9a\x2c\x20\xb3\xe7\x1e\xba\xdd\x59\xfc\xe8\xaf\x64\x69\xb8\xea\x55\x6a\x3d\x33\x3b\xfc\x06\x02\xe9\xe5\xef\xb9\x98\x95\x73\x8e\x26\xaa\xec\x3e\x52\x47\x32\xfb\x5c\x6c\xa5\x69\x65\x74\x03\x82\x5a\xb9\x7c\xfc\x46\x56\x82\xb4\xa3\x03\x46\x80\x2e\x9c\x2d\x2f\x26\x8b\x29\xc2\xe8\x10\x91\xbd\x2d\x32\xb6\x23\x78\x09\x65\x17\xb0\xfe\x9e\x31\x79\xb6\x04\xb1\xbf\x3f\xb2\x14\xba\xe8\xd4\xa8\x23\xa4\x49\x0b\xf3\xea\x7b\x6c\x22\xf0\xde\x2e\x9a\x1e\x46\xe8\x9f\x7d\xa7\xed\xf8\x60\x3d\x2f\x33\x3e\xf1\xf6\xbe\xec\x7a\x6d\xbd\xeb\x35\x28\x2a\xa0\x28\x34\x15\x9d\x40\xd1\x60\xc3\x08\x62\x16\x28\x8a\x3f\x79\x1b\x2d\x72\xe4\xba\xff\xa3\xb7\xd1\x4e\xd8\xe9\x29\xf3\x36\xcd\x66\x1a\x1e\x30\x65\x58\x1b\x0e\x1a\x4f\xea\x96\xf7\xef\x23\x22\x37\xbd\xea\x5f\xbe\xfe\xfa\x6b\x14\xef\xed\x21\xf4\xce\x0c\xa9\xfb\xd7\x81\x84\x83\x01\x24\x4c\xf7\xf6\xb6\x83\xd4\x6d\xe7\x1b\xdd\x4b\xa7\x27\xb8\xed\xb7\xf1\x90\x7c\xb7\xb2\xd6\x6d\x2c\x89\xd5\xba\x8d\x37\x75\xbe\xe9\x2d\x89\xed\x42\xf2\x87\x90\x92\x1d\xbb\x5d\xb7\x33\xbf\x49\x80\x5a\xc5\x51\x42\xdc\x57\x3d\x87\x24\xbf\xaa\x87\xfb\xce\x0d\x53\xdb\xee\x67\x06\xb7\x1a\x27\x1c\xdd\x44\x05\x1c\x76\xfb\x5d\x7c\x3c\xb1\x3d\xe1\x72\xca\x20\xc3\x1c\x43\x37\x51\x0a\xd5\x99\xdc\x1d\x7c\x87\xd4\x3e\xa1\x89\x7e\x08\x56\xca\x13\x41\x78\xb3\xd5\xaa\x36\xdb\xd4\x5e\xab\x3c\xfa\x27\x4b\x70\xa2\x95\x60\xbf\x53\xd4\x69\x64\x1e\xdb\x1a\x64\xf0\x4e\xcd\x84\x83\x8e\xcb\xcc\xc9\x1c\xda\x45\x0a\xa2\x2c\xc1\x5a\x09\xc6\x7a\x51\x2c\x4f\xb6\xca\x22\x12\x9a\x47\x3c\xd8\x40\x16\x98\x66\x68\xbf\x46\xbb\x2f\x98\xba\x2f\x1f\x7a\xb3\x6e\x1e\x43\x43\x82\x8e\x6a\xc6\xec\x0b\xd6\x9a\x30\x08\xc7\x75\x62\x00\x20\x7c\x5d\x3f\x4f\xbb\xf8\x13\xee\xd1\x14\x7e\x41\xee\x4c\x78\x2d\x01\x9b\xb6\xf9\xd0\xc8\x16\x69\x3f\xdb\x3a\x1a\xd9\x0e\x9d\x54\x82\x11\x15\x31\xe1\xfa\x77\xd9\x1a\x95\x75\x42\x55\x07\x52\x86\x17\xe6\x3a\x91\xaa\x03\x29\xc1\x4f\xcc\x75\x62\x55\x07\x6c\x7e\xf6\x65\x1b\xf6\xcb\x36\xec\x97\x6d\xd8\x61\xb4\xf9\x65\x1b\xf6\x9f\x72\x8d\x37\x8c\x76\x5e\xe3\x0d\xa3\xd1\x35\x5e\x7d\xce\x36\x5c\xe3\x0d\xa3\x2f\x6b\xbc\xd7\xbe\xc6\x1b\x46\xdb\xae\xf1\x9a\x84\xd3\x5d\xe3\x05\x01\xb9\x0f\x6d\x37\x7b\x67\xe6\xad\x59\xea\xfd\xa9\xb7\x66\x37\x51\xf0\x87\x3c\x5c\xd0\xe0\xf9\xb2\x0a\xdc\x5d\x05\xde\x44\xb0\xa7\x7a\xb0\x89\x02\xed\xf7\xd7\x51\xa0\xb2\x74\x43\x8d\x03\x2d\x4f\xf4\x4e\x39\xdd\xb4\xfe\xbd\x38\x7e\xf6\xd3\xb3\xc7\x8f\x5f\x3e\x7a\xf5\xb2\xbf\x5a\xfc\xfc\xbb\x9f\xbe\xfb\xe1\xdb\x47\xaf\x1f\x0d\x5f\xe5\x7e\xf1\xec\xef\x3f\x7c\xfb\xd3\xc3\x67\x3f\xbc\x7c\xf5\xe0\x87\xa6\xa5\x86\x4e\x2e\x2b\x3f\xdc\x6e\x59\x59\x6b\xb1\x9a\x2d\xeb\xa4\x2d\xbd\x35\xe9\x1a\xb5\x98\x5d\xe3\x29\xba\xb4\xa5\x2a\xaf\xe4\x92\x48\x85\xee\x23\x12\xdc\x43\x95\x61\x49\x44\xeb\xf3\x9b\x0d\xda\x47\x21\xba\x8d\x2e\xe5\xed\xc1\xaa\xbe\xa4\x09\x9f\xc8\x1e\xac\x54\xa2\xbf\xa1\x68\x10\x8b\x40\x18\xc8\x2f\x5e\xa3\x23\x74\x89\xfe\x86\x42\x53\x94\xc8\x2f\xfe\x53\x40\x25\xe8\x36\x12\x78\x7c\x81\x67\xcf\x50\x79\x23\x97\xe5\x5e\xf7\x7e\xbe\x94\x3f\xff\xa7\x65\x29\x58\x63\xdb\x59\x89\x4a\x78\x4e\xc0\xc0\xb4\x86\x33\x1b\xc9\x99\x8d\xbc\xa0\xb9\x31\x30\xa6\xa9\x2a\xb9\x8b\x2e\x65\xd5\x4b\xcb\xb2\x52\xab\x20\x5d\x36\x5e\xc2\x03\x3f\xc3\x5e\x0b\xbe\xf6\xbb\xfe\x71\xb4\x6f\xbd\x5d\x8e\xae\x36\x3c\x79\xfc\xf2\x85\xa0\x75\xe3\x61\x93\x32\xe8\xef\x4e\x58\xd6\xc7\x44\x35\x40\x51\x2b\xeb\xd3\xf5\x45\x4f\xb7\x8c\xd5\x9e\xd4\xd5\x2c\x2c\x54\x2f\x4f\xfc\x8c\xee\xa3\xf8\x1e\xfa\xd9\xb1\x32\x07\x7d\x80\xab\xa9\xe6\xac\x28\x35\xfa\xb4\xac\x9e\x2f\xd7\x90\xc7\x55\x68\x15\x3c\x96\xfb\xf3\x1e\xba\x83\x4c\xa7\xa9\x6b\xe0\x7a\xa3\xfb\x48\xe5\x8b\x30\x55\x16\x7f\x83\x0e\xbe\x3b\x42\x80\x46\x83\x62\xc1\xd5\x3d\x51\xad\x63\xfd\xfa\x08\xd0\xda\x0f\x57\x0f\x30\x3f\xd5\x30\x77\x40\xdd\x31\xcc\x7b\x1a\x02\xb6\x5b\x5a\xd2\x14\x6b\xc1\x37\x15\x28\xd0\x88\x58\xa8\xfd\x24\xfa\xe1\x21\x7a\xbe\x2a\x4f\xcb\xaa\xfc\xc0\xd1\xd9\x72\x7e\xb9\x58\x9e\x96\x6c\x8e\x96\x1f\xf8\x0a\xfd\xc7\xe3\x09\xd9\xbb\x8b\x36\xef\x28\xda\x47\x9b\x77\x11\xfc\x1b\xc2\xbf\x81\x70\x33\x66\x90\x4a\xa3\x25\x7a\x79\x7f\xe0\x1d\xf2\x36\xb1\xe3\xc8\xbc\x85\x38\x05\xe1\xc8\xa8\x1f\x23\x9b\x5e\x3d\x07\x2f\xd7\xf8\xd4\xf0\x53\x27\x18\xeb\xcb\x6c\x3a\xd0\x9f\xbd\x5d\x77\x53\xd6\x60\x3f\x15\x3f\x3d\x5b\xae\xd8\xea\xb2\xf3\x12\x9d\x30\x81\x57\xfa\x40\x64\xdd\xa5\x34\xbe\x3a\x63\xb6\xfe\x57\xc6\x9e\x8d\xd1\xdd\xdb\xdb\xf1\xb7\xdb\xd9\xf1\x3b\xfb\x3a\xbe\x6b\x57\xe7\xfa\x9f\x12\x58\x9e\x57\x67\xe7\xd5\x13\x98\x5a\x77\xea\x22\x08\xd2\x73\xbe\x2e\x57\x3c\xd7\x1e\x1a\x48\xcb\x6a\x5d\x27\x84\x96\x8d\x3b\xb3\x85\xba\xf1\xb3\xc5\xbc\x16\x93\x96\x83\x9b\xad\xf8\x5d\x44\x48\x30\x45\x24\x8c\xa6\xc8\xa7\xc1\x14\x85\x98\xf4\x1b\xab\x37\x0b\xee\x8a\x32\xbd\xa8\xff\x68\x41\x3d\x69\xb6\xbe\x5b\xa0\xf7\xae\x07\xed\x0a\xef\x17\xc0\x4a\x2d\xbc\x84\x58\xcf\xbd\xeb\x6f\x6f\xde\x5a\xbc\xfd\x16\xaa\x26\xfe\x00\x8e\x54\xb9\x05\xbf\x68\xd4\x0e\x36\xe1\xc6\x52\x09\x00\x25\xcd\x6b\xbd\x30\x02\x44\x9e\x87\xee\x20\x31\xd0\x36\x2f\x25\xe8\x9c\x10\xd1\x8b\x4f\x3e\xd7\x8e\x9e\x61\x61\xce\xc0\x34\xe3\xe2\x59\xdd\x89\x27\x6c\x01\x6b\x3f\xbd\xae\x1d\x22\x62\x5a\x43\x4b\xd7\xcb\x55\x3a\xce\xff\x1e\xf8\x4f\xc9\x24\xf8\x94\x94\xa8\xbb\x29\x26\x78\x6d\x5d\x36\x7f\x4a\xe0\x0d\xfa\x7e\x75\xe1\xeb\x5d\xc9\x2c\xac\x4f\x50\x0b\xf4\xce\x7c\x82\xa4\x93\x48\x90\x5c\x25\x83\x20\xe9\xa4\x0e\x24\x57\xcf\x19\xa8\x08\xc6\x63\x14\xe3\x2e\xc9\xf8\x4a\x34\xe3\x2e\xd1\x78\x17\xaa\x8d\x72\x90\xca\xd5\x2c\x8d\x94\x8b\x6a\x29\xb5\xd9\x2c\xe9\x39\x83\xc5\xbc\xda\x9c\x0d\xac\x10\x35\x0e\xe0\xbd\xd9\x77\x47\xc0\x17\x5b\x9d\xf9\xf2\x02\xa9\x3a\xe3\xbb\x11\x2f\xc4\x00\xbb\xb6\xd8\x80\x0c\x94\xc1\x0e\xe4\x47\x19\xf4\xc2\x67\xbb\x09\xbc\x9a\xf1\x8a\x0d\x4b\x76\x98\x35\x68\xc0\x9e\x96\x62\x0a\x32\x3f\x3f\x5d\x40\xe7\x0c\x66\x55\x73\xb0\x0e\xb3\xa7\xa8\x8d\xa4\x8d\x95\x77\x9c\x93\xe8\x38\x3a\x52\x6a\x67\x28\x16\x44\xe2\xaf\x0e\x3d\x1b\xe9\xb9\xea\x3e\xd1\xea\xce\x97\x17\xd6\xb8\xd4\xca\xad\x57\xc6\x38\xc7\xd4\x93\x57\x42\x0a\xaf\xde\x6c\x6c\xb4\xbf\xda\x48\x5d\x3b\x82\x1e\xd8\x2b\x81\xb2\x1d\x01\xe9\xdb\x9d\xbe\xb9\x9a\x1a\x38\xdc\x6a\xdb\xa3\x00\xba\x34\x11\x72\x09\x60\x7a\xe8\xda\x2c\x7f\xb5\xc1\x6d\x75\xbc\x4d\x75\xa9\x5f\xaf\x36\xd8\x25\x47\x55\xf7\x49\x53\x17\xe4\xe8\x54\xef\xf5\xf9\x0a\x2c\x4a\x3e\x27\x22\x54\x7d\x5c\xcb\x5f\x6d\x02\xe5\x0b\xd0\x64\xa2\x68\x6b\xae\x06\x2b\xfc\xea\x7e\xb0\x6d\x7a\x03\xd0\x9e\x34\xd0\xa4\xd7\x90\xd0\x9e\xf4\xa0\x3d\x1d\x87\xf6\x87\x1a\x55\xc7\x15\x3a\xf4\x13\xf5\x5d\xa2\x45\x4d\xd1\x4e\xb3\xbd\x17\xb3\x25\x7a\x5e\x3a\x34\x5b\xa0\xac\xdf\x7c\xc4\xf7\xb4\xaf\x32\x94\x6b\xbe\x7f\xb2\xca\x77\x38\xd7\x80\x75\xa9\xb1\xa8\x24\x35\x68\xcc\x21\xd5\xb5\x9f\xb4\xb5\xed\x2e\x09\x06\x8b\xd9\xf2\x99\x8c\x52\x8e\x3a\xeb\x61\x3a\x5d\xd6\xce\xbe\x58\x42\xa0\xe7\x70\xf1\x62\x02\xdd\xa2\x18\x5d\x78\xd0\x6c\x65\x52\x77\xfa\xfe\xfd\x96\x48\x50\xed\xba\x7f\xf0\x94\xa6\x4f\xd0\x1d\xad\xdc\xa6\xe8\xa8\x6b\x3a\x0d\x0c\x23\xf0\xa7\x3b\x02\xef\xae\x79\xb4\xdd\xdd\x6a\xc5\xa3\xdf\x65\x45\x95\x06\x06\x56\x3b\x86\xc4\x45\xc1\x95\x7b\xfe\x74\x04\xc7\x93\x1d\x71\xb8\xc6\xb6\x15\x5b\xac\xcf\x96\x6b\xa7\x96\x80\xfb\x7d\x5e\x3e\x91\x86\xf1\xea\x8d\xb6\xa0\xd8\xea\xa1\x75\xcc\x93\x0d\xb7\x19\xf8\x54\xcd\xb1\xd1\xcf\xea\x3f\xce\x4a\xc4\x2a\x18\x02\xc1\x5f\x9a\x63\xc2\x57\x1e\xf4\xc1\x98\xb4\xb5\x99\x1c\x79\x8d\x03\x30\xd6\x7b\xe5\xd5\xdd\x91\xb5\x6d\x26\xff\xca\xab\x3b\xa3\xea\x59\xc6\xad\xc3\x43\xf4\x70\xe6\x72\x7e\xdb\x0f\xeb\x57\x1c\x32\xc6\x5d\x23\xd2\xdc\x57\xed\x87\x9b\x71\x65\x44\xb9\x77\x73\xa9\x75\xab\x57\x8d\xc2\x6d\xdf\x64\x83\x9b\x46\x13\x2d\x08\xd9\xdb\x66\x00\x94\x00\x48\x0f\x00\x19\x00\x70\x72\x51\xc4\x1e\xab\xe5\x85\x83\x89\x73\xcd\x1a\x5e\xb5\xa6\xf1\x0e\x4d\x7e\x57\xe4\xcb\x1f\x6e\xd6\xc4\xc0\x57\x97\xff\x98\x6b\x56\xf3\xaa\x35\x21\x1d\x22\xfc\xd0\x42\x9c\x2f\x2f\x3e\x7d\x81\xf6\xbb\xa5\x69\x46\x32\x90\xb7\xd5\xd2\x3a\xcb\x90\x62\x7c\xeb\x2d\x66\x42\xf9\xe8\xa4\xad\x03\xc5\x66\x88\x9d\x78\xa5\xdb\x42\x98\xa4\x63\xb3\xe3\x9f\xeb\x58\x94\x61\x91\xe6\xda\x4f\x45\x0d\xea\x37\x2b\x3e\xa2\xdd\x70\x19\xe8\x36\x2c\x5e\x0d\xd7\x81\xae\x7a\x96\x0a\x5f\xe5\x28\x15\x1c\x92\xca\x78\x39\xef\x9e\x77\xc2\x7b\xe8\xb0\x4b\xff\x1e\xba\xdd\xff\x01\x90\xc3\x06\x4d\x73\x9a\xeb\x9f\xe4\x10\xd4\x27\xaf\xe1\xe9\xcb\x8c\x35\xf1\xc6\x35\x48\x74\x68\x14\xbd\x5e\xa5\x5e\x05\x1c\xc2\x3c\x34\x1e\xa6\x7b\xf9\x5f\xe7\x9c\xff\xc2\x87\x40\x67\x6c\x3d\xab\x95\x7b\xab\xb7\xe8\x07\x54\x7c\xca\x62\xe1\xf8\x9a\xd0\xf6\x21\xbd\x2d\x9c\xdf\x7d\x0d\xb1\xc5\x67\x5f\x95\xd3\x42\x43\xb5\x30\xa7\x07\x9c\x3b\xad\xcd\x69\xa0\xd4\xf2\x9c\x0e\xea\xaa\xeb\x8a\x2d\x2b\xdc\x9d\x78\x32\xe8\xc4\x93\xab\x76\xe2\xc9\xa0\x13\x4f\x76\xeb\x84\x59\x54\x52\x75\x95\x91\x55\x4b\xb4\xe2\xd5\xaa\xe4\x1f\xb8\xe1\x00\x22\x52\x97\xbb\xa5\x3f\x38\x3b\x5f\xcf\x6a\x32\x4c\x2c\x32\xd4\x7c\x3a\xac\xf9\xe9\xe9\x89\x0d\xb7\x87\x1a\xd4\xd3\xa1\x09\x5b\xef\x13\x5d\xd3\xa9\x49\xbb\xff\x52\x47\x28\x0d\xee\xac\xb9\xec\xb4\x85\x87\xd8\x72\x33\xa7\xfe\xd8\x9e\xcf\x74\xb2\xfd\xcb\x71\xcd\x2b\x1e\xd7\xf4\x77\x3d\xac\xe9\x8f\x1d\xd5\xf4\x1d\x07\x35\xfd\x2f\xc7\x34\xaf\xfb\x98\xa6\xbf\xe5\x21\x4d\x83\x58\x3a\x47\x34\xfd\x6d\x0e\x68\xfa\xf6\x6b\xf8\xcd\xc1\xc3\xbb\x34\xf8\xf8\x76\x4a\xf1\xbf\xc8\x71\xcd\x7e\x82\x9d\x10\x93\x3f\xec\x0c\x67\x9d\x6e\x47\xe0\xfc\x73\xa5\xdb\xb9\xd2\x69\x4b\x55\xdc\x9e\xf6\xac\xeb\xec\x94\x90\x27\xc4\xa4\x73\x2c\x24\xc4\xc4\x7a\xcc\x84\x6e\x99\x90\x47\x54\xec\x1c\x35\xa1\x2a\xab\x45\x88\xc9\xb5\x5d\x21\xd6\xbb\x6f\xcd\xc9\x33\x38\xe4\xe0\x6d\xb2\x34\x4d\x93\x3c\xcc\xa7\x5a\xc2\x9e\xbd\xa9\xa9\x66\x44\x12\x46\x12\xc2\xf4\x74\x3e\x7b\x86\xbc\x3d\x86\xa6\x09\x0e\x13\x0f\x87\x4c\xcf\xfe\x63\x46\x82\x43\x52\xf0\x4c\xe6\x0c\xaa\x73\x03\x6d\x89\x24\x8a\x7d\x9f\x44\x91\x4c\x2b\xa4\x32\x07\x99\x91\x50\x9e\x06\x01\xa3\xb1\x9e\x57\x68\x4b\x24\x79\xea\x65\x84\x7b\xb9\x9e\x86\xc8\x8c\x24\x88\xd3\x30\xa0\x38\xd7\x93\x14\xf5\x42\xd3\xeb\xce\x52\x24\xf4\xe9\x8a\x59\x8a\x70\xf4\x25\x4d\xd1\x35\xc5\x44\x74\xe7\x34\x45\xa2\xc9\x58\x5c\xa4\xfb\x8c\x61\x64\x44\xbf\xa4\x29\xba\xfe\xd8\x88\x6e\x9b\xa6\xc8\x28\x9c\x6e\x7c\x44\x47\xd3\x14\xf9\xd4\x9d\xa6\x48\x0c\xe3\x77\x29\x31\x45\x4b\xe4\x5f\x24\x5a\xfa\x97\xbe\xdc\x72\xbd\x17\x5b\x3e\xd3\x95\x95\xab\x07\x51\xb2\xa8\xe9\xae\x02\xf4\x53\x7d\x82\xd7\xf0\xd6\x4d\xf7\x90\xef\x01\x3b\x3b\x9b\x5f\x4e\xd4\x8f\x53\xc4\x56\x27\xe7\xa7\x7c\x51\xad\xfb\x6f\xf2\xe8\xd7\x67\x5a\x7a\x20\x95\x52\x8b\xa2\x87\xde\xdb\x04\x84\x32\x52\x24\x10\x57\xe4\x31\xa1\x8c\x13\xb2\x37\x1d\xd6\x8b\xb1\x1f\x07\x41\x02\x69\x06\x89\xcf\x8b\x28\xcc\x72\x3d\x34\x18\x34\x48\xc3\xcc\x2b\xd2\xac\x80\x07\x10\xb2\x20\xf7\x53\x52\x98\x00\xf3\x24\x0d\xf3\x94\x85\xf0\x7a\x36\xa6\x49\x9e\xa6\x99\x13\xb0\x9f\x84\x51\x46\xc2\x14\xc2\x19\x3f\xa0\x69\xe8\x53\x13\xe0\x30\x29\x30\xc6\x05\x50\x9c\x46\x5e\x98\x7b\x38\x71\x02\x4e\x88\x5f\x50\xc2\xe0\xc9\x6d\x56\xe0\x24\x28\x92\xd4\x04\x98\xa5\x38\x0b\x79\x0e\x14\xe7\x2c\xca\x29\xc6\xd4\x09\x38\xa7\x5e\xcc\x98\xe4\x31\xf3\x3d\xdf\x23\x81\x91\xc7\x98\x50\x3f\x4c\xe5\x9b\x11\x41\x18\x7b\x51\x91\x72\x27\x60\x12\xf8\x98\x86\x29\xbc\x1d\x11\x70\x1e\xa4\x84\x66\x46\x56\x84\x5e\x16\xe7\x19\x3c\x20\x9e\x87\x45\x91\x06\x9c\x38\x01\xc7\x24\xe5\x61\x1e\x03\x2b\x0a\x12\xa7\x34\x89\x8c\xc2\xa3\x5e\xce\x53\x2c\x1f\xaf\xf0\x53\x1c\x25\x51\x8a\xdd\x3c\x4e\xf3\xcc\x8b\x64\x86\x4a\x12\x66\x31\x26\x7e\x68\x02\x9c\xe1\x24\x2d\xb0\x24\x20\x2b\xa2\x84\x44\x49\xe0\x04\xcc\x83\x24\x8d\x92\x0c\x78\x97\xf0\x02\x07\x2c\x37\xf2\x98\x17\x29\x0f\x62\x0a\xcf\x88\xfb\x34\x28\x48\xc8\x7d\x27\x60\xaf\xc8\x70\x92\x67\xd0\x80\xa6\x34\xcb\xc3\xd4\x48\x31\x09\xbc\x8c\xe1\x2c\x83\x47\xda\x63\x96\x25\x59\x14\xba\x85\x97\xf3\x84\x64\x11\x18\x48\x98\x90\xd4\x23\xb1\x11\x70\xc0\xe2\x80\x06\x0c\xe6\x08\x11\x67\x11\x0f\xa8\x9b\xe2\x30\x4b\x3d\x96\xe4\x40\x49\x9a\x07\xb8\x48\xf3\xc0\x68\xd2\x51\x91\x50\x9a\x03\x60\xea\x63\x1c\xfa\xa9\x9b\xe2\x84\xfa\x3c\xc4\x21\x01\x93\xe6\x51\x94\x17\xcc\x6c\x20\xd4\xc7\x59\x14\x41\x84\x4f\xf2\x34\xf0\x09\xf6\xdc\xbe\xc2\xf3\x7c\x12\x67\x54\xbe\xf9\x5e\xa4\x04\xfb\x46\x75\x4b\x8b\x30\x89\x8b\x4c\xe5\x37\xe5\x85\xc7\xb9\x5b\x2b\xb2\x88\x7b\x5e\x5a\x80\xe2\xfb\x39\xa3\xb4\xc8\x8c\x5a\x91\x87\x2c\x4e\x70\x00\x80\x13\xdf\x63\x2c\x26\x6e\x56\x78\x51\xc6\x22\x3f\x94\xcf\xbb\x78\x9e\x4f\x89\xd9\x40\x70\x40\x12\x92\xc8\xb9\x97\xc7\x3c\x1e\xf1\xd8\xcd\x0a\x12\xa7\xb1\xc7\x28\x38\x97\x20\xca\x09\x29\x0a\xa3\x49\x13\x8e\x05\x9b\x80\x65\x61\x46\xa2\x2c\x21\x91\x13\x70\x90\x93\x2c\xca\x0b\xd0\x8a\x90\x65\x01\x61\x3c\x37\xfa\x0a\xdf\xa7\x5e\x8e\x81\x65\x49\x9e\x84\xa9\x9f\x17\x4e\xc0\x51\xe8\xb1\xd8\x0f\x03\x69\x20\xac\x88\xfc\x9c\x9b\xd5\x2d\x62\x1e\x4b\xc1\x6f\xfb\x59\x1c\xa7\x84\xb9\xdd\x26\xc5\x19\xc9\x12\x22\xbd\x5b\xcc\x73\xc6\x79\x64\x02\x9c\x90\x98\x90\x4c\xb2\x0c\x07\x94\xf8\xa1\x9f\x3a\x01\x33\x92\x16\x9c\x32\xe9\x67\xb3\x02\x7b\x7e\x64\x34\x10\x46\x31\x8b\xa2\x00\x28\x4e\xb3\x80\xf8\x9e\xe7\xf6\x6e\x19\x09\x52\x9a\xc6\x1e\xf8\x59\xaf\xa0\x49\x9c\x60\xa3\x77\x8b\xa3\x2c\xc4\x0c\x78\xec\x45\x61\x90\x72\xdf\xad\x15\x39\x4e\x08\xa7\x38\x01\xc0\x11\x2f\x42\x82\x8d\x63\x5e\x1e\x25\x89\x17\x11\x90\x45\x18\x46\x21\x4b\x46\x2c\xaf\x08\x3c\xee\x87\x92\x77\x61\x1c\x63\xe2\x11\x66\xd4\x63\x2f\x62\xcc\x93\x3d\xf3\x49\x9a\xe6\x38\x75\x0b\x0f\x27\x2c\xc8\x30\x06\xb7\x99\xd2\x9c\xe4\x5e\x66\xa4\x18\x73\x3f\x8e\x32\x4f\xea\x31\x0e\x30\x4b\x43\xb7\x77\x23\x71\x40\xe3\x38\x00\x3d\xce\x0b\xca\x79\x9a\x24\x26\xc0\x7e\x90\x7a\x69\x96\x42\xcf\x38\x4e\xd2\x80\x8e\xa8\x9b\x9f\xe0\xcc\xcb\x52\x10\x4a\x16\x66\x49\xc8\x22\xdf\xe8\x8f\x79\x4e\x19\x0b\xc0\x6d\x72\x3f\xc0\x94\x65\x6e\x75\x0b\xd3\x24\xcb\x58\x50\xc8\x91\x21\xf2\xb9\x1f\x1b\x01\x47\x94\xf0\xa8\x90\xce\x2a\x8f\x52\x92\x52\xe6\x66\x45\x1c\xd0\x82\x12\x0e\x06\x12\xe6\xbc\x48\x89\xd9\x57\xc4\x94\x85\x91\x2f\x47\x9a\xc0\xc7\x31\x29\x22\xb7\x56\xd0\x20\xa3\x31\xc5\x32\x12\xc2\x85\xc7\xd2\xd8\xe8\x36\x69\x96\xc5\x1e\x91\xc2\xc3\x2c\x0a\xfc\x84\xbb\x63\xb7\xc4\x4b\x79\x51\x14\x4c\x46\x91\x91\x8f\x39\x31\x6a\x05\x0b\x42\x2f\xca\x38\x58\x5e\xce\x29\x49\x73\xee\x8e\xdd\x52\x5e\x24\xcc\x2f\xe4\xc8\x40\xb2\x28\x4e\xb0\x39\xae\x88\x62\x1c\xd3\x42\x0e\x61\x7e\x4c\x42\x9f\xb8\x85\x97\x31\x12\xfb\x3c\x03\x1e\x73\x46\xa2\x08\x27\x46\x1e\xe7\x98\x46\x29\x95\x43\x13\x11\x8a\x44\xba\x8b\x80\xc3\x40\x84\xe5\x2c\xce\x73\x30\x90\x2c\xe7\x1e\x4f\xb1\xd1\x6d\x16\x61\x9c\x07\x45\x5c\xa8\x41\x97\xe7\x38\x76\xeb\xb1\x17\x15\x5e\x14\xcb\x78\x21\x26\x38\x8e\x8a\xd4\x68\xd2\x1e\x8b\xfc\x38\xcf\xc0\x40\x18\xc9\x68\x42\x99\x7b\x04\xc1\xd8\x2f\x12\xea\x05\x6a\xe1\x2e\xf1\x72\x66\xa4\x18\xa7\x31\xf6\x52\x5f\xfa\x63\x1f\x67\x41\x8c\xdd\x3c\x26\x34\x4f\xe3\xb8\x08\xa5\x56\x78\x41\x9c\x53\xa3\x3f\xf6\x49\xc6\x58\x1a\x83\x56\x04\x5e\x16\x93\x20\x71\x1b\x88\x9f\x25\x3c\xe5\x1e\xb0\x02\x87\x59\x92\xf2\xd4\x28\xbc\xc0\xc7\x79\x14\x67\xd0\xb3\x24\xc3\x9e\x97\x07\x6e\x3d\x0e\xb2\x2c\xcc\x03\x19\x78\x67\xa9\xcf\x03\x92\x1a\x87\x26\x11\xae\x90\x24\x01\x67\x55\x64\x51\x18\x73\xe1\x5e\x5d\xbe\xa2\xc8\xd2\xa8\x60\x72\x90\x64\x79\x54\x30\x6e\xa4\x38\xca\x82\x00\x27\x14\x00\x07\x2c\x88\x43\x8a\x63\xb5\x88\xfa\xd6\x71\x6d\xb5\x9d\x17\xfe\x78\xd5\x1b\xaa\xb6\x67\xd0\x7e\xec\xdc\x50\xfd\xe9\x6a\x37\x54\x43\x4c\xb6\xdb\x3a\x30\x6c\x47\x5c\x7f\xf6\xd1\xab\x6e\x1d\x44\xcc\x4b\x78\xbd\xe0\xee\xa7\x59\x96\x78\x96\xad\x83\x34\x8d\x62\xc6\xe5\xf0\x4b\x83\x8c\xb1\xb8\x1b\xba\x38\x90\xf8\x59\xc4\x0b\x3f\x06\x4f\x56\xf0\x24\x28\xa8\xf0\x64\xa6\x9a\x2c\x0c\x8a\x22\xf4\xc1\x0a\xc2\x02\xe7\x7e\x54\x6c\xbb\xaa\x1f\x62\x8f\x87\x44\x3a\x1f\x96\xf3\x88\x92\xdc\xb2\x75\x90\xa4\x5e\x18\x51\xa9\x90\x24\xf5\x79\x94\xe1\x62\x4b\x24\xb8\xa0\x7e\x9e\x48\x9d\x2f\xd2\x00\xa7\x79\x64\xe9\x49\x98\x72\x2f\xcb\x65\x18\x84\xfd\x98\x13\x1c\x27\xbb\x6c\x1d\x5c\xf7\x3d\xd2\x6d\x52\xc3\x42\x3d\xcf\x9e\xf9\xf5\x18\xdb\x53\xbf\x1e\x13\x7b\xee\xd7\x63\xdf\x9e\xfc\xf5\x38\xb0\x67\x7f\x3d\x0e\xed\xe9\x5f\x8f\x23\x7b\xfe\xd7\xe3\xd8\x92\x00\x56\x76\x10\xd2\xc3\x1a\xcf\x81\xcb\xf2\xb9\x2c\x1f\x5e\xf6\x90\x3c\x80\xe6\xc6\x2b\x50\xb2\x7c\x2e\xcb\x2d\xcd\x09\x34\x27\xd6\xe6\x64\x2e\xcb\x2d\xcd\x7d\x68\xee\x5b\x9b\xfb\x73\x59\x6e\x69\x1e\x40\xf3\xc0\xda\x3c\x98\xcb\x72\x4b\xf3\x10\x9a\x87\xd6\xe6\xe1\x5c\x96\x5b\x9a\x47\xd0\x3c\xb2\x36\x8f\xe6\xb2\xdc\xd2\x3c\x86\xe6\xb1\xb5\x79\x3c\x97\xe5\x86\x63\x7d\x5b\x26\x3d\x96\x9a\x61\x02\xce\xa4\x52\xf4\x33\xee\xc1\x91\x5b\xa9\x10\xa6\x56\xa9\xd4\x05\x53\xab\x4c\xea\x81\xa9\x55\x26\x55\xc0\xd4\x2a\x97\xe2\x37\xb5\xca\xa5\xe4\x4d\xad\xb8\x94\xba\xa9\x15\x97\x02\x37\xb5\x2a\xa4\xb0\x4d\xad\x0a\x29\x67\x53\xab\x13\x29\x63\x53\xab\x13\x29\x5e\x53\xab\x99\x14\xad\xa9\xd5\x4c\x4a\x75\x6e\xca\x3b\xe8\xba\xba\xbb\xe5\x73\xa8\xd6\x7c\xda\x35\xfe\x1f\x4b\x99\x7b\xd8\x76\xdd\xfc\x11\x8c\xe0\xf5\xf6\xd9\xb0\xca\x16\x89\xa2\x25\x1a\xc1\x82\x1f\xcb\xfa\xb6\x81\x9e\x35\x1a\xdd\x46\xe4\x2d\xd4\x34\xe7\x72\x6d\x61\xcc\x25\x0c\x75\xbf\xa0\x0f\x03\x6e\xcd\x5f\x29\x03\xf5\xe1\x21\xfa\x0f\xc8\x46\x6c\x47\x5e\xa7\x74\xde\x29\x43\xf5\x66\xd6\xe4\x39\xde\x8c\xdd\xc5\x53\xd5\xe6\x5a\x0b\xf7\x7d\x3c\x59\x6b\xd6\xc9\x82\x3d\x93\xc9\x7f\xf5\xe4\xd5\x73\x48\x51\x5c\xa7\x03\xee\xd4\xa3\x83\x7a\x70\xe8\xf5\x1d\xea\x56\x8b\x5d\x37\x4c\x65\xcd\x79\x87\x8a\xf9\x90\x8a\x99\x89\x8a\xf9\x90\x8a\x99\x4e\x45\xb7\x5e\x3c\xac\x67\xc9\x64\xac\x8b\xd4\x92\x33\xe7\x83\x96\x7b\x7b\x97\xe4\xdb\xad\x44\xf1\x76\x12\xc5\xad\x44\xf1\x56\x12\xc5\xb3\x4e\x82\xef\x59\x9d\x85\x5b\x4b\xcc\x3d\x57\xb9\xba\x35\x26\x61\xc5\xe1\x6e\x35\x38\xc7\x9c\x68\x22\xad\xe1\x45\xa3\x22\xc5\xf3\x0e\x19\x73\x03\x19\x33\x13\x19\xf3\x01\x19\xb3\x0e\x19\x5d\x80\xd1\x00\x1e\x89\x9c\x32\xdd\x29\x77\xb8\xcb\x95\xc4\xad\xd8\x63\x97\xd8\x7f\x2c\x63\xe9\xb9\x8c\x03\x73\xaf\xe6\x5c\xd5\x74\xdc\x09\x97\x35\x71\xa4\x39\x12\xeb\xab\xd0\x75\x5d\x49\x00\x36\x46\x16\xfd\xba\xf3\xba\xee\x28\x0d\xad\xa7\x99\x0b\xa6\x95\x71\x7f\xe4\xea\x56\x6f\x5d\xd9\x4c\x56\x9f\x41\xce\x36\x01\x47\x48\xd2\xdb\x43\xf7\x6b\xeb\x6c\x7e\xf9\xff\x11\x46\x77\xd1\xe0\xd8\xf4\x90\x0e\xf1\x6f\x2d\xc1\x71\x32\xc4\xbf\xfb\x8d\xb5\x58\xa8\xc0\x57\xa5\x02\xb8\xb8\x25\x0d\x52\x3a\x43\x0a\xa4\x24\x06\xf8\xcd\x40\xdb\x51\xf1\xc7\xd2\x26\xde\x76\xd4\xfb\xb1\x34\x11\x67\xcf\x89\xaf\x92\xe2\xcf\xd0\x4d\x54\xcc\x54\x5a\x7c\xf1\xc5\x7c\x8f\x4f\xb6\x91\xb6\xcf\xe7\xa2\xcd\x5c\xb5\x11\x5f\x4e\xe6\x8e\x64\xfa\x33\xc8\xa6\x2f\x40\xa7\x12\x0f\x7c\xce\xe4\xe7\x54\x7d\xb6\x37\x9f\x43\x73\x81\x25\x95\x28\xe1\x73\x26\x3f\xa7\xea\xb3\x3b\x25\xff\x4c\xe6\xe4\x57\x0e\x47\x8e\x2b\x6c\x2e\xd3\x4b\xef\xc9\xe4\x07\x6c\x56\x67\xec\x57\x85\x9d\x9c\xfd\x33\xed\x15\x09\x56\x8f\x3a\xce\xcc\xfc\x30\x9b\x9a\x34\x80\x14\xce\x59\x17\xe7\xbc\x83\x73\xd6\xc5\x39\xd7\x71\xce\xb6\xc1\x89\x65\x3f\xb9\x1a\x1a\xe4\x7d\x13\x2e\x07\x05\x5a\xa7\xfd\x9f\xd5\x8f\x56\x68\x85\x41\x5b\x28\x70\xfa\x75\x99\x4c\xc3\xed\xc6\x29\xfb\xa9\x2a\xd7\x38\x67\x5d\x9c\xf3\x0e\xce\x59\x17\xe7\x5c\xc7\x39\x6b\x71\x1a\xa3\xce\xf1\x77\x08\xcc\xb4\x7e\x0f\xd9\x97\xbe\xb7\x5f\xa6\xfa\x1e\x8c\xf7\xfb\xd2\x75\x8d\xea\x7b\x70\x06\xdf\x97\x36\x17\xfa\x01\x1e\x4a\x10\x75\x66\xf3\x86\x44\x93\x51\xca\x8a\x02\xe1\xac\xed\x8b\x74\x17\x15\xd6\xdd\xc5\x6c\x1b\x5f\xd5\xa2\x15\xff\x0a\x8e\xb8\x71\x56\x80\x2a\x9b\x99\x10\x66\x57\xc2\xf8\xbd\xd1\xf5\xf4\x31\x7e\x5f\x9a\x30\x7e\x5f\x5e\x05\xa3\xd9\xd9\xf5\x31\xfe\x68\xc4\xf8\xa3\x09\xa3\x59\xdb\xfa\x8f\x57\x58\x50\xc2\xe2\x45\x6d\xf6\x50\xd1\x4a\x1d\xac\x83\xd4\x5e\x69\x5f\xba\x47\x20\x91\xe8\x24\xd6\xb0\xb6\x23\xf3\xef\x67\x39\xab\x38\xba\x70\xcf\xf4\xc5\x1f\xcc\x37\x8d\xfa\x0d\xd3\xcd\x13\x13\xd9\x30\x00\x15\xa6\x36\x30\xb1\x2d\x4c\x6d\x60\x0e\xcd\x4d\x6d\x60\x0a\xcd\x4d\x6d\x60\x4a\x3e\xc9\xe7\xf0\x7c\xc7\xdc\xf6\x7e\x07\xcc\xe9\x27\xf9\x0c\x6a\x49\xd6\x71\x9d\x73\xf9\x80\x69\xd6\x97\x40\x04\xa4\xcc\x44\x23\x2c\x29\x64\x26\x1a\x61\xf5\x22\x35\xb5\x81\xc5\x8b\xd4\xd4\x06\xd6\x49\x98\xa9\x0d\x2c\x93\x0c\x5e\x33\x10\x7f\xb0\xec\x32\x91\xaa\x5e\x11\x2b\x33\x60\xe1\x66\x22\xf9\x20\x34\x6b\xbf\x1d\x71\x24\x37\xaa\x61\xb0\x73\xad\x8f\x95\x68\x6b\x86\x10\x19\x1c\x83\xfe\xb3\x41\x34\x70\xdc\x24\xa3\x98\x1c\x83\xde\x33\x49\xec\xb1\xa7\x53\xcb\x86\xc4\xf6\xe1\x68\xab\x8c\x12\x21\xb0\x28\x1d\x22\xc4\x2d\x42\x60\x4f\xaa\x10\x76\x3c\x41\x3a\x8e\x50\x5b\x97\x94\x08\x09\xb8\xd8\x21\x42\xd2\x22\x24\xb3\x7a\x5c\x9a\x40\x7d\xcd\xbd\x8e\x23\xd4\x56\x32\x25\x42\x5f\x20\xcc\x87\x08\xfd\x16\xa1\x2f\x70\xe5\x0a\xa1\x3f\x62\x0e\x7d\x38\xda\xda\xa7\x44\x18\x08\x84\x7c\x88\x30\x68\x11\x06\x02\x17\x57\x08\x03\x1d\x21\x1f\x47\xa8\xad\x96\x4a\x84\xa1\x40\x58\x0c\x11\x86\x2d\xc2\x50\xe0\x2a\x14\xc2\x50\x47\x58\x8c\x23\xd4\xd6\x57\x25\xc2\x08\x26\x15\x43\x84\x51\x8b\x10\xa2\xf7\x13\x85\x30\xea\x4c\x22\xc6\x11\x6a\x2b\xb2\x12\x61\x2c\x10\xce\x86\x08\xe3\x16\x21\x4c\x9b\xd4\x98\x2c\xea\xbb\x82\x80\x4f\xbe\x7b\xf1\xe5\x51\x9c\xeb\x7b\x14\x07\x8b\xe0\x5e\xbd\x6c\x26\x80\x41\x1e\x16\xdf\xbb\xee\x67\x71\xcc\x68\xf0\x3f\xe5\xc3\x38\x0f\x97\x8b\x0f\x7c\x25\xb3\xfc\xa2\x6a\x89\x7c\x72\x27\x2d\x2b\x11\xa0\xe4\x88\xc1\xf9\xec\x94\x17\xcb\x15\x57\xc7\xa9\x07\x52\xd3\xee\x9a\x68\x7b\x77\xd5\xf2\xb5\x4f\xae\xe3\x21\x9e\x3f\xeb\x13\x3c\x3a\x9d\x4d\x7e\x90\xbb\x08\x7b\x24\x38\xf4\x55\x9e\xe2\x2f\xb7\x9b\xac\x57\x95\x42\x4c\x76\xbd\xdd\x24\x9a\x8c\xdc\x6e\xea\x1c\x6b\x18\xdc\x6e\x0a\x31\xf9\x72\xbb\xe9\xba\x6f\x37\x09\xa9\x6c\x77\xbb\xc9\x28\x9c\xce\xed\x26\x29\x20\xe7\xed\x26\x79\x8f\x76\xcb\xdb\xdf\xfe\x9f\xfa\x3e\x13\x5f\x64\x77\x52\xb6\xe6\x51\xd0\x2b\x38\xcd\xc3\x7e\xd5\x0f\x67\xef\xf3\xa2\xf7\x63\x56\x9e\xcd\xf8\xea\x0f\xb9\x12\xa5\x91\x0a\xdf\x05\x85\xb2\x40\x12\x06\x9f\x75\x7a\xfe\x15\xae\x4e\xfd\xb8\xd5\x9b\x40\x70\x78\xe6\x21\x74\xbd\xa9\xa7\xfd\x36\x7e\x15\xea\xf0\x10\x3d\xe7\xab\x53\x18\x45\x1f\xce\x96\x65\xc6\x11\xee\x3f\x9b\x22\x9a\x3f\x7f\x88\xbb\x77\x97\xc2\x78\x8a\x82\x64\x8a\x02\x3c\x45\xbe\x3f\x45\x24\x9c\x22\x1c\x4f\x51\x32\x45\x08\x6b\x47\x8d\x42\x3a\x45\xa1\x37\x45\x01\x99\x22\x3f\x98\x22\x12\x4d\x11\xa6\x53\x84\xbd\x29\x22\x7a\xbd\x64\x8a\x42\x3c\x45\x81\x3f\x45\x7e\x38\x45\x24\x9e\x22\x9c\x4c\x11\x16\xf0\xb5\x7a\x91\x37\x45\x21\x99\xa2\x20\x98\x22\x3f\x9a\xa2\xc8\x9f\xa2\x30\x9c\xa2\x20\x9e\x22\x3f\xd1\x2a\xfa\x78\x8a\x88\x3f\x45\x38\x9c\xa2\x78\x8a\x50\x44\xa6\x28\x0c\xa6\x28\x80\xa7\x05\xf4\x8a\x82\x12\x32\x45\x38\x98\xa2\x48\x54\xc4\x53\x14\xfa\x53\x14\x84\x53\xe4\xc7\x5a\x45\x92\x4c\x11\xc1\x53\x84\x05\xca\x29\x42\x84\x4e\x11\xf1\xa6\x08\x0b\x72\x64\xb5\xb7\x0e\xbe\x12\x33\x5f\x49\x97\xaf\x82\x0a\xc1\x47\xd1\x6f\x22\x3e\x4f\x11\x0a\x75\x6a\x15\x62\xd1\x2d\x41\x2d\x10\xe4\xe9\x54\xfa\x8a\x71\x82\x2a\x51\x21\x9a\x22\xbd\xbb\x38\x92\xfc\x10\x0c\x06\xea\xfd\xae\x20\x84\x40\x05\x83\x05\xff\xfc\x58\x32\x36\x0c\x7b\xfc\x0a\x3c\x25\xad\x50\x4a\x3f\xd0\x31\x08\xd1\x08\xd5\xf0\x85\x48\x23\x29\xf6\x50\x97\xa1\x10\x81\xd0\x07\xa1\x17\x42\x86\x82\xb1\x75\x54\xd3\x79\x11\xea\xfc\xf4\x7c\xce\xe0\x99\x14\x11\x54\xae\x67\x65\x31\x78\xe1\x09\xac\xe0\xbb\x57\x3f\xbd\x3c\xfe\xee\xb1\x7c\x53\x4a\x70\x8c\x4c\x11\x74\x5e\x70\x88\x0a\x8d\x54\x62\x02\xee\x2a\x4d\xc5\x4a\x9c\x44\x69\x2f\x30\x84\xea\xf8\x5f\x7e\xf3\xec\x35\x5f\x23\xb6\xc8\x55\x6e\xf4\x33\x10\xa9\x7c\x4f\xc3\x40\x87\xa8\xff\xd3\xf3\xae\x3c\x7b\x21\xa5\xb7\xf1\xee\xc2\x64\x84\x12\xcf\x9b\xf6\xcb\xea\xb9\x82\xac\x62\xa8\x40\x3a\x15\xa8\xe7\x91\x41\x15\x5f\xab\x32\x2c\x0d\xf4\x52\x03\x82\xb0\x8b\x80\x18\x10\x44\x5d\x22\x4d\x55\xe2\x5e\x3f\x0c\x88\x68\x87\x90\x21\x88\xa4\x8f\x65\x08\x82\xe9\x55\x4c\x15\xd2\x3e\xb7\x86\x55\xb2\x1e\x9a\x41\x85\xbc\xdf\x95\x61\x15\xae\x55\x19\x62\x28\xba\x54\x0e\x9b\x53\x57\x6b\x4c\x47\xe5\x41\xe8\x08\x02\x9f\x8e\x68\x55\xd0\x47\x62\xd0\x0b\xea\xd6\x9b\x88\x8e\x2a\x66\x4c\x5d\x8a\x49\xe9\xa8\xbc\x13\x3a\x22\x6f\xd6\x27\xc2\xa0\x12\x7d\x34\x43\x4a\x32\x3a\x2a\xf1\x9c\x8e\x68\x0d\xa7\x6e\xed\x2e\xfa\x38\x0c\x92\xb7\x8a\x4b\x79\x09\x6c\x66\x24\xd1\x4a\x2d\xc2\xf4\x3b\x55\x8c\xd8\x83\x2e\x14\x53\x1f\x43\xbd\x8a\x51\x27\x74\x3a\x0d\xe5\x71\x97\x0c\x87\x6d\x60\x87\xfa\x27\x7d\x4a\xad\x8e\x02\x3b\x24\x9a\x76\x3b\x63\xd0\x8a\x4e\x67\xac\x7e\x02\x3b\xf4\x97\xf7\xaa\xd8\x5c\x05\x36\xbb\x02\x3a\xca\x0a\x4c\x47\x59\x41\xe8\xa8\xe8\x7d\xea\x16\x5b\xd0\x03\x61\xf3\x15\x2e\x76\x47\xd4\xa5\xc2\x31\x1d\x11\x06\xa5\x23\x9c\x4c\xe8\xa8\x6a\x31\xea\x16\x68\xda\xe7\xb7\x61\xf0\xe8\x63\x19\x56\xc9\xa9\x4b\xa4\x9c\x8e\x98\x50\xd1\x97\xa8\xfe\x46\xd5\x74\x2c\xca\x08\x3c\x8f\x06\x1e\xb6\x7a\x10\x55\xc7\x1a\x66\x34\x02\xb4\x79\x90\x1a\x89\x67\x42\x12\x74\x91\x18\xeb\x84\x5d\x38\x46\x62\xa2\x2e\x1c\x63\x9d\xb8\xad\x63\xc0\xa2\x3b\x5b\x63\xf3\xa4\x8f\xc2\x00\x84\xf5\xbb\x63\x0f\x38\x14\x22\x03\x90\xac\xc3\x58\x43\x85\xbc\xad\x60\x75\x20\x92\x04\x43\xe3\xa2\x2f\x15\x6b\xdc\xe5\x64\x26\xa6\x23\xbd\x20\xd4\xc5\x6d\xbf\x8f\xc2\xa4\x1b\xb4\x27\x77\x93\x6e\xd0\x71\x86\x47\x74\x44\x51\x63\x3a\xae\xa8\x94\x8e\x08\x25\xa1\x0e\xa1\x30\xea\xb6\xa5\xb4\x4f\x81\xdd\x91\x38\x4d\x25\xa7\x23\x4a\xcc\xfb\x3c\xb5\xfb\x13\xab\x06\xe9\x13\x10\x43\x29\xde\xc2\xec\x31\xd9\xc2\x98\xb0\xbf\x85\xe1\xe3\x60\x0b\x7d\xc6\xa1\xd3\xf4\x71\x34\x66\x92\x38\x1e\x71\x86\x7a\x08\x6e\x86\x90\x8c\xb9\x4b\xcc\xc6\xec\x1e\xa7\x5b\x78\x4b\x9c\x8d\x39\x32\x9c\x6f\xe1\x2c\x31\xdf\xc2\x95\xe1\xa2\x2f\x21\xa3\xba\x8c\xb9\x0a\x8c\xc7\x2c\x14\x93\x2d\x0c\x04\xfb\x23\x56\x86\x83\x6d\x1c\x5b\xb8\x85\xdb\xc1\x91\xd3\xbb\xe1\x78\x0b\xb7\x84\xe9\x16\xb6\x88\x93\x2d\xac\x1e\xb3\x2d\xbc\x29\x4e\xc7\x3c\x18\xce\x5c\x2e\x0c\xe7\x63\x6e\x81\x6f\xe1\x46\x71\xd1\xf3\x50\xbb\x84\x2a\xd8\x0b\x2c\xce\xc8\x4c\x32\xe9\x70\x05\x5b\x43\x14\x09\xdb\x04\x3d\xd0\xca\x3d\x43\x79\xd8\x13\xce\xb0\x46\xd4\x61\x9a\x09\x47\xdc\xa9\x31\x3e\x1c\xdb\x63\x93\x16\x8b\x2d\x32\xa9\x7b\x6a\x8b\x4a\x5a\x2a\x86\x74\x66\x3d\x6e\x0e\x6b\xe4\x1d\x6e\xd9\x42\x13\x80\x60\x09\x4b\x54\x5b\x33\x07\x5c\xdd\xc3\x74\x8c\x7c\x42\xed\x8a\xe2\xd3\x31\x45\x09\xe8\x98\xa0\x43\xea\xee\x7c\x44\xdd\xaa\x14\x6b\xe5\xc3\x52\x4a\xed\xac\x4b\xa8\x8b\x75\x8c\x8e\xa9\x57\x4a\xdd\x46\x90\x51\xb7\xea\xe4\x74\x4c\x31\x38\x1d\x33\x82\x82\x8e\xa9\x78\x27\xac\xb0\x28\x01\x1e\x31\x57\x4c\x46\x34\x14\xfb\xa3\x2e\x03\x07\x4e\x4d\xc5\xe1\xa8\xc1\xe3\x68\xd4\x6b\xe0\xd8\xe5\x89\xe9\xa8\x25\xe2\x64\xd4\x65\x60\xe6\xb0\x46\x9c\x8e\xb8\x0b\x9c\x8d\x7a\x2d\xac\xbb\x03\x03\x0a\x3e\xe2\x7b\x71\x31\xea\x92\x54\x68\xe1\xec\x26\x76\xda\x15\x26\xe3\xae\xc5\x77\x78\x0e\x1c\x8c\x98\x35\x0e\x47\x7d\x0b\x8e\x9c\x06\x8c\xe3\x51\xdf\x86\xe9\x88\xf3\xc1\xc9\xa8\x05\x62\x36\xe2\x06\x70\x3a\xea\x03\x71\x36\xea\x0a\x70\x3e\xea\x8f\x30\x77\x38\x3b\x5c\x74\xbd\xd1\x2e\xf1\x03\xf5\x24\x4a\xb3\x6f\xa9\xa3\x4f\xec\x05\x96\x50\xa2\x26\xda\x50\xee\xb7\x10\x02\xb3\x22\x06\x76\x25\x0a\xbb\x1c\x31\xc7\x10\x4d\x70\x6c\x42\x1f\x7b\x9d\xf0\xcf\x3e\x7e\xd6\x3b\x2a\xe6\x08\xa2\x95\xad\x39\x7e\x90\xe5\xe6\xd8\xa1\x65\x9f\x6d\x07\xa5\x65\x8f\x01\x46\xae\x59\xa9\x25\x72\xa8\xd5\xdb\x1c\x3b\xb4\x02\xb6\xf4\xdf\x29\x5f\x4c\xed\xdd\x23\x74\x8c\x78\x9f\x8e\x31\x20\xa0\x6e\x11\x87\x74\xac\x0b\x11\xb5\xea\x4f\x4c\xc7\x94\x8f\x52\x17\xff\x92\x2e\x72\x5b\x10\xe1\xd0\x8e\x94\xba\xa4\x97\xd1\x31\xed\xcb\xa9\x5b\x7f\x39\x75\x9b\x5f\x41\xc7\x2c\x04\x7b\x23\x26\x82\xf1\x88\x15\x62\x32\x6a\x86\xd8\x77\x8d\x14\x4e\x0d\xc7\xe1\xa8\x89\xe0\xc8\x1b\x93\x13\x8e\x47\x3d\x19\xa6\xa3\xd6\x82\x93\x51\x77\x81\xd9\xa8\xc3\xc3\xe9\x88\xcf\xc4\xd9\xa8\xdf\xc0\xf9\x88\x5b\xc2\xdc\xe1\x97\x70\xe1\x74\x1b\x32\x7a\x70\xf7\x01\x8f\xda\x25\x26\x76\xc3\xc4\xfe\x88\xd9\xe3\x60\x44\xf1\x71\x38\x6a\x3b\x38\x1a\xf7\x6e\xb1\xc3\xbd\x61\x3a\x6e\x3c\x89\xd3\x7f\x60\x36\xea\xff\x70\x3a\xea\x44\x71\xe6\x74\x22\x38\x1f\xf5\x52\x98\x8f\xb8\x29\x5c\x74\xfd\xc8\x6e\xc1\x83\xd1\xa7\xd4\xf4\xda\x76\x48\x1a\x6a\x8c\x21\xc3\x5d\xed\xb8\x86\x31\x62\x50\x15\x60\x3d\xc5\x18\x37\x34\x31\x9f\xa1\x3c\xaa\x01\xd8\x2a\xc4\x2d\x81\x86\x52\x5d\xe6\xb6\x90\xa1\xa5\xcf\x12\x33\xb4\x3d\x34\x60\x48\x5b\x02\xcd\x24\x64\x9d\x0a\xa6\x81\xc3\x6a\x7b\x5c\x17\x8e\x01\x74\xd1\x61\x8e\x79\xcd\xc1\xd5\x1e\xd3\x11\xe6\x12\xea\xd9\x14\xc7\xa7\x6e\xc5\x09\xa8\x4b\x71\x42\x3a\xa2\x17\x11\x1d\xe1\x5a\x4c\x47\x54\x8f\xd2\x11\xd1\x26\xd4\xc6\x77\x46\x47\x64\x9a\x52\xb7\xd6\x66\x74\x44\x6b\x72\x3a\x22\x39\x4e\xdd\x8a\x5b\x50\x97\xda\x63\xcf\x69\xb6\x18\x7b\x56\xb9\x62\x32\x66\xd3\xd8\x1f\xb3\x49\x1c\x8c\x58\x35\x0e\xc7\x8c\x02\x47\x63\x9e\x03\xc7\x23\xb6\xdd\x8c\x7b\x56\x31\xe2\x64\xcc\x80\x30\x1b\xf1\x8f\x38\x1d\xf3\x20\x38\x73\x7a\x28\x9c\x8f\x79\x18\xcc\xed\x83\x73\x31\xe2\x21\x20\x3e\x70\xcb\x0a\x8f\x68\x1a\x26\x23\x96\x8e\xfd\x31\x63\xc6\xc1\x98\xb1\xe2\x70\xcc\x55\x45\x76\x57\x84\xe3\x31\x67\x81\xa9\xdb\x5c\x92\x31\x83\xc7\xcc\xea\x2c\x70\x3a\x66\xcb\x38\x1b\x71\x17\x38\x77\x3a\x4b\xcc\xc7\x5c\x19\x2e\x7a\x0e\x67\x97\xa8\x40\x91\x4d\x4d\x5e\xa4\x86\x69\x8a\x0b\x64\x5b\x62\xee\xb3\xdf\x96\x13\x13\xec\xa0\xe5\x88\x11\x7e\xa8\xf7\xc7\x14\x15\x34\xa5\x43\xd8\x71\x47\xa1\xad\xa3\xa2\x31\x1a\xd0\x88\x1a\x02\x66\x35\x5a\x23\xc9\xa9\x52\x50\x53\x04\xa0\xf1\x6a\x58\x9e\x6b\x60\x87\xa5\xbc\xe9\xeb\xb0\xac\xe8\x70\xd9\xd4\x53\xa7\x90\x30\x75\x0b\x89\x50\x4b\x8f\x7c\xea\x92\x4e\x40\x5d\xfd\x09\xa9\x5b\xeb\x22\xea\xd6\x8c\x98\xda\xf9\x41\xa9\x4b\x2f\x12\x6a\xd7\x67\x46\xdd\xa2\x4f\xa9\x5b\x86\x19\xb5\xe8\x54\x4e\xdd\x22\xe2\xd4\xa5\x53\x05\x75\xab\x32\xf6\x46\xec\x08\xe3\x11\xe5\xc3\x64\xc4\x52\xb1\xef\x50\x40\x1c\x38\xed\x14\x87\x23\xa6\x88\x23\x6f\xc4\x07\xc5\x4e\x9b\x6b\x22\x58\x0b\xed\x89\xd5\x6b\x33\x9b\xb5\xe2\x74\xc4\xb5\xe1\xcc\xe1\x17\x71\x3e\xe2\x43\x30\x1f\xb1\x59\x5c\x38\x9d\x9b\x18\xd1\x2d\x84\x63\xa7\x2a\x61\xe2\x34\x5a\xec\x8f\xd8\x25\x0e\x46\x0c\x13\x87\x0e\xcb\xc4\xd1\x88\xaf\xc1\xf1\xa8\xb3\x1a\xb1\x24\x9c\x8c\xd8\x28\x66\x0e\x07\x80\x53\xa7\xd7\xc2\x99\xd3\xb5\xe0\xdc\x66\xff\x98\x8f\x99\x70\xd1\x75\x3d\xbb\x0f\xdd\x06\x1d\xa9\x49\x0d\x3c\x6c\x18\xba\x55\xa8\x61\x18\xb4\x15\x50\x53\xb3\xa0\x09\x72\x4c\xa5\xa1\xa5\xfb\x91\x04\x69\x18\xa3\xdb\x90\x69\x58\x4a\xb5\x0e\x98\x86\xe9\xa6\xef\xc3\xa6\x4c\x53\xf2\x61\x69\xaa\x75\xc2\x34\x55\xd7\xe2\x38\xc3\x30\x2d\xf9\x36\x84\xca\x5b\xbe\x99\x26\xe9\x5a\xe4\x3b\xec\xa9\x8b\x0d\x98\x9a\x99\x4a\xa8\x4b\xbe\x3e\x75\xf5\x31\xa0\x0e\xc5\x09\xa9\x8b\x79\x11\x75\xf5\x24\xa6\x36\xf6\x50\xea\x50\xab\x84\xba\x44\xcd\xa8\x4b\x22\x29\x75\x28\x42\x46\x6d\x6a\x9e\x53\x97\x26\x73\x6a\xd6\xd8\x82\x3a\x84\x8c\x3d\xa7\x94\x31\x76\x9a\x2b\x71\xda\x2b\xf6\x9d\xb6\x82\x03\x97\x39\xe0\xd0\x69\x4a\x38\x72\x1a\x04\x8e\x5d\x1e\x41\x8d\x37\xc6\xa2\xc4\xe9\x2d\x30\x73\x59\x0c\x4e\x2d\x4e\x03\x67\x36\x27\x9b\x3b\x2d\x17\x73\xa7\x53\xc0\x85\xd5\x23\x62\xcf\x29\x75\xec\x34\x44\x4c\xdc\xd6\xed\x5b\x34\x0d\x07\x4e\x43\xc3\xa1\xcb\x84\x71\x64\xb5\x43\x1c\x3b\x3d\x03\xa6\x4e\xeb\xc7\x89\xd3\x16\x31\xb3\x38\x2b\x9c\x3a\xcd\x0d\x67\x2e\xef\x80\x73\xab\x15\x63\xee\xf4\x1c\xb8\xd0\x9c\xc3\x2e\x63\x2a\x15\x03\x3c\x31\x00\x6c\x98\x33\xf4\xc7\x77\xdb\xcd\x8d\xa1\x3b\x96\xed\x86\x8e\x58\xc1\x33\x14\x85\x12\x1e\x31\xd2\x11\x35\x85\x26\x27\xac\x28\x31\x8f\x33\xd4\x33\xd3\x9f\x34\xfd\x36\xb9\x60\x49\xa7\xa9\x28\x6d\x80\x1a\xe8\xcc\xee\xca\xcb\x1e\x43\xf7\x6b\xd6\x13\xde\x30\xd1\xd0\xa6\x50\x44\x18\x8a\xea\x4d\x25\x6b\xcf\x65\x31\x76\xf1\x54\xd5\x21\x2e\xf9\xab\x3a\xbe\x4b\xd6\xea\xf7\xc0\xc5\x6c\x55\x27\xb4\xb3\x55\xd5\x88\x46\xfb\x1c\x5b\x54\x4b\x15\x53\x17\x47\x55\x9d\xc4\x26\x25\x55\xce\xec\x5a\xaa\x6a\xa4\x2e\x7d\x54\x75\x32\xb3\xc8\x55\x69\xee\x52\x23\x55\x87\xbb\x54\x54\xd5\x29\xec\x16\x5a\x47\xc4\x46\xc3\xc6\xae\x1e\x60\x62\x61\x32\xf6\x6d\x1a\x87\x03\x17\xb1\x38\x74\x89\x05\x47\x2e\x66\xe0\xd8\xd1\x45\x9b\xff\x4d\xec\x22\xc4\xcc\xa5\xa9\x38\x75\xfa\xc3\xcc\x65\x51\x38\xb7\xeb\x37\xe6\x36\xa5\xc3\xc5\xb8\x75\xb5\x93\x1b\x6b\x0d\xec\xf6\x05\x98\x8c\x2b\x1c\xf6\xc7\xac\x0f\x07\x4e\xeb\xc3\xe1\xb8\x13\xa8\x85\xed\xec\x6e\x3c\xee\x94\x30\x1d\x77\x6e\x38\x19\xf7\x06\xb5\x3a\xb8\xac\x4c\x2a\x85\xb5\x34\x1b\x73\x6b\x52\x31\x1c\x74\xf2\x31\x8f\x53\x2b\x09\x60\xd1\x46\x76\xf9\x51\xcf\x6b\xf0\x94\xad\xdf\xaf\x51\x35\x63\x15\x5a\xf3\x39\xcf\x2a\xc8\x47\xf4\xf2\x9b\x67\xaf\x51\xb9\x38\xab\x9f\x89\x68\x32\x1a\x3c\x7d\xf0\xb2\xf7\x70\x71\x7b\x31\x71\x8a\xda\x83\xff\xf0\x80\xa2\xfa\x02\x9f\xd5\x97\xa9\xde\xd0\x53\xbf\xca\x0a\xf2\x4b\xfd\x59\x7c\x99\x6a\xfd\xe9\x53\xae\x65\x55\xfa\xf6\xd1\x4b\x99\x18\x0b\xc9\xc4\x2f\xee\x37\xaa\x44\xed\xe6\x81\x2a\xf9\x45\xcb\x92\x72\xd5\x27\xaa\xdc\xa9\xf5\xde\xf3\xcb\x26\x05\xd8\x7b\x7e\x69\x48\x7d\xf7\x9e\x5f\xd6\x79\xf5\xde\xf3\x4b\x73\x5a\x3d\x81\x43\x8a\x28\x8c\x50\x5a\x56\x6b\xc4\xb2\x6c\xb9\xca\xcb\xc5\x09\xaa\x96\xe8\xf9\x43\x6c\x84\xfb\x4d\x09\xa9\x80\xde\xf4\x73\x20\x9b\xde\x0e\x09\x23\xfb\xdb\x21\x2d\xb8\xe7\x4b\x01\xf0\xf9\x43\xfc\xa6\x7c\x8b\xee\x20\x6c\xc8\x51\xaa\xf0\xca\xf4\xfc\x93\xba\x77\x6f\xda\xf6\x2a\x1d\x9f\xf8\xcf\xc4\xc7\xe8\x8e\x06\x1a\xf2\xf0\xed\xa1\x9b\x03\xc0\x86\x84\xa5\x0f\xd6\x6b\x7e\x9a\xce\x39\xc2\x11\x5a\x9f\xa7\xef\xf9\xa5\x81\xfd\xeb\xf3\xf4\x7b\x7e\xb9\x6e\x44\xd0\x7e\xb7\x33\x65\xf1\x12\x2a\x49\xd6\xd4\x5f\xee\x23\x1c\x35\xdf\xec\x4f\xac\x3c\x84\x8c\x53\x8a\x1e\x33\x23\xd7\x35\x74\x45\xcb\x1b\x05\xf4\xad\x22\xca\x08\xd7\xfd\x74\x4b\x5a\x56\x2f\x21\x2b\xca\x91\x96\x04\xa5\x81\x6b\x03\x29\x15\x2a\xa0\x46\x85\x22\xc3\x36\x26\xad\x21\x81\x5d\x6b\xba\x78\x8a\xd5\xf2\x14\x1c\xcc\x9c\x17\x15\x22\x14\x2c\x43\x60\x36\x37\x94\xcc\x79\x33\x29\xd1\xa1\x7c\x1b\xc2\x83\x04\x8e\xb5\x72\x4d\x26\xcf\x1f\x12\xa5\x83\x7b\x68\xbf\xe1\xc0\x1e\xfa\x1b\x22\xf4\x2d\xe4\x78\x04\xdd\x2a\xd1\xdf\xe0\x8d\x8b\xad\xc9\x5b\x95\x27\xb3\xed\xe9\x0b\x20\x7d\x67\x4b\xe4\x5e\x87\x4a\x42\xa1\x58\xd2\x8a\xf6\x11\x09\x2c\x04\xef\x19\x28\x1e\xa0\x35\x65\xf6\x17\x1d\x28\x17\x19\x47\x9c\x65\x33\xa5\x76\xa8\x5c\x23\x76\x76\x36\x2f\x79\x2e\x64\xc9\x16\x88\x6f\xce\xd8\x22\xe7\x79\x9d\x97\x11\xdc\xfb\xd4\x08\x4d\xb0\x40\x81\xc9\xd8\x02\xa5\x1c\xa5\xab\xe5\x7b\xbe\x40\xe5\xa2\x5a\x22\x2a\x93\x02\xaf\xd1\x3a\x63\x73\x09\x5e\x82\x5c\x9b\xa1\x5d\xcc\xca\x6c\x86\xd8\x7c\xbe\xbc\x58\x03\x68\x01\xb7\x5a\x0a\xb0\xe7\x6b\x9e\xa3\x8b\xb2\x9a\x2d\xcf\x2b\x49\xe0\xba\x5c\x2e\x86\x50\x14\xa3\x21\xbd\xe6\xa4\xfd\x72\xff\xbe\x7a\x56\xa6\xfd\x49\x38\x14\x1f\x9b\x38\xd7\xd1\x5c\x2c\x35\x37\x76\x2b\xae\x02\x0b\x4e\xac\xfd\x0c\x3e\x6b\x52\x4a\x21\xde\x46\x42\xfa\xbe\x59\x54\xb6\x7e\xc4\x7a\x3f\xe2\xb7\x2a\xb1\xe7\x6f\xfa\x4f\xf0\x28\xc0\xe0\xa9\x1d\x83\x07\x7c\x28\x13\x5f\xa2\x72\xf1\x81\xaf\xd6\xdc\xee\x05\xcb\xc5\x87\x97\x3d\x47\xd8\xf9\x69\xab\x01\x02\x3b\x06\x88\x16\x9a\xce\xb1\xf5\x1b\x1c\x0a\x85\xee\x43\xff\xd8\x59\x70\x68\xbf\xf0\x45\xb6\xba\x3c\xab\x76\x78\x0a\x50\x65\xac\x5d\x3e\x6c\xda\xb5\x95\xa7\x5d\x97\x6f\x4d\xa1\x9b\xf3\xcf\x81\xb5\xe5\x88\x2b\x77\xef\x43\x37\xe6\x69\xcd\x48\x53\xd0\xf1\x1f\xbc\xd2\xe3\xb4\x2e\x71\x73\x00\xaa\x3d\x8d\xd5\x97\x81\xac\xb6\xea\x57\x83\x97\xb3\x0c\xd1\xc7\x77\x8b\xb2\x2a\xd9\x5c\x4f\x7d\xd5\xad\xc3\x37\xd9\x8c\x2d\x4e\xf8\x93\x17\x6d\x5a\x54\x99\x79\xcc\xdb\x78\x85\xfc\x5f\x5f\xa5\xcd\x6d\xe4\xfb\xd4\x30\x63\x2d\x0a\x6b\x9b\x17\x4f\xf4\x36\x04\xf0\xf8\xea\x6f\xbb\x36\x54\xd2\xe6\x15\x85\xf8\xff\x96\xb4\x41\x9b\x50\xfd\x19\x33\xd3\xba\x9e\x6a\x93\xe9\xc3\xc0\xa2\xe4\x47\x69\x55\xf0\x79\xfc\xd9\x36\xc3\x48\x64\x8c\x27\x00\x9c\xed\xd9\x8b\x46\x31\x74\x3d\xb1\xd4\x5d\x75\xeb\xae\x54\x5d\x23\x91\x8f\x79\xb9\xae\xf8\xbc\xd1\x62\x33\xc4\x02\x3a\xbf\x5d\x68\x41\xdd\x0e\xba\x10\x03\xad\x4c\xb5\xf6\xa6\x7c\xfb\x66\x32\x51\xd4\xbe\x6b\xdd\xb5\x08\x24\x9b\xa9\x0b\x7c\x87\xb4\xda\x26\xd6\x18\x1c\x76\xcf\x90\x56\x36\x4e\xf5\x2c\x69\x5e\x93\x51\x8c\x3b\xf0\xbf\x2f\xf2\x25\x5a\x5f\xb0\x33\x19\x7e\xcc\xd9\xba\x92\xca\x30\x74\xe1\x95\x5b\x64\x3d\x62\xbb\x02\x73\x19\x7e\x65\xd0\x61\xc8\x28\xbe\xab\xa9\x0f\x4c\xe3\xda\x4c\xf0\x2a\xa6\x7e\x15\x97\x32\xe2\xba\x0c\x33\xb2\x0a\x2d\xcf\xab\x81\x07\x6e\x5c\xae\x5b\x64\x1d\x97\x6b\x97\x59\x67\xc8\x78\xcf\x2f\x65\x0a\xe8\x28\x38\xf4\x89\x5e\x52\x7e\xb0\x14\x68\x79\xa3\x23\x63\xd6\xe8\x43\xf4\x52\x68\xa0\x9a\x04\xac\x96\xeb\x75\x1b\xa6\x43\xce\x43\x08\x88\x61\x5a\x2a\x5b\x34\x03\x55\xcb\xb8\x49\x3d\x5e\x9d\xb2\xf5\xfb\x8e\xc9\xd6\xba\x3b\x99\x74\x54\x54\x18\x62\x3d\xba\xbe\xeb\x74\x5d\x18\xad\x80\xa2\xb1\xa0\xa3\xb2\xef\x40\x67\xbf\x32\x2a\xbe\x28\x13\x11\x95\x84\xac\x6a\xd5\x76\x37\x20\xfb\xc5\x93\xed\xc9\x5e\xd9\xc9\x9e\xbb\xc9\x9e\x3b\xc8\x5e\x6d\x41\xb6\x33\x89\xf4\xba\xce\x22\x2d\x97\x3f\xb6\xcb\x23\x3d\x96\x84\x59\xc2\xaa\xf8\xa6\xd2\x53\x31\x7f\xfb\xe8\xe5\x81\x0a\xd0\x3a\xb9\x98\xa7\x28\x2b\x4e\x0c\xc9\xb5\xcf\xe6\x4c\x10\xb1\xa9\x50\x1f\x8a\x0a\xb8\x26\x2d\x1e\x13\xa0\x26\xb3\xf3\x70\xa1\xa6\x9b\x74\xfb\xdb\x47\x2f\x8d\x19\xb7\x5f\xad\xca\xb3\x39\xbf\xb3\xdb\x12\x91\x6c\xd4\x59\x28\xd2\x7f\xfa\xf3\x2c\x17\xa9\x85\x08\x41\x76\x09\x19\x4a\xb3\xfe\xf3\x40\x2a\x8a\xe5\x6b\x8c\x8e\x44\xbd\x03\xc9\xd5\x47\x52\xc6\xcb\xd5\xa4\x7d\x67\x5d\x3d\x1c\x5f\xa3\x3e\x58\xcf\xcb\x8c\x4f\xbc\x29\x22\x7b\x83\xb7\x30\x1a\xb0\xe4\x8a\x60\xc9\x14\x05\x0e\xb0\xfe\x15\xc1\x06\x53\x14\xed\xd9\x1f\xd2\xb8\xf2\xdc\x83\xaf\xf1\x81\xde\x58\x6b\x61\xe5\xcc\x81\x3e\xe7\xd8\xa2\x81\xbf\x05\x86\xeb\x99\xd3\x08\x5c\x3b\x12\x47\x76\xed\x3e\xde\x02\x83\x79\xd4\xc3\x09\xb9\xb6\x61\xef\x9f\xc4\xad\x36\xde\xe5\x1a\x9c\x6b\x0b\x6b\x47\x17\x6b\x73\x71\x5d\x47\xdb\xd4\x72\xe6\xcf\x6f\x6a\xf5\x52\xe8\x6b\x89\xd9\xef\x86\x64\xda\xcb\xaa\xaf\x25\x77\xbf\x1b\x06\xd3\x36\xab\xfb\xdd\x30\x9a\xaa\x64\xef\x77\x23\xfc\xf1\xed\x94\x06\x9f\x94\x70\xff\x8f\xcc\xb4\xff\xd9\xf2\xe1\xff\xf7\x64\xb6\x87\x97\x0a\xca\x05\xcf\xaf\x37\xc5\xfd\x37\x6c\xcd\xdb\xac\xf5\x6c\xcd\xb5\xb2\xd7\x3e\x71\x66\xc0\x1f\xda\xf2\x26\x0a\xd0\x82\x9d\xf2\xf5\x99\x6e\xa5\x87\x3a\x19\xa2\x8a\x20\x43\xfe\xf7\xd7\x8f\x26\x30\x0f\x50\x14\x34\x4f\xd8\x98\xc0\xbc\x8e\x02\x41\x07\x10\xb5\x89\x82\x03\xf5\x45\xd0\x6f\x88\x0c\x5a\xd0\x12\xbc\x5a\x4e\x29\x7f\xe1\x6b\xc4\xd0\x82\x5f\xcc\x2f\x91\xb4\xb5\xdc\x84\x58\x77\x28\xa8\xf3\x9a\xc7\xe2\xfc\x34\xe5\xab\x8f\x08\x5e\x95\x82\x57\x55\xc4\x07\x9f\x40\x38\x7f\xe0\x6c\x32\x5f\x5e\x40\x0b\xf1\x5f\x53\x83\x6e\xe3\xae\x77\x1b\x56\xa8\xf9\xb2\x69\xf9\x52\x7b\x84\x9a\x3d\xf5\xc0\x2c\x77\xff\x3c\xe2\xf9\x30\x2b\x0b\xbc\xd0\x8b\xbc\xee\x7a\x67\xcd\x69\x70\xf1\x8b\xb2\x13\x51\x89\x1e\x4e\x05\xd5\xe6\x31\x4c\xbd\xaf\x65\x78\xd5\x13\x8a\x45\x6f\x8f\x50\xf7\xf5\x6d\x7d\x66\xde\x97\xd4\x37\x65\x75\x51\xae\x39\xfa\xe1\xd9\xab\x35\x40\x18\x13\x4c\xfd\x50\x8a\x52\x90\x8f\xe8\x81\x90\xaf\xe0\xcb\x1d\x60\x8c\x1a\x49\x58\x51\xf1\x15\x5a\xf0\x13\x56\x95\x8b\x93\x6b\x60\x3c\x80\xe2\x82\xf1\x4a\x04\x07\x8b\x65\x35\xb1\x72\xf5\xf0\x10\x2d\x96\xa3\x91\x2a\xbc\xc9\x22\x19\xfa\x7b\xc3\xdd\x7b\xc6\x6a\x92\xb1\xbf\xd7\x4c\x36\x84\xa4\x8a\x33\x8a\x31\xb5\x36\xb4\xe2\xbc\xd7\xa1\xae\x13\x01\xd8\xa4\xf2\xe0\x87\x6f\x35\xa9\xc0\x76\x02\x8c\xdb\x67\x6c\x0d\xdb\x0b\x5b\xd9\x50\x23\x29\x80\x21\x4c\xa2\x11\x56\xb5\x14\x28\x6a\xb8\xd7\x2c\xfc\x07\x3f\x7c\x7b\x3d\xa2\x97\x7b\x3b\xad\xe0\xd9\x22\x9f\xb0\xc5\xb2\x9a\xf1\x95\x22\xc4\xa5\x06\x6c\x91\xeb\x6a\x20\x7a\x38\xa2\x0a\xad\x9d\xdd\x94\x0c\x19\xd3\x8a\xc6\xf2\x54\xfd\x3f\x4c\x3f\x9e\xbd\xf8\xdc\xea\xf1\xec\xc5\x67\xd2\x8e\x67\x2f\xae\x47\x39\x96\xab\x8e\x6e\x2c\x57\x3b\xa8\xc6\x72\x75\x65\xcd\xf8\x6d\x47\xcd\xf8\xed\x0f\xd6\x8c\xd7\x9f\x5f\x35\x5e\x7f\x36\xdd\x78\x7d\x5d\xca\xb1\xe9\x69\xc7\x66\x27\xf5\xd8\x7c\x82\x7e\xbc\xdb\x51\x3f\xde\xfd\x41\xfa\x01\x9b\xf2\xba\x66\x2c\xe4\xca\xa8\x9a\x10\xce\x79\x51\x6d\x1f\x95\x2d\x40\x27\xe4\x37\xb4\x2c\x1a\x48\xf0\x84\xcd\x75\x29\x03\x00\xbb\x1e\x75\x00\x50\x1d\x85\x80\x5f\x9e\x4c\x48\xe8\xd2\x03\x59\x49\x57\x85\x85\x49\x0f\xc4\x14\x68\x81\xee\x23\x9f\xd8\x76\xba\x34\x4d\x99\xb4\xaa\x72\xff\x3e\x5a\xc0\x16\x79\xa3\x0c\xf2\xe8\x10\x41\x77\xd0\xc2\xf8\x58\xbd\x59\x85\x04\x9c\xa1\xae\x7d\x44\xf5\xe4\xc9\x4d\x90\x0e\x66\xb2\x40\x77\x0c\x2f\x86\x0e\x50\xf7\xb7\xba\x04\xba\xff\x4e\xed\x85\xa5\xfc\x7f\x3b\xf5\x7d\x31\xb1\x4f\x2e\x6a\xed\x7d\x71\x4d\xda\x2b\xe5\xde\xd5\x54\x4d\x79\x6b\x7d\xde\x42\x79\x07\x1e\x13\x40\x5d\x41\x7f\x35\x2b\x68\xe0\x8c\x2b\xb0\x42\xff\x87\x6b\xf0\x8b\x65\xc5\x2a\xfe\xb9\x1d\xf0\x0a\xb0\x5c\x97\x0a\x03\xb4\xeb\x51\x61\x49\x98\xae\xc2\xab\xe5\xa8\xff\x15\x55\x46\xf5\x57\xf5\x08\xf4\x40\x79\xf5\xc5\x9e\x08\x07\xdb\x5f\x5e\x4c\xa2\x60\xa0\x96\x9f\x2a\xb0\x6b\xf2\x39\x7f\x2e\x89\x8d\xb8\x1c\x51\x63\x77\x81\xbd\x18\x08\xec\xc9\x55\x04\xf6\x20\xcf\x3f\x77\xe4\xcb\xf2\xfc\x33\x45\xbe\xf2\xc9\xef\xeb\x98\x33\xe7\xbd\x39\x73\xbe\xd3\x9c\x39\xdf\x7a\xce\xdc\x1f\x11\xf6\x9b\x40\x16\x0e\x8c\x9a\x83\xdf\x8c\xad\x56\x97\xa2\x59\x3d\x86\xc8\x87\xe1\x3b\xc3\x4a\xfb\x3c\xbc\x19\xc6\x30\x90\xda\x6f\x63\x6e\xb4\x2f\x71\x28\x1a\x3e\xd5\xa3\xcb\x6f\xe6\xdd\x95\x07\x0b\xf5\x04\xf8\xb2\xd0\xd7\x36\xd7\xa6\x17\x8e\x57\xcb\x33\xbe\xaa\x2e\xd1\xaf\xea\x89\x61\xa8\x08\xea\xd5\x80\x18\x2c\x2b\x2a\x05\x59\x1f\x98\xe0\xd4\x6e\xa5\x79\x13\xbd\xeb\x5d\xd6\xe5\xc9\xa2\x2c\xca\x8c\x2d\x2a\x94\x42\x79\xb9\xd0\x6c\x03\x90\x3a\x56\x7f\xdb\x75\xe9\x9a\x98\xfa\x97\x6b\x58\x07\x1e\x52\x60\x37\xc7\x0e\xbb\x26\xcf\xce\x84\x5a\xb2\xf9\x5e\x87\xf7\xa3\x8c\x43\x46\x87\xdc\x70\x4e\x03\xbb\x15\x13\x79\x57\xcc\x9f\x60\xab\x17\x3a\xab\xfb\xbd\xe8\xec\xf9\x76\x6d\xf6\x13\x81\xbd\x19\xb4\x17\x7f\xbb\x2e\x6b\x4f\x77\x85\x82\x29\x4e\x30\xc3\x29\xdc\xa9\xc9\x70\x8e\x39\x2e\xf6\x06\x40\xde\xfe\x1b\x75\x75\x8a\xb0\xb7\xf5\xf6\x00\x28\xdd\xb4\x51\xdb\x81\x5b\xbe\x50\x87\x27\xc0\x2d\xd6\x5f\xe4\x7f\x7f\xfb\xcd\x70\x01\x43\xc4\xfd\x8d\x0d\xfc\xe5\x08\x0d\x77\xc1\xf4\x3f\x39\x36\xd7\xd5\x8f\x1a\x32\xfa\x67\x01\xad\x41\x7b\x1f\x80\xb4\xa1\x39\x5f\x9c\x54\x33\x74\x1b\xd1\x2d\x8f\x52\xf7\x1d\xcd\xc3\xe5\xe2\x03\x5f\xd5\x53\x43\xcd\x0d\x2b\xff\x20\x06\xed\xfa\x76\xc0\x56\x8e\xa7\x1e\xb5\x1b\xe9\x76\x76\xe6\x3e\xa2\x57\x5d\x27\x7a\x6b\x8d\x72\x56\x31\xc4\xd6\x3b\xe2\xd9\x7a\x25\xab\xbb\x53\xb8\xd1\x1c\xf4\x41\xb5\x7c\xed\x13\xfb\x56\x08\x14\x7f\xc2\x99\x1d\x85\xab\xab\x54\x86\x93\x3b\x75\xbd\x27\x52\x98\x0d\x91\xb5\x78\x4d\xa7\x78\xa4\xd8\x0c\xb0\x64\x77\xb7\x3e\xbc\xdf\xc5\xed\xbe\xe9\xd5\x6e\xe1\xd5\xad\xde\x0c\x8e\xf0\x8b\xbf\x9a\x86\x83\xb3\xf3\xf5\x6c\x52\x07\x52\x22\x46\x30\xcd\x2b\xcd\xb5\x7b\xb1\x04\x32\x9c\x93\xad\x43\x11\x4d\xc0\xb5\x07\xa9\x61\x4e\xbb\x66\x63\x3d\x48\x32\xb0\x0a\x00\x23\x54\x32\x5b\x9e\xc1\x20\x69\x19\xfb\xd1\x68\xd8\xda\xa8\x3d\x47\xd9\x7c\xb9\x70\xcd\x54\xb6\x55\x69\x80\xd3\xd7\x65\xf8\xd1\xae\xcb\x50\xec\xd4\x65\x1d\x32\x44\x29\x92\xdc\xe6\xe4\xab\xe9\xa4\xeb\x43\xa8\xff\x57\x50\xec\xbf\x4a\xce\x0c\x81\xd6\xbe\x54\xc2\x1b\xba\xd9\xfa\xd4\x98\x1d\x01\xdc\x61\xaa\x37\xd6\x65\x70\x62\x41\xd3\x98\xd0\x45\xc7\x7e\x46\xcd\xe0\x62\x1b\x1b\xb8\x50\x2a\x5f\x83\x7f\x53\xbe\x35\xb1\xdd\xae\xaa\x50\xb9\xb3\xbf\xdc\x84\xc7\xd6\x73\x33\xbd\xd3\x32\xea\x68\xcc\xc7\xb7\x53\x1a\x6e\x73\xde\xe5\xf0\xf6\x5f\xd0\xac\xaa\xce\xd6\x77\x0f\x0f\x4f\xab\xd9\xfa\x20\xe5\x87\xe7\x55\x41\x7f\x5e\xa3\x0f\xe4\x00\x1f\x10\x94\x5e\xa2\xff\x71\xca\xaa\x59\xc9\xd6\x42\x63\xda\x03\x32\x70\x2a\x44\x1e\xf6\x38\x3c\x44\xdf\xf2\x4a\x5e\x87\xe3\x5c\xb0\xbb\x64\xe9\x9c\xaf\xd1\x3f\x14\xa6\x7f\xdc\xf8\x0a\x8e\xf1\xaf\x38\x7f\xd4\x9c\x7f\x19\x9c\xa4\x41\xb7\xa4\xf0\x6e\xa1\x9b\x37\xeb\x9f\xef\xd9\xc1\xa3\x7f\xc8\xee\x68\xc0\x9f\xc2\x0f\x2d\xec\x53\xf5\xbd\x0b\x5a\xfd\x7a\xf3\xa6\xe1\x7c\xce\x51\x87\xc8\xa6\xb2\x93\x8c\x13\x38\x39\xf3\x8f\xa9\x3c\x8d\xff\xc3\x32\xe7\x07\x3f\xaf\xd1\x72\x85\xbe\x91\x47\x69\xca\xa2\xe4\x39\xca\x96\x39\x9f\x02\x14\xb6\xc8\xd1\xf9\x9a\xa3\xb2\x12\xe3\xda\x3f\x04\x1f\xb5\x3e\xa8\x73\x38\x4d\x1f\x4e\xd4\xf7\x6e\x1f\xe4\xaf\xf7\xe4\x99\xa4\xb6\xd9\x41\x53\xfb\x48\x07\xf6\xdb\x6f\xda\xb7\x83\x8b\x72\x91\x8b\xd9\x65\xa7\x8e\x3c\x3a\x24\x68\x41\xfa\xcf\x70\xd8\xe7\xc6\x57\x87\xb7\xef\x5c\xdb\xdf\xed\xc3\x1b\xb2\xb7\xeb\x6a\x55\x2e\x4e\x1e\xaf\x96\xa7\x0f\x67\x6c\xf5\x70\x99\x0b\xc9\xbd\x84\x1f\x0f\x0a\xed\x57\xc5\xfc\x57\xec\x3d\x5f\x48\x1e\xf7\x55\xf6\xec\x7c\x71\x29\xf8\x7b\xe3\xab\xc6\x83\x9d\x67\x6b\x92\x73\xf1\xe3\x44\xe2\x91\x1d\x84\xad\x4d\x38\x7c\x5f\x0f\x81\xf0\x53\xb6\x3c\x5f\x54\x7c\xa5\x56\x2e\xe1\xa7\x79\xed\x2b\x64\xf3\xd6\x59\x40\x29\xdc\x67\xac\xbf\xf0\x4d\xb5\x62\xe2\xcb\xc5\xac\x9c\x73\x34\xa9\xa1\xdd\x57\x40\x24\xea\xaf\xa0\x4d\x0b\x30\x53\xdd\x7b\x50\xd5\x0d\xf6\xf7\x85\xa9\x7f\x05\x32\x95\x95\xbf\x3e\x42\xde\xe6\x5b\xea\x79\x42\xe6\xf2\xa7\xfb\xf0\xd3\x37\x8f\x1f\x8b\x9f\x2c\x98\x04\xbb\x60\xba\xbe\x3e\x5f\xad\x96\x27\xac\xe2\x53\xd0\xba\x6a\xc6\x57\x1c\xee\x79\xa2\x05\xdf\x54\x48\x90\xc0\xb2\x8a\xaf\xa0\x11\x74\x63\x1b\xfa\x80\xc0\x89\xac\x7e\x13\x79\x9b\xc7\x0f\x3d\x6f\x4f\x68\xa8\xb7\xf9\x16\x3e\xfe\x2a\x9c\xf3\x7c\x79\xd1\xe2\x87\x66\x5f\x49\xce\xcb\xa1\x7c\xa2\xba\x28\x00\xf8\x8f\x1f\xef\xc1\xd5\x4c\x6f\x0f\xed\x23\x0d\x32\x14\xec\xd7\x19\x87\x14\xf6\x36\x0a\x56\x5d\x3d\x5f\x9c\xb2\x2a\x9b\xf1\xbc\xc5\x77\x0f\x2d\x17\xf3\x4b\xc4\xce\xce\x38\xf4\xbb\x5c\x83\x01\xa2\xf3\x45\x59\x4d\xc5\x44\x33\x63\x6b\x0e\xb3\x4d\xc1\x88\x06\x52\x53\x47\x30\xa9\xaa\xcf\x45\x35\x50\xc5\x50\xcf\xb4\xaf\x67\xac\x5c\x0d\x7b\x06\xfd\x52\xb4\x7e\xa5\x58\x77\xe7\x8e\xa2\xfd\x46\xbf\x03\x96\x96\xa2\xa2\xf8\xbf\xf2\xf7\xb2\x56\x6d\x8d\x57\x31\x06\xbe\x00\x63\x80\x51\xb8\xb5\x85\x46\xcb\x65\xdc\xd2\x55\xf2\x72\x91\xf3\x0d\x3a\x42\x77\xb0\x51\xed\x1b\x3b\xba\x75\x4b\x53\xfe\xfd\x7d\xd9\xcc\xa2\xfc\x80\xe7\x0d\x54\x79\xdb\x57\x76\xa1\x4a\x8f\x85\xc4\x25\x67\xe4\xaf\x77\x8e\x6a\xf1\xdf\xd3\xf8\x85\xf6\x8f\x0c\xfe\xa3\x06\xf4\xf5\xd7\x08\x7b\xb5\x02\xa1\xdf\x94\x0d\x29\x91\xd4\x94\x48\x65\x45\xbf\xa1\x8e\x1e\x36\xcc\xdf\x02\x11\x00\xb4\x09\xa9\x61\x7e\x36\xe3\xd9\xfb\x97\x19\x9b\xb3\xd5\xff\x12\xad\x26\x42\x0e\xcf\x97\xe5\x42\x9e\xa6\x06\x06\x34\x3f\x75\x2d\xbe\xfd\x59\x5a\x7d\xcb\x9c\x6a\xb6\x5a\x5e\xa0\x47\xab\xd5\x72\x35\x81\x5e\xdd\x7a\x22\x42\xa1\x56\x35\xff\xbe\x7f\x0b\xed\xb7\x00\x0e\xaa\xa5\xf4\xac\x13\x1c\xed\x1d\x54\xcb\xbf\x9f\x9d\xf1\xd5\x43\xb6\xe6\x93\x3d\xb4\x2f\x01\x08\x95\x5f\x2c\x2b\xa1\xe0\x40\xac\xe4\xcb\x2d\x51\x58\x77\xf4\xe3\x67\x18\x09\x5a\x3e\x41\x54\x2d\x22\xf1\x96\x1d\x53\xb9\xcd\xa6\x06\x27\xc9\x65\x83\x34\x26\x3a\x03\xbf\xae\xdb\x48\x89\xc2\x52\xe5\x86\x7a\x7b\x7d\xb9\x48\x83\x78\x58\x37\x34\x89\x45\x03\x7b\x53\x29\xe7\xe3\xc7\x54\xf9\x3a\xe5\xe6\xf0\x9d\xf4\xb2\xe2\x68\xcd\xff\xeb\x9c\x2f\x32\x70\x74\x76\x42\x5b\x1c\xb5\xea\xc0\x40\x78\x79\x9a\x2e\xe7\x8d\x21\xd9\x30\x53\xaf\x8b\x99\x0c\x31\x37\x90\xc6\x99\x14\x49\x06\x61\xc5\xa0\x87\x5e\x43\x52\x73\xf0\xd8\x40\x04\xb8\x61\x9d\x08\x7f\x48\x84\x43\xe1\xef\xed\x48\x24\x26\x92\x4a\x4f\x51\xf9\xc8\xeb\x80\xd8\x3f\xb2\x68\x4d\xb4\x45\x67\x1e\x79\x83\xce\x04\x9f\xc4\x51\x4c\x15\xb1\xb1\x24\xf6\xf1\x96\xc4\x62\xb2\x6b\xa7\xda\x9a\x26\xaa\xba\x1d\xed\x5a\x40\xa3\x9b\x00\xa1\x6f\x12\x22\xf4\x57\xe3\x44\x3f\x68\x6a\x80\x8a\xd0\x7d\x18\x5c\x0d\xa2\xa6\xb6\xfe\xe8\xa0\xd2\x54\xad\x7f\x10\x42\x90\xde\x6a\xcb\xc1\xa5\xed\xb1\x8e\x58\x1f\x65\x34\x90\xfb\x47\x0e\xd3\xef\x79\xf4\xb6\xd9\xe7\x0a\x84\x1b\xde\xaf\x38\xcb\x1f\x2e\x17\x55\xb9\x38\x87\xcb\xb3\x20\xfd\xd6\x15\x09\x4a\xbe\x83\xbe\x7f\x7d\x04\x64\x3d\x14\x81\x85\x61\x34\xb8\xf5\xdd\xe2\x03\x9b\x97\x39\x54\x92\xdc\xbe\xa5\xba\xd5\xf0\xbb\x8b\x05\x49\x80\xb0\x50\xf0\xa6\xc1\xf3\x56\x99\x89\x68\xda\xfc\xb8\xbf\x2f\x82\xf1\xda\x43\xf5\xc0\xdc\x94\x6e\x44\x06\x82\xc2\x4b\xfe\xaa\x39\x43\x63\x6d\xff\x71\x43\xd8\xe1\x21\xfa\xae\x40\x17\x1c\x89\x78\xed\xfc\x0c\x89\x48\x75\x8a\xca\xea\xff\xfe\xef\xff\x53\x0f\x4b\x3a\x08\xa0\xf8\x86\xa5\xe7\x83\x8a\xb7\x06\xce\x5f\x6a\xef\x4b\xb0\x82\x49\xab\xe5\xa2\x32\xd6\xd5\x90\xe8\x5f\x7c\xfd\x4b\x60\x50\xdf\xa1\xac\x3e\x41\x54\x5d\x48\x47\x43\xa9\x2b\xce\x16\x6c\x0e\x97\x1f\x1a\x3e\xbe\xe0\x2c\x47\x45\xb9\x5a\x57\x35\x97\xa0\x5b\xbb\x8b\x79\x38\xba\xa1\xc9\x62\x39\x64\xef\x7a\xaf\xd6\x09\x89\xe8\xa6\x92\xbf\xf2\xac\x1a\xad\x0d\x7f\x6b\x5a\x87\x63\x58\x0f\xce\xa3\x5a\xa1\x1e\xd6\xa0\x40\x2c\xe8\xc8\x62\x30\xf7\xfa\xfe\x40\x07\x86\xe5\x34\x03\x72\xee\x34\xd2\x35\x05\x60\x8d\xf6\xb6\xea\xab\xf9\xa8\x6e\x00\xbf\x83\x0a\xd6\x61\xbd\xec\xbb\xdf\xe7\xed\x29\xbb\x44\xe5\x22\x9b\x9f\xc3\x24\x44\x4c\x2e\xf4\x29\x8d\x89\xcb\x8f\x6b\xee\x3c\xda\x81\x3b\xa0\xca\x57\x63\xa0\xa7\xe6\x69\x04\xce\x26\x49\x5c\x3a\x43\x7d\x1b\x43\x3d\x08\x5e\x24\xc3\xc6\xe2\x83\xcf\xc9\xf3\xe1\x08\xdf\xe7\x28\x55\x1c\x7d\x7c\xbd\x1c\x05\x97\x71\x45\xa6\xc7\xc0\x74\x6f\xd3\x67\xbb\xb7\xf1\x1e\xee\xa1\xdf\x80\x23\x13\x49\x83\xfc\xb5\x91\x47\x60\x95\x07\xcc\xa8\x0c\x73\x0c\xec\xe9\x53\x30\xb3\x24\x6a\x7e\x1a\xa5\xf0\xf7\x57\x8f\xef\x50\x94\xc3\x4a\x19\xcf\x1b\xcf\x5b\xbb\x4d\x75\x03\xab\xf9\x0e\x0e\x4d\xfb\x0e\xfe\xe7\x5e\x2f\x26\x51\xb1\x46\x3b\x1a\x4b\xfa\x1a\x78\xdd\x90\x44\xab\x56\x7b\x35\xc0\xa2\x3b\x40\x2d\x28\xd1\x7c\x6c\xbb\xfa\xd3\x09\x77\xda\x75\xa2\xea\xf4\x4c\x8b\x46\x26\xd5\xe9\x19\x3a\xea\x8d\x25\x7b\xe8\x2f\x47\x47\xd2\x29\xf7\xa3\x13\xb5\x89\x51\x9d\x9e\xf5\xe3\x0c\x6d\x82\xde\xd6\xde\xfb\x9c\x8b\x6f\x82\xad\xe8\x08\x08\xbc\xf5\x81\xaf\xd6\xe5\x72\x71\xeb\x2e\xba\x05\x8b\xbe\xb7\xa6\xe2\x57\x49\xcf\xad\xbb\x5a\x54\x08\xbf\xcb\xee\xaa\xdf\xe5\x97\x1b\x5f\x7d\x54\x8b\x74\x2f\x97\xa7\x1c\x3d\x78\xfa\x2d\x4a\xcf\xcb\x79\x8e\x96\x67\x55\x79\x5a\xfe\xc2\x57\xeb\x29\x9a\x97\xef\x39\x5a\x1d\xfc\xbc\x9e\xca\x29\x31\xac\xb4\xaf\xcf\x78\x56\x16\x65\x26\x8c\x37\x2f\x41\xe0\x67\xac\xaa\xf8\x6a\xb1\x06\x78\xd0\xa8\x9a\x71\x54\x2c\xe7\xf3\xe5\x45\xb9\x38\xb9\x2b\xd7\x3c\x85\xfa\xf5\xee\x45\xa2\x5b\xb5\xd2\xdc\x92\x8b\xbb\x9d\x0a\x07\xec\x34\xef\xad\xa2\x36\x57\x24\x45\xd9\x8d\xaf\xa4\xb8\xd4\xa5\xc9\x66\x99\xbb\x3b\x80\x89\x3e\x83\xec\x40\x38\xed\xec\xa2\xb7\x6a\xfc\x17\xed\xfb\xc1\x62\x99\xf3\x57\x97\x67\xbc\x0d\xe6\xda\xb5\x6a\x35\xf1\x28\x17\xfa\xba\xf1\x8b\x72\x71\xb2\xfc\x9f\x2f\xd1\x07\xef\x80\x1e\x78\x30\x3d\x6f\x5b\x68\x77\x49\x1b\x62\x94\x6b\xac\x21\xb1\xd5\xc5\x8c\xcd\x7b\x90\xe2\x03\xef\x8e\x5c\x88\x59\xd5\x67\xa3\xe4\x2d\x46\xf5\xdb\x8c\xad\x9f\x5d\x2c\x9e\xd7\x47\x60\x8e\x54\xa5\x83\xee\xef\x50\xbd\xd9\x22\x81\xac\x71\x92\x29\xb5\xc7\xe8\x56\x97\xfb\x43\xa2\x1c\x2e\x12\xef\x09\xde\xe8\xbc\x7a\xf3\x5e\x26\x30\x14\x35\xe0\x73\x67\xf1\xab\xd7\xaf\x17\xb3\x72\xb1\x14\xbd\x62\xe8\x82\xa7\x48\x5d\x54\x55\xab\xd6\x07\x4a\xa1\x15\x4f\x3e\xde\x50\x57\x54\x61\xdb\xe4\xe3\xf4\xd7\x8f\x6f\xa7\x34\xda\x66\x4b\x64\x70\x63\xf7\xf5\xd3\x27\xc7\x55\x75\xf6\x42\x0c\x19\xeb\xaa\x81\xf6\xd7\xb4\x3c\x91\x87\x59\x0e\x7e\x5e\xff\x75\x1b\xc8\xb7\xce\xd7\x1c\x26\x6c\x59\x75\xeb\xde\x8d\x21\xa2\x6f\xca\x93\x1f\x00\xe0\x3d\xd1\xe1\x9f\xd7\x33\xe1\x94\xcb\x93\xc5\x72\xc5\xef\xce\xcb\x05\xbf\xd1\xa0\xbe\xe0\xa9\xbf\x15\x4a\x21\xa4\x1f\x79\x2a\xc7\x26\x79\xcd\xf8\xd6\xc1\xe1\xbc\x4c\x0f\x05\x08\xe1\x9c\x6f\x1c\x1e\xa2\x7c\xb9\xa8\xd0\xf2\x03\x5f\xad\xca\x9c\xd7\x1b\x0e\xf5\xfe\xc6\x0d\xed\x0a\xb2\xda\x39\x10\x0e\xee\x56\x73\xa0\x01\xf6\x23\x3a\x15\x0e\x24\xca\x6e\x2d\xa1\x20\xb0\x4d\xa6\x57\x01\xe2\xee\xdd\xf8\x68\xe0\x86\x2c\x51\x1b\x5b\x35\xc5\x7f\xbd\x4b\xc8\xc7\xb7\x82\x0b\xd3\x37\x92\x0b\x6f\xf7\x6e\x1c\x1e\xfe\x7f\x68\xbd\x3c\x5f\x65\xfc\x29\x3b\x3b\x2b\x17\x27\x7f\x7f\xf1\xe4\x48\x14\xde\x99\xc3\x21\xd2\x9f\xd7\x07\xa7\xec\xec\xc6\xff\x0b\x00\x00\xff\xff\x8e\xc8\x22\x88\x33\x2c\x06\x00") +var _web3Js = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xfd\x6b\x7b\x13\x39\xd2\x38\x0e\xbf\xcf\xa7\x50\xfc\xdc\x0f\xb6\x89\xb1\x9d\x84\x61\x18\x67\x32\x6c\x08\x30\x64\xef\x81\x70\x01\xd9\xd9\xbd\xb3\x59\xae\x8e\x5b\xb6\x7b\x68\x77\xfb\xd7\xdd\xce\x61\x48\xbe\xfb\xff\x52\xe9\x54\x3a\xf4\xc1\x49\x98\xd3\x26\x2f\xc0\x2d\x95\x4e\xa5\x52\xa9\x54\x2a\x55\x65\xf4\xff\x2d\xa3\x8c\xee\x76\x26\xcb\x64\x5c\x44\x69\x42\x68\xa7\xe8\x25\xbd\xac\xfb\x45\xa5\xe4\x9d\xb4\xb7\xec\x7e\x89\x26\x9d\xf5\xe4\x38\x3d\xe1\xbf\x0a\xf8\x75\x16\x64\x24\xd8\x2d\x2e\x17\x34\x9d\x10\x59\xd7\x6e\x4b\x16\x6d\x3d\x78\x20\x12\x77\x58\x99\xe5\x83\x07\x41\x37\xa3\xc5\x32\x4b\x48\xd0\x49\x7b\xeb\xc3\x2e\x4b\x8f\x64\x5a\x24\xd2\x58\xad\x93\xdd\x84\x9e\x93\x97\x59\x96\x66\x9d\xd6\x7e\x90\x24\x69\x41\x26\x51\x12\x92\x79\x1a\x2e\x63\x4a\xda\xad\x8d\x74\xa3\xd5\x6e\x75\x77\x8a\x59\x96\x9e\x93\x49\x7f\x9c\x86\x74\xb7\xf5\xe6\xf0\xc5\xd1\x4f\x2f\x3f\xbd\x3d\xfc\xf8\xe9\xd5\xe1\xd1\xdb\x17\xad\xde\xe4\x9a\xd5\x17\xef\xb2\xbe\xef\x7e\xa1\x17\x8b\x34\x2b\xf2\xd1\x97\xeb\xeb\x1d\x36\x86\xe3\xe1\x49\x7f\x1c\xc4\x71\x27\xee\x8b\xac\x9e\xec\x7d\x87\xf2\x01\x26\xbb\x00\xb8\x79\x72\x4c\x4f\x76\x44\x57\xf3\x4e\xf2\x2c\x19\xd1\xee\x75\x2f\xee\xe9\x92\xb4\xc7\x71\x77\x2d\xa0\x58\x93\x32\x13\x7a\x11\x35\xc2\xd5\x24\xcd\x3a\x0c\x3a\xdd\x1d\xee\xa4\xdf\x67\xfd\x98\x26\xd3\x62\xb6\x93\x6e\x6c\x74\xf3\x4e\xc6\x10\xaf\xba\x71\xdd\xed\x7c\xd9\x1c\x1d\xab\x2e\x8b\x2a\x7a\x1c\x4b\x3d\xd1\x76\xf7\xcb\x1a\x4f\x90\x9d\xd9\x3d\x5e\x23\xe4\xcb\x1a\x21\x84\xb4\xc6\x69\x92\x17\x41\x52\xb4\x46\xa4\xc8\x96\xb4\xc7\x53\xa3\x64\xb1\x2c\xf2\xd6\x88\x1c\xc3\xb7\x84\x86\xbc\x24\x98\xd3\xd6\x88\xb4\x3e\xa5\xe7\x09\xcd\x5a\x3d\x9d\xc3\x46\xc7\x72\x82\x30\xcc\x68\x9e\xb7\x44\xce\x35\xfc\x7f\x22\xaa\x96\xc5\xe1\x7f\x91\x96\x2e\x8b\xfa\xf6\xd2\x4f\xa8\x88\xd1\xde\xe9\x65\x41\xf3\xed\x2d\x7f\x7b\x12\x48\x61\x7a\x8d\x90\xeb\xde\x9d\x20\xe0\x46\xfd\x51\xc3\x41\xd8\x6b\x86\x80\x95\x51\xfd\x47\x1d\xfa\x38\x4d\x0a\x9a\x14\xb7\x1e\xfc\x9f\x72\xde\xd9\x8c\xfd\x61\xa6\x7d\x12\xc4\xf9\x6f\x37\xf4\x8c\xe6\x34\x3b\xf3\xad\xfa\x3f\xfa\xa4\xe5\xcb\xd3\xf7\x74\x1a\xe5\x45\x16\xfc\x17\x4c\x5e\xaf\xaa\x0e\x7a\x7e\x78\x2b\xbe\x5f\x64\x41\x92\x4f\xbc\xac\xef\xcf\x82\x83\xcc\x22\x85\xd5\x91\x90\xd3\xe2\x43\x35\x49\xdd\x19\x2e\xec\xa6\x7f\x93\x46\xbf\xf2\x04\x04\x4d\x10\x5f\x55\xc1\x22\x8b\xe6\x41\x76\xe9\xed\x47\x9a\xc6\xb5\x93\xb7\x27\xda\xfa\xf3\xa2\xd0\xdc\x83\x2b\xab\x29\x43\xc2\x7e\xe9\x36\xfe\x47\x42\x82\xb7\xf7\x61\x94\xa7\xe7\xc9\x2d\x7a\x1e\x24\x69\x72\x39\x4f\x97\xf9\x0a\x5d\x8f\x92\x90\x5e\xd0\xd0\xd8\xbb\xee\x6c\x62\x75\xe5\xa8\x3b\x66\xed\xe7\x51\x72\x1b\xc6\xbd\xb7\x04\x4c\xbc\x4c\x42\x1a\xb6\x2c\x34\xd1\x33\x46\x08\x7f\x01\x1c\x9d\x46\x61\xd8\x0c\x47\x37\xab\xff\x2c\x88\x97\xde\xee\x2f\xa3\xa4\xd8\xfa\xe6\x49\xf5\x14\xbc\xa5\xe7\xcf\xa3\xdf\x11\xf9\xb7\x5a\x73\xfb\xb3\x20\x99\xfe\x9e\xa4\x73\x27\x94\x53\x52\x37\x92\xea\x2b\xa9\xc6\x8b\x99\x77\x7c\x37\xaa\x45\xd0\xda\xc9\xda\xda\x75\xef\xcb\xf5\x49\x6f\xeb\x77\x3b\xf4\xff\x85\xce\xbc\xbf\x93\xec\x38\x59\x26\xe1\x8d\x49\xe5\xd6\x1b\xd7\xfd\xb1\xf7\xcf\x7d\xec\xbd\x3f\xf4\xfd\x91\xcf\x1c\xde\xc1\x8b\xf3\xc2\x1f\x4d\xda\xfc\xba\x9b\xb9\xde\xab\xb6\xef\x6c\xaf\x5a\x75\xde\x27\x59\x3a\xbf\xe5\xb4\x17\xe9\x2d\x8f\x9a\xb7\x13\xf8\x7e\xdf\x75\xf3\x47\xc0\x5f\x94\x84\x51\x46\xc7\xc5\x81\x77\xcf\x5c\xa1\x27\xb7\x9b\x88\x68\x1c\x2c\x3e\xfe\xae\x93\xe1\xc7\x64\xb3\xd3\x2e\x5d\xa4\x79\x54\x75\x50\x5f\x04\x97\xc1\x69\x4c\x4d\xa1\xe0\x77\xe1\x4a\x65\x34\x77\x27\xc7\xaf\xdb\xd1\xc0\x9e\x1c\xef\x0b\x13\x9f\xbf\xfd\x49\xe6\x4e\x90\x54\x52\x77\x33\x3a\xfb\x1d\xd0\xff\x87\xc5\xfa\x5d\x9c\x1f\x6f\xcc\x27\xbf\x36\xd6\x6d\xa6\x77\x8f\xf6\x86\x68\xbf\xf5\xc6\xf5\xb5\x67\xf6\xc0\xb3\xa5\x55\xc9\x71\x8f\x9b\xc8\x71\x60\xbc\x41\x76\xa5\x85\x43\xa7\xdd\x1f\x4c\xd2\x6c\x1e\x14\x05\xcd\xf2\x76\x77\x07\x00\x3e\xa4\x71\x14\x46\xc5\xe5\xc7\xcb\x05\x35\x61\x59\xfb\x0c\x6a\x6d\xf0\xf0\xe1\x1a\x79\x68\x40\x0a\x9d\x3b\x89\x72\x12\x90\x45\x96\xa6\x0c\x98\x14\xb3\xa0\x20\x19\x5d\xb0\x43\x56\x52\xe4\x44\xcc\x1d\x61\x99\xac\x86\x83\x82\xcc\x83\x62\x3c\xa3\xf9\x88\x7d\x8a\x6c\xf4\xf3\xf8\x04\x7f\x3c\x36\xbe\x4e\xcc\xcc\x6d\xeb\xfb\xe4\xf8\xc9\xc9\xf1\x49\x8f\xf4\xfb\xfd\x35\xf2\x70\xe0\x8c\x4d\xf6\x78\x97\x28\x6b\x9a\x4e\x57\x4c\x71\x31\x8b\xf2\xfe\x27\x58\x18\xaf\x24\x82\x18\x60\x9f\xa3\xeb\x80\x65\x1c\x24\xc5\x0e\x02\xe6\xfb\xb6\x0f\xfa\x10\x72\x44\x73\x3b\x6b\xd7\x3b\x6b\x6b\x9e\x7e\xf4\x17\x59\x5a\x70\xac\xed\x92\x84\x9e\x1b\x7d\xed\x7c\xb9\xee\xee\x54\x97\xea\x83\xf4\x92\x2d\xc7\x45\xca\x1a\xf7\xc0\xd6\xb5\xdb\x8f\x72\x31\xe7\x1a\x21\x8c\x1c\x25\x52\x84\x5d\xcb\xfa\x3a\x4b\xec\xc3\xbc\x75\x06\x02\xdb\x9d\x7f\x1f\x77\x8e\x87\x8f\xbe\x3b\x79\xd8\xfd\xf7\x49\xf7\xd9\xa0\xcb\xc7\x69\x1e\x1c\x4a\xbb\x75\xdd\xfb\xd2\xc2\xa4\xd8\x1a\x7d\xd7\x6b\x71\x7a\x6b\x8d\x36\x1f\x5f\x9f\xf4\xbe\xf9\x9d\xc9\xfb\x79\x9a\xc6\x35\xb4\x7d\xca\x40\x4a\x08\x9b\xe5\xc9\xff\x39\x95\xc2\xaf\xc7\xfa\xe7\x09\x4a\xde\xc6\x1f\x75\x64\x0c\x3d\xbb\x29\x0d\xb3\xc2\xab\x10\x31\x87\xb7\x29\x98\xa5\xae\x48\xbe\x66\x91\x0a\xda\xe5\x2d\x56\x95\xbd\x09\xd5\xfe\x87\xa1\xd6\xa4\xd9\x87\xff\xd3\x88\x68\x45\x7f\xea\x29\xf6\xc9\xef\x4d\xb1\x6c\x0f\x53\x24\x5b\xf8\x69\xb6\x98\x51\x02\x9b\x1d\x10\x6e\xdf\x47\xb9\x2c\x57\xfd\x10\x74\x09\x3f\x1f\xa3\xdf\x27\x38\x63\xdb\xf8\x32\xe9\x97\x88\xad\x55\xfd\x7c\x6a\xd4\x23\x8a\x7a\xa8\x1c\x3a\x79\x63\x32\x67\xa5\x57\xa2\x73\x5e\xc0\x21\x74\x96\xbc\x2a\xa5\x9b\x65\xaa\x48\x9d\x37\x5a\x59\xfa\x66\xc4\xce\x2a\xe1\xa4\xfe\x65\xb3\x77\xdd\xbd\x19\xe1\x8b\xde\xd5\x53\xfe\xb7\x4d\x28\x7f\xf0\x10\x3a\xfc\x71\x16\xe5\x64\x12\xc5\x94\x51\xea\x22\xc8\x0a\x92\x4e\xc8\x39\x3d\xdd\xee\xff\x92\xf7\xd7\x00\x44\x7c\x31\x80\x49\x46\x29\xc9\xd3\x49\x71\x1e\x64\x74\x44\x2e\xd3\x25\x19\x07\x09\xc9\x68\x18\xe5\x45\x16\x9d\x2e\x0b\x4a\xa2\x82\x04\x49\x38\x48\x33\x32\x4f\xc3\x68\x72\x09\x75\x44\x05\x59\x26\x21\xcd\x80\xe0\x0b\x9a\xcd\x73\xd6\x0e\xfb\xf8\xf1\xed\x11\xf9\x89\xe6\x39\xcd\xc8\x8f\x34\xa1\x59\x10\x93\x77\xcb\xd3\x38\x1a\x93\x9f\xa2\x31\x4d\x72\x4a\x82\x9c\x2c\x58\x4a\x3e\xa3\x21\x39\xbd\x14\x54\x44\xc9\x2b\xd6\x99\x0f\xa2\x33\xe4\x55\xba\x4c\xc2\x80\x8d\xb9\x47\x68\x54\xcc\x68\x46\xce\x68\x96\xb3\x19\xda\x96\x6d\x89\x1a\x7b\x24\xcd\xa0\x96\x4e\x50\xb0\x31\x64\x24\x5d\xb0\x82\x5d\x12\x24\x97\x24\x0e\x0a\x5d\xd6\x45\x81\x1e\x69\x48\xa2\x04\xaa\x9d\xa5\x72\x65\x47\x05\x39\x8f\xe2\x98\x9c\x52\xb2\xcc\xe9\x64\x19\x73\xc1\xf1\x74\x59\x90\x9f\x0f\x3e\xbe\x3e\x3c\xfa\x48\xf6\xde\xfe\x8b\xfc\xbc\xf7\xfe\xfd\xde\xdb\x8f\xff\xda\x21\xe7\x51\x31\x4b\x97\x05\x61\x12\x25\xd4\x15\xcd\x17\x71\x44\x43\x72\x1e\x64\x59\x90\x14\x97\x24\x9d\x40\x15\x6f\x5e\xbe\xdf\x7f\xbd\xf7\xf6\xe3\xde\xf3\x83\x9f\x0e\x3e\xfe\x8b\xa4\x19\x79\x75\xf0\xf1\xed\xcb\x0f\x1f\xc8\xab\xc3\xf7\x64\x8f\xbc\xdb\x7b\xff\xf1\x60\xff\xe8\xa7\xbd\xf7\xe4\xdd\xd1\xfb\x77\x87\x1f\x5e\xf6\x09\xf9\x40\x59\xc7\x28\xd4\x50\x8f\xe8\x09\xcc\x59\x46\x49\x48\x8b\x20\x8a\xe5\xfc\xff\x2b\x5d\x92\x7c\x96\x2e\xe3\x90\xcc\x82\x33\x4a\x32\x3a\xa6\xd1\x19\x0d\x49\x40\xc6\xe9\xe2\xb2\xf1\x44\x42\x65\x41\x9c\x26\x53\x18\xb6\xa2\x32\x42\x0e\x26\x24\x49\x8b\x1e\xc9\x29\x25\xdf\xcf\x8a\x62\x31\x1a\x0c\xce\xcf\xcf\xfb\xd3\x64\xd9\x4f\xb3\xe9\x20\xe6\x15\xe4\x83\x1f\xfa\x6b\x0f\x07\x92\xd9\xfe\x0d\xc8\x76\x9c\x86\x34\xeb\xff\x02\x2c\xf2\x6f\xc1\xb2\x98\xa5\x19\x79\x13\x64\xf4\x33\xf9\xdf\xb4\xa0\xe7\xd1\xf8\x57\xf2\xfd\x9c\x7d\xff\x8d\x16\xb3\x90\x9e\xf5\xc7\xe9\xfc\x07\x00\x0e\x83\x82\x92\xad\xe1\xe6\x37\xc0\xf0\xea\xb7\x82\x0a\x01\x16\x95\x11\xf2\x98\x6f\xef\x10\x92\x02\x02\x66\xbb\xa0\x0f\xf2\x20\x29\x4c\xc0\x28\x29\x7c\x70\x47\x0e\xe0\xb2\x04\xf2\xc5\x65\x12\xcc\xa3\xb1\x64\xe3\xa8\x44\xc8\x73\x80\x47\xf9\x4a\x7e\x28\xb2\x28\x99\x9a\x65\x72\x48\xf3\x41\xbf\xa7\x81\x35\xc6\x8c\x06\xde\x31\x1e\xb9\xa0\xcb\x32\x58\x4f\xb7\x55\x7f\x01\x38\xca\xc5\x00\x0d\xce\x9c\xa3\x2a\x7a\xb0\xc3\x0a\x3e\x2d\x2d\xc4\x51\x7e\x5f\x55\x01\xdb\x08\x07\xbe\xba\x52\xa7\x47\x52\x02\xbd\x97\x65\xc1\x25\x07\xe7\x4c\xdc\x12\x05\xf6\x19\x7d\x22\x09\x40\xac\x24\xce\x21\x42\x52\xa4\x84\x26\x8c\x86\x07\x21\x65\xff\xa9\x56\x18\x33\x0e\x38\x9b\x64\x5c\x49\xc8\xb5\xe6\xc6\xcc\xeb\xc6\x23\x66\x60\xb9\xb9\x33\x43\x12\xd9\x85\x1a\x72\xa3\x8b\xc0\xfb\xe7\xb4\x98\xa5\xa1\xa7\x5b\x5c\xb9\x9e\x66\x73\xc2\x25\x97\xd4\x98\x91\x35\xc2\xd7\xa0\x28\xfe\x49\xcc\x8c\xc8\x22\x7f\x83\xde\x93\x2f\x9c\x78\xae\x95\x58\xfe\x37\x8e\xf9\x9c\x7c\xc1\x95\x5d\x43\x16\xbc\x55\xc8\xc9\x17\x78\xd7\x70\x4d\xc4\x67\xc4\x78\x03\x97\x88\x18\x19\x42\x5f\xd8\x4e\xc4\xd8\x3d\x20\xc4\x40\x06\xda\xa9\x71\x97\x1c\x1c\x49\x14\x31\x6c\xe6\xa6\x78\x87\xb0\xd6\x9f\x44\x71\x41\xb3\x0e\x2a\xdb\x45\x3a\x08\x41\x45\x85\x10\x0a\x24\x11\x80\x4e\xa1\x7b\x3c\x3c\xd9\xe1\xfc\x33\x9a\x90\xce\x3a\x6e\x04\xd7\xc1\x1f\x68\xf0\xa7\x1c\xed\x28\x39\x0b\xe2\x28\xd4\x34\xc0\x6a\x5c\x1f\x91\x36\xd9\x20\xb8\xf2\x35\x2c\x6b\xe0\x9a\x4d\x0a\x2c\xa1\x34\xb2\x88\x83\x28\xe1\xf4\x65\x4d\x23\x07\x78\x27\x72\xca\x67\x51\xa4\x1f\x9e\xfe\x42\xc7\xc5\xb5\x55\xa1\x9c\x64\x5d\x8e\x57\x1b\x5a\x70\xe5\x53\x87\xba\xe1\xcc\x5c\x8f\x97\xb7\x04\x2e\x98\x34\x54\x2c\xef\x1c\x33\xe0\x93\x1e\x39\x06\xf0\x93\x6e\x33\xd4\xc4\x51\x0e\x12\x10\x5f\x7c\xe5\xd8\xc9\x31\x1a\x80\x05\x70\xec\xf8\xd2\x17\xba\x40\x19\x62\x9c\x66\x1b\xe1\x26\x77\x97\xbe\xc0\x4e\x5e\x46\xdf\xb9\x24\xf0\x29\x2d\xf0\x0a\xcc\x05\xe7\x10\x24\xcb\x8a\x89\xbe\xb1\x12\x46\x0d\xfd\x79\xb0\xe8\x94\xf1\x58\xd0\xca\x79\xd6\x88\xc1\x3b\x79\xcd\x1d\xde\xd3\x63\x28\x72\xc2\xd9\xb3\xfc\x52\xab\x08\xf5\x47\xec\x53\x87\x93\x49\x4e\x0b\xa7\x53\x19\x0d\x97\x63\x8a\xfa\x15\x8c\xc7\x3d\x52\xd3\x39\xc0\x4e\x11\x14\xd1\xf8\x5d\x90\x15\x3f\xc1\x4b\x22\xab\xe6\xbe\x9d\xdf\xf1\xf4\x53\xd6\x95\x31\xa6\x44\xc3\x0f\x6e\x95\x6f\x82\x62\xd6\x9f\xc4\x69\x9a\x75\x3a\x4e\x8b\x1b\x64\x7b\xb3\x4b\x06\x64\x7b\xab\x4b\x1e\x92\xed\x2d\x31\x68\x84\xbe\x60\x3c\x26\x1b\xa4\xa3\x36\x1d\x03\xeb\x25\x28\x24\xcf\xd0\xde\x45\xc8\xf6\x16\x19\x19\x09\x25\x9d\x95\xa8\xef\x91\x21\xc6\x7e\x46\xf3\x65\x5c\x48\xea\xe1\x33\xf8\x66\x19\x17\xd1\xcf\x51\x31\xe3\x73\x22\x29\xd0\xe8\x5b\x4f\xd1\x51\xcf\x9c\x41\x59\xb9\x18\x21\xaf\xdf\x3c\xf1\xf9\x49\xdf\x6a\xd5\xb7\x06\x1a\xf6\x00\xad\x11\x35\xbc\x56\x6b\x47\x2f\x1c\x1a\x4f\xc4\x88\x45\x67\xc5\xae\x90\x66\x2f\x83\xf1\xac\x63\x33\xa6\x08\xd3\x16\xe3\xfa\xa5\xf3\xa5\xe7\xea\xa4\x8b\x0b\x71\x84\x40\x57\x36\x5c\x6d\x67\xc7\xec\xbe\x5c\x47\x88\x08\xd5\xda\x65\x54\x4c\xe3\x89\x00\xb1\xe7\x08\x3a\xe0\x76\x49\xe2\x09\x3e\xec\xc9\xc2\x4d\x98\x4b\x71\x63\x97\x50\xf1\x0c\x8f\x0c\xc8\x96\x06\xbd\x26\x34\xce\xa9\x35\xbc\xc1\x80\x84\x69\xd2\x2e\x48\x10\x86\x44\x94\x2a\x52\xb3\xca\x3e\x89\x8a\x76\x4e\x82\x38\xa3\x41\x78\x49\xc6\xe9\x32\x29\x68\x58\x82\xa5\xaf\x34\xce\x6b\xbd\x08\x07\x03\xf2\xf1\xf0\xc5\xe1\x88\x4c\xa2\xe9\x32\xa3\x84\x1d\xd8\x12\x9a\xb3\x13\x20\x3b\xa5\x5d\xe6\x26\xb3\xfa\x2d\x88\xe4\x8f\x33\xc9\xe6\x64\x50\x8c\x40\x89\x95\x92\x65\xae\xd0\x9a\xd1\x49\x00\xea\x98\xf3\x59\x1a\x53\xde\xc3\x28\x99\xae\xd7\x30\x82\x0a\x1e\x60\x73\x7e\x31\xe8\x1e\x49\x9d\x95\x6f\x2c\x72\x39\x27\xb5\xa2\xbe\x67\x8b\xeb\xb8\xaa\x31\x44\x40\xbc\x61\x72\x1e\x68\xb2\xce\x69\xe1\xcc\x29\x27\xab\xb7\xc1\x9c\xda\xfb\x90\xce\xc1\x72\xa6\x5b\xd6\xb3\xf9\x54\xef\x67\xba\x62\x4f\x9d\x8a\x2f\x0a\x0c\x6a\xa9\x56\xfe\x55\x0c\x5b\x56\xb2\xc8\xe8\x59\x94\x2e\x73\xd5\xa1\xad\x1d\x86\x92\x28\x21\x51\x52\x38\x25\xea\xf0\x8f\xfa\xeb\x6b\x90\xfd\x4d\xd2\x8c\xc0\x23\xe1\x88\xec\x92\xcd\x1d\x12\x91\xef\xe5\x00\xe4\x7b\x61\x12\x6d\x6c\x94\x15\x67\x7f\x56\x9f\x37\x76\xc9\x46\x47\xe2\x20\x22\x8f\xc8\xe6\x09\x93\xf0\xc9\xd5\x15\x19\xee\x94\x56\x52\xc1\xca\x05\x3d\x6c\x90\x88\x3c\x2c\x9b\xb9\x0d\xbb\x17\x4c\x38\x28\x63\xfb\xf2\xef\xda\x49\x35\x53\xae\xbb\x9d\xae\x35\x85\x83\x01\x99\x44\x59\x5e\x10\x1a\xd3\x39\x4d\x0a\x76\xbe\xe2\x68\xea\x91\xfc\x73\xb4\x20\x51\xb1\xca\x94\x1b\xd8\x1f\xfa\xb0\xcf\xf0\x57\x39\x03\xf0\x74\x3e\x0c\x23\xd6\x48\x10\xab\x45\x2e\xf0\xe9\xf0\x1f\x17\xdf\x7e\xbe\xa8\x49\xa7\x84\x41\x1c\x47\x64\x83\x6c\x9e\x48\x3e\x41\x36\x88\xd3\x0d\x0f\xda\x6b\x11\x6c\x31\x3f\x0f\xa4\xd8\x2a\x3d\xb4\xcf\xa9\xe2\xc6\xac\xe7\x0f\xcd\x54\x98\xb0\x65\x62\xea\x96\x8b\xbf\x86\x32\x49\x19\x43\x1a\x56\x31\x24\xd2\x88\xa6\x6b\x39\xca\x60\x40\xc6\x41\x3c\x5e\xc6\x41\x41\xa5\xe0\xc3\x8e\x7c\xa2\x2f\x24\x2a\xe8\xfc\x16\xec\x88\xb1\xa2\xe3\x3f\x11\x53\xea\xda\xb0\xd7\x2b\xed\x2b\xb7\x9c\x90\xdf\x8f\xc1\x60\xe6\xf2\xd5\x79\x0b\x71\xb4\x45\xa2\x1f\x35\xda\x10\xa1\x8b\x14\x37\x93\x69\x85\xc6\x88\x43\x36\xd6\x18\xc9\x74\x75\xab\xa9\x54\x22\x7e\x5d\x52\xb9\x1e\x04\x35\xec\x11\xff\xa0\x7e\x9f\x8e\x08\x15\xd3\x3a\x22\x0e\x0d\xb2\x4d\x13\xb4\x54\x2a\x89\x4a\x10\x52\xa6\x23\x2a\x47\x88\x28\x01\x27\x0c\x68\x4d\x23\xa6\x5a\x43\x84\x87\xe8\x3b\x1d\x1b\xb8\x59\x5d\x41\x24\x4b\x71\x2a\xc6\xf0\x9c\x88\x73\xef\x29\xdc\x3a\xee\xdf\xb1\x46\x89\x0f\xb9\x03\x23\x93\xeb\x4b\xab\x45\x0c\xbd\x88\xac\x51\x6b\x98\xaa\x54\x0e\x7a\x54\xb5\x7a\x06\x8c\x51\xce\x81\x58\x99\xbb\x1e\x69\x13\x75\x94\x3a\x89\xfa\xe4\x60\xd1\xb5\x52\x26\x39\x18\x90\x7c\x39\xe7\x37\x74\x9e\x5d\x4a\x88\x88\x0a\x5e\x54\x77\x1c\x9d\x30\xae\xa8\xbe\x60\x4b\xf2\xf1\x1f\xd9\xbc\x89\x08\x29\x6d\x3a\x28\x18\x0c\x48\x46\xe7\xe9\x19\x5c\x63\x92\xf1\x32\xcb\x98\x7c\xaa\x84\xd3\x14\x92\x45\x37\xa3\x1c\x7a\xee\xe9\x6d\xbe\x8a\xc6\x4f\x22\xb3\xb1\xe6\xcf\x18\x19\x79\xe4\xd4\xdf\x98\xd2\x3e\x58\xeb\xb0\xe4\x5a\xc7\x7b\x6a\x95\x3c\xce\x43\x65\x85\x75\xe5\x20\xc9\x8a\xed\x60\xf8\x92\xc4\xbc\xbf\xe0\xbd\x65\x6d\x8d\xc5\x2d\x13\x36\xb5\x80\xde\x77\xb8\xbd\xaa\x6d\x82\x21\xae\x45\x3b\xdd\x9e\x37\xfb\x79\x9a\xc6\x65\x79\x4c\x08\x29\xc9\x3a\xaa\xc8\xc3\x97\x9b\xa5\xcd\x56\x65\x72\x2e\x5c\x96\xfb\x9e\x06\xa5\x3d\x3e\xe2\x99\x6b\x8c\x20\x5c\xfb\x0d\x40\x9d\xb2\xd9\x90\x86\xb3\xa3\xc7\xbd\x16\xbf\xfb\x6d\x8d\xbe\x81\x9f\xac\x6f\xad\xd1\x13\xf6\x1b\x5f\xc7\xb6\x46\x4f\x7b\x3e\x5b\x8f\x28\x29\x5a\xa3\xcd\x21\xfb\x99\xd1\x20\x6e\x8d\x36\xb7\xd8\x6f\x7e\x2b\xdb\x1a\x6d\x6e\xb3\xaf\x25\x87\x82\x06\x96\x02\xec\xc9\xf5\x49\xef\xe9\x6f\x69\x17\x55\x73\x0d\x7d\x33\x6b\x22\x5c\xc9\x2a\x46\x45\x66\x39\xdb\xb6\x08\xe7\xae\x68\x62\xe4\x2f\x5a\x61\x69\x64\xf6\xa4\x49\x5d\xb7\xb0\x3b\x2a\x31\x36\x6a\xd4\x28\xba\x12\xf7\x4e\x97\x64\x3b\xd9\x92\x36\x30\x61\xb2\x86\x5d\x6f\xc9\xf4\xdd\xbd\x25\xd3\xbd\x25\xd3\x7f\x8b\x25\x93\x5e\x08\x77\x65\xce\xf4\x3c\x9a\xbe\x5d\xce\x4f\x81\x15\x2a\xee\x7c\x1a\x4d\x13\x48\xec\xff\xa2\x38\xf9\xb2\x88\x62\xd3\xbe\xa6\x3f\x80\x34\xfe\xaf\x04\x1b\x7b\x41\xc6\x69\x32\x89\x1c\x63\x20\x79\x32\x43\xbb\x02\x9c\x5d\x60\x5b\x90\x03\xe7\xbc\x3a\x27\xc0\xef\x09\x3c\xd8\x60\xe7\x2c\xc6\xb7\xb4\x95\x2c\x2c\x05\x36\x37\xa0\x9c\x79\xc8\x70\xcc\x21\xa3\x9c\x24\x74\x1a\x14\xd1\x19\xed\x49\x4e\x04\x17\x47\xc5\x79\xda\xce\xc9\x38\x9d\x2f\xa4\xb4\x0a\xa5\xd8\xdc\xaa\x92\x93\x38\x0d\x8a\x28\x99\x92\x45\x1a\x25\x45\x8f\x5f\x87\x32\xb2\x0f\xd3\xf3\xc4\x3a\xd3\x99\x6a\x12\xf7\xf8\x76\xc5\xb1\x7c\xa5\xf0\x7d\x2d\xc7\xc2\x96\x52\x42\x69\x08\xa7\xe8\x53\x3d\xc7\xa1\xdf\x18\x06\x90\x76\xad\xec\x7c\xcc\x76\x0d\x06\x0c\xf5\x4b\x2e\xac\xda\xed\xf3\xb9\xe8\x8c\xfb\x2f\x3f\xbe\xfe\xf4\xfc\xe0\xc7\xb7\x47\x6f\x9e\xbf\x7c\xff\xe9\xfd\xe1\xd1\xdb\x17\x07\x6f\x7f\xfc\xf4\xe6\xf0\xc5\x4b\x74\x86\x53\x9a\x38\x98\xc9\xfe\x22\x08\x7f\xa2\x93\xa2\xc3\xbf\x8a\xf4\xe3\x79\x9a\xef\x2b\x2c\x8a\x36\xfb\x45\x2a\xc4\xa5\xcd\x27\xdd\x1e\x79\xf2\xd8\xbc\xe1\xc1\xbb\x25\x0c\xa7\xc3\x1b\x31\x0d\x30\xcc\x89\x97\x87\xdf\x12\x9c\x3f\x57\x67\x63\xf3\xd0\xbc\x2a\x0e\x5d\xa9\xc3\xc0\xa2\x07\x21\x45\xfa\x9a\x5e\xc8\x71\xe7\xcb\xd3\xbc\xc8\x3a\x5b\x08\x7f\xb1\x75\xb5\xcf\x8b\x4b\x2d\xf7\x06\x79\xb2\xdd\x25\x03\x8c\x22\x1b\xdd\xef\xa3\xe9\xac\x10\xc5\x7a\x24\x26\x0f\xbf\x32\x3e\xc5\x0e\x7c\xa7\x68\x2d\x95\xe9\x6e\x8d\x5d\x79\x3c\x33\xd1\xaa\xb4\x73\xbf\xdb\x0c\x58\x6a\x53\xde\x58\xb7\xcf\xd7\xfc\x06\xa9\x9f\xa0\x3a\x4e\xc7\x25\xf9\xf2\x15\xf1\x41\xe6\xdf\x76\xee\x94\x71\x67\xf3\x59\x9b\x64\xe9\xfc\xa8\x98\x3c\xbd\x9f\x38\xcf\xc4\x89\x77\x46\x65\x8c\x4c\xbc\x42\x92\x93\xc6\xbe\x69\x90\xac\xce\xc8\xec\x27\x47\xe5\x73\xd6\x1e\xde\xee\xaf\x4d\x36\x44\xf5\xe4\x19\x21\xed\xcd\x36\x19\x91\xf6\xb0\x7d\x7b\x1e\x55\x87\x49\x76\x62\x65\xa5\xfe\xc1\xe0\x72\xc2\x04\xe3\xf9\x32\x2e\x22\x2e\x54\x9e\x5e\x92\xad\xff\xcc\x99\x78\xae\x6c\xe8\x02\x56\x73\x41\xa7\x34\xab\xd8\x4a\xde\x8b\x5a\xeb\xf6\xef\x55\x67\x44\xd8\x32\x97\xcc\x88\x40\x93\x45\x7d\x0c\x6b\xaa\x45\xb5\xb9\x46\x73\x9a\x5b\x59\x5b\xdd\xfe\x22\x3d\xef\x6c\x6e\x3d\xed\x76\x4d\x94\xee\xcf\xe8\xf8\x33\x89\x26\x06\x4e\x91\x58\x64\x21\x22\x8f\xa6\x09\x0d\x0f\xf2\xb7\x3a\xdb\x51\x44\xab\x3a\x66\xf4\x42\xf4\xd8\x44\x86\x24\x5a\x38\xf4\x41\xdb\x85\x29\x89\xa5\xec\xc8\x72\x1e\x31\x31\x3c\x88\x73\x6d\xb5\x6c\xb7\x5e\x8b\x2f\x1f\x86\x24\xbb\x19\xf6\xc8\x66\xb7\x47\x36\x9f\x20\x79\x64\xab\x6b\xe4\x76\xc9\xee\xee\x2e\x23\x59\x2f\x15\x66\x8c\x7d\x3c\x0a\x62\xe8\x14\xe1\xaa\x03\x7d\xe1\xc1\x45\x4d\x97\x88\xb8\x22\xc1\x16\x02\x0d\xf2\x70\xec\x60\x19\xce\xb4\x60\x58\xd1\xae\x12\x0e\x61\x59\x44\x53\xc2\xe5\x74\x8b\xde\x54\x17\x0c\xfc\x19\x46\xb1\x0c\x98\xcf\xe3\x2e\xef\x0d\xd2\x65\x76\xba\xe4\xea\x8a\xb4\x86\x2d\xa1\x23\x1e\x0c\xc8\x58\x51\x11\x13\x9e\xe5\x44\xaa\xd6\x39\x50\x54\xf0\x89\x56\x92\xb6\x2b\x64\xcb\xfb\x5b\x6b\x9e\xc5\xdc\x7a\x54\x90\x9e\xf9\xe5\x53\x3a\x8f\x92\xa5\xbd\x0a\xda\x93\x5b\xfe\xb5\xa1\x6e\x59\xf9\xa6\xba\x1e\x6b\xd0\xa1\x1b\x50\xd0\xb2\x9a\x84\x8e\x2a\x69\xc8\x47\x3d\x74\x25\xf2\x11\xcd\xbb\x84\x73\x74\x17\x94\xf3\x75\x50\x26\x58\x7e\x19\xca\x1c\xde\x5d\x8b\x32\xc0\x18\x12\x89\x4d\x14\x89\xe6\x5c\x14\x39\xcc\xdc\x67\x71\x6e\x2d\x46\x01\xd3\x0f\xa3\xb3\x28\xa4\xe1\xf3\xcb\x0a\x1e\x7e\x13\x6a\xaa\xc1\xcd\xd1\x5d\x23\x67\x59\x8a\x9d\xa3\x95\xd1\x73\x74\x1b\xfc\xb8\xb7\xb0\xbc\x6a\x85\xa2\x32\x89\x4b\x3f\x98\x6e\x8c\x17\xb9\xb3\x99\x73\x51\x8a\x23\xd1\xb4\x8b\x22\x47\x3e\xf3\x61\xc8\xb3\xbc\x60\xbf\xba\xa5\xc0\xb6\xd9\x26\xcf\xf8\xd6\x2c\x3c\x63\xac\x86\xcd\xd2\x93\x23\x7a\x97\x5b\xb1\xf7\xc5\x74\xa2\x11\xc7\x24\x88\x8a\xb3\x8d\x23\x7a\x24\xc1\x9c\xf2\x07\x3e\xec\x97\x25\x82\x09\x18\x56\xa7\xaa\xc1\x83\x79\xe7\x10\x0a\x6d\xf4\x08\x56\x96\xb3\x42\xe2\x89\x35\xd9\x25\x65\x2f\x75\x1f\x76\x07\xe8\x48\x93\x47\xbf\x0a\x9e\x98\xc3\x2d\x95\x28\x7f\xbc\x79\x62\x8a\xc2\xed\xe1\x05\x13\x99\xdd\xc9\xed\xe7\x71\x34\xa6\x4c\x32\xd9\x22\x0f\xa1\xba\x15\xe9\xbc\x66\x66\xf0\x29\xfc\xce\x26\x68\x55\xf4\x97\xaa\x02\x9c\x4d\x46\x1d\x11\x2d\x3e\xc0\x11\x27\x2e\xc1\x6c\xcc\x3d\x79\xdc\x15\x7b\x78\x91\x0a\xf8\x2e\x79\x28\x4f\x95\xbe\x19\xb0\x2a\xe2\xd2\xe1\x93\xc7\x3d\xd1\xfe\x6a\x53\x50\x71\x2a\xe7\xc3\xf7\x1c\xcb\xef\x14\xfb\x41\x3e\x8e\xa2\x2a\xfc\x7b\x8e\xf3\xbf\x21\xe6\xa5\x56\x07\xb4\x03\xcd\xf0\xbf\xda\x04\x68\xf7\x34\x65\x33\xb0\xa7\x1d\xd8\x94\x4c\x41\x29\x6f\x2f\x41\xb9\xaa\xd0\xc5\xb6\xcf\x81\xcd\x0a\xd2\x94\x81\xbb\xd6\xf0\xa2\x45\x36\x88\x38\xe3\x00\xda\xf9\x6f\x65\x56\xf0\x78\xd8\x23\x38\xa9\xcc\x67\xc0\x17\x69\xfa\x81\xce\x9a\x23\xeb\xbb\x67\xc3\xc0\x8a\x1d\x39\x29\x0e\x1c\x5e\xe0\xa3\xb2\x0c\xa7\x14\x47\xe6\xc8\x4d\x72\xfb\x91\xa6\xf1\xc8\x4e\x70\xa0\x98\x04\x32\xb2\x13\x30\x94\x12\xcb\x46\x76\x82\x0b\x75\xe4\x80\x1d\x79\xe1\x70\xa3\x3a\xc5\x53\x9f\x0b\x78\xe4\x87\xc4\x83\xd5\x29\x1e\x38\x8c\x6d\x94\xe4\x42\xfa\xa6\xc7\xcd\x71\xcb\x99\x13\x84\xd3\x5c\x58\x41\xf5\x23\xef\xba\xbb\x96\xd7\xba\xe6\xe5\x50\x6b\xb4\xf9\xb4\xd7\x32\x2f\x95\x5a\xa3\x2d\xb0\x60\x80\x85\xd1\x1a\x6d\x6e\xf6\x5a\xf8\x6a\xaa\x35\x32\x3f\xaf\x4f\x7a\x9b\xc3\xdf\xd9\xa5\xcb\x01\xb7\x8d\xaf\xf0\x41\x14\x25\x45\x99\x0b\x22\x71\x7b\x15\x25\x05\xf7\xce\xc2\x7e\x3c\x56\xbf\x4e\x74\xe2\x36\xfa\x6d\x39\x6f\x89\x92\x82\xbb\x6e\x89\x92\xe2\xc9\x63\x05\xf6\x54\x57\xb4\xf5\xcd\x93\x92\xba\x18\x7c\x8d\x2b\x23\xfb\x68\xf8\x15\xbd\x71\x01\xb8\x6d\x86\x70\x90\x14\x2b\x5a\x5e\x18\x25\x2a\x0c\x2e\xa0\xb9\x8a\x92\x37\x32\xaf\x88\x92\x42\x8a\x8a\xcf\x6e\xe4\xd2\x85\xf7\xaa\xde\x0c\x62\xb3\x51\x14\xbb\x7b\x3b\x88\x7b\x3b\x88\x3f\xaf\x1d\x04\xd1\x86\x10\x5c\x54\xba\x23\x1b\x88\x06\xa6\x0d\x36\xab\xe7\xa6\x0b\x29\x18\xa4\x6b\xcf\x1d\x7d\x8f\x84\x7a\x3e\xa3\x89\x7a\xaf\xd8\xe3\xb6\xdf\x4c\x00\x57\x0e\x1c\xa4\x64\x39\xf0\xda\x46\x58\xea\x6f\xfb\x79\x22\x70\x52\x29\x3f\xf2\xff\xaf\xae\x48\xbb\x8d\xf8\x6c\x2a\x5f\x2e\xf0\x1f\x3b\xe8\xa9\x61\x94\x88\xd6\x1b\x7b\xfc\x98\xd2\x02\x9b\xfc\x82\x01\x79\x3b\x97\x0f\x41\x81\x97\xb0\x4a\x0c\x6b\x77\x2d\xdf\x73\x63\x57\x53\x8a\x96\x6a\x26\x5d\x2b\xae\x8c\x74\x64\x1f\xbb\x86\x41\x3b\xa0\x07\x1b\xb4\xdb\x8d\x54\x9a\xa2\x81\x95\xbf\x71\xec\xc0\xd7\x8f\x8d\x91\x31\xce\x28\x23\x26\xb9\x1e\x4c\xb7\x2c\x9c\xdc\xc3\x68\x32\xa1\x60\x90\xcc\x51\x6e\x9d\x4b\xce\xd5\xbb\x10\x7c\x1c\x91\x28\x11\xb3\x24\x6d\x97\x13\xef\x21\xc4\x3c\xba\xb0\xed\xd0\xd7\x8f\x60\xc1\x39\x8c\xea\x45\x39\x2a\xcf\xfd\x6f\x66\x4d\xba\x2b\xbd\xd5\xd3\x04\xa9\x48\x75\x15\x8c\xa6\xf3\xd3\x28\x71\x3d\xdc\x14\xe9\x94\x32\xee\xce\x6a\xa0\xd3\x3e\x5f\x54\xc1\x62\x41\x13\x58\x4b\x41\xc2\xdf\x40\x58\xd8\x15\xb5\xd5\xdd\xc3\x08\xc6\x34\x8b\xc6\x8c\x3d\xc9\x5e\xd5\x17\x16\x17\xa8\xe9\x44\xc0\xc2\x3e\x54\x89\x5a\x39\xbc\x3a\xbd\x5f\x15\x5a\x95\xde\x82\x5f\x99\xec\x90\x7a\xec\x8e\x83\x38\x16\xf8\x95\xd7\x38\x7c\x44\xb3\x40\x2f\xdd\x3c\xfa\x55\x38\x17\x84\xeb\xba\x59\x90\xf7\xd8\xff\x92\xd0\xc0\xfd\xaf\xe7\xde\x0e\xe3\x5b\xd9\x82\xfa\x75\xa6\x95\xa8\xf1\x7b\x67\xf2\x2d\x5c\xb1\x2a\xd6\x77\x77\x41\xba\x98\x44\x89\xf5\x56\xa9\x0e\x09\xda\x6b\x91\xa8\x4a\xdc\x30\xdb\x4a\x03\x9e\xbb\x97\x3f\x2f\x3f\xfa\x73\x8d\xaf\xab\xa1\x69\xb0\xcc\x8c\xda\xab\x06\xbd\x0e\xa3\xd6\x2e\x00\xba\xe4\x19\x69\xb7\xc9\xa8\x99\x41\x16\x42\x99\xd7\x2c\x6b\x05\xbc\x31\xde\xcf\x95\x13\x4a\x66\xf4\x3d\xf7\xd2\xfa\x0b\x3f\xce\xe4\xde\x23\x6f\x85\x03\xcc\xf0\x83\x39\x26\x32\x20\xf1\x4a\x2c\xea\xc6\xbc\x28\x04\xbf\x4a\x36\xfe\x7c\xfe\x99\xd4\xf2\xda\x21\xfc\xca\x8f\x94\xd0\x9d\x98\xb0\xce\xea\xa8\x33\xb6\xb5\x12\xdc\xa1\x4d\xc9\x8f\x3c\x99\x10\xc8\x4b\xf8\x06\x58\xa4\xf3\x45\x71\x89\x55\x82\x0d\x36\xd1\xda\x55\x68\xd2\x23\x62\x4f\x23\x90\x3e\x56\xc0\x8d\xf4\x38\x55\xea\x6b\xca\x8b\x89\xca\x81\x88\x2a\xeb\xc6\x60\x5c\xac\x6c\x78\xc4\x82\x9b\x8c\x43\x3f\xc6\x2b\xf7\x0f\xf5\x53\x94\x17\xce\xcb\xbf\x63\x63\x34\x27\x1e\xa7\x50\x95\xa3\xd7\x35\xbb\xdb\x8b\x7a\x17\x24\x6f\xea\x97\x8b\x90\x5b\xb6\x8a\x77\x70\x4a\x15\x59\xa4\x05\x7a\xeb\xca\x0b\x4b\xe1\x88\xfb\x1d\x22\xc6\xdb\x3e\xf5\x84\x50\x80\x9a\xcf\x8a\x8c\xbd\x4d\xad\x47\xbe\x7d\x95\x2c\x48\xfb\xf6\xcb\x76\x16\x62\x36\x4f\x76\x71\x8f\x35\x2c\x1e\xc6\xc6\xae\xab\xe8\x17\xaf\xb5\xdc\x17\x5a\x1c\x52\x8b\x40\x9d\x14\xbf\xba\x55\xaf\xe6\x06\x03\x39\xdd\xf4\x8c\x66\x97\xc5\x0c\x7c\x91\xa0\x7a\x30\x76\x5c\xc7\x53\xd2\x22\xcd\xc1\x8f\xf1\x52\xd7\x7f\x43\xa1\x7c\x2f\xdd\x69\x13\xae\xd2\xf9\xba\x47\xda\x6d\xa9\x7c\xaf\x50\x52\xbc\xe3\xb3\x64\xe9\xf4\x94\xfa\xee\xfa\xa4\xb7\xd9\x28\xd6\xde\x57\xd4\xc9\xc1\x6d\x74\xb5\x52\x2e\x63\x20\x25\x5a\x39\x69\x66\xc6\xfe\xe7\xaa\x32\xf8\xf5\x58\xff\x3c\x41\xc9\xdb\xf8\xc3\xd2\xcd\xb1\x34\xae\x9c\x63\xbf\xa4\x76\x8e\xfd\x7e\x8a\xaa\x43\xfa\x39\xa7\xc6\x06\x1a\x3a\xe7\xee\x7d\x15\x15\x1d\x2b\xbc\x8a\x8e\x8e\xc3\xdb\x4a\x3a\x96\xba\xa2\x96\xce\x2c\x52\xa1\xa6\xe3\x2d\x56\x95\xbd\x89\xa2\x8e\xe1\xb6\x44\x51\xd7\xcc\x51\xbe\xe8\x56\x03\x45\x5d\xa3\x68\x5e\x5f\xeb\x71\x9d\xe7\xf6\x6f\x15\xf2\xe0\xc5\x57\x21\x10\x59\xc2\x26\x11\x9e\xbe\x22\x91\xd8\x85\x2a\xc8\x44\xb6\x5b\x5d\xfe\x46\x3a\x5d\x2e\x49\x35\x79\x33\xe7\x69\xef\x6e\x5f\xcb\xa9\x51\x36\xa0\xbb\xbb\x8f\x3e\x52\xf9\x7e\xc7\xc3\x87\x91\x8b\xdb\x28\x6f\xee\xdb\x76\x4c\xb3\x22\x88\x12\xbf\x7f\x5b\x07\x91\xfc\x36\xa9\x86\xa8\x39\x50\xdf\x4c\xaf\x26\x6b\x51\xc4\xca\xa8\xf5\x06\x51\xd0\x6c\xce\x8e\xfc\xd1\x04\x6a\x36\xfb\x1d\x0a\xaf\xb5\x64\x1a\x9d\xd1\x44\x9a\xb4\x98\x47\xea\x32\x77\xb9\x96\xfd\x0b\x3f\x66\x6b\x8b\x5b\xc0\x32\xaf\xdc\x69\xd7\x6f\x7f\x8b\x21\x9a\x2f\x11\xee\x9c\xb6\x55\x78\x85\xe3\xf4\x8c\x66\xd9\x79\x16\x15\x05\x05\x73\x2f\xde\xab\x16\xd9\x80\xde\x37\xc6\xdd\x39\x68\xd9\x73\xfc\x90\x1f\xac\x20\xf4\x51\x34\x4a\x04\x0a\x0b\xd7\xef\xb0\xfd\xd6\xbe\x11\x32\x5d\xad\xa4\xd5\x9c\xd6\xda\x96\xe0\xcd\xe3\x42\xc0\x8f\xc1\xc1\x00\x54\xe1\xc1\x9c\xad\x0a\xf0\x7a\x28\xb4\x59\x6c\xbc\x8c\x13\x50\x7e\xc7\x10\x47\x9f\x29\x09\x48\x1e\x25\xd3\x98\x2a\x3f\x5c\x00\xd9\x37\x4c\xa2\x81\x82\xb9\x9b\x19\xee\x96\x83\xb7\x76\x75\x45\x8e\xdb\xc7\x9b\x27\xed\x93\xae\x12\x06\x6b\xdc\x00\x88\xee\x99\x78\x67\x5f\xd8\xb5\x61\x89\xe8\xce\x6d\xa0\x38\x2a\xc0\x56\x61\xb3\x47\x1e\x81\x3d\xf6\x10\xfa\xb2\x89\x1d\xd1\xe8\x0e\x39\x82\xac\x74\xd4\xd0\x93\xae\x1d\xca\x4e\x0b\xd2\xa1\xc3\x43\x09\xa8\x1b\x18\x0c\x48\x10\xc7\xe4\x34\xc8\xa3\x31\xf7\x7f\x00\x8f\x05\xb6\xb7\x84\x02\x27\x4e\xd9\xc9\x58\xf6\xa6\x47\xb6\xb7\xea\x8c\x4e\xcc\x85\x2d\x38\x9a\x3c\x81\x4b\x5d\x24\xa1\x53\x10\x20\x21\x28\xd4\xf1\x49\x8b\xec\xfe\x00\xeb\x53\xa7\x3d\xe6\x89\x95\xca\xb4\x3d\x59\xdb\xaa\x1c\x60\x46\x4b\x7b\x56\xb1\xda\x71\xab\xa5\x34\xab\xdd\x7e\x19\x0e\x61\x1c\xa2\xdb\xb1\xb6\x51\x54\xe4\xc1\x03\x82\xbf\x8f\xd1\x6f\xe4\x02\xee\x44\xee\xba\x2a\x32\xc6\x60\x7a\xa3\xb9\x11\xcb\xb7\x6a\x6a\xe4\x2c\x98\x73\x23\x26\xcc\x9c\x1a\xe4\x71\xed\x96\x33\x63\xf5\xab\x62\x62\x50\x9b\x5f\x7b\x5e\xee\x72\x62\x4c\xd7\x27\x9a\x91\xa2\x99\x80\xb3\x51\x0b\x6c\x11\xb6\x38\xd2\xf9\x21\xa9\x25\x8c\x15\x36\xc5\x54\x6c\x3e\x56\x80\x5b\x27\xc7\xdb\x02\x54\xa6\x71\x10\x05\xb1\x79\x62\x25\xe8\x6f\x77\x77\x00\xac\xde\x60\x7b\xc0\x63\x11\x43\xac\xdf\x13\x50\x63\x77\x34\x91\xd1\x84\x74\x50\x16\xe2\x90\x36\x3f\xbe\xe1\xc4\x02\xc3\xf6\xbd\x86\xd8\xac\x98\x72\xb1\x49\xc8\x53\xb5\x6f\x9e\x61\xde\x7c\x53\xdd\x52\xf1\xf7\x9c\x09\x17\x9f\x2d\x63\xde\x8d\x8a\x8e\xcd\xca\xf1\x74\x6b\xef\x6b\x8d\xe6\x59\x65\xf0\xa1\x88\xfc\xd2\xf9\x35\xbc\x28\x96\xee\xf6\xc2\x5b\x51\x1c\xe4\x05\x39\x3e\x61\xc2\x04\xaf\xf7\x46\xd3\xbe\xee\x9f\x77\x35\x07\x20\x67\x11\xc7\xc7\x12\x1c\x68\xf4\x4b\x28\xf8\x54\x34\xd0\x84\x48\x2a\x8c\x63\xd1\x11\x46\x71\x60\xfb\xa6\x89\x9c\x5e\x92\x90\x4e\x82\x65\x0c\x8a\xd0\x7c\xc9\xe4\x54\xb5\x31\xb7\x84\x9b\x9a\x9e\x08\xf3\x68\xcf\xa2\x71\x8c\xba\x01\x03\xd6\x3b\xe2\x8a\xa2\x70\xc3\xd3\x5b\xa9\x51\xbd\xf4\xd5\x2e\x75\xc4\x68\x89\xe4\xf6\x1a\x01\x8a\x17\xa4\x7c\xdc\x62\x14\xdf\x23\x2d\xb6\x08\xd8\x7f\x27\xad\x13\x4d\xed\x02\x02\xa5\x41\xa1\x64\x19\xdb\xcf\x1e\xd0\x6c\x36\x42\x9b\xed\x60\xce\xea\x6f\xcd\x42\x70\x9d\x54\x39\x2b\x81\xef\x0d\xc2\x59\x1e\x9f\xf5\x1c\x6e\x78\xd9\x70\x8c\xf1\xb2\x7f\x61\xd5\x5b\x44\x2c\xb8\x55\xe7\xdf\xc7\xfc\x34\xfe\xef\x93\x6e\xbd\x88\x20\x94\xb7\xca\xdb\x43\xf9\xbd\x83\x15\xc6\x42\x42\x37\x67\x1d\xf2\xed\xa9\x7b\x97\x65\xe1\xcc\x73\x69\x21\xee\xd1\xed\x8d\xc1\xeb\x8f\xda\xbc\x95\x11\xae\x50\xa5\x13\x54\x9b\x2d\xd4\x78\x83\x55\xf6\xdf\xd8\x98\x78\x87\x94\xfe\xf9\x1d\xa3\xba\x7e\x65\x69\x3c\xc1\xfe\x64\x05\x2b\x73\x0a\xa9\x97\xc9\xc7\x27\x3e\x27\xe2\xfd\xc5\x32\x9f\x75\x1c\xcf\xa4\xf2\xa5\xb6\x74\x33\xea\xd6\xcc\xc6\xe2\xfa\x5c\x3f\xf3\x39\x00\xc5\x2d\x21\x3f\x9e\x9d\xb3\x1e\xc1\xfe\x65\x2d\xf7\xa4\xb7\x72\xea\x2b\x26\x10\x3b\xf3\xbd\xf5\xfc\x41\xd7\x1d\xa9\x43\x20\xfe\xb7\x9f\x3f\x9f\x47\xd6\x1a\x4f\xac\xa5\x13\xc1\x66\x13\x5c\xa5\x56\xcc\xc7\xca\xb3\xb1\xe6\xdc\x11\x5a\xba\x23\x63\x49\x22\x8f\xb6\x4d\x7c\x82\xf2\xfb\xd1\x49\x96\xce\xbd\xe6\x06\x1c\xca\xc7\x5b\x4e\xed\x07\x3b\x96\x81\x90\x61\x19\xb4\xc2\x83\x29\xc9\xd4\x78\xcb\x0d\x58\x94\x18\x08\x66\x51\x86\x3f\xcd\x1a\x56\xf5\x55\x78\x15\xec\x4d\xf8\xc6\x92\x0b\xba\xe2\x89\x0f\x74\x4f\x0a\x3a\x02\x5d\x0f\xc9\x16\x18\x3f\x74\xa5\x47\x67\x81\xbc\xb2\x45\x54\x59\x27\x6e\xde\xa9\xd8\xb7\xa2\xa0\xc0\x87\x82\xdf\xb1\xe3\xd2\x1b\x64\x9b\x3b\xbd\xe7\xbb\x6d\xce\x40\x72\x12\x4c\x0a\x9a\xa9\x45\x82\xfb\x7b\xa3\xb5\xea\x2f\xe3\xf3\xdd\xad\x39\x47\x89\xcf\x6e\x52\x89\x3d\x11\x3a\xe6\x6d\x59\xfd\xd8\xaf\x47\xa9\x1b\x69\x3b\xe6\x4d\x25\xa3\x69\xc8\x69\xc8\xc3\xea\xbe\x31\xd8\x8d\xdd\x6a\x98\x46\x8c\xca\x74\x38\x8b\xa6\x7d\x83\x44\x77\xcb\xb5\xfe\x10\x7b\x08\xfe\x6b\x48\xfd\xd2\x20\xb5\xe1\xdf\x1f\x8a\xf8\xef\x69\x1f\xfd\xfd\x2e\xb4\x4f\xbc\xa4\x8f\x03\x34\xde\x94\xf4\xed\x30\x62\x2b\x6e\x2a\x0e\xb1\xda\xf5\x37\xdb\x59\xcc\x5e\xac\x52\xbf\x98\x3f\x2f\xbd\xc5\x0e\x7d\xf9\xd7\x5f\xf9\x12\x5e\x88\x5b\x3f\xd7\x48\xb5\xae\xfb\x1d\xb2\x49\x36\xcc\xde\x75\xb9\x4f\x26\x1e\x49\xcc\x33\xf5\xdc\x03\xb1\x75\xe9\x66\x3c\xd8\xae\xf0\x67\x6f\xe0\xda\xb2\xf8\x32\xb8\xd8\xda\x8a\x63\xc3\x73\xae\x56\xd6\x56\xd7\x54\xab\x7a\x2f\x12\xad\xae\xd7\x5e\xf0\x96\x5f\xed\xaa\x37\x71\xd7\x27\xbd\xcd\xdf\x3b\xf4\xfe\x51\xfd\xb3\xb7\x65\xc5\xbb\x37\xe1\x89\x04\xfe\xe7\xb6\x2e\x4b\xfd\xf4\x6d\x89\xde\xbe\x2d\xf1\x83\xb5\xa5\xe7\xf5\xdb\x52\x3d\x7f\x5b\xa2\xf7\x6f\x4b\xf4\x00\x6e\x69\xbe\x80\x73\x6a\x6c\x60\x61\xe3\xf8\x47\xf9\x8a\x8f\xe0\x8e\xbc\xaf\xe0\x8e\x56\x7f\x06\x77\xd4\xf4\x1d\xdc\x91\xfb\x10\xee\xe8\x0e\x5e\xc2\x2d\x6f\xfd\x14\xee\xa8\xf1\x5b\xb8\xdf\x3b\xae\xff\x51\x03\x8b\xb3\x65\x95\xc9\x99\x74\xad\xc2\x7f\x08\xe2\x44\x56\x67\x4b\x6c\x76\xb6\x34\xac\xc4\x96\x3e\xc3\xb3\xa5\xb6\x3c\x5b\x62\xd3\xb3\x25\xb6\x3d\x5b\x5a\xc6\x67\x9e\x7a\x9b\x2c\x8e\xdf\xd4\xfe\xec\xc8\x6f\x80\x76\x74\x03\x0b\xb4\xa3\xc6\x26\x68\x47\x1e\x1b\x34\xbb\xf4\xcd\xd6\x48\x85\x19\x5a\xd3\x45\xd2\xdc\x10\xed\xdb\x26\xab\xa4\xbd\xcc\x29\x28\x66\xc7\x45\x9b\x07\xe4\x9b\xa6\x84\x26\x67\x24\x4c\x29\x58\x2b\xc0\xeb\xc0\x20\x09\xc1\x87\x2d\xf9\xe7\x9b\x9f\x5e\x17\xc5\xe2\x3d\xfd\x7f\x4b\x9a\x17\x6b\x20\x98\x5d\x2e\x68\x3a\xb1\x72\xb8\x1f\x1b\xf5\x7e\xa3\x2d\xf1\x22\x1a\xee\xdb\xd0\xe4\xcb\xf5\xce\x9a\x11\x2c\xb2\x14\xd2\x4c\x00\x49\xfd\x97\x7c\xc6\x76\x9f\x68\x9a\xa4\x19\x1d\xc5\x51\x42\xd7\xae\xb9\xc5\x2a\xc3\x43\x23\x6f\xf7\xf7\x2f\x67\xef\x5f\xce\xfe\x89\x5f\xce\xf2\x57\xb3\xc2\x86\xcd\x78\x36\xcb\x37\x1c\x72\xb3\xd7\xb3\x62\xef\x3b\x2a\xa2\x18\xea\xe4\xfa\x4c\x58\x3b\xfc\x79\x92\x03\x16\x15\x97\x8a\x25\xea\x22\xe3\x38\xc8\x73\x72\x0c\x45\x4e\x44\x37\x79\x86\x66\xc2\xbc\xaa\xb5\x01\xdc\x1b\xc1\x2a\x15\xca\x55\xc6\x41\x48\x85\x33\xeb\xe6\x7e\xce\x01\x92\xd5\x74\xf4\xf6\xe0\xe3\x07\x76\xb6\x86\x49\x68\x9f\xd3\xa8\xcd\x49\xb3\xfd\x19\xfd\x7e\x83\x7e\xff\x88\x7e\xe7\xbf\x06\xa7\xa9\xfc\x98\x44\x49\x42\x2f\xd5\x17\x9d\x17\x29\x3c\x65\x94\x29\x8b\x68\x6c\x26\x24\x41\x62\x26\xcc\xa3\x71\x66\xa7\xc4\x71\xe4\x14\x32\xe0\x0d\x50\xf9\x61\x14\x99\x66\x41\x12\xaa\xa1\x18\x59\x3f\x1a\x5f\x1f\x8d\xaf\x77\xc6\xd7\x4b\xe3\xeb\xff\x8c\xaf\x7f\x19\x5f\x6f\x8d\xaf\x17\xc6\xd7\x3f\x8c\xaf\x23\xfe\xb5\x76\x52\xee\xba\x86\xcd\xd1\xbb\xbd\x17\x6c\x8a\x47\x64\x7b\xab\xa7\x12\x3f\x1c\xfc\xf8\x76\xef\xe3\xd1\xfb\x97\x9f\x7e\x7a\xf9\xf6\xc7\x8f\xaf\x47\xe4\xb1\xce\x84\x59\x1d\xe9\x9f\x3a\xa7\x84\x72\x46\xe4\x0b\xb1\x12\xb4\x1f\x75\xc8\xf8\xf4\xe2\xf0\xe7\xb7\xe4\x5a\xd7\xf4\xee\xf0\xa7\x9f\x18\xf4\xc7\x83\x37\x2f\x0f\x8f\x3e\x8e\xc8\xe6\x70\x38\x1c\x88\x1e\x8a\x1b\xef\xe7\x71\x3a\xfe\x3c\x22\x6d\xc6\x3a\xf3\xa2\x6d\xe4\xed\x8d\x21\x94\xf1\x48\xbf\x6d\xe4\x0f\x30\xd8\x7e\x5e\xe7\xfb\xe4\x3e\x14\xc6\xfd\x46\xf6\x57\xdf\xc8\xd6\x94\x0b\x88\x7c\x16\x6c\xdf\x95\x07\x88\xfd\xec\x72\x51\xa4\x7f\xff\x80\x37\x87\x31\xa4\x3d\xd2\x11\x30\x58\x83\x5e\x80\x01\xcb\x69\x7b\xa3\x3b\xb9\xee\x1b\x80\xe2\x72\xfc\x40\x55\x24\x91\x07\x0f\x64\x6e\x5f\xfa\x8b\xe0\x62\xf2\x8c\x5e\xb4\xed\x57\x74\x86\xe7\xaf\x1f\xc8\x16\x2b\x6d\x7b\x3f\xde\x92\xee\x22\xcd\xe2\x44\x5e\x86\xab\x0b\x7e\xcb\x3f\x3b\xb1\x5e\xdb\x71\x50\x89\x23\xd6\xb9\xfe\x6b\x7a\xd1\x07\xed\xa5\xf0\xdc\xeb\xb3\x31\x62\x58\x91\xc3\xd6\xad\xf3\x13\x1d\x57\xbf\x8d\xc8\xd6\x37\x4f\x78\x49\xf4\x38\x59\xbe\x39\x63\x2c\x4f\xe1\xb8\x35\xfa\xe6\xbb\x5e\xcb\x44\x79\x6b\xf4\x74\x78\x7d\xd2\xdb\x6a\xe4\xf3\xe9\x9e\xef\xdd\xf3\xbd\x3f\x2f\xdf\xd3\x6c\x8f\xbf\xf3\xbf\x03\xbe\x67\xc9\xee\xab\x8b\xee\x1e\xc9\x5d\x16\xf4\x09\xee\x2b\x45\x1b\xb2\x79\x6d\x7f\x20\xd8\xbd\x0e\x47\x34\x79\x8a\x01\xd8\xb7\x12\xe1\x97\x49\x54\xbc\x09\x16\x4a\x5c\x6c\x4b\x89\x7a\xc4\x79\x50\x7b\x28\x65\x4d\x26\xb5\x8f\x34\x5b\x6c\x6f\x1a\x72\xfe\x08\x65\x0c\x87\xaa\xd0\xff\x56\xe4\x9d\x06\xa7\xa7\xc1\x94\xaa\x96\x70\x1e\x12\xfe\x47\x76\xde\xdc\x53\x27\xca\x7e\x53\x9d\x1d\xa7\x67\x34\x0e\xc6\xb2\x59\x3b\x5b\x9f\x31\x46\xbe\xec\xa9\xbf\x72\x04\xf1\x63\x2d\x44\x3e\x0b\x92\x24\x4d\x8c\x71\x9b\x10\xfa\x5c\x33\xaa\x80\xa8\x69\x05\x4e\x56\x23\x0f\x04\x46\xa5\x3e\x2f\x8d\xaa\x81\xea\x6a\x12\x67\xb7\x91\x17\xc8\xa8\x4c\x9d\xc7\xec\xb1\x79\x00\xfd\x43\x34\x01\x0d\x72\xf5\xc0\x21\xd0\xcf\x26\xac\x0f\x14\xcf\x35\x9c\xfa\x2a\x2b\xc6\xfd\x6d\x54\x37\xae\xbe\x69\x01\x54\xa6\x58\xa1\x0c\x2b\xe6\x37\xb6\xd2\x8e\x18\x16\x41\x28\x4c\x49\xc1\xd4\xf3\x62\x41\xc7\x6c\xf3\x52\xe6\xf9\xd8\xe8\x4a\x78\x4f\xf1\x59\x4e\xe9\x2a\x4e\x29\x83\x0b\x45\x44\x2e\xcb\x06\x6b\x3c\x0b\xb2\x60\x5c\xd0\x2c\x97\x2a\x7e\xb8\x97\x17\xa5\xd1\x3e\xe2\x6d\x23\x9a\x26\x3d\x64\x0b\x4d\x86\x6b\x7e\xb7\x1f\xd1\x74\x56\x10\xe9\x91\xd6\xf2\xee\x2b\xc6\x60\x48\x9b\x1c\xa4\x07\xbd\xcb\x7b\xd0\x8e\xc7\xc7\x10\xb7\x10\x01\x18\x08\x4a\x0b\xaf\x55\xd5\x0d\xf1\x66\xb7\xff\x4b\x1a\x25\x10\xac\x81\x3c\x83\x3a\xc8\x88\xb4\x86\xad\x2e\xd9\x10\xc0\x25\x86\x6f\x37\x9e\x0b\x08\xd8\xf3\x67\x9f\x0c\x18\xc4\x8a\xb3\x21\x7a\xb8\xc1\x3d\x2e\xdf\x74\x5e\xca\x0c\x11\x4d\x47\x34\xb0\x75\x82\x19\x22\x04\xf3\x70\x7d\x4c\x5b\xf3\xc2\xbd\x35\x57\xcc\x4a\x94\xb0\x4a\xfc\xc8\xc2\xfe\xa8\x3d\x8e\x92\x58\xe3\xda\xec\x90\x7b\x20\x39\xe2\x5b\xbb\x12\xe9\x67\x3c\xde\xf3\x60\x40\x5e\x45\x49\x48\xf8\xe3\x2e\xd1\x51\x15\xaf\x99\x49\x14\xad\x96\xbe\xc9\x07\xdb\x97\x1e\x84\x90\x9a\xd1\x0b\x69\xc2\xac\xce\x5c\x2c\x8d\x9f\x7a\xd8\x89\xa3\xfc\xac\xc4\xaa\xd9\xc2\xef\x5e\xc0\xb8\x46\xd8\xd4\xec\x90\x68\x63\x77\x0b\x83\xcb\x58\xc8\xd8\xb6\x43\x37\xd5\x89\x58\x3b\x22\xf4\x85\x6a\x61\x42\x3a\xbc\xc8\xee\x2e\x19\x76\x8d\x53\xda\x69\x46\x83\xcf\x1a\x94\x8d\x72\x63\x97\x88\x57\xe5\x6c\x06\xf7\x67\x41\xb6\x9f\x86\x14\x6a\xf0\x1e\xc2\xd8\x64\x4b\x73\x9c\xbc\xc8\x9a\x51\x08\x9f\xb4\x95\x48\x64\x8f\x15\xf9\xed\x68\x04\x9a\xfb\xef\x21\x92\x9b\xcc\x7c\x5e\x94\xbd\x4e\x37\x27\xdb\xe3\x63\xbe\xb3\xc8\xe8\x24\xba\xe0\x41\xb4\x86\x17\x5d\x36\x0b\xc0\x35\xfc\xee\xed\x45\xb4\xb7\xf2\xd9\xf7\xda\x2e\xc3\x11\x34\x88\x81\x9b\x57\x06\x13\xf0\x45\xf9\x34\x7c\xed\x0b\xb7\xeb\xa2\x1b\x98\x2a\x18\xc5\x0b\xcc\xf3\xd9\x87\xe5\x20\xcc\xb6\xf9\x72\x90\x33\xc2\x5a\xd2\xd4\x31\x49\x33\xdb\x84\x2e\x2f\xb2\xb2\x88\xf8\x68\x46\x19\xd4\x58\xcc\xcd\x5e\xd1\x89\x6e\xb6\xd2\xc1\x3a\x51\x04\x07\x37\xbc\xb6\x69\x10\xd6\xdf\x8d\x5d\x92\xc8\x7d\xe1\x7b\xb2\x45\x9e\xb1\x93\x0d\xd9\x20\x6c\x3f\x48\x7c\x34\x21\x5c\xc8\xcf\xe8\xc5\x5d\x92\x86\x15\x73\xc0\xa6\x8d\x1a\xd6\xf0\x9b\x11\x87\xc3\x33\x10\x75\xfc\x36\x14\xf0\xbb\x4d\xab\xe5\xb1\x74\xb2\x8c\x63\x85\x86\x01\x3d\xa3\x49\xc1\x1f\x0a\x00\xcb\xff\x25\x4f\x13\x12\x9c\x46\x36\x8f\x97\x6e\x13\x3f\xa6\xaf\x96\x71\x6c\xbf\xa1\x94\x8f\x09\x58\xe9\x47\xbc\xb4\xfb\x18\x8a\x37\xec\xb4\xab\x19\xbb\xdb\x86\x21\x48\xb1\xca\xb1\xea\x94\x7d\xf7\xc1\x84\x22\x4a\x42\x7a\x71\x38\xe9\xb4\x3b\xed\x2e\xf8\x86\x7c\xb4\xe9\x79\x0e\xa9\xe0\x1d\x3b\xc1\xe2\x72\x41\x45\x73\x00\x04\x54\x64\xfa\x33\xeb\x44\xdd\x2f\x32\x84\x70\x9f\xc1\xef\x90\x6b\x21\x8a\x99\x96\x7f\xaa\x15\xb2\x41\xda\x1d\x36\x73\xaa\xf6\x0d\xd2\xee\xb6\x1b\xad\xbd\x30\xca\x17\x71\x70\xc9\xe7\x05\x7c\x8c\x26\x05\x93\x6d\x15\x36\xec\x37\x6b\x17\x90\xfd\x82\x17\xab\x7a\xe1\xca\x6a\x33\x27\xdf\xbf\xbc\x8c\x1e\xb0\x2d\xcd\xa2\x18\x3a\xed\xcb\x78\x8b\x97\x1d\x61\x56\xd7\x25\x8f\x7e\x50\x89\x6a\x5a\xdd\xbe\x55\x3e\x7c\x56\x36\x9b\xce\xcc\x1a\x68\x16\x60\x7c\xb2\xc9\x33\xfb\x4d\xab\x78\x0f\xc6\xd6\x8c\x76\x36\x32\x18\xe8\x81\xa6\x67\x34\x8b\xd3\x20\xa4\xa1\x52\x04\x7b\xd6\x04\x1e\xc0\x47\x4d\x24\x65\x6f\x1a\x07\xe4\xe3\xe1\x8b\xc3\x11\x99\x07\x9f\x41\x35\x1c\x25\x67\xcb\x38\xa1\x59\x70\x1a\xd3\xbb\x1c\xa0\x3e\x0d\xd8\xaf\x77\x37\xc9\x23\x82\xb2\xbb\xdd\x7e\x46\x17\x71\x30\xa6\x9d\x36\x69\x83\x53\x37\x76\x5a\x68\x99\x41\x22\xd3\xe4\x8c\x66\x45\xae\x43\x6e\x82\xdc\x17\xd2\x71\x34\x0f\x62\x9b\xc9\x46\x89\x9f\xd9\x17\xe9\x0b\x5e\xc0\xa5\xbc\xca\xf0\x99\xa6\x5b\x43\x2e\xe0\x89\x9a\x6a\x03\x40\x16\xa9\x1b\x1f\x53\x85\x9f\x69\x32\xc6\x5a\xd9\x96\xf1\xc4\xbb\x1a\x17\xaa\xab\x3a\x38\x6b\x22\xb5\xa4\xee\xf8\x3c\xa1\xb9\x85\xfa\xd4\xdc\x51\x8c\xc3\x3e\x07\x88\x69\x9e\x7f\x9c\x05\x49\x67\x08\x4e\x64\x1f\x71\xab\x73\x61\xbd\x2f\x08\x6b\xb3\x0b\xe1\x5b\x51\x8e\x81\xc5\xbd\x25\xb8\x69\x16\xa8\x0c\x92\x4b\xe1\x78\x47\xb8\x23\x4d\xca\xd1\xda\x17\x78\xdd\x4b\x42\xae\xfe\xe7\x34\x14\x4d\x2e\x73\xe1\x48\x3d\x27\xa7\x74\x92\x66\xb4\xef\xd0\xd5\x6b\x71\x74\xa8\xc6\xfd\x95\xd8\x83\x6a\x48\xeb\x35\xec\xf3\x06\xf2\xd5\xfa\x7d\x28\x4c\xc5\xe6\xc1\x05\x0f\x5b\x79\x11\x15\x97\x23\xf2\x14\x54\xd8\x72\xd7\x89\x72\xe1\xd2\x18\x8a\x76\xed\x4d\x06\x4d\x72\x67\x83\x41\xec\x18\x45\xf1\x74\x56\x17\xb6\xca\x0a\x43\xba\x33\x46\x3b\xec\x14\xc2\x91\xd6\xf6\x56\x01\xf1\x95\xfe\xfe\xe1\xf0\x6d\x5f\x61\x99\xb7\xa7\x1d\x58\x82\xeb\xd8\x9c\x04\x76\x34\xcf\x1e\x59\x04\x79\xce\x78\x57\x31\xcb\xd2\xe5\x74\x66\xae\x00\x35\x10\x41\x6b\x50\xab\x7b\x39\xa9\xb9\xda\x23\x38\x2d\x79\x64\xde\xd2\x11\x4b\x00\xf1\xb6\xc3\xac\xae\xa6\xb6\x33\x69\x3f\x8a\x2a\x20\x9d\xf5\x28\x7f\x15\x25\x51\x41\x2d\xa4\x5b\xdd\x00\x09\x11\x75\xc2\x94\xb2\xdc\x8e\xa2\x75\xf1\x5e\x6c\x2a\x7c\x1d\xb0\xf3\x52\x02\xdc\x9f\xfc\x4c\x6d\x41\x6a\x4a\x0b\x88\x58\x7c\x38\x39\x4a\x22\xaf\xb6\x0b\xca\x16\x33\x2a\x7e\xa8\x05\x47\x8a\xb4\xa7\xb4\x53\xca\x21\xba\x37\x6a\xa3\xea\x87\xaa\xa6\xc3\x3b\xd3\x85\x22\xe0\xb6\x2b\x27\x34\xcb\xd2\x4c\xba\xa4\xe1\x3d\xce\x49\x92\x16\x64\x9c\x66\x19\x1d\x17\xa3\x73\xb5\x6e\xcc\x5e\x1b\x0b\x88\x15\x94\x24\xb0\xe4\x99\xf0\xdf\x33\xf8\xaf\x5f\xa4\x3f\xa5\xe7\x34\xdb\x0f\x72\xda\x01\xe6\xc2\xf5\xbd\x9a\x8f\x31\xa8\x7f\x88\x5b\x66\x71\x75\x73\xcc\xfe\x3f\xd1\x47\x71\x04\x82\xfd\x7e\x63\xc2\xe3\x9e\xc8\x12\x7a\x4e\x5e\xb2\x51\x75\xda\x70\xd5\x0b\x1d\x01\x5b\xd5\x7f\xb7\x0b\x42\x2f\xa2\xbc\xc8\x7b\x64\x11\xd3\x20\x07\xb1\x18\x46\x9e\x26\x0a\x55\x93\x34\x8e\xd3\xf3\x28\x99\x42\xc9\x9c\x71\x41\x6b\x19\x89\x1e\xf6\xc0\xbf\x42\x4f\x3f\xfb\xa8\x88\x12\xab\x7a\x0f\xde\xaf\x4c\xaf\xc2\xc1\x67\x0a\x8b\x90\x33\x7c\xb8\x8c\x8e\xc0\x9e\x56\x31\x59\x4e\x02\x8c\xd5\x82\xaf\x0a\x3e\xf1\x1c\xb5\x82\xb2\xde\xa5\x79\x1e\x9d\xc6\x7c\x0a\xc1\x85\x86\x30\xea\xfb\x70\xc0\xe4\xcb\xac\xe0\x3f\x99\x48\x2d\xb1\xf5\x72\x32\x89\xa6\x97\xe2\xe3\x50\x92\xd2\x23\xf2\x99\x35\xcf\xff\xf4\x75\x15\x7c\x8a\x9b\x2d\x0e\x36\xd7\x60\xea\x72\x89\x7f\xca\xab\x28\x0e\x37\xd5\x70\xea\xfe\x87\x7f\x8a\x0b\x23\x9d\xc7\x0b\x3c\x7a\xa4\x16\xa6\xbe\xc7\xe1\x05\x7e\x0d\x4e\x53\x23\xcf\x53\x42\xde\xc3\xf0\x01\xc0\xf5\x0d\xce\xe3\x25\x50\x2f\x50\x61\xfe\x29\xb0\x80\x40\x88\x05\x81\x3e\xe0\x32\x45\x20\x84\x6a\x1c\x4e\xd1\xef\x42\xfe\xb6\x45\x0a\xce\x17\xac\x93\xef\x95\x92\xd3\x39\x39\x8c\x83\x84\x9d\x0c\x02\xc5\x9a\x45\xba\xd0\x95\xa5\x19\x09\xc8\xeb\x97\xff\x84\x43\xb8\x94\xd6\xee\x8c\xa1\xa8\x7d\x56\x1e\xed\x7e\x9e\x51\xe9\x67\x2f\x40\x57\xb9\x22\x0a\x0a\x0a\x16\xc0\xd6\x53\x90\x93\x73\xca\x16\x88\x76\xb0\x22\x87\xb1\x86\xa4\xa1\x9f\xa9\x71\x24\x97\xe3\xc4\x2c\x85\x8b\x3a\xac\x66\xc9\x24\xb0\x50\xc4\x4b\xe0\xa8\xb1\x26\xa7\xe2\xdc\xc9\x92\x87\xf0\x36\x2c\x2a\x20\xcf\x8c\x46\x46\xf8\x0b\x49\x56\xb5\xcb\x37\xe0\x38\xf6\xac\xe0\x73\x1a\xdd\x2f\xd8\xff\x96\x25\x5e\xa4\x55\x0b\x1c\x9d\x17\x7e\xb3\xa5\xce\x56\xdb\xef\xb8\xd8\x01\x21\x77\xb3\xd4\x8b\x68\x4e\xf3\xdf\x63\x99\x27\x42\xb9\xc8\x16\xb7\x52\x55\xe5\xfc\x98\x0f\x5b\x34\x51\xb6\x2c\x0e\x39\xa8\x9e\x34\x22\x0a\x4d\x06\xf2\xee\x90\xcd\xbd\xa6\x05\xb3\x36\xe5\xe5\x4a\x57\xa0\x01\x14\xfe\xb1\xf1\x8d\x35\x0b\x35\xe7\x9f\x6f\x98\x10\x08\xcb\x5e\x96\x17\x3f\xae\xae\xc8\x70\xc7\x7b\xb8\x11\xf5\x3a\x87\x13\x9e\x6e\x9c\x88\x04\xce\x65\x4f\x1e\x3c\x20\xe2\xb7\x4f\xe8\x67\x4d\xda\xb9\xf8\x84\xe1\xf3\x81\x66\xc8\x62\xa2\xb0\xd2\x89\x0c\x2f\xda\xbd\x76\x1b\x5f\xb8\x58\x9e\xd2\x7c\xa5\x31\xa1\x94\xca\x74\x89\x8c\x1d\xeb\x21\x15\x45\x27\x1c\x4c\x46\xf1\x50\x47\x31\x61\x36\x09\xb0\xc5\x79\xda\xce\xc9\x58\xc5\x74\x71\x48\xcb\x0c\xf9\xd2\x84\xbe\x4a\xa8\x06\x1d\x92\xcd\x3a\x4d\x85\x97\x41\x32\x0c\xfc\x14\x51\x96\x6f\xc1\xc2\x93\xef\x0e\xf2\x5a\xa7\x0a\x60\x95\x44\xed\xd4\xb5\x26\xb7\xfc\x6b\xc1\x2c\xf7\x17\xf1\x32\xd7\x5d\x10\xdf\x5e\xf7\x86\x0a\xc8\xd4\x24\xcd\xe8\xf8\x73\x2e\x8f\x4d\x9c\x47\xca\x6b\xce\x5c\x3c\x96\x8b\x2f\xc1\x8f\xaf\x37\x1a\x31\x27\xf9\xb1\x37\x12\xb1\x19\x53\x18\x35\xc0\xd6\x7f\xa0\xe1\xb1\x63\x3b\x08\xae\x24\x66\xce\xaa\xdb\x98\x38\x51\xa9\xa5\x41\x1b\xfc\x67\x78\x71\x3c\x7c\xf4\x5d\xf0\x68\x72\xf2\xe5\xf1\xf0\xfa\x7f\x06\x51\xbf\xa0\x79\xa1\xc0\x57\x18\x7b\xc5\x90\xbf\xce\x60\x1b\x0c\x13\xce\xff\x83\xff\x74\x86\x17\xdd\x67\x95\xe3\xc4\xf4\x37\x18\xe8\x58\x59\x3c\x1a\x16\xf4\x8e\x7b\x10\x16\x46\x87\x73\x78\xc7\xcb\xf6\x63\x34\x6a\x93\x7e\x85\x23\x40\x62\xba\xaa\xf0\x76\xc6\xec\x0b\x63\x73\x08\x6c\xef\xd1\x2b\x2f\x98\xd5\x65\x08\xdd\xd5\xce\xc1\xd9\x71\x3e\x67\xff\x8e\x83\x45\x0e\xb2\x43\x1c\x13\xf9\xdd\xc3\x1e\x1a\xed\x1e\x73\xc7\xf3\xa8\xc3\x46\x03\x87\x6a\x7b\xe7\xd8\xa1\xc1\x78\x46\xc6\x41\xee\x54\x13\xe5\x9c\x50\x96\x73\x31\x43\x88\x9a\xf8\x2a\x6b\x4e\x53\xbc\xad\x7c\x39\x9f\xd3\xb0\x94\xbc\xac\xe6\xee\x98\xcc\xac\xda\xab\xc8\x6d\x30\xe0\xe3\xb1\x70\x13\xa8\x92\xe2\x97\xb3\x03\x69\x7d\x88\x80\x78\x1d\xe4\xe0\x8c\x66\x16\x6c\xcb\x46\x4c\x5d\x8a\x94\x76\x7c\x0e\x5f\x1e\x0f\xe1\x8e\x92\x58\x14\x02\xce\xbb\x8b\x19\x89\x29\x3c\xa7\x46\x11\xf8\x16\x0b\x9a\xb1\xde\xca\x69\x48\x20\x7a\xe1\x34\xe2\x01\xee\x82\x9c\xce\x83\x05\x9b\x8e\x4d\x43\xd3\xd7\x51\x16\x0c\xa8\xd3\xe0\x96\x6d\xf3\x49\x97\xfc\x40\xbe\x65\xdb\xb9\xc8\x3a\x8e\x4e\xfa\x45\x7a\xc4\x1a\x12\xba\xa0\xf5\xdd\x5d\x94\x09\x44\x5f\x5d\xe1\xf7\xbb\x9e\x1a\xb1\x76\xc9\xaa\xb1\xc4\x57\x38\x5a\x96\x9a\xe5\x1b\x8c\x5f\xc7\x5f\x50\x54\xfa\x46\x1c\xf5\x24\x35\x96\x90\x62\x91\xde\x25\x29\x4a\xed\xb5\xda\x97\x57\xa0\x44\xa4\x33\x56\xd4\x67\xbf\xba\x16\xed\xb4\xdb\x82\x94\x5c\x32\x35\xf0\x7b\x23\xa2\x45\x40\x63\xa7\xf7\xac\xa2\x0a\x32\x96\xbd\x40\xd7\xee\x36\x49\x03\xd3\x9b\x69\xd3\x3f\x46\xa4\xdf\xb1\x83\xcf\x84\x3b\xd0\x97\x37\x71\x8a\xc2\x0d\x02\xae\xa3\x5f\x93\x82\xec\xfe\x6f\xec\x96\x12\x37\x22\x2f\x9b\x91\xd6\xd6\x54\x49\x9a\x56\x49\x53\xf2\xd4\x92\xa6\xc1\x46\x8b\x94\x49\x94\x51\x48\xb6\x86\xdc\x67\xd0\x23\x71\x41\xc8\xdb\xe4\xef\x13\x86\x17\x84\x1b\x77\xb8\xc6\x5d\xb5\x94\xec\xbf\xed\x17\xde\x07\x30\xd7\x56\x06\x5c\xcd\xe8\xd7\x12\x67\xbc\x1b\x9f\x74\xaa\x2b\xf1\x81\x64\x78\xbe\xdb\x56\x6d\xb4\x9e\x8a\xc4\xe5\x97\xaf\x3e\x13\x42\x86\x5e\x84\x2b\x25\x55\xa3\x7e\x4d\xd5\x23\x8f\x87\xfe\x5b\x02\xe9\x88\x58\x9e\xa6\x73\x2d\xe5\xd6\x07\xd9\xf4\x9e\x24\x7d\x57\x5f\x46\xe0\x4d\xbe\x91\xf9\xce\x80\xa4\xc3\xbb\x61\xc9\x85\xb2\x6f\x49\x5e\x04\xc9\x98\x71\x11\x5d\xf8\xea\x4a\x21\x4d\x14\x86\xd7\x6b\xf0\xcb\x70\x9c\xe1\x4d\xe5\xb6\x11\xc0\x8b\x54\x95\xed\xa6\x88\x92\xe7\xe1\x3a\x2c\x7d\x70\x8c\x8b\x1a\xa2\xc8\x13\x22\xc9\x8b\x1f\xc1\x5a\x45\xcf\x60\x34\xbc\x6f\xed\xbb\x43\x0f\xef\x4b\x63\xdc\xc8\x1e\xd7\x63\xe7\x95\x36\x22\x59\x15\x3f\xb2\xe8\x8d\x30\x24\x4b\xb4\x1b\x8e\x88\xf5\xa9\xa8\x1f\x0e\xef\xfa\x0d\x06\x73\x28\xfa\xd6\x70\x31\x30\xf1\x22\x59\xc6\x31\x44\x49\xe8\xb8\x2b\x04\x0c\xb7\x41\x85\xe1\x19\xbb\xb8\xaf\x6d\x38\xf2\x53\xde\xd9\x06\xec\x80\x03\xde\x84\x19\xf0\xa4\x1b\x4d\xa4\xe8\x5e\xd3\xd1\x80\x0b\xc0\xfa\xb1\x38\x11\x35\x1a\x8e\xc4\x8d\x8a\xd1\x90\xa5\x41\xc1\xca\x31\xd8\xc7\x11\xbe\x8f\x82\x8d\x5c\x2a\xa9\xce\x1c\xc4\xdf\x73\x73\x5d\x69\x0b\x84\xca\x31\xb0\x62\xf6\xab\x01\xe5\x3a\x29\xbb\x74\xf7\xa9\xf5\x75\xb8\x99\xe4\xcf\x70\xb5\x31\xeb\x35\x19\x43\xd8\xa7\x0e\xf5\xec\x6d\xf8\x40\xba\xca\xa8\x03\x31\xee\x97\x6c\x02\xe9\x72\x4e\x4e\xe3\x74\xfc\x99\xcc\x68\x10\xd2\x8c\x7d\xa4\x73\xdb\x6a\x23\xca\x9f\xb3\x64\x9f\xd0\x30\xa3\x17\xca\x2f\x3a\x94\x25\x93\x28\x2e\x6c\x65\xa6\x87\x60\x01\xd6\x70\x3f\xcc\x52\x2a\x4f\xfa\xdf\x6c\x6e\xe9\xa3\x3e\x07\xaf\xc1\x4b\xf9\x41\x9d\xd7\x85\xab\xf2\x9d\xd3\x5d\x28\x5f\xc4\x61\x7d\xce\x5e\x73\xfb\x71\x83\x99\x89\x53\x26\xe6\x2d\xa2\xb1\x3b\x0f\x1f\x59\x72\xdd\x3c\x14\x0a\xa8\x62\x02\xa0\x26\x63\x02\xa0\x58\xe5\x04\x3c\x79\xac\xf1\xcf\xa1\x6f\x8c\x7f\xa8\x0a\xd7\xe4\x43\xbf\x03\x74\x23\xec\x97\x38\x1e\x11\x22\xdf\x48\xfe\xe8\xc9\x54\x78\xf4\x33\x52\xbf\x78\x3a\x08\x86\x23\xfe\x9f\x4c\x11\x16\x24\x23\xfd\x93\xe7\x20\xeb\x92\x11\xfe\x90\xe5\x8e\x8a\xc9\xd3\x91\xf8\x5f\xa6\x81\xbd\xca\x48\xfe\xd0\xf5\x70\x58\xf9\x4b\xa7\x0b\x78\xf5\x53\xd4\xe3\x1a\xdd\x8e\x7c\x89\x1c\xda\xb5\xe5\x1c\x79\xd2\x0c\x58\x69\x36\x39\xb2\x13\xe4\x38\x7e\xa6\x30\x8a\x9f\x29\x1a\x03\xa4\x89\x1f\x12\x4e\x49\x8b\x23\xfc\x21\x73\x4d\x95\xf5\xc8\x49\x51\x58\xe3\x82\xfa\x48\xff\xe4\x39\x48\x3a\x1e\xe1\x0f\x99\x6b\x9c\x44\x46\x76\x82\x84\x42\xf9\x56\x8e\x75\x74\x1f\xb9\x49\xb2\x87\x0e\xa4\x93\x24\xeb\x94\xc2\xd8\x08\xfd\xc6\xfd\x4d\xa6\x23\xf5\x4b\xa6\xf3\x3d\x75\xa4\x7e\xa9\xd1\xf3\xf5\x3e\xd2\x3f\xd5\x98\xd8\x2e\x39\x92\x3f\x64\x2a\xdb\xb0\x46\xe2\x7f\x55\x07\xe3\x77\x23\xf9\x43\xa6\x02\xdb\x18\xc9\x1f\x3d\x58\x60\xdc\x41\x9d\x78\xd5\xdd\x1a\x6d\x7e\xd7\xab\xf4\x6f\xd3\x6b\x2d\x8b\xc9\xd3\xd6\xe8\xe9\x37\xd7\x27\xbd\xad\xcd\x26\x1e\x1f\xcc\x25\xbc\xcb\x17\x70\x4b\x38\x3a\x68\x8d\x48\x6b\xd8\xdf\x1a\xf6\x37\x5b\x6b\xd7\xd2\x15\xdc\x56\xa3\x48\xc5\xf7\x9e\x24\xee\x3d\x49\xfc\x15\x3c\x49\x88\x5a\xd6\x5c\x5f\x70\x7f\xa7\x93\x49\x46\x2f\xc9\xcf\x51\x3c\xfe\x4c\xc9\xf7\xbf\xd0\xc9\xc4\x76\x27\xd1\xd0\x63\x1c\x80\x45\x41\x42\x0e\x99\xc4\x1d\x00\x54\x14\x24\x2e\xd8\xab\xe0\x94\x81\xfd\x23\x9d\xd2\x38\x2f\x68\x1c\xd3\x8c\x7c\x3f\x81\x44\x17\xf8\xc7\xe0\x8c\xfc\x9c\xa6\x21\xf9\x7e\x5a\xea\xe6\xe2\xb1\x76\xef\x23\x7c\x41\xbe\x09\x92\x60\x6a\xfa\x9e\xe8\x0f\x18\x16\x06\x19\x07\x98\x73\x00\xe9\x63\xe2\xe0\x14\x0e\x47\x36\x70\x74\x1a\x24\x12\xe4\x25\x98\xf1\xdb\x10\x5c\xf2\xca\x07\xb4\x98\x49\xc0\x17\xcf\x2b\xe0\xc2\x53\xe5\x6f\x76\x56\x55\x5f\x3e\x53\xf5\xbd\x05\xcf\xe4\x65\x80\x09\x2d\x24\xe0\x3b\x9a\xe5\xf0\x94\xaa\x1c\x7a\x21\x40\x54\x27\xce\x83\x6c\x5e\xd5\x0d\x96\xaf\x80\x69\x51\x40\xd4\x26\x17\x3e\x17\x59\x12\x54\x72\x15\x03\x52\xb2\x0b\x76\xa2\xd2\xce\x3d\xa2\xd8\xaa\x10\x85\x95\x2f\xf7\x11\xc2\x81\xa4\x37\x26\xf1\x70\x83\x26\xa1\xa7\x6f\x3c\x43\x82\x3d\x87\x13\x93\x0b\x75\xca\xd2\x15\x26\xb3\x74\x41\xb3\xe2\xd2\x03\xb7\x10\x59\x12\xf4\x75\x51\x2c\xde\x65\xe9\x59\x14\x7a\xc9\x8d\x2d\xd4\x85\xc8\x56\xc4\xb6\x18\x57\x94\x88\x16\x63\xbb\x40\x33\x8f\x86\x6b\x6b\x4a\x56\xff\x99\x9e\x6e\x93\x8e\xac\xc6\xf4\xca\x9b\xd9\x2b\x24\xa1\xe7\xd6\xb2\xd1\x25\x91\x83\x5e\x11\x6a\x15\xf5\x5c\x42\x21\x20\xca\xdf\xba\xd0\x73\xb6\x5c\xc0\x51\x3f\xae\x22\x3c\x15\x99\x2f\x9e\x3b\x79\xf9\x4c\x96\xfc\x30\x73\x4b\x26\xb0\x06\x58\xee\x5b\x5a\x38\xb9\x0b\x4d\xf8\x0c\x44\xae\x03\x07\xee\xf4\xd7\x5f\x65\x1b\x8c\xae\xdd\x3e\x68\x02\x07\x20\xf1\xd9\xc1\x30\x9a\xb2\xf5\x51\x23\x58\x44\x23\xb5\x19\x8a\xff\xf9\x91\x03\x77\x52\x60\x2b\x37\x8a\x62\xf2\x19\x19\x5f\x3d\x05\x83\xe8\x65\x84\x3f\x9c\x26\x3e\xa9\x35\xc0\x7f\x38\x03\x14\x00\x1d\xdd\xbe\x20\xe7\x88\xe6\x23\xf4\xbb\xc3\x8d\x79\xae\xbb\x3b\x4c\x62\x1a\x0c\xc0\x05\x6f\x4e\x89\x1e\x43\xca\x77\x62\xf0\x09\xb4\xc6\xc8\xcd\x33\xbe\xba\xb1\x95\x8e\x8b\x09\x8d\xb2\x4e\x19\x4f\x93\x62\xca\xc3\x31\x83\xeb\x69\x1c\x17\x5e\x99\xb4\x3d\x7d\xc9\x28\x0f\x16\xa1\x7b\xf1\x99\xd2\xc5\x41\xfe\xe1\x32\x19\x47\xc9\xb4\xb2\x2b\x50\xd6\x82\x6f\x46\x81\x9e\x8e\x60\xbe\xf0\x5c\xdb\xaf\x58\x50\xf2\x19\x0c\x77\x27\x05\x5f\x1e\x18\xf9\x64\x56\x42\xc1\xb7\x07\x4e\xbc\xbb\x96\x60\xec\xd3\x81\xc2\x4f\x70\x39\xa0\x4a\xf1\xc2\x1a\x75\xca\x04\x4f\xdb\xfa\x3d\x95\x6c\x5e\xa4\x78\x6b\xb5\xa1\x51\x9a\xa7\x6e\x8c\x4b\x59\x7b\x15\x4e\xb9\x89\xa3\x84\xfc\x99\xfa\x47\x86\xa1\xc4\xb7\x03\x87\x4d\x5b\x38\xa4\x4a\xf1\xc0\xba\xb7\xc2\xb2\xcc\xbe\x7d\x5b\xe8\xf4\xb9\xac\xac\x93\xe3\x69\xf7\xe0\xf9\xde\x5b\xd4\x18\xfb\x74\xa0\xb4\x7b\x1a\x0e\x26\xbe\x7d\x70\xd2\x73\x8a\x02\x84\x04\xb6\x8b\xd9\x0b\x9f\x6f\xfd\xf8\x25\x37\xbf\x14\x32\xbd\x2b\x9a\xd7\x75\x70\x27\x6d\x43\x96\x5d\x9f\x86\x51\x06\xaa\xe2\x71\xb0\x80\xd7\x17\xe8\x02\xd3\x33\xa3\x07\xfb\x7b\xef\x8c\xb5\xcf\xca\x61\x0b\xb9\x88\x8b\x92\x6c\xf9\x32\xa9\x92\xe7\x1b\x8f\x3d\x19\x44\x5f\x34\x23\x57\x36\x38\x94\x51\xfc\xb7\x2a\xe2\xe8\xb1\xe2\xdd\xb0\xd7\x09\x71\xa4\x63\xde\x39\x27\xa0\x83\x69\xcb\x3d\x29\x49\x43\xda\xee\x19\x10\x53\x30\x0b\x19\x91\x36\x13\x3a\x3e\x8d\xe3\x88\x26\xc5\x3f\x38\x78\x5b\xdf\x49\x77\x7b\x37\x69\x8d\x16\xe7\x69\xf6\xb9\xac\xc1\x84\x16\x9f\x04\xa8\x05\x62\x06\x0c\x18\xd9\xab\xfc\x96\xdd\xa2\x42\xa1\x5d\xd6\x2f\x5a\xcc\x3e\xc1\x5c\x8f\xd3\xf8\x1f\xbf\x43\xff\xce\x67\x51\xbe\x50\xbe\x91\x9d\xee\xe5\xb3\xd9\xad\xd1\x06\x3f\x4f\xbc\x7b\x49\x94\xef\xa7\x49\xc2\x7d\x36\xa1\xe5\xd6\x35\x68\xaf\xe3\xdd\x2e\x1f\x3c\xf0\x6e\xa3\xb8\xca\x4e\xd7\xbf\x83\x71\x2f\x05\x52\x26\x2f\xa5\x79\x30\x0e\x85\xc8\x09\x42\xa2\xf1\xea\x6d\x59\xdd\xd2\x9b\x28\x3e\x21\x70\x95\x93\x71\xb0\x68\x8d\xb6\x86\x2c\x09\x1f\x49\x5a\xa3\xad\x4d\x96\xa6\x8f\x03\xad\xd1\xd6\x63\x95\xc2\x45\xa7\xd6\x68\xeb\xa9\x4a\xc2\xc2\x7d\x6b\xb4\xbd\xa5\x32\xd8\x0a\x6f\x8d\xb6\xb7\x75\x82\x16\xea\x5b\xa3\x6d\x5d\xa9\x3e\x16\xb6\x46\xdb\xdf\x3a\xc9\xb4\x98\xb5\x46\xdb\x4f\x9d\xf4\x84\x16\xad\xd1\xf6\x77\x4e\xba\x14\x84\x5b\xa3\xc7\x43\x27\x33\x9f\xcd\x5a\xa3\xc7\x9b\x6e\x3a\x93\x85\x5b\xa3\xc7\xba\xfb\xf2\x8c\xd3\x1a\x3d\xfe\x46\x25\x9a\x07\xe7\xd6\xe8\xf1\x13\x95\x25\xa5\x96\xd6\xe8\xf1\xb7\xd5\xba\xbd\xeb\x93\xde\xd6\xf6\xbd\xe6\xed\x5e\xf3\xf6\xdf\xa2\x79\x0b\xe2\x18\x1c\x4c\xdc\xce\x8f\x2b\x52\x70\x39\xaa\x10\x9f\x2e\x44\x86\x89\x79\x79\xc6\x2d\xfa\x91\x8e\x01\x7a\x23\xe1\x74\xd0\x98\xba\xe8\x48\xae\x9e\xc6\xab\xa8\x79\x05\x97\xbb\x56\x65\x90\x26\x21\xce\x79\xec\x23\x13\x44\xb2\x22\x91\xa9\xbc\xbb\xee\xc5\xb1\x31\x14\x53\x30\x32\x8f\x56\x3d\xb8\xa9\xef\x11\xcb\xb4\xac\x44\xe9\x61\x26\xe0\x23\xf2\x2f\xfc\x72\x9e\xfd\x87\x93\x1d\x73\x49\xbe\x09\x39\x3d\xac\x0e\xf3\x6d\x49\xad\xd2\x1f\xf8\xae\xfa\x75\x75\x05\xf1\x6f\x88\xed\xf7\x81\x25\x42\xea\x71\x9b\x49\xa1\x10\x57\xa0\xdd\x23\xed\x22\xe5\x3f\x4f\xfa\x1c\xcd\x28\xde\xe1\xc4\x73\x1b\x2a\x9a\x39\x9e\x9c\x80\x81\x8b\xb2\x0f\x15\x37\xa4\x5d\x4f\xd0\x6c\xab\x1a\xd6\x1f\x56\x7c\x17\x11\x0f\x77\xa1\x03\x1d\xe1\xe7\x25\x1d\x04\x4f\x37\x28\x6d\x16\xf4\xc3\x2d\xf0\x45\xa1\xf1\x6a\xe0\xd9\x7c\xdd\x85\xbd\x53\x54\x61\xdc\x13\xb5\x38\x0c\x8a\x40\x8e\x80\xfd\xee\xb3\x7f\xc8\x2e\xfa\x7d\x75\x05\x46\xb1\x0a\x00\xae\x92\x73\x09\x22\xbe\xae\xae\x74\xf4\x4d\xd0\x36\xb2\xa6\xe5\x1d\x39\x02\x3c\x1e\x9e\xf4\x73\xc6\x10\x94\x8b\x75\x06\x3d\x17\x02\x8e\xa6\x30\x77\xba\x7e\xf1\x4c\x17\x6e\x65\x57\x98\xda\x0a\xe9\xce\xbd\xb4\xed\xfc\xa2\xde\xa7\x77\x8f\x87\x27\xe8\xe1\xd5\x3a\xb4\xdf\x25\x5f\xe0\xb1\x43\x90\x24\x69\x41\x26\x51\x12\xf2\x7e\x45\xc9\x94\x37\xf4\x4c\x35\x3f\x4e\x93\x3c\x8d\x69\xff\x3c\xc8\x92\x4e\x1b\x97\xe0\xde\x72\x18\x2b\x8e\xd3\x69\x1b\x99\xbe\x8a\x1e\x33\x54\x38\x1e\x97\xa8\x60\x43\x38\x32\x17\xcc\x5d\xc7\xb7\x3a\x7b\xbc\x5b\x3d\x93\x20\xcc\x23\x14\xd4\x28\x9d\x1d\xc2\x14\x37\x58\x8e\x17\x74\xcc\x24\x00\xcf\x7a\xec\x81\x47\xa6\xd3\x60\xfc\x59\xc5\x10\x05\x57\x04\xe2\xb0\x2b\xaf\x5b\x3b\x41\x36\x5d\xc2\x5b\x90\x63\xf5\x0b\x79\xe3\x31\x8d\xd0\x65\x8d\x10\xfb\xb9\xb2\x18\xf6\x1b\xd7\x71\x20\xd8\xc4\x6f\x9a\x7e\x2c\x34\xdb\x48\x96\x71\xec\xa0\x3b\x95\x94\x26\xbc\xdf\xe9\x03\xb0\x84\x98\xa0\x28\x6b\x5c\x33\x0b\x98\xec\x9f\x46\xa6\xd2\x10\x89\xdf\x9c\xb3\x77\xd2\x1e\x1c\x94\xda\x3d\x2f\x63\xed\x49\xf6\xce\x0e\x5b\x9d\x6e\x4f\x37\x84\x30\x5c\x3f\x53\x41\x51\x04\xe3\xd9\xc7\x74\x5f\x3a\xc2\xc2\x53\x26\xbd\x63\xe1\x33\xb7\x9e\x5a\x3e\x6e\xfe\xe9\x0c\x47\x16\xed\x07\x71\xac\xf6\x13\x01\x5c\x72\xa6\x70\xba\xa9\x0e\x18\x9e\x13\x86\xf7\x88\x01\xa4\xda\x1a\x6d\x81\x74\xcf\x57\x7d\x6b\xb4\x05\xb2\x3b\x8e\xd9\xb6\x0d\xc0\xd6\x46\xd8\x1a\x3d\xde\x66\x22\xf3\xe3\x7b\x91\xf9\x5e\x64\xfe\x6b\x8b\xcc\x28\xdc\x0b\x9c\xbd\xef\x2a\xde\xcb\xdf\xf3\x34\xc9\x16\x63\x53\xde\xfc\x85\x27\xaa\xab\xc3\x2c\x4b\x6d\x11\x98\xa7\x29\x49\xd4\x55\x51\xb0\xc1\x1a\x42\xa6\x23\x63\x02\x3a\x3e\x95\x4a\x9a\x22\x23\x17\x81\xbd\x6b\x1c\x05\x06\x61\x28\x7d\x3a\x32\x76\x2c\x0a\x83\x9b\x6c\xe8\x9a\x48\xb0\x2c\x02\x83\x30\xf4\xd8\xd8\x12\x31\x7e\x5e\xa8\xd0\xd6\xad\x83\x35\x18\x27\x66\xc5\x61\xe8\x93\xb9\x7d\x03\xcf\x79\x54\x70\x09\x51\x3b\x22\xc9\xb4\xab\xfa\x2f\x60\xbc\x5d\xf3\xed\xe7\xa6\x77\x01\x85\x5f\xa3\x9b\xee\x14\xe8\x7b\xa2\x24\xe4\x6a\x26\x09\xdb\x43\x75\xd3\x2c\xeb\x09\x49\x34\x77\x65\x62\x4e\x3e\xfc\x97\x10\x16\x35\x80\xc0\x0f\x76\x31\xa9\x50\xd9\x23\xf0\xba\xbd\xe4\xfd\x9a\xa8\xf2\x18\x60\x4e\xf0\xf1\xa0\x54\x60\xe7\x45\x4a\xaa\x65\x62\x8d\xec\x8f\xa8\xb4\xef\xc8\x3e\x76\x81\x75\xb1\x88\xfa\x51\xfe\x8f\x20\x8e\xc2\xf7\x34\x5f\xa4\x49\x4e\x45\x53\xce\xdb\x3b\x67\x0c\xfe\xf6\x3a\x7c\x8d\xf5\x0f\x92\x33\x6f\xad\x3b\x4e\xa5\xd7\x6e\xff\x4a\x2b\xe7\x3e\x9b\x9c\xc1\xf2\x3d\x17\x7c\x43\xf8\x32\x44\xe3\x7d\xd1\x07\xf0\x1a\x81\x13\x9c\x28\xf6\x7a\x2a\xd4\xf9\x86\xf8\x45\x09\xa0\x2c\xad\x9f\xe4\x83\x6f\x8d\xb6\x40\x8f\x26\x56\x64\x6b\xb4\x0d\x56\x6f\x8d\xa2\x7c\xdf\x6f\xf8\xf7\x1b\xfe\x9f\x77\xc3\xd7\xfb\xbd\x12\xcb\xef\x48\x45\xd6\x50\x57\xc5\x4e\x3c\x99\x05\x96\x0b\x59\x7f\x00\x99\xab\xaa\xd3\x24\x1c\x7a\x37\x85\xf5\x60\xf2\x41\x94\x80\xde\x43\x87\x10\x04\xa6\x34\x86\x46\xc8\x71\xdf\xfe\xc9\xd5\x4b\xf8\x91\x19\x6c\xf3\xf6\x33\x65\x0e\xb7\xaf\xc1\xde\x49\x28\x25\x17\x80\xb1\xef\x35\x91\xbe\x9c\xcd\x54\x6f\x03\xc2\xdb\xaf\xbf\x6a\xf3\xa9\xe7\x69\xd4\x13\xe5\xac\x5b\x9d\xe0\x34\xf2\xa8\x41\x90\xdf\x67\x62\x39\x5a\xe6\x01\xbe\x77\x77\x49\x1b\xf5\xa9\x4d\x1e\x3c\x30\x1c\x39\xa3\x73\x33\x6f\xd6\xf0\xf6\x7f\xdd\xb5\xb6\xe1\xaa\x06\x3d\xae\xa1\x49\x07\x12\x4b\xb6\x6b\xc8\xe3\x1e\xa3\x3d\x3b\x83\x55\x11\x03\xcb\x3d\x4d\x03\xed\x89\xc3\x3b\x47\x28\x07\x55\x68\x44\x5a\x1e\xa9\xbd\x6a\x20\x3d\xaa\x80\x5e\xc2\x55\x14\x3f\x5a\x7b\x5f\x36\x05\x61\x28\x69\x38\xd7\xc7\x70\x4c\x1b\x32\xed\x5a\xd5\x54\x4a\x4f\x9c\x54\xfc\x55\x56\x9e\xec\xf5\x71\xfd\xe6\x84\x82\x5e\x21\xae\x32\xfb\x58\x53\xa5\xb4\x3f\xaa\x3f\x9f\x68\x31\x93\xea\x66\xdd\x49\xd3\xeb\x45\xad\x2a\x75\xe2\xa8\x39\x34\x02\xb4\xaa\xb4\xc1\xbc\x72\x6e\xd1\x68\x52\x39\xbf\xb9\xbb\x19\xb5\xeb\xab\x57\xd4\x48\x86\x77\x17\x73\xcb\x79\xaf\xa5\x56\x16\x9c\x55\x68\x1b\x15\x8f\x35\x27\xcf\xd5\x5b\xf1\x8e\x95\x4e\xe7\x5e\x1c\x57\x4e\x17\x00\x89\x8b\x9e\x95\x09\x8c\xab\x42\x6b\x3a\xb8\x3a\xb5\x19\x8f\x02\x5d\xa5\x5a\x19\xb5\x55\x91\x9b\x72\x94\x03\xb6\x7f\x72\xd2\xa7\xb4\xc8\x85\xf1\x4a\x7c\x49\x42\xba\x88\xd3\x4b\x1a\x4a\x13\x41\x78\x3e\x38\x9e\x05\x51\x62\x3f\x57\x83\xda\x5e\xa5\x99\xec\x91\xc7\xf7\x80\x3c\xb0\xfa\x48\x52\xae\xcb\x6b\xa5\x5a\x5c\x33\x5c\xe4\x1e\xc9\xcb\x0d\xfd\xac\xad\xa4\x45\x6c\xf0\x20\x5b\x42\x0a\x4b\x4d\xbe\x10\xb0\x19\x22\xc9\x38\x6a\xde\x57\x10\xa5\x7c\x57\x3e\x2c\x83\xfc\xc1\x80\x9c\x07\x11\x57\x97\x83\xc8\xb5\x28\xb4\x0a\x56\xde\x94\x99\xf3\x2e\x96\x82\x0a\x18\xad\x3b\x46\xbb\xa6\xe7\xe5\x75\x0a\x4f\x93\x8d\xf6\xed\x5d\x09\xfa\xbb\xb1\xb1\x63\x1e\x9b\x06\x03\x92\x17\xe9\x82\xeb\x6a\xa3\x64\x4a\x82\x09\xeb\xca\x37\x43\x3e\x57\x39\xe9\x14\xd1\x9c\xa6\xcb\xa2\xeb\x1c\x1d\x39\x02\x7e\x20\xdf\x0c\xbd\x87\x45\xde\xfb\x3e\xab\xfd\x67\x51\xb9\x8e\xa9\xd0\x25\x5f\xae\x3d\x67\x3a\x1b\x81\xfc\xc1\x9e\xf7\x1c\xaa\x66\xc4\x7b\xda\xd4\x27\x3f\xed\x18\x58\x31\x26\xb8\x2f\x09\xf8\xca\x18\x33\xc2\x06\x27\xc1\xa7\x4c\x62\x5e\x26\xa1\x8d\x81\xb6\xef\xf0\x49\x63\xe4\x50\x04\xff\x39\xee\x88\x6f\xdc\x2a\x5b\x7e\xb8\x66\xe5\x4f\xc4\xc5\x9a\x41\x35\x53\x5a\x7c\xd4\x4d\xbd\xe7\xa4\xa6\x39\x0a\xea\xc6\xeb\x20\x9f\x61\xa2\xea\x49\xc2\xec\xfa\x8f\xf0\xd1\xa4\x23\x00\xfc\xd4\xe6\x2d\xe4\xed\x20\x84\x30\x12\x75\xf5\xc7\xe6\x02\x34\x7b\x04\x71\x8e\xfc\xdd\x91\x7f\x65\xde\xdb\x9f\x28\xef\xed\x65\x7f\xd1\xa4\x63\x52\xdc\xd5\x15\x59\x87\x16\x2b\x8b\x11\xc5\xba\x3d\xb4\x89\xff\x6e\xb2\x04\xf0\x5f\xc3\xe5\x60\x0f\x29\x0d\x51\x88\xe8\xed\xca\x99\x91\x7f\x83\x81\xba\xe7\x8b\xd3\x29\xa2\x5a\x38\x56\x48\x36\xbe\xde\xee\xd6\x34\x4f\x0c\x51\x4d\x71\xd4\x92\xa9\x6e\x50\xd9\x60\x40\xf8\x66\x25\xc5\x85\x20\x09\x89\xb8\x19\x21\xc1\x34\x88\x12\xb1\x72\xce\xa9\x88\xf0\x57\xf3\xe7\x97\x3d\xed\x0d\xb0\xa6\x06\x5b\xd6\x71\xb6\xff\x9a\x21\x8d\xb9\x53\x36\x71\x29\xc8\xb6\x04\xb6\x3b\xe6\x74\x9c\x26\x21\x61\x0c\xb7\xb6\x12\x44\xba\xf5\xc4\x4a\x0c\x8e\x08\xba\xb0\xa6\x1d\xf6\x7a\x31\xba\xe3\x0e\x61\xdf\xed\x48\x94\x10\x27\x5a\xc4\x29\xf3\x22\xcd\x68\xa8\xfc\xb8\x73\x09\x04\x34\x3e\xd3\x20\x27\xc1\x9c\x6d\x48\x7d\x2f\xbf\xb6\xff\x4a\xf9\xb7\xfd\xe7\x71\x2f\x7f\x17\x5d\xac\xee\xe1\x75\x69\x6e\x19\xc7\x70\x4b\xd8\x90\x48\x3b\xd9\xf4\x40\x81\xae\x18\x24\xa1\xbf\x0a\xd8\x31\xfb\x52\xf9\xd2\xb0\xa4\x38\x0b\xac\xe6\xd0\x60\x57\x8a\x0f\x0c\x70\xaa\x0a\x4e\x23\xe3\x72\x81\xbf\x28\xa2\xf2\xf8\x0e\x69\xc1\x69\x44\x76\x19\xa4\x94\xb3\x1e\x72\x4d\x68\xfd\x98\xf4\x09\x29\x21\x01\x12\x4d\x45\x71\x59\x8b\x1c\x5b\x42\xcf\x55\x92\x1c\x53\x72\x79\x8d\x89\xc1\xd2\x8d\x6c\x4a\x9b\x82\x20\xee\xae\x58\x74\xab\xa2\xa8\x2d\x07\x1b\x92\x85\xf0\x75\x22\x15\xc5\xa1\x53\xda\x27\x29\x0b\x08\x25\x2d\xeb\xe3\x9f\x4c\x52\x6d\xe9\x89\x87\x42\x03\x3d\x11\x0c\xa5\xbe\xeb\x17\x52\xb1\x45\x7f\x2b\x6b\x60\x7f\xea\x07\x97\xae\xd5\x29\x12\xd3\x5f\x47\xd2\x41\x4f\xcd\x3e\xe6\x60\x83\x01\x8f\xad\xa8\xad\x2c\x8c\x4a\xb5\xad\xc4\x97\xeb\x1d\x06\x2c\xb1\xb4\x6e\xb6\x2d\x10\x83\x2a\x86\x33\x6e\x06\x6f\x71\x80\x90\xf1\xa3\x84\x38\x1a\x53\xb8\x6a\xd0\xf6\x1a\x56\xf8\x3f\x9f\xed\x08\xd8\x7f\x94\x5b\x8c\x10\xc7\x6a\x24\xef\x2f\xd2\x85\xe1\x60\xce\xec\x5e\x1c\xe4\x85\x80\x74\xaa\xf6\x77\x87\x13\x52\x87\x15\x04\xe7\x45\xeb\xea\xc5\x09\x04\xa2\x85\x74\xbb\x4f\x1a\x85\x35\x5d\x62\x0d\x09\xe0\x3e\x8f\x4a\xf2\x03\x19\xda\xb5\x89\x99\x96\xb4\xbf\x27\xd7\x72\xbd\x16\x40\xfe\xdd\x4a\x25\x88\xd0\x64\x31\x4b\xa9\x4e\x53\xa6\x76\x78\x58\xeb\x66\x97\xfb\x8b\xe0\x32\x38\x8d\xa9\xaf\x7b\xee\x71\x80\xdb\x4f\xe5\x34\x09\x75\x44\xaa\x24\x4d\x1e\x89\x4a\x30\x3a\xec\x6d\xe2\xba\x6c\xea\xc1\xb7\x1f\xe3\x8c\x7e\x15\x6c\x47\x2e\x95\x1e\x8c\x18\xd5\x2a\x27\x08\x6c\xdf\x36\x76\x79\x45\x3b\xe6\x24\x96\xde\x08\xe2\x13\xad\xa1\x03\x90\x72\x5f\x10\x26\xb6\x96\x20\xa4\xe4\x3c\xc8\x95\x40\xb9\x66\xe2\x8a\x2f\x6d\xb8\x7a\x45\x47\x18\x6d\x98\x65\xdd\xbf\xce\x82\x7c\xe6\x43\x3a\xeb\x35\xcd\xb2\xb2\x9b\x48\x7c\xe5\xe8\xbb\x57\xac\x92\x78\x98\x38\x1a\x86\xfc\xda\x0b\x71\x5d\xd6\x13\x7f\x5b\x25\xc7\x2e\xb2\x0b\x65\x4a\x84\xaf\x52\x09\x71\x12\x65\x79\x51\x2e\x20\xae\x28\xe3\x95\x68\x40\x7c\x6a\x0f\xdf\xf5\xab\xf1\x55\xe7\xf8\x12\x22\x6d\xf2\x81\xd7\xcd\xb3\xd5\x58\x53\x94\xd7\xa2\x7a\x95\xa1\xfb\x79\x9a\xd2\xc9\x73\x20\xa1\x2b\x13\xd8\x95\x9b\x20\x3b\xdf\xbe\xe0\x76\xa5\x90\x24\x3e\x0d\x03\xb4\x1b\x0b\x5e\xb6\xd6\xac\x4e\x3b\xeb\xd9\xd4\x45\x4d\xd7\xa6\x0c\x34\x51\xf5\x0f\xd6\x06\x03\x6b\x07\x36\x2e\x70\xb4\xc7\x63\xa4\xbe\xb4\x2a\xef\xf0\x7d\x79\x30\x30\x5c\xe9\x96\xc6\x9d\x1e\x8f\xc1\x2b\x6e\xca\x03\x35\x45\xc9\xb4\x42\x36\x33\xd5\xd8\xe6\xc8\xf9\x24\x5e\xbb\x9c\x08\x8b\x43\x55\xa2\x10\xf9\x82\xa4\xae\xa6\x12\xd1\x84\x24\xa9\xae\x81\xb1\xb7\x45\x90\xe7\x34\xec\xb1\x2a\xb4\xeb\x3b\x06\x91\xa3\x25\x6d\xf2\x32\x45\x78\x30\x03\x16\x3a\x0d\x73\x48\x9f\xef\x54\xd3\x66\x95\xac\x2c\x43\x69\x4b\x79\xad\xad\x2c\x66\xc8\xb5\x24\x04\xab\x81\x10\x61\xd2\xa8\x40\x75\xa9\x27\x0b\x9c\xd2\x71\xb0\xcc\x29\x3b\x89\x87\x69\x52\x90\xf3\x20\x01\x9b\xa4\x7c\x91\x46\x31\xbf\x0d\x4f\x0a\x9a\x4d\x82\xb1\xf2\x8d\xdd\xe0\x24\xde\xe4\xb4\x6d\x6f\x53\xf5\xfc\x90\x38\xee\x75\xd5\x9a\x46\x6b\xf3\x47\x5a\x70\x67\xcd\x6c\x7f\xec\x91\xf3\x59\x34\x9e\x81\xd1\x00\x5b\xde\x45\x2a\xb6\x31\xb2\x88\x97\x79\xfd\xd5\xab\xe0\x03\x35\xf3\xab\x99\x87\xdf\x90\xa9\x46\x84\x5d\x5d\x4e\x55\xc5\xea\xe5\xc7\xdb\xc8\x8e\xe5\x72\x23\x32\x56\xbe\x91\x1c\x53\x25\xc3\x98\x2f\x1d\xfa\xdc\x20\xbd\x39\xf3\xf5\x9c\x7a\xbc\xc7\xdd\x06\xd7\xe7\x65\xac\xc9\x39\x0c\x7b\x4f\xc1\x25\x2f\x59\x7c\xe7\x61\x77\xf7\xd3\x76\xe1\x1c\x7f\xee\xe3\x15\xe2\x39\x4c\x7b\xcd\x96\x2c\xba\xdd\x51\xe6\xcf\xa6\xad\x44\x6b\xf4\x6d\x99\x05\xb4\xb2\x68\x68\x8d\xb6\xb6\x5d\x93\x68\x31\xf2\xd6\x68\x7b\xf3\xfa\xa4\xb7\xf5\xe4\xde\xf4\xe9\xde\xf4\xe9\xaf\x6d\xfa\x84\x6c\x9d\x85\x09\xe4\x1d\x18\x3b\x97\xb8\xb1\x14\xc6\x95\xfc\x5d\xd6\xe1\x44\xde\x39\xef\x65\xd3\x7c\x54\xa2\xb9\x41\x32\x9e\x38\xc1\x8a\x4a\x70\xec\x3b\xb9\x9d\x30\xf6\x29\x2b\x25\xd8\xc4\x09\xf8\x7c\xcf\xd7\x87\xf7\xef\xf6\x39\x73\xbf\x4d\x07\x78\xbc\x25\x60\xb5\x14\x1e\x30\x16\x29\x79\xff\x6e\x5f\xdc\x13\xf8\x3b\x20\x9e\xa3\x83\x13\x45\xdd\xf2\x2c\xcd\xf1\xed\x97\xdb\xf8\xfe\xe1\xdb\xb7\x2f\xf7\x3f\x1e\x1c\xbe\x25\x2f\xdf\xbf\x3f\x7c\x3f\x22\xfb\x4a\xfd\x3b\xe6\x55\xf2\x13\x7d\x48\x49\x7b\x83\xb0\xfa\xc8\x46\xbb\xef\xef\x83\xf6\x78\xd3\x74\xec\xea\x9d\x3d\x57\x22\x14\x6c\xf5\x44\xbc\x32\x7f\x13\xd2\x90\x76\x44\x6c\xa3\x60\x34\x4c\x78\x96\x46\xf3\x3c\x98\x52\xb2\x4b\xd6\xd7\xc5\x4b\x43\xb6\xad\x8b\xdf\x7d\x1e\x32\xd6\x49\xe9\xcb\x62\xcf\x88\x37\x79\x44\xd4\x74\xfd\xfd\xc3\xe1\x5b\x98\x95\x4c\x75\xc9\x13\x66\x55\xf4\xcd\x79\x4b\xa6\x71\x20\xaa\x36\x47\xab\x67\xf3\x23\xbf\xae\xc6\xe3\x9d\xe7\x4d\xa7\xf4\xe3\xc1\x9b\x97\x87\x47\x1f\x47\x44\x5c\x7a\x33\xe2\x62\x9d\x9c\xe7\x64\x83\xb4\xd9\x7f\xc1\x78\xc6\x38\x46\xdb\x08\x68\x23\xdc\x48\x7e\x7b\xbf\x5b\xdd\xef\x56\x7f\xed\xdd\x0a\x6d\x56\xf0\xea\xf2\x8f\x6a\xa5\xdb\xfc\x31\x7b\xa3\x37\xf4\x77\xf8\x94\x5d\xfa\x1c\x62\xeb\x5f\x1d\xce\x70\x44\xa6\xdc\x38\x86\x88\x37\xb6\xd0\x96\x3e\x2c\xd8\x46\xc8\x5f\xfb\x1d\xfc\x42\x9a\xf2\x22\x45\x3a\xce\xe7\xa1\x2b\x48\xc5\x73\xe4\x3c\x4d\xba\x35\x4f\xe8\x51\x66\x92\x26\x97\xf3\x74\xa9\x5a\x54\x09\x25\xa7\x37\x89\xb4\x29\x95\xb8\xa2\x21\x97\x07\x20\x88\x81\x13\xac\x49\xa4\xa9\xe3\xd9\xf3\x34\x8d\xaf\x21\xbc\x6a\x08\x2e\xc8\xf9\x26\x41\x39\x64\x88\x66\x07\xde\x87\xd0\xd0\x70\x98\x2e\x4f\x7c\x10\x8c\x80\x2d\x4a\x51\xfb\x60\xcd\x98\x26\xec\x7d\x8b\x41\x98\x8e\xa3\x78\xbd\x76\x00\x06\x84\x7c\xf7\x4a\x24\xf2\x88\x0a\x51\x5f\xd4\x04\xf7\x1b\xe2\x77\x89\xb9\xab\xbf\xbc\xb6\x57\x2e\xbd\x21\xc6\xd8\xe6\xf4\x19\x72\x17\xe0\xe0\xc5\xc8\xc2\x75\xa8\xbd\x83\x7b\xa3\x05\x79\x2b\x28\x47\x1d\xaa\xae\xca\x49\x10\xa7\x44\xd7\x41\x79\x47\xd3\x6b\xf3\xd1\xc1\x0a\xf5\x0c\xad\x10\xfe\xcc\x2b\xc6\x85\x8b\x56\xd3\xc3\x4a\x23\x92\x9e\xd4\x6f\x34\x9c\x3c\x9a\x26\x41\xb1\xcc\xec\xe1\xe0\xf4\xb2\xf1\x60\x98\xf2\xf1\x28\xa8\xaa\x01\x81\x03\x83\xe6\xfd\x17\x2f\x1c\x24\x79\x0b\x8e\x14\x24\xa1\x52\x2d\x15\x29\x04\x25\x9e\x44\x49\x10\xfb\xad\x9e\x79\x1d\x3e\x9b\x52\xbc\xae\xad\x2c\x51\xbd\x81\x14\x99\x47\xcf\x68\x76\x59\xcc\xb8\xc6\x7a\x7e\x1a\x01\xcb\x48\x79\x94\x68\xe8\x9b\x08\xb3\x50\x89\x2d\x8f\x6b\x10\xd1\x1d\xc7\xb3\x9d\x5a\xdc\xea\x17\x7a\x04\x78\xef\x40\x44\xbb\xeb\x50\xfe\x39\xea\x3c\x8b\x48\xbd\xe6\xba\xb5\xf3\xb8\xfd\x14\x95\xf3\x87\xad\xc2\xb7\x20\xf7\xd3\x29\xa9\xbd\xd3\x75\x55\x9a\x62\x9e\x3e\xca\x8e\xdd\x96\xa5\xa3\x10\x16\x95\xfc\x1c\x1c\x2f\x8b\x60\xda\xa2\xfc\x71\x04\x21\xa6\x2c\x63\x00\x01\x84\xe7\x8f\xd1\x8d\x4e\x4e\x96\x71\x5c\xf2\xc2\x45\x6b\x16\x89\x7b\xf9\x6f\x2a\x84\xa1\xbe\xb2\xc0\x8c\x90\x69\x8d\xe6\xac\xe2\xb6\x5f\x60\xdf\x79\x1b\xd3\xe1\xdb\x57\x8f\x9c\xd9\x37\xe7\x5d\x3b\xb6\xde\x4a\xb5\x41\xdf\x6b\x28\xce\x24\x92\x71\x9a\x8c\x83\xa2\x63\xcc\x7e\xb7\xdc\x8f\x4d\x29\xd7\x13\x4e\x6c\xca\xb9\x9e\xbd\xdb\xd2\x32\x0e\x17\xf2\xbb\x07\x97\x87\x09\xae\x20\x0c\x87\xe0\x84\xc0\x6b\x09\x55\xb3\x0f\x1e\x80\xbe\xc1\xec\x45\xf5\x36\x5d\xee\x7c\x07\x70\x70\x87\xde\x77\x82\x6c\x6a\xad\x2e\x2d\x3e\x3e\x33\x4a\x8e\xf0\x97\xf0\xcc\xb3\x89\x3c\xa1\x88\xf1\x89\xfb\x17\x55\xaf\xfd\x52\x8b\x4f\x26\xf9\xa2\xa4\x34\x5c\xdf\x56\x77\x87\xad\xcc\x5f\xd2\x28\xe9\xb4\x5a\x6e\xe5\xea\x51\x1c\x27\x37\x8e\x27\x7c\xbd\x01\xb2\x61\x87\x2d\xf3\x6e\x0f\xf7\x08\x5f\xd5\x24\x69\x71\x60\xf4\x55\xa1\xd0\xe3\x6f\x48\x03\x37\x6c\x1b\x5e\x2d\x74\x7b\x56\x2b\xb8\x7d\xb5\x91\x20\xae\x9d\x2e\x8b\xc5\xb2\xf8\x29\x9d\x6a\x76\x2d\x7c\xf1\xa0\xd5\x22\x9d\xff\x70\x3f\x33\x48\x2c\x33\xc1\x34\xb7\x86\x31\xd9\x6e\xa0\x38\x0c\xbf\xe5\x32\xf8\x69\x46\xc3\xe5\x98\xa2\xb9\x0a\xc6\xe3\x1e\x11\xae\x28\x31\x3f\x09\xc6\xe3\x63\x91\xcc\x79\x22\x43\x8a\xf8\x96\x54\xfe\xcc\x9c\xb2\x7e\x3e\x8b\x26\x45\xa7\x4b\x46\x0e\x46\x65\x96\xa3\xb4\x0a\xc6\x63\xa9\xa5\xe2\xc6\xde\x9c\xb4\x69\x4c\x0b\x2a\xc7\xa1\x9d\x24\x99\xe9\x9c\xaa\x6e\xc0\x32\xd0\xfd\x95\x78\x57\x22\x96\x36\xdb\xea\xb9\x18\x57\xea\x58\xe1\xae\xe4\x22\xa3\xe1\x6a\xe1\xc7\xe3\xb8\xc1\x96\x7e\xfe\xe8\x1e\x99\xb6\xea\x3d\x32\x55\x15\xdf\x2c\xb7\xb1\x33\x2b\x20\x86\x04\x68\xf8\x7e\xb0\xc5\x0e\xdb\xed\x93\x23\x50\xfe\xa1\xfc\x3f\x95\xd2\x32\x36\xfd\x6f\xf0\xa8\xd1\x7a\xd5\xe6\x7d\xd1\x58\x49\x8d\x5f\xcb\xd9\x14\x03\x35\x4f\xae\x65\x1c\x50\xda\x17\x42\x4b\xc7\x08\xe0\xc4\xa0\x5e\x1f\x00\xf6\x5f\xa5\x89\xc2\x0b\x7a\xac\xd8\x3d\x6f\xfb\xa4\x74\x00\x86\xd5\x84\xf7\x4e\xd8\xc0\x25\xf2\x88\x55\x75\x25\x5c\xe7\x27\xeb\x86\xae\xb1\x9e\x36\x51\xc0\xdf\xd6\xd7\xe5\xc0\xaf\x9b\x7c\xc3\x69\xd0\xa3\xff\xab\x0e\x24\x82\x63\x88\xac\x0d\x06\xe4\xe3\xe1\x8b\xc3\x11\xc9\x28\x37\xc8\xea\x91\x3c\x15\xa6\x33\xea\x8a\x4b\xdb\xe2\x04\x5c\xd3\xd5\x67\xe5\xa2\xa2\x9d\x93\x84\x8e\x69\x9e\x07\xd9\x25\x5b\x2c\x10\x01\x3b\x67\xe4\xd6\x06\x7f\xc5\xe0\x2d\x9a\x9c\xa7\xd9\x67\x2e\xe5\xcd\x97\x71\x11\x2d\x62\x14\xc9\xc1\x8c\x9d\xe2\x77\x6f\x34\x78\x48\xbc\xb6\xdc\xdf\x48\x53\x6e\x5e\x87\x69\xc6\x20\x9b\x37\x6c\x48\x75\x63\x34\xe4\x1b\x87\x79\x32\x51\xa5\xfa\x12\x47\x3e\x07\x36\xeb\xac\x73\xc7\x2e\xec\x89\xef\xfc\x50\x06\x6b\xb1\x53\xe2\xd8\x37\x9a\xfd\x14\xfe\x9c\x7c\x35\xd5\x98\x41\x7a\xeb\x29\x3d\x42\xe9\xfa\x05\xc1\xdb\x63\x72\x00\x3c\x47\x6e\x9e\xe3\xc3\x06\xcf\x51\x4c\x4f\x98\xf4\x98\x5d\xf4\x58\x7e\x8a\x62\x39\x2d\xac\x48\x31\x3e\x1f\x57\x95\x07\xb1\xea\xe9\x8e\x68\xc5\x78\x35\x8c\x67\xc8\x65\xf4\x42\x74\x90\x93\xcb\x95\x87\xad\x0a\xde\xc1\xc0\x09\xb2\x1b\xa5\x17\x7d\x83\x1d\xe9\x8f\x1d\x22\x01\x24\x17\x82\xff\x77\x64\xaa\x62\x39\xfc\x87\x4a\x47\x8c\x46\xfe\x34\xe5\x48\x7a\x21\x9e\x77\xbb\xdc\x9c\xa3\x41\x7b\x26\x2a\xe1\xcf\x25\x1c\xb9\x35\xda\x06\x0f\x46\xd8\x69\x38\x63\xcc\xdf\xdd\xdf\x8c\xde\xdf\x8c\xfe\xb5\x6f\x46\xc5\xb5\xa8\x78\xf2\xfb\x5f\x11\x5f\xef\x4e\x3d\x86\xc3\x21\xe0\x21\xd9\x4f\x93\x33\xca\x58\x51\x20\x42\x1e\xc3\x39\x18\xce\x02\x10\xb7\x58\x06\x72\x61\x04\x1c\xc4\x79\x4a\x82\x38\x4e\xcf\x73\x1e\x9e\x1d\x14\x75\x79\x7f\x8d\x55\x24\x05\xff\x37\xd1\x05\x0d\xaf\x79\xd6\x9a\x7b\xaf\xb1\x26\x6e\x54\x8b\xd4\x0e\x72\x2c\x54\x96\xea\xc0\xd9\x31\x55\xa2\xe4\xea\x4a\x06\x48\xd7\x19\x6d\xa5\x43\x6d\x77\x6d\x65\x00\x3f\xcb\x09\x11\x89\x2b\x66\x79\x1f\x3a\x52\xbf\x68\x34\xc4\xf5\x10\x87\x13\x50\x35\x77\xa1\xf6\xa1\x53\x27\x40\x0a\xbe\x8f\x5f\xb4\x1a\x77\x46\x32\x88\x92\x6a\x07\x8e\x5c\x4c\xd4\x64\x9c\x56\x5e\xfe\xd8\x96\xb0\xa9\xd2\xef\x8b\xc3\x56\x8f\x4d\xc2\x19\xcd\xa2\x09\xf8\xf5\xc8\xe8\x38\x60\x1c\x07\x05\xaa\x79\xf0\x80\xc4\xc1\xaf\x97\x24\x4e\x83\x90\x84\x97\x49\x30\x8f\xc6\x24\x4d\x68\x0e\xad\x89\x09\xd1\x0d\x89\x60\xd6\xa9\xd2\x13\x00\x94\xb4\xaf\x97\x8d\x3b\x50\x6c\xb6\xa6\xb4\x38\x54\x87\x64\x8f\x07\x67\x36\x31\x5a\x60\xad\x73\x0f\x80\x95\x09\x62\x4a\xe4\x31\xb9\xfc\xd6\xc3\xd0\xf4\x97\x5e\xbd\xf0\xec\xfc\x3c\x82\x78\x25\xa8\x57\x04\x74\x10\x39\xe5\x27\xe8\x91\xf3\xb2\x8a\x0b\xef\xcb\x8c\x0a\xf5\x62\x0f\x2e\xf0\xc6\x7c\x75\xf0\xc3\xf1\x8c\x5e\xf8\xd4\x06\x5a\x6b\x6a\x25\x58\x9e\x28\x1b\x14\x31\x34\x9f\x22\xac\x76\xa9\x52\xde\x52\xf8\xcb\x20\xdc\xcf\x44\x78\x72\x56\x95\x58\x64\x5d\x32\x92\xeb\x4d\x80\xb9\xb2\x92\xef\x9a\xc0\xf3\xbc\x0e\xba\x39\xb2\xba\xdd\x73\xe0\xd8\x12\xd0\x50\xec\xcb\x85\x29\x52\x5c\x8f\x9b\x1f\xc8\xa8\xcc\x12\x28\xc0\x31\x99\xed\xd6\xe0\xfe\x6a\xb4\xd2\xb5\x56\x5f\x95\xeb\xfa\x7a\x77\x93\x1a\x45\x29\x53\x3f\x85\x0e\x3a\x9c\x02\xf3\x19\xa3\x40\x0f\xc2\x2d\x52\x97\xaa\x9a\xbd\x30\xe4\xcf\x22\x94\x12\x2d\x48\x42\x92\xd3\x22\x27\xcb\x05\x64\x88\xd3\x08\xb0\x8c\xa8\xa0\x19\xdb\x3b\xd2\x33\x21\x6c\x09\x37\xa6\xfd\xb5\x35\xf4\x34\xe2\xa7\x74\x9a\xef\x15\x1f\x8a\x20\x2b\xd6\x6c\x4d\x63\x4e\xe3\x89\x4a\x9c\xb8\xef\x97\x05\x0b\x37\x6b\x31\xe2\x84\xd1\x78\xe2\xf8\xf0\x91\x8f\xec\xa6\xb4\xe0\xfa\x2c\x56\xd8\x7a\x69\x07\xfa\x05\x3d\xcc\x1c\xba\x47\xe4\xc9\xd3\xe2\x19\xac\x95\xbe\x8f\x71\x40\xc6\x94\x16\x1d\xeb\xcd\x8f\xb0\x64\x74\x4e\x39\x83\x01\x09\xd3\xa4\x2d\x5e\x89\xb2\x3e\x0a\xb4\x81\xd9\x24\x5c\x74\xcb\x44\x69\x76\x04\x9e\x30\xfa\xfd\x3e\xf9\x65\xc9\x1d\x01\xb3\x36\x19\xef\x75\xce\xcb\x25\x0f\x23\x2b\x1e\x45\x5e\xdb\x2f\x60\xad\x95\xae\x86\xe1\x3f\x63\xf2\x4c\xef\xc1\x94\x1b\x72\xd6\x3d\xd3\xe4\x8f\x77\x4c\xb3\x4f\xa3\x7f\xf5\x7e\x58\xbf\x1e\xe9\x2e\xd2\x38\xe6\xe4\xe3\x27\x5b\x41\x9b\x1a\xcc\xa6\x4b\xa5\x12\x01\xb5\x6d\xf2\x46\x99\xe1\x1a\xc4\x92\x96\x90\x8b\x98\xd1\xd4\x99\x53\x69\x64\xc1\x48\x4f\x8e\xd5\x37\x09\xbe\x67\x53\x3e\x9a\x48\x1b\x9f\xe4\x9b\x52\xc7\xcd\x28\x43\x9b\x29\xc3\xd0\xb4\xf2\xfa\x99\x95\xa0\x2b\x19\xc9\x42\x2e\xe9\xdc\x0a\x3d\xb7\x23\xd2\x52\x7d\x00\xf4\xc9\x76\x46\xcd\x18\xcf\xbb\x34\x8e\x19\x9f\xd1\x3d\xe1\x34\x38\xe2\x45\xd8\x39\x8d\xce\x69\x52\xc0\x91\xb3\xcf\x28\x0e\x86\xa6\xf7\x92\x85\x30\xb4\x3f\xe6\x98\x02\x72\x3c\x08\x4f\x7a\xf2\x8a\xca\x48\xee\x69\x62\x14\x39\xd8\x8d\x11\x57\x10\x03\xfd\xb2\xcd\x5a\x46\x2d\x74\x48\xdc\x92\xc9\x7a\xc4\x09\xef\x21\x97\x9b\xe7\x76\xa0\x27\x4e\x53\xfb\x19\x85\x31\x81\xbd\xf6\xbe\xe7\xa1\x23\x30\x3b\xae\xc1\x46\x17\xae\x06\x3e\x90\x86\x6f\x15\x55\x59\xa9\xae\xab\x54\xd9\xe3\x57\xaa\x99\x9d\x41\xb6\x04\xa4\xd4\x63\x7c\xa9\x35\xa6\x16\x36\xb5\x18\x6c\x89\xbe\x08\xda\x41\x83\x99\x80\x20\xe5\xcc\xbb\x4f\xc6\xd4\x0a\x11\x96\x35\x2a\x43\x6c\xb9\xfb\x65\xf9\x9a\xed\x39\x59\xf8\xda\x49\xfd\x2e\xed\x77\x3f\xa1\xe7\xe2\xd6\x09\xe3\x00\xfb\x0a\xe3\x4c\x32\x0a\x0d\xd7\x78\x7e\xe6\x58\xb3\xec\x3b\xe3\x53\x8f\x98\x3b\x3e\xad\xe5\x83\x44\x70\x64\x71\x2e\xac\xa0\x5e\xcb\x21\xa9\xcb\x5e\x2a\xca\xfa\xbb\x51\xad\x77\x36\x96\x36\x23\x82\xd0\xf5\x03\x88\x5d\x35\x64\x14\x2e\x19\xd8\x99\x63\x41\x93\x10\x0c\xdc\xd4\x24\x07\x39\x28\x5a\x92\x9c\x51\xa8\xf2\x05\xa3\x2b\x4a\x27\x00\xcc\x0a\x31\xa9\xa7\xcb\x95\x2b\xaa\xf5\x65\x12\xe4\x79\x34\x4d\x68\xd8\x77\xfb\x68\x53\x94\x8f\x27\xfb\x66\x47\xc9\x58\xe3\xd3\x9a\x09\xf2\x36\x83\x4d\xc6\xd0\x48\xb4\x3d\x31\x89\xb1\x74\x18\xc4\x19\x0d\xc2\x4b\xfd\x5e\x5d\x0b\x8a\xf9\xed\x29\xcd\x14\x64\xa5\xf4\x5a\x37\xae\x68\xd2\xb1\x5a\x53\x3e\xe0\x86\xae\x47\x2e\xbd\x32\x39\x17\xf7\xb9\x85\x64\x52\x74\x91\x8a\xb1\x45\xf3\x39\x0d\xa3\xa0\xa0\xf1\xa5\xdd\xac\x20\xf7\x71\x53\xda\x36\xa5\x13\xa8\xbe\x53\xe2\x69\xc2\xe7\xb5\x0a\x6b\xb2\x39\xcb\x67\xdb\x0f\x1f\x0c\xba\xcb\x3d\x77\xa2\x74\xd8\x9b\xb9\xc9\xdb\xb8\x61\x1f\xea\x87\x54\xc7\x18\xcc\x11\x8f\xc6\x9a\x27\x71\x5d\xea\x0e\x04\xe1\x1a\xdd\x09\x5f\x37\x1d\x08\xde\x77\xeb\xc7\xe3\x48\x0e\xe9\x42\x0a\x0e\xe6\x40\x6a\xf8\x3b\x3c\x2d\x9f\xa7\x67\x52\xa5\x49\x82\xfc\x32\x19\xab\xc3\x8f\x4f\x30\xf2\xf1\xed\x65\x02\x6f\xa7\x0d\x04\x20\x19\xc3\xc2\x96\xc3\xbb\xb0\x21\xfc\x2a\x35\x1b\x82\xbf\x83\xd1\xa9\x15\xb2\xdd\xe7\x3c\xc1\x91\x29\xbc\x26\x27\xaa\xa4\x2d\x94\x5b\x3b\x6a\x89\x1d\xe5\x60\x40\x0e\x26\x9a\x33\x46\xb9\x7a\xd7\x77\x49\x85\xfb\x15\x12\x15\x44\x7b\xe9\xd2\xe5\xce\x67\x14\x8c\x31\xc4\xe8\xbb\x84\x33\xd5\x9c\x44\x85\xc9\x56\xbd\x1b\xb5\x43\xec\x6a\x99\xf9\x76\x0f\x1f\xfa\x45\x8d\xf6\x84\xe2\xfd\x18\x22\xa4\x78\xf8\xdb\x57\xf4\xcf\x63\xc9\xe3\x19\xb5\xad\xf7\xe2\x74\x5a\xd6\x2e\xb1\x18\x53\xc5\xd9\x02\x6a\x19\xb1\x3d\xa1\xc4\x1d\x9f\x3f\x60\x89\x09\xe2\x1c\x00\xec\x81\x35\xa7\x23\xc7\xcd\x94\x10\xc4\x0f\x5e\xf0\x84\x91\xa0\xb1\x4e\xb7\xcf\x77\xe4\x71\x20\x1d\x16\x82\x5b\x15\x1a\x12\xb6\xba\x67\x59\x9a\xa4\xcb\x5c\x79\x2f\x14\x86\x01\x6c\xb7\xb7\x3d\x11\xf1\x6a\x84\xb0\xdb\xf6\x9a\xd7\x82\x53\x89\x54\x5b\xe9\x35\x21\x20\xd7\x86\x8e\xd5\x50\x3f\x87\xb7\x98\xb7\xeb\x1a\x7e\xec\x5c\x91\x72\xdc\x3a\xb1\xdf\x2a\x2e\x48\xaf\x4f\x7a\xdb\xc3\x26\x57\xa0\xed\x65\xce\xf5\xe2\xe3\xa2\xbd\x76\x7f\x21\x7a\x7f\x21\xfa\x27\xbe\x10\xd5\x4f\x45\x91\xca\xfa\x26\xef\x45\x05\xf0\x0a\x37\x99\xbe\xd8\x6f\x8d\x9f\x98\x26\x93\x68\xea\x85\xe3\x59\x12\xf0\xe0\x34\xb0\x62\xba\x44\xa7\x41\xe2\x89\xd3\x02\xda\x64\x1e\x68\x8a\xdb\x48\xf3\xcb\xcc\xd3\x68\x2a\x3c\x18\x58\x56\x8c\x1c\xe8\x79\x34\xb5\x94\xfa\xd8\x9a\x91\x6b\x9c\xaf\x38\xc4\x95\x82\xbd\x36\x9d\x56\xe9\x74\x6c\x89\x0b\x7a\xc6\x92\x36\x0c\xa9\x88\xf7\xce\xfb\x0c\xad\x48\x55\x59\x09\xb6\xa3\x94\x40\x51\xfe\x2e\xa3\xe2\x1a\x14\xdd\x4e\x18\x75\x9f\xea\x74\xab\x81\x63\xe5\xee\xbe\x2d\x4e\x9e\xed\x5e\x9b\x06\x59\x1c\xf1\xc4\x71\x3a\x9f\x47\x45\x41\xc3\xf6\x89\xba\x22\x35\x6a\xfb\x61\x97\x0c\x51\x67\x92\xc5\xb2\x78\x41\x27\xc1\x32\xf6\x5e\x95\xd4\xf5\x8a\xed\xc1\xa7\x78\x10\xf8\xa1\x8c\x37\x5e\x0b\x23\x92\x7e\x88\x5a\xf4\x78\x9b\x2a\xbf\xb9\xc1\x5d\xb0\x46\xf1\x5b\x74\xdf\x7e\xc3\xc5\x45\x12\x56\x4b\xc9\xac\x1a\x8d\x7a\x2a\x44\xd9\x1e\x3c\x48\x6a\x7a\x4d\x2f\xaa\x46\xfe\x72\x91\x8e\x67\x55\x23\xa7\x1a\x80\xf7\x01\x44\x4c\x9d\xe8\xbe\x6f\xb2\x33\x5b\x9c\xea\x5a\x16\x35\xca\x64\xd6\x77\xd6\x73\x4f\xbf\x71\xdb\x86\x39\x33\xef\x6a\x8e\xcc\x37\xd3\x09\x09\x0c\x27\x86\x41\x12\xca\x3b\xdd\x1c\xee\x74\xb8\x05\x03\xe3\x10\xaf\x5f\xfe\xd3\x62\x0c\x50\x07\x93\xe0\xbd\x2c\x41\xde\x3a\x18\xce\x80\x1d\x13\x7d\x79\x99\x2f\xef\x25\xdc\x3a\xbd\x21\xca\xbf\x18\xd7\xdc\x70\x51\x89\x2e\x8b\xe1\xf3\xea\xca\xa2\xfd\xbd\x31\x04\x88\x40\x2e\xda\x30\xbc\xc7\x37\x98\xac\x16\xfa\x24\x1c\x66\xf9\x2f\x49\x4d\x89\x0d\x57\x5d\xa4\x22\xb2\x75\x54\x90\x79\x34\x9d\x71\x11\x57\xb9\x59\x16\xea\x34\xa7\xe5\x22\xad\x6d\xb7\x48\xcd\x56\x8f\xdb\xf3\xe0\xe2\x15\xa5\xef\x68\xf6\x63\x90\xb7\x7b\x84\x7d\xbf\xcb\xa2\x34\x8b\x8a\x4b\x23\x7d\x1a\xe4\xef\xb2\x68\x4c\xc5\x6f\xf6\x1f\x4c\x33\xfb\x91\xa4\xc9\x98\xfa\xde\x5b\x7e\xa6\x97\x15\x2f\x2e\x3f\xd3\xcb\xa6\x6f\x2e\xa1\x26\x07\xd7\xbc\x86\x5d\x64\x21\xf2\x82\x8e\xa3\x79\x10\x77\x30\x80\xfb\xe6\xcd\xbc\x16\xfe\xda\xc4\x8e\x9c\x83\xde\x35\xcd\xfb\xaa\xbe\x7b\xd2\xbf\x29\x75\xdf\xd3\xf5\x1f\x91\xae\x85\xf8\xe6\x10\x36\xdc\x14\xcb\xa8\x47\x82\xaa\xbd\x42\x5d\x63\x7a\xbe\x30\x05\x39\x91\xbe\x66\x48\x6f\xb5\x14\x5c\x5c\x74\xbf\x28\x1d\xe6\x45\x1f\x8b\x01\xeb\x52\x8f\xa0\x75\x77\x26\x80\xf2\xe5\x91\x4a\xfc\x99\x00\xea\xb5\x0a\x4b\x47\xb8\x80\x77\x71\xfe\xea\x1d\x28\x6f\x1b\x36\x94\x54\x53\x5e\xf4\x81\xa4\xfc\x85\x20\x4b\x43\x4e\x83\xdc\x0f\x37\x0d\x72\x03\x0a\xc8\x17\x81\x6a\xa1\x16\xe5\x1b\x43\xc5\x6b\xc3\x24\x54\x43\x11\x6a\x01\x96\xb4\x80\x61\x0c\x9f\xa4\xaa\x2d\x67\xdd\xd5\xb5\xe9\x16\x28\x6f\xdb\x81\x35\xfa\x50\x5c\xf4\xa5\x99\xa2\xb7\x02\xfc\x28\x5a\xea\x4c\x2e\x56\x5e\x3a\x32\x9e\xd0\x4d\x96\x90\x08\x6d\x54\xb9\x92\x54\xa4\xad\x55\x96\x93\x5d\xb1\xe5\x60\x07\x87\x48\xd2\x21\x91\x6a\xd6\x97\x0f\xca\xa5\x51\x0f\x94\x26\x3f\x99\xd9\x60\xb9\x95\x82\x96\x37\x59\xb2\xf0\x54\xe4\x9e\xe5\x7c\x19\x07\x45\x74\x46\x7f\x0c\xf2\xa3\x1c\x5e\x20\x96\x55\xe5\xc0\x5a\x75\x4d\x6b\x6b\x98\xaa\x72\x72\xf0\xa6\x51\x89\x84\x8b\xd3\xa9\x6d\xa3\xa9\x33\x50\xdc\x21\x47\x89\x08\x9a\x40\xaf\x0a\xd1\xf3\x8e\x99\xc1\xd6\xe9\x0b\x45\x4b\x8d\x16\x00\xcc\x6e\x73\x92\x87\xd3\x56\x25\x95\x43\x85\x0d\x68\xdc\xac\x09\x5b\x22\x41\x0d\xca\x14\x69\x30\x20\xca\x89\x13\x78\x33\x14\x7a\x0a\x7c\xa0\xec\x9f\x06\x39\x6d\xc0\x97\x7c\xc0\x3e\x96\xe2\x81\x33\xf8\x11\xcf\x9f\x06\xf9\x4f\xd1\x3c\x2a\x3c\xb4\x63\x02\x88\xb2\x2a\xb1\x84\xe0\x8c\x7c\xa3\x4c\x1e\xfd\xea\xdb\x6d\x74\xa6\x01\x5d\x44\x73\x9a\x17\xc1\x7c\x51\x5a\x44\x41\xe8\x05\xcd\x33\x92\x32\x96\x61\x64\x97\x55\xab\xb4\x2f\xa8\x33\x61\x34\x99\x44\xe3\x65\x0c\x2f\x80\xca\x30\xad\x81\xcc\x81\xa4\x45\x10\xbf\x68\x52\x81\x05\x89\xa5\x56\x73\xb1\x0a\x70\xcd\x5f\xcc\x25\xeb\x66\xbb\xb2\x5e\x54\xd0\x79\xd7\x7e\xfb\xe7\x18\x60\x02\x94\x7b\xd5\x6d\x2c\x6c\x9f\xd4\xc4\x0b\xd6\xad\xf0\x53\xae\xcb\xb9\xde\x69\xb4\xbc\x3f\x44\xd3\x84\x66\x24\x8e\x72\xfb\x95\xf2\x4a\x8b\x9a\x57\x93\xfb\xd7\x36\x71\x17\xb7\x80\x2f\x5f\xe3\x02\x40\x6b\x49\x3c\x73\x25\x61\xe4\x2c\xe1\xc4\x9a\xb9\xa9\x9f\x15\x81\x4d\xae\x11\x3d\x84\x9e\xcb\xe0\x0b\x68\x1a\xf8\x14\x20\x8d\x0b\xee\x43\x23\x26\x1b\xa7\x53\x2f\xe2\x31\x67\xf7\xa1\x3d\x4e\xa7\x5a\x5b\xea\x22\x1d\xea\x35\xf0\x8e\x2b\xc4\xe8\x46\xb7\x54\xd1\x84\x7d\x19\xbb\xba\xc2\x87\x95\xe1\x59\xe8\x76\xd1\x1d\x5c\xa7\xb3\x6d\x1b\x15\x37\xd8\xff\xbd\x95\x18\x4d\xc4\xe9\xd4\x53\xb5\x4c\x2d\xa9\x52\x15\x32\x8f\x58\x70\xf3\x56\xaf\x36\x38\x9f\x45\x39\xdb\x15\x17\x69\x5e\xdc\x40\x6f\xf0\x2e\xcd\xab\xc5\x42\x37\x62\x56\xe5\xee\xe9\x56\x8a\x27\x9a\x75\x12\x6f\x9d\xec\xbb\xbf\x08\x2e\xe1\x19\xcc\xae\xa1\x2b\xc4\x59\x02\xc9\x90\x54\x14\xb1\xf7\xd0\x2a\x33\x31\xec\x79\x9a\x7d\xfe\x98\xbe\xcb\xd2\x33\x5a\x5e\x06\x01\xe1\xb2\x0b\x21\xf2\x97\x17\x94\x10\x28\x10\xc4\x04\xc7\x09\x33\x0c\xdf\x39\xcf\xe0\x9d\xe4\x5e\x71\x30\x63\x47\xe9\x64\xd7\xf8\x7a\x46\x8e\xd1\xe7\x09\x19\x29\xab\x93\x6b\xdd\x2a\xbf\x32\xe1\xb7\x27\x71\x9c\x9e\xc3\x2b\x20\xa9\xdc\xa9\xaa\xbe\xfa\xd5\x0a\x8f\x74\xc9\x88\x89\xa4\x49\x7c\xc9\xc3\x77\x14\xc6\x63\x1a\xf9\xa0\x85\x3f\x5c\xf1\xbd\xc3\x92\xaf\x5a\xc8\xc8\x7e\x63\x85\xdf\xb3\xd8\xfa\x05\xd6\xc7\x46\xbc\x4b\x5d\xdf\x01\xfd\x0b\xa3\x62\x2f\x37\xab\xa3\xf4\x26\x1b\x47\x35\x61\x0b\xba\x06\xfc\xd2\x8b\x45\x94\x5d\x7a\x56\x3c\xca\xc5\xe4\x96\x73\x6f\x3f\x5e\x68\x96\x57\xb6\x04\x2c\x50\xcf\x02\x00\xca\xf6\x09\x74\x16\x44\x77\xc7\xb7\x2a\xdf\x07\xe7\x92\x64\x44\x8a\x17\x0c\x55\xbf\x97\x8f\xa3\xc8\x5e\xbe\xb2\x0c\xde\x46\xff\x9e\x0b\xc4\x29\x38\x1d\x2f\x47\xaf\x0a\xdd\x00\xb8\xbf\x86\x98\x75\x3e\xe6\x30\x18\xac\xb2\x22\x60\x6d\xe2\xd5\x58\xba\x18\xf5\x72\xbb\xc5\x4a\xb2\xee\x42\x38\x8a\x9a\xd1\xbf\x62\xaa\xb6\x5a\xd2\x17\xdc\x06\xdf\x65\x89\xa4\x7e\xbe\x3c\xe5\x0f\x03\x3b\xc3\xde\x36\x5f\x95\xad\x8b\x70\xdc\x32\x9c\x3c\x29\x2f\x52\xad\xe1\x45\x8b\x6c\x10\xb7\xf0\xb6\x71\xc4\x80\x5e\xf1\x6b\xdd\x84\x9e\xc3\x0d\x6f\xc7\x8c\xad\x0e\x17\x61\xa7\x41\xd2\x8f\xf2\x7f\x04\x71\x14\x76\x20\xf6\x89\x48\x79\x11\x65\x74\x5c\x74\x7c\xb7\x60\xc2\xc5\x1c\x00\x8a\x1a\x3b\x5d\xe7\x8a\x0d\x0b\x4e\x3a\x24\x95\xec\x81\xa7\x5a\xc3\x8b\xa1\xa7\xa2\x06\x55\x88\x9e\x99\x35\x71\x05\x90\x6d\x53\x24\xfc\xcc\x4b\xd8\xb6\x8c\x11\xaf\x39\xc9\x87\xcb\x64\x1c\x25\x7e\x71\x48\x38\x76\x47\x73\xb9\x6e\x26\x11\xd7\xcf\x95\x21\x84\x83\x57\x2c\x30\x4a\x8d\x92\x29\x08\xbb\x5e\x05\x82\x0b\x66\xfa\x16\x13\x6e\xbe\x6a\x2a\xc0\x50\x66\xf9\x59\x34\x9d\xd1\xbc\xae\x3c\x86\x42\xb4\x23\x72\x3f\x27\xe9\x79\xf2\xa1\x08\x0a\xea\xf3\x33\x89\x72\xcb\x1b\xc0\x55\xec\xd8\x35\x2c\x96\x71\x4c\xc3\xba\x2a\x30\x54\x89\x4e\x43\xbb\x1b\x2b\x89\x28\x51\x77\xbd\x3e\xaa\x85\xe8\xe9\x7a\x2a\x2a\xa8\x29\xe9\xbb\x20\x1e\x95\x67\xa1\x92\xc6\xdd\xe7\xc8\x93\x86\x60\x7d\x67\xc7\x51\x79\x16\x2a\x69\xb3\xb9\x91\x3f\x19\x95\x30\x36\xe5\x91\x27\x8d\xc3\x96\x19\x72\x8c\x4a\x73\x70\x39\xff\x80\xca\xf3\x4a\xca\xda\xfa\x52\x4f\x15\x36\x88\xd1\x7b\xe3\x28\x3c\xf2\xa6\x3a\xf0\xf6\x41\x77\x54\x95\x89\x4b\xe3\xe3\xda\xc8\x93\x86\x61\xad\x49\xf0\x24\x62\x68\x9b\xfb\x8d\x4a\xd2\x39\xd7\x34\x6c\x07\xf9\xf5\x61\x6b\xb4\xf9\xb4\xcc\x23\x16\xdb\x3a\x5a\xa3\xed\xed\xeb\x93\xde\xf6\xe6\xbd\x37\x95\x7b\xe3\xc1\xff\x1a\xe3\x41\x41\xe9\x77\x11\x16\x69\xb5\x18\x12\x0d\x2d\x06\x79\xd4\x26\xd3\x14\x90\xa7\x7d\x85\x60\x14\xcd\xc3\x47\x04\x71\x3c\xb0\x02\xac\xc2\xbb\x70\x3b\x3c\x93\x1b\x54\x42\x3e\x6e\x70\x23\xd1\x55\x04\x93\xf0\x85\xa2\xfb\xc4\xb7\x46\x11\xec\x00\xc7\x60\x5e\x3d\x10\x81\xae\x54\xec\x2d\xb8\x56\x9e\x74\xbb\x6a\x21\x7e\x63\x00\xe7\x55\xa8\x53\x7e\x63\x18\x19\xa3\x59\x80\x88\x4f\x0c\x71\x27\x81\x30\xd8\xfe\x60\x4f\x86\xe1\x32\x15\xac\x3e\xf4\x53\x42\x7c\x64\xca\xa6\xc6\x79\xe9\x06\x91\xc7\xe5\xd9\x42\x87\x69\x04\x7f\x24\xc0\xeb\xf9\xe3\xb7\x6c\x9a\xf3\x68\x17\xeb\x42\x68\x6c\xd6\x61\x2c\x04\x56\x76\x1a\x77\xef\x07\x87\x94\x64\x0e\x0e\x3a\x29\x5e\xd9\xba\x83\xf3\x8f\xcd\xf6\xa1\x51\x21\x9e\x76\x34\x1e\x1a\x22\xa2\x2a\xb4\x24\x0e\x86\xed\x8b\x9f\x16\xe5\x64\x9c\x66\x99\xeb\xda\x14\x4e\x5e\x41\x41\xf7\xb2\x69\xee\x8b\x36\xa9\xc3\xdd\x3f\x24\x7f\x83\x93\x5b\x4e\xbe\xc0\xb9\xed\x9a\xb5\x17\x15\xe2\x6d\x91\xe1\xfd\xd4\x33\x55\xb8\x9d\xd2\x39\xd2\xa7\x77\x0e\x05\x28\x72\x2c\x7d\x02\x8d\xf8\xc1\x40\x3e\x22\x03\x4d\x97\xe1\x56\x08\x36\x4f\x70\x66\xa9\xa3\xc8\xb1\xad\x36\x80\x27\xa8\x59\x70\x29\x1f\x54\x8a\xb9\x5b\xef\x38\x41\x48\x83\xae\x72\x8d\xcf\xce\xe3\xce\x05\x90\x75\xc7\x21\xc0\xb9\x9b\xeb\x4a\x78\x7d\xe5\x65\x94\xb1\x0a\x58\xaf\xb9\x41\x47\x20\xb1\x23\x09\x71\x7d\x77\xb7\x8c\x90\xcd\x17\x74\xec\xcc\x2d\xe2\x00\x56\x44\xeb\xeb\x38\x8e\x2d\xaa\x5c\x41\x4b\x6d\x13\x58\xa0\x61\x52\x31\x82\x99\xa4\xef\x38\x98\x87\xbc\x9c\x4d\x43\xbb\x80\x2f\x71\x0b\x1d\xc4\xaa\x55\x6d\x90\x57\x49\x79\xaa\xfd\x4a\xb2\x33\xc2\xdf\xae\xce\x30\x56\xe5\x17\x66\xd4\xda\x92\xb0\xb8\xd7\x9a\x9b\xe3\xe5\xd3\xf1\xc4\xa8\x2d\x52\x7f\x04\x0a\x23\x86\xed\x2e\x29\x89\x2e\xe1\x0b\x52\x20\xde\x4f\xa1\xe1\x1a\xc1\x71\x2b\x2c\xdb\x4a\xa2\x27\x49\xd4\xdf\x2c\x4a\x8c\xb7\x78\xe5\xbc\xdf\x28\x56\x8c\x70\x73\x3f\xec\x91\xa7\x52\x0b\x55\xd1\xc4\x32\x59\x04\xe3\xcf\xfc\xae\xd1\xb4\xf1\x84\x24\x43\x27\x65\x26\xe9\x2e\x18\xfa\x91\x54\x56\xc5\x7f\x28\xd2\xdb\x25\x5b\xe4\x99\x4c\x94\x9e\xf8\x89\x3c\x07\x6a\xd7\x14\xca\x7f\x7e\x99\x23\x7e\x2c\xe4\xf4\x44\x71\x73\x46\x85\x0e\x07\xbb\x11\x57\x31\x18\x8f\x87\x27\x64\xe4\x73\x16\xbf\x0f\x21\xc8\x03\x14\xf5\x5d\x22\xcb\x8e\x2b\x1f\xc4\x31\x5e\xdc\xfd\x7e\x5f\xae\xef\x7d\xbb\xac\xb5\xf9\x38\x6e\x9a\x0e\xf8\x76\x07\xe1\xa5\x25\x28\xdb\x8d\x02\x55\x43\x8f\x7b\xe4\xb1\x2b\xe6\x3e\x09\xe1\x0d\xac\x3c\x74\x05\xc6\x2b\xc5\x20\x09\x4d\x5f\x3e\x12\x8c\xc7\x5f\xe7\x27\x23\x56\x07\x0f\x5e\xc9\xc0\x05\xda\xbc\xb4\x2b\x66\x15\xe2\x47\xd7\x51\x2d\xf4\xaa\x2c\x46\xf7\x2a\x01\xb8\xfd\xfb\xa6\x94\xc1\x2c\x5b\x58\xb5\xc7\xc0\x41\x46\xcb\x7f\xc2\x75\xb7\x21\x16\x62\xf6\x03\xee\xc7\x4d\xe9\x0b\x17\xc1\xe2\x8f\x5d\x4c\xdf\x54\x70\x97\xe1\x92\x4b\x4b\x38\x6d\xf4\xb1\xee\x7b\x12\xae\x75\xc3\x8a\xf1\xd1\x62\xc6\x91\x20\xaa\xee\x19\x5d\x73\x1f\x80\x42\x29\xbc\x84\x3b\xc6\x7a\x40\x4e\xef\x9d\x27\xdb\x4d\x1a\xec\xb9\x8e\x96\x5c\x1e\x80\xdc\x2c\xc9\x77\x3e\x86\x33\x8e\x1e\x37\xdd\xd9\x31\x9d\x5b\xf3\x4e\xd3\xd0\x71\xe4\x5f\x64\x97\xd6\xfb\x55\x04\x0a\x4f\x56\xcb\xc7\x4b\x8c\x37\xb6\x63\x70\x72\xd0\x71\x5c\x25\x71\x8a\xdf\x25\x14\x17\x42\xa5\xcc\xce\xcb\xd6\x91\x24\x53\xb9\x51\x34\x39\x57\xda\xdb\x86\x59\xa4\x76\x57\xb0\x5a\xf8\x53\x2d\xb5\xda\x35\x23\x49\x4a\x00\x0a\x43\xdb\x1f\xc8\x10\x0e\x35\xc6\x59\xd3\x95\x0e\x71\xe4\xda\x20\xe1\xfe\x02\x92\x50\xf8\x14\x85\xd0\xc3\xc9\x23\x79\x50\x75\x62\x30\xd7\x2c\x57\x23\xee\x20\x5b\x37\xd6\x3c\x74\xcc\xeb\x49\x51\x5d\x2d\x78\xf3\x80\x0f\x34\x2f\xa2\x79\x50\xd0\x1f\x03\x50\x20\xd6\x51\x15\x02\xaf\xa3\x28\x5c\xf3\x5d\x50\xd3\xd7\xa7\x8e\x66\x33\x84\xc6\x55\x37\x3b\x1e\xd0\xb2\x99\x79\x2f\x9b\xa1\x32\x84\x1d\x84\xe2\x91\xba\x40\x21\x1f\xe0\xa9\x98\xd2\xe2\x85\x1d\x62\x4a\xee\xac\x76\x35\x75\x73\x25\xea\xba\xe3\x79\x6a\x84\x78\x79\x55\x2d\x56\x26\x8f\xce\xd3\x5c\x6a\xbe\x45\x20\x4c\x5c\x54\xe2\x19\x91\x7d\x25\xc2\x7e\xdb\xa8\x98\xaa\xfe\x1b\x05\xc6\x54\x85\x56\x1d\xe4\xd7\x8c\x92\xa9\x75\x34\x6c\x80\xd9\x62\x2c\xdd\xaf\xe5\xfc\xd4\x5c\xc7\x88\x04\x74\xb9\xb9\x4d\xc5\xb8\x44\xd9\x3f\x36\x57\x22\x46\xb4\x20\x09\x86\xc5\x14\x23\xe8\x0d\x9e\x13\xd7\x5f\xa1\xa5\x71\x7d\x06\x4e\x90\x3f\xb1\x1e\xb7\xc9\x88\x7f\x58\x3b\x49\xbb\xe7\x08\x2f\x23\xed\x27\x50\xe5\x29\x0f\x87\x62\x38\x27\x3a\x8b\x77\x5c\xfa\xcf\xe5\x0c\xb2\x96\x18\x64\x78\x9f\xb2\xed\x47\x05\xce\xaa\xde\x7a\x3c\x31\xae\xf0\x04\x17\x86\xa0\xb3\x6e\x62\x47\x9b\x19\xc1\x36\x5f\x60\x19\x4a\xfa\x88\xd1\x69\x65\x5b\x85\x85\xce\x7e\xb0\x58\xc4\x97\xc2\x63\x55\x23\xc2\xea\xda\xf6\x79\x7c\x0b\xb0\x9a\x61\x89\x37\xaa\xbb\x66\x1e\x44\x1c\x28\xcd\x78\x74\x28\xa8\x5b\xc7\x80\xf2\x4c\xd8\xd7\x0a\x03\x25\xd3\xf5\x8a\xc7\x2e\xbb\x4a\xc1\xc5\x61\x53\x63\xb8\x0c\xd0\x95\x9a\xbd\x93\x5f\x56\xdc\x14\x91\xf8\x48\x74\x52\x69\x31\xbd\x5b\x4b\x57\x53\xec\xf3\x4f\x19\x03\x4b\x96\x05\x02\x8f\xb2\xf1\x32\x0e\xb2\xf5\xf5\xf5\xf5\xea\xc8\x57\x92\x82\x76\xee\x24\xf6\x15\xd7\xfe\xb6\x46\x5b\x4f\xfc\x8e\x84\xb6\xee\x6f\xff\xef\x6f\xff\xff\xda\xb7\xff\xe2\xea\x9f\xc1\xca\xd8\x64\xfe\x88\x2a\xbf\x5b\xac\x14\x9f\x65\x41\xb5\x21\xc0\xda\x60\x00\xb1\xd7\x82\x8c\x91\x32\xdb\xc1\x96\xb9\x39\x44\x46\x70\x61\x34\x99\xd0\x8c\x26\x05\xa1\xc9\x59\x0e\x85\x4e\xb3\xf4\x3c\xa7\xd9\x1a\x72\x2c\x7b\x1e\x25\x61\x7a\x0e\x1a\x0b\x14\x71\x84\x3c\x78\x20\x72\xfa\xff\x7c\xf3\xd3\xeb\xa2\x58\x08\x9f\xc5\x9c\x6b\x9a\x69\x64\xd7\x0f\x0b\xac\x4f\x04\xcc\x88\xa6\x49\xca\x18\x41\x1c\x25\x94\xf5\x24\x49\x43\xba\x86\xbc\xd4\x39\x35\xaa\x81\x5f\xcc\x63\x36\x32\xb1\xb1\xb5\xbb\x4d\x1b\xb9\xe6\x98\xfc\xe7\xeb\xf7\x5b\x46\x75\xb3\x6c\xab\xdd\x2d\x2d\x25\x25\x07\xd6\xc2\x3b\x89\x4c\xd7\x24\x02\xe4\x27\x26\xda\x83\x9b\x56\xee\xd4\x9d\xf5\x52\x19\x40\x18\xe5\xf1\x96\x3f\x4b\xf3\xa2\x47\x8a\x68\x4e\xd3\x65\xd1\x63\x15\x66\x3d\x50\x32\x9f\xa7\x99\x78\xec\x08\x9b\x09\x83\x23\xbb\x04\xfe\xbb\xba\x22\x6d\x41\xec\x71\x3a\x0e\x62\x96\x38\x7a\xfa\xcd\xe3\x6f\x20\xc0\x31\xdf\x7b\x78\x85\x6c\x27\x14\xbf\xae\xae\xc8\x50\x65\xb3\x66\xc8\x2e\xb4\xa6\xd2\x64\xa3\x64\x57\xb5\x5f\x2b\x3c\x2d\x32\xba\x80\x88\x81\xf4\xdc\x9a\x32\x4b\x76\x12\x80\xef\xd1\x59\x46\x48\x4e\xcf\xd3\x34\xa6\x41\x72\x0d\x77\xac\x6c\x7f\x96\x12\x8c\xc6\xb2\x70\x0f\x8a\x0e\x7c\x66\x5b\x86\x0f\x2a\x8c\x69\x24\x77\x99\x1d\x30\x2f\x02\x59\xf5\x1c\xd5\xfc\x06\x85\x13\xd2\x9a\x78\xc5\x86\xb2\x09\xd1\xe2\x15\x0c\xf9\xf5\xfb\x2d\x1d\x5f\x98\x4b\x5a\x08\xf3\x68\x22\xe0\xc9\x19\x76\xc2\x68\x55\x64\x8c\xa7\x23\x5e\xa8\xad\xe9\x5a\xd3\x05\x4d\x3a\xed\x77\x87\x1f\x3e\xca\x90\xa8\x9c\x70\x78\xe7\x76\xd6\x90\x47\x47\x98\xdb\x07\x0f\xcc\x49\x35\x0e\x7d\x4b\x30\xa8\x69\x3f\x0f\xf2\x68\x4c\xda\x64\x03\xba\xf0\x7c\xc9\xd8\x03\xaa\x62\x83\xb4\x47\xea\xaa\x50\xd5\xd3\x2f\x52\xf1\xf8\xae\x7d\x1a\xe4\xf4\xc9\xe3\xb6\x35\x7e\xed\xcf\xfc\x35\x0d\x42\x9a\x75\xda\x7b\xc0\x57\xa3\x5f\x03\x7e\xda\x82\xf6\xf9\x08\x2b\x0a\x31\xf9\x98\x26\xc5\x23\x76\xd0\x6e\xf7\x48\x9b\x49\xfe\xd1\x18\xaa\x18\xfc\x92\x4b\xb5\xa3\xba\xb1\x12\x53\x56\x43\xae\x3c\xf2\xcd\x65\x32\x46\x87\x6a\x5b\x93\xec\xbb\x78\x5e\xa0\xeb\x6b\x7f\x8c\xf3\x2a\xd2\xcb\xed\x98\x97\x52\x97\x66\x93\x9c\xa4\x19\x93\x56\x45\xd0\x6c\xa0\x47\xad\xdd\xd7\x98\x4b\xc2\x0e\xbc\xf4\xe0\xef\x0e\xa2\xc9\xa5\xaa\x5f\x20\x59\x2a\xf2\xb1\xbb\x72\x9f\x35\xc0\x7e\x9a\x24\x54\xbc\xc7\x90\x14\xa6\x29\xd1\xb8\x5c\x94\xad\xcb\xc0\x21\x1f\xe9\x45\xe1\x74\x50\xc0\xa2\x67\x28\xc2\x2a\xdf\xec\x56\x55\x97\xde\x8b\xfa\x3b\xbe\x06\xf1\x2a\x69\x1e\xc3\x1a\x68\x20\xa8\x21\x82\x3d\xc5\x71\x2a\x28\x41\x64\xbd\x72\xa2\xc6\x90\x22\x8b\xa6\x53\x9a\xf1\x50\x57\x6c\xf6\x41\x6c\x51\x7e\x6b\x19\x0e\xea\x08\x06\x7a\xe0\xa3\x1a\x33\x62\x75\x13\xfa\x01\xe3\x95\x1d\x83\x9b\x24\xe0\x63\x3c\x2f\x82\x82\x8e\x67\x41\x32\xf5\x2b\x10\xf8\xb3\x02\x89\xf8\x20\xbc\x04\x83\x7e\xb8\x11\x7e\xcc\x38\x8c\xcd\xf2\xd6\xcd\x88\xd3\x0d\x28\x46\x03\xca\x5b\x25\x14\xca\xcc\xbe\xcc\xaa\xa1\x28\x38\x93\x79\x6f\xad\xd4\x8d\xd5\x8a\xb4\x45\xf0\xd5\x96\x7d\xb1\x65\xb4\xcc\xce\x82\xd7\x16\x8a\xf5\x46\xe0\x62\xd6\xac\x2c\xef\xeb\xa5\xf7\x91\x97\xea\xe0\xcd\x43\x2c\xe4\xbb\xe5\x00\x76\x17\xaa\x98\x80\x58\x69\x78\x5d\xe9\xcb\xf2\xf8\x92\xd1\x3b\x7f\x34\x0b\x8b\x8b\x51\x75\xc9\xda\x8a\x72\x51\x3f\x35\x99\xa9\x12\x02\xa4\x82\xd3\x16\x06\xd8\xf9\x21\x69\x17\x64\x12\x44\x31\x0d\xfb\xe4\x90\x9d\xd3\xce\x23\x76\xf6\x08\x20\x3a\x5d\xf9\x6a\x42\x6d\x7a\xe6\x42\xe3\x53\xe9\x33\x54\x14\x94\x28\x1c\x91\xef\xd4\x9f\xd4\xf7\xb1\xdd\x27\x5b\x8c\x47\xa4\xbd\xd5\x1f\x2a\xe5\xa1\xd4\x3f\xb6\x13\x5a\x7c\x8a\xa3\xbc\xa0\x09\xf8\x97\x5c\xb3\xb4\x87\x27\x86\x41\x97\x54\x70\x65\x3c\xd4\x9e\x4b\xbe\xd2\xaa\x90\x0d\x52\x4f\x82\xa3\x2e\xc0\x43\x97\xaa\x02\xe3\xb4\xcf\xc4\xdc\xd6\xe8\x29\xfb\x65\xc8\xcf\xad\xd1\xe6\xb7\xec\xe4\xbf\x7d\x7f\xf2\xbf\x3f\xf9\xff\xc5\x4f\xfe\xda\xf0\x1f\x1e\x4b\xde\x91\xd1\xbf\x32\xe4\xc4\xa7\xca\xd3\x68\xca\x6d\x70\xfb\xbf\xf0\x13\x3a\xbf\x07\x09\x7f\xa2\x13\x73\x43\x50\x31\x47\x2f\xd1\x83\x3d\x63\xe3\xe4\x10\x9c\x5d\x9c\xcf\x58\xef\x3b\xa6\x81\xd6\xf7\xbc\x30\x79\x48\xb6\xdc\x17\x7f\x60\xf1\xc7\xa4\x78\xf3\xdd\x23\xf1\xbf\xc4\x13\xcc\xfd\x9d\x38\xd5\x05\x09\x39\x78\xbe\xf7\x56\x4c\x72\x48\xbe\xfb\x96\x8c\xd3\xf9\x62\x29\xe2\xfd\x9c\x5e\x92\x79\x7a\x16\x25\x53\x14\xd5\xee\x31\x19\xcf\x82\x0c\xf6\x02\x7e\x33\x1b\x72\x53\x2a\x69\xae\x2e\xa1\x63\xca\x1f\x2d\x14\x29\x6b\x90\xe3\x2a\x27\x9d\x3d\xb2\x4b\x36\x87\x3d\xf2\x9c\xfd\xbf\xd9\x23\xfd\x7e\xbf\x47\xfe\x8f\xec\x92\xed\x6f\xba\xec\xb0\x43\xf2\x05\x1d\x47\x93\x88\x2f\xa4\x83\x0f\x87\x9b\xdb\x4f\x36\x9f\xd8\x26\x66\x51\x9e\x42\xba\x18\x87\xeb\xdd\xf8\x9a\xbf\xc5\x65\x1d\x61\x03\x34\xaf\xd6\xf0\xcd\xb2\x90\xa4\x42\x09\x26\x7c\x36\x98\xf5\x1b\x13\xca\x2a\xc6\xf3\xc8\x46\xd4\xde\x6b\xf7\x19\x5a\xf6\xd3\x90\xee\x15\x9d\x21\xd2\x5a\xb3\xb1\xb5\xff\xcf\xc9\xe6\x0c\x90\xbf\x17\x06\x62\x2d\xd2\xa3\xc5\x82\x66\xfb\x41\xae\x55\xd9\x28\x9b\x3f\x3b\xee\x3c\xee\xca\x97\xc0\x22\x61\xd8\x7b\x6c\xdd\x98\xf1\xdc\x45\x1c\x15\x9d\x76\xbb\x6b\xbe\xc2\x4e\xba\xa6\x75\xd5\x38\x0d\xd9\xe0\x12\x5f\xe7\xa5\x7c\x08\x30\x3f\xec\x92\x3d\x26\x10\xc2\xc7\xf7\xbb\xe4\xff\xba\x4e\x2c\x0a\xcf\xcc\x8a\x89\x35\x20\x95\xab\xe3\x90\x92\x47\x64\x8f\x6c\x90\xcd\x21\xb2\x33\xf2\xc5\x67\x90\x31\x70\x6d\x1b\xa6\xeb\x6e\xff\x97\x34\x4a\xd8\x30\x6d\x4b\xc5\xf1\x12\x7c\xef\xc2\x14\xbf\x39\x7c\xc1\x08\x7b\x73\x28\x99\x92\xb0\xf0\x03\xca\xf7\x50\xdc\xb7\xc3\x27\x8f\x6d\x82\x9b\xa7\xe1\x77\xdf\x6e\x0e\xcb\x08\xcd\xa4\x2f\xed\x4f\x9b\x53\x93\x28\x5c\x49\x45\x19\x9d\x07\x51\xc2\x75\x47\x2c\x4f\xdf\x3d\x0a\xd7\x41\x26\x7b\x10\xc0\xda\x6e\x79\xab\x6b\x39\x45\x02\x66\x25\xc1\x94\xc5\xeb\x77\x86\x89\x9c\x6e\x12\x64\xed\x83\xa4\xe0\x3e\x7c\x7a\x64\x73\xd8\x25\xff\x7f\x86\xb5\x0d\xa7\x16\xee\x72\x49\x98\x9f\xfb\x5e\xfe\xaa\xba\x54\x49\x5d\x9f\x31\x4f\xf5\xef\x90\xb8\x09\x3a\xac\x03\x61\xf0\x0f\x17\xea\x90\x20\xde\x3a\x08\xf6\x29\xe7\xcb\x3f\x39\x03\xec\x75\xdd\x3f\x09\xc2\x12\x5a\x2f\x39\xb7\xab\x4e\xb4\xe3\xba\x7e\x52\x08\x86\xb5\x9c\xcb\xd7\x39\x16\x51\x31\x98\x3d\x95\xe3\xf4\x3d\x40\x59\x52\x8c\x66\x43\xb8\x56\x6c\x0d\x6b\xc5\x58\x4e\x1f\xd5\x58\xe5\x0c\x01\x74\x44\xf9\x73\xe9\xab\x00\xbd\x54\x10\x51\x69\xc9\xe6\x13\xc4\xc2\x4e\x83\x9c\x6e\x3f\x21\xbb\x50\x46\xab\x87\xb6\x9f\x18\x26\x00\x61\x48\xb9\x66\x11\xf6\xc0\x0e\x2f\xd4\x23\x9b\xdf\x98\x92\xb0\xea\xe7\xf3\xd3\x20\xe9\xf0\x62\x26\xf3\xb3\x16\xb3\xf0\xb7\x82\x16\xee\x73\x36\xf4\x22\x35\x76\x2f\x36\x7d\x04\x1c\xe7\x66\x97\x72\x45\x73\x65\x12\xd8\xeb\xbe\xe3\x31\x49\x92\xb4\x10\x42\xd9\xf7\xd1\x0f\xad\x29\x48\x24\xdc\x8f\xcf\x44\x23\x35\x9f\x05\x5c\x5a\x83\xfd\xed\x62\x1c\x2f\xf3\xe8\x4c\x85\x50\x8d\x4e\xa3\x38\x2a\x94\x80\x73\x1a\x24\x9f\x07\xa7\x59\x90\x8c\x67\x24\xa7\xd9\x59\x34\x96\x1b\x60\xc0\xfd\xf8\xb6\xbe\x1f\x44\x3f\xf4\x6d\x1a\x52\xe1\x4c\x72\xb9\x0b\x4d\x68\xc6\xb6\xa1\x20\x9e\xa6\x59\x54\xcc\xe6\x24\xa4\xf9\x38\x8b\x4e\x39\x5b\x12\xf2\x0f\x4d\xfa\xe7\xd1\xe7\x68\x41\xc3\x28\x00\x21\x88\x7d\x0d\x0e\x92\x82\x66\x49\xc0\x9f\x4e\x7c\x7a\x1e\x24\x9f\x3f\x09\x27\xc2\x9f\xf8\xbc\xfe\xff\x7e\x14\x23\x4d\xa6\x9f\xd8\x10\x3f\xc1\x5b\xa2\x4f\x61\x34\x8d\x9c\xa7\x1c\x72\x6a\x7c\x14\x79\x2a\xf7\x54\x39\x03\xd2\x19\x4e\x91\x7a\xb6\xd9\x06\xb4\xfa\xdc\x5e\x91\xa7\x16\x5b\x14\x33\xba\xcf\xf7\xa9\xf6\x3f\x5f\xb6\x77\xd6\xbc\x3c\x53\xf0\xd8\x8e\xb5\x73\x77\x70\x05\x1b\xa4\x3d\x04\x51\x09\x5a\xc1\xe6\x2e\x0c\x1d\x2f\x18\x36\xc8\x2e\xe9\x70\x71\xaa\xf3\xdd\x53\xf2\x48\x37\xd1\x95\xcf\x06\x1e\x6d\x59\xfb\xad\xf2\xf6\x61\x36\x85\xea\x14\x0d\xd6\xa8\xad\x04\x13\x41\xb8\x02\xc2\xe6\x81\xec\xa3\x24\x2f\xa2\x62\x59\x48\x57\xd8\x51\x48\x93\x82\x6d\x5a\x76\x00\x08\x5e\xcb\x41\x12\x46\x19\x35\x0d\x18\xcc\x37\x36\x79\x4f\xca\xb2\xea\x91\x0d\xbc\x9a\x6a\xa1\x96\x5a\xd0\x54\x4b\xb7\xd5\x5a\x85\x17\x99\x3d\xf1\xba\xc7\x36\x8f\xc0\x26\x67\x68\xbf\xfc\xf8\x9a\xcd\x83\x7c\xdd\x82\x31\x80\x52\x55\xdf\xba\x16\xbf\x4e\xab\xf8\xb5\x7c\x4a\xc7\x91\x2b\xa2\xc4\x47\x39\x7f\x29\x87\xf9\xb8\x23\x77\x82\xe7\x96\x52\x79\x53\xed\x45\x1e\xc5\x87\x54\x78\xf0\xe7\x74\xbc\x25\x25\x74\x1e\x20\xbf\x30\x95\x72\x42\x84\xfd\xcb\x44\x9c\xac\xb0\xf0\xa7\x9d\xcb\xd4\xea\xca\x15\x16\xa0\xeb\xa5\xaf\x07\xf1\x98\x75\x74\x10\xef\xa8\x7a\x24\xf5\x68\x6d\x60\x6c\x58\x5b\xe3\x8e\xd2\xa2\x84\xc1\x7f\xfe\xf9\xf2\x78\xf8\xe8\xbb\x93\x2f\x5b\xd7\x9d\x97\x1f\x5f\xb3\xdf\x7b\x8f\xfe\xef\xe4\xcb\xe6\xf6\xf5\x95\xfa\xd8\x1e\xf6\xb6\x37\xaf\xbb\xff\x33\xe8\x17\xa0\x04\x55\x1b\xb8\xf1\x2e\xaf\x8c\x31\x20\x70\xfe\x3c\x6f\x73\x45\x84\x89\x27\x98\x70\xfa\xf7\xa2\xed\x85\x5e\x82\x77\x83\xb7\x17\xee\x4a\xb2\x10\xa7\x07\x85\x1f\xf7\x6c\x3f\x26\x57\x57\x65\x79\xdf\xdc\x70\xd8\x13\x12\x25\x25\x03\x37\xb8\xcf\xdd\x0c\xdd\xcb\x46\x1a\x0d\x7e\x6b\xd8\xc8\x6a\x93\x8b\x94\x6c\xa4\xf9\x72\xce\x00\x8f\x72\x71\x7c\x98\xa7\xe1\xa3\xef\xbe\x7d\xb4\x39\x54\xd9\x70\xc6\x85\xde\x8d\xd3\x98\x74\x0e\x3e\x1c\x0e\x0e\x5e\xee\x13\x76\x6e\x18\x6d\x0d\x87\xdb\x5d\x9b\x27\xa3\x6a\xdd\x53\x28\xca\x75\x06\x2e\xf3\x1a\x0e\x5b\x9c\x09\xb7\x7a\x64\xab\x99\xad\x2a\x66\xaa\xc6\x96\x42\xe8\xb4\x4f\xfe\xf9\xfe\xe5\x8f\x8e\x87\x44\x55\xc0\x3f\x9a\xd2\x1a\xdd\x49\x45\x90\x75\xc3\xd3\x04\xd0\x01\xf7\x79\xce\x90\xbf\xed\x91\xc7\x5d\x32\x22\xed\x76\xa3\x71\x8f\xe3\x08\x1e\x92\xa9\x0e\x82\xf2\x29\x4a\xec\xf1\x31\x2c\xfc\xb8\xf7\x8f\xc3\x57\xff\x3a\x7c\xff\xbf\xf6\xac\x42\x1d\x25\x73\x6a\xd7\xef\x9d\x5c\x0e\x74\xeb\xb1\x6f\x6e\xae\x3e\x72\xb1\x9a\xfc\xe7\x12\xf7\xe0\xe1\x0e\xcd\xa9\xc0\x19\x5e\xe0\x39\x87\xe0\x7b\x27\x31\x38\x9f\xe3\x33\xe3\xd0\xe1\x0e\xf8\x31\x3a\xc4\x96\x1e\x65\xe4\xf9\x43\x9d\x52\x8c\x13\x2a\x3f\xa3\x98\xe7\x99\xcd\x27\xdd\x1e\xd9\x1a\x2a\xd7\x6a\x86\x94\x27\xd1\x6b\x0d\x52\x16\x6e\xb6\x40\x4b\xbc\x61\x1d\x40\x16\x57\xea\x63\xbd\x62\x6b\x64\x7e\x5e\x9f\xf4\xb6\x1f\xdf\xab\xf1\xef\xd5\xf8\x7f\x71\x35\xbe\x50\xe1\x2f\xc6\xd5\xf6\x7b\xb7\xb0\xb8\x6b\xe9\x50\x99\xad\x9d\x95\x42\xfc\xd5\xd8\xe9\x71\x3d\xd3\x62\xec\xb5\x04\x5b\x04\xc5\xac\x47\x12\x6a\x58\x7f\x7f\x02\xcd\x85\xf3\xf0\x54\x5e\x55\xe3\x20\xe3\xd2\x6b\x81\xb0\xd7\x01\x1b\x1f\xf6\x1f\x4f\xd5\x59\x63\x75\xc3\x0b\x5c\xb1\x90\x09\x9d\x2f\x0c\x7a\xa4\xcb\x2b\x1f\x9b\x56\xb1\x7e\x9a\x74\xda\x30\xaa\x36\x0e\xca\xdb\x35\xec\xa7\xf3\x94\x31\x31\xfe\x96\xf0\xe0\xdd\x3e\xd1\xf7\xca\xfc\x85\x61\xbb\x47\x28\x62\xbd\x9f\x38\x1b\x14\x17\xde\x1d\xdb\xcb\xa7\xb7\x07\x49\x88\xdb\x47\xcd\x97\x56\x46\xd6\xd4\x1b\x83\x9f\x0e\x3e\x7c\x7c\xf9\x16\x56\xd0\xfe\xe1\xdb\xb7\x2f\xf7\x3f\x1e\x1c\xbe\x25\xef\x5f\x7e\x78\x77\xf8\xf6\xc3\xcb\x0f\xa5\xad\x86\x41\x11\xe0\x66\xd9\x37\xde\x9c\x06\x0f\x85\x19\xe1\x3c\xb8\x18\xa7\xf3\x45\x4c\x2f\xa2\xe2\x72\x44\x9e\x00\x65\x59\x3d\x04\x5d\xa8\xb2\x43\x60\x55\xe9\xfd\xa6\xeb\x89\x4b\x24\x6c\x0e\xbe\x98\x01\xd5\xe1\xe0\x17\xda\xb6\x13\xa2\x3b\x3c\xd0\x3c\xf0\x97\x90\x9c\xcf\xa2\xf1\x8c\xcc\x83\x62\x3c\x13\xe2\x2b\xdf\x84\x18\x43\x0b\x8d\x72\x9e\xb0\x18\xd0\xb4\x3f\xe2\x3a\x5c\x47\x39\xbd\x05\x0b\x04\x17\x5c\x54\xff\xc9\x4f\xc8\xc7\xf0\x36\x2e\x0a\x4f\x5c\x67\xfb\xaa\x30\x1b\xab\x00\xdb\x71\xa0\xec\xd8\xf5\xa5\x31\x9d\xa1\x1a\xd1\x77\xbb\xa2\x6b\x07\x8b\x93\x28\xa3\x86\x47\x00\x1b\x5d\x65\xe3\x61\x43\xf1\xb4\x5e\x01\xae\x03\x4c\x63\xd3\x16\xfd\x17\xd2\x98\x16\xb4\xaa\x06\x7b\x30\x36\x6e\xf0\x2b\xec\x9f\xd9\xae\x05\x84\x28\x08\x82\xd7\x07\xca\x1d\x6e\x2b\x95\x70\x67\x39\x24\xe5\x3e\xa4\xa3\xa2\xbf\xb6\x26\x85\x41\x93\x84\xd7\x6c\xb5\x07\xbc\xc8\x64\xc2\x9f\xe6\x79\x48\x3c\x32\x0b\x63\x8f\xae\x78\x55\xd9\x6c\xb0\x67\xc9\x6b\xff\xe0\x2e\xdb\xb5\xe7\x61\xb9\xc4\x5f\xbc\x7c\xb4\xff\xfa\xe8\xed\xff\xbe\x7c\xaf\xea\x09\xe9\x78\xb6\x4c\x3e\xd3\x50\xbc\x2a\xe1\x2f\x46\xc5\x5f\x3f\xa3\x8b\x38\x18\xd3\xce\xe0\xdf\xd7\xc7\xff\x4e\xfe\x9d\x9d\x3c\xfb\xf7\x97\xc1\xb4\xd7\xbe\xbe\x7a\xf4\xe8\xea\x4b\xbb\x0b\x3e\x93\xbf\x78\xe1\xff\x7d\x22\x4b\x1c\x8b\x32\x27\xac\xd0\xb1\x2c\x75\x72\xec\x2f\x67\x97\x32\x0a\x95\x94\xd1\x6d\xa1\x96\x54\x43\xa8\x8c\xb8\xe6\x63\xd9\x6d\xc9\x49\x0d\x0c\xb8\x6b\x16\x10\x8f\xf8\xcb\x60\x00\x77\xa0\x54\xb8\xc3\x00\x4f\x1b\x50\xc1\x9a\x43\xfa\x2c\x6f\x9f\x65\x99\x2b\x57\xf8\x9d\xb1\x60\xc8\x06\xe1\xef\x5f\x0d\x51\x5d\xdd\x59\x5b\x9c\xcc\x75\x6a\xe0\xb3\x05\x83\xbe\xa3\x52\xc2\x9a\x86\x1b\xd3\xac\xb9\x8b\x4f\x77\x66\xd7\xee\x8c\x18\x3a\xf8\xfa\x55\x16\xd4\xe0\xfa\x2e\x19\xd3\x18\x22\x05\xc8\x47\x9c\x46\x99\x71\x4c\x83\x4c\x9a\x70\x59\xad\x88\x64\x6b\x41\xfb\x81\xc0\x57\x43\x21\x2b\xf2\xed\x71\x66\x79\x7b\xaf\xc3\x7f\x95\x76\x95\x02\x67\x18\xfe\xba\x47\x36\x87\xc3\x21\x79\xc8\x2f\x67\x3c\x77\xad\x5e\xc7\x0f\xf0\x6e\x0f\xb0\x23\xf1\xc5\x38\x48\x4e\x05\xbd\xf0\x58\x3f\xe2\x5d\xdf\xea\xa8\x72\x67\xcc\x22\x11\x08\x62\x25\x2c\x2b\x9d\x0e\x73\x16\xd1\x5f\x2c\xf3\x99\x69\x31\x68\xbb\x11\xc7\xe0\xc2\xf9\x0f\xe3\x91\x3f\x8a\x2d\x34\x08\xc3\x1c\x47\xac\x17\x56\x0e\xae\x34\xc6\xd5\xc3\xbd\x35\xbe\xe1\xca\x83\x81\x38\x6b\x47\xdc\x0f\xbf\xe0\x7a\xb0\x1b\xcb\x5b\x21\x95\x7a\x10\xf2\x52\x41\x96\x45\x67\x14\x33\xdc\x20\x54\xb3\x27\xdb\xab\xe0\xb0\x1e\x68\xc3\x0d\xbf\xdf\xa6\x14\xc9\x14\xf2\xb5\x7a\x04\xa1\x6d\xc5\xd7\xf1\xf0\x44\x6d\x99\x70\x85\xcd\xfb\xa6\xa1\x45\x82\x59\x82\x27\x62\x89\xce\xbb\x79\x91\x5d\xd5\x9b\x2a\x89\x97\x81\xf6\x55\xc3\xb2\x6e\xb9\xab\xc9\x75\x84\x57\x2a\x39\x9f\x51\xe9\x77\x20\xe4\x62\x39\x9c\xbe\x40\xe3\xce\xf6\xf7\x10\xa1\x59\x10\x71\x05\x6a\x5d\xfb\x4e\x75\xb4\x9f\xa4\x59\x87\xe1\xe5\x33\xbd\xe4\x27\x45\xdf\x00\x4c\x27\x30\x1d\x3f\x50\x7f\x16\xe4\x87\xe7\xc9\x3b\x88\xe4\x55\x5c\x42\x64\x4a\x8b\x0b\x94\xa0\xe7\x33\xbd\x3c\x29\xb7\xed\x6c\xa7\x09\x39\x78\xb7\xdf\xee\x5a\x8b\x5f\xc8\x16\x15\x75\x3a\x66\x16\x7a\x99\xec\x63\x1f\x84\xc2\xcd\x39\x41\xc7\x8d\x28\x27\x79\x11\xf1\x28\x2b\x51\x88\x88\x1a\x9b\x85\x96\x22\xdc\x6f\xc7\xd9\x29\x3f\x2d\x49\x39\x80\xed\x1e\x19\x15\xfd\xe8\x71\x2a\x30\x7b\x35\x4d\x13\x2a\x34\x4f\x9d\xf5\x4f\xb6\xd8\x7f\x9e\x45\x05\xf8\x4b\xb1\xb8\x11\x02\xb1\x8e\x50\x9f\xdc\x33\x94\x74\x31\xb8\x5e\x56\xbb\x50\x20\x79\x87\x5e\xf5\x82\x60\x0d\xd3\x8f\x55\x2f\xfd\x80\x9e\xae\x10\x63\x93\xdd\x31\x38\xf7\x0a\x28\x92\x68\xaa\xc7\x12\xf1\x1c\xa1\x6a\xcf\x9a\xb2\x97\x21\x7a\xf6\xeb\x1b\x55\x85\xc5\xf3\xcd\xc4\x06\x45\xd5\x58\x6a\x30\x87\x52\xbb\x8f\x12\xeb\xcf\xb7\x4f\x5a\x66\x77\x42\x9b\x68\x9d\x51\x1c\x77\x3c\xff\x4a\x97\x60\x65\xad\x5f\x9b\xb5\xda\x1b\x36\xbb\xdd\x68\xb7\x48\x8e\x0d\xb3\xfb\xd8\x4e\x5b\xf3\x41\x78\xb1\x95\x16\x24\x5f\x2e\x16\x69\x56\x80\x6e\x8d\xdf\xd4\xbe\xdb\x27\x4a\xab\xd2\x36\x1c\x41\x96\x13\x66\xe3\x97\x0a\x37\x59\x8c\xf5\x54\xb6\x12\x85\x79\x8f\xf5\x40\x53\x95\x16\xf4\xc8\xa1\xae\xbd\x9b\x96\x7a\xbb\x71\xf5\xb8\x1a\x83\x8e\x93\xf6\x92\x57\xda\xd7\x27\xbd\xed\x6f\xee\x55\xba\xf7\x2a\xdd\xff\x0a\x95\xae\x78\x58\x71\xab\xe7\xd8\x7b\x41\x96\x26\xe4\x7f\x97\xf3\xe0\x2c\xca\xc9\xf7\x01\xfb\xfc\xdb\x67\xfe\xd9\x9f\x53\xaf\xba\x77\x30\x20\x07\x49\x54\x44\x41\x1c\xfd\x4a\xc9\xdf\x79\x2f\x18\xa1\x06\x24\x07\x4b\x2c\x69\x70\x03\x03\x65\x4b\xd5\x70\x72\xde\x07\xad\xae\x2c\x26\xa3\x97\x88\xc8\x5a\x07\xe1\x88\x0c\xeb\x6e\xde\xb8\xb5\x07\x1b\xbe\xed\x56\xd7\x6b\x66\xe2\x75\xa7\xab\x5f\xa1\xc9\x20\x5e\x13\x89\x50\x68\x49\x1b\xf4\x78\x9c\xf0\xf2\xd7\x29\x3d\xa4\xea\x99\xc8\x6a\x64\x96\xf4\xbd\xeb\x75\x43\x84\x46\xc0\xda\x73\x7a\x3f\x58\x13\xe8\x29\x71\xc5\xcb\xdb\xea\x89\xc6\x0c\xa7\xa9\x3c\xab\x5b\xa6\x5a\x96\x4d\x3a\xc6\x3c\xca\x6c\x77\xbd\x8d\xc2\x69\x05\xe1\x19\x3b\xa3\xca\xd9\x21\x07\x2f\x20\x47\xf6\x4e\x4d\xda\xc6\x46\x99\x9f\x21\xff\xeb\x1f\xfe\x56\xc8\xa9\x46\x67\xcb\xe7\x41\x62\xa4\x2a\x5d\xbe\x0b\xe2\xff\xb3\x03\x93\x7c\x21\xd4\xdc\xf0\x42\xe2\x40\x1d\x1e\xa5\x01\x91\xdf\x54\x47\x29\xeb\xea\x42\xba\x79\x5e\x66\x5b\x0d\xf8\xcd\x33\x24\x1a\xac\xf6\xac\x50\xd7\x3c\xd1\xba\x0c\xe5\x3e\x7d\x90\xce\x59\x00\x3d\x53\x6d\xf7\xe9\x19\xcd\x2e\x3b\xd2\x1b\xf2\x87\x28\x99\xc6\xf4\x0d\x47\x78\x97\x8c\x88\x37\x43\xd7\x24\xa6\x55\x75\xc4\x0f\x2e\x26\x50\x1d\xb4\x94\xf0\x2e\xe9\x06\x59\x10\xc9\x34\x4e\x91\x86\x6d\x91\xc8\x90\xf3\xb3\xbb\xbb\xcb\xa9\x06\x03\x09\xb7\x0b\x12\x96\x9d\xb9\x19\x18\xbf\xd6\x6d\xfb\xaa\x13\x32\xac\xe5\x53\x72\x30\xe0\x31\x07\x55\x92\xf0\xca\x8e\x99\x8b\x5c\x8f\x8d\xfc\xc9\x73\x46\x74\x0a\xef\xd1\x6a\xd8\xd1\x73\x06\x54\xee\xe2\x5b\x74\xdc\xe2\x2f\xbc\xae\x9c\x33\x55\x51\x95\x14\x70\xc2\x2e\x28\x8f\xc4\xa2\xe8\x48\xde\xd3\x25\x93\x88\xc6\xa1\x65\x7a\x20\x5a\x31\x7a\x6a\xf1\x1c\xdc\x41\x8b\xf1\xf0\xae\x59\x64\x28\x93\xad\xa8\x0f\x92\x2c\x5c\x47\x58\x0e\x7b\x93\xb0\x7d\xc9\xda\xe4\xb7\x60\x71\xa6\x1e\xde\x91\x15\x45\x7d\x42\x4e\x64\x62\xe0\x93\x7b\x31\xf0\x5e\x0c\xfc\x6b\x8b\x81\xfa\x7d\x1e\x5f\x34\x77\xf5\x42\xef\x6e\xee\xee\x19\xc8\x1b\xa9\x6e\x2c\x35\x56\x86\x73\xa2\x88\xd4\x22\xad\x90\xd9\x27\x3a\x45\x0a\x97\x6b\x32\x97\x7d\x1a\x17\xf7\xc0\xf3\x74\xbe\x96\x0c\x86\x08\x0c\x7c\xf2\xe3\x60\x88\xda\x10\x1a\x67\xa0\x12\xdc\xd3\xb3\xaf\x88\x95\x63\x28\x5d\x41\x63\xf0\x26\x48\x82\x29\xd5\xaf\xf3\x19\xcb\xe2\xa8\x30\x54\x01\xd2\x85\x87\x06\x47\xfb\xfd\xdc\xc0\x90\x53\x71\x36\xaf\xb1\x7f\x0f\x29\xe3\x30\x51\x62\xfa\xf7\xb4\xc4\xbf\xd3\x20\xe7\x3e\x17\xca\x22\x51\x4c\x29\x78\xa9\xf4\x6c\x52\xa6\xa7\x79\xdb\xb1\xa8\x6c\xd3\x6c\x0f\x48\xcc\x41\x84\x68\xa3\x34\xd6\x84\xe1\x4e\x14\x85\xcf\x51\xc4\xa1\xec\xf8\xa4\x2f\xc3\x9c\x09\x36\x2a\xa5\xce\xcd\x31\x77\xc6\xa9\x2f\x29\x44\x68\x0e\xb1\xed\xaa\x71\xf6\xc9\x1b\xc6\xca\x23\x9a\x8b\xe8\xd8\x80\x0f\xc7\x0b\xa5\xe1\xd9\xb3\x31\xde\xe4\xa0\xae\xde\x2e\xe3\x58\x3b\xc6\xe8\x31\x29\x92\x5e\x44\x70\x6d\xe6\xc3\xdd\x1f\x33\xfe\xd0\x9d\x85\xdd\x21\x6b\x5f\x2b\xee\x8e\x83\xc9\x46\xd1\x76\xec\x00\x27\x2a\x94\x8c\x79\x10\x23\x35\xe1\x63\xde\xbf\xdb\x17\x11\x26\xaa\x63\xc7\x68\xb4\x09\x57\xaf\x9c\xf0\x00\xe9\xea\xc4\x69\xa3\x89\x83\x1e\x30\x48\x17\x4b\x06\xd1\xa9\x24\x0f\x3a\x50\x2d\x95\xd8\x58\xf7\x70\xd7\x12\x0a\xf2\x3d\x6e\xf4\x94\xb6\x64\x48\xe5\x74\xb1\x47\x20\xfa\x77\x55\x08\x29\xf2\x4c\xff\xe6\xd4\x0d\x45\x4e\x18\x3b\x40\x9f\x35\x9e\xf5\x1d\xac\x73\x7e\xaf\xa2\xe6\x62\xcc\xbb\x88\xe7\x0e\x78\xab\xcf\x8a\xa6\x3b\xe2\x12\xdc\x7b\x62\xa4\x98\x41\x7a\x31\x0a\xed\xcd\x0a\x9c\xcd\xc0\xb1\xe7\x99\x17\x40\x55\xe5\x8d\x4d\x22\x70\xe1\x0b\x59\x24\xdf\x4f\x49\x3a\x5c\x21\x72\x51\x20\xd7\x6d\x23\x24\x34\x8b\x41\x84\xdd\xb1\x8a\x7d\xc4\xf6\x92\xbc\xb2\xf3\x65\x21\x4f\x00\x30\x5a\x06\x18\x10\xf2\x8c\x00\x43\xea\x98\xe2\xd7\x82\x48\x75\x06\x68\x96\x4a\x94\x19\x55\x6e\x95\xb1\x8a\xc3\x41\x95\x74\x91\xcb\xf1\x69\x4a\x5b\xa7\xbf\x60\x74\xb1\x0c\x39\xb4\xd3\x65\x14\x87\x80\x30\x31\x28\x96\xe9\xf8\xb7\x05\x86\xff\xf1\xf0\xc5\xe1\xfa\xfa\x3a\x88\xf7\xed\x9c\x2c\xa7\xf1\x65\x5f\x44\x11\x63\x07\x82\x65\xce\xf6\xc4\x42\xb5\x92\x20\x97\xb2\xec\xb7\xb4\xab\x51\x37\x24\x8c\x71\x40\x86\x7a\x6f\xbd\x69\x44\x7a\x3a\xfd\xe5\x98\x65\x1f\x0f\x4f\x4e\x98\xd8\x85\x3f\xaf\xae\x94\xdd\xa6\x0d\xca\x7f\x6c\x42\x19\x36\x96\x1d\xff\x55\x91\x55\x3b\x40\x12\xc4\x85\x1d\xf4\x2a\x44\x95\xdd\xa2\xaa\x4b\x75\x6d\x74\xca\x43\xa0\x24\xfe\x67\x59\xc4\xf1\xf3\x2d\xe4\x77\x7d\x1a\x5e\xc5\x0f\x34\xb1\x22\x58\xf8\x42\x15\x18\x67\x75\x68\xcb\x94\x28\xf5\xc5\x94\xbe\x9f\x31\x62\xb1\x28\xf3\x3a\x8f\x69\x9e\xdd\x30\x87\x17\xed\x60\x66\xa6\x8c\x22\x2d\x03\x1a\x6f\x38\x15\xb3\xbb\x46\x35\xe5\x43\xb0\xaf\xa1\x04\xa9\xb0\xac\xa6\x9e\x9e\x65\x98\x2b\x9a\xd4\xbb\x73\x94\x1c\x72\x99\x51\xb8\x21\x7d\xff\x6e\x5f\x79\x60\xe2\xa6\x2c\xe3\x20\x51\xc2\x66\x94\x08\xa5\x8b\xdf\xd7\x53\xe6\xfa\x7a\xec\xf7\xfb\xd7\x38\xbe\x9b\xed\x4b\x4f\x6b\x32\x65\x51\x0f\x27\xad\xf3\x69\x5f\xea\x6e\x7e\x15\x22\x94\x34\x60\xfa\xa4\xc7\xb3\x56\x86\x68\x51\xb2\x44\xb1\xf3\x46\xda\xc0\x34\xbd\xfe\xfb\xf6\x5e\xef\x73\xaf\xf7\xf9\x6b\xeb\x7d\x84\xd2\x27\x3c\xbd\xc5\xcd\x9f\x4f\xef\xa3\xb4\x35\x58\xf1\xc3\x99\x93\xd2\xe8\xbc\x78\x6e\xf0\x11\x36\x0c\xd3\xe5\x87\xa3\xa9\x80\x91\x5a\xc9\x3b\x15\x81\xc2\xd6\xb4\xbc\x94\x77\x3c\x36\xfd\xe2\x82\x8b\x7c\x21\x96\x74\x65\xc9\x41\x1d\x56\x33\xda\x59\x04\x90\xa3\x76\xe9\xf8\x3a\x68\xe9\x9b\xf5\x2e\x5f\x1e\xb0\x68\xb1\x2c\xd4\xe3\xb5\x84\x9e\x0b\x6c\x76\xf4\x76\xc9\x84\x8e\x11\x69\x2b\x38\x2b\x8e\xc6\x88\xb4\xc3\xd3\x4f\xbe\x5c\x29\x26\x6e\xab\x3e\xa9\x46\xa7\xb4\x59\xa3\x0a\xce\xdb\xa8\x2f\x57\x36\xba\xe5\x36\xba\x58\x16\xaf\xe9\x45\xfd\x30\x5f\xd3\x8b\xb2\x31\x9a\x59\xd5\x03\xac\x6f\x8b\x03\x95\x0d\xcd\xdf\x96\x35\x2e\xb1\x19\x1d\x6b\x38\x39\x11\x3d\x8d\xe4\x9e\x18\x7a\x4f\x74\x0b\x80\x4f\x4a\x76\xae\x17\xcf\xf5\xae\xc5\x69\xa7\x35\xda\x86\x2d\xea\xe9\xfd\x16\x75\xbf\x45\xfd\xb5\xb7\x28\x7d\x35\x41\x8b\xd9\x8d\xee\x25\x04\xf0\xdd\xbe\x4a\x2c\x89\xfe\xef\x0b\xff\xef\xbb\x04\xf1\xdf\x83\xd4\x6c\x9b\x0c\x44\x9a\x23\x5b\x40\x0b\x91\x2c\xc1\xc6\x65\xed\x8d\xd3\x64\x12\x4d\x25\x18\x0a\x85\x83\xa1\x65\x64\x15\x09\x76\x2e\x9e\xad\x19\x17\x34\x22\x51\xc2\xbc\xe2\xa1\xc0\x2d\x64\x40\xa2\x04\x39\xc8\x3f\x5c\x26\x63\xbe\xc5\x60\xa8\x9c\xa7\x4a\x30\xc6\x8a\x33\x6a\x03\x89\x54\x55\x17\x77\x50\x84\x21\xa2\xd3\x20\x91\xd9\xdc\xeb\xa1\xd3\x1f\x99\xac\x84\x10\xf0\x99\xd6\xe4\xce\x40\xe9\xbc\xc5\x1b\x41\x50\x02\x0e\x4f\xba\xe4\xc1\x03\x22\x7e\xf7\x41\x27\x78\x38\xe9\xb4\x87\x17\x6d\xee\xba\x64\xd8\x25\xcf\x48\x8b\x16\x33\xb6\x7b\x40\x60\xd2\xe7\x97\xaf\x83\x7c\xd6\x22\x23\x3b\x99\x6b\x74\x5b\x5a\x4a\x80\xae\x7d\x88\xa6\x09\xcd\xf2\x8a\x1e\xde\x71\xff\x44\x83\x25\xdd\x54\xb9\x4e\x6f\xf3\x22\xf8\x4c\xb3\xf7\x87\x07\xf5\x5d\xbd\x59\x4f\x51\x47\x3f\xc8\xb6\xde\x04\x79\x41\xb3\x24\x0d\x29\xee\xa9\xca\xb6\x91\xf9\x2a\x4a\x82\x38\x2a\x2e\x7f\x3b\x6c\xca\x16\x4b\xd0\xa9\xb3\x1d\x7c\xa2\xe8\x5f\xaf\xb2\x74\xfe\xfc\x37\xa0\xd3\xb6\xe8\x1a\x0a\x2a\xf5\xfc\x12\x1a\x66\x9d\xdf\x4b\xc2\x03\x56\x4e\xc5\x72\xf3\x42\xf2\x71\x28\x58\x3d\x9e\x65\x32\x8e\xe9\x6f\x34\x80\x23\xd6\x56\x4d\xd7\x31\x4c\x69\xa7\xe5\x3c\xa1\x71\xee\xa7\xcb\xa4\xd1\x25\xe3\x1d\x8c\xc3\xdb\x36\x27\x25\x3c\x94\x12\x30\x3e\x2a\x67\x0a\x7e\xc3\xfe\x1f\xa9\x06\xd1\x64\x38\x93\x80\x01\x8c\x3e\xab\xee\xbd\x2c\x66\x77\x7d\x3c\x6c\x7c\x34\xbc\xa3\x93\x21\x84\x7f\x2e\x3f\x19\x72\xc5\x17\xdf\xc3\x23\xea\xed\xd1\x02\x77\x66\x51\xd3\x8f\xc5\x0d\xba\x80\x2c\x1c\xf8\xde\xca\xbd\x9f\x10\xec\x9f\xfd\xe0\xf9\xde\x5b\x2b\x14\x9d\xd8\x51\xb9\x4e\x8e\x3f\x9f\x16\x9a\xb9\xeb\xb5\x35\xde\xbb\x3e\xb7\x8b\x53\x2f\xa9\x5e\x16\x33\xad\x0b\xec\x91\x36\x0e\xdc\xdd\xee\x89\x61\x4e\x69\x31\x2a\xd1\x78\x4b\x4f\xb5\x7d\x5c\x50\x8c\xa4\x27\xb4\xb4\x46\xe1\xb3\x20\x36\x62\xcc\xf5\xad\xb0\xe9\x67\x41\xec\xb8\xa2\x51\x69\xd7\x6b\x80\x9e\x95\x86\x22\xbc\x3c\xde\x64\x30\xa2\xe8\x4d\x86\x23\x8a\x36\x1c\x50\x13\x4d\x04\xe3\x2e\x41\x0c\x76\xbb\xb5\xe7\x66\x01\xe8\x9e\x9d\x25\x9b\x72\xf2\xd5\x01\x1a\xd9\xf2\x1a\x17\xb8\x23\x72\xac\xc5\x69\x7e\xb9\x2b\x9c\xa8\xbe\xd2\x77\xb9\x36\x04\x8e\x7b\xcf\xf9\x89\x02\x46\x81\x43\xad\x5b\xcc\x11\xae\x86\xe7\x29\x8f\x45\x0a\xa8\x44\x69\x92\x66\xc1\x94\xee\x15\x4d\xf4\x26\x02\xb4\x14\x47\x3e\x08\xa5\xd2\xa8\xc0\x12\x5f\x77\x9c\x63\x17\x29\xe8\x15\x56\x41\x8b\x77\x60\xc2\xb5\x67\xcd\x98\x18\x54\xe9\x70\xac\xcc\xdf\x7e\xbe\xbd\x03\x13\xcb\xe4\x20\x99\xa4\xf5\xe3\x43\xc0\xa5\xc3\xf4\xc3\xfc\x41\x46\x2b\x79\x5c\xdd\xea\xe5\xcc\xd7\x1a\xa1\x3a\x1e\xdd\x6e\x58\xbe\xde\xf6\x1c\x86\xa6\x6d\xbd\x19\xab\x22\xd7\xab\xad\x56\x90\xa7\x2b\x57\x2a\x3e\xc1\xf8\x11\x62\xa1\x43\xc0\x2a\xac\x20\x9c\xa0\x73\x99\x1d\x67\x64\x53\x26\xdc\x08\x2d\x6a\xd0\x0d\x87\x2c\x3a\x52\xc7\xa3\xc4\x89\xa8\x09\x8f\x12\xa0\x0e\x2d\x18\x27\x3c\x97\x1e\x36\xab\xe8\x41\x0a\x31\xd6\xce\x45\x8c\xdd\x09\xdb\xdc\x94\xac\x07\x5e\xc1\xc8\x74\x67\xd0\x94\x50\xf0\x15\xe2\x7b\x1a\xc4\xe5\x44\x22\xcf\x65\x8d\xa8\x44\x02\xfb\xc8\x04\x9f\x38\x7f\x07\x3a\xc1\x23\x3e\x48\x0a\xef\x80\x41\x06\xaf\xa7\x0b\x00\x73\x68\x42\x9d\xea\xbe\x06\x7f\x40\xdb\xd9\x2d\x58\x41\x6f\xad\x64\x77\x9b\x2f\xa2\xb8\x94\x13\x98\x5b\x9c\x00\xad\xd8\xe7\x5c\x08\x89\x87\x61\x39\x99\xd9\x67\xb6\x86\x5c\xda\x2e\xe6\x74\xab\xea\xd8\xba\xe2\xc2\x5d\x85\x12\x3d\x73\x23\xa7\xf0\x05\x1d\x47\xf3\xaa\x15\xa7\x4f\x82\x0d\x91\xa0\x0b\x94\x10\xe5\x1f\x77\xc0\xe6\x01\xaa\x66\xb0\xe5\xd1\xf2\x4b\xd4\x30\x70\xc6\xae\x1c\x74\xfd\x0a\x42\x15\x56\x6f\x2c\x1f\x3d\x5a\xaa\x95\xc6\xa4\x4a\x39\x83\x2b\x53\x80\xfd\x91\x38\xcd\x4d\xf0\xf4\x9e\x8e\x69\xb4\x68\x40\xe6\x6e\x99\x26\x04\xe0\x82\xde\x96\x02\x44\x8d\x8d\x07\xd8\x70\x15\xd7\x72\x31\xcf\xe0\x6c\xc0\x26\x14\xc0\x8f\x46\x77\x74\x48\x2c\x5b\xde\x44\x5a\x1b\x48\x6b\xbd\xf7\xc1\x79\xf3\x65\xee\x16\xf0\x23\xa3\x12\xae\x09\x77\x63\xb8\xf0\x9c\x12\x58\xbd\xab\xf5\xb6\x51\x57\x6f\xde\x4f\x7b\xb6\x7c\xeb\xcc\x37\x8e\x68\x9a\xac\x30\x0e\x13\xba\x64\x1c\xa5\x40\x5f\x79\x1c\x0d\x3a\x5f\xde\xe3\x3b\x3f\x85\x96\x10\x8e\x30\xf1\xad\xea\x28\x03\xf1\x77\xd4\xca\xb9\x49\x47\xd9\x7e\x70\x67\x67\x65\x9a\x17\xd1\x3c\x28\xe8\x8f\x41\x9d\x4c\x88\x20\xfd\x43\xf3\x03\xdc\x84\x62\x8c\x11\xde\x4a\xf0\x18\x73\x19\xf5\x43\x1a\x47\x61\xe9\xd1\x46\x4f\x1b\x87\xee\xe7\x02\xbc\x64\x0a\xcd\x3a\x7d\x63\x2d\xed\xc8\x4f\x3f\xfd\xd4\xb0\x0f\x71\x29\x05\xa9\x9a\x56\x6a\xf9\x03\xcd\x16\xb4\x76\x8b\x52\x18\xe0\xd0\xd5\x08\x70\x60\x2a\x7a\x91\x2f\x4f\xe7\x51\xf1\x73\x9a\xd5\x49\x4a\x1a\xb0\x64\xa5\xfb\xf2\xab\x0d\xa0\x1a\xb4\x2a\xa0\x4a\xb7\xe3\x92\xf6\xfc\xc7\x9c\xfd\x20\x09\xf9\x23\xff\x22\x28\x96\xb5\x5a\x17\x0b\xdc\x3a\x51\xab\xd3\x56\x09\x94\xc3\x41\xee\x40\xdd\xf6\x72\x91\x8e\x67\xa5\xbc\xe3\xff\x63\xef\x5d\xd7\xdb\xb8\x95\x44\xd1\xdf\xf6\x53\x20\xde\x67\x45\x64\x4c\x53\xbc\x4b\xa6\xad\xcc\xc8\x94\x64\x69\x6c\x59\xda\x92\x9c\x64\x6d\x7d\x8a\xbf\x26\x09\x8a\x6d\x93\xdd\x9c\xee\xa6\x2e\x89\xb5\xdf\xe7\x3c\xc7\x79\xb1\xf3\xa1\x70\xbf\x35\x9b\xba\x38\x4e\x46\x5a\x33\x31\xbb\x1b\x28\x14\x80\x42\xa1\x50\xa8\x8b\xab\xa7\x45\x0e\x94\xa2\xac\xff\x44\xe9\x2a\x72\x1b\x06\xe2\xef\x80\x95\x0a\x57\x5a\xac\x49\x6d\x7d\x45\x7d\x27\xb4\xd3\xda\xdb\x5e\x3c\xd4\x8b\x29\xea\x50\xed\x3d\xf0\x61\xfb\x0d\xd3\x60\x19\x2d\x31\x5d\x93\x5d\x9c\xab\x54\x74\x1c\xc4\x70\xb9\x5f\x53\x4a\xd1\xbe\xc1\x01\xd2\xe8\x08\x3b\xc5\xdb\x8d\x9a\x32\xa8\x5d\x42\x9e\x47\xb5\x6f\x4a\x45\xdf\x7b\x71\xba\xf1\x15\x60\x02\xb8\xef\xb3\xd1\xa8\xee\x17\xa4\xec\x44\xf2\xa5\x2d\x47\x2a\xdf\x74\x81\x47\xaf\xe4\xad\xa1\x34\xaf\x6f\x09\xd6\x87\xf7\xef\xdf\xdb\x85\x29\xfb\x54\x40\x0a\xce\xa6\x75\x9a\xbc\x80\x67\x66\x28\x49\x53\xbb\x8a\x5b\xd3\xbc\x54\x0f\x92\xb6\xc9\xda\x14\xd7\x77\xba\x2a\x52\x70\xfe\x30\xea\x07\xa9\xaa\xed\x62\x08\xc0\x12\x63\x8c\x9f\x95\x11\x45\x6e\xca\x95\x25\xda\x98\x86\x91\x6e\x24\x6b\xb5\xc0\x4a\xdc\x12\xfe\x38\x48\xc7\x49\x90\xe5\xf6\xc1\x53\xa6\x90\x68\xb1\x3c\x46\xdc\xc8\x2b\x07\x21\x77\x91\xc5\x87\x55\x66\x55\xa6\x9f\x50\x97\xc7\xf0\x3c\x48\x0f\x93\x70\x90\x3b\x66\x9e\x32\xb7\xbe\x4d\x5c\x1e\x4b\x96\xbd\x30\xcd\xc3\x52\x94\xb9\x65\x1b\x7d\xc5\x16\x23\xa7\x19\x7f\xb1\x07\xa2\x21\x9e\xda\xe9\x17\x6a\xb2\x9b\x87\x9b\x59\x54\x69\x51\x65\x21\xda\xfd\x7d\x75\x20\xcd\x21\x15\xdb\x98\x7e\xa8\x39\x3e\x06\x83\x2c\x4e\xb8\xfc\xcc\x0d\x28\xc1\x1b\xa9\x82\x48\x59\x6d\x4f\x65\xa5\x5d\x8d\x8d\xb8\xc1\xa4\x15\xd1\xa2\xa2\x78\xed\xd3\x52\xbd\x04\x83\xc1\x33\xf8\xa0\xf7\x0c\xaf\x3c\x25\xdd\x21\x35\xc2\x94\x70\xc8\x50\xac\x54\x9c\x06\x33\x15\x6e\xd5\x59\xc5\xd9\xb8\x54\xae\xd8\x24\xfb\x3e\x3e\x57\x24\xa3\x62\x28\xb9\x3a\x2a\xed\x39\xf3\x33\xf1\xf0\xd1\x2f\xb1\x0a\xd5\xf3\x49\xdc\x0f\x26\x55\x32\xa8\xd5\xc0\x7e\xcd\x52\xa7\xba\x9a\x0c\x07\xc1\xec\xc3\x6d\x9b\x25\x95\xad\x46\xe9\xcb\xbc\x26\x15\xe3\x56\xd9\xa0\xe9\x41\xa9\xa6\xa6\xe4\x15\x4a\xee\xe9\x59\x14\xd4\x72\x3b\x1b\x4b\xb7\x00\xc3\xbe\xf7\x59\xb7\xbe\x5e\x79\x66\xd9\x19\x33\x3f\x37\x69\xe0\xfb\xac\xdb\x68\xc3\x0b\x3a\xa7\xcf\xba\x8d\x97\xf4\x51\xd0\xc2\xb3\x6e\x93\x56\x09\xfb\x41\xf4\xac\xdb\x6c\x56\x74\x2f\x04\x78\x64\x83\xf4\xac\xdb\x6a\xc1\x33\xb7\x46\x7e\xd6\x6d\x51\xf0\x8c\xb3\x3f\xeb\xb6\x28\x5a\xdc\x6a\xe8\x59\xb7\x45\x1a\xe4\xb6\xc4\xcf\xba\xad\xe6\xcd\x59\xa5\xf9\xf2\xd1\xad\xe1\xd1\xad\xe1\x9f\xed\xd6\xe0\xf3\x69\xb8\xb3\xeb\x5d\x71\x6f\x83\x02\xae\x04\x50\xee\x03\xce\x1e\xd2\x53\x0f\xde\x2e\xb6\x7d\x94\x3e\x7a\xb7\x31\x7e\x2c\xe0\x99\xb7\xba\xba\x2a\x43\xdb\xb9\xc2\xe5\xb1\xbc\xcf\x84\xc5\x03\x38\x9c\x8d\x51\x30\x0b\x15\xdc\x1f\xe8\x40\x32\x09\xd3\x0c\xe7\x9d\x17\x22\x9c\x7d\x92\x85\x6e\x2b\x5c\x61\x9c\x98\x17\x2c\x56\x2b\xbe\x42\x4b\x08\x7c\xaa\xf8\x65\x6d\x6a\x1f\x70\xe6\xd8\xd4\xf4\xcd\x4b\xdd\x5d\x6e\xce\x2a\xad\xda\xe3\x6e\xf1\xb8\x5b\xfc\xb3\x77\x8b\xef\xd4\x09\xee\xfe\xfc\xd5\x0a\xba\xd3\x49\x9f\x80\x43\x9c\xa4\x71\x14\x4c\x1e\x1d\x03\x1e\xda\x31\xe0\xa6\x98\xa9\x78\x84\x2f\xa5\xfd\x79\x9e\x02\x5c\x16\xb4\xb5\xdf\x33\x36\xab\x9f\x9c\x85\xee\x70\xc5\x1d\x4e\xc9\x46\x70\x14\x5c\xbe\xc3\x8b\xae\xbe\xd4\xa2\x2b\x95\xa7\x4f\x9e\x98\xb8\x59\x05\x72\x1c\xdc\x8b\x5f\xe5\xda\xed\x88\x0f\x8a\x05\xf8\x93\x27\x05\x0d\x1c\x0a\xdf\xe1\xe2\xc1\x11\x1e\xc4\x17\x34\xc6\x64\xde\xa5\x27\x2f\xe7\xc4\x55\xff\x9a\x33\x20\xf3\x68\x12\x0f\xbe\x14\xa3\x14\xad\x6c\x0e\xb1\xf8\xca\x15\xb1\x9c\x2f\x36\x6e\xde\xd1\xbb\x67\xd3\x09\x39\xf7\x0b\xed\x27\x96\xb9\x27\x77\xd9\x1d\x78\xbb\x54\x7c\x7e\x8a\xcd\x4e\xfe\xdc\x2c\x73\x97\x65\xce\x8d\x81\xbc\x4b\xb2\x66\x0d\x2b\x8d\x28\x8b\x57\xbe\xd5\x28\x48\xb9\x3d\xe1\x54\xed\xbb\xed\xf0\x5e\x8a\x28\xe0\x54\x79\xf7\xe1\xce\x07\x9b\x0b\xd4\xc2\x72\x3a\xd4\xc2\x1e\xb1\xdc\x96\xcb\xf9\x76\x2b\x85\x73\x87\x8a\xc8\xd0\x0a\x99\x72\x7a\xfd\x51\x4e\x7f\x94\xd3\xff\xd9\x72\x3a\x13\xd2\xd3\xb1\x47\xab\xb3\x40\xfc\xc6\x09\x9e\x4f\x09\xe8\x9f\x17\x28\x81\x06\x71\x82\xab\x61\xac\xcb\xe9\x6b\x85\xe3\x2f\x15\x8c\xd7\xb0\x28\xec\x03\x14\x3a\x1e\x8f\x1f\x5c\x3b\xf4\xfd\xc8\xe3\x84\x3b\x1e\x8f\xb5\xdb\x0d\x7c\xc9\x72\x57\xec\x7c\x8b\x0b\x9d\x74\xbc\xf8\x42\x27\x1d\xc3\x85\x0e\x15\x5c\x96\xb9\xb7\xc9\x93\xf3\xfd\x9b\x93\x25\x1e\x28\x5b\xd3\x85\xf3\xa6\x8e\x89\x08\xe9\x78\xfc\xc9\x5d\x40\xb7\x2a\x42\x0e\x5d\x56\x5e\xa3\xa1\xee\x88\x67\xb4\xe8\xf8\x7a\xb7\xe6\x52\x9c\xed\x07\x57\x8c\x08\x8e\xc3\x3f\xcc\xcb\x61\xa5\xed\x45\x45\x75\xb3\xb1\xdb\x20\x12\x46\x87\xf1\xaf\xf9\x08\xb8\x8a\xdc\xad\xe1\x69\x90\x7c\x39\x49\xe6\x69\x86\x87\x87\xd8\xba\x0c\x56\x9a\xcf\x2f\x78\x37\x24\x22\x4c\x64\xba\xc3\x20\xcc\x69\xdf\x5b\xe6\x6e\x14\x10\x0c\x87\x87\x49\x78\x11\x64\x98\x1e\x09\x3d\xad\xe7\x15\xbb\x5b\xdf\x69\xee\xd0\x85\xdd\xcf\x2b\x76\x37\x04\xc6\x41\xba\xb0\x75\x6f\x99\xbb\x35\x7d\x8e\x33\xba\xa1\xe7\x8e\x7d\x4e\xa9\xbb\x37\x5f\x60\xee\xf3\x8a\xdd\x99\xee\x8f\xaf\xa7\xb9\x8d\xfb\x8a\xdc\x99\xea\x17\x35\xec\x2b\x72\xd7\x21\x27\x72\x5c\x86\x29\xe8\x9d\x24\x9e\x1e\x06\x69\x7a\x19\x27\xc3\xbc\xf1\x2f\x58\xe7\xce\xeb\x60\xd1\x98\xf8\x8a\xdc\x99\x0c\x17\x35\xec\x2b\x72\x1f\xac\x67\x51\xdb\x39\xa5\xdc\xcd\x8b\x87\xd5\x55\x94\xce\xfb\x70\xf3\x86\x21\x35\xd5\x3c\x92\xcf\xd3\x30\x4d\xc3\xe8\xfc\x69\x61\x6c\x67\x71\x6a\x5e\x5d\x29\x58\x3a\xbe\x3a\xf4\x14\x28\x5f\xef\x88\x16\xdf\x72\x1d\x8f\xc7\x4a\x1e\x52\xc3\xf6\x42\x3b\x45\x1b\x96\x11\xad\xc6\xe3\x19\xfa\xf1\x0c\xfd\xcf\x3e\x43\xcb\xbb\xae\xfe\x1f\x7f\x18\x77\x5d\x9b\x13\x7c\x85\xde\xe0\x04\x9f\xa7\x7f\x04\xe9\x1f\x21\x7a\x1d\x4c\xf0\xd5\x7f\x26\xd9\x28\xad\x8e\xe7\xfa\x71\xb8\xc3\x82\xa2\x1f\xe1\x11\x4e\x70\x34\xc0\x5d\x44\xda\x4f\xbb\xab\xab\xe7\x61\x36\x9e\xf7\xab\x83\x78\xba\xfa\x5b\x18\xed\x84\xd1\x41\x72\xbe\xfa\xdb\xd6\x61\x7c\xdc\x1b\x07\x61\xb4\xda\x9f\xc4\xfd\xd5\xf4\x32\x48\xa6\xab\x61\x94\xe1\x24\x0a\x26\xab\xa4\x4b\xf8\x2a\xe3\xff\x56\xcf\xe3\xff\xf5\xbe\xd9\x7c\xe0\xab\x31\x79\xdf\x75\x4c\xb0\xf9\x87\x1f\xae\xe1\xc7\xdf\xe2\xb2\x8b\x5a\xbe\xe2\xec\x32\x4e\xbe\x1c\x61\x88\x78\x9f\xa7\x28\x37\x8b\xdb\xda\xf2\xfe\x1f\x7f\x7c\xca\x29\x75\x17\xe7\xce\xeb\x68\xb0\x1d\x05\xfd\x09\x5e\x84\xa5\x52\xd2\x8d\xa0\xbb\xc0\x5d\x70\xbb\x0c\x66\x05\x71\x93\x25\x3d\xb8\x39\x0b\xdc\x01\xb7\x61\x7c\x19\xb1\x64\x06\x79\x88\xf1\x62\x6e\xac\x1c\x5f\x8b\xfb\x2c\x7b\x10\x9b\xcf\x0a\xa0\x45\x0b\xb9\x91\xb2\xbe\xdd\x19\xa5\x04\x67\x49\x88\x2f\x16\x85\x11\xe1\xc5\xdc\x68\x39\xbe\xde\x85\xb4\x32\xb2\xdb\x2d\x20\x2a\x52\xc6\x43\x4e\xc6\xa7\x3b\x0f\xd1\x39\x2e\xe0\x13\xef\xc6\x45\xff\x70\x87\x31\xa1\x49\xa0\x16\x84\x5a\x77\xe3\xa0\x7f\xb8\xf3\x68\xb0\xbc\x6f\xf9\xc8\xd0\x42\x6e\x7c\xac\x6f\x1c\xa5\x56\x21\x94\x72\x6e\x75\x2d\x15\xa7\xc9\x96\x95\xdb\x3f\xc9\x0f\x95\x97\x92\x11\xc9\x97\x9c\x0f\x28\x37\x8e\x33\xfd\x99\x53\xbf\x02\x88\x90\xa0\x7c\x3c\xc7\xca\xc5\xe4\x6c\xae\x3c\x28\xb2\xf8\x83\x5e\x33\x8e\xc3\x0b\xaf\x6f\x0c\x99\x13\xf8\xee\x3d\x43\xe6\xc3\x76\x28\x65\x35\xd8\xf0\xdd\x73\xbc\x72\x9c\xaf\x88\xb0\xe4\x8a\x99\xef\xbc\x97\x6c\x3e\x9e\xa9\x1e\xcf\x54\xff\xec\x33\x15\x3b\x50\xf1\x0b\xa2\x6f\x9b\xec\xe5\x36\x86\xd5\xdc\x3b\x2a\x98\x85\x5c\x18\xa7\x99\x82\xb3\x71\x9e\x05\x1a\xbd\x2e\xcb\x0d\x6f\xcc\x4b\x67\xd7\x33\x22\x1f\xb0\x50\xc6\xaf\x9e\x2a\x0c\x3c\xcc\x06\xe3\x12\xf9\x6e\xc6\xaa\x1b\x04\x29\x46\x2b\x84\xe2\xd3\x6c\xa5\xab\x7d\x82\xc9\x4a\xce\xd3\x6a\x3a\x0e\x47\x59\xc9\xc8\x4b\x86\xac\x1c\xc3\x35\xbb\x00\x63\xc9\xe0\xbe\x16\xe1\x4b\xe6\xed\x0c\x17\xb2\xaf\x1c\x68\xcc\x70\x34\x0c\xa3\xf3\x07\xc7\xe3\x90\xb6\xa3\xda\x10\xb9\x90\x62\x31\x68\x6d\x6c\x0c\x70\x56\x65\x9a\xa7\xed\x46\x91\x0e\x44\xa9\xc5\x96\x84\x0c\x9a\x29\x23\x68\xa4\xe0\x90\x9d\x1c\x52\x75\x14\x46\x69\x16\x4c\x26\x85\x5a\x36\x4a\xbb\xdd\xf8\xfd\x85\x72\xf0\x38\xc7\xd9\xfb\xf8\xbc\x40\x10\x01\x52\xca\x1b\x3e\x80\xb6\x68\x14\xc9\x69\x75\x16\x2f\x0c\xe4\x42\x8a\x2c\x68\xaf\x37\x0e\xa2\x73\x77\xc4\x82\x05\x32\x96\x98\x2f\xd5\x24\x4b\x1b\x3d\x4d\x10\x22\x1d\x53\x1a\x89\x59\x30\xc8\xb3\x5b\x3a\x72\xa4\xe3\x71\x15\x58\xa3\xc5\x6e\xd2\xb1\xcd\x6e\xfc\xe2\xd3\x82\x5b\x1a\x8b\x0c\x90\x75\x4b\xa3\x59\x12\xdc\xab\x9a\xde\x4f\x8c\xc8\xa5\xa9\x7f\x38\x44\x6c\xd2\x45\xd6\x35\x05\x6d\x96\xe1\x60\x16\xbd\x5b\xf3\x06\x19\xdf\x43\xdb\x2a\xe9\x59\x92\x28\xc5\x01\x67\xe3\x2e\xf9\x0f\x05\x96\x8e\xc7\x5d\xf2\x9f\x0a\x95\x5e\x5d\x89\x9d\x5a\xad\x47\x99\xf4\x51\x26\xfd\x87\xcb\xa4\x52\xd1\xcf\x9d\xac\x6f\xe3\xd8\xe2\x10\x48\xa9\x83\xf8\x11\x3e\x27\xf3\x1c\x24\x9b\xfd\xd0\x93\xdf\x28\x5d\x7d\xab\x17\xad\x7e\x4e\x63\x91\x43\x28\x1c\x04\x33\x15\x88\x0f\xc6\x5e\x6f\xf3\xd0\x86\xa0\x60\xc2\x3c\xd1\x99\xf9\x32\xda\x40\x2b\xb5\xab\x41\x67\xf8\x72\xd8\x18\x0c\x5b\xad\x97\xc1\x5a\xbb\x35\x68\xbd\x6c\x35\x3a\x2d\x5c\x5f\xaf\xbd\x1c\xb4\x6b\xb8\xd9\x1a\x76\x5a\xed\x4e\xa3\xbf\x22\x71\x71\x81\x09\xea\x41\xbd\x5e\xef\x0f\x6a\x6b\xad\xc1\xcb\xc1\x28\x58\x5b\xaf\x8f\x6a\x83\xe6\x3a\xee\x34\xfb\xc3\x76\x7d\xf0\xb2\xde\x5f\x0f\x46\xb5\xda\x8a\x9f\x39\x51\x1c\xbb\x8a\xa8\x1b\xf4\xc3\xae\x63\x10\x25\x2b\x64\x7e\xf0\x5d\x67\xff\xe8\x56\x4f\x0b\x13\xb4\x2d\xc8\xe6\xb8\x3a\xe0\xda\xdd\xa5\x50\x35\x8e\x99\x3f\x8b\xcf\xba\xf5\xca\xb3\x05\xf3\xf4\xac\xdb\x20\xcc\xb6\xfd\xc8\x6c\x1f\x99\xed\x3f\x9b\xd9\x4a\x5e\xcb\xb5\x5f\x06\xb3\xcd\xb3\x4c\x1e\x25\xf1\x1f\x78\x1a\x44\xd5\x21\xfe\xf9\x5e\x18\xb4\xcb\x47\xdd\x70\x50\x37\x6f\x48\x2d\x9b\x5a\xed\x1a\x94\xe5\x89\x67\x9f\xe0\x51\xc9\x5c\x43\x35\x89\xca\x77\xfa\x42\xcb\x6d\x63\x94\x48\xcd\x12\x86\x83\xb3\x52\xd4\xf8\xa2\xd4\xd1\x15\xd0\x4a\x15\xfd\x83\x52\xc3\xba\xcd\x8d\xe6\x93\x09\x15\x2d\xf9\x58\xa8\x59\xb4\xcd\xdb\x4d\x6d\x9c\x92\xa9\x36\x44\x16\xe8\x64\xba\x30\x29\x39\x4b\xc1\x0d\xe8\x82\x56\x81\xd0\x2e\x15\x54\x8d\x8c\xe3\xb4\xe4\x1e\x29\xa8\x66\x1d\x87\xbc\xdf\x37\x5a\xc2\x71\xf1\x6a\xd5\xd5\x25\x05\x8e\xa9\xc1\x71\xc5\x6e\x31\x46\xf8\x3f\x5c\x6f\x69\xdd\x2e\xc1\xbf\x68\x87\x63\x2d\xc7\xfc\x82\x4e\xd3\xf0\xfa\x6a\xaf\x59\x4e\x75\x57\x9e\xf5\xfc\x7e\x53\x50\xfa\x2c\x6a\xb9\xf2\xd5\xbe\x9b\x14\xf9\xe3\x8f\x2c\xb1\x3e\xfa\x61\x83\x12\x8e\xf1\x8a\x6c\x28\xa3\x30\xc2\x43\x3e\x4e\x06\x04\xd1\x56\x97\xd5\xf2\x0c\x17\x64\xa0\xcf\x62\x84\xaf\x68\xb0\x24\x6e\x61\x8e\x46\x49\x3c\x95\xa7\x6d\x91\xd8\xbd\x8a\xf6\xc9\xc6\x16\xe2\x94\x51\x12\x0c\x93\x31\x96\x0c\x18\x37\x48\xb7\x89\x48\xc2\xd3\xc6\x75\x87\x8d\xd4\xd7\x0f\xf3\xc9\xe4\x46\xb1\x76\x0f\x47\x08\x5f\x85\x29\x14\x77\x0e\xb9\xd1\xa2\x57\x61\x18\x8e\x64\x2e\x34\xde\x1a\xcd\x86\x06\x7a\xb6\x09\x8e\xce\xb3\x31\x7a\x81\xea\x67\x65\x47\x5e\x27\x28\x33\x8b\x67\xa5\xf2\x2b\xb4\xba\xca\x2f\xbe\x08\xff\x87\xf5\x04\xa3\xf5\x83\x2a\xdc\xe8\xc3\x4d\x0d\x1c\x24\x66\x59\xec\x26\x45\xdd\x10\xc2\x47\x8c\xec\x15\xef\x85\x97\x1a\x75\x68\x3a\xf7\xed\x7f\xd6\x52\x55\x93\x3a\x42\x94\x44\x3c\xd5\x15\x90\x57\x7f\x1e\x4e\x86\x6f\x71\x56\x52\x8e\xe7\x38\x9a\x4f\x71\x12\xf4\x27\xb8\x8b\xb2\x64\x8e\x6d\xdd\x5f\x30\x85\x1b\x2b\xc1\xd6\xab\xe9\x6c\x12\x66\xa5\x95\xea\x8a\x12\x70\x93\xf1\x7b\x28\x0c\xda\x5b\x3e\x51\xf0\x86\xcf\xc9\xcf\xa8\xae\xce\x48\xdc\xff\x7c\xca\x6b\x9c\x11\x6e\xac\x3d\x7f\xfd\x8a\xfe\xbc\x79\xa5\x16\x36\x8b\xbc\xd2\x54\x62\xa2\xf9\xfa\x19\x4f\xab\x05\xff\xb8\xf3\x84\xc5\xfd\xcf\x15\x28\x5f\xa1\x43\xc6\xfa\x42\xe0\x07\xe9\x75\x34\x78\x0b\xfb\x0d\x11\x79\xa1\x0b\xe5\x33\x3e\x04\x30\x88\x9b\xac\x48\x49\xf1\xd3\x30\xaa\x69\x93\x04\x20\x74\x96\x01\xd7\xcb\xe8\x39\xe0\x50\x1d\x8c\x83\x64\x33\x2b\xd5\xca\xd5\x2c\xfe\x38\x9b\xe1\xa4\x17\xa4\xb8\x54\xe6\x9f\x53\x22\x3f\x94\xea\x65\xef\xc6\xc3\x67\xd6\x9f\xc1\x5c\x6e\xdc\x32\x1d\x3b\x0f\x89\xc6\x6b\x9c\x93\x0e\xd9\x2b\x46\x08\x28\x2a\x4f\x2c\x89\xb7\xfa\x3e\x06\x59\xe9\x0c\x4d\x0f\x5d\x12\x5d\x09\x88\x6e\xf7\x8a\xca\x86\x1b\xfc\xe4\x77\x90\x8f\xfa\x72\xbd\x94\x97\xfd\xfe\x28\x60\x48\xda\x39\x39\x3b\x04\x2d\x2f\xdb\x2b\x35\xa1\x12\x4e\x92\x0a\xd2\xb7\x0e\xfe\xc7\x71\xa1\x65\xdc\x83\xcd\x6a\x2a\x97\x07\x37\x72\xc8\xd8\x22\xe7\x78\x73\x42\x65\x8f\x34\x0f\x20\xcb\x00\xa8\xcc\xea\x39\xf6\x6d\x27\x72\xf7\x1d\x24\x98\xc8\x8a\xb3\x79\x82\xd1\x7f\x1d\x1f\x7c\x38\x3a\xec\x21\xde\xca\xe5\x38\x1c\x8c\xe1\xf0\xc4\x77\xa0\x30\x42\x7d\x50\xda\xb2\x22\x06\x47\x94\x6f\x05\xdf\xab\x56\xab\x37\x4c\x85\xe7\xda\x9b\x11\x39\x12\x26\xb3\x81\x52\xd5\xc9\x1d\x65\xc7\x3d\x64\x11\x5c\x33\x0b\x1d\xd3\x6e\xae\xab\xca\xa3\xb6\x96\xfc\xf4\x4c\xd7\xaf\x93\x69\x62\x55\x8c\xcd\xaa\x04\x7b\xa2\x2a\x0a\x92\x25\x5b\x25\x95\x4a\x62\x9f\x2c\x97\xd5\x29\x63\x58\xb1\x89\xe6\xb3\xa6\x4e\xbb\x6f\xea\x58\x4d\x8f\x86\x93\x0f\x90\x72\x30\x37\x82\xf6\x90\x23\x76\xe7\xf1\x88\xfd\x78\xc4\xfe\x67\x1f\xb1\x15\x7d\x26\xe3\x10\x53\xc6\xd2\xf5\x93\xf6\x7f\xe1\xd1\x28\xc1\xd7\xe8\xd7\x70\x32\xf8\x82\xd1\xeb\xcf\x78\x34\xf2\x85\xeb\x59\x2a\xb6\xcf\x7e\x90\x90\x23\xfc\x41\x10\x0d\x70\x00\x65\x5d\x51\x7d\x6e\x11\x08\x88\x55\x79\x1b\x5c\xa0\x5f\xe3\x78\x88\x5e\x9f\x7b\x0f\xf9\x2d\x79\xc8\xff\x2f\xc6\x4d\x35\xef\x61\xc6\x62\xf3\x52\xe3\x3b\x22\xd5\x99\xd9\xec\x5d\xa9\xec\x71\x92\xc4\x46\xf4\xa0\x55\xfa\x8e\x1a\x21\xd0\x6d\x67\x2f\x5b\x49\xc9\xc6\x38\x8b\xa3\x34\xec\x4f\x28\x81\xcd\x02\xf0\x22\x41\x53\x76\xe9\x43\xf6\xa2\x59\x12\x5f\x84\x43\x9c\xa4\xa2\x56\x30\x49\x63\xbb\x6a\x3c\x99\x90\xaa\x84\xda\xb8\x03\x37\x8a\xe2\x21\xfd\x1a\x46\x83\x78\xaa\x42\x26\xc0\x58\x56\x0a\x7a\xe7\x9a\x85\x53\x4c\x16\x5b\x98\xa2\x3a\x4a\xf1\x20\x8e\x86\xb0\x3b\x86\xd1\xf9\x04\x67\x71\x04\xc3\x49\xba\x97\x73\xd0\xe7\xa8\x6a\xc7\x7d\xfe\x12\x6d\x88\xae\x28\x7a\x06\xd2\x36\x68\x80\x6f\x94\x97\x1c\x17\x55\xeb\xe0\x3d\xfc\x11\x09\x65\x9c\xc4\x51\x3c\x4f\x27\xd7\x10\x07\xc3\xb3\x0f\x93\x4f\x8e\xf3\x08\x1a\x06\x59\xe0\x3d\x21\xeb\xbd\xd5\x54\x1e\xd1\x50\xeb\x3c\x01\xa3\x9e\xd4\x7e\xd0\x7a\xaf\xa5\xc9\x8d\xa3\x34\x26\x5b\x17\x21\x8a\x12\x25\x8d\xea\x5e\x74\x11\x4c\xc2\xe1\x21\x2b\x5f\x52\x65\x1e\xee\x86\x0d\x83\xa1\x48\xf8\xfa\x1e\xcf\xc8\xbc\x9a\xc5\x87\xf4\x1d\xa0\x54\xa5\xbd\xaf\x40\x37\x99\xb5\x85\x72\x7e\x61\xa7\xf2\x0d\x7d\xae\xa8\x30\xcb\x40\xf3\xbb\x72\xe8\x14\x6f\x24\x4c\x7f\x21\xe8\x1e\x51\x2a\xc4\x42\x50\x53\xba\x99\x8d\x93\xf8\x12\xe9\xdd\x33\xcb\x6b\xdd\x61\xdd\xa4\x9f\xaa\x85\x4e\xfe\xc1\x52\xb3\x0f\xd2\x6c\x2e\x09\x98\xe7\x52\x21\xfd\x2c\x26\x06\x00\x6e\x51\x84\x12\x3d\xb7\x10\x6d\xf0\x24\xcc\x8a\x6c\x9c\x47\x1d\xf7\x43\x08\xf6\xdc\x53\xb9\x9f\x81\x2c\x20\xcf\x93\x4e\xe1\x24\xf1\xa4\xd4\x54\x7b\x53\x36\xed\x6d\x10\xcf\x58\x75\x1b\x1a\x5b\x3c\x64\x56\x6d\xb5\x7d\x4b\xc8\x65\x79\xc3\x35\x12\x34\xa3\x73\xfa\x8f\x0d\x2e\x6a\xcc\x3b\x19\x90\x02\x6f\xc8\x77\x87\x92\x89\xd6\xbb\x0f\xc2\x84\x16\xbe\x33\xc2\x04\x9c\x54\xea\xe4\x4c\xe6\x76\xa4\x98\xde\x03\x2d\xea\x34\xc8\xf5\x6c\x30\x1b\x25\xde\xca\x9d\x48\x2f\x5d\x44\x7b\x5a\x87\x04\xd1\xa1\x05\xdb\x1f\xce\xc4\xbe\x4a\xa4\x4d\x7e\x26\x64\x22\x9f\x45\x71\x19\x9f\x2a\xb7\x6a\x2e\x97\x96\x44\x5d\x7d\xd7\xf7\x6e\xf7\x8b\x76\xee\x8c\x1c\xa9\x98\xe0\x62\x22\x4a\xbe\x1d\x8a\x4f\x0b\x39\x36\x0d\xfe\x7f\x03\xd0\xf6\x86\x0b\x97\x8c\xe3\xab\xb0\x4b\xe2\x98\x64\xf1\x30\x46\x83\x09\x0e\xa2\xf9\x0c\x45\x00\x9f\x0c\xb0\x38\xb6\xe7\x0d\x95\x82\xbd\x63\xe5\x51\x24\xd5\x88\x28\xa2\x71\x7d\x2c\x89\x70\x74\x4a\x4b\x9f\x11\x21\x89\x54\xef\x22\x0a\x24\x1c\x76\x2d\x40\x5d\x17\xc8\xae\xfc\x09\x7a\x5d\xc4\x7c\x99\xf5\xd1\xd7\x18\x00\x13\xc0\xf4\xdd\x9c\x21\x54\x12\x2b\x7c\xc1\xe4\xc6\x33\x21\x94\x12\x11\x94\xd9\xd1\xc2\xe9\xe6\x3c\x24\x47\xba\xd0\xd4\x1d\x93\x3a\x8e\x39\xb7\xe6\x36\x77\xe4\x05\x08\x9d\x48\xa1\x2e\xef\x10\x35\x2d\x73\x0c\xf2\x2b\x65\x78\x24\xfe\x6c\x74\x4a\x4c\xa3\xfa\x05\x5f\xa7\x25\x59\xb7\xcc\xb5\xbc\x1b\x1b\x1b\xa8\x86\x7e\xfc\x11\xf9\xc6\x90\x10\x53\x72\x42\xdf\x97\xb4\x42\xaf\xf4\x71\x36\x05\xe0\x9c\xf1\x96\xbb\x4f\x82\x09\x2f\x20\xf2\x3f\x1f\xf6\x29\x1e\x8c\x83\x28\x4c\xa7\xfc\x18\x9a\xcf\x1c\x00\x40\xfe\xf0\xd2\x36\xd4\x81\xfd\x82\xf1\x4c\x24\x10\xe0\x9d\x5d\xfd\xe9\x73\x3a\x0e\x23\xd2\xd0\xd5\x20\x9e\xce\x26\xf8\x2a\xcc\xae\xbb\x6d\x38\x92\x91\x02\x84\x20\x4a\x64\x73\xf8\x82\xaf\xa9\xa6\x40\x8c\xa6\x32\x5e\xab\xab\x28\xc1\xd3\xf8\x02\xa3\x60\x32\x81\x5e\xa5\x15\x84\xaf\x06\x78\x96\x81\xd8\xcf\x5e\xa9\xe5\xb3\x31\xbe\x46\x11\xa6\x23\xd2\xc7\xac\xfe\x90\xf4\x78\x1e\x4c\x26\xd7\xa8\x7f\x0d\x43\x46\x86\x87\xe5\x02\x00\x9a\xf9\x95\x6c\x48\x61\x74\x5e\x2a\x2b\xfb\x40\xe9\x07\xad\x77\xe8\xeb\x57\x82\x6f\x35\x8c\x86\xf8\xea\x60\x54\x02\x3f\x45\x42\x6c\x9f\x56\xca\x30\xf9\x2f\xea\xe6\x06\xa1\x50\xd8\x17\x7c\x7d\x56\x15\x2b\xd1\xb4\x87\xb6\x29\x92\x94\xb7\x6c\x93\xff\xc6\xe4\x09\xa7\x4c\x32\xef\x03\x6a\x9c\x8b\xe2\xa8\x08\x4f\xa0\x36\xb5\x79\x34\xc9\x4c\x86\x6d\x15\xa8\x87\x0a\x51\x87\x80\x73\x74\x26\xc5\x99\xd6\x7b\x02\x58\x51\x45\x56\xd0\xa0\xba\x7d\xb2\xfb\xe9\xf0\xe0\xfd\xfb\xbd\x0f\x6f\x3f\x9d\xec\xed\x6f\x1f\x7c\x3c\x51\x8f\x47\x45\x66\xc0\x16\xaa\x34\x89\xe9\x41\x8e\x8e\xb6\x4c\x46\xf0\xda\x0a\xb2\x00\x6d\xa0\xd3\xb3\x57\xfa\xfb\x3d\xf0\x37\xe6\xaf\x8b\x2d\x55\x01\xb0\x3a\x9b\xa7\xe3\x92\x49\xf7\x4c\xc4\xd3\x4a\xef\x0d\x53\x5a\xf8\x0b\xbe\x2e\x5b\x63\x20\x01\x2e\x31\x78\x85\xc4\x4d\x01\x59\x4d\x97\xbb\xba\x8a\xa6\xc1\x4c\x63\x92\x21\x90\x2d\x30\x14\x20\x31\x42\x9a\xfa\x30\xed\x07\x33\x45\x75\xa1\xe8\xb5\x75\x57\x71\x2a\xb8\x02\xd7\x28\xff\x69\x8e\xc1\x7e\x30\x3b\x85\x6a\x21\x6c\xf1\x7c\x64\x4e\xa1\xf8\x99\xe2\x92\x2e\x1a\xd7\x1c\xe7\xd1\xd2\x32\x73\xac\x4b\xcd\x5a\x7c\x93\x93\x83\xad\x83\x2e\x27\x32\x34\x89\xcf\xff\xc3\x94\xaa\x63\x8f\x5c\x7d\x57\x49\xba\x80\xb2\x20\x75\x1e\x1d\xd9\xb7\xea\x34\x98\x95\x7c\xc6\x0a\xfc\x0f\xec\x17\x87\x72\x94\xc9\xd8\xb3\xa3\x5e\x38\x54\x3d\x6f\x04\x45\x7c\xc1\x28\x9d\x27\xa0\x27\xe6\xcc\x2a\x4c\x51\x9a\x85\x84\x1e\x28\x27\xc7\x43\x14\x8c\xc0\x43\x28\x49\xc2\x8b\x60\x62\xec\xb5\x1a\x4c\x32\x20\xe0\xf7\x4f\x97\x46\x38\x3c\x33\x51\x94\x5d\xaa\x0e\xa4\x3d\x80\x5e\x47\x7c\xf1\x7a\xcc\x70\xdd\x89\xfa\xe9\x06\xe1\x09\xd3\x33\x3b\x6a\x8c\x82\x49\x8a\xd5\x5b\x36\xe6\xf7\xb4\x70\x4c\x59\xfd\x1f\x7e\x60\x6d\xa2\x5b\xc0\x20\xf3\x02\x33\xae\x2c\x5a\xcf\xe1\xff\x95\x35\x9e\x3f\x40\xcd\x02\xe3\x58\x5c\x31\x80\x34\x0a\x53\x7a\x09\x15\xf5\x51\x32\x16\xbb\x7f\x98\x74\x5c\xfc\x7a\x06\xa4\x5e\x72\xfa\x52\x2e\x1d\x99\x51\x35\xf4\x1b\x2f\x23\xf7\x92\x9d\xbb\x82\x29\xa4\x9f\x75\x1b\x10\xdb\x87\x29\xc3\x9f\x75\x9b\xe0\x87\xba\x56\xe4\x8e\x8c\x05\xdd\xc4\x59\x16\x46\xe7\x6e\xd7\x5e\x60\x4c\x43\x25\xf7\x31\xda\x10\x4e\x6b\xaf\xac\x12\x32\xd4\xb3\xb0\x0f\xf2\x45\x2d\x62\x8d\xb2\x7e\x13\x94\xd7\x1f\xaf\xf5\x1e\xaf\xf5\xfe\xe1\xd7\x7a\x2c\xa4\x2f\x3b\xb5\xdc\x26\xac\xef\x22\x73\x58\x4f\xf2\x0b\x23\xf7\xc5\x32\x86\xb3\x7c\x49\xd7\xd9\xe1\x60\x73\x38\x4c\x61\xe8\xc4\xee\x16\x44\xa0\x96\x4a\xd1\x9c\x8a\x5f\xcc\xeb\xad\x42\x84\xaf\x30\x83\x50\x79\x08\xb2\x02\xd0\x4d\x95\xee\xf6\x4f\x9f\xaa\xe7\x03\x76\x3e\x7b\x6a\x2a\x89\xc8\xb6\xf9\x94\x5d\x5b\x29\xe5\x14\x5e\x45\x03\xf5\x70\x5f\x3a\x52\x2e\x8e\x98\xc7\x95\xc6\xd1\x98\xdc\x44\xc6\xde\xa1\x6a\xf4\x09\x45\x74\xdf\xe6\x3d\x4d\x1d\x9b\x85\xcb\x1e\x87\xff\xe9\xfb\x96\xb9\x3d\xf9\x74\x97\xc2\x42\x90\x47\x22\x02\x94\x7f\xfc\x11\x70\xa7\x8a\xa9\x30\x3a\x07\x6e\x5c\xd6\x20\xf2\xeb\x8b\x45\x39\x4d\x29\x44\xd5\x4d\xf9\xb6\x9d\x14\xd2\xd0\x24\x48\xa1\x99\xe3\x8c\x4c\xf6\x0f\x1b\x1b\xd6\x40\xf3\x3f\xeb\xc5\xea\x2a\xcd\xfd\xaf\x91\x14\x2c\xb5\x2c\x99\x13\x99\x2d\x49\x33\x94\xc6\xd4\xce\x71\x36\x03\xd6\x0d\x67\xe7\x20\xba\xce\xc8\x81\xbf\x82\xfa\x78\x44\x18\x00\x5d\xe2\xfc\x0a\x15\x46\x83\x2a\x19\x8d\xbf\x70\x54\xfa\xc1\x81\xf5\x8f\x3f\x22\xd7\xc8\x97\xad\xfa\xc8\xbe\x6e\x20\xa8\x3a\xfc\xa3\xbd\x9d\x8d\x29\xdf\x8c\xf0\x55\x86\x7a\x87\x1f\xd1\xe0\x7a\x30\xc1\x15\xd1\x4d\x18\x76\xb1\xd9\x40\x4f\xa0\xcb\xcc\x66\x69\x96\xc4\x03\xc2\xb3\x52\x3a\x3a\x56\x2b\xca\x31\x58\x2c\x13\xd7\x5c\x38\x3a\xc2\x48\xc3\x2c\x75\x53\x41\xb5\x22\xfd\x73\x0c\x2b\x25\x05\x9f\x68\xa6\x18\x83\x3d\x15\x00\x4c\x33\x36\x45\x17\x5b\xb2\xed\xa0\x3c\xf9\x7e\x4d\x4b\xa8\x9b\x8a\x14\xc2\xf7\x86\x15\xc9\x26\xd8\x7b\x55\x87\x44\x75\x06\xc0\x59\xc8\x3a\xe1\x76\x92\x7b\xce\xbc\x9c\xde\x64\x9b\xf9\x26\xf3\x86\xfc\x87\x54\x5d\xd3\x1e\x91\xa3\x15\xe5\xd4\x73\xca\x85\x9f\x3f\x57\xca\x89\xf5\xaa\x9c\xf4\xe1\x43\x30\x1c\x0a\xdb\x2e\x25\xf1\xa7\xf8\x6e\x4e\x8f\x72\x70\x50\x58\x2c\x37\xde\x82\xf7\x8a\xad\x38\x15\xe8\xc4\x48\xa8\x96\xbe\xb2\xdd\x5c\x8b\xc5\x70\x24\x5f\xe9\x5a\x29\xc9\x82\x40\xab\x60\x20\x5f\x08\x09\x75\x16\xfd\x12\xad\x45\x60\x42\xe5\x5c\x52\xe6\xa0\x9c\x33\xda\x4e\xa9\x56\x20\xe4\x37\x60\x23\xb2\xba\x9e\xee\x82\xc8\xbe\x8f\x49\x4a\x1f\x65\xdf\x7f\xba\xec\x2b\x4d\xda\x78\xc6\xde\xfb\xf2\xd1\xdd\xeb\x07\x91\x2e\xed\x86\xfd\x40\xb8\xde\xe2\x2b\xaa\xae\xce\x73\xdd\x3d\x9e\x06\x49\xb6\xcd\x0a\x4a\xb7\x5b\xef\xd5\x18\xa8\x95\xa0\x59\xde\x17\x43\xe7\xad\xbc\x16\x97\x60\xc7\x59\x12\x46\xe7\x37\xe0\xda\xe2\x7a\x4f\xa4\xe5\x7e\x10\xa9\x9f\x7e\x09\x26\x73\x7c\x83\x2e\xc8\x3f\xec\x3a\x84\x40\x1e\xe1\x04\x2f\xb8\x21\xad\xe8\xe6\x05\x10\xa5\x86\xe1\xa4\x8b\xc5\xd9\xb8\x02\x18\x11\x69\xbd\x42\x5b\xb2\xb7\x30\x50\xbb\xd1\x51\x86\x74\xd3\xfd\x20\x2a\x65\x71\x99\xa9\x8a\x40\x87\x43\x3e\x73\x95\x4f\xc9\x61\x45\x44\xea\x41\x9e\x88\xd2\x4a\x48\xd5\x37\x14\x22\xf3\xd3\x5d\xb1\xf5\xc7\x0c\xe2\x56\x98\x10\x59\xcc\xe5\x10\xc3\x7b\x74\x12\x33\xcf\x5e\xb5\x3b\x50\x9d\x41\x2f\x95\xed\xae\xf1\xf6\x84\x1c\x03\xdd\x70\x49\xba\xe0\x22\x21\x3c\xa5\x71\x36\x56\x73\x82\x97\xca\xd0\x08\xc3\x36\x4a\xb3\x30\x9b\x53\x81\xcb\x36\xff\x1a\xe2\x59\x9c\x86\x99\x8a\x25\x83\x2b\xd0\x03\x30\x83\x49\x88\xa3\xcc\xb4\xc4\x28\xdc\xb0\x65\x62\xc1\x73\x8d\xdb\x23\xb8\x2c\x46\xf6\xf8\x71\x15\x7c\xee\x55\xb2\x20\xbd\xd1\x3c\x1a\x82\x4d\xe4\x00\x27\x59\x10\x8a\xe9\xf7\x2c\x1f\x31\xb1\xcb\xad\xa3\x07\x5f\x42\x02\xaf\x5b\xac\x25\x36\xf2\x64\x36\x8d\x94\x5f\x8a\x6c\x2b\xbc\xd7\xb3\x58\x4a\xb4\x04\x74\x97\x36\xa0\xd0\xe6\x64\x8e\xbb\xf4\x1f\x2e\xe6\x1a\xd9\xde\xbd\xb3\xc2\x26\x5f\x4e\x0a\x04\xb6\x0f\x07\x88\x73\x42\xc4\x39\x24\x2a\x4d\xe7\x69\x06\x5b\x1d\x9e\xe2\x28\x13\x74\xd3\xbf\xce\x70\xda\x6c\x94\x99\x30\xfe\x43\xd9\x98\x48\x56\xee\xde\xa7\x2f\xb5\xe6\x8f\x57\xa7\x94\x8a\xe6\x51\xf8\xdf\x73\x8c\xc2\x21\x8e\xb2\x70\x14\xea\x9c\xb8\xd0\x5c\xf3\xd1\x29\x30\xc3\xd0\xa4\x9b\x6b\x06\xb0\xeb\x28\x7b\xd0\x2b\x93\x08\xf8\x18\x97\x82\x7e\x58\xae\x06\x19\x61\xac\x55\x3e\xbe\x1c\xf4\x9f\x77\x25\x02\x4b\x56\xe5\xa3\xe8\x0c\x82\x60\xef\x87\xcf\xba\x4d\x22\xba\xf2\xcc\xfd\x37\x67\x95\x76\xa1\x5c\xc9\x4c\xbb\xdb\x2e\x94\xb0\xed\x95\xaa\x84\x8f\x89\x7c\x31\x0a\x06\x59\x9c\x5c\x57\xa8\x42\x99\x0c\xec\x13\xc2\xa6\x89\xa8\x1f\x8f\x90\xe8\xcd\xc6\x06\x7a\x46\x23\x32\x3d\x83\x32\x4f\x56\x57\x51\x2f\x9e\x4e\xe3\xe8\xbf\x8e\x9f\x3e\x79\x62\x75\x5e\xfe\x62\x0d\x70\x9c\x4a\xcf\xc8\x30\x24\xf8\x59\xb9\x82\x94\x57\x38\x1a\xbc\xe8\x07\x29\xee\xb4\x8c\x0f\xd3\x61\xdb\x2c\x7a\x31\xfb\x32\x1c\x19\x2f\x07\xe1\x6c\x8c\x93\x17\x14\x72\xf9\xd5\xd3\x27\x37\x4f\x9f\xe0\x49\x8a\x91\xd2\x19\xaa\x30\xa7\x7d\xe1\xc3\xf0\x0c\xfd\xf8\x23\xfb\x50\x0d\xa6\x43\xd1\xb7\xcd\xfd\xad\xa7\x4f\x9e\xd0\x0f\xa5\x53\x8e\x73\x05\xe9\xa8\xc2\x33\xc1\x90\x7e\xa0\x88\xc1\x6f\x15\x9f\x33\x31\xca\x2a\x62\xac\x21\x1a\x0d\x03\x95\xfa\x49\x7c\x99\xe2\xa4\xfc\xf4\xc9\x13\x31\x62\x71\x9c\x55\x7b\xc9\xf5\x2c\x8b\xff\xeb\x98\x56\xbd\x81\xd3\x93\xba\xfd\x88\xef\xe8\xcf\xa7\x4f\x9f\x94\xf4\xe3\xd8\x13\x44\x35\x22\xc7\xe3\x38\xc9\x06\xf3\x2c\xa5\x6f\xc8\xb2\xe9\xa1\x0d\xc4\xeb\xbe\x52\x5e\x7f\x9a\x84\x7d\xf2\xa9\x3a\x09\xfb\xca\x7b\x50\x86\xf5\xa0\x53\xe4\x2b\x29\x55\x55\xde\x69\x10\x82\xc9\x79\x0c\x20\xc8\x8f\x57\x4f\x05\x16\xef\xe3\xf8\xcb\x7c\x86\xb2\xa0\x3f\xc1\x0a\x26\xc7\x6f\x0e\x7e\x63\x67\x3e\xf1\x6e\xef\xc3\x2f\x9f\x5c\xef\x8f\x3f\xbe\xf9\xb4\xbf\xf7\xdb\xa7\x9a\xef\x43\xdd\xf7\xa1\xe1\xfb\xd0\x74\xb6\xed\x6b\x47\xfd\x68\xb5\xa5\x7e\xb4\xda\x53\x3f\xf2\x36\xc5\xd0\xf4\xe2\xe9\x8c\x1c\x14\x27\xf6\x10\xb9\xa6\xd4\xa8\x35\x8c\xe7\x7d\x22\xf5\x93\x5a\xb2\x00\xb0\x58\x15\x0b\xa4\x5a\x2a\x84\x10\x4e\x10\x85\xe8\x35\x6a\xb4\x3b\xaf\x50\xf8\xfc\xb9\x06\x5e\xc8\x88\xe8\x35\xaa\x37\xd6\xad\x6f\xe4\x6f\x78\x1a\x9e\xa1\x0d\x02\xe3\x35\xaa\xbf\xd2\xbf\xd3\xab\xd4\x9c\x5a\x25\x5a\xad\x8c\x7e\x47\xb5\xab\x7a\xbd\x6f\xd6\x97\x8f\x37\x4f\xb5\x5e\xff\x1a\x4c\xbe\xa0\xb7\x3b\xa5\xc6\xef\xeb\x65\xbd\xb7\x57\x34\x44\xa2\xfe\x2e\x34\x5e\x2e\x35\x02\xca\x20\xa7\xfd\xf8\x4a\xff\x08\x86\x06\xa4\xcd\xab\x10\xfd\x8e\x4a\x57\xb2\x43\xec\x77\x43\xf9\xdd\x54\x7e\xb7\xca\x46\x67\x01\x4a\x29\xbd\x42\x3f\xff\xfc\x33\x5a\x87\x92\xe9\x15\xfa\x11\xd5\xae\x46\x23\x3a\x40\x9d\xa6\x51\x85\xac\x8e\xd3\x2b\x32\x90\xe9\x95\xf1\x89\x2f\x9e\xd3\x14\xbe\x5f\xbd\x7a\xea\xed\xd4\x74\x3e\xc9\xc2\xd9\x24\x1c\x80\x96\xc0\xee\xde\x15\x21\xe3\xe1\xe9\xd5\xd9\x2b\xc7\xb7\x16\xfd\xd6\x70\x7e\x5c\xa7\x1f\x5b\x67\x39\xad\xa7\xf3\x3e\x02\xf9\xa6\x82\xa6\xe1\x15\x1a\xc4\x93\xf9\x34\x4a\x35\xea\x57\x61\x12\x49\xa1\x34\x84\x5e\xfd\x44\x68\xa6\x56\xe7\x23\xc5\x1e\x6b\xf5\x5a\xcd\x1c\x5a\xb1\x92\xe9\x60\x95\x32\x98\x98\x56\x19\x7d\x25\xbf\xe9\x78\x7b\xaa\xd4\xd5\x2a\xf5\x8e\x52\xa5\xde\xf1\xd5\x69\xa8\x75\xd6\xcb\x48\xd6\x69\x58\xb3\x2e\xb8\x01\xad\x93\xe5\x8c\x54\x18\x5d\xa8\xa3\x45\x1e\x0b\x8f\xd8\xd5\xba\x32\x3e\x8c\x3c\x5b\xec\x55\x8d\xbf\x68\x68\x43\x9a\x3b\xa2\x1a\x7f\x64\x34\x56\x64\x58\x35\xd6\xa9\xd5\x5b\x30\xb6\x1a\x5b\xd5\x2a\x2e\x18\x60\x8d\xe5\xb2\x8a\x79\xa3\x0c\x97\x05\xa0\x07\xc6\x89\xcd\x09\x7f\xb8\x72\x32\x41\xc6\x00\x36\x96\xe0\x80\x50\xa5\x81\x7e\x47\xc3\x53\xf2\xbf\xab\x75\xf4\x3b\xba\x6a\x9c\x9d\x99\x0b\x09\xca\x86\xe8\xf7\x0d\x28\x78\x15\x5a\x05\x34\x26\x09\x3f\x6f\xe0\x4c\x2b\xf6\x95\xc3\x04\x0f\x68\xe7\x86\xe8\x68\x10\x47\x6c\x83\x91\xbb\xd2\x51\xef\xe0\x03\xd9\x23\x6a\x57\xb5\x5a\x05\xd5\xae\x6a\x75\xf8\x6f\x03\xfe\xdb\x82\xff\xae\x57\x80\x16\xc8\x7f\x1b\xf0\xdf\x16\xfc\x77\x1d\xfe\x5b\xef\x93\xff\x36\x3b\x72\x33\xfb\xe9\x27\x86\xd4\x4f\x68\x73\xfb\x98\x06\x64\x47\x54\x1c\x42\x44\x20\x48\xc2\x6c\x3c\xad\xf2\x32\xab\x12\x15\x52\x7a\x83\x89\x0f\x55\xfa\xa0\x48\x18\x55\x7c\x95\xd1\xe8\x01\xa2\xcb\x9f\x86\xf1\x11\x4e\x71\xd6\x45\x9e\x2d\x92\x0d\xc2\xf1\x97\x70\xc6\x2c\x7f\xe3\x11\x8a\x8e\x62\x38\x8d\x8d\x83\x14\xf5\x31\x8e\xc0\x3b\x80\xdd\x6f\x05\xd1\x10\x4c\xf8\x86\xe1\x10\x45\x71\xc6\xcc\x30\x6d\x52\xa0\xd9\x5c\x38\x24\x6e\x2e\xfa\xe9\x0b\xbe\x3e\x4c\xc2\x38\x39\xa2\x16\xc0\x1b\x1b\xf2\xbd\x93\x74\xb8\x59\x98\x31\xa7\x76\x07\x74\xf1\x8d\xff\x71\x83\xc3\x0d\x77\xf3\xf2\xad\x83\x3f\x7f\xc1\xd7\xbf\xc6\x09\x18\x31\x7e\xc1\xd7\xd5\x4b\xf2\xdb\x5d\xec\x38\xfc\x03\xb3\x52\x69\x78\xfe\x86\x30\x20\xb4\x8a\x5a\x79\xcb\x48\xf8\x01\x24\x30\x40\x36\x58\x3e\x72\x1c\x47\xf9\xcc\x1b\x7c\x8e\x3a\x85\x5a\x20\xfd\x4f\x07\x63\x4c\x8e\x1f\x88\x88\xd0\x8e\x3e\xa4\x47\xf1\x25\x81\x5d\xe2\xcd\x3c\x27\xbb\xf4\x4f\xb9\x7d\x50\xe1\xba\x87\x85\x37\xaa\x8c\xb3\xf2\xee\xd4\x5c\xaa\xd2\x44\x94\xa0\x43\x45\x0f\xfa\xf3\x35\xc3\x90\x3d\x3b\xa4\x10\xc4\xc8\x4e\x94\xa7\x83\xe4\x2c\x47\xfe\x14\x54\x4e\xa1\xce\x19\x1d\x59\x98\x71\xf6\xc6\xc1\x6a\xfc\x0c\x0b\x29\xfb\x89\x05\x1c\xa2\xe9\x98\x43\xa9\xa2\xfd\x03\x43\xfc\x5f\x02\x71\x2f\xe6\x6c\x16\x8e\xe2\x0c\x11\x92\xf4\x17\xca\xd4\x3d\x40\xdf\x02\x72\x21\x1f\xcf\xfb\x45\x20\x83\xf8\xc4\x61\x9e\x29\x7b\x1b\x7c\x90\x3b\x15\x93\xd1\xce\x94\x5d\x4c\x2d\xb1\xae\x15\x00\x4c\x19\x64\xf6\x7a\x01\xb6\xfb\xe1\x15\xb0\xed\x3c\x6c\x7f\xdf\x00\x26\x7e\xca\x06\x79\x55\x52\xc7\x57\x54\x63\xa8\x3b\x26\x1b\xc9\x09\x07\xd2\x62\xeb\xee\x67\xd4\x21\xfc\xcc\x98\x30\xb4\xb1\x81\x5a\x8b\x26\xed\xbb\x1b\x5a\x77\x9f\x3d\x23\xee\x5b\x33\x16\xad\xb3\x21\x39\x43\xbf\x13\x59\xc2\x5e\x44\x0b\xb9\xb9\x2a\xd3\xe5\xb3\x99\x30\xba\x78\xe7\xe0\x34\xd6\x6b\x3f\xb3\x21\x45\x25\xbf\x11\x4f\x92\xe5\xf0\x57\x1e\xae\xa3\x32\x2c\xc6\x47\x5f\x88\x3a\x2e\xe2\x85\x23\x23\x6f\xe6\x5f\x39\x44\xe3\x65\x27\xf7\xcb\x99\x5a\x4e\x70\x8b\x10\x7f\x8d\x5a\xe0\xc8\x42\x1f\xf2\x68\x5f\x9f\x8b\x53\x0e\x81\x49\x9a\x4b\x76\x24\x07\x98\x2e\x74\xeb\x6b\x88\x90\xa2\x2e\x5c\x7b\x96\xd2\x19\xfa\xdd\xbf\x38\x3d\x7f\xba\xf0\xed\x5e\x81\x26\x02\xcd\x53\x7d\x29\xba\xe7\xc0\x2b\xc9\x56\x94\xe9\xc1\xd1\x20\xb9\x9e\x51\xcb\x58\x55\xce\xdb\xaf\xa0\x78\x34\x4a\x71\x66\xcd\x0c\x5d\x23\xc3\xb8\x27\xea\xc9\xc2\x15\x7b\xaf\xae\xc8\x13\xa2\xfc\x59\x97\x3f\x1b\xf2\x67\xb3\x02\x2c\x46\x3d\x65\x68\xb8\x0e\xf1\xb2\xb8\x12\xae\x79\x19\xcc\x50\x23\x1a\x82\xec\xd9\xca\xc6\x1e\x21\x86\xd0\xf7\xfe\x29\x05\x43\xe4\x17\x73\x48\xb5\x6f\x7a\xd9\x66\x4e\xd9\xa6\xf3\x48\x54\x64\x08\x75\x5a\xad\xe8\x04\xaa\x3f\xd6\xf5\xc7\x86\xfe\xd8\xac\x08\x85\x85\xb5\x79\xaf\xae\xa2\x3d\x72\xf2\xfd\x2e\xc6\xc8\x3d\xe9\xda\x30\x39\x67\xbd\x82\xee\x46\x6e\x2e\xa2\x61\x07\x82\xc2\x92\xb5\x63\x60\xdf\x62\x16\x2b\x14\x2e\x24\xa9\xa8\x4e\x30\x75\xe8\xb8\x6a\xca\x60\x9d\xc1\xeb\xdf\x35\x66\x5b\x73\x69\x80\xd2\xba\x39\x1d\x46\x2d\x6b\x7e\xa0\x56\x43\xaf\xd5\x30\x6b\x39\xb5\x4d\x69\xd3\x9c\x4e\xa3\x56\xd3\xa5\x86\x7a\x67\x9c\x1d\xdc\x47\x7f\x75\x0b\x74\x9d\x18\x8e\x1c\x67\x1c\xb1\xff\xd2\x51\xdd\x40\xf5\x57\xec\xe7\x6b\x3e\x43\xec\x85\x67\xdf\x85\x39\x0e\x47\x19\x50\x7a\xc5\xa3\x28\xcb\x9d\x38\x8e\x7a\x46\x26\x4f\x51\xd7\xd4\x84\xe4\xf5\xbb\xa2\xe8\x2a\xa5\x75\x4b\xee\xfa\x5d\x51\x6a\x95\xd2\x86\x29\x75\xfd\xae\xe8\xaf\xd2\xa6\xf2\xda\xda\x86\x9f\x3f\x77\x6d\x00\x80\x5c\x5d\x47\xae\xee\x41\xae\xb1\x00\xb9\x66\x2e\x72\xb5\x5b\x22\xd7\xd0\x91\x6b\x78\x90\x6b\x2e\x40\xae\x96\x8b\x5c\xfd\x96\xc8\x35\x75\xe4\x9a\x1e\xe4\x6a\x0b\x90\xab\xe7\x22\xd7\x58\x88\x9c\x93\x74\x3f\xce\xc0\x86\x28\xcd\x82\x0c\xdb\x05\x80\x9d\x64\x35\x47\xc7\x80\x65\x64\xa6\x1e\x0d\xbe\x90\xb9\xc8\x1a\xae\x2f\x64\x20\x32\x53\x3b\xee\x54\xa2\x38\xd7\xd3\x02\xde\x07\xcb\xa7\x44\x4f\x1e\xca\xda\x31\x4f\x2d\x8e\xe5\x63\x1e\x5b\xec\x15\xa4\x9d\x5b\xe4\x12\x2a\x17\xa3\x04\xb1\x7e\x38\x76\x75\x3f\x76\xf6\xfa\xb1\xb0\xb3\x96\x90\x8e\x5d\xed\x36\xd8\x35\x14\xec\x1a\x7e\xec\xec\x05\x64\x61\x67\xad\x21\x1d\xbb\xfa\x6d\xb0\x6b\x2a\xd8\x35\xfd\xd8\xd9\x2b\xc8\xc2\xce\x5a\x44\x3a\x76\x8d\xc5\xd8\xd9\xd4\x8a\x79\x60\x6b\xb7\x5c\x42\xb7\x61\xc7\x3a\x32\x85\x1c\x6b\x39\xe9\x9b\xab\x63\x55\x59\xa2\x4f\xd3\x27\xfb\xb0\xa3\x70\x17\x35\xda\x9d\xd5\x66\x83\x69\xa0\xcb\x2e\x55\x30\x97\x58\x84\x80\x94\x32\xc7\x61\xa6\x1a\x5e\x49\x59\xc2\x27\x04\x39\xbc\x47\xc1\x00\x0b\x1d\xb1\x00\xf2\x9f\xf8\x2a\x98\xce\xc4\x49\x59\x7e\xe0\x73\x4a\x61\x65\xf8\x2a\x53\x6e\xb7\xab\x9b\xdb\xc7\x55\x76\x8e\x28\x4d\xb9\x45\xfa\x17\x7c\x5d\x41\x83\xd1\xb9\x90\xe6\x25\x94\xd9\x24\x20\x48\x5c\x65\xc8\x84\xc2\x24\xfc\x92\x6c\xc7\x05\x88\xe9\xb4\x7b\x0e\x25\xf6\x27\x1a\x35\x75\x17\x4f\x66\x38\x29\x6d\x6e\xd3\x6b\x7d\xaa\xb3\x7f\xfa\x84\xd9\xac\xa8\x4d\xbe\x7a\xfa\x14\x22\xe0\x82\x01\x89\x66\x55\xd0\x6d\x37\x2a\xdc\x2e\xa1\xdb\x06\xdb\x11\xc5\x32\xa1\xdb\x6e\x55\xa4\x49\x42\xb7\x0d\x2e\x8c\xd3\x61\xfb\x59\xb7\x53\xbf\x39\xab\xb4\x1b\x77\xb2\x16\xf9\x96\x66\x22\x0f\x66\xcc\xf1\x0d\xcd\x32\xe8\x4a\xf8\x09\x31\x03\x0a\xd2\x3c\x1a\xc4\xd3\x59\x1c\x41\xc8\x75\xf2\x6d\xf5\xe9\x13\x31\xef\x93\xb0\x5f\x65\x45\xbf\x7e\x55\x0d\x00\x84\xd3\xe7\x3d\x1b\x77\x04\x29\x96\x56\x1d\x41\x8a\x95\x6f\xbf\xc6\xc9\x10\xdc\xd2\x45\x01\xf1\x46\x85\x30\x1f\x81\xbd\x18\xd0\xfa\x26\xbf\xe5\x91\x30\x9d\x9f\x35\xcc\x30\x78\x56\xf5\xc8\x42\x55\xde\x7f\xcc\x46\xeb\x00\x05\x47\x83\x2a\x79\x30\xb0\xee\xb4\xc4\x57\xfa\x98\x67\x88\x22\xbe\x6c\x5f\xcc\xde\x6d\xed\xc8\xcb\x26\xfa\xec\xbc\xc1\xea\xa7\xd4\x3c\x8f\x2c\x2b\x7e\x8b\x95\xe1\xe9\x6c\x12\x64\x2e\x06\x25\x82\x4c\xff\x19\xb1\x80\x3c\x5c\x83\x0a\x4e\x05\x82\xd7\x81\xde\x2f\xfc\x03\x57\x79\x80\xc9\x2e\x6a\xa1\x52\xbd\xb1\x8e\xfa\x61\x96\x96\xf3\x00\x86\x17\x0e\x78\x7b\xbf\xdc\x16\xdc\xa7\xed\x0f\xbd\x4f\xbf\xed\x1c\x1c\xed\x7f\xda\x3f\xd8\xda\x46\x9b\x10\xda\x20\x0b\xa2\x0c\x25\x78\x96\xe0\x14\x47\x59\x18\x9d\x73\x45\x0c\x21\xc3\x69\x3c\x94\x7d\x77\xc2\xdc\xda\x2e\x04\x93\xb1\x53\x0b\xa6\x72\x29\x68\x98\x1c\x89\x47\x37\x45\x39\x2e\x09\xe5\x6c\x52\x74\x7b\xe0\xf6\x3d\x4f\xc0\xe0\x41\xe4\xf8\x50\x8b\x68\xc5\x95\xde\x09\xba\x27\x73\x80\x4e\xc6\x98\x8c\x7a\x16\xa3\x39\x73\x13\x20\x2c\x00\x91\xc2\x00\x5a\x03\xb9\x2a\x1f\x06\xa3\xf3\x2e\x90\x2e\xc7\xb5\xac\xee\xa8\x16\xb6\xb0\x5d\xa4\x14\x36\x23\xbf\x30\xf2\x4d\x86\x0b\x7d\x6a\x8f\xa9\xe0\x4e\x48\x8f\x20\xff\x05\x5f\x57\x9d\x65\xb9\x67\xe8\x60\x74\x8e\x4a\x07\xd0\x4a\x30\x29\x43\x9d\x81\x6b\xf0\x0a\x8e\x81\xde\x16\x8f\x23\x4a\x27\xf4\x86\x90\x08\xef\x1d\x21\x94\x41\x5e\x9f\xc8\xb9\x22\x1c\xf8\xbf\xeb\x52\x82\x5d\x00\x69\xd2\x82\xba\xc7\xf3\xab\xe7\x2a\xdd\xa6\xb7\xe9\x30\xc7\x49\x89\x5d\x9e\xc1\x10\x56\xd0\x9f\x28\xbc\xe8\xa2\xf0\x42\xf2\xc6\x1b\xcd\xf4\x40\x9b\x6f\x1d\x52\x57\x0b\x0b\xc5\x24\x07\x53\x03\xa0\x26\x0e\xa1\xf5\xd9\x8d\xb3\xbe\x56\x1d\xb2\x87\x29\xa1\x15\xa4\x27\xcf\x42\x7c\xa4\xa7\xfb\xa5\xa7\x2d\x7c\x5f\xf4\x24\x20\xdd\x8d\x9e\x74\x3e\x7d\x0b\x7a\xda\x8b\xc2\x2c\x0c\x26\xe1\x1f\x38\x45\x01\x8a\xf0\xe5\xe4\x9a\x61\x38\x64\xc3\xb1\x98\x96\xf8\xae\x71\x35\x8a\x93\xe9\x7e\x3c\xc4\x68\x9b\xfa\xaa\x41\x98\x66\xc9\xe9\xe2\x44\xa5\x53\xb0\xae\x06\x37\x3f\x4e\xb5\x62\x93\x71\x93\xe1\x77\x47\xb2\xf7\x46\x56\x25\xfb\x83\x8b\x53\xdc\x92\xe0\xc2\x28\xd4\x2c\x6c\xc4\x34\x29\xe4\xe2\x50\x51\x6f\xce\x66\x84\x16\x60\xb4\x78\xba\xe9\xd4\x71\xcd\x40\x86\x78\x43\xfc\xe4\x9b\x22\xa5\x41\xfb\x54\x9c\x11\xc9\x99\x1a\xd6\xc7\xc9\x94\x4e\x7b\xe0\xd2\xdd\x50\xfa\x96\x24\xb5\x21\xc9\xeb\x95\xab\x24\xb5\xa3\x01\x5b\x19\xe7\x59\x3c\xa4\x84\x4e\x3d\x00\x5c\xfd\x00\xfb\xa2\x52\xe1\x85\x03\x36\x3a\x3a\x1f\x86\x58\x0e\xa9\x68\x09\xb4\x67\x77\x24\x1f\xb6\x04\x6d\xdc\xb4\x19\x4e\x8a\x18\x51\x51\xa3\xa2\x61\x90\x05\xa8\x0f\xb2\x97\x5e\xc2\x23\x8f\x01\x68\x9a\xe9\x82\x7b\x3b\x9b\x80\x0f\x71\x02\x73\x39\x88\xa3\x41\x82\x33\xfc\x82\x0d\xc7\x24\x3e\xd7\x98\xb2\x72\x2f\x75\xb4\xdc\x58\x43\x3c\x0d\xc0\x9c\xba\xb7\x30\x9e\x82\x87\x0a\x4b\xc1\xc3\x25\x36\xbd\xaf\x29\x73\x85\x21\x40\x99\xb2\x93\xf0\x06\xde\x06\x6b\x40\x01\x5f\x60\xe7\x52\xf8\x93\x80\x45\x83\x66\xb1\x60\x04\x61\x74\x7e\x0f\xdc\x44\x76\x7e\x83\x93\x07\x83\x5f\x5a\x21\x6d\xae\xe8\x64\x52\xa4\xde\x25\xc7\xdc\x4b\x61\xac\x64\xd7\x88\xf2\x4a\x87\xce\xc3\x3d\x70\x34\x74\xcd\x7e\x00\x5f\xd4\xea\x2e\x9a\xa2\xed\xa1\xe0\x22\x08\x27\x41\x7f\x82\xa9\x19\x62\xea\xdf\x16\x3f\xf1\xce\x14\xa6\xaa\x9d\x30\x62\x1b\x5f\xee\x3e\xc5\xe0\xea\xfb\xcc\x87\x38\x63\xde\xd1\x34\x68\x1a\x85\x24\x77\x0d\x14\xa6\x08\x8f\x46\x78\x90\x85\x17\x78\x72\x8d\x02\x34\xc4\x69\x96\xcc\xe1\xb9\x82\x12\x1c\x0c\x5f\xc4\xd1\x00\x17\xda\x67\x8a\x52\x2f\xa0\xf1\x50\x34\x4c\x81\x3f\x34\x25\xf3\x91\x2c\x15\x27\x62\x51\x65\x59\xea\x17\x15\x17\x93\x3f\x2f\x5a\x9c\xfe\x77\xe4\x5c\xcc\xa1\x90\x5e\x22\x1c\xe5\x02\x40\xb9\xab\x45\x2b\xea\xb8\x28\x59\x82\x21\x43\x3c\x24\x82\x2a\x5b\x70\x78\xc8\xe2\x65\x72\x4e\xbd\xa3\x4c\x88\x73\xf1\xd9\xb5\x17\x2a\x9b\xeb\x8d\xf5\xd5\x66\x43\xfd\x44\x55\x22\xae\x2f\x86\x1c\xd4\x45\x75\xed\xab\x2e\xff\x76\x51\xa3\xc8\xd9\x29\x75\xaa\xb2\x83\xc5\x8a\x6c\xe4\x5d\x9b\xfc\xd4\xc2\x46\xfa\x64\x8c\x15\xa1\x80\x25\xda\x0a\xd0\x18\xb4\xc6\x44\xc8\x2c\xb0\x14\xb9\x08\xbb\x19\x71\x7c\x20\xc0\x00\x5f\xd6\x44\x68\x62\xeb\xda\xd1\xa1\x6f\x70\x58\x62\xd6\xde\xb6\xca\xd3\xd0\x91\x5b\xb2\xad\x77\x95\x69\xf5\xba\x5e\xbf\x29\xf2\x27\x3e\xa5\x78\x82\x07\x19\x6d\xf8\x38\x4b\x82\x0c\x9f\x5f\x97\x7c\xe6\xda\x8a\xf6\x19\xc4\xc5\x0d\xb4\x42\x59\xe9\x8a\xd7\x3c\x8c\xcd\xc6\x61\x90\xa6\x84\x4d\xbc\x09\x52\x3c\xd4\x3c\xe6\xd4\xbf\x7c\xe3\x30\x06\xea\x18\x27\x70\xe0\x22\xbb\x9a\x1f\x52\xfe\x22\x37\x73\xfb\xb1\xfb\x8c\x1c\x1b\x75\x1f\x52\x8c\x9c\x54\xc6\x66\xdf\xb0\xe4\xd9\x8d\xca\x20\x60\xee\x79\x10\x17\x37\x14\xc5\x0a\xf2\x5f\xe0\x98\x63\x50\xf1\x58\x7a\x32\xb2\xef\x5a\xfd\x37\xee\x73\xee\x84\xb6\x7e\x53\x54\x41\xb9\x37\x46\x26\xe6\x8e\x09\x35\xd9\xb6\xca\x25\x4b\x65\xa6\xe1\x75\x5f\xbd\xe9\x3a\xec\x34\x4b\x70\x30\xbd\x95\x2a\x1b\x64\x28\xa6\x7c\x56\x6d\xf0\x9b\x8d\x17\xfd\x90\x1a\x6c\xeb\x27\x1a\x2a\x9d\x40\x18\x6b\x45\x33\x5d\x47\xa5\x66\x43\x57\x4c\x2b\x0a\xdf\x63\xc0\xcf\x50\xfb\x9a\x2f\x73\x3c\x42\x76\x1c\x7b\xad\x6b\x87\xe5\x22\xe2\x2c\x48\xe0\xb8\xe5\x12\x10\xed\xed\x0d\x8e\x37\xd2\xba\x8a\x0b\x8d\x3f\xfc\xb0\x32\x9a\xcc\xd3\xf1\x4a\xb1\x6d\x8e\x42\xf1\x6d\x74\x62\x98\xbb\xa8\x9e\x37\xaf\x70\xae\x85\xac\xa6\x33\xf5\xb6\x54\x55\x9e\x7f\x9a\xd2\xb3\x6f\xaf\xca\x7e\xfc\x79\xb3\x98\x42\x34\x8f\x1d\xa8\x67\x51\x89\xd2\x86\x72\xbb\xc9\x0e\xda\x96\x73\x30\x7b\xaf\x2a\xbd\xf3\x14\xf4\xaa\x8a\x72\xca\x93\x73\x49\xf9\x7a\xe9\xdd\x74\x53\xef\x91\x53\x21\x68\x66\x96\x91\x0a\x7e\xa0\xea\x6f\xb0\x1f\xf2\x99\xe2\xdb\x1d\xe8\x61\x7b\x6f\x7a\x96\x2a\x9a\x73\x94\xf0\x82\x7a\xed\xdc\x46\xf3\x2c\x61\xe4\xea\x0a\x45\x5d\xae\x68\x52\xea\xdd\x4a\xe3\x2c\xa6\x53\x1e\x90\xfe\x67\x4e\xa7\xd4\x04\x2f\x39\x9d\x4e\xc5\x6f\xc1\xe9\x14\x75\xef\x30\x9d\x79\x0a\xdf\x62\x57\x07\xdf\x74\x3a\xef\x3c\x5d\x39\x4b\x60\xc1\x7c\x99\x7a\xd3\x9c\x49\xa2\x9b\x89\xd0\xf3\x0e\x5c\x62\x1d\xb3\xba\xbe\x40\x1b\x28\xbc\x50\x67\x2b\x6f\x8b\x60\x3b\x26\x8d\x2b\xdd\x1b\x07\x61\x04\x29\x4f\x7c\x77\xad\x6f\xc0\x6e\xe0\x13\xef\x3c\xda\xf0\x07\x1f\x30\x55\x6c\xda\x0e\x42\xea\x5a\xc4\xa0\x0c\x8d\x6c\xcc\xd8\x25\xc4\x9d\xe8\xab\x3c\x8e\xf2\xa6\xc7\xb7\x03\xe3\x24\xa4\x34\xa1\xcd\x1d\xe9\xd5\x9b\x9e\x63\xef\xb1\xc1\xd3\x26\x0e\x45\xf8\xcf\x8c\xab\x31\x28\x95\x06\x19\x33\xea\xae\x9a\x75\x2c\x18\x06\xcd\x52\xe9\x48\x68\x45\x98\xb0\x14\x73\x19\x09\xe9\x9c\x10\x39\x6f\x48\x98\x5d\x16\x01\xc2\x7e\x5e\x8e\x31\x8b\xbc\x4f\xf1\x83\x40\x9e\x69\x01\xe4\xec\x85\xe1\x2e\x48\xfe\x60\x2a\x99\xa8\x43\xbd\x01\x20\x3d\x1e\x74\x41\xb8\x36\x98\xb2\xac\x3a\x19\x48\xaa\x00\x2d\x33\x79\x1d\x8a\xd7\x16\xda\xe9\x00\x8b\xcc\x1b\x12\x75\x21\x79\x0c\x67\xa5\x10\x2b\x34\x39\xe2\x95\xc7\x9c\xf5\xb7\x83\x23\x38\x2f\x33\xa2\xb3\xcb\x5c\xc5\x09\xf4\x4b\x2a\xba\x2b\x48\xeb\x57\x45\x36\xeb\x12\xfa\x19\x1e\xaa\xaf\x4b\xc9\x1c\x5d\x27\x66\x47\x78\x8a\x41\x0a\x87\xdd\x95\x92\x00\xbb\x8a\x82\xd3\x3e\x38\xb4\xc3\x6b\xbb\x3a\x97\x60\xf1\x05\x0f\x3b\x4f\x99\x29\xcd\x27\xcf\xf1\x16\xa6\x80\xde\x0e\xa8\x9e\x3b\x0b\xd7\xed\x10\x17\x58\xb7\x62\x9f\x7a\x5c\xb7\x8f\xeb\x16\xdd\x7e\xdd\xde\x65\x75\x80\x85\xf0\x38\x4c\x97\x5e\x1b\x4e\x4c\x18\x45\x03\x17\xf9\xed\xe0\xc8\xcb\x01\x54\x0f\x32\x8b\x03\xdc\x95\xed\x38\x31\x3b\x91\x43\xd3\xc7\x83\x78\xca\x96\x0e\x61\x0b\x61\x3c\x4f\x8b\x33\x0f\x31\x58\x45\xd9\x83\x20\x25\xde\x8d\x92\x17\xf7\xa5\x3c\xa0\x40\x44\xe2\xd2\x92\xcb\xc3\x7f\x1c\xc7\x29\x46\xd3\xf0\x8a\xc8\x42\x8e\xfe\x81\x27\xa8\x2d\xa4\x21\x95\x10\x99\x14\xe6\x23\xbb\xf8\x02\xa4\x53\x72\xd2\x49\xe7\xfd\x14\xff\xf7\x1c\x47\x99\x53\xc5\x80\x74\xd1\x4e\xc9\xea\xa1\x8f\xa2\x57\x35\xa8\xa2\x64\xcc\xca\x62\x55\x3f\xd9\xd9\x5c\x58\xb9\x62\x24\xc9\xd5\xe6\x8c\x94\x44\xfe\x60\x02\xa5\xf5\x78\x78\x86\x7e\xdf\xa0\xf5\x4e\xc3\xdc\xd0\x25\xf2\x37\x37\x81\x7e\xd3\x63\xe5\xb5\x80\x26\x8a\x68\x7b\x18\x0c\x87\x64\x02\x17\x28\x40\x66\x90\xe5\xaa\x57\xa5\xff\xba\xd5\x1f\x87\xef\x7a\xc7\xe8\x7f\xb5\x57\xd7\xd0\x8c\x01\x4d\x99\x2e\xcf\x05\xf3\xf0\xcb\x20\x5d\x03\x39\x79\x16\x0c\xab\xfc\x29\x47\x36\x3e\x0c\xf8\xf5\xf3\x3c\xe5\xa1\xf3\x45\x20\x14\x66\xae\x0c\x71\x93\x05\x1e\x4b\xd9\x5f\x01\x64\xf5\xf6\x99\xa0\xe5\xac\xe4\xd6\xe3\xb1\x10\x50\xca\x7d\x24\x00\x4a\x45\x30\x4b\x32\x28\x10\xce\xf2\x81\x8f\xcd\xe2\xf0\x25\xc6\x95\xfc\x92\xd7\x6b\x15\x23\x6e\x96\x76\xc1\x1c\x0c\xcd\xcb\xb5\x5b\x33\x10\x51\x8d\xc6\x3a\xd9\x50\xc6\xcb\x17\x33\x64\x1e\x65\x82\x76\xc0\xaf\xc8\x86\x1a\x31\x82\xb5\x80\xd2\x17\x2f\x68\xca\x69\x11\x61\xe5\x5f\x46\x01\x57\xb3\xf4\x5e\x88\xb7\x6b\x87\x5e\xa0\x99\xde\xe0\x2b\xa1\x17\x88\x80\xa2\x61\x21\x7d\x5d\xac\xf7\xcc\xc1\xc5\x7a\x0f\x6e\x2d\xda\xdb\x85\x98\xe5\x22\x95\xe6\x87\x2f\x90\xec\x47\x6f\x13\x85\xe8\xb9\xcf\x2d\x5f\x85\x4e\xc3\xdc\x2b\x6f\x72\xa4\x57\x03\x3b\xb4\x21\x6d\xdf\xf9\xe1\x5f\x05\x5d\xd1\x51\x72\x99\x21\x6c\x0e\x87\xee\x41\x80\xb9\x1e\xc4\xd1\x20\xc8\x38\xcc\xc2\x1a\x98\x8f\xd1\x4c\x30\x14\x58\xb2\xe3\x60\x48\x03\x19\xb1\x85\xfa\x6d\xb8\xcc\x3c\x32\xf9\xcc\x37\xe1\x08\xd0\x6c\x81\x2b\x77\x28\x67\xb2\x04\x17\x1f\x78\x8b\x33\x2d\x71\xb1\xb2\x88\x21\x06\x2c\x9a\x04\x69\x06\xcf\x8b\xd7\xb4\x14\xaf\x4f\x4b\xfa\x72\x7e\x81\xea\x65\xea\x62\x76\xc6\x9c\xc1\x5c\x9e\xc4\x54\x70\xf0\x53\x8c\x04\xb7\x61\xae\x41\x65\x33\xa5\xdb\xe6\x92\x7a\xfe\xbf\xe2\x22\xc8\xe5\xa2\xe0\xbe\x59\x70\xdd\x2a\xe4\xdd\x03\xdd\x9f\xd1\xff\x7e\x3c\xc4\x37\x54\x3d\x78\x22\x4e\x6b\xf4\x52\x04\x4e\x12\x4a\x77\x7a\x6f\x7a\x3e\x28\x6c\xae\x6e\x04\x7d\x11\x58\xa6\xb0\x61\x43\x04\x92\xf7\x10\x38\xf8\x11\xb0\x01\x50\x0c\x27\x0d\x02\x27\x98\x02\x66\x15\xe3\x54\x47\xdb\xb6\x9a\xb8\xd1\xbc\x11\x96\x30\x0c\xa4\x13\xad\x7f\xec\x29\xd6\x87\xf9\x36\x80\x39\x01\xce\x74\xfb\x50\x87\x1f\x27\xc8\xcd\x64\x04\x34\xb5\x28\xd2\x15\xbb\xe4\xfb\x14\x6c\x3f\x3d\xf8\xcb\x89\xb5\x0f\x03\x96\x2d\x29\x97\xb4\x75\xe3\x12\xef\x89\x81\x40\x85\x2d\x11\x34\x1a\x70\x2a\x37\xee\x66\xdc\xd2\xfe\xea\x4f\xf9\xcd\xeb\xd6\x2b\x65\xf4\xd3\xea\xd2\x18\x08\x55\x8b\xe7\x2c\xf3\x0e\xe3\x19\x0a\x32\x34\xc1\x84\x0b\xc6\x11\x5f\x01\x2c\xcb\x07\xb5\x04\x85\xfd\x1a\x18\xae\xcd\xb7\x90\x38\xdf\x4c\xc3\x88\x1a\x89\xb2\x43\xbc\x15\x2e\x51\x7f\x64\x95\xe8\xf4\x29\xf8\x53\x42\x9a\x82\xfd\x31\x3d\xf2\x86\x17\xe8\xc7\x1f\x9d\xfa\x78\x33\x50\xc7\xe1\xad\x74\x19\x12\x13\x5d\x99\xe2\x3d\x9f\x9b\xcd\x16\xbd\x92\xf6\x8b\xa4\x52\x24\x11\x86\xd2\xec\x95\x83\xa0\x79\x73\xf7\x4b\xc8\xab\xab\xe4\x20\x43\xd3\x7d\xf9\x44\x2e\x90\xd7\x99\xe9\x17\x48\xe0\xf0\x7b\xa1\x0e\x82\x5f\xc5\x53\x1b\x41\xdf\x29\xf9\x56\x97\xf1\x0f\xb7\xac\x1e\x16\x6f\x67\x7b\x20\xf9\x2d\x98\x01\x2a\x1f\xb9\xda\x5b\x64\xf9\x77\x47\x4b\x05\x30\xbd\x63\xb2\x87\xdb\x0c\x05\x0d\xe2\xc9\x04\x53\xfa\x8f\x47\x5c\x34\x00\x51\x13\x43\x2e\xbd\x3c\xd1\x43\x11\x45\x15\x27\x6f\xb2\x8d\x26\xc1\xa5\xf2\xca\xe9\x97\xe8\x76\xfd\xa0\x0e\xe8\x42\x48\x29\x52\x5b\x5e\x3c\x42\x86\x07\xc6\x05\x69\x7d\xb2\x3e\x2d\x73\x5c\x1f\xa0\x34\x98\x50\xec\xe1\x07\x00\x03\x95\x64\x40\xc3\x8f\xe2\x24\xbc\xa0\xb2\x0a\xe7\x18\x4e\x80\xfc\x2a\x55\xca\xf9\x8a\xe5\xa0\x1d\x6b\xb5\x98\x5c\x73\x9b\x9e\xe5\xcb\x37\x83\x31\x9e\xde\x0e\xae\x5b\xe0\x64\x2a\x73\xb0\x98\x1e\x29\xf0\x9c\x20\x68\x4e\xc6\x1b\x99\xb3\x91\x9e\x62\xa8\x88\xc5\xdf\x9a\x62\xd8\x20\x8e\x2e\x70\x92\x69\x32\x2c\xcd\x76\xc7\x8d\x29\xc1\xe2\x93\x5a\xff\xf9\xdd\x56\x0f\x69\x15\xdd\x79\x55\xbc\x2c\x68\x0f\xb3\xd8\xc5\x4a\x47\x6d\xf1\xb1\x4e\x78\x37\xa9\xf8\x18\x76\xa2\x41\x24\x92\x58\xcd\xe2\x34\x0d\xfb\x13\xec\x5f\xb1\x8e\xa6\x96\x73\x6e\x92\x03\x65\xdb\x83\xd2\x6f\xfc\x04\xfe\xa7\x05\x05\x09\xf5\x39\x59\xc1\x5d\xe5\xb7\x74\x78\x72\x56\xfa\x82\xaf\xbb\xba\x5f\x94\xb3\x98\xe1\x29\xe5\x2e\x44\x96\x71\x17\xfe\xbb\xa0\xa0\x58\x95\x5d\xdb\x9d\xcb\x5d\x83\x89\xf0\xa6\x65\x82\xbb\xb0\x90\xeb\xf5\xa3\xf3\xbb\xde\xf1\x9a\xbb\x82\xc2\xc2\x5b\xee\x12\x62\xe1\x28\x40\xe9\xbb\xea\xc1\x0c\x47\xc7\xc7\xef\xad\x6a\xc5\x9d\xc9\xd4\xe9\x77\x0b\x5e\xd3\xf0\x6a\x2f\xd2\xcb\x15\x36\x3d\xa2\xab\x38\x5d\x6e\x19\x23\xef\xba\xb1\x59\x89\xe1\x1b\xe8\xe1\x26\xe4\x50\xe7\x07\xce\x0d\x6c\xb9\x57\x06\xec\x0a\xf0\x3b\x1c\x85\xe6\x1a\xcf\x81\x03\x49\xc0\x52\x9a\x01\x0c\xb2\xc7\x61\xe9\x45\x29\x31\x8e\x62\xfa\xc6\x60\x80\x2c\x67\x3f\xce\xe3\x1e\x45\x97\x34\x45\x5e\x5c\xd3\xb1\xb5\xfd\x1c\xad\xac\xb8\x7d\x2b\x9c\xe5\xab\x59\x4c\xf3\x0d\xf9\x5c\x39\x16\xd4\xf2\x90\xaa\x97\x30\x79\x45\x95\x38\xc5\xd8\xf8\xac\xaa\x64\x09\xf4\xf5\x2b\x25\x57\x59\xa7\xca\x27\xf1\x9a\x1f\x7b\x2d\x1d\x8d\x53\x4e\xa2\x54\xb6\xe8\x5e\x83\xb6\x03\x57\x1b\xe2\xa7\xfb\x76\x83\xf5\xdc\x45\x9c\x2e\xd0\xac\xb8\x48\x65\x0c\xbb\x97\x3e\x88\xf9\xd7\x1d\x62\xd5\x05\xfe\x25\x17\xf1\x66\x5e\x0c\xe2\xe9\x2c\xc8\x60\x7b\x29\xba\x0c\xd5\x6d\xc1\xd8\xc4\x14\xf1\xa7\xe8\x9e\xe8\x5a\x7e\xb7\x41\xee\xbe\x0c\x07\x63\xda\xf6\x31\x27\x6f\x0f\x21\x2b\xd4\xe5\xe3\x8d\x1a\x7d\x8b\xe2\x85\xb9\xef\x02\xb5\x8c\x1a\x69\x49\x5b\x82\xf2\x8b\x2b\x50\x23\x11\x77\x8d\x0a\xe4\x9d\xeb\x18\x0b\xfd\xb5\x0f\xb1\xa4\xb8\x57\xd5\x72\xa9\x44\xab\xb1\xb4\xf7\xa7\xb5\xab\x76\xb3\x53\xef\x0c\xd6\x20\xb1\x41\xa7\xdd\x69\xb5\x47\xed\xd1\x59\x99\xab\xe2\x01\x34\x7f\x90\xfd\xf0\x9c\x23\x0b\xa0\xe0\x1d\x0b\xcf\xe1\x4b\xd4\x95\x8c\x8c\x86\xb5\x59\x7e\xcf\xcb\x5b\x63\xaa\xbf\xd2\xb2\xc2\x23\x5f\x27\x92\x4e\x6f\xbd\x64\xf4\x98\x0d\x7c\x41\xdf\x62\x0d\xdf\x6f\x00\x07\x5b\x18\x35\x96\xde\x2c\x48\x52\x5c\xd2\x16\x6a\xce\xc5\x64\x92\x6a\x8a\x1f\x59\xcd\xe9\x95\x40\x8a\x23\x1a\xc3\x6b\xc1\xa2\xa3\x84\x61\x21\x93\xa7\x5e\xcd\x83\xc8\x2f\xe3\x94\xc3\x30\x4b\x0a\x61\x81\x3b\xc1\x69\x46\x6d\x1b\x82\x89\x63\x81\x1a\x30\x4f\x6b\x67\x68\x63\x03\xc9\xb5\x87\x7e\xfc\xd1\x6c\xf7\xb4\xce\xca\xf0\x35\xe9\x53\x41\x6d\x5f\xd1\x0b\x0c\xbb\x65\xa4\x73\x18\x6b\xf1\x1b\x2d\x32\x53\x9e\x46\x05\xb5\xca\x39\xd6\x75\xf1\x05\x3b\xa2\xc3\x55\x90\x84\x61\x97\xb7\xe0\xcf\xa0\x81\x9a\x79\x6b\x6d\x15\xd7\x6e\x75\xea\x9d\x62\x8c\xc2\x79\x34\xf2\x1c\x83\x2a\xca\xe9\x44\x17\xcd\x73\xef\x8a\xf8\x22\xbc\x4c\x82\xd9\x0c\xe4\xc8\x20\x63\xcd\xab\x2a\x13\x14\x90\x9d\x3e\x55\xbc\xd2\x72\x57\xaf\xe6\xea\x63\xb9\xb2\x49\x87\x1f\xd7\xa7\xa2\x0e\x24\xb7\xbe\xec\x11\x42\x0f\x97\xf1\xf3\xa4\x7a\xae\x23\x50\x7b\xcb\x3a\x4b\x1d\x42\xa3\x21\xa5\x1a\x71\xc0\x90\x17\x3b\x8e\x83\x53\x5e\x88\x28\xd3\x7b\x11\x10\xea\x5a\xa2\x9a\x32\xb1\xb9\x41\xa5\xd8\xb5\x03\x99\x37\xe6\x4d\x77\x17\x0f\x55\xa9\x7c\x72\x1c\x75\x72\xbc\xcf\x59\xd3\xd4\x06\x85\xfd\x96\x7e\xe7\x7f\x93\x18\x2e\xee\x2d\x6c\xf3\xaf\xdd\xc0\xc8\xb2\x74\x6b\x54\xec\x65\x25\xfc\x2b\x6d\x6d\x84\xe6\x6a\xe9\x39\x85\x3d\x5c\x83\x32\x48\x8d\xa9\x4e\xf8\xa6\x8d\x57\xc4\x6a\xf3\x48\x03\x39\xca\x0e\x87\x73\xac\xdf\x8b\xf5\x76\x21\x74\x96\x8a\x9e\xb3\xed\xb2\x5f\x57\xa2\x1b\xc4\xd2\xf9\xc4\x15\x00\xcd\xe9\xb3\x6a\x89\x25\xd2\x33\x43\x04\x48\x60\x9d\xbd\x8d\x64\xd2\x83\xfe\x49\x98\x70\x05\x6c\x41\x61\xf6\x46\x84\xe3\x0a\xc7\x5c\xdf\x7e\x54\x7c\x3b\xcd\xdb\xb4\xb5\xfd\xd5\x2e\xc8\x55\x8b\x8e\x4f\x84\xac\x44\xdf\xaa\xe1\x85\xa3\x88\xa2\x23\x64\xf4\x62\x97\xa1\x5a\x41\x09\x08\x2e\x44\xed\x62\x42\x1f\x28\x4b\xb2\x57\x8e\xc2\x8a\x2e\xd0\xb4\xb0\x76\x94\x56\xf4\x82\x84\xf4\x46\x8e\xe3\xda\x4d\xe1\x63\x0b\xbb\x87\x4e\xc5\xc4\x09\xc5\x97\x7a\x2d\x83\x1e\x6c\x7b\x52\x09\x40\xec\x50\xc6\x45\x93\xf2\x08\xa9\xbd\xff\x8e\xfb\x94\x11\xa0\x45\x44\x3a\xfe\x06\x7b\x93\x8c\xaa\xbc\x98\x4d\x73\xef\x79\x07\x9b\xe6\x64\xc7\xc2\x28\x28\x1e\xf5\xb7\x66\xd9\xf7\x8d\xa2\xb9\x2f\xdd\xe3\x96\xe2\x8d\x5d\xe0\x89\x30\xf0\x0d\x76\x15\xa6\x71\x50\x54\x0b\xea\x62\x32\x00\xab\x3b\x05\xbb\xfd\x86\xf3\xab\x8a\xbc\xe4\x26\xae\xe6\x18\xa7\xb0\x37\x0c\x75\xf2\xb4\x4d\x4c\x8b\xba\x48\x87\x45\xee\x4d\x0a\x93\xd1\x14\x3e\xce\x6d\x42\x34\xb1\xb4\x36\xc6\xc9\xd6\xcc\xb1\xd2\xef\x5f\x40\xc7\x14\xa4\xe9\x7c\x8a\x87\xfa\x7d\x62\x30\x49\x70\x30\xbc\x56\xf6\x3b\xed\x40\x36\x8f\x68\xda\xca\x02\x11\xcd\x96\x63\x7b\x6e\xfe\xb5\xd4\xa1\x89\x30\x2e\x30\x51\x4f\x52\xbc\x34\xaf\xf7\xeb\x8b\xe6\xd1\xb2\xb0\xfe\x42\x89\xdb\x22\x79\xaa\x42\x3a\xe0\x54\x80\x04\xf1\xbb\x79\xc0\x27\x4b\xa7\xa4\xae\x1e\x56\xd9\x95\xca\x9b\xc5\xae\x51\x17\xe1\x82\x10\x36\xdc\x26\x84\xb2\x27\x7b\xa9\x9a\x17\x1b\x28\x57\x3b\xca\xa0\xe5\x28\x45\x2d\xcd\x84\xf3\x86\xe4\x9d\xdb\x44\x62\xd1\x95\xc9\x97\xe1\x08\xee\x4b\xe8\xbf\xf9\x97\x25\x8b\xac\x30\xec\x0b\x93\x77\x14\x3a\x69\xa5\xd8\x3d\xc9\x16\x01\x0f\x77\xfa\xa4\x31\xb2\x96\xf7\x7e\xe1\x0a\x83\x19\x8b\x17\x54\x5c\x1d\xcb\x6b\x30\xcb\x0b\xf6\x00\x72\x0a\x69\x06\x00\xe7\x7b\x85\xc8\x40\xe5\x98\xda\x56\x84\x11\xb3\xe4\x65\x76\x00\xcc\x64\xe6\x1c\x47\x60\xcc\x9b\x0f\x4d\x44\x29\xf7\x00\xa3\xa1\xb3\xf3\x61\xd9\x3a\x03\x50\x61\x29\x42\xd2\x26\xea\xb4\xc0\xe4\x18\x3e\x70\xfb\xd9\xbd\x11\x8a\xa7\x21\x91\x11\x2a\x28\xa0\x9f\x2e\xc3\xc9\x04\xf5\xb1\x68\x70\x88\x92\x20\x1a\xc6\xd3\xc9\xf5\x3d\x1d\xee\xa9\xd5\x04\x1b\xa6\x0a\xda\xfb\xa5\x02\x53\x4a\x1a\xff\x06\x5c\x88\x4e\xf2\xd0\x66\x41\x0a\x35\x56\xf1\x15\x1e\xcc\x33\x5c\x5a\xe1\xd1\xa8\x56\x2a\x2c\x71\x47\x85\x99\x6f\x79\xc4\xa2\x7b\x82\x5e\x41\x2b\x64\x38\xc8\xff\xaf\xf8\xcf\xcc\x14\x8c\xca\xdd\x38\x35\x57\x38\x89\x56\x18\x75\x51\xc5\xa6\xdb\xa8\x9f\x4e\x33\x9b\x65\x8f\xa2\xfa\x07\xef\x55\x92\xa5\x44\xa6\x70\x4a\x9d\xd6\xaa\x95\xd6\xdc\xe1\x56\x47\x97\xb6\xb2\xae\x6d\x69\x85\xc6\x9b\xa5\x89\x07\xa4\x02\x57\xc4\xb8\x93\x69\x90\xd9\x42\xba\x29\x57\x59\x22\x6f\x65\x3c\x00\x7f\x67\xc0\x5a\x42\x9b\x59\x3e\x06\x60\x37\x6d\xa9\xc9\x45\x32\x68\xa6\x20\xe7\xc9\x64\xf9\x98\xa3\x9f\x6c\x7d\xb6\x96\x1a\x5a\xa6\x70\x76\x3b\x4b\x1d\x31\x51\x6a\xc9\xc3\xb8\x3c\x52\x0b\x29\xfa\x76\x5a\x6d\x97\x66\x40\x53\x71\x0f\x19\x5f\xe6\x2c\xcf\x60\xc9\x15\x01\xcb\x23\x7e\xdd\x5e\x1f\xee\x88\x12\x27\x14\xe2\xee\x6f\x2e\x0d\xd7\x03\xea\xc7\xdf\x6d\xed\xdc\x20\xb2\x7d\x72\x0b\x4a\xd7\x2e\x2c\xa5\x3c\xce\x6c\xf3\xb7\xb8\xa5\xb4\xe2\x8e\x0e\xfb\x9d\x1f\xbe\x0c\x47\x5d\x65\x7b\x56\x28\x64\x49\xf5\x38\x73\xa9\x5a\x66\x5f\xfe\x3e\xf4\xe5\xb9\xd2\xc1\x77\xa0\x8e\xf8\x9b\xa8\xcd\x1d\x8b\xaf\x90\x26\x79\x85\x0f\xb5\x2f\xac\xec\xc3\x37\x5c\x41\x7f\x3e\xb0\x06\x5b\x6e\x47\xdf\x48\xe1\x60\xec\xae\x71\xe6\x53\xee\xba\x64\x17\x02\x9e\x88\x2d\x5c\x5c\x51\xb0\xa7\xc3\x2b\x64\x0c\xf6\x4c\xb7\x3d\x9f\x77\x27\x15\x63\x69\xdf\xac\x2e\x55\x61\x8b\xd5\x30\xa8\x3a\x43\x12\x78\x15\xf3\x9a\xbe\xc4\x7f\x9d\xa1\x06\x80\xb0\xe6\x47\x6f\x5f\xd1\xe3\x5b\x68\xec\x87\x57\x34\x19\x08\x54\x70\x0e\xa9\x72\xb6\xa6\x86\x99\x1a\x74\x9f\xde\xc4\x79\xe2\xbb\x83\x3e\xf8\x2f\xe0\xc7\xf7\xac\x20\xfe\xde\x19\xf3\xf7\xa8\x27\x76\x31\xc3\x65\x15\xc5\x77\x62\x8c\xf7\x8e\xa2\xad\x28\xbe\x2f\xc6\x5d\x50\x4f\xfc\xcd\x79\xf7\x37\x57\x16\x7f\xfb\xad\xa2\xa2\xd9\xf6\x78\x4e\x68\xf7\xb7\x77\x14\xd2\x87\xfb\xef\x2f\x5c\x5b\x87\x3a\xbe\x05\x77\x8f\x3c\x05\xb9\x54\xe5\x89\x4c\x97\x6a\x4a\x4b\x96\xbf\xf2\xe6\xac\xd2\x6e\x7e\xaf\x49\x29\xef\x3d\x07\xe5\xb2\xb9\x27\xb5\x9c\x93\x16\x62\x76\xfa\x49\x23\xed\x24\xaf\xe8\x49\x3c\x09\xfa\x51\x09\x5c\xfc\xd4\x93\x4f\xee\x07\xd9\xb8\x82\x1c\x29\x28\xe5\xf1\xfa\x7d\x3c\x08\x26\x68\x16\x4f\xae\x47\xe1\x04\xc5\x23\x44\x37\x2d\x76\x8a\x77\x1c\x79\x59\x6c\xfb\x0d\xbd\xa0\xd1\xb0\xc6\x98\xc4\xeb\x1d\xf2\xfe\xe6\x95\x1d\x3b\x48\xb1\xb5\xec\x7f\xb6\x98\x1a\xd8\x08\xce\xfb\x64\x06\x4d\x22\xde\xa9\xce\x92\x38\x8b\xc9\x27\xb4\x41\x4e\x1f\x66\x01\x56\x0f\x6d\xa0\x08\x5f\x12\x04\xf2\x21\x44\xf3\xc9\xc4\xb3\x50\x04\x06\x72\x99\x28\xf1\x8e\x5c\x91\x3c\xf9\x9c\xe4\x2b\xb9\xbd\x8a\xed\xf7\x61\x3f\x09\x92\xeb\x45\x3a\x72\x25\x3f\xa8\x17\x14\x64\x0b\x65\x5a\x4f\x22\x5c\xf0\x2e\x07\x13\x14\x46\x63\x9c\x84\x5a\x00\x57\x2d\xa2\x83\x99\x67\xd4\x8e\x30\x6a\x4f\x67\x81\xb0\x7f\x3c\xc6\x30\xb8\xc7\x09\x3f\x83\x71\x90\x71\x84\x58\x28\x0f\x2a\x06\x59\xa7\x4a\x84\xf2\xe2\x00\x72\xb9\x2b\xbe\xc0\x49\x12\x0e\x71\x8a\x0e\xa9\x42\x24\xc4\x29\x65\xe0\xb3\x6b\x14\x46\x2c\x9b\xb1\x44\xa0\x40\x0b\x66\xae\x86\x93\x65\x01\x58\x32\x97\xa7\xdc\x32\x51\x03\xc9\x44\xed\x5f\x9f\x50\x12\xd6\xa4\x9b\x1c\x93\x44\xd5\x5f\x2c\xc4\x93\x61\x17\xad\x40\xa6\xac\x15\xd3\x70\xc4\xdd\x26\xf9\x9b\xe2\x6c\x1c\x0f\x73\x7d\xe4\x95\xd2\x66\x8c\x7c\x97\xe3\x19\x42\x76\x38\x43\x8a\xbe\x66\x90\xcd\xe7\xd5\x1b\xc4\x70\x16\x5c\x46\xf6\x17\x85\x91\x10\x61\x41\xa6\xd5\xf3\x99\x13\x6f\xce\xcf\xa7\x38\x72\x98\x0e\x93\x1d\x25\x1f\x0b\x24\x99\x0f\x3b\x77\xc9\xf2\xce\xf4\x0f\x4e\x04\x98\x99\x14\x77\xfd\x0a\x85\x63\x69\xe2\xc6\xe9\x07\xde\xe4\x38\x48\x0f\x2e\x23\x46\xf6\xd7\xa5\x15\x52\x73\xa5\x2c\x7c\x9e\xc8\x23\x6c\x82\xbc\x3c\x79\xb1\xb0\x1f\xb4\x56\xee\x74\x3b\x6a\xfd\x3f\xe9\x7c\x46\x44\xad\x28\xcc\xaa\x01\x11\x4e\xd9\xd6\x17\x24\xe7\x73\x32\xba\xce\xf1\x40\x8e\x0c\x0a\x39\xe3\x24\x3d\x6e\x93\x95\x14\x49\x8e\x1e\x52\xa5\x30\x9f\x74\xba\x4a\x6d\x08\x6a\x07\xb5\xfd\xc0\xb3\xed\x20\xae\x18\x1f\xe1\x04\x47\x03\xd2\x00\x8c\xf3\xcc\x5c\xaf\xd6\x30\x30\xb9\xd8\x05\xd0\xbb\xcf\x20\x57\x6a\x0c\x17\x53\xdd\x86\x95\x92\xaa\x4c\x93\xaa\xbc\xe7\x11\x1d\x07\x98\x40\xba\x6a\xed\x10\xa8\x9b\x7c\x3e\x64\x06\x9b\x52\x59\x5c\xc3\x11\x51\x1a\x42\xca\x01\x90\x52\xf9\xef\xcc\x2b\x79\xc4\x72\xb4\xc1\xd8\x26\xbf\xb3\x58\xc8\x8b\x68\xb9\x7c\x8e\x67\x37\x02\x4b\x4e\xc6\xc9\xb6\x57\x2e\x8f\xa0\xae\xac\x11\xfe\x4e\x5f\x27\x5e\xaa\xe1\xc5\x6f\x43\x36\x79\xee\xea\x9e\xb9\x42\x07\x8c\x99\xb1\x24\x01\x40\x52\x60\x42\x3f\x1c\xa2\x34\x9e\x62\x9a\x7a\x0a\x5d\x8e\x71\x84\xae\xe3\x79\x22\xcc\xec\x03\x22\xce\x52\xe0\xf7\x1c\x3b\xf7\xae\xbb\xa0\xe9\xe8\x9c\xb7\x97\x21\xca\x00\xaa\x55\x7b\x64\xc4\xd0\xdf\x72\xbb\x5b\x88\x46\xa1\x39\xed\xc5\x33\x22\xec\xcc\xa4\xdc\xc3\xe4\x9d\x3b\x88\x53\x0a\x30\xd0\x30\x69\x32\xd5\x14\x34\x91\xf7\x3c\xa5\x6c\x75\xd2\xfd\xb3\xa8\xfc\x72\xcb\x71\x87\x46\xb4\x4b\x6c\xd1\x3f\xe7\x1a\x17\x11\x0f\xf9\x65\xdb\x87\x60\x0a\x46\x13\x0b\xea\x21\xb6\x55\xcb\x62\xe6\x66\xad\x02\x2c\xe7\x6e\xb1\x64\x3a\x4f\xd5\xe2\x67\x68\x43\x69\x5f\xff\xb4\x44\xea\x22\xcf\x26\xbb\x8d\x2e\xe3\x68\x25\xa3\xf2\x33\x77\x77\x54\x82\x17\x4e\xe2\x78\x86\x82\x7e\x7c\xe1\xd8\x06\xf3\xbb\xbc\xc2\xa1\xad\xf8\x3b\x0c\x5c\x54\xb4\xaa\xf6\x53\xbc\x2d\x90\x57\xab\xd0\xe2\x11\x87\x13\xe8\x29\xd8\xbf\x2c\xb3\x6e\x5c\x1b\xdf\x60\x12\x47\xf8\x01\x38\x1e\xc0\x45\x1b\x72\x0f\x81\x17\x05\x76\x32\x52\x6c\xe1\x46\xa6\xe6\x22\xd1\x85\x23\xce\x4f\x9d\xf6\x64\xee\x33\xb2\xf3\x76\x3f\x42\x01\x78\xde\x1a\xb1\x08\x73\x23\x0b\x59\x71\xde\xf3\x41\xb8\xc2\xd3\x08\xe3\x07\x3d\x1c\x62\x1a\x9e\x47\xe1\x28\x1c\x04\x51\xc6\x02\x4a\x86\xb4\xf7\x00\x92\xb6\xe3\x3a\x26\xff\xaa\x78\x10\xd3\xb3\xb2\xfa\xe6\x1e\xc2\xc6\xd8\xcd\x9b\x64\xe1\x09\x83\xaf\x9a\x5e\x2d\x18\x6b\xe4\x34\x0b\x13\x23\x65\xdc\x60\x2c\x1c\x34\x7c\x6f\xa9\x5e\x54\xff\x6c\x6d\x63\xb7\x6c\x61\x3c\xda\xff\xe2\x00\x4e\x6b\x57\xb5\x5a\xad\x5e\x6b\xd4\x9a\x15\x54\xbb\xaa\xb5\x6a\xed\x5a\xa7\xb6\x76\xf6\x60\x80\x2b\xa8\x53\x38\xf4\x0a\x0b\x5f\xc7\x67\xc4\x5a\xb1\x97\xcc\x21\x18\x96\x2b\x7f\xa0\xff\x7e\xfd\x0a\x31\x7b\x0d\x51\x63\x84\x4a\x62\x7a\x7f\xd8\x70\x28\x0a\xd5\x3f\x80\xaa\x18\x0d\xf1\x9f\x85\x8d\x49\x4d\x00\x94\x3c\x26\x38\x3a\xcf\xc6\xd4\xf4\xc8\xcb\x45\x8a\xc7\x8c\x91\x0b\x65\xb9\x48\x31\xdb\xd1\x20\x1e\x12\x7a\xc7\xf4\x87\x49\xee\xf0\x3a\x3f\xf6\xa7\x20\x00\x1c\x0d\xaa\xbb\xf8\xca\xdf\xe6\xa2\x00\x32\x85\x56\xfb\xd2\xc1\x5d\x24\xb1\x16\x88\xec\xe2\x88\x6b\xb0\x28\xac\x8b\xa3\x8a\x36\x24\x1f\xb3\xd1\xfa\x52\xd1\x5c\xd8\x54\x78\x63\xb9\xf0\xa9\xfa\xfa\x15\xed\xe2\xab\xdc\xf0\x2d\x0b\x08\x68\x10\x64\x38\x62\x7b\xbe\x4e\x41\x1e\xe6\xef\x27\x24\xe5\x1e\x56\x0e\xf8\x09\xe3\x86\x0a\x65\x42\x9a\xdf\x65\xef\x75\x8b\xe2\x52\x84\x36\x04\x76\x75\x1e\x3f\x43\xbc\x69\xf8\x53\x9a\x41\x49\x93\x29\xd1\xc0\xce\xcb\x85\x23\x21\x03\xfb\xab\xc5\xb0\x1c\xbe\x8a\xd9\x38\x10\xa1\x0e\x24\x89\xf9\x4b\x87\xe9\xb1\xe4\x31\x1a\xcf\xf1\x00\x3f\xd6\x59\x12\x85\x2f\xeb\x58\x9d\xea\x4d\x82\xe9\x0c\xe1\x2b\x88\x24\xd9\x0f\xcd\xce\xd1\x7b\x55\x52\xc6\xbe\x6d\xa0\xf7\xa9\x03\x57\x90\x14\x0d\xf1\x7f\x79\x02\xa5\x43\x7d\x22\x92\x46\x18\xb6\x5a\x14\x64\x28\x40\x59\x38\x75\x48\xdc\xae\x90\xec\x6a\x77\xfd\x49\x21\xd4\xc1\x21\x45\xd1\x06\x41\x8f\xcd\xc2\x69\xc8\xa3\x62\x93\x7f\x4a\x8d\x16\x7a\x81\x4a\x21\xc5\xf8\x27\xb4\x5e\x2e\x8b\x68\xd9\x5e\x29\x9e\xc2\xd1\x7b\xfc\x1c\x85\x22\xdc\xf6\xd7\x0d\xd9\xf4\xeb\xd7\xbc\x0d\x47\x79\xd1\x68\x01\xc1\xdf\xbb\x2d\xa9\x63\x4a\x17\xd7\x9d\xc6\xd4\x1f\xe5\xbe\x68\xf7\x37\x90\x3d\xd8\x45\x32\x06\xdb\x54\x28\x36\xdb\xe7\x1b\x3a\x9a\xae\x1c\x2b\x41\x18\x05\x7d\xf3\xe4\xa1\x1c\x00\x8a\xb2\x53\x1a\x83\x83\x08\x81\x9a\x60\x18\x66\x77\x15\x05\xe5\xe2\x14\xab\xcb\xc3\xa4\xc8\xe7\xa2\xa1\x7b\x1d\xac\xc9\x96\xa3\x5c\x71\x91\xbc\x4c\xc6\xcd\x30\x1c\xa2\xda\xa9\x80\xc1\xe3\xcc\x6f\xc0\xd2\xa1\x7f\x40\xfa\xcd\x06\x21\xfd\x54\xe3\x0b\x0e\x82\xd7\x44\xa9\x0d\xb4\x1f\x64\xe3\xea\x00\x87\x13\x59\x73\x15\x2d\x11\x91\xc8\x7d\xfe\x2d\xb4\xf3\x78\xcc\x91\xac\xe3\xef\x6d\xed\x3e\xd9\x71\x57\xa5\x05\xeb\xbc\xab\xd3\xc2\xa2\x73\xae\x0a\x16\x4e\x6a\x14\x57\x35\xfa\xb9\x7d\x72\xae\xda\x34\xc2\xcc\xef\x6b\x5e\x93\x3a\x52\x6f\xf9\x29\x50\xc4\x86\x51\x38\x99\xf0\xb0\xb3\xcc\x4d\x02\xce\x5b\x8b\x85\x12\x7e\x98\x8b\x5c\x87\x5e\x15\x94\xd7\xc5\xa7\xd0\x2c\x33\x48\x85\x08\xe5\xbe\x8c\xcf\x0a\x1c\xc1\x98\x2b\x48\xdd\x7f\xd2\xa2\x25\x54\x32\x89\xdc\x47\x2c\x95\x3d\xd8\x07\x2a\xf2\x35\xd1\x6f\xc8\xa7\x9f\x2e\xfd\x51\xe6\x3f\x5d\xa2\x0d\xf2\x5f\x4f\x02\xb5\xe9\xa7\x3f\xc8\x36\x73\xd5\x0c\x86\xb8\xb3\xde\x37\xc3\xaf\x8b\x62\x41\xfa\x05\xa9\x9c\x23\xe7\x9e\xa0\xc0\xdd\x1d\x6d\xb5\x54\xbb\x7a\x59\xeb\xbc\x44\x3f\x91\x2e\xfc\x01\x7b\xfa\xce\xce\xce\x4e\x19\x3d\xa7\x2f\x7e\xfe\x19\xd5\xae\xea\x35\xd8\xee\x09\x02\x9e\xed\x9e\x76\xb1\x54\xbb\x6a\x75\xda\x35\x0a\xec\xd2\x04\x76\x59\x14\x18\x0c\x2f\x4e\xe7\xe0\xe9\x53\x02\x34\x5e\xbf\xa6\x35\xd1\x73\x04\x23\x9d\x5b\x9f\xd5\x5d\xdd\x80\x3a\xec\x2f\xbf\xec\xf3\x0d\x54\xab\xb6\xbd\x65\x60\x4c\x59\xd1\x9f\xa8\xbd\x0d\xa7\xb6\x32\xfa\x19\x55\xdb\xe8\x3f\x50\x1d\x75\xd1\x8b\x7a\x11\x11\xc5\xe2\x1c\xba\xb8\x51\x41\xc9\x20\x18\x8c\x31\xcb\xae\xb3\x58\xe0\x20\x35\x3f\x11\x7a\x4c\x4a\x25\x5a\x95\x1c\x95\x34\x24\xc9\x6e\xa2\x0c\x86\xfb\x8a\x89\x56\xdd\x40\x9f\x92\x12\x2d\x0f\x04\xb9\xd6\x5f\x73\xf4\xe9\x52\xe6\xf0\x29\x89\xf2\x12\x3e\xfa\x8a\x6a\x05\xc3\x9a\x47\xf8\x52\x71\x76\x82\x5b\x47\xa6\x00\x89\x78\xfa\x9e\x27\xc6\x48\xba\x9d\x4f\xd9\xd1\x7e\x91\x21\x0d\x8e\x06\x60\x48\x43\xff\x75\x1b\xd2\xec\xe2\x2b\x5b\x13\xe0\x02\x47\x0a\x6e\x50\xa0\x55\xfa\xbb\x58\xfc\x4d\x53\x7d\x31\xc6\x57\x85\x55\x18\x05\x4e\x9e\x4b\x46\xd5\x2c\xd4\xfa\x7d\x31\xf2\x31\xbe\xb2\x43\x68\xb2\xf1\x53\x8e\xf6\x8b\x13\x09\x39\x03\x67\xde\xf6\x98\x7a\x59\xf8\xe4\x99\x2e\x7b\x8c\xa4\xb3\x6e\x03\x1a\xe3\xab\xde\x38\x48\x0a\xe7\xd9\x4a\x17\x1e\xe8\x20\x47\x5a\x48\x0f\x72\x97\x77\x3c\xc4\x71\xec\xd8\x1a\x07\xb0\x04\x48\xab\x2c\xd5\x3e\xf5\x4e\xd9\xc5\xef\x5c\x55\x49\x3b\xb5\x51\x7e\x5d\x0f\x83\x10\xe0\x3e\xc7\x61\x54\x5a\x59\xb9\x45\xc4\x4d\x85\xc2\xe9\x7a\x5b\x46\xd3\xc3\x57\x0a\x25\xdc\xe2\x0b\xc6\x23\x3c\xfd\xf5\x52\x13\x5f\x6c\xd4\x66\x5b\xac\xc7\xe2\x91\x32\x69\x95\xe5\x12\xa5\xd0\x3a\xef\xf9\xd1\x85\x3e\xb2\xa3\xcc\x32\xab\xe6\x72\x99\xd4\x74\x6a\xa3\x6c\x0b\x6d\xe4\xe4\xc7\xa4\xab\xa5\x09\x9a\x09\xe8\xf4\x5e\x94\xb1\xce\x56\xd3\x79\x3f\xcd\x92\x52\x58\x41\x8d\x72\x05\x92\xf0\x49\x95\x05\x59\x51\xeb\x65\x97\x03\xee\xd2\x7b\x9e\x36\x4c\xab\xa8\x51\xd4\x7d\xf6\x7d\x90\x85\x51\xbd\xd8\xa6\xc5\xca\xf2\x7d\x4b\x3c\xde\x6e\xeb\x62\xd5\xff\xba\xdd\xab\x28\x02\xf7\xb5\xa6\x26\xd0\x9e\x7b\x0f\xa3\xb8\xfc\x8f\xda\xc6\xe8\x70\x7c\xc7\x3b\x99\x82\x20\xdd\x91\xe8\xd4\x55\x47\x49\x3c\x25\x6f\x7b\xf1\x10\xc3\x26\x55\x74\x43\x52\x01\xde\x61\x4f\xd2\xe8\xf6\xf6\xdb\x92\x20\xc7\xa5\x16\xc3\x77\xbd\x39\xb1\x55\x44\xf7\x27\x75\xb9\x15\xdf\xa2\x44\xad\xe5\x76\x29\x51\x4d\x6c\x54\xe2\xcd\x43\xef\x55\x46\xd3\x8b\x72\x39\x87\x8a\x16\x5d\xf6\xb6\x3a\x60\x04\xbd\x99\x95\x42\xbe\x26\xcc\xad\xca\xad\x5b\x5c\x7a\xab\x32\x10\x2e\xba\x53\x7d\x3c\xd9\x79\xb1\x5e\x6c\xa3\xfa\x98\x8d\xd6\xc5\x36\xc5\x1e\x6e\xb7\x49\xd1\x46\xff\xba\x3d\xaa\x60\xfb\xf7\xb5\xb2\xe6\xd9\x68\xdd\xbd\x41\x91\x51\x7c\xc8\xed\x29\x4b\xae\x73\x0c\x8c\x86\x98\x1c\xd1\x3f\x1e\xed\xf5\xb8\xa7\x53\x09\xa7\x83\x60\x86\x4b\x39\x1b\xa7\xcd\x96\xd1\x20\xc8\x06\x63\x54\xb2\xd3\x47\x03\x0a\xe3\x24\xbe\x04\xba\x85\x8c\x2b\xa5\x95\xfd\x60\x32\x8a\x93\x29\x1e\xb2\x69\x18\x06\x59\x60\xa7\xa0\x5b\x9e\x81\xab\x93\x7a\x7b\xfe\xcd\xe6\x6a\x19\x32\xf9\xae\x99\x37\x50\x18\x65\xdd\x92\x0c\x8b\x33\x6e\x56\xc7\x67\x0c\xa0\x6d\x0d\xf3\x88\x51\x0f\xb5\x10\xd0\xe8\x8a\xc3\x29\x17\x0e\x40\x23\x52\xf0\x42\x2e\x4c\x3c\x64\xd9\xcc\x14\x2f\x74\x6f\x26\x5e\xc5\x4e\xf6\x5a\x49\x89\x36\x9d\xa7\x19\xea\x63\x14\x92\x11\x9d\xe2\x28\xa3\x79\xd6\x02\xb8\x5e\x4f\x70\x26\x3c\x16\x0a\xe5\xf6\x35\xf2\x74\xea\xca\x7d\x9a\xe3\x90\xba\x56\xc9\x04\xf1\x5f\xf0\x2c\x43\xf3\x68\xc6\x93\x06\xea\xd9\x41\x15\x9b\x96\x9a\x83\xfb\xbe\x61\xe3\x00\x99\x06\x37\xc5\x28\x08\x2f\x31\xdf\xe7\x82\x66\x70\x90\xdd\x95\x59\xf3\x18\x23\xbd\xc2\x92\x68\xb3\x24\xa6\x59\x8c\xc2\x2c\xe5\x5e\x31\x88\x50\xf0\x5d\xef\x98\xfa\x4e\xe4\x69\x42\x5c\xff\x25\x53\xa1\xac\xbb\xcc\xbc\x0f\x81\x95\xb2\xcb\x66\x00\x32\x70\x32\x4f\x45\x63\x67\x35\x99\x12\x2d\x1f\x6d\x05\x59\xc0\x85\xf5\x5a\x51\x49\x73\x73\x38\x4c\xa1\x0d\x9e\x17\xdc\x33\xd2\x8c\x16\x8a\x6f\x8a\x22\xc8\x82\x95\x79\x9c\x19\xbb\x20\xba\xe6\x99\x13\x00\xe5\x97\xd4\xa7\x24\x50\x2c\x28\xa9\x3d\x31\x70\xbc\x87\x99\xcc\x4f\x14\x9d\xd2\x8a\xcd\xef\x0b\xd5\x5b\xbc\x37\xb2\x92\x45\x92\x99\xdb\xee\xf5\x32\x1d\x9d\x1a\x50\x54\x19\x20\x16\x4c\x54\x07\xa5\xfa\x38\x03\x19\x2d\x88\x13\xc9\x68\x4d\x61\xca\x80\xe1\xe2\x48\x69\x9b\xd0\x35\x1f\xf9\x72\x53\x22\x17\x30\x8b\x68\x9f\x6f\xe8\x49\xd2\x8b\x52\x30\xcf\x75\x9a\xa2\xe0\x22\x08\x27\x10\xb1\x8b\xf2\x05\x60\x76\x7e\xaa\x39\x51\x9c\x55\xc2\xe8\x22\xfe\x82\x53\x33\xc9\x70\x89\x25\x07\xae\xa0\xcb\x71\x38\x18\x3b\x59\x75\xff\x3a\x87\x55\xdb\xad\xf2\x85\xd2\x8f\xe3\x09\x0e\xa2\x1b\x34\x8c\x77\x26\xf3\x74\x8c\x7e\x1d\xe3\x8c\xc6\x33\xe1\xb9\x68\xc1\x5d\x6b\x16\x24\xc0\x28\xd8\x2b\xc9\xb5\x05\xbb\xbe\x45\x38\x10\xc1\xe9\x61\xc4\xef\xbe\xcd\x0b\x80\x5b\x94\x90\x7c\x6b\x86\xa7\xca\xf5\xc5\xe5\x58\x12\x8c\x3b\x53\xb0\x1e\x6b\x95\x16\xd5\x16\x1f\x1d\xf0\x25\x75\x26\x6c\x89\x48\xe2\x76\x68\x4b\xc8\x6b\x6e\x9c\x06\x23\xeb\x53\xab\x90\x8f\x8a\xa1\x99\x8f\xee\x79\x71\x29\x2b\x6c\x18\x29\x99\xf3\x0a\x73\xe8\xb2\xb6\x3b\xa2\x5f\x2f\x9e\x47\x19\xa7\x2f\x07\x33\x21\x40\x23\x9a\x48\xf8\x08\xe2\x16\x6f\xe8\xf8\xaf\x1a\x4d\xbe\xb2\x79\x91\x6f\xc8\x19\x06\x47\xf1\x3c\x1a\xa2\xf9\x8c\x3a\x14\x0e\x26\xf3\x21\x36\xe8\xde\xae\x66\x60\x24\x8d\x5c\xd4\x0f\xc5\x63\xdb\x0a\x2c\x86\xf1\x65\xa4\xe2\x11\x47\x93\x6b\x34\x9a\x8b\x45\xe9\x88\xa4\xbf\xba\x8a\x26\x38\xa5\x4e\x95\x6e\x59\x0b\xf8\x46\x82\xa7\x41\x18\xe9\xc2\x55\xb1\x7e\x4d\x83\xab\x92\xd6\x2f\xb8\x38\x45\x2f\x5c\x99\xd9\x2b\x8b\xaf\x54\xc5\x9c\x53\xcd\x83\x6f\xca\x81\x92\x39\x1e\x5a\xeb\x3f\x21\x85\x00\x7d\xf4\x04\xb4\xe1\x25\x27\xf2\x55\xef\x63\x18\x95\xd4\x26\x7f\x42\xad\x8a\x46\x67\x2e\xf3\x49\x9e\xc1\xdb\x45\x24\x84\xee\x14\x80\xf9\x6e\x5b\x94\xcf\x53\x35\x0b\xfb\xfd\x5a\x1d\x01\xf1\xf6\xb9\xb2\x9e\xbc\x46\x13\x04\x33\x9c\x90\xd3\xa4\xd8\x18\x5e\xc8\x03\x02\x38\x43\xba\x2b\x32\xee\xa2\xef\x41\x82\xab\xb8\x72\xd5\xfb\xe6\x18\x69\x29\xb0\x24\xc3\x87\x29\xb7\x8b\x6a\xdc\x57\x65\x61\x66\x32\x2c\x75\x44\x1d\x68\x68\x9c\x0c\xbd\xd8\x50\x67\x7a\x31\x55\xf2\xd8\xa2\x79\xd8\xfa\x15\x4e\x3a\xfe\x15\xb5\xe9\xbb\x1a\xbb\x15\xce\x42\x99\xeb\xe4\x75\x47\x2b\x37\xcf\x6e\xf8\x17\x99\xbc\x7d\xb2\x36\x44\x89\x89\x73\xc6\x72\x2d\xde\x74\x1e\x26\x4e\x9a\x9e\x4c\xf4\xfc\x0c\x3e\x0e\x52\xc8\x90\xeb\x3d\x71\x2f\x4c\x45\x2e\xd9\xb5\xea\x03\x45\x27\x9d\x41\xa7\x61\xd7\x70\x8a\xe2\x48\x39\x0a\xd7\x3b\xa8\xd4\xae\x37\xc0\x92\xb5\xec\x38\x16\xef\xd2\xca\xfc\x18\x2c\x1e\xdd\xe7\xe1\x7b\x89\xfa\x9a\x97\x81\x2c\x37\x60\x6a\x9e\xab\x19\x1d\x84\x25\x72\x92\xdf\x36\xba\x1d\x69\x08\xd1\x10\xc9\x8b\x82\xdc\x15\xb6\x21\x11\x73\xa0\x85\x6e\x3b\xde\xdd\x6c\xb4\x3b\x6e\x27\xb1\xbc\x54\xd7\xb7\x8e\xb0\xc6\x63\xab\x15\x0f\xb3\x76\x8c\x45\x78\x0f\xbf\x86\xc0\x56\x43\x2c\xb0\xc4\x96\x9a\x14\xbe\x70\xee\x5f\x65\xc2\xe8\xe5\x3e\x54\x24\x80\xb0\xaa\xe2\xd1\x4b\x78\x56\x12\x80\xd6\x98\x97\x2d\x35\x98\x7b\x33\x1b\x0e\xc7\xc6\xcc\x37\xe4\xa3\xe5\xc6\xfa\xe3\x6c\x08\x2c\x43\x1d\x6c\x9a\x96\xbf\x78\xc6\x3e\x6f\x04\x61\x0a\xdc\x8c\x23\x5c\xd8\x85\x88\xb2\x22\xe6\x3f\xb4\x70\x79\x2f\x31\xe7\x73\xc0\xab\xb4\xc2\x90\x72\xe9\x52\xf4\x92\x8b\x55\x27\xb4\xa0\x4a\x28\xda\x18\x78\xd6\xa3\x47\x23\xc1\x14\x36\x3a\x04\x07\x79\xb0\xf1\x25\x42\x3a\xc1\xd7\x05\x4a\x39\xc7\xda\xe2\xef\xbd\xf9\x4e\xec\xb0\x24\x37\xa9\xc0\xc5\xcb\x20\xd1\x87\x18\x50\x0e\x32\x9a\x2f\x9e\xd5\x94\x31\x43\x51\x98\x22\x3c\x1a\xe1\x41\x16\x5e\xe0\xc9\x35\x0a\xd0\x10\xa7\x59\x32\x87\xe7\x0a\xc8\xe9\x2f\xe2\x68\x80\x0b\x45\x19\x2d\x48\xa1\x5a\xa2\x07\x40\x49\x06\xe4\x86\x12\xcb\x6b\x2e\xc8\x20\xdc\xd3\xce\x80\x36\x38\x39\x8a\x64\x42\x1e\xb5\x84\xa7\x74\x1e\xa1\xe7\x54\x5b\x4c\xf5\xbc\xe8\x52\x74\xbf\xe3\x18\x5f\xfb\x40\x94\x0f\x06\x2d\x5a\x2b\x8b\x04\xf8\x25\x38\xab\x32\x42\x9c\xc9\xee\x28\xf3\xe0\x5c\x3c\xa4\xbc\x6f\xf1\x28\xc9\xef\xda\xf5\xc6\x6a\xb3\x51\x4c\xcc\x4f\x99\xc6\x47\x8b\x7f\x1f\xb0\x49\x5b\x11\x81\x93\xc2\x28\xc3\xc9\x48\xb1\x16\x46\xde\x55\xc1\xf9\x2b\xeb\x3a\xa7\x5a\xba\xdd\xb2\xf8\x88\x01\x1a\xe3\xc9\x0c\x27\x44\xfc\x29\xb0\x08\x76\x18\x6e\xcc\x37\xd8\x44\xf9\x1b\xdc\xe3\x51\x99\xc9\x74\xaa\xa0\x5d\xad\x7e\xa2\xbd\xda\x85\x2e\x95\x5c\xc2\x96\x5f\x3f\xa7\x56\xd5\x8c\x07\x01\xb4\xef\x7e\xcf\x5a\x17\xee\x00\xb8\x48\x3f\x2f\xb2\x95\x08\x87\x45\x3d\x8b\x98\xcc\x70\xa9\x53\xf8\xf2\xc7\x46\x27\x3d\x11\x96\xbc\xbb\xbf\xd9\xbb\x7f\x7a\x22\x22\x34\x0f\x4a\x41\x5a\x60\x74\xf5\xb7\xa0\xa9\xdd\x69\x30\x28\x44\x57\xd3\x60\x70\x17\xda\x12\xd5\xef\x44\x5f\x5f\xb0\x5b\x85\xa4\xd0\x57\xef\x13\xa0\x45\xe6\x81\x12\x19\x6d\x84\xd6\x5d\x8e\xd8\x72\x8f\xbf\x42\x93\xb4\xc0\x87\x81\x60\x03\x4e\x0c\xec\x87\xf4\x62\xe0\x99\x5a\x20\xa4\xef\x7e\x90\x8d\x69\x58\xdf\x27\xfc\x3d\x1b\xe6\x57\x32\xd2\xef\xcd\x59\xa5\xdd\xfa\x5e\xc3\xfb\x32\x64\x4a\x3c\x1c\x71\xf9\xde\xe3\xfd\x72\xc8\xcb\xc6\xfd\x15\x18\xaa\xf1\x7f\x7d\x41\x7f\xc5\x77\x08\xfe\xeb\x0a\xa0\x6b\x5f\x51\xf0\xa8\xb1\x72\xca\x14\x02\x50\xa2\xc1\x2a\xef\x73\xc2\xd3\x68\xb5\x15\x17\x18\x5f\x18\xd9\x4e\xab\x98\x89\x16\x2b\xcb\x8d\xb4\xc4\xe3\xed\xcc\xb4\x58\xf5\xbf\xce\x4e\xab\x28\x02\xf7\xc5\x29\xfb\xd0\x9e\xdb\x54\x8b\xe2\xf2\x0f\xb0\x25\xb6\xca\x4f\x83\x99\x10\x0e\xa7\xc1\x6c\xf9\xd8\x0b\x0e\x17\x71\x1b\x84\xcf\x2a\x93\x8e\xf9\x6d\x0d\x96\xd1\xf3\x0d\xd4\xf4\xdb\x2c\x5f\x67\xb8\xee\x30\x5a\xa6\x7f\x3e\xd3\x65\xfa\xe7\x35\x60\xe6\x80\x1b\x12\x70\x29\x44\xcf\x51\xbd\xec\xb0\x89\xe6\x5f\x8a\x58\x46\x73\xc0\x4d\x03\x70\xc3\x0b\xb8\xe1\x04\xec\x86\x9c\x25\xe1\x6c\x02\x57\x2f\x25\x3a\x2c\xaf\x5f\x83\xdf\xc4\x57\xfa\xdc\x20\xcf\xeb\xe4\x11\x50\x70\x41\x11\x53\xf1\x99\x4e\x45\xe9\x33\x7a\x4d\x5a\xff\xf1\x47\x04\xd8\x7c\x46\x3f\xa1\x5a\x75\xad\xad\xcc\x50\xf9\x15\xfa\x9c\x13\xee\x42\x99\x7b\x6a\x0b\x3e\x0d\x66\x60\x33\xbb\x99\x95\x4a\x1c\x61\xe8\x74\x07\xfd\x84\x4a\x4d\xf4\x02\x7d\x2e\xb3\x9e\x36\x47\x4e\x6f\x27\x2b\x3e\x83\xad\xb8\x18\x0e\x79\xba\x6f\x9b\x1a\xd9\x07\x82\x12\xda\x40\x0a\x3a\x1d\xcb\x99\x04\x62\xeb\xc9\xe2\x6e\xe3\xe0\x71\x38\xc1\xa8\xa4\xf6\x93\x85\x0b\xf0\xc5\x1a\x71\x0e\x8b\xda\xcc\xf2\x7d\x66\x9c\x55\x85\x7a\x07\x3b\x79\x8d\x27\xdf\xde\xce\x52\xb0\xda\xa5\x18\xfd\x77\x6d\x6a\xc9\x76\x08\x6a\xd7\xa3\x6e\x25\xc5\xcd\x2d\x45\xad\x25\x37\x07\x51\x4f\x18\xca\x8b\x37\xc2\x50\x7e\x31\xdf\xb7\x4a\x24\xf8\x02\x27\x29\xde\x57\x0a\xca\x57\xae\xb8\x66\x3f\xc8\xcf\x5e\xea\xce\x05\xea\xda\x02\xf8\x9f\xc9\x7f\x08\xfb\x21\x2b\x94\x75\x30\x97\xd3\xe8\x0d\x9f\xf2\x85\xcd\x6c\xf3\x3f\x97\xcf\xd0\x06\xfa\x5c\x2c\x56\xa7\x83\xa5\xec\x9d\x47\x71\x82\xbf\x19\x57\x51\x40\xee\x45\x43\xf0\x73\x96\xd3\x1d\x92\x37\x07\xa3\x45\x3c\x43\x69\x87\xc2\xf8\x61\x63\x03\xbd\xa8\x2f\xe0\x49\x2a\x85\xa9\xb5\x6f\xc5\x88\x9d\x22\x41\x22\xd2\x5e\xa6\xf8\x7d\x1c\xcf\xe4\x92\xa8\x98\x38\x54\x94\x19\xd5\x44\x0e\xe3\xc6\x33\x98\x75\xd1\xca\xe6\x9b\xde\xd6\xf6\xce\xdb\xdd\xbd\xff\x7a\xf7\x7e\xff\xc3\xc1\xe1\xff\x3e\x3a\x3e\xf9\xf8\xcb\xaf\xbf\xfd\xfb\xff\x04\xfd\xc1\x10\x8f\xce\xc7\xe1\xe7\x2f\x93\x69\x14\xcf\xfe\x3b\x49\xb3\xf9\xc5\xe5\xd5\xf5\x1f\xb5\x7a\xa3\xd9\x6a\x77\xd6\xd6\x5f\x3e\x5f\xdd\x60\x11\x6e\xc5\xd1\x4e\x2c\xda\xa5\x51\x95\x43\xec\xf1\x4a\x91\x96\x1b\x9a\x85\xa9\x4b\x14\x32\xda\x71\xb9\xa9\x90\x99\x0e\x3d\xfb\x0d\x73\xec\x4a\x89\x90\xa4\x2c\x0f\x49\x4d\xaa\x03\x0b\x7a\x81\xea\xe5\x33\xf0\x5e\x91\x02\x53\xc3\x26\x2e\x0e\xb4\x51\x04\x68\xf9\x8c\x6f\xf0\xaa\x18\xe6\x80\x4a\x05\xa2\x48\x8b\xdc\xf3\x95\x08\x33\x80\xfe\x57\xda\xa2\xea\x5b\x13\xe5\x07\xef\x41\x6c\x88\x9f\x3f\xd7\x3e\x08\xb2\x15\x3f\x18\x45\x5a\xb1\x25\x9d\x61\x11\x6e\x64\xee\x1e\xf3\x90\xaf\xec\x11\xaf\xbc\x99\x7d\xda\x8f\x47\xff\xc7\xa3\xbf\x38\xfa\x7f\x3c\xd9\x79\x51\xef\xa0\x37\xdb\x85\x1d\xb4\xea\x9d\x37\xdb\xaa\x8f\x56\xbd\xa3\x3f\xc1\xd7\xdb\x3b\x6d\x51\x64\xfe\x5a\xc7\xad\x82\x38\xdc\xa3\xf3\x56\xbd\xe3\xf5\xde\xaa\x77\xfe\x01\x1a\x81\xe2\x87\x75\x18\x8c\xbb\x9c\xd5\xdd\xfe\xfe\x60\x19\x15\x0f\xf1\x61\x1c\x46\x99\xcf\xc9\xb8\xde\xf1\x38\x19\x3b\x0f\xd3\x12\x53\xbf\x97\xb1\x68\xb2\xa8\xab\xb1\x02\xf4\x0e\x27\x28\x93\x88\xef\xe4\xac\x06\xb4\xb9\xec\xda\xf8\xae\x8f\x51\x74\x55\x09\x97\x35\xbe\xf8\x96\xf2\x59\x83\x4a\xcb\xf9\x1a\xf3\x5a\x42\xbe\xe5\x2f\x1e\xda\xd3\x58\x6f\xb8\x98\xa3\x71\x1d\x64\x1f\x81\xa1\xee\x66\x4c\x44\x20\xb9\x58\x1a\x64\xb1\x18\x41\xd8\xfc\x14\xee\x93\x72\x8c\xd1\xf9\xa9\x78\x28\x0c\x46\x96\xef\x0b\xec\x61\xca\x3e\xf5\xfe\xce\xfb\xd4\xfb\xef\x60\x9f\x2a\x82\xc3\x7d\xef\x53\xce\xe5\xf4\x7e\xfb\x71\x9b\x12\x7f\xf7\xb6\x4d\xa5\x97\xc1\x6c\x3b\x1a\x86\x41\x54\x5a\x76\xc7\x72\x1d\xc9\xbf\xff\x2d\xeb\xfd\xc3\x6c\x59\x45\x96\xc9\xf7\xbf\x65\xbd\xdf\x36\x36\xad\xc7\x1d\xcb\xda\xb1\x94\x15\xb3\xd4\xe6\xf5\x4d\x77\x2f\x31\x2f\x0a\xb6\x04\x90\xd6\x47\x1e\x0d\x1f\xbe\xb0\xbb\x13\xba\xb8\x6b\x35\xf2\xff\x70\xb1\x42\x3f\x92\xee\xb3\xaf\xf4\x9b\x5c\xfe\x8b\xd4\x05\x40\x58\x7e\x6d\x41\xe7\x4e\xda\x02\x96\xa3\xf6\x5b\x2a\x0d\x2a\x48\x79\x95\x8e\x83\xba\xf1\x6a\x3c\x0d\x06\x0f\xa8\x5a\xa8\x20\xde\x2c\xfc\x82\xd6\xfe\x09\xea\x06\x2b\x5f\xec\x2d\x54\x11\x9a\x11\x8b\xf2\x65\x7f\xab\x0d\x35\xc1\xe4\x66\x7f\xab\xed\x92\xf1\xc0\xc4\xf9\x0b\xbe\xa6\x59\xb0\xa9\x1d\xac\xe8\x2b\x38\xff\x06\x51\xc6\x93\x78\xc7\xc9\x94\xda\x68\x6f\xff\x72\xf8\x09\x36\xdd\x93\xf8\x1d\x96\xc2\x20\xba\xbc\xbc\xac\xc6\x33\x1c\xa5\xe9\xa4\x1a\x27\xe7\xab\xc3\x78\x90\xae\x42\x12\xee\x78\xd5\xa8\x33\xce\xa6\x13\x87\x22\x64\xfb\x62\xf6\x6e\x6b\x47\xa2\x2d\x9e\x0b\x06\x43\x58\xec\x03\x62\xec\x71\x96\xf7\x0b\x4b\x79\x0e\x7b\x14\x19\x98\x94\x3c\x84\x11\x77\x7b\x51\xc2\x3d\x4b\x57\x97\x16\x2a\xd5\x1b\xeb\x9a\xa7\x8b\x05\xdf\x63\xa4\xa6\x86\xc5\x30\x13\xa4\xec\x6f\xb5\x17\x61\x1b\x66\xcc\x16\xd9\x0c\x52\xad\x7c\xc8\x62\x34\xa3\x56\xa7\xaa\x77\x8e\x67\x87\xb3\xfc\x62\x8c\xdd\x81\x0d\x4f\x17\xd5\x1b\xeb\x60\x42\xaa\x7d\xa5\x9d\x03\xcc\x8d\x2f\x12\x1f\xad\xed\x9b\x5b\xbb\xdd\x78\x88\xf6\xa1\xfd\x70\xb0\xd2\xe8\x3d\x98\x59\x7f\x19\x8e\x2c\xef\x1b\x4a\xf3\x0b\x52\x34\x2d\xae\xf8\xa7\x9c\xab\x75\x23\x9f\xdf\x6d\xc1\x54\xf4\x69\xac\xd5\x6a\x26\xe0\x25\xbd\x83\x16\xfa\xfd\x14\x93\x77\xb7\x20\x85\x3f\xa1\x11\x42\x15\x90\x08\x3b\x80\x0c\xac\x64\xd1\xde\xc6\x4a\x9f\xd7\xa5\xb1\x00\x5c\x80\x72\x2a\xa7\xc1\x24\x43\x9b\xf0\xcf\xf2\x62\x31\x50\x17\x25\xef\xfb\x20\x2f\x4c\x36\x8f\x2f\xc3\x51\x95\xba\x45\xe0\x12\xef\x4c\x05\xf0\xcb\xc9\x5b\x03\xc5\xb5\xfc\x8e\x7a\xcd\xa5\x04\x5e\x7d\x8a\x1d\xe2\x2d\x59\xe9\x8c\x7b\xd8\xb5\x85\x97\x1a\x21\x0f\x66\xa2\x2c\x57\x87\x13\x96\xcf\x2d\x0c\x42\x0b\xd0\x21\x7e\x07\x63\xe3\x4a\x89\xb6\xcc\x19\x59\x02\x13\x3e\xc1\xe2\x8d\xf7\xb8\xcc\xf7\x18\xda\x23\xf6\xe4\x28\xa7\x30\x71\x5a\x54\xbe\x70\x60\xf9\x96\x6d\x4c\x04\xbc\xfe\x91\x19\xb3\x18\xb8\x72\x83\x96\xd7\x1c\x1f\xe7\x51\x80\x88\x71\xe0\x39\xe0\xbd\x60\xd6\x5d\x96\x68\xd9\xc5\xd7\xca\x48\x0d\xc6\x20\x9d\x40\x18\x14\x4e\x6c\x8a\x51\xb0\x45\xaf\x7a\xf3\xc2\x9f\xce\x2e\x41\x68\x42\x0c\x9c\xfd\x59\x3b\x28\xd5\xe9\x41\x49\x19\xe8\xdc\xb4\x3f\x06\xf6\x02\x59\xef\x28\xb8\x30\x76\x0c\x95\xfd\x4e\x21\x2b\x16\x33\xc6\xd9\x86\x31\xca\x4a\x2d\x45\x47\xc3\xe9\xcf\x11\xed\x42\x04\x98\xe3\xf5\x8a\xda\x5c\x17\xe2\xc1\xaa\xdf\xf1\xad\x78\xef\x92\x7c\xf7\x1e\xbd\x6f\x1d\x7e\x65\x4a\x6f\x8a\x73\x73\xa5\x92\xa6\xdd\x50\xde\xeb\xdc\x5d\x7e\x40\x1a\x57\x17\x9b\x36\xdd\xaf\x7d\x9c\x7d\xb9\x6a\x15\xe4\x11\x1b\xee\x02\x26\x57\x6c\x10\x2a\x64\x29\xeb\xfb\xf6\x1c\xdb\x85\x85\x0d\xbb\x2e\xb1\x80\xe3\x4a\xfe\x7e\x77\xf3\x2a\xe7\xf8\x4e\xa1\xb9\xcf\xee\x15\x7e\xf8\xec\xb6\xd7\x2b\xfc\x48\xda\x5d\x5b\x23\x67\xfa\xb5\xbf\xf5\x99\x7e\x10\xce\xc6\x38\x79\xf1\xc0\x26\x02\x70\x7a\x57\x9b\xfa\x6b\x0e\xf1\x76\xe6\xce\x7b\x39\xcd\xf7\xa0\x63\x87\x84\xe3\xa4\xe2\xd0\xae\xbe\xf4\x9b\x10\x88\xf7\x46\x26\x0c\xad\x06\x39\xc3\x05\x19\x54\xa2\x3f\x39\x23\x66\x15\x77\xe0\x65\xc6\xa2\x2a\xd0\x22\x4b\xa4\xd3\x20\xa7\x1b\x3a\x37\x19\xbe\xca\xc8\x29\x32\x60\xcf\x68\x46\xfb\xc4\x7c\xb3\x78\xaa\x8d\x60\x88\x07\xe1\x34\x98\x4c\xae\x59\x1a\xd0\x61\xe1\x9b\x1b\x75\x54\x6e\x58\x2b\x6c\xe0\x4e\x04\x1a\x7a\xb3\xcb\x27\xe3\xb8\x0d\x7e\x0f\x9a\x9e\x43\x4e\x89\x72\xab\xa3\x76\x7e\xb9\x8b\x1d\xad\xa6\xc7\x51\x4b\x2d\x53\x95\xb3\x2b\x13\x48\xec\xe2\xab\x5b\x66\x82\x70\x0c\xaf\x42\x3e\xea\x7d\xc3\x92\xd3\x69\xdc\x3c\x84\xd1\x6c\x9e\xdd\x65\x4e\x39\x79\xe8\x44\x77\x0b\x3a\xbb\x2f\xe2\x18\x18\x8c\xc2\x41\x1f\xb7\x4e\x2a\x01\xa3\xe5\x0e\x61\x23\x27\x67\x03\xc9\x36\x68\x85\x57\x4e\xea\xe9\x69\xd4\xc3\x35\x02\x12\x50\x57\x05\x7a\xe3\xd6\xcd\xfb\x77\x5a\xd9\x5d\x63\xb7\x55\x36\x88\x6e\xbb\x51\x31\x94\xe7\xeb\x8f\xa6\x76\xff\x74\xdd\xb7\x6f\x77\xb4\x22\x99\xe7\x69\xc2\xed\x43\x0a\x38\x00\x0b\x8d\xab\x33\x11\x15\x29\xb1\xa1\x3a\xaa\xde\x4f\x42\x7a\x70\x79\x5d\xc8\xf1\x0a\x2b\x89\x0b\xaa\xa2\x88\xac\x0e\xce\xcb\x78\x90\xe0\xec\x9e\x94\x4a\x44\xfe\xdd\x75\x07\x0e\x82\x5e\x32\x36\xe1\xf2\x44\xa6\x8e\xbe\x45\x35\x86\xaa\x73\xb0\x27\x40\xb0\x53\x67\x24\xf4\x45\xd4\x47\x41\x3c\x9a\x1e\xee\x39\xde\x6e\xf7\x19\x5f\x16\x0e\x4c\x0b\xc2\xcb\xd2\x43\x95\x12\x5d\xd6\x1c\x27\xb7\x21\x7e\x8e\x62\x8a\x76\xf4\x8d\x12\x17\x93\x75\x3d\x2f\x32\xa6\x51\x89\xeb\x0b\x4c\x58\xee\x28\x99\x9b\x93\x49\x7c\x89\x82\xa4\x1f\x66\x49\x90\x5c\x23\xa6\x5e\xfa\x82\xaf\x1d\x71\x07\xbf\xa8\x1a\x89\x9f\x9d\x0d\xe7\x0c\x94\xa9\x6e\x29\x36\x5a\x0b\x9c\x21\x09\x4a\x39\x6e\x90\x10\xff\x0d\x74\x1b\x71\x82\xc2\x28\xc2\x09\x44\x9f\x8d\xe7\x19\x08\x10\x66\x14\x3e\x88\x99\x48\x75\x8c\x94\x0c\xd9\x03\x6d\xc5\x0a\x48\xc7\x35\x7e\x6a\x8d\xd0\x51\x63\x19\x12\x88\x15\xad\x64\x9c\xa7\x8f\x0c\x95\x82\xa1\x52\xd0\x6a\xec\xb7\x83\x23\x98\x4f\x7a\x0d\x38\x0b\x86\x68\x10\x47\x69\x16\x44\x66\xf3\xce\x24\x52\xfa\x1c\xfb\x15\x6b\x02\xef\xd3\xf0\x0c\xfd\xbe\x81\x6a\x57\xed\x01\xfd\x9f\xcb\x1d\xc6\x2a\xdc\xec\xd0\xff\xe5\x6b\xc6\x62\x43\x27\x16\x1a\xcf\x2e\x8a\xfc\x0b\xe2\x90\xc1\x0e\xf4\x10\x51\xc8\x04\x13\xbf\x97\x48\x64\x39\xf9\xca\x5c\xcc\xd8\x31\x90\xd0\x69\x17\x1f\xf7\xe8\x49\x75\x7d\xb1\x5c\x30\xb7\x8b\x40\x06\xc3\xfc\xdd\xc4\x1f\xdb\xdf\xec\xb1\xe8\x63\x80\x57\x08\x4b\x2c\x37\x12\xca\x92\x53\x5e\x24\x10\x99\x55\xfa\xfe\x83\x91\xa9\x24\xc1\x5b\x59\x18\x7c\xec\xa1\xa2\x87\xc1\x50\xff\x4f\x8f\x1e\xb6\x40\x4c\x5d\x46\x44\x24\x3c\x54\xd2\xd0\xc2\x08\x62\xfe\x1a\x0b\xa3\x88\xf9\xab\x3e\x50\x24\xb1\xbb\x73\xbb\x1e\x55\x4f\xc3\x78\x3b\xf6\x63\x22\x5d\xec\xba\x83\xa3\xe5\x06\x1c\xcb\xe5\x98\xea\x58\x19\x40\xa5\x84\xc2\x25\x0d\x7e\xc9\x24\x50\x29\x7b\x43\x8e\x4d\x83\x81\xfb\x92\x48\x1c\xfc\x3d\x46\x70\x2f\xff\xd6\x0a\xf3\xab\x4e\xeb\x85\xe3\xf5\x24\xec\xbf\x20\xa8\x0c\xc1\xb6\x35\x35\xbe\xe2\x68\xf0\x02\x6c\x1a\x1d\xef\xa9\x9b\xa5\xf1\x61\x3a\x6c\x2f\x36\xbe\x4b\xc7\x41\xa3\x6d\x82\x24\x2f\x1b\x26\xb8\x74\x1c\xb4\xeb\x0d\xfb\x65\x73\xdd\x51\xb2\x69\xbc\x4a\xc2\x19\x9e\x0e\xeb\x9d\x9a\xd3\xf6\x4f\x7b\x35\xeb\x7f\x19\x8e\xcc\x76\xf0\xc5\xec\xcb\x70\x94\x77\xef\xa0\x77\x3d\x1e\xe2\x17\x83\x51\xdf\xf9\x3a\x4b\x3c\xaf\x5f\x9c\x4f\x82\xe1\x34\x88\x5c\x9f\x63\x37\x30\x3c\x30\x5f\xcf\x82\xe1\x8b\x20\x4a\xc3\xab\x97\x0d\x73\x10\xc8\xa7\x30\x8d\xeb\xb5\x7a\xc3\x1c\x71\xf6\xe9\xe5\xda\xcb\x35\x73\x86\xc8\xa7\x3f\x70\x12\x33\xd7\x6b\xc7\xd7\xc8\xf3\x8d\xea\xc8\x5e\x8c\xf1\x95\xf1\x21\xc0\x26\x71\xd1\xb8\x1b\x43\xeb\x7d\x32\x30\x27\x37\x09\xfa\xfd\x30\x73\xbe\x7c\x31\xc1\xe7\xc1\xe0\xfa\xa1\xef\x80\xc4\xea\x81\x27\x73\xd1\xc0\x4b\xb9\x56\xc4\x23\x5b\x22\xf0\x4c\x56\x86\x61\x16\xca\xd6\x81\xf8\xdd\x68\x89\xdf\x84\xea\xf9\x6f\x42\xec\xe2\x37\xfd\x25\x49\x5b\xda\x97\xc2\x2f\x46\xc8\x14\x03\x4a\xbf\xd6\x1d\x16\x45\x87\x53\xab\xf2\x94\x25\xfa\x93\xa0\x4d\xf9\x36\xd6\x6a\x10\x4a\xa4\xcd\xaa\x04\x28\xde\x08\xba\x53\xdf\x50\x72\x13\x6f\x54\x2a\x13\x2f\x23\xfd\x95\x42\x53\xf0\x4c\x48\x09\x7e\x48\x0a\xa2\xa3\x32\x60\x03\xc5\xe8\x45\xf9\xcd\xc9\x64\x59\x45\xa4\xa6\x80\x54\x79\xed\xf2\x8a\x49\x7f\x28\x36\xd6\xa5\x6e\xbb\x5e\xc9\xd7\x26\x57\x74\xba\xea\xb6\x5b\x15\x8d\xf0\xba\xed\x76\x45\x4e\x7c\xb7\xdd\xa9\xe8\xa3\xd7\x6d\xaf\x99\x37\xc2\x26\x29\x77\x3b\xb5\x0a\xa3\xd6\x6e\x07\xf0\x11\x94\xd2\xed\x34\x2a\x2a\xad\x74\x3b\xad\x8a\x8b\x5a\xba\x9d\x66\x45\xa5\x90\x6e\xa7\x5d\x51\xe9\xa7\xdb\x01\xbc\x34\x9a\xe9\x76\xd6\x2a\x26\xd5\x74\x3b\xeb\x15\x93\x6e\xba\x9d\x97\x15\x8b\x48\xba\x6b\xb5\x8a\x83\x9c\xba\x6b\x80\x3f\x5b\x12\xdd\x35\xc0\x9e\x91\x46\x77\xad\x55\xb1\x88\xa3\xbb\x06\x88\x13\x32\xea\xae\x01\xce\x72\x9d\x75\xd7\x3a\xea\x05\x7a\x45\x2e\xd9\xee\x1a\xbf\x5a\x27\x8b\xb9\xbb\xf6\xb2\xc2\x97\x6a\x77\xbd\x56\x91\x4b\xb8\xbb\x5e\xaf\xc8\xc5\xdd\x5d\x07\x74\x24\x05\x77\xd7\xa1\x71\xc1\x68\xba\xeb\xad\x9b\xb3\x4a\xa7\xf6\x78\x79\xf0\xd7\x5f\x1e\xf4\xc6\x78\xf0\x85\x74\x0a\x56\x0a\x75\x03\xa2\x69\xce\xd2\xf9\x8c\x0c\x0c\x66\xf1\xa9\x95\x7e\x83\x1c\x4f\x43\x9a\xa3\x1f\x36\xd0\x0a\x87\xbc\xe2\xb0\x08\x11\x4e\x1a\xf7\x78\x5d\x91\x6b\x8e\x2f\xda\x39\xc2\x23\x9c\x60\x38\xe8\x25\xe1\x39\x9c\xc9\xc2\x28\xcc\x24\x98\x74\x3e\xc3\x09\xa8\xae\x37\x8c\xf4\x1c\x0a\x94\xcd\xf9\xf9\x14\x47\x99\x51\x00\x65\x31\x1a\x07\xd1\x70\x82\xb5\x71\x53\x61\xf7\x9d\x90\x35\x9b\x1a\xa8\x6a\xbb\x03\x2a\xba\x6f\x1a\x4b\x9e\x9a\x40\x85\x51\xb6\xae\x68\xe8\x47\x6a\x7d\xa1\x98\xd0\x67\xc7\x3e\xe6\xcb\x1a\x54\x09\xff\x91\x40\x85\x17\x2a\x36\xda\x21\xc2\x89\x58\x4c\xd3\x7f\x01\xa4\x8b\x10\x5f\xfa\x50\xf4\x36\xaf\x20\xbc\xc7\x51\x40\x5f\xbf\xea\xe5\x39\xc1\x01\x96\xa0\x33\xe6\xd5\x7f\x20\x6b\x4e\xd8\x8e\xc0\xa2\x73\x03\xb7\xaa\x96\xad\x56\xbc\x58\xd5\x3b\x6e\xb4\xfc\x2d\x2d\x57\x63\x2f\xca\x9a\x8d\x65\x9b\x58\xae\xc6\xce\x24\x0e\x6e\x53\xa5\xd3\x82\xf7\xb2\xfc\x2d\x49\xa9\x4a\x29\xb8\x82\xd4\x57\xd7\x19\x3e\x80\xe4\x40\xd6\x6b\x57\xde\x65\x8d\xfe\x76\xe9\xa2\x93\x6d\x15\x59\x11\xb2\xf4\x72\x2a\x04\x09\xed\x8d\xc0\x0d\x6d\xb8\x71\x76\x68\x16\xb6\xaf\x58\xf6\xd5\xeb\xcc\x65\xfc\xbc\x94\xbb\xa0\x0b\x95\x65\xf2\x69\xcb\xfa\xa7\xe1\xd9\xad\x92\x67\x4b\x73\xee\xf0\x0f\x4c\x55\xb5\xd2\x71\x54\x2f\x2a\x18\xab\x4c\x6d\x51\x41\xcc\x8d\xd0\xd5\x11\x6d\xbe\x9d\x59\xcf\xc8\x68\x92\xd7\x04\x1e\x8a\x88\xd4\xa7\x32\x73\xbb\xdd\x60\x36\x9b\x5c\xb3\x86\x83\xe4\x7c\x4e\x58\x78\x9a\xe7\xaf\xc8\xf8\x75\x75\x96\xc4\x59\x4c\x70\x54\x39\x77\x9e\xe1\x84\xb9\xfb\xb8\x15\x2c\x9d\xfa\xa3\xac\xf3\xd7\xc8\x3a\x10\x30\xfa\x2f\x88\x4b\xe4\xcc\xa9\x54\xc0\x44\x02\xb6\x58\x7a\x8f\x87\x32\xa9\x5b\x27\x55\x4e\x18\xb3\x50\x4a\x52\xd5\xa5\x71\xf3\xe7\x92\xf4\x7c\x7c\xa5\xd3\x72\x73\x91\x13\xc2\x26\x36\xe8\xf0\x55\x83\x7e\x4a\x7f\xa4\x61\xc4\x82\xb1\x12\x96\x51\xbb\xaa\xd7\xd8\x5f\x19\x7d\xd5\xd3\xf8\xb2\xe5\x55\x2a\x3b\x2d\xd4\xf7\xb7\xda\x86\x35\x85\xcb\x00\xc4\xf4\x9a\x44\x1b\x6c\x54\x1d\x06\x20\x3c\xed\x4d\xee\xed\x98\xd4\x04\xbb\x73\x15\x9f\xda\x9c\xb4\x76\xd5\x59\x6b\xb5\x1b\xcd\x5a\xbd\x82\x6a\x57\x78\x34\x18\x06\xfd\xf5\x97\x8e\xbc\x8a\xb5\xab\x97\xeb\xfd\x60\x38\x18\xe1\x0a\x0c\x4c\xb3\xd1\x6e\xad\x75\xf4\x72\x67\xde\x1b\x31\x23\x8d\x9e\xda\x8b\x7d\x91\x49\xcf\xb5\x77\x5d\x06\x33\x84\xc1\xbd\x7a\xf1\x1e\x52\xef\xf8\x77\x0c\xff\xf5\x35\x9f\x0d\x8a\xc4\x27\x02\x8f\xa7\x17\x44\xa1\x27\x02\xef\xfe\x27\xa5\xf4\xfe\x29\x7f\x38\x73\xb9\x84\x28\x9f\x09\xc1\xd9\x05\xc8\x5f\xa9\x54\x52\x60\x52\x4f\x71\xf4\x15\xa9\x2f\x61\xaf\x6b\x95\x0d\x1f\x71\xf4\xb5\x20\xc0\x46\xab\xec\x00\x08\xa1\x8c\x35\x97\x74\x1b\xdc\xdd\x8c\x43\x76\xb5\x1b\x0a\xf7\x75\xbf\x36\xa4\x35\xa4\x8c\x29\x7a\x8e\x6a\xa6\xf8\xa0\x95\xae\x1b\xa5\xeb\xb9\xa5\x1b\x46\xe9\x46\x6e\xe9\xa6\x51\xba\x99\x5b\xba\x65\x94\x6e\xe5\x96\x6e\x1b\xa5\xdb\xb9\xa5\x3b\x46\xe9\x4e\x6e\xe9\x35\xa3\xf4\x5a\x6e\xe9\x75\xa3\xf4\x7a\x6e\xe9\x97\x46\xe9\x97\xf9\xb3\x53\x33\x66\x67\xc1\x64\xd6\x8d\xe2\xf9\xb3\x59\x6f\x18\xc5\xf3\xa7\xb3\xde\x34\x8a\xe7\xcf\x67\xbd\x65\x14\xcf\x9f\xd0\x7a\xdb\x28\xde\xb6\xb8\xc1\xea\x2a\x61\xc8\x5f\xc2\xe8\x9c\x54\x0d\x83\x49\xdf\x25\x36\x07\x64\x1b\x38\x75\x0e\x54\x1f\x3e\x39\x07\x65\x00\x9f\x9c\x03\x30\x84\x4f\x4d\x17\x3a\x3d\x79\x07\xad\x7f\x23\x48\xec\xec\x94\x82\x0a\xea\x57\xd0\xa0\x82\x86\x15\x65\x81\x56\x10\x5a\xab\x90\x2d\xb4\x76\x66\xf2\x86\x21\xad\x37\xac\x20\x51\x55\x8e\x50\x05\xa1\x7a\xa3\x82\x4e\x4e\xeb\x56\xbd\x01\xad\x47\x5b\xa2\x55\xe5\xa2\x25\xf5\xd6\x48\xbd\x86\x55\xaf\x4f\xeb\x09\x24\x03\xa5\x5e\xb3\x82\x50\x03\xda\x6b\x5a\xf5\xf2\xfa\xd7\x12\xfd\x6b\x2d\xd5\xbf\xb6\xe8\x5f\x7b\xa9\xfe\x75\x44\xff\x3a\x4b\xf5\x6f\x4d\xf4\x6f\x6d\xa9\xfe\xad\x8b\xfe\xad\x2f\xd5\xbf\x97\xa2\x7f\x2f\x97\xea\x5f\xbd\x56\x61\xfd\xab\xdb\x04\x93\xd7\xc1\x7a\xbd\xc2\x3a\x58\xb7\x29\x26\xaf\x87\x04\x4b\xda\xc3\xba\x4d\x32\xb9\x24\xda\xac\x70\x12\xb5\x69\x26\xb7\x8f\x2d\xd1\x47\x9b\x68\x72\xfb\xd8\x16\x7d\x04\xaa\xb1\x3b\xf9\xf6\xad\xa7\x93\x15\x84\xda\xb4\x93\x36\xdd\x0c\x69\x45\x67\x27\x09\xbd\xbd\xa4\x15\x6d\xc2\x19\xd0\x8a\xee\x4e\xd6\x2b\x88\x74\xf4\xe4\xb4\x6e\x53\x4e\x9f\x56\x74\x76\x92\x70\x8c\x46\x0d\x2a\xda\xa4\x93\xd7\xc7\xb6\xe8\x63\xc3\xcd\x6b\x7c\x7d\x24\x34\x47\xfb\xd8\x70\x33\x1b\x6f\x1f\xdb\xbc\x8f\x0d\x37\xb7\xf1\xf5\xb1\x25\xfa\xd8\x70\xb3\x1b\x5f\x1f\x5f\xca\x3e\xba\xf9\x8d\xb7\x8f\x2d\xd1\x47\x37\xc3\xf1\xf5\x91\x30\x46\xd6\x47\x37\xc7\xf1\xf5\x71\x5d\xf6\xd1\xcd\x72\xbc\xb4\xda\xac\xf0\x3e\xba\x79\x8e\xaf\x8f\x0d\x41\xab\x0d\x37\xd3\xf1\xf5\x71\x4d\xf4\xb1\xe9\x66\x3a\xbe\x3e\x92\xe5\x4f\xfb\xd8\xac\xbb\x17\xe4\xee\xae\x9f\x58\x5b\x80\x6b\xd3\xcd\x75\x76\x77\xdd\x9d\x24\xc3\x4a\xd6\xd6\xc9\x69\xd3\xcd\x75\x76\x77\x73\x16\x64\x07\x2a\xba\xb9\xce\xee\xae\xa7\x93\xad\x0a\x6a\x34\xa1\xa2\x4d\x3a\x79\x7d\xac\xcb\x3e\xba\x99\x8e\xaf\x8f\x2d\xd9\x47\x37\xd3\xf1\xf5\x11\x26\x92\xf6\xd1\xcd\x74\xbc\x7d\xac\x89\x3e\xba\x99\x8e\xb7\x8f\xcd\x0a\xeb\x63\xcb\xcd\x74\x7c\x7d\xac\x89\x3e\xb6\xdc\x4c\xc7\xd7\xc7\xa6\xe8\x63\xcb\xcd\x74\x7c\x7d\x24\xac\x9c\xf6\xb1\xe5\x66\x3a\xbe\x3e\xbe\x14\xf3\xd8\x72\x33\x1d\x5f\x1f\xc9\xf2\x60\x7d\x74\x33\x1d\x2f\xad\xb6\x39\xad\xb6\xdc\x4c\xc7\xd7\xc7\x86\xec\xe3\x9a\x7b\x41\xee\xed\xf9\x05\xd5\x0e\xed\xa4\x9b\xeb\xec\xed\xb9\x3b\x09\x34\x07\x3c\xa0\xe5\xe6\x3a\x7b\x7b\x39\x62\x40\x1b\x44\x40\x37\xd7\xd9\xdb\x73\x77\x92\xf0\x8e\x06\x0c\x6b\xdb\x2d\xea\xf8\xfa\x48\xe6\x83\xf6\xb1\xed\x66\x3a\xbe\x3e\x36\x45\x1f\xdb\x6e\xa6\xe3\xed\x63\x4d\xf4\xd1\xcd\x74\x7c\x7d\xac\xcb\x3e\xba\x99\x8e\xaf\x8f\xeb\x62\x1e\xdb\x6e\xa6\xe3\xeb\x23\xd0\x1c\xed\xa3\x9b\xe9\xf8\xfa\x08\x22\x39\xed\xa3\x9b\xe9\x78\xfb\xd8\xac\xf0\x3e\xba\x99\x8e\xaf\x8f\x2d\xd1\xc7\x8e\x9b\xe9\x78\xfb\x58\xe7\x7d\xec\xb8\x99\x8e\xaf\x8f\x0d\xd1\xc7\x8e\x9b\xe9\xf8\xfa\xf8\x52\xcc\x63\xa7\x69\x2f\x48\xb8\x46\xc9\x70\x32\xc5\xc3\x30\xc8\x98\x53\x19\xb8\x2b\xe8\xe5\xc8\x11\x17\x6d\xa0\x12\xfc\xfb\x1c\x05\xa6\x86\x95\x96\xa9\xb3\x32\x75\x52\xa6\xef\x2e\xd3\x60\x65\x1a\xa4\xcc\xc0\x5d\xa6\xc9\xca\x34\x49\x99\xa1\xa5\xcd\x35\x54\x95\x3b\x0e\x4b\xdd\x25\x03\xda\x42\xa6\x74\x91\x4d\x37\xc8\x02\xd7\xc1\x3c\xc8\x02\x11\xca\x27\xc8\x02\xbf\x72\x2c\x7a\x13\x66\xe9\x49\x9c\x05\x13\x01\x33\xda\x0a\xb2\x80\x7a\x90\xfc\x84\xd6\x1d\xd0\xa1\xce\x7b\x3c\xca\x38\x74\xe1\x71\x02\xe5\xad\xce\x78\x53\x5e\x09\x34\x4f\x25\xc8\x9f\x7f\xfe\x19\xb5\xe1\xe2\xad\x76\xb5\x5e\x93\xf7\x6d\xb2\xc4\xbf\x50\xb3\x61\x11\x87\xde\x97\x5d\xb4\x81\x40\xed\x3e\x9a\xc4\x71\x52\x52\x3a\xb9\xaa\xe9\xde\x7d\x9d\x83\xb2\xef\xd1\x86\xf2\x64\x2e\x1c\x81\x7a\xa9\x54\x92\xb8\x3d\x47\x9d\x16\xcd\x97\xf6\x12\x82\x89\xb6\xca\x54\x61\xe3\xd6\xcf\xf2\xaa\x0c\x67\xa9\x9c\x55\xdf\x16\xd7\xce\xda\xe0\x98\x6a\xd6\x04\xb7\x48\x37\x6b\x71\x89\x65\x3a\xdb\x2a\xd2\xd9\xf7\xce\xce\xbe\xbf\x6d\x67\xdf\x3b\x3b\xfb\xbe\x68\x67\xed\xde\xaa\x4e\x54\x25\xd1\x7d\x1e\x6c\x0a\x72\xea\xb9\xfd\x07\xc1\xe0\x9d\xba\x31\x80\x8f\xa2\xcb\x93\x2a\x37\xaf\xfc\x02\x6f\x48\x4d\xe7\xed\x20\xdf\x5d\x66\x18\xef\xf5\x7e\x5b\xea\xde\xc3\x73\xc5\x85\xf2\xae\xff\x05\x26\x70\x85\xb1\x7b\xea\xbe\xbb\xd8\x65\xb7\x64\xa5\xd2\xae\x76\x2d\xb1\xbb\xf4\x7d\x04\xa5\x85\x5d\xed\x2e\x62\xd7\x7b\x09\xb1\xf8\xc6\xe1\x88\xe5\x06\x86\x39\x64\x11\x78\x86\x30\xa6\x7a\xd1\x02\xc9\xca\xc1\x0d\x21\x97\xd5\x83\x82\x15\x9c\x32\xc5\x0d\x1d\x3c\xca\xeb\x7f\x6b\xe3\x85\xcf\x9f\x2c\x5a\xf0\x79\x57\xf2\x08\x1a\xe4\xab\xdb\xc3\x81\xfe\x12\x48\x1a\xaa\xaf\xab\x0a\x4a\x2b\x48\xbf\x42\x03\x3e\x89\x36\x50\x80\x9e\xa3\x52\xa9\x8f\x7e\xa4\x9b\x63\xe9\xff\x92\x9f\xc3\x32\x61\x03\x57\xe8\x39\xca\x94\xf6\x44\xc0\xe2\x88\x4c\x53\x4a\x57\x2a\x8d\x53\xde\x6c\xa0\x17\x28\x2d\x43\xb5\xbe\x61\xf4\x26\xb0\x32\xce\xff\xc5\xb0\x82\xed\xb8\x34\x40\x3f\xa2\xff\xfb\x30\x58\x19\x87\xa0\x85\x58\xf5\xd1\xef\x68\x80\x7e\x27\x88\xdd\x3f\x32\x86\x00\xb8\x10\x19\x82\x48\xa9\x8f\xbe\xde\xf3\xe0\xa8\xb7\xd5\xc7\xbe\x34\xe9\x0b\x13\xef\x17\x09\xb2\xc6\xfd\xc4\x0c\x17\x45\x58\x0d\x36\x18\x8f\xb3\x98\xa7\xf4\x6d\xc3\x9a\xb1\x75\x29\x8c\x5c\xf6\xb7\xda\x0e\xdf\xaf\xfc\xf2\xb6\xc3\x97\x8c\x2f\xa6\x5d\xe6\xeb\x19\xf9\xf7\xb7\xda\x4e\x93\x01\xef\x24\x2c\xc8\x55\x7f\x5f\x53\x70\xab\xd0\x0e\x8b\x27\x4e\xf5\xf2\xbb\x8f\x89\xa3\x4e\x65\x62\x22\x76\xa7\xc1\x80\x4c\x86\x96\x19\xde\x9e\x0f\x56\xcc\x9e\x13\x99\xcd\x9e\xce\x4b\x6e\x06\x76\x16\xd9\xda\x63\x01\xd5\xf8\x5b\xbb\x98\xfd\xf3\x63\xb2\xd1\xc5\xf6\x13\x8b\x33\x84\x76\x30\x1e\xf6\x83\xc1\x17\x16\x57\x73\x1a\x0f\x61\x49\x11\x9a\x11\xf3\x0d\x2f\x7b\x3b\x6f\x88\x08\xe4\x10\x0f\xc0\xcc\x09\xbe\x6a\xd6\x72\x60\xe1\x42\x5b\xd9\x27\x00\x98\x31\x8f\x58\xf5\xbd\x9d\x37\xd5\xed\x88\xc6\x2a\x07\x03\xaa\x9d\x37\x0e\x83\x9f\x99\xc7\x5c\x86\x99\x19\xe6\x98\xcc\xf8\x45\x53\x16\x82\x8a\x0b\x24\xf4\xd1\x75\xcf\xac\x84\xf2\xa0\x85\xd4\x50\x1e\x7a\x79\x1e\xa3\xfc\x1d\xbe\x4e\xb3\x04\x07\xd3\xcd\x68\xc8\x7a\xe7\xb0\x8e\x8c\x99\x59\xac\x00\x57\x61\x0d\xb8\x84\xec\x23\x3c\xc5\x10\x64\x1c\x8c\x31\xe9\x3c\xb1\x58\x99\xe0\x3f\x1f\xe1\xab\x8c\xbe\x76\x8b\xef\xf8\xe2\x0d\x8b\x99\x0a\xad\x57\xd3\x49\x38\xc0\x25\x8e\x82\xb8\xa9\x17\xb8\xb8\xec\x27\xb5\x59\xdb\xc2\xff\x94\x59\xbb\xc3\xe8\x82\xe1\xf0\x38\x4c\x97\x1e\xdb\x6f\x46\x37\x27\xb2\x43\x7d\x3c\x88\xa7\xcc\xeb\x9e\x10\x44\x18\xcf\xd3\x62\x24\x23\xba\x58\x48\x1c\xcf\xe9\x4d\x69\x61\x17\x0c\xdf\x08\xfb\xc0\x06\xe7\xbd\x0b\x19\xac\xe5\xe2\x95\x6e\x34\xae\x86\x63\xa6\xcd\xcb\xcf\x90\xd9\xf5\xc2\x79\xa4\x11\xa5\xd1\x06\x0a\x2f\xd8\x14\xd6\x3c\x2b\x31\xbe\xc0\x68\xef\x17\x38\x7f\xa6\xf3\x7e\x8a\xff\x7b\x8e\xa3\x2c\xe7\xf4\x0c\xf8\x0a\x07\x86\x85\x06\xd0\x26\x3e\xc6\x84\xd8\x93\x40\xfe\x18\x95\x63\x3a\xd0\x50\xb0\x24\x80\x54\x90\xde\x95\xd5\x55\xc4\x66\x44\xbe\x73\x66\xcb\xcd\x8f\x1a\x43\x4d\xcf\xa5\x85\x20\x44\x82\x11\x8d\xc2\x39\xda\xa2\x17\x86\x05\x17\x27\x76\xde\xe4\x19\x5c\xf3\x4d\x67\x99\x38\x75\x9d\xe6\xa3\xf0\xf1\xbd\x0b\x1f\xe8\x3f\x67\x09\x4e\x71\x72\x81\xa9\x18\x12\xcf\x89\x28\xaf\x88\x1f\xa0\xc6\x08\xb2\xb0\x3f\x61\x1c\x18\x6d\x25\xe8\x4d\x12\x06\x11\x7a\x4b\xdd\x33\xd1\x28\x9c\x60\x1c\x0d\xaa\x03\x00\xc1\x43\x3e\x43\x04\x6c\x83\x7e\x4e\x8e\xa0\xc8\x7f\x05\x11\xda\x4d\xe6\xfd\x6b\xf4\x79\x4c\xfe\xa9\x5e\xe2\xfe\x7f\x9e\x4f\x83\x70\x52\x1d\xc4\x53\xb7\xbc\x73\x72\xc4\x9b\xcb\x11\x7b\xd4\x42\x85\xa5\x9f\x27\x32\xdf\x4b\x34\x20\x07\x05\x9a\x32\xe9\xe9\x93\x27\x64\xd0\x81\xf4\x44\x3a\x24\x50\x12\x51\xa5\x50\x19\x66\x9d\xfe\xfa\x13\xad\xae\xc6\x17\x38\x19\x4d\xe2\x4b\x52\x07\x36\xbe\x3a\x4f\x07\x4a\xea\xd5\x3b\xe5\x1f\x49\xd9\x57\xe2\x73\x43\xfd\xbc\x6e\x7e\x6d\xb2\x3d\x8c\x35\x06\x78\x02\x2a\x04\xac\x68\x77\x75\x15\xf1\x66\x51\xbf\x4e\x8a\x00\xca\xd0\x74\xed\x95\xa8\xd2\x90\x55\x44\x99\x27\x80\x00\x2d\x44\x4b\x35\xf5\x52\xac\xd8\x13\x40\x85\x95\xbb\x81\xff\x12\x82\x54\x4b\x3c\x7f\xde\x6f\x2a\xdf\xe1\x3f\xbc\x0c\x2d\xf2\xfc\x79\xbf\xf1\xea\xa9\xbf\xc0\xf3\xe7\xfd\x3a\xfb\x4e\xfe\x0b\x1d\xe7\x8d\xc2\xc3\xf3\x0d\xe8\xf9\xeb\xd7\x2c\x1f\xa4\xfa\xba\x41\x55\x80\xda\x5b\x86\x90\xdd\x92\xa8\x56\xbb\xaa\xd5\x99\xd6\x4f\x16\x65\x5c\x8f\x14\x22\x2f\x6f\x4c\xea\x60\xcb\xa3\x34\xa0\xff\xea\x34\xc2\x5e\xd2\x1b\x24\x4e\x4a\xf2\x65\x99\x11\x8c\x32\x05\xab\xab\x88\xec\x12\x70\x13\x83\x42\x65\x21\xd1\xc5\x63\xad\xb4\x95\x14\x01\xbc\x14\xc5\xd1\xe4\x9a\x2e\xc7\xad\x5f\x0f\x8e\xb6\xd0\x67\xf4\x1a\xad\x03\x4c\xde\x60\xdd\x85\x05\xbd\x8b\xd3\x3b\xcb\xbe\xf1\xfe\xf2\xb5\xa4\x9d\x05\xc4\xba\xaa\x7a\x5e\xff\x85\x32\xe7\xb2\x22\xa7\x55\xdc\x90\x61\xec\x56\x19\x4f\x14\xcd\xf2\x01\xb3\x50\xcf\x93\x78\x90\x5f\xea\x01\xa1\xc1\xdd\x48\xbe\x0c\x84\x6e\x21\x07\xa1\xc5\xb2\x10\x97\x0e\x08\x61\xdb\x34\x4f\x59\xd1\x13\x53\x34\x62\x9f\x15\x5c\x75\xd5\xf3\x32\x42\x11\xf2\x08\x46\xe8\x76\xc2\x11\x5a\x52\x40\x42\xba\x3c\x67\x1f\xba\x24\xdd\xab\x67\x2f\xb1\x34\x5e\x19\x92\x95\x28\xae\x08\x58\x5e\x11\x4b\x29\xbc\x84\xa4\xd5\x7a\x94\xb4\xbe\x77\x49\xcb\x23\x5f\x79\xd4\x3b\x27\x47\xf9\x72\xce\xb2\xea\x1d\x07\x4b\x37\x79\xf9\x23\x13\xff\xe7\x31\xf1\xdc\xd3\xec\x03\xb0\xec\xbd\x68\x90\x60\x88\xdc\xc0\x80\x1b\x20\x99\x1c\x22\x27\xf7\x05\xa2\xc6\x34\x9e\x2f\x70\x5b\xfe\x15\xd5\xfe\x56\x9b\x43\xd1\x5d\x61\xf1\x79\x9b\x94\x59\x62\x17\x68\x3f\xee\x02\x7f\x8b\x5d\x60\x7b\x82\x07\x59\x12\x47\xe1\x00\xf5\xe2\x21\xee\xc7\xf1\x62\x85\xff\x76\x2f\x4f\xe1\x4f\xbf\x2e\xb5\x23\x6c\xf7\x74\x85\x3f\x79\xbe\xaf\x1d\x40\x65\xed\x3a\x03\xd1\xeb\xe5\x69\x31\x09\x3e\xda\x42\x7a\x28\xfc\x86\xf8\x56\xf8\xf1\xd4\x4b\xbd\xc5\x7a\x33\x28\xb3\xc4\x3a\xfe\x7b\x27\x47\xfe\x9f\xb3\x8e\x0f\xe6\xd9\x6c\x9e\x15\xbf\xb4\x3b\xc8\xbd\xb4\x3b\x58\xfe\xd2\xce\x94\xea\x0e\x8c\x4b\xbc\x83\xbf\xf6\x3a\xe8\xc1\xa5\x3a\x5b\x37\x2f\xde\xdc\xaf\x64\x97\xd3\xd0\xf7\x22\xdd\xfd\x93\x4e\xd8\x07\xc6\xb5\xa6\x4f\x88\x3a\x28\x70\x69\x71\xb0\xe4\xa5\xc5\x63\x16\xbb\xbf\x07\xf3\xdd\xfc\x70\xbc\x87\x7e\xab\xbe\x6c\x34\xb9\x81\x38\x4a\x33\xb2\xbc\xcf\xaf\x2d\xee\x3b\x0b\x86\xd5\xcd\x28\x0d\x7f\x23\xa5\x45\x2e\xb8\x59\x30\x54\xd9\xdf\x30\xc8\x02\xe5\x22\xd4\x77\x01\x9a\xea\x37\xa0\xa4\xd6\xb1\x34\xf8\xd5\x0c\x80\x5f\xe9\x45\xfb\x66\x5a\x91\xbe\x2f\xa1\x08\x10\xc5\x3c\xca\x44\xcf\x8c\x60\x56\x60\x8b\x77\x48\xbf\x59\xc0\xe8\x8b\x17\x3a\x66\xff\x32\xbe\x5b\xad\xd1\x98\x36\x93\x20\xa5\x91\xb3\xd0\x2c\x4e\x43\xdd\x03\x9f\x34\x4a\xbe\x93\xfa\x87\x31\xef\xac\x68\xe1\xb9\x81\xd1\x0b\x54\x37\x1a\x39\x0c\x86\xf2\x19\x06\x4a\x64\x1b\xd1\x5f\x53\x56\xa2\xb6\x25\x43\x6a\xe9\x8d\xc8\x90\x5a\x6a\x69\x57\x70\x2d\xdd\x32\xfb\xb9\x01\x88\xdb\x21\x72\x0b\xdc\x79\xe4\x20\x0e\x93\x22\xde\xe2\x4c\x49\x38\xaf\x4d\x15\x55\xe0\x8b\xd1\xcc\x9f\x39\xa5\xcf\x25\x1d\xcd\x17\xe4\xf8\xcb\xfa\x2e\x2f\x82\x14\x14\xd8\xbe\x62\x79\x48\x18\x60\x3c\xbd\x7d\xfa\xe4\xc6\xc9\x37\xf9\x72\xb9\x7a\xd9\x68\x2e\xc5\x3b\xef\x96\x98\xec\x91\x77\x7e\x2b\xde\xb9\x77\x7c\x80\x20\x24\x6e\x31\xd6\xb9\xc7\x02\xe8\xde\x95\x75\xfe\xe5\xec\x50\x2e\x89\x05\xfc\xd0\xc1\xaa\x68\x3a\x00\x77\x04\xba\x6a\x12\x44\xc3\x78\x5a\xb2\x38\x60\xb9\x5c\x35\x24\xa5\x7c\x38\x2c\x75\xd8\xa9\xc5\xe5\x1a\xad\xb3\x0a\x01\xf7\xc8\xa8\x4c\x46\xc5\x89\x73\x29\x46\xf5\xf7\xce\xbc\xf0\x3f\x8a\x51\xad\xee\x6d\xf7\xd0\xcb\xb5\x97\x6b\x2f\xea\x88\xd1\x06\xda\xc7\xd9\x38\x1e\xa2\x86\x8f\x5b\x41\x68\xef\xdb\x72\xab\xcd\xe1\x90\xfa\x0f\xea\x0b\xa2\x00\x17\xe0\xab\x97\xd4\xa6\x7f\x7c\xd1\x6a\x0d\xfc\x1f\x9c\xc4\x90\x3b\x2c\x1b\x63\x94\xe0\x54\xe1\x8b\x5a\x47\x48\x39\xd6\x63\xf2\x6c\xe1\x7d\x2b\x5e\xc0\x16\xe2\x1f\x0c\x07\x7d\x35\x7a\x9b\x07\xd0\x14\x9e\x7b\x61\xc7\x11\x46\xd3\x38\xc1\x54\x78\x7c\xf1\x02\xfa\xe6\x1b\x45\xbe\xde\x5f\xbc\x28\xb8\xc0\x61\x3e\x97\x59\xe0\x6b\x77\x8b\x72\xfe\xb8\xc0\xbf\xd9\x29\x0e\x45\x71\x3c\x2b\x26\x86\x7c\xe0\xe4\xe8\x5d\xd9\x82\xd8\xfd\x6b\x42\x16\xc9\xa3\x39\xd1\xd4\x52\x44\x77\xb7\x70\xb3\x8f\x44\xf7\xad\x88\xee\xff\x28\xcc\x2f\x9f\xe4\x14\x1e\xf8\x17\x0a\xbf\x85\x0f\xce\xea\xf9\xd6\x12\x80\x4b\xa5\x7c\x11\xb8\x8c\xbe\x7e\x35\x5f\xdd\x6a\x8b\x71\xf7\x78\x71\x5c\x81\xd5\x55\xf4\x91\xc0\xd7\xeb\x85\x56\xa4\x00\xd0\x2c\x88\x32\x97\xe3\x70\x82\x51\xe9\x87\x92\xf4\xb5\x96\x31\xb8\xc1\xe3\xd0\x8a\xb9\x2d\x4c\x38\x2d\x45\x66\x28\xb6\x24\xa4\xab\x28\x4d\xc7\x6e\x88\xc7\x5b\x64\xf7\x52\x28\x68\x29\x5e\xf2\xf7\x76\xdc\x72\xe4\xe8\xa2\x49\xb2\x1e\x96\xaf\xc8\x4c\x48\xd0\xda\x5f\x9f\xe7\xe3\x61\x93\x84\x17\x8b\x89\x6d\xc5\xbc\x16\x5f\x8e\x77\x37\xeb\x32\xd6\x33\x79\x52\x3e\xda\x89\xc0\x5d\x0e\xa2\x87\x41\x9a\x92\x85\xfc\x82\xa0\x36\x44\xef\xf0\x35\xda\xc2\x49\x78\x41\x73\x42\xee\xf0\x41\x69\xe4\xc7\x9c\x3e\x7c\xf3\x6e\x6b\xa7\x21\x5b\x13\xcf\x05\x13\x8f\xf7\xe2\x68\x14\x9e\xcf\x59\x26\xca\x18\xb2\x42\xa6\x79\xf9\x25\x93\x78\x86\x93\xec\x1a\xfd\x49\x8f\xc5\xe0\x4d\x0a\xcc\xf7\x64\x4c\x73\x1c\xa7\xe4\x21\x8c\x58\xba\x80\x2c\x16\xbe\x34\x55\xb4\x85\x47\xc1\x7c\x92\x75\x51\x0b\x95\xea\x8d\x75\x48\xa4\x5c\xf6\xc1\xf7\x24\x34\xc7\x09\x4f\x64\x2e\xc1\x91\xf1\x5f\x84\x66\x98\xb1\xe4\x99\x29\x80\x92\x87\x7a\xe5\x43\x16\xa3\x19\x4e\x46\x71\x32\x55\x80\x6b\x90\x95\xf4\x8f\x83\xd1\x79\xd7\x37\xca\x88\x5e\x7c\x1d\x43\xcc\x99\x7a\x63\x7d\xb5\xd9\x30\x42\x70\xd3\xae\x50\xd4\x8d\x4f\x12\x21\xad\xf1\x9b\x72\x5e\x42\xd2\xbc\x04\xf2\x64\x56\x86\x92\xb4\xf8\x7a\x5b\x9c\x45\xf4\x00\xf8\xdc\x0d\xe9\xaa\x9a\x31\x94\x8c\xdf\xc0\x45\x37\xdc\xdf\x6c\x14\x27\x70\x8a\x91\x8d\xde\x43\x62\xd0\x2f\xc3\x91\x95\x34\x9e\x52\x3b\x3f\x3d\x6a\x66\x58\xcb\x54\xfc\x53\x4e\xd6\x3a\x4d\x3f\x79\x67\x30\x15\x7d\x1a\x6b\xb5\x9a\x09\x38\x27\x7b\xfd\x60\x74\xee\x36\xbc\x20\x13\xb1\x21\x7e\x72\xc2\x23\xc5\x7d\xc1\x30\xec\xf5\x0e\xd7\x15\xd4\x83\xae\x28\x0b\xba\x4d\xbe\xd9\x19\x83\x0d\xd4\xc2\x1f\xaa\x05\x2b\xa7\xc1\x24\x43\x9b\xf0\xcf\xf2\x89\x68\xb9\x1b\x8d\xe2\xd7\x7e\x17\xb2\xa3\x89\xd4\x87\xa3\x2a\x8b\x4a\x52\xe2\x9d\xa9\x00\x7e\xde\x49\x65\xc5\xd5\x79\x35\x6a\x2e\x95\xdb\x45\x9f\x7a\xa7\x01\x61\x98\x79\x92\xc2\x32\x2f\x7b\xf0\xdd\x67\xb4\x4a\xc8\x87\xf2\xa0\x8a\x98\x1d\xb7\x59\xa2\x3f\x41\x39\xc8\xa6\x74\xb0\x69\xba\x79\x4b\x9f\xe3\x0a\xf5\x04\x72\xf2\x5e\x34\xc4\x57\xae\x1a\xa7\xb5\x2b\xa6\x00\x72\x44\xeb\x5c\x10\xa2\x4b\xa0\x22\x84\x65\xf1\xc6\x9b\xbf\x5e\x62\xc3\x2b\xc9\x37\xde\x4a\x7c\xcb\xdb\x20\xb3\x52\x65\x4f\x2e\x23\x0c\xb9\xb5\xd0\xa2\xf2\xc5\x02\x23\x0b\xfd\x23\x13\xd4\x8d\x0e\xf2\xb8\x48\xaf\x39\x3e\x4e\xe3\x02\xd1\x49\x96\xe7\x98\x27\xcb\x06\x0a\x94\x69\x7c\x65\xaf\xcd\x39\x43\x2c\xa3\xb7\x4c\x0d\x6c\x7f\x5f\x9c\x8d\x01\xe0\x6b\x43\xec\x1c\x5d\xbb\xb8\xc8\x62\x24\x5f\xb1\x8e\x7b\x10\xd9\x13\x63\xec\x06\x1d\xaa\xd1\xec\x18\x58\x07\x16\x9a\x2d\x47\x9d\xda\x72\x28\xd3\xe7\x35\xe6\x40\xc0\xcf\xb5\x26\x60\xf4\xc4\x48\xab\x1f\x5d\x63\x5d\x64\xbc\xd1\xa2\x50\x50\xae\xce\xf2\xd1\x57\xdf\xb9\x03\x56\x29\x4d\xfc\x76\x70\xa4\x77\x07\x5c\xa7\x1c\x1e\xd7\xd6\xb8\x7d\xa6\x36\x30\x9f\xb9\x0d\x8c\x32\x9b\xaf\xd0\xe7\x9c\xd1\x23\x7f\xb2\xc6\xe9\x67\x30\x87\xb1\x3a\x72\xfa\xd9\x34\x8b\xe1\x7f\x37\xf6\x6b\x33\xe0\x14\xf9\x53\x98\x03\xd3\x4d\x43\xa3\xae\x29\x31\x98\xc4\x69\xed\xec\xf9\xf3\x7c\x93\x22\x05\xb8\x72\xf4\xe5\x7c\xc3\x11\xc4\x8c\xed\x65\xb2\x5e\x9e\x01\xa5\x7a\x8c\xb8\xd3\x86\x5e\x24\xd8\x4c\xee\x46\xbe\xe4\x26\x7e\x5f\xa2\x65\x98\xba\xd2\xed\x2f\x8e\x5e\xe3\x10\x0d\xee\x21\x88\x0d\x15\x11\x84\x64\x48\x85\x42\x9f\x98\xb0\x5c\xb5\x0a\xf2\xc8\xa6\x77\x01\x93\x2b\x9b\xca\x20\x3b\xe2\x28\xe9\x13\x60\x2a\xc8\x14\x54\xd9\xb0\xeb\x62\x31\x29\xb4\x40\x78\xba\xc9\xb3\x45\xa3\xd0\xdc\x81\x7a\xcc\x14\xba\x3c\x27\xec\xcd\x59\x65\xed\xef\xed\x43\xbf\x44\x5a\xf7\xc5\xc9\xd1\x1f\x56\x77\xe4\x4d\xaf\xed\xcb\x7a\xfd\x4f\xd0\x2e\x1d\x83\x71\x66\x8f\x1b\xef\x52\x25\x92\xfa\x32\x4f\x8f\x24\xf0\x38\xc2\xf3\x34\xe8\x4f\x30\x0b\x07\xa6\xa0\x73\x8c\xd4\x54\x8b\x14\x8a\xf9\xe6\x2d\xd2\x33\xac\x29\xdb\xc2\x11\x64\x53\x46\xcc\xd0\x96\xd9\x18\xdb\x9a\x24\x51\x1e\x62\xac\x84\x29\x0a\x10\x4d\xc0\x8c\x2e\x70\x92\x42\xd4\xb2\x71\x90\xa1\x08\x9f\x4f\xf0\x20\xc3\x43\xc2\x86\x07\x2c\xa5\x6a\xc6\x14\x3e\x59\x8c\x26\x61\x96\x4d\xf0\x0b\x1a\xe0\xb2\xaa\x03\xc5\x49\x12\x27\x68\x18\xe3\x34\x5a\xc9\x50\x30\x1a\xe1\x01\xad\x4b\x91\x5a\x49\x51\x8a\x07\xf3\x24\xcc\xae\x2b\xa2\x62\x7f\x9e\xa1\x30\x83\x4a\xbc\x46\x98\xa5\x22\xa0\x42\x38\x09\x33\xe6\xc4\x4d\xf3\xba\x86\x84\x3f\x4f\x71\x44\xf7\x83\xd4\xa5\x28\xa3\x03\xf2\x9e\x76\x4e\xa8\xcb\x8c\xb7\xea\xfc\xdd\x36\x69\x5b\xfe\x21\xe5\x9d\x6a\x06\xed\x3d\x60\x48\xeb\x6d\x38\x35\x5c\xe4\x9d\x16\x42\x76\x42\x23\xbb\x17\xf6\x9e\xd3\x7e\x13\xed\x92\x5f\x8e\xc4\x71\xef\x4e\x6b\x67\x15\x54\x7a\x77\xda\x3c\x63\xc1\x02\xd0\x57\xf2\xc8\xae\x02\xea\x9d\xb2\x23\x89\xdc\xbb\xd3\x3a\xad\x54\xd3\x2b\x35\xf3\x2b\x35\x68\xa5\xba\x5e\xa9\x96\x5f\xa9\x49\x2b\x35\xf4\x4a\x75\x51\x49\xaf\xe3\xca\x8e\x64\x0d\x19\xf7\x32\xf4\x0d\x5a\x4f\x0c\x5a\xcf\x3d\x68\x36\x3e\xca\x70\xb1\x3e\xd1\x0b\x93\xd1\x88\xa7\x1d\xa4\x48\xd3\x20\xab\xb5\x1a\xf9\xe2\xea\xaf\x3d\x11\x4d\x1d\x72\xdd\x09\xb9\x51\x08\x72\xcd\x3b\xf0\x0a\x0c\x03\x72\xb3\x10\xe4\xba\x6f\x76\x2a\x0a\x0c\x03\x72\xcd\x80\xbc\x78\x22\x7b\x41\x92\x5c\xa3\xbe\x99\x4e\x95\x4e\x55\x9f\xc6\xbf\xb0\x35\x19\x19\x9d\x7c\xc2\x7a\xd2\xeb\x34\xc3\x53\x34\x8a\xe7\x09\xca\xc2\xa9\x39\xf7\x4b\x06\xe5\x8d\xf0\x55\x76\x4c\x56\x9f\x3f\x7e\xac\x23\xe2\xed\x7e\x3c\x0c\x47\xd7\x94\x13\x52\x3a\x2c\x80\xc5\xba\x1f\x8b\xde\x29\x75\x1c\xf8\xed\x14\x52\x5e\x42\xb4\x15\x2b\x53\x9c\x2b\x49\xee\x2f\x28\xc5\xd9\x7c\xa6\x7f\xc8\xf1\xe8\x58\x7c\xd8\xdf\xfb\x85\xba\x76\xe4\x9d\xf0\xf7\x7e\xf9\x54\x43\x1b\x68\xef\x17\x3b\x35\x9a\x52\xa4\x4e\x8b\xd4\x9d\xd1\x8c\xd5\x25\x0d\x53\x99\xce\xfb\x17\x98\x88\x0a\xbe\xa3\x7f\x8d\x06\x3f\x86\xb6\x69\xf4\xe3\xaf\x88\x3e\xf9\xa2\x1f\xab\xc5\x59\x98\x63\x51\x5e\x5e\x87\xba\xc3\x1c\x8b\x66\x1b\xa2\xd9\xba\xd6\x6c\x7d\x51\xb3\x75\xbd\xd9\xfa\x72\xcd\x42\x18\x9d\xb0\xc6\x97\x20\x01\x12\x36\xf4\x15\xe8\xab\xda\x84\xaa\x0d\xbe\x98\xa1\x6a\x4d\x5f\xa6\x9e\x19\x61\x64\x9d\xc7\x5a\x11\x50\x6b\x8d\x9e\xeb\xcd\xd8\xfe\xf4\x63\x9d\x7e\xac\x3b\x3f\x36\xe8\xc7\x86\xf3\x63\x93\x7e\x6c\x3a\x3f\xb6\xf2\xda\x6c\xe7\xb5\xd9\xc9\x6b\x73\x4d\xb4\x99\xa3\x91\x2a\xc4\x79\xd0\xf2\xdc\x07\x15\xe3\x40\xc8\x56\x52\xa8\x7e\x44\xf7\x92\xdc\xd5\xab\xbc\x56\xa4\x8f\x42\x9c\x59\x2f\xe2\xee\x9d\x7f\x7b\x87\xc1\x95\x5e\x66\xc0\x85\xf4\xd2\xc7\x34\xd4\xd0\x6f\x40\x84\xa8\xf4\x1b\x99\x7b\xbe\x4a\xe0\x59\xec\xbd\xaf\xcc\x8a\x75\x5a\xb1\xc1\x2a\xae\x19\x15\xdb\xde\x8a\x0d\x5a\xb1\xc5\x2a\xd6\x8d\x8a\x6b\xde\x8a\x4d\x5a\xb1\x73\x26\x50\xd3\x2a\xd6\x65\xc5\x3b\xed\x62\x79\x51\xea\x29\x22\x3c\x76\xfc\x31\x4b\xc9\xce\x82\xc7\xc3\xe3\x6d\xa2\xc7\x73\x38\x8c\xc1\x09\x38\xae\xf8\xf1\x4e\x7c\x9d\x4e\x78\x48\xc9\xd1\x2b\xbc\xe9\x8e\xf3\xbd\xe8\x54\xea\x17\x76\x3c\xf2\xe6\x56\x7e\x0c\x2f\xe8\x97\x4e\x6b\xb5\xd9\x30\xd5\x72\x62\x99\x08\x82\x2d\x15\x74\x85\xd2\xd6\x87\xf6\x45\x11\x41\x0d\x83\x9f\xe3\xe0\x02\xa3\x78\x32\xf4\xb2\xda\x25\xe4\x87\xde\x27\x3a\xb9\x3d\x33\xde\xa1\xd6\x62\x2f\x98\x0c\xe6\x13\xb2\xc2\x22\x7c\xe9\x6d\xb6\xc7\x12\xc1\xf4\x68\x22\x98\xda\x55\x6b\xd8\x84\xff\x43\xcf\xb9\x84\x66\xe6\x6b\xe9\xb1\xbc\x30\x3d\x9a\x17\xa6\x76\xc5\x6a\x34\x21\xa6\x7c\x8f\x0b\xa8\xb5\x32\x7a\x8d\x4a\xbd\x4f\xca\xf3\x7f\xa0\x3a\xea\xa2\x5a\xd9\x86\xd8\x60\x10\x1b\x14\x22\x03\xd8\x62\x10\xeb\x06\xc4\x7a\x01\x88\x4d\x06\xb1\x69\x75\xab\x44\xdb\xd1\x20\x36\x0a\x40\x6c\x31\x88\x2d\x67\xaf\x9b\x06\xc4\x66\x01\x88\x6d\x06\xb1\xed\xec\x75\xcb\x80\xd8\x2a\x00\xb1\xc3\x20\x76\x9c\xbd\x6e\x1b\x10\xdb\x05\x20\xae\x31\x88\x6b\xce\x5e\x77\x0c\x88\x9d\x85\x10\xa5\xd8\x4f\x81\x6a\xd5\xd7\xcc\xea\xa6\x77\x8c\xa0\x69\xb2\xfb\x9c\xbf\xb8\xc3\x22\x22\xa5\xce\xaf\x80\x57\x87\xa4\x6b\x3d\x47\x12\x0e\x9e\x2e\x3f\x99\x0f\x32\x34\x0e\xcf\xc7\x28\x88\x86\x68\x12\x5f\xa2\x20\x39\x9f\x43\xf8\x17\x70\x73\xfe\xef\x79\x90\x58\x89\x7b\xa0\x81\x00\x6d\x90\x56\xb8\x14\xe7\x50\x1e\x9c\xf7\x69\x11\xba\x4b\x38\x8f\x4f\xbc\xcf\x1a\x06\x09\x4e\xe7\x93\x0c\xc5\xa3\xbc\xe6\xc7\x74\x0b\x28\x9d\x07\xe8\x27\x74\x1e\x50\xd7\x95\xfa\x5a\x19\x3d\x47\xf4\x55\x9f\xbd\x6a\xc3\xab\x3e\xbc\x72\x21\x39\xa1\x80\x94\xae\xd0\x23\xe1\x4f\xe8\xfc\x0a\x66\xb8\x0c\x04\xc1\x0b\x08\xb1\x53\x29\xe0\x4a\x04\x43\x3a\xf4\xdb\xc1\x11\x82\x70\x92\xea\xc7\xb7\x94\xc3\x9d\x8f\xd1\xef\xe8\x7c\x52\x94\xc9\xb9\x95\x2a\xbf\x31\x16\xf7\x96\xb2\xb8\x52\xe9\xad\xdc\xbe\xc9\x4e\xf6\x56\x11\x0b\xca\xac\x40\x47\x2f\xd0\x91\x05\x4c\x7a\xfe\x8d\x71\xc3\xb7\x94\x1b\x96\x68\x33\x72\xbf\x7d\xcb\xf9\x1f\xec\xb7\xcf\x11\x69\xcd\x86\xd1\x60\x30\x1a\x1c\x46\x5d\x47\xa0\x6e\x61\x58\xd3\x0b\xd4\xf2\x30\x6c\x32\xe8\x4d\x0e\xbd\xa1\x63\xd8\x30\x30\xac\x3b\x30\x6c\x31\x18\x2d\x0e\xa3\xa9\x23\xd0\xb4\x30\x6c\xe8\x05\x1a\x79\x18\xb6\x19\xf4\x36\x87\xde\xd2\x31\x6c\x19\x18\x36\x1d\x18\x76\x18\x8c\x0e\x87\xd1\xd6\x11\x68\x5b\x18\xb6\xf4\x02\xad\x3c\x0c\xd7\x18\xf4\xb5\x33\x8d\x44\x04\x86\x1d\x03\xc3\xb6\x86\x61\xa1\xc4\x1f\x29\x4f\x3a\x21\x74\xad\x05\xd2\x4e\x2c\xba\xee\xa2\xb0\x32\x7c\x95\xa9\xf7\x4e\xaa\x26\x95\x87\x52\xd0\xd2\x38\xd0\xdb\x22\xfb\xfe\x6a\x36\x09\x08\x36\x57\x19\xf2\x82\x63\x71\x66\x4a\xb2\x65\x17\x44\x71\x71\x95\xa7\xd4\xd5\x93\x77\xa8\x25\xcb\x79\x77\x50\x6a\xc1\xc2\xc6\xc8\x15\xfd\x6e\xa4\xdb\x6e\x55\xe4\xa5\x48\xb7\xdd\xa9\xb0\xbb\x92\x6e\xa7\x7e\x73\x56\x59\xfb\x7b\x47\x22\x7c\xbc\xaf\x7a\xbc\xaf\x7a\xb0\xfb\x2a\x63\x89\xcb\xfb\x1c\xf3\x26\xe7\xef\x75\x87\x73\x5f\x59\xe1\xde\x89\xa3\xf9\x3b\xfd\x68\xfe\xee\xb6\x47\xf3\x77\xfa\xd1\xfc\x5d\xde\xd1\x7c\x91\x82\xf9\xf1\xa6\xea\xf1\xa6\xea\xf1\xa6\x4a\xfb\xf2\x78\x53\xf5\x78\x53\xf5\x78\x53\x25\x9b\x7d\xbc\xa9\x32\x3f\x3e\xde\x54\x79\x1e\x1f\x6f\xaa\x1e\x6f\xaa\x1e\x6f\xaa\xe0\xef\xf1\xa6\xaa\x98\x12\xf7\xf1\xa6\xea\xf1\xa6\xea\xf1\xa6\x4a\xf9\x7b\xbc\xa9\x7a\xbc\xa9\x7a\xbc\xa9\x7a\xbc\xa9\xfa\x9f\x7c\x53\x75\x6f\x77\x54\xb7\xbb\x9d\x2a\x72\x2f\x55\xe0\x46\xea\xa1\xee\xa2\xfe\xde\xf9\x50\x1e\xef\xa2\xfe\xf9\x77\x51\xea\xdd\x51\xaf\xb5\xd0\xd1\x49\xbd\x39\xea\xb5\x94\x6b\x23\x78\x78\xf8\x3b\x23\xea\xa5\x29\x6e\x8d\xdc\x41\x05\xb8\x87\x76\xde\xb5\x12\xb8\x71\xaa\x1e\xc5\x4a\xcc\x74\x5b\x5f\x11\x85\x19\x4a\xfb\xf1\x95\x0d\xe7\x58\xa0\x73\xac\x5e\xd3\xf1\x3f\x97\x34\xd9\x68\x77\xfc\x87\x72\x76\xe8\x0e\x17\xab\x71\xdf\xe1\x6b\x97\x1e\x57\x6f\xb1\xc2\xfd\xc7\x17\x36\xcc\x06\x85\x0c\x01\x8f\x2a\x11\xa2\x7f\xa9\xe3\xe4\x51\x1d\xb2\x4a\x64\x6b\xe3\x63\x7f\xaa\x01\xb2\x23\xa1\x69\x9f\xad\xa0\x68\xae\xb3\x3f\xe9\x45\xe9\x33\x7a\x4e\xc7\xe7\x39\x6f\xb4\x8c\xfe\x05\xbd\xf2\xc4\x52\xb8\x0c\x66\x6e\x9c\x61\xdf\xb0\x35\x04\xca\x04\x1c\xbb\x1d\xe3\xc9\x6b\x32\xe3\x8b\xa7\xa7\xe7\x54\xf1\xb3\xac\x1a\x82\x68\x3e\xb3\x2c\xb3\x02\xd0\x9d\xd5\x72\x5c\x13\x02\x5a\x10\x2b\xff\x3a\x99\x1e\xb7\xca\x50\x6b\x59\x38\x39\x37\xda\x1d\x8f\x42\xa4\xe6\x55\x86\x38\x1b\x2d\xaa\x18\x51\xd6\x93\xa1\x18\x91\x83\x16\x1a\x5f\x3e\xcb\xe1\x5c\x98\x01\x1e\x94\x83\x7a\xf5\x2f\x2a\x9e\xc6\x7c\x88\xd5\x14\xd1\x65\x14\x51\x95\x5a\xe4\x58\x44\x21\x68\xd0\x69\xc2\x38\x46\x95\xda\x77\x8d\x84\x3d\x84\xeb\x24\xda\x1c\x82\xf5\x13\xab\x24\x54\xfd\xbd\xde\xd9\xaf\xa4\x6e\x89\xad\x29\x52\x85\xe1\x75\x26\xf3\x1a\x44\x66\x1e\x03\xe3\xf8\xf4\x11\xe2\xa0\x38\x6e\xb4\x24\xa9\x87\xd6\xd9\x9d\x8c\x85\x36\x57\x4c\x2c\xd3\xb0\xfb\x5e\xe5\xde\x5e\xeb\x3e\x84\xde\x5e\x6b\x69\x89\xd7\xde\x63\x0d\x71\xb7\xd7\x72\xc6\xb6\x80\x1b\x9a\x10\x0f\x6f\xb1\xc3\x6f\x25\xf1\x4c\xdb\xe5\xd9\x0b\x18\x84\x6f\x10\x15\x6f\x48\x9a\xd3\x03\xcd\x19\x7a\x7e\x32\xf1\xa4\x94\x08\x35\x87\xea\x2f\x1b\x2a\x58\x33\xd6\x1c\x41\x5d\x89\xfa\x65\xac\x62\x02\xaa\xab\x83\xd0\x23\xc6\x15\x12\x62\x48\x1b\xbc\x60\xfe\x1d\x06\x19\xcf\x9c\x0d\x5c\x18\xbe\x10\xbc\xc8\x2e\xfe\x33\x6c\xe6\x2f\x5e\x38\xf7\xf0\x25\xd8\x3d\x5a\x90\x00\xe9\x3b\x5a\x6d\x64\x88\xee\x67\xc5\x01\xa4\xe5\x57\x1d\xa3\xf9\xfc\x95\x47\x0a\xe5\x9f\x34\x7b\xad\x87\x3a\x66\xde\x2d\x5d\xdf\xb7\x3c\x5f\x3e\xd8\x29\xf0\xdb\x06\x71\x26\xac\x0a\xa7\x38\xb9\xc0\x4f\x9f\x94\x06\x65\xd4\xa8\xd5\x1b\xa8\x7f\x8d\x7a\xff\xdf\xff\x3b\x4c\xc2\x01\xda\xc7\x69\x14\x4e\xaa\x68\x73\x32\x41\x49\x78\x3e\xce\x52\xc4\xca\x0f\xab\x4f\x9f\x3e\x39\xc2\xc3\x30\xcd\x92\xb0\x3f\x07\xf8\x41\x34\x84\xa0\x3c\x61\x84\xd2\x78\x9e\x0c\x30\xbc\xe9\x87\x51\x90\x5c\x13\x76\x30\x4d\x2b\x2c\x4a\x43\x02\xff\xc6\xf3\x0c\x4d\x81\xa7\x0f\x80\xb3\x56\x50\x90\x60\x34\xc3\xc9\x34\xcc\x32\x3c\x44\xb3\x24\xbe\x08\x87\x78\x48\x83\x4e\x90\x75\x3a\x8a\x27\x93\xf8\x32\x8c\xce\xd1\x20\x8e\x86\x21\x5d\xc3\xa4\xd2\x14\x67\x5d\xb6\xe2\x5f\x20\x1d\xad\x14\x14\xc3\x14\x9f\x41\x3c\xc4\x68\x3a\x4f\x33\xb2\x51\x07\x61\x04\x40\x83\x7e\x7c\x41\x3e\xcd\xae\xa1\x8b\x28\x8a\xb3\x70\x80\x2b\x34\xae\xd0\x24\x4c\x41\xb3\xac\xb6\x17\x0d\x0d\x64\x86\x61\x3a\x98\x04\xe1\x14\x27\x55\x1f\x0e\x61\xa4\x0e\x04\xc7\x61\x96\xc4\xc3\xf9\x00\xdf\x3b\x1a\x88\x75\x6d\x18\x0f\xe6\x22\x0e\x06\xa9\xb1\x1a\x27\x2c\x46\xc6\x34\xc8\x70\x12\x06\x93\x54\x0e\x33\xcc\x0d\x54\x53\x50\x27\xf3\x7c\xb2\xbb\x77\x8c\x8e\x0f\x76\x4e\x7e\xdd\x3c\xda\x46\x7b\xc7\xe8\xf0\xe8\xe0\x97\xbd\xad\xed\x2d\xf4\xe6\xdf\xe8\x64\x77\x1b\xf5\x0e\x0e\xff\x7d\xb4\xf7\x76\xf7\x04\xed\x1e\xbc\xdf\xda\x3e\x3a\x46\x9b\x1f\xb6\x50\xef\xe0\xc3\xc9\xd1\xde\x9b\x8f\x27\x07\x47\xc7\xe8\xd9\xe6\x31\xda\x3b\x7e\x06\x1f\x36\x3f\xfc\x1b\x6d\xff\x76\x78\xb4\x7d\x7c\x8c\x0e\x8e\xd0\xde\xfe\xe1\xfb\xbd\xed\x2d\xf4\xeb\xe6\xd1\xd1\xe6\x87\x93\xbd\xed\xe3\x0a\xda\xfb\xd0\x7b\xff\x71\x6b\xef\xc3\xdb\x0a\x7a\xf3\xf1\x04\x7d\x38\x38\x41\xef\xf7\xf6\xf7\x4e\xb6\xb7\xd0\xc9\x41\x05\x1a\xb5\xab\xa1\x83\x1d\xb4\xbf\x7d\xd4\xdb\xdd\xfc\x70\xb2\xf9\x66\xef\xfd\xde\xc9\xbf\xa1\xbd\x9d\xbd\x93\x0f\xa4\xad\x9d\x83\x23\xb4\x89\x0e\x37\x8f\x4e\xf6\x7a\x1f\xdf\x6f\x1e\xa1\xc3\x8f\x47\x87\x07\xc7\xdb\x88\x74\x6b\x6b\xef\xb8\xf7\x7e\x73\x6f\x7f\x7b\xab\x8a\xf6\x3e\xa0\x0f\x07\x68\xfb\x97\xed\x0f\x27\xe8\x78\x77\xf3\xfd\x7b\x67\x2f\x09\xee\x5a\x1f\xdf\x6c\xa3\xf7\x7b\x9b\x6f\xde\x6f\xd3\x96\x3e\xfc\x1b\x6d\xed\x1d\x6d\xf7\x4e\x48\x77\xe4\xaf\xde\xde\xd6\xf6\x87\x93\xcd\xf7\x15\x74\x7c\xb8\xdd\xdb\x23\x3f\xb6\x7f\xdb\xde\x3f\x7c\xbf\x79\xf4\xef\x0a\x83\x79\xbc\xfd\xbf\x3f\x6e\x7f\x38\xd9\xdb\x7c\x8f\xb6\x36\xf7\x37\xdf\x6e\x1f\xa3\xd2\x82\x21\x39\x3c\x3a\xe8\x7d\x3c\xda\xde\x27\x38\x1f\xec\xa0\xe3\x8f\x6f\x8e\x4f\xf6\x4e\x3e\x9e\x6c\xa3\xb7\x07\x07\x5b\x30\xd0\xc7\xdb\x47\xbf\xec\xf5\xb6\x8f\x5f\xa1\xf7\x07\xc7\x30\x5a\x1f\x8f\xb7\x2b\x68\x6b\xf3\x64\x13\x1a\x3e\x3c\x3a\xd8\xd9\x3b\x39\x7e\x45\x7e\xbf\xf9\x78\xbc\x07\x83\xb6\xf7\xe1\x64\xfb\xe8\xe8\xe3\xe1\xc9\xde\xc1\x87\x32\xda\x3d\xf8\x75\xfb\x97\xed\x23\xd4\xdb\xfc\x78\xbc\xbd\x05\xa3\x7b\xf0\x01\xba\x7a\xb2\xbb\x7d\x70\xf4\x6f\x02\x94\x8c\x01\x0c\x7e\x05\xfd\xba\xbb\x7d\xb2\xbb\x7d\x44\x06\x14\x46\x6a\x93\x0c\xc1\xf1\xc9\xd1\x5e\xef\x44\x2d\x76\x70\x84\x4e\x0e\x8e\x4e\x94\x3e\xa2\x0f\xdb\x6f\xdf\xef\xbd\xdd\xfe\xd0\xdb\x26\x5f\x0f\x08\x94\x5f\xf7\x8e\xb7\xcb\x68\xf3\x68\xef\x98\x14\xd8\xa3\xcd\xfe\xba\xf9\x6f\x74\xf0\x11\xba\x4c\xe6\xe8\xe3\xf1\x36\xfd\xa9\x50\x6c\x05\x66\x12\xed\xed\xa0\xcd\xad\x5f\xf6\x08\xda\xac\xf0\xe1\xc1\xf1\xf1\x1e\xa3\x13\x18\xb2\xde\x2e\x1b\xee\xea\xd3\x27\x3f\xad\xea\x3a\xaf\xfd\x20\x1b\xdf\xaf\xde\xab\x58\xd4\x69\x1a\xf8\x58\x14\xa1\x8f\x85\xac\xb3\xe1\xc2\x2e\x88\xb2\x14\x65\x41\x9f\x4b\x2c\xa4\xca\xa7\x3f\x26\xce\x60\x9b\x52\x8e\xaa\x55\x10\xaa\x57\x10\x6a\x54\x10\x6a\x56\x10\x6a\x55\x10\x6a\x57\x10\xea\x54\x10\x5a\xab\x20\xb4\x5e\x41\xe8\x65\x05\xd5\x6b\x15\x54\xaf\x57\x50\xbd\x51\x41\xf5\x66\x05\xd5\x5b\x15\x54\x6f\x2b\x16\x96\x6b\xb4\x2e\xf9\x46\xe0\x91\xf2\x04\x46\xbd\x4d\xe1\x92\x7a\xd0\xd6\x4b\x06\xbf\xc1\x60\xd4\xa1\x0d\x09\xa7\xc9\xda\x6a\x31\x5c\x5e\x32\x18\xeb\x0a\x9e\x6b\x0c\x56\x87\xe1\x52\xa7\x30\xeb\x6a\xac\xe5\x3a\xab\xcb\x71\xa9\x51\x18\x80\x07\xc7\xb3\x49\x61\x11\xf8\x75\xb5\xdf\x2a\x9c\x16\xab\xdb\x66\xb8\xaf\x31\x18\x0d\x05\xcf\x3a\x83\xb5\xce\x70\x61\xfd\xae\x37\xcf\xca\xaf\xd4\xb9\x48\x16\xcc\x05\xc7\x63\x4d\x19\xab\x06\x83\xc9\x71\xee\xe8\xe3\x01\x7d\x6b\x1a\x7d\xef\xb0\x3a\x4d\x09\x0b\xea\xb6\x25\xce\x1c\x06\x1f\x0f\x68\xab\x6e\xf4\x1d\x0a\xb5\x95\x0e\xae\x31\x04\x3b\x72\x70\x05\x90\x86\x32\xd0\x14\x59\x09\x68\x9d\xd5\x51\x06\x0b\x26\xa6\x2d\x07\x57\xc0\x68\x2a\x03\x4d\x91\x55\x10\x6a\xb0\x91\xad\x29\xc0\xf8\x68\xac\x89\xd9\x13\x14\x8a\xd8\xe8\x50\x64\xf5\xd9\x48\x17\xad\x0c\x8a\x22\x1b\x2b\x40\x4f\x6d\x89\xd3\x56\x53\x19\xcf\x8e\xfc\xa6\xd1\xf4\x5a\x05\x3e\xc1\x50\x71\x7a\x7d\x29\x69\x8f\xd3\x54\xbd\xad\x0c\xeb\x1a\x2b\xab\xcd\x47\x5d\x12\x81\x98\x8b\x97\xac\x20\x27\x9e\x75\xa5\x0c\x47\x7c\x0d\x7e\xab\x67\x29\xb1\x96\x5b\xb2\x2a\x6f\x5f\xac\x79\x75\x4d\xac\x6b\x20\x25\x28\xbe\x3e\xdb\x92\xf6\x45\x3f\x1b\x12\x05\x31\x4e\x8c\x64\x28\x5c\x64\x4c\xc9\xa2\x05\xc2\x10\xd3\x06\xbf\x2d\x11\x80\x7e\xae\xc9\x85\x08\x0d\xb6\x18\x22\x1d\x03\xe9\xa6\x3e\xf8\xa2\xd3\x75\x09\x47\x8c\x9d\x58\xd0\xf0\x5d\x83\x23\x18\x48\x5d\x19\xa4\x8e\x6c\x57\x2c\x3c\xb6\x80\xeb\x4d\xc7\x7c\x88\x0e\x18\x88\x73\x40\x62\xc1\x35\x94\x7f\xdb\x62\x15\xeb\x03\xd4\x76\x94\x6b\xe9\x33\x23\x66\x52\x76\x0a\xd5\xeb\xe8\x4c\xcb\x92\xfd\x69\x4c\x56\x88\x63\x3e\x90\x08\xd5\x5c\xab\xa0\xda\x55\x7b\x73\xbd\xb1\xf6\xf2\xe5\x4b\xf2\xbb\xb3\xbd\xf5\x72\xfb\xcd\x66\x9d\xfc\x5e\xdf\xa9\xbf\x79\xd3\xdb\xea\x91\xdf\x9b\x2f\xdb\xcd\x9d\xad\xd6\xb6\x3e\xdf\xe3\xc4\xdb\x40\xbb\xb6\xd9\x58\x7f\xb3\xdd\x81\x06\x7a\xad\xad\xad\x7a\xa3\x05\x0d\x6c\xad\xd5\x9a\xdb\x3b\x4d\xf2\x7b\x6d\xb3\xb3\xb5\xd6\xd9\x86\x86\x39\x42\x67\x4e\x7d\xc0\xd1\xde\xe1\xf6\xfe\x56\xbd\x53\x83\xf0\xfb\x0b\x74\x48\xa2\xac\xd4\x22\x29\xaf\xe8\xae\x7c\xdb\xbb\x22\xaa\x4c\x04\x24\x3c\x41\xb0\x3b\x6b\xad\x76\xa3\x59\x83\x11\xdc\xde\xe9\x6d\x6d\xbe\x59\x87\x0e\xbe\x5c\x7f\xb3\xb9\xd5\xdb\xd9\x26\xbf\xeb\xb5\x66\xa3\xdd\x5a\x83\xc1\xe9\x35\xb7\x1a\xdb\xf5\x9d\xda\x99\x57\x35\x5e\x54\x29\xef\x54\xec\x16\xf6\x52\xaa\xe7\xdc\xd4\x2c\x36\xc7\xa7\x58\x80\xee\x55\x9a\x45\x7a\xae\x6f\xf6\x3f\x29\xa5\xf9\xe5\xc1\x27\xdb\x90\x09\xe5\xdd\xa9\x28\xf5\xd0\x06\x2a\xd9\x05\x10\x35\x00\x55\x1a\x93\x86\x0f\xca\xcb\xe5\x8c\x4a\x2d\x80\xcc\xae\xd4\x00\x68\x5b\x97\xda\xe0\x72\x54\x63\x68\x91\xad\xf3\x2e\x12\xf7\x0f\x84\x14\xbd\x57\x8e\xc0\x00\x3e\x8d\x27\xfe\x02\x09\x14\x48\xbc\x05\x40\xfc\xfc\xf4\x87\x1f\x02\xc8\x44\x9f\xfe\xf0\x43\x80\x6d\xfa\x53\xea\x87\x00\x9b\xc6\xa7\x34\x71\x47\xb4\x5e\x5d\x25\xab\xec\xcb\xff\xcf\xde\xdb\x6f\x49\x6d\x63\x8b\xe2\x7f\x87\xa7\xd0\xcc\x6f\x0d\x54\xd3\x45\xb7\x25\x7f\xc9\x40\xe7\x77\x09\x81\xd3\xb9\x81\xc0\x02\xe6\x86\xb3\x58\x90\x91\x6d\xb9\xcb\xa1\xba\xaa\x4f\x95\x9b\xae\x4e\x42\xd6\x7d\x8d\xfb\x7a\xf7\x49\xee\xd2\x96\x6c\xcb\xb6\x24\x57\x35\x4d\xce\x64\x86\x9e\x35\xa4\xaa\x24\xed\xbd\xb5\xbf\xb4\xf5\xb5\x25\x26\xcd\x1f\xd8\xaa\x14\xd1\xb1\x61\x93\x96\xcd\xa7\x28\x9d\x4f\x51\x36\x9f\xa2\x7c\x3e\x45\x7c\x6e\x40\xc4\x56\x53\x94\xae\xa6\x28\x5b\x4d\x51\xbe\x9a\x22\xbe\xea\x23\x63\x82\x14\x26\x08\x3e\x1e\x5e\x19\x49\x57\x90\x74\x1c\x0a\x71\xbf\x30\x13\x85\x99\x2c\x24\xfd\xc2\x5c\x14\xe6\xb2\xd0\xef\x17\xc2\x84\x81\xcb\xc2\xa0\x5f\xd8\x3c\x53\xcd\xba\xef\x52\xd7\x5d\xea\xef\x0a\x1a\x8f\x12\xc2\x7f\xf7\x8f\x10\x36\xda\x76\x25\xcc\x87\xcd\xd1\x7e\x6b\x53\xfb\xbf\xcc\xdf\x94\x6f\xdf\xee\xfd\x66\xba\xc4\x00\xb7\x76\xee\xe3\x68\xef\xd7\x1b\x5f\x75\x5d\xa3\xc0\x81\x0a\x3c\x49\xe7\xd3\x6c\x3e\xcd\xe7\x7b\x68\x1f\xcd\xe6\xe6\xbb\x37\x1f\x51\xb3\x20\x57\xde\xf7\x89\x5c\x6a\x33\x40\x23\x7d\x68\x03\xce\x0f\xa0\x05\xd4\x0a\xcd\xef\x43\x1b\x88\x6a\x00\x2d\x0a\xac\xd0\x82\x3e\xb4\x81\x6c\x35\x68\xbf\x1e\x1e\x2a\x88\xd4\xb3\x42\x0c\xfb\x10\x07\x0a\x81\xcc\x69\xd2\x85\x10\x2b\xa3\xb8\x44\x09\x5a\x2d\xab\xf9\xa4\x9a\xae\x85\x58\x4d\x97\x36\x40\x07\xaa\x7d\x3e\x37\x8b\x1c\x2c\x62\x60\x52\xe2\x0f\xf4\x36\x37\x95\x80\xba\x03\x5e\x61\x93\xd8\x78\x0d\x08\xec\x25\x35\xb5\x06\x33\x1b\xec\x24\x36\xa4\xb2\x15\xda\xd7\xb4\x75\x75\x75\x6d\x0d\x27\xe9\x6a\x9a\xad\xa6\xf9\x0a\x38\xbe\xfa\x34\x6d\x0d\xfa\xd0\x3e\x55\x5b\xbb\xd0\x3e\x49\x5b\x49\x1f\xda\x27\x6b\x2b\xee\x43\xbc\x66\x6d\x5d\xc1\xae\xb5\x43\x5d\x57\x16\x75\x05\x8f\xba\x32\xa9\x2b\x38\x62\x53\x09\xb8\x68\xa9\xae\x2b\xab\xba\xc2\x00\x60\x6a\x0d\x43\xc3\xf0\x84\x46\xdf\x95\x7f\xa7\x3f\xc7\x00\x31\x24\x9c\xfa\xed\x45\x98\xe2\x9f\x23\x34\x39\x96\x47\x73\x33\xe1\x99\x73\x43\x4f\x8f\xd5\x11\xde\x63\x79\xfc\x36\x17\xf5\x4c\x1c\x39\x56\xc7\x74\x8f\xe5\x41\x5a\x2e\xea\x31\x63\x3d\x5f\xd5\x83\xc3\xb2\x30\x22\xa4\xc6\x7a\x81\xaa\x07\x07\x93\x53\x51\x2f\x33\xd6\x83\x03\xcc\x1d\xb6\xf4\xc3\xda\xc7\xea\x69\x8d\x4f\x38\x9e\x95\xb3\x8a\x35\xc1\x90\xf8\x62\x18\xf8\xc7\x9f\x61\xac\x6b\x2e\xbe\x29\xab\xf5\xab\x65\x05\x1e\x4f\xc2\x5c\x7c\xcb\x2a\x26\x4f\x6d\xdd\x46\xd4\x00\x1d\xda\x3c\xe1\x45\x35\x78\xb4\x11\xea\x0f\x3a\xf3\x20\xcf\x87\xaf\x10\x23\xf5\xde\xa2\x3c\xcc\xd4\x82\x14\xd1\x64\xf8\x16\xfd\x76\x24\x1f\x16\x6e\xcf\x48\x34\x35\xfe\x86\x7c\xd2\xd7\xd6\x16\xd2\x64\x32\x69\xab\xee\x23\xe1\x1f\x04\xc8\x64\x4f\x80\x0a\x84\xdd\xe2\xc0\x12\x40\xd7\x4d\x25\x3b\xda\xe0\x59\xfb\x71\xfb\xe0\x79\x00\x4c\x05\xce\x3d\x60\x63\x81\xb3\xa9\xa3\xfa\x3b\x1d\xed\x7b\x98\xf5\x1b\x3b\x70\x38\xc6\xf0\x6c\xc7\xe1\x21\xcc\x04\x11\xbc\xee\x22\x2f\x64\x19\x0f\x4e\x9d\xc9\x99\xd7\xf0\x35\x17\xb7\x5a\x82\x75\xeb\x31\xba\x41\x71\x8e\xd1\x11\xd2\xc3\xf7\x4f\x9b\xbf\x85\x5b\x4d\xdf\xcc\x33\xb2\x63\x98\x8a\x1d\x1b\x2e\x93\x20\xd7\x1c\xec\xb8\xb9\xae\x77\xdc\x99\x5e\x1d\xef\x3c\xaf\x92\x1a\x72\xdc\x99\x53\x1d\x5b\x27\x53\xe3\x47\xe1\x5e\xc8\x9d\x70\x29\x5c\xf5\x82\x45\x0e\xcc\xee\x56\x55\x3b\xe6\x3d\x01\x75\xdc\x54\x36\x5f\x2e\xdc\x0e\x0a\x8e\x12\x88\x5a\xed\xea\x02\x7c\xb5\x1f\x83\x90\xc5\x3f\x0d\x94\x44\xb6\x1b\xea\x9a\x22\x13\x4a\x3b\xe7\xa2\xe0\xe3\x47\xb9\xfb\x8f\xf4\x13\x71\x05\x9e\x6c\xa6\xe8\x72\x8a\x7e\x31\x3d\xf3\x31\x99\x6c\xe0\x66\xe7\x25\xfc\xfb\x4b\xfb\x5a\xfb\xc7\x01\x1c\xe2\x86\x33\xd9\xec\xdd\x9c\x5c\xee\xc9\xeb\xe4\xbf\x8b\x2f\xbf\xec\xed\xed\xdd\xb3\x41\xf3\x47\xa1\x09\x40\xbf\x0b\x88\x2d\x69\x16\x58\xc1\x38\xac\x9b\x00\x01\x68\xbb\xdc\xbb\x39\xf9\x1d\x88\xb3\x43\x0c\xb7\xe1\x99\x60\xda\x6f\x2d\x28\x0b\x2c\x08\x25\x36\xd3\x85\x11\xd2\xe6\xfe\xfd\x05\x50\xb5\xf9\xfa\xeb\xaf\x27\x3e\xb9\xb3\xd0\x89\x92\x1f\x9c\xa7\x61\xea\xc3\x30\xf2\x1d\xb8\xed\x0e\xc3\x58\x5f\xfb\x51\xe7\x5b\xe0\xcc\x53\xfd\xb9\x5a\x4a\xcf\x34\x04\x63\x79\x9f\xc7\x52\xfb\xaa\x0f\xf3\x28\xcb\x68\x4f\xb2\xd4\x0b\x78\x93\x5b\x8a\xc4\x5b\x86\x53\x38\xf6\x56\x17\x35\xb5\xa6\xe3\x36\xc3\xc5\xc1\xde\x51\x9b\xba\xc2\x76\x47\x95\x6a\xe1\x1c\x3f\x7d\xf0\xf0\x0f\x10\x8d\xa3\xf9\x7b\x7e\x09\x4d\xd7\x3c\x5b\xf1\xca\xf2\x76\x92\x45\xa0\xf0\xe4\xe0\x35\x0a\x54\x3e\x64\xd8\x88\xe6\xf8\x94\x65\xad\x78\xf4\x23\x56\x06\x09\x75\x2a\x0f\xa5\x74\xca\x32\x83\xa4\xbe\xfa\x28\xf7\x81\x2d\x47\xa3\xea\x9a\xe6\xd7\x89\x3e\xbe\x9d\xc6\xf1\x97\x23\x4e\xff\x0a\x57\x56\x3e\xf7\xd6\x7d\x2f\xb1\x9a\x86\xd8\x9a\x32\xed\xe5\xf1\x83\x3b\x78\x8b\x9d\x8c\xe1\x5b\xd5\xd7\xb9\x7f\x71\x04\xb7\x4f\xdb\x2d\x8c\x72\x51\x56\x13\x43\x02\xaa\xee\x96\x06\x2f\xb2\x9c\xa5\x34\x31\xe4\x66\xf2\x36\x09\x4d\x59\x9e\x15\xbc\xb3\xc7\x61\xaa\x98\xf9\x39\xe1\xb8\xf0\xba\x65\x9f\xbe\x05\x62\x8b\xd0\xcd\xc1\xf7\x70\x05\x7d\x00\x60\x9b\xb5\x67\xf3\x72\xb1\x28\x4a\xcd\x8b\xc5\x10\x30\x9a\x97\x8a\x61\xba\x6a\x5e\x28\x16\x45\xbc\x59\x26\x1e\x50\x6a\x5d\x27\xb6\xae\x09\x5b\x66\x0b\xb0\xee\x83\xe4\x0d\x53\x4b\x2e\x98\x1f\x65\xe0\xdf\x4d\x81\xd1\xbd\x7b\x5a\xff\xd5\x0b\x4a\x66\x40\xf5\x3d\x87\x1f\xdf\x94\xe8\x0e\xf2\xdf\xa2\x77\xea\x23\x6d\x3f\xe2\x40\xfb\x1c\xd9\xde\x8e\x54\x24\x4d\x16\x70\x39\x56\xce\x2d\x61\xfa\xe0\x63\x73\x9a\x1a\xf3\x4c\x08\x96\x96\x26\x4c\x00\x09\x01\x08\x93\x33\x99\x18\x2e\xc8\x72\xb4\x0f\x88\x6c\x0b\x8d\xe8\x3e\x22\x9e\x95\x6b\xb0\x6c\x36\x99\xa4\xe8\x26\xca\x64\x9c\x2b\x3e\xe6\x00\xd9\xdb\x84\x4c\xee\xc2\x8e\x2c\xf1\xa1\xfb\x28\x18\x43\x91\xa2\x77\x28\x43\xef\x50\x2e\x21\x47\x3c\x4f\x78\xca\x4c\x49\x87\x7a\x90\xa3\x1d\x88\x97\xb4\x8b\x4f\x99\xea\xc5\x1d\xe4\x6d\x62\x8f\x07\x81\x4f\x02\x3b\xae\xc3\xdb\x0d\x3a\xea\xed\xa1\xdb\x87\x5b\xf7\x45\xc0\xf7\xc3\x24\xf7\x39\xe9\xaf\xf2\x20\x8b\x48\x85\xbd\xe4\xa6\xe5\x3e\x74\x84\x32\xd3\x12\x1f\x02\x94\xf7\xef\x23\xdf\x53\xbd\x04\xf1\x1b\xdf\x16\x45\x47\xc8\x44\x07\xdb\xee\xb6\xd6\x56\x8b\x81\x6a\x11\xad\x5e\x6c\x63\xfd\x1b\xde\xa8\xb3\x10\x08\x0b\x86\x83\xcc\x27\xa8\xb3\x08\x08\x8b\x85\x99\xb9\x8e\xaf\x2f\x14\xe6\xe6\x3a\x81\xbe\x48\xc8\xfb\x75\xbe\x2c\xf0\xfd\xb3\x2e\xf0\x89\x58\xf8\xa0\x98\x2f\x97\x2b\x7d\xcd\xed\x10\x06\x6a\xf5\xf7\x49\x48\x20\x17\x42\x0b\x79\x64\x9d\x6e\xb0\x4c\xf7\x99\x56\xe8\x76\x5c\x07\x32\x2e\xd7\xfd\x19\x57\x83\xbe\x2c\x21\x0c\x16\x03\x44\xf8\xbc\xd3\xea\x01\x34\x70\x2d\x1c\x74\x03\xf2\xee\x9a\x81\x28\xfb\xb2\x5c\x70\xad\xcb\x05\x20\x8f\x2d\x56\x0a\xcc\x62\x69\x17\x09\x94\x68\xec\xd7\xa6\x44\x05\xfb\xb2\x00\xfd\x53\x27\xd8\x58\xcf\x18\x09\xa3\xcf\x9d\x1b\x43\x61\xf9\xf7\x59\x3e\x18\x2c\x0f\xe8\x73\x78\x12\x46\x9d\x59\xbc\x76\x0b\xbb\xbf\x2a\x40\x48\xb0\xdd\xba\x80\xa8\xd8\x81\x09\xdf\x25\xf0\x3f\x74\x6d\x20\xc3\x5e\x98\xf0\x9c\x8a\x29\xbf\x1f\xc5\x59\x1e\x7a\x31\x7c\xf6\x62\x2f\xcf\x31\x7c\x2e\x62\x8f\x87\x89\x6f\x5e\x33\x28\x8a\xcc\xf3\x52\x1f\x16\x17\x22\x1a\x52\x1c\x62\xf9\x39\x28\x12\x5a\x30\x00\x90\xf2\x82\x05\x05\x0b\x76\x58\x2e\xd8\x2a\xf2\xd4\xdc\xbe\x62\x9d\xd6\xd2\x71\x8b\x16\x3c\x6a\x13\xce\xdc\x39\x1a\x06\x2f\x96\x8d\xa5\x2f\x43\xf4\xc8\x88\x4b\x48\xb0\xeb\x20\x2d\x9a\x8c\x0c\xd3\x1d\xeb\x18\x0c\xd4\x84\x98\x2f\xb1\x7f\x19\xaa\x3f\x61\xa8\x16\x52\xd9\x6e\xb0\x36\x0a\xa7\x33\x5c\x4b\x01\x39\x07\x6c\x42\xfa\x57\x9d\xb5\x7b\xcd\x6a\x38\xba\x1b\x27\x62\x00\x4f\xbe\xac\xeb\xff\xf7\x0c\xcc\x7f\xbe\x6b\x79\xdf\xc9\x47\x1c\xca\x5f\x9a\x5b\xb9\x68\xb5\x3c\x5f\xe4\x28\xeb\xde\xd7\xd3\x7a\x70\xdc\x7f\x3a\xe5\xfb\xee\x36\x40\xbd\x50\xcb\x5b\x18\xb2\xc4\x14\xc1\x20\x7d\x4b\xb9\x5c\x3f\x5f\x95\xa7\x7c\xb2\x30\x0e\x63\xeb\xff\x5a\x55\x3f\xd4\xf3\x7c\xf1\x65\xb2\xe8\xcf\x33\x9b\x85\x60\x29\x4e\x74\x84\xc8\xbd\xfa\xf3\xfd\x23\x09\xa1\xfe\xc1\xb1\x36\xfc\x97\xc9\x02\xfd\x4d\x55\xdb\xb3\xae\x17\x2a\x1b\x2d\xd8\x7c\xcd\xc7\x4f\x05\xf6\xd7\xc7\xea\xf9\xf8\xea\xbc\x3b\xc3\x35\xb0\xe5\x84\x57\x8f\x57\x0c\x3e\xb3\xf9\x37\x65\xb5\x36\x30\xa8\xd9\xc2\x5f\xa0\x3b\x68\xb2\x80\xcc\x9e\x7b\xe8\x76\x67\xf1\xa3\xbf\x92\xa5\xe1\xaa\x57\xa9\xf5\xcc\xec\xf0\x1b\x08\xa4\x97\xbf\xe7\x62\x56\xce\x39\x9a\xa8\xb2\xfb\x48\x1d\xc9\xec\x73\xb1\x95\xa6\x95\xd1\x0d\x08\x6a\xe5\xf2\xf1\x1b\x59\x09\xd2\x8e\x0e\x18\x01\xba\x70\xb6\xbc\x98\x2c\xa6\x08\xa3\x43\x44\xf6\xb6\xc8\xd8\x8e\xe0\x25\x94\x5d\xc0\xfa\x7b\xc6\xe4\xd9\x12\xc4\xfe\xfe\xc8\x52\xe8\xa2\x53\xa3\x8e\x90\x26\x2d\xcc\xab\xef\xb1\x89\xc0\x7b\xbb\x68\x7a\x18\xa1\x7f\xf6\x9d\xb6\xe3\x83\xf5\xbc\xcc\xf8\xc4\xdb\xfb\xb2\xeb\xb5\xf5\xae\xd7\xa0\xa8\x80\xa2\xd0\x54\x74\x02\x45\x83\x0d\x23\x88\x59\xa0\x28\xfe\xe4\x6d\xb4\xc8\x91\xeb\xfe\x8f\xde\x46\x3b\x61\xa7\xa7\xcc\xdb\x34\x9b\x69\x78\xc0\x94\x61\x6d\x38\x68\x3c\xa9\x5b\xde\xbf\x8f\x88\xdc\xf4\xaa\x7f\xf9\xfa\xeb\xaf\x51\xbc\xb7\x87\xd0\x3b\x33\xa4\xee\x5f\x07\x12\x0e\x06\x90\x30\xdd\xdb\xdb\x0e\x52\xb7\x9d\x6f\x74\x2f\x9d\x9e\xe0\xb6\xdf\xc6\x43\xf2\xdd\xca\x5a\xb7\xb1\x24\x56\xeb\x36\xde\xd4\xf9\xa6\xb7\x24\xb6\x0b\xc9\x1f\x42\x4a\x76\xec\x76\xdd\xce\xfc\x26\x01\x6a\x15\x47\x09\x71\x5f\xf5\x1c\x92\xfc\xaa\x1e\xee\x3b\x37\x4c\x6d\xbb\x9f\x19\xdc\x6a\x9c\x70\x74\x13\x15\x70\xd8\xed\x77\xf1\xf1\xc4\xf6\x84\xcb\x29\x83\x0c\x73\x0c\xdd\x44\x29\x54\x67\x72\x77\xf0\x1d\x52\xfb\x84\x26\xfa\x21\x58\x29\x4f\x04\xe1\xcd\x56\xab\xda\x6c\x53\x7b\xad\xf2\xe8\x9f\x2c\xc1\x89\x56\x82\xfd\x4e\x51\xa7\x91\x79\x6c\x6b\x90\xc1\x3b\x35\x13\x0e\x3a\x2e\x33\x27\x73\x68\x17\x29\x88\xb2\x04\x6b\x25\x18\xeb\x45\xb1\x3c\xd9\x2a\x8b\x48\x68\x1e\xf1\x60\x03\x59\x60\x9a\xa1\xfd\x1a\xed\xbe\x60\xea\xbe\x7c\xe8\xcd\xba\x79\x0c\x0d\x09\x3a\xaa\x19\xb3\x2f\x58\x6b\xc2\x20\x1c\xd7\x89\x01\x80\xf0\x75\xfd\x3c\xed\xe2\x4f\xb8\x47\x53\xf8\x05\xb9\x33\xe1\xb5\x04\x6c\xda\xe6\x43\x23\x5b\xa4\xfd\x6c\xeb\x68\x64\x3b\x74\x52\x09\x46\x54\xc4\x84\xeb\xdf\x65\x6b\x54\xd6\x09\x55\x1d\x48\x19\x5e\x98\xeb\x44\xaa\x0e\xa4\x04\x3f\x31\xd7\x89\x55\x1d\xb0\xf9\xd9\x97\x6d\xd8\x2f\xdb\xb0\x5f\xb6\x61\x87\xd1\xe6\x97\x6d\xd8\x7f\xca\x35\xde\x30\xda\x79\x8d\x37\x8c\x46\xd7\x78\xf5\x39\xdb\x70\x8d\x37\x8c\xbe\xac\xf1\x5e\xfb\x1a\x6f\x18\x6d\xbb\xc6\x6b\x12\x4e\x77\x8d\x17\x04\xe4\x3e\xb4\xdd\xec\x9d\x99\xb7\x66\xa9\xf7\xa7\xde\x9a\xdd\x44\xc1\x1f\xf2\x70\x41\x83\xe7\xcb\x2a\x70\x77\x15\x78\x13\xc1\x9e\xea\xc1\x26\x0a\xb4\xdf\x5f\x47\x81\xca\xd2\x0d\x35\x0e\xb4\x3c\xd1\x3b\xe5\x74\xd3\xfa\xf7\xe2\xf8\xd9\x4f\xcf\x1e\x3f\x7e\xf9\xe8\xd5\xcb\xfe\x6a\xf1\xf3\xef\x7e\xfa\xee\x87\x6f\x1f\xbd\x7e\x34\x7c\x95\xfb\xc5\xb3\xbf\xff\xf0\xed\x4f\x0f\x9f\xfd\xf0\xf2\xd5\x83\x1f\x9a\x96\x1a\x3a\xb9\xac\xfc\x70\xbb\x65\x65\xad\xc5\x6a\xb6\xac\x93\xb6\xf4\xd6\xa4\x6b\xd4\x62\x76\x8d\xa7\xe8\xd2\x96\xaa\xbc\x92\x4b\x22\x15\xba\x8f\x48\x70\x0f\x55\x86\x25\x11\xad\xcf\x6f\x36\x68\x1f\x85\xe8\x36\xba\x94\xb7\x07\xab\xfa\x92\x26\x7c\x22\x7b\xb0\x52\x89\xfe\x86\xa2\x41\x2c\x02\x61\x20\xbf\x78\x8d\x8e\xd0\x25\xfa\x1b\x0a\x4d\x51\x22\xbf\xf8\x4f\x01\x95\xa0\xdb\x48\xe0\xf1\x05\x9e\x3d\x43\xe5\x8d\x5c\x96\x7b\xdd\xfb\xf9\x52\xfe\xfc\x9f\x96\xa5\x60\x8d\x6d\x67\x25\x2a\xe1\x39\x01\x03\xd3\x1a\xce\x6c\x24\x67\x36\xf2\x82\xe6\xc6\xc0\x98\xa6\xaa\xe4\x2e\xba\x94\x55\x2f\x2d\xcb\x4a\xad\x82\x74\xd9\x78\x09\x0f\xfc\x0c\x7b\x2d\xf8\xda\xef\xfa\xc7\xd1\xbe\xf5\x76\x39\xba\xda\xf0\xe4\xf1\xcb\x17\x82\xd6\x8d\x87\x4d\xca\xa0\xbf\x3b\x61\x59\x1f\x13\xd5\x00\x45\xad\xac\x4f\xd7\x17\x3d\xdd\x32\x56\x7b\x52\x57\xb3\xb0\x50\xbd\x3c\xf1\x33\xba\x8f\xe2\x7b\xe8\x67\xc7\xca\x1c\xf4\x01\xae\xa6\x9a\xb3\xa2\xd4\xe8\xd3\xb2\x7a\xbe\x5c\x43\x1e\x57\xa1\x55\xf0\x58\xee\xcf\x7b\xe8\x0e\x32\x9d\xa6\xae\x81\xeb\x8d\xee\x23\x95\x2f\xc2\x54\x59\xfc\x0d\x3a\xf8\xee\x08\x01\x1a\x0d\x8a\x05\x57\xf7\x44\xb5\x8e\xf5\xeb\x23\x40\x6b\x3f\x5c\x3d\xc0\xfc\x54\xc3\xdc\x01\x75\xc7\x30\xef\x69\x08\xd8\x6e\x69\x49\x53\xac\x05\xdf\x54\xa0\x40\x23\x62\xa1\xf6\x93\xe8\x87\x87\xe8\xf9\xaa\x3c\x2d\xab\xf2\x03\x47\x67\xcb\xf9\xe5\x62\x79\x5a\xb2\x39\x5a\x7e\xe0\x2b\xf4\x1f\x8f\x27\x64\xef\x2e\xda\xbc\xa3\x68\x1f\x6d\xde\x45\xf0\x6f\x08\xff\x06\xc2\xcd\x98\x41\x2a\x8d\x96\xe8\xe5\xfd\x81\x77\xc8\xdb\xc4\x8e\x23\xf3\x16\xe2\x14\x84\x23\xa3\x7e\x8c\x6c\x7a\xf5\x1c\xbc\x5c\xe3\x53\xc3\x4f\x9d\x60\xac\x2f\xb3\xe9\x40\x7f\xf6\x76\xdd\x4d\x59\x83\xfd\x54\xfc\xf4\x6c\xb9\x62\xab\xcb\xce\x4b\x74\xc2\x04\x5e\xe9\x03\x91\x75\x97\xd2\xf8\xea\x8c\xd9\xfa\x5f\x19\x7b\x36\x46\x77\x6f\x6f\xc7\xdf\x6e\x67\xc7\xef\xec\xeb\xf8\xae\x5d\x9d\xeb\x7f\x4a\x60\x79\x5e\x9d\x9d\x57\x4f\x60\x6a\xdd\xa9\x8b\x20\x48\xcf\xf9\xba\x5c\xf1\x5c\x7b\x68\x20\x2d\xab\x75\x9d\x10\x5a\x36\xee\xcc\x16\xea\xc6\xcf\x16\xf3\x5a\x4c\x5a\x0e\x6e\xb6\xe2\x77\x11\x21\xc1\x14\x91\x30\x9a\x22\x9f\x06\x53\x14\x62\xd2\x6f\xac\xde\x2c\xb8\x2b\xca\xf4\xa2\xfe\xa3\x05\xf5\xa4\xd9\xfa\x6e\x81\xde\xbb\x1e\xb4\x2b\xbc\x5f\x00\x2b\xb5\xf0\x12\x62\x3d\xf7\xae\xbf\xbd\x79\x6b\xf1\xf6\x5b\xa8\x9a\xf8\x03\x38\x52\xe5\x16\xfc\xa2\x51\x3b\xd8\x84\x1b\x4b\x25\x00\x94\x34\xaf\xf5\xc2\x08\x10\x79\x1e\xba\x83\xc4\x40\xdb\xbc\x94\xa0\x73\x42\x44\x2f\x3e\xf9\x5c\x3b\x7a\x86\x85\x39\x03\xd3\x8c\x8b\x67\x75\x27\x9e\xb0\x05\xac\xfd\xf4\xba\x76\x88\x88\x69\x0d\x2d\x5d\x2f\x57\xe9\x38\xff\x7b\xe0\x3f\x25\x93\xe0\x53\x52\xa2\xee\xa6\x98\xe0\xb5\x75\xd9\xfc\x29\x81\x37\xe8\xfb\xd5\x85\xaf\x77\x25\xb3\xb0\x3e\x41\x2d\xd0\x3b\xf3\x09\x92\x4e\x22\x41\x72\x95\x0c\x82\xa4\x93\x3a\x90\x5c\x3d\x67\xa0\x22\x18\x8f\x51\x8c\xbb\x24\xe3\x2b\xd1\x8c\xbb\x44\xe3\x5d\xa8\x36\xca\x41\x2a\x57\xb3\x34\x52\x2e\xaa\xa5\xd4\x66\xb3\xa4\xe7\x0c\x16\xf3\x6a\x73\x36\xb0\x42\xd4\x38\x80\xf7\x66\xdf\x1d\x01\x5f\x6c\x75\xe6\xcb\x0b\xa4\xea\x8c\xef\x46\xbc\x10\x03\xec\xda\x62\x03\x32\x50\x06\x3b\x90\x1f\x65\xd0\x0b\x9f\xed\x26\xf0\x6a\xc6\x2b\x36\x2c\xd9\x61\xd6\xa0\x01\x7b\x5a\x8a\x29\xc8\xfc\xfc\x74\x01\x9d\x33\x98\x55\xcd\xc1\x3a\xcc\x9e\xa2\x36\x92\x36\x56\xde\x71\x4e\xa2\xe3\xe8\x48\xa9\x9d\xa1\x58\x10\x89\xbf\x3a\xf4\x6c\xa4\xe7\xaa\xfb\x44\xab\x3b\x5f\x5e\x58\xe3\x52\x2b\xb7\x5e\x19\xe3\x1c\x53\x4f\x5e\x09\x29\xbc\x7a\xb3\xb1\xd1\xfe\x6a\x23\x75\xed\x08\x7a\x60\xaf\x04\xca\x76\x04\xa4\x6f\x77\xfa\xe6\x6a\x6a\xe0\x70\xab\x6d\x8f\x02\xe8\xd2\x44\xc8\x25\x80\xe9\xa1\x6b\xb3\xfc\xd5\x06\xb7\xd5\xf1\x36\xd5\xa5\x7e\xbd\xda\x60\x97\x1c\x55\xdd\x27\x4d\x5d\x90\xa3\x53\xbd\xd7\xe7\x2b\xb0\x28\xf9\x9c\x88\x50\xf5\x71\x2d\x7f\xb5\x09\x94\x2f\x40\x93\x89\xa2\xad\xb9\x1a\xac\xf0\xab\xfb\xc1\xb6\xe9\x0d\x40\x7b\xd2\x40\x93\x5e\x43\x42\x7b\xd2\x83\xf6\x74\x1c\xda\x1f\x6a\x54\x1d\x57\xe8\xd0\x4f\xd4\x77\x89\x16\x35\x45\x3b\xcd\xf6\x5e\xcc\x96\xe8\x79\xe9\xd0\x6c\x81\xb2\x7e\xf3\x11\xdf\xd3\xbe\xca\x50\xae\xf9\xfe\xc9\x2a\xdf\xe1\x5c\x03\xd6\xa5\xc6\xa2\x92\xd4\xa0\x31\x87\x54\xd7\x7e\xd2\xd6\xb6\xbb\x24\x18\x2c\x66\xcb\x67\x32\x4a\x39\xea\xac\x87\xe9\x74\x59\x3b\xfb\x62\x09\x81\x9e\xc3\xc5\x8b\x09\x74\x8b\x62\x74\xe1\x41\xb3\x95\x49\xdd\xe9\xfb\xf7\x5b\x22\x41\xb5\xeb\xfe\xc1\x53\x9a\x3e\x41\x77\xb4\x72\x9b\xa2\xa3\xae\xe9\x34\x30\x8c\xc0\x9f\xee\x08\xbc\xbb\xe6\xd1\x76\x77\xab\x15\x8f\x7e\x97\x15\x55\x1a\x18\x58\xed\x18\x12\x17\x05\x57\xee\xf9\xd3\x11\x1c\x4f\x76\xc4\xe1\x1a\xdb\x56\x6c\xb1\x3e\x5b\xae\x9d\x5a\x02\xee\xf7\x79\xf9\x44\x1a\xc6\xab\x37\xda\x82\x62\xab\x87\xd6\x31\x4f\x36\xdc\x66\xe0\x53\x35\xc7\x46\x3f\xab\xff\x38\x2b\x11\xab\x60\x08\x04\x7f\x69\x8e\x09\x5f\x79\xd0\x07\x63\xd2\xd6\x66\x72\xe4\x35\x0e\xc0\x58\xef\x95\x57\x77\x47\xd6\xb6\x99\xfc\x2b\xaf\xee\x8c\xaa\x67\x19\xb7\x0e\x0f\xd1\xc3\x99\xcb\xf9\x6d\x3f\xac\x5f\x71\xc8\x18\x77\x8d\x48\x73\x5f\xb5\x1f\x6e\xc6\x95\x11\xe5\xde\xcd\xa5\xd6\xad\x5e\x35\x0a\xb7\x7d\x93\x0d\x6e\x1a\x4d\xb4\x20\x64\x6f\x9b\x01\x50\x02\x20\x3d\x00\x64\x00\xc0\xc9\x45\x11\x7b\xac\x96\x17\x0e\x26\xce\x35\x6b\x78\xd5\x9a\xc6\x3b\x34\xf9\x5d\x91\x2f\x7f\xb8\x59\x13\x03\x5f\x5d\xfe\x63\xae\x59\xcd\xab\xd6\x84\x74\x88\xf0\x43\x0b\x71\xbe\xbc\xf8\xf4\x05\xda\xef\x96\xa6\x19\xc9\x40\xde\x56\x4b\xeb\x2c\x43\x8a\xf1\xad\xb7\x98\x09\xe5\xa3\x93\xb6\x0e\x14\x9b\x21\x76\xe2\x95\x6e\x0b\x61\x92\x8e\xcd\x8e\x7f\xae\x63\x51\x86\x45\x9a\x6b\x3f\x15\x35\xa8\xdf\xac\xf8\x88\x76\xc3\x65\xa0\xdb\xb0\x78\x35\x5c\x07\xba\xea\x59\x2a\x7c\x95\xa3\x54\x70\x48\x2a\xe3\xe5\xbc\x7b\xde\x09\xef\xa1\xc3\x2e\xfd\x7b\xe8\x76\xff\x07\x40\x0e\x1b\x34\xcd\x69\xae\x7f\x92\x43\x50\x9f\xbc\x86\xa7\x2f\x33\xd6\xc4\x1b\xd7\x20\xd1\xa1\x51\xf4\x7a\x95\x7a\x15\x70\x08\xf3\xd0\x78\x98\xee\xe5\x7f\x9d\x73\xfe\x0b\x1f\x02\x9d\xb1\xf5\xac\x56\xee\xad\xde\xa2\x1f\x50\xf1\x29\x8b\x85\xe3\x6b\x42\xdb\x87\xf4\xb6\x70\x7e\xf7\x35\xc4\x16\x9f\x7d\x55\x4e\x0b\x0d\xd5\xc2\x9c\x1e\x70\xee\xb4\x36\xa7\x81\x52\xcb\x73\x3a\xa8\xab\xae\x2b\xb6\xac\x70\x77\xe2\xc9\xa0\x13\x4f\xae\xda\x89\x27\x83\x4e\x3c\xd9\xad\x13\x66\x51\x49\xd5\x55\x46\x56\x2d\xd1\x8a\x57\xab\x92\x7f\xe0\x86\x03\x88\x48\x5d\xee\x96\xfe\xe0\xec\x7c\x3d\xab\xc9\x30\xb1\xc8\x50\xf3\xe9\xb0\xe6\xa7\xa7\x27\x36\xdc\x1e\x6a\x50\x4f\x87\x26\x6c\xbd\x4f\x74\x4d\xa7\x26\xed\xfe\x4b\x1d\xa1\x34\xb8\xb3\xe6\xb2\xd3\x16\x1e\x62\xcb\xcd\x9c\xfa\x63\x7b\x3e\xd3\xc9\xf6\x2f\xc7\x35\xaf\x78\x5c\xd3\xdf\xf5\xb0\xa6\x3f\x76\x54\xd3\x77\x1c\xd4\xf4\xbf\x1c\xd3\xbc\xee\x63\x9a\xfe\x96\x87\x34\x0d\x62\xe9\x1c\xd1\xf4\xb7\x39\xa0\xe9\xdb\xaf\xe1\x37\x07\x0f\xef\xd2\xe0\xe3\xdb\x29\xc5\xff\x22\xc7\x35\xfb\x09\x76\x42\x4c\xfe\xb0\x33\x9c\x75\xba\x1d\x81\xf3\xcf\x95\x6e\xe7\x4a\xa7\x2d\x55\x71\x7b\xda\xb3\xae\xb3\x53\x42\x9e\x10\x93\xce\xb1\x90\x10\x13\xeb\x31\x13\xba\x65\x42\x1e\x51\xb1\x73\xd4\x84\xaa\xac\x16\x21\x26\xd7\x76\x85\x58\xef\xbe\x35\x27\xcf\xe0\x90\x83\xb7\xc9\xd2\x34\x4d\xf2\x30\x9f\x6a\x09\x7b\xf6\xa6\xa6\x9a\x11\x49\x18\x49\x08\xd3\xd3\xf9\xec\x19\xf2\xf6\x18\x9a\x26\x38\x4c\x3c\x1c\x32\x3d\xfb\x8f\x19\x09\x0e\x49\xc1\x33\x99\x33\xa8\xce\x0d\xb4\x25\x92\x28\xf6\x7d\x12\x45\x32\xad\x90\xca\x1c\x64\x46\x42\x79\x1a\x04\x8c\xc6\x7a\x5e\xa1\x2d\x91\xe4\xa9\x97\x11\xee\xe5\x7a\x1a\x22\x33\x92\x20\x4e\xc3\x80\xe2\x5c\x4f\x52\xd4\x0b\x4d\xaf\x3b\x4b\x91\xd0\xa7\x2b\x66\x29\xc2\xd1\x97\x34\x45\xd7\x14\x13\xd1\x9d\xd3\x14\x89\x26\x63\x71\x91\xee\x33\x86\x91\x11\xfd\x92\xa6\xe8\xfa\x63\x23\xba\x6d\x9a\x22\xa3\x70\xba\xf1\x11\x1d\x4d\x53\xe4\x53\x77\x9a\x22\x31\x8c\xdf\xa5\xc4\x14\x2d\x91\x7f\x91\x68\xe9\x5f\xfa\x72\xcb\xf5\x5e\x6c\xf9\x4c\x57\x56\xae\x1e\x44\xc9\xa2\xa6\xbb\x0a\xd0\x4f\xf5\x09\x5e\xc3\x5b\x37\xdd\x43\xbe\x07\xec\xec\x6c\x7e\x39\x51\x3f\x4e\x11\x5b\x9d\x9c\x9f\xf2\x45\xb5\xee\xbf\xc9\xa3\x5f\x9f\x69\xe9\x81\x54\x4a\x2d\x8a\x1e\x7a\x6f\x13\x10\xca\x48\x91\x40\x5c\x91\xc7\x84\x32\x4e\xc8\xde\x74\x58\x2f\xc6\x7e\x1c\x04\x09\xa4\x19\x24\x3e\x2f\xa2\x30\xcb\xf5\xd0\x60\xd0\x20\x0d\x33\xaf\x48\xb3\x02\x1e\x40\xc8\x82\xdc\x4f\x49\x61\x02\xcc\x93\x34\xcc\x53\x16\xc2\xeb\xd9\x98\x26\x79\x9a\x66\x4e\xc0\x7e\x12\x46\x19\x09\x53\x08\x67\xfc\x80\xa6\xa1\x4f\x4d\x80\xc3\xa4\xc0\x18\x17\x40\x71\x1a\x79\x61\xee\xe1\xc4\x09\x38\x21\x7e\x41\x09\x83\x27\xb7\x59\x81\x93\xa0\x48\x52\x13\x60\x96\xe2\x2c\xe4\x39\x50\x9c\xb3\x28\xa7\x18\x53\x27\xe0\x9c\x7a\x31\x63\x92\xc7\xcc\xf7\x7c\x8f\x04\x46\x1e\x63\x42\xfd\x30\x95\x6f\x46\x04\x61\xec\x45\x45\xca\x9d\x80\x49\xe0\x63\x1a\xa6\xf0\x76\x44\xc0\x79\x90\x12\x9a\x19\x59\x11\x7a\x59\x9c\x67\xf0\x80\x78\x1e\x16\x45\x1a\x70\xe2\x04\x1c\x93\x94\x87\x79\x0c\xac\x28\x48\x9c\xd2\x24\x32\x0a\x8f\x7a\x39\x4f\xb1\x7c\xbc\xc2\x4f\x71\x94\x44\x29\x76\xf3\x38\xcd\x33\x2f\x92\x19\x2a\x49\x98\xc5\x98\xf8\xa1\x09\x70\x86\x93\xb4\xc0\x92\x80\xac\x88\x12\x12\x25\x81\x13\x30\x0f\x92\x34\x4a\x32\xe0\x5d\xc2\x0b\x1c\xb0\xdc\xc8\x63\x5e\xa4\x3c\x88\x29\x3c\x23\xee\xd3\xa0\x20\x21\xf7\x9d\x80\xbd\x22\xc3\x49\x9e\x41\x03\x9a\xd2\x2c\x0f\x53\x23\xc5\x24\xf0\x32\x86\xb3\x0c\x1e\x69\x8f\x59\x96\x64\x51\xe8\x16\x5e\xce\x13\x92\x45\x60\x20\x61\x42\x52\x8f\xc4\x46\xc0\x01\x8b\x03\x1a\x30\x98\x23\x44\x9c\x45\x3c\xa0\x6e\x8a\xc3\x2c\xf5\x58\x92\x03\x25\x69\x1e\xe0\x22\xcd\x03\xa3\x49\x47\x45\x42\x69\x0e\x80\xa9\x8f\x71\xe8\xa7\x6e\x8a\x13\xea\xf3\x10\x87\x04\x4c\x9a\x47\x51\x5e\x30\xb3\x81\x50\x1f\x67\x51\x04\x11\x3e\xc9\xd3\xc0\x27\xd8\x73\xfb\x0a\xcf\xf3\x49\x9c\x51\xf9\xe6\x7b\x91\x12\xec\x1b\xd5\x2d\x2d\xc2\x24\x2e\x32\x95\xdf\x94\x17\x1e\xe7\x6e\xad\xc8\x22\xee\x79\x69\x01\x8a\xef\xe7\x8c\xd2\x22\x33\x6a\x45\x1e\xb2\x38\xc1\x01\x00\x4e\x7c\x8f\xb1\x98\xb8\x59\xe1\x45\x19\x8b\xfc\x50\x3e\xef\xe2\x79\x3e\x25\x66\x03\xc1\x01\x49\x48\x22\xe7\x5e\x1e\xf3\x78\xc4\x63\x37\x2b\x48\x9c\xc6\x1e\xa3\xe0\x5c\x82\x28\x27\xa4\x28\x8c\x26\x4d\x38\x16\x6c\x02\x96\x85\x19\x89\xb2\x84\x44\x4e\xc0\x41\x4e\xb2\x28\x2f\x40\x2b\x42\x96\x05\x84\xf1\xdc\xe8\x2b\x7c\x9f\x7a\x39\x06\x96\x25\x79\x12\xa6\x7e\x5e\x38\x01\x47\xa1\xc7\x62\x3f\x0c\xa4\x81\xb0\x22\xf2\x73\x6e\x56\xb7\x88\x79\x2c\x05\xbf\xed\x67\x71\x9c\x12\xe6\x76\x9b\x14\x67\x24\x4b\x88\xf4\x6e\x31\xcf\x19\xe7\x91\x09\x70\x42\x62\x42\x32\xc9\x32\x1c\x50\xe2\x87\x7e\xea\x04\xcc\x48\x5a\x70\xca\xa4\x9f\xcd\x0a\xec\xf9\x91\xd1\x40\x18\xc5\x2c\x8a\x02\xa0\x38\xcd\x02\xe2\x7b\x9e\xdb\xbb\x65\x24\x48\x69\x1a\x7b\xe0\x67\xbd\x82\x26\x71\x82\x8d\xde\x2d\x8e\xb2\x10\x33\xe0\xb1\x17\x85\x41\xca\x7d\xb7\x56\xe4\x38\x21\x9c\xe2\x04\x00\x47\xbc\x08\x09\x36\x8e\x79\x79\x94\x24\x5e\x44\x40\x16\x61\x18\x85\x2c\x19\xb1\xbc\x22\xf0\xb8\x1f\x4a\xde\x85\x71\x8c\x89\x47\x98\x51\x8f\xbd\x88\x31\x4f\xf6\xcc\x27\x69\x9a\xe3\xd4\x2d\x3c\x9c\xb0\x20\xc3\x18\xdc\x66\x4a\x73\x92\x7b\x99\x91\x62\xcc\xfd\x38\xca\x3c\xa9\xc7\x38\xc0\x2c\x0d\xdd\xde\x8d\xc4\x01\x8d\xe3\x00\xf4\x38\x2f\x28\xe7\x69\x92\x98\x00\xfb\x41\xea\xa5\x59\x0a\x3d\xe3\x38\x49\x03\x3a\xa2\x6e\x7e\x82\x33\x2f\x4b\x41\x28\x59\x98\x25\x21\x8b\x7c\xa3\x3f\xe6\x39\x65\x2c\x00\xb7\xc9\xfd\x00\x53\x96\xb9\xd5\x2d\x4c\x93\x2c\x63\x41\x21\x47\x86\xc8\xe7\x7e\x6c\x04\x1c\x51\xc2\xa3\x42\x3a\xab\x3c\x4a\x49\x4a\x99\x9b\x15\x71\x40\x0b\x4a\x38\x18\x48\x98\xf3\x22\x25\x66\x5f\x11\x53\x16\x46\xbe\x1c\x69\x02\x1f\xc7\xa4\x88\xdc\x5a\x41\x83\x8c\xc6\x14\xcb\x48\x08\x17\x1e\x4b\x63\xa3\xdb\xa4\x59\x16\x7b\x44\x0a\x0f\xb3\x28\xf0\x13\xee\x8e\xdd\x12\x2f\xe5\x45\x51\x30\x19\x45\x46\x3e\xe6\xc4\xa8\x15\x2c\x08\xbd\x28\xe3\x60\x79\x39\xa7\x24\xcd\xb9\x3b\x76\x4b\x79\x91\x30\xbf\x90\x23\x03\xc9\xa2\x38\xc1\xe6\xb8\x22\x8a\x71\x4c\x0b\x39\x84\xf9\x31\x09\x7d\xe2\x16\x5e\xc6\x48\xec\xf3\x0c\x78\xcc\x19\x89\x22\x9c\x18\x79\x9c\x63\x1a\xa5\x54\x0e\x4d\x44\x28\x12\xe9\x2e\x02\x0e\x03\x11\x96\xb3\x38\xcf\xc1\x40\xb2\x9c\x7b\x3c\xc5\x46\xb7\x59\x84\x71\x1e\x14\x71\xa1\x06\x5d\x9e\xe3\xd8\xad\xc7\x5e\x54\x78\x51\x2c\xe3\x85\x98\xe0\x38\x2a\x52\xa3\x49\x7b\x2c\xf2\xe3\x3c\x03\x03\x61\x24\xa3\x09\x65\xee\x11\x04\x63\xbf\x48\xa8\x17\xa8\x85\xbb\xc4\xcb\x99\x91\x62\x9c\xc6\xd8\x4b\x7d\xe9\x8f\x7d\x9c\x05\x31\x76\xf3\x98\xd0\x3c\x8d\xe3\x22\x94\x5a\xe1\x05\x71\x4e\x8d\xfe\xd8\x27\x19\x63\x69\x0c\x5a\x11\x78\x59\x4c\x82\xc4\x6d\x20\x7e\x96\xf0\x94\x7b\xc0\x0a\x1c\x66\x49\xca\x53\xa3\xf0\x02\x1f\xe7\x51\x9c\x41\xcf\x92\x0c\x7b\x5e\x1e\xb8\xf5\x38\xc8\xb2\x30\x0f\x64\xe0\x9d\xa5\x3e\x0f\x48\x6a\x1c\x9a\x44\xb8\x42\x92\x04\x9c\x55\x91\x45\x61\xcc\x85\x7b\x75\xf9\x8a\x22\x4b\xa3\x82\xc9\x41\x92\xe5\x51\xc1\xb8\x91\xe2\x28\x0b\x02\x9c\x50\x00\x1c\xb0\x20\x0e\x29\x8e\xd5\x22\xea\x5b\xc7\xb5\xd5\x76\x5e\xf8\xe3\x55\x6f\xa8\xda\x9e\x41\xfb\xb1\x73\x43\xf5\xa7\xab\xdd\x50\x0d\x31\xd9\x6e\xeb\xc0\xb0\x1d\x71\xfd\xd9\x47\xaf\xba\x75\x10\x31\x2f\xe1\xf5\x82\xbb\x9f\x66\x59\xe2\x59\xb6\x0e\xd2\x34\x8a\x19\x97\xc3\x2f\x0d\x32\xc6\xe2\x6e\xe8\xe2\x40\xe2\x67\x11\x2f\xfc\x18\x3c\x59\xc1\x93\xa0\xa0\xc2\x93\x99\x6a\xb2\x30\x28\x8a\xd0\x07\x2b\x08\x0b\x9c\xfb\x51\xb1\xed\xaa\x7e\x88\x3d\x1e\x12\xe9\x7c\x58\xce\x23\x4a\x72\xcb\xd6\x41\x92\x7a\x61\x44\xa5\x42\x92\xd4\xe7\x51\x86\x8b\x2d\x91\xe0\x82\xfa\x79\x22\x75\xbe\x48\x03\x9c\xe6\x91\xa5\x27\x61\xca\xbd\x2c\x97\x61\x10\xf6\x63\x4e\x70\x9c\xec\xb2\x75\x70\xdd\xf7\x48\xb7\x49\x0d\x0b\xf5\x3c\x7b\xe6\xd7\x63\x6c\x4f\xfd\x7a\x4c\xec\xb9\x5f\x8f\x7d\x7b\xf2\xd7\xe3\xc0\x9e\xfd\xf5\x38\xb4\xa7\x7f\x3d\x8e\xec\xf9\x5f\x8f\x63\x4b\x02\x58\xd9\x41\x48\x0f\x6b\x3c\x07\x2e\xcb\xe7\xb2\x7c\x78\xd9\x43\xf2\x00\x9a\x1b\xaf\x40\xc9\xf2\xb9\x2c\xb7\x34\x27\xd0\x9c\x58\x9b\x93\xb9\x2c\xb7\x34\xf7\xa1\xb9\x6f\x6d\xee\xcf\x65\xb9\xa5\x79\x00\xcd\x03\x6b\xf3\x60\x2e\xcb\x2d\xcd\x43\x68\x1e\x5a\x9b\x87\x73\x59\x6e\x69\x1e\x41\xf3\xc8\xda\x3c\x9a\xcb\x72\x4b\xf3\x18\x9a\xc7\xd6\xe6\xf1\x5c\x96\x1b\x8e\xf5\x6d\x99\xf4\x58\x6a\x86\x09\x38\x93\x4a\xd1\xcf\xb8\x07\x47\x6e\xa5\x42\x98\x5a\xa5\x52\x17\x4c\xad\x32\xa9\x07\xa6\x56\x99\x54\x01\x53\xab\x5c\x8a\xdf\xd4\x2a\x97\x92\x37\xb5\xe2\x52\xea\xa6\x56\x5c\x0a\xdc\xd4\xaa\x90\xc2\x36\xb5\x2a\xa4\x9c\x4d\xad\x4e\xa4\x8c\x4d\xad\x4e\xa4\x78\x4d\xad\x66\x52\xb4\xa6\x56\x33\x29\xd5\xb9\x29\xef\xa0\xeb\xea\xee\x96\xcf\xa1\x5a\xf3\x69\xd7\xf8\x7f\x2c\x65\xee\x61\xdb\x75\xf3\x47\x30\x82\xd7\xdb\x67\xc3\x2a\x5b\x24\x8a\x96\x68\x04\x0b\x7e\x2c\xeb\xdb\x06\x7a\xd6\x68\x74\x1b\x91\xb7\x50\xd3\x9c\xcb\xb5\x85\x31\x97\x30\xd4\xfd\x82\x3e\x0c\xb8\x35\x7f\xa5\x0c\xd4\x87\x87\xe8\x3f\x20\x1b\xb1\x1d\x79\x9d\xd2\x79\xa7\x0c\xd5\x9b\x59\x93\xe7\x78\x33\x76\x17\x4f\x55\x9b\x6b\x2d\xdc\xf7\xf1\x64\xad\x59\x27\x0b\xf6\x4c\x26\xff\xd5\x93\x57\xcf\x21\x45\x71\x9d\x0e\xb8\x53\x8f\x0e\xea\xc1\xa1\xd7\x77\xa8\x5b\x2d\x76\xdd\x30\x95\x35\xe7\x1d\x2a\xe6\x43\x2a\x66\x26\x2a\xe6\x43\x2a\x66\x3a\x15\xdd\x7a\xf1\xb0\x9e\x25\x93\xb1\x2e\x52\x4b\xce\x9c\x0f\x5a\xee\xed\x5d\x92\x6f\xb7\x12\xc5\xdb\x49\x14\xb7\x12\xc5\x5b\x49\x14\xcf\x3a\x09\xbe\x67\x75\x16\x6e\x2d\x31\xf7\x5c\xe5\xea\xd6\x98\x84\x15\x87\xbb\xd5\xe0\x1c\x73\xa2\x89\xb4\x86\x17\x8d\x8a\x14\xcf\x3b\x64\xcc\x0d\x64\xcc\x4c\x64\xcc\x07\x64\xcc\x3a\x64\x74\x01\x46\x03\x78\x24\x72\xca\x74\xa7\xdc\xe1\x2e\x57\x12\xb7\x62\x8f\x5d\x62\xff\xb1\x8c\xa5\xe7\x32\x0e\xcc\xbd\x9a\x73\x55\xd3\x71\x27\x5c\xd6\xc4\x91\xe6\x48\xac\xaf\x42\xd7\x75\x25\x01\xd8\x18\x59\xf4\xeb\xce\xeb\xba\xa3\x34\xb4\x9e\x66\x2e\x98\x56\xc6\xfd\x91\xab\x5b\xbd\x75\x65\x33\x59\x7d\x06\x39\xdb\x04\x1c\x21\x49\x6f\x0f\xdd\xaf\xad\xb3\xf9\xe5\xff\x47\x18\xdd\x45\x83\x63\xd3\x43\x3a\xc4\xbf\xb5\x04\xc7\xc9\x10\xff\xee\x37\xd6\x62\xa1\x02\x5f\x95\x0a\xe0\xe2\x96\x34\x48\xe9\x0c\x29\x90\x92\x18\xe0\x37\x03\x6d\x47\xc5\x1f\x4b\x9b\x78\xdb\x51\xef\xc7\xd2\x44\x9c\x3d\x27\xbe\x4a\x8a\x3f\x43\x37\x51\x31\x53\x69\xf1\xc5\x17\xf3\x3d\x3e\xd9\x46\xda\x3e\x9f\x8b\x36\x73\xd5\x46\x7c\x39\x99\x3b\x92\xe9\xcf\x20\x9b\xbe\x00\x9d\x4a\x3c\xf0\x39\x93\x9f\x53\xf5\xd9\xde\x7c\x0e\xcd\x05\x96\x54\xa2\x84\xcf\x99\xfc\x9c\xaa\xcf\xee\x94\xfc\x33\x99\x93\x5f\x39\x1c\x39\xae\xb0\xb9\x4c\x2f\xbd\x27\x93\x1f\xb0\x59\x9d\xb1\x5f\x15\x76\x72\xf6\xcf\xb4\x57\x24\x58\x3d\xea\x38\x33\xf3\xc3\x6c\x6a\xd2\x00\x52\x38\x67\x5d\x9c\xf3\x0e\xce\x59\x17\xe7\x5c\xc7\x39\xdb\x06\x27\x96\xfd\xe4\x6a\x68\x90\xf7\x4d\xb8\x1c\x14\x68\x9d\xf6\x7f\x56\x3f\x5a\xa1\x15\x06\x6d\xa1\xc0\xe9\xd7\x65\x32\x0d\xb7\x1b\xa7\xec\xa7\xaa\x5c\xe3\x9c\x75\x71\xce\x3b\x38\x67\x5d\x9c\x73\x1d\xe7\xac\xc5\x69\x8c\x3a\xc7\xdf\x21\x30\xd3\xfa\x3d\x64\x5f\xfa\xde\x7e\x99\xea\x7b\x30\xde\xef\x4b\xd7\x35\xaa\xef\xc1\x19\x7c\x5f\xda\x5c\xe8\x07\x78\x28\x41\xd4\x99\xcd\x1b\x12\x4d\x46\x29\x2b\x0a\x84\xb3\xb6\x2f\xd2\x5d\x54\x58\x77\x17\xb3\x6d\x7c\x55\x8b\x56\xfc\x2b\x38\xe2\xc6\x59\x01\xaa\x6c\x66\x42\x98\x5d\x09\xe3\xf7\x46\xd7\xd3\xc7\xf8\x7d\x69\xc2\xf8\x7d\x79\x15\x8c\x66\x67\xd7\xc7\xf8\xa3\x11\xe3\x8f\x26\x8c\x66\x6d\xeb\x3f\x5e\x61\x41\x09\x8b\x17\xb5\xd9\x43\x45\x2b\x75\xb0\x0e\x52\x7b\xa5\x7d\xe9\x1e\x81\x44\xa2\x93\x58\xc3\xda\x8e\xcc\xbf\x9f\xe5\xac\xe2\xe8\xc2\x3d\xd3\x17\x7f\x30\xdf\x34\xea\x37\x4c\x37\x4f\x4c\x64\xc3\x00\x54\x98\xda\xc0\xc4\xb6\x30\xb5\x81\x39\x34\x37\xb5\x81\x29\x34\x37\xb5\x81\x29\xf9\x24\x9f\xc3\xf3\x1d\x73\xdb\xfb\x1d\x30\xa7\x9f\xe4\x33\xa8\x25\x59\xc7\x75\xce\xe5\x03\xa6\x59\x5f\x02\x11\x90\x32\x13\x8d\xb0\xa4\x90\x99\x68\x84\xd5\x8b\xd4\xd4\x06\x16\x2f\x52\x53\x1b\x58\x27\x61\xa6\x36\xb0\x4c\x32\x78\xcd\x40\xfc\xc1\xb2\xcb\x44\xaa\x7a\x45\xac\xcc\x80\x85\x9b\x89\xe4\x83\xd0\xac\xfd\x76\xc4\x91\xdc\xa8\x86\xc1\xce\xb5\x3e\x56\xa2\xad\x19\x42\x64\x70\x0c\xfa\xcf\x06\xd1\xc0\x71\x93\x8c\x62\x72\x0c\x7a\xcf\x24\xb1\xc7\x9e\x4e\x2d\x1b\x12\xdb\x87\xa3\xad\x32\x4a\x84\xc0\xa2\x74\x88\x10\xb7\x08\x81\x3d\xa9\x42\xd8\xf1\x04\xe9\x38\x42\x6d\x5d\x52\x22\x24\xe0\x62\x87\x08\x49\x8b\x90\xcc\xea\x71\x69\x02\xf5\x35\xf7\x3a\x8e\x50\x5b\xc9\x94\x08\x7d\x81\x30\x1f\x22\xf4\x5b\x84\xbe\xc0\x95\x2b\x84\xfe\x88\x39\xf4\xe1\x68\x6b\x9f\x12\x61\x20\x10\xf2\x21\xc2\xa0\x45\x18\x08\x5c\x5c\x21\x0c\x74\x84\x7c\x1c\xa1\xb6\x5a\x2a\x11\x86\x02\x61\x31\x44\x18\xb6\x08\x43\x81\xab\x50\x08\x43\x1d\x61\x31\x8e\x50\x5b\x5f\x95\x08\x23\x98\x54\x0c\x11\x46\x2d\x42\x88\xde\x4f\x14\xc2\xa8\x33\x89\x18\x47\xa8\xad\xc8\x4a\x84\xb1\x40\x38\x1b\x22\x8c\x5b\x84\x30\x6d\x52\x63\xb2\xa8\xef\x0a\x02\x3e\xf9\xee\xc5\x97\x47\x71\xae\xef\x51\x1c\x2c\x82\x7b\xf5\xb2\x99\x00\x06\x79\x58\x7c\xef\xba\x9f\xc5\x31\xa3\xc1\xff\x94\x0f\xe3\x3c\x5c\x2e\x3e\xf0\x95\xcc\xf2\x8b\xaa\x25\xf2\xc9\x9d\xb4\xac\x44\x80\x92\x23\x06\xe7\xb3\x53\x5e\x2c\x57\x5c\x1d\xa7\x1e\x48\x4d\xbb\x6b\xa2\xed\xdd\x55\xcb\xd7\x3e\xb9\x8e\x87\x78\xfe\xac\x4f\xf0\xe8\x74\x36\xf9\x41\xee\x22\xec\x91\xe0\xd0\x57\x79\x8a\xbf\xdc\x6e\xb2\x5e\x55\x0a\x31\xd9\xf5\x76\x93\x68\x32\x72\xbb\xa9\x73\xac\x61\x70\xbb\x29\xc4\xe4\xcb\xed\xa6\xeb\xbe\xdd\x24\xa4\xb2\xdd\xed\x26\xa3\x70\x3a\xb7\x9b\xa4\x80\x9c\xb7\x9b\xe4\x3d\xda\x2d\x6f\x7f\xfb\x7f\xea\xfb\x4c\x7c\x91\xdd\x49\xd9\x9a\x47\x41\xaf\xe0\x34\x0f\xfb\x55\x3f\x9c\xbd\xcf\x8b\xde\x8f\x59\x79\x36\xe3\xab\x3f\xe4\x4a\x94\x46\x2a\x7c\x17\x14\xca\x02\x49\x18\x7c\xd6\xe9\xf9\x57\xb8\x3a\xf5\xe3\x56\x6f\x02\xc1\xe1\x99\x87\xd0\xf5\xa6\x9e\xf6\xdb\xf8\x55\xa8\xc3\x43\xf4\x9c\xaf\x4e\x61\x14\x7d\x38\x5b\x96\x19\x47\xb8\xff\x6c\x8a\x68\xfe\xfc\x21\xee\xde\x5d\x0a\xe3\x29\x0a\x92\x29\x0a\xf0\x14\xf9\xfe\x14\x91\x70\x8a\x70\x3c\x45\xc9\x14\x21\xac\x1d\x35\x0a\xe9\x14\x85\xde\x14\x05\x64\x8a\xfc\x60\x8a\x48\x34\x45\x98\x4e\x11\xf6\xa6\x88\xe8\xf5\x92\x29\x0a\xf1\x14\x05\xfe\x14\xf9\xe1\x14\x91\x78\x8a\x70\x32\x45\x58\xc0\xd7\xea\x45\xde\x14\x85\x64\x8a\x82\x60\x8a\xfc\x68\x8a\x22\x7f\x8a\xc2\x70\x8a\x82\x78\x8a\xfc\x44\xab\xe8\xe3\x29\x22\xfe\x14\xe1\x70\x8a\xe2\x29\x42\x11\x99\xa2\x30\x98\xa2\x00\x9e\x16\xd0\x2b\x0a\x4a\xc8\x14\xe1\x60\x8a\x22\x51\x11\x4f\x51\xe8\x4f\x51\x10\x4e\x91\x1f\x6b\x15\x49\x32\x45\x04\x4f\x11\x16\x28\xa7\x08\x11\x3a\x45\xc4\x9b\x22\x2c\xc8\x91\xd5\xde\x3a\xf8\x4a\xcc\x7c\x25\x5d\xbe\x0a\x2a\x04\x1f\x45\xbf\x89\xf8\x3c\x45\x28\xd4\xa9\x55\x88\x45\xb7\x04\xb5\x40\x90\xa7\x53\xe9\x2b\xc6\x09\xaa\x44\x85\x68\x8a\xf4\xee\xe2\x48\xf2\x43\x30\x18\xa8\xf7\xbb\x82\x10\x02\x15\x0c\x16\xfc\xf3\x63\xc9\xd8\x30\xec\xf1\x2b\xf0\x94\xb4\x42\x29\xfd\x40\xc7\x20\x44\x23\x54\xc3\x17\x22\x8d\xa4\xd8\x43\x5d\x86\x42\x04\x42\x1f\x84\x5e\x08\x19\x0a\xc6\xd6\x51\x4d\xe7\x45\xa8\xf3\xd3\xf3\x39\x83\x67\x52\x44\x50\xb9\x9e\x95\xc5\xe0\x85\x27\xb0\x82\xef\x5e\xfd\xf4\xf2\xf8\xbb\xc7\xf2\x4d\x29\xc1\x31\x32\x45\xd0\x79\xc1\x21\x2a\x34\x52\x89\x09\xb8\xab\x34\x15\x2b\x71\x12\xa5\xbd\xc0\x10\xaa\xe3\x7f\xf9\xcd\xb3\xd7\x7c\x8d\xd8\x22\x57\xb9\xd1\xcf\x40\xa4\xf2\x3d\x0d\x03\x1d\xa2\xfe\x4f\xcf\xbb\xf2\xec\x85\x94\xde\xc6\xbb\x0b\x93\x11\x4a\x3c\x6f\xda\x2f\xab\xe7\x0a\xb2\x8a\xa1\x02\xe9\x54\xa0\x9e\x47\x06\x55\x7c\xad\xca\xb0\x34\xd0\x4b\x0d\x08\xc2\x2e\x02\x62\x40\x10\x75\x89\x34\x55\x89\x7b\xfd\x30\x20\xa2\x1d\x42\x86\x20\x92\x3e\x96\x21\x08\xa6\x57\x31\x55\x48\xfb\xdc\x1a\x56\xc9\x7a\x68\x06\x15\xf2\x7e\x57\x86\x55\xb8\x56\x65\x88\xa1\xe8\x52\x39\x6c\x4e\x5d\xad\x31\x1d\x95\x07\xa1\x23\x08\x7c\x3a\xa2\x55\x41\x1f\x89\x41\x2f\xa8\x5b\x6f\x22\x3a\xaa\x98\x31\x75\x29\x26\xa5\xa3\xf2\x4e\xe8\x88\xbc\x59\x9f\x08\x83\x4a\xf4\xd1\x0c\x29\xc9\xe8\xa8\xc4\x73\x3a\xa2\x35\x9c\xba\xb5\xbb\xe8\xe3\x30\x48\xde\x2a\x2e\xe5\x25\xb0\x99\x91\x44\x2b\xb5\x08\xd3\xef\x54\x31\x62\x0f\xba\x50\x4c\x7d\x0c\xf5\x2a\x46\x9d\xd0\xe9\x34\x94\xc7\x5d\x32\x1c\xb6\x81\x1d\xea\x9f\xf4\x29\xb5\x3a\x0a\xec\x90\x68\xda\xed\x8c\x41\x2b\x3a\x9d\xb1\xfa\x09\xec\xd0\x5f\xde\xab\x62\x73\x15\xd8\xec\x0a\xe8\x28\x2b\x30\x1d\x65\x05\xa1\xa3\xa2\xf7\xa9\x5b\x6c\x41\x0f\x84\xcd\x57\xb8\xd8\x1d\x51\x97\x0a\xc7\x74\x44\x18\x94\x8e\x70\x32\xa1\xa3\xaa\xc5\xa8\x5b\xa0\x69\x9f\xdf\x86\xc1\xa3\x8f\x65\x58\x25\xa7\x2e\x91\x72\x3a\x62\x42\x45\x5f\xa2\xfa\x1b\x55\xd3\xb1\x28\x23\xf0\x3c\x1a\x78\xd8\xea\x41\x54\x1d\x6b\x98\xd1\x08\xd0\xe6\x41\x6a\x24\x9e\x09\x49\xd0\x45\x62\xac\x13\x76\xe1\x18\x89\x89\xba\x70\x8c\x75\xe2\xb6\x8e\x01\x8b\xee\x6c\x8d\xcd\x93\x3e\x0a\x03\x10\xd6\xef\x8e\x3d\xe0\x50\x88\x0c\x40\xb2\x0e\x63\x0d\x15\xf2\xb6\x82\xd5\x81\x48\x12\x0c\x8d\x8b\xbe\x54\xac\x71\x97\x93\x99\x98\x8e\xf4\x82\x50\x17\xb7\xfd\x3e\x0a\x93\x6e\xd0\x9e\xdc\x4d\xba\x41\xc7\x19\x1e\xd1\x11\x45\x8d\xe9\xb8\xa2\x52\x3a\x22\x94\x84\x3a\x84\xc2\xa8\xdb\x96\xd2\x3e\x05\x76\x47\xe2\x34\x95\x9c\x8e\x28\x31\xef\xf3\xd4\xee\x4f\xac\x1a\xa4\x4f\x40\x0c\xa5\x78\x0b\xb3\xc7\x64\x0b\x63\xc2\xfe\x16\x86\x8f\x83\x2d\xf4\x19\x87\x4e\xd3\xc7\xd1\x98\x49\xe2\x78\xc4\x19\xea\x21\xb8\x19\x42\x32\xe6\x2e\x31\x1b\xb3\x7b\x9c\x6e\xe1\x2d\x71\x36\xe6\xc8\x70\xbe\x85\xb3\xc4\x7c\x0b\x57\x86\x8b\xbe\x84\x8c\xea\x32\xe6\x2a\x30\x1e\xb3\x50\x4c\xb6\x30\x10\xec\x8f\x58\x19\x0e\xb6\x71\x6c\xe1\x16\x6e\x07\x47\x4e\xef\x86\xe3\x2d\xdc\x12\xa6\x5b\xd8\x22\x4e\xb6\xb0\x7a\xcc\xb6\xf0\xa6\x38\x1d\xf3\x60\x38\x73\xb9\x30\x9c\x8f\xb9\x05\xbe\x85\x1b\xc5\x45\xcf\x43\xed\x12\xaa\x60\x2f\xb0\x38\x23\x33\xc9\xa4\xc3\x15\x6c\x0d\x51\x24\x6c\x13\xf4\x40\x2b\xf7\x0c\xe5\x61\x4f\x38\xc3\x1a\x51\x87\x69\x26\x1c\x71\xa7\xc6\xf8\x70\x6c\x8f\x4d\x5a\x2c\xb6\xc8\xa4\xee\xa9\x2d\x2a\x69\xa9\x18\xd2\x99\xf5\xb8\x39\xac\x91\x77\xb8\x65\x0b\x4d\x00\x82\x25\x2c\x51\x6d\xcd\x1c\x70\x75\x0f\xd3\x31\xf2\x09\xb5\x2b\x8a\x4f\xc7\x14\x25\xa0\x63\x82\x0e\xa9\xbb\xf3\x11\x75\xab\x52\xac\x95\x0f\x4b\x29\xb5\xb3\x2e\xa1\x2e\xd6\x31\x3a\xa6\x5e\x29\x75\x1b\x41\x46\xdd\xaa\x93\xd3\x31\xc5\xe0\x74\xcc\x08\x0a\x3a\xa6\xe2\x9d\xb0\xc2\xa2\x04\x78\xc4\x5c\x31\x19\xd1\x50\xec\x8f\xba\x0c\x1c\x38\x35\x15\x87\xa3\x06\x8f\xa3\x51\xaf\x81\x63\x97\x27\xa6\xa3\x96\x88\x93\x51\x97\x81\x99\xc3\x1a\x71\x3a\xe2\x2e\x70\x36\xea\xb5\xb0\xee\x0e\x0c\x28\xf8\x88\xef\xc5\xc5\xa8\x4b\x52\xa1\x85\xb3\x9b\xd8\x69\x57\x98\x8c\xbb\x16\xdf\xe1\x39\x70\x30\x62\xd6\x38\x1c\xf5\x2d\x38\x72\x1a\x30\x8e\x47\x7d\x1b\xa6\x23\xce\x07\x27\xa3\x16\x88\xd9\x88\x1b\xc0\xe9\xa8\x0f\xc4\xd9\xa8\x2b\xc0\xf9\xa8\x3f\xc2\xdc\xe1\xec\x70\xd1\xf5\x46\xbb\xc4\x0f\xd4\x93\x28\xcd\xbe\xa5\x8e\x3e\xb1\x17\x58\x42\x89\x9a\x68\x43\xb9\xdf\x42\x08\xcc\x8a\x18\xd8\x95\x28\xec\x72\xc4\x1c\x43\x34\xc1\xb1\x09\x7d\xec\x75\xc2\x3f\xfb\xf8\x59\xef\xa8\x98\x23\x88\x56\xb6\xe6\xf8\x41\x96\x9b\x63\x87\x96\x7d\xb6\x1d\x94\x96\x3d\x06\x18\xb9\x66\xa5\x96\xc8\xa1\x56\x6f\x73\xec\xd0\x0a\xd8\xd2\x7f\xa7\x7c\x31\xb5\x77\x8f\xd0\x31\xe2\x7d\x3a\xc6\x80\x80\xba\x45\x1c\xd2\xb1\x2e\x44\xd4\xaa\x3f\x31\x1d\x53\x3e\x4a\x5d\xfc\x4b\xba\xc8\x6d\x41\x84\x43\x3b\x52\xea\x92\x5e\x46\xc7\xb4\x2f\xa7\x6e\xfd\xe5\xd4\x6d\x7e\x05\x1d\xb3\x10\xec\x8d\x98\x08\xc6\x23\x56\x88\xc9\xa8\x19\x62\xdf\x35\x52\x38\x35\x1c\x87\xa3\x26\x82\x23\x6f\x4c\x4e\x38\x1e\xf5\x64\x98\x8e\x5a\x0b\x4e\x46\xdd\x05\x66\xa3\x0e\x0f\xa7\x23\x3e\x13\x67\xa3\x7e\x03\xe7\x23\x6e\x09\x73\x87\x5f\xc2\x85\xd3\x6d\xc8\xe8\xc1\xdd\x07\x3c\x6a\x97\x98\xd8\x0d\x13\xfb\x23\x66\x8f\x83\x11\xc5\xc7\xe1\xa8\xed\xe0\x68\xdc\xbb\xc5\x0e\xf7\x86\xe9\xb8\xf1\x24\x4e\xff\x81\xd9\xa8\xff\xc3\xe9\xa8\x13\xc5\x99\xd3\x89\xe0\x7c\xd4\x4b\x61\x3e\xe2\xa6\x70\xd1\xf5\x23\xbb\x05\x0f\x46\x9f\x52\xd3\x6b\xdb\x21\x69\xa8\x31\x86\x0c\x77\xb5\xe3\x1a\xc6\x88\x41\x55\x80\xf5\x14\x63\xdc\xd0\xc4\x7c\x86\xf2\xa8\x06\x60\xab\x10\xb7\x04\x1a\x4a\x75\x99\xdb\x42\x86\x96\x3e\x4b\xcc\xd0\xf6\xd0\x80\x21\x6d\x09\x34\x93\x90\x75\x2a\x98\x06\x0e\xab\xed\x71\x5d\x38\x06\xd0\x45\x87\x39\xe6\x35\x07\x57\x7b\x4c\x47\x98\x4b\xa8\x67\x53\x1c\x9f\xba\x15\x27\xa0\x2e\xc5\x09\xe9\x88\x5e\x44\x74\x84\x6b\x31\x1d\x51\x3d\x4a\x47\x44\x9b\x50\x1b\xdf\x19\x1d\x91\x69\x4a\xdd\x5a\x9b\xd1\x11\xad\xc9\xe9\x88\xe4\x38\x75\x2b\x6e\x41\x5d\x6a\x8f\x3d\xa7\xd9\x62\xec\x59\xe5\x8a\xc9\x98\x4d\x63\x7f\xcc\x26\x71\x30\x62\xd5\x38\x1c\x33\x0a\x1c\x8d\x79\x0e\x1c\x8f\xd8\x76\x33\xee\x59\xc5\x88\x93\x31\x03\xc2\x6c\xc4\x3f\xe2\x74\xcc\x83\xe0\xcc\xe9\xa1\x70\x3e\xe6\x61\x30\xb7\x0f\xce\xc5\x88\x87\x80\xf8\xc0\x2d\x2b\x3c\xa2\x69\x98\x8c\x58\x3a\xf6\xc7\x8c\x19\x07\x63\xc6\x8a\xc3\x31\x57\x15\xd9\x5d\x11\x8e\xc7\x9c\x05\xa6\x6e\x73\x49\xc6\x0c\x1e\x33\xab\xb3\xc0\xe9\x98\x2d\xe3\x6c\xc4\x5d\xe0\xdc\xe9\x2c\x31\x1f\x73\x65\xb8\xe8\x39\x9c\x5d\xa2\x02\x45\x36\x35\x79\x91\x1a\xa6\x29\x2e\x90\x6d\x89\xb9\xcf\x7e\x5b\x4e\x4c\xb0\x83\x96\x23\x46\xf8\xa1\xde\x1f\x53\x54\xd0\x94\x0e\x61\xc7\x1d\x85\xb6\x8e\x8a\xc6\x68\x40\x23\x6a\x08\x98\xd5\x68\x8d\x24\xa7\x4a\x41\x4d\x11\x80\xc6\xab\x61\x79\xae\x81\x1d\x96\xf2\xa6\xaf\xc3\xb2\xa2\xc3\x65\x53\x4f\x9d\x42\xc2\xd4\x2d\x24\x42\x2d\x3d\xf2\xa9\x4b\x3a\x01\x75\xf5\x27\xa4\x6e\xad\x8b\xa8\x5b\x33\x62\x6a\xe7\x07\xa5\x2e\xbd\x48\xa8\x5d\x9f\x19\x75\x8b\x3e\xa5\x6e\x19\x66\xd4\xa2\x53\x39\x75\x8b\x88\x53\x97\x4e\x15\xd4\xad\xca\xd8\x1b\xb1\x23\x8c\x47\x94\x0f\x93\x11\x4b\xc5\xbe\x43\x01\x71\xe0\xb4\x53\x1c\x8e\x98\x22\x8e\xbc\x11\x1f\x14\x3b\x6d\xae\x89\x60\x2d\xb4\x27\x56\xaf\xcd\x6c\xd6\x8a\xd3\x11\xd7\x86\x33\x87\x5f\xc4\xf9\x88\x0f\xc1\x7c\xc4\x66\x71\xe1\x74\x6e\x62\x44\xb7\x10\x8e\x9d\xaa\x84\x89\xd3\x68\xb1\x3f\x62\x97\x38\x18\x31\x4c\x1c\x3a\x2c\x13\x47\x23\xbe\x06\xc7\xa3\xce\x6a\xc4\x92\x70\x32\x62\xa3\x98\x39\x1c\x00\x4e\x9d\x5e\x0b\x67\x4e\xd7\x82\x73\x9b\xfd\x63\x3e\x66\xc2\x45\xd7\xf5\xec\x3e\x74\x1b\x74\xa4\x26\x35\xf0\xb0\x61\xe8\x56\xa1\x86\x61\xd0\x56\x40\x4d\xcd\x82\x26\xc8\x31\x95\x86\x96\xee\x47\x12\xa4\x61\x8c\x6e\x43\xa6\x61\x29\xd5\x3a\x60\x1a\xa6\x9b\xbe\x0f\x9b\x32\x4d\xc9\x87\xa5\xa9\xd6\x09\xd3\x54\x5d\x8b\xe3\x0c\xc3\xb4\xe4\xdb\x10\x2a\x6f\xf9\x66\x9a\xa4\x6b\x91\xef\xb0\xa7\x2e\x36\x60\x6a\x66\x2a\xa1\x2e\xf9\xfa\xd4\xd5\xc7\x80\x3a\x14\x27\xa4\x2e\xe6\x45\xd4\xd5\x93\x98\xda\xd8\x43\xa9\x43\xad\x12\xea\x12\x35\xa3\x2e\x89\xa4\xd4\xa1\x08\x19\xb5\xa9\x79\x4e\x5d\x9a\xcc\xa9\x59\x63\x0b\xea\x10\x32\xf6\x9c\x52\xc6\xd8\x69\xae\xc4\x69\xaf\xd8\x77\xda\x0a\x0e\x5c\xe6\x80\x43\xa7\x29\xe1\xc8\x69\x10\x38\x76\x79\x04\x35\xde\x18\x8b\x12\xa7\xb7\xc0\xcc\x65\x31\x38\xb5\x38\x0d\x9c\xd9\x9c\x6c\xee\xb4\x5c\xcc\x9d\x4e\x01\x17\x56\x8f\x88\x3d\xa7\xd4\xb1\xd3\x10\x31\x71\x5b\xb7\x6f\xd1\x34\x1c\x38\x0d\x0d\x87\x2e\x13\xc6\x91\xd5\x0e\x71\xec\xf4\x0c\x98\x3a\xad\x1f\x27\x4e\x5b\xc4\xcc\xe2\xac\x70\xea\x34\x37\x9c\xb9\xbc\x03\xce\xad\x56\x8c\xb9\xd3\x73\xe0\x42\x73\x0e\xbb\x8c\xa9\x54\x0c\xf0\xc4\x00\xb0\x61\xce\xd0\x1f\xdf\x6d\x37\x37\x86\xee\x58\xb6\x1b\x3a\x62\x05\xcf\x50\x14\x4a\x78\xc4\x48\x47\xd4\x14\x9a\x9c\xb0\xa2\xc4\x3c\xce\x50\xcf\x4c\x7f\xd2\xf4\xdb\xe4\x82\x25\x9d\xa6\xa2\xb4\x01\x6a\xa0\x33\xbb\x2b\x2f\x7b\x0c\xdd\xaf\x59\x4f\x78\xc3\x44\x43\x9b\x42\x11\x61\x28\xaa\x37\x95\xac\x3d\x97\xc5\xd8\xc5\x53\x55\x87\xb8\xe4\xaf\xea\xf8\x2e\x59\xab\xdf\x03\x17\xb3\x55\x9d\xd0\xce\x56\x55\x23\x1a\xed\x73\x6c\x51\x2d\x55\x4c\x5d\x1c\x55\x75\x12\x9b\x94\x54\x39\xb3\x6b\xa9\xaa\x91\xba\xf4\x51\xd5\xc9\xcc\x22\x57\xa5\xb9\x4b\x8d\x54\x1d\xee\x52\x51\x55\xa7\xb0\x5b\x68\x1d\x11\x1b\x0d\x1b\xbb\x7a\x80\x89\x85\xc9\xd8\xb7\x69\x1c\x0e\x5c\xc4\xe2\xd0\x25\x16\x1c\xb9\x98\x81\x63\x47\x17\x6d\xfe\x37\xb1\x8b\x10\x33\x97\xa6\xe2\xd4\xe9\x0f\x33\x97\x45\xe1\xdc\xae\xdf\x98\xdb\x94\x0e\x17\xe3\xd6\xd5\x4e\x6e\xac\x35\xb0\xdb\x17\x60\x32\xae\x70\xd8\x1f\xb3\x3e\x1c\x38\xad\x0f\x87\xe3\x4e\xa0\x16\xb6\xb3\xbb\xf1\xb8\x53\xc2\x74\xdc\xb9\xe1\x64\xdc\x1b\xd4\xea\xe0\xb2\x32\xa9\x14\xd6\xd2\x6c\xcc\xad\x49\xc5\x70\xd0\xc9\xc7\x3c\x4e\xad\x24\x80\x45\x1b\xd9\xe5\x47\x3d\xaf\xc1\x53\xb6\x7e\xbf\x46\xd5\x8c\x55\x68\xcd\xe7\x3c\xab\x20\x1f\xd1\xcb\x6f\x9e\xbd\x46\xe5\xe2\xac\x7e\x26\xa2\xc9\x68\xf0\xf4\xc1\xcb\xde\xc3\xc5\xed\xc5\xc4\x29\x6a\x0f\xfe\xc3\x03\x8a\xea\x0b\x7c\x56\x5f\xa6\x7a\x43\x4f\xfd\x2a\x2b\xc8\x2f\xf5\x67\xf1\x65\xaa\xf5\xa7\x4f\xb9\x96\x55\xe9\xdb\x47\x2f\x65\x62\x2c\x24\x13\xbf\xb8\xdf\xa8\x12\xb5\x9b\x07\xaa\xe4\x17\x2d\x4b\xca\x55\x9f\xa8\x72\xa7\xd6\x7b\xcf\x2f\x9b\x14\x60\xef\xf9\xa5\x21\xf5\xdd\x7b\x7e\x59\xe7\xd5\x7b\xcf\x2f\xcd\x69\xf5\x04\x0e\x29\xa2\x30\x42\x69\x59\xad\x11\xcb\xb2\xe5\x2a\x2f\x17\x27\xa8\x5a\xa2\xe7\x0f\xb1\x11\xee\x37\x25\xa4\x02\x7a\xd3\xcf\x81\x6c\x7a\x3b\x24\x8c\xec\x6f\x87\xb4\xe0\x9e\x2f\x05\xc0\xe7\x0f\xf1\x9b\xf2\x2d\xba\x83\xb0\x21\x47\xa9\xc2\x2b\xd3\xf3\x4f\xea\xde\xbd\x69\xdb\xab\x74\x7c\xe2\x3f\x13\x1f\xa3\x3b\x1a\x68\xc8\xc3\xb7\x87\x6e\x0e\x00\x1b\x12\x96\x3e\x58\xaf\xf9\x69\x3a\xe7\x08\x47\x68\x7d\x9e\xbe\xe7\x97\x06\xf6\xaf\xcf\xd3\xef\xf9\xe5\xba\x11\x41\xfb\xdd\xce\x94\xc5\x4b\xa8\x24\x59\x53\x7f\xb9\x8f\x70\xd4\x7c\xb3\x3f\xb1\xf2\x10\x32\x4e\x29\x7a\xcc\x8c\x5c\xd7\xd0\x15\x2d\x6f\x14\xd0\xb7\x8a\x28\x23\x5c\xf7\xd3\x2d\x69\x59\xbd\x84\xac\x28\x47\x5a\x12\x94\x06\xae\x0d\xa4\x54\xa8\x80\x1a\x15\x8a\x0c\xdb\x98\xb4\x86\x04\x76\xad\xe9\xe2\x29\x56\xcb\x53\x70\x30\x73\x5e\x54\x88\x50\xb0\x0c\x81\xd9\xdc\x50\x32\xe7\xcd\xa4\x44\x87\xf2\x6d\x08\x0f\x12\x38\xd6\xca\x35\x99\x3c\x7f\x48\x94\x0e\xee\xa1\xfd\x86\x03\x7b\xe8\x6f\x88\xd0\xb7\x90\xe3\x11\x74\xab\x44\x7f\x83\x37\x2e\xb6\x26\x6f\x55\x9e\xcc\xb6\xa7\x2f\x80\xf4\x9d\x2d\x91\x7b\x1d\x2a\x09\x85\x62\x49\x2b\xda\x47\x24\xb0\x10\xbc\x67\xa0\x78\x80\xd6\x94\xd9\x5f\x74\xa0\x5c\x64\x1c\x71\x96\xcd\x94\xda\xa1\x72\x8d\xd8\xd9\xd9\xbc\xe4\xb9\x90\x25\x5b\x20\xbe\x39\x63\x8b\x9c\xe7\x75\x5e\x46\x70\xef\x53\x23\x34\xc1\x02\x05\x26\x63\x0b\x94\x72\x94\xae\x96\xef\xf9\x02\x95\x8b\x6a\x89\xa8\x4c\x0a\xbc\x46\xeb\x8c\xcd\x25\x78\x09\x72\x6d\x86\x76\x31\x2b\xb3\x19\x62\xf3\xf9\xf2\x62\x0d\xa0\x05\xdc\x6a\x29\xc0\x9e\xaf\x79\x8e\x2e\xca\x6a\xb6\x3c\xaf\x24\x81\xeb\x72\xb9\x18\x42\x51\x8c\x86\xf4\x9a\x93\xf6\xcb\xfd\xfb\xea\x59\x99\xf6\x27\xe1\x50\x7c\x6c\xe2\x5c\x47\x73\xb1\xd4\xdc\xd8\xad\xb8\x0a\x2c\x38\xb1\xf6\x33\xf8\xac\x49\x29\x85\x78\x1b\x09\xe9\xfb\x66\x51\xd9\xfa\x11\xeb\xfd\x88\xdf\xaa\xc4\x9e\xbf\xe9\x3f\xc1\xa3\x00\x83\xa7\x76\x0c\x1e\xf0\xa1\x4c\x7c\x89\xca\xc5\x07\xbe\x5a\x73\xbb\x17\x2c\x17\x1f\x5e\xf6\x1c\x61\xe7\xa7\xad\x06\x08\xec\x18\x20\x5a\x68\x3a\xc7\xd6\x6f\x70\x28\x14\xba\x0f\xfd\x63\x67\xc1\xa1\xfd\xc2\x17\xd9\xea\xf2\xac\xda\xe1\x29\x40\x95\xb1\x76\xf9\xb0\x69\xd7\x56\x9e\x76\x5d\xbe\x35\x85\x6e\xce\x3f\x07\xd6\x96\x23\xae\xdc\xbd\x0f\xdd\x98\xa7\x35\x23\x4d\x41\xc7\x7f\xf0\x4a\x8f\xd3\xba\xc4\xcd\x01\xa8\xf6\x34\x56\x5f\x06\xb2\xda\xaa\x5f\x0d\x5e\xce\x32\x44\x1f\xdf\x2d\xca\xaa\x64\x73\x3d\xf5\x55\xb7\x0e\xdf\x64\x33\xb6\x38\xe1\x4f\x5e\xb4\x69\x51\x65\xe6\x31\x6f\xe3\x15\xf2\x7f\x7d\x95\x36\xb7\x91\xef\x53\xc3\x8c\xb5\x28\xac\x6d\x5e\x3c\xd1\xdb\x10\xc0\xe3\xab\xbf\xed\xda\x50\x49\x9b\x57\x14\xe2\xff\x5b\xd2\x06\x6d\x42\xf5\x67\xcc\x4c\xeb\x7a\xaa\x4d\xa6\x0f\x03\x8b\x92\x1f\xa5\x55\xc1\xe7\xf1\x67\xdb\x0c\x23\x91\x31\x9e\x00\x70\xb6\x67\x2f\x1a\xc5\xd0\xf5\xc4\x52\x77\xd5\xad\xbb\x52\x75\x8d\x44\x3e\xe6\xe5\xba\xe2\xf3\x46\x8b\xcd\x10\x0b\xe8\xfc\x76\xa1\x05\x75\x3b\xe8\x42\x0c\xb4\x32\xd5\xda\x9b\xf2\xed\x9b\xc9\x44\x51\xfb\xae\x75\xd7\x22\x90\x6c\xa6\x2e\xf0\x1d\xd2\x6a\x9b\x58\x63\x70\xd8\x3d\x43\x5a\xd9\x38\xd5\xb3\xa4\x79\x4d\x46\x31\xee\xc0\xff\xbe\xc8\x97\x68\x7d\xc1\xce\x64\xf8\x31\x67\xeb\x4a\x2a\xc3\xd0\x85\x57\x6e\x91\xf5\x88\xed\x0a\xcc\x65\xf8\x95\x41\x87\x21\xa3\xf8\xae\xa6\x3e\x30\x8d\x6b\x33\xc1\xab\x98\xfa\x55\x5c\xca\x88\xeb\x32\xcc\xc8\x2a\xb4\x3c\xaf\x06\x1e\xb8\x71\xb9\x6e\x91\x75\x5c\xae\x5d\x66\x9d\x21\xe3\x3d\xbf\x94\x29\xa0\xa3\xe0\xd0\x27\x7a\x49\xf9\xc1\x52\xa0\xe5\x8d\x8e\x8c\x59\xa3\x0f\xd1\x4b\xa1\x81\x6a\x12\xb0\x5a\xae\xd7\x6d\x98\x0e\x39\x0f\x21\x20\x86\x69\xa9\x6c\xd1\x0c\x54\x2d\xe3\x26\xf5\x78\x75\xca\xd6\xef\x3b\x26\x5b\xeb\xee\x64\xd2\x51\x51\x61\x88\xf5\xe8\xfa\xae\xd3\x75\x61\xb4\x02\x8a\xc6\x82\x8e\xca\xbe\x03\x9d\xfd\xca\xa8\xf8\xa2\x4c\x44\x54\x12\xb2\xaa\x55\xdb\xdd\x80\xec\x17\x4f\xb6\x27\x7b\x65\x27\x7b\xee\x26\x7b\xee\x20\x7b\xb5\x05\xd9\xce\x24\xd2\xeb\x3a\x8b\xb4\x5c\xfe\xd8\x2e\x8f\xf4\x58\x12\x66\x09\xab\xe2\x9b\x4a\x4f\xc5\xfc\xed\xa3\x97\x07\x2a\x40\xeb\xe4\x62\x9e\xa2\xac\x38\x31\x24\xd7\x3e\x9b\x33\x41\xc4\xa6\x42\x7d\x28\x2a\xe0\x9a\xb4\x78\x4c\x80\x9a\xcc\xce\xc3\x85\x9a\x6e\xd2\xed\x6f\x1f\xbd\x34\x66\xdc\x7e\xb5\x2a\xcf\xe6\xfc\xce\x6e\x4b\x44\xb2\x51\x67\xa1\x48\xff\xe9\xcf\xb3\x5c\xa4\x16\x22\x04\xd9\x25\x64\x28\xcd\xfa\xcf\x03\xa9\x28\x96\xaf\x31\x3a\x12\xf5\x0e\x24\x57\x1f\x49\x19\x2f\x57\x93\xf6\x9d\x75\xf5\x70\x7c\x8d\xfa\x60\x3d\x2f\x33\x3e\xf1\xa6\x88\xec\x0d\xde\xc2\x68\xc0\x92\x2b\x82\x25\x53\x14\x38\xc0\xfa\x57\x04\x1b\x4c\x51\xb4\x67\x7f\x48\xe3\xca\x73\x0f\xbe\xc6\x07\x7a\x63\xad\x85\x95\x33\x07\xfa\x9c\x63\x8b\x06\xfe\x16\x18\xae\x67\x4e\x23\x70\xed\x48\x1c\xd9\xb5\xfb\x78\x0b\x0c\xe6\x51\x0f\x27\xe4\xda\x86\xbd\x7f\x12\xb7\xda\x78\x97\x6b\x70\xae\x2d\xac\x1d\x5d\xac\xcd\xc5\x75\x1d\x6d\x53\xcb\x99\x3f\xbf\xa9\xd5\x4b\xa1\xaf\x25\x66\xbf\x1b\x92\x69\x2f\xab\xbe\x96\xdc\xfd\x6e\x18\x4c\xdb\xac\xee\x77\xc3\x68\xaa\x92\xbd\xdf\x8d\xf0\xc7\xb7\x53\x1a\x7c\x52\xc2\xfd\x3f\x32\xd3\xfe\x67\xcb\x87\xff\xdf\x93\xd9\x1e\x5e\x2a\x28\x17\x3c\xbf\xde\x14\xf7\xdf\xb0\x35\x6f\xb3\xd6\xb3\x35\xd7\xca\x5e\xfb\xc4\x99\x01\x7f\x68\xcb\x9b\x28\x40\x0b\x76\xca\xd7\x67\xba\x95\x1e\xea\x64\x88\x2a\x82\x0c\xf9\xdf\x5f\x3f\x9a\xc0\x3c\x40\x51\xd0\x3c\x61\x63\x02\xf3\x3a\x0a\x04\x1d\x40\xd4\x26\x0a\x0e\xd4\x17\x41\xbf\x21\x32\x68\x41\x4b\xf0\x6a\x39\xa5\xfc\x85\xaf\x11\x43\x0b\x7e\x31\xbf\x44\xd2\xd6\x72\x13\x62\xdd\xa1\xa0\xce\x6b\x1e\x8b\xf3\xd3\x94\xaf\x3e\x22\x78\x55\x0a\x5e\x55\x11\x1f\x7c\x02\xe1\xfc\x81\xb3\xc9\x7c\x79\x01\x2d\xc4\x7f\x4d\x0d\xba\x8d\xbb\xde\x6d\x58\xa1\xe6\xcb\xa6\xe5\x4b\xed\x11\x6a\xf6\xd4\x03\xb3\xdc\xfd\xf3\x88\xe7\xc3\xac\x2c\xf0\x42\x2f\xf2\xba\xeb\x9d\x35\xa7\xc1\xc5\x2f\xca\x4e\x44\x25\x7a\x38\x15\x54\x9b\xc7\x30\xf5\xbe\x96\xe1\x55\x4f\x28\x16\xbd\x3d\x42\xdd\xd7\xb7\xf5\x99\x79\x5f\x52\xdf\x94\xd5\x45\xb9\xe6\xe8\x87\x67\xaf\xd6\x00\x61\x4c\x30\xf5\x43\x29\x4a\x41\x3e\xa2\x07\x42\xbe\x82\x2f\x77\x80\x31\x6a\x24\x61\x45\xc5\x57\x68\xc1\x4f\x58\x55\x2e\x4e\xae\x81\xf1\x00\x8a\x0b\xc6\x2b\x11\x1c\x2c\x96\xd5\xc4\xca\xd5\xc3\x43\xb4\x58\x8e\x46\xaa\xf0\x26\x8b\x64\xe8\xef\x0d\x77\xef\x19\xab\x49\xc6\xfe\x5e\x33\xd9\x10\x92\x2a\xce\x28\xc6\xd4\xda\xd0\x8a\xf3\x5e\x87\xba\x4e\x04\x60\x93\xca\x83\x1f\xbe\xd5\xa4\x02\xdb\x09\x30\x6e\x9f\xb1\x35\x6c\x2f\x6c\x65\x43\x8d\xa4\x00\x86\x30\x89\x46\x58\xd5\x52\xa0\xa8\xe1\x5e\xb3\xf0\x1f\xfc\xf0\xed\xf5\x88\x5e\xee\xed\xb4\x82\x67\x8b\x7c\xc2\x16\xcb\x6a\xc6\x57\x8a\x10\x97\x1a\xb0\x45\xae\xab\x81\xe8\xe1\x88\x2a\xb4\x76\x76\x53\x32\x64\x4c\x2b\x1a\xcb\x53\xf5\xff\x30\xfd\x78\xf6\xe2\x73\xab\xc7\xb3\x17\x9f\x49\x3b\x9e\xbd\xb8\x1e\xe5\x58\xae\x3a\xba\xb1\x5c\xed\xa0\x1a\xcb\xd5\x95\x35\xe3\xb7\x1d\x35\xe3\xb7\x3f\x58\x33\x5e\x7f\x7e\xd5\x78\xfd\xd9\x74\xe3\xf5\x75\x29\xc7\xa6\xa7\x1d\x9b\x9d\xd4\x63\xf3\x09\xfa\xf1\x6e\x47\xfd\x78\xf7\x07\xe9\x07\x6c\xca\xeb\x9a\xb1\x90\x2b\xa3\x6a\x42\x38\xe7\x45\xb5\x7d\x54\xb6\x00\x9d\x90\xdf\xd0\xb2\x68\x20\xc1\x13\x36\xd7\xa5\x0c\x00\xec\x7a\xd4\x01\x40\x75\x14\x02\x7e\x79\x32\x21\xa1\x4b\x0f\x64\x25\x5d\x15\x16\x26\x3d\x10\x53\xa0\x05\xba\x8f\x7c\x62\xdb\xe9\xd2\x34\x65\xd2\xaa\xca\xfd\xfb\x68\x01\x5b\xe4\x8d\x32\xc8\xa3\x43\x04\xdd\x41\x0b\xe3\x63\xf5\x66\x15\x12\x70\x86\xba\xf6\x11\xd5\x93\x27\x37\x41\x3a\x98\xc9\x02\xdd\x31\xbc\x18\x3a\x40\xdd\xdf\xea\x12\xe8\xfe\x3b\xb5\x17\x96\xf2\xff\xed\xd4\xf7\xc5\xc4\x3e\xb9\xa8\xb5\xf7\xc5\x35\x69\xaf\x94\x7b\x57\x53\x35\xe5\xad\xf5\x79\x0b\xe5\x1d\x78\x4c\x00\x75\x05\xfd\xd5\xac\xa0\x81\x33\xae\xc0\x0a\xfd\x1f\xae\xc1\x2f\x96\x15\xab\xf8\xe7\x76\xc0\x2b\xc0\x72\x5d\x2a\x0c\xd0\xae\x47\x85\x25\x61\xba\x0a\xaf\x96\xa3\xfe\x57\x54\x19\xd5\x5f\xd5\x23\xd0\x03\xe5\xd5\x17\x7b\x22\x1c\x6c\x7f\x79\x31\x89\x82\x81\x5a\x7e\xaa\xc0\xae\xc9\xe7\xfc\xb9\x24\x36\xe2\x72\x44\x8d\xdd\x05\xf6\x62\x20\xb0\x27\x57\x11\xd8\x83\x3c\xff\xdc\x91\x2f\xcb\xf3\xcf\x14\xf9\xca\x27\xbf\xaf\x63\xce\x9c\xf7\xe6\xcc\xf9\x4e\x73\xe6\x7c\xeb\x39\x73\x7f\x44\xd8\x6f\x02\x59\x38\x30\x6a\x0e\x7e\x33\xb6\x5a\x5d\x8a\x66\xf5\x18\x22\x1f\x86\xef\x0c\x2b\xed\xf3\xf0\x66\x18\xc3\x40\x6a\xbf\x8d\xb9\xd1\xbe\xc4\xa1\x68\xf8\x54\x8f\x2e\xbf\x99\x77\x57\x1e\x2c\xd4\x13\xe0\xcb\x42\x5f\xdb\x5c\x9b\x5e\x38\x5e\x2d\xcf\xf8\xaa\xba\x44\xbf\xaa\x27\x86\xa1\x22\xa8\x57\x03\x62\xb0\xac\xa8\x14\x64\x7d\x60\x82\x53\xbb\x95\xe6\x4d\xf4\xae\x77\x59\x97\x27\x8b\xb2\x28\x33\xb6\xa8\x50\x0a\xe5\xe5\x42\xb3\x0d\x40\xea\x58\xfd\x6d\xd7\xa5\x6b\x62\xea\x5f\xae\x61\x1d\x78\x48\x81\xdd\x1c\x3b\xec\x9a\x3c\x3b\x13\x6a\xc9\xe6\x7b\x1d\xde\x8f\x32\x0e\x19\x1d\x72\xc3\x39\x0d\xec\x56\x4c\xe4\x5d\x31\x7f\x82\xad\x5e\xe8\xac\xee\xf7\xa2\xb3\xe7\xdb\xb5\xd9\x4f\x04\xf6\x66\xd0\x5e\xfc\xed\xba\xac\x3d\xdd\x15\x0a\xa6\x38\xc1\x0c\xa7\x70\xa7\x26\xc3\x39\xe6\xb8\xd8\x1b\x00\x79\xfb\x6f\xd4\xd5\x29\xc2\xde\xd6\xdb\x03\xa0\x74\xd3\x46\x6d\x07\x6e\xf9\x42\x1d\x9e\x00\xb7\x58\x7f\x91\xff\xfd\xed\x37\xc3\x05\x0c\x11\xf7\x37\x36\xf0\x97\x23\x34\xdc\x05\xd3\xff\xe4\xd8\x5c\x57\x3f\x6a\xc8\xe8\x9f\x05\xb4\x06\xed\x7d\x00\xd2\x86\xe6\x7c\x71\x52\xcd\xd0\x6d\x44\xb7\x3c\x4a\xdd\x77\x34\x0f\x97\x8b\x0f\x7c\x55\x4f\x0d\x35\x37\xac\xfc\x83\x18\xb4\xeb\xdb\x01\x5b\x39\x9e\x7a\xd4\x6e\xa4\xdb\xd9\x99\xfb\x88\x5e\x75\x9d\xe8\xad\x35\xca\x59\xc5\x10\x5b\xef\x88\x67\xeb\x95\xac\xee\x4e\xe1\x46\x73\xd0\x07\xd5\xf2\xb5\x4f\xec\x5b\x21\x50\xfc\x09\x67\x76\x14\xae\xae\x52\x19\x4e\xee\xd4\xf5\x9e\x48\x61\x36\x44\xd6\xe2\x35\x9d\xe2\x91\x62\x33\xc0\x92\xdd\xdd\xfa\xf0\x7e\x17\xb7\xfb\xa6\x57\xbb\x85\x57\xb7\x7a\x33\x38\xc2\x2f\xfe\x6a\x1a\x0e\xce\xce\xd7\xb3\x49\x1d\x48\x89\x18\xc1\x34\xaf\x34\xd7\xee\xc5\x12\xc8\x70\x4e\xb6\x0e\x45\x34\x01\xd7\x1e\xa4\x86\x39\xed\x9a\x8d\xf5\x20\xc9\xc0\x2a\x00\x8c\x50\xc9\x6c\x79\x06\x83\xa4\x65\xec\x47\xa3\x61\x6b\xa3\xf6\x1c\x65\xf3\xe5\xc2\x35\x53\xd9\x56\xa5\x01\x4e\x5f\x97\xe1\x47\xbb\x2e\x43\xb1\x53\x97\x75\xc8\x10\xa5\x48\x72\x9b\x93\xaf\xa6\x93\xae\x0f\xa1\xfe\x5f\x41\xb1\xff\x2a\x39\x33\x04\x5a\xfb\x52\x09\x6f\xe8\x66\xeb\x53\x63\x76\x04\x70\x87\xa9\xde\x58\x97\xc1\x89\x05\x4d\x63\x42\x17\x1d\xfb\x19\x35\x83\x8b\x6d\x6c\xe0\x42\xa9\x7c\x0d\xfe\x4d\xf9\xd6\xc4\x76\xbb\xaa\x42\xe5\xce\xfe\x72\x13\x1e\x5b\xcf\xcd\xf4\x4e\xcb\xa8\xa3\x31\x1f\xdf\x4e\x69\xb8\xcd\x79\x97\xc3\xdb\x7f\x41\xb3\xaa\x3a\x5b\xdf\x3d\x3c\x3c\xad\x66\xeb\x83\x94\x1f\x9e\x57\x05\xfd\x79\x8d\x3e\x90\x03\x7c\x40\x50\x7a\x89\xfe\xc7\x29\xab\x66\x25\x5b\x0b\x8d\x69\x0f\xc8\xc0\xa9\x10\x79\xd8\xe3\xf0\x10\x7d\xcb\x2b\x79\x1d\x8e\x73\xc1\xee\x92\xa5\x73\xbe\x46\xff\x50\x98\xfe\x71\xe3\x2b\x38\xc6\xbf\xe2\xfc\x51\x73\xfe\x65\x70\x92\x06\xdd\x92\xc2\xbb\x85\x6e\xde\xac\x7f\xbe\x67\x07\x8f\xfe\x21\xbb\xa3\x01\x7f\x0a\x3f\xb4\xb0\x4f\xd5\xf7\x2e\x68\xf5\xeb\xcd\x9b\x86\xf3\x39\x47\x1d\x22\x9b\xca\x4e\x32\x4e\xe0\xe4\xcc\x3f\xa6\xf2\x34\xfe\x0f\xcb\x9c\x1f\xfc\xbc\x46\xcb\x15\xfa\x46\x1e\xa5\x29\x8b\x92\xe7\x28\x5b\xe6\x7c\x0a\x50\xd8\x22\x47\xe7\x6b\x8e\xca\x4a\x8c\x6b\xff\x10\x7c\xd4\xfa\xa0\xce\xe1\x34\x7d\x38\x51\xdf\xbb\x7d\x90\xbf\xde\x93\x67\x92\xda\x66\x07\x4d\xed\x23\x1d\xd8\x6f\xbf\x69\xdf\x0e\x2e\xca\x45\x2e\x66\x97\x9d\x3a\xf2\xe8\x90\xa0\x05\xe9\x3f\xc3\x61\x9f\x1b\x5f\x1d\xde\xbe\x73\x6d\x7f\xb7\x0f\x6f\xc8\xde\xae\xab\x55\xb9\x38\x79\xbc\x5a\x9e\x3e\x9c\xb1\xd5\xc3\x65\x2e\x24\xf7\x12\x7e\x3c\x28\xb4\x5f\x15\xf3\x5f\xb1\xf7\x7c\x21\x79\xdc\x57\xd9\xb3\xf3\xc5\xa5\xe0\xef\x8d\xaf\x1a\x0f\x76\x9e\xad\x49\xce\xc5\x8f\x13\x89\x47\x76\x10\xb6\x36\xe1\xf0\x7d\x3d\x04\xc2\x4f\xd9\xf2\x7c\x51\xf1\x95\x5a\xb9\x84\x9f\xe6\xb5\xaf\x90\xcd\x5b\x67\x01\xa5\x70\x9f\xb1\xfe\xc2\x37\xd5\x8a\x89\x2f\x17\xb3\x72\xce\xd1\xa4\x86\x76\x5f\x01\x91\xa8\xbf\x82\x36\x2d\xc0\x4c\x75\xef\x41\x55\x37\xd8\xdf\x17\xa6\xfe\x15\xc8\x54\x56\xfe\xfa\x08\x79\x9b\x6f\xa9\xe7\x09\x99\xcb\x9f\xee\xc3\x4f\xdf\x3c\x7e\x2c\x7e\xb2\x60\x12\xec\x82\xe9\xfa\xfa\x7c\xb5\x5a\x9e\xb0\x8a\x4f\x41\xeb\xaa\x19\x5f\x71\xb8\xe7\x89\x16\x7c\x53\x21\x41\x02\xcb\x2a\xbe\x82\x46\xd0\x8d\x6d\xe8\x03\x02\x27\xb2\xfa\x4d\xe4\x6d\x1e\x3f\xf4\xbc\x3d\xa1\xa1\xde\xe6\x5b\xf8\xf8\xab\x70\xce\xf3\xe5\x45\x8b\x1f\x9a\x7d\x25\x39\x2f\x87\xf2\x89\xea\xa2\x00\xe0\x3f\x7e\xbc\x07\x57\x33\xbd\x3d\xb4\x8f\x34\xc8\x50\xb0\x5f\x67\x1c\x52\xd8\xdb\x28\x58\x75\xf5\x7c\x71\xca\xaa\x6c\xc6\xf3\x16\xdf\x3d\xb4\x5c\xcc\x2f\x11\x3b\x3b\xe3\xd0\xef\x72\x0d\x06\x88\xce\x17\x65\x35\x15\x13\xcd\x8c\xad\x39\xcc\x36\x05\x23\x1a\x48\x4d\x1d\xc1\xa4\xaa\x3e\x17\xd5\x40\x15\x43\x3d\xd3\xbe\x9e\xb1\x72\x35\xec\x19\xf4\x4b\xd1\xfa\x95\x62\xdd\x9d\x3b\x8a\xf6\x1b\xfd\x0e\x58\x5a\x8a\x8a\xe2\xff\xca\xdf\xcb\x5a\xb5\x35\x5e\xc5\x18\xf8\x02\x8c\x01\x46\xe1\xd6\x16\x1a\x2d\x97\x71\x4b\x57\xc9\xcb\x45\xce\x37\xe8\x08\xdd\xc1\x46\xb5\x6f\xec\xe8\xd6\x2d\x4d\xf9\xf7\xf7\x65\x33\x8b\xf2\x03\x9e\x37\x50\xe5\x6d\x5f\xd9\x85\x2a\x3d\x16\x12\x97\x9c\x91\xbf\xde\x39\xaa\xc5\x7f\x4f\xe3\x17\xda\x3f\x32\xf8\x8f\x1a\xd0\xd7\x5f\x23\xec\xd5\x0a\x84\x7e\x53\x36\xa4\x44\x52\x53\x22\x95\x15\xfd\x86\x3a\x7a\xd8\x30\x7f\x0b\x44\x00\xd0\x26\xa4\x86\xf9\xd9\x8c\x67\xef\x5f\x66\x6c\xce\x56\xff\x4b\xb4\x9a\x08\x39\x3c\x5f\x96\x0b\x79\x9a\x1a\x18\xd0\xfc\xd4\xb5\xf8\xf6\x67\x69\xf5\x2d\x73\xaa\xd9\x6a\x79\x81\x1e\xad\x56\xcb\xd5\x04\x7a\x75\xeb\x89\x08\x85\x5a\xd5\xfc\xfb\xfe\x2d\xb4\xdf\x02\x38\xa8\x96\xd2\xb3\x4e\x70\xb4\x77\x50\x2d\xff\x7e\x76\xc6\x57\x0f\xd9\x9a\x4f\xf6\xd0\xbe\x04\x20\x54\x7e\xb1\xac\x84\x82\x03\xb1\x92\x2f\xb7\x44\x61\xdd\xd1\x8f\x9f\x61\x24\x68\xf9\x04\x51\xb5\x88\xc4\x5b\x76\x4c\xe5\x36\x9b\x1a\x9c\x24\x97\x0d\xd2\x98\xe8\x0c\xfc\xba\x6e\x23\x25\x0a\x4b\x95\x1b\xea\xed\xf5\xe5\x22\x0d\xe2\x61\xdd\xd0\x24\x16\x0d\xec\x4d\xa5\x9c\x8f\x1f\x53\xe5\xeb\x94\x9b\xc3\x77\xd2\xcb\x8a\xa3\x35\xff\xaf\x73\xbe\xc8\xc0\xd1\xd9\x09\x6d\x71\xd4\xaa\x03\x03\xe1\xe5\x69\xba\x9c\x37\x86\x64\xc3\x4c\xbd\x2e\x66\x32\xc4\xdc\x40\x1a\x67\x52\x24\x19\x84\x15\x83\x1e\x7a\x0d\x49\xcd\xc1\x63\x03\x11\xe0\x86\x75\x22\xfc\x21\x11\x0e\x85\xbf\xb7\x23\x91\x98\x48\x2a\x3d\x45\xe5\x23\xaf\x03\x62\xff\xc8\xa2\x35\xd1\x16\x9d\x79\xe4\x0d\x3a\x13\x7c\x12\x47\x31\x55\xc4\xc6\x92\xd8\xc7\x5b\x12\x8b\xc9\xae\x9d\x6a\x6b\x9a\xa8\xea\x76\xb4\x6b\x01\x8d\x6e\x02\x84\xbe\x49\x88\xd0\x5f\x8d\x13\xfd\xa0\xa9\x01\x2a\x42\xf7\x61\x70\x35\x88\x9a\xda\xfa\xa3\x83\x4a\x53\xb5\xfe\x41\x08\x41\x7a\xab\x2d\x07\x97\xb6\xc7\x3a\x62\x7d\x94\xd1\x40\xee\x1f\x39\x4c\xbf\xe7\xd1\xdb\x66\x9f\x2b\x10\x6e\x78\xbf\xe2\x2c\x7f\xb8\x5c\x54\xe5\xe2\x1c\x2e\xcf\x82\xf4\x5b\x57\x24\x28\xf9\x0e\xfa\xfe\xf5\x11\x90\xf5\x50\x04\x16\x86\xd1\xe0\xd6\x77\x8b\x0f\x6c\x5e\xe6\x50\x49\x72\xfb\x96\xea\x56\xc3\xef\x2e\x16\x24\x01\xc2\x42\xc1\x9b\x06\xcf\x5b\x65\x26\xa2\x69\xf3\xe3\xfe\xbe\x08\xc6\x6b\x0f\xd5\x03\x73\x53\xba\x11\x19\x08\x0a\x2f\xf9\xab\xe6\x0c\x8d\xb5\xfd\xc7\x0d\x61\x87\x87\xe8\xbb\x02\x5d\x70\x24\xe2\xb5\xf3\x33\x24\x22\xd5\x29\x2a\xab\xff\xfb\xbf\xff\x4f\x3d\x2c\xe9\x20\x80\xe2\x1b\x96\x9e\x0f\x2a\xde\x1a\x38\x7f\xa9\xbd\x2f\xc1\x0a\x26\xad\x96\x8b\xca\x58\x57\x43\xa2\x7f\xf1\xf5\x2f\x81\x41\x7d\x87\xb2\xfa\x04\x51\x75\x21\x1d\x0d\xa5\xae\x38\x5b\xb0\x39\x5c\x7e\x68\xf8\xf8\x82\xb3\x1c\x15\xe5\x6a\x5d\xd5\x5c\x82\x6e\xed\x2e\xe6\xe1\xe8\x86\x26\x8b\xe5\x90\xbd\xeb\xbd\x5a\x27\x24\xa2\x9b\x4a\xfe\xca\xb3\x6a\xb4\x36\xfc\xad\x69\x1d\x8e\x61\x3d\x38\x8f\x6a\x85\x7a\x58\x83\x02\xb1\xa0\x23\x8b\xc1\xdc\xeb\xfb\x03\x1d\x18\x96\xd3\x0c\xc8\xb9\xd3\x48\xd7\x14\x80\x35\xda\xdb\xaa\xaf\xe6\xa3\xba\x01\xfc\x0e\x2a\x58\x87\xf5\xb2\xef\x7e\x9f\xb7\xa7\xec\x12\x95\x8b\x6c\x7e\x0e\x93\x10\x31\xb9\xd0\xa7\x34\x26\x2e\x3f\xae\xb9\xf3\x68\x07\xee\x80\x2a\x5f\x8d\x81\x9e\x9a\xa7\x11\x38\x9b\x24\x71\xe9\x0c\xf5\x6d\x0c\xf5\x20\x78\x91\x0c\x1b\x8b\x0f\x3e\x27\xcf\x87\x23\x7c\x9f\xa3\x54\x71\xf4\xf1\xf5\x72\x14\x5c\xc6\x15\x99\x1e\x03\xd3\xbd\x4d\x9f\xed\xde\xc6\x7b\xb8\x87\x7e\x03\x8e\x4c\x24\x0d\xf2\xd7\x46\x1e\x81\x55\x1e\x30\xa3\x32\xcc\x31\xb0\xa7\x4f\xc1\xcc\x92\xa8\xf9\x69\x94\xc2\xdf\x5f\x3d\xbe\x43\x51\x0e\x2b\x65\x3c\x6f\x3c\x6f\xed\x36\xd5\x0d\xac\xe6\x3b\x38\x34\xed\x3b\xf8\x9f\x7b\xbd\x98\x44\xc5\x1a\xed\x68\x2c\xe9\x6b\xe0\x75\x43\x12\xad\x5a\xed\xd5\x00\x8b\xee\x00\xb5\xa0\x44\xf3\xb1\xed\xea\x4f\x27\xdc\x69\xd7\x89\xaa\xd3\x33\x2d\x1a\x99\x54\xa7\x67\xe8\xa8\x37\x96\xec\xa1\xbf\x1c\x1d\x49\xa7\xdc\x8f\x4e\xd4\x26\x46\x75\x7a\xd6\x8f\x33\xb4\x09\x7a\x5b\x7b\xef\x73\x2e\xbe\x09\xb6\xa2\x23\x20\xf0\xd6\x07\xbe\x5a\x97\xcb\xc5\xad\xbb\xe8\x16\x2c\xfa\xde\x9a\x8a\x5f\x25\x3d\xb7\xee\x6a\x51\x21\xfc\x2e\xbb\xab\x7e\x97\x5f\x6e\x7c\xf5\x51\x2d\xd2\xbd\x5c\x9e\x72\xf4\xe0\xe9\xb7\x28\x3d\x2f\xe7\x39\x5a\x9e\x55\xe5\x69\xf9\x0b\x5f\xad\xa7\x68\x5e\xbe\xe7\x68\x75\xf0\xf3\x7a\x2a\xa7\xc4\xb0\xd2\xbe\x3e\xe3\x59\x59\x94\x99\x30\xde\xbc\x04\x81\x9f\xb1\xaa\xe2\xab\xc5\x1a\xe0\x41\xa3\x6a\xc6\x51\xb1\x9c\xcf\x97\x17\xe5\xe2\xe4\xae\x5c\xf3\x14\xea\xd7\xbb\x17\x89\x6e\xd5\x4a\x73\x4b\x2e\xee\x76\x2a\x1c\xb0\xd3\xbc\xb7\x8a\xda\x5c\x91\x14\x65\x37\xbe\x92\xe2\x52\x97\x26\x9b\x65\xee\xee\x00\x26\xfa\x0c\xb2\x03\xe1\xb4\xb3\x8b\xde\xaa\xf1\x5f\xb4\xef\x07\x8b\x65\xce\x5f\x5d\x9e\xf1\x36\x98\x6b\xd7\xaa\xd5\xc4\xa3\x5c\xe8\xeb\xc6\x2f\xca\xc5\xc9\xf2\x7f\xbe\x44\x1f\xbc\x03\x7a\xe0\xc1\xf4\xbc\x6d\xa1\xdd\x25\x6d\x88\x51\xae\xb1\x86\xc4\x56\x17\x33\x36\xef\x41\x8a\x0f\xbc\x3b\x72\x21\x66\x55\x9f\x8d\x92\xb7\x18\xd5\x6f\x33\xb6\x7e\x76\xb1\x78\x5e\x1f\x81\x39\x52\x95\x0e\xba\xbf\x43\xf5\x66\x8b\x04\xb2\xc6\x49\xa6\xd4\x1e\xa3\x5b\x5d\xee\x0f\x89\x72\xb8\x48\xbc\x27\x78\xa3\xf3\xea\xcd\x7b\x99\xc0\x50\xd4\x80\xcf\x9d\xc5\xaf\x5e\xbf\x5e\xcc\xca\xc5\x52\xf4\x8a\xa1\x0b\x9e\x22\x75\x51\x55\xad\x5a\x1f\x28\x85\x56\x3c\xf9\x78\x43\x5d\x51\x85\x6d\x93\x8f\xd3\x5f\x3f\xbe\x9d\xd2\x68\x9b\x2d\x91\xc1\x8d\xdd\xd7\x4f\x9f\x1c\x57\xd5\xd9\x0b\x31\x64\xac\xab\x06\xda\x5f\xd3\xf2\x44\x1e\x66\x39\xf8\x79\xfd\xd7\x6d\x20\xdf\x3a\x5f\x73\x98\xb0\x65\xd5\xad\x7b\x37\x86\x88\xbe\x29\x4f\x7e\x00\x80\xf7\x44\x87\x7f\x5e\xcf\x84\x53\x2e\x4f\x16\xcb\x15\xbf\x3b\x2f\x17\xfc\x46\x83\xfa\x82\xa7\xfe\x56\x28\x85\x90\x7e\xe4\xa9\x1c\x9b\xe4\x35\xe3\x5b\x07\x87\xf3\x32\x3d\x14\x20\x84\x73\xbe\x71\x78\x88\xf2\xe5\xa2\x42\xcb\x0f\x7c\xb5\x2a\x73\x5e\x6f\x38\xd4\xfb\x1b\x37\xb4\x2b\xc8\x6a\xe7\x40\x38\xb8\x5b\xcd\x81\x06\xd8\x8f\xe8\x54\x38\x90\x28\xbb\xb5\x84\x82\xc0\x36\x99\x5e\x05\x88\xbb\x77\xe3\xa3\x81\x1b\xb2\x44\x6d\x6c\xd5\x14\xff\xf5\x2e\x21\x1f\xdf\x0a\x2e\x4c\xdf\x48\x2e\xbc\xdd\xbb\x71\x78\xf8\xff\xa1\xf5\xf2\x7c\x95\xf1\xa7\xec\xec\xac\x5c\x9c\xfc\xfd\xc5\x93\x23\x51\x78\x67\x0e\x87\x48\x7f\x5e\x1f\x9c\xb2\xb3\x1b\xff\x2f\x00\x00\xff\xff\x22\xa6\xc9\x06\x5b\x2c\x06\x00") func web3JsBytes() ([]byte, error) { return bindataRead( diff --git a/internal/jsre/deps/web3.js b/internal/jsre/deps/web3.js index b6a646aede5e..462ffe031f1b 100644 --- a/internal/jsre/deps/web3.js +++ b/internal/jsre/deps/web3.js @@ -3741,7 +3741,7 @@ var inputCallFormatter = function (options){ options.to = inputAddressFormatter(options.to); } - ['gasPrice', 'gas', 'value', 'nonce'].filter(function (key) { + ['maxFeePerGas', 'maxPriorityFeePerGas', 'gasPrice', 'gas', 'value', 'nonce'].filter(function (key) { return options[key] !== undefined; }).forEach(function(key){ options[key] = utils.fromDecimal(options[key]); From c7d49072f1158f3455ae49d816b6229248099189 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 19 Sep 2024 13:58:57 +0800 Subject: [PATCH 090/479] core/vm: evm fix panic (#23047) --- core/vm/runtime/env.go | 1 + core/vm/runtime/runtime.go | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go index 89d44ad83311..d525e8b312a5 100644 --- a/core/vm/runtime/env.go +++ b/core/vm/runtime/env.go @@ -35,6 +35,7 @@ func NewEnv(cfg *Config) *vm.EVM { Time: cfg.Time, Difficulty: cfg.Difficulty, GasLimit: cfg.GasLimit, + BaseFee: cfg.BaseFee, } return vm.NewEVM(blockContext, txContext, cfg.State, nil, cfg.ChainConfig, cfg.EVMConfig) diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index db5a79ae2f3d..14964385c2de 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -43,6 +43,7 @@ type Config struct { Value *big.Int Debug bool EVMConfig vm.Config + BaseFee *big.Int State *state.StateDB GetHashFn func(n uint64) common.Hash @@ -68,6 +69,7 @@ func setDefaults(cfg *Config) { LondonBlock: new(big.Int), MergeBlock: new(big.Int), ShanghaiBlock: new(big.Int), + Eip1559Block: new(big.Int), } } @@ -94,6 +96,9 @@ func setDefaults(cfg *Config) { return common.BytesToHash(crypto.Keccak256([]byte(new(big.Int).SetUint64(n).String()))) } } + if cfg.BaseFee == nil { + cfg.BaseFee = big.NewInt(params.InitialBaseFee) + } } // Execute executes the code using the input as call data during the execution. From 1c47afb2867c9049faa0a8b44612860300e6b0e7 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 18 Oct 2024 18:26:09 +0800 Subject: [PATCH 091/479] core/vm/runtime: add function TestColdAccountAccessCost --- core/vm/runtime/runtime_test.go | 80 +++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index fc986c3a7628..48c707f006e9 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -597,3 +597,83 @@ func TestEip2929Cases(t *testing.T) { "account (cheap)", code) } } + +// TestColdAccountAccessCost test that the cold account access cost is reported +// correctly +// see: https://github.com/ethereum/go-ethereum/issues/22649 +func TestColdAccountAccessCost(t *testing.T) { + for i, tc := range []struct { + code []byte + step int + want uint64 + }{ + { // EXTCODEHASH(0xff) + code: []byte{byte(vm.PUSH1), 0xFF, byte(vm.EXTCODEHASH), byte(vm.POP)}, + step: 1, + want: 2600, + }, + { // BALANCE(0xff) + code: []byte{byte(vm.PUSH1), 0xFF, byte(vm.BALANCE), byte(vm.POP)}, + step: 1, + want: 2600, + }, + { // CALL(0xff) + code: []byte{ + byte(vm.PUSH1), 0x0, + byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), + byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.CALL), byte(vm.POP), + }, + step: 7, + want: 2855, + }, + { // CALLCODE(0xff) + code: []byte{ + byte(vm.PUSH1), 0x0, + byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), + byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.CALLCODE), byte(vm.POP), + }, + step: 7, + want: 2855, + }, + { // DELEGATECALL(0xff) + code: []byte{ + byte(vm.PUSH1), 0x0, + byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), + byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.DELEGATECALL), byte(vm.POP), + }, + step: 6, + want: 2855, + }, + { // STATICCALL(0xff) + code: []byte{ + byte(vm.PUSH1), 0x0, + byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), + byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.STATICCALL), byte(vm.POP), + }, + step: 6, + want: 2855, + }, + { // SELFDESTRUCT(0xff) + code: []byte{ + byte(vm.PUSH1), 0xff, byte(vm.SELFDESTRUCT), + }, + step: 1, + want: 7600, + }, + } { + tracer := vm.NewStructLogger(nil) + Execute(tc.code, nil, &Config{ + EVMConfig: vm.Config{ + Debug: true, + Tracer: tracer, + }, + }) + have := tracer.StructLogs()[tc.step].GasCost + if want := tc.want; have != want { + for ii, op := range tracer.StructLogs() { + t.Logf("%d: %v %d", ii, op.OpName(), op.GasCost) + } + t.Fatalf("tescase %d, gas report wrong, step %d, have %d want %d", i, tc.step, have, want) + } + } +} From 32778572d7f026ecf650b7f16dc9f3472cdc8157 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 24 May 2024 18:19:11 +0800 Subject: [PATCH 092/479] Access list state test format (#22290) --- tests/gen_stenv.go | 2 ++ tests/gen_sttransaction.go | 37 +++++++++++++++++++++++-------------- tests/gen_vmexec.go | 2 ++ tests/state_test_util.go | 21 +++++++++++++-------- 4 files changed, 40 insertions(+), 22 deletions(-) diff --git a/tests/gen_stenv.go b/tests/gen_stenv.go index b4f90f62102f..9248bebc7ab5 100644 --- a/tests/gen_stenv.go +++ b/tests/gen_stenv.go @@ -13,6 +13,7 @@ import ( var _ = (*stEnvMarshaling)(nil) +// MarshalJSON marshals as JSON. func (s stEnv) MarshalJSON() ([]byte, error) { type stEnv struct { Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` @@ -30,6 +31,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) { return json.Marshal(&enc) } +// UnmarshalJSON unmarshals from JSON. func (s *stEnv) UnmarshalJSON(input []byte) error { type stEnv struct { Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` diff --git a/tests/gen_sttransaction.go b/tests/gen_sttransaction.go index d2ff5a9743c1..fdf0d5df2a87 100644 --- a/tests/gen_sttransaction.go +++ b/tests/gen_sttransaction.go @@ -8,25 +8,29 @@ import ( "github.com/XinFinOrg/XDPoSChain/common/hexutil" "github.com/XinFinOrg/XDPoSChain/common/math" + "github.com/XinFinOrg/XDPoSChain/core/types" ) var _ = (*stTransactionMarshaling)(nil) +// MarshalJSON marshals as JSON. func (s stTransaction) MarshalJSON() ([]byte, error) { type stTransaction struct { - GasPrice *math.HexOrDecimal256 `json:"gasPrice"` - Nonce math.HexOrDecimal64 `json:"nonce"` - To string `json:"to"` - Data []string `json:"data"` - GasLimit []math.HexOrDecimal64 `json:"gasLimit"` - Value []string `json:"value"` - PrivateKey hexutil.Bytes `json:"secretKey"` + GasPrice *math.HexOrDecimal256 `json:"gasPrice"` + Nonce math.HexOrDecimal64 `json:"nonce"` + To string `json:"to"` + Data []string `json:"data"` + AccessLists []*types.AccessList `json:"accessLists,omitempty"` + GasLimit []math.HexOrDecimal64 `json:"gasLimit"` + Value []string `json:"value"` + PrivateKey hexutil.Bytes `json:"secretKey"` } var enc stTransaction enc.GasPrice = (*math.HexOrDecimal256)(s.GasPrice) enc.Nonce = math.HexOrDecimal64(s.Nonce) enc.To = s.To enc.Data = s.Data + enc.AccessLists = s.AccessLists if s.GasLimit != nil { enc.GasLimit = make([]math.HexOrDecimal64, len(s.GasLimit)) for k, v := range s.GasLimit { @@ -38,15 +42,17 @@ func (s stTransaction) MarshalJSON() ([]byte, error) { return json.Marshal(&enc) } +// UnmarshalJSON unmarshals from JSON. func (s *stTransaction) UnmarshalJSON(input []byte) error { type stTransaction struct { - GasPrice *math.HexOrDecimal256 `json:"gasPrice"` - Nonce *math.HexOrDecimal64 `json:"nonce"` - To *string `json:"to"` - Data []string `json:"data"` - GasLimit []math.HexOrDecimal64 `json:"gasLimit"` - Value []string `json:"value"` - PrivateKey *hexutil.Bytes `json:"secretKey"` + GasPrice *math.HexOrDecimal256 `json:"gasPrice"` + Nonce *math.HexOrDecimal64 `json:"nonce"` + To *string `json:"to"` + Data []string `json:"data"` + AccessLists []*types.AccessList `json:"accessLists,omitempty"` + GasLimit []math.HexOrDecimal64 `json:"gasLimit"` + Value []string `json:"value"` + PrivateKey *hexutil.Bytes `json:"secretKey"` } var dec stTransaction if err := json.Unmarshal(input, &dec); err != nil { @@ -64,6 +70,9 @@ func (s *stTransaction) UnmarshalJSON(input []byte) error { if dec.Data != nil { s.Data = dec.Data } + if dec.AccessLists != nil { + s.AccessLists = dec.AccessLists + } if dec.GasLimit != nil { s.GasLimit = make([]uint64, len(dec.GasLimit)) for k, v := range dec.GasLimit { diff --git a/tests/gen_vmexec.go b/tests/gen_vmexec.go index f539a288f3c2..e75c23489d99 100644 --- a/tests/gen_vmexec.go +++ b/tests/gen_vmexec.go @@ -14,6 +14,7 @@ import ( var _ = (*vmExecMarshaling)(nil) +// MarshalJSON marshals as JSON. func (v vmExec) MarshalJSON() ([]byte, error) { type vmExec struct { Address common.UnprefixedAddress `json:"address" gencodec:"required"` @@ -37,6 +38,7 @@ func (v vmExec) MarshalJSON() ([]byte, error) { return json.Marshal(&enc) } +// UnmarshalJSON unmarshals from JSON. func (v *vmExec) UnmarshalJSON(input []byte) error { type vmExec struct { Address *common.UnprefixedAddress `json:"address" gencodec:"required"` diff --git a/tests/state_test_util.go b/tests/state_test_util.go index cc1e79223237..36e882b14cd9 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -93,13 +93,14 @@ type stEnvMarshaling struct { //go:generate gencodec -type stTransaction -field-override stTransactionMarshaling -out gen_sttransaction.go type stTransaction struct { - GasPrice *big.Int `json:"gasPrice"` - Nonce uint64 `json:"nonce"` - To string `json:"to"` - Data []string `json:"data"` - GasLimit []uint64 `json:"gasLimit"` - Value []string `json:"value"` - PrivateKey []byte `json:"secretKey"` + GasPrice *big.Int `json:"gasPrice"` + Nonce uint64 `json:"nonce"` + To string `json:"to"` + Data []string `json:"data"` + AccessLists []*types.AccessList `json:"accessLists,omitempty"` + GasLimit []uint64 `json:"gasLimit"` + Value []string `json:"value"` + PrivateKey []byte `json:"secretKey"` } type stTransactionMarshaling struct { @@ -241,7 +242,11 @@ func (tx *stTransaction) toMessage(ps stPostState, number *big.Int) (core.Messag if err != nil { return nil, fmt.Errorf("invalid tx data %q", dataHex) } - msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, nil, nil, data, nil, true, nil, number) + var accessList types.AccessList + if tx.AccessLists != nil && tx.AccessLists[ps.Indexes.Data] != nil { + accessList = *tx.AccessLists[ps.Indexes.Data] + } + msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, nil, nil, data, accessList, true, nil, number) return msg, nil } From e93d78cc09ac54b2ad3aac1e12d6f86bbc2a3a7f Mon Sep 17 00:00:00 2001 From: Martin Redmond <21436+reds@users.noreply.github.com> Date: Fri, 19 Mar 2021 06:56:10 -0400 Subject: [PATCH 093/479] accounts/abi/bind: add NoSend transact option (#22446) This adds a new option to avoid sending the transaction which is created by calling a bound contract method. --- accounts/abi/bind/base.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index 0477d8c59e9e..22425c3f2888 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -58,6 +58,8 @@ type TransactOpts struct { GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate) Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) + + NoSend bool // Do all transact steps but do not send the transaction } // FilterOpts is the collection of options to fine tune filtering for events @@ -242,6 +244,9 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i if err != nil { return nil, err } + if opts.NoSend { + return signedTx, nil + } if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil { return nil, err } From 6b67327a4b713e86bf96993b0a4c46b250b96b1d Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 24 May 2024 19:23:05 +0800 Subject: [PATCH 094/479] tests: update for London (#22976) --- core/gen_genesis.go | 8 +++++ core/gen_genesis_account.go | 2 ++ core/genesis.go | 7 ++++- tests/block_test_util.go | 24 +++++++++----- tests/gen_btheader.go | 8 +++++ tests/gen_stenv.go | 6 ++++ tests/gen_sttransaction.go | 44 ++++++++++++++++---------- tests/state_test_util.go | 62 ++++++++++++++++++++++++++++--------- 8 files changed, 121 insertions(+), 40 deletions(-) diff --git a/core/gen_genesis.go b/core/gen_genesis.go index 82fe48663caf..63ea023f4e82 100644 --- a/core/gen_genesis.go +++ b/core/gen_genesis.go @@ -15,6 +15,7 @@ import ( var _ = (*genesisSpecMarshaling)(nil) +// MarshalJSON marshals as JSON. func (g Genesis) MarshalJSON() ([]byte, error) { type Genesis struct { Config *params.ChainConfig `json:"config"` @@ -29,6 +30,7 @@ func (g Genesis) MarshalJSON() ([]byte, error) { Number math.HexOrDecimal64 `json:"number"` GasUsed math.HexOrDecimal64 `json:"gasUsed"` ParentHash common.Hash `json:"parentHash"` + BaseFee *big.Int `json:"baseFee"` } var enc Genesis enc.Config = g.Config @@ -48,9 +50,11 @@ func (g Genesis) MarshalJSON() ([]byte, error) { enc.Number = math.HexOrDecimal64(g.Number) enc.GasUsed = math.HexOrDecimal64(g.GasUsed) enc.ParentHash = g.ParentHash + enc.BaseFee = g.BaseFee return json.Marshal(&enc) } +// UnmarshalJSON unmarshals from JSON. func (g *Genesis) UnmarshalJSON(input []byte) error { type Genesis struct { Config *params.ChainConfig `json:"config"` @@ -65,6 +69,7 @@ func (g *Genesis) UnmarshalJSON(input []byte) error { Number *math.HexOrDecimal64 `json:"number"` GasUsed *math.HexOrDecimal64 `json:"gasUsed"` ParentHash *common.Hash `json:"parentHash"` + BaseFee *big.Int `json:"baseFee"` } var dec Genesis if err := json.Unmarshal(input, &dec); err != nil { @@ -112,5 +117,8 @@ func (g *Genesis) UnmarshalJSON(input []byte) error { if dec.ParentHash != nil { g.ParentHash = *dec.ParentHash } + if dec.BaseFee != nil { + g.BaseFee = dec.BaseFee + } return nil } diff --git a/core/gen_genesis_account.go b/core/gen_genesis_account.go index 9d3ea26d6814..dd0a6f671967 100644 --- a/core/gen_genesis_account.go +++ b/core/gen_genesis_account.go @@ -14,6 +14,7 @@ import ( var _ = (*genesisAccountMarshaling)(nil) +// MarshalJSON marshals as JSON. func (g GenesisAccount) MarshalJSON() ([]byte, error) { type GenesisAccount struct { Code hexutil.Bytes `json:"code,omitempty"` @@ -36,6 +37,7 @@ func (g GenesisAccount) MarshalJSON() ([]byte, error) { return json.Marshal(&enc) } +// UnmarshalJSON unmarshals from JSON. func (g *GenesisAccount) UnmarshalJSON(input []byte) error { type GenesisAccount struct { Code *hexutil.Bytes `json:"code,omitempty"` diff --git a/core/genesis.go b/core/genesis.go index c45ec967a99c..0e552fb84e12 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -61,6 +61,7 @@ type Genesis struct { Number uint64 `json:"number"` GasUsed uint64 `json:"gasUsed"` ParentHash common.Hash `json:"parentHash"` + BaseFee *big.Int `json:"baseFee"` } // GenesisAlloc specifies the initial state that is part of the genesis block. @@ -265,7 +266,11 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { head.Difficulty = params.GenesisDifficulty } if g.Config != nil && g.Config.IsEIP1559(common.Big0) { - head.BaseFee = new(big.Int).SetUint64(params.InitialBaseFee) + if g.BaseFee != nil { + head.BaseFee = g.BaseFee + } else { + head.BaseFee = new(big.Int).SetUint64(params.InitialBaseFee) + } } statedb.Commit(false) statedb.Database().TrieDB().Commit(root, true) diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 905c81b0d3e6..44d1c1b4d72f 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -21,17 +21,16 @@ import ( "bytes" "encoding/hex" "encoding/json" - "errors" "fmt" "math/big" - - "github.com/XinFinOrg/XDPoSChain/core/rawdb" + "os" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/hexutil" "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/consensus/ethash" "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" @@ -58,9 +57,10 @@ type btJSON struct { } type btBlock struct { - BlockHeader *btHeader - Rlp string - UncleHeaders []*btHeader + BlockHeader *btHeader + ExpectException string + Rlp string + UncleHeaders []*btHeader } //go:generate gencodec -type btHeader -field-override btHeaderMarshaling -out gen_btheader.go @@ -82,6 +82,7 @@ type btHeader struct { GasLimit uint64 GasUsed uint64 Timestamp *big.Int + BaseFee *big.Int } type btHeaderMarshaling struct { @@ -91,6 +92,7 @@ type btHeaderMarshaling struct { GasLimit math.HexOrDecimal64 GasUsed math.HexOrDecimal64 Timestamp *math.HexOrDecimal256 + BaseFee *math.HexOrDecimal256 } func (t *BlockTest) Run() error { @@ -149,6 +151,7 @@ func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis { Mixhash: t.json.Genesis.MixHash, Coinbase: t.json.Genesis.Coinbase, Alloc: t.json.Pre, + BaseFee: t.json.Genesis.BaseFee, } } @@ -168,7 +171,7 @@ See https://github.com/ethereum/tests/wiki/Blockchain-Tests-II func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) { validBlocks := make([]btBlock, 0) // insert the test blocks, which will execute all transactions - for _, b := range t.json.Blocks { + for bi, b := range t.json.Blocks { cb, err := b.decode() if err != nil { if b.BlockHeader == nil { @@ -188,7 +191,12 @@ func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) } } if b.BlockHeader == nil { - return nil, errors.New("block insertion should have failed") + if data, err := json.MarshalIndent(cb.Header(), "", " "); err == nil { + fmt.Fprintf(os.Stderr, "block (index %d) insertion should have failed due to: %v:\n%v\n", + bi, b.ExpectException, string(data)) + } + return nil, fmt.Errorf("block (index %d) insertion should have failed due to: %v", + bi, b.ExpectException) } // validate RLP decoding by checking all values against test file JSON diff --git a/tests/gen_btheader.go b/tests/gen_btheader.go index 003ca5fb39ef..abe99159cc9b 100644 --- a/tests/gen_btheader.go +++ b/tests/gen_btheader.go @@ -14,6 +14,7 @@ import ( var _ = (*btHeaderMarshaling)(nil) +// MarshalJSON marshals as JSON. func (b btHeader) MarshalJSON() ([]byte, error) { type btHeader struct { Bloom types.Bloom @@ -32,6 +33,7 @@ func (b btHeader) MarshalJSON() ([]byte, error) { GasLimit math.HexOrDecimal64 GasUsed math.HexOrDecimal64 Timestamp *math.HexOrDecimal256 + BaseFee *math.HexOrDecimal256 } var enc btHeader enc.Bloom = b.Bloom @@ -50,9 +52,11 @@ func (b btHeader) MarshalJSON() ([]byte, error) { enc.GasLimit = math.HexOrDecimal64(b.GasLimit) enc.GasUsed = math.HexOrDecimal64(b.GasUsed) enc.Timestamp = (*math.HexOrDecimal256)(b.Timestamp) + enc.BaseFee = (*math.HexOrDecimal256)(b.BaseFee) return json.Marshal(&enc) } +// UnmarshalJSON unmarshals from JSON. func (b *btHeader) UnmarshalJSON(input []byte) error { type btHeader struct { Bloom *types.Bloom @@ -71,6 +75,7 @@ func (b *btHeader) UnmarshalJSON(input []byte) error { GasLimit *math.HexOrDecimal64 GasUsed *math.HexOrDecimal64 Timestamp *math.HexOrDecimal256 + BaseFee *math.HexOrDecimal256 } var dec btHeader if err := json.Unmarshal(input, &dec); err != nil { @@ -124,5 +129,8 @@ func (b *btHeader) UnmarshalJSON(input []byte) error { if dec.Timestamp != nil { b.Timestamp = (*big.Int)(dec.Timestamp) } + if dec.BaseFee != nil { + b.BaseFee = (*big.Int)(dec.BaseFee) + } return nil } diff --git a/tests/gen_stenv.go b/tests/gen_stenv.go index 9248bebc7ab5..5a43270a9a5d 100644 --- a/tests/gen_stenv.go +++ b/tests/gen_stenv.go @@ -21,6 +21,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) { GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` + BaseFee *math.HexOrDecimal256 `json:"currentBaseFee" gencodec:"optional"` } var enc stEnv enc.Coinbase = common.UnprefixedAddress(s.Coinbase) @@ -28,6 +29,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) { enc.GasLimit = math.HexOrDecimal64(s.GasLimit) enc.Number = math.HexOrDecimal64(s.Number) enc.Timestamp = math.HexOrDecimal64(s.Timestamp) + enc.BaseFee = (*math.HexOrDecimal256)(s.BaseFee) return json.Marshal(&enc) } @@ -39,6 +41,7 @@ func (s *stEnv) UnmarshalJSON(input []byte) error { GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` + BaseFee *math.HexOrDecimal256 `json:"currentBaseFee" gencodec:"optional"` } var dec stEnv if err := json.Unmarshal(input, &dec); err != nil { @@ -64,5 +67,8 @@ func (s *stEnv) UnmarshalJSON(input []byte) error { return errors.New("missing required field 'currentTimestamp' for stEnv") } s.Timestamp = uint64(*dec.Timestamp) + if dec.BaseFee != nil { + s.BaseFee = (*big.Int)(dec.BaseFee) + } return nil } diff --git a/tests/gen_sttransaction.go b/tests/gen_sttransaction.go index fdf0d5df2a87..ac8989372780 100644 --- a/tests/gen_sttransaction.go +++ b/tests/gen_sttransaction.go @@ -16,17 +16,21 @@ var _ = (*stTransactionMarshaling)(nil) // MarshalJSON marshals as JSON. func (s stTransaction) MarshalJSON() ([]byte, error) { type stTransaction struct { - GasPrice *math.HexOrDecimal256 `json:"gasPrice"` - Nonce math.HexOrDecimal64 `json:"nonce"` - To string `json:"to"` - Data []string `json:"data"` - AccessLists []*types.AccessList `json:"accessLists,omitempty"` - GasLimit []math.HexOrDecimal64 `json:"gasLimit"` - Value []string `json:"value"` - PrivateKey hexutil.Bytes `json:"secretKey"` + GasPrice *math.HexOrDecimal256 `json:"gasPrice"` + MaxFeePerGas *math.HexOrDecimal256 `json:"maxFeePerGas"` + MaxPriorityFeePerGas *math.HexOrDecimal256 `json:"maxPriorityFeePerGas"` + Nonce math.HexOrDecimal64 `json:"nonce"` + To string `json:"to"` + Data []string `json:"data"` + AccessLists []*types.AccessList `json:"accessLists,omitempty"` + GasLimit []math.HexOrDecimal64 `json:"gasLimit"` + Value []string `json:"value"` + PrivateKey hexutil.Bytes `json:"secretKey"` } var enc stTransaction enc.GasPrice = (*math.HexOrDecimal256)(s.GasPrice) + enc.MaxFeePerGas = (*math.HexOrDecimal256)(s.MaxFeePerGas) + enc.MaxPriorityFeePerGas = (*math.HexOrDecimal256)(s.MaxPriorityFeePerGas) enc.Nonce = math.HexOrDecimal64(s.Nonce) enc.To = s.To enc.Data = s.Data @@ -45,14 +49,16 @@ func (s stTransaction) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshals from JSON. func (s *stTransaction) UnmarshalJSON(input []byte) error { type stTransaction struct { - GasPrice *math.HexOrDecimal256 `json:"gasPrice"` - Nonce *math.HexOrDecimal64 `json:"nonce"` - To *string `json:"to"` - Data []string `json:"data"` - AccessLists []*types.AccessList `json:"accessLists,omitempty"` - GasLimit []math.HexOrDecimal64 `json:"gasLimit"` - Value []string `json:"value"` - PrivateKey *hexutil.Bytes `json:"secretKey"` + GasPrice *math.HexOrDecimal256 `json:"gasPrice"` + MaxFeePerGas *math.HexOrDecimal256 `json:"maxFeePerGas"` + MaxPriorityFeePerGas *math.HexOrDecimal256 `json:"maxPriorityFeePerGas"` + Nonce *math.HexOrDecimal64 `json:"nonce"` + To *string `json:"to"` + Data []string `json:"data"` + AccessLists []*types.AccessList `json:"accessLists,omitempty"` + GasLimit []math.HexOrDecimal64 `json:"gasLimit"` + Value []string `json:"value"` + PrivateKey *hexutil.Bytes `json:"secretKey"` } var dec stTransaction if err := json.Unmarshal(input, &dec); err != nil { @@ -61,6 +67,12 @@ func (s *stTransaction) UnmarshalJSON(input []byte) error { if dec.GasPrice != nil { s.GasPrice = (*big.Int)(dec.GasPrice) } + if dec.MaxFeePerGas != nil { + s.MaxFeePerGas = (*big.Int)(dec.MaxFeePerGas) + } + if dec.MaxPriorityFeePerGas != nil { + s.MaxPriorityFeePerGas = (*big.Int)(dec.MaxPriorityFeePerGas) + } if dec.Nonce != nil { s.Nonce = uint64(*dec.Nonce) } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 36e882b14cd9..a09fb119925f 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -80,6 +80,7 @@ type stEnv struct { GasLimit uint64 `json:"currentGasLimit" gencodec:"required"` Number uint64 `json:"currentNumber" gencodec:"required"` Timestamp uint64 `json:"currentTimestamp" gencodec:"required"` + BaseFee *big.Int `json:"currentBaseFee" gencodec:"optional"` } type stEnvMarshaling struct { @@ -88,26 +89,31 @@ type stEnvMarshaling struct { GasLimit math.HexOrDecimal64 Number math.HexOrDecimal64 Timestamp math.HexOrDecimal64 + BaseFee *math.HexOrDecimal256 } //go:generate gencodec -type stTransaction -field-override stTransactionMarshaling -out gen_sttransaction.go type stTransaction struct { - GasPrice *big.Int `json:"gasPrice"` - Nonce uint64 `json:"nonce"` - To string `json:"to"` - Data []string `json:"data"` - AccessLists []*types.AccessList `json:"accessLists,omitempty"` - GasLimit []uint64 `json:"gasLimit"` - Value []string `json:"value"` - PrivateKey []byte `json:"secretKey"` + GasPrice *big.Int `json:"gasPrice"` + MaxFeePerGas *big.Int `json:"maxFeePerGas"` + MaxPriorityFeePerGas *big.Int `json:"maxPriorityFeePerGas"` + Nonce uint64 `json:"nonce"` + To string `json:"to"` + Data []string `json:"data"` + AccessLists []*types.AccessList `json:"accessLists,omitempty"` + GasLimit []uint64 `json:"gasLimit"` + Value []string `json:"value"` + PrivateKey []byte `json:"secretKey"` } type stTransactionMarshaling struct { - GasPrice *math.HexOrDecimal256 - Nonce math.HexOrDecimal64 - GasLimit []math.HexOrDecimal64 - PrivateKey hexutil.Bytes + GasPrice *math.HexOrDecimal256 + MaxFeePerGas *math.HexOrDecimal256 + MaxPriorityFeePerGas *math.HexOrDecimal256 + Nonce math.HexOrDecimal64 + GasLimit []math.HexOrDecimal64 + PrivateKey hexutil.Bytes } // Subtests returns all valid subtests of the test. @@ -131,8 +137,17 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD db := rawdb.NewMemoryDatabase() statedb := MakePreState(db, t.json.Pre) + var baseFee *big.Int + if config.IsEIP1559(new(big.Int)) { + baseFee = t.json.Env.BaseFee + if baseFee == nil { + // Retesteth uses `0x10` for genesis baseFee. Therefore, it defaults to + // parent - 2 : 0xa as the basefee for 'this' context. + baseFee = big.NewInt(common.BaseFee.Int64()) + } + } post := t.json.Post[subtest.Fork][subtest.Index] - msg, err := t.json.Tx.toMessage(post, block.Number()) + msg, err := t.json.Tx.toMessage(post, block.Number(), baseFee) if err != nil { return nil, err } @@ -141,6 +156,7 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase) context.GetHash = vmTestBlockHash + context.BaseFee = baseFee evm := vm.NewEVM(context, txContext, statedb, nil, config, vmconfig) // Execute the message. @@ -197,7 +213,7 @@ func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis { } } -func (tx *stTransaction) toMessage(ps stPostState, number *big.Int) (core.Message, error) { +func (tx *stTransaction) toMessage(ps stPostState, number *big.Int, baseFee *big.Int) (core.Message, error) { // Derive sender from private key if present. var from common.Address if len(tx.PrivateKey) > 0 { @@ -246,7 +262,23 @@ func (tx *stTransaction) toMessage(ps stPostState, number *big.Int) (core.Messag if tx.AccessLists != nil && tx.AccessLists[ps.Indexes.Data] != nil { accessList = *tx.AccessLists[ps.Indexes.Data] } - msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, nil, nil, data, accessList, true, nil, number) + // If baseFee provided, set gasPrice to effectiveGasPrice. + gasPrice := tx.GasPrice + if baseFee != nil { + if tx.MaxFeePerGas == nil { + tx.MaxFeePerGas = gasPrice + } + if tx.MaxFeePerGas == nil { + tx.MaxFeePerGas = new(big.Int) + } + if tx.MaxPriorityFeePerGas == nil { + tx.MaxPriorityFeePerGas = tx.MaxFeePerGas + } + gasPrice = math.BigMin(new(big.Int).Add(tx.MaxPriorityFeePerGas, baseFee), + tx.MaxFeePerGas) + } + + msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, tx.MaxFeePerGas, tx.MaxPriorityFeePerGas, data, accessList, true, nil, number) return msg, nil } From 05c11eb96e19b39a0ff01b5931d97b3ddebe8157 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 24 May 2024 22:03:00 +0800 Subject: [PATCH 095/479] core: make genesis parse baseFee correctly (#23013) --- core/gen_genesis.go | 8 ++++---- core/genesis.go | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/core/gen_genesis.go b/core/gen_genesis.go index 63ea023f4e82..21297313c811 100644 --- a/core/gen_genesis.go +++ b/core/gen_genesis.go @@ -30,7 +30,7 @@ func (g Genesis) MarshalJSON() ([]byte, error) { Number math.HexOrDecimal64 `json:"number"` GasUsed math.HexOrDecimal64 `json:"gasUsed"` ParentHash common.Hash `json:"parentHash"` - BaseFee *big.Int `json:"baseFee"` + BaseFee *math.HexOrDecimal256 `json:"baseFee"` } var enc Genesis enc.Config = g.Config @@ -50,7 +50,7 @@ func (g Genesis) MarshalJSON() ([]byte, error) { enc.Number = math.HexOrDecimal64(g.Number) enc.GasUsed = math.HexOrDecimal64(g.GasUsed) enc.ParentHash = g.ParentHash - enc.BaseFee = g.BaseFee + enc.BaseFee = (*math.HexOrDecimal256)(g.BaseFee) return json.Marshal(&enc) } @@ -69,7 +69,7 @@ func (g *Genesis) UnmarshalJSON(input []byte) error { Number *math.HexOrDecimal64 `json:"number"` GasUsed *math.HexOrDecimal64 `json:"gasUsed"` ParentHash *common.Hash `json:"parentHash"` - BaseFee *big.Int `json:"baseFee"` + BaseFee *math.HexOrDecimal256 `json:"baseFee"` } var dec Genesis if err := json.Unmarshal(input, &dec); err != nil { @@ -118,7 +118,7 @@ func (g *Genesis) UnmarshalJSON(input []byte) error { g.ParentHash = *dec.ParentHash } if dec.BaseFee != nil { - g.BaseFee = dec.BaseFee + g.BaseFee = (*big.Int)(dec.BaseFee) } return nil } diff --git a/core/genesis.go b/core/genesis.go index 0e552fb84e12..a9cc02c1799a 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -97,6 +97,7 @@ type genesisSpecMarshaling struct { GasUsed math.HexOrDecimal64 Number math.HexOrDecimal64 Difficulty *math.HexOrDecimal256 + BaseFee *math.HexOrDecimal256 Alloc map[common.UnprefixedAddress]GenesisAccount } From 1cb1ba79d7330bb7f25472dc9ff25307693174fe Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 24 May 2024 22:25:10 +0800 Subject: [PATCH 096/479] core: change baseFee into baseFeePerGas in genesis json (#23039) --- core/gen_genesis.go | 4 ++-- core/genesis.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/gen_genesis.go b/core/gen_genesis.go index 21297313c811..27dcb1f7cc13 100644 --- a/core/gen_genesis.go +++ b/core/gen_genesis.go @@ -30,7 +30,7 @@ func (g Genesis) MarshalJSON() ([]byte, error) { Number math.HexOrDecimal64 `json:"number"` GasUsed math.HexOrDecimal64 `json:"gasUsed"` ParentHash common.Hash `json:"parentHash"` - BaseFee *math.HexOrDecimal256 `json:"baseFee"` + BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"` } var enc Genesis enc.Config = g.Config @@ -69,7 +69,7 @@ func (g *Genesis) UnmarshalJSON(input []byte) error { Number *math.HexOrDecimal64 `json:"number"` GasUsed *math.HexOrDecimal64 `json:"gasUsed"` ParentHash *common.Hash `json:"parentHash"` - BaseFee *math.HexOrDecimal256 `json:"baseFee"` + BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"` } var dec Genesis if err := json.Unmarshal(input, &dec); err != nil { diff --git a/core/genesis.go b/core/genesis.go index a9cc02c1799a..ca397ccbf6c5 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -61,7 +61,7 @@ type Genesis struct { Number uint64 `json:"number"` GasUsed uint64 `json:"gasUsed"` ParentHash common.Hash `json:"parentHash"` - BaseFee *big.Int `json:"baseFee"` + BaseFee *big.Int `json:"baseFeePerGas"` } // GenesisAlloc specifies the initial state that is part of the genesis block. From dbdca115014bd5118b5e196bc3bb408089fe9e73 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 27 May 2024 17:23:52 +0800 Subject: [PATCH 097/479] accounts/abi/bind: fix bounded contracts and sim backend for 1559 (#23038) --- accounts/abi/bind/backend.go | 22 +++++-- accounts/abi/bind/backends/simulated.go | 55 +++++++++++++++-- accounts/abi/bind/base.go | 82 +++++++++++++++++++++---- accounts/abi/bind/bind_test.go | 20 +++--- accounts/abi/bind/util_test.go | 13 ++-- core/blockchain_test.go | 65 +++++++++++--------- core/chain_makers.go | 5 ++ core/dao_test.go | 4 +- core/genesis.go | 7 ++- eth/api_tracer.go | 2 +- eth/downloader/downloader_test.go | 16 +++-- eth/filters/filter_system_test.go | 2 +- eth/gasprice/gasprice_test.go | 2 + ethclient/ethclient.go | 10 +++ les/odr_test.go | 8 +-- light/odr_test.go | 36 ++++++----- light/trie_test.go | 9 ++- light/txpool_test.go | 14 +++-- 18 files changed, 270 insertions(+), 102 deletions(-) diff --git a/accounts/abi/bind/backend.go b/accounts/abi/bind/backend.go index 126787963a5b..25ac008c03ec 100644 --- a/accounts/abi/bind/backend.go +++ b/accounts/abi/bind/backend.go @@ -32,12 +32,12 @@ var ( // have any code associated with it (i.e. suicided). ErrNoCode = errors.New("no contract code at given address") - // This error is raised when attempting to perform a pending state action + // ErrNoPendingState is raised when attempting to perform a pending state action // on a backend that doesn't implement PendingContractCaller. ErrNoPendingState = errors.New("backend does not support pending state") - // This error is returned by WaitDeployed if contract creation leaves an - // empty contract behind. + // ErrNoCodeAfterDeploy is returned by WaitDeployed if contract creation leaves + // an empty contract behind. ErrNoCodeAfterDeploy = errors.New("no contract code after deployment") ) @@ -47,7 +47,8 @@ type ContractCaller interface { // CodeAt returns the code of the given account. This is needed to differentiate // between contract internal errors and the local chain being out of sync. CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) - // ContractCall executes an Ethereum contract call with the specified data as the + + // CallContract executes an Ethereum contract call with the specified data as the // input. CallContract(ctx context.Context, call XDPoSChain.CallMsg, blockNumber *big.Int) ([]byte, error) } @@ -58,6 +59,7 @@ type ContractCaller interface { type PendingContractCaller interface { // PendingCodeAt returns the code of the given account in the pending state. PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) + // PendingCallContract executes an Ethereum contract call against the pending state. PendingCallContract(ctx context.Context, call XDPoSChain.CallMsg) ([]byte, error) } @@ -67,19 +69,31 @@ type PendingContractCaller interface { // used when the user does not provide some needed values, but rather leaves it up // to the transactor to decide. type ContractTransactor interface { + // HeaderByNumber returns a block header from the current canonical chain. If + // number is nil, the latest known header is returned. + HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) + // PendingCodeAt returns the code of the given account in the pending state. PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) + // PendingNonceAt retrieves the current pending nonce associated with an account. PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) + // SuggestGasPrice retrieves the currently suggested gas price to allow a timely // execution of a transaction. SuggestGasPrice(ctx context.Context) (*big.Int, error) + + // SuggestGasTipCap retrieves the currently suggested 1559 priority fee to allow + // a timely execution of a transaction. + SuggestGasTipCap(ctx context.Context) (*big.Int, error) + // EstimateGas tries to estimate the gas needed to execute a specific // transaction based on the current pending state of the backend blockchain. // There is no guarantee that this is the true gas limit requirement as other // transactions may be added or removed by miners, but it should provide a basis // for setting a reasonable default. EstimateGas(ctx context.Context, call XDPoSChain.CallMsg) (gas uint64, err error) + // SendTransaction injects the transaction into the pending pool for execution. SendTransaction(ctx context.Context, tx *types.Transaction) error } diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 58e73976122c..6ad9d01cdbe1 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -256,6 +256,19 @@ func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common return receipt, nil } +// HeaderByNumber returns a block header from the current canonical chain. If number is +// nil, the latest known header is returned. +func (b *SimulatedBackend) HeaderByNumber(ctx context.Context, block *big.Int) (*types.Header, error) { + b.mu.Lock() + defer b.mu.Unlock() + + if block == nil || block.Cmp(b.pendingBlock.Number()) == 0 { + return b.blockchain.CurrentHeader(), nil + } + + return b.blockchain.GetHeaderByNumber(uint64(block.Int64())), nil +} + // PendingCodeAt returns the code associated with an account in the pending state. func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) { b.mu.Lock() @@ -305,6 +318,12 @@ func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error return big.NewInt(1), nil } +// SuggestGasTipCap implements ContractTransactor.SuggestGasTipCap. Since the simulated +// chain doesn't have miners, we just return a gas tip of 1 for any call. +func (b *SimulatedBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { + return big.NewInt(1), nil +} + // EstimateGas executes the requested code against the currently pending block/state and // returns the used amount of gas. func (b *SimulatedBackend) EstimateGas(ctx context.Context, call XDPoSChain.CallMsg) (uint64, error) { @@ -358,10 +377,38 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call XDPoSChain.Call // callContract implements common code between normal and pending contract calls. // state is modified during execution, make sure to copy it if necessary. func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.CallMsg, block *types.Block, statedb *state.StateDB) (ret []byte, usedGas uint64, failed bool, err error) { - // Ensure message is initialized properly. - if call.GasPrice == nil { - call.GasPrice = big.NewInt(1) + // Gas prices post 1559 need to be initialized + if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) { + return nil, 0, false, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") + } + head := b.blockchain.CurrentHeader() + if !b.blockchain.Config().IsEIP1559(head.Number) { + // If there's no basefee, then it must be a non-1559 execution + if call.GasPrice == nil { + call.GasPrice = new(big.Int) + } + call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice + } else { + // A basefee is provided, necessitating 1559-type execution + if call.GasPrice != nil { + // User specified the legacy gas field, convert to 1559 gas typing + call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice + } else { + // User specified 1559 gas feilds (or none), use those + if call.GasFeeCap == nil { + call.GasFeeCap = new(big.Int) + } + if call.GasTipCap == nil { + call.GasTipCap = new(big.Int) + } + // Backfill the legacy gasPrice for EVM execution, unless we're all zeroes + call.GasPrice = new(big.Int) + if call.GasFeeCap.BitLen() > 0 || call.GasTipCap.BitLen() > 0 { + call.GasPrice = math.BigMin(new(big.Int).Add(call.GasTipCap, head.BaseFee), call.GasFeeCap) + } + } } + // Ensure message is initialized properly. if call.Gas == 0 { call.Gas = 50000000 } @@ -384,7 +431,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.Cal evmContext := core.NewEVMBlockContext(block.Header(), b.blockchain, nil) // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. - vmenv := vm.NewEVM(evmContext, txContext, statedb, nil, b.config, vm.Config{}) + vmenv := vm.NewEVM(evmContext, txContext, statedb, nil, b.config, vm.Config{NoBaseFee: true}) gaspool := new(core.GasPool).AddGas(math.MaxUint64) owner := common.Address{} ret, usedGas, failed, err, _ = core.NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner) diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index 22425c3f2888..b7e8ec4fb1a2 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -53,9 +53,11 @@ type TransactOpts struct { Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state) Signer SignerFn // Method to use for signing the transaction (mandatory) - Value *big.Int // Funds to transfer along along the transaction (nil = 0 = no funds) - GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle) - GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate) + Value *big.Int // Funds to transfer along along the transaction (nil = 0 = no funds) + GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle) + GasFeeCap *big.Int // Gas fee cap to use for the 1559 transaction execution (nil = gas price oracle) + GasTipCap *big.Int // Gas priority fee cap to use for the 1559 transaction execution (nil = gas price oracle) + GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate) Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) @@ -205,12 +207,45 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i } else { nonce = opts.Nonce.Uint64() } - // Figure out the gas allowance and gas price values - gasPrice := opts.GasPrice - if gasPrice == nil { - gasPrice, err = c.transactor.SuggestGasPrice(ensureContext(opts.Context)) - if err != nil { - return nil, fmt.Errorf("failed to suggest gas price: %v", err) + // Figure out reasonable gas price values + if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) { + return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") + } + head, err := c.transactor.HeaderByNumber(opts.Context, nil) + if err != nil { + return nil, err + } + if head.BaseFee != nil && opts.GasPrice == nil { + if opts.GasTipCap == nil { + tip, err := c.transactor.SuggestGasTipCap(opts.Context) + if err != nil { + return nil, err + } + opts.GasTipCap = tip + } + if opts.GasFeeCap == nil { + gasFeeCap := new(big.Int).Add( + opts.GasTipCap, + new(big.Int).Mul(head.BaseFee, big.NewInt(2)), + ) + opts.GasFeeCap = gasFeeCap + } + if opts.GasFeeCap.Cmp(opts.GasTipCap) < 0 { + return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", opts.GasFeeCap, opts.GasTipCap) + } + } else { + if opts.GasFeeCap != nil || opts.GasTipCap != nil { + return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") + } + if opts.GasPrice == nil { + price, err := c.transactor.SuggestGasTipCap(opts.Context) + if err != nil { + return nil, err + } + if head.BaseFee != nil { + price.Add(price, head.BaseFee) + } + opts.GasPrice = price } } gasLimit := opts.GasLimit @@ -224,7 +259,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i } } // If the contract surely has code (or code is not needed), estimate the transaction - msg := XDPoSChain.CallMsg{From: opts.From, To: contract, Value: value, Data: input} + msg := XDPoSChain.CallMsg{From: opts.From, To: contract, GasPrice: opts.GasPrice, GasTipCap: opts.GasTipCap, GasFeeCap: opts.GasFeeCap, Value: value, Data: input} gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg) if err != nil { return nil, fmt.Errorf("failed to estimate gas needed: %v", err) @@ -232,10 +267,31 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i } // Create the transaction, sign it and schedule it for execution var rawTx *types.Transaction - if contract == nil { - rawTx = types.NewContractCreation(nonce, value, gasLimit, gasPrice, input) + if opts.GasFeeCap == nil { + baseTx := &types.LegacyTx{ + Nonce: nonce, + GasPrice: opts.GasPrice, + Gas: gasLimit, + Value: value, + Data: input, + } + if contract != nil { + baseTx.To = &c.address + } + rawTx = types.NewTx(baseTx) } else { - rawTx = types.NewTransaction(nonce, c.address, value, gasLimit, gasPrice, input) + baseTx := &types.DynamicFeeTx{ + Nonce: nonce, + GasFeeCap: opts.GasFeeCap, + GasTipCap: opts.GasTipCap, + Gas: gasLimit, + Value: value, + Data: input, + } + if contract != nil { + baseTx.To = &c.address + } + rawTx = types.NewTx(baseTx) } if opts.Signer == nil { return nil, errors.New("no signer to authorize the transaction with") diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index ba5668486447..2c477882ebf3 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -229,7 +229,7 @@ var bindTests = []struct { // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig) + sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig) // Deploy an interaction tester contract and call a transaction on it _, _, interactor, err := DeployInteractor(auth, sim, "Deploy string") @@ -270,7 +270,7 @@ var bindTests = []struct { // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig) + sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig) // Deploy a tuple tester contract and execute a structured call on it _, _, getter, err := DeployGetter(auth, sim) @@ -302,7 +302,7 @@ var bindTests = []struct { // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig) + sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig) // Deploy a tuple tester contract and execute a structured call on it _, _, tupler, err := DeployTupler(auth, sim) @@ -344,7 +344,7 @@ var bindTests = []struct { // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig) + sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig) // Deploy a slice tester contract and execute a n array call on it _, _, slicer, err := DeploySlicer(auth, sim) @@ -378,7 +378,7 @@ var bindTests = []struct { // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig) + sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig) // Deploy a default method invoker contract and execute its default method _, _, defaulter, err := DeployDefaulter(auth, sim) @@ -447,7 +447,7 @@ var bindTests = []struct { // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig) + sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig) // Deploy a funky gas pattern contract _, _, limiter, err := DeployFunkyGasPattern(auth, sim) @@ -482,7 +482,7 @@ var bindTests = []struct { // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig) + sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig) // Deploy a sender tester contract and execute a structured call on it _, _, callfrom, err := DeployCallFrom(auth, sim) @@ -542,7 +542,7 @@ var bindTests = []struct { // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig) + sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig) // Deploy a underscorer tester contract and execute a structured call on it _, _, underscorer, err := DeployUnderscorer(auth, sim) @@ -612,7 +612,7 @@ var bindTests = []struct { // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig) + sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig) // Deploy an eventer contract _, _, eventer, err := DeployEventer(auth, sim) @@ -761,7 +761,7 @@ var bindTests = []struct { // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig) + sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig) //deploy the test contract _, _, testContract, err := DeployDeeplyNestedArray(auth, sim) diff --git a/accounts/abi/bind/util_test.go b/accounts/abi/bind/util_test.go index 06efcd980c18..5b5224920970 100644 --- a/accounts/abi/bind/util_test.go +++ b/accounts/abi/bind/util_test.go @@ -53,15 +53,20 @@ var waitDeployedTests = map[string]struct { } func TestWaitDeployed(t *testing.T) { + config := *params.TestXDPoSMockChainConfig + config.Eip1559Block = big.NewInt(0) for name, test := range waitDeployedTests { backend := backends.NewXDCSimulatedBackend( core.GenesisAlloc{ - crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000)}, - }, 10000000, params.TestXDPoSMockChainConfig, + crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(100000000000000000)}, + }, 10000000, &config, ) - // Create the transaction. - tx := types.NewContractCreation(0, big.NewInt(0), test.gas, big.NewInt(1), common.FromHex(test.code)) + // Create the transaction + head, _ := backend.HeaderByNumber(context.Background(), nil) // Should be child's, good enough + gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) + + tx := types.NewContractCreation(0, big.NewInt(0), test.gas, gasPrice, common.FromHex(test.code)) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) // Wait for it to get mined in the background. diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 87f031b834a9..ebf05f31d277 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -19,7 +19,6 @@ package core import ( "errors" "fmt" - "math" "math/big" "math/rand" "sync" @@ -557,10 +556,11 @@ func TestFastVsFullChains(t *testing.T) { gendb = rawdb.NewMemoryDatabase() key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") address = crypto.PubkeyToAddress(key.PublicKey) - funds = big.NewInt(1000000000) + funds = big.NewInt(1000000000000000) gspec = &Genesis{ - Config: params.TestChainConfig, - Alloc: GenesisAlloc{address: {Balance: funds}}, + Config: params.TestChainConfig, + Alloc: GenesisAlloc{address: {Balance: funds}}, + BaseFee: big.NewInt(params.InitialBaseFee), } genesis = gspec.MustCommit(gendb) signer = types.LatestSigner(gspec.Config) @@ -571,7 +571,7 @@ func TestFastVsFullChains(t *testing.T) { // If the block number is multiple of 3, send a few bonus transactions to the miner if i%3 == 2 { for j := 0; j < i%4+1; j++ { - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, nil, nil), signer, key) + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, block.header.BaseFee, nil), signer, key) if err != nil { panic(err) } @@ -645,8 +645,12 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { gendb = rawdb.NewMemoryDatabase() key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") address = crypto.PubkeyToAddress(key.PublicKey) - funds = big.NewInt(1000000000) - gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}} + funds = big.NewInt(1000000000000000) + gspec = &Genesis{ + Config: params.TestChainConfig, + Alloc: GenesisAlloc{address: {Balance: funds}}, + BaseFee: big.NewInt(params.InitialBaseFee), + } genesis = gspec.MustCommit(gendb) ) height := uint64(1024) @@ -732,9 +736,9 @@ func TestChainTxReorgs(t *testing.T) { Config: params.TestChainConfig, GasLimit: 3141592, Alloc: GenesisAlloc{ - addr1: {Balance: big.NewInt(1000000)}, - addr2: {Balance: big.NewInt(1000000)}, - addr3: {Balance: big.NewInt(1000000)}, + addr1: {Balance: big.NewInt(1000000000000000)}, + addr2: {Balance: big.NewInt(1000000000000000)}, + addr3: {Balance: big.NewInt(1000000000000000)}, }, } genesis = gspec.MustCommit(db) @@ -744,8 +748,8 @@ func TestChainTxReorgs(t *testing.T) { // Create two transactions shared between the chains: // - postponed: transaction included at a later block in the forked chain // - swapped: transaction included at the same block number in the forked chain - postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1) - swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1) + postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, key1) + swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, key1) // Create two transactions that will be dropped by the forked chain: // - pastDrop: transaction dropped retroactively from a past block @@ -761,13 +765,13 @@ func TestChainTxReorgs(t *testing.T) { chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) { switch i { case 0: - pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2) + pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key2) gen.AddTx(pastDrop) // This transaction will be dropped in the fork from below the split point gen.AddTx(postponed) // This transaction will be postponed till block #3 in the fork case 2: - freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2) + freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key2) gen.AddTx(freshDrop) // This transaction will be dropped in the fork from exactly at the split point gen.AddTx(swapped) // This transaction will be swapped out at the exact height @@ -786,18 +790,18 @@ func TestChainTxReorgs(t *testing.T) { chain, _ = GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 5, func(i int, gen *BlockGen) { switch i { case 0: - pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3) + pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key3) gen.AddTx(pastAdd) // This transaction needs to be injected during reorg case 2: gen.AddTx(postponed) // This transaction was postponed from block #1 in the original chain gen.AddTx(swapped) // This transaction was swapped from the exact current spot in the original chain - freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3) + freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key3) gen.AddTx(freshAdd) // This transaction will be added exactly at reorg time case 3: - futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3) + futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key3) gen.AddTx(futureAdd) // This transaction will be added after a full reorg } }) @@ -842,7 +846,7 @@ func TestLogReorgs(t *testing.T) { db = rawdb.NewMemoryDatabase() // this code generates a log code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") - gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}} + gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}} genesis = gspec.MustCommit(db) signer = types.LatestSigner(gspec.Config) ) @@ -854,7 +858,7 @@ func TestLogReorgs(t *testing.T) { blockchain.SubscribeRemovedLogsEvent(rmLogsCh) chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) { if i == 1 { - tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1) + tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, gen.header.BaseFee, code), signer, key1) if err != nil { t.Fatalf("failed to create tx: %v", err) } @@ -888,7 +892,7 @@ func TestLogReorgs(t *testing.T) { // addr1 = crypto.PubkeyToAddress(key1.PublicKey) // gspec = &Genesis{ // Config: params.TestChainConfig, -// Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}, +// Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}, // } // genesis = gspec.MustCommit(db) // signer = types.LatestSigner(gspec.Config) @@ -903,7 +907,7 @@ func TestLogReorgs(t *testing.T) { // } // // replacementBlocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, gen *BlockGen) { -// tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), nil), signer, key1) +// tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, gen.header.BaseFee, nil), signer, key1) // if i == 2 { // gen.OffsetTime(-9) // } @@ -1190,7 +1194,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) { engine := ethash.NewFaker() db := rawdb.NewMemoryDatabase() - genesis := new(Genesis).MustCommit(db) + genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) // Generate a bunch of fork blocks, each side forking from the canonical chain @@ -1206,7 +1210,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) { // Import the canonical and fork chain side by side, verifying the current block // and current header consistency diskdb := rawdb.NewMemoryDatabase() - new(Genesis).MustCommit(diskdb) + (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}) if err != nil { @@ -1235,7 +1239,7 @@ func TestTrieForkGC(t *testing.T) { engine := ethash.NewFaker() db := rawdb.NewMemoryDatabase() - genesis := new(Genesis).MustCommit(db) + genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*triesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) // Generate a bunch of fork blocks, each side forking from the canonical chain @@ -1250,7 +1254,7 @@ func TestTrieForkGC(t *testing.T) { } // Import the canonical and fork chain side by side, forcing the trie cache to cache both diskdb := rawdb.NewMemoryDatabase() - new(Genesis).MustCommit(diskdb) + (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}) if err != nil { @@ -1281,7 +1285,7 @@ func TestLargeReorgTrieGC(t *testing.T) { engine := ethash.NewFaker() db := rawdb.NewMemoryDatabase() - genesis := new(Genesis).MustCommit(db) + genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) original, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*triesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) }) @@ -1289,7 +1293,7 @@ func TestLargeReorgTrieGC(t *testing.T) { // Import the shared chain and the original canonical one diskdb := rawdb.NewMemoryDatabase() - new(Genesis).MustCommit(diskdb) + (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}) if err != nil { @@ -1433,8 +1437,9 @@ func TestEIP2718Transition(t *testing.T) { // A sender who makes transactions, has some funds key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") address = crypto.PubkeyToAddress(key.PublicKey) - funds = big.NewInt(math.MaxInt64) - gspec = &Genesis{ + // funds = big.NewInt(math.MaxInt64) + funds = big.NewInt(1000000000000000) + gspec = &Genesis{ Config: ¶ms.ChainConfig{ ChainId: new(big.Int).SetBytes([]byte("eip1559")), HomesteadBlock: big.NewInt(0), @@ -1477,7 +1482,7 @@ func TestEIP2718Transition(t *testing.T) { Nonce: 0, To: &aa, Gas: 30000, - GasPrice: new(big.Int).Set(common.BaseFee), + GasPrice: b.header.BaseFee, AccessList: types.AccessList{{ Address: aa, StorageKeys: []common.Hash{{0}}, diff --git a/core/chain_makers.go b/core/chain_makers.go index e5cab11ecb0e..5f46caf43d5f 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -127,6 +127,11 @@ func (b *BlockGen) Number() *big.Int { return new(big.Int).Set(b.header.Number) } +// BaseFee returns the EIP-1559 base fee of the block being generated. +func (b *BlockGen) BaseFee() *big.Int { + return new(big.Int).Set(b.header.BaseFee) +} + // AddUncheckedReceipt forcefully adds a receipts to the block without a // backing transaction. // diff --git a/core/dao_test.go b/core/dao_test.go index cfd636e7c03f..f1d60bc0ca17 100644 --- a/core/dao_test.go +++ b/core/dao_test.go @@ -17,11 +17,11 @@ package core import ( - "github.com/XinFinOrg/XDPoSChain/core/rawdb" "math/big" "testing" "github.com/XinFinOrg/XDPoSChain/consensus/ethash" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/params" ) @@ -33,7 +33,7 @@ func TestDAOForkRangeExtradata(t *testing.T) { // Generate a common prefix for both pro-forkers and non-forkers db := rawdb.NewMemoryDatabase() - gspec := new(Genesis) + gspec := &Genesis{BaseFee: big.NewInt(params.InitialBaseFee)} genesis := gspec.MustCommit(db) prefix, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {}) diff --git a/core/genesis.go b/core/genesis.go index ca397ccbf6c5..a005b3a14a58 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -255,6 +255,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { Extra: g.ExtraData, GasLimit: g.GasLimit, GasUsed: g.GasUsed, + BaseFee: g.BaseFee, Difficulty: g.Difficulty, MixDigest: g.Mixhash, Coinbase: g.Coinbase, @@ -317,7 +318,10 @@ func (g *Genesis) MustCommit(db ethdb.Database) *types.Block { // GenesisBlockForTesting creates and writes a block in which addr has the given wei balance. func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block { - g := Genesis{Alloc: GenesisAlloc{addr: {Balance: balance}}} + g := Genesis{ + Alloc: GenesisAlloc{addr: {Balance: balance}}, + BaseFee: big.NewInt(params.InitialBaseFee), + } return g.MustCommit(db) } @@ -374,6 +378,7 @@ func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis { Config: &config, ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, 65)...), GasLimit: 6283185, + BaseFee: big.NewInt(params.InitialBaseFee), Difficulty: big.NewInt(1), Alloc: map[common.Address]GenesisAccount{ common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover diff --git a/eth/api_tracer.go b/eth/api_tracer.go index 6b7d346eabe4..2964a2b5d6f6 100644 --- a/eth/api_tracer.go +++ b/eth/api_tracer.go @@ -742,7 +742,7 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, t tracer = vm.NewStructLogger(config.LogConfig) } // Run the transaction with tracing enabled. - vmenv := vm.NewEVM(vmctx, txContext, statedb, nil, api.config, vm.Config{Debug: true, Tracer: tracer}) + vmenv := vm.NewEVM(vmctx, txContext, statedb, nil, api.config, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true}) // Call Prepare to clear out the statedb access list statedb.Prepare(txctx.TxHash, txctx.TxIndex) diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index bbf5889d0e9f..3df24e6d370a 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -19,6 +19,7 @@ package downloader import ( "errors" "fmt" + "math" "math/big" "sync" "sync/atomic" @@ -78,7 +79,7 @@ type downloadTester struct { // newTester creates a new downloader test mocker. func newTester() *downloadTester { testdb := rawdb.NewMemoryDatabase() - genesis := core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000)) + genesis := core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(math.MaxInt64)) tester := &downloadTester{ genesis: genesis, @@ -109,7 +110,8 @@ func newTester() *downloadTester { // reassembly. func (dl *downloadTester) makeChain(n int, seed byte, parent *types.Block, parentReceipts types.Receipts, heavy bool) ([]common.Hash, map[common.Hash]*types.Header, map[common.Hash]*types.Block, map[common.Hash]types.Receipts) { // Generate the block chain - blocks, receipts := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), dl.peerDb, n, func(i int, block *core.BlockGen) { + config := dl.Config() + blocks, receipts := core.GenerateChain(config, parent, ethash.NewFaker(), dl.peerDb, n, func(i int, block *core.BlockGen) { block.SetCoinbase(common.Address{seed}) // If a heavy chain is requested, delay blocks to raise difficulty @@ -118,8 +120,8 @@ func (dl *downloadTester) makeChain(n int, seed byte, parent *types.Block, paren } // If the block number is multiple of 3, send a bonus transaction to the miner if parent == dl.genesis && i%3 == 0 { - signer := types.MakeSigner(params.TestChainConfig, block.Number()) - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil), signer, testKey) + signer := types.MakeSigner(config, block.Number()) + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey) if err != nil { panic(err) } @@ -464,7 +466,11 @@ func (dl *downloadTester) handleProposedBlock(header *types.Header) error { } // Config retrieves the blockchain's chain configuration. -func (dl *downloadTester) Config() *params.ChainConfig { return params.TestChainConfig } +func (dl *downloadTester) Config() *params.ChainConfig { + config := *params.TestChainConfig + config.Eip1559Block = big.NewInt((0)) + return &config +} type downloadTesterPeer struct { dl *downloadTester diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index 2610376d3bf3..0d169167dbc8 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -164,7 +164,7 @@ func TestBlockSubscription(t *testing.T) { db = rawdb.NewMemoryDatabase() backend, sys = newTestFilterSystem(t, db, Config{}) api = NewFilterAPI(sys, false) - genesis = new(core.Genesis).MustCommit(db) + genesis = (&core.Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) chain, _ = core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 10, func(i int, gen *core.BlockGen) {}) chainEvents = []core.ChainEvent{} ) diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index bdd04b2fa209..b3a12374838d 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -68,6 +68,8 @@ func newTestBackend(t *testing.T, eip1559Block *big.Int) *testBackend { if eip1559Block != nil { gspec.Config.Eip1559Block = eip1559Block signer = types.LatestSigner(gspec.Config) + } else { + gspec.Config.Eip1559Block = nil } engine := ethash.NewFaker() db := rawdb.NewMemoryDatabase() diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 1e76ec6d5d43..6fd6e3aebb3e 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -476,6 +476,16 @@ func (ec *Client) SuggestGasPrice(ctx context.Context) (*big.Int, error) { return (*big.Int)(&hex), nil } +// SuggestGasTipCap retrieves the currently suggested gas tip cap after 1559 to +// allow a timely execution of a transaction. +func (ec *Client) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { + var hex hexutil.Big + if err := ec.c.CallContext(ctx, &hex, "eth_maxPriorityFeePerGas"); err != nil { + return nil, err + } + return (*big.Int)(&hex), nil +} + // EstimateGas tries to estimate the gas needed to execute a specific transaction based on // the current pending state of the backend blockchain. There is no guarantee that this is // the true gas limit requirement as other transactions may be added or removed by miners, diff --git a/les/odr_test.go b/les/odr_test.go index 7d9431dcf302..20eac1f09e3e 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -133,11 +133,11 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai if value, ok := feeCapacity[testContractAddr]; ok { balanceTokenFee = value } - msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, new(big.Int), nil, nil, data, nil, false, balanceTokenFee, header.Number)} + msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, false, balanceTokenFee, header.Number)} context := core.NewEVMBlockContext(header, bc, nil) txContext := core.NewEVMTxContext(msg) - vmenv := vm.NewEVM(context, txContext, statedb, nil, config, vm.Config{}) + vmenv := vm.NewEVM(context, txContext, statedb, nil, config, vm.Config{NoBaseFee: true}) //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxUint64) @@ -154,10 +154,10 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai if value, ok := feeCapacity[testContractAddr]; ok { balanceTokenFee = value } - msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 100000, new(big.Int), nil, nil, data, nil, false, balanceTokenFee, header.Number)} + msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 100000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, false, balanceTokenFee, header.Number)} context := core.NewEVMBlockContext(header, lc, nil) txContext := core.NewEVMTxContext(msg) - vmenv := vm.NewEVM(context, txContext, statedb, nil, config, vm.Config{}) + vmenv := vm.NewEVM(context, txContext, statedb, nil, config, vm.Config{NoBaseFee: true}) gp := new(core.GasPool).AddGas(math.MaxUint64) owner := common.Address{} ret, _, _, _, _ := core.ApplyMessage(vmenv, msg, gp, owner) diff --git a/light/odr_test.go b/light/odr_test.go index 7be95dd96975..3d211b948b12 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -44,7 +44,7 @@ import ( var ( testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) - testBankFunds = big.NewInt(100000000) + testBankFunds = big.NewInt(math.MaxInt64) acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") @@ -157,6 +157,7 @@ func (callmsg) CheckNonce() bool { return false } func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) { data := common.Hex2Bytes("60CD26850000000000000000000000000000000000000000000000000000000000000000") config := params.TestChainConfig + config.Eip1559Block = big.NewInt(0) var res []byte for i := 0; i < 3; i++ { @@ -184,10 +185,10 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain if value, ok := feeCapacity[testContractAddr]; ok { balanceTokenFee = value } - msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, new(big.Int), nil, nil, data, nil, false, balanceTokenFee, header.Number)} + msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, false, balanceTokenFee, header.Number)} txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(header, chain, nil) - vmenv := vm.NewEVM(context, txContext, st, nil, config, vm.Config{}) + vmenv := vm.NewEVM(context, txContext, st, nil, config, vm.Config{NoBaseFee: true}) gp := new(core.GasPool).AddGas(math.MaxUint64) owner := common.Address{} ret, _, _, _, _ := core.ApplyMessage(vmenv, msg, gp, owner) @@ -204,17 +205,17 @@ func testChainGen(i int, block *core.BlockGen) { switch i { case 0: // In block 1, the test bank sends account #1 some ether. - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey) + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(90_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, testBankKey) block.AddTx(tx) case 1: // In block 2, the test bank sends some more ether to account #1. // acc1Addr passes it on to account #2. // acc1Addr creates a test contract. - tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey) + tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, testBankKey) nonce := block.TxNonce(acc1Addr) - tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key) + tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, acc1Key) nonce++ - tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 1000000, big.NewInt(0), testContractCode), signer, acc1Key) + tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 1000000, block.BaseFee(), testContractCode), signer, acc1Key) testContractAddr = crypto.CreateAddress(acc1Addr, nonce) block.AddTx(tx1) block.AddTx(tx2) @@ -224,7 +225,7 @@ func testChainGen(i int, block *core.BlockGen) { block.SetCoinbase(acc2Addr) block.SetExtra([]byte("yeehaw")) data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001") - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, nil, data), signer, testBankKey) + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, block.BaseFee(), data), signer, testBankKey) block.AddTx(tx) case 3: // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). @@ -235,28 +236,33 @@ func testChainGen(i int, block *core.BlockGen) { b3.Extra = []byte("foo") block.AddUncle(b3) data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002") - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, nil, data), signer, testBankKey) + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, block.BaseFee(), data), signer, testBankKey) block.AddTx(tx) } } func testChainOdr(t *testing.T, protocol int, fn odrTestFn) { var ( - sdb = rawdb.NewMemoryDatabase() - ldb = rawdb.NewMemoryDatabase() - gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}} + sdb = rawdb.NewMemoryDatabase() + ldb = rawdb.NewMemoryDatabase() + gspec = core.Genesis{ + Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, + BaseFee: big.NewInt(params.InitialBaseFee), + } genesis = gspec.MustCommit(sdb) ) gspec.MustCommit(ldb) // Assemble the test environment - blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}) - gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, 4, testChainGen) + config := *params.TestChainConfig + config.Eip1559Block = big.NewInt(0) + blockchain, _ := core.NewBlockChain(sdb, nil, &config, ethash.NewFullFaker(), vm.Config{}) + gchain, _ := core.GenerateChain(&config, genesis, ethash.NewFaker(), sdb, 4, testChainGen) if _, err := blockchain.InsertChain(gchain); err != nil { t.Fatal(err) } odr := &testOdr{sdb: sdb, ldb: ldb} - lightchain, err := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker()) + lightchain, err := NewLightChain(odr, &config, ethash.NewFullFaker()) if err != nil { t.Fatal(err) } diff --git a/light/trie_test.go b/light/trie_test.go index 2332043d2611..1dda0cc73ee7 100644 --- a/light/trie_test.go +++ b/light/trie_test.go @@ -21,12 +21,12 @@ import ( "context" "errors" "fmt" + "math/big" "testing" - "github.com/XinFinOrg/XDPoSChain/core/rawdb" - "github.com/XinFinOrg/XDPoSChain/consensus/ethash" "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/params" @@ -38,7 +38,10 @@ func TestNodeIterator(t *testing.T) { var ( fulldb = rawdb.NewMemoryDatabase() lightdb = rawdb.NewMemoryDatabase() - gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}} + gspec = core.Genesis{ + Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, + BaseFee: big.NewInt(params.InitialBaseFee), + } genesis = gspec.MustCommit(fulldb) ) gspec.MustCommit(lightdb) diff --git a/light/txpool_test.go b/light/txpool_test.go index 467efb9cd0b0..e52e8dff7370 100644 --- a/light/txpool_test.go +++ b/light/txpool_test.go @@ -18,12 +18,13 @@ package light import ( "context" - "github.com/XinFinOrg/XDPoSChain/core/rawdb" "math" "math/big" "testing" "time" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" + "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus/ethash" "github.com/XinFinOrg/XDPoSChain/core" @@ -77,13 +78,16 @@ func txPoolTestChainGen(i int, block *core.BlockGen) { func TestTxPool(t *testing.T) { for i := range testTx { - testTx[i], _ = types.SignTx(types.NewTransaction(uint64(i), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey) + testTx[i], _ = types.SignTx(types.NewTransaction(uint64(i), acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), types.HomesteadSigner{}, testBankKey) } var ( - sdb = rawdb.NewMemoryDatabase() - ldb = rawdb.NewMemoryDatabase() - gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}} + sdb = rawdb.NewMemoryDatabase() + ldb = rawdb.NewMemoryDatabase() + gspec = core.Genesis{ + Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, + BaseFee: big.NewInt(params.InitialBaseFee), + } genesis = gspec.MustCommit(sdb) ) gspec.MustCommit(ldb) From b02922fc53d599e89209ee71e8ffebb13a4e98b0 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 28 May 2024 10:32:18 +0800 Subject: [PATCH 098/479] core, internal: expose effectiveGasPrice in receipts (#23050) --- core/tx_pool.go | 2 +- core/types/transaction.go | 4 ++-- internal/ethapi/api.go | 12 +++++++++++- internal/jsre/deps/bindata.go | 2 +- internal/jsre/deps/web3.js | 4 +++- 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index cef3c78542bc..9c7f3f7dbc09 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -553,7 +553,7 @@ func (pool *TxPool) Pending(enforceTips bool) (map[common.Address]types.Transact // If the miner requests tip enforcement, cap the lists now if enforceTips && !pool.locals.contains(addr) { for i, tx := range txs { - if tx.EffectiveTipIntCmp(pool.gasPrice, pool.priced.urgent.baseFee) < 0 { + if tx.EffectiveGasTipIntCmp(pool.gasPrice, pool.priced.urgent.baseFee) < 0 { txs = txs[:i] break } diff --git a/core/types/transaction.go b/core/types/transaction.go index f39a96c734e3..0bb03f43c5fd 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -385,8 +385,8 @@ func (tx *Transaction) EffectiveGasTipCmp(other *Transaction, baseFee *big.Int) return tx.EffectiveGasTipValue(baseFee).Cmp(other.EffectiveGasTipValue(baseFee)) } -// EffectiveTipIntCmp compares the effective gasTipCap of a transaction to the given gasTipCap. -func (tx *Transaction) EffectiveTipIntCmp(other *big.Int, baseFee *big.Int) int { +// EffectiveGasTipIntCmp compares the effective gasTipCap of a transaction to the given gasTipCap. +func (tx *Transaction) EffectiveGasTipIntCmp(other *big.Int, baseFee *big.Int) int { if baseFee == nil { return tx.GasTipCapIntCmp(other) } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 2b636a77181f..32c7d1d52c0c 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2194,7 +2194,17 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha "logsBloom": receipt.Bloom, "type": hexutil.Uint(tx.Type()), } - + // Assign the effective gas price paid + if !s.b.ChainConfig().IsEIP1559(bigblock) { + fields["effectiveGasPrice"] = hexutil.Uint64(tx.GasPrice().Uint64()) + } else { + header, err := s.b.HeaderByHash(ctx, blockHash) + if err != nil { + return nil, err + } + gasPrice := new(big.Int).Add(header.BaseFee, tx.EffectiveGasTipValue(header.BaseFee)) + fields["effectiveGasPrice"] = hexutil.Uint64(gasPrice.Uint64()) + } // Assign receipt status or post state. if len(receipt.PostState) > 0 { fields["root"] = hexutil.Bytes(receipt.PostState) diff --git a/internal/jsre/deps/bindata.go b/internal/jsre/deps/bindata.go index fc97d9751a71..bae4115a6c00 100644 --- a/internal/jsre/deps/bindata.go +++ b/internal/jsre/deps/bindata.go @@ -98,7 +98,7 @@ func bignumberJs() (*asset, error) { return a, nil } -var _web3Js = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xfd\x6b\x7b\x13\x39\xd2\x38\x0e\xbf\xcf\xa7\x50\xfc\xdc\x0f\xb6\x89\xb1\x9d\x84\x61\x18\x67\x32\x6c\x08\x30\x64\xef\x81\x70\x01\xd9\xd9\xbd\xb3\x59\xae\x8e\x5b\xb6\x7b\x68\x77\xfb\xd7\xdd\xce\x61\x48\xbe\xfb\xff\x52\xe9\x54\x3a\xf4\xc1\x49\x98\xd3\x26\x2f\xc0\x2d\x95\x4e\xa5\x52\xa9\x54\x2a\x55\x65\xf4\xff\x2d\xa3\x8c\xee\x76\x26\xcb\x64\x5c\x44\x69\x42\x68\xa7\xe8\x25\xbd\xac\xfb\x45\xa5\xe4\x9d\xb4\xb7\xec\x7e\x89\x26\x9d\xf5\xe4\x38\x3d\xe1\xbf\x0a\xf8\x75\x16\x64\x24\xd8\x2d\x2e\x17\x34\x9d\x10\x59\xd7\x6e\x4b\x16\x6d\x3d\x78\x20\x12\x77\x58\x99\xe5\x83\x07\x41\x37\xa3\xc5\x32\x4b\x48\xd0\x49\x7b\xeb\xc3\x2e\x4b\x8f\x64\x5a\x24\xd2\x58\xad\x93\xdd\x84\x9e\x93\x97\x59\x96\x66\x9d\xd6\x7e\x90\x24\x69\x41\x26\x51\x12\x92\x79\x1a\x2e\x63\x4a\xda\xad\x8d\x74\xa3\xd5\x6e\x75\x77\x8a\x59\x96\x9e\x93\x49\x7f\x9c\x86\x74\xb7\xf5\xe6\xf0\xc5\xd1\x4f\x2f\x3f\xbd\x3d\xfc\xf8\xe9\xd5\xe1\xd1\xdb\x17\xad\xde\xe4\x9a\xd5\x17\xef\xb2\xbe\xef\x7e\xa1\x17\x8b\x34\x2b\xf2\xd1\x97\xeb\xeb\x1d\x36\x86\xe3\xe1\x49\x7f\x1c\xc4\x71\x27\xee\x8b\xac\x9e\xec\x7d\x87\xf2\x01\x26\xbb\x00\xb8\x79\x72\x4c\x4f\x76\x44\x57\xf3\x4e\xf2\x2c\x19\xd1\xee\x75\x2f\xee\xe9\x92\xb4\xc7\x71\x77\x2d\xa0\x58\x93\x32\x13\x7a\x11\x35\xc2\xd5\x24\xcd\x3a\x0c\x3a\xdd\x1d\xee\xa4\xdf\x67\xfd\x98\x26\xd3\x62\xb6\x93\x6e\x6c\x74\xf3\x4e\xc6\x10\xaf\xba\x71\xdd\xed\x7c\xd9\x1c\x1d\xab\x2e\x8b\x2a\x7a\x1c\x4b\x3d\xd1\x76\xf7\xcb\x1a\x4f\x90\x9d\xd9\x3d\x5e\x23\xe4\xcb\x1a\x21\x84\xb4\xc6\x69\x92\x17\x41\x52\xb4\x46\xa4\xc8\x96\xb4\xc7\x53\xa3\x64\xb1\x2c\xf2\xd6\x88\x1c\xc3\xb7\x84\x86\xbc\x24\x98\xd3\xd6\x88\xb4\x3e\xa5\xe7\x09\xcd\x5a\x3d\x9d\xc3\x46\xc7\x72\x82\x30\xcc\x68\x9e\xb7\x44\xce\x35\xfc\x7f\x22\xaa\x96\xc5\xe1\x7f\x91\x96\x2e\x8b\xfa\xf6\xd2\x4f\xa8\x88\xd1\xde\xe9\x65\x41\xf3\xed\x2d\x7f\x7b\x12\x48\x61\x7a\x8d\x90\xeb\xde\x9d\x20\xe0\x46\xfd\x51\xc3\x41\xd8\x6b\x86\x80\x95\x51\xfd\x47\x1d\xfa\x38\x4d\x0a\x9a\x14\xb7\x1e\xfc\x9f\x72\xde\xd9\x8c\xfd\x61\xa6\x7d\x12\xc4\xf9\x6f\x37\xf4\x8c\xe6\x34\x3b\xf3\xad\xfa\x3f\xfa\xa4\xe5\xcb\xd3\xf7\x74\x1a\xe5\x45\x16\xfc\x17\x4c\x5e\xaf\xaa\x0e\x7a\x7e\x78\x2b\xbe\x5f\x64\x41\x92\x4f\xbc\xac\xef\xcf\x82\x83\xcc\x22\x85\xd5\x91\x90\xd3\xe2\x43\x35\x49\xdd\x19\x2e\xec\xa6\x7f\x93\x46\xbf\xf2\x04\x04\x4d\x10\x5f\x55\xc1\x22\x8b\xe6\x41\x76\xe9\xed\x47\x9a\xc6\xb5\x93\xb7\x27\xda\xfa\xf3\xa2\xd0\xdc\x83\x2b\xab\x29\x43\xc2\x7e\xe9\x36\xfe\x47\x42\x82\xb7\xf7\x61\x94\xa7\xe7\xc9\x2d\x7a\x1e\x24\x69\x72\x39\x4f\x97\xf9\x0a\x5d\x8f\x92\x90\x5e\xd0\xd0\xd8\xbb\xee\x6c\x62\x75\xe5\xa8\x3b\x66\xed\xe7\x51\x72\x1b\xc6\xbd\xb7\x04\x4c\xbc\x4c\x42\x1a\xb6\x2c\x34\xd1\x33\x46\x08\x7f\x01\x1c\x9d\x46\x61\xd8\x0c\x47\x37\xab\xff\x2c\x88\x97\xde\xee\x2f\xa3\xa4\xd8\xfa\xe6\x49\xf5\x14\xbc\xa5\xe7\xcf\xa3\xdf\x11\xf9\xb7\x5a\x73\xfb\xb3\x20\x99\xfe\x9e\xa4\x73\x27\x94\x53\x52\x37\x92\xea\x2b\xa9\xc6\x8b\x99\x77\x7c\x37\xaa\x45\xd0\xda\xc9\xda\xda\x75\xef\xcb\xf5\x49\x6f\xeb\x77\x3b\xf4\xff\x85\xce\xbc\xbf\x93\xec\x38\x59\x26\xe1\x8d\x49\xe5\xd6\x1b\xd7\xfd\xb1\xf7\xcf\x7d\xec\xbd\x3f\xf4\xfd\x91\xcf\x1c\xde\xc1\x8b\xf3\xc2\x1f\x4d\xda\xfc\xba\x9b\xb9\xde\xab\xb6\xef\x6c\xaf\x5a\x75\xde\x27\x59\x3a\xbf\xe5\xb4\x17\xe9\x2d\x8f\x9a\xb7\x13\xf8\x7e\xdf\x75\xf3\x47\xc0\x5f\x94\x84\x51\x46\xc7\xc5\x81\x77\xcf\x5c\xa1\x27\xb7\x9b\x88\x68\x1c\x2c\x3e\xfe\xae\x93\xe1\xc7\x64\xb3\xd3\x2e\x5d\xa4\x79\x54\x75\x50\x5f\x04\x97\xc1\x69\x4c\x4d\xa1\xe0\x77\xe1\x4a\x65\x34\x77\x27\xc7\xaf\xdb\xd1\xc0\x9e\x1c\xef\x0b\x13\x9f\xbf\xfd\x49\xe6\x4e\x90\x54\x52\x77\x33\x3a\xfb\x1d\xd0\xff\x87\xc5\xfa\x5d\x9c\x1f\x6f\xcc\x27\xbf\x36\xd6\x6d\xa6\x77\x8f\xf6\x86\x68\xbf\xf5\xc6\xf5\xb5\x67\xf6\xc0\xb3\xa5\x55\xc9\x71\x8f\x9b\xc8\x71\x60\xbc\x41\x76\xa5\x85\x43\xa7\xdd\x1f\x4c\xd2\x6c\x1e\x14\x05\xcd\xf2\x76\x77\x07\x00\x3e\xa4\x71\x14\x46\xc5\xe5\xc7\xcb\x05\x35\x61\x59\xfb\x0c\x6a\x6d\xf0\xf0\xe1\x1a\x79\x68\x40\x0a\x9d\x3b\x89\x72\x12\x90\x45\x96\xa6\x0c\x98\x14\xb3\xa0\x20\x19\x5d\xb0\x43\x56\x52\xe4\x44\xcc\x1d\x61\x99\xac\x86\x83\x82\xcc\x83\x62\x3c\xa3\xf9\x88\x7d\x8a\x6c\xf4\xf3\xf8\x04\x7f\x3c\x36\xbe\x4e\xcc\xcc\x6d\xeb\xfb\xe4\xf8\xc9\xc9\xf1\x49\x8f\xf4\xfb\xfd\x35\xf2\x70\xe0\x8c\x4d\xf6\x78\x97\x28\x6b\x9a\x4e\x57\x4c\x71\x31\x8b\xf2\xfe\x27\x58\x18\xaf\x24\x82\x18\x60\x9f\xa3\xeb\x80\x65\x1c\x24\xc5\x0e\x02\xe6\xfb\xb6\x0f\xfa\x10\x72\x44\x73\x3b\x6b\xd7\x3b\x6b\x6b\x9e\x7e\xf4\x17\x59\x5a\x70\xac\xed\x92\x84\x9e\x1b\x7d\xed\x7c\xb9\xee\xee\x54\x97\xea\x83\xf4\x92\x2d\xc7\x45\xca\x1a\xf7\xc0\xd6\xb5\xdb\x8f\x72\x31\xe7\x1a\x21\x8c\x1c\x25\x52\x84\x5d\xcb\xfa\x3a\x4b\xec\xc3\xbc\x75\x06\x02\xdb\x9d\x7f\x1f\x77\x8e\x87\x8f\xbe\x3b\x79\xd8\xfd\xf7\x49\xf7\xd9\xa0\xcb\xc7\x69\x1e\x1c\x4a\xbb\x75\xdd\xfb\xd2\xc2\xa4\xd8\x1a\x7d\xd7\x6b\x71\x7a\x6b\x8d\x36\x1f\x5f\x9f\xf4\xbe\xf9\x9d\xc9\xfb\x79\x9a\xc6\x35\xb4\x7d\xca\x40\x4a\x08\x9b\xe5\xc9\xff\x39\x95\xc2\xaf\xc7\xfa\xe7\x09\x4a\xde\xc6\x1f\x75\x64\x0c\x3d\xbb\x29\x0d\xb3\xc2\xab\x10\x31\x87\xb7\x29\x98\xa5\xae\x48\xbe\x66\x91\x0a\xda\xe5\x2d\x56\x95\xbd\x09\xd5\xfe\x87\xa1\xd6\xa4\xd9\x87\xff\xd3\x88\x68\x45\x7f\xea\x29\xf6\xc9\xef\x4d\xb1\x6c\x0f\x53\x24\x5b\xf8\x69\xb6\x98\x51\x02\x9b\x1d\x10\x6e\xdf\x47\xb9\x2c\x57\xfd\x10\x74\x09\x3f\x1f\xa3\xdf\x27\x38\x63\xdb\xf8\x32\xe9\x97\x88\xad\x55\xfd\x7c\x6a\xd4\x23\x8a\x7a\xa8\x1c\x3a\x79\x63\x32\x67\xa5\x57\xa2\x73\x5e\xc0\x21\x74\x96\xbc\x2a\xa5\x9b\x65\xaa\x48\x9d\x37\x5a\x59\xfa\x66\xc4\xce\x2a\xe1\xa4\xfe\x65\xb3\x77\xdd\xbd\x19\xe1\x8b\xde\xd5\x53\xfe\xb7\x4d\x28\x7f\xf0\x10\x3a\xfc\x71\x16\xe5\x64\x12\xc5\x94\x51\xea\x22\xc8\x0a\x92\x4e\xc8\x39\x3d\xdd\xee\xff\x92\xf7\xd7\x00\x44\x7c\x31\x80\x49\x46\x29\xc9\xd3\x49\x71\x1e\x64\x74\x44\x2e\xd3\x25\x19\x07\x09\xc9\x68\x18\xe5\x45\x16\x9d\x2e\x0b\x4a\xa2\x82\x04\x49\x38\x48\x33\x32\x4f\xc3\x68\x72\x09\x75\x44\x05\x59\x26\x21\xcd\x80\xe0\x0b\x9a\xcd\x73\xd6\x0e\xfb\xf8\xf1\xed\x11\xf9\x89\xe6\x39\xcd\xc8\x8f\x34\xa1\x59\x10\x93\x77\xcb\xd3\x38\x1a\x93\x9f\xa2\x31\x4d\x72\x4a\x82\x9c\x2c\x58\x4a\x3e\xa3\x21\x39\xbd\x14\x54\x44\xc9\x2b\xd6\x99\x0f\xa2\x33\xe4\x55\xba\x4c\xc2\x80\x8d\xb9\x47\x68\x54\xcc\x68\x46\xce\x68\x96\xb3\x19\xda\x96\x6d\x89\x1a\x7b\x24\xcd\xa0\x96\x4e\x50\xb0\x31\x64\x24\x5d\xb0\x82\x5d\x12\x24\x97\x24\x0e\x0a\x5d\xd6\x45\x81\x1e\x69\x48\xa2\x04\xaa\x9d\xa5\x72\x65\x47\x05\x39\x8f\xe2\x98\x9c\x52\xb2\xcc\xe9\x64\x19\x73\xc1\xf1\x74\x59\x90\x9f\x0f\x3e\xbe\x3e\x3c\xfa\x48\xf6\xde\xfe\x8b\xfc\xbc\xf7\xfe\xfd\xde\xdb\x8f\xff\xda\x21\xe7\x51\x31\x4b\x97\x05\x61\x12\x25\xd4\x15\xcd\x17\x71\x44\x43\x72\x1e\x64\x59\x90\x14\x97\x24\x9d\x40\x15\x6f\x5e\xbe\xdf\x7f\xbd\xf7\xf6\xe3\xde\xf3\x83\x9f\x0e\x3e\xfe\x8b\xa4\x19\x79\x75\xf0\xf1\xed\xcb\x0f\x1f\xc8\xab\xc3\xf7\x64\x8f\xbc\xdb\x7b\xff\xf1\x60\xff\xe8\xa7\xbd\xf7\xe4\xdd\xd1\xfb\x77\x87\x1f\x5e\xf6\x09\xf9\x40\x59\xc7\x28\xd4\x50\x8f\xe8\x09\xcc\x59\x46\x49\x48\x8b\x20\x8a\xe5\xfc\xff\x2b\x5d\x92\x7c\x96\x2e\xe3\x90\xcc\x82\x33\x4a\x32\x3a\xa6\xd1\x19\x0d\x49\x40\xc6\xe9\xe2\xb2\xf1\x44\x42\x65\x41\x9c\x26\x53\x18\xb6\xa2\x32\x42\x0e\x26\x24\x49\x8b\x1e\xc9\x29\x25\xdf\xcf\x8a\x62\x31\x1a\x0c\xce\xcf\xcf\xfb\xd3\x64\xd9\x4f\xb3\xe9\x20\xe6\x15\xe4\x83\x1f\xfa\x6b\x0f\x07\x92\xd9\xfe\x0d\xc8\x76\x9c\x86\x34\xeb\xff\x02\x2c\xf2\x6f\xc1\xb2\x98\xa5\x19\x79\x13\x64\xf4\x33\xf9\xdf\xb4\xa0\xe7\xd1\xf8\x57\xf2\xfd\x9c\x7d\xff\x8d\x16\xb3\x90\x9e\xf5\xc7\xe9\xfc\x07\x00\x0e\x83\x82\x92\xad\xe1\xe6\x37\xc0\xf0\xea\xb7\x82\x0a\x01\x16\x95\x11\xf2\x98\x6f\xef\x10\x92\x02\x02\x66\xbb\xa0\x0f\xf2\x20\x29\x4c\xc0\x28\x29\x7c\x70\x47\x0e\xe0\xb2\x04\xf2\xc5\x65\x12\xcc\xa3\xb1\x64\xe3\xa8\x44\xc8\x73\x80\x47\xf9\x4a\x7e\x28\xb2\x28\x99\x9a\x65\x72\x48\xf3\x41\xbf\xa7\x81\x35\xc6\x8c\x06\xde\x31\x1e\xb9\xa0\xcb\x32\x58\x4f\xb7\x55\x7f\x01\x38\xca\xc5\x00\x0d\xce\x9c\xa3\x2a\x7a\xb0\xc3\x0a\x3e\x2d\x2d\xc4\x51\x7e\x5f\x55\x01\xdb\x08\x07\xbe\xba\x52\xa7\x47\x52\x02\xbd\x97\x65\xc1\x25\x07\xe7\x4c\xdc\x12\x05\xf6\x19\x7d\x22\x09\x40\xac\x24\xce\x21\x42\x52\xa4\x84\x26\x8c\x86\x07\x21\x65\xff\xa9\x56\x18\x33\x0e\x38\x9b\x64\x5c\x49\xc8\xb5\xe6\xc6\xcc\xeb\xc6\x23\x66\x60\xb9\xb9\x33\x43\x12\xd9\x85\x1a\x72\xa3\x8b\xc0\xfb\xe7\xb4\x98\xa5\xa1\xa7\x5b\x5c\xb9\x9e\x66\x73\xc2\x25\x97\xd4\x98\x91\x35\xc2\xd7\xa0\x28\xfe\x49\xcc\x8c\xc8\x22\x7f\x83\xde\x93\x2f\x9c\x78\xae\x95\x58\xfe\x37\x8e\xf9\x9c\x7c\xc1\x95\x5d\x43\x16\xbc\x55\xc8\xc9\x17\x78\xd7\x70\x4d\xc4\x67\xc4\x78\x03\x97\x88\x18\x19\x42\x5f\xd8\x4e\xc4\xd8\x3d\x20\xc4\x40\x06\xda\xa9\x71\x97\x1c\x1c\x49\x14\x31\x6c\xe6\xa6\x78\x87\xb0\xd6\x9f\x44\x71\x41\xb3\x0e\x2a\xdb\x45\x3a\x08\x41\x45\x85\x10\x0a\x24\x11\x80\x4e\xa1\x7b\x3c\x3c\xd9\xe1\xfc\x33\x9a\x90\xce\x3a\x6e\x04\xd7\xc1\x1f\x68\xf0\xa7\x1c\xed\x28\x39\x0b\xe2\x28\xd4\x34\xc0\x6a\x5c\x1f\x91\x36\xd9\x20\xb8\xf2\x35\x2c\x6b\xe0\x9a\x4d\x0a\x2c\xa1\x34\xb2\x88\x83\x28\xe1\xf4\x65\x4d\x23\x07\x78\x27\x72\xca\x67\x51\xa4\x1f\x9e\xfe\x42\xc7\xc5\xb5\x55\xa1\x9c\x64\x5d\x8e\x57\x1b\x5a\x70\xe5\x53\x87\xba\xe1\xcc\x5c\x8f\x97\xb7\x04\x2e\x98\x34\x54\x2c\xef\x1c\x33\xe0\x93\x1e\x39\x06\xf0\x93\x6e\x33\xd4\xc4\x51\x0e\x12\x10\x5f\x7c\xe5\xd8\xc9\x31\x1a\x80\x05\x70\xec\xf8\xd2\x17\xba\x40\x19\x62\x9c\x66\x1b\xe1\x26\x77\x97\xbe\xc0\x4e\x5e\x46\xdf\xb9\x24\xf0\x29\x2d\xf0\x0a\xcc\x05\xe7\x10\x24\xcb\x8a\x89\xbe\xb1\x12\x46\x0d\xfd\x79\xb0\xe8\x94\xf1\x58\xd0\xca\x79\xd6\x88\xc1\x3b\x79\xcd\x1d\xde\xd3\x63\x28\x72\xc2\xd9\xb3\xfc\x52\xab\x08\xf5\x47\xec\x53\x87\x93\x49\x4e\x0b\xa7\x53\x19\x0d\x97\x63\x8a\xfa\x15\x8c\xc7\x3d\x52\xd3\x39\xc0\x4e\x11\x14\xd1\xf8\x5d\x90\x15\x3f\xc1\x4b\x22\xab\xe6\xbe\x9d\xdf\xf1\xf4\x53\xd6\x95\x31\xa6\x44\xc3\x0f\x6e\x95\x6f\x82\x62\xd6\x9f\xc4\x69\x9a\x75\x3a\x4e\x8b\x1b\x64\x7b\xb3\x4b\x06\x64\x7b\xab\x4b\x1e\x92\xed\x2d\x31\x68\x84\xbe\x60\x3c\x26\x1b\xa4\xa3\x36\x1d\x03\xeb\x25\x28\x24\xcf\xd0\xde\x45\xc8\xf6\x16\x19\x19\x09\x25\x9d\x95\xa8\xef\x91\x21\xc6\x7e\x46\xf3\x65\x5c\x48\xea\xe1\x33\xf8\x66\x19\x17\xd1\xcf\x51\x31\xe3\x73\x22\x29\xd0\xe8\x5b\x4f\xd1\x51\xcf\x9c\x41\x59\xb9\x18\x21\xaf\xdf\x3c\xf1\xf9\x49\xdf\x6a\xd5\xb7\x06\x1a\xf6\x00\xad\x11\x35\xbc\x56\x6b\x47\x2f\x1c\x1a\x4f\xc4\x88\x45\x67\xc5\xae\x90\x66\x2f\x83\xf1\xac\x63\x33\xa6\x08\xd3\x16\xe3\xfa\xa5\xf3\xa5\xe7\xea\xa4\x8b\x0b\x71\x84\x40\x57\x36\x5c\x6d\x67\xc7\xec\xbe\x5c\x47\x88\x08\xd5\xda\x65\x54\x4c\xe3\x89\x00\xb1\xe7\x08\x3a\xe0\x76\x49\xe2\x09\x3e\xec\xc9\xc2\x4d\x98\x4b\x71\x63\x97\x50\xf1\x0c\x8f\x0c\xc8\x96\x06\xbd\x26\x34\xce\xa9\x35\xbc\xc1\x80\x84\x69\xd2\x2e\x48\x10\x86\x44\x94\x2a\x52\xb3\xca\x3e\x89\x8a\x76\x4e\x82\x38\xa3\x41\x78\x49\xc6\xe9\x32\x29\x68\x58\x82\xa5\xaf\x34\xce\x6b\xbd\x08\x07\x03\xf2\xf1\xf0\xc5\xe1\x88\x4c\xa2\xe9\x32\xa3\x84\x1d\xd8\x12\x9a\xb3\x13\x20\x3b\xa5\x5d\xe6\x26\xb3\xfa\x2d\x88\xe4\x8f\x33\xc9\xe6\x64\x50\x8c\x40\x89\x95\x92\x65\xae\xd0\x9a\xd1\x49\x00\xea\x98\xf3\x59\x1a\x53\xde\xc3\x28\x99\xae\xd7\x30\x82\x0a\x1e\x60\x73\x7e\x31\xe8\x1e\x49\x9d\x95\x6f\x2c\x72\x39\x27\xb5\xa2\xbe\x67\x8b\xeb\xb8\xaa\x31\x44\x40\xbc\x61\x72\x1e\x68\xb2\xce\x69\xe1\xcc\x29\x27\xab\xb7\xc1\x9c\xda\xfb\x90\xce\xc1\x72\xa6\x5b\xd6\xb3\xf9\x54\xef\x67\xba\x62\x4f\x9d\x8a\x2f\x0a\x0c\x6a\xa9\x56\xfe\x55\x0c\x5b\x56\xb2\xc8\xe8\x59\x94\x2e\x73\xd5\xa1\xad\x1d\x86\x92\x28\x21\x51\x52\x38\x25\xea\xf0\x8f\xfa\xeb\x6b\x90\xfd\x4d\xd2\x8c\xc0\x23\xe1\x88\xec\x92\xcd\x1d\x12\x91\xef\xe5\x00\xe4\x7b\x61\x12\x6d\x6c\x94\x15\x67\x7f\x56\x9f\x37\x76\xc9\x46\x47\xe2\x20\x22\x8f\xc8\xe6\x09\x93\xf0\xc9\xd5\x15\x19\xee\x94\x56\x52\xc1\xca\x05\x3d\x6c\x90\x88\x3c\x2c\x9b\xb9\x0d\xbb\x17\x4c\x38\x28\x63\xfb\xf2\xef\xda\x49\x35\x53\xae\xbb\x9d\xae\x35\x85\x83\x01\x99\x44\x59\x5e\x10\x1a\xd3\x39\x4d\x0a\x76\xbe\xe2\x68\xea\x91\xfc\x73\xb4\x20\x51\xb1\xca\x94\x1b\xd8\x1f\xfa\xb0\xcf\xf0\x57\x39\x03\xf0\x74\x3e\x0c\x23\xd6\x48\x10\xab\x45\x2e\xf0\xe9\xf0\x1f\x17\xdf\x7e\xbe\xa8\x49\xa7\x84\x41\x1c\x47\x64\x83\x6c\x9e\x48\x3e\x41\x36\x88\xd3\x0d\x0f\xda\x6b\x11\x6c\x31\x3f\x0f\xa4\xd8\x2a\x3d\xb4\xcf\xa9\xe2\xc6\xac\xe7\x0f\xcd\x54\x98\xb0\x65\x62\xea\x96\x8b\xbf\x86\x32\x49\x19\x43\x1a\x56\x31\x24\xd2\x88\xa6\x6b\x39\xca\x60\x40\xc6\x41\x3c\x5e\xc6\x41\x41\xa5\xe0\xc3\x8e\x7c\xa2\x2f\x24\x2a\xe8\xfc\x16\xec\x88\xb1\xa2\xe3\x3f\x11\x53\xea\xda\xb0\xd7\x2b\xed\x2b\xb7\x9c\x90\xdf\x8f\xc1\x60\xe6\xf2\xd5\x79\x0b\x71\xb4\x45\xa2\x1f\x35\xda\x10\xa1\x8b\x14\x37\x93\x69\x85\xc6\x88\x43\x36\xd6\x18\xc9\x74\x75\xab\xa9\x54\x22\x7e\x5d\x52\xb9\x1e\x04\x35\xec\x11\xff\xa0\x7e\x9f\x8e\x08\x15\xd3\x3a\x22\x0e\x0d\xb2\x4d\x13\xb4\x54\x2a\x89\x4a\x10\x52\xa6\x23\x2a\x47\x88\x28\x01\x27\x0c\x68\x4d\x23\xa6\x5a\x43\x84\x87\xe8\x3b\x1d\x1b\xb8\x59\x5d\x41\x24\x4b\x71\x2a\xc6\xf0\x9c\x88\x73\xef\x29\xdc\x3a\xee\xdf\xb1\x46\x89\x0f\xb9\x03\x23\x93\xeb\x4b\xab\x45\x0c\xbd\x88\xac\x51\x6b\x98\xaa\x54\x0e\x7a\x54\xb5\x7a\x06\x8c\x51\xce\x81\x58\x99\xbb\x1e\x69\x13\x75\x94\x3a\x89\xfa\xe4\x60\xd1\xb5\x52\x26\x39\x18\x90\x7c\x39\xe7\x37\x74\x9e\x5d\x4a\x88\x88\x0a\x5e\x54\x77\x1c\x9d\x30\xae\xa8\xbe\x60\x4b\xf2\xf1\x1f\xd9\xbc\x89\x08\x29\x6d\x3a\x28\x18\x0c\x48\x46\xe7\xe9\x19\x5c\x63\x92\xf1\x32\xcb\x98\x7c\xaa\x84\xd3\x14\x92\x45\x37\xa3\x1c\x7a\xee\xe9\x6d\xbe\x8a\xc6\x4f\x22\xb3\xb1\xe6\xcf\x18\x19\x79\xe4\xd4\xdf\x98\xd2\x3e\x58\xeb\xb0\xe4\x5a\xc7\x7b\x6a\x95\x3c\xce\x43\x65\x85\x75\xe5\x20\xc9\x8a\xed\x60\xf8\x92\xc4\xbc\xbf\xe0\xbd\x65\x6d\x8d\xc5\x2d\x13\x36\xb5\x80\xde\x77\xb8\xbd\xaa\x6d\x82\x21\xae\x45\x3b\xdd\x9e\x37\xfb\x79\x9a\xc6\x65\x79\x4c\x08\x29\xc9\x3a\xaa\xc8\xc3\x97\x9b\xa5\xcd\x56\x65\x72\x2e\x5c\x96\xfb\x9e\x06\xa5\x3d\x3e\xe2\x99\x6b\x8c\x20\x5c\xfb\x0d\x40\x9d\xb2\xd9\x90\x86\xb3\xa3\xc7\xbd\x16\xbf\xfb\x6d\x8d\xbe\x81\x9f\xac\x6f\xad\xd1\x13\xf6\x1b\x5f\xc7\xb6\x46\x4f\x7b\x3e\x5b\x8f\x28\x29\x5a\xa3\xcd\x21\xfb\x99\xd1\x20\x6e\x8d\x36\xb7\xd8\x6f\x7e\x2b\xdb\x1a\x6d\x6e\xb3\xaf\x25\x87\x82\x06\x96\x02\xec\xc9\xf5\x49\xef\xe9\x6f\x69\x17\x55\x73\x0d\x7d\x33\x6b\x22\x5c\xc9\x2a\x46\x45\x66\x39\xdb\xb6\x08\xe7\xae\x68\x62\xe4\x2f\x5a\x61\x69\x64\xf6\xa4\x49\x5d\xb7\xb0\x3b\x2a\x31\x36\x6a\xd4\x28\xba\x12\xf7\x4e\x97\x64\x3b\xd9\x92\x36\x30\x61\xb2\x86\x5d\x6f\xc9\xf4\xdd\xbd\x25\xd3\xbd\x25\xd3\x7f\x8b\x25\x93\x5e\x08\x77\x65\xce\xf4\x3c\x9a\xbe\x5d\xce\x4f\x81\x15\x2a\xee\x7c\x1a\x4d\x13\x48\xec\xff\xa2\x38\xf9\xb2\x88\x62\xd3\xbe\xa6\x3f\x80\x34\xfe\xaf\x04\x1b\x7b\x41\xc6\x69\x32\x89\x1c\x63\x20\x79\x32\x43\xbb\x02\x9c\x5d\x60\x5b\x90\x03\xe7\xbc\x3a\x27\xc0\xef\x09\x3c\xd8\x60\xe7\x2c\xc6\xb7\xb4\x95\x2c\x2c\x05\x36\x37\xa0\x9c\x79\xc8\x70\xcc\x21\xa3\x9c\x24\x74\x1a\x14\xd1\x19\xed\x49\x4e\x04\x17\x47\xc5\x79\xda\xce\xc9\x38\x9d\x2f\xa4\xb4\x0a\xa5\xd8\xdc\xaa\x92\x93\x38\x0d\x8a\x28\x99\x92\x45\x1a\x25\x45\x8f\x5f\x87\x32\xb2\x0f\xd3\xf3\xc4\x3a\xd3\x99\x6a\x12\xf7\xf8\x76\xc5\xb1\x7c\xa5\xf0\x7d\x2d\xc7\xc2\x96\x52\x42\x69\x08\xa7\xe8\x53\x3d\xc7\xa1\xdf\x18\x06\x90\x76\xad\xec\x7c\xcc\x76\x0d\x06\x0c\xf5\x4b\x2e\xac\xda\xed\xf3\xb9\xe8\x8c\xfb\x2f\x3f\xbe\xfe\xf4\xfc\xe0\xc7\xb7\x47\x6f\x9e\xbf\x7c\xff\xe9\xfd\xe1\xd1\xdb\x17\x07\x6f\x7f\xfc\xf4\xe6\xf0\xc5\x4b\x74\x86\x53\x9a\x38\x98\xc9\xfe\x22\x08\x7f\xa2\x93\xa2\xc3\xbf\x8a\xf4\xe3\x79\x9a\xef\x2b\x2c\x8a\x36\xfb\x45\x2a\xc4\xa5\xcd\x27\xdd\x1e\x79\xf2\xd8\xbc\xe1\xc1\xbb\x25\x0c\xa7\xc3\x1b\x31\x0d\x30\xcc\x89\x97\x87\xdf\x12\x9c\x3f\x57\x67\x63\xf3\xd0\xbc\x2a\x0e\x5d\xa9\xc3\xc0\xa2\x07\x21\x45\xfa\x9a\x5e\xc8\x71\xe7\xcb\xd3\xbc\xc8\x3a\x5b\x08\x7f\xb1\x75\xb5\xcf\x8b\x4b\x2d\xf7\x06\x79\xb2\xdd\x25\x03\x8c\x22\x1b\xdd\xef\xa3\xe9\xac\x10\xc5\x7a\x24\x26\x0f\xbf\x32\x3e\xc5\x0e\x7c\xa7\x68\x2d\x95\xe9\x6e\x8d\x5d\x79\x3c\x33\xd1\xaa\xb4\x73\xbf\xdb\x0c\x58\x6a\x53\xde\x58\xb7\xcf\xd7\xfc\x06\xa9\x9f\xa0\x3a\x4e\xc7\x25\xf9\xf2\x15\xf1\x41\xe6\xdf\x76\xee\x94\x71\x67\xf3\x59\x9b\x64\xe9\xfc\xa8\x98\x3c\xbd\x9f\x38\xcf\xc4\x89\x77\x46\x65\x8c\x4c\xbc\x42\x92\x93\xc6\xbe\x69\x90\xac\xce\xc8\xec\x27\x47\xe5\x73\xd6\x1e\xde\xee\xaf\x4d\x36\x44\xf5\xe4\x19\x21\xed\xcd\x36\x19\x91\xf6\xb0\x7d\x7b\x1e\x55\x87\x49\x76\x62\x65\xa5\xfe\xc1\xe0\x72\xc2\x04\xe3\xf9\x32\x2e\x22\x2e\x54\x9e\x5e\x92\xad\xff\xcc\x99\x78\xae\x6c\xe8\x02\x56\x73\x41\xa7\x34\xab\xd8\x4a\xde\x8b\x5a\xeb\xf6\xef\x55\x67\x44\xd8\x32\x97\xcc\x88\x40\x93\x45\x7d\x0c\x6b\xaa\x45\xb5\xb9\x46\x73\x9a\x5b\x59\x5b\xdd\xfe\x22\x3d\xef\x6c\x6e\x3d\xed\x76\x4d\x94\xee\xcf\xe8\xf8\x33\x89\x26\x06\x4e\x91\x58\x64\x21\x22\x8f\xa6\x09\x0d\x0f\xf2\xb7\x3a\xdb\x51\x44\xab\x3a\x66\xf4\x42\xf4\xd8\x44\x86\x24\x5a\x38\xf4\x41\xdb\x85\x29\x89\xa5\xec\xc8\x72\x1e\x31\x31\x3c\x88\x73\x6d\xb5\x6c\xb7\x5e\x8b\x2f\x1f\x86\x24\xbb\x19\xf6\xc8\x66\xb7\x47\x36\x9f\x20\x79\x64\xab\x6b\xe4\x76\xc9\xee\xee\x2e\x23\x59\x2f\x15\x66\x8c\x7d\x3c\x0a\x62\xe8\x14\xe1\xaa\x03\x7d\xe1\xc1\x45\x4d\x97\x88\xb8\x22\xc1\x16\x02\x0d\xf2\x70\xec\x60\x19\xce\xb4\x60\x58\xd1\xae\x12\x0e\x61\x59\x44\x53\xc2\xe5\x74\x8b\xde\x54\x17\x0c\xfc\x19\x46\xb1\x0c\x98\xcf\xe3\x2e\xef\x0d\xd2\x65\x76\xba\xe4\xea\x8a\xb4\x86\x2d\xa1\x23\x1e\x0c\xc8\x58\x51\x11\x13\x9e\xe5\x44\xaa\xd6\x39\x50\x54\xf0\x89\x56\x92\xb6\x2b\x64\xcb\xfb\x5b\x6b\x9e\xc5\xdc\x7a\x54\x90\x9e\xf9\xe5\x53\x3a\x8f\x92\xa5\xbd\x0a\xda\x93\x5b\xfe\xb5\xa1\x6e\x59\xf9\xa6\xba\x1e\x6b\xd0\xa1\x1b\x50\xd0\xb2\x9a\x84\x8e\x2a\x69\xc8\x47\x3d\x74\x25\xf2\x11\xcd\xbb\x84\x73\x74\x17\x94\xf3\x75\x50\x26\x58\x7e\x19\xca\x1c\xde\x5d\x8b\x32\xc0\x18\x12\x89\x4d\x14\x89\xe6\x5c\x14\x39\xcc\xdc\x67\x71\x6e\x2d\x46\x01\xd3\x0f\xa3\xb3\x28\xa4\xe1\xf3\xcb\x0a\x1e\x7e\x13\x6a\xaa\xc1\xcd\xd1\x5d\x23\x67\x59\x8a\x9d\xa3\x95\xd1\x73\x74\x1b\xfc\xb8\xb7\xb0\xbc\x6a\x85\xa2\x32\x89\x4b\x3f\x98\x6e\x8c\x17\xb9\xb3\x99\x73\x51\x8a\x23\xd1\xb4\x8b\x22\x47\x3e\xf3\x61\xc8\xb3\xbc\x60\xbf\xba\xa5\xc0\xb6\xd9\x26\xcf\xf8\xd6\x2c\x3c\x63\xac\x86\xcd\xd2\x93\x23\x7a\x97\x5b\xb1\xf7\xc5\x74\xa2\x11\xc7\x24\x88\x8a\xb3\x8d\x23\x7a\x24\xc1\x9c\xf2\x07\x3e\xec\x97\x25\x82\x09\x18\x56\xa7\xaa\xc1\x83\x79\xe7\x10\x0a\x6d\xf4\x08\x56\x96\xb3\x42\xe2\x89\x35\xd9\x25\x65\x2f\x75\x1f\x76\x07\xe8\x48\x93\x47\xbf\x0a\x9e\x98\xc3\x2d\x95\x28\x7f\xbc\x79\x62\x8a\xc2\xed\xe1\x05\x13\x99\xdd\xc9\xed\xe7\x71\x34\xa6\x4c\x32\xd9\x22\x0f\xa1\xba\x15\xe9\xbc\x66\x66\xf0\x29\xfc\xce\x26\x68\x55\xf4\x97\xaa\x02\x9c\x4d\x46\x1d\x11\x2d\x3e\xc0\x11\x27\x2e\xc1\x6c\xcc\x3d\x79\xdc\x15\x7b\x78\x91\x0a\xf8\x2e\x79\x28\x4f\x95\xbe\x19\xb0\x2a\xe2\xd2\xe1\x93\xc7\x3d\xd1\xfe\x6a\x53\x50\x71\x2a\xe7\xc3\xf7\x1c\xcb\xef\x14\xfb\x41\x3e\x8e\xa2\x2a\xfc\x7b\x8e\xf3\xbf\x21\xe6\xa5\x56\x07\xb4\x03\xcd\xf0\xbf\xda\x04\x68\xf7\x34\x65\x33\xb0\xa7\x1d\xd8\x94\x4c\x41\x29\x6f\x2f\x41\xb9\xaa\xd0\xc5\xb6\xcf\x81\xcd\x0a\xd2\x94\x81\xbb\xd6\xf0\xa2\x45\x36\x88\x38\xe3\x00\xda\xf9\x6f\x65\x56\xf0\x78\xd8\x23\x38\xa9\xcc\x67\xc0\x17\x69\xfa\x81\xce\x9a\x23\xeb\xbb\x67\xc3\xc0\x8a\x1d\x39\x29\x0e\x1c\x5e\xe0\xa3\xb2\x0c\xa7\x14\x47\xe6\xc8\x4d\x72\xfb\x91\xa6\xf1\xc8\x4e\x70\xa0\x98\x04\x32\xb2\x13\x30\x94\x12\xcb\x46\x76\x82\x0b\x75\xe4\x80\x1d\x79\xe1\x70\xa3\x3a\xc5\x53\x9f\x0b\x78\xe4\x87\xc4\x83\xd5\x29\x1e\x38\x8c\x6d\x94\xe4\x42\xfa\xa6\xc7\xcd\x71\xcb\x99\x13\x84\xd3\x5c\x58\x41\xf5\x23\xef\xba\xbb\x96\xd7\xba\xe6\xe5\x50\x6b\xb4\xf9\xb4\xd7\x32\x2f\x95\x5a\xa3\x2d\xb0\x60\x80\x85\xd1\x1a\x6d\x6e\xf6\x5a\xf8\x6a\xaa\x35\x32\x3f\xaf\x4f\x7a\x9b\xc3\xdf\xd9\xa5\xcb\x01\xb7\x8d\xaf\xf0\x41\x14\x25\x45\x99\x0b\x22\x71\x7b\x15\x25\x05\xf7\xce\xc2\x7e\x3c\x56\xbf\x4e\x74\xe2\x36\xfa\x6d\x39\x6f\x89\x92\x82\xbb\x6e\x89\x92\xe2\xc9\x63\x05\xf6\x54\x57\xb4\xf5\xcd\x93\x92\xba\x18\x7c\x8d\x2b\x23\xfb\x68\xf8\x15\xbd\x71\x01\xb8\x6d\x86\x70\x90\x14\x2b\x5a\x5e\x18\x25\x2a\x0c\x2e\xa0\xb9\x8a\x92\x37\x32\xaf\x88\x92\x42\x8a\x8a\xcf\x6e\xe4\xd2\x85\xf7\xaa\xde\x0c\x62\xb3\x51\x14\xbb\x7b\x3b\x88\x7b\x3b\x88\x3f\xaf\x1d\x04\xd1\x86\x10\x5c\x54\xba\x23\x1b\x88\x06\xa6\x0d\x36\xab\xe7\xa6\x0b\x29\x18\xa4\x6b\xcf\x1d\x7d\x8f\x84\x7a\x3e\xa3\x89\x7a\xaf\xd8\xe3\xb6\xdf\x4c\x00\x57\x0e\x1c\xa4\x64\x39\xf0\xda\x46\x58\xea\x6f\xfb\x79\x22\x70\x52\x29\x3f\xf2\xff\xaf\xae\x48\xbb\x8d\xf8\x6c\x2a\x5f\x2e\xf0\x1f\x3b\xe8\xa9\x61\x94\x88\xd6\x1b\x7b\xfc\x98\xd2\x02\x9b\xfc\x82\x01\x79\x3b\x97\x0f\x41\x81\x97\xb0\x4a\x0c\x6b\x77\x2d\xdf\x73\x63\x57\x53\x8a\x96\x6a\x26\x5d\x2b\xae\x8c\x74\x64\x1f\xbb\x86\x41\x3b\xa0\x07\x1b\xb4\xdb\x8d\x54\x9a\xa2\x81\x95\xbf\x71\xec\xc0\xd7\x8f\x8d\x91\x31\xce\x28\x23\x26\xb9\x1e\x4c\xb7\x2c\x9c\xdc\xc3\x68\x32\xa1\x60\x90\xcc\x51\x6e\x9d\x4b\xce\xd5\xbb\x10\x7c\x1c\x91\x28\x11\xb3\x24\x6d\x97\x13\xef\x21\xc4\x3c\xba\xb0\xed\xd0\xd7\x8f\x60\xc1\x39\x8c\xea\x45\x39\x2a\xcf\xfd\x6f\x66\x4d\xba\x2b\xbd\xd5\xd3\x04\xa9\x48\x75\x15\x8c\xa6\xf3\xd3\x28\x71\x3d\xdc\x14\xe9\x94\x32\xee\xce\x6a\xa0\xd3\x3e\x5f\x54\xc1\x62\x41\x13\x58\x4b\x41\xc2\xdf\x40\x58\xd8\x15\xb5\xd5\xdd\xc3\x08\xc6\x34\x8b\xc6\x8c\x3d\xc9\x5e\xd5\x17\x16\x17\xa8\xe9\x44\xc0\xc2\x3e\x54\x89\x5a\x39\xbc\x3a\xbd\x5f\x15\x5a\x95\xde\x82\x5f\x99\xec\x90\x7a\xec\x8e\x83\x38\x16\xf8\x95\xd7\x38\x7c\x44\xb3\x40\x2f\xdd\x3c\xfa\x55\x38\x17\x84\xeb\xba\x59\x90\xf7\xd8\xff\x92\xd0\xc0\xfd\xaf\xe7\xde\x0e\xe3\x5b\xd9\x82\xfa\x75\xa6\x95\xa8\xf1\x7b\x67\xf2\x2d\x5c\xb1\x2a\xd6\x77\x77\x41\xba\x98\x44\x89\xf5\x56\xa9\x0e\x09\xda\x6b\x91\xa8\x4a\xdc\x30\xdb\x4a\x03\x9e\xbb\x97\x3f\x2f\x3f\xfa\x73\x8d\xaf\xab\xa1\x69\xb0\xcc\x8c\xda\xab\x06\xbd\x0e\xa3\xd6\x2e\x00\xba\xe4\x19\x69\xb7\xc9\xa8\x99\x41\x16\x42\x99\xd7\x2c\x6b\x05\xbc\x31\xde\xcf\x95\x13\x4a\x66\xf4\x3d\xf7\xd2\xfa\x0b\x3f\xce\xe4\xde\x23\x6f\x85\x03\xcc\xf0\x83\x39\x26\x32\x20\xf1\x4a\x2c\xea\xc6\xbc\x28\x04\xbf\x4a\x36\xfe\x7c\xfe\x99\xd4\xf2\xda\x21\xfc\xca\x8f\x94\xd0\x9d\x98\xb0\xce\xea\xa8\x33\xb6\xb5\x12\xdc\xa1\x4d\xc9\x8f\x3c\x99\x10\xc8\x4b\xf8\x06\x58\xa4\xf3\x45\x71\x89\x55\x82\x0d\x36\xd1\xda\x55\x68\xd2\x23\x62\x4f\x23\x90\x3e\x56\xc0\x8d\xf4\x38\x55\xea\x6b\xca\x8b\x89\xca\x81\x88\x2a\xeb\xc6\x60\x5c\xac\x6c\x78\xc4\x82\x9b\x8c\x43\x3f\xc6\x2b\xf7\x0f\xf5\x53\x94\x17\xce\xcb\xbf\x63\x63\x34\x27\x1e\xa7\x50\x95\xa3\xd7\x35\xbb\xdb\x8b\x7a\x17\x24\x6f\xea\x97\x8b\x90\x5b\xb6\x8a\x77\x70\x4a\x15\x59\xa4\x05\x7a\xeb\xca\x0b\x4b\xe1\x88\xfb\x1d\x22\xc6\xdb\x3e\xf5\x84\x50\x80\x9a\xcf\x8a\x8c\xbd\x4d\xad\x47\xbe\x7d\x95\x2c\x48\xfb\xf6\xcb\x76\x16\x62\x36\x4f\x76\x71\x8f\x35\x2c\x1e\xc6\xc6\xae\xab\xe8\x17\xaf\xb5\xdc\x17\x5a\x1c\x52\x8b\x40\x9d\x14\xbf\xba\x55\xaf\xe6\x06\x03\x39\xdd\xf4\x8c\x66\x97\xc5\x0c\x7c\x91\xa0\x7a\x30\x76\x5c\xc7\x53\xd2\x22\xcd\xc1\x8f\xf1\x52\xd7\x7f\x43\xa1\x7c\x2f\xdd\x69\x13\xae\xd2\xf9\xba\x47\xda\x6d\xa9\x7c\xaf\x50\x52\xbc\xe3\xb3\x64\xe9\xf4\x94\xfa\xee\xfa\xa4\xb7\xd9\x28\xd6\xde\x57\xd4\xc9\xc1\x6d\x74\xb5\x52\x2e\x63\x20\x25\x5a\x39\x69\x66\xc6\xfe\xe7\xaa\x32\xf8\xf5\x58\xff\x3c\x41\xc9\xdb\xf8\xc3\xd2\xcd\xb1\x34\xae\x9c\x63\xbf\xa4\x76\x8e\xfd\x7e\x8a\xaa\x43\xfa\x39\xa7\xc6\x06\x1a\x3a\xe7\xee\x7d\x15\x15\x1d\x2b\xbc\x8a\x8e\x8e\xc3\xdb\x4a\x3a\x96\xba\xa2\x96\xce\x2c\x52\xa1\xa6\xe3\x2d\x56\x95\xbd\x89\xa2\x8e\xe1\xb6\x44\x51\xd7\xcc\x51\xbe\xe8\x56\x03\x45\x5d\xa3\x68\x5e\x5f\xeb\x71\x9d\xe7\xf6\x6f\x15\xf2\xe0\xc5\x57\x21\x10\x59\xc2\x26\x11\x9e\xbe\x22\x91\xd8\x85\x2a\xc8\x44\xb6\x5b\x5d\xfe\x46\x3a\x5d\x2e\x49\x35\x79\x33\xe7\x69\xef\x6e\x5f\xcb\xa9\x51\x36\xa0\xbb\xbb\x8f\x3e\x52\xf9\x7e\xc7\xc3\x87\x91\x8b\xdb\x28\x6f\xee\xdb\x76\x4c\xb3\x22\x88\x12\xbf\x7f\x5b\x07\x91\xfc\x36\xa9\x86\xa8\x39\x50\xdf\x4c\xaf\x26\x6b\x51\xc4\xca\xa8\xf5\x06\x51\xd0\x6c\xce\x8e\xfc\xd1\x04\x6a\x36\xfb\x1d\x0a\xaf\xb5\x64\x1a\x9d\xd1\x44\x9a\xb4\x98\x47\xea\x32\x77\xb9\x96\xfd\x0b\x3f\x66\x6b\x8b\x5b\xc0\x32\xaf\xdc\x69\xd7\x6f\x7f\x8b\x21\x9a\x2f\x11\xee\x9c\xb6\x55\x78\x85\xe3\xf4\x8c\x66\xd9\x79\x16\x15\x05\x05\x73\x2f\xde\xab\x16\xd9\x80\xde\x37\xc6\xdd\x39\x68\xd9\x73\xfc\x90\x1f\xac\x20\xf4\x51\x34\x4a\x04\x0a\x0b\xd7\xef\xb0\xfd\xd6\xbe\x11\x32\x5d\xad\xa4\xd5\x9c\xd6\xda\x96\xe0\xcd\xe3\x42\xc0\x8f\xc1\xc1\x00\x54\xe1\xc1\x9c\xad\x0a\xf0\x7a\x28\xb4\x59\x6c\xbc\x8c\x13\x50\x7e\xc7\x10\x47\x9f\x29\x09\x48\x1e\x25\xd3\x98\x2a\x3f\x5c\x00\xd9\x37\x4c\xa2\x81\x82\xb9\x9b\x19\xee\x96\x83\xb7\x76\x75\x45\x8e\xdb\xc7\x9b\x27\xed\x93\xae\x12\x06\x6b\xdc\x00\x88\xee\x99\x78\x67\x5f\xd8\xb5\x61\x89\xe8\xce\x6d\xa0\x38\x2a\xc0\x56\x61\xb3\x47\x1e\x81\x3d\xf6\x10\xfa\xb2\x89\x1d\xd1\xe8\x0e\x39\x82\xac\x74\xd4\xd0\x93\xae\x1d\xca\x4e\x0b\xd2\xa1\xc3\x43\x09\xa8\x1b\x18\x0c\x48\x10\xc7\xe4\x34\xc8\xa3\x31\xf7\x7f\x00\x8f\x05\xb6\xb7\x84\x02\x27\x4e\xd9\xc9\x58\xf6\xa6\x47\xb6\xb7\xea\x8c\x4e\xcc\x85\x2d\x38\x9a\x3c\x81\x4b\x5d\x24\xa1\x53\x10\x20\x21\x28\xd4\xf1\x49\x8b\xec\xfe\x00\xeb\x53\xa7\x3d\xe6\x89\x95\xca\xb4\x3d\x59\xdb\xaa\x1c\x60\x46\x4b\x7b\x56\xb1\xda\x71\xab\xa5\x34\xab\xdd\x7e\x19\x0e\x61\x1c\xa2\xdb\xb1\xb6\x51\x54\xe4\xc1\x03\x82\xbf\x8f\xd1\x6f\xe4\x02\xee\x44\xee\xba\x2a\x32\xc6\x60\x7a\xa3\xb9\x11\xcb\xb7\x6a\x6a\xe4\x2c\x98\x73\x23\x26\xcc\x9c\x1a\xe4\x71\xed\x96\x33\x63\xf5\xab\x62\x62\x50\x9b\x5f\x7b\x5e\xee\x72\x62\x4c\xd7\x27\x9a\x91\xa2\x99\x80\xb3\x51\x0b\x6c\x11\xb6\x38\xd2\xf9\x21\xa9\x25\x8c\x15\x36\xc5\x54\x6c\x3e\x56\x80\x5b\x27\xc7\xdb\x02\x54\xa6\x71\x10\x05\xb1\x79\x62\x25\xe8\x6f\x77\x77\x00\xac\xde\x60\x7b\xc0\x63\x11\x43\xac\xdf\x13\x50\x63\x77\x34\x91\xd1\x84\x74\x50\x16\xe2\x90\x36\x3f\xbe\xe1\xc4\x02\xc3\xf6\xbd\x86\xd8\xac\x98\x72\xb1\x49\xc8\x53\xb5\x6f\x9e\x61\xde\x7c\x53\xdd\x52\xf1\xf7\x9c\x09\x17\x9f\x2d\x63\xde\x8d\x8a\x8e\xcd\xca\xf1\x74\x6b\xef\x6b\x8d\xe6\x59\x65\xf0\xa1\x88\xfc\xd2\xf9\x35\xbc\x28\x96\xee\xf6\xc2\x5b\x51\x1c\xe4\x05\x39\x3e\x61\xc2\x04\xaf\xf7\x46\xd3\xbe\xee\x9f\x77\x35\x07\x20\x67\x11\xc7\xc7\x12\x1c\x68\xf4\x4b\x28\xf8\x54\x34\xd0\x84\x48\x2a\x8c\x63\xd1\x11\x46\x71\x60\xfb\xa6\x89\x9c\x5e\x92\x90\x4e\x82\x65\x0c\x8a\xd0\x7c\xc9\xe4\x54\xb5\x31\xb7\x84\x9b\x9a\x9e\x08\xf3\x68\xcf\xa2\x71\x8c\xba\x01\x03\xd6\x3b\xe2\x8a\xa2\x70\xc3\xd3\x5b\xa9\x51\xbd\xf4\xd5\x2e\x75\xc4\x68\x89\xe4\xf6\x1a\x01\x8a\x17\xa4\x7c\xdc\x62\x14\xdf\x23\x2d\xb6\x08\xd8\x7f\x27\xad\x13\x4d\xed\x02\x02\xa5\x41\xa1\x64\x19\xdb\xcf\x1e\xd0\x6c\x36\x42\x9b\xed\x60\xce\xea\x6f\xcd\x42\x70\x9d\x54\x39\x2b\x81\xef\x0d\xc2\x59\x1e\x9f\xf5\x1c\x6e\x78\xd9\x70\x8c\xf1\xb2\x7f\x61\xd5\x5b\x44\x2c\xb8\x55\xe7\xdf\xc7\xfc\x34\xfe\xef\x93\x6e\xbd\x88\x20\x94\xb7\xca\xdb\x43\xf9\xbd\x83\x15\xc6\x42\x42\x37\x67\x1d\xf2\xed\xa9\x7b\x97\x65\xe1\xcc\x73\x69\x21\xee\xd1\xed\x8d\xc1\xeb\x8f\xda\xbc\x95\x11\xae\x50\xa5\x13\x54\x9b\x2d\xd4\x78\x83\x55\xf6\xdf\xd8\x98\x78\x87\x94\xfe\xf9\x1d\xa3\xba\x7e\x65\x69\x3c\xc1\xfe\x64\x05\x2b\x73\x0a\xa9\x97\xc9\xc7\x27\x3e\x27\xe2\xfd\xc5\x32\x9f\x75\x1c\xcf\xa4\xf2\xa5\xb6\x74\x33\xea\xd6\xcc\xc6\xe2\xfa\x5c\x3f\xf3\x39\x00\xc5\x2d\x21\x3f\x9e\x9d\xb3\x1e\xc1\xfe\x65\x2d\xf7\xa4\xb7\x72\xea\x2b\x26\x10\x3b\xf3\xbd\xf5\xfc\x41\xd7\x1d\xa9\x43\x20\xfe\xb7\x9f\x3f\x9f\x47\xd6\x1a\x4f\xac\xa5\x13\xc1\x66\x13\x5c\xa5\x56\xcc\xc7\xca\xb3\xb1\xe6\xdc\x11\x5a\xba\x23\x63\x49\x22\x8f\xb6\x4d\x7c\x82\xf2\xfb\xd1\x49\x96\xce\xbd\xe6\x06\x1c\xca\xc7\x5b\x4e\xed\x07\x3b\x96\x81\x90\x61\x19\xb4\xc2\x83\x29\xc9\xd4\x78\xcb\x0d\x58\x94\x18\x08\x66\x51\x86\x3f\xcd\x1a\x56\xf5\x55\x78\x15\xec\x4d\xf8\xc6\x92\x0b\xba\xe2\x89\x0f\x74\x4f\x0a\x3a\x02\x5d\x0f\xc9\x16\x18\x3f\x74\xa5\x47\x67\x81\xbc\xb2\x45\x54\x59\x27\x6e\xde\xa9\xd8\xb7\xa2\xa0\xc0\x87\x82\xdf\xb1\xe3\xd2\x1b\x64\x9b\x3b\xbd\xe7\xbb\x6d\xce\x40\x72\x12\x4c\x0a\x9a\xa9\x45\x82\xfb\x7b\xa3\xb5\xea\x2f\xe3\xf3\xdd\xad\x39\x47\x89\xcf\x6e\x52\x89\x3d\x11\x3a\xe6\x6d\x59\xfd\xd8\xaf\x47\xa9\x1b\x69\x3b\xe6\x4d\x25\xa3\x69\xc8\x69\xc8\xc3\xea\xbe\x31\xd8\x8d\xdd\x6a\x98\x46\x8c\xca\x74\x38\x8b\xa6\x7d\x83\x44\x77\xcb\xb5\xfe\x10\x7b\x08\xfe\x6b\x48\xfd\xd2\x20\xb5\xe1\xdf\x1f\x8a\xf8\xef\x69\x1f\xfd\xfd\x2e\xb4\x4f\xbc\xa4\x8f\x03\x34\xde\x94\xf4\xed\x30\x62\x2b\x6e\x2a\x0e\xb1\xda\xf5\x37\xdb\x59\xcc\x5e\xac\x52\xbf\x98\x3f\x2f\xbd\xc5\x0e\x7d\xf9\xd7\x5f\xf9\x12\x5e\x88\x5b\x3f\xd7\x48\xb5\xae\xfb\x1d\xb2\x49\x36\xcc\xde\x75\xb9\x4f\x26\x1e\x49\xcc\x33\xf5\xdc\x03\xb1\x75\xe9\x66\x3c\xd8\xae\xf0\x67\x6f\xe0\xda\xb2\xf8\x32\xb8\xd8\xda\x8a\x63\xc3\x73\xae\x56\xd6\x56\xd7\x54\xab\x7a\x2f\x12\xad\xae\xd7\x5e\xf0\x96\x5f\xed\xaa\x37\x71\xd7\x27\xbd\xcd\xdf\x3b\xf4\xfe\x51\xfd\xb3\xb7\x65\xc5\xbb\x37\xe1\x89\x04\xfe\xe7\xb6\x2e\x4b\xfd\xf4\x6d\x89\xde\xbe\x2d\xf1\x83\xb5\xa5\xe7\xf5\xdb\x52\x3d\x7f\x5b\xa2\xf7\x6f\x4b\xf4\x00\x6e\x69\xbe\x80\x73\x6a\x6c\x60\x61\xe3\xf8\x47\xf9\x8a\x8f\xe0\x8e\xbc\xaf\xe0\x8e\x56\x7f\x06\x77\xd4\xf4\x1d\xdc\x91\xfb\x10\xee\xe8\x0e\x5e\xc2\x2d\x6f\xfd\x14\xee\xa8\xf1\x5b\xb8\xdf\x3b\xae\xff\x51\x03\x8b\xb3\x65\x95\xc9\x99\x74\xad\xc2\x7f\x08\xe2\x44\x56\x67\x4b\x6c\x76\xb6\x34\xac\xc4\x96\x3e\xc3\xb3\xa5\xb6\x3c\x5b\x62\xd3\xb3\x25\xb6\x3d\x5b\x5a\xc6\x67\x9e\x7a\x9b\x2c\x8e\xdf\xd4\xfe\xec\xc8\x6f\x80\x76\x74\x03\x0b\xb4\xa3\xc6\x26\x68\x47\x1e\x1b\x34\xbb\xf4\xcd\xd6\x48\x85\x19\x5a\xd3\x45\xd2\xdc\x10\xed\xdb\x26\xab\xa4\xbd\xcc\x29\x28\x66\xc7\x45\x9b\x07\xe4\x9b\xa6\x84\x26\x67\x24\x4c\x29\x58\x2b\xc0\xeb\xc0\x20\x09\xc1\x87\x2d\xf9\xe7\x9b\x9f\x5e\x17\xc5\xe2\x3d\xfd\x7f\x4b\x9a\x17\x6b\x20\x98\x5d\x2e\x68\x3a\xb1\x72\xb8\x1f\x1b\xf5\x7e\xa3\x2d\xf1\x22\x1a\xee\xdb\xd0\xe4\xcb\xf5\xce\x9a\x11\x2c\xb2\x14\xd2\x4c\x00\x49\xfd\x97\x7c\xc6\x76\x9f\x68\x9a\xa4\x19\x1d\xc5\x51\x42\xd7\xae\xb9\xc5\x2a\xc3\x43\x23\x6f\xf7\xf7\x2f\x67\xef\x5f\xce\xfe\x89\x5f\xce\xf2\x57\xb3\xc2\x86\xcd\x78\x36\xcb\x37\x1c\x72\xb3\xd7\xb3\x62\xef\x3b\x2a\xa2\x18\xea\xe4\xfa\x4c\x58\x3b\xfc\x79\x92\x03\x16\x15\x97\x8a\x25\xea\x22\xe3\x38\xc8\x73\x72\x0c\x45\x4e\x44\x37\x79\x86\x66\xc2\xbc\xaa\xb5\x01\xdc\x1b\xc1\x2a\x15\xca\x55\xc6\x41\x48\x85\x33\xeb\xe6\x7e\xce\x01\x92\xd5\x74\xf4\xf6\xe0\xe3\x07\x76\xb6\x86\x49\x68\x9f\xd3\xa8\xcd\x49\xb3\xfd\x19\xfd\x7e\x83\x7e\xff\x88\x7e\xe7\xbf\x06\xa7\xa9\xfc\x98\x44\x49\x42\x2f\xd5\x17\x9d\x17\x29\x3c\x65\x94\x29\x8b\x68\x6c\x26\x24\x41\x62\x26\xcc\xa3\x71\x66\xa7\xc4\x71\xe4\x14\x32\xe0\x0d\x50\xf9\x61\x14\x99\x66\x41\x12\xaa\xa1\x18\x59\x3f\x1a\x5f\x1f\x8d\xaf\x77\xc6\xd7\x4b\xe3\xeb\xff\x8c\xaf\x7f\x19\x5f\x6f\x8d\xaf\x17\xc6\xd7\x3f\x8c\xaf\x23\xfe\xb5\x76\x52\xee\xba\x86\xcd\xd1\xbb\xbd\x17\x6c\x8a\x47\x64\x7b\xab\xa7\x12\x3f\x1c\xfc\xf8\x76\xef\xe3\xd1\xfb\x97\x9f\x7e\x7a\xf9\xf6\xc7\x8f\xaf\x47\xe4\xb1\xce\x84\x59\x1d\xe9\x9f\x3a\xa7\x84\x72\x46\xe4\x0b\xb1\x12\xb4\x1f\x75\xc8\xf8\xf4\xe2\xf0\xe7\xb7\xe4\x5a\xd7\xf4\xee\xf0\xa7\x9f\x18\xf4\xc7\x83\x37\x2f\x0f\x8f\x3e\x8e\xc8\xe6\x70\x38\x1c\x88\x1e\x8a\x1b\xef\xe7\x71\x3a\xfe\x3c\x22\x6d\xc6\x3a\xf3\xa2\x6d\xe4\xed\x8d\x21\x94\xf1\x48\xbf\x6d\xe4\x0f\x30\xd8\x7e\x5e\xe7\xfb\xe4\x3e\x14\xc6\xfd\x46\xf6\x57\xdf\xc8\xd6\x94\x0b\x88\x7c\x16\x6c\xdf\x95\x07\x88\xfd\xec\x72\x51\xa4\x7f\xff\x80\x37\x87\x31\xa4\x3d\xd2\x11\x30\x58\x83\x5e\x80\x01\xcb\x69\x7b\xa3\x3b\xb9\xee\x1b\x80\xe2\x72\xfc\x40\x55\x24\x91\x07\x0f\x64\x6e\x5f\xfa\x8b\xe0\x62\xf2\x8c\x5e\xb4\xed\x57\x74\x86\xe7\xaf\x1f\xc8\x16\x2b\x6d\x7b\x3f\xde\x92\xee\x22\xcd\xe2\x44\x5e\x86\xab\x0b\x7e\xcb\x3f\x3b\xb1\x5e\xdb\x71\x50\x89\x23\xd6\xb9\xfe\x6b\x7a\xd1\x07\xed\xa5\xf0\xdc\xeb\xb3\x31\x62\x58\x91\xc3\xd6\xad\xf3\x13\x1d\x57\xbf\x8d\xc8\xd6\x37\x4f\x78\x49\xf4\x38\x59\xbe\x39\x63\x2c\x4f\xe1\xb8\x35\xfa\xe6\xbb\x5e\xcb\x44\x79\x6b\xf4\x74\x78\x7d\xd2\xdb\x6a\xe4\xf3\xe9\x9e\xef\xdd\xf3\xbd\x3f\x2f\xdf\xd3\x6c\x8f\xbf\xf3\xbf\x03\xbe\x67\xc9\xee\xab\x8b\xee\x1e\xc9\x5d\x16\xf4\x09\xee\x2b\x45\x1b\xb2\x79\x6d\x7f\x20\xd8\xbd\x0e\x47\x34\x79\x8a\x01\xd8\xb7\x12\xe1\x97\x49\x54\xbc\x09\x16\x4a\x5c\x6c\x4b\x89\x7a\xc4\x79\x50\x7b\x28\x65\x4d\x26\xb5\x8f\x34\x5b\x6c\x6f\x1a\x72\xfe\x08\x65\x0c\x87\xaa\xd0\xff\x56\xe4\x9d\x06\xa7\xa7\xc1\x94\xaa\x96\x70\x1e\x12\xfe\x47\x76\xde\xdc\x53\x27\xca\x7e\x53\x9d\x1d\xa7\x67\x34\x0e\xc6\xb2\x59\x3b\x5b\x9f\x31\x46\xbe\xec\xa9\xbf\x72\x04\xf1\x63\x2d\x44\x3e\x0b\x92\x24\x4d\x8c\x71\x9b\x10\xfa\x5c\x33\xaa\x80\xa8\x69\x05\x4e\x56\x23\x0f\x04\x46\xa5\x3e\x2f\x8d\xaa\x81\xea\x6a\x12\x67\xb7\x91\x17\xc8\xa8\x4c\x9d\xc7\xec\xb1\x79\x00\xfd\x43\x34\x01\x0d\x72\xf5\xc0\x21\xd0\xcf\x26\xac\x0f\x14\xcf\x35\x9c\xfa\x2a\x2b\xc6\xfd\x6d\x54\x37\xae\xbe\x69\x01\x54\xa6\x58\xa1\x0c\x2b\xe6\x37\xb6\xd2\x8e\x18\x16\x41\x28\x4c\x49\xc1\xd4\xf3\x62\x41\xc7\x6c\xf3\x52\xe6\xf9\xd8\xe8\x4a\x78\x4f\xf1\x59\x4e\xe9\x2a\x4e\x29\x83\x0b\x45\x44\x2e\xcb\x06\x6b\x3c\x0b\xb2\x60\x5c\xd0\x2c\x97\x2a\x7e\xb8\x97\x17\xa5\xd1\x3e\xe2\x6d\x23\x9a\x26\x3d\x64\x0b\x4d\x86\x6b\x7e\xb7\x1f\xd1\x74\x56\x10\xe9\x91\xd6\xf2\xee\x2b\xc6\x60\x48\x9b\x1c\xa4\x07\xbd\xcb\x7b\xd0\x8e\xc7\xc7\x10\xb7\x10\x01\x18\x08\x4a\x0b\xaf\x55\xd5\x0d\xf1\x66\xb7\xff\x4b\x1a\x25\x10\xac\x81\x3c\x83\x3a\xc8\x88\xb4\x86\xad\x2e\xd9\x10\xc0\x25\x86\x6f\x37\x9e\x0b\x08\xd8\xf3\x67\x9f\x0c\x18\xc4\x8a\xb3\x21\x7a\xb8\xc1\x3d\x2e\xdf\x74\x5e\xca\x0c\x11\x4d\x47\x34\xb0\x75\x82\x19\x22\x04\xf3\x70\x7d\x4c\x5b\xf3\xc2\xbd\x35\x57\xcc\x4a\x94\xb0\x4a\xfc\xc8\xc2\xfe\xa8\x3d\x8e\x92\x58\xe3\xda\xec\x90\x7b\x20\x39\xe2\x5b\xbb\x12\xe9\x67\x3c\xde\xf3\x60\x40\x5e\x45\x49\x48\xf8\xe3\x2e\xd1\x51\x15\xaf\x99\x49\x14\xad\x96\xbe\xc9\x07\xdb\x97\x1e\x84\x90\x9a\xd1\x0b\x69\xc2\xac\xce\x5c\x2c\x8d\x9f\x7a\xd8\x89\xa3\xfc\xac\xc4\xaa\xd9\xc2\xef\x5e\xc0\xb8\x46\xd8\xd4\xec\x90\x68\x63\x77\x0b\x83\xcb\x58\xc8\xd8\xb6\x43\x37\xd5\x89\x58\x3b\x22\xf4\x85\x6a\x61\x42\x3a\xbc\xc8\xee\x2e\x19\x76\x8d\x53\xda\x69\x46\x83\xcf\x1a\x94\x8d\x72\x63\x97\x88\x57\xe5\x6c\x06\xf7\x67\x41\xb6\x9f\x86\x14\x6a\xf0\x1e\xc2\xd8\x64\x4b\x73\x9c\xbc\xc8\x9a\x51\x08\x9f\xb4\x95\x48\x64\x8f\x15\xf9\xed\x68\x04\x9a\xfb\xef\x21\x92\x9b\xcc\x7c\x5e\x94\xbd\x4e\x37\x27\xdb\xe3\x63\xbe\xb3\xc8\xe8\x24\xba\xe0\x41\xb4\x86\x17\x5d\x36\x0b\xc0\x35\xfc\xee\xed\x45\xb4\xb7\xf2\xd9\xf7\xda\x2e\xc3\x11\x34\x88\x81\x9b\x57\x06\x13\xf0\x45\xf9\x34\x7c\xed\x0b\xb7\xeb\xa2\x1b\x98\x2a\x18\xc5\x0b\xcc\xf3\xd9\x87\xe5\x20\xcc\xb6\xf9\x72\x90\x33\xc2\x5a\xd2\xd4\x31\x49\x33\xdb\x84\x2e\x2f\xb2\xb2\x88\xf8\x68\x46\x19\xd4\x58\xcc\xcd\x5e\xd1\x89\x6e\xb6\xd2\xc1\x3a\x51\x04\x07\x37\xbc\xb6\x69\x10\xd6\xdf\x8d\x5d\x92\xc8\x7d\xe1\x7b\xb2\x45\x9e\xb1\x93\x0d\xd9\x20\x6c\x3f\x48\x7c\x34\x21\x5c\xc8\xcf\xe8\xc5\x5d\x92\x86\x15\x73\xc0\xa6\x8d\x1a\xd6\xf0\x9b\x11\x87\xc3\x33\x10\x75\xfc\x36\x14\xf0\xbb\x4d\xab\xe5\xb1\x74\xb2\x8c\x63\x85\x86\x01\x3d\xa3\x49\xc1\x1f\x0a\x00\xcb\xff\x25\x4f\x13\x12\x9c\x46\x36\x8f\x97\x6e\x13\x3f\xa6\xaf\x96\x71\x6c\xbf\xa1\x94\x8f\x09\x58\xe9\x47\xbc\xb4\xfb\x18\x8a\x37\xec\xb4\xab\x19\xbb\xdb\x86\x21\x48\xb1\xca\xb1\xea\x94\x7d\xf7\xc1\x84\x22\x4a\x42\x7a\x71\x38\xe9\xb4\x3b\xed\x2e\xf8\x86\x7c\xb4\xe9\x79\x0e\xa9\xe0\x1d\x3b\xc1\xe2\x72\x41\x45\x73\x00\x04\x54\x64\xfa\x33\xeb\x44\xdd\x2f\x32\x84\x70\x9f\xc1\xef\x90\x6b\x21\x8a\x99\x96\x7f\xaa\x15\xb2\x41\xda\x1d\x36\x73\xaa\xf6\x0d\xd2\xee\xb6\x1b\xad\xbd\x30\xca\x17\x71\x70\xc9\xe7\x05\x7c\x8c\x26\x05\x93\x6d\x15\x36\xec\x37\x6b\x17\x90\xfd\x82\x17\xab\x7a\xe1\xca\x6a\x33\x27\xdf\xbf\xbc\x8c\x1e\xb0\x2d\xcd\xa2\x18\x3a\xed\xcb\x78\x8b\x97\x1d\x61\x56\xd7\x25\x8f\x7e\x50\x89\x6a\x5a\xdd\xbe\x55\x3e\x7c\x56\x36\x9b\xce\xcc\x1a\x68\x16\x60\x7c\xb2\xc9\x33\xfb\x4d\xab\x78\x0f\xc6\xd6\x8c\x76\x36\x32\x18\xe8\x81\xa6\x67\x34\x8b\xd3\x20\xa4\xa1\x52\x04\x7b\xd6\x04\x1e\xc0\x47\x4d\x24\x65\x6f\x1a\x07\xe4\xe3\xe1\x8b\xc3\x11\x99\x07\x9f\x41\x35\x1c\x25\x67\xcb\x38\xa1\x59\x70\x1a\xd3\xbb\x1c\xa0\x3e\x0d\xd8\xaf\x77\x37\xc9\x23\x82\xb2\xbb\xdd\x7e\x46\x17\x71\x30\xa6\x9d\x36\x69\x83\x53\x37\x76\x5a\x68\x99\x41\x22\xd3\xe4\x8c\x66\x45\xae\x43\x6e\x82\xdc\x17\xd2\x71\x34\x0f\x62\x9b\xc9\x46\x89\x9f\xd9\x17\xe9\x0b\x5e\xc0\xa5\xbc\xca\xf0\x99\xa6\x5b\x43\x2e\xe0\x89\x9a\x6a\x03\x40\x16\xa9\x1b\x1f\x53\x85\x9f\x69\x32\xc6\x5a\xd9\x96\xf1\xc4\xbb\x1a\x17\xaa\xab\x3a\x38\x6b\x22\xb5\xa4\xee\xf8\x3c\xa1\xb9\x85\xfa\xd4\xdc\x51\x8c\xc3\x3e\x07\x88\x69\x9e\x7f\x9c\x05\x49\x67\x08\x4e\x64\x1f\x71\xab\x73\x61\xbd\x2f\x08\x6b\xb3\x0b\xe1\x5b\x51\x8e\x81\xc5\xbd\x25\xb8\x69\x16\xa8\x0c\x92\x4b\xe1\x78\x47\xb8\x23\x4d\xca\xd1\xda\x17\x78\xdd\x4b\x42\xae\xfe\xe7\x34\x14\x4d\x2e\x73\xe1\x48\x3d\x27\xa7\x74\x92\x66\xb4\xef\xd0\xd5\x6b\x71\x74\xa8\xc6\xfd\x95\xd8\x83\x6a\x48\xeb\x35\xec\xf3\x06\xf2\xd5\xfa\x7d\x28\x4c\xc5\xe6\xc1\x05\x0f\x5b\x79\x11\x15\x97\x23\xf2\x14\x54\xd8\x72\xd7\x89\x72\xe1\xd2\x18\x8a\x76\xed\x4d\x06\x4d\x72\x67\x83\x41\xec\x18\x45\xf1\x74\x56\x17\xb6\xca\x0a\x43\xba\x33\x46\x3b\xec\x14\xc2\x91\xd6\xf6\x56\x01\xf1\x95\xfe\xfe\xe1\xf0\x6d\x5f\x61\x99\xb7\xa7\x1d\x58\x82\xeb\xd8\x9c\x04\x76\x34\xcf\x1e\x59\x04\x79\xce\x78\x57\x31\xcb\xd2\xe5\x74\x66\xae\x00\x35\x10\x41\x6b\x50\xab\x7b\x39\xa9\xb9\xda\x23\x38\x2d\x79\x64\xde\xd2\x11\x4b\x00\xf1\xb6\xc3\xac\xae\xa6\xb6\x33\x69\x3f\x8a\x2a\x20\x9d\xf5\x28\x7f\x15\x25\x51\x41\x2d\xa4\x5b\xdd\x00\x09\x11\x75\xc2\x94\xb2\xdc\x8e\xa2\x75\xf1\x5e\x6c\x2a\x7c\x1d\xb0\xf3\x52\x02\xdc\x9f\xfc\x4c\x6d\x41\x6a\x4a\x0b\x88\x58\x7c\x38\x39\x4a\x22\xaf\xb6\x0b\xca\x16\x33\x2a\x7e\xa8\x05\x47\x8a\xb4\xa7\xb4\x53\xca\x21\xba\x37\x6a\xa3\xea\x87\xaa\xa6\xc3\x3b\xd3\x85\x22\xe0\xb6\x2b\x27\x34\xcb\xd2\x4c\xba\xa4\xe1\x3d\xce\x49\x92\x16\x64\x9c\x66\x19\x1d\x17\xa3\x73\xb5\x6e\xcc\x5e\x1b\x0b\x88\x15\x94\x24\xb0\xe4\x99\xf0\xdf\x33\xf8\xaf\x5f\xa4\x3f\xa5\xe7\x34\xdb\x0f\x72\xda\x01\xe6\xc2\xf5\xbd\x9a\x8f\x31\xa8\x7f\x88\x5b\x66\x71\x75\x73\xcc\xfe\x3f\xd1\x47\x71\x04\x82\xfd\x7e\x63\xc2\xe3\x9e\xc8\x12\x7a\x4e\x5e\xb2\x51\x75\xda\x70\xd5\x0b\x1d\x01\x5b\xd5\x7f\xb7\x0b\x42\x2f\xa2\xbc\xc8\x7b\x64\x11\xd3\x20\x07\xb1\x18\x46\x9e\x26\x0a\x55\x93\x34\x8e\xd3\xf3\x28\x99\x42\xc9\x9c\x71\x41\x6b\x19\x89\x1e\xf6\xc0\xbf\x42\x4f\x3f\xfb\xa8\x88\x12\xab\x7a\x0f\xde\xaf\x4c\xaf\xc2\xc1\x67\x0a\x8b\x90\x33\x7c\xb8\x8c\x8e\xc0\x9e\x56\x31\x59\x4e\x02\x8c\xd5\x82\xaf\x0a\x3e\xf1\x1c\xb5\x82\xb2\xde\xa5\x79\x1e\x9d\xc6\x7c\x0a\xc1\x85\x86\x30\xea\xfb\x70\xc0\xe4\xcb\xac\xe0\x3f\x99\x48\x2d\xb1\xf5\x72\x32\x89\xa6\x97\xe2\xe3\x50\x92\xd2\x23\xf2\x99\x35\xcf\xff\xf4\x75\x15\x7c\x8a\x9b\x2d\x0e\x36\xd7\x60\xea\x72\x89\x7f\xca\xab\x28\x0e\x37\xd5\x70\xea\xfe\x87\x7f\x8a\x0b\x23\x9d\xc7\x0b\x3c\x7a\xa4\x16\xa6\xbe\xc7\xe1\x05\x7e\x0d\x4e\x53\x23\xcf\x53\x42\xde\xc3\xf0\x01\xc0\xf5\x0d\xce\xe3\x25\x50\x2f\x50\x61\xfe\x29\xb0\x80\x40\x88\x05\x81\x3e\xe0\x32\x45\x20\x84\x6a\x1c\x4e\xd1\xef\x42\xfe\xb6\x45\x0a\xce\x17\xac\x93\xef\x95\x92\xd3\x39\x39\x8c\x83\x84\x9d\x0c\x02\xc5\x9a\x45\xba\xd0\x95\xa5\x19\x09\xc8\xeb\x97\xff\x84\x43\xb8\x94\xd6\xee\x8c\xa1\xa8\x7d\x56\x1e\xed\x7e\x9e\x51\xe9\x67\x2f\x40\x57\xb9\x22\x0a\x0a\x0a\x16\xc0\xd6\x53\x90\x93\x73\xca\x16\x88\x76\xb0\x22\x87\xb1\x86\xa4\xa1\x9f\xa9\x71\x24\x97\xe3\xc4\x2c\x85\x8b\x3a\xac\x66\xc9\x24\xb0\x50\xc4\x4b\xe0\xa8\xb1\x26\xa7\xe2\xdc\xc9\x92\x87\xf0\x36\x2c\x2a\x20\xcf\x8c\x46\x46\xf8\x0b\x49\x56\xb5\xcb\x37\xe0\x38\xf6\xac\xe0\x73\x1a\xdd\x2f\xd8\xff\x96\x25\x5e\xa4\x55\x0b\x1c\x9d\x17\x7e\xb3\xa5\xce\x56\xdb\xef\xb8\xd8\x01\x21\x77\xb3\xd4\x8b\x68\x4e\xf3\xdf\x63\x99\x27\x42\xb9\xc8\x16\xb7\x52\x55\xe5\xfc\x98\x0f\x5b\x34\x51\xb6\x2c\x0e\x39\xa8\x9e\x34\x22\x0a\x4d\x06\xf2\xee\x90\xcd\xbd\xa6\x05\xb3\x36\xe5\xe5\x4a\x57\xa0\x01\x14\xfe\xb1\xf1\x8d\x35\x0b\x35\xe7\x9f\x6f\x98\x10\x08\xcb\x5e\x96\x17\x3f\xae\xae\xc8\x70\xc7\x7b\xb8\x11\xf5\x3a\x87\x13\x9e\x6e\x9c\x88\x04\xce\x65\x4f\x1e\x3c\x20\xe2\xb7\x4f\xe8\x67\x4d\xda\xb9\xf8\x84\xe1\xf3\x81\x66\xc8\x62\xa2\xb0\xd2\x89\x0c\x2f\xda\xbd\x76\x1b\x5f\xb8\x58\x9e\xd2\x7c\xa5\x31\xa1\x94\xca\x74\x89\x8c\x1d\xeb\x21\x15\x45\x27\x1c\x4c\x46\xf1\x50\x47\x31\x61\x36\x09\xb0\xc5\x79\xda\xce\xc9\x58\xc5\x74\x71\x48\xcb\x0c\xf9\xd2\x84\xbe\x4a\xa8\x06\x1d\x92\xcd\x3a\x4d\x85\x97\x41\x32\x0c\xfc\x14\x51\x96\x6f\xc1\xc2\x93\xef\x0e\xf2\x5a\xa7\x0a\x60\x95\x44\xed\xd4\xb5\x26\xb7\xfc\x6b\xc1\x2c\xf7\x17\xf1\x32\xd7\x5d\x10\xdf\x5e\xf7\x86\x0a\xc8\xd4\x24\xcd\xe8\xf8\x73\x2e\x8f\x4d\x9c\x47\xca\x6b\xce\x5c\x3c\x96\x8b\x2f\xc1\x8f\xaf\x37\x1a\x31\x27\xf9\xb1\x37\x12\xb1\x19\x53\x18\x35\xc0\xd6\x7f\xa0\xe1\xb1\x63\x3b\x08\xae\x24\x66\xce\xaa\xdb\x98\x38\x51\xa9\xa5\x41\x1b\xfc\x67\x78\x71\x3c\x7c\xf4\x5d\xf0\x68\x72\xf2\xe5\xf1\xf0\xfa\x7f\x06\x51\xbf\xa0\x79\xa1\xc0\x57\x18\x7b\xc5\x90\xbf\xce\x60\x1b\x0c\x13\xce\xff\x83\xff\x74\x86\x17\xdd\x67\x95\xe3\xc4\xf4\x37\x18\xe8\x58\x59\x3c\x1a\x16\xf4\x8e\x7b\x10\x16\x46\x87\x73\x78\xc7\xcb\xf6\x63\x34\x6a\x93\x7e\x85\x23\x40\x62\xba\xaa\xf0\x76\xc6\xec\x0b\x63\x73\x08\x6c\xef\xd1\x2b\x2f\x98\xd5\x65\x08\xdd\xd5\xce\xc1\xd9\x71\x3e\x67\xff\x8e\x83\x45\x0e\xb2\x43\x1c\x13\xf9\xdd\xc3\x1e\x1a\xed\x1e\x73\xc7\xf3\xa8\xc3\x46\x03\x87\x6a\x7b\xe7\xd8\xa1\xc1\x78\x46\xc6\x41\xee\x54\x13\xe5\x9c\x50\x96\x73\x31\x43\x88\x9a\xf8\x2a\x6b\x4e\x53\xbc\xad\x7c\x39\x9f\xd3\xb0\x94\xbc\xac\xe6\xee\x98\xcc\xac\xda\xab\xc8\x6d\x30\xe0\xe3\xb1\x70\x13\xa8\x92\xe2\x97\xb3\x03\x69\x7d\x88\x80\x78\x1d\xe4\xe0\x8c\x66\x16\x6c\xcb\x46\x4c\x5d\x8a\x94\x76\x7c\x0e\x5f\x1e\x0f\xe1\x8e\x92\x58\x14\x02\xce\xbb\x8b\x19\x89\x29\x3c\xa7\x46\x11\xf8\x16\x0b\x9a\xb1\xde\xca\x69\x48\x20\x7a\xe1\x34\xe2\x01\xee\x82\x9c\xce\x83\x05\x9b\x8e\x4d\x43\xd3\xd7\x51\x16\x0c\xa8\xd3\xe0\x96\x6d\xf3\x49\x97\xfc\x40\xbe\x65\xdb\xb9\xc8\x3a\x8e\x4e\xfa\x45\x7a\xc4\x1a\x12\xba\xa0\xf5\xdd\x5d\x94\x09\x44\x5f\x5d\xe1\xf7\xbb\x9e\x1a\xb1\x76\xc9\xaa\xb1\xc4\x57\x38\x5a\x96\x9a\xe5\x1b\x8c\x5f\xc7\x5f\x50\x54\xfa\x46\x1c\xf5\x24\x35\x96\x90\x62\x91\xde\x25\x29\x4a\xed\xb5\xda\x97\x57\xa0\x44\xa4\x33\x56\xd4\x67\xbf\xba\x16\xed\xb4\xdb\x82\x94\x5c\x32\x35\xf0\x7b\x23\xa2\x45\x40\x63\xa7\xf7\xac\xa2\x0a\x32\x96\xbd\x40\xd7\xee\x36\x49\x03\xd3\x9b\x69\xd3\x3f\x46\xa4\xdf\xb1\x83\xcf\x84\x3b\xd0\x97\x37\x71\x8a\xc2\x0d\x02\xae\xa3\x5f\x93\x82\xec\xfe\x6f\xec\x96\x12\x37\x22\x2f\x9b\x91\xd6\xd6\x54\x49\x9a\x56\x49\x53\xf2\xd4\x92\xa6\xc1\x46\x8b\x94\x49\x94\x51\x48\xb6\x86\xdc\x67\xd0\x23\x71\x41\xc8\xdb\xe4\xef\x13\x86\x17\x84\x1b\x77\xb8\xc6\x5d\xb5\x94\xec\xbf\xed\x17\xde\x07\x30\xd7\x56\x06\x5c\xcd\xe8\xd7\x12\x67\xbc\x1b\x9f\x74\xaa\x2b\xf1\x81\x64\x78\xbe\xdb\x56\x6d\xb4\x9e\x8a\xc4\xe5\x97\xaf\x3e\x13\x42\x86\x5e\x84\x2b\x25\x55\xa3\x7e\x4d\xd5\x23\x8f\x87\xfe\x5b\x02\xe9\x88\x58\x9e\xa6\x73\x2d\xe5\xd6\x07\xd9\xf4\x9e\x24\x7d\x57\x5f\x46\xe0\x4d\xbe\x91\xf9\xce\x80\xa4\xc3\xbb\x61\xc9\x85\xb2\x6f\x49\x5e\x04\xc9\x98\x71\x11\x5d\xf8\xea\x4a\x21\x4d\x14\x86\xd7\x6b\xf0\xcb\x70\x9c\xe1\x4d\xe5\xb6\x11\xc0\x8b\x54\x95\xed\xa6\x88\x92\xe7\xe1\x3a\x2c\x7d\x70\x8c\x8b\x1a\xa2\xc8\x13\x22\xc9\x8b\x1f\xc1\x5a\x45\xcf\x60\x34\xbc\x6f\xed\xbb\x43\x0f\xef\x4b\x63\xdc\xc8\x1e\xd7\x63\xe7\x95\x36\x22\x59\x15\x3f\xb2\xe8\x8d\x30\x24\x4b\xb4\x1b\x8e\x88\xf5\xa9\xa8\x1f\x0e\xef\xfa\x0d\x06\x73\x28\xfa\xd6\x70\x31\x30\xf1\x22\x59\xc6\x31\x44\x49\xe8\xb8\x2b\x04\x0c\xb7\x41\x85\xe1\x19\xbb\xb8\xaf\x6d\x38\xf2\x53\xde\xd9\x06\xec\x80\x03\xde\x84\x19\xf0\xa4\x1b\x4d\xa4\xe8\x5e\xd3\xd1\x80\x0b\xc0\xfa\xb1\x38\x11\x35\x1a\x8e\xc4\x8d\x8a\xd1\x90\xa5\x41\xc1\xca\x31\xd8\xc7\x11\xbe\x8f\x82\x8d\x5c\x2a\xa9\xce\x1c\xc4\xdf\x73\x73\x5d\x69\x0b\x84\xca\x31\xb0\x62\xf6\xab\x01\xe5\x3a\x29\xbb\x74\xf7\xa9\xf5\x75\xb8\x99\xe4\xcf\x70\xb5\x31\xeb\x35\x19\x43\xd8\xa7\x0e\xf5\xec\x6d\xf8\x40\xba\xca\xa8\x03\x31\xee\x97\x6c\x02\xe9\x72\x4e\x4e\xe3\x74\xfc\x99\xcc\x68\x10\xd2\x8c\x7d\xa4\x73\xdb\x6a\x23\xca\x9f\xb3\x64\x9f\xd0\x30\xa3\x17\xca\x2f\x3a\x94\x25\x93\x28\x2e\x6c\x65\xa6\x87\x60\x01\xd6\x70\x3f\xcc\x52\x2a\x4f\xfa\xdf\x6c\x6e\xe9\xa3\x3e\x07\xaf\xc1\x4b\xf9\x41\x9d\xd7\x85\xab\xf2\x9d\xd3\x5d\x28\x5f\xc4\x61\x7d\xce\x5e\x73\xfb\x71\x83\x99\x89\x53\x26\xe6\x2d\xa2\xb1\x3b\x0f\x1f\x59\x72\xdd\x3c\x14\x0a\xa8\x62\x02\xa0\x26\x63\x02\xa0\x58\xe5\x04\x3c\x79\xac\xf1\xcf\xa1\x6f\x8c\x7f\xa8\x0a\xd7\xe4\x43\xbf\x03\x74\x23\xec\x97\x38\x1e\x11\x22\xdf\x48\xfe\xe8\xc9\x54\x78\xf4\x33\x52\xbf\x78\x3a\x08\x86\x23\xfe\x9f\x4c\x11\x16\x24\x23\xfd\x93\xe7\x20\xeb\x92\x11\xfe\x90\xe5\x8e\x8a\xc9\xd3\x91\xf8\x5f\xa6\x81\xbd\xca\x48\xfe\xd0\xf5\x70\x58\xf9\x4b\xa7\x0b\x78\xf5\x53\xd4\xe3\x1a\xdd\x8e\x7c\x89\x1c\xda\xb5\xe5\x1c\x79\xd2\x0c\x58\x69\x36\x39\xb2\x13\xe4\x38\x7e\xa6\x30\x8a\x9f\x29\x1a\x03\xa4\x89\x1f\x12\x4e\x49\x8b\x23\xfc\x21\x73\x4d\x95\xf5\xc8\x49\x51\x58\xe3\x82\xfa\x48\xff\xe4\x39\x48\x3a\x1e\xe1\x0f\x99\x6b\x9c\x44\x46\x76\x82\x84\x42\xf9\x56\x8e\x75\x74\x1f\xb9\x49\xb2\x87\x0e\xa4\x93\x24\xeb\x94\xc2\xd8\x08\xfd\xc6\xfd\x4d\xa6\x23\xf5\x4b\xa6\xf3\x3d\x75\xa4\x7e\xa9\xd1\xf3\xf5\x3e\xd2\x3f\xd5\x98\xd8\x2e\x39\x92\x3f\x64\x2a\xdb\xb0\x46\xe2\x7f\x55\x07\xe3\x77\x23\xf9\x43\xa6\x02\xdb\x18\xc9\x1f\x3d\x58\x60\xdc\x41\x9d\x78\xd5\xdd\x1a\x6d\x7e\xd7\xab\xf4\x6f\xd3\x6b\x2d\x8b\xc9\xd3\xd6\xe8\xe9\x37\xd7\x27\xbd\xad\xcd\x26\x1e\x1f\xcc\x25\xbc\xcb\x17\x70\x4b\x38\x3a\x68\x8d\x48\x6b\xd8\xdf\x1a\xf6\x37\x5b\x6b\xd7\xd2\x15\xdc\x56\xa3\x48\xc5\xf7\x9e\x24\xee\x3d\x49\xfc\x15\x3c\x49\x88\x5a\xd6\x5c\x5f\x70\x7f\xa7\x93\x49\x46\x2f\xc9\xcf\x51\x3c\xfe\x4c\xc9\xf7\xbf\xd0\xc9\xc4\x76\x27\xd1\xd0\x63\x1c\x80\x45\x41\x42\x0e\x99\xc4\x1d\x00\x54\x14\x24\x2e\xd8\xab\xe0\x94\x81\xfd\x23\x9d\xd2\x38\x2f\x68\x1c\xd3\x8c\x7c\x3f\x81\x44\x17\xf8\xc7\xe0\x8c\xfc\x9c\xa6\x21\xf9\x7e\x5a\xea\xe6\xe2\xb1\x76\xef\x23\x7c\x41\xbe\x09\x92\x60\x6a\xfa\x9e\xe8\x0f\x18\x16\x06\x19\x07\x98\x73\x00\xe9\x63\xe2\xe0\x14\x0e\x47\x36\x70\x74\x1a\x24\x12\xe4\x25\x98\xf1\xdb\x10\x5c\xf2\xca\x07\xb4\x98\x49\xc0\x17\xcf\x2b\xe0\xc2\x53\xe5\x6f\x76\x56\x55\x5f\x3e\x53\xf5\xbd\x05\xcf\xe4\x65\x80\x09\x2d\x24\xe0\x3b\x9a\xe5\xf0\x94\xaa\x1c\x7a\x21\x40\x54\x27\xce\x83\x6c\x5e\xd5\x0d\x96\xaf\x80\x69\x51\x40\xd4\x26\x17\x3e\x17\x59\x12\x54\x72\x15\x03\x52\xb2\x0b\x76\xa2\xd2\xce\x3d\xa2\xd8\xaa\x10\x85\x95\x2f\xf7\x11\xc2\x81\xa4\x37\x26\xf1\x70\x83\x26\xa1\xa7\x6f\x3c\x43\x82\x3d\x87\x13\x93\x0b\x75\xca\xd2\x15\x26\xb3\x74\x41\xb3\xe2\xd2\x03\xb7\x10\x59\x12\xf4\x75\x51\x2c\xde\x65\xe9\x59\x14\x7a\xc9\x8d\x2d\xd4\x85\xc8\x56\xc4\xb6\x18\x57\x94\x88\x16\x63\xbb\x40\x33\x8f\x86\x6b\x6b\x4a\x56\xff\x99\x9e\x6e\x93\x8e\xac\xc6\xf4\xca\x9b\xd9\x2b\x24\xa1\xe7\xd6\xb2\xd1\x25\x91\x83\x5e\x11\x6a\x15\xf5\x5c\x42\x21\x20\xca\xdf\xba\xd0\x73\xb6\x5c\xc0\x51\x3f\xae\x22\x3c\x15\x99\x2f\x9e\x3b\x79\xf9\x4c\x96\xfc\x30\x73\x4b\x26\xb0\x06\x58\xee\x5b\x5a\x38\xb9\x0b\x4d\xf8\x0c\x44\xae\x03\x07\xee\xf4\xd7\x5f\x65\x1b\x8c\xae\xdd\x3e\x68\x02\x07\x20\xf1\xd9\xc1\x30\x9a\xb2\xf5\x51\x23\x58\x44\x23\xb5\x19\x8a\xff\xf9\x91\x03\x77\x52\x60\x2b\x37\x8a\x62\xf2\x19\x19\x5f\x3d\x05\x83\xe8\x65\x84\x3f\x9c\x26\x3e\xa9\x35\xc0\x7f\x38\x03\x14\x00\x1d\xdd\xbe\x20\xe7\x88\xe6\x23\xf4\xbb\xc3\x8d\x79\xae\xbb\x3b\x4c\x62\x1a\x0c\xc0\x05\x6f\x4e\x89\x1e\x43\xca\x77\x62\xf0\x09\xb4\xc6\xc8\xcd\x33\xbe\xba\xb1\x95\x8e\x8b\x09\x8d\xb2\x4e\x19\x4f\x93\x62\xca\xc3\x31\x83\xeb\x69\x1c\x17\x5e\x99\xb4\x3d\x7d\xc9\x28\x0f\x16\xa1\x7b\xf1\x99\xd2\xc5\x41\xfe\xe1\x32\x19\x47\xc9\xb4\xb2\x2b\x50\xd6\x82\x6f\x46\x81\x9e\x8e\x60\xbe\xf0\x5c\xdb\xaf\x58\x50\xf2\x19\x0c\x77\x27\x05\x5f\x1e\x18\xf9\x64\x56\x42\xc1\xb7\x07\x4e\xbc\xbb\x96\x60\xec\xd3\x81\xc2\x4f\x70\x39\xa0\x4a\xf1\xc2\x1a\x75\xca\x04\x4f\xdb\xfa\x3d\x95\x6c\x5e\xa4\x78\x6b\xb5\xa1\x51\x9a\xa7\x6e\x8c\x4b\x59\x7b\x15\x4e\xb9\x89\xa3\x84\xfc\x99\xfa\x47\x86\xa1\xc4\xb7\x03\x87\x4d\x5b\x38\xa4\x4a\xf1\xc0\xba\xb7\xc2\xb2\xcc\xbe\x7d\x5b\xe8\xf4\xb9\xac\xac\x93\xe3\x69\xf7\xe0\xf9\xde\x5b\xd4\x18\xfb\x74\xa0\xb4\x7b\x1a\x0e\x26\xbe\x7d\x70\xd2\x73\x8a\x02\x84\x04\xb6\x8b\xd9\x0b\x9f\x6f\xfd\xf8\x25\x37\xbf\x14\x32\xbd\x2b\x9a\xd7\x75\x70\x27\x6d\x43\x96\x5d\x9f\x86\x51\x06\xaa\xe2\x71\xb0\x80\xd7\x17\xe8\x02\xd3\x33\xa3\x07\xfb\x7b\xef\x8c\xb5\xcf\xca\x61\x0b\xb9\x88\x8b\x92\x6c\xf9\x32\xa9\x92\xe7\x1b\x8f\x3d\x19\x44\x5f\x34\x23\x57\x36\x38\x94\x51\xfc\xb7\x2a\xe2\xe8\xb1\xe2\xdd\xb0\xd7\x09\x71\xa4\x63\xde\x39\x27\xa0\x83\x69\xcb\x3d\x29\x49\x43\xda\xee\x19\x10\x53\x30\x0b\x19\x91\x36\x13\x3a\x3e\x8d\xe3\x88\x26\xc5\x3f\x38\x78\x5b\xdf\x49\x77\x7b\x37\x69\x8d\x16\xe7\x69\xf6\xb9\xac\xc1\x84\x16\x9f\x04\xa8\x05\x62\x06\x0c\x18\xd9\xab\xfc\x96\xdd\xa2\x42\xa1\x5d\xd6\x2f\x5a\xcc\x3e\xc1\x5c\x8f\xd3\xf8\x1f\xbf\x43\xff\xce\x67\x51\xbe\x50\xbe\x91\x9d\xee\xe5\xb3\xd9\xad\xd1\x06\x3f\x4f\xbc\x7b\x49\x94\xef\xa7\x49\xc2\x7d\x36\xa1\xe5\xd6\x35\x68\xaf\xe3\xdd\x2e\x1f\x3c\xf0\x6e\xa3\xb8\xca\x4e\xd7\xbf\x83\x71\x2f\x05\x52\x26\x2f\xa5\x79\x30\x0e\x85\xc8\x09\x42\xa2\xf1\xea\x6d\x59\xdd\xd2\x9b\x28\x3e\x21\x70\x95\x93\x71\xb0\x68\x8d\xb6\x86\x2c\x09\x1f\x49\x5a\xa3\xad\x4d\x96\xa6\x8f\x03\xad\xd1\xd6\x63\x95\xc2\x45\xa7\xd6\x68\xeb\xa9\x4a\xc2\xc2\x7d\x6b\xb4\xbd\xa5\x32\xd8\x0a\x6f\x8d\xb6\xb7\x75\x82\x16\xea\x5b\xa3\x6d\x5d\xa9\x3e\x16\xb6\x46\xdb\xdf\x3a\xc9\xb4\x98\xb5\x46\xdb\x4f\x9d\xf4\x84\x16\xad\xd1\xf6\x77\x4e\xba\x14\x84\x5b\xa3\xc7\x43\x27\x33\x9f\xcd\x5a\xa3\xc7\x9b\x6e\x3a\x93\x85\x5b\xa3\xc7\xba\xfb\xf2\x8c\xd3\x1a\x3d\xfe\x46\x25\x9a\x07\xe7\xd6\xe8\xf1\x13\x95\x25\xa5\x96\xd6\xe8\xf1\xb7\xd5\xba\xbd\xeb\x93\xde\xd6\xf6\xbd\xe6\xed\x5e\xf3\xf6\xdf\xa2\x79\x0b\xe2\x18\x1c\x4c\xdc\xce\x8f\x2b\x52\x70\x39\xaa\x10\x9f\x2e\x44\x86\x89\x79\x79\xc6\x2d\xfa\x91\x8e\x01\x7a\x23\xe1\x74\xd0\x98\xba\xe8\x48\xae\x9e\xc6\xab\xa8\x79\x05\x97\xbb\x56\x65\x90\x26\x21\xce\x79\xec\x23\x13\x44\xb2\x22\x91\xa9\xbc\xbb\xee\xc5\xb1\x31\x14\x53\x30\x32\x8f\x56\x3d\xb8\xa9\xef\x11\xcb\xb4\xac\x44\xe9\x61\x26\xe0\x23\xf2\x2f\xfc\x72\x9e\xfd\x87\x93\x1d\x73\x49\xbe\x09\x39\x3d\xac\x0e\xf3\x6d\x49\xad\xd2\x1f\xf8\xae\xfa\x75\x75\x05\xf1\x6f\x88\xed\xf7\x81\x25\x42\xea\x71\x9b\x49\xa1\x10\x57\xa0\xdd\x23\xed\x22\xe5\x3f\x4f\xfa\x1c\xcd\x28\xde\xe1\xc4\x73\x1b\x2a\x9a\x39\x9e\x9c\x80\x81\x8b\xb2\x0f\x15\x37\xa4\x5d\x4f\xd0\x6c\xab\x1a\xd6\x1f\x56\x7c\x17\x11\x0f\x77\xa1\x03\x1d\xe1\xe7\x25\x1d\x04\x4f\x37\x28\x6d\x16\xf4\xc3\x2d\xf0\x45\xa1\xf1\x6a\xe0\xd9\x7c\xdd\x85\xbd\x53\x54\x61\xdc\x13\xb5\x38\x0c\x8a\x40\x8e\x80\xfd\xee\xb3\x7f\xc8\x2e\xfa\x7d\x75\x05\x46\xb1\x0a\x00\xae\x92\x73\x09\x22\xbe\xae\xae\x74\xf4\x4d\xd0\x36\xb2\xa6\xe5\x1d\x39\x02\x3c\x1e\x9e\xf4\x73\xc6\x10\x94\x8b\x75\x06\x3d\x17\x02\x8e\xa6\x30\x77\xba\x7e\xf1\x4c\x17\x6e\x65\x57\x98\xda\x0a\xe9\xce\xbd\xb4\xed\xfc\xa2\xde\xa7\x77\x8f\x87\x27\xe8\xe1\xd5\x3a\xb4\xdf\x25\x5f\xe0\xb1\x43\x90\x24\x69\x41\x26\x51\x12\xf2\x7e\x45\xc9\x94\x37\xf4\x4c\x35\x3f\x4e\x93\x3c\x8d\x69\xff\x3c\xc8\x92\x4e\x1b\x97\xe0\xde\x72\x18\x2b\x8e\xd3\x69\x1b\x99\xbe\x8a\x1e\x33\x54\x38\x1e\x97\xa8\x60\x43\x38\x32\x17\xcc\x5d\xc7\xb7\x3a\x7b\xbc\x5b\x3d\x93\x20\xcc\x23\x14\xd4\x28\x9d\x1d\xc2\x14\x37\x58\x8e\x17\x74\xcc\x24\x00\xcf\x7a\xec\x81\x47\xa6\xd3\x60\xfc\x59\xc5\x10\x05\x57\x04\xe2\xb0\x2b\xaf\x5b\x3b\x41\x36\x5d\xc2\x5b\x90\x63\xf5\x0b\x79\xe3\x31\x8d\xd0\x65\x8d\x10\xfb\xb9\xb2\x18\xf6\x1b\xd7\x71\x20\xd8\xc4\x6f\x9a\x7e\x2c\x34\xdb\x48\x96\x71\xec\xa0\x3b\x95\x94\x26\xbc\xdf\xe9\x03\xb0\x84\x98\xa0\x28\x6b\x5c\x33\x0b\x98\xec\x9f\x46\xa6\xd2\x10\x89\xdf\x9c\xb3\x77\xd2\x1e\x1c\x94\xda\x3d\x2f\x63\xed\x49\xf6\xce\x0e\x5b\x9d\x6e\x4f\x37\x84\x30\x5c\x3f\x53\x41\x51\x04\xe3\xd9\xc7\x74\x5f\x3a\xc2\xc2\x53\x26\xbd\x63\xe1\x33\xb7\x9e\x5a\x3e\x6e\xfe\xe9\x0c\x47\x16\xed\x07\x71\xac\xf6\x13\x01\x5c\x72\xa6\x70\xba\xa9\x0e\x18\x9e\x13\x86\xf7\x88\x01\xa4\xda\x1a\x6d\x81\x74\xcf\x57\x7d\x6b\xb4\x05\xb2\x3b\x8e\xd9\xb6\x0d\xc0\xd6\x46\xd8\x1a\x3d\xde\x66\x22\xf3\xe3\x7b\x91\xf9\x5e\x64\xfe\x6b\x8b\xcc\x28\xdc\x0b\x9c\xbd\xef\x2a\xde\xcb\xdf\xf3\x34\xc9\x16\x63\x53\xde\xfc\x85\x27\xaa\xab\xc3\x2c\x4b\x6d\x11\x98\xa7\x29\x49\xd4\x55\x51\xb0\xc1\x1a\x42\xa6\x23\x63\x02\x3a\x3e\x95\x4a\x9a\x22\x23\x17\x81\xbd\x6b\x1c\x05\x06\x61\x28\x7d\x3a\x32\x76\x2c\x0a\x83\x9b\x6c\xe8\x9a\x48\xb0\x2c\x02\x83\x30\xf4\xd8\xd8\x12\x31\x7e\x5e\xa8\xd0\xd6\xad\x83\x35\x18\x27\x66\xc5\x61\xe8\x93\xb9\x7d\x03\xcf\x79\x54\x70\x09\x51\x3b\x22\xc9\xb4\xab\xfa\x2f\x60\xbc\x5d\xf3\xed\xe7\xa6\x77\x01\x85\x5f\xa3\x9b\xee\x14\xe8\x7b\xa2\x24\xe4\x6a\x26\x09\xdb\x43\x75\xd3\x2c\xeb\x09\x49\x34\x77\x65\x62\x4e\x3e\xfc\x97\x10\x16\x35\x80\xc0\x0f\x76\x31\xa9\x50\xd9\x23\xf0\xba\xbd\xe4\xfd\x9a\xa8\xf2\x18\x60\x4e\xf0\xf1\xa0\x54\x60\xe7\x45\x4a\xaa\x65\x62\x8d\xec\x8f\xa8\xb4\xef\xc8\x3e\x76\x81\x75\xb1\x88\xfa\x51\xfe\x8f\x20\x8e\xc2\xf7\x34\x5f\xa4\x49\x4e\x45\x53\xce\xdb\x3b\x67\x0c\xfe\xf6\x3a\x7c\x8d\xf5\x0f\x92\x33\x6f\xad\x3b\x4e\xa5\xd7\x6e\xff\x4a\x2b\xe7\x3e\x9b\x9c\xc1\xf2\x3d\x17\x7c\x43\xf8\x32\x44\xe3\x7d\xd1\x07\xf0\x1a\x81\x13\x9c\x28\xf6\x7a\x2a\xd4\xf9\x86\xf8\x45\x09\xa0\x2c\xad\x9f\xe4\x83\x6f\x8d\xb6\x40\x8f\x26\x56\x64\x6b\xb4\x0d\x56\x6f\x8d\xa2\x7c\xdf\x6f\xf8\xf7\x1b\xfe\x9f\x77\xc3\xd7\xfb\xbd\x12\xcb\xef\x48\x45\xd6\x50\x57\xc5\x4e\x3c\x99\x05\x96\x0b\x59\x7f\x00\x99\xab\xaa\xd3\x24\x1c\x7a\x37\x85\xf5\x60\xf2\x41\x94\x80\xde\x43\x87\x10\x04\xa6\x34\x86\x46\xc8\x71\xdf\xfe\xc9\xd5\x4b\xf8\x91\x19\x6c\xf3\xf6\x33\x65\x0e\xb7\xaf\xc1\xde\x49\x28\x25\x17\x80\xb1\xef\x35\x91\xbe\x9c\xcd\x54\x6f\x03\xc2\xdb\xaf\xbf\x6a\xf3\xa9\xe7\x69\xd4\x13\xe5\xac\x5b\x9d\xe0\x34\xf2\xa8\x41\x90\xdf\x67\x62\x39\x5a\xe6\x01\xbe\x77\x77\x49\x1b\xf5\xa9\x4d\x1e\x3c\x30\x1c\x39\xa3\x73\x33\x6f\xd6\xf0\xf6\x7f\xdd\xb5\xb6\xe1\xaa\x06\x3d\xae\xa1\x49\x07\x12\x4b\xb6\x6b\xc8\xe3\x1e\xa3\x3d\x3b\x83\x55\x11\x03\xcb\x3d\x4d\x03\xed\x89\xc3\x3b\x47\x28\x07\x55\x68\x44\x5a\x1e\xa9\xbd\x6a\x20\x3d\xaa\x80\x5e\xc2\x55\x14\x3f\x5a\x7b\x5f\x36\x05\x61\x28\x69\x38\xd7\xc7\x70\x4c\x1b\x32\xed\x5a\xd5\x54\x4a\x4f\x9c\x54\xfc\x55\x56\x9e\xec\xf5\x71\xfd\xe6\x84\x82\x5e\x21\xae\x32\xfb\x58\x53\xa5\xb4\x3f\xaa\x3f\x9f\x68\x31\x93\xea\x66\xdd\x49\xd3\xeb\x45\xad\x2a\x75\xe2\xa8\x39\x34\x02\xb4\xaa\xb4\xc1\xbc\x72\x6e\xd1\x68\x52\x39\xbf\xb9\xbb\x19\xb5\xeb\xab\x57\xd4\x48\x86\x77\x17\x73\xcb\x79\xaf\xa5\x56\x16\x9c\x55\x68\x1b\x15\x8f\x35\x27\xcf\xd5\x5b\xf1\x8e\x95\x4e\xe7\x5e\x1c\x57\x4e\x17\x00\x89\x8b\x9e\x95\x09\x8c\xab\x42\x6b\x3a\xb8\x3a\xb5\x19\x8f\x02\x5d\xa5\x5a\x19\xb5\x55\x91\x9b\x72\x94\x03\xb6\x7f\x72\xd2\xa7\xb4\xc8\x85\xf1\x4a\x7c\x49\x42\xba\x88\xd3\x4b\x1a\x4a\x13\x41\x78\x3e\x38\x9e\x05\x51\x62\x3f\x57\x83\xda\x5e\xa5\x99\xec\x91\xc7\xf7\x80\x3c\xb0\xfa\x48\x52\xae\xcb\x6b\xa5\x5a\x5c\x33\x5c\xe4\x1e\xc9\xcb\x0d\xfd\xac\xad\xa4\x45\x6c\xf0\x20\x5b\x42\x0a\x4b\x4d\xbe\x10\xb0\x19\x22\xc9\x38\x6a\xde\x57\x10\xa5\x7c\x57\x3e\x2c\x83\xfc\xc1\x80\x9c\x07\x11\x57\x97\x83\xc8\xb5\x28\xb4\x0a\x56\xde\x94\x99\xf3\x2e\x96\x82\x0a\x18\xad\x3b\x46\xbb\xa6\xe7\xe5\x75\x0a\x4f\x93\x8d\xf6\xed\x5d\x09\xfa\xbb\xb1\xb1\x63\x1e\x9b\x06\x03\x92\x17\xe9\x82\xeb\x6a\xa3\x64\x4a\x82\x09\xeb\xca\x37\x43\x3e\x57\x39\xe9\x14\xd1\x9c\xa6\xcb\xa2\xeb\x1c\x1d\x39\x02\x7e\x20\xdf\x0c\xbd\x87\x45\xde\xfb\x3e\xab\xfd\x67\x51\xb9\x8e\xa9\xd0\x25\x5f\xae\x3d\x67\x3a\x1b\x81\xfc\xc1\x9e\xf7\x1c\xaa\x66\xc4\x7b\xda\xd4\x27\x3f\xed\x18\x58\x31\x26\xb8\x2f\x09\xf8\xca\x18\x33\xc2\x06\x27\xc1\xa7\x4c\x62\x5e\x26\xa1\x8d\x81\xb6\xef\xf0\x49\x63\xe4\x50\x04\xff\x39\xee\x88\x6f\xdc\x2a\x5b\x7e\xb8\x66\xe5\x4f\xc4\xc5\x9a\x41\x35\x53\x5a\x7c\xd4\x4d\xbd\xe7\xa4\xa6\x39\x0a\xea\xc6\xeb\x20\x9f\x61\xa2\xea\x49\xc2\xec\xfa\x8f\xf0\xd1\xa4\x23\x00\xfc\xd4\xe6\x2d\xe4\xed\x20\x84\x30\x12\x75\xf5\xc7\xe6\x02\x34\x7b\x04\x71\x8e\xfc\xdd\x91\x7f\x65\xde\xdb\x9f\x28\xef\xed\x65\x7f\xd1\xa4\x63\x52\xdc\xd5\x15\x59\x87\x16\x2b\x8b\x11\xc5\xba\x3d\xb4\x89\xff\x6e\xb2\x04\xf0\x5f\xc3\xe5\x60\x0f\x29\x0d\x51\x88\xe8\xed\xca\x99\x91\x7f\x83\x81\xba\xe7\x8b\xd3\x29\xa2\x5a\x38\x56\x48\x36\xbe\xde\xee\xd6\x34\x4f\x0c\x51\x4d\x71\xd4\x92\xa9\x6e\x50\xd9\x60\x40\xf8\x66\x25\xc5\x85\x20\x09\x89\xb8\x19\x21\xc1\x34\x88\x12\xb1\x72\xce\xa9\x88\xf0\x57\xf3\xe7\x97\x3d\xed\x0d\xb0\xa6\x06\x5b\xd6\x71\xb6\xff\x9a\x21\x8d\xb9\x53\x36\x71\x29\xc8\xb6\x04\xb6\x3b\xe6\x74\x9c\x26\x21\x61\x0c\xb7\xb6\x12\x44\xba\xf5\xc4\x4a\x0c\x8e\x08\xba\xb0\xa6\x1d\xf6\x7a\x31\xba\xe3\x0e\x61\xdf\xed\x48\x94\x10\x27\x5a\xc4\x29\xf3\x22\xcd\x68\xa8\xfc\xb8\x73\x09\x04\x34\x3e\xd3\x20\x27\xc1\x9c\x6d\x48\x7d\x2f\xbf\xb6\xff\x4a\xf9\xb7\xfd\xe7\x71\x2f\x7f\x17\x5d\xac\xee\xe1\x75\x69\x6e\x19\xc7\x70\x4b\xd8\x90\x48\x3b\xd9\xf4\x40\x81\xae\x18\x24\xa1\xbf\x0a\xd8\x31\xfb\x52\xf9\xd2\xb0\xa4\x38\x0b\xac\xe6\xd0\x60\x57\x8a\x0f\x0c\x70\xaa\x0a\x4e\x23\xe3\x72\x81\xbf\x28\xa2\xf2\xf8\x0e\x69\xc1\x69\x44\x76\x19\xa4\x94\xb3\x1e\x72\x4d\x68\xfd\x98\xf4\x09\x29\x21\x01\x12\x4d\x45\x71\x59\x8b\x1c\x5b\x42\xcf\x55\x92\x1c\x53\x72\x79\x8d\x89\xc1\xd2\x8d\x6c\x4a\x9b\x82\x20\xee\xae\x58\x74\xab\xa2\xa8\x2d\x07\x1b\x92\x85\xf0\x75\x22\x15\xc5\xa1\x53\xda\x27\x29\x0b\x08\x25\x2d\xeb\xe3\x9f\x4c\x52\x6d\xe9\x89\x87\x42\x03\x3d\x11\x0c\xa5\xbe\xeb\x17\x52\xb1\x45\x7f\x2b\x6b\x60\x7f\xea\x07\x97\xae\xd5\x29\x12\xd3\x5f\x47\xd2\x41\x4f\xcd\x3e\xe6\x60\x83\x01\x8f\xad\xa8\xad\x2c\x8c\x4a\xb5\xad\xc4\x97\xeb\x1d\x06\x2c\xb1\xb4\x6e\xb6\x2d\x10\x83\x2a\x86\x33\x6e\x06\x6f\x71\x80\x90\xf1\xa3\x84\x38\x1a\x53\xb8\x6a\xd0\xf6\x1a\x56\xf8\x3f\x9f\xed\x08\xd8\x7f\x94\x5b\x8c\x10\xc7\x6a\x24\xef\x2f\xd2\x85\xe1\x60\xce\xec\x5e\x1c\xe4\x85\x80\x74\xaa\xf6\x77\x87\x13\x52\x87\x15\x04\xe7\x45\xeb\xea\xc5\x09\x04\xa2\x85\x74\xbb\x4f\x1a\x85\x35\x5d\x62\x0d\x09\xe0\x3e\x8f\x4a\xf2\x03\x19\xda\xb5\x89\x99\x96\xb4\xbf\x27\xd7\x72\xbd\x16\x40\xfe\xdd\x4a\x25\x88\xd0\x64\x31\x4b\xa9\x4e\x53\xa6\x76\x78\x58\xeb\x66\x97\xfb\x8b\xe0\x32\x38\x8d\xa9\xaf\x7b\xee\x71\x80\xdb\x4f\xe5\x34\x09\x75\x44\xaa\x24\x4d\x1e\x89\x4a\x30\x3a\xec\x6d\xe2\xba\x6c\xea\xc1\xb7\x1f\xe3\x8c\x7e\x15\x6c\x47\x2e\x95\x1e\x8c\x18\xd5\x2a\x27\x08\x6c\xdf\x36\x76\x79\x45\x3b\xe6\x24\x96\xde\x08\xe2\x13\xad\xa1\x03\x90\x72\x5f\x10\x26\xb6\x96\x20\xa4\xe4\x3c\xc8\x95\x40\xb9\x66\xe2\x8a\x2f\x6d\xb8\x7a\x45\x47\x18\x6d\x98\x65\xdd\xbf\xce\x82\x7c\xe6\x43\x3a\xeb\x35\xcd\xb2\xb2\x9b\x48\x7c\xe5\xe8\xbb\x57\xac\x92\x78\x98\x38\x1a\x86\xfc\xda\x0b\x71\x5d\xd6\x13\x7f\x5b\x25\xc7\x2e\xb2\x0b\x65\x4a\x84\xaf\x52\x09\x71\x12\x65\x79\x51\x2e\x20\xae\x28\xe3\x95\x68\x40\x7c\x6a\x0f\xdf\xf5\xab\xf1\x55\xe7\xf8\x12\x22\x6d\xf2\x81\xd7\xcd\xb3\xd5\x58\x53\x94\xd7\xa2\x7a\x95\xa1\xfb\x79\x9a\xd2\xc9\x73\x20\xa1\x2b\x13\xd8\x95\x9b\x20\x3b\xdf\xbe\xe0\x76\xa5\x90\x24\x3e\x0d\x03\xb4\x1b\x0b\x5e\xb6\xd6\xac\x4e\x3b\xeb\xd9\xd4\x45\x4d\xd7\xa6\x0c\x34\x51\xf5\x0f\xd6\x06\x03\x6b\x07\x36\x2e\x70\xb4\xc7\x63\xa4\xbe\xb4\x2a\xef\xf0\x7d\x79\x30\x30\x5c\xe9\x96\xc6\x9d\x1e\x8f\xc1\x2b\x6e\xca\x03\x35\x45\xc9\xb4\x42\x36\x33\xd5\xd8\xe6\xc8\xf9\x24\x5e\xbb\x9c\x08\x8b\x43\x55\xa2\x10\xf9\x82\xa4\xae\xa6\x12\xd1\x84\x24\xa9\xae\x81\xb1\xb7\x45\x90\xe7\x34\xec\xb1\x2a\xb4\xeb\x3b\x06\x91\xa3\x25\x6d\xf2\x32\x45\x78\x30\x03\x16\x3a\x0d\x73\x48\x9f\xef\x54\xd3\x66\x95\xac\x2c\x43\x69\x4b\x79\xad\xad\x2c\x66\xc8\xb5\x24\x04\xab\x81\x10\x61\xd2\xa8\x40\x75\xa9\x27\x0b\x9c\xd2\x71\xb0\xcc\x29\x3b\x89\x87\x69\x52\x90\xf3\x20\x01\x9b\xa4\x7c\x91\x46\x31\xbf\x0d\x4f\x0a\x9a\x4d\x82\xb1\xf2\x8d\xdd\xe0\x24\xde\xe4\xb4\x6d\x6f\x53\xf5\xfc\x90\x38\xee\x75\xd5\x9a\x46\x6b\xf3\x47\x5a\x70\x67\xcd\x6c\x7f\xec\x91\xf3\x59\x34\x9e\x81\xd1\x00\x5b\xde\x45\x2a\xb6\x31\xb2\x88\x97\x79\xfd\xd5\xab\xe0\x03\x35\xf3\xab\x99\x87\xdf\x90\xa9\x46\x84\x5d\x5d\x4e\x55\xc5\xea\xe5\xc7\xdb\xc8\x8e\xe5\x72\x23\x32\x56\xbe\x91\x1c\x53\x25\xc3\x98\x2f\x1d\xfa\xdc\x20\xbd\x39\xf3\xf5\x9c\x7a\xbc\xc7\xdd\x06\xd7\xe7\x65\xac\xc9\x39\x0c\x7b\x4f\xc1\x25\x2f\x59\x7c\xe7\x61\x77\xf7\xd3\x76\xe1\x1c\x7f\xee\xe3\x15\xe2\x39\x4c\x7b\xcd\x96\x2c\xba\xdd\x51\xe6\xcf\xa6\xad\x44\x6b\xf4\x6d\x99\x05\xb4\xb2\x68\x68\x8d\xb6\xb6\x5d\x93\x68\x31\xf2\xd6\x68\x7b\xf3\xfa\xa4\xb7\xf5\xe4\xde\xf4\xe9\xde\xf4\xe9\xaf\x6d\xfa\x84\x6c\x9d\x85\x09\xe4\x1d\x18\x3b\x97\xb8\xb1\x14\xc6\x95\xfc\x5d\xd6\xe1\x44\xde\x39\xef\x65\xd3\x7c\x54\xa2\xb9\x41\x32\x9e\x38\xc1\x8a\x4a\x70\xec\x3b\xb9\x9d\x30\xf6\x29\x2b\x25\xd8\xc4\x09\xf8\x7c\xcf\xd7\x87\xf7\xef\xf6\x39\x73\xbf\x4d\x07\x78\xbc\x25\x60\xb5\x14\x1e\x30\x16\x29\x79\xff\x6e\x5f\xdc\x13\xf8\x3b\x20\x9e\xa3\x83\x13\x45\xdd\xf2\x2c\xcd\xf1\xed\x97\xdb\xf8\xfe\xe1\xdb\xb7\x2f\xf7\x3f\x1e\x1c\xbe\x25\x2f\xdf\xbf\x3f\x7c\x3f\x22\xfb\x4a\xfd\x3b\xe6\x55\xf2\x13\x7d\x48\x49\x7b\x83\xb0\xfa\xc8\x46\xbb\xef\xef\x83\xf6\x78\xd3\x74\xec\xea\x9d\x3d\x57\x22\x14\x6c\xf5\x44\xbc\x32\x7f\x13\xd2\x90\x76\x44\x6c\xa3\x60\x34\x4c\x78\x96\x46\xf3\x3c\x98\x52\xb2\x4b\xd6\xd7\xc5\x4b\x43\xb6\xad\x8b\xdf\x7d\x1e\x32\xd6\x49\xe9\xcb\x62\xcf\x88\x37\x79\x44\xd4\x74\xfd\xfd\xc3\xe1\x5b\x98\x95\x4c\x75\xc9\x13\x66\x55\xf4\xcd\x79\x4b\xa6\x71\x20\xaa\x36\x47\xab\x67\xf3\x23\xbf\xae\xc6\xe3\x9d\xe7\x4d\xa7\xf4\xe3\xc1\x9b\x97\x87\x47\x1f\x47\x44\x5c\x7a\x33\xe2\x62\x9d\x9c\xe7\x64\x83\xb4\xd9\x7f\xc1\x78\xc6\x38\x46\xdb\x08\x68\x23\xdc\x48\x7e\x7b\xbf\x5b\xdd\xef\x56\x7f\xed\xdd\x0a\x6d\x56\xf0\xea\xf2\x8f\x6a\xa5\xdb\xfc\x31\x7b\xa3\x37\xf4\x77\xf8\x94\x5d\xfa\x1c\x62\xeb\x5f\x1d\xce\x70\x44\xa6\xdc\x38\x86\x88\x37\xb6\xd0\x96\x3e\x2c\xd8\x46\xc8\x5f\xfb\x1d\xfc\x42\x9a\xf2\x22\x45\x3a\xce\xe7\xa1\x2b\x48\xc5\x73\xe4\x3c\x4d\xba\x35\x4f\xe8\x51\x66\x92\x26\x97\xf3\x74\xa9\x5a\x54\x09\x25\xa7\x37\x89\xb4\x29\x95\xb8\xa2\x21\x97\x07\x20\x88\x81\x13\xac\x49\xa4\xa9\xe3\xd9\xf3\x34\x8d\xaf\x21\xbc\x6a\x08\x2e\xc8\xf9\x26\x41\x39\x64\x88\x66\x07\xde\x87\xd0\xd0\x70\x98\x2e\x4f\x7c\x10\x8c\x80\x2d\x4a\x51\xfb\x60\xcd\x98\x26\xec\x7d\x8b\x41\x98\x8e\xa3\x78\xbd\x76\x00\x06\x84\x7c\xf7\x4a\x24\xf2\x88\x0a\x51\x5f\xd4\x04\xf7\x1b\xe2\x77\x89\xb9\xab\xbf\xbc\xb6\x57\x2e\xbd\x21\xc6\xd8\xe6\xf4\x19\x72\x17\xe0\xe0\xc5\xc8\xc2\x75\xa8\xbd\x83\x7b\xa3\x05\x79\x2b\x28\x47\x1d\xaa\xae\xca\x49\x10\xa7\x44\xd7\x41\x79\x47\xd3\x6b\xf3\xd1\xc1\x0a\xf5\x0c\xad\x10\xfe\xcc\x2b\xc6\x85\x8b\x56\xd3\xc3\x4a\x23\x92\x9e\xd4\x6f\x34\x9c\x3c\x9a\x26\x41\xb1\xcc\xec\xe1\xe0\xf4\xb2\xf1\x60\x98\xf2\xf1\x28\xa8\xaa\x01\x81\x03\x83\xe6\xfd\x17\x2f\x1c\x24\x79\x0b\x8e\x14\x24\xa1\x52\x2d\x15\x29\x04\x25\x9e\x44\x49\x10\xfb\xad\x9e\x79\x1d\x3e\x9b\x52\xbc\xae\xad\x2c\x51\xbd\x81\x14\x99\x47\xcf\x68\x76\x59\xcc\xb8\xc6\x7a\x7e\x1a\x01\xcb\x48\x79\x94\x68\xe8\x9b\x08\xb3\x50\x89\x2d\x8f\x6b\x10\xd1\x1d\xc7\xb3\x9d\x5a\xdc\xea\x17\x7a\x04\x78\xef\x40\x44\xbb\xeb\x50\xfe\x39\xea\x3c\x8b\x48\xbd\xe6\xba\xb5\xf3\xb8\xfd\x14\x95\xf3\x87\xad\xc2\xb7\x20\xf7\xd3\x29\xa9\xbd\xd3\x75\x55\x9a\x62\x9e\x3e\xca\x8e\xdd\x96\xa5\xa3\x10\x16\x95\xfc\x1c\x1c\x2f\x8b\x60\xda\xa2\xfc\x71\x04\x21\xa6\x2c\x63\x00\x01\x84\xe7\x8f\xd1\x8d\x4e\x4e\x96\x71\x5c\xf2\xc2\x45\x6b\x16\x89\x7b\xf9\x6f\x2a\x84\xa1\xbe\xb2\xc0\x8c\x90\x69\x8d\xe6\xac\xe2\xb6\x5f\x60\xdf\x79\x1b\xd3\xe1\xdb\x57\x8f\x9c\xd9\x37\xe7\x5d\x3b\xb6\xde\x4a\xb5\x41\xdf\x6b\x28\xce\x24\x92\x71\x9a\x8c\x83\xa2\x63\xcc\x7e\xb7\xdc\x8f\x4d\x29\xd7\x13\x4e\x6c\xca\xb9\x9e\xbd\xdb\xd2\x32\x0e\x17\xf2\xbb\x07\x97\x87\x09\xae\x20\x0c\x87\xe0\x84\xc0\x6b\x09\x55\xb3\x0f\x1e\x80\xbe\xc1\xec\x45\xf5\x36\x5d\xee\x7c\x07\x70\x70\x87\xde\x77\x82\x6c\x6a\xad\x2e\x2d\x3e\x3e\x33\x4a\x8e\xf0\x97\xf0\xcc\xb3\x89\x3c\xa1\x88\xf1\x89\xfb\x17\x55\xaf\xfd\x52\x8b\x4f\x26\xf9\xa2\xa4\x34\x5c\xdf\x56\x77\x87\xad\xcc\x5f\xd2\x28\xe9\xb4\x5a\x6e\xe5\xea\x51\x1c\x27\x37\x8e\x27\x7c\xbd\x01\xb2\x61\x87\x2d\xf3\x6e\x0f\xf7\x08\x5f\xd5\x24\x69\x71\x60\xf4\x55\xa1\xd0\xe3\x6f\x48\x03\x37\x6c\x1b\x5e\x2d\x74\x7b\x56\x2b\xb8\x7d\xb5\x91\x20\xae\x9d\x2e\x8b\xc5\xb2\xf8\x29\x9d\x6a\x76\x2d\x7c\xf1\xa0\xd5\x22\x9d\xff\x70\x3f\x33\x48\x2c\x33\xc1\x34\xb7\x86\x31\xd9\x6e\xa0\x38\x0c\xbf\xe5\x32\xf8\x69\x46\xc3\xe5\x98\xa2\xb9\x0a\xc6\xe3\x1e\x11\xae\x28\x31\x3f\x09\xc6\xe3\x63\x91\xcc\x79\x22\x43\x8a\xf8\x96\x54\xfe\xcc\x9c\xb2\x7e\x3e\x8b\x26\x45\xa7\x4b\x46\x0e\x46\x65\x96\xa3\xb4\x0a\xc6\x63\xa9\xa5\xe2\xc6\xde\x9c\xb4\x69\x4c\x0b\x2a\xc7\xa1\x9d\x24\x99\xe9\x9c\xaa\x6e\xc0\x32\xd0\xfd\x95\x78\x57\x22\x96\x36\xdb\xea\xb9\x18\x57\xea\x58\xe1\xae\xe4\x22\xa3\xe1\x6a\xe1\xc7\xe3\xb8\xc1\x96\x7e\xfe\xe8\x1e\x99\xb6\xea\x3d\x32\x55\x15\xdf\x2c\xb7\xb1\x33\x2b\x20\x86\x04\x68\xf8\x7e\xb0\xc5\x0e\xdb\xed\x93\x23\x50\xfe\xa1\xfc\x3f\x95\xd2\x32\x36\xfd\x6f\xf0\xa8\xd1\x7a\xd5\xe6\x7d\xd1\x58\x49\x8d\x5f\xcb\xd9\x14\x03\x35\x4f\xae\x65\x1c\x50\xda\x17\x42\x4b\xc7\x08\xe0\xc4\xa0\x5e\x1f\x00\xf6\x5f\xa5\x89\xc2\x0b\x7a\xac\xd8\x3d\x6f\xfb\xa4\x74\x00\x86\xd5\x84\xf7\x4e\xd8\xc0\x25\xf2\x88\x55\x75\x25\x5c\xe7\x27\xeb\x86\xae\xb1\x9e\x36\x51\xc0\xdf\xd6\xd7\xe5\xc0\xaf\x9b\x7c\xc3\x69\xd0\xa3\xff\xab\x0e\x24\x82\x63\x88\xac\x0d\x06\xe4\xe3\xe1\x8b\xc3\x11\xc9\x28\x37\xc8\xea\x91\x3c\x15\xa6\x33\xea\x8a\x4b\xdb\xe2\x04\x5c\xd3\xd5\x67\xe5\xa2\xa2\x9d\x93\x84\x8e\x69\x9e\x07\xd9\x25\x5b\x2c\x10\x01\x3b\x67\xe4\xd6\x06\x7f\xc5\xe0\x2d\x9a\x9c\xa7\xd9\x67\x2e\xe5\xcd\x97\x71\x11\x2d\x62\x14\xc9\xc1\x8c\x9d\xe2\x77\x6f\x34\x78\x48\xbc\xb6\xdc\xdf\x48\x53\x6e\x5e\x87\x69\xc6\x20\x9b\x37\x6c\x48\x75\x63\x34\xe4\x1b\x87\x79\x32\x51\xa5\xfa\x12\x47\x3e\x07\x36\xeb\xac\x73\xc7\x2e\xec\x89\xef\xfc\x50\x06\x6b\xb1\x53\xe2\xd8\x37\x9a\xfd\x14\xfe\x9c\x7c\x35\xd5\x98\x41\x7a\xeb\x29\x3d\x42\xe9\xfa\x05\xc1\xdb\x63\x72\x00\x3c\x47\x6e\x9e\xe3\xc3\x06\xcf\x51\x4c\x4f\x98\xf4\x98\x5d\xf4\x58\x7e\x8a\x62\x39\x2d\xac\x48\x31\x3e\x1f\x57\x95\x07\xb1\xea\xe9\x8e\x68\xc5\x78\x35\x8c\x67\xc8\x65\xf4\x42\x74\x90\x93\xcb\x95\x87\xad\x0a\xde\xc1\xc0\x09\xb2\x1b\xa5\x17\x7d\x83\x1d\xe9\x8f\x1d\x22\x01\x24\x17\x82\xff\x77\x64\xaa\x62\x39\xfc\x87\x4a\x47\x8c\x46\xfe\x34\xe5\x48\x7a\x21\x9e\x77\xbb\xdc\x9c\xa3\x41\x7b\x26\x2a\xe1\xcf\x25\x1c\xb9\x35\xda\x06\x0f\x46\xd8\x69\x38\x63\xcc\xdf\xdd\xdf\x8c\xde\xdf\x8c\xfe\xb5\x6f\x46\xc5\xb5\xa8\x78\xf2\xfb\x5f\x11\x5f\xef\x4e\x3d\x86\xc3\x21\xe0\x21\xd9\x4f\x93\x33\xca\x58\x51\x20\x42\x1e\xc3\x39\x18\xce\x02\x10\xb7\x58\x06\x72\x61\x04\x1c\xc4\x79\x4a\x82\x38\x4e\xcf\x73\x1e\x9e\x1d\x14\x75\x79\x7f\x8d\x55\x24\x05\xff\x37\xd1\x05\x0d\xaf\x79\xd6\x9a\x7b\xaf\xb1\x26\x6e\x54\x8b\xd4\x0e\x72\x2c\x54\x96\xea\xc0\xd9\x31\x55\xa2\xe4\xea\x4a\x06\x48\xd7\x19\x6d\xa5\x43\x6d\x77\x6d\x65\x00\x3f\xcb\x09\x11\x89\x2b\x66\x79\x1f\x3a\x52\xbf\x68\x34\xc4\xf5\x10\x87\x13\x50\x35\x77\xa1\xf6\xa1\x53\x27\x40\x0a\xbe\x8f\x5f\xb4\x1a\x77\x46\x32\x88\x92\x6a\x07\x8e\x5c\x4c\xd4\x64\x9c\x56\x5e\xfe\xd8\x96\xb0\xa9\xd2\xef\x8b\xc3\x56\x8f\x4d\xc2\x19\xcd\xa2\x09\xf8\xf5\xc8\xe8\x38\x60\x1c\x07\x05\xaa\x79\xf0\x80\xc4\xc1\xaf\x97\x24\x4e\x83\x90\x84\x97\x49\x30\x8f\xc6\x24\x4d\x68\x0e\xad\x89\x09\xd1\x0d\x89\x60\xd6\xa9\xd2\x13\x00\x94\xb4\xaf\x97\x8d\x3b\x50\x6c\xb6\xa6\xb4\x38\x54\x87\x64\x8f\x07\x67\x36\x31\x5a\x60\xad\x73\x0f\x80\x95\x09\x62\x4a\xe4\x31\xb9\xfc\xd6\xc3\xd0\xf4\x97\x5e\xbd\xf0\xec\xfc\x3c\x82\x78\x25\xa8\x57\x04\x74\x10\x39\xe5\x27\xe8\x91\xf3\xb2\x8a\x0b\xef\xcb\x8c\x0a\xf5\x62\x0f\x2e\xf0\xc6\x7c\x75\xf0\xc3\xf1\x8c\x5e\xf8\xd4\x06\x5a\x6b\x6a\x25\x58\x9e\x28\x1b\x14\x31\x34\x9f\x22\xac\x76\xa9\x52\xde\x52\xf8\xcb\x20\xdc\xcf\x44\x78\x72\x56\x95\x58\x64\x5d\x32\x92\xeb\x4d\x80\xb9\xb2\x92\xef\x9a\xc0\xf3\xbc\x0e\xba\x39\xb2\xba\xdd\x73\xe0\xd8\x12\xd0\x50\xec\xcb\x85\x29\x52\x5c\x8f\x9b\x1f\xc8\xa8\xcc\x12\x28\xc0\x31\x99\xed\xd6\xe0\xfe\x6a\xb4\xd2\xb5\x56\x5f\x95\xeb\xfa\x7a\x77\x93\x1a\x45\x29\x53\x3f\x85\x0e\x3a\x9c\x02\xf3\x19\xa3\x40\x0f\xc2\x2d\x52\x97\xaa\x9a\xbd\x30\xe4\xcf\x22\x94\x12\x2d\x48\x42\x92\xd3\x22\x27\xcb\x05\x64\x88\xd3\x08\xb0\x8c\xa8\xa0\x19\xdb\x3b\xd2\x33\x21\x6c\x09\x37\xa6\xfd\xb5\x35\xf4\x34\xe2\xa7\x74\x9a\xef\x15\x1f\x8a\x20\x2b\xd6\x6c\x4d\x63\x4e\xe3\x89\x4a\x9c\xb8\xef\x97\x05\x0b\x37\x6b\x31\xe2\x84\xd1\x78\xe2\xf8\xf0\x91\x8f\xec\xa6\xb4\xe0\xfa\x2c\x56\xd8\x7a\x69\x07\xfa\x05\x3d\xcc\x1c\xba\x47\xe4\xc9\xd3\xe2\x19\xac\x95\xbe\x8f\x71\x40\xc6\x94\x16\x1d\xeb\xcd\x8f\xb0\x64\x74\x4e\x39\x83\x01\x09\xd3\xa4\x2d\x5e\x89\xb2\x3e\x0a\xb4\x81\xd9\x24\x5c\x74\xcb\x44\x69\x76\x04\x9e\x30\xfa\xfd\x3e\xf9\x65\xc9\x1d\x01\xb3\x36\x19\xef\x75\xce\xcb\x25\x0f\x23\x2b\x1e\x45\x5e\xdb\x2f\x60\xad\x95\xae\x86\xe1\x3f\x63\xf2\x4c\xef\xc1\x94\x1b\x72\xd6\x3d\xd3\xe4\x8f\x77\x4c\xb3\x4f\xa3\x7f\xf5\x7e\x58\xbf\x1e\xe9\x2e\xd2\x38\xe6\xe4\xe3\x27\x5b\x41\x9b\x1a\xcc\xa6\x4b\xa5\x12\x01\xb5\x6d\xf2\x46\x99\xe1\x1a\xc4\x92\x96\x90\x8b\x98\xd1\xd4\x99\x53\x69\x64\xc1\x48\x4f\x8e\xd5\x37\x09\xbe\x67\x53\x3e\x9a\x48\x1b\x9f\xe4\x9b\x52\xc7\xcd\x28\x43\x9b\x29\xc3\xd0\xb4\xf2\xfa\x99\x95\xa0\x2b\x19\xc9\x42\x2e\xe9\xdc\x0a\x3d\xb7\x23\xd2\x52\x7d\x00\xf4\xc9\x76\x46\xcd\x18\xcf\xbb\x34\x8e\x19\x9f\xd1\x3d\xe1\x34\x38\xe2\x45\xd8\x39\x8d\xce\x69\x52\xc0\x91\xb3\xcf\x28\x0e\x86\xa6\xf7\x92\x85\x30\xb4\x3f\xe6\x98\x02\x72\x3c\x08\x4f\x7a\xf2\x8a\xca\x48\xee\x69\x62\x14\x39\xd8\x8d\x11\x57\x10\x03\xfd\xb2\xcd\x5a\x46\x2d\x74\x48\xdc\x92\xc9\x7a\xc4\x09\xef\x21\x97\x9b\xe7\x76\xa0\x27\x4e\x53\xfb\x19\x85\x31\x81\xbd\xf6\xbe\xe7\xa1\x23\x30\x3b\xae\xc1\x46\x17\xae\x06\x3e\x90\x86\x6f\x15\x55\x59\xa9\xae\xab\x54\xd9\xe3\x57\xaa\x99\x9d\x41\xb6\x04\xa4\xd4\x63\x7c\xa9\x35\xa6\x16\x36\xb5\x18\x6c\x89\xbe\x08\xda\x41\x83\x99\x80\x20\xe5\xcc\xbb\x4f\xc6\xd4\x0a\x11\x96\x35\x2a\x43\x6c\xb9\xfb\x65\xf9\x9a\xed\x39\x59\xf8\xda\x49\xfd\x2e\xed\x77\x3f\xa1\xe7\xe2\xd6\x09\xe3\x00\xfb\x0a\xe3\x4c\x32\x0a\x0d\xd7\x78\x7e\xe6\x58\xb3\xec\x3b\xe3\x53\x8f\x98\x3b\x3e\xad\xe5\x83\x44\x70\x64\x71\x2e\xac\xa0\x5e\xcb\x21\xa9\xcb\x5e\x2a\xca\xfa\xbb\x51\xad\x77\x36\x96\x36\x23\x82\xd0\xf5\x03\x88\x5d\x35\x64\x14\x2e\x19\xd8\x99\x63\x41\x93\x10\x0c\xdc\xd4\x24\x07\x39\x28\x5a\x92\x9c\x51\xa8\xf2\x05\xa3\x2b\x4a\x27\x00\xcc\x0a\x31\xa9\xa7\xcb\x95\x2b\xaa\xf5\x65\x12\xe4\x79\x34\x4d\x68\xd8\x77\xfb\x68\x53\x94\x8f\x27\xfb\x66\x47\xc9\x58\xe3\xd3\x9a\x09\xf2\x36\x83\x4d\xc6\xd0\x48\xb4\x3d\x31\x89\xb1\x74\x18\xc4\x19\x0d\xc2\x4b\xfd\x5e\x5d\x0b\x8a\xf9\xed\x29\xcd\x14\x64\xa5\xf4\x5a\x37\xae\x68\xd2\xb1\x5a\x53\x3e\xe0\x86\xae\x47\x2e\xbd\x32\x39\x17\xf7\xb9\x85\x64\x52\x74\x91\x8a\xb1\x45\xf3\x39\x0d\xa3\xa0\xa0\xf1\xa5\xdd\xac\x20\xf7\x71\x53\xda\x36\xa5\x13\xa8\xbe\x53\xe2\x69\xc2\xe7\xb5\x0a\x6b\xb2\x39\xcb\x67\xdb\x0f\x1f\x0c\xba\xcb\x3d\x77\xa2\x74\xd8\x9b\xb9\xc9\xdb\xb8\x61\x1f\xea\x87\x54\xc7\x18\xcc\x11\x8f\xc6\x9a\x27\x71\x5d\xea\x0e\x04\xe1\x1a\xdd\x09\x5f\x37\x1d\x08\xde\x77\xeb\xc7\xe3\x48\x0e\xe9\x42\x0a\x0e\xe6\x40\x6a\xf8\x3b\x3c\x2d\x9f\xa7\x67\x52\xa5\x49\x82\xfc\x32\x19\xab\xc3\x8f\x4f\x30\xf2\xf1\xed\x65\x02\x6f\xa7\x0d\x04\x20\x19\xc3\xc2\x96\xc3\xbb\xb0\x21\xfc\x2a\x35\x1b\x82\xbf\x83\xd1\xa9\x15\xb2\xdd\xe7\x3c\xc1\x91\x29\xbc\x26\x27\xaa\xa4\x2d\x94\x5b\x3b\x6a\x89\x1d\xe5\x60\x40\x0e\x26\x9a\x33\x46\xb9\x7a\xd7\x77\x49\x85\xfb\x15\x12\x15\x44\x7b\xe9\xd2\xe5\xce\x67\x14\x8c\x31\xc4\xe8\xbb\x84\x33\xd5\x9c\x44\x85\xc9\x56\xbd\x1b\xb5\x43\xec\x6a\x99\xf9\x76\x0f\x1f\xfa\x45\x8d\xf6\x84\xe2\xfd\x18\x22\xa4\x78\xf8\xdb\x57\xf4\xcf\x63\xc9\xe3\x19\xb5\xad\xf7\xe2\x74\x5a\xd6\x2e\xb1\x18\x53\xc5\xd9\x02\x6a\x19\xb1\x3d\xa1\xc4\x1d\x9f\x3f\x60\x89\x09\xe2\x1c\x00\xec\x81\x35\xa7\x23\xc7\xcd\x94\x10\xc4\x0f\x5e\xf0\x84\x91\xa0\xb1\x4e\xb7\xcf\x77\xe4\x71\x20\x1d\x16\x82\x5b\x15\x1a\x12\xb6\xba\x67\x59\x9a\xa4\xcb\x5c\x79\x2f\x14\x86\x01\x6c\xb7\xb7\x3d\x11\xf1\x6a\x84\xb0\xdb\xf6\x9a\xd7\x82\x53\x89\x54\x5b\xe9\x35\x21\x20\xd7\x86\x8e\xd5\x50\x3f\x87\xb7\x98\xb7\xeb\x1a\x7e\xec\x5c\x91\x72\xdc\x3a\xb1\xdf\x2a\x2e\x48\xaf\x4f\x7a\xdb\xc3\x26\x57\xa0\xed\x65\xce\xf5\xe2\xe3\xa2\xbd\x76\x7f\x21\x7a\x7f\x21\xfa\x27\xbe\x10\xd5\x4f\x45\x91\xca\xfa\x26\xef\x45\x05\xf0\x0a\x37\x99\xbe\xd8\x6f\x8d\x9f\x98\x26\x93\x68\xea\x85\xe3\x59\x12\xf0\xe0\x34\xb0\x62\xba\x44\xa7\x41\xe2\x89\xd3\x02\xda\x64\x1e\x68\x8a\xdb\x48\xf3\xcb\xcc\xd3\x68\x2a\x3c\x18\x58\x56\x8c\x1c\xe8\x79\x34\xb5\x94\xfa\xd8\x9a\x91\x6b\x9c\xaf\x38\xc4\x95\x82\xbd\x36\x9d\x56\xe9\x74\x6c\x89\x0b\x7a\xc6\x92\x36\x0c\xa9\x88\xf7\xce\xfb\x0c\xad\x48\x55\x59\x09\xb6\xa3\x94\x40\x51\xfe\x2e\xa3\xe2\x1a\x14\xdd\x4e\x18\x75\x9f\xea\x74\xab\x81\x63\xe5\xee\xbe\x2d\x4e\x9e\xed\x5e\x9b\x06\x59\x1c\xf1\xc4\x71\x3a\x9f\x47\x45\x41\xc3\xf6\x89\xba\x22\x35\x6a\xfb\x61\x97\x0c\x51\x67\x92\xc5\xb2\x78\x41\x27\xc1\x32\xf6\x5e\x95\xd4\xf5\x8a\xed\xc1\xa7\x78\x10\xf8\xa1\x8c\x37\x5e\x0b\x23\x92\x7e\x88\x5a\xf4\x78\x9b\x2a\xbf\xb9\xc1\x5d\xb0\x46\xf1\x5b\x74\xdf\x7e\xc3\xc5\x45\x12\x56\x4b\xc9\xac\x1a\x8d\x7a\x2a\x44\xd9\x1e\x3c\x48\x6a\x7a\x4d\x2f\xaa\x46\xfe\x72\x91\x8e\x67\x55\x23\xa7\x1a\x80\xf7\x01\x44\x4c\x9d\xe8\xbe\x6f\xb2\x33\x5b\x9c\xea\x5a\x16\x35\xca\x64\xd6\x77\xd6\x73\x4f\xbf\x71\xdb\x86\x39\x33\xef\x6a\x8e\xcc\x37\xd3\x09\x09\x0c\x27\x86\x41\x12\xca\x3b\xdd\x1c\xee\x74\xb8\x05\x03\xe3\x10\xaf\x5f\xfe\xd3\x62\x0c\x50\x07\x93\xe0\xbd\x2c\x41\xde\x3a\x18\xce\x80\x1d\x13\x7d\x79\x99\x2f\xef\x25\xdc\x3a\xbd\x21\xca\xbf\x18\xd7\xdc\x70\x51\x89\x2e\x8b\xe1\xf3\xea\xca\xa2\xfd\xbd\x31\x04\x88\x40\x2e\xda\x30\xbc\xc7\x37\x98\xac\x16\xfa\x24\x1c\x66\xf9\x2f\x49\x4d\x89\x0d\x57\x5d\xa4\x22\xb2\x75\x54\x90\x79\x34\x9d\x71\x11\x57\xb9\x59\x16\xea\x34\xa7\xe5\x22\xad\x6d\xb7\x48\xcd\x56\x8f\xdb\xf3\xe0\xe2\x15\xa5\xef\x68\xf6\x63\x90\xb7\x7b\x84\x7d\xbf\xcb\xa2\x34\x8b\x8a\x4b\x23\x7d\x1a\xe4\xef\xb2\x68\x4c\xc5\x6f\xf6\x1f\x4c\x33\xfb\x91\xa4\xc9\x98\xfa\xde\x5b\x7e\xa6\x97\x15\x2f\x2e\x3f\xd3\xcb\xa6\x6f\x2e\xa1\x26\x07\xd7\xbc\x86\x5d\x64\x21\xf2\x82\x8e\xa3\x79\x10\x77\x30\x80\xfb\xe6\xcd\xbc\x16\xfe\xda\xc4\x8e\x9c\x83\xde\x35\xcd\xfb\xaa\xbe\x7b\xd2\xbf\x29\x75\xdf\xd3\xf5\x1f\x91\xae\x85\xf8\xe6\x10\x36\xdc\x14\xcb\xa8\x47\x82\xaa\xbd\x42\x5d\x63\x7a\xbe\x30\x05\x39\x91\xbe\x66\x48\x6f\xb5\x14\x5c\x5c\x74\xbf\x28\x1d\xe6\x45\x1f\x8b\x01\xeb\x52\x8f\xa0\x75\x77\x26\x80\xf2\xe5\x91\x4a\xfc\x99\x00\xea\xb5\x0a\x4b\x47\xb8\x80\x77\x71\xfe\xea\x1d\x28\x6f\x1b\x36\x94\x54\x53\x5e\xf4\x81\xa4\xfc\x85\x20\x4b\x43\x4e\x83\xdc\x0f\x37\x0d\x72\x03\x0a\xc8\x17\x81\x6a\xa1\x16\xe5\x1b\x43\xc5\x6b\xc3\x24\x54\x43\x11\x6a\x01\x96\xb4\x80\x61\x0c\x9f\xa4\xaa\x2d\x67\xdd\xd5\xb5\xe9\x16\x28\x6f\xdb\x81\x35\xfa\x50\x5c\xf4\xa5\x99\xa2\xb7\x02\xfc\x28\x5a\xea\x4c\x2e\x56\x5e\x3a\x32\x9e\xd0\x4d\x96\x90\x08\x6d\x54\xb9\x92\x54\xa4\xad\x55\x96\x93\x5d\xb1\xe5\x60\x07\x87\x48\xd2\x21\x91\x6a\xd6\x97\x0f\xca\xa5\x51\x0f\x94\x26\x3f\x99\xd9\x60\xb9\x95\x82\x96\x37\x59\xb2\xf0\x54\xe4\x9e\xe5\x7c\x19\x07\x45\x74\x46\x7f\x0c\xf2\xa3\x1c\x5e\x20\x96\x55\xe5\xc0\x5a\x75\x4d\x6b\x6b\x98\xaa\x72\x72\xf0\xa6\x51\x89\x84\x8b\xd3\xa9\x6d\xa3\xa9\x33\x50\xdc\x21\x47\x89\x08\x9a\x40\xaf\x0a\xd1\xf3\x8e\x99\xc1\xd6\xe9\x0b\x45\x4b\x8d\x16\x00\xcc\x6e\x73\x92\x87\xd3\x56\x25\x95\x43\x85\x0d\x68\xdc\xac\x09\x5b\x22\x41\x0d\xca\x14\x69\x30\x20\xca\x89\x13\x78\x33\x14\x7a\x0a\x7c\xa0\xec\x9f\x06\x39\x6d\xc0\x97\x7c\xc0\x3e\x96\xe2\x81\x33\xf8\x11\xcf\x9f\x06\xf9\x4f\xd1\x3c\x2a\x3c\xb4\x63\x02\x88\xb2\x2a\xb1\x84\xe0\x8c\x7c\xa3\x4c\x1e\xfd\xea\xdb\x6d\x74\xa6\x01\x5d\x44\x73\x9a\x17\xc1\x7c\x51\x5a\x44\x41\xe8\x05\xcd\x33\x92\x32\x96\x61\x64\x97\x55\xab\xb4\x2f\xa8\x33\x61\x34\x99\x44\xe3\x65\x0c\x2f\x80\xca\x30\xad\x81\xcc\x81\xa4\x45\x10\xbf\x68\x52\x81\x05\x89\xa5\x56\x73\xb1\x0a\x70\xcd\x5f\xcc\x25\xeb\x66\xbb\xb2\x5e\x54\xd0\x79\xd7\x7e\xfb\xe7\x18\x60\x02\x94\x7b\xd5\x6d\x2c\x6c\x9f\xd4\xc4\x0b\xd6\xad\xf0\x53\xae\xcb\xb9\xde\x69\xb4\xbc\x3f\x44\xd3\x84\x66\x24\x8e\x72\xfb\x95\xf2\x4a\x8b\x9a\x57\x93\xfb\xd7\x36\x71\x17\xb7\x80\x2f\x5f\xe3\x02\x40\x6b\x49\x3c\x73\x25\x61\xe4\x2c\xe1\xc4\x9a\xb9\xa9\x9f\x15\x81\x4d\xae\x11\x3d\x84\x9e\xcb\xe0\x0b\x68\x1a\xf8\x14\x20\x8d\x0b\xee\x43\x23\x26\x1b\xa7\x53\x2f\xe2\x31\x67\xf7\xa1\x3d\x4e\xa7\x5a\x5b\xea\x22\x1d\xea\x35\xf0\x8e\x2b\xc4\xe8\x46\xb7\x54\xd1\x84\x7d\x19\xbb\xba\xc2\x87\x95\xe1\x59\xe8\x76\xd1\x1d\x5c\xa7\xb3\x6d\x1b\x15\x37\xd8\xff\xbd\x95\x18\x4d\xc4\xe9\xd4\x53\xb5\x4c\x2d\xa9\x52\x15\x32\x8f\x58\x70\xf3\x56\xaf\x36\x38\x9f\x45\x39\xdb\x15\x17\x69\x5e\xdc\x40\x6f\xf0\x2e\xcd\xab\xc5\x42\x37\x62\x56\xe5\xee\xe9\x56\x8a\x27\x9a\x75\x12\x6f\x9d\xec\xbb\xbf\x08\x2e\xe1\x19\xcc\xae\xa1\x2b\xc4\x59\x02\xc9\x90\x54\x14\xb1\xf7\xd0\x2a\x33\x31\xec\x79\x9a\x7d\xfe\x98\xbe\xcb\xd2\x33\x5a\x5e\x06\x01\xe1\xb2\x0b\x21\xf2\x97\x17\x94\x10\x28\x10\xc4\x04\xc7\x09\x33\x0c\xdf\x39\xcf\xe0\x9d\xe4\x5e\x71\x30\x63\x47\xe9\x64\xd7\xf8\x7a\x46\x8e\xd1\xe7\x09\x19\x29\xab\x93\x6b\xdd\x2a\xbf\x32\xe1\xb7\x27\x71\x9c\x9e\xc3\x2b\x20\xa9\xdc\xa9\xaa\xbe\xfa\xd5\x0a\x8f\x74\xc9\x88\x89\xa4\x49\x7c\xc9\xc3\x77\x14\xc6\x63\x1a\xf9\xa0\x85\x3f\x5c\xf1\xbd\xc3\x92\xaf\x5a\xc8\xc8\x7e\x63\x85\xdf\xb3\xd8\xfa\x05\xd6\xc7\x46\xbc\x4b\x5d\xdf\x01\xfd\x0b\xa3\x62\x2f\x37\xab\xa3\xf4\x26\x1b\x47\x35\x61\x0b\xba\x06\xfc\xd2\x8b\x45\x94\x5d\x7a\x56\x3c\xca\xc5\xe4\x96\x73\x6f\x3f\x5e\x68\x96\x57\xb6\x04\x2c\x50\xcf\x02\x00\xca\xf6\x09\x74\x16\x44\x77\xc7\xb7\x2a\xdf\x07\xe7\x92\x64\x44\x8a\x17\x0c\x55\xbf\x97\x8f\xa3\xc8\x5e\xbe\xb2\x0c\xde\x46\xff\x9e\x0b\xc4\x29\x38\x1d\x2f\x47\xaf\x0a\xdd\x00\xb8\xbf\x86\x98\x75\x3e\xe6\x30\x18\xac\xb2\x22\x60\x6d\xe2\xd5\x58\xba\x18\xf5\x72\xbb\xc5\x4a\xb2\xee\x42\x38\x8a\x9a\xd1\xbf\x62\xaa\xb6\x5a\xd2\x17\xdc\x06\xdf\x65\x89\xa4\x7e\xbe\x3c\xe5\x0f\x03\x3b\xc3\xde\x36\x5f\x95\xad\x8b\x70\xdc\x32\x9c\x3c\x29\x2f\x52\xad\xe1\x45\x8b\x6c\x10\xb7\xf0\xb6\x71\xc4\x80\x5e\xf1\x6b\xdd\x84\x9e\xc3\x0d\x6f\xc7\x8c\xad\x0e\x17\x61\xa7\x41\xd2\x8f\xf2\x7f\x04\x71\x14\x76\x20\xf6\x89\x48\x79\x11\x65\x74\x5c\x74\x7c\xb7\x60\xc2\xc5\x1c\x00\x8a\x1a\x3b\x5d\xe7\x8a\x0d\x0b\x4e\x3a\x24\x95\xec\x81\xa7\x5a\xc3\x8b\xa1\xa7\xa2\x06\x55\x88\x9e\x99\x35\x71\x05\x90\x6d\x53\x24\xfc\xcc\x4b\xd8\xb6\x8c\x11\xaf\x39\xc9\x87\xcb\x64\x1c\x25\x7e\x71\x48\x38\x76\x47\x73\xb9\x6e\x26\x11\xd7\xcf\x95\x21\x84\x83\x57\x2c\x30\x4a\x8d\x92\x29\x08\xbb\x5e\x05\x82\x0b\x66\xfa\x16\x13\x6e\xbe\x6a\x2a\xc0\x50\x66\xf9\x59\x34\x9d\xd1\xbc\xae\x3c\x86\x42\xb4\x23\x72\x3f\x27\xe9\x79\xf2\xa1\x08\x0a\xea\xf3\x33\x89\x72\xcb\x1b\xc0\x55\xec\xd8\x35\x2c\x96\x71\x4c\xc3\xba\x2a\x30\x54\x89\x4e\x43\xbb\x1b\x2b\x89\x28\x51\x77\xbd\x3e\xaa\x85\xe8\xe9\x7a\x2a\x2a\xa8\x29\xe9\xbb\x20\x1e\x95\x67\xa1\x92\xc6\xdd\xe7\xc8\x93\x86\x60\x7d\x67\xc7\x51\x79\x16\x2a\x69\xb3\xb9\x91\x3f\x19\x95\x30\x36\xe5\x91\x27\x8d\xc3\x96\x19\x72\x8c\x4a\x73\x70\x39\xff\x80\xca\xf3\x4a\xca\xda\xfa\x52\x4f\x15\x36\x88\xd1\x7b\xe3\x28\x3c\xf2\xa6\x3a\xf0\xf6\x41\x77\x54\x95\x89\x4b\xe3\xe3\xda\xc8\x93\x86\x61\xad\x49\xf0\x24\x62\x68\x9b\xfb\x8d\x4a\xd2\x39\xd7\x34\x6c\x07\xf9\xf5\x61\x6b\xb4\xf9\xb4\xcc\x23\x16\xdb\x3a\x5a\xa3\xed\xed\xeb\x93\xde\xf6\xe6\xbd\x37\x95\x7b\xe3\xc1\xff\x1a\xe3\x41\x41\xe9\x77\x11\x16\x69\xb5\x18\x12\x0d\x2d\x06\x79\xd4\x26\xd3\x14\x90\xa7\x7d\x85\x60\x14\xcd\xc3\x47\x04\x71\x3c\xb0\x02\xac\xc2\xbb\x70\x3b\x3c\x93\x1b\x54\x42\x3e\x6e\x70\x23\xd1\x55\x04\x93\xf0\x85\xa2\xfb\xc4\xb7\x46\x11\xec\x00\xc7\x60\x5e\x3d\x10\x81\xae\x54\xec\x2d\xb8\x56\x9e\x74\xbb\x6a\x21\x7e\x63\x00\xe7\x55\xa8\x53\x7e\x63\x18\x19\xa3\x59\x80\x88\x4f\x0c\x71\x27\x81\x30\xd8\xfe\x60\x4f\x86\xe1\x32\x15\xac\x3e\xf4\x53\x42\x7c\x64\xca\xa6\xc6\x79\xe9\x06\x91\xc7\xe5\xd9\x42\x87\x69\x04\x7f\x24\xc0\xeb\xf9\xe3\xb7\x6c\x9a\xf3\x68\x17\xeb\x42\x68\x6c\xd6\x61\x2c\x04\x56\x76\x1a\x77\xef\x07\x87\x94\x64\x0e\x0e\x3a\x29\x5e\xd9\xba\x83\xf3\x8f\xcd\xf6\xa1\x51\x21\x9e\x76\x34\x1e\x1a\x22\xa2\x2a\xb4\x24\x0e\x86\xed\x8b\x9f\x16\xe5\x64\x9c\x66\x99\xeb\xda\x14\x4e\x5e\x41\x41\xf7\xb2\x69\xee\x8b\x36\xa9\xc3\xdd\x3f\x24\x7f\x83\x93\x5b\x4e\xbe\xc0\xb9\xed\x9a\xb5\x17\x15\xe2\x6d\x91\xe1\xfd\xd4\x33\x55\xb8\x9d\xd2\x39\xd2\xa7\x77\x0e\x05\x28\x72\x2c\x7d\x02\x8d\xf8\xc1\x40\x3e\x22\x03\x4d\x97\xe1\x56\x08\x36\x4f\x70\x66\xa9\xa3\xc8\xb1\xad\x36\x80\x27\xa8\x59\x70\x29\x1f\x54\x8a\xb9\x5b\xef\x38\x41\x48\x83\xae\x72\x8d\xcf\xce\xe3\xce\x05\x90\x75\xc7\x21\xc0\xb9\x9b\xeb\x4a\x78\x7d\xe5\x65\x94\xb1\x0a\x58\xaf\xb9\x41\x47\x20\xb1\x23\x09\x71\x7d\x77\xb7\x8c\x90\xcd\x17\x74\xec\xcc\x2d\xe2\x00\x56\x44\xeb\xeb\x38\x8e\x2d\xaa\x5c\x41\x4b\x6d\x13\x58\xa0\x61\x52\x31\x82\x99\xa4\xef\x38\x98\x87\xbc\x9c\x4d\x43\xbb\x80\x2f\x71\x0b\x1d\xc4\xaa\x55\x6d\x90\x57\x49\x79\xaa\xfd\x4a\xb2\x33\xc2\xdf\xae\xce\x30\x56\xe5\x17\x66\xd4\xda\x92\xb0\xb8\xd7\x9a\x9b\xe3\xe5\xd3\xf1\xc4\xa8\x2d\x52\x7f\x04\x0a\x23\x86\xed\x2e\x29\x89\x2e\xe1\x0b\x52\x20\xde\x4f\xa1\xe1\x1a\xc1\x71\x2b\x2c\xdb\x4a\xa2\x27\x49\xd4\xdf\x2c\x4a\x8c\xb7\x78\xe5\xbc\xdf\x28\x56\x8c\x70\x73\x3f\xec\x91\xa7\x52\x0b\x55\xd1\xc4\x32\x59\x04\xe3\xcf\xfc\xae\xd1\xb4\xf1\x84\x24\x43\x27\x65\x26\xe9\x2e\x18\xfa\x91\x54\x56\xc5\x7f\x28\xd2\xdb\x25\x5b\xe4\x99\x4c\x94\x9e\xf8\x89\x3c\x07\x6a\xd7\x14\xca\x7f\x7e\x99\x23\x7e\x2c\xe4\xf4\x44\x71\x73\x46\x85\x0e\x07\xbb\x11\x57\x31\x18\x8f\x87\x27\x64\xe4\x73\x16\xbf\x0f\x21\xc8\x03\x14\xf5\x5d\x22\xcb\x8e\x2b\x1f\xc4\x31\x5e\xdc\xfd\x7e\x5f\xae\xef\x7d\xbb\xac\xb5\xf9\x38\x6e\x9a\x0e\xf8\x76\x07\xe1\xa5\x25\x28\xdb\x8d\x02\x55\x43\x8f\x7b\xe4\xb1\x2b\xe6\x3e\x09\xe1\x0d\xac\x3c\x74\x05\xc6\x2b\xc5\x20\x09\x4d\x5f\x3e\x12\x8c\xc7\x5f\xe7\x27\x23\x56\x07\x0f\x5e\xc9\xc0\x05\xda\xbc\xb4\x2b\x66\x15\xe2\x47\xd7\x51\x2d\xf4\xaa\x2c\x46\xf7\x2a\x01\xb8\xfd\xfb\xa6\x94\xc1\x2c\x5b\x58\xb5\xc7\xc0\x41\x46\xcb\x7f\xc2\x75\xb7\x21\x16\x62\xf6\x03\xee\xc7\x4d\xe9\x0b\x17\xc1\xe2\x8f\x5d\x4c\xdf\x54\x70\x97\xe1\x92\x4b\x4b\x38\x6d\xf4\xb1\xee\x7b\x12\xae\x75\xc3\x8a\xf1\xd1\x62\xc6\x91\x20\xaa\xee\x19\x5d\x73\x1f\x80\x42\x29\xbc\x84\x3b\xc6\x7a\x40\x4e\xef\x9d\x27\xdb\x4d\x1a\xec\xb9\x8e\x96\x5c\x1e\x80\xdc\x2c\xc9\x77\x3e\x86\x33\x8e\x1e\x37\xdd\xd9\x31\x9d\x5b\xf3\x4e\xd3\xd0\x71\xe4\x5f\x64\x97\xd6\xfb\x55\x04\x0a\x4f\x56\xcb\xc7\x4b\x8c\x37\xb6\x63\x70\x72\xd0\x71\x5c\x25\x71\x8a\xdf\x25\x14\x17\x42\xa5\xcc\xce\xcb\xd6\x91\x24\x53\xb9\x51\x34\x39\x57\xda\xdb\x86\x59\xa4\x76\x57\xb0\x5a\xf8\x53\x2d\xb5\xda\x35\x23\x49\x4a\x00\x0a\x43\xdb\x1f\xc8\x10\x0e\x35\xc6\x59\xd3\x95\x0e\x71\xe4\xda\x20\xe1\xfe\x02\x92\x50\xf8\x14\x85\xd0\xc3\xc9\x23\x79\x50\x75\x62\x30\xd7\x2c\x57\x23\xee\x20\x5b\x37\xd6\x3c\x74\xcc\xeb\x49\x51\x5d\x2d\x78\xf3\x80\x0f\x34\x2f\xa2\x79\x50\xd0\x1f\x03\x50\x20\xd6\x51\x15\x02\xaf\xa3\x28\x5c\xf3\x5d\x50\xd3\xd7\xa7\x8e\x66\x33\x84\xc6\x55\x37\x3b\x1e\xd0\xb2\x99\x79\x2f\x9b\xa1\x32\x84\x1d\x84\xe2\x91\xba\x40\x21\x1f\xe0\xa9\x98\xd2\xe2\x85\x1d\x62\x4a\xee\xac\x76\x35\x75\x73\x25\xea\xba\xe3\x79\x6a\x84\x78\x79\x55\x2d\x56\x26\x8f\xce\xd3\x5c\x6a\xbe\x45\x20\x4c\x5c\x54\xe2\x19\x91\x7d\x25\xc2\x7e\xdb\xa8\x98\xaa\xfe\x1b\x05\xc6\x54\x85\x56\x1d\xe4\xd7\x8c\x92\xa9\x75\x34\x6c\x80\xd9\x62\x2c\xdd\xaf\xe5\xfc\xd4\x5c\xc7\x88\x04\x74\xb9\xb9\x4d\xc5\xb8\x44\xd9\x3f\x36\x57\x22\x46\xb4\x20\x09\x86\xc5\x14\x23\xe8\x0d\x9e\x13\xd7\x5f\xa1\xa5\x71\x7d\x06\x4e\x90\x3f\xb1\x1e\xb7\xc9\x88\x7f\x58\x3b\x49\xbb\xe7\x08\x2f\x23\xed\x27\x50\xe5\x29\x0f\x87\x62\x38\x27\x3a\x8b\x77\x5c\xfa\xcf\xe5\x0c\xb2\x96\x18\x64\x78\x9f\xb2\xed\x47\x05\xce\xaa\xde\x7a\x3c\x31\xae\xf0\x04\x17\x86\xa0\xb3\x6e\x62\x47\x9b\x19\xc1\x36\x5f\x60\x19\x4a\xfa\x88\xd1\x69\x65\x5b\x85\x85\xce\x7e\xb0\x58\xc4\x97\xc2\x63\x55\x23\xc2\xea\xda\xf6\x79\x7c\x0b\xb0\x9a\x61\x89\x37\xaa\xbb\x66\x1e\x44\x1c\x28\xcd\x78\x74\x28\xa8\x5b\xc7\x80\xf2\x4c\xd8\xd7\x0a\x03\x25\xd3\xf5\x8a\xc7\x2e\xbb\x4a\xc1\xc5\x61\x53\x63\xb8\x0c\xd0\x95\x9a\xbd\x93\x5f\x56\xdc\x14\x91\xf8\x48\x74\x52\x69\x31\xbd\x5b\x4b\x57\x53\xec\xf3\x4f\x19\x03\x4b\x96\x05\x02\x8f\xb2\xf1\x32\x0e\xb2\xf5\xf5\xf5\xf5\xea\xc8\x57\x92\x82\x76\xee\x24\xf6\x15\xd7\xfe\xb6\x46\x5b\x4f\xfc\x8e\x84\xb6\xee\x6f\xff\xef\x6f\xff\xff\xda\xb7\xff\xe2\xea\x9f\xc1\xca\xd8\x64\xfe\x88\x2a\xbf\x5b\xac\x14\x9f\x65\x41\xb5\x21\xc0\xda\x60\x00\xb1\xd7\x82\x8c\x91\x32\xdb\xc1\x96\xb9\x39\x44\x46\x70\x61\x34\x99\xd0\x8c\x26\x05\xa1\xc9\x59\x0e\x85\x4e\xb3\xf4\x3c\xa7\xd9\x1a\x72\x2c\x7b\x1e\x25\x61\x7a\x0e\x1a\x0b\x14\x71\x84\x3c\x78\x20\x72\xfa\xff\x7c\xf3\xd3\xeb\xa2\x58\x08\x9f\xc5\x9c\x6b\x9a\x69\x64\xd7\x0f\x0b\xac\x4f\x04\xcc\x88\xa6\x49\xca\x18\x41\x1c\x25\x94\xf5\x24\x49\x43\xba\x86\xbc\xd4\x39\x35\xaa\x81\x5f\xcc\x63\x36\x32\xb1\xb1\xb5\xbb\x4d\x1b\xb9\xe6\x98\xfc\xe7\xeb\xf7\x5b\x46\x75\xb3\x6c\xab\xdd\x2d\x2d\x25\x25\x07\xd6\xc2\x3b\x89\x4c\xd7\x24\x02\xe4\x27\x26\xda\x83\x9b\x56\xee\xd4\x9d\xf5\x52\x19\x40\x18\xe5\xf1\x96\x3f\x4b\xf3\xa2\x47\x8a\x68\x4e\xd3\x65\xd1\x63\x15\x66\x3d\x50\x32\x9f\xa7\x99\x78\xec\x08\x9b\x09\x83\x23\xbb\x04\xfe\xbb\xba\x22\x6d\x41\xec\x71\x3a\x0e\x62\x96\x38\x7a\xfa\xcd\xe3\x6f\x20\xc0\x31\xdf\x7b\x78\x85\x6c\x27\x14\xbf\xae\xae\xc8\x50\x65\xb3\x66\xc8\x2e\xb4\xa6\xd2\x64\xa3\x64\x57\xb5\x5f\x2b\x3c\x2d\x32\xba\x80\x88\x81\xf4\xdc\x9a\x32\x4b\x76\x12\x80\xef\xd1\x59\x46\x48\x4e\xcf\xd3\x34\xa6\x41\x72\x0d\x77\xac\x6c\x7f\x96\x12\x8c\xc6\xb2\x70\x0f\x8a\x0e\x7c\x66\x5b\x86\x0f\x2a\x8c\x69\x24\x77\x99\x1d\x30\x2f\x02\x59\xf5\x1c\xd5\xfc\x06\x85\x13\xd2\x9a\x78\xc5\x86\xb2\x09\xd1\xe2\x15\x0c\xf9\xf5\xfb\x2d\x1d\x5f\x98\x4b\x5a\x08\xf3\x68\x22\xe0\xc9\x19\x76\xc2\x68\x55\x64\x8c\xa7\x23\x5e\xa8\xad\xe9\x5a\xd3\x05\x4d\x3a\xed\x77\x87\x1f\x3e\xca\x90\xa8\x9c\x70\x78\xe7\x76\xd6\x90\x47\x47\x98\xdb\x07\x0f\xcc\x49\x35\x0e\x7d\x4b\x30\xa8\x69\x3f\x0f\xf2\x68\x4c\xda\x64\x03\xba\xf0\x7c\xc9\xd8\x03\xaa\x62\x83\xb4\x47\xea\xaa\x50\xd5\xd3\x2f\x52\xf1\xf8\xae\x7d\x1a\xe4\xf4\xc9\xe3\xb6\x35\x7e\xed\xcf\xfc\x35\x0d\x42\x9a\x75\xda\x7b\xc0\x57\xa3\x5f\x03\x7e\xda\x82\xf6\xf9\x08\x2b\x0a\x31\xf9\x98\x26\xc5\x23\x76\xd0\x6e\xf7\x48\x9b\x49\xfe\xd1\x18\xaa\x18\xfc\x92\x4b\xb5\xa3\xba\xb1\x12\x53\x56\x43\xae\x3c\xf2\xcd\x65\x32\x46\x87\x6a\x5b\x93\xec\xbb\x78\x5e\xa0\xeb\x6b\x7f\x8c\xf3\x2a\xd2\xcb\xed\x98\x97\x52\x97\x66\x93\x9c\xa4\x19\x93\x56\x45\xd0\x6c\xa0\x47\xad\xdd\xd7\x98\x4b\xc2\x0e\xbc\xf4\xe0\xef\x0e\xa2\xc9\xa5\xaa\x5f\x20\x59\x2a\xf2\xb1\xbb\x72\x9f\x35\xc0\x7e\x9a\x24\x54\xbc\xc7\x90\x14\xa6\x29\xd1\xb8\x5c\x94\xad\xcb\xc0\x21\x1f\xe9\x45\xe1\x74\x50\xc0\xa2\x67\x28\xc2\x2a\xdf\xec\x56\x55\x97\xde\x8b\xfa\x3b\xbe\x06\xf1\x2a\x69\x1e\xc3\x1a\x68\x20\xa8\x21\x82\x3d\xc5\x71\x2a\x28\x41\x64\xbd\x72\xa2\xc6\x90\x22\x8b\xa6\x53\x9a\xf1\x50\x57\x6c\xf6\x41\x6c\x51\x7e\x6b\x19\x0e\xea\x08\x06\x7a\xe0\xa3\x1a\x33\x62\x75\x13\xfa\x01\xe3\x95\x1d\x83\x9b\x24\xe0\x63\x3c\x2f\x82\x82\x8e\x67\x41\x32\xf5\x2b\x10\xf8\xb3\x02\x89\xf8\x20\xbc\x04\x83\x7e\xb8\x11\x7e\xcc\x38\x8c\xcd\xf2\xd6\xcd\x88\xd3\x0d\x28\x46\x03\xca\x5b\x25\x14\xca\xcc\xbe\xcc\xaa\xa1\x28\x38\x93\x79\x6f\xad\xd4\x8d\xd5\x8a\xb4\x45\xf0\xd5\x96\x7d\xb1\x65\xb4\xcc\xce\x82\xd7\x16\x8a\xf5\x46\xe0\x62\xd6\xac\x2c\xef\xeb\xa5\xf7\x91\x97\xea\xe0\xcd\x43\x2c\xe4\xbb\xe5\x00\x76\x17\xaa\x98\x80\x58\x69\x78\x5d\xe9\xcb\xf2\xf8\x92\xd1\x3b\x7f\x34\x0b\x8b\x8b\x51\x75\xc9\xda\x8a\x72\x51\x3f\x35\x99\xa9\x12\x02\xa4\x82\xd3\x16\x06\xd8\xf9\x21\x69\x17\x64\x12\x44\x31\x0d\xfb\xe4\x90\x9d\xd3\xce\x23\x76\xf6\x08\x20\x3a\x5d\xf9\x6a\x42\x6d\x7a\xe6\x42\xe3\x53\xe9\x33\x54\x14\x94\x28\x1c\x91\xef\xd4\x9f\xd4\xf7\xb1\xdd\x27\x5b\x8c\x47\xa4\xbd\xd5\x1f\x2a\xe5\xa1\xd4\x3f\xb6\x13\x5a\x7c\x8a\xa3\xbc\xa0\x09\xf8\x97\x5c\xb3\xb4\x87\x27\x86\x41\x97\x54\x70\x65\x3c\xd4\x9e\x4b\xbe\xd2\xaa\x90\x0d\x52\x4f\x82\xa3\x2e\xc0\x43\x97\xaa\x02\xe3\xb4\xcf\xc4\xdc\xd6\xe8\x29\xfb\x65\xc8\xcf\xad\xd1\xe6\xb7\xec\xe4\xbf\x7d\x7f\xf2\xbf\x3f\xf9\xff\xc5\x4f\xfe\xda\xf0\x1f\x1e\x4b\xde\x91\xd1\xbf\x32\xe4\xc4\xa7\xca\xd3\x68\xca\x6d\x70\xfb\xbf\xf0\x13\x3a\xbf\x07\x09\x7f\xa2\x13\x73\x43\x50\x31\x47\x2f\xd1\x83\x3d\x63\xe3\xe4\x10\x9c\x5d\x9c\xcf\x58\xef\x3b\xa6\x81\xd6\xf7\xbc\x30\x79\x48\xb6\xdc\x17\x7f\x60\xf1\xc7\xa4\x78\xf3\xdd\x23\xf1\xbf\xc4\x13\xcc\xfd\x9d\x38\xd5\x05\x09\x39\x78\xbe\xf7\x56\x4c\x72\x48\xbe\xfb\x96\x8c\xd3\xf9\x62\x29\xe2\xfd\x9c\x5e\x92\x79\x7a\x16\x25\x53\x14\xd5\xee\x31\x19\xcf\x82\x0c\xf6\x02\x7e\x33\x1b\x72\x53\x2a\x69\xae\x2e\xa1\x63\xca\x1f\x2d\x14\x29\x6b\x90\xe3\x2a\x27\x9d\x3d\xb2\x4b\x36\x87\x3d\xf2\x9c\xfd\xbf\xd9\x23\xfd\x7e\xbf\x47\xfe\x8f\xec\x92\xed\x6f\xba\xec\xb0\x43\xf2\x05\x1d\x47\x93\x88\x2f\xa4\x83\x0f\x87\x9b\xdb\x4f\x36\x9f\xd8\x26\x66\x51\x9e\x42\xba\x18\x87\xeb\xdd\xf8\x9a\xbf\xc5\x65\x1d\x61\x03\x34\xaf\xd6\xf0\xcd\xb2\x90\xa4\x42\x09\x26\x7c\x36\x98\xf5\x1b\x13\xca\x2a\xc6\xf3\xc8\x46\xd4\xde\x6b\xf7\x19\x5a\xf6\xd3\x90\xee\x15\x9d\x21\xd2\x5a\xb3\xb1\xb5\xff\xcf\xc9\xe6\x0c\x90\xbf\x17\x06\x62\x2d\xd2\xa3\xc5\x82\x66\xfb\x41\xae\x55\xd9\x28\x9b\x3f\x3b\xee\x3c\xee\xca\x97\xc0\x22\x61\xd8\x7b\x6c\xdd\x98\xf1\xdc\x45\x1c\x15\x9d\x76\xbb\x6b\xbe\xc2\x4e\xba\xa6\x75\xd5\x38\x0d\xd9\xe0\x12\x5f\xe7\xa5\x7c\x08\x30\x3f\xec\x92\x3d\x26\x10\xc2\xc7\xf7\xbb\xe4\xff\xba\x4e\x2c\x0a\xcf\xcc\x8a\x89\x35\x20\x95\xab\xe3\x90\x92\x47\x64\x8f\x6c\x90\xcd\x21\xb2\x33\xf2\xc5\x67\x90\x31\x70\x6d\x1b\xa6\xeb\x6e\xff\x97\x34\x4a\xd8\x30\x6d\x4b\xc5\xf1\x12\x7c\xef\xc2\x14\xbf\x39\x7c\xc1\x08\x7b\x73\x28\x99\x92\xb0\xf0\x03\xca\xf7\x50\xdc\xb7\xc3\x27\x8f\x6d\x82\x9b\xa7\xe1\x77\xdf\x6e\x0e\xcb\x08\xcd\xa4\x2f\xed\x4f\x9b\x53\x93\x28\x5c\x49\x45\x19\x9d\x07\x51\xc2\x75\x47\x2c\x4f\xdf\x3d\x0a\xd7\x41\x26\x7b\x10\xc0\xda\x6e\x79\xab\x6b\x39\x45\x02\x66\x25\xc1\x94\xc5\xeb\x77\x86\x89\x9c\x6e\x12\x64\xed\x83\xa4\xe0\x3e\x7c\x7a\x64\x73\xd8\x25\xff\x7f\x86\xb5\x0d\xa7\x16\xee\x72\x49\x98\x9f\xfb\x5e\xfe\xaa\xba\x54\x49\x5d\x9f\x31\x4f\xf5\xef\x90\xb8\x09\x3a\xac\x03\x61\xf0\x0f\x17\xea\x90\x20\xde\x3a\x08\xf6\x29\xe7\xcb\x3f\x39\x03\xec\x75\xdd\x3f\x09\xc2\x12\x5a\x2f\x39\xb7\xab\x4e\xb4\xe3\xba\x7e\x52\x08\x86\xb5\x9c\xcb\xd7\x39\x16\x51\x31\x98\x3d\x95\xe3\xf4\x3d\x40\x59\x52\x8c\x66\x43\xb8\x56\x6c\x0d\x6b\xc5\x58\x4e\x1f\xd5\x58\xe5\x0c\x01\x74\x44\xf9\x73\xe9\xab\x00\xbd\x54\x10\x51\x69\xc9\xe6\x13\xc4\xc2\x4e\x83\x9c\x6e\x3f\x21\xbb\x50\x46\xab\x87\xb6\x9f\x18\x26\x00\x61\x48\xb9\x66\x11\xf6\xc0\x0e\x2f\xd4\x23\x9b\xdf\x98\x92\xb0\xea\xe7\xf3\xd3\x20\xe9\xf0\x62\x26\xf3\xb3\x16\xb3\xf0\xb7\x82\x16\xee\x73\x36\xf4\x22\x35\x76\x2f\x36\x7d\x04\x1c\xe7\x66\x97\x72\x45\x73\x65\x12\xd8\xeb\xbe\xe3\x31\x49\x92\xb4\x10\x42\xd9\xf7\xd1\x0f\xad\x29\x48\x24\xdc\x8f\xcf\x44\x23\x35\x9f\x05\x5c\x5a\x83\xfd\xed\x62\x1c\x2f\xf3\xe8\x4c\x85\x50\x8d\x4e\xa3\x38\x2a\x94\x80\x73\x1a\x24\x9f\x07\xa7\x59\x90\x8c\x67\x24\xa7\xd9\x59\x34\x96\x1b\x60\xc0\xfd\xf8\xb6\xbe\x1f\x44\x3f\xf4\x6d\x1a\x52\xe1\x4c\x72\xb9\x0b\x4d\x68\xc6\xb6\xa1\x20\x9e\xa6\x59\x54\xcc\xe6\x24\xa4\xf9\x38\x8b\x4e\x39\x5b\x12\xf2\x0f\x4d\xfa\xe7\xd1\xe7\x68\x41\xc3\x28\x00\x21\x88\x7d\x0d\x0e\x92\x82\x66\x49\xc0\x9f\x4e\x7c\x7a\x1e\x24\x9f\x3f\x09\x27\xc2\x9f\xf8\xbc\xfe\xff\x7e\x14\x23\x4d\xa6\x9f\xd8\x10\x3f\xc1\x5b\xa2\x4f\x61\x34\x8d\x9c\xa7\x1c\x72\x6a\x7c\x14\x79\x2a\xf7\x54\x39\x03\xd2\x19\x4e\x91\x7a\xb6\xd9\x06\xb4\xfa\xdc\x5e\x91\xa7\x16\x5b\x14\x33\xba\xcf\xf7\xa9\xf6\x3f\x5f\xb6\x77\xd6\xbc\x3c\x53\xf0\xd8\x8e\xb5\x73\x77\x70\x05\x1b\xa4\x3d\x04\x51\x09\x5a\xc1\xe6\x2e\x0c\x1d\x2f\x18\x36\xc8\x2e\xe9\x70\x71\xaa\xf3\xdd\x53\xf2\x48\x37\xd1\x95\xcf\x06\x1e\x6d\x59\xfb\xad\xf2\xf6\x61\x36\x85\xea\x14\x0d\xd6\xa8\xad\x04\x13\x41\xb8\x02\xc2\xe6\x81\xec\xa3\x24\x2f\xa2\x62\x59\x48\x57\xd8\x51\x48\x93\x82\x6d\x5a\x76\x00\x08\x5e\xcb\x41\x12\x46\x19\x35\x0d\x18\xcc\x37\x36\x79\x4f\xca\xb2\xea\x91\x0d\xbc\x9a\x6a\xa1\x96\x5a\xd0\x54\x4b\xb7\xd5\x5a\x85\x17\x99\x3d\xf1\xba\xc7\x36\x8f\xc0\x26\x67\x68\xbf\xfc\xf8\x9a\xcd\x83\x7c\xdd\x82\x31\x80\x52\x55\xdf\xba\x16\xbf\x4e\xab\xf8\xb5\x7c\x4a\xc7\x91\x2b\xa2\xc4\x47\x39\x7f\x29\x87\xf9\xb8\x23\x77\x82\xe7\x96\x52\x79\x53\xed\x45\x1e\xc5\x87\x54\x78\xf0\xe7\x74\xbc\x25\x25\x74\x1e\x20\xbf\x30\x95\x72\x42\x84\xfd\xcb\x44\x9c\xac\xb0\xf0\xa7\x9d\xcb\xd4\xea\xca\x15\x16\xa0\xeb\xa5\xaf\x07\xf1\x98\x75\x74\x10\xef\xa8\x7a\x24\xf5\x68\x6d\x60\x6c\x58\x5b\xe3\x8e\xd2\xa2\x84\xc1\x7f\xfe\xf9\xf2\x78\xf8\xe8\xbb\x93\x2f\x5b\xd7\x9d\x97\x1f\x5f\xb3\xdf\x7b\x8f\xfe\xef\xe4\xcb\xe6\xf6\xf5\x95\xfa\xd8\x1e\xf6\xb6\x37\xaf\xbb\xff\x33\xe8\x17\xa0\x04\x55\x1b\xb8\xf1\x2e\xaf\x8c\x31\x20\x70\xfe\x3c\x6f\x73\x45\x84\x89\x27\x98\x70\xfa\xf7\xa2\xed\x85\x5e\x82\x77\x83\xb7\x17\xee\x4a\xb2\x10\xa7\x07\x85\x1f\xf7\x6c\x3f\x26\x57\x57\x65\x79\xdf\xdc\x70\xd8\x13\x12\x25\x25\x03\x37\xb8\xcf\xdd\x0c\xdd\xcb\x46\x1a\x0d\x7e\x6b\xd8\xc8\x6a\x93\x8b\x94\x6c\xa4\xf9\x72\xce\x00\x8f\x72\x71\x7c\x98\xa7\xe1\xa3\xef\xbe\x7d\xb4\x39\x54\xd9\x70\xc6\x85\xde\x8d\xd3\x98\x74\x0e\x3e\x1c\x0e\x0e\x5e\xee\x13\x76\x6e\x18\x6d\x0d\x87\xdb\x5d\x9b\x27\xa3\x6a\xdd\x53\x28\xca\x75\x06\x2e\xf3\x1a\x0e\x5b\x9c\x09\xb7\x7a\x64\xab\x99\xad\x2a\x66\xaa\xc6\x96\x42\xe8\xb4\x4f\xfe\xf9\xfe\xe5\x8f\x8e\x87\x44\x55\xc0\x3f\x9a\xd2\x1a\xdd\x49\x45\x90\x75\xc3\xd3\x04\xd0\x01\xf7\x79\xce\x90\xbf\xed\x91\xc7\x5d\x32\x22\xed\x76\xa3\x71\x8f\xe3\x08\x1e\x92\xa9\x0e\x82\xf2\x29\x4a\xec\xf1\x31\x2c\xfc\xb8\xf7\x8f\xc3\x57\xff\x3a\x7c\xff\xbf\xf6\xac\x42\x1d\x25\x73\x6a\xd7\xef\x9d\x5c\x0e\x74\xeb\xb1\x6f\x6e\xae\x3e\x72\xb1\x9a\xfc\xe7\x12\xf7\xe0\xe1\x0e\xcd\xa9\xc0\x19\x5e\xe0\x39\x87\xe0\x7b\x27\x31\x38\x9f\xe3\x33\xe3\xd0\xe1\x0e\xf8\x31\x3a\xc4\x96\x1e\x65\xe4\xf9\x43\x9d\x52\x8c\x13\x2a\x3f\xa3\x98\xe7\x99\xcd\x27\xdd\x1e\xd9\x1a\x2a\xd7\x6a\x86\x94\x27\xd1\x6b\x0d\x52\x16\x6e\xb6\x40\x4b\xbc\x61\x1d\x40\x16\x57\xea\x63\xbd\x62\x6b\x64\x7e\x5e\x9f\xf4\xb6\x1f\xdf\xab\xf1\xef\xd5\xf8\x7f\x71\x35\xbe\x50\xe1\x2f\xc6\xd5\xf6\x7b\xb7\xb0\xb8\x6b\xe9\x50\x99\xad\x9d\x95\x42\xfc\xd5\xd8\xe9\x71\x3d\xd3\x62\xec\xb5\x04\x5b\x04\xc5\xac\x47\x12\x6a\x58\x7f\x7f\x02\xcd\x85\xf3\xf0\x54\x5e\x55\xe3\x20\xe3\xd2\x6b\x81\xb0\xd7\x01\x1b\x1f\xf6\x1f\x4f\xd5\x59\x63\x75\xc3\x0b\x5c\xb1\x90\x09\x9d\x2f\x0c\x7a\xa4\xcb\x2b\x1f\x9b\x56\xb1\x7e\x9a\x74\xda\x30\xaa\x36\x0e\xca\xdb\x35\xec\xa7\xf3\x94\x31\x31\xfe\x96\xf0\xe0\xdd\x3e\xd1\xf7\xca\xfc\x85\x61\xbb\x47\x28\x62\xbd\x9f\x38\x1b\x14\x17\xde\x1d\xdb\xcb\xa7\xb7\x07\x49\x88\xdb\x47\xcd\x97\x56\x46\xd6\xd4\x1b\x83\x9f\x0e\x3e\x7c\x7c\xf9\x16\x56\xd0\xfe\xe1\xdb\xb7\x2f\xf7\x3f\x1e\x1c\xbe\x25\xef\x5f\x7e\x78\x77\xf8\xf6\xc3\xcb\x0f\xa5\xad\x86\x41\x11\xe0\x66\xd9\x37\xde\x9c\x06\x0f\x85\x19\xe1\x3c\xb8\x18\xa7\xf3\x45\x4c\x2f\xa2\xe2\x72\x44\x9e\x00\x65\x59\x3d\x04\x5d\xa8\xb2\x43\x60\x55\xe9\xfd\xa6\xeb\x89\x4b\x24\x6c\x0e\xbe\x98\x01\xd5\xe1\xe0\x17\xda\xb6\x13\xa2\x3b\x3c\xd0\x3c\xf0\x97\x90\x9c\xcf\xa2\xf1\x8c\xcc\x83\x62\x3c\x13\xe2\x2b\xdf\x84\x18\x43\x0b\x8d\x72\x9e\xb0\x18\xd0\xb4\x3f\xe2\x3a\x5c\x47\x39\xbd\x05\x0b\x04\x17\x5c\x54\xff\xc9\x4f\xc8\xc7\xf0\x36\x2e\x0a\x4f\x5c\x67\xfb\xaa\x30\x1b\xab\x00\xdb\x71\xa0\xec\xd8\xf5\xa5\x31\x9d\xa1\x1a\xd1\x77\xbb\xa2\x6b\x07\x8b\x93\x28\xa3\x86\x47\x00\x1b\x5d\x65\xe3\x61\x43\xf1\xb4\x5e\x01\xae\x03\x4c\x63\xd3\x16\xfd\x17\xd2\x98\x16\xb4\xaa\x06\x7b\x30\x36\x6e\xf0\x2b\xec\x9f\xd9\xae\x05\x84\x28\x08\x82\xd7\x07\xca\x1d\x6e\x2b\x95\x70\x67\x39\x24\xe5\x3e\xa4\xa3\xa2\xbf\xb6\x26\x85\x41\x93\x84\xd7\x6c\xb5\x07\xbc\xc8\x64\xc2\x9f\xe6\x79\x48\x3c\x32\x0b\x63\x8f\xae\x78\x55\xd9\x6c\xb0\x67\xc9\x6b\xff\xe0\x2e\xdb\xb5\xe7\x61\xb9\xc4\x5f\xbc\x7c\xb4\xff\xfa\xe8\xed\xff\xbe\x7c\xaf\xea\x09\xe9\x78\xb6\x4c\x3e\xd3\x50\xbc\x2a\xe1\x2f\x46\xc5\x5f\x3f\xa3\x8b\x38\x18\xd3\xce\xe0\xdf\xd7\xc7\xff\x4e\xfe\x9d\x9d\x3c\xfb\xf7\x97\xc1\xb4\xd7\xbe\xbe\x7a\xf4\xe8\xea\x4b\xbb\x0b\x3e\x93\xbf\x78\xe1\xff\x7d\x22\x4b\x1c\x8b\x32\x27\xac\xd0\xb1\x2c\x75\x72\xec\x2f\x67\x97\x32\x0a\x95\x94\xd1\x6d\xa1\x96\x54\x43\xa8\x8c\xb8\xe6\x63\xd9\x6d\xc9\x49\x0d\x0c\xb8\x6b\x16\x10\x8f\xf8\xcb\x60\x00\x77\xa0\x54\xb8\xc3\x00\x4f\x1b\x50\xc1\x9a\x43\xfa\x2c\x6f\x9f\x65\x99\x2b\x57\xf8\x9d\xb1\x60\xc8\x06\xe1\xef\x5f\x0d\x51\x5d\xdd\x59\x5b\x9c\xcc\x75\x6a\xe0\xb3\x05\x83\xbe\xa3\x52\xc2\x9a\x86\x1b\xd3\xac\xb9\x8b\x4f\x77\x66\xd7\xee\x8c\x18\x3a\xf8\xfa\x55\x16\xd4\xe0\xfa\x2e\x19\xd3\x18\x22\x05\xc8\x47\x9c\x46\x99\x71\x4c\x83\x4c\x9a\x70\x59\xad\x88\x64\x6b\x41\xfb\x81\xc0\x57\x43\x21\x2b\xf2\xed\x71\x66\x79\x7b\xaf\xc3\x7f\x95\x76\x95\x02\x67\x18\xfe\xba\x47\x36\x87\xc3\x21\x79\xc8\x2f\x67\x3c\x77\xad\x5e\xc7\x0f\xf0\x6e\x0f\xb0\x23\xf1\xc5\x38\x48\x4e\x05\xbd\xf0\x58\x3f\xe2\x5d\xdf\xea\xa8\x72\x67\xcc\x22\x11\x08\x62\x25\x2c\x2b\x9d\x0e\x73\x16\xd1\x5f\x2c\xf3\x99\x69\x31\x68\xbb\x11\xc7\xe0\xc2\xf9\x0f\xe3\x91\x3f\x8a\x2d\x34\x08\xc3\x1c\x47\xac\x17\x56\x0e\xae\x34\xc6\xd5\xc3\xbd\x35\xbe\xe1\xca\x83\x81\x38\x6b\x47\xdc\x0f\xbf\xe0\x7a\xb0\x1b\xcb\x5b\x21\x95\x7a\x10\xf2\x52\x41\x96\x45\x67\x14\x33\xdc\x20\x54\xb3\x27\xdb\xab\xe0\xb0\x1e\x68\xc3\x0d\xbf\xdf\xa6\x14\xc9\x14\xf2\xb5\x7a\x04\xa1\x6d\xc5\xd7\xf1\xf0\x44\x6d\x99\x70\x85\xcd\xfb\xa6\xa1\x45\x82\x59\x82\x27\x62\x89\xce\xbb\x79\x91\x5d\xd5\x9b\x2a\x89\x97\x81\xf6\x55\xc3\xb2\x6e\xb9\xab\xc9\x75\x84\x57\x2a\x39\x9f\x51\xe9\x77\x20\xe4\x62\x39\x9c\xbe\x40\xe3\xce\xf6\xf7\x10\xa1\x59\x10\x71\x05\x6a\x5d\xfb\x4e\x75\xb4\x9f\xa4\x59\x87\xe1\xe5\x33\xbd\xe4\x27\x45\xdf\x00\x4c\x27\x30\x1d\x3f\x50\x7f\x16\xe4\x87\xe7\xc9\x3b\x88\xe4\x55\x5c\x42\x64\x4a\x8b\x0b\x94\xa0\xe7\x33\xbd\x3c\x29\xb7\xed\x6c\xa7\x09\x39\x78\xb7\xdf\xee\x5a\x8b\x5f\xc8\x16\x15\x75\x3a\x66\x16\x7a\x99\xec\x63\x1f\x84\xc2\xcd\x39\x41\xc7\x8d\x28\x27\x79\x11\xf1\x28\x2b\x51\x88\x88\x1a\x9b\x85\x96\x22\xdc\x6f\xc7\xd9\x29\x3f\x2d\x49\x39\x80\xed\x1e\x19\x15\xfd\xe8\x71\x2a\x30\x7b\x35\x4d\x13\x2a\x34\x4f\x9d\xf5\x4f\xb6\xd8\x7f\x9e\x45\x05\xf8\x4b\xb1\xb8\x11\x02\xb1\x8e\x50\x9f\xdc\x33\x94\x74\x31\xb8\x5e\x56\xbb\x50\x20\x79\x87\x5e\xf5\x82\x60\x0d\xd3\x8f\x55\x2f\xfd\x80\x9e\xae\x10\x63\x93\xdd\x31\x38\xf7\x0a\x28\x92\x68\xaa\xc7\x12\xf1\x1c\xa1\x6a\xcf\x9a\xb2\x97\x21\x7a\xf6\xeb\x1b\x55\x85\xc5\xf3\xcd\xc4\x06\x45\xd5\x58\x6a\x30\x87\x52\xbb\x8f\x12\xeb\xcf\xb7\x4f\x5a\x66\x77\x42\x9b\x68\x9d\x51\x1c\x77\x3c\xff\x4a\x97\x60\x65\xad\x5f\x9b\xb5\xda\x1b\x36\xbb\xdd\x68\xb7\x48\x8e\x0d\xb3\xfb\xd8\x4e\x5b\xf3\x41\x78\xb1\x95\x16\x24\x5f\x2e\x16\x69\x56\x80\x6e\x8d\xdf\xd4\xbe\xdb\x27\x4a\xab\xd2\x36\x1c\x41\x96\x13\x66\xe3\x97\x0a\x37\x59\x8c\xf5\x54\xb6\x12\x85\x79\x8f\xf5\x40\x53\x95\x16\xf4\xc8\xa1\xae\xbd\x9b\x96\x7a\xbb\x71\xf5\xb8\x1a\x83\x8e\x93\xf6\x92\x57\xda\xd7\x27\xbd\xed\x6f\xee\x55\xba\xf7\x2a\xdd\xff\x0a\x95\xae\x78\x58\x71\xab\xe7\xd8\x7b\x41\x96\x26\xe4\x7f\x97\xf3\xe0\x2c\xca\xc9\xf7\x01\xfb\xfc\xdb\x67\xfe\xd9\x9f\x53\xaf\xba\x77\x30\x20\x07\x49\x54\x44\x41\x1c\xfd\x4a\xc9\xdf\x79\x2f\x18\xa1\x06\x24\x07\x4b\x2c\x69\x70\x03\x03\x65\x4b\xd5\x70\x72\xde\x07\xad\xae\x2c\x26\xa3\x97\x88\xc8\x5a\x07\xe1\x88\x0c\xeb\x6e\xde\xb8\xb5\x07\x1b\xbe\xed\x56\xd7\x6b\x66\xe2\x75\xa7\xab\x5f\xa1\xc9\x20\x5e\x13\x89\x50\x68\x49\x1b\xf4\x78\x9c\xf0\xf2\xd7\x29\x3d\xa4\xea\x99\xc8\x6a\x64\x96\xf4\xbd\xeb\x75\x43\x84\x46\xc0\xda\x73\x7a\x3f\x58\x13\xe8\x29\x71\xc5\xcb\xdb\xea\x89\xc6\x0c\xa7\xa9\x3c\xab\x5b\xa6\x5a\x96\x4d\x3a\xc6\x3c\xca\x6c\x77\xbd\x8d\xc2\x69\x05\xe1\x19\x3b\xa3\xca\xd9\x21\x07\x2f\x20\x47\xf6\x4e\x4d\xda\xc6\x46\x99\x9f\x21\xff\xeb\x1f\xfe\x56\xc8\xa9\x46\x67\xcb\xe7\x41\x62\xa4\x2a\x5d\xbe\x0b\xe2\xff\xb3\x03\x93\x7c\x21\xd4\xdc\xf0\x42\xe2\x40\x1d\x1e\xa5\x01\x91\xdf\x54\x47\x29\xeb\xea\x42\xba\x79\x5e\x66\x5b\x0d\xf8\xcd\x33\x24\x1a\xac\xf6\xac\x50\xd7\x3c\xd1\xba\x0c\xe5\x3e\x7d\x90\xce\x59\x00\x3d\x53\x6d\xf7\xe9\x19\xcd\x2e\x3b\xd2\x1b\xf2\x87\x28\x99\xc6\xf4\x0d\x47\x78\x97\x8c\x88\x37\x43\xd7\x24\xa6\x55\x75\xc4\x0f\x2e\x26\x50\x1d\xb4\x94\xf0\x2e\xe9\x06\x59\x10\xc9\x34\x4e\x91\x86\x6d\x91\xc8\x90\xf3\xb3\xbb\xbb\xcb\xa9\x06\x03\x09\xb7\x0b\x12\x96\x9d\xb9\x19\x18\xbf\xd6\x6d\xfb\xaa\x13\x32\xac\xe5\x53\x72\x30\xe0\x31\x07\x55\x92\xf0\xca\x8e\x99\x8b\x5c\x8f\x8d\xfc\xc9\x73\x46\x74\x0a\xef\xd1\x6a\xd8\xd1\x73\x06\x54\xee\xe2\x5b\x74\xdc\xe2\x2f\xbc\xae\x9c\x33\x55\x51\x95\x14\x70\xc2\x2e\x28\x8f\xc4\xa2\xe8\x48\xde\xd3\x25\x93\x88\xc6\xa1\x65\x7a\x20\x5a\x31\x7a\x6a\xf1\x1c\xdc\x41\x8b\xf1\xf0\xae\x59\x64\x28\x93\xad\xa8\x0f\x92\x2c\x5c\x47\x58\x0e\x7b\x93\xb0\x7d\xc9\xda\xe4\xb7\x60\x71\xa6\x1e\xde\x91\x15\x45\x7d\x42\x4e\x64\x62\xe0\x93\x7b\x31\xf0\x5e\x0c\xfc\x6b\x8b\x81\xfa\x7d\x1e\x5f\x34\x77\xf5\x42\xef\x6e\xee\xee\x19\xc8\x1b\xa9\x6e\x2c\x35\x56\x86\x73\xa2\x88\xd4\x22\xad\x90\xd9\x27\x3a\x45\x0a\x97\x6b\x32\x97\x7d\x1a\x17\xf7\xc0\xf3\x74\xbe\x96\x0c\x86\x08\x0c\x7c\xf2\xe3\x60\x88\xda\x10\x1a\x67\xa0\x12\xdc\xd3\xb3\xaf\x88\x95\x63\x28\x5d\x41\x63\xf0\x26\x48\x82\x29\xd5\xaf\xf3\x19\xcb\xe2\xa8\x30\x54\x01\xd2\x85\x87\x06\x47\xfb\xfd\xdc\xc0\x90\x53\x71\x36\xaf\xb1\x7f\x0f\x29\xe3\x30\x51\x62\xfa\xf7\xb4\xc4\xbf\xd3\x20\xe7\x3e\x17\xca\x22\x51\x4c\x29\x78\xa9\xf4\x6c\x52\xa6\xa7\x79\xdb\xb1\xa8\x6c\xd3\x6c\x0f\x48\xcc\x41\x84\x68\xa3\x34\xd6\x84\xe1\x4e\x14\x85\xcf\x51\xc4\xa1\xec\xf8\xa4\x2f\xc3\x9c\x09\x36\x2a\xa5\xce\xcd\x31\x77\xc6\xa9\x2f\x29\x44\x68\x0e\xb1\xed\xaa\x71\xf6\xc9\x1b\xc6\xca\x23\x9a\x8b\xe8\xd8\x80\x0f\xc7\x0b\xa5\xe1\xd9\xb3\x31\xde\xe4\xa0\xae\xde\x2e\xe3\x58\x3b\xc6\xe8\x31\x29\x92\x5e\x44\x70\x6d\xe6\xc3\xdd\x1f\x33\xfe\xd0\x9d\x85\xdd\x21\x6b\x5f\x2b\xee\x8e\x83\xc9\x46\xd1\x76\xec\x00\x27\x2a\x94\x8c\x79\x10\x23\x35\xe1\x63\xde\xbf\xdb\x17\x11\x26\xaa\x63\xc7\x68\xb4\x09\x57\xaf\x9c\xf0\x00\xe9\xea\xc4\x69\xa3\x89\x83\x1e\x30\x48\x17\x4b\x06\xd1\xa9\x24\x0f\x3a\x50\x2d\x95\xd8\x58\xf7\x70\xd7\x12\x0a\xf2\x3d\x6e\xf4\x94\xb6\x64\x48\xe5\x74\xb1\x47\x20\xfa\x77\x55\x08\x29\xf2\x4c\xff\xe6\xd4\x0d\x45\x4e\x18\x3b\x40\x9f\x35\x9e\xf5\x1d\xac\x73\x7e\xaf\xa2\xe6\x62\xcc\xbb\x88\xe7\x0e\x78\xab\xcf\x8a\xa6\x3b\xe2\x12\xdc\x7b\x62\xa4\x98\x41\x7a\x31\x0a\xed\xcd\x0a\x9c\xcd\xc0\xb1\xe7\x99\x17\x40\x55\xe5\x8d\x4d\x22\x70\xe1\x0b\x59\x24\xdf\x4f\x49\x3a\x5c\x21\x72\x51\x20\xd7\x6d\x23\x24\x34\x8b\x41\x84\xdd\xb1\x8a\x7d\xc4\xf6\x92\xbc\xb2\xf3\x65\x21\x4f\x00\x30\x5a\x06\x18\x10\xf2\x8c\x00\x43\xea\x98\xe2\xd7\x82\x48\x75\x06\x68\x96\x4a\x94\x19\x55\x6e\x95\xb1\x8a\xc3\x41\x95\x74\x91\xcb\xf1\x69\x4a\x5b\xa7\xbf\x60\x74\xb1\x0c\x39\xb4\xd3\x65\x14\x87\x80\x30\x31\x28\x96\xe9\xf8\xb7\x05\x86\xff\xf1\xf0\xc5\xe1\xfa\xfa\x3a\x88\xf7\xed\x9c\x2c\xa7\xf1\x65\x5f\x44\x11\x63\x07\x82\x65\xce\xf6\xc4\x42\xb5\x92\x20\x97\xb2\xec\xb7\xb4\xab\x51\x37\x24\x8c\x71\x40\x86\x7a\x6f\xbd\x69\x44\x7a\x3a\xfd\xe5\x98\x65\x1f\x0f\x4f\x4e\x98\xd8\x85\x3f\xaf\xae\x94\xdd\xa6\x0d\xca\x7f\x6c\x42\x19\x36\x96\x1d\xff\x55\x91\x55\x3b\x40\x12\xc4\x85\x1d\xf4\x2a\x44\x95\xdd\xa2\xaa\x4b\x75\x6d\x74\xca\x43\xa0\x24\xfe\x67\x59\xc4\xf1\xf3\x2d\xe4\x77\x7d\x1a\x5e\xc5\x0f\x34\xb1\x22\x58\xf8\x42\x15\x18\x67\x75\x68\xcb\x94\x28\xf5\xc5\x94\xbe\x9f\x31\x62\xb1\x28\xf3\x3a\x8f\x69\x9e\xdd\x30\x87\x17\xed\x60\x66\xa6\x8c\x22\x2d\x03\x1a\x6f\x38\x15\xb3\xbb\x46\x35\xe5\x43\xb0\xaf\xa1\x04\xa9\xb0\xac\xa6\x9e\x9e\x65\x98\x2b\x9a\xd4\xbb\x73\x94\x1c\x72\x99\x51\xb8\x21\x7d\xff\x6e\x5f\x79\x60\xe2\xa6\x2c\xe3\x20\x51\xc2\x66\x94\x08\xa5\x8b\xdf\xd7\x53\xe6\xfa\x7a\xec\xf7\xfb\xd7\x38\xbe\x9b\xed\x4b\x4f\x6b\x32\x65\x51\x0f\x27\xad\xf3\x69\x5f\xea\x6e\x7e\x15\x22\x94\x34\x60\xfa\xa4\xc7\xb3\x56\x86\x68\x51\xb2\x44\xb1\xf3\x46\xda\xc0\x34\xbd\xfe\xfb\xf6\x5e\xef\x73\xaf\xf7\xf9\x6b\xeb\x7d\x84\xd2\x27\x3c\xbd\xc5\xcd\x9f\x4f\xef\xa3\xb4\x35\x58\xf1\xc3\x99\x93\xd2\xe8\xbc\x78\x6e\xf0\x11\x36\x0c\xd3\xe5\x87\xa3\xa9\x80\x91\x5a\xc9\x3b\x15\x81\xc2\xd6\xb4\xbc\x94\x77\x3c\x36\xfd\xe2\x82\x8b\x7c\x21\x96\x74\x65\xc9\x41\x1d\x56\x33\xda\x59\x04\x90\xa3\x76\xe9\xf8\x3a\x68\xe9\x9b\xf5\x2e\x5f\x1e\xb0\x68\xb1\x2c\xd4\xe3\xb5\x84\x9e\x0b\x6c\x76\xf4\x76\xc9\x84\x8e\x11\x69\x2b\x38\x2b\x8e\xc6\x88\xb4\xc3\xd3\x4f\xbe\x5c\x29\x26\x6e\xab\x3e\xa9\x46\xa7\xb4\x59\xa3\x0a\xce\xdb\xa8\x2f\x57\x36\xba\xe5\x36\xba\x58\x16\xaf\xe9\x45\xfd\x30\x5f\xd3\x8b\xb2\x31\x9a\x59\xd5\x03\xac\x6f\x8b\x03\x95\x0d\xcd\xdf\x96\x35\x2e\xb1\x19\x1d\x6b\x38\x39\x11\x3d\x8d\xe4\x9e\x18\x7a\x4f\x74\x0b\x80\x4f\x4a\x76\xae\x17\xcf\xf5\xae\xc5\x69\xa7\x35\xda\x86\x2d\xea\xe9\xfd\x16\x75\xbf\x45\xfd\xb5\xb7\x28\x7d\x35\x41\x8b\xd9\x8d\xee\x25\x04\xf0\xdd\xbe\x4a\x2c\x89\xfe\xef\x0b\xff\xef\xbb\x04\xf1\xdf\x83\xd4\x6c\x9b\x0c\x44\x9a\x23\x5b\x40\x0b\x91\x2c\xc1\xc6\x65\xed\x8d\xd3\x64\x12\x4d\x25\x18\x0a\x85\x83\xa1\x65\x64\x15\x09\x76\x2e\x9e\xad\x19\x17\x34\x22\x51\xc2\xbc\xe2\xa1\xc0\x2d\x64\x40\xa2\x04\x39\xc8\x3f\x5c\x26\x63\xbe\xc5\x60\xa8\x9c\xa7\x4a\x30\xc6\x8a\x33\x6a\x03\x89\x54\x55\x17\x77\x50\x84\x21\xa2\xd3\x20\x91\xd9\xdc\xeb\xa1\xd3\x1f\x99\xac\x84\x10\xf0\x99\xd6\xe4\xce\x40\xe9\xbc\xc5\x1b\x41\x50\x02\x0e\x4f\xba\xe4\xc1\x03\x22\x7e\xf7\x41\x27\x78\x38\xe9\xb4\x87\x17\x6d\xee\xba\x64\xd8\x25\xcf\x48\x8b\x16\x33\xb6\x7b\x40\x60\xd2\xe7\x97\xaf\x83\x7c\xd6\x22\x23\x3b\x99\x6b\x74\x5b\x5a\x4a\x80\xae\x7d\x88\xa6\x09\xcd\xf2\x8a\x1e\xde\x71\xff\x44\x83\x25\xdd\x54\xb9\x4e\x6f\xf3\x22\xf8\x4c\xb3\xf7\x87\x07\xf5\x5d\xbd\x59\x4f\x51\x47\x3f\xc8\xb6\xde\x04\x79\x41\xb3\x24\x0d\x29\xee\xa9\xca\xb6\x91\xf9\x2a\x4a\x82\x38\x2a\x2e\x7f\x3b\x6c\xca\x16\x4b\xd0\xa9\xb3\x1d\x7c\xa2\xe8\x5f\xaf\xb2\x74\xfe\xfc\x37\xa0\xd3\xb6\xe8\x1a\x0a\x2a\xf5\xfc\x12\x1a\x66\x9d\xdf\x4b\xc2\x03\x56\x4e\xc5\x72\xf3\x42\xf2\x71\x28\x58\x3d\x9e\x65\x32\x8e\xe9\x6f\x34\x80\x23\xd6\x56\x4d\xd7\x31\x4c\x69\xa7\xe5\x3c\xa1\x71\xee\xa7\xcb\xa4\xd1\x25\xe3\x1d\x8c\xc3\xdb\x36\x27\x25\x3c\x94\x12\x30\x3e\x2a\x67\x0a\x7e\xc3\xfe\x1f\xa9\x06\xd1\x64\x38\x93\x80\x01\x8c\x3e\xab\xee\xbd\x2c\x66\x77\x7d\x3c\x6c\x7c\x34\xbc\xa3\x93\x21\x84\x7f\x2e\x3f\x19\x72\xc5\x17\xdf\xc3\x23\xea\xed\xd1\x02\x77\x66\x51\xd3\x8f\xc5\x0d\xba\x80\x2c\x1c\xf8\xde\xca\xbd\x9f\x10\xec\x9f\xfd\xe0\xf9\xde\x5b\x2b\x14\x9d\xd8\x51\xb9\x4e\x8e\x3f\x9f\x16\x9a\xb9\xeb\xb5\x35\xde\xbb\x3e\xb7\x8b\x53\x2f\xa9\x5e\x16\x33\xad\x0b\xec\x91\x36\x0e\xdc\xdd\xee\x89\x61\x4e\x69\x31\x2a\xd1\x78\x4b\x4f\xb5\x7d\x5c\x50\x8c\xa4\x27\xb4\xb4\x46\xe1\xb3\x20\x36\x62\xcc\xf5\xad\xb0\xe9\x67\x41\xec\xb8\xa2\x51\x69\xd7\x6b\x80\x9e\x95\x86\x22\xbc\x3c\xde\x64\x30\xa2\xe8\x4d\x86\x23\x8a\x36\x1c\x50\x13\x4d\x04\xe3\x2e\x41\x0c\x76\xbb\xb5\xe7\x66\x01\xe8\x9e\x9d\x25\x9b\x72\xf2\xd5\x01\x1a\xd9\xf2\x1a\x17\xb8\x23\x72\xac\xc5\x69\x7e\xb9\x2b\x9c\xa8\xbe\xd2\x77\xb9\x36\x04\x8e\x7b\xcf\xf9\x89\x02\x46\x81\x43\xad\x5b\xcc\x11\xae\x86\xe7\x29\x8f\x45\x0a\xa8\x44\x69\x92\x66\xc1\x94\xee\x15\x4d\xf4\x26\x02\xb4\x14\x47\x3e\x08\xa5\xd2\xa8\xc0\x12\x5f\x77\x9c\x63\x17\x29\xe8\x15\x56\x41\x8b\x77\x60\xc2\xb5\x67\xcd\x98\x18\x54\xe9\x70\xac\xcc\xdf\x7e\xbe\xbd\x03\x13\xcb\xe4\x20\x99\xa4\xf5\xe3\x43\xc0\xa5\xc3\xf4\xc3\xfc\x41\x46\x2b\x79\x5c\xdd\xea\xe5\xcc\xd7\x1a\xa1\x3a\x1e\xdd\x6e\x58\xbe\xde\xf6\x1c\x86\xa6\x6d\xbd\x19\xab\x22\xd7\xab\xad\x56\x90\xa7\x2b\x57\x2a\x3e\xc1\xf8\x11\x62\xa1\x43\xc0\x2a\xac\x20\x9c\xa0\x73\x99\x1d\x67\x64\x53\x26\xdc\x08\x2d\x6a\xd0\x0d\x87\x2c\x3a\x52\xc7\xa3\xc4\x89\xa8\x09\x8f\x12\xa0\x0e\x2d\x18\x27\x3c\x97\x1e\x36\xab\xe8\x41\x0a\x31\xd6\xce\x45\x8c\xdd\x09\xdb\xdc\x94\xac\x07\x5e\xc1\xc8\x74\x67\xd0\x94\x50\xf0\x15\xe2\x7b\x1a\xc4\xe5\x44\x22\xcf\x65\x8d\xa8\x44\x02\xfb\xc8\x04\x9f\x38\x7f\x07\x3a\xc1\x23\x3e\x48\x0a\xef\x80\x41\x06\xaf\xa7\x0b\x00\x73\x68\x42\x9d\xea\xbe\x06\x7f\x40\xdb\xd9\x2d\x58\x41\x6f\xad\x64\x77\x9b\x2f\xa2\xb8\x94\x13\x98\x5b\x9c\x00\xad\xd8\xe7\x5c\x08\x89\x87\x61\x39\x99\xd9\x67\xb6\x86\x5c\xda\x2e\xe6\x74\xab\xea\xd8\xba\xe2\xc2\x5d\x85\x12\x3d\x73\x23\xa7\xf0\x05\x1d\x47\xf3\xaa\x15\xa7\x4f\x82\x0d\x91\xa0\x0b\x94\x10\xe5\x1f\x77\xc0\xe6\x01\xaa\x66\xb0\xe5\xd1\xf2\x4b\xd4\x30\x70\xc6\xae\x1c\x74\xfd\x0a\x42\x15\x56\x6f\x2c\x1f\x3d\x5a\xaa\x95\xc6\xa4\x4a\x39\x83\x2b\x53\x80\xfd\x91\x38\xcd\x4d\xf0\xf4\x9e\x8e\x69\xb4\x68\x40\xe6\x6e\x99\x26\x04\xe0\x82\xde\x96\x02\x44\x8d\x8d\x07\xd8\x70\x15\xd7\x72\x31\xcf\xe0\x6c\xc0\x26\x14\xc0\x8f\x46\x77\x74\x48\x2c\x5b\xde\x44\x5a\x1b\x48\x6b\xbd\xf7\xc1\x79\xf3\x65\xee\x16\xf0\x23\xa3\x12\xae\x09\x77\x63\xb8\xf0\x9c\x12\x58\xbd\xab\xf5\xb6\x51\x57\x6f\xde\x4f\x7b\xb6\x7c\xeb\xcc\x37\x8e\x68\x9a\xac\x30\x0e\x13\xba\x64\x1c\xa5\x40\x5f\x79\x1c\x0d\x3a\x5f\xde\xe3\x3b\x3f\x85\x96\x10\x8e\x30\xf1\xad\xea\x28\x03\xf1\x77\xd4\xca\xb9\x49\x47\xd9\x7e\x70\x67\x67\x65\x9a\x17\xd1\x3c\x28\xe8\x8f\x41\x9d\x4c\x88\x20\xfd\x43\xf3\x03\xdc\x84\x62\x8c\x11\xde\x4a\xf0\x18\x73\x19\xf5\x43\x1a\x47\x61\xe9\xd1\x46\x4f\x1b\x87\xee\xe7\x02\xbc\x64\x0a\xcd\x3a\x7d\x63\x2d\xed\xc8\x4f\x3f\xfd\xd4\xb0\x0f\x71\x29\x05\xa9\x9a\x56\x6a\xf9\x03\xcd\x16\xb4\x76\x8b\x52\x18\xe0\xd0\xd5\x08\x70\x60\x2a\x7a\x91\x2f\x4f\xe7\x51\xf1\x73\x9a\xd5\x49\x4a\x1a\xb0\x64\xa5\xfb\xf2\xab\x0d\xa0\x1a\xb4\x2a\xa0\x4a\xb7\xe3\x92\xf6\xfc\xc7\x9c\xfd\x20\x09\xf9\x23\xff\x22\x28\x96\xb5\x5a\x17\x0b\xdc\x3a\x51\xab\xd3\x56\x09\x94\xc3\x41\xee\x40\xdd\xf6\x72\x91\x8e\x67\xa5\xbc\xe3\xff\x63\xef\x5d\xd7\xdb\xb8\x95\x44\xd1\xdf\xf6\x53\x20\xde\x67\x45\x64\x4c\x53\xbc\x4b\xa6\xad\xcc\xc8\x94\x64\x69\x6c\x59\xda\x92\x9c\x64\x6d\x7d\x8a\xbf\x26\x09\x8a\x6d\x93\xdd\x9c\xee\xa6\x2e\x89\xb5\xdf\xe7\x3c\xc7\x79\xb1\xf3\xa1\x70\xbf\x35\x9b\xba\x38\x4e\x46\x5a\x33\x31\xbb\x1b\x28\x14\x80\x42\xa1\x50\xa8\x8b\xab\xa7\x45\x0e\x94\xa2\xac\xff\x44\xe9\x2a\x72\x1b\x06\xe2\xef\x80\x95\x0a\x57\x5a\xac\x49\x6d\x7d\x45\x7d\x27\xb4\xd3\xda\xdb\x5e\x3c\xd4\x8b\x29\xea\x50\xed\x3d\xf0\x61\xfb\x0d\xd3\x60\x19\x2d\x31\x5d\x93\x5d\x9c\xab\x54\x74\x1c\xc4\x70\xb9\x5f\x53\x4a\xd1\xbe\xc1\x01\xd2\xe8\x08\x3b\xc5\xdb\x8d\x9a\x32\xa8\x5d\x42\x9e\x47\xb5\x6f\x4a\x45\xdf\x7b\x71\xba\xf1\x15\x60\x02\xb8\xef\xb3\xd1\xa8\xee\x17\xa4\xec\x44\xf2\xa5\x2d\x47\x2a\xdf\x74\x81\x47\xaf\xe4\xad\xa1\x34\xaf\x6f\x09\xd6\x87\xf7\xef\xdf\xdb\x85\x29\xfb\x54\x40\x0a\xce\xa6\x75\x9a\xbc\x80\x67\x66\x28\x49\x53\xbb\x8a\x5b\xd3\xbc\x54\x0f\x92\xb6\xc9\xda\x14\xd7\x77\xba\x2a\x52\x70\xfe\x30\xea\x07\xa9\xaa\xed\x62\x08\xc0\x12\x63\x8c\x9f\x95\x11\x45\x6e\xca\x95\x25\xda\x98\x86\x91\x6e\x24\x6b\xb5\xc0\x4a\xdc\x12\xfe\x38\x48\xc7\x49\x90\xe5\xf6\xc1\x53\xa6\x90\x68\xb1\x3c\x46\xdc\xc8\x2b\x07\x21\x77\x91\xc5\x87\x55\x66\x55\xa6\x9f\x50\x97\xc7\xf0\x3c\x48\x0f\x93\x70\x90\x3b\x66\x9e\x32\xb7\xbe\x4d\x5c\x1e\x4b\x96\xbd\x30\xcd\xc3\x52\x94\xb9\x65\x1b\x7d\xc5\x16\x23\xa7\x19\x7f\xb1\x07\xa2\x21\x9e\xda\xe9\x17\x6a\xb2\x9b\x87\x9b\x59\x54\x69\x51\x65\x21\xda\xfd\x7d\x75\x20\xcd\x21\x15\xdb\x98\x7e\xa8\x39\x3e\x06\x83\x2c\x4e\xb8\xfc\xcc\x0d\x28\xc1\x1b\xa9\x82\x48\x59\x6d\x4f\x65\xa5\x5d\x8d\x8d\xb8\xc1\xa4\x15\xd1\xa2\xa2\x78\xed\xd3\x52\xbd\x04\x83\xc1\x33\xf8\xa0\xf7\x0c\xaf\x3c\x25\xdd\x21\x35\xc2\x94\x70\xc8\x50\xac\x54\x9c\x06\x33\x15\x6e\xd5\x59\xc5\xd9\xb8\x54\xae\xd8\x24\xfb\x3e\x3e\x57\x24\xa3\x62\x28\xb9\x3a\x2a\xed\x39\xf3\x33\xf1\xf0\xd1\x2f\xb1\x0a\xd5\xf3\x49\xdc\x0f\x26\x55\x32\xa8\xd5\xc0\x7e\xcd\x52\xa7\xba\x9a\x0c\x07\xc1\xec\xc3\x6d\x9b\x25\x95\xad\x46\xe9\xcb\xbc\x26\x15\xe3\x56\xd9\xa0\xe9\x41\xa9\xa6\xa6\xe4\x15\x4a\xee\xe9\x59\x14\xd4\x72\x3b\x1b\x4b\xb7\x00\xc3\xbe\xf7\x59\xb7\xbe\x5e\x79\x66\xd9\x19\x33\x3f\x37\x69\xe0\xfb\xac\xdb\x68\xc3\x0b\x3a\xa7\xcf\xba\x8d\x97\xf4\x51\xd0\xc2\xb3\x6e\x93\x56\x09\xfb\x41\xf4\xac\xdb\x6c\x56\x74\x2f\x04\x78\x64\x83\xf4\xac\xdb\x6a\xc1\x33\xb7\x46\x7e\xd6\x6d\x51\xf0\x8c\xb3\x3f\xeb\xb6\x28\x5a\xdc\x6a\xe8\x59\xb7\x45\x1a\xe4\xb6\xc4\xcf\xba\xad\xe6\xcd\x59\xa5\xf9\xf2\xd1\xad\xe1\xd1\xad\xe1\x9f\xed\xd6\xe0\xf3\x69\xb8\xb3\xeb\x5d\x71\x6f\x83\x02\xae\x04\x50\xee\x03\xce\x1e\xd2\x53\x0f\xde\x2e\xb6\x7d\x94\x3e\x7a\xb7\x31\x7e\x2c\xe0\x99\xb7\xba\xba\x2a\x43\xdb\xb9\xc2\xe5\xb1\xbc\xcf\x84\xc5\x03\x38\x9c\x8d\x51\x30\x0b\x15\xdc\x1f\xe8\x40\x32\x09\xd3\x0c\xe7\x9d\x17\x22\x9c\x7d\x92\x85\x6e\x2b\x5c\x61\x9c\x98\x17\x2c\x56\x2b\xbe\x42\x4b\x08\x7c\xaa\xf8\x65\x6d\x6a\x1f\x70\xe6\xd8\xd4\xf4\xcd\x4b\xdd\x5d\x6e\xce\x2a\xad\xda\xe3\x6e\xf1\xb8\x5b\xfc\xb3\x77\x8b\xef\xd4\x09\xee\xfe\xfc\xd5\x0a\xba\xd3\x49\x9f\x80\x43\x9c\xa4\x71\x14\x4c\x1e\x1d\x03\x1e\xda\x31\xe0\xa6\x98\xa9\x78\x84\x2f\xa5\xfd\x79\x9e\x02\x5c\x16\xb4\xb5\xdf\x33\x36\xab\x9f\x9c\x85\xee\x70\xc5\x1d\x4e\xc9\x46\x70\x14\x5c\xbe\xc3\x8b\xae\xbe\xd4\xa2\x2b\x95\xa7\x4f\x9e\x98\xb8\x59\x05\x72\x1c\xdc\x8b\x5f\xe5\xda\xed\x88\x0f\x8a\x05\xf8\x93\x27\x05\x0d\x1c\x0a\xdf\xe1\xe2\xc1\x11\x1e\xc4\x17\x34\xc6\x64\xde\xa5\x27\x2f\xe7\xc4\x55\xff\x9a\x33\x20\xf3\x68\x12\x0f\xbe\x14\xa3\x14\xad\x6c\x0e\xb1\xf8\xca\x15\xb1\x9c\x2f\x36\x6e\xde\xd1\xbb\x67\xd3\x09\x39\xf7\x0b\xed\x27\x96\xb9\x27\x77\xd9\x1d\x78\xbb\x54\x7c\x7e\x8a\xcd\x4e\xfe\xdc\x2c\x73\x97\x65\xce\x8d\x81\xbc\x4b\xb2\x66\x0d\x2b\x8d\x28\x8b\x57\xbe\xd5\x28\x48\xb9\x3d\xe1\x54\xed\xbb\xed\xf0\x5e\x8a\x28\xe0\x54\x79\xf7\xe1\xce\x07\x9b\x0b\xd4\xc2\x72\x3a\xd4\xc2\x1e\xb1\xdc\x96\xcb\xf9\x76\x2b\x85\x73\x87\x8a\xc8\xd0\x0a\x99\x72\x7a\xfd\x51\x4e\x7f\x94\xd3\xff\xd9\x72\x3a\x13\xd2\xd3\xb1\x47\xab\xb3\x40\xfc\xc6\x09\x9e\x4f\x09\xe8\x9f\x17\x28\x81\x06\x71\x82\xab\x61\xac\xcb\xe9\x6b\x85\xe3\x2f\x15\x8c\xd7\xb0\x28\xec\x03\x14\x3a\x1e\x8f\x1f\x5c\x3b\xf4\xfd\xc8\xe3\x84\x3b\x1e\x8f\xb5\xdb\x0d\x7c\xc9\x72\x57\xec\x7c\x8b\x0b\x9d\x74\xbc\xf8\x42\x27\x1d\xc3\x85\x0e\x15\x5c\x96\xb9\xb7\xc9\x93\xf3\xfd\x9b\x93\x25\x1e\x28\x5b\xd3\x85\xf3\xa6\x8e\x89\x08\xe9\x78\xfc\xc9\x5d\x40\xb7\x2a\x42\x0e\x5d\x56\x5e\xa3\xa1\xee\x88\x67\xb4\xe8\xf8\x7a\xb7\xe6\x52\x9c\xed\x07\x57\x8c\x08\x8e\xc3\x3f\xcc\xcb\x61\xa5\xed\x45\x45\x75\xb3\xb1\xdb\x20\x12\x46\x87\xf1\xaf\xf9\x08\xb8\x8a\xdc\xad\xe1\x69\x90\x7c\x39\x49\xe6\x69\x86\x87\x87\xd8\xba\x0c\x56\x9a\xcf\x2f\x78\x37\x24\x22\x4c\x64\xba\xc3\x20\xcc\x69\xdf\x5b\xe6\x6e\x14\x10\x0c\x87\x87\x49\x78\x11\x64\x98\x1e\x09\x3d\xad\xe7\x15\xbb\x5b\xdf\x69\xee\xd0\x85\xdd\xcf\x2b\x76\x37\x04\xc6\x41\xba\xb0\x75\x6f\x99\xbb\x35\x7d\x8e\x33\xba\xa1\xe7\x8e\x7d\x4e\xa9\xbb\x37\x5f\x60\xee\xf3\x8a\xdd\x99\xee\x8f\xaf\xa7\xb9\x8d\xfb\x8a\xdc\x99\xea\x17\x35\xec\x2b\x72\xd7\x21\x27\x72\x5c\x86\x29\xe8\x9d\x24\x9e\x1e\x06\x69\x7a\x19\x27\xc3\xbc\xf1\x2f\x58\xe7\xce\xeb\x60\xd1\x98\xf8\x8a\xdc\x99\x0c\x17\x35\xec\x2b\x72\x1f\xac\x67\x51\xdb\x39\xa5\xdc\xcd\x8b\x87\xd5\x55\x94\xce\xfb\x70\xf3\x86\x21\x35\xd5\x3c\x92\xcf\xd3\x30\x4d\xc3\xe8\xfc\x69\x61\x6c\x67\x71\x6a\x5e\x5d\x29\x58\x3a\xbe\x3a\xf4\x14\x28\x5f\xef\x88\x16\xdf\x72\x1d\x8f\xc7\x4a\x1e\x52\xc3\xf6\x42\x3b\x45\x1b\x96\x11\xad\xc6\xe3\x19\xfa\xf1\x0c\xfd\xcf\x3e\x43\xcb\xbb\xae\xfe\x1f\x7f\x18\x77\x5d\x9b\x13\x7c\x85\xde\xe0\x04\x9f\xa7\x7f\x04\xe9\x1f\x21\x7a\x1d\x4c\xf0\xd5\x7f\x26\xd9\x28\xad\x8e\xe7\xfa\x71\xb8\xc3\x82\xa2\x1f\xe1\x11\x4e\x70\x34\xc0\x5d\x44\xda\x4f\xbb\xab\xab\xe7\x61\x36\x9e\xf7\xab\x83\x78\xba\xfa\x5b\x18\xed\x84\xd1\x41\x72\xbe\xfa\xdb\xd6\x61\x7c\xdc\x1b\x07\x61\xb4\xda\x9f\xc4\xfd\xd5\xf4\x32\x48\xa6\xab\x61\x94\xe1\x24\x0a\x26\xab\xa4\x4b\xf8\x2a\xe3\xff\x56\xcf\xe3\xff\xf5\xbe\xd9\x7c\xe0\xab\x31\x79\xdf\x75\x4c\xb0\xf9\x87\x1f\xae\xe1\xc7\xdf\xe2\xb2\x8b\x5a\xbe\xe2\xec\x32\x4e\xbe\x1c\x61\x88\x78\x9f\xa7\x28\x37\x8b\xdb\xda\xf2\xfe\x1f\x7f\x7c\xca\x29\x75\x17\xe7\xce\xeb\x68\xb0\x1d\x05\xfd\x09\x5e\x84\xa5\x52\xd2\x8d\xa0\xbb\xc0\x5d\x70\xbb\x0c\x66\x05\x71\x93\x25\x3d\xb8\x39\x0b\xdc\x01\xb7\x61\x7c\x19\xb1\x64\x06\x79\x88\xf1\x62\x6e\xac\x1c\x5f\x8b\xfb\x2c\x7b\x10\x9b\xcf\x0a\xa0\x45\x0b\xb9\x91\xb2\xbe\xdd\x19\xa5\x04\x67\x49\x88\x2f\x16\x85\x11\xe1\xc5\xdc\x68\x39\xbe\xde\x85\xb4\x32\xb2\xdb\x2d\x20\x2a\x52\xc6\x43\x4e\xc6\xa7\x3b\x0f\xd1\x39\x2e\xe0\x13\xef\xc6\x45\xff\x70\x87\x31\xa1\x49\xa0\x16\x84\x5a\x77\xe3\xa0\x7f\xb8\xf3\x68\xb0\xbc\x6f\xf9\xc8\xd0\x42\x6e\x7c\xac\x6f\x1c\xa5\x56\x21\x94\x72\x6e\x75\x2d\x15\xa7\xc9\x96\x95\xdb\x3f\xc9\x0f\x95\x97\x92\x11\xc9\x97\x9c\x0f\x28\x37\x8e\x33\xfd\x99\x53\xbf\x02\x88\x90\xa0\x7c\x3c\xc7\xca\xc5\xe4\x6c\xae\x3c\x28\xb2\xf8\x83\x5e\x33\x8e\xc3\x0b\xaf\x6f\x0c\x99\x13\xf8\xee\x3d\x43\xe6\xc3\x76\x28\x65\x35\xd8\xf0\xdd\x73\xbc\x72\x9c\xaf\x88\xb0\xe4\x8a\x99\xef\xbc\x97\x6c\x3e\x9e\xa9\x1e\xcf\x54\xff\xec\x33\x15\x3b\x50\xf1\x0b\xa2\x6f\x9b\xec\xe5\x36\x86\xd5\xdc\x3b\x2a\x98\x85\x5c\x18\xa7\x99\x82\xb3\x71\x9e\x05\x1a\xbd\x2e\xcb\x0d\x6f\xcc\x4b\x67\xd7\x33\x22\x1f\xb0\x50\xc6\xaf\x9e\x2a\x0c\x3c\xcc\x06\xe3\x12\xf9\x6e\xc6\xaa\x1b\x04\x29\x46\x2b\x84\xe2\xd3\x6c\xa5\xab\x7d\x82\xc9\x4a\xce\xd3\x6a\x3a\x0e\x47\x59\xc9\xc8\x4b\x86\xac\x1c\xc3\x35\xbb\x00\x63\xc9\xe0\xbe\x16\xe1\x4b\xe6\xed\x0c\x17\xb2\xaf\x1c\x68\xcc\x70\x34\x0c\xa3\xf3\x07\xc7\xe3\x90\xb6\xa3\xda\x10\xb9\x90\x62\x31\x68\x6d\x6c\x0c\x70\x56\x65\x9a\xa7\xed\x46\x91\x0e\x44\xa9\xc5\x96\x84\x0c\x9a\x29\x23\x68\xa4\xe0\x90\x9d\x1c\x52\x75\x14\x46\x69\x16\x4c\x26\x85\x5a\x36\x4a\xbb\xdd\xf8\xfd\x85\x72\xf0\x38\xc7\xd9\xfb\xf8\xbc\x40\x10\x01\x52\xca\x1b\x3e\x80\xb6\x68\x14\xc9\x69\x75\x16\x2f\x0c\xe4\x42\x8a\x2c\x68\xaf\x37\x0e\xa2\x73\x77\xc4\x82\x05\x32\x96\x98\x2f\xd5\x24\x4b\x1b\x3d\x4d\x10\x22\x1d\x53\x1a\x89\x59\x30\xc8\xb3\x5b\x3a\x72\xa4\xe3\x71\x15\x58\xa3\xc5\x6e\xd2\xb1\xcd\x6e\xfc\xe2\xd3\x82\x5b\x1a\x8b\x0c\x90\x75\x4b\xa3\x59\x12\xdc\xab\x9a\xde\x4f\x8c\xc8\xa5\xa9\x7f\x38\x44\x6c\xd2\x45\xd6\x35\x05\x6d\x96\xe1\x60\x16\xbd\x5b\xf3\x06\x19\xdf\x43\xdb\x2a\xe9\x59\x92\x28\xc5\x01\x67\xe3\x2e\xf9\x0f\x05\x96\x8e\xc7\x5d\xf2\x9f\x0a\x95\x5e\x5d\x89\x9d\x5a\xad\x47\x99\xf4\x51\x26\xfd\x87\xcb\xa4\x52\xd1\xcf\x9d\xac\x6f\xe3\xd8\xe2\x10\x48\xa9\x83\xf8\x11\x3e\x27\xf3\x1c\x24\x9b\xfd\xd0\x93\xdf\x28\x5d\x7d\xab\x17\xad\x7e\x4e\x63\x91\x43\x28\x1c\x04\x33\x15\x88\x0f\xc6\x5e\x6f\xf3\xd0\x86\xa0\x60\xc2\x3c\xd1\x99\xf9\x32\xda\x40\x2b\xb5\xab\x41\x67\xf8\x72\xd8\x18\x0c\x5b\xad\x97\xc1\x5a\xbb\x35\x68\xbd\x6c\x35\x3a\x2d\x5c\x5f\xaf\xbd\x1c\xb4\x6b\xb8\xd9\x1a\x76\x5a\xed\x4e\xa3\xbf\x22\x71\x71\x81\x09\xea\x41\xbd\x5e\xef\x0f\x6a\x6b\xad\xc1\xcb\xc1\x28\x58\x5b\xaf\x8f\x6a\x83\xe6\x3a\xee\x34\xfb\xc3\x76\x7d\xf0\xb2\xde\x5f\x0f\x46\xb5\xda\x8a\x9f\x39\x51\x1c\xbb\x8a\xa8\x1b\xf4\xc3\xae\x63\x10\x25\x2b\x64\x7e\xf0\x5d\x67\xff\xe8\x56\x4f\x0b\x13\xb4\x2d\xc8\xe6\xb8\x3a\xe0\xda\xdd\xa5\x50\x35\x8e\x99\x3f\x8b\xcf\xba\xf5\xca\xb3\x05\xf3\xf4\xac\xdb\x20\xcc\xb6\xfd\xc8\x6c\x1f\x99\xed\x3f\x9b\xd9\x4a\x5e\xcb\xb5\x5f\x06\xb3\xcd\xb3\x4c\x1e\x25\xf1\x1f\x78\x1a\x44\xd5\x21\xfe\xf9\x5e\x18\xb4\xcb\x47\xdd\x70\x50\x37\x6f\x48\x2d\x9b\x5a\xed\x1a\x94\xe5\x89\x67\x9f\xe0\x51\xc9\x5c\x43\x35\x89\xca\x77\xfa\x42\xcb\x6d\x63\x94\x48\xcd\x12\x86\x83\xb3\x52\xd4\xf8\xa2\xd4\xd1\x15\xd0\x4a\x15\xfd\x83\x52\xc3\xba\xcd\x8d\xe6\x93\x09\x15\x2d\xf9\x58\xa8\x59\xb4\xcd\xdb\x4d\x6d\x9c\x92\xa9\x36\x44\x16\xe8\x64\xba\x30\x29\x39\x4b\xc1\x0d\xe8\x82\x56\x81\xd0\x2e\x15\x54\x8d\x8c\xe3\xb4\xe4\x1e\x29\xa8\x66\x1d\x87\xbc\xdf\x37\x5a\xc2\x71\xf1\x6a\xd5\xd5\x25\x05\x8e\xa9\xc1\x71\xc5\x6e\x31\x46\xf8\x3f\x5c\x6f\x69\xdd\x2e\xc1\xbf\x68\x87\x63\x2d\xc7\xfc\x82\x4e\xd3\xf0\xfa\x6a\xaf\x59\x4e\x75\x57\x9e\xf5\xfc\x7e\x53\x50\xfa\x2c\x6a\xb9\xf2\xd5\xbe\x9b\x14\xf9\xe3\x8f\x2c\xb1\x3e\xfa\x61\x83\x12\x8e\xf1\x8a\x6c\x28\xa3\x30\xc2\x43\x3e\x4e\x06\x04\xd1\x56\x97\xd5\xf2\x0c\x17\x64\xa0\xcf\x62\x84\xaf\x68\xb0\x24\x6e\x61\x8e\x46\x49\x3c\x95\xa7\x6d\x91\xd8\xbd\x8a\xf6\xc9\xc6\x16\xe2\x94\x51\x12\x0c\x93\x31\x96\x0c\x18\x37\x48\xb7\x89\x48\xc2\xd3\xc6\x75\x87\x8d\xd4\xd7\x0f\xf3\xc9\xe4\x46\xb1\x76\x0f\x47\x08\x5f\x85\x29\x14\x77\x0e\xb9\xd1\xa2\x57\x61\x18\x8e\x64\x2e\x34\xde\x1a\xcd\x86\x06\x7a\xb6\x09\x8e\xce\xb3\x31\x7a\x81\xea\x67\x65\x47\x5e\x27\x28\x33\x8b\x67\xa5\xf2\x2b\xb4\xba\xca\x2f\xbe\x08\xff\x87\xf5\x04\xa3\xf5\x83\x2a\xdc\xe8\xc3\x4d\x0d\x1c\x24\x66\x59\xec\x26\x45\xdd\x10\xc2\x47\x8c\xec\x15\xef\x85\x97\x1a\x75\x68\x3a\xf7\xed\x7f\xd6\x52\x55\x93\x3a\x42\x94\x44\x3c\xd5\x15\x90\x57\x7f\x1e\x4e\x86\x6f\x71\x56\x52\x8e\xe7\x38\x9a\x4f\x71\x12\xf4\x27\xb8\x8b\xb2\x64\x8e\x6d\xdd\x5f\x30\x85\x1b\x2b\xc1\xd6\xab\xe9\x6c\x12\x66\xa5\x95\xea\x8a\x12\x70\x93\xf1\x7b\x28\x0c\xda\x5b\x3e\x51\xf0\x86\xcf\xc9\xcf\xa8\xae\xce\x48\xdc\xff\x7c\xca\x6b\x9c\x11\x6e\xac\x3d\x7f\xfd\x8a\xfe\xbc\x79\xa5\x16\x36\x8b\xbc\xd2\x54\x62\xa2\xf9\xfa\x19\x4f\xab\x05\xff\xb8\xf3\x84\xc5\xfd\xcf\x15\x28\x5f\xa1\x43\xc6\xfa\x42\xe0\x07\xe9\x75\x34\x78\x0b\xfb\x0d\x11\x79\xa1\x0b\xe5\x33\x3e\x04\x30\x88\x9b\xac\x48\x49\xf1\xd3\x30\xaa\x69\x93\x04\x20\x74\x96\x01\xd7\xcb\xe8\x39\xe0\x50\x1d\x8c\x83\x64\x33\x2b\xd5\xca\xd5\x2c\xfe\x38\x9b\xe1\xa4\x17\xa4\xb8\x54\xe6\x9f\x53\x22\x3f\x94\xea\x65\xef\xc6\xc3\x67\xd6\x9f\xc1\x5c\x6e\xdc\x32\x1d\x3b\x0f\x89\xc6\x6b\x9c\x93\x0e\xd9\x2b\x46\x08\x28\x2a\x4f\x2c\x89\xb7\xfa\x3e\x06\x59\xe9\x0c\x4d\x0f\x5d\x12\x5d\x09\x88\x6e\xf7\x8a\xca\x86\x1b\xfc\xe4\x77\x90\x8f\xfa\x72\xbd\x94\x97\xfd\xfe\x28\x60\x48\xda\x39\x39\x3b\x04\x2d\x2f\xdb\x2b\x35\xa1\x12\x4e\x92\x0a\xd2\xb7\x0e\xfe\xc7\x71\xa1\x65\xdc\x83\xcd\x6a\x2a\x97\x07\x37\x72\xc8\xd8\x22\xe7\x78\x73\x42\x65\x8f\x34\x0f\x20\xcb\x00\xa8\xcc\xea\x39\xf6\x6d\x27\x72\xf7\x1d\x24\x98\xc8\x8a\xb3\x79\x82\xd1\x7f\x1d\x1f\x7c\x38\x3a\xec\x21\xde\xca\xe5\x38\x1c\x8c\xe1\xf0\xc4\x77\xa0\x30\x42\x7d\x50\xda\xb2\x22\x06\x47\x94\x6f\x05\xdf\xab\x56\xab\x37\x4c\x85\xe7\xda\x9b\x11\x39\x12\x26\xb3\x81\x52\xd5\xc9\x1d\x65\xc7\x3d\x64\x11\x5c\x33\x0b\x1d\xd3\x6e\xae\xab\xca\xa3\xb6\x96\xfc\xf4\x4c\xd7\xaf\x93\x69\x62\x55\x8c\xcd\xaa\x04\x7b\xa2\x2a\x0a\x92\x25\x5b\x25\x95\x4a\x62\x9f\x2c\x97\xd5\x29\x63\x58\xb1\x89\xe6\xb3\xa6\x4e\xbb\x6f\xea\x58\x4d\x8f\x86\x93\x0f\x90\x72\x30\x37\x82\xf6\x90\x23\x76\xe7\xf1\x88\xfd\x78\xc4\xfe\x67\x1f\xb1\x15\x7d\x26\xe3\x10\x53\xc6\xd2\xf5\x93\xf6\x7f\xe1\xd1\x28\xc1\xd7\xe8\xd7\x70\x32\xf8\x82\xd1\xeb\xcf\x78\x34\xf2\x85\xeb\x59\x2a\xb6\xcf\x7e\x90\x90\x23\xfc\x41\x10\x0d\x70\x00\x65\x5d\x51\x7d\x6e\x11\x08\x88\x55\x79\x1b\x5c\xa0\x5f\xe3\x78\x88\x5e\x9f\x7b\x0f\xf9\x2d\x79\xc8\xff\x2f\xc6\x4d\x35\xef\x61\xc6\x62\xf3\x52\xe3\x3b\x22\xd5\x99\xd9\xec\x5d\xa9\xec\x71\x92\xc4\x46\xf4\xa0\x55\xfa\x8e\x1a\x21\xd0\x6d\x67\x2f\x5b\x49\xc9\xc6\x38\x8b\xa3\x34\xec\x4f\x28\x81\xcd\x02\xf0\x22\x41\x53\x76\xe9\x43\xf6\xa2\x59\x12\x5f\x84\x43\x9c\xa4\xa2\x56\x30\x49\x63\xbb\x6a\x3c\x99\x90\xaa\x84\xda\xb8\x03\x37\x8a\xe2\x21\xfd\x1a\x46\x83\x78\xaa\x42\x26\xc0\x58\x56\x0a\x7a\xe7\x9a\x85\x53\x4c\x16\x5b\x98\xa2\x3a\x4a\xf1\x20\x8e\x86\xb0\x3b\x86\xd1\xf9\x04\x67\x71\x04\xc3\x49\xba\x97\x73\xd0\xe7\xa8\x6a\xc7\x7d\xfe\x12\x6d\x88\xae\x28\x7a\x06\xd2\x36\x68\x80\x6f\x94\x97\x1c\x17\x55\xeb\xe0\x3d\xfc\x11\x09\x65\x9c\xc4\x51\x3c\x4f\x27\xd7\x10\x07\xc3\xb3\x0f\x93\x4f\x8e\xf3\x08\x1a\x06\x59\xe0\x3d\x21\xeb\xbd\xd5\x54\x1e\xd1\x50\xeb\x3c\x01\xa3\x9e\xd4\x7e\xd0\x7a\xaf\xa5\xc9\x8d\xa3\x34\x26\x5b\x17\x21\x8a\x12\x25\x8d\xea\x5e\x74\x11\x4c\xc2\xe1\x21\x2b\x5f\x52\x65\x1e\xee\x86\x0d\x83\xa1\x48\xf8\xfa\x1e\xcf\xc8\xbc\x9a\xc5\x87\xf4\x1d\xa0\x54\xa5\xbd\xaf\x40\x37\x99\xb5\x85\x72\x7e\x61\xa7\xf2\x0d\x7d\xae\xa8\x30\xcb\x40\xf3\xbb\x72\xe8\x14\x6f\x24\x4c\x7f\x21\xe8\x1e\x51\x2a\xc4\x42\x50\x53\xba\x99\x8d\x93\xf8\x12\xe9\xdd\x33\xcb\x6b\xdd\x61\xdd\xa4\x9f\xaa\x85\x4e\xfe\xc1\x52\xb3\x0f\xd2\x6c\x2e\x09\x98\xe7\x52\x21\xfd\x2c\x26\x06\x00\x6e\x51\x84\x12\x3d\xb7\x10\x6d\xf0\x24\xcc\x8a\x6c\x9c\x47\x1d\xf7\x43\x08\xf6\xdc\x53\xb9\x9f\x81\x2c\x20\xcf\x93\x4e\xe1\x24\xf1\xa4\xd4\x54\x7b\x53\x36\xed\x6d\x10\xcf\x58\x75\x1b\x1a\x5b\x3c\x64\x56\x6d\xb5\x7d\x4b\xc8\x65\x79\xc3\x35\x12\x34\xa3\x73\xfa\x8f\x0d\x2e\x6a\xcc\x3b\x19\x90\x02\x6f\xc8\x77\x87\x92\x89\xd6\xbb\x0f\xc2\x84\x16\xbe\x33\xc2\x04\x9c\x54\xea\xe4\x4c\xe6\x76\xa4\x98\xde\x03\x2d\xea\x34\xc8\xf5\x6c\x30\x1b\x25\xde\xca\x9d\x48\x2f\x5d\x44\x7b\x5a\x87\x04\xd1\xa1\x05\xdb\x1f\xce\xc4\xbe\x4a\xa4\x4d\x7e\x26\x64\x22\x9f\x45\x71\x19\x9f\x2a\xb7\x6a\x2e\x97\x96\x44\x5d\x7d\xd7\xf7\x6e\xf7\x8b\x76\xee\x8c\x1c\xa9\x98\xe0\x62\x22\x4a\xbe\x1d\x8a\x4f\x0b\x39\x36\x0d\xfe\x7f\x03\xd0\xf6\x86\x0b\x97\x8c\xe3\xab\xb0\x4b\xe2\x98\x64\xf1\x30\x46\x83\x09\x0e\xa2\xf9\x0c\x45\x00\x9f\x0c\xb0\x38\xb6\xe7\x0d\x95\x82\xbd\x63\xe5\x51\x24\xd5\x88\x28\xa2\x71\x7d\x2c\x89\x70\x74\x4a\x4b\x9f\x11\x21\x89\x54\xef\x22\x0a\x24\x1c\x76\x2d\x40\x5d\x17\xc8\xae\xfc\x09\x7a\x5d\xc4\x7c\x99\xf5\xd1\xd7\x18\x00\x13\xc0\xf4\xdd\x9c\x21\x54\x12\x2b\x7c\xc1\xe4\xc6\x33\x21\x94\x12\x11\x94\xd9\xd1\xc2\xe9\xe6\x3c\x24\x47\xba\xd0\xd4\x1d\x93\x3a\x8e\x39\xb7\xe6\x36\x77\xe4\x05\x08\x9d\x48\xa1\x2e\xef\x10\x35\x2d\x73\x0c\xf2\x2b\x65\x78\x24\xfe\x6c\x74\x4a\x4c\xa3\xfa\x05\x5f\xa7\x25\x59\xb7\xcc\xb5\xbc\x1b\x1b\x1b\xa8\x86\x7e\xfc\x11\xf9\xc6\x90\x10\x53\x72\x42\xdf\x97\xb4\x42\xaf\xf4\x71\x36\x05\xe0\x9c\xf1\x96\xbb\x4f\x82\x09\x2f\x20\xf2\x3f\x1f\xf6\x29\x1e\x8c\x83\x28\x4c\xa7\xfc\x18\x9a\xcf\x1c\x00\x40\xfe\xf0\xd2\x36\xd4\x81\xfd\x82\xf1\x4c\x24\x10\xe0\x9d\x5d\xfd\xe9\x73\x3a\x0e\x23\xd2\xd0\xd5\x20\x9e\xce\x26\xf8\x2a\xcc\xae\xbb\x6d\x38\x92\x91\x02\x84\x20\x4a\x64\x73\xf8\x82\xaf\xa9\xa6\x40\x8c\xa6\x32\x5e\xab\xab\x28\xc1\xd3\xf8\x02\xa3\x60\x32\x81\x5e\xa5\x15\x84\xaf\x06\x78\x96\x81\xd8\xcf\x5e\xa9\xe5\xb3\x31\xbe\x46\x11\xa6\x23\xd2\xc7\xac\xfe\x90\xf4\x78\x1e\x4c\x26\xd7\xa8\x7f\x0d\x43\x46\x86\x87\xe5\x02\x00\x9a\xf9\x95\x6c\x48\x61\x74\x5e\x2a\x2b\xfb\x40\xe9\x07\xad\x77\xe8\xeb\x57\x82\x6f\x35\x8c\x86\xf8\xea\x60\x54\x02\x3f\x45\x42\x6c\x9f\x56\xca\x30\xf9\x2f\xea\xe6\x06\xa1\x50\xd8\x17\x7c\x7d\x56\x15\x2b\xd1\xb4\x87\xb6\x29\x92\x94\xb7\x6c\x93\xff\xc6\xe4\x09\xa7\x4c\x32\xef\x03\x6a\x9c\x8b\xe2\xa8\x08\x4f\xa0\x36\xb5\x79\x34\xc9\x4c\x86\x6d\x15\xa8\x87\x0a\x51\x87\x80\x73\x74\x26\xc5\x99\xd6\x7b\x02\x58\x51\x45\x56\xd0\xa0\xba\x7d\xb2\xfb\xe9\xf0\xe0\xfd\xfb\xbd\x0f\x6f\x3f\x9d\xec\xed\x6f\x1f\x7c\x3c\x51\x8f\x47\x45\x66\xc0\x16\xaa\x34\x89\xe9\x41\x8e\x8e\xb6\x4c\x46\xf0\xda\x0a\xb2\x00\x6d\xa0\xd3\xb3\x57\xfa\xfb\x3d\xf0\x37\xe6\xaf\x8b\x2d\x55\x01\xb0\x3a\x9b\xa7\xe3\x92\x49\xf7\x4c\xc4\xd3\x4a\xef\x0d\x53\x5a\xf8\x0b\xbe\x2e\x5b\x63\x20\x01\x2e\x31\x78\x85\xc4\x4d\x01\x59\x4d\x97\xbb\xba\x8a\xa6\xc1\x4c\x63\x92\x21\x90\x2d\x30\x14\x20\x31\x42\x9a\xfa\x30\xed\x07\x33\x45\x75\xa1\xe8\xb5\x75\x57\x71\x2a\xb8\x02\xd7\x28\xff\x69\x8e\xc1\x7e\x30\x3b\x85\x6a\x21\x6c\xf1\x7c\x64\x4e\xa1\xf8\x99\xe2\x92\x2e\x1a\xd7\x1c\xe7\xd1\xd2\x32\x73\xac\x4b\xcd\x5a\x7c\x93\x93\x83\xad\x83\x2e\x27\x32\x34\x89\xcf\xff\xc3\x94\xaa\x63\x8f\x5c\x7d\x57\x49\xba\x80\xb2\x20\x75\x1e\x1d\xd9\xb7\xea\x34\x98\x95\x7c\xc6\x0a\xfc\x0f\xec\x17\x87\x72\x94\xc9\xd8\xb3\xa3\x5e\x38\x54\x3d\x6f\x04\x45\x7c\xc1\x28\x9d\x27\xa0\x27\xe6\xcc\x2a\x4c\x51\x9a\x85\x84\x1e\x28\x27\xc7\x43\x14\x8c\xc0\x43\x28\x49\xc2\x8b\x60\x62\xec\xb5\x1a\x4c\x32\x20\xe0\xf7\x4f\x97\x46\x38\x3c\x33\x51\x94\x5d\xaa\x0e\xa4\x3d\x80\x5e\x47\x7c\xf1\x7a\xcc\x70\xdd\x89\xfa\xe9\x06\xe1\x09\xd3\x33\x3b\x6a\x8c\x82\x49\x8a\xd5\x5b\x36\xe6\xf7\xb4\x70\x4c\x59\xfd\x1f\x7e\x60\x6d\xa2\x5b\xc0\x20\xf3\x02\x33\xae\x2c\x5a\xcf\xe1\xff\x95\x35\x9e\x3f\x40\xcd\x02\xe3\x58\x5c\x31\x80\x34\x0a\x53\x7a\x09\x15\xf5\x51\x32\x16\xbb\x7f\x98\x74\x5c\xfc\x7a\x06\xa4\x5e\x72\xfa\x52\x2e\x1d\x99\x51\x35\xf4\x1b\x2f\x23\xf7\x92\x9d\xbb\x82\x29\xa4\x9f\x75\x1b\x10\xdb\x87\x29\xc3\x9f\x75\x9b\xe0\x87\xba\x56\xe4\x8e\x8c\x05\xdd\xc4\x59\x16\x46\xe7\x6e\xd7\x5e\x60\x4c\x43\x25\xf7\x31\xda\x10\x4e\x6b\xaf\xac\x12\x32\xd4\xb3\xb0\x0f\xf2\x45\x2d\x62\x8d\xb2\x7e\x13\x94\xd7\x1f\xaf\xf5\x1e\xaf\xf5\xfe\xe1\xd7\x7a\x2c\xa4\x2f\x3b\xb5\xdc\x26\xac\xef\x22\x73\x58\x4f\xf2\x0b\x23\xf7\xc5\x32\x86\xb3\x7c\x49\xd7\xd9\xe1\x60\x73\x38\x4c\x61\xe8\xc4\xee\x16\x44\xa0\x96\x4a\xd1\x9c\x8a\x5f\xcc\xeb\xad\x42\x84\xaf\x30\x83\x50\x79\x08\xb2\x02\xd0\x4d\x95\xee\xf6\x4f\x9f\xaa\xe7\x03\x76\x3e\x7b\x6a\x2a\x89\xc8\xb6\xf9\x94\x5d\x5b\x29\xe5\x14\x5e\x45\x03\xf5\x70\x5f\x3a\x52\x2e\x8e\x98\xc7\x95\xc6\xd1\x98\xdc\x44\xc6\xde\xa1\x6a\xf4\x09\x45\x74\xdf\xe6\x3d\x4d\x1d\x9b\x85\xcb\x1e\x87\xff\xe9\xfb\x96\xb9\x3d\xf9\x74\x97\xc2\x42\x90\x47\x22\x02\x94\x7f\xfc\x11\x70\xa7\x8a\xa9\x30\x3a\x07\x6e\x5c\xd6\x20\xf2\xeb\x8b\x45\x39\x4d\x29\x44\xd5\x4d\xf9\xb6\x9d\x14\xd2\xd0\x24\x48\xa1\x99\xe3\x8c\x4c\xf6\x0f\x1b\x1b\xd6\x40\xf3\x3f\xeb\xc5\xea\x2a\xcd\xfd\xaf\x91\x14\x2c\xb5\x2c\x99\x13\x99\x2d\x49\x33\x94\xc6\xd4\xce\x71\x36\x03\xd6\x0d\x67\xe7\x20\xba\xce\xc8\x81\xbf\x82\xfa\x78\x44\x18\x00\x5d\xe2\xfc\x0a\x15\x46\x83\x2a\x19\x8d\xbf\x70\x54\xfa\xc1\x81\xf5\x8f\x3f\x22\xd7\xc8\x97\xad\xfa\xc8\xbe\x6e\x20\xa8\x3a\xfc\xa3\xbd\x9d\x8d\x29\xdf\x8c\xf0\x55\x86\x7a\x87\x1f\xd1\xe0\x7a\x30\xc1\x15\xd1\x4d\x18\x76\xb1\xd9\x40\x4f\xa0\xcb\xcc\x66\x69\x96\xc4\x03\xc2\xb3\x52\x3a\x3a\x56\x2b\xca\x31\x58\x2c\x13\xd7\x5c\x38\x3a\xc2\x48\xc3\x2c\x75\x53\x41\xb5\x22\xfd\x73\x0c\x2b\x25\x05\x9f\x68\xa6\x18\x83\x3d\x15\x00\x4c\x33\x36\x45\x17\x5b\xb2\xed\xa0\x3c\xf9\x7e\x4d\x4b\xa8\x9b\x8a\x14\xc2\xf7\x86\x15\xc9\x26\xd8\x7b\x55\x87\x44\x75\x06\xc0\x59\xc8\x3a\xe1\x76\x92\x7b\xce\xbc\x9c\xde\x64\x9b\xf9\x26\xf3\x86\xfc\x87\x54\x5d\xd3\x1e\x91\xa3\x15\xe5\xd4\x73\xca\x85\x9f\x3f\x57\xca\x89\xf5\xaa\x9c\xf4\xe1\x43\x30\x1c\x0a\xdb\x2e\x25\xf1\xa7\xf8\x6e\x4e\x8f\x72\x70\x50\x58\x2c\x37\xde\x82\xf7\x8a\xad\x38\x15\xe8\xc4\x48\xa8\x96\xbe\xb2\xdd\x5c\x8b\xc5\x70\x24\x5f\xe9\x5a\x29\xc9\x82\x40\xab\x60\x20\x5f\x08\x09\x75\x16\xfd\x12\xad\x45\x60\x42\xe5\x5c\x52\xe6\xa0\x9c\x33\xda\x4e\xa9\x56\x20\xe4\x37\x60\x23\xb2\xba\x9e\xee\x82\xc8\xbe\x8f\x49\x4a\x1f\x65\xdf\x7f\xba\xec\x2b\x4d\xda\x78\xc6\xde\xfb\xf2\xd1\xdd\xeb\x07\x91\x2e\xed\x86\xfd\x40\xb8\xde\xe2\x2b\xaa\xae\xce\x73\xdd\x3d\x9e\x06\x49\xb6\xcd\x0a\x4a\xb7\x5b\xef\xd5\x18\xa8\x95\xa0\x59\xde\x17\x43\xe7\xad\xbc\x16\x97\x60\xc7\x59\x12\x46\xe7\x37\xe0\xda\xe2\x7a\x4f\xa4\xe5\x7e\x10\xa9\x9f\x7e\x09\x26\x73\x7c\x83\x2e\xc8\x3f\xec\x3a\x84\x40\x1e\xe1\x04\x2f\xb8\x21\xad\xe8\xe6\x05\x10\xa5\x86\xe1\xa4\x8b\xc5\xd9\xb8\x02\x18\x11\x69\xbd\x42\x5b\xb2\xb7\x30\x50\xbb\xd1\x51\x86\x74\xd3\xfd\x20\x2a\x65\x71\x99\xa9\x8a\x40\x87\x43\x3e\x73\x95\x4f\xc9\x61\x45\x44\xea\x41\x9e\x88\xd2\x4a\x48\xd5\x37\x14\x22\xf3\xd3\x5d\xb1\xf5\xc7\x0c\xe2\x56\x98\x10\x59\xcc\xe5\x10\xc3\x7b\x74\x12\x33\xcf\x5e\xb5\x3b\x50\x9d\x41\x2f\x95\xed\xae\xf1\xf6\x84\x1c\x03\xdd\x70\x49\xba\xe0\x22\x21\x3c\xa5\x71\x36\x56\x73\x82\x97\xca\xd0\x08\xc3\x36\x4a\xb3\x30\x9b\x53\x81\xcb\x36\xff\x1a\xe2\x59\x9c\x86\x99\x8a\x25\x83\x2b\xd0\x03\x30\x83\x49\x88\xa3\xcc\xb4\xc4\x28\xdc\xb0\x65\x62\xc1\x73\x8d\xdb\x23\xb8\x2c\x46\xf6\xf8\x71\x15\x7c\xee\x55\xb2\x20\xbd\xd1\x3c\x1a\x82\x4d\xe4\x00\x27\x59\x10\x8a\xe9\xf7\x2c\x1f\x31\xb1\xcb\xad\xa3\x07\x5f\x42\x02\xaf\x5b\xac\x25\x36\xf2\x64\x36\x8d\x94\x5f\x8a\x6c\x2b\xbc\xd7\xb3\x58\x4a\xb4\x04\x74\x97\x36\xa0\xd0\xe6\x64\x8e\xbb\xf4\x1f\x2e\xe6\x1a\xd9\xde\xbd\xb3\xc2\x26\x5f\x4e\x0a\x04\xb6\x0f\x07\x88\x73\x42\xc4\x39\x24\x2a\x4d\xe7\x69\x06\x5b\x1d\x9e\xe2\x28\x13\x74\xd3\xbf\xce\x70\xda\x6c\x94\x99\x30\xfe\x43\xd9\x98\x48\x56\xee\xde\xa7\x2f\xb5\xe6\x8f\x57\xa7\x94\x8a\xe6\x51\xf8\xdf\x73\x8c\xc2\x21\x8e\xb2\x70\x14\xea\x9c\xb8\xd0\x5c\xf3\xd1\x29\x30\xc3\xd0\xa4\x9b\x6b\x06\xb0\xeb\x28\x7b\xd0\x2b\x93\x08\xf8\x18\x97\x82\x7e\x58\xae\x06\x19\x61\xac\x55\x3e\xbe\x1c\xf4\x9f\x77\x25\x02\x4b\x56\xe5\xa3\xe8\x0c\x82\x60\xef\x87\xcf\xba\x4d\x22\xba\xf2\xcc\xfd\x37\x67\x95\x76\xa1\x5c\xc9\x4c\xbb\xdb\x2e\x94\xb0\xed\x95\xaa\x84\x8f\x89\x7c\x31\x0a\x06\x59\x9c\x5c\x57\xa8\x42\x99\x0c\xec\x13\xc2\xa6\x89\xa8\x1f\x8f\x90\xe8\xcd\xc6\x06\x7a\x46\x23\x32\x3d\x83\x32\x4f\x56\x57\x51\x2f\x9e\x4e\xe3\xe8\xbf\x8e\x9f\x3e\x79\x62\x75\x5e\xfe\x62\x0d\x70\x9c\x4a\xcf\xc8\x30\x24\xf8\x59\xb9\x82\x94\x57\x38\x1a\xbc\xe8\x07\x29\xee\xb4\x8c\x0f\xd3\x61\xdb\x2c\x7a\x31\xfb\x32\x1c\x19\x2f\x07\xe1\x6c\x8c\x93\x17\x14\x72\xf9\xd5\xd3\x27\x37\x4f\x9f\xe0\x49\x8a\x91\xd2\x19\xaa\x30\xa7\x7d\xe1\xc3\xf0\x0c\xfd\xf8\x23\xfb\x50\x0d\xa6\x43\xd1\xb7\xcd\xfd\xad\xa7\x4f\x9e\xd0\x0f\xa5\x53\x8e\x73\x05\xe9\xa8\xc2\x33\xc1\x90\x7e\xa0\x88\xc1\x6f\x15\x9f\x33\x31\xca\x2a\x62\xac\x21\x1a\x0d\x03\x95\xfa\x49\x7c\x99\xe2\xa4\xfc\xf4\xc9\x13\x31\x62\x71\x9c\x55\x7b\xc9\xf5\x2c\x8b\xff\xeb\x98\x56\xbd\x81\xd3\x93\xba\xfd\x88\xef\xe8\xcf\xa7\x4f\x9f\x94\xf4\xe3\xd8\x13\x44\x35\x22\xc7\xe3\x38\xc9\x06\xf3\x2c\xa5\x6f\xc8\xb2\xe9\xa1\x0d\xc4\xeb\xbe\x52\x5e\x7f\x9a\x84\x7d\xf2\xa9\x3a\x09\xfb\xca\x7b\x50\x86\xf5\xa0\x53\xe4\x2b\x29\x55\x55\xde\x69\x10\x82\xc9\x79\x0c\x20\xc8\x8f\x57\x4f\x05\x16\xef\xe3\xf8\xcb\x7c\x86\xb2\xa0\x3f\xc1\x0a\x26\xc7\x6f\x0e\x7e\x63\x67\x3e\xf1\x6e\xef\xc3\x2f\x9f\x5c\xef\x8f\x3f\xbe\xf9\xb4\xbf\xf7\xdb\xa7\x9a\xef\x43\xdd\xf7\xa1\xe1\xfb\xd0\x74\xb6\xed\x6b\x47\xfd\x68\xb5\xa5\x7e\xb4\xda\x53\x3f\xf2\x36\xc5\xd0\xf4\xe2\xe9\x8c\x1c\x14\x27\xf6\x10\xb9\xa6\xd4\xa8\x35\x8c\xe7\x7d\x22\xf5\x93\x5a\xb2\x00\xb0\x58\x15\x0b\xa4\x5a\x2a\x84\x10\x4e\x10\x85\xe8\x35\x6a\xb4\x3b\xaf\x50\xf8\xfc\xb9\x06\x5e\xc8\x88\xe8\x35\xaa\x37\xd6\xad\x6f\xe4\x6f\x78\x1a\x9e\xa1\x0d\x02\xe3\x35\xaa\xbf\xd2\xbf\xd3\xab\xd4\x9c\x5a\x25\x5a\xad\x8c\x7e\x47\xb5\xab\x7a\xbd\x6f\xd6\x97\x8f\x37\x4f\xb5\x5e\xff\x1a\x4c\xbe\xa0\xb7\x3b\xa5\xc6\xef\xeb\x65\xbd\xb7\x57\x34\x44\xa2\xfe\x2e\x34\x5e\x2e\x35\x02\xca\x20\xa7\xfd\xf8\x4a\xff\x08\x86\x06\xa4\xcd\xab\x10\xfd\x8e\x4a\x57\xb2\x43\xec\x77\x43\xf9\xdd\x54\x7e\xb7\xca\x46\x67\x01\x4a\x29\xbd\x42\x3f\xff\xfc\x33\x5a\x87\x92\xe9\x15\xfa\x11\xd5\xae\x46\x23\x3a\x40\x9d\xa6\x51\x85\xac\x8e\xd3\x2b\x32\x90\xe9\x95\xf1\x89\x2f\x9e\xd3\x14\xbe\x5f\xbd\x7a\xea\xed\xd4\x74\x3e\xc9\xc2\xd9\x24\x1c\x80\x96\xc0\xee\xde\x15\x21\xe3\xe1\xe9\xd5\xd9\x2b\xc7\xb7\x16\xfd\xd6\x70\x7e\x5c\xa7\x1f\x5b\x67\x39\xad\xa7\xf3\x3e\x02\xf9\xa6\x82\xa6\xe1\x15\x1a\xc4\x93\xf9\x34\x4a\x35\xea\x57\x61\x12\x49\xa1\x34\x84\x5e\xfd\x44\x68\xa6\x56\xe7\x23\xc5\x1e\x6b\xf5\x5a\xcd\x1c\x5a\xb1\x92\xe9\x60\x95\x32\x98\x98\x56\x19\x7d\x25\xbf\xe9\x78\x7b\xaa\xd4\xd5\x2a\xf5\x8e\x52\xa5\xde\xf1\xd5\x69\xa8\x75\xd6\xcb\x48\xd6\x69\x58\xb3\x2e\xb8\x01\xad\x93\xe5\x8c\x54\x18\x5d\xa8\xa3\x45\x1e\x0b\x8f\xd8\xd5\xba\x32\x3e\x8c\x3c\x5b\xec\x55\x8d\xbf\x68\x68\x43\x9a\x3b\xa2\x1a\x7f\x64\x34\x56\x64\x58\x35\xd6\xa9\xd5\x5b\x30\xb6\x1a\x5b\xd5\x2a\x2e\x18\x60\x8d\xe5\xb2\x8a\x79\xa3\x0c\x97\x05\xa0\x07\xc6\x89\xcd\x09\x7f\xb8\x72\x32\x41\xc6\x00\x36\x96\xe0\x80\x50\xa5\x81\x7e\x47\xc3\x53\xf2\xbf\xab\x75\xf4\x3b\xba\x6a\x9c\x9d\x99\x0b\x09\xca\x86\xe8\xf7\x0d\x28\x78\x15\x5a\x05\x34\x26\x09\x3f\x6f\xe0\x4c\x2b\xf6\x95\xc3\x04\x0f\x68\xe7\x86\xe8\x68\x10\x47\x6c\x83\x91\xbb\xd2\x51\xef\xe0\x03\xd9\x23\x6a\x57\xb5\x5a\x05\xd5\xae\x6a\x75\xf8\x6f\x03\xfe\xdb\x82\xff\xae\x57\x80\x16\xc8\x7f\x1b\xf0\xdf\x16\xfc\x77\x1d\xfe\x5b\xef\x93\xff\x36\x3b\x72\x33\xfb\xe9\x27\x86\xd4\x4f\x68\x73\xfb\x98\x06\x64\x47\x54\x1c\x42\x44\x20\x48\xc2\x6c\x3c\xad\xf2\x32\xab\x12\x15\x52\x7a\x83\x89\x0f\x55\xfa\xa0\x48\x18\x55\x7c\x95\xd1\xe8\x01\xa2\xcb\x9f\x86\xf1\x11\x4e\x71\xd6\x45\x9e\x2d\x92\x0d\xc2\xf1\x97\x70\xc6\x2c\x7f\xe3\x11\x8a\x8e\x62\x38\x8d\x8d\x83\x14\xf5\x31\x8e\xc0\x3b\x80\xdd\x6f\x05\xd1\x10\x4c\xf8\x86\xe1\x10\x45\x71\xc6\xcc\x30\x6d\x52\xa0\xd9\x5c\x38\x24\x6e\x2e\xfa\xe9\x0b\xbe\x3e\x4c\xc2\x38\x39\xa2\x16\xc0\x1b\x1b\xf2\xbd\x93\x74\xb8\x59\x98\x31\xa7\x76\x07\x74\xf1\x8d\xff\x71\x83\xc3\x0d\x77\xf3\xf2\xad\x83\x3f\x7f\xc1\xd7\xbf\xc6\x09\x18\x31\x7e\xc1\xd7\xd5\x4b\xf2\xdb\x5d\xec\x38\xfc\x03\xb3\x52\x69\x78\xfe\x86\x30\x20\xb4\x8a\x5a\x79\xcb\x48\xf8\x01\x24\x30\x40\x36\x58\x3e\x72\x1c\x47\xf9\xcc\x1b\x7c\x8e\x3a\x85\x5a\x20\xfd\x4f\x07\x63\x4c\x8e\x1f\x88\x88\xd0\x8e\x3e\xa4\x47\xf1\x25\x81\x5d\xe2\xcd\x3c\x27\xbb\xf4\x4f\xb9\x7d\x50\xe1\xba\x87\x85\x37\xaa\x8c\xb3\xf2\xee\xd4\x5c\xaa\xd2\x44\x94\xa0\x43\x45\x0f\xfa\xf3\x35\xc3\x90\x3d\x3b\xa4\x10\xc4\xc8\x4e\x94\xa7\x83\xe4\x2c\x47\xfe\x14\x54\x4e\xa1\xce\x19\x1d\x59\x98\x71\xf6\xc6\xc1\x6a\xfc\x0c\x0b\x29\xfb\x89\x05\x1c\xa2\xe9\x98\x43\xa9\xa2\xfd\x03\x43\xfc\x5f\x02\x71\x2f\xe6\x6c\x16\x8e\xe2\x0c\x11\x92\xf4\x17\xca\xd4\x3d\x40\xdf\x02\x72\x21\x1f\xcf\xfb\x45\x20\x83\xf8\xc4\x61\x9e\x29\x7b\x1b\x7c\x90\x3b\x15\x93\xd1\xce\x94\x5d\x4c\x2d\xb1\xae\x15\x00\x4c\x19\x64\xf6\x7a\x01\xb6\xfb\xe1\x15\xb0\xed\x3c\x6c\x7f\xdf\x00\x26\x7e\xca\x06\x79\x55\x52\xc7\x57\x54\x63\xa8\x3b\x26\x1b\xc9\x09\x07\xd2\x62\xeb\xee\x67\xd4\x21\xfc\xcc\x98\x30\xb4\xb1\x81\x5a\x8b\x26\xed\xbb\x1b\x5a\x77\x9f\x3d\x23\xee\x5b\x33\x16\xad\xb3\x21\x39\x43\xbf\x13\x59\xc2\x5e\x44\x0b\xb9\xb9\x2a\xd3\xe5\xb3\x99\x30\xba\x78\xe7\xe0\x34\xd6\x6b\x3f\xb3\x21\x45\x25\xbf\x11\x4f\x92\xe5\xf0\x57\x1e\xae\xa3\x32\x2c\xc6\x47\x5f\x88\x3a\x2e\xe2\x85\x23\x23\x6f\xe6\x5f\x39\x44\xe3\x65\x27\xf7\xcb\x99\x5a\x4e\x70\x8b\x10\x7f\x8d\x5a\xe0\xc8\x42\x1f\xf2\x68\x5f\x9f\x8b\x53\x0e\x81\x49\x9a\x4b\x76\x24\x07\x98\x2e\x74\xeb\x6b\x88\x90\xa2\x2e\x5c\x7b\x96\xd2\x19\xfa\xdd\xbf\x38\x3d\x7f\xba\xf0\xed\x5e\x81\x26\x02\xcd\x53\x7d\x29\xba\xe7\xc0\x2b\xc9\x56\x94\xe9\xc1\xd1\x20\xb9\x9e\x51\xcb\x58\x55\xce\xdb\xaf\xa0\x78\x34\x4a\x71\x66\xcd\x0c\x5d\x23\xc3\xb8\x27\xea\xc9\xc2\x15\x7b\xaf\xae\xc8\x13\xa2\xfc\x59\x97\x3f\x1b\xf2\x67\xb3\x02\x2c\x46\x3d\x65\x68\xb8\x0e\xf1\xb2\xb8\x12\xae\x79\x19\xcc\x50\x23\x1a\x82\xec\xd9\xca\xc6\x1e\x21\x86\xd0\xf7\xfe\x29\x05\x43\xe4\x17\x73\x48\xb5\x6f\x7a\xd9\x66\x4e\xd9\xa6\xf3\x48\x54\x64\x08\x75\x5a\xad\xe8\x04\xaa\x3f\xd6\xf5\xc7\x86\xfe\xd8\xac\x08\x85\x85\xb5\x79\xaf\xae\xa2\x3d\x72\xf2\xfd\x2e\xc6\xc8\x3d\xe9\xda\x30\x39\x67\xbd\x82\xee\x46\x6e\x2e\xa2\x61\x07\x82\xc2\x92\xb5\x63\x60\xdf\x62\x16\x2b\x14\x2e\x24\xa9\xa8\x4e\x30\x75\xe8\xb8\x6a\xca\x60\x9d\xc1\xeb\xdf\x35\x66\x5b\x73\x69\x80\xd2\xba\x39\x1d\x46\x2d\x6b\x7e\xa0\x56\x43\xaf\xd5\x30\x6b\x39\xb5\x4d\x69\xd3\x9c\x4e\xa3\x56\xd3\xa5\x86\x7a\x67\x9c\x1d\xdc\x47\x7f\x75\x0b\x74\x9d\x18\x8e\x1c\x67\x1c\xb1\xff\xd2\x51\xdd\x40\xf5\x57\xec\xe7\x6b\x3e\x43\xec\x85\x67\xdf\x85\x39\x0e\x47\x19\x50\x7a\xc5\xa3\x28\xcb\x9d\x38\x8e\x7a\x46\x26\x4f\x51\xd7\xd4\x84\xe4\xf5\xbb\xa2\xe8\x2a\xa5\x75\x4b\xee\xfa\x5d\x51\x6a\x95\xd2\x86\x29\x75\xfd\xae\xe8\xaf\xd2\xa6\xf2\xda\xda\x86\x9f\x3f\x77\x6d\x00\x80\x5c\x5d\x47\xae\xee\x41\xae\xb1\x00\xb9\x66\x2e\x72\xb5\x5b\x22\xd7\xd0\x91\x6b\x78\x90\x6b\x2e\x40\xae\x96\x8b\x5c\xfd\x96\xc8\x35\x75\xe4\x9a\x1e\xe4\x6a\x0b\x90\xab\xe7\x22\xd7\x58\x88\x9c\x93\x74\x3f\xce\xc0\x86\x28\xcd\x82\x0c\xdb\x05\x80\x9d\x64\x35\x47\xc7\x80\x65\x64\xa6\x1e\x0d\xbe\x90\xb9\xc8\x1a\xae\x2f\x64\x20\x32\x53\x3b\xee\x54\xa2\x38\xd7\xd3\x02\xde\x07\xcb\xa7\x44\x4f\x1e\xca\xda\x31\x4f\x2d\x8e\xe5\x63\x1e\x5b\xec\x15\xa4\x9d\x5b\xe4\x12\x2a\x17\xa3\x04\xb1\x7e\x38\x76\x75\x3f\x76\xf6\xfa\xb1\xb0\xb3\x96\x90\x8e\x5d\xed\x36\xd8\x35\x14\xec\x1a\x7e\xec\xec\x05\x64\x61\x67\xad\x21\x1d\xbb\xfa\x6d\xb0\x6b\x2a\xd8\x35\xfd\xd8\xd9\x2b\xc8\xc2\xce\x5a\x44\x3a\x76\x8d\xc5\xd8\xd9\xd4\x8a\x79\x60\x6b\xb7\x5c\x42\xb7\x61\xc7\x3a\x32\x85\x1c\x6b\x39\xe9\x9b\xab\x63\x55\x59\xa2\x4f\xd3\x27\xfb\xb0\xa3\x70\x17\x35\xda\x9d\xd5\x66\x83\x69\xa0\xcb\x2e\x55\x30\x97\x58\x84\x80\x94\x32\xc7\x61\xa6\x1a\x5e\x49\x59\xc2\x27\x04\x39\xbc\x47\xc1\x00\x0b\x1d\xb1\x00\xf2\x9f\xf8\x2a\x98\xce\xc4\x49\x59\x7e\xe0\x73\x4a\x61\x65\xf8\x2a\x53\x6e\xb7\xab\x9b\xdb\xc7\x55\x76\x8e\x28\x4d\xb9\x45\xfa\x17\x7c\x5d\x41\x83\xd1\xb9\x90\xe6\x25\x94\xd9\x24\x20\x48\x5c\x65\xc8\x84\xc2\x24\xfc\x92\x6c\xc7\x05\x88\xe9\xb4\x7b\x0e\x25\xf6\x27\x1a\x35\x75\x17\x4f\x66\x38\x29\x6d\x6e\xd3\x6b\x7d\xaa\xb3\x7f\xfa\x84\xd9\xac\xa8\x4d\xbe\x7a\xfa\x14\x22\xe0\x82\x01\x89\x66\x55\xd0\x6d\x37\x2a\xdc\x2e\xa1\xdb\x06\xdb\x11\xc5\x32\xa1\xdb\x6e\x55\xa4\x49\x42\xb7\x0d\x2e\x8c\xd3\x61\xfb\x59\xb7\x53\xbf\x39\xab\xb4\x1b\x77\xb2\x16\xf9\x96\x66\x22\x0f\x66\xcc\xf1\x0d\xcd\x32\xe8\x4a\xf8\x09\x31\x03\x0a\xd2\x3c\x1a\xc4\xd3\x59\x1c\x41\xc8\x75\xf2\x6d\xf5\xe9\x13\x31\xef\x93\xb0\x5f\x65\x45\xbf\x7e\x55\x0d\x00\x84\xd3\xe7\x3d\x1b\x77\x04\x29\x96\x56\x1d\x41\x8a\x95\x6f\xbf\xc6\xc9\x10\xdc\xd2\x45\x01\xf1\x46\x85\x30\x1f\x81\xbd\x18\xd0\xfa\x26\xbf\xe5\x91\x30\x9d\x9f\x35\xcc\x30\x78\x56\xf5\xc8\x42\x55\xde\x7f\xcc\x46\xeb\x00\x05\x47\x83\x2a\x79\x30\xb0\xee\xb4\xc4\x57\xfa\x98\x67\x88\x22\xbe\x6c\x5f\xcc\xde\x6d\xed\xc8\xcb\x26\xfa\xec\xbc\xc1\xea\xa7\xd4\x3c\x8f\x2c\x2b\x7e\x8b\x95\xe1\xe9\x6c\x12\x64\x2e\x06\x25\x82\x4c\xff\x19\xb1\x80\x3c\x5c\x83\x0a\x4e\x05\x82\xd7\x81\xde\x2f\xfc\x03\x57\x79\x80\xc9\x2e\x6a\xa1\x52\xbd\xb1\x8e\xfa\x61\x96\x96\xf3\x00\x86\x17\x0e\x78\x7b\xbf\xdc\x16\xdc\xa7\xed\x0f\xbd\x4f\xbf\xed\x1c\x1c\xed\x7f\xda\x3f\xd8\xda\x46\x9b\x10\xda\x20\x0b\xa2\x0c\x25\x78\x96\xe0\x14\x47\x59\x18\x9d\x73\x45\x0c\x21\xc3\x69\x3c\x94\x7d\x77\xc2\xdc\xda\x2e\x04\x93\xb1\x53\x0b\xa6\x72\x29\x68\x98\x1c\x89\x47\x37\x45\x39\x2e\x09\xe5\x6c\x52\x74\x7b\xe0\xf6\x3d\x4f\xc0\xe0\x41\xe4\xf8\x50\x8b\x68\xc5\x95\xde\x09\xba\x27\x73\x80\x4e\xc6\x98\x8c\x7a\x16\xa3\x39\x73\x13\x20\x2c\x00\x91\xc2\x00\x5a\x03\xb9\x2a\x1f\x06\xa3\xf3\x2e\x90\x2e\xc7\xb5\xac\xee\xa8\x16\xb6\xb0\x5d\xa4\x14\x36\x23\xbf\x30\xf2\x4d\x86\x0b\x7d\x6a\x8f\xa9\xe0\x4e\x48\x8f\x20\xff\x05\x5f\x57\x9d\x65\xb9\x67\xe8\x60\x74\x8e\x4a\x07\xd0\x4a\x30\x29\x43\x9d\x81\x6b\xf0\x0a\x8e\x81\xde\x16\x8f\x23\x4a\x27\xf4\x86\x90\x08\xef\x1d\x21\x94\x41\x5e\x9f\xc8\xb9\x22\x1c\xf8\xbf\xeb\x52\x82\x5d\x00\x69\xd2\x82\xba\xc7\xf3\xab\xe7\x2a\xdd\xa6\xb7\xe9\x30\xc7\x49\x89\x5d\x9e\xc1\x10\x56\xd0\x9f\x28\xbc\xe8\xa2\xf0\x42\xf2\xc6\x1b\xcd\xf4\x40\x9b\x6f\x1d\x52\x57\x0b\x0b\xc5\x24\x07\x53\x03\xa0\x26\x0e\xa1\xf5\xd9\x8d\xb3\xbe\x56\x1d\xb2\x87\x29\xa1\x15\xa4\x27\xcf\x42\x7c\xa4\xa7\xfb\xa5\xa7\x2d\x7c\x5f\xf4\x24\x20\xdd\x8d\x9e\x74\x3e\x7d\x0b\x7a\xda\x8b\xc2\x2c\x0c\x26\xe1\x1f\x38\x45\x01\x8a\xf0\xe5\xe4\x9a\x61\x38\x64\xc3\xb1\x98\x96\xf8\xae\x71\x35\x8a\x93\xe9\x7e\x3c\xc4\x68\x9b\xfa\xaa\x41\x98\x66\xc9\xe9\xe2\x44\xa5\x53\xb0\xae\x06\x37\x3f\x4e\xb5\x62\x93\x71\x93\xe1\x77\x47\xb2\xf7\x46\x56\x25\xfb\x83\x8b\x53\xdc\x92\xe0\xc2\x28\xd4\x2c\x6c\xc4\x34\x29\xe4\xe2\x50\x51\x6f\xce\x66\x84\x16\x60\xb4\x78\xba\xe9\xd4\x71\xcd\x40\x86\x78\x43\xfc\xe4\x9b\x22\xa5\x41\xfb\x54\x9c\x11\xc9\x99\x1a\xd6\xc7\xc9\x94\x4e\x7b\xe0\xd2\xdd\x50\xfa\x96\x24\xb5\x21\xc9\xeb\x95\xab\x24\xb5\xa3\x01\x5b\x19\xe7\x59\x3c\xa4\x84\x4e\x3d\x00\x5c\xfd\x00\xfb\xa2\x52\xe1\x85\x03\x36\x3a\x3a\x1f\x86\x58\x0e\xa9\x68\x09\xb4\x67\x77\x24\x1f\xb6\x04\x6d\xdc\xb4\x19\x4e\x8a\x18\x51\x51\xa3\xa2\x61\x90\x05\xa8\x0f\xb2\x97\x5e\xc2\x23\x8f\x01\x68\x9a\xe9\x82\x7b\x3b\x9b\x80\x0f\x71\x02\x73\x39\x88\xa3\x41\x82\x33\xfc\x82\x0d\xc7\x24\x3e\xd7\x98\xb2\x72\x2f\x75\xb4\xdc\x58\x43\x3c\x0d\xc0\x9c\xba\xb7\x30\x9e\x82\x87\x0a\x4b\xc1\xc3\x25\x36\xbd\xaf\x29\x73\x85\x21\x40\x99\xb2\x93\xf0\x06\xde\x06\x6b\x40\x01\x5f\x60\xe7\x52\xf8\x93\x80\x45\x83\x66\xb1\x60\x04\x61\x74\x7e\x0f\xdc\x44\x76\x7e\x83\x93\x07\x83\x5f\x5a\x21\x6d\xae\xe8\x64\x52\xa4\xde\x25\xc7\xdc\x4b\x61\xac\x64\xd7\x88\xf2\x4a\x87\xce\xc3\x3d\x70\x34\x74\xcd\x7e\x00\x5f\xd4\xea\x2e\x9a\xa2\xed\xa1\xe0\x22\x08\x27\x41\x7f\x82\xa9\x19\x62\xea\xdf\x16\x3f\xf1\xce\x14\xa6\xaa\x9d\x30\x62\x1b\x5f\xee\x3e\xc5\xe0\xea\xfb\xcc\x87\x38\x63\xde\xd1\x34\x68\x1a\x85\x24\x77\x0d\x14\xa6\x08\x8f\x46\x78\x90\x85\x17\x78\x72\x8d\x02\x34\xc4\x69\x96\xcc\xe1\xb9\x82\x12\x1c\x0c\x5f\xc4\xd1\x00\x17\xda\x67\x8a\x52\x2f\xa0\xf1\x50\x34\x4c\x81\x3f\x34\x25\xf3\x91\x2c\x15\x27\x62\x51\x65\x59\xea\x17\x15\x17\x93\x3f\x2f\x5a\x9c\xfe\x77\xe4\x5c\xcc\xa1\x90\x5e\x22\x1c\xe5\x02\x40\xb9\xab\x45\x2b\xea\xb8\x28\x59\x82\x21\x43\x3c\x24\x82\x2a\x5b\x70\x78\xc8\xe2\x65\x72\x4e\xbd\xa3\x4c\x88\x73\xf1\xd9\xb5\x17\x2a\x9b\xeb\x8d\xf5\xd5\x66\x43\xfd\x44\x55\x22\xae\x2f\x86\x1c\xd4\x45\x75\xed\xab\x2e\xff\x76\x51\xa3\xc8\xd9\x29\x75\xaa\xb2\x83\xc5\x8a\x6c\xe4\x5d\x9b\xfc\xd4\xc2\x46\xfa\x64\x8c\x15\xa1\x80\x25\xda\x0a\xd0\x18\xb4\xc6\x44\xc8\x2c\xb0\x14\xb9\x08\xbb\x19\x71\x7c\x20\xc0\x00\x5f\xd6\x44\x68\x62\xeb\xda\xd1\xa1\x6f\x70\x58\x62\xd6\xde\xb6\xca\xd3\xd0\x91\x5b\xb2\xad\x77\x95\x69\xf5\xba\x5e\xbf\x29\xf2\x27\x3e\xa5\x78\x82\x07\x19\x6d\xf8\x38\x4b\x82\x0c\x9f\x5f\x97\x7c\xe6\xda\x8a\xf6\x19\xc4\xc5\x0d\xb4\x42\x59\xe9\x8a\xd7\x3c\x8c\xcd\xc6\x61\x90\xa6\x84\x4d\xbc\x09\x52\x3c\xd4\x3c\xe6\xd4\xbf\x7c\xe3\x30\x06\xea\x18\x27\x70\xe0\x22\xbb\x9a\x1f\x52\xfe\x22\x37\x73\xfb\xb1\xfb\x8c\x1c\x1b\x75\x1f\x52\x8c\x9c\x54\xc6\x66\xdf\xb0\xe4\xd9\x8d\xca\x20\x60\xee\x79\x10\x17\x37\x14\xc5\x0a\xf2\x5f\xe0\x98\x63\x50\xf1\x58\x7a\x32\xb2\xef\x5a\xfd\x37\xee\x73\xee\x84\xb6\x7e\x53\x54\x41\xb9\x37\x46\x26\xe6\x8e\x09\x35\xd9\xb6\xca\x25\x4b\x65\xa6\xe1\x75\x5f\xbd\xe9\x3a\xec\x34\x4b\x70\x30\xbd\x95\x2a\x1b\x64\x28\xa6\x7c\x56\x6d\xf0\x9b\x8d\x17\xfd\x90\x1a\x6c\xeb\x27\x1a\x2a\x9d\x40\x18\x6b\x45\x33\x5d\x47\xa5\x66\x43\x57\x4c\x2b\x0a\xdf\x63\xc0\xcf\x50\xfb\x9a\x2f\x73\x3c\x42\x76\x1c\x7b\xad\x6b\x87\xe5\x22\xe2\x2c\x48\xe0\xb8\xe5\x12\x10\xed\xed\x0d\x8e\x37\xd2\xba\x8a\x0b\x8d\x3f\xfc\xb0\x32\x9a\xcc\xd3\xf1\x4a\xb1\x6d\x8e\x42\xf1\x6d\x74\x62\x98\xbb\xa8\x9e\x37\xaf\x70\xae\x85\xac\xa6\x33\xf5\xb6\x54\x55\x9e\x7f\x9a\xd2\xb3\x6f\xaf\xca\x7e\xfc\x79\xb3\x98\x42\x34\x8f\x1d\xa8\x67\x51\x89\xd2\x86\x72\xbb\xc9\x0e\xda\x96\x73\x30\x7b\xaf\x2a\xbd\xf3\x14\xf4\xaa\x8a\x72\xca\x93\x73\x49\xf9\x7a\xe9\xdd\x74\x53\xef\x91\x53\x21\x68\x66\x96\x91\x0a\x7e\xa0\xea\x6f\xb0\x1f\xf2\x99\xe2\xdb\x1d\xe8\x61\x7b\x6f\x7a\x96\x2a\x9a\x73\x94\xf0\x82\x7a\xed\xdc\x46\xf3\x2c\x61\xe4\xea\x0a\x45\x5d\xae\x68\x52\xea\xdd\x4a\xe3\x2c\xa6\x53\x1e\x90\xfe\x67\x4e\xa7\xd4\x04\x2f\x39\x9d\x4e\xc5\x6f\xc1\xe9\x14\x75\xef\x30\x9d\x79\x0a\xdf\x62\x57\x07\xdf\x74\x3a\xef\x3c\x5d\x39\x4b\x60\xc1\x7c\x99\x7a\xd3\x9c\x49\xa2\x9b\x89\xd0\xf3\x0e\x5c\x62\x1d\xb3\xba\xbe\x40\x1b\x28\xbc\x50\x67\x2b\x6f\x8b\x60\x3b\x26\x8d\x2b\xdd\x1b\x07\x61\x04\x29\x4f\x7c\x77\xad\x6f\xc0\x6e\xe0\x13\xef\x3c\xda\xf0\x07\x1f\x30\x55\x6c\xda\x0e\x42\xea\x5a\xc4\xa0\x0c\x8d\x6c\xcc\xd8\x25\xc4\x9d\xe8\xab\x3c\x8e\xf2\xa6\xc7\xb7\x03\xe3\x24\xa4\x34\xa1\xcd\x1d\xe9\xd5\x9b\x9e\x63\xef\xb1\xc1\xd3\x26\x0e\x45\xf8\xcf\x8c\xab\x31\x28\x95\x06\x19\x33\xea\xae\x9a\x75\x2c\x18\x06\xcd\x52\xe9\x48\x68\x45\x98\xb0\x14\x73\x19\x09\xe9\x9c\x10\x39\x6f\x48\x98\x5d\x16\x01\xc2\x7e\x5e\x8e\x31\x8b\xbc\x4f\xf1\x83\x40\x9e\x69\x01\xe4\xec\x85\xe1\x2e\x48\xfe\x60\x2a\x99\xa8\x43\xbd\x01\x20\x3d\x1e\x74\x41\xb8\x36\x98\xb2\xac\x3a\x19\x48\xaa\x00\x2d\x33\x79\x1d\x8a\xd7\x16\xda\xe9\x00\x8b\xcc\x1b\x12\x75\x21\x79\x0c\x67\xa5\x10\x2b\x34\x39\xe2\x95\xc7\x9c\xf5\xb7\x83\x23\x38\x2f\x33\xa2\xb3\xcb\x5c\xc5\x09\xf4\x4b\x2a\xba\x2b\x48\xeb\x57\x45\x36\xeb\x12\xfa\x19\x1e\xaa\xaf\x4b\xc9\x1c\x5d\x27\x66\x47\x78\x8a\x41\x0a\x87\xdd\x95\x92\x00\xbb\x8a\x82\xd3\x3e\x38\xb4\xc3\x6b\xbb\x3a\x97\x60\xf1\x05\x0f\x3b\x4f\x99\x29\xcd\x27\xcf\xf1\x16\xa6\x80\xde\x0e\xa8\x9e\x3b\x0b\xd7\xed\x10\x17\x58\xb7\x62\x9f\x7a\x5c\xb7\x8f\xeb\x16\xdd\x7e\xdd\xde\x65\x75\x80\x85\xf0\x38\x4c\x97\x5e\x1b\x4e\x4c\x18\x45\x03\x17\xf9\xed\xe0\xc8\xcb\x01\x54\x0f\x32\x8b\x03\xdc\x95\xed\x38\x31\x3b\x91\x43\xd3\xc7\x83\x78\xca\x96\x0e\x61\x0b\x61\x3c\x4f\x8b\x33\x0f\x31\x58\x45\xd9\x83\x20\x25\xde\x8d\x92\x17\xf7\xa5\x3c\xa0\x40\x44\xe2\xd2\x92\xcb\xc3\x7f\x1c\xc7\x29\x46\xd3\xf0\x8a\xc8\x42\x8e\xfe\x81\x27\xa8\x2d\xa4\x21\x95\x10\x99\x14\xe6\x23\xbb\xf8\x02\xa4\x53\x72\xd2\x49\xe7\xfd\x14\xff\xf7\x1c\x47\x99\x53\xc5\x80\x74\xd1\x4e\xc9\xea\xa1\x8f\xa2\x57\x35\xa8\xa2\x64\xcc\xca\x62\x55\x3f\xd9\xd9\x5c\x58\xb9\x62\x24\xc9\xd5\xe6\x8c\x94\x44\xfe\x60\x02\xa5\xf5\x78\x78\x86\x7e\xdf\xa0\xf5\x4e\xc3\xdc\xd0\x25\xf2\x37\x37\x81\x7e\xd3\x63\xe5\xb5\x80\x26\x8a\x68\x7b\x18\x0c\x87\x64\x02\x17\x28\x40\x66\x90\xe5\xaa\x57\xa5\xff\xba\xd5\x1f\x87\xef\x7a\xc7\xe8\x7f\xb5\x57\xd7\xd0\x8c\x01\x4d\x99\x2e\xcf\x05\xf3\xf0\xcb\x20\x5d\x03\x39\x79\x16\x0c\xab\xfc\x29\x47\x36\x3e\x0c\xf8\xf5\xf3\x3c\xe5\xa1\xf3\x45\x20\x14\x66\xae\x0c\x71\x93\x05\x1e\x4b\xd9\x5f\x01\x64\xf5\xf6\x99\xa0\xe5\xac\xe4\xd6\xe3\xb1\x10\x50\xca\x7d\x24\x00\x4a\x45\x30\x4b\x32\x28\x10\xce\xf2\x81\x8f\xcd\xe2\xf0\x25\xc6\x95\xfc\x92\xd7\x6b\x15\x23\x6e\x96\x76\xc1\x1c\x0c\xcd\xcb\xb5\x5b\x33\x10\x51\x8d\xc6\x3a\xd9\x50\xc6\xcb\x17\x33\x64\x1e\x65\x82\x76\xc0\xaf\xc8\x86\x1a\x31\x82\xb5\x80\xd2\x17\x2f\x68\xca\x69\x11\x61\xe5\x5f\x46\x01\x57\xb3\xf4\x5e\x88\xb7\x6b\x87\x5e\xa0\x99\xde\xe0\x2b\xa1\x17\x88\x80\xa2\x61\x21\x7d\x5d\xac\xf7\xcc\xc1\xc5\x7a\x0f\x6e\x2d\xda\xdb\x85\x98\xe5\x22\x95\xe6\x87\x2f\x90\xec\x47\x6f\x13\x85\xe8\xb9\xcf\x2d\x5f\x85\x4e\xc3\xdc\x2b\x6f\x72\xa4\x57\x03\x3b\xb4\x21\x6d\xdf\xf9\xe1\x5f\x05\x5d\xd1\x51\x72\x99\x21\x6c\x0e\x87\xee\x41\x80\xb9\x1e\xc4\xd1\x20\xc8\x38\xcc\xc2\x1a\x98\x8f\xd1\x4c\x30\x14\x58\xb2\xe3\x60\x48\x03\x19\xb1\x85\xfa\x6d\xb8\xcc\x3c\x32\xf9\xcc\x37\xe1\x08\xd0\x6c\x81\x2b\x77\x28\x67\xb2\x04\x17\x1f\x78\x8b\x33\x2d\x71\xb1\xb2\x88\x21\x06\x2c\x9a\x04\x69\x06\xcf\x8b\xd7\xb4\x14\xaf\x4f\x4b\xfa\x72\x7e\x81\xea\x65\xea\x62\x76\xc6\x9c\xc1\x5c\x9e\xc4\x54\x70\xf0\x53\x8c\x04\xb7\x61\xae\x41\x65\x33\xa5\xdb\xe6\x92\x7a\xfe\xbf\xe2\x22\xc8\xe5\xa2\xe0\xbe\x59\x70\xdd\x2a\xe4\xdd\x03\xdd\x9f\xd1\xff\x7e\x3c\xc4\x37\x54\x3d\x78\x22\x4e\x6b\xf4\x52\x04\x4e\x12\x4a\x77\x7a\x6f\x7a\x3e\x28\x6c\xae\x6e\x04\x7d\x11\x58\xa6\xb0\x61\x43\x04\x92\xf7\x10\x38\xf8\x11\xb0\x01\x50\x0c\x27\x0d\x02\x27\x98\x02\x66\x15\xe3\x54\x47\xdb\xb6\x9a\xb8\xd1\xbc\x11\x96\x30\x0c\xa4\x13\xad\x7f\xec\x29\xd6\x87\xf9\x36\x80\x39\x01\xce\x74\xfb\x50\x87\x1f\x27\xc8\xcd\x64\x04\x34\xb5\x28\xd2\x15\xbb\xe4\xfb\x14\x6c\x3f\x3d\xf8\xcb\x89\xb5\x0f\x03\x96\x2d\x29\x97\xb4\x75\xe3\x12\xef\x89\x81\x40\x85\x2d\x11\x34\x1a\x70\x2a\x37\xee\x66\xdc\xd2\xfe\xea\x4f\xf9\xcd\xeb\xd6\x2b\x65\xf4\xd3\xea\xd2\x18\x08\x55\x8b\xe7\x2c\xf3\x0e\xe3\x19\x0a\x32\x34\xc1\x84\x0b\xc6\x11\x5f\x01\x2c\xcb\x07\xb5\x04\x85\xfd\x1a\x18\xae\xcd\xb7\x90\x38\xdf\x4c\xc3\x88\x1a\x89\xb2\x43\xbc\x15\x2e\x51\x7f\x64\x95\xe8\xf4\x29\xf8\x53\x42\x9a\x82\xfd\x31\x3d\xf2\x86\x17\xe8\xc7\x1f\x9d\xfa\x78\x33\x50\xc7\xe1\xad\x74\x19\x12\x13\x5d\x99\xe2\x3d\x9f\x9b\xcd\x16\xbd\x92\xf6\x8b\xa4\x52\x24\x11\x86\xd2\xec\x95\x83\xa0\x79\x73\xf7\x4b\xc8\xab\xab\xe4\x20\x43\xd3\x7d\xf9\x44\x2e\x90\xd7\x99\xe9\x17\x48\xe0\xf0\x7b\xa1\x0e\x82\x5f\xc5\x53\x1b\x41\xdf\x29\xf9\x56\x97\xf1\x0f\xb7\xac\x1e\x16\x6f\x67\x7b\x20\xf9\x2d\x98\x01\x2a\x1f\xb9\xda\x5b\x64\xf9\x77\x47\x4b\x05\x30\xbd\x63\xb2\x87\xdb\x0c\x05\x0d\xe2\xc9\x04\x53\xfa\x8f\x47\x5c\x34\x00\x51\x13\x43\x2e\xbd\x3c\xd1\x43\x11\x45\x15\x27\x6f\xb2\x8d\x26\xc1\xa5\xf2\xca\xe9\x97\xe8\x76\xfd\xa0\x0e\xe8\x42\x48\x29\x52\x5b\x5e\x3c\x42\x86\x07\xc6\x05\x69\x7d\xb2\x3e\x2d\x73\x5c\x1f\xa0\x34\x98\x50\xec\xe1\x07\x00\x03\x95\x64\x40\xc3\x8f\xe2\x24\xbc\xa0\xb2\x0a\xe7\x18\x4e\x80\xfc\x2a\x55\xca\xf9\x8a\xe5\xa0\x1d\x6b\xb5\x98\x5c\x73\x9b\x9e\xe5\xcb\x37\x83\x31\x9e\xde\x0e\xae\x5b\xe0\x64\x2a\x73\xb0\x98\x1e\x29\xf0\x9c\x20\x68\x4e\xc6\x1b\x99\xb3\x91\x9e\x62\xa8\x88\xc5\xdf\x9a\x62\xd8\x20\x8e\x2e\x70\x92\x69\x32\x2c\xcd\x76\xc7\x8d\x29\xc1\xe2\x93\x5a\xff\xf9\xdd\x56\x0f\x69\x15\xdd\x79\x55\xbc\x2c\x68\x0f\xb3\xd8\xc5\x4a\x47\x6d\xf1\xb1\x4e\x78\x37\xa9\xf8\x18\x76\xa2\x41\x24\x92\x58\xcd\xe2\x34\x0d\xfb\x13\xec\x5f\xb1\x8e\xa6\x96\x73\x6e\x92\x03\x65\xdb\x83\xd2\x6f\xfc\x04\xfe\xa7\x05\x05\x09\xf5\x39\x59\xc1\x5d\xe5\xb7\x74\x78\x72\x56\xfa\x82\xaf\xbb\xba\x5f\x94\xb3\x98\xe1\x29\xe5\x2e\x44\x96\x71\x17\xfe\xbb\xa0\xa0\x58\x95\x5d\xdb\x9d\xcb\x5d\x83\x89\xf0\xa6\x65\x82\xbb\xb0\x90\xeb\xf5\xa3\xf3\xbb\xde\xf1\x9a\xbb\x82\xc2\xc2\x5b\xee\x12\x62\xe1\x28\x40\xe9\xbb\xea\xc1\x0c\x47\xc7\xc7\xef\xad\x6a\xc5\x9d\xc9\xd4\xe9\x77\x0b\x5e\xd3\xf0\x6a\x2f\xd2\xcb\x15\x36\x3d\xa2\xab\x38\x5d\x6e\x19\x23\xef\xba\xb1\x59\x89\xe1\x1b\xe8\xe1\x26\xe4\x50\xe7\x07\xce\x0d\x6c\xb9\x57\x06\xec\x0a\xf0\x3b\x1c\x85\xe6\x1a\xcf\x81\x03\x49\xc0\x52\x9a\x01\x0c\xb2\xc7\x61\xe9\x45\x29\x31\x8e\x62\xfa\xc6\x60\x80\x2c\x67\x3f\xce\xe3\x1e\x45\x97\x34\x45\x5e\x5c\xd3\xb1\xb5\xfd\x1c\xad\xac\xb8\x7d\x2b\x9c\xe5\xab\x59\x4c\xf3\x0d\xf9\x5c\x39\x16\xd4\xf2\x90\xaa\x97\x30\x79\x45\x95\x38\xc5\xd8\xf8\xac\xaa\x64\x09\xf4\xf5\x2b\x25\x57\x59\xa7\xca\x27\xf1\x9a\x1f\x7b\x2d\x1d\x8d\x53\x4e\xa2\x54\xb6\xe8\x5e\x83\xb6\x03\x57\x1b\xe2\xa7\xfb\x76\x83\xf5\xdc\x45\x9c\x2e\xd0\xac\xb8\x48\x65\x0c\xbb\x97\x3e\x88\xf9\xd7\x1d\x62\xd5\x05\xfe\x25\x17\xf1\x66\x5e\x0c\xe2\xe9\x2c\xc8\x60\x7b\x29\xba\x0c\xd5\x6d\xc1\xd8\xc4\x14\xf1\xa7\xe8\x9e\xe8\x5a\x7e\xb7\x41\xee\xbe\x0c\x07\x63\xda\xf6\x31\x27\x6f\x0f\x21\x2b\xd4\xe5\xe3\x8d\x1a\x7d\x8b\xe2\x85\xb9\xef\x02\xb5\x8c\x1a\x69\x49\x5b\x82\xf2\x8b\x2b\x50\x23\x11\x77\x8d\x0a\xe4\x9d\xeb\x18\x0b\xfd\xb5\x0f\xb1\xa4\xb8\x57\xd5\x72\xa9\x44\xab\xb1\xb4\xf7\xa7\xb5\xab\x76\xb3\x53\xef\x0c\xd6\x20\xb1\x41\xa7\xdd\x69\xb5\x47\xed\xd1\x59\x99\xab\xe2\x01\x34\x7f\x90\xfd\xf0\x9c\x23\x0b\xa0\xe0\x1d\x0b\xcf\xe1\x4b\xd4\x95\x8c\x8c\x86\xb5\x59\x7e\xcf\xcb\x5b\x63\xaa\xbf\xd2\xb2\xc2\x23\x5f\x27\x92\x4e\x6f\xbd\x64\xf4\x98\x0d\x7c\x41\xdf\x62\x0d\xdf\x6f\x00\x07\x5b\x18\x35\x96\xde\x2c\x48\x52\x5c\xd2\x16\x6a\xce\xc5\x64\x92\x6a\x8a\x1f\x59\xcd\xe9\x95\x40\x8a\x23\x1a\xc3\x6b\xc1\xa2\xa3\x84\x61\x21\x93\xa7\x5e\xcd\x83\xc8\x2f\xe3\x94\xc3\x30\x4b\x0a\x61\x81\x3b\xc1\x69\x46\x6d\x1b\x82\x89\x63\x81\x1a\x30\x4f\x6b\x67\x68\x63\x03\xc9\xb5\x87\x7e\xfc\xd1\x6c\xf7\xb4\xce\xca\xf0\x35\xe9\x53\x41\x6d\x5f\xd1\x0b\x0c\xbb\x65\xa4\x73\x18\x6b\xf1\x1b\x2d\x32\x53\x9e\x46\x05\xb5\xca\x39\xd6\x75\xf1\x05\x3b\xa2\xc3\x55\x90\x84\x61\x97\xb7\xe0\xcf\xa0\x81\x9a\x79\x6b\x6d\x15\xd7\x6e\x75\xea\x9d\x62\x8c\xc2\x79\x34\xf2\x1c\x83\x2a\xca\xe9\x44\x17\xcd\x73\xef\x8a\xf8\x22\xbc\x4c\x82\xd9\x0c\xe4\xc8\x20\x63\xcd\xab\x2a\x13\x14\x90\x9d\x3e\x55\xbc\xd2\x72\x57\xaf\xe6\xea\x63\xb9\xb2\x49\x87\x1f\xd7\xa7\xa2\x0e\x24\xb7\xbe\xec\x11\x42\x0f\x97\xf1\xf3\xa4\x7a\xae\x23\x50\x7b\xcb\x3a\x4b\x1d\x42\xa3\x21\xa5\x1a\x71\xc0\x90\x17\x3b\x8e\x83\x53\x5e\x88\x28\xd3\x7b\x11\x10\xea\x5a\xa2\x9a\x32\xb1\xb9\x41\xa5\xd8\xb5\x03\x99\x37\xe6\x4d\x77\x17\x0f\x55\xa9\x7c\x72\x1c\x75\x72\xbc\xcf\x59\xd3\xd4\x06\x85\xfd\x96\x7e\xe7\x7f\x93\x18\x2e\xee\x2d\x6c\xf3\xaf\xdd\xc0\xc8\xb2\x74\x6b\x54\xec\x65\x25\xfc\x2b\x6d\x6d\x84\xe6\x6a\xe9\x39\x85\x3d\x5c\x83\x32\x48\x8d\xa9\x4e\xf8\xa6\x8d\x57\xc4\x6a\xf3\x48\x03\x39\xca\x0e\x87\x73\xac\xdf\x8b\xf5\x76\x21\x74\x96\x8a\x9e\xb3\xed\xb2\x5f\x57\xa2\x1b\xc4\xd2\xf9\xc4\x15\x00\xcd\xe9\xb3\x6a\x89\x25\xd2\x33\x43\x04\x48\x60\x9d\xbd\x8d\x64\xd2\x83\xfe\x49\x98\x70\x05\x6c\x41\x61\xf6\x46\x84\xe3\x0a\xc7\x5c\xdf\x7e\x54\x7c\x3b\xcd\xdb\xb4\xb5\xfd\xd5\x2e\xc8\x55\x8b\x8e\x4f\x84\xac\x44\xdf\xaa\xe1\x85\xa3\x88\xa2\x23\x64\xf4\x62\x97\xa1\x5a\x41\x09\x08\x2e\x44\xed\x62\x42\x1f\x28\x4b\xb2\x57\x8e\xc2\x8a\x2e\xd0\xb4\xb0\x76\x94\x56\xf4\x82\x84\xf4\x46\x8e\xe3\xda\x4d\xe1\x63\x0b\xbb\x87\x4e\xc5\xc4\x09\xc5\x97\x7a\x2d\x83\x1e\x6c\x7b\x52\x09\x40\xec\x50\xc6\x45\x93\xf2\x08\xa9\xbd\xff\x8e\xfb\x94\x11\xa0\x45\x44\x3a\xfe\x06\x7b\x93\x8c\xaa\xbc\x98\x4d\x73\xef\x79\x07\x9b\xe6\x64\xc7\xc2\x28\x28\x1e\xf5\xb7\x66\xd9\xf7\x8d\xa2\xb9\x2f\xdd\xe3\x96\xe2\x8d\x5d\xe0\x89\x30\xf0\x0d\x76\x15\xa6\x71\x50\x54\x0b\xea\x62\x32\x00\xab\x3b\x05\xbb\xfd\x86\xf3\xab\x8a\xbc\xe4\x26\xae\xe6\x18\xa7\xb0\x37\x0c\x75\xf2\xb4\x4d\x4c\x8b\xba\x48\x87\x45\xee\x4d\x0a\x93\xd1\x14\x3e\xce\x6d\x42\x34\xb1\xb4\x36\xc6\xc9\xd6\xcc\xb1\xd2\xef\x5f\x40\xc7\x14\xa4\xe9\x7c\x8a\x87\xfa\x7d\x62\x30\x49\x70\x30\xbc\x56\xf6\x3b\xed\x40\x36\x8f\x68\xda\xca\x02\x11\xcd\x96\x63\x7b\x6e\xfe\xb5\xd4\xa1\x89\x30\x2e\x30\x51\x4f\x52\xbc\x34\xaf\xf7\xeb\x8b\xe6\xd1\xb2\xb0\xfe\x42\x89\xdb\x22\x79\xaa\x42\x3a\xe0\x54\x80\x04\xf1\xbb\x79\xc0\x27\x4b\xa7\xa4\xae\x1e\x56\xd9\x95\xca\x9b\xc5\xae\x51\x17\xe1\x82\x10\x36\xdc\x26\x84\xb2\x27\x7b\xa9\x9a\x17\x1b\x28\x57\x3b\xca\xa0\xe5\x28\x45\x2d\xcd\x84\xf3\x86\xe4\x9d\xdb\x44\x62\xd1\x95\xc9\x97\xe1\x08\xee\x4b\xe8\xbf\xf9\x97\x25\x8b\xac\x30\xec\x0b\x93\x77\x14\x3a\x69\xa5\xd8\x3d\xc9\x16\x01\x0f\x77\xfa\xa4\x31\xb2\x96\xf7\x7e\xe1\x0a\x83\x19\x8b\x17\x54\x5c\x1d\xcb\x6b\x30\xcb\x0b\xf6\x00\x72\x0a\x69\x06\x00\xe7\x7b\x85\xc8\x40\xe5\x98\xda\x56\x84\x11\xb3\xe4\x65\x76\x00\xcc\x64\xe6\x1c\x47\x60\xcc\x9b\x0f\x4d\x44\x29\xf7\x00\xa3\xa1\xb3\xf3\x61\xd9\x3a\x03\x50\x61\x29\x42\xd2\x26\xea\xb4\xc0\xe4\x18\x3e\x70\xfb\xd9\xbd\x11\x8a\xa7\x21\x91\x11\x2a\x28\xa0\x9f\x2e\xc3\xc9\x04\xf5\xb1\x68\x70\x88\x92\x20\x1a\xc6\xd3\xc9\xf5\x3d\x1d\xee\xa9\xd5\x04\x1b\xa6\x0a\xda\xfb\xa5\x02\x53\x4a\x1a\xff\x06\x5c\x88\x4e\xf2\xd0\x66\x41\x0a\x35\x56\xf1\x15\x1e\xcc\x33\x5c\x5a\xe1\xd1\xa8\x56\x2a\x2c\x71\x47\x85\x99\x6f\x79\xc4\xa2\x7b\x82\x5e\x41\x2b\x64\x38\xc8\xff\xaf\xf8\xcf\xcc\x14\x8c\xca\xdd\x38\x35\x57\x38\x89\x56\x18\x75\x51\xc5\xa6\xdb\xa8\x9f\x4e\x33\x9b\x65\x8f\xa2\xfa\x07\xef\x55\x92\xa5\x44\xa6\x70\x4a\x9d\xd6\xaa\x95\xd6\xdc\xe1\x56\x47\x97\xb6\xb2\xae\x6d\x69\x85\xc6\x9b\xa5\x89\x07\xa4\x02\x57\xc4\xb8\x93\x69\x90\xd9\x42\xba\x29\x57\x59\x22\x6f\x65\x3c\x00\x7f\x67\xc0\x5a\x42\x9b\x59\x3e\x06\x60\x37\x6d\xa9\xc9\x45\x32\x68\xa6\x20\xe7\xc9\x64\xf9\x98\xa3\x9f\x6c\x7d\xb6\x96\x1a\x5a\xa6\x70\x76\x3b\x4b\x1d\x31\x51\x6a\xc9\xc3\xb8\x3c\x52\x0b\x29\xfa\x76\x5a\x6d\x97\x66\x40\x53\x71\x0f\x19\x5f\xe6\x2c\xcf\x60\xc9\x15\x01\xcb\x23\x7e\xdd\x5e\x1f\xee\x88\x12\x27\x14\xe2\xee\x6f\x2e\x0d\xd7\x03\xea\xc7\xdf\x6d\xed\xdc\x20\xb2\x7d\x72\x0b\x4a\xd7\x2e\x2c\xa5\x3c\xce\x6c\xf3\xb7\xb8\xa5\xb4\xe2\x8e\x0e\xfb\x9d\x1f\xbe\x0c\x47\x5d\x65\x7b\x56\x28\x64\x49\xf5\x38\x73\xa9\x5a\x66\x5f\xfe\x3e\xf4\xe5\xb9\xd2\xc1\x77\xa0\x8e\xf8\x9b\xa8\xcd\x1d\x8b\xaf\x90\x26\x79\x85\x0f\xb5\x2f\xac\xec\xc3\x37\x5c\x41\x7f\x3e\xb0\x06\x5b\x6e\x47\xdf\x48\xe1\x60\xec\xae\x71\xe6\x53\xee\xba\x64\x17\x02\x9e\x88\x2d\x5c\x5c\x51\xb0\xa7\xc3\x2b\x64\x0c\xf6\x4c\xb7\x3d\x9f\x77\x27\x15\x63\x69\xdf\xac\x2e\x55\x61\x8b\xd5\x30\xa8\x3a\x43\x12\x78\x15\xf3\x9a\xbe\xc4\x7f\x9d\xa1\x06\x80\xb0\xe6\x47\x6f\x5f\xd1\xe3\x5b\x68\xec\x87\x57\x34\x19\x08\x54\x70\x0e\xa9\x72\xb6\xa6\x86\x99\x1a\x74\x9f\xde\xc4\x79\xe2\xbb\x83\x3e\xf8\x2f\xe0\xc7\xf7\xac\x20\xfe\xde\x19\xf3\xf7\xa8\x27\x76\x31\xc3\x65\x15\xc5\x77\x62\x8c\xf7\x8e\xa2\xad\x28\xbe\x2f\xc6\x5d\x50\x4f\xfc\xcd\x79\xf7\x37\x57\x16\x7f\xfb\xad\xa2\xa2\xd9\xf6\x78\x4e\x68\xf7\xb7\x77\x14\xd2\x87\xfb\xef\x2f\x5c\x5b\x87\x3a\xbe\x05\x77\x8f\x3c\x05\xb9\x54\xe5\x89\x4c\x97\x6a\x4a\x4b\x96\xbf\xf2\xe6\xac\xd2\x6e\x7e\xaf\x49\x29\xef\x3d\x07\xe5\xb2\xb9\x27\xb5\x9c\x93\x16\x62\x76\xfa\x49\x23\xed\x24\xaf\xe8\x49\x3c\x09\xfa\x51\x09\x5c\xfc\xd4\x93\x4f\xee\x07\xd9\xb8\x82\x1c\x29\x28\xe5\xf1\xfa\x7d\x3c\x08\x26\x68\x16\x4f\xae\x47\xe1\x04\xc5\x23\x44\x37\x2d\x76\x8a\x77\x1c\x79\x59\x6c\xfb\x0d\xbd\xa0\xd1\xb0\xc6\x98\xc4\xeb\x1d\xf2\xfe\xe6\x95\x1d\x3b\x48\xb1\xb5\xec\x7f\xb6\x98\x1a\xd8\x08\xce\xfb\x64\x06\x4d\x22\xde\xa9\xce\x92\x38\x8b\xc9\x27\xb4\x41\x4e\x1f\x66\x01\x56\x0f\x6d\xa0\x08\x5f\x12\x04\xf2\x21\x44\xf3\xc9\xc4\xb3\x50\x04\x06\x72\x99\x28\xf1\x8e\x5c\x91\x3c\xf9\x9c\xe4\x2b\xb9\xbd\x8a\xed\xf7\x61\x3f\x09\x92\xeb\x45\x3a\x72\x25\x3f\xa8\x17\x14\x64\x0b\x65\x5a\x4f\x22\x5c\xf0\x2e\x07\x13\x14\x46\x63\x9c\x84\x5a\x00\x57\x2d\xa2\x83\x99\x67\xd4\x8e\x30\x6a\x4f\x67\x81\xb0\x7f\x3c\xc6\x30\xb8\xc7\x09\x3f\x83\x71\x90\x71\x84\x58\x28\x0f\x2a\x06\x59\xa7\x4a\x84\xf2\xe2\x00\x72\xb9\x2b\xbe\xc0\x49\x12\x0e\x71\x8a\x0e\xa9\x42\x24\xc4\x29\x65\xe0\xb3\x6b\x14\x46\x2c\x9b\xb1\x44\xa0\x40\x0b\x66\xae\x86\x93\x65\x01\x58\x32\x97\xa7\xdc\x32\x51\x03\xc9\x44\xed\x5f\x9f\x50\x12\xd6\xa4\x9b\x1c\x93\x44\xd5\x5f\x2c\xc4\x93\x61\x17\xad\x40\xa6\xac\x15\xd3\x70\xc4\xdd\x26\xf9\x9b\xe2\x6c\x1c\x0f\x73\x7d\xe4\x95\xd2\x66\x8c\x7c\x97\xe3\x19\x42\x76\x38\x43\x8a\xbe\x66\x90\xcd\xe7\xd5\x1b\xc4\x70\x16\x5c\x46\xf6\x17\x85\x91\x10\x61\x41\xa6\xd5\xf3\x99\x13\x6f\xce\xcf\xa7\x38\x72\x98\x0e\x93\x1d\x25\x1f\x0b\x24\x99\x0f\x3b\x77\xc9\xf2\xce\xf4\x0f\x4e\x04\x98\x99\x14\x77\xfd\x0a\x85\x63\x69\xe2\xc6\xe9\x07\xde\xe4\x38\x48\x0f\x2e\x23\x46\xf6\xd7\xa5\x15\x52\x73\xa5\x2c\x7c\x9e\xc8\x23\x6c\x82\xbc\x3c\x79\xb1\xb0\x1f\xb4\x56\xee\x74\x3b\x6a\xfd\x3f\xe9\x7c\x46\x44\xad\x28\xcc\xaa\x01\x11\x4e\xd9\xd6\x17\x24\xe7\x73\x32\xba\xce\xf1\x40\x8e\x0c\x0a\x39\xe3\x24\x3d\x6e\x93\x95\x14\x49\x8e\x1e\x52\xa5\x30\x9f\x74\xba\x4a\x6d\x08\x6a\x07\xb5\xfd\xc0\xb3\xed\x20\xae\x18\x1f\xe1\x04\x47\x03\xd2\x00\x8c\xf3\xcc\x5c\xaf\xd6\x30\x30\xb9\xd8\x05\xd0\xbb\xcf\x20\x57\x6a\x0c\x17\x53\xdd\x86\x95\x92\xaa\x4c\x93\xaa\xbc\xe7\x11\x1d\x07\x98\x40\xba\x6a\xed\x10\xa8\x9b\x7c\x3e\x64\x06\x9b\x52\x59\x5c\xc3\x11\x51\x1a\x42\xca\x01\x90\x52\xf9\xef\xcc\x2b\x79\xc4\x72\xb4\xc1\xd8\x26\xbf\xb3\x58\xc8\x8b\x68\xb9\x7c\x8e\x67\x37\x02\x4b\x4e\xc6\xc9\xb6\x57\x2e\x8f\xa0\xae\xac\x11\xfe\x4e\x5f\x27\x5e\xaa\xe1\xc5\x6f\x43\x36\x79\xee\xea\x9e\xb9\x42\x07\x8c\x99\xb1\x24\x01\x40\x52\x60\x42\x3f\x1c\xa2\x34\x9e\x62\x9a\x7a\x0a\x5d\x8e\x71\x84\xae\xe3\x79\x22\xcc\xec\x03\x22\xce\x52\xe0\xf7\x1c\x3b\xf7\xae\xbb\xa0\xe9\xe8\x9c\xb7\x97\x21\xca\x00\xaa\x55\x7b\x64\xc4\xd0\xdf\x72\xbb\x5b\x88\x46\xa1\x39\xed\xc5\x33\x22\xec\xcc\xa4\xdc\xc3\xe4\x9d\x3b\x88\x53\x0a\x30\xd0\x30\x69\x32\xd5\x14\x34\x91\xf7\x3c\xa5\x6c\x75\xd2\xfd\xb3\xa8\xfc\x72\xcb\x71\x87\x46\xb4\x4b\x6c\xd1\x3f\xe7\x1a\x17\x11\x0f\xf9\x65\xdb\x87\x60\x0a\x46\x13\x0b\xea\x21\xb6\x55\xcb\x62\xe6\x66\xad\x02\x2c\xe7\x6e\xb1\x64\x3a\x4f\xd5\xe2\x67\x68\x43\x69\x5f\xff\xb4\x44\xea\x22\xcf\x26\xbb\x8d\x2e\xe3\x68\x25\xa3\xf2\x33\x77\x77\x54\x82\x17\x4e\xe2\x78\x86\x82\x7e\x7c\xe1\xd8\x06\xf3\xbb\xbc\xc2\xa1\xad\xf8\x3b\x0c\x5c\x54\xb4\xaa\xf6\x53\xbc\x2d\x90\x57\xab\xd0\xe2\x11\x87\x13\xe8\x29\xd8\xbf\x2c\xb3\x6e\x5c\x1b\xdf\x60\x12\x47\xf8\x01\x38\x1e\xc0\x45\x1b\x72\x0f\x81\x17\x05\x76\x32\x52\x6c\xe1\x46\xa6\xe6\x22\xd1\x85\x23\xce\x4f\x9d\xf6\x64\xee\x33\xb2\xf3\x76\x3f\x42\x01\x78\xde\x1a\xb1\x08\x73\x23\x0b\x59\x71\xde\xf3\x41\xb8\xc2\xd3\x08\xe3\x07\x3d\x1c\x62\x1a\x9e\x47\xe1\x28\x1c\x04\x51\xc6\x02\x4a\x86\xb4\xf7\x00\x92\xb6\xe3\x3a\x26\xff\xaa\x78\x10\xd3\xb3\xb2\xfa\xe6\x1e\xc2\xc6\xd8\xcd\x9b\x64\xe1\x09\x83\xaf\x9a\x5e\x2d\x18\x6b\xe4\x34\x0b\x13\x23\x65\xdc\x60\x2c\x1c\x34\x7c\x6f\xa9\x5e\x54\xff\x6c\x6d\x63\xb7\x6c\x61\x3c\xda\xff\xe2\x00\x4e\x6b\x57\xb5\x5a\xad\x5e\x6b\xd4\x9a\x15\x54\xbb\xaa\xb5\x6a\xed\x5a\xa7\xb6\x76\xf6\x60\x80\x2b\xa8\x53\x38\xf4\x0a\x0b\x5f\xc7\x67\xc4\x5a\xb1\x97\xcc\x21\x18\x96\x2b\x7f\xa0\xff\x7e\xfd\x0a\x31\x7b\x0d\x51\x63\x84\x4a\x62\x7a\x7f\xd8\x70\x28\x0a\xd5\x3f\x80\xaa\x18\x0d\xf1\x9f\x85\x8d\x49\x4d\x00\x94\x3c\x26\x38\x3a\xcf\xc6\xd4\xf4\xc8\xcb\x45\x8a\xc7\x8c\x91\x0b\x65\xb9\x48\x31\xdb\xd1\x20\x1e\x12\x7a\xc7\xf4\x87\x49\xee\xf0\x3a\x3f\xf6\xa7\x20\x00\x1c\x0d\xaa\xbb\xf8\xca\xdf\xe6\xa2\x00\x32\x85\x56\xfb\xd2\xc1\x5d\x24\xb1\x16\x88\xec\xe2\x88\x6b\xb0\x28\xac\x8b\xa3\x8a\x36\x24\x1f\xb3\xd1\xfa\x52\xd1\x5c\xd8\x54\x78\x63\xb9\xf0\xa9\xfa\xfa\x15\xed\xe2\xab\xdc\xf0\x2d\x0b\x08\x68\x10\x64\x38\x62\x7b\xbe\x4e\x41\x1e\xe6\xef\x27\x24\xe5\x1e\x56\x0e\xf8\x09\xe3\x86\x0a\x65\x42\x9a\xdf\x65\xef\x75\x8b\xe2\x52\x84\x36\x04\x76\x75\x1e\x3f\x43\xbc\x69\xf8\x53\x9a\x41\x49\x93\x29\xd1\xc0\xce\xcb\x85\x23\x21\x03\xfb\xab\xc5\xb0\x1c\xbe\x8a\xd9\x38\x10\xa1\x0e\x24\x89\xf9\x4b\x87\xe9\xb1\xe4\x31\x1a\xcf\xf1\x00\x3f\xd6\x59\x12\x85\x2f\xeb\x58\x9d\xea\x4d\x82\xe9\x0c\xe1\x2b\x88\x24\xd9\x0f\xcd\xce\xd1\x7b\x55\x52\xc6\xbe\x6d\xa0\xf7\xa9\x03\x57\x90\x14\x0d\xf1\x7f\x79\x02\xa5\x43\x7d\x22\x92\x46\x18\xb6\x5a\x14\x64\x28\x40\x59\x38\x75\x48\xdc\xae\x90\xec\x6a\x77\xfd\x49\x21\xd4\xc1\x21\x45\xd1\x06\x41\x8f\xcd\xc2\x69\xc8\xa3\x62\x93\x7f\x4a\x8d\x16\x7a\x81\x4a\x21\xc5\xf8\x27\xb4\x5e\x2e\x8b\x68\xd9\x5e\x29\x9e\xc2\xd1\x7b\xfc\x1c\x85\x22\xdc\xf6\xd7\x0d\xd9\xf4\xeb\xd7\xbc\x0d\x47\x79\xd1\x68\x01\xc1\xdf\xbb\x2d\xa9\x63\x4a\x17\xd7\x9d\xc6\xd4\x1f\xe5\xbe\x68\xf7\x37\x90\x3d\xd8\x45\x32\x06\xdb\x54\x28\x36\xdb\xe7\x1b\x3a\x9a\xae\x1c\x2b\x41\x18\x05\x7d\xf3\xe4\xa1\x1c\x00\x8a\xb2\x53\x1a\x83\x83\x08\x81\x9a\x60\x18\x66\x77\x15\x05\xe5\xe2\x14\xab\xcb\xc3\xa4\xc8\xe7\xa2\xa1\x7b\x1d\xac\xc9\x96\xa3\x5c\x71\x91\xbc\x4c\xc6\xcd\x30\x1c\xa2\xda\xa9\x80\xc1\xe3\xcc\x6f\xc0\xd2\xa1\x7f\x40\xfa\xcd\x06\x21\xfd\x54\xe3\x0b\x0e\x82\xd7\x44\xa9\x0d\xb4\x1f\x64\xe3\xea\x00\x87\x13\x59\x73\x15\x2d\x11\x91\xc8\x7d\xfe\x2d\xb4\xf3\x78\xcc\x91\xac\xe3\xef\x6d\xed\x3e\xd9\x71\x57\xa5\x05\xeb\xbc\xab\xd3\xc2\xa2\x73\xae\x0a\x16\x4e\x6a\x14\x57\x35\xfa\xb9\x7d\x72\xae\xda\x34\xc2\xcc\xef\x6b\x5e\x93\x3a\x52\x6f\xf9\x29\x50\xc4\x86\x51\x38\x99\xf0\xb0\xb3\xcc\x4d\x02\xce\x5b\x8b\x85\x12\x7e\x98\x8b\x5c\x87\x5e\x15\x94\xd7\xc5\xa7\xd0\x2c\x33\x48\x85\x08\xe5\xbe\x8c\xcf\x0a\x1c\xc1\x98\x2b\x48\xdd\x7f\xd2\xa2\x25\x54\x32\x89\xdc\x47\x2c\x95\x3d\xd8\x07\x2a\xf2\x35\xd1\x6f\xc8\xa7\x9f\x2e\xfd\x51\xe6\x3f\x5d\xa2\x0d\xf2\x5f\x4f\x02\xb5\xe9\xa7\x3f\xc8\x36\x73\xd5\x0c\x86\xb8\xb3\xde\x37\xc3\xaf\x8b\x62\x41\xfa\x05\xa9\x9c\x23\xe7\x9e\xa0\xc0\xdd\x1d\x6d\xb5\x54\xbb\x7a\x59\xeb\xbc\x44\x3f\x91\x2e\xfc\x01\x7b\xfa\xce\xce\xce\x4e\x19\x3d\xa7\x2f\x7e\xfe\x19\xd5\xae\xea\x35\xd8\xee\x09\x02\x9e\xed\x9e\x76\xb1\x54\xbb\x6a\x75\xda\x35\x0a\xec\xd2\x04\x76\x59\x14\x18\x0c\x2f\x4e\xe7\xe0\xe9\x53\x02\x34\x5e\xbf\xa6\x35\xd1\x73\x04\x23\x9d\x5b\x9f\xd5\x5d\xdd\x80\x3a\xec\x2f\xbf\xec\xf3\x0d\x54\xab\xb6\xbd\x65\x60\x4c\x59\xd1\x9f\xa8\xbd\x0d\xa7\xb6\x32\xfa\x19\x55\xdb\xe8\x3f\x50\x1d\x75\xd1\x8b\x7a\x11\x11\xc5\xe2\x1c\xba\xb8\x51\x41\xc9\x20\x18\x8c\x31\xcb\xae\xb3\x58\xe0\x20\x35\x3f\x11\x7a\x4c\x4a\x25\x5a\x95\x1c\x95\x34\x24\xc9\x6e\xa2\x0c\x86\xfb\x8a\x89\x56\xdd\x40\x9f\x92\x12\x2d\x0f\x04\xb9\xd6\x5f\x73\xf4\xe9\x52\xe6\xf0\x29\x89\xf2\x12\x3e\xfa\x8a\x6a\x05\xc3\x9a\x47\xf8\x52\x71\x76\x82\x5b\x47\xa6\x00\x89\x78\xfa\x9e\x27\xc6\x48\xba\x9d\x4f\xd9\xd1\x7e\x91\x21\x0d\x8e\x06\x60\x48\x43\xff\x75\x1b\xd2\xec\xe2\x2b\x5b\x13\xe0\x02\x47\x0a\x6e\x50\xa0\x55\xfa\xbb\x58\xfc\x4d\x53\x7d\x31\xc6\x57\x85\x55\x18\x05\x4e\x9e\x4b\x46\xd5\x2c\xd4\xfa\x7d\x31\xf2\x31\xbe\xb2\x43\x68\xb2\xf1\x53\x8e\xf6\x8b\x13\x09\x39\x03\x67\xde\xf6\x98\x7a\x59\xf8\xe4\x99\x2e\x7b\x8c\xa4\xb3\x6e\x03\x1a\xe3\xab\xde\x38\x48\x0a\xe7\xd9\x4a\x17\x1e\xe8\x20\x47\x5a\x48\x0f\x72\x97\x77\x3c\xc4\x71\xec\xd8\x1a\x07\xb0\x04\x48\xab\x2c\xd5\x3e\xf5\x4e\xd9\xc5\xef\x5c\x55\x49\x3b\xb5\x51\x7e\x5d\x0f\x83\x10\xe0\x3e\xc7\x61\x54\x5a\x59\xb9\x45\xc4\x4d\x85\xc2\xe9\x7a\x5b\x46\xd3\xc3\x57\x0a\x25\xdc\xe2\x0b\xc6\x23\x3c\xfd\xf5\x52\x13\x5f\x6c\xd4\x66\x5b\xac\xc7\xe2\x91\x32\x69\x95\xe5\x12\xa5\xd0\x3a\xef\xf9\xd1\x85\x3e\xb2\xa3\xcc\x32\xab\xe6\x72\x99\xd4\x74\x6a\xa3\x6c\x0b\x6d\xe4\xe4\xc7\xa4\xab\xa5\x09\x9a\x09\xe8\xf4\x5e\x94\xb1\xce\x56\xd3\x79\x3f\xcd\x92\x52\x58\x41\x8d\x72\x05\x92\xf0\x49\x95\x05\x59\x51\xeb\x65\x97\x03\xee\xd2\x7b\x9e\x36\x4c\xab\xa8\x51\xd4\x7d\xf6\x7d\x90\x85\x51\xbd\xd8\xa6\xc5\xca\xf2\x7d\x4b\x3c\xde\x6e\xeb\x62\xd5\xff\xba\xdd\xab\x28\x02\xf7\xb5\xa6\x26\xd0\x9e\x7b\x0f\xa3\xb8\xfc\x8f\xda\xc6\xe8\x70\x7c\xc7\x3b\x99\x82\x20\xdd\x91\xe8\xd4\x55\x47\x49\x3c\x25\x6f\x7b\xf1\x10\xc3\x26\x55\x74\x43\x52\x01\xde\x61\x4f\xd2\xe8\xf6\xf6\xdb\x92\x20\xc7\xa5\x16\xc3\x77\xbd\x39\xb1\x55\x44\xf7\x27\x75\xb9\x15\xdf\xa2\x44\xad\xe5\x76\x29\x51\x4d\x6c\x54\xe2\xcd\x43\xef\x55\x46\xd3\x8b\x72\x39\x87\x8a\x16\x5d\xf6\xb6\x3a\x60\x04\xbd\x99\x95\x42\xbe\x26\xcc\xad\xca\xad\x5b\x5c\x7a\xab\x32\x10\x2e\xba\x53\x7d\x3c\xd9\x79\xb1\x5e\x6c\xa3\xfa\x98\x8d\xd6\xc5\x36\xc5\x1e\x6e\xb7\x49\xd1\x46\xff\xba\x3d\xaa\x60\xfb\xf7\xb5\xb2\xe6\xd9\x68\xdd\xbd\x41\x91\x51\x7c\xc8\xed\x29\x4b\xae\x73\x0c\x8c\x86\x98\x1c\xd1\x3f\x1e\xed\xf5\xb8\xa7\x53\x09\xa7\x83\x60\x86\x4b\x39\x1b\xa7\xcd\x96\xd1\x20\xc8\x06\x63\x54\xb2\xd3\x47\x03\x0a\xe3\x24\xbe\x04\xba\x85\x8c\x2b\xa5\x95\xfd\x60\x32\x8a\x93\x29\x1e\xb2\x69\x18\x06\x59\x60\xa7\xa0\x5b\x9e\x81\xab\x93\x7a\x7b\xfe\xcd\xe6\x6a\x19\x32\xf9\xae\x99\x37\x50\x18\x65\xdd\x92\x0c\x8b\x33\x6e\x56\xc7\x67\x0c\xa0\x6d\x0d\xf3\x88\x51\x0f\xb5\x10\xd0\xe8\x8a\xc3\x29\x17\x0e\x40\x23\x52\xf0\x42\x2e\x4c\x3c\x64\xd9\xcc\x14\x2f\x74\x6f\x26\x5e\xc5\x4e\xf6\x5a\x49\x89\x36\x9d\xa7\x19\xea\x63\x14\x92\x11\x9d\xe2\x28\xa3\x79\xd6\x02\xb8\x5e\x4f\x70\x26\x3c\x16\x0a\xe5\xf6\x35\xf2\x74\xea\xca\x7d\x9a\xe3\x90\xba\x56\xc9\x04\xf1\x5f\xf0\x2c\x43\xf3\x68\xc6\x93\x06\xea\xd9\x41\x15\x9b\x96\x9a\x83\xfb\xbe\x61\xe3\x00\x99\x06\x37\xc5\x28\x08\x2f\x31\xdf\xe7\x82\x66\x70\x90\xdd\x95\x59\xf3\x18\x23\xbd\xc2\x92\x68\xb3\x24\xa6\x59\x8c\xc2\x2c\xe5\x5e\x31\x88\x50\xf0\x5d\xef\x98\xfa\x4e\xe4\x69\x42\x5c\xff\x25\x53\xa1\xac\xbb\xcc\xbc\x0f\x81\x95\xb2\xcb\x66\x00\x32\x70\x32\x4f\x45\x63\x67\x35\x99\x12\x2d\x1f\x6d\x05\x59\xc0\x85\xf5\x5a\x51\x49\x73\x73\x38\x4c\xa1\x0d\x9e\x17\xdc\x33\xd2\x8c\x16\x8a\x6f\x8a\x22\xc8\x82\x95\x79\x9c\x19\xbb\x20\xba\xe6\x99\x13\x00\xe5\x97\xd4\xa7\x24\x50\x2c\x28\xa9\x3d\x31\x70\xbc\x87\x99\xcc\x4f\x14\x9d\xd2\x8a\xcd\xef\x0b\xd5\x5b\xbc\x37\xb2\x92\x45\x92\x99\xdb\xee\xf5\x32\x1d\x9d\x1a\x50\x54\x19\x20\x16\x4c\x54\x07\xa5\xfa\x38\x03\x19\x2d\x88\x13\xc9\x68\x4d\x61\xca\x80\xe1\xe2\x48\x69\x9b\xd0\x35\x1f\xf9\x72\x53\x22\x17\x30\x8b\x68\x9f\x6f\xe8\x49\xd2\x8b\x52\x30\xcf\x75\x9a\xa2\xe0\x22\x08\x27\x10\xb1\x8b\xf2\x05\x60\x76\x7e\xaa\x39\x51\x9c\x55\xc2\xe8\x22\xfe\x82\x53\x33\xc9\x70\x89\x25\x07\xae\xa0\xcb\x71\x38\x18\x3b\x59\x75\xff\x3a\x87\x55\xdb\xad\xf2\x85\xd2\x8f\xe3\x09\x0e\xa2\x1b\x34\x8c\x77\x26\xf3\x74\x8c\x7e\x1d\xe3\x8c\xc6\x33\xe1\xb9\x68\xc1\x5d\x6b\x16\x24\xc0\x28\xd8\x2b\xc9\xb5\x05\xbb\xbe\x45\x38\x10\xc1\xe9\x61\xc4\xef\xbe\xcd\x0b\x80\x5b\x94\x90\x7c\x6b\x86\xa7\xca\xf5\xc5\xe5\x58\x12\x8c\x3b\x53\xb0\x1e\x6b\x95\x16\xd5\x16\x1f\x1d\xf0\x25\x75\x26\x6c\x89\x48\xe2\x76\x68\x4b\xc8\x6b\x6e\x9c\x06\x23\xeb\x53\xab\x90\x8f\x8a\xa1\x99\x8f\xee\x79\x71\x29\x2b\x6c\x18\x29\x99\xf3\x0a\x73\xe8\xb2\xb6\x3b\xa2\x5f\x2f\x9e\x47\x19\xa7\x2f\x07\x33\x21\x40\x23\x9a\x48\xf8\x08\xe2\x16\x6f\xe8\xf8\xaf\x1a\x4d\xbe\xb2\x79\x91\x6f\xc8\x19\x06\x47\xf1\x3c\x1a\xa2\xf9\x8c\x3a\x14\x0e\x26\xf3\x21\x36\xe8\xde\xae\x66\x60\x24\x8d\x5c\xd4\x0f\xc5\x63\xdb\x0a\x2c\x86\xf1\x65\xa4\xe2\x11\x47\x93\x6b\x34\x9a\x8b\x45\xe9\x88\xa4\xbf\xba\x8a\x26\x38\xa5\x4e\x95\x6e\x59\x0b\xf8\x46\x82\xa7\x41\x18\xe9\xc2\x55\xb1\x7e\x4d\x83\xab\x92\xd6\x2f\xb8\x38\x45\x2f\x5c\x99\xd9\x2b\x8b\xaf\x54\xc5\x9c\x53\xcd\x83\x6f\xca\x81\x92\x39\x1e\x5a\xeb\x3f\x21\x85\x00\x7d\xf4\x04\xb4\xe1\x25\x27\xf2\x55\xef\x63\x18\x95\xd4\x26\x7f\x42\xad\x8a\x46\x67\x2e\xf3\x49\x9e\xc1\xdb\x45\x24\x84\xee\x14\x80\xf9\x6e\x5b\x94\xcf\x53\x35\x0b\xfb\xfd\x5a\x1d\x01\xf1\xf6\xb9\xb2\x9e\xbc\x46\x13\x04\x33\x9c\x90\xd3\xa4\xd8\x18\x5e\xc8\x03\x02\x38\x43\xba\x2b\x32\xee\xa2\xef\x41\x82\xab\xb8\x72\xd5\xfb\xe6\x18\x69\x29\xb0\x24\xc3\x87\x29\xb7\x8b\x6a\xdc\x57\x65\x61\x66\x32\x2c\x75\x44\x1d\x68\x68\x9c\x0c\xbd\xd8\x50\x67\x7a\x31\x55\xf2\xd8\xa2\x79\xd8\xfa\x15\x4e\x3a\xfe\x15\xb5\xe9\xbb\x1a\xbb\x15\xce\x42\x99\xeb\xe4\x75\x47\x2b\x37\xcf\x6e\xf8\x17\x99\xbc\x7d\xb2\x36\x44\x89\x89\x73\xc6\x72\x2d\xde\x74\x1e\x26\x4e\x9a\x9e\x4c\xf4\xfc\x0c\x3e\x0e\x52\xc8\x90\xeb\x3d\x71\x2f\x4c\x45\x2e\xd9\xb5\xea\x03\x45\x27\x9d\x41\xa7\x61\xd7\x70\x8a\xe2\x48\x39\x0a\xd7\x3b\xa8\xd4\xae\x37\xc0\x92\xb5\xec\x38\x16\xef\xd2\xca\xfc\x18\x2c\x1e\xdd\xe7\xe1\x7b\x89\xfa\x9a\x97\x81\x2c\x37\x60\x6a\x9e\xab\x19\x1d\x84\x25\x72\x92\xdf\x36\xba\x1d\x69\x08\xd1\x10\xc9\x8b\x82\xdc\x15\xb6\x21\x11\x73\xa0\x85\x6e\x3b\xde\xdd\x6c\xb4\x3b\x6e\x27\xb1\xbc\x54\xd7\xb7\x8e\xb0\xc6\x63\xab\x15\x0f\xb3\x76\x8c\x45\x78\x0f\xbf\x86\xc0\x56\x43\x2c\xb0\xc4\x96\x9a\x14\xbe\x70\xee\x5f\x65\xc2\xe8\xe5\x3e\x54\x24\x80\xb0\xaa\xe2\xd1\x4b\x78\x56\x12\x80\xd6\x98\x97\x2d\x35\x98\x7b\x33\x1b\x0e\xc7\xc6\xcc\x37\xe4\xa3\xe5\xc6\xfa\xe3\x6c\x08\x2c\x43\x1d\x6c\x9a\x96\xbf\x78\xc6\x3e\x6f\x04\x61\x0a\xdc\x8c\x23\x5c\xd8\x85\x88\xb2\x22\xe6\x3f\xb4\x70\x79\x2f\x31\xe7\x73\xc0\xab\xb4\xc2\x90\x72\xe9\x52\xf4\x92\x8b\x55\x27\xb4\xa0\x4a\x28\xda\x18\x78\xd6\xa3\x47\x23\xc1\x14\x36\x3a\x04\x07\x79\xb0\xf1\x25\x42\x3a\xc1\xd7\x05\x4a\x39\xc7\xda\xe2\xef\xbd\xf9\x4e\xec\xb0\x24\x37\xa9\xc0\xc5\xcb\x20\xd1\x87\x18\x50\x0e\x32\x9a\x2f\x9e\xd5\x94\x31\x43\x51\x98\x22\x3c\x1a\xe1\x41\x16\x5e\xe0\xc9\x35\x0a\xd0\x10\xa7\x59\x32\x87\xe7\x0a\xc8\xe9\x2f\xe2\x68\x80\x0b\x45\x19\x2d\x48\xa1\x5a\xa2\x07\x40\x49\x06\xe4\x86\x12\xcb\x6b\x2e\xc8\x20\xdc\xd3\xce\x80\x36\x38\x39\x8a\x64\x42\x1e\xb5\x84\xa7\x74\x1e\xa1\xe7\x54\x5b\x4c\xf5\xbc\xe8\x52\x74\xbf\xe3\x18\x5f\xfb\x40\x94\x0f\x06\x2d\x5a\x2b\x8b\x04\xf8\x25\x38\xab\x32\x42\x9c\xc9\xee\x28\xf3\xe0\x5c\x3c\xa4\xbc\x6f\xf1\x28\xc9\xef\xda\xf5\xc6\x6a\xb3\x51\x4c\xcc\x4f\x99\xc6\x47\x8b\x7f\x1f\xb0\x49\x5b\x11\x81\x93\xc2\x28\xc3\xc9\x48\xb1\x16\x46\xde\x55\xc1\xf9\x2b\xeb\x3a\xa7\x5a\xba\xdd\xb2\xf8\x88\x01\x1a\xe3\xc9\x0c\x27\x44\xfc\x29\xb0\x08\x76\x18\x6e\xcc\x37\xd8\x44\xf9\x1b\xdc\xe3\x51\x99\xc9\x74\xaa\xa0\x5d\xad\x7e\xa2\xbd\xda\x85\x2e\x95\x5c\xc2\x96\x5f\x3f\xa7\x56\xd5\x8c\x07\x01\xb4\xef\x7e\xcf\x5a\x17\xee\x00\xb8\x48\x3f\x2f\xb2\x95\x08\x87\x45\x3d\x8b\x98\xcc\x70\xa9\x53\xf8\xf2\xc7\x46\x27\x3d\x11\x96\xbc\xbb\xbf\xd9\xbb\x7f\x7a\x22\x22\x34\x0f\x4a\x41\x5a\x60\x74\xf5\xb7\xa0\xa9\xdd\x69\x30\x28\x44\x57\xd3\x60\x70\x17\xda\x12\xd5\xef\x44\x5f\x5f\xb0\x5b\x85\xa4\xd0\x57\xef\x13\xa0\x45\xe6\x81\x12\x19\x6d\x84\xd6\x5d\x8e\xd8\x72\x8f\xbf\x42\x93\xb4\xc0\x87\x81\x60\x03\x4e\x0c\xec\x87\xf4\x62\xe0\x99\x5a\x20\xa4\xef\x7e\x90\x8d\x69\x58\xdf\x27\xfc\x3d\x1b\xe6\x57\x32\xd2\xef\xcd\x59\xa5\xdd\xfa\x5e\xc3\xfb\x32\x64\x4a\x3c\x1c\x71\xf9\xde\xe3\xfd\x72\xc8\xcb\xc6\xfd\x15\x18\xaa\xf1\x7f\x7d\x41\x7f\xc5\x77\x08\xfe\xeb\x0a\xa0\x6b\x5f\x51\xf0\xa8\xb1\x72\xca\x14\x02\x50\xa2\xc1\x2a\xef\x73\xc2\xd3\x68\xb5\x15\x17\x18\x5f\x18\xd9\x4e\xab\x98\x89\x16\x2b\xcb\x8d\xb4\xc4\xe3\xed\xcc\xb4\x58\xf5\xbf\xce\x4e\xab\x28\x02\xf7\xc5\x29\xfb\xd0\x9e\xdb\x54\x8b\xe2\xf2\x0f\xb0\x25\xb6\xca\x4f\x83\x99\x10\x0e\xa7\xc1\x6c\xf9\xd8\x0b\x0e\x17\x71\x1b\x84\xcf\x2a\x93\x8e\xf9\x6d\x0d\x96\xd1\xf3\x0d\xd4\xf4\xdb\x2c\x5f\x67\xb8\xee\x30\x5a\xa6\x7f\x3e\xd3\x65\xfa\xe7\x35\x60\xe6\x80\x1b\x12\x70\x29\x44\xcf\x51\xbd\xec\xb0\x89\xe6\x5f\x8a\x58\x46\x73\xc0\x4d\x03\x70\xc3\x0b\xb8\xe1\x04\xec\x86\x9c\x25\xe1\x6c\x02\x57\x2f\x25\x3a\x2c\xaf\x5f\x83\xdf\xc4\x57\xfa\xdc\x20\xcf\xeb\xe4\x11\x50\x70\x41\x11\x53\xf1\x99\x4e\x45\xe9\x33\x7a\x4d\x5a\xff\xf1\x47\x04\xd8\x7c\x46\x3f\xa1\x5a\x75\xad\xad\xcc\x50\xf9\x15\xfa\x9c\x13\xee\x42\x99\x7b\x6a\x0b\x3e\x0d\x66\x60\x33\xbb\x99\x95\x4a\x1c\x61\xe8\x74\x07\xfd\x84\x4a\x4d\xf4\x02\x7d\x2e\xb3\x9e\x36\x47\x4e\x6f\x27\x2b\x3e\x83\xad\xb8\x18\x0e\x79\xba\x6f\x9b\x1a\xd9\x07\x82\x12\xda\x40\x0a\x3a\x1d\xcb\x99\x04\x62\xeb\xc9\xe2\x6e\xe3\xe0\x71\x38\xc1\xa8\xa4\xf6\x93\x85\x0b\xf0\xc5\x1a\x71\x0e\x8b\xda\xcc\xf2\x7d\x66\x9c\x55\x85\x7a\x07\x3b\x79\x8d\x27\xdf\xde\xce\x52\xb0\xda\xa5\x18\xfd\x77\x6d\x6a\xc9\x76\x08\x6a\xd7\xa3\x6e\x25\xc5\xcd\x2d\x45\xad\x25\x37\x07\x51\x4f\x18\xca\x8b\x37\xc2\x50\x7e\x31\xdf\xb7\x4a\x24\xf8\x02\x27\x29\xde\x57\x0a\xca\x57\xae\xb8\x66\x3f\xc8\xcf\x5e\xea\xce\x05\xea\xda\x02\xf8\x9f\xc9\x7f\x08\xfb\x21\x2b\x94\x75\x30\x97\xd3\xe8\x0d\x9f\xf2\x85\xcd\x6c\xf3\x3f\x97\xcf\xd0\x06\xfa\x5c\x2c\x56\xa7\x83\xa5\xec\x9d\x47\x71\x82\xbf\x19\x57\x51\x40\xee\x45\x43\xf0\x73\x96\xd3\x1d\x92\x37\x07\xa3\x45\x3c\x43\x69\x87\xc2\xf8\x61\x63\x03\xbd\xa8\x2f\xe0\x49\x2a\x85\xa9\xb5\x6f\xc5\x88\x9d\x22\x41\x22\xd2\x5e\xa6\xf8\x7d\x1c\xcf\xe4\x92\xa8\x98\x38\x54\x94\x19\xd5\x44\x0e\xe3\xc6\x33\x98\x75\xd1\xca\xe6\x9b\xde\xd6\xf6\xce\xdb\xdd\xbd\xff\x7a\xf7\x7e\xff\xc3\xc1\xe1\xff\x3e\x3a\x3e\xf9\xf8\xcb\xaf\xbf\xfd\xfb\xff\x04\xfd\xc1\x10\x8f\xce\xc7\xe1\xe7\x2f\x93\x69\x14\xcf\xfe\x3b\x49\xb3\xf9\xc5\xe5\xd5\xf5\x1f\xb5\x7a\xa3\xd9\x6a\x77\xd6\xd6\x5f\x3e\x5f\xdd\x60\x11\x6e\xc5\xd1\x4e\x2c\xda\xa5\x51\x95\x43\xec\xf1\x4a\x91\x96\x1b\x9a\x85\xa9\x4b\x14\x32\xda\x71\xb9\xa9\x90\x99\x0e\x3d\xfb\x0d\x73\xec\x4a\x89\x90\xa4\x2c\x0f\x49\x4d\xaa\x03\x0b\x7a\x81\xea\xe5\x33\xf0\x5e\x91\x02\x53\xc3\x26\x2e\x0e\xb4\x51\x04\x68\xf9\x8c\x6f\xf0\xaa\x18\xe6\x80\x4a\x05\xa2\x48\x8b\xdc\xf3\x95\x08\x33\x80\xfe\x57\xda\xa2\xea\x5b\x13\xe5\x07\xef\x41\x6c\x88\x9f\x3f\xd7\x3e\x08\xb2\x15\x3f\x18\x45\x5a\xb1\x25\x9d\x61\x11\x6e\x64\xee\x1e\xf3\x90\xaf\xec\x11\xaf\xbc\x99\x7d\xda\x8f\x47\xff\xc7\xa3\xbf\x38\xfa\x7f\x3c\xd9\x79\x51\xef\xa0\x37\xdb\x85\x1d\xb4\xea\x9d\x37\xdb\xaa\x8f\x56\xbd\xa3\x3f\xc1\xd7\xdb\x3b\x6d\x51\x64\xfe\x5a\xc7\xad\x82\x38\xdc\xa3\xf3\x56\xbd\xe3\xf5\xde\xaa\x77\xfe\x01\x1a\x81\xe2\x87\x75\x18\x8c\xbb\x9c\xd5\xdd\xfe\xfe\x60\x19\x15\x0f\xf1\x61\x1c\x46\x99\xcf\xc9\xb8\xde\xf1\x38\x19\x3b\x0f\xd3\x12\x53\xbf\x97\xb1\x68\xb2\xa8\xab\xb1\x02\xf4\x0e\x27\x28\x93\x88\xef\xe4\xac\x06\xb4\xb9\xec\xda\xf8\xae\x8f\x51\x74\x55\x09\x97\x35\xbe\xf8\x96\xf2\x59\x83\x4a\xcb\xf9\x1a\xf3\x5a\x42\xbe\xe5\x2f\x1e\xda\xd3\x58\x6f\xb8\x98\xa3\x71\x1d\x64\x1f\x81\xa1\xee\x66\x4c\x44\x20\xb9\x58\x1a\x64\xb1\x18\x41\xd8\xfc\x14\xee\x93\x72\x8c\xd1\xf9\xa9\x78\x28\x0c\x46\x96\xef\x0b\xec\x61\xca\x3e\xf5\xfe\xce\xfb\xd4\xfb\xef\x60\x9f\x2a\x82\xc3\x7d\xef\x53\xce\xe5\xf4\x7e\xfb\x71\x9b\x12\x7f\xf7\xb6\x4d\xa5\x97\xc1\x6c\x3b\x1a\x86\x41\x54\x5a\x76\xc7\x72\x1d\xc9\xbf\xff\x2d\xeb\xfd\xc3\x6c\x59\x45\x96\xc9\xf7\xbf\x65\xbd\xdf\x36\x36\xad\xc7\x1d\xcb\xda\xb1\x94\x15\xb3\xd4\xe6\xf5\x4d\x77\x2f\x31\x2f\x0a\xb6\x04\x90\xd6\x47\x1e\x0d\x1f\xbe\xb0\xbb\x13\xba\xb8\x6b\x35\xf2\xff\x70\xb1\x42\x3f\x92\xee\xb3\xaf\xf4\x9b\x5c\xfe\x8b\xd4\x05\x40\x58\x7e\x6d\x41\xe7\x4e\xda\x02\x96\xa3\xf6\x5b\x2a\x0d\x2a\x48\x79\x95\x8e\x83\xba\xf1\x6a\x3c\x0d\x06\x0f\xa8\x5a\xa8\x20\xde\x2c\xfc\x82\xd6\xfe\x09\xea\x06\x2b\x5f\xec\x2d\x54\x11\x9a\x11\x8b\xf2\x65\x7f\xab\x0d\x35\xc1\xe4\x66\x7f\xab\xed\x92\xf1\xc0\xc4\xf9\x0b\xbe\xa6\x59\xb0\xa9\x1d\xac\xe8\x2b\x38\xff\x06\x51\xc6\x93\x78\xc7\xc9\x94\xda\x68\x6f\xff\x72\xf8\x09\x36\xdd\x93\xf8\x1d\x96\xc2\x20\xba\xbc\xbc\xac\xc6\x33\x1c\xa5\xe9\xa4\x1a\x27\xe7\xab\xc3\x78\x90\xae\x42\x12\xee\x78\xd5\xa8\x33\xce\xa6\x13\x87\x22\x64\xfb\x62\xf6\x6e\x6b\x47\xa2\x2d\x9e\x0b\x06\x43\x58\xec\x03\x62\xec\x71\x96\xf7\x0b\x4b\x79\x0e\x7b\x14\x19\x98\x94\x3c\x84\x11\x77\x7b\x51\xc2\x3d\x4b\x57\x97\x16\x2a\xd5\x1b\xeb\x9a\xa7\x8b\x05\xdf\x63\xa4\xa6\x86\xc5\x30\x13\xa4\xec\x6f\xb5\x17\x61\x1b\x66\xcc\x16\xd9\x0c\x52\xad\x7c\xc8\x62\x34\xa3\x56\xa7\xaa\x77\x8e\x67\x87\xb3\xfc\x62\x8c\xdd\x81\x0d\x4f\x17\xd5\x1b\xeb\x60\x42\xaa\x7d\xa5\x9d\x03\xcc\x8d\x2f\x12\x1f\xad\xed\x9b\x5b\xbb\xdd\x78\x88\xf6\xa1\xfd\x70\xb0\xd2\xe8\x3d\x98\x59\x7f\x19\x8e\x2c\xef\x1b\x4a\xf3\x0b\x52\x34\x2d\xae\xf8\xa7\x9c\xab\x75\x23\x9f\xdf\x6d\xc1\x54\xf4\x69\xac\xd5\x6a\x26\xe0\x25\xbd\x83\x16\xfa\xfd\x14\x93\x77\xb7\x20\x85\x3f\xa1\x11\x42\x15\x90\x08\x3b\x80\x0c\xac\x64\xd1\xde\xc6\x4a\x9f\xd7\xa5\xb1\x00\x5c\x80\x72\x2a\xa7\xc1\x24\x43\x9b\xf0\xcf\xf2\x62\x31\x50\x17\x25\xef\xfb\x20\x2f\x4c\x36\x8f\x2f\xc3\x51\x95\xba\x45\xe0\x12\xef\x4c\x05\xf0\xcb\xc9\x5b\x03\xc5\xb5\xfc\x8e\x7a\xcd\xa5\x04\x5e\x7d\x8a\x1d\xe2\x2d\x59\xe9\x8c\x7b\xd8\xb5\x85\x97\x1a\x21\x0f\x66\xa2\x2c\x57\x87\x13\x96\xcf\x2d\x0c\x42\x0b\xd0\x21\x7e\x07\x63\xe3\x4a\x89\xb6\xcc\x19\x59\x02\x13\x3e\xc1\xe2\x8d\xf7\xb8\xcc\xf7\x18\xda\x23\xf6\xe4\x28\xa7\x30\x71\x5a\x54\xbe\x70\x60\xf9\x96\x6d\x4c\x04\xbc\xfe\x91\x19\xb3\x18\xb8\x72\x83\x96\xd7\x1c\x1f\xe7\x51\x80\x88\x71\xe0\x39\xe0\xbd\x60\xd6\x5d\x96\x68\xd9\xc5\xd7\xca\x48\x0d\xc6\x20\x9d\x40\x18\x14\x4e\x6c\x8a\x51\xb0\x45\xaf\x7a\xf3\xc2\x9f\xce\x2e\x41\x68\x42\x0c\x9c\xfd\x59\x3b\x28\xd5\xe9\x41\x49\x19\xe8\xdc\xb4\x3f\x06\xf6\x02\x59\xef\x28\xb8\x30\x76\x0c\x95\xfd\x4e\x21\x2b\x16\x33\xc6\xd9\x86\x31\xca\x4a\x2d\x45\x47\xc3\xe9\xcf\x11\xed\x42\x04\x98\xe3\xf5\x8a\xda\x5c\x17\xe2\xc1\xaa\xdf\xf1\xad\x78\xef\x92\x7c\xf7\x1e\xbd\x6f\x1d\x7e\x65\x4a\x6f\x8a\x73\x73\xa5\x92\xa6\xdd\x50\xde\xeb\xdc\x5d\x7e\x40\x1a\x57\x17\x9b\x36\xdd\xaf\x7d\x9c\x7d\xb9\x6a\x15\xe4\x11\x1b\xee\x02\x26\x57\x6c\x10\x2a\x64\x29\xeb\xfb\xf6\x1c\xdb\x85\x85\x0d\xbb\x2e\xb1\x80\xe3\x4a\xfe\x7e\x77\xf3\x2a\xe7\xf8\x4e\xa1\xb9\xcf\xee\x15\x7e\xf8\xec\xb6\xd7\x2b\xfc\x48\xda\x5d\x5b\x23\x67\xfa\xb5\xbf\xf5\x99\x7e\x10\xce\xc6\x38\x79\xf1\xc0\x26\x02\x70\x7a\x57\x9b\xfa\x6b\x0e\xf1\x76\xe6\xce\x7b\x39\xcd\xf7\xa0\x63\x87\x84\xe3\xa4\xe2\xd0\xae\xbe\xf4\x9b\x10\x88\xf7\x46\x26\x0c\xad\x06\x39\xc3\x05\x19\x54\xa2\x3f\x39\x23\x66\x15\x77\xe0\x65\xc6\xa2\x2a\xd0\x22\x4b\xa4\xd3\x20\xa7\x1b\x3a\x37\x19\xbe\xca\xc8\x29\x32\x60\xcf\x68\x46\xfb\xc4\x7c\xb3\x78\xaa\x8d\x60\x88\x07\xe1\x34\x98\x4c\xae\x59\x1a\xd0\x61\xe1\x9b\x1b\x75\x54\x6e\x58\x2b\x6c\xe0\x4e\x04\x1a\x7a\xb3\xcb\x27\xe3\xb8\x0d\x7e\x0f\x9a\x9e\x43\x4e\x89\x72\xab\xa3\x76\x7e\xb9\x8b\x1d\xad\xa6\xc7\x51\x4b\x2d\x53\x95\xb3\x2b\x13\x48\xec\xe2\xab\x5b\x66\x82\x70\x0c\xaf\x42\x3e\xea\x7d\xc3\x92\xd3\x69\xdc\x3c\x84\xd1\x6c\x9e\xdd\x65\x4e\x39\x79\xe8\x44\x77\x0b\x3a\xbb\x2f\xe2\x18\x18\x8c\xc2\x41\x1f\xb7\x4e\x2a\x01\xa3\xe5\x0e\x61\x23\x27\x67\x03\xc9\x36\x68\x85\x57\x4e\xea\xe9\x69\xd4\xc3\x35\x02\x12\x50\x57\x05\x7a\xe3\xd6\xcd\xfb\x77\x5a\xd9\x5d\x63\xb7\x55\x36\x88\x6e\xbb\x51\x31\x94\xe7\xeb\x8f\xa6\x76\xff\x74\xdd\xb7\x6f\x77\xb4\x22\x99\xe7\x69\xc2\xed\x43\x0a\x38\x00\x0b\x8d\xab\x33\x11\x15\x29\xb1\xa1\x3a\xaa\xde\x4f\x42\x7a\x70\x79\x5d\xc8\xf1\x0a\x2b\x89\x0b\xaa\xa2\x88\xac\x0e\xce\xcb\x78\x90\xe0\xec\x9e\x94\x4a\x44\xfe\xdd\x75\x07\x0e\x82\x5e\x32\x36\xe1\xf2\x44\xa6\x8e\xbe\x45\x35\x86\xaa\x73\xb0\x27\x40\xb0\x53\x67\x24\xf4\x45\xd4\x47\x41\x3c\x9a\x1e\xee\x39\xde\x6e\xf7\x19\x5f\x16\x0e\x4c\x0b\xc2\xcb\xd2\x43\x95\x12\x5d\xd6\x1c\x27\xb7\x21\x7e\x8e\x62\x8a\x76\xf4\x8d\x12\x17\x93\x75\x3d\x2f\x32\xa6\x51\x89\xeb\x0b\x4c\x58\xee\x28\x99\x9b\x93\x49\x7c\x89\x82\xa4\x1f\x66\x49\x90\x5c\x23\xa6\x5e\xfa\x82\xaf\x1d\x71\x07\xbf\xa8\x1a\x89\x9f\x9d\x0d\xe7\x0c\x94\xa9\x6e\x29\x36\x5a\x0b\x9c\x21\x09\x4a\x39\x6e\x90\x10\xff\x0d\x74\x1b\x71\x82\xc2\x28\xc2\x09\x44\x9f\x8d\xe7\x19\x08\x10\x66\x14\x3e\x88\x99\x48\x75\x8c\x94\x0c\xd9\x03\x6d\xc5\x0a\x48\xc7\x35\x7e\x6a\x8d\xd0\x51\x63\x19\x12\x88\x15\xad\x64\x9c\xa7\x8f\x0c\x95\x82\xa1\x52\xd0\x6a\xec\xb7\x83\x23\x98\x4f\x7a\x0d\x38\x0b\x86\x68\x10\x47\x69\x16\x44\x66\xf3\xce\x24\x52\xfa\x1c\xfb\x15\x6b\x02\xef\xd3\xf0\x0c\xfd\xbe\x81\x6a\x57\xed\x01\xfd\x9f\xcb\x1d\xc6\x2a\xdc\xec\xd0\xff\xe5\x6b\xc6\x62\x43\x27\x16\x1a\xcf\x2e\x8a\xfc\x0b\xe2\x90\xc1\x0e\xf4\x10\x51\xc8\x04\x13\xbf\x97\x48\x64\x39\xf9\xca\x5c\xcc\xd8\x31\x90\xd0\x69\x17\x1f\xf7\xe8\x49\x75\x7d\xb1\x5c\x30\xb7\x8b\x40\x06\xc3\xfc\xdd\xc4\x1f\xdb\xdf\xec\xb1\xe8\x63\x80\x57\x08\x4b\x2c\x37\x12\xca\x92\x53\x5e\x24\x10\x99\x55\xfa\xfe\x83\x91\xa9\x24\xc1\x5b\x59\x18\x7c\xec\xa1\xa2\x87\xc1\x50\xff\x4f\x8f\x1e\xb6\x40\x4c\x5d\x46\x44\x24\x3c\x54\xd2\xd0\xc2\x08\x62\xfe\x1a\x0b\xa3\x88\xf9\xab\x3e\x50\x24\xb1\xbb\x73\xbb\x1e\x55\x4f\xc3\x78\x3b\xf6\x63\x22\x5d\xec\xba\x83\xa3\xe5\x06\x1c\xcb\xe5\x98\xea\x58\x19\x40\xa5\x84\xc2\x25\x0d\x7e\xc9\x24\x50\x29\x7b\x43\x8e\x4d\x83\x81\xfb\x92\x48\x1c\xfc\x3d\x46\x70\x2f\xff\xd6\x0a\xf3\xab\x4e\xeb\x85\xe3\xf5\x24\xec\xbf\x20\xa8\x0c\xc1\xb6\x35\x35\xbe\xe2\x68\xf0\x02\x6c\x1a\x1d\xef\xa9\x9b\xa5\xf1\x61\x3a\x6c\x2f\x36\xbe\x4b\xc7\x41\xa3\x6d\x82\x24\x2f\x1b\x26\xb8\x74\x1c\xb4\xeb\x0d\xfb\x65\x73\xdd\x51\xb2\x69\xbc\x4a\xc2\x19\x9e\x0e\xeb\x9d\x9a\xd3\xf6\x4f\x7b\x35\xeb\x7f\x19\x8e\xcc\x76\xf0\xc5\xec\xcb\x70\x94\x77\xef\xa0\x77\x3d\x1e\xe2\x17\x83\x51\xdf\xf9\x3a\x4b\x3c\xaf\x5f\x9c\x4f\x82\xe1\x34\x88\x5c\x9f\x63\x37\x30\x3c\x30\x5f\xcf\x82\xe1\x8b\x20\x4a\xc3\xab\x97\x0d\x73\x10\xc8\xa7\x30\x8d\xeb\xb5\x7a\xc3\x1c\x71\xf6\xe9\xe5\xda\xcb\x35\x73\x86\xc8\xa7\x3f\x70\x12\x33\xd7\x6b\xc7\xd7\xc8\xf3\x8d\xea\xc8\x5e\x8c\xf1\x95\xf1\x21\xc0\x26\x71\xd1\xb8\x1b\x43\xeb\x7d\x32\x30\x27\x37\x09\xfa\xfd\x30\x73\xbe\x7c\x31\xc1\xe7\xc1\xe0\xfa\xa1\xef\x80\xc4\xea\x81\x27\x73\xd1\xc0\x4b\xb9\x56\xc4\x23\x5b\x22\xf0\x4c\x56\x86\x61\x16\xca\xd6\x81\xf8\xdd\x68\x89\xdf\x84\xea\xf9\x6f\x42\xec\xe2\x37\xfd\x25\x49\x5b\xda\x97\xc2\x2f\x46\xc8\x14\x03\x4a\xbf\xd6\x1d\x16\x45\x87\x53\xab\xf2\x94\x25\xfa\x93\xa0\x4d\xf9\x36\xd6\x6a\x10\x4a\xa4\xcd\xaa\x04\x28\xde\x08\xba\x53\xdf\x50\x72\x13\x6f\x54\x2a\x13\x2f\x23\xfd\x95\x42\x53\xf0\x4c\x48\x09\x7e\x48\x0a\xa2\xa3\x32\x60\x03\xc5\xe8\x45\xf9\xcd\xc9\x64\x59\x45\xa4\xa6\x80\x54\x79\xed\xf2\x8a\x49\x7f\x28\x36\xd6\xa5\x6e\xbb\x5e\xc9\xd7\x26\x57\x74\xba\xea\xb6\x5b\x15\x8d\xf0\xba\xed\x76\x45\x4e\x7c\xb7\xdd\xa9\xe8\xa3\xd7\x6d\xaf\x99\x37\xc2\x26\x29\x77\x3b\xb5\x0a\xa3\xd6\x6e\x07\xf0\x11\x94\xd2\xed\x34\x2a\x2a\xad\x74\x3b\xad\x8a\x8b\x5a\xba\x9d\x66\x45\xa5\x90\x6e\xa7\x5d\x51\xe9\xa7\xdb\x01\xbc\x34\x9a\xe9\x76\xd6\x2a\x26\xd5\x74\x3b\xeb\x15\x93\x6e\xba\x9d\x97\x15\x8b\x48\xba\x6b\xb5\x8a\x83\x9c\xba\x6b\x80\x3f\x5b\x12\xdd\x35\xc0\x9e\x91\x46\x77\xad\x55\xb1\x88\xa3\xbb\x06\x88\x13\x32\xea\xae\x01\xce\x72\x9d\x75\xd7\x3a\xea\x05\x7a\x45\x2e\xd9\xee\x1a\xbf\x5a\x27\x8b\xb9\xbb\xf6\xb2\xc2\x97\x6a\x77\xbd\x56\x91\x4b\xb8\xbb\x5e\xaf\xc8\xc5\xdd\x5d\x07\x74\x24\x05\x77\xd7\xa1\x71\xc1\x68\xba\xeb\xad\x9b\xb3\x4a\xa7\xf6\x78\x79\xf0\xd7\x5f\x1e\xf4\xc6\x78\xf0\x85\x74\x0a\x56\x0a\x75\x03\xa2\x69\xce\xd2\xf9\x8c\x0c\x0c\x66\xf1\xa9\x95\x7e\x83\x1c\x4f\x43\x9a\xa3\x1f\x36\xd0\x0a\x87\xbc\xe2\xb0\x08\x11\x4e\x1a\xf7\x78\x5d\x91\x6b\x8e\x2f\xda\x39\xc2\x23\x9c\x60\x38\xe8\x25\xe1\x39\x9c\xc9\xc2\x28\xcc\x24\x98\x74\x3e\xc3\x09\xa8\xae\x37\x8c\xf4\x1c\x0a\x94\xcd\xf9\xf9\x14\x47\x99\x51\x00\x65\x31\x1a\x07\xd1\x70\x82\xb5\x71\x53\x61\xf7\x9d\x90\x35\x9b\x1a\xa8\x6a\xbb\x03\x2a\xba\x6f\x1a\x4b\x9e\x9a\x40\x85\x51\xb6\xae\x68\xe8\x47\x6a\x7d\xa1\x98\xd0\x67\xc7\x3e\xe6\xcb\x1a\x54\x09\xff\x91\x40\x85\x17\x2a\x36\xda\x21\xc2\x89\x58\x4c\xd3\x7f\x01\xa4\x8b\x10\x5f\xfa\x50\xf4\x36\xaf\x20\xbc\xc7\x51\x40\x5f\xbf\xea\xe5\x39\xc1\x01\x96\xa0\x33\xe6\xd5\x7f\x20\x6b\x4e\xd8\x8e\xc0\xa2\x73\x03\xb7\xaa\x96\xad\x56\xbc\x58\xd5\x3b\x6e\xb4\xfc\x2d\x2d\x57\x63\x2f\xca\x9a\x8d\x65\x9b\x58\xae\xc6\xce\x24\x0e\x6e\x53\xa5\xd3\x82\xf7\xb2\xfc\x2d\x49\xa9\x4a\x29\xb8\x82\xd4\x57\xd7\x19\x3e\x80\xe4\x40\xd6\x6b\x57\xde\x65\x8d\xfe\x76\xe9\xa2\x93\x6d\x15\x59\x11\xb2\xf4\x72\x2a\x04\x09\xed\x8d\xc0\x0d\x6d\xb8\x71\x76\x68\x16\xb6\xaf\x58\xf6\xd5\xeb\xcc\x65\xfc\xbc\x94\xbb\xa0\x0b\x95\x65\xf2\x69\xcb\xfa\xa7\xe1\xd9\xad\x92\x67\x4b\x73\xee\xf0\x0f\x4c\x55\xb5\xd2\x71\x54\x2f\x2a\x18\xab\x4c\x6d\x51\x41\xcc\x8d\xd0\xd5\x11\x6d\xbe\x9d\x59\xcf\xc8\x68\x92\xd7\x04\x1e\x8a\x88\xd4\xa7\x32\x73\xbb\xdd\x60\x36\x9b\x5c\xb3\x86\x83\xe4\x7c\x4e\x58\x78\x9a\xe7\xaf\xc8\xf8\x75\x75\x96\xc4\x59\x4c\x70\x54\x39\x77\x9e\xe1\x84\xb9\xfb\xb8\x15\x2c\x9d\xfa\xa3\xac\xf3\xd7\xc8\x3a\x10\x30\xfa\x2f\x88\x4b\xe4\xcc\xa9\x54\xc0\x44\x02\xb6\x58\x7a\x8f\x87\x32\xa9\x5b\x27\x55\x4e\x18\xb3\x50\x4a\x52\xd5\xa5\x71\xf3\xe7\x92\xf4\x7c\x7c\xa5\xd3\x72\x73\x91\x13\xc2\x26\x36\xe8\xf0\x55\x83\x7e\x4a\x7f\xa4\x61\xc4\x82\xb1\x12\x96\x51\xbb\xaa\xd7\xd8\x5f\x19\x7d\xd5\xd3\xf8\xb2\xe5\x55\x2a\x3b\x2d\xd4\xf7\xb7\xda\x86\x35\x85\xcb\x00\xc4\xf4\x9a\x44\x1b\x6c\x54\x1d\x06\x20\x3c\xed\x4d\xee\xed\x98\xd4\x04\xbb\x73\x15\x9f\xda\x9c\xb4\x76\xd5\x59\x6b\xb5\x1b\xcd\x5a\xbd\x82\x6a\x57\x78\x34\x18\x06\xfd\xf5\x97\x8e\xbc\x8a\xb5\xab\x97\xeb\xfd\x60\x38\x18\xe1\x0a\x0c\x4c\xb3\xd1\x6e\xad\x75\xf4\x72\x67\xde\x1b\x31\x23\x8d\x9e\xda\x8b\x7d\x91\x49\xcf\xb5\x77\x5d\x06\x33\x84\xc1\xbd\x7a\xf1\x1e\x52\xef\xf8\x77\x0c\xff\xf5\x35\x9f\x0d\x8a\xc4\x27\x02\x8f\xa7\x17\x44\xa1\x27\x02\xef\xfe\x27\xa5\xf4\xfe\x29\x7f\x38\x73\xb9\x84\x28\x9f\x09\xc1\xd9\x05\xc8\x5f\xa9\x54\x52\x60\x52\x4f\x71\xf4\x15\xa9\x2f\x61\xaf\x6b\x95\x0d\x1f\x71\xf4\xb5\x20\xc0\x46\xab\xec\x00\x08\xa1\x8c\x35\x97\x74\x1b\xdc\xdd\x8c\x43\x76\xb5\x1b\x0a\xf7\x75\xbf\x36\xa4\x35\xa4\x8c\x29\x7a\x8e\x6a\xa6\xf8\xa0\x95\xae\x1b\xa5\xeb\xb9\xa5\x1b\x46\xe9\x46\x6e\xe9\xa6\x51\xba\x99\x5b\xba\x65\x94\x6e\xe5\x96\x6e\x1b\xa5\xdb\xb9\xa5\x3b\x46\xe9\x4e\x6e\xe9\x35\xa3\xf4\x5a\x6e\xe9\x75\xa3\xf4\x7a\x6e\xe9\x97\x46\xe9\x97\xf9\xb3\x53\x33\x66\x67\xc1\x64\xd6\x8d\xe2\xf9\xb3\x59\x6f\x18\xc5\xf3\xa7\xb3\xde\x34\x8a\xe7\xcf\x67\xbd\x65\x14\xcf\x9f\xd0\x7a\xdb\x28\xde\xb6\xb8\xc1\xea\x2a\x61\xc8\x5f\xc2\xe8\x9c\x54\x0d\x83\x49\xdf\x25\x36\x07\x64\x1b\x38\x75\x0e\x54\x1f\x3e\x39\x07\x65\x00\x9f\x9c\x03\x30\x84\x4f\x4d\x17\x3a\x3d\x79\x07\xad\x7f\x23\x48\xec\xec\x94\x82\x0a\xea\x57\xd0\xa0\x82\x86\x15\x65\x81\x56\x10\x5a\xab\x90\x2d\xb4\x76\x66\xf2\x86\x21\xad\x37\xac\x20\x51\x55\x8e\x50\x05\xa1\x7a\xa3\x82\x4e\x4e\xeb\x56\xbd\x01\xad\x47\x5b\xa2\x55\xe5\xa2\x25\xf5\xd6\x48\xbd\x86\x55\xaf\x4f\xeb\x09\x24\x03\xa5\x5e\xb3\x82\x50\x03\xda\x6b\x5a\xf5\xf2\xfa\xd7\x12\xfd\x6b\x2d\xd5\xbf\xb6\xe8\x5f\x7b\xa9\xfe\x75\x44\xff\x3a\x4b\xf5\x6f\x4d\xf4\x6f\x6d\xa9\xfe\xad\x8b\xfe\xad\x2f\xd5\xbf\x97\xa2\x7f\x2f\x97\xea\x5f\xbd\x56\x61\xfd\xab\xdb\x04\x93\xd7\xc1\x7a\xbd\xc2\x3a\x58\xb7\x29\x26\xaf\x87\x04\x4b\xda\xc3\xba\x4d\x32\xb9\x24\xda\xac\x70\x12\xb5\x69\x26\xb7\x8f\x2d\xd1\x47\x9b\x68\x72\xfb\xd8\x16\x7d\x04\xaa\xb1\x3b\xf9\xf6\xad\xa7\x93\x15\x84\xda\xb4\x93\x36\xdd\x0c\x69\x45\x67\x27\x09\xbd\xbd\xa4\x15\x6d\xc2\x19\xd0\x8a\xee\x4e\xd6\x2b\x88\x74\xf4\xe4\xb4\x6e\x53\x4e\x9f\x56\x74\x76\x92\x70\x8c\x46\x0d\x2a\xda\xa4\x93\xd7\xc7\xb6\xe8\x63\xc3\xcd\x6b\x7c\x7d\x24\x34\x47\xfb\xd8\x70\x33\x1b\x6f\x1f\xdb\xbc\x8f\x0d\x37\xb7\xf1\xf5\xb1\x25\xfa\xd8\x70\xb3\x1b\x5f\x1f\x5f\xca\x3e\xba\xf9\x8d\xb7\x8f\x2d\xd1\x47\x37\xc3\xf1\xf5\x91\x30\x46\xd6\x47\x37\xc7\xf1\xf5\x71\x5d\xf6\xd1\xcd\x72\xbc\xb4\xda\xac\xf0\x3e\xba\x79\x8e\xaf\x8f\x0d\x41\xab\x0d\x37\xd3\xf1\xf5\x71\x4d\xf4\xb1\xe9\x66\x3a\xbe\x3e\x92\xe5\x4f\xfb\xd8\xac\xbb\x17\xe4\xee\xae\x9f\x58\x5b\x80\x6b\xd3\xcd\x75\x76\x77\xdd\x9d\x24\xc3\x4a\xd6\xd6\xc9\x69\xd3\xcd\x75\x76\x77\x73\x16\x64\x07\x2a\xba\xb9\xce\xee\xae\xa7\x93\xad\x0a\x6a\x34\xa1\xa2\x4d\x3a\x79\x7d\xac\xcb\x3e\xba\x99\x8e\xaf\x8f\x2d\xd9\x47\x37\xd3\xf1\xf5\x11\x26\x92\xf6\xd1\xcd\x74\xbc\x7d\xac\x89\x3e\xba\x99\x8e\xb7\x8f\xcd\x0a\xeb\x63\xcb\xcd\x74\x7c\x7d\xac\x89\x3e\xb6\xdc\x4c\xc7\xd7\xc7\xa6\xe8\x63\xcb\xcd\x74\x7c\x7d\x24\xac\x9c\xf6\xb1\xe5\x66\x3a\xbe\x3e\xbe\x14\xf3\xd8\x72\x33\x1d\x5f\x1f\xc9\xf2\x60\x7d\x74\x33\x1d\x2f\xad\xb6\x39\xad\xb6\xdc\x4c\xc7\xd7\xc7\x86\xec\xe3\x9a\x7b\x41\xee\xed\xf9\x05\xd5\x0e\xed\xa4\x9b\xeb\xec\xed\xb9\x3b\x09\x34\x07\x3c\xa0\xe5\xe6\x3a\x7b\x7b\x39\x62\x40\x1b\x44\x40\x37\xd7\xd9\xdb\x73\x77\x92\xf0\x8e\x06\x0c\x6b\xdb\x2d\xea\xf8\xfa\x48\xe6\x83\xf6\xb1\xed\x66\x3a\xbe\x3e\x36\x45\x1f\xdb\x6e\xa6\xe3\xed\x63\x4d\xf4\xd1\xcd\x74\x7c\x7d\xac\xcb\x3e\xba\x99\x8e\xaf\x8f\xeb\x62\x1e\xdb\x6e\xa6\xe3\xeb\x23\xd0\x1c\xed\xa3\x9b\xe9\xf8\xfa\x08\x22\x39\xed\xa3\x9b\xe9\x78\xfb\xd8\xac\xf0\x3e\xba\x99\x8e\xaf\x8f\x2d\xd1\xc7\x8e\x9b\xe9\x78\xfb\x58\xe7\x7d\xec\xb8\x99\x8e\xaf\x8f\x0d\xd1\xc7\x8e\x9b\xe9\xf8\xfa\xf8\x52\xcc\x63\xa7\x69\x2f\x48\xb8\x46\xc9\x70\x32\xc5\xc3\x30\xc8\x98\x53\x19\xb8\x2b\xe8\xe5\xc8\x11\x17\x6d\xa0\x12\xfc\xfb\x1c\x05\xa6\x86\x95\x96\xa9\xb3\x32\x75\x52\xa6\xef\x2e\xd3\x60\x65\x1a\xa4\xcc\xc0\x5d\xa6\xc9\xca\x34\x49\x99\xa1\xa5\xcd\x35\x54\x95\x3b\x0e\x4b\xdd\x25\x03\xda\x42\xa6\x74\x91\x4d\x37\xc8\x02\xd7\xc1\x3c\xc8\x02\x11\xca\x27\xc8\x02\xbf\x72\x2c\x7a\x13\x66\xe9\x49\x9c\x05\x13\x01\x33\xda\x0a\xb2\x80\x7a\x90\xfc\x84\xd6\x1d\xd0\xa1\xce\x7b\x3c\xca\x38\x74\xe1\x71\x02\xe5\xad\xce\x78\x53\x5e\x09\x34\x4f\x25\xc8\x9f\x7f\xfe\x19\xb5\xe1\xe2\xad\x76\xb5\x5e\x93\xf7\x6d\xb2\xc4\xbf\x50\xb3\x61\x11\x87\xde\x97\x5d\xb4\x81\x40\xed\x3e\x9a\xc4\x71\x52\x52\x3a\xb9\xaa\xe9\xde\x7d\x9d\x83\xb2\xef\xd1\x86\xf2\x64\x2e\x1c\x81\x7a\xa9\x54\x92\xb8\x3d\x47\x9d\x16\xcd\x97\xf6\x12\x82\x89\xb6\xca\x54\x61\xe3\xd6\xcf\xf2\xaa\x0c\x67\xa9\x9c\x55\xdf\x16\xd7\xce\xda\xe0\x98\x6a\xd6\x04\xb7\x48\x37\x6b\x71\x89\x65\x3a\xdb\x2a\xd2\xd9\xf7\xce\xce\xbe\xbf\x6d\x67\xdf\x3b\x3b\xfb\xbe\x68\x67\xed\xde\xaa\x4e\x54\x25\xd1\x7d\x1e\x6c\x0a\x72\xea\xb9\xfd\x07\xc1\xe0\x9d\xba\x31\x80\x8f\xa2\xcb\x93\x2a\x37\xaf\xfc\x02\x6f\x48\x4d\xe7\xed\x20\xdf\x5d\x66\x18\xef\xf5\x7e\x5b\xea\xde\xc3\x73\xc5\x85\xf2\xae\xff\x05\x26\x70\x85\xb1\x7b\xea\xbe\xbb\xd8\x65\xb7\x64\xa5\xd2\xae\x76\x2d\xb1\xbb\xf4\x7d\x04\xa5\x85\x5d\xed\x2e\x62\xd7\x7b\x09\xb1\xf8\xc6\xe1\x88\xe5\x06\x86\x39\x64\x11\x78\x86\x30\xa6\x7a\xd1\x02\xc9\xca\xc1\x0d\x21\x97\xd5\x83\x82\x15\x9c\x32\xc5\x0d\x1d\x3c\xca\xeb\x7f\x6b\xe3\x85\xcf\x9f\x2c\x5a\xf0\x79\x57\xf2\x08\x1a\xe4\xab\xdb\xc3\x81\xfe\x12\x48\x1a\xaa\xaf\xab\x0a\x4a\x2b\x48\xbf\x42\x03\x3e\x89\x36\x50\x80\x9e\xa3\x52\xa9\x8f\x7e\xa4\x9b\x63\xe9\xff\x92\x9f\xc3\x32\x61\x03\x57\xe8\x39\xca\x94\xf6\x44\xc0\xe2\x88\x4c\x53\x4a\x57\x2a\x8d\x53\xde\x6c\xa0\x17\x28\x2d\x43\xb5\xbe\x61\xf4\x26\xb0\x32\xce\xff\xc5\xb0\x82\xed\xb8\x34\x40\x3f\xa2\xff\xfb\x30\x58\x19\x87\xa0\x85\x58\xf5\xd1\xef\x68\x80\x7e\x27\x88\xdd\x3f\x32\x86\x00\xb8\x10\x19\x82\x48\xa9\x8f\xbe\xde\xf3\xe0\xa8\xb7\xd5\xc7\xbe\x34\xe9\x0b\x13\xef\x17\x09\xb2\xc6\xfd\xc4\x0c\x17\x45\x58\x0d\x36\x18\x8f\xb3\x98\xa7\xf4\x6d\xc3\x9a\xb1\x75\x29\x8c\x5c\xf6\xb7\xda\x0e\xdf\xaf\xfc\xf2\xb6\xc3\x97\x8c\x2f\xa6\x5d\xe6\xeb\x19\xf9\xf7\xb7\xda\x4e\x93\x01\xef\x24\x2c\xc8\x55\x7f\x5f\x53\x70\xab\xd0\x0e\x8b\x27\x4e\xf5\xf2\xbb\x8f\x89\xa3\x4e\x65\x62\x22\x76\xa7\xc1\x80\x4c\x86\x96\x19\xde\x9e\x0f\x56\xcc\x9e\x13\x99\xcd\x9e\xce\x4b\x6e\x06\x76\x16\xd9\xda\x63\x01\xd5\xf8\x5b\xbb\x98\xfd\xf3\x63\xb2\xd1\xc5\xf6\x13\x8b\x33\x84\x76\x30\x1e\xf6\x83\xc1\x17\x16\x57\x73\x1a\x0f\x61\x49\x11\x9a\x11\xf3\x0d\x2f\x7b\x3b\x6f\x88\x08\xe4\x10\x0f\xc0\xcc\x09\xbe\x6a\xd6\x72\x60\xe1\x42\x5b\xd9\x27\x00\x98\x31\x8f\x58\xf5\xbd\x9d\x37\xd5\xed\x88\xc6\x2a\x07\x03\xaa\x9d\x37\x0e\x83\x9f\x99\xc7\x5c\x86\x99\x19\xe6\x98\xcc\xf8\x45\x53\x16\x82\x8a\x0b\x24\xf4\xd1\x75\xcf\xac\x84\xf2\xa0\x85\xd4\x50\x1e\x7a\x79\x1e\xa3\xfc\x1d\xbe\x4e\xb3\x04\x07\xd3\xcd\x68\xc8\x7a\xe7\xb0\x8e\x8c\x99\x59\xac\x00\x57\x61\x0d\xb8\x84\xec\x23\x3c\xc5\x10\x64\x1c\x8c\x31\xe9\x3c\xb1\x58\x99\xe0\x3f\x1f\xe1\xab\x8c\xbe\x76\x8b\xef\xf8\xe2\x0d\x8b\x99\x0a\xad\x57\xd3\x49\x38\xc0\x25\x8e\x82\xb8\xa9\x17\xb8\xb8\xec\x27\xb5\x59\xdb\xc2\xff\x94\x59\xbb\xc3\xe8\x82\xe1\xf0\x38\x4c\x97\x1e\xdb\x6f\x46\x37\x27\xb2\x43\x7d\x3c\x88\xa7\xcc\xeb\x9e\x10\x44\x18\xcf\xd3\x62\x24\x23\xba\x58\x48\x1c\xcf\xe9\x4d\x69\x61\x17\x0c\xdf\x08\xfb\xc0\x06\xe7\xbd\x0b\x19\xac\xe5\xe2\x95\x6e\x34\xae\x86\x63\xa6\xcd\xcb\xcf\x90\xd9\xf5\xc2\x79\xa4\x11\xa5\xd1\x06\x0a\x2f\xd8\x14\xd6\x3c\x2b\x31\xbe\xc0\x68\xef\x17\x38\x7f\xa6\xf3\x7e\x8a\xff\x7b\x8e\xa3\x2c\xe7\xf4\x0c\xf8\x0a\x07\x86\x85\x06\xd0\x26\x3e\xc6\x84\xd8\x93\x40\xfe\x18\x95\x63\x3a\xd0\x50\xb0\x24\x80\x54\x90\xde\x95\xd5\x55\xc4\x66\x44\xbe\x73\x66\xcb\xcd\x8f\x1a\x43\x4d\xcf\xa5\x85\x20\x44\x82\x11\x8d\xc2\x39\xda\xa2\x17\x86\x05\x17\x27\x76\xde\xe4\x19\x5c\xf3\x4d\x67\x99\x38\x75\x9d\xe6\xa3\xf0\xf1\xbd\x0b\x1f\xe8\x3f\x67\x09\x4e\x71\x72\x81\xa9\x18\x12\xcf\x89\x28\xaf\x88\x1f\xa0\xc6\x08\xb2\xb0\x3f\x61\x1c\x18\x6d\x25\xe8\x4d\x12\x06\x11\x7a\x4b\xdd\x33\xd1\x28\x9c\x60\x1c\x0d\xaa\x03\x00\xc1\x43\x3e\x43\x04\x6c\x83\x7e\x4e\x8e\xa0\xc8\x7f\x05\x11\xda\x4d\xe6\xfd\x6b\xf4\x79\x4c\xfe\xa9\x5e\xe2\xfe\x7f\x9e\x4f\x83\x70\x52\x1d\xc4\x53\xb7\xbc\x73\x72\xc4\x9b\xcb\x11\x7b\xd4\x42\x85\xa5\x9f\x27\x32\xdf\x4b\x34\x20\x07\x05\x9a\x32\xe9\xe9\x93\x27\x64\xd0\x81\xf4\x44\x3a\x24\x50\x12\x51\xa5\x50\x19\x66\x9d\xfe\xfa\x13\xad\xae\xc6\x17\x38\x19\x4d\xe2\x4b\x52\x07\x36\xbe\x3a\x4f\x07\x4a\xea\xd5\x3b\xe5\x1f\x49\xd9\x57\xe2\x73\x43\xfd\xbc\x6e\x7e\x6d\xb2\x3d\x8c\x35\x06\x78\x02\x2a\x04\xac\x68\x77\x75\x15\xf1\x66\x51\xbf\x4e\x8a\x00\xca\xd0\x74\xed\x95\xa8\xd2\x90\x55\x44\x99\x27\x80\x00\x2d\x44\x4b\x35\xf5\x52\xac\xd8\x13\x40\x85\x95\xbb\x81\xff\x12\x82\x54\x4b\x3c\x7f\xde\x6f\x2a\xdf\xe1\x3f\xbc\x0c\x2d\xf2\xfc\x79\xbf\xf1\xea\xa9\xbf\xc0\xf3\xe7\xfd\x3a\xfb\x4e\xfe\x0b\x1d\xe7\x8d\xc2\xc3\xf3\x0d\xe8\xf9\xeb\xd7\x2c\x1f\xa4\xfa\xba\x41\x55\x80\xda\x5b\x86\x90\xdd\x92\xa8\x56\xbb\xaa\xd5\x99\xd6\x4f\x16\x65\x5c\x8f\x14\x22\x2f\x6f\x4c\xea\x60\xcb\xa3\x34\xa0\xff\xea\x34\xc2\x5e\xd2\x1b\x24\x4e\x4a\xf2\x65\x99\x11\x8c\x32\x05\xab\xab\x88\xec\x12\x70\x13\x83\x42\x65\x21\xd1\xc5\x63\xad\xb4\x95\x14\x01\xbc\x14\xc5\xd1\xe4\x9a\x2e\xc7\xad\x5f\x0f\x8e\xb6\xd0\x67\xf4\x1a\xad\x03\x4c\xde\x60\xdd\x85\x05\xbd\x8b\xd3\x3b\xcb\xbe\xf1\xfe\xf2\xb5\xa4\x9d\x05\xc4\xba\xaa\x7a\x5e\xff\x85\x32\xe7\xb2\x22\xa7\x55\xdc\x90\x61\xec\x56\x19\x4f\x14\xcd\xf2\x01\xb3\x50\xcf\x93\x78\x90\x5f\xea\x01\xa1\xc1\xdd\x48\xbe\x0c\x84\x6e\x21\x07\xa1\xc5\xb2\x10\x97\x0e\x08\x61\xdb\x34\x4f\x59\xd1\x13\x53\x34\x62\x9f\x15\x5c\x75\xd5\xf3\x32\x42\x11\xf2\x08\x46\xe8\x76\xc2\x11\x5a\x52\x40\x42\xba\x3c\x67\x1f\xba\x24\xdd\xab\x67\x2f\xb1\x34\x5e\x19\x92\x95\x28\xae\x08\x58\x5e\x11\x4b\x29\xbc\x84\xa4\xd5\x7a\x94\xb4\xbe\x77\x49\xcb\x23\x5f\x79\xd4\x3b\x27\x47\xf9\x72\xce\xb2\xea\x1d\x07\x4b\x37\x79\xf9\x23\x13\xff\xe7\x31\xf1\xdc\xd3\xec\x03\xb0\xec\xbd\x68\x90\x60\x88\xdc\xc0\x80\x1b\x20\x99\x1c\x22\x27\xf7\x05\xa2\xc6\x34\x9e\x2f\x70\x5b\xfe\x15\xd5\xfe\x56\x9b\x43\xd1\x5d\x61\xf1\x79\x9b\x94\x59\x62\x17\x68\x3f\xee\x02\x7f\x8b\x5d\x60\x7b\x82\x07\x59\x12\x47\xe1\x00\xf5\xe2\x21\xee\xc7\xf1\x62\x85\xff\x76\x2f\x4f\xe1\x4f\xbf\x2e\xb5\x23\x6c\xf7\x74\x85\x3f\x79\xbe\xaf\x1d\x40\x65\xed\x3a\x03\xd1\xeb\xe5\x69\x31\x09\x3e\xda\x42\x7a\x28\xfc\x86\xf8\x56\xf8\xf1\xd4\x4b\xbd\xc5\x7a\x33\x28\xb3\xc4\x3a\xfe\x7b\x27\x47\xfe\x9f\xb3\x8e\x0f\xe6\xd9\x6c\x9e\x15\xbf\xb4\x3b\xc8\xbd\xb4\x3b\x58\xfe\xd2\xce\x94\xea\x0e\x8c\x4b\xbc\x83\xbf\xf6\x3a\xe8\xc1\xa5\x3a\x5b\x37\x2f\xde\xdc\xaf\x64\x97\xd3\xd0\xf7\x22\xdd\xfd\x93\x4e\xd8\x07\xc6\xb5\xa6\x4f\x88\x3a\x28\x70\x69\x71\xb0\xe4\xa5\xc5\x63\x16\xbb\xbf\x07\xf3\xdd\xfc\x70\xbc\x87\x7e\xab\xbe\x6c\x34\xb9\x81\x38\x4a\x33\xb2\xbc\xcf\xaf\x2d\xee\x3b\x0b\x86\xd5\xcd\x28\x0d\x7f\x23\xa5\x45\x2e\xb8\x59\x30\x54\xd9\xdf\x30\xc8\x02\xe5\x22\xd4\x77\x01\x9a\xea\x37\xa0\xa4\xd6\xb1\x34\xf8\xd5\x0c\x80\x5f\xe9\x45\xfb\x66\x5a\x91\xbe\x2f\xa1\x08\x10\xc5\x3c\xca\x44\xcf\x8c\x60\x56\x60\x8b\x77\x48\xbf\x59\xc0\xe8\x8b\x17\x3a\x66\xff\x32\xbe\x5b\xad\xd1\x98\x36\x93\x20\xa5\x91\xb3\xd0\x2c\x4e\x43\xdd\x03\x9f\x34\x4a\xbe\x93\xfa\x87\x31\xef\xac\x68\xe1\xb9\x81\xd1\x0b\x54\x37\x1a\x39\x0c\x86\xf2\x19\x06\x4a\x64\x1b\xd1\x5f\x53\x56\xa2\xb6\x25\x43\x6a\xe9\x8d\xc8\x90\x5a\x6a\x69\x57\x70\x2d\xdd\x32\xfb\xb9\x01\x88\xdb\x21\x72\x0b\xdc\x79\xe4\x20\x0e\x93\x22\xde\xe2\x4c\x49\x38\xaf\x4d\x15\x55\xe0\x8b\xd1\xcc\x9f\x39\xa5\xcf\x25\x1d\xcd\x17\xe4\xf8\xcb\xfa\x2e\x2f\x82\x14\x14\xd8\xbe\x62\x79\x48\x18\x60\x3c\xbd\x7d\xfa\xe4\xc6\xc9\x37\xf9\x72\xb9\x7a\xd9\x68\x2e\xc5\x3b\xef\x96\x98\xec\x91\x77\x7e\x2b\xde\xb9\x77\x7c\x80\x20\x24\x6e\x31\xd6\xb9\xc7\x02\xe8\xde\x95\x75\xfe\xe5\xec\x50\x2e\x89\x05\xfc\xd0\xc1\xaa\x68\x3a\x00\x77\x04\xba\x6a\x12\x44\xc3\x78\x5a\xb2\x38\x60\xb9\x5c\x35\x24\xa5\x7c\x38\x2c\x75\xd8\xa9\xc5\xe5\x1a\xad\xb3\x0a\x01\xf7\xc8\xa8\x4c\x46\xc5\x89\x73\x29\x46\xf5\xf7\xce\xbc\xf0\x3f\x8a\x51\xad\xee\x6d\xf7\xd0\xcb\xb5\x97\x6b\x2f\xea\x88\xd1\x06\xda\xc7\xd9\x38\x1e\xa2\x86\x8f\x5b\x41\x68\xef\xdb\x72\xab\xcd\xe1\x90\xfa\x0f\xea\x0b\xa2\x00\x17\xe0\xab\x97\xd4\xa6\x7f\x7c\xd1\x6a\x0d\xfc\x1f\x9c\xc4\x90\x3b\x2c\x1b\x63\x94\xe0\x54\xe1\x8b\x5a\x47\x48\x39\xd6\x63\xf2\x6c\xe1\x7d\x2b\x5e\xc0\x16\xe2\x1f\x0c\x07\x7d\x35\x7a\x9b\x07\xd0\x14\x9e\x7b\x61\xc7\x11\x46\xd3\x38\xc1\x54\x78\x7c\xf1\x02\xfa\xe6\x1b\x45\xbe\xde\x5f\xbc\x28\xb8\xc0\x61\x3e\x97\x59\xe0\x6b\x77\x8b\x72\xfe\xb8\xc0\xbf\xd9\x29\x0e\x45\x71\x3c\x2b\x26\x86\x7c\xe0\xe4\xe8\x5d\xd9\x82\xd8\xfd\x6b\x42\x16\xc9\xa3\x39\xd1\xd4\x52\x44\x77\xb7\x70\xb3\x8f\x44\xf7\xad\x88\xee\xff\x28\xcc\x2f\x9f\xe4\x14\x1e\xf8\x17\x0a\xbf\x85\x0f\xce\xea\xf9\xd6\x12\x80\x4b\xa5\x7c\x11\xb8\x8c\xbe\x7e\x35\x5f\xdd\x6a\x8b\x71\xf7\x78\x71\x5c\x81\xd5\x55\xf4\x91\xc0\xd7\xeb\x85\x56\xa4\x00\xd0\x2c\x88\x32\x97\xe3\x70\x82\x51\xe9\x87\x92\xf4\xb5\x96\x31\xb8\xc1\xe3\xd0\x8a\xb9\x2d\x4c\x38\x2d\x45\x66\x28\xb6\x24\xa4\xab\x28\x4d\xc7\x6e\x88\xc7\x5b\x64\xf7\x52\x28\x68\x29\x5e\xf2\xf7\x76\xdc\x72\xe4\xe8\xa2\x49\xb2\x1e\x96\xaf\xc8\x4c\x48\xd0\xda\x5f\x9f\xe7\xe3\x61\x93\x84\x17\x8b\x89\x6d\xc5\xbc\x16\x5f\x8e\x77\x37\xeb\x32\xd6\x33\x79\x52\x3e\xda\x89\xc0\x5d\x0e\xa2\x87\x41\x9a\x92\x85\xfc\x82\xa0\x36\x44\xef\xf0\x35\xda\xc2\x49\x78\x41\x73\x42\xee\xf0\x41\x69\xe4\xc7\x9c\x3e\x7c\xf3\x6e\x6b\xa7\x21\x5b\x13\xcf\x05\x13\x8f\xf7\xe2\x68\x14\x9e\xcf\x59\x26\xca\x18\xb2\x42\xa6\x79\xf9\x25\x93\x78\x86\x93\xec\x1a\xfd\x49\x8f\xc5\xe0\x4d\x0a\xcc\xf7\x64\x4c\x73\x1c\xa7\xe4\x21\x8c\x58\xba\x80\x2c\x16\xbe\x34\x55\xb4\x85\x47\xc1\x7c\x92\x75\x51\x0b\x95\xea\x8d\x75\x48\xa4\x5c\xf6\xc1\xf7\x24\x34\xc7\x09\x4f\x64\x2e\xc1\x91\xf1\x5f\x84\x66\x98\xb1\xe4\x99\x29\x80\x92\x87\x7a\xe5\x43\x16\xa3\x19\x4e\x46\x71\x32\x55\x80\x6b\x90\x95\xf4\x8f\x83\xd1\x79\xd7\x37\xca\x88\x5e\x7c\x1d\x43\xcc\x99\x7a\x63\x7d\xb5\xd9\x30\x42\x70\xd3\xae\x50\xd4\x8d\x4f\x12\x21\xad\xf1\x9b\x72\x5e\x42\xd2\xbc\x04\xf2\x64\x56\x86\x92\xb4\xf8\x7a\x5b\x9c\x45\xf4\x00\xf8\xdc\x0d\xe9\xaa\x9a\x31\x94\x8c\xdf\xc0\x45\x37\xdc\xdf\x6c\x14\x27\x70\x8a\x91\x8d\xde\x43\x62\xd0\x2f\xc3\x91\x95\x34\x9e\x52\x3b\x3f\x3d\x6a\x66\x58\xcb\x54\xfc\x53\x4e\xd6\x3a\x4d\x3f\x79\x67\x30\x15\x7d\x1a\x6b\xb5\x9a\x09\x38\x27\x7b\xfd\x60\x74\xee\x36\xbc\x20\x13\xb1\x21\x7e\x72\xc2\x23\xc5\x7d\xc1\x30\xec\xf5\x0e\xd7\x15\xd4\x83\xae\x28\x0b\xba\x4d\xbe\xd9\x19\x83\x0d\xd4\xc2\x1f\xaa\x05\x2b\xa7\xc1\x24\x43\x9b\xf0\xcf\xf2\x89\x68\xb9\x1b\x8d\xe2\xd7\x7e\x17\xb2\xa3\x89\xd4\x87\xa3\x2a\x8b\x4a\x52\xe2\x9d\xa9\x00\x7e\xde\x49\x65\xc5\xd5\x79\x35\x6a\x2e\x95\xdb\x45\x9f\x7a\xa7\x01\x61\x98\x79\x92\xc2\x32\x2f\x7b\xf0\xdd\x67\xb4\x4a\xc8\x87\xf2\xa0\x8a\x98\x1d\xb7\x59\xa2\x3f\x41\x39\xc8\xa6\x74\xb0\x69\xba\x79\x4b\x9f\xe3\x0a\xf5\x04\x72\xf2\x5e\x34\xc4\x57\xae\x1a\xa7\xb5\x2b\xa6\x00\x72\x44\xeb\x5c\x10\xa2\x4b\xa0\x22\x84\x65\xf1\xc6\x9b\xbf\x5e\x62\xc3\x2b\xc9\x37\xde\x4a\x7c\xcb\xdb\x20\xb3\x52\x65\x4f\x2e\x23\x0c\xb9\xb5\xd0\xa2\xf2\xc5\x02\x23\x0b\xfd\x23\x13\xd4\x8d\x0e\xf2\xb8\x48\xaf\x39\x3e\x4e\xe3\x02\xd1\x49\x96\xe7\x98\x27\xcb\x06\x0a\x94\x69\x7c\x65\xaf\xcd\x39\x43\x2c\xa3\xb7\x4c\x0d\x6c\x7f\x5f\x9c\x8d\x01\xe0\x6b\x43\xec\x1c\x5d\xbb\xb8\xc8\x62\x24\x5f\xb1\x8e\x7b\x10\xd9\x13\x63\xec\x06\x1d\xaa\xd1\xec\x18\x58\x07\x16\x9a\x2d\x47\x9d\xda\x72\x28\xd3\xe7\x35\xe6\x40\xc0\xcf\xb5\x26\x60\xf4\xc4\x48\xab\x1f\x5d\x63\x5d\x64\xbc\xd1\xa2\x50\x50\xae\xce\xf2\xd1\x57\xdf\xb9\x03\x56\x29\x4d\xfc\x76\x70\xa4\x77\x07\x5c\xa7\x1c\x1e\xd7\xd6\xb8\x7d\xa6\x36\x30\x9f\xb9\x0d\x8c\x32\x9b\xaf\xd0\xe7\x9c\xd1\x23\x7f\xb2\xc6\xe9\x67\x30\x87\xb1\x3a\x72\xfa\xd9\x34\x8b\xe1\x7f\x37\xf6\x6b\x33\xe0\x14\xf9\x53\x98\x03\xd3\x4d\x43\xa3\xae\x29\x31\x98\xc4\x69\xed\xec\xf9\xf3\x7c\x93\x22\x05\xb8\x72\xf4\xe5\x7c\xc3\x11\xc4\x8c\xed\x65\xb2\x5e\x9e\x01\xa5\x7a\x8c\xb8\xd3\x86\x5e\x24\xd8\x4c\xee\x46\xbe\xe4\x26\x7e\x5f\xa2\x65\x98\xba\xd2\xed\x2f\x8e\x5e\xe3\x10\x0d\xee\x21\x88\x0d\x15\x11\x84\x64\x48\x85\x42\x9f\x98\xb0\x5c\xb5\x0a\xf2\xc8\xa6\x77\x01\x93\x2b\x9b\xca\x20\x3b\xe2\x28\xe9\x13\x60\x2a\xc8\x14\x54\xd9\xb0\xeb\x62\x31\x29\xb4\x40\x78\xba\xc9\xb3\x45\xa3\xd0\xdc\x81\x7a\xcc\x14\xba\x3c\x27\xec\xcd\x59\x65\xed\xef\xed\x43\xbf\x44\x5a\xf7\xc5\xc9\xd1\x1f\x56\x77\xe4\x4d\xaf\xed\xcb\x7a\xfd\x4f\xd0\x2e\x1d\x83\x71\x66\x8f\x1b\xef\x52\x25\x92\xfa\x32\x4f\x8f\x24\xf0\x38\xc2\xf3\x34\xe8\x4f\x30\x0b\x07\xa6\xa0\x73\x8c\xd4\x54\x8b\x14\x8a\xf9\xe6\x2d\xd2\x33\xac\x29\xdb\xc2\x11\x64\x53\x46\xcc\xd0\x96\xd9\x18\xdb\x9a\x24\x51\x1e\x62\xac\x84\x29\x0a\x10\x4d\xc0\x8c\x2e\x70\x92\x42\xd4\xb2\x71\x90\xa1\x08\x9f\x4f\xf0\x20\xc3\x43\xc2\x86\x07\x2c\xa5\x6a\xc6\x14\x3e\x59\x8c\x26\x61\x96\x4d\xf0\x0b\x1a\xe0\xb2\xaa\x03\xc5\x49\x12\x27\x68\x18\xe3\x34\x5a\xc9\x50\x30\x1a\xe1\x01\xad\x4b\x91\x5a\x49\x51\x8a\x07\xf3\x24\xcc\xae\x2b\xa2\x62\x7f\x9e\xa1\x30\x83\x4a\xbc\x46\x98\xa5\x22\xa0\x42\x38\x09\x33\xe6\xc4\x4d\xf3\xba\x86\x84\x3f\x4f\x71\x44\xf7\x83\xd4\xa5\x28\xa3\x03\xf2\x9e\x76\x4e\xa8\xcb\x8c\xb7\xea\xfc\xdd\x36\x69\x5b\xfe\x21\xe5\x9d\x6a\x06\xed\x3d\x60\x48\xeb\x6d\x38\x35\x5c\xe4\x9d\x16\x42\x76\x42\x23\xbb\x17\xf6\x9e\xd3\x7e\x13\xed\x92\x5f\x8e\xc4\x71\xef\x4e\x6b\x67\x15\x54\x7a\x77\xda\x3c\x63\xc1\x02\xd0\x57\xf2\xc8\xae\x02\xea\x9d\xb2\x23\x89\xdc\xbb\xd3\x3a\xad\x54\xd3\x2b\x35\xf3\x2b\x35\x68\xa5\xba\x5e\xa9\x96\x5f\xa9\x49\x2b\x35\xf4\x4a\x75\x51\x49\xaf\xe3\xca\x8e\x64\x0d\x19\xf7\x32\xf4\x0d\x5a\x4f\x0c\x5a\xcf\x3d\x68\x36\x3e\xca\x70\xb1\x3e\xd1\x0b\x93\xd1\x88\xa7\x1d\xa4\x48\xd3\x20\xab\xb5\x1a\xf9\xe2\xea\xaf\x3d\x11\x4d\x1d\x72\xdd\x09\xb9\x51\x08\x72\xcd\x3b\xf0\x0a\x0c\x03\x72\xb3\x10\xe4\xba\x6f\x76\x2a\x0a\x0c\x03\x72\xcd\x80\xbc\x78\x22\x7b\x41\x92\x5c\xa3\xbe\x99\x4e\x95\x4e\x55\x9f\xc6\xbf\xb0\x35\x19\x19\x9d\x7c\xc2\x7a\xd2\xeb\x34\xc3\x53\x34\x8a\xe7\x09\xca\xc2\xa9\x39\xf7\x4b\x06\xe5\x8d\xf0\x55\x76\x4c\x56\x9f\x3f\x7e\xac\x23\xe2\xed\x7e\x3c\x0c\x47\xd7\x94\x13\x52\x3a\x2c\x80\xc5\xba\x1f\x8b\xde\x29\x75\x1c\xf8\xed\x14\x52\x5e\x42\xb4\x15\x2b\x53\x9c\x2b\x49\xee\x2f\x28\xc5\xd9\x7c\xa6\x7f\xc8\xf1\xe8\x58\x7c\xd8\xdf\xfb\x85\xba\x76\xe4\x9d\xf0\xf7\x7e\xf9\x54\x43\x1b\x68\xef\x17\x3b\x35\x9a\x52\xa4\x4e\x8b\xd4\x9d\xd1\x8c\xd5\x25\x0d\x53\x99\xce\xfb\x17\x98\x88\x0a\xbe\xa3\x7f\x8d\x06\x3f\x86\xb6\x69\xf4\xe3\xaf\x88\x3e\xf9\xa2\x1f\xab\xc5\x59\x98\x63\x51\x5e\x5e\x87\xba\xc3\x1c\x8b\x66\x1b\xa2\xd9\xba\xd6\x6c\x7d\x51\xb3\x75\xbd\xd9\xfa\x72\xcd\x42\x18\x9d\xb0\xc6\x97\x20\x01\x12\x36\xf4\x15\xe8\xab\xda\x84\xaa\x0d\xbe\x98\xa1\x6a\x4d\x5f\xa6\x9e\x19\x61\x64\x9d\xc7\x5a\x11\x50\x6b\x8d\x9e\xeb\xcd\xd8\xfe\xf4\x63\x9d\x7e\xac\x3b\x3f\x36\xe8\xc7\x86\xf3\x63\x93\x7e\x6c\x3a\x3f\xb6\xf2\xda\x6c\xe7\xb5\xd9\xc9\x6b\x73\x4d\xb4\x99\xa3\x91\x2a\xc4\x79\xd0\xf2\xdc\x07\x15\xe3\x40\xc8\x56\x52\xa8\x7e\x44\xf7\x92\xdc\xd5\xab\xbc\x56\xa4\x8f\x42\x9c\x59\x2f\xe2\xee\x9d\x7f\x7b\x87\xc1\x95\x5e\x66\xc0\x85\xf4\xd2\xc7\x34\xd4\xd0\x6f\x40\x84\xa8\xf4\x1b\x99\x7b\xbe\x4a\xe0\x59\xec\xbd\xaf\xcc\x8a\x75\x5a\xb1\xc1\x2a\xae\x19\x15\xdb\xde\x8a\x0d\x5a\xb1\xc5\x2a\xd6\x8d\x8a\x6b\xde\x8a\x4d\x5a\xb1\x73\x26\x50\xd3\x2a\xd6\x65\xc5\x3b\xed\x62\x79\x51\xea\x29\x22\x3c\x76\xfc\x31\x4b\xc9\xce\x82\xc7\xc3\xe3\x6d\xa2\xc7\x73\x38\x8c\xc1\x09\x38\xae\xf8\xf1\x4e\x7c\x9d\x4e\x78\x48\xc9\xd1\x2b\xbc\xe9\x8e\xf3\xbd\xe8\x54\xea\x17\x76\x3c\xf2\xe6\x56\x7e\x0c\x2f\xe8\x97\x4e\x6b\xb5\xd9\x30\xd5\x72\x62\x99\x08\x82\x2d\x15\x74\x85\xd2\xd6\x87\xf6\x45\x11\x41\x0d\x83\x9f\xe3\xe0\x02\xa3\x78\x32\xf4\xb2\xda\x25\xe4\x87\xde\x27\x3a\xb9\x3d\x33\xde\xa1\xd6\x62\x2f\x98\x0c\xe6\x13\xb2\xc2\x22\x7c\xe9\x6d\xb6\xc7\x12\xc1\xf4\x68\x22\x98\xda\x55\x6b\xd8\x84\xff\x43\xcf\xb9\x84\x66\xe6\x6b\xe9\xb1\xbc\x30\x3d\x9a\x17\xa6\x76\xc5\x6a\x34\x21\xa6\x7c\x8f\x0b\xa8\xb5\x32\x7a\x8d\x4a\xbd\x4f\xca\xf3\x7f\xa0\x3a\xea\xa2\x5a\xd9\x86\xd8\x60\x10\x1b\x14\x22\x03\xd8\x62\x10\xeb\x06\xc4\x7a\x01\x88\x4d\x06\xb1\x69\x75\xab\x44\xdb\xd1\x20\x36\x0a\x40\x6c\x31\x88\x2d\x67\xaf\x9b\x06\xc4\x66\x01\x88\x6d\x06\xb1\xed\xec\x75\xcb\x80\xd8\x2a\x00\xb1\xc3\x20\x76\x9c\xbd\x6e\x1b\x10\xdb\x05\x20\xae\x31\x88\x6b\xce\x5e\x77\x0c\x88\x9d\x85\x10\xa5\xd8\x4f\x81\x6a\xd5\xd7\xcc\xea\xa6\x77\x8c\xa0\x69\xb2\xfb\x9c\xbf\xb8\xc3\x22\x22\xa5\xce\xaf\x80\x57\x87\xa4\x6b\x3d\x47\x12\x0e\x9e\x2e\x3f\x99\x0f\x32\x34\x0e\xcf\xc7\x28\x88\x86\x68\x12\x5f\xa2\x20\x39\x9f\x43\xf8\x17\x70\x73\xfe\xef\x79\x90\x58\x89\x7b\xa0\x81\x00\x6d\x90\x56\xb8\x14\xe7\x50\x1e\x9c\xf7\x69\x11\xba\x4b\x38\x8f\x4f\xbc\xcf\x1a\x06\x09\x4e\xe7\x93\x0c\xc5\xa3\xbc\xe6\xc7\x74\x0b\x28\x9d\x07\xe8\x27\x74\x1e\x50\xd7\x95\xfa\x5a\x19\x3d\x47\xf4\x55\x9f\xbd\x6a\xc3\xab\x3e\xbc\x72\x21\x39\xa1\x80\x94\xae\xd0\x23\xe1\x4f\xe8\xfc\x0a\x66\xb8\x0c\x04\xc1\x0b\x08\xb1\x53\x29\xe0\x4a\x04\x43\x3a\xf4\xdb\xc1\x11\x82\x70\x92\xea\xc7\xb7\x94\xc3\x9d\x8f\xd1\xef\xe8\x7c\x52\x94\xc9\xb9\x95\x2a\xbf\x31\x16\xf7\x96\xb2\xb8\x52\xe9\xad\xdc\xbe\xc9\x4e\xf6\x56\x11\x0b\xca\xac\x40\x47\x2f\xd0\x91\x05\x4c\x7a\xfe\x8d\x71\xc3\xb7\x94\x1b\x96\x68\x33\x72\xbf\x7d\xcb\xf9\x1f\xec\xb7\xcf\x11\x69\xcd\x86\xd1\x60\x30\x1a\x1c\x46\x5d\x47\xa0\x6e\x61\x58\xd3\x0b\xd4\xf2\x30\x6c\x32\xe8\x4d\x0e\xbd\xa1\x63\xd8\x30\x30\xac\x3b\x30\x6c\x31\x18\x2d\x0e\xa3\xa9\x23\xd0\xb4\x30\x6c\xe8\x05\x1a\x79\x18\xb6\x19\xf4\x36\x87\xde\xd2\x31\x6c\x19\x18\x36\x1d\x18\x76\x18\x8c\x0e\x87\xd1\xd6\x11\x68\x5b\x18\xb6\xf4\x02\xad\x3c\x0c\xd7\x18\xf4\xb5\x33\x8d\x44\x04\x86\x1d\x03\xc3\xb6\x86\x61\xa1\xc4\x1f\x29\x4f\x3a\x21\x74\xad\x05\xd2\x4e\x2c\xba\xee\xa2\xb0\x32\x7c\x95\xa9\xf7\x4e\xaa\x26\x95\x87\x52\xd0\xd2\x38\xd0\xdb\x22\xfb\xfe\x6a\x36\x09\x08\x36\x57\x19\xf2\x82\x63\x71\x66\x4a\xb2\x65\x17\x44\x71\x71\x95\xa7\xd4\xd5\x93\x77\xa8\x25\xcb\x79\x77\x50\x6a\xc1\xc2\xc6\xc8\x15\xfd\x6e\xa4\xdb\x6e\x55\xe4\xa5\x48\xb7\xdd\xa9\xb0\xbb\x92\x6e\xa7\x7e\x73\x56\x59\xfb\x7b\x47\x22\x7c\xbc\xaf\x7a\xbc\xaf\x7a\xb0\xfb\x2a\x63\x89\xcb\xfb\x1c\xf3\x26\xe7\xef\x75\x87\x73\x5f\x59\xe1\xde\x89\xa3\xf9\x3b\xfd\x68\xfe\xee\xb6\x47\xf3\x77\xfa\xd1\xfc\x5d\xde\xd1\x7c\x91\x82\xf9\xf1\xa6\xea\xf1\xa6\xea\xf1\xa6\x4a\xfb\xf2\x78\x53\xf5\x78\x53\xf5\x78\x53\x25\x9b\x7d\xbc\xa9\x32\x3f\x3e\xde\x54\x79\x1e\x1f\x6f\xaa\x1e\x6f\xaa\x1e\x6f\xaa\xe0\xef\xf1\xa6\xaa\x98\x12\xf7\xf1\xa6\xea\xf1\xa6\xea\xf1\xa6\x4a\xf9\x7b\xbc\xa9\x7a\xbc\xa9\x7a\xbc\xa9\x7a\xbc\xa9\xfa\x9f\x7c\x53\x75\x6f\x77\x54\xb7\xbb\x9d\x2a\x72\x2f\x55\xe0\x46\xea\xa1\xee\xa2\xfe\xde\xf9\x50\x1e\xef\xa2\xfe\xf9\x77\x51\xea\xdd\x51\xaf\xb5\xd0\xd1\x49\xbd\x39\xea\xb5\x94\x6b\x23\x78\x78\xf8\x3b\x23\xea\xa5\x29\x6e\x8d\xdc\x41\x05\xb8\x87\x76\xde\xb5\x12\xb8\x71\xaa\x1e\xc5\x4a\xcc\x74\x5b\x5f\x11\x85\x19\x4a\xfb\xf1\x95\x0d\xe7\x58\xa0\x73\xac\x5e\xd3\xf1\x3f\x97\x34\xd9\x68\x77\xfc\x87\x72\x76\xe8\x0e\x17\xab\x71\xdf\xe1\x6b\x97\x1e\x57\x6f\xb1\xc2\xfd\xc7\x17\x36\xcc\x06\x85\x0c\x01\x8f\x2a\x11\xa2\x7f\xa9\xe3\xe4\x51\x1d\xb2\x4a\x64\x6b\xe3\x63\x7f\xaa\x01\xb2\x23\xa1\x69\x9f\xad\xa0\x68\xae\xb3\x3f\xe9\x45\xe9\x33\x7a\x4e\xc7\xe7\x39\x6f\xb4\x8c\xfe\x05\xbd\xf2\xc4\x52\xb8\x0c\x66\x6e\x9c\x61\xdf\xb0\x35\x04\xca\x04\x1c\xbb\x1d\xe3\xc9\x6b\x32\xe3\x8b\xa7\xa7\xe7\x54\xf1\xb3\xac\x1a\x82\x68\x3e\xb3\x2c\xb3\x02\xd0\x9d\xd5\x72\x5c\x13\x02\x5a\x10\x2b\xff\x3a\x99\x1e\xb7\xca\x50\x6b\x59\x38\x39\x37\xda\x1d\x8f\x42\xa4\xe6\x55\x86\x38\x1b\x2d\xaa\x18\x51\xd6\x93\xa1\x18\x91\x83\x16\x1a\x5f\x3e\xcb\xe1\x5c\x98\x01\x1e\x94\x83\x7a\xf5\x2f\x2a\x9e\xc6\x7c\x88\xd5\x14\xd1\x65\x14\x51\x95\x5a\xe4\x58\x44\x21\x68\xd0\x69\xc2\x38\x46\x95\xda\x77\x8d\x84\x3d\x84\xeb\x24\xda\x1c\x82\xf5\x13\xab\x24\x54\xfd\xbd\xde\xd9\xaf\xa4\x6e\x89\xad\x29\x52\x85\xe1\x75\x26\xf3\x1a\x44\x66\x1e\x03\xe3\xf8\xf4\x11\xe2\xa0\x38\x6e\xb4\x24\xa9\x87\xd6\xd9\x9d\x8c\x85\x36\x57\x4c\x2c\xd3\xb0\xfb\x5e\xe5\xde\x5e\xeb\x3e\x84\xde\x5e\x6b\x69\x89\xd7\xde\x63\x0d\x71\xb7\xd7\x72\xc6\xb6\x80\x1b\x9a\x10\x0f\x6f\xb1\xc3\x6f\x25\xf1\x4c\xdb\xe5\xd9\x0b\x18\x84\x6f\x10\x15\x6f\x48\x9a\xd3\x03\xcd\x19\x7a\x7e\x32\xf1\xa4\x94\x08\x35\x87\xea\x2f\x1b\x2a\x58\x33\xd6\x1c\x41\x5d\x89\xfa\x65\xac\x62\x02\xaa\xab\x83\xd0\x23\xc6\x15\x12\x62\x48\x1b\xbc\x60\xfe\x1d\x06\x19\xcf\x9c\x0d\x5c\x18\xbe\x10\xbc\xc8\x2e\xfe\x33\x6c\xe6\x2f\x5e\x38\xf7\xf0\x25\xd8\x3d\x5a\x90\x00\xe9\x3b\x5a\x6d\x64\x88\xee\x67\xc5\x01\xa4\xe5\x57\x1d\xa3\xf9\xfc\x95\x47\x0a\xe5\x9f\x34\x7b\xad\x87\x3a\x66\xde\x2d\x5d\xdf\xb7\x3c\x5f\x3e\xd8\x29\xf0\xdb\x06\x71\x26\xac\x0a\xa7\x38\xb9\xc0\x4f\x9f\x94\x06\x65\xd4\xa8\xd5\x1b\xa8\x7f\x8d\x7a\xff\xdf\xff\x3b\x4c\xc2\x01\xda\xc7\x69\x14\x4e\xaa\x68\x73\x32\x41\x49\x78\x3e\xce\x52\xc4\xca\x0f\xab\x4f\x9f\x3e\x39\xc2\xc3\x30\xcd\x92\xb0\x3f\x07\xf8\x41\x34\x84\xa0\x3c\x61\x84\xd2\x78\x9e\x0c\x30\xbc\xe9\x87\x51\x90\x5c\x13\x76\x30\x4d\x2b\x2c\x4a\x43\x02\xff\xc6\xf3\x0c\x4d\x81\xa7\x0f\x80\xb3\x56\x50\x90\x60\x34\xc3\xc9\x34\xcc\x32\x3c\x44\xb3\x24\xbe\x08\x87\x78\x48\x83\x4e\x90\x75\x3a\x8a\x27\x93\xf8\x32\x8c\xce\xd1\x20\x8e\x86\x21\x5d\xc3\xa4\xd2\x14\x67\x5d\xb6\xe2\x5f\x20\x1d\xad\x14\x14\xc3\x14\x9f\x41\x3c\xc4\x68\x3a\x4f\x33\xb2\x51\x07\x61\x04\x40\x83\x7e\x7c\x41\x3e\xcd\xae\xa1\x8b\x28\x8a\xb3\x70\x80\x2b\x34\xae\xd0\x24\x4c\x41\xb3\xac\xb6\x17\x0d\x0d\x64\x86\x61\x3a\x98\x04\xe1\x14\x27\x55\x1f\x0e\x61\xa4\x0e\x04\xc7\x61\x96\xc4\xc3\xf9\x00\xdf\x3b\x1a\x88\x75\x6d\x18\x0f\xe6\x22\x0e\x06\xa9\xb1\x1a\x27\x2c\x46\xc6\x34\xc8\x70\x12\x06\x93\x54\x0e\x33\xcc\x0d\x54\x53\x50\x27\xf3\x7c\xb2\xbb\x77\x8c\x8e\x0f\x76\x4e\x7e\xdd\x3c\xda\x46\x7b\xc7\xe8\xf0\xe8\xe0\x97\xbd\xad\xed\x2d\xf4\xe6\xdf\xe8\x64\x77\x1b\xf5\x0e\x0e\xff\x7d\xb4\xf7\x76\xf7\x04\xed\x1e\xbc\xdf\xda\x3e\x3a\x46\x9b\x1f\xb6\x50\xef\xe0\xc3\xc9\xd1\xde\x9b\x8f\x27\x07\x47\xc7\xe8\xd9\xe6\x31\xda\x3b\x7e\x06\x1f\x36\x3f\xfc\x1b\x6d\xff\x76\x78\xb4\x7d\x7c\x8c\x0e\x8e\xd0\xde\xfe\xe1\xfb\xbd\xed\x2d\xf4\xeb\xe6\xd1\xd1\xe6\x87\x93\xbd\xed\xe3\x0a\xda\xfb\xd0\x7b\xff\x71\x6b\xef\xc3\xdb\x0a\x7a\xf3\xf1\x04\x7d\x38\x38\x41\xef\xf7\xf6\xf7\x4e\xb6\xb7\xd0\xc9\x41\x05\x1a\xb5\xab\xa1\x83\x1d\xb4\xbf\x7d\xd4\xdb\xdd\xfc\x70\xb2\xf9\x66\xef\xfd\xde\xc9\xbf\xa1\xbd\x9d\xbd\x93\x0f\xa4\xad\x9d\x83\x23\xb4\x89\x0e\x37\x8f\x4e\xf6\x7a\x1f\xdf\x6f\x1e\xa1\xc3\x8f\x47\x87\x07\xc7\xdb\x88\x74\x6b\x6b\xef\xb8\xf7\x7e\x73\x6f\x7f\x7b\xab\x8a\xf6\x3e\xa0\x0f\x07\x68\xfb\x97\xed\x0f\x27\xe8\x78\x77\xf3\xfd\x7b\x67\x2f\x09\xee\x5a\x1f\xdf\x6c\xa3\xf7\x7b\x9b\x6f\xde\x6f\xd3\x96\x3e\xfc\x1b\x6d\xed\x1d\x6d\xf7\x4e\x48\x77\xe4\xaf\xde\xde\xd6\xf6\x87\x93\xcd\xf7\x15\x74\x7c\xb8\xdd\xdb\x23\x3f\xb6\x7f\xdb\xde\x3f\x7c\xbf\x79\xf4\xef\x0a\x83\x79\xbc\xfd\xbf\x3f\x6e\x7f\x38\xd9\xdb\x7c\x8f\xb6\x36\xf7\x37\xdf\x6e\x1f\xa3\xd2\x82\x21\x39\x3c\x3a\xe8\x7d\x3c\xda\xde\x27\x38\x1f\xec\xa0\xe3\x8f\x6f\x8e\x4f\xf6\x4e\x3e\x9e\x6c\xa3\xb7\x07\x07\x5b\x30\xd0\xc7\xdb\x47\xbf\xec\xf5\xb6\x8f\x5f\xa1\xf7\x07\xc7\x30\x5a\x1f\x8f\xb7\x2b\x68\x6b\xf3\x64\x13\x1a\x3e\x3c\x3a\xd8\xd9\x3b\x39\x7e\x45\x7e\xbf\xf9\x78\xbc\x07\x83\xb6\xf7\xe1\x64\xfb\xe8\xe8\xe3\xe1\xc9\xde\xc1\x87\x32\xda\x3d\xf8\x75\xfb\x97\xed\x23\xd4\xdb\xfc\x78\xbc\xbd\x05\xa3\x7b\xf0\x01\xba\x7a\xb2\xbb\x7d\x70\xf4\x6f\x02\x94\x8c\x01\x0c\x7e\x05\xfd\xba\xbb\x7d\xb2\xbb\x7d\x44\x06\x14\x46\x6a\x93\x0c\xc1\xf1\xc9\xd1\x5e\xef\x44\x2d\x76\x70\x84\x4e\x0e\x8e\x4e\x94\x3e\xa2\x0f\xdb\x6f\xdf\xef\xbd\xdd\xfe\xd0\xdb\x26\x5f\x0f\x08\x94\x5f\xf7\x8e\xb7\xcb\x68\xf3\x68\xef\x98\x14\xd8\xa3\xcd\xfe\xba\xf9\x6f\x74\xf0\x11\xba\x4c\xe6\xe8\xe3\xf1\x36\xfd\xa9\x50\x6c\x05\x66\x12\xed\xed\xa0\xcd\xad\x5f\xf6\x08\xda\xac\xf0\xe1\xc1\xf1\xf1\x1e\xa3\x13\x18\xb2\xde\x2e\x1b\xee\xea\xd3\x27\x3f\xad\xea\x3a\xaf\xfd\x20\x1b\xdf\xaf\xde\xab\x58\xd4\x69\x1a\xf8\x58\x14\xa1\x8f\x85\xac\xb3\xe1\xc2\x2e\x88\xb2\x14\x65\x41\x9f\x4b\x2c\xa4\xca\xa7\x3f\x26\xce\x60\x9b\x52\x8e\xaa\x55\x10\xaa\x57\x10\x6a\x54\x10\x6a\x56\x10\x6a\x55\x10\x6a\x57\x10\xea\x54\x10\x5a\xab\x20\xb4\x5e\x41\xe8\x65\x05\xd5\x6b\x15\x54\xaf\x57\x50\xbd\x51\x41\xf5\x66\x05\xd5\x5b\x15\x54\x6f\x2b\x16\x96\x6b\xb4\x2e\xf9\x46\xe0\x91\xf2\x04\x46\xbd\x4d\xe1\x92\x7a\xd0\xd6\x4b\x06\xbf\xc1\x60\xd4\xa1\x0d\x09\xa7\xc9\xda\x6a\x31\x5c\x5e\x32\x18\xeb\x0a\x9e\x6b\x0c\x56\x87\xe1\x52\xa7\x30\xeb\x6a\xac\xe5\x3a\xab\xcb\x71\xa9\x51\x18\x80\x07\xc7\xb3\x49\x61\x11\xf8\x75\xb5\xdf\x2a\x9c\x16\xab\xdb\x66\xb8\xaf\x31\x18\x0d\x05\xcf\x3a\x83\xb5\xce\x70\x61\xfd\xae\x37\xcf\xca\xaf\xd4\xb9\x48\x16\xcc\x05\xc7\x63\x4d\x19\xab\x06\x83\xc9\x71\xee\xe8\xe3\x01\x7d\x6b\x1a\x7d\xef\xb0\x3a\x4d\x09\x0b\xea\xb6\x25\xce\x1c\x06\x1f\x0f\x68\xab\x6e\xf4\x1d\x0a\xb5\x95\x0e\xae\x31\x04\x3b\x72\x70\x05\x90\x86\x32\xd0\x14\x59\x09\x68\x9d\xd5\x51\x06\x0b\x26\xa6\x2d\x07\x57\xc0\x68\x2a\x03\x4d\x91\x55\x10\x6a\xb0\x91\xad\x29\xc0\xf8\x68\xac\x89\xd9\x13\x14\x8a\xd8\xe8\x50\x64\xf5\xd9\x48\x17\xad\x0c\x8a\x22\x1b\x2b\x40\x4f\x6d\x89\xd3\x56\x53\x19\xcf\x8e\xfc\xa6\xd1\xf4\x5a\x05\x3e\xc1\x50\x71\x7a\x7d\x29\x69\x8f\xd3\x54\xbd\xad\x0c\xeb\x1a\x2b\xab\xcd\x47\x5d\x12\x81\x98\x8b\x97\xac\x20\x27\x9e\x75\xa5\x0c\x47\x7c\x0d\x7e\xab\x67\x29\xb1\x96\x5b\xb2\x2a\x6f\x5f\xac\x79\x75\x4d\xac\x6b\x20\x25\x28\xbe\x3e\xdb\x92\xf6\x45\x3f\x1b\x12\x05\x31\x4e\x8c\x64\x28\x5c\x64\x4c\xc9\xa2\x05\xc2\x10\xd3\x06\xbf\x2d\x11\x80\x7e\xae\xc9\x85\x08\x0d\xb6\x18\x22\x1d\x03\xe9\xa6\x3e\xf8\xa2\xd3\x75\x09\x47\x8c\x9d\x58\xd0\xf0\x5d\x83\x23\x18\x48\x5d\x19\xa4\x8e\x6c\x57\x2c\x3c\xb6\x80\xeb\x4d\xc7\x7c\x88\x0e\x18\x88\x73\x40\x62\xc1\x35\x94\x7f\xdb\x62\x15\xeb\x03\xd4\x76\x94\x6b\xe9\x33\x23\x66\x52\x76\x0a\xd5\xeb\xe8\x4c\xcb\x92\xfd\x69\x4c\x56\x88\x63\x3e\x90\x08\xd5\x5c\xab\xa0\xda\x55\x7b\x73\xbd\xb1\xf6\xf2\xe5\x4b\xf2\xbb\xb3\xbd\xf5\x72\xfb\xcd\x66\x9d\xfc\x5e\xdf\xa9\xbf\x79\xd3\xdb\xea\x91\xdf\x9b\x2f\xdb\xcd\x9d\xad\xd6\xb6\x3e\xdf\xe3\xc4\xdb\x40\xbb\xb6\xd9\x58\x7f\xb3\xdd\x81\x06\x7a\xad\xad\xad\x7a\xa3\x05\x0d\x6c\xad\xd5\x9a\xdb\x3b\x4d\xf2\x7b\x6d\xb3\xb3\xb5\xd6\xd9\x86\x86\x39\x42\x67\x4e\x7d\xc0\xd1\xde\xe1\xf6\xfe\x56\xbd\x53\x83\xf0\xfb\x0b\x74\x48\xa2\xac\xd4\x22\x29\xaf\xe8\xae\x7c\xdb\xbb\x22\xaa\x4c\x04\x24\x3c\x41\xb0\x3b\x6b\xad\x76\xa3\x59\x83\x11\xdc\xde\xe9\x6d\x6d\xbe\x59\x87\x0e\xbe\x5c\x7f\xb3\xb9\xd5\xdb\xd9\x26\xbf\xeb\xb5\x66\xa3\xdd\x5a\x83\xc1\xe9\x35\xb7\x1a\xdb\xf5\x9d\xda\x99\x57\x35\x5e\x54\x29\xef\x54\xec\x16\xf6\x52\xaa\xe7\xdc\xd4\x2c\x36\xc7\xa7\x58\x80\xee\x55\x9a\x45\x7a\xae\x6f\xf6\x3f\x29\xa5\xf9\xe5\xc1\x27\xdb\x90\x09\xe5\xdd\xa9\x28\xf5\xd0\x06\x2a\xd9\x05\x10\x35\x00\x55\x1a\x93\x86\x0f\xca\xcb\xe5\x8c\x4a\x2d\x80\xcc\xae\xd4\x00\x68\x5b\x97\xda\xe0\x72\x54\x63\x68\x91\xad\xf3\x2e\x12\xf7\x0f\x84\x14\xbd\x57\x8e\xc0\x00\x3e\x8d\x27\xfe\x02\x09\x14\x48\xbc\x05\x40\xfc\xfc\xf4\x87\x1f\x02\xc8\x44\x9f\xfe\xf0\x43\x80\x6d\xfa\x53\xea\x87\x00\x9b\xc6\xa7\x34\x71\x47\xb4\x5e\x5d\x25\xab\xec\xcb\xff\xcf\xde\xdb\x6f\x49\x6d\x63\x8b\xe2\x7f\x87\xa7\xd0\xcc\x6f\x0d\x54\xd3\x45\xb7\x25\x7f\xc9\x40\xe7\x77\x09\x81\xd3\xb9\x81\xc0\x02\xe6\x86\xb3\x58\x90\x91\x6d\xb9\xcb\xa1\xba\xaa\x4f\x95\x9b\xae\x4e\x42\xd6\x7d\x8d\xfb\x7a\xf7\x49\xee\xd2\x96\x6c\xcb\xb6\x24\x57\x35\x4d\xce\x64\x86\x9e\x35\xa4\xaa\x24\xed\xbd\xb5\xbf\xb4\xf5\xb5\x25\x26\xcd\x1f\xd8\xaa\x14\xd1\xb1\x61\x93\x96\xcd\xa7\x28\x9d\x4f\x51\x36\x9f\xa2\x7c\x3e\x45\x7c\x6e\x40\xc4\x56\x53\x94\xae\xa6\x28\x5b\x4d\x51\xbe\x9a\x22\xbe\xea\x23\x63\x82\x14\x26\x08\x3e\x1e\x5e\x19\x49\x57\x90\x74\x1c\x0a\x71\xbf\x30\x13\x85\x99\x2c\x24\xfd\xc2\x5c\x14\xe6\xb2\xd0\xef\x17\xc2\x84\x81\xcb\xc2\xa0\x5f\xd8\x3c\x53\xcd\xba\xef\x52\xd7\x5d\xea\xef\x0a\x1a\x8f\x12\xc2\x7f\xf7\x8f\x10\x36\xda\x76\x25\xcc\x87\xcd\xd1\x7e\x6b\x53\xfb\xbf\xcc\xdf\x94\x6f\xdf\xee\xfd\x66\xba\xc4\x00\xb7\x76\xee\xe3\x68\xef\xd7\x1b\x5f\x75\x5d\xa3\xc0\x81\x0a\x3c\x49\xe7\xd3\x6c\x3e\xcd\xe7\x7b\x68\x1f\xcd\xe6\xe6\xbb\x37\x1f\x51\xb3\x20\x57\xde\xf7\x89\x5c\x6a\x33\x40\x23\x7d\x68\x03\xce\x0f\xa0\x05\xd4\x0a\xcd\xef\x43\x1b\x88\x6a\x00\x2d\x0a\xac\xd0\x82\x3e\xb4\x81\x6c\x35\x68\xbf\x1e\x1e\x2a\x88\xd4\xb3\x42\x0c\xfb\x10\x07\x0a\x81\xcc\x69\xd2\x85\x10\x2b\xa3\xb8\x44\x09\x5a\x2d\xab\xf9\xa4\x9a\xae\x85\x58\x4d\x97\x36\x40\x07\xaa\x7d\x3e\x37\x8b\x1c\x2c\x62\x60\x52\xe2\x0f\xf4\x36\x37\x95\x80\xba\x03\x5e\x61\x93\xd8\x78\x0d\x08\xec\x25\x35\xb5\x06\x33\x1b\xec\x24\x36\xa4\xb2\x15\xda\xd7\xb4\x75\x75\x75\x6d\x0d\x27\xe9\x6a\x9a\xad\xa6\xf9\x0a\x38\xbe\xfa\x34\x6d\x0d\xfa\xd0\x3e\x55\x5b\xbb\xd0\x3e\x49\x5b\x49\x1f\xda\x27\x6b\x2b\xee\x43\xbc\x66\x6d\x5d\xc1\xae\xb5\x43\x5d\x57\x16\x75\x05\x8f\xba\x32\xa9\x2b\x38\x62\x53\x09\xb8\x68\xa9\xae\x2b\xab\xba\xc2\x00\x60\x6a\x0d\x43\xc3\xf0\x84\x46\xdf\x95\x7f\xa7\x3f\xc7\x00\x31\x24\x9c\xfa\xed\x45\x98\xe2\x9f\x23\x34\x39\x96\x47\x73\x33\xe1\x99\x73\x43\x4f\x8f\xd5\x11\xde\x63\x79\xfc\x36\x17\xf5\x4c\x1c\x39\x56\xc7\x74\x8f\xe5\x41\x5a\x2e\xea\x31\x63\x3d\x5f\xd5\x83\xc3\xb2\x30\x22\xa4\xc6\x7a\x81\xaa\x07\x07\x93\x53\x51\x2f\x33\xd6\x83\x03\xcc\x1d\xb6\xf4\xc3\xda\xc7\xea\x69\x8d\x4f\x38\x9e\x95\xb3\x8a\x35\xc1\x90\xf8\x62\x18\xf8\xc7\x9f\x61\xac\x6b\x2e\xbe\x29\xab\xf5\xab\x65\x05\x1e\x4f\xc2\x5c\x7c\xcb\x2a\x26\x4f\x6d\xdd\x46\xd4\x00\x1d\xda\x3c\xe1\x45\x35\x78\xb4\x11\xea\x0f\x3a\xf3\x20\xcf\x87\xaf\x10\x23\xf5\xde\xa2\x3c\xcc\xd4\x82\x14\xd1\x64\xf8\x16\xfd\x76\x24\x1f\x16\x6e\xcf\x48\x34\x35\xfe\x86\x7c\xd2\xd7\xd6\x16\xd2\x64\x32\x69\xab\xee\x23\xe1\x1f\x04\xc8\x64\x4f\x80\x0a\x84\xdd\xe2\xc0\x12\x40\xd7\x4d\x25\x3b\xda\xe0\x59\xfb\x71\xfb\xe0\x79\x00\x4c\x05\xce\x3d\x60\x63\x81\xb3\xa9\xa3\xfa\x3b\x1d\xed\x7b\x98\xf5\x1b\x3b\x70\x38\xc6\xf0\x6c\xc7\xe1\x21\xcc\x04\x11\xbc\xee\x22\x2f\x64\x19\x0f\x4e\x9d\xc9\x99\xd7\xf0\x35\x17\xb7\x5a\x82\x75\xeb\x31\xba\x41\x71\x8e\xd1\x11\xd2\xc3\xf7\x4f\x9b\xbf\x85\x5b\x4d\xdf\xcc\x33\xb2\x63\x98\x8a\x1d\x1b\x2e\x93\x20\xd7\x1c\xec\xb8\xb9\xae\x77\xdc\x99\x5e\x1d\xef\x3c\xaf\x92\x1a\x72\xdc\x99\x53\x1d\x5b\x27\x53\xe3\x47\xe1\x5e\xc8\x9d\x70\x29\x5c\xf5\x82\x45\x0e\xcc\xee\x56\x55\x3b\xe6\x3d\x01\x75\xdc\x54\x36\x5f\x2e\xdc\x0e\x0a\x8e\x12\x88\x5a\xed\xea\x02\x7c\xb5\x1f\x83\x90\xc5\x3f\x0d\x94\x44\xb6\x1b\xea\x9a\x22\x13\x4a\x3b\xe7\xa2\xe0\xe3\x47\xb9\xfb\x8f\xf4\x13\x71\x05\x9e\x6c\xa6\xe8\x72\x8a\x7e\x31\x3d\xf3\x31\x99\x6c\xe0\x66\xe7\x25\xfc\xfb\x4b\xfb\x5a\xfb\xc7\x01\x1c\xe2\x86\x33\xd9\xec\xdd\x9c\x5c\xee\xc9\xeb\xe4\xbf\x8b\x2f\xbf\xec\xed\xed\xdd\xb3\x41\xf3\x47\xa1\x09\x40\xbf\x0b\x88\x2d\x69\x16\x58\xc1\x38\xac\x9b\x00\x01\x68\xbb\xdc\xbb\x39\xf9\x1d\x88\xb3\x43\x0c\xb7\xe1\x99\x60\xda\x6f\x2d\x28\x0b\x2c\x08\x25\x36\xd3\x85\x11\xd2\xe6\xfe\xfd\x05\x50\xb5\xf9\xfa\xeb\xaf\x27\x3e\xb9\xb3\xd0\x89\x92\x1f\x9c\xa7\x61\xea\xc3\x30\xf2\x1d\xb8\xed\x0e\xc3\x58\x5f\xfb\x51\xe7\x5b\xe0\xcc\x53\xfd\xb9\x5a\x4a\xcf\x34\x04\x63\x79\x9f\xc7\x52\xfb\xaa\x0f\xf3\x28\xcb\x68\x4f\xb2\xd4\x0b\x78\x93\x5b\x8a\xc4\x5b\x86\x53\x38\xf6\x56\x17\x35\xb5\xa6\xe3\x36\xc3\xc5\xc1\xde\x51\x9b\xba\xc2\x76\x47\x95\x6a\xe1\x1c\x3f\x7d\xf0\xf0\x0f\x10\x8d\xa3\xf9\x7b\x7e\x09\x4d\xd7\x3c\x5b\xf1\xca\xf2\x76\x92\x45\xa0\xf0\xe4\xe0\x35\x0a\x54\x3e\x64\xd8\x88\xe6\xf8\x94\x65\xad\x78\xf4\x23\x56\x06\x09\x75\x2a\x0f\xa5\x74\xca\x32\x83\xa4\xbe\xfa\x28\xf7\x81\x2d\x47\xa3\xea\x9a\xe6\xd7\x89\x3e\xbe\x9d\xc6\xf1\x97\x23\x4e\xff\x0a\x57\x56\x3e\xf7\xd6\x7d\x2f\xb1\x9a\x86\xd8\x9a\x32\xed\xe5\xf1\x83\x3b\x78\x8b\x9d\x8c\xe1\x5b\xd5\xd7\xb9\x7f\x71\x04\xb7\x4f\xdb\x2d\x8c\x72\x51\x56\x13\x43\x02\xaa\xee\x96\x06\x2f\xb2\x9c\xa5\x34\x31\xe4\x66\xf2\x36\x09\x4d\x59\x9e\x15\xbc\xb3\xc7\x61\xaa\x98\xf9\x39\xe1\xb8\xf0\xba\x65\x9f\xbe\x05\x62\x8b\xd0\xcd\xc1\xf7\x70\x05\x7d\x00\x60\x9b\xb5\x67\xf3\x72\xb1\x28\x4a\xcd\x8b\xc5\x10\x30\x9a\x97\x8a\x61\xba\x6a\x5e\x28\x16\x45\xbc\x59\x26\x1e\x50\x6a\x5d\x27\xb6\xae\x09\x5b\x66\x0b\xb0\xee\x83\xe4\x0d\x53\x4b\x2e\x98\x1f\x65\xe0\xdf\x4d\x81\xd1\xbd\x7b\x5a\xff\xd5\x0b\x4a\x66\x40\xf5\x3d\x87\x1f\xdf\x94\xe8\x0e\xf2\xdf\xa2\x77\xea\x23\x6d\x3f\xe2\x40\xfb\x1c\xd9\xde\x8e\x54\x24\x4d\x16\x70\x39\x56\xce\x2d\x61\xfa\xe0\x63\x73\x9a\x1a\xf3\x4c\x08\x96\x96\x26\x4c\x00\x09\x01\x08\x93\x33\x99\x18\x2e\xc8\x72\xb4\x0f\x88\x6c\x0b\x8d\xe8\x3e\x22\x9e\x95\x6b\xb0\x6c\x36\x99\xa4\xe8\x26\xca\x64\x9c\x2b\x3e\xe6\x00\xd9\xdb\x84\x4c\xee\xc2\x8e\x2c\xf1\xa1\xfb\x28\x18\x43\x91\xa2\x77\x28\x43\xef\x50\x2e\x21\x47\x3c\x4f\x78\xca\x4c\x49\x87\x7a\x90\xa3\x1d\x88\x97\xb4\x8b\x4f\x99\xea\xc5\x1d\xe4\x6d\x62\x8f\x07\x81\x4f\x02\x3b\xae\xc3\xdb\x0d\x3a\xea\xed\xa1\xdb\x87\x5b\xf7\x45\xc0\xf7\xc3\x24\xf7\x39\xe9\xaf\xf2\x20\x8b\x48\x85\xbd\xe4\xa6\xe5\x3e\x74\x84\x32\xd3\x12\x1f\x02\x94\xf7\xef\x23\xdf\x53\xbd\x04\xf1\x1b\xdf\x16\x45\x47\xc8\x44\x07\xdb\xee\xb6\xd6\x56\x8b\x81\x6a\x11\xad\x5e\x6c\x63\xfd\x1b\xde\xa8\xb3\x10\x08\x0b\x86\x83\xcc\x27\xa8\xb3\x08\x08\x8b\x85\x99\xb9\x8e\xaf\x2f\x14\xe6\xe6\x3a\x81\xbe\x48\xc8\xfb\x75\xbe\x2c\xf0\xfd\xb3\x2e\xf0\x89\x58\xf8\xa0\x98\x2f\x97\x2b\x7d\xcd\xed\x10\x06\x6a\xf5\xf7\x49\x48\x20\x17\x42\x0b\x79\x64\x9d\x6e\xb0\x4c\xf7\x99\x56\xe8\x76\x5c\x07\x32\x2e\xd7\xfd\x19\x57\x83\xbe\x2c\x21\x0c\x16\x03\x44\xf8\xbc\xd3\xea\x01\x34\x70\x2d\x1c\x74\x03\xf2\xee\x9a\x81\x28\xfb\xb2\x5c\x70\xad\xcb\x05\x20\x8f\x2d\x56\x0a\xcc\x62\x69\x17\x09\x94\x68\xec\xd7\xa6\x44\x05\xfb\xb2\x00\xfd\x53\x27\xd8\x58\xcf\x18\x09\xa3\xcf\x9d\x1b\x43\x61\xf9\xf7\x59\x3e\x18\x2c\x0f\xe8\x73\x78\x12\x46\x9d\x59\xbc\x76\x0b\xbb\xbf\x2a\x40\x48\xb0\xdd\xba\x80\xa8\xd8\x81\x09\xdf\x25\xf0\x3f\x74\x6d\x20\xc3\x5e\x98\xf0\x9c\x8a\x29\xbf\x1f\xc5\x59\x1e\x7a\x31\x7c\xf6\x62\x2f\xcf\x31\x7c\x2e\x62\x8f\x87\x89\x6f\x5e\x33\x28\x8a\xcc\xf3\x52\x1f\x16\x17\x22\x1a\x52\x1c\x62\xf9\x39\x28\x12\x5a\x30\x00\x90\xf2\x82\x05\x05\x0b\x76\x58\x2e\xd8\x2a\xf2\xd4\xdc\xbe\x62\x9d\xd6\xd2\x71\x8b\x16\x3c\x6a\x13\xce\xdc\x39\x1a\x06\x2f\x96\x8d\xa5\x2f\x43\xf4\xc8\x88\x4b\x48\xb0\xeb\x20\x2d\x9a\x8c\x0c\xd3\x1d\xeb\x18\x0c\xd4\x84\x98\x2f\xb1\x7f\x19\xaa\x3f\x61\xa8\x16\x52\xd9\x6e\xb0\x36\x0a\xa7\x33\x5c\x4b\x01\x39\x07\x6c\x42\xfa\x57\x9d\xb5\x7b\xcd\x6a\x38\xba\x1b\x27\x62\x00\x4f\xbe\xac\xeb\xff\xf7\x0c\xcc\x7f\xbe\x6b\x79\xdf\xc9\x47\x1c\xca\x5f\x9a\x5b\xb9\x68\xb5\x3c\x5f\xe4\x28\xeb\xde\xd7\xd3\x7a\x70\xdc\x7f\x3a\xe5\xfb\xee\x36\x40\xbd\x50\xcb\x5b\x18\xb2\xc4\x14\xc1\x20\x7d\x4b\xb9\x5c\x3f\x5f\x95\xa7\x7c\xb2\x30\x0e\x63\xeb\xff\x5a\x55\x3f\xd4\xf3\x7c\xf1\x65\xb2\xe8\xcf\x33\x9b\x85\x60\x29\x4e\x74\x84\xc8\xbd\xfa\xf3\xfd\x23\x09\xa1\xfe\xc1\xb1\x36\xfc\x97\xc9\x02\xfd\x4d\x55\xdb\xb3\xae\x17\x2a\x1b\x2d\xd8\x7c\xcd\xc7\x4f\x05\xf6\xd7\xc7\xea\xf9\xf8\xea\xbc\x3b\xc3\x35\xb0\xe5\x84\x57\x8f\x57\x0c\x3e\xb3\xf9\x37\x65\xb5\x36\x30\xa8\xd9\xc2\x5f\xa0\x3b\x68\xb2\x80\xcc\x9e\x7b\xe8\x76\x67\xf1\xa3\xbf\x92\xa5\xe1\xaa\x57\xa9\xf5\xcc\xec\xf0\x1b\x08\xa4\x97\xbf\xe7\x62\x56\xce\x39\x9a\xa8\xb2\xfb\x48\x1d\xc9\xec\x73\xb1\x95\xa6\x95\xd1\x0d\x08\x6a\xe5\xf2\xf1\x1b\x59\x09\xd2\x8e\x0e\x18\x01\xba\x70\xb6\xbc\x98\x2c\xa6\x08\xa3\x43\x44\xf6\xb6\xc8\xd8\x8e\xe0\x25\x94\x5d\xc0\xfa\x7b\xc6\xe4\xd9\x12\xc4\xfe\xfe\xc8\x52\xe8\xa2\x53\xa3\x8e\x90\x26\x2d\xcc\xab\xef\xb1\x89\xc0\x7b\xbb\x68\x7a\x18\xa1\x7f\xf6\x9d\xb6\xe3\x83\xf5\xbc\xcc\xf8\xc4\xdb\xfb\xb2\xeb\xb5\xf5\xae\xd7\xa0\xa8\x80\xa2\xd0\x54\x74\x02\x45\x83\x0d\x23\x88\x59\xa0\x28\xfe\xe4\x6d\xb4\xc8\x91\xeb\xfe\x8f\xde\x46\x3b\x61\xa7\xa7\xcc\xdb\x34\x9b\x69\x78\xc0\x94\x61\x6d\x38\x68\x3c\xa9\x5b\xde\xbf\x8f\x88\xdc\xf4\xaa\x7f\xf9\xfa\xeb\xaf\x51\xbc\xb7\x87\xd0\x3b\x33\xa4\xee\x5f\x07\x12\x0e\x06\x90\x30\xdd\xdb\xdb\x0e\x52\xb7\x9d\x6f\x74\x2f\x9d\x9e\xe0\xb6\xdf\xc6\x43\xf2\xdd\xca\x5a\xb7\xb1\x24\x56\xeb\x36\xde\xd4\xf9\xa6\xb7\x24\xb6\x0b\xc9\x1f\x42\x4a\x76\xec\x76\xdd\xce\xfc\x26\x01\x6a\x15\x47\x09\x71\x5f\xf5\x1c\x92\xfc\xaa\x1e\xee\x3b\x37\x4c\x6d\xbb\x9f\x19\xdc\x6a\x9c\x70\x74\x13\x15\x70\xd8\xed\x77\xf1\xf1\xc4\xf6\x84\xcb\x29\x83\x0c\x73\x0c\xdd\x44\x29\x54\x67\x72\x77\xf0\x1d\x52\xfb\x84\x26\xfa\x21\x58\x29\x4f\x04\xe1\xcd\x56\xab\xda\x6c\x53\x7b\xad\xf2\xe8\x9f\x2c\xc1\x89\x56\x82\xfd\x4e\x51\xa7\x91\x79\x6c\x6b\x90\xc1\x3b\x35\x13\x0e\x3a\x2e\x33\x27\x73\x68\x17\x29\x88\xb2\x04\x6b\x25\x18\xeb\x45\xb1\x3c\xd9\x2a\x8b\x48\x68\x1e\xf1\x60\x03\x59\x60\x9a\xa1\xfd\x1a\xed\xbe\x60\xea\xbe\x7c\xe8\xcd\xba\x79\x0c\x0d\x09\x3a\xaa\x19\xb3\x2f\x58\x6b\xc2\x20\x1c\xd7\x89\x01\x80\xf0\x75\xfd\x3c\xed\xe2\x4f\xb8\x47\x53\xf8\x05\xb9\x33\xe1\xb5\x04\x6c\xda\xe6\x43\x23\x5b\xa4\xfd\x6c\xeb\x68\x64\x3b\x74\x52\x09\x46\x54\xc4\x84\xeb\xdf\x65\x6b\x54\xd6\x09\x55\x1d\x48\x19\x5e\x98\xeb\x44\xaa\x0e\xa4\x04\x3f\x31\xd7\x89\x55\x1d\xb0\xf9\xd9\x97\x6d\xd8\x2f\xdb\xb0\x5f\xb6\x61\x87\xd1\xe6\x97\x6d\xd8\x7f\xca\x35\xde\x30\xda\x79\x8d\x37\x8c\x46\xd7\x78\xf5\x39\xdb\x70\x8d\x37\x8c\xbe\xac\xf1\x5e\xfb\x1a\x6f\x18\x6d\xbb\xc6\x6b\x12\x4e\x77\x8d\x17\x04\xe4\x3e\xb4\xdd\xec\x9d\x99\xb7\x66\xa9\xf7\xa7\xde\x9a\xdd\x44\xc1\x1f\xf2\x70\x41\x83\xe7\xcb\x2a\x70\x77\x15\x78\x13\xc1\x9e\xea\xc1\x26\x0a\xb4\xdf\x5f\x47\x81\xca\xd2\x0d\x35\x0e\xb4\x3c\xd1\x3b\xe5\x74\xd3\xfa\xf7\xe2\xf8\xd9\x4f\xcf\x1e\x3f\x7e\xf9\xe8\xd5\xcb\xfe\x6a\xf1\xf3\xef\x7e\xfa\xee\x87\x6f\x1f\xbd\x7e\x34\x7c\x95\xfb\xc5\xb3\xbf\xff\xf0\xed\x4f\x0f\x9f\xfd\xf0\xf2\xd5\x83\x1f\x9a\x96\x1a\x3a\xb9\xac\xfc\x70\xbb\x65\x65\xad\xc5\x6a\xb6\xac\x93\xb6\xf4\xd6\xa4\x6b\xd4\x62\x76\x8d\xa7\xe8\xd2\x96\xaa\xbc\x92\x4b\x22\x15\xba\x8f\x48\x70\x0f\x55\x86\x25\x11\xad\xcf\x6f\x36\x68\x1f\x85\xe8\x36\xba\x94\xb7\x07\xab\xfa\x92\x26\x7c\x22\x7b\xb0\x52\x89\xfe\x86\xa2\x41\x2c\x02\x61\x20\xbf\x78\x8d\x8e\xd0\x25\xfa\x1b\x0a\x4d\x51\x22\xbf\xf8\x4f\x01\x95\xa0\xdb\x48\xe0\xf1\x05\x9e\x3d\x43\xe5\x8d\x5c\x96\x7b\xdd\xfb\xf9\x52\xfe\xfc\x9f\x96\xa5\x60\x8d\x6d\x67\x25\x2a\xe1\x39\x01\x03\xd3\x1a\xce\x6c\x24\x67\x36\xf2\x82\xe6\xc6\xc0\x98\xa6\xaa\xe4\x2e\xba\x94\x55\x2f\x2d\xcb\x4a\xad\x82\x74\xd9\x78\x09\x0f\xfc\x0c\x7b\x2d\xf8\xda\xef\xfa\xc7\xd1\xbe\xf5\x76\x39\xba\xda\xf0\xe4\xf1\xcb\x17\x82\xd6\x8d\x87\x4d\xca\xa0\xbf\x3b\x61\x59\x1f\x13\xd5\x00\x45\xad\xac\x4f\xd7\x17\x3d\xdd\x32\x56\x7b\x52\x57\xb3\xb0\x50\xbd\x3c\xf1\x33\xba\x8f\xe2\x7b\xe8\x67\xc7\xca\x1c\xf4\x01\xae\xa6\x9a\xb3\xa2\xd4\xe8\xd3\xb2\x7a\xbe\x5c\x43\x1e\x57\xa1\x55\xf0\x58\xee\xcf\x7b\xe8\x0e\x32\x9d\xa6\xae\x81\xeb\x8d\xee\x23\x95\x2f\xc2\x54\x59\xfc\x0d\x3a\xf8\xee\x08\x01\x1a\x0d\x8a\x05\x57\xf7\x44\xb5\x8e\xf5\xeb\x23\x40\x6b\x3f\x5c\x3d\xc0\xfc\x54\xc3\xdc\x01\x75\xc7\x30\xef\x69\x08\xd8\x6e\x69\x49\x53\xac\x05\xdf\x54\xa0\x40\x23\x62\xa1\xf6\x93\xe8\x87\x87\xe8\xf9\xaa\x3c\x2d\xab\xf2\x03\x47\x67\xcb\xf9\xe5\x62\x79\x5a\xb2\x39\x5a\x7e\xe0\x2b\xf4\x1f\x8f\x27\x64\xef\x2e\xda\xbc\xa3\x68\x1f\x6d\xde\x45\xf0\x6f\x08\xff\x06\xc2\xcd\x98\x41\x2a\x8d\x96\xe8\xe5\xfd\x81\x77\xc8\xdb\xc4\x8e\x23\xf3\x16\xe2\x14\x84\x23\xa3\x7e\x8c\x6c\x7a\xf5\x1c\xbc\x5c\xe3\x53\xc3\x4f\x9d\x60\xac\x2f\xb3\xe9\x40\x7f\xf6\x76\xdd\x4d\x59\x83\xfd\x54\xfc\xf4\x6c\xb9\x62\xab\xcb\xce\x4b\x74\xc2\x04\x5e\xe9\x03\x91\x75\x97\xd2\xf8\xea\x8c\xd9\xfa\x5f\x19\x7b\x36\x46\x77\x6f\x6f\xc7\xdf\x6e\x67\xc7\xef\xec\xeb\xf8\xae\x5d\x9d\xeb\x7f\x4a\x60\x79\x5e\x9d\x9d\x57\x4f\x60\x6a\xdd\xa9\x8b\x20\x48\xcf\xf9\xba\x5c\xf1\x5c\x7b\x68\x20\x2d\xab\x75\x9d\x10\x5a\x36\xee\xcc\x16\xea\xc6\xcf\x16\xf3\x5a\x4c\x5a\x0e\x6e\xb6\xe2\x77\x11\x21\xc1\x14\x91\x30\x9a\x22\x9f\x06\x53\x14\x62\xd2\x6f\xac\xde\x2c\xb8\x2b\xca\xf4\xa2\xfe\xa3\x05\xf5\xa4\xd9\xfa\x6e\x81\xde\xbb\x1e\xb4\x2b\xbc\x5f\x00\x2b\xb5\xf0\x12\x62\x3d\xf7\xae\xbf\xbd\x79\x6b\xf1\xf6\x5b\xa8\x9a\xf8\x03\x38\x52\xe5\x16\xfc\xa2\x51\x3b\xd8\x84\x1b\x4b\x25\x00\x94\x34\xaf\xf5\xc2\x08\x10\x79\x1e\xba\x83\xc4\x40\xdb\xbc\x94\xa0\x73\x42\x44\x2f\x3e\xf9\x5c\x3b\x7a\x86\x85\x39\x03\xd3\x8c\x8b\x67\x75\x27\x9e\xb0\x05\xac\xfd\xf4\xba\x76\x88\x88\x69\x0d\x2d\x5d\x2f\x57\xe9\x38\xff\x7b\xe0\x3f\x25\x93\xe0\x53\x52\xa2\xee\xa6\x98\xe0\xb5\x75\xd9\xfc\x29\x81\x37\xe8\xfb\xd5\x85\xaf\x77\x25\xb3\xb0\x3e\x41\x2d\xd0\x3b\xf3\x09\x92\x4e\x22\x41\x72\x95\x0c\x82\xa4\x93\x3a\x90\x5c\x3d\x67\xa0\x22\x18\x8f\x51\x8c\xbb\x24\xe3\x2b\xd1\x8c\xbb\x44\xe3\x5d\xa8\x36\xca\x41\x2a\x57\xb3\x34\x52\x2e\xaa\xa5\xd4\x66\xb3\xa4\xe7\x0c\x16\xf3\x6a\x73\x36\xb0\x42\xd4\x38\x80\xf7\x66\xdf\x1d\x01\x5f\x6c\x75\xe6\xcb\x0b\xa4\xea\x8c\xef\x46\xbc\x10\x03\xec\xda\x62\x03\x32\x50\x06\x3b\x90\x1f\x65\xd0\x0b\x9f\xed\x26\xf0\x6a\xc6\x2b\x36\x2c\xd9\x61\xd6\xa0\x01\x7b\x5a\x8a\x29\xc8\xfc\xfc\x74\x01\x9d\x33\x98\x55\xcd\xc1\x3a\xcc\x9e\xa2\x36\x92\x36\x56\xde\x71\x4e\xa2\xe3\xe8\x48\xa9\x9d\xa1\x58\x10\x89\xbf\x3a\xf4\x6c\xa4\xe7\xaa\xfb\x44\xab\x3b\x5f\x5e\x58\xe3\x52\x2b\xb7\x5e\x19\xe3\x1c\x53\x4f\x5e\x09\x29\xbc\x7a\xb3\xb1\xd1\xfe\x6a\x23\x75\xed\x08\x7a\x60\xaf\x04\xca\x76\x04\xa4\x6f\x77\xfa\xe6\x6a\x6a\xe0\x70\xab\x6d\x8f\x02\xe8\xd2\x44\xc8\x25\x80\xe9\xa1\x6b\xb3\xfc\xd5\x06\xb7\xd5\xf1\x36\xd5\xa5\x7e\xbd\xda\x60\x97\x1c\x55\xdd\x27\x4d\x5d\x90\xa3\x53\xbd\xd7\xe7\x2b\xb0\x28\xf9\x9c\x88\x50\xf5\x71\x2d\x7f\xb5\x09\x94\x2f\x40\x93\x89\xa2\xad\xb9\x1a\xac\xf0\xab\xfb\xc1\xb6\xe9\x0d\x40\x7b\xd2\x40\x93\x5e\x43\x42\x7b\xd2\x83\xf6\x74\x1c\xda\x1f\x6a\x54\x1d\x57\xe8\xd0\x4f\xd4\x77\x89\x16\x35\x45\x3b\xcd\xf6\x5e\xcc\x96\xe8\x79\xe9\xd0\x6c\x81\xb2\x7e\xf3\x11\xdf\xd3\xbe\xca\x50\xae\xf9\xfe\xc9\x2a\xdf\xe1\x5c\x03\xd6\xa5\xc6\xa2\x92\xd4\xa0\x31\x87\x54\xd7\x7e\xd2\xd6\xb6\xbb\x24\x18\x2c\x66\xcb\x67\x32\x4a\x39\xea\xac\x87\xe9\x74\x59\x3b\xfb\x62\x09\x81\x9e\xc3\xc5\x8b\x09\x74\x8b\x62\x74\xe1\x41\xb3\x95\x49\xdd\xe9\xfb\xf7\x5b\x22\x41\xb5\xeb\xfe\xc1\x53\x9a\x3e\x41\x77\xb4\x72\x9b\xa2\xa3\xae\xe9\x34\x30\x8c\xc0\x9f\xee\x08\xbc\xbb\xe6\xd1\x76\x77\xab\x15\x8f\x7e\x97\x15\x55\x1a\x18\x58\xed\x18\x12\x17\x05\x57\xee\xf9\xd3\x11\x1c\x4f\x76\xc4\xe1\x1a\xdb\x56\x6c\xb1\x3e\x5b\xae\x9d\x5a\x02\xee\xf7\x79\xf9\x44\x1a\xc6\xab\x37\xda\x82\x62\xab\x87\xd6\x31\x4f\x36\xdc\x66\xe0\x53\x35\xc7\x46\x3f\xab\xff\x38\x2b\x11\xab\x60\x08\x04\x7f\x69\x8e\x09\x5f\x79\xd0\x07\x63\xd2\xd6\x66\x72\xe4\x35\x0e\xc0\x58\xef\x95\x57\x77\x47\xd6\xb6\x99\xfc\x2b\xaf\xee\x8c\xaa\x67\x19\xb7\x0e\x0f\xd1\xc3\x99\xcb\xf9\x6d\x3f\xac\x5f\x71\xc8\x18\x77\x8d\x48\x73\x5f\xb5\x1f\x6e\xc6\x95\x11\xe5\xde\xcd\xa5\xd6\xad\x5e\x35\x0a\xb7\x7d\x93\x0d\x6e\x1a\x4d\xb4\x20\x64\x6f\x9b\x01\x50\x02\x20\x3d\x00\x64\x00\xc0\xc9\x45\x11\x7b\xac\x96\x17\x0e\x26\xce\x35\x6b\x78\xd5\x9a\xc6\x3b\x34\xf9\x5d\x91\x2f\x7f\xb8\x59\x13\x03\x5f\x5d\xfe\x63\xae\x59\xcd\xab\xd6\x84\x74\x88\xf0\x43\x0b\x71\xbe\xbc\xf8\xf4\x05\xda\xef\x96\xa6\x19\xc9\x40\xde\x56\x4b\xeb\x2c\x43\x8a\xf1\xad\xb7\x98\x09\xe5\xa3\x93\xb6\x0e\x14\x9b\x21\x76\xe2\x95\x6e\x0b\x61\x92\x8e\xcd\x8e\x7f\xae\x63\x51\x86\x45\x9a\x6b\x3f\x15\x35\xa8\xdf\xac\xf8\x88\x76\xc3\x65\xa0\xdb\xb0\x78\x35\x5c\x07\xba\xea\x59\x2a\x7c\x95\xa3\x54\x70\x48\x2a\xe3\xe5\xbc\x7b\xde\x09\xef\xa1\xc3\x2e\xfd\x7b\xe8\x76\xff\x07\x40\x0e\x1b\x34\xcd\x69\xae\x7f\x92\x43\x50\x9f\xbc\x86\xa7\x2f\x33\xd6\xc4\x1b\xd7\x20\xd1\xa1\x51\xf4\x7a\x95\x7a\x15\x70\x08\xf3\xd0\x78\x98\xee\xe5\x7f\x9d\x73\xfe\x0b\x1f\x02\x9d\xb1\xf5\xac\x56\xee\xad\xde\xa2\x1f\x50\xf1\x29\x8b\x85\xe3\x6b\x42\xdb\x87\xf4\xb6\x70\x7e\xf7\x35\xc4\x16\x9f\x7d\x55\x4e\x0b\x0d\xd5\xc2\x9c\x1e\x70\xee\xb4\x36\xa7\x81\x52\xcb\x73\x3a\xa8\xab\xae\x2b\xb6\xac\x70\x77\xe2\xc9\xa0\x13\x4f\xae\xda\x89\x27\x83\x4e\x3c\xd9\xad\x13\x66\x51\x49\xd5\x55\x46\x56\x2d\xd1\x8a\x57\xab\x92\x7f\xe0\x86\x03\x88\x48\x5d\xee\x96\xfe\xe0\xec\x7c\x3d\xab\xc9\x30\xb1\xc8\x50\xf3\xe9\xb0\xe6\xa7\xa7\x27\x36\xdc\x1e\x6a\x50\x4f\x87\x26\x6c\xbd\x4f\x74\x4d\xa7\x26\xed\xfe\x4b\x1d\xa1\x34\xb8\xb3\xe6\xb2\xd3\x16\x1e\x62\xcb\xcd\x9c\xfa\x63\x7b\x3e\xd3\xc9\xf6\x2f\xc7\x35\xaf\x78\x5c\xd3\xdf\xf5\xb0\xa6\x3f\x76\x54\xd3\x77\x1c\xd4\xf4\xbf\x1c\xd3\xbc\xee\x63\x9a\xfe\x96\x87\x34\x0d\x62\xe9\x1c\xd1\xf4\xb7\x39\xa0\xe9\xdb\xaf\xe1\x37\x07\x0f\xef\xd2\xe0\xe3\xdb\x29\xc5\xff\x22\xc7\x35\xfb\x09\x76\x42\x4c\xfe\xb0\x33\x9c\x75\xba\x1d\x81\xf3\xcf\x95\x6e\xe7\x4a\xa7\x2d\x55\x71\x7b\xda\xb3\xae\xb3\x53\x42\x9e\x10\x93\xce\xb1\x90\x10\x13\xeb\x31\x13\xba\x65\x42\x1e\x51\xb1\x73\xd4\x84\xaa\xac\x16\x21\x26\xd7\x76\x85\x58\xef\xbe\x35\x27\xcf\xe0\x90\x83\xb7\xc9\xd2\x34\x4d\xf2\x30\x9f\x6a\x09\x7b\xf6\xa6\xa6\x9a\x11\x49\x18\x49\x08\xd3\xd3\xf9\xec\x19\xf2\xf6\x18\x9a\x26\x38\x4c\x3c\x1c\x32\x3d\xfb\x8f\x19\x09\x0e\x49\xc1\x33\x99\x33\xa8\xce\x0d\xb4\x25\x92\x28\xf6\x7d\x12\x45\x32\xad\x90\xca\x1c\x64\x46\x42\x79\x1a\x04\x8c\xc6\x7a\x5e\xa1\x2d\x91\xe4\xa9\x97\x11\xee\xe5\x7a\x1a\x22\x33\x92\x20\x4e\xc3\x80\xe2\x5c\x4f\x52\xd4\x0b\x4d\xaf\x3b\x4b\x91\xd0\xa7\x2b\x66\x29\xc2\xd1\x97\x34\x45\xd7\x14\x13\xd1\x9d\xd3\x14\x89\x26\x63\x71\x91\xee\x33\x86\x91\x11\xfd\x92\xa6\xe8\xfa\x63\x23\xba\x6d\x9a\x22\xa3\x70\xba\xf1\x11\x1d\x4d\x53\xe4\x53\x77\x9a\x22\x31\x8c\xdf\xa5\xc4\x14\x2d\x91\x7f\x91\x68\xe9\x5f\xfa\x72\xcb\xf5\x5e\x6c\xf9\x4c\x57\x56\xae\x1e\x44\xc9\xa2\xa6\xbb\x0a\xd0\x4f\xf5\x09\x5e\xc3\x5b\x37\xdd\x43\xbe\x07\xec\xec\x6c\x7e\x39\x51\x3f\x4e\x11\x5b\x9d\x9c\x9f\xf2\x45\xb5\xee\xbf\xc9\xa3\x5f\x9f\x69\xe9\x81\x54\x4a\x2d\x8a\x1e\x7a\x6f\x13\x10\xca\x48\x91\x40\x5c\x91\xc7\x84\x32\x4e\xc8\xde\x74\x58\x2f\xc6\x7e\x1c\x04\x09\xa4\x19\x24\x3e\x2f\xa2\x30\xcb\xf5\xd0\x60\xd0\x20\x0d\x33\xaf\x48\xb3\x02\x1e\x40\xc8\x82\xdc\x4f\x49\x61\x02\xcc\x93\x34\xcc\x53\x16\xc2\xeb\xd9\x98\x26\x79\x9a\x66\x4e\xc0\x7e\x12\x46\x19\x09\x53\x08\x67\xfc\x80\xa6\xa1\x4f\x4d\x80\xc3\xa4\xc0\x18\x17\x40\x71\x1a\x79\x61\xee\xe1\xc4\x09\x38\x21\x7e\x41\x09\x83\x27\xb7\x59\x81\x93\xa0\x48\x52\x13\x60\x96\xe2\x2c\xe4\x39\x50\x9c\xb3\x28\xa7\x18\x53\x27\xe0\x9c\x7a\x31\x63\x92\xc7\xcc\xf7\x7c\x8f\x04\x46\x1e\x63\x42\xfd\x30\x95\x6f\x46\x04\x61\xec\x45\x45\xca\x9d\x80\x49\xe0\x63\x1a\xa6\xf0\x76\x44\xc0\x79\x90\x12\x9a\x19\x59\x11\x7a\x59\x9c\x67\xf0\x80\x78\x1e\x16\x45\x1a\x70\xe2\x04\x1c\x93\x94\x87\x79\x0c\xac\x28\x48\x9c\xd2\x24\x32\x0a\x8f\x7a\x39\x4f\xb1\x7c\xbc\xc2\x4f\x71\x94\x44\x29\x76\xf3\x38\xcd\x33\x2f\x92\x19\x2a\x49\x98\xc5\x98\xf8\xa1\x09\x70\x86\x93\xb4\xc0\x92\x80\xac\x88\x12\x12\x25\x81\x13\x30\x0f\x92\x34\x4a\x32\xe0\x5d\xc2\x0b\x1c\xb0\xdc\xc8\x63\x5e\xa4\x3c\x88\x29\x3c\x23\xee\xd3\xa0\x20\x21\xf7\x9d\x80\xbd\x22\xc3\x49\x9e\x41\x03\x9a\xd2\x2c\x0f\x53\x23\xc5\x24\xf0\x32\x86\xb3\x0c\x1e\x69\x8f\x59\x96\x64\x51\xe8\x16\x5e\xce\x13\x92\x45\x60\x20\x61\x42\x52\x8f\xc4\x46\xc0\x01\x8b\x03\x1a\x30\x98\x23\x44\x9c\x45\x3c\xa0\x6e\x8a\xc3\x2c\xf5\x58\x92\x03\x25\x69\x1e\xe0\x22\xcd\x03\xa3\x49\x47\x45\x42\x69\x0e\x80\xa9\x8f\x71\xe8\xa7\x6e\x8a\x13\xea\xf3\x10\x87\x04\x4c\x9a\x47\x51\x5e\x30\xb3\x81\x50\x1f\x67\x51\x04\x11\x3e\xc9\xd3\xc0\x27\xd8\x73\xfb\x0a\xcf\xf3\x49\x9c\x51\xf9\xe6\x7b\x91\x12\xec\x1b\xd5\x2d\x2d\xc2\x24\x2e\x32\x95\xdf\x94\x17\x1e\xe7\x6e\xad\xc8\x22\xee\x79\x69\x01\x8a\xef\xe7\x8c\xd2\x22\x33\x6a\x45\x1e\xb2\x38\xc1\x01\x00\x4e\x7c\x8f\xb1\x98\xb8\x59\xe1\x45\x19\x8b\xfc\x50\x3e\xef\xe2\x79\x3e\x25\x66\x03\xc1\x01\x49\x48\x22\xe7\x5e\x1e\xf3\x78\xc4\x63\x37\x2b\x48\x9c\xc6\x1e\xa3\xe0\x5c\x82\x28\x27\xa4\x28\x8c\x26\x4d\x38\x16\x6c\x02\x96\x85\x19\x89\xb2\x84\x44\x4e\xc0\x41\x4e\xb2\x28\x2f\x40\x2b\x42\x96\x05\x84\xf1\xdc\xe8\x2b\x7c\x9f\x7a\x39\x06\x96\x25\x79\x12\xa6\x7e\x5e\x38\x01\x47\xa1\xc7\x62\x3f\x0c\xa4\x81\xb0\x22\xf2\x73\x6e\x56\xb7\x88\x79\x2c\x05\xbf\xed\x67\x71\x9c\x12\xe6\x76\x9b\x14\x67\x24\x4b\x88\xf4\x6e\x31\xcf\x19\xe7\x91\x09\x70\x42\x62\x42\x32\xc9\x32\x1c\x50\xe2\x87\x7e\xea\x04\xcc\x48\x5a\x70\xca\xa4\x9f\xcd\x0a\xec\xf9\x91\xd1\x40\x18\xc5\x2c\x8a\x02\xa0\x38\xcd\x02\xe2\x7b\x9e\xdb\xbb\x65\x24\x48\x69\x1a\x7b\xe0\x67\xbd\x82\x26\x71\x82\x8d\xde\x2d\x8e\xb2\x10\x33\xe0\xb1\x17\x85\x41\xca\x7d\xb7\x56\xe4\x38\x21\x9c\xe2\x04\x00\x47\xbc\x08\x09\x36\x8e\x79\x79\x94\x24\x5e\x44\x40\x16\x61\x18\x85\x2c\x19\xb1\xbc\x22\xf0\xb8\x1f\x4a\xde\x85\x71\x8c\x89\x47\x98\x51\x8f\xbd\x88\x31\x4f\xf6\xcc\x27\x69\x9a\xe3\xd4\x2d\x3c\x9c\xb0\x20\xc3\x18\xdc\x66\x4a\x73\x92\x7b\x99\x91\x62\xcc\xfd\x38\xca\x3c\xa9\xc7\x38\xc0\x2c\x0d\xdd\xde\x8d\xc4\x01\x8d\xe3\x00\xf4\x38\x2f\x28\xe7\x69\x92\x98\x00\xfb\x41\xea\xa5\x59\x0a\x3d\xe3\x38\x49\x03\x3a\xa2\x6e\x7e\x82\x33\x2f\x4b\x41\x28\x59\x98\x25\x21\x8b\x7c\xa3\x3f\xe6\x39\x65\x2c\x00\xb7\xc9\xfd\x00\x53\x96\xb9\xd5\x2d\x4c\x93\x2c\x63\x41\x21\x47\x86\xc8\xe7\x7e\x6c\x04\x1c\x51\xc2\xa3\x42\x3a\xab\x3c\x4a\x49\x4a\x99\x9b\x15\x71\x40\x0b\x4a\x38\x18\x48\x98\xf3\x22\x25\x66\x5f\x11\x53\x16\x46\xbe\x1c\x69\x02\x1f\xc7\xa4\x88\xdc\x5a\x41\x83\x8c\xc6\x14\xcb\x48\x08\x17\x1e\x4b\x63\xa3\xdb\xa4\x59\x16\x7b\x44\x0a\x0f\xb3\x28\xf0\x13\xee\x8e\xdd\x12\x2f\xe5\x45\x51\x30\x19\x45\x46\x3e\xe6\xc4\xa8\x15\x2c\x08\xbd\x28\xe3\x60\x79\x39\xa7\x24\xcd\xb9\x3b\x76\x4b\x79\x91\x30\xbf\x90\x23\x03\xc9\xa2\x38\xc1\xe6\xb8\x22\x8a\x71\x4c\x0b\x39\x84\xf9\x31\x09\x7d\xe2\x16\x5e\xc6\x48\xec\xf3\x0c\x78\xcc\x19\x89\x22\x9c\x18\x79\x9c\x63\x1a\xa5\x54\x0e\x4d\x44\x28\x12\xe9\x2e\x02\x0e\x03\x11\x96\xb3\x38\xcf\xc1\x40\xb2\x9c\x7b\x3c\xc5\x46\xb7\x59\x84\x71\x1e\x14\x71\xa1\x06\x5d\x9e\xe3\xd8\xad\xc7\x5e\x54\x78\x51\x2c\xe3\x85\x98\xe0\x38\x2a\x52\xa3\x49\x7b\x2c\xf2\xe3\x3c\x03\x03\x61\x24\xa3\x09\x65\xee\x11\x04\x63\xbf\x48\xa8\x17\xa8\x85\xbb\xc4\xcb\x99\x91\x62\x9c\xc6\xd8\x4b\x7d\xe9\x8f\x7d\x9c\x05\x31\x76\xf3\x98\xd0\x3c\x8d\xe3\x22\x94\x5a\xe1\x05\x71\x4e\x8d\xfe\xd8\x27\x19\x63\x69\x0c\x5a\x11\x78\x59\x4c\x82\xc4\x6d\x20\x7e\x96\xf0\x94\x7b\xc0\x0a\x1c\x66\x49\xca\x53\xa3\xf0\x02\x1f\xe7\x51\x9c\x41\xcf\x92\x0c\x7b\x5e\x1e\xb8\xf5\x38\xc8\xb2\x30\x0f\x64\xe0\x9d\xa5\x3e\x0f\x48\x6a\x1c\x9a\x44\xb8\x42\x92\x04\x9c\x55\x91\x45\x61\xcc\x85\x7b\x75\xf9\x8a\x22\x4b\xa3\x82\xc9\x41\x92\xe5\x51\xc1\xb8\x91\xe2\x28\x0b\x02\x9c\x50\x00\x1c\xb0\x20\x0e\x29\x8e\xd5\x22\xea\x5b\xc7\xb5\xd5\x76\x5e\xf8\xe3\x55\x6f\xa8\xda\x9e\x41\xfb\xb1\x73\x43\xf5\xa7\xab\xdd\x50\x0d\x31\xd9\x6e\xeb\xc0\xb0\x1d\x71\xfd\xd9\x47\xaf\xba\x75\x10\x31\x2f\xe1\xf5\x82\xbb\x9f\x66\x59\xe2\x59\xb6\x0e\xd2\x34\x8a\x19\x97\xc3\x2f\x0d\x32\xc6\xe2\x6e\xe8\xe2\x40\xe2\x67\x11\x2f\xfc\x18\x3c\x59\xc1\x93\xa0\xa0\xc2\x93\x99\x6a\xb2\x30\x28\x8a\xd0\x07\x2b\x08\x0b\x9c\xfb\x51\xb1\xed\xaa\x7e\x88\x3d\x1e\x12\xe9\x7c\x58\xce\x23\x4a\x72\xcb\xd6\x41\x92\x7a\x61\x44\xa5\x42\x92\xd4\xe7\x51\x86\x8b\x2d\x91\xe0\x82\xfa\x79\x22\x75\xbe\x48\x03\x9c\xe6\x91\xa5\x27\x61\xca\xbd\x2c\x97\x61\x10\xf6\x63\x4e\x70\x9c\xec\xb2\x75\x70\xdd\xf7\x48\xb7\x49\x0d\x0b\xf5\x3c\x7b\xe6\xd7\x63\x6c\x4f\xfd\x7a\x4c\xec\xb9\x5f\x8f\x7d\x7b\xf2\xd7\xe3\xc0\x9e\xfd\xf5\x38\xb4\xa7\x7f\x3d\x8e\xec\xf9\x5f\x8f\x63\x4b\x02\x58\xd9\x41\x48\x0f\x6b\x3c\x07\x2e\xcb\xe7\xb2\x7c\x78\xd9\x43\xf2\x00\x9a\x1b\xaf\x40\xc9\xf2\xb9\x2c\xb7\x34\x27\xd0\x9c\x58\x9b\x93\xb9\x2c\xb7\x34\xf7\xa1\xb9\x6f\x6d\xee\xcf\x65\xb9\xa5\x79\x00\xcd\x03\x6b\xf3\x60\x2e\xcb\x2d\xcd\x43\x68\x1e\x5a\x9b\x87\x73\x59\x6e\x69\x1e\x41\xf3\xc8\xda\x3c\x9a\xcb\x72\x4b\xf3\x18\x9a\xc7\xd6\xe6\xf1\x5c\x96\x1b\x8e\xf5\x6d\x99\xf4\x58\x6a\x86\x09\x38\x93\x4a\xd1\xcf\xb8\x07\x47\x6e\xa5\x42\x98\x5a\xa5\x52\x17\x4c\xad\x32\xa9\x07\xa6\x56\x99\x54\x01\x53\xab\x5c\x8a\xdf\xd4\x2a\x97\x92\x37\xb5\xe2\x52\xea\xa6\x56\x5c\x0a\xdc\xd4\xaa\x90\xc2\x36\xb5\x2a\xa4\x9c\x4d\xad\x4e\xa4\x8c\x4d\xad\x4e\xa4\x78\x4d\xad\x66\x52\xb4\xa6\x56\x33\x29\xd5\xb9\x29\xef\xa0\xeb\xea\xee\x96\xcf\xa1\x5a\xf3\x69\xd7\xf8\x7f\x2c\x65\xee\x61\xdb\x75\xf3\x47\x30\x82\xd7\xdb\x67\xc3\x2a\x5b\x24\x8a\x96\x68\x04\x0b\x7e\x2c\xeb\xdb\x06\x7a\xd6\x68\x74\x1b\x91\xb7\x50\xd3\x9c\xcb\xb5\x85\x31\x97\x30\xd4\xfd\x82\x3e\x0c\xb8\x35\x7f\xa5\x0c\xd4\x87\x87\xe8\x3f\x20\x1b\xb1\x1d\x79\x9d\xd2\x79\xa7\x0c\xd5\x9b\x59\x93\xe7\x78\x33\x76\x17\x4f\x55\x9b\x6b\x2d\xdc\xf7\xf1\x64\xad\x59\x27\x0b\xf6\x4c\x26\xff\xd5\x93\x57\xcf\x21\x45\x71\x9d\x0e\xb8\x53\x8f\x0e\xea\xc1\xa1\xd7\x77\xa8\x5b\x2d\x76\xdd\x30\x95\x35\xe7\x1d\x2a\xe6\x43\x2a\x66\x26\x2a\xe6\x43\x2a\x66\x3a\x15\xdd\x7a\xf1\xb0\x9e\x25\x93\xb1\x2e\x52\x4b\xce\x9c\x0f\x5a\xee\xed\x5d\x92\x6f\xb7\x12\xc5\xdb\x49\x14\xb7\x12\xc5\x5b\x49\x14\xcf\x3a\x09\xbe\x67\x75\x16\x6e\x2d\x31\xf7\x5c\xe5\xea\xd6\x98\x84\x15\x87\xbb\xd5\xe0\x1c\x73\xa2\x89\xb4\x86\x17\x8d\x8a\x14\xcf\x3b\x64\xcc\x0d\x64\xcc\x4c\x64\xcc\x07\x64\xcc\x3a\x64\x74\x01\x46\x03\x78\x24\x72\xca\x74\xa7\xdc\xe1\x2e\x57\x12\xb7\x62\x8f\x5d\x62\xff\xb1\x8c\xa5\xe7\x32\x0e\xcc\xbd\x9a\x73\x55\xd3\x71\x27\x5c\xd6\xc4\x91\xe6\x48\xac\xaf\x42\xd7\x75\x25\x01\xd8\x18\x59\xf4\xeb\xce\xeb\xba\xa3\x34\xb4\x9e\x66\x2e\x98\x56\xc6\xfd\x91\xab\x5b\xbd\x75\x65\x33\x59\x7d\x06\x39\xdb\x04\x1c\x21\x49\x6f\x0f\xdd\xaf\xad\xb3\xf9\xe5\xff\x47\x18\xdd\x45\x83\x63\xd3\x43\x3a\xc4\xbf\xb5\x04\xc7\xc9\x10\xff\xee\x37\xd6\x62\xa1\x02\x5f\x95\x0a\xe0\xe2\x96\x34\x48\xe9\x0c\x29\x90\x92\x18\xe0\x37\x03\x6d\x47\xc5\x1f\x4b\x9b\x78\xdb\x51\xef\xc7\xd2\x44\x9c\x3d\x27\xbe\x4a\x8a\x3f\x43\x37\x51\x31\x53\x69\xf1\xc5\x17\xf3\x3d\x3e\xd9\x46\xda\x3e\x9f\x8b\x36\x73\xd5\x46\x7c\x39\x99\x3b\x92\xe9\xcf\x20\x9b\xbe\x00\x9d\x4a\x3c\xf0\x39\x93\x9f\x53\xf5\xd9\xde\x7c\x0e\xcd\x05\x96\x54\xa2\x84\xcf\x99\xfc\x9c\xaa\xcf\xee\x94\xfc\x33\x99\x93\x5f\x39\x1c\x39\xae\xb0\xb9\x4c\x2f\xbd\x27\x93\x1f\xb0\x59\x9d\xb1\x5f\x15\x76\x72\xf6\xcf\xb4\x57\x24\x58\x3d\xea\x38\x33\xf3\xc3\x6c\x6a\xd2\x00\x52\x38\x67\x5d\x9c\xf3\x0e\xce\x59\x17\xe7\x5c\xc7\x39\xdb\x06\x27\x96\xfd\xe4\x6a\x68\x90\xf7\x4d\xb8\x1c\x14\x68\x9d\xf6\x7f\x56\x3f\x5a\xa1\x15\x06\x6d\xa1\xc0\xe9\xd7\x65\x32\x0d\xb7\x1b\xa7\xec\xa7\xaa\x5c\xe3\x9c\x75\x71\xce\x3b\x38\x67\x5d\x9c\x73\x1d\xe7\xac\xc5\x69\x8c\x3a\xc7\xdf\x21\x30\xd3\xfa\x3d\x64\x5f\xfa\xde\x7e\x99\xea\x7b\x30\xde\xef\x4b\xd7\x35\xaa\xef\xc1\x19\x7c\x5f\xda\x5c\xe8\x07\x78\x28\x41\xd4\x99\xcd\x1b\x12\x4d\x46\x29\x2b\x0a\x84\xb3\xb6\x2f\xd2\x5d\x54\x58\x77\x17\xb3\x6d\x7c\x55\x8b\x56\xfc\x2b\x38\xe2\xc6\x59\x01\xaa\x6c\x66\x42\x98\x5d\x09\xe3\xf7\x46\xd7\xd3\xc7\xf8\x7d\x69\xc2\xf8\x7d\x79\x15\x8c\x66\x67\xd7\xc7\xf8\xa3\x11\xe3\x8f\x26\x8c\x66\x6d\xeb\x3f\x5e\x61\x41\x09\x8b\x17\xb5\xd9\x43\x45\x2b\x75\xb0\x0e\x52\x7b\xa5\x7d\xe9\x1e\x81\x44\xa2\x93\x58\xc3\xda\x8e\xcc\xbf\x9f\xe5\xac\xe2\xe8\xc2\x3d\xd3\x17\x7f\x30\xdf\x34\xea\x37\x4c\x37\x4f\x4c\x64\xc3\x00\x54\x98\xda\xc0\xc4\xb6\x30\xb5\x81\x39\x34\x37\xb5\x81\x29\x34\x37\xb5\x81\x29\xf9\x24\x9f\xc3\xf3\x1d\x73\xdb\xfb\x1d\x30\xa7\x9f\xe4\x33\xa8\x25\x59\xc7\x75\xce\xe5\x03\xa6\x59\x5f\x02\x11\x90\x32\x13\x8d\xb0\xa4\x90\x99\x68\x84\xd5\x8b\xd4\xd4\x06\x16\x2f\x52\x53\x1b\x58\x27\x61\xa6\x36\xb0\x4c\x32\x78\xcd\x40\xfc\xc1\xb2\xcb\x44\xaa\x7a\x45\xac\xcc\x80\x85\x9b\x89\xe4\x83\xd0\xac\xfd\x76\xc4\x91\xdc\xa8\x86\xc1\xce\xb5\x3e\x56\xa2\xad\x19\x42\x64\x70\x0c\xfa\xcf\x06\xd1\xc0\x71\x93\x8c\x62\x72\x0c\x7a\xcf\x24\xb1\xc7\x9e\x4e\x2d\x1b\x12\xdb\x87\xa3\xad\x32\x4a\x84\xc0\xa2\x74\x88\x10\xb7\x08\x81\x3d\xa9\x42\xd8\xf1\x04\xe9\x38\x42\x6d\x5d\x52\x22\x24\xe0\x62\x87\x08\x49\x8b\x90\xcc\xea\x71\x69\x02\xf5\x35\xf7\x3a\x8e\x50\x5b\xc9\x94\x08\x7d\x81\x30\x1f\x22\xf4\x5b\x84\xbe\xc0\x95\x2b\x84\xfe\x88\x39\xf4\xe1\x68\x6b\x9f\x12\x61\x20\x10\xf2\x21\xc2\xa0\x45\x18\x08\x5c\x5c\x21\x0c\x74\x84\x7c\x1c\xa1\xb6\x5a\x2a\x11\x86\x02\x61\x31\x44\x18\xb6\x08\x43\x81\xab\x50\x08\x43\x1d\x61\x31\x8e\x50\x5b\x5f\x95\x08\x23\x98\x54\x0c\x11\x46\x2d\x42\x88\xde\x4f\x14\xc2\xa8\x33\x89\x18\x47\xa8\xad\xc8\x4a\x84\xb1\x40\x38\x1b\x22\x8c\x5b\x84\x30\x6d\x52\x63\xb2\xa8\xef\x0a\x02\x3e\xf9\xee\xc5\x97\x47\x71\xae\xef\x51\x1c\x2c\x82\x7b\xf5\xb2\x99\x00\x06\x79\x58\x7c\xef\xba\x9f\xc5\x31\xa3\xc1\xff\x94\x0f\xe3\x3c\x5c\x2e\x3e\xf0\x95\xcc\xf2\x8b\xaa\x25\xf2\xc9\x9d\xb4\xac\x44\x80\x92\x23\x06\xe7\xb3\x53\x5e\x2c\x57\x5c\x1d\xa7\x1e\x48\x4d\xbb\x6b\xa2\xed\xdd\x55\xcb\xd7\x3e\xb9\x8e\x87\x78\xfe\xac\x4f\xf0\xe8\x74\x36\xf9\x41\xee\x22\xec\x91\xe0\xd0\x57\x79\x8a\xbf\xdc\x6e\xb2\x5e\x55\x0a\x31\xd9\xf5\x76\x93\x68\x32\x72\xbb\xa9\x73\xac\x61\x70\xbb\x29\xc4\xe4\xcb\xed\xa6\xeb\xbe\xdd\x24\xa4\xb2\xdd\xed\x26\xa3\x70\x3a\xb7\x9b\xa4\x80\x9c\xb7\x9b\xe4\x3d\xda\x2d\x6f\x7f\xfb\x7f\xea\xfb\x4c\x7c\x91\xdd\x49\xd9\x9a\x47\x41\xaf\xe0\x34\x0f\xfb\x55\x3f\x9c\xbd\xcf\x8b\xde\x8f\x59\x79\x36\xe3\xab\x3f\xe4\x4a\x94\x46\x2a\x7c\x17\x14\xca\x02\x49\x18\x7c\xd6\xe9\xf9\x57\xb8\x3a\xf5\xe3\x56\x6f\x02\xc1\xe1\x99\x87\xd0\xf5\xa6\x9e\xf6\xdb\xf8\x55\xa8\xc3\x43\xf4\x9c\xaf\x4e\x61\x14\x7d\x38\x5b\x96\x19\x47\xb8\xff\x6c\x8a\x68\xfe\xfc\x21\xee\xde\x5d\x0a\xe3\x29\x0a\x92\x29\x0a\xf0\x14\xf9\xfe\x14\x91\x70\x8a\x70\x3c\x45\xc9\x14\x21\xac\x1d\x35\x0a\xe9\x14\x85\xde\x14\x05\x64\x8a\xfc\x60\x8a\x48\x34\x45\x98\x4e\x11\xf6\xa6\x88\xe8\xf5\x92\x29\x0a\xf1\x14\x05\xfe\x14\xf9\xe1\x14\x91\x78\x8a\x70\x32\x45\x58\xc0\xd7\xea\x45\xde\x14\x85\x64\x8a\x82\x60\x8a\xfc\x68\x8a\x22\x7f\x8a\xc2\x70\x8a\x82\x78\x8a\xfc\x44\xab\xe8\xe3\x29\x22\xfe\x14\xe1\x70\x8a\xe2\x29\x42\x11\x99\xa2\x30\x98\xa2\x00\x9e\x16\xd0\x2b\x0a\x4a\xc8\x14\xe1\x60\x8a\x22\x51\x11\x4f\x51\xe8\x4f\x51\x10\x4e\x91\x1f\x6b\x15\x49\x32\x45\x04\x4f\x11\x16\x28\xa7\x08\x11\x3a\x45\xc4\x9b\x22\x2c\xc8\x91\xd5\xde\x3a\xf8\x4a\xcc\x7c\x25\x5d\xbe\x0a\x2a\x04\x1f\x45\xbf\x89\xf8\x3c\x45\x28\xd4\xa9\x55\x88\x45\xb7\x04\xb5\x40\x90\xa7\x53\xe9\x2b\xc6\x09\xaa\x44\x85\x68\x8a\xf4\xee\xe2\x48\xf2\x43\x30\x18\xa8\xf7\xbb\x82\x10\x02\x15\x0c\x16\xfc\xf3\x63\xc9\xd8\x30\xec\xf1\x2b\xf0\x94\xb4\x42\x29\xfd\x40\xc7\x20\x44\x23\x54\xc3\x17\x22\x8d\xa4\xd8\x43\x5d\x86\x42\x04\x42\x1f\x84\x5e\x08\x19\x0a\xc6\xd6\x51\x4d\xe7\x45\xa8\xf3\xd3\xf3\x39\x83\x67\x52\x44\x50\xb9\x9e\x95\xc5\xe0\x85\x27\xb0\x82\xef\x5e\xfd\xf4\xf2\xf8\xbb\xc7\xf2\x4d\x29\xc1\x31\x32\x45\xd0\x79\xc1\x21\x2a\x34\x52\x89\x09\xb8\xab\x34\x15\x2b\x71\x12\xa5\xbd\xc0\x10\xaa\xe3\x7f\xf9\xcd\xb3\xd7\x7c\x8d\xd8\x22\x57\xb9\xd1\xcf\x40\xa4\xf2\x3d\x0d\x03\x1d\xa2\xfe\x4f\xcf\xbb\xf2\xec\x85\x94\xde\xc6\xbb\x0b\x93\x11\x4a\x3c\x6f\xda\x2f\xab\xe7\x0a\xb2\x8a\xa1\x02\xe9\x54\xa0\x9e\x47\x06\x55\x7c\xad\xca\xb0\x34\xd0\x4b\x0d\x08\xc2\x2e\x02\x62\x40\x10\x75\x89\x34\x55\x89\x7b\xfd\x30\x20\xa2\x1d\x42\x86\x20\x92\x3e\x96\x21\x08\xa6\x57\x31\x55\x48\xfb\xdc\x1a\x56\xc9\x7a\x68\x06\x15\xf2\x7e\x57\x86\x55\xb8\x56\x65\x88\xa1\xe8\x52\x39\x6c\x4e\x5d\xad\x31\x1d\x95\x07\xa1\x23\x08\x7c\x3a\xa2\x55\x41\x1f\x89\x41\x2f\xa8\x5b\x6f\x22\x3a\xaa\x98\x31\x75\x29\x26\xa5\xa3\xf2\x4e\xe8\x88\xbc\x59\x9f\x08\x83\x4a\xf4\xd1\x0c\x29\xc9\xe8\xa8\xc4\x73\x3a\xa2\x35\x9c\xba\xb5\xbb\xe8\xe3\x30\x48\xde\x2a\x2e\xe5\x25\xb0\x99\x91\x44\x2b\xb5\x08\xd3\xef\x54\x31\x62\x0f\xba\x50\x4c\x7d\x0c\xf5\x2a\x46\x9d\xd0\xe9\x34\x94\xc7\x5d\x32\x1c\xb6\x81\x1d\xea\x9f\xf4\x29\xb5\x3a\x0a\xec\x90\x68\xda\xed\x8c\x41\x2b\x3a\x9d\xb1\xfa\x09\xec\xd0\x5f\xde\xab\x62\x73\x15\xd8\xec\x0a\xe8\x28\x2b\x30\x1d\x65\x05\xa1\xa3\xa2\xf7\xa9\x5b\x6c\x41\x0f\x84\xcd\x57\xb8\xd8\x1d\x51\x97\x0a\xc7\x74\x44\x18\x94\x8e\x70\x32\xa1\xa3\xaa\xc5\xa8\x5b\xa0\x69\x9f\xdf\x86\xc1\xa3\x8f\x65\x58\x25\xa7\x2e\x91\x72\x3a\x62\x42\x45\x5f\xa2\xfa\x1b\x55\xd3\xb1\x28\x23\xf0\x3c\x1a\x78\xd8\xea\x41\x54\x1d\x6b\x98\xd1\x08\xd0\xe6\x41\x6a\x24\x9e\x09\x49\xd0\x45\x62\xac\x13\x76\xe1\x18\x89\x89\xba\x70\x8c\x75\xe2\xb6\x8e\x01\x8b\xee\x6c\x8d\xcd\x93\x3e\x0a\x03\x10\xd6\xef\x8e\x3d\xe0\x50\x88\x0c\x40\xb2\x0e\x63\x0d\x15\xf2\xb6\x82\xd5\x81\x48\x12\x0c\x8d\x8b\xbe\x54\xac\x71\x97\x93\x99\x98\x8e\xf4\x82\x50\x17\xb7\xfd\x3e\x0a\x93\x6e\xd0\x9e\xdc\x4d\xba\x41\xc7\x19\x1e\xd1\x11\x45\x8d\xe9\xb8\xa2\x52\x3a\x22\x94\x84\x3a\x84\xc2\xa8\xdb\x96\xd2\x3e\x05\x76\x47\xe2\x34\x95\x9c\x8e\x28\x31\xef\xf3\xd4\xee\x4f\xac\x1a\xa4\x4f\x40\x0c\xa5\x78\x0b\xb3\xc7\x64\x0b\x63\xc2\xfe\x16\x86\x8f\x83\x2d\xf4\x19\x87\x4e\xd3\xc7\xd1\x98\x49\xe2\x78\xc4\x19\xea\x21\xb8\x19\x42\x32\xe6\x2e\x31\x1b\xb3\x7b\x9c\x6e\xe1\x2d\x71\x36\xe6\xc8\x70\xbe\x85\xb3\xc4\x7c\x0b\x57\x86\x8b\xbe\x84\x8c\xea\x32\xe6\x2a\x30\x1e\xb3\x50\x4c\xb6\x30\x10\xec\x8f\x58\x19\x0e\xb6\x71\x6c\xe1\x16\x6e\x07\x47\x4e\xef\x86\xe3\x2d\xdc\x12\xa6\x5b\xd8\x22\x4e\xb6\xb0\x7a\xcc\xb6\xf0\xa6\x38\x1d\xf3\x60\x38\x73\xb9\x30\x9c\x8f\xb9\x05\xbe\x85\x1b\xc5\x45\xcf\x43\xed\x12\xaa\x60\x2f\xb0\x38\x23\x33\xc9\xa4\xc3\x15\x6c\x0d\x51\x24\x6c\x13\xf4\x40\x2b\xf7\x0c\xe5\x61\x4f\x38\xc3\x1a\x51\x87\x69\x26\x1c\x71\xa7\xc6\xf8\x70\x6c\x8f\x4d\x5a\x2c\xb6\xc8\xa4\xee\xa9\x2d\x2a\x69\xa9\x18\xd2\x99\xf5\xb8\x39\xac\x91\x77\xb8\x65\x0b\x4d\x00\x82\x25\x2c\x51\x6d\xcd\x1c\x70\x75\x0f\xd3\x31\xf2\x09\xb5\x2b\x8a\x4f\xc7\x14\x25\xa0\x63\x82\x0e\xa9\xbb\xf3\x11\x75\xab\x52\xac\x95\x0f\x4b\x29\xb5\xb3\x2e\xa1\x2e\xd6\x31\x3a\xa6\x5e\x29\x75\x1b\x41\x46\xdd\xaa\x93\xd3\x31\xc5\xe0\x74\xcc\x08\x0a\x3a\xa6\xe2\x9d\xb0\xc2\xa2\x04\x78\xc4\x5c\x31\x19\xd1\x50\xec\x8f\xba\x0c\x1c\x38\x35\x15\x87\xa3\x06\x8f\xa3\x51\xaf\x81\x63\x97\x27\xa6\xa3\x96\x88\x93\x51\x97\x81\x99\xc3\x1a\x71\x3a\xe2\x2e\x70\x36\xea\xb5\xb0\xee\x0e\x0c\x28\xf8\x88\xef\xc5\xc5\xa8\x4b\x52\xa1\x85\xb3\x9b\xd8\x69\x57\x98\x8c\xbb\x16\xdf\xe1\x39\x70\x30\x62\xd6\x38\x1c\xf5\x2d\x38\x72\x1a\x30\x8e\x47\x7d\x1b\xa6\x23\xce\x07\x27\xa3\x16\x88\xd9\x88\x1b\xc0\xe9\xa8\x0f\xc4\xd9\xa8\x2b\xc0\xf9\xa8\x3f\xc2\xdc\xe1\xec\x70\xd1\xf5\x46\xbb\xc4\x0f\xd4\x93\x28\xcd\xbe\xa5\x8e\x3e\xb1\x17\x58\x42\x89\x9a\x68\x43\xb9\xdf\x42\x08\xcc\x8a\x18\xd8\x95\x28\xec\x72\xc4\x1c\x43\x34\xc1\xb1\x09\x7d\xec\x75\xc2\x3f\xfb\xf8\x59\xef\xa8\x98\x23\x88\x56\xb6\xe6\xf8\x41\x96\x9b\x63\x87\x96\x7d\xb6\x1d\x94\x96\x3d\x06\x18\xb9\x66\xa5\x96\xc8\xa1\x56\x6f\x73\xec\xd0\x0a\xd8\xd2\x7f\xa7\x7c\x31\xb5\x77\x8f\xd0\x31\xe2\x7d\x3a\xc6\x80\x80\xba\x45\x1c\xd2\xb1\x2e\x44\xd4\xaa\x3f\x31\x1d\x53\x3e\x4a\x5d\xfc\x4b\xba\xc8\x6d\x41\x84\x43\x3b\x52\xea\x92\x5e\x46\xc7\xb4\x2f\xa7\x6e\xfd\xe5\xd4\x6d\x7e\x05\x1d\xb3\x10\xec\x8d\x98\x08\xc6\x23\x56\x88\xc9\xa8\x19\x62\xdf\x35\x52\x38\x35\x1c\x87\xa3\x26\x82\x23\x6f\x4c\x4e\x38\x1e\xf5\x64\x98\x8e\x5a\x0b\x4e\x46\xdd\x05\x66\xa3\x0e\x0f\xa7\x23\x3e\x13\x67\xa3\x7e\x03\xe7\x23\x6e\x09\x73\x87\x5f\xc2\x85\xd3\x6d\xc8\xe8\xc1\xdd\x07\x3c\x6a\x97\x98\xd8\x0d\x13\xfb\x23\x66\x8f\x83\x11\xc5\xc7\xe1\xa8\xed\xe0\x68\xdc\xbb\xc5\x0e\xf7\x86\xe9\xb8\xf1\x24\x4e\xff\x81\xd9\xa8\xff\xc3\xe9\xa8\x13\xc5\x99\xd3\x89\xe0\x7c\xd4\x4b\x61\x3e\xe2\xa6\x70\xd1\xf5\x23\xbb\x05\x0f\x46\x9f\x52\xd3\x6b\xdb\x21\x69\xa8\x31\x86\x0c\x77\xb5\xe3\x1a\xc6\x88\x41\x55\x80\xf5\x14\x63\xdc\xd0\xc4\x7c\x86\xf2\xa8\x06\x60\xab\x10\xb7\x04\x1a\x4a\x75\x99\xdb\x42\x86\x96\x3e\x4b\xcc\xd0\xf6\xd0\x80\x21\x6d\x09\x34\x93\x90\x75\x2a\x98\x06\x0e\xab\xed\x71\x5d\x38\x06\xd0\x45\x87\x39\xe6\x35\x07\x57\x7b\x4c\x47\x98\x4b\xa8\x67\x53\x1c\x9f\xba\x15\x27\xa0\x2e\xc5\x09\xe9\x88\x5e\x44\x74\x84\x6b\x31\x1d\x51\x3d\x4a\x47\x44\x9b\x50\x1b\xdf\x19\x1d\x91\x69\x4a\xdd\x5a\x9b\xd1\x11\xad\xc9\xe9\x88\xe4\x38\x75\x2b\x6e\x41\x5d\x6a\x8f\x3d\xa7\xd9\x62\xec\x59\xe5\x8a\xc9\x98\x4d\x63\x7f\xcc\x26\x71\x30\x62\xd5\x38\x1c\x33\x0a\x1c\x8d\x79\x0e\x1c\x8f\xd8\x76\x33\xee\x59\xc5\x88\x93\x31\x03\xc2\x6c\xc4\x3f\xe2\x74\xcc\x83\xe0\xcc\xe9\xa1\x70\x3e\xe6\x61\x30\xb7\x0f\xce\xc5\x88\x87\x80\xf8\xc0\x2d\x2b\x3c\xa2\x69\x98\x8c\x58\x3a\xf6\xc7\x8c\x19\x07\x63\xc6\x8a\xc3\x31\x57\x15\xd9\x5d\x11\x8e\xc7\x9c\x05\xa6\x6e\x73\x49\xc6\x0c\x1e\x33\xab\xb3\xc0\xe9\x98\x2d\xe3\x6c\xc4\x5d\xe0\xdc\xe9\x2c\x31\x1f\x73\x65\xb8\xe8\x39\x9c\x5d\xa2\x02\x45\x36\x35\x79\x91\x1a\xa6\x29\x2e\x90\x6d\x89\xb9\xcf\x7e\x5b\x4e\x4c\xb0\x83\x96\x23\x46\xf8\xa1\xde\x1f\x53\x54\xd0\x94\x0e\x61\xc7\x1d\x85\xb6\x8e\x8a\xc6\x68\x40\x23\x6a\x08\x98\xd5\x68\x8d\x24\xa7\x4a\x41\x4d\x11\x80\xc6\xab\x61\x79\xae\x81\x1d\x96\xf2\xa6\xaf\xc3\xb2\xa2\xc3\x65\x53\x4f\x9d\x42\xc2\xd4\x2d\x24\x42\x2d\x3d\xf2\xa9\x4b\x3a\x01\x75\xf5\x27\xa4\x6e\xad\x8b\xa8\x5b\x33\x62\x6a\xe7\x07\xa5\x2e\xbd\x48\xa8\x5d\x9f\x19\x75\x8b\x3e\xa5\x6e\x19\x66\xd4\xa2\x53\x39\x75\x8b\x88\x53\x97\x4e\x15\xd4\xad\xca\xd8\x1b\xb1\x23\x8c\x47\x94\x0f\x93\x11\x4b\xc5\xbe\x43\x01\x71\xe0\xb4\x53\x1c\x8e\x98\x22\x8e\xbc\x11\x1f\x14\x3b\x6d\xae\x89\x60\x2d\xb4\x27\x56\xaf\xcd\x6c\xd6\x8a\xd3\x11\xd7\x86\x33\x87\x5f\xc4\xf9\x88\x0f\xc1\x7c\xc4\x66\x71\xe1\x74\x6e\x62\x44\xb7\x10\x8e\x9d\xaa\x84\x89\xd3\x68\xb1\x3f\x62\x97\x38\x18\x31\x4c\x1c\x3a\x2c\x13\x47\x23\xbe\x06\xc7\xa3\xce\x6a\xc4\x92\x70\x32\x62\xa3\x98\x39\x1c\x00\x4e\x9d\x5e\x0b\x67\x4e\xd7\x82\x73\x9b\xfd\x63\x3e\x66\xc2\x45\xd7\xf5\xec\x3e\x74\x1b\x74\xa4\x26\x35\xf0\xb0\x61\xe8\x56\xa1\x86\x61\xd0\x56\x40\x4d\xcd\x82\x26\xc8\x31\x95\x86\x96\xee\x47\x12\xa4\x61\x8c\x6e\x43\xa6\x61\x29\xd5\x3a\x60\x1a\xa6\x9b\xbe\x0f\x9b\x32\x4d\xc9\x87\xa5\xa9\xd6\x09\xd3\x54\x5d\x8b\xe3\x0c\xc3\xb4\xe4\xdb\x10\x2a\x6f\xf9\x66\x9a\xa4\x6b\x91\xef\xb0\xa7\x2e\x36\x60\x6a\x66\x2a\xa1\x2e\xf9\xfa\xd4\xd5\xc7\x80\x3a\x14\x27\xa4\x2e\xe6\x45\xd4\xd5\x93\x98\xda\xd8\x43\xa9\x43\xad\x12\xea\x12\x35\xa3\x2e\x89\xa4\xd4\xa1\x08\x19\xb5\xa9\x79\x4e\x5d\x9a\xcc\xa9\x59\x63\x0b\xea\x10\x32\xf6\x9c\x52\xc6\xd8\x69\xae\xc4\x69\xaf\xd8\x77\xda\x0a\x0e\x5c\xe6\x80\x43\xa7\x29\xe1\xc8\x69\x10\x38\x76\x79\x04\x35\xde\x18\x8b\x12\xa7\xb7\xc0\xcc\x65\x31\x38\xb5\x38\x0d\x9c\xd9\x9c\x6c\xee\xb4\x5c\xcc\x9d\x4e\x01\x17\x56\x8f\x88\x3d\xa7\xd4\xb1\xd3\x10\x31\x71\x5b\xb7\x6f\xd1\x34\x1c\x38\x0d\x0d\x87\x2e\x13\xc6\x91\xd5\x0e\x71\xec\xf4\x0c\x98\x3a\xad\x1f\x27\x4e\x5b\xc4\xcc\xe2\xac\x70\xea\x34\x37\x9c\xb9\xbc\x03\xce\xad\x56\x8c\xb9\xd3\x73\xe0\x42\x73\x0e\xbb\x8c\xa9\x54\x0c\xf0\xc4\x00\xb0\x61\xce\xd0\x1f\xdf\x6d\x37\x37\x86\xee\x58\xb6\x1b\x3a\x62\x05\xcf\x50\x14\x4a\x78\xc4\x48\x47\xd4\x14\x9a\x9c\xb0\xa2\xc4\x3c\xce\x50\xcf\x4c\x7f\xd2\xf4\xdb\xe4\x82\x25\x9d\xa6\xa2\xb4\x01\x6a\xa0\x33\xbb\x2b\x2f\x7b\x0c\xdd\xaf\x59\x4f\x78\xc3\x44\x43\x9b\x42\x11\x61\x28\xaa\x37\x95\xac\x3d\x97\xc5\xd8\xc5\x53\x55\x87\xb8\xe4\xaf\xea\xf8\x2e\x59\xab\xdf\x03\x17\xb3\x55\x9d\xd0\xce\x56\x55\x23\x1a\xed\x73\x6c\x51\x2d\x55\x4c\x5d\x1c\x55\x75\x12\x9b\x94\x54\x39\xb3\x6b\xa9\xaa\x91\xba\xf4\x51\xd5\xc9\xcc\x22\x57\xa5\xb9\x4b\x8d\x54\x1d\xee\x52\x51\x55\xa7\xb0\x5b\x68\x1d\x11\x1b\x0d\x1b\xbb\x7a\x80\x89\x85\xc9\xd8\xb7\x69\x1c\x0e\x5c\xc4\xe2\xd0\x25\x16\x1c\xb9\x98\x81\x63\x47\x17\x6d\xfe\x37\xb1\x8b\x10\x33\x97\xa6\xe2\xd4\xe9\x0f\x33\x97\x45\xe1\xdc\xae\xdf\x98\xdb\x94\x0e\x17\xe3\xd6\xd5\x4e\x6e\xac\x35\xb0\xdb\x17\x60\x32\xae\x70\xd8\x1f\xb3\x3e\x1c\x38\xad\x0f\x87\xe3\x4e\xa0\x16\xb6\xb3\xbb\xf1\xb8\x53\xc2\x74\xdc\xb9\xe1\x64\xdc\x1b\xd4\xea\xe0\xb2\x32\xa9\x14\xd6\xd2\x6c\xcc\xad\x49\xc5\x70\xd0\xc9\xc7\x3c\x4e\xad\x24\x80\x45\x1b\xd9\xe5\x47\x3d\xaf\xc1\x53\xb6\x7e\xbf\x46\xd5\x8c\x55\x68\xcd\xe7\x3c\xab\x20\x1f\xd1\xcb\x6f\x9e\xbd\x46\xe5\xe2\xac\x7e\x26\xa2\xc9\x68\xf0\xf4\xc1\xcb\xde\xc3\xc5\xed\xc5\xc4\x29\x6a\x0f\xfe\xc3\x03\x8a\xea\x0b\x7c\x56\x5f\xa6\x7a\x43\x4f\xfd\x2a\x2b\xc8\x2f\xf5\x67\xf1\x65\xaa\xf5\xa7\x4f\xb9\x96\x55\xe9\xdb\x47\x2f\x65\x62\x2c\x24\x13\xbf\xb8\xdf\xa8\x12\xb5\x9b\x07\xaa\xe4\x17\x2d\x4b\xca\x55\x9f\xa8\x72\xa7\xd6\x7b\xcf\x2f\x9b\x14\x60\xef\xf9\xa5\x21\xf5\xdd\x7b\x7e\x59\xe7\xd5\x7b\xcf\x2f\xcd\x69\xf5\x04\x0e\x29\xa2\x30\x42\x69\x59\xad\x11\xcb\xb2\xe5\x2a\x2f\x17\x27\xa8\x5a\xa2\xe7\x0f\xb1\x11\xee\x37\x25\xa4\x02\x7a\xd3\xcf\x81\x6c\x7a\x3b\x24\x8c\xec\x6f\x87\xb4\xe0\x9e\x2f\x05\xc0\xe7\x0f\xf1\x9b\xf2\x2d\xba\x83\xb0\x21\x47\xa9\xc2\x2b\xd3\xf3\x4f\xea\xde\xbd\x69\xdb\xab\x74\x7c\xe2\x3f\x13\x1f\xa3\x3b\x1a\x68\xc8\xc3\xb7\x87\x6e\x0e\x00\x1b\x12\x96\x3e\x58\xaf\xf9\x69\x3a\xe7\x08\x47\x68\x7d\x9e\xbe\xe7\x97\x06\xf6\xaf\xcf\xd3\xef\xf9\xe5\xba\x11\x41\xfb\xdd\xce\x94\xc5\x4b\xa8\x24\x59\x53\x7f\xb9\x8f\x70\xd4\x7c\xb3\x3f\xb1\xf2\x10\x32\x4e\x29\x7a\xcc\x8c\x5c\xd7\xd0\x15\x2d\x6f\x14\xd0\xb7\x8a\x28\x23\x5c\xf7\xd3\x2d\x69\x59\xbd\x84\xac\x28\x47\x5a\x12\x94\x06\xae\x0d\xa4\x54\xa8\x80\x1a\x15\x8a\x0c\xdb\x98\xb4\x86\x04\x76\xad\xe9\xe2\x29\x56\xcb\x53\x70\x30\x73\x5e\x54\x88\x50\xb0\x0c\x81\xd9\xdc\x50\x32\xe7\xcd\xa4\x44\x87\xf2\x6d\x08\x0f\x12\x38\xd6\xca\x35\x99\x3c\x7f\x48\x94\x0e\xee\xa1\xfd\x86\x03\x7b\xe8\x6f\x88\xd0\xb7\x90\xe3\x11\x74\xab\x44\x7f\x83\x37\x2e\xb6\x26\x6f\x55\x9e\xcc\xb6\xa7\x2f\x80\xf4\x9d\x2d\x91\x7b\x1d\x2a\x09\x85\x62\x49\x2b\xda\x47\x24\xb0\x10\xbc\x67\xa0\x78\x80\xd6\x94\xd9\x5f\x74\xa0\x5c\x64\x1c\x71\x96\xcd\x94\xda\xa1\x72\x8d\xd8\xd9\xd9\xbc\xe4\xb9\x90\x25\x5b\x20\xbe\x39\x63\x8b\x9c\xe7\x75\x5e\x46\x70\xef\x53\x23\x34\xc1\x02\x05\x26\x63\x0b\x94\x72\x94\xae\x96\xef\xf9\x02\x95\x8b\x6a\x89\xa8\x4c\x0a\xbc\x46\xeb\x8c\xcd\x25\x78\x09\x72\x6d\x86\x76\x31\x2b\xb3\x19\x62\xf3\xf9\xf2\x62\x0d\xa0\x05\xdc\x6a\x29\xc0\x9e\xaf\x79\x8e\x2e\xca\x6a\xb6\x3c\xaf\x24\x81\xeb\x72\xb9\x18\x42\x51\x8c\x86\xf4\x9a\x93\xf6\xcb\xfd\xfb\xea\x59\x99\xf6\x27\xe1\x50\x7c\x6c\xe2\x5c\x47\x73\xb1\xd4\xdc\xd8\xad\xb8\x0a\x2c\x38\xb1\xf6\x33\xf8\xac\x49\x29\x85\x78\x1b\x09\xe9\xfb\x66\x51\xd9\xfa\x11\xeb\xfd\x88\xdf\xaa\xc4\x9e\xbf\xe9\x3f\xc1\xa3\x00\x83\xa7\x76\x0c\x1e\xf0\xa1\x4c\x7c\x89\xca\xc5\x07\xbe\x5a\x73\xbb\x17\x2c\x17\x1f\x5e\xf6\x1c\x61\xe7\xa7\xad\x06\x08\xec\x18\x20\x5a\x68\x3a\xc7\xd6\x6f\x70\x28\x14\xba\x0f\xfd\x63\x67\xc1\xa1\xfd\xc2\x17\xd9\xea\xf2\xac\xda\xe1\x29\x40\x95\xb1\x76\xf9\xb0\x69\xd7\x56\x9e\x76\x5d\xbe\x35\x85\x6e\xce\x3f\x07\xd6\x96\x23\xae\xdc\xbd\x0f\xdd\x98\xa7\x35\x23\x4d\x41\xc7\x7f\xf0\x4a\x8f\xd3\xba\xc4\xcd\x01\xa8\xf6\x34\x56\x5f\x06\xb2\xda\xaa\x5f\x0d\x5e\xce\x32\x44\x1f\xdf\x2d\xca\xaa\x64\x73\x3d\xf5\x55\xb7\x0e\xdf\x64\x33\xb6\x38\xe1\x4f\x5e\xb4\x69\x51\x65\xe6\x31\x6f\xe3\x15\xf2\x7f\x7d\x95\x36\xb7\x91\xef\x53\xc3\x8c\xb5\x28\xac\x6d\x5e\x3c\xd1\xdb\x10\xc0\xe3\xab\xbf\xed\xda\x50\x49\x9b\x57\x14\xe2\xff\x5b\xd2\x06\x6d\x42\xf5\x67\xcc\x4c\xeb\x7a\xaa\x4d\xa6\x0f\x03\x8b\x92\x1f\xa5\x55\xc1\xe7\xf1\x67\xdb\x0c\x23\x91\x31\x9e\x00\x70\xb6\x67\x2f\x1a\xc5\xd0\xf5\xc4\x52\x77\xd5\xad\xbb\x52\x75\x8d\x44\x3e\xe6\xe5\xba\xe2\xf3\x46\x8b\xcd\x10\x0b\xe8\xfc\x76\xa1\x05\x75\x3b\xe8\x42\x0c\xb4\x32\xd5\xda\x9b\xf2\xed\x9b\xc9\x44\x51\xfb\xae\x75\xd7\x22\x90\x6c\xa6\x2e\xf0\x1d\xd2\x6a\x9b\x58\x63\x70\xd8\x3d\x43\x5a\xd9\x38\xd5\xb3\xa4\x79\x4d\x46\x31\xee\xc0\xff\xbe\xc8\x97\x68\x7d\xc1\xce\x64\xf8\x31\x67\xeb\x4a\x2a\xc3\xd0\x85\x57\x6e\x91\xf5\x88\xed\x0a\xcc\x65\xf8\x95\x41\x87\x21\xa3\xf8\xae\xa6\x3e\x30\x8d\x6b\x33\xc1\xab\x98\xfa\x55\x5c\xca\x88\xeb\x32\xcc\xc8\x2a\xb4\x3c\xaf\x06\x1e\xb8\x71\xb9\x6e\x91\x75\x5c\xae\x5d\x66\x9d\x21\xe3\x3d\xbf\x94\x29\xa0\xa3\xe0\xd0\x27\x7a\x49\xf9\xc1\x52\xa0\xe5\x8d\x8e\x8c\x59\xa3\x0f\xd1\x4b\xa1\x81\x6a\x12\xb0\x5a\xae\xd7\x6d\x98\x0e\x39\x0f\x21\x20\x86\x69\xa9\x6c\xd1\x0c\x54\x2d\xe3\x26\xf5\x78\x75\xca\xd6\xef\x3b\x26\x5b\xeb\xee\x64\xd2\x51\x51\x61\x88\xf5\xe8\xfa\xae\xd3\x75\x61\xb4\x02\x8a\xc6\x82\x8e\xca\xbe\x03\x9d\xfd\xca\xa8\xf8\xa2\x4c\x44\x54\x12\xb2\xaa\x55\xdb\xdd\x80\xec\x17\x4f\xb6\x27\x7b\x65\x27\x7b\xee\x26\x7b\xee\x20\x7b\xb5\x05\xd9\xce\x24\xd2\xeb\x3a\x8b\xb4\x5c\xfe\xd8\x2e\x8f\xf4\x58\x12\x66\x09\xab\xe2\x9b\x4a\x4f\xc5\xfc\xed\xa3\x97\x07\x2a\x40\xeb\xe4\x62\x9e\xa2\xac\x38\x31\x24\xd7\x3e\x9b\x33\x41\xc4\xa6\x42\x7d\x28\x2a\xe0\x9a\xb4\x78\x4c\x80\x9a\xcc\xce\xc3\x85\x9a\x6e\xd2\xed\x6f\x1f\xbd\x34\x66\xdc\x7e\xb5\x2a\xcf\xe6\xfc\xce\x6e\x4b\x44\xb2\x51\x67\xa1\x48\xff\xe9\xcf\xb3\x5c\xa4\x16\x22\x04\xd9\x25\x64\x28\xcd\xfa\xcf\x03\xa9\x28\x96\xaf\x31\x3a\x12\xf5\x0e\x24\x57\x1f\x49\x19\x2f\x57\x93\xf6\x9d\x75\xf5\x70\x7c\x8d\xfa\x60\x3d\x2f\x33\x3e\xf1\xa6\x88\xec\x0d\xde\xc2\x68\xc0\x92\x2b\x82\x25\x53\x14\x38\xc0\xfa\x57\x04\x1b\x4c\x51\xb4\x67\x7f\x48\xe3\xca\x73\x0f\xbe\xc6\x07\x7a\x63\xad\x85\x95\x33\x07\xfa\x9c\x63\x8b\x06\xfe\x16\x18\xae\x67\x4e\x23\x70\xed\x48\x1c\xd9\xb5\xfb\x78\x0b\x0c\xe6\x51\x0f\x27\xe4\xda\x86\xbd\x7f\x12\xb7\xda\x78\x97\x6b\x70\xae\x2d\xac\x1d\x5d\xac\xcd\xc5\x75\x1d\x6d\x53\xcb\x99\x3f\xbf\xa9\xd5\x4b\xa1\xaf\x25\x66\xbf\x1b\x92\x69\x2f\xab\xbe\x96\xdc\xfd\x6e\x18\x4c\xdb\xac\xee\x77\xc3\x68\xaa\x92\xbd\xdf\x8d\xf0\xc7\xb7\x53\x1a\x7c\x52\xc2\xfd\x3f\x32\xd3\xfe\x67\xcb\x87\xff\xdf\x93\xd9\x1e\x5e\x2a\x28\x17\x3c\xbf\xde\x14\xf7\xdf\xb0\x35\x6f\xb3\xd6\xb3\x35\xd7\xca\x5e\xfb\xc4\x99\x01\x7f\x68\xcb\x9b\x28\x40\x0b\x76\xca\xd7\x67\xba\x95\x1e\xea\x64\x88\x2a\x82\x0c\xf9\xdf\x5f\x3f\x9a\xc0\x3c\x40\x51\xd0\x3c\x61\x63\x02\xf3\x3a\x0a\x04\x1d\x40\xd4\x26\x0a\x0e\xd4\x17\x41\xbf\x21\x32\x68\x41\x4b\xf0\x6a\x39\xa5\xfc\x85\xaf\x11\x43\x0b\x7e\x31\xbf\x44\xd2\xd6\x72\x13\x62\xdd\xa1\xa0\xce\x6b\x1e\x8b\xf3\xd3\x94\xaf\x3e\x22\x78\x55\x0a\x5e\x55\x11\x1f\x7c\x02\xe1\xfc\x81\xb3\xc9\x7c\x79\x01\x2d\xc4\x7f\x4d\x0d\xba\x8d\xbb\xde\x6d\x58\xa1\xe6\xcb\xa6\xe5\x4b\xed\x11\x6a\xf6\xd4\x03\xb3\xdc\xfd\xf3\x88\xe7\xc3\xac\x2c\xf0\x42\x2f\xf2\xba\xeb\x9d\x35\xa7\xc1\xc5\x2f\xca\x4e\x44\x25\x7a\x38\x15\x54\x9b\xc7\x30\xf5\xbe\x96\xe1\x55\x4f\x28\x16\xbd\x3d\x42\xdd\xd7\xb7\xf5\x99\x79\x5f\x52\xdf\x94\xd5\x45\xb9\xe6\xe8\x87\x67\xaf\xd6\x00\x61\x4c\x30\xf5\x43\x29\x4a\x41\x3e\xa2\x07\x42\xbe\x82\x2f\x77\x80\x31\x6a\x24\x61\x45\xc5\x57\x68\xc1\x4f\x58\x55\x2e\x4e\xae\x81\xf1\x00\x8a\x0b\xc6\x2b\x11\x1c\x2c\x96\xd5\xc4\xca\xd5\xc3\x43\xb4\x58\x8e\x46\xaa\xf0\x26\x8b\x64\xe8\xef\x0d\x77\xef\x19\xab\x49\xc6\xfe\x5e\x33\xd9\x10\x92\x2a\xce\x28\xc6\xd4\xda\xd0\x8a\xf3\x5e\x87\xba\x4e\x04\x60\x93\xca\x83\x1f\xbe\xd5\xa4\x02\xdb\x09\x30\x6e\x9f\xb1\x35\x6c\x2f\x6c\x65\x43\x8d\xa4\x00\x86\x30\x89\x46\x58\xd5\x52\xa0\xa8\xe1\x5e\xb3\xf0\x1f\xfc\xf0\xed\xf5\x88\x5e\xee\xed\xb4\x82\x67\x8b\x7c\xc2\x16\xcb\x6a\xc6\x57\x8a\x10\x97\x1a\xb0\x45\xae\xab\x81\xe8\xe1\x88\x2a\xb4\x76\x76\x53\x32\x64\x4c\x2b\x1a\xcb\x53\xf5\xff\x30\xfd\x78\xf6\xe2\x73\xab\xc7\xb3\x17\x9f\x49\x3b\x9e\xbd\xb8\x1e\xe5\x58\xae\x3a\xba\xb1\x5c\xed\xa0\x1a\xcb\xd5\x95\x35\xe3\xb7\x1d\x35\xe3\xb7\x3f\x58\x33\x5e\x7f\x7e\xd5\x78\xfd\xd9\x74\xe3\xf5\x75\x29\xc7\xa6\xa7\x1d\x9b\x9d\xd4\x63\xf3\x09\xfa\xf1\x6e\x47\xfd\x78\xf7\x07\xe9\x07\x6c\xca\xeb\x9a\xb1\x90\x2b\xa3\x6a\x42\x38\xe7\x45\xb5\x7d\x54\xb6\x00\x9d\x90\xdf\xd0\xb2\x68\x20\xc1\x13\x36\xd7\xa5\x0c\x00\xec\x7a\xd4\x01\x40\x75\x14\x02\x7e\x79\x32\x21\xa1\x4b\x0f\x64\x25\x5d\x15\x16\x26\x3d\x10\x53\xa0\x05\xba\x8f\x7c\x62\xdb\xe9\xd2\x34\x65\xd2\xaa\xca\xfd\xfb\x68\x01\x5b\xe4\x8d\x32\xc8\xa3\x43\x04\xdd\x41\x0b\xe3\x63\xf5\x66\x15\x12\x70\x86\xba\xf6\x11\xd5\x93\x27\x37\x41\x3a\x98\xc9\x02\xdd\x31\xbc\x18\x3a\x40\xdd\xdf\xea\x12\xe8\xfe\x3b\xb5\x17\x96\xf2\xff\xed\xd4\xf7\xc5\xc4\x3e\xb9\xa8\xb5\xf7\xc5\x35\x69\xaf\x94\x7b\x57\x53\x35\xe5\xad\xf5\x79\x0b\xe5\x1d\x78\x4c\x00\x75\x05\xfd\xd5\xac\xa0\x81\x33\xae\xc0\x0a\xfd\x1f\xae\xc1\x2f\x96\x15\xab\xf8\xe7\x76\xc0\x2b\xc0\x72\x5d\x2a\x0c\xd0\xae\x47\x85\x25\x61\xba\x0a\xaf\x96\xa3\xfe\x57\x54\x19\xd5\x5f\xd5\x23\xd0\x03\xe5\xd5\x17\x7b\x22\x1c\x6c\x7f\x79\x31\x89\x82\x81\x5a\x7e\xaa\xc0\xae\xc9\xe7\xfc\xb9\x24\x36\xe2\x72\x44\x8d\xdd\x05\xf6\x62\x20\xb0\x27\x57\x11\xd8\x83\x3c\xff\xdc\x91\x2f\xcb\xf3\xcf\x14\xf9\xca\x27\xbf\xaf\x63\xce\x9c\xf7\xe6\xcc\xf9\x4e\x73\xe6\x7c\xeb\x39\x73\x7f\x44\xd8\x6f\x02\x59\x38\x30\x6a\x0e\x7e\x33\xb6\x5a\x5d\x8a\x66\xf5\x18\x22\x1f\x86\xef\x0c\x2b\xed\xf3\xf0\x66\x18\xc3\x40\x6a\xbf\x8d\xb9\xd1\xbe\xc4\xa1\x68\xf8\x54\x8f\x2e\xbf\x99\x77\x57\x1e\x2c\xd4\x13\xe0\xcb\x42\x5f\xdb\x5c\x9b\x5e\x38\x5e\x2d\xcf\xf8\xaa\xba\x44\xbf\xaa\x27\x86\xa1\x22\xa8\x57\x03\x62\xb0\xac\xa8\x14\x64\x7d\x60\x82\x53\xbb\x95\xe6\x4d\xf4\xae\x77\x59\x97\x27\x8b\xb2\x28\x33\xb6\xa8\x50\x0a\xe5\xe5\x42\xb3\x0d\x40\xea\x58\xfd\x6d\xd7\xa5\x6b\x62\xea\x5f\xae\x61\x1d\x78\x48\x81\xdd\x1c\x3b\xec\x9a\x3c\x3b\x13\x6a\xc9\xe6\x7b\x1d\xde\x8f\x32\x0e\x19\x1d\x72\xc3\x39\x0d\xec\x56\x4c\xe4\x5d\x31\x7f\x82\xad\x5e\xe8\xac\xee\xf7\xa2\xb3\xe7\xdb\xb5\xd9\x4f\x04\xf6\x66\xd0\x5e\xfc\xed\xba\xac\x3d\xdd\x15\x0a\xa6\x38\xc1\x0c\xa7\x70\xa7\x26\xc3\x39\xe6\xb8\xd8\x1b\x00\x79\xfb\x6f\xd4\xd5\x29\xc2\xde\xd6\xdb\x03\xa0\x74\xd3\x46\x6d\x07\x6e\xf9\x42\x1d\x9e\x00\xb7\x58\x7f\x91\xff\xfd\xed\x37\xc3\x05\x0c\x11\xf7\x37\x36\xf0\x97\x23\x34\xdc\x05\xd3\xff\xe4\xd8\x5c\x57\x3f\x6a\xc8\xe8\x9f\x05\xb4\x06\xed\x7d\x00\xd2\x86\xe6\x7c\x71\x52\xcd\xd0\x6d\x44\xb7\x3c\x4a\xdd\x77\x34\x0f\x97\x8b\x0f\x7c\x55\x4f\x0d\x35\x37\xac\xfc\x83\x18\xb4\xeb\xdb\x01\x5b\x39\x9e\x7a\xd4\x6e\xa4\xdb\xd9\x99\xfb\x88\x5e\x75\x9d\xe8\xad\x35\xca\x59\xc5\x10\x5b\xef\x88\x67\xeb\x95\xac\xee\x4e\xe1\x46\x73\xd0\x07\xd5\xf2\xb5\x4f\xec\x5b\x21\x50\xfc\x09\x67\x76\x14\xae\xae\x52\x19\x4e\xee\xd4\xf5\x9e\x48\x61\x36\x44\xd6\xe2\x35\x9d\xe2\x91\x62\x33\xc0\x92\xdd\xdd\xfa\xf0\x7e\x17\xb7\xfb\xa6\x57\xbb\x85\x57\xb7\x7a\x33\x38\xc2\x2f\xfe\x6a\x1a\x0e\xce\xce\xd7\xb3\x49\x1d\x48\x89\x18\xc1\x34\xaf\x34\xd7\xee\xc5\x12\xc8\x70\x4e\xb6\x0e\x45\x34\x01\xd7\x1e\xa4\x86\x39\xed\x9a\x8d\xf5\x20\xc9\xc0\x2a\x00\x8c\x50\xc9\x6c\x79\x06\x83\xa4\x65\xec\x47\xa3\x61\x6b\xa3\xf6\x1c\x65\xf3\xe5\xc2\x35\x53\xd9\x56\xa5\x01\x4e\x5f\x97\xe1\x47\xbb\x2e\x43\xb1\x53\x97\x75\xc8\x10\xa5\x48\x72\x9b\x93\xaf\xa6\x93\xae\x0f\xa1\xfe\x5f\x41\xb1\xff\x2a\x39\x33\x04\x5a\xfb\x52\x09\x6f\xe8\x66\xeb\x53\x63\x76\x04\x70\x87\xa9\xde\x58\x97\xc1\x89\x05\x4d\x63\x42\x17\x1d\xfb\x19\x35\x83\x8b\x6d\x6c\xe0\x42\xa9\x7c\x0d\xfe\x4d\xf9\xd6\xc4\x76\xbb\xaa\x42\xe5\xce\xfe\x72\x13\x1e\x5b\xcf\xcd\xf4\x4e\xcb\xa8\xa3\x31\x1f\xdf\x4e\x69\xb8\xcd\x79\x97\xc3\xdb\x7f\x41\xb3\xaa\x3a\x5b\xdf\x3d\x3c\x3c\xad\x66\xeb\x83\x94\x1f\x9e\x57\x05\xfd\x79\x8d\x3e\x90\x03\x7c\x40\x50\x7a\x89\xfe\xc7\x29\xab\x66\x25\x5b\x0b\x8d\x69\x0f\xc8\xc0\xa9\x10\x79\xd8\xe3\xf0\x10\x7d\xcb\x2b\x79\x1d\x8e\x73\xc1\xee\x92\xa5\x73\xbe\x46\xff\x50\x98\xfe\x71\xe3\x2b\x38\xc6\xbf\xe2\xfc\x51\x73\xfe\x65\x70\x92\x06\xdd\x92\xc2\xbb\x85\x6e\xde\xac\x7f\xbe\x67\x07\x8f\xfe\x21\xbb\xa3\x01\x7f\x0a\x3f\xb4\xb0\x4f\xd5\xf7\x2e\x68\xf5\xeb\xcd\x9b\x86\xf3\x39\x47\x1d\x22\x9b\xca\x4e\x32\x4e\xe0\xe4\xcc\x3f\xa6\xf2\x34\xfe\x0f\xcb\x9c\x1f\xfc\xbc\x46\xcb\x15\xfa\x46\x1e\xa5\x29\x8b\x92\xe7\x28\x5b\xe6\x7c\x0a\x50\xd8\x22\x47\xe7\x6b\x8e\xca\x4a\x8c\x6b\xff\x10\x7c\xd4\xfa\xa0\xce\xe1\x34\x7d\x38\x51\xdf\xbb\x7d\x90\xbf\xde\x93\x67\x92\xda\x66\x07\x4d\xed\x23\x1d\xd8\x6f\xbf\x69\xdf\x0e\x2e\xca\x45\x2e\x66\x97\x9d\x3a\xf2\xe8\x90\xa0\x05\xe9\x3f\xc3\x61\x9f\x1b\x5f\x1d\xde\xbe\x73\x6d\x7f\xb7\x0f\x6f\xc8\xde\xae\xab\x55\xb9\x38\x79\xbc\x5a\x9e\x3e\x9c\xb1\xd5\xc3\x65\x2e\x24\xf7\x12\x7e\x3c\x28\xb4\x5f\x15\xf3\x5f\xb1\xf7\x7c\x21\x79\xdc\x57\xd9\xb3\xf3\xc5\xa5\xe0\xef\x8d\xaf\x1a\x0f\x76\x9e\xad\x49\xce\xc5\x8f\x13\x89\x47\x76\x10\xb6\x36\xe1\xf0\x7d\x3d\x04\xc2\x4f\xd9\xf2\x7c\x51\xf1\x95\x5a\xb9\x84\x9f\xe6\xb5\xaf\x90\xcd\x5b\x67\x01\xa5\x70\x9f\xb1\xfe\xc2\x37\xd5\x8a\x89\x2f\x17\xb3\x72\xce\xd1\xa4\x86\x76\x5f\x01\x91\xa8\xbf\x82\x36\x2d\xc0\x4c\x75\xef\x41\x55\x37\xd8\xdf\x17\xa6\xfe\x15\xc8\x54\x56\xfe\xfa\x08\x79\x9b\x6f\xa9\xe7\x09\x99\xcb\x9f\xee\xc3\x4f\xdf\x3c\x7e\x2c\x7e\xb2\x60\x12\xec\x82\xe9\xfa\xfa\x7c\xb5\x5a\x9e\xb0\x8a\x4f\x41\xeb\xaa\x19\x5f\x71\xb8\xe7\x89\x16\x7c\x53\x21\x41\x02\xcb\x2a\xbe\x82\x46\xd0\x8d\x6d\xe8\x03\x02\x27\xb2\xfa\x4d\xe4\x6d\x1e\x3f\xf4\xbc\x3d\xa1\xa1\xde\xe6\x5b\xf8\xf8\xab\x70\xce\xf3\xe5\x45\x8b\x1f\x9a\x7d\x25\x39\x2f\x87\xf2\x89\xea\xa2\x00\xe0\x3f\x7e\xbc\x07\x57\x33\xbd\x3d\xb4\x8f\x34\xc8\x50\xb0\x5f\x67\x1c\x52\xd8\xdb\x28\x58\x75\xf5\x7c\x71\xca\xaa\x6c\xc6\xf3\x16\xdf\x3d\xb4\x5c\xcc\x2f\x11\x3b\x3b\xe3\xd0\xef\x72\x0d\x06\x88\xce\x17\x65\x35\x15\x13\xcd\x8c\xad\x39\xcc\x36\x05\x23\x1a\x48\x4d\x1d\xc1\xa4\xaa\x3e\x17\xd5\x40\x15\x43\x3d\xd3\xbe\x9e\xb1\x72\x35\xec\x19\xf4\x4b\xd1\xfa\x95\x62\xdd\x9d\x3b\x8a\xf6\x1b\xfd\x0e\x58\x5a\x8a\x8a\xe2\xff\xca\xdf\xcb\x5a\xb5\x35\x5e\xc5\x18\xf8\x02\x8c\x01\x46\xe1\xd6\x16\x1a\x2d\x97\x71\x4b\x57\xc9\xcb\x45\xce\x37\xe8\x08\xdd\xc1\x46\xb5\x6f\xec\xe8\xd6\x2d\x4d\xf9\xf7\xf7\x65\x33\x8b\xf2\x03\x9e\x37\x50\xe5\x6d\x5f\xd9\x85\x2a\x3d\x16\x12\x97\x9c\x91\xbf\xde\x39\xaa\xc5\x7f\x4f\xe3\x17\xda\x3f\x32\xf8\x8f\x1a\xd0\xd7\x5f\x23\xec\xd5\x0a\x84\x7e\x53\x36\xa4\x44\x52\x53\x22\x95\x15\xfd\x86\x3a\x7a\xd8\x30\x7f\x0b\x44\x00\xd0\x26\xa4\x86\xf9\xd9\x8c\x67\xef\x5f\x66\x6c\xce\x56\xff\x4b\xb4\x9a\x08\x39\x3c\x5f\x96\x0b\x79\x9a\x1a\x18\xd0\xfc\xd4\xb5\xf8\xf6\x67\x69\xf5\x2d\x73\xaa\xd9\x6a\x79\x81\x1e\xad\x56\xcb\xd5\x04\x7a\x75\xeb\x89\x08\x85\x5a\xd5\xfc\xfb\xfe\x2d\xb4\xdf\x02\x38\xa8\x96\xd2\xb3\x4e\x70\xb4\x77\x50\x2d\xff\x7e\x76\xc6\x57\x0f\xd9\x9a\x4f\xf6\xd0\xbe\x04\x20\x54\x7e\xb1\xac\x84\x82\x03\xb1\x92\x2f\xb7\x44\x61\xdd\xd1\x8f\x9f\x61\x24\x68\xf9\x04\x51\xb5\x88\xc4\x5b\x76\x4c\xe5\x36\x9b\x1a\x9c\x24\x97\x0d\xd2\x98\xe8\x0c\xfc\xba\x6e\x23\x25\x0a\x4b\x95\x1b\xea\xed\xf5\xe5\x22\x0d\xe2\x61\xdd\xd0\x24\x16\x0d\xec\x4d\xa5\x9c\x8f\x1f\x53\xe5\xeb\x94\x9b\xc3\x77\xd2\xcb\x8a\xa3\x35\xff\xaf\x73\xbe\xc8\xc0\xd1\xd9\x09\x6d\x71\xd4\xaa\x03\x03\xe1\xe5\x69\xba\x9c\x37\x86\x64\xc3\x4c\xbd\x2e\x66\x32\xc4\xdc\x40\x1a\x67\x52\x24\x19\x84\x15\x83\x1e\x7a\x0d\x49\xcd\xc1\x63\x03\x11\xe0\x86\x75\x22\xfc\x21\x11\x0e\x85\xbf\xb7\x23\x91\x98\x48\x2a\x3d\x45\xe5\x23\xaf\x03\x62\xff\xc8\xa2\x35\xd1\x16\x9d\x79\xe4\x0d\x3a\x13\x7c\x12\x47\x31\x55\xc4\xc6\x92\xd8\xc7\x5b\x12\x8b\xc9\xae\x9d\x6a\x6b\x9a\xa8\xea\x76\xb4\x6b\x01\x8d\x6e\x02\x84\xbe\x49\x88\xd0\x5f\x8d\x13\xfd\xa0\xa9\x01\x2a\x42\xf7\x61\x70\x35\x88\x9a\xda\xfa\xa3\x83\x4a\x53\xb5\xfe\x41\x08\x41\x7a\xab\x2d\x07\x97\xb6\xc7\x3a\x62\x7d\x94\xd1\x40\xee\x1f\x39\x4c\xbf\xe7\xd1\xdb\x66\x9f\x2b\x10\x6e\x78\xbf\xe2\x2c\x7f\xb8\x5c\x54\xe5\xe2\x1c\x2e\xcf\x82\xf4\x5b\x57\x24\x28\xf9\x0e\xfa\xfe\xf5\x11\x90\xf5\x50\x04\x16\x86\xd1\xe0\xd6\x77\x8b\x0f\x6c\x5e\xe6\x50\x49\x72\xfb\x96\xea\x56\xc3\xef\x2e\x16\x24\x01\xc2\x42\xc1\x9b\x06\xcf\x5b\x65\x26\xa2\x69\xf3\xe3\xfe\xbe\x08\xc6\x6b\x0f\xd5\x03\x73\x53\xba\x11\x19\x08\x0a\x2f\xf9\xab\xe6\x0c\x8d\xb5\xfd\xc7\x0d\x61\x87\x87\xe8\xbb\x02\x5d\x70\x24\xe2\xb5\xf3\x33\x24\x22\xd5\x29\x2a\xab\xff\xfb\xbf\xff\x4f\x3d\x2c\xe9\x20\x80\xe2\x1b\x96\x9e\x0f\x2a\xde\x1a\x38\x7f\xa9\xbd\x2f\xc1\x0a\x26\xad\x96\x8b\xca\x58\x57\x43\xa2\x7f\xf1\xf5\x2f\x81\x41\x7d\x87\xb2\xfa\x04\x51\x75\x21\x1d\x0d\xa5\xae\x38\x5b\xb0\x39\x5c\x7e\x68\xf8\xf8\x82\xb3\x1c\x15\xe5\x6a\x5d\xd5\x5c\x82\x6e\xed\x2e\xe6\xe1\xe8\x86\x26\x8b\xe5\x90\xbd\xeb\xbd\x5a\x27\x24\xa2\x9b\x4a\xfe\xca\xb3\x6a\xb4\x36\xfc\xad\x69\x1d\x8e\x61\x3d\x38\x8f\x6a\x85\x7a\x58\x83\x02\xb1\xa0\x23\x8b\xc1\xdc\xeb\xfb\x03\x1d\x18\x96\xd3\x0c\xc8\xb9\xd3\x48\xd7\x14\x80\x35\xda\xdb\xaa\xaf\xe6\xa3\xba\x01\xfc\x0e\x2a\x58\x87\xf5\xb2\xef\x7e\x9f\xb7\xa7\xec\x12\x95\x8b\x6c\x7e\x0e\x93\x10\x31\xb9\xd0\xa7\x34\x26\x2e\x3f\xae\xb9\xf3\x68\x07\xee\x80\x2a\x5f\x8d\x81\x9e\x9a\xa7\x11\x38\x9b\x24\x71\xe9\x0c\xf5\x6d\x0c\xf5\x20\x78\x91\x0c\x1b\x8b\x0f\x3e\x27\xcf\x87\x23\x7c\x9f\xa3\x54\x71\xf4\xf1\xf5\x72\x14\x5c\xc6\x15\x99\x1e\x03\xd3\xbd\x4d\x9f\xed\xde\xc6\x7b\xb8\x87\x7e\x03\x8e\x4c\x24\x0d\xf2\xd7\x46\x1e\x81\x55\x1e\x30\xa3\x32\xcc\x31\xb0\xa7\x4f\xc1\xcc\x92\xa8\xf9\x69\x94\xc2\xdf\x5f\x3d\xbe\x43\x51\x0e\x2b\x65\x3c\x6f\x3c\x6f\xed\x36\xd5\x0d\xac\xe6\x3b\x38\x34\xed\x3b\xf8\x9f\x7b\xbd\x98\x44\xc5\x1a\xed\x68\x2c\xe9\x6b\xe0\x75\x43\x12\xad\x5a\xed\xd5\x00\x8b\xee\x00\xb5\xa0\x44\xf3\xb1\xed\xea\x4f\x27\xdc\x69\xd7\x89\xaa\xd3\x33\x2d\x1a\x99\x54\xa7\x67\xe8\xa8\x37\x96\xec\xa1\xbf\x1c\x1d\x49\xa7\xdc\x8f\x4e\xd4\x26\x46\x75\x7a\xd6\x8f\x33\xb4\x09\x7a\x5b\x7b\xef\x73\x2e\xbe\x09\xb6\xa2\x23\x20\xf0\xd6\x07\xbe\x5a\x97\xcb\xc5\xad\xbb\xe8\x16\x2c\xfa\xde\x9a\x8a\x5f\x25\x3d\xb7\xee\x6a\x51\x21\xfc\x2e\xbb\xab\x7e\x97\x5f\x6e\x7c\xf5\x51\x2d\xd2\xbd\x5c\x9e\x72\xf4\xe0\xe9\xb7\x28\x3d\x2f\xe7\x39\x5a\x9e\x55\xe5\x69\xf9\x0b\x5f\xad\xa7\x68\x5e\xbe\xe7\x68\x75\xf0\xf3\x7a\x2a\xa7\xc4\xb0\xd2\xbe\x3e\xe3\x59\x59\x94\x99\x30\xde\xbc\x04\x81\x9f\xb1\xaa\xe2\xab\xc5\x1a\xe0\x41\xa3\x6a\xc6\x51\xb1\x9c\xcf\x97\x17\xe5\xe2\xe4\xae\x5c\xf3\x14\xea\xd7\xbb\x17\x89\x6e\xd5\x4a\x73\x4b\x2e\xee\x76\x2a\x1c\xb0\xd3\xbc\xb7\x8a\xda\x5c\x91\x14\x65\x37\xbe\x92\xe2\x52\x97\x26\x9b\x65\xee\xee\x00\x26\xfa\x0c\xb2\x03\xe1\xb4\xb3\x8b\xde\xaa\xf1\x5f\xb4\xef\x07\x8b\x65\xce\x5f\x5d\x9e\xf1\x36\x98\x6b\xd7\xaa\xd5\xc4\xa3\x5c\xe8\xeb\xc6\x2f\xca\xc5\xc9\xf2\x7f\xbe\x44\x1f\xbc\x03\x7a\xe0\xc1\xf4\xbc\x6d\xa1\xdd\x25\x6d\x88\x51\xae\xb1\x86\xc4\x56\x17\x33\x36\xef\x41\x8a\x0f\xbc\x3b\x72\x21\x66\x55\x9f\x8d\x92\xb7\x18\xd5\x6f\x33\xb6\x7e\x76\xb1\x78\x5e\x1f\x81\x39\x52\x95\x0e\xba\xbf\x43\xf5\x66\x8b\x04\xb2\xc6\x49\xa6\xd4\x1e\xa3\x5b\x5d\xee\x0f\x89\x72\xb8\x48\xbc\x27\x78\xa3\xf3\xea\xcd\x7b\x99\xc0\x50\xd4\x80\xcf\x9d\xc5\xaf\x5e\xbf\x5e\xcc\xca\xc5\x52\xf4\x8a\xa1\x0b\x9e\x22\x75\x51\x55\xad\x5a\x1f\x28\x85\x56\x3c\xf9\x78\x43\x5d\x51\x85\x6d\x93\x8f\xd3\x5f\x3f\xbe\x9d\xd2\x68\x9b\x2d\x91\xc1\x8d\xdd\xd7\x4f\x9f\x1c\x57\xd5\xd9\x0b\x31\x64\xac\xab\x06\xda\x5f\xd3\xf2\x44\x1e\x66\x39\xf8\x79\xfd\xd7\x6d\x20\xdf\x3a\x5f\x73\x98\xb0\x65\xd5\xad\x7b\x37\x86\x88\xbe\x29\x4f\x7e\x00\x80\xf7\x44\x87\x7f\x5e\xcf\x84\x53\x2e\x4f\x16\xcb\x15\xbf\x3b\x2f\x17\xfc\x46\x83\xfa\x82\xa7\xfe\x56\x28\x85\x90\x7e\xe4\xa9\x1c\x9b\xe4\x35\xe3\x5b\x07\x87\xf3\x32\x3d\x14\x20\x84\x73\xbe\x71\x78\x88\xf2\xe5\xa2\x42\xcb\x0f\x7c\xb5\x2a\x73\x5e\x6f\x38\xd4\xfb\x1b\x37\xb4\x2b\xc8\x6a\xe7\x40\x38\xb8\x5b\xcd\x81\x06\xd8\x8f\xe8\x54\x38\x90\x28\xbb\xb5\x84\x82\xc0\x36\x99\x5e\x05\x88\xbb\x77\xe3\xa3\x81\x1b\xb2\x44\x6d\x6c\xd5\x14\xff\xf5\x2e\x21\x1f\xdf\x0a\x2e\x4c\xdf\x48\x2e\xbc\xdd\xbb\x71\x78\xf8\xff\xa1\xf5\xf2\x7c\x95\xf1\xa7\xec\xec\xac\x5c\x9c\xfc\xfd\xc5\x93\x23\x51\x78\x67\x0e\x87\x48\x7f\x5e\x1f\x9c\xb2\xb3\x1b\xff\x2f\x00\x00\xff\xff\x22\xa6\xc9\x06\x5b\x2c\x06\x00") +var _web3Js = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xfd\x6b\x7b\x13\x39\xd2\x38\x0e\xbf\xcf\xa7\x50\xfc\xdc\x0f\xb6\x89\xb1\x9d\x84\x61\x18\x67\x32\x6c\x08\x30\x64\xef\x81\x70\x01\xd9\xd9\xbd\xb3\x59\xae\x8e\x5b\xb6\x7b\x68\x77\xfb\xd7\xdd\xce\x61\x48\xbe\xfb\xff\x52\xe9\x54\x3a\xf4\xc1\x49\x98\xd3\x26\x2f\xc0\x2d\x95\x4e\xa5\x52\xa9\x54\x2a\x55\x65\xf4\xff\x2d\xa3\x8c\xee\x76\x26\xcb\x64\x5c\x44\x69\x42\x68\xa7\xe8\x25\xbd\xac\xfb\x45\xa5\xe4\x9d\xb4\xb7\xec\x7e\x89\x26\x9d\xf5\xe4\x38\x3d\xe1\xbf\x0a\xf8\x75\x16\x64\x24\xd8\x2d\x2e\x17\x34\x9d\x10\x59\xd7\x6e\x4b\x16\x6d\x3d\x78\x20\x12\x77\x58\x99\xe5\x83\x07\x41\x37\xa3\xc5\x32\x4b\x48\xd0\x49\x7b\xeb\xc3\x2e\x4b\x8f\x64\x5a\x24\xd2\x58\xad\x93\xdd\x84\x9e\x93\x97\x59\x96\x66\x9d\xd6\x7e\x90\x24\x69\x41\x26\x51\x12\x92\x79\x1a\x2e\x63\x4a\xda\xad\x8d\x74\xa3\xd5\x6e\x75\x77\x8a\x59\x96\x9e\x93\x49\x7f\x9c\x86\x74\xb7\xf5\xe6\xf0\xc5\xd1\x4f\x2f\x3f\xbd\x3d\xfc\xf8\xe9\xd5\xe1\xd1\xdb\x17\xad\xde\xe4\x9a\xd5\x17\xef\xb2\xbe\xef\x7e\xa1\x17\x8b\x34\x2b\xf2\xd1\x97\xeb\xeb\x1d\x36\x86\xe3\xe1\x49\x7f\x1c\xc4\x71\x27\xee\x8b\xac\x9e\xec\x7d\x87\xf2\x01\x26\xbb\x00\xb8\x79\x72\x4c\x4f\x76\x44\x57\xf3\x4e\xf2\x2c\x19\xd1\xee\x75\x2f\xee\xe9\x92\xb4\xc7\x71\x77\x2d\xa0\x58\x93\x32\x13\x7a\x11\x35\xc2\xd5\x24\xcd\x3a\x0c\x3a\xdd\x1d\xee\xa4\xdf\x67\xfd\x98\x26\xd3\x62\xb6\x93\x6e\x6c\x74\xf3\x4e\xc6\x10\xaf\xba\x71\xdd\xed\x7c\xd9\x1c\x1d\xab\x2e\x8b\x2a\x7a\x1c\x4b\x3d\xd1\x76\xf7\xcb\x1a\x4f\x90\x9d\xd9\x3d\x5e\x23\xe4\xcb\x1a\x21\x84\xb4\xc6\x69\x92\x17\x41\x52\xb4\x46\xa4\xc8\x96\xb4\xc7\x53\xa3\x64\xb1\x2c\xf2\xd6\x88\x1c\xc3\xb7\x84\x86\xbc\x24\x98\xd3\xd6\x88\xb4\x3e\xa5\xe7\x09\xcd\x5a\x3d\x9d\xc3\x46\xc7\x72\x82\x30\xcc\x68\x9e\xb7\x44\xce\x35\xfc\x7f\x22\xaa\x96\xc5\xe1\x7f\x91\x96\x2e\x8b\xfa\xf6\xd2\x4f\xa8\x88\xd1\xde\xe9\x65\x41\xf3\xed\x2d\x7f\x7b\x12\x48\x61\x7a\x8d\x90\xeb\xde\x9d\x20\xe0\x46\xfd\x51\xc3\x41\xd8\x6b\x86\x80\x95\x51\xfd\x47\x1d\xfa\x38\x4d\x0a\x9a\x14\xb7\x1e\xfc\x9f\x72\xde\xd9\x8c\xfd\x61\xa6\x7d\x12\xc4\xf9\x6f\x37\xf4\x8c\xe6\x34\x3b\xf3\xad\xfa\x3f\xfa\xa4\xe5\xcb\xd3\xf7\x74\x1a\xe5\x45\x16\xfc\x17\x4c\x5e\xaf\xaa\x0e\x7a\x7e\x78\x2b\xbe\x5f\x64\x41\x92\x4f\xbc\xac\xef\xcf\x82\x83\xcc\x22\x85\xd5\x91\x90\xd3\xe2\x43\x35\x49\xdd\x19\x2e\xec\xa6\x7f\x93\x46\xbf\xf2\x04\x04\x4d\x10\x5f\x55\xc1\x22\x8b\xe6\x41\x76\xe9\xed\x47\x9a\xc6\xb5\x93\xb7\x27\xda\xfa\xf3\xa2\xd0\xdc\x83\x2b\xab\x29\x43\xc2\x7e\xe9\x36\xfe\x47\x42\x82\xb7\xf7\x61\x94\xa7\xe7\xc9\x2d\x7a\x1e\x24\x69\x72\x39\x4f\x97\xf9\x0a\x5d\x8f\x92\x90\x5e\xd0\xd0\xd8\xbb\xee\x6c\x62\x75\xe5\xa8\x3b\x66\xed\xe7\x51\x72\x1b\xc6\xbd\xb7\x04\x4c\xbc\x4c\x42\x1a\xb6\x2c\x34\xd1\x33\x46\x08\x7f\x01\x1c\x9d\x46\x61\xd8\x0c\x47\x37\xab\xff\x2c\x88\x97\xde\xee\x2f\xa3\xa4\xd8\xfa\xe6\x49\xf5\x14\xbc\xa5\xe7\xcf\xa3\xdf\x11\xf9\xb7\x5a\x73\xfb\xb3\x20\x99\xfe\x9e\xa4\x73\x27\x94\x53\x52\x37\x92\xea\x2b\xa9\xc6\x8b\x99\x77\x7c\x37\xaa\x45\xd0\xda\xc9\xda\xda\x75\xef\xcb\xf5\x49\x6f\xeb\x77\x3b\xf4\xff\x85\xce\xbc\xbf\x93\xec\x38\x59\x26\xe1\x8d\x49\xe5\xd6\x1b\xd7\xfd\xb1\xf7\xcf\x7d\xec\xbd\x3f\xf4\xfd\x91\xcf\x1c\xde\xc1\x8b\xf3\xc2\x1f\x4d\xda\xfc\xba\x9b\xb9\xde\xab\xb6\xef\x6c\xaf\x5a\x75\xde\x27\x59\x3a\xbf\xe5\xb4\x17\xe9\x2d\x8f\x9a\xb7\x13\xf8\x7e\xdf\x75\xf3\x47\xc0\x5f\x94\x84\x51\x46\xc7\xc5\x81\x77\xcf\x5c\xa1\x27\xb7\x9b\x88\x68\x1c\x2c\x3e\xfe\xae\x93\xe1\xc7\x64\xb3\xd3\x2e\x5d\xa4\x79\x54\x75\x50\x5f\x04\x97\xc1\x69\x4c\x4d\xa1\xe0\x77\xe1\x4a\x65\x34\x77\x27\xc7\xaf\xdb\xd1\xc0\x9e\x1c\xef\x0b\x13\x9f\xbf\xfd\x49\xe6\x4e\x90\x54\x52\x77\x33\x3a\xfb\x1d\xd0\xff\x87\xc5\xfa\x5d\x9c\x1f\x6f\xcc\x27\xbf\x36\xd6\x6d\xa6\x77\x8f\xf6\x86\x68\xbf\xf5\xc6\xf5\xb5\x67\xf6\xc0\xb3\xa5\x55\xc9\x71\x8f\x9b\xc8\x71\x60\xbc\x41\x76\xa5\x85\x43\xa7\xdd\x1f\x4c\xd2\x6c\x1e\x14\x05\xcd\xf2\x76\x77\x07\x00\x3e\xa4\x71\x14\x46\xc5\xe5\xc7\xcb\x05\x35\x61\x59\xfb\x0c\x6a\x6d\xf0\xf0\xe1\x1a\x79\x68\x40\x0a\x9d\x3b\x89\x72\x12\x90\x45\x96\xa6\x0c\x98\x14\xb3\xa0\x20\x19\x5d\xb0\x43\x56\x52\xe4\x44\xcc\x1d\x61\x99\xac\x86\x83\x82\xcc\x83\x62\x3c\xa3\xf9\x88\x7d\x8a\x6c\xf4\xf3\xf8\x04\x7f\x3c\x36\xbe\x4e\xcc\xcc\x6d\xeb\xfb\xe4\xf8\xc9\xc9\xf1\x49\x8f\xf4\xfb\xfd\x35\xf2\x70\xe0\x8c\x4d\xf6\x78\x97\x28\x6b\x9a\x4e\x57\x4c\x71\x31\x8b\xf2\xfe\x27\x58\x18\xaf\x24\x82\x18\x60\x9f\xa3\xeb\x80\x65\x1c\x24\xc5\x0e\x02\xe6\xfb\xb6\x0f\xfa\x10\x72\x44\x73\x3b\x6b\xd7\x3b\x6b\x6b\x9e\x7e\xf4\x17\x59\x5a\x70\xac\xed\x92\x84\x9e\x1b\x7d\xed\x7c\xb9\xee\xee\x54\x97\xea\x83\xf4\x92\x2d\xc7\x45\xca\x1a\xf7\xc0\xd6\xb5\xdb\x8f\x72\x31\xe7\x1a\x21\x8c\x1c\x25\x52\x84\x5d\xcb\xfa\x3a\x4b\xec\xc3\xbc\x75\x06\x02\xdb\x9d\x7f\x1f\x77\x8e\x87\x8f\xbe\x3b\x79\xd8\xfd\xf7\x49\xf7\xd9\xa0\xcb\xc7\x69\x1e\x1c\x4a\xbb\x75\xdd\xfb\xd2\xc2\xa4\xd8\x1a\x7d\xd7\x6b\x71\x7a\x6b\x8d\x36\x1f\x5f\x9f\xf4\xbe\xf9\x9d\xc9\xfb\x79\x9a\xc6\x35\xb4\x7d\xca\x40\x4a\x08\x9b\xe5\xc9\xff\x39\x95\xc2\xaf\xc7\xfa\xe7\x09\x4a\xde\xc6\x1f\x75\x64\x0c\x3d\xbb\x29\x0d\xb3\xc2\xab\x10\x31\x87\xb7\x29\x98\xa5\xae\x48\xbe\x66\x91\x0a\xda\xe5\x2d\x56\x95\xbd\x09\xd5\xfe\x87\xa1\xd6\xa4\xd9\x87\xff\xd3\x88\x68\x45\x7f\xea\x29\xf6\xc9\xef\x4d\xb1\x6c\x0f\x53\x24\x5b\xf8\x69\xb6\x98\x51\x02\x9b\x1d\x10\x6e\xdf\x47\xb9\x2c\x57\xfd\x10\x74\x09\x3f\x1f\xa3\xdf\x27\x38\x63\xdb\xf8\x32\xe9\x97\x88\xad\x55\xfd\x7c\x6a\xd4\x23\x8a\x7a\xa8\x1c\x3a\x79\x63\x32\x67\xa5\x57\xa2\x73\x5e\xc0\x21\x74\x96\xbc\x2a\xa5\x9b\x65\xaa\x48\x9d\x37\x5a\x59\xfa\x66\xc4\xce\x2a\xe1\xa4\xfe\x65\xb3\x77\xdd\xbd\x19\xe1\x8b\xde\xd5\x53\xfe\xb7\x4d\x28\x7f\xf0\x10\x3a\xfc\x71\x16\xe5\x64\x12\xc5\x94\x51\xea\x22\xc8\x0a\x92\x4e\xc8\x39\x3d\xdd\xee\xff\x92\xf7\xd7\x00\x44\x7c\x31\x80\x49\x46\x29\xc9\xd3\x49\x71\x1e\x64\x74\x44\x2e\xd3\x25\x19\x07\x09\xc9\x68\x18\xe5\x45\x16\x9d\x2e\x0b\x4a\xa2\x82\x04\x49\x38\x48\x33\x32\x4f\xc3\x68\x72\x09\x75\x44\x05\x59\x26\x21\xcd\x80\xe0\x0b\x9a\xcd\x73\xd6\x0e\xfb\xf8\xf1\xed\x11\xf9\x89\xe6\x39\xcd\xc8\x8f\x34\xa1\x59\x10\x93\x77\xcb\xd3\x38\x1a\x93\x9f\xa2\x31\x4d\x72\x4a\x82\x9c\x2c\x58\x4a\x3e\xa3\x21\x39\xbd\x14\x54\x44\xc9\x2b\xd6\x99\x0f\xa2\x33\xe4\x55\xba\x4c\xc2\x80\x8d\xb9\x47\x68\x54\xcc\x68\x46\xce\x68\x96\xb3\x19\xda\x96\x6d\x89\x1a\x7b\x24\xcd\xa0\x96\x4e\x50\xb0\x31\x64\x24\x5d\xb0\x82\x5d\x12\x24\x97\x24\x0e\x0a\x5d\xd6\x45\x81\x1e\x69\x48\xa2\x04\xaa\x9d\xa5\x72\x65\x47\x05\x39\x8f\xe2\x98\x9c\x52\xb2\xcc\xe9\x64\x19\x73\xc1\xf1\x74\x59\x90\x9f\x0f\x3e\xbe\x3e\x3c\xfa\x48\xf6\xde\xfe\x8b\xfc\xbc\xf7\xfe\xfd\xde\xdb\x8f\xff\xda\x21\xe7\x51\x31\x4b\x97\x05\x61\x12\x25\xd4\x15\xcd\x17\x71\x44\x43\x72\x1e\x64\x59\x90\x14\x97\x24\x9d\x40\x15\x6f\x5e\xbe\xdf\x7f\xbd\xf7\xf6\xe3\xde\xf3\x83\x9f\x0e\x3e\xfe\x8b\xa4\x19\x79\x75\xf0\xf1\xed\xcb\x0f\x1f\xc8\xab\xc3\xf7\x64\x8f\xbc\xdb\x7b\xff\xf1\x60\xff\xe8\xa7\xbd\xf7\xe4\xdd\xd1\xfb\x77\x87\x1f\x5e\xf6\x09\xf9\x40\x59\xc7\x28\xd4\x50\x8f\xe8\x09\xcc\x59\x46\x49\x48\x8b\x20\x8a\xe5\xfc\xff\x2b\x5d\x92\x7c\x96\x2e\xe3\x90\xcc\x82\x33\x4a\x32\x3a\xa6\xd1\x19\x0d\x49\x40\xc6\xe9\xe2\xb2\xf1\x44\x42\x65\x41\x9c\x26\x53\x18\xb6\xa2\x32\x42\x0e\x26\x24\x49\x8b\x1e\xc9\x29\x25\xdf\xcf\x8a\x62\x31\x1a\x0c\xce\xcf\xcf\xfb\xd3\x64\xd9\x4f\xb3\xe9\x20\xe6\x15\xe4\x83\x1f\xfa\x6b\x0f\x07\x92\xd9\xfe\x0d\xc8\x76\x9c\x86\x34\xeb\xff\x02\x2c\xf2\x6f\xc1\xb2\x98\xa5\x19\x79\x13\x64\xf4\x33\xf9\xdf\xb4\xa0\xe7\xd1\xf8\x57\xf2\xfd\x9c\x7d\xff\x8d\x16\xb3\x90\x9e\xf5\xc7\xe9\xfc\x07\x00\x0e\x83\x82\x92\xad\xe1\xe6\x37\xc0\xf0\xea\xb7\x82\x0a\x01\x16\x95\x11\xf2\x98\x6f\xef\x10\x92\x02\x02\x66\xbb\xa0\x0f\xf2\x20\x29\x4c\xc0\x28\x29\x7c\x70\x47\x0e\xe0\xb2\x04\xf2\xc5\x65\x12\xcc\xa3\xb1\x64\xe3\xa8\x44\xc8\x73\x80\x47\xf9\x4a\x7e\x28\xb2\x28\x99\x9a\x65\x72\x48\xf3\x41\xbf\xa7\x81\x35\xc6\x8c\x06\xde\x31\x1e\xb9\xa0\xcb\x32\x58\x4f\xb7\x55\x7f\x01\x38\xca\xc5\x00\x0d\xce\x9c\xa3\x2a\x7a\xb0\xc3\x0a\x3e\x2d\x2d\xc4\x51\x7e\x5f\x55\x01\xdb\x08\x07\xbe\xba\x52\xa7\x47\x52\x02\xbd\x97\x65\xc1\x25\x07\xe7\x4c\xdc\x12\x05\xf6\x19\x7d\x22\x09\x40\xac\x24\xce\x21\x42\x52\xa4\x84\x26\x8c\x86\x07\x21\x65\xff\xa9\x56\x18\x33\x0e\x38\x9b\x64\x5c\x49\xc8\xb5\xe6\xc6\xcc\xeb\xc6\x23\x66\x60\xb9\xb9\x33\x43\x12\xd9\x85\x1a\x72\xa3\x8b\xc0\xfb\xe7\xb4\x98\xa5\xa1\xa7\x5b\x5c\xb9\x9e\x66\x73\xc2\x25\x97\xd4\x98\x91\x35\xc2\xd7\xa0\x28\xfe\x49\xcc\x8c\xc8\x22\x7f\x83\xde\x93\x2f\x9c\x78\xae\x95\x58\xfe\x37\x8e\xf9\x9c\x7c\xc1\x95\x5d\x43\x16\xbc\x55\xc8\xc9\x17\x78\xd7\x70\x4d\xc4\x67\xc4\x78\x03\x97\x88\x18\x19\x42\x5f\xd8\x4e\xc4\xd8\x3d\x20\xc4\x40\x06\xda\xa9\x71\x97\x1c\x1c\x49\x14\x31\x6c\xe6\xa6\x78\x87\xb0\xd6\x9f\x44\x71\x41\xb3\x0e\x2a\xdb\x45\x3a\x08\x41\x45\x85\x10\x0a\x24\x11\x80\x4e\xa1\x7b\x3c\x3c\xd9\xe1\xfc\x33\x9a\x90\xce\x3a\x6e\x04\xd7\xc1\x1f\x68\xf0\xa7\x1c\xed\x28\x39\x0b\xe2\x28\xd4\x34\xc0\x6a\x5c\x1f\x91\x36\xd9\x20\xb8\xf2\x35\x2c\x6b\xe0\x9a\x4d\x0a\x2c\xa1\x34\xb2\x88\x83\x28\xe1\xf4\x65\x4d\x23\x07\x78\x27\x72\xca\x67\x51\xa4\x1f\x9e\xfe\x42\xc7\xc5\xb5\x55\xa1\x9c\x64\x5d\x8e\x57\x1b\x5a\x70\xe5\x53\x87\xba\xe1\xcc\x5c\x8f\x97\xb7\x04\x2e\x98\x34\x54\x2c\xef\x1c\x33\xe0\x93\x1e\x39\x06\xf0\x93\x6e\x33\xd4\xc4\x51\x0e\x12\x10\x5f\x7c\xe5\xd8\xc9\x31\x1a\x80\x05\x70\xec\xf8\xd2\x17\xba\x40\x19\x62\x9c\x66\x1b\xe1\x26\x77\x97\xbe\xc0\x4e\x5e\x46\xdf\xb9\x24\xf0\x29\x2d\xf0\x0a\xcc\x05\xe7\x10\x24\xcb\x8a\x89\xbe\xb1\x12\x46\x0d\xfd\x79\xb0\xe8\x94\xf1\x58\xd0\xca\x79\xd6\x88\xc1\x3b\x79\xcd\x1d\xde\xd3\x63\x28\x72\xc2\xd9\xb3\xfc\x52\xab\x08\xf5\x47\xec\x53\x87\x93\x49\x4e\x0b\xa7\x53\x19\x0d\x97\x63\x8a\xfa\x15\x8c\xc7\x3d\x52\xd3\x39\xc0\x4e\x11\x14\xd1\xf8\x5d\x90\x15\x3f\xc1\x4b\x22\xab\xe6\xbe\x9d\xdf\xf1\xf4\x53\xd6\x95\x31\xa6\x44\xc3\x0f\x6e\x95\x6f\x82\x62\xd6\x9f\xc4\x69\x9a\x75\x3a\x4e\x8b\x1b\x64\x7b\xb3\x4b\x06\x64\x7b\xab\x4b\x1e\x92\xed\x2d\x31\x68\x84\xbe\x60\x3c\x26\x1b\xa4\xa3\x36\x1d\x03\xeb\x25\x28\x24\xcf\xd0\xde\x45\xc8\xf6\x16\x19\x19\x09\x25\x9d\x95\xa8\xef\x91\x21\xc6\x7e\x46\xf3\x65\x5c\x48\xea\xe1\x33\xf8\x66\x19\x17\xd1\xcf\x51\x31\xe3\x73\x22\x29\xd0\xe8\x5b\x4f\xd1\x51\xcf\x9c\x41\x59\xb9\x18\x21\xaf\xdf\x3c\xf1\xf9\x49\xdf\x6a\xd5\xb7\x06\x1a\xf6\x00\xad\x11\x35\xbc\x56\x6b\x47\x2f\x1c\x1a\x4f\xc4\x88\x45\x67\xc5\xae\x90\x66\x2f\x83\xf1\xac\x63\x33\xa6\x08\xd3\x16\xe3\xfa\xa5\xf3\xa5\xe7\xea\xa4\x8b\x0b\x71\x84\x40\x57\x36\x5c\x6d\x67\xc7\xec\xbe\x5c\x47\x88\x08\xd5\xda\x65\x54\x4c\xe3\x89\x00\xb1\xe7\x08\x3a\xe0\x76\x49\xe2\x09\x3e\xec\xc9\xc2\x4d\x98\x4b\x71\x63\x97\x50\xf1\x0c\x8f\x0c\xc8\x96\x06\xbd\x26\x34\xce\xa9\x35\xbc\xc1\x80\x84\x69\xd2\x2e\x48\x10\x86\x44\x94\x2a\x52\xb3\xca\x3e\x89\x8a\x76\x4e\x82\x38\xa3\x41\x78\x49\xc6\xe9\x32\x29\x68\x58\x82\xa5\xaf\x34\xce\x6b\xbd\x08\x07\x03\xf2\xf1\xf0\xc5\xe1\x88\x4c\xa2\xe9\x32\xa3\x84\x1d\xd8\x12\x9a\xb3\x13\x20\x3b\xa5\x5d\xe6\x26\xb3\xfa\x2d\x88\xe4\x8f\x33\xc9\xe6\x64\x50\x8c\x40\x89\x95\x92\x65\xae\xd0\x9a\xd1\x49\x00\xea\x98\xf3\x59\x1a\x53\xde\xc3\x28\x99\xae\xd7\x30\x82\x0a\x1e\x60\x73\x7e\x31\xe8\x1e\x49\x9d\x95\x6f\x2c\x72\x39\x27\xb5\xa2\xbe\x67\x8b\xeb\xb8\xaa\x31\x44\x40\xbc\x61\x72\x1e\x68\xb2\xce\x69\xe1\xcc\x29\x27\xab\xb7\xc1\x9c\xda\xfb\x90\xce\xc1\x72\xa6\x5b\xd6\xb3\xf9\x54\xef\x67\xba\x62\x4f\x9d\x8a\x2f\x0a\x0c\x6a\xa9\x56\xfe\x55\x0c\x5b\x56\xb2\xc8\xe8\x59\x94\x2e\x73\xd5\xa1\xad\x1d\x86\x92\x28\x21\x51\x52\x38\x25\xea\xf0\x8f\xfa\xeb\x6b\x90\xfd\x4d\xd2\x8c\xc0\x23\xe1\x88\xec\x92\xcd\x1d\x12\x91\xef\xe5\x00\xe4\x7b\x61\x12\x6d\x6c\x94\x15\x67\x7f\x56\x9f\x37\x76\xc9\x46\x47\xe2\x20\x22\x8f\xc8\xe6\x09\x93\xf0\xc9\xd5\x15\x19\xee\x94\x56\x52\xc1\xca\x05\x3d\x6c\x90\x88\x3c\x2c\x9b\xb9\x0d\xbb\x17\x4c\x38\x28\x63\xfb\xf2\xef\xda\x49\x35\x53\xae\xbb\x9d\xae\x35\x85\x83\x01\x99\x44\x59\x5e\x10\x1a\xd3\x39\x4d\x0a\x76\xbe\xe2\x68\xea\x91\xfc\x73\xb4\x20\x51\xb1\xca\x94\x1b\xd8\x1f\xfa\xb0\xcf\xf0\x57\x39\x03\xf0\x74\x3e\x0c\x23\xd6\x48\x10\xab\x45\x2e\xf0\xe9\xf0\x1f\x17\xdf\x7e\xbe\xa8\x49\xa7\x84\x41\x1c\x47\x64\x83\x6c\x9e\x48\x3e\x41\x36\x88\xd3\x0d\x0f\xda\x6b\x11\x6c\x31\x3f\x0f\xa4\xd8\x2a\x3d\xb4\xcf\xa9\xe2\xc6\xac\xe7\x0f\xcd\x54\x98\xb0\x65\x62\xea\x96\x8b\xbf\x86\x32\x49\x19\x43\x1a\x56\x31\x24\xd2\x88\xa6\x6b\x39\xca\x60\x40\xc6\x41\x3c\x5e\xc6\x41\x41\xa5\xe0\xc3\x8e\x7c\xa2\x2f\x24\x2a\xe8\xfc\x16\xec\x88\xb1\xa2\xe3\x3f\x11\x53\xea\xda\xb0\xd7\x2b\xed\x2b\xb7\x9c\x90\xdf\x8f\xc1\x60\xe6\xf2\xd5\x79\x0b\x71\xb4\x45\xa2\x1f\x35\xda\x10\xa1\x8b\x14\x37\x93\x69\x85\xc6\x88\x43\x36\xd6\x18\xc9\x74\x75\xab\xa9\x54\x22\x7e\x5d\x52\xb9\x1e\x04\x35\xec\x11\xff\xa0\x7e\x9f\x8e\x08\x15\xd3\x3a\x22\x0e\x0d\xb2\x4d\x13\xb4\x54\x2a\x89\x4a\x10\x52\xa6\x23\x2a\x47\x88\x28\x01\x27\x0c\x68\x4d\x23\xa6\x5a\x43\x84\x87\xe8\x3b\x1d\x1b\xb8\x59\x5d\x41\x24\x4b\x71\x2a\xc6\xf0\x9c\x88\x73\xef\x29\xdc\x3a\xee\xdf\xb1\x46\x89\x0f\xb9\x03\x23\x93\xeb\x4b\xab\x45\x0c\xbd\x88\xac\x51\x6b\x98\xaa\x54\x0e\x7a\x54\xb5\x7a\x06\x8c\x51\xce\x81\x58\x99\xbb\x1e\x69\x13\x75\x94\x3a\x89\xfa\xe4\x60\xd1\xb5\x52\x26\x39\x18\x90\x7c\x39\xe7\x37\x74\x9e\x5d\x4a\x88\x88\x0a\x5e\x54\x77\x1c\x9d\x30\xae\xa8\xbe\x60\x4b\xf2\xf1\x1f\xd9\xbc\x89\x08\x29\x6d\x3a\x28\x18\x0c\x48\x46\xe7\xe9\x19\x5c\x63\x92\xf1\x32\xcb\x98\x7c\xaa\x84\xd3\x14\x92\x45\x37\xa3\x1c\x7a\xee\xe9\x6d\xbe\x8a\xc6\x4f\x22\xb3\xb1\xe6\xcf\x18\x19\x79\xe4\xd4\xdf\x98\xd2\x3e\x58\xeb\xb0\xe4\x5a\xc7\x7b\x6a\x95\x3c\xce\x43\x65\x85\x75\xe5\x20\xc9\x8a\xed\x60\xf8\x92\xc4\xbc\xbf\xe0\xbd\x65\x6d\x8d\xc5\x2d\x13\x36\xb5\x80\xde\x77\xb8\xbd\xaa\x6d\x82\x21\xae\x45\x3b\xdd\x9e\x37\xfb\x79\x9a\xc6\x65\x79\x4c\x08\x29\xc9\x3a\xaa\xc8\xc3\x97\x9b\xa5\xcd\x56\x65\x72\x2e\x5c\x96\xfb\x9e\x06\xa5\x3d\x3e\xe2\x99\x6b\x8c\x20\x5c\xfb\x0d\x40\x9d\xb2\xd9\x90\x86\xb3\xa3\xc7\xbd\x16\xbf\xfb\x6d\x8d\xbe\x81\x9f\xac\x6f\xad\xd1\x13\xf6\x1b\x5f\xc7\xb6\x46\x4f\x7b\x3e\x5b\x8f\x28\x29\x5a\xa3\xcd\x21\xfb\x99\xd1\x20\x6e\x8d\x36\xb7\xd8\x6f\x7e\x2b\xdb\x1a\x6d\x6e\xb3\xaf\x25\x87\x82\x06\x96\x02\xec\xc9\xf5\x49\xef\xe9\x6f\x69\x17\x55\x73\x0d\x7d\x33\x6b\x22\x5c\xc9\x2a\x46\x45\x66\x39\xdb\xb6\x08\xe7\xae\x68\x62\xe4\x2f\x5a\x61\x69\x64\xf6\xa4\x49\x5d\xb7\xb0\x3b\x2a\x31\x36\x6a\xd4\x28\xba\x12\xf7\x4e\x97\x64\x3b\xd9\x92\x36\x30\x61\xb2\x86\x5d\x6f\xc9\xf4\xdd\xbd\x25\xd3\xbd\x25\xd3\x7f\x8b\x25\x93\x5e\x08\x77\x65\xce\xf4\x3c\x9a\xbe\x5d\xce\x4f\x81\x15\x2a\xee\x7c\x1a\x4d\x13\x48\xec\xff\xa2\x38\xf9\xb2\x88\x62\xd3\xbe\xa6\x3f\x80\x34\xfe\xaf\x04\x1b\x7b\x41\xc6\x69\x32\x89\x1c\x63\x20\x79\x32\x43\xbb\x02\x9c\x5d\x60\x5b\x90\x03\xe7\xbc\x3a\x27\xc0\xef\x09\x3c\xd8\x60\xe7\x2c\xc6\xb7\xb4\x95\x2c\x2c\x05\x36\x37\xa0\x9c\x79\xc8\x70\xcc\x21\xa3\x9c\x24\x74\x1a\x14\xd1\x19\xed\x49\x4e\x04\x17\x47\xc5\x79\xda\xce\xc9\x38\x9d\x2f\xa4\xb4\x0a\xa5\xd8\xdc\xaa\x92\x93\x38\x0d\x8a\x28\x99\x92\x45\x1a\x25\x45\x8f\x5f\x87\x32\xb2\x0f\xd3\xf3\xc4\x3a\xd3\x99\x6a\x12\xf7\xf8\x76\xc5\xb1\x7c\xa5\xf0\x7d\x2d\xc7\xc2\x96\x52\x42\x69\x08\xa7\xe8\x53\x3d\xc7\xa1\xdf\x18\x06\x90\x76\xad\xec\x7c\xcc\x76\x0d\x06\x0c\xf5\x4b\x2e\xac\xda\xed\xf3\xb9\xe8\x8c\xfb\x2f\x3f\xbe\xfe\xf4\xfc\xe0\xc7\xb7\x47\x6f\x9e\xbf\x7c\xff\xe9\xfd\xe1\xd1\xdb\x17\x07\x6f\x7f\xfc\xf4\xe6\xf0\xc5\x4b\x74\x86\x53\x9a\x38\x98\xc9\xfe\x22\x08\x7f\xa2\x93\xa2\xc3\xbf\x8a\xf4\xe3\x79\x9a\xef\x2b\x2c\x8a\x36\xfb\x45\x2a\xc4\xa5\xcd\x27\xdd\x1e\x79\xf2\xd8\xbc\xe1\xc1\xbb\x25\x0c\xa7\xc3\x1b\x31\x0d\x30\xcc\x89\x97\x87\xdf\x12\x9c\x3f\x57\x67\x63\xf3\xd0\xbc\x2a\x0e\x5d\xa9\xc3\xc0\xa2\x07\x21\x45\xfa\x9a\x5e\xc8\x71\xe7\xcb\xd3\xbc\xc8\x3a\x5b\x08\x7f\xb1\x75\xb5\xcf\x8b\x4b\x2d\xf7\x06\x79\xb2\xdd\x25\x03\x8c\x22\x1b\xdd\xef\xa3\xe9\xac\x10\xc5\x7a\x24\x26\x0f\xbf\x32\x3e\xc5\x0e\x7c\xa7\x68\x2d\x95\xe9\x6e\x8d\x5d\x79\x3c\x33\xd1\xaa\xb4\x73\xbf\xdb\x0c\x58\x6a\x53\xde\x58\xb7\xcf\xd7\xfc\x06\xa9\x9f\xa0\x3a\x4e\xc7\x25\xf9\xf2\x15\xf1\x41\xe6\xdf\x76\xee\x94\x71\x67\xf3\x59\x9b\x64\xe9\xfc\xa8\x98\x3c\xbd\x9f\x38\xcf\xc4\x89\x77\x46\x65\x8c\x4c\xbc\x42\x92\x93\xc6\xbe\x69\x90\xac\xce\xc8\xec\x27\x47\xe5\x73\xd6\x1e\xde\xee\xaf\x4d\x36\x44\xf5\xe4\x19\x21\xed\xcd\x36\x19\x91\xf6\xb0\x7d\x7b\x1e\x55\x87\x49\x76\x62\x65\xa5\xfe\xc1\xe0\x72\xc2\x04\xe3\xf9\x32\x2e\x22\x2e\x54\x9e\x5e\x92\xad\xff\xcc\x99\x78\xae\x6c\xe8\x02\x56\x73\x41\xa7\x34\xab\xd8\x4a\xde\x8b\x5a\xeb\xf6\xef\x55\x67\x44\xd8\x32\x97\xcc\x88\x40\x93\x45\x7d\x0c\x6b\xaa\x45\xb5\xb9\x46\x73\x9a\x5b\x59\x5b\xdd\xfe\x22\x3d\xef\x6c\x6e\x3d\xed\x76\x4d\x94\xee\xcf\xe8\xf8\x33\x89\x26\x06\x4e\x91\x58\x64\x21\x22\x8f\xa6\x09\x0d\x0f\xf2\xb7\x3a\xdb\x51\x44\xab\x3a\x66\xf4\x42\xf4\xd8\x44\x86\x24\x5a\x38\xf4\x41\xdb\x85\x29\x89\xa5\xec\xc8\x72\x1e\x31\x31\x3c\x88\x73\x6d\xb5\x6c\xb7\x5e\x8b\x2f\x1f\x86\x24\xbb\x19\xf6\xc8\x66\xb7\x47\x36\x9f\x20\x79\x64\xab\x6b\xe4\x76\xc9\xee\xee\x2e\x23\x59\x2f\x15\x66\x8c\x7d\x3c\x0a\x62\xe8\x14\xe1\xaa\x03\x7d\xe1\xc1\x45\x4d\x97\x88\xb8\x22\xc1\x16\x02\x0d\xf2\x70\xec\x60\x19\xce\xb4\x60\x58\xd1\xae\x12\x0e\x61\x59\x44\x53\xc2\xe5\x74\x8b\xde\x54\x17\x0c\xfc\x19\x46\xb1\x0c\x98\xcf\xe3\x2e\xef\x0d\xd2\x65\x76\xba\xe4\xea\x8a\xb4\x86\x2d\xa1\x23\x1e\x0c\xc8\x58\x51\x11\x13\x9e\xe5\x44\xaa\xd6\x39\x50\x54\xf0\x89\x56\x92\xb6\x2b\x64\xcb\xfb\x5b\x6b\x9e\xc5\xdc\x7a\x54\x90\x9e\xf9\xe5\x53\x3a\x8f\x92\xa5\xbd\x0a\xda\x93\x5b\xfe\xb5\xa1\x6e\x59\xf9\xa6\xba\x1e\x6b\xd0\xa1\x1b\x50\xd0\xb2\x9a\x84\x8e\x2a\x69\xc8\x47\x3d\x74\x25\xf2\x11\xcd\xbb\x84\x73\x74\x17\x94\xf3\x75\x50\x26\x58\x7e\x19\xca\x1c\xde\x5d\x8b\x32\xc0\x18\x12\x89\x4d\x14\x89\xe6\x5c\x14\x39\xcc\xdc\x67\x71\x6e\x2d\x46\x01\xd3\x0f\xa3\xb3\x28\xa4\xe1\xf3\xcb\x0a\x1e\x7e\x13\x6a\xaa\xc1\xcd\xd1\x5d\x23\x67\x59\x8a\x9d\xa3\x95\xd1\x73\x74\x1b\xfc\xb8\xb7\xb0\xbc\x6a\x85\xa2\x32\x89\x4b\x3f\x98\x6e\x8c\x17\xb9\xb3\x99\x73\x51\x8a\x23\xd1\xb4\x8b\x22\x47\x3e\xf3\x61\xc8\xb3\xbc\x60\xbf\xba\xa5\xc0\xb6\xd9\x26\xcf\xf8\xd6\x2c\x3c\x63\xac\x86\xcd\xd2\x93\x23\x7a\x97\x5b\xb1\xf7\xc5\x74\xa2\x11\xc7\x24\x88\x8a\xb3\x8d\x23\x7a\x24\xc1\x9c\xf2\x07\x3e\xec\x97\x25\x82\x09\x18\x56\xa7\xaa\xc1\x83\x79\xe7\x10\x0a\x6d\xf4\x08\x56\x96\xb3\x42\xe2\x89\x35\xd9\x25\x65\x2f\x75\x1f\x76\x07\xe8\x48\x93\x47\xbf\x0a\x9e\x98\xc3\x2d\x95\x28\x7f\xbc\x79\x62\x8a\xc2\xed\xe1\x05\x13\x99\xdd\xc9\xed\xe7\x71\x34\xa6\x4c\x32\xd9\x22\x0f\xa1\xba\x15\xe9\xbc\x66\x66\xf0\x29\xfc\xce\x26\x68\x55\xf4\x97\xaa\x02\x9c\x4d\x46\x1d\x11\x2d\x3e\xc0\x11\x27\x2e\xc1\x6c\xcc\x3d\x79\xdc\x15\x7b\x78\x91\x0a\xf8\x2e\x79\x28\x4f\x95\xbe\x19\xb0\x2a\xe2\xd2\xe1\x93\xc7\x3d\xd1\xfe\x6a\x53\x50\x71\x2a\xe7\xc3\xf7\x1c\xcb\xef\x14\xfb\x41\x3e\x8e\xa2\x2a\xfc\x7b\x8e\xf3\xbf\x21\xe6\xa5\x56\x07\xb4\x03\xcd\xf0\xbf\xda\x04\x68\xf7\x34\x65\x33\xb0\xa7\x1d\xd8\x94\x4c\x41\x29\x6f\x2f\x41\xb9\xaa\xd0\xc5\xb6\xcf\x81\xcd\x0a\xd2\x94\x81\xbb\xd6\xf0\xa2\x45\x36\x88\x38\xe3\x00\xda\xf9\x6f\x65\x56\xf0\x78\xd8\x23\x38\xa9\xcc\x67\xc0\x17\x69\xfa\x81\xce\x9a\x23\xeb\xbb\x67\xc3\xc0\x8a\x1d\x39\x29\x0e\x1c\x5e\xe0\xa3\xb2\x0c\xa7\x14\x47\xe6\xc8\x4d\x72\xfb\x91\xa6\xf1\xc8\x4e\x70\xa0\x98\x04\x32\xb2\x13\x30\x94\x12\xcb\x46\x76\x82\x0b\x75\xe4\x80\x1d\x79\xe1\x70\xa3\x3a\xc5\x53\x9f\x0b\x78\xe4\x87\xc4\x83\xd5\x29\x1e\x38\x8c\x6d\x94\xe4\x42\xfa\xa6\xc7\xcd\x71\xcb\x99\x13\x84\xd3\x5c\x58\x41\xf5\x23\xef\xba\xbb\x96\xd7\xba\xe6\xe5\x50\x6b\xb4\xf9\xb4\xd7\x32\x2f\x95\x5a\xa3\x2d\xb0\x60\x80\x85\xd1\x1a\x6d\x6e\xf6\x5a\xf8\x6a\xaa\x35\x32\x3f\xaf\x4f\x7a\x9b\xc3\xdf\xd9\xa5\xcb\x01\xb7\x8d\xaf\xf0\x41\x14\x25\x45\x99\x0b\x22\x71\x7b\x15\x25\x05\xf7\xce\xc2\x7e\x3c\x56\xbf\x4e\x74\xe2\x36\xfa\x6d\x39\x6f\x89\x92\x82\xbb\x6e\x89\x92\xe2\xc9\x63\x05\xf6\x54\x57\xb4\xf5\xcd\x93\x92\xba\x18\x7c\x8d\x2b\x23\xfb\x68\xf8\x15\xbd\x71\x01\xb8\x6d\x86\x70\x90\x14\x2b\x5a\x5e\x18\x25\x2a\x0c\x2e\xa0\xb9\x8a\x92\x37\x32\xaf\x88\x92\x42\x8a\x8a\xcf\x6e\xe4\xd2\x85\xf7\xaa\xde\x0c\x62\xb3\x51\x14\xbb\x7b\x3b\x88\x7b\x3b\x88\x3f\xaf\x1d\x04\xd1\x86\x10\x5c\x54\xba\x23\x1b\x88\x06\xa6\x0d\x36\xab\xe7\xa6\x0b\x29\x18\xa4\x6b\xcf\x1d\x7d\x8f\x84\x7a\x3e\xa3\x89\x7a\xaf\xd8\xe3\xb6\xdf\x4c\x00\x57\x0e\x1c\xa4\x64\x39\xf0\xda\x46\x58\xea\x6f\xfb\x79\x22\x70\x52\x29\x3f\xf2\xff\xaf\xae\x48\xbb\x8d\xf8\x6c\x2a\x5f\x2e\xf0\x1f\x3b\xe8\xa9\x61\x94\x88\xd6\x1b\x7b\xfc\x98\xd2\x02\x9b\xfc\x82\x01\x79\x3b\x97\x0f\x41\x81\x97\xb0\x4a\x0c\x6b\x77\x2d\xdf\x73\x63\x57\x53\x8a\x96\x6a\x26\x5d\x2b\xae\x8c\x74\x64\x1f\xbb\x86\x41\x3b\xa0\x07\x1b\xb4\xdb\x8d\x54\x9a\xa2\x81\x95\xbf\x71\xec\xc0\xd7\x8f\x8d\x91\x31\xce\x28\x23\x26\xb9\x1e\x4c\xb7\x2c\x9c\xdc\xc3\x68\x32\xa1\x60\x90\xcc\x51\x6e\x9d\x4b\xce\xd5\xbb\x10\x7c\x1c\x91\x28\x11\xb3\x24\x6d\x97\x13\xef\x21\xc4\x3c\xba\xb0\xed\xd0\xd7\x8f\x60\xc1\x39\x8c\xea\x45\x39\x2a\xcf\xfd\x6f\x66\x4d\xba\x2b\xbd\xd5\xd3\x04\xa9\x48\x75\x15\x8c\xa6\xf3\xd3\x28\x71\x3d\xdc\x14\xe9\x94\x32\xee\xce\x6a\xa0\xd3\x3e\x5f\x54\xc1\x62\x41\x13\x58\x4b\x41\xc2\xdf\x40\x58\xd8\x15\xb5\xd5\xdd\xc3\x08\xc6\x34\x8b\xc6\x8c\x3d\xc9\x5e\xd5\x17\x16\x17\xa8\xe9\x44\xc0\xc2\x3e\x54\x89\x5a\x39\xbc\x3a\xbd\x5f\x15\x5a\x95\xde\x82\x5f\x99\xec\x90\x7a\xec\x8e\x83\x38\x16\xf8\x95\xd7\x38\x7c\x44\xb3\x40\x2f\xdd\x3c\xfa\x55\x38\x17\x84\xeb\xba\x59\x90\xf7\xd8\xff\x92\xd0\xc0\xfd\xaf\xe7\xde\x0e\xe3\x5b\xd9\x82\xfa\x75\xa6\x95\xa8\xf1\x7b\x67\xf2\x2d\x5c\xb1\x2a\xd6\x77\x77\x41\xba\x98\x44\x89\xf5\x56\xa9\x0e\x09\xda\x6b\x91\xa8\x4a\xdc\x30\xdb\x4a\x03\x9e\xbb\x97\x3f\x2f\x3f\xfa\x73\x8d\xaf\xab\xa1\x69\xb0\xcc\x8c\xda\xab\x06\xbd\x0e\xa3\xd6\x2e\x00\xba\xe4\x19\x69\xb7\xc9\xa8\x99\x41\x16\x42\x99\xd7\x2c\x6b\x05\xbc\x31\xde\xcf\x95\x13\x4a\x66\xf4\x3d\xf7\xd2\xfa\x0b\x3f\xce\xe4\xde\x23\x6f\x85\x03\xcc\xf0\x83\x39\x26\x32\x20\xf1\x4a\x2c\xea\xc6\xbc\x28\x04\xbf\x4a\x36\xfe\x7c\xfe\x99\xd4\xf2\xda\x21\xfc\xca\x8f\x94\xd0\x9d\x98\xb0\xce\xea\xa8\x33\xb6\xb5\x12\xdc\xa1\x4d\xc9\x8f\x3c\x99\x10\xc8\x4b\xf8\x06\x58\xa4\xf3\x45\x71\x89\x55\x82\x0d\x36\xd1\xda\x55\x68\xd2\x23\x62\x4f\x23\x90\x3e\x56\xc0\x8d\xf4\x38\x55\xea\x6b\xca\x8b\x89\xca\x81\x88\x2a\xeb\xc6\x60\x5c\xac\x6c\x78\xc4\x82\x9b\x8c\x43\x3f\xc6\x2b\xf7\x0f\xf5\x53\x94\x17\xce\xcb\xbf\x63\x63\x34\x27\x1e\xa7\x50\x95\xa3\xd7\x35\xbb\xdb\x8b\x7a\x17\x24\x6f\xea\x97\x8b\x90\x5b\xb6\x8a\x77\x70\x4a\x15\x59\xa4\x05\x7a\xeb\xca\x0b\x4b\xe1\x88\xfb\x1d\x22\xc6\xdb\x3e\xf5\x84\x50\x80\x9a\xcf\x8a\x8c\xbd\x4d\xad\x47\xbe\x7d\x95\x2c\x48\xfb\xf6\xcb\x76\x16\x62\x36\x4f\x76\x71\x8f\x35\x2c\x1e\xc6\xc6\xae\xab\xe8\x17\xaf\xb5\xdc\x17\x5a\x1c\x52\x8b\x40\x9d\x14\xbf\xba\x55\xaf\xe6\x06\x03\x39\xdd\xf4\x8c\x66\x97\xc5\x0c\x7c\x91\xa0\x7a\x30\x76\x5c\xc7\x53\xd2\x22\xcd\xc1\x8f\xf1\x52\xd7\x7f\x43\xa1\x7c\x2f\xdd\x69\x13\xae\xd2\xf9\xba\x47\xda\x6d\xa9\x7c\xaf\x50\x52\xbc\xe3\xb3\x64\xe9\xf4\x94\xfa\xee\xfa\xa4\xb7\xd9\x28\xd6\xde\x57\xd4\xc9\xc1\x6d\x74\xb5\x52\x2e\x63\x20\x25\x5a\x39\x69\x66\xc6\xfe\xe7\xaa\x32\xf8\xf5\x58\xff\x3c\x41\xc9\xdb\xf8\xc3\xd2\xcd\xb1\x34\xae\x9c\x63\xbf\xa4\x76\x8e\xfd\x7e\x8a\xaa\x43\xfa\x39\xa7\xc6\x06\x1a\x3a\xe7\xee\x7d\x15\x15\x1d\x2b\xbc\x8a\x8e\x8e\xc3\xdb\x4a\x3a\x96\xba\xa2\x96\xce\x2c\x52\xa1\xa6\xe3\x2d\x56\x95\xbd\x89\xa2\x8e\xe1\xb6\x44\x51\xd7\xcc\x51\xbe\xe8\x56\x03\x45\x5d\xa3\x68\x5e\x5f\xeb\x71\x9d\xe7\xf6\x6f\x15\xf2\xe0\xc5\x57\x21\x10\x59\xc2\x26\x11\x9e\xbe\x22\x91\xd8\x85\x2a\xc8\x44\xb6\x5b\x5d\xfe\x46\x3a\x5d\x2e\x49\x35\x79\x33\xe7\x69\xef\x6e\x5f\xcb\xa9\x51\x36\xa0\xbb\xbb\x8f\x3e\x52\xf9\x7e\xc7\xc3\x87\x91\x8b\xdb\x28\x6f\xee\xdb\x76\x4c\xb3\x22\x88\x12\xbf\x7f\x5b\x07\x91\xfc\x36\xa9\x86\xa8\x39\x50\xdf\x4c\xaf\x26\x6b\x51\xc4\xca\xa8\xf5\x06\x51\xd0\x6c\xce\x8e\xfc\xd1\x04\x6a\x36\xfb\x1d\x0a\xaf\xb5\x64\x1a\x9d\xd1\x44\x9a\xb4\x98\x47\xea\x32\x77\xb9\x96\xfd\x0b\x3f\x66\x6b\x8b\x5b\xc0\x32\xaf\xdc\x69\xd7\x6f\x7f\x8b\x21\x9a\x2f\x11\xee\x9c\xb6\x55\x78\x85\xe3\xf4\x8c\x66\xd9\x79\x16\x15\x05\x05\x73\x2f\xde\xab\x16\xd9\x80\xde\x37\xc6\xdd\x39\x68\xd9\x73\xfc\x90\x1f\xac\x20\xf4\x51\x34\x4a\x04\x0a\x0b\xd7\xef\xb0\xfd\xd6\xbe\x11\x32\x5d\xad\xa4\xd5\x9c\xd6\xda\x96\xe0\xcd\xe3\x42\xc0\x8f\xc1\xc1\x00\x54\xe1\xc1\x9c\xad\x0a\xf0\x7a\x28\xb4\x59\x6c\xbc\x8c\x13\x50\x7e\xc7\x10\x47\x9f\x29\x09\x48\x1e\x25\xd3\x98\x2a\x3f\x5c\x00\xd9\x37\x4c\xa2\x81\x82\xb9\x9b\x19\xee\x96\x83\xb7\x76\x75\x45\x8e\xdb\xc7\x9b\x27\xed\x93\xae\x12\x06\x6b\xdc\x00\x88\xee\x99\x78\x67\x5f\xd8\xb5\x61\x89\xe8\xce\x6d\xa0\x38\x2a\xc0\x56\x61\xb3\x47\x1e\x81\x3d\xf6\x10\xfa\xb2\x89\x1d\xd1\xe8\x0e\x39\x82\xac\x74\xd4\xd0\x93\xae\x1d\xca\x4e\x0b\xd2\xa1\xc3\x43\x09\xa8\x1b\x18\x0c\x48\x10\xc7\xe4\x34\xc8\xa3\x31\xf7\x7f\x00\x8f\x05\xb6\xb7\x84\x02\x27\x4e\xd9\xc9\x58\xf6\xa6\x47\xb6\xb7\xea\x8c\x4e\xcc\x85\x2d\x38\x9a\x3c\x81\x4b\x5d\x24\xa1\x53\x10\x20\x21\x28\xd4\xf1\x49\x8b\xec\xfe\x00\xeb\x53\xa7\x3d\xe6\x89\x95\xca\xb4\x3d\x59\xdb\xaa\x1c\x60\x46\x4b\x7b\x56\xb1\xda\x71\xab\xa5\x34\xab\xdd\x7e\x19\x0e\x61\x1c\xa2\xdb\xb1\xb6\x51\x54\xe4\xc1\x03\x82\xbf\x8f\xd1\x6f\xe4\x02\xee\x44\xee\xba\x2a\x32\xc6\x60\x7a\xa3\xb9\x11\xcb\xb7\x6a\x6a\xe4\x2c\x98\x73\x23\x26\xcc\x9c\x1a\xe4\x71\xed\x96\x33\x63\xf5\xab\x62\x62\x50\x9b\x5f\x7b\x5e\xee\x72\x62\x4c\xd7\x27\x9a\x91\xa2\x99\x80\xb3\x51\x0b\x6c\x11\xb6\x38\xd2\xf9\x21\xa9\x25\x8c\x15\x36\xc5\x54\x6c\x3e\x56\x80\x5b\x27\xc7\xdb\x02\x54\xa6\x71\x10\x05\xb1\x79\x62\x25\xe8\x6f\x77\x77\x00\xac\xde\x60\x7b\xc0\x63\x11\x43\xac\xdf\x13\x50\x63\x77\x34\x91\xd1\x84\x74\x50\x16\xe2\x90\x36\x3f\xbe\xe1\xc4\x02\xc3\xf6\xbd\x86\xd8\xac\x98\x72\xb1\x49\xc8\x53\xb5\x6f\x9e\x61\xde\x7c\x53\xdd\x52\xf1\xf7\x9c\x09\x17\x9f\x2d\x63\xde\x8d\x8a\x8e\xcd\xca\xf1\x74\x6b\xef\x6b\x8d\xe6\x59\x65\xf0\xa1\x88\xfc\xd2\xf9\x35\xbc\x28\x96\xee\xf6\xc2\x5b\x51\x1c\xe4\x05\x39\x3e\x61\xc2\x04\xaf\xf7\x46\xd3\xbe\xee\x9f\x77\x35\x07\x20\x67\x11\xc7\xc7\x12\x1c\x68\xf4\x4b\x28\xf8\x54\x34\xd0\x84\x48\x2a\x8c\x63\xd1\x11\x46\x71\x60\xfb\xa6\x89\x9c\x5e\x92\x90\x4e\x82\x65\x0c\x8a\xd0\x7c\xc9\xe4\x54\xb5\x31\xb7\x84\x9b\x9a\x9e\x08\xf3\x68\xcf\xa2\x71\x8c\xba\x01\x03\xd6\x3b\xe2\x8a\xa2\x70\xc3\xd3\x5b\xa9\x51\xbd\xf4\xd5\x2e\x75\xc4\x68\x89\xe4\xf6\x1a\x01\x8a\x17\xa4\x7c\xdc\x62\x14\xdf\x23\x2d\xb6\x08\xd8\x7f\x27\xad\x13\x4d\xed\x02\x02\xa5\x41\xa1\x64\x19\xdb\xcf\x1e\xd0\x6c\x36\x42\x9b\xed\x60\xce\xea\x6f\xcd\x42\x70\x9d\x54\x39\x2b\x81\xef\x0d\xc2\x59\x1e\x9f\xf5\x1c\x6e\x78\xd9\x70\x8c\xf1\xb2\x7f\x61\xd5\x5b\x44\x2c\xb8\x55\xe7\xdf\xc7\xfc\x34\xfe\xef\x93\x6e\xbd\x88\x20\x94\xb7\xca\xdb\x43\xf9\xbd\x83\x15\xc6\x42\x42\x37\x67\x1d\xf2\xed\xa9\x7b\x97\x65\xe1\xcc\x73\x69\x21\xee\xd1\xed\x8d\xc1\xeb\x8f\xda\xbc\x95\x11\xae\x50\xa5\x13\x54\x9b\x2d\xd4\x78\x83\x55\xf6\xdf\xd8\x98\x78\x87\x94\xfe\xf9\x1d\xa3\xba\x7e\x65\x69\x3c\xc1\xfe\x64\x05\x2b\x73\x0a\xa9\x97\xc9\xc7\x27\x3e\x27\xe2\xfd\xc5\x32\x9f\x75\x1c\xcf\xa4\xf2\xa5\xb6\x74\x33\xea\xd6\xcc\xc6\xe2\xfa\x5c\x3f\xf3\x39\x00\xc5\x2d\x21\x3f\x9e\x9d\xb3\x1e\xc1\xfe\x65\x2d\xf7\xa4\xb7\x72\xea\x2b\x26\x10\x3b\xf3\xbd\xf5\xfc\x41\xd7\x1d\xa9\x43\x20\xfe\xb7\x9f\x3f\x9f\x47\xd6\x1a\x4f\xac\xa5\x13\xc1\x66\x13\x5c\xa5\x56\xcc\xc7\xca\xb3\xb1\xe6\xdc\x11\x5a\xba\x23\x63\x49\x22\x8f\xb6\x4d\x7c\x82\xf2\xfb\xd1\x49\x96\xce\xbd\xe6\x06\x1c\xca\xc7\x5b\x4e\xed\x07\x3b\x96\x81\x90\x61\x19\xb4\xc2\x83\x29\xc9\xd4\x78\xcb\x0d\x58\x94\x18\x08\x66\x51\x86\x3f\xcd\x1a\x56\xf5\x55\x78\x15\xec\x4d\xf8\xc6\x92\x0b\xba\xe2\x89\x0f\x74\x4f\x0a\x3a\x02\x5d\x0f\xc9\x16\x18\x3f\x74\xa5\x47\x67\x81\xbc\xb2\x45\x54\x59\x27\x6e\xde\xa9\xd8\xb7\xa2\xa0\xc0\x87\x82\xdf\xb1\xe3\xd2\x1b\x64\x9b\x3b\xbd\xe7\xbb\x6d\xce\x40\x72\x12\x4c\x0a\x9a\xa9\x45\x82\xfb\x7b\xa3\xb5\xea\x2f\xe3\xf3\xdd\xad\x39\x47\x89\xcf\x6e\x52\x89\x3d\x11\x3a\xe6\x6d\x59\xfd\xd8\xaf\x47\xa9\x1b\x69\x3b\xe6\x4d\x25\xa3\x69\xc8\x69\xc8\xc3\xea\xbe\x31\xd8\x8d\xdd\x6a\x98\x46\x8c\xca\x74\x38\x8b\xa6\x7d\x83\x44\x77\xcb\xb5\xfe\x10\x7b\x08\xfe\x6b\x48\xfd\xd2\x20\xb5\xe1\xdf\x1f\x8a\xf8\xef\x69\x1f\xfd\xfd\x2e\xb4\x4f\xbc\xa4\x8f\x03\x34\xde\x94\xf4\xed\x30\x62\x2b\x6e\x2a\x0e\xb1\xda\xf5\x37\xdb\x59\xcc\x5e\xac\x52\xbf\x98\x3f\x2f\xbd\xc5\x0e\x7d\xf9\xd7\x5f\xf9\x12\x5e\x88\x5b\x3f\xd7\x48\xb5\xae\xfb\x1d\xb2\x49\x36\xcc\xde\x75\xb9\x4f\x26\x1e\x49\xcc\x33\xf5\xdc\x03\xb1\x75\xe9\x66\x3c\xd8\xae\xf0\x67\x6f\xe0\xda\xb2\xf8\x32\xb8\xd8\xda\x8a\x63\xc3\x73\xae\x56\xd6\x56\xd7\x54\xab\x7a\x2f\x12\xad\xae\xd7\x5e\xf0\x96\x5f\xed\xaa\x37\x71\xd7\x27\xbd\xcd\xdf\x3b\xf4\xfe\x51\xfd\xb3\xb7\x65\xc5\xbb\x37\xe1\x89\x04\xfe\xe7\xb6\x2e\x4b\xfd\xf4\x6d\x89\xde\xbe\x2d\xf1\x83\xb5\xa5\xe7\xf5\xdb\x52\x3d\x7f\x5b\xa2\xf7\x6f\x4b\xf4\x00\x6e\x69\xbe\x80\x73\x6a\x6c\x60\x61\xe3\xf8\x47\xf9\x8a\x8f\xe0\x8e\xbc\xaf\xe0\x8e\x56\x7f\x06\x77\xd4\xf4\x1d\xdc\x91\xfb\x10\xee\xe8\x0e\x5e\xc2\x2d\x6f\xfd\x14\xee\xa8\xf1\x5b\xb8\xdf\x3b\xae\xff\x51\x03\x8b\xb3\x65\x95\xc9\x99\x74\xad\xc2\x7f\x08\xe2\x44\x56\x67\x4b\x6c\x76\xb6\x34\xac\xc4\x96\x3e\xc3\xb3\xa5\xb6\x3c\x5b\x62\xd3\xb3\x25\xb6\x3d\x5b\x5a\xc6\x67\x9e\x7a\x9b\x2c\x8e\xdf\xd4\xfe\xec\xc8\x6f\x80\x76\x74\x03\x0b\xb4\xa3\xc6\x26\x68\x47\x1e\x1b\x34\xbb\xf4\xcd\xd6\x48\x85\x19\x5a\xd3\x45\xd2\xdc\x10\xed\xdb\x26\xab\xa4\xbd\xcc\x29\x28\x66\xc7\x45\x9b\x07\xe4\x9b\xa6\x84\x26\x67\x24\x4c\x29\x58\x2b\xc0\xeb\xc0\x20\x09\xc1\x87\x2d\xf9\xe7\x9b\x9f\x5e\x17\xc5\xe2\x3d\xfd\x7f\x4b\x9a\x17\x6b\x20\x98\x5d\x2e\x68\x3a\xb1\x72\xb8\x1f\x1b\xf5\x7e\xa3\x2d\xf1\x22\x1a\xee\xdb\xd0\xe4\xcb\xf5\xce\x9a\x11\x2c\xb2\x14\xd2\x4c\x00\x49\xfd\x97\x7c\xc6\x76\x9f\x68\x9a\xa4\x19\x1d\xc5\x51\x42\xd7\xae\xb9\xc5\x2a\xc3\x43\x23\x6f\xf7\xf7\x2f\x67\xef\x5f\xce\xfe\x89\x5f\xce\xf2\x57\xb3\xc2\x86\xcd\x78\x36\xcb\x37\x1c\x72\xb3\xd7\xb3\x62\xef\x3b\x2a\xa2\x18\xea\xe4\xfa\x4c\x58\x3b\xfc\x79\x92\x03\x16\x15\x97\x8a\x25\xea\x22\xe3\x38\xc8\x73\x72\x0c\x45\x4e\x44\x37\x79\x86\x66\xc2\xbc\xaa\xb5\x01\xdc\x1b\xc1\x2a\x15\xca\x55\xc6\x41\x48\x85\x33\xeb\xe6\x7e\xce\x01\x92\xd5\x74\xf4\xf6\xe0\xe3\x07\x76\xb6\x86\x49\x68\x9f\xd3\xa8\xcd\x49\xb3\xfd\x19\xfd\x7e\x83\x7e\xff\x88\x7e\xe7\xbf\x06\xa7\xa9\xfc\x98\x44\x49\x42\x2f\xd5\x17\x9d\x17\x29\x3c\x65\x94\x29\x8b\x68\x6c\x26\x24\x41\x62\x26\xcc\xa3\x71\x66\xa7\xc4\x71\xe4\x14\x32\xe0\x0d\x50\xf9\x61\x14\x99\x66\x41\x12\xaa\xa1\x18\x59\x3f\x1a\x5f\x1f\x8d\xaf\x77\xc6\xd7\x4b\xe3\xeb\xff\x8c\xaf\x7f\x19\x5f\x6f\x8d\xaf\x17\xc6\xd7\x3f\x8c\xaf\x23\xfe\xb5\x76\x52\xee\xba\x86\xcd\xd1\xbb\xbd\x17\x6c\x8a\x47\x64\x7b\xab\xa7\x12\x3f\x1c\xfc\xf8\x76\xef\xe3\xd1\xfb\x97\x9f\x7e\x7a\xf9\xf6\xc7\x8f\xaf\x47\xe4\xb1\xce\x84\x59\x1d\xe9\x9f\x3a\xa7\x84\x72\x46\xe4\x0b\xb1\x12\xb4\x1f\x75\xc8\xf8\xf4\xe2\xf0\xe7\xb7\xe4\x5a\xd7\xf4\xee\xf0\xa7\x9f\x18\xf4\xc7\x83\x37\x2f\x0f\x8f\x3e\x8e\xc8\xe6\x70\x38\x1c\x88\x1e\x8a\x1b\xef\xe7\x71\x3a\xfe\x3c\x22\x6d\xc6\x3a\xf3\xa2\x6d\xe4\xed\x8d\x21\x94\xf1\x48\xbf\x6d\xe4\x0f\x30\xd8\x7e\x5e\xe7\xfb\xe4\x3e\x14\xc6\xfd\x46\xf6\x57\xdf\xc8\xd6\x94\x0b\x88\x7c\x16\x6c\xdf\x95\x07\x88\xfd\xec\x72\x51\xa4\x7f\xff\x80\x37\x87\x31\xa4\x3d\xd2\x11\x30\x58\x83\x5e\x80\x01\xcb\x69\x7b\xa3\x3b\xb9\xee\x1b\x80\xe2\x72\xfc\x40\x55\x24\x91\x07\x0f\x64\x6e\x5f\xfa\x8b\xe0\x62\xf2\x8c\x5e\xb4\xed\x57\x74\x86\xe7\xaf\x1f\xc8\x16\x2b\x6d\x7b\x3f\xde\x92\xee\x22\xcd\xe2\x44\x5e\x86\xab\x0b\x7e\xcb\x3f\x3b\xb1\x5e\xdb\x71\x50\x89\x23\xd6\xb9\xfe\x6b\x7a\xd1\x07\xed\xa5\xf0\xdc\xeb\xb3\x31\x62\x58\x91\xc3\xd6\xad\xf3\x13\x1d\x57\xbf\x8d\xc8\xd6\x37\x4f\x78\x49\xf4\x38\x59\xbe\x39\x63\x2c\x4f\xe1\xb8\x35\xfa\xe6\xbb\x5e\xcb\x44\x79\x6b\xf4\x74\x78\x7d\xd2\xdb\x6a\xe4\xf3\xe9\x9e\xef\xdd\xf3\xbd\x3f\x2f\xdf\xd3\x6c\x8f\xbf\xf3\xbf\x03\xbe\x67\xc9\xee\xab\x8b\xee\x1e\xc9\x5d\x16\xf4\x09\xee\x2b\x45\x1b\xb2\x79\x6d\x7f\x20\xd8\xbd\x0e\x47\x34\x79\x8a\x01\xd8\xb7\x12\xe1\x97\x49\x54\xbc\x09\x16\x4a\x5c\x6c\x4b\x89\x7a\xc4\x79\x50\x7b\x28\x65\x4d\x26\xb5\x8f\x34\x5b\x6c\x6f\x1a\x72\xfe\x08\x65\x0c\x87\xaa\xd0\xff\x56\xe4\x9d\x06\xa7\xa7\xc1\x94\xaa\x96\x70\x1e\x12\xfe\x47\x76\xde\xdc\x53\x27\xca\x7e\x53\x9d\x1d\xa7\x67\x34\x0e\xc6\xb2\x59\x3b\x5b\x9f\x31\x46\xbe\xec\xa9\xbf\x72\x04\xf1\x63\x2d\x44\x3e\x0b\x92\x24\x4d\x8c\x71\x9b\x10\xfa\x5c\x33\xaa\x80\xa8\x69\x05\x4e\x56\x23\x0f\x04\x46\xa5\x3e\x2f\x8d\xaa\x81\xea\x6a\x12\x67\xb7\x91\x17\xc8\xa8\x4c\x9d\xc7\xec\xb1\x79\x00\xfd\x43\x34\x01\x0d\x72\xf5\xc0\x21\xd0\xcf\x26\xac\x0f\x14\xcf\x35\x9c\xfa\x2a\x2b\xc6\xfd\x6d\x54\x37\xae\xbe\x69\x01\x54\xa6\x58\xa1\x0c\x2b\xe6\x37\xb6\xd2\x8e\x18\x16\x41\x28\x4c\x49\xc1\xd4\xf3\x62\x41\xc7\x6c\xf3\x52\xe6\xf9\xd8\xe8\x4a\x78\x4f\xf1\x59\x4e\xe9\x2a\x4e\x29\x83\x0b\x45\x44\x2e\xcb\x06\x6b\x3c\x0b\xb2\x60\x5c\xd0\x2c\x97\x2a\x7e\xb8\x97\x17\xa5\xd1\x3e\xe2\x6d\x23\x9a\x26\x3d\x64\x0b\x4d\x86\x6b\x7e\xb7\x1f\xd1\x74\x56\x10\xe9\x91\xd6\xf2\xee\x2b\xc6\x60\x48\x9b\x1c\xa4\x07\xbd\xcb\x7b\xd0\x8e\xc7\xc7\x10\xb7\x10\x01\x18\x08\x4a\x0b\xaf\x55\xd5\x0d\xf1\x66\xb7\xff\x4b\x1a\x25\x10\xac\x81\x3c\x83\x3a\xc8\x88\xb4\x86\xad\x2e\xd9\x10\xc0\x25\x86\x6f\x37\x9e\x0b\x08\xd8\xf3\x67\x9f\x0c\x18\xc4\x8a\xb3\x21\x7a\xb8\xc1\x3d\x2e\xdf\x74\x5e\xca\x0c\x11\x4d\x47\x34\xb0\x75\x82\x19\x22\x04\xf3\x70\x7d\x4c\x5b\xf3\xc2\xbd\x35\x57\xcc\x4a\x94\xb0\x4a\xfc\xc8\xc2\xfe\xa8\x3d\x8e\x92\x58\xe3\xda\xec\x90\x7b\x20\x39\xe2\x5b\xbb\x12\xe9\x67\x3c\xde\xf3\x60\x40\x5e\x45\x49\x48\xf8\xe3\x2e\xd1\x51\x15\xaf\x99\x49\x14\xad\x96\xbe\xc9\x07\xdb\x97\x1e\x84\x90\x9a\xd1\x0b\x69\xc2\xac\xce\x5c\x2c\x8d\x9f\x7a\xd8\x89\xa3\xfc\xac\xc4\xaa\xd9\xc2\xef\x5e\xc0\xb8\x46\xd8\xd4\xec\x90\x68\x63\x77\x0b\x83\xcb\x58\xc8\xd8\xb6\x43\x37\xd5\x89\x58\x3b\x22\xf4\x85\x6a\x61\x42\x3a\xbc\xc8\xee\x2e\x19\x76\x8d\x53\xda\x69\x46\x83\xcf\x1a\x94\x8d\x72\x63\x97\x88\x57\xe5\x6c\x06\xf7\x67\x41\xb6\x9f\x86\x14\x6a\xf0\x1e\xc2\xd8\x64\x4b\x73\x9c\xbc\xc8\x9a\x51\x08\x9f\xb4\x95\x48\x64\x8f\x15\xf9\xed\x68\x04\x9a\xfb\xef\x21\x92\x9b\xcc\x7c\x5e\x94\xbd\x4e\x37\x27\xdb\xe3\x63\xbe\xb3\xc8\xe8\x24\xba\xe0\x41\xb4\x86\x17\x5d\x36\x0b\xc0\x35\xfc\xee\xed\x45\xb4\xb7\xf2\xd9\xf7\xda\x2e\xc3\x11\x34\x88\x81\x9b\x57\x06\x13\xf0\x45\xf9\x34\x7c\xed\x0b\xb7\xeb\xa2\x1b\x98\x2a\x18\xc5\x0b\xcc\xf3\xd9\x87\xe5\x20\xcc\xb6\xf9\x72\x90\x33\xc2\x5a\xd2\xd4\x31\x49\x33\xdb\x84\x2e\x2f\xb2\xb2\x88\xf8\x68\x46\x19\xd4\x58\xcc\xcd\x5e\xd1\x89\x6e\xb6\xd2\xc1\x3a\x51\x04\x07\x37\xbc\xb6\x69\x10\xd6\xdf\x8d\x5d\x92\xc8\x7d\xe1\x7b\xb2\x45\x9e\xb1\x93\x0d\xd9\x20\x6c\x3f\x48\x7c\x34\x21\x5c\xc8\xcf\xe8\xc5\x5d\x92\x86\x15\x73\xc0\xa6\x8d\x1a\xd6\xf0\x9b\x11\x87\xc3\x33\x10\x75\xfc\x36\x14\xf0\xbb\x4d\xab\xe5\xb1\x74\xb2\x8c\x63\x85\x86\x01\x3d\xa3\x49\xc1\x1f\x0a\x00\xcb\xff\x25\x4f\x13\x12\x9c\x46\x36\x8f\x97\x6e\x13\x3f\xa6\xaf\x96\x71\x6c\xbf\xa1\x94\x8f\x09\x58\xe9\x47\xbc\xb4\xfb\x18\x8a\x37\xec\xb4\xab\x19\xbb\xdb\x86\x21\x48\xb1\xca\xb1\xea\x94\x7d\xf7\xc1\x84\x22\x4a\x42\x7a\x71\x38\xe9\xb4\x3b\xed\x2e\xf8\x86\x7c\xb4\xe9\x79\x0e\xa9\xe0\x1d\x3b\xc1\xe2\x72\x41\x45\x73\x00\x04\x54\x64\xfa\x33\xeb\x44\xdd\x2f\x32\x84\x70\x9f\xc1\xef\x90\x6b\x21\x8a\x99\x96\x7f\xaa\x15\xb2\x41\xda\x1d\x36\x73\xaa\xf6\x0d\xd2\xee\xb6\x1b\xad\xbd\x30\xca\x17\x71\x70\xc9\xe7\x05\x7c\x8c\x26\x05\x93\x6d\x15\x36\xec\x37\x6b\x17\x90\xfd\x82\x17\xab\x7a\xe1\xca\x6a\x33\x27\xdf\xbf\xbc\x8c\x1e\xb0\x2d\xcd\xa2\x18\x3a\xed\xcb\x78\x8b\x97\x1d\x61\x56\xd7\x25\x8f\x7e\x50\x89\x6a\x5a\xdd\xbe\x55\x3e\x7c\x56\x36\x9b\xce\xcc\x1a\x68\x16\x60\x7c\xb2\xc9\x33\xfb\x4d\xab\x78\x0f\xc6\xd6\x8c\x76\x36\x32\x18\xe8\x81\xa6\x67\x34\x8b\xd3\x20\xa4\xa1\x52\x04\x7b\xd6\x04\x1e\xc0\x47\x4d\x24\x65\x6f\x1a\x07\xe4\xe3\xe1\x8b\xc3\x11\x99\x07\x9f\x41\x35\x1c\x25\x67\xcb\x38\xa1\x59\x70\x1a\xd3\xbb\x1c\xa0\x3e\x0d\xd8\xaf\x77\x37\xc9\x23\x82\xb2\xbb\xdd\x7e\x46\x17\x71\x30\xa6\x9d\x36\x69\x83\x53\x37\x76\x5a\x68\x99\x41\x22\xd3\xe4\x8c\x66\x45\xae\x43\x6e\x82\xdc\x17\xd2\x71\x34\x0f\x62\x9b\xc9\x46\x89\x9f\xd9\x17\xe9\x0b\x5e\xc0\xa5\xbc\xca\xf0\x99\xa6\x5b\x43\x2e\xe0\x89\x9a\x6a\x03\x40\x16\xa9\x1b\x1f\x53\x85\x9f\x69\x32\xc6\x5a\xd9\x96\xf1\xc4\xbb\x1a\x17\xaa\xab\x3a\x38\x6b\x22\xb5\xa4\xee\xf8\x3c\xa1\xb9\x85\xfa\xd4\xdc\x51\x8c\xc3\x3e\x07\x88\x69\x9e\x7f\x9c\x05\x49\x67\x08\x4e\x64\x1f\x71\xab\x73\x61\xbd\x2f\x08\x6b\xb3\x0b\xe1\x5b\x51\x8e\x81\xc5\xbd\x25\xb8\x69\x16\xa8\x0c\x92\x4b\xe1\x78\x47\xb8\x23\x4d\xca\xd1\xda\x17\x78\xdd\x4b\x42\xae\xfe\xe7\x34\x14\x4d\x2e\x73\xe1\x48\x3d\x27\xa7\x74\x92\x66\xb4\xef\xd0\xd5\x6b\x71\x74\xa8\xc6\xfd\x95\xd8\x83\x6a\x48\xeb\x35\xec\xf3\x06\xf2\xd5\xfa\x7d\x28\x4c\xc5\xe6\xc1\x05\x0f\x5b\x79\x11\x15\x97\x23\xf2\x14\x54\xd8\x72\xd7\x89\x72\xe1\xd2\x18\x8a\x76\xed\x4d\x06\x4d\x72\x67\x83\x41\xec\x18\x45\xf1\x74\x56\x17\xb6\xca\x0a\x43\xba\x33\x46\x3b\xec\x14\xc2\x91\xd6\xf6\x56\x01\xf1\x95\xfe\xfe\xe1\xf0\x6d\x5f\x61\x99\xb7\xa7\x1d\x58\x82\xeb\xd8\x9c\x04\x76\x34\xcf\x1e\x59\x04\x79\xce\x78\x57\x31\xcb\xd2\xe5\x74\x66\xae\x00\x35\x10\x41\x6b\x50\xab\x7b\x39\xa9\xb9\xda\x23\x38\x2d\x79\x64\xde\xd2\x11\x4b\x00\xf1\xb6\xc3\xac\xae\xa6\xb6\x33\x69\x3f\x8a\x2a\x20\x9d\xf5\x28\x7f\x15\x25\x51\x41\x2d\xa4\x5b\xdd\x00\x09\x11\x75\xc2\x94\xb2\xdc\x8e\xa2\x75\xf1\x5e\x6c\x2a\x7c\x1d\xb0\xf3\x52\x02\xdc\x9f\xfc\x4c\x6d\x41\x6a\x4a\x0b\x88\x58\x7c\x38\x39\x4a\x22\xaf\xb6\x0b\xca\x16\x33\x2a\x7e\xa8\x05\x47\x8a\xb4\xa7\xb4\x53\xca\x21\xba\x37\x6a\xa3\xea\x87\xaa\xa6\xc3\x3b\xd3\x85\x22\xe0\xb6\x2b\x27\x34\xcb\xd2\x4c\xba\xa4\xe1\x3d\xce\x49\x92\x16\x64\x9c\x66\x19\x1d\x17\xa3\x73\xb5\x6e\xcc\x5e\x1b\x0b\x88\x15\x94\x24\xb0\xe4\x99\xf0\xdf\x33\xf8\xaf\x5f\xa4\x3f\xa5\xe7\x34\xdb\x0f\x72\xda\x01\xe6\xc2\xf5\xbd\x9a\x8f\x31\xa8\x7f\x88\x5b\x66\x71\x75\x73\xcc\xfe\x3f\xd1\x47\x71\x04\x82\xfd\x7e\x63\xc2\xe3\x9e\xc8\x12\x7a\x4e\x5e\xb2\x51\x75\xda\x70\xd5\x0b\x1d\x01\x5b\xd5\x7f\xb7\x0b\x42\x2f\xa2\xbc\xc8\x7b\x64\x11\xd3\x20\x07\xb1\x18\x46\x9e\x26\x0a\x55\x93\x34\x8e\xd3\xf3\x28\x99\x42\xc9\x9c\x71\x41\x6b\x19\x89\x1e\xf6\xc0\xbf\x42\x4f\x3f\xfb\xa8\x88\x12\xab\x7a\x0f\xde\xaf\x4c\xaf\xc2\xc1\x67\x0a\x8b\x90\x33\x7c\xb8\x8c\x8e\xc0\x9e\x56\x31\x59\x4e\x02\x8c\xd5\x82\xaf\x0a\x3e\xf1\x1c\xb5\x82\xb2\xde\xa5\x79\x1e\x9d\xc6\x7c\x0a\xc1\x85\x86\x30\xea\xfb\x70\xc0\xe4\xcb\xac\xe0\x3f\x99\x48\x2d\xb1\xf5\x72\x32\x89\xa6\x97\xe2\xe3\x50\x92\xd2\x23\xf2\x99\x35\xcf\xff\xf4\x75\x15\x7c\x8a\x9b\x2d\x0e\x36\xd7\x60\xea\x72\x89\x7f\xca\xab\x28\x0e\x37\xd5\x70\xea\xfe\x87\x7f\x8a\x0b\x23\x9d\xc7\x0b\x3c\x7a\xa4\x16\xa6\xbe\xc7\xe1\x05\x7e\x0d\x4e\x53\x23\xcf\x53\x42\xde\xc3\xf0\x01\xc0\xf5\x0d\xce\xe3\x25\x50\x2f\x50\x61\xfe\x29\xb0\x80\x40\x88\x05\x81\x3e\xe0\x32\x45\x20\x84\x6a\x1c\x4e\xd1\xef\x42\xfe\xb6\x45\x0a\xce\x17\xac\x93\xef\x95\x92\xd3\x39\x39\x8c\x83\x84\x9d\x0c\x02\xc5\x9a\x45\xba\xd0\x95\xa5\x19\x09\xc8\xeb\x97\xff\x84\x43\xb8\x94\xd6\xee\x8c\xa1\xa8\x7d\x56\x1e\xed\x7e\x9e\x51\xe9\x67\x2f\x40\x57\xb9\x22\x0a\x0a\x0a\x16\xc0\xd6\x53\x90\x93\x73\xca\x16\x88\x76\xb0\x22\x87\xb1\x86\xa4\xa1\x9f\xa9\x71\x24\x97\xe3\xc4\x2c\x85\x8b\x3a\xac\x66\xc9\x24\xb0\x50\xc4\x4b\xe0\xa8\xb1\x26\xa7\xe2\xdc\xc9\x92\x87\xf0\x36\x2c\x2a\x20\xcf\x8c\x46\x46\xf8\x0b\x49\x56\xb5\xcb\x37\xe0\x38\xf6\xac\xe0\x73\x1a\xdd\x2f\xd8\xff\x96\x25\x5e\xa4\x55\x0b\x1c\x9d\x17\x7e\xb3\xa5\xce\x56\xdb\xef\xb8\xd8\x01\x21\x77\xb3\xd4\x8b\x68\x4e\xf3\xdf\x63\x99\x27\x42\xb9\xc8\x16\xb7\x52\x55\xe5\xfc\x98\x0f\x5b\x34\x51\xb6\x2c\x0e\x39\xa8\x9e\x34\x22\x0a\x4d\x06\xf2\xee\x90\xcd\xbd\xa6\x05\xb3\x36\xe5\xe5\x4a\x57\xa0\x01\x14\xfe\xb1\xf1\x8d\x35\x0b\x35\xe7\x9f\x6f\x98\x10\x08\xcb\x5e\x96\x17\x3f\xae\xae\xc8\x70\xc7\x7b\xb8\x11\xf5\x3a\x87\x13\x9e\x6e\x9c\x88\x04\xce\x65\x4f\x1e\x3c\x20\xe2\xb7\x4f\xe8\x67\x4d\xda\xb9\xf8\x84\xe1\xf3\x81\x66\xc8\x62\xa2\xb0\xd2\x89\x0c\x2f\xda\xbd\x76\x1b\x5f\xb8\x58\x9e\xd2\x7c\xa5\x31\xa1\x94\xca\x74\x89\x8c\x1d\xeb\x21\x15\x45\x27\x1c\x4c\x46\xf1\x50\x47\x31\x61\x36\x09\xb0\xc5\x79\xda\xce\xc9\x58\xc5\x74\x71\x48\xcb\x0c\xf9\xd2\x84\xbe\x4a\xa8\x06\x1d\x92\xcd\x3a\x4d\x85\x97\x41\x32\x0c\xfc\x14\x51\x96\x6f\xc1\xc2\x93\xef\x0e\xf2\x5a\xa7\x0a\x60\x95\x44\xed\xd4\xb5\x26\xb7\xfc\x6b\xc1\x2c\xf7\x17\xf1\x32\xd7\x5d\x10\xdf\x5e\xf7\x86\x0a\xc8\xd4\x24\xcd\xe8\xf8\x73\x2e\x8f\x4d\x9c\x47\xca\x6b\xce\x5c\x3c\x96\x8b\x2f\xc1\x8f\xaf\x37\x1a\x31\x27\xf9\xb1\x37\x12\xb1\x19\x53\x18\x35\xc0\xd6\x7f\xa0\xe1\xb1\x63\x3b\x08\xae\x24\x66\xce\xaa\xdb\x98\x38\x51\xa9\xa5\x41\x1b\xfc\x67\x78\x71\x3c\x7c\xf4\x5d\xf0\x68\x72\xf2\xe5\xf1\xf0\xfa\x7f\x06\x51\xbf\xa0\x79\xa1\xc0\x57\x18\x7b\xc5\x90\xbf\xce\x60\x1b\x0c\x13\xce\xff\x83\xff\x74\x86\x17\xdd\x67\x95\xe3\xc4\xf4\x37\x18\xe8\x58\x59\x3c\x1a\x16\xf4\x8e\x7b\x10\x16\x46\x87\x73\x78\xc7\xcb\xf6\x63\x34\x6a\x93\x7e\x85\x23\x40\x62\xba\xaa\xf0\x76\xc6\xec\x0b\x63\x73\x08\x6c\xef\xd1\x2b\x2f\x98\xd5\x65\x08\xdd\xd5\xce\xc1\xd9\x71\x3e\x67\xff\x8e\x83\x45\x0e\xb2\x43\x1c\x13\xf9\xdd\xc3\x1e\x1a\xed\x1e\x73\xc7\xf3\xa8\xc3\x46\x03\x87\x6a\x7b\xe7\xd8\xa1\xc1\x78\x46\xc6\x41\xee\x54\x13\xe5\x9c\x50\x96\x73\x31\x43\x88\x9a\xf8\x2a\x6b\x4e\x53\xbc\xad\x7c\x39\x9f\xd3\xb0\x94\xbc\xac\xe6\xee\x98\xcc\xac\xda\xab\xc8\x6d\x30\xe0\xe3\xb1\x70\x13\xa8\x92\xe2\x97\xb3\x03\x69\x7d\x88\x80\x78\x1d\xe4\xe0\x8c\x66\x16\x6c\xcb\x46\x4c\x5d\x8a\x94\x76\x7c\x0e\x5f\x1e\x0f\xe1\x8e\x92\x58\x14\x02\xce\xbb\x8b\x19\x89\x29\x3c\xa7\x46\x11\xf8\x16\x0b\x9a\xb1\xde\xca\x69\x48\x20\x7a\xe1\x34\xe2\x01\xee\x82\x9c\xce\x83\x05\x9b\x8e\x4d\x43\xd3\xd7\x51\x16\x0c\xa8\xd3\xe0\x96\x6d\xf3\x49\x97\xfc\x40\xbe\x65\xdb\xb9\xc8\x3a\x8e\x4e\xfa\x45\x7a\xc4\x1a\x12\xba\xa0\xf5\xdd\x5d\x94\x09\x44\x5f\x5d\xe1\xf7\xbb\x9e\x1a\xb1\x76\xc9\xaa\xb1\xc4\x57\x38\x5a\x96\x9a\xe5\x1b\x8c\x5f\xc7\x5f\x50\x54\xfa\x46\x1c\xf5\x24\x35\x96\x90\x62\x91\xde\x25\x29\x4a\xed\xb5\xda\x97\x57\xa0\x44\xa4\x33\x56\xd4\x67\xbf\xba\x16\xed\xb4\xdb\x82\x94\x5c\x32\x35\xf0\x7b\x23\xa2\x45\x40\x63\xa7\xf7\xac\xa2\x0a\x32\x96\xbd\x40\xd7\xee\x36\x49\x03\xd3\x9b\x69\xd3\x3f\x46\xa4\xdf\xb1\x83\xcf\x84\x3b\xd0\x97\x37\x71\x8a\xc2\x0d\x02\xae\xa3\x5f\x93\x82\xec\xfe\x6f\xec\x96\x12\x37\x22\x2f\x9b\x91\xd6\xd6\x54\x49\x9a\x56\x49\x53\xf2\xd4\x92\xa6\xc1\x46\x8b\x94\x49\x94\x51\x48\xb6\x86\xdc\x67\xd0\x23\x71\x41\xc8\xdb\xe4\xef\x13\x86\x17\x84\x1b\x77\xb8\xc6\x5d\xb5\x94\xec\xbf\xed\x17\xde\x07\x30\xd7\x56\x06\x5c\xcd\xe8\xd7\x12\x67\xbc\x1b\x9f\x74\xaa\x2b\xf1\x81\x64\x78\xbe\xdb\x56\x6d\xb4\x9e\x8a\xc4\xe5\x97\xaf\x3e\x13\x42\x86\x5e\x84\x2b\x25\x55\xa3\x7e\x4d\xd5\x23\x8f\x87\xfe\x5b\x02\xe9\x88\x58\x9e\xa6\x73\x2d\xe5\xd6\x07\xd9\xf4\x9e\x24\x7d\x57\x5f\x46\xe0\x4d\xbe\x91\xf9\xce\x80\xa4\xc3\xbb\x61\xc9\x85\xb2\x6f\x49\x5e\x04\xc9\x98\x71\x11\x5d\xf8\xea\x4a\x21\x4d\x14\x86\xd7\x6b\xf0\xcb\x70\x9c\xe1\x4d\xe5\xb6\x11\xc0\x8b\x54\x95\xed\xa6\x88\x92\xe7\xe1\x3a\x2c\x7d\x70\x8c\x8b\x1a\xa2\xc8\x13\x22\xc9\x8b\x1f\xc1\x5a\x45\xcf\x60\x34\xbc\x6f\xed\xbb\x43\x0f\xef\x4b\x63\xdc\xc8\x1e\xd7\x63\xe7\x95\x36\x22\x59\x15\x3f\xb2\xe8\x8d\x30\x24\x4b\xb4\x1b\x8e\x88\xf5\xa9\xa8\x1f\x0e\xef\xfa\x0d\x06\x73\x28\xfa\xd6\x70\x31\x30\xf1\x22\x59\xc6\x31\x44\x49\xe8\xb8\x2b\x04\x0c\xb7\x41\x85\xe1\x19\xbb\xb8\xaf\x6d\x38\xf2\x53\xde\xd9\x06\xec\x80\x03\xde\x84\x19\xf0\xa4\x1b\x4d\xa4\xe8\x5e\xd3\xd1\x80\x0b\xc0\xfa\xb1\x38\x11\x35\x1a\x8e\xc4\x8d\x8a\xd1\x90\xa5\x41\xc1\xca\x31\xd8\xc7\x11\xbe\x8f\x82\x8d\x5c\x2a\xa9\xce\x1c\xc4\xdf\x73\x73\x5d\x69\x0b\x84\xca\x31\xb0\x62\xf6\xab\x01\xe5\x3a\x29\xbb\x74\xf7\xa9\xf5\x75\xb8\x99\xe4\xcf\x70\xb5\x31\xeb\x35\x19\x43\xd8\xa7\x0e\xf5\xec\x6d\xf8\x40\xba\xca\xa8\x03\x31\xee\x97\x6c\x02\xe9\x72\x4e\x4e\xe3\x74\xfc\x99\xcc\x68\x10\xd2\x8c\x7d\xa4\x73\xdb\x6a\x23\xca\x9f\xb3\x64\x9f\xd0\x30\xa3\x17\xca\x2f\x3a\x94\x25\x93\x28\x2e\x6c\x65\xa6\x87\x60\x01\xd6\x70\x3f\xcc\x52\x2a\x4f\xfa\xdf\x6c\x6e\xe9\xa3\x3e\x07\xaf\xc1\x4b\xf9\x41\x9d\xd7\x85\xab\xf2\x9d\xd3\x5d\x28\x5f\xc4\x61\x7d\xce\x5e\x73\xfb\x71\x83\x99\x89\x53\x26\xe6\x2d\xa2\xb1\x3b\x0f\x1f\x59\x72\xdd\x3c\x14\x0a\xa8\x62\x02\xa0\x26\x63\x02\xa0\x58\xe5\x04\x3c\x79\xac\xf1\xcf\xa1\x6f\x8c\x7f\xa8\x0a\xd7\xe4\x43\xbf\x03\x74\x23\xec\x97\x38\x1e\x11\x22\xdf\x48\xfe\xe8\xc9\x54\x78\xf4\x33\x52\xbf\x78\x3a\x08\x86\x23\xfe\x9f\x4c\x11\x16\x24\x23\xfd\x93\xe7\x20\xeb\x92\x11\xfe\x90\xe5\x8e\x8a\xc9\xd3\x91\xf8\x5f\xa6\x81\xbd\xca\x48\xfe\xd0\xf5\x70\x58\xf9\x4b\xa7\x0b\x78\xf5\x53\xd4\xe3\x1a\xdd\x8e\x7c\x89\x1c\xda\xb5\xe5\x1c\x79\xd2\x0c\x58\x69\x36\x39\xb2\x13\xe4\x38\x7e\xa6\x30\x8a\x9f\x29\x1a\x03\xa4\x89\x1f\x12\x4e\x49\x8b\x23\xfc\x21\x73\x4d\x95\xf5\xc8\x49\x51\x58\xe3\x82\xfa\x48\xff\xe4\x39\x48\x3a\x1e\xe1\x0f\x99\x6b\x9c\x44\x46\x76\x82\x84\x42\xf9\x56\x8e\x75\x74\x1f\xb9\x49\xb2\x87\x0e\xa4\x93\x24\xeb\x94\xc2\xd8\x08\xfd\xc6\xfd\x4d\xa6\x23\xf5\x4b\xa6\xf3\x3d\x75\xa4\x7e\xa9\xd1\xf3\xf5\x3e\xd2\x3f\xd5\x98\xd8\x2e\x39\x92\x3f\x64\x2a\xdb\xb0\x46\xe2\x7f\x55\x07\xe3\x77\x23\xf9\x43\xa6\x02\xdb\x18\xc9\x1f\x3d\x58\x60\xdc\x41\x9d\x78\xd5\xdd\x1a\x6d\x7e\xd7\xab\xf4\x6f\xd3\x6b\x2d\x8b\xc9\xd3\xd6\xe8\xe9\x37\xd7\x27\xbd\xad\xcd\x26\x1e\x1f\xcc\x25\xbc\xcb\x17\x70\x4b\x38\x3a\x68\x8d\x48\x6b\xd8\xdf\x1a\xf6\x37\x5b\x6b\xd7\xd2\x15\xdc\x56\xa3\x48\xc5\xf7\x9e\x24\xee\x3d\x49\xfc\x15\x3c\x49\x88\x5a\xd6\x5c\x5f\x70\x7f\xa7\x93\x49\x46\x2f\xc9\xcf\x51\x3c\xfe\x4c\xc9\xf7\xbf\xd0\xc9\xc4\x76\x27\xd1\xd0\x63\x1c\x80\x45\x41\x42\x0e\x99\xc4\x1d\x00\x54\x14\x24\x2e\xd8\xab\xe0\x94\x81\xfd\x23\x9d\xd2\x38\x2f\x68\x1c\xd3\x8c\x7c\x3f\x81\x44\x17\xf8\xc7\xe0\x8c\xfc\x9c\xa6\x21\xf9\x7e\x5a\xea\xe6\xe2\xb1\x76\xef\x23\x7c\x41\xbe\x09\x92\x60\x6a\xfa\x9e\xe8\x0f\x18\x16\x06\x19\x07\x98\x73\x00\xe9\x63\xe2\xe0\x14\x0e\x47\x36\x70\x74\x1a\x24\x12\xe4\x25\x98\xf1\xdb\x10\x5c\xf2\xca\x07\xb4\x98\x49\xc0\x17\xcf\x2b\xe0\xc2\x53\xe5\x6f\x76\x56\x55\x5f\x3e\x53\xf5\xbd\x05\xcf\xe4\x65\x80\x09\x2d\x24\xe0\x3b\x9a\xe5\xf0\x94\xaa\x1c\x7a\x21\x40\x54\x27\xce\x83\x6c\x5e\xd5\x0d\x96\xaf\x80\x69\x51\x40\xd4\x26\x17\x3e\x17\x59\x12\x54\x72\x15\x03\x52\xb2\x0b\x76\xa2\xd2\xce\x3d\xa2\xd8\xaa\x10\x85\x95\x2f\xf7\x11\xc2\x81\xa4\x37\x26\xf1\x70\x83\x26\xa1\xa7\x6f\x3c\x43\x82\x3d\x87\x13\x93\x0b\x75\xca\xd2\x15\x26\xb3\x74\x41\xb3\xe2\xd2\x03\xb7\x10\x59\x12\xf4\x75\x51\x2c\xde\x65\xe9\x59\x14\x7a\xc9\x8d\x2d\xd4\x85\xc8\x56\xc4\xb6\x18\x57\x94\x88\x16\x63\xbb\x40\x33\x8f\x86\x6b\x6b\x4a\x56\xff\x99\x9e\x6e\x93\x8e\xac\xc6\xf4\xca\x9b\xd9\x2b\x24\xa1\xe7\xd6\xb2\xd1\x25\x91\x83\x5e\x11\x6a\x15\xf5\x5c\x42\x21\x20\xca\xdf\xba\xd0\x73\xb6\x5c\xc0\x51\x3f\xae\x22\x3c\x15\x99\x2f\x9e\x3b\x79\xf9\x4c\x96\xfc\x30\x73\x4b\x26\xb0\x06\x58\xee\x5b\x5a\x38\xb9\x0b\x4d\xf8\x0c\x44\xae\x03\x07\xee\xf4\xd7\x5f\x65\x1b\x8c\xae\xdd\x3e\x68\x02\x07\x20\xf1\xd9\xc1\x30\x9a\xb2\xf5\x51\x23\x58\x44\x23\xb5\x19\x8a\xff\xf9\x91\x03\x77\x52\x60\x2b\x37\x8a\x62\xf2\x19\x19\x5f\x3d\x05\x83\xe8\x65\x84\x3f\x9c\x26\x3e\xa9\x35\xc0\x7f\x38\x03\x14\x00\x1d\xdd\xbe\x20\xe7\x88\xe6\x23\xf4\xbb\xc3\x8d\x79\xae\xbb\x3b\x4c\x62\x1a\x0c\xc0\x05\x6f\x4e\x89\x1e\x43\xca\x77\x62\xf0\x09\xb4\xc6\xc8\xcd\x33\xbe\xba\xb1\x95\x8e\x8b\x09\x8d\xb2\x4e\x19\x4f\x93\x62\xca\xc3\x31\x83\xeb\x69\x1c\x17\x5e\x99\xb4\x3d\x7d\xc9\x28\x0f\x16\xa1\x7b\xf1\x99\xd2\xc5\x41\xfe\xe1\x32\x19\x47\xc9\xb4\xb2\x2b\x50\xd6\x82\x6f\x46\x81\x9e\x8e\x60\xbe\xf0\x5c\xdb\xaf\x58\x50\xf2\x19\x0c\x77\x27\x05\x5f\x1e\x18\xf9\x64\x56\x42\xc1\xb7\x07\x4e\xbc\xbb\x96\x60\xec\xd3\x81\xc2\x4f\x70\x39\xa0\x4a\xf1\xc2\x1a\x75\xca\x04\x4f\xdb\xfa\x3d\x95\x6c\x5e\xa4\x78\x6b\xb5\xa1\x51\x9a\xa7\x6e\x8c\x4b\x59\x7b\x15\x4e\xb9\x89\xa3\x84\xfc\x99\xfa\x47\x86\xa1\xc4\xb7\x03\x87\x4d\x5b\x38\xa4\x4a\xf1\xc0\xba\xb7\xc2\xb2\xcc\xbe\x7d\x5b\xe8\xf4\xb9\xac\xac\x93\xe3\x69\xf7\xe0\xf9\xde\x5b\xd4\x18\xfb\x74\xa0\xb4\x7b\x1a\x0e\x26\xbe\x7d\x70\xd2\x73\x8a\x02\x84\x04\xb6\x8b\xd9\x0b\x9f\x6f\xfd\xf8\x25\x37\xbf\x14\x32\xbd\x2b\x9a\xd7\x75\x70\x27\x6d\x43\x96\x5d\x9f\x86\x51\x06\xaa\xe2\x71\xb0\x80\xd7\x17\xe8\x02\xd3\x33\xa3\x07\xfb\x7b\xef\x8c\xb5\xcf\xca\x61\x0b\xb9\x88\x8b\x92\x6c\xf9\x32\xa9\x92\xe7\x1b\x8f\x3d\x19\x44\x5f\x34\x23\x57\x36\x38\x94\x51\xfc\xb7\x2a\xe2\xe8\xb1\xe2\xdd\xb0\xd7\x09\x71\xa4\x63\xde\x39\x27\xa0\x83\x69\xcb\x3d\x29\x49\x43\xda\xee\x19\x10\x53\x30\x0b\x19\x91\x36\x13\x3a\x3e\x8d\xe3\x88\x26\xc5\x3f\x38\x78\x5b\xdf\x49\x77\x7b\x37\x69\x8d\x16\xe7\x69\xf6\xb9\xac\xc1\x84\x16\x9f\x04\xa8\x05\x62\x06\x0c\x18\xd9\xab\xfc\x96\xdd\xa2\x42\xa1\x5d\xd6\x2f\x5a\xcc\x3e\xc1\x5c\x8f\xd3\xf8\x1f\xbf\x43\xff\xce\x67\x51\xbe\x50\xbe\x91\x9d\xee\xe5\xb3\xd9\xad\xd1\x06\x3f\x4f\xbc\x7b\x49\x94\xef\xa7\x49\xc2\x7d\x36\xa1\xe5\xd6\x35\x68\xaf\xe3\xdd\x2e\x1f\x3c\xf0\x6e\xa3\xb8\xca\x4e\xd7\xbf\x83\x71\x2f\x05\x52\x26\x2f\xa5\x79\x30\x0e\x85\xc8\x09\x42\xa2\xf1\xea\x6d\x59\xdd\xd2\x9b\x28\x3e\x21\x70\x95\x93\x71\xb0\x68\x8d\xb6\x86\x2c\x09\x1f\x49\x5a\xa3\xad\x4d\x96\xa6\x8f\x03\xad\xd1\xd6\x63\x95\xc2\x45\xa7\xd6\x68\xeb\xa9\x4a\xc2\xc2\x7d\x6b\xb4\xbd\xa5\x32\xd8\x0a\x6f\x8d\xb6\xb7\x75\x82\x16\xea\x5b\xa3\x6d\x5d\xa9\x3e\x16\xb6\x46\xdb\xdf\x3a\xc9\xb4\x98\xb5\x46\xdb\x4f\x9d\xf4\x84\x16\xad\xd1\xf6\x77\x4e\xba\x14\x84\x5b\xa3\xc7\x43\x27\x33\x9f\xcd\x5a\xa3\xc7\x9b\x6e\x3a\x93\x85\x5b\xa3\xc7\xba\xfb\xf2\x8c\xd3\x1a\x3d\xfe\x46\x25\x9a\x07\xe7\xd6\xe8\xf1\x13\x95\x25\xa5\x96\xd6\xe8\xf1\xb7\xd5\xba\xbd\xeb\x93\xde\xd6\xf6\xbd\xe6\xed\x5e\xf3\xf6\xdf\xa2\x79\x0b\xe2\x18\x1c\x4c\xdc\xce\x8f\x2b\x52\x70\x39\xaa\x10\x9f\x2e\x44\x86\x89\x79\x79\xc6\x2d\xfa\x91\x8e\x01\x7a\x23\xe1\x74\xd0\x98\xba\xe8\x48\xae\x9e\xc6\xab\xa8\x79\x05\x97\xbb\x56\x65\x90\x26\x21\xce\x79\xec\x23\x13\x44\xb2\x22\x91\xa9\xbc\xbb\xee\xc5\xb1\x31\x14\x53\x30\x32\x8f\x56\x3d\xb8\xa9\xef\x11\xcb\xb4\xac\x44\xe9\x61\x26\xe0\x23\xf2\x2f\xfc\x72\x9e\xfd\x87\x93\x1d\x73\x49\xbe\x09\x39\x3d\xac\x0e\xf3\x6d\x49\xad\xd2\x1f\xf8\xae\xfa\x75\x75\x05\xf1\x6f\x88\xed\xf7\x81\x25\x42\xea\x71\x9b\x49\xa1\x10\x57\xa0\xdd\x23\xed\x22\xe5\x3f\x4f\xfa\x1c\xcd\x28\xde\xe1\xc4\x73\x1b\x2a\x9a\x39\x9e\x9c\x80\x81\x8b\xb2\x0f\x15\x37\xa4\x5d\x4f\xd0\x6c\xab\x1a\xd6\x1f\x56\x7c\x17\x11\x0f\x77\xa1\x03\x1d\xe1\xe7\x25\x1d\x04\x4f\x37\x28\x6d\x16\xf4\xc3\x2d\xf0\x45\xa1\xf1\x6a\xe0\xd9\x7c\xdd\x85\xbd\x53\x54\x61\xdc\x13\xb5\x38\x0c\x8a\x40\x8e\x80\xfd\xee\xb3\x7f\xc8\x2e\xfa\x7d\x75\x05\x46\xb1\x0a\x00\xae\x92\x73\x09\x22\xbe\xae\xae\x74\xf4\x4d\xd0\x36\xb2\xa6\xe5\x1d\x39\x02\x3c\x1e\x9e\xf4\x73\xc6\x10\x94\x8b\x75\x06\x3d\x17\x02\x8e\xa6\x30\x77\xba\x7e\xf1\x4c\x17\x6e\x65\x57\x98\xda\x0a\xe9\xce\xbd\xb4\xed\xfc\xa2\xde\xa7\x77\x8f\x87\x27\xe8\xe1\xd5\x3a\xb4\xdf\x25\x5f\xe0\xb1\x43\x90\x24\x69\x41\x26\x51\x12\xf2\x7e\x45\xc9\x94\x37\xf4\x4c\x35\x3f\x4e\x93\x3c\x8d\x69\xff\x3c\xc8\x92\x4e\x1b\x97\xe0\xde\x72\x18\x2b\x8e\xd3\x69\x1b\x99\xbe\x8a\x1e\x33\x54\x38\x1e\x97\xa8\x60\x43\x38\x32\x17\xcc\x5d\xc7\xb7\x3a\x7b\xbc\x5b\x3d\x93\x20\xcc\x23\x14\xd4\x28\x9d\x1d\xc2\x14\x37\x58\x8e\x17\x74\xcc\x24\x00\xcf\x7a\xec\x81\x47\xa6\xd3\x60\xfc\x59\xc5\x10\x05\x57\x04\xe2\xb0\x2b\xaf\x5b\x3b\x41\x36\x5d\xc2\x5b\x90\x63\xf5\x0b\x79\xe3\x31\x8d\xd0\x65\x8d\x10\xfb\xb9\xb2\x18\xf6\x1b\xd7\x71\x20\xd8\xc4\x6f\x9a\x7e\x2c\x34\xdb\x48\x96\x71\xec\xa0\x3b\x95\x94\x26\xbc\xdf\xe9\x03\xb0\x84\x98\xa0\x28\x6b\x5c\x33\x0b\x98\xec\x9f\x46\xa6\xd2\x10\x89\xdf\x9c\xb3\x77\xd2\x1e\x1c\x94\xda\x3d\x2f\x63\xed\x49\xf6\xce\x0e\x5b\x9d\x6e\x4f\x37\x84\x30\x5c\x3f\x53\x41\x51\x04\xe3\xd9\xc7\x74\x5f\x3a\xc2\xc2\x53\x26\xbd\x63\xe1\x33\xb7\x9e\x5a\x3e\x6e\xfe\xe9\x0c\x47\x16\xed\x07\x71\xac\xf6\x13\x01\x5c\x72\xa6\x70\xba\xa9\x0e\x18\x9e\x13\x86\xf7\x88\x01\xa4\xda\x1a\x6d\x81\x74\xcf\x57\x7d\x6b\xb4\x05\xb2\x3b\x8e\xd9\xb6\x0d\xc0\xd6\x46\xd8\x1a\x3d\xde\x66\x22\xf3\xe3\x7b\x91\xf9\x5e\x64\xfe\x6b\x8b\xcc\x28\xdc\x0b\x9c\xbd\xef\x2a\xde\xcb\xdf\xf3\x34\xc9\x16\x63\x53\xde\xfc\x85\x27\xaa\xab\xc3\x2c\x4b\x6d\x11\x98\xa7\x29\x49\xd4\x55\x51\xb0\xc1\x1a\x42\xa6\x23\x63\x02\x3a\x3e\x95\x4a\x9a\x22\x23\x17\x81\xbd\x6b\x1c\x05\x06\x61\x28\x7d\x3a\x32\x76\x2c\x0a\x83\x9b\x6c\xe8\x9a\x48\xb0\x2c\x02\x83\x30\xf4\xd8\xd8\x12\x31\x7e\x5e\xa8\xd0\xd6\xad\x83\x35\x18\x27\x66\xc5\x61\xe8\x93\xb9\x7d\x03\xcf\x79\x54\x70\x09\x51\x3b\x22\xc9\xb4\xab\xfa\x2f\x60\xbc\x5d\xf3\xed\xe7\xa6\x77\x01\x85\x5f\xa3\x9b\xee\x14\xe8\x7b\xa2\x24\xe4\x6a\x26\x09\xdb\x43\x75\xd3\x2c\xeb\x09\x49\x34\x77\x65\x62\x4e\x3e\xfc\x97\x10\x16\x35\x80\xc0\x0f\x76\x31\xa9\x50\xd9\x23\xf0\xba\xbd\xe4\xfd\x9a\xa8\xf2\x18\x60\x4e\xf0\xf1\xa0\x54\x60\xe7\x45\x4a\xaa\x65\x62\x8d\xec\x8f\xa8\xb4\xef\xc8\x3e\x76\x81\x75\xb1\x88\xfa\x51\xfe\x8f\x20\x8e\xc2\xf7\x34\x5f\xa4\x49\x4e\x45\x53\xce\xdb\x3b\x67\x0c\xfe\xf6\x3a\x7c\x8d\xf5\x0f\x92\x33\x6f\xad\x3b\x4e\xa5\xd7\x6e\xff\x4a\x2b\xe7\x3e\x9b\x9c\xc1\xf2\x3d\x17\x7c\x43\xf8\x32\x44\xe3\x7d\xd1\x07\xf0\x1a\x81\x13\x9c\x28\xf6\x7a\x2a\xd4\xf9\x86\xf8\x45\x09\xa0\x2c\xad\x9f\xe4\x83\x6f\x8d\xb6\x40\x8f\x26\x56\x64\x6b\xb4\x0d\x56\x6f\x8d\xa2\x7c\xdf\x6f\xf8\xf7\x1b\xfe\x9f\x77\xc3\xd7\xfb\xbd\x12\xcb\xef\x48\x45\xd6\x50\x57\xc5\x4e\x3c\x99\x05\x96\x0b\x59\x7f\x00\x99\xab\xaa\xd3\x24\x1c\x7a\x37\x85\xf5\x60\xf2\x41\x94\x80\xde\x43\x87\x10\x04\xa6\x34\x86\x46\xc8\x71\xdf\xfe\xc9\xd5\x4b\xf8\x91\x19\x6c\xf3\xf6\x33\x65\x0e\xb7\xaf\xc1\xde\x49\x28\x25\x17\x80\xb1\xef\x35\x91\xbe\x9c\xcd\x54\x6f\x03\xc2\xdb\xaf\xbf\x6a\xf3\xa9\xe7\x69\xd4\x13\xe5\xac\x5b\x9d\xe0\x34\xf2\xa8\x41\x90\xdf\x67\x62\x39\x5a\xe6\x01\xbe\x77\x77\x49\x1b\xf5\xa9\x4d\x1e\x3c\x30\x1c\x39\xa3\x73\x33\x6f\xd6\xf0\xf6\x7f\xdd\xb5\xb6\xe1\xaa\x06\x3d\xae\xa1\x49\x07\x12\x4b\xb6\x6b\xc8\xe3\x1e\xa3\x3d\x3b\x83\x55\x11\x03\xcb\x3d\x4d\x03\xed\x89\xc3\x3b\x47\x28\x07\x55\x68\x44\x5a\x1e\xa9\xbd\x6a\x20\x3d\xaa\x80\x5e\xc2\x55\x14\x3f\x5a\x7b\x5f\x36\x05\x61\x28\x69\x38\xd7\xc7\x70\x4c\x1b\x32\xed\x5a\xd5\x54\x4a\x4f\x9c\x54\xfc\x55\x56\x9e\xec\xf5\x71\xfd\xe6\x84\x82\x5e\x21\xae\x32\xfb\x58\x53\xa5\xb4\x3f\xaa\x3f\x9f\x68\x31\x93\xea\x66\xdd\x49\xd3\xeb\x45\xad\x2a\x75\xe2\xa8\x39\x34\x02\xb4\xaa\xb4\xc1\xbc\x72\x6e\xd1\x68\x52\x39\xbf\xb9\xbb\x19\xb5\xeb\xab\x57\xd4\x48\x86\x77\x17\x73\xcb\x79\xaf\xa5\x56\x16\x9c\x55\x68\x1b\x15\x8f\x35\x27\xcf\xd5\x5b\xf1\x8e\x95\x4e\xe7\x5e\x1c\x57\x4e\x17\x00\x89\x8b\x9e\x95\x09\x8c\xab\x42\x6b\x3a\xb8\x3a\xb5\x19\x8f\x02\x5d\xa5\x5a\x19\xb5\x55\x91\x9b\x72\x94\x03\xb6\x7f\x72\xd2\xa7\xb4\xc8\x85\xf1\x4a\x7c\x49\x42\xba\x88\xd3\x4b\x1a\x4a\x13\x41\x78\x3e\x38\x9e\x05\x51\x62\x3f\x57\x83\xda\x5e\xa5\x99\xec\x91\xc7\xf7\x80\x3c\xb0\xfa\x48\x52\xae\xcb\x6b\xa5\x5a\x5c\x33\x5c\xe4\x1e\xc9\xcb\x0d\xfd\xac\xad\xa4\x45\x6c\xf0\x20\x5b\x42\x0a\x4b\x4d\xbe\x10\xb0\x19\x22\xc9\x38\x6a\xde\x57\x10\xa5\x7c\x57\x3e\x2c\x83\xfc\xc1\x80\x9c\x07\x11\x57\x97\x83\xc8\xb5\x28\xb4\x0a\x56\xde\x94\x99\xf3\x2e\x96\x82\x0a\x18\xad\x3b\x46\xbb\xa6\xe7\xe5\x75\x0a\x4f\x93\x8d\xf6\xed\x5d\x09\xfa\xbb\xb1\xb1\x63\x1e\x9b\x06\x03\x92\x17\xe9\x82\xeb\x6a\xa3\x64\x4a\x82\x09\xeb\xca\x37\x43\x3e\x57\x39\xe9\x14\xd1\x9c\xa6\xcb\xa2\xeb\x1c\x1d\x39\x02\x7e\x20\xdf\x0c\xbd\x87\x45\xde\xfb\x3e\xab\xfd\x67\x51\xb9\x8e\xa9\xd0\x25\x5f\xae\x3d\x67\x3a\x1b\x81\xfc\xc1\x9e\xf7\x1c\xaa\x66\xc4\x7b\xda\xd4\x27\x3f\xed\x18\x58\x31\x26\xb8\x2f\x09\xf8\xca\x18\x33\xc2\x06\x27\xc1\xa7\x4c\x62\x5e\x26\xa1\x8d\x81\xb6\xef\xf0\x49\x63\xe4\x50\x04\xff\x39\xee\x88\x6f\xdc\x2a\x5b\x7e\xb8\x66\xe5\x4f\xc4\xc5\x9a\x41\x35\x53\x5a\x7c\xd4\x4d\xbd\xe7\xa4\xa6\x39\x0a\xea\xc6\xeb\x20\x9f\x61\xa2\xea\x49\xc2\xec\xfa\x8f\xf0\xd1\xa4\x23\x00\xfc\xd4\xe6\x2d\xe4\xed\x20\x84\x30\x12\x75\xf5\xc7\xe6\x02\x34\x7b\x04\x71\x8e\xfc\xdd\x91\x7f\x65\xde\xdb\x9f\x28\xef\xed\x65\x7f\xd1\xa4\x63\x52\xdc\xd5\x15\x59\x87\x16\x2b\x8b\x11\xc5\xba\x3d\xb4\x89\xff\x6e\xb2\x04\xf0\x5f\xc3\xe5\x60\x0f\x29\x0d\x51\x88\xe8\xed\xca\x99\x91\x7f\x83\x81\xba\xe7\x8b\xd3\x29\xa2\x5a\x38\x56\x48\x36\xbe\xde\xee\xd6\x34\x4f\x0c\x51\x4d\x71\xd4\x92\xa9\x6e\x50\xd9\x60\x40\xf8\x66\x25\xc5\x85\x20\x09\x89\xb8\x19\x21\xc1\x34\x88\x12\xb1\x72\xce\xa9\x88\xf0\x57\xf3\xe7\x97\x3d\xed\x0d\xb0\xa6\x06\x5b\xd6\x71\xb6\xff\x9a\x21\x8d\xb9\x53\x36\x71\x29\xc8\xb6\x04\xb6\x3b\xe6\x74\x9c\x26\x21\x61\x0c\xb7\xb6\x12\x44\xba\xf5\xc4\x4a\x0c\x8e\x08\xba\xb0\xa6\x1d\xf6\x7a\x31\xba\xe3\x0e\x61\xdf\xed\x48\x94\x10\x27\x5a\xc4\x29\xf3\x22\xcd\x68\xa8\xfc\xb8\x73\x09\x04\x34\x3e\xd3\x20\x27\xc1\x9c\x6d\x48\x7d\x2f\xbf\xb6\xff\x4a\xf9\xb7\xfd\xe7\x71\x2f\x7f\x17\x5d\xac\xee\xe1\x75\x69\x6e\x19\xc7\x70\x4b\xd8\x90\x48\x3b\xd9\xf4\x40\x81\xae\x18\x24\xa1\xbf\x0a\xd8\x31\xfb\x52\xf9\xd2\xb0\xa4\x38\x0b\xac\xe6\xd0\x60\x57\x8a\x0f\x0c\x70\xaa\x0a\x4e\x23\xe3\x72\x81\xbf\x28\xa2\xf2\xf8\x0e\x69\xc1\x69\x44\x76\x19\xa4\x94\xb3\x1e\x72\x4d\x68\xfd\x98\xf4\x09\x29\x21\x01\x12\x4d\x45\x71\x59\x8b\x1c\x5b\x42\xcf\x55\x92\x1c\x53\x72\x79\x8d\x89\xc1\xd2\x8d\x6c\x4a\x9b\x82\x20\xee\xae\x58\x74\xab\xa2\xa8\x2d\x07\x1b\x92\x85\xf0\x75\x22\x15\xc5\xa1\x53\xda\x27\x29\x0b\x08\x25\x2d\xeb\xe3\x9f\x4c\x52\x6d\xe9\x89\x87\x42\x03\x3d\x11\x0c\xa5\xbe\xeb\x17\x52\xb1\x45\x7f\x2b\x6b\x60\x7f\xea\x07\x97\xae\xd5\x29\x12\xd3\x5f\x47\xd2\x41\x4f\xcd\x3e\xe6\x60\x83\x01\x8f\xad\xa8\xad\x2c\x8c\x4a\xb5\xad\xc4\x97\xeb\x1d\x06\x2c\xb1\xb4\x6e\xb6\x2d\x10\x83\x2a\x86\x33\x6e\x06\x6f\x71\x80\x90\xf1\xa3\x84\x38\x1a\x53\xb8\x6a\xd0\xf6\x1a\x56\xf8\x3f\x9f\xed\x08\xd8\x7f\x94\x5b\x8c\x10\xc7\x6a\x24\xef\x2f\xd2\x85\xe1\x60\xce\xec\x5e\x1c\xe4\x85\x80\x74\xaa\xf6\x77\x87\x13\x52\x87\x15\x04\xe7\x45\xeb\xea\xc5\x09\x04\xa2\x85\x74\xbb\x4f\x1a\x85\x35\x5d\x62\x0d\x09\xe0\x3e\x8f\x4a\xf2\x03\x19\xda\xb5\x89\x99\x96\xb4\xbf\x27\xd7\x72\xbd\x16\x40\xfe\xdd\x4a\x25\x88\xd0\x64\x31\x4b\xa9\x4e\x53\xa6\x76\x78\x58\xeb\x66\x97\xfb\x8b\xe0\x32\x38\x8d\xa9\xaf\x7b\xee\x71\x80\xdb\x4f\xe5\x34\x09\x75\x44\xaa\x24\x4d\x1e\x89\x4a\x30\x3a\xec\x6d\xe2\xba\x6c\xea\xc1\xb7\x1f\xe3\x8c\x7e\x15\x6c\x47\x2e\x95\x1e\x8c\x18\xd5\x2a\x27\x08\x6c\xdf\x36\x76\x79\x45\x3b\xe6\x24\x96\xde\x08\xe2\x13\xad\xa1\x03\x90\x72\x5f\x10\x26\xb6\x96\x20\xa4\xe4\x3c\xc8\x95\x40\xb9\x66\xe2\x8a\x2f\x6d\xb8\x7a\x45\x47\x18\x6d\x98\x65\xdd\xbf\xce\x82\x7c\xe6\x43\x3a\xeb\x35\xcd\xb2\xb2\x9b\x48\x7c\xe5\xe8\xbb\x57\xac\x92\x78\x98\x38\x1a\x86\xfc\xda\x0b\x71\x5d\xd6\x13\x7f\x5b\x25\xc7\x2e\xb2\x0b\x65\x4a\x84\xaf\x52\x09\x71\x12\x65\x79\x51\x2e\x20\xae\x28\xe3\x95\x68\x40\x7c\x6a\x0f\xdf\xf5\xab\xf1\x55\xe7\xf8\x12\x22\x6d\xf2\x81\xd7\xcd\xb3\xd5\x58\x53\x94\xd7\xa2\x7a\x95\xa1\xfb\x79\x9a\xd2\xc9\x73\x20\xa1\x2b\x13\xd8\x95\x9b\x20\x3b\xdf\xbe\xe0\x76\xa5\x90\x24\x3e\x0d\x03\xb4\x1b\x0b\x5e\xb6\xd6\xac\x4e\x3b\xeb\xd9\xd4\x45\x4d\xd7\xa6\x0c\x34\x51\xf5\x0f\xd6\x06\x03\x6b\x07\x36\x2e\x70\xb4\xc7\x63\xa4\xbe\xb4\x2a\xef\xf0\x7d\x79\x30\x30\x5c\xe9\x96\xc6\x9d\x1e\x8f\xc1\x2b\x6e\xca\x03\x35\x45\xc9\xb4\x42\x36\x33\xd5\xd8\xe6\xc8\xf9\x24\x5e\xbb\x9c\x08\x8b\x43\x55\xa2\x10\xf9\x82\xa4\xae\xa6\x12\xd1\x84\x24\xa9\xae\x81\xb1\xb7\x45\x90\xe7\x34\xec\xb1\x2a\xb4\xeb\x3b\x06\x91\xa3\x25\x6d\xf2\x32\x45\x78\x30\x03\x16\x3a\x0d\x73\x48\x9f\xef\x54\xd3\x66\x95\xac\x2c\x43\x69\x4b\x79\xad\xad\x2c\x66\xc8\xb5\x24\x04\xab\x81\x10\x61\xd2\xa8\x40\x75\xa9\x27\x0b\x9c\xd2\x71\xb0\xcc\x29\x3b\x89\x87\x69\x52\x90\xf3\x20\x01\x9b\xa4\x7c\x91\x46\x31\xbf\x0d\x4f\x0a\x9a\x4d\x82\xb1\xf2\x8d\xdd\xe0\x24\xde\xe4\xb4\x6d\x6f\x53\xf5\xfc\x90\x38\xee\x75\xd5\x9a\x46\x6b\xf3\x47\x5a\x70\x67\xcd\x6c\x7f\xec\x91\xf3\x59\x34\x9e\x81\xd1\x00\x5b\xde\x45\x2a\xb6\x31\xb2\x88\x97\x79\xfd\xd5\xab\xe0\x03\x35\xf3\xab\x99\x87\xdf\x90\xa9\x46\x84\x5d\x5d\x4e\x55\xc5\xea\xe5\xc7\xdb\xc8\x8e\xe5\x72\x23\x32\x56\xbe\x91\x1c\x53\x25\xc3\x98\x2f\x1d\xfa\xdc\x20\xbd\x39\xf3\xf5\x9c\x7a\xbc\xc7\xdd\x06\xd7\xe7\x65\xac\xc9\x39\x0c\x7b\x4f\xc1\x25\x2f\x59\x7c\xe7\x61\x77\xf7\xd3\x76\xe1\x1c\x7f\xee\xe3\x15\xe2\x39\x4c\x7b\xcd\x96\x2c\xba\xdd\x51\xe6\xcf\xa6\xad\x44\x6b\xf4\x6d\x99\x05\xb4\xb2\x68\x68\x8d\xb6\xb6\x5d\x93\x68\x31\xf2\xd6\x68\x7b\xf3\xfa\xa4\xb7\xf5\xe4\xde\xf4\xe9\xde\xf4\xe9\xaf\x6d\xfa\x84\x6c\x9d\x85\x09\xe4\x1d\x18\x3b\x97\xb8\xb1\x14\xc6\x95\xfc\x5d\xd6\xe1\x44\xde\x39\xef\x65\xd3\x7c\x54\xa2\xb9\x41\x32\x9e\x38\xc1\x8a\x4a\x70\xec\x3b\xb9\x9d\x30\xf6\x29\x2b\x25\xd8\xc4\x09\xf8\x7c\xcf\xd7\x87\xf7\xef\xf6\x39\x73\xbf\x4d\x07\x78\xbc\x25\x60\xb5\x14\x1e\x30\x16\x29\x79\xff\x6e\x5f\xdc\x13\xf8\x3b\x20\x9e\xa3\x83\x13\x45\xdd\xf2\x2c\xcd\xf1\xed\x97\xdb\xf8\xfe\xe1\xdb\xb7\x2f\xf7\x3f\x1e\x1c\xbe\x25\x2f\xdf\xbf\x3f\x7c\x3f\x22\xfb\x4a\xfd\x3b\xe6\x55\xf2\x13\x7d\x48\x49\x7b\x83\xb0\xfa\xc8\x46\xbb\xef\xef\x83\xf6\x78\xd3\x74\xec\xea\x9d\x3d\x57\x22\x14\x6c\xf5\x44\xbc\x32\x7f\x13\xd2\x90\x76\x44\x6c\xa3\x60\x34\x4c\x78\x96\x46\xf3\x3c\x98\x52\xb2\x4b\xd6\xd7\xc5\x4b\x43\xb6\xad\x8b\xdf\x7d\x1e\x32\xd6\x49\xe9\xcb\x62\xcf\x88\x37\x79\x44\xd4\x74\xfd\xfd\xc3\xe1\x5b\x98\x95\x4c\x75\xc9\x13\x66\x55\xf4\xcd\x79\x4b\xa6\x71\x20\xaa\x36\x47\xab\x67\xf3\x23\xbf\xae\xc6\xe3\x9d\xe7\x4d\xa7\xf4\xe3\xc1\x9b\x97\x87\x47\x1f\x47\x44\x5c\x7a\x33\xe2\x62\x9d\x9c\xe7\x64\x83\xb4\xd9\x7f\xc1\x78\xc6\x38\x46\xdb\x08\x68\x23\xdc\x48\x7e\x7b\xbf\x5b\xdd\xef\x56\x7f\xed\xdd\x0a\x6d\x56\xf0\xea\xf2\x8f\x6a\xa5\xdb\xfc\x31\x7b\xa3\x37\xf4\x77\xf8\x94\x5d\xfa\x1c\x62\xeb\x5f\x1d\xce\x70\x44\xa6\xdc\x38\x86\x88\x37\xb6\xd0\x96\x3e\x2c\xd8\x46\xc8\x5f\xfb\x1d\xfc\x42\x9a\xf2\x22\x45\x3a\xce\xe7\xa1\x2b\x48\xc5\x73\xe4\x3c\x4d\xba\x35\x4f\xe8\x51\x66\x92\x26\x97\xf3\x74\xa9\x5a\x54\x09\x25\xa7\x37\x89\xb4\x29\x95\xb8\xa2\x21\x97\x07\x20\x88\x81\x13\xac\x49\xa4\xa9\xe3\xd9\xf3\x34\x8d\xaf\x21\xbc\x6a\x08\x2e\xc8\xf9\x26\x41\x39\x64\x88\x66\x07\xde\x87\xd0\xd0\x70\x98\x2e\x4f\x7c\x10\x8c\x80\x2d\x4a\x51\xfb\x60\xcd\x98\x26\xec\x7d\x8b\x41\x98\x8e\xa3\x78\xbd\x76\x00\x06\x84\x7c\xf7\x4a\x24\xf2\x88\x0a\x51\x5f\xd4\x04\xf7\x1b\xe2\x77\x89\xb9\xab\xbf\xbc\xb6\x57\x2e\xbd\x21\xc6\xd8\xe6\xf4\x19\x72\x17\xe0\xe0\xc5\xc8\xc2\x75\xa8\xbd\x83\x7b\xa3\x05\x79\x2b\x28\x47\x1d\xaa\xae\xca\x49\x10\xa7\x44\xd7\x41\x79\x47\xd3\x6b\xf3\xd1\xc1\x0a\xf5\x0c\xad\x10\xfe\xcc\x2b\xc6\x85\x8b\x56\xd3\xc3\x4a\x23\x92\x9e\xd4\x6f\x34\x9c\x3c\x9a\x26\x41\xb1\xcc\xec\xe1\xe0\xf4\xb2\xf1\x60\x98\xf2\xf1\x28\xa8\xaa\x01\x81\x03\x83\xe6\xfd\x17\x2f\x1c\x24\x79\x0b\x8e\x14\x24\xa1\x52\x2d\x15\x29\x04\x25\x9e\x44\x49\x10\xfb\xad\x9e\x79\x1d\x3e\x9b\x52\xbc\xae\xad\x2c\x51\xbd\x81\x14\x99\x47\xcf\x68\x76\x59\xcc\xb8\xc6\x7a\x7e\x1a\x01\xcb\x48\x79\x94\x68\xe8\x9b\x08\xb3\x50\x89\x2d\x8f\x6b\x10\xd1\x1d\xc7\xb3\x9d\x5a\xdc\xea\x17\x7a\x04\x78\xef\x40\x44\xbb\xeb\x50\xfe\x39\xea\x3c\x8b\x48\xbd\xe6\xba\xb5\xf3\xb8\xfd\x14\x95\xf3\x87\xad\xc2\xb7\x20\xf7\xd3\x29\xa9\xbd\xd3\x75\x55\x9a\x62\x9e\x3e\xca\x8e\xdd\x96\xa5\xa3\x10\x16\x95\xfc\x1c\x1c\x2f\x8b\x60\xda\xa2\xfc\x71\x04\x21\xa6\x2c\x63\x00\x01\x84\xe7\x8f\xd1\x8d\x4e\x4e\x96\x71\x5c\xf2\xc2\x45\x6b\x16\x89\x7b\xf9\x6f\x2a\x84\xa1\xbe\xb2\xc0\x8c\x90\x69\x8d\xe6\xac\xe2\xb6\x5f\x60\xdf\x79\x1b\xd3\xe1\xdb\x57\x8f\x9c\xd9\x37\xe7\x5d\x3b\xb6\xde\x4a\xb5\x41\xdf\x6b\x28\xce\x24\x92\x71\x9a\x8c\x83\xa2\x63\xcc\x7e\xb7\xdc\x8f\x4d\x29\xd7\x13\x4e\x6c\xca\xb9\x9e\xbd\xdb\xd2\x32\x0e\x17\xf2\xbb\x07\x97\x87\x09\xae\x20\x0c\x87\xe0\x84\xc0\x6b\x09\x55\xb3\x0f\x1e\x80\xbe\xc1\xec\x45\xf5\x36\x5d\xee\x7c\x07\x70\x70\x87\xde\x77\x82\x6c\x6a\xad\x2e\x2d\x3e\x3e\x33\x4a\x8e\xf0\x97\xf0\xcc\xb3\x89\x3c\xa1\x88\xf1\x89\xfb\x17\x55\xaf\xfd\x52\x8b\x4f\x26\xf9\xa2\xa4\x34\x5c\xdf\x56\x77\x87\xad\xcc\x5f\xd2\x28\xe9\xb4\x5a\x6e\xe5\xea\x51\x1c\x27\x37\x8e\x27\x7c\xbd\x01\xb2\x61\x87\x2d\xf3\x6e\x0f\xf7\x08\x5f\xd5\x24\x69\x71\x60\xf4\x55\xa1\xd0\xe3\x6f\x48\x03\x37\x6c\x1b\x5e\x2d\x74\x7b\x56\x2b\xb8\x7d\xb5\x91\x20\xae\x9d\x2e\x8b\xc5\xb2\xf8\x29\x9d\x6a\x76\x2d\x7c\xf1\xa0\xd5\x22\x9d\xff\x70\x3f\x33\x48\x2c\x33\xc1\x34\xb7\x86\x31\xd9\x6e\xa0\x38\x0c\xbf\xe5\x32\xf8\x69\x46\xc3\xe5\x98\xa2\xb9\x0a\xc6\xe3\x1e\x11\xae\x28\x31\x3f\x09\xc6\xe3\x63\x91\xcc\x79\x22\x43\x8a\xf8\x96\x54\xfe\xcc\x9c\xb2\x7e\x3e\x8b\x26\x45\xa7\x4b\x46\x0e\x46\x65\x96\xa3\xb4\x0a\xc6\x63\xa9\xa5\xe2\xc6\xde\x9c\xb4\x69\x4c\x0b\x2a\xc7\xa1\x9d\x24\x99\xe9\x9c\xaa\x6e\xc0\x32\xd0\xfd\x95\x78\x57\x22\x96\x36\xdb\xea\xb9\x18\x57\xea\x58\xe1\xae\xe4\x22\xa3\xe1\x6a\xe1\xc7\xe3\xb8\xc1\x96\x7e\xfe\xe8\x1e\x99\xb6\xea\x3d\x32\x55\x15\xdf\x2c\xb7\xb1\x33\x2b\x20\x86\x04\x68\xf8\x7e\xb0\xc5\x0e\xdb\xed\x93\x23\x50\xfe\xa1\xfc\x3f\x95\xd2\x32\x36\xfd\x6f\xf0\xa8\xd1\x7a\xd5\xe6\x7d\xd1\x58\x49\x8d\x5f\xcb\xd9\x14\x03\x35\x4f\xae\x65\x1c\x50\xda\x17\x42\x4b\xc7\x08\xe0\xc4\xa0\x5e\x1f\x00\xf6\x5f\xa5\x89\xc2\x0b\x7a\xac\xd8\x3d\x6f\xfb\xa4\x74\x00\x86\xd5\x84\xf7\x4e\xd8\xc0\x25\xf2\x88\x55\x75\x25\x5c\xe7\x27\xeb\x86\xae\xb1\x9e\x36\x51\xc0\xdf\xd6\xd7\xe5\xc0\xaf\x9b\x7c\xc3\x69\xd0\xa3\xff\xab\x0e\x24\x82\x63\x88\xac\x0d\x06\xe4\xe3\xe1\x8b\xc3\x11\xc9\x28\x37\xc8\xea\x91\x3c\x15\xa6\x33\xea\x8a\x4b\xdb\xe2\x04\x5c\xd3\xd5\x67\xe5\xa2\xa2\x9d\x93\x84\x8e\x69\x9e\x07\xd9\x25\x5b\x2c\x10\x01\x3b\x67\xe4\xd6\x06\x7f\xc5\xe0\x2d\x9a\x9c\xa7\xd9\x67\x2e\xe5\xcd\x97\x71\x11\x2d\x62\x14\xc9\xc1\x8c\x9d\xe2\x77\x6f\x34\x78\x48\xbc\xb6\xdc\xdf\x48\x53\x6e\x5e\x87\x69\xc6\x20\x9b\x37\x6c\x48\x75\x63\x34\xe4\x1b\x87\x79\x32\x51\xa5\xfa\x12\x47\x3e\x07\x36\xeb\xac\x73\xc7\x2e\xec\x89\xef\xfc\x50\x06\x6b\xb1\x53\xe2\xd8\x37\x9a\xfd\x14\xfe\x9c\x7c\x35\xd5\x98\x41\x7a\xeb\x29\x3d\x42\xe9\xfa\x05\xc1\xdb\x63\x72\x00\x3c\x47\x6e\x9e\xe3\xc3\x06\xcf\x51\x4c\x4f\x98\xf4\x98\x5d\xf4\x58\x7e\x8a\x62\x39\x2d\xac\x48\x31\x3e\x1f\x57\x95\x07\xb1\xea\xe9\x8e\x68\xc5\x78\x35\x8c\x67\xc8\x65\xf4\x42\x74\x90\x93\xcb\x95\x87\xad\x0a\xde\xc1\xc0\x09\xb2\x1b\xa5\x17\x7d\x83\x1d\xe9\x8f\x1d\x22\x01\x24\x17\x82\xff\x77\x64\xaa\x62\x39\xfc\x87\x4a\x47\x8c\x46\xfe\x34\xe5\x48\x7a\x21\x9e\x77\xbb\xdc\x9c\xa3\x41\x7b\x26\x2a\xe1\xcf\x25\x1c\xb9\x35\xda\x06\x0f\x46\xd8\x69\x38\x63\xcc\xdf\xdd\xdf\x8c\xde\xdf\x8c\xfe\xb5\x6f\x46\xc5\xb5\xa8\x78\xf2\xfb\x5f\x11\x5f\xef\x4e\x3d\x86\xc3\x21\xe0\x21\xd9\x4f\x93\x33\xca\x58\x51\x20\x42\x1e\xc3\x39\x18\xce\x02\x10\xb7\x58\x06\x72\x61\x04\x1c\xc4\x79\x4a\x82\x38\x4e\xcf\x73\x1e\x9e\x1d\x14\x75\x79\x7f\x8d\x55\x24\x05\xff\x37\xd1\x05\x0d\xaf\x79\xd6\x9a\x7b\xaf\xb1\x26\x6e\x54\x8b\xd4\x0e\x72\x2c\x54\x96\xea\xc0\xd9\x31\x55\xa2\xe4\xea\x4a\x06\x48\xd7\x19\x6d\xa5\x43\x6d\x77\x6d\x65\x00\x3f\xcb\x09\x11\x89\x2b\x66\x79\x1f\x3a\x52\xbf\x68\x34\xc4\xf5\x10\x87\x13\x50\x35\x77\xa1\xf6\xa1\x53\x27\x40\x0a\xbe\x8f\x5f\xb4\x1a\x77\x46\x32\x88\x92\x6a\x07\x8e\x5c\x4c\xd4\x64\x9c\x56\x5e\xfe\xd8\x96\xb0\xa9\xd2\xef\x8b\xc3\x56\x8f\x4d\xc2\x19\xcd\xa2\x09\xf8\xf5\xc8\xe8\x38\x60\x1c\x07\x05\xaa\x79\xf0\x80\xc4\xc1\xaf\x97\x24\x4e\x83\x90\x84\x97\x49\x30\x8f\xc6\x24\x4d\x68\x0e\xad\x89\x09\xd1\x0d\x89\x60\xd6\xa9\xd2\x13\x00\x94\xb4\xaf\x97\x8d\x3b\x50\x6c\xb6\xa6\xb4\x38\x54\x87\x64\x8f\x07\x67\x36\x31\x5a\x60\xad\x73\x0f\x80\x95\x09\x62\x4a\xe4\x31\xb9\xfc\xd6\xc3\xd0\xf4\x97\x5e\xbd\xf0\xec\xfc\x3c\x82\x78\x25\xa8\x57\x04\x74\x10\x39\xe5\x27\xe8\x91\xf3\xb2\x8a\x0b\xef\xcb\x8c\x0a\xf5\x62\x0f\x2e\xf0\xc6\x7c\x75\xf0\xc3\xf1\x8c\x5e\xf8\xd4\x06\x5a\x6b\x6a\x25\x58\x9e\x28\x1b\x14\x31\x34\x9f\x22\xac\x76\xa9\x52\xde\x52\xf8\xcb\x20\xdc\xcf\x44\x78\x72\x56\x95\x58\x64\x5d\x32\x92\xeb\x4d\x80\xb9\xb2\x92\xef\x9a\xc0\xf3\xbc\x0e\xba\x39\xb2\xba\xdd\x73\xe0\xd8\x12\xd0\x50\xec\xcb\x85\x29\x52\x5c\x8f\x9b\x1f\xc8\xa8\xcc\x12\x28\xc0\x31\x99\xed\xd6\xe0\xfe\x6a\xb4\xd2\xb5\x56\x5f\x95\xeb\xfa\x7a\x77\x93\x1a\x45\x29\x53\x3f\x85\x0e\x3a\x9c\x02\xf3\x19\xa3\x40\x0f\xc2\x2d\x52\x97\xaa\x9a\xbd\x30\xe4\xcf\x22\x94\x12\x2d\x48\x42\x92\xd3\x22\x27\xcb\x05\x64\x88\xd3\x08\xb0\x8c\xa8\xa0\x19\xdb\x3b\xd2\x33\x21\x6c\x09\x37\xa6\xfd\xb5\x35\xf4\x34\xe2\xa7\x74\x9a\xef\x15\x1f\x8a\x20\x2b\xd6\x6c\x4d\x63\x4e\xe3\x89\x4a\x9c\xb8\xef\x97\x05\x0b\x37\x6b\x31\xe2\x84\xd1\x78\xe2\xf8\xf0\x91\x8f\xec\xa6\xb4\xe0\xfa\x2c\x56\xd8\x7a\x69\x07\xfa\x05\x3d\xcc\x1c\xba\x47\xe4\xc9\xd3\xe2\x19\xac\x95\xbe\x8f\x71\x40\xc6\x94\x16\x1d\xeb\xcd\x8f\xb0\x64\x74\x4e\x39\x83\x01\x09\xd3\xa4\x2d\x5e\x89\xb2\x3e\x0a\xb4\x81\xd9\x24\x5c\x74\xcb\x44\x69\x76\x04\x9e\x30\xfa\xfd\x3e\xf9\x65\xc9\x1d\x01\xb3\x36\x19\xef\x75\xce\xcb\x25\x0f\x23\x2b\x1e\x45\x5e\xdb\x2f\x60\xad\x95\xae\x86\xe1\x3f\x63\xf2\x4c\xef\xc1\x94\x1b\x72\xd6\x3d\xd3\xe4\x8f\x77\x4c\xb3\x4f\xa3\x7f\xf5\x7e\x58\xbf\x1e\xe9\x2e\xd2\x38\xe6\xe4\xe3\x27\x5b\x41\x9b\x1a\xcc\xa6\x4b\xa5\x12\x01\xb5\x6d\xf2\x46\x99\xe1\x1a\xc4\x92\x96\x90\x8b\x98\xd1\xd4\x99\x53\x69\x64\xc1\x48\x4f\x8e\xd5\x37\x09\xbe\x67\x53\x3e\x9a\x48\x1b\x9f\xe4\x9b\x52\xc7\xcd\x28\x43\x9b\x29\xc3\xd0\xb4\xf2\xfa\x99\x95\xa0\x2b\x19\xc9\x42\x2e\xe9\xdc\x0a\x3d\xb7\x23\xd2\x52\x7d\x00\xf4\xc9\x76\x46\xcd\x18\xcf\xbb\x34\x8e\x19\x9f\xd1\x3d\xe1\x34\x38\xe2\x45\xd8\x39\x8d\xce\x69\x52\xc0\x91\xb3\xcf\x28\x0e\x86\xa6\xf7\x92\x85\x30\xb4\x3f\xe6\x98\x02\x72\x3c\x08\x4f\x7a\xf2\x8a\xca\x48\xee\x69\x62\x14\x39\xd8\x8d\x11\x57\x10\x03\xfd\xb2\xcd\x5a\x46\x2d\x74\x48\xdc\x92\xc9\x7a\xc4\x09\xef\x21\x97\x9b\xe7\x76\xa0\x27\x4e\x53\xfb\x19\x85\x31\x81\xbd\xf6\xbe\xe7\xa1\x23\x30\x3b\xae\xc1\x46\x17\xae\x06\x3e\x90\x86\x6f\x15\x55\x59\xa9\xae\xab\x54\xd9\xe3\x57\xaa\x99\x9d\x41\xb6\x04\xa4\xd4\x63\x7c\xa9\x35\xa6\x16\x36\xb5\x18\x6c\x89\xbe\x08\xda\x41\x83\x99\x80\x20\xe5\xcc\xbb\x4f\xc6\xd4\x0a\x11\x96\x35\x2a\x43\x6c\xb9\xfb\x65\xf9\x9a\xed\x39\x59\xf8\xda\x49\xfd\x2e\xed\x77\x3f\xa1\xe7\xe2\xd6\x09\xe3\x00\xfb\x0a\xe3\x4c\x32\x0a\x0d\xd7\x78\x7e\xe6\x58\xb3\xec\x3b\xe3\x53\x8f\x98\x3b\x3e\xad\xe5\x83\x44\x70\x64\x71\x2e\xac\xa0\x5e\xcb\x21\xa9\xcb\x5e\x2a\xca\xfa\xbb\x51\xad\x77\x36\x96\x36\x23\x82\xd0\xf5\x03\x88\x5d\x35\x64\x14\x2e\x19\xd8\x99\x63\x41\x93\x10\x0c\xdc\xd4\x24\x07\x39\x28\x5a\x92\x9c\x51\xa8\xf2\x05\xa3\x2b\x4a\x27\x00\xcc\x0a\x31\xa9\xa7\xcb\x95\x2b\xaa\xf5\x65\x12\xe4\x79\x34\x4d\x68\xd8\x77\xfb\x68\x53\x94\x8f\x27\xfb\x66\x47\xc9\x58\xe3\xd3\x9a\x09\xf2\x36\x83\x4d\xc6\xd0\x48\xb4\x3d\x31\x89\xb1\x74\x18\xc4\x19\x0d\xc2\x4b\xfd\x5e\x5d\x0b\x8a\xf9\xed\x29\xcd\x14\x64\xa5\xf4\x5a\x37\xae\x68\xd2\xb1\x5a\x53\x3e\xe0\x86\xae\x47\x2e\xbd\x32\x39\x17\xf7\xb9\x85\x64\x52\x74\x91\x8a\xb1\x45\xf3\x39\x0d\xa3\xa0\xa0\xf1\xa5\xdd\xac\x20\xf7\x71\x53\xda\x36\xa5\x13\xa8\xbe\x53\xe2\x69\xc2\xe7\xb5\x0a\x6b\xb2\x39\xcb\x67\xdb\x0f\x1f\x0c\xba\xcb\x3d\x77\xa2\x74\xd8\x9b\xb9\xc9\xdb\xb8\x61\x1f\xea\x87\x54\xc7\x18\xcc\x11\x8f\xc6\x9a\x27\x71\x5d\xea\x0e\x04\xe1\x1a\xdd\x09\x5f\x37\x1d\x08\xde\x77\xeb\xc7\xe3\x48\x0e\xe9\x42\x0a\x0e\xe6\x40\x6a\xf8\x3b\x3c\x2d\x9f\xa7\x67\x52\xa5\x49\x82\xfc\x32\x19\xab\xc3\x8f\x4f\x30\xf2\xf1\xed\x65\x02\x6f\xa7\x0d\x04\x20\x19\xc3\xc2\x96\xc3\xbb\xb0\x21\xfc\x2a\x35\x1b\x82\xbf\x83\xd1\xa9\x15\xb2\xdd\xe7\x3c\xc1\x91\x29\xbc\x26\x27\xaa\xa4\x2d\x94\x5b\x3b\x6a\x89\x1d\xe5\x60\x40\x0e\x26\x9a\x33\x46\xb9\x7a\xd7\x77\x49\x85\xfb\x15\x12\x15\x44\x7b\xe9\xd2\xe5\xce\x67\x14\x8c\x31\xc4\xe8\xbb\x84\x33\xd5\x9c\x44\x85\xc9\x56\xbd\x1b\xb5\x43\xec\x6a\x99\xf9\x76\x0f\x1f\xfa\x45\x8d\xf6\x84\xe2\xfd\x18\x22\xa4\x78\xf8\xdb\x57\xf4\xcf\x63\xc9\xe3\x19\xb5\xad\xf7\xe2\x74\x5a\xd6\x2e\xb1\x18\x53\xc5\xd9\x02\x6a\x19\xb1\x3d\xa1\xc4\x1d\x9f\x3f\x60\x89\x09\xe2\x1c\x00\xec\x81\x35\xa7\x23\xc7\xcd\x94\x10\xc4\x0f\x5e\xf0\x84\x91\xa0\xb1\x4e\xb7\xcf\x77\xe4\x71\x20\x1d\x16\x82\x5b\x15\x1a\x12\xb6\xba\x67\x59\x9a\xa4\xcb\x5c\x79\x2f\x14\x86\x01\x6c\xb7\xb7\x3d\x11\xf1\x6a\x84\xb0\xdb\xf6\x9a\xd7\x82\x53\x89\x54\x5b\xe9\x35\x21\x20\xd7\x86\x8e\xd5\x50\x3f\x87\xb7\x98\xb7\xeb\x1a\x7e\xec\x5c\x91\x72\xdc\x3a\xb1\xdf\x2a\x2e\x48\xaf\x4f\x7a\xdb\xc3\x26\x57\xa0\xed\x65\xce\xf5\xe2\xe3\xa2\xbd\x76\x7f\x21\x7a\x7f\x21\xfa\x27\xbe\x10\xd5\x4f\x45\x91\xca\xfa\x26\xef\x45\x05\xf0\x0a\x37\x99\xbe\xd8\x6f\x8d\x9f\x98\x26\x93\x68\xea\x85\xe3\x59\x12\xf0\xe0\x34\xb0\x62\xba\x44\xa7\x41\xe2\x89\xd3\x02\xda\x64\x1e\x68\x8a\xdb\x48\xf3\xcb\xcc\xd3\x68\x2a\x3c\x18\x58\x56\x8c\x1c\xe8\x79\x34\xb5\x94\xfa\xd8\x9a\x91\x6b\x9c\xaf\x38\xc4\x95\x82\xbd\x36\x9d\x56\xe9\x74\x6c\x89\x0b\x7a\xc6\x92\x36\x0c\xa9\x88\xf7\xce\xfb\x0c\xad\x48\x55\x59\x09\xb6\xa3\x94\x40\x51\xfe\x2e\xa3\xe2\x1a\x14\xdd\x4e\x18\x75\x9f\xea\x74\xab\x81\x63\xe5\xee\xbe\x2d\x4e\x9e\xed\x5e\x9b\x06\x59\x1c\xf1\xc4\x71\x3a\x9f\x47\x45\x41\xc3\xf6\x89\xba\x22\x35\x6a\xfb\x61\x97\x0c\x51\x67\x92\xc5\xb2\x78\x41\x27\xc1\x32\xf6\x5e\x95\xd4\xf5\x8a\xed\xc1\xa7\x78\x10\xf8\xa1\x8c\x37\x5e\x0b\x23\x92\x7e\x88\x5a\xf4\x78\x9b\x2a\xbf\xb9\xc1\x5d\xb0\x46\xf1\x5b\x74\xdf\x7e\xc3\xc5\x45\x12\x56\x4b\xc9\xac\x1a\x8d\x7a\x2a\x44\xd9\x1e\x3c\x48\x6a\x7a\x4d\x2f\xaa\x46\xfe\x72\x91\x8e\x67\x55\x23\xa7\x1a\x80\xf7\x01\x44\x4c\x9d\xe8\xbe\x6f\xb2\x33\x5b\x9c\xea\x5a\x16\x35\xca\x64\xd6\x77\xd6\x73\x4f\xbf\x71\xdb\x86\x39\x33\xef\x6a\x8e\xcc\x37\xd3\x09\x09\x0c\x27\x86\x41\x12\xca\x3b\xdd\x1c\xee\x74\xb8\x05\x03\xe3\x10\xaf\x5f\xfe\xd3\x62\x0c\x50\x07\x93\xe0\xbd\x2c\x41\xde\x3a\x18\xce\x80\x1d\x13\x7d\x79\x99\x2f\xef\x25\xdc\x3a\xbd\x21\xca\xbf\x18\xd7\xdc\x70\x51\x89\x2e\x8b\xe1\xf3\xea\xca\xa2\xfd\xbd\x31\x04\x88\x40\x2e\xda\x30\xbc\xc7\x37\x98\xac\x16\xfa\x24\x1c\x66\xf9\x2f\x49\x4d\x89\x0d\x57\x5d\xa4\x22\xb2\x75\x54\x90\x79\x34\x9d\x71\x11\x57\xb9\x59\x16\xea\x34\xa7\xe5\x22\xad\x6d\xb7\x48\xcd\x56\x8f\xdb\xf3\xe0\xe2\x15\xa5\xef\x68\xf6\x63\x90\xb7\x7b\x84\x7d\xbf\xcb\xa2\x34\x8b\x8a\x4b\x23\x7d\x1a\xe4\xef\xb2\x68\x4c\xc5\x6f\xf6\x1f\x4c\x33\xfb\x91\xa4\xc9\x98\xfa\xde\x5b\x7e\xa6\x97\x15\x2f\x2e\x3f\xd3\xcb\xa6\x6f\x2e\xa1\x26\x07\xd7\xbc\x86\x5d\x64\x21\xf2\x82\x8e\xa3\x79\x10\x77\x30\x80\xfb\xe6\xcd\xbc\x16\xfe\xda\xc4\x8e\x9c\x83\xde\x35\xcd\xfb\xaa\xbe\x7b\xd2\xbf\x29\x75\xdf\xd3\xf5\x1f\x91\xae\x85\xf8\xe6\x10\x36\xdc\x14\xcb\xa8\x47\x82\xaa\xbd\x42\x5d\x63\x7a\xbe\x30\x05\x39\x91\xbe\x66\x48\x6f\xb5\x14\x5c\x5c\x74\xbf\x28\x1d\xe6\x45\x1f\x8b\x01\xeb\x52\x8f\xa0\x75\x77\x26\x80\xf2\xe5\x91\x4a\xfc\x99\x00\xea\xb5\x0a\x4b\x47\xb8\x80\x77\x71\xfe\xea\x1d\x28\x6f\x1b\x36\x94\x54\x53\x5e\xf4\x81\xa4\xfc\x85\x20\x4b\x43\x4e\x83\xdc\x0f\x37\x0d\x72\x03\x0a\xc8\x17\x81\x6a\xa1\x16\xe5\x1b\x43\xc5\x6b\xc3\x24\x54\x43\x11\x6a\x01\x96\xb4\x80\x61\x0c\x9f\xa4\xaa\x2d\x67\xdd\xd5\xb5\xe9\x16\x28\x6f\xdb\x81\x35\xfa\x50\x5c\xf4\xa5\x99\xa2\xb7\x02\xfc\x28\x5a\xea\x4c\x2e\x56\x5e\x3a\x32\x9e\xd0\x4d\x96\x90\x08\x6d\x54\xb9\x92\x54\xa4\xad\x55\x96\x93\x5d\xb1\xe5\x60\x07\x87\x48\xd2\x21\x91\x6a\xd6\x97\x0f\xca\xa5\x51\x0f\x94\x26\x3f\x99\xd9\x60\xb9\x95\x82\x96\x37\x59\xb2\xf0\x54\xe4\x9e\xe5\x7c\x19\x07\x45\x74\x46\x7f\x0c\xf2\xa3\x1c\x5e\x20\x96\x55\xe5\xc0\x5a\x75\x4d\x6b\x6b\x98\x1a\xe5\xd0\xd8\xe9\x64\x42\xc7\xa2\x66\xbe\x7a\x4b\x17\x44\x79\x11\x1f\x45\x97\x42\xdb\x0b\xd3\xb4\x6e\x91\xc5\xe2\x74\x6a\x1b\x8b\xea\x0c\x14\x00\xc9\xd1\x66\x82\x4a\xd2\xab\xcb\xf4\x3c\xa8\x66\xb0\x75\x8a\x4b\xd1\x52\xa3\x95\x08\x64\xd6\x7c\xed\xc1\xb1\xaf\x72\xb9\x41\x85\x0d\x16\x9b\x59\x13\x36\x89\x82\x1a\x94\x4d\xd4\x60\x40\x94\x37\x29\x70\xab\x28\x14\x26\xf8\x64\xdb\x3f\x0d\x72\xda\x80\x41\xfa\x80\x7d\x94\xe0\x81\x33\x68\x80\xe7\x4f\x83\xfc\xa7\x68\x1e\x15\x1e\x22\x36\x01\x44\x59\x95\x58\x42\xf9\x46\xbe\x51\x26\x8f\x7e\xf5\x6d\x7b\x3a\xd3\x80\x2e\xa2\x39\xcd\x8b\x60\xbe\x28\x2d\xa2\x20\xf4\xea\xe2\x19\x49\x19\xef\x32\xb2\xcb\xaa\x55\x6a\x20\xd4\x99\x30\x9a\x4c\xa2\xf1\x32\x86\xa7\x48\x65\x98\xd6\x40\xe6\x40\xd2\x22\x88\x5f\x34\xa9\xc0\x82\xc4\xe2\xb3\xb9\x58\x05\xb8\x66\x74\xe6\x92\x75\xb3\x5d\xa1\x33\x2a\xe8\xbc\x6b\x3f\x42\x74\x2c\x41\x01\xca\xbd\x73\x37\x16\xb6\x4f\x7c\xe3\x05\xeb\x56\xf8\x29\x57\x2a\x5d\xef\x34\x5a\xde\x1f\xa2\x69\x42\x33\x12\x47\xb9\xfd\x5c\x7a\xa5\x45\xcd\xab\xc9\xfd\x6b\x9b\xb8\x8b\x5b\xc0\x97\xaf\x71\x01\xa0\xd5\x35\x9e\xb9\x92\x30\x72\x96\x70\x62\xcd\xdc\xd4\xcf\x8a\xc0\x26\x57\xcd\x1e\x42\xcf\x65\x14\x08\x34\x0d\x7c\x0a\x90\xea\x07\xf7\xa1\x11\x93\x8d\xd3\xa9\x17\xf1\x98\xb3\xfb\xd0\x1e\xa7\x53\xad\xb6\x75\x91\x0e\xf5\x1a\x78\xc7\x15\x62\x74\xa3\xeb\xb2\x68\xc2\xbe\x0c\xf1\x42\xe1\xc3\xca\xf0\x2c\x74\xbb\xe8\x0e\xae\xd3\x91\x1f\x8c\x8a\x1b\x08\x22\xde\x4a\x8c\x26\xe2\x74\xea\xa9\x5a\xa6\x96\x54\xa9\x0a\x99\x67\x3d\xb8\x02\xac\xd7\x5f\x9c\xcf\xa2\x9c\xed\x8a\x8b\x34\x2f\x6e\xa0\xc0\x78\x97\xe6\xd5\xf2\xa9\x1b\xba\xab\x72\xf7\x74\x2b\xc5\x13\xcd\x3a\x89\xb7\x4e\xf6\xdd\x5f\x04\x97\xf0\x1e\x67\xd7\x50\x5a\xe2\x2c\x81\x64\x48\x2a\x8a\xd8\x7b\x7a\x96\x99\x18\xf6\x3c\xcd\x3e\x7f\x4c\xdf\x65\xe9\x19\x2d\x2f\x83\x80\x70\xd9\x85\x38\x7b\x94\x17\x94\x10\x28\x22\xc5\x04\x07\x2c\x33\x2c\xf0\x39\xcf\xe0\x9d\xe4\xee\x79\x30\x63\x47\xe9\x64\xd7\xf8\x7a\x46\x8e\xd1\xe7\x09\x19\x29\xf3\x97\x6b\xdd\x2a\xbf\xbb\xe1\xd7\x38\x71\x9c\x9e\xc3\x73\x24\xa9\x65\xaa\xaa\xbe\xfa\xf9\x0c\x0f\xb9\xc9\x88\x89\xa4\x49\x7c\xc9\xe3\x88\x14\xc6\xab\x1e\xf9\xb2\x86\xbf\xa0\xf1\x3d\x08\x93\xcf\x6b\xc8\xc8\x7e\xec\x85\x1f\xd6\xd8\x8a\x0e\xd6\xc7\x46\xbc\x4b\xdd\x23\x02\xfd\x0b\xeb\x66\x2f\x37\xab\xa3\xf4\x26\x1b\x47\x35\x61\x0b\xba\x06\xfc\xd2\x8b\x45\x94\x5d\x7a\x56\x3c\xca\xc5\xe4\x96\x73\xb7\x43\x5e\x68\x96\x57\xb6\x04\x2c\x50\xcf\x02\x00\xca\xf6\x09\x74\x16\x44\x77\xc7\xb7\x2a\xdf\x07\xe7\x92\x64\x44\x8a\x17\x0c\x55\xbf\x97\x8f\xa3\xc8\x5e\xbe\xb2\x0c\xde\x46\xff\x9e\x0b\xc4\x29\x38\x1d\xb8\x47\xaf\x0a\xdd\x00\xf8\xe1\x86\xe0\x79\x3e\xe6\x30\x18\xac\xb2\x22\x60\x6d\xe2\xd5\x58\xba\x18\xf5\x72\xbb\xc5\x4a\xb2\x2e\x65\x38\x8a\x9a\xd1\xbf\x62\xaa\xb6\x7e\xd4\x17\x65\x07\x5f\xaa\x89\xa4\x7e\xbe\x3c\xe5\x2f\x14\x3b\xc3\xde\x36\x5f\x95\xad\x8b\x70\xdc\x32\xbc\x4d\x29\x77\x56\xad\xe1\x45\x8b\x6c\x10\xb7\xf0\xb6\x71\xc4\x80\x5e\xf1\xfb\xe5\x84\x9e\xc3\x55\x73\xc7\x0c\xf2\x0e\x37\x72\xa7\x41\xd2\x8f\xf2\x7f\x04\x71\x14\x76\x20\x08\x8b\x48\x79\x11\x65\x74\x5c\x74\x7c\xd7\x71\xc2\xd7\x1d\x00\x8a\x1a\x3b\x5d\xe7\xae\x0f\x0b\x4e\x3a\x36\x96\xec\x81\xa7\x5a\xc3\x9d\xa2\xa7\xa2\x06\x55\x88\x9e\x99\x35\x71\x4d\x94\x6d\xdc\x24\x1c\xde\x4b\xd8\xb6\x0c\x56\xaf\x39\xc9\x87\xcb\x64\x1c\x25\x7e\x71\x48\x78\x98\x47\x73\xb9\x6e\x26\x11\xd7\xe1\x96\x21\x84\x83\x7b\x2e\xb0\x8e\x8d\x92\x29\x08\xbb\x5e\x4d\x86\x0b\x66\x3a\x39\x13\xfe\xc6\x6a\x2a\xc0\x50\x66\xf9\x59\x34\x9d\xd1\xbc\xae\x3c\x86\x42\xb4\x23\x72\x3f\x27\xe9\x79\xf2\xa1\x08\x0a\xea\x73\x78\x89\x72\xcb\x1b\xc0\x55\xec\xd8\x35\x2c\x96\x71\x4c\xc3\xba\x2a\x30\x54\x89\x4e\x43\xfb\x3d\x2b\x09\x6d\x51\x77\xcf\x3f\xaa\x85\xe8\xe9\x7a\x2a\x2a\xa8\x29\xe9\xbb\xa9\x1e\x95\x67\xa1\x92\xc6\x25\xec\xc8\x93\x86\x60\x7d\x67\xc7\x51\x79\x16\x2a\x69\xb3\xb9\x91\x3f\x19\x95\x30\x36\xe5\x91\x27\x8d\xc3\x96\x59\x94\x8c\x4a\x73\x70\x39\xff\x80\xca\xf3\x4a\xca\xda\x8a\x5b\x4f\x15\x36\x88\xd1\x7b\xe3\x28\x3c\xf2\xa6\x3a\xf0\xf6\x41\x77\x54\x95\x89\x4b\xe3\xe3\xda\xc8\x93\x86\x61\xad\x49\xf0\x24\x62\x68\x9b\xfb\x8d\x4a\xd2\x39\xd7\x34\x8c\x18\xf9\x3d\x66\x6b\xb4\xf9\xb4\xcc\x35\x17\xdb\x3a\x5a\xa3\xed\xed\xeb\x93\xde\xf6\xe6\xbd\x5b\x97\x7b\x2b\xc6\xff\x1a\x2b\x46\x41\xe9\x77\x11\x9f\x69\xb5\x60\x16\x0d\x4d\x17\x79\xf8\x28\xd3\x26\x91\xa7\x7d\x85\xa8\x18\xcd\xe3\x58\x04\x71\x3c\xb0\x22\xbd\xc2\x03\x75\x3b\x4e\x94\x1b\xdd\x42\xbe\xb2\x70\x43\xe2\x55\x44\xb5\xf0\xc5\xc4\xfb\xc4\xb7\x46\x11\x75\x01\x07\x83\x5e\x3d\x22\x82\xae\x54\xec\x2d\xb8\x56\x9e\x74\xbb\x6a\x21\x90\x64\x00\xe7\x55\xa8\x53\x7e\x63\x18\x19\x2c\x5a\x80\x88\x4f\x0c\x71\x27\x11\x39\xd8\xfe\x60\x4f\x86\xe1\xbb\x15\xcc\x4f\xf4\x9b\x46\x7c\x64\xca\xa6\xc6\x79\xe9\x06\x21\xd0\xe5\xd9\x42\xc7\x8b\x04\xc7\x28\xc0\xeb\xf9\x2b\xbc\x6c\x9a\xf3\xb0\x1b\xeb\x42\x68\x6c\xd6\x61\x2c\x04\x56\x76\x1a\x77\xef\x07\x87\x94\x64\x0e\x8e\x7e\x29\x9e\xfb\xba\x83\xf3\x8f\xcd\x76\xe6\x51\x21\x9e\x76\x34\x1e\x1a\x22\xa2\x2a\xc6\x25\x8e\xca\xed\x0b\xe4\x16\xe5\x64\x9c\x66\x99\xeb\x63\x15\x4e\x5e\x41\x41\xf7\xb2\x69\xee\x0b\x7b\xa9\xe3\xee\x3f\x24\x7f\x83\x93\x5b\x4e\xbe\xc0\xb9\xed\x9a\xb5\x17\x15\xe2\x91\x93\xe1\x86\xd5\x33\x55\xb8\x9d\xd2\x39\xd2\xa7\x77\x0e\x05\x28\x72\x4c\x8e\x02\x8d\xf8\xc1\x40\xbe\x66\x03\x4d\x97\xe1\xdf\x08\x36\x4f\xf0\xaa\xa9\xc3\xd9\xb1\xad\x36\x80\xb7\xb0\x59\x70\x29\x5f\x76\x8a\xb9\x5b\xef\x38\xd1\x50\x83\xae\xf2\xd1\xcf\xce\xe3\xce\x05\x90\x75\xc7\x21\xc0\xb9\xbf\xed\x4a\x78\x7d\xe5\x65\x94\xb1\x0a\x58\xcf\xca\x41\x47\x20\xb1\x23\x09\x71\x7d\x77\xb7\x8c\x90\xcd\xa7\x7c\xec\xcc\x2d\x02\x12\x56\x84\x0d\xec\x38\x1e\x36\xaa\x7c\x52\x4b\x6d\x13\x98\xc2\x61\x52\x31\xa2\xaa\xa4\xef\x38\x98\x87\xbc\x9c\x4d\x43\xfb\xa2\x2f\xf1\x4f\x1d\xc4\xaa\x55\x6d\x19\x58\x49\x79\xaa\xfd\x4a\xb2\x33\xe2\xf0\xae\xce\x30\x56\xe5\x17\x66\xf8\xdc\x92\xf8\xbc\xd7\x9a\x9b\xe3\xe5\xd3\xf1\x04\xcb\x2d\x52\x7f\x28\x0c\x23\x98\xee\x2e\x29\x09\x73\xe1\x8b\x96\x20\x1e\x72\xa1\xe1\x1a\x51\x7a\x2b\x4c\xec\x4a\xc2\x38\x49\xd4\xdf\x2c\x5c\x8d\xb7\x78\xe5\xbc\xdf\x28\x68\x8d\xf0\xb7\x3f\xec\x91\xa7\x52\x0b\x55\xd1\xc4\x32\x59\x04\xe3\xcf\xfc\xae\xd1\x34\x36\x85\x24\x43\x27\x65\x26\xe9\x2e\x18\xfa\x91\x54\x56\xc5\x7f\x28\xd2\xdb\x25\x5b\xe4\x99\x4c\x94\x21\x01\x88\x3c\x07\x6a\x1f\x19\xca\x91\x7f\x59\x44\x00\x2c\xe4\xf4\x44\x71\x73\x46\x85\x0e\x07\xfb\x33\x57\xc1\x20\x8f\x87\x27\x64\xe4\xf3\x5a\xbf\x0f\xb1\xd0\x03\x14\x7e\x5e\x22\xcb\x0e\x70\x1f\xc4\x31\x5e\xdc\xfd\x7e\x5f\xae\xef\x7d\xbb\xac\xb5\xf9\x38\xfe\xa2\x0e\xf8\x76\x07\x71\xae\x25\x28\xdb\x8d\x02\x55\x43\x8f\xbb\x06\xb2\x2b\xe6\xce\x11\xe1\x31\xae\x3c\x74\x05\xc6\x73\xc9\x20\x09\x4d\xa7\x42\x12\x8c\x07\x82\xe7\x27\x23\x56\x07\x8f\xa2\xc9\xc0\x05\xda\xbc\xb4\x2b\x66\x15\x02\x59\xd7\x51\x2d\xf4\xaa\x2c\x58\xf8\x2a\x91\xc0\xfd\xfb\xa6\x94\xc1\x2c\xa3\x5c\xb5\xc7\xc0\x41\x46\xcb\x7f\xc2\x87\xb8\x21\x16\x62\xf6\x03\x7e\xd0\x4d\xe9\x0b\x17\xc1\xe2\x8f\x5d\x4c\xdf\x54\x70\xdf\xe5\x92\x4b\x4b\x38\x6d\xf4\xb1\xee\x7b\x9b\xae\x75\xc3\x8a\xf1\xd1\x62\xc6\x91\x20\xaa\xee\x19\x5d\x73\x5f\xa2\x42\x29\xbc\x84\x3b\xc6\x7a\x40\xde\xf7\x9d\xb7\xe3\x4d\x1a\xec\xb9\x1e\x9f\x5c\x1e\x80\xfc\x3d\xc9\x07\x47\x86\x57\x90\x1e\x37\xdd\xd9\x31\xbd\x6c\xf3\x4e\xd3\xd0\x89\x28\x50\x64\x97\xd6\x43\x5a\x04\x0a\x6f\x67\xcb\xc7\x4b\x8c\xc7\xbe\x63\xf0\xb6\xd0\x71\x7c\x36\x71\x8a\xdf\x25\x14\x17\x42\xa5\xcc\xce\xcb\xd6\x91\x24\x53\xb9\x51\x34\x39\x57\xda\xdb\x86\x59\xa4\x76\x57\xb0\x5a\xf8\x53\x2d\xb5\xda\x35\x23\x49\x4a\x00\x0a\x8b\xdf\x1f\xc8\x10\x0e\x35\xc6\x59\xd3\x95\x0e\x71\x08\xdd\x20\xe1\x8e\x0b\x92\x50\x38\x37\x85\x18\xc8\xc9\x23\x79\x50\x75\x82\x41\xd7\x2c\x57\x23\x00\x22\x5b\x37\xd6\x3c\x74\xcc\xeb\x49\x51\x5d\x2d\x78\xf3\xc8\x13\x34\x2f\xa2\x79\x50\xd0\x1f\x03\x50\x20\xd6\x51\x15\x02\xaf\xa3\x28\x5c\xf3\x5d\x50\xd3\xd7\xa7\x8e\x66\x33\x84\xc6\x55\x37\x3b\x1e\xd0\xb2\x99\x79\x2f\x9b\xa1\x32\x96\x1e\xc4\x04\x92\xba\x40\x21\x1f\xe0\xa9\x98\xd2\xe2\x85\x1d\xeb\x4a\xee\xac\x76\x35\x75\x73\x25\xea\xba\xe3\x79\x6a\x84\x78\x79\x55\x2d\x56\x26\x0f\x13\xd4\x5c\x6a\xbe\x45\x44\x4e\x5c\x54\xe2\x19\x91\x7d\x25\xc2\x7e\xdb\xf0\x9c\xaa\xfe\x1b\x45\xe8\x54\x85\x56\x1d\xe4\xd7\x0c\xd7\xa9\x75\x34\x6c\x80\xd9\x62\x2c\xfd\xc0\xe5\xfc\xd4\x5c\xc7\x88\x04\x74\xb9\xb9\x4d\xc5\xb8\x44\xd9\x3f\x36\x57\x22\x46\xd8\x22\x09\x86\xc5\x14\x23\xfa\x0e\x9e\x13\xd7\x71\xa2\xa5\x71\x7d\x06\xde\x98\x3f\xb1\x1e\xb7\xc9\x88\x7f\x58\x3b\x49\xbb\xe7\x08\x2f\x23\xed\xb0\x50\xe5\x29\x57\x8b\x62\x38\x27\x3a\x8b\x77\x5c\x3a\xf2\xe5\x0c\xb2\x96\x18\x64\x9c\xa1\xb2\xed\x47\x45\xf0\xaa\xde\x7a\x3c\xc1\xb6\xf0\x04\x17\x86\xa0\xb3\x6e\x62\x47\x9b\x19\xc1\x36\x5f\x60\x19\x4a\x3a\xab\xd1\x69\x65\x5b\x85\x85\xce\x7e\xb0\x58\xc4\x97\xc2\x75\x56\x23\xc2\xea\xda\xf6\x79\x7c\x0b\xb0\x9a\x61\x89\x37\xaa\xbb\x66\x1e\x44\x40\x2a\xcd\x78\x74\x4c\xaa\x5b\x07\xa3\xf2\x4c\xd8\xd7\x8a\x47\x25\xd3\xf5\x8a\xc7\xbe\xc3\x4a\xc1\xc5\x61\x53\x63\xb8\x0c\xd0\x95\x9a\xbd\x93\x5f\x56\xdc\x14\x91\xf8\x48\x74\x52\x69\x31\xbd\x5b\x4b\x9f\x57\xec\xf3\x4f\x19\x8c\x4b\x96\x05\x02\x8f\xb2\xf1\x32\x0e\xb2\xf5\xf5\xf5\xf5\xea\x10\x5c\x92\x82\x76\xee\x24\x08\x17\xd7\xfe\xb6\x46\x5b\x4f\xfc\x1e\x8d\xb6\xee\x6f\xff\xef\x6f\xff\xff\xda\xb7\xff\xe2\xea\x9f\xc1\xca\x20\x69\xfe\xd0\x2e\xbf\x5b\xd0\x16\x9f\x65\x41\xb5\x21\xc0\xda\x60\x00\x41\xe0\x82\x8c\x91\x32\xdb\xc1\x96\xb9\x39\x44\x46\x70\x61\x34\x99\xd0\x8c\x26\x05\xa1\xc9\x59\x0e\x85\x4e\xb3\xf4\x3c\xa7\xd9\x1a\xf2\x70\x7b\x1e\x25\x61\x7a\x0e\x1a\x0b\x14\xfa\x84\x3c\x78\x20\x72\xfa\xff\x7c\xf3\xd3\xeb\xa2\x58\x08\xe7\xc9\x9c\x6b\x9a\x69\x64\xd7\x0f\x0b\xac\x4f\x44\xee\x88\xa6\x49\xca\x18\x41\x1c\x25\x94\xf5\x24\x49\x43\xba\x86\xdc\xe5\x39\x35\xaa\x81\x5f\xcc\x63\x36\x32\xb1\xb1\xb5\xbb\x4d\x1b\xb9\xe6\x98\xfc\xe7\xeb\xf7\x5b\x46\x75\xb3\x6c\xab\xdd\x2d\x2d\x25\x25\x07\xd6\xc2\x3b\x89\x4c\xd7\x24\x02\xe4\x27\x26\xda\x83\xbf\x58\xee\x5d\x9e\xf5\x52\x19\x40\x18\xe5\xf1\x96\x3f\x4b\xf3\xa2\x47\x8a\x68\x4e\xd3\x65\xd1\x63\x15\x66\x3d\x50\x32\x9f\xa7\x99\x78\xec\x08\x9b\x09\x83\x23\xbb\x04\xfe\xbb\xba\x22\x6d\x41\xec\x71\x3a\x0e\x62\x96\x38\x7a\xfa\xcd\xe3\x6f\x20\xd2\x32\xdf\x7b\x78\x85\x6c\x27\x14\xbf\xae\xae\xc8\x50\x65\xb3\x66\xc8\x2e\xb4\xa6\xd2\x64\xa3\x64\x57\xb5\x5f\x2b\x3c\x2d\x32\xba\x80\xd0\x85\xf4\xdc\x9a\x32\x4b\x76\x12\x80\xef\xd1\x59\x46\x48\x4e\xcf\xd3\x34\xa6\x41\x72\x0d\x77\xac\x6c\x7f\x96\x12\x8c\xc6\xb2\xf0\x53\x8a\x0e\x7c\x66\x5b\x86\x33\x2c\x8c\x69\x24\x77\x99\x1d\x30\x2f\x02\x59\xf5\x1c\xd5\xfc\x06\x85\x13\xd2\x9a\x78\xc5\x86\xb2\x09\xd1\xe2\x15\x0c\xf9\xf5\xfb\x2d\x1d\xe8\x98\x4b\x5a\x08\xf3\x68\x22\xe0\xc9\x19\xf6\x06\x69\x55\x64\x8c\xa7\x23\x5e\xa8\xad\xe9\x5a\xd3\x05\x4d\x3a\xed\x77\x87\x1f\x3e\xca\xd8\xac\x9c\x70\x78\xe7\x76\xd6\x90\x6b\x49\x98\xdb\x07\x0f\xcc\x49\x35\x0e\x7d\x4b\x30\xa8\x69\x3f\x0f\xf2\x68\x4c\xda\x64\x03\xba\xf0\x7c\xc9\xd8\x03\xaa\x62\x83\xb4\x47\xea\xaa\x50\xd5\xd3\x2f\x52\xf1\xf8\xae\x7d\x1a\xe4\xf4\xc9\xe3\xb6\x35\x7e\xed\x58\xfd\x35\x0d\x42\x9a\x75\xda\x7b\xc0\x57\xa3\x5f\x03\x7e\xda\x82\xf6\xf9\x08\x2b\x0a\x31\xf9\x98\x26\xc5\x23\x76\xd0\x6e\xf7\x48\x9b\x49\xfe\xd1\x18\xaa\x18\xfc\x92\x4b\xb5\xa3\xba\xb1\x12\x53\x56\x43\xae\x3c\x04\xcf\x65\x32\x46\x87\x6a\x5b\x93\xec\xbb\x78\x5e\xa0\xeb\x6b\x7f\xb0\xf5\x2a\xd2\xcb\xed\xe0\x9b\x52\x97\x66\x93\x9c\xa4\x19\x93\x56\x45\xf4\x6e\xa0\x47\xad\xdd\xd7\x98\x4b\xc2\x0e\xbc\xf4\xe0\xef\x0e\xa2\xc9\xa5\xaa\x5f\x20\x59\x2a\xf2\xb1\xdf\x74\x9f\x35\xc0\x7e\x9a\x24\x54\xbc\xc7\x90\x14\xa6\x29\xd1\xb8\x5c\x94\xad\xcb\x08\x26\x1f\xe9\x45\xe1\x74\x50\xc0\xa2\x67\x28\xc2\x2a\xdf\xec\x56\x55\x97\xde\x8b\xfa\x3b\xbe\x06\xf1\x2a\x69\x1e\x4c\x1b\x68\x20\xa8\x21\x82\x3d\xc5\x71\x2a\x28\x41\x64\xbd\x72\xc2\xd7\x90\x22\x8b\xa6\x53\x9a\xf1\x98\x5b\x6c\xf6\x41\x6c\x51\x0e\x74\x19\x0e\xea\x08\x06\x7a\xe0\xa3\x1a\x33\x74\x76\x13\xfa\x01\xe3\x95\x1d\x83\x9b\x24\xe0\xec\x3c\x2f\x82\x82\x8e\x67\x41\x32\xf5\x2b\x10\xf8\xb3\x02\x89\xf8\x20\xbc\x04\x83\x7e\xb8\x11\x7e\xcc\x38\x8c\xcd\xf2\xd6\xcd\xd0\xd7\x0d\x28\x46\x03\xca\x5b\x25\x14\x53\xcd\xbe\xcc\xaa\xa1\x28\x38\x93\x79\x6f\xad\xd4\x8d\xd5\x8a\xb4\x45\xf0\xd5\x96\x7d\xb1\x65\xb4\xcc\xce\x82\xd7\x16\x8a\xf5\x46\xe0\x62\xd6\xac\x2c\xef\xeb\xa5\xf7\x91\x97\xea\xe0\xcd\x43\x2c\xe4\xbb\xe5\x00\x76\x17\xaa\x98\x80\x58\x69\x78\x5d\xe9\xcb\xf2\xf8\x92\xd1\x3b\x7f\x34\x0b\x8b\x8b\x51\x75\xc9\xda\x8a\x72\x51\x3f\x35\x99\xa9\x12\x02\xa4\x82\xd3\x16\x06\xd8\xf9\x21\x69\x17\x64\x12\x44\x31\x0d\xfb\xe4\x90\x9d\xd3\xce\x23\x76\xf6\x08\x20\x4c\x5e\xf9\x6a\x42\x6d\x7a\xe6\x42\xe3\x53\xe9\x33\x54\x38\x96\x28\x1c\x91\xef\xd4\x9f\xd4\xf7\xb1\xdd\x27\x5b\x8c\x47\xa4\xbd\xd5\x1f\x2a\xe5\xa1\xd4\x3f\xb6\x13\x5a\x7c\x8a\xa3\xbc\xa0\x09\x38\xba\x5c\xb3\xb4\x87\x27\x86\x41\x97\x54\x70\x65\x3c\xe6\x9f\x4b\xbe\xd2\xaa\x90\x0d\x52\x4f\x82\xa3\x2e\xc0\x43\x97\xaa\x02\xe3\xb4\xcf\xc4\xdc\xd6\xe8\x29\xfb\x65\xc8\xcf\xad\xd1\xe6\xb7\xec\xe4\xbf\x7d\x7f\xf2\xbf\x3f\xf9\xff\xc5\x4f\xfe\xda\xf0\x1f\x1e\x4b\xde\x91\xd1\xbf\x32\xe4\xc4\xa7\xca\xd3\x68\xca\x6d\x70\xfb\xbf\xf0\x13\x3a\xbf\x07\x09\x7f\xa2\x13\x73\x43\x50\xc1\x4f\x2f\xd1\x83\x3d\x63\xe3\xe4\x10\x9c\x5d\x9c\xcf\x58\xef\x3b\xa6\x81\xd6\xf7\xbc\x30\x79\x48\xb6\xdc\x17\x7f\x60\xf1\xc7\xa4\x78\xf3\xdd\x23\xf1\xbf\xc4\x13\xcc\xfd\x9d\x38\xd5\x05\x09\x39\x78\xbe\xf7\x56\x4c\x72\x48\xbe\xfb\x96\x8c\xd3\xf9\x62\x29\x02\x0f\x9d\x5e\x92\x79\x7a\x16\x25\x53\x14\x5e\xef\x31\x19\xcf\x82\x0c\xf6\x02\x7e\x33\x1b\x72\x53\x2a\x69\xae\x2e\xa1\x63\xca\x1f\x2d\x14\x29\x6b\x90\xe3\x2a\x27\x9d\x3d\xb2\x4b\x36\x87\x3d\xf2\x9c\xfd\xbf\xd9\x23\xfd\x7e\xbf\x47\xfe\x8f\xec\x92\xed\x6f\xba\xec\xb0\x43\xf2\x05\x1d\x47\x93\x88\x2f\xa4\x83\x0f\x87\x9b\xdb\x4f\x36\x9f\xd8\x26\x66\x51\x9e\x42\xba\x18\x87\xeb\x66\xf9\x9a\xbf\xc5\x65\x1d\x61\x03\x34\xaf\xd6\xf0\xcd\xb2\x90\xa4\x42\x09\x26\x7c\x36\x98\xf5\x1b\x13\xca\x2a\xc6\xf3\xc8\x46\xd4\xde\x6b\xf7\x19\x5a\xf6\xd3\x90\xee\x15\x9d\x21\xd2\x5a\xb3\xb1\xb5\xff\xcf\xc9\xe6\x0c\x90\xbf\x17\x06\x62\x2d\xd2\xa3\xc5\x82\x66\xfb\x41\xae\x55\xd9\x28\x9b\x3f\x3b\xee\x3c\xee\xca\x97\xc0\x22\x61\xd8\x7b\x6c\xdd\x98\xf1\xdc\x45\x1c\x15\x9d\x76\xbb\x6b\xbe\xc2\x4e\xba\xa6\x75\xd5\x38\x0d\xd9\xe0\x12\x5f\xe7\xa5\x7c\x08\x30\x3f\xec\x92\x3d\x26\x10\xc2\xc7\xf7\xbb\xe4\xff\xba\x4e\x50\x0c\xcf\xcc\x8a\x89\x35\x20\x95\xcf\xe5\x90\x92\x47\x64\x8f\x6c\x90\xcd\x21\xb2\x33\xf2\x05\x8a\x90\xc1\x78\x6d\x1b\xa6\xeb\x6e\xff\x97\x34\x4a\xd8\x30\x6d\x4b\xc5\xf1\x12\x9c\x00\xc3\x14\xbf\x39\x7c\xc1\x08\x7b\x73\x28\x99\x92\xb0\xf0\x03\xca\xf7\x50\xdc\xb7\xc3\x27\x8f\x6d\x82\x9b\xa7\xe1\x77\xdf\x6e\x0e\xcb\x08\xcd\xa4\x2f\xed\xd8\x9b\x53\x93\x28\x5c\x49\x45\x19\x9d\x07\x51\xc2\x75\x47\x2c\x4f\xdf\x3d\x0a\xd7\x41\x26\x7b\x10\xc0\xda\x6e\x79\xab\x6b\x39\x45\x02\x66\x25\xc1\x94\xc5\xeb\x77\x86\x89\x9c\x6e\x12\x64\xed\x83\xa4\xe0\x3e\x7c\x7a\x64\x73\xd8\x25\xff\x7f\x86\xb5\x0d\xa7\x16\xee\x72\x49\x98\x9f\xfb\x5e\xfe\xaa\xba\x54\x49\x5d\x9f\x31\x4f\xf5\xef\x90\xb8\x09\x3a\xac\x03\x61\xf0\x0f\x17\xea\x90\x20\xde\x3a\x08\xf6\x29\xe7\xcb\x3f\x39\x03\xec\xfe\xdd\x3f\x09\xc2\x12\x5a\x2f\x39\xb7\xab\x4e\xd8\xe5\xba\x7e\x52\x88\xca\xb5\x9c\xcb\xd7\x39\x16\x51\x31\x98\x3d\x95\xe3\xf4\x3d\x40\x59\x52\x8c\x66\x43\xb8\x56\x6c\x0d\x6b\xc5\x58\x4e\x1f\xd5\x58\xe5\x0c\x01\x74\x44\xf9\x73\xe9\xab\x00\xbd\x54\x10\xe1\x71\xc9\xe6\x13\xc4\xc2\x4e\x83\x9c\x6e\x3f\x21\xbb\x50\x46\xab\x87\xb6\x9f\x18\x26\x00\x61\x48\xb9\x66\x11\xf6\xc0\x0e\x2f\xd4\x23\x9b\xdf\x98\x92\xb0\xea\xe7\xf3\xd3\x20\xe9\xf0\x62\x26\xf3\xb3\x16\xb3\xf0\xb7\x82\x16\xee\x73\x36\xf4\x22\x35\x76\x2f\x36\x7d\x04\x3c\xf8\x66\x97\x72\x45\x73\x65\x12\xd8\xeb\xbe\xe3\xc1\x51\x92\xb4\x10\x42\xd9\xf7\xd1\x0f\xad\x29\x48\x24\xdc\x8f\xcf\x44\x23\x35\x9f\x05\x5c\x5a\x83\xfd\xed\x62\x1c\x2f\xf3\xe8\x4c\xc5\x72\x8d\x4e\xa3\x38\x2a\x94\x80\x73\x1a\x24\x9f\x07\xa7\x59\x90\x8c\x67\x24\xa7\xd9\x59\x34\x96\x1b\x60\xc0\x1d\x0a\xb7\xbe\x1f\x44\x3f\xf4\x6d\x1a\x52\x71\x55\x72\xb9\x0b\x4d\x68\xc6\xb6\xa1\x20\x9e\xa6\x59\x54\xcc\xe6\x24\xa4\xf9\x38\x8b\x4e\x39\x5b\x12\xf2\x0f\x4d\xfa\xe7\xd1\xe7\x68\x41\xc3\x28\x00\x21\x88\x7d\x0d\x0e\x92\x82\x66\x49\xc0\x9f\x4e\x7c\x7a\x1e\x24\x9f\x3f\x09\x6f\xc6\x9f\xf8\xbc\xfe\xff\x7e\x14\x23\x4d\xa6\x9f\xd8\x10\x3f\xc1\x5b\xa2\x4f\x61\x34\x8d\x9c\xa7\x1c\x72\x6a\x7c\x14\x79\x2a\xf7\x54\x39\x03\xd2\x19\x4e\x91\x7a\xb6\xd9\x06\xb4\xfa\xdc\x5e\x91\xa7\x16\x5b\x14\x33\xba\xcf\xf7\xa9\xf6\x3f\x5f\xb6\x77\xd6\xbc\x3c\x53\xf0\xd8\x8e\xb5\x73\x77\x70\x05\x1b\xa4\x3d\x04\x51\x09\x5a\xc1\xe6\x2e\x0c\x1d\x2f\x18\x36\xc8\x2e\xe9\x70\x71\xaa\xf3\xdd\x53\xf2\x48\x37\xd1\x95\xcf\x06\x1e\x6d\x59\xfb\xad\xf2\xf6\x61\x36\x85\xea\x14\x0d\xd6\xa8\xad\x04\x13\x41\xb8\x02\xc2\xe6\x11\xf5\xa3\x24\x2f\xa2\x62\x59\x48\x9f\xdc\x51\x48\x93\x82\x6d\x5a\x76\x24\x0a\x5e\xcb\x41\x12\x46\x19\x35\x0d\x18\xcc\x37\x36\x79\x4f\xca\xb2\xea\x91\x0d\xbc\x9a\x6a\xa1\x96\x5a\xd0\x54\x4b\xb7\xd5\x5a\x85\x17\x99\x3d\xf1\xfa\xe9\x36\x8f\xc0\x26\x67\x68\xbf\xfc\xf8\x9a\xcd\x83\x7c\xdd\x82\x31\x80\x52\x55\xdf\xba\x16\xbf\x4e\xab\xf8\xb5\x7c\x4a\xc7\x91\x2b\xc2\xd5\x47\x39\x7f\x29\x87\xf9\xb8\x23\x77\x82\xe7\x96\x52\x79\x53\xed\x45\x1e\xc5\x87\x54\x78\xf0\xe7\x74\xbc\x25\x25\x74\x1e\x20\xbf\x30\x95\x72\x42\x84\xfd\xcb\x44\x9c\xac\xb0\xf0\xa7\x9d\xcb\xd4\xea\xca\x15\x16\xa0\xeb\xa5\xaf\x07\xf1\x98\x75\x98\x12\xef\xa8\x7a\x24\xf5\x68\x6d\x60\x6c\x58\x5b\xe3\x8e\xd2\xa2\x84\xc1\x7f\xfe\xf9\xf2\x78\xf8\xe8\xbb\x93\x2f\x5b\xd7\x9d\x97\x1f\x5f\xb3\xdf\x7b\x8f\xfe\xef\xe4\xcb\xe6\xf6\xf5\x95\xfa\xd8\x1e\xf6\xb6\x37\xaf\xbb\xff\x33\xe8\x17\xa0\x04\x55\x1b\xb8\xf1\x2e\xaf\x8c\x31\x20\x70\xfe\x3c\x6f\x73\x45\x84\x89\x27\x98\x70\xfa\xf7\xa2\xed\x85\x5e\x82\x77\x83\xb7\x17\xee\x4a\xb2\x10\xa7\x07\x85\x1f\xf7\x6c\x3f\x26\x57\x57\x65\x79\xdf\xdc\x70\xd8\x13\x12\x25\x25\x03\x37\xb8\xcf\xdd\x0c\xdd\xcb\x46\x1a\x0d\x7e\x6b\xd8\xc8\x6a\x93\x8b\x94\x6c\xa4\xf9\x72\xce\x00\x8f\x72\x71\x7c\x98\xa7\xe1\xa3\xef\xbe\x7d\xb4\x39\x54\xd9\x70\xc6\x85\xde\x8d\xd3\x98\x74\x0e\x3e\x1c\x0e\x0e\x5e\xee\x13\x76\x6e\x18\x6d\x0d\x87\xdb\x5d\x9b\x27\xa3\x6a\xdd\x53\x28\xca\x75\x06\x2e\xf3\x1a\x0e\x5b\x9c\x09\xb7\x7a\x64\xab\x99\xad\x2a\x66\xaa\xc6\x96\x42\xe8\xb4\x4f\xfe\xf9\xfe\xe5\x8f\x8e\x87\x44\x55\xc0\x3f\x9a\xd2\x1a\xdd\x49\x45\x90\x75\xc3\xd3\x04\xd0\x01\xf7\x79\xce\x90\xbf\xed\x91\xc7\x5d\x32\x22\xed\x76\xa3\x71\x8f\xe3\x08\x1e\x92\xa9\x0e\x82\xf2\x29\x4a\xec\xf1\x31\x2c\xfc\xb8\xf7\x8f\xc3\x57\xff\x3a\x7c\xff\xbf\xf6\xac\x42\x1d\x25\x73\x6a\xd7\xef\x9d\x5c\x0e\x74\xeb\xb1\x6f\x6e\xae\x3e\x72\xb1\x9a\xfc\xe7\x12\xf7\xe0\xe1\x0e\xcd\xa9\xc0\x19\x5e\xe0\x39\x87\xe0\x7b\x27\x31\x38\x9f\xe3\x33\xe3\xd0\xe1\x0e\xf8\x31\x3a\xc4\x96\x1e\x65\xe4\xf9\x43\x9d\x52\x8c\x13\x2a\x3f\xa3\x98\xe7\x99\xcd\x27\xdd\x1e\xd9\x1a\x2a\xd7\x6a\x86\x94\x27\xd1\x6b\x0d\x52\x16\x6e\xb6\x40\x4b\xbc\x61\x1d\x40\x16\x57\xea\x63\xbd\x62\x6b\x64\x7e\x5e\x9f\xf4\xb6\x1f\xdf\xab\xf1\xef\xd5\xf8\x7f\x71\x35\xbe\x50\xe1\x2f\xc6\xd5\xf6\x7b\xb7\xb0\xb8\x6b\xe9\x98\x9d\xad\x9d\x95\x62\x0d\xd6\xd8\xe9\x71\x3d\xd3\x62\xec\xb5\x04\x5b\x04\xc5\xac\x47\x12\x6a\x58\x7f\x7f\x02\xcd\x85\xf3\xf0\x54\x5e\x55\xe3\x68\xe7\xd2\x6b\x81\xb0\xd7\x01\x1b\x1f\xf6\x1f\x4f\xd5\x59\x63\x75\xc3\x0b\x5c\xb1\x90\x09\x9d\x2f\x0c\x7a\xa4\xcb\x2b\x1f\x9b\x56\xb1\x7e\x9a\x74\xda\x30\xaa\x36\x8e\x0e\xdc\x35\xec\xa7\xf3\x94\x31\x31\xfe\x96\xf0\xe0\xdd\x3e\xd1\xf7\xca\xfc\x85\x61\xbb\x47\x28\x62\xbd\x9f\x38\x1b\x14\x17\xde\x1d\xdb\xcb\xa7\xb7\x07\x49\x88\xdb\x47\xcd\x97\x56\x46\xd6\xd4\x1b\x83\x9f\x0e\x3e\x7c\x7c\xf9\x16\x56\xd0\xfe\xe1\xdb\xb7\x2f\xf7\x3f\x1e\x1c\xbe\x25\xef\x5f\x7e\x78\x77\xf8\xf6\xc3\xcb\x0f\xa5\xad\x86\x41\x11\xe0\x66\xd9\x37\xde\x9c\x06\x0f\x85\x19\xe1\x3c\xb8\x18\xa7\xf3\x45\x4c\x2f\xa2\xe2\x72\x44\x9e\x00\x65\x59\x3d\x04\x5d\xa8\xb2\x43\x60\x55\xe9\xfd\xa6\xeb\x09\x90\x24\x6c\x0e\xbe\x98\x91\xdd\xe1\xe0\x17\xda\xb6\x13\xa2\x3b\x3c\xe2\x3d\xf0\x97\x90\x9c\xcf\xa2\xf1\x8c\xcc\x83\x62\x3c\x13\xe2\x2b\xdf\x84\x18\x43\x0b\x8d\x72\x9e\xb0\x18\xd0\xb4\x3f\xf4\x3b\x5c\x47\x39\xbd\x05\x0b\x04\x17\x5c\x54\xff\xc9\x4f\xc8\xc7\xf0\x36\x2e\x0a\x4f\x5c\x67\xfb\xaa\x30\x1b\xab\x00\xdb\x71\xa0\xec\x20\xfa\xa5\xc1\xa5\xa1\x1a\xd1\x77\xbb\xa2\x6b\x07\x8b\x93\x28\xa3\x86\x47\x00\x1b\x5d\x65\xe3\x61\x43\xf1\xb4\x5e\x01\xae\x23\x5d\x63\xd3\x16\xfd\x17\xd2\x98\x16\xb4\xaa\x06\x7b\x30\x36\x6e\xf0\x2b\xec\x9f\xd9\xae\x05\x84\x28\x08\x82\xd7\x07\xca\x1d\x6e\x2b\x95\x70\x67\x39\x24\xe5\x3e\xa4\xa3\xa2\xbf\xb6\x26\x85\x41\x93\x84\xd7\x6c\xb5\x07\xbc\xc8\x64\xc2\x9f\xe6\x79\x48\x3c\x32\x0b\x63\x8f\xae\x78\x55\xd9\x6c\xb0\x67\xc9\x6b\xff\xe0\x2e\xdb\xb5\xe7\x61\xb9\xc4\x5f\xbc\x7c\xb4\xff\xfa\xe8\xed\xff\xbe\x7c\xaf\xea\x09\xe9\x78\xb6\x4c\x3e\xd3\x50\xbc\x2a\xe1\x2f\x46\xc5\x5f\x3f\xa3\x8b\x38\x18\xd3\xce\xe0\xdf\xd7\xc7\xff\x4e\xfe\x9d\x9d\x3c\xfb\xf7\x97\xc1\xb4\xd7\xbe\xbe\x7a\xf4\xe8\xea\x4b\xbb\x0b\x3e\x93\xbf\x78\xe1\xff\x7d\x22\x4b\x1c\x8b\x32\x27\xac\xd0\xb1\x2c\x75\x72\xec\x2f\x67\x97\x32\x0a\x95\x94\xd1\x6d\xa1\x96\x54\x43\xa8\x8c\xb8\xe6\x63\xd9\x6d\xc9\x49\x0d\x0c\xb8\x6b\x16\x10\x8f\xf8\xcb\x60\x00\x77\xa0\x54\xb8\xc3\x00\x4f\x1b\x50\xc1\x9a\x43\xfa\x2c\x6f\x9f\x65\x99\x2b\x57\xf8\x9d\xb1\x60\xc8\x06\xe1\xef\x5f\x0d\x51\x5d\xdd\x59\x5b\x9c\xcc\x75\x6a\xe0\xb3\x05\x83\xbe\xa3\x52\xc2\x9a\x86\x1b\xd3\xac\xb9\x8b\x4f\x77\x66\xd7\xee\x8c\x18\x3a\xf8\xfa\x55\x16\xd4\xe0\xfa\x2e\x19\xd3\x18\x22\x05\xc8\x47\x9c\x46\x99\x71\x4c\x83\x4c\x9a\x70\x59\xad\x88\x64\x6b\x41\xfb\x81\xc0\x57\x43\x21\x2b\xf2\xed\x71\x66\x79\x7b\xaf\xc3\x7f\x95\x76\x95\x02\x67\x18\xfe\xba\x47\x36\x87\xc3\x21\x79\xc8\x2f\x67\x3c\x77\xad\x5e\xc7\x0f\xf0\x6e\x0f\xb0\x23\xf1\xc5\x38\x48\x4e\x05\xbd\xf0\x58\x3f\xe2\x5d\xdf\xea\xa8\x72\x67\xcc\x22\x11\x88\x28\x25\x2c\x2b\x9d\x0e\x73\x16\xd1\x5f\x2c\xf3\x99\x69\x31\x68\xbb\x11\xc7\xe0\xc2\xf9\x0f\xe3\x91\x3f\x8a\x2d\x34\x08\xc3\x1c\x87\xce\x17\x56\x0e\xae\x34\xc6\xd5\xc3\xbd\x35\xbe\xe1\xca\x83\x81\x38\x6b\x47\xdc\x0f\xbf\xe0\x7a\xb0\x1b\xcb\x5b\x21\x95\x7a\x10\xf2\x52\x41\x96\x45\x67\x14\x33\xdc\x20\x54\xb3\x27\xdb\xab\xe0\xb0\x1e\x68\xc3\x0d\xbf\xdf\xa6\x14\xc9\x14\xf2\xb5\x7a\x04\x31\x76\xc5\xd7\xf1\xf0\x44\x6d\x99\x70\x85\xcd\xfb\xa6\xa1\x45\x82\x59\x82\x27\x62\x89\xce\xbb\x79\x91\x5d\xd5\x9b\x2a\x89\x97\x81\xf6\x55\xc3\xb2\x6e\xb9\xab\xc9\x75\x84\x57\x2a\x39\x9f\x51\xe9\x77\x20\xe4\x62\x39\x9c\xbe\x40\xe3\xce\xf6\xf7\x10\xa1\x59\x10\x71\x05\x6a\x5d\xfb\x4e\x75\xb4\x9f\xa4\x59\x87\xe1\xe5\x33\xbd\xe4\x27\x45\xdf\x00\x4c\x27\x30\x1d\x3f\x50\x7f\x16\xe4\x87\xe7\xc9\x3b\x88\xe4\x55\x5c\x42\x88\x4c\x8b\x0b\x94\xa0\xe7\x33\xbd\x3c\x29\xb7\xed\x6c\xa7\x09\x39\x78\xb7\xdf\xee\x5a\x8b\x5f\xc8\x16\x15\x75\x3a\x66\x16\x7a\x99\xec\x63\x1f\x84\xc2\xcd\x39\x41\xc7\x8d\x28\x27\x79\x11\xf1\x28\x2b\x51\x88\x88\x1a\x9b\x85\x96\x22\xdc\x6f\xc7\xd9\x29\x3f\x2d\x49\x39\x80\xed\x1e\x19\x15\xfd\xe8\x71\x2a\x30\x7b\x35\x4d\x13\x2a\x34\x4f\x9d\xf5\x4f\xb6\xd8\x7f\x9e\x45\x05\xf8\x4b\xb1\xb8\x11\x02\xb1\x8e\x50\x9f\xdc\x33\x94\x74\x31\xb8\x5e\x56\xbb\x50\x20\x79\x87\x5e\xf5\x82\x60\x0d\xd3\x8f\x55\x2f\xfd\x80\x9e\xae\x10\x63\x93\xdd\x31\x38\xf7\x0a\x28\x92\x68\xaa\xc7\x12\xf1\x1c\xa1\x6a\xcf\x9a\xb2\x97\x21\x7a\xf6\xeb\x1b\x55\x85\xc5\xf3\xcd\xc4\x06\x45\xd5\x58\x6a\x30\x87\x52\xbb\x8f\x12\xeb\xcf\xb7\x4f\x5a\x66\x77\x42\x9b\x68\x9d\x51\x1c\x77\x3c\xff\x4a\x97\x60\x65\xad\x5f\x9b\xb5\xda\x1b\x36\xbb\xdd\x68\xb7\x48\x8e\x0d\xb3\xfb\xd8\x4e\x5b\xf3\x41\x78\xb1\x95\x16\x24\x5f\x2e\x16\x69\x56\x80\x6e\x8d\xdf\xd4\xbe\xdb\x27\x4a\xab\xd2\x36\x1c\x41\x96\x13\x66\xe3\x97\x0a\x37\x59\x8c\xf5\x54\xb6\x12\x85\x79\x8f\xf5\x40\x53\x95\x16\xf4\xc8\xa1\xae\xbd\x9b\x96\x7a\xbb\x71\xf5\xb8\x1a\x83\x8e\x93\xf6\x92\x57\xda\xd7\x27\xbd\xed\x6f\xee\x55\xba\xf7\x2a\xdd\xff\x0a\x95\xae\x78\x58\x71\xab\xe7\xd8\x7b\x41\x96\x26\xe4\x7f\x97\xf3\xe0\x2c\xca\xc9\xf7\x01\xfb\xfc\xdb\x67\xfe\xd9\x9f\x53\xaf\xba\x77\x30\x20\x07\x49\x54\x44\x41\x1c\xfd\x4a\xc9\xdf\x79\x2f\x18\xa1\x06\x24\x07\x4b\x2c\x69\x70\x03\x03\x65\x4b\xd5\x70\x72\xde\x07\xad\xae\x2c\x26\xa3\x97\x88\xc8\x5a\x07\xe1\x88\x0c\xeb\x6e\xde\xb8\xb5\x07\x1b\xbe\xed\x56\xd7\x6b\x66\xe2\x75\xa7\xab\x5f\xa1\xc9\x20\x5e\x13\x89\x50\x68\x49\x1b\xf4\x78\x9c\xf0\xf2\xd7\x29\x3d\xa4\xea\x99\xc8\x6a\x64\x96\xf4\xbd\xeb\x75\x43\x84\x46\xc0\xda\x73\x7a\x3f\x58\x13\xe8\x29\x71\xc5\xcb\xdb\xea\x89\xc6\x0c\xa7\xa9\x3c\xab\x5b\xa6\x5a\x96\x4d\x3a\xc6\x3c\xca\x6c\x77\xbd\x8d\xc2\x69\x05\xe1\x19\x3b\xa3\xca\xd9\x21\x07\x2f\x20\x47\xf6\x4e\x4d\xda\xc6\x46\x99\x9f\x21\xff\xeb\x1f\xfe\x56\xc8\xa9\x46\x67\xcb\xe7\x41\x62\xa4\x2a\x5d\xbe\x0b\xe2\xff\xb3\x03\x93\x7c\x21\xd4\xdc\xf0\x42\xe2\x40\x1d\x1e\xa5\x01\x91\xdf\x54\x47\x29\xeb\xea\x42\xba\x79\x5e\x66\x5b\x0d\xf8\xcd\x33\x24\x1a\xac\xf6\xac\x98\xdb\x3c\xd1\xba\x0c\xe5\x3e\x7d\x90\xce\x59\x00\x3d\x53\x6d\xf7\xe9\x19\xcd\x2e\x3b\xd2\x1b\xf2\x87\x28\x99\xc6\xf4\x0d\x47\x78\x97\x8c\x88\x37\x43\xd7\x24\xa6\x55\x75\xc4\x0f\x2e\x26\x50\x1d\xb4\x94\xf0\x2e\xe9\x06\x59\x10\xc9\x34\x4e\x91\x86\x6d\x91\xc8\x90\xf3\xb3\xbb\xbb\xcb\xa9\x06\x03\x09\xb7\x0b\x12\x96\x9d\xb9\x19\x18\xbf\xd6\x6d\xfb\xaa\x13\x32\xac\xe5\x53\x72\x30\xe0\x31\x07\x55\x92\xf0\xca\x8e\x99\x8b\x5c\x8f\x8d\xfc\xc9\x73\x46\x74\x0a\xef\xd1\x6a\xd8\xd1\x73\x06\x54\xee\xe2\x5b\x74\xdc\xe2\x2f\xbc\xae\x9c\x33\x55\x51\x95\x14\x70\xc2\x2e\x28\x8f\xc4\xa2\xe8\x48\xde\xd3\x25\x93\x88\xc6\xa1\x65\x7a\x20\x5a\x31\x7a\x6a\xf1\x1c\xdc\x41\x8b\xf1\xf0\xae\x59\x64\x28\x93\xad\xa8\x0f\x92\x2c\x5c\x47\x58\x0e\x7b\x93\xb0\x7d\xc9\xda\xe4\xb7\x60\x71\xa6\x1e\xde\x91\x15\x45\x7d\x42\x4e\x64\x62\xe0\x93\x7b\x31\xf0\x5e\x0c\xfc\x6b\x8b\x81\xfa\x7d\x1e\x5f\x34\x77\xf5\x42\xef\x6e\xee\xee\x19\xc8\x1b\xa9\x6e\x2c\x35\x56\x86\x73\xa2\x88\xd4\x22\xad\x90\xd9\x27\x3a\x45\x0a\x97\x6b\x32\x97\x7d\x1a\x17\xf7\xc0\xf3\x74\xbe\x96\x0c\x86\x08\x0c\x7c\xf2\xe3\x60\x88\xda\x10\x1a\x67\xa0\x12\xdc\xd3\xb3\xaf\x88\x95\x63\x28\x5d\x41\x63\xf0\x26\x48\x82\x29\xd5\xaf\xf3\x19\xcb\xe2\xa8\x30\x54\x01\xd2\x85\x87\x06\x47\xfb\xfd\xdc\xc0\x90\x53\x71\x36\xaf\xb1\x7f\x0f\x29\xe3\x30\x51\x62\xfa\xf7\xb4\xc4\xbf\xd3\x20\xe7\x3e\x17\xca\x22\x51\x4c\x29\x78\xa9\xf4\x6c\x52\xa6\xa7\x79\xdb\xb1\xa8\x6c\xd3\x6c\x0f\x48\xcc\x41\x84\x68\xa3\x34\xd6\x84\xe1\x4e\x14\x85\xcf\x51\xc4\xa1\xec\xf8\xa4\x2f\xc3\x9c\x09\x36\x2a\xa5\xce\xcd\x31\x77\xc6\xa9\x2f\x29\x44\x68\x0e\xb1\xed\xaa\x71\xf6\xc9\x1b\xc6\xca\x23\x9a\x8b\xe8\xd8\x80\x0f\xc7\x0b\xa5\xe1\xd9\xb3\x31\xde\xe4\xa0\xae\xde\x2e\xe3\x58\x3b\xc6\xe8\x31\x29\x92\x5e\x44\x70\x6d\xe6\xc3\xdd\x1f\x33\xfe\xd0\x9d\x85\xdd\x21\x6b\x5f\x2b\xee\x8e\x83\xc9\x46\xd1\x76\xec\x00\x27\x2a\x94\x8c\x79\x10\x23\x35\xe1\x63\xde\xbf\xdb\x17\x11\x26\xaa\x63\xc7\x68\xb4\x09\x57\xaf\x9c\xf0\x00\xe9\xea\xc4\x69\xa3\x89\x83\x1e\x30\x48\x17\x4b\x06\xd1\xa9\x24\x0f\x3a\x50\x2d\x95\xd8\x58\xf7\x70\xd7\x12\x0a\xf2\x3d\x6e\xf4\x94\xb6\x64\x48\xe5\x74\xb1\x47\x20\xfa\x77\x55\x08\x29\xf2\x4c\xff\xe6\xd4\x0d\x45\x4e\x18\x3b\x40\x9f\x35\x9e\xf5\x1d\xac\x73\x7e\xaf\xa2\xe6\x62\xcc\xbb\x88\xe7\x0e\x78\xab\xcf\x8a\xa6\x3b\xe2\x12\xdc\x7b\x62\xa4\x98\x41\x7a\x31\x0a\xed\xcd\x0a\x9c\xcd\xc0\xb1\xe7\x99\x17\x40\x55\xe5\x8d\x4d\x22\x70\xe1\x0b\x59\x24\xdf\x4f\x49\x3a\x5c\x21\x72\x51\x20\xd7\x6d\x23\x24\x34\x8b\x41\x84\xdd\xb1\x8a\x7d\xc4\xf6\x92\xbc\xb2\xf3\x65\x21\x4f\x00\x30\x5a\x06\x18\x10\xf2\x8c\x00\x43\xea\x98\xe2\xd7\x82\x48\x75\x06\x68\x96\x4a\x94\x19\x55\x6e\x95\xb1\x8a\xc3\x41\x95\x74\x91\xcb\xf1\x69\x4a\x5b\xa7\xbf\x60\x74\xb1\x0c\x39\xb4\xd3\x65\x14\x87\x80\x30\x31\x28\x96\xe9\xf8\xb7\x05\x86\xff\xf1\xf0\xc5\xe1\xfa\xfa\x3a\x88\xf7\xed\x9c\x2c\xa7\xf1\x65\x5f\x44\x11\x63\x07\x82\x65\xce\xf6\xc4\x42\xb5\x92\x20\x97\xb2\xec\xb7\xb4\xab\x51\x37\x24\x8c\x71\x40\x86\x7a\x6f\xbd\x69\x44\x7a\x3a\xfd\xe5\x98\x65\x1f\x0f\x4f\x4e\x98\xd8\x85\x3f\xaf\xae\x94\xdd\xa6\x0d\xca\x7f\x6c\x42\x19\x36\x96\x1d\xff\x55\x91\x55\x3b\x40\x12\xc4\x85\x1d\xf4\x2a\x44\x95\xdd\xa2\xaa\x4b\x75\x6d\x74\xca\x43\xa0\x24\xfe\x67\x59\xc4\xf1\xf3\x2d\xe4\x77\x7d\x1a\x5e\xc5\x0f\x34\xb1\x22\x58\xf8\x42\x15\x18\x67\x75\x68\xcb\x94\x28\xf5\xc5\x94\xbe\x9f\x31\x62\xb1\x28\xf3\x3a\x8f\x69\x9e\xdd\x30\x87\x17\xed\x60\x66\xa6\x8c\x22\x2d\x03\x1a\x6f\x38\x15\xb3\xbb\x46\x35\xe5\x43\xb0\xaf\xa1\x04\xa9\xb0\xac\xa6\x9e\x9e\x65\x98\x2b\x9a\xd4\xbb\x73\x94\x1c\x72\x99\x51\xb8\x21\x7d\xff\x6e\x5f\x79\x60\xe2\xa6\x2c\xe3\x20\x51\xc2\x66\x94\x08\xa5\x8b\xdf\xd7\x53\xe6\xfa\x7a\xec\xf7\xfb\xd7\x38\xbe\x9b\xed\x4b\x4f\x6b\x32\x65\x51\x0f\x27\xad\xf3\x69\x5f\xea\x6e\x7e\x15\x22\x94\x34\x60\xfa\xa4\xc7\xb3\x56\x86\x68\x51\xb2\x44\xb1\xf3\x46\xda\xc0\x34\xbd\xfe\xfb\xf6\x5e\xef\x73\xaf\xf7\xf9\x6b\xeb\x7d\x84\xd2\x27\x3c\xbd\xc5\xcd\x9f\x4f\xef\xa3\xb4\x35\x58\xf1\xc3\x99\x93\xd2\xe8\xbc\x78\x6e\xf0\x11\x36\x0c\xd3\xe5\x87\xa3\xa9\x80\x91\x5a\xc9\x3b\x15\x81\xc2\xd6\xb4\xbc\x94\x77\x3c\x36\xfd\xe2\x82\x8b\x7c\x21\x96\x74\x65\xc9\x41\x1d\x56\x33\xda\x59\x04\x90\xa3\x76\xe9\xf8\x3a\x68\xe9\x9b\xf5\x2e\x5f\x1e\xb0\x68\xb1\x2c\xd4\xe3\xb5\x84\x9e\x0b\x6c\x76\xf4\x76\xc9\x84\x8e\x11\x69\x2b\x38\x2b\x8e\xc6\x88\xb4\xc3\xd3\x4f\xbe\x5c\x29\x26\x6e\xab\x3e\xa9\x46\xa7\xb4\x59\xa3\x0a\xce\xdb\xa8\x2f\x57\x36\xba\xe5\x36\xba\x58\x16\xaf\xe9\x45\xfd\x30\x5f\xd3\x8b\xb2\x31\x9a\x59\xd5\x03\xac\x6f\x8b\x03\x95\x0d\xcd\xdf\x96\x35\x2e\xb1\x19\x1d\x6b\x38\x39\x11\x3d\x8d\xe4\x9e\x18\x7a\x4f\x74\x0b\x80\x4f\x4a\x76\xae\x17\xcf\xf5\xae\xc5\x69\xa7\x35\xda\x86\x2d\xea\xe9\xfd\x16\x75\xbf\x45\xfd\xb5\xb7\x28\x7d\x35\x41\x8b\xd9\x8d\xee\x25\x04\xf0\xdd\xbe\x4a\x2c\x89\xfe\xef\x0b\xff\xef\xbb\x04\xf1\xdf\x83\xd4\x6c\x9b\x0c\x44\x9a\x23\x5b\x40\x0b\x91\x2c\xc1\xc6\x65\xed\x8d\xd3\x64\x12\x4d\x25\x18\x0a\x85\x83\xa1\x65\x64\x15\x09\x76\x2e\x9e\xad\x19\x17\x34\x22\x51\xc2\xbc\xe2\xa1\xc0\x2d\x64\x40\xa2\x04\x39\xc8\x3f\x5c\x26\x63\xbe\xc5\x60\xa8\x9c\xa7\x4a\x30\xc6\x8a\x33\x6a\x03\x89\x54\x55\x17\x77\x50\x84\x21\xa2\xd3\x20\x91\xd9\xdc\xeb\xa1\xd3\x1f\x99\xac\x84\x10\xf0\x99\xd6\xe4\xce\x40\xe9\xbc\xc5\x1b\x41\x50\x02\x0e\x4f\xba\xe4\xc1\x03\x22\x7e\xf7\x41\x27\x78\x38\xe9\xb4\x87\x17\x6d\xee\xba\x64\xd8\x25\xcf\x48\x8b\x16\x33\xb6\x7b\x40\x60\xd2\xe7\x97\xaf\x83\x7c\xd6\x22\x23\x3b\x99\x6b\x74\x5b\x5a\x4a\x80\xae\x7d\x88\xa6\x09\xcd\xf2\x8a\x1e\xde\x71\xff\x44\x83\x25\xdd\x54\xb9\x4e\x6f\xf3\x22\xf8\x4c\xb3\xf7\x87\x07\xf5\x5d\xbd\x59\x4f\x51\x47\x3f\xc8\xb6\xde\x04\x79\x41\xb3\x24\x0d\x29\xee\xa9\xca\xb6\x91\xf9\x2a\x4a\x82\x38\x2a\x2e\x7f\x3b\x6c\xca\x16\x4b\xd0\xa9\xb3\x1d\x7c\xa2\xe8\x5f\xaf\xb2\x74\xfe\xfc\x37\xa0\xd3\xb6\xe8\x1a\x0a\x2a\xf5\xfc\x12\x1a\x66\x9d\xdf\x4b\xc2\x03\x56\x4e\xc5\x72\xf3\x42\xf2\x71\x28\x58\x3d\x9e\x65\x32\x8e\xe9\x6f\x34\x80\x23\xd6\x56\x4d\xd7\x31\x4c\x69\xa7\xe5\x3c\xa1\x71\xee\xa7\xcb\xa4\xd1\x25\xe3\x1d\x8c\xc3\xdb\x36\x27\x25\x3c\x94\x12\x30\x3e\x2a\x67\x0a\x7e\xc3\xfe\x1f\xa9\x06\xd1\x64\x38\x93\x80\x01\x8c\x3e\xab\xee\xbd\x2c\x66\x77\x7d\x3c\x6c\x7c\x34\xbc\xa3\x93\x21\x84\x7f\x2e\x3f\x19\x72\xc5\x17\xdf\xc3\x23\xea\xed\xd1\x02\x77\x66\x51\xd3\x8f\xc5\x0d\xba\x80\x2c\x1c\xf8\xde\xca\xbd\x9f\x10\xec\x9f\xfd\xe0\xf9\xde\x5b\x2b\x14\x9d\xd8\x51\xb9\x4e\x8e\x3f\x9f\x16\x9a\xb9\xeb\xb5\x35\xde\xbb\x3e\xb7\x8b\x53\x2f\xa9\x5e\x16\x33\xad\x0b\xec\x91\x36\x0e\xdc\xdd\xee\x89\x61\x4e\x69\x31\x2a\xd1\x78\x4b\x4f\xb5\x7d\x5c\x50\x8c\xa4\x27\xb4\xb4\x46\xe1\xb3\x20\x36\x62\xcc\xf5\xad\xb0\xe9\x67\x41\xec\xb8\xa2\x51\x69\xd7\x6b\x80\x9e\x95\x86\x22\xbc\x3c\xde\x64\x30\xa2\xe8\x4d\x86\x23\x8a\x36\x1c\x50\x13\x4d\x04\xe3\x2e\x41\x0c\x76\xbb\xb5\xe7\x66\x01\xe8\x9e\x9d\x25\x9b\x72\xf2\xd5\x01\x1a\xd9\xf2\x1a\x17\xb8\x23\x72\xac\xc5\x69\x7e\xb9\x2b\x9c\xa8\xbe\xd2\x77\xb9\x36\x04\x8e\x7b\xcf\xf9\x89\x02\x46\x81\x43\xad\x5b\xcc\x11\xae\x86\xe7\x29\x8f\x45\x0a\xa8\x44\x69\x92\x66\xc1\x94\xee\x15\x4d\xf4\x26\x02\xb4\x14\x47\x3e\x08\xa5\xd2\xa8\xc0\x12\x5f\x77\x9c\x63\x17\x29\xe8\x15\x56\x41\x8b\x77\x60\xc2\xb5\x67\xcd\x98\x18\x54\xe9\x70\xac\xcc\xdf\x7e\xbe\xbd\x03\x13\xcb\xe4\x20\x99\xa4\xf5\xe3\x43\xc0\xa5\xc3\xf4\xc3\xfc\x41\x46\x2b\x79\x5c\xdd\xea\xe5\xcc\xd7\x1a\xa1\x3a\x1e\xdd\x6e\x58\xbe\xde\xf6\x1c\x86\xa6\x6d\xbd\x19\xab\x22\xd7\xab\xad\x56\x90\xa7\x2b\x57\x2a\x3e\xc1\xf8\x11\x62\xa1\x43\xc0\x2a\xac\x20\x9c\xa0\x73\x99\x1d\x67\x64\x53\x26\xdc\x08\x2d\x6a\xd0\x0d\x87\x2c\x3a\x52\xc7\xa3\xc4\x89\xa8\x09\x8f\x12\xa0\x0e\x2d\x18\x27\x3c\x97\x1e\x36\xab\xe8\x41\x0a\x31\xd6\xce\x45\x8c\xdd\x09\xdb\xdc\x94\xac\x07\x5e\xc1\xc8\x74\x67\xd0\x94\x50\xf0\x15\xe2\x7b\x1a\xc4\xe5\x44\x22\xcf\x65\x8d\xa8\x44\x02\xfb\xc8\x04\x9f\x38\x7f\x07\x3a\xc1\x23\x3e\x48\x0a\xef\x80\x41\x06\xaf\xa7\x0b\x00\x73\x68\x42\x9d\xea\xbe\x06\x7f\x40\xdb\xd9\x2d\x58\x41\x6f\xad\x64\x77\x9b\x2f\xa2\xb8\x94\x13\x98\x5b\x9c\x00\xad\xd8\xe7\x5c\x08\x89\x87\x61\x39\x99\xd9\x67\xb6\x86\x5c\xda\x2e\xe6\x74\xab\xea\xd8\xba\xe2\xc2\x5d\x85\x12\x3d\x73\x23\xa7\xf0\x05\x1d\x47\xf3\xaa\x15\xa7\x4f\x82\x0d\x91\xa0\x0b\x94\x10\xe5\x1f\x77\xc0\xe6\x01\xaa\x66\xb0\xe5\xd1\xf2\x4b\xd4\x30\x70\xc6\xae\x1c\x74\xfd\x0a\x42\x15\x56\x6f\x2c\x1f\x3d\x5a\xaa\x95\xc6\xa4\x4a\x39\x83\x2b\x53\x80\xfd\x91\x38\xcd\x4d\xf0\xf4\x9e\x8e\x69\xb4\x68\x40\xe6\x6e\x99\x26\x04\xe0\x82\xde\x96\x02\x44\x8d\x8d\x07\xd8\x70\x15\xd7\x72\x31\xcf\xe0\x6c\xc0\x26\x14\xc0\x8f\x46\x77\x74\x48\x2c\x5b\xde\x44\x5a\x1b\x48\x6b\xbd\xf7\xc1\x79\xf3\x65\xee\x16\xf0\x23\xa3\x12\xae\x09\x77\x63\xb8\xf0\x9c\x12\x58\xbd\xab\xf5\xb6\x51\x57\x6f\xde\x4f\x7b\xb6\x7c\xeb\xcc\x37\x8e\x68\x9a\xac\x30\x0e\x13\xba\x64\x1c\xa5\x40\x5f\x79\x1c\x0d\x3a\x5f\xde\xe3\x3b\x3f\x85\x96\x10\x8e\x30\xf1\xad\xea\x28\x03\xf1\x77\xd4\xca\xb9\x49\x47\xd9\x7e\x70\x67\x67\x65\x9a\x17\xd1\x3c\x28\xe8\x8f\x41\x9d\x4c\x88\x20\xfd\x43\xf3\x03\xdc\x84\x62\x8c\x11\xde\x4a\xf0\x18\x73\x19\xf5\x43\x1a\x47\x61\xe9\xd1\x46\x4f\x1b\x87\xee\xe7\x02\xbc\x64\x0a\xcd\x3a\x7d\x63\x2d\xed\xc8\x4f\x3f\xfd\xd4\xb0\x0f\x71\x29\x05\xa9\x9a\x56\x6a\xf9\x03\xcd\x16\xb4\x76\x8b\x52\x18\xe0\xd0\xd5\x08\x70\x60\x2a\x7a\x91\x2f\x4f\xe7\x51\xf1\x73\x9a\xd5\x49\x4a\x1a\xb0\x64\xa5\xfb\xf2\xab\x0d\xa0\x1a\xb4\x2a\xa0\x4a\xb7\xe3\x92\xf6\xfc\xc7\x9c\xfd\x20\x09\xf9\x23\xff\x22\x28\x96\xb5\x5a\x97\xff\x8f\xbd\x77\x5d\x6f\xe3\x56\x12\x45\x7f\xdb\x4f\x81\x78\x9f\x15\x91\x31\x4d\xf1\x2e\x99\xb6\x32\x23\x53\x92\xa5\xb1\x65\x69\x4b\x72\x92\xb5\xf5\x29\xfe\x9a\x24\x28\xb6\x4d\x76\x73\xba\x9b\xba\x24\xd6\x7e\x9f\xf3\x1c\xe7\xc5\xce\x87\xc2\xfd\xd6\x6c\xea\xe2\x38\x19\x69\xcd\xc4\xec\x6e\xa0\x50\x00\x0a\x85\x42\xa1\x2e\x46\x71\xe3\x44\x2d\x4e\x5b\x9e\x52\x16\x07\xb9\x07\x75\xdb\xf6\x2c\x1e\x8c\xbd\xbc\xc3\xd5\xd3\x22\x07\x4a\x51\xd6\x7f\xa2\x74\x15\xb9\x0d\x03\xf1\x77\xc0\x4a\x85\x2b\x2d\xd6\xa4\xb6\xbe\xa2\xbe\x13\xda\x69\xed\x6d\x2f\x1e\xea\xc5\x14\x75\xa8\xf6\x1e\xf8\xb0\xfd\x86\x69\xb0\x8c\x96\x98\xae\xc9\x2e\xce\x55\x2a\x3a\x0e\x62\xb8\xdc\xaf\x29\xa5\x68\xdf\xe0\x00\x69\x74\x84\x9d\xe2\xed\x46\x4d\x19\xd4\x2e\x21\xcf\xa3\xda\x37\xa5\xa2\xef\xbd\x38\xdd\xf8\x0a\x30\x01\xdc\xf7\xd9\x68\x54\xf7\x0b\x52\x76\x22\xf9\xd2\x96\x23\x95\x6f\xba\xc0\xa3\x57\xf2\xd6\x50\x9a\xd7\xb7\x04\xeb\xc3\xfb\xf7\xef\xed\xc2\x94\x7d\x2a\x20\x05\x67\xd3\x3a\x4d\x5e\xc0\x33\x33\x94\xa4\xa9\x5d\xc5\xad\x69\x5e\xaa\x07\x49\xdb\x64\x6d\x8a\xeb\x3b\x5d\x15\x29\x38\x7f\x18\xf5\x83\x54\xd5\x76\x31\x04\x60\x89\x31\xc6\xcf\xca\x88\x22\x37\xe5\xca\x12\x6d\x4c\xc3\x48\x37\x92\xb5\x5a\x60\x25\x6e\x09\x7f\x1c\xa4\xe3\x24\xc8\x72\xfb\xe0\x29\x53\x48\xb4\x58\x1e\x23\x6e\xe4\x95\x83\x90\xbb\xc8\xe2\xc3\x2a\xb3\x2a\xd3\x4f\xa8\xcb\x63\x78\x1e\xa4\x87\x49\x38\xc8\x1d\x33\x4f\x99\x5b\xdf\x26\x2e\x8f\x25\xcb\x5e\x98\xe6\x61\x29\xca\xdc\xb2\x8d\xbe\x62\x8b\x91\xd3\x8c\xbf\xd8\x03\xd1\x10\x4f\xed\xf4\x0b\x35\xd9\xcd\xc3\xcd\x2c\xaa\xb4\xa8\xb2\x10\xed\xfe\xbe\x3a\x90\xe6\x90\x8a\x6d\x4c\x3f\xd4\x1c\x1f\x83\x41\x16\x27\x5c\x7e\xe6\x06\x94\xe0\x8d\x54\x41\xa4\xac\xb6\xa7\xb2\xd2\xae\xc6\x46\xdc\x60\xd2\x8a\x68\x51\x51\xbc\xf6\x69\xa9\x5e\x82\xc1\xe0\x19\x7c\xd0\x7b\x86\x57\x9e\x92\xee\x90\x1a\x61\x4a\x38\x64\x28\x56\x2a\x4e\x83\x99\x0a\xb7\xea\xac\xe2\x6c\x5c\x2a\x57\x6c\x92\x7d\x1f\x9f\x2b\x92\x51\x31\x94\x5c\x1d\x95\xf6\x9c\xf9\x99\x78\xf8\xe8\x97\x58\x85\xea\xf9\x24\xee\x07\x93\x2a\x19\xd4\x6a\x60\xbf\x66\xa9\x53\x5d\x4d\x86\x83\x60\xf6\xe1\xb6\xcd\x92\xca\x56\xa3\xf4\x65\x5e\x93\x8a\x71\xab\x6c\xd0\xf4\xa0\x54\x53\x53\xf2\x0a\x25\xf7\xf4\x2c\x0a\x6a\xb9\x9d\x8d\xa5\x5b\x80\x61\xdf\xfb\xac\x5b\x5f\xaf\x3c\xb3\xec\x8c\x99\x9f\x9b\x34\xf0\x7d\xd6\x6d\xb4\xe1\x05\x9d\xd3\x67\xdd\xc6\x4b\xfa\x28\x68\xe1\x59\xb7\x49\xab\x84\xfd\x20\x7a\xd6\x6d\x36\x2b\xba\x17\x02\x3c\xb2\x41\x7a\xd6\x6d\xb5\xe0\x99\x5b\x23\x3f\xeb\xb6\x28\x78\xc6\xd9\x9f\x75\x5b\x14\x2d\x6e\x35\xf4\xac\xdb\x22\x0d\x72\x5b\xe2\x67\xdd\x56\xf3\xe6\xac\xd2\x7c\xf9\xe8\xd6\xf0\xe8\xd6\xf0\xcf\x76\x6b\xf0\xf9\x34\xdc\xd9\xf5\xae\xb8\xb7\x41\x01\x57\x02\x28\xf7\x01\x67\x0f\xe9\xa9\x07\x6f\x17\xdb\x3e\x4a\x1f\xbd\xdb\x18\x3f\x16\xf0\xcc\x5b\x5d\x5d\x95\xa1\xed\x5c\xe1\xf2\x58\xde\x67\xc2\xe2\x01\x1c\xce\xc6\x28\x98\x85\x0a\xee\x0f\x74\x20\x99\x84\x69\x86\xf3\xce\x0b\x11\xce\x3e\xc9\x42\xb7\x15\xae\x30\x4e\xcc\x0b\x16\xab\x15\x5f\xa1\x25\x04\x3e\x55\xfc\xb2\x36\xb5\x0f\x38\x73\x6c\x6a\xfa\xe6\xa5\xee\x2e\x37\x67\x95\x56\xed\x71\xb7\x78\xdc\x2d\xfe\xd9\xbb\xc5\x77\xea\x04\x77\x7f\xfe\x6a\x05\xdd\xe9\xa4\x4f\xc0\x21\x4e\xd2\x38\x0a\x26\x8f\x8e\x01\x0f\xed\x18\x70\x53\xcc\x54\x3c\xc2\x97\xd2\xfe\x3c\x4f\x01\x2e\x0b\xda\xda\xef\x19\x9b\xd5\x4f\xce\x42\x77\xb8\xe2\x0e\xa7\x64\x23\x38\x0a\x2e\xdf\xe1\x45\x57\x5f\x6a\xd1\x95\xca\xd3\x27\x4f\x4c\xdc\xac\x02\x39\x0e\xee\xc5\xaf\x72\xed\x76\xc4\x07\xc5\x02\xfc\xc9\x93\x82\x06\x0e\x85\xef\x70\xf1\xe0\x08\x0f\xe2\x0b\x1a\x63\x32\xef\xd2\x93\x97\x73\xe2\xaa\x7f\xcd\x19\x90\x79\x34\x89\x07\x5f\x8a\x51\x8a\x56\x36\x87\x58\x7c\xe5\x8a\x58\xce\x17\x1b\x37\xef\xe8\xdd\xb3\xe9\x84\x9c\xfb\x85\xf6\x13\xcb\xdc\x93\xbb\xec\x0e\xbc\x5d\x2a\x3e\x3f\xc5\x66\x27\x7f\x6e\x96\xb9\xcb\x32\xe7\xc6\x40\xde\x25\x59\xb3\x86\x95\x46\x94\xc5\x2b\xdf\x6a\x14\xa4\xdc\x9e\x70\xaa\xf6\xdd\x76\x78\x2f\x45\x14\x70\xaa\xbc\xfb\x70\xe7\x83\xcd\x05\x6a\x61\x39\x1d\x6a\x61\x8f\x58\x6e\xcb\xe5\x7c\xbb\x95\xc2\xb9\x43\x45\x64\x68\x85\x4c\x39\xbd\xfe\x28\xa7\x3f\xca\xe9\xff\x6c\x39\x9d\x09\xe9\xe9\xd8\xa3\xd5\x59\x20\x7e\xe3\x04\xcf\xa7\x04\xf4\xcf\x0b\x94\x40\x83\x38\xc1\xd5\x30\xd6\xe5\xf4\xb5\xc2\xf1\x97\x0a\xc6\x6b\x58\x14\xf6\x01\x0a\x1d\x8f\xc7\x0f\xae\x1d\xfa\x7e\xe4\x71\xc2\x1d\x8f\xc7\xda\xed\x06\xbe\x64\xb9\x2b\x76\xbe\xc5\x85\x4e\x3a\x5e\x7c\xa1\x93\x8e\xe1\x42\x87\x0a\x2e\xcb\xdc\xdb\xe4\xc9\xf9\xfe\xcd\xc9\x12\x0f\x94\xad\xe9\xc2\x79\x53\xc7\x44\x84\x74\x3c\xfe\xe4\x2e\xa0\x5b\x15\x21\x87\x2e\x2b\xaf\xd1\x50\x77\xc4\x33\x5a\x74\x7c\xbd\x5b\x73\x29\xce\xf6\x83\x2b\x46\x04\xc7\xe1\x1f\xe6\xe5\xb0\xd2\xf6\xa2\xa2\xba\xd9\xd8\x6d\x10\x09\xa3\xc3\xf8\xd7\x7c\x04\x5c\x45\xee\xd6\xf0\x34\x48\xbe\x9c\x24\xf3\x34\xc3\xc3\x43\x6c\x5d\x06\x2b\xcd\xe7\x17\xbc\x1b\x12\x11\x26\x32\xdd\x61\x10\xe6\xb4\xef\x2d\x73\x37\x0a\x08\x86\xc3\xc3\x24\xbc\x08\x32\x4c\x8f\x84\x9e\xd6\xf3\x8a\xdd\xad\xef\x34\x77\xe8\xc2\xee\xe7\x15\xbb\x1b\x02\xe3\x20\x5d\xd8\xba\xb7\xcc\xdd\x9a\x3e\xc7\x19\xdd\xd0\x73\xc7\x3e\xa7\xd4\xdd\x9b\x2f\x30\xf7\x79\xc5\xee\x4c\xf7\xc7\xd7\xd3\xdc\xc6\x7d\x45\xee\x4c\xf5\x8b\x1a\xf6\x15\xb9\xeb\x90\x13\x39\x2e\xc3\x14\xf4\x4e\x12\x4f\x0f\x83\x34\xbd\x8c\x93\x61\xde\xf8\x17\xac\x73\xe7\x75\xb0\x68\x4c\x7c\x45\xee\x4c\x86\x8b\x1a\xf6\x15\xb9\x0f\xd6\xb3\xa8\xed\x9c\x52\xee\xe6\xc5\xc3\xea\x2a\x4a\xe7\x7d\xb8\x79\xc3\x90\x9a\x6a\x1e\xc9\xe7\x69\x98\xa6\x61\x74\xfe\xb4\x30\xb6\xb3\x38\x35\xaf\xae\x14\x2c\x1d\x5f\x1d\x7a\x0a\x94\xaf\x77\x44\x8b\x6f\xb9\x8e\xc7\x63\x25\x0f\xa9\x61\x7b\xa1\x9d\xa2\x0d\xcb\x88\x56\xe3\xf1\x0c\xfd\x78\x86\xfe\x67\x9f\xa1\xe5\x5d\x57\xff\x8f\x3f\x8c\xbb\xae\xcd\x09\xbe\x42\x6f\x70\x82\xcf\xd3\x3f\x82\xf4\x8f\x10\xbd\x0e\x26\xf8\xea\x3f\x93\x6c\x94\x56\xc7\x73\xfd\x38\xdc\x61\x41\xd1\x8f\xf0\x08\x27\x38\x1a\xe0\x2e\x22\xed\xa7\xdd\xd5\xd5\xf3\x30\x1b\xcf\xfb\xd5\x41\x3c\x5d\xfd\x2d\x8c\x76\xc2\xe8\x20\x39\x5f\xfd\x6d\xeb\x30\x3e\xee\x8d\x83\x30\x5a\xed\x4f\xe2\xfe\x6a\x7a\x19\x24\xd3\xd5\x30\xca\x70\x12\x05\x93\x55\xd2\x25\x7c\x95\xf1\x7f\xab\xe7\xf1\xff\x7a\xdf\x6c\x3e\xf0\xd5\x98\xbc\xef\x3a\x26\xd8\xfc\xc3\x0f\xd7\xf0\xe3\x6f\x71\xd9\x45\x2d\x5f\x71\x76\x19\x27\x5f\x8e\x30\x44\xbc\xcf\x53\x94\x9b\xc5\x6d\x6d\x79\xff\x8f\x3f\x3e\xe5\x94\xba\x8b\x73\xe7\x75\x34\xd8\x8e\x82\xfe\x04\x2f\xc2\x52\x29\xe9\x46\xd0\x5d\xe0\x2e\xb8\x5d\x06\xb3\x82\xb8\xc9\x92\x1e\xdc\x9c\x05\xee\x80\xdb\x30\xbe\x8c\x58\x32\x83\x3c\xc4\x78\x31\x37\x56\x8e\xaf\xc5\x7d\x96\x3d\x88\xcd\x67\x05\xd0\xa2\x85\xdc\x48\x59\xdf\xee\x8c\x52\x82\xb3\x24\xc4\x17\x8b\xc2\x88\xf0\x62\x6e\xb4\x1c\x5f\xef\x42\x5a\x19\xd9\xed\x16\x10\x15\x29\xe3\x21\x27\xe3\xd3\x9d\x87\xe8\x1c\x17\xf0\x89\x77\xe3\xa2\x7f\xb8\xc3\x98\xd0\x24\x50\x0b\x42\xad\xbb\x71\xd0\x3f\xdc\x79\x34\x58\xde\xb7\x7c\x64\x68\x21\x37\x3e\xd6\x37\x8e\x52\xab\x10\x4a\x39\xb7\xba\x96\x8a\xd3\x64\xcb\xca\xed\x9f\xe4\x87\xca\x4b\xc9\x88\xe4\x4b\xce\x07\x94\x1b\xc7\x99\xfe\xcc\xa9\x5f\x01\x44\x48\x50\x3e\x9e\x63\xe5\x62\x72\x36\x57\x1e\x14\x59\xfc\x41\xaf\x19\xc7\xe1\x85\xd7\x37\x86\xcc\x09\x7c\xf7\x9e\x21\xf3\x61\x3b\x94\xb2\x1a\x6c\xf8\xee\x39\x5e\x39\xce\x57\x44\x58\x72\xc5\xcc\x77\xde\x4b\x36\x1f\xcf\x54\x8f\x67\xaa\x7f\xf6\x99\x8a\x1d\xa8\xf8\x05\xd1\xb7\x4d\xf6\x72\x1b\xc3\x6a\xee\x1d\x15\xcc\x42\x2e\x8c\xd3\x4c\xc1\xd9\x38\xcf\x02\x8d\x5e\x97\xe5\x86\x37\xe6\xa5\xb3\xeb\x19\x91\x0f\x58\x28\xe3\x57\x4f\x15\x06\x1e\x66\x83\x71\x89\x7c\x37\x63\xd5\x0d\x82\x14\xa3\x15\x42\xf1\x69\xb6\xd2\xd5\x3e\xc1\x64\x25\xe7\x69\x35\x1d\x87\xa3\xac\x64\xe4\x25\x43\x56\x8e\xe1\x9a\x5d\x80\xb1\x64\x70\x5f\x8b\xf0\x25\xf3\x76\x86\x0b\xd9\x57\x0e\x34\x66\x38\x1a\x86\xd1\xf9\x83\xe3\x71\x48\xdb\x51\x6d\x88\x5c\x48\xb1\x18\xb4\x36\x36\x06\x38\xab\x32\xcd\xd3\x76\xa3\x48\x07\xa2\xd4\x62\x4b\x42\x06\xcd\x94\x11\x34\x52\x70\xc8\x4e\x0e\xa9\x3a\x0a\xa3\x34\x0b\x26\x93\x42\x2d\x1b\xa5\xdd\x6e\xfc\xfe\x42\x39\x78\x9c\xe3\xec\x7d\x7c\x5e\x20\x88\x00\x29\xe5\x0d\x1f\x40\x5b\x34\x8a\xe4\xb4\x3a\x8b\x17\x06\x72\x21\x45\x16\xb4\xd7\x1b\x07\xd1\xb9\x3b\x62\xc1\x02\x19\x4b\xcc\x97\x6a\x92\xa5\x8d\x9e\x26\x08\x91\x8e\x29\x8d\xc4\x2c\x18\xe4\xd9\x2d\x1d\x39\xd2\xf1\xb8\x0a\xac\xd1\x62\x37\xe9\xd8\x66\x37\x7e\xf1\x69\xc1\x2d\x8d\x45\x06\xc8\xba\xa5\xd1\x2c\x09\xee\x55\x4d\xef\x27\x46\xe4\xd2\xd4\x3f\x1c\x22\x36\xe9\x22\xeb\x9a\x82\x36\xcb\x70\x30\x8b\xde\xad\x79\x83\x8c\xef\xa1\x6d\x95\xf4\x2c\x49\x94\xe2\x80\xb3\x71\x97\xfc\x87\x02\x4b\xc7\xe3\x2e\xf9\x4f\x85\x4a\xaf\xae\xc4\x4e\xad\xd6\xa3\x4c\xfa\x28\x93\xfe\xc3\x65\x52\xa9\xe8\xe7\x4e\xd6\xb7\x71\x6c\x71\x08\xa4\xd4\x41\xfc\x08\x9f\x93\x79\x0e\x92\xcd\x7e\xe8\xc9\x6f\x94\xae\xbe\xd5\x8b\x56\x3f\xa7\xb1\xc8\x21\x14\x0e\x82\x99\x0a\xc4\x07\x63\xaf\xb7\x79\x68\x43\x50\x30\x61\x9e\xe8\xcc\x7c\x19\x6d\xa0\x95\xda\xd5\xa0\x33\x7c\x39\x6c\x0c\x86\xad\xd6\xcb\x60\xad\xdd\x1a\xb4\x5e\xb6\x1a\x9d\x16\xae\xaf\xd7\x5e\x0e\xda\x35\xdc\x6c\x0d\x3b\xad\x76\xa7\xd1\x5f\x91\xb8\xb8\xc0\x04\xf5\xa0\x5e\xaf\xf7\x07\xb5\xb5\xd6\xe0\xe5\x60\x14\xac\xad\xd7\x47\xb5\x41\x73\x1d\x77\x9a\xfd\x61\xbb\x3e\x78\x59\xef\xaf\x07\xa3\x5a\x6d\xc5\xcf\x9c\x28\x8e\x5d\x45\xd4\x0d\xfa\x61\xd7\x31\x88\x92\x15\x32\x3f\xf8\xae\xb3\x7f\x74\xab\xa7\x85\x09\xda\x16\x64\x73\x5c\x1d\x70\xed\xee\x52\xa8\x1a\xc7\xcc\x9f\xc5\x67\xdd\x7a\xe5\xd9\x82\x79\x7a\xd6\x6d\x10\x66\xdb\x7e\x64\xb6\x8f\xcc\xf6\x9f\xcd\x6c\x25\xaf\xe5\xda\x2f\x83\xd9\xe6\x59\x26\x8f\x92\xf8\x0f\x3c\x0d\xa2\xea\x10\xff\x7c\x2f\x0c\xda\xe5\xa3\x6e\x38\xa8\x9b\x37\xa4\x96\x4d\xad\x76\x0d\xca\xf2\xc4\xb3\x4f\xf0\xa8\x64\xae\xa1\x9a\x44\xe5\x3b\x7d\xa1\xe5\xb6\x31\x4a\xa4\x66\x09\xc3\xc1\x59\x29\x6a\x7c\x51\xea\xe8\x0a\x68\xa5\x8a\xfe\x41\xa9\x61\xdd\xe6\x46\xf3\xc9\x84\x8a\x96\x7c\x2c\xd4\x2c\xda\xe6\xed\xa6\x36\x4e\xc9\x54\x1b\x22\x0b\x74\x32\x5d\x98\x94\x9c\xa5\xe0\x06\x74\x41\xab\x40\x68\x97\x0a\xaa\x46\xc6\x71\x5a\x72\x8f\x14\x54\xb3\x8e\x43\xde\xef\x1b\x2d\xe1\xb8\x78\xb5\xea\xea\x92\x02\xc7\xd4\xe0\xb8\x62\xb7\x18\x23\xfc\x1f\xae\xb7\xb4\x6e\x97\xe0\x5f\xb4\xc3\xb1\x96\x63\x7e\x41\xa7\x69\x78\x7d\xb5\xd7\x2c\xa7\xba\x2b\xcf\x7a\x7e\xbf\x29\x28\x7d\x16\xb5\x5c\xf9\x6a\xdf\x4d\x8a\xfc\xf1\x47\x96\x58\x1f\xfd\xb0\x41\x09\xc7\x78\x45\x36\x94\x51\x18\xe1\x21\x1f\x27\x03\x82\x68\xab\xcb\x6a\x79\x86\x0b\x32\xd0\x67\x31\xc2\x57\x34\x58\x12\xb7\x30\x47\xa3\x24\x9e\xca\xd3\xb6\x48\xec\x5e\x45\xfb\x64\x63\x0b\x71\xca\x28\x09\x86\xc9\x18\x4b\x06\x8c\x1b\xa4\xdb\x44\x24\xe1\x69\xe3\xba\xc3\x46\xea\xeb\x87\xf9\x64\x72\xa3\x58\xbb\x87\x23\x84\xaf\xc2\x14\x8a\x3b\x87\xdc\x68\xd1\xab\x30\x0c\x47\x32\x17\x1a\x6f\x8d\x66\x43\x03\x3d\xdb\x04\x47\xe7\xd9\x18\xbd\x40\xf5\xb3\xb2\x23\xaf\x13\x94\x99\xc5\xb3\x52\xf9\x15\x5a\x5d\xe5\x17\x5f\x84\xff\xc3\x7a\x82\xd1\xfa\x41\x15\x6e\xf4\xe1\xa6\x06\x0e\x12\xb3\x2c\x76\x93\xa2\x6e\x08\xe1\x23\x46\xf6\x8a\xf7\xc2\x4b\x8d\x3a\x34\x9d\xfb\xf6\x3f\x6b\xa9\xaa\x49\x1d\x21\x4a\x22\x9e\xea\x0a\xc8\xab\x3f\x0f\x27\xc3\xb7\x38\x2b\x29\xc7\x73\x1c\xcd\xa7\x38\x09\xfa\x13\xdc\x45\x59\x32\xc7\xb6\xee\x2f\x98\xc2\x8d\x95\x60\xeb\xd5\x74\x36\x09\xb3\xd2\x4a\x75\x45\x09\xb8\xc9\xf8\x3d\x14\x06\xed\x2d\x9f\x28\x78\xc3\xe7\xe4\x67\x54\x57\x67\x24\xee\x7f\x3e\xe5\x35\xce\x08\x37\xd6\x9e\xbf\x7e\x45\x7f\xde\xbc\x52\x0b\x9b\x45\x5e\x69\x2a\x31\xd1\x7c\xfd\x8c\xa7\xd5\x82\x7f\xdc\x79\xc2\xe2\xfe\xe7\x0a\x94\xaf\xd0\x21\x63\x7d\x21\xf0\x83\xf4\x3a\x1a\xbc\x85\xfd\x86\x88\xbc\xd0\x85\xf2\x19\x1f\x02\x18\xc4\x4d\x56\xa4\xa4\xf8\x69\x18\xd5\xb4\x49\x02\x10\x3a\xcb\x80\xeb\x65\xf4\x1c\x70\xa8\x0e\xc6\x41\xb2\x99\x95\x6a\xe5\x6a\x16\x7f\x9c\xcd\x70\xd2\x0b\x52\x5c\x2a\xf3\xcf\x29\x91\x1f\x4a\xf5\xb2\x77\xe3\xe1\x33\xeb\xcf\x60\x2e\x37\x6e\x99\x8e\x9d\x87\x44\xe3\x35\xce\x49\x87\xec\x15\x23\x04\x14\x95\x27\x96\xc4\x5b\x7d\x1f\x83\xac\x74\x86\xa6\x87\x2e\x89\xae\x04\x44\xb7\x7b\x45\x65\xc3\x0d\x7e\xf2\x3b\xc8\x47\x7d\xb9\x5e\xca\xcb\x7e\x7f\x14\x30\x24\xed\x9c\x9c\x1d\x82\x96\x97\xed\x95\x9a\x50\x09\x27\x49\x05\xe9\x5b\x07\xff\xe3\xb8\xd0\x32\xee\xc1\x66\x35\x95\xcb\x83\x1b\x39\x64\x6c\x91\x73\xbc\x39\xa1\xb2\x47\x9a\x07\x90\x65\x00\x54\x66\xf5\x1c\xfb\xb6\x13\xb9\xfb\x0e\x12\x4c\x64\xc5\xd9\x3c\xc1\xe8\xbf\x8e\x0f\x3e\x1c\x1d\xf6\x10\x6f\xe5\x72\x1c\x0e\xc6\x70\x78\xe2\x3b\x50\x18\xa1\x3e\x28\x6d\x59\x11\x83\x23\xca\xb7\x82\xef\x55\xab\xd5\x1b\xa6\xc2\x73\xed\xcd\x88\x1c\x09\x93\xd9\x40\xa9\xea\xe4\x8e\xb2\xe3\x1e\xb2\x08\xae\x99\x85\x8e\x69\x37\xd7\x55\xe5\x51\x5b\x4b\x7e\x7a\xa6\xeb\xd7\xc9\x34\xb1\x2a\xc6\x66\x55\x82\x3d\x51\x15\x05\xc9\x92\xad\x92\x4a\x25\xb1\x4f\x96\xcb\xea\x94\x31\xac\xd8\x44\xf3\x59\x53\xa7\xdd\x37\x75\xac\xa6\x47\xc3\xc9\x07\x48\x39\x98\x1b\x41\x7b\xc8\x11\xbb\xf3\x78\xc4\x7e\x3c\x62\xff\xb3\x8f\xd8\x8a\x3e\x93\x71\x88\x29\x63\xe9\xfa\x49\xfb\xbf\xf0\x68\x94\xe0\x6b\xf4\x6b\x38\x19\x7c\xc1\xe8\xf5\x67\x3c\x1a\xf9\xc2\xf5\x2c\x15\xdb\x67\x3f\x48\xc8\x11\xfe\x20\x88\x06\x38\x80\xb2\xae\xa8\x3e\xb7\x08\x04\xc4\xaa\xbc\x0d\x2e\xd0\xaf\x71\x3c\x44\xaf\xcf\xbd\x87\xfc\x96\x3c\xe4\xff\x17\xe3\xa6\x9a\xf7\x30\x63\xb1\x79\xa9\xf1\x1d\x91\xea\xcc\x6c\xf6\xae\x54\xf6\x38\x49\x62\x23\x7a\xd0\x2a\x7d\x47\x8d\x10\xe8\xb6\xb3\x97\xad\xa4\x64\x63\x9c\xc5\x51\x1a\xf6\x27\x94\xc0\x66\x01\x78\x91\xa0\x29\xbb\xf4\x21\x7b\xd1\x2c\x89\x2f\xc2\x21\x4e\x52\x51\x2b\x98\xa4\xb1\x5d\x35\x9e\x4c\x48\x55\x42\x6d\xdc\x81\x1b\x45\xf1\x90\x7e\x0d\xa3\x41\x3c\x55\x21\x13\x60\x2c\x2b\x05\xbd\x73\xcd\xc2\x29\x26\x8b\x2d\x4c\x51\x1d\xa5\x78\x10\x47\x43\xd8\x1d\xc3\xe8\x7c\x82\xb3\x38\x82\xe1\x24\xdd\xcb\x39\xe8\x73\x54\xb5\xe3\x3e\x7f\x89\x36\x44\x57\x14\x3d\x03\x69\x1b\x34\xc0\x37\xca\x4b\x8e\x8b\xaa\x75\xf0\x1e\xfe\x88\x84\x32\x4e\xe2\x28\x9e\xa7\x93\x6b\x88\x83\xe1\xd9\x87\xc9\x27\xc7\x79\x04\x0d\x83\x2c\xf0\x9e\x90\xf5\xde\x6a\x2a\x8f\x68\xa8\x75\x9e\x80\x51\x4f\x6a\x3f\x68\xbd\xd7\xd2\xe4\xc6\x51\x1a\x93\xad\x8b\x10\x45\x89\x92\x46\x75\x2f\xba\x08\x26\xe1\xf0\x90\x95\x2f\xa9\x32\x0f\x77\xc3\x86\xc1\x50\x24\x7c\x7d\x8f\x67\x64\x5e\xcd\xe2\x43\xfa\x0e\x50\xaa\xd2\xde\x57\xa0\x9b\xcc\xda\x42\x39\xbf\xb0\x53\xf9\x86\x3e\x57\x54\x98\x65\xa0\xf9\x5d\x39\x74\x8a\x37\x12\xa6\xbf\x10\x74\x8f\x28\x15\x62\x21\xa8\x29\xdd\xcc\xc6\x49\x7c\x89\xf4\xee\x99\xe5\xb5\xee\xb0\x6e\xd2\x4f\xd5\x42\x27\xff\x60\xa9\xd9\x07\x69\x36\x97\x04\xcc\x73\xa9\x90\x7e\x16\x13\x03\x00\xb7\x28\x42\x89\x9e\x5b\x88\x36\x78\x12\x66\x45\x36\xce\xa3\x8e\xfb\x21\x04\x7b\xee\xa9\xdc\xcf\x40\x16\x90\xe7\x49\xa7\x70\x92\x78\x52\x6a\xaa\xbd\x29\x9b\xf6\x36\x88\x67\xac\xba\x0d\x8d\x2d\x1e\x32\xab\xb6\xda\xbe\x25\xe4\xb2\xbc\xe1\x1a\x09\x9a\xd1\x39\xfd\xc7\x06\x17\x35\xe6\x9d\x0c\x48\x81\x37\xe4\xbb\x43\xc9\x44\xeb\xdd\x07\x61\x42\x0b\xdf\x19\x61\x02\x4e\x2a\x75\x72\x26\x73\x3b\x52\x4c\xef\x81\x16\x75\x1a\xe4\x7a\x36\x98\x8d\x12\x6f\xe5\x4e\xa4\x97\x2e\xa2\x3d\xad\x43\x82\xe8\xd0\x82\xed\x0f\x67\x62\x5f\x25\xd2\x26\x3f\x13\x32\x91\xcf\xa2\xb8\x8c\x4f\x95\x5b\x35\x97\x4b\x4b\xa2\xae\xbe\xeb\x7b\xb7\xfb\x45\x3b\x77\x46\x8e\x54\x4c\x70\x31\x11\x25\xdf\x0e\xc5\xa7\x85\x1c\x9b\x06\xff\xbf\x01\x68\x7b\xc3\x85\x4b\xc6\xf1\x55\xd8\x25\x71\x4c\xb2\x78\x18\xa3\xc1\x04\x07\xd1\x7c\x86\x22\x80\x4f\x06\x58\x1c\xdb\xf3\x86\x4a\xc1\xde\xb1\xf2\x28\x92\x6a\x44\x14\xd1\xb8\x3e\x96\x44\x38\x3a\xa5\xa5\xcf\x88\x90\x44\xaa\x77\x11\x05\x12\x0e\xbb\x16\xa0\xae\x0b\x64\x57\xfe\x04\xbd\x2e\x62\xbe\xcc\xfa\xe8\x6b\x0c\x80\x09\x60\xfa\x6e\xce\x10\x2a\x89\x15\xbe\x60\x72\xe3\x99\x10\x4a\x89\x08\xca\xec\x68\xe1\x74\x73\x1e\x92\x23\x5d\x68\xea\x8e\x49\x1d\xc7\x9c\x5b\x73\x9b\x3b\xf2\x02\x84\x4e\xa4\x50\x97\x77\x88\x9a\x96\x39\x06\xf9\x95\x32\x3c\x12\x7f\x36\x3a\x25\xa6\x51\xfd\x82\xaf\xd3\x92\xac\x5b\xe6\x5a\xde\x8d\x8d\x0d\x54\x43\x3f\xfe\x88\x7c\x63\x48\x88\x29\x39\xa1\xef\x4b\x5a\xa1\x57\xfa\x38\x9b\x02\x70\xce\x78\xcb\xdd\x27\xc1\x84\x17\x10\xf9\x9f\x0f\xfb\x14\x0f\xc6\x41\x14\xa6\x53\x7e\x0c\xcd\x67\x0e\x00\x20\x7f\x78\x69\x1b\xea\xc0\x7e\xc1\x78\x26\x12\x08\xf0\xce\xae\xfe\xf4\x39\x1d\x87\x11\x69\xe8\x6a\x10\x4f\x67\x13\x7c\x15\x66\xd7\xdd\x36\x1c\xc9\x48\x01\x42\x10\x25\xb2\x39\x7c\xc1\xd7\x54\x53\x20\x46\x53\x19\xaf\xd5\x55\x94\xe0\x69\x7c\x81\x51\x30\x99\x40\xaf\xd2\x0a\xc2\x57\x03\x3c\xcb\x40\xec\x67\xaf\xd4\xf2\xd9\x18\x5f\xa3\x08\xd3\x11\xe9\x63\x56\x7f\x48\x7a\x3c\x0f\x26\x93\x6b\xd4\xbf\x86\x21\x23\xc3\xc3\x72\x01\x00\xcd\xfc\x4a\x36\xa4\x30\x3a\x2f\x95\x95\x7d\xa0\xf4\x83\xd6\x3b\xf4\xf5\x2b\xc1\xb7\x1a\x46\x43\x7c\x75\x30\x2a\x81\x9f\x22\x21\xb6\x4f\x2b\x65\x98\xfc\x17\x75\x73\x83\x50\x28\xec\x0b\xbe\x3e\xab\x8a\x95\x68\xda\x43\xdb\x14\x49\xca\x5b\xb6\xc9\x7f\x63\xf2\x84\x53\x26\x99\xf7\x01\x35\xce\x45\x71\x54\x84\x27\x50\x9b\xda\x3c\x9a\x64\x26\xc3\xb6\x0a\xd4\x43\x85\xa8\x43\xc0\x39\x3a\x93\xe2\x4c\xeb\x3d\x01\xac\xa8\x22\x2b\x68\x50\xdd\x3e\xd9\xfd\x74\x78\xf0\xfe\xfd\xde\x87\xb7\x9f\x4e\xf6\xf6\xb7\x0f\x3e\x9e\xa8\xc7\xa3\x22\x33\x60\x0b\x55\x9a\xc4\xf4\x20\x47\x47\x5b\x26\x23\x78\x6d\x05\x59\x80\x36\xd0\xe9\xd9\x2b\xfd\xfd\x1e\xf8\x1b\xf3\xd7\xc5\x96\xaa\x00\x58\x9d\xcd\xd3\x71\xc9\xa4\x7b\x26\xe2\x69\xa5\xf7\x86\x29\x2d\xfc\x05\x5f\x97\xad\x31\x90\x00\x97\x18\xbc\x42\xe2\xa6\x80\xac\xa6\xcb\x5d\x5d\x45\xd3\x60\xa6\x31\xc9\x10\xc8\x16\x18\x0a\x90\x18\x21\x4d\x7d\x98\xf6\x83\x99\xa2\xba\x50\xf4\xda\xba\xab\x38\x15\x5c\x81\x6b\x94\xff\x34\xc7\x60\x3f\x98\x9d\x42\xb5\x10\xb6\x78\x3e\x32\xa7\x50\xfc\x4c\x71\x49\x17\x8d\x6b\x8e\xf3\x68\x69\x99\x39\xd6\xa5\x66\x2d\xbe\xc9\xc9\xc1\xd6\x41\x97\x13\x19\x9a\xc4\xe7\xff\x61\x4a\xd5\xb1\x47\xae\xbe\xab\x24\x5d\x40\x59\x90\x3a\x8f\x8e\xec\x5b\x75\x1a\xcc\x4a\x3e\x63\x05\xfe\x07\xf6\x8b\x43\x39\xca\x64\xec\xd9\x51\x2f\x1c\xaa\x9e\x37\x82\x22\xbe\x60\x94\xce\x13\xd0\x13\x73\x66\x15\xa6\x28\xcd\x42\x42\x0f\x94\x93\xe3\x21\x0a\x46\xe0\x21\x94\x24\xe1\x45\x30\x31\xf6\x5a\x0d\x26\x19\x10\xf0\xfb\xa7\x4b\x23\x1c\x9e\x99\x28\xca\x2e\x55\x07\xd2\x1e\x40\xaf\x23\xbe\x78\x3d\x66\xb8\xee\x44\xfd\x74\x83\xf0\x84\xe9\x99\x1d\x35\x46\xc1\x24\xc5\xea\x2d\x1b\xf3\x7b\x5a\x38\xa6\xac\xfe\x0f\x3f\xb0\x36\xd1\x2d\x60\x90\x79\x81\x19\x57\x16\xad\xe7\xf0\xff\xca\x1a\xcf\x1f\xa0\x66\x81\x71\x2c\xae\x18\x40\x1a\x85\x29\xbd\x84\x8a\xfa\x28\x19\x8b\xdd\x3f\x4c\x3a\x2e\x7e\x3d\x03\x52\x2f\x39\x7d\x29\x97\x8e\xcc\xa8\x1a\xfa\x8d\x97\x91\x7b\xc9\xce\x5d\xc1\x14\xd2\xcf\xba\x0d\x88\xed\xc3\x94\xe1\xcf\xba\x4d\xf0\x43\x5d\x2b\x72\x47\xc6\x82\x6e\xe2\x2c\x0b\xa3\x73\xb7\x6b\x2f\x30\xa6\xa1\x92\xfb\x18\x6d\x08\xa7\xb5\x57\x56\x09\x19\xea\x59\xd8\x07\xf9\xa2\x16\xb1\x46\x59\xbf\x09\xca\xeb\x8f\xd7\x7a\x8f\xd7\x7a\xff\xf0\x6b\x3d\x16\xd2\x97\x9d\x5a\x6e\x13\xd6\x77\x91\x39\xac\x27\xf9\x85\x91\xfb\x62\x19\xc3\x59\xbe\xa4\xeb\xec\x70\xb0\x39\x1c\xa6\x30\x74\x62\x77\x0b\x22\x50\x4b\xa5\x68\x4e\xc5\x2f\xe6\xf5\x56\x21\xc2\x57\x98\x41\xa8\x3c\x04\x59\x01\xe8\xa6\x4a\x77\xfb\xa7\x4f\xd5\xf3\x01\x3b\x9f\x3d\x35\x95\x44\x64\xdb\x7c\xca\xae\xad\x94\x72\x0a\xaf\xa2\x81\x7a\xb8\x2f\x1d\x29\x17\x47\xcc\xe3\x4a\xe3\x68\x4c\x6e\x22\x63\xef\x50\x35\xfa\x84\x22\xba\x6f\xf3\x9e\xa6\x8e\xcd\xc2\x65\x8f\xc3\xff\xf4\x7d\xcb\xdc\x9e\x7c\xba\x4b\x61\x21\xc8\x23\x11\x01\xca\x3f\xfe\x08\xb8\x53\xc5\x54\x18\x9d\x03\x37\x2e\x6b\x10\xf9\xf5\xc5\xa2\x9c\xa6\x14\xa2\xea\xa6\x7c\xdb\x4e\x0a\x69\x68\x12\xa4\xd0\xcc\x71\x46\x26\xfb\x87\x8d\x0d\x6b\xa0\xf9\x9f\xf5\x62\x75\x95\xe6\xfe\xd7\x48\x0a\x96\x5a\x96\xcc\x89\xcc\x96\xa4\x19\x4a\x63\x6a\xe7\x38\x9b\x01\xeb\x86\xb3\x73\x10\x5d\x67\xe4\xc0\x5f\x41\x7d\x3c\x22\x0c\x80\x2e\x71\x7e\x85\x0a\xa3\x41\x95\x8c\xc6\x5f\x38\x2a\xfd\xe0\xc0\xfa\xc7\x1f\x91\x6b\xe4\xcb\x56\x7d\x64\x5f\x37\x10\x54\x1d\xfe\xd1\xde\xce\xc6\x94\x6f\x46\xf8\x2a\x43\xbd\xc3\x8f\x68\x70\x3d\x98\xe0\x8a\xe8\x26\x0c\xbb\xd8\x6c\xa0\x27\xd0\x65\x66\xb3\x34\x4b\xe2\x01\xe1\x59\x29\x1d\x1d\xab\x15\xe5\x18\x2c\x96\x89\x6b\x2e\x1c\x1d\x61\xa4\x61\x96\xba\xa9\xa0\x5a\x91\xfe\x39\x86\x95\x92\x82\x4f\x34\x53\x8c\xc1\x9e\x0a\x00\xa6\x19\x9b\xa2\x8b\x2d\xd9\x76\x50\x9e\x7c\xbf\xa6\x25\xd4\x4d\x45\x0a\xe1\x7b\xc3\x8a\x64\x13\xec\xbd\xaa\x43\xa2\x3a\x03\xe0\x2c\x64\x9d\x70\x3b\xc9\x3d\x67\x5e\x4e\x6f\xb2\xcd\x7c\x93\x79\x43\xfe\x43\xaa\xae\x69\x8f\xc8\xd1\x8a\x72\xea\x39\xe5\xc2\xcf\x9f\x2b\xe5\xc4\x7a\x55\x4e\xfa\xf0\x21\x18\x0e\x85\x6d\x97\x92\xf8\x53\x7c\x37\xa7\x47\x39\x38\x28\x2c\x96\x1b\x6f\xc1\x7b\xc5\x56\x9c\x0a\x74\x62\x24\x54\x4b\x5f\xd9\x6e\xae\xc5\x62\x38\x92\xaf\x74\xad\x94\x64\x41\xa0\x55\x30\x90\x2f\x84\x84\x3a\x8b\x7e\x89\xd6\x22\x30\xa1\x72\x2e\x29\x73\x50\xce\x19\x6d\xa7\x54\x2b\x10\xf2\x1b\xb0\x11\x59\x5d\x4f\x77\x41\x64\xdf\xc7\x24\xa5\x8f\xb2\xef\x3f\x5d\xf6\x95\x26\x6d\x3c\x63\xef\x7d\xf9\xe8\xee\xf5\x83\x48\x97\x76\xc3\x7e\x20\x5c\x6f\xf1\x15\x55\x57\xe7\xb9\xee\x1e\x4f\x83\x24\xdb\x66\x05\xa5\xdb\xad\xf7\x6a\x0c\xd4\x4a\xd0\x2c\xef\x8b\xa1\xf3\x56\x5e\x8b\x4b\xb0\xe3\x2c\x09\xa3\xf3\x1b\x70\x6d\x71\xbd\x27\xd2\x72\x3f\x88\xd4\x4f\xbf\x04\x93\x39\xbe\x41\x17\xe4\x1f\x76\x1d\x42\x20\x8f\x70\x82\x17\xdc\x90\x56\x74\xf3\x02\x88\x52\xc3\x70\xd2\xc5\xe2\x6c\x5c\x01\x8c\x88\xb4\x5e\xa1\x2d\xd9\x5b\x18\xa8\xdd\xe8\x28\x43\xba\xe9\x7e\x10\x95\xb2\xb8\xcc\x54\x45\xa0\xc3\x21\x9f\xb9\xca\xa7\xe4\xb0\x22\x22\xf5\x20\x4f\x44\x69\x25\xa4\xea\x1b\x0a\x91\xf9\xe9\xae\xd8\xfa\x63\x06\x71\x2b\x4c\x88\x2c\xe6\x72\x88\xe1\x3d\x3a\x89\x99\x67\xaf\xda\x1d\xa8\xce\xa0\x97\xca\x76\xd7\x78\x7b\x42\x8e\x81\x6e\xb8\x24\x5d\x70\x91\x10\x9e\xd2\x38\x1b\xab\x39\xc1\x4b\x65\x68\x84\x61\x1b\xa5\x59\x98\xcd\xa9\xc0\x65\x9b\x7f\x0d\xf1\x2c\x4e\xc3\x4c\xc5\x92\xc1\x15\xe8\x01\x98\xc1\x24\xc4\x51\x66\x5a\x62\x14\x6e\xd8\x32\xb1\xe0\xb9\xc6\xed\x11\x5c\x16\x23\x7b\xfc\xb8\x0a\x3e\xf7\x2a\x59\x90\xde\x68\x1e\x0d\xc1\x26\x72\x80\x93\x2c\x08\xc5\xf4\x7b\x96\x8f\x98\xd8\xe5\xd6\xd1\x83\x2f\x21\x81\xd7\x2d\xd6\x12\x1b\x79\x32\x9b\x46\xca\x2f\x45\xb6\x15\xde\xeb\x59\x2c\x25\x5a\x02\xba\x4b\x1b\x50\x68\x73\x32\xc7\x5d\xfa\x0f\x17\x73\x8d\x6c\xef\xde\x59\x61\x93\x2f\x27\x05\x02\xdb\x87\x03\xc4\x39\x21\xe2\x1c\x12\x95\xa6\xf3\x34\x83\xad\x0e\x4f\x71\x94\x09\xba\xe9\x5f\x67\x38\x6d\x36\xca\x4c\x18\xff\xa1\x6c\x4c\x24\x2b\x77\xef\xd3\x97\x5a\xf3\xc7\xab\x53\x4a\x45\xf3\x28\xfc\xef\x39\x46\xe1\x10\x47\x59\x38\x0a\x75\x4e\x5c\x68\xae\xf9\xe8\x14\x98\x61\x68\xd2\xcd\x35\x03\xd8\x75\x94\x3d\xe8\x95\x49\x04\x7c\x8c\x4b\x41\x3f\x2c\x57\x83\x8c\x30\xd6\x2a\x1f\x5f\x0e\xfa\xcf\xbb\x12\x81\x25\xab\xf2\x51\x74\x06\x41\xb0\xf7\xc3\x67\xdd\x26\x11\x5d\x79\xe6\xfe\x9b\xb3\x4a\xbb\x50\xae\x64\xa6\xdd\x6d\x17\x4a\xd8\xf6\x4a\x55\xc2\xc7\x44\xbe\x18\x05\x83\x2c\x4e\xae\x2b\x54\xa1\x4c\x06\xf6\x09\x61\xd3\x44\xd4\x8f\x47\x48\xf4\x66\x63\x03\x3d\xa3\x11\x99\x9e\x41\x99\x27\xab\xab\xa8\x17\x4f\xa7\x71\xf4\x5f\xc7\x4f\x9f\x3c\xb1\x3a\x2f\x7f\xb1\x06\x38\x4e\xa5\x67\x64\x18\x12\xfc\xac\x5c\x41\xca\x2b\x1c\x0d\x5e\xf4\x83\x14\x77\x5a\xc6\x87\xe9\xb0\x6d\x16\xbd\x98\x7d\x19\x8e\x8c\x97\x83\x70\x36\xc6\xc9\x0b\x0a\xb9\xfc\xea\xe9\x93\x9b\xa7\x4f\xf0\x24\xc5\x48\xe9\x0c\x55\x98\xd3\xbe\xf0\x61\x78\x86\x7e\xfc\x91\x7d\xa8\x06\xd3\xa1\xe8\xdb\xe6\xfe\xd6\xd3\x27\x4f\xe8\x87\xd2\x29\xc7\xb9\x82\x74\x54\xe1\x99\x60\x48\x3f\x50\xc4\xe0\xb7\x8a\xcf\x99\x18\x65\x15\x31\xd6\x10\x8d\x86\x81\x4a\xfd\x24\xbe\x4c\x71\x52\x7e\xfa\xe4\x89\x18\xb1\x38\xce\xaa\xbd\xe4\x7a\x96\xc5\xff\x75\x4c\xab\xde\xc0\xe9\x49\xdd\x7e\xc4\x77\xf4\xe7\xd3\xa7\x4f\x4a\xfa\x71\xec\x09\xa2\x1a\x91\xe3\x71\x9c\x64\x83\x79\x96\xd2\x37\x64\xd9\xf4\xd0\x06\xe2\x75\x5f\x29\xaf\x3f\x4d\xc2\x3e\xf9\x54\x9d\x84\x7d\xe5\x3d\x28\xc3\x7a\xd0\x29\xf2\x95\x94\xaa\x2a\xef\x34\x08\xc1\xe4\x3c\x06\x10\xe4\xc7\xab\xa7\x02\x8b\xf7\x71\xfc\x65\x3e\x43\x59\xd0\x9f\x60\x05\x93\xe3\x37\x07\xbf\xb1\x33\x9f\x78\xb7\xf7\xe1\x97\x4f\xae\xf7\xc7\x1f\xdf\x7c\xda\xdf\xfb\xed\x53\xcd\xf7\xa1\xee\xfb\xd0\xf0\x7d\x68\x3a\xdb\xf6\xb5\xa3\x7e\xb4\xda\x52\x3f\x5a\xed\xa9\x1f\x79\x9b\x62\x68\x7a\xf1\x74\x46\x0e\x8a\x13\x7b\x88\x5c\x53\x6a\xd4\x1a\xc6\xf3\x3e\x91\xfa\x49\x2d\x59\x00\x58\xac\x8a\x05\x52\x2d\x15\x42\x08\x27\x88\x42\xf4\x1a\x35\xda\x9d\x57\x28\x7c\xfe\x5c\x03\x2f\x64\x44\xf4\x1a\xd5\x1b\xeb\xd6\x37\xf2\x37\x3c\x0d\xcf\xd0\x06\x81\xf1\x1a\xd5\x5f\xe9\xdf\xe9\x55\x6a\x4e\xad\x12\xad\x56\x46\xbf\xa3\xda\x55\xbd\xde\x37\xeb\xcb\xc7\x9b\xa7\x5a\xaf\x7f\x0d\x26\x5f\xd0\xdb\x9d\x52\xe3\xf7\xf5\xb2\xde\xdb\x2b\x1a\x22\x51\x7f\x17\x1a\x2f\x97\x1a\x01\x65\x90\xd3\x7e\x7c\xa5\x7f\x04\x43\x03\xd2\xe6\x55\x88\x7e\x47\xa5\x2b\xd9\x21\xf6\xbb\xa1\xfc\x6e\x2a\xbf\x5b\x65\xa3\xb3\x00\xa5\x94\x5e\xa1\x9f\x7f\xfe\x19\xad\x43\xc9\xf4\x0a\xfd\x88\x6a\x57\xa3\x11\x1d\xa0\x4e\xd3\xa8\x42\x56\xc7\xe9\x15\x19\xc8\xf4\xca\xf8\xc4\x17\xcf\x69\x0a\xdf\xaf\x5e\x3d\xf5\x76\x6a\x3a\x9f\x64\xe1\x6c\x12\x0e\x40\x4b\x60\x77\xef\x8a\x90\xf1\xf0\xf4\xea\xec\x95\xe3\x5b\x8b\x7e\x6b\x38\x3f\xae\xd3\x8f\xad\xb3\x9c\xd6\xd3\x79\x1f\x81\x7c\x53\x41\xd3\xf0\x0a\x0d\xe2\xc9\x7c\x1a\xa5\x1a\xf5\xab\x30\x89\xa4\x50\x1a\x42\xaf\x7e\x22\x34\x53\xab\xf3\x91\x62\x8f\xb5\x7a\xad\x66\x0e\xad\x58\xc9\x74\xb0\x4a\x19\x4c\x4c\xab\x8c\xbe\x92\xdf\x74\xbc\x3d\x55\xea\x6a\x95\x7a\x47\xa9\x52\xef\xf8\xea\x34\xd4\x3a\xeb\x65\x24\xeb\x34\xac\x59\x17\xdc\x80\xd6\xc9\x72\x46\x2a\x8c\x2e\xd4\xd1\x22\x8f\x85\x47\xec\x6a\x5d\x19\x1f\x46\x9e\x2d\xf6\xaa\xc6\x5f\x34\xb4\x21\xcd\x1d\x51\x8d\x3f\x32\x1a\x2b\x32\xac\x1a\xeb\xd4\xea\x2d\x18\x5b\x8d\xad\x6a\x15\x17\x0c\xb0\xc6\x72\x59\xc5\xbc\x51\x86\xcb\x02\xd0\x03\xe3\xc4\xe6\x84\x3f\x5c\x39\x99\x20\x63\x00\x1b\x4b\x70\x40\xa8\xd2\x40\xbf\xa3\xe1\x29\xf9\xdf\xd5\x3a\xfa\x1d\x5d\x35\xce\xce\xcc\x85\x04\x65\x43\xf4\xfb\x06\x14\xbc\x0a\xad\x02\x1a\x93\x84\x9f\x37\x70\xa6\x15\xfb\xca\x61\x82\x07\xb4\x73\x43\x74\x34\x88\x23\xb6\xc1\xc8\x5d\xe9\xa8\x77\xf0\x81\xec\x11\xb5\xab\x5a\xad\x82\x6a\x57\xb5\x3a\xfc\xb7\x01\xff\x6d\xc1\x7f\xd7\x2b\x40\x0b\xe4\xbf\x0d\xf8\x6f\x0b\xfe\xbb\x0e\xff\xad\xf7\xc9\x7f\x9b\x1d\xb9\x99\xfd\xf4\x13\x43\xea\x27\xb4\xb9\x7d\x4c\x03\xb2\x23\x2a\x0e\x21\x22\x10\x24\x61\x36\x9e\x56\x79\x99\x55\x89\x0a\x29\xbd\xc1\xc4\x87\x2a\x7d\x50\x24\x8c\x2a\xbe\xca\x68\xf4\x00\xd1\xe5\x4f\xc3\xf8\x08\xa7\x38\xeb\x22\xcf\x16\xc9\x06\xe1\xf8\x4b\x38\x63\x96\xbf\xf1\x08\x45\x47\x31\x9c\xc6\xc6\x41\x8a\xfa\x18\x47\xe0\x1d\xc0\xee\xb7\x82\x68\x08\x26\x7c\xc3\x70\x88\xa2\x38\x63\x66\x98\x36\x29\xd0\x6c\x2e\x1c\x12\x37\x17\xfd\xf4\x05\x5f\x1f\x26\x61\x9c\x1c\x51\x0b\xe0\x8d\x0d\xf9\xde\x49\x3a\xdc\x2c\xcc\x98\x53\xbb\x03\xba\xf8\xc6\xff\xb8\xc1\xe1\x86\xbb\x79\xf9\xd6\xc1\x9f\xbf\xe0\xeb\x5f\xe3\x04\x8c\x18\xbf\xe0\xeb\xea\x25\xf9\xed\x2e\x76\x1c\xfe\x81\x59\xa9\x34\x3c\x7f\x43\x18\x10\x5a\x45\xad\xbc\x65\x24\xfc\x00\x12\x18\x20\x1b\x2c\x1f\x39\x8e\xa3\x7c\xe6\x0d\x3e\x47\x9d\x42\x2d\x90\xfe\xa7\x83\x31\x26\xc7\x0f\x44\x44\x68\x47\x1f\xd2\xa3\xf8\x92\xc0\x2e\xf1\x66\x9e\x93\x5d\xfa\xa7\xdc\x3e\xa8\x70\xdd\xc3\xc2\x1b\x55\xc6\x59\x79\x77\x6a\x2e\x55\x69\x22\x4a\xd0\xa1\xa2\x07\xfd\xf9\x9a\x61\xc8\x9e\x1d\x52\x08\x62\x64\x27\xca\xd3\x41\x72\x96\x23\x7f\x0a\x2a\xa7\x50\xe7\x8c\x8e\x2c\xcc\x38\x7b\xe3\x60\x35\x7e\x86\x85\x94\xfd\xc4\x02\x0e\xd1\x74\xcc\xa1\x54\xd1\xfe\x81\x21\xfe\x2f\x81\xb8\x17\x73\x36\x0b\x47\x71\x86\x08\x49\xfa\x0b\x65\xea\x1e\xa0\x6f\x01\xb9\x90\x8f\xe7\xfd\x22\x90\x41\x7c\xe2\x30\xcf\x94\xbd\x0d\x3e\xc8\x9d\x8a\xc9\x68\x67\xca\x2e\xa6\x96\x58\xd7\x0a\x00\xa6\x0c\x32\x7b\xbd\x00\xdb\xfd\xf0\x0a\xd8\x76\x1e\xb6\xbf\x6f\x00\x13\x3f\x65\x83\xbc\x2a\xa9\xe3\x2b\xaa\x31\xd4\x1d\x93\x8d\xe4\x84\x03\x69\xb1\x75\xf7\x33\xea\x10\x7e\x66\x4c\x18\xda\xd8\x40\xad\x45\x93\xf6\xdd\x0d\xad\xbb\xcf\x9e\x11\xf7\xad\x19\x8b\xd6\xd9\x90\x9c\xa1\xdf\x89\x2c\x61\x2f\xa2\x85\xdc\x5c\x95\xe9\xf2\xd9\x4c\x18\x5d\xbc\x73\x70\x1a\xeb\xb5\x9f\xd9\x90\xa2\x92\xdf\x88\x27\xc9\x72\xf8\x2b\x0f\xd7\x51\x19\x16\xe3\xa3\x2f\x44\x1d\x17\xf1\xc2\x91\x91\x37\xf3\xaf\x1c\xa2\xf1\xb2\x93\xfb\xe5\x4c\x2d\x27\xb8\x45\x88\xbf\x46\x2d\x70\x64\xa1\x0f\x79\xb4\xaf\xcf\xc5\x29\x87\xc0\x24\xcd\x25\x3b\x92\x03\x4c\x17\xba\xf5\x35\x44\x48\x51\x17\xae\x3d\x4b\xe9\x0c\xfd\xee\x5f\x9c\x9e\x3f\x5d\xf8\x76\xaf\x40\x13\x81\xe6\xa9\xbe\x14\xdd\x73\xe0\x95\x64\x2b\xca\xf4\xe0\x68\x90\x5c\xcf\xa8\x65\xac\x2a\xe7\xed\x57\x50\x3c\x1a\xa5\x38\xb3\x66\x86\xae\x91\x61\xdc\x13\xf5\x64\xe1\x8a\xbd\x57\x57\xe4\x09\x51\xfe\xac\xcb\x9f\x0d\xf9\xb3\x59\x01\x16\xa3\x9e\x32\x34\x5c\x87\x78\x59\x5c\x09\xd7\xbc\x0c\x66\xa8\x11\x0d\x41\xf6\x6c\x65\x63\x8f\x10\x43\xe8\x7b\xff\x94\x82\x21\xf2\x8b\x39\xa4\xda\x37\xbd\x6c\x33\xa7\x6c\xd3\x79\x24\x2a\x32\x84\x3a\xad\x56\x74\x02\xd5\x1f\xeb\xfa\x63\x43\x7f\x6c\x56\x84\xc2\xc2\xda\xbc\x57\x57\xd1\x1e\x39\xf9\x7e\x17\x63\xe4\x9e\x74\x6d\x98\x9c\xb3\x5e\x41\x77\x23\x37\x17\xd1\xb0\x03\x41\x61\xc9\xda\x31\xb0\x6f\x31\x8b\x15\x0a\x17\x92\x54\x54\x27\x98\x3a\x74\x5c\x35\x65\xb0\xce\xe0\xf5\xef\x1a\xb3\xad\xb9\x34\x40\x69\xdd\x9c\x0e\xa3\x96\x35\x3f\x50\xab\xa1\xd7\x6a\x98\xb5\x9c\xda\xa6\xb4\x69\x4e\xa7\x51\xab\xe9\x52\x43\xbd\x33\xce\x0e\xee\xa3\xbf\xba\x05\xba\x4e\x0c\x47\x8e\x33\x8e\xd8\x7f\xe9\xa8\x6e\xa0\xfa\x2b\xf6\xf3\x35\x9f\x21\xf6\xc2\xb3\xef\xc2\x1c\x87\xa3\x0c\x28\xbd\xe2\x51\x94\xe5\x4e\x1c\x47\x3d\x23\x93\xa7\xa8\x6b\x6a\x42\xf2\xfa\x5d\x51\x74\x95\xd2\xba\x25\x77\xfd\xae\x28\xb5\x4a\x69\xc3\x94\xba\x7e\x57\xf4\x57\x69\x53\x79\x6d\x6d\xc3\xcf\x9f\xbb\x36\x00\x40\xae\xae\x23\x57\xf7\x20\xd7\x58\x80\x5c\x33\x17\xb9\xda\x2d\x91\x6b\xe8\xc8\x35\x3c\xc8\x35\x17\x20\x57\xcb\x45\xae\x7e\x4b\xe4\x9a\x3a\x72\x4d\x0f\x72\xb5\x05\xc8\xd5\x73\x91\x6b\x2c\x44\xce\x49\xba\x1f\x67\x60\x43\x94\x66\x41\x86\xed\x02\xc0\x4e\xb2\x9a\xa3\x63\xc0\x32\x32\x53\x8f\x06\x5f\xc8\x5c\x64\x0d\xd7\x17\x32\x10\x99\xa9\x1d\x77\x2a\x51\x9c\xeb\x69\x01\xef\x83\xe5\x53\xa2\x27\x0f\x65\xed\x98\xa7\x16\xc7\xf2\x31\x8f\x2d\xf6\x0a\xd2\xce\x2d\x72\x09\x95\x8b\x51\x82\x58\x3f\x1c\xbb\xba\x1f\x3b\x7b\xfd\x58\xd8\x59\x4b\x48\xc7\xae\x76\x1b\xec\x1a\x0a\x76\x0d\x3f\x76\xf6\x02\xb2\xb0\xb3\xd6\x90\x8e\x5d\xfd\x36\xd8\x35\x15\xec\x9a\x7e\xec\xec\x15\x64\x61\x67\x2d\x22\x1d\xbb\xc6\x62\xec\x6c\x6a\xc5\x3c\xb0\xb5\x5b\x2e\xa1\xdb\xb0\x63\x1d\x99\x42\x8e\xb5\x9c\xf4\xcd\xd5\xb1\xaa\x2c\xd1\xa7\xe9\x93\x7d\xd8\x51\xb8\x8b\x1a\xed\xce\x6a\xb3\xc1\x34\xd0\x65\x97\x2a\x98\x4b\x2c\x42\x40\x4a\x99\xe3\x30\x53\x0d\xaf\xa4\x2c\xe1\x13\x82\x1c\xde\xa3\x60\x80\x85\x8e\x58\x00\xf9\x4f\x7c\x15\x4c\x67\xe2\xa4\x2c\x3f\xf0\x39\xa5\xb0\x32\x7c\x95\x29\xb7\xdb\xd5\xcd\xed\xe3\x2a\x3b\x47\x94\xa6\xdc\x22\xfd\x0b\xbe\xae\xa0\xc1\xe8\x5c\x48\xf3\x12\xca\x6c\x12\x10\x24\xae\x32\x64\x42\x61\x12\x7e\x49\xb6\xe3\x02\xc4\x74\xda\x3d\x87\x12\xfb\x13\x8d\x9a\xba\x8b\x27\x33\x9c\x94\x36\xb7\xe9\xb5\x3e\xd5\xd9\x3f\x7d\xc2\x6c\x56\xd4\x26\x5f\x3d\x7d\x0a\x11\x70\xc1\x80\x44\xb3\x2a\xe8\xb6\x1b\x15\x6e\x97\xd0\x6d\x83\xed\x88\x62\x99\xd0\x6d\xb7\x2a\xd2\x24\xa1\xdb\x06\x17\xc6\xe9\xb0\xfd\xac\xdb\xa9\xdf\x9c\x55\xda\x8d\x3b\x59\x8b\x7c\x4b\x33\x91\x07\x33\xe6\xf8\x86\x66\x19\x74\x25\xfc\x84\x98\x01\x05\x69\x1e\x0d\xe2\xe9\x2c\x8e\x20\xe4\x3a\xf9\xb6\xfa\xf4\x89\x98\xf7\x49\xd8\xaf\xb2\xa2\x5f\xbf\xaa\x06\x00\xc2\xe9\xf3\x9e\x8d\x3b\x82\x14\x4b\xab\x8e\x20\xc5\xca\xb7\x5f\xe3\x64\x08\x6e\xe9\xa2\x80\x78\xa3\x42\x98\x8f\xc0\x5e\x0c\x68\x7d\x93\xdf\xf2\x48\x98\xce\xcf\x1a\x66\x18\x3c\xab\x7a\x64\xa1\x2a\xef\x3f\x66\xa3\x75\x80\x82\xa3\x41\x95\x3c\x18\x58\x77\x5a\xe2\x2b\x7d\xcc\x33\x44\x11\x5f\xb6\x2f\x66\xef\xb6\x76\xe4\x65\x13\x7d\x76\xde\x60\xf5\x53\x6a\x9e\x47\x96\x15\xbf\xc5\xca\xf0\x74\x36\x09\x32\x17\x83\x12\x41\xa6\xff\x8c\x58\x40\x1e\xae\x41\x05\xa7\x02\xc1\xeb\x40\xef\x17\xfe\x81\xab\x3c\xc0\x64\x17\xb5\x50\xa9\xde\x58\x47\xfd\x30\x4b\xcb\x79\x00\xc3\x0b\x07\xbc\xbd\x5f\x6e\x0b\xee\xd3\xf6\x87\xde\xa7\xdf\x76\x0e\x8e\xf6\x3f\xed\x1f\x6c\x6d\xa3\x4d\x08\x6d\x90\x05\x51\x86\x12\x3c\x4b\x70\x8a\xa3\x2c\x8c\xce\xb9\x22\x86\x90\xe1\x34\x1e\xca\xbe\x3b\x61\x6e\x6d\x17\x82\xc9\xd8\xa9\x05\x53\xb9\x14\x34\x4c\x8e\xc4\xa3\x9b\xa2\x1c\x97\x84\x72\x36\x29\xba\x3d\x70\xfb\x9e\x27\x60\xf0\x20\x72\x7c\xa8\x45\xb4\xe2\x4a\xef\x04\xdd\x93\x39\x40\x27\x63\x4c\x46\x3d\x8b\xd1\x9c\xb9\x09\x10\x16\x80\x48\x61\x00\xad\x81\x5c\x95\x0f\x83\xd1\x79\x17\x48\x97\xe3\x5a\x56\x77\x54\x0b\x5b\xd8\x2e\x52\x0a\x9b\x91\x5f\x18\xf9\x26\xc3\x85\x3e\xb5\xc7\x54\x70\x27\xa4\x47\x90\xff\x82\xaf\xab\xce\xb2\xdc\x33\x74\x30\x3a\x47\xa5\x03\x68\x25\x98\x94\xa1\xce\xc0\x35\x78\x05\xc7\x40\x6f\x8b\xc7\x11\xa5\x13\x7a\x43\x48\x84\xf7\x8e\x10\xca\x20\xaf\x4f\xe4\x5c\x11\x0e\xfc\xdf\x75\x29\xc1\x2e\x80\x34\x69\x41\xdd\xe3\xf9\xd5\x73\x95\x6e\xd3\xdb\x74\x98\xe3\xa4\xc4\x2e\xcf\x60\x08\x2b\xe8\x4f\x14\x5e\x74\x51\x78\x21\x79\xe3\x8d\x66\x7a\xa0\xcd\xb7\x0e\xa9\xab\x85\x85\x62\x92\x83\xa9\x01\x50\x13\x87\xd0\xfa\xec\xc6\x59\x5f\xab\x0e\xd9\xc3\x94\xd0\x0a\xd2\x93\x67\x21\x3e\xd2\xd3\xfd\xd2\xd3\x16\xbe\x2f\x7a\x12\x90\xee\x46\x4f\x3a\x9f\xbe\x05\x3d\xed\x45\x61\x16\x06\x93\xf0\x0f\x9c\xa2\x00\x45\xf8\x72\x72\xcd\x30\x1c\xb2\xe1\x58\x4c\x4b\x7c\xd7\xb8\x1a\xc5\xc9\x74\x3f\x1e\x62\xb4\x4d\x7d\xd5\x20\x4c\xb3\xe4\x74\x71\xa2\xd2\x29\x58\x57\x83\x9b\x1f\xa7\x5a\xb1\xc9\xb8\xc9\xf0\xbb\x23\xd9\x7b\x23\xab\x92\xfd\xc1\xc5\x29\x6e\x49\x70\x61\x14\x6a\x16\x36\x62\x9a\x14\x72\x71\xa8\xa8\x37\x67\x33\x42\x0b\x30\x5a\x3c\xdd\x74\xea\xb8\x66\x20\x43\xbc\x21\x7e\xf2\x4d\x91\xd2\xa0\x7d\x2a\xce\x88\xe4\x4c\x0d\xeb\xe3\x64\x4a\xa7\x3d\x70\xe9\x6e\x28\x7d\x4b\x92\xda\x90\xe4\xf5\xca\x55\x92\xda\xd1\x80\xad\x8c\xf3\x2c\x1e\x52\x42\xa7\x1e\x00\xae\x7e\x80\x7d\x51\xa9\xf0\xc2\x01\x1b\x1d\x9d\x0f\x43\x2c\x87\x54\xb4\x04\xda\xb3\x3b\x92\x0f\x5b\x82\x36\x6e\xda\x0c\x27\x45\x8c\xa8\xa8\x51\xd1\x30\xc8\x02\xd4\x07\xd9\x4b\x2f\xe1\x91\xc7\x00\x34\xcd\x74\xc1\xbd\x9d\x4d\xc0\x87\x38\x81\xb9\x1c\xc4\xd1\x20\xc1\x19\x7e\xc1\x86\x63\x12\x9f\x6b\x4c\x59\xb9\x97\x3a\x5a\x6e\xac\x21\x9e\x06\x60\x4e\xdd\x5b\x18\x4f\xc1\x43\x85\xa5\xe0\xe1\x12\x9b\xde\xd7\x94\xb9\xc2\x10\xa0\x4c\xd9\x49\x78\x03\x6f\x83\x35\xa0\x80\x2f\xb0\x73\x29\xfc\x49\xc0\xa2\x41\xb3\x58\x30\x82\x30\x3a\xbf\x07\x6e\x22\x3b\xbf\xc1\xc9\x83\xc1\x2f\xad\x90\x36\x57\x74\x32\x29\x52\xef\x92\x63\xee\xa5\x30\x56\xb2\x6b\x44\x79\xa5\x43\xe7\xe1\x1e\x38\x1a\xba\x66\x3f\x80\x2f\x6a\x75\x17\x4d\xd1\xf6\x50\x70\x11\x84\x93\xa0\x3f\xc1\xd4\x0c\x31\xf5\x6f\x8b\x9f\x78\x67\x0a\x53\xd5\x4e\x18\xb1\x8d\x2f\x77\x9f\x62\x70\xf5\x7d\xe6\x43\x9c\x31\xef\x68\x1a\x34\x8d\x42\x92\xbb\x06\x0a\x53\x84\x47\x23\x3c\xc8\xc2\x0b\x3c\xb9\x46\x01\x1a\xe2\x34\x4b\xe6\xf0\x5c\x41\x09\x0e\x86\x2f\xe2\x68\x80\x0b\xed\x33\x45\xa9\x17\xd0\x78\x28\x1a\xa6\xc0\x1f\x9a\x92\xf9\x48\x96\x8a\x13\xb1\xa8\xb2\x2c\xf5\x8b\x8a\x8b\xc9\x9f\x17\x2d\x4e\xff\x3b\x72\x2e\xe6\x50\x48\x2f\x11\x8e\x72\x01\xa0\xdc\xd5\xa2\x15\x75\x5c\x94\x2c\xc1\x90\x21\x1e\x12\x41\x95\x2d\x38\x3c\x64\xf1\x32\x39\xa7\xde\x51\x26\xc4\xb9\xf8\xec\xda\x0b\x95\xcd\xf5\xc6\xfa\x6a\xb3\xa1\x7e\xa2\x2a\x11\xd7\x17\x43\x0e\xea\xa2\xba\xf6\x55\x97\x7f\xbb\xa8\x51\xe4\xec\x94\x3a\x55\xd9\xc1\x62\x45\x36\xf2\xae\x4d\x7e\x6a\x61\x23\x7d\x32\xc6\x8a\x50\xc0\x12\x6d\x05\x68\x0c\x5a\x63\x22\x64\x16\x58\x8a\x5c\x84\xdd\x8c\x38\x3e\x10\x60\x80\x2f\x6b\x22\x34\xb1\x75\xed\xe8\xd0\x37\x38\x2c\x31\x6b\x6f\x5b\xe5\x69\xe8\xc8\x2d\xd9\xd6\xbb\xca\xb4\x7a\x5d\xaf\xdf\x14\xf9\x13\x9f\x52\x3c\xc1\x83\x8c\x36\x7c\x9c\x25\x41\x86\xcf\xaf\x4b\x3e\x73\x6d\x45\xfb\x0c\xe2\xe2\x06\x5a\xa1\xac\x74\xc5\x6b\x1e\xc6\x66\xe3\x30\x48\x53\xc2\x26\xde\x04\x29\x1e\x6a\x1e\x73\xea\x5f\xbe\x71\x18\x03\x75\x8c\x13\x38\x70\x91\x5d\xcd\x0f\x29\x7f\x91\x9b\xb9\xfd\xd8\x7d\x46\x8e\x8d\xba\x0f\x29\x46\x4e\x2a\x63\xb3\x6f\x58\xf2\xec\x46\x65\x10\x30\xf7\x3c\x88\x8b\x1b\x8a\x62\x05\xf9\x2f\x70\xcc\x31\xa8\x78\x2c\x3d\x19\xd9\x77\xad\xfe\x1b\xf7\x39\x77\x42\x5b\xbf\x29\xaa\xa0\xdc\x1b\x23\x13\x73\xc7\x84\x9a\x6c\x5b\xe5\x92\xa5\x32\xd3\xf0\xba\xaf\xde\x74\x1d\x76\x9a\x25\x38\x98\xde\x4a\x95\x0d\x32\x14\x53\x3e\xab\x36\xf8\xcd\xc6\x8b\x7e\x48\x0d\xb6\xf5\x13\x0d\x95\x4e\x20\x8c\xb5\xa2\x99\xae\xa3\x52\xb3\xa1\x2b\xa6\x15\x85\xef\x31\xe0\x67\xa8\x7d\xcd\x97\x39\x1e\x21\x3b\x8e\xbd\xd6\xb5\xc3\x72\x11\x71\x16\x24\x70\xdc\x72\x09\x88\xf6\xf6\x06\xc7\x1b\x69\x5d\xc5\x85\xc6\x1f\x7e\x58\x19\x4d\xe6\xe9\x78\xa5\xd8\x36\x47\xa1\xf8\x36\x3a\x31\xcc\x5d\x54\xcf\x9b\x57\x38\xd7\x42\x56\xd3\x99\x7a\x5b\xaa\x2a\xcf\x3f\x4d\xe9\xd9\xb7\x57\x65\x3f\xfe\xbc\x59\x4c\x21\x9a\xc7\x0e\xd4\xb3\xa8\x44\x69\x43\xb9\xdd\x64\x07\x6d\xcb\x39\x98\xbd\x57\x95\xde\x79\x0a\x7a\x55\x45\x39\xe5\xc9\xb9\xa4\x7c\xbd\xf4\x6e\xba\xa9\xf7\xc8\xa9\x10\x34\x33\xcb\x48\x05\x3f\x50\xf5\x37\xd8\x0f\xf9\x4c\xf1\xed\x0e\xf4\xb0\xbd\x37\x3d\x4b\x15\xcd\x39\x4a\x78\x41\xbd\x76\x6e\xa3\x79\x96\x30\x72\x75\x85\xa2\x2e\x57\x34\x29\xf5\x6e\xa5\x71\x16\xd3\x29\x0f\x48\xff\x33\xa7\x53\x6a\x82\x97\x9c\x4e\xa7\xe2\xb7\xe0\x74\x8a\xba\x77\x98\xce\x3c\x85\x6f\xb1\xab\x83\x6f\x3a\x9d\x77\x9e\xae\x9c\x25\xb0\x60\xbe\x4c\xbd\x69\xce\x24\xd1\xcd\x44\xe8\x79\x07\x2e\xb1\x8e\x59\x5d\x5f\xa0\x0d\x14\x5e\xa8\xb3\x95\xb7\x45\xb0\x1d\x93\xc6\x95\xee\x8d\x83\x30\x82\x94\x27\xbe\xbb\xd6\x37\x60\x37\xf0\x89\x77\x1e\x6d\xf8\x83\x0f\x98\x2a\x36\x6d\x07\x21\x75\x2d\x62\x50\x86\x46\x36\x66\xec\x12\xe2\x4e\xf4\x55\x1e\x47\x79\xd3\xe3\xdb\x81\x71\x12\x52\x9a\xd0\xe6\x8e\xf4\xea\x4d\xcf\xb1\xf7\xd8\xe0\x69\x13\x87\x22\xfc\x67\xc6\xd5\x18\x94\x4a\x83\x8c\x19\x75\x57\xcd\x3a\x16\x0c\x83\x66\xa9\x74\x24\xb4\x22\x4c\x58\x8a\xb9\x8c\x84\x74\x4e\x88\x9c\x37\x24\xcc\x2e\x8b\x00\x61\x3f\x2f\xc7\x98\x45\xde\xa7\xf8\x41\x20\xcf\xb4\x00\x72\xf6\xc2\x70\x17\x24\x7f\x30\x95\x4c\xd4\xa1\xde\x00\x90\x1e\x0f\xba\x20\x5c\x1b\x4c\x59\x56\x9d\x0c\x24\x55\x80\x96\x99\xbc\x0e\xc5\x6b\x0b\xed\x74\x80\x45\xe6\x0d\x89\xba\x90\x3c\x86\xb3\x52\x88\x15\x9a\x1c\xf1\xca\x63\xce\xfa\xdb\xc1\x11\x9c\x97\x19\xd1\xd9\x65\xae\xe2\x04\xfa\x25\x15\xdd\x15\xa4\xf5\xab\x22\x9b\x75\x09\xfd\x0c\x0f\xd5\xd7\xa5\x64\x8e\xae\x13\xb3\x23\x3c\xc5\x20\x85\xc3\xee\x4a\x49\x80\x5d\x45\xc1\x69\x1f\x1c\xda\xe1\xb5\x5d\x9d\x4b\xb0\xf8\x82\x87\x9d\xa7\xcc\x94\xe6\x93\xe7\x78\x0b\x53\x40\x6f\x07\x54\xcf\x9d\x85\xeb\x76\x88\x0b\xac\x5b\xb1\x4f\x3d\xae\xdb\xc7\x75\x8b\x6e\xbf\x6e\xef\xb2\x3a\xc0\x42\x78\x1c\xa6\x4b\xaf\x0d\x27\x26\x8c\xa2\x81\x8b\xfc\x76\x70\xe4\xe5\x00\xaa\x07\x99\xc5\x01\xee\xca\x76\x9c\x98\x9d\xc8\xa1\xe9\xe3\x41\x3c\x65\x4b\x87\xb0\x85\x30\x9e\xa7\xc5\x99\x87\x18\xac\xa2\xec\x41\x90\x12\xef\x46\xc9\x8b\xfb\x52\x1e\x50\x20\x22\x71\x69\xc9\xe5\xe1\x3f\x8e\xe3\x14\xa3\x69\x78\x45\x64\x21\x47\xff\xc0\x13\xd4\x16\xd2\x90\x4a\x88\x4c\x0a\xf3\x91\x5d\x7c\x01\xd2\x29\x39\xe9\xa4\xf3\x7e\x8a\xff\x7b\x8e\xa3\xcc\xa9\x62\x40\xba\x68\xa7\x64\xf5\xd0\x47\xd1\xab\x1a\x54\x51\x32\x66\x65\xb1\xaa\x9f\xec\x6c\x2e\xac\x5c\x31\x92\xe4\x6a\x73\x46\x4a\x22\x7f\x30\x81\xd2\x7a\x3c\x3c\x43\xbf\x6f\xd0\x7a\xa7\x61\x6e\xe8\x12\xf9\x9b\x9b\x40\xbf\xe9\xb1\xf2\x5a\x40\x13\x45\xb4\x3d\x0c\x86\x43\x32\x81\x0b\x14\x20\x33\xc8\x72\xd5\xab\xd2\x7f\xdd\xea\x8f\xc3\x77\xbd\x63\xf4\xbf\xda\xab\x6b\x68\xc6\x80\xa6\x4c\x97\xe7\x82\x79\xf8\x65\x90\xae\x81\x9c\x3c\x0b\x86\x55\xfe\x94\x23\x1b\x1f\x06\xfc\xfa\x79\x9e\xf2\xd0\xf9\x22\x10\x0a\x33\x57\x86\xb8\xc9\x02\x8f\xa5\xec\xaf\x00\xb2\x7a\xfb\x4c\xd0\x72\x56\x72\xeb\xf1\x58\x08\x28\xe5\x3e\x12\x00\xa5\x22\x98\x25\x19\x14\x08\x67\xf9\xc0\xc7\x66\x71\xf8\x12\xe3\x4a\x7e\xc9\xeb\xb5\x8a\x11\x37\x4b\xbb\x60\x0e\x86\xe6\xe5\xda\xad\x19\x88\xa8\x46\x63\x9d\x6c\x28\xe3\xe5\x8b\x19\x32\x8f\x32\x41\x3b\xe0\x57\x64\x43\x8d\x18\xc1\x5a\x40\xe9\x8b\x17\x34\xe5\xb4\x88\xb0\xf2\x2f\xa3\x80\xab\x59\x7a\x2f\xc4\xdb\xb5\x43\x2f\xd0\x4c\x6f\xf0\x95\xd0\x0b\x44\x40\xd1\xb0\x90\xbe\x2e\xd6\x7b\xe6\xe0\x62\xbd\x07\xb7\x16\xed\xed\x42\xcc\x72\x91\x4a\xf3\xc3\x17\x48\xf6\xa3\xb7\x89\x42\xf4\xdc\xe7\x96\xaf\x42\xa7\x61\xee\x95\x37\x39\xd2\xab\x81\x1d\xda\x90\xb6\xef\xfc\xf0\xaf\x82\xae\xe8\x28\xb9\xcc\x10\x36\x87\x43\xf7\x20\xc0\x5c\x0f\xe2\x68\x10\x64\x1c\x66\x61\x0d\xcc\xc7\x68\x26\x18\x0a\x2c\xd9\x71\x30\xa4\x81\x8c\xd8\x42\xfd\x36\x5c\x66\x1e\x99\x7c\xe6\x9b\x70\x04\x68\xb6\xc0\x95\x3b\x94\x33\x59\x82\x8b\x0f\xbc\xc5\x99\x96\xb8\x58\x59\xc4\x10\x03\x16\x4d\x82\x34\x83\xe7\xc5\x6b\x5a\x8a\xd7\xa7\x25\x7d\x39\xbf\x40\xf5\x32\x75\x31\x3b\x63\xce\x60\x2e\x4f\x62\x2a\x38\xf8\x29\x46\x82\xdb\x30\xd7\xa0\xb2\x99\xd2\x6d\x73\x49\x3d\xff\x5f\x71\x11\xe4\x72\x51\x70\xdf\x2c\xb8\x6e\x15\xf2\xee\x81\xee\xcf\xe8\x7f\x3f\x1e\xe2\x1b\xaa\x1e\x3c\x11\xa7\x35\x7a\x29\x02\x27\x09\xa5\x3b\xbd\x37\x3d\x1f\x14\x36\x57\x37\x82\xbe\x08\x2c\x53\xd8\xb0\x21\x02\xc9\x7b\x08\x1c\xfc\x08\xd8\x00\x28\x86\x93\x06\x81\x13\x4c\x01\xb3\x8a\x71\xaa\xa3\x6d\x5b\x4d\xdc\x68\xde\x08\x4b\x18\x06\xd2\x89\xd6\x3f\xf6\x14\xeb\xc3\x7c\x1b\xc0\x9c\x00\x67\xba\x7d\xa8\xc3\x8f\x13\xe4\x66\x32\x02\x9a\x5a\x14\xe9\x8a\x5d\xf2\x7d\x0a\xb6\x9f\x1e\xfc\xe5\xc4\xda\x87\x01\xcb\x96\x94\x4b\xda\xba\x71\x89\xf7\xc4\x40\xa0\xc2\x96\x08\x1a\x0d\x38\x95\x1b\x77\x33\x6e\x69\x7f\xf5\xa7\xfc\xe6\x75\xeb\x95\x32\xfa\x69\x75\x69\x0c\x84\xaa\xc5\x73\x96\x79\x87\xf1\x0c\x05\x19\x9a\x60\xc2\x05\xe3\x88\xaf\x00\x96\xe5\x83\x5a\x82\xc2\x7e\x0d\x0c\xd7\xe6\x5b\x48\x9c\x6f\xa6\x61\x44\x8d\x44\xd9\x21\xde\x0a\x97\xa8\x3f\xb2\x4a\x74\xfa\x14\xfc\x29\x21\x4d\xc1\xfe\x98\x1e\x79\xc3\x0b\xf4\xe3\x8f\x4e\x7d\xbc\x19\xa8\xe3\xf0\x56\xba\x0c\x89\x89\xae\x4c\xf1\x9e\xcf\xcd\x66\x8b\x5e\x49\xfb\x45\x52\x29\x92\x08\x43\x69\xf6\xca\x41\xd0\xbc\xb9\xfb\x25\xe4\xd5\x55\x72\x90\xa1\xe9\xbe\x7c\x22\x17\xc8\xeb\xcc\xf4\x0b\x24\x70\xf8\xbd\x50\x07\xc1\xaf\xe2\xa9\x8d\xa0\xef\x94\x7c\xab\xcb\xf8\x87\x5b\x56\x0f\x8b\xb7\xb3\x3d\x90\xfc\x16\xcc\x00\x95\x8f\x5c\xed\x2d\xb2\xfc\xbb\xa3\xa5\x02\x98\xde\x31\xd9\xc3\x6d\x86\x82\x06\xf1\x64\x82\x29\xfd\xc7\x23\x2e\x1a\x80\xa8\x89\x21\x97\x5e\x9e\xe8\xa1\x88\xa2\x8a\x93\x37\xd9\x46\x93\xe0\x52\x79\xe5\xf4\x4b\x74\xbb\x7e\x50\x07\x74\x21\xa4\x14\xa9\x2d\x2f\x1e\x21\xc3\x03\xe3\x82\xb4\x3e\x59\x9f\x96\x39\xae\x0f\x50\x1a\x4c\x28\xf6\xf0\x03\x80\x81\x4a\x32\xa0\xe1\x47\x71\x12\x5e\x50\x59\x85\x73\x0c\x27\x40\x7e\x95\x2a\xe5\x7c\xc5\x72\xd0\x8e\xb5\x5a\x4c\xae\xb9\x4d\xcf\xf2\xe5\x9b\xc1\x18\x4f\x6f\x07\xd7\x2d\x70\x32\x95\x39\x58\x4c\x8f\x14\x78\x4e\x10\x34\x27\xe3\x8d\xcc\xd9\x48\x4f\x31\x54\xc4\xe2\x6f\x4d\x31\x6c\x10\x47\x17\x38\xc9\x34\x19\x96\x66\xbb\xe3\xc6\x94\x60\xf1\x49\xad\xff\xfc\x6e\xab\x87\xb4\x8a\xee\xbc\x2a\x5e\x16\xb4\x87\x59\xec\x62\xa5\xa3\xb6\xf8\x58\x27\xbc\x9b\x54\x7c\x0c\x3b\xd1\x20\x12\x49\xac\x66\x71\x9a\x86\xfd\x09\xf6\xaf\x58\x47\x53\xcb\x39\x37\xc9\x81\xb2\xed\x41\xe9\x37\x7e\x02\xff\xd3\x82\x82\x84\xfa\x9c\xac\xe0\xae\xf2\x5b\x3a\x3c\x39\x2b\x7d\xc1\xd7\x5d\xdd\x2f\xca\x59\xcc\xf0\x94\x72\x17\x22\xcb\xb8\x0b\xff\x5d\x50\x50\xac\xca\xae\xed\xce\xe5\xae\xc1\x44\x78\xd3\x32\xc1\x5d\x58\xc8\xf5\xfa\xd1\xf9\x5d\xef\x78\xcd\x5d\x41\x61\xe1\x2d\x77\x09\xb1\x70\x14\xa0\xf4\x5d\xf5\x60\x86\xa3\xe3\xe3\xf7\x56\xb5\xe2\xce\x64\xea\xf4\xbb\x05\xaf\x69\x78\xb5\x17\xe9\xe5\x0a\x9b\x1e\xd1\x55\x9c\x2e\xb7\x8c\x91\x77\xdd\xd8\xac\xc4\xf0\x0d\xf4\x70\x13\x72\xa8\xf3\x03\xe7\x06\xb6\xdc\x2b\x03\x76\x05\xf8\x1d\x8e\x42\x73\x8d\xe7\xc0\x81\x24\x60\x29\xcd\x00\x06\xd9\xe3\xb0\xf4\xa2\x94\x18\x47\x31\x7d\x63\x30\x40\x96\xb3\x1f\xe7\x71\x8f\xa2\x4b\x9a\x22\x2f\xae\xe9\xd8\xda\x7e\x8e\x56\x56\xdc\xbe\x15\xce\xf2\xd5\x2c\xa6\xf9\x86\x7c\xae\x1c\x0b\x6a\x79\x48\xd5\x4b\x98\xbc\xa2\x4a\x9c\x62\x6c\x7c\x56\x55\xb2\x04\xfa\xfa\x95\x92\xab\xac\x53\xe5\x93\x78\xcd\x8f\xbd\x96\x8e\xc6\x29\x27\x51\x2a\x5b\x74\xaf\x41\xdb\x81\xab\x0d\xf1\xd3\x7d\xbb\xc1\x7a\xee\x22\x4e\x17\x68\x56\x5c\xa4\x32\x86\xdd\x4b\x1f\xc4\xfc\xeb\x0e\xb1\xea\x02\xff\x92\x8b\x78\x33\x2f\x06\xf1\x74\x16\x64\xb0\xbd\x14\x5d\x86\xea\xb6\x60\x6c\x62\x8a\xf8\x53\x74\x4f\x74\x2d\xbf\xdb\x20\x77\x5f\x86\x83\x31\x6d\xfb\x98\x93\xb7\x87\x90\x15\xea\xf2\xf1\x46\x8d\xbe\x45\xf1\xc2\xdc\x77\x81\x5a\x46\x8d\xb4\xa4\x2d\x41\xf9\xc5\x15\xa8\x91\x88\xbb\x46\x05\xf2\xce\x75\x8c\x85\xfe\xda\x87\x58\x52\xdc\xab\x6a\xb9\x54\xa2\xd5\x58\xda\xfb\xd3\xda\x55\xbb\xd9\xa9\x77\x06\x6b\x90\xd8\xa0\xd3\xee\xb4\xda\xa3\xf6\xe8\xac\xcc\x55\xf1\x00\x9a\x3f\xc8\x7e\x78\xce\x91\x05\x50\xf0\x8e\x85\xe7\xf0\x25\xea\x4a\x46\x46\xc3\xda\x2c\xbf\xe7\xe5\xad\x31\xd5\x5f\x69\x59\xe1\x91\xaf\x13\x49\xa7\xb7\x5e\x32\x7a\xcc\x06\xbe\xa0\x6f\xb1\x86\xef\x37\x80\x83\x2d\x8c\x1a\x4b\x6f\x16\x24\x29\x2e\x69\x0b\x35\xe7\x62\x32\x49\x35\xc5\x8f\xac\xe6\xf4\x4a\x20\xc5\x11\x8d\xe1\xb5\x60\xd1\x51\xc2\xb0\x90\xc9\x53\xaf\xe6\x41\xe4\x97\x71\xca\x61\x98\x25\x85\xb0\xc0\x9d\xe0\x34\xa3\xb6\x0d\xc1\xc4\xb1\x40\x0d\x98\xa7\xb5\x33\xb4\xb1\x81\xe4\xda\x43\x3f\xfe\x68\xb6\x7b\x5a\x67\x65\xf8\x9a\xf4\xa9\xa0\xb6\xaf\xe8\x05\x86\xdd\x32\xd2\x39\x8c\xb5\xf8\x8d\x16\x99\x29\x4f\xa3\x82\x5a\xe5\x1c\xeb\xba\xf8\x82\x1d\xd1\xe1\x2a\x48\xc2\xb0\xcb\x5b\xf0\x67\xd0\x40\xcd\xbc\xb5\xb6\x8a\x6b\xb7\x3a\xf5\x4e\x31\x46\xe1\x3c\x1a\x79\x8e\x41\x15\xe5\x74\xa2\x8b\xe6\xb9\x77\x45\x7c\x11\x5e\x26\xc1\x6c\x06\x72\x64\x90\xb1\xe6\x55\x95\x09\x0a\xc8\x4e\x9f\x2a\x5e\x69\xb9\xab\x57\x73\xf5\xb1\x5c\xd9\xa4\xc3\x8f\xeb\x53\x51\x07\x92\x5b\x5f\xf6\x08\xa1\x87\xcb\xf8\x79\x52\x3d\xd7\x11\xa8\xbd\x65\x9d\xa5\x0e\xa1\xd1\x90\x52\x8d\x38\x60\xc8\x8b\x1d\xc7\xc1\x29\x2f\x44\x94\xe9\xbd\x08\x08\x75\x2d\x51\x4d\x99\xd8\xdc\xa0\x52\xec\xda\x81\xcc\x1b\xf3\xa6\xbb\x8b\x87\xaa\x54\x3e\x39\x8e\x3a\x39\xde\xe7\xac\x69\x6a\x83\xc2\x7e\x4b\xbf\xf3\xbf\x49\x0c\x17\xf7\x16\xb6\xf9\xd7\x6e\x60\x64\x59\xba\x35\x2a\xf6\xb2\x12\xfe\x95\xb6\x36\x42\x73\xb5\xf4\x9c\xc2\x1e\xae\x41\x19\xa4\xc6\x54\x27\x7c\xd3\xc6\x2b\x62\xb5\x79\xa4\x81\x1c\x65\x87\xc3\x39\xd6\xef\xc5\x7a\xbb\x10\x3a\x4b\x45\xcf\xd9\x76\xd9\xaf\x2b\xd1\x0d\x62\xe9\x7c\xe2\x0a\x80\xe6\xf4\x59\xb5\xc4\x12\xe9\x99\x21\x02\x24\xb0\xce\xde\x46\x32\xe9\x41\xff\x24\x4c\xb8\x02\xb6\xa0\x30\x7b\x23\xc2\x71\x85\x63\xae\x6f\x3f\x2a\xbe\x9d\xe6\x6d\xda\xda\xfe\x6a\x17\xe4\xaa\x45\xc7\x27\x42\x56\xa2\x6f\xd5\xf0\xc2\x51\x44\xd1\x11\x32\x7a\xb1\xcb\x50\xad\xa0\x04\x04\x17\xa2\x76\x31\xa1\x0f\x94\x25\xd9\x2b\x47\x61\x45\x17\x68\x5a\x58\x3b\x4a\x2b\x7a\x41\x42\x7a\x23\xc7\x71\xed\xa6\xf0\xb1\x85\xdd\x43\xa7\x62\xe2\x84\xe2\x4b\xbd\x96\x41\x0f\xb6\x3d\xa9\x04\x20\x76\x28\xe3\xa2\x49\x79\x84\xd4\xde\x7f\xc7\x7d\xca\x08\xd0\x22\x22\x1d\x7f\x83\xbd\x49\x46\x55\x5e\xcc\xa6\xb9\xf7\xbc\x83\x4d\x73\xb2\x63\x61\x14\x14\x8f\xfa\x5b\xb3\xec\xfb\x46\xd1\xdc\x97\xee\x71\x4b\xf1\xc6\x2e\xf0\x44\x18\xf8\x06\xbb\x0a\xd3\x38\x28\xaa\x05\x75\x31\x19\x80\xd5\x9d\x82\xdd\x7e\xc3\xf9\x55\x45\x5e\x72\x13\x57\x73\x8c\x53\xd8\x1b\x86\x3a\x79\xda\x26\xa6\x45\x5d\xa4\xc3\x22\xf7\x26\x85\xc9\x68\x0a\x1f\xe7\x36\x21\x9a\x58\x5a\x1b\xe3\x64\x6b\xe6\x58\xe9\xf7\x2f\xa0\x63\x0a\xd2\x74\x3e\xc5\x43\xfd\x3e\x31\x98\x24\x38\x18\x5e\x2b\xfb\x9d\x76\x20\x9b\x47\x34\x6d\x65\x81\x88\x66\xcb\xb1\x3d\x37\xff\x5a\xea\xd0\x44\x18\x17\x98\xa8\x27\x29\x5e\x9a\xd7\xfb\xf5\x45\xf3\x68\x59\x58\x7f\xa1\xc4\x6d\x91\x3c\x55\x21\x1d\x70\x2a\x40\x82\xf8\xdd\x3c\xe0\x93\xa5\x53\x52\x57\x0f\xab\xec\x4a\xe5\xcd\x62\xd7\xa8\x8b\x70\x41\x08\x1b\x6e\x13\x42\xd9\x93\xbd\x54\xcd\x8b\x0d\x94\xab\x1d\x65\xd0\x72\x94\xa2\x96\x66\xc2\x79\x43\xf2\xce\x6d\x22\xb1\xe8\xca\xe4\xcb\x70\x04\xf7\x25\xf4\xdf\xfc\xcb\x92\x45\x56\x18\xf6\x85\xc9\x3b\x0a\x9d\xb4\x52\xec\x9e\x64\x8b\x80\x87\x3b\x7d\xd2\x18\x59\xcb\x7b\xbf\x70\x85\xc1\x8c\xc5\x0b\x2a\xae\x8e\xe5\x35\x98\xe5\x05\x7b\x00\x39\x85\x34\x03\x80\xf3\xbd\x42\x64\xa0\x72\x4c\x6d\x2b\xc2\x88\x59\xf2\x32\x3b\x00\x66\x32\x73\x8e\x23\x30\xe6\xcd\x87\x26\xa2\x94\x7b\x80\xd1\xd0\xd9\xf9\xb0\x6c\x9d\x01\xa8\xb0\x14\x21\x69\x13\x75\x5a\x60\x72\x0c\x1f\xb8\xfd\xec\xde\x08\xc5\xd3\x90\xc8\x08\x15\x14\xd0\x4f\x97\xe1\x64\x82\xfa\x58\x34\x38\x44\x49\x10\x0d\xe3\xe9\xe4\xfa\x9e\x0e\xf7\xd4\x6a\x82\x0d\x53\x05\xed\xfd\x52\x81\x29\x25\x8d\x7f\x03\x2e\x44\x27\x79\x68\xb3\x20\x85\x1a\xab\xf8\x0a\x0f\xe6\x19\x2e\xad\xf0\x68\x54\x2b\x15\x96\xb8\xa3\xc2\xcc\xb7\x3c\x62\xd1\x3d\x41\xaf\xa0\x15\x32\x1c\xe4\xff\x57\xfc\x67\x66\x0a\x46\xe5\x6e\x9c\x9a\x2b\x9c\x44\x2b\x8c\xba\xa8\x62\xd3\x6d\xd4\x4f\xa7\x99\xcd\xb2\x47\x51\xfd\x83\xf7\x2a\xc9\x52\x22\x53\x38\xa5\x4e\x6b\xd5\x4a\x6b\xee\x70\xab\xa3\x4b\x5b\x59\xd7\xb6\xb4\x42\xe3\xcd\xd2\xc4\x03\x52\x81\x2b\x62\xdc\xc9\x34\xc8\x6c\x21\xdd\x94\xab\x2c\x91\xb7\x32\x1e\x80\xbf\x33\x60\x2d\xa1\xcd\x2c\x1f\x03\xb0\x9b\xb6\xd4\xe4\x22\x19\x34\x53\x90\xf3\x64\xb2\x7c\xcc\xd1\x4f\xb6\x3e\x5b\x4b\x0d\x2d\x53\x38\xbb\x9d\xa5\x8e\x98\x28\xb5\xe4\x61\x5c\x1e\xa9\x85\x14\x7d\x3b\xad\xb6\x4b\x33\xa0\xa9\xb8\x87\x8c\x2f\x73\x96\x67\xb0\xe4\x8a\x80\xe5\x11\xbf\x6e\xaf\x0f\x77\x44\x89\x13\x0a\x71\xf7\x37\x97\x86\xeb\x01\xf5\xe3\xef\xb6\x76\x6e\x10\xd9\x3e\xb9\x05\xa5\x6b\x17\x96\x52\x1e\x67\xb6\xf9\x5b\xdc\x52\x5a\x71\x47\x87\xfd\xce\x0f\x5f\x86\xa3\xae\xb2\x3d\x2b\x14\xb2\xa4\x7a\x9c\xb9\x54\x2d\xb3\x2f\x7f\x1f\xfa\xf2\x5c\xe9\xe0\x3b\x50\x47\xfc\x4d\xd4\xe6\x8e\xc5\x57\x48\x93\xbc\xc2\x87\xda\x17\x56\xf6\xe1\x1b\xae\xa0\x3f\x1f\x58\x83\x2d\xb7\xa3\x6f\xa4\x70\x30\x76\xd7\x38\xf3\x29\x77\x5d\xb2\x0b\x01\x4f\xc4\x16\x2e\xae\x28\xd8\xd3\xe1\x15\x32\x06\x7b\xa6\xdb\x9e\xcf\xbb\x93\x8a\xb1\xb4\x6f\x56\x97\xaa\xb0\xc5\x6a\x18\x54\x9d\x21\x09\xbc\x8a\x79\x4d\x5f\xe2\xbf\xce\x50\x03\x40\x58\xf3\xa3\xb7\xaf\xe8\xf1\x2d\x34\xf6\xc3\x2b\x9a\x0c\x04\x2a\x38\x87\x54\x39\x5b\x53\xc3\x4c\x0d\xba\x4f\x6f\xe2\x3c\xf1\xdd\x41\x1f\xfc\x17\xf0\xe3\x7b\x56\x10\x7f\xef\x8c\xf9\x7b\xd4\x13\xbb\x98\xe1\xb2\x8a\xe2\x3b\x31\xc6\x7b\x47\xd1\x56\x14\xdf\x17\xe3\x2e\xa8\x27\xfe\xe6\xbc\xfb\x9b\x2b\x8b\xbf\xfd\x56\x51\xd1\x6c\x7b\x3c\x27\xb4\xfb\xdb\x3b\x0a\xe9\xc3\xfd\xf7\x17\xae\xad\x43\x1d\xdf\x82\xbb\x47\x9e\x82\x5c\xaa\xf2\x44\xa6\x4b\x35\xa5\x25\xcb\x5f\x79\x73\x56\x69\x37\xbf\xd7\xa4\x94\xf7\x9e\x83\x72\xd9\xdc\x93\x5a\xce\x49\x0b\x31\x3b\xfd\xa4\x91\x76\x92\x57\xf4\x24\x9e\x04\xfd\xa8\x04\x2e\x7e\xea\xc9\x27\xf7\x83\x6c\x5c\x41\x8e\x14\x94\xf2\x78\xfd\x3e\x1e\x04\x13\x34\x8b\x27\xd7\xa3\x70\x82\xe2\x11\xa2\x9b\x16\x3b\xc5\x3b\x8e\xbc\x2c\xb6\xfd\x86\x5e\xd0\x68\x58\x63\x4c\xe2\xf5\x0e\x79\x7f\xf3\xca\x8e\x1d\xa4\xd8\x5a\xf6\x3f\x5b\x4c\x0d\x6c\x04\xe7\x7d\x32\x83\x26\x11\xef\x54\x67\x49\x9c\xc5\xe4\x13\xda\x20\xa7\x0f\xb3\x00\xab\x87\x36\x50\x84\x2f\x09\x02\xf9\x10\xa2\xf9\x64\xe2\x59\x28\x02\x03\xb9\x4c\x94\x78\x47\xae\x48\x9e\x7c\x4e\xf2\x95\xdc\x5e\xc5\xf6\xfb\xb0\x9f\x04\xc9\xf5\x22\x1d\xb9\x92\x1f\xd4\x0b\x0a\xb2\x85\x32\xad\x27\x11\x2e\x78\x97\x83\x09\x0a\xa3\x31\x4e\x42\x2d\x80\xab\x16\xd1\xc1\xcc\x33\x6a\x47\x18\xb5\xa7\xb3\x40\xd8\x3f\x1e\x63\x18\xdc\xe3\x84\x9f\xc1\x38\xc8\x38\x42\x2c\x94\x07\x15\x83\xac\x53\x25\x42\x79\x71\x00\xb9\xdc\x15\x5f\xe0\x24\x09\x87\x38\x45\x87\x54\x21\x12\xe2\x94\x32\xf0\xd9\x35\x0a\x23\x96\xcd\x58\x22\x50\xa0\x05\x33\x57\xc3\xc9\xb2\x00\x2c\x99\xcb\x53\x6e\x99\xa8\x81\x64\xa2\xf6\xaf\x4f\x28\x09\x6b\xd2\x4d\x8e\x49\xa2\xea\x2f\x16\xe2\xc9\xb0\x8b\x56\x20\x53\xd6\x8a\x69\x38\xe2\x6e\x93\xfc\x4d\x71\x36\x8e\x87\xb9\x3e\xf2\x4a\x69\x33\x46\xbe\xcb\xf1\x0c\x21\x3b\x9c\x21\x45\x5f\x33\xc8\xe6\xf3\xea\x0d\x62\x38\x0b\x2e\x23\xfb\x8b\xc2\x48\x88\xb0\x20\xd3\xea\xf9\xcc\x89\x37\xe7\xe7\x53\x1c\x39\x4c\x87\xc9\x8e\x92\x8f\x05\x92\xcc\x87\x9d\xbb\x64\x79\x67\xfa\x07\x27\x02\xcc\x4c\x8a\xbb\x7e\x85\xc2\xb1\x34\x71\xe3\xf4\x03\x6f\x72\x1c\xa4\x07\x97\x11\x23\xfb\xeb\xd2\x0a\xa9\xb9\x52\x16\x3e\x4f\xe4\x11\x36\x41\x5e\x9e\xbc\x58\xd8\x0f\x5a\x2b\x77\xba\x1d\xb5\xfe\x9f\x74\x3e\x23\xa2\x56\x14\x66\xd5\x80\x08\xa7\x6c\xeb\x0b\x92\xf3\x39\x19\x5d\xe7\x78\x20\x47\x06\x85\x9c\x71\x92\x1e\xb7\xc9\x4a\x8a\x24\x47\x0f\xa9\x52\x98\x4f\x3a\x5d\xa5\x36\x04\xb5\x83\xda\x7e\xe0\xd9\x76\x10\x57\x8c\x8f\x70\x82\xa3\x01\x69\x00\xc6\x79\x66\xae\x57\x6b\x18\x98\x5c\xec\x02\xe8\xdd\x67\x90\x2b\x35\x86\x8b\xa9\x6e\xc3\x4a\x49\x55\xa6\x49\x55\xde\xf3\x88\x8e\x03\x4c\x20\x5d\xb5\x76\x08\xd4\x4d\x3e\x1f\x32\x83\x4d\xa9\x2c\xae\xe1\x88\x28\x0d\x21\xe5\x00\x48\xa9\xfc\x77\xe6\x95\x3c\x62\x39\xda\x60\x6c\x93\xdf\x59\x2c\xe4\x45\xb4\x5c\x3e\xc7\xb3\x1b\x81\x25\x27\xe3\x64\xdb\x2b\x97\x47\x50\x57\xd6\x08\x7f\xa7\xaf\x13\x2f\xd5\xf0\xe2\xb7\x21\x9b\x3c\x77\x75\xcf\x5c\xa1\x03\xc6\xcc\x58\x92\x00\x20\x29\x30\xa1\x1f\x0e\x51\x1a\x4f\x31\x4d\x3d\x85\x2e\xc7\x38\x42\xd7\xf1\x3c\x11\x66\xf6\x01\x11\x67\x29\xf0\x7b\x8e\x9d\x7b\xd7\x5d\xd0\x74\x74\xce\xdb\xcb\x10\x65\x00\xd5\xaa\x3d\x32\x62\xe8\x6f\xb9\xdd\x2d\x44\xa3\xd0\x9c\xf6\xe2\x19\x11\x76\x66\x52\xee\x61\xf2\xce\x1d\xc4\x29\x05\x18\x68\x98\x34\x99\x6a\x0a\x9a\xc8\x7b\x9e\x52\xb6\x3a\xe9\xfe\x59\x54\x7e\xb9\xe5\xb8\x43\x23\xda\x25\xb6\xe8\x9f\x73\x8d\x8b\x88\x87\xfc\xb2\xed\x43\x30\x05\xa3\x89\x05\xf5\x10\xdb\xaa\x65\x31\x73\xb3\x56\x01\x96\x73\xb7\x58\x32\x9d\xa7\x6a\xf1\x33\xb4\xa1\xb4\xaf\x7f\x5a\x22\x75\x91\x67\x93\xdd\x46\x97\x71\xb4\x92\x51\xf9\x99\xbb\x3b\x2a\xc1\x0b\x27\x71\x3c\x43\x41\x3f\xbe\x70\x6c\x83\xf9\x5d\x5e\xe1\xd0\x56\xfc\x1d\x06\x2e\x2a\x5a\x55\xfb\x29\xde\x16\xc8\xab\x55\x68\xf1\x88\xc3\x09\xf4\x14\xec\x5f\x96\x59\x37\xae\x8d\x6f\x30\x89\x23\xfc\x00\x1c\x0f\xe0\xa2\x0d\xb9\x87\xc0\x8b\x02\x3b\x19\x29\xb6\x70\x23\x53\x73\x91\xe8\xc2\x11\xe7\xa7\x4e\x7b\x32\xf7\x19\xd9\x79\xbb\x1f\xa1\x00\x3c\x6f\x8d\x58\x84\xb9\x91\x85\xac\x38\xef\xf9\x20\x5c\xe1\x69\x84\xf1\x83\x1e\x0e\x31\x0d\xcf\xa3\x70\x14\x0e\x82\x28\x63\x01\x25\x43\xda\x7b\x00\x49\xdb\x71\x1d\x93\x7f\x55\x3c\x88\xe9\x59\x59\x7d\x73\x0f\x61\x63\xec\xe6\x4d\xb2\xf0\x84\xc1\x57\x4d\xaf\x16\x8c\x35\x72\x9a\x85\x89\x91\x32\x6e\x30\x16\x0e\x1a\xbe\xb7\x54\x2f\xaa\x7f\xb6\xb6\xb1\x5b\xb6\x30\x1e\xed\x7f\x71\x00\xa7\xb5\xab\x5a\xad\x56\xaf\x35\x6a\xcd\x0a\xaa\x5d\xd5\x5a\xb5\x76\xad\x53\x5b\x3b\x7b\x30\xc0\x15\xd4\x29\x1c\x7a\x85\x85\xaf\xe3\x33\x62\xad\xd8\x4b\xe6\x10\x0c\xcb\x95\x3f\xd0\x7f\xbf\x7e\x85\x98\xbd\x86\xa8\x31\x42\x25\x31\xbd\x3f\x6c\x38\x14\x85\xea\x1f\x40\x55\x8c\x86\xf8\xcf\xc2\xc6\xa4\x26\x00\x4a\x1e\x13\x1c\x9d\x67\x63\x6a\x7a\xe4\xe5\x22\xc5\x63\xc6\xc8\x85\xb2\x5c\xa4\x98\xed\x68\x10\x0f\x09\xbd\x63\xfa\xc3\x24\x77\x78\x9d\x1f\xfb\x53\x10\x00\x8e\x06\xd5\x5d\x7c\xe5\x6f\x73\x51\x00\x99\x42\xab\x7d\xe9\xe0\x2e\x92\x58\x0b\x44\x76\x71\xc4\x35\x58\x14\xd6\xc5\x51\x45\x1b\x92\x8f\xd9\x68\x7d\xa9\x68\x2e\x6c\x2a\xbc\xb1\x5c\xf8\x54\x7d\xfd\x8a\x76\xf1\x55\x6e\xf8\x96\x05\x04\x34\x08\x32\x1c\xb1\x3d\x5f\xa7\x20\x0f\xf3\xf7\x13\x92\x72\x0f\x2b\x07\xfc\x84\x71\x43\x85\x32\x21\xcd\xef\xb2\xf7\xba\x45\x71\x29\x42\x1b\x02\xbb\x3a\x8f\x9f\x21\xde\x34\xfc\x29\xcd\xa0\xa4\xc9\x94\x68\x60\xe7\xe5\xc2\x91\x90\x81\xfd\xd5\x62\x58\x0e\x5f\xc5\x6c\x1c\x88\x50\x07\x92\xc4\xfc\xa5\xc3\xf4\x58\xf2\x18\x8d\xe7\x78\x80\x1f\xeb\x2c\x89\xc2\x97\x75\xac\x4e\xf5\x26\xc1\x74\x86\xf0\x15\x44\x92\xec\x87\x66\xe7\xe8\xbd\x2a\x29\x63\xdf\x36\xd0\xfb\xd4\x81\x2b\x48\x8a\x86\xf8\xbf\x3c\x81\xd2\xa1\x3e\x11\x49\x23\x0c\x5b\x2d\x0a\x32\x14\xa0\x2c\x9c\x3a\x24\x6e\x57\x48\x76\xb5\xbb\xfe\xa4\x10\xea\xe0\x90\xa2\x68\x83\xa0\xc7\x66\xe1\x34\xe4\x51\xb1\xc9\x3f\xa5\x46\x0b\xbd\x40\xa5\x90\x62\xfc\x13\x5a\x2f\x97\x45\xb4\x6c\xaf\x14\x4f\xe1\xe8\x3d\x7e\x8e\x42\x11\x6e\xfb\xeb\x86\x6c\xfa\xf5\x6b\xde\x86\xa3\xbc\x68\xb4\x80\xe0\xef\xdd\x96\xd4\x31\xa5\x8b\xeb\x4e\x63\xea\x8f\x72\x5f\xb4\xfb\x1b\xc8\x1e\xec\x22\x19\x83\x6d\x2a\x14\x9b\xed\xf3\x0d\x1d\x4d\x57\x8e\x95\x20\x8c\x82\xbe\x79\xf2\x50\x0e\x00\x45\xd9\x29\x8d\xc1\x41\x84\x40\x4d\x30\x0c\xb3\xbb\x8a\x82\x72\x71\x8a\xd5\xe5\x61\x52\xe4\x73\xd1\xd0\xbd\x0e\xd6\x64\xcb\x51\xae\xb8\x48\x5e\x26\xe3\x66\x18\x0e\x51\xed\x54\xc0\xe0\x71\xe6\x37\x60\xe9\xd0\x3f\x20\xfd\x66\x83\x90\x7e\xaa\xf1\x05\x07\xc1\x6b\xa2\xd4\x06\xda\x0f\xb2\x71\x75\x80\xc3\x89\xac\xb9\x8a\x96\x88\x48\xe4\x3e\xff\x16\xda\x79\x3c\xe6\x48\xd6\xf1\xf7\xb6\x76\x9f\xec\xb8\xab\xd2\x82\x75\xde\xd5\x69\x61\xd1\x39\x57\x05\x0b\x27\x35\x8a\xab\x1a\xfd\xdc\x3e\x39\x57\x6d\x1a\x61\xe6\xf7\x35\xaf\x49\x1d\xa9\xb7\xfc\x14\x28\x62\xc3\x28\x9c\x4c\x78\xd8\x59\xe6\x26\x01\xe7\xad\xc5\x42\x09\x3f\xcc\x45\xae\x43\xaf\x0a\xca\xeb\xe2\x53\x68\x96\x19\xa4\x42\x84\x72\x5f\xc6\x67\x05\x8e\x60\xcc\x15\xa4\xee\x3f\x69\xd1\x12\x2a\x99\x44\xee\x23\x96\xca\x1e\xec\x03\x15\xf9\x9a\xe8\x37\xe4\xd3\x4f\x97\xfe\x28\xf3\x9f\x2e\xd1\x06\xf9\xaf\x27\x81\xda\xf4\xd3\x1f\x64\x9b\xb9\x6a\x06\x43\xdc\x59\xef\x9b\xe1\xd7\x45\xb1\x20\xfd\x82\x54\xce\x91\x73\x4f\x50\xe0\xee\x8e\xb6\x5a\xaa\x5d\xbd\xac\x75\x5e\xa2\x9f\x48\x17\xfe\x80\x3d\x7d\x67\x67\x67\xa7\x8c\x9e\xd3\x17\x3f\xff\x8c\x6a\x57\xf5\x1a\x6c\xf7\x04\x01\xcf\x76\x4f\xbb\x58\xaa\x5d\xb5\x3a\xed\x1a\x05\x76\x69\x02\xbb\x2c\x0a\x0c\x86\x17\xa7\x73\xf0\xf4\x29\x01\x1a\xaf\x5f\xd3\x9a\xe8\x39\x82\x91\xce\xad\xcf\xea\xae\x6e\x40\x1d\xf6\x97\x5f\xf6\xf9\x06\xaa\x55\xdb\xde\x32\x30\xa6\xac\xe8\x4f\xd4\xde\x86\x53\x5b\x19\xfd\x8c\xaa\x6d\xf4\x1f\xa8\x8e\xba\xe8\x45\xbd\x88\x88\x62\x71\x0e\x5d\xdc\xa8\xa0\x64\x10\x0c\xc6\x98\x65\xd7\x59\x2c\x70\x90\x9a\x9f\x08\x3d\x26\xa5\x12\xad\x4a\x8e\x4a\x1a\x92\x64\x37\x51\x06\xc3\x7d\xc5\x44\xab\x6e\xa0\x4f\x49\x89\x96\x07\x82\x5c\xeb\xaf\x39\xfa\x74\x29\x73\xf8\x94\x44\x79\x09\x1f\x7d\x45\xb5\x82\x61\xcd\x23\x7c\xa9\x38\x3b\xc1\xad\x23\x53\x80\x44\x3c\x7d\xcf\x13\x63\x24\xdd\xce\xa7\xec\x68\xbf\xc8\x90\x06\x47\x03\x30\xa4\xa1\xff\xba\x0d\x69\x76\xf1\x95\xad\x09\x70\x81\x23\x05\x37\x28\xd0\x2a\xfd\x5d\x2c\xfe\xa6\xa9\xbe\x18\xe3\xab\xc2\x2a\x8c\x02\x27\xcf\x25\xa3\x6a\x16\x6a\xfd\xbe\x18\xf9\x18\x5f\xd9\x21\x34\xd9\xf8\x29\x47\xfb\xc5\x89\x84\x9c\x81\x33\x6f\x7b\x4c\xbd\x2c\x7c\xf2\x4c\x97\x3d\x46\xd2\x59\xb7\x01\x8d\xf1\x55\x6f\x1c\x24\x85\xf3\x6c\xa5\x0b\x0f\x74\x90\x23\x2d\xa4\x07\xb9\xcb\x3b\x1e\xe2\x38\x76\x6c\x8d\x03\x58\x02\xa4\x55\x96\x6a\x9f\x7a\xa7\xec\xe2\x77\xae\xaa\xa4\x9d\xda\x28\xbf\xae\x87\x41\x08\x70\x9f\xe3\x30\x2a\xad\xac\xdc\x22\xe2\xa6\x42\xe1\x74\xbd\x2d\xa3\xe9\xe1\x2b\x85\x12\x6e\xf1\x05\xe3\x11\x9e\xfe\x7a\xa9\x89\x2f\x36\x6a\xb3\x2d\xd6\x63\xf1\x48\x99\xb4\xca\x72\x89\x52\x68\x9d\xf7\xfc\xe8\x42\x1f\xd9\x51\x66\x99\x55\x73\xb9\x4c\x6a\x3a\xb5\x51\xb6\x85\x36\x72\xf2\x63\xd2\xd5\xd2\x04\xcd\x04\x74\x7a\x2f\xca\x58\x67\xab\xe9\xbc\x9f\x66\x49\x29\xac\xa0\x46\xb9\x02\x49\xf8\xa4\xca\x82\xac\xa8\xf5\xb2\xcb\x01\x77\xe9\x3d\x4f\x1b\xa6\x55\xd4\x28\xea\x3e\xfb\x3e\xc8\xc2\xa8\x5e\x6c\xd3\x62\x65\xf9\xbe\x25\x1e\x6f\xb7\x75\xb1\xea\x7f\xdd\xee\x55\x14\x81\xfb\x5a\x53\x13\x68\xcf\xbd\x87\x51\x5c\xfe\x47\x6d\x63\x74\x38\xbe\xe3\x9d\x4c\x41\x90\xee\x48\x74\xea\xaa\xa3\x24\x9e\x92\xb7\xbd\x78\x88\x61\x93\x2a\xba\x21\xa9\x00\xef\xb0\x27\x69\x74\x7b\xfb\x6d\x49\x90\xe3\x52\x8b\xe1\xbb\xde\x9c\xd8\x2a\xa2\xfb\x93\xba\xdc\x8a\x6f\x51\xa2\xd6\x72\xbb\x94\xa8\x26\x36\x2a\xf1\xe6\xa1\xf7\x2a\xa3\xe9\x45\xb9\x9c\x43\x45\x8b\x2e\x7b\x5b\x1d\x30\x82\xde\xcc\x4a\x21\x5f\x13\xe6\x56\xe5\xd6\x2d\x2e\xbd\x55\x19\x08\x17\xdd\xa9\x3e\x9e\xec\xbc\x58\x2f\xb6\x51\x7d\xcc\x46\xeb\x62\x9b\x62\x0f\xb7\xdb\xa4\x68\xa3\x7f\xdd\x1e\x55\xb0\xfd\xfb\x5a\x59\xf3\x6c\xb4\xee\xde\xa0\xc8\x28\x3e\xe4\xf6\x94\x25\xd7\x39\x06\x46\x43\x4c\x8e\xe8\x1f\x8f\xf6\x7a\xdc\xd3\xa9\x84\xd3\x41\x30\xc3\xa5\x9c\x8d\xd3\x66\xcb\x68\x10\x64\x83\x31\x2a\xd9\xe9\xa3\x01\x85\x71\x12\x5f\x02\xdd\x42\xc6\x95\xd2\xca\x7e\x30\x19\xc5\xc9\x14\x0f\xd9\x34\x0c\x83\x2c\xb0\x53\xd0\x2d\xcf\xc0\xd5\x49\xbd\x3d\xff\x66\x73\xb5\x0c\x99\x7c\xd7\xcc\x1b\x28\x8c\xb2\x6e\x49\x86\xc5\x19\x37\xab\xe3\x33\x06\xd0\xb6\x86\x79\xc4\xa8\x87\x5a\x08\x68\x74\xc5\xe1\x94\x0b\x07\xa0\x11\x29\x78\x21\x17\x26\x1e\xb2\x6c\x66\x8a\x17\xba\x37\x13\xaf\x62\x27\x7b\xad\xa4\x44\x9b\xce\xd3\x0c\xf5\x31\x0a\xc9\x88\x4e\x71\x94\xd1\x3c\x6b\x01\x5c\xaf\x27\x38\x13\x1e\x0b\x85\x72\xfb\x1a\x79\x3a\x75\xe5\x3e\xcd\x71\x48\x5d\xab\x64\x82\xf8\x2f\x78\x96\xa1\x79\x34\xe3\x49\x03\xf5\xec\xa0\x8a\x4d\x4b\xcd\xc1\x7d\xdf\xb0\x71\x80\x4c\x83\x9b\x62\x14\x84\x97\x98\xef\x73\x41\x33\x38\xc8\xee\xca\xac\x79\x8c\x91\x5e\x61\x49\xb4\x59\x12\xd3\x2c\x46\x61\x96\x72\xaf\x18\x44\x28\xf8\xae\x77\x4c\x7d\x27\xf2\x34\x21\xae\xff\x92\xa9\x50\xd6\x5d\x66\xde\x87\xc0\x4a\xd9\x65\x33\x00\x19\x38\x99\xa7\xa2\xb1\xb3\x9a\x4c\x89\x96\x8f\xb6\x82\x2c\xe0\xc2\x7a\xad\xa8\xa4\xb9\x39\x1c\xa6\xd0\x06\xcf\x0b\xee\x19\x69\x46\x0b\xc5\x37\x45\x11\x64\xc1\xca\x3c\xce\x8c\x5d\x10\x5d\xf3\xcc\x09\x80\xf2\x4b\xea\x53\x12\x28\x16\x94\xd4\x9e\x18\x38\xde\xc3\x4c\xe6\x27\x8a\x4e\x69\xc5\xe6\xf7\x85\xea\x2d\xde\x1b\x59\xc9\x22\xc9\xcc\x6d\xf7\x7a\x99\x8e\x4e\x0d\x28\xaa\x0c\x10\x0b\x26\xaa\x83\x52\x7d\x9c\x81\x8c\x16\xc4\x89\x64\xb4\xa6\x30\x65\xc0\x70\x71\xa4\xb4\x4d\xe8\x9a\x8f\x7c\xb9\x29\x91\x0b\x98\x45\xb4\xcf\x37\xf4\x24\xe9\x45\x29\x98\xe7\x3a\x4d\x51\x70\x11\x84\x13\x88\xd8\x45\xf9\x02\x30\x3b\x3f\xd5\x9c\x28\xce\x2a\x61\x74\x11\x7f\xc1\xa9\x99\x64\xb8\xc4\x92\x03\x57\xd0\xe5\x38\x1c\x8c\x9d\xac\xba\x7f\x9d\xc3\xaa\xed\x56\xf9\x42\xe9\xc7\xf1\x04\x07\xd1\x0d\x1a\xc6\x3b\x93\x79\x3a\x46\xbf\x8e\x71\x46\xe3\x99\xf0\x5c\xb4\xe0\xae\x35\x0b\x12\x60\x14\xec\x95\xe4\xda\x82\x5d\xdf\x22\x1c\x88\xe0\xf4\x30\xe2\x77\xdf\xe6\x05\xc0\x2d\x4a\x48\xbe\x35\xc3\x53\xe5\xfa\xe2\x72\x2c\x09\xc6\x9d\x29\x58\x8f\xb5\x4a\x8b\x6a\x8b\x8f\x0e\xf8\x92\x3a\x13\xb6\x44\x24\x71\x3b\xb4\x25\xe4\x35\x37\x4e\x83\x91\xf5\xa9\x55\xc8\x47\xc5\xd0\xcc\x47\xf7\xbc\xb8\x94\x15\x36\x8c\x94\xcc\x79\x85\x39\x74\x59\xdb\x1d\xd1\xaf\x17\xcf\xa3\x8c\xd3\x97\x83\x99\x10\xa0\x11\x4d\x24\x7c\x04\x71\x8b\x37\x74\xfc\x57\x8d\x26\x5f\xd9\xbc\xc8\x37\xe4\x0c\x83\xa3\x78\x1e\x0d\xd1\x7c\x46\x1d\x0a\x07\x93\xf9\x10\x1b\x74\x6f\x57\x33\x30\x92\x46\x2e\xea\x87\xe2\xb1\x6d\x05\x16\xc3\xf8\x32\x52\xf1\x88\xa3\xc9\x35\x1a\xcd\xc5\xa2\x74\x44\xd2\x5f\x5d\x45\x13\x9c\x52\xa7\x4a\xb7\xac\x05\x7c\x23\xc1\xd3\x20\x8c\x74\xe1\xaa\x58\xbf\xa6\xc1\x55\x49\xeb\x17\x5c\x9c\xa2\x17\xae\xcc\xec\x95\xc5\x57\xaa\x62\xce\xa9\xe6\xc1\x37\xe5\x40\xc9\x1c\x0f\xad\xf5\x9f\x90\x42\x80\x3e\x7a\x02\xda\xf0\x92\x13\xf9\xaa\xf7\x31\x8c\x4a\x6a\x93\x3f\xa1\x56\x45\xa3\x33\x97\xf9\x24\xcf\xe0\xed\x22\x12\x42\x77\x0a\xc0\x7c\xb7\x2d\xca\xe7\xa9\x9a\x85\xfd\x7e\xad\x8e\x80\x78\xfb\x5c\x59\x4f\x5e\xa3\x09\x82\x19\x4e\xc8\x69\x52\x6c\x0c\x2f\xe4\x01\x01\x9c\x21\xdd\x15\x19\x77\xd1\xf7\x20\xc1\x55\x5c\xb9\xea\x7d\x73\x8c\xb4\x14\x58\x92\xe1\xc3\x94\xdb\x45\x35\xee\xab\xb2\x30\x33\x19\x96\x3a\xa2\x0e\x34\x34\x4e\x86\x5e\x6c\xa8\x33\xbd\x98\x2a\x79\x6c\xd1\x3c\x6c\xfd\x0a\x27\x1d\xff\x8a\xda\xf4\x5d\x8d\xdd\x0a\x67\xa1\xcc\x75\xf2\xba\xa3\x95\x9b\x67\x37\xfc\x8b\x4c\xde\x3e\x59\x1b\xa2\xc4\xc4\x39\x63\xb9\x16\x6f\x3a\x0f\x13\x27\x4d\x4f\x26\x7a\x7e\x06\x1f\x07\x29\x64\xc8\xf5\x9e\xb8\x17\xa6\x22\x97\xec\x5a\xf5\x81\xa2\x93\xce\xa0\xd3\xb0\x6b\x38\x45\x71\xa4\x1c\x85\xeb\x1d\x54\x6a\xd7\x1b\x60\xc9\x5a\x76\x1c\x8b\x77\x69\x65\x7e\x0c\x16\x8f\xee\xf3\xf0\xbd\x44\x7d\xcd\xcb\x40\x96\x1b\x30\x35\xcf\xd5\x8c\x0e\xc2\x12\x39\xc9\x6f\x1b\xdd\x8e\x34\x84\x68\x88\xe4\x45\x41\xee\x0a\xdb\x90\x88\x39\xd0\x42\xb7\x1d\xef\x6e\x36\xda\x1d\xb7\x93\x58\x5e\xaa\xeb\x5b\x47\x58\xe3\xb1\xd5\x8a\x87\x59\x3b\xc6\x22\xbc\x87\x5f\x43\x60\xab\x21\x16\x58\x62\x4b\x4d\x0a\x5f\x38\xf7\xaf\x32\x61\xf4\x72\x1f\x2a\x12\x40\x58\x55\xf1\xe8\x25\x3c\x2b\x09\x40\x6b\xcc\xcb\x96\x1a\xcc\xbd\x99\x0d\x87\x63\x63\xe6\x1b\xf2\xd1\x72\x63\xfd\x71\x36\x04\x96\xa1\x0e\x36\x4d\xcb\x5f\x3c\x63\x9f\x37\x82\x30\x05\x6e\xc6\x11\x2e\xec\x42\x44\x59\x11\xf3\x1f\x5a\xb8\xbc\x97\x98\xf3\x39\xe0\x55\x5a\x61\x48\xb9\x74\x29\x7a\xc9\xc5\xaa\x13\x5a\x50\x25\x14\x6d\x0c\x3c\xeb\xd1\xa3\x91\x60\x0a\x1b\x1d\x82\x83\x3c\xd8\xf8\x12\x21\x9d\xe0\xeb\x02\xa5\x9c\x63\x6d\xf1\xf7\xde\x7c\x27\x76\x58\x92\x9b\x54\xe0\xe2\x65\x90\xe8\x43\x0c\x28\x07\x19\xcd\x17\xcf\x6a\xca\x98\xa1\x28\x4c\x11\x1e\x8d\xf0\x20\x0b\x2f\xf0\xe4\x1a\x05\x68\x88\xd3\x2c\x99\xc3\x73\x05\xe4\xf4\x17\x71\x34\xc0\x85\xa2\x8c\x16\xa4\x50\x2d\xd1\x03\xa0\x24\x03\x72\x43\x89\xe5\x35\x17\x64\x10\xee\x69\x67\x40\x1b\x9c\x1c\x45\x32\x21\x8f\x5a\xc2\x53\x3a\x8f\xd0\x73\xaa\x2d\xa6\x7a\x5e\x74\x29\xba\xdf\x71\x8c\xaf\x7d\x20\xca\x07\x83\x16\xad\x95\x45\x02\xfc\x12\x9c\x55\x19\x21\xce\x64\x77\x94\x79\x70\x2e\x1e\x52\xde\xb7\x78\x94\xe4\x77\xed\x7a\x63\xb5\xd9\x28\x26\xe6\xa7\x4c\xe3\xa3\xc5\xbf\x0f\xd8\xa4\xad\x88\xc0\x49\x61\x94\xe1\x64\xa4\x58\x0b\x23\xef\xaa\xe0\xfc\x95\x75\x9d\x53\x2d\xdd\x6e\x59\x7c\xc4\x00\x8d\xf1\x64\x86\x13\x22\xfe\x14\x58\x04\x3b\x0c\x37\xe6\x1b\x6c\xa2\xfc\x0d\xee\xf1\xa8\xcc\x64\x3a\x55\xd0\xae\x56\x3f\xd1\x5e\xed\x42\x97\x4a\x2e\x61\xcb\xaf\x9f\x53\xab\x6a\xc6\x83\x00\xda\x77\xbf\x67\xad\x0b\x77\x00\x5c\xa4\x9f\x17\xd9\x4a\x84\xc3\xa2\x9e\x45\x4c\x66\xb8\xd4\x29\x7c\xf9\x63\xa3\x93\x9e\x08\x4b\xde\xdd\xdf\xec\xdd\x3f\x3d\x11\x11\x9a\x07\xa5\x20\x2d\x30\xba\xfa\x5b\xd0\xd4\xee\x34\x18\x14\xa2\xab\x69\x30\xb8\x0b\x6d\x89\xea\x77\xa2\xaf\x2f\xd8\xad\x42\x52\xe8\xab\xf7\x09\xd0\x22\xf3\x40\x89\x8c\x36\x42\xeb\x2e\x47\x6c\xb9\xc7\x5f\xa1\x49\x5a\xe0\xc3\x40\xb0\x01\x27\x06\xf6\x43\x7a\x31\xf0\x4c\x2d\x10\xd2\x77\x3f\xc8\xc6\x34\xac\xef\x13\xfe\x9e\x0d\xf3\x2b\x19\xe9\xf7\xe6\xac\xd2\x6e\x7d\xaf\xe1\x7d\x19\x32\x25\x1e\x8e\xb8\x7c\xef\xf1\x7e\x39\xe4\x65\xe3\xfe\x0a\x0c\xd5\xf8\xbf\xbe\xa0\xbf\xe2\x3b\x04\xff\x75\x05\xd0\xb5\xaf\x28\x78\xd4\x58\x39\x65\x0a\x01\x28\xd1\x60\x95\xf7\x39\xe1\x69\xb4\xda\x8a\x0b\x8c\x2f\x8c\x6c\xa7\x55\xcc\x44\x8b\x95\xe5\x46\x5a\xe2\xf1\x76\x66\x5a\xac\xfa\x5f\x67\xa7\x55\x14\x81\xfb\xe2\x94\x7d\x68\xcf\x6d\xaa\x45\x71\xf9\x07\xd8\x12\x5b\xe5\xa7\xc1\x4c\x08\x87\xd3\x60\xb6\x7c\xec\x05\x87\x8b\xb8\x0d\xc2\x67\x95\x49\xc7\xfc\xb6\x06\xcb\xe8\xf9\x06\x6a\xfa\x6d\x96\xaf\x33\x5c\x77\x18\x2d\xd3\x3f\x9f\xe9\x32\xfd\xf3\x1a\x30\x73\xc0\x0d\x09\xb8\x14\xa2\xe7\xa8\x5e\x76\xd8\x44\xf3\x2f\x45\x2c\xa3\x39\xe0\xa6\x01\xb8\xe1\x05\xdc\x70\x02\x76\x43\xce\x92\x70\x36\x81\xab\x97\x12\x1d\x96\xd7\xaf\xc1\x6f\xe2\x2b\x7d\x6e\x90\xe7\x75\xf2\x08\x28\xb8\xa0\x88\xa9\xf8\x4c\xa7\xa2\xf4\x19\xbd\x26\xad\xff\xf8\x23\x02\x6c\x3e\xa3\x9f\x50\xad\xba\xd6\x56\x66\xa8\xfc\x0a\x7d\xce\x09\x77\xa1\xcc\x3d\xb5\x05\x9f\x06\x33\xb0\x99\xdd\xcc\x4a\x25\x8e\x30\x74\xba\x83\x7e\x42\xa5\x26\x7a\x81\x3e\x97\x59\x4f\x9b\x23\xa7\xb7\x93\x15\x9f\xc1\x56\x5c\x0c\x87\x3c\xdd\xb7\x4d\x8d\xec\x03\x41\x09\x6d\x20\x05\x9d\x8e\xe5\x4c\x02\xb1\xf5\x64\x71\xb7\x71\xf0\x38\x9c\x60\x54\x52\xfb\xc9\xc2\x05\xf8\x62\x8d\x38\x87\x45\x6d\x66\xf9\x3e\x33\xce\xaa\x42\xbd\x83\x9d\xbc\xc6\x93\x6f\x6f\x67\x29\x58\xed\x52\x8c\xfe\xbb\x36\xb5\x64\x3b\x04\xb5\xeb\x51\xb7\x92\xe2\xe6\x96\xa2\xd6\x92\x9b\x83\xa8\x27\x0c\xe5\xc5\x1b\x61\x28\xbf\x98\xef\x5b\x25\x12\x7c\x81\x93\x14\xef\x2b\x05\xe5\x2b\x57\x5c\xb3\x1f\xe4\x67\x2f\x75\xe7\x02\x75\x6d\x01\xfc\xcf\xe4\x3f\x84\xfd\x90\x15\xca\x3a\x98\xcb\x69\xf4\x86\x4f\xf9\xc2\x66\xb6\xf9\x9f\xcb\x67\x68\x03\x7d\x2e\x16\xab\xd3\xc1\x52\xf6\xce\xa3\x38\xc1\xdf\x8c\xab\x28\x20\xf7\xa2\x21\xf8\x39\xcb\xe9\x0e\xc9\x9b\x83\xd1\x22\x9e\xa1\xb4\x43\x61\xfc\xb0\xb1\x81\x5e\xd4\x17\xf0\x24\x95\xc2\xd4\xda\xb7\x62\xc4\x4e\x91\x20\x11\x69\x2f\x53\xfc\x3e\x8e\x67\x72\x49\x54\x4c\x1c\x2a\xca\x8c\x6a\x22\x87\x71\xe3\x19\xcc\xba\x68\x65\xf3\x4d\x6f\x6b\x7b\xe7\xed\xee\xde\x7f\xbd\x7b\xbf\xff\xe1\xe0\xf0\x7f\x1f\x1d\x9f\x7c\xfc\xe5\xd7\xdf\xfe\xfd\x7f\x82\xfe\x60\x88\x47\xe7\xe3\xf0\xf3\x97\xc9\x34\x8a\x67\xff\x9d\xa4\xd9\xfc\xe2\xf2\xea\xfa\x8f\x5a\xbd\xd1\x6c\xb5\x3b\x6b\xeb\x2f\x9f\xaf\x6e\xb0\x08\xb7\xe2\x68\x27\x16\xed\xd2\xa8\xca\x21\xf6\x78\xa5\x48\xcb\x0d\xcd\xc2\xd4\x25\x0a\x19\xed\xb8\xdc\x54\xc8\x4c\x87\x9e\xfd\x86\x39\x76\xa5\x44\x48\x52\x96\x87\xa4\x26\xd5\x81\x05\xbd\x40\xf5\xf2\x19\x78\xaf\x48\x81\xa9\x61\x13\x17\x07\xda\x28\x02\xb4\x7c\xc6\x37\x78\x55\x0c\x73\x40\xa5\x02\x51\xa4\x45\xee\xf9\x4a\x84\x19\x40\xff\x2b\x6d\x51\xf5\xad\x89\xf2\x83\xf7\x20\x36\xc4\xcf\x9f\x6b\x1f\x04\xd9\x8a\x1f\x8c\x22\xad\xd8\x92\xce\xb0\x08\x37\x32\x77\x8f\x79\xc8\x57\xf6\x88\x57\xde\xcc\x3e\xed\xc7\xa3\xff\xe3\xd1\x5f\x1c\xfd\x3f\x9e\xec\xbc\xa8\x77\xd0\x9b\xed\xc2\x0e\x5a\xf5\xce\x9b\x6d\xd5\x47\xab\xde\xd1\x9f\xe0\xeb\xed\x9d\xb6\x28\x32\x7f\xad\xe3\x56\x41\x1c\xee\xd1\x79\xab\xde\xf1\x7a\x6f\xd5\x3b\xff\x00\x8d\x40\xf1\xc3\x3a\x0c\xc6\x5d\xce\xea\x6e\x7f\x7f\xb0\x8c\x8a\x87\xf8\x30\x0e\xa3\xcc\xe7\x64\x5c\xef\x78\x9c\x8c\x9d\x87\x69\x89\xa9\xdf\xcb\x58\x34\x59\xd4\xd5\x58\x01\x7a\x87\x13\x94\x49\xc4\x77\x72\x56\x03\xda\x5c\x76\x6d\x7c\xd7\xc7\x28\xba\xaa\x84\xcb\x1a\x5f\x7c\x4b\xf9\xac\x41\xa5\xe5\x7c\x8d\x79\x2d\x21\xdf\xf2\x17\x0f\xed\x69\xac\x37\x5c\xcc\xd1\xb8\x0e\xb2\x8f\xc0\x50\x77\x33\x26\x22\x90\x5c\x2c\x0d\xb2\x58\x8c\x20\x6c\x7e\x0a\xf7\x49\x39\xc6\xe8\xfc\x54\x3c\x14\x06\x23\xcb\xf7\x05\xf6\x30\x65\x9f\x7a\x7f\xe7\x7d\xea\xfd\x77\xb0\x4f\x15\xc1\xe1\xbe\xf7\x29\xe7\x72\x7a\xbf\xfd\xb8\x4d\x89\xbf\x7b\xdb\xa6\xd2\xcb\x60\xb6\x1d\x0d\xc3\x20\x2a\x2d\xbb\x63\xb9\x8e\xe4\xdf\xff\x96\xf5\xfe\x61\xb6\xac\x22\xcb\xe4\xfb\xdf\xb2\xde\x6f\x1b\x9b\xd6\xe3\x8e\x65\xed\x58\xca\x8a\x59\x6a\xf3\xfa\xa6\xbb\x97\x98\x17\x05\x5b\x02\x48\xeb\x23\x8f\x86\x0f\x5f\xd8\xdd\x09\x5d\xdc\xb5\x1a\xf9\x7f\xb8\x58\xa1\x1f\x49\xf7\xd9\x57\xfa\x4d\x2e\xff\x45\xea\x02\x20\x2c\xbf\xb6\xa0\x73\x27\x6d\x01\xcb\x51\xfb\x2d\x95\x06\x15\xa4\xbc\x4a\xc7\x41\xdd\x78\x35\x9e\x06\x83\x07\x54\x2d\x54\x10\x6f\x16\x7e\x41\x6b\xff\x04\x75\x83\x95\x2f\xf6\x16\xaa\x08\xcd\x88\x45\xf9\xb2\xbf\xd5\x86\x9a\x60\x72\xb3\xbf\xd5\x76\xc9\x78\x60\xe2\xfc\x05\x5f\xd3\x2c\xd8\xd4\x0e\x56\xf4\x15\x9c\x7f\x83\x28\xe3\x49\xbc\xe3\x64\x4a\x6d\xb4\xb7\x7f\x39\xfc\x04\x9b\xee\x49\xfc\x0e\x4b\x61\x10\x5d\x5e\x5e\x56\xe3\x19\x8e\xd2\x74\x52\x8d\x93\xf3\xd5\x61\x3c\x48\x57\x21\x09\x77\xbc\x6a\xd4\x19\x67\xd3\x89\x43\x11\xb2\x7d\x31\x7b\xb7\xb5\x23\xd1\x16\xcf\x05\x83\x21\x2c\xf6\x01\x31\xf6\x38\xcb\xfb\x85\xa5\x3c\x87\x3d\x8a\x0c\x4c\x4a\x1e\xc2\x88\xbb\xbd\x28\xe1\x9e\xa5\xab\x4b\x0b\x95\xea\x8d\x75\xcd\xd3\xc5\x82\xef\x31\x52\x53\xc3\x62\x98\x09\x52\xf6\xb7\xda\x8b\xb0\x0d\x33\x66\x8b\x6c\x06\xa9\x56\x3e\x64\x31\x9a\x51\xab\x53\xd5\x3b\xc7\xb3\xc3\x59\x7e\x31\xc6\xee\xc0\x86\xa7\x8b\xea\x8d\x75\x30\x21\xd5\xbe\xd2\xce\x01\xe6\xc6\x17\x89\x8f\xd6\xf6\xcd\xad\xdd\x6e\x3c\x44\xfb\xd0\x7e\x38\x58\x69\xf4\x1e\xcc\xac\xbf\x0c\x47\x96\xf7\x0d\xa5\xf9\x05\x29\x9a\x16\x57\xfc\x53\xce\xd5\xba\x91\xcf\xef\xb6\x60\x2a\xfa\x34\xd6\x6a\x35\x13\xf0\x92\xde\x41\x0b\xfd\x7e\x8a\xc9\xbb\x5b\x90\xc2\x9f\xd0\x08\xa1\x0a\x48\x84\x1d\x40\x06\x56\xb2\x68\x6f\x63\xa5\xcf\xeb\xd2\x58\x00\x2e\x40\x39\x95\xd3\x60\x92\xa1\x4d\xf8\x67\x79\xb1\x18\xa8\x8b\x92\xf7\x7d\x90\x17\x26\x9b\xc7\x97\xe1\xa8\x4a\xdd\x22\x70\x89\x77\xa6\x02\xf8\xe5\xe4\xad\x81\xe2\x5a\x7e\x47\xbd\xe6\x52\x02\xaf\x3e\xc5\x0e\xf1\x96\xac\x74\xc6\x3d\xec\xda\xc2\x4b\x8d\x90\x07\x33\x51\x96\xab\xc3\x09\xcb\xe7\x16\x06\xa1\x05\xe8\x10\xbf\x83\xb1\x71\xa5\x44\x5b\xe6\x8c\x2c\x81\x09\x9f\x60\xf1\xc6\x7b\x5c\xe6\x7b\x0c\xed\x11\x7b\x72\x94\x53\x98\x38\x2d\x2a\x5f\x38\xb0\x7c\xcb\x36\x26\x02\x5e\xff\xc8\x8c\x59\x0c\x5c\xb9\x41\xcb\x6b\x8e\x8f\xf3\x28\x40\xc4\x38\xf0\x1c\xf0\x5e\x30\xeb\x2e\x4b\xb4\xec\xe2\x6b\x65\xa4\x06\x63\x90\x4e\x20\x0c\x0a\x27\x36\xc5\x28\xd8\xa2\x57\xbd\x79\xe1\x4f\x67\x97\x20\x34\x21\x06\xce\xfe\xac\x1d\x94\xea\xf4\xa0\xa4\x0c\x74\x6e\xda\x1f\x03\x7b\x81\xac\x77\x14\x5c\x18\x3b\x86\xca\x7e\xa7\x90\x15\x8b\x19\xe3\x6c\xc3\x18\x65\xa5\x96\xa2\xa3\xe1\xf4\xe7\x88\x76\x21\x02\xcc\xf1\x7a\x45\x6d\xae\x0b\xf1\x60\xd5\xef\xf8\x56\xbc\x77\x49\xbe\x7b\x8f\xde\xb7\x0e\xbf\x32\xa5\x37\xc5\xb9\xb9\x52\x49\xd3\x6e\x28\xef\x75\xee\x2e\x3f\x20\x8d\xab\x8b\x4d\x9b\xee\xd7\x3e\xce\xbe\x5c\xb5\x0a\xf2\x88\x0d\x77\x01\x93\x2b\x36\x08\x15\xb2\x94\xf5\x7d\x7b\x8e\xed\xc2\xc2\x86\x5d\x97\x58\xc0\x71\x25\x7f\xbf\xbb\x79\x95\x73\x7c\xa7\xd0\xdc\x67\xf7\x0a\x3f\x7c\x76\xdb\xeb\x15\x7e\x24\xed\xae\xad\x91\x33\xfd\xda\xdf\xfa\x4c\x3f\x08\x67\x63\x9c\xbc\x78\x60\x13\x01\x38\xbd\xab\x4d\xfd\x35\x87\x78\x3b\x73\xe7\xbd\x9c\xe6\x7b\xd0\xb1\x43\xc2\x71\x52\x71\x68\x57\x5f\xfa\x4d\x08\xc4\x7b\x23\x13\x86\x56\x83\x9c\xe1\x82\x0c\x2a\xd1\x9f\x9c\x11\xb3\x8a\x3b\xf0\x32\x63\x51\x15\x68\x91\x25\xd2\x69\x90\xd3\x0d\x9d\x9b\x0c\x5f\x65\xe4\x14\x19\xb0\x67\x34\xa3\x7d\x62\xbe\x59\x3c\xd5\x46\x30\xc4\x83\x70\x1a\x4c\x26\xd7\x2c\x0d\xe8\xb0\xf0\xcd\x8d\x3a\x2a\x37\xac\x15\x36\x70\x27\x02\x0d\xbd\xd9\xe5\x93\x71\xdc\x06\xbf\x07\x4d\xcf\x21\xa7\x44\xb9\xd5\x51\x3b\xbf\xdc\xc5\x8e\x56\xd3\xe3\xa8\xa5\x96\xa9\xca\xd9\x95\x09\x24\x76\xf1\xd5\x2d\x33\x41\x38\x86\x57\x21\x1f\xf5\xbe\x61\xc9\xe9\x34\x6e\x1e\xc2\x68\x36\xcf\xee\x32\xa7\x9c\x3c\x74\xa2\xbb\x05\x9d\xdd\x17\x71\x0c\x0c\x46\xe1\xa0\x8f\x5b\x27\x95\x80\xd1\x72\x87\xb0\x91\x93\xb3\x81\x64\x1b\xb4\xc2\x2b\x27\xf5\xf4\x34\xea\xe1\x1a\x01\x09\xa8\xab\x02\xbd\x71\xeb\xe6\xfd\x3b\xad\xec\xae\xb1\xdb\x2a\x1b\x44\xb7\xdd\xa8\x18\xca\xf3\xf5\x47\x53\xbb\x7f\xba\xee\xdb\xb7\x3b\x5a\x91\xcc\xf3\x34\xe1\xf6\x21\x05\x1c\x80\x85\xc6\xd5\x99\x88\x8a\x94\xd8\x50\x1d\x55\xef\x27\x21\x3d\xb8\xbc\x2e\xe4\x78\x85\x95\xc4\x05\x55\x51\x44\x56\x07\xe7\x65\x3c\x48\x70\x76\x4f\x4a\x25\x22\xff\xee\xba\x03\x07\x41\x2f\x19\x9b\x70\x79\x22\x53\x47\xdf\xa2\x1a\x43\xd5\x39\xd8\x13\x20\xd8\xa9\x33\x12\xfa\x22\xea\xa3\x20\x1e\x4d\x0f\xf7\x1c\x6f\xb7\xfb\x8c\x2f\x0b\x07\xa6\x05\xe1\x65\xe9\xa1\x4a\x89\x2e\x6b\x8e\x93\xdb\x10\x3f\x47\x31\x45\x3b\xfa\x46\x89\x8b\xc9\xba\x9e\x17\x19\xd3\xa8\xc4\xf5\x05\x26\x2c\x77\x94\xcc\xcd\xc9\x24\xbe\x44\x41\xd2\x0f\xb3\x24\x48\xae\x11\x53\x2f\x7d\xc1\xd7\x8e\xb8\x83\x5f\x54\x8d\xc4\xcf\xce\x86\x73\x06\xca\x54\xb7\x14\x1b\xad\x05\xce\x90\x04\xa5\x1c\x37\x48\x88\xff\x06\xba\x8d\x38\x41\x61\x14\xe1\x04\xa2\xcf\xc6\xf3\x0c\x04\x08\x33\x0a\x1f\xc4\x4c\xa4\x3a\x46\x4a\x86\xec\x81\xb6\x62\x05\xa4\xe3\x1a\x3f\xb5\x46\xe8\xa8\xb1\x0c\x09\xc4\x8a\x56\x32\xce\xd3\x47\x86\x4a\xc1\x50\x29\x68\x35\xf6\xdb\xc1\x11\xcc\x27\xbd\x06\x9c\x05\x43\x34\x88\xa3\x34\x0b\x22\xb3\x79\x67\x12\x29\x7d\x8e\xfd\x8a\x35\x81\xf7\x69\x78\x86\x7e\xdf\x40\xb5\xab\xf6\x80\xfe\xcf\xe5\x0e\x63\x15\x6e\x76\xe8\xff\xf2\x35\x63\xb1\xa1\x13\x0b\x8d\x67\x17\x45\xfe\x05\x71\xc8\x60\x07\x7a\x88\x28\x64\x82\x89\xdf\x4b\x24\xb2\x9c\x7c\x65\x2e\x66\xec\x18\x48\xe8\xb4\x8b\x8f\x7b\xf4\xa4\xba\xbe\x58\x2e\x98\xdb\x45\x20\x83\x61\xfe\x6e\xe2\x8f\xed\x6f\xf6\x58\xf4\x31\xc0\x2b\x84\x25\x96\x1b\x09\x65\xc9\x29\x2f\x12\x88\xcc\x2a\x7d\xff\xc1\xc8\x54\x92\xe0\xad\x2c\x0c\x3e\xf6\x50\xd1\xc3\x60\xa8\xff\xa7\x47\x0f\x5b\x20\xa6\x2e\x23\x22\x12\x1e\x2a\x69\x68\x61\x04\x31\x7f\x8d\x85\x51\xc4\xfc\x55\x1f\x28\x92\xd8\xdd\xb9\x5d\x8f\xaa\xa7\x61\xbc\x1d\xfb\x31\x91\x2e\x76\xdd\xc1\xd1\x72\x03\x8e\xe5\x72\x4c\x75\xac\x0c\xa0\x52\x42\xe1\x92\x06\xbf\x64\x12\xa8\x94\xbd\x21\xc7\xa6\xc1\xc0\x7d\x49\x24\x0e\xfe\x1e\x23\xb8\x97\x7f\x6b\x85\xf9\x55\xa7\xf5\xc2\xf1\x7a\x12\xf6\x5f\x10\x54\x86\x60\xdb\x9a\x1a\x5f\x71\x34\x78\x01\x36\x8d\x8e\xf7\xd4\xcd\xd2\xf8\x30\x1d\xb6\x17\x1b\xdf\xa5\xe3\xa0\xd1\x36\x41\x92\x97\x0d\x13\x5c\x3a\x0e\xda\xf5\x86\xfd\xb2\xb9\xee\x28\xd9\x34\x5e\x25\xe1\x0c\x4f\x87\xf5\x4e\xcd\x69\xfb\xa7\xbd\x9a\xf5\xbf\x0c\x47\x66\x3b\xf8\x62\xf6\x65\x38\xca\xbb\x77\xd0\xbb\x1e\x0f\xf1\x8b\xc1\xa8\xef\x7c\x9d\x25\x9e\xd7\x2f\xce\x27\xc1\x70\x1a\x44\xae\xcf\xb1\x1b\x18\x1e\x98\xaf\x67\xc1\xf0\x45\x10\xa5\xe1\xd5\xcb\x86\x39\x08\xe4\x53\x98\xc6\xf5\x5a\xbd\x61\x8e\x38\xfb\xf4\x72\xed\xe5\x9a\x39\x43\xe4\xd3\x1f\x38\x89\x99\xeb\xb5\xe3\x6b\xe4\xf9\x46\x75\x64\x2f\xc6\xf8\xca\xf8\x10\x60\x93\xb8\x68\xdc\x8d\xa1\xf5\x3e\x19\x98\x93\x9b\x04\xfd\x7e\x98\x39\x5f\xbe\x98\xe0\xf3\x60\x70\xfd\xd0\x77\x40\x62\xf5\xc0\x93\xb9\x68\xe0\xa5\x5c\x2b\xe2\x91\x2d\x11\x78\x26\x2b\xc3\x30\x0b\x65\xeb\x40\xfc\x6e\xb4\xc4\x6f\x42\xf5\xfc\x37\x21\x76\xf1\x9b\xfe\x92\xa4\x2d\xed\x4b\xe1\x17\x23\x64\x8a\x01\xa5\x5f\xeb\x0e\x8b\xa2\xc3\xa9\x55\x79\xca\x12\xfd\x49\xd0\xa6\x7c\x1b\x6b\x35\x08\x25\xd2\x66\x55\x02\x14\x6f\x04\xdd\xa9\x6f\x28\xb9\x89\x37\x2a\x95\x89\x97\x91\xfe\x4a\xa1\x29\x78\x26\xa4\x04\x3f\x24\x05\xd1\x51\x19\xb0\x81\x62\xf4\xa2\xfc\xe6\x64\xb2\xac\x22\x52\x53\x40\xaa\xbc\x76\x79\xc5\xa4\x3f\x14\x1b\xeb\x52\xb7\x5d\xaf\xe4\x6b\x93\x2b\x3a\x5d\x75\xdb\xad\x8a\x46\x78\xdd\x76\xbb\x22\x27\xbe\xdb\xee\x54\xf4\xd1\xeb\xb6\xd7\xcc\x1b\x61\x93\x94\xbb\x9d\x5a\x85\x51\x6b\xb7\x03\xf8\x08\x4a\xe9\x76\x1a\x15\x95\x56\xba\x9d\x56\xc5\x45\x2d\xdd\x4e\xb3\xa2\x52\x48\xb7\xd3\xae\xa8\xf4\xd3\xed\x00\x5e\x1a\xcd\x74\x3b\x6b\x15\x93\x6a\xba\x9d\xf5\x8a\x49\x37\xdd\xce\xcb\x8a\x45\x24\xdd\xb5\x5a\xc5\x41\x4e\xdd\x35\xc0\x9f\x2d\x89\xee\x1a\x60\xcf\x48\xa3\xbb\xd6\xaa\x58\xc4\xd1\x5d\x03\xc4\x09\x19\x75\xd7\x00\x67\xb9\xce\xba\x6b\x1d\xf5\x02\xbd\x22\x97\x6c\x77\x8d\x5f\xad\x93\xc5\xdc\x5d\x7b\x59\xe1\x4b\xb5\xbb\x5e\xab\xc8\x25\xdc\x5d\xaf\x57\xe4\xe2\xee\xae\x03\x3a\x92\x82\xbb\xeb\xd0\xb8\x60\x34\xdd\xf5\xd6\xcd\x59\xa5\x53\x7b\xbc\x3c\xf8\xeb\x2f\x0f\x7a\x63\x3c\xf8\x42\x3a\x05\x2b\x85\xba\x01\xd1\x34\x67\xe9\x7c\x46\x06\x06\xb3\xf8\xd4\x4a\xbf\x41\x8e\xa7\x21\xcd\xd1\x0f\x1b\x68\x85\x43\x5e\x71\x58\x84\x08\x27\x8d\x7b\xbc\xae\xc8\x35\xc7\x17\xed\x1c\xe1\x11\x4e\x30\x1c\xf4\x92\xf0\x1c\xce\x64\x61\x14\x66\x12\x4c\x3a\x9f\xe1\x04\x54\xd7\x1b\x46\x7a\x0e\x05\xca\xe6\xfc\x7c\x8a\xa3\xcc\x28\x80\xb2\x18\x8d\x83\x68\x38\xc1\xda\xb8\xa9\xb0\xfb\x4e\xc8\x9a\x4d\x0d\x54\xb5\xdd\x01\x15\xdd\x37\x8d\x25\x4f\x4d\xa0\xc2\x28\x5b\x57\x34\xf4\x23\xb5\xbe\x50\x4c\xe8\xb3\x63\x1f\xf3\x65\x0d\xaa\x84\xff\x48\xa0\xc2\x0b\x15\x1b\xed\x10\xe1\x44\x2c\xa6\xe9\xbf\x00\xd2\x45\x88\x2f\x7d\x28\x7a\x9b\x57\x10\xde\xe3\x28\xa0\xaf\x5f\xf5\xf2\x9c\xe0\x00\x4b\xd0\x19\xf3\xea\x3f\x90\x35\x27\x6c\x47\x60\xd1\xb9\x81\x5b\x55\xcb\x56\x2b\x5e\xac\xea\x1d\x37\x5a\xfe\x96\x96\xab\xb1\x17\x65\xcd\xc6\xb2\x4d\x2c\x57\x63\x67\x12\x07\xb7\xa9\xd2\x69\xc1\x7b\x59\xfe\x96\xa4\x54\xa5\x14\x5c\x41\xea\xab\xeb\x0c\x1f\x40\x72\x20\xeb\xb5\x2b\xef\xb2\x46\x7f\xbb\x74\xd1\xc9\xb6\x8a\xac\x08\x59\x7a\x39\x15\x82\x84\xf6\x46\xe0\x86\x36\xdc\x38\x3b\x34\x0b\xdb\x57\x2c\xfb\xea\x75\xe6\x32\x7e\x5e\xca\x5d\xd0\x85\xca\x32\xf9\xb4\x65\xfd\xd3\xf0\xec\x56\xc9\xb3\xa5\x39\x77\xf8\x07\xa6\xaa\x5a\xe9\x38\xaa\x17\x15\x8c\x55\xa6\xb6\xa8\x20\xe6\x46\xe8\xea\x88\x36\xdf\xce\xac\x67\x64\x34\xc9\x6b\x02\x0f\x45\x44\xea\x53\x99\xb9\xdd\x6e\x30\x9b\x4d\xae\x59\xc3\x41\x72\x3e\x27\x2c\x3c\xcd\xf3\x57\x64\xfc\xba\x3a\x4b\xe2\x2c\x26\x38\xaa\x9c\x3b\xcf\x70\xc2\xdc\x7d\xdc\x0a\x96\x4e\xfd\x51\xd6\xf9\x6b\x64\x1d\x08\x18\xfd\x17\xc4\x25\x72\xe6\x54\x2a\x60\x22\x01\x5b\x2c\xbd\xc7\x43\x99\xd4\xad\x93\x2a\x27\x8c\x59\x28\x25\xa9\xea\xd2\xb8\xf9\x73\x49\x7a\x3e\xbe\xd2\x69\xb9\xb9\xc8\x09\x61\x13\x1b\x74\xf8\xaa\x41\x3f\xa5\x3f\xd2\x30\x62\xc1\x58\x09\xcb\xa8\x5d\xd5\x6b\xec\xaf\x8c\xbe\xea\x69\x7c\xd9\xf2\x2a\x95\x9d\x16\xea\xfb\x5b\x6d\xc3\x9a\xc2\x65\x00\x62\x7a\x4d\xa2\x0d\x36\xaa\x0e\x03\x10\x9e\xf6\x26\xf7\x76\x4c\x6a\x82\xdd\xb9\x8a\x4f\x6d\x4e\x5a\xbb\xea\xac\xb5\xda\x8d\x66\xad\x5e\x41\xb5\x2b\x3c\x1a\x0c\x83\xfe\xfa\x4b\x47\x5e\xc5\xda\xd5\xcb\xf5\x7e\x30\x1c\x8c\x70\x05\x06\xa6\xd9\x68\xb7\xd6\x3a\x7a\xb9\x33\xef\x8d\x98\x91\x46\x4f\xed\xc5\xbe\xc8\xa4\xe7\xda\xbb\x2e\x83\x19\xc2\xe0\x5e\xbd\x78\x0f\xa9\x77\xfc\x3b\x86\xff\xfa\x9a\xcf\x06\x45\xe2\x13\x81\xc7\xd3\x0b\xa2\xd0\x13\x81\x77\xff\x93\x52\x7a\xff\x94\x3f\x9c\xb9\x5c\x42\x94\xcf\x84\xe0\xec\x02\xe4\xaf\x54\x2a\x29\x30\xa9\xa7\x38\xfa\x8a\xd4\x97\xb0\xd7\xb5\xca\x86\x8f\x38\xfa\x5a\x10\x60\xa3\x55\x76\x00\x84\x50\xc6\x9a\x4b\xba\x0d\xee\x6e\xc6\x21\xbb\xda\x0d\x85\xfb\xba\x5f\x1b\xd2\x1a\x52\xc6\x14\x3d\x47\x35\x53\x7c\xd0\x4a\xd7\x8d\xd2\xf5\xdc\xd2\x0d\xa3\x74\x23\xb7\x74\xd3\x28\xdd\xcc\x2d\xdd\x32\x4a\xb7\x72\x4b\xb7\x8d\xd2\xed\xdc\xd2\x1d\xa3\x74\x27\xb7\xf4\x9a\x51\x7a\x2d\xb7\xf4\xba\x51\x7a\x3d\xb7\xf4\x4b\xa3\xf4\xcb\xfc\xd9\xa9\x19\xb3\xb3\x60\x32\xeb\x46\xf1\xfc\xd9\xac\x37\x8c\xe2\xf9\xd3\x59\x6f\x1a\xc5\xf3\xe7\xb3\xde\x32\x8a\xe7\x4f\x68\xbd\x6d\x14\x6f\x5b\xdc\x60\x75\x95\x30\xe4\x2f\x61\x74\x4e\xaa\x86\xc1\xa4\xef\x12\x9b\x03\xb2\x0d\x9c\x3a\x07\xaa\x0f\x9f\x9c\x83\x32\x80\x4f\xce\x01\x18\xc2\xa7\xa6\x0b\x9d\x9e\xbc\x83\xd6\xbf\x11\x24\x76\x76\x4a\x41\x05\xf5\x2b\x68\x50\x41\xc3\x8a\xb2\x40\x2b\x08\xad\x55\xc8\x16\x5a\x3b\x33\x79\xc3\x90\xd6\x1b\x56\x90\xa8\x2a\x47\xa8\x82\x50\xbd\x51\x41\x27\xa7\x75\xab\xde\x80\xd6\xa3\x2d\xd1\xaa\x72\xd1\x92\x7a\x6b\xa4\x5e\xc3\xaa\xd7\xa7\xf5\x04\x92\x81\x52\xaf\x59\x41\xa8\x01\xed\x35\xad\x7a\x79\xfd\x6b\x89\xfe\xb5\x96\xea\x5f\x5b\xf4\xaf\xbd\x54\xff\x3a\xa2\x7f\x9d\xa5\xfa\xb7\x26\xfa\xb7\xb6\x54\xff\xd6\x45\xff\xd6\x97\xea\xdf\x4b\xd1\xbf\x97\x4b\xf5\xaf\x5e\xab\xb0\xfe\xd5\x6d\x82\xc9\xeb\x60\xbd\x5e\x61\x1d\xac\xdb\x14\x93\xd7\x43\x82\x25\xed\x61\xdd\x26\x99\x5c\x12\x6d\x56\x38\x89\xda\x34\x93\xdb\xc7\x96\xe8\xa3\x4d\x34\xb9\x7d\x6c\x8b\x3e\x02\xd5\xd8\x9d\x7c\xfb\xd6\xd3\xc9\x0a\x42\x6d\xda\x49\x9b\x6e\x86\xb4\xa2\xb3\x93\x84\xde\x5e\xd2\x8a\x36\xe1\x0c\x68\x45\x77\x27\xeb\x15\x44\x3a\x7a\x72\x5a\xb7\x29\xa7\x4f\x2b\x3a\x3b\x49\x38\x46\xa3\x06\x15\x6d\xd2\xc9\xeb\x63\x5b\xf4\xb1\xe1\xe6\x35\xbe\x3e\x12\x9a\xa3\x7d\x6c\xb8\x99\x8d\xb7\x8f\x6d\xde\xc7\x86\x9b\xdb\xf8\xfa\xd8\x12\x7d\x6c\xb8\xd9\x8d\xaf\x8f\x2f\x65\x1f\xdd\xfc\xc6\xdb\xc7\x96\xe8\xa3\x9b\xe1\xf8\xfa\x48\x18\x23\xeb\xa3\x9b\xe3\xf8\xfa\xb8\x2e\xfb\xe8\x66\x39\x5e\x5a\x6d\x56\x78\x1f\xdd\x3c\xc7\xd7\xc7\x86\xa0\xd5\x86\x9b\xe9\xf8\xfa\xb8\x26\xfa\xd8\x74\x33\x1d\x5f\x1f\xc9\xf2\xa7\x7d\x6c\xd6\xdd\x0b\x72\x77\xd7\x4f\xac\x2d\xc0\xb5\xe9\xe6\x3a\xbb\xbb\xee\x4e\x92\x61\x25\x6b\xeb\xe4\xb4\xe9\xe6\x3a\xbb\xbb\x39\x0b\xb2\x03\x15\xdd\x5c\x67\x77\xd7\xd3\xc9\x56\x05\x35\x9a\x50\xd1\x26\x9d\xbc\x3e\xd6\x65\x1f\xdd\x4c\xc7\xd7\xc7\x96\xec\xa3\x9b\xe9\xf8\xfa\x08\x13\x49\xfb\xe8\x66\x3a\xde\x3e\xd6\x44\x1f\xdd\x4c\xc7\xdb\xc7\x66\x85\xf5\xb1\xe5\x66\x3a\xbe\x3e\xd6\x44\x1f\x5b\x6e\xa6\xe3\xeb\x63\x53\xf4\xb1\xe5\x66\x3a\xbe\x3e\x12\x56\x4e\xfb\xd8\x72\x33\x1d\x5f\x1f\x5f\x8a\x79\x6c\xb9\x99\x8e\xaf\x8f\x64\x79\xb0\x3e\xba\x99\x8e\x97\x56\xdb\x9c\x56\x5b\x6e\xa6\xe3\xeb\x63\x43\xf6\x71\xcd\xbd\x20\xf7\xf6\xfc\x82\x6a\x87\x76\xd2\xcd\x75\xf6\xf6\xdc\x9d\x04\x9a\x03\x1e\xd0\x72\x73\x9d\xbd\xbd\x1c\x31\xa0\x0d\x22\xa0\x9b\xeb\xec\xed\xb9\x3b\x49\x78\x47\x03\x86\xb5\xed\x16\x75\x7c\x7d\x24\xf3\x41\xfb\xd8\x76\x33\x1d\x5f\x1f\x9b\xa2\x8f\x6d\x37\xd3\xf1\xf6\xb1\x26\xfa\xe8\x66\x3a\xbe\x3e\xd6\x65\x1f\xdd\x4c\xc7\xd7\xc7\x75\x31\x8f\x6d\x37\xd3\xf1\xf5\x11\x68\x8e\xf6\xd1\xcd\x74\x7c\x7d\x04\x91\x9c\xf6\xd1\xcd\x74\xbc\x7d\x6c\x56\x78\x1f\xdd\x4c\xc7\xd7\xc7\x96\xe8\x63\xc7\xcd\x74\xbc\x7d\xac\xf3\x3e\x76\xdc\x4c\xc7\xd7\xc7\x86\xe8\x63\xc7\xcd\x74\x7c\x7d\x7c\x29\xe6\xb1\xd3\xb4\x17\x24\x5c\xa3\x64\x38\x99\xe2\x61\x18\x64\xcc\xa9\x0c\xdc\x15\xf4\x72\xe4\x88\x8b\x36\x50\x09\xfe\x7d\x8e\x02\x53\xc3\x4a\xcb\xd4\x59\x99\x3a\x29\xd3\x77\x97\x69\xb0\x32\x0d\x52\x66\xe0\x2e\xd3\x64\x65\x9a\xa4\xcc\xd0\xd2\xe6\x1a\xaa\xca\x1d\x87\xa5\xee\x92\x01\x6d\x21\x53\xba\xc8\xa6\x1b\x64\x81\xeb\x60\x1e\x64\x81\x08\xe5\x13\x64\x81\x5f\x39\x16\xbd\x09\xb3\xf4\x24\xce\x82\x89\x80\x19\x6d\x05\x59\x40\x3d\x48\x7e\x42\xeb\x0e\xe8\x50\xe7\x3d\x1e\x65\x1c\xba\xf0\x38\x81\xf2\x56\x67\xbc\x29\xaf\x04\x9a\xa7\x12\xe4\xcf\x3f\xff\x8c\xda\x70\xf1\x56\xbb\x5a\xaf\xc9\xfb\x36\x59\xe2\x5f\xa8\xd9\xb0\x88\x43\xef\xcb\x2e\xda\x40\xa0\x76\x1f\x4d\xe2\x38\x29\x29\x9d\x5c\xd5\x74\xef\xbe\xce\x41\xd9\xf7\x68\x43\x79\x32\x17\x8e\x40\xbd\x54\x2a\x49\xdc\x9e\xa3\x4e\x8b\xe6\x4b\x7b\x09\xc1\x44\x5b\x65\xaa\xb0\x71\xeb\x67\x79\x55\x86\xb3\x54\xce\xaa\x6f\x8b\x6b\x67\x6d\x70\x4c\x35\x6b\x82\x5b\xa4\x9b\xb5\xb8\xc4\x32\x9d\x6d\x15\xe9\xec\x7b\x67\x67\xdf\xdf\xb6\xb3\xef\x9d\x9d\x7d\x5f\xb4\xb3\x76\x6f\x55\x27\xaa\x92\xe8\x3e\x0f\x36\x05\x39\xf5\xdc\xfe\x83\x60\xf0\x4e\xdd\x18\xc0\x47\xd1\xe5\x49\x95\x9b\x57\x7e\x81\x37\xa4\xa6\xf3\x76\x90\xef\x2e\x33\x8c\xf7\x7a\xbf\x2d\x75\xef\xe1\xb9\xe2\x42\x79\xd7\xff\x02\x13\xb8\xc2\xd8\x3d\x75\xdf\x5d\xec\xb2\x5b\xb2\x52\x69\x57\xbb\x96\xd8\x5d\xfa\x3e\x82\xd2\xc2\xae\x76\x17\xb1\xeb\xbd\x84\x58\x7c\xe3\x70\xc4\x72\x03\xc3\x1c\xb2\x08\x3c\x43\x18\x53\xbd\x68\x81\x64\xe5\xe0\x86\x90\xcb\xea\x41\xc1\x0a\x4e\x99\xe2\x86\x0e\x1e\xe5\xf5\xbf\xb5\xf1\xc2\xe7\x4f\x16\x2d\xf8\xbc\x2b\x79\x04\x0d\xf2\xd5\xed\xe1\x40\x7f\x09\x24\x0d\xd5\xd7\x55\x05\xa5\x15\xa4\x5f\xa1\x01\x9f\x44\x1b\x28\x40\xcf\x51\xa9\xd4\x47\x3f\xd2\xcd\xb1\xf4\x7f\xc9\xcf\x61\x99\xb0\x81\x2b\xf4\x1c\x65\x4a\x7b\x22\x60\x71\x44\xa6\x29\xa5\x2b\x95\xc6\x29\x6f\x36\xd0\x0b\x94\x96\xa1\x5a\xdf\x30\x7a\x13\x58\x19\xe7\xff\x62\x58\xc1\x76\x5c\x1a\xa0\x1f\xd1\xff\x7d\x18\xac\x8c\x43\xd0\x42\xac\xfa\xe8\x77\x34\x40\xbf\x13\xc4\xee\x1f\x19\x43\x00\x5c\x88\x0c\x41\xa4\xd4\x47\x5f\xef\x79\x70\xd4\xdb\xea\x63\x5f\x9a\xf4\x85\x89\xf7\x8b\x04\x59\xe3\x7e\x62\x86\x8b\x22\xac\x06\x1b\x8c\xc7\x59\xcc\x53\xfa\xb6\x61\xcd\xd8\xba\x14\x46\x2e\xfb\x5b\x6d\x87\xef\x57\x7e\x79\xdb\xe1\x4b\xc6\x17\xd3\x2e\xf3\xf5\x8c\xfc\xfb\x5b\x6d\xa7\xc9\x80\x77\x12\x16\xe4\xaa\xbf\xaf\x29\xb8\x55\x68\x87\xc5\x13\xa7\x7a\xf9\xdd\xc7\xc4\x51\xa7\x32\x31\x11\xbb\xd3\x60\x40\x26\x43\xcb\x0c\x6f\xcf\x07\x2b\x66\xcf\x89\xcc\x66\x4f\xe7\x25\x37\x03\x3b\x8b\x6c\xed\xb1\x80\x6a\xfc\xad\x5d\xcc\xfe\xf9\x31\xd9\xe8\x62\xfb\x89\xc5\x19\x42\x3b\x18\x0f\xfb\xc1\xe0\x0b\x8b\xab\x39\x8d\x87\xb0\xa4\x08\xcd\x88\xf9\x86\x97\xbd\x9d\x37\x44\x04\x72\x88\x07\x60\xe6\x04\x5f\x35\x6b\x39\xb0\x70\xa1\xad\xec\x13\x00\xcc\x98\x47\xac\xfa\xde\xce\x9b\xea\x76\x44\x63\x95\x83\x01\xd5\xce\x1b\x87\xc1\xcf\xcc\x63\x2e\xc3\xcc\x0c\x73\x4c\x66\xfc\xa2\x29\x0b\x41\xc5\x05\x12\xfa\xe8\xba\x67\x56\x42\x79\xd0\x42\x6a\x28\x0f\xbd\x3c\x8f\x51\xfe\x0e\x5f\xa7\x59\x82\x83\xe9\x66\x34\x64\xbd\x73\x58\x47\xc6\xcc\x2c\x56\x80\xab\xb0\x06\x5c\x42\xf6\x11\x9e\x62\x08\x32\x0e\xc6\x98\x74\x9e\x58\xac\x4c\xf0\x9f\x8f\xf0\x55\x46\x5f\xbb\xc5\x77\x7c\xf1\x86\xc5\x4c\x85\xd6\xab\xe9\x24\x1c\xe0\x12\x47\x41\xdc\xd4\x0b\x5c\x5c\xf6\x93\xda\xac\x6d\xe1\x7f\xca\xac\xdd\x61\x74\xc1\x70\x78\x1c\xa6\x4b\x8f\xed\x37\xa3\x9b\x13\xd9\xa1\x3e\x1e\xc4\x53\xe6\x75\x4f\x08\x22\x8c\xe7\x69\x31\x92\x11\x5d\x2c\x24\x8e\xe7\xf4\xa6\xb4\xb0\x0b\x86\x6f\x84\x7d\x60\x83\xf3\xde\x85\x0c\xd6\x72\xf1\x4a\x37\x1a\x57\xc3\x31\xd3\xe6\xe5\x67\xc8\xec\x7a\xe1\x3c\xd2\x88\xd2\x68\x03\x85\x17\x6c\x0a\x6b\x9e\x95\x18\x5f\x60\xb4\xf7\x0b\x9c\x3f\xd3\x79\x3f\xc5\xff\x3d\xc7\x51\x96\x73\x7a\x06\x7c\x85\x03\xc3\x42\x03\x68\x13\x1f\x63\x42\xec\x49\x20\x7f\x8c\xca\x31\x1d\x68\x28\x58\x12\x40\x2a\x48\xef\xca\xea\x2a\x62\x33\x22\xdf\x39\xb3\xe5\xe6\x47\x8d\xa1\xa6\xe7\xd2\x42\x10\x22\xc1\x88\x46\xe1\x1c\x6d\xd1\x0b\xc3\x82\x8b\x13\x3b\x6f\xf2\x0c\xae\xf9\xa6\xb3\x4c\x9c\xba\x4e\xf3\x51\xf8\xf8\xde\x85\x0f\xf4\x9f\xb3\x04\xa7\x38\xb9\xc0\x54\x0c\x89\xe7\x44\x94\x57\xc4\x0f\x50\x63\x04\x59\xd8\x9f\x30\x0e\x8c\xb6\x12\xf4\x26\x09\x83\x08\xbd\xa5\xee\x99\x68\x14\x4e\x30\x8e\x06\xd5\x01\x80\xe0\x21\x9f\x21\x02\xb6\x41\x3f\x27\x47\x50\xe4\xbf\x82\x08\xed\x26\xf3\xfe\x35\xfa\x3c\x26\xff\x54\x2f\x71\xff\x3f\xcf\xa7\x41\x38\xa9\x0e\xe2\xa9\x5b\xde\x39\x39\xe2\xcd\xe5\x88\x3d\x6a\xa1\xc2\xd2\xcf\x13\x99\xef\x25\x1a\x90\x83\x02\x4d\x99\xf4\xf4\xc9\x13\x32\xe8\x40\x7a\x22\x1d\x12\x28\x89\xa8\x52\xa8\x0c\xb3\x4e\x7f\xfd\x89\x56\x57\xe3\x0b\x9c\x8c\x26\xf1\x25\xa9\x03\x1b\x5f\x9d\xa7\x03\x25\xf5\xea\x9d\xf2\x8f\xa4\xec\x2b\xf1\xb9\xa1\x7e\x5e\x37\xbf\x36\xd9\x1e\xc6\x1a\x03\x3c\x01\x15\x02\x56\xb4\xbb\xba\x8a\x78\xb3\xa8\x5f\x27\x45\x00\x65\x68\xba\xf6\x4a\x54\x69\xc8\x2a\xa2\xcc\x13\x40\x80\x16\xa2\xa5\x9a\x7a\x29\x56\xec\x09\xa0\xc2\xca\xdd\xc0\x7f\x09\x41\xaa\x25\x9e\x3f\xef\x37\x95\xef\xf0\x1f\x5e\x86\x16\x79\xfe\xbc\xdf\x78\xf5\xd4\x5f\xe0\xf9\xf3\x7e\x9d\x7d\x27\xff\x85\x8e\xf3\x46\xe1\xe1\xf9\x06\xf4\xfc\xf5\x6b\x96\x0f\x52\x7d\xdd\xa0\x2a\x40\xed\x2d\x43\xc8\x6e\x49\x54\xab\x5d\xd5\xea\x4c\xeb\x27\x8b\x32\xae\x47\x0a\x91\x97\x37\x26\x75\xb0\xe5\x51\x1a\xd0\x7f\x75\x1a\x61\x2f\xe9\x0d\x12\x27\x25\xf9\xb2\xcc\x08\x46\x99\x82\xd5\x55\x44\x76\x09\xb8\x89\x41\xa1\xb2\x90\xe8\xe2\xb1\x56\xda\x4a\x8a\x00\x5e\x8a\xe2\x68\x72\x4d\x97\xe3\xd6\xaf\x07\x47\x5b\xe8\x33\x7a\x8d\xd6\x01\x26\x6f\xb0\xee\xc2\x82\xde\xc5\xe9\x9d\x65\xdf\x78\x7f\xf9\x5a\xd2\xce\x02\x62\x5d\x55\x3d\xaf\xff\x42\x99\x73\x59\x91\xd3\x2a\x6e\xc8\x30\x76\xab\x8c\x27\x8a\x66\xf9\x80\x59\xa8\xe7\x49\x3c\xc8\x2f\xf5\x80\xd0\xe0\x6e\x24\x5f\x06\x42\xb7\x90\x83\xd0\x62\x59\x88\x4b\x07\x84\xb0\x6d\x9a\xa7\xac\xe8\x89\x29\x1a\xb1\xcf\x0a\xae\xba\xea\x79\x19\xa1\x08\x79\x04\x23\x74\x3b\xe1\x08\x2d\x29\x20\x21\x5d\x9e\xb3\x0f\x5d\x92\xee\xd5\xb3\x97\x58\x1a\xaf\x0c\xc9\x4a\x14\x57\x04\x2c\xaf\x88\xa5\x14\x5e\x42\xd2\x6a\x3d\x4a\x5a\xdf\xbb\xa4\xe5\x91\xaf\x3c\xea\x9d\x93\xa3\x7c\x39\x67\x59\xf5\x8e\x83\xa5\x9b\xbc\xfc\x91\x89\xff\xf3\x98\x78\xee\x69\xf6\x01\x58\xf6\x5e\x34\x48\x30\x44\x6e\x60\xc0\x0d\x90\x4c\x0e\x91\x93\xfb\x02\x51\x63\x1a\xcf\x17\xb8\x2d\xff\x8a\x6a\x7f\xab\xcd\xa1\xe8\xae\xb0\xf8\xbc\x4d\xca\x2c\xb1\x0b\xb4\x1f\x77\x81\xbf\xc5\x2e\xb0\x3d\xc1\x83\x2c\x89\xa3\x70\x80\x7a\xf1\x10\xf7\xe3\x78\xb1\xc2\x7f\xbb\x97\xa7\xf0\xa7\x5f\x97\xda\x11\xb6\x7b\xba\xc2\x9f\x3c\xdf\xd7\x0e\xa0\xb2\x76\x9d\x81\xe8\xf5\xf2\xb4\x98\x04\x1f\x6d\x21\x3d\x14\x7e\x43\x7c\x2b\xfc\x78\xea\xa5\xde\x62\xbd\x19\x94\x59\x62\x1d\xff\xbd\x93\x23\xff\xcf\x59\xc7\x07\xf3\x6c\x36\xcf\x8a\x5f\xda\x1d\xe4\x5e\xda\x1d\x2c\x7f\x69\x67\x4a\x75\x07\xc6\x25\xde\xc1\x5f\x7b\x1d\xf4\xe0\x52\x9d\xad\x9b\x17\x6f\xee\x57\xb2\xcb\x69\xe8\x7b\x91\xee\xfe\x49\x27\xec\x03\xe3\x5a\xd3\x27\x44\x1d\x14\xb8\xb4\x38\x58\xf2\xd2\xe2\x31\x8b\xdd\xdf\x83\xf9\x6e\x7e\x38\xde\x43\xbf\x55\x5f\x36\x9a\xdc\x40\x1c\xa5\x19\x59\xde\xe7\xd7\x16\xf7\x9d\x05\xc3\xea\x66\x94\x86\xbf\x91\xd2\x22\x17\xdc\x2c\x18\xaa\xec\x6f\x18\x64\x81\x72\x11\xea\xbb\x00\x4d\xf5\x1b\x50\x52\xeb\x58\x1a\xfc\x6a\x06\xc0\xaf\xf4\xa2\x7d\x33\xad\x48\xdf\x97\x50\x04\x88\x62\x1e\x65\xa2\x67\x46\x30\x2b\xb0\xc5\x3b\xa4\xdf\x2c\x60\xf4\xc5\x0b\x1d\xb3\x7f\x19\xdf\xad\xd6\x68\x4c\x9b\x49\x90\xd2\xc8\x59\x68\x16\xa7\xa1\xee\x81\x4f\x1a\x25\xdf\x49\xfd\xc3\x98\x77\x56\xb4\xf0\xdc\xc0\xe8\x05\xaa\x1b\x8d\x1c\x06\x43\xf9\x0c\x03\x25\xb2\x8d\xe8\xaf\x29\x2b\x51\xdb\x92\x21\xb5\xf4\x46\x64\x48\x2d\xb5\xb4\x2b\xb8\x96\x6e\x99\xfd\xdc\x00\xc4\xed\x10\xb9\x05\xee\x3c\x72\x10\x87\x49\x11\x6f\x71\xa6\x24\x9c\xd7\xa6\x8a\x2a\xf0\xc5\x68\xe6\xcf\x9c\xd2\xe7\x92\x8e\xe6\x0b\x72\xfc\x65\x7d\x97\x17\x41\x0a\x0a\x6c\x5f\xb1\x3c\x24\x0c\x30\x9e\xde\x3e\x7d\x72\xe3\xe4\x9b\x7c\xb9\x5c\xbd\x6c\x34\x97\xe2\x9d\x77\x4b\x4c\xf6\xc8\x3b\xbf\x15\xef\xdc\x3b\x3e\x40\x10\x12\xb7\x18\xeb\xdc\x63\x01\x74\xef\xca\x3a\xff\x72\x76\x28\x97\xc4\x02\x7e\xe8\x60\x55\x34\x1d\x80\x3b\x02\x5d\x35\x09\xa2\x61\x3c\x2d\x59\x1c\xb0\x5c\xae\x1a\x92\x52\x3e\x1c\x96\x3a\xec\xd4\xe2\x72\x8d\xd6\x59\x85\x80\x7b\x64\x54\x26\xa3\xe2\xc4\xb9\x14\xa3\xfa\x7b\x67\x5e\xf8\x1f\xc5\xa8\x56\xf7\xb6\x7b\xe8\xe5\xda\xcb\xb5\x17\x75\xc4\x68\x03\xed\xe3\x6c\x1c\x0f\x51\xc3\xc7\xad\x20\xb4\xf7\x6d\xb9\xd5\xe6\x70\x48\xfd\x07\xf5\x05\x51\x80\x0b\xf0\xd5\x4b\x6a\xd3\x3f\xbe\x68\xb5\x06\xfe\x0f\x4e\x62\xc8\x1d\x96\x8d\x31\x4a\x70\xaa\xf0\x45\xad\x23\xa4\x1c\xeb\x31\x79\xb6\xf0\xbe\x15\x2f\x60\x0b\xf1\x0f\x86\x83\xbe\x1a\xbd\xcd\x03\x68\x0a\xcf\xbd\xb0\xe3\x08\xa3\x69\x9c\x60\x2a\x3c\xbe\x78\x01\x7d\xf3\x8d\x22\x5f\xef\x2f\x5e\x14\x5c\xe0\x30\x9f\xcb\x2c\xf0\xb5\xbb\x45\x39\x7f\x5c\xe0\xdf\xec\x14\x87\xa2\x38\x9e\x15\x13\x43\x3e\x70\x72\xf4\xae\x6c\x41\xec\xfe\x35\x21\x8b\xe4\xd1\x9c\x68\x6a\x29\xa2\xbb\x5b\xb8\xd9\x47\xa2\xfb\x56\x44\xf7\x7f\x14\xe6\x97\x4f\x72\x0a\x0f\xfc\x0b\x85\xdf\xc2\x07\x67\xf5\x7c\x6b\x09\xc0\xa5\x52\xbe\x08\x5c\x46\x5f\xbf\x9a\xaf\x6e\xb5\xc5\xb8\x7b\xbc\x38\xae\xc0\xea\x2a\xfa\x48\xe0\xeb\xf5\x42\x2b\x52\x00\x68\x16\x44\x99\xcb\x71\x38\xc1\xa8\xf4\x43\x49\xfa\x5a\xcb\x18\xdc\xe0\x71\x68\xc5\xdc\x16\x26\x9c\x96\x22\x33\x14\x5b\x12\xd2\x55\x94\xa6\x63\x37\xc4\xe3\x2d\xb2\x7b\x29\x14\xb4\x14\x2f\xf9\x7b\x3b\x6e\x39\x72\x74\xd1\x24\x59\x0f\xcb\x57\x64\x26\x24\x68\xed\xaf\xcf\xf3\xf1\xb0\x49\xc2\x8b\xc5\xc4\xb6\x62\x5e\x8b\x2f\xc7\xbb\x9b\x75\x19\xeb\x99\x3c\x29\x1f\xed\x44\xe0\x2e\x07\xd1\xc3\x20\x4d\xc9\x42\x7e\x41\x50\x1b\xa2\x77\xf8\x1a\x6d\xe1\x24\xbc\xa0\x39\x21\x77\xf8\xa0\x34\xf2\x63\x4e\x1f\xbe\x79\xb7\xb5\xd3\x90\xad\x89\xe7\x82\x89\xc7\x7b\x71\x34\x0a\xcf\xe7\x2c\x13\x65\x0c\x59\x21\xd3\xbc\xfc\x92\x49\x3c\xc3\x49\x76\x8d\xfe\xa4\xc7\x62\xf0\x26\x05\xe6\x7b\x32\xa6\x39\x8e\x53\xf2\x10\x46\x2c\x5d\x40\x16\x0b\x5f\x9a\x2a\xda\xc2\xa3\x60\x3e\xc9\xba\xa8\x85\x4a\xf5\xc6\x3a\x24\x52\x2e\xfb\xe0\x7b\x12\x9a\xe3\x84\x27\x32\x97\xe0\xc8\xf8\x2f\x42\x33\xcc\x58\xf2\xcc\x14\x40\xc9\x43\xbd\xf2\x21\x8b\xd1\x0c\x27\xa3\x38\x99\x2a\xc0\x35\xc8\x4a\xfa\xc7\xc1\xe8\xbc\xeb\x1b\x65\x44\x2f\xbe\x8e\x21\xe6\x4c\xbd\xb1\xbe\xda\x6c\x18\x21\xb8\x69\x57\x28\xea\xc6\x27\x89\x90\xd6\xf8\x4d\x39\x2f\x21\x69\x5e\x02\x79\x32\x2b\x43\x49\x5a\x7c\xbd\x2d\xce\x22\x7a\x00\x7c\xee\x86\x74\x55\xcd\x18\x4a\xc6\x6f\xe0\xa2\x1b\xee\x6f\x36\x8a\x13\x38\xc5\xc8\x46\xef\x21\x31\xe8\x97\xe1\xc8\x4a\x1a\x4f\xa9\x9d\x9f\x1e\x35\x33\xac\x65\x2a\xfe\x29\x27\x6b\x9d\xa6\x9f\xbc\x33\x98\x8a\x3e\x8d\xb5\x5a\xcd\x04\x9c\x93\xbd\x7e\x30\x3a\x77\x1b\x5e\x90\x89\xd8\x10\x3f\x39\xe1\x91\xe2\xbe\x60\x18\xf6\x7a\x87\xeb\x0a\xea\x41\x57\x94\x05\xdd\x26\xdf\xec\x8c\xc1\x06\x6a\xe1\x0f\xd5\x82\x95\xd3\x60\x92\xa1\x4d\xf8\x67\xf9\x44\xb4\xdc\x8d\x46\xf1\x6b\xbf\x0b\xd9\xd1\x44\xea\xc3\x51\x95\x45\x25\x29\xf1\xce\x54\x00\x3f\xef\xa4\xb2\xe2\xea\xbc\x1a\x35\x97\xca\xed\xa2\x4f\xbd\xd3\x80\x30\xcc\x3c\x49\x61\x99\x97\x3d\xf8\xee\x33\x5a\x25\xe4\x43\x79\x50\x45\xcc\x8e\xdb\x2c\xd1\x9f\xa0\x1c\x64\x53\x3a\xd8\x34\xdd\xbc\xa5\xcf\x71\x85\x7a\x02\x39\x79\x2f\x1a\xe2\x2b\x57\x8d\xd3\xda\x15\x53\x00\x39\xa2\x75\x2e\x08\xd1\x25\x50\x11\xc2\xb2\x78\xe3\xcd\x5f\x2f\xb1\xe1\x95\xe4\x1b\x6f\x25\xbe\xe5\x6d\x90\x59\xa9\xb2\x27\x97\x11\x86\xdc\x5a\x68\x51\xf9\x62\x81\x91\x85\xfe\x91\x09\xea\x46\x07\x79\x5c\xa4\xd7\x1c\x1f\xa7\x71\x81\xe8\x24\xcb\x73\xcc\x93\x65\x03\x05\xca\x34\xbe\xb2\xd7\xe6\x9c\x21\x96\xd1\x5b\xa6\x06\xb6\xbf\x2f\xce\xc6\x00\xf0\xb5\x21\x76\x8e\xae\x5d\x5c\x64\x31\x92\xaf\x58\xc7\x3d\x88\xec\x89\x31\x76\x83\x0e\xd5\x68\x76\x0c\xac\x03\x0b\xcd\x96\xa3\x4e\x6d\x39\x94\xe9\xf3\x1a\x73\x20\xe0\xe7\x5a\x13\x30\x7a\x62\xa4\xd5\x8f\xae\xb1\x2e\x32\xde\x68\x51\x28\x28\x57\x67\xf9\xe8\xab\xef\xdc\x01\xab\x94\x26\x7e\x3b\x38\xd2\xbb\x03\xae\x53\x0e\x8f\x6b\x6b\xdc\x3e\x53\x1b\x98\xcf\xdc\x06\x46\x99\xcd\x57\xe8\x73\xce\xe8\x91\x3f\x59\xe3\xf4\x33\x98\xc3\x58\x1d\x39\xfd\x6c\x9a\xc5\xf0\xbf\x1b\xfb\xb5\x19\x70\x8a\xfc\x29\xcc\x81\xe9\xa6\xa1\x51\xd7\x94\x18\x4c\xe2\xb4\x76\xf6\xfc\x79\xbe\x49\x91\x02\x5c\x39\xfa\x72\xbe\xe1\x08\x62\xc6\xf6\x32\x59\x2f\xcf\x80\x52\x3d\x46\xdc\x69\x43\x2f\x12\x6c\x26\x77\x23\x5f\x72\x13\xbf\x2f\xd1\x32\x4c\x5d\xe9\xf6\x17\x47\xaf\x71\x88\x06\xf7\x10\xc4\x86\x8a\x08\x42\x32\xa4\x42\xa1\x4f\x4c\x58\xae\x5a\x05\x79\x64\xd3\xbb\x80\xc9\x95\x4d\x65\x90\x1d\x71\x94\xf4\x09\x30\x15\x64\x0a\xaa\x6c\xd8\x75\xb1\x98\x14\x5a\x20\x3c\xdd\xe4\xd9\xa2\x51\x68\xee\x40\x3d\x66\x0a\x5d\x9e\x13\xf6\xe6\xac\xb2\xf6\xf7\xf6\xa1\x5f\x22\xad\xfb\xe2\xe4\xe8\x0f\xab\x3b\xf2\xa6\xd7\xf6\x65\xbd\xfe\x27\x68\x97\x8e\xc1\x38\xb3\xc7\x8d\x77\xa9\x12\x49\x7d\x99\xa7\x47\x12\x78\x1c\xe1\x79\x1a\xf4\x27\x98\x85\x03\x53\xd0\x39\x46\x6a\xaa\x45\x0a\xc5\x7c\xf3\x16\xe9\x19\xd6\x94\x6d\xe1\x08\xb2\x29\x23\x66\x68\xcb\x6c\x8c\x6d\x4d\x92\x28\x0f\x31\x56\xc2\x14\x05\x88\x26\x60\x46\x17\x38\x49\x21\x6a\xd9\x38\xc8\x50\x84\xcf\x27\x78\x90\xe1\x21\x61\xc3\x03\x96\x52\x35\x63\x0a\x9f\x2c\x46\x93\x30\xcb\x26\xf8\x05\x0d\x70\x59\xd5\x81\xe2\x24\x89\x13\x34\x8c\x71\x1a\xad\x64\x28\x18\x8d\xf0\x80\xd6\xa5\x48\xad\xa4\x28\xc5\x83\x79\x12\x66\xd7\x15\x51\xb1\x3f\xcf\x50\x98\x41\x25\x5e\x23\xcc\x52\x11\x50\x21\x9c\x84\x19\x73\xe2\xa6\x79\x5d\x43\xc2\x9f\xa7\x38\xa2\xfb\x41\xea\x52\x94\xd1\x01\x79\x4f\x3b\x27\xd4\x65\xc6\x5b\x75\xfe\x6e\x9b\xb4\x2d\xff\x90\xf2\x4e\x35\x83\xf6\x1e\x30\xa4\xf5\x36\x9c\x1a\x2e\xf2\x4e\x0b\x21\x3b\xa1\x91\xdd\x0b\x7b\xcf\x69\xbf\x89\x76\xc9\x2f\x47\xe2\xb8\x77\xa7\xb5\xb3\x0a\x2a\xbd\x3b\x6d\x9e\xb1\x60\x01\xe8\x2b\x79\x64\x57\x01\xf5\x4e\xd9\x91\x44\xee\xdd\x69\x9d\x56\xaa\xe9\x95\x9a\xf9\x95\x1a\xb4\x52\x5d\xaf\x54\xcb\xaf\xd4\xa4\x95\x1a\x7a\xa5\xba\xa8\xa4\xd7\x71\x65\x47\xb2\x86\x8c\x7b\x19\xfa\x06\xad\x27\x06\xad\xe7\x1e\x34\x1b\x1f\x65\xb8\x58\x9f\xe8\x85\xc9\x68\xc4\xd3\x0e\x52\xa4\x69\x90\xd5\x5a\x8d\x7c\x71\xf5\xd7\x9e\x88\xa6\x0e\xb9\xee\x84\xdc\x28\x04\xb9\xe6\x1d\x78\x05\x86\x01\xb9\x59\x08\x72\xdd\x37\x3b\x15\x05\x86\x01\xb9\x66\x40\x5e\x3c\x91\xbd\x20\x49\xae\x51\xdf\x4c\xa7\x4a\xa7\xaa\x4f\xe3\x5f\xd8\x9a\x8c\x8c\x4e\x3e\x61\x3d\xe9\x75\x9a\xe1\x29\x1a\xc5\xf3\x04\x65\xe1\xd4\x9c\xfb\x25\x83\xf2\x46\xf8\x2a\x3b\x26\xab\xcf\x1f\x3f\xd6\x11\xf1\x76\x3f\x1e\x86\xa3\x6b\xca\x09\x29\x1d\x16\xc0\x62\xdd\x8f\x45\xef\x94\x3a\x0e\xfc\x76\x0a\x29\x2f\x21\xda\x8a\x95\x29\xce\x95\x24\xf7\x17\x94\xe2\x6c\x3e\xd3\x3f\xe4\x78\x74\x2c\x3e\xec\xef\xfd\x42\x5d\x3b\xf2\x4e\xf8\x7b\xbf\x7c\xaa\xa1\x0d\xb4\xf7\x8b\x9d\x1a\x4d\x29\x52\xa7\x45\xea\xce\x68\xc6\xea\x92\x86\xa9\x4c\xe7\xfd\x0b\x4c\x44\x05\xdf\xd1\xbf\x46\x83\x1f\x43\xdb\x34\xfa\xf1\x57\x44\x9f\x7c\xd1\x8f\xd5\xe2\x2c\xcc\xb1\x28\x2f\xaf\x43\xdd\x61\x8e\x45\xb3\x0d\xd1\x6c\x5d\x6b\xb6\xbe\xa8\xd9\xba\xde\x6c\x7d\xb9\x66\x21\x8c\x4e\x58\xe3\x4b\x90\x00\x09\x1b\xfa\x0a\xf4\x55\x6d\x42\xd5\x06\x5f\xcc\x50\xb5\xa6\x2f\x53\xcf\x8c\x30\xb2\xce\x63\xad\x08\xa8\xb5\x46\xcf\xf5\x66\x6c\x7f\xfa\xb1\x4e\x3f\xd6\x9d\x1f\x1b\xf4\x63\xc3\xf9\xb1\x49\x3f\x36\x9d\x1f\x5b\x79\x6d\xb6\xf3\xda\xec\xe4\xb5\xb9\x26\xda\xcc\xd1\x48\x15\xe2\x3c\x68\x79\xee\x83\x8a\x71\x20\x64\x2b\x29\x54\x3f\xa2\x7b\x49\xee\xea\x55\x5e\x2b\xd2\x47\x21\xce\xac\x17\x71\xf7\xce\xbf\xbd\xc3\xe0\x4a\x2f\x33\xe0\x42\x7a\xe9\x63\x1a\x6a\xe8\x37\x20\x42\x54\xfa\x8d\xcc\x3d\x5f\x25\xf0\x2c\xf6\xde\x57\x66\xc5\x3a\xad\xd8\x60\x15\xd7\x8c\x8a\x6d\x6f\xc5\x06\xad\xd8\x62\x15\xeb\x46\xc5\x35\x6f\xc5\x26\xad\xd8\x39\x13\xa8\x69\x15\xeb\xb2\xe2\x9d\x76\xb1\xbc\x28\xf5\x14\x11\x1e\x3b\xfe\x98\xa5\x64\x67\xc1\xe3\xe1\xf1\x36\xd1\xe3\x39\x1c\xc6\xe0\x04\x1c\x57\xfc\x78\x27\xbe\x4e\x27\x3c\xa4\xe4\xe8\x15\xde\x74\xc7\xf9\x5e\x74\x2a\xf5\x0b\x3b\x1e\x79\x73\x2b\x3f\x86\x17\xf4\x4b\xa7\xb5\xda\x6c\x98\x6a\x39\xb1\x4c\x04\xc1\x96\x0a\xba\x42\x69\xeb\x43\xfb\xa2\x88\xa0\x86\xc1\xcf\x71\x70\x81\x51\x3c\x19\x7a\x59\xed\x12\xf2\x43\xef\x13\x9d\xdc\x9e\x19\xef\x50\x6b\xb1\x17\x4c\x06\xf3\x09\x59\x61\x11\xbe\xf4\x36\xdb\x63\x89\x60\x7a\x34\x11\x4c\xed\xaa\x35\x6c\xc2\xff\xa1\xe7\x5c\x42\x33\xf3\xb5\xf4\x58\x5e\x98\x1e\xcd\x0b\x53\xbb\x62\x35\x9a\x10\x53\xbe\xc7\x05\xd4\x5a\x19\xbd\x46\xa5\xde\x27\xe5\xf9\x3f\x50\x1d\x75\x51\xad\x6c\x43\x6c\x30\x88\x0d\x0a\x91\x01\x6c\x31\x88\x75\x03\x62\xbd\x00\xc4\x26\x83\xd8\xb4\xba\x55\xa2\xed\x68\x10\x1b\x05\x20\xb6\x18\xc4\x96\xb3\xd7\x4d\x03\x62\xb3\x00\xc4\x36\x83\xd8\x76\xf6\xba\x65\x40\x6c\x15\x80\xd8\x61\x10\x3b\xce\x5e\xb7\x0d\x88\xed\x02\x10\xd7\x18\xc4\x35\x67\xaf\x3b\x06\xc4\xce\x42\x88\x52\xec\xa7\x40\xb5\xea\x6b\x66\x75\xd3\x3b\x46\xd0\x34\xd9\x7d\xce\x5f\xdc\x61\x11\x91\x52\xe7\x57\xc0\xab\x43\xd2\xb5\x9e\x23\x09\x07\x4f\x97\x9f\xcc\x07\x19\x1a\x87\xe7\x63\x14\x44\x43\x34\x89\x2f\x51\x90\x9c\xcf\x21\xfc\x0b\xb8\x39\xff\xf7\x3c\x48\xac\xc4\x3d\xd0\x40\x80\x36\x48\x2b\x5c\x8a\x73\x28\x0f\xce\xfb\xb4\x08\xdd\x25\x9c\xc7\x27\xde\x67\x0d\x83\x04\xa7\xf3\x49\x86\xe2\x51\x5e\xf3\x63\xba\x05\x94\xce\x03\xf4\x13\x3a\x0f\xa8\xeb\x4a\x7d\xad\x8c\x9e\x23\xfa\xaa\xcf\x5e\xb5\xe1\x55\x1f\x5e\xb9\x90\x9c\x50\x40\x4a\x57\xe8\x91\xf0\x27\x74\x7e\x05\x33\x5c\x06\x82\xe0\x05\x84\xd8\xa9\x14\x70\x25\x82\x21\x1d\xfa\xed\xe0\x08\x41\x38\x49\xf5\xe3\x5b\xca\xe1\xce\xc7\xe8\x77\x74\x3e\x29\xca\xe4\xdc\x4a\x95\xdf\x18\x8b\x7b\x4b\x59\x5c\xa9\xf4\x56\x6e\xdf\x64\x27\x7b\xab\x88\x05\x65\x56\xa0\xa3\x17\xe8\xc8\x02\x26\x3d\xff\xc6\xb8\xe1\x5b\xca\x0d\x4b\xb4\x19\xb9\xdf\xbe\xe5\xfc\x0f\xf6\xdb\xe7\x88\xb4\x66\xc3\x68\x30\x18\x0d\x0e\xa3\xae\x23\x50\xb7\x30\xac\xe9\x05\x6a\x79\x18\x36\x19\xf4\x26\x87\xde\xd0\x31\x6c\x18\x18\xd6\x1d\x18\xb6\x18\x8c\x16\x87\xd1\xd4\x11\x68\x5a\x18\x36\xf4\x02\x8d\x3c\x0c\xdb\x0c\x7a\x9b\x43\x6f\xe9\x18\xb6\x0c\x0c\x9b\x0e\x0c\x3b\x0c\x46\x87\xc3\x68\xeb\x08\xb4\x2d\x0c\x5b\x7a\x81\x56\x1e\x86\x6b\x0c\xfa\xda\x99\x46\x22\x02\xc3\x8e\x81\x61\x5b\xc3\xb0\x50\xe2\x8f\x94\x27\x9d\x10\xba\xd6\x02\x69\x27\x16\x5d\x77\x51\x58\x19\xbe\xca\xd4\x7b\x27\x55\x93\xca\x43\x29\x68\x69\x1c\xe8\x6d\x91\x7d\x7f\x35\x9b\x04\x04\x9b\xab\x0c\x79\xc1\xb1\x38\x33\x25\xd9\xb2\x0b\xa2\xb8\xb8\xca\x53\xea\xea\xc9\x3b\xd4\x92\xe5\xbc\x3b\x28\xb5\x60\x61\x63\xe4\x8a\x7e\x37\xd2\x6d\xb7\x2a\xf2\x52\xa4\xdb\xee\x54\xd8\x5d\x49\xb7\x53\xbf\x39\xab\xac\xfd\xbd\x23\x11\x3e\xde\x57\x3d\xde\x57\x3d\xd8\x7d\x95\xb1\xc4\xe5\x7d\x8e\x79\x93\xf3\xf7\xba\xc3\xb9\xaf\xac\x70\xef\xc4\xd1\xfc\x9d\x7e\x34\x7f\x77\xdb\xa3\xf9\x3b\xfd\x68\xfe\x2e\xef\x68\xbe\x48\xc1\xfc\x78\x53\xf5\x78\x53\xf5\x78\x53\xa5\x7d\x79\xbc\xa9\x7a\xbc\xa9\x7a\xbc\xa9\x92\xcd\x3e\xde\x54\x99\x1f\x1f\x6f\xaa\x3c\x8f\x8f\x37\x55\x8f\x37\x55\x8f\x37\x55\xf0\xf7\x78\x53\x55\x4c\x89\xfb\x78\x53\xf5\x78\x53\xf5\x78\x53\xa5\xfc\x3d\xde\x54\x3d\xde\x54\x3d\xde\x54\x3d\xde\x54\xfd\x4f\xbe\xa9\xba\xb7\x3b\xaa\xdb\xdd\x4e\x15\xb9\x97\x2a\x70\x23\xf5\x50\x77\x51\x7f\xef\x7c\x28\x8f\x77\x51\xff\xfc\xbb\x28\xf5\xee\xa8\xd7\x5a\xe8\xe8\xa4\xde\x1c\xf5\x5a\xca\xb5\x11\x3c\x3c\xfc\x9d\x11\xf5\xd2\x14\xb7\x46\xee\xa0\x02\xdc\x43\x3b\xef\x5a\x09\xdc\x38\x55\x8f\x62\x25\x66\xba\xad\xaf\x88\xc2\x0c\xa5\xfd\xf8\xca\x86\x73\x2c\xd0\x39\x56\xaf\xe9\xf8\x9f\x4b\x9a\x6c\xb4\x3b\xfe\x43\x39\x3b\x74\x87\x8b\xd5\xb8\xef\xf0\xb5\x4b\x8f\xab\xb7\x58\xe1\xfe\xe3\x0b\x1b\x66\x83\x42\x86\x80\x47\x95\x08\xd1\xbf\xd4\x71\xf2\xa8\x0e\x59\x25\xb2\xb5\xf1\xb1\x3f\xd5\x00\xd9\x91\xd0\xb4\xcf\x56\x50\x34\xd7\xd9\x9f\xf4\xa2\xf4\x19\x3d\xa7\xe3\xf3\x9c\x37\x5a\x46\xff\x82\x5e\x79\x62\x29\x5c\x06\x33\x37\xce\xb0\x6f\xd8\x1a\x02\x65\x02\x8e\xdd\x8e\xf1\xe4\x35\x99\xf1\xc5\xd3\xd3\x73\xaa\xf8\x59\x56\x0d\x41\x34\x9f\x59\x96\x59\x01\xe8\xce\x6a\x39\xae\x09\x01\x2d\x88\x95\x7f\x9d\x4c\x8f\x5b\x65\xa8\xb5\x2c\x9c\x9c\x1b\xed\x8e\x47\x21\x52\xf3\x2a\x43\x9c\x8d\x16\x55\x8c\x28\xeb\xc9\x50\x8c\xc8\x41\x0b\x8d\x2f\x9f\xe5\x70\x2e\xcc\x00\x0f\xca\x41\xbd\xfa\x17\x15\x4f\x63\x3e\xc4\x6a\x8a\xe8\x32\x8a\xa8\x4a\x2d\x72\x2c\xa2\x10\x34\xe8\x34\x61\x1c\xa3\x4a\xed\xbb\x46\xc2\x1e\xc2\x75\x12\x6d\x0e\xc1\xfa\x89\x55\x12\xaa\xfe\x5e\xef\xec\x57\x52\xb7\xc4\xd6\x14\xa9\xc2\xf0\x3a\x93\x79\x0d\x22\x33\x8f\x81\x71\x7c\xfa\x08\x71\x50\x1c\x37\x5a\x92\xd4\x43\xeb\xec\x4e\xc6\x42\x9b\x2b\x26\x96\x69\xd8\x7d\xaf\x72\x6f\xaf\x75\x1f\x42\x6f\xaf\xb5\xb4\xc4\x6b\xef\xb1\x86\xb8\xdb\x6b\x39\x63\x5b\xc0\x0d\x4d\x88\x87\xb7\xd8\xe1\xb7\x92\x78\xa6\xed\xf2\xec\x05\x0c\xc2\x37\x88\x8a\x37\x24\xcd\xe9\x81\xe6\x0c\x3d\x3f\x99\x78\x52\x4a\x84\x9a\x43\xf5\x97\x0d\x15\xac\x19\x6b\x8e\xa0\xae\x44\xfd\x32\x56\x31\x01\xd5\xd5\x41\xe8\x11\xe3\x0a\x09\x31\xa4\x0d\x5e\x30\xff\x0e\x83\x8c\x67\xce\x06\x2e\x0c\x5f\x08\x5e\x64\x17\xff\x19\x36\xf3\x17\x2f\x9c\x7b\xf8\x12\xec\x1e\x2d\x48\x80\xf4\x1d\xad\x36\x32\x44\xf7\xb3\xe2\x00\xd2\xf2\xab\x8e\xd1\x7c\xfe\xca\x23\x85\xf2\x4f\x9a\xbd\xd6\x43\x1d\x33\xef\x96\xae\xef\x5b\x9e\x2f\x1f\xec\x14\xf8\x6d\x83\x38\x13\x56\x85\x53\x9c\x5c\xe0\xa7\x4f\x4a\x83\x32\x6a\xd4\xea\x0d\xd4\xbf\x46\xbd\xff\xef\xff\x1d\x26\xe1\x00\xed\xe3\x34\x0a\x27\x55\xb4\x39\x99\xa0\x24\x3c\x1f\x67\x29\x62\xe5\x87\xd5\xa7\x4f\x9f\x1c\xe1\x61\x98\x66\x49\xd8\x9f\x03\xfc\x20\x1a\x42\x50\x9e\x30\x42\x69\x3c\x4f\x06\x18\xde\xf4\xc3\x28\x48\xae\x09\x3b\x98\xa6\x15\x16\xa5\x21\x81\x7f\xe3\x79\x86\xa6\xc0\xd3\x07\xc0\x59\x2b\x28\x48\x30\x9a\xe1\x64\x1a\x66\x19\x1e\xa2\x59\x12\x5f\x84\x43\x3c\xa4\x41\x27\xc8\x3a\x1d\xc5\x93\x49\x7c\x19\x46\xe7\x68\x10\x47\xc3\x90\xae\x61\x52\x69\x8a\xb3\x2e\x5b\xf1\x2f\x90\x8e\x56\x0a\x8a\x61\x8a\xcf\x20\x1e\x62\x34\x9d\xa7\x19\xd9\xa8\x83\x30\x02\xa0\x41\x3f\xbe\x20\x9f\x66\xd7\xd0\x45\x14\xc5\x59\x38\xc0\x15\x1a\x57\x68\x12\xa6\xa0\x59\x56\xdb\x8b\x86\x06\x32\xc3\x30\x1d\x4c\x82\x70\x8a\x93\xaa\x0f\x87\x30\x52\x07\x82\xe3\x30\x4b\xe2\xe1\x7c\x80\xef\x1d\x0d\xc4\xba\x36\x8c\x07\x73\x11\x07\x83\xd4\x58\x8d\x13\x16\x23\x63\x1a\x64\x38\x09\x83\x49\x2a\x87\x19\xe6\x06\xaa\x29\xa8\x93\x79\x3e\xd9\xdd\x3b\x46\xc7\x07\x3b\x27\xbf\x6e\x1e\x6d\xa3\xbd\x63\x74\x78\x74\xf0\xcb\xde\xd6\xf6\x16\x7a\xf3\x6f\x74\xb2\xbb\x8d\x7a\x07\x87\xff\x3e\xda\x7b\xbb\x7b\x82\x76\x0f\xde\x6f\x6d\x1f\x1d\xa3\xcd\x0f\x5b\xa8\x77\xf0\xe1\xe4\x68\xef\xcd\xc7\x93\x83\xa3\x63\xf4\x6c\xf3\x18\xed\x1d\x3f\x83\x0f\x9b\x1f\xfe\x8d\xb6\x7f\x3b\x3c\xda\x3e\x3e\x46\x07\x47\x68\x6f\xff\xf0\xfd\xde\xf6\x16\xfa\x75\xf3\xe8\x68\xf3\xc3\xc9\xde\xf6\x71\x05\xed\x7d\xe8\xbd\xff\xb8\xb5\xf7\xe1\x6d\x05\xbd\xf9\x78\x82\x3e\x1c\x9c\xa0\xf7\x7b\xfb\x7b\x27\xdb\x5b\xe8\xe4\xa0\x02\x8d\xda\xd5\xd0\xc1\x0e\xda\xdf\x3e\xea\xed\x6e\x7e\x38\xd9\x7c\xb3\xf7\x7e\xef\xe4\xdf\xd0\xde\xce\xde\xc9\x07\xd2\xd6\xce\xc1\x11\xda\x44\x87\x9b\x47\x27\x7b\xbd\x8f\xef\x37\x8f\xd0\xe1\xc7\xa3\xc3\x83\xe3\x6d\x44\xba\xb5\xb5\x77\xdc\x7b\xbf\xb9\xb7\xbf\xbd\x55\x45\x7b\x1f\xd0\x87\x03\xb4\xfd\xcb\xf6\x87\x13\x74\xbc\xbb\xf9\xfe\xbd\xb3\x97\x04\x77\xad\x8f\x6f\xb6\xd1\xfb\xbd\xcd\x37\xef\xb7\x69\x4b\x1f\xfe\x8d\xb6\xf6\x8e\xb6\x7b\x27\xa4\x3b\xf2\x57\x6f\x6f\x6b\xfb\xc3\xc9\xe6\xfb\x0a\x3a\x3e\xdc\xee\xed\x91\x1f\xdb\xbf\x6d\xef\x1f\xbe\xdf\x3c\xfa\x77\x85\xc1\x3c\xde\xfe\xdf\x1f\xb7\x3f\x9c\xec\x6d\xbe\x47\x5b\x9b\xfb\x9b\x6f\xb7\x8f\x51\x69\xc1\x90\x1c\x1e\x1d\xf4\x3e\x1e\x6d\xef\x13\x9c\x0f\x76\xd0\xf1\xc7\x37\xc7\x27\x7b\x27\x1f\x4f\xb6\xd1\xdb\x83\x83\x2d\x18\xe8\xe3\xed\xa3\x5f\xf6\x7a\xdb\xc7\xaf\xd0\xfb\x83\x63\x18\xad\x8f\xc7\xdb\x15\xb4\xb5\x79\xb2\x09\x0d\x1f\x1e\x1d\xec\xec\x9d\x1c\xbf\x22\xbf\xdf\x7c\x3c\xde\x83\x41\xdb\xfb\x70\xb2\x7d\x74\xf4\xf1\xf0\x64\xef\xe0\x43\x19\xed\x1e\xfc\xba\xfd\xcb\xf6\x11\xea\x6d\x7e\x3c\xde\xde\x82\xd1\x3d\xf8\x00\x5d\x3d\xd9\xdd\x3e\x38\xfa\x37\x01\x4a\xc6\x00\x06\xbf\x82\x7e\xdd\xdd\x3e\xd9\xdd\x3e\x22\x03\x0a\x23\xb5\x49\x86\xe0\xf8\xe4\x68\xaf\x77\xa2\x16\x3b\x38\x42\x27\x07\x47\x27\x4a\x1f\xd1\x87\xed\xb7\xef\xf7\xde\x6e\x7f\xe8\x6d\x93\xaf\x07\x04\xca\xaf\x7b\xc7\xdb\x65\xb4\x79\xb4\x77\x4c\x0a\xec\xd1\x66\x7f\xdd\xfc\x37\x3a\xf8\x08\x5d\x26\x73\xf4\xf1\x78\x9b\xfe\x54\x28\xb6\x02\x33\x89\xf6\x76\xd0\xe6\xd6\x2f\x7b\x04\x6d\x56\xf8\xf0\xe0\xf8\x78\x8f\xd1\x09\x0c\x59\x6f\x97\x0d\x77\xf5\xe9\x93\x9f\x56\x75\x9d\xd7\x7e\x90\x8d\xef\x57\xef\x55\x2c\xea\x34\x0d\x7c\x2c\x8a\xd0\xc7\x42\xd6\xd9\x70\x61\x17\x44\x59\x8a\xb2\xa0\xcf\x25\x16\x52\xe5\xd3\x1f\x13\x67\xb0\x4d\x29\x47\xd5\x2a\x08\xd5\x2b\x08\x35\x2a\x08\x35\x2b\x08\xb5\x2a\x08\xb5\x2b\x08\x75\x2a\x08\xad\x55\x10\x5a\xaf\x20\xf4\xb2\x82\xea\xb5\x0a\xaa\xd7\x2b\xa8\xde\xa8\xa0\x7a\xb3\x82\xea\xad\x0a\xaa\xb7\x15\x0b\xcb\x35\x5a\x97\x7c\x23\xf0\x48\x79\x02\xa3\xde\xa6\x70\x49\x3d\x68\xeb\x25\x83\xdf\x60\x30\xea\xd0\x86\x84\xd3\x64\x6d\xb5\x18\x2e\x2f\x19\x8c\x75\x05\xcf\x35\x06\xab\xc3\x70\xa9\x53\x98\x75\x35\xd6\x72\x9d\xd5\xe5\xb8\xd4\x28\x0c\xc0\x83\xe3\xd9\xa4\xb0\x08\xfc\xba\xda\x6f\x15\x4e\x8b\xd5\x6d\x33\xdc\xd7\x18\x8c\x86\x82\x67\x9d\xc1\x5a\x67\xb8\xb0\x7e\xd7\x9b\x67\xe5\x57\xea\x5c\x24\x0b\xe6\x82\xe3\xb1\xa6\x8c\x55\x83\xc1\xe4\x38\x77\xf4\xf1\x80\xbe\x35\x8d\xbe\x77\x58\x9d\xa6\x84\x05\x75\xdb\x12\x67\x0e\x83\x8f\x07\xb4\x55\x37\xfa\x0e\x85\xda\x4a\x07\xd7\x18\x82\x1d\x39\xb8\x02\x48\x43\x19\x68\x8a\xac\x04\xb4\xce\xea\x28\x83\x05\x13\xd3\x96\x83\x2b\x60\x34\x95\x81\xa6\xc8\x2a\x08\x35\xd8\xc8\xd6\x14\x60\x7c\x34\xd6\xc4\xec\x09\x0a\x45\x6c\x74\x28\xb2\xfa\x6c\xa4\x8b\x56\x06\x45\x91\x8d\x15\xa0\xa7\xb6\xc4\x69\xab\xa9\x8c\x67\x47\x7e\xd3\x68\x7a\xad\x02\x9f\x60\xa8\x38\xbd\xbe\x94\xb4\xc7\x69\xaa\xde\x56\x86\x75\x8d\x95\xd5\xe6\xa3\x2e\x89\x40\xcc\xc5\x4b\x56\x90\x13\xcf\xba\x52\x86\x23\xbe\x06\xbf\xd5\xb3\x94\x58\xcb\x2d\x59\x95\xb7\x2f\xd6\xbc\xba\x26\xd6\x35\x90\x12\x14\x5f\x9f\x6d\x49\xfb\xa2\x9f\x0d\x89\x82\x18\x27\x46\x32\x14\x2e\x32\xa6\x64\xd1\x02\x61\x88\x69\x83\xdf\x96\x08\x40\x3f\xd7\xe4\x42\x84\x06\x5b\x0c\x91\x8e\x81\x74\x53\x1f\x7c\xd1\xe9\xba\x84\x23\xc6\x4e\x2c\x68\xf8\xae\xc1\x11\x0c\xa4\xae\x0c\x52\x47\xb6\x2b\x16\x1e\x5b\xc0\xf5\xa6\x63\x3e\x44\x07\x0c\xc4\x39\x20\xb1\xe0\x1a\xca\xbf\x6d\xb1\x8a\xf5\x01\x6a\x3b\xca\xb5\xf4\x99\x11\x33\x29\x3b\x85\xea\x75\x74\xa6\x65\xc9\xfe\x34\x26\x2b\xc4\x31\x1f\x48\x84\x6a\xae\x55\x50\xed\xaa\xbd\xb9\xde\x58\x7b\xf9\xf2\x25\xf9\xdd\xd9\xde\x7a\xb9\xfd\x66\xb3\x4e\x7e\xaf\xef\xd4\xdf\xbc\xe9\x6d\xf5\xc8\xef\xcd\x97\xed\xe6\xce\x56\x6b\x5b\x9f\xef\x71\xe2\x6d\xa0\x5d\xdb\x6c\xac\xbf\xd9\xee\x40\x03\xbd\xd6\xd6\x56\xbd\xd1\x82\x06\xb6\xd6\x6a\xcd\xed\x9d\x26\xf9\xbd\xb6\xd9\xd9\x5a\xeb\x6c\x43\xc3\x1c\xa1\x33\xa7\x3e\xe0\x68\xef\x70\x7b\x7f\xab\xde\xa9\x41\xf8\xfd\x05\x3a\x24\x51\x56\x6a\x91\x94\x57\x74\x57\xbe\xed\x5d\x11\x55\x26\x02\x12\x9e\x20\xd8\x9d\xb5\x56\xbb\xd1\xac\xc1\x08\x6e\xef\xf4\xb6\x36\xdf\xac\x43\x07\x5f\xae\xbf\xd9\xdc\xea\xed\x6c\x93\xdf\xf5\x5a\xb3\xd1\x6e\xad\xc1\xe0\xf4\x9a\x5b\x8d\xed\xfa\x4e\xed\xcc\xab\x1a\x2f\xaa\x94\x77\x2a\x76\x0b\x7b\x29\xd5\x73\x6e\x6a\x16\x9b\xe3\x53\x2c\x40\xf7\x2a\xcd\x22\x3d\xd7\x37\xfb\x9f\x94\xd2\xfc\xf2\xe0\x93\x6d\xc8\x84\xf2\xee\x54\x94\x7a\x68\x03\x95\xec\x02\x88\x1a\x80\x2a\x8d\x49\xc3\x07\xe5\xe5\x72\x46\xa5\x16\x40\x66\x57\x6a\x00\xb4\xad\x4b\x6d\x70\x39\xaa\x31\xb4\xc8\xd6\x79\x17\x89\xfb\x07\x42\x8a\xde\x2b\x47\x60\x00\x9f\xc6\x13\x7f\x81\x04\x0a\x24\xde\x02\x20\x7e\x7e\xfa\xc3\x0f\x01\x64\xa2\x4f\x7f\xf8\x21\xc0\x36\xfd\xff\xb3\xf7\xf6\x5b\x52\xdb\xd8\xa2\xf8\xdf\xe1\x29\x34\xf3\x5b\x03\xd5\x74\xd1\x6d\xc9\x5f\x32\xd0\xf9\x5d\x42\xe0\x74\x6e\x20\xb0\x80\xb9\xe1\x2c\x16\x64\x64\x5b\xee\x72\xa8\xae\xea\x53\xe5\xa6\xab\x93\x90\x75\x5f\xe3\xbe\xde\x7d\x92\xbb\xb4\x25\xdb\xb2\x2d\xc9\x55\x4d\x93\x33\x99\xa1\x67\x0d\xa9\x2a\x49\x7b\x6f\xed\x2f\x6d\x7d\x6d\xfd\xb4\xb6\x43\x80\x41\xe3\xa7\xf5\xca\x9c\xd1\xfa\xf0\x50\x58\xd9\x7b\x31\x69\xfe\xc0\x56\xa5\x88\x8e\x0d\x9b\xb4\x6c\x3e\x45\xe9\x7c\x8a\xb2\xf9\x14\xe5\xf3\x29\xe2\x73\x03\x22\xb6\x9a\xa2\x74\x35\x45\xd9\x6a\x8a\xf2\xd5\x14\xf1\x55\x1f\x19\x13\xa4\x30\x41\xf0\xf1\xf0\xca\x48\xba\x82\xa4\xe3\x50\x88\xfb\x85\x99\x28\xcc\x64\x21\xe9\x17\xe6\xa2\x30\x97\x85\x7e\xbf\x10\x26\x0c\x5c\x16\x06\xfd\xc2\xe6\x99\x6a\xd6\x7d\x97\xba\xee\x52\x7f\x57\xd0\x78\x94\x10\xfe\xbb\x7f\x84\xb0\xd1\xb6\x2b\x61\x3e\x6c\x8e\xf6\x5b\x9b\xda\xff\x65\xfe\xa6\x7c\xfb\x76\xef\x37\xd3\x25\x06\xb8\xb5\x73\x1f\x47\x7b\xbf\xde\xf8\xaa\xeb\x1a\x05\x0e\x54\xe0\x49\x3a\x9f\x66\xf3\x69\x3e\xdf\x43\xfb\x68\x36\x37\xdf\xbd\xf9\x88\x9a\x05\xb9\xf2\xbe\x4f\xe4\x52\x9b\x01\x1a\xe9\x43\x1b\x70\x7e\x00\x2d\xa0\x56\x68\x7e\x1f\xda\x40\x54\x03\x68\x51\x60\x85\x16\xf4\xa1\x0d\x64\xab\x41\xfb\xf5\xf0\x50\x41\xa4\x9e\x15\x62\xd8\x87\x38\x50\x08\x64\x4e\x93\x2e\x84\x58\x19\xc5\x25\x4a\xd0\x6a\x59\xcd\x27\xd5\x74\x2d\xc4\x6a\xba\xb4\x01\x3a\x50\xed\xf3\xb9\x59\xe4\x60\x11\x03\x93\x12\x7f\xa0\xb7\xb9\xa9\x04\xd4\x1d\xf0\x0a\x9b\xc4\xc6\x6b\x40\x60\x2f\xa9\xa9\x35\x98\xd9\x60\x27\xb1\x21\x95\xad\xd0\xbe\xa6\xad\xab\xab\x6b\x6b\x38\x49\x57\xd3\x6c\x35\xcd\x57\xc0\xf1\xd5\xa7\x69\x6b\xd0\x87\xf6\xa9\xda\xda\x85\xf6\x49\xda\x4a\xfa\xd0\x3e\x59\x5b\x71\x1f\xe2\x35\x6b\xeb\x0a\x76\xad\x1d\xea\xba\xb2\xa8\x2b\x78\xd4\x95\x49\x5d\xc1\x11\x9b\x4a\xc0\x45\x4b\x75\x5d\x59\xd5\x15\x06\x00\x53\x6b\x18\x1a\x86\x27\x34\xfa\xae\xfc\x3b\xfd\x39\x06\x88\x21\xe1\xd4\x6f\x2f\xc2\x14\xff\x1c\xa1\xc9\xb1\x3c\x9a\x9b\x09\xcf\x9c\x1b\x7a\x7a\xac\x8e\xf0\x1e\xcb\xe3\xb7\xb9\xa8\x67\xe2\xc8\xb1\x3a\xa6\x7b\x2c\x0f\xd2\x72\x51\x8f\x19\xeb\xf9\xaa\x1e\x1c\x96\x85\x11\x21\x35\xd6\x0b\x54\x3d\x38\x98\x9c\x8a\x7a\x99\xb1\x1e\x1c\x60\xee\xb0\xa5\x1f\xd6\x3e\x56\x4f\x6b\x7c\xc2\xf1\xac\x9c\x55\xac\x09\x86\xc4\x17\xc3\xc0\x3f\xfe\x0c\x63\x5d\x73\xf1\x4d\x59\xad\x5f\x2d\x2b\xf0\x78\x12\xe6\xe2\x5b\x56\x31\x79\x6a\xeb\x36\xa2\x06\xe8\xd0\xe6\x09\x2f\xaa\xc1\xa3\x8d\x50\x7f\xd0\x99\x07\x79\x3e\x7c\x85\x18\xa9\xf7\x16\xe5\x61\xa6\x16\xa4\x88\x26\xc3\xb7\xe8\xb7\x23\xf9\xb0\x70\x7b\x46\xa2\xa9\xf1\x37\xe4\x93\xbe\xb6\xb6\x90\x26\x93\x49\x5b\x75\x1f\x09\xff\x20\x40\x26\x7b\x02\x54\x20\xec\x16\x07\x96\x00\xba\x6e\x2a\xd9\xd1\x06\xcf\xda\x8f\xdb\x07\xcf\x03\x60\x2a\x70\xee\x01\x1b\x0b\x9c\x4d\x1d\xd5\xdf\xe9\x68\xdf\xc3\xac\xdf\xd8\x81\xc3\x31\x86\x67\x3b\x0e\x0f\x61\x26\x88\xe0\x75\x17\x79\x21\xcb\x78\x70\xea\x4c\xce\xbc\x86\xaf\xb9\xb8\xd5\x12\xac\x5b\x8f\xd1\x0d\x8a\x73\x8c\x8e\x90\x1e\xbe\x7f\xda\xfc\x2d\xdc\x6a\xfa\x66\x9e\x91\x1d\xc3\x54\xec\xd8\x70\x99\x04\xb9\xe6\x60\xc7\xcd\x75\xbd\xe3\xce\xf4\xea\x78\xe7\x79\x95\xd4\x90\xe3\xce\x9c\xea\xd8\x3a\x99\x1a\x3f\x0a\xf7\x42\xee\x84\x4b\xe1\xaa\x17\x2c\x72\x60\x76\xb7\xaa\xda\x31\xef\x09\xa8\xe3\xa6\xb2\xf9\x72\xe1\x76\x50\x70\x94\x40\xd4\x6a\x57\x17\xe0\xab\xfd\x18\x84\x2c\xfe\x69\xa0\x24\xb2\xdd\x50\xd7\x14\x99\x50\xda\x39\x17\x05\x1f\x3f\xca\xdd\x7f\xa4\x9f\x88\x2b\xf0\x64\x33\x45\x97\x53\xf4\x8b\xe9\x99\x8f\xc9\x64\x03\x37\x3b\x2f\xe1\xdf\x5f\xda\xd7\xda\x3f\x0e\xe0\x10\x37\x9c\xc9\x66\xef\xe6\xe4\x72\x4f\x5e\x27\xff\x5d\x7c\xf9\x65\x6f\x6f\xef\x9e\x0d\x9a\x3f\x0a\x4d\x00\xfa\x5d\x40\x6c\x49\xb3\xc0\x0a\xc6\x61\xdd\x04\x08\x40\xdb\xe5\xde\xcd\xc9\xef\x40\x9c\x1d\x62\xb8\x0d\xcf\x04\xd3\x7e\x6b\x41\x59\x60\x41\x28\xb1\x99\x2e\x8c\x90\x36\xf7\xef\x2f\x80\xaa\xcd\xd7\x5f\x7f\x3d\xf1\xc9\x9d\x85\x4e\x94\xfc\xe0\x3c\x0d\x53\x1f\x86\x91\xef\xc0\x6d\x77\x18\xc6\xfa\xda\x8f\x3a\xdf\x02\x67\x9e\xea\xcf\xd5\x52\x7a\xa6\x21\x18\xcb\xfb\x3c\x96\xda\x57\x7d\x98\x47\x59\x46\x7b\x92\xa5\x5e\xc0\x9b\xdc\x52\x24\xde\x32\x9c\xc2\xb1\xb7\xba\xa8\xa9\x35\x1d\xb7\x19\x2e\x0e\xf6\x8e\xda\xd4\x15\xb6\x3b\xaa\x54\x0b\xe7\xf8\xe9\x83\x87\x7f\x80\x68\x1c\xcd\xdf\xf3\x4b\x68\xba\xe6\xd9\x8a\x57\x96\xb7\x93\x2c\x02\x85\x27\x07\xaf\x51\xa0\xf2\x21\xc3\x46\x34\xc7\xa7\x2c\x6b\xc5\xa3\x1f\xb1\x32\x48\xa8\x53\x79\x28\xa5\x53\x96\x19\x24\xf5\xd5\x47\xb9\x0f\x6c\x39\x1a\x55\xd7\x34\xbf\x4e\xf4\xf1\xed\x34\x8e\xbf\x1c\x71\xfa\x57\xb8\xb2\xf2\xb9\xb7\xee\x7b\x89\xd5\x34\xc4\xd6\x94\x69\x2f\x8f\x1f\xdc\xc1\x5b\xec\x64\x0c\xdf\xaa\xbe\xce\xfd\x8b\x23\xb8\x7d\xda\x6e\x61\x94\x8b\xb2\x9a\x18\x12\x50\x75\xb7\x34\x78\x91\xe5\x2c\xa5\x89\x21\x37\x93\xb7\x49\x68\xca\xf2\xac\xe0\x9d\x3d\x0e\x53\xc5\xcc\xcf\x09\xc7\x85\xd7\x2d\xfb\xf4\x2d\x10\x5b\x84\x6e\x0e\xbe\x87\x2b\xe8\x03\x00\xdb\xac\x3d\x9b\x97\x8b\x45\x51\x6a\x5e\x2c\x86\x80\xd1\xbc\x54\x0c\xd3\x55\xf3\x42\xb1\x28\xe2\xcd\x32\xf1\x80\x52\xeb\x3a\xb1\x75\x4d\xd8\x32\x5b\x80\x75\x1f\x24\x6f\x98\x5a\x72\xc1\xfc\x28\x03\xff\x6e\x0a\x8c\xee\xdd\xd3\xfa\xaf\x5e\x50\x32\x03\xaa\xef\x39\xfc\xf8\xa6\x44\x77\x90\xff\x16\xbd\x53\x1f\x69\xfb\x11\x07\xda\xe7\xc8\xf6\x76\xa4\x22\x69\xb2\x80\xcb\xb1\x72\x6e\x09\xd3\x07\x1f\x9b\xd3\xd4\x98\x67\x42\xb0\xb4\x34\x61\x02\x48\x08\x40\x98\x9c\xc9\xc4\x70\x41\x96\xa3\x7d\x40\x64\x5b\x68\x44\xf7\x11\xf1\xac\x5c\x83\x65\xb3\xc9\x24\x45\x37\x51\x26\xe3\x5c\xf1\x31\x07\xc8\xde\x26\x64\x72\x17\x76\x64\x89\x0f\xdd\x47\xc1\x18\x8a\x14\xbd\x43\x19\x7a\x87\x72\x09\x39\xe2\x79\xc2\x53\x66\x4a\x3a\xd4\x83\x1c\xed\x40\xbc\xa4\x5d\x7c\xca\x54\x2f\xee\x20\x6f\x13\x7b\x3c\x08\x7c\x12\xd8\x71\x1d\xde\x6e\xd0\x51\x6f\x0f\xdd\x3e\xdc\xba\x2f\x02\xbe\x1f\x26\xb9\xcf\x49\x7f\x95\x07\x59\x44\x2a\xec\x25\x37\x2d\xf7\xa1\x23\x94\x99\x96\xf8\x10\xa0\xbc\x7f\x1f\xf9\x9e\xea\x25\x88\xdf\xf8\xb6\x28\x3a\x42\x26\x3a\xd8\x76\xb7\xb5\xb6\x5a\x0c\x54\x8b\x68\xf5\x62\x1b\xeb\xdf\xf0\x46\x9d\x85\x40\x58\x30\x1c\x64\x3e\x41\x9d\x45\x40\x58\x2c\xcc\xcc\x75\x7c\x7d\xa1\x30\x37\xd7\x09\xf4\x45\x42\xde\xaf\xf3\x65\x81\xef\x9f\x75\x81\x4f\xc4\xc2\x07\xc5\x7c\xb9\x5c\xe9\x6b\x6e\x87\x30\x50\xab\xbf\x4f\x42\x02\xb9\x10\x5a\xc8\x23\xeb\x74\x83\x65\xba\xcf\xb4\x42\xb7\xe3\x3a\x90\x71\xb9\xee\xcf\xb8\x1a\xf4\x65\x09\x61\xb0\x18\x20\xc2\xe7\x9d\x56\x0f\xa0\x81\x6b\xe1\xa0\x1b\x90\x77\xd7\x0c\x44\xd9\x97\xe5\x82\x6b\x5d\x2e\x00\x79\x6c\xb1\x52\x60\x16\x4b\xbb\x48\xa0\x44\x63\xbf\x36\x25\x2a\xd8\x97\x05\xe8\x9f\x3a\xc1\xc6\x7a\xc6\x48\x18\x7d\xee\xdc\x18\x0a\xcb\xbf\xcf\xf2\xc1\x60\x79\x40\x9f\xc3\x93\x30\xea\xcc\xe2\xb5\x5b\xd8\xfd\x55\x01\x42\x82\xed\xd6\x05\x44\xc5\x0e\x4c\xf8\x2e\x81\xff\xa1\x6b\x03\x19\xf6\xc2\x84\xe7\x54\x4c\xf9\xfd\x28\xce\xf2\xd0\x8b\xe1\xb3\x17\x7b\x79\x8e\xe1\x73\x11\x7b\x3c\x4c\x7c\xf3\x9a\x41\x51\x64\x9e\x97\xfa\xb0\xb8\x10\xd1\x90\xe2\x10\xcb\xcf\x41\x91\xd0\x82\x01\x80\x94\x17\x2c\x28\x58\xb0\xc3\x72\xc1\x56\x91\xa7\xe6\xf6\x15\xeb\xb4\x96\x8e\x5b\xb4\xe0\x51\x9b\x70\xe6\xce\xd1\x30\x78\xb1\x6c\x2c\x7d\x19\xa2\x47\x46\x5c\x42\x82\x5d\x07\x69\xd1\x64\x64\x98\xee\x58\xc7\x60\xa0\x26\xc4\x7c\x89\xfd\xcb\x50\xfd\x09\x43\xb5\x90\xca\x76\x83\xb5\x51\x38\x9d\xe1\x5a\x0a\xc8\x39\x60\x13\xd2\xbf\xea\xac\xdd\x6b\x56\xc3\xd1\xdd\x38\x11\x03\x78\xf2\x65\x5d\xff\xbf\x67\x60\xfe\xf3\x5d\xcb\xfb\x4e\x3e\xe2\x50\xfe\xd2\xdc\xca\x45\xab\xe5\xf9\x22\x47\x59\xf7\xbe\x9e\xd6\x83\xe3\xfe\xd3\x29\xdf\x77\xb7\x01\xea\x85\x5a\xde\xc2\x90\x25\xa6\x08\x06\xe9\x5b\xca\xe5\xfa\xf9\xaa\x3c\xe5\x93\x85\x71\x18\x5b\xff\xd7\xaa\xfa\xa1\x9e\xe7\x8b\x2f\x93\x45\x7f\x9e\xd9\x2c\x04\x4b\x71\xa2\x23\x44\xee\xd5\x9f\xef\x1f\x49\x08\xf5\x0f\x8e\xb5\xe1\xbf\x4c\x16\xe8\x6f\xaa\xda\x9e\x75\xbd\x50\xd9\x68\xc1\xe6\x6b\x3e\x7e\x2a\xb0\xbf\x3e\x56\xcf\xc7\x57\xe7\xdd\x19\xae\x81\x2d\x27\xbc\x7a\xbc\x62\xf0\x99\xcd\xbf\x29\xab\xb5\x81\x41\xcd\x16\xfe\x02\xdd\x41\x93\x05\x64\xf6\xdc\x43\xb7\x3b\x8b\x1f\xfd\x95\x2c\x0d\x57\xbd\x4a\xad\x67\x66\x87\xdf\x40\x20\xbd\xfc\x3d\x17\xb3\x72\xce\xd1\x44\x95\xdd\x47\xea\x48\x66\x9f\x8b\xad\x34\xad\x8c\x6e\x40\x50\x2b\x97\x8f\xdf\xc8\x4a\x90\x76\x74\xc0\x08\xd0\x85\xb3\xe5\xc5\x64\x31\x45\x18\x1d\x22\xb2\xb7\x45\xc6\x76\x04\x2f\xa1\xec\x02\xd6\xdf\x33\x26\xcf\x96\x20\xf6\xf7\x47\x96\x42\x17\x9d\x1a\x75\x84\x34\x69\x61\x5e\x7d\x8f\x4d\x04\xde\xdb\x45\xd3\xc3\x08\xfd\xb3\xef\xb4\x1d\x1f\xac\xe7\x65\xc6\x27\xde\xde\x97\x5d\xaf\xad\x77\xbd\x06\x45\x05\x14\x85\xa6\xa2\x13\x28\x1a\x6c\x18\x41\xcc\x02\x45\xf1\x27\x6f\xa3\x45\x8e\x5c\xf7\x7f\xf4\x36\xda\x09\x3b\x3d\x65\xde\xa6\xd9\x4c\xc3\x03\xa6\x0c\x6b\xc3\x41\xe3\x49\xdd\xf2\xfe\x7d\x44\xe4\xa6\x57\xfd\xcb\xd7\x5f\x7f\x8d\xe2\xbd\x3d\x84\xde\x99\x21\x75\xff\x3a\x90\x70\x30\x80\x84\xe9\xde\xde\x76\x90\xba\xed\x7c\xa3\x7b\xe9\xf4\x04\xb7\xfd\x36\x1e\x92\xef\x56\xd6\xba\x8d\x25\xb1\x5a\xb7\xf1\xa6\xce\x37\xbd\x25\xb1\x5d\x48\xfe\x10\x52\xb2\x63\xb7\xeb\x76\xe6\x37\x09\x50\xab\x38\x4a\x88\xfb\xaa\xe7\x90\xe4\x57\xf5\x70\xdf\xb9\x61\x6a\xdb\xfd\xcc\xe0\x56\xe3\x84\xa3\x9b\xa8\x80\xc3\x6e\xbf\x8b\x8f\x27\xb6\x27\x5c\x4e\x19\x64\x98\x63\xe8\x26\x4a\xa1\x3a\x93\xbb\x83\xef\x90\xda\x27\x34\xd1\x0f\xc1\x4a\x79\x22\x08\x6f\xb6\x5a\xd5\x66\x9b\xda\x6b\x95\x47\xff\x64\x09\x4e\xb4\x12\xec\x77\x8a\x3a\x8d\xcc\x63\x5b\x83\x0c\xde\xa9\x99\x70\xd0\x71\x99\x39\x99\x43\xbb\x48\x41\x94\x25\x58\x2b\xc1\x58\x2f\x8a\xe5\xc9\x56\x59\x44\x42\xf3\x88\x07\x1b\xc8\x02\xd3\x0c\xed\xd7\x68\xf7\x05\x53\xf7\xe5\x43\x6f\xd6\xcd\x63\x68\x48\xd0\x51\xcd\x98\x7d\xc1\x5a\x13\x06\xe1\xb8\x4e\x0c\x00\x84\xaf\xeb\xe7\x69\x17\x7f\xc2\x3d\x9a\xc2\x2f\xc8\x9d\x09\xaf\x25\x60\xd3\x36\x1f\x1a\xd9\x22\xed\x67\x5b\x47\x23\xdb\xa1\x93\x4a\x30\xa2\x22\x26\x5c\xff\x2e\x5b\xa3\xb2\x4e\xa8\xea\x40\xca\xf0\xc2\x5c\x27\x52\x75\x20\x25\xf8\x89\xb9\x4e\xac\xea\x80\xcd\xcf\xbe\x6c\xc3\x7e\xd9\x86\xfd\xb2\x0d\x3b\x8c\x36\xbf\x6c\xc3\xfe\x53\xae\xf1\x86\xd1\xce\x6b\xbc\x61\x34\xba\xc6\xab\xcf\xd9\x86\x6b\xbc\x61\xf4\x65\x8d\xf7\xda\xd7\x78\xc3\x68\xdb\x35\x5e\x93\x70\xba\x6b\xbc\x20\x20\xf7\xa1\xed\x66\xef\xcc\xbc\x35\x4b\xbd\x3f\xf5\xd6\xec\x26\x0a\xfe\x90\x87\x0b\x1a\x3c\x5f\x56\x81\xbb\xab\xc0\x9b\x08\xf6\x54\x0f\x36\x51\xa0\xfd\xfe\x3a\x0a\x54\x96\x6e\xa8\x71\xa0\xe5\x89\xde\x29\xa7\x9b\xd6\xbf\x17\xc7\xcf\x7e\x7a\xf6\xf8\xf1\xcb\x47\xaf\x5e\xf6\x57\x8b\x9f\x7f\xf7\xd3\x77\x3f\x7c\xfb\xe8\xf5\xa3\xe1\xab\xdc\x2f\x9e\xfd\xfd\x87\x6f\x7f\x7a\xf8\xec\x87\x97\xaf\x1e\xfc\xd0\xb4\xd4\xd0\xc9\x65\xe5\x87\xdb\x2d\x2b\x6b\x2d\x56\xb3\x65\x9d\xb4\xa5\xb7\x26\x5d\xa3\x16\xb3\x6b\x3c\x45\x97\xb6\x54\xe5\x95\x5c\x12\xa9\xd0\x7d\x44\x82\x7b\xa8\x32\x2c\x89\x68\x7d\x7e\xb3\x41\xfb\x28\x44\xb7\xd1\xa5\xbc\x3d\x58\xd5\x97\x34\xe1\x13\xd9\x83\x95\x4a\xf4\x37\x14\x0d\x62\x11\x08\x03\xf9\xc5\x6b\x74\x84\x2e\xd1\xdf\x50\x68\x8a\x12\xf9\xc5\x7f\x0a\xa8\x04\xdd\x46\x02\x8f\x2f\xf0\xec\x19\x2a\x6f\xe4\xb2\xdc\xeb\xde\xcf\x97\xf2\xe7\xff\xb4\x2c\x05\x6b\x6c\x3b\x2b\x51\x09\xcf\x09\x18\x98\xd6\x70\x66\x23\x39\xb3\x91\x17\x34\x37\x06\xc6\x34\x55\x25\x77\xd1\xa5\xac\x7a\x69\x59\x56\x6a\x15\xa4\xcb\xc6\x4b\x78\xe0\x67\xd8\x6b\xc1\xd7\x7e\xd7\x3f\x8e\xf6\xad\xb7\xcb\xd1\xd5\x86\x27\x8f\x5f\xbe\x10\xb4\x6e\x3c\x6c\x52\x06\xfd\xdd\x09\xcb\xfa\x98\xa8\x06\x28\x6a\x65\x7d\xba\xbe\xe8\xe9\x96\xb1\xda\x93\xba\x9a\x85\x85\xea\xe5\x89\x9f\xd1\x7d\x14\xdf\x43\x3f\x3b\x56\xe6\xa0\x0f\x70\x35\xd5\x9c\x15\xa5\x46\x9f\x96\xd5\xf3\xe5\x1a\xf2\xb8\x0a\xad\x82\xc7\x72\x7f\xde\x43\x77\x90\xe9\x34\x75\x0d\x5c\x6f\x74\x1f\xa9\x7c\x11\xa6\xca\xe2\x6f\xd0\xc1\x77\x47\x08\xd0\x68\x50\x2c\xb8\xba\x27\xaa\x75\xac\x5f\x1f\x01\x5a\xfb\xe1\xea\x01\xe6\xa7\x1a\xe6\x0e\xa8\x3b\x86\x79\x4f\x43\xc0\x76\x4b\x4b\x9a\x62\x2d\xf8\xa6\x02\x05\x1a\x11\x0b\xb5\x9f\x44\x3f\x3c\x44\xcf\x57\xe5\x69\x59\x95\x1f\x38\x3a\x5b\xce\x2f\x17\xcb\xd3\x92\xcd\xd1\xf2\x03\x5f\xa1\xff\x78\x3c\x21\x7b\x77\xd1\xe6\x1d\x45\xfb\x68\xf3\x2e\x82\x7f\x43\xf8\x37\x10\x6e\xc6\x0c\x52\x69\xb4\x44\x2f\xef\x0f\xbc\x43\xde\x26\x76\x1c\x99\xb7\x10\xa7\x20\x1c\x19\xf5\x63\x64\xd3\xab\xe7\xe0\xe5\x1a\x9f\x1a\x7e\xea\x04\x63\x7d\x99\x4d\x07\xfa\xb3\xb7\xeb\x6e\xca\x1a\xec\xa7\xe2\xa7\x67\xcb\x15\x5b\x5d\x76\x5e\xa2\x13\x26\xf0\x4a\x1f\x88\xac\xbb\x94\xc6\x57\x67\xcc\xd6\xff\xca\xd8\xb3\x31\xba\x7b\x7b\x3b\xfe\x76\x3b\x3b\x7e\x67\x5f\xc7\x77\xed\xea\x5c\xff\x53\x02\xcb\xf3\xea\xec\xbc\x7a\x02\x53\xeb\x4e\x5d\x04\x41\x7a\xce\xd7\xe5\x8a\xe7\xda\x43\x03\x69\x59\xad\xeb\x84\xd0\xb2\x71\x67\xb6\x50\x37\x7e\xb6\x98\xd7\x62\xd2\x72\x70\xb3\x15\xbf\x8b\x08\x09\xa6\x88\x84\xd1\x14\xf9\x34\x98\xa2\x10\x93\x7e\x63\xf5\x66\xc1\x5d\x51\xa6\x17\xf5\x1f\x2d\xa8\x27\xcd\xd6\x77\x0b\xf4\xde\xf5\xa0\x5d\xe1\xfd\x02\x58\xa9\x85\x97\x10\xeb\xb9\x77\xfd\xed\xcd\x5b\x8b\xb7\xdf\x42\xd5\xc4\x1f\xc0\x91\x2a\xb7\xe0\x17\x8d\xda\xc1\x26\xdc\x58\x2a\x01\xa0\xa4\x79\xad\x17\x46\x80\xc8\xf3\xd0\x1d\x24\x06\xda\xe6\xa5\x04\x9d\x13\x22\x7a\xf1\xc9\xe7\xda\xd1\x33\x2c\xcc\x19\x98\x66\x5c\x3c\xab\x3b\xf1\x84\x2d\x60\xed\xa7\xd7\xb5\x43\x44\x4c\x6b\x68\xe9\x7a\xb9\x4a\xc7\xf9\xdf\x03\xff\x29\x99\x04\x9f\x92\x12\x75\x37\xc5\x04\xaf\xad\xcb\xe6\x4f\x09\xbc\x41\xdf\xaf\x2e\x7c\xbd\x2b\x99\x85\xf5\x09\x6a\x81\xde\x99\x4f\x90\x74\x12\x09\x92\xab\x64\x10\x24\x9d\xd4\x81\xe4\xea\x39\x03\x15\xc1\x78\x8c\x62\xdc\x25\x19\x5f\x89\x66\xdc\x25\x1a\xef\x42\xb5\x51\x0e\x52\xb9\x9a\xa5\x91\x72\x51\x2d\xa5\x36\x9b\x25\x3d\x67\xb0\x98\x57\x9b\xb3\x81\x15\xa2\xc6\x01\xbc\x37\xfb\xee\x08\xf8\x62\xab\x33\x5f\x5e\x20\x55\x67\x7c\x37\xe2\x85\x18\x60\xd7\x16\x1b\x90\x81\x32\xd8\x81\xfc\x28\x83\x5e\xf8\x6c\x37\x81\x57\x33\x5e\xb1\x61\xc9\x0e\xb3\x06\x0d\xd8\xd3\x52\x4c\x41\xe6\xe7\xa7\x0b\xe8\x9c\xc1\xac\x6a\x0e\xd6\x61\xf6\x14\xb5\x91\xb4\xb1\xf2\x8e\x73\x12\x1d\x47\x47\x4a\xed\x0c\xc5\x82\x48\xfc\xd5\xa1\x67\x23\x3d\x57\xdd\x27\x5a\xdd\xf9\xf2\xc2\x1a\x97\x5a\xb9\xf5\xca\x18\xe7\x98\x7a\xf2\x4a\x48\xe1\xd5\x9b\x8d\x8d\xf6\x57\x1b\xa9\x6b\x47\xd0\x03\x7b\x25\x50\xb6\x23\x20\x7d\xbb\xd3\x37\x57\x53\x03\x87\x5b\x6d\x7b\x14\x40\x97\x26\x42\x2e\x01\x4c\x0f\x5d\x9b\xe5\xaf\x36\xb8\xad\x8e\xb7\xa9\x2e\xf5\xeb\xd5\x06\xbb\xe4\xa8\xea\x3e\x69\xea\x82\x1c\x9d\xea\xbd\x3e\x5f\x81\x45\xc9\xe7\x44\x84\xaa\x8f\x6b\xf9\xab\x4d\xa0\x7c\x01\x9a\x4c\x14\x6d\xcd\xd5\x60\x85\x5f\xdd\x0f\xb6\x4d\x6f\x00\xda\x93\x06\x9a\xf4\x1a\x12\xda\x93\x1e\xb4\xa7\xe3\xd0\xfe\x50\xa3\xea\xb8\x42\x87\x7e\xa2\xbe\x4b\xb4\xa8\x29\xda\x69\xb6\xf7\x62\xb6\x44\xcf\x4b\x87\x66\x0b\x94\xf5\x9b\x8f\xf8\x9e\xf6\x55\x86\x72\xcd\xf7\x4f\x56\xf9\x0e\xe7\x1a\xb0\x2e\x35\x16\x95\xa4\x06\x8d\x39\xa4\xba\xf6\x93\xb6\xb6\xdd\x25\xc1\x60\x31\x5b\x3e\x93\x51\xca\x51\x67\x3d\x4c\xa7\xcb\xda\xd9\x17\x4b\x08\xf4\x1c\x2e\x5e\x4c\xa0\x5b\x14\xa3\x0b\x0f\x9a\xad\x4c\xea\x4e\xdf\xbf\xdf\x12\x09\xaa\x5d\xf7\x0f\x9e\xd2\xf4\x09\xba\xa3\x95\xdb\x14\x1d\x75\x4d\xa7\x81\x61\x04\xfe\x74\x47\xe0\xdd\x35\x8f\xb6\xbb\x5b\xad\x78\xf4\xbb\xac\xa8\xd2\xc0\xc0\x6a\xc7\x90\xb8\x28\xb8\x72\xcf\x9f\x8e\xe0\x78\xb2\x23\x0e\xd7\xd8\xb6\x62\x8b\xf5\xd9\x72\xed\xd4\x12\x70\xbf\xcf\xcb\x27\xd2\x30\x5e\xbd\xd1\x16\x14\x5b\x3d\xb4\x8e\x79\xb2\xe1\x36\x03\x9f\xaa\x39\x36\xfa\x59\xfd\xc7\x59\x89\x58\x05\x43\x20\xf8\x4b\x73\x4c\xf8\xca\x83\x3e\x18\x93\xb6\x36\x93\x23\xaf\x71\x00\xc6\x7a\xaf\xbc\xba\x3b\xb2\xb6\xcd\xe4\x5f\x79\x75\x67\x54\x3d\xcb\xb8\x75\x78\x88\x1e\xce\x5c\xce\x6f\xfb\x61\xfd\x8a\x43\xc6\xb8\x6b\x44\x9a\xfb\xaa\xfd\x70\x33\xae\x8c\x28\xf7\x6e\x2e\xb5\x6e\xf5\xaa\x51\xb8\xed\x9b\x6c\x70\xd3\x68\xa2\x05\x21\x7b\xdb\x0c\x80\x12\x00\xe9\x01\x20\x03\x00\x4e\x2e\x8a\xd8\x63\xb5\xbc\x70\x30\x71\xae\x59\xc3\xab\xd6\x34\xde\xa1\xc9\xef\x8a\x7c\xf9\xc3\xcd\x9a\x18\xf8\xea\xf2\x1f\x73\xcd\x6a\x5e\xb5\x26\xa4\x43\x84\x1f\x5a\x88\xf3\xe5\xc5\xa7\x2f\xd0\x7e\xb7\x34\xcd\x48\x06\xf2\xb6\x5a\x5a\x67\x19\x52\x8c\x6f\xbd\xc5\x4c\x28\x1f\x9d\xb4\x75\xa0\xd8\x0c\xb1\x13\xaf\x74\x5b\x08\x93\x74\x6c\x76\xfc\x73\x1d\x8b\x32\x2c\xd2\x5c\xfb\xa9\xa8\x41\xfd\x66\xc5\x47\xb4\x1b\x2e\x03\xdd\x86\xc5\xab\xe1\x3a\xd0\x55\xcf\x52\xe1\xab\x1c\xa5\x82\x43\x52\x19\x2f\xe7\xdd\xf3\x4e\x78\x0f\x1d\x76\xe9\xdf\x43\xb7\xfb\x3f\x00\x72\xd8\xa0\x69\x4e\x73\xfd\x93\x1c\x82\xfa\xe4\x35\x3c\x7d\x99\xb1\x26\xde\xb8\x06\x89\x0e\x8d\xa2\xd7\xab\xd4\xab\x80\x43\x98\x87\xc6\xc3\x74\x2f\xff\xeb\x9c\xf3\x5f\xf8\x10\xe8\x8c\xad\x67\xb5\x72\x6f\xf5\x16\xfd\x80\x8a\x4f\x59\x2c\x1c\x5f\x13\xda\x3e\xa4\xb7\x85\xf3\xbb\xaf\x21\xb6\xf8\xec\xab\x72\x5a\x68\xa8\x16\xe6\xf4\x80\x73\xa7\xb5\x39\x0d\x94\x5a\x9e\xd3\x41\x5d\x75\x5d\xb1\x65\x85\xbb\x13\x4f\x06\x9d\x78\x72\xd5\x4e\x3c\x19\x74\xe2\xc9\x6e\x9d\x30\x8b\x4a\xaa\xae\x32\xb2\x6a\x89\x56\xbc\x5a\x95\xfc\x03\x37\x1c\x40\x44\xea\x72\xb7\xf4\x07\x67\xe7\xeb\x59\x4d\x86\x89\x45\x86\x9a\x4f\x87\x35\x3f\x3d\x3d\xb1\xe1\xf6\x50\x83\x7a\x3a\x34\x61\xeb\x7d\xa2\x6b\x3a\x35\x69\xf7\x5f\xea\x08\xa5\xc1\x9d\x35\x97\x9d\xb6\xf0\x10\x5b\x6e\xe6\xd4\x1f\xdb\xf3\x99\x4e\xb6\x7f\x39\xae\x79\xc5\xe3\x9a\xfe\xae\x87\x35\xfd\xb1\xa3\x9a\xbe\xe3\xa0\xa6\xff\xe5\x98\xe6\x75\x1f\xd3\xf4\xb7\x3c\xa4\x69\x10\x4b\xe7\x88\xa6\xbf\xcd\x01\x4d\xdf\x7e\x0d\xbf\x39\x78\x78\x97\x06\x1f\xdf\x4e\x29\xfe\x17\x39\xae\xd9\x4f\xb0\x13\x62\xf2\x87\x9d\xe1\xac\xd3\xed\x08\x9c\x7f\xae\x74\x3b\x57\x3a\x6d\xa9\x8a\xdb\xd3\x9e\x75\x9d\x9d\x12\xf2\x84\x98\x74\x8e\x85\x84\x98\x58\x8f\x99\xd0\x2d\x13\xf2\x88\x8a\x9d\xa3\x26\x54\x65\xb5\x08\x31\xb9\xb6\x2b\xc4\x7a\xf7\xad\x39\x79\x06\x87\x1c\xbc\x4d\x96\xa6\x69\x92\x87\xf9\x54\x4b\xd8\xb3\x37\x35\xd5\x8c\x48\xc2\x48\x42\x98\x9e\xce\x67\xcf\x90\xb7\xc7\xd0\x34\xc1\x61\xe2\xe1\x90\xe9\xd9\x7f\xcc\x48\x70\x48\x0a\x9e\xc9\x9c\x41\x75\x6e\xa0\x2d\x91\x44\xb1\xef\x93\x28\x92\x69\x85\x54\xe6\x20\x33\x12\xca\xd3\x20\x60\x34\xd6\xf3\x0a\x6d\x89\x24\x4f\xbd\x8c\x70\x2f\xd7\xd3\x10\x99\x91\x04\x71\x1a\x06\x14\xe7\x7a\x92\xa2\x5e\x68\x7a\xdd\x59\x8a\x84\x3e\x5d\x31\x4b\x11\x8e\xbe\xa4\x29\xba\xa6\x98\x88\xee\x9c\xa6\x48\x34\x19\x8b\x8b\x74\x9f\x31\x8c\x8c\xe8\x97\x34\x45\xd7\x1f\x1b\xd1\x6d\xd3\x14\x19\x85\xd3\x8d\x8f\xe8\x68\x9a\x22\x9f\xba\xd3\x14\x89\x61\xfc\x2e\x25\xa6\x68\x89\xfc\x8b\x44\x4b\xff\xd2\x97\x5b\xae\xf7\x62\xcb\x67\xba\xb2\x72\xf5\x20\x4a\x16\x35\xdd\x55\x80\x7e\xaa\x4f\xf0\x1a\xde\xba\xe9\x1e\xf2\x3d\x60\x67\x67\xf3\xcb\x89\xfa\x71\x8a\xd8\xea\xe4\xfc\x94\x2f\xaa\x75\xff\x4d\x1e\xfd\xfa\x4c\x4b\x0f\xa4\x52\x6a\x51\xf4\xd0\x7b\x9b\x80\x50\x46\x8a\x04\xe2\x8a\x3c\x26\x94\x71\x42\xf6\xa6\xc3\x7a\x31\xf6\xe3\x20\x48\x20\xcd\x20\xf1\x79\x11\x85\x59\xae\x87\x06\x83\x06\x69\x98\x79\x45\x9a\x15\xf0\x00\x42\x16\xe4\x7e\x4a\x0a\x13\x60\x9e\xa4\x61\x9e\xb2\x10\x5e\xcf\xc6\x34\xc9\xd3\x34\x73\x02\xf6\x93\x30\xca\x48\x98\x42\x38\xe3\x07\x34\x0d\x7d\x6a\x02\x1c\x26\x05\xc6\xb8\x00\x8a\xd3\xc8\x0b\x73\x0f\x27\x4e\xc0\x09\xf1\x0b\x4a\x18\x3c\xb9\xcd\x0a\x9c\x04\x45\x92\x9a\x00\xb3\x14\x67\x21\xcf\x81\xe2\x9c\x45\x39\xc5\x98\x3a\x01\xe7\xd4\x8b\x19\x93\x3c\x66\xbe\xe7\x7b\x24\x30\xf2\x18\x13\xea\x87\xa9\x7c\x33\x22\x08\x63\x2f\x2a\x52\xee\x04\x4c\x02\x1f\xd3\x30\x85\xb7\x23\x02\xce\x83\x94\xd0\xcc\xc8\x8a\xd0\xcb\xe2\x3c\x83\x07\xc4\xf3\xb0\x28\xd2\x80\x13\x27\xe0\x98\xa4\x3c\xcc\x63\x60\x45\x41\xe2\x94\x26\x91\x51\x78\xd4\xcb\x79\x8a\xe5\xe3\x15\x7e\x8a\xa3\x24\x4a\xb1\x9b\xc7\x69\x9e\x79\x91\xcc\x50\x49\xc2\x2c\xc6\xc4\x0f\x4d\x80\x33\x9c\xa4\x05\x96\x04\x64\x45\x94\x90\x28\x09\x9c\x80\x79\x90\xa4\x51\x92\x01\xef\x12\x5e\xe0\x80\xe5\x46\x1e\xf3\x22\xe5\x41\x4c\xe1\x19\x71\x9f\x06\x05\x09\xb9\xef\x04\xec\x15\x19\x4e\xf2\x0c\x1a\xd0\x94\x66\x79\x98\x1a\x29\x26\x81\x97\x31\x9c\x65\xf0\x48\x7b\xcc\xb2\x24\x8b\x42\xb7\xf0\x72\x9e\x90\x2c\x02\x03\x09\x13\x92\x7a\x24\x36\x02\x0e\x58\x1c\xd0\x80\xc1\x1c\x21\xe2\x2c\xe2\x01\x75\x53\x1c\x66\xa9\xc7\x92\x1c\x28\x49\xf3\x00\x17\x69\x1e\x18\x4d\x3a\x2a\x12\x4a\x73\x00\x4c\x7d\x8c\x43\x3f\x75\x53\x9c\x50\x9f\x87\x38\x24\x60\xd2\x3c\x8a\xf2\x82\x99\x0d\x84\xfa\x38\x8b\x22\x88\xf0\x49\x9e\x06\x3e\xc1\x9e\xdb\x57\x78\x9e\x4f\xe2\x8c\xca\x37\xdf\x8b\x94\x60\xdf\xa8\x6e\x69\x11\x26\x71\x91\xa9\xfc\xa6\xbc\xf0\x38\x77\x6b\x45\x16\x71\xcf\x4b\x0b\x50\x7c\x3f\x67\x94\x16\x99\x51\x2b\xf2\x90\xc5\x09\x0e\x00\x70\xe2\x7b\x8c\xc5\xc4\xcd\x0a\x2f\xca\x58\xe4\x87\xf2\x79\x17\xcf\xf3\x29\x31\x1b\x08\x0e\x48\x42\x12\x39\xf7\xf2\x98\xc7\x23\x1e\xbb\x59\x41\xe2\x34\xf6\x18\x05\xe7\x12\x44\x39\x21\x45\x61\x34\x69\xc2\xb1\x60\x13\xb0\x2c\xcc\x48\x94\x25\x24\x72\x02\x0e\x72\x92\x45\x79\x01\x5a\x11\xb2\x2c\x20\x8c\xe7\x46\x5f\xe1\xfb\xd4\xcb\x31\xb0\x2c\xc9\x93\x30\xf5\xf3\xc2\x09\x38\x0a\x3d\x16\xfb\x61\x20\x0d\x84\x15\x91\x9f\x73\xb3\xba\x45\xcc\x63\x29\xf8\x6d\x3f\x8b\xe3\x94\x30\xb7\xdb\xa4\x38\x23\x59\x42\xa4\x77\x8b\x79\xce\x38\x8f\x4c\x80\x13\x12\x13\x92\x49\x96\xe1\x80\x12\x3f\xf4\x53\x27\x60\x46\xd2\x82\x53\x26\xfd\x6c\x56\x60\xcf\x8f\x8c\x06\xc2\x28\x66\x51\x14\x00\xc5\x69\x16\x10\xdf\xf3\xdc\xde\x2d\x23\x41\x4a\xd3\xd8\x03\x3f\xeb\x15\x34\x89\x13\x6c\xf4\x6e\x71\x94\x85\x98\x01\x8f\xbd\x28\x0c\x52\xee\xbb\xb5\x22\xc7\x09\xe1\x14\x27\x00\x38\xe2\x45\x48\xb0\x71\xcc\xcb\xa3\x24\xf1\x22\x02\xb2\x08\xc3\x28\x64\xc9\x88\xe5\x15\x81\xc7\xfd\x50\xf2\x2e\x8c\x63\x4c\x3c\xc2\x8c\x7a\xec\x45\x8c\x79\xb2\x67\x3e\x49\xd3\x1c\xa7\x6e\xe1\xe1\x84\x05\x19\xc6\xe0\x36\x53\x9a\x93\xdc\xcb\x8c\x14\x63\xee\xc7\x51\xe6\x49\x3d\xc6\x01\x66\x69\xe8\xf6\x6e\x24\x0e\x68\x1c\x07\xa0\xc7\x79\x41\x39\x4f\x93\xc4\x04\xd8\x0f\x52\x2f\xcd\x52\xe8\x19\xc7\x49\x1a\xd0\x11\x75\xf3\x13\x9c\x79\x59\x0a\x42\xc9\xc2\x2c\x09\x59\xe4\x1b\xfd\x31\xcf\x29\x63\x01\xb8\x4d\xee\x07\x98\xb2\xcc\xad\x6e\x61\x9a\x64\x19\x0b\x0a\x39\x32\x44\x3e\xf7\x63\x23\xe0\x88\x12\x1e\x15\xd2\x59\xe5\x51\x4a\x52\xca\xdc\xac\x88\x03\x5a\x50\xc2\xc1\x40\xc2\x9c\x17\x29\x31\xfb\x8a\x98\xb2\x30\xf2\xe5\x48\x13\xf8\x38\x26\x45\xe4\xd6\x0a\x1a\x64\x34\xa6\x58\x46\x42\xb8\xf0\x58\x1a\x1b\xdd\x26\xcd\xb2\xd8\x23\x52\x78\x98\x45\x81\x9f\x70\x77\xec\x96\x78\x29\x2f\x8a\x82\xc9\x28\x32\xf2\x31\x27\x46\xad\x60\x41\xe8\x45\x19\x07\xcb\xcb\x39\x25\x69\xce\xdd\xb1\x5b\xca\x8b\x84\xf9\x85\x1c\x19\x48\x16\xc5\x09\x36\xc7\x15\x51\x8c\x63\x5a\xc8\x21\xcc\x8f\x49\xe8\x13\xb7\xf0\x32\x46\x62\x9f\x67\xc0\x63\xce\x48\x14\xe1\xc4\xc8\xe3\x1c\xd3\x28\xa5\x72\x68\x22\x42\x91\x48\x77\x11\x70\x18\x88\xb0\x9c\xc5\x79\x0e\x06\x92\xe5\xdc\xe3\x29\x36\xba\xcd\x22\x8c\xf3\xa0\x88\x0b\x35\xe8\xf2\x1c\xc7\x6e\x3d\xf6\xa2\xc2\x8b\x62\x19\x2f\xc4\x04\xc7\x51\x91\x1a\x4d\xda\x63\x91\x1f\xe7\x19\x18\x08\x23\x19\x4d\x28\x73\x8f\x20\x18\xfb\x45\x42\xbd\x40\x2d\xdc\x25\x5e\xce\x8c\x14\xe3\x34\xc6\x5e\xea\x4b\x7f\xec\xe3\x2c\x88\xb1\x9b\xc7\x84\xe6\x69\x1c\x17\xa1\xd4\x0a\x2f\x88\x73\x6a\xf4\xc7\x3e\xc9\x18\x4b\x63\xd0\x8a\xc0\xcb\x62\x12\x24\x6e\x03\xf1\xb3\x84\xa7\xdc\x03\x56\xe0\x30\x4b\x52\x9e\x1a\x85\x17\xf8\x38\x8f\xe2\x0c\x7a\x96\x64\xd8\xf3\xf2\xc0\xad\xc7\x41\x96\x85\x79\x20\x03\xef\x2c\xf5\x79\x40\x52\xe3\xd0\x24\xc2\x15\x92\x24\xe0\xac\x8a\x2c\x0a\x63\x2e\xdc\xab\xcb\x57\x14\x59\x1a\x15\x4c\x0e\x92\x2c\x8f\x0a\xc6\x8d\x14\x47\x59\x10\xe0\x84\x02\xe0\x80\x05\x71\x48\x71\xac\x16\x51\xdf\x3a\xae\xad\xb6\xf3\xc2\x1f\xaf\x7a\x43\xd5\xf6\x0c\xda\x8f\x9d\x1b\xaa\x3f\x5d\xed\x86\x6a\x88\xc9\x76\x5b\x07\x86\xed\x88\xeb\xcf\x3e\x7a\xd5\xad\x83\x88\x79\x09\xaf\x17\xdc\xfd\x34\xcb\x12\xcf\xb2\x75\x90\xa6\x51\xcc\xb8\x1c\x7e\x69\x90\x31\x16\x77\x43\x17\x07\x12\x3f\x8b\x78\xe1\xc7\xe0\xc9\x0a\x9e\x04\x05\x15\x9e\xcc\x54\x93\x85\x41\x51\x84\x3e\x58\x41\x58\xe0\xdc\x8f\x8a\x6d\x57\xf5\x43\xec\xf1\x90\x48\xe7\xc3\x72\x1e\x51\x92\x5b\xb6\x0e\x92\xd4\x0b\x23\x2a\x15\x92\xa4\x3e\x8f\x32\x5c\x6c\x89\x04\x17\xd4\xcf\x13\xa9\xf3\x45\x1a\xe0\x34\x8f\x2c\x3d\x09\x53\xee\x65\xb9\x0c\x83\xb0\x1f\x73\x82\xe3\x64\x97\xad\x83\xeb\xbe\x47\xba\x4d\x6a\x58\xa8\xe7\xd9\x33\xbf\x1e\x63\x7b\xea\xd7\x63\x62\xcf\xfd\x7a\xec\xdb\x93\xbf\x1e\x07\xf6\xec\xaf\xc7\xa1\x3d\xfd\xeb\x71\x64\xcf\xff\x7a\x1c\x5b\x12\xc0\xca\x0e\x42\x7a\x58\xe3\x39\x70\x59\x3e\x97\xe5\xc3\xcb\x1e\x92\x07\xd0\xdc\x78\x05\x4a\x96\xcf\x65\xb9\xa5\x39\x81\xe6\xc4\xda\x9c\xcc\x65\xb9\xa5\xb9\x0f\xcd\x7d\x6b\x73\x7f\x2e\xcb\x2d\xcd\x03\x68\x1e\x58\x9b\x07\x73\x59\x6e\x69\x1e\x42\xf3\xd0\xda\x3c\x9c\xcb\x72\x4b\xf3\x08\x9a\x47\xd6\xe6\xd1\x5c\x96\x5b\x9a\xc7\xd0\x3c\xb6\x36\x8f\xe7\xb2\xdc\x70\xac\x6f\xcb\xa4\xc7\x52\x33\x4c\xc0\x99\x54\x8a\x7e\xc6\x3d\x38\x72\x2b\x15\xc2\xd4\x2a\x95\xba\x60\x6a\x95\x49\x3d\x30\xb5\xca\xa4\x0a\x98\x5a\xe5\x52\xfc\xa6\x56\xb9\x94\xbc\xa9\x15\x97\x52\x37\xb5\xe2\x52\xe0\xa6\x56\x85\x14\xb6\xa9\x55\x21\xe5\x6c\x6a\x75\x22\x65\x6c\x6a\x75\x22\xc5\x6b\x6a\x35\x93\xa2\x35\xb5\x9a\x49\xa9\xce\x4d\x79\x07\x5d\x57\x77\xb7\x7c\x0e\xd5\x9a\x4f\xbb\xc6\xff\x63\x29\x73\x0f\xdb\xae\x9b\x3f\x82\x11\xbc\xde\x3e\x1b\x56\xd9\x22\x51\xb4\x44\x23\x58\xf0\x63\x59\xdf\x36\xd0\xb3\x46\xa3\xdb\x88\xbc\x85\x9a\xe6\x5c\xae\x2d\x8c\xb9\x84\xa1\xee\x17\xf4\x61\xc0\xad\xf9\x2b\x65\xa0\x3e\x3c\x44\xff\x01\xd9\x88\xed\xc8\xeb\x94\xce\x3b\x65\xa8\xde\xcc\x9a\x3c\xc7\x9b\xb1\xbb\x78\xaa\xda\x5c\x6b\xe1\xbe\x8f\x27\x6b\xcd\x3a\x59\xb0\x67\x32\xf9\xaf\x9e\xbc\x7a\x0e\x29\x8a\xeb\x74\xc0\x9d\x7a\x74\x50\x0f\x0e\xbd\xbe\x43\xdd\x6a\xb1\xeb\x86\xa9\xac\x39\xef\x50\x31\x1f\x52\x31\x33\x51\x31\x1f\x52\x31\xd3\xa9\xe8\xd6\x8b\x87\xf5\x2c\x99\x8c\x75\x91\x5a\x72\xe6\x7c\xd0\x72\x6f\xef\x92\x7c\xbb\x95\x28\xde\x4e\xa2\xb8\x95\x28\xde\x4a\xa2\x78\xd6\x49\xf0\x3d\xab\xb3\x70\x6b\x89\xb9\xe7\x2a\x57\xb7\xc6\x24\xac\x38\xdc\xad\x06\xe7\x98\x13\x4d\xa4\x35\xbc\x68\x54\xa4\x78\xde\x21\x63\x6e\x20\x63\x66\x22\x63\x3e\x20\x63\xd6\x21\xa3\x0b\x30\x1a\xc0\x23\x91\x53\xa6\x3b\xe5\x0e\x77\xb9\x92\xb8\x15\x7b\xec\x12\xfb\x8f\x65\x2c\x3d\x97\x71\x60\xee\xd5\x9c\xab\x9a\x8e\x3b\xe1\xb2\x26\x8e\x34\x47\x62\x7d\x15\xba\xae\x2b\x09\xc0\xc6\xc8\xa2\x5f\x77\x5e\xd7\x1d\xa5\xa1\xf5\x34\x73\xc1\xb4\x32\xee\x8f\x5c\xdd\xea\xad\x2b\x9b\xc9\xea\x33\xc8\xd9\x26\xe0\x08\x49\x7a\x7b\xe8\x7e\x6d\x9d\xcd\x2f\xff\x3f\xc2\xe8\x2e\x1a\x1c\x9b\x1e\xd2\x21\xfe\xad\x25\x38\x4e\x86\xf8\x77\xbf\xb1\x16\x0b\x15\xf8\xaa\x54\x00\x17\xb7\xa4\x41\x4a\x67\x48\x81\x94\xc4\x00\xbf\x19\x68\x3b\x2a\xfe\x58\xda\xc4\xdb\x8e\x7a\x3f\x96\x26\xe2\xec\x39\xf1\x55\x52\xfc\x19\xba\x89\x8a\x99\x4a\x8b\x2f\xbe\x98\xef\xf1\xc9\x36\xd2\xf6\xf9\x5c\xb4\x99\xab\x36\xe2\xcb\xc9\xdc\x91\x4c\x7f\x06\xd9\xf4\x05\xe8\x54\xe2\x81\xcf\x99\xfc\x9c\xaa\xcf\xf6\xe6\x73\x68\x2e\xb0\xa4\x12\x25\x7c\xce\xe4\xe7\x54\x7d\x76\xa7\xe4\x9f\xc9\x9c\xfc\xca\xe1\xc8\x71\x85\xcd\x65\x7a\xe9\x3d\x99\xfc\x80\xcd\xea\x8c\xfd\xaa\xb0\x93\xb3\x7f\xa6\xbd\x22\xc1\xea\x51\xc7\x99\x99\x1f\x66\x53\x93\x06\x90\xc2\x39\xeb\xe2\x9c\x77\x70\xce\xba\x38\xe7\x3a\xce\xd9\x36\x38\xb1\xec\x27\x57\x43\x83\xbc\x6f\xc2\xe5\xa0\x40\xeb\xb4\xff\xb3\xfa\xd1\x0a\xad\x30\x68\x0b\x05\x4e\xbf\x2e\x93\x69\xb8\xdd\x38\x65\x3f\x55\xe5\x1a\xe7\xac\x8b\x73\xde\xc1\x39\xeb\xe2\x9c\xeb\x38\x67\x2d\x4e\x63\xd4\x39\xfe\x0e\x81\x99\xd6\xef\x21\xfb\xd2\xf7\xf6\xcb\x54\xdf\x83\xf1\x7e\x5f\xba\xae\x51\x7d\x0f\xce\xe0\xfb\xd2\xe6\x42\x3f\xc0\x43\x09\xa2\xce\x6c\xde\x90\x68\x32\x4a\x59\x51\x20\x9c\xb5\x7d\x91\xee\xa2\xc2\xba\xbb\x98\x6d\xe3\xab\x5a\xb4\xe2\x5f\xc1\x11\x37\xce\x0a\x50\x65\x33\x13\xc2\xec\x4a\x18\xbf\x37\xba\x9e\x3e\xc6\xef\x4b\x13\xc6\xef\xcb\xab\x60\x34\x3b\xbb\x3e\xc6\x1f\x8d\x18\x7f\x34\x61\x34\x6b\x5b\xff\xf1\x0a\x0b\x4a\x58\xbc\xa8\xcd\x1e\x2a\x5a\xa9\x83\x75\x90\xda\x2b\xed\x4b\xf7\x08\x24\x12\x9d\xc4\x1a\xd6\x76\x64\xfe\xfd\x2c\x67\x15\x47\x17\xee\x99\xbe\xf8\x83\xf9\xa6\x51\xbf\x61\xba\x79\x62\x22\x1b\x06\xa0\xc2\xd4\x06\x26\xb6\x85\xa9\x0d\xcc\xa1\xb9\xa9\x0d\x4c\xa1\xb9\xa9\x0d\x4c\xc9\x27\xf9\x1c\x9e\xef\x98\xdb\xde\xef\x80\x39\xfd\x24\x9f\x41\x2d\xc9\x3a\xae\x73\x2e\x1f\x30\xcd\xfa\x12\x88\x80\x94\x99\x68\x84\x25\x85\xcc\x44\x23\xac\x5e\xa4\xa6\x36\xb0\x78\x91\x9a\xda\xc0\x3a\x09\x33\xb5\x81\x65\x92\xc1\x6b\x06\xe2\x0f\x96\x5d\x26\x52\xd5\x2b\x62\x65\x06\x2c\xdc\x4c\x24\x1f\x84\x66\xed\xb7\x23\x8e\xe4\x46\x35\x0c\x76\xae\xf5\xb1\x12\x6d\xcd\x10\x22\x83\x63\xd0\x7f\x36\x88\x06\x8e\x9b\x64\x14\x93\x63\xd0\x7b\x26\x89\x3d\xf6\x74\x6a\xd9\x90\xd8\x3e\x1c\x6d\x95\x51\x22\x04\x16\xa5\x43\x84\xb8\x45\x08\xec\x49\x15\xc2\x8e\x27\x48\xc7\x11\x6a\xeb\x92\x12\x21\x01\x17\x3b\x44\x48\x5a\x84\x64\x56\x8f\x4b\x13\xa8\xaf\xb9\xd7\x71\x84\xda\x4a\xa6\x44\xe8\x0b\x84\xf9\x10\xa1\xdf\x22\xf4\x05\xae\x5c\x21\xf4\x47\xcc\xa1\x0f\x47\x5b\xfb\x94\x08\x03\x81\x90\x0f\x11\x06\x2d\xc2\x40\xe0\xe2\x0a\x61\xa0\x23\xe4\xe3\x08\xb5\xd5\x52\x89\x30\x14\x08\x8b\x21\xc2\xb0\x45\x18\x0a\x5c\x85\x42\x18\xea\x08\x8b\x71\x84\xda\xfa\xaa\x44\x18\xc1\xa4\x62\x88\x30\x6a\x11\x42\xf4\x7e\xa2\x10\x46\x9d\x49\xc4\x38\x42\x6d\x45\x56\x22\x8c\x05\xc2\xd9\x10\x61\xdc\x22\x84\x69\x93\x1a\x93\x45\x7d\x57\x10\xf0\xc9\x77\x2f\xbe\x3c\x8a\x73\x7d\x8f\xe2\x60\x11\xdc\xab\x97\xcd\x04\x30\xc8\xc3\xe2\x7b\xd7\xfd\x2c\x8e\x19\x0d\xfe\xa7\x7c\x18\xe7\xe1\x72\xf1\x81\xaf\x64\x96\x5f\x54\x2d\x91\x4f\xee\xa4\x65\x25\x02\x94\x1c\x31\x38\x9f\x9d\xf2\x62\xb9\xe2\xea\x38\xf5\x40\x6a\xda\x5d\x13\x6d\xef\xae\x5a\xbe\xf6\xc9\x75\x3c\xc4\xf3\x67\x7d\x82\x47\xa7\xb3\xc9\x0f\x72\x17\x61\x8f\x04\x87\xbe\xca\x53\xfc\xe5\x76\x93\xf5\xaa\x52\x88\xc9\xae\xb7\x9b\x44\x93\x91\xdb\x4d\x9d\x63\x0d\x83\xdb\x4d\x21\x26\x5f\x6e\x37\x5d\xf7\xed\x26\x21\x95\xed\x6e\x37\x19\x85\xd3\xb9\xdd\x24\x05\xe4\xbc\xdd\x24\xef\xd1\x6e\x79\xfb\xdb\xff\x53\xdf\x67\xe2\x8b\xec\x4e\xca\xd6\x3c\x0a\x7a\x05\xa7\x79\xd8\xaf\xfa\xe1\xec\x7d\x5e\xf4\x7e\xcc\xca\xb3\x19\x5f\xfd\x21\x57\xa2\x34\x52\xe1\xbb\xa0\x50\x16\x48\xc2\xe0\xb3\x4e\xcf\xbf\xc2\xd5\xa9\x1f\xb7\x7a\x13\x08\x0e\xcf\x3c\x84\xae\x37\xf5\xb4\xdf\xc6\xaf\x42\x1d\x1e\xa2\xe7\x7c\x75\x0a\xa3\xe8\xc3\xd9\xb2\xcc\x38\xc2\xfd\x67\x53\x44\xf3\xe7\x0f\x71\xf7\xee\x52\x18\x4f\x51\x90\x4c\x51\x80\xa7\xc8\xf7\xa7\x88\x84\x53\x84\xe3\x29\x4a\xa6\x08\x61\xed\xa8\x51\x48\xa7\x28\xf4\xa6\x28\x20\x53\xe4\x07\x53\x44\xa2\x29\xc2\x74\x8a\xb0\x37\x45\x44\xaf\x97\x4c\x51\x88\xa7\x28\xf0\xa7\xc8\x0f\xa7\x88\xc4\x53\x84\x93\x29\xc2\x02\xbe\x56\x2f\xf2\xa6\x28\x24\x53\x14\x04\x53\xe4\x47\x53\x14\xf9\x53\x14\x86\x53\x14\xc4\x53\xe4\x27\x5a\x45\x1f\x4f\x11\xf1\xa7\x08\x87\x53\x14\x4f\x11\x8a\xc8\x14\x85\xc1\x14\x05\xf0\xb4\x80\x5e\x51\x50\x42\xa6\x08\x07\x53\x14\x89\x8a\x78\x8a\x42\x7f\x8a\x82\x70\x8a\xfc\x58\xab\x48\x92\x29\x22\x78\x8a\xb0\x40\x39\x45\x88\xd0\x29\x22\xde\x14\x61\x41\x8e\xac\xf6\xd6\xc1\x57\x62\xe6\x2b\xe9\xf2\x55\x50\x21\xf8\x28\xfa\x4d\xc4\xe7\x29\x42\xa1\x4e\xad\x42\x2c\xba\x25\xa8\x05\x82\x3c\x9d\x4a\x5f\x31\x4e\x50\x25\x2a\x44\x53\xa4\x77\x17\x47\x92\x1f\x82\xc1\x40\xbd\xdf\x15\x84\x10\xa8\x60\xb0\xe0\x9f\x1f\x4b\xc6\x86\x61\x8f\x5f\x81\xa7\xa4\x15\x4a\xe9\x07\x3a\x06\x21\x1a\xa1\x1a\xbe\x10\x69\x24\xc5\x1e\xea\x32\x14\x22\x10\xfa\x20\xf4\x42\xc8\x50\x30\xb6\x8e\x6a\x3a\x2f\x42\x9d\x9f\x9e\xcf\x19\x3c\x93\x22\x82\xca\xf5\xac\x2c\x06\x2f\x3c\x81\x15\x7c\xf7\xea\xa7\x97\xc7\xdf\x3d\x96\x6f\x4a\x09\x8e\x91\x29\x82\xce\x0b\x0e\x51\xa1\x91\x4a\x4c\xc0\x5d\xa5\xa9\x58\x89\x93\x28\xed\x05\x86\x50\x1d\xff\xcb\x6f\x9e\xbd\xe6\x6b\xc4\x16\xb9\xca\x8d\x7e\x06\x22\x95\xef\x69\x18\xe8\x10\xf5\x7f\x7a\xde\x95\x67\x2f\xa4\xf4\x36\xde\x5d\x98\x8c\x50\xe2\x79\xd3\x7e\x59\x3d\x57\x90\x55\x0c\x15\x48\xa7\x02\xf5\x3c\x32\xa8\xe2\x6b\x55\x86\xa5\x81\x5e\x6a\x40\x10\x76\x11\x10\x03\x82\xa8\x4b\xa4\xa9\x4a\xdc\xeb\x87\x01\x11\xed\x10\x32\x04\x91\xf4\xb1\x0c\x41\x30\xbd\x8a\xa9\x42\xda\xe7\xd6\xb0\x4a\xd6\x43\x33\xa8\x90\xf7\xbb\x32\xac\xc2\xb5\x2a\x43\x0c\x45\x97\xca\x61\x73\xea\x6a\x8d\xe9\xa8\x3c\x08\x1d\x41\xe0\xd3\x11\xad\x0a\xfa\x48\x0c\x7a\x41\xdd\x7a\x13\xd1\x51\xc5\x8c\xa9\x4b\x31\x29\x1d\x95\x77\x42\x47\xe4\xcd\xfa\x44\x18\x54\xa2\x8f\x66\x48\x49\x46\x47\x25\x9e\xd3\x11\xad\xe1\xd4\xad\xdd\x45\x1f\x87\x41\xf2\x56\x71\x29\x2f\x81\xcd\x8c\x24\x5a\xa9\x45\x98\x7e\xa7\x8a\x11\x7b\xd0\x85\x62\xea\x63\xa8\x57\x31\xea\x84\x4e\xa7\xa1\x3c\xee\x92\xe1\xb0\x0d\xec\x50\xff\xa4\x4f\xa9\xd5\x51\x60\x87\x44\xd3\x6e\x67\x0c\x5a\xd1\xe9\x8c\xd5\x4f\x60\x87\xfe\xf2\x5e\x15\x9b\xab\xc0\x66\x57\x40\x47\x59\x81\xe9\x28\x2b\x08\x1d\x15\xbd\x4f\xdd\x62\x0b\x7a\x20\x6c\xbe\xc2\xc5\xee\x88\xba\x54\x38\xa6\x23\xc2\xa0\x74\x84\x93\x09\x1d\x55\x2d\x46\xdd\x02\x4d\xfb\xfc\x36\x0c\x1e\x7d\x2c\xc3\x2a\x39\x75\x89\x94\xd3\x11\x13\x2a\xfa\x12\xd5\xdf\xa8\x9a\x8e\x45\x19\x81\xe7\xd1\xc0\xc3\x56\x0f\xa2\xea\x58\xc3\x8c\x46\x80\x36\x0f\x52\x23\xf1\x4c\x48\x82\x2e\x12\x63\x9d\xb0\x0b\xc7\x48\x4c\xd4\x85\x63\xac\x13\xb7\x75\x0c\x58\x74\x67\x6b\x6c\x9e\xf4\x51\x18\x80\xb0\x7e\x77\xec\x01\x87\x42\x64\x00\x92\x75\x18\x6b\xa8\x90\xb7\x15\xac\x0e\x44\x92\x60\x68\x5c\xf4\xa5\x62\x8d\xbb\x9c\xcc\xc4\x74\xa4\x17\x84\xba\xb8\xed\xf7\x51\x98\x74\x83\xf6\xe4\x6e\xd2\x0d\x3a\xce\xf0\x88\x8e\x28\x6a\x4c\xc7\x15\x95\xd2\x11\xa1\x24\xd4\x21\x14\x46\xdd\xb6\x94\xf6\x29\xb0\x3b\x12\xa7\xa9\xe4\x74\x44\x89\x79\x9f\xa7\x76\x7f\x62\xd5\x20\x7d\x02\x62\x28\xc5\x5b\x98\x3d\x26\x5b\x18\x13\xf6\xb7\x30\x7c\x1c\x6c\xa1\xcf\x38\x74\x9a\x3e\x8e\xc6\x4c\x12\xc7\x23\xce\x50\x0f\xc1\xcd\x10\x92\x31\x77\x89\xd9\x98\xdd\xe3\x74\x0b\x6f\x89\xb3\x31\x47\x86\xf3\x2d\x9c\x25\xe6\x5b\xb8\x32\x5c\xf4\x25\x64\x54\x97\x31\x57\x81\xf1\x98\x85\x62\xb2\x85\x81\x60\x7f\xc4\xca\x70\xb0\x8d\x63\x0b\xb7\x70\x3b\x38\x72\x7a\x37\x1c\x6f\xe1\x96\x30\xdd\xc2\x16\x71\xb2\x85\xd5\x63\xb6\x85\x37\xc5\xe9\x98\x07\xc3\x99\xcb\x85\xe1\x7c\xcc\x2d\xf0\x2d\xdc\x28\x2e\x7a\x1e\x6a\x97\x50\x05\x7b\x81\xc5\x19\x99\x49\x26\x1d\xae\x60\x6b\x88\x22\x61\x9b\xa0\x07\x5a\xb9\x67\x28\x0f\x7b\xc2\x19\xd6\x88\x3a\x4c\x33\xe1\x88\x3b\x35\xc6\x87\x63\x7b\x6c\xd2\x62\xb1\x45\x26\x75\x4f\x6d\x51\x49\x4b\xc5\x90\xce\xac\xc7\xcd\x61\x8d\xbc\xc3\x2d\x5b\x68\x02\x10\x2c\x61\x89\x6a\x6b\xe6\x80\xab\x7b\x98\x8e\x91\x4f\xa8\x5d\x51\x7c\x3a\xa6\x28\x01\x1d\x13\x74\x48\xdd\x9d\x8f\xa8\x5b\x95\x62\xad\x7c\x58\x4a\xa9\x9d\x75\x09\x75\xb1\x8e\xd1\x31\xf5\x4a\xa9\xdb\x08\x32\xea\x56\x9d\x9c\x8e\x29\x06\xa7\x63\x46\x50\xd0\x31\x15\xef\x84\x15\x16\x25\xc0\x23\xe6\x8a\xc9\x88\x86\x62\x7f\xd4\x65\xe0\xc0\xa9\xa9\x38\x1c\x35\x78\x1c\x8d\x7a\x0d\x1c\xbb\x3c\x31\x1d\xb5\x44\x9c\x8c\xba\x0c\xcc\x1c\xd6\x88\xd3\x11\x77\x81\xb3\x51\xaf\x85\x75\x77\x60\x40\xc1\x47\x7c\x2f\x2e\x46\x5d\x92\x0a\x2d\x9c\xdd\xc4\x4e\xbb\xc2\x64\xdc\xb5\xf8\x0e\xcf\x81\x83\x11\xb3\xc6\xe1\xa8\x6f\xc1\x91\xd3\x80\x71\x3c\xea\xdb\x30\x1d\x71\x3e\x38\x19\xb5\x40\xcc\x46\xdc\x00\x4e\x47\x7d\x20\xce\x46\x5d\x01\xce\x47\xfd\x11\xe6\x0e\x67\x87\x8b\xae\x37\xda\x25\x7e\xa0\x9e\x44\x69\xf6\x2d\x75\xf4\x89\xbd\xc0\x12\x4a\xd4\x44\x1b\xca\xfd\x16\x42\x60\x56\xc4\xc0\xae\x44\x61\x97\x23\xe6\x18\xa2\x09\x8e\x4d\xe8\x63\xaf\x13\xfe\xd9\xc7\xcf\x7a\x47\xc5\x1c\x41\xb4\xb2\x35\xc7\x0f\xb2\xdc\x1c\x3b\xb4\xec\xb3\xed\xa0\xb4\xec\x31\xc0\xc8\x35\x2b\xb5\x44\x0e\xb5\x7a\x9b\x63\x87\x56\xc0\x96\xfe\x3b\xe5\x8b\xa9\xbd\x7b\x84\x8e\x11\xef\xd3\x31\x06\x04\xd4\x2d\xe2\x90\x8e\x75\x21\xa2\x56\xfd\x89\xe9\x98\xf2\x51\xea\xe2\x5f\xd2\x45\x6e\x0b\x22\x1c\xda\x91\x52\x97\xf4\x32\x3a\xa6\x7d\x39\x75\xeb\x2f\xa7\x6e\xf3\x2b\xe8\x98\x85\x60\x6f\xc4\x44\x30\x1e\xb1\x42\x4c\x46\xcd\x10\xfb\xae\x91\xc2\xa9\xe1\x38\x1c\x35\x11\x1c\x79\x63\x72\xc2\xf1\xa8\x27\xc3\x74\xd4\x5a\x70\x32\xea\x2e\x30\x1b\x75\x78\x38\x1d\xf1\x99\x38\x1b\xf5\x1b\x38\x1f\x71\x4b\x98\x3b\xfc\x12\x2e\x9c\x6e\x43\x46\x0f\xee\x3e\xe0\x51\xbb\xc4\xc4\x6e\x98\xd8\x1f\x31\x7b\x1c\x8c\x28\x3e\x0e\x47\x6d\x07\x47\xe3\xde\x2d\x76\xb8\x37\x4c\xc7\x8d\x27\x71\xfa\x0f\xcc\x46\xfd\x1f\x4e\x47\x9d\x28\xce\x9c\x4e\x04\xe7\xa3\x5e\x0a\xf3\x11\x37\x85\x8b\xae\x1f\xd9\x2d\x78\x30\xfa\x94\x9a\x5e\xdb\x0e\x49\x43\x8d\x31\x64\xb8\xab\x1d\xd7\x30\x46\x0c\xaa\x02\xac\xa7\x18\xe3\x86\x26\xe6\x33\x94\x47\x35\x00\x5b\x85\xb8\x25\xd0\x50\xaa\xcb\xdc\x16\x32\xb4\xf4\x59\x62\x86\xb6\x87\x06\x0c\x69\x4b\xa0\x99\x84\xac\x53\xc1\x34\x70\x58\x6d\x8f\xeb\xc2\x31\x80\x2e\x3a\xcc\x31\xaf\x39\xb8\xda\x63\x3a\xc2\x5c\x42\x3d\x9b\xe2\xf8\xd4\xad\x38\x01\x75\x29\x4e\x48\x47\xf4\x22\xa2\x23\x5c\x8b\xe9\x88\xea\x51\x3a\x22\xda\x84\xda\xf8\xce\xe8\x88\x4c\x53\xea\xd6\xda\x8c\x8e\x68\x4d\x4e\x47\x24\xc7\xa9\x5b\x71\x0b\xea\x52\x7b\xec\x39\xcd\x16\x63\xcf\x2a\x57\x4c\xc6\x6c\x1a\xfb\x63\x36\x89\x83\x11\xab\xc6\xe1\x98\x51\xe0\x68\xcc\x73\xe0\x78\xc4\xb6\x9b\x71\xcf\x2a\x46\x9c\x8c\x19\x10\x66\x23\xfe\x11\xa7\x63\x1e\x04\x67\x4e\x0f\x85\xf3\x31\x0f\x83\xb9\x7d\x70\x2e\x46\x3c\x04\xc4\x07\x6e\x59\xe1\x11\x4d\xc3\x64\xc4\xd2\xb1\x3f\x66\xcc\x38\x18\x33\x56\x1c\x8e\xb9\xaa\xc8\xee\x8a\x70\x3c\xe6\x2c\x30\x75\x9b\x4b\x32\x66\xf0\x98\x59\x9d\x05\x4e\xc7\x6c\x19\x67\x23\xee\x02\xe7\x4e\x67\x89\xf9\x98\x2b\xc3\x45\xcf\xe1\xec\x12\x15\x28\xb2\xa9\xc9\x8b\xd4\x30\x4d\x71\x81\x6c\x4b\xcc\x7d\xf6\xdb\x72\x62\x82\x1d\xb4\x1c\x31\xc2\x0f\xf5\xfe\x98\xa2\x82\xa6\x74\x08\x3b\xee\x28\xb4\x75\x54\x34\x46\x03\x1a\x51\x43\xc0\xac\x46\x6b\x24\x39\x55\x0a\x6a\x8a\x00\x34\x5e\x0d\xcb\x73\x0d\xec\xb0\x94\x37\x7d\x1d\x96\x15\x1d\x2e\x9b\x7a\xea\x14\x12\xa6\x6e\x21\x11\x6a\xe9\x91\x4f\x5d\xd2\x09\xa8\xab\x3f\x21\x75\x6b\x5d\x44\xdd\x9a\x11\x53\x3b\x3f\x28\x75\xe9\x45\x42\xed\xfa\xcc\xa8\x5b\xf4\x29\x75\xcb\x30\xa3\x16\x9d\xca\xa9\x5b\x44\x9c\xba\x74\xaa\xa0\x6e\x55\xc6\xde\x88\x1d\x61\x3c\xa2\x7c\x98\x8c\x58\x2a\xf6\x1d\x0a\x88\x03\xa7\x9d\xe2\x70\xc4\x14\x71\xe4\x8d\xf8\xa0\xd8\x69\x73\x4d\x04\x6b\xa1\x3d\xb1\x7a\x6d\x66\xb3\x56\x9c\x8e\xb8\x36\x9c\x39\xfc\x22\xce\x47\x7c\x08\xe6\x23\x36\x8b\x0b\xa7\x73\x13\x23\xba\x85\x70\xec\x54\x25\x4c\x9c\x46\x8b\xfd\x11\xbb\xc4\xc1\x88\x61\xe2\xd0\x61\x99\x38\x1a\xf1\x35\x38\x1e\x75\x56\x23\x96\x84\x93\x11\x1b\xc5\xcc\xe1\x00\x70\xea\xf4\x5a\x38\x73\xba\x16\x9c\xdb\xec\x1f\xf3\x31\x13\x2e\xba\xae\x67\xf7\xa1\xdb\xa0\x23\x35\xa9\x81\x87\x0d\x43\xb7\x0a\x35\x0c\x83\xb6\x02\x6a\x6a\x16\x34\x41\x8e\xa9\x34\xb4\x74\x3f\x92\x20\x0d\x63\x74\x1b\x32\x0d\x4b\xa9\xd6\x01\xd3\x30\xdd\xf4\x7d\xd8\x94\x69\x4a\x3e\x2c\x4d\xb5\x4e\x98\xa6\xea\x5a\x1c\x67\x18\xa6\x25\xdf\x86\x50\x79\xcb\x37\xd3\x24\x5d\x8b\x7c\x87\x3d\x75\xb1\x01\x53\x33\x53\x09\x75\xc9\xd7\xa7\xae\x3e\x06\xd4\xa1\x38\x21\x75\x31\x2f\xa2\xae\x9e\xc4\xd4\xc6\x1e\x4a\x1d\x6a\x95\x50\x97\xa8\x19\x75\x49\x24\xa5\x0e\x45\xc8\xa8\x4d\xcd\x73\xea\xd2\x64\x4e\xcd\x1a\x5b\x50\x87\x90\xb1\xe7\x94\x32\xc6\x4e\x73\x25\x4e\x7b\xc5\xbe\xd3\x56\x70\xe0\x32\x07\x1c\x3a\x4d\x09\x47\x4e\x83\xc0\xb1\xcb\x23\xa8\xf1\xc6\x58\x94\x38\xbd\x05\x66\x2e\x8b\xc1\xa9\xc5\x69\xe0\xcc\xe6\x64\x73\xa7\xe5\x62\xee\x74\x0a\xb8\xb0\x7a\x44\xec\x39\xa5\x8e\x9d\x86\x88\x89\xdb\xba\x7d\x8b\xa6\xe1\xc0\x69\x68\x38\x74\x99\x30\x8e\xac\x76\x88\x63\xa7\x67\xc0\xd4\x69\xfd\x38\x71\xda\x22\x66\x16\x67\x85\x53\xa7\xb9\xe1\xcc\xe5\x1d\x70\x6e\xb5\x62\xcc\x9d\x9e\x03\x17\x9a\x73\xd8\x65\x4c\xa5\x62\x80\x27\x06\x80\x0d\x73\x86\xfe\xf8\x6e\xbb\xb9\x31\x74\xc7\xb2\xdd\xd0\x11\x2b\x78\x86\xa2\x50\xc2\x23\x46\x3a\xa2\xa6\xd0\xe4\x84\x15\x25\xe6\x71\x86\x7a\x66\xfa\x93\xa6\xdf\x26\x17\x2c\xe9\x34\x15\xa5\x0d\x50\x03\x9d\xd9\x5d\x79\xd9\x63\xe8\x7e\xcd\x7a\xc2\x1b\x26\x1a\xda\x14\x8a\x08\x43\x51\xbd\xa9\x64\xed\xb9\x2c\xc6\x2e\x9e\xaa\x3a\xc4\x25\x7f\x55\xc7\x77\xc9\x5a\xfd\x1e\xb8\x98\xad\xea\x84\x76\xb6\xaa\x1a\xd1\x68\x9f\x63\x8b\x6a\xa9\x62\xea\xe2\xa8\xaa\x93\xd8\xa4\xa4\xca\x99\x5d\x4b\x55\x8d\xd4\xa5\x8f\xaa\x4e\x66\x16\xb9\x2a\xcd\x5d\x6a\xa4\xea\x70\x97\x8a\xaa\x3a\x85\xdd\x42\xeb\x88\xd8\x68\xd8\xd8\xd5\x03\x4c\x2c\x4c\xc6\xbe\x4d\xe3\x70\xe0\x22\x16\x87\x2e\xb1\xe0\xc8\xc5\x0c\x1c\x3b\xba\x68\xf3\xbf\x89\x5d\x84\x98\xb9\x34\x15\xa7\x4e\x7f\x98\xb9\x2c\x0a\xe7\x76\xfd\xc6\xdc\xa6\x74\xb8\x18\xb7\xae\x76\x72\x63\xad\x81\xdd\xbe\x00\x93\x71\x85\xc3\xfe\x98\xf5\xe1\xc0\x69\x7d\x38\x1c\x77\x02\xb5\xb0\x9d\xdd\x8d\xc7\x9d\x12\xa6\xe3\xce\x0d\x27\xe3\xde\xa0\x56\x07\x97\x95\x49\xa5\xb0\x96\x66\x63\x6e\x4d\x2a\x86\x83\x4e\x3e\xe6\x71\x6a\x25\x01\x2c\xda\xc8\x2e\x3f\xea\x79\x0d\x9e\xb2\xf5\xfb\x35\xaa\x66\xac\x42\x6b\x3e\xe7\x59\x05\xf9\x88\x5e\x7e\xf3\xec\x35\x2a\x17\x67\xf5\x33\x11\x4d\x46\x83\xa7\x0f\x5e\xf6\x1e\x2e\x6e\x2f\x26\x4e\x51\x7b\xf0\x1f\x1e\x50\x54\x5f\xe0\xb3\xfa\x32\xd5\x1b\x7a\xea\x57\x59\x41\x7e\xa9\x3f\x8b\x2f\x53\xad\x3f\x7d\xca\xb5\xac\x4a\xdf\x3e\x7a\x29\x13\x63\x21\x99\xf8\xc5\xfd\x46\x95\xa8\xdd\x3c\x50\x25\xbf\x68\x59\x52\xae\xfa\x44\x95\x3b\xb5\xde\x7b\x7e\xd9\xa4\x00\x7b\xcf\x2f\x0d\xa9\xef\xde\xf3\xcb\x3a\xaf\xde\x7b\x7e\x69\x4e\xab\x27\x70\x48\x11\x85\x11\x4a\xcb\x6a\x8d\x58\x96\x2d\x57\x79\xb9\x38\x41\xd5\x12\x3d\x7f\x88\x8d\x70\xbf\x29\x21\x15\xd0\x9b\x7e\x0e\x64\xd3\xdb\x21\x61\x64\x7f\x3b\xa4\x05\xf7\x7c\x29\x00\x3e\x7f\x88\xdf\x94\x6f\xd1\x1d\x84\x0d\x39\x4a\x15\x5e\x99\x9e\x7f\x52\xf7\xee\x4d\xdb\x5e\xa5\xe3\x13\xff\x99\xf8\x18\xdd\xd1\x40\x43\x1e\xbe\x3d\x74\x73\x00\xd8\x90\xb0\xf4\xc1\x7a\xcd\x4f\xd3\x39\x47\x38\x42\xeb\xf3\xf4\x3d\xbf\x34\xb0\x7f\x7d\x9e\x7e\xcf\x2f\xd7\x8d\x08\xda\xef\x76\xa6\x2c\x5e\x42\x25\xc9\x9a\xfa\xcb\x7d\x84\xa3\xe6\x9b\xfd\x89\x95\x87\x90\x71\x4a\xd1\x63\x66\xe4\xba\x86\xae\x68\x79\xa3\x80\xbe\x55\x44\x19\xe1\xba\x9f\x6e\x49\xcb\xea\x25\x64\x45\x39\xd2\x92\xa0\x34\x70\x6d\x20\xa5\x42\x05\xd4\xa8\x50\x64\xd8\xc6\xa4\x35\x24\xb0\x6b\x4d\x17\x4f\xb1\x5a\x9e\x82\x83\x99\xf3\xa2\x42\x84\x82\x65\x08\xcc\xe6\x86\x92\x39\x6f\x26\x25\x3a\x94\x6f\x43\x78\x90\xc0\xb1\x56\xae\xc9\xe4\xf9\x43\xa2\x74\x70\x0f\xed\x37\x1c\xd8\x43\x7f\x43\x84\xbe\x85\x1c\x8f\xa0\x5b\x25\xfa\x1b\xbc\x71\xb1\x35\x79\xab\xf2\x64\xb6\x3d\x7d\x01\xa4\xef\x6c\x89\xdc\xeb\x50\x49\x28\x14\x4b\x5a\xd1\x3e\x22\x81\x85\xe0\x3d\x03\xc5\x03\xb4\xa6\xcc\xfe\xa2\x03\xe5\x22\xe3\x88\xb3\x6c\xa6\xd4\x0e\x95\x6b\xc4\xce\xce\xe6\x25\xcf\x85\x2c\xd9\x02\xf1\xcd\x19\x5b\xe4\x3c\xaf\xf3\x32\x82\x7b\x9f\x1a\xa1\x09\x16\x28\x30\x19\x5b\xa0\x94\xa3\x74\xb5\x7c\xcf\x17\xa8\x5c\x54\x4b\x44\x65\x52\xe0\x35\x5a\x67\x6c\x2e\xc1\x4b\x90\x6b\x33\xb4\x8b\x59\x99\xcd\x10\x9b\xcf\x97\x17\x6b\x00\x2d\xe0\x56\x4b\x01\xf6\x7c\xcd\x73\x74\x51\x56\xb3\xe5\x79\x25\x09\x5c\x97\xcb\xc5\x10\x8a\x62\x34\xa4\xd7\x9c\xb4\x5f\xee\xdf\x57\xcf\xca\xb4\x3f\x09\x87\xe2\x63\x13\xe7\x3a\x9a\x8b\xa5\xe6\xc6\x6e\xc5\x55\x60\xc1\x89\xb5\x9f\xc1\x67\x4d\x4a\x29\xc4\xdb\x48\x48\xdf\x37\x8b\xca\xd6\x8f\x58\xef\x47\xfc\x56\x25\xf6\xfc\x4d\xff\x09\x1e\x05\x18\x3c\xb5\x63\xf0\x80\x0f\x65\xe2\x4b\x54\x2e\x3e\xf0\xd5\x9a\xdb\xbd\x60\xb9\xf8\xf0\xb2\xe7\x08\x3b\x3f\x6d\x35\x40\x60\xc7\x00\xd1\x42\xd3\x39\xb6\x7e\x83\x43\xa1\xd0\x7d\xe8\x1f\x3b\x0b\x0e\xed\x17\xbe\xc8\x56\x97\x67\xd5\x0e\x4f\x01\xaa\x8c\xb5\xcb\x87\x4d\xbb\xb6\xf2\xb4\xeb\xf2\xad\x29\x74\x73\xfe\x39\xb0\xb6\x1c\x71\xe5\xee\x7d\xe8\xc6\x3c\xad\x19\x69\x0a\x3a\xfe\x83\x57\x7a\x9c\xd6\x25\x6e\x0e\x40\xb5\xa7\xb1\xfa\x32\x90\xd5\x56\xfd\x6a\xf0\x72\x96\x21\xfa\xf8\x6e\x51\x56\x25\x9b\xeb\xa9\xaf\xba\x75\xf8\x26\x9b\xb1\xc5\x09\x7f\xf2\xa2\x4d\x8b\x2a\x33\x8f\x79\x1b\xaf\x90\xff\xeb\xab\xb4\xb9\x8d\x7c\x9f\x1a\x66\xac\x45\x61\x6d\xf3\xe2\x89\xde\x86\x00\x1e\x5f\xfd\x6d\xd7\x86\x4a\xda\xbc\xa2\x10\xff\xdf\x92\x36\x68\x13\xaa\x3f\x63\x66\x5a\xd7\x53\x6d\x32\x7d\x18\x58\x94\xfc\x28\xad\x0a\x3e\x8f\x3f\xdb\x66\x18\x89\x8c\xf1\x04\x80\xb3\x3d\x7b\xd1\x28\x86\xae\x27\x96\xba\xab\x6e\xdd\x95\xaa\x6b\x24\xf2\x31\x2f\xd7\x15\x9f\x37\x5a\x6c\x86\x58\x40\xe7\xb7\x0b\x2d\xa8\xdb\x41\x17\x62\xa0\x95\xa9\xd6\xde\x94\x6f\xdf\x4c\x26\x8a\xda\x77\xad\xbb\x16\x81\x64\x33\x75\x81\xef\x90\x56\xdb\xc4\x1a\x83\xc3\xee\x19\xd2\xca\xc6\xa9\x9e\x25\xcd\x6b\x32\x8a\x71\x07\xfe\xf7\x45\xbe\x44\xeb\x0b\x76\x26\xc3\x8f\x39\x5b\x57\x52\x19\x86\x2e\xbc\x72\x8b\xac\x47\x6c\x57\x60\x2e\xc3\xaf\x0c\x3a\x0c\x19\xc5\x77\x35\xf5\x81\x69\x5c\x9b\x09\x5e\xc5\xd4\xaf\xe2\x52\x46\x5c\x97\x61\x46\x56\xa1\xe5\x79\x35\xf0\xc0\x8d\xcb\x75\x8b\xac\xe3\x72\xed\x32\xeb\x0c\x19\xef\xf9\xa5\x4c\x01\x1d\x05\x87\x3e\xd1\x4b\xca\x0f\x96\x02\x2d\x6f\x74\x64\xcc\x1a\x7d\x88\x5e\x0a\x0d\x54\x93\x80\xd5\x72\xbd\x6e\xc3\x74\xc8\x79\x08\x01\x31\x4c\x4b\x65\x8b\x66\xa0\x6a\x19\x37\xa9\xc7\xab\x53\xb6\x7e\xdf\x31\xd9\x5a\x77\x27\x93\x8e\x8a\x0a\x43\xac\x47\xd7\x77\x9d\xae\x0b\xa3\x15\x50\x34\x16\x74\x54\xf6\x1d\xe8\xec\x57\x46\xc5\x17\x65\x22\xa2\x92\x90\x55\xad\xda\xee\x06\x64\xbf\x78\xb2\x3d\xd9\x2b\x3b\xd9\x73\x37\xd9\x73\x07\xd9\xab\x2d\xc8\x76\x26\x91\x5e\xd7\x59\xa4\xe5\xf2\xc7\x76\x79\xa4\xc7\x92\x30\x4b\x58\x15\xdf\x54\x7a\x2a\xe6\x6f\x1f\xbd\x3c\x50\x01\x5a\x27\x17\xf3\x14\x65\xc5\x89\x21\xb9\xf6\xd9\x9c\x09\x22\x36\x15\xea\x43\x51\x01\xd7\xa4\xc5\x63\x02\xd4\x64\x76\x1e\x2e\xd4\x74\x93\x6e\x7f\xfb\xe8\xa5\x31\xe3\xf6\xab\x55\x79\x36\xe7\x77\x76\x5b\x22\x92\x8d\x3a\x0b\x45\xfa\x4f\x7f\x9e\xe5\x22\xb5\x10\x21\xc8\x2e\x21\x43\x69\xd6\x7f\x1e\x48\x45\xb1\x7c\x8d\xd1\x91\xa8\x77\x20\xb9\xfa\x48\xca\x78\xb9\x9a\xb4\xef\xac\xab\x87\xe3\x6b\xd4\x07\xeb\x79\x99\xf1\x89\x37\x45\x64\x6f\xf0\x16\x46\x03\x96\x5c\x11\x2c\x99\xa2\xc0\x01\xd6\xbf\x22\xd8\x60\x8a\xa2\x3d\xfb\x43\x1a\x57\x9e\x7b\xf0\x35\x3e\xd0\x1b\x6b\x2d\xac\x9c\x39\xd0\xe7\x1c\x5b\x34\xf0\xb7\xc0\x70\x3d\x73\x1a\x81\x6b\x47\xe2\xc8\xae\xdd\xc7\x5b\x60\x30\x8f\x7a\x38\x21\xd7\x36\xec\xfd\x93\xb8\xd5\xc6\xbb\x5c\x83\x73\x6d\x61\xed\xe8\x62\x6d\x2e\xae\xeb\x68\x9b\x5a\xce\xfc\xf9\x4d\xad\x5e\x0a\x7d\x2d\x31\xfb\xdd\x90\x4c\x7b\x59\xf5\xb5\xe4\xee\x77\xc3\x60\xda\x66\x75\xbf\x1b\x46\x53\x95\xec\xfd\x6e\x84\x3f\xbe\x9d\xd2\xe0\x93\x12\xee\xff\x91\x99\xf6\x3f\x5b\x3e\xfc\xff\x9e\xcc\xf6\xf0\x52\x41\xb9\xe0\xf9\xf5\xa6\xb8\xff\x86\xad\x79\x9b\xb5\x9e\xad\xb9\x56\xf6\xda\x27\xce\x0c\xf8\x43\x5b\xde\x44\x01\x5a\xb0\x53\xbe\x3e\xd3\xad\xf4\x50\x27\x43\x54\x11\x64\xc8\xff\xfe\xfa\xd1\x04\xe6\x01\x8a\x82\xe6\x09\x1b\x13\x98\xd7\x51\x20\xe8\x00\xa2\x36\x51\x70\xa0\xbe\x08\xfa\x0d\x91\x41\x0b\x5a\x82\x57\xcb\x29\xe5\x2f\x7c\x8d\x18\x5a\xf0\x8b\xf9\x25\x92\xb6\x96\x9b\x10\xeb\x0e\x05\x75\x5e\xf3\x58\x9c\x9f\xa6\x7c\xf5\x11\xc1\xab\x52\xf0\xaa\x8a\xf8\xe0\x13\x08\xe7\x0f\x9c\x4d\xe6\xcb\x0b\x68\x21\xfe\x6b\x6a\xd0\x6d\xdc\xf5\x6e\xc3\x0a\x35\x5f\x36\x2d\x5f\x6a\x8f\x50\xb3\xa7\x1e\x98\xe5\xee\x9f\x47\x3c\x1f\x66\x65\x81\x17\x7a\x91\xd7\x5d\xef\xac\x39\x0d\x2e\x7e\x51\x76\x22\x2a\xd1\xc3\xa9\xa0\xda\x3c\x86\xa9\xf7\xb5\x0c\xaf\x7a\x42\xb1\xe8\xed\x11\xea\xbe\xbe\xad\xcf\xcc\xfb\x92\xfa\xa6\xac\x2e\xca\x35\x47\x3f\x3c\x7b\xb5\x06\x08\x63\x82\xa9\x1f\x4a\x51\x0a\xf2\x11\x3d\x10\xf2\x15\x7c\xb9\x03\x8c\x51\x23\x09\x2b\x2a\xbe\x42\x0b\x7e\xc2\xaa\x72\x71\x72\x0d\x8c\x07\x50\x5c\x30\x5e\x89\xe0\x60\xb1\xac\x26\x56\xae\x1e\x1e\xa2\xc5\x72\x34\x52\x85\x37\x59\x24\x43\x7f\x6f\xb8\x7b\xcf\x58\x4d\x32\xf6\xf7\x9a\xc9\x86\x90\x54\x71\x46\x31\xa6\xd6\x86\x56\x9c\xf7\x3a\xd4\x75\x22\x00\x9b\x54\x1e\xfc\xf0\xad\x26\x15\xd8\x4e\x80\x71\xfb\x8c\xad\x61\x7b\x61\x2b\x1b\x6a\x24\x05\x30\x84\x49\x34\xc2\xaa\x96\x02\x45\x0d\xf7\x9a\x85\xff\xe0\x87\x6f\xaf\x47\xf4\x72\x6f\xa7\x15\x3c\x5b\xe4\x13\xb6\x58\x56\x33\xbe\x52\x84\xb8\xd4\x80\x2d\x72\x5d\x0d\x44\x0f\x47\x54\xa1\xb5\xb3\x9b\x92\x21\x63\x5a\xd1\x58\x9e\xaa\xff\x87\xe9\xc7\xb3\x17\x9f\x5b\x3d\x9e\xbd\xf8\x4c\xda\xf1\xec\xc5\xf5\x28\xc7\x72\xd5\xd1\x8d\xe5\x6a\x07\xd5\x58\xae\xae\xac\x19\xbf\xed\xa8\x19\xbf\xfd\xc1\x9a\xf1\xfa\xf3\xab\xc6\xeb\xcf\xa6\x1b\xaf\xaf\x4b\x39\x36\x3d\xed\xd8\xec\xa4\x1e\x9b\x4f\xd0\x8f\x77\x3b\xea\xc7\xbb\x3f\x48\x3f\x60\x53\x5e\xd7\x8c\x85\x5c\x19\x55\x13\xc2\x39\x2f\xaa\xed\xa3\xb2\x05\xe8\x84\xfc\x86\x96\x45\x03\x09\x9e\xb0\xb9\x2e\x65\x00\x60\xd7\xa3\x0e\x00\xaa\xa3\x10\xf0\xcb\x93\x09\x09\x5d\x7a\x20\x2b\xe9\xaa\xb0\x30\xe9\x81\x98\x02\x2d\xd0\x7d\xe4\x13\xdb\x4e\x97\xa6\x29\x93\x56\x55\xee\xdf\x47\x0b\xd8\x22\x6f\x94\x41\x1e\x1d\x22\xe8\x0e\x5a\x18\x1f\xab\x37\xab\x90\x80\x33\xd4\xb5\x8f\xa8\x9e\x3c\xb9\x09\xd2\xc1\x4c\x16\xe8\x8e\xe1\xc5\xd0\x01\xea\xfe\x56\x97\x40\xf7\xdf\xa9\xbd\xb0\x94\xff\x6f\xa7\xbe\x2f\x26\xf6\xc9\x45\xad\xbd\x2f\xae\x49\x7b\xa5\xdc\xbb\x9a\xaa\x29\x6f\xad\xcf\x5b\x28\xef\xc0\x63\x02\xa8\x2b\xe8\xaf\x66\x05\x0d\x9c\x71\x05\x56\xe8\xff\x70\x0d\x7e\xb1\xac\x58\xc5\x3f\xb7\x03\x5e\x01\x96\xeb\x52\x61\x80\x76\x3d\x2a\x2c\x09\xd3\x55\x78\xb5\x1c\xf5\xbf\xa2\xca\xa8\xfe\xaa\x1e\x81\x1e\x28\xaf\xbe\xd8\x13\xe1\x60\xfb\xcb\x8b\x49\x14\x0c\xd4\xf2\x53\x05\x76\x4d\x3e\xe7\xcf\x25\xb1\x11\x97\x23\x6a\xec\x2e\xb0\x17\x03\x81\x3d\xb9\x8a\xc0\x1e\xe4\xf9\xe7\x8e\x7c\x59\x9e\x7f\xa6\xc8\x57\x3e\xf9\x7d\x1d\x73\xe6\xbc\x37\x67\xce\x77\x9a\x33\xe7\x5b\xcf\x99\xfb\x23\xc2\x7e\x13\xc8\xc2\x81\x51\x73\xf0\x9b\xb1\xd5\xea\x52\x34\xab\xc7\x10\xf9\x30\x7c\x67\x58\x69\x9f\x87\x37\xc3\x18\x06\x52\xfb\x6d\xcc\x8d\xf6\x25\x0e\x45\xc3\xa7\x7a\x74\xf9\xcd\xbc\xbb\xf2\x60\xa1\x9e\x00\x5f\x16\xfa\xda\xe6\xda\xf4\xc2\xf1\x6a\x79\xc6\x57\xd5\x25\xfa\x55\x3d\x31\x0c\x15\x41\xbd\x1a\x10\x83\x65\x45\xa5\x20\xeb\x03\x13\x9c\xda\xad\x34\x6f\xa2\x77\xbd\xcb\xba\x3c\x59\x94\x45\x99\xb1\x45\x85\x52\x28\x2f\x17\x9a\x6d\x00\x52\xc7\xea\x6f\xbb\x2e\x5d\x13\x53\xff\x72\x0d\xeb\xc0\x43\x0a\xec\xe6\xd8\x61\xd7\xe4\xd9\x99\x50\x4b\x36\xdf\xeb\xf0\x7e\x94\x71\xc8\xe8\x90\x1b\xce\x69\x60\xb7\x62\x22\xef\x8a\xf9\x13\x6c\xf5\x42\x67\x75\xbf\x17\x9d\x3d\xdf\xae\xcd\x7e\x22\xb0\x37\x83\xf6\xe2\x6f\xd7\x65\xed\xe9\xae\x50\x30\xc5\x09\x66\x38\x85\x3b\x35\x19\xce\x31\xc7\xc5\xde\x00\xc8\xdb\x7f\xa3\xae\x4e\x11\xf6\xb6\xde\x1e\x00\xa5\x9b\x36\x6a\x3b\x70\xcb\x17\xea\xf0\x04\xb8\xc5\xfa\x8b\xfc\xef\x6f\xbf\x19\x2e\x60\x88\xb8\xbf\xb1\x81\xbf\x1c\xa1\xe1\x2e\x98\xfe\x27\xc7\xe6\xba\xfa\x51\x43\x46\xff\x2c\xa0\x35\x68\xef\x03\x90\x36\x34\xe7\x8b\x93\x6a\x86\x6e\x23\xba\xe5\x51\xea\xbe\xa3\x79\xb8\x5c\x7c\xe0\xab\x7a\x6a\xa8\xb9\x61\xe5\x1f\xc4\xa0\x5d\xdf\x0e\xd8\xca\xf1\xd4\xa3\x76\x23\xdd\xce\xce\xdc\x47\xf4\xaa\xeb\x44\x6f\xad\x51\xce\x2a\x86\xd8\x7a\x47\x3c\x5b\xaf\x64\x75\x77\x0a\x37\x9a\x83\x3e\xa8\x96\xaf\x7d\x62\xdf\x0a\x81\xe2\x4f\x38\xb3\xa3\x70\x75\x95\xca\x70\x72\xa7\xae\xf7\x44\x0a\xb3\x21\xb2\x16\xaf\xe9\x14\x8f\x14\x9b\x01\x96\xec\xee\xd6\x87\xf7\xbb\xb8\xdd\x37\xbd\xda\x2d\xbc\xba\xd5\x9b\xc1\x11\x7e\xf1\x57\xd3\x70\x70\x76\xbe\x9e\x4d\xea\x40\x4a\xc4\x08\xa6\x79\xa5\xb9\x76\x2f\x96\x40\x86\x73\xb2\x75\x28\xa2\x09\xb8\xf6\x20\x35\xcc\x69\xd7\x6c\xac\x07\x49\x06\x56\x01\x60\x84\x4a\x66\xcb\x33\x18\x24\x2d\x63\x3f\x1a\x0d\x5b\x1b\xb5\xe7\x28\x9b\x2f\x17\xae\x99\xca\xb6\x2a\x0d\x70\xfa\xba\x0c\x3f\xda\x75\x19\x8a\x9d\xba\xac\x43\x86\x28\x45\x92\xdb\x9c\x7c\x35\x9d\x74\x7d\x08\xf5\xff\x0a\x8a\xfd\x57\xc9\x99\x21\xd0\xda\x97\x4a\x78\x43\x37\x5b\x9f\x1a\xb3\x23\x80\x3b\x4c\xf5\xc6\xba\x0c\x4e\x2c\x68\x1a\x13\xba\xe8\xd8\xcf\xa8\x19\x5c\x6c\x63\x03\x17\x4a\xe5\x6b\xf0\x6f\xca\xb7\x26\xb6\xdb\x55\x15\x2a\x77\xf6\x97\x9b\xf0\xd8\x7a\x6e\xa6\x77\x5a\x46\x1d\x8d\xf9\xf8\x76\x4a\xc3\x6d\xce\xbb\x1c\xde\xfe\x0b\x9a\x55\xd5\xd9\xfa\xee\xe1\xe1\x69\x35\x5b\x1f\xa4\xfc\xf0\xbc\x2a\xe8\xcf\x6b\xf4\x81\x1c\xe0\x03\x82\xd2\x4b\xf4\x3f\x4e\x59\x35\x2b\xd9\x5a\x68\x4c\x7b\x40\x06\x4e\x85\xc8\xc3\x1e\x87\x87\xe8\x5b\x5e\xc9\xeb\x70\x9c\x0b\x76\x97\x2c\x9d\xf3\x35\xfa\x87\xc2\xf4\x8f\x1b\x5f\xc1\x31\xfe\x15\xe7\x8f\x9a\xf3\x2f\x83\x93\x34\xe8\x96\x14\xde\x2d\x74\xf3\x66\xfd\xf3\x3d\x3b\x78\xf4\x0f\xd9\x1d\x0d\xf8\x53\xf8\xa1\x85\x7d\xaa\xbe\x77\x41\xab\x5f\x6f\xde\x34\x9c\xcf\x39\xea\x10\xd9\x54\x76\x92\x71\x02\x27\x67\xfe\x31\x95\xa7\xf1\x7f\x58\xe6\xfc\xe0\xe7\x35\x5a\xae\xd0\x37\xf2\x28\x4d\x59\x94\x3c\x47\xd9\x32\xe7\x53\x80\xc2\x16\x39\x3a\x5f\x73\x54\x56\x62\x5c\xfb\x87\xe0\xa3\xd6\x07\x75\x0e\xa7\xe9\xc3\x89\xfa\xde\xed\x83\xfc\xf5\x9e\x3c\x93\xd4\x36\x3b\x68\x6a\x1f\xe9\xc0\x7e\xfb\x4d\xfb\x76\x70\x51\x2e\x72\x31\xbb\xec\xd4\x91\x47\x87\x04\x2d\x48\xff\x19\x0e\xfb\xdc\xf8\xea\xf0\xf6\x9d\x6b\xfb\xbb\x7d\x78\x43\xf6\x76\x5d\xad\xca\xc5\xc9\xe3\xd5\xf2\xf4\xe1\x8c\xad\x1e\x2e\x73\x21\xb9\x97\xf0\xe3\x41\xa1\xfd\xaa\x98\xff\x8a\xbd\xe7\x0b\xc9\xe3\xbe\xca\x9e\x9d\x2f\x2e\x05\x7f\x6f\x7c\xd5\x78\xb0\xf3\x6c\x4d\x72\x2e\x7e\x9c\x48\x3c\xb2\x83\xb0\xb5\x09\x87\xef\xeb\x21\x10\x7e\xca\x96\xe7\x8b\x8a\xaf\xd4\xca\x25\xfc\x34\xaf\x7d\x85\x6c\xde\x3a\x0b\x28\x85\xfb\x8c\xf5\x17\xbe\xa9\x56\x4c\x7c\xb9\x98\x95\x73\x8e\x26\x35\xb4\xfb\x0a\x88\x44\xfd\x15\xb4\x69\x01\x66\xaa\x7b\x0f\xaa\xba\xc1\xfe\xbe\x30\xf5\xaf\x40\xa6\xb2\xf2\xd7\x47\xc8\xdb\x7c\x4b\x3d\x4f\xc8\x5c\xfe\x74\x1f\x7e\xfa\xe6\xf1\x63\xf1\x93\x05\x93\x60\x17\x4c\xd7\xd7\xe7\xab\xd5\xf2\x84\x55\x7c\x0a\x5a\x57\xcd\xf8\x8a\xc3\x3d\x4f\xb4\xe0\x9b\x0a\x09\x12\x58\x56\xf1\x15\x34\x82\x6e\x6c\x43\x1f\x10\x38\x91\xd5\x6f\x22\x6f\xf3\xf8\xa1\xe7\xed\x09\x0d\xf5\x36\xdf\xc2\xc7\x5f\x85\x73\x9e\x2f\x2f\x5a\xfc\xd0\xec\x2b\xc9\x79\x39\x94\x4f\x54\x17\x05\x00\xff\xf1\xe3\x3d\xb8\x9a\xe9\xed\xa1\x7d\xa4\x41\x86\x82\xfd\x3a\xe3\x90\xc2\xde\x46\xc1\xaa\xab\xe7\x8b\x53\x56\x65\x33\x9e\xb7\xf8\xee\xa1\xe5\x62\x7e\x89\xd8\xd9\x19\x87\x7e\x97\x6b\x30\x40\x74\xbe\x28\xab\xa9\x98\x68\x66\x6c\xcd\x61\xb6\x29\x18\xd1\x40\x6a\xea\x08\x26\x55\xf5\xb9\xa8\x06\xaa\x18\xea\x99\xf6\xf5\x8c\x95\xab\x61\xcf\xa0\x5f\x8a\xd6\xaf\x14\xeb\xee\xdc\x51\xb4\xdf\xe8\x77\xc0\xd2\x52\x54\x14\xff\x57\xfe\x5e\xd6\xaa\xad\xf1\x2a\xc6\xc0\x17\x60\x0c\x30\x0a\xb7\xb6\xd0\x68\xb9\x8c\x5b\xba\x4a\x5e\x2e\x72\xbe\x41\x47\xe8\x0e\x36\xaa\x7d\x63\x47\xb7\x6e\x69\xca\xbf\xbf\x2f\x9b\x59\x94\x1f\xf0\xbc\x81\x2a\x6f\xfb\xca\x2e\x54\xe9\xb1\x90\xb8\xe4\x8c\xfc\xf5\xce\x51\x2d\xfe\x7b\x1a\xbf\xd0\xfe\x91\xc1\x7f\xd4\x80\xbe\xfe\x1a\x61\xaf\x56\x20\xf4\x9b\xb2\x21\x25\x92\x9a\x12\xa9\xac\xe8\x37\xd4\xd1\xc3\x86\xf9\x5b\x20\x02\x80\x36\x21\x35\xcc\xcf\x66\x3c\x7b\xff\x32\x63\x73\xb6\xfa\x5f\xa2\xd5\x44\xc8\xe1\xf9\xb2\x5c\xc8\xd3\xd4\xc0\x80\xe6\xa7\xae\xc5\xb7\x3f\x4b\xab\x6f\x99\x53\xcd\x56\xcb\x0b\xf4\x68\xb5\x5a\xae\x26\xd0\xab\x5b\x4f\x44\x28\xd4\xaa\xe6\xdf\xf7\x6f\xa1\xfd\x16\xc0\x41\xb5\x94\x9e\x75\x82\xa3\xbd\x83\x6a\xf9\xf7\xb3\x33\xbe\x7a\xc8\xd6\x7c\xb2\x87\xf6\x25\x00\xa1\xf2\x8b\x65\x25\x14\x1c\x88\x95\x7c\xb9\x25\x0a\xeb\x8e\x7e\xfc\x0c\x23\x41\xcb\x27\x88\xaa\x45\x24\xde\xb2\x63\x2a\xb7\xd9\xd4\xe0\x24\xb9\x6c\x90\xc6\x44\x67\xe0\xd7\x75\x1b\x29\x51\x58\xaa\xdc\x50\x6f\xaf\x2f\x17\x69\x10\x0f\xeb\x86\x26\xb1\x68\x60\x6f\x2a\xe5\x7c\xfc\x98\x2a\x5f\xa7\xdc\x1c\xbe\x93\x5e\x56\x1c\xad\xf9\x7f\x9d\xf3\x45\x06\x8e\xce\x4e\x68\x8b\xa3\x56\x1d\x18\x08\x2f\x4f\xd3\xe5\xbc\x31\x24\x1b\x66\xea\x75\x31\x93\x21\xe6\x06\xd2\x38\x93\x22\xc9\x20\xac\x18\xf4\xd0\x6b\x48\x6a\x0e\x1e\x1b\x88\x00\x37\xac\x13\xe1\x0f\x89\x70\x28\xfc\xbd\x1d\x89\xc4\x44\x52\xe9\x29\x2a\x1f\x79\x1d\x10\xfb\x47\x16\xad\x89\xb6\xe8\xcc\x23\x6f\xd0\x99\xe0\x93\x38\x8a\xa9\x22\x36\x96\xc4\x3e\xde\x92\x58\x4c\x76\xed\x54\x5b\xd3\x44\x55\xb7\xa3\x5d\x0b\x68\x74\x13\x20\xf4\x4d\x42\x84\xfe\x6a\x9c\xe8\x07\x4d\x0d\x50\x11\xba\x0f\x83\xab\x41\xd4\xd4\xd6\x1f\x1d\x54\x9a\xaa\xf5\x0f\x42\x08\xd2\x5b\x6d\x39\xb8\xb4\x3d\xd6\x11\xeb\xa3\x8c\x06\x72\xff\xc8\x61\xfa\x3d\x8f\xde\x36\xfb\x5c\x81\x70\xc3\xfb\x15\x67\xf9\xc3\xe5\xa2\x2a\x17\xe7\x70\x79\x16\xa4\xdf\xba\x22\x41\xc9\x77\xd0\xf7\xaf\x8f\x80\xac\x87\x22\xb0\x30\x8c\x06\xb7\xbe\x5b\x7c\x60\xf3\x32\x87\x4a\x92\xdb\xb7\x54\xb7\x1a\x7e\x77\xb1\x20\x09\x10\x16\x0a\xde\x34\x78\xde\x2a\x33\x11\x4d\x9b\x1f\xf7\xf7\x45\x30\x5e\x7b\xa8\x1e\x98\x9b\xd2\x8d\xc8\x40\x50\x78\xc9\x5f\x35\x67\x68\xac\xed\x3f\x6e\x08\x3b\x3c\x44\xdf\x15\xe8\x82\x23\x11\xaf\x9d\x9f\x21\x11\xa9\x4e\x51\x59\xfd\xdf\xff\xfd\x7f\xea\x61\x49\x07\x01\x14\xdf\xb0\xf4\x7c\x50\xf1\xd6\xc0\xf9\x4b\xed\x7d\x09\x56\x30\x69\xb5\x5c\x54\xc6\xba\x1a\x12\xfd\x8b\xaf\x7f\x09\x0c\xea\x3b\x94\xd5\x27\x88\xaa\x0b\xe9\x68\x28\x75\xc5\xd9\x82\xcd\xe1\xf2\x43\xc3\xc7\x17\x9c\xe5\xa8\x28\x57\xeb\xaa\xe6\x12\x74\x6b\x77\x31\x0f\x47\x37\x34\x59\x2c\x87\xec\x5d\xef\xd5\x3a\x21\x11\xdd\x54\xf2\x57\x9e\x55\xa3\xb5\xe1\x6f\x4d\xeb\x70\x0c\xeb\xc1\x79\x54\x2b\xd4\xc3\x1a\x14\x88\x05\x1d\x59\x0c\xe6\x5e\xdf\x1f\xe8\xc0\xb0\x9c\x66\x40\xce\x9d\x46\xba\xa6\x00\xac\xd1\xde\x56\x7d\x35\x1f\xd5\x0d\xe0\x77\x50\xc1\x3a\xac\x97\x7d\xf7\xfb\xbc\x3d\x65\x97\xa8\x5c\x64\xf3\x73\x98\x84\x88\xc9\x85\x3e\xa5\x31\x71\xf9\x71\xcd\x9d\x47\x3b\x70\x07\x54\xf9\x6a\x0c\xf4\xd4\x3c\x8d\xc0\xd9\x24\x89\x4b\x67\xa8\x6f\x63\xa8\x07\xc1\x8b\x64\xd8\x58\x7c\xf0\x39\x79\x3e\x1c\xe1\xfb\x1c\xa5\x8a\xa3\x8f\xaf\x97\xa3\xe0\x32\xae\xc8\xf4\x18\x98\xee\x6d\xfa\x6c\xf7\x36\xde\xc3\x3d\xf4\x1b\x70\x64\x22\x69\x90\xbf\x36\xf2\x08\xac\xf2\x80\x19\x95\x61\x8e\x81\x3d\x7d\x0a\x66\x96\x44\xcd\x4f\xa3\x14\xfe\xfe\xea\xf1\x1d\x8a\x72\x58\x29\xe3\x79\xe3\x79\x6b\xb7\xa9\x6e\x60\x35\xdf\xc1\xa1\x69\xdf\xc1\xff\xdc\xeb\xc5\x24\x2a\xd6\x68\x47\x63\x49\x5f\x03\xaf\x1b\x92\x68\xd5\x6a\xaf\x06\x58\x74\x07\xa8\x05\x25\x9a\x8f\x6d\x57\x7f\x3a\xe1\x4e\xbb\x4e\x54\x9d\x9e\x69\xd1\xc8\xa4\x3a\x3d\x43\x47\xbd\xb1\x64\x0f\xfd\xe5\xe8\x48\x3a\xe5\x7e\x74\xa2\x36\x31\xaa\xd3\xb3\x7e\x9c\xa1\x4d\xd0\xdb\xda\x7b\x9f\x73\xf1\x4d\xb0\x15\x1d\x01\x81\xb7\x3e\xf0\xd5\xba\x5c\x2e\x6e\xdd\x45\xb7\x60\xd1\xf7\xd6\x54\xfc\x2a\xe9\xb9\x75\x57\x8b\x0a\xe1\x77\xd9\x5d\xf5\xbb\xfc\x72\xe3\xab\x8f\x6a\x91\xee\xe5\xf2\x94\xa3\x07\x4f\xbf\x45\xe9\x79\x39\xcf\xd1\xf2\xac\x2a\x4f\xcb\x5f\xf8\x6a\x3d\x45\xf3\xf2\x3d\x47\xab\x83\x9f\xd7\x53\x39\x25\x86\x95\xf6\xf5\x19\xcf\xca\xa2\xcc\x84\xf1\xe6\x25\x08\xfc\x8c\x55\x15\x5f\x2d\xd6\x00\x0f\x1a\x55\x33\x8e\x8a\xe5\x7c\xbe\xbc\x28\x17\x27\x77\xe5\x9a\xa7\x50\xbf\xde\xbd\x48\x74\xab\x56\x9a\x5b\x72\x71\xb7\x53\xe1\x80\x9d\xe6\xbd\x55\xd4\xe6\x8a\xa4\x28\xbb\xf1\x95\x14\x97\xba\x34\xd9\x2c\x73\x77\x07\x30\xd1\x67\x90\x1d\x08\xa7\x9d\x5d\xf4\x56\x8d\xff\xa2\x7d\x3f\x58\x2c\x73\xfe\xea\xf2\x8c\xb7\xc1\x5c\xbb\x56\xad\x26\x1e\xe5\x42\x5f\x37\x7e\x51\x2e\x4e\x96\xff\xf3\x25\xfa\xe0\x1d\xd0\x03\x0f\xa6\xe7\x6d\x0b\xed\x2e\x69\x43\x8c\x72\x8d\x35\x24\xb6\xba\x98\xb1\x79\x0f\x52\x7c\xe0\xdd\x91\x0b\x31\xab\xfa\x6c\x94\xbc\xc5\xa8\x7e\x9b\xb1\xf5\xb3\x8b\xc5\xf3\xfa\x08\xcc\x91\xaa\x74\xd0\xfd\x1d\xaa\x37\x5b\x24\x90\x35\x4e\x32\xa5\xf6\x18\xdd\xea\x72\x7f\x48\x94\xc3\x45\xe2\x3d\xc1\x1b\x9d\x57\x6f\xde\xcb\x04\x86\xa2\x06\x7c\xee\x2c\x7e\xf5\xfa\xf5\x62\x56\x2e\x96\xa2\x57\x0c\x5d\xf0\x14\xa9\x8b\xaa\x6a\xd5\xfa\x40\x29\xb4\xe2\xc9\xc7\x1b\xea\x8a\x2a\x6c\x9b\x7c\x9c\xfe\xfa\xf1\xed\x94\x46\xdb\x6c\x89\x0c\x6e\xec\xbe\x7e\xfa\xe4\xb8\xaa\xce\x5e\x88\x21\x63\x5d\x35\xd0\xfe\x9a\x96\x27\xf2\x30\xcb\xc1\xcf\xeb\xbf\x6e\x03\xf9\xd6\xf9\x9a\xc3\x84\x2d\xab\x6e\xdd\xbb\x31\x44\xf4\x4d\x79\xf2\x03\x00\xbc\x27\x3a\xfc\xf3\x7a\x26\x9c\x72\x79\xb2\x58\xae\xf8\xdd\x79\xb9\xe0\x37\x1a\xd4\x17\x3c\xf5\xb7\x42\x29\x84\xf4\x23\x4f\xe5\xd8\x24\xaf\x19\xdf\x3a\x38\x9c\x97\xe9\xa1\x00\x21\x9c\xf3\x8d\xc3\x43\x94\x2f\x17\x15\x5a\x7e\xe0\xab\x55\x99\xf3\x7a\xc3\xa1\xde\xdf\xb8\xa1\x5d\x41\x56\x3b\x07\xc2\xc1\xdd\x6a\x0e\x34\xc0\x7e\x44\xa7\xc2\x81\x44\xd9\xad\x25\x14\x04\xb6\xc9\xf4\x2a\x40\xdc\xbd\x1b\x1f\x0d\xdc\x90\x25\x6a\x63\xab\xa6\xf8\xaf\x77\x09\xf9\xf8\x56\x70\x61\xfa\x46\x72\xe1\xed\xde\x8d\xc3\xc3\xff\x0f\xad\x97\xe7\xab\x8c\x3f\x65\x67\x67\xe5\xe2\xe4\xef\x2f\x9e\x1c\x89\xc2\x3b\x73\x38\x44\xfa\xf3\xfa\xe0\x94\x9d\xdd\xf8\x7f\x01\x00\x00\xff\xff\x78\x96\x95\xca\xe4\x2c\x06\x00") func web3JsBytes() ([]byte, error) { return bindataRead( diff --git a/internal/jsre/deps/web3.js b/internal/jsre/deps/web3.js index 462ffe031f1b..83cbc36e7843 100644 --- a/internal/jsre/deps/web3.js +++ b/internal/jsre/deps/web3.js @@ -3814,7 +3814,9 @@ var outputTransactionReceiptFormatter = function (receipt){ receipt.transactionIndex = utils.toDecimal(receipt.transactionIndex); receipt.cumulativeGasUsed = utils.toDecimal(receipt.cumulativeGasUsed); receipt.gasUsed = utils.toDecimal(receipt.gasUsed); - + if(receipt.effectiveGasPrice !== undefined) { + receipt.effectiveGasPrice = utils.toBigNumber(receipt.effectiveGasPrice); + } if(utils.isArray(receipt.logs)) { receipt.logs = receipt.logs.map(function(log){ return outputLogFormatter(log); From c0a63a529508b6b945cf9a3fb2db2548b971802f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 28 May 2024 10:56:53 +0800 Subject: [PATCH 099/479] tests: fix eip1559 tx on non-eip1559 network (#23054) --- tests/state_test_util.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/state_test_util.go b/tests/state_test_util.go index a09fb119925f..51c4664eafca 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -19,6 +19,7 @@ package tests import ( "encoding/hex" "encoding/json" + "errors" "fmt" "math/big" "strings" @@ -277,6 +278,9 @@ func (tx *stTransaction) toMessage(ps stPostState, number *big.Int, baseFee *big gasPrice = math.BigMin(new(big.Int).Add(tx.MaxPriorityFeePerGas, baseFee), tx.MaxFeePerGas) } + if gasPrice == nil { + return nil, errors.New("no gas price provided") + } msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, tx.MaxFeePerGas, tx.MaxPriorityFeePerGas, data, accessList, true, nil, number) return msg, nil From 26fe46c008bd1d7a2e2763d02379cdb50f705a27 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 29 May 2024 12:22:48 +0800 Subject: [PATCH 100/479] eth/gasprice: implement feeHistory API (#23033) --- cmd/utils/flags.go | 3 +- eth/api_backend.go | 4 + eth/ethconfig/config.go | 20 ++- eth/gasprice/feehistory.go | 294 ++++++++++++++++++++++++++++++++ eth/gasprice/feehistory_test.go | 88 ++++++++++ eth/gasprice/gasprice.go | 80 +++++---- eth/gasprice/gasprice_test.go | 49 +++++- internal/ethapi/api.go | 34 ++++ internal/ethapi/backend.go | 1 + internal/web3ext/web3ext.go | 6 + les/api_backend.go | 13 +- 11 files changed, 535 insertions(+), 57 deletions(-) create mode 100644 eth/gasprice/feehistory.go create mode 100644 eth/gasprice/feehistory_test.go diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 427a1841eebc..d45919a76f69 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1016,8 +1016,7 @@ func setGPO(ctx *cli.Context, cfg *gasprice.Config, light bool) { // If we are running the light client, apply another group // settings for gas oracle. if light { - cfg.Blocks = ethconfig.LightClientGPO.Blocks - cfg.Percentile = ethconfig.LightClientGPO.Percentile + *cfg = ethconfig.LightClientGPO } if ctx.GlobalIsSet(GpoBlocksFlag.Name) { cfg.Blocks = ctx.GlobalInt(GpoBlocksFlag.Name) diff --git a/eth/api_backend.go b/eth/api_backend.go index cd395b364f39..b0beead8c548 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -348,6 +348,10 @@ func (b *EthApiBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) return b.gpo.SuggestTipCap(ctx) } +func (b *EthApiBackend) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock rpc.BlockNumber, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) { + return b.gpo.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) +} + func (b *EthApiBackend) ChainDb() ethdb.Database { return b.eth.ChainDb() } diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index efdd2197d92e..81760c03a050 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -36,18 +36,22 @@ import ( // FullNodeGPO contains default gasprice oracle settings for full node. var FullNodeGPO = gasprice.Config{ - Blocks: 20, - Percentile: 60, - MaxPrice: gasprice.DefaultMaxPrice, - IgnorePrice: gasprice.DefaultIgnorePrice, + Blocks: 20, + Percentile: 60, + MaxHeaderHistory: 0, + MaxBlockHistory: 0, + MaxPrice: gasprice.DefaultMaxPrice, + IgnorePrice: gasprice.DefaultIgnorePrice, } // LightClientGPO contains default gasprice oracle settings for light client. var LightClientGPO = gasprice.Config{ - Blocks: 2, - Percentile: 60, - MaxPrice: gasprice.DefaultMaxPrice, - IgnorePrice: gasprice.DefaultIgnorePrice, + Blocks: 2, + Percentile: 60, + MaxHeaderHistory: 300, + MaxBlockHistory: 5, + MaxPrice: gasprice.DefaultMaxPrice, + IgnorePrice: gasprice.DefaultIgnorePrice, } // Defaults contains default settings for use on the Ethereum main net. diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go new file mode 100644 index 000000000000..c3016bfb9c84 --- /dev/null +++ b/eth/gasprice/feehistory.go @@ -0,0 +1,294 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package gasprice + +import ( + "context" + "errors" + "math/big" + "sort" + "sync/atomic" + + "github.com/XinFinOrg/XDPoSChain/consensus/misc" + "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/log" + "github.com/XinFinOrg/XDPoSChain/rpc" +) + +var ( + errInvalidPercentiles = errors.New("Invalid reward percentiles") + errRequestBeyondHead = errors.New("Request beyond head block") +) + +const maxBlockCount = 1024 // number of blocks retrievable with a single query + +// blockFees represents a single block for processing +type blockFees struct { + // set by the caller + blockNumber rpc.BlockNumber + header *types.Header + block *types.Block // only set if reward percentiles are requested + receipts types.Receipts + // filled by processBlock + reward []*big.Int + baseFee, nextBaseFee *big.Int + gasUsedRatio float64 + err error +} + +// txGasAndReward is sorted in ascending order based on reward +type ( + txGasAndReward struct { + gasUsed uint64 + reward *big.Int + } + sortGasAndReward []txGasAndReward +) + +func (s sortGasAndReward) Len() int { return len(s) } +func (s sortGasAndReward) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} +func (s sortGasAndReward) Less(i, j int) bool { + return s[i].reward.Cmp(s[j].reward) < 0 +} + +// processBlock takes a blockFees structure with the blockNumber, the header and optionally +// the block field filled in, retrieves the block from the backend if not present yet and +// fills in the rest of the fields. +func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { + chainconfig := oracle.backend.ChainConfig() + if bf.baseFee = bf.header.BaseFee; bf.baseFee == nil { + bf.baseFee = new(big.Int) + } + if chainconfig.IsEIP1559(big.NewInt(int64(bf.blockNumber + 1))) { + bf.nextBaseFee = misc.CalcBaseFee(chainconfig, bf.header) + } else { + bf.nextBaseFee = new(big.Int) + } + bf.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit) + if len(percentiles) == 0 { + // rewards were not requested, return null + return + } + if bf.block == nil || (bf.receipts == nil && len(bf.block.Transactions()) != 0) { + log.Error("Block or receipts are missing while reward percentiles are requested") + return + } + + bf.reward = make([]*big.Int, len(percentiles)) + if len(bf.block.Transactions()) == 0 { + // return an all zero row if there are no transactions to gather data from + for i := range bf.reward { + bf.reward[i] = new(big.Int) + } + return + } + + sorter := make(sortGasAndReward, len(bf.block.Transactions())) + for i, tx := range bf.block.Transactions() { + reward, _ := tx.EffectiveGasTip(bf.block.BaseFee()) + sorter[i] = txGasAndReward{gasUsed: bf.receipts[i].GasUsed, reward: reward} + } + sort.Sort(sorter) + + var txIndex int + sumGasUsed := sorter[0].gasUsed + + for i, p := range percentiles { + thresholdGasUsed := uint64(float64(bf.block.GasUsed()) * p / 100) + for sumGasUsed < thresholdGasUsed && txIndex < len(bf.block.Transactions())-1 { + txIndex++ + sumGasUsed += sorter[txIndex].gasUsed + } + bf.reward[i] = sorter[txIndex].reward + } +} + +// resolveBlockRange resolves the specified block range to absolute block numbers while also +// enforcing backend specific limitations. The pending block and corresponding receipts are +// also returned if requested and available. +// Note: an error is only returned if retrieving the head header has failed. If there are no +// retrievable blocks in the specified range then zero block count is returned with no error. +func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlockNumber rpc.BlockNumber, blockCount, maxHistory int) (*types.Block, types.Receipts, rpc.BlockNumber, int, error) { + var ( + headBlockNumber rpc.BlockNumber + pendingBlock *types.Block + pendingReceipts types.Receipts + ) + + // query either pending block or head header and set headBlockNumber + if lastBlockNumber == rpc.PendingBlockNumber { + if pendingBlock, pendingReceipts = oracle.backend.PendingBlockAndReceipts(); pendingBlock != nil { + lastBlockNumber = rpc.BlockNumber(pendingBlock.NumberU64()) + headBlockNumber = lastBlockNumber - 1 + } else { + // pending block not supported by backend, process until latest block + lastBlockNumber = rpc.LatestBlockNumber + blockCount-- + if blockCount == 0 { + return nil, nil, 0, 0, nil + } + } + } + if pendingBlock == nil { + // if pending block is not fetched then we retrieve the head header to get the head block number + if latestHeader, err := oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber); err == nil { + headBlockNumber = rpc.BlockNumber(latestHeader.Number.Uint64()) + } else { + return nil, nil, 0, 0, err + } + } + if lastBlockNumber == rpc.LatestBlockNumber { + lastBlockNumber = headBlockNumber + } else if pendingBlock == nil && lastBlockNumber > headBlockNumber { + return nil, nil, 0, 0, errRequestBeyondHead + } + if maxHistory != 0 { + // limit retrieval to the given number of latest blocks + if tooOldCount := int64(headBlockNumber) - int64(maxHistory) - int64(lastBlockNumber) + int64(blockCount); tooOldCount > 0 { + // tooOldCount is the number of requested blocks that are too old to be served + if int64(blockCount) > tooOldCount { + blockCount -= int(tooOldCount) + } else { + return nil, nil, 0, 0, nil + } + } + } + // ensure not trying to retrieve before genesis + if rpc.BlockNumber(blockCount) > lastBlockNumber+1 { + blockCount = int(lastBlockNumber + 1) + } + return pendingBlock, pendingReceipts, lastBlockNumber, blockCount, nil +} + +// FeeHistory returns data relevant for fee estimation based on the specified range of blocks. +// The range can be specified either with absolute block numbers or ending with the latest +// or pending block. Backends may or may not support gathering data from the pending block +// or blocks older than a certain age (specified in maxHistory). The first block of the +// actually processed range is returned to avoid ambiguity when parts of the requested range +// are not available or when the head has changed during processing this request. +// Three arrays are returned based on the processed blocks: +// - reward: the requested percentiles of effective priority fees per gas of transactions in each +// block, sorted in ascending order and weighted by gas used. +// - baseFee: base fee per gas in the given block +// - gasUsedRatio: gasUsed/gasLimit in the given block +// +// Note: baseFee includes the next block after the newest of the returned range, because this +// value can be derived from the newest block. +func (oracle *Oracle) FeeHistory(ctx context.Context, blockCount int, lastBlockNumber rpc.BlockNumber, rewardPercentiles []float64) (firstBlockNumber rpc.BlockNumber, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) { + if blockCount < 1 { + // returning with no data and no error means there are no retrievable blocks + return + } + if blockCount > maxBlockCount { + blockCount = maxBlockCount + } + for i, p := range rewardPercentiles { + if p < 0 || p > 100 || (i > 0 && p < rewardPercentiles[i-1]) { + return 0, nil, nil, nil, errInvalidPercentiles + } + } + + processBlocks := len(rewardPercentiles) != 0 + // limit retrieval to maxHistory if set + var maxHistory int + if processBlocks { + maxHistory = oracle.maxBlockHistory + } else { + maxHistory = oracle.maxHeaderHistory + } + + var ( + pendingBlock *types.Block + pendingReceipts types.Receipts + ) + if pendingBlock, pendingReceipts, lastBlockNumber, blockCount, err = oracle.resolveBlockRange(ctx, lastBlockNumber, blockCount, maxHistory); err != nil || blockCount == 0 { + return + } + firstBlockNumber = lastBlockNumber + 1 - rpc.BlockNumber(blockCount) + + processNext := int64(firstBlockNumber) + resultCh := make(chan *blockFees, blockCount) + threadCount := 4 + if blockCount < threadCount { + threadCount = blockCount + } + for i := 0; i < threadCount; i++ { + go func() { + for { + blockNumber := rpc.BlockNumber(atomic.AddInt64(&processNext, 1) - 1) + if blockNumber > lastBlockNumber { + return + } + + bf := &blockFees{blockNumber: blockNumber} + if pendingBlock != nil && blockNumber >= rpc.BlockNumber(pendingBlock.NumberU64()) { + bf.block, bf.receipts = pendingBlock, pendingReceipts + } else { + if processBlocks { + bf.block, bf.err = oracle.backend.BlockByNumber(ctx, blockNumber) + if bf.block != nil { + bf.receipts, bf.err = oracle.backend.GetReceipts(ctx, bf.block.Hash()) + } + } else { + bf.header, bf.err = oracle.backend.HeaderByNumber(ctx, blockNumber) + } + } + if bf.block != nil { + bf.header = bf.block.Header() + } + if bf.header != nil { + oracle.processBlock(bf, rewardPercentiles) + } + // send to resultCh even if empty to guarantee that blockCount items are sent in total + resultCh <- bf + } + }() + } + + reward = make([][]*big.Int, blockCount) + baseFee = make([]*big.Int, blockCount+1) + gasUsedRatio = make([]float64, blockCount) + firstMissing := blockCount + + for ; blockCount > 0; blockCount-- { + bf := <-resultCh + if bf.err != nil { + return 0, nil, nil, nil, bf.err + } + i := int(bf.blockNumber - firstBlockNumber) + if bf.header != nil { + reward[i], baseFee[i], baseFee[i+1], gasUsedRatio[i] = bf.reward, bf.baseFee, bf.nextBaseFee, bf.gasUsedRatio + } else { + // getting no block and no error means we are requesting into the future (might happen because of a reorg) + if i < firstMissing { + firstMissing = i + } + } + } + if firstMissing == 0 { + return 0, nil, nil, nil, nil + } + if processBlocks { + reward = reward[:firstMissing] + } else { + reward = nil + } + baseFee, gasUsedRatio = baseFee[:firstMissing+1], gasUsedRatio[:firstMissing] + return +} diff --git a/eth/gasprice/feehistory_test.go b/eth/gasprice/feehistory_test.go new file mode 100644 index 000000000000..1621ffbc1abf --- /dev/null +++ b/eth/gasprice/feehistory_test.go @@ -0,0 +1,88 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package gasprice + +import ( + "context" + "math/big" + "testing" + + "github.com/XinFinOrg/XDPoSChain/rpc" +) + +func TestFeeHistory(t *testing.T) { + var cases = []struct { + pending bool + maxHeader, maxBlock int + count int + last rpc.BlockNumber + percent []float64 + expFirst rpc.BlockNumber + expCount int + expErr error + }{ + {false, 0, 0, 10, 30, nil, 21, 10, nil}, + {false, 0, 0, 10, 30, []float64{0, 10}, 21, 10, nil}, + {false, 0, 0, 10, 30, []float64{20, 10}, 0, 0, errInvalidPercentiles}, + {false, 0, 0, 1000000000, 30, nil, 0, 31, nil}, + {false, 0, 0, 1000000000, rpc.LatestBlockNumber, nil, 0, 33, nil}, + {false, 0, 0, 10, 40, nil, 0, 0, errRequestBeyondHead}, + {true, 0, 0, 10, 40, nil, 0, 0, errRequestBeyondHead}, + {false, 20, 2, 100, rpc.LatestBlockNumber, nil, 13, 20, nil}, + {false, 20, 2, 100, rpc.LatestBlockNumber, []float64{0, 10}, 31, 2, nil}, + {false, 20, 2, 100, 32, []float64{0, 10}, 31, 2, nil}, + {false, 0, 0, 1, rpc.PendingBlockNumber, nil, 0, 0, nil}, + {false, 0, 0, 2, rpc.PendingBlockNumber, nil, 32, 1, nil}, + {true, 0, 0, 2, rpc.PendingBlockNumber, nil, 32, 2, nil}, + {true, 0, 0, 2, rpc.PendingBlockNumber, []float64{0, 10}, 32, 2, nil}, + } + for i, c := range cases { + config := Config{ + MaxHeaderHistory: c.maxHeader, + MaxBlockHistory: c.maxBlock, + } + backend := newTestBackend(t, big.NewInt(16), c.pending) + oracle := NewOracle(backend, config) + + first, reward, baseFee, ratio, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent) + + expReward := c.expCount + if len(c.percent) == 0 { + expReward = 0 + } + expBaseFee := c.expCount + if expBaseFee != 0 { + expBaseFee++ + } + + if first != c.expFirst { + t.Fatalf("Test case %d: first block mismatch, want %d, got %d", i, c.expFirst, first) + } + if len(reward) != expReward { + t.Fatalf("Test case %d: reward array length mismatch, want %d, got %d", i, expReward, len(reward)) + } + if len(baseFee) != expBaseFee { + t.Fatalf("Test case %d: baseFee array length mismatch, want %d, got %d", i, expBaseFee, len(baseFee)) + } + if len(ratio) != c.expCount { + t.Fatalf("Test case %d: gasUsedRatio array length mismatch, want %d, got %d", i, c.expCount, len(ratio)) + } + if err != c.expErr { + t.Fatalf("Test case %d: error mismatch, want %v, got %v", i, c.expErr, err) + } + } +} diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index 4b58d2109125..8d6e7f0d2067 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -37,17 +37,21 @@ var ( ) type Config struct { - Blocks int - Percentile int - Default *big.Int `toml:",omitempty"` - MaxPrice *big.Int `toml:",omitempty"` - IgnorePrice *big.Int `toml:",omitempty"` + Blocks int + Percentile int + MaxHeaderHistory int + MaxBlockHistory int + Default *big.Int `toml:",omitempty"` + MaxPrice *big.Int `toml:",omitempty"` + IgnorePrice *big.Int `toml:",omitempty"` } // OracleBackend includes all necessary background APIs for oracle. type OracleBackend interface { HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) + GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) + PendingBlockAndReceipts() (*types.Block, types.Receipts) ChainConfig() *params.ChainConfig } @@ -62,8 +66,8 @@ type Oracle struct { cacheLock sync.RWMutex fetchLock sync.Mutex - checkBlocks int - percentile int + checkBlocks, percentile int + maxHeaderHistory, maxBlockHistory int } // NewOracle returns a new gasprice oracle which can recommend suitable @@ -96,12 +100,14 @@ func NewOracle(backend OracleBackend, params Config) *Oracle { log.Info("Gasprice oracle is ignoring threshold set", "threshold", ignorePrice) } return &Oracle{ - backend: backend, - lastPrice: params.Default, - maxPrice: maxPrice, - ignorePrice: ignorePrice, - checkBlocks: blocks, - percentile: percent, + backend: backend, + lastPrice: params.Default, + maxPrice: maxPrice, + ignorePrice: ignorePrice, + checkBlocks: blocks, + percentile: percent, + maxHeaderHistory: params.MaxHeaderHistory, + maxBlockHistory: params.MaxBlockHistory, } } @@ -111,36 +117,36 @@ func NewOracle(backend OracleBackend, params Config) *Oracle { // Note, for legacy transactions and the legacy eth_gasPrice RPC call, it will be // necessary to add the basefee to the returned number to fall back to the legacy // behavior. -func (gpo *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) { - head, _ := gpo.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) +func (oracle *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) { + head, _ := oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) headHash := head.Hash() // If the latest gasprice is still available, return it. - gpo.cacheLock.RLock() - lastHead, lastPrice := gpo.lastHead, gpo.lastPrice - gpo.cacheLock.RUnlock() + oracle.cacheLock.RLock() + lastHead, lastPrice := oracle.lastHead, oracle.lastPrice + oracle.cacheLock.RUnlock() if headHash == lastHead { return new(big.Int).Set(lastPrice), nil } - gpo.fetchLock.Lock() - defer gpo.fetchLock.Unlock() + oracle.fetchLock.Lock() + defer oracle.fetchLock.Unlock() // Try checking the cache again, maybe the last fetch fetched what we need - gpo.cacheLock.RLock() - lastHead, lastPrice = gpo.lastHead, gpo.lastPrice - gpo.cacheLock.RUnlock() + oracle.cacheLock.RLock() + lastHead, lastPrice = oracle.lastHead, oracle.lastPrice + oracle.cacheLock.RUnlock() if headHash == lastHead { return new(big.Int).Set(lastPrice), nil } var ( sent, exp int number = head.Number.Uint64() - result = make(chan results, gpo.checkBlocks) + result = make(chan results, oracle.checkBlocks) quit = make(chan struct{}) results []*big.Int ) - for sent < gpo.checkBlocks && number > 0 { - go gpo.getBlockValues(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, gpo.ignorePrice, result, quit) + for sent < oracle.checkBlocks && number > 0 { + go oracle.getBlockValues(ctx, types.MakeSigner(oracle.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, oracle.ignorePrice, result, quit) sent++ exp++ number-- @@ -162,8 +168,8 @@ func (gpo *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) { // Besides, in order to collect enough data for sampling, if nothing // meaningful returned, try to query more blocks. But the maximum // is 2*checkBlocks. - if len(res.values) == 1 && len(results)+1+exp < gpo.checkBlocks*2 && number > 0 { - go gpo.getBlockValues(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, gpo.ignorePrice, result, quit) + if len(res.values) == 1 && len(results)+1+exp < oracle.checkBlocks*2 && number > 0 { + go oracle.getBlockValues(ctx, types.MakeSigner(oracle.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, oracle.ignorePrice, result, quit) sent++ exp++ number-- @@ -173,10 +179,10 @@ func (gpo *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) { price := lastPrice if len(results) > 0 { sort.Sort(bigIntArray(results)) - price = results[(len(results)-1)*gpo.percentile/100] + price = results[(len(results)-1)*oracle.percentile/100] } - if price.Cmp(gpo.maxPrice) > 0 { - price = new(big.Int).Set(gpo.maxPrice) + if price.Cmp(oracle.maxPrice) > 0 { + price = new(big.Int).Set(oracle.maxPrice) } // Check min gas price for non-eip1559 block @@ -187,10 +193,10 @@ func (gpo *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) { } } - gpo.cacheLock.Lock() - gpo.lastHead = headHash - gpo.lastPrice = price - gpo.cacheLock.Unlock() + oracle.cacheLock.Lock() + oracle.lastHead = headHash + oracle.lastPrice = price + oracle.cacheLock.Unlock() return new(big.Int).Set(price), nil } @@ -228,8 +234,8 @@ func (s *txSorter) Less(i, j int) bool { // and sends it to the result channel. If the block is empty or all transactions // are sent by the miner itself(it doesn't make any sense to include this kind of // transaction prices for sampling), nil gasprice is returned. -func (gpo *Oracle) getBlockValues(ctx context.Context, signer types.Signer, blockNum uint64, limit int, ignoreUnder *big.Int, result chan results, quit chan struct{}) { - block, err := gpo.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum)) +func (oracle *Oracle) getBlockValues(ctx context.Context, signer types.Signer, blockNum uint64, limit int, ignoreUnder *big.Int, result chan results, quit chan struct{}) { + block, err := oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum)) if block == nil { select { case result <- results{nil, err}: diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index b3a12374838d..a56220290141 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -33,29 +33,64 @@ import ( "github.com/XinFinOrg/XDPoSChain/rpc" ) +const testHead = 32 + type testBackend struct { - chain *core.BlockChain + chain *core.BlockChain + pending bool // pending block available } func (b *testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { + if number > testHead { + return nil, nil + } if number == rpc.LatestBlockNumber { - return b.chain.CurrentBlock().Header(), nil + number = testHead + } + if number == rpc.PendingBlockNumber { + if b.pending { + number = testHead + 1 + } else { + return nil, nil + } } return b.chain.GetHeaderByNumber(uint64(number)), nil } func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { + if number > testHead { + return nil, nil + } if number == rpc.LatestBlockNumber { - return b.chain.CurrentBlock(), nil + number = testHead + } + if number == rpc.PendingBlockNumber { + if b.pending { + number = testHead + 1 + } else { + return nil, nil + } } return b.chain.GetBlockByNumber(uint64(number)), nil } +func (b *testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { + return b.chain.GetReceiptsByHash(hash), nil +} + +func (b *testBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) { + if b.pending { + block := b.chain.GetBlockByNumber(testHead + 1) + return block, b.chain.GetReceiptsByHash(block.Hash()) + } + return nil, nil +} + func (b *testBackend) ChainConfig() *params.ChainConfig { return b.chain.Config() } -func newTestBackend(t *testing.T, eip1559Block *big.Int) *testBackend { +func newTestBackend(t *testing.T, eip1559Block *big.Int, pending bool) *testBackend { var ( key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") addr = crypto.PubkeyToAddress(key.PublicKey) @@ -76,7 +111,7 @@ func newTestBackend(t *testing.T, eip1559Block *big.Int) *testBackend { genesis, _ := gspec.Commit(db) // Generate testing blocks - blocks, _ := core.GenerateChain(gspec.Config, genesis, engine, db, 32, func(i int, b *core.BlockGen) { + blocks, _ := core.GenerateChain(gspec.Config, genesis, engine, db, testHead+1, func(i int, b *core.BlockGen) { b.SetCoinbase(common.Address{1}) var tx *types.Transaction @@ -116,7 +151,7 @@ func newTestBackend(t *testing.T, eip1559Block *big.Int) *testBackend { t.Fatalf("Failed to create local chain, %v", err) } chain.InsertChain(blocks) - return &testBackend{chain: chain} + return &testBackend{chain: chain, pending: pending} } func (b *testBackend) CurrentHeader() *types.Header { @@ -144,7 +179,7 @@ func TestSuggestTipCap(t *testing.T) { {big.NewInt(33), big.NewInt(params.GWei * int64(30))}, // Fork point in the future } for _, c := range cases { - backend := newTestBackend(t, c.fork) + backend := newTestBackend(t, c.fork, false) oracle := NewOracle(backend, config) // The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 32c7d1d52c0c..6b38db894d2c 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -102,6 +102,40 @@ func (s *PublicEthereumAPI) MaxPriorityFeePerGas(ctx context.Context) (*hexutil. return (*hexutil.Big)(tipcap), err } +type feeHistoryResults struct { + FirstBlock rpc.BlockNumber + Reward [][]*hexutil.Big + BaseFee []*hexutil.Big + GasUsedRatio []float64 +} + +func (s *PublicEthereumAPI) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (feeHistoryResults, error) { + firstBlock, reward, baseFee, gasUsedRatio, err := s.b.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) + if err != nil { + return feeHistoryResults{}, err + } + results := feeHistoryResults{ + FirstBlock: firstBlock, + GasUsedRatio: gasUsedRatio, + } + if reward != nil { + results.Reward = make([][]*hexutil.Big, len(reward)) + for j, w := range reward { + results.Reward[j] = make([]*hexutil.Big, len(w)) + for i, v := range w { + results.Reward[j][i] = (*hexutil.Big)(v) + } + } + } + if baseFee != nil { + results.BaseFee = make([]*hexutil.Big, len(baseFee)) + for i, v := range baseFee { + results.BaseFee[i] = (*hexutil.Big)(v) + } + } + return results, nil +} + // ProtocolVersion returns the current Ethereum protocol version this node supports func (s *PublicEthereumAPI) ProtocolVersion() hexutil.Uint { return hexutil.Uint(s.b.ProtocolVersion()) diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 39d732542992..a773c0033536 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -49,6 +49,7 @@ type Backend interface { Downloader() *downloader.Downloader ProtocolVersion() int SuggestGasTipCap(ctx context.Context) (*big.Int, error) + FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (rpc.BlockNumber, [][]*big.Int, []*big.Int, []float64, error) ChainDb() ethdb.Database AccountManager() *accounts.Manager RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index d0350ff8a7ad..ab4c8d70e259 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -538,6 +538,12 @@ web3._extend({ params: 2, inputFormatter: [null, web3._extend.formatters.inputBlockNumberFormatter], }), + new web3._extend.Method({ + name: 'feeHistory', + call: 'eth_feeHistory', + params: 3, + inputFormatter: [null, web3._extend.formatters.inputBlockNumberFormatter, null] + }), new web3._extend.Method({ name: 'getBlockReceipts', call: 'eth_getBlockReceipts', diff --git a/les/api_backend.go b/les/api_backend.go index a7a3e8e38004..3a4dd191f7b4 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -66,12 +66,15 @@ func (b *LesApiBackend) SetHead(number uint64) { b.eth.blockchain.SetHead(number) } -func (b *LesApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { - if blockNr == rpc.LatestBlockNumber || blockNr == rpc.PendingBlockNumber { +func (b *LesApiBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { + if number == rpc.PendingBlockNumber { + return nil, nil + } + if number == rpc.LatestBlockNumber { return b.eth.blockchain.CurrentHeader(), nil } - return b.eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(blockNr)) + return b.eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(number)) } func (b *LesApiBackend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) { @@ -273,6 +276,10 @@ func (b *LesApiBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) return b.gpo.SuggestTipCap(ctx) } +func (b *LesApiBackend) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock rpc.BlockNumber, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) { + return b.gpo.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) +} + func (b *LesApiBackend) ChainDb() ethdb.Database { return b.eth.chainDb } From 3212ec319b8ac361d0d7ab93aad1578462201bea Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 28 May 2024 11:22:34 +0800 Subject: [PATCH 101/479] accounts/abi/bind: fix gas price suggestion with pre EIP-1559 clients (#23102) --- accounts/abi/bind/base.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index b7e8ec4fb1a2..63e56fdfbab3 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -238,13 +238,10 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") } if opts.GasPrice == nil { - price, err := c.transactor.SuggestGasTipCap(opts.Context) + price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context)) if err != nil { return nil, err } - if head.BaseFee != nil { - price.Add(price, head.BaseFee) - } opts.GasPrice = price } } From e94a0a91d82ec488edc9486751ba54dbc495a923 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 28 May 2024 15:23:34 +0800 Subject: [PATCH 102/479] ethclient/gethclient: RPC client wrapper for geth-specific API (#22977) --- ethclient/ethclient.go | 24 ++- ethclient/gethclient/gethclient.go | 235 +++++++++++++++++++++++++++++ 2 files changed, 246 insertions(+), 13 deletions(-) create mode 100644 ethclient/gethclient/gethclient.go diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 6fd6e3aebb3e..0a83d6352980 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -274,17 +274,6 @@ func (ec *Client) GetTransactionReceiptResult(ctx context.Context, txHash common return r, result, err } -func toBlockNumArg(number *big.Int) string { - if number == nil { - return "latest" - } - pending := big.NewInt(-1) - if number.Cmp(pending) == 0 { - return "pending" - } - return hexutil.EncodeBig(number) -} - type rpcProgress struct { StartingBlock hexutil.Uint64 CurrentBlock hexutil.Uint64 @@ -436,8 +425,6 @@ func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) { return uint(num), err } -// TODO: SubscribePendingTransactions (needs server side) - // Contract Calling // CallContract executes a message call transaction, which is directly executed in the VM @@ -511,6 +498,17 @@ func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) er return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", common.ToHex(data)) } +func toBlockNumArg(number *big.Int) string { + if number == nil { + return "latest" + } + pending := big.NewInt(-1) + if number.Cmp(pending) == 0 { + return "pending" + } + return hexutil.EncodeBig(number) +} + // SendOrderTransaction injects a signed transaction into the pending pool for execution. // // If the transaction was a contract creation use the TransactionReceipt method to get the diff --git a/ethclient/gethclient/gethclient.go b/ethclient/gethclient/gethclient.go new file mode 100644 index 000000000000..93e82633849b --- /dev/null +++ b/ethclient/gethclient/gethclient.go @@ -0,0 +1,235 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package gethclient provides an RPC client for geth-specific APIs. +package gethclient + +import ( + "context" + "math/big" + "runtime" + "runtime/debug" + + ethereum "github.com/XinFinOrg/XDPoSChain" + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/hexutil" + "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/p2p" + "github.com/XinFinOrg/XDPoSChain/rpc" +) + +// Client is a wrapper around rpc.Client that implements geth-specific functionality. +// +// If you want to use the standardized Ethereum RPC functionality, use ethclient.Client instead. +type Client struct { + c *rpc.Client +} + +// New creates a client that uses the given RPC client. +func New(c *rpc.Client) *Client { + return &Client{c} +} + +// CreateAccessList tries to create an access list for a specific transaction based on the +// current pending state of the blockchain. +func (ec *Client) CreateAccessList(ctx context.Context, msg ethereum.CallMsg) (*types.AccessList, uint64, string, error) { + type accessListResult struct { + Accesslist *types.AccessList `json:"accessList"` + Error string `json:"error,omitempty"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + } + var result accessListResult + if err := ec.c.CallContext(ctx, &result, "eth_createAccessList", toCallArg(msg)); err != nil { + return nil, 0, "", err + } + return result.Accesslist, uint64(result.GasUsed), result.Error, nil +} + +// AccountResult is the result of a GetProof operation. +type AccountResult struct { + Address common.Address `json:"address"` + AccountProof []string `json:"accountProof"` + Balance *big.Int `json:"balance"` + CodeHash common.Hash `json:"codeHash"` + Nonce uint64 `json:"nonce"` + StorageHash common.Hash `json:"storageHash"` + StorageProof []StorageResult `json:"storageProof"` +} + +// StorageResult provides a proof for a key-value pair. +type StorageResult struct { + Key string `json:"key"` + Value *big.Int `json:"value"` + Proof []string `json:"proof"` +} + +// GetProof returns the account and storage values of the specified account including the Merkle-proof. +// The block number can be nil, in which case the value is taken from the latest known block. +func (ec *Client) GetProof(ctx context.Context, account common.Address, keys []string, blockNumber *big.Int) (*AccountResult, error) { + + type storageResult struct { + Key string `json:"key"` + Value *hexutil.Big `json:"value"` + Proof []string `json:"proof"` + } + + type accountResult struct { + Address common.Address `json:"address"` + AccountProof []string `json:"accountProof"` + Balance *hexutil.Big `json:"balance"` + CodeHash common.Hash `json:"codeHash"` + Nonce hexutil.Uint64 `json:"nonce"` + StorageHash common.Hash `json:"storageHash"` + StorageProof []storageResult `json:"storageProof"` + } + + var res accountResult + err := ec.c.CallContext(ctx, &res, "eth_getProof", account, keys, toBlockNumArg(blockNumber)) + // Turn hexutils back to normal datatypes + storageResults := make([]StorageResult, 0, len(res.StorageProof)) + for _, st := range res.StorageProof { + storageResults = append(storageResults, StorageResult{ + Key: st.Key, + Value: st.Value.ToInt(), + Proof: st.Proof, + }) + } + result := AccountResult{ + Address: res.Address, + AccountProof: res.AccountProof, + Balance: res.Balance.ToInt(), + Nonce: uint64(res.Nonce), + CodeHash: res.CodeHash, + StorageHash: res.StorageHash, + } + return &result, err +} + +// OverrideAccount specifies the state of an account to be overridden. +type OverrideAccount struct { + Nonce uint64 `json:"nonce"` + Code []byte `json:"code"` + Balance *big.Int `json:"balance"` + State map[common.Hash]common.Hash `json:"state"` + StateDiff map[common.Hash]common.Hash `json:"stateDiff"` +} + +// CallContract executes a message call transaction, which is directly executed in the VM +// of the node, but never mined into the blockchain. +// +// blockNumber selects the block height at which the call runs. It can be nil, in which +// case the code is taken from the latest known block. Note that state from very old +// blocks might not be available. +// +// overrides specifies a map of contract states that should be overwritten before executing +// the message call. +// Please use ethclient.CallContract instead if you don't need the override functionality. +func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int, overrides *map[common.Address]OverrideAccount) ([]byte, error) { + var hex hexutil.Bytes + err := ec.c.CallContext( + ctx, &hex, "eth_call", toCallArg(msg), + toBlockNumArg(blockNumber), toOverrideMap(overrides), + ) + return hex, err +} + +// GCStats retrieves the current garbage collection stats from a geth node. +func (ec *Client) GCStats(ctx context.Context) (*debug.GCStats, error) { + var result debug.GCStats + err := ec.c.CallContext(ctx, &result, "debug_gcStats") + return &result, err +} + +// MemStats retrieves the current memory stats from a geth node. +func (ec *Client) MemStats(ctx context.Context) (*runtime.MemStats, error) { + var result runtime.MemStats + err := ec.c.CallContext(ctx, &result, "debug_memStats") + return &result, err +} + +// SetHead sets the current head of the local chain by block number. +// Note, this is a destructive action and may severely damage your chain. +// Use with extreme caution. +func (ec *Client) SetHead(ctx context.Context, number *big.Int) error { + return ec.c.CallContext(ctx, nil, "debug_setHead", toBlockNumArg(number)) +} + +// GetNodeInfo retrieves the node info of a geth node. +func (ec *Client) GetNodeInfo(ctx context.Context) (*p2p.NodeInfo, error) { + var result p2p.NodeInfo + err := ec.c.CallContext(ctx, &result, "admin_nodeInfo") + return &result, err +} + +// SubscribePendingTransactions subscribes to new pending transactions. +func (ec *Client) SubscribePendingTransactions(ctx context.Context, ch chan<- common.Hash) (*rpc.ClientSubscription, error) { + return ec.c.EthSubscribe(ctx, ch, "newPendingTransactions") +} + +func toBlockNumArg(number *big.Int) string { + if number == nil { + return "latest" + } + pending := big.NewInt(-1) + if number.Cmp(pending) == 0 { + return "pending" + } + return hexutil.EncodeBig(number) +} + +func toCallArg(msg ethereum.CallMsg) interface{} { + arg := map[string]interface{}{ + "from": msg.From, + "to": msg.To, + } + if len(msg.Data) > 0 { + arg["data"] = hexutil.Bytes(msg.Data) + } + if msg.Value != nil { + arg["value"] = (*hexutil.Big)(msg.Value) + } + if msg.Gas != 0 { + arg["gas"] = hexutil.Uint64(msg.Gas) + } + if msg.GasPrice != nil { + arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) + } + return arg +} + +func toOverrideMap(overrides *map[common.Address]OverrideAccount) interface{} { + if overrides == nil { + return nil + } + type overrideAccount struct { + Nonce hexutil.Uint64 `json:"nonce"` + Code hexutil.Bytes `json:"code"` + Balance *hexutil.Big `json:"balance"` + State map[common.Hash]common.Hash `json:"state"` + StateDiff map[common.Hash]common.Hash `json:"stateDiff"` + } + result := make(map[common.Address]overrideAccount) + for addr, override := range *overrides { + result[addr] = overrideAccount{ + Nonce: hexutil.Uint64(override.Nonce), + Code: override.Code, + Balance: (*hexutil.Big)(override.Balance), + State: override.State, + StateDiff: override.StateDiff, + } + } + return &result +} From 5d4ad883648caeafaab184067326fc82d166b6a5 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 28 May 2024 15:47:15 +0800 Subject: [PATCH 103/479] internal/ethapi: fix panic in access list creation (#23133) --- internal/ethapi/api.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 6b38db894d2c..c110a2fd33aa 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2044,11 +2044,11 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH if value, ok := feeCapacity[to]; ok { balanceTokenFee = value } - msg := types.NewMessage(args.from(), args.To, uint64(*args.Nonce), args.Value.ToInt(), uint64(*args.Gas), args.GasPrice.ToInt(), nil, nil, args.data(), accessList, false, balanceTokenFee, header.Number) + msg := types.NewMessage(args.from(), args.To, uint64(*args.Nonce), args.Value.ToInt(), uint64(*args.Gas), args.GasPrice.ToInt(), big.NewInt(0), big.NewInt(0), args.data(), accessList, false, balanceTokenFee, header.Number) // Apply the transaction with the access list tracer tracer := vm.NewAccessListTracer(accessList, args.from(), to, precompiles) - config := vm.Config{Tracer: tracer, Debug: true} + config := vm.Config{Tracer: tracer, Debug: true, NoBaseFee: true} vmenv, _, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &config) if err != nil { return nil, 0, nil, err From 012c4cb0fbab16916fbd2ce33e0349cb0a4e6e79 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 28 May 2024 16:17:19 +0800 Subject: [PATCH 104/479] ethstats: fix full node interface post 1559 (#23159) --- ethstats/ethstats.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go index 0fe575546ab5..d4872e6362e5 100644 --- a/ethstats/ethstats.go +++ b/ethstats/ethstats.go @@ -816,11 +816,11 @@ func (s *Service) reportStats(conn *connWrapper) error { sync := s.eth.Downloader().Progress() syncing = s.eth.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock - tipcap, _ := s.eth.ApiBackend.SuggestGasTipCap(context.Background()) - if head := s.eth.ApiBackend.CurrentHeader(); head.BaseFee != nil { - tipcap.Add(tipcap, head.BaseFee) + price, _ := s.eth.ApiBackend.SuggestGasTipCap(context.Background()) + gasprice = int(price.Uint64()) + if basefee := s.eth.ApiBackend.CurrentHeader().BaseFee; basefee != nil { + gasprice += int(basefee.Uint64()) } - gasprice = int(tipcap.Uint64()) } else { sync := s.les.Downloader().Progress() syncing = s.les.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock From 5766ca45203e78e55158dc89c2bcbecfb74351ef Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 29 May 2024 13:37:00 +0800 Subject: [PATCH 105/479] eth/gasprice, internal/ethapi: minor feehistory fixes (#23178) --- eth/gasprice/feehistory.go | 169 +++++++++++++++++--------------- eth/gasprice/feehistory_test.go | 5 +- internal/ethapi/api.go | 30 +++--- 3 files changed, 106 insertions(+), 98 deletions(-) diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index c3016bfb9c84..62561605f903 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -19,6 +19,7 @@ package gasprice import ( "context" "errors" + "fmt" "math/big" "sort" "sync/atomic" @@ -30,11 +31,19 @@ import ( ) var ( - errInvalidPercentiles = errors.New("Invalid reward percentiles") - errRequestBeyondHead = errors.New("Request beyond head block") + errInvalidPercentile = errors.New("invalid reward percentile") + errRequestBeyondHead = errors.New("request beyond head block") ) -const maxBlockCount = 1024 // number of blocks retrievable with a single query +const ( + // maxFeeHistory is the maximum number of blocks that can be retrieved for a + // fee history request. + maxFeeHistory = 1024 + + // maxBlockFetchers is the max number of goroutines to spin up to pull blocks + // for the fee history calculation (mostly relevant for LES). + maxBlockFetchers = 4 +) // blockFees represents a single block for processing type blockFees struct { @@ -124,23 +133,22 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { // also returned if requested and available. // Note: an error is only returned if retrieving the head header has failed. If there are no // retrievable blocks in the specified range then zero block count is returned with no error. -func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlockNumber rpc.BlockNumber, blockCount, maxHistory int) (*types.Block, types.Receipts, rpc.BlockNumber, int, error) { +func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlock rpc.BlockNumber, blocks, maxHistory int) (*types.Block, []*types.Receipt, rpc.BlockNumber, int, error) { var ( - headBlockNumber rpc.BlockNumber + headBlock rpc.BlockNumber pendingBlock *types.Block pendingReceipts types.Receipts ) - - // query either pending block or head header and set headBlockNumber - if lastBlockNumber == rpc.PendingBlockNumber { + // query either pending block or head header and set headBlock + if lastBlock == rpc.PendingBlockNumber { if pendingBlock, pendingReceipts = oracle.backend.PendingBlockAndReceipts(); pendingBlock != nil { - lastBlockNumber = rpc.BlockNumber(pendingBlock.NumberU64()) - headBlockNumber = lastBlockNumber - 1 + lastBlock = rpc.BlockNumber(pendingBlock.NumberU64()) + headBlock = lastBlock - 1 } else { // pending block not supported by backend, process until latest block - lastBlockNumber = rpc.LatestBlockNumber - blockCount-- - if blockCount == 0 { + lastBlock = rpc.LatestBlockNumber + blocks-- + if blocks == 0 { return nil, nil, 0, 0, nil } } @@ -148,32 +156,32 @@ func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlockNumber rpc if pendingBlock == nil { // if pending block is not fetched then we retrieve the head header to get the head block number if latestHeader, err := oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber); err == nil { - headBlockNumber = rpc.BlockNumber(latestHeader.Number.Uint64()) + headBlock = rpc.BlockNumber(latestHeader.Number.Uint64()) } else { return nil, nil, 0, 0, err } } - if lastBlockNumber == rpc.LatestBlockNumber { - lastBlockNumber = headBlockNumber - } else if pendingBlock == nil && lastBlockNumber > headBlockNumber { - return nil, nil, 0, 0, errRequestBeyondHead + if lastBlock == rpc.LatestBlockNumber { + lastBlock = headBlock + } else if pendingBlock == nil && lastBlock > headBlock { + return nil, nil, 0, 0, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, lastBlock, headBlock) } if maxHistory != 0 { // limit retrieval to the given number of latest blocks - if tooOldCount := int64(headBlockNumber) - int64(maxHistory) - int64(lastBlockNumber) + int64(blockCount); tooOldCount > 0 { + if tooOldCount := int64(headBlock) - int64(maxHistory) - int64(lastBlock) + int64(blocks); tooOldCount > 0 { // tooOldCount is the number of requested blocks that are too old to be served - if int64(blockCount) > tooOldCount { - blockCount -= int(tooOldCount) + if int64(blocks) > tooOldCount { + blocks -= int(tooOldCount) } else { return nil, nil, 0, 0, nil } } } // ensure not trying to retrieve before genesis - if rpc.BlockNumber(blockCount) > lastBlockNumber+1 { - blockCount = int(lastBlockNumber + 1) + if rpc.BlockNumber(blocks) > lastBlock+1 { + blocks = int(lastBlock + 1) } - return pendingBlock, pendingReceipts, lastBlockNumber, blockCount, nil + return pendingBlock, pendingReceipts, lastBlock, blocks, nil } // FeeHistory returns data relevant for fee estimation based on the specified range of blocks. @@ -190,90 +198,89 @@ func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlockNumber rpc // // Note: baseFee includes the next block after the newest of the returned range, because this // value can be derived from the newest block. -func (oracle *Oracle) FeeHistory(ctx context.Context, blockCount int, lastBlockNumber rpc.BlockNumber, rewardPercentiles []float64) (firstBlockNumber rpc.BlockNumber, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) { - if blockCount < 1 { - // returning with no data and no error means there are no retrievable blocks - return +func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (rpc.BlockNumber, [][]*big.Int, []*big.Int, []float64, error) { + if blocks < 1 { + return 0, nil, nil, nil, nil // returning with no data and no error means there are no retrievable blocks } - if blockCount > maxBlockCount { - blockCount = maxBlockCount + if blocks > maxFeeHistory { + log.Warn("Sanitizing fee history length", "requested", blocks, "truncated", maxFeeHistory) + blocks = maxFeeHistory } for i, p := range rewardPercentiles { - if p < 0 || p > 100 || (i > 0 && p < rewardPercentiles[i-1]) { - return 0, nil, nil, nil, errInvalidPercentiles + if p < 0 || p > 100 { + return 0, nil, nil, nil, fmt.Errorf("%w: %f", errInvalidPercentile, p) + } + if i > 0 && p < rewardPercentiles[i-1] { + return 0, nil, nil, nil, fmt.Errorf("%w: #%d:%f > #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p) } } - - processBlocks := len(rewardPercentiles) != 0 - // limit retrieval to maxHistory if set - var maxHistory int - if processBlocks { + // Only process blocks if reward percentiles were requested + maxHistory := oracle.maxHeaderHistory + if len(rewardPercentiles) != 0 { maxHistory = oracle.maxBlockHistory - } else { - maxHistory = oracle.maxHeaderHistory } - var ( pendingBlock *types.Block - pendingReceipts types.Receipts + pendingReceipts []*types.Receipt + err error ) - if pendingBlock, pendingReceipts, lastBlockNumber, blockCount, err = oracle.resolveBlockRange(ctx, lastBlockNumber, blockCount, maxHistory); err != nil || blockCount == 0 { - return + pendingBlock, pendingReceipts, lastBlock, blocks, err = oracle.resolveBlockRange(ctx, lastBlock, blocks, maxHistory) + if err != nil || blocks == 0 { + return 0, nil, nil, nil, err } - firstBlockNumber = lastBlockNumber + 1 - rpc.BlockNumber(blockCount) + oldestBlock := lastBlock + 1 - rpc.BlockNumber(blocks) - processNext := int64(firstBlockNumber) - resultCh := make(chan *blockFees, blockCount) - threadCount := 4 - if blockCount < threadCount { - threadCount = blockCount - } - for i := 0; i < threadCount; i++ { + var ( + next = int64(oldestBlock) + results = make(chan *blockFees, blocks) + ) + for i := 0; i < maxBlockFetchers && i < blocks; i++ { go func() { for { - blockNumber := rpc.BlockNumber(atomic.AddInt64(&processNext, 1) - 1) - if blockNumber > lastBlockNumber { + // Retrieve the next block number to fetch with this goroutine + blockNumber := rpc.BlockNumber(atomic.AddInt64(&next, 1) - 1) + if blockNumber > lastBlock { return } - bf := &blockFees{blockNumber: blockNumber} + fees := &blockFees{blockNumber: blockNumber} if pendingBlock != nil && blockNumber >= rpc.BlockNumber(pendingBlock.NumberU64()) { - bf.block, bf.receipts = pendingBlock, pendingReceipts + fees.block, fees.receipts = pendingBlock, pendingReceipts } else { - if processBlocks { - bf.block, bf.err = oracle.backend.BlockByNumber(ctx, blockNumber) - if bf.block != nil { - bf.receipts, bf.err = oracle.backend.GetReceipts(ctx, bf.block.Hash()) + if len(rewardPercentiles) != 0 { + fees.block, fees.err = oracle.backend.BlockByNumber(ctx, blockNumber) + if fees.block != nil && fees.err == nil { + fees.receipts, fees.err = oracle.backend.GetReceipts(ctx, fees.block.Hash()) } } else { - bf.header, bf.err = oracle.backend.HeaderByNumber(ctx, blockNumber) + fees.header, fees.err = oracle.backend.HeaderByNumber(ctx, blockNumber) } } - if bf.block != nil { - bf.header = bf.block.Header() + if fees.block != nil { + fees.header = fees.block.Header() } - if bf.header != nil { - oracle.processBlock(bf, rewardPercentiles) + if fees.header != nil { + oracle.processBlock(fees, rewardPercentiles) } - // send to resultCh even if empty to guarantee that blockCount items are sent in total - resultCh <- bf + // send to results even if empty to guarantee that blocks items are sent in total + results <- fees } }() } - - reward = make([][]*big.Int, blockCount) - baseFee = make([]*big.Int, blockCount+1) - gasUsedRatio = make([]float64, blockCount) - firstMissing := blockCount - - for ; blockCount > 0; blockCount-- { - bf := <-resultCh - if bf.err != nil { - return 0, nil, nil, nil, bf.err + var ( + reward = make([][]*big.Int, blocks) + baseFee = make([]*big.Int, blocks+1) + gasUsedRatio = make([]float64, blocks) + firstMissing = blocks + ) + for ; blocks > 0; blocks-- { + fees := <-results + if fees.err != nil { + return 0, nil, nil, nil, fees.err } - i := int(bf.blockNumber - firstBlockNumber) - if bf.header != nil { - reward[i], baseFee[i], baseFee[i+1], gasUsedRatio[i] = bf.reward, bf.baseFee, bf.nextBaseFee, bf.gasUsedRatio + i := int(fees.blockNumber - oldestBlock) + if fees.header != nil { + reward[i], baseFee[i], baseFee[i+1], gasUsedRatio[i] = fees.reward, fees.baseFee, fees.nextBaseFee, fees.gasUsedRatio } else { // getting no block and no error means we are requesting into the future (might happen because of a reorg) if i < firstMissing { @@ -284,11 +291,11 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blockCount int, lastBlockN if firstMissing == 0 { return 0, nil, nil, nil, nil } - if processBlocks { + if len(rewardPercentiles) != 0 { reward = reward[:firstMissing] } else { reward = nil } baseFee, gasUsedRatio = baseFee[:firstMissing+1], gasUsedRatio[:firstMissing] - return + return oldestBlock, reward, baseFee, gasUsedRatio, nil } diff --git a/eth/gasprice/feehistory_test.go b/eth/gasprice/feehistory_test.go index 1621ffbc1abf..195a12eecaf8 100644 --- a/eth/gasprice/feehistory_test.go +++ b/eth/gasprice/feehistory_test.go @@ -18,6 +18,7 @@ package gasprice import ( "context" + "errors" "math/big" "testing" @@ -37,7 +38,7 @@ func TestFeeHistory(t *testing.T) { }{ {false, 0, 0, 10, 30, nil, 21, 10, nil}, {false, 0, 0, 10, 30, []float64{0, 10}, 21, 10, nil}, - {false, 0, 0, 10, 30, []float64{20, 10}, 0, 0, errInvalidPercentiles}, + {false, 0, 0, 10, 30, []float64{20, 10}, 0, 0, errInvalidPercentile}, {false, 0, 0, 1000000000, 30, nil, 0, 31, nil}, {false, 0, 0, 1000000000, rpc.LatestBlockNumber, nil, 0, 33, nil}, {false, 0, 0, 10, 40, nil, 0, 0, errRequestBeyondHead}, @@ -81,7 +82,7 @@ func TestFeeHistory(t *testing.T) { if len(ratio) != c.expCount { t.Fatalf("Test case %d: gasUsedRatio array length mismatch, want %d, got %d", i, c.expCount, len(ratio)) } - if err != c.expErr { + if err != c.expErr && !errors.Is(err, c.expErr) { t.Fatalf("Test case %d: error mismatch, want %v, got %v", i, c.expErr, err) } } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index c110a2fd33aa..bf780611b414 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -102,28 +102,28 @@ func (s *PublicEthereumAPI) MaxPriorityFeePerGas(ctx context.Context) (*hexutil. return (*hexutil.Big)(tipcap), err } -type feeHistoryResults struct { - FirstBlock rpc.BlockNumber - Reward [][]*hexutil.Big - BaseFee []*hexutil.Big - GasUsedRatio []float64 +type feeHistoryResult struct { + OldestBlock rpc.BlockNumber `json:"oldestBlock"` + Reward [][]*hexutil.Big `json:"reward,omitempty"` + BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"` + GasUsedRatio []float64 `json:"gasUsedRatio"` } -func (s *PublicEthereumAPI) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (feeHistoryResults, error) { - firstBlock, reward, baseFee, gasUsedRatio, err := s.b.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) +func (s *PublicEthereumAPI) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*feeHistoryResult, error) { + oldest, reward, baseFee, gasUsed, err := s.b.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) if err != nil { - return feeHistoryResults{}, err + return nil, err } - results := feeHistoryResults{ - FirstBlock: firstBlock, - GasUsedRatio: gasUsedRatio, + results := &feeHistoryResult{ + OldestBlock: oldest, + GasUsedRatio: gasUsed, } if reward != nil { results.Reward = make([][]*hexutil.Big, len(reward)) - for j, w := range reward { - results.Reward[j] = make([]*hexutil.Big, len(w)) - for i, v := range w { - results.Reward[j][i] = (*hexutil.Big)(v) + for i, w := range reward { + results.Reward[i] = make([]*hexutil.Big, len(w)) + for j, v := range w { + results.Reward[i][j] = (*hexutil.Big)(v) } } } From 93dd33b60701ffe67dc9d46b3b9f485c7be23b31 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 13 Jun 2024 14:06:10 +0800 Subject: [PATCH 106/479] internal/ethapi: fix transaction APIs (#23179) --- internal/ethapi/api.go | 23 +++++++++++++---------- internal/ethapi/transaction_args.go | 5 ++++- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index bf780611b414..7812c88856e4 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -460,14 +460,15 @@ func (s *PrivateAccountAPI) SignTransaction(ctx context.Context, args Transactio if args.Gas == nil { return nil, errors.New("gas not specified") } - if args.GasPrice == nil { - return nil, errors.New("gasPrice not specified") + if args.GasPrice == nil && (args.MaxFeePerGas == nil || args.MaxPriorityFeePerGas == nil) { + return nil, errors.New("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas") } if args.Nonce == nil { return nil, errors.New("nonce not specified") } // Before actually sign the transaction, ensure the transaction fee is reasonable. - if err := checkTxFee(args.GasPrice.ToInt(), uint64(*args.Gas), s.b.RPCTxFeeCap()); err != nil { + tx := args.toTransaction() + if err := checkTxFee(tx.GasPrice(), tx.Gas(), s.b.RPCTxFeeCap()); err != nil { return nil, err } signed, err := s.signTransaction(ctx, &args, passwd) @@ -2396,8 +2397,9 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Tra return SubmitTransaction(ctx, s.b, signed) } -// FillTransaction fills the defaults (nonce, gas, gasPrice) on a given unsigned transaction, -// and returns it to the caller for further processing (signing + broadcast) +// FillTransaction fills the defaults (nonce, gas, gasPrice or 1559 fields) +// on a given unsigned transaction, and returns it to the caller for further +// processing (signing + broadcast). func (s *PublicTransactionPoolAPI) FillTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) { // Set some sanity defaults and terminate on failure if err := args.setDefaults(ctx, s.b); err != nil { @@ -3299,8 +3301,8 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Tra if args.Gas == nil { return nil, errors.New("not specify Gas") } - if args.GasPrice == nil { - return nil, errors.New("not specify GasPrice") + if args.GasPrice == nil && (args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil) { + return nil, errors.New("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas") } if args.Nonce == nil { return nil, errors.New("not specify Nonce") @@ -3309,14 +3311,15 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Tra return nil, err } // Before actually sign the transaction, ensure the transaction fee is reasonable. - if err := checkTxFee(args.GasPrice.ToInt(), uint64(*args.Gas), s.b.RPCTxFeeCap()); err != nil { + tx := args.toTransaction() + if err := checkTxFee(tx.GasPrice(), tx.Gas(), s.b.RPCTxFeeCap()); err != nil { return nil, err } - tx, err := s.sign(args.from(), args.toTransaction()) + signed, err := s.sign(args.from(), tx) if err != nil { return nil, err } - data, err := tx.MarshalBinary() + data, err := signed.MarshalBinary() if err != nil { return nil, err } diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 926122df06f1..cacc5dc2e939 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -49,7 +49,7 @@ type TransactionArgs struct { Data *hexutil.Bytes `json:"data"` Input *hexutil.Bytes `json:"input"` - // For non-legacy transactions + // Introduced by AccessListTxType transaction. AccessList *types.AccessList `json:"accessList,omitempty"` ChainID *hexutil.Big `json:"chainId,omitempty"` } @@ -108,6 +108,9 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { return err } if b.ChainConfig().IsEIP1559(head.Number) { + // The legacy tx gas price suggestion should not add 2x base fee + // because all fees are consumed, so it would result in a spiral + // upwards. price.Add(price, head.BaseFee) } args.GasPrice = (*hexutil.Big)(price) From 5b01b23af69ed0444c3331a56e3a5cdaa4c3a62f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 29 May 2024 14:18:20 +0800 Subject: [PATCH 107/479] internal: get pending and queued transaction by address (#22992) --- core/tx_pool.go | 17 +++++++++++++++++ eth/api_backend.go | 4 ++++ internal/ethapi/api.go | 23 +++++++++++++++++++++++ internal/ethapi/backend.go | 1 + internal/web3ext/web3ext.go | 5 +++++ les/api_backend.go | 4 ++++ light/txpool.go | 19 +++++++++++++++++++ 7 files changed, 73 insertions(+) diff --git a/core/tx_pool.go b/core/tx_pool.go index 9c7f3f7dbc09..7162dd778c4e 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -535,6 +535,23 @@ func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common return pending, queued } +// ContentFrom retrieves the data content of the transaction pool, returning the +// pending as well as queued transactions of this address, grouped by nonce. +func (pool *TxPool) ContentFrom(addr common.Address) (types.Transactions, types.Transactions) { + pool.mu.RLock() + defer pool.mu.RUnlock() + + var pending types.Transactions + if list, ok := pool.pending[addr]; ok { + pending = list.Flatten() + } + var queued types.Transactions + if list, ok := pool.queue[addr]; ok { + queued = list.Flatten() + } + return pending, queued +} + // Pending retrieves all currently processable transactions, grouped by origin // account and sorted by nonce. The returned transaction set is a copy and can be // freely modified by calling code. diff --git a/eth/api_backend.go b/eth/api_backend.go index b0beead8c548..7de897a1befc 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -325,6 +325,10 @@ func (b *EthApiBackend) TxPoolContent() (map[common.Address]types.Transactions, return b.eth.TxPool().Content() } +func (b *EthApiBackend) TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions) { + return b.eth.TxPool().ContentFrom(addr) +} + func (b *EthApiBackend) OrderTxPoolContent() (map[common.Address]types.OrderTransactions, map[common.Address]types.OrderTransactions) { return b.eth.OrderPool().Content() } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 7812c88856e4..0209d6ce644f 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -202,6 +202,29 @@ func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string]*RPCTransac return content } +// ContentFrom returns the transactions contained within the transaction pool. +func (s *PublicTxPoolAPI) ContentFrom(addr common.Address) map[string]map[string]*RPCTransaction { + content := make(map[string]map[string]*RPCTransaction, 2) + pending, queue := s.b.TxPoolContentFrom(addr) + curHeader := s.b.CurrentHeader() + + // Build the pending transactions + dump := make(map[string]*RPCTransaction, len(pending)) + for _, tx := range pending { + dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()) + } + content["pending"] = dump + + // Build the queued transactions + dump = make(map[string]*RPCTransaction, len(queue)) + for _, tx := range queue { + dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()) + } + content["queued"] = dump + + return content +} + // Status returns the number of pending and queued transaction in the pool. func (s *PublicTxPoolAPI) Status() map[string]hexutil.Uint { pending, queue := s.b.Stats() diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index a773c0033536..6b66806ebf28 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -84,6 +84,7 @@ type Backend interface { GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) Stats() (pending int, queued int) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) + TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions) SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription // Order Pool Transaction diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index ab4c8d70e259..5c8f88aa7cdf 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -1071,6 +1071,11 @@ web3._extend({ return status; } }), + new web3._extend.Method({ + name: 'contentFrom', + call: 'txpool_contentFrom', + params: 1, + }), ] }); ` diff --git a/les/api_backend.go b/les/api_backend.go index 3a4dd191f7b4..59c2c474331b 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -226,6 +226,10 @@ func (b *LesApiBackend) TxPoolContent() (map[common.Address]types.Transactions, return b.eth.txPool.Content() } +func (b *LesApiBackend) TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions) { + return b.eth.txPool.ContentFrom(addr) +} + func (b *LesApiBackend) OrderTxPoolContent() (map[common.Address]types.OrderTransactions, map[common.Address]types.OrderTransactions) { return make(map[common.Address]types.OrderTransactions), make(map[common.Address]types.OrderTransactions) } diff --git a/light/txpool.go b/light/txpool.go index 92e42101a733..d9566e2a7076 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -530,6 +530,25 @@ func (p *TxPool) Content() (map[common.Address]types.Transactions, map[common.Ad return pending, queued } +// ContentFrom retrieves the data content of the transaction pool, returning the +// pending as well as queued transactions of this address, grouped by nonce. +func (pool *TxPool) ContentFrom(addr common.Address) (types.Transactions, types.Transactions) { + pool.mu.RLock() + defer pool.mu.RUnlock() + + // Retrieve the pending transactions and sort by nonce + var pending types.Transactions + for _, tx := range pool.pending { + account, _ := types.Sender(pool.signer, tx) + if account != addr { + continue + } + pending = append(pending, tx) + } + // There are no queued transactions in a light pool, just return an empty map + return pending, types.Transactions{} +} + // RemoveTransactions removes all given transactions from the pool. func (p *TxPool) RemoveTransactions(txs types.Transactions) { p.mu.Lock() From 43a0cdf7ced6cfebee5d8494b197567ba2072813 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 28 May 2024 16:58:19 +0800 Subject: [PATCH 108/479] core: fix pre-check for account balance under EIP-1559 (#23244) --- core/state_processor_test.go | 2 +- core/state_transition.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/state_processor_test.go b/core/state_processor_test.go index c07d3c50656d..cce40167528d 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -120,7 +120,7 @@ func TestStateProcessorErrors(t *testing.T) { txs: []*types.Transaction{ makeTx(0, common.Address{}, big.NewInt(1000000000000000000), params.TxGas, big.NewInt(875000000), nil), }, - want: "insufficient funds for transfer: address xdc71562b71999873DB5b286dF957af199Ec94617F7", + want: "insufficient funds for gas * price + value: address xdc71562b71999873DB5b286dF957af199Ec94617F7", }, { // ErrInsufficientFunds txs: []*types.Transaction{ diff --git a/core/state_transition.go b/core/state_transition.go index cf4e5f26606c..fa9c501c1296 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -186,6 +186,7 @@ func (st *StateTransition) buyGas() error { if st.gasFeeCap != nil { balanceCheck = new(big.Int).SetUint64(st.msg.Gas()) balanceCheck = balanceCheck.Mul(balanceCheck, st.gasFeeCap) + balanceCheck.Add(balanceCheck, st.value) } if have, want := st.state.GetBalance(st.msg.From()), balanceCheck; have.Cmp(want) < 0 { return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), have, want) From 7500b0ac9545719e7a59a13b4d759771cee108cb Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 29 May 2024 14:39:53 +0800 Subject: [PATCH 109/479] Use hexutil.Uint for blockCount parameter and oldestBlock result value in feeHistory method (#23239) --- eth/api_backend.go | 2 +- eth/gasprice/feehistory.go | 37 +++++++++++++++++---------------- eth/gasprice/feehistory_test.go | 4 ++-- internal/ethapi/api.go | 8 +++---- internal/ethapi/backend.go | 2 +- les/api_backend.go | 2 +- 6 files changed, 28 insertions(+), 27 deletions(-) diff --git a/eth/api_backend.go b/eth/api_backend.go index 7de897a1befc..087cd3cf1abe 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -352,7 +352,7 @@ func (b *EthApiBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) return b.gpo.SuggestTipCap(ctx) } -func (b *EthApiBackend) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock rpc.BlockNumber, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) { +func (b *EthApiBackend) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock *big.Int, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) { return b.gpo.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) } diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index 62561605f903..7f2016823c96 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -24,6 +24,7 @@ import ( "sort" "sync/atomic" + "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus/misc" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/log" @@ -48,7 +49,7 @@ const ( // blockFees represents a single block for processing type blockFees struct { // set by the caller - blockNumber rpc.BlockNumber + blockNumber uint64 header *types.Header block *types.Block // only set if reward percentiles are requested receipts types.Receipts @@ -133,7 +134,7 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { // also returned if requested and available. // Note: an error is only returned if retrieving the head header has failed. If there are no // retrievable blocks in the specified range then zero block count is returned with no error. -func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlock rpc.BlockNumber, blocks, maxHistory int) (*types.Block, []*types.Receipt, rpc.BlockNumber, int, error) { +func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlock rpc.BlockNumber, blocks, maxHistory int) (*types.Block, []*types.Receipt, uint64, int, error) { var ( headBlock rpc.BlockNumber pendingBlock *types.Block @@ -181,7 +182,7 @@ func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlock rpc.Block if rpc.BlockNumber(blocks) > lastBlock+1 { blocks = int(lastBlock + 1) } - return pendingBlock, pendingReceipts, lastBlock, blocks, nil + return pendingBlock, pendingReceipts, uint64(lastBlock), blocks, nil } // FeeHistory returns data relevant for fee estimation based on the specified range of blocks. @@ -198,9 +199,9 @@ func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlock rpc.Block // // Note: baseFee includes the next block after the newest of the returned range, because this // value can be derived from the newest block. -func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (rpc.BlockNumber, [][]*big.Int, []*big.Int, []float64, error) { +func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) { if blocks < 1 { - return 0, nil, nil, nil, nil // returning with no data and no error means there are no retrievable blocks + return common.Big0, nil, nil, nil, nil // returning with no data and no error means there are no retrievable blocks } if blocks > maxFeeHistory { log.Warn("Sanitizing fee history length", "requested", blocks, "truncated", maxFeeHistory) @@ -208,10 +209,10 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, lastBlock rpc. } for i, p := range rewardPercentiles { if p < 0 || p > 100 { - return 0, nil, nil, nil, fmt.Errorf("%w: %f", errInvalidPercentile, p) + return common.Big0, nil, nil, nil, fmt.Errorf("%w: %f", errInvalidPercentile, p) } if i > 0 && p < rewardPercentiles[i-1] { - return 0, nil, nil, nil, fmt.Errorf("%w: #%d:%f > #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p) + return common.Big0, nil, nil, nil, fmt.Errorf("%w: #%d:%f > #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p) } } // Only process blocks if reward percentiles were requested @@ -224,36 +225,36 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, lastBlock rpc. pendingReceipts []*types.Receipt err error ) - pendingBlock, pendingReceipts, lastBlock, blocks, err = oracle.resolveBlockRange(ctx, lastBlock, blocks, maxHistory) + pendingBlock, pendingReceipts, lastBlock, blocks, err := oracle.resolveBlockRange(ctx, unresolvedLastBlock, blocks, maxHistory) if err != nil || blocks == 0 { - return 0, nil, nil, nil, err + return common.Big0, nil, nil, nil, err } - oldestBlock := lastBlock + 1 - rpc.BlockNumber(blocks) + oldestBlock := lastBlock + 1 - uint64(blocks) var ( - next = int64(oldestBlock) + next = oldestBlock results = make(chan *blockFees, blocks) ) for i := 0; i < maxBlockFetchers && i < blocks; i++ { go func() { for { // Retrieve the next block number to fetch with this goroutine - blockNumber := rpc.BlockNumber(atomic.AddInt64(&next, 1) - 1) + blockNumber := atomic.AddUint64(&next, 1) - 1 if blockNumber > lastBlock { return } fees := &blockFees{blockNumber: blockNumber} - if pendingBlock != nil && blockNumber >= rpc.BlockNumber(pendingBlock.NumberU64()) { + if pendingBlock != nil && blockNumber >= pendingBlock.NumberU64() { fees.block, fees.receipts = pendingBlock, pendingReceipts } else { if len(rewardPercentiles) != 0 { - fees.block, fees.err = oracle.backend.BlockByNumber(ctx, blockNumber) + fees.block, fees.err = oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNumber)) if fees.block != nil && fees.err == nil { fees.receipts, fees.err = oracle.backend.GetReceipts(ctx, fees.block.Hash()) } } else { - fees.header, fees.err = oracle.backend.HeaderByNumber(ctx, blockNumber) + fees.header, fees.err = oracle.backend.HeaderByNumber(ctx, rpc.BlockNumber(blockNumber)) } } if fees.block != nil { @@ -276,7 +277,7 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, lastBlock rpc. for ; blocks > 0; blocks-- { fees := <-results if fees.err != nil { - return 0, nil, nil, nil, fees.err + return common.Big0, nil, nil, nil, fees.err } i := int(fees.blockNumber - oldestBlock) if fees.header != nil { @@ -289,7 +290,7 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, lastBlock rpc. } } if firstMissing == 0 { - return 0, nil, nil, nil, nil + return common.Big0, nil, nil, nil, nil } if len(rewardPercentiles) != 0 { reward = reward[:firstMissing] @@ -297,5 +298,5 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, lastBlock rpc. reward = nil } baseFee, gasUsedRatio = baseFee[:firstMissing+1], gasUsedRatio[:firstMissing] - return oldestBlock, reward, baseFee, gasUsedRatio, nil + return new(big.Int).SetUint64(oldestBlock), reward, baseFee, gasUsedRatio, nil } diff --git a/eth/gasprice/feehistory_test.go b/eth/gasprice/feehistory_test.go index 195a12eecaf8..3940e1fb7e2d 100644 --- a/eth/gasprice/feehistory_test.go +++ b/eth/gasprice/feehistory_test.go @@ -32,7 +32,7 @@ func TestFeeHistory(t *testing.T) { count int last rpc.BlockNumber percent []float64 - expFirst rpc.BlockNumber + expFirst uint64 expCount int expErr error }{ @@ -70,7 +70,7 @@ func TestFeeHistory(t *testing.T) { expBaseFee++ } - if first != c.expFirst { + if first.Uint64() != c.expFirst { t.Fatalf("Test case %d: first block mismatch, want %d, got %d", i, c.expFirst, first) } if len(reward) != expReward { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 0209d6ce644f..9ceb5db1b78a 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -103,19 +103,19 @@ func (s *PublicEthereumAPI) MaxPriorityFeePerGas(ctx context.Context) (*hexutil. } type feeHistoryResult struct { - OldestBlock rpc.BlockNumber `json:"oldestBlock"` + OldestBlock *hexutil.Big `json:"oldestBlock"` Reward [][]*hexutil.Big `json:"reward,omitempty"` BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"` GasUsedRatio []float64 `json:"gasUsedRatio"` } -func (s *PublicEthereumAPI) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*feeHistoryResult, error) { - oldest, reward, baseFee, gasUsed, err := s.b.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) +func (s *PublicEthereumAPI) FeeHistory(ctx context.Context, blockCount hexutil.Uint, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*feeHistoryResult, error) { + oldest, reward, baseFee, gasUsed, err := s.b.FeeHistory(ctx, int(blockCount), lastBlock, rewardPercentiles) if err != nil { return nil, err } results := &feeHistoryResult{ - OldestBlock: oldest, + OldestBlock: (*hexutil.Big)(oldest), GasUsedRatio: gasUsed, } if reward != nil { diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 6b66806ebf28..ea7e2de766b9 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -49,7 +49,7 @@ type Backend interface { Downloader() *downloader.Downloader ProtocolVersion() int SuggestGasTipCap(ctx context.Context) (*big.Int, error) - FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (rpc.BlockNumber, [][]*big.Int, []*big.Int, []float64, error) + FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) ChainDb() ethdb.Database AccountManager() *accounts.Manager RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection diff --git a/les/api_backend.go b/les/api_backend.go index 59c2c474331b..93c94b0b2b6f 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -280,7 +280,7 @@ func (b *LesApiBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) return b.gpo.SuggestTipCap(ctx) } -func (b *LesApiBackend) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock rpc.BlockNumber, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) { +func (b *LesApiBackend) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock *big.Int, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) { return b.gpo.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) } From 62a70f0cde6fb00d8e39e283c116f28e0b032fd9 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 28 May 2024 17:12:47 +0800 Subject: [PATCH 110/479] internal/ethapi: fix panic in accesslist creation (#23225) --- core/types/transaction.go | 4 ++++ internal/ethapi/api.go | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/core/types/transaction.go b/core/types/transaction.go index 0bb03f43c5fd..fd736184c4b6 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -861,3 +861,7 @@ func (m *Message) SetBalanceTokenFeeForCall() { m.balanceTokenFee = new(big.Int).SetUint64(m.gasLimit) m.balanceTokenFee.Mul(m.balanceTokenFee, m.gasPrice) } + +func (m *Message) SetBalanceTokenFee(balanceTokenFee *big.Int) { + m.balanceTokenFee = balanceTokenFee +} diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 9ceb5db1b78a..0a7c12a5c2fc 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2063,12 +2063,19 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH } // Copy the original db so we don't modify it statedb := db.Copy() + // Set the accesslist to the last al + args.AccessList = &accessList + msg, err := args.ToMessage(b, block.Number(), b.RPCGasCap(), header.BaseFee) + if err != nil { + return nil, 0, nil, err + } + feeCapacity := state.GetTRC21FeeCapacityFromState(statedb) var balanceTokenFee *big.Int if value, ok := feeCapacity[to]; ok { balanceTokenFee = value } - msg := types.NewMessage(args.from(), args.To, uint64(*args.Nonce), args.Value.ToInt(), uint64(*args.Gas), args.GasPrice.ToInt(), big.NewInt(0), big.NewInt(0), args.data(), accessList, false, balanceTokenFee, header.Number) + msg.SetBalanceTokenFee(balanceTokenFee) // Apply the transaction with the access list tracer tracer := vm.NewAccessListTracer(accessList, args.from(), to, precompiles) From 206175fb43757c527bba61f1bb72812a8420024c Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 29 May 2024 14:54:59 +0800 Subject: [PATCH 111/479] all: remove term whitelist in comments and log messages (#23294) --- core/tx_pool.go | 4 ++-- rpc/websocket.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index 7162dd778c4e..e1ee3940df2f 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -748,8 +748,8 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { // pending or queued one, it overwrites the previous transaction if its price is higher. // // If a newly added transaction is marked as local, its sending account will be -// whitelisted, preventing any associated transaction from being dropped out of the pool -// due to pricing constraints. +// be added to the allowlist, preventing any associated transaction from being dropped +// out of the pool due to pricing constraints. func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err error) { // If the transaction is already known, discard it hash := tx.Hash() diff --git a/rpc/websocket.go b/rpc/websocket.go index 829271da4f9a..fdc1d05e9fd1 100644 --- a/rpc/websocket.go +++ b/rpc/websocket.go @@ -103,7 +103,7 @@ func wsHandshakeValidator(allowedOrigins []string) func(*http.Request) bool { if _, ok := req.Header["Origin"]; !ok { return true } - // Verify origin against whitelist. + // Verify origin against allow list. origin := strings.ToLower(req.Header.Get("Origin")) if allowAllOrigins || originIsAllowed(origins, origin) { return true From 655fb584b3aa86cd1fa249cf2d5fef09a123e079 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 29 May 2024 18:18:40 +0800 Subject: [PATCH 112/479] internal/ethapi: make ext signer sign legacy (#23274) --- internal/ethapi/transaction_args.go | 67 ++++++++++++++++------------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index cacc5dc2e939..27f93d6087ba 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -80,40 +80,45 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { } // After london, default to 1559 unless gasPrice is set head := b.CurrentHeader() - if b.ChainConfig().IsEIP1559(head.Number) && args.GasPrice == nil { - if args.MaxPriorityFeePerGas == nil { - tip, err := b.SuggestGasTipCap(ctx) - if err != nil { - return err + // If user specifies both maxPriorityfee and maxFee, then we do not + // need to consult the chain for defaults. It's definitely a London tx. + if args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil { + // In this clause, user left some fields unspecified. + if b.ChainConfig().IsEIP1559(head.Number) && args.GasPrice == nil { + if args.MaxPriorityFeePerGas == nil { + tip, err := b.SuggestGasTipCap(ctx) + if err != nil { + return err + } + args.MaxPriorityFeePerGas = (*hexutil.Big)(tip) } - args.MaxPriorityFeePerGas = (*hexutil.Big)(tip) - } - if args.MaxFeePerGas == nil { - gasFeeCap := new(big.Int).Add( - (*big.Int)(args.MaxPriorityFeePerGas), - new(big.Int).Mul(head.BaseFee, big.NewInt(2)), - ) - args.MaxFeePerGas = (*hexutil.Big)(gasFeeCap) - } - if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { - return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) - } - } else { - if args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil { - return errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") - } - if args.GasPrice == nil { - price, err := b.SuggestGasTipCap(ctx) - if err != nil { - return err + if args.MaxFeePerGas == nil { + gasFeeCap := new(big.Int).Add( + (*big.Int)(args.MaxPriorityFeePerGas), + new(big.Int).Mul(head.BaseFee, big.NewInt(2)), + ) + args.MaxFeePerGas = (*hexutil.Big)(gasFeeCap) + } + if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { + return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) + } + } else { + if args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil { + return errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") } - if b.ChainConfig().IsEIP1559(head.Number) { - // The legacy tx gas price suggestion should not add 2x base fee - // because all fees are consumed, so it would result in a spiral - // upwards. - price.Add(price, head.BaseFee) + if args.GasPrice == nil { + price, err := b.SuggestGasTipCap(ctx) + if err != nil { + return err + } + if b.ChainConfig().IsEIP1559(head.Number) { + // The legacy tx gas price suggestion should not add 2x base fee + // because all fees are consumed, so it would result in a spiral + // upwards. + price.Add(price, head.BaseFee) + } + args.GasPrice = (*hexutil.Big)(price) } - args.GasPrice = (*hexutil.Big)(price) } } if args.Value == nil { From d6daac576d51f037b71d374f8da93134afe6067f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 13 Jun 2024 15:19:48 +0800 Subject: [PATCH 113/479] core: check if sender is EOA (#23303) --- core/error.go | 3 +++ core/state_transition.go | 8 ++++++++ tests/init_test.go | 29 +++++++++++++++++++++++++---- tests/state_test.go | 7 +++++++ 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/core/error.go b/core/error.go index 327e68278b97..35106d0f1c30 100644 --- a/core/error.go +++ b/core/error.go @@ -69,4 +69,7 @@ var ( // ErrFeeCapTooLow is returned if the transaction fee cap is less than the // the base fee of the block. ErrFeeCapTooLow = errors.New("max fee per gas less than block base fee") + + // ErrSenderNoEOA is returned if the sender of a transaction is a contract. + ErrSenderNoEOA = errors.New("sender not an eoa") ) diff --git a/core/state_transition.go b/core/state_transition.go index fa9c501c1296..2b8347bbe08b 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -26,10 +26,13 @@ import ( cmath "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" + "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" ) +var emptyCodeHash = crypto.Keccak256Hash(nil) + var ( errInsufficientBalanceForGas = errors.New("insufficient balance to pay for gas") ) @@ -222,6 +225,11 @@ func (st *StateTransition) preCheck() error { msg.From().Hex(), stNonce) } } + // Make sure the sender is an EOA + if codeHash := st.state.GetCodeHash(st.msg.From()); codeHash != emptyCodeHash && codeHash != (common.Hash{}) { + return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA, + st.msg.From().Hex(), codeHash) + } // Make sure that transaction gasFeeCap is greater than the baseFee (post london) if st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber) { // Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call) diff --git a/tests/init_test.go b/tests/init_test.go index 53fe8b41201c..b6499a2e199e 100644 --- a/tests/init_test.go +++ b/tests/init_test.go @@ -87,10 +87,11 @@ func findLine(data []byte, offset int64) (line int) { // testMatcher controls skipping and chain config assignment to tests. type testMatcher struct { - configpat []testConfig - failpat []testFailure - skiploadpat []*regexp.Regexp - skipshortpat []*regexp.Regexp + configpat []testConfig + failpat []testFailure + skiploadpat []*regexp.Regexp + skipshortpat []*regexp.Regexp + runonlylistpat *regexp.Regexp } type testConfig struct { @@ -121,6 +122,10 @@ func (tm *testMatcher) fails(pattern string, reason string) { tm.failpat = append(tm.failpat, testFailure{regexp.MustCompile(pattern), reason}) } +func (tm *testMatcher) runonly(pattern string) { + tm.runonlylistpat = regexp.MustCompile(pattern) +} + // config defines chain config for tests matching the pattern. func (tm *testMatcher) config(pattern string, cfg params.ChainConfig) { tm.configpat = append(tm.configpat, testConfig{regexp.MustCompile(pattern), cfg}) @@ -209,6 +214,11 @@ func (tm *testMatcher) runTestFile(t *testing.T, path, name string, runTest inte if r, _ := tm.findSkip(name); r != "" { t.Skip(r) } + if tm.runonlylistpat != nil { + if !tm.runonlylistpat.MatchString(name) { + t.Skip("Skipped by runonly") + } + } t.Parallel() // Load the file as map[string]. @@ -262,3 +272,14 @@ func runTestFunc(runTest interface{}, t *testing.T, name string, m reflect.Value m.MapIndex(reflect.ValueOf(key)), }) } + +func TestMatcherRunonlylist(t *testing.T) { + t.Parallel() + tm := new(testMatcher) + tm.runonly("invalid*") + tm.walk(t, rlpTestDir, func(t *testing.T, name string, test *RLPTest) { + if name[:len("invalidRLPTest.json")] != "invalidRLPTest.json" { + t.Fatalf("invalid test found: %s != invalidRLPTest.json", name) + } + }) +} diff --git a/tests/state_test.go b/tests/state_test.go index 5c91a978c520..1b77c6df5265 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -31,9 +31,16 @@ func TestState(t *testing.T) { st := new(testMatcher) // Long tests: st.skipShortMode(`^stQuadraticComplexityTest/`) + // Broken tests: st.skipLoad(`^stTransactionTest/OverflowGasRequire\.json`) // gasLimit > 256 bits st.skipLoad(`^stTransactionTest/zeroSigTransa[^/]*\.json`) // EIP-86 is not supported yet + + // Uses 1GB RAM per tested fork + st.skipLoad(`^stStaticCall/static_Call1MB`) + // Un-skip this when https://github.com/ethereum/tests/issues/908 is closed + st.skipLoad(`^stQuadraticComplexityTest/QuadraticComplexitySolidity_CallDataCopy`) + // Expected failures: st.fails(`^stRevertTest/RevertPrecompiledTouch\.json/EIP158`, "bug in test") st.fails(`^stRevertTest/RevertPrefoundEmptyOOG\.json/EIP158`, "bug in test") From 48616d5d4becefe9e9d2f7137751b3d8a04601b3 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 28 May 2024 17:16:54 +0800 Subject: [PATCH 114/479] internal/ethapi/api: return maxFeePerGas for gasPrice for EIP-1559 txs (#23345) --- internal/ethapi/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 0a7c12a5c2fc..b5e2f329dd65 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1928,7 +1928,7 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber price := math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap()) result.GasPrice = (*hexutil.Big)(price) } else { - result.GasPrice = nil + result.GasPrice = (*hexutil.Big)(tx.GasFeeCap()) } } return result From e4895bf5c5ba8a848862c8ab22b71ffe74dcfeee Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 29 May 2024 19:17:05 +0800 Subject: [PATCH 115/479] internal/ethapi: add back missing check for maxfee < maxPriorityFee (#23384) --- internal/ethapi/transaction_args.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 27f93d6087ba..9e13b876b28c 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -120,6 +120,11 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { args.GasPrice = (*hexutil.Big)(price) } } + } else { + // Both maxPriorityfee and maxFee set by caller. Sanity-check their internal relation + if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { + return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) + } } if args.Value == nil { args.Value = new(hexutil.Big) From d850fc408141042cec9a9dda0cd1996960dc92d6 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 13 Jun 2024 15:50:50 +0800 Subject: [PATCH 116/479] core: only check sendernoeoa in non fake mode (#23424) --- accounts/abi/bind/backends/simulated.go | 2 +- core/state_transition.go | 17 +++++++++-------- core/token_validator.go | 2 +- core/types/transaction.go | 10 +++++----- internal/ethapi/transaction_args.go | 4 ++-- les/odr_test.go | 4 ++-- light/odr_test.go | 2 +- tests/state_test_util.go | 2 +- 8 files changed, 22 insertions(+), 21 deletions(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 6ad9d01cdbe1..3ee19bb96148 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -569,7 +569,7 @@ type callMsg struct { func (m callMsg) From() common.Address { return m.CallMsg.From } func (m callMsg) Nonce() uint64 { return 0 } -func (m callMsg) CheckNonce() bool { return false } +func (m callMsg) IsFake() bool { return true } func (m callMsg) To() *common.Address { return m.CallMsg.To } func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } func (m callMsg) GasFeeCap() *big.Int { return m.CallMsg.GasFeeCap } diff --git a/core/state_transition.go b/core/state_transition.go index 2b8347bbe08b..d75867da7b30 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -83,7 +83,7 @@ type Message interface { Value() *big.Int Nonce() uint64 - CheckNonce() bool + IsFake() bool Data() []byte BalanceTokenFee() *big.Int AccessList() types.AccessList @@ -210,9 +210,10 @@ func (st *StateTransition) buyGas() error { } func (st *StateTransition) preCheck() error { - // Make sure this transaction's nonce is correct. + // Only check transactions that are not fake msg := st.msg - if msg.CheckNonce() { + if !msg.IsFake() { + // Make sure this transaction's nonce is correct. stNonce := st.state.GetNonce(msg.From()) if msgNonce := msg.Nonce(); stNonce < msgNonce { return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooHigh, @@ -224,11 +225,11 @@ func (st *StateTransition) preCheck() error { return fmt.Errorf("%w: address %v, nonce: %d", ErrNonceMax, msg.From().Hex(), stNonce) } - } - // Make sure the sender is an EOA - if codeHash := st.state.GetCodeHash(st.msg.From()); codeHash != emptyCodeHash && codeHash != (common.Hash{}) { - return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA, - st.msg.From().Hex(), codeHash) + // Make sure the sender is an EOA + if codeHash := st.state.GetCodeHash(msg.From()); codeHash != emptyCodeHash && codeHash != (common.Hash{}) { + return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA, + msg.From().Hex(), codeHash) + } } // Make sure that transaction gasFeeCap is greater than the baseFee (post london) if st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber) { diff --git a/core/token_validator.go b/core/token_validator.go index c8c3ba4ad806..628db3c98a98 100644 --- a/core/token_validator.go +++ b/core/token_validator.go @@ -45,7 +45,7 @@ type callMsg struct { func (m callMsg) From() common.Address { return m.CallMsg.From } func (m callMsg) Nonce() uint64 { return 0 } -func (m callMsg) CheckNonce() bool { return false } +func (m callMsg) IsFake() bool { return true } func (m callMsg) To() *common.Address { return m.CallMsg.To } func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } func (m callMsg) GasFeeCap() *big.Int { return m.CallMsg.GasFeeCap } diff --git a/core/types/transaction.go b/core/types/transaction.go index fd736184c4b6..e660c49c0bcd 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -433,7 +433,7 @@ func (tx *Transaction) AsMessage(s Signer, balanceFee, blockNumber, baseFee *big amount: tx.Value(), data: tx.Data(), accessList: tx.AccessList(), - checkNonce: true, + isFake: false, balanceTokenFee: balanceFee, } @@ -818,11 +818,11 @@ type Message struct { gasTipCap *big.Int data []byte accessList AccessList - checkNonce bool + isFake bool balanceTokenFee *big.Int } -func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice, gasFeeCap, gasTipCap *big.Int, data []byte, accessList AccessList, checkNonce bool, balanceTokenFee *big.Int, number *big.Int) Message { +func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice, gasFeeCap, gasTipCap *big.Int, data []byte, accessList AccessList, isFake bool, balanceTokenFee *big.Int, number *big.Int) Message { if balanceTokenFee != nil { gasPrice = common.GetGasPrice(number) } @@ -837,7 +837,7 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *b gasTipCap: gasTipCap, data: data, accessList: accessList, - checkNonce: checkNonce, + isFake: isFake, balanceTokenFee: balanceTokenFee, } } @@ -852,7 +852,7 @@ func (m Message) Value() *big.Int { return m.amount } func (m Message) Gas() uint64 { return m.gasLimit } func (m Message) Nonce() uint64 { return m.nonce } func (m Message) Data() []byte { return m.data } -func (m Message) CheckNonce() bool { return m.checkNonce } +func (m Message) IsFake() bool { return m.isFake } func (m Message) AccessList() AccessList { return m.accessList } func (m *Message) SetNonce(nonce uint64) { m.nonce = nonce } diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 9e13b876b28c..4b92a964ea29 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -172,7 +172,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { return nil } -// ToMessage converts th transaction arguments to the Message type used by the +// ToMessage converts the transaction arguments to the Message type used by the // core evm. This method is used in calls and traces that do not require a real // live transaction. func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap uint64, baseFee *big.Int) (types.Message, error) { @@ -252,7 +252,7 @@ func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap accessList = *args.AccessList } - msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, gasFeeCap, gasTipCap, data, accessList, false, nil, number) + msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, gasFeeCap, gasTipCap, data, accessList, true, nil, number) return msg, nil } diff --git a/les/odr_test.go b/les/odr_test.go index 20eac1f09e3e..b4aa8cf21d0f 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -133,7 +133,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai if value, ok := feeCapacity[testContractAddr]; ok { balanceTokenFee = value } - msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, false, balanceTokenFee, header.Number)} + msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true, balanceTokenFee, header.Number)} context := core.NewEVMBlockContext(header, bc, nil) txContext := core.NewEVMTxContext(msg) @@ -154,7 +154,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai if value, ok := feeCapacity[testContractAddr]; ok { balanceTokenFee = value } - msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 100000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, false, balanceTokenFee, header.Number)} + msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 100000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true, balanceTokenFee, header.Number)} context := core.NewEVMBlockContext(header, lc, nil) txContext := core.NewEVMTxContext(msg) vmenv := vm.NewEVM(context, txContext, statedb, nil, config, vm.Config{NoBaseFee: true}) diff --git a/light/odr_test.go b/light/odr_test.go index 3d211b948b12..94c1246065ad 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -185,7 +185,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain if value, ok := feeCapacity[testContractAddr]; ok { balanceTokenFee = value } - msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, false, balanceTokenFee, header.Number)} + msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true, balanceTokenFee, header.Number)} txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(header, chain, nil) vmenv := vm.NewEVM(context, txContext, st, nil, config, vm.Config{NoBaseFee: true}) diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 51c4664eafca..30750b973c73 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -282,7 +282,7 @@ func (tx *stTransaction) toMessage(ps stPostState, number *big.Int, baseFee *big return nil, errors.New("no gas price provided") } - msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, tx.MaxFeePerGas, tx.MaxPriorityFeePerGas, data, accessList, true, nil, number) + msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, tx.MaxFeePerGas, tx.MaxPriorityFeePerGas, data, accessList, false, nil, number) return msg, nil } From e0a1ef31eba13092e8b9d6e25de6d1ee16b30498 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 30 May 2024 12:48:06 +0800 Subject: [PATCH 117/479] eth/gasprice: feeHistory improvements (#23422) --- eth/ethconfig/config.go | 4 +- eth/gasprice/feehistory.go | 105 +++++++++++++++++--------------- eth/gasprice/feehistory_test.go | 22 +++---- eth/gasprice/gasprice.go | 20 ++++++ eth/gasprice/gasprice_test.go | 5 ++ 5 files changed, 95 insertions(+), 61 deletions(-) diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 81760c03a050..74857fa80031 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -38,8 +38,8 @@ import ( var FullNodeGPO = gasprice.Config{ Blocks: 20, Percentile: 60, - MaxHeaderHistory: 0, - MaxBlockHistory: 0, + MaxHeaderHistory: 1024, + MaxBlockHistory: 1024, MaxPrice: gasprice.DefaultMaxPrice, IgnorePrice: gasprice.DefaultIgnorePrice, } diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index 7f2016823c96..509d9bb5b673 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -18,8 +18,10 @@ package gasprice import ( "context" + "encoding/binary" "errors" "fmt" + "math" "math/big" "sort" "sync/atomic" @@ -37,10 +39,6 @@ var ( ) const ( - // maxFeeHistory is the maximum number of blocks that can be retrieved for a - // fee history request. - maxFeeHistory = 1024 - // maxBlockFetchers is the max number of goroutines to spin up to pull blocks // for the fee history calculation (mostly relevant for LES). maxBlockFetchers = 4 @@ -54,10 +52,15 @@ type blockFees struct { block *types.Block // only set if reward percentiles are requested receipts types.Receipts // filled by processBlock + results processedFees + err error +} + +// processedFees contains the results of a processed block and is also used for caching +type processedFees struct { reward []*big.Int baseFee, nextBaseFee *big.Int gasUsedRatio float64 - err error } // txGasAndReward is sorted in ascending order based on reward @@ -82,15 +85,15 @@ func (s sortGasAndReward) Less(i, j int) bool { // fills in the rest of the fields. func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { chainconfig := oracle.backend.ChainConfig() - if bf.baseFee = bf.header.BaseFee; bf.baseFee == nil { - bf.baseFee = new(big.Int) + if bf.results.baseFee = bf.header.BaseFee; bf.results.baseFee == nil { + bf.results.baseFee = new(big.Int) } if chainconfig.IsEIP1559(big.NewInt(int64(bf.blockNumber + 1))) { - bf.nextBaseFee = misc.CalcBaseFee(chainconfig, bf.header) + bf.results.nextBaseFee = misc.CalcBaseFee(chainconfig, bf.header) } else { - bf.nextBaseFee = new(big.Int) + bf.results.nextBaseFee = new(big.Int) } - bf.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit) + bf.results.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit) if len(percentiles) == 0 { // rewards were not requested, return null return @@ -100,11 +103,11 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { return } - bf.reward = make([]*big.Int, len(percentiles)) + bf.results.reward = make([]*big.Int, len(percentiles)) if len(bf.block.Transactions()) == 0 { // return an all zero row if there are no transactions to gather data from - for i := range bf.reward { - bf.reward[i] = new(big.Int) + for i := range bf.results.reward { + bf.results.reward[i] = new(big.Int) } return } @@ -125,7 +128,7 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { txIndex++ sumGasUsed += sorter[txIndex].gasUsed } - bf.reward[i] = sorter[txIndex].reward + bf.results.reward[i] = sorter[txIndex].reward } } @@ -134,7 +137,7 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { // also returned if requested and available. // Note: an error is only returned if retrieving the head header has failed. If there are no // retrievable blocks in the specified range then zero block count is returned with no error. -func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlock rpc.BlockNumber, blocks, maxHistory int) (*types.Block, []*types.Receipt, uint64, int, error) { +func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlock rpc.BlockNumber, blocks int) (*types.Block, []*types.Receipt, uint64, int, error) { var ( headBlock rpc.BlockNumber pendingBlock *types.Block @@ -167,17 +170,6 @@ func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlock rpc.Block } else if pendingBlock == nil && lastBlock > headBlock { return nil, nil, 0, 0, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, lastBlock, headBlock) } - if maxHistory != 0 { - // limit retrieval to the given number of latest blocks - if tooOldCount := int64(headBlock) - int64(maxHistory) - int64(lastBlock) + int64(blocks); tooOldCount > 0 { - // tooOldCount is the number of requested blocks that are too old to be served - if int64(blocks) > tooOldCount { - blocks -= int(tooOldCount) - } else { - return nil, nil, 0, 0, nil - } - } - } // ensure not trying to retrieve before genesis if rpc.BlockNumber(blocks) > lastBlock+1 { blocks = int(lastBlock + 1) @@ -203,6 +195,10 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast if blocks < 1 { return common.Big0, nil, nil, nil, nil // returning with no data and no error means there are no retrievable blocks } + maxFeeHistory := oracle.maxHeaderHistory + if len(rewardPercentiles) != 0 { + maxFeeHistory = oracle.maxBlockHistory + } if blocks > maxFeeHistory { log.Warn("Sanitizing fee history length", "requested", blocks, "truncated", maxFeeHistory) blocks = maxFeeHistory @@ -215,17 +211,12 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast return common.Big0, nil, nil, nil, fmt.Errorf("%w: #%d:%f > #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p) } } - // Only process blocks if reward percentiles were requested - maxHistory := oracle.maxHeaderHistory - if len(rewardPercentiles) != 0 { - maxHistory = oracle.maxBlockHistory - } var ( pendingBlock *types.Block pendingReceipts []*types.Receipt err error ) - pendingBlock, pendingReceipts, lastBlock, blocks, err := oracle.resolveBlockRange(ctx, unresolvedLastBlock, blocks, maxHistory) + pendingBlock, pendingReceipts, lastBlock, blocks, err := oracle.resolveBlockRange(ctx, unresolvedLastBlock, blocks) if err != nil || blocks == 0 { return common.Big0, nil, nil, nil, err } @@ -235,6 +226,10 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast next = oldestBlock results = make(chan *blockFees, blocks) ) + percentileKey := make([]byte, 8*len(rewardPercentiles)) + for i, p := range rewardPercentiles { + binary.LittleEndian.PutUint64(percentileKey[i*8:(i+1)*8], math.Float64bits(p)) + } for i := 0; i < maxBlockFetchers && i < blocks; i++ { go func() { for { @@ -247,24 +242,38 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast fees := &blockFees{blockNumber: blockNumber} if pendingBlock != nil && blockNumber >= pendingBlock.NumberU64() { fees.block, fees.receipts = pendingBlock, pendingReceipts + fees.header = fees.block.Header() + oracle.processBlock(fees, rewardPercentiles) + results <- fees } else { - if len(rewardPercentiles) != 0 { - fees.block, fees.err = oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNumber)) - if fees.block != nil && fees.err == nil { - fees.receipts, fees.err = oracle.backend.GetReceipts(ctx, fees.block.Hash()) - } + cacheKey := struct { + number uint64 + percentiles string + }{blockNumber, string(percentileKey)} + + if p, ok := oracle.historyCache.Get(cacheKey); ok { + fees.results = p.(processedFees) + results <- fees } else { - fees.header, fees.err = oracle.backend.HeaderByNumber(ctx, rpc.BlockNumber(blockNumber)) + if len(rewardPercentiles) != 0 { + fees.block, fees.err = oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNumber)) + if fees.block != nil && fees.err == nil { + fees.receipts, fees.err = oracle.backend.GetReceipts(ctx, fees.block.Hash()) + fees.header = fees.block.Header() + } + } else { + fees.header, fees.err = oracle.backend.HeaderByNumber(ctx, rpc.BlockNumber(blockNumber)) + } + if fees.header != nil && fees.err == nil { + oracle.processBlock(fees, rewardPercentiles) + if fees.err == nil { + oracle.historyCache.Add(cacheKey, fees.results) + } + } + // send to results even if empty to guarantee that blocks items are sent in total + results <- fees } } - if fees.block != nil { - fees.header = fees.block.Header() - } - if fees.header != nil { - oracle.processBlock(fees, rewardPercentiles) - } - // send to results even if empty to guarantee that blocks items are sent in total - results <- fees } }() } @@ -280,8 +289,8 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast return common.Big0, nil, nil, nil, fees.err } i := int(fees.blockNumber - oldestBlock) - if fees.header != nil { - reward[i], baseFee[i], baseFee[i+1], gasUsedRatio[i] = fees.reward, fees.baseFee, fees.nextBaseFee, fees.gasUsedRatio + if fees.results.baseFee != nil { + reward[i], baseFee[i], baseFee[i+1], gasUsedRatio[i] = fees.results.reward, fees.results.baseFee, fees.results.nextBaseFee, fees.results.gasUsedRatio } else { // getting no block and no error means we are requesting into the future (might happen because of a reorg) if i < firstMissing { diff --git a/eth/gasprice/feehistory_test.go b/eth/gasprice/feehistory_test.go index 3940e1fb7e2d..2c1e3cefc59e 100644 --- a/eth/gasprice/feehistory_test.go +++ b/eth/gasprice/feehistory_test.go @@ -36,20 +36,20 @@ func TestFeeHistory(t *testing.T) { expCount int expErr error }{ - {false, 0, 0, 10, 30, nil, 21, 10, nil}, - {false, 0, 0, 10, 30, []float64{0, 10}, 21, 10, nil}, - {false, 0, 0, 10, 30, []float64{20, 10}, 0, 0, errInvalidPercentile}, - {false, 0, 0, 1000000000, 30, nil, 0, 31, nil}, - {false, 0, 0, 1000000000, rpc.LatestBlockNumber, nil, 0, 33, nil}, - {false, 0, 0, 10, 40, nil, 0, 0, errRequestBeyondHead}, - {true, 0, 0, 10, 40, nil, 0, 0, errRequestBeyondHead}, + {false, 1000, 1000, 10, 30, nil, 21, 10, nil}, + {false, 1000, 1000, 10, 30, []float64{0, 10}, 21, 10, nil}, + {false, 1000, 1000, 10, 30, []float64{20, 10}, 0, 0, errInvalidPercentile}, + {false, 1000, 1000, 1000000000, 30, nil, 0, 31, nil}, + {false, 1000, 1000, 1000000000, rpc.LatestBlockNumber, nil, 0, 33, nil}, + {false, 1000, 1000, 10, 40, nil, 0, 0, errRequestBeyondHead}, + {true, 1000, 1000, 10, 40, nil, 0, 0, errRequestBeyondHead}, {false, 20, 2, 100, rpc.LatestBlockNumber, nil, 13, 20, nil}, {false, 20, 2, 100, rpc.LatestBlockNumber, []float64{0, 10}, 31, 2, nil}, {false, 20, 2, 100, 32, []float64{0, 10}, 31, 2, nil}, - {false, 0, 0, 1, rpc.PendingBlockNumber, nil, 0, 0, nil}, - {false, 0, 0, 2, rpc.PendingBlockNumber, nil, 32, 1, nil}, - {true, 0, 0, 2, rpc.PendingBlockNumber, nil, 32, 2, nil}, - {true, 0, 0, 2, rpc.PendingBlockNumber, []float64{0, 10}, 32, 2, nil}, + {false, 1000, 1000, 1, rpc.PendingBlockNumber, nil, 0, 0, nil}, + {false, 1000, 1000, 2, rpc.PendingBlockNumber, nil, 32, 1, nil}, + {true, 1000, 1000, 2, rpc.PendingBlockNumber, nil, 32, 2, nil}, + {true, 1000, 1000, 2, rpc.PendingBlockNumber, []float64{0, 10}, 32, 2, nil}, } for i, c := range cases { config := Config{ diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index 8d6e7f0d2067..9087b4bdd868 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -23,10 +23,13 @@ import ( "sync" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/event" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rpc" + lru "github.com/hashicorp/golang-lru" ) const sampleNumber = 3 // Number of transactions sampled in a block @@ -53,6 +56,7 @@ type OracleBackend interface { GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) PendingBlockAndReceipts() (*types.Block, types.Receipts) ChainConfig() *params.ChainConfig + SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription } // Oracle recommends gas prices based on the content of recent @@ -68,6 +72,7 @@ type Oracle struct { checkBlocks, percentile int maxHeaderHistory, maxBlockHistory int + historyCache *lru.Cache } // NewOracle returns a new gasprice oracle which can recommend suitable @@ -99,6 +104,20 @@ func NewOracle(backend OracleBackend, params Config) *Oracle { } else if ignorePrice.Int64() > 0 { log.Info("Gasprice oracle is ignoring threshold set", "threshold", ignorePrice) } + + cache, _ := lru.New(2048) + headEvent := make(chan core.ChainHeadEvent, 1) + backend.SubscribeChainHeadEvent(headEvent) + go func() { + var lastHead common.Hash + for ev := range headEvent { + if ev.Block.ParentHash() != lastHead { + cache.Purge() + } + lastHead = ev.Block.Hash() + } + }() + return &Oracle{ backend: backend, lastPrice: params.Default, @@ -108,6 +127,7 @@ func NewOracle(backend OracleBackend, params Config) *Oracle { percentile: percent, maxHeaderHistory: params.MaxHeaderHistory, maxBlockHistory: params.MaxBlockHistory, + historyCache: cache, } } diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index a56220290141..b51ebc9556e6 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -29,6 +29,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/event" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rpc" ) @@ -90,6 +91,10 @@ func (b *testBackend) ChainConfig() *params.ChainConfig { return b.chain.Config() } +func (b *testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { + return nil +} + func newTestBackend(t *testing.T, eip1559Block *big.Int, pending bool) *testBackend { var ( key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") From e18e6111afce1dee871588ba5ddcc603451a1756 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 30 May 2024 13:43:58 +0800 Subject: [PATCH 118/479] core: add change counter (#23095) --- core/tx_pool.go | 26 ++++++++++++++++++++++++++ core/tx_pool_test.go | 14 +++++++------- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index e1ee3940df2f..ce0f5194de49 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -134,6 +134,14 @@ var ( invalidTxMeter = metrics.NewRegisteredMeter("txpool/invalid", nil) underpricedTxMeter = metrics.NewRegisteredMeter("txpool/underpriced", nil) overflowedTxMeter = metrics.NewRegisteredMeter("txpool/overflowed", nil) + // throttleTxMeter counts how many transactions are rejected due to too-many-changes between + // txpool reorgs. + throttleTxMeter = metrics.NewRegisteredMeter("txpool/throttle", nil) + // reorgDurationTimer measures how long time a txpool reorg takes. + reorgDurationTimer = metrics.NewRegisteredTimer("txpool/reorgtime", nil) + // dropBetweenReorgHistogram counts how many drops we experience between two reorg runs. It is expected + // that this number is pretty low, since txpool reorgs happen very frequently. + dropBetweenReorgHistogram = metrics.NewRegisteredHistogram("txpool/dropbetweenreorg", nil, metrics.NewExpDecaySample(1028, 0.015)) pendingGauge = metrics.NewRegisteredGauge("txpool/pending", nil) queuedGauge = metrics.NewRegisteredGauge("txpool/queued", nil) @@ -291,6 +299,8 @@ type TxPool struct { wg sync.WaitGroup // tracks loop, scheduleReorgLoop initDoneCh chan struct{} // is closed once the pool is initialized (for tests) + changesSinceReorg int // A counter for how many drops we've performed in-between reorg. + IsSigner func(address common.Address) bool trc21FeeCapacity map[common.Address]*big.Int } @@ -780,6 +790,15 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e underpricedTxMeter.Mark(1) return false, ErrUnderpriced } + // We're about to replace a transaction. The reorg does a more thorough + // analysis of what to remove and how, but it runs async. We don't want to + // do too many replacements between reorg-runs, so we cap the number of + // replacements to 25% of the slots + if pool.changesSinceReorg > int(pool.config.GlobalSlots/4) { + throttleTxMeter.Mark(1) + return false, ErrTxPoolOverflow + } + // New transaction is better than our worse ones, make room for it. // If it's a local transaction, forcibly discard all available transactions. // Otherwise if we can't make enough room for new one, abort the operation. @@ -791,6 +810,8 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e overflowedTxMeter.Mark(1) return false, ErrTxPoolOverflow } + // Bump the counter of rejections-since-reorg + pool.changesSinceReorg += len(drop) // Kick out the underpriced remote transactions. for _, tx := range drop { log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap()) @@ -1269,6 +1290,9 @@ func (pool *TxPool) scheduleReorgLoop() { // runReorg runs reset and promoteExecutables on behalf of scheduleReorgLoop. func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirtyAccounts *accountSet, events map[common.Address]*txSortedMap) { + defer func(t0 time.Time) { + reorgDurationTimer.Update(time.Since(t0)) + }(time.Now()) defer close(done) var promoteAddrs []common.Address @@ -1318,6 +1342,8 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt highestPending := list.LastElement() pool.pendingNonces.set(addr, highestPending.Nonce()+1) } + dropBetweenReorgHistogram.Update(int64(pool.changesSinceReorg)) + pool.changesSinceReorg = 0 // Reset change counter pool.mu.Unlock() // Notify subsystems for newly added transactions diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index 3e0bcb7b055a..b309deefc21d 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -1981,20 +1981,20 @@ func TestDualHeapEviction(t *testing.T) { } add := func(urgent bool) { - txs := make([]*types.Transaction, 20) - for i := range txs { + for i := 0; i < 20; i++ { + var tx *types.Transaction // Create a test accounts and fund it key, _ := crypto.GenerateKey() testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000000)) if urgent { - txs[i] = dynamicFeeTx(0, 100000, big.NewInt(int64(baseFee+1+i)), big.NewInt(int64(1+i)), key) - highTip = txs[i] + tx = dynamicFeeTx(0, 100000, big.NewInt(int64(baseFee+1+i)), big.NewInt(int64(1+i)), key) + highTip = tx } else { - txs[i] = dynamicFeeTx(0, 100000, big.NewInt(int64(baseFee+200+i)), big.NewInt(1), key) - highCap = txs[i] + tx = dynamicFeeTx(0, 100000, big.NewInt(int64(baseFee+200+i)), big.NewInt(1), key) + highCap = tx } + pool.AddRemotesSync([]*types.Transaction{tx}) } - pool.AddRemotes(txs) pending, queued := pool.Stats() if pending+queued != 20 { t.Fatalf("transaction count mismatch: have %d, want %d", pending+queued, 20) From 17b62319c02e46db989aaedb81b88fd3eb944555 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 11 Jun 2024 13:10:54 +0800 Subject: [PATCH 119/479] core/types: copy tx recipient address (#23376) --- core/types/access_list_tx.go | 3 +-- core/types/dynamic_fee_tx.go | 3 +-- core/types/legacy_tx.go | 2 +- core/types/transaction.go | 17 ++++++++++------- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/core/types/access_list_tx.go b/core/types/access_list_tx.go index 265fd7545812..f01889238f74 100644 --- a/core/types/access_list_tx.go +++ b/core/types/access_list_tx.go @@ -59,7 +59,7 @@ type AccessListTx struct { func (tx *AccessListTx) copy() TxData { cpy := &AccessListTx{ Nonce: tx.Nonce, - To: tx.To, // TODO: copy pointed-to address + To: copyAddressPtr(tx.To), Data: common.CopyBytes(tx.Data), Gas: tx.Gas, // These are copied below. @@ -96,7 +96,6 @@ func (tx *AccessListTx) copy() TxData { // accessors for innerTx. func (tx *AccessListTx) txType() byte { return AccessListTxType } func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID } -func (tx *AccessListTx) protected() bool { return true } func (tx *AccessListTx) accessList() AccessList { return tx.AccessList } func (tx *AccessListTx) data() []byte { return tx.Data } func (tx *AccessListTx) gas() uint64 { return tx.Gas } diff --git a/core/types/dynamic_fee_tx.go b/core/types/dynamic_fee_tx.go index 7dfb2ad9f37a..b559f1ae6652 100644 --- a/core/types/dynamic_fee_tx.go +++ b/core/types/dynamic_fee_tx.go @@ -43,7 +43,7 @@ type DynamicFeeTx struct { func (tx *DynamicFeeTx) copy() TxData { cpy := &DynamicFeeTx{ Nonce: tx.Nonce, - To: tx.To, // TODO: copy pointed-to address + To: copyAddressPtr(tx.To), Data: common.CopyBytes(tx.Data), Gas: tx.Gas, // These are copied below. @@ -84,7 +84,6 @@ func (tx *DynamicFeeTx) copy() TxData { // accessors for innerTx. func (tx *DynamicFeeTx) txType() byte { return DynamicFeeTxType } func (tx *DynamicFeeTx) chainID() *big.Int { return tx.ChainID } -func (tx *DynamicFeeTx) protected() bool { return true } func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList } func (tx *DynamicFeeTx) data() []byte { return tx.Data } func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas } diff --git a/core/types/legacy_tx.go b/core/types/legacy_tx.go index 819909a2a375..b3a4ca9667cf 100644 --- a/core/types/legacy_tx.go +++ b/core/types/legacy_tx.go @@ -62,7 +62,7 @@ func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPric func (tx *LegacyTx) copy() TxData { cpy := &LegacyTx{ Nonce: tx.Nonce, - To: tx.To, // TODO: copy pointed-to address + To: copyAddressPtr(tx.To), Data: common.CopyBytes(tx.Data), Gas: tx.Gas, // These are initialized below. diff --git a/core/types/transaction.go b/core/types/transaction.go index e660c49c0bcd..299f763620f4 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -299,13 +299,7 @@ func (tx *Transaction) Nonce() uint64 { return tx.inner.nonce() } // To returns the recipient address of the transaction. // For contract-creation transactions, To returns nil. func (tx *Transaction) To() *common.Address { - // Copy the pointed-to address. - ito := tx.inner.to() - if ito == nil { - return nil - } - cpy := *ito - return &cpy + return copyAddressPtr(tx.inner.to()) } func (tx *Transaction) From() *common.Address { @@ -865,3 +859,12 @@ func (m *Message) SetBalanceTokenFeeForCall() { func (m *Message) SetBalanceTokenFee(balanceTokenFee *big.Int) { m.balanceTokenFee = balanceTokenFee } + +// copyAddressPtr copies an address. +func copyAddressPtr(a *common.Address) *common.Address { + if a == nil { + return nil + } + cpy := *a + return &cpy +} From 27071a4219386546b79d498b7c0ed6c6462f26de Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 31 May 2024 14:34:27 +0800 Subject: [PATCH 120/479] eth/gasprice: avoid modifying TestChainConfig (#23204) --- eth/gasprice/gasprice_test.go | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index b51ebc9556e6..c3831e9c7c3b 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -96,21 +96,18 @@ func (b *testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) eve } func newTestBackend(t *testing.T, eip1559Block *big.Int, pending bool) *testBackend { + config := *params.TestChainConfig // needs copy because it is modified below + config.Eip1559Block = eip1559Block + var ( key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") addr = crypto.PubkeyToAddress(key.PublicKey) gspec = &core.Genesis{ - Config: params.TestChainConfig, + Config: &config, Alloc: core.GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}}, } signer = types.LatestSigner(gspec.Config) ) - if eip1559Block != nil { - gspec.Config.Eip1559Block = eip1559Block - signer = types.LatestSigner(gspec.Config) - } else { - gspec.Config.Eip1559Block = nil - } engine := ethash.NewFaker() db := rawdb.NewMemoryDatabase() genesis, _ := gspec.Commit(db) @@ -119,9 +116,9 @@ func newTestBackend(t *testing.T, eip1559Block *big.Int, pending bool) *testBack blocks, _ := core.GenerateChain(gspec.Config, genesis, engine, db, testHead+1, func(i int, b *core.BlockGen) { b.SetCoinbase(common.Address{1}) - var tx *types.Transaction + var txdata types.TxData if eip1559Block != nil && b.Number().Cmp(eip1559Block) >= 0 { - txdata := &types.DynamicFeeTx{ + txdata = &types.DynamicFeeTx{ ChainID: gspec.Config.ChainId, Nonce: b.TxNonce(addr), To: &common.Address{}, @@ -130,9 +127,8 @@ func newTestBackend(t *testing.T, eip1559Block *big.Int, pending bool) *testBack GasTipCap: big.NewInt(int64(i+1) * params.GWei), Data: []byte{}, } - tx = types.NewTx(txdata) } else { - txdata := &types.LegacyTx{ + txdata = &types.LegacyTx{ Nonce: b.TxNonce(addr), To: &common.Address{}, Gas: 21000, @@ -140,13 +136,8 @@ func newTestBackend(t *testing.T, eip1559Block *big.Int, pending bool) *testBack Value: big.NewInt(100), Data: []byte{}, } - tx = types.NewTx(txdata) - } - tx, err := types.SignTx(tx, signer, key) - if err != nil { - t.Fatalf("failed to create tx: %v", err) } - b.AddTx(tx) + b.AddTx(types.MustSignNewTx(key, signer, txdata)) }) // Construct testing chain diskdb := rawdb.NewMemoryDatabase() From e5fb0b4d730bd81d15a4c387389ba4b72e690e04 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 30 May 2024 15:17:33 +0800 Subject: [PATCH 121/479] core: remove unused error from TxPool.Pending (#23720) --- core/tx_pool.go | 4 ++-- core/tx_pool_test.go | 4 ---- eth/api_backend.go | 5 +---- eth/helper_test.go | 4 ++-- eth/protocol.go | 2 +- eth/sync.go | 2 +- miner/worker.go | 6 +----- 7 files changed, 8 insertions(+), 19 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index ce0f5194de49..a1a9655e46b3 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -569,7 +569,7 @@ func (pool *TxPool) ContentFrom(addr common.Address) (types.Transactions, types. // The enforceTips parameter can be used to do an extra filtering on the pending // transactions and only return those whose **effective** tip is large enough in // the next pending execution environment. -func (pool *TxPool) Pending(enforceTips bool) (map[common.Address]types.Transactions, error) { +func (pool *TxPool) Pending(enforceTips bool) map[common.Address]types.Transactions { pool.mu.Lock() defer pool.mu.Unlock() @@ -590,7 +590,7 @@ func (pool *TxPool) Pending(enforceTips bool) (map[common.Address]types.Transact pending[addr] = txs } } - return pending, nil + return pending } // Locals retrieves the accounts currently considered local by the pool. diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index b309deefc21d..fc5d82c78494 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -272,10 +272,6 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) { trigger = true <-pool.requestReset(nil, nil) - _, err := pool.Pending(false) - if err != nil { - t.Fatalf("Could not fetch pending transactions: %v", err) - } nonce = pool.Nonce(address) if nonce != 2 { t.Fatalf("Invalid nonce, want 2, got %d", nonce) diff --git a/eth/api_backend.go b/eth/api_backend.go index 087cd3cf1abe..ce8ddd05de6f 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -298,10 +298,7 @@ func (b *EthApiBackend) SendLendingTx(ctx context.Context, signedTx *types.Lendi } func (b *EthApiBackend) GetPoolTransactions() (types.Transactions, error) { - pending, err := b.eth.txPool.Pending(false) - if err != nil { - return nil, err - } + pending := b.eth.txPool.Pending(false) var txs types.Transactions for _, batch := range pending { txs = append(txs, batch...) diff --git a/eth/helper_test.go b/eth/helper_test.go index de175bbcee3d..0b793d09bc5a 100644 --- a/eth/helper_test.go +++ b/eth/helper_test.go @@ -111,7 +111,7 @@ func (p *testTxPool) AddRemotes(txs []*types.Transaction) []error { } // Pending returns all the transactions known to the pool -func (p *testTxPool) Pending(enforceTips bool) (map[common.Address]types.Transactions, error) { +func (p *testTxPool) Pending(enforceTips bool) map[common.Address]types.Transactions { p.lock.RLock() defer p.lock.RUnlock() @@ -123,7 +123,7 @@ func (p *testTxPool) Pending(enforceTips bool) (map[common.Address]types.Transac for _, batch := range batches { sort.Sort(types.TxByNonce(batch)) } - return batches, nil + return batches } func (p *testTxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription { diff --git a/eth/protocol.go b/eth/protocol.go index 3876133e830e..6cba0e1ebadd 100644 --- a/eth/protocol.go +++ b/eth/protocol.go @@ -108,7 +108,7 @@ type txPool interface { // Pending should return pending transactions. // The slice should be modifiable by the caller. - Pending(enforceTips bool) (map[common.Address]types.Transactions, error) + Pending(enforceTips bool) map[common.Address]types.Transactions // SubscribeNewTxsEvent should return an event subscription of // NewTxsEvent and send events to the given channel. diff --git a/eth/sync.go b/eth/sync.go index 76edb614fbdb..5a0ea512f55f 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -45,7 +45,7 @@ type txsync struct { // syncTransactions starts sending all currently pending transactions to the given peer. func (pm *ProtocolManager) syncTransactions(p *peer) { var txs types.Transactions - pending, _ := pm.txpool.Pending(false) + pending := pm.txpool.Pending(false) for _, batch := range pending { txs = append(txs, batch...) } diff --git a/miner/worker.go b/miner/worker.go index c076518a60d9..73aecd9fe772 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -679,11 +679,7 @@ func (w *worker) commitNewWork() { log.Error("[commitNewWork] fail to check if block is epoch switch block when fetching pending transactions", "BlockNum", header.Number, "Hash", header.Hash()) } if !isEpochSwitchBlock { - pending, err := w.eth.TxPool().Pending(true) - if err != nil { - log.Error("Failed to fetch pending transactions", "err", err) - return - } + pending := w.eth.TxPool().Pending(true) txs, specialTxs = types.NewTransactionsByPriceAndNonce(w.current.signer, pending, signers, feeCapacity) } } From a402cbf50e850a9d6ed6439ae7953f7dc02a32c4 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 12 Jun 2024 14:59:36 +0800 Subject: [PATCH 122/479] core/types: add MarshalBinary, UnmarshalBinary for Receipt (#22806) --- core/types/receipt.go | 95 +++++++++++++++++++++++++++++--------- core/types/receipt_test.go | 88 +++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 21 deletions(-) diff --git a/core/types/receipt.go b/core/types/receipt.go index ce6af8c2f68e..13d82b8e45a7 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -128,12 +128,29 @@ func (r *Receipt) EncodeRLP(w io.Writer) error { defer encodeBufferPool.Put(buf) buf.Reset() buf.WriteByte(r.Type) - if err := rlp.Encode(buf, data); err != nil { + if err := r.encodeTyped(data, buf); err != nil { return err } return rlp.Encode(w, buf.Bytes()) } +// encodeTyped writes the canonical encoding of a typed receipt to w. +func (r *Receipt) encodeTyped(data *receiptRLP, w *bytes.Buffer) error { + w.WriteByte(r.Type) + return rlp.Encode(w, data) +} + +// MarshalBinary returns the consensus encoding of the receipt. +func (r *Receipt) MarshalBinary() ([]byte, error) { + if r.Type == LegacyTxType { + return rlp.EncodeToBytes(r) + } + data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs} + var buf bytes.Buffer + err := r.encodeTyped(data, &buf) + return buf.Bytes(), err +} + // DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt // from an RLP stream. func (r *Receipt) DecodeRLP(s *rlp.Stream) error { @@ -172,6 +189,42 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error { } } +// UnmarshalBinary decodes the consensus encoding of receipts. +// It supports legacy RLP receipts and EIP-2718 typed receipts. +func (r *Receipt) UnmarshalBinary(b []byte) error { + if len(b) > 0 && b[0] > 0x7f { + // It's a legacy receipt decode the RLP + var data receiptRLP + err := rlp.DecodeBytes(b, &data) + if err != nil { + return err + } + r.Type = LegacyTxType + return r.setFromRLP(data) + } + // It's an EIP2718 typed transaction envelope. + return r.decodeTyped(b) +} + +// decodeTyped decodes a typed receipt from the canonical format. +func (r *Receipt) decodeTyped(b []byte) error { + if len(b) == 0 { + return errEmptyTypedReceipt + } + switch b[0] { + case DynamicFeeTxType, AccessListTxType: + var data receiptRLP + err := rlp.DecodeBytes(b[1:], &data) + if err != nil { + return err + } + r.Type = b[0] + return r.setFromRLP(data) + default: + return ErrTxTypeNotSupported + } +} + func (r *Receipt) setFromRLP(data receiptRLP) error { r.CumulativeGasUsed, r.Bloom, r.Logs = data.CumulativeGasUsed, data.Bloom, data.Logs return r.setStatus(data.PostStateOrStatus) @@ -279,11 +332,11 @@ func decodeStoredReceiptRLP(r *ReceiptForStorage, blob []byte) error { type Receipts []*Receipt // Len returns the number of receipts in this list. -func (r Receipts) Len() int { return len(r) } +func (rs Receipts) Len() int { return len(rs) } // GetRlp returns the RLP encoding of one receipt from the list. -func (r Receipts) GetRlp(i int) []byte { - bytes, err := rlp.EncodeToBytes(r[i]) +func (rs Receipts) GetRlp(i int) []byte { + bytes, err := rlp.EncodeToBytes(rs[i]) if err != nil { panic(err) } @@ -292,42 +345,42 @@ func (r Receipts) GetRlp(i int) []byte { // DeriveFields fills the receipts with their computed fields based on consensus // data and contextual infos like containing block and transactions. -func (r Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, txs Transactions) error { +func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, txs Transactions) error { signer := MakeSigner(config, new(big.Int).SetUint64(number)) logIndex := uint(0) - if len(txs) != len(r) { + if len(txs) != len(rs) { return errors.New("transaction and receipt count mismatch") } - for i := 0; i < len(r); i++ { + for i := 0; i < len(rs); i++ { // The transaction type and hash can be retrieved from the transaction itself - r[i].Type = txs[i].Type() - r[i].TxHash = txs[i].Hash() + rs[i].Type = txs[i].Type() + rs[i].TxHash = txs[i].Hash() // block location fields - r[i].BlockHash = hash - r[i].BlockNumber = new(big.Int).SetUint64(number) - r[i].TransactionIndex = uint(i) + rs[i].BlockHash = hash + rs[i].BlockNumber = new(big.Int).SetUint64(number) + rs[i].TransactionIndex = uint(i) // The contract address can be derived from the transaction itself if txs[i].To() == nil { // Deriving the signer is expensive, only do if it's actually needed from, _ := Sender(signer, txs[i]) - r[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce()) + rs[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce()) } // The used gas can be calculated based on previous r if i == 0 { - r[i].GasUsed = r[i].CumulativeGasUsed + rs[i].GasUsed = rs[i].CumulativeGasUsed } else { - r[i].GasUsed = r[i].CumulativeGasUsed - r[i-1].CumulativeGasUsed + rs[i].GasUsed = rs[i].CumulativeGasUsed - rs[i-1].CumulativeGasUsed } // The derived log fields can simply be set from the block and transaction - for j := 0; j < len(r[i].Logs); j++ { - r[i].Logs[j].BlockNumber = number - r[i].Logs[j].BlockHash = hash - r[i].Logs[j].TxHash = r[i].TxHash - r[i].Logs[j].TxIndex = uint(i) - r[i].Logs[j].Index = logIndex + for j := 0; j < len(rs[i].Logs); j++ { + rs[i].Logs[j].BlockNumber = number + rs[i].Logs[j].BlockHash = hash + rs[i].Logs[j].TxHash = rs[i].TxHash + rs[i].Logs[j].TxIndex = uint(i) + rs[i].Logs[j].Index = logIndex logIndex++ } } diff --git a/core/types/receipt_test.go b/core/types/receipt_test.go index ac6b6b89ea08..92709b9268f6 100644 --- a/core/types/receipt_test.go +++ b/core/types/receipt_test.go @@ -29,6 +29,59 @@ import ( "github.com/XinFinOrg/XDPoSChain/rlp" ) +var ( + legacyReceipt = &Receipt{ + Status: ReceiptStatusFailed, + CumulativeGasUsed: 1, + Logs: []*Log{ + { + Address: common.BytesToAddress([]byte{0x11}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + Data: []byte{0x01, 0x00, 0xff}, + }, + { + Address: common.BytesToAddress([]byte{0x01, 0x11}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + Data: []byte{0x01, 0x00, 0xff}, + }, + }, + } + accessListReceipt = &Receipt{ + Status: ReceiptStatusFailed, + CumulativeGasUsed: 1, + Logs: []*Log{ + { + Address: common.BytesToAddress([]byte{0x11}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + Data: []byte{0x01, 0x00, 0xff}, + }, + { + Address: common.BytesToAddress([]byte{0x01, 0x11}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + Data: []byte{0x01, 0x00, 0xff}, + }, + }, + Type: AccessListTxType, + } + eip1559Receipt = &Receipt{ + Status: ReceiptStatusFailed, + CumulativeGasUsed: 1, + Logs: []*Log{ + { + Address: common.BytesToAddress([]byte{0x11}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + Data: []byte{0x01, 0x00, 0xff}, + }, + { + Address: common.BytesToAddress([]byte{0x01, 0x11}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + Data: []byte{0x01, 0x00, 0xff}, + }, + }, + Type: DynamicFeeTxType, + } +) + func TestDecodeEmptyTypedReceipt(t *testing.T) { input := []byte{0x80} var r Receipt @@ -315,3 +368,38 @@ func TestTypedReceiptEncodingDecoding(t *testing.T) { check(bundle) } } + +func TestReceiptUnmarshalBinary(t *testing.T) { + // Legacy Receipt + legacyBinary := common.FromHex("f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff") + gotLegacyReceipt := new(Receipt) + if err := gotLegacyReceipt.UnmarshalBinary(legacyBinary); err != nil { + t.Fatalf("unmarshal binary error: %v", err) + } + legacyReceipt.Bloom = CreateBloom(Receipts{legacyReceipt}) + if !reflect.DeepEqual(gotLegacyReceipt, legacyReceipt) { + t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", gotLegacyReceipt, legacyReceipt) + } + + // 2930 Receipt + accessListBinary := common.FromHex("01f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff") + gotAccessListReceipt := new(Receipt) + if err := gotAccessListReceipt.UnmarshalBinary(accessListBinary); err != nil { + t.Fatalf("unmarshal binary error: %v", err) + } + accessListReceipt.Bloom = CreateBloom(Receipts{accessListReceipt}) + if !reflect.DeepEqual(gotAccessListReceipt, accessListReceipt) { + t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", gotAccessListReceipt, accessListReceipt) + } + + // 1559 Receipt + eip1559RctBinary := common.FromHex("02f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff") + got1559Receipt := new(Receipt) + if err := got1559Receipt.UnmarshalBinary(eip1559RctBinary); err != nil { + t.Fatalf("unmarshal binary error: %v", err) + } + eip1559Receipt.Bloom = CreateBloom(Receipts{eip1559Receipt}) + if !reflect.DeepEqual(got1559Receipt, eip1559Receipt) { + t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", got1559Receipt, eip1559Receipt) + } +} From eec8da371c1815dfe0ffc0aa5b81ec34bee1ce63 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 30 May 2024 18:14:57 +0800 Subject: [PATCH 123/479] accounts/abi/bind: refactor transact method (#23719) --- accounts/abi/bind/base.go | 212 ++++++++++++++++++++------------- accounts/abi/bind/base_test.go | 177 +++++++++++++++++++++++++++ 2 files changed, 308 insertions(+), 81 deletions(-) create mode 100644 accounts/abi/bind/base_test.go diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index 63e56fdfbab3..076fd8519842 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -188,108 +188,158 @@ func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error) return c.transact(opts, &c.address, nil) } -// transact executes an actual transaction invocation, first deriving any missing -// authorization fields, and then scheduling the transaction for execution. -func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) { - var err error - - // Ensure a valid value field and resolve the account nonce +func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Address, input []byte, head *types.Header) (*types.Transaction, error) { + // Normalize value value := opts.Value if value == nil { value = new(big.Int) } - var nonce uint64 - if opts.Nonce == nil { - nonce, err = c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From) + // Estimate TipCap + gasTipCap := opts.GasTipCap + if gasTipCap == nil { + tip, err := c.transactor.SuggestGasTipCap(ensureContext(opts.Context)) if err != nil { - return nil, fmt.Errorf("failed to retrieve account nonce: %v", err) + return nil, err } - } else { - nonce = opts.Nonce.Uint64() + gasTipCap = tip } - // Figure out reasonable gas price values - if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) { - return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") + // Estimate FeeCap + gasFeeCap := opts.GasFeeCap + if gasFeeCap == nil { + gasFeeCap = new(big.Int).Add( + gasTipCap, + new(big.Int).Mul(head.BaseFee, big.NewInt(2)), + ) } - head, err := c.transactor.HeaderByNumber(opts.Context, nil) + if gasFeeCap.Cmp(gasTipCap) < 0 { + return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", gasFeeCap, gasTipCap) + } + // Estimate GasLimit + gasLimit := opts.GasLimit + if opts.GasLimit == 0 { + var err error + gasLimit, err = c.estimateGasLimit(opts, contract, input, nil, gasTipCap, gasFeeCap, value) + if err != nil { + return nil, err + } + } + // create the transaction + nonce, err := c.getNonce(opts) if err != nil { return nil, err } - if head.BaseFee != nil && opts.GasPrice == nil { - if opts.GasTipCap == nil { - tip, err := c.transactor.SuggestGasTipCap(opts.Context) - if err != nil { - return nil, err - } - opts.GasTipCap = tip - } - if opts.GasFeeCap == nil { - gasFeeCap := new(big.Int).Add( - opts.GasTipCap, - new(big.Int).Mul(head.BaseFee, big.NewInt(2)), - ) - opts.GasFeeCap = gasFeeCap - } - if opts.GasFeeCap.Cmp(opts.GasTipCap) < 0 { - return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", opts.GasFeeCap, opts.GasTipCap) - } - } else { - if opts.GasFeeCap != nil || opts.GasTipCap != nil { - return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") - } - if opts.GasPrice == nil { - price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context)) - if err != nil { - return nil, err - } - opts.GasPrice = price + baseTx := &types.DynamicFeeTx{ + To: contract, + Nonce: nonce, + GasFeeCap: gasFeeCap, + GasTipCap: gasTipCap, + Gas: gasLimit, + Value: value, + Data: input, + } + return types.NewTx(baseTx), nil +} + +func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) { + if opts.GasFeeCap != nil || opts.GasTipCap != nil { + return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but EIP-1559 is not active yet") + } + // Normalize value + value := opts.Value + if value == nil { + value = new(big.Int) + } + // Estimate GasPrice + gasPrice := opts.GasPrice + if gasPrice == nil { + price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context)) + if err != nil { + return nil, err } + gasPrice = price } + // Estimate GasLimit gasLimit := opts.GasLimit - if gasLimit == 0 { - // Gas estimation cannot succeed without code for method invocations - if contract != nil { - if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil { - return nil, err - } else if len(code) == 0 { - return nil, ErrNoCode - } - } - // If the contract surely has code (or code is not needed), estimate the transaction - msg := XDPoSChain.CallMsg{From: opts.From, To: contract, GasPrice: opts.GasPrice, GasTipCap: opts.GasTipCap, GasFeeCap: opts.GasFeeCap, Value: value, Data: input} - gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg) + if opts.GasLimit == 0 { + var err error + gasLimit, err = c.estimateGasLimit(opts, contract, input, gasPrice, nil, nil, value) if err != nil { - return nil, fmt.Errorf("failed to estimate gas needed: %v", err) + return nil, err } } - // Create the transaction, sign it and schedule it for execution - var rawTx *types.Transaction - if opts.GasFeeCap == nil { - baseTx := &types.LegacyTx{ - Nonce: nonce, - GasPrice: opts.GasPrice, - Gas: gasLimit, - Value: value, - Data: input, - } - if contract != nil { - baseTx.To = &c.address + // create the transaction + nonce, err := c.getNonce(opts) + if err != nil { + return nil, err + } + baseTx := &types.LegacyTx{ + To: contract, + Nonce: nonce, + GasPrice: gasPrice, + Gas: gasLimit, + Value: value, + Data: input, + } + return types.NewTx(baseTx), nil +} + +func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Address, input []byte, gasPrice, gasTipCap, gasFeeCap, value *big.Int) (uint64, error) { + if contract != nil { + // Gas estimation cannot succeed without code for method invocations. + if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil { + return 0, err + } else if len(code) == 0 { + return 0, ErrNoCode } - rawTx = types.NewTx(baseTx) + } + msg := XDPoSChain.CallMsg{ + From: opts.From, + To: contract, + GasPrice: gasPrice, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Value: value, + Data: input, + } + return c.transactor.EstimateGas(ensureContext(opts.Context), msg) +} + +func (c *BoundContract) getNonce(opts *TransactOpts) (uint64, error) { + if opts.Nonce == nil { + return c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From) } else { - baseTx := &types.DynamicFeeTx{ - Nonce: nonce, - GasFeeCap: opts.GasFeeCap, - GasTipCap: opts.GasTipCap, - Gas: gasLimit, - Value: value, - Data: input, - } - if contract != nil { - baseTx.To = &c.address + return opts.Nonce.Uint64(), nil + } +} + +// transact executes an actual transaction invocation, first deriving any missing +// authorization fields, and then scheduling the transaction for execution. +func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) { + if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) { + return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") + } + // Create the transaction + var ( + rawTx *types.Transaction + err error + ) + if opts.GasPrice != nil { + rawTx, err = c.createLegacyTx(opts, contract, input) + } else { + // Only query for basefee if gasPrice not specified + if head, errHead := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil); err != nil { + return nil, errHead + } else if head.BaseFee != nil { + rawTx, err = c.createDynamicTx(opts, contract, input, head) + } else { + // Chain is not London ready -> use legacy transaction + rawTx, err = c.createLegacyTx(opts, contract, input) } - rawTx = types.NewTx(baseTx) } + if err != nil { + return nil, err + } + // Sign the transaction and schedule it for execution if opts.Signer == nil { return nil, errors.New("no signer to authorize the transaction with") } diff --git a/accounts/abi/bind/base_test.go b/accounts/abi/bind/base_test.go new file mode 100644 index 000000000000..037f1d24e8db --- /dev/null +++ b/accounts/abi/bind/base_test.go @@ -0,0 +1,177 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bind_test + +import ( + "context" + "math/big" + "testing" + + "github.com/XinFinOrg/XDPoSChain" + "github.com/XinFinOrg/XDPoSChain/accounts/abi" + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/stretchr/testify/assert" +) + +func mockSign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { return tx, nil } + +type mockTransactor struct { + baseFee *big.Int + gasTipCap *big.Int + gasPrice *big.Int + suggestGasTipCapCalled bool + suggestGasPriceCalled bool +} + +func (mt *mockTransactor) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { + return &types.Header{BaseFee: mt.baseFee}, nil +} + +func (mt *mockTransactor) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { + return []byte{1}, nil +} + +func (mt *mockTransactor) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { + return 0, nil +} + +func (mt *mockTransactor) SuggestGasPrice(ctx context.Context) (*big.Int, error) { + mt.suggestGasPriceCalled = true + return mt.gasPrice, nil +} + +func (mt *mockTransactor) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { + mt.suggestGasTipCapCalled = true + return mt.gasTipCap, nil +} + +func (mt *mockTransactor) EstimateGas(ctx context.Context, call XDPoSChain.CallMsg) (gas uint64, err error) { + return 0, nil +} + +func (mt *mockTransactor) SendTransaction(ctx context.Context, tx *types.Transaction) error { + return nil +} + +type mockCaller struct { + codeAtBlockNumber *big.Int + callContractBlockNumber *big.Int + pendingCodeAtCalled bool + pendingCallContractCalled bool +} + +func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { + mc.codeAtBlockNumber = blockNumber + return []byte{1, 2, 3}, nil +} + +func (mc *mockCaller) CallContract(ctx context.Context, call XDPoSChain.CallMsg, blockNumber *big.Int) ([]byte, error) { + mc.callContractBlockNumber = blockNumber + return nil, nil +} + +func (mc *mockCaller) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) { + mc.pendingCodeAtCalled = true + return nil, nil +} + +func (mc *mockCaller) PendingCallContract(ctx context.Context, call XDPoSChain.CallMsg) ([]byte, error) { + mc.pendingCallContractCalled = true + return nil, nil +} +func TestPassingBlockNumber(t *testing.T) { + + mc := &mockCaller{} + + bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{ + Methods: map[string]abi.Method{ + "something": { + Name: "something", + Outputs: abi.Arguments{}, + }, + }, + }, mc, nil, nil) + + bc.Call(&bind.CallOpts{}, nil, "something") + + bc.Call(&bind.CallOpts{}, nil, "something") + + if mc.callContractBlockNumber != nil { + t.Fatalf("CallContract() was passed a block number when it should not have been") + } + + if mc.codeAtBlockNumber != nil { + t.Fatalf("CodeAt() was passed a block number when it should not have been") + } + + bc.Call(&bind.CallOpts{Pending: true}, nil, "something") + + if !mc.pendingCallContractCalled { + t.Fatalf("CallContract() was not passed the block number") + } + + if !mc.pendingCodeAtCalled { + t.Fatalf("CodeAt() was not passed the block number") + } +} + +func TestTransactGasFee(t *testing.T) { + assert := assert.New(t) + + // GasTipCap and GasFeeCap + // When opts.GasTipCap and opts.GasFeeCap are nil + mt := &mockTransactor{baseFee: big.NewInt(100), gasTipCap: big.NewInt(5)} + bc := bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil) + opts := &bind.TransactOpts{Signer: mockSign} + tx, err := bc.Transact(opts, "") + assert.Nil(err) + assert.Equal(big.NewInt(5), tx.GasTipCap()) + assert.Equal(big.NewInt(205), tx.GasFeeCap()) + assert.Nil(opts.GasTipCap) + assert.Nil(opts.GasFeeCap) + assert.True(mt.suggestGasTipCapCalled) + + // Second call to Transact should use latest suggested GasTipCap + mt.gasTipCap = big.NewInt(6) + mt.suggestGasTipCapCalled = false + tx, err = bc.Transact(opts, "") + assert.Nil(err) + assert.Equal(big.NewInt(6), tx.GasTipCap()) + assert.Equal(big.NewInt(206), tx.GasFeeCap()) + assert.True(mt.suggestGasTipCapCalled) + + // GasPrice + // When opts.GasPrice is nil + mt = &mockTransactor{gasPrice: big.NewInt(5)} + bc = bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil) + opts = &bind.TransactOpts{Signer: mockSign} + tx, err = bc.Transact(opts, "") + assert.Nil(err) + assert.Equal(big.NewInt(5), tx.GasPrice()) + assert.Nil(opts.GasPrice) + assert.True(mt.suggestGasPriceCalled) + + // Second call to Transact should use latest suggested GasPrice + mt.gasPrice = big.NewInt(6) + mt.suggestGasPriceCalled = false + tx, err = bc.Transact(opts, "") + assert.Nil(err) + assert.Equal(big.NewInt(6), tx.GasPrice()) + assert.True(mt.suggestGasPriceCalled) +} From 2d5f9d9e62d1059fb8a679b17812852d7cfad49b Mon Sep 17 00:00:00 2001 From: KibGzr Date: Thu, 21 Oct 2021 16:40:35 +0700 Subject: [PATCH 124/479] accounts/abi/bind: fix error handling in baseFee query (#23781) This fixes a panic that occurs when HeaderByNumber() returns an error. --- accounts/abi/bind/base.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index 076fd8519842..ebdd9b6cc328 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -327,7 +327,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i rawTx, err = c.createLegacyTx(opts, contract, input) } else { // Only query for basefee if gasPrice not specified - if head, errHead := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil); err != nil { + if head, errHead := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil); errHead != nil { return nil, errHead } else if head.BaseFee != nil { rawTx, err = c.createDynamicTx(opts, contract, input, head) From 66be9b665becd14ad4f0527ba71a9aa9a09d7cac Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 31 May 2024 10:39:30 +0800 Subject: [PATCH 125/479] accounts/abi/bin/backends: return basefee in suggestGasPrice's (#23838) --- accounts/abi/bind/backends/simulated.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 3ee19bb96148..c5e11b06ad78 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -313,9 +313,9 @@ func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Ad } // SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated -// chain doens't have miners, we just return a gas price of 1 for any call. +// chain doesn't have miners, we just return a gas price of 1 for any call. func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { - return big.NewInt(1), nil + return b.pendingBlock.Header().BaseFee, nil } // SuggestGasTipCap implements ContractTransactor.SuggestGasTipCap. Since the simulated From d2a10ef8ecb783cb2618ae6d11f5333dc9bff411 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 1 Nov 2021 10:01:22 +0100 Subject: [PATCH 126/479] accounts/abi/bind/backends: make suggestGasPrice compatible with non-1559 chains (#23840) --- accounts/abi/bind/backends/simulated.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index c5e11b06ad78..48bc5fb5c9a6 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -315,7 +315,10 @@ func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Ad // SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated // chain doesn't have miners, we just return a gas price of 1 for any call. func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { - return b.pendingBlock.Header().BaseFee, nil + if b.pendingBlock.Header().BaseFee != nil { + return b.pendingBlock.Header().BaseFee, nil + } + return big.NewInt(1), nil } // SuggestGasTipCap implements ContractTransactor.SuggestGasTipCap. Since the simulated From 81dff92ced7349fc556190eb95a6693256f9499f Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 2 Nov 2021 18:32:23 +0100 Subject: [PATCH 127/479] core: more efficient nonce-update in txpool (#22231) * Adjust pending nonce update operation Benchmark the speed of transaction insertion under multiple accounts core: fix rebase issues + docstring core: make benchmark test use sync:ed method * core: address review comments * core: add memreport to benchmark Co-authored-by: WeiLoy --- core/tx_noncer.go | 8 ++++++++ core/tx_pool.go | 12 +++++++----- core/tx_pool_test.go | 21 +++++++++++++++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/core/tx_noncer.go b/core/tx_noncer.go index cbadc39354a3..60779dd3121e 100644 --- a/core/tx_noncer.go +++ b/core/tx_noncer.go @@ -77,3 +77,11 @@ func (txn *txNoncer) setIfLower(addr common.Address, nonce uint64) { } txn.nonces[addr] = nonce } + +// setAll sets the nonces for all accounts to the given map. +func (txn *txNoncer) setAll(all map[common.Address]uint64) { + txn.lock.Lock() + defer txn.lock.Unlock() + + txn.nonces = all +} diff --git a/core/tx_pool.go b/core/tx_pool.go index a1a9655e46b3..41e995a55a33 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -1332,16 +1332,18 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt pendingBaseFee := misc.CalcBaseFee(pool.chainconfig, reset.newHead) pool.priced.SetBaseFee(pendingBaseFee) } + // Update all accounts to the latest known pending nonce + nonces := make(map[common.Address]uint64, len(pool.pending)) + for addr, list := range pool.pending { + highestPending := list.LastElement() + nonces[addr] = highestPending.Nonce() + 1 + } + pool.pendingNonces.setAll(nonces) } // Ensure pool.queue and pool.pending sizes stay within the configured limits. pool.truncatePending() pool.truncateQueue() - // Update all accounts to the latest known pending nonce - for addr, list := range pool.pending { - highestPending := list.LastElement() - pool.pendingNonces.set(addr, highestPending.Nonce()+1) - } dropBetweenReorgHistogram.Update(int64(pool.changesSinceReorg)) pool.changesSinceReorg = 0 // Reset change counter pool.mu.Unlock() diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index fc5d82c78494..cae0b55c8eb6 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -2575,3 +2575,24 @@ func BenchmarkInsertRemoteWithAllLocals(b *testing.B) { pool.Stop() } } + +// Benchmarks the speed of batch transaction insertion in case of multiple accounts. +func BenchmarkPoolMultiAccountBatchInsert(b *testing.B) { + // Generate a batch of transactions to enqueue into the pool + pool, _ := setupTxPool() + defer pool.Stop() + b.ReportAllocs() + batches := make(types.Transactions, b.N) + for i := 0; i < b.N; i++ { + key, _ := crypto.GenerateKey() + account := crypto.PubkeyToAddress(key.PublicKey) + pool.currentState.AddBalance(account, big.NewInt(1000000)) + tx := transaction(uint64(0), 100000, key) + batches[i] = tx + } + // Benchmark importing the transactions into the queue + b.ResetTimer() + for _, tx := range batches { + pool.AddRemotesSync([]*types.Transaction{tx}) + } +} From 6feb71e5c8aa779ef32d66a543f89216bbe95648 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 31 May 2024 11:25:25 +0800 Subject: [PATCH 128/479] core: check effective tip in txpool pricelimit validation (#23855) --- core/tx_pool.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index 41e995a55a33..c353951e745e 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -673,8 +673,9 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { if err != nil { return ErrInvalidSender } - // Drop non-local transactions under our own minimal accepted gas price or tip - if !local && tx.GasTipCapIntCmp(pool.gasPrice) < 0 { + // Drop non-local transactions under our own minimal accepted gas price or tip. + pendingBaseFee := pool.priced.urgent.baseFee + if !local && tx.EffectiveGasTipIntCmp(pool.gasPrice, pendingBaseFee) < 0 { if !tx.IsSpecialTransaction() || (pool.IsSigner != nil && !pool.IsSigner(from)) { return ErrUnderpriced } From bdae20085afe361284f46c00fcda9a311410e241 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 31 May 2024 11:31:45 +0800 Subject: [PATCH 129/479] core: ignore basefee when comparing with pool gasprice in txpool (#24080) --- core/tx_pool.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index c353951e745e..41e995a55a33 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -673,9 +673,8 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { if err != nil { return ErrInvalidSender } - // Drop non-local transactions under our own minimal accepted gas price or tip. - pendingBaseFee := pool.priced.urgent.baseFee - if !local && tx.EffectiveGasTipIntCmp(pool.gasPrice, pendingBaseFee) < 0 { + // Drop non-local transactions under our own minimal accepted gas price or tip + if !local && tx.GasTipCapIntCmp(pool.gasPrice) < 0 { if !tx.IsSpecialTransaction() || (pool.IsSigner != nil && !pool.IsSigner(from)) { return ErrUnderpriced } From 8eaa9e2c3ba5a9e67591cb776024000575869028 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 11 Jun 2024 13:19:45 +0800 Subject: [PATCH 130/479] core/types: document JSON field name equivalents of DynamicFeeTx (#24143) --- core/types/dynamic_fee_tx.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/types/dynamic_fee_tx.go b/core/types/dynamic_fee_tx.go index b559f1ae6652..4c2386e02ef7 100644 --- a/core/types/dynamic_fee_tx.go +++ b/core/types/dynamic_fee_tx.go @@ -25,8 +25,8 @@ import ( type DynamicFeeTx struct { ChainID *big.Int Nonce uint64 - GasTipCap *big.Int - GasFeeCap *big.Int + GasTipCap *big.Int // a.k.a. maxPriorityFeePerGas + GasFeeCap *big.Int // a.k.a. maxFeePerGas Gas uint64 To *common.Address `rlp:"nil"` // nil means contract creation Value *big.Int From 588847ccea9b16e255cd9788298a41415ef4a0a8 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 13 Jun 2024 16:41:32 +0800 Subject: [PATCH 131/479] internal/ethapi: use same receiver names (#24252) --- internal/ethapi/transaction_args.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 4b92a964ea29..662381c544a4 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -55,20 +55,20 @@ type TransactionArgs struct { } // from retrieves the transaction sender address. -func (arg *TransactionArgs) from() common.Address { - if arg.From == nil { +func (args *TransactionArgs) from() common.Address { + if args.From == nil { return common.Address{} } - return *arg.From + return *args.From } // data retrieves the transaction calldata. Input field is preferred. -func (arg *TransactionArgs) data() []byte { - if arg.Input != nil { - return *arg.Input +func (args *TransactionArgs) data() []byte { + if args.Input != nil { + return *args.Input } - if arg.Data != nil { - return *arg.Data + if args.Data != nil { + return *args.Data } return nil } From d1dc9e383cd53c1761dccefb89a9d13435628b9b Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 31 May 2024 11:35:19 +0800 Subject: [PATCH 132/479] core/statedb: always clear out access list when setting a new one (#24515) --- core/state/statedb.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index aa2495cf401e..fc8de74793fa 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -643,7 +643,6 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { func (s *StateDB) Prepare(thash common.Hash, ti int) { s.thash = thash s.txIndex = ti - s.accessList = newAccessList() } // DeleteSuicides flags the suicided objects for deletion so that it @@ -728,6 +727,9 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) // // This method should only be called if Yolov3/Berlin/2929+2930 is applicable at the current number. func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) { + // Clear out any leftover from previous executions + s.accessList = newAccessList() + s.AddAddressToAccessList(sender) if dst != nil { s.AddAddressToAccessList(*dst) From 7cb33cc57dc2254b953171efc8f4c78d8e6fbe87 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 12 Jun 2024 16:30:30 +0800 Subject: [PATCH 133/479] core/types: improve error for too short transaction / receipt encoding (#24256) --- core/types/receipt.go | 24 +++++------------------- core/types/receipt_test.go | 2 +- core/types/transaction.go | 9 +++------ core/types/transaction_test.go | 2 +- 4 files changed, 10 insertions(+), 27 deletions(-) diff --git a/core/types/receipt.go b/core/types/receipt.go index 13d82b8e45a7..b4c22a297218 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -38,8 +38,7 @@ var ( receiptStatusSuccessfulRLP = []byte{0x01} ) -// This error is returned when a typed receipt is decoded, but the string is empty. -var errEmptyTypedReceipt = errors.New("empty typed receipt bytes") +var errShortTypedReceipt = errors.New("typed receipt too short") const ( // ReceiptStatusFailed is the status code of a transaction if execution failed. @@ -166,26 +165,13 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error { } r.Type = LegacyTxType return r.setFromRLP(dec) - case kind == rlp.String: + default: // It's an EIP-2718 typed tx receipt. b, err := s.Bytes() if err != nil { return err } - if len(b) == 0 { - return errEmptyTypedReceipt - } - r.Type = b[0] - if r.Type == AccessListTxType || r.Type == DynamicFeeTxType { - var dec receiptRLP - if err := rlp.DecodeBytes(b[1:], &dec); err != nil { - return err - } - return r.setFromRLP(dec) - } - return ErrTxTypeNotSupported - default: - return rlp.ErrExpectedList + return r.decodeTyped(b) } } @@ -208,8 +194,8 @@ func (r *Receipt) UnmarshalBinary(b []byte) error { // decodeTyped decodes a typed receipt from the canonical format. func (r *Receipt) decodeTyped(b []byte) error { - if len(b) == 0 { - return errEmptyTypedReceipt + if len(b) <= 1 { + return errShortTypedReceipt } switch b[0] { case DynamicFeeTxType, AccessListTxType: diff --git a/core/types/receipt_test.go b/core/types/receipt_test.go index 92709b9268f6..a71648b17fa0 100644 --- a/core/types/receipt_test.go +++ b/core/types/receipt_test.go @@ -86,7 +86,7 @@ func TestDecodeEmptyTypedReceipt(t *testing.T) { input := []byte{0x80} var r Receipt err := rlp.DecodeBytes(input, &r) - if err != errEmptyTypedReceipt { + if err != errShortTypedReceipt { t.Fatal("wrong error:", err) } } diff --git a/core/types/transaction.go b/core/types/transaction.go index 299f763620f4..7d4c12959dda 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -43,7 +43,6 @@ var ( errInvalidYParity = errors.New("'yParity' field must be 0 or 1") errVYParityMismatch = errors.New("'v' and 'yParity' fields do not match") errVYParityMissing = errors.New("missing 'yParity' or 'v' field in transaction") - errEmptyTypedTx = errors.New("empty typed transaction bytes") errNoSigner = errors.New("missing signing methods") ErrFeeCapTooLow = errors.New("fee cap less than base fee") @@ -149,7 +148,7 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error { tx.setDecoded(&inner, int(rlp.ListSize(size))) } return err - case kind == rlp.String: + default: // It's an EIP-2718 typed TX envelope. var b []byte if b, err = s.Bytes(); err != nil { @@ -160,8 +159,6 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error { tx.setDecoded(inner, len(b)) } return err - default: - return rlp.ErrExpectedList } } @@ -189,8 +186,8 @@ func (tx *Transaction) UnmarshalBinary(b []byte) error { // decodeTyped decodes a typed transaction from the canonical format. func (tx *Transaction) decodeTyped(b []byte) (TxData, error) { - if len(b) == 0 { - return nil, errEmptyTypedTx + if len(b) <= 1 { + return nil, errShortTypedTx } switch b[0] { case AccessListTxType: diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index b5b84be08ce9..e43e55f79bec 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -76,7 +76,7 @@ func TestDecodeEmptyTypedTx(t *testing.T) { input := []byte{0x80} var tx Transaction err := rlp.DecodeBytes(input, &tx) - if err != errEmptyTypedTx { + if err != errShortTypedTx { t.Fatal("wrong error:", err) } } From c8c43adac0ed8fe4afdc141a1b4bf48d20a1addc Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 20 Jun 2024 09:46:08 +0800 Subject: [PATCH 134/479] graphql: fee history fields (#24452) --- eth/gasprice/feehistory.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index 509d9bb5b673..804897cd059b 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -117,7 +117,7 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { reward, _ := tx.EffectiveGasTip(bf.block.BaseFee()) sorter[i] = txGasAndReward{gasUsed: bf.receipts[i].GasUsed, reward: reward} } - sort.Sort(sorter) + sort.Stable(sorter) var txIndex int sumGasUsed := sorter[0].gasUsed From 59bc3bfd09feab90b6c61eb6e38182cae8d3eae0 Mon Sep 17 00:00:00 2001 From: Emmanuel T Odeke Date: Fri, 22 Apr 2022 00:30:15 +0100 Subject: [PATCH 135/479] eth/tracers/logger: remove unnecessary comparisons in accessList.equal (#24663) This change removes extraneous/unnecessary checks for equality when comparing 2 accessList values A and B. Given that we validate that their lengths of A and B are equal, if so and if every element in A is in B, reflexively every element in B is already in A. If that weren't the case and an element g existed in A but not in B, that would mean that there is an extra element and hence a mathematical contradiction. Fixes #24658 --- core/vm/access_list_tracer.go | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/core/vm/access_list_tracer.go b/core/vm/access_list_tracer.go index 97dd59fac8ce..db7d1022b09b 100644 --- a/core/vm/access_list_tracer.go +++ b/core/vm/access_list_tracer.go @@ -61,16 +61,14 @@ func (al accessList) equal(other accessList) bool { if len(al) != len(other) { return false } + // Given that len(al) == len(other), we only need to check that + // all the items from al are in other. for addr := range al { if _, ok := other[addr]; !ok { return false } } - for addr := range other { - if _, ok := al[addr]; !ok { - return false - } - } + // Accounts match, cross reference the storage slots too for addr, slots := range al { otherslots := other[addr] @@ -78,16 +76,13 @@ func (al accessList) equal(other accessList) bool { if len(slots) != len(otherslots) { return false } + // Given that len(slots) == len(otherslots), we only need to check that + // all the items from slots are in otherslots. for hash := range slots { if _, ok := otherslots[hash]; !ok { return false } } - for hash := range otherslots { - if _, ok := slots[hash]; !ok { - return false - } - } } return true } From dc94ad52f6f1693803aa26da6f5b6d19adba3e86 Mon Sep 17 00:00:00 2001 From: zhaochonghe <41711151+zhaochonghe@users.noreply.github.com> Date: Thu, 19 May 2022 15:25:22 +0800 Subject: [PATCH 136/479] core: fix the order of address in queue (#24908) reverse the order of address in queue --- core/tx_pool.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index 41e995a55a33..ea9bfaf6ed77 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -1635,7 +1635,7 @@ func (pool *TxPool) truncateQueue() { addresses = append(addresses, addressByHeartbeat{addr, pool.beats[addr]}) } } - sort.Sort(addresses) + sort.Sort(sort.Reverse(addresses)) // Drop transactions until the total is below the limit or only locals remain for drop := queued - pool.config.GlobalQueue; drop > 0 && len(addresses) > 0; { From 77e2ad90cfb274bf8c470119d5fcdca799613bf3 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 25 Jun 2024 16:48:58 +0800 Subject: [PATCH 137/479] internal/ethapi: add comment explaining return of nil instead of error (#25097) --- internal/ethapi/api.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index b5e2f329dd65..a39e3282bb09 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2229,6 +2229,8 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context, func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) { tx, blockHash, blockNumber, index := core.GetTransaction(s.b.ChainDb(), hash) if tx == nil { + // When the transaction doesn't exist, the RPC method should return JSON null + // as per specification. return nil, nil } receipts, err := s.b.GetReceipts(ctx, blockHash) From 723781192826c8f5b63badc51bb4df8e869f2a2d Mon Sep 17 00:00:00 2001 From: aaronbuchwald Date: Wed, 22 Jun 2022 16:51:45 -0400 Subject: [PATCH 138/479] core: fix typo in txpool (#25149) Fix typo in txPool truncateQueue comment --- core/tx_pool.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index ea9bfaf6ed77..57738b64fd43 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -1618,7 +1618,7 @@ func (pool *TxPool) truncatePending() { pendingRateLimitMeter.Mark(int64(pendingBeforeCap - pending)) } -// truncateQueue drops the oldes transactions in the queue if the pool is above the global queue limit. +// truncateQueue drops the oldest transactions in the queue if the pool is above the global queue limit. func (pool *TxPool) truncateQueue() { queued := uint64(0) for _, list := range pool.queue { From fa83f32e5c26a37fd34a8c70cdfb4b4388602613 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 13 Jun 2024 17:20:22 +0800 Subject: [PATCH 139/479] internal/ethapi: error if tx args includes ChainId not match local (#25157) --- internal/ethapi/transaction_args.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 662381c544a4..34388ebf343d 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -165,9 +165,15 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { args.Gas = &estimated log.Trace("Estimate gas usage automatically", "gas", args.Gas) } - if args.ChainID == nil { - id := (*hexutil.Big)(b.ChainConfig().ChainId) - args.ChainID = id + // If chain id is provided, ensure it matches the local chain id. Otherwise, set the local + // chain id as the default. + want := b.ChainConfig().ChainId + if args.ChainID != nil { + if have := (*big.Int)(args.ChainID); have.Cmp(want) != 0 { + return fmt.Errorf("chainId does not match node's (have=%v, want=%v)", have, want) + } + } else { + args.ChainID = (*hexutil.Big)(want) } return nil } From 9535b3ade5eb09b3da7909a0a6738626ba43f3eb Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 31 May 2024 12:19:12 +0800 Subject: [PATCH 140/479] ethclient: add FeeHistory support (#25403) --- ethclient/ethclient.go | 32 ++++++++++++++++++++++++++++++++ interfaces.go | 9 +++++++++ internal/ethapi/api.go | 1 + 3 files changed, 42 insertions(+) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 0a83d6352980..ebaf68c64999 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -473,6 +473,38 @@ func (ec *Client) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { return (*big.Int)(&hex), nil } +type feeHistoryResultMarshaling struct { + OldestBlock *hexutil.Big `json:"oldestBlock"` + Reward [][]*hexutil.Big `json:"reward,omitempty"` + BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"` + GasUsedRatio []float64 `json:"gasUsedRatio"` +} + +// FeeHistory retrieves the fee market history. +func (ec *Client) FeeHistory(ctx context.Context, blockCount uint64, lastBlock *big.Int, rewardPercentiles []float64) (*ethereum.FeeHistory, error) { + var res feeHistoryResultMarshaling + if err := ec.c.CallContext(ctx, &res, "eth_feeHistory", hexutil.Uint(blockCount), toBlockNumArg(lastBlock), rewardPercentiles); err != nil { + return nil, err + } + reward := make([][]*big.Int, len(res.Reward)) + for i, r := range res.Reward { + reward[i] = make([]*big.Int, len(r)) + for j, r := range r { + reward[i][j] = (*big.Int)(r) + } + } + baseFee := make([]*big.Int, len(res.BaseFee)) + for i, b := range res.BaseFee { + baseFee[i] = (*big.Int)(b) + } + return ðereum.FeeHistory{ + OldestBlock: (*big.Int)(res.OldestBlock), + Reward: reward, + BaseFee: baseFee, + GasUsedRatio: res.GasUsedRatio, + }, nil +} + // EstimateGas tries to estimate the gas needed to execute a specific transaction based on // the current pending state of the backend blockchain. There is no guarantee that this is // the true gas limit requirement as other transactions may be added or removed by miners, diff --git a/interfaces.go b/interfaces.go index 233385fa52dc..7c7312679c50 100644 --- a/interfaces.go +++ b/interfaces.go @@ -183,6 +183,15 @@ type GasPricer interface { SuggestGasPrice(ctx context.Context) (*big.Int, error) } +// FeeHistory provides recent fee market data that consumers can use to determine +// a reasonable maxPriorityFeePerGas value. +type FeeHistory struct { + OldestBlock *big.Int // block coresponding to first response value + Reward [][]*big.Int // list every txs priority fee per block + BaseFee []*big.Int // list of each block's base fee + GasUsedRatio []float64 // ratio of gas used out of the total available limit +} + // A PendingStateReader provides access to the pending state, which is the result of all // known executable transactions which have not yet been included in the blockchain. It is // commonly used to display the result of ’unconfirmed’ actions (e.g. wallet value diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index a39e3282bb09..6b073ef46fd6 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -109,6 +109,7 @@ type feeHistoryResult struct { GasUsedRatio []float64 `json:"gasUsedRatio"` } +// FeeHistory returns the fee market history. func (s *PublicEthereumAPI) FeeHistory(ctx context.Context, blockCount hexutil.Uint, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*feeHistoryResult, error) { oldest, reward, baseFee, gasUsed, err := s.b.FeeHistory(ctx, int(blockCount), lastBlock, rewardPercentiles) if err != nil { From 754c6456af1d8a44a22ebd1e937a795c8762b0c9 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 31 May 2024 15:05:49 +0800 Subject: [PATCH 141/479] eth/gasprice/feehistory: support finalized block (#25442) --- eth/gasprice/feehistory.go | 76 +++++++++++++++++++++------------ eth/gasprice/feehistory_test.go | 1 + eth/gasprice/gasprice_test.go | 12 ++++++ 3 files changed, 62 insertions(+), 27 deletions(-) diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index 804897cd059b..f32cff1ccb9d 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -137,44 +137,66 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { // also returned if requested and available. // Note: an error is only returned if retrieving the head header has failed. If there are no // retrievable blocks in the specified range then zero block count is returned with no error. -func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlock rpc.BlockNumber, blocks int) (*types.Block, []*types.Receipt, uint64, int, error) { +func (oracle *Oracle) resolveBlockRange(ctx context.Context, reqEnd rpc.BlockNumber, blocks int) (*types.Block, []*types.Receipt, uint64, int, error) { var ( - headBlock rpc.BlockNumber + headBlock *types.Header pendingBlock *types.Block pendingReceipts types.Receipts + err error ) - // query either pending block or head header and set headBlock - if lastBlock == rpc.PendingBlockNumber { - if pendingBlock, pendingReceipts = oracle.backend.PendingBlockAndReceipts(); pendingBlock != nil { - lastBlock = rpc.BlockNumber(pendingBlock.NumberU64()) - headBlock = lastBlock - 1 - } else { - // pending block not supported by backend, process until latest block - lastBlock = rpc.LatestBlockNumber - blocks-- - if blocks == 0 { - return nil, nil, 0, 0, nil + + // Get the chain's current head. + if headBlock, err = oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber); err != nil { + return nil, nil, 0, 0, err + } + head := rpc.BlockNumber(headBlock.Number.Uint64()) + + // Fail if request block is beyond the chain's current head. + if head < reqEnd { + return nil, nil, 0, 0, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, reqEnd, head) + } + + // Resolve block tag. + if reqEnd < 0 { + var ( + resolved *types.Header + err error + ) + switch reqEnd { + case rpc.PendingBlockNumber: + if pendingBlock, pendingReceipts = oracle.backend.PendingBlockAndReceipts(); pendingBlock != nil { + resolved = pendingBlock.Header() + } else { + // Pending block not supported by backend, process only until latest block. + resolved = headBlock + + // Update total blocks to return to account for this. + blocks-- } + case rpc.LatestBlockNumber: + // Retrieved above. + resolved = headBlock + case rpc.CommittedBlockNumber: + resolved, err = oracle.backend.HeaderByNumber(ctx, rpc.CommittedBlockNumber) + case rpc.EarliestBlockNumber: + resolved, err = oracle.backend.HeaderByNumber(ctx, rpc.EarliestBlockNumber) } - } - if pendingBlock == nil { - // if pending block is not fetched then we retrieve the head header to get the head block number - if latestHeader, err := oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber); err == nil { - headBlock = rpc.BlockNumber(latestHeader.Number.Uint64()) - } else { + if resolved == nil || err != nil { return nil, nil, 0, 0, err } + // Absolute number resolved. + reqEnd = rpc.BlockNumber(resolved.Number.Uint64()) } - if lastBlock == rpc.LatestBlockNumber { - lastBlock = headBlock - } else if pendingBlock == nil && lastBlock > headBlock { - return nil, nil, 0, 0, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, lastBlock, headBlock) + + // If there are no blocks to return, short circuit. + if blocks == 0 { + return nil, nil, 0, 0, nil } - // ensure not trying to retrieve before genesis - if rpc.BlockNumber(blocks) > lastBlock+1 { - blocks = int(lastBlock + 1) + // Ensure not trying to retrieve before genesis. + if int(reqEnd+1) < blocks { + blocks = int(reqEnd + 1) } - return pendingBlock, pendingReceipts, uint64(lastBlock), blocks, nil + return pendingBlock, pendingReceipts, uint64(reqEnd), blocks, nil } // FeeHistory returns data relevant for fee estimation based on the specified range of blocks. diff --git a/eth/gasprice/feehistory_test.go b/eth/gasprice/feehistory_test.go index 2c1e3cefc59e..c44bde49c486 100644 --- a/eth/gasprice/feehistory_test.go +++ b/eth/gasprice/feehistory_test.go @@ -50,6 +50,7 @@ func TestFeeHistory(t *testing.T) { {false, 1000, 1000, 2, rpc.PendingBlockNumber, nil, 32, 1, nil}, {true, 1000, 1000, 2, rpc.PendingBlockNumber, nil, 32, 2, nil}, {true, 1000, 1000, 2, rpc.PendingBlockNumber, []float64{0, 10}, 32, 2, nil}, + {false, 1000, 1000, 2, rpc.CommittedBlockNumber, []float64{0, 10}, 32, 1, nil}, } for i, c := range cases { config := Config{ diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index c3831e9c7c3b..ded61a289905 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -45,6 +45,12 @@ func (b *testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber if number > testHead { return nil, nil } + if number == rpc.EarliestBlockNumber { + number = 0 + } + if number == rpc.CommittedBlockNumber { + return b.chain.CurrentBlock().Header(), nil + } if number == rpc.LatestBlockNumber { number = testHead } @@ -62,6 +68,12 @@ func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) if number > testHead { return nil, nil } + if number == rpc.EarliestBlockNumber { + number = 0 + } + if number == rpc.CommittedBlockNumber { + return b.chain.CurrentBlock(), nil + } if number == rpc.LatestBlockNumber { number = testHead } From c668279c4851cb49220706dac118d6dea1f8372c Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 10 Jun 2024 14:02:20 +0800 Subject: [PATCH 142/479] internal/ethapi: don't estimate gas if no limit provided in eth_createAccessList (#25467) --- internal/ethapi/api.go | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 6b073ef46fd6..5e761e8d147d 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2026,9 +2026,11 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH } owner := common.Address{} - // If the gas amount is not set, extract this as it will depend on access - // lists and we'll need to reestimate every time - nogas := args.Gas == nil + // If the gas amount is not set, default to RPC gas cap. + if args.Gas == nil { + tmp := hexutil.Uint64(b.RPCGasCap()) + args.Gas = &tmp + } // Ensure any missing fields are filled, extract the recipient and input data if err := args.setDefaults(ctx, b); err != nil { @@ -2053,15 +2055,6 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH accessList := prevTracer.AccessList() log.Trace("Creating access list", "input", accessList) - // If no gas amount was specified, each unique access list needs it's own - // gas calculation. This is quite expensive, but we need to be accurate - // and it's convered by the sender only anyway. - if nogas { - args.Gas = nil - if err := args.setDefaults(ctx, b); err != nil { - return nil, 0, nil, err // shouldn't happen, just in case - } - } // Copy the original db so we don't modify it statedb := db.Copy() // Set the accesslist to the last al From 49b54aaede670a450667ffbdbcf13f3e326adff4 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 13 Jun 2024 18:33:24 +0800 Subject: [PATCH 143/479] internal/ethapi: rework setDefaults for tx args so fee logic is separate (#25197) --- internal/ethapi/transaction_args.go | 118 +++--- internal/ethapi/transaction_args_test.go | 449 +++++++++++++++++++++++ 2 files changed, 517 insertions(+), 50 deletions(-) create mode 100644 internal/ethapi/transaction_args_test.go diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 34388ebf343d..84746cc1c231 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -75,56 +75,8 @@ func (args *TransactionArgs) data() []byte { // setDefaults fills in default values for unspecified tx fields. func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { - if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { - return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") - } - // After london, default to 1559 unless gasPrice is set - head := b.CurrentHeader() - // If user specifies both maxPriorityfee and maxFee, then we do not - // need to consult the chain for defaults. It's definitely a London tx. - if args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil { - // In this clause, user left some fields unspecified. - if b.ChainConfig().IsEIP1559(head.Number) && args.GasPrice == nil { - if args.MaxPriorityFeePerGas == nil { - tip, err := b.SuggestGasTipCap(ctx) - if err != nil { - return err - } - args.MaxPriorityFeePerGas = (*hexutil.Big)(tip) - } - if args.MaxFeePerGas == nil { - gasFeeCap := new(big.Int).Add( - (*big.Int)(args.MaxPriorityFeePerGas), - new(big.Int).Mul(head.BaseFee, big.NewInt(2)), - ) - args.MaxFeePerGas = (*hexutil.Big)(gasFeeCap) - } - if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { - return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) - } - } else { - if args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil { - return errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") - } - if args.GasPrice == nil { - price, err := b.SuggestGasTipCap(ctx) - if err != nil { - return err - } - if b.ChainConfig().IsEIP1559(head.Number) { - // The legacy tx gas price suggestion should not add 2x base fee - // because all fees are consumed, so it would result in a spiral - // upwards. - price.Add(price, head.BaseFee) - } - args.GasPrice = (*hexutil.Big)(price) - } - } - } else { - // Both maxPriorityfee and maxFee set by caller. Sanity-check their internal relation - if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { - return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) - } + if err := args.setFeeDefaults(ctx, b); err != nil { + return err } if args.Value == nil { args.Value = new(hexutil.Big) @@ -178,6 +130,72 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { return nil } +// setFeeDefaults fills in default fee values for unspecified tx fields. +func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b Backend) error { + // If both gasPrice and at least one of the EIP-1559 fee parameters are specified, error. + if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { + return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") + } + // If the tx has completely specified a fee mechanism, no default is needed. This allows users + // who are not yet synced past London to get defaults for other tx values. See + // https://github.com/ethereum/go-ethereum/pull/23274 for more information. + eip1559ParamsSet := args.MaxFeePerGas != nil && args.MaxPriorityFeePerGas != nil + if (args.GasPrice != nil && !eip1559ParamsSet) || (args.GasPrice == nil && eip1559ParamsSet) { + // Sanity check the EIP-1559 fee parameters if present. + if args.GasPrice == nil && args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { + return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) + } + return nil + } + // Now attempt to fill in default value depending on whether London is active or not. + head := b.CurrentHeader() + if b.ChainConfig().IsEIP1559(head.Number) { + // London is active, set maxPriorityFeePerGas and maxFeePerGas. + if err := args.setLondonFeeDefaults(ctx, head, b); err != nil { + return err + } + } else { + if args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil { + return fmt.Errorf("maxFeePerGas and maxPriorityFeePerGas are not valid before London is active") + } + // London not active, set gas price. + price, err := b.SuggestGasTipCap(ctx) + if err != nil { + return err + } + args.GasPrice = (*hexutil.Big)(price) + } + return nil +} + +// setLondonFeeDefaults fills in reasonable default fee values for unspecified fields. +func (args *TransactionArgs) setLondonFeeDefaults(ctx context.Context, head *types.Header, b Backend) error { + // Set maxPriorityFeePerGas if it is missing. + if args.MaxPriorityFeePerGas == nil { + tip, err := b.SuggestGasTipCap(ctx) + if err != nil { + return err + } + args.MaxPriorityFeePerGas = (*hexutil.Big)(tip) + } + // Set maxFeePerGas if it is missing. + if args.MaxFeePerGas == nil { + // Set the max fee to be 2 times larger than the previous block's base fee. + // The additional slack allows the tx to not become invalidated if the base + // fee is rising. + val := new(big.Int).Add( + args.MaxPriorityFeePerGas.ToInt(), + new(big.Int).Mul(head.BaseFee, big.NewInt(2)), + ) + args.MaxFeePerGas = (*hexutil.Big)(val) + } + // Both EIP-1559 fee parameters are now set; sanity check them. + if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { + return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) + } + return nil +} + // ToMessage converts the transaction arguments to the Message type used by the // core evm. This method is used in calls and traces that do not require a real // live transaction. diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go new file mode 100644 index 000000000000..3717cda5572b --- /dev/null +++ b/internal/ethapi/transaction_args_test.go @@ -0,0 +1,449 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethapi + +import ( + "context" + "fmt" + "math/big" + "reflect" + "testing" + "time" + + ethereum "github.com/XinFinOrg/XDPoSChain" + "github.com/XinFinOrg/XDPoSChain/XDCx" + "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate" + "github.com/XinFinOrg/XDPoSChain/XDCxlending" + "github.com/XinFinOrg/XDPoSChain/accounts" + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/hexutil" + "github.com/XinFinOrg/XDPoSChain/consensus" + "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/core/bloombits" + "github.com/XinFinOrg/XDPoSChain/core/state" + "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/core/vm" + "github.com/XinFinOrg/XDPoSChain/eth/downloader" + "github.com/XinFinOrg/XDPoSChain/ethdb" + "github.com/XinFinOrg/XDPoSChain/event" + "github.com/XinFinOrg/XDPoSChain/params" + "github.com/XinFinOrg/XDPoSChain/rpc" +) + +// TestSetFeeDefaults tests the logic for filling in default fee values works as expected. +func TestSetFeeDefaults(t *testing.T) { + type test struct { + name string + isLondon bool + in *TransactionArgs + want *TransactionArgs + err error + } + + var ( + b = newBackendMock() + fortytwo = (*hexutil.Big)(big.NewInt(42)) + maxFee = (*hexutil.Big)(new(big.Int).Add(new(big.Int).Mul(b.current.BaseFee, big.NewInt(2)), fortytwo.ToInt())) + al = &types.AccessList{types.AccessTuple{Address: common.Address{0xaa}, StorageKeys: []common.Hash{{0x01}}}} + ) + + tests := []test{ + // Legacy txs + { + "legacy tx pre-London", + false, + &TransactionArgs{}, + &TransactionArgs{GasPrice: fortytwo}, + nil, + }, + { + "legacy tx post-London, explicit gas price", + true, + &TransactionArgs{GasPrice: fortytwo}, + &TransactionArgs{GasPrice: fortytwo}, + nil, + }, + + // Access list txs + { + "access list tx pre-London", + false, + &TransactionArgs{AccessList: al}, + &TransactionArgs{AccessList: al, GasPrice: fortytwo}, + nil, + }, + { + "access list tx post-London, explicit gas price", + false, + &TransactionArgs{AccessList: al, GasPrice: fortytwo}, + &TransactionArgs{AccessList: al, GasPrice: fortytwo}, + nil, + }, + { + "access list tx post-London", + true, + &TransactionArgs{AccessList: al}, + &TransactionArgs{AccessList: al, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, + nil, + }, + { + "access list tx post-London, only max fee", + true, + &TransactionArgs{AccessList: al, MaxFeePerGas: maxFee}, + &TransactionArgs{AccessList: al, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, + nil, + }, + { + "access list tx post-London, only priority fee", + true, + &TransactionArgs{AccessList: al, MaxFeePerGas: maxFee}, + &TransactionArgs{AccessList: al, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, + nil, + }, + + // Dynamic fee txs + { + "dynamic tx post-London", + true, + &TransactionArgs{}, + &TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, + nil, + }, + { + "dynamic tx post-London, only max fee", + true, + &TransactionArgs{MaxFeePerGas: maxFee}, + &TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, + nil, + }, + { + "dynamic tx post-London, only priority fee", + true, + &TransactionArgs{MaxFeePerGas: maxFee}, + &TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, + nil, + }, + { + "dynamic fee tx pre-London, maxFee set", + false, + &TransactionArgs{MaxFeePerGas: maxFee}, + nil, + fmt.Errorf("maxFeePerGas and maxPriorityFeePerGas are not valid before London is active"), + }, + { + "dynamic fee tx pre-London, priorityFee set", + false, + &TransactionArgs{MaxPriorityFeePerGas: fortytwo}, + nil, + fmt.Errorf("maxFeePerGas and maxPriorityFeePerGas are not valid before London is active"), + }, + { + "dynamic fee tx, maxFee < priorityFee", + true, + &TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: (*hexutil.Big)(big.NewInt(1000))}, + nil, + fmt.Errorf("maxFeePerGas (0x3e) < maxPriorityFeePerGas (0x3e8)"), + }, + { + "dynamic fee tx, maxFee < priorityFee while setting default", + true, + &TransactionArgs{MaxFeePerGas: (*hexutil.Big)(big.NewInt(7))}, + nil, + fmt.Errorf("maxFeePerGas (0x7) < maxPriorityFeePerGas (0x2a)"), + }, + + // Misc + { + "set all fee parameters", + false, + &TransactionArgs{GasPrice: fortytwo, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, + nil, + fmt.Errorf("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"), + }, + { + "set gas price and maxPriorityFee", + false, + &TransactionArgs{GasPrice: fortytwo, MaxPriorityFeePerGas: fortytwo}, + nil, + fmt.Errorf("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"), + }, + { + "set gas price and maxFee", + true, + &TransactionArgs{GasPrice: fortytwo, MaxFeePerGas: maxFee}, + nil, + fmt.Errorf("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"), + }, + } + + ctx := context.Background() + for i, test := range tests { + if test.isLondon { + b.activateLondon() + } else { + b.deactivateLondon() + } + got := test.in + err := got.setFeeDefaults(ctx, b) + if err != nil && err.Error() == test.err.Error() { + // Test threw expected error. + continue + } else if err != nil { + t.Fatalf("test %d (%s): unexpected error: %s", i, test.name, err) + } + if !reflect.DeepEqual(got, test.want) { + t.Fatalf("test %d (%s): did not fill defaults as expected: (got: %v, want: %v)", i, test.name, got, test.want) + } + } +} + +type backendMock struct { + current *types.Header + config *params.ChainConfig +} + +func newBackendMock() *backendMock { + config := ¶ms.ChainConfig{ + ChainId: big.NewInt(42), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + Eip1559Block: big.NewInt(1000), + } + return &backendMock{ + current: &types.Header{ + Difficulty: big.NewInt(10000000000), + Number: big.NewInt(1100), + GasLimit: 8_000_000, + GasUsed: 8_000_000, + Time: big.NewInt(555), + Extra: make([]byte, 32), + BaseFee: big.NewInt(10), + }, + config: config, + } +} + +func (b *backendMock) activateLondon() { + b.current.Number = big.NewInt(1100) +} + +func (b *backendMock) deactivateLondon() { + b.current.Number = big.NewInt(900) +} + +func (b *backendMock) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { + return big.NewInt(42), nil +} + +func (b *backendMock) CurrentHeader() *types.Header { return b.current } + +func (b *backendMock) ChainConfig() *params.ChainConfig { return b.config } + +// Other methods needed to implement Backend interface. +func (b *backendMock) SyncProgress() ethereum.SyncProgress { return ethereum.SyncProgress{} } +func (b *backendMock) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) { + return nil, nil, nil, nil, nil +} +func (b *backendMock) ChainDb() ethdb.Database { return nil } +func (b *backendMock) AccountManager() *accounts.Manager { return nil } +func (b *backendMock) ExtRPCEnabled() bool { return false } +func (b *backendMock) RPCGasCap() uint64 { return 0 } +func (b *backendMock) RPCEVMTimeout() time.Duration { return time.Second } +func (b *backendMock) RPCTxFeeCap() float64 { return 0 } +func (b *backendMock) UnprotectedAllowed() bool { return false } +func (b *backendMock) SetHead(number uint64) {} + +func (b *backendMock) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { + return nil, nil +} + +func (b *backendMock) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { + return nil, nil +} + +func (b *backendMock) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) { + return nil, nil +} + +func (b *backendMock) CurrentBlock() *types.Block { return nil } + +func (b *backendMock) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { + return nil, nil +} + +func (b *backendMock) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { + return nil, nil +} + +func (b *backendMock) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) { + return nil, nil +} + +func (b *backendMock) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) { + return nil, nil, nil +} + +func (b *backendMock) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) { + return nil, nil, nil +} + +func (b *backendMock) PendingBlockAndReceipts() (*types.Block, types.Receipts) { return nil, nil } + +func (b *backendMock) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { + return nil, nil +} + +func (b *backendMock) GetTd(common.Hash) *big.Int { + return nil +} + +func (b *backendMock) GetEVM(context.Context, core.Message, *state.StateDB, *tradingstate.TradingStateDB, *types.Header, *vm.Config) (*vm.EVM, func() error, error) { + return nil, nil, nil +} + +func (b *backendMock) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { return nil } +func (b *backendMock) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { + return nil +} +func (b *backendMock) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription { + return nil +} +func (b *backendMock) SendTx(ctx context.Context, signedTx *types.Transaction) error { return nil } +func (b *backendMock) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) { + return nil, [32]byte{}, 0, 0, nil +} +func (b *backendMock) GetPoolTransactions() (types.Transactions, error) { return nil, nil } +func (b *backendMock) GetPoolTransaction(txHash common.Hash) *types.Transaction { return nil } +func (b *backendMock) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { + return 0, nil +} +func (b *backendMock) Stats() (pending int, queued int) { return 0, 0 } +func (b *backendMock) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { + return nil, nil +} +func (b *backendMock) TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions) { + return nil, nil +} +func (b *backendMock) SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription { return nil } +func (b *backendMock) BloomStatus() (uint64, uint64) { return 0, 0 } +func (b *backendMock) GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) { + return nil, nil +} +func (b *backendMock) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) {} +func (b *backendMock) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { return nil } +func (b *backendMock) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription { + return nil +} +func (b *backendMock) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { + return nil +} + +func (b *backendMock) Engine() consensus.Engine { return nil } + +func (b *backendMock) AreTwoBlockSamePath(bh1 common.Hash, bh2 common.Hash) bool { + return true +} + +func (b *backendMock) Downloader() *downloader.Downloader { + return nil +} + +func (b *backendMock) EventMux() *event.TypeMux { + return nil +} + +func (b *backendMock) GetBlock(context.Context, common.Hash) (*types.Block, error) { + return nil, nil +} + +func (b *backendMock) GetBlocksHashCache(blockNr uint64) []common.Hash { + return []common.Hash{} +} + +func (b *backendMock) GetEngine() consensus.Engine { + return nil +} + +func (b *backendMock) GetEpochDuration() *big.Int { + return nil +} + +func (b *backendMock) GetIPCClient() (bind.ContractBackend, error) { + return nil, nil +} + +func (b *backendMock) GetMasternodesCap(uint64) map[common.Address]*big.Int { + return nil +} + +func (b *backendMock) GetOrderNonce(common.Hash) (uint64, error) { + return 0, nil +} + +func (b *backendMock) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) { + return nil, nil +} + +func (b *backendMock) GetRewardByHash(common.Hash) map[string]map[string]map[string]*big.Int { + return nil +} + +func (b *backendMock) GetVotersCap(*big.Int, common.Address, []common.Address) map[common.Address]*big.Int { + return nil +} + +func (b *backendMock) GetVotersRewards(common.Address) map[common.Address]*big.Int { + return nil +} + +func (b *backendMock) LendingService() *XDCxlending.Lending { + return nil +} + +func (b *backendMock) OrderStats() (int, int) { + return 0, 0 +} + +func (b *backendMock) OrderTxPoolContent() (map[common.Address]types.OrderTransactions, map[common.Address]types.OrderTransactions) { + return nil, nil +} + +func (b *backendMock) ProtocolVersion() int { + return 0 +} + +func (b *backendMock) SendLendingTx(context.Context, *types.LendingTransaction) error { + return nil +} + +func (b *backendMock) SendOrderTx(context.Context, *types.OrderTransaction) error { + return nil +} + +func (b *backendMock) XDCxService() *XDCx.XDCX { + return nil +} From e4153f756d02c9c867721b00500ef23c561b965e Mon Sep 17 00:00:00 2001 From: ucwong Date: Fri, 19 Aug 2022 14:01:09 +0800 Subject: [PATCH 144/479] internal/ethapi: fix comment typo (#25548) --- internal/ethapi/transaction_args.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 84746cc1c231..f23926592d2a 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -250,7 +250,7 @@ func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap gasPrice = args.GasPrice.ToInt() gasFeeCap, gasTipCap = gasPrice, gasPrice } else { - // User specified 1559 gas feilds (or none), use those + // User specified 1559 gas fields (or none), use those gasFeeCap = new(big.Int) if args.MaxFeePerGas != nil { gasFeeCap = args.MaxFeePerGas.ToInt() From a410f7b38ef3871c2896b7e349d6abdc8b5dc402 Mon Sep 17 00:00:00 2001 From: Seungbae Yu Date: Mon, 12 Sep 2022 22:02:41 +0900 Subject: [PATCH 145/479] core: preallocate maps in TxPool helper methods (#25737) --- core/tx_pool.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index 57738b64fd43..573ba44f4ece 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -534,11 +534,11 @@ func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common pool.mu.Lock() defer pool.mu.Unlock() - pending := make(map[common.Address]types.Transactions) + pending := make(map[common.Address]types.Transactions, len(pool.pending)) for addr, list := range pool.pending { pending[addr] = list.Flatten() } - queued := make(map[common.Address]types.Transactions) + queued := make(map[common.Address]types.Transactions, len(pool.queue)) for addr, list := range pool.queue { queued[addr] = list.Flatten() } @@ -1752,7 +1752,7 @@ type accountSet struct { // derivations. func newAccountSet(signer types.Signer, addrs ...common.Address) *accountSet { as := &accountSet{ - accounts: make(map[common.Address]struct{}), + accounts: make(map[common.Address]struct{}, len(addrs)), signer: signer, } for _, addr := range addrs { From 66763aa8aecbb34175d1dc73fa4b60038a0100f7 Mon Sep 17 00:00:00 2001 From: Seungbae Yu Date: Sat, 17 Sep 2022 01:23:13 +0900 Subject: [PATCH 146/479] core: don't cache zero nonce in txNoncer (#25603) This changes the nonce cache used by TxPool to not store cached nonces for non-existing accounts. --- core/tx_noncer.go | 8 ++++++-- core/tx_pool.go | 3 --- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/core/tx_noncer.go b/core/tx_noncer.go index 60779dd3121e..83c3118c0ac5 100644 --- a/core/tx_noncer.go +++ b/core/tx_noncer.go @@ -49,7 +49,9 @@ func (txn *txNoncer) get(addr common.Address) uint64 { defer txn.lock.Unlock() if _, ok := txn.nonces[addr]; !ok { - txn.nonces[addr] = txn.fallback.GetNonce(addr) + if nonce := txn.fallback.GetNonce(addr); nonce != 0 { + txn.nonces[addr] = nonce + } } return txn.nonces[addr] } @@ -70,7 +72,9 @@ func (txn *txNoncer) setIfLower(addr common.Address, nonce uint64) { defer txn.lock.Unlock() if _, ok := txn.nonces[addr]; !ok { - txn.nonces[addr] = txn.fallback.GetNonce(addr) + if nonce := txn.fallback.GetNonce(addr); nonce != 0 { + txn.nonces[addr] = nonce + } } if txn.nonces[addr] <= nonce { return diff --git a/core/tx_pool.go b/core/tx_pool.go index 573ba44f4ece..c63ef8127b1a 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -499,9 +499,6 @@ func (pool *TxPool) SetGasPrice(price *big.Int) { // Nonce returns the next nonce of an account, with all transactions executable // by the pool already applied on top. func (pool *TxPool) Nonce(addr common.Address) uint64 { - pool.mu.RLock() - defer pool.mu.RUnlock() - return pool.pendingNonces.get(addr) } From 05797846fccc542a4492a716a9d9e904dd5b45ee Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Mon, 26 Sep 2022 11:34:15 +0200 Subject: [PATCH 147/479] core: fix datarace in txpool, fixes #25870 and #25869 (#25872) core: fix datarace in txpool pendingnoce, fixes #25870 --- core/tx_pool.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/tx_pool.go b/core/tx_pool.go index c63ef8127b1a..573ba44f4ece 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -499,6 +499,9 @@ func (pool *TxPool) SetGasPrice(price *big.Int) { // Nonce returns the next nonce of an account, with all transactions executable // by the pool already applied on top. func (pool *TxPool) Nonce(addr common.Address) uint64 { + pool.mu.RLock() + defer pool.mu.RUnlock() + return pool.pendingNonces.get(addr) } From 8b2e8d9b3a221156f77cf3898307c493ad2d2aba Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 10 Jun 2024 20:14:49 +0800 Subject: [PATCH 148/479] all: refactor txpool into it's own package in prep for 4844 (#26038) --- cmd/utils/flags.go | 7 +- contracts/utils.go | 3 +- core/blockchain.go | 9 +- core/error.go | 32 +- core/helper_test.go | 91 ------ core/{tx_cacher.go => sender_cacher.go} | 14 +- core/{tx_journal.go => txpool/journal.go} | 18 +- core/{ => txpool}/lending_pool.go | 19 +- core/{ => txpool}/lending_pool_test.go | 2 +- core/{ => txpool}/lending_tx_journal.go | 2 +- core/{ => txpool}/lending_tx_list.go | 2 +- core/{tx_list.go => txpool/list.go} | 102 +++---- core/{tx_list_test.go => txpool/list_test.go} | 18 +- core/{tx_noncer.go => txpool/noncer.go} | 20 +- core/{ => txpool}/order_pool.go | 21 +- core/{ => txpool}/order_pool_test.go | 2 +- core/{ => txpool}/order_tx_journal.go | 2 +- core/{ => txpool}/order_tx_list.go | 2 +- core/{tx_pool.go => txpool/txpool.go} | 174 +++++------ .../txpool_test.go} | 289 +++++++++--------- eth/backend.go | 19 +- eth/ethconfig/config.go | 5 +- eth/ethconfig/gen_config.go | 5 +- les/handler.go | 12 +- les/handler_test.go | 39 +-- les/protocol.go | 3 +- light/txpool.go | 7 +- miner/miner.go | 10 +- miner/worker.go | 2 +- 29 files changed, 425 insertions(+), 506 deletions(-) delete mode 100644 core/helper_test.go rename core/{tx_cacher.go => sender_cacher.go} (88%) rename core/{tx_journal.go => txpool/journal.go} (91%) rename core/{ => txpool}/lending_pool.go (98%) rename core/{ => txpool}/lending_pool_test.go (99%) rename core/{ => txpool}/lending_tx_journal.go (99%) rename core/{ => txpool}/lending_tx_list.go (99%) rename core/{tx_list.go => txpool/list.go} (87%) rename core/{tx_list_test.go => txpool/list_test.go} (84%) rename core/{tx_noncer.go => txpool/noncer.go} (81%) rename core/{ => txpool}/order_pool.go (98%) rename core/{ => txpool}/order_pool_test.go (99%) rename core/{ => txpool}/order_tx_journal.go (99%) rename core/{ => txpool}/order_tx_list.go (99%) rename core/{tx_pool.go => txpool/txpool.go} (93%) rename core/{tx_pool_test.go => txpool/txpool_test.go} (92%) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index d45919a76f69..5d661fa0e77c 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -38,6 +38,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" "github.com/XinFinOrg/XDPoSChain/consensus/ethash" "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/core/txpool" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/eth/downloader" @@ -261,12 +262,12 @@ var ( TxPoolJournalFlag = cli.StringFlag{ Name: "txpool.journal", Usage: "Disk journal for local transaction to survive node restarts", - Value: core.DefaultTxPoolConfig.Journal, + Value: txpool.DefaultConfig.Journal, } TxPoolRejournalFlag = cli.DurationFlag{ Name: "txpool.rejournal", Usage: "Time interval to regenerate the local transaction journal", - Value: core.DefaultTxPoolConfig.Rejournal, + Value: txpool.DefaultConfig.Rejournal, } TxPoolPriceLimitFlag = cli.Uint64Flag{ Name: "txpool.pricelimit", @@ -1032,7 +1033,7 @@ func setGPO(ctx *cli.Context, cfg *gasprice.Config, light bool) { } } -func setTxPool(ctx *cli.Context, cfg *core.TxPoolConfig) { +func setTxPool(ctx *cli.Context, cfg *txpool.Config) { if ctx.GlobalIsSet(TxPoolNoLocalsFlag.Name) { cfg.NoLocals = ctx.GlobalBool(TxPoolNoLocalsFlag.Name) } diff --git a/contracts/utils.go b/contracts/utils.go index 17e108ee6466..5e236743ea27 100644 --- a/contracts/utils.go +++ b/contracts/utils.go @@ -41,6 +41,7 @@ import ( randomizeContract "github.com/XinFinOrg/XDPoSChain/contracts/randomize/contract" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/state" + "github.com/XinFinOrg/XDPoSChain/core/txpool" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/log" @@ -60,7 +61,7 @@ type RewardLog struct { var TxSignMu sync.RWMutex // Send tx sign for block number to smart contract blockSigner. -func CreateTransactionSign(chainConfig *params.ChainConfig, pool *core.TxPool, manager *accounts.Manager, block *types.Block, chainDb ethdb.Database, eb common.Address) error { +func CreateTransactionSign(chainConfig *params.ChainConfig, pool *txpool.TxPool, manager *accounts.Manager, block *types.Block, chainDb ethdb.Database, eb common.Address) error { TxSignMu.Lock() defer TxSignMu.Unlock() if chainConfig.XDPoS != nil { diff --git a/core/blockchain.go b/core/blockchain.go index 247319a91e43..05fbdeb3a88c 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -60,10 +60,9 @@ var ( CheckpointCh = make(chan int) ErrNoGenesis = errors.New("Genesis not found in chain") - blockReorgMeter = metrics.NewRegisteredMeter("chain/reorg/executes", nil) - blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil) - blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil) - blockReorgInvalidatedTx = metrics.NewRegisteredMeter("chain/reorg/invalidTx", nil) + blockReorgMeter = metrics.NewRegisteredMeter("chain/reorg/executes", nil) + blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil) + blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil) ) const ( @@ -1440,7 +1439,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, [] defer close(abort) // Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss) - senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain) + SenderCacher.RecoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain) // Iterate over the blocks and insert when the verifier permits for i, block := range chain { diff --git a/core/error.go b/core/error.go index 35106d0f1c30..72a835149ede 100644 --- a/core/error.go +++ b/core/error.go @@ -26,13 +26,13 @@ var ( // ErrKnownBlock is returned when a block to import is already known locally. ErrKnownBlock = errors.New("block already known") - // ErrGasLimitReached is returned by the gas pool if the amount of gas required - // by a transaction is higher than what's left in the block. - ErrGasLimitReached = errors.New("gas limit reached") - // ErrBlacklistedHash is returned if a block to import is on the blacklist. ErrBlacklistedHash = errors.New("blacklisted hash") + // ErrNonceTooLow is returned if the nonce of a transaction is lower than the + // one present in the local chain. + ErrNonceTooLow = errors.New("nonce too low") + // ErrNonceTooHigh is returned if the nonce of a transaction is higher than the // next one expected based on the local chain. ErrNonceTooHigh = errors.New("nonce too high") @@ -41,19 +41,25 @@ var ( // maximum allowed value and would become invalid if incremented. ErrNonceMax = errors.New("nonce has max value") - ErrNotXDPoS = errors.New("XDPoS not found in config") + // ErrGasLimitReached is returned by the gas pool if the amount of gas required + // by a transaction is higher than what's left in the block. + ErrGasLimitReached = errors.New("gas limit reached") - ErrNotFoundM1 = errors.New("list M1 not found ") + // ErrInsufficientFunds is returned if the total cost of executing a transaction + // is higher than the balance of the user's account. + ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value") - ErrStopPreparingBlock = errors.New("stop calculating a block not verified by M2") + // ErrGasUintOverflow is returned when calculating gas usage. + ErrGasUintOverflow = errors.New("gas uint64 overflow") + + // ErrIntrinsicGas is returned if the transaction is specified to use less gas + // than required to start the invocation. + ErrIntrinsicGas = errors.New("intrinsic gas too low") // ErrTxTypeNotSupported is returned if a transaction is not supported in the // current network configuration. ErrTxTypeNotSupported = types.ErrTxTypeNotSupported - // ErrGasUintOverflow is returned when calculating gas usage. - ErrGasUintOverflow = errors.New("gas uint64 overflow") - // ErrTipAboveFeeCap is a sanity error to ensure no one is able to specify a // transaction with a tip higher than the total fee cap. ErrTipAboveFeeCap = errors.New("max priority fee per gas higher than max fee per gas") @@ -72,4 +78,10 @@ var ( // ErrSenderNoEOA is returned if the sender of a transaction is a contract. ErrSenderNoEOA = errors.New("sender not an eoa") + + ErrNotXDPoS = errors.New("XDPoS not found in config") + + ErrNotFoundM1 = errors.New("list M1 not found ") + + ErrStopPreparingBlock = errors.New("stop calculating a block not verified by M2") ) diff --git a/core/helper_test.go b/core/helper_test.go deleted file mode 100644 index c499d15db510..000000000000 --- a/core/helper_test.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2014 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core - -import ( - "container/list" - - "github.com/XinFinOrg/XDPoSChain/core/rawdb" - - "github.com/XinFinOrg/XDPoSChain/core/types" - "github.com/XinFinOrg/XDPoSChain/ethdb" - "github.com/XinFinOrg/XDPoSChain/event" -) - -// Implement our EthTest Manager -type TestManager struct { - // stateManager *StateManager - eventMux *event.TypeMux - - db ethdb.Database - txPool *TxPool - blockChain *BlockChain - Blocks []*types.Block -} - -func (tm *TestManager) IsListening() bool { - return false -} - -func (tm *TestManager) IsMining() bool { - return false -} - -func (tm *TestManager) PeerCount() int { - return 0 -} - -func (tm *TestManager) Peers() *list.List { - return list.New() -} - -func (tm *TestManager) BlockChain() *BlockChain { - return tm.blockChain -} - -func (tm *TestManager) TxPool() *TxPool { - return tm.txPool -} - -// func (tm *TestManager) StateManager() *StateManager { -// return tm.stateManager -// } - -func (tm *TestManager) EventMux() *event.TypeMux { - return tm.eventMux -} - -// func (tm *TestManager) KeyManager() *crypto.KeyManager { -// return nil -// } - -func (tm *TestManager) Db() ethdb.Database { - return tm.db -} - -func NewTestManager() *TestManager { - db := rawdb.NewMemoryDatabase() - - testManager := &TestManager{} - testManager.eventMux = new(event.TypeMux) - testManager.db = db - // testManager.txPool = NewTxPool(testManager) - // testManager.blockChain = NewBlockChain(testManager) - // testManager.stateManager = NewStateManager(testManager) - - return testManager -} diff --git a/core/tx_cacher.go b/core/sender_cacher.go similarity index 88% rename from core/tx_cacher.go rename to core/sender_cacher.go index ea4ab6cc07f6..e556ebd4e407 100644 --- a/core/tx_cacher.go +++ b/core/sender_cacher.go @@ -22,8 +22,8 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/types" ) -// senderCacher is a concurrent tranaction sender recoverer anc cacher. -var senderCacher = newTxSenderCacher(runtime.NumCPU()) +// SenderCacher is a concurrent transaction sender recoverer and cacher. +var SenderCacher = newTxSenderCacher(runtime.NumCPU()) // txSenderCacherRequest is a request for recovering transaction senders with a // specific signature scheme and caching it into the transactions themselves. @@ -67,10 +67,10 @@ func (cacher *txSenderCacher) cache() { } } -// recover recovers the senders from a batch of transactions and caches them +// Recover recovers the senders from a batch of transactions and caches them // back into the same data structures. There is no validation being done, nor // any reaction to invalid signatures. That is up to calling code later. -func (cacher *txSenderCacher) recover(signer types.Signer, txs []*types.Transaction) { +func (cacher *txSenderCacher) Recover(signer types.Signer, txs []*types.Transaction) { // If there's nothing to recover, abort if len(txs) == 0 { return @@ -89,10 +89,10 @@ func (cacher *txSenderCacher) recover(signer types.Signer, txs []*types.Transact } } -// recoverFromBlocks recovers the senders from a batch of blocks and caches them +// RecoverFromBlocks recovers the senders from a batch of blocks and caches them // back into the same data structures. There is no validation being done, nor // any reaction to invalid signatures. That is up to calling code later. -func (cacher *txSenderCacher) recoverFromBlocks(signer types.Signer, blocks []*types.Block) { +func (cacher *txSenderCacher) RecoverFromBlocks(signer types.Signer, blocks []*types.Block) { count := 0 for _, block := range blocks { count += len(block.Transactions()) @@ -101,5 +101,5 @@ func (cacher *txSenderCacher) recoverFromBlocks(signer types.Signer, blocks []*t for _, block := range blocks { txs = append(txs, block.Transactions()...) } - cacher.recover(signer, txs) + cacher.Recover(signer, txs) } diff --git a/core/tx_journal.go b/core/txpool/journal.go similarity index 91% rename from core/tx_journal.go rename to core/txpool/journal.go index 4fe5fdca365c..871807729ce7 100644 --- a/core/tx_journal.go +++ b/core/txpool/journal.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package core +package txpool import ( "errors" @@ -40,23 +40,23 @@ type devNull struct{} func (*devNull) Write(p []byte) (n int, err error) { return len(p), nil } func (*devNull) Close() error { return nil } -// txJournal is a rotating log of transactions with the aim of storing locally +// journal is a rotating log of transactions with the aim of storing locally // created transactions to allow non-executed ones to survive node restarts. -type txJournal struct { +type journal struct { path string // Filesystem path to store the transactions at writer io.WriteCloser // Output stream to write new transactions into } // newTxJournal creates a new transaction journal to -func newTxJournal(path string) *txJournal { - return &txJournal{ +func newTxJournal(path string) *journal { + return &journal{ path: path, } } // load parses a transaction journal dump from disk, loading its contents into // the specified pool. -func (journal *txJournal) load(add func([]*types.Transaction) []error) error { +func (journal *journal) load(add func([]*types.Transaction) []error) error { // Skip the parsing if the journal file doens't exist at all if _, err := os.Stat(journal.path); os.IsNotExist(err) { return nil @@ -116,7 +116,7 @@ func (journal *txJournal) load(add func([]*types.Transaction) []error) error { } // insert adds the specified transaction to the local disk journal. -func (journal *txJournal) insert(tx *types.Transaction) error { +func (journal *journal) insert(tx *types.Transaction) error { if journal.writer == nil { return errNoActiveJournal } @@ -128,7 +128,7 @@ func (journal *txJournal) insert(tx *types.Transaction) error { // rotate regenerates the transaction journal based on the current contents of // the transaction pool. -func (journal *txJournal) rotate(all map[common.Address]types.Transactions) error { +func (journal *journal) rotate(all map[common.Address]types.Transactions) error { // Close the current journal (if any is open) if journal.writer != nil { if err := journal.writer.Close(); err != nil { @@ -168,7 +168,7 @@ func (journal *txJournal) rotate(all map[common.Address]types.Transactions) erro } // close flushes the transaction journal contents to disk and closes the file. -func (journal *txJournal) close() error { +func (journal *journal) close() error { var err error if journal.writer != nil { diff --git a/core/lending_pool.go b/core/txpool/lending_pool.go similarity index 98% rename from core/lending_pool.go rename to core/txpool/lending_pool.go index c5197a3754a5..4a199384cf12 100644 --- a/core/lending_pool.go +++ b/core/txpool/lending_pool.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package core +package txpool import ( "errors" @@ -29,6 +29,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common/prque" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" + "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/event" @@ -75,7 +76,7 @@ type blockChainLending interface { GetBlock(hash common.Hash, number uint64) *types.Block LendingStateAt(block *types.Block) (*lendingstate.LendingStateDB, error) StateAt(root common.Hash) (*state.StateDB, error) - SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription + SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription Engine() consensus.Engine // GetHeader returns the hash corresponding to their hash. GetHeader(common.Hash, uint64) *types.Header @@ -124,7 +125,7 @@ type LendingPool struct { txFeed event.Feed scope event.SubscriptionScope - chainHeadCh chan ChainHeadEvent + chainHeadCh chan core.ChainHeadEvent chainHeadSub event.Subscription signer types.LendingSigner mu sync.RWMutex @@ -161,7 +162,7 @@ func NewLendingPool(chainconfig *params.ChainConfig, chain blockChainLending) *L queue: make(map[common.Address]*lendingtxList), beats: make(map[common.Address]time.Time), all: make(map[common.Hash]*types.LendingTransaction), - chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize), + chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), } pool.locals = newLendingAccountSet(pool.signer) pool.reset(nil, chain.CurrentBlock()) @@ -334,7 +335,7 @@ func (pool *LendingPool) Stop() { // SubscribeTxPreEvent registers a subscription of TxPreEvent and // starts sending event to the given channel. -func (pool *LendingPool) SubscribeTxPreEvent(ch chan<- LendingTxPreEvent) event.Subscription { +func (pool *LendingPool) SubscribeTxPreEvent(ch chan<- core.LendingTxPreEvent) event.Subscription { return pool.scope.Track(pool.txFeed.Subscribe(ch)) } @@ -514,7 +515,7 @@ func (pool *LendingPool) validateTopupLending(cloneStateDb *state.StateDB, clone func (pool *LendingPool) validateBalance(cloneStateDb *state.StateDB, cloneLendingStateDb *lendingstate.LendingStateDB, tx *types.LendingTransaction, collateralToken common.Address) error { XDPoSEngine, ok := pool.chain.Engine().(*XDPoS.XDPoS) if !ok { - return ErrNotXDPoS + return core.ErrNotXDPoS } XDCXServ := XDPoSEngine.GetXDCXService() lendingServ := XDPoSEngine.GetLendingService() @@ -641,10 +642,10 @@ func (pool *LendingPool) validateTx(tx *types.LendingTransaction, local bool) er } // Ensure the transaction adheres to nonce lending if pool.currentLendingState.GetNonce(from.Hash()) > tx.Nonce() { - return ErrNonceTooLow + return core.ErrNonceTooLow } if pool.pendingState.GetNonce(from.Hash())+common.LimitThresholdNonceInQueue < tx.Nonce() { - return ErrNonceTooHigh + return core.ErrNonceTooHigh } return nil @@ -778,7 +779,7 @@ func (pool *LendingPool) promoteTx(addr common.Address, hash common.Hash, tx *ty pool.beats[addr] = time.Now() pool.pendingState.SetNonce(addr.Hash(), tx.Nonce()+1) - go pool.txFeed.Send(LendingTxPreEvent{tx}) + go pool.txFeed.Send(core.LendingTxPreEvent{Tx: tx}) } // AddLocal enqueues a single transaction into the pool if it is valid, marking diff --git a/core/lending_pool_test.go b/core/txpool/lending_pool_test.go similarity index 99% rename from core/lending_pool_test.go rename to core/txpool/lending_pool_test.go index 555c53ebc5cf..0df24cf5a4aa 100644 --- a/core/lending_pool_test.go +++ b/core/txpool/lending_pool_test.go @@ -1,4 +1,4 @@ -package core +package txpool import ( "context" diff --git a/core/lending_tx_journal.go b/core/txpool/lending_tx_journal.go similarity index 99% rename from core/lending_tx_journal.go rename to core/txpool/lending_tx_journal.go index 4bf835e9cee8..fb9b487ac5e5 100644 --- a/core/lending_tx_journal.go +++ b/core/txpool/lending_tx_journal.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package core +package txpool import ( "io" diff --git a/core/lending_tx_list.go b/core/txpool/lending_tx_list.go similarity index 99% rename from core/lending_tx_list.go rename to core/txpool/lending_tx_list.go index 5d25ac47ef03..d7c30cea5959 100644 --- a/core/lending_tx_list.go +++ b/core/txpool/lending_tx_list.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package core +package txpool import ( "container/heap" diff --git a/core/tx_list.go b/core/txpool/list.go similarity index 87% rename from core/tx_list.go rename to core/txpool/list.go index 8e92debe4934..70d2322f9660 100644 --- a/core/tx_list.go +++ b/core/txpool/list.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package core +package txpool import ( "container/heap" @@ -49,30 +49,30 @@ func (h *nonceHeap) Pop() interface{} { return x } -// txSortedMap is a nonce->transaction hash map with a heap based index to allow +// sortedMap is a nonce->transaction hash map with a heap based index to allow // iterating over the contents in a nonce-incrementing way. -type txSortedMap struct { +type sortedMap struct { items map[uint64]*types.Transaction // Hash map storing the transaction data index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode) cache types.Transactions // Cache of the transactions already sorted } -// newTxSortedMap creates a new nonce-sorted transaction map. -func newTxSortedMap() *txSortedMap { - return &txSortedMap{ +// newSortedMap creates a new nonce-sorted transaction map. +func newSortedMap() *sortedMap { + return &sortedMap{ items: make(map[uint64]*types.Transaction), index: new(nonceHeap), } } // Get retrieves the current transactions associated with the given nonce. -func (m *txSortedMap) Get(nonce uint64) *types.Transaction { +func (m *sortedMap) Get(nonce uint64) *types.Transaction { return m.items[nonce] } // Put inserts a new transaction into the map, also updating the map's nonce // index. If a transaction already exists with the same nonce, it's overwritten. -func (m *txSortedMap) Put(tx *types.Transaction) { +func (m *sortedMap) Put(tx *types.Transaction) { nonce := tx.Nonce() if m.items[nonce] == nil { heap.Push(m.index, nonce) @@ -83,7 +83,7 @@ func (m *txSortedMap) Put(tx *types.Transaction) { // Forward removes all transactions from the map with a nonce lower than the // provided threshold. Every removed transaction is returned for any post-removal // maintenance. -func (m *txSortedMap) Forward(threshold uint64) types.Transactions { +func (m *sortedMap) Forward(threshold uint64) types.Transactions { var removed types.Transactions // Pop off heap items until the threshold is reached @@ -104,7 +104,7 @@ func (m *txSortedMap) Forward(threshold uint64) types.Transactions { // Filter, as opposed to 'filter', re-initialises the heap after the operation is done. // If you want to do several consecutive filterings, it's therefore better to first // do a .filter(func1) followed by .Filter(func2) or reheap() -func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions { +func (m *sortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions { removed := m.filter(filter) // If transactions were removed, the heap and cache are ruined if len(removed) > 0 { @@ -113,7 +113,7 @@ func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transac return removed } -func (m *txSortedMap) reheap() { +func (m *sortedMap) reheap() { *m.index = make([]uint64, 0, len(m.items)) for nonce := range m.items { *m.index = append(*m.index, nonce) @@ -124,7 +124,7 @@ func (m *txSortedMap) reheap() { // filter is identical to Filter, but **does not** regenerate the heap. This method // should only be used if followed immediately by a call to Filter or reheap() -func (m *txSortedMap) filter(filter func(*types.Transaction) bool) types.Transactions { +func (m *sortedMap) filter(filter func(*types.Transaction) bool) types.Transactions { var removed types.Transactions // Collect all the transactions to filter out @@ -142,7 +142,7 @@ func (m *txSortedMap) filter(filter func(*types.Transaction) bool) types.Transac // Cap places a hard limit on the number of items, returning all transactions // exceeding that limit. -func (m *txSortedMap) Cap(threshold int) types.Transactions { +func (m *sortedMap) Cap(threshold int) types.Transactions { // Short circuit if the number of items is under the limit if len(m.items) <= threshold { return nil @@ -167,7 +167,7 @@ func (m *txSortedMap) Cap(threshold int) types.Transactions { // Remove deletes a transaction from the maintained map, returning whether the // transaction was found. -func (m *txSortedMap) Remove(nonce uint64) bool { +func (m *sortedMap) Remove(nonce uint64) bool { // Short circuit if no transaction is present _, ok := m.items[nonce] if !ok { @@ -193,7 +193,7 @@ func (m *txSortedMap) Remove(nonce uint64) bool { // Note, all transactions with nonces lower than start will also be returned to // prevent getting into and invalid state. This is not something that should ever // happen but better to be self correcting than failing! -func (m *txSortedMap) Ready(start uint64) types.Transactions { +func (m *sortedMap) Ready(start uint64) types.Transactions { // Short circuit if no transactions are available if m.index.Len() == 0 || (*m.index)[0] > start { return nil @@ -211,11 +211,11 @@ func (m *txSortedMap) Ready(start uint64) types.Transactions { } // Len returns the length of the transaction map. -func (m *txSortedMap) Len() int { +func (m *sortedMap) Len() int { return len(m.items) } -func (m *txSortedMap) flatten() types.Transactions { +func (m *sortedMap) flatten() types.Transactions { // If the sorting was not cached yet, create and cache it if m.cache == nil { m.cache = make(types.Transactions, 0, len(m.items)) @@ -230,7 +230,7 @@ func (m *txSortedMap) flatten() types.Transactions { // Flatten creates a nonce-sorted slice of transactions based on the loosely // sorted internal representation. The result of the sorting is cached in case // it's requested again before any modifications are made to the contents. -func (m *txSortedMap) Flatten() types.Transactions { +func (m *sortedMap) Flatten() types.Transactions { // Copy the cache to prevent accidental modifications cache := m.flatten() txs := make(types.Transactions, len(cache)) @@ -240,36 +240,36 @@ func (m *txSortedMap) Flatten() types.Transactions { // LastElement returns the last element of a flattened list, thus, the // transaction with the highest nonce -func (m *txSortedMap) LastElement() *types.Transaction { +func (m *sortedMap) LastElement() *types.Transaction { cache := m.flatten() return cache[len(cache)-1] } -// txList is a "list" of transactions belonging to an account, sorted by account +// list is a "list" of transactions belonging to an account, sorted by account // nonce. The same type can be used both for storing contiguous transactions for // the executable/pending queue; and for storing gapped transactions for the non- // executable/future queue, with minor behavioral changes. -type txList struct { - strict bool // Whether nonces are strictly continuous or not - txs *txSortedMap // Heap indexed sorted hash map of the transactions +type list struct { + strict bool // Whether nonces are strictly continuous or not + txs *sortedMap // Heap indexed sorted hash map of the transactions costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance) gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit) } -// newTxList create a new transaction list for maintaining nonce-indexable fast, +// newList create a new transaction list for maintaining nonce-indexable fast, // gapped, sortable transaction lists. -func newTxList(strict bool) *txList { - return &txList{ +func newList(strict bool) *list { + return &list{ strict: strict, - txs: newTxSortedMap(), + txs: newSortedMap(), costcap: new(big.Int), } } // Overlaps returns whether the transaction specified has the same nonce as one // already contained within the list. -func (l *txList) Overlaps(tx *types.Transaction) bool { +func (l *list) Overlaps(tx *types.Transaction) bool { return l.txs.Get(tx.Nonce()) != nil } @@ -278,7 +278,7 @@ func (l *txList) Overlaps(tx *types.Transaction) bool { // // If the new transaction is accepted into the list, the lists' cost and gas // thresholds are also potentially updated. -func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) { +func (l *list) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) { // If there's an older better transaction, abort old := l.txs.Get(tx.Nonce()) if old != nil && old.IsSpecialTransaction() { @@ -319,7 +319,7 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran // Forward removes all transactions from the list with a nonce lower than the // provided threshold. Every removed transaction is returned for any post-removal // maintenance. -func (l *txList) Forward(threshold uint64) types.Transactions { +func (l *list) Forward(threshold uint64) types.Transactions { return l.txs.Forward(threshold) } @@ -332,7 +332,7 @@ func (l *txList) Forward(threshold uint64) types.Transactions { // a point in calculating all the costs or if the balance covers all. If the threshold // is lower than the costgas cap, the caps will be reset to a new high after removing // the newly invalidated transactions. -func (l *txList) Filter(costLimit *big.Int, gasLimit uint64, trc21Issuers map[common.Address]*big.Int, number *big.Int) (types.Transactions, types.Transactions) { +func (l *list) Filter(costLimit *big.Int, gasLimit uint64, trc21Issuers map[common.Address]*big.Int, number *big.Int) (types.Transactions, types.Transactions) { // If all transactions are below the threshold, short circuit if l.costcap.Cmp(costLimit) <= 0 && l.gascap <= gasLimit { return nil, nil @@ -371,14 +371,14 @@ func (l *txList) Filter(costLimit *big.Int, gasLimit uint64, trc21Issuers map[co // Cap places a hard limit on the number of items, returning all transactions // exceeding that limit. -func (l *txList) Cap(threshold int) types.Transactions { +func (l *list) Cap(threshold int) types.Transactions { return l.txs.Cap(threshold) } // Remove deletes a transaction from the maintained list, returning whether the // transaction was found, and also returning any transaction invalidated due to // the deletion (strict mode only). -func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) { +func (l *list) Remove(tx *types.Transaction) (bool, types.Transactions) { // Remove the transaction from the set nonce := tx.Nonce() if removed := l.txs.Remove(nonce); !removed { @@ -398,30 +398,30 @@ func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) { // Note, all transactions with nonces lower than start will also be returned to // prevent getting into and invalid state. This is not something that should ever // happen but better to be self correcting than failing! -func (l *txList) Ready(start uint64) types.Transactions { +func (l *list) Ready(start uint64) types.Transactions { return l.txs.Ready(start) } // Len returns the length of the transaction list. -func (l *txList) Len() int { +func (l *list) Len() int { return l.txs.Len() } // Empty returns whether the list of transactions is empty or not. -func (l *txList) Empty() bool { +func (l *list) Empty() bool { return l.Len() == 0 } // Flatten creates a nonce-sorted slice of transactions based on the loosely // sorted internal representation. The result of the sorting is cached in case // it's requested again before any modifications are made to the contents. -func (l *txList) Flatten() types.Transactions { +func (l *list) Flatten() types.Transactions { return l.txs.Flatten() } // LastElement returns the last element of a flattened list, thus, the // transaction with the highest nonce -func (l *txList) LastElement() *types.Transaction { +func (l *list) LastElement() *types.Transaction { return l.txs.LastElement() } @@ -477,7 +477,7 @@ func (h *priceHeap) Pop() interface{} { return x } -// txPricedList is a price-sorted heap to allow operating on transactions pool +// pricedList is a price-sorted heap to allow operating on transactions pool // contents in a price-incrementing way. It's built opon the all transactions // in txpool but only interested in the remote part. It means only remote transactions // will be considered for tracking, sorting, eviction, etc. @@ -488,8 +488,8 @@ func (h *priceHeap) Pop() interface{} { // In some cases (during a congestion, when blocks are full) the urgent heap can provide // better candidates for inclusion while in other cases (at the top of the baseFee peak) // the floating heap is better. When baseFee is decreasing they behave similarly. -type txPricedList struct { - all *txLookup // Pointer to the map of all transactions +type pricedList struct { + all *lookup // Pointer to the map of all transactions urgent, floating priceHeap // Heaps of prices of all the stored **remote** transactions stales int64 // Number of stale price points to (re-heap trigger) reheapMu sync.Mutex // Mutex asserts that only one routine is reheaping the list @@ -501,15 +501,15 @@ const ( floatingRatio = 1 ) -// newTxPricedList creates a new price-sorted transaction heap. -func newTxPricedList(all *txLookup) *txPricedList { - return &txPricedList{ +// newPricedList creates a new price-sorted transaction heap. +func newPricedList(all *lookup) *pricedList { + return &pricedList{ all: all, } } // Put inserts a new transaction into the heap. -func (l *txPricedList) Put(tx *types.Transaction, local bool) { +func (l *pricedList) Put(tx *types.Transaction, local bool) { if local { return } @@ -520,7 +520,7 @@ func (l *txPricedList) Put(tx *types.Transaction, local bool) { // Removed notifies the prices transaction list that an old transaction dropped // from the pool. The list will just keep a counter of stale objects and update // the heap if a large enough ratio of transactions go stale. -func (l *txPricedList) Removed(count int) { +func (l *pricedList) Removed(count int) { // Bump the stale counter, but exit if still too low (< 25%) stales := atomic.AddInt64(&l.stales, int64(count)) if int(stales) <= (len(l.urgent.list)+len(l.floating.list))/4 { @@ -532,7 +532,7 @@ func (l *txPricedList) Removed(count int) { // Underpriced checks whether a transaction is cheaper than (or as cheap as) the // lowest priced (remote) transaction currently being tracked. -func (l *txPricedList) Underpriced(tx *types.Transaction) bool { +func (l *pricedList) Underpriced(tx *types.Transaction) bool { // Note: with two queues, being underpriced is defined as being worse than the worst item // in all non-empty queues if there is any. If both queues are empty then nothing is underpriced. return (l.underpricedFor(&l.urgent, tx) || len(l.urgent.list) == 0) && @@ -542,7 +542,7 @@ func (l *txPricedList) Underpriced(tx *types.Transaction) bool { // underpricedFor checks whether a transaction is cheaper than (or as cheap as) the // lowest priced (remote) transaction in the given heap. -func (l *txPricedList) underpricedFor(h *priceHeap, tx *types.Transaction) bool { +func (l *pricedList) underpricedFor(h *priceHeap, tx *types.Transaction) bool { // Discard stale price points if found at the heap start for len(h.list) > 0 { head := h.list[0] @@ -566,7 +566,7 @@ func (l *txPricedList) underpricedFor(h *priceHeap, tx *types.Transaction) bool // priced list and returns them for further removal from the entire pool. // // Note local transaction won't be considered for eviction. -func (l *txPricedList) Discard(slots int, force bool) (types.Transactions, bool) { +func (l *pricedList) Discard(slots int, force bool) (types.Transactions, bool) { drop := make(types.Transactions, 0, slots) // Remote underpriced transactions to drop for slots > 0 { if len(l.urgent.list)*floatingRatio > len(l.floating.list)*urgentRatio || floatingRatio == 0 { @@ -605,7 +605,7 @@ func (l *txPricedList) Discard(slots int, force bool) (types.Transactions, bool) } // Reheap forcibly rebuilds the heap based on the current remote transaction set. -func (l *txPricedList) Reheap() { +func (l *pricedList) Reheap() { l.reheapMu.Lock() defer l.reheapMu.Unlock() start := time.Now() @@ -633,7 +633,7 @@ func (l *txPricedList) Reheap() { // SetBaseFee updates the base fee and triggers a re-heap. Note that Removed is not // necessary to call right before SetBaseFee when processing a new block. -func (l *txPricedList) SetBaseFee(baseFee *big.Int) { +func (l *pricedList) SetBaseFee(baseFee *big.Int) { l.urgent.baseFee = baseFee l.Reheap() } diff --git a/core/tx_list_test.go b/core/txpool/list_test.go similarity index 84% rename from core/tx_list_test.go rename to core/txpool/list_test.go index 36a0196f1eb3..10fe2c940566 100644 --- a/core/tx_list_test.go +++ b/core/txpool/list_test.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package core +package txpool import ( "math/big" @@ -27,7 +27,7 @@ import ( // Tests that transactions can be added to strict lists and list contents and // nonce boundaries are correctly maintained. -func TestStrictTxListAdd(t *testing.T) { +func TestStrictListAdd(t *testing.T) { // Generate a list of transactions to insert key, _ := crypto.GenerateKey() @@ -36,9 +36,9 @@ func TestStrictTxListAdd(t *testing.T) { txs[i] = transaction(uint64(i), 0, key) } // Insert the transactions in a random order - list := newTxList(true) + list := newList(true) for _, v := range rand.Perm(len(txs)) { - list.Add(txs[v], DefaultTxPoolConfig.PriceBump) + list.Add(txs[v], DefaultConfig.PriceBump) } // Verify internal state if len(list.txs.items) != len(txs) { @@ -51,7 +51,7 @@ func TestStrictTxListAdd(t *testing.T) { } } -func BenchmarkTxListAdd(t *testing.B) { +func BenchmarkListAdd(t *testing.B) { // Generate a list of transactions to insert key, _ := crypto.GenerateKey() @@ -60,11 +60,11 @@ func BenchmarkTxListAdd(t *testing.B) { txs[i] = transaction(uint64(i), 0, key) } // Insert the transactions in a random order - list := newTxList(true) - priceLimit := big.NewInt(int64(DefaultTxPoolConfig.PriceLimit)) + list := newList(true) + priceLimit := big.NewInt(int64(DefaultConfig.PriceLimit)) t.ResetTimer() for _, v := range rand.Perm(len(txs)) { - list.Add(txs[v], DefaultTxPoolConfig.PriceBump) - list.Filter(priceLimit, DefaultTxPoolConfig.PriceBump, nil, nil) + list.Add(txs[v], DefaultConfig.PriceBump) + list.Filter(priceLimit, DefaultConfig.PriceBump, nil, nil) } } diff --git a/core/tx_noncer.go b/core/txpool/noncer.go similarity index 81% rename from core/tx_noncer.go rename to core/txpool/noncer.go index 83c3118c0ac5..c9854a4238bd 100644 --- a/core/tx_noncer.go +++ b/core/txpool/noncer.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package core +package txpool import ( "sync" @@ -23,18 +23,18 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/state" ) -// txNoncer is a tiny virtual state database to manage the executable nonces of +// noncer is a tiny virtual state database to manage the executable nonces of // accounts in the pool, falling back to reading from a real state database if // an account is unknown. -type txNoncer struct { +type noncer struct { fallback *state.StateDB nonces map[common.Address]uint64 lock sync.Mutex } -// newTxNoncer creates a new virtual state database to track the pool nonces. -func newTxNoncer(statedb *state.StateDB) *txNoncer { - return &txNoncer{ +// newNoncer creates a new virtual state database to track the pool nonces. +func newNoncer(statedb *state.StateDB) *noncer { + return &noncer{ fallback: statedb.Copy(), nonces: make(map[common.Address]uint64), } @@ -42,7 +42,7 @@ func newTxNoncer(statedb *state.StateDB) *txNoncer { // get returns the current nonce of an account, falling back to a real state // database if the account is unknown. -func (txn *txNoncer) get(addr common.Address) uint64 { +func (txn *noncer) get(addr common.Address) uint64 { // We use mutex for get operation is the underlying // state will mutate db even for read access. txn.lock.Lock() @@ -58,7 +58,7 @@ func (txn *txNoncer) get(addr common.Address) uint64 { // set inserts a new virtual nonce into the virtual state database to be returned // whenever the pool requests it instead of reaching into the real state database. -func (txn *txNoncer) set(addr common.Address, nonce uint64) { +func (txn *noncer) set(addr common.Address, nonce uint64) { txn.lock.Lock() defer txn.lock.Unlock() @@ -67,7 +67,7 @@ func (txn *txNoncer) set(addr common.Address, nonce uint64) { // setIfLower updates a new virtual nonce into the virtual state database if the // the new one is lower. -func (txn *txNoncer) setIfLower(addr common.Address, nonce uint64) { +func (txn *noncer) setIfLower(addr common.Address, nonce uint64) { txn.lock.Lock() defer txn.lock.Unlock() @@ -83,7 +83,7 @@ func (txn *txNoncer) setIfLower(addr common.Address, nonce uint64) { } // setAll sets the nonces for all accounts to the given map. -func (txn *txNoncer) setAll(all map[common.Address]uint64) { +func (txn *noncer) setAll(all map[common.Address]uint64) { txn.lock.Lock() defer txn.lock.Unlock() diff --git a/core/order_pool.go b/core/txpool/order_pool.go similarity index 98% rename from core/order_pool.go rename to core/txpool/order_pool.go index 90ea6fb10388..87ef9e80dc65 100644 --- a/core/order_pool.go +++ b/core/txpool/order_pool.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package core +package txpool import ( "errors" @@ -29,6 +29,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common/prque" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" + "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/event" @@ -84,7 +85,7 @@ type blockChainXDCx interface { GetBlock(hash common.Hash, number uint64) *types.Block OrderStateAt(block *types.Block) (*tradingstate.TradingStateDB, error) StateAt(root common.Hash) (*state.StateDB, error) - SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription + SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription Engine() consensus.Engine // GetHeader returns the hash corresponding to their hash. GetHeader(common.Hash, uint64) *types.Header @@ -133,7 +134,7 @@ type OrderPool struct { txFeed event.Feed scope event.SubscriptionScope - chainHeadCh chan ChainHeadEvent + chainHeadCh chan core.ChainHeadEvent chainHeadSub event.Subscription signer types.OrderSigner mu sync.RWMutex @@ -170,7 +171,7 @@ func NewOrderPool(chainconfig *params.ChainConfig, chain blockChainXDCx) *OrderP queue: make(map[common.Address]*ordertxList), beats: make(map[common.Address]time.Time), all: make(map[common.Hash]*types.OrderTransaction), - chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize), + chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), } pool.locals = newOrderAccountSet(pool.signer) pool.reset(nil, chain.CurrentBlock()) @@ -340,7 +341,7 @@ func (pool *OrderPool) Stop() { // SubscribeTxPreEvent registers a subscription of TxPreEvent and // starts sending event to the given channel. -func (pool *OrderPool) SubscribeTxPreEvent(ch chan<- OrderTxPreEvent) event.Subscription { +func (pool *OrderPool) SubscribeTxPreEvent(ch chan<- core.OrderTxPreEvent) event.Subscription { return pool.scope.Track(pool.txFeed.Subscribe(ch)) } @@ -464,7 +465,7 @@ func (pool *OrderPool) validateOrder(tx *types.OrderTransaction) error { if orderType == OrderTypeLimit { XDPoSEngine, ok := pool.chain.Engine().(*XDPoS.XDPoS) if !ok { - return ErrNotXDPoS + return core.ErrNotXDPoS } XDCXServ := XDPoSEngine.GetXDCXService() if XDCXServ == nil { @@ -550,10 +551,10 @@ func (pool *OrderPool) validateTx(tx *types.OrderTransaction, local bool) error } // Ensure the transaction adheres to nonce ordering if pool.currentOrderState.GetNonce(from.Hash()) > tx.Nonce() { - return ErrNonceTooLow + return core.ErrNonceTooLow } if pool.pendingState.GetNonce(from.Hash())+common.LimitThresholdNonceInQueue < tx.Nonce() { - return ErrNonceTooHigh + return core.ErrNonceTooHigh } return nil @@ -603,7 +604,7 @@ func (pool *OrderPool) add(tx *types.OrderTransaction, local bool) (bool, error) pool.journalTx(from, tx) log.Debug("Pooled new executable transaction", "hash", hash, "useraddress", tx.UserAddress().Hex(), "nonce", tx.Nonce(), "status", tx.Status(), "orderid", tx.OrderID()) - go pool.txFeed.Send(OrderTxPreEvent{tx}) + go pool.txFeed.Send(core.OrderTxPreEvent{Tx: tx}) return old != nil, nil } @@ -690,7 +691,7 @@ func (pool *OrderPool) promoteTx(addr common.Address, hash common.Hash, tx *type pool.beats[addr] = time.Now() pool.pendingState.SetNonce(addr.Hash(), tx.Nonce()+1) log.Debug("promoteTx txFeed.Send", "addr", tx.UserAddress().Hex(), "nonce", tx.Nonce(), "ohash", tx.OrderHash().Hex(), "status", tx.Status(), "orderid", tx.OrderID()) - go pool.txFeed.Send(OrderTxPreEvent{tx}) + go pool.txFeed.Send(core.OrderTxPreEvent{Tx: tx}) } // AddLocal enqueues a single transaction into the pool if it is valid, marking diff --git a/core/order_pool_test.go b/core/txpool/order_pool_test.go similarity index 99% rename from core/order_pool_test.go rename to core/txpool/order_pool_test.go index 87cbfdad1ba9..c5e161856188 100644 --- a/core/order_pool_test.go +++ b/core/txpool/order_pool_test.go @@ -1,4 +1,4 @@ -package core +package txpool import ( "context" diff --git a/core/order_tx_journal.go b/core/txpool/order_tx_journal.go similarity index 99% rename from core/order_tx_journal.go rename to core/txpool/order_tx_journal.go index 471c2f34f9c7..cbcb49c7b72b 100644 --- a/core/order_tx_journal.go +++ b/core/txpool/order_tx_journal.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package core +package txpool import ( "io" diff --git a/core/order_tx_list.go b/core/txpool/order_tx_list.go similarity index 99% rename from core/order_tx_list.go rename to core/txpool/order_tx_list.go index 5135bfe4fd47..60df14e8586d 100644 --- a/core/order_tx_list.go +++ b/core/txpool/order_tx_list.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package core +package txpool import ( "container/heap" diff --git a/core/tx_pool.go b/core/txpool/txpool.go similarity index 93% rename from core/tx_pool.go rename to core/txpool/txpool.go index 573ba44f4ece..14fb416384b2 100644 --- a/core/tx_pool.go +++ b/core/txpool/txpool.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package core +package txpool import ( "errors" @@ -30,6 +30,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common/prque" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/misc" + "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/event" @@ -63,10 +64,6 @@ var ( // ErrInvalidSender is returned if the transaction contains an invalid signature. ErrInvalidSender = errors.New("invalid sender") - // ErrNonceTooLow is returned if the nonce of a transaction is lower than the - // one present in the local chain. - ErrNonceTooLow = errors.New("nonce too low") - // ErrUnderpriced is returned if a transaction's gas price is below the minimum // configured for the transaction pool. ErrUnderpriced = errors.New("transaction underpriced") @@ -79,14 +76,6 @@ var ( // with a different one without the required price bump. ErrReplaceUnderpriced = errors.New("replacement transaction underpriced") - // ErrInsufficientFunds is returned if the total cost of executing a transaction - // is higher than the balance of the user's account. - ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value") - - // ErrIntrinsicGas is returned if the transaction is specified to use less gas - // than required to start the invocation. - ErrIntrinsicGas = errors.New("intrinsic gas too low") - // ErrGasLimit is returned if a transaction's requested gas limit exceeds the // maximum allowance of the current block. ErrGasLimit = errors.New("exceeds block gas limit") @@ -134,6 +123,7 @@ var ( invalidTxMeter = metrics.NewRegisteredMeter("txpool/invalid", nil) underpricedTxMeter = metrics.NewRegisteredMeter("txpool/underpriced", nil) overflowedTxMeter = metrics.NewRegisteredMeter("txpool/overflowed", nil) + // throttleTxMeter counts how many transactions are rejected due to too-many-changes between // txpool reorgs. throttleTxMeter = metrics.NewRegisteredMeter("txpool/throttle", nil) @@ -167,7 +157,7 @@ type blockChain interface { CurrentBlock() *types.Block GetBlock(hash common.Hash, number uint64) *types.Block StateAt(root common.Hash) (*state.StateDB, error) - SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription + SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription // Engine retrieves the chain's consensus engine. Engine() consensus.Engine @@ -182,8 +172,8 @@ type blockChain interface { Config() *params.ChainConfig } -// TxPoolConfig are the configuration parameters of the transaction pool. -type TxPoolConfig struct { +// Config are the configuration parameters of the transaction pool. +type Config struct { Locals []common.Address // Addresses that should be treated by default as local NoLocals bool // Whether local transaction handling should be disabled Journal string // Journal of local transactions to survive node restarts @@ -200,9 +190,9 @@ type TxPoolConfig struct { Lifetime time.Duration // Maximum amount of time non-executable transaction are queued } -// DefaultTxPoolConfig contains the default configurations for the transaction +// DefaultConfig contains the default configurations for the transaction // pool. -var DefaultTxPoolConfig = TxPoolConfig{ +var DefaultConfig = Config{ Journal: "transactions.rlp", Rejournal: time.Hour, @@ -219,39 +209,39 @@ var DefaultTxPoolConfig = TxPoolConfig{ // sanitize checks the provided user configurations and changes anything that's // unreasonable or unworkable. -func (config *TxPoolConfig) sanitize() TxPoolConfig { +func (config *Config) sanitize() Config { conf := *config if conf.Rejournal < time.Second { log.Warn("Sanitizing invalid txpool journal time", "provided", conf.Rejournal, "updated", time.Second) conf.Rejournal = time.Second } if conf.PriceLimit < 1 { - log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultTxPoolConfig.PriceLimit) - conf.PriceLimit = DefaultTxPoolConfig.PriceLimit + log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultConfig.PriceLimit) + conf.PriceLimit = DefaultConfig.PriceLimit } if conf.PriceBump < 1 { - log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", DefaultTxPoolConfig.PriceBump) - conf.PriceBump = DefaultTxPoolConfig.PriceBump + log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", DefaultConfig.PriceBump) + conf.PriceBump = DefaultConfig.PriceBump } if conf.AccountSlots < 1 { - log.Warn("Sanitizing invalid txpool account slots", "provided", conf.AccountSlots, "updated", DefaultTxPoolConfig.AccountSlots) - conf.AccountSlots = DefaultTxPoolConfig.AccountSlots + log.Warn("Sanitizing invalid txpool account slots", "provided", conf.AccountSlots, "updated", DefaultConfig.AccountSlots) + conf.AccountSlots = DefaultConfig.AccountSlots } if conf.GlobalSlots < 1 { - log.Warn("Sanitizing invalid txpool global slots", "provided", conf.GlobalSlots, "updated", DefaultTxPoolConfig.GlobalSlots) - conf.GlobalSlots = DefaultTxPoolConfig.GlobalSlots + log.Warn("Sanitizing invalid txpool global slots", "provided", conf.GlobalSlots, "updated", DefaultConfig.GlobalSlots) + conf.GlobalSlots = DefaultConfig.GlobalSlots } if conf.AccountQueue < 1 { - log.Warn("Sanitizing invalid txpool account queue", "provided", conf.AccountQueue, "updated", DefaultTxPoolConfig.AccountQueue) - conf.AccountQueue = DefaultTxPoolConfig.AccountQueue + log.Warn("Sanitizing invalid txpool account queue", "provided", conf.AccountQueue, "updated", DefaultConfig.AccountQueue) + conf.AccountQueue = DefaultConfig.AccountQueue } if conf.GlobalQueue < 1 { - log.Warn("Sanitizing invalid txpool global queue", "provided", conf.GlobalQueue, "updated", DefaultTxPoolConfig.GlobalQueue) - conf.GlobalQueue = DefaultTxPoolConfig.GlobalQueue + log.Warn("Sanitizing invalid txpool global queue", "provided", conf.GlobalQueue, "updated", DefaultConfig.GlobalQueue) + conf.GlobalQueue = DefaultConfig.GlobalQueue } if conf.Lifetime < 1 { - log.Warn("Sanitizing invalid txpool lifetime", "provided", conf.Lifetime, "updated", DefaultTxPoolConfig.Lifetime) - conf.Lifetime = DefaultTxPoolConfig.Lifetime + log.Warn("Sanitizing invalid txpool lifetime", "provided", conf.Lifetime, "updated", DefaultConfig.Lifetime) + conf.Lifetime = DefaultConfig.Lifetime } return conf } @@ -264,7 +254,7 @@ func (config *TxPoolConfig) sanitize() TxPoolConfig { // current state) and future transactions. Transactions move between those // two states over time as they are received and processed. type TxPool struct { - config TxPoolConfig + config Config chainconfig *params.ChainConfig chain blockChain gasPrice *big.Int @@ -277,19 +267,19 @@ type TxPool struct { eip1559 bool // Fork indicator whether we are using EIP-1559 type transactions. currentState *state.StateDB // Current state in the blockchain head - pendingNonces *txNoncer // Pending state tracking virtual nonces + pendingNonces *noncer // Pending state tracking virtual nonces currentMaxGas uint64 // Current gas limit for transaction caps locals *accountSet // Set of local transaction to exempt from eviction rules - journal *txJournal // Journal of local transaction to back up to disk + journal *journal // Journal of local transaction to back up to disk - pending map[common.Address]*txList // All currently processable transactions - queue map[common.Address]*txList // Queued but non-processable transactions + pending map[common.Address]*list // All currently processable transactions + queue map[common.Address]*list // Queued but non-processable transactions beats map[common.Address]time.Time // Last heartbeat from each known account - all *txLookup // All transactions to allow lookups - priced *txPricedList // All transactions sorted by price + all *lookup // All transactions to allow lookups + priced *pricedList // All transactions sorted by price - chainHeadCh chan ChainHeadEvent + chainHeadCh chan core.ChainHeadEvent chainHeadSub event.Subscription reqResetCh chan *txpoolResetRequest reqPromoteCh chan *accountSet @@ -311,7 +301,7 @@ type txpoolResetRequest struct { // NewTxPool creates a new transaction pool to gather, sort and filter inbound // transactions from the network. -func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain blockChain) *TxPool { +func NewTxPool(config Config, chainconfig *params.ChainConfig, chain blockChain) *TxPool { // Sanitize the input to ensure no vulnerable gas prices are set config = (&config).sanitize() @@ -321,11 +311,11 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block chainconfig: chainconfig, chain: chain, signer: types.LatestSigner(chainconfig), - pending: make(map[common.Address]*txList), - queue: make(map[common.Address]*txList), + pending: make(map[common.Address]*list), + queue: make(map[common.Address]*list), beats: make(map[common.Address]time.Time), - all: newTxLookup(), - chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize), + all: newLookup(), + chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), reqResetCh: make(chan *txpoolResetRequest), reqPromoteCh: make(chan *accountSet), queueTxEventCh: make(chan *types.Transaction), @@ -340,7 +330,7 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block log.Info("Setting new local account", "address", addr) pool.locals.add(addr) } - pool.priced = newTxPricedList(pool.all) + pool.priced = newPricedList(pool.all) pool.reset(nil, chain.CurrentBlock().Header()) // Start the reorg loop early so it can handle requests generated during journal loading. @@ -463,7 +453,7 @@ func (pool *TxPool) Stop() { // SubscribeNewTxsEvent registers a subscription of NewTxsEvent and // starts sending event to the given channel. -func (pool *TxPool) SubscribeNewTxsEvent(ch chan<- NewTxsEvent) event.Subscription { +func (pool *TxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription { return pool.scope.Track(pool.txFeed.Subscribe(ch)) } @@ -630,11 +620,11 @@ func (pool *TxPool) GetSender(tx *types.Transaction) (common.Address, error) { func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { // Accept only legacy transactions until EIP-2718/2930 activates. if !pool.eip2718 && tx.Type() != types.LegacyTxType { - return ErrTxTypeNotSupported + return core.ErrTxTypeNotSupported } // Reject dynamic fee transactions until EIP-1559 activates. if !pool.eip1559 && tx.Type() == types.DynamicFeeTxType { - return ErrTxTypeNotSupported + return core.ErrTxTypeNotSupported } // Reject transactions over defined size to prevent DOS attacks if uint64(tx.Size()) > txMaxSize { @@ -659,14 +649,14 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { } // Sanity check for extremely large numbers if tx.GasFeeCap().BitLen() > 256 { - return ErrFeeCapVeryHigh + return core.ErrFeeCapVeryHigh } if tx.GasTipCap().BitLen() > 256 { - return ErrTipVeryHigh + return core.ErrTipVeryHigh } // Ensure gasFeeCap is greater than or equal to gasTipCap. if tx.GasFeeCapIntCmp(tx.GasTipCap()) < 0 { - return ErrTipAboveFeeCap + return core.ErrTipAboveFeeCap } // Make sure the transaction is signed properly. from, err := types.Sender(pool.signer, tx) @@ -681,10 +671,10 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { } // Ensure the transaction adheres to nonce ordering if pool.currentState.GetNonce(from) > tx.Nonce() { - return ErrNonceTooLow + return core.ErrNonceTooLow } if pool.pendingNonces.get(from)+common.LimitThresholdNonceInQueue < tx.Nonce() { - return ErrNonceTooHigh + return core.ErrNonceTooHigh } // Transactor should have enough funds to cover the costs // cost == V + GP * GL @@ -701,24 +691,24 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { if value, ok := pool.trc21FeeCapacity[*tx.To()]; ok { feeCapacity = value if !state.ValidateTRC21Tx(pool.currentState, from, *tx.To(), tx.Data()) { - return ErrInsufficientFunds + return core.ErrInsufficientFunds } cost = tx.TxCost(number) } } if new(big.Int).Add(balance, feeCapacity).Cmp(cost) < 0 { - return ErrInsufficientFunds + return core.ErrInsufficientFunds } if tx.To() == nil || (tx.To() != nil && !tx.IsSpecialTransaction()) { // Ensure the transaction has more gas than the basic tx fee. - intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true) + intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true) if err != nil { return err } // Exclude check smart contract sign address. if tx.Gas() < intrGas { - return ErrIntrinsicGas + return core.ErrIntrinsicGas } // Check zero gas price. @@ -742,13 +732,13 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { // validate minFee slot for XDCZ if tx.IsXDCZApplyTransaction() { copyState := pool.currentState.Copy() - return ValidateXDCZApplyTransaction(pool.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:])) + return core.ValidateXDCZApplyTransaction(pool.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:])) } // validate balance slot, token decimal for XDCX if tx.IsXDCXApplyTransaction() { copyState := pool.currentState.Copy() - return ValidateXDCXApplyTransaction(pool.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:])) + return core.ValidateXDCXApplyTransaction(pool.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:])) } return nil } @@ -870,7 +860,7 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction, local boo // Try to insert the transaction into the future queue from, _ := types.Sender(pool.signer, tx) // already validated if pool.queue[from] == nil { - pool.queue[from] = newTxList(false) + pool.queue[from] = newList(false) } inserted, old := pool.queue[from].Add(tx, pool.config.PriceBump) if !inserted { @@ -922,7 +912,7 @@ func (pool *TxPool) journalTx(from common.Address, tx *types.Transaction) { func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) bool { // Try to insert the transaction into the pending queue if pool.pending[addr] == nil { - pool.pending[addr] = newTxList(true) + pool.pending[addr] = newList(true) } list := pool.pending[addr] @@ -955,7 +945,7 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T func (pool *TxPool) promoteSpecialTx(addr common.Address, tx *types.Transaction, isLocal bool) (bool, error) { // Try to insert the transaction into the pending queue if pool.pending[addr] == nil { - pool.pending[addr] = newTxList(true) + pool.pending[addr] = newList(true) } list := pool.pending[addr] @@ -986,7 +976,7 @@ func (pool *TxPool) promoteSpecialTx(addr common.Address, tx *types.Transaction, // Set the potentially new pending nonce and notify any subsystems of the new tx pool.beats[addr] = time.Now() pool.pendingNonces.set(addr, tx.Nonce()+1) - go pool.txFeed.Send(NewTxsEvent{types.Transactions{tx}}) + go pool.txFeed.Send(core.NewTxsEvent{Txs: types.Transactions{tx}}) return true, nil } @@ -1228,7 +1218,7 @@ func (pool *TxPool) scheduleReorgLoop() { launchNextRun bool reset *txpoolResetRequest dirtyAccounts *accountSet - queuedEvents = make(map[common.Address]*txSortedMap) + queuedEvents = make(map[common.Address]*sortedMap) ) for { // Launch next background reorg if needed @@ -1241,7 +1231,7 @@ func (pool *TxPool) scheduleReorgLoop() { launchNextRun = false reset, dirtyAccounts = nil, nil - queuedEvents = make(map[common.Address]*txSortedMap) + queuedEvents = make(map[common.Address]*sortedMap) } select { @@ -1270,7 +1260,7 @@ func (pool *TxPool) scheduleReorgLoop() { // request one later if they want the events sent. addr, _ := types.Sender(pool.signer, tx) if _, ok := queuedEvents[addr]; !ok { - queuedEvents[addr] = newTxSortedMap() + queuedEvents[addr] = newSortedMap() } queuedEvents[addr].Put(tx) @@ -1289,7 +1279,7 @@ func (pool *TxPool) scheduleReorgLoop() { } // runReorg runs reset and promoteExecutables on behalf of scheduleReorgLoop. -func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirtyAccounts *accountSet, events map[common.Address]*txSortedMap) { +func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirtyAccounts *accountSet, events map[common.Address]*sortedMap) { defer func(t0 time.Time) { reorgDurationTimer.Update(time.Since(t0)) }(time.Now()) @@ -1352,7 +1342,7 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt for _, tx := range promoted { addr, _ := types.Sender(pool.signer, tx) if _, ok := events[addr]; !ok { - events[addr] = newTxSortedMap() + events[addr] = newSortedMap() } events[addr].Put(tx) } @@ -1361,7 +1351,7 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt for _, set := range events { txs = append(txs, set.Flatten()...) } - pool.txFeed.Send(NewTxsEvent{txs}) + pool.txFeed.Send(core.NewTxsEvent{Txs: txs}) } } @@ -1388,7 +1378,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { if rem == nil { // This can happen if a setHead is performed, where we simply discard the old // head from the chain. - // If that is the case, we don't have the lost transactions any more, and + // If that is the case, we don't have the lost transactions anymore, and // there's nothing to add if newNum >= oldNum { // If we reorged to a same or higher number, then it's not a case of setHead @@ -1442,12 +1432,12 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { } pool.currentState = statedb pool.trc21FeeCapacity = state.GetTRC21FeeCapacityFromStateWithCache(newHead.Root, statedb) - pool.pendingNonces = newTxNoncer(statedb) + pool.pendingNonces = newNoncer(statedb) pool.currentMaxGas = newHead.GasLimit // Inject any transactions discarded due to reorgs log.Debug("Reinjecting stale transactions", "count", len(reinject)) - senderCacher.recover(pool.signer, reinject) + core.SenderCacher.Recover(pool.signer, reinject) pool.addTxsLocked(reinject, false) // Update all fork indicator by next pending block number. @@ -1718,8 +1708,6 @@ func (pool *TxPool) demoteUnexecutables() { pool.enqueueTx(hash, tx, false, false) } pendingGauge.Dec(int64(len(gapped))) - // This might happen in a reorg, so log it to the metering - blockReorgInvalidatedTx.Mark(int64(len(gapped))) } // Delete the entire pending entry if it became empty. if list.Empty() { @@ -1814,7 +1802,7 @@ func (as *accountSet) merge(other *accountSet) { as.cache = nil } -// txLookup is used internally by TxPool to track transactions while allowing +// lookup is used internally by TxPool to track transactions while allowing // lookup without mutex contention. // // Note, although this type is properly protected against concurrent access, it @@ -1826,16 +1814,16 @@ func (as *accountSet) merge(other *accountSet) { // // This lookup set combines the notion of "local transactions", which is useful // to build upper-level structure. -type txLookup struct { +type lookup struct { slots int lock sync.RWMutex locals map[common.Hash]*types.Transaction remotes map[common.Hash]*types.Transaction } -// newTxLookup returns a new txLookup structure. -func newTxLookup() *txLookup { - return &txLookup{ +// newLookup returns a new lookup structure. +func newLookup() *lookup { + return &lookup{ locals: make(map[common.Hash]*types.Transaction), remotes: make(map[common.Hash]*types.Transaction), } @@ -1844,7 +1832,7 @@ func newTxLookup() *txLookup { // Range calls f on each key and value present in the map. The callback passed // should return the indicator whether the iteration needs to be continued. // Callers need to specify which set (or both) to be iterated. -func (t *txLookup) Range(f func(hash common.Hash, tx *types.Transaction, local bool) bool, local bool, remote bool) { +func (t *lookup) Range(f func(hash common.Hash, tx *types.Transaction, local bool) bool, local bool, remote bool) { t.lock.RLock() defer t.lock.RUnlock() @@ -1865,7 +1853,7 @@ func (t *txLookup) Range(f func(hash common.Hash, tx *types.Transaction, local b } // Get returns a transaction if it exists in the lookup, or nil if not found. -func (t *txLookup) Get(hash common.Hash) *types.Transaction { +func (t *lookup) Get(hash common.Hash) *types.Transaction { t.lock.RLock() defer t.lock.RUnlock() @@ -1876,7 +1864,7 @@ func (t *txLookup) Get(hash common.Hash) *types.Transaction { } // GetLocal returns a transaction if it exists in the lookup, or nil if not found. -func (t *txLookup) GetLocal(hash common.Hash) *types.Transaction { +func (t *lookup) GetLocal(hash common.Hash) *types.Transaction { t.lock.RLock() defer t.lock.RUnlock() @@ -1884,7 +1872,7 @@ func (t *txLookup) GetLocal(hash common.Hash) *types.Transaction { } // GetRemote returns a transaction if it exists in the lookup, or nil if not found. -func (t *txLookup) GetRemote(hash common.Hash) *types.Transaction { +func (t *lookup) GetRemote(hash common.Hash) *types.Transaction { t.lock.RLock() defer t.lock.RUnlock() @@ -1892,7 +1880,7 @@ func (t *txLookup) GetRemote(hash common.Hash) *types.Transaction { } // Count returns the current number of transactions in the lookup. -func (t *txLookup) Count() int { +func (t *lookup) Count() int { t.lock.RLock() defer t.lock.RUnlock() @@ -1900,7 +1888,7 @@ func (t *txLookup) Count() int { } // LocalCount returns the current number of local transactions in the lookup. -func (t *txLookup) LocalCount() int { +func (t *lookup) LocalCount() int { t.lock.RLock() defer t.lock.RUnlock() @@ -1908,7 +1896,7 @@ func (t *txLookup) LocalCount() int { } // RemoteCount returns the current number of remote transactions in the lookup. -func (t *txLookup) RemoteCount() int { +func (t *lookup) RemoteCount() int { t.lock.RLock() defer t.lock.RUnlock() @@ -1916,7 +1904,7 @@ func (t *txLookup) RemoteCount() int { } // Slots returns the current number of slots used in the lookup. -func (t *txLookup) Slots() int { +func (t *lookup) Slots() int { t.lock.RLock() defer t.lock.RUnlock() @@ -1924,7 +1912,7 @@ func (t *txLookup) Slots() int { } // Add adds a transaction to the lookup. -func (t *txLookup) Add(tx *types.Transaction, local bool) { +func (t *lookup) Add(tx *types.Transaction, local bool) { t.lock.Lock() defer t.lock.Unlock() @@ -1939,7 +1927,7 @@ func (t *txLookup) Add(tx *types.Transaction, local bool) { } // Remove removes a transaction from the lookup. -func (t *txLookup) Remove(hash common.Hash) { +func (t *lookup) Remove(hash common.Hash) { t.lock.Lock() defer t.lock.Unlock() @@ -1960,7 +1948,7 @@ func (t *txLookup) Remove(hash common.Hash) { // RemoteToLocals migrates the transactions belongs to the given locals to locals // set. The assumption is held the locals set is thread-safe to be used. -func (t *txLookup) RemoteToLocals(locals *accountSet) int { +func (t *lookup) RemoteToLocals(locals *accountSet) int { t.lock.Lock() defer t.lock.Unlock() @@ -1976,7 +1964,7 @@ func (t *txLookup) RemoteToLocals(locals *accountSet) int { } // RemotesBelowTip finds all remote transactions below the given tip threshold. -func (t *txLookup) RemotesBelowTip(threshold *big.Int) types.Transactions { +func (t *lookup) RemotesBelowTip(threshold *big.Int) types.Transactions { found := make(types.Transactions, 0, 128) t.Range(func(hash common.Hash, tx *types.Transaction, local bool) bool { if tx.GasTipCapIntCmp(threshold) < 0 { diff --git a/core/tx_pool_test.go b/core/txpool/txpool_test.go similarity index 92% rename from core/tx_pool_test.go rename to core/txpool/txpool_test.go index cae0b55c8eb6..3b1ac21f62f8 100644 --- a/core/tx_pool_test.go +++ b/core/txpool/txpool_test.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package core +package txpool import ( "crypto/ecdsa" @@ -28,6 +28,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus" + "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" @@ -39,14 +40,14 @@ import ( var ( // testTxPoolConfig is a transaction pool configuration without stateful disk // sideeffects used during testing. - testTxPoolConfig TxPoolConfig + testTxPoolConfig Config // eip1559Config is a chain config with EIP-1559 enabled at block 0. eip1559Config *params.ChainConfig ) func init() { - testTxPoolConfig = DefaultTxPoolConfig + testTxPoolConfig = DefaultConfig testTxPoolConfig.Journal = "" cpy := *params.TestChainConfig @@ -91,7 +92,7 @@ func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) { return bc.statedb, nil } -func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription { +func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { return bc.chainHeadFeed.Subscribe(ch) } @@ -127,11 +128,11 @@ func dynamicFeeTx(nonce uint64, gaslimit uint64, gasFee *big.Int, tip *big.Int, return tx } -func setupTxPool() (*TxPool, *ecdsa.PrivateKey) { - return setupTxPoolWithConfig(params.TestChainConfig) +func setupPool() (*TxPool, *ecdsa.PrivateKey) { + return setupPoolWithConfig(params.TestChainConfig) } -func setupTxPoolWithConfig(config *params.ChainConfig) (*TxPool, *ecdsa.PrivateKey) { +func setupPoolWithConfig(config *params.ChainConfig) (*TxPool, *ecdsa.PrivateKey) { diskdb := rawdb.NewMemoryDatabase() statedb, _ := state.New(common.Hash{}, state.NewDatabase(diskdb)) blockchain := &testBlockChain{statedb, 10000000, new(event.Feed)} @@ -144,8 +145,8 @@ func setupTxPoolWithConfig(config *params.ChainConfig) (*TxPool, *ecdsa.PrivateK return pool, key } -// validateTxPoolInternals checks various consistency invariants within the pool. -func validateTxPoolInternals(pool *TxPool) error { +// validatePoolInternals checks various consistency invariants within the pool. +func validatePoolInternals(pool *TxPool) error { pool.mu.RLock() defer pool.mu.RUnlock() @@ -177,7 +178,7 @@ func validateTxPoolInternals(pool *TxPool) error { // validateEvents checks that the correct number of transaction addition events // were fired on the pool's event feed. -func validateEvents(events chan NewTxsEvent, count int) error { +func validateEvents(events chan core.NewTxsEvent, count int) error { var received []*types.Transaction for len(received) < count { @@ -235,7 +236,7 @@ func (c *testChain) State() (*state.StateDB, error) { // This test simulates a scenario where a new block is imported during a // state reset and tests whether the pending state is in sync with the // block head event that initiated the resetState(). -func TestStateChangeDuringTransactionPoolReset(t *testing.T) { +func TestStateChangeDuringReset(t *testing.T) { t.Parallel() var ( @@ -293,28 +294,28 @@ func testSetNonce(pool *TxPool, addr common.Address, nonce uint64) { func TestInvalidTransactions(t *testing.T) { t.Parallel() - pool, key := setupTxPool() + pool, key := setupPool() defer pool.Stop() tx := transaction(0, 100, key) from, _ := deriveSender(tx) testAddBalance(pool, from, big.NewInt(1)) - if err := pool.AddRemote(tx); err != ErrInsufficientFunds { - t.Error("expected", ErrInsufficientFunds) + if err := pool.AddRemote(tx); err != core.ErrInsufficientFunds { + t.Error("expected", core.ErrInsufficientFunds) } balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(new(big.Int).SetUint64(tx.Gas()), tx.GasPrice())) testAddBalance(pool, from, balance) - if err := pool.AddRemote(tx); err != ErrIntrinsicGas { - t.Error("expected", ErrIntrinsicGas, "got", err) + if err := pool.AddRemote(tx); err != core.ErrIntrinsicGas { + t.Error("expected", core.ErrIntrinsicGas, "got", err) } testSetNonce(pool, from, 1) testAddBalance(pool, from, big.NewInt(0xffffffffffffff)) tx = transaction(0, 100000, key) - if err := pool.AddRemote(tx); err != ErrNonceTooLow { - t.Error("expected", ErrNonceTooLow) + if err := pool.AddRemote(tx); err != core.ErrNonceTooLow { + t.Error("expected", core.ErrNonceTooLow) } tx = transaction(1, 100000, key) @@ -327,10 +328,10 @@ func TestInvalidTransactions(t *testing.T) { } } -func TestTransactionQueue(t *testing.T) { +func TestQueue(t *testing.T) { t.Parallel() - pool, key := setupTxPool() + pool, key := setupPool() defer pool.Stop() tx := transaction(0, 100, key) @@ -359,10 +360,10 @@ func TestTransactionQueue(t *testing.T) { } } -func TestTransactionQueue2(t *testing.T) { +func TestQueue2(t *testing.T) { t.Parallel() - pool, key := setupTxPool() + pool, key := setupPool() defer pool.Stop() tx1 := transaction(0, 100, key) @@ -385,10 +386,10 @@ func TestTransactionQueue2(t *testing.T) { } } -func TestTransactionNegativeValue(t *testing.T) { +func TestNegativeValue(t *testing.T) { t.Parallel() - pool, key := setupTxPool() + pool, key := setupPool() defer pool.Stop() tx, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(-1), 100, big.NewInt(1), nil), types.HomesteadSigner{}, key) @@ -399,43 +400,43 @@ func TestTransactionNegativeValue(t *testing.T) { } } -func TestTransactionTipAboveFeeCap(t *testing.T) { +func TestTipAboveFeeCap(t *testing.T) { t.Parallel() - pool, key := setupTxPoolWithConfig(eip1559Config) + pool, key := setupPoolWithConfig(eip1559Config) defer pool.Stop() tx := dynamicFeeTx(0, 100, big.NewInt(1), big.NewInt(2), key) - if err := pool.AddRemote(tx); err != ErrTipAboveFeeCap { - t.Error("expected", ErrTipAboveFeeCap, "got", err) + if err := pool.AddRemote(tx); err != core.ErrTipAboveFeeCap { + t.Error("expected", core.ErrTipAboveFeeCap, "got", err) } } -func TestTransactionVeryHighValues(t *testing.T) { +func TestVeryHighValues(t *testing.T) { t.Parallel() - pool, key := setupTxPoolWithConfig(eip1559Config) + pool, key := setupPoolWithConfig(eip1559Config) defer pool.Stop() veryBigNumber := big.NewInt(1) veryBigNumber.Lsh(veryBigNumber, 300) tx := dynamicFeeTx(0, 100, big.NewInt(1), veryBigNumber, key) - if err := pool.AddRemote(tx); err != ErrTipVeryHigh { - t.Error("expected", ErrTipVeryHigh, "got", err) + if err := pool.AddRemote(tx); err != core.ErrTipVeryHigh { + t.Error("expected", core.ErrTipVeryHigh, "got", err) } tx2 := dynamicFeeTx(0, 100, veryBigNumber, big.NewInt(1), key) - if err := pool.AddRemote(tx2); err != ErrFeeCapVeryHigh { - t.Error("expected", ErrFeeCapVeryHigh, "got", err) + if err := pool.AddRemote(tx2); err != core.ErrFeeCapVeryHigh { + t.Error("expected", core.ErrFeeCapVeryHigh, "got", err) } } -func TestTransactionChainFork(t *testing.T) { +func TestChainFork(t *testing.T) { t.Parallel() - pool, key := setupTxPool() + pool, key := setupPool() defer pool.Stop() addr := crypto.PubkeyToAddress(key.PublicKey) @@ -462,10 +463,10 @@ func TestTransactionChainFork(t *testing.T) { } } -func TestTransactionDoubleNonce(t *testing.T) { +func TestDoubleNonce(t *testing.T) { t.Parallel() - pool, key := setupTxPool() + pool, key := setupPool() defer pool.Stop() addr := crypto.PubkeyToAddress(key.PublicKey) @@ -514,10 +515,10 @@ func TestTransactionDoubleNonce(t *testing.T) { } } -func TestTransactionMissingNonce(t *testing.T) { +func TestMissingNonce(t *testing.T) { t.Parallel() - pool, key := setupTxPool() + pool, key := setupPool() defer pool.Stop() addr := crypto.PubkeyToAddress(key.PublicKey) @@ -537,11 +538,11 @@ func TestTransactionMissingNonce(t *testing.T) { } } -func TestTransactionNonceRecovery(t *testing.T) { +func TestNonceRecovery(t *testing.T) { t.Parallel() const n = 10 - pool, key := setupTxPool() + pool, key := setupPool() defer pool.Stop() addr := crypto.PubkeyToAddress(key.PublicKey) @@ -563,11 +564,11 @@ func TestTransactionNonceRecovery(t *testing.T) { // Tests that if an account runs out of funds, any pending and queued transactions // are dropped. -func TestTransactionDropping(t *testing.T) { +func TestDropping(t *testing.T) { t.Parallel() // Create a test account and fund it - pool, key := setupTxPool() + pool, key := setupPool() defer pool.Stop() account := crypto.PubkeyToAddress(key.PublicKey) @@ -667,7 +668,7 @@ func TestTransactionDropping(t *testing.T) { // Tests that if a transaction is dropped from the current pending pool (e.g. out // of fund), all consecutive (still valid, but not executable) transactions are // postponed back into the future queue to prevent broadcasting them. -func TestTransactionPostponing(t *testing.T) { +func TestPostponing(t *testing.T) { t.Parallel() // Create the pool to test the postponing with @@ -782,18 +783,18 @@ func TestTransactionPostponing(t *testing.T) { // Tests that if the transaction pool has both executable and non-executable // transactions from an origin account, filling the nonce gap moves all queued // ones into the pending pool. -func TestTransactionGapFilling(t *testing.T) { +func TestGapFilling(t *testing.T) { t.Parallel() // Create a test account and fund it - pool, key := setupTxPool() + pool, key := setupPool() defer pool.Stop() account := crypto.PubkeyToAddress(key.PublicKey) testAddBalance(pool, account, big.NewInt(1000000)) // Keep track of transaction events to ensure all executables get announced - events := make(chan NewTxsEvent, testTxPoolConfig.AccountQueue+5) + events := make(chan core.NewTxsEvent, testTxPoolConfig.AccountQueue+5) sub := pool.txFeed.Subscribe(events) defer sub.Unsubscribe() @@ -812,7 +813,7 @@ func TestTransactionGapFilling(t *testing.T) { if err := validateEvents(events, 1); err != nil { t.Fatalf("original event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } // Fill the nonce gap and ensure all transactions become pending @@ -829,18 +830,18 @@ func TestTransactionGapFilling(t *testing.T) { if err := validateEvents(events, 2); err != nil { t.Fatalf("gap-filling event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } } // Tests that if the transaction count belonging to a single account goes above // some threshold, the higher transactions are dropped to prevent DOS attacks. -func TestTransactionQueueAccountLimiting(t *testing.T) { +func TestQueueAccountLimiting(t *testing.T) { t.Parallel() // Create a test account and fund it - pool, key := setupTxPool() + pool, key := setupPool() defer pool.Stop() account := crypto.PubkeyToAddress(key.PublicKey) @@ -874,14 +875,14 @@ func TestTransactionQueueAccountLimiting(t *testing.T) { // // This logic should not hold for local transactions, unless the local tracking // mechanism is disabled. -func TestTransactionQueueGlobalLimiting(t *testing.T) { - testTransactionQueueGlobalLimiting(t, false) +func TestQueueGlobalLimiting(t *testing.T) { + testQueueGlobalLimiting(t, false) } -func TestTransactionQueueGlobalLimitingNoLocals(t *testing.T) { - testTransactionQueueGlobalLimiting(t, true) +func TestQueueGlobalLimitingNoLocals(t *testing.T) { + testQueueGlobalLimiting(t, true) } -func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) { +func testQueueGlobalLimiting(t *testing.T, nolocals bool) { t.Parallel() // Create the pool to test the limit enforcement with @@ -966,12 +967,12 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) { // // This logic should not hold for local transactions, unless the local tracking // mechanism is disabled. -func TestTransactionQueueTimeLimiting(t *testing.T) { testTransactionQueueTimeLimiting(t, false) } -func TestTransactionQueueTimeLimitingNoLocals(t *testing.T) { - testTransactionQueueTimeLimiting(t, true) +func TestQueueTimeLimiting(t *testing.T) { testQueueTimeLimiting(t, false) } +func TestQueueTimeLimitingNoLocals(t *testing.T) { + testQueueTimeLimiting(t, true) } -func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) { +func testQueueTimeLimiting(t *testing.T, nolocals bool) { common.MinGasPrice = big.NewInt(0) // Reduce the eviction interval to a testable amount defer func(old time.Duration) { evictionInterval = old }(evictionInterval) @@ -1010,7 +1011,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) { if queued != 2 { t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } @@ -1025,7 +1026,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) { if queued != 2 { t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } @@ -1045,7 +1046,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) { t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1) } } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } @@ -1062,7 +1063,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) { if queued != 0 { t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } @@ -1092,7 +1093,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) { if queued != 2 { t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 3) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } @@ -1111,7 +1112,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) { t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1) } } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } } @@ -1119,18 +1120,18 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) { // Tests that even if the transaction count belonging to a single account goes // above some threshold, as long as the transactions are executable, they are // accepted. -func TestTransactionPendingLimiting(t *testing.T) { +func TestPendingLimiting(t *testing.T) { t.Parallel() // Create a test account and fund it - pool, key := setupTxPool() + pool, key := setupPool() defer pool.Stop() account := crypto.PubkeyToAddress(key.PublicKey) testAddBalance(pool, account, big.NewInt(1000000)) testTxPoolConfig.AccountQueue = 10 // Keep track of transaction events to ensure all executables get announced - events := make(chan NewTxsEvent, testTxPoolConfig.AccountQueue) + events := make(chan core.NewTxsEvent, testTxPoolConfig.AccountQueue) sub := pool.txFeed.Subscribe(events) defer sub.Unsubscribe() @@ -1152,7 +1153,7 @@ func TestTransactionPendingLimiting(t *testing.T) { if err := validateEvents(events, int(testTxPoolConfig.AccountQueue)); err != nil { t.Fatalf("event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } } @@ -1160,7 +1161,7 @@ func TestTransactionPendingLimiting(t *testing.T) { // Tests that if the transaction count belonging to multiple accounts go above // some hard threshold, the higher transactions are dropped to prevent DOS // attacks. -func TestTransactionPendingGlobalLimiting(t *testing.T) { +func TestPendingGlobalLimiting(t *testing.T) { t.Parallel() // Create the pool to test the limit enforcement with @@ -1201,7 +1202,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) { if pending > int(config.GlobalSlots) { t.Fatalf("total pending transactions overflow allowance: %d > %d", pending, config.GlobalSlots) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } } @@ -1209,11 +1210,11 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) { // Test the limit on transaction size is enforced correctly. // This test verifies every transaction having allowed size // is added to the pool, and longer transactions are rejected. -func TestTransactionAllowedTxSize(t *testing.T) { +func TestAllowedTxSize(t *testing.T) { t.Parallel() // Create a test account and fund it - pool, key := setupTxPool() + pool, key := setupPool() defer pool.Stop() account := crypto.PubkeyToAddress(key.PublicKey) @@ -1257,13 +1258,13 @@ func TestTransactionAllowedTxSize(t *testing.T) { if queued != 0 { t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } } // Tests that if transactions start being capped, transactions are also removed from 'all' -func TestTransactionCapClearsFromAll(t *testing.T) { +func TestCapClearsFromAll(t *testing.T) { t.Parallel() // Create the pool to test the limit enforcement with @@ -1290,7 +1291,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) { } // Import the batch and verify that limits have been enforced pool.AddRemotes(txs) - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } } @@ -1298,7 +1299,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) { // Tests that if the transaction count belonging to multiple accounts go above // some hard threshold, if they are under the minimum guaranteed slot count then // the transactions are still kept. -func TestTransactionPendingMinimumAllowance(t *testing.T) { +func TestPendingMinimumAllowance(t *testing.T) { t.Parallel() // Create the pool to test the limit enforcement with @@ -1337,7 +1338,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) { t.Errorf("addr %x: total pending transactions mismatch: have %d, want %d", addr, list.Len(), config.AccountSlots) } } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } } @@ -1347,7 +1348,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) { // from the pending pool to the queue. // // Note, local transactions are never allowed to be dropped. -func TestTransactionPoolRepricing(t *testing.T) { +func TestRepricing(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with @@ -1359,7 +1360,7 @@ func TestTransactionPoolRepricing(t *testing.T) { defer pool.Stop() // Keep track of transaction events to ensure all executables get announced - events := make(chan NewTxsEvent, 32) + events := make(chan core.NewTxsEvent, 32) sub := pool.txFeed.Subscribe(events) defer sub.Unsubscribe() @@ -1400,7 +1401,7 @@ func TestTransactionPoolRepricing(t *testing.T) { if err := validateEvents(events, 7); err != nil { t.Fatalf("original event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } // Reprice the pool and check that underpriced transactions get dropped @@ -1416,7 +1417,7 @@ func TestTransactionPoolRepricing(t *testing.T) { if err := validateEvents(events, 0); err != nil { t.Fatalf("reprice event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } // Check that we can't add the old transactions back @@ -1432,7 +1433,7 @@ func TestTransactionPoolRepricing(t *testing.T) { if err := validateEvents(events, 0); err != nil { t.Fatalf("post-reprice event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } // However we can add local underpriced transactions @@ -1446,7 +1447,7 @@ func TestTransactionPoolRepricing(t *testing.T) { if err := validateEvents(events, 1); err != nil { t.Fatalf("post-reprice local event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } // And we can fill gaps with properly priced transactions @@ -1462,7 +1463,7 @@ func TestTransactionPoolRepricing(t *testing.T) { if err := validateEvents(events, 5); err != nil { t.Fatalf("post-reprice event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } } @@ -1472,15 +1473,15 @@ func TestTransactionPoolRepricing(t *testing.T) { // gapped transactions back from the pending pool to the queue. // // Note, local transactions are never allowed to be dropped. -func TestTransactionPoolRepricingDynamicFee(t *testing.T) { +func TestRepricingDynamicFee(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - pool, _ := setupTxPoolWithConfig(eip1559Config) + pool, _ := setupPoolWithConfig(eip1559Config) defer pool.Stop() // Keep track of transaction events to ensure all executables get announced - events := make(chan NewTxsEvent, 32) + events := make(chan core.NewTxsEvent, 32) sub := pool.txFeed.Subscribe(events) defer sub.Unsubscribe() @@ -1521,7 +1522,7 @@ func TestTransactionPoolRepricingDynamicFee(t *testing.T) { if err := validateEvents(events, 7); err != nil { t.Fatalf("original event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } // Reprice the pool and check that underpriced transactions get dropped @@ -1537,7 +1538,7 @@ func TestTransactionPoolRepricingDynamicFee(t *testing.T) { if err := validateEvents(events, 0); err != nil { t.Fatalf("reprice event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } // Check that we can't add the old transactions back @@ -1556,7 +1557,7 @@ func TestTransactionPoolRepricingDynamicFee(t *testing.T) { if err := validateEvents(events, 0); err != nil { t.Fatalf("post-reprice event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } // However we can add local underpriced transactions @@ -1570,7 +1571,7 @@ func TestTransactionPoolRepricingDynamicFee(t *testing.T) { if err := validateEvents(events, 1); err != nil { t.Fatalf("post-reprice local event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } // And we can fill gaps with properly priced transactions @@ -1589,14 +1590,14 @@ func TestTransactionPoolRepricingDynamicFee(t *testing.T) { if err := validateEvents(events, 5); err != nil { t.Fatalf("post-reprice event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } } // Tests that setting the transaction pool gas price to a higher value does not // remove local transactions (legacy & dynamic fee). -func TestTransactionPoolRepricingKeepsLocals(t *testing.T) { +func TestRepricingKeepsLocals(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with @@ -1649,7 +1650,7 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) { t.Fatalf("queued transactions mismatched: have %d, want %d", queued, expQueued) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } } @@ -1671,7 +1672,7 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) { // pending transactions are moved into the queue. // // Note, local transactions are never allowed to be dropped. -func TestTransactionPoolUnderpricing(t *testing.T) { +func TestPoolUnderpricing(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with @@ -1687,7 +1688,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) { defer pool.Stop() // Keep track of transaction events to ensure all executables get announced - events := make(chan NewTxsEvent, 32) + events := make(chan core.NewTxsEvent, 32) sub := pool.txFeed.Subscribe(events) defer sub.Unsubscribe() @@ -1721,7 +1722,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) { if err := validateEvents(events, 3); err != nil { t.Fatalf("original event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } // Ensure that adding an underpriced transaction on block limit fails @@ -1748,7 +1749,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) { if err := validateEvents(events, 1); err != nil { t.Fatalf("additional event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } // Ensure that adding local transactions can push out even higher priced ones @@ -1770,7 +1771,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) { if err := validateEvents(events, 2); err != nil { t.Fatalf("local event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } } @@ -1778,7 +1779,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) { // Tests that more expensive transactions push out cheap ones from the pool, but // without producing instability by creating gaps that start jumping transactions // back and forth between queued/pending. -func TestTransactionPoolStableUnderpricing(t *testing.T) { +func TestPoolStableUnderpricing(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with @@ -1795,7 +1796,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) { defer pool.Stop() // Keep track of transaction events to ensure all executables get announced - events := make(chan NewTxsEvent, 32) + events := make(chan core.NewTxsEvent, 32) sub := pool.txFeed.Subscribe(events) defer sub.Unsubscribe() @@ -1822,7 +1823,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) { if err := validateEvents(events, int(config.GlobalSlots)); err != nil { t.Fatalf("original event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } // Ensure that adding high priced transactions drops a cheap, but doesn't produce a gap @@ -1839,7 +1840,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) { if err := validateEvents(events, 1); err != nil { t.Fatalf("additional event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } } @@ -1849,17 +1850,17 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) { // expensive ones and any gapped pending transactions are moved into the queue. // // Note, local transactions are never allowed to be dropped. -func TestTransactionPoolUnderpricingDynamicFee(t *testing.T) { +func TestPoolUnderpricingDynamicFee(t *testing.T) { t.Parallel() - pool, _ := setupTxPoolWithConfig(eip1559Config) + pool, _ := setupPoolWithConfig(eip1559Config) defer pool.Stop() pool.config.GlobalSlots = 2 pool.config.GlobalQueue = 2 // Keep track of transaction events to ensure all executables get announced - events := make(chan NewTxsEvent, 32) + events := make(chan core.NewTxsEvent, 32) sub := pool.txFeed.Subscribe(events) defer sub.Unsubscribe() @@ -1893,7 +1894,7 @@ func TestTransactionPoolUnderpricingDynamicFee(t *testing.T) { if err := validateEvents(events, 3); err != nil { t.Fatalf("original event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } @@ -1927,7 +1928,7 @@ func TestTransactionPoolUnderpricingDynamicFee(t *testing.T) { if err := validateEvents(events, 1); err != nil { t.Fatalf("additional event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } // Ensure that adding local transactions can push out even higher priced ones @@ -1949,7 +1950,7 @@ func TestTransactionPoolUnderpricingDynamicFee(t *testing.T) { if err := validateEvents(events, 2); err != nil { t.Fatalf("local event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } } @@ -1959,7 +1960,7 @@ func TestTransactionPoolUnderpricingDynamicFee(t *testing.T) { func TestDualHeapEviction(t *testing.T) { t.Parallel() - pool, _ := setupTxPoolWithConfig(eip1559Config) + pool, _ := setupPoolWithConfig(eip1559Config) defer pool.Stop() pool.config.GlobalSlots = 10 @@ -2006,13 +2007,13 @@ func TestDualHeapEviction(t *testing.T) { check(highTip, "effective tip") } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } } // Tests that the pool rejects duplicate transactions. -func TestTransactionDeduplication(t *testing.T) { +func TestDeduplication(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with @@ -2071,14 +2072,14 @@ func TestTransactionDeduplication(t *testing.T) { if queued != 0 { t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } } // Tests that the pool rejects replacement transactions that don't meet the minimum // price bump required. -func TestTransactionReplacement(t *testing.T) { +func TestReplacement(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with @@ -2090,7 +2091,7 @@ func TestTransactionReplacement(t *testing.T) { defer pool.Stop() // Keep track of transaction events to ensure all executables get announced - events := make(chan NewTxsEvent, 32) + events := make(chan core.NewTxsEvent, 32) sub := pool.txFeed.Subscribe(events) defer sub.Unsubscribe() @@ -2152,23 +2153,23 @@ func TestTransactionReplacement(t *testing.T) { if err := validateEvents(events, 0); err != nil { t.Fatalf("queued replacement event firing failed: %v", err) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } } // Tests that the pool rejects replacement dynamic fee transactions that don't // meet the minimum price bump required. -func TestTransactionReplacementDynamicFee(t *testing.T) { +func TestReplacementDynamicFee(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - pool, key := setupTxPoolWithConfig(eip1559Config) + pool, key := setupPoolWithConfig(eip1559Config) defer pool.Stop() testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000)) // Keep track of transaction events to ensure all executables get announced - events := make(chan NewTxsEvent, 32) + events := make(chan core.NewTxsEvent, 32) sub := pool.txFeed.Subscribe(events) defer sub.Unsubscribe() @@ -2262,15 +2263,15 @@ func TestTransactionReplacementDynamicFee(t *testing.T) { } } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } } // Tests that local transactions are journaled to disk, but remote transactions // get discarded between restarts. -func TestTransactionJournaling(t *testing.T) { testTransactionJournaling(t, false) } -func TestTransactionJournalingNoLocals(t *testing.T) { testTransactionJournaling(t, true) } +func TestJournaling(t *testing.T) { testTransactionJournaling(t, false) } +func TestJournalingNoLocals(t *testing.T) { testTransactionJournaling(t, true) } func testTransactionJournaling(t *testing.T, nolocals bool) { t.Parallel() @@ -2326,7 +2327,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) { if queued != 0 { t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } // Terminate the old pool, bump the local nonce, create a new pool and ensure relevant transaction survive @@ -2349,7 +2350,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) { t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) } } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } // Bump the nonce temporarily and ensure the newly invalidated transaction is removed @@ -2375,7 +2376,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) { t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1) } } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } pool.Stop() @@ -2383,7 +2384,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) { // TestTransactionStatusCheck tests that the pool can correctly retrieve the // pending status of individual transactions. -func TestTransactionStatusCheck(t *testing.T) { +func TestStatusCheck(t *testing.T) { t.Parallel() // Create the pool to test the status retrievals with @@ -2418,7 +2419,7 @@ func TestTransactionStatusCheck(t *testing.T) { if queued != 2 { t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2) } - if err := validateTxPoolInternals(pool); err != nil { + if err := validatePoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } // Retrieve the status of each transaction and validate them @@ -2439,7 +2440,7 @@ func TestTransactionStatusCheck(t *testing.T) { } // Test the transaction slots consumption is computed correctly -func TestTransactionSlotCount(t *testing.T) { +func TestSlotCount(t *testing.T) { t.Parallel() key, _ := crypto.GenerateKey() @@ -2464,7 +2465,7 @@ func BenchmarkPendingDemotion10000(b *testing.B) { benchmarkPendingDemotion(b, 1 func benchmarkPendingDemotion(b *testing.B, size int) { // Add a batch of transactions to a pool one by one - pool, key := setupTxPool() + pool, key := setupPool() defer pool.Stop() account := crypto.PubkeyToAddress(key.PublicKey) @@ -2489,7 +2490,7 @@ func BenchmarkFuturePromotion10000(b *testing.B) { benchmarkFuturePromotion(b, 1 func benchmarkFuturePromotion(b *testing.B, size int) { // Add a batch of transactions to a pool one by one - pool, key := setupTxPool() + pool, key := setupPool() defer pool.Stop() account := crypto.PubkeyToAddress(key.PublicKey) @@ -2507,17 +2508,17 @@ func benchmarkFuturePromotion(b *testing.B, size int) { } // Benchmarks the speed of batched transaction insertion. -func BenchmarkPoolBatchInsert100(b *testing.B) { benchmarkPoolBatchInsert(b, 100, false) } -func BenchmarkPoolBatchInsert1000(b *testing.B) { benchmarkPoolBatchInsert(b, 1000, false) } -func BenchmarkPoolBatchInsert10000(b *testing.B) { benchmarkPoolBatchInsert(b, 10000, false) } +func BenchmarkBatchInsert100(b *testing.B) { benchmarkBatchInsert(b, 100, false) } +func BenchmarkBatchInsert1000(b *testing.B) { benchmarkBatchInsert(b, 1000, false) } +func BenchmarkBatchInsert10000(b *testing.B) { benchmarkBatchInsert(b, 10000, false) } -func BenchmarkPoolBatchLocalInsert100(b *testing.B) { benchmarkPoolBatchInsert(b, 100, true) } -func BenchmarkPoolBatchLocalInsert1000(b *testing.B) { benchmarkPoolBatchInsert(b, 1000, true) } -func BenchmarkPoolBatchLocalInsert10000(b *testing.B) { benchmarkPoolBatchInsert(b, 10000, true) } +func BenchmarkBatchLocalInsert100(b *testing.B) { benchmarkBatchInsert(b, 100, true) } +func BenchmarkBatchLocalInsert1000(b *testing.B) { benchmarkBatchInsert(b, 1000, true) } +func BenchmarkBatchLocalInsert10000(b *testing.B) { benchmarkBatchInsert(b, 10000, true) } -func benchmarkPoolBatchInsert(b *testing.B, size int, local bool) { +func benchmarkBatchInsert(b *testing.B, size int, local bool) { // Generate a batch of transactions to enqueue into the pool - pool, key := setupTxPool() + pool, key := setupPool() defer pool.Stop() account := crypto.PubkeyToAddress(key.PublicKey) @@ -2561,7 +2562,7 @@ func BenchmarkInsertRemoteWithAllLocals(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { b.StopTimer() - pool, _ := setupTxPool() + pool, _ := setupPool() testAddBalance(pool, account, big.NewInt(100000000)) for _, local := range locals { pool.AddLocal(local) @@ -2577,9 +2578,9 @@ func BenchmarkInsertRemoteWithAllLocals(b *testing.B) { } // Benchmarks the speed of batch transaction insertion in case of multiple accounts. -func BenchmarkPoolMultiAccountBatchInsert(b *testing.B) { +func BenchmarkMultiAccountBatchInsert(b *testing.B) { // Generate a batch of transactions to enqueue into the pool - pool, _ := setupTxPool() + pool, _ := setupPool() defer pool.Stop() b.ReportAllocs() batches := make(types.Transactions, b.N) diff --git a/eth/backend.go b/eth/backend.go index 3b6138bb9633..1842bb47779d 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -37,6 +37,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/contracts" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/bloombits" + "github.com/XinFinOrg/XDPoSChain/core/txpool" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/eth/downloader" @@ -72,9 +73,9 @@ type Ethereum struct { shutdownChan chan bool // Channel for shutting down the ethereum // Handlers - txPool *core.TxPool - orderPool *core.OrderPool - lendingPool *core.LendingPool + txPool *txpool.TxPool + orderPool *txpool.OrderPool + lendingPool *txpool.LendingPool blockchain *core.BlockChain protocolManager *ProtocolManager lesServer LesServer @@ -186,9 +187,9 @@ func New(ctx *node.ServiceContext, config *ethconfig.Config, XDCXServ *XDCx.XDCX if config.TxPool.Journal != "" { config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal) } - eth.txPool = core.NewTxPool(config.TxPool, eth.chainConfig, eth.blockchain) - eth.orderPool = core.NewOrderPool(eth.chainConfig, eth.blockchain) - eth.lendingPool = core.NewLendingPool(eth.chainConfig, eth.blockchain) + eth.txPool = txpool.NewTxPool(config.TxPool, eth.chainConfig, eth.blockchain) + eth.orderPool = txpool.NewOrderPool(eth.chainConfig, eth.blockchain) + eth.lendingPool = txpool.NewLendingPool(eth.chainConfig, eth.blockchain) if common.RollbackHash != (common.Hash{}) { curBlock := eth.blockchain.CurrentBlock() if curBlock == nil { @@ -517,7 +518,7 @@ func (e *Ethereum) Miner() *miner.Miner { return e.miner } func (e *Ethereum) AccountManager() *accounts.Manager { return e.accountManager } func (e *Ethereum) BlockChain() *core.BlockChain { return e.blockchain } -func (e *Ethereum) TxPool() *core.TxPool { return e.txPool } +func (e *Ethereum) TxPool() *txpool.TxPool { return e.txPool } func (e *Ethereum) EventMux() *event.TypeMux { return e.eventMux } func (e *Ethereum) Engine() consensus.Engine { return e.engine } func (e *Ethereum) ChainDb() ethdb.Database { return e.chainDb } @@ -591,7 +592,7 @@ func (e *Ethereum) GetXDCX() *XDCx.XDCX { return e.XDCX } -func (e *Ethereum) OrderPool() *core.OrderPool { +func (e *Ethereum) OrderPool() *txpool.OrderPool { return e.orderPool } @@ -600,6 +601,6 @@ func (e *Ethereum) GetXDCXLending() *XDCxlending.Lending { } // LendingPool geth eth lending pool -func (e *Ethereum) LendingPool() *core.LendingPool { +func (e *Ethereum) LendingPool() *txpool.LendingPool { return e.lendingPool } diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 74857fa80031..512e57387620 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -29,6 +29,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common/hexutil" "github.com/XinFinOrg/XDPoSChain/consensus/ethash" "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/core/txpool" "github.com/XinFinOrg/XDPoSChain/eth/downloader" "github.com/XinFinOrg/XDPoSChain/eth/gasprice" "github.com/XinFinOrg/XDPoSChain/params" @@ -72,7 +73,7 @@ var Defaults = Config{ FilterLogCacheSize: 32, GasPrice: big.NewInt(0.25 * params.Shannon), - TxPool: core.DefaultTxPoolConfig, + TxPool: txpool.DefaultConfig, RPCGasCap: 50000000, GPO: FullNodeGPO, RPCTxFeeCap: 1, // 1 ether @@ -129,7 +130,7 @@ type Config struct { Ethash ethash.Config // Transaction pool options - TxPool core.TxPoolConfig + TxPool txpool.Config // Gas Price Oracle options GPO gasprice.Config diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index 6b27542f1933..f11304094efe 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -9,6 +9,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus/ethash" "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/core/txpool" "github.com/XinFinOrg/XDPoSChain/eth/downloader" "github.com/XinFinOrg/XDPoSChain/eth/gasprice" ) @@ -33,7 +34,7 @@ func (c Config) MarshalTOML() (interface{}, error) { GasPrice *big.Int FilterLogCacheSize int Ethash ethash.Config - TxPool core.TxPoolConfig + TxPool txpool.Config GPO gasprice.Config EnablePreimageRecording bool DocRoot string `toml:"-"` @@ -87,7 +88,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { GasPrice *big.Int FilterLogCacheSize *int Ethash *ethash.Config - TxPool *core.TxPoolConfig + TxPool *txpool.Config GPO *gasprice.Config EnablePreimageRecording *bool DocRoot *string `toml:"-"` diff --git a/les/handler.go b/les/handler.go index bb8c89f1566b..7e7b99d4194e 100644 --- a/les/handler.go +++ b/les/handler.go @@ -27,12 +27,12 @@ import ( "sync" "time" - "github.com/XinFinOrg/XDPoSChain/core/rawdb" - "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/state" + "github.com/XinFinOrg/XDPoSChain/core/txpool" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/eth/downloader" "github.com/XinFinOrg/XDPoSChain/ethdb" @@ -92,7 +92,7 @@ type BlockChain interface { type txPool interface { AddRemotes(txs []*types.Transaction) []error AddRemotesSync(txs []*types.Transaction) []error - Status(hashes []common.Hash) []core.TxStatus + Status(hashes []common.Hash) []txpool.TxStatus } type ProtocolManager struct { @@ -1044,7 +1044,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } stats := pm.txStatus(hashes) for i, stat := range stats { - if stat.Status == core.TxStatusUnknown { + if stat.Status == txpool.TxStatusUnknown { if errs := pm.txpool.AddRemotes([]*types.Transaction{req.Txs[i]}); errs[0] != nil { stats[i].Error = errs[0].Error() continue @@ -1160,9 +1160,9 @@ func (pm *ProtocolManager) txStatus(hashes []common.Hash) []txStatus { stats[i].Status = stat // If the transaction is unknown to the pool, try looking it up locally - if stat == core.TxStatusUnknown { + if stat == txpool.TxStatusUnknown { if block, number, index := core.GetTxLookupEntry(pm.chainDb, hashes[i]); block != (common.Hash{}) { - stats[i].Status = core.TxStatusIncluded + stats[i].Status = txpool.TxStatusIncluded stats[i].Lookup = &core.TxLookupEntry{BlockHash: block, BlockIndex: number, Index: index} } } diff --git a/les/handler_test.go b/les/handler_test.go index bac89b5d3353..85d9581b3d82 100644 --- a/les/handler_test.go +++ b/les/handler_test.go @@ -18,7 +18,6 @@ package les import ( "encoding/binary" - "github.com/XinFinOrg/XDPoSChain/core/rawdb" "math/big" "math/rand" "testing" @@ -27,6 +26,8 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus/ethash" "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" + "github.com/XinFinOrg/XDPoSChain/core/txpool" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/eth/downloader" @@ -493,10 +494,10 @@ func TestTransactionStatusLes2(t *testing.T) { db := rawdb.NewMemoryDatabase() pm := newTestProtocolManagerMust(t, false, 0, nil, nil, nil, db) chain := pm.blockchain.(*core.BlockChain) - config := core.DefaultTxPoolConfig + config := txpool.DefaultConfig config.Journal = "" - txpool := core.NewTxPool(config, params.TestChainConfig, chain) - pm.txpool = txpool + txPool := txpool.NewTxPool(config, params.TestChainConfig, chain) + pm.txpool = txPool peer, _ := newTestPeer(t, "peer", 2, pm, true) defer peer.close() @@ -520,20 +521,20 @@ func TestTransactionStatusLes2(t *testing.T) { // test error status by sending an underpriced transaction tx0, _ := types.SignTx(types.NewTransaction(0, acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey) - test(tx0, true, txStatus{Status: core.TxStatusUnknown, Error: core.ErrUnderpriced.Error()}) + test(tx0, true, txStatus{Status: txpool.TxStatusUnknown, Error: txpool.ErrUnderpriced.Error()}) tx1, _ := types.SignTx(types.NewTransaction(0, acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, testBankKey) - test(tx1, false, txStatus{Status: core.TxStatusUnknown}) // query before sending, should be unknown - test(tx1, true, txStatus{Status: core.TxStatusPending}) // send valid processable tx, should return pending - test(tx1, true, txStatus{Status: core.TxStatusPending}) // adding it again should not return an error + test(tx1, false, txStatus{Status: txpool.TxStatusUnknown}) // query before sending, should be unknown + test(tx1, true, txStatus{Status: txpool.TxStatusPending}) // send valid processable tx, should return pending + test(tx1, true, txStatus{Status: txpool.TxStatusPending}) // adding it again should not return an error tx2, _ := types.SignTx(types.NewTransaction(1, acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, testBankKey) tx3, _ := types.SignTx(types.NewTransaction(2, acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, testBankKey) // send transactions in the wrong order, tx3 should be queued - test(tx3, true, txStatus{Status: core.TxStatusQueued}) - test(tx2, true, txStatus{Status: core.TxStatusPending}) + test(tx3, true, txStatus{Status: txpool.TxStatusQueued}) + test(tx2, true, txStatus{Status: txpool.TxStatusPending}) // query again, now tx3 should be pending too - test(tx3, false, txStatus{Status: core.TxStatusPending}) + test(tx3, false, txStatus{Status: txpool.TxStatusPending}) // generate and add a block with tx1 and tx2 included gchain, _ := core.GenerateChain(params.TestChainConfig, chain.GetBlockByNumber(0), ethash.NewFaker(), db, 1, func(i int, block *core.BlockGen) { @@ -545,19 +546,19 @@ func TestTransactionStatusLes2(t *testing.T) { } // wait until TxPool processes the inserted block for i := 0; i < 10; i++ { - if pending, _ := txpool.Stats(); pending == 1 { + if pending, _ := txPool.Stats(); pending == 1 { break } time.Sleep(100 * time.Millisecond) } - if pending, _ := txpool.Stats(); pending != 1 { + if pending, _ := txPool.Stats(); pending != 1 { t.Fatalf("pending count mismatch: have %d, want 1", pending) } // check if their status is included now block1hash := core.GetCanonicalHash(db, 1) - test(tx1, false, txStatus{Status: core.TxStatusIncluded, Lookup: &core.TxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 0}}) - test(tx2, false, txStatus{Status: core.TxStatusIncluded, Lookup: &core.TxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 1}}) + test(tx1, false, txStatus{Status: txpool.TxStatusIncluded, Lookup: &core.TxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 0}}) + test(tx2, false, txStatus{Status: txpool.TxStatusIncluded, Lookup: &core.TxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 1}}) // create a reorg that rolls them back gchain, _ = core.GenerateChain(params.TestChainConfig, chain.GetBlockByNumber(0), ethash.NewFaker(), db, 2, func(i int, block *core.BlockGen) {}) @@ -566,15 +567,15 @@ func TestTransactionStatusLes2(t *testing.T) { } // wait until TxPool processes the reorg for i := 0; i < 10; i++ { - if pending, _ := txpool.Stats(); pending == 3 { + if pending, _ := txPool.Stats(); pending == 3 { break } time.Sleep(100 * time.Millisecond) } - if pending, _ := txpool.Stats(); pending != 3 { + if pending, _ := txPool.Stats(); pending != 3 { t.Fatalf("pending count mismatch: have %d, want 3", pending) } // check if their status is pending again - test(tx1, false, txStatus{Status: core.TxStatusPending}) - test(tx2, false, txStatus{Status: core.TxStatusPending}) + test(tx1, false, txStatus{Status: txpool.TxStatusPending}) + test(tx2, false, txStatus{Status: txpool.TxStatusPending}) } diff --git a/les/protocol.go b/les/protocol.go index 1122f13e9bc9..888b0ac1795e 100644 --- a/les/protocol.go +++ b/les/protocol.go @@ -28,6 +28,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/core/txpool" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/crypto/secp256k1" "github.com/XinFinOrg/XDPoSChain/rlp" @@ -223,7 +224,7 @@ type CodeData []struct { type proofsData [][]rlp.RawValue type txStatus struct { - Status core.TxStatus + Status txpool.TxStatus Lookup *core.TxLookupEntry `rlp:"nil"` Error string } diff --git a/light/txpool.go b/light/txpool.go index d9566e2a7076..39a3aede52b6 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -26,6 +26,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/state" + "github.com/XinFinOrg/XDPoSChain/core/txpool" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/event" @@ -378,7 +379,7 @@ func (p *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error { // Validate the transaction sender and it's sig. Throw // if the from fields is invalid. if from, err = types.Sender(p.signer, tx); err != nil { - return core.ErrInvalidSender + return txpool.ErrInvalidSender } // Last but not least check for nonce errors currentState := p.currentState(ctx) @@ -390,14 +391,14 @@ func (p *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error { // block limit gas. header := p.chain.GetHeaderByHash(p.head) if header.GasLimit < tx.Gas() { - return core.ErrGasLimit + return txpool.ErrGasLimit } // Transactions can't be negative. This may never happen // using RLP decoded transactions but may occur if you create // a transaction using the RPC for example. if tx.Value().Sign() < 0 { - return core.ErrNegativeValue + return txpool.ErrNegativeValue } // Transactor should have enough funds to cover the costs diff --git a/miner/miner.go b/miner/miner.go index 0b4354c4d8db..f48df9491d41 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -21,14 +21,14 @@ import ( "fmt" "sync/atomic" - "github.com/XinFinOrg/XDPoSChain/XDCxlending" - "github.com/XinFinOrg/XDPoSChain/XDCx" + "github.com/XinFinOrg/XDPoSChain/XDCxlending" "github.com/XinFinOrg/XDPoSChain/accounts" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/state" + "github.com/XinFinOrg/XDPoSChain/core/txpool" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/eth/downloader" "github.com/XinFinOrg/XDPoSChain/ethdb" @@ -41,11 +41,11 @@ import ( type Backend interface { AccountManager() *accounts.Manager BlockChain() *core.BlockChain - TxPool() *core.TxPool + TxPool() *txpool.TxPool ChainDb() ethdb.Database GetXDCX() *XDCx.XDCX - OrderPool() *core.OrderPool - LendingPool() *core.LendingPool + OrderPool() *txpool.OrderPool + LendingPool() *txpool.LendingPool GetXDCXLending() *XDCxlending.Lending } diff --git a/miner/worker.go b/miner/worker.go index 73aecd9fe772..ac065d650e0b 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -1048,7 +1048,7 @@ func (w *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Addr w.tcount++ txs.Shift() - case errors.Is(err, core.ErrTxTypeNotSupported): + case errors.Is(err, types.ErrTxTypeNotSupported): // Pop the unsupported transaction without shifting in the next from the account log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type()) txs.Pop() From cda663db644bb328afc0bf993e96c12a973e2d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Wed, 26 Oct 2022 13:30:51 +0300 Subject: [PATCH 149/479] core/types: rename tx files to group them better together (#26044) --- core/types/{access_list_tx.go => tx_access_list.go} | 0 core/types/{dynamic_fee_tx.go => tx_dynamic_fee.go} | 0 core/types/{legacy_tx.go => tx_legacy.go} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename core/types/{access_list_tx.go => tx_access_list.go} (100%) rename core/types/{dynamic_fee_tx.go => tx_dynamic_fee.go} (100%) rename core/types/{legacy_tx.go => tx_legacy.go} (100%) diff --git a/core/types/access_list_tx.go b/core/types/tx_access_list.go similarity index 100% rename from core/types/access_list_tx.go rename to core/types/tx_access_list.go diff --git a/core/types/dynamic_fee_tx.go b/core/types/tx_dynamic_fee.go similarity index 100% rename from core/types/dynamic_fee_tx.go rename to core/types/tx_dynamic_fee.go diff --git a/core/types/legacy_tx.go b/core/types/tx_legacy.go similarity index 100% rename from core/types/legacy_tx.go rename to core/types/tx_legacy.go From f64aea4ba03b6fe2ce18ad81ee3bcccbf36f7099 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 22 Aug 2024 12:34:47 +0800 Subject: [PATCH 150/479] core/vm: implement EIP-3860: Limit and meter initcode (#23847) --- core/bench_test.go | 2 +- core/error.go | 4 +++ core/state_transition.go | 47 ++++++++++++++++++++------ core/txpool/txpool.go | 2 +- core/vm/eips.go | 8 +++++ core/vm/errors.go | 1 + core/vm/gas_table.go | 34 +++++++++++++++++++ core/vm/gas_table_test.go | 71 +++++++++++++++++++++++++++++++++++++++ core/vm/instructions.go | 1 - core/vm/jump_table.go | 1 + eth/tracers/tracer.go | 3 +- light/txpool.go | 4 ++- params/protocol_params.go | 9 +++-- 13 files changed, 169 insertions(+), 18 deletions(-) diff --git a/core/bench_test.go b/core/bench_test.go index 5c4b97c97465..0d797dbea5a4 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -85,7 +85,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) { return func(i int, gen *BlockGen) { toaddr := common.Address{} data := make([]byte, nbytes) - gas, _ := IntrinsicGas(data, nil, false, false) + gas, _ := IntrinsicGas(data, nil, false, false, false) tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data), types.HomesteadSigner{}, benchRootKey) gen.AddTx(tx) } diff --git a/core/error.go b/core/error.go index 72a835149ede..4eb7eebf0ec9 100644 --- a/core/error.go +++ b/core/error.go @@ -45,6 +45,10 @@ var ( // by a transaction is higher than what's left in the block. ErrGasLimitReached = errors.New("gas limit reached") + // ErrMaxInitCodeSizeExceeded is returned if creation transaction provides the init code bigger + // than init code size limit. + ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded") + // ErrInsufficientFunds is returned if the total cost of executing a transaction // is higher than the balance of the user's account. ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value") diff --git a/core/state_transition.go b/core/state_transition.go index d75867da7b30..506d12a95767 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -90,7 +90,7 @@ type Message interface { } // IntrinsicGas computes the 'intrinsic gas' for a message with the given data. -func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, isHomestead bool) (uint64, error) { +func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, isHomestead bool, isEIP3860 bool) (uint64, error) { // Set the starting gas for the raw transaction var gas uint64 if isContractCreation && isHomestead { @@ -98,8 +98,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, } else { gas = params.TxGas } + dataLen := uint64(len(data)) // Bump the required gas by the amount of transactional data - if len(data) > 0 { + if dataLen > 0 { // Zero and non-zero bytes are priced differently var nz uint64 for _, byt := range data { @@ -113,11 +114,19 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, } gas += nz * params.TxDataNonZeroGas - z := uint64(len(data)) - nz + z := dataLen - nz if (math.MaxUint64-gas)/params.TxDataZeroGas < z { return 0, ErrGasUintOverflow } gas += z * params.TxDataZeroGas + + if isContractCreation && isEIP3860 { + lenWords := toWordSize(dataLen) + if (math.MaxUint64-gas)/params.InitCodeWordGas < lenWords { + return 0, ErrGasUintOverflow + } + gas += lenWords * params.InitCodeWordGas + } } if accessList != nil { gas += uint64(len(accessList)) * params.TxAccessListAddressGas @@ -126,6 +135,15 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, return gas, nil } +// toWordSize returns the ceiled word size required for init code payment calculation. +func toWordSize(size uint64) uint64 { + if size > math.MaxUint64-31 { + return math.MaxUint64/32 + 1 + } + + return (size + 31) / 32 +} + // NewStateTransition initialises and returns a new state transition object. func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition { return &StateTransition{ @@ -286,14 +304,18 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG if err = st.preCheck(); err != nil { return nil, 0, false, err, nil } - msg := st.msg - sender := st.from() // err checked in preCheck - homestead := st.evm.ChainConfig().IsHomestead(st.evm.Context.BlockNumber) - eip3529 := st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber) - contractCreation := msg.To() == nil + + var ( + msg = st.msg + sender = st.from() // err checked in preCheck + rules = st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber) + homestead = rules.IsHomestead + eip3529 = rules.IsEIP1559 + contractCreation = msg.To() == nil + ) // Check clauses 4-5, subtract intrinsic gas if everything is correct - gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead) + gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead, rules.IsEIP1559) if err != nil { return nil, 0, false, err, nil } @@ -302,7 +324,12 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG } st.gas -= gas - if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber); rules.IsEIP1559 { + // Check whether the init code size has been exceeded. + if rules.IsEIP1559 && contractCreation && len(st.data) > params.MaxInitCodeSize { + return nil, 0, false, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(st.data), params.MaxInitCodeSize), nil + } + + if rules.IsEIP1559 { st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList()) } diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 14fb416384b2..7ad1f3cfd4ba 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -702,7 +702,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { if tx.To() == nil || (tx.To() != nil && !tx.IsSpecialTransaction()) { // Ensure the transaction has more gas than the basic tx fee. - intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true) + intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.eip1559) if err != nil { return err } diff --git a/core/vm/eips.go b/core/vm/eips.go index 2c4641d699d3..95e4379b0bca 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -26,6 +26,7 @@ import ( var activators = map[int]func(*JumpTable){ 3855: enable3855, + 3860: enable3860, 3529: enable3529, 3198: enable3198, 2929: enable2929, @@ -179,3 +180,10 @@ func opPush0(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) callContext.Stack.push(new(uint256.Int)) return nil, nil } + +// ebnable3860 enables "EIP-3860: Limit and meter initcode" +// https://eips.ethereum.org/EIPS/eip-3860 +func enable3860(jt *JumpTable) { + jt[CREATE].dynamicGas = gasCreateEip3860 + jt[CREATE2].dynamicGas = gasCreate2Eip3860 +} diff --git a/core/vm/errors.go b/core/vm/errors.go index 02ce2a678b34..9f98dae2f7b0 100644 --- a/core/vm/errors.go +++ b/core/vm/errors.go @@ -29,6 +29,7 @@ var ( ErrInsufficientBalance = errors.New("insufficient balance for transfer") ErrContractAddressCollision = errors.New("contract address collision") ErrExecutionReverted = errors.New("execution reverted") + ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded") ErrMaxCodeSizeExceeded = errors.New("max code size exceeded") ErrInvalidJump = errors.New("invalid jump destination") ErrWriteProtection = errors.New("write protection") diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 98a737706eb7..748d1f3ddd96 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -300,6 +300,40 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS return gas, nil } +func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + size, overflow := stack.Back(2).Uint64WithOverflow() + if overflow || size > params.MaxInitCodeSize { + return 0, ErrGasUintOverflow + } + // Since size <= params.MaxInitCodeSize, these multiplication cannot overflow + moreGas := params.InitCodeWordGas * ((size + 31) / 32) + if gas, overflow = math.SafeAdd(gas, moreGas); overflow { + return 0, ErrGasUintOverflow + } + return gas, nil +} + +func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + size, overflow := stack.Back(2).Uint64WithOverflow() + if overflow || size > params.MaxInitCodeSize { + return 0, ErrGasUintOverflow + } + // Since size <= params.MaxInitCodeSize, these multiplication cannot overflow + moreGas := (params.InitCodeWordGas + params.Keccak256WordGas) * ((size + 31) / 32) + if gas, overflow = math.SafeAdd(gas, moreGas); overflow { + return 0, ErrGasUintOverflow + } + return gas, nil +} + func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8) diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go index c9c7e9244dd1..73132023d9b4 100644 --- a/core/vm/gas_table_test.go +++ b/core/vm/gas_table_test.go @@ -17,8 +17,10 @@ package vm import ( + "bytes" "math" "math/big" + "sort" "testing" "github.com/XinFinOrg/XDPoSChain/core/rawdb" @@ -106,3 +108,72 @@ func TestEIP2200(t *testing.T) { } } } + +var createGasTests = []struct { + code string + eip3860 bool + gasUsed uint64 + minimumGas uint64 +}{ + // legacy create(0, 0, 0xc000) without 3860 used + {"0x61C00060006000f0" + "600052" + "60206000F3", false, 41237, 41237}, + // legacy create(0, 0, 0xc000) _with_ 3860 + {"0x61C00060006000f0" + "600052" + "60206000F3", true, 44309, 44309}, + // create2(0, 0, 0xc001, 0) without 3860 + {"0x600061C00160006000f5" + "600052" + "60206000F3", false, 50471, 100_000}, + // create2(0, 0, 0xc001, 0) (too large), with 3860 + {"0x600061C00160006000f5" + "600052" + "60206000F3", true, 32012, 100_000}, + // create2(0, 0, 0xc000, 0) + // This case is trying to deploy code at (within) the limit + {"0x600061C00060006000f5" + "600052" + "60206000F3", true, 53528, 100_000}, + // create2(0, 0, 0xc001, 0) + // This case is trying to deploy code exceeding the limit + {"0x600061C00160006000f5" + "600052" + "60206000F3", true, 32024, 100_000}} + +func TestCreateGas(t *testing.T) { + for i, tt := range createGasTests { + var gasUsed = uint64(0) + doCheck := func(testGas int) bool { + address := common.BytesToAddress([]byte("contract")) + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) + statedb.CreateAccount(address) + statedb.SetCode(address, hexutil.MustDecode(tt.code)) + statedb.Finalise(true) + vmctx := BlockContext{ + CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true }, + Transfer: func(StateDB, common.Address, common.Address, *big.Int) {}, + BlockNumber: big.NewInt(0), + } + config := Config{} + if tt.eip3860 { + config.ExtraEips = []int{3860} + } + + vmenv := NewEVM(vmctx, TxContext{}, statedb, nil, params.AllEthashProtocolChanges, config) + var startGas = uint64(testGas) + ret, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(big.Int)) + if err != nil { + return false + } + gasUsed = startGas - gas + if len(ret) != 32 { + t.Fatalf("test %d: expected 32 bytes returned, have %d", i, len(ret)) + } + if bytes.Equal(ret, make([]byte, 32)) { + // Failure + return false + } + return true + } + minGas := sort.Search(100_000, doCheck) + if uint64(minGas) != tt.minimumGas { + t.Fatalf("test %d: min gas error, want %d, have %d", i, tt.minimumGas, minGas) + } + // If the deployment succeeded, we also check the gas used + if minGas < 100_000 { + if gasUsed != tt.gasUsed { + t.Errorf("test %d: gas used mismatch: have %v, want %v", i, gasUsed, tt.gasUsed) + } + } + } +} diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 4f2d04639475..d26e5dfad6c4 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -646,7 +646,6 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) gas = scope.Contract.Gas ) - // Apply EIP150 gas -= gas / 64 scope.Contract.UseGas(gas) diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 56f5dee91d3a..fc4b9b8521c0 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -84,6 +84,7 @@ func newEip1559InstructionSet() JumpTable { instructionSet := newShanghaiInstructionSet() enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929 enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529 + enable3860(&instructionSet) // Limit and meter initcode return validate(instructionSet) } diff --git a/eth/tracers/tracer.go b/eth/tracers/tracer.go index 9e27a04dade2..2fa04cd1ad26 100644 --- a/eth/tracers/tracer.go +++ b/eth/tracers/tracer.go @@ -682,9 +682,10 @@ func (jst *JsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Ad // Compute intrinsic gas isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber) + isEIP1559 := env.ChainConfig().IsEIP1559(env.Context.BlockNumber) // after update core.IntrinsicGas, use isIstanbul in it // isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber) - intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead) + intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead, isEIP1559) if err != nil { return } diff --git a/light/txpool.go b/light/txpool.go index 39a3aede52b6..578323914429 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -69,6 +69,7 @@ type TxPool struct { homestead bool eip2718 bool // Fork indicator whether we are in the eip2718 stage. + eip1559 bool // Fork indicator whether we are in the eip1559 stage. } // TxRelayBackend provides an interface to the mechanism that forwards transacions @@ -317,6 +318,7 @@ func (p *TxPool) setNewHead(head *types.Header) { next := new(big.Int).Add(head.Number, big.NewInt(1)) p.homestead = p.config.IsHomestead(head.Number) p.eip2718 = p.config.IsEIP1559(next) + p.eip1559 = p.config.IsEIP1559(next) } // Stop stops the light transaction pool @@ -408,7 +410,7 @@ func (p *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error { } // Should supply enough intrinsic gas - gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, p.homestead) + gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, p.homestead, p.eip1559) if err != nil { return err } diff --git a/params/protocol_params.go b/params/protocol_params.go index 73cf17a8a38b..1799a24b0c47 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -42,8 +42,10 @@ const ( LogDataGas uint64 = 8 // Per byte in a LOG* operation's data. CallStipend uint64 = 2300 // Free gas given at beginning of call. - Keccak256Gas uint64 = 30 // Once per KECCAK256 operation. - Keccak256WordGas uint64 = 6 // Once per word of the KECCAK256 operation's data. + Keccak256Gas uint64 = 30 // Once per KECCAK256 operation. + Keccak256WordGas uint64 = 6 // Once per word of the KECCAK256 operation's data. + InitCodeWordGas uint64 = 2 // Once per word of the init code when creating a contract. + SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero. SstoreClearGas uint64 = 5000 // Once per SSTORE operation if the zeroness doesn't change. SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero. @@ -69,7 +71,8 @@ const ( InitialBaseFee = 12500000000 // Initial base fee for EIP-1559 blocks. - MaxCodeSize = 24576 // Maximum bytecode to permit for a contract + MaxCodeSize = 24576 // Maximum bytecode to permit for a contract + MaxInitCodeSize = 2 * MaxCodeSize // Maximum initcode to permit in a creation transaction and create instructions // Precompiled contract gas prices From 9b20ac785e4fb326ed9ff2bd384ff07ae7498230 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 11 Jun 2024 11:27:46 +0800 Subject: [PATCH 151/479] consensus/misc: move eip1559 into a package (#27828) --- consensus/XDPoS/engines/engine_v1/engine.go | 3 ++- consensus/XDPoS/engines/engine_v2/verifyHeader.go | 3 ++- consensus/ethash/consensus.go | 3 ++- consensus/misc/{ => eip1559}/eip1559.go | 2 +- core/chain_makers.go | 6 +++--- core/txpool/txpool.go | 4 ++-- eth/gasprice/feehistory.go | 4 ++-- internal/ethapi/api.go | 4 ++-- miner/worker.go | 3 ++- 9 files changed, 18 insertions(+), 14 deletions(-) rename consensus/misc/{ => eip1559}/eip1559.go (99%) diff --git a/consensus/XDPoS/engines/engine_v1/engine.go b/consensus/XDPoS/engines/engine_v1/engine.go index 61fca6fdb870..b93a8729d6ef 100644 --- a/consensus/XDPoS/engines/engine_v1/engine.go +++ b/consensus/XDPoS/engines/engine_v1/engine.go @@ -19,6 +19,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" "github.com/XinFinOrg/XDPoSChain/consensus/clique" "github.com/XinFinOrg/XDPoSChain/consensus/misc" + "github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/crypto" @@ -242,7 +243,7 @@ func (x *XDPoS_v1) verifyCascadingFields(chain consensus.ChainReader, header *ty return utils.ErrInvalidTimestamp } // Verify the header's EIP-1559 attributes. - if err := misc.VerifyEip1559Header(chain.Config(), header); err != nil { + if err := eip1559.VerifyEip1559Header(chain.Config(), header); err != nil { return err } diff --git a/consensus/XDPoS/engines/engine_v2/verifyHeader.go b/consensus/XDPoS/engines/engine_v2/verifyHeader.go index becbe8c5c17e..4e18e2031e68 100644 --- a/consensus/XDPoS/engines/engine_v2/verifyHeader.go +++ b/consensus/XDPoS/engines/engine_v2/verifyHeader.go @@ -9,6 +9,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" "github.com/XinFinOrg/XDPoSChain/consensus/misc" + "github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/log" ) @@ -95,7 +96,7 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade return utils.ErrInvalidUncleHash } // Verify the header's EIP-1559 attributes. - if err := misc.VerifyEip1559Header(chain.Config(), header); err != nil { + if err := eip1559.VerifyEip1559Header(chain.Config(), header); err != nil { return err } if header.Difficulty.Cmp(big.NewInt(1)) != 0 { diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 7b978a6da1c3..92806693aeb4 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -28,6 +28,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/misc" + "github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/params" @@ -254,7 +255,7 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent * return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit) } // Verify the header's EIP-1559 attributes. - if err := misc.VerifyEip1559Header(chain.Config(), header); err != nil { + if err := eip1559.VerifyEip1559Header(chain.Config(), header); err != nil { return err } diff --git a/consensus/misc/eip1559.go b/consensus/misc/eip1559/eip1559.go similarity index 99% rename from consensus/misc/eip1559.go rename to consensus/misc/eip1559/eip1559.go index c993d97062a2..0763ebd9c9bd 100644 --- a/consensus/misc/eip1559.go +++ b/consensus/misc/eip1559/eip1559.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package misc +package eip1559 import ( "fmt" diff --git a/core/chain_makers.go b/core/chain_makers.go index 5f46caf43d5f..156a54ad0f7c 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -20,11 +20,11 @@ import ( "fmt" "math/big" - "github.com/XinFinOrg/XDPoSChain/core/rawdb" - "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/misc" + "github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" @@ -285,7 +285,7 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S Time: time, } - header.BaseFee = misc.CalcBaseFee(chain.Config(), header) + header.BaseFee = eip1559.CalcBaseFee(chain.Config(), header) return header } diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 7ad1f3cfd4ba..7b755747fac4 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -29,7 +29,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/prque" "github.com/XinFinOrg/XDPoSChain/consensus" - "github.com/XinFinOrg/XDPoSChain/consensus/misc" + "github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" @@ -1319,7 +1319,7 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt if reset != nil { pool.demoteUnexecutables() if reset.newHead != nil && pool.chainconfig.IsEIP1559(new(big.Int).Add(reset.newHead.Number, big.NewInt(1))) { - pendingBaseFee := misc.CalcBaseFee(pool.chainconfig, reset.newHead) + pendingBaseFee := eip1559.CalcBaseFee(pool.chainconfig, reset.newHead) pool.priced.SetBaseFee(pendingBaseFee) } // Update all accounts to the latest known pending nonce diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index f32cff1ccb9d..09e2f991e7cc 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -27,7 +27,7 @@ import ( "sync/atomic" "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/consensus/misc" + "github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/rpc" @@ -89,7 +89,7 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { bf.results.baseFee = new(big.Int) } if chainconfig.IsEIP1559(big.NewInt(int64(bf.blockNumber + 1))) { - bf.results.nextBaseFee = misc.CalcBaseFee(chainconfig, bf.header) + bf.results.nextBaseFee = eip1559.CalcBaseFee(chainconfig, bf.header) } else { bf.results.nextBaseFee = new(big.Int) } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 5e761e8d147d..677c576caaa3 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -39,7 +39,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" "github.com/XinFinOrg/XDPoSChain/consensus/ethash" - "github.com/XinFinOrg/XDPoSChain/consensus/misc" + "github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559" contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/state" @@ -1939,7 +1939,7 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber func newRPCPendingTransaction(tx *types.Transaction, current *types.Header, config *params.ChainConfig) *RPCTransaction { var baseFee *big.Int if current != nil { - baseFee = misc.CalcBaseFee(config, current) + baseFee = eip1559.CalcBaseFee(config, current) } return newRPCTransaction(tx, common.Hash{}, 0, 0, baseFee) } diff --git a/miner/worker.go b/miner/worker.go index ac065d650e0b..5395733990ac 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -32,6 +32,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" "github.com/XinFinOrg/XDPoSChain/consensus/misc" + "github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559" "github.com/XinFinOrg/XDPoSChain/contracts" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/state" @@ -616,7 +617,7 @@ func (w *worker) commitNewWork() { Time: big.NewInt(tstamp), } // Set baseFee if we are on an EIP-1559 chain - header.BaseFee = misc.CalcBaseFee(w.config, header) + header.BaseFee = eip1559.CalcBaseFee(w.config, header) // Only set the coinbase if we are mining (avoid spurious block rewards) if atomic.LoadInt32(&w.mining) == 1 { From 2a9d4501016fcc30efb149dc3b1888d82bd4768f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 11 Jun 2024 18:20:50 +0800 Subject: [PATCH 152/479] core/types: add EffectiveGasPrice in Receipt (#26713) --- core/blockchain.go | 44 +++--- core/rawdb/accessors_chain.go | 48 ++++-- core/types/gen_receipt_json.go | 6 + core/types/receipt.go | 16 +- core/types/receipt_test.go | 261 ++++++++++++++++++++------------- core/types/transaction.go | 12 ++ core/types/tx_access_list.go | 4 + core/types/tx_dynamic_fee.go | 11 ++ core/types/tx_legacy.go | 4 + go.mod | 1 + internal/ethapi/api.go | 13 +- light/odr_util.go | 2 +- 12 files changed, 272 insertions(+), 150 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 05fbdeb3a88c..c96865d05ae3 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1067,7 +1067,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ continue } // Compute all the non-consensus fields of the receipts - if err := receipts.DeriveFields(bc.chainConfig, blockHash, blockNumber, block.Transactions()); err != nil { + if err := receipts.DeriveFields(bc.chainConfig, blockHash, blockNumber, block.BaseFee(), block.Transactions()); err != nil { return i, fmt.Errorf("failed to set receipts data: %v", err) } // Write all the data out into the database @@ -2088,6 +2088,25 @@ func countTransactions(chain []*types.Block) (c int) { return c } +// collectLogs collects the logs that were generated or removed during +// the processing of a block. These logs are later announced as deleted or reborn. +func (bc *BlockChain) collectLogs(b *types.Block, removed bool) []*types.Log { + receipts := rawdb.ReadRawReceipts(bc.db, b.Hash(), b.NumberU64()) + if err := receipts.DeriveFields(bc.chainConfig, b.Hash(), b.NumberU64(), b.BaseFee(), b.Transactions()); err != nil { + log.Error("Failed to derive block receipts fields", "hash", b.Hash(), "number", b.NumberU64(), "err", err) + } + + var logs []*types.Log + for _, receipt := range receipts { + for _, log := range receipt.Logs { + l := *log + l.Removed = removed + logs = append(logs, &l) + } + } + return logs +} + // reorgs takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them // to be part of the new canonical chain and accumulates potential missing transactions and post an // event about them @@ -2098,20 +2117,6 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { commonBlock *types.Block deletedTxs types.Transactions deletedLogs []*types.Log - // collectLogs collects the logs that were generated during the - // processing of the block that corresponds with the given hash. - // These logs are later announced as deleted. - collectLogs = func(h common.Hash) { - // Coalesce logs and set 'Removed'. - receipts := GetBlockReceipts(bc.db, h, bc.hc.GetBlockNumber(h)) - for _, receipt := range receipts { - for _, log := range receipt.Logs { - del := *log - del.Removed = true - deletedLogs = append(deletedLogs, &del) - } - } - } ) log.Warn("Reorg", "oldBlock hash", oldBlock.Hash().Hex(), "number", oldBlock.NumberU64(), "newBlock hash", newBlock.Hash().Hex(), "number", newBlock.NumberU64()) @@ -2121,8 +2126,9 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { for ; oldBlock != nil && oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1) { oldChain = append(oldChain, oldBlock) deletedTxs = append(deletedTxs, oldBlock.Transactions()...) - - collectLogs(oldBlock.Hash()) + if logs := bc.collectLogs(oldBlock, true); len(logs) > 0 { + deletedLogs = append(deletedLogs, logs...) + } } } else { // reduce new chain and append new chain blocks for inserting later on @@ -2146,7 +2152,9 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { oldChain = append(oldChain, oldBlock) newChain = append(newChain, newBlock) deletedTxs = append(deletedTxs, oldBlock.Transactions()...) - collectLogs(oldBlock.Hash()) + if logs := bc.collectLogs(oldBlock, true); len(logs) > 0 { + deletedLogs = append(deletedLogs, logs...) + } oldBlock, newBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1), bc.GetBlock(newBlock.ParentHash(), newBlock.NumberU64()-1) if oldBlock == nil { diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 6790fe141c58..ebed7acffcce 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/binary" "errors" + "math/big" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/types" @@ -36,15 +37,6 @@ func WriteCanonicalHash(db ethdb.KeyValueWriter, hash common.Hash, number uint64 } } -// WriteHeaderNumber stores the hash->number mapping. -func WriteHeaderNumber(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { - key := headerNumberKey(hash) - enc := encodeBlockNumber(number) - if err := db.Put(key, enc); err != nil { - log.Crit("Failed to store hash to number mapping", "err", err) - } -} - // ReadHeaderNumber returns the header number assigned to a hash. func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 { data, _ := db.Get(headerNumberKey(hash)) @@ -55,6 +47,15 @@ func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 { return &number } +// WriteHeaderNumber stores the hash->number mapping. +func WriteHeaderNumber(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { + key := headerNumberKey(hash) + enc := encodeBlockNumber(number) + if err := db.Put(key, enc); err != nil { + log.Crit("Failed to store hash to number mapping", "err", err) + } +} + // WriteHeadBlockHash stores the head block's hash. func WriteHeadBlockHash(db ethdb.KeyValueWriter, hash common.Hash) { if err := db.Put(headBlockKey, hash.Bytes()); err != nil { @@ -62,6 +63,26 @@ func WriteHeadBlockHash(db ethdb.KeyValueWriter, hash common.Hash) { } } +// ReadHeaderRLP retrieves a block header in its raw RLP database encoding. +func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue { + data, _ := db.Get(headerKey(number, hash)) + return data +} + +// ReadHeader retrieves the block header corresponding to the hash. +func ReadHeader(db ethdb.Reader, hash common.Hash, number uint64) *types.Header { + data := ReadHeaderRLP(db, hash, number) + if len(data) == 0 { + return nil + } + header := new(types.Header) + if err := rlp.Decode(bytes.NewReader(data), header); err != nil { + log.Error("Invalid block header RLP", "hash", hash, "err", err) + return nil + } + return header +} + // WriteHeader stores a block header into the database and also stores the hash- // to-number mapping. func WriteHeader(db ethdb.KeyValueWriter, header *types.Header) { @@ -215,7 +236,14 @@ func ReadReceipts(db ethdb.Reader, hash common.Hash, number uint64, config *para log.Error("Missing body but have receipt", "hash", hash, "number", number) return nil } - if err := receipts.DeriveFields(config, hash, number, body.Transactions); err != nil { + header := ReadHeader(db, hash, number) + var baseFee *big.Int + if header == nil { + baseFee = big.NewInt(0) + } else { + baseFee = header.BaseFee + } + if err := receipts.DeriveFields(config, hash, number, baseFee, body.Transactions); err != nil { log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err) return nil } diff --git a/core/types/gen_receipt_json.go b/core/types/gen_receipt_json.go index 89379a823a16..ecbb14ba6ca4 100644 --- a/core/types/gen_receipt_json.go +++ b/core/types/gen_receipt_json.go @@ -25,6 +25,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) { TxHash common.Hash `json:"transactionHash" gencodec:"required"` ContractAddress common.Address `json:"contractAddress"` GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice,omitempty"` BlockHash common.Hash `json:"blockHash,omitempty"` BlockNumber *hexutil.Big `json:"blockNumber,omitempty"` TransactionIndex hexutil.Uint `json:"transactionIndex"` @@ -39,6 +40,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) { enc.TxHash = r.TxHash enc.ContractAddress = r.ContractAddress enc.GasUsed = hexutil.Uint64(r.GasUsed) + enc.EffectiveGasPrice = (*hexutil.Big)(r.EffectiveGasPrice) enc.BlockHash = r.BlockHash enc.BlockNumber = (*hexutil.Big)(r.BlockNumber) enc.TransactionIndex = hexutil.Uint(r.TransactionIndex) @@ -57,6 +59,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error { TxHash *common.Hash `json:"transactionHash" gencodec:"required"` ContractAddress *common.Address `json:"contractAddress"` GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice,omitempty"` BlockHash *common.Hash `json:"blockHash,omitempty"` BlockNumber *hexutil.Big `json:"blockNumber,omitempty"` TransactionIndex *hexutil.Uint `json:"transactionIndex"` @@ -97,6 +100,9 @@ func (r *Receipt) UnmarshalJSON(input []byte) error { return errors.New("missing required field 'gasUsed' for Receipt") } r.GasUsed = uint64(*dec.GasUsed) + if dec.EffectiveGasPrice != nil { + r.EffectiveGasPrice = (*big.Int)(dec.EffectiveGasPrice) + } if dec.BlockHash != nil { r.BlockHash = *dec.BlockHash } diff --git a/core/types/receipt.go b/core/types/receipt.go index b4c22a297218..8a79d7a5ee7f 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -59,10 +59,10 @@ type Receipt struct { Logs []*Log `json:"logs" gencodec:"required"` // Implementation fields: These fields are added by geth when processing a transaction. - // They are stored in the chain database. - TxHash common.Hash `json:"transactionHash" gencodec:"required"` - ContractAddress common.Address `json:"contractAddress"` - GasUsed uint64 `json:"gasUsed" gencodec:"required"` + TxHash common.Hash `json:"transactionHash" gencodec:"required"` + ContractAddress common.Address `json:"contractAddress"` + GasUsed uint64 `json:"gasUsed" gencodec:"required"` + EffectiveGasPrice *big.Int `json:"effectiveGasPrice"` // Inclusion information: These fields provide information about the inclusion of the // transaction corresponding to this receipt. @@ -331,7 +331,7 @@ func (rs Receipts) GetRlp(i int) []byte { // DeriveFields fills the receipts with their computed fields based on consensus // data and contextual infos like containing block and transactions. -func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, txs Transactions) error { +func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, baseFee *big.Int, txs []*Transaction) error { signer := MakeSigner(config, new(big.Int).SetUint64(number)) logIndex := uint(0) @@ -343,6 +343,8 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu rs[i].Type = txs[i].Type() rs[i].TxHash = txs[i].Hash() + rs[i].EffectiveGasPrice = txs[i].inner.effectiveGasPrice(new(big.Int), baseFee) + // block location fields rs[i].BlockHash = hash rs[i].BlockNumber = new(big.Int).SetUint64(number) @@ -353,13 +355,17 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu // Deriving the signer is expensive, only do if it's actually needed from, _ := Sender(signer, txs[i]) rs[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce()) + } else { + rs[i].ContractAddress = common.Address{} } + // The used gas can be calculated based on previous r if i == 0 { rs[i].GasUsed = rs[i].CumulativeGasUsed } else { rs[i].GasUsed = rs[i].CumulativeGasUsed - rs[i-1].CumulativeGasUsed } + // The derived log fields can simply be set from the block and transaction for j := 0; j < len(rs[i].Logs); j++ { rs[i].Logs[j].BlockNumber = number diff --git a/core/types/receipt_test.go b/core/types/receipt_test.go index a71648b17fa0..620c882301dc 100644 --- a/core/types/receipt_test.go +++ b/core/types/receipt_test.go @@ -18,15 +18,16 @@ package types import ( "bytes" + "encoding/json" "math" "math/big" "reflect" "testing" "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rlp" + "github.com/kylelemons/godebug/diff" ) var ( @@ -183,162 +184,212 @@ func TestDeriveFields(t *testing.T) { // Create a few transactions to have receipts for to2 := common.HexToAddress("0x2") to3 := common.HexToAddress("0x3") + to4 := common.HexToAddress("0x4") + to5 := common.HexToAddress("0x5") txs := Transactions{ NewTx(&LegacyTx{ Nonce: 1, Value: big.NewInt(1), Gas: 1, - GasPrice: big.NewInt(1), + GasPrice: big.NewInt(11), }), NewTx(&LegacyTx{ To: &to2, Nonce: 2, Value: big.NewInt(2), Gas: 2, - GasPrice: big.NewInt(2), + GasPrice: big.NewInt(22), }), NewTx(&AccessListTx{ To: &to3, Nonce: 3, Value: big.NewInt(3), Gas: 3, - GasPrice: big.NewInt(3), + GasPrice: big.NewInt(33), + }), + // EIP-1559 transactions. + NewTx(&DynamicFeeTx{ + To: &to4, + Nonce: 4, + Value: big.NewInt(4), + Gas: 4, + GasTipCap: big.NewInt(44), + GasFeeCap: big.NewInt(1045), + }), + NewTx(&DynamicFeeTx{ + To: &to5, + Nonce: 5, + Value: big.NewInt(5), + Gas: 5, + GasTipCap: big.NewInt(56), + GasFeeCap: big.NewInt(1055), }), } + + blockNumber := big.NewInt(1) + blockHash := common.BytesToHash([]byte{0x03, 0x14}) + // Create the corresponding receipts receipts := Receipts{ &Receipt{ Status: ReceiptStatusFailed, CumulativeGasUsed: 1, Logs: []*Log{ - {Address: common.BytesToAddress([]byte{0x11})}, - {Address: common.BytesToAddress([]byte{0x01, 0x11})}, + { + Address: common.BytesToAddress([]byte{0x11}), + // derived fields: + BlockNumber: blockNumber.Uint64(), + TxHash: txs[0].Hash(), + TxIndex: 0, + BlockHash: blockHash, + Index: 0, + }, + { + Address: common.BytesToAddress([]byte{0x01, 0x11}), + // derived fields: + BlockNumber: blockNumber.Uint64(), + TxHash: txs[0].Hash(), + TxIndex: 0, + BlockHash: blockHash, + Index: 1, + }, }, - TxHash: txs[0].Hash(), - ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}), - GasUsed: 1, + // derived fields: + TxHash: txs[0].Hash(), + ContractAddress: common.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"), + GasUsed: 1, + EffectiveGasPrice: big.NewInt(11), + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: 0, }, &Receipt{ PostState: common.Hash{2}.Bytes(), CumulativeGasUsed: 3, Logs: []*Log{ - {Address: common.BytesToAddress([]byte{0x22})}, - {Address: common.BytesToAddress([]byte{0x02, 0x22})}, + { + Address: common.BytesToAddress([]byte{0x22}), + // derived fields: + BlockNumber: blockNumber.Uint64(), + TxHash: txs[1].Hash(), + TxIndex: 1, + BlockHash: blockHash, + Index: 2, + }, + { + Address: common.BytesToAddress([]byte{0x02, 0x22}), + // derived fields: + BlockNumber: blockNumber.Uint64(), + TxHash: txs[1].Hash(), + TxIndex: 1, + BlockHash: blockHash, + Index: 3, + }, }, - TxHash: txs[1].Hash(), - ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}), - GasUsed: 2, + // derived fields: + TxHash: txs[1].Hash(), + GasUsed: 2, + EffectiveGasPrice: big.NewInt(22), + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: 1, }, &Receipt{ Type: AccessListTxType, PostState: common.Hash{3}.Bytes(), CumulativeGasUsed: 6, - Logs: []*Log{ - {Address: common.BytesToAddress([]byte{0x33})}, - {Address: common.BytesToAddress([]byte{0x03, 0x33})}, - }, - TxHash: txs[2].Hash(), - ContractAddress: common.BytesToAddress([]byte{0x03, 0x33, 0x33}), - GasUsed: 3, + Logs: []*Log{}, + // derived fields: + TxHash: txs[2].Hash(), + GasUsed: 3, + EffectiveGasPrice: big.NewInt(33), + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: 2, + }, + &Receipt{ + Type: DynamicFeeTxType, + PostState: common.Hash{4}.Bytes(), + CumulativeGasUsed: 10, + Logs: []*Log{}, + // derived fields: + TxHash: txs[3].Hash(), + GasUsed: 4, + EffectiveGasPrice: big.NewInt(1044), + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: 3, + }, + &Receipt{ + Type: DynamicFeeTxType, + PostState: common.Hash{5}.Bytes(), + CumulativeGasUsed: 15, + Logs: []*Log{}, + // derived fields: + TxHash: txs[4].Hash(), + GasUsed: 5, + EffectiveGasPrice: big.NewInt(1055), + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: 4, }, } - // Clear all the computed fields and re-derive them - number := big.NewInt(1) - hash := common.BytesToHash([]byte{0x03, 0x14}) - clearComputedFieldsOnReceipts(t, receipts) - if err := receipts.DeriveFields(params.TestChainConfig, hash, number.Uint64(), txs); err != nil { + // Re-derive receipts. + basefee := big.NewInt(1000) + derivedReceipts := clearComputedFieldsOnReceipts(receipts) + err := Receipts(derivedReceipts).DeriveFields(params.TestChainConfig, blockHash, blockNumber.Uint64(), basefee, txs) + if err != nil { t.Fatalf("DeriveFields(...) = %v, want ", err) } - // Iterate over all the computed fields and check that they're correct - signer := MakeSigner(params.TestChainConfig, number) - logIndex := uint(0) - for i := range receipts { - if receipts[i].Type != txs[i].Type() { - t.Errorf("receipts[%d].Type = %d, want %d", i, receipts[i].Type, txs[i].Type()) - } - if receipts[i].TxHash != txs[i].Hash() { - t.Errorf("receipts[%d].TxHash = %s, want %s", i, receipts[i].TxHash.String(), txs[i].Hash().String()) - } - if receipts[i].BlockHash != hash { - t.Errorf("receipts[%d].BlockHash = %s, want %s", i, receipts[i].BlockHash.String(), hash.String()) - } - if receipts[i].BlockNumber.Cmp(number) != 0 { - t.Errorf("receipts[%c].BlockNumber = %s, want %s", i, receipts[i].BlockNumber.String(), number.String()) - } - if receipts[i].TransactionIndex != uint(i) { - t.Errorf("receipts[%d].TransactionIndex = %d, want %d", i, receipts[i].TransactionIndex, i) - } - if receipts[i].GasUsed != txs[i].Gas() { - t.Errorf("receipts[%d].GasUsed = %d, want %d", i, receipts[i].GasUsed, txs[i].Gas()) - } - if txs[i].To() != nil && receipts[i].ContractAddress != (common.Address{}) { - t.Errorf("receipts[%d].ContractAddress = %s, want %s", i, receipts[i].ContractAddress.String(), (common.Address{}).String()) - } - from, _ := Sender(signer, txs[i]) - contractAddress := crypto.CreateAddress(from, txs[i].Nonce()) - if txs[i].To() == nil && receipts[i].ContractAddress != contractAddress { - t.Errorf("receipts[%d].ContractAddress = %s, want %s", i, receipts[i].ContractAddress.String(), contractAddress.String()) - } - for j := range receipts[i].Logs { - if receipts[i].Logs[j].BlockNumber != number.Uint64() { - t.Errorf("receipts[%d].Logs[%d].BlockNumber = %d, want %d", i, j, receipts[i].Logs[j].BlockNumber, number.Uint64()) - } - if receipts[i].Logs[j].BlockHash != hash { - t.Errorf("receipts[%d].Logs[%d].BlockHash = %s, want %s", i, j, receipts[i].Logs[j].BlockHash.String(), hash.String()) - } - if receipts[i].Logs[j].TxHash != txs[i].Hash() { - t.Errorf("receipts[%d].Logs[%d].TxHash = %s, want %s", i, j, receipts[i].Logs[j].TxHash.String(), txs[i].Hash().String()) - } - if receipts[i].Logs[j].TxIndex != uint(i) { - t.Errorf("receipts[%d].Logs[%d].TransactionIndex = %d, want %d", i, j, receipts[i].Logs[j].TxIndex, i) - } - if receipts[i].Logs[j].Index != logIndex { - t.Errorf("receipts[%d].Logs[%d].Index = %d, want %d", i, j, receipts[i].Logs[j].Index, logIndex) - } - logIndex++ - } + // Check diff of receipts against derivedReceipts. + r1, err := json.MarshalIndent(receipts, "", " ") + if err != nil { + t.Fatal("error marshaling input receipts:", err) + } + r2, err := json.MarshalIndent(derivedReceipts, "", " ") + if err != nil { + t.Fatal("error marshaling derived receipts:", err) + } + d := diff.Diff(string(r1), string(r2)) + if d != "" { + t.Fatal("receipts differ:", d) } } -func clearComputedFieldsOnReceipts(t *testing.T, receipts Receipts) { - t.Helper() - - for _, receipt := range receipts { - clearComputedFieldsOnReceipt(t, receipt) +func clearComputedFieldsOnReceipts(receipts []*Receipt) []*Receipt { + r := make([]*Receipt, len(receipts)) + for i, receipt := range receipts { + r[i] = clearComputedFieldsOnReceipt(receipt) } + return r } -func clearComputedFieldsOnReceipt(t *testing.T, receipt *Receipt) { - t.Helper() - - receipt.TxHash = common.Hash{} - receipt.BlockHash = common.Hash{} - receipt.BlockNumber = big.NewInt(math.MaxUint32) - receipt.TransactionIndex = math.MaxUint32 - receipt.ContractAddress = common.Address{} - receipt.GasUsed = 0 - - clearComputedFieldsOnLogs(t, receipt.Logs) +func clearComputedFieldsOnReceipt(receipt *Receipt) *Receipt { + cpy := *receipt + cpy.TxHash = common.Hash{0xff, 0xff, 0x11} + cpy.BlockHash = common.Hash{0xff, 0xff, 0x22} + cpy.BlockNumber = big.NewInt(math.MaxUint32) + cpy.TransactionIndex = math.MaxUint32 + cpy.ContractAddress = common.Address{0xff, 0xff, 0x33} + cpy.GasUsed = 0xffffffff + cpy.Logs = clearComputedFieldsOnLogs(receipt.Logs) + return &cpy } -func clearComputedFieldsOnLogs(t *testing.T, logs []*Log) { - t.Helper() - - for _, log := range logs { - clearComputedFieldsOnLog(t, log) +func clearComputedFieldsOnLogs(logs []*Log) []*Log { + l := make([]*Log, len(logs)) + for i, log := range logs { + cpy := *log + cpy.BlockNumber = math.MaxUint32 + cpy.BlockHash = common.Hash{} + cpy.TxHash = common.Hash{} + cpy.TxIndex = math.MaxUint32 + cpy.Index = math.MaxUint32 + l[i] = &cpy } -} - -func clearComputedFieldsOnLog(t *testing.T, log *Log) { - t.Helper() - - log.BlockNumber = math.MaxUint32 - log.BlockHash = common.Hash{} - log.TxHash = common.Hash{} - log.TxIndex = math.MaxUint32 - log.Index = math.MaxUint32 + return l } // TestTypedReceiptEncodingDecoding reproduces a flaw that existed in the receipt diff --git a/core/types/transaction.go b/core/types/transaction.go index 7d4c12959dda..95f5e3b8acf3 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -99,6 +99,14 @@ type TxData interface { rawSignatureValues() (v, r, s *big.Int) setSignatureValues(chainID, v, r, s *big.Int) + + // effectiveGasPrice computes the gas price paid by the transaction, given + // the inclusion block baseFee. + // + // Unlike other TxData methods, the returned *big.Int should be an independent + // copy of the computed value, i.e. callers are allowed to mutate the result. + // Method implementations can use 'dst' to store the result. + effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int } // EncodeRLP implements rlp.Encoder @@ -412,6 +420,10 @@ func (tx *Transaction) Size() common.StorageSize { return common.StorageSize(c) } +func (tx *Transaction) EffectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int { + return tx.inner.effectiveGasPrice(dst, baseFee) +} + // AsMessage returns the transaction as a core.Message. func (tx *Transaction) AsMessage(s Signer, balanceFee, blockNumber, baseFee *big.Int) (Message, error) { msg := Message{ diff --git a/core/types/tx_access_list.go b/core/types/tx_access_list.go index f01889238f74..f2d4ee4881d3 100644 --- a/core/types/tx_access_list.go +++ b/core/types/tx_access_list.go @@ -106,6 +106,10 @@ func (tx *AccessListTx) value() *big.Int { return tx.Value } func (tx *AccessListTx) nonce() uint64 { return tx.Nonce } func (tx *AccessListTx) to() *common.Address { return tx.To } +func (tx *AccessListTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int { + return dst.Set(tx.GasPrice) +} + func (tx *AccessListTx) rawSignatureValues() (v, r, s *big.Int) { return tx.V, tx.R, tx.S } diff --git a/core/types/tx_dynamic_fee.go b/core/types/tx_dynamic_fee.go index 4c2386e02ef7..0961ac1e7884 100644 --- a/core/types/tx_dynamic_fee.go +++ b/core/types/tx_dynamic_fee.go @@ -94,6 +94,17 @@ func (tx *DynamicFeeTx) value() *big.Int { return tx.Value } func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce } func (tx *DynamicFeeTx) to() *common.Address { return tx.To } +func (tx *DynamicFeeTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int { + if baseFee == nil { + return dst.Set(tx.GasFeeCap) + } + tip := dst.Sub(tx.GasFeeCap, baseFee) + if tip.Cmp(tx.GasTipCap) > 0 { + tip.Set(tx.GasTipCap) + } + return tip.Add(tip, baseFee) +} + func (tx *DynamicFeeTx) rawSignatureValues() (v, r, s *big.Int) { return tx.V, tx.R, tx.S } diff --git a/core/types/tx_legacy.go b/core/types/tx_legacy.go index b3a4ca9667cf..752aff10dd5e 100644 --- a/core/types/tx_legacy.go +++ b/core/types/tx_legacy.go @@ -104,6 +104,10 @@ func (tx *LegacyTx) value() *big.Int { return tx.Value } func (tx *LegacyTx) nonce() uint64 { return tx.Nonce } func (tx *LegacyTx) to() *common.Address { return tx.To } +func (tx *LegacyTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int { + return dst.Set(tx.GasPrice) +} + func (tx *LegacyTx) rawSignatureValues() (v, r, s *big.Int) { return tx.V, tx.R, tx.S } diff --git a/go.mod b/go.mod index b7baee7256b0..9e5d403aa9a5 100644 --- a/go.mod +++ b/go.mod @@ -63,6 +63,7 @@ require ( github.com/google/uuid v1.3.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/maruel/panicparse v0.0.0-20160720141634-ad661195ed0e // indirect github.com/maruel/ut v1.0.2 // indirect github.com/mattn/go-isatty v0.0.17 // indirect diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 677c576caaa3..53678ebf6414 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2254,18 +2254,9 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha "logs": receipt.Logs, "logsBloom": receipt.Bloom, "type": hexutil.Uint(tx.Type()), + "effectiveGasPrice": (*hexutil.Big)(receipt.EffectiveGasPrice), } - // Assign the effective gas price paid - if !s.b.ChainConfig().IsEIP1559(bigblock) { - fields["effectiveGasPrice"] = hexutil.Uint64(tx.GasPrice().Uint64()) - } else { - header, err := s.b.HeaderByHash(ctx, blockHash) - if err != nil { - return nil, err - } - gasPrice := new(big.Int).Add(header.BaseFee, tx.EffectiveGasTipValue(header.BaseFee)) - fields["effectiveGasPrice"] = hexutil.Uint64(gasPrice.Uint64()) - } + // Assign receipt status or post state. if len(receipt.PostState) > 0 { fields["root"] = hexutil.Bytes(receipt.PostState) diff --git a/light/odr_util.go b/light/odr_util.go index 11ddda877ae4..7b94ebe7a33d 100644 --- a/light/odr_util.go +++ b/light/odr_util.go @@ -159,7 +159,7 @@ func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, num genesis := core.GetCanonicalHash(odr.Database(), 0) config, _ := core.GetChainConfig(odr.Database(), genesis) - if err := receipts.DeriveFields(config, hash, number, block.Transactions()); err != nil { + if err := receipts.DeriveFields(config, hash, number, block.BaseFee(), block.Transactions()); err != nil { return nil, err } core.WriteBlockReceipts(odr.Database(), hash, number, receipts) From a294c2080dd2e90c2d362b101fde44c908ff8b4f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 26 Jun 2024 14:20:25 +0800 Subject: [PATCH 153/479] all: fix null effectiveGasPrice --- accounts/abi/bind/backends/simulated.go | 6 +++++- core/blockchain.go | 18 +++++++++++++++++- core/rawdb/accessors_chain.go | 3 +++ core/rawdb/schema.go | 2 ++ eth/api_backend.go | 2 +- les/api_backend.go | 12 +++++++----- 6 files changed, 35 insertions(+), 8 deletions(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 48bc5fb5c9a6..4de5a89885dd 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -606,7 +606,11 @@ func (fb *filterBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*t } func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { - return core.GetBlockReceipts(fb.db, hash, core.GetBlockNumber(fb.db, hash)), nil + number := rawdb.ReadHeaderNumber(fb.db, hash) + if number == nil { + return nil, nil + } + return rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config()), nil } func (fb *filterBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) { diff --git a/core/blockchain.go b/core/blockchain.go index c96865d05ae3..845b939f657c 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -68,6 +68,7 @@ var ( const ( bodyCacheLimit = 256 blockCacheLimit = 256 + receiptsCacheLimit = 32 maxFutureBlocks = 256 maxTimeFutureBlocks = 30 badBlockLimit = 10 @@ -141,6 +142,7 @@ type BlockChain struct { bodyCache *lru.Cache[common.Hash, *types.Body] // Cache for the most recent block bodies bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue] // Cache for the most recent block bodies in RLP encoded format + receiptsCache *lru.Cache[common.Hash, types.Receipts] // Cache for the most recent block receipts blockCache *lru.Cache[common.Hash, *types.Block] // Cache for the most recent entire blocks resultProcess *lru.Cache[common.Hash, *ResultProcessBlock] // Cache for processed blocks calculatingBlock *lru.Cache[common.Hash, *CalculatedBlock] // Cache for processing blocks @@ -195,6 +197,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par quit: make(chan struct{}), bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit), bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit), + receiptsCache: lru.NewCache[common.Hash, types.Receipts](receiptsCacheLimit), blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit), futureBlocks: lru.NewCache[common.Hash, *types.Block](maxFutureBlocks), resultProcess: lru.NewCache[common.Hash, *ResultProcessBlock](blockCacheLimit), @@ -396,6 +399,7 @@ func (bc *BlockChain) SetHead(head uint64) error { // Clear out any stale content from the caches bc.bodyCache.Purge() bc.bodyRLPCache.Purge() + bc.receiptsCache.Purge() bc.blockCache.Purge() bc.futureBlocks.Purge() bc.blocksHashCache.Purge() @@ -808,7 +812,19 @@ func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block { // GetReceiptsByHash retrieves the receipts for all transactions in a given block. func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts { - return GetBlockReceipts(bc.db, hash, GetBlockNumber(bc.db, hash)) + if receipts, ok := bc.receiptsCache.Get(hash); ok { + return receipts + } + number := rawdb.ReadHeaderNumber(bc.db, hash) + if number == nil { + return nil + } + receipts := rawdb.ReadReceipts(bc.db, hash, *number, bc.chainConfig) + if receipts == nil { + return nil + } + bc.receiptsCache.Add(hash, receipts) + return receipts } // GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors. diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index ebed7acffcce..7073e8431a60 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -214,6 +214,9 @@ func ReadRawReceipts(db ethdb.Reader, hash common.Hash, number uint64) types.Rec receipts := make(types.Receipts, len(storageReceipts)) for i, storageReceipt := range storageReceipts { receipts[i] = (*types.Receipt)(storageReceipt) + receipts[i].BlockHash = hash + receipts[i].BlockNumber = new(big.Int).SetUint64(number) + receipts[i].TransactionIndex = uint(i) } return receipts } diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index 0a08eeed69ed..26d098347e74 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -25,7 +25,9 @@ import ( // The fields below define the low level database schema prefixing. var ( + // headBlockKey tracks the latest known full block's hash. headBlockKey = []byte("LastBlock") + // Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes). headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header headerHashSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + headerHashSuffix -> hash diff --git a/eth/api_backend.go b/eth/api_backend.go index ce8ddd05de6f..6d7f68fcd31d 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -237,7 +237,7 @@ func (b *EthApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*t } func (b *EthApiBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) { - return core.GetBlockReceipts(b.eth.chainDb, blockHash, core.GetBlockNumber(b.eth.chainDb, blockHash)), nil + return b.eth.blockchain.GetReceiptsByHash(blockHash), nil } func (b *EthApiBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) { diff --git a/les/api_backend.go b/les/api_backend.go index 93c94b0b2b6f..e43d43073520 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -24,18 +24,17 @@ import ( "os" "path/filepath" + "github.com/XinFinOrg/XDPoSChain/XDCx" "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate" "github.com/XinFinOrg/XDPoSChain/XDCxlending" - "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" - - "github.com/XinFinOrg/XDPoSChain/XDCx" - "github.com/XinFinOrg/XDPoSChain/accounts" + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/bloombits" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" @@ -171,7 +170,10 @@ func (b *LesApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*t } func (b *LesApiBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) { - return light.GetBlockReceipts(ctx, b.eth.odr, blockHash, core.GetBlockNumber(b.eth.chainDb, blockHash)) + if number := rawdb.ReadHeaderNumber(b.eth.chainDb, blockHash); number != nil { + return light.GetBlockReceipts(ctx, b.eth.odr, blockHash, *number) + } + return nil, nil } func (b *LesApiBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) { From b8236888d4d749d865eccac8cb628120e4e5f8e5 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 25 Jun 2024 17:07:59 +0800 Subject: [PATCH 154/479] internal/ethapi: avoid int overflow in GetTransactionReceipt (#26911) --- internal/ethapi/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 53678ebf6414..677f3ed5e929 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2231,7 +2231,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha if err != nil { return nil, err } - if len(receipts) <= int(index) { + if uint64(len(receipts)) <= index { return nil, nil } receipt := receipts[index] From 419f81f0226f286e10db52d196228f6f05b1d3cc Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 12 Jun 2024 11:13:22 +0800 Subject: [PATCH 155/479] eth/gasprice: change feehistory input type from int to uint64 (#26922) --- eth/api_backend.go | 2 +- eth/gasprice/feehistory.go | 14 +++++++------- eth/gasprice/feehistory_test.go | 4 ++-- eth/gasprice/gasprice.go | 6 +++--- internal/ethapi/api.go | 2 +- internal/ethapi/backend.go | 2 +- internal/ethapi/transaction_args_test.go | 2 +- les/api_backend.go | 2 +- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/eth/api_backend.go b/eth/api_backend.go index 6d7f68fcd31d..183a0f1f4485 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -349,7 +349,7 @@ func (b *EthApiBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) return b.gpo.SuggestTipCap(ctx) } -func (b *EthApiBackend) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock *big.Int, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) { +func (b *EthApiBackend) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock *big.Int, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) { return b.gpo.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) } diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index 09e2f991e7cc..2645312dca47 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -137,7 +137,7 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { // also returned if requested and available. // Note: an error is only returned if retrieving the head header has failed. If there are no // retrievable blocks in the specified range then zero block count is returned with no error. -func (oracle *Oracle) resolveBlockRange(ctx context.Context, reqEnd rpc.BlockNumber, blocks int) (*types.Block, []*types.Receipt, uint64, int, error) { +func (oracle *Oracle) resolveBlockRange(ctx context.Context, reqEnd rpc.BlockNumber, blocks uint64) (*types.Block, []*types.Receipt, uint64, uint64, error) { var ( headBlock *types.Header pendingBlock *types.Block @@ -193,8 +193,8 @@ func (oracle *Oracle) resolveBlockRange(ctx context.Context, reqEnd rpc.BlockNum return nil, nil, 0, 0, nil } // Ensure not trying to retrieve before genesis. - if int(reqEnd+1) < blocks { - blocks = int(reqEnd + 1) + if uint64(reqEnd+1) < blocks { + blocks = uint64(reqEnd + 1) } return pendingBlock, pendingReceipts, uint64(reqEnd), blocks, nil } @@ -213,7 +213,7 @@ func (oracle *Oracle) resolveBlockRange(ctx context.Context, reqEnd rpc.BlockNum // // Note: baseFee includes the next block after the newest of the returned range, because this // value can be derived from the newest block. -func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) { +func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedLastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) { if blocks < 1 { return common.Big0, nil, nil, nil, nil // returning with no data and no error means there are no retrievable blocks } @@ -242,7 +242,7 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast if err != nil || blocks == 0 { return common.Big0, nil, nil, nil, err } - oldestBlock := lastBlock + 1 - uint64(blocks) + oldestBlock := lastBlock + 1 - blocks var ( next = oldestBlock @@ -252,7 +252,7 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast for i, p := range rewardPercentiles { binary.LittleEndian.PutUint64(percentileKey[i*8:(i+1)*8], math.Float64bits(p)) } - for i := 0; i < maxBlockFetchers && i < blocks; i++ { + for i := 0; i < maxBlockFetchers && i < int(blocks); i++ { go func() { for { // Retrieve the next block number to fetch with this goroutine @@ -310,7 +310,7 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast if fees.err != nil { return common.Big0, nil, nil, nil, fees.err } - i := int(fees.blockNumber - oldestBlock) + i := fees.blockNumber - oldestBlock if fees.results.baseFee != nil { reward[i], baseFee[i], baseFee[i+1], gasUsedRatio[i] = fees.results.reward, fees.results.baseFee, fees.results.nextBaseFee, fees.results.gasUsedRatio } else { diff --git a/eth/gasprice/feehistory_test.go b/eth/gasprice/feehistory_test.go index c44bde49c486..4c372f0bcc14 100644 --- a/eth/gasprice/feehistory_test.go +++ b/eth/gasprice/feehistory_test.go @@ -28,8 +28,8 @@ import ( func TestFeeHistory(t *testing.T) { var cases = []struct { pending bool - maxHeader, maxBlock int - count int + maxHeader, maxBlock uint64 + count uint64 last rpc.BlockNumber percent []float64 expFirst uint64 diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index 9087b4bdd868..b59a95d296c6 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -42,8 +42,8 @@ var ( type Config struct { Blocks int Percentile int - MaxHeaderHistory int - MaxBlockHistory int + MaxHeaderHistory uint64 + MaxBlockHistory uint64 Default *big.Int `toml:",omitempty"` MaxPrice *big.Int `toml:",omitempty"` IgnorePrice *big.Int `toml:",omitempty"` @@ -71,7 +71,7 @@ type Oracle struct { fetchLock sync.Mutex checkBlocks, percentile int - maxHeaderHistory, maxBlockHistory int + maxHeaderHistory, maxBlockHistory uint64 historyCache *lru.Cache } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 677f3ed5e929..9a94fde719c9 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -111,7 +111,7 @@ type feeHistoryResult struct { // FeeHistory returns the fee market history. func (s *PublicEthereumAPI) FeeHistory(ctx context.Context, blockCount hexutil.Uint, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*feeHistoryResult, error) { - oldest, reward, baseFee, gasUsed, err := s.b.FeeHistory(ctx, int(blockCount), lastBlock, rewardPercentiles) + oldest, reward, baseFee, gasUsed, err := s.b.FeeHistory(ctx, uint64(blockCount), lastBlock, rewardPercentiles) if err != nil { return nil, err } diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index ea7e2de766b9..b9be939a6a42 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -49,7 +49,7 @@ type Backend interface { Downloader() *downloader.Downloader ProtocolVersion() int SuggestGasTipCap(ctx context.Context) (*big.Int, error) - FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) + FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) ChainDb() ethdb.Database AccountManager() *accounts.Manager RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index 3717cda5572b..c1e624f0486c 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -265,7 +265,7 @@ func (b *backendMock) ChainConfig() *params.ChainConfig { return b.config } // Other methods needed to implement Backend interface. func (b *backendMock) SyncProgress() ethereum.SyncProgress { return ethereum.SyncProgress{} } -func (b *backendMock) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) { +func (b *backendMock) FeeHistory(context.Context, uint64, rpc.BlockNumber, []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) { return nil, nil, nil, nil, nil } func (b *backendMock) ChainDb() ethdb.Database { return nil } diff --git a/les/api_backend.go b/les/api_backend.go index e43d43073520..fe8371c5a25d 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -282,7 +282,7 @@ func (b *LesApiBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) return b.gpo.SuggestTipCap(ctx) } -func (b *LesApiBackend) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock *big.Int, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) { +func (b *LesApiBackend) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock *big.Int, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) { return b.gpo.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) } From da9d2e29f5186fa6d7ee9a7dcbe9dcb412619e52 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 12 Jun 2024 17:44:36 +0800 Subject: [PATCH 156/479] core/types: fix discrepancy in receipt.EffectiveGasPrice json encoding tags (#27114) --- core/types/gen_receipt_json.go | 4 +- core/types/receipt.go | 3 +- core/types/receipt_test.go | 193 +++++++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+), 3 deletions(-) diff --git a/core/types/gen_receipt_json.go b/core/types/gen_receipt_json.go index ecbb14ba6ca4..c22dc8e21ec4 100644 --- a/core/types/gen_receipt_json.go +++ b/core/types/gen_receipt_json.go @@ -25,7 +25,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) { TxHash common.Hash `json:"transactionHash" gencodec:"required"` ContractAddress common.Address `json:"contractAddress"` GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` - EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice,omitempty"` + EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice"` BlockHash common.Hash `json:"blockHash,omitempty"` BlockNumber *hexutil.Big `json:"blockNumber,omitempty"` TransactionIndex hexutil.Uint `json:"transactionIndex"` @@ -59,7 +59,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error { TxHash *common.Hash `json:"transactionHash" gencodec:"required"` ContractAddress *common.Address `json:"contractAddress"` GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` - EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice,omitempty"` + EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice"` BlockHash *common.Hash `json:"blockHash,omitempty"` BlockNumber *hexutil.Big `json:"blockNumber,omitempty"` TransactionIndex *hexutil.Uint `json:"transactionIndex"` diff --git a/core/types/receipt.go b/core/types/receipt.go index 8a79d7a5ee7f..f858e8855eea 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -62,7 +62,7 @@ type Receipt struct { TxHash common.Hash `json:"transactionHash" gencodec:"required"` ContractAddress common.Address `json:"contractAddress"` GasUsed uint64 `json:"gasUsed" gencodec:"required"` - EffectiveGasPrice *big.Int `json:"effectiveGasPrice"` + EffectiveGasPrice *big.Int `json:"effectiveGasPrice"` // required, but tag omitted for backwards compatibility // Inclusion information: These fields provide information about the inclusion of the // transaction corresponding to this receipt. @@ -77,6 +77,7 @@ type receiptMarshaling struct { Status hexutil.Uint64 CumulativeGasUsed hexutil.Uint64 GasUsed hexutil.Uint64 + EffectiveGasPrice *hexutil.Big BlockNumber *hexutil.Big TransactionIndex hexutil.Uint } diff --git a/core/types/receipt_test.go b/core/types/receipt_test.go index 620c882301dc..2fee4fa88fcb 100644 --- a/core/types/receipt_test.go +++ b/core/types/receipt_test.go @@ -81,6 +81,167 @@ var ( }, Type: DynamicFeeTxType, } + + // Create a few transactions to have receipts for + to2 = common.HexToAddress("0x2") + to3 = common.HexToAddress("0x3") + to4 = common.HexToAddress("0x4") + to5 = common.HexToAddress("0x5") + to6 = common.HexToAddress("0x6") + to7 = common.HexToAddress("0x7") + txs = Transactions{ + NewTx(&LegacyTx{ + Nonce: 1, + Value: big.NewInt(1), + Gas: 1, + GasPrice: big.NewInt(11), + }), + NewTx(&LegacyTx{ + To: &to2, + Nonce: 2, + Value: big.NewInt(2), + Gas: 2, + GasPrice: big.NewInt(22), + }), + NewTx(&AccessListTx{ + To: &to3, + Nonce: 3, + Value: big.NewInt(3), + Gas: 3, + GasPrice: big.NewInt(33), + }), + // EIP-1559 transactions. + NewTx(&DynamicFeeTx{ + To: &to4, + Nonce: 4, + Value: big.NewInt(4), + Gas: 4, + GasTipCap: big.NewInt(44), + GasFeeCap: big.NewInt(1044), + }), + NewTx(&DynamicFeeTx{ + To: &to5, + Nonce: 5, + Value: big.NewInt(5), + Gas: 5, + GasTipCap: big.NewInt(55), + GasFeeCap: big.NewInt(1055), + }), + } + + blockNumber = big.NewInt(1) + blockTime = uint64(2) + blockHash = common.BytesToHash([]byte{0x03, 0x14}) + + // Create the corresponding receipts + receipts = Receipts{ + &Receipt{ + Status: ReceiptStatusFailed, + CumulativeGasUsed: 1, + Logs: []*Log{ + { + Address: common.BytesToAddress([]byte{0x11}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + // derived fields: + BlockNumber: blockNumber.Uint64(), + TxHash: txs[0].Hash(), + TxIndex: 0, + BlockHash: blockHash, + Index: 0, + }, + { + Address: common.BytesToAddress([]byte{0x01, 0x11}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + // derived fields: + BlockNumber: blockNumber.Uint64(), + TxHash: txs[0].Hash(), + TxIndex: 0, + BlockHash: blockHash, + Index: 1, + }, + }, + // derived fields: + TxHash: txs[0].Hash(), + ContractAddress: common.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"), + GasUsed: 1, + EffectiveGasPrice: big.NewInt(11), + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: 0, + }, + &Receipt{ + PostState: common.Hash{2}.Bytes(), + CumulativeGasUsed: 3, + Logs: []*Log{ + { + Address: common.BytesToAddress([]byte{0x22}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + // derived fields: + BlockNumber: blockNumber.Uint64(), + TxHash: txs[1].Hash(), + TxIndex: 1, + BlockHash: blockHash, + Index: 2, + }, + { + Address: common.BytesToAddress([]byte{0x02, 0x22}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + // derived fields: + BlockNumber: blockNumber.Uint64(), + TxHash: txs[1].Hash(), + TxIndex: 1, + BlockHash: blockHash, + Index: 3, + }, + }, + // derived fields: + TxHash: txs[1].Hash(), + GasUsed: 2, + EffectiveGasPrice: big.NewInt(22), + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: 1, + }, + &Receipt{ + Type: AccessListTxType, + PostState: common.Hash{3}.Bytes(), + CumulativeGasUsed: 6, + Logs: []*Log{}, + // derived fields: + TxHash: txs[2].Hash(), + GasUsed: 3, + EffectiveGasPrice: big.NewInt(33), + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: 2, + }, + &Receipt{ + Type: DynamicFeeTxType, + PostState: common.Hash{4}.Bytes(), + CumulativeGasUsed: 10, + Logs: []*Log{}, + // derived fields: + TxHash: txs[3].Hash(), + GasUsed: 4, + EffectiveGasPrice: big.NewInt(1044), + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: 3, + }, + &Receipt{ + Type: DynamicFeeTxType, + PostState: common.Hash{5}.Bytes(), + CumulativeGasUsed: 15, + Logs: []*Log{}, + // derived fields: + TxHash: txs[4].Hash(), + GasUsed: 5, + EffectiveGasPrice: big.NewInt(1055), + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: 4, + }, + } ) func TestDecodeEmptyTypedReceipt(t *testing.T) { @@ -92,6 +253,38 @@ func TestDecodeEmptyTypedReceipt(t *testing.T) { } } +// Test that we can marshal/unmarshal receipts to/from json without errors. +// This also confirms that our test receipts contain all the required fields. +func TestReceiptJSON(t *testing.T) { + for i := range receipts { + b, err := receipts[i].MarshalJSON() + if err != nil { + t.Fatal("error marshaling receipt to json:", err) + } + r := Receipt{} + err = r.UnmarshalJSON(b) + if err != nil { + t.Fatal("error unmarshaling receipt from json:", err) + } + } +} + +// Test we can still parse receipt without EffectiveGasPrice for backwards compatibility, even +// though it is required per the spec. +func TestEffectiveGasPriceNotRequired(t *testing.T) { + r := *receipts[0] + r.EffectiveGasPrice = nil + b, err := r.MarshalJSON() + if err != nil { + t.Fatal("error marshaling receipt to json:", err) + } + r2 := Receipt{} + err = r2.UnmarshalJSON(b) + if err != nil { + t.Fatal("error unmarshaling receipt from json:", err) + } +} + func TestLegacyReceiptDecoding(t *testing.T) { tests := []struct { name string From f23e1a648c5923ccff62d0b0cc9845f8aa98b3a0 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 25 Jun 2024 17:21:52 +0800 Subject: [PATCH 157/479] internal/ethapi: implement eth_getBlockReceipts (#27702) --- internal/ethapi/api.go | 36 ++---------------------------------- 1 file changed, 2 insertions(+), 34 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 9a94fde719c9..9328e592818b 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2239,38 +2239,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha // Derive the sender. bigblock := new(big.Int).SetUint64(blockNumber) signer := types.MakeSigner(s.b.ChainConfig(), bigblock) - from, _ := types.Sender(signer, tx) - - fields := map[string]interface{}{ - "blockHash": blockHash, - "blockNumber": hexutil.Uint64(blockNumber), - "transactionHash": hash, - "transactionIndex": hexutil.Uint64(index), - "from": from, - "to": tx.To(), - "gasUsed": hexutil.Uint64(receipt.GasUsed), - "cumulativeGasUsed": hexutil.Uint64(receipt.CumulativeGasUsed), - "contractAddress": nil, - "logs": receipt.Logs, - "logsBloom": receipt.Bloom, - "type": hexutil.Uint(tx.Type()), - "effectiveGasPrice": (*hexutil.Big)(receipt.EffectiveGasPrice), - } - - // Assign receipt status or post state. - if len(receipt.PostState) > 0 { - fields["root"] = hexutil.Bytes(receipt.PostState) - } else { - fields["status"] = hexutil.Uint(receipt.Status) - } - if receipt.Logs == nil { - fields["logs"] = [][]*types.Log{} - } - // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation - if receipt.ContractAddress != (common.Address{}) { - fields["contractAddress"] = receipt.ContractAddress - } - return fields, nil + return marshalReceipt(receipt, blockHash, blockNumber, signer, tx, int(index)), nil } // marshalReceipt marshals a transaction receipt into a JSON object. @@ -2290,8 +2259,7 @@ func marshalReceipt(receipt *types.Receipt, blockHash common.Hash, blockNumber u "logs": receipt.Logs, "logsBloom": receipt.Bloom, "type": hexutil.Uint(tx.Type()), - // uncomment below line after EIP-1559 - // TODO: "effectiveGasPrice": (*hexutil.Big)(receipt.EffectiveGasPrice), + "effectiveGasPrice": (*hexutil.Big)(receipt.EffectiveGasPrice), } // Assign receipt status or post state. From 4340e7b6f410605990ffd4bb813ad064dd47d70e Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 12 Jun 2024 17:55:20 +0800 Subject: [PATCH 158/479] ethclient: fix forwarding 1559 gas fields (#28462) --- ethclient/ethclient.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index ebaf68c64999..7df8f201fb83 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -579,5 +579,11 @@ func toCallArg(msg ethereum.CallMsg) interface{} { if msg.GasPrice != nil { arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) } + if msg.GasFeeCap != nil { + arg["maxFeePerGas"] = (*hexutil.Big)(msg.GasFeeCap) + } + if msg.GasTipCap != nil { + arg["maxPriorityFeePerGas"] = (*hexutil.Big)(msg.GasTipCap) + } return arg } From 0237985f58f433c39961b089f7e905181461be82 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 14 Jun 2024 11:41:25 +0800 Subject: [PATCH 159/479] internal/ethapi: ethSendTransaction check baseFee (#27834) --- internal/ethapi/transaction_args.go | 35 +++++++++++++++++------- internal/ethapi/transaction_args_test.go | 27 ++++++++++++++++-- 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index f23926592d2a..ca69427c7a1d 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -136,27 +136,42 @@ func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b Backend) erro if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") } - // If the tx has completely specified a fee mechanism, no default is needed. This allows users - // who are not yet synced past London to get defaults for other tx values. See - // https://github.com/ethereum/go-ethereum/pull/23274 for more information. + // If the tx has completely specified a fee mechanism, no default is needed. + // This allows users who are not yet synced past London to get defaults for + // other tx values. See https://github.com/ethereum/go-ethereum/pull/23274 + // for more information. eip1559ParamsSet := args.MaxFeePerGas != nil && args.MaxPriorityFeePerGas != nil - if (args.GasPrice != nil && !eip1559ParamsSet) || (args.GasPrice == nil && eip1559ParamsSet) { - // Sanity check the EIP-1559 fee parameters if present. - if args.GasPrice == nil && args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { + + // Sanity check the EIP-1559 fee parameters if present. + if args.GasPrice == nil && eip1559ParamsSet { + if args.MaxFeePerGas.ToInt().Sign() == 0 { + return errors.New("maxFeePerGas must be non-zero") + } + if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) } - return nil + return nil // No need to set anything, user already set MaxFeePerGas and MaxPriorityFeePerGas } - // Now attempt to fill in default value depending on whether London is active or not. + // Sanity check the non-EIP-1559 fee parameters. head := b.CurrentHeader() - if b.ChainConfig().IsEIP1559(head.Number) { + isEIP1559 := b.ChainConfig().IsEIP1559(head.Number) + if args.GasPrice != nil && !eip1559ParamsSet { + // Zero gas-price is not allowed after London fork + if args.GasPrice.ToInt().Sign() == 0 && isEIP1559 { + return errors.New("gasPrice must be non-zero after EIP-1559 fork") + } + return nil // No need to set anything, user already set GasPrice + } + + // Now attempt to fill in default value depending on whether London is active or not. + if isEIP1559 { // London is active, set maxPriorityFeePerGas and maxFeePerGas. if err := args.setLondonFeeDefaults(ctx, head, b); err != nil { return err } } else { if args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil { - return fmt.Errorf("maxFeePerGas and maxPriorityFeePerGas are not valid before London is active") + return errors.New("maxFeePerGas and maxPriorityFeePerGas are not valid before EIP-1559 is active") } // London not active, set gas price. price, err := b.SuggestGasTipCap(ctx) diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index c1e624f0486c..7e7f2499948b 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -18,6 +18,7 @@ package ethapi import ( "context" + "errors" "fmt" "math/big" "reflect" @@ -57,6 +58,7 @@ func TestSetFeeDefaults(t *testing.T) { var ( b = newBackendMock() + zero = (*hexutil.Big)(big.NewInt(0)) fortytwo = (*hexutil.Big)(big.NewInt(42)) maxFee = (*hexutil.Big)(new(big.Int).Add(new(big.Int).Mul(b.current.BaseFee, big.NewInt(2)), fortytwo.ToInt())) al = &types.AccessList{types.AccessTuple{Address: common.Address{0xaa}, StorageKeys: []common.Hash{{0x01}}}} @@ -71,6 +73,13 @@ func TestSetFeeDefaults(t *testing.T) { &TransactionArgs{GasPrice: fortytwo}, nil, }, + { + "legacy tx pre-London with zero price", + false, + &TransactionArgs{GasPrice: zero}, + &TransactionArgs{GasPrice: zero}, + nil, + }, { "legacy tx post-London, explicit gas price", true, @@ -78,6 +87,13 @@ func TestSetFeeDefaults(t *testing.T) { &TransactionArgs{GasPrice: fortytwo}, nil, }, + { + "legacy tx post-London with zero price", + true, + &TransactionArgs{GasPrice: zero}, + nil, + errors.New("gasPrice must be non-zero after EIP-1559 fork"), + }, // Access list txs { @@ -143,14 +159,14 @@ func TestSetFeeDefaults(t *testing.T) { false, &TransactionArgs{MaxFeePerGas: maxFee}, nil, - fmt.Errorf("maxFeePerGas and maxPriorityFeePerGas are not valid before London is active"), + fmt.Errorf("maxFeePerGas and maxPriorityFeePerGas are not valid before EIP-1559 is active"), }, { "dynamic fee tx pre-London, priorityFee set", false, &TransactionArgs{MaxPriorityFeePerGas: fortytwo}, nil, - fmt.Errorf("maxFeePerGas and maxPriorityFeePerGas are not valid before London is active"), + fmt.Errorf("maxFeePerGas and maxPriorityFeePerGas are not valid before EIP-1559 is active"), }, { "dynamic fee tx, maxFee < priorityFee", @@ -166,6 +182,13 @@ func TestSetFeeDefaults(t *testing.T) { nil, fmt.Errorf("maxFeePerGas (0x7) < maxPriorityFeePerGas (0x2a)"), }, + { + "dynamic fee tx post-London, explicit gas price", + true, + &TransactionArgs{MaxFeePerGas: zero, MaxPriorityFeePerGas: zero}, + nil, + errors.New("maxFeePerGas must be non-zero"), + }, // Misc { From bb55c1044b2ffdb68e20ab897aa4cf5a6dbb282a Mon Sep 17 00:00:00 2001 From: colin <102356659+colinlyguo@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:43:02 +0800 Subject: [PATCH 160/479] ethclient: apply accessList field in toCallArg (#28832) Co-authored-by: Felix Lange --- ethclient/ethclient.go | 3 +++ ethclient/gethclient/gethclient.go | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 7df8f201fb83..936c08153421 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -585,5 +585,8 @@ func toCallArg(msg ethereum.CallMsg) interface{} { if msg.GasTipCap != nil { arg["maxPriorityFeePerGas"] = (*hexutil.Big)(msg.GasTipCap) } + if msg.AccessList != nil { + arg["accessList"] = msg.AccessList + } return arg } diff --git a/ethclient/gethclient/gethclient.go b/ethclient/gethclient/gethclient.go index 93e82633849b..755705ab7fec 100644 --- a/ethclient/gethclient/gethclient.go +++ b/ethclient/gethclient/gethclient.go @@ -207,6 +207,15 @@ func toCallArg(msg ethereum.CallMsg) interface{} { if msg.GasPrice != nil { arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) } + if msg.GasFeeCap != nil { + arg["maxFeePerGas"] = (*hexutil.Big)(msg.GasFeeCap) + } + if msg.GasTipCap != nil { + arg["maxPriorityFeePerGas"] = (*hexutil.Big)(msg.GasTipCap) + } + if msg.AccessList != nil { + arg["accessList"] = msg.AccessList + } return arg } From 6b8dd86b527b38123d079d47bd2c7e208bfa3878 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 8 Feb 2024 13:34:38 +0100 Subject: [PATCH 161/479] eth/gasprice: fix percentile validation in eth_feeHistory (#28954) --- eth/gasprice/feehistory.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index 2645312dca47..c0570876c28c 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -229,8 +229,8 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL if p < 0 || p > 100 { return common.Big0, nil, nil, nil, fmt.Errorf("%w: %f", errInvalidPercentile, p) } - if i > 0 && p < rewardPercentiles[i-1] { - return common.Big0, nil, nil, nil, fmt.Errorf("%w: #%d:%f > #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p) + if i > 0 && p <= rewardPercentiles[i-1] { + return common.Big0, nil, nil, nil, fmt.Errorf("%w: #%d:%f >= #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p) } } var ( From bc14c672f2ca0e8a6a25771c89e9a24de41fc45c Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 14 Jun 2024 15:27:55 +0800 Subject: [PATCH 162/479] internal/ethapi: support unlimited rpc gas cap in eth_createAccessList (#28846) --- internal/ethapi/api.go | 18 ++++------ internal/ethapi/transaction_args.go | 51 +++++++++++++++++------------ 2 files changed, 36 insertions(+), 33 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 9328e592818b..504ce19bf27f 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -440,7 +440,7 @@ func (s *PrivateAccountAPI) signTransaction(ctx context.Context, args *Transacti return nil, err } // Set some sanity defaults and terminate on failure - if err := args.setDefaults(ctx, s.b); err != nil { + if err := args.setDefaults(ctx, s.b, false); err != nil { return nil, err } // Assemble the transaction and sign with the wallet @@ -2026,14 +2026,8 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH } owner := common.Address{} - // If the gas amount is not set, default to RPC gas cap. - if args.Gas == nil { - tmp := hexutil.Uint64(b.RPCGasCap()) - args.Gas = &tmp - } - // Ensure any missing fields are filled, extract the recipient and input data - if err := args.setDefaults(ctx, b); err != nil { + if err := args.setDefaults(ctx, b, true); err != nil { return nil, 0, nil, err } var to common.Address @@ -2365,7 +2359,7 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Tra } // Set some sanity defaults and terminate on failure - if err := args.setDefaults(ctx, s.b); err != nil { + if err := args.setDefaults(ctx, s.b, false); err != nil { return common.Hash{}, err } // Assemble the transaction and sign with the wallet @@ -2387,7 +2381,7 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Tra // processing (signing + broadcast). func (s *PublicTransactionPoolAPI) FillTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) { // Set some sanity defaults and terminate on failure - if err := args.setDefaults(ctx, s.b); err != nil { + if err := args.setDefaults(ctx, s.b, false); err != nil { return nil, err } // Assemble the transaction and obtain rlp @@ -3292,7 +3286,7 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Tra if args.Nonce == nil { return nil, errors.New("not specify Nonce") } - if err := args.setDefaults(ctx, s.b); err != nil { + if err := args.setDefaults(ctx, s.b, false); err != nil { return nil, err } // Before actually sign the transaction, ensure the transaction fee is reasonable. @@ -3341,7 +3335,7 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs Transact if sendArgs.Nonce == nil { return common.Hash{}, errors.New("missing transaction nonce in transaction spec") } - if err := sendArgs.setDefaults(ctx, s.b); err != nil { + if err := sendArgs.setDefaults(ctx, s.b, false); err != nil { return common.Hash{}, err } matchTx := sendArgs.toTransaction() diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index ca69427c7a1d..2202fe580e78 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -74,7 +74,7 @@ func (args *TransactionArgs) data() []byte { } // setDefaults fills in default values for unspecified tx fields. -func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { +func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGasEstimation bool) error { if err := args.setFeeDefaults(ctx, b); err != nil { return err } @@ -94,29 +94,38 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { if args.To == nil && len(args.data()) == 0 { return errors.New(`contract creation without any data provided`) } - // Estimate the gas usage if necessary. + if args.Gas == nil { - // These fields are immutable during the estimation, safe to - // pass the pointer directly. - data := args.data() - callArgs := TransactionArgs{ - From: args.From, - To: args.To, - GasPrice: args.GasPrice, - MaxFeePerGas: args.MaxFeePerGas, - MaxPriorityFeePerGas: args.MaxPriorityFeePerGas, - Value: args.Value, - Data: (*hexutil.Bytes)(&data), - AccessList: args.AccessList, - } - pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) - estimated, err := DoEstimateGas(ctx, b, callArgs, pendingBlockNr, nil, b.RPCGasCap()) - if err != nil { - return err + if skipGasEstimation { // Skip gas usage estimation if a precise gas limit is not critical, e.g., in non-transaction calls. + gas := hexutil.Uint64(b.RPCGasCap()) + if gas == 0 { + gas = hexutil.Uint64(math.MaxUint64 / 2) + } + args.Gas = &gas + } else { // Estimate the gas usage otherwise. + // These fields are immutable during the estimation, safe to + // pass the pointer directly. + data := args.data() + callArgs := TransactionArgs{ + From: args.From, + To: args.To, + GasPrice: args.GasPrice, + MaxFeePerGas: args.MaxFeePerGas, + MaxPriorityFeePerGas: args.MaxPriorityFeePerGas, + Value: args.Value, + Data: (*hexutil.Bytes)(&data), + AccessList: args.AccessList, + } + latestBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) + estimated, err := DoEstimateGas(ctx, b, callArgs, latestBlockNr, nil, b.RPCGasCap()) + if err != nil { + return err + } + args.Gas = &estimated + log.Trace("Estimate gas usage automatically", "gas", args.Gas) } - args.Gas = &estimated - log.Trace("Estimate gas usage automatically", "gas", args.Gas) } + // If chain id is provided, ensure it matches the local chain id. Otherwise, set the local // chain id as the default. want := b.ChainConfig().ChainId From 416e5ac00feb6dde155b644cc9c8b26ad4ae5607 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 14 Jun 2024 16:10:16 +0800 Subject: [PATCH 163/479] eth/tracers,internal/ethapi: use correct baseFee when BlockOverrides is provided in call/traceCall (#29051) --- eth/api_tracer.go | 2 ++ internal/ethapi/api.go | 14 ++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/eth/api_tracer.go b/eth/api_tracer.go index 2964a2b5d6f6..4be1c89fb60a 100644 --- a/eth/api_tracer.go +++ b/eth/api_tracer.go @@ -691,6 +691,8 @@ func (api *PrivateDebugAPI) TraceCall(ctx context.Context, args ethapi.Transacti } } // Execute the trace + // TODO: replace block.BaseFee() with vmctx.BaseFee + // reference: https://github.com/ethereum/go-ethereum/pull/29051 msg, err := args.ToMessage(api.eth.ApiBackend, block.Number(), api.eth.ApiBackend.RPCGasCap(), block.BaseFee()) if err != nil { return nil, err diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 504ce19bf27f..ac7ac6739b3e 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1336,12 +1336,6 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash return nil, 0, false, err, nil } - msg, err := args.ToMessage(b, header.Number, globalGasCap, header.BaseFee) - if err != nil { - return nil, 0, false, err, nil - } - msg.SetBalanceTokenFeeForCall() - // Setup context so it may be cancelled the call has completed // or, in case of unmetered gas, setup a context with a timeout. var cancel context.CancelFunc @@ -1370,6 +1364,14 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash return nil, 0, false, err, nil } + // TODO: replace header.BaseFee with blockCtx.BaseFee + // reference: https://github.com/ethereum/go-ethereum/pull/29051 + msg, err := args.ToMessage(b, header.Number, globalGasCap, header.BaseFee) + if err != nil { + return nil, 0, false, err, nil + } + msg.SetBalanceTokenFeeForCall() + // Get a new instance of the EVM. evm, vmError, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &vm.Config{NoBaseFee: true}) if err != nil { From 56ed523fc06e432cab3426b8155172652827b8eb Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 31 Oct 2024 17:04:10 +0800 Subject: [PATCH 164/479] common: set Eip1559Block to 23580000 for devnet --- common/constants/constants.go.devnet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/constants/constants.go.devnet b/common/constants/constants.go.devnet index d0a585cc9a76..a18316600246 100644 --- a/common/constants/constants.go.devnet +++ b/common/constants/constants.go.devnet @@ -51,7 +51,7 @@ var BerlinBlock = big.NewInt(16832700) var LondonBlock = big.NewInt(16832700) var MergeBlock = big.NewInt(16832700) var ShanghaiBlock = big.NewInt(16832700) -var Eip1559Block = big.NewInt(9999999999) +var Eip1559Block = big.NewInt(23580000) var TIPXDCXTestnet = big.NewInt(0) var IsTestnet bool = false From 338fb86bcde6fda598f4942460aa063ac6b34e43 Mon Sep 17 00:00:00 2001 From: benjamin202410 Date: Thu, 31 Oct 2024 22:43:07 -0700 Subject: [PATCH 165/479] downsize devnet rpc (#724) Co-authored-by: Name --- cicd/devnet/terraform/main.tf | 1 - cicd/devnet/terraform/module/region/main.tf | 1 - cicd/devnet/terraform/s3.tf | 1 - cicd/devnet/terraform/variables.tf | 13 ++------- cicd/terraform/main.tf | 10 +++---- cicd/terraform/variables.tf | 29 +-------------------- 6 files changed, 7 insertions(+), 48 deletions(-) diff --git a/cicd/devnet/terraform/main.tf b/cicd/devnet/terraform/main.tf index e7723398bf44..fc998b799ce7 100644 --- a/cicd/devnet/terraform/main.tf +++ b/cicd/devnet/terraform/main.tf @@ -2,7 +2,6 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "~> 5.13.1" } } } diff --git a/cicd/devnet/terraform/module/region/main.tf b/cicd/devnet/terraform/module/region/main.tf index 8e2ac0dd55d8..10fc5b08e603 100644 --- a/cicd/devnet/terraform/module/region/main.tf +++ b/cicd/devnet/terraform/module/region/main.tf @@ -2,7 +2,6 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "~> 5.13.1" } } } diff --git a/cicd/devnet/terraform/s3.tf b/cicd/devnet/terraform/s3.tf index f04eeeb7d890..cd30e5ce324a 100644 --- a/cicd/devnet/terraform/s3.tf +++ b/cicd/devnet/terraform/s3.tf @@ -1,4 +1,3 @@ -# Bucket need to be created first. If first time run terraform init, need to comment out the below section terraform { backend "s3" { bucket = "tf-xinfin-bucket" // This name need to be updated to be the same as local.s3BucketName. We can't use variable here. diff --git a/cicd/devnet/terraform/variables.tf b/cicd/devnet/terraform/variables.tf index 0d9b3bb125b0..12285d8ca76e 100644 --- a/cicd/devnet/terraform/variables.tf +++ b/cicd/devnet/terraform/variables.tf @@ -5,15 +5,6 @@ variable docker_tag { } locals { - /** - Load the nodes data from s3 - Below is the the format the config needs to follow: - {{Name of the node, in a pattern of 'xdc'+ number. i.e xdc50}}: { - pk: {{Value of the node private key}}, - ... any other configuration we want to pass. - } - Note: No `n` is allowed in the node name - **/ predefinedNodesConfig = jsondecode(data.aws_s3_object.devnet_xdc_node_config.body) envs = { for tuple in regexall("(.*)=(.*)", file(".env")) : tuple[0] => tuple[1] } logLevel = local.envs["log_level"] @@ -37,12 +28,12 @@ locals { ] keyNames = { - for r in local.regions : + for r in local.regions : r.name => [for i in range(r.start, r.end+1) : "xdc${i}"] } devnetNodeKeys = { - for r in local.regions : + for r in local.regions : r.name => { for i in local.keyNames[r.name]: i => local.predefinedNodesConfig[i] } } } diff --git a/cicd/terraform/main.tf b/cicd/terraform/main.tf index e34f8c5ffecb..7fc541fb7f1d 100644 --- a/cicd/terraform/main.tf +++ b/cicd/terraform/main.tf @@ -12,8 +12,6 @@ provider "aws" { region = "us-east-1" } -# WARNING: APSE-1 will only be used to host rpc node -# Workaround to avoid conflicts with existing ecs cluster in existing regions provider "aws" { alias = "ap-southeast-1" region = "ap-southeast-1" @@ -25,9 +23,9 @@ module "devnet_rpc" { vpc_id = local.vpc_id aws_subnet_id = local.aws_subnet_id ami_id = local.ami_id - instance_type = "t3.xlarge" + instance_type = "t3.large" ssh_key_name = local.ssh_key_name - rpc_image = local.rpc_image + rpc_image = local.rpc_image volume_size = 1500 providers = { @@ -43,7 +41,7 @@ module "testnet_rpc" { ami_id = local.ami_id instance_type = "t3.large" ssh_key_name = local.ssh_key_name - rpc_image = local.rpc_image + rpc_image = local.rpc_image volume_size = 1500 providers = { @@ -59,7 +57,7 @@ module "mainnet_rpc" { ami_id = local.ami_id instance_type = "t3.large" ssh_key_name = local.ssh_key_name - rpc_image = local.rpc_image + rpc_image = local.rpc_image volume_size = 3000 providers = { diff --git a/cicd/terraform/variables.tf b/cicd/terraform/variables.tf index c5a1eb8970d0..02bf5bdcf46c 100644 --- a/cicd/terraform/variables.tf +++ b/cicd/terraform/variables.tf @@ -1,41 +1,14 @@ locals { - /** - Load the nodes data from s3 - Below is the the format the config needs to follow: - {{Name of the node, in a pattern of 'xdc'+ number. i.e xdc50}}: { - pk: {{Value of the node private key}}, - ... any other configuration we want to pass. - } - Note: No `n` is allowed in the node name - **/ predefinedNodesConfig = jsondecode(data.aws_s3_object.xdc_node_config.body) envs = { for tuple in regexall("(.*)=(.*)", file(".env")) : tuple[0] => tuple[1] } logLevel = local.envs["log_level"] - # regions = [ - # { - # "name": "us-east-2", // Ohio - # "start": local.envs["us_east_2_start"], - # "end": local.envs["us_east_2_end"], - # } - # ] - - # keyNames = { - # for r in local.regions : - # r.name => [for i in range(r.start, r.end+1) : "xdc${i}"] - # } - - # nodeKeys = { - # for r in local.regions : - # r.name => { for i in local.keyNames[r.name]: i => local.predefinedNodesConfig[i] } - # } - rpcDevnetNodeKeys = { "devnet-rpc1": local.predefinedNodesConfig["devnet-rpc1"]} // we hardcode the rpc to a single node for now rpcTestnetNodeKeys = { "testnet-rpc1": local.predefinedNodesConfig["testnet-rpc1"]} // we hardcode the rpc to a single node for now rpcMainnetNodeKeys = { "mainnet-rpc1": local.predefinedNodesConfig["mainnet-rpc1"]} // we hardcode the rpc to a single node for now } -locals { //ec2_rpc values +locals { ami_id = "ami-097c4e1feeea169e5" rpc_image = "xinfinorg/xdposchain:v2.2.0-beta1" vpc_id = "vpc-20a06846" From 25bde78887c9ee23f1778004cfe8f1cbf190fa17 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 1 Nov 2024 15:45:40 +0800 Subject: [PATCH 166/479] remove accidental file test.txt --- test.txt | 801 ------------------------------------------------------- 1 file changed, 801 deletions(-) delete mode 100644 test.txt diff --git a/test.txt b/test.txt deleted file mode 100644 index f357fafea3ce..000000000000 --- a/test.txt +++ /dev/null @@ -1,801 +0,0 @@ -go run build/ci.go install ->>> /home/me/govm/golang/go-1.21.13/bin/go install -ldflags -X main.gitCommit=5be30c01a972daaea449fc1acb02180546f85557 -v ./... -# gopkg.in/olebedev/go-duktape.v3 -duk_logging.c: In function ‘duk__logger_prototype_log_shared’: -duk_logging.c:184:71: warning: ‘Z’ directive writing 1 byte into a region of size between 0 and 9 [-Wformat-overflow=] - 184 | sprintf((char *) date_buf, "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", - | ^ -In file included from /usr/include/stdio.h:894, - from duk_logging.c:5: -/usr/include/x86_64-linux-gnu/bits/stdio2.h:38:10: note: ‘__builtin___sprintf_chk’ output between 25 and 85 bytes into a destination of size 32 - 38 | return __builtin___sprintf_chk (__s, __USE_FORTIFY_LEVEL - 1, - | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 39 | __glibc_objsize (__s), __fmt, - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 40 | __va_arg_pack ()); - | ~~~~~~~~~~~~~~~~~ -github.com/XinFinOrg/XDPoSChain/cmd/abigen -github.com/XinFinOrg/XDPoSChain/cmd/rlpdump -github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/deploy -github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/fee/lending -github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/fee/trading -github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/price -github.com/XinFinOrg/XDPoSChain/cmd/p2psim -github.com/XinFinOrg/XDPoSChain/cmd/XDC -github.com/XinFinOrg/XDPoSChain/cmd/bootnode -github.com/XinFinOrg/XDPoSChain/cmd/ethkey -github.com/XinFinOrg/XDPoSChain/cmd/evm -github.com/XinFinOrg/XDPoSChain/cmd/faucet -github.com/XinFinOrg/XDPoSChain/cmd/gc -github.com/XinFinOrg/XDPoSChain/cmd/puppeth -github.com/XinFinOrg/XDPoSChain/cmd/wnode -github.com/XinFinOrg/XDPoSChain/contracts/XDCx/testnet/deploy -github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer/simulation/deploy -github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer/simulation/test -github.com/XinFinOrg/XDPoSChain/p2p/simulations/examples -github.com/XinFinOrg/XDPoSChain/rlp/rlpgen -go run build/ci.go test ->>> /home/me/govm/golang/go-1.21.13/bin/go test -ldflags -X main.gitCommit=5be30c01a972daaea449fc1acb02180546f85557 -p 1 github.com/XinFinOrg/XDPoSChain github.com/XinFinOrg/XDPoSChain/XDCx github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate github.com/XinFinOrg/XDPoSChain/XDCxDAO github.com/XinFinOrg/XDPoSChain/XDCxlending github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate github.com/XinFinOrg/XDPoSChain/accounts github.com/XinFinOrg/XDPoSChain/accounts/abi github.com/XinFinOrg/XDPoSChain/accounts/abi/bind github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends github.com/XinFinOrg/XDPoSChain/accounts/keystore github.com/XinFinOrg/XDPoSChain/accounts/usbwallet github.com/XinFinOrg/XDPoSChain/accounts/usbwallet/internal/trezor github.com/XinFinOrg/XDPoSChain/bmt github.com/XinFinOrg/XDPoSChain/cmd/XDC github.com/XinFinOrg/XDPoSChain/cmd/abigen github.com/XinFinOrg/XDPoSChain/cmd/bootnode github.com/XinFinOrg/XDPoSChain/cmd/ethkey github.com/XinFinOrg/XDPoSChain/cmd/evm github.com/XinFinOrg/XDPoSChain/cmd/evm/internal/compiler github.com/XinFinOrg/XDPoSChain/cmd/faucet github.com/XinFinOrg/XDPoSChain/cmd/gc github.com/XinFinOrg/XDPoSChain/cmd/internal/browser github.com/XinFinOrg/XDPoSChain/cmd/p2psim github.com/XinFinOrg/XDPoSChain/cmd/puppeth github.com/XinFinOrg/XDPoSChain/cmd/rlpdump github.com/XinFinOrg/XDPoSChain/cmd/utils github.com/XinFinOrg/XDPoSChain/cmd/wnode github.com/XinFinOrg/XDPoSChain/common github.com/XinFinOrg/XDPoSChain/common/bitutil github.com/XinFinOrg/XDPoSChain/common/compiler github.com/XinFinOrg/XDPoSChain/common/countdown github.com/XinFinOrg/XDPoSChain/common/fdlimit github.com/XinFinOrg/XDPoSChain/common/hexutil github.com/XinFinOrg/XDPoSChain/common/lru github.com/XinFinOrg/XDPoSChain/common/math github.com/XinFinOrg/XDPoSChain/common/mclock github.com/XinFinOrg/XDPoSChain/common/number github.com/XinFinOrg/XDPoSChain/common/prque github.com/XinFinOrg/XDPoSChain/common/sort github.com/XinFinOrg/XDPoSChain/compression/rle github.com/XinFinOrg/XDPoSChain/consensus github.com/XinFinOrg/XDPoSChain/consensus/XDPoS github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/engines/engine_v1 github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/engines/engine_v2 github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils github.com/XinFinOrg/XDPoSChain/consensus/clique github.com/XinFinOrg/XDPoSChain/consensus/ethash github.com/XinFinOrg/XDPoSChain/consensus/misc github.com/XinFinOrg/XDPoSChain/consensus/tests github.com/XinFinOrg/XDPoSChain/consensus/tests/engine_v1_tests github.com/XinFinOrg/XDPoSChain/consensus/tests/engine_v2_tests github.com/XinFinOrg/XDPoSChain/console github.com/XinFinOrg/XDPoSChain/contracts github.com/XinFinOrg/XDPoSChain/contracts/XDCx github.com/XinFinOrg/XDPoSChain/contracts/XDCx/contract github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/deploy github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/fee/lending github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/fee/trading github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/price github.com/XinFinOrg/XDPoSChain/contracts/XDCx/testnet github.com/XinFinOrg/XDPoSChain/contracts/XDCx/testnet/deploy github.com/XinFinOrg/XDPoSChain/contracts/blocksigner github.com/XinFinOrg/XDPoSChain/contracts/blocksigner/contract github.com/XinFinOrg/XDPoSChain/contracts/ens github.com/XinFinOrg/XDPoSChain/contracts/ens/contract github.com/XinFinOrg/XDPoSChain/contracts/multisigwallet github.com/XinFinOrg/XDPoSChain/contracts/multisigwallet/contract github.com/XinFinOrg/XDPoSChain/contracts/randomize github.com/XinFinOrg/XDPoSChain/contracts/randomize/contract github.com/XinFinOrg/XDPoSChain/contracts/tests github.com/XinFinOrg/XDPoSChain/contracts/tests/contract github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer/contract github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer/simulation github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer/simulation/deploy github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer/simulation/test github.com/XinFinOrg/XDPoSChain/contracts/validator github.com/XinFinOrg/XDPoSChain/contracts/validator/contract github.com/XinFinOrg/XDPoSChain/core github.com/XinFinOrg/XDPoSChain/core/asm github.com/XinFinOrg/XDPoSChain/core/bloombits github.com/XinFinOrg/XDPoSChain/core/rawdb github.com/XinFinOrg/XDPoSChain/core/state github.com/XinFinOrg/XDPoSChain/core/types github.com/XinFinOrg/XDPoSChain/core/vm github.com/XinFinOrg/XDPoSChain/core/vm/privacy github.com/XinFinOrg/XDPoSChain/core/vm/runtime github.com/XinFinOrg/XDPoSChain/crypto github.com/XinFinOrg/XDPoSChain/crypto/blake2b github.com/XinFinOrg/XDPoSChain/crypto/bn256 github.com/XinFinOrg/XDPoSChain/crypto/bn256/cloudflare github.com/XinFinOrg/XDPoSChain/crypto/bn256/google github.com/XinFinOrg/XDPoSChain/crypto/ecies github.com/XinFinOrg/XDPoSChain/crypto/randentropy github.com/XinFinOrg/XDPoSChain/crypto/secp256k1 github.com/XinFinOrg/XDPoSChain/crypto/sha3 github.com/XinFinOrg/XDPoSChain/eth github.com/XinFinOrg/XDPoSChain/eth/bft github.com/XinFinOrg/XDPoSChain/eth/downloader github.com/XinFinOrg/XDPoSChain/eth/ethconfig github.com/XinFinOrg/XDPoSChain/eth/fetcher github.com/XinFinOrg/XDPoSChain/eth/filters github.com/XinFinOrg/XDPoSChain/eth/gasprice github.com/XinFinOrg/XDPoSChain/eth/hooks github.com/XinFinOrg/XDPoSChain/eth/tracers github.com/XinFinOrg/XDPoSChain/eth/tracers/internal/tracers github.com/XinFinOrg/XDPoSChain/eth/tracers/native github.com/XinFinOrg/XDPoSChain/eth/tracers/testing github.com/XinFinOrg/XDPoSChain/eth/util github.com/XinFinOrg/XDPoSChain/ethclient github.com/XinFinOrg/XDPoSChain/ethdb github.com/XinFinOrg/XDPoSChain/ethdb/dbtest github.com/XinFinOrg/XDPoSChain/ethdb/leveldb github.com/XinFinOrg/XDPoSChain/ethdb/memorydb github.com/XinFinOrg/XDPoSChain/ethstats github.com/XinFinOrg/XDPoSChain/event github.com/XinFinOrg/XDPoSChain/event/filter github.com/XinFinOrg/XDPoSChain/internal/build github.com/XinFinOrg/XDPoSChain/internal/cmdtest github.com/XinFinOrg/XDPoSChain/internal/debug github.com/XinFinOrg/XDPoSChain/internal/ethapi github.com/XinFinOrg/XDPoSChain/internal/guide github.com/XinFinOrg/XDPoSChain/internal/jsre github.com/XinFinOrg/XDPoSChain/internal/jsre/deps github.com/XinFinOrg/XDPoSChain/internal/web3ext github.com/XinFinOrg/XDPoSChain/les github.com/XinFinOrg/XDPoSChain/les/flowcontrol github.com/XinFinOrg/XDPoSChain/light github.com/XinFinOrg/XDPoSChain/log github.com/XinFinOrg/XDPoSChain/log/term github.com/XinFinOrg/XDPoSChain/metrics github.com/XinFinOrg/XDPoSChain/metrics/exp github.com/XinFinOrg/XDPoSChain/metrics/influxdb github.com/XinFinOrg/XDPoSChain/metrics/librato github.com/XinFinOrg/XDPoSChain/miner github.com/XinFinOrg/XDPoSChain/mobile github.com/XinFinOrg/XDPoSChain/node github.com/XinFinOrg/XDPoSChain/p2p github.com/XinFinOrg/XDPoSChain/p2p/discover github.com/XinFinOrg/XDPoSChain/p2p/discv5 github.com/XinFinOrg/XDPoSChain/p2p/enr github.com/XinFinOrg/XDPoSChain/p2p/nat github.com/XinFinOrg/XDPoSChain/p2p/netutil github.com/XinFinOrg/XDPoSChain/p2p/protocols github.com/XinFinOrg/XDPoSChain/p2p/simulations github.com/XinFinOrg/XDPoSChain/p2p/simulations/adapters github.com/XinFinOrg/XDPoSChain/p2p/simulations/examples github.com/XinFinOrg/XDPoSChain/p2p/testing github.com/XinFinOrg/XDPoSChain/params github.com/XinFinOrg/XDPoSChain/rlp github.com/XinFinOrg/XDPoSChain/rlp/internal/rlpstruct github.com/XinFinOrg/XDPoSChain/rlp/rlpgen github.com/XinFinOrg/XDPoSChain/rpc github.com/XinFinOrg/XDPoSChain/tests github.com/XinFinOrg/XDPoSChain/tests/fuzzers/bitutil github.com/XinFinOrg/XDPoSChain/tests/fuzzers/bn256 github.com/XinFinOrg/XDPoSChain/tests/fuzzers/runtime github.com/XinFinOrg/XDPoSChain/trie github.com/XinFinOrg/XDPoSChain/whisper/mailserver github.com/XinFinOrg/XDPoSChain/whisper/shhclient github.com/XinFinOrg/XDPoSChain/whisper/whisperv5 github.com/XinFinOrg/XDPoSChain/whisper/whisperv6 -? github.com/XinFinOrg/XDPoSChain [no test files] -ok github.com/XinFinOrg/XDPoSChain/XDCx (cached) -ok github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate (cached) -? github.com/XinFinOrg/XDPoSChain/XDCxDAO [no test files] -ok github.com/XinFinOrg/XDPoSChain/XDCxlending (cached) -ok github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate (cached) -ok github.com/XinFinOrg/XDPoSChain/accounts (cached) -ok github.com/XinFinOrg/XDPoSChain/accounts/abi (cached) -ok github.com/XinFinOrg/XDPoSChain/accounts/abi/bind 2.409s -? github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends [no test files] -ok github.com/XinFinOrg/XDPoSChain/accounts/keystore 15.031s -? github.com/XinFinOrg/XDPoSChain/accounts/usbwallet [no test files] -? github.com/XinFinOrg/XDPoSChain/accounts/usbwallet/internal/trezor [no test files] -ok github.com/XinFinOrg/XDPoSChain/bmt (cached) -# gopkg.in/olebedev/go-duktape.v3 -duk_logging.c: In function ‘duk__logger_prototype_log_shared’: -duk_logging.c:184:71: warning: ‘Z’ directive writing 1 byte into a region of size between 0 and 9 [-Wformat-overflow=] - 184 | sprintf((char *) date_buf, "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", - | ^ -In file included from /usr/include/stdio.h:894, - from duk_logging.c:5: -/usr/include/x86_64-linux-gnu/bits/stdio2.h:38:10: note: ‘__builtin___sprintf_chk’ output between 25 and 85 bytes into a destination of size 32 - 38 | return __builtin___sprintf_chk (__s, __USE_FORTIFY_LEVEL - 1, - | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 39 | __glibc_objsize (__s), __fmt, - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 40 | __va_arg_pack ()); - | ~~~~~~~~~~~~~~~~~ -ok github.com/XinFinOrg/XDPoSChain/cmd/XDC (cached) -? github.com/XinFinOrg/XDPoSChain/cmd/abigen [no test files] -? github.com/XinFinOrg/XDPoSChain/cmd/bootnode [no test files] -ok github.com/XinFinOrg/XDPoSChain/cmd/ethkey (cached) -? github.com/XinFinOrg/XDPoSChain/cmd/evm [no test files] -? github.com/XinFinOrg/XDPoSChain/cmd/evm/internal/compiler [no test files] -? github.com/XinFinOrg/XDPoSChain/cmd/faucet [no test files] -? github.com/XinFinOrg/XDPoSChain/cmd/gc [no test files] -? github.com/XinFinOrg/XDPoSChain/cmd/internal/browser [no test files] -? github.com/XinFinOrg/XDPoSChain/cmd/p2psim [no test files] -? github.com/XinFinOrg/XDPoSChain/cmd/puppeth [no test files] -? github.com/XinFinOrg/XDPoSChain/cmd/rlpdump [no test files] -ok github.com/XinFinOrg/XDPoSChain/cmd/utils (cached) -? github.com/XinFinOrg/XDPoSChain/cmd/wnode [no test files] -ok github.com/XinFinOrg/XDPoSChain/common (cached) -ok github.com/XinFinOrg/XDPoSChain/common/bitutil (cached) -ok github.com/XinFinOrg/XDPoSChain/common/compiler (cached) -ok github.com/XinFinOrg/XDPoSChain/common/countdown (cached) -ok github.com/XinFinOrg/XDPoSChain/common/fdlimit (cached) -ok github.com/XinFinOrg/XDPoSChain/common/hexutil (cached) -ok github.com/XinFinOrg/XDPoSChain/common/lru (cached) -ok github.com/XinFinOrg/XDPoSChain/common/math (cached) -ok github.com/XinFinOrg/XDPoSChain/common/mclock (cached) -ok github.com/XinFinOrg/XDPoSChain/common/number (cached) -ok github.com/XinFinOrg/XDPoSChain/common/prque (cached) -? github.com/XinFinOrg/XDPoSChain/common/sort [no test files] -ok github.com/XinFinOrg/XDPoSChain/compression/rle (cached) -? github.com/XinFinOrg/XDPoSChain/consensus [no test files] -ok github.com/XinFinOrg/XDPoSChain/consensus/XDPoS (cached) -ok github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/engines/engine_v1 (cached) -ok github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/engines/engine_v2 (cached) -ok github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils (cached) -? github.com/XinFinOrg/XDPoSChain/consensus/clique [no test files] -ok github.com/XinFinOrg/XDPoSChain/consensus/ethash (cached) -? github.com/XinFinOrg/XDPoSChain/consensus/misc [no test files] -ok github.com/XinFinOrg/XDPoSChain/consensus/tests (cached) -ok github.com/XinFinOrg/XDPoSChain/consensus/tests/engine_v1_tests (cached) -ok github.com/XinFinOrg/XDPoSChain/consensus/tests/engine_v2_tests (cached) -ok github.com/XinFinOrg/XDPoSChain/console (cached) -ok github.com/XinFinOrg/XDPoSChain/contracts (cached) -? github.com/XinFinOrg/XDPoSChain/contracts/XDCx [no test files] -? github.com/XinFinOrg/XDPoSChain/contracts/XDCx/contract [no test files] -? github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation [no test files] -? github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/deploy [no test files] -? github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/fee/lending [no test files] -? github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/fee/trading [no test files] -? github.com/XinFinOrg/XDPoSChain/contracts/XDCx/simulation/price [no test files] -? github.com/XinFinOrg/XDPoSChain/contracts/XDCx/testnet [no test files] -? github.com/XinFinOrg/XDPoSChain/contracts/XDCx/testnet/deploy [no test files] -ok github.com/XinFinOrg/XDPoSChain/contracts/blocksigner (cached) -? github.com/XinFinOrg/XDPoSChain/contracts/blocksigner/contract [no test files] -ok github.com/XinFinOrg/XDPoSChain/contracts/ens (cached) -? github.com/XinFinOrg/XDPoSChain/contracts/ens/contract [no test files] -? github.com/XinFinOrg/XDPoSChain/contracts/multisigwallet [no test files] -? github.com/XinFinOrg/XDPoSChain/contracts/multisigwallet/contract [no test files] -ok github.com/XinFinOrg/XDPoSChain/contracts/randomize (cached) -? github.com/XinFinOrg/XDPoSChain/contracts/randomize/contract [no test files] -ok github.com/XinFinOrg/XDPoSChain/contracts/tests (cached) -? github.com/XinFinOrg/XDPoSChain/contracts/tests/contract [no test files] -ok github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer (cached) -? github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer/contract [no test files] -? github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer/simulation [no test files] -? github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer/simulation/deploy [no test files] -? github.com/XinFinOrg/XDPoSChain/contracts/trc21issuer/simulation/test [no test files] -ok github.com/XinFinOrg/XDPoSChain/contracts/validator (cached) -? github.com/XinFinOrg/XDPoSChain/contracts/validator/contract [no test files] -ok github.com/XinFinOrg/XDPoSChain/core (cached) -ok github.com/XinFinOrg/XDPoSChain/core/asm (cached) -ok github.com/XinFinOrg/XDPoSChain/core/bloombits (cached) -ok github.com/XinFinOrg/XDPoSChain/core/rawdb (cached) -ok github.com/XinFinOrg/XDPoSChain/core/state (cached) -ok github.com/XinFinOrg/XDPoSChain/core/types (cached) -ok github.com/XinFinOrg/XDPoSChain/core/vm (cached) -ok github.com/XinFinOrg/XDPoSChain/core/vm/privacy (cached) -ok github.com/XinFinOrg/XDPoSChain/core/vm/runtime (cached) -ok github.com/XinFinOrg/XDPoSChain/crypto (cached) -ok github.com/XinFinOrg/XDPoSChain/crypto/blake2b (cached) -? github.com/XinFinOrg/XDPoSChain/crypto/bn256 [no test files] -ok github.com/XinFinOrg/XDPoSChain/crypto/bn256/cloudflare (cached) -ok github.com/XinFinOrg/XDPoSChain/crypto/bn256/google (cached) -ok github.com/XinFinOrg/XDPoSChain/crypto/ecies (cached) -? github.com/XinFinOrg/XDPoSChain/crypto/randentropy [no test files] -ok github.com/XinFinOrg/XDPoSChain/crypto/secp256k1 (cached) -ok github.com/XinFinOrg/XDPoSChain/crypto/sha3 (cached) -ok github.com/XinFinOrg/XDPoSChain/eth (cached) -ok github.com/XinFinOrg/XDPoSChain/eth/bft (cached) -ok github.com/XinFinOrg/XDPoSChain/eth/downloader (cached) -? github.com/XinFinOrg/XDPoSChain/eth/ethconfig [no test files] -ok github.com/XinFinOrg/XDPoSChain/eth/fetcher (cached) -ok github.com/XinFinOrg/XDPoSChain/eth/filters (cached) -? github.com/XinFinOrg/XDPoSChain/eth/gasprice [no test files] -? github.com/XinFinOrg/XDPoSChain/eth/hooks [no test files] -ok github.com/XinFinOrg/XDPoSChain/eth/tracers (cached) -? github.com/XinFinOrg/XDPoSChain/eth/tracers/internal/tracers [no test files] -? github.com/XinFinOrg/XDPoSChain/eth/tracers/native [no test files] -ok github.com/XinFinOrg/XDPoSChain/eth/tracers/testing (cached) -? github.com/XinFinOrg/XDPoSChain/eth/util [no test files] -ok github.com/XinFinOrg/XDPoSChain/ethclient (cached) [no tests to run] -? github.com/XinFinOrg/XDPoSChain/ethdb [no test files] -? github.com/XinFinOrg/XDPoSChain/ethdb/dbtest [no test files] -ok github.com/XinFinOrg/XDPoSChain/ethdb/leveldb (cached) -ok github.com/XinFinOrg/XDPoSChain/ethdb/memorydb (cached) -? github.com/XinFinOrg/XDPoSChain/ethstats [no test files] -ok github.com/XinFinOrg/XDPoSChain/event (cached) -ok github.com/XinFinOrg/XDPoSChain/event/filter (cached) -? github.com/XinFinOrg/XDPoSChain/internal/build [no test files] -? github.com/XinFinOrg/XDPoSChain/internal/cmdtest [no test files] -? github.com/XinFinOrg/XDPoSChain/internal/debug [no test files] -ok github.com/XinFinOrg/XDPoSChain/internal/ethapi (cached) -ok github.com/XinFinOrg/XDPoSChain/internal/guide (cached) -ok github.com/XinFinOrg/XDPoSChain/internal/jsre (cached) -? github.com/XinFinOrg/XDPoSChain/internal/jsre/deps [no test files] -? github.com/XinFinOrg/XDPoSChain/internal/web3ext [no test files] -ok github.com/XinFinOrg/XDPoSChain/les (cached) -? github.com/XinFinOrg/XDPoSChain/les/flowcontrol [no test files] -ok github.com/XinFinOrg/XDPoSChain/light (cached) -? github.com/XinFinOrg/XDPoSChain/log [no test files] -? github.com/XinFinOrg/XDPoSChain/log/term [no test files] -ok github.com/XinFinOrg/XDPoSChain/metrics (cached) -? github.com/XinFinOrg/XDPoSChain/metrics/exp [no test files] -? github.com/XinFinOrg/XDPoSChain/metrics/influxdb [no test files] -? github.com/XinFinOrg/XDPoSChain/metrics/librato [no test files] -ok github.com/XinFinOrg/XDPoSChain/miner (cached) -ok github.com/XinFinOrg/XDPoSChain/mobile (cached) -ok github.com/XinFinOrg/XDPoSChain/node (cached) -ok github.com/XinFinOrg/XDPoSChain/p2p (cached) -ok github.com/XinFinOrg/XDPoSChain/p2p/discover (cached) -ok github.com/XinFinOrg/XDPoSChain/p2p/discv5 (cached) -ok github.com/XinFinOrg/XDPoSChain/p2p/enr (cached) -ok github.com/XinFinOrg/XDPoSChain/p2p/nat (cached) -ok github.com/XinFinOrg/XDPoSChain/p2p/netutil (cached) -ok github.com/XinFinOrg/XDPoSChain/p2p/protocols (cached) -ok github.com/XinFinOrg/XDPoSChain/p2p/simulations (cached) -? github.com/XinFinOrg/XDPoSChain/p2p/simulations/adapters [no test files] -? github.com/XinFinOrg/XDPoSChain/p2p/simulations/examples [no test files] -? github.com/XinFinOrg/XDPoSChain/p2p/testing [no test files] -ok github.com/XinFinOrg/XDPoSChain/params (cached) -ok github.com/XinFinOrg/XDPoSChain/rlp (cached) -? github.com/XinFinOrg/XDPoSChain/rlp/internal/rlpstruct [no test files] ---- FAIL: TestOutput (0.05s) - --- FAIL: TestOutput/nil (0.00s) - gen_test.go:78: output mismatch, want: package test - - import "github.com/XinFinOrg/XDPoSChain/rlp" - import "io" - - func (obj *Test) EncodeRLP(_w io.Writer) error { - w := rlp.NewEncoderBuffer(_w) - _tmp0 := w.List() - if obj.Uint8 == nil { - w.Write([]byte{0x80}) - } else { - w.WriteUint64(uint64((*obj.Uint8))) - } - if obj.Uint8List == nil { - w.Write([]byte{0xC0}) - } else { - w.WriteUint64(uint64((*obj.Uint8List))) - } - if obj.Uint32 == nil { - w.Write([]byte{0x80}) - } else { - w.WriteUint64(uint64((*obj.Uint32))) - } - if obj.Uint32List == nil { - w.Write([]byte{0xC0}) - } else { - w.WriteUint64(uint64((*obj.Uint32List))) - } - if obj.Uint64 == nil { - w.Write([]byte{0x80}) - } else { - w.WriteUint64((*obj.Uint64)) - } - if obj.Uint64List == nil { - w.Write([]byte{0xC0}) - } else { - w.WriteUint64((*obj.Uint64List)) - } - if obj.String == nil { - w.Write([]byte{0x80}) - } else { - w.WriteString((*obj.String)) - } - if obj.StringList == nil { - w.Write([]byte{0xC0}) - } else { - w.WriteString((*obj.StringList)) - } - if obj.ByteArray == nil { - w.Write([]byte{0x80}) - } else { - w.WriteBytes(obj.ByteArray[:]) - } - if obj.ByteArrayList == nil { - w.Write([]byte{0xC0}) - } else { - w.WriteBytes(obj.ByteArrayList[:]) - } - if obj.ByteSlice == nil { - w.Write([]byte{0x80}) - } else { - w.WriteBytes((*obj.ByteSlice)) - } - if obj.ByteSliceList == nil { - w.Write([]byte{0xC0}) - } else { - w.WriteBytes((*obj.ByteSliceList)) - } - if obj.Struct == nil { - w.Write([]byte{0xC0}) - } else { - _tmp1 := w.List() - w.WriteUint64(uint64(obj.Struct.A)) - w.ListEnd(_tmp1) - } - if obj.StructString == nil { - w.Write([]byte{0x80}) - } else { - _tmp2 := w.List() - w.WriteUint64(uint64(obj.StructString.A)) - w.ListEnd(_tmp2) - } - w.ListEnd(_tmp0) - return w.Flush() - } - - func (obj *Test) DecodeRLP(dec *rlp.Stream) error { - var _tmp0 Test - { - if _, err := dec.List(); err != nil { - return err - } - // Uint8: - var _tmp2 *byte - if _tmp3, _tmp4, err := dec.Kind(); err != nil { - return err - } else if _tmp4 != 0 || _tmp3 != rlp.String { - _tmp1, err := dec.Uint8() - if err != nil { - return err - } - _tmp2 = &_tmp1 - } - _tmp0.Uint8 = _tmp2 - // Uint8List: - var _tmp6 *byte - if _tmp7, _tmp8, err := dec.Kind(); err != nil { - return err - } else if _tmp8 != 0 || _tmp7 != rlp.List { - _tmp5, err := dec.Uint8() - if err != nil { - return err - } - _tmp6 = &_tmp5 - } - _tmp0.Uint8List = _tmp6 - // Uint32: - var _tmp10 *uint32 - if _tmp11, _tmp12, err := dec.Kind(); err != nil { - return err - } else if _tmp12 != 0 || _tmp11 != rlp.String { - _tmp9, err := dec.Uint32() - if err != nil { - return err - } - _tmp10 = &_tmp9 - } - _tmp0.Uint32 = _tmp10 - // Uint32List: - var _tmp14 *uint32 - if _tmp15, _tmp16, err := dec.Kind(); err != nil { - return err - } else if _tmp16 != 0 || _tmp15 != rlp.List { - _tmp13, err := dec.Uint32() - if err != nil { - return err - } - _tmp14 = &_tmp13 - } - _tmp0.Uint32List = _tmp14 - // Uint64: - var _tmp18 *uint64 - if _tmp19, _tmp20, err := dec.Kind(); err != nil { - return err - } else if _tmp20 != 0 || _tmp19 != rlp.String { - _tmp17, err := dec.Uint64() - if err != nil { - return err - } - _tmp18 = &_tmp17 - } - _tmp0.Uint64 = _tmp18 - // Uint64List: - var _tmp22 *uint64 - if _tmp23, _tmp24, err := dec.Kind(); err != nil { - return err - } else if _tmp24 != 0 || _tmp23 != rlp.List { - _tmp21, err := dec.Uint64() - if err != nil { - return err - } - _tmp22 = &_tmp21 - } - _tmp0.Uint64List = _tmp22 - // String: - var _tmp26 *string - if _tmp27, _tmp28, err := dec.Kind(); err != nil { - return err - } else if _tmp28 != 0 || _tmp27 != rlp.String { - _tmp25, err := dec.String() - if err != nil { - return err - } - _tmp26 = &_tmp25 - } - _tmp0.String = _tmp26 - // StringList: - var _tmp30 *string - if _tmp31, _tmp32, err := dec.Kind(); err != nil { - return err - } else if _tmp32 != 0 || _tmp31 != rlp.List { - _tmp29, err := dec.String() - if err != nil { - return err - } - _tmp30 = &_tmp29 - } - _tmp0.StringList = _tmp30 - // ByteArray: - var _tmp34 *[3]byte - if _tmp35, _tmp36, err := dec.Kind(); err != nil { - return err - } else if _tmp36 != 0 || _tmp35 != rlp.String { - var _tmp33 [3]byte - if err := dec.ReadBytes(_tmp33[:]); err != nil { - return err - } - _tmp34 = &_tmp33 - } - _tmp0.ByteArray = _tmp34 - // ByteArrayList: - var _tmp38 *[3]byte - if _tmp39, _tmp40, err := dec.Kind(); err != nil { - return err - } else if _tmp40 != 0 || _tmp39 != rlp.List { - var _tmp37 [3]byte - if err := dec.ReadBytes(_tmp37[:]); err != nil { - return err - } - _tmp38 = &_tmp37 - } - _tmp0.ByteArrayList = _tmp38 - // ByteSlice: - var _tmp42 *[]byte - if _tmp43, _tmp44, err := dec.Kind(); err != nil { - return err - } else if _tmp44 != 0 || _tmp43 != rlp.String { - _tmp41, err := dec.Bytes() - if err != nil { - return err - } - _tmp42 = &_tmp41 - } - _tmp0.ByteSlice = _tmp42 - // ByteSliceList: - var _tmp46 *[]byte - if _tmp47, _tmp48, err := dec.Kind(); err != nil { - return err - } else if _tmp48 != 0 || _tmp47 != rlp.List { - _tmp45, err := dec.Bytes() - if err != nil { - return err - } - _tmp46 = &_tmp45 - } - _tmp0.ByteSliceList = _tmp46 - // Struct: - var _tmp51 *Aux - if _tmp52, _tmp53, err := dec.Kind(); err != nil { - return err - } else if _tmp53 != 0 || _tmp52 != rlp.List { - var _tmp49 Aux - { - if _, err := dec.List(); err != nil { - return err - } - // A: - _tmp50, err := dec.Uint32() - if err != nil { - return err - } - _tmp49.A = _tmp50 - if err := dec.ListEnd(); err != nil { - return err - } - } - _tmp51 = &_tmp49 - } - _tmp0.Struct = _tmp51 - // StructString: - var _tmp56 *Aux - if _tmp57, _tmp58, err := dec.Kind(); err != nil { - return err - } else if _tmp58 != 0 || _tmp57 != rlp.String { - var _tmp54 Aux - { - if _, err := dec.List(); err != nil { - return err - } - // A: - _tmp55, err := dec.Uint32() - if err != nil { - return err - } - _tmp54.A = _tmp55 - if err := dec.ListEnd(); err != nil { - return err - } - } - _tmp56 = &_tmp54 - } - _tmp0.StructString = _tmp56 - if err := dec.ListEnd(); err != nil { - return err - } - } - *obj = _tmp0 - return nil - } - got package test - - import "github.com/XinFinOrg/XDPoSChain/rlp" - import "io" - - func (obj *Test) EncodeRLP(_w io.Writer) error { - w := rlp.NewEncoderBuffer(_w) - _tmp0 := w.List() - if obj.Uint8 == nil { - w.Write([]byte{0x80}) - } else { - w.WriteUint64(uint64((*obj.Uint8))) - } - if obj.Uint8List == nil { - w.Write([]byte{0xc0}) - } else { - w.WriteUint64(uint64((*obj.Uint8List))) - } - if obj.Uint32 == nil { - w.Write([]byte{0x80}) - } else { - w.WriteUint64(uint64((*obj.Uint32))) - } - if obj.Uint32List == nil { - w.Write([]byte{0xc0}) - } else { - w.WriteUint64(uint64((*obj.Uint32List))) - } - if obj.Uint64 == nil { - w.Write([]byte{0x80}) - } else { - w.WriteUint64((*obj.Uint64)) - } - if obj.Uint64List == nil { - w.Write([]byte{0xc0}) - } else { - w.WriteUint64((*obj.Uint64List)) - } - if obj.String == nil { - w.Write([]byte{0x80}) - } else { - w.WriteString((*obj.String)) - } - if obj.StringList == nil { - w.Write([]byte{0xc0}) - } else { - w.WriteString((*obj.StringList)) - } - if obj.ByteArray == nil { - w.Write([]byte{0x80}) - } else { - w.WriteBytes(obj.ByteArray[:]) - } - if obj.ByteArrayList == nil { - w.Write([]byte{0xc0}) - } else { - w.WriteBytes(obj.ByteArrayList[:]) - } - if obj.ByteSlice == nil { - w.Write([]byte{0x80}) - } else { - w.WriteBytes((*obj.ByteSlice)) - } - if obj.ByteSliceList == nil { - w.Write([]byte{0xc0}) - } else { - w.WriteBytes((*obj.ByteSliceList)) - } - if obj.Struct == nil { - w.Write([]byte{0xc0}) - } else { - _tmp1 := w.List() - w.WriteUint64(uint64(obj.Struct.A)) - w.ListEnd(_tmp1) - } - if obj.StructString == nil { - w.Write([]byte{0x80}) - } else { - _tmp2 := w.List() - w.WriteUint64(uint64(obj.StructString.A)) - w.ListEnd(_tmp2) - } - w.ListEnd(_tmp0) - return w.Flush() - } - - func (obj *Test) DecodeRLP(dec *rlp.Stream) error { - var _tmp0 Test - { - if _, err := dec.List(); err != nil { - return err - } - // Uint8: - var _tmp2 *byte - if _tmp3, _tmp4, err := dec.Kind(); err != nil { - return err - } else if _tmp4 != 0 || _tmp3 != rlp.String { - _tmp1, err := dec.Uint8() - if err != nil { - return err - } - _tmp2 = &_tmp1 - } - _tmp0.Uint8 = _tmp2 - // Uint8List: - var _tmp6 *byte - if _tmp7, _tmp8, err := dec.Kind(); err != nil { - return err - } else if _tmp8 != 0 || _tmp7 != rlp.List { - _tmp5, err := dec.Uint8() - if err != nil { - return err - } - _tmp6 = &_tmp5 - } - _tmp0.Uint8List = _tmp6 - // Uint32: - var _tmp10 *uint32 - if _tmp11, _tmp12, err := dec.Kind(); err != nil { - return err - } else if _tmp12 != 0 || _tmp11 != rlp.String { - _tmp9, err := dec.Uint32() - if err != nil { - return err - } - _tmp10 = &_tmp9 - } - _tmp0.Uint32 = _tmp10 - // Uint32List: - var _tmp14 *uint32 - if _tmp15, _tmp16, err := dec.Kind(); err != nil { - return err - } else if _tmp16 != 0 || _tmp15 != rlp.List { - _tmp13, err := dec.Uint32() - if err != nil { - return err - } - _tmp14 = &_tmp13 - } - _tmp0.Uint32List = _tmp14 - // Uint64: - var _tmp18 *uint64 - if _tmp19, _tmp20, err := dec.Kind(); err != nil { - return err - } else if _tmp20 != 0 || _tmp19 != rlp.String { - _tmp17, err := dec.Uint64() - if err != nil { - return err - } - _tmp18 = &_tmp17 - } - _tmp0.Uint64 = _tmp18 - // Uint64List: - var _tmp22 *uint64 - if _tmp23, _tmp24, err := dec.Kind(); err != nil { - return err - } else if _tmp24 != 0 || _tmp23 != rlp.List { - _tmp21, err := dec.Uint64() - if err != nil { - return err - } - _tmp22 = &_tmp21 - } - _tmp0.Uint64List = _tmp22 - // String: - var _tmp26 *string - if _tmp27, _tmp28, err := dec.Kind(); err != nil { - return err - } else if _tmp28 != 0 || _tmp27 != rlp.String { - _tmp25, err := dec.String() - if err != nil { - return err - } - _tmp26 = &_tmp25 - } - _tmp0.String = _tmp26 - // StringList: - var _tmp30 *string - if _tmp31, _tmp32, err := dec.Kind(); err != nil { - return err - } else if _tmp32 != 0 || _tmp31 != rlp.List { - _tmp29, err := dec.String() - if err != nil { - return err - } - _tmp30 = &_tmp29 - } - _tmp0.StringList = _tmp30 - // ByteArray: - var _tmp34 *[3]byte - if _tmp35, _tmp36, err := dec.Kind(); err != nil { - return err - } else if _tmp36 != 0 || _tmp35 != rlp.String { - var _tmp33 [3]byte - if err := dec.ReadBytes(_tmp33[:]); err != nil { - return err - } - _tmp34 = &_tmp33 - } - _tmp0.ByteArray = _tmp34 - // ByteArrayList: - var _tmp38 *[3]byte - if _tmp39, _tmp40, err := dec.Kind(); err != nil { - return err - } else if _tmp40 != 0 || _tmp39 != rlp.List { - var _tmp37 [3]byte - if err := dec.ReadBytes(_tmp37[:]); err != nil { - return err - } - _tmp38 = &_tmp37 - } - _tmp0.ByteArrayList = _tmp38 - // ByteSlice: - var _tmp42 *[]byte - if _tmp43, _tmp44, err := dec.Kind(); err != nil { - return err - } else if _tmp44 != 0 || _tmp43 != rlp.String { - _tmp41, err := dec.Bytes() - if err != nil { - return err - } - _tmp42 = &_tmp41 - } - _tmp0.ByteSlice = _tmp42 - // ByteSliceList: - var _tmp46 *[]byte - if _tmp47, _tmp48, err := dec.Kind(); err != nil { - return err - } else if _tmp48 != 0 || _tmp47 != rlp.List { - _tmp45, err := dec.Bytes() - if err != nil { - return err - } - _tmp46 = &_tmp45 - } - _tmp0.ByteSliceList = _tmp46 - // Struct: - var _tmp51 *Aux - if _tmp52, _tmp53, err := dec.Kind(); err != nil { - return err - } else if _tmp53 != 0 || _tmp52 != rlp.List { - var _tmp49 Aux - { - if _, err := dec.List(); err != nil { - return err - } - // A: - _tmp50, err := dec.Uint32() - if err != nil { - return err - } - _tmp49.A = _tmp50 - if err := dec.ListEnd(); err != nil { - return err - } - } - _tmp51 = &_tmp49 - } - _tmp0.Struct = _tmp51 - // StructString: - var _tmp56 *Aux - if _tmp57, _tmp58, err := dec.Kind(); err != nil { - return err - } else if _tmp58 != 0 || _tmp57 != rlp.String { - var _tmp54 Aux - { - if _, err := dec.List(); err != nil { - return err - } - // A: - _tmp55, err := dec.Uint32() - if err != nil { - return err - } - _tmp54.A = _tmp55 - if err := dec.ListEnd(); err != nil { - return err - } - } - _tmp56 = &_tmp54 - } - _tmp0.StructString = _tmp56 - if err := dec.ListEnd(); err != nil { - return err - } - } - *obj = _tmp0 - return nil - } -FAIL -FAIL github.com/XinFinOrg/XDPoSChain/rlp/rlpgen 0.537s -ok github.com/XinFinOrg/XDPoSChain/rpc (cached) -ok github.com/XinFinOrg/XDPoSChain/tests (cached) -? github.com/XinFinOrg/XDPoSChain/tests/fuzzers/bitutil [no test files] -? github.com/XinFinOrg/XDPoSChain/tests/fuzzers/bn256 [no test files] -? github.com/XinFinOrg/XDPoSChain/tests/fuzzers/runtime [no test files] -ok github.com/XinFinOrg/XDPoSChain/trie (cached) -ok github.com/XinFinOrg/XDPoSChain/whisper/mailserver (cached) -? github.com/XinFinOrg/XDPoSChain/whisper/shhclient [no test files] -ok github.com/XinFinOrg/XDPoSChain/whisper/whisperv5 (cached) -ok github.com/XinFinOrg/XDPoSChain/whisper/whisperv6 (cached) -FAIL -util.go:43: exit status 1 -exit status 1 -make: *** [Makefile:48: test] Error 1 From 6b653a22ad56fe70c3203a93c82589a9bef69eb7 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 4 Nov 2024 12:31:15 +0800 Subject: [PATCH 167/479] node: fix staticcheck warning SA1019: NewHTTPServer and NewWSServer are deprecated (#16154) --- node/node.go | 95 +++++++----------------------------------------- rpc/endpoints.go | 89 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 99 insertions(+), 85 deletions(-) diff --git a/node/node.go b/node/node.go index a9f767bf28f1..8feeabe125bc 100644 --- a/node/node.go +++ b/node/node.go @@ -324,47 +324,21 @@ func (n *Node) startIPC(apis []rpc.API) error { if n.ipcEndpoint == "" { return nil } - // Register all the APIs exposed by the services - handler := rpc.NewServer() - for _, api := range apis { - if err := handler.RegisterName(api.Namespace, api.Service); err != nil { - return err - } - n.log.Debug("IPC registered", "service", api.Service, "namespace", api.Namespace) - } - // All APIs registered, start the IPC listener - var ( - listener net.Listener - err error - ) - if listener, err = rpc.CreateIPCListener(n.ipcEndpoint); err != nil { + isClosed := func() bool { + n.lock.RLock() + defer n.lock.RUnlock() + return n.ipcListener == nil + } + + listener, handler, err := rpc.StartIPCEndpoint(isClosed, n.ipcEndpoint, apis) + if err != nil { return err } - go func() { - n.log.Info("IPC endpoint opened", "url", n.ipcEndpoint) - - for { - conn, err := listener.Accept() - if err != nil { - // Terminate if the listener was closed - n.lock.RLock() - closed := n.ipcListener == nil - n.lock.RUnlock() - if closed { - return - } - // Not closed, just some error; report and continue - n.log.Error("IPC accept failed", "err", err) - continue - } - log.Trace("Accepted RPC connection", "conn", conn.RemoteAddr()) - go handler.ServeCodec(rpc.NewCodec(conn), 0) - } - }() + // All listeners booted successfully n.ipcListener = listener n.ipcHandler = handler - + log.Info("IPC endpoint opened", "url", n.ipcEndpoint) return nil } @@ -388,30 +362,10 @@ func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors if endpoint == "" { return nil } - // Generate the whitelist based on the allowed modules - whitelist := make(map[string]bool) - for _, module := range modules { - whitelist[module] = true - } - // Register all the APIs exposed by the services - handler := rpc.NewServer() - for _, api := range apis { - if whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { - if err := handler.RegisterName(api.Namespace, api.Service); err != nil { - return err - } - n.log.Debug("HTTP registered", "service", api.Service, "namespace", api.Namespace) - } - } - // All APIs registered, start the HTTP listener - var ( - listener net.Listener - err error - ) - if listener, err = net.Listen("tcp", endpoint); err != nil { + listener, handler, err := rpc.StartHTTPEndpoint(endpoint, apis, modules, cors, vhosts, n.config.HTTPWriteTimeout) + if err != nil { return err } - go rpc.NewHTTPServer(cors, vhosts, handler, n.config.HTTPWriteTimeout).Serve(listener) n.log.Info("HTTP endpoint opened", "url", fmt.Sprintf("http://%s", endpoint), "cors", strings.Join(cors, ","), "vhosts", strings.Join(vhosts, ",")) // All listeners booted successfully n.httpEndpoint = endpoint @@ -441,32 +395,11 @@ func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrig if endpoint == "" { return nil } - // Generate the whitelist based on the allowed modules - whitelist := make(map[string]bool) - for _, module := range modules { - whitelist[module] = true - } - // Register all the APIs exposed by the services - handler := rpc.NewServer() - for _, api := range apis { - if exposeAll || whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { - if err := handler.RegisterName(api.Namespace, api.Service); err != nil { - return err - } - n.log.Debug("WebSocket registered", "service", api.Service, "namespace", api.Namespace) - } - } - // All APIs registered, start the HTTP listener - var ( - listener net.Listener - err error - ) - if listener, err = net.Listen("tcp", endpoint); err != nil { + listener, handler, err := rpc.StartWSEndpoint(endpoint, apis, modules, wsOrigins, exposeAll) + if err != nil { return err } - go rpc.NewWSServer(wsOrigins, handler).Serve(listener) n.log.Info("WebSocket endpoint opened", "url", fmt.Sprintf("ws://%s", listener.Addr())) - // All listeners booted successfully n.wsEndpoint = endpoint n.wsListener = listener diff --git a/rpc/endpoints.go b/rpc/endpoints.go index 1f5770a45d50..c3d828527170 100644 --- a/rpc/endpoints.go +++ b/rpc/endpoints.go @@ -19,12 +19,72 @@ package rpc import ( "net" "strings" + "time" "github.com/XinFinOrg/XDPoSChain/log" ) +// StartHTTPEndpoint starts the HTTP RPC endpoint, configured with cors/vhosts/modules +func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []string, vhosts []string, timeout time.Duration) (net.Listener, *Server, error) { + // Generate the whitelist based on the allowed modules + whitelist := make(map[string]bool) + for _, module := range modules { + whitelist[module] = true + } + // Register all the APIs exposed by the services + handler := NewServer() + for _, api := range apis { + if whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { + if err := handler.RegisterName(api.Namespace, api.Service); err != nil { + return nil, nil, err + } + log.Debug("HTTP registered", "namespace", api.Namespace) + } + } + // All APIs registered, start the HTTP listener + var ( + listener net.Listener + err error + ) + if listener, err = net.Listen("tcp", endpoint); err != nil { + return nil, nil, err + } + go NewHTTPServer(cors, vhosts, handler, timeout).Serve(listener) + return listener, handler, err +} + +// StartWSEndpoint starts a websocket endpoint +func StartWSEndpoint(endpoint string, apis []API, modules []string, wsOrigins []string, exposeAll bool) (net.Listener, *Server, error) { + // Generate the whitelist based on the allowed modules + whitelist := make(map[string]bool) + for _, module := range modules { + whitelist[module] = true + } + // Register all the APIs exposed by the services + handler := NewServer() + for _, api := range apis { + if exposeAll || whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { + if err := handler.RegisterName(api.Namespace, api.Service); err != nil { + return nil, nil, err + } + log.Debug("WebSocket registered", "service", api.Service, "namespace", api.Namespace) + } + } + // All APIs registered, start the HTTP listener + var ( + listener net.Listener + err error + ) + if listener, err = net.Listen("tcp", endpoint); err != nil { + return nil, nil, err + } + go NewWSServer(wsOrigins, handler).Serve(listener) + return listener, handler, err + +} + // StartIPCEndpoint starts an IPC endpoint. -func StartIPCEndpoint(ipcEndpoint string, apis []API) (net.Listener, *Server, error) { +func StartIPCEndpoint(isClosedFn func() bool, ipcEndpoint string, apis []API) (net.Listener, *Server, error) { // Register all the APIs exposed by the services. var ( handler = NewServer() @@ -36,6 +96,7 @@ func StartIPCEndpoint(ipcEndpoint string, apis []API) (net.Listener, *Server, er log.Info("IPC registration failed", "namespace", api.Namespace, "error", err) return nil, nil, err } + log.Debug("IPC registered", "service", api.Service, "namespace", api.Namespace) if _, ok := regMap[api.Namespace]; !ok { registered = append(registered, api.Namespace) regMap[api.Namespace] = struct{}{} @@ -43,10 +104,30 @@ func StartIPCEndpoint(ipcEndpoint string, apis []API) (net.Listener, *Server, er } log.Debug("IPCs registered", "namespaces", strings.Join(registered, ",")) // All APIs registered, start the IPC listener. - listener, err := ipcListen(ipcEndpoint) - if err != nil { + var ( + listener net.Listener + err error + ) + if listener, err = CreateIPCListener(ipcEndpoint); err != nil { return nil, nil, err } - go handler.ServeListener(listener) + go func() { + for { + conn, err := listener.Accept() + if err != nil { + // Terminate if the listener was closed + if isClosedFn() { + log.Info("IPC closed", "err", err) + return + } + // Not closed, just some error; report and continue + log.Error("IPC accept failed", "err", err) + continue + } + log.Trace("Accepted RPC connection", "conn", conn.RemoteAddr()) + go handler.ServeCodec(NewCodec(conn), 0) + } + }() + return listener, handler, nil } From 56bce3983dc6787a2b2b35f27929ccac87618353 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 4 Nov 2024 12:31:15 +0800 Subject: [PATCH 168/479] rpc: clean up IPC handler (#16524) --- node/node.go | 23 +++++++---------------- rpc/endpoints.go | 29 ++++------------------------- rpc/ipc.go | 12 +++--------- 3 files changed, 14 insertions(+), 50 deletions(-) diff --git a/node/node.go b/node/node.go index 8feeabe125bc..c1092f33f755 100644 --- a/node/node.go +++ b/node/node.go @@ -140,7 +140,7 @@ func (n *Node) Register(constructor ServiceConstructor) error { return nil } -// Start create a live P2P node and starts running it. +// Start creates a live P2P node and starts running it. func (n *Node) Start() error { n.lock.Lock() defer n.lock.Unlock() @@ -217,7 +217,7 @@ func (n *Node) Start() error { // Mark the service started for potential cleanup started = append(started, kind) } - // Lastly start the configured RPC interfaces + // Lastly, start the configured RPC interfaces if err := n.startRPC(services); err != nil { for _, service := range services { service.Stop() @@ -252,7 +252,7 @@ func (n *Node) openDataDir() error { return nil } -// startRPC is a helper method to start all the various RPC endpoint during node +// startRPC is a helper method to start all the various RPC endpoints during node // startup. It's not meant to be called at any time afterwards as it makes certain // assumptions about the state of the node. func (n *Node) startRPC(services map[reflect.Type]Service) error { @@ -293,7 +293,7 @@ func (n *Node) startInProc(apis []rpc.API) error { if err := handler.RegisterName(api.Namespace, api.Service); err != nil { return err } - n.log.Debug("InProc registered", "service", api.Service, "namespace", api.Namespace) + n.log.Debug("InProc registered", "namespace", api.Namespace) } n.inprocHandler = handler return nil @@ -320,22 +320,13 @@ func (n *Node) RegisterAPIs(apis []rpc.API) { // startIPC initializes and starts the IPC RPC endpoint. func (n *Node) startIPC(apis []rpc.API) error { - // Short circuit if the IPC endpoint isn't being exposed if n.ipcEndpoint == "" { - return nil - } - isClosed := func() bool { - n.lock.RLock() - defer n.lock.RUnlock() - return n.ipcListener == nil + return nil // IPC disabled. } - - listener, handler, err := rpc.StartIPCEndpoint(isClosed, n.ipcEndpoint, apis) + listener, handler, err := rpc.StartIPCEndpoint(n.ipcEndpoint, apis) if err != nil { return err } - - // All listeners booted successfully n.ipcListener = listener n.ipcHandler = handler log.Info("IPC endpoint opened", "url", n.ipcEndpoint) @@ -348,7 +339,7 @@ func (n *Node) stopIPC() { n.ipcListener.Close() n.ipcListener = nil - n.log.Info("IPC endpoint closed", "endpoint", n.ipcEndpoint) + n.log.Info("IPC endpoint closed", "url", n.ipcEndpoint) } if n.ipcHandler != nil { n.ipcHandler.Stop() diff --git a/rpc/endpoints.go b/rpc/endpoints.go index c3d828527170..e5bd7d9648d5 100644 --- a/rpc/endpoints.go +++ b/rpc/endpoints.go @@ -80,11 +80,10 @@ func StartWSEndpoint(endpoint string, apis []API, modules []string, wsOrigins [] } go NewWSServer(wsOrigins, handler).Serve(listener) return listener, handler, err - } // StartIPCEndpoint starts an IPC endpoint. -func StartIPCEndpoint(isClosedFn func() bool, ipcEndpoint string, apis []API) (net.Listener, *Server, error) { +func StartIPCEndpoint(ipcEndpoint string, apis []API) (net.Listener, *Server, error) { // Register all the APIs exposed by the services. var ( handler = NewServer() @@ -104,30 +103,10 @@ func StartIPCEndpoint(isClosedFn func() bool, ipcEndpoint string, apis []API) (n } log.Debug("IPCs registered", "namespaces", strings.Join(registered, ",")) // All APIs registered, start the IPC listener. - var ( - listener net.Listener - err error - ) - if listener, err = CreateIPCListener(ipcEndpoint); err != nil { + listener, err := ipcListen(ipcEndpoint) + if err != nil { return nil, nil, err } - go func() { - for { - conn, err := listener.Accept() - if err != nil { - // Terminate if the listener was closed - if isClosedFn() { - log.Info("IPC closed", "err", err) - return - } - // Not closed, just some error; report and continue - log.Error("IPC accept failed", "err", err) - continue - } - log.Trace("Accepted RPC connection", "conn", conn.RemoteAddr()) - go handler.ServeCodec(NewCodec(conn), 0) - } - }() - + go handler.ServeListener(listener) return listener, handler, nil } diff --git a/rpc/ipc.go b/rpc/ipc.go index 77e06b491950..e617e37ca7c1 100644 --- a/rpc/ipc.go +++ b/rpc/ipc.go @@ -24,23 +24,17 @@ import ( "github.com/XinFinOrg/XDPoSChain/p2p/netutil" ) -// CreateIPCListener creates an listener, on Unix platforms this is a unix socket, on -// Windows this is a named pipe -func CreateIPCListener(endpoint string) (net.Listener, error) { - return ipcListen(endpoint) -} - -// ServeListener accepts connections on l, serving JSON-RPC on them. +// ServeListener accepts connections on l, serving IPC-RPC on them. func (s *Server) ServeListener(l net.Listener) error { for { conn, err := l.Accept() if netutil.IsTemporaryError(err) { - log.Warn("RPC accept error", "err", err) + log.Warn("IPC accept error", "err", err) continue } else if err != nil { return err } - log.Trace("Accepted RPC connection", "conn", conn.RemoteAddr()) + log.Trace("IPC accepted connection") go s.ServeCodec(NewCodec(conn), 0) } } From 97a5ff616b70e88beae411d4e561763d480d65bf Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 4 Nov 2024 12:31:15 +0800 Subject: [PATCH 169/479] rpc: Make HTTP server timeout values configurable (#17240) --- cmd/XDC/main.go | 2 ++ cmd/XDC/usage.go | 2 ++ cmd/utils/flags.go | 22 +++++++++++++++++++--- node/api.go | 2 +- node/config.go | 9 +++++---- node/defaults.go | 13 ++++++------- node/node.go | 6 +++--- rpc/endpoints.go | 5 ++--- rpc/http.go | 30 ++++++++++++++++++++++++------ 9 files changed, 64 insertions(+), 27 deletions(-) diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go index 864e5e1cd087..dcaad38cd9c2 100644 --- a/cmd/XDC/main.go +++ b/cmd/XDC/main.go @@ -147,7 +147,9 @@ var ( utils.RPCGlobalGasCapFlag, utils.RPCListenAddrFlag, utils.RPCPortFlag, + utils.RPCHttpReadTimeoutFlag, utils.RPCHttpWriteTimeoutFlag, + utils.RPCHttpIdleTimeoutFlag, utils.RPCApiFlag, utils.WSEnabledFlag, utils.WSListenAddrFlag, diff --git a/cmd/XDC/usage.go b/cmd/XDC/usage.go index b724155162a5..39a7c4367320 100644 --- a/cmd/XDC/usage.go +++ b/cmd/XDC/usage.go @@ -147,7 +147,9 @@ var AppHelpFlagGroups = []flagGroup{ utils.RPCGlobalGasCapFlag, utils.RPCListenAddrFlag, utils.RPCPortFlag, + utils.RPCHttpReadTimeoutFlag, utils.RPCHttpWriteTimeoutFlag, + utils.RPCHttpIdleTimeoutFlag, utils.RPCApiFlag, utils.WSEnabledFlag, utils.WSListenAddrFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 5d661fa0e77c..8041c2f337af 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -435,10 +435,20 @@ var ( Usage: "HTTP-RPC server listening port", Value: node.DefaultHTTPPort, } + RPCHttpReadTimeoutFlag = cli.DurationFlag{ + Name: "rpcreadtimeout", + Usage: "HTTP-RPC server read timeout", + Value: rpc.DefaultHTTPTimeouts.ReadTimeout, + } RPCHttpWriteTimeoutFlag = cli.DurationFlag{ Name: "rpcwritetimeout", - Usage: "HTTP-RPC server write timeout (default = 10s)", - Value: node.DefaultHTTPWriteTimeOut, + Usage: "HTTP-RPC server write timeout", + Value: rpc.DefaultHTTPTimeouts.WriteTimeout, + } + RPCHttpIdleTimeoutFlag = cli.DurationFlag{ + Name: "rpcidletimeout", + Usage: "HTTP-RPC server idle timeout", + Value: rpc.DefaultHTTPTimeouts.IdleTimeout, } RPCCORSDomainFlag = cli.StringFlag{ Name: "rpccorsdomain", @@ -779,8 +789,14 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) { if ctx.GlobalIsSet(RPCPortFlag.Name) { cfg.HTTPPort = ctx.GlobalInt(RPCPortFlag.Name) } + if ctx.GlobalIsSet(RPCHttpReadTimeoutFlag.Name) { + cfg.HTTPTimeouts.ReadTimeout = ctx.GlobalDuration(RPCHttpReadTimeoutFlag.Name) + } if ctx.GlobalIsSet(RPCHttpWriteTimeoutFlag.Name) { - cfg.HTTPWriteTimeout = ctx.GlobalDuration(RPCHttpWriteTimeoutFlag.Name) + cfg.HTTPTimeouts.WriteTimeout = ctx.GlobalDuration(RPCHttpWriteTimeoutFlag.Name) + } + if ctx.GlobalIsSet(RPCHttpIdleTimeoutFlag.Name) { + cfg.HTTPTimeouts.IdleTimeout = ctx.GlobalDuration(RPCHttpIdleTimeoutFlag.Name) } if ctx.GlobalIsSet(RPCCORSDomainFlag.Name) { cfg.HTTPCors = splitAndTrim(ctx.GlobalString(RPCCORSDomainFlag.Name)) diff --git a/node/api.go b/node/api.go index 8a3611524418..4db65d762fd5 100644 --- a/node/api.go +++ b/node/api.go @@ -158,7 +158,7 @@ func (api *PrivateAdminAPI) StartRPC(host *string, port *int, cors *string, apis } } - if err := api.node.startHTTP(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, allowedOrigins, allowedVHosts); err != nil { + if err := api.node.startHTTP(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, allowedOrigins, allowedVHosts, api.node.config.HTTPTimeouts); err != nil { return false, err } return true, nil diff --git a/node/config.go b/node/config.go index 848d563e9fa2..1984120f9ff3 100644 --- a/node/config.go +++ b/node/config.go @@ -23,7 +23,6 @@ import ( "path/filepath" "runtime" "strings" - "time" "github.com/XinFinOrg/XDPoSChain/accounts" "github.com/XinFinOrg/XDPoSChain/accounts/keystore" @@ -33,6 +32,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/p2p" "github.com/XinFinOrg/XDPoSChain/p2p/discover" + "github.com/XinFinOrg/XDPoSChain/rpc" ) const ( @@ -100,9 +100,6 @@ type Config struct { // for ephemeral nodes). HTTPPort int `toml:",omitempty"` - // HTTPWriteTimeout is the write timeout for the HTTP RPC server. - HTTPWriteTimeout time.Duration `toml:",omitempty"` - // HTTPCors is the Cross-Origin Resource Sharing header to send to requesting // clients. Please be aware that CORS is a browser enforced security, it's fully // useless for custom HTTP clients. @@ -122,6 +119,10 @@ type Config struct { // exposed. HTTPModules []string `toml:",omitempty"` + // HTTPTimeouts allows for customization of the timeout values used by the HTTP RPC + // interface. + HTTPTimeouts rpc.HTTPTimeouts + // WSHost is the host interface on which to start the websocket RPC server. If // this field is empty, no websocket API endpoint will be started. WSHost string `toml:",omitempty"` diff --git a/node/defaults.go b/node/defaults.go index f9f4f8f2688e..ff9348929554 100644 --- a/node/defaults.go +++ b/node/defaults.go @@ -21,27 +21,26 @@ import ( "os/user" "path/filepath" "runtime" - "time" "github.com/XinFinOrg/XDPoSChain/p2p" "github.com/XinFinOrg/XDPoSChain/p2p/nat" + "github.com/XinFinOrg/XDPoSChain/rpc" ) const ( - DefaultHTTPHost = "localhost" // Default host interface for the HTTP RPC server - DefaultHTTPPort = 8545 // Default TCP port for the HTTP RPC server - DefaultHTTPWriteTimeOut = 10 * time.Second // Default write timeout for the HTTP RPC server - DefaultWSHost = "localhost" // Default host interface for the websocket RPC server - DefaultWSPort = 8546 // Default TCP port for the websocket RPC server + DefaultHTTPHost = "localhost" // Default host interface for the HTTP RPC server + DefaultHTTPPort = 8545 // Default TCP port for the HTTP RPC server + DefaultWSHost = "localhost" // Default host interface for the websocket RPC server + DefaultWSPort = 8546 // Default TCP port for the websocket RPC server ) // DefaultConfig contains reasonable default settings. var DefaultConfig = Config{ DataDir: DefaultDataDir(), HTTPPort: DefaultHTTPPort, - HTTPWriteTimeout: DefaultHTTPWriteTimeOut, HTTPModules: []string{"net", "web3"}, HTTPVirtualHosts: []string{"localhost"}, + HTTPTimeouts: rpc.DefaultHTTPTimeouts, WSPort: DefaultWSPort, WSModules: []string{"net", "web3"}, P2P: p2p.Config{ diff --git a/node/node.go b/node/node.go index c1092f33f755..fb1d9b4a995d 100644 --- a/node/node.go +++ b/node/node.go @@ -269,7 +269,7 @@ func (n *Node) startRPC(services map[reflect.Type]Service) error { n.stopInProc() return err } - if err := n.startHTTP(n.httpEndpoint, apis, n.config.HTTPModules, n.config.HTTPCors, n.config.HTTPVirtualHosts); err != nil { + if err := n.startHTTP(n.httpEndpoint, apis, n.config.HTTPModules, n.config.HTTPCors, n.config.HTTPVirtualHosts, n.config.HTTPTimeouts); err != nil { n.stopIPC() n.stopInProc() return err @@ -348,12 +348,12 @@ func (n *Node) stopIPC() { } // startHTTP initializes and starts the HTTP RPC endpoint. -func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string) error { +func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string, timeouts rpc.HTTPTimeouts) error { // Short circuit if the HTTP endpoint isn't being exposed if endpoint == "" { return nil } - listener, handler, err := rpc.StartHTTPEndpoint(endpoint, apis, modules, cors, vhosts, n.config.HTTPWriteTimeout) + listener, handler, err := rpc.StartHTTPEndpoint(endpoint, apis, modules, cors, vhosts, timeouts) if err != nil { return err } diff --git a/rpc/endpoints.go b/rpc/endpoints.go index e5bd7d9648d5..21802c9542db 100644 --- a/rpc/endpoints.go +++ b/rpc/endpoints.go @@ -19,13 +19,12 @@ package rpc import ( "net" "strings" - "time" "github.com/XinFinOrg/XDPoSChain/log" ) // StartHTTPEndpoint starts the HTTP RPC endpoint, configured with cors/vhosts/modules -func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []string, vhosts []string, timeout time.Duration) (net.Listener, *Server, error) { +func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []string, vhosts []string, timeouts HTTPTimeouts) (net.Listener, *Server, error) { // Generate the whitelist based on the allowed modules whitelist := make(map[string]bool) for _, module := range modules { @@ -49,7 +48,7 @@ func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []str if listener, err = net.Listen("tcp", endpoint); err != nil { return nil, nil, err } - go NewHTTPServer(cors, vhosts, handler, timeout).Serve(listener) + go NewHTTPServer(cors, vhosts, timeouts, handler).Serve(listener) return listener, handler, err } diff --git a/rpc/http.go b/rpc/http.go index 6f267aa9d66e..2416d66753b4 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -240,17 +240,35 @@ func (t *httpServerConn) SetWriteDeadline(time.Time) error { return nil } // NewHTTPServer creates a new HTTP RPC server around an API provider. // // Deprecated: Server implements http.Handler -func NewHTTPServer(cors []string, vhosts []string, srv *Server, writeTimeout time.Duration) *http.Server { +func NewHTTPServer(cors []string, vhosts []string, timeouts HTTPTimeouts, srv *Server) *http.Server { // Wrap the CORS-handler within a host-handler handler := newCorsHandler(srv, cors) handler = newVHostHandler(vhosts, handler) - handler = http.TimeoutHandler(handler, writeTimeout, `{"error":"http server timeout"}`) - log.Info("NewHTTPServer", "writeTimeout", writeTimeout) + + // Make sure timeout values are meaningful + if timeouts.ReadTimeout < time.Second { + log.Warn("Sanitizing invalid HTTP read timeout", "provided", timeouts.ReadTimeout, "updated", DefaultHTTPTimeouts.ReadTimeout) + timeouts.ReadTimeout = DefaultHTTPTimeouts.ReadTimeout + } + if timeouts.WriteTimeout < time.Second { + log.Warn("Sanitizing invalid HTTP write timeout", "provided", timeouts.WriteTimeout, "updated", DefaultHTTPTimeouts.WriteTimeout) + timeouts.WriteTimeout = DefaultHTTPTimeouts.WriteTimeout + } + if timeouts.IdleTimeout < time.Second { + log.Warn("Sanitizing invalid HTTP idle timeout", "provided", timeouts.IdleTimeout, "updated", DefaultHTTPTimeouts.IdleTimeout) + timeouts.IdleTimeout = DefaultHTTPTimeouts.IdleTimeout + } + + // PR #469: return http code 503 and error message to client when timeout + handler = http.TimeoutHandler(handler, timeouts.WriteTimeout, `{"error":"http server timeout"}`) + log.Info("NewHTTPServer", "writeTimeout", timeouts.WriteTimeout) + + // Bundle and start the HTTP server return &http.Server{ Handler: handler, - ReadTimeout: 5 * time.Second, - WriteTimeout: writeTimeout + time.Second, - IdleTimeout: 120 * time.Second, + ReadTimeout: timeouts.ReadTimeout, + WriteTimeout: timeouts.WriteTimeout + time.Second, + IdleTimeout: timeouts.IdleTimeout, } } From 4cb240981ebaf10ba3213e604ebc10b5fcf1dc16 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 4 Nov 2024 12:31:16 +0800 Subject: [PATCH 170/479] node: fix a deadlock (#17891) --- node/node.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/node/node.go b/node/node.go index fb1d9b4a995d..9673bb49e07e 100644 --- a/node/node.go +++ b/node/node.go @@ -569,11 +569,23 @@ func (n *Node) IPCEndpoint() string { // HTTPEndpoint retrieves the current HTTP endpoint used by the protocol stack. func (n *Node) HTTPEndpoint() string { + n.lock.Lock() + defer n.lock.Unlock() + + if n.httpListener != nil { + return n.httpListener.Addr().String() + } return n.httpEndpoint } // WSEndpoint retrieves the current WS endpoint used by the protocol stack. func (n *Node) WSEndpoint() string { + n.lock.Lock() + defer n.lock.Unlock() + + if n.wsListener != nil { + return n.wsListener.Addr().String() + } return n.wsEndpoint } From ef8fa666d3d79e30a5984ebfd39f0f440df84e44 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 4 Nov 2024 12:31:16 +0800 Subject: [PATCH 171/479] node: prefer nil slices over zero-length slices (#19083) --- node/node.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/node.go b/node/node.go index 9673bb49e07e..709b1a3cfb72 100644 --- a/node/node.go +++ b/node/node.go @@ -203,7 +203,7 @@ func (n *Node) Start() error { return convertFileLockError(err) } // Start each of the services - started := []reflect.Type{} + var started []reflect.Type for kind, service := range services { // Start the next service, stopping all previous upon failure if err := service.Start(running); err != nil { From cec7dcb02acb6c3ba58a6372eaa61fde0c5bf56c Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 4 Nov 2024 12:31:16 +0800 Subject: [PATCH 172/479] node: report actual port used for http rpc (#20789) --- node/node.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/node/node.go b/node/node.go index 709b1a3cfb72..c5adec4451bd 100644 --- a/node/node.go +++ b/node/node.go @@ -357,7 +357,9 @@ func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors if err != nil { return err } - n.log.Info("HTTP endpoint opened", "url", fmt.Sprintf("http://%s", endpoint), "cors", strings.Join(cors, ","), "vhosts", strings.Join(vhosts, ",")) + n.log.Info("HTTP endpoint opened", "url", fmt.Sprintf("http://%v/", listener.Addr()), + "cors", strings.Join(cors, ","), + "vhosts", strings.Join(vhosts, ",")) // All listeners booted successfully n.httpEndpoint = endpoint n.httpListener = listener @@ -369,10 +371,10 @@ func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors // stopHTTP terminates the HTTP RPC endpoint. func (n *Node) stopHTTP() { if n.httpListener != nil { + url := fmt.Sprintf("http://%v/", n.httpListener.Addr()) n.httpListener.Close() n.httpListener = nil - - n.log.Info("HTTP endpoint closed", "url", fmt.Sprintf("http://%s", n.httpEndpoint)) + n.log.Info("HTTP endpoint closed", "url", url) } if n.httpHandler != nil { n.httpHandler.Stop() From 216ee418f18e9ac3c82703d15dfb067e415fb60f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 4 Nov 2024 15:46:22 +0800 Subject: [PATCH 173/479] cmd/utils, node: increase default maxpeers to 50 (#19497) --- cmd/utils/flags.go | 4 ++-- node/defaults.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 8041c2f337af..dbeadc9c180d 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -510,12 +510,12 @@ var ( MaxPeersFlag = cli.IntFlag{ Name: "maxpeers", Usage: "Maximum number of network peers (network disabled if set to 0)", - Value: 25, + Value: node.DefaultConfig.P2P.MaxPeers, } MaxPendingPeersFlag = cli.IntFlag{ Name: "maxpendpeers", Usage: "Maximum number of pending connection attempts (defaults used if set to 0)", - Value: 0, + Value: node.DefaultConfig.P2P.MaxPendingPeers, } ListenPortFlag = cli.IntFlag{ Name: "port", diff --git a/node/defaults.go b/node/defaults.go index ff9348929554..3f36a50d8f72 100644 --- a/node/defaults.go +++ b/node/defaults.go @@ -45,7 +45,7 @@ var DefaultConfig = Config{ WSModules: []string{"net", "web3"}, P2P: p2p.Config{ ListenAddr: ":30303", - MaxPeers: 25, + MaxPeers: 50, NAT: nat.Any(), }, } From c254b084436e8838016e7a1b97225f72c0f1cb66 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 7 Nov 2024 16:33:15 +0800 Subject: [PATCH 174/479] core/txpool: fix very high pending special transactions --- core/txpool/txpool.go | 2 +- core/types/transaction.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 7b755747fac4..fc57d9355aa9 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -570,7 +570,7 @@ func (pool *TxPool) Pending(enforceTips bool) map[common.Address]types.Transacti // If the miner requests tip enforcement, cap the lists now if enforceTips && !pool.locals.contains(addr) { for i, tx := range txs { - if tx.EffectiveGasTipIntCmp(pool.gasPrice, pool.priced.urgent.baseFee) < 0 { + if !tx.IsSpecialTransaction() && tx.EffectiveGasTipIntCmp(pool.gasPrice, pool.priced.urgent.baseFee) < 0 { txs = txs[:i] break } diff --git a/core/types/transaction.go b/core/types/transaction.go index 95f5e3b8acf3..f0afdca06092 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -481,7 +481,7 @@ func (tx *Transaction) TxCost(number *big.Int) *big.Int { func (tx *Transaction) IsSpecialTransaction() bool { to := tx.To() - return to != nil && (*to == common.RandomizeSMCBinary || *to == common.BlockSignersBinary) + return to != nil && (*to == common.BlockSignersBinary || *to == common.RandomizeSMCBinary) } func (tx *Transaction) IsTradingTransaction() bool { From 179185438fba184db13d6ed3e4a37292ec91fab5 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 13 Nov 2024 09:30:54 +0800 Subject: [PATCH 175/479] eth/gasprice: sanitize max header and block history (#23886) --- eth/gasprice/gasprice.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index b59a95d296c6..8581cfa8a4c7 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -87,8 +87,7 @@ func NewOracle(backend OracleBackend, params Config) *Oracle { if percent < 0 { percent = 0 log.Warn("Sanitizing invalid gasprice oracle sample percentile", "provided", params.Percentile, "updated", percent) - } - if percent > 100 { + } else if percent > 100 { percent = 100 log.Warn("Sanitizing invalid gasprice oracle sample percentile", "provided", params.Percentile, "updated", percent) } @@ -104,6 +103,16 @@ func NewOracle(backend OracleBackend, params Config) *Oracle { } else if ignorePrice.Int64() > 0 { log.Info("Gasprice oracle is ignoring threshold set", "threshold", ignorePrice) } + maxHeaderHistory := params.MaxHeaderHistory + if maxHeaderHistory < 1 { + maxHeaderHistory = 1 + log.Warn("Sanitizing invalid gasprice oracle max header history", "provided", params.MaxHeaderHistory, "updated", maxHeaderHistory) + } + maxBlockHistory := params.MaxBlockHistory + if maxBlockHistory < 1 { + maxBlockHistory = 1 + log.Warn("Sanitizing invalid gasprice oracle max block history", "provided", params.MaxBlockHistory, "updated", maxBlockHistory) + } cache, _ := lru.New(2048) headEvent := make(chan core.ChainHeadEvent, 1) @@ -125,8 +134,8 @@ func NewOracle(backend OracleBackend, params Config) *Oracle { ignorePrice: ignorePrice, checkBlocks: blocks, percentile: percent, - maxHeaderHistory: params.MaxHeaderHistory, - maxBlockHistory: params.MaxBlockHistory, + maxHeaderHistory: maxHeaderHistory, + maxBlockHistory: maxBlockHistory, historyCache: cache, } } From f9e431888e1139de8ab2bb3f855add4c60815bf5 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 13 Nov 2024 09:30:54 +0800 Subject: [PATCH 176/479] eth/gasprice: add generic LRU implementation (#26162) --- eth/gasprice/feehistory.go | 14 ++++++++------ eth/gasprice/gasprice.go | 7 ++++--- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index c0570876c28c..4b50966785c5 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -56,7 +56,12 @@ type blockFees struct { err error } -// processedFees contains the results of a processed block and is also used for caching +type cacheKey struct { + number uint64 + percentiles string +} + +// processedFees contains the results of a processed block. type processedFees struct { reward []*big.Int baseFee, nextBaseFee *big.Int @@ -268,13 +273,10 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL oracle.processBlock(fees, rewardPercentiles) results <- fees } else { - cacheKey := struct { - number uint64 - percentiles string - }{blockNumber, string(percentileKey)} + cacheKey := cacheKey{number: blockNumber, percentiles: string(percentileKey)} if p, ok := oracle.historyCache.Get(cacheKey); ok { - fees.results = p.(processedFees) + fees.results = p results <- fees } else { if len(rewardPercentiles) != 0 { diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index 8581cfa8a4c7..a091cc2be6d2 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -29,7 +29,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rpc" - lru "github.com/hashicorp/golang-lru" + "github.com/XinFinOrg/XDPoSChain/common/lru" ) const sampleNumber = 3 // Number of transactions sampled in a block @@ -72,7 +72,8 @@ type Oracle struct { checkBlocks, percentile int maxHeaderHistory, maxBlockHistory uint64 - historyCache *lru.Cache + + historyCache *lru.Cache[cacheKey, processedFees] } // NewOracle returns a new gasprice oracle which can recommend suitable @@ -114,7 +115,7 @@ func NewOracle(backend OracleBackend, params Config) *Oracle { log.Warn("Sanitizing invalid gasprice oracle max block history", "provided", params.MaxBlockHistory, "updated", maxBlockHistory) } - cache, _ := lru.New(2048) + cache := lru.NewCache[cacheKey, processedFees](2048) headEvent := make(chan core.ChainHeadEvent, 1) backend.SubscribeChainHeadEvent(headEvent) go func() { From 4dbed3582fde252b19d996ce77bb9228b35d3ff2 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 13 Nov 2024 09:30:55 +0800 Subject: [PATCH 177/479] eth/gasprice: simplify function getBlockValues --- eth/gasprice/gasprice.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index a091cc2be6d2..3043a65ca0d8 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -23,13 +23,13 @@ import ( "sync" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/lru" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/event" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rpc" - "github.com/XinFinOrg/XDPoSChain/common/lru" ) const sampleNumber = 3 // Number of transactions sampled in a block @@ -176,7 +176,7 @@ func (oracle *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) { results []*big.Int ) for sent < oracle.checkBlocks && number > 0 { - go oracle.getBlockValues(ctx, types.MakeSigner(oracle.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, oracle.ignorePrice, result, quit) + go oracle.getBlockValues(ctx, number, sampleNumber, oracle.ignorePrice, result, quit) sent++ exp++ number-- @@ -199,7 +199,7 @@ func (oracle *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) { // meaningful returned, try to query more blocks. But the maximum // is 2*checkBlocks. if len(res.values) == 1 && len(results)+1+exp < oracle.checkBlocks*2 && number > 0 { - go oracle.getBlockValues(ctx, types.MakeSigner(oracle.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, oracle.ignorePrice, result, quit) + go oracle.getBlockValues(ctx, number, sampleNumber, oracle.ignorePrice, result, quit) sent++ exp++ number-- @@ -260,11 +260,11 @@ func (s *txSorter) Less(i, j int) bool { return tip1.Cmp(tip2) < 0 } -// getBlockPrices calculates the lowest transaction gas price in a given block +// getBlockValues calculates the lowest transaction gas price in a given block // and sends it to the result channel. If the block is empty or all transactions // are sent by the miner itself(it doesn't make any sense to include this kind of // transaction prices for sampling), nil gasprice is returned. -func (oracle *Oracle) getBlockValues(ctx context.Context, signer types.Signer, blockNum uint64, limit int, ignoreUnder *big.Int, result chan results, quit chan struct{}) { +func (oracle *Oracle) getBlockValues(ctx context.Context, blockNum uint64, limit int, ignoreUnder *big.Int, result chan results, quit chan struct{}) { block, err := oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum)) if block == nil { select { @@ -273,6 +273,8 @@ func (oracle *Oracle) getBlockValues(ctx context.Context, signer types.Signer, b } return } + signer := types.MakeSigner(oracle.backend.ChainConfig(), block.Number()) + // Sort the transaction by effective tip in ascending sort. txs := make([]*types.Transaction, len(block.Transactions())) copy(txs, block.Transactions()) From d3eaeb93814a860247c0a09eff90704cad8287f4 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 13 Nov 2024 09:30:55 +0800 Subject: [PATCH 178/479] eth/gasprice: use slices package for sorting (#27490 #27909 #29314) --- common/types.go | 29 ++++++++++++++++++--- eth/gasprice/feehistory.go | 25 ++++++------------ eth/gasprice/gasprice.go | 53 +++++++++++--------------------------- 3 files changed, 49 insertions(+), 58 deletions(-) diff --git a/common/types.go b/common/types.go index 2f621a4a5f40..162d2c4083ef 100644 --- a/common/types.go +++ b/common/types.go @@ -17,6 +17,7 @@ package common import ( + "bytes" "encoding/hex" "fmt" "math/big" @@ -77,23 +78,44 @@ type Vote struct { Voter Address } +// BytesToHash sets b to hash. +// If b is larger than len(h), b will be cropped from the left. func BytesToHash(b []byte) Hash { var h Hash h.SetBytes(b) return h } + func StringToHash(s string) Hash { return BytesToHash([]byte(s)) } -func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) } + +// BigToHash sets byte representation of b to hash. +// If b is larger than len(h), b will be cropped from the left. +func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) } + func Uint64ToHash(b uint64) Hash { return BytesToHash(new(big.Int).SetUint64(b).Bytes()) } -func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) } + +// HexToHash sets byte representation of s to hash. +// If b is larger than len(h), b will be cropped from the left. +func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) } + +// Cmp compares two hashes. +func (h Hash) Cmp(other Hash) int { + return bytes.Compare(h[:], other[:]) +} // IsZero returns if a Hash is empty func (h Hash) IsZero() bool { return h == Hash{} } // Get the string representation of the underlying hash func (h Hash) Str() string { return string(h[:]) } + +// Bytes gets the byte representation of the underlying hash. func (h Hash) Bytes() []byte { return h[:] } + +// Big converts a hash to a big integer. func (h Hash) Big() *big.Int { return new(big.Int).SetBytes(h[:]) } + +// Hex converts a hash to a hex string. func (h Hash) Hex() string { return hexutil.Encode(h[:]) } // TerminalString implements log.TerminalStringer, formatting a string for console @@ -129,7 +151,8 @@ func (h Hash) MarshalText() ([]byte, error) { return hexutil.Bytes(h[:]).MarshalText() } -// Sets the hash to the value of b. If b is larger than len(h), 'b' will be cropped (from the left). +// SetBytes sets the hash to the value of b. +// If b is larger than len(h), b will be cropped from the left. func (h *Hash) SetBytes(b []byte) { if len(b) > len(h) { b = b[len(b)-HashLength:] diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index 4b50966785c5..c3cffa490222 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -23,7 +23,7 @@ import ( "fmt" "math" "math/big" - "sort" + "slices" "sync/atomic" "github.com/XinFinOrg/XDPoSChain/common" @@ -69,20 +69,9 @@ type processedFees struct { } // txGasAndReward is sorted in ascending order based on reward -type ( - txGasAndReward struct { - gasUsed uint64 - reward *big.Int - } - sortGasAndReward []txGasAndReward -) - -func (s sortGasAndReward) Len() int { return len(s) } -func (s sortGasAndReward) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} -func (s sortGasAndReward) Less(i, j int) bool { - return s[i].reward.Cmp(s[j].reward) < 0 +type txGasAndReward struct { + gasUsed uint64 + reward *big.Int } // processBlock takes a blockFees structure with the blockNumber, the header and optionally @@ -117,12 +106,14 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { return } - sorter := make(sortGasAndReward, len(bf.block.Transactions())) + sorter := make([]txGasAndReward, len(bf.block.Transactions())) for i, tx := range bf.block.Transactions() { reward, _ := tx.EffectiveGasTip(bf.block.BaseFee()) sorter[i] = txGasAndReward{gasUsed: bf.receipts[i].GasUsed, reward: reward} } - sort.Stable(sorter) + slices.SortStableFunc(sorter, func(a, b txGasAndReward) int { + return a.reward.Cmp(b.reward) + }) var txIndex int sumGasUsed := sorter[0].gasUsed diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index 3043a65ca0d8..b185f3828aec 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -19,7 +19,7 @@ package gasprice import ( "context" "math/big" - "sort" + "slices" "sync" "github.com/XinFinOrg/XDPoSChain/common" @@ -208,7 +208,7 @@ func (oracle *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) { } price := lastPrice if len(results) > 0 { - sort.Sort(bigIntArray(results)) + slices.SortFunc(results, func(a, b *big.Int) int { return a.Cmp(b) }) price = results[(len(results)-1)*oracle.percentile/100] } if price.Cmp(oracle.maxPrice) > 0 { @@ -236,30 +236,6 @@ type results struct { err error } -type txSorter struct { - txs []*types.Transaction - baseFee *big.Int -} - -func newSorter(txs []*types.Transaction, baseFee *big.Int) *txSorter { - return &txSorter{ - txs: txs, - baseFee: baseFee, - } -} - -func (s *txSorter) Len() int { return len(s.txs) } -func (s *txSorter) Swap(i, j int) { - s.txs[i], s.txs[j] = s.txs[j], s.txs[i] -} -func (s *txSorter) Less(i, j int) bool { - // It's okay to discard the error because a tx would never be - // accepted into a block with an invalid effective tip. - tip1, _ := s.txs[i].EffectiveGasTip(s.baseFee) - tip2, _ := s.txs[j].EffectiveGasTip(s.baseFee) - return tip1.Cmp(tip2) < 0 -} - // getBlockValues calculates the lowest transaction gas price in a given block // and sends it to the result channel. If the block is empty or all transactions // are sent by the miner itself(it doesn't make any sense to include this kind of @@ -276,14 +252,21 @@ func (oracle *Oracle) getBlockValues(ctx context.Context, blockNum uint64, limit signer := types.MakeSigner(oracle.backend.ChainConfig(), block.Number()) // Sort the transaction by effective tip in ascending sort. - txs := make([]*types.Transaction, len(block.Transactions())) - copy(txs, block.Transactions()) - sorter := newSorter(txs, block.BaseFee()) - sort.Sort(sorter) + txs := block.Transactions() + sortedTxs := make([]*types.Transaction, len(txs)) + copy(sortedTxs, txs) + baseFee := block.BaseFee() + slices.SortFunc(sortedTxs, func(a, b *types.Transaction) int { + // It's okay to discard the error because a tx would never be + // accepted into a block with an invalid effective tip. + tip1, _ := a.EffectiveGasTip(baseFee) + tip2, _ := b.EffectiveGasTip(baseFee) + return tip1.Cmp(tip2) + }) var prices []*big.Int - for _, tx := range sorter.txs { - tip, _ := tx.EffectiveGasTip(block.BaseFee()) + for _, tx := range sortedTxs { + tip, _ := tx.EffectiveGasTip(baseFee) if ignoreUnder != nil && tip.Cmp(ignoreUnder) == -1 { continue } @@ -300,9 +283,3 @@ func (oracle *Oracle) getBlockValues(ctx context.Context, blockNum uint64, limit case <-quit: } } - -type bigIntArray []*big.Int - -func (s bigIntArray) Len() int { return len(s) } -func (s bigIntArray) Less(i, j int) bool { return s[i].Cmp(s[j]) < 0 } -func (s bigIntArray) Swap(i, j int) { s[i], s[j] = s[j], s[i] } From de7203ac886099926b2e28219c3359fb5d48cd19 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 13 Nov 2024 09:30:55 +0800 Subject: [PATCH 179/479] eth/gasprice: remove default from config (#30080) --- eth/backend.go | 6 +----- eth/gasprice/feehistory_test.go | 2 +- eth/gasprice/gasprice.go | 8 +++++--- eth/gasprice/gasprice_test.go | 3 +-- les/backend.go | 6 +----- 5 files changed, 9 insertions(+), 16 deletions(-) diff --git a/eth/backend.go b/eth/backend.go index 1842bb47779d..b9cef04b11c8 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -225,11 +225,7 @@ func New(ctx *node.ServiceContext, config *ethconfig.Config, XDCXServ *XDCx.XDCX } else { eth.ApiBackend = &EthApiBackend{eth, nil, nil} } - gpoParams := config.GPO - if gpoParams.Default == nil { - gpoParams.Default = config.GasPrice - } - eth.ApiBackend.gpo = gasprice.NewOracle(eth.ApiBackend, gpoParams) + eth.ApiBackend.gpo = gasprice.NewOracle(eth.ApiBackend, config.GPO, config.GasPrice) // Set global ipc endpoint. eth.blockchain.IPCEndpoint = ctx.GetConfig().IPCEndpoint() diff --git a/eth/gasprice/feehistory_test.go b/eth/gasprice/feehistory_test.go index 4c372f0bcc14..f3344348c2be 100644 --- a/eth/gasprice/feehistory_test.go +++ b/eth/gasprice/feehistory_test.go @@ -58,7 +58,7 @@ func TestFeeHistory(t *testing.T) { MaxBlockHistory: c.maxBlock, } backend := newTestBackend(t, big.NewInt(16), c.pending) - oracle := NewOracle(backend, config) + oracle := NewOracle(backend, config, nil) first, reward, baseFee, ratio, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent) diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index b185f3828aec..3395965da65f 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -44,7 +44,6 @@ type Config struct { Percentile int MaxHeaderHistory uint64 MaxBlockHistory uint64 - Default *big.Int `toml:",omitempty"` MaxPrice *big.Int `toml:",omitempty"` IgnorePrice *big.Int `toml:",omitempty"` } @@ -78,7 +77,7 @@ type Oracle struct { // NewOracle returns a new gasprice oracle which can recommend suitable // gasprice for newly created transaction. -func NewOracle(backend OracleBackend, params Config) *Oracle { +func NewOracle(backend OracleBackend, params Config, startPrice *big.Int) *Oracle { blocks := params.Blocks if blocks < 1 { blocks = 1 @@ -114,6 +113,9 @@ func NewOracle(backend OracleBackend, params Config) *Oracle { maxBlockHistory = 1 log.Warn("Sanitizing invalid gasprice oracle max block history", "provided", params.MaxBlockHistory, "updated", maxBlockHistory) } + if startPrice == nil { + startPrice = new(big.Int) + } cache := lru.NewCache[cacheKey, processedFees](2048) headEvent := make(chan core.ChainHeadEvent, 1) @@ -130,7 +132,7 @@ func NewOracle(backend OracleBackend, params Config) *Oracle { return &Oracle{ backend: backend, - lastPrice: params.Default, + lastPrice: startPrice, maxPrice: maxPrice, ignorePrice: ignorePrice, checkBlocks: blocks, diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index ded61a289905..abaca1190136 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -174,7 +174,6 @@ func TestSuggestTipCap(t *testing.T) { config := Config{ Blocks: 3, Percentile: 60, - Default: big.NewInt(params.GWei), } var cases = []struct { fork *big.Int // Eip1559 fork number @@ -188,7 +187,7 @@ func TestSuggestTipCap(t *testing.T) { } for _, c := range cases { backend := newTestBackend(t, c.fork, false) - oracle := NewOracle(backend, config) + oracle := NewOracle(backend, config, big.NewInt(params.GWei)) // The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G got, err := oracle.SuggestTipCap(context.Background()) diff --git a/les/backend.go b/les/backend.go index ac496a7ecf6d..08933f93a0a4 100644 --- a/les/backend.go +++ b/les/backend.go @@ -131,11 +131,7 @@ func New(ctx *node.ServiceContext, config *ethconfig.Config) (*LightEthereum, er return nil, err } leth.ApiBackend = &LesApiBackend{leth, nil} - gpoParams := config.GPO - if gpoParams.Default == nil { - gpoParams.Default = config.GasPrice - } - leth.ApiBackend.gpo = gasprice.NewOracle(leth.ApiBackend, gpoParams) + leth.ApiBackend.gpo = gasprice.NewOracle(leth.ApiBackend, config.GPO, config.GasPrice) return leth, nil } From 59b06e4a0328b8a2ca5839b1a2646725e447be4b Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 13 Nov 2024 09:30:55 +0800 Subject: [PATCH 180/479] eth/gasprice: reduce default gas price for empty block --- eth/gasprice/gasprice.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index 3395965da65f..900455fa718b 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -193,9 +193,9 @@ func (oracle *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) { // Nothing returned. There are two special cases here: // - The block is empty // - All the transactions included are sent by the miner itself. - // In these cases, use the latest calculated price for samping. + // In these cases, use half of the latest calculated price for samping. if len(res.values) == 0 { - res.values = []*big.Int{lastPrice} + res.values = []*big.Int{new(big.Int).Div(lastPrice, common.Big2)} } // Besides, in order to collect enough data for sampling, if nothing // meaningful returned, try to query more blocks. But the maximum From f9f172af760c671e455ce8eb7e1a7be505e81ff0 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 8 Nov 2024 18:54:39 +0800 Subject: [PATCH 181/479] rpc: Add admin_addTrustedPeer and admin_removeTrustedPeer. (#16333) --- internal/web3ext/web3ext.go | 10 ++++ node/api.go | 33 ++++++++++- p2p/peer.go | 2 +- p2p/server.go | 62 ++++++++++++++++++-- p2p/server_test.go | 110 +++++++++++++++++++++++++++++++++++- 5 files changed, 208 insertions(+), 9 deletions(-) diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 5c8f88aa7cdf..8aaa3e9d263f 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -197,6 +197,16 @@ web3._extend({ call: 'admin_removePeer', params: 1 }), + new web3._extend.Method({ + name: 'addTrustedPeer', + call: 'admin_addTrustedPeer', + params: 1 + }), + new web3._extend.Method({ + name: 'removeTrustedPeer', + call: 'admin_removeTrustedPeer', + params: 1 + }), new web3._extend.Method({ name: 'exportChain', call: 'admin_exportChain', diff --git a/node/api.go b/node/api.go index 4db65d762fd5..a014931efc6a 100644 --- a/node/api.go +++ b/node/api.go @@ -60,7 +60,7 @@ func (api *PrivateAdminAPI) AddPeer(url string) (bool, error) { return true, nil } -// RemovePeer disconnects from a a remote node if the connection exists +// RemovePeer disconnects from a remote node if the connection exists func (api *PrivateAdminAPI) RemovePeer(url string) (bool, error) { // Make sure the server is running, fail otherwise server := api.node.Server() @@ -76,6 +76,37 @@ func (api *PrivateAdminAPI) RemovePeer(url string) (bool, error) { return true, nil } +// AddTrustedPeer allows a remote node to always connect, even if slots are full +func (api *PrivateAdminAPI) AddTrustedPeer(url string) (bool, error) { + // Make sure the server is running, fail otherwise + server := api.node.Server() + if server == nil { + return false, ErrNodeStopped + } + node, err := discover.ParseNode(url) + if err != nil { + return false, fmt.Errorf("invalid enode: %v", err) + } + server.AddTrustedPeer(node) + return true, nil +} + +// RemoveTrustedPeer removes a remote node from the trusted peer set, but it +// does not disconnect it automatically. +func (api *PrivateAdminAPI) RemoveTrustedPeer(url string) (bool, error) { + // Make sure the server is running, fail otherwise + server := api.node.Server() + if server == nil { + return false, ErrNodeStopped + } + node, err := discover.ParseNode(url) + if err != nil { + return false, fmt.Errorf("invalid enode: %v", err) + } + server.RemoveTrustedPeer(node) + return true, nil +} + // PeerEvents creates an RPC subscription which receives peer events from the // node's p2p.Server func (api *PrivateAdminAPI) PeerEvents(ctx context.Context) (*rpc.Subscription, error) { diff --git a/p2p/peer.go b/p2p/peer.go index e54d70a3b2c8..ac6c464e7e69 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -165,7 +165,7 @@ func (p *Peer) String() string { // Inbound returns true if the peer is an inbound connection func (p *Peer) Inbound() bool { - return p.rw.flags&inboundConn != 0 + return p.rw.is(inboundConn) } func newPeer(conn *conn, protocols []Protocol) *Peer { diff --git a/p2p/server.go b/p2p/server.go index d43451b000dd..fabbb9cdbfcd 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -22,6 +22,7 @@ import ( "errors" "net" "sync" + "sync/atomic" "time" "github.com/XinFinOrg/XDPoSChain/common" @@ -168,6 +169,8 @@ type Server struct { quit chan struct{} addstatic chan *discover.Node removestatic chan *discover.Node + addtrusted chan *discover.Node + removetrusted chan *discover.Node posthandshake chan *conn addpeer chan *conn delpeer chan peerDrop @@ -184,7 +187,7 @@ type peerDrop struct { requested bool // true if signaled by the peer } -type connFlag int +type connFlag int32 const ( dynDialedConn connFlag = 1 << iota @@ -249,7 +252,18 @@ func (f connFlag) String() string { } func (c *conn) is(f connFlag) bool { - return c.flags&f != 0 + flags := connFlag(atomic.LoadInt32((*int32)(&c.flags))) + return flags&f != 0 +} + +func (c *conn) set(f connFlag, val bool) { + flags := connFlag(atomic.LoadInt32((*int32)(&c.flags))) + if val { + flags |= f + } else { + flags &= ^f + } + atomic.StoreInt32((*int32)(&c.flags), int32(flags)) } // Peers returns all connected peers. @@ -300,6 +314,23 @@ func (srv *Server) RemovePeer(node *discover.Node) { } } +// AddTrustedPeer adds the given node to a reserved whitelist which allows the +// node to always connect, even if the slot are full. +func (srv *Server) AddTrustedPeer(node *discover.Node) { + select { + case srv.addtrusted <- node: + case <-srv.quit: + } +} + +// RemoveTrustedPeer removes the given node from the trusted peer set. +func (srv *Server) RemoveTrustedPeer(node *discover.Node) { + select { + case srv.removetrusted <- node: + case <-srv.quit: + } +} + // SubscribePeers subscribes the given channel to peer events func (srv *Server) SubscribeEvents(ch chan *PeerEvent) event.Subscription { return srv.peerFeed.Subscribe(ch) @@ -410,6 +441,8 @@ func (srv *Server) Start() (err error) { srv.posthandshake = make(chan *conn) srv.addstatic = make(chan *discover.Node) srv.removestatic = make(chan *discover.Node) + srv.addtrusted = make(chan *discover.Node) + srv.removetrusted = make(chan *discover.Node) srv.peerOp = make(chan peerOpFunc) srv.peerOpDone = make(chan struct{}) @@ -546,8 +579,7 @@ func (srv *Server) run(dialstate dialer) { queuedTasks []task // tasks that can't run yet ) // Put trusted nodes into a map to speed up checks. - // Trusted peers are loaded on startup and cannot be - // modified while the server is running. + // Trusted peers are loaded on startup or added via AddTrustedPeer RPC. for _, n := range srv.TrustedNodes { trusted[n.ID] = true } @@ -599,12 +631,32 @@ running: case n := <-srv.removestatic: // This channel is used by RemovePeer to send a // disconnect request to a peer and begin the - // stop keeping the node connected + // stop keeping the node connected. srv.log.Debug("Removing static node", "node", n) dialstate.removeStatic(n) if p, ok := peers[n.ID]; ok { p.Disconnect(DiscRequested) } + case n := <-srv.addtrusted: + // This channel is used by AddTrustedPeer to add an enode + // to the trusted node set. + srv.log.Trace("Adding trusted node", "node", n) + trusted[n.ID] = true + // Mark any already-connected peer as trusted + if p, ok := peers[n.ID]; ok { + p.rw.set(trustedConn, true) + } + case n := <-srv.removetrusted: + // This channel is used by RemoveTrustedPeer to remove an enode + // from the trusted node set. + srv.log.Trace("Removing trusted node", "node", n) + if _, ok := trusted[n.ID]; ok { + delete(trusted, n.ID) + } + // Unmark any already-connected peer as trusted + if p, ok := peers[n.ID]; ok { + p.rw.set(trustedConn, false) + } case op := <-srv.peerOp: // This channel is used by Peers and PeerCount. op(peers) diff --git a/p2p/server_test.go b/p2p/server_test.go index ceda0f8d6b54..a22a47147b51 100644 --- a/p2p/server_test.go +++ b/p2p/server_test.go @@ -148,7 +148,8 @@ func TestServerDial(t *testing.T) { // tell the server to connect tcpAddr := listener.Addr().(*net.TCPAddr) - srv.AddPeer(&discover.Node{ID: remid, IP: tcpAddr.IP, TCP: uint16(tcpAddr.Port)}) + node := &discover.Node{ID: remid, IP: tcpAddr.IP, TCP: uint16(tcpAddr.Port)} + srv.AddPeer(node) select { case conn := <-accepted: @@ -169,6 +170,29 @@ func TestServerDial(t *testing.T) { if !reflect.DeepEqual(peers, []*Peer{peer}) { t.Errorf("Peers mismatch: got %v, want %v", peers, []*Peer{peer}) } + + // Test AddTrustedPeer/RemoveTrustedPeer and changing Trusted flags + // Particularly for race conditions on changing the flag state. + if peer := srv.Peers()[0]; peer.Info().Network.Trusted { + t.Errorf("peer is trusted prematurely: %v", peer) + } + done := make(chan bool) + go func() { + srv.AddTrustedPeer(node) + if peer := srv.Peers()[0]; !peer.Info().Network.Trusted { + t.Errorf("peer is not trusted after AddTrustedPeer: %v", peer) + } + srv.RemoveTrustedPeer(node) + if peer := srv.Peers()[0]; peer.Info().Network.Trusted { + t.Errorf("peer is trusted after RemoveTrustedPeer: %v", peer) + } + done <- true + }() + // Trigger potential race conditions + peer = srv.Peers()[0] + _ = peer.Inbound() + _ = peer.Info() + <-done case <-time.After(1 * time.Second): t.Error("server did not launch peer within one second") } @@ -365,7 +389,8 @@ func TestServerAtCap(t *testing.T) { } } // Try inserting a non-trusted connection. - c := newconn(randomID()) + anotherID := randomID() + c := newconn(anotherID) if err := srv.checkpoint(c, srv.posthandshake); err != DiscTooManyPeers { t.Error("wrong error for insert:", err) } @@ -378,6 +403,87 @@ func TestServerAtCap(t *testing.T) { t.Error("Server did not set trusted flag") } + // Remove from trusted set and try again + srv.RemoveTrustedPeer(&discover.Node{ID: trustedID}) + c = newconn(trustedID) + if err := srv.checkpoint(c, srv.posthandshake); err != DiscTooManyPeers { + t.Error("wrong error for insert:", err) + } + + // Add anotherID to trusted set and try again + srv.AddTrustedPeer(&discover.Node{ID: anotherID}) + c = newconn(anotherID) + if err := srv.checkpoint(c, srv.posthandshake); err != nil { + t.Error("unexpected error for trusted conn @posthandshake:", err) + } + if !c.is(trustedConn) { + t.Error("Server did not set trusted flag") + } +} + +func TestServerPeerLimits(t *testing.T) { + srvkey := newkey() + + clientid := randomID() + clientnode := &discover.Node{ID: clientid} + + var tp *setupTransport = &setupTransport{ + id: clientid, + phs: &protoHandshake{ + ID: clientid, + // Force "DiscUselessPeer" due to unmatching caps + // Caps: []Cap{discard.cap()}, + }, + } + var flags connFlag = dynDialedConn + var dialDest *discover.Node = &discover.Node{ID: clientid} + + srv := &Server{ + Config: Config{ + PrivateKey: srvkey, + MaxPeers: 0, + NoDial: true, + Protocols: []Protocol{discard}, + }, + newTransport: func(fd net.Conn) transport { return tp }, + log: log.New(), + } + if err := srv.Start(); err != nil { + t.Fatalf("couldn't start server: %v", err) + } + defer srv.Stop() + + // Check that server is full (MaxPeers=0) + conn, _ := net.Pipe() + srv.SetupConn(conn, flags, dialDest) + if tp.closeErr != DiscTooManyPeers { + t.Errorf("unexpected close error: %q", tp.closeErr) + } + conn.Close() + + srv.AddTrustedPeer(clientnode) + + // Check that server allows a trusted peer despite being full. + conn, _ = net.Pipe() + srv.SetupConn(conn, flags, dialDest) + if tp.closeErr == DiscTooManyPeers { + t.Errorf("failed to bypass MaxPeers with trusted node: %q", tp.closeErr) + } + + if tp.closeErr != DiscUselessPeer { + t.Errorf("unexpected close error: %q", tp.closeErr) + } + conn.Close() + + srv.RemoveTrustedPeer(clientnode) + + // Check that server is full again. + conn, _ = net.Pipe() + srv.SetupConn(conn, flags, dialDest) + if tp.closeErr != DiscTooManyPeers { + t.Errorf("unexpected close error: %q", tp.closeErr) + } + conn.Close() } func TestServerSetupConn(t *testing.T) { From fb2f822abe74e39e3bf60e672b0c41574431709f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 8 Nov 2024 17:24:31 +0800 Subject: [PATCH 182/479] node: fix golint warnings --- node/api.go | 2 +- node/config.go | 2 +- node/doc.go | 6 +++--- node/node_test.go | 4 ++-- node/utils_test.go | 6 +++--- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/node/api.go b/node/api.go index a014931efc6a..82982fe5a7f7 100644 --- a/node/api.go +++ b/node/api.go @@ -249,7 +249,7 @@ func (api *PrivateAdminAPI) StartWS(host *string, port *int, allowedOrigins *str return true, nil } -// StopRPC terminates an already running websocket RPC API endpoint. +// StopWS terminates an already running websocket RPC API endpoint. func (api *PrivateAdminAPI) StopWS() (bool, error) { api.node.lock.Lock() defer api.node.lock.Unlock() diff --git a/node/config.go b/node/config.go index 1984120f9ff3..8de6448acc5b 100644 --- a/node/config.go +++ b/node/config.go @@ -215,7 +215,7 @@ func DefaultHTTPEndpoint() string { return config.HTTPEndpoint() } -// WSEndpoint resolves an websocket endpoint based on the configured host interface +// WSEndpoint resolves a websocket endpoint based on the configured host interface // and port parameters. func (c *Config) WSEndpoint() string { if c.WSHost == "" { diff --git a/node/doc.go b/node/doc.go index d9688e0a12cb..e3cc58e5f49c 100644 --- a/node/doc.go +++ b/node/doc.go @@ -59,7 +59,7 @@ using the same data directory will store this information in different subdirect the data directory. LevelDB databases are also stored within the instance subdirectory. If multiple node -instances use the same data directory, openening the databases with identical names will +instances use the same data directory, opening the databases with identical names will create one database for each instance. The account key store is shared among all node instances using the same data directory @@ -69,7 +69,7 @@ unless its location is changed through the KeyStoreDir configuration option. Data Directory Sharing Example In this example, two node instances named A and B are started with the same data -directory. Mode instance A opens the database "db", node instance B opens the databases +directory. Node instance A opens the database "db", node instance B opens the databases "db" and "db-2". The following files will be created in the data directory: data-directory/ @@ -84,7 +84,7 @@ directory. Mode instance A opens the database "db", node instance B opens the da static-nodes.json -- devp2p static node list of instance B db/ -- LevelDB content for "db" db-2/ -- LevelDB content for "db-2" - B.ipc -- JSON-RPC UNIX domain socket endpoint of instance A + B.ipc -- JSON-RPC UNIX domain socket endpoint of instance B keystore/ -- account key store, used by both instances */ package node diff --git a/node/node_test.go b/node/node_test.go index 40f4e7118aaa..1dee63a72eb7 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -506,8 +506,8 @@ func TestAPIGather(t *testing.T) { } // Register a batch of services with some configured APIs calls := make(chan string, 1) - makeAPI := func(result string) *OneMethodApi { - return &OneMethodApi{fun: func() { calls <- result }} + makeAPI := func(result string) *OneMethodAPI { + return &OneMethodAPI{fun: func() { calls <- result }} } services := map[string]struct { APIs []rpc.API diff --git a/node/utils_test.go b/node/utils_test.go index 034b92d03aa1..d6aff608e05d 100644 --- a/node/utils_test.go +++ b/node/utils_test.go @@ -123,12 +123,12 @@ func InstrumentedServiceMakerC(base ServiceConstructor) ServiceConstructor { return InstrumentingWrapperMaker(base, reflect.TypeOf(InstrumentedServiceC{})) } -// OneMethodApi is a single-method API handler to be returned by test services. -type OneMethodApi struct { +// OneMethodAPI is a single-method API handler to be returned by test services. +type OneMethodAPI struct { fun func() } -func (api *OneMethodApi) TheOneMethod() { +func (api *OneMethodAPI) TheOneMethod() { if api.fun != nil { api.fun() } From 3ed9ce95c17362dd57a135d34e8becdcdcf699fa Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 8 Nov 2024 17:24:31 +0800 Subject: [PATCH 183/479] eth, node: use APPDATA env to support cygwin/msys correctly (#17786) --- eth/ethconfig/config.go | 11 +++++++++-- node/defaults.go | 37 +++++++++++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 512e57387620..642b59c4a0c0 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -86,8 +86,15 @@ func init() { home = user.HomeDir } } - if runtime.GOOS == "windows" { - Defaults.Ethash.DatasetDir = filepath.Join(home, "AppData", "Ethash") + if runtime.GOOS == "darwin" { + Defaults.Ethash.DatasetDir = filepath.Join(home, "Library", "Ethash") + } else if runtime.GOOS == "windows" { + localappdata := os.Getenv("LOCALAPPDATA") + if localappdata != "" { + Defaults.Ethash.DatasetDir = filepath.Join(localappdata, "Ethash") + } else { + Defaults.Ethash.DatasetDir = filepath.Join(home, "AppData", "Local", "Ethash") + } } else { Defaults.Ethash.DatasetDir = filepath.Join(home, ".ethash") } diff --git a/node/defaults.go b/node/defaults.go index 3f36a50d8f72..f43fbeec98bd 100644 --- a/node/defaults.go +++ b/node/defaults.go @@ -56,11 +56,20 @@ func DefaultDataDir() string { // Try to place the data folder in the user's home dir home := homeDir() if home != "" { - if runtime.GOOS == "darwin" { + switch runtime.GOOS { + case "darwin": return filepath.Join(home, "Library", "XDCchain") - } else if runtime.GOOS == "windows" { - return filepath.Join(home, "AppData", "Roaming", "XDCchain") - } else { + case "windows": + // We used to put everything in %HOME%\AppData\Roaming, but this caused + // problems with non-typical setups. If this fallback location exists and + // is non-empty, use it, otherwise DTRT and check %LOCALAPPDATA%. + fallback := filepath.Join(home, "AppData", "Roaming", "XDCchain") + appdata := windowsAppData() + if appdata == "" || isNonEmptyDir(fallback) { + return fallback + } + return filepath.Join(appdata, "XDCchain") + default: return filepath.Join(home, ".XDC") } } @@ -68,6 +77,26 @@ func DefaultDataDir() string { return "" } +func windowsAppData() string { + if v := os.Getenv("LOCALAPPDATA"); v != "" { + return v // Vista+ + } + if v := os.Getenv("APPDATA"); v != "" { + return filepath.Join(v, "Local") + } + return "" +} + +func isNonEmptyDir(dir string) bool { + f, err := os.Open(dir) + if err != nil { + return false + } + names, _ := f.Readdir(1) + f.Close() + return len(names) > 0 +} + func homeDir() string { if home := os.Getenv("HOME"); home != "" { return home From 68d9dcbee4a34d9a3c1fc3f01e9705ef1c19f9cc Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 8 Nov 2024 17:24:31 +0800 Subject: [PATCH 184/479] node: require LocalAppData variable (#19132) --- node/defaults.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/node/defaults.go b/node/defaults.go index f43fbeec98bd..8baa8b6e3935 100644 --- a/node/defaults.go +++ b/node/defaults.go @@ -78,13 +78,14 @@ func DefaultDataDir() string { } func windowsAppData() string { - if v := os.Getenv("LOCALAPPDATA"); v != "" { - return v // Vista+ + v := os.Getenv("LOCALAPPDATA") + if v == "" { + // Windows XP and below don't have LocalAppData. Crash here because + // we don't support Windows XP and undefining the variable will cause + // other issues. + panic("environment variable LocalAppData is undefined") } - if v := os.Getenv("APPDATA"); v != "" { - return filepath.Join(v, "Local") - } - return "" + return v } func isNonEmptyDir(dir string) bool { From d3c023ed37f260950938acb494bad69cdce3268a Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 8 Nov 2024 17:24:31 +0800 Subject: [PATCH 185/479] cmd/XDC, internal, node: nuke XDC monitor (#19399) --- cmd/XDC/monitorcmd.go | 351 ------------------------------------ internal/web3ext/web3ext.go | 5 - node/api.go | 117 ------------ node/node.go | 5 - 4 files changed, 478 deletions(-) delete mode 100644 cmd/XDC/monitorcmd.go diff --git a/cmd/XDC/monitorcmd.go b/cmd/XDC/monitorcmd.go deleted file mode 100644 index 42decc574ff5..000000000000 --- a/cmd/XDC/monitorcmd.go +++ /dev/null @@ -1,351 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . - -package main - -import ( - "fmt" - "math" - "reflect" - "runtime" - "sort" - "strings" - "time" - - "github.com/XinFinOrg/XDPoSChain/cmd/utils" - "github.com/XinFinOrg/XDPoSChain/node" - "github.com/XinFinOrg/XDPoSChain/rpc" - "github.com/gizak/termui" - "gopkg.in/urfave/cli.v1" -) - -var ( - monitorCommandAttachFlag = cli.StringFlag{ - Name: "attach", - Value: node.DefaultIPCEndpoint(clientIdentifier), - Usage: "API endpoint to attach to", - } - monitorCommandRowsFlag = cli.IntFlag{ - Name: "rows", - Value: 5, - Usage: "Maximum rows in the chart grid", - } - monitorCommandRefreshFlag = cli.IntFlag{ - Name: "refresh", - Value: 3, - Usage: "Refresh interval in seconds", - } - monitorCommand = cli.Command{ - Action: utils.MigrateFlags(monitor), // keep track of migration progress - Name: "monitor", - Usage: "Monitor and visualize node metrics", - ArgsUsage: " ", - Category: "MONITOR COMMANDS", - Description: ` -The XDC monitor is a tool to collect and visualize various internal metrics -gathered by the node, supporting different chart types as well as the capacity -to display multiple metrics simultaneously. -`, - Flags: []cli.Flag{ - monitorCommandAttachFlag, - monitorCommandRowsFlag, - monitorCommandRefreshFlag, - }, - } -) - -// monitor starts a terminal UI based monitoring tool for the requested metrics. -func monitor(ctx *cli.Context) error { - var ( - client *rpc.Client - err error - ) - // Attach to an Ethereum node over IPC or RPC - endpoint := ctx.String(monitorCommandAttachFlag.Name) - if client, err = dialRPC(endpoint); err != nil { - utils.Fatalf("Unable to attach to XDC node: %v", err) - } - defer client.Close() - - // Retrieve all the available metrics and resolve the user pattens - metrics, err := retrieveMetrics(client) - if err != nil { - utils.Fatalf("Failed to retrieve system metrics: %v", err) - } - monitored := resolveMetrics(metrics, ctx.Args()) - if len(monitored) == 0 { - list := expandMetrics(metrics, "") - sort.Strings(list) - - if len(list) > 0 { - utils.Fatalf("No metrics specified.\n\nAvailable:\n - %s", strings.Join(list, "\n - ")) - } else { - utils.Fatalf("No metrics collected by XDC (--%s).\n", utils.MetricsEnabledFlag.Name) - } - } - sort.Strings(monitored) - if cols := len(monitored) / ctx.Int(monitorCommandRowsFlag.Name); cols > 6 { - utils.Fatalf("Requested metrics (%d) spans more that 6 columns:\n - %s", len(monitored), strings.Join(monitored, "\n - ")) - } - // Create and configure the chart UI defaults - if err := termui.Init(); err != nil { - utils.Fatalf("Unable to initialize terminal UI: %v", err) - } - defer termui.Close() - - rows := len(monitored) - if max := ctx.Int(monitorCommandRowsFlag.Name); rows > max { - rows = max - } - cols := (len(monitored) + rows - 1) / rows - for i := 0; i < rows; i++ { - termui.Body.AddRows(termui.NewRow()) - } - // Create each individual data chart - footer := termui.NewPar("") - footer.Block.Border = true - footer.Height = 3 - - charts := make([]*termui.LineChart, len(monitored)) - units := make([]int, len(monitored)) - data := make([][]float64, len(monitored)) - for i := 0; i < len(monitored); i++ { - charts[i] = createChart((termui.TermHeight() - footer.Height) / rows) - row := termui.Body.Rows[i%rows] - row.Cols = append(row.Cols, termui.NewCol(12/cols, 0, charts[i])) - } - termui.Body.AddRows(termui.NewRow(termui.NewCol(12, 0, footer))) - - refreshCharts(client, monitored, data, units, charts, ctx, footer) - termui.Body.Align() - termui.Render(termui.Body) - - // Watch for various system events, and periodically refresh the charts - termui.Handle("/sys/kbd/C-c", func(termui.Event) { - termui.StopLoop() - }) - termui.Handle("/sys/wnd/resize", func(termui.Event) { - termui.Body.Width = termui.TermWidth() - for _, chart := range charts { - chart.Height = (termui.TermHeight() - footer.Height) / rows - } - termui.Body.Align() - termui.Render(termui.Body) - }) - go func() { - tick := time.NewTicker(time.Duration(ctx.Int(monitorCommandRefreshFlag.Name)) * time.Second) - for range tick.C { - if refreshCharts(client, monitored, data, units, charts, ctx, footer) { - termui.Body.Align() - } - termui.Render(termui.Body) - } - }() - termui.Loop() - return nil -} - -// retrieveMetrics contacts the attached XDC node and retrieves the entire set -// of collected system metrics. -func retrieveMetrics(client *rpc.Client) (map[string]interface{}, error) { - var metrics map[string]interface{} - err := client.Call(&metrics, "debug_metrics", true) - return metrics, err -} - -// resolveMetrics takes a list of input metric patterns, and resolves each to one -// or more canonical metric names. -func resolveMetrics(metrics map[string]interface{}, patterns []string) []string { - res := []string{} - for _, pattern := range patterns { - res = append(res, resolveMetric(metrics, pattern, "")...) - } - return res -} - -// resolveMetrics takes a single of input metric pattern, and resolves it to one -// or more canonical metric names. -func resolveMetric(metrics map[string]interface{}, pattern string, path string) []string { - results := []string{} - - // If a nested metric was requested, recurse optionally branching (via comma) - parts := strings.SplitN(pattern, "/", 2) - if len(parts) > 1 { - for _, variation := range strings.Split(parts[0], ",") { - if submetrics, ok := metrics[variation].(map[string]interface{}); !ok { - utils.Fatalf("Failed to retrieve system metrics: %s", path+variation) - return nil - } else { - results = append(results, resolveMetric(submetrics, parts[1], path+variation+"/")...) - } - } - return results - } - // Depending what the last link is, return or expand - for _, variation := range strings.Split(pattern, ",") { - switch metric := metrics[variation].(type) { - case float64: - // Final metric value found, return as singleton - results = append(results, path+variation) - - case map[string]interface{}: - results = append(results, expandMetrics(metric, path+variation+"/")...) - - default: - utils.Fatalf("Metric pattern resolved to unexpected type: %v", reflect.TypeOf(metric)) - return nil - } - } - return results -} - -// expandMetrics expands the entire tree of metrics into a flat list of paths. -func expandMetrics(metrics map[string]interface{}, path string) []string { - // Iterate over all fields and expand individually - list := []string{} - for name, metric := range metrics { - switch metric := metric.(type) { - case float64: - // Final metric value found, append to list - list = append(list, path+name) - - case map[string]interface{}: - // Tree of metrics found, expand recursively - list = append(list, expandMetrics(metric, path+name+"/")...) - - default: - utils.Fatalf("Metric pattern %s resolved to unexpected type: %v", path+name, reflect.TypeOf(metric)) - return nil - } - } - return list -} - -// fetchMetric iterates over the metrics map and retrieves a specific one. -func fetchMetric(metrics map[string]interface{}, metric string) float64 { - parts := strings.Split(metric, "/") - for _, part := range parts[:len(parts)-1] { - var found bool - metrics, found = metrics[part].(map[string]interface{}) - if !found { - return 0 - } - } - if v, ok := metrics[parts[len(parts)-1]].(float64); ok { - return v - } - return 0 -} - -// refreshCharts retrieves a next batch of metrics, and inserts all the new -// values into the active datasets and charts -func refreshCharts(client *rpc.Client, metrics []string, data [][]float64, units []int, charts []*termui.LineChart, ctx *cli.Context, footer *termui.Par) (realign bool) { - values, err := retrieveMetrics(client) - for i, metric := range metrics { - if len(data) < 512 { - data[i] = append([]float64{fetchMetric(values, metric)}, data[i]...) - } else { - data[i] = append([]float64{fetchMetric(values, metric)}, data[i][:len(data[i])-1]...) - } - if updateChart(metric, data[i], &units[i], charts[i], err) { - realign = true - } - } - updateFooter(ctx, err, footer) - return -} - -// updateChart inserts a dataset into a line chart, scaling appropriately as to -// not display weird labels, also updating the chart label accordingly. -func updateChart(metric string, data []float64, base *int, chart *termui.LineChart, err error) (realign bool) { - dataUnits := []string{"", "K", "M", "G", "T", "E"} - timeUnits := []string{"ns", "µs", "ms", "s", "ks", "ms"} - colors := []termui.Attribute{termui.ColorBlue, termui.ColorCyan, termui.ColorGreen, termui.ColorYellow, termui.ColorRed, termui.ColorRed} - - // Extract only part of the data that's actually visible - if chart.Width*2 < len(data) { - data = data[:chart.Width*2] - } - // Find the maximum value and scale under 1K - high := 0.0 - if len(data) > 0 { - high = data[0] - for _, value := range data[1:] { - high = math.Max(high, value) - } - } - unit, scale := 0, 1.0 - for high >= 1000 && unit+1 < len(dataUnits) { - high, unit, scale = high/1000, unit+1, scale*1000 - } - // If the unit changes, re-create the chart (hack to set max height...) - if unit != *base { - realign, *base, *chart = true, unit, *createChart(chart.Height) - } - // Update the chart's data points with the scaled values - if cap(chart.Data) < len(data) { - chart.Data = make([]float64, len(data)) - } - chart.Data = chart.Data[:len(data)] - for i, value := range data { - chart.Data[i] = value / scale - } - // Update the chart's label with the scale units - units := dataUnits - if strings.Contains(metric, "/Percentiles/") || strings.Contains(metric, "/pauses/") || strings.Contains(metric, "/time/") { - units = timeUnits - } - chart.BorderLabel = metric - if len(units[unit]) > 0 { - chart.BorderLabel += " [" + units[unit] + "]" - } - chart.LineColor = colors[unit] | termui.AttrBold - if err != nil { - chart.LineColor = termui.ColorRed | termui.AttrBold - } - return -} - -// createChart creates an empty line chart with the default configs. -func createChart(height int) *termui.LineChart { - chart := termui.NewLineChart() - if runtime.GOOS == "windows" { - chart.Mode = "dot" - } - chart.DataLabels = []string{""} - chart.Height = height - chart.AxesColor = termui.ColorWhite - chart.PaddingBottom = -2 - - chart.BorderLabelFg = chart.BorderFg | termui.AttrBold - chart.BorderFg = chart.BorderBg - - return chart -} - -// updateFooter updates the footer contents based on any encountered errors. -func updateFooter(ctx *cli.Context, err error, footer *termui.Par) { - // Generate the basic footer - refresh := time.Duration(ctx.Int(monitorCommandRefreshFlag.Name)) * time.Second - footer.Text = fmt.Sprintf("Press Ctrl+C to quit. Refresh interval: %v.", refresh) - footer.TextFgColor = termui.ThemeAttr("par.fg") | termui.AttrBold - - // Append any encountered errors - if err != nil { - footer.Text = fmt.Sprintf("Error: %v.", err) - footer.TextFgColor = termui.ColorRed | termui.AttrBold - } -} diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 8aaa3e9d263f..f726b5d6205b 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -300,11 +300,6 @@ web3._extend({ name: 'chaindbCompact', call: 'debug_chaindbCompact', }), - new web3._extend.Method({ - name: 'metrics', - call: 'debug_metrics', - params: 1 - }), new web3._extend.Method({ name: 'verbosity', call: 'debug_verbosity', diff --git a/node/api.go b/node/api.go index 82982fe5a7f7..3c6528e4d50d 100644 --- a/node/api.go +++ b/node/api.go @@ -21,11 +21,9 @@ import ( "errors" "fmt" "strings" - "time" "github.com/XinFinOrg/XDPoSChain/common/hexutil" "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/metrics" "github.com/XinFinOrg/XDPoSChain/p2p" "github.com/XinFinOrg/XDPoSChain/p2p/discover" "github.com/XinFinOrg/XDPoSChain/rpc" @@ -298,121 +296,6 @@ func (api *PublicAdminAPI) Datadir() string { return api.node.DataDir() } -// PublicDebugAPI is the collection of debugging related API methods exposed over -// both secure and unsecure RPC channels. -type PublicDebugAPI struct { - node *Node // Node interfaced by this API -} - -// NewPublicDebugAPI creates a new API definition for the public debug methods -// of the node itself. -func NewPublicDebugAPI(node *Node) *PublicDebugAPI { - return &PublicDebugAPI{node: node} -} - -// Metrics retrieves all the known system metric collected by the node. -func (api *PublicDebugAPI) Metrics(raw bool) (map[string]interface{}, error) { - // Create a rate formatter - units := []string{"", "K", "M", "G", "T", "E", "P"} - round := func(value float64, prec int) string { - unit := 0 - for value >= 1000 { - unit, value, prec = unit+1, value/1000, 2 - } - return fmt.Sprintf(fmt.Sprintf("%%.%df%s", prec, units[unit]), value) - } - format := func(total float64, rate float64) string { - return fmt.Sprintf("%s (%s/s)", round(total, 0), round(rate, 2)) - } - // Iterate over all the metrics, and just dump for now - counters := make(map[string]interface{}) - metrics.DefaultRegistry.Each(func(name string, metric interface{}) { - // Create or retrieve the counter hierarchy for this metric - root, parts := counters, strings.Split(name, "/") - for _, part := range parts[:len(parts)-1] { - if _, ok := root[part]; !ok { - root[part] = make(map[string]interface{}) - } - root = root[part].(map[string]interface{}) - } - name = parts[len(parts)-1] - - // Fill the counter with the metric details, formatting if requested - if raw { - switch metric := metric.(type) { - case metrics.Counter: - root[name] = map[string]interface{}{ - "Overall": float64(metric.Count()), - } - - case metrics.Meter: - root[name] = map[string]interface{}{ - "AvgRate01Min": metric.Rate1(), - "AvgRate05Min": metric.Rate5(), - "AvgRate15Min": metric.Rate15(), - "MeanRate": metric.RateMean(), - "Overall": float64(metric.Count()), - } - - case metrics.Timer: - root[name] = map[string]interface{}{ - "AvgRate01Min": metric.Rate1(), - "AvgRate05Min": metric.Rate5(), - "AvgRate15Min": metric.Rate15(), - "MeanRate": metric.RateMean(), - "Overall": float64(metric.Count()), - "Percentiles": map[string]interface{}{ - "5": metric.Percentile(0.05), - "20": metric.Percentile(0.2), - "50": metric.Percentile(0.5), - "80": metric.Percentile(0.8), - "95": metric.Percentile(0.95), - }, - } - - default: - root[name] = "Unknown metric type" - } - } else { - switch metric := metric.(type) { - case metrics.Counter: - root[name] = map[string]interface{}{ - "Overall": float64(metric.Count()), - } - - case metrics.Meter: - root[name] = map[string]interface{}{ - "Avg01Min": format(metric.Rate1()*60, metric.Rate1()), - "Avg05Min": format(metric.Rate5()*300, metric.Rate5()), - "Avg15Min": format(metric.Rate15()*900, metric.Rate15()), - "Overall": format(float64(metric.Count()), metric.RateMean()), - } - - case metrics.Timer: - root[name] = map[string]interface{}{ - "Avg01Min": format(metric.Rate1()*60, metric.Rate1()), - "Avg05Min": format(metric.Rate5()*300, metric.Rate5()), - "Avg15Min": format(metric.Rate15()*900, metric.Rate15()), - "Overall": format(float64(metric.Count()), metric.RateMean()), - "Maximum": time.Duration(metric.Max()).String(), - "Minimum": time.Duration(metric.Min()).String(), - "Percentiles": map[string]interface{}{ - "5": time.Duration(metric.Percentile(0.05)).String(), - "20": time.Duration(metric.Percentile(0.2)).String(), - "50": time.Duration(metric.Percentile(0.5)).String(), - "80": time.Duration(metric.Percentile(0.8)).String(), - "95": time.Duration(metric.Percentile(0.95)).String(), - }, - } - - default: - root[name] = "Unknown metric type" - } - } - }) - return counters, nil -} - // PublicWeb3API offers helper utils type PublicWeb3API struct { stack *Node diff --git a/node/node.go b/node/node.go index c5adec4451bd..11813d8865ff 100644 --- a/node/node.go +++ b/node/node.go @@ -628,11 +628,6 @@ func (n *Node) apis() []rpc.API { Namespace: "debug", Version: "1.0", Service: debug.Handler, - }, { - Namespace: "debug", - Version: "1.0", - Service: NewPublicDebugAPI(n), - Public: true, }, { Namespace: "web3", Version: "1.0", From 3f1b7d3a7b87bf47e3b8d841f2a82f53182d86b4 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 8 Nov 2024 17:24:31 +0800 Subject: [PATCH 186/479] rpc: check module availability at startup (#20597) --- cmd/XDC/config.go | 4 ++-- rpc/endpoints.go | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/cmd/XDC/config.go b/cmd/XDC/config.go index a47f19c7bf20..26908a0b2c25 100644 --- a/cmd/XDC/config.go +++ b/cmd/XDC/config.go @@ -120,8 +120,8 @@ func defaultNodeConfig() node.Config { cfg := node.DefaultConfig cfg.Name = clientIdentifier cfg.Version = params.VersionWithCommit(gitCommit) - cfg.HTTPModules = append(cfg.HTTPModules, "eth", "shh") - cfg.WSModules = append(cfg.WSModules, "eth", "shh") + cfg.HTTPModules = append(cfg.HTTPModules, "eth") + cfg.WSModules = append(cfg.WSModules, "eth") cfg.IPCPath = "XDC.ipc" return cfg } diff --git a/rpc/endpoints.go b/rpc/endpoints.go index 21802c9542db..787da2d0a90f 100644 --- a/rpc/endpoints.go +++ b/rpc/endpoints.go @@ -17,14 +17,40 @@ package rpc import ( + "fmt" "net" "strings" "github.com/XinFinOrg/XDPoSChain/log" ) -// StartHTTPEndpoint starts the HTTP RPC endpoint, configured with cors/vhosts/modules +// checkModuleAvailability check that all names given in modules are actually +// available API services. +func checkModuleAvailability(modules []string, apis []API) error { + available := make(map[string]struct{}) + var availableNames string + for i, api := range apis { + if _, ok := available[api.Namespace]; !ok { + available[api.Namespace] = struct{}{} + if i > 0 { + availableNames += ", " + } + availableNames += api.Namespace + } + } + for _, name := range modules { + if _, ok := available[name]; !ok { + return fmt.Errorf("invalid API %q in whitelist (available: %s)", name, availableNames) + } + } + return nil +} + +// StartHTTPEndpoint starts the HTTP RPC endpoint, configured with cors/vhosts/modules. func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []string, vhosts []string, timeouts HTTPTimeouts) (net.Listener, *Server, error) { + if err := checkModuleAvailability(modules, apis); err != nil { + return nil, nil, err + } // Generate the whitelist based on the allowed modules whitelist := make(map[string]bool) for _, module := range modules { @@ -52,8 +78,11 @@ func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []str return listener, handler, err } -// StartWSEndpoint starts a websocket endpoint +// StartWSEndpoint starts a websocket endpoint. func StartWSEndpoint(endpoint string, apis []API, modules []string, wsOrigins []string, exposeAll bool) (net.Listener, *Server, error) { + if err := checkModuleAvailability(modules, apis); err != nil { + return nil, nil, err + } // Generate the whitelist based on the allowed modules whitelist := make(map[string]bool) for _, module := range modules { From fd2b21702ce19261a0597a8573a57c73ac43feb6 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 8 Nov 2024 17:24:31 +0800 Subject: [PATCH 187/479] rpc: remove startup error for invalid modules, log it instead (#20684) --- rpc/endpoints.go | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/rpc/endpoints.go b/rpc/endpoints.go index 787da2d0a90f..9cb6cc777045 100644 --- a/rpc/endpoints.go +++ b/rpc/endpoints.go @@ -17,7 +17,6 @@ package rpc import ( - "fmt" "net" "strings" @@ -26,30 +25,26 @@ import ( // checkModuleAvailability check that all names given in modules are actually // available API services. -func checkModuleAvailability(modules []string, apis []API) error { - available := make(map[string]struct{}) - var availableNames string - for i, api := range apis { - if _, ok := available[api.Namespace]; !ok { - available[api.Namespace] = struct{}{} - if i > 0 { - availableNames += ", " - } - availableNames += api.Namespace +func checkModuleAvailability(modules []string, apis []API) (bad, available []string) { + availableSet := make(map[string]struct{}) + for _, api := range apis { + if _, ok := availableSet[api.Namespace]; !ok { + availableSet[api.Namespace] = struct{}{} + available = append(available, api.Namespace) } } for _, name := range modules { - if _, ok := available[name]; !ok { - return fmt.Errorf("invalid API %q in whitelist (available: %s)", name, availableNames) + if _, ok := availableSet[name]; !ok { + bad = append(bad, name) } } - return nil + return bad, available } // StartHTTPEndpoint starts the HTTP RPC endpoint, configured with cors/vhosts/modules. func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []string, vhosts []string, timeouts HTTPTimeouts) (net.Listener, *Server, error) { - if err := checkModuleAvailability(modules, apis); err != nil { - return nil, nil, err + if bad, available := checkModuleAvailability(modules, apis); len(bad) > 0 { + log.Error("Unavailable modules in HTTP API list", "unavailable", bad, "available", available) } // Generate the whitelist based on the allowed modules whitelist := make(map[string]bool) @@ -80,8 +75,8 @@ func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []str // StartWSEndpoint starts a websocket endpoint. func StartWSEndpoint(endpoint string, apis []API, modules []string, wsOrigins []string, exposeAll bool) (net.Listener, *Server, error) { - if err := checkModuleAvailability(modules, apis); err != nil { - return nil, nil, err + if bad, available := checkModuleAvailability(modules, apis); len(bad) > 0 { + log.Error("Unavailable modules in WS API list", "unavailable", bad, "available", available) } // Generate the whitelist based on the allowed modules whitelist := make(map[string]bool) From 57e5f5ec589d681cbfbc7d40c8c96cc9a52b768d Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 8 Nov 2024 17:24:31 +0800 Subject: [PATCH 188/479] rpc: not log error if user configures --rpcapi=rpc (#20776) --- rpc/endpoints.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/rpc/endpoints.go b/rpc/endpoints.go index 9cb6cc777045..eb14e99f6fa3 100644 --- a/rpc/endpoints.go +++ b/rpc/endpoints.go @@ -23,8 +23,9 @@ import ( "github.com/XinFinOrg/XDPoSChain/log" ) -// checkModuleAvailability check that all names given in modules are actually -// available API services. +// checkModuleAvailability checks that all names given in modules are actually +// available API services. It assumes that the MetadataApi module ("rpc") is always available; +// the registration of this "rpc" module happens in NewServer() and is thus common to all endpoints. func checkModuleAvailability(modules []string, apis []API) (bad, available []string) { availableSet := make(map[string]struct{}) for _, api := range apis { @@ -34,7 +35,7 @@ func checkModuleAvailability(modules []string, apis []API) (bad, available []str } } for _, name := range modules { - if _, ok := availableSet[name]; !ok { + if _, ok := availableSet[name]; !ok && name != MetadataApi { bad = append(bad, name) } } From b76438aafe0e36e0ddba8f00fdb37ad803ec298e Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 8 Nov 2024 17:24:31 +0800 Subject: [PATCH 189/479] node: allow WebSocket and HTTP work on same port (#20810) --- node/api.go | 2 +- node/endpoints.go | 98 ++++++++++++++++++++++++ node/node.go | 69 ++++++++++++++--- node/node_test.go | 59 ++++++++++++++- node/rpcstack.go | 170 ++++++++++++++++++++++++++++++++++++++++++ node/rpcstack_test.go | 38 ++++++++++ rpc/endpoints.go | 85 +-------------------- rpc/http.go | 101 ------------------------- rpc/websocket.go | 7 -- 9 files changed, 424 insertions(+), 205 deletions(-) create mode 100644 node/endpoints.go create mode 100644 node/rpcstack.go create mode 100644 node/rpcstack_test.go diff --git a/node/api.go b/node/api.go index 3c6528e4d50d..362880ceaa7b 100644 --- a/node/api.go +++ b/node/api.go @@ -187,7 +187,7 @@ func (api *PrivateAdminAPI) StartRPC(host *string, port *int, cors *string, apis } } - if err := api.node.startHTTP(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, allowedOrigins, allowedVHosts, api.node.config.HTTPTimeouts); err != nil { + if err := api.node.startHTTP(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, allowedOrigins, allowedVHosts, api.node.config.HTTPTimeouts, api.node.config.WSOrigins); err != nil { return false, err } return true, nil diff --git a/node/endpoints.go b/node/endpoints.go new file mode 100644 index 000000000000..277a6644bfa3 --- /dev/null +++ b/node/endpoints.go @@ -0,0 +1,98 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package node + +import ( + "net" + "net/http" + "time" + + "github.com/XinFinOrg/XDPoSChain/log" + "github.com/XinFinOrg/XDPoSChain/rpc" +) + +// StartHTTPEndpoint starts the HTTP RPC endpoint. +func StartHTTPEndpoint(endpoint string, timeouts rpc.HTTPTimeouts, handler http.Handler) (net.Listener, error) { + // start the HTTP listener + var ( + listener net.Listener + err error + ) + if listener, err = net.Listen("tcp", endpoint); err != nil { + return nil, err + } + // Bundle and start the HTTP server + httpSrv := &http.Server{ + Handler: handler, + ReadTimeout: timeouts.ReadTimeout, + WriteTimeout: timeouts.WriteTimeout, + IdleTimeout: timeouts.IdleTimeout, + } + log.Info("StartHTTPEndpoint", "ReadTimeout", timeouts.ReadTimeout, "WriteTimeout", timeouts.WriteTimeout, "IdleTimeout", timeouts.IdleTimeout) + go httpSrv.Serve(listener) + return listener, err +} + +// startWSEndpoint starts a websocket endpoint. +func startWSEndpoint(endpoint string, handler http.Handler) (net.Listener, error) { + // start the HTTP listener + var ( + listener net.Listener + err error + ) + if listener, err = net.Listen("tcp", endpoint); err != nil { + return nil, err + } + wsSrv := &http.Server{Handler: handler} + go wsSrv.Serve(listener) + return listener, err +} + +// checkModuleAvailability checks that all names given in modules are actually +// available API services. It assumes that the MetadataApi module ("rpc") is always available; +// the registration of this "rpc" module happens in NewServer() and is thus common to all endpoints. +func checkModuleAvailability(modules []string, apis []rpc.API) (bad, available []string) { + availableSet := make(map[string]struct{}) + for _, api := range apis { + if _, ok := availableSet[api.Namespace]; !ok { + availableSet[api.Namespace] = struct{}{} + available = append(available, api.Namespace) + } + } + for _, name := range modules { + if _, ok := availableSet[name]; !ok && name != rpc.MetadataApi { + bad = append(bad, name) + } + } + return bad, available +} + +// CheckTimeouts ensures that timeout values are meaningful +func CheckTimeouts(timeouts *rpc.HTTPTimeouts) { + if timeouts.ReadTimeout < time.Second { + log.Warn("Sanitizing invalid HTTP read timeout", "provided", timeouts.ReadTimeout, "updated", rpc.DefaultHTTPTimeouts.ReadTimeout) + timeouts.ReadTimeout = rpc.DefaultHTTPTimeouts.ReadTimeout + } + if timeouts.WriteTimeout < time.Second { + log.Warn("Sanitizing invalid HTTP write timeout", "provided", timeouts.WriteTimeout, "updated", rpc.DefaultHTTPTimeouts.WriteTimeout) + timeouts.WriteTimeout = rpc.DefaultHTTPTimeouts.WriteTimeout + } + if timeouts.IdleTimeout < time.Second { + log.Warn("Sanitizing invalid HTTP idle timeout", "provided", timeouts.IdleTimeout, "updated", rpc.DefaultHTTPTimeouts.IdleTimeout) + timeouts.IdleTimeout = rpc.DefaultHTTPTimeouts.IdleTimeout + } +} diff --git a/node/node.go b/node/node.go index 11813d8865ff..b53ab4a85548 100644 --- a/node/node.go +++ b/node/node.go @@ -269,17 +269,21 @@ func (n *Node) startRPC(services map[reflect.Type]Service) error { n.stopInProc() return err } - if err := n.startHTTP(n.httpEndpoint, apis, n.config.HTTPModules, n.config.HTTPCors, n.config.HTTPVirtualHosts, n.config.HTTPTimeouts); err != nil { + if err := n.startHTTP(n.httpEndpoint, apis, n.config.HTTPModules, n.config.HTTPCors, n.config.HTTPVirtualHosts, n.config.HTTPTimeouts, n.config.WSOrigins); err != nil { n.stopIPC() n.stopInProc() return err } - if err := n.startWS(n.wsEndpoint, apis, n.config.WSModules, n.config.WSOrigins, n.config.WSExposeAll); err != nil { - n.stopHTTP() - n.stopIPC() - n.stopInProc() - return err + // if endpoints are not the same, start separate servers + if n.httpEndpoint != n.wsEndpoint { + if err := n.startWS(n.wsEndpoint, apis, n.config.WSModules, n.config.WSOrigins, n.config.WSExposeAll); err != nil { + n.stopHTTP() + n.stopIPC() + n.stopInProc() + return err + } } + // All API endpoints started successfully n.rpcAPIs = apis return nil @@ -348,22 +352,36 @@ func (n *Node) stopIPC() { } // startHTTP initializes and starts the HTTP RPC endpoint. -func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string, timeouts rpc.HTTPTimeouts) error { +func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string, timeouts rpc.HTTPTimeouts, wsOrigins []string) error { // Short circuit if the HTTP endpoint isn't being exposed if endpoint == "" { return nil } - listener, handler, err := rpc.StartHTTPEndpoint(endpoint, apis, modules, cors, vhosts, timeouts) + // register apis and create handler stack + srv := rpc.NewServer() + err := RegisterApisFromWhitelist(apis, modules, srv, false) + if err != nil { + return err + } + handler := NewHTTPHandlerStack(srv, cors, vhosts, &timeouts) + // wrap handler in websocket handler only if websocket port is the same as http rpc + if n.httpEndpoint == n.wsEndpoint { + handler = NewWebsocketUpgradeHandler(handler, srv.WebsocketHandler(wsOrigins)) + } + listener, err := StartHTTPEndpoint(endpoint, timeouts, handler) if err != nil { return err } n.log.Info("HTTP endpoint opened", "url", fmt.Sprintf("http://%v/", listener.Addr()), "cors", strings.Join(cors, ","), "vhosts", strings.Join(vhosts, ",")) + if n.httpEndpoint == n.wsEndpoint { + n.log.Info("WebSocket endpoint opened", "url", fmt.Sprintf("ws://%v", listener.Addr())) + } // All listeners booted successfully n.httpEndpoint = endpoint n.httpListener = listener - n.httpHandler = handler + n.httpHandler = srv return nil } @@ -388,7 +406,14 @@ func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrig if endpoint == "" { return nil } - listener, handler, err := rpc.StartWSEndpoint(endpoint, apis, modules, wsOrigins, exposeAll) + + srv := rpc.NewServer() + handler := srv.WebsocketHandler(wsOrigins) + err := RegisterApisFromWhitelist(apis, modules, srv, exposeAll) + if err != nil { + return err + } + listener, err := startWSEndpoint(endpoint, handler) if err != nil { return err } @@ -396,7 +421,7 @@ func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrig // All listeners booted successfully n.wsEndpoint = endpoint n.wsListener = listener - n.wsHandler = handler + n.wsHandler = srv return nil } @@ -636,3 +661,25 @@ func (n *Node) apis() []rpc.API { }, } } + +// RegisterApisFromWhitelist checks the given modules' availability, generates a whitelist based on the allowed modules, +// and then registers all of the APIs exposed by the services. +func RegisterApisFromWhitelist(apis []rpc.API, modules []string, srv *rpc.Server, exposeAll bool) error { + if bad, available := checkModuleAvailability(modules, apis); len(bad) > 0 { + log.Error("Unavailable modules in HTTP API list", "unavailable", bad, "available", available) + } + // Generate the whitelist based on the allowed modules + whitelist := make(map[string]bool) + for _, module := range modules { + whitelist[module] = true + } + // Register all the APIs exposed by the services + for _, api := range apis { + if exposeAll || whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { + if err := srv.RegisterName(api.Namespace, api.Service); err != nil { + return err + } + } + } + return nil +} diff --git a/node/node_test.go b/node/node_test.go index 1dee63a72eb7..4b4eec464d7d 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -18,6 +18,7 @@ package node import ( "errors" + "net/http" "os" "reflect" "testing" @@ -26,6 +27,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/p2p" "github.com/XinFinOrg/XDPoSChain/rpc" + "github.com/stretchr/testify/assert" ) var ( @@ -332,7 +334,7 @@ func TestServiceStartupAbortion(t *testing.T) { } // Tests that even if a registered service fails to shut down cleanly, it does -// not influece the rest of the shutdown invocations. +// not influence the rest of the shutdown invocations. func TestServiceTerminationGuarantee(t *testing.T) { stack, err := New(testNodeConfig()) if err != nil { @@ -572,3 +574,58 @@ func TestAPIGather(t *testing.T) { } } } + +func TestWebsocketHTTPOnSamePort_WebsocketRequest(t *testing.T) { + node := startHTTP(t) + defer node.stopHTTP() + + wsReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:7453", nil) + if err != nil { + t.Error("could not issue new http request ", err) + } + wsReq.Header.Set("Connection", "upgrade") + wsReq.Header.Set("Upgrade", "websocket") + wsReq.Header.Set("Sec-WebSocket-Version", "13") + wsReq.Header.Set("Sec-Websocket-Key", "SGVsbG8sIHdvcmxkIQ==") + + resp := doHTTPRequest(t, wsReq) + assert.Equal(t, "websocket", resp.Header.Get("Upgrade")) +} + +func TestWebsocketHTTPOnSamePort_HTTPRequest(t *testing.T) { + node := startHTTP(t) + defer node.stopHTTP() + + httpReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:7453", nil) + if err != nil { + t.Error("could not issue new http request ", err) + } + httpReq.Header.Set("Accept-Encoding", "gzip") + + resp := doHTTPRequest(t, httpReq) + assert.Equal(t, "gzip", resp.Header.Get("Content-Encoding")) +} + +func startHTTP(t *testing.T) *Node { + conf := &Config{HTTPPort: 7453, WSPort: 7453} + node, err := New(conf) + if err != nil { + t.Error("could not create a new node ", err) + } + + err = node.startHTTP("127.0.0.1:7453", []rpc.API{}, []string{}, []string{}, []string{}, rpc.HTTPTimeouts{}, []string{}) + if err != nil { + t.Error("could not start http service on node ", err) + } + + return node +} + +func doHTTPRequest(t *testing.T, req *http.Request) *http.Response { + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + t.Error("could not issue a GET request to the given endpoint", err) + } + return resp +} diff --git a/node/rpcstack.go b/node/rpcstack.go new file mode 100644 index 000000000000..0c6ecc4485ec --- /dev/null +++ b/node/rpcstack.go @@ -0,0 +1,170 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package node + +import ( + "compress/gzip" + "io" + "net" + "net/http" + "strings" + "sync" + "time" + + "github.com/XinFinOrg/XDPoSChain/log" + "github.com/XinFinOrg/XDPoSChain/rpc" + "github.com/rs/cors" +) + +// NewHTTPHandlerStack returns wrapped http-related handlers +func NewHTTPHandlerStack(srv http.Handler, cors []string, vhosts []string, timeouts *rpc.HTTPTimeouts) http.Handler { + // Wrap the CORS-handler within a host-handler + handler := newCorsHandler(srv, cors) + handler = newVHostHandler(vhosts, handler) + handler = newGzipHandler(handler) + + // make sure timeout values are meaningful + CheckTimeouts(timeouts) + + // PR #469: register timeout handler before WebSocket and HTTP + handler = http.TimeoutHandler(handler, timeouts.WriteTimeout, `{"error":"http server timeout"}`) + + // add 1 second to let TimeoutHandler works first + timeouts.WriteTimeout = timeouts.WriteTimeout + time.Second + return handler +} + +func newCorsHandler(srv http.Handler, allowedOrigins []string) http.Handler { + // disable CORS support if user has not specified a custom CORS configuration + if len(allowedOrigins) == 0 { + return srv + } + c := cors.New(cors.Options{ + AllowedOrigins: allowedOrigins, + AllowedMethods: []string{http.MethodPost, http.MethodGet}, + MaxAge: 600, + AllowedHeaders: []string{"*"}, + }) + return c.Handler(srv) +} + +// virtualHostHandler is a handler which validates the Host-header of incoming requests. +// Using virtual hosts can help prevent DNS rebinding attacks, where a 'random' domain name points to +// the service ip address (but without CORS headers). By verifying the targeted virtual host, we can +// ensure that it's a destination that the node operator has defined. +type virtualHostHandler struct { + vhosts map[string]struct{} + next http.Handler +} + +func newVHostHandler(vhosts []string, next http.Handler) http.Handler { + vhostMap := make(map[string]struct{}) + for _, allowedHost := range vhosts { + vhostMap[strings.ToLower(allowedHost)] = struct{}{} + } + return &virtualHostHandler{vhostMap, next} +} + +// ServeHTTP serves JSON-RPC requests over HTTP, implements http.Handler +func (h *virtualHostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // if r.Host is not set, we can continue serving since a browser would set the Host header + if r.Host == "" { + h.next.ServeHTTP(w, r) + return + } + host, _, err := net.SplitHostPort(r.Host) + if err != nil { + // Either invalid (too many colons) or no port specified + host = r.Host + } + if ipAddr := net.ParseIP(host); ipAddr != nil { + // It's an IP address, we can serve that + h.next.ServeHTTP(w, r) + return + + } + // Not an IP address, but a hostname. Need to validate + if _, exist := h.vhosts["*"]; exist { + h.next.ServeHTTP(w, r) + return + } + if _, exist := h.vhosts[host]; exist { + h.next.ServeHTTP(w, r) + return + } + http.Error(w, "invalid host specified", http.StatusForbidden) +} + +var gzPool = sync.Pool{ + New: func() interface{} { + w := gzip.NewWriter(io.Discard) + return w + }, +} + +type gzipResponseWriter struct { + io.Writer + http.ResponseWriter +} + +func (w *gzipResponseWriter) WriteHeader(status int) { + w.Header().Del("Content-Length") + w.ResponseWriter.WriteHeader(status) +} + +func (w *gzipResponseWriter) Write(b []byte) (int, error) { + return w.Writer.Write(b) +} + +func newGzipHandler(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { + next.ServeHTTP(w, r) + return + } + + w.Header().Set("Content-Encoding", "gzip") + + gz := gzPool.Get().(*gzip.Writer) + defer gzPool.Put(gz) + + gz.Reset(w) + defer gz.Close() + + next.ServeHTTP(&gzipResponseWriter{ResponseWriter: w, Writer: gz}, r) + }) +} + +// NewWebsocketUpgradeHandler returns a websocket handler that serves an incoming request only if it contains an upgrade +// request to the websocket protocol. If not, serves the the request with the http handler. +func NewWebsocketUpgradeHandler(h http.Handler, ws http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if isWebsocket(r) { + ws.ServeHTTP(w, r) + log.Debug("serving websocket request") + return + } + + h.ServeHTTP(w, r) + }) +} + +// isWebsocket checks the header of an http request for a websocket upgrade request. +func isWebsocket(r *http.Request) bool { + return strings.ToLower(r.Header.Get("Upgrade")) == "websocket" && + strings.ToLower(r.Header.Get("Connection")) == "upgrade" +} diff --git a/node/rpcstack_test.go b/node/rpcstack_test.go new file mode 100644 index 000000000000..28bb786858a1 --- /dev/null +++ b/node/rpcstack_test.go @@ -0,0 +1,38 @@ +package node + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/XinFinOrg/XDPoSChain/rpc" + "github.com/stretchr/testify/assert" +) + +func TestNewWebsocketUpgradeHandler_websocket(t *testing.T) { + srv := rpc.NewServer() + + handler := NewWebsocketUpgradeHandler(nil, srv.WebsocketHandler([]string{})) + ts := httptest.NewServer(handler) + defer ts.Close() + + responses := make(chan *http.Response) + go func(responses chan *http.Response) { + client := &http.Client{} + + req, _ := http.NewRequest(http.MethodGet, ts.URL, nil) + req.Header.Set("Connection", "upgrade") + req.Header.Set("Upgrade", "websocket") + req.Header.Set("Sec-WebSocket-Version", "13") + req.Header.Set("Sec-Websocket-Key", "SGVsbG8sIHdvcmxkIQ==") + + resp, err := client.Do(req) + if err != nil { + t.Error("could not issue a GET request to the test http server", err) + } + responses <- resp + }(responses) + + response := <-responses + assert.Equal(t, "websocket", response.Header.Get("Upgrade")) +} diff --git a/rpc/endpoints.go b/rpc/endpoints.go index eb14e99f6fa3..0b543102fc0e 100644 --- a/rpc/endpoints.go +++ b/rpc/endpoints.go @@ -23,89 +23,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/log" ) -// checkModuleAvailability checks that all names given in modules are actually -// available API services. It assumes that the MetadataApi module ("rpc") is always available; -// the registration of this "rpc" module happens in NewServer() and is thus common to all endpoints. -func checkModuleAvailability(modules []string, apis []API) (bad, available []string) { - availableSet := make(map[string]struct{}) - for _, api := range apis { - if _, ok := availableSet[api.Namespace]; !ok { - availableSet[api.Namespace] = struct{}{} - available = append(available, api.Namespace) - } - } - for _, name := range modules { - if _, ok := availableSet[name]; !ok && name != MetadataApi { - bad = append(bad, name) - } - } - return bad, available -} - -// StartHTTPEndpoint starts the HTTP RPC endpoint, configured with cors/vhosts/modules. -func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []string, vhosts []string, timeouts HTTPTimeouts) (net.Listener, *Server, error) { - if bad, available := checkModuleAvailability(modules, apis); len(bad) > 0 { - log.Error("Unavailable modules in HTTP API list", "unavailable", bad, "available", available) - } - // Generate the whitelist based on the allowed modules - whitelist := make(map[string]bool) - for _, module := range modules { - whitelist[module] = true - } - // Register all the APIs exposed by the services - handler := NewServer() - for _, api := range apis { - if whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { - if err := handler.RegisterName(api.Namespace, api.Service); err != nil { - return nil, nil, err - } - log.Debug("HTTP registered", "namespace", api.Namespace) - } - } - // All APIs registered, start the HTTP listener - var ( - listener net.Listener - err error - ) - if listener, err = net.Listen("tcp", endpoint); err != nil { - return nil, nil, err - } - go NewHTTPServer(cors, vhosts, timeouts, handler).Serve(listener) - return listener, handler, err -} - -// StartWSEndpoint starts a websocket endpoint. -func StartWSEndpoint(endpoint string, apis []API, modules []string, wsOrigins []string, exposeAll bool) (net.Listener, *Server, error) { - if bad, available := checkModuleAvailability(modules, apis); len(bad) > 0 { - log.Error("Unavailable modules in WS API list", "unavailable", bad, "available", available) - } - // Generate the whitelist based on the allowed modules - whitelist := make(map[string]bool) - for _, module := range modules { - whitelist[module] = true - } - // Register all the APIs exposed by the services - handler := NewServer() - for _, api := range apis { - if exposeAll || whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { - if err := handler.RegisterName(api.Namespace, api.Service); err != nil { - return nil, nil, err - } - log.Debug("WebSocket registered", "service", api.Service, "namespace", api.Namespace) - } - } - // All APIs registered, start the HTTP listener - var ( - listener net.Listener - err error - ) - if listener, err = net.Listen("tcp", endpoint); err != nil { - return nil, nil, err - } - go NewWSServer(wsOrigins, handler).Serve(listener) - return listener, handler, err -} - // StartIPCEndpoint starts an IPC endpoint. func StartIPCEndpoint(ipcEndpoint string, apis []API) (net.Listener, *Server, error) { // Register all the APIs exposed by the services. @@ -119,7 +36,7 @@ func StartIPCEndpoint(ipcEndpoint string, apis []API) (net.Listener, *Server, er log.Info("IPC registration failed", "namespace", api.Namespace, "error", err) return nil, nil, err } - log.Debug("IPC registered", "service", api.Service, "namespace", api.Namespace) + log.Debug("IPC registered", "namespace", api.Namespace) if _, ok := regMap[api.Namespace]; !ok { registered = append(registered, api.Namespace) regMap[api.Namespace] = struct{}{} diff --git a/rpc/http.go b/rpc/http.go index 2416d66753b4..ee23c79a1a95 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -24,15 +24,10 @@ import ( "fmt" "io" "mime" - "net" "net/http" "net/url" - "strings" "sync" "time" - - "github.com/XinFinOrg/XDPoSChain/log" - "github.com/rs/cors" ) const ( @@ -237,41 +232,6 @@ func (t *httpServerConn) RemoteAddr() string { // SetWriteDeadline does nothing and always returns nil. func (t *httpServerConn) SetWriteDeadline(time.Time) error { return nil } -// NewHTTPServer creates a new HTTP RPC server around an API provider. -// -// Deprecated: Server implements http.Handler -func NewHTTPServer(cors []string, vhosts []string, timeouts HTTPTimeouts, srv *Server) *http.Server { - // Wrap the CORS-handler within a host-handler - handler := newCorsHandler(srv, cors) - handler = newVHostHandler(vhosts, handler) - - // Make sure timeout values are meaningful - if timeouts.ReadTimeout < time.Second { - log.Warn("Sanitizing invalid HTTP read timeout", "provided", timeouts.ReadTimeout, "updated", DefaultHTTPTimeouts.ReadTimeout) - timeouts.ReadTimeout = DefaultHTTPTimeouts.ReadTimeout - } - if timeouts.WriteTimeout < time.Second { - log.Warn("Sanitizing invalid HTTP write timeout", "provided", timeouts.WriteTimeout, "updated", DefaultHTTPTimeouts.WriteTimeout) - timeouts.WriteTimeout = DefaultHTTPTimeouts.WriteTimeout - } - if timeouts.IdleTimeout < time.Second { - log.Warn("Sanitizing invalid HTTP idle timeout", "provided", timeouts.IdleTimeout, "updated", DefaultHTTPTimeouts.IdleTimeout) - timeouts.IdleTimeout = DefaultHTTPTimeouts.IdleTimeout - } - - // PR #469: return http code 503 and error message to client when timeout - handler = http.TimeoutHandler(handler, timeouts.WriteTimeout, `{"error":"http server timeout"}`) - log.Info("NewHTTPServer", "writeTimeout", timeouts.WriteTimeout) - - // Bundle and start the HTTP server - return &http.Server{ - Handler: handler, - ReadTimeout: timeouts.ReadTimeout, - WriteTimeout: timeouts.WriteTimeout + time.Second, - IdleTimeout: timeouts.IdleTimeout, - } -} - // ServeHTTP serves JSON-RPC requests over HTTP. func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Permit dumb empty requests for remote health-checks (AWS) @@ -328,64 +288,3 @@ func validateRequest(r *http.Request) (int, error) { err := fmt.Errorf("invalid content type, only %s is supported", contentType) return http.StatusUnsupportedMediaType, err } - -func newCorsHandler(srv *Server, allowedOrigins []string) http.Handler { - // disable CORS support if user has not specified a custom CORS configuration - if len(allowedOrigins) == 0 { - return srv - } - c := cors.New(cors.Options{ - AllowedOrigins: allowedOrigins, - AllowedMethods: []string{http.MethodPost, http.MethodGet}, - MaxAge: 600, - AllowedHeaders: []string{"*"}, - }) - return c.Handler(srv) -} - -// virtualHostHandler is a handler which validates the Host-header of incoming requests. -// The virtualHostHandler can prevent DNS rebinding attacks, which do not utilize CORS-headers, -// since they do in-domain requests against the RPC api. Instead, we can see on the Host-header -// which domain was used, and validate that against a whitelist. -type virtualHostHandler struct { - vhosts map[string]struct{} - next http.Handler -} - -func newVHostHandler(vhosts []string, next http.Handler) http.Handler { - vhostMap := make(map[string]struct{}) - for _, allowedHost := range vhosts { - vhostMap[strings.ToLower(allowedHost)] = struct{}{} - } - return &virtualHostHandler{vhostMap, next} -} - -// ServeHTTP serves JSON-RPC requests over HTTP, implements http.Handler -func (h *virtualHostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - // if r.Host is not set, we can continue serving since a browser would set the Host header - if r.Host == "" { - h.next.ServeHTTP(w, r) - return - } - host, _, err := net.SplitHostPort(r.Host) - if err != nil { - // Either invalid (too many colons) or no port specified - host = r.Host - } - if ipAddr := net.ParseIP(host); ipAddr != nil { - // It's an IP address, we can serve that - h.next.ServeHTTP(w, r) - return - - } - // Not an ip address, but a hostname. Need to validate - if _, exist := h.vhosts["*"]; exist { - h.next.ServeHTTP(w, r) - return - } - if _, exist := h.vhosts[host]; exist { - h.next.ServeHTTP(w, r) - return - } - http.Error(w, "invalid host specified", http.StatusForbidden) -} diff --git a/rpc/websocket.go b/rpc/websocket.go index fdc1d05e9fd1..7c7cae455473 100644 --- a/rpc/websocket.go +++ b/rpc/websocket.go @@ -64,13 +64,6 @@ func (s *Server) WebsocketHandler(allowedOrigins []string) http.Handler { }) } -// NewWSServer creates a new websocket RPC server around an API provider. -// -// Deprecated: use Server.WebsocketHandler -func NewWSServer(allowedOrigins []string, srv *Server) *http.Server { - return &http.Server{Handler: srv.WebsocketHandler(allowedOrigins)} -} - // wsHandshakeValidator returns a handler that verifies the origin during the // websocket upgrade process. When a '*' is specified as an allowed origins all // connections are accepted. From ba23964062a4fb9412dfd5d7f0e9e9c475b63bb1 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 8 Nov 2024 17:24:31 +0800 Subject: [PATCH 190/479] node: shutdown HTTP and WebSocket servers gracefully (#20956) --- node/endpoints.go | 12 ++++----- node/node.go | 66 +++++++++++++++++++++++++---------------------- 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/node/endpoints.go b/node/endpoints.go index 277a6644bfa3..234b49e73d71 100644 --- a/node/endpoints.go +++ b/node/endpoints.go @@ -26,14 +26,14 @@ import ( ) // StartHTTPEndpoint starts the HTTP RPC endpoint. -func StartHTTPEndpoint(endpoint string, timeouts rpc.HTTPTimeouts, handler http.Handler) (net.Listener, error) { +func StartHTTPEndpoint(endpoint string, timeouts rpc.HTTPTimeouts, handler http.Handler) (*http.Server, net.Addr, error) { // start the HTTP listener var ( listener net.Listener err error ) if listener, err = net.Listen("tcp", endpoint); err != nil { - return nil, err + return nil, nil, err } // Bundle and start the HTTP server httpSrv := &http.Server{ @@ -44,22 +44,22 @@ func StartHTTPEndpoint(endpoint string, timeouts rpc.HTTPTimeouts, handler http. } log.Info("StartHTTPEndpoint", "ReadTimeout", timeouts.ReadTimeout, "WriteTimeout", timeouts.WriteTimeout, "IdleTimeout", timeouts.IdleTimeout) go httpSrv.Serve(listener) - return listener, err + return httpSrv, listener.Addr(), err } // startWSEndpoint starts a websocket endpoint. -func startWSEndpoint(endpoint string, handler http.Handler) (net.Listener, error) { +func startWSEndpoint(endpoint string, handler http.Handler) (*http.Server, net.Addr, error) { // start the HTTP listener var ( listener net.Listener err error ) if listener, err = net.Listen("tcp", endpoint); err != nil { - return nil, err + return nil, nil, err } wsSrv := &http.Server{Handler: handler} go wsSrv.Serve(listener) - return listener, err + return wsSrv, listener.Addr(), err } // checkModuleAvailability checks that all names given in modules are actually diff --git a/node/node.go b/node/node.go index b53ab4a85548..dfa261640c18 100644 --- a/node/node.go +++ b/node/node.go @@ -17,9 +17,11 @@ package node import ( + "context" "errors" "fmt" "net" + "net/http" "os" "path/filepath" "reflect" @@ -60,14 +62,16 @@ type Node struct { ipcListener net.Listener // IPC RPC listener socket to serve API requests ipcHandler *rpc.Server // IPC RPC request handler to process the API requests - httpEndpoint string // HTTP endpoint (interface + port) to listen at (empty = HTTP disabled) - httpWhitelist []string // HTTP RPC modules to allow through this endpoint - httpListener net.Listener // HTTP RPC listener socket to server API requests - httpHandler *rpc.Server // HTTP RPC request handler to process the API requests + httpEndpoint string // HTTP endpoint (interface + port) to listen at (empty = HTTP disabled) + httpWhitelist []string // HTTP RPC modules to allow through this endpoint + httpListenerAddr net.Addr // Address of HTTP RPC listener socket serving API requests + httpServer *http.Server // HTTP RPC HTTP server + httpHandler *rpc.Server // HTTP RPC request handler to process the API requests - wsEndpoint string // Websocket endpoint (interface + port) to listen at (empty = websocket disabled) - wsListener net.Listener // Websocket RPC listener socket to server API requests - wsHandler *rpc.Server // Websocket RPC request handler to process the API requests + wsEndpoint string // WebSocket endpoint (interface + port) to listen at (empty = WebSocket disabled) + wsListenerAddr net.Addr // Address of WebSocket RPC listener socket serving API requests + wsHTTPServer *http.Server // WebSocket RPC HTTP server + wsHandler *rpc.Server // WebSocket RPC request handler to process the API requests stop chan struct{} // Channel to wait for termination notifications lock sync.RWMutex @@ -364,23 +368,24 @@ func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors return err } handler := NewHTTPHandlerStack(srv, cors, vhosts, &timeouts) - // wrap handler in websocket handler only if websocket port is the same as http rpc + // wrap handler in WebSocket handler only if WebSocket port is the same as http rpc if n.httpEndpoint == n.wsEndpoint { handler = NewWebsocketUpgradeHandler(handler, srv.WebsocketHandler(wsOrigins)) } - listener, err := StartHTTPEndpoint(endpoint, timeouts, handler) + httpServer, addr, err := StartHTTPEndpoint(endpoint, timeouts, handler) if err != nil { return err } - n.log.Info("HTTP endpoint opened", "url", fmt.Sprintf("http://%v/", listener.Addr()), + n.log.Info("HTTP endpoint opened", "url", fmt.Sprintf("http://%v/", addr), "cors", strings.Join(cors, ","), "vhosts", strings.Join(vhosts, ",")) if n.httpEndpoint == n.wsEndpoint { - n.log.Info("WebSocket endpoint opened", "url", fmt.Sprintf("ws://%v", listener.Addr())) + n.log.Info("WebSocket endpoint opened", "url", fmt.Sprintf("ws://%v", addr)) } // All listeners booted successfully n.httpEndpoint = endpoint - n.httpListener = listener + n.httpListenerAddr = addr + n.httpServer = httpServer n.httpHandler = srv return nil @@ -388,11 +393,10 @@ func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors // stopHTTP terminates the HTTP RPC endpoint. func (n *Node) stopHTTP() { - if n.httpListener != nil { - url := fmt.Sprintf("http://%v/", n.httpListener.Addr()) - n.httpListener.Close() - n.httpListener = nil - n.log.Info("HTTP endpoint closed", "url", url) + if n.httpServer != nil { + // Don't bother imposing a timeout here. + n.httpServer.Shutdown(context.Background()) + n.log.Info("HTTP endpoint closed", "url", fmt.Sprintf("http://%v/", n.httpListenerAddr)) } if n.httpHandler != nil { n.httpHandler.Stop() @@ -400,7 +404,7 @@ func (n *Node) stopHTTP() { } } -// startWS initializes and starts the websocket RPC endpoint. +// startWS initializes and starts the WebSocket RPC endpoint. func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrigins []string, exposeAll bool) error { // Short circuit if the WS endpoint isn't being exposed if endpoint == "" { @@ -413,26 +417,26 @@ func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrig if err != nil { return err } - listener, err := startWSEndpoint(endpoint, handler) + httpServer, addr, err := startWSEndpoint(endpoint, handler) if err != nil { return err } - n.log.Info("WebSocket endpoint opened", "url", fmt.Sprintf("ws://%s", listener.Addr())) + n.log.Info("WebSocket endpoint opened", "url", fmt.Sprintf("ws://%v", addr)) // All listeners booted successfully n.wsEndpoint = endpoint - n.wsListener = listener + n.wsListenerAddr = addr + n.wsHTTPServer = httpServer n.wsHandler = srv return nil } -// stopWS terminates the websocket RPC endpoint. +// stopWS terminates the WebSocket RPC endpoint. func (n *Node) stopWS() { - if n.wsListener != nil { - n.wsListener.Close() - n.wsListener = nil - - n.log.Info("WebSocket endpoint closed", "url", fmt.Sprintf("ws://%s", n.wsEndpoint)) + if n.wsHTTPServer != nil { + // Don't bother imposing a timeout here. + n.wsHTTPServer.Shutdown(context.Background()) + n.log.Info("WebSocket endpoint closed", "url", fmt.Sprintf("ws://%v", n.wsListenerAddr)) } if n.wsHandler != nil { n.wsHandler.Stop() @@ -599,8 +603,8 @@ func (n *Node) HTTPEndpoint() string { n.lock.Lock() defer n.lock.Unlock() - if n.httpListener != nil { - return n.httpListener.Addr().String() + if n.httpListenerAddr != nil { + return n.httpListenerAddr.String() } return n.httpEndpoint } @@ -610,8 +614,8 @@ func (n *Node) WSEndpoint() string { n.lock.Lock() defer n.lock.Unlock() - if n.wsListener != nil { - return n.wsListener.Addr().String() + if n.wsListenerAddr != nil { + return n.wsListenerAddr.String() } return n.wsEndpoint } From b1d6bec3bdace3d7c66c176c9ab1436b0f6ac068 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 8 Nov 2024 17:24:31 +0800 Subject: [PATCH 191/479] cmd, node: dump empty value config (#21296) --- cmd/utils/flags.go | 13 ++++++++----- node/config.go | 10 +++++----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index dbeadc9c180d..a4e7174859c1 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -768,12 +768,15 @@ func setNAT(ctx *cli.Context, cfg *p2p.Config) { // splitAndTrim splits input separated by a comma // and trims excessive white space from the substrings. -func splitAndTrim(input string) []string { - result := strings.Split(input, ",") - for i, r := range result { - result[i] = strings.TrimSpace(r) +func splitAndTrim(input string) (ret []string) { + l := strings.Split(input, ",") + for _, r := range l { + r = strings.TrimSpace(r) + if len(r) > 0 { + ret = append(ret, r) + } } - return result + return ret } // setHTTP creates the HTTP RPC listener interface string from the set diff --git a/node/config.go b/node/config.go index 8de6448acc5b..355f316a19c6 100644 --- a/node/config.go +++ b/node/config.go @@ -89,11 +89,11 @@ type Config struct { // a simple file name, it is placed inside the data directory (or on the root // pipe path on Windows), whereas if it's a resolvable path name (absolute or // relative), then that specific path is enforced. An empty path disables IPC. - IPCPath string `toml:",omitempty"` + IPCPath string // HTTPHost is the host interface on which to start the HTTP RPC server. If this // field is empty, no HTTP API endpoint will be started. - HTTPHost string `toml:",omitempty"` + HTTPHost string // HTTPPort is the TCP port number on which to start the HTTP RPC server. The // default zero value is/ valid and will pick a port number randomly (useful @@ -117,7 +117,7 @@ type Config struct { // HTTPModules is a list of API modules to expose via the HTTP RPC interface. // If the module list is empty, all RPC API endpoints designated public will be // exposed. - HTTPModules []string `toml:",omitempty"` + HTTPModules []string // HTTPTimeouts allows for customization of the timeout values used by the HTTP RPC // interface. @@ -125,7 +125,7 @@ type Config struct { // WSHost is the host interface on which to start the websocket RPC server. If // this field is empty, no websocket API endpoint will be started. - WSHost string `toml:",omitempty"` + WSHost string // WSPort is the TCP port number on which to start the websocket RPC server. The // default zero value is/ valid and will pick a port number randomly (useful for @@ -140,7 +140,7 @@ type Config struct { // WSModules is a list of API modules to expose via the websocket RPC interface. // If the module list is empty, all RPC API endpoints designated public will be // exposed. - WSModules []string `toml:",omitempty"` + WSModules []string // WSExposeAll exposes all API modules via the WebSocket RPC interface rather // than just the public ones. From 22ad2f57ef91824fdec5010aa08755a4a6cca562 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 8 Nov 2024 17:24:31 +0800 Subject: [PATCH 192/479] node: fix websocket connection header check (#21646) --- node/rpcstack.go | 2 +- node/rpcstack_test.go | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/node/rpcstack.go b/node/rpcstack.go index 0c6ecc4485ec..2de884f4c3b8 100644 --- a/node/rpcstack.go +++ b/node/rpcstack.go @@ -166,5 +166,5 @@ func NewWebsocketUpgradeHandler(h http.Handler, ws http.Handler) http.Handler { // isWebsocket checks the header of an http request for a websocket upgrade request. func isWebsocket(r *http.Request) bool { return strings.ToLower(r.Header.Get("Upgrade")) == "websocket" && - strings.ToLower(r.Header.Get("Connection")) == "upgrade" + strings.Contains(strings.ToLower(r.Header.Get("Connection")), "upgrade") } diff --git a/node/rpcstack_test.go b/node/rpcstack_test.go index 28bb786858a1..db08bd40cb43 100644 --- a/node/rpcstack_test.go +++ b/node/rpcstack_test.go @@ -36,3 +36,18 @@ func TestNewWebsocketUpgradeHandler_websocket(t *testing.T) { response := <-responses assert.Equal(t, "websocket", response.Header.Get("Upgrade")) } + +// TestIsWebsocket tests if an incoming websocket upgrade request is handled properly. +func TestIsWebsocket(t *testing.T) { + r, _ := http.NewRequest("GET", "/", nil) + + assert.False(t, isWebsocket(r)) + r.Header.Set("upgrade", "websocket") + assert.False(t, isWebsocket(r)) + r.Header.Set("connection", "upgrade") + assert.True(t, isWebsocket(r)) + r.Header.Set("connection", "upgrade,keep-alive") + assert.True(t, isWebsocket(r)) + r.Header.Set("connection", " UPGRADE,keep-alive") + assert.True(t, isWebsocket(r)) +} From 9a5fb2a9b8108d2f68ac8bf1383222dd28b48585 Mon Sep 17 00:00:00 2001 From: JukLee0ira Date: Wed, 13 Nov 2024 11:07:40 +0800 Subject: [PATCH 193/479] whisper: Remove v5 (#18432) --- whisper/whisperv5/api.go | 565 --------------- whisper/whisperv5/benchmarks_test.go | 206 ------ whisper/whisperv5/config.go | 27 - whisper/whisperv5/doc.go | 87 --- whisper/whisperv5/envelope.go | 246 ------- whisper/whisperv5/filter.go | 244 ------- whisper/whisperv5/filter_test.go | 848 ---------------------- whisper/whisperv5/gen_criteria_json.go | 64 -- whisper/whisperv5/gen_message_json.go | 82 --- whisper/whisperv5/gen_newmessage_json.go | 88 --- whisper/whisperv5/message.go | 352 ---------- whisper/whisperv5/message_test.go | 415 ----------- whisper/whisperv5/peer.go | 174 ----- whisper/whisperv5/peer_test.go | 313 --------- whisper/whisperv5/topic.go | 55 -- whisper/whisperv5/topic_test.go | 134 ---- whisper/whisperv5/whisper.go | 858 ----------------------- whisper/whisperv5/whisper_test.go | 851 ---------------------- 18 files changed, 5609 deletions(-) delete mode 100644 whisper/whisperv5/api.go delete mode 100644 whisper/whisperv5/benchmarks_test.go delete mode 100644 whisper/whisperv5/config.go delete mode 100644 whisper/whisperv5/doc.go delete mode 100644 whisper/whisperv5/envelope.go delete mode 100644 whisper/whisperv5/filter.go delete mode 100644 whisper/whisperv5/filter_test.go delete mode 100644 whisper/whisperv5/gen_criteria_json.go delete mode 100644 whisper/whisperv5/gen_message_json.go delete mode 100644 whisper/whisperv5/gen_newmessage_json.go delete mode 100644 whisper/whisperv5/message.go delete mode 100644 whisper/whisperv5/message_test.go delete mode 100644 whisper/whisperv5/peer.go delete mode 100644 whisper/whisperv5/peer_test.go delete mode 100644 whisper/whisperv5/topic.go delete mode 100644 whisper/whisperv5/topic_test.go delete mode 100644 whisper/whisperv5/whisper.go delete mode 100644 whisper/whisperv5/whisper_test.go diff --git a/whisper/whisperv5/api.go b/whisper/whisperv5/api.go deleted file mode 100644 index b28ea5075d5d..000000000000 --- a/whisper/whisperv5/api.go +++ /dev/null @@ -1,565 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -import ( - "context" - "crypto/ecdsa" - "errors" - "fmt" - "sync" - "time" - - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/common/hexutil" - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/log" - "github.com/XinFinOrg/XDPoSChain/p2p/discover" - "github.com/XinFinOrg/XDPoSChain/rpc" -) - -const ( - filterTimeout = 300 // filters are considered timeout out after filterTimeout seconds -) - -var ( - ErrSymAsym = errors.New("specify either a symmetric or an asymmetric key") - ErrInvalidSymmetricKey = errors.New("invalid symmetric key") - ErrInvalidPublicKey = errors.New("invalid public key") - ErrInvalidSigningPubKey = errors.New("invalid signing public key") - ErrTooLowPoW = errors.New("message rejected, PoW too low") - ErrNoTopics = errors.New("missing topic(s)") -) - -// PublicWhisperAPI provides the whisper RPC service that can be -// use publicly without security implications. -type PublicWhisperAPI struct { - w *Whisper - - mu sync.Mutex - lastUsed map[string]time.Time // keeps track when a filter was polled for the last time. -} - -// NewPublicWhisperAPI create a new RPC whisper service. -func NewPublicWhisperAPI(w *Whisper) *PublicWhisperAPI { - api := &PublicWhisperAPI{ - w: w, - lastUsed: make(map[string]time.Time), - } - return api -} - -// Version returns the Whisper sub-protocol version. -func (api *PublicWhisperAPI) Version(ctx context.Context) string { - return ProtocolVersionStr -} - -// Info contains diagnostic information. -type Info struct { - Memory int `json:"memory"` // Memory size of the floating messages in bytes. - Messages int `json:"messages"` // Number of floating messages. - MinPow float64 `json:"minPow"` // Minimal accepted PoW - MaxMessageSize uint32 `json:"maxMessageSize"` // Maximum accepted message size -} - -// Info returns diagnostic information about the whisper node. -func (api *PublicWhisperAPI) Info(ctx context.Context) Info { - stats := api.w.Stats() - return Info{ - Memory: stats.memoryUsed, - Messages: len(api.w.messageQueue) + len(api.w.p2pMsgQueue), - MinPow: api.w.MinPow(), - MaxMessageSize: api.w.MaxMessageSize(), - } -} - -// SetMaxMessageSize sets the maximum message size that is accepted. -// Upper limit is defined in whisperv5.MaxMessageSize. -func (api *PublicWhisperAPI) SetMaxMessageSize(ctx context.Context, size uint32) (bool, error) { - return true, api.w.SetMaxMessageSize(size) -} - -// SetMinPow sets the minimum PoW for a message before it is accepted. -func (api *PublicWhisperAPI) SetMinPoW(ctx context.Context, pow float64) (bool, error) { - return true, api.w.SetMinimumPoW(pow) -} - -// MarkTrustedPeer marks a peer trusted. , which will allow it to send historic (expired) messages. -// Note: This function is not adding new nodes, the node needs to exists as a peer. -func (api *PublicWhisperAPI) MarkTrustedPeer(ctx context.Context, enode string) (bool, error) { - n, err := discover.ParseNode(enode) - if err != nil { - return false, err - } - return true, api.w.AllowP2PMessagesFromPeer(n.ID[:]) -} - -// NewKeyPair generates a new public and private key pair for message decryption and encryption. -// It returns an ID that can be used to refer to the keypair. -func (api *PublicWhisperAPI) NewKeyPair(ctx context.Context) (string, error) { - return api.w.NewKeyPair() -} - -// AddPrivateKey imports the given private key. -func (api *PublicWhisperAPI) AddPrivateKey(ctx context.Context, privateKey hexutil.Bytes) (string, error) { - key, err := crypto.ToECDSA(privateKey) - if err != nil { - return "", err - } - return api.w.AddKeyPair(key) -} - -// DeleteKeyPair removes the key with the given key if it exists. -func (api *PublicWhisperAPI) DeleteKeyPair(ctx context.Context, key string) (bool, error) { - if ok := api.w.DeleteKeyPair(key); ok { - return true, nil - } - return false, fmt.Errorf("key pair %s not found", key) -} - -// HasKeyPair returns an indication if the node has a key pair that is associated with the given id. -func (api *PublicWhisperAPI) HasKeyPair(ctx context.Context, id string) bool { - return api.w.HasKeyPair(id) -} - -// GetPublicKey returns the public key associated with the given key. The key is the hex -// encoded representation of a key in the form specified in section 4.3.6 of ANSI X9.62. -func (api *PublicWhisperAPI) GetPublicKey(ctx context.Context, id string) (hexutil.Bytes, error) { - key, err := api.w.GetPrivateKey(id) - if err != nil { - return hexutil.Bytes{}, err - } - return crypto.FromECDSAPub(&key.PublicKey), nil -} - -// GetPublicKey returns the private key associated with the given key. The key is the hex -// encoded representation of a key in the form specified in section 4.3.6 of ANSI X9.62. -func (api *PublicWhisperAPI) GetPrivateKey(ctx context.Context, id string) (hexutil.Bytes, error) { - key, err := api.w.GetPrivateKey(id) - if err != nil { - return hexutil.Bytes{}, err - } - return crypto.FromECDSA(key), nil -} - -// NewSymKey generate a random symmetric key. -// It returns an ID that can be used to refer to the key. -// Can be used encrypting and decrypting messages where the key is known to both parties. -func (api *PublicWhisperAPI) NewSymKey(ctx context.Context) (string, error) { - return api.w.GenerateSymKey() -} - -// AddSymKey import a symmetric key. -// It returns an ID that can be used to refer to the key. -// Can be used encrypting and decrypting messages where the key is known to both parties. -func (api *PublicWhisperAPI) AddSymKey(ctx context.Context, key hexutil.Bytes) (string, error) { - return api.w.AddSymKeyDirect([]byte(key)) -} - -// GenerateSymKeyFromPassword derive a key from the given password, stores it, and returns its ID. -func (api *PublicWhisperAPI) GenerateSymKeyFromPassword(ctx context.Context, passwd string) (string, error) { - return api.w.AddSymKeyFromPassword(passwd) -} - -// HasSymKey returns an indication if the node has a symmetric key associated with the given key. -func (api *PublicWhisperAPI) HasSymKey(ctx context.Context, id string) bool { - return api.w.HasSymKey(id) -} - -// GetSymKey returns the symmetric key associated with the given id. -func (api *PublicWhisperAPI) GetSymKey(ctx context.Context, id string) (hexutil.Bytes, error) { - return api.w.GetSymKey(id) -} - -// DeleteSymKey deletes the symmetric key that is associated with the given id. -func (api *PublicWhisperAPI) DeleteSymKey(ctx context.Context, id string) bool { - return api.w.DeleteSymKey(id) -} - -//go:generate gencodec -type NewMessage -field-override newMessageOverride -out gen_newmessage_json.go - -// NewMessage represents a new whisper message that is posted through the RPC. -type NewMessage struct { - SymKeyID string `json:"symKeyID"` - PublicKey []byte `json:"pubKey"` - Sig string `json:"sig"` - TTL uint32 `json:"ttl"` - Topic TopicType `json:"topic"` - Payload []byte `json:"payload"` - Padding []byte `json:"padding"` - PowTime uint32 `json:"powTime"` - PowTarget float64 `json:"powTarget"` - TargetPeer string `json:"targetPeer"` -} - -type newMessageOverride struct { - PublicKey hexutil.Bytes - Payload hexutil.Bytes - Padding hexutil.Bytes -} - -// Post a message on the Whisper network. -func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (bool, error) { - var ( - symKeyGiven = len(req.SymKeyID) > 0 - pubKeyGiven = len(req.PublicKey) > 0 - err error - ) - - // user must specify either a symmetric or an asymmetric key - if (symKeyGiven && pubKeyGiven) || (!symKeyGiven && !pubKeyGiven) { - return false, ErrSymAsym - } - - params := &MessageParams{ - TTL: req.TTL, - Payload: req.Payload, - Padding: req.Padding, - WorkTime: req.PowTime, - PoW: req.PowTarget, - Topic: req.Topic, - } - - // Set key that is used to sign the message - if len(req.Sig) > 0 { - if params.Src, err = api.w.GetPrivateKey(req.Sig); err != nil { - return false, err - } - } - - // Set symmetric key that is used to encrypt the message - if symKeyGiven { - if params.Topic == (TopicType{}) { // topics are mandatory with symmetric encryption - return false, ErrNoTopics - } - if params.KeySym, err = api.w.GetSymKey(req.SymKeyID); err != nil { - return false, err - } - if !validateSymmetricKey(params.KeySym) { - return false, ErrInvalidSymmetricKey - } - } - - // Set asymmetric key that is used to encrypt the message - if pubKeyGiven { - if params.Dst, err = crypto.UnmarshalPubkey(req.PublicKey); err != nil { - return false, ErrInvalidPublicKey - } - } - - // encrypt and sent message - whisperMsg, err := NewSentMessage(params) - if err != nil { - return false, err - } - - env, err := whisperMsg.Wrap(params) - if err != nil { - return false, err - } - - // send to specific node (skip PoW check) - if len(req.TargetPeer) > 0 { - n, err := discover.ParseNode(req.TargetPeer) - if err != nil { - return false, fmt.Errorf("failed to parse target peer: %s", err) - } - return true, api.w.SendP2PMessage(n.ID[:], env) - } - - // ensure that the message PoW meets the node's minimum accepted PoW - if req.PowTarget < api.w.MinPow() { - return false, ErrTooLowPoW - } - - return true, api.w.Send(env) -} - -//go:generate gencodec -type Criteria -field-override criteriaOverride -out gen_criteria_json.go - -// Criteria holds various filter options for inbound messages. -type Criteria struct { - SymKeyID string `json:"symKeyID"` - PrivateKeyID string `json:"privateKeyID"` - Sig []byte `json:"sig"` - MinPow float64 `json:"minPow"` - Topics []TopicType `json:"topics"` - AllowP2P bool `json:"allowP2P"` -} - -type criteriaOverride struct { - Sig hexutil.Bytes -} - -// Messages set up a subscription that fires events when messages arrive that match -// the given set of criteria. -func (api *PublicWhisperAPI) Messages(ctx context.Context, crit Criteria) (*rpc.Subscription, error) { - var ( - symKeyGiven = len(crit.SymKeyID) > 0 - pubKeyGiven = len(crit.PrivateKeyID) > 0 - err error - ) - - // ensure that the RPC connection supports subscriptions - notifier, supported := rpc.NotifierFromContext(ctx) - if !supported { - return nil, rpc.ErrNotificationsUnsupported - } - - // user must specify either a symmetric or an asymmetric key - if (symKeyGiven && pubKeyGiven) || (!symKeyGiven && !pubKeyGiven) { - return nil, ErrSymAsym - } - - filter := Filter{ - PoW: crit.MinPow, - Messages: make(map[common.Hash]*ReceivedMessage), - AllowP2P: crit.AllowP2P, - } - - if len(crit.Sig) > 0 { - if filter.Src, err = crypto.UnmarshalPubkey(crit.Sig); err != nil { - return nil, ErrInvalidSigningPubKey - } - } - - for i, bt := range crit.Topics { - if len(bt) == 0 || len(bt) > 4 { - return nil, fmt.Errorf("subscribe: topic %d has wrong size: %d", i, len(bt)) - } - filter.Topics = append(filter.Topics, bt[:]) - } - - // listen for message that are encrypted with the given symmetric key - if symKeyGiven { - if len(filter.Topics) == 0 { - return nil, ErrNoTopics - } - key, err := api.w.GetSymKey(crit.SymKeyID) - if err != nil { - return nil, err - } - if !validateSymmetricKey(key) { - return nil, ErrInvalidSymmetricKey - } - filter.KeySym = key - filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym) - } - - // listen for messages that are encrypted with the given public key - if pubKeyGiven { - filter.KeyAsym, err = api.w.GetPrivateKey(crit.PrivateKeyID) - if err != nil || filter.KeyAsym == nil { - return nil, ErrInvalidPublicKey - } - } - - id, err := api.w.Subscribe(&filter) - if err != nil { - return nil, err - } - - // create subscription and start waiting for message events - rpcSub := notifier.CreateSubscription() - go func() { - // for now poll internally, refactor whisper internal for channel support - ticker := time.NewTicker(250 * time.Millisecond) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - if filter := api.w.GetFilter(id); filter != nil { - for _, rpcMessage := range toMessage(filter.Retrieve()) { - if err := notifier.Notify(rpcSub.ID, rpcMessage); err != nil { - log.Error("Failed to send notification", "err", err) - } - } - } - case <-rpcSub.Err(): - api.w.Unsubscribe(id) - return - case <-notifier.Closed(): - api.w.Unsubscribe(id) - return - } - } - }() - - return rpcSub, nil -} - -//go:generate gencodec -type Message -field-override messageOverride -out gen_message_json.go - -// Message is the RPC representation of a whisper message. -type Message struct { - Sig []byte `json:"sig,omitempty"` - TTL uint32 `json:"ttl"` - Timestamp uint32 `json:"timestamp"` - Topic TopicType `json:"topic"` - Payload []byte `json:"payload"` - Padding []byte `json:"padding"` - PoW float64 `json:"pow"` - Hash []byte `json:"hash"` - Dst []byte `json:"recipientPublicKey,omitempty"` -} - -type messageOverride struct { - Sig hexutil.Bytes - Payload hexutil.Bytes - Padding hexutil.Bytes - Hash hexutil.Bytes - Dst hexutil.Bytes -} - -// ToWhisperMessage converts an internal message into an API version. -func ToWhisperMessage(message *ReceivedMessage) *Message { - msg := Message{ - Payload: message.Payload, - Padding: message.Padding, - Timestamp: message.Sent, - TTL: message.TTL, - PoW: message.PoW, - Hash: message.EnvelopeHash.Bytes(), - Topic: message.Topic, - } - - if message.Dst != nil { - b := crypto.FromECDSAPub(message.Dst) - if b != nil { - msg.Dst = b - } - } - - if isMessageSigned(message.Raw[0]) { - b := crypto.FromECDSAPub(message.SigToPubKey()) - if b != nil { - msg.Sig = b - } - } - - return &msg -} - -// toMessage converts a set of messages to its RPC representation. -func toMessage(messages []*ReceivedMessage) []*Message { - msgs := make([]*Message, len(messages)) - for i, msg := range messages { - msgs[i] = ToWhisperMessage(msg) - } - return msgs -} - -// GetFilterMessages returns the messages that match the filter criteria and -// are received between the last poll and now. -func (api *PublicWhisperAPI) GetFilterMessages(id string) ([]*Message, error) { - api.mu.Lock() - f := api.w.GetFilter(id) - if f == nil { - api.mu.Unlock() - return nil, errors.New("filter not found") - } - api.lastUsed[id] = time.Now() - api.mu.Unlock() - - receivedMessages := f.Retrieve() - messages := make([]*Message, 0, len(receivedMessages)) - for _, msg := range receivedMessages { - messages = append(messages, ToWhisperMessage(msg)) - } - - return messages, nil -} - -// DeleteMessageFilter deletes a filter. -func (api *PublicWhisperAPI) DeleteMessageFilter(id string) (bool, error) { - api.mu.Lock() - defer api.mu.Unlock() - - delete(api.lastUsed, id) - return true, api.w.Unsubscribe(id) -} - -// NewMessageFilter creates a new filter that can be used to poll for -// (new) messages that satisfy the given criteria. -func (api *PublicWhisperAPI) NewMessageFilter(req Criteria) (string, error) { - var ( - src *ecdsa.PublicKey - keySym []byte - keyAsym *ecdsa.PrivateKey - topics [][]byte - - symKeyGiven = len(req.SymKeyID) > 0 - asymKeyGiven = len(req.PrivateKeyID) > 0 - - err error - ) - - // user must specify either a symmetric or an asymmetric key - if (symKeyGiven && asymKeyGiven) || (!symKeyGiven && !asymKeyGiven) { - return "", ErrSymAsym - } - - if len(req.Sig) > 0 { - if src, err = crypto.UnmarshalPubkey(req.Sig); err != nil { - return "", ErrInvalidSigningPubKey - } - } - - if symKeyGiven { - if keySym, err = api.w.GetSymKey(req.SymKeyID); err != nil { - return "", err - } - if !validateSymmetricKey(keySym) { - return "", ErrInvalidSymmetricKey - } - } - - if asymKeyGiven { - if keyAsym, err = api.w.GetPrivateKey(req.PrivateKeyID); err != nil { - return "", err - } - } - - if len(req.Topics) > 0 { - topics = make([][]byte, 0, len(req.Topics)) - for _, topic := range req.Topics { - topics = append(topics, topic[:]) - } - } - - f := &Filter{ - Src: src, - KeySym: keySym, - KeyAsym: keyAsym, - PoW: req.MinPow, - AllowP2P: req.AllowP2P, - Topics: topics, - Messages: make(map[common.Hash]*ReceivedMessage), - } - - id, err := api.w.Subscribe(f) - if err != nil { - return "", err - } - - api.mu.Lock() - api.lastUsed[id] = time.Now() - api.mu.Unlock() - - return id, nil -} diff --git a/whisper/whisperv5/benchmarks_test.go b/whisper/whisperv5/benchmarks_test.go deleted file mode 100644 index 12d8cb657e60..000000000000 --- a/whisper/whisperv5/benchmarks_test.go +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -import ( - "testing" - - "github.com/XinFinOrg/XDPoSChain/crypto" -) - -func BenchmarkDeriveKeyMaterial(b *testing.B) { - for i := 0; i < b.N; i++ { - deriveKeyMaterial([]byte("test"), 0) - } -} - -func BenchmarkEncryptionSym(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - for i := 0; i < b.N; i++ { - msg, _ := NewSentMessage(params) - _, err := msg.Wrap(params) - if err != nil { - b.Errorf("failed Wrap with seed %d: %s.", seed, err) - b.Errorf("i = %d, len(msg.Raw) = %d, params.Payload = %d.", i, len(msg.Raw), len(params.Payload)) - return - } - } -} - -func BenchmarkEncryptionAsym(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - key, err := crypto.GenerateKey() - if err != nil { - b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - params.KeySym = nil - params.Dst = &key.PublicKey - - for i := 0; i < b.N; i++ { - msg, _ := NewSentMessage(params) - _, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - } -} - -func BenchmarkDecryptionSymValid(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, _ := NewSentMessage(params) - env, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - f := Filter{KeySym: params.KeySym} - - for i := 0; i < b.N; i++ { - msg := env.Open(&f) - if msg == nil { - b.Fatalf("failed to open with seed %d.", seed) - } - } -} - -func BenchmarkDecryptionSymInvalid(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, _ := NewSentMessage(params) - env, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - f := Filter{KeySym: []byte("arbitrary stuff here")} - - for i := 0; i < b.N; i++ { - msg := env.Open(&f) - if msg != nil { - b.Fatalf("opened envelope with invalid key, seed: %d.", seed) - } - } -} - -func BenchmarkDecryptionAsymValid(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - key, err := crypto.GenerateKey() - if err != nil { - b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - f := Filter{KeyAsym: key} - params.KeySym = nil - params.Dst = &key.PublicKey - msg, _ := NewSentMessage(params) - env, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - for i := 0; i < b.N; i++ { - msg := env.Open(&f) - if msg == nil { - b.Fatalf("fail to open, seed: %d.", seed) - } - } -} - -func BenchmarkDecryptionAsymInvalid(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - key, err := crypto.GenerateKey() - if err != nil { - b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - params.KeySym = nil - params.Dst = &key.PublicKey - msg, _ := NewSentMessage(params) - env, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - key, err = crypto.GenerateKey() - if err != nil { - b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - f := Filter{KeyAsym: key} - - for i := 0; i < b.N; i++ { - msg := env.Open(&f) - if msg != nil { - b.Fatalf("opened envelope with invalid key, seed: %d.", seed) - } - } -} - -func increment(x []byte) { - for i := 0; i < len(x); i++ { - x[i]++ - if x[i] != 0 { - break - } - } -} - -func BenchmarkPoW(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - params.Payload = make([]byte, 32) - params.PoW = 10.0 - params.TTL = 1 - - for i := 0; i < b.N; i++ { - increment(params.Payload) - msg, _ := NewSentMessage(params) - _, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - } -} diff --git a/whisper/whisperv5/config.go b/whisper/whisperv5/config.go deleted file mode 100644 index fcc2307046aa..000000000000 --- a/whisper/whisperv5/config.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -type Config struct { - MaxMessageSize uint32 `toml:",omitempty"` - MinimumAcceptedPOW float64 `toml:",omitempty"` -} - -var DefaultConfig = Config{ - MaxMessageSize: DefaultMaxMessageSize, - MinimumAcceptedPOW: DefaultMinimumPoW, -} diff --git a/whisper/whisperv5/doc.go b/whisper/whisperv5/doc.go deleted file mode 100644 index 7a57488bd7a3..000000000000 --- a/whisper/whisperv5/doc.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -/* -Package whisper implements the Whisper protocol (version 5). - -Whisper combines aspects of both DHTs and datagram messaging systems (e.g. UDP). -As such it may be likened and compared to both, not dissimilar to the -matter/energy duality (apologies to physicists for the blatant abuse of a -fundamental and beautiful natural principle). - -Whisper is a pure identity-based messaging system. Whisper provides a low-level -(non-application-specific) but easily-accessible API without being based upon -or prejudiced by the low-level hardware attributes and characteristics, -particularly the notion of singular endpoints. -*/ -package whisperv5 - -import ( - "fmt" - "time" -) - -const ( - EnvelopeVersion = uint64(0) - ProtocolVersion = uint64(5) - ProtocolVersionStr = "5.0" - ProtocolName = "shh" - - statusCode = 0 // used by whisper protocol - messagesCode = 1 // normal whisper message - p2pCode = 2 // peer-to-peer message (to be consumed by the peer, but not forwarded any further) - p2pRequestCode = 3 // peer-to-peer message, used by Dapp protocol - NumberOfMessageCodes = 64 - - paddingMask = byte(3) - signatureFlag = byte(4) - - TopicLength = 4 - signatureLength = 65 - aesKeyLength = 32 - AESNonceLength = 12 - keyIdSize = 32 - - MaxMessageSize = uint32(10 * 1024 * 1024) // maximum accepted size of a message. - DefaultMaxMessageSize = uint32(1024 * 1024) - DefaultMinimumPoW = 0.2 - - padSizeLimit = 256 // just an arbitrary number, could be changed without breaking the protocol (must not exceed 2^24) - messageQueueLimit = 1024 - - expirationCycle = time.Second - transmissionCycle = 300 * time.Millisecond - - DefaultTTL = 50 // seconds - SynchAllowance = 10 // seconds -) - -type unknownVersionError uint64 - -func (e unknownVersionError) Error() string { - return fmt.Sprintf("invalid envelope version %d", uint64(e)) -} - -// MailServer represents a mail server, capable of -// archiving the old messages for subsequent delivery -// to the peers. Any implementation must ensure that both -// functions are thread-safe. Also, they must return ASAP. -// DeliverMail should use directMessagesCode for delivery, -// in order to bypass the expiry checks. -type MailServer interface { - Archive(env *Envelope) - DeliverMail(whisperPeer *Peer, request *Envelope) -} diff --git a/whisper/whisperv5/envelope.go b/whisper/whisperv5/envelope.go deleted file mode 100644 index 588d9b85a45e..000000000000 --- a/whisper/whisperv5/envelope.go +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains the Whisper protocol Envelope element. - -package whisperv5 - -import ( - "crypto/ecdsa" - "encoding/binary" - "fmt" - gmath "math" - "math/big" - "time" - - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/common/math" - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/ecies" - "github.com/XinFinOrg/XDPoSChain/rlp" -) - -// Envelope represents a clear-text data packet to transmit through the Whisper -// network. Its contents may or may not be encrypted and signed. -type Envelope struct { - Version []byte - Expiry uint32 - TTL uint32 - Topic TopicType - AESNonce []byte - Data []byte - EnvNonce uint64 - - pow float64 // Message-specific PoW as described in the Whisper specification. - hash common.Hash // Cached hash of the envelope to avoid rehashing every time. - // Don't access hash directly, use Hash() function instead. -} - -// size returns the size of envelope as it is sent (i.e. public fields only) -func (e *Envelope) size() int { - return 20 + len(e.Version) + len(e.AESNonce) + len(e.Data) -} - -// rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce. -func (e *Envelope) rlpWithoutNonce() []byte { - res, _ := rlp.EncodeToBytes([]interface{}{e.Version, e.Expiry, e.TTL, e.Topic, e.AESNonce, e.Data}) - return res -} - -// NewEnvelope wraps a Whisper message with expiration and destination data -// included into an envelope for network forwarding. -func NewEnvelope(ttl uint32, topic TopicType, aesNonce []byte, msg *sentMessage) *Envelope { - env := Envelope{ - Version: make([]byte, 1), - Expiry: uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()), - TTL: ttl, - Topic: topic, - AESNonce: aesNonce, - Data: msg.Raw, - EnvNonce: 0, - } - - if EnvelopeVersion < 256 { - env.Version[0] = byte(EnvelopeVersion) - } else { - panic("please increase the size of Envelope.Version before releasing this version") - } - - return &env -} - -func (e *Envelope) IsSymmetric() bool { - return len(e.AESNonce) > 0 -} - -func (e *Envelope) isAsymmetric() bool { - return !e.IsSymmetric() -} - -func (e *Envelope) Ver() uint64 { - return bytesToUintLittleEndian(e.Version) -} - -// Seal closes the envelope by spending the requested amount of time as a proof -// of work on hashing the data. -func (e *Envelope) Seal(options *MessageParams) error { - var target, bestBit int - if options.PoW == 0 { - // adjust for the duration of Seal() execution only if execution time is predefined unconditionally - e.Expiry += options.WorkTime - } else { - target = e.powToFirstBit(options.PoW) - if target < 1 { - target = 1 - } - } - - buf := make([]byte, 64) - h := crypto.Keccak256(e.rlpWithoutNonce()) - copy(buf[:32], h) - - finish := time.Now().Add(time.Duration(options.WorkTime) * time.Second).UnixNano() - for nonce := uint64(0); time.Now().UnixNano() < finish; { - for i := 0; i < 1024; i++ { - binary.BigEndian.PutUint64(buf[56:], nonce) - d := new(big.Int).SetBytes(crypto.Keccak256(buf)) - firstBit := math.FirstBitSet(d) - if firstBit > bestBit { - e.EnvNonce, bestBit = nonce, firstBit - if target > 0 && bestBit >= target { - return nil - } - } - nonce++ - } - } - - if target > 0 && bestBit < target { - return fmt.Errorf("failed to reach the PoW target, specified pow time (%d seconds) was insufficient", options.WorkTime) - } - - return nil -} - -func (e *Envelope) PoW() float64 { - if e.pow == 0 { - e.calculatePoW(0) - } - return e.pow -} - -func (e *Envelope) calculatePoW(diff uint32) { - buf := make([]byte, 64) - h := crypto.Keccak256(e.rlpWithoutNonce()) - copy(buf[:32], h) - binary.BigEndian.PutUint64(buf[56:], e.EnvNonce) - d := new(big.Int).SetBytes(crypto.Keccak256(buf)) - firstBit := math.FirstBitSet(d) - x := gmath.Pow(2, float64(firstBit)) - x /= float64(e.size()) - x /= float64(e.TTL + diff) - e.pow = x -} - -func (e *Envelope) powToFirstBit(pow float64) int { - x := pow - x *= float64(e.size()) - x *= float64(e.TTL) - bits := gmath.Log2(x) - bits = gmath.Ceil(bits) - return int(bits) -} - -// Hash returns the SHA3 hash of the envelope, calculating it if not yet done. -func (e *Envelope) Hash() common.Hash { - if (e.hash == common.Hash{}) { - encoded, _ := rlp.EncodeToBytes(e) - e.hash = crypto.Keccak256Hash(encoded) - } - return e.hash -} - -// DecodeRLP decodes an Envelope from an RLP data stream. -func (e *Envelope) DecodeRLP(s *rlp.Stream) error { - raw, err := s.Raw() - if err != nil { - return err - } - // The decoding of Envelope uses the struct fields but also needs - // to compute the hash of the whole RLP-encoded envelope. This - // type has the same structure as Envelope but is not an - // rlp.Decoder (does not implement DecodeRLP function). - // Only public members will be encoded. - type rlpenv Envelope - if err := rlp.DecodeBytes(raw, (*rlpenv)(e)); err != nil { - return err - } - e.hash = crypto.Keccak256Hash(raw) - return nil -} - -// OpenAsymmetric tries to decrypt an envelope, potentially encrypted with a particular key. -func (e *Envelope) OpenAsymmetric(key *ecdsa.PrivateKey) (*ReceivedMessage, error) { - message := &ReceivedMessage{Raw: e.Data} - err := message.decryptAsymmetric(key) - switch err { - case nil: - return message, nil - case ecies.ErrInvalidPublicKey: // addressed to somebody else - return nil, err - default: - return nil, fmt.Errorf("unable to open envelope, decrypt failed: %v", err) - } -} - -// OpenSymmetric tries to decrypt an envelope, potentially encrypted with a particular key. -func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) { - msg = &ReceivedMessage{Raw: e.Data} - err = msg.decryptSymmetric(key, e.AESNonce) - if err != nil { - msg = nil - } - return msg, err -} - -// Open tries to decrypt an envelope, and populates the message fields in case of success. -func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) { - if e.isAsymmetric() { - msg, _ = e.OpenAsymmetric(watcher.KeyAsym) - if msg != nil { - msg.Dst = &watcher.KeyAsym.PublicKey - } - } else if e.IsSymmetric() { - msg, _ = e.OpenSymmetric(watcher.KeySym) - if msg != nil { - msg.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym) - } - } - - if msg != nil { - ok := msg.Validate() - if !ok { - return nil - } - msg.Topic = e.Topic - msg.PoW = e.PoW() - msg.TTL = e.TTL - msg.Sent = e.Expiry - e.TTL - msg.EnvelopeHash = e.Hash() - msg.EnvelopeVersion = e.Ver() - } - return msg -} diff --git a/whisper/whisperv5/filter.go b/whisper/whisperv5/filter.go deleted file mode 100644 index 5c64d46910d1..000000000000 --- a/whisper/whisperv5/filter.go +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -import ( - "crypto/ecdsa" - "errors" - "fmt" - "sync" - - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/log" -) - -type Filter struct { - Src *ecdsa.PublicKey // Sender of the message - KeyAsym *ecdsa.PrivateKey // Private Key of recipient - KeySym []byte // Key associated with the Topic - Topics [][]byte // Topics to filter messages with - PoW float64 // Proof of work as described in the Whisper spec - AllowP2P bool // Indicates whether this filter is interested in direct peer-to-peer messages - SymKeyHash common.Hash // The Keccak256Hash of the symmetric key, needed for optimization - - Messages map[common.Hash]*ReceivedMessage - mutex sync.RWMutex -} - -type Filters struct { - watchers map[string]*Filter - whisper *Whisper - mutex sync.RWMutex -} - -func NewFilters(w *Whisper) *Filters { - return &Filters{ - watchers: make(map[string]*Filter), - whisper: w, - } -} - -func (fs *Filters) Install(watcher *Filter) (string, error) { - if watcher.Messages == nil { - watcher.Messages = make(map[common.Hash]*ReceivedMessage) - } - - id, err := GenerateRandomID() - if err != nil { - return "", err - } - - fs.mutex.Lock() - defer fs.mutex.Unlock() - - if fs.watchers[id] != nil { - return "", errors.New("failed to generate unique ID") - } - - if watcher.expectsSymmetricEncryption() { - watcher.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym) - } - - fs.watchers[id] = watcher - return id, err -} - -func (fs *Filters) Uninstall(id string) bool { - fs.mutex.Lock() - defer fs.mutex.Unlock() - if fs.watchers[id] != nil { - delete(fs.watchers, id) - return true - } - return false -} - -func (fs *Filters) Get(id string) *Filter { - fs.mutex.RLock() - defer fs.mutex.RUnlock() - return fs.watchers[id] -} - -func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) { - var msg *ReceivedMessage - - fs.mutex.RLock() - defer fs.mutex.RUnlock() - - i := -1 // only used for logging info - for _, watcher := range fs.watchers { - i++ - if p2pMessage && !watcher.AllowP2P { - log.Trace(fmt.Sprintf("msg [%x], filter [%d]: p2p messages are not allowed", env.Hash(), i)) - continue - } - - var match bool - if msg != nil { - match = watcher.MatchMessage(msg) - } else { - match = watcher.MatchEnvelope(env) - if match { - msg = env.Open(watcher) - if msg == nil { - log.Trace("processing message: failed to open", "message", env.Hash().Hex(), "filter", i) - } - } else { - log.Trace("processing message: does not match", "message", env.Hash().Hex(), "filter", i) - } - } - - if match && msg != nil { - log.Trace("processing message: decrypted", "hash", env.Hash().Hex()) - if watcher.Src == nil || IsPubKeyEqual(msg.Src, watcher.Src) { - watcher.Trigger(msg) - } - } - } -} - -func (f *Filter) processEnvelope(env *Envelope) *ReceivedMessage { - if f.MatchEnvelope(env) { - msg := env.Open(f) - if msg != nil { - return msg - } else { - log.Trace("processing envelope: failed to open", "hash", env.Hash().Hex()) - } - } else { - log.Trace("processing envelope: does not match", "hash", env.Hash().Hex()) - } - return nil -} - -func (f *Filter) expectsAsymmetricEncryption() bool { - return f.KeyAsym != nil -} - -func (f *Filter) expectsSymmetricEncryption() bool { - return f.KeySym != nil -} - -func (f *Filter) Trigger(msg *ReceivedMessage) { - f.mutex.Lock() - defer f.mutex.Unlock() - - if _, exist := f.Messages[msg.EnvelopeHash]; !exist { - f.Messages[msg.EnvelopeHash] = msg - } -} - -func (f *Filter) Retrieve() (all []*ReceivedMessage) { - f.mutex.Lock() - defer f.mutex.Unlock() - - all = make([]*ReceivedMessage, 0, len(f.Messages)) - for _, msg := range f.Messages { - all = append(all, msg) - } - - f.Messages = make(map[common.Hash]*ReceivedMessage) // delete old messages - return all -} - -func (f *Filter) MatchMessage(msg *ReceivedMessage) bool { - if f.PoW > 0 && msg.PoW < f.PoW { - return false - } - - if f.expectsAsymmetricEncryption() && msg.isAsymmetricEncryption() { - return IsPubKeyEqual(&f.KeyAsym.PublicKey, msg.Dst) && f.MatchTopic(msg.Topic) - } else if f.expectsSymmetricEncryption() && msg.isSymmetricEncryption() { - return f.SymKeyHash == msg.SymKeyHash && f.MatchTopic(msg.Topic) - } - return false -} - -func (f *Filter) MatchEnvelope(envelope *Envelope) bool { - if f.PoW > 0 && envelope.pow < f.PoW { - return false - } - - if f.expectsAsymmetricEncryption() && envelope.isAsymmetric() { - return f.MatchTopic(envelope.Topic) - } else if f.expectsSymmetricEncryption() && envelope.IsSymmetric() { - return f.MatchTopic(envelope.Topic) - } - return false -} - -func (f *Filter) MatchTopic(topic TopicType) bool { - if len(f.Topics) == 0 { - // any topic matches - return true - } - - for _, bt := range f.Topics { - if matchSingleTopic(topic, bt) { - return true - } - } - return false -} - -func matchSingleTopic(topic TopicType, bt []byte) bool { - if len(bt) > TopicLength { - bt = bt[:TopicLength] - } - - if len(bt) < TopicLength { - return false - } - - for j, b := range bt { - if topic[j] != b { - return false - } - } - return true -} - -func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool { - if !ValidatePublicKey(a) { - return false - } else if !ValidatePublicKey(b) { - return false - } - // the curve is always the same, just compare the points - return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0 -} diff --git a/whisper/whisperv5/filter_test.go b/whisper/whisperv5/filter_test.go deleted file mode 100644 index fc8b5c98b623..000000000000 --- a/whisper/whisperv5/filter_test.go +++ /dev/null @@ -1,848 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -import ( - "math/big" - mrand "math/rand" - "testing" - "time" - - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/crypto" -) - -var seed int64 - -// InitSingleTest should be called in the beginning of every -// test, which uses RNG, in order to make the tests -// reproduciblity independent of their sequence. -func InitSingleTest() { - seed = time.Now().Unix() - mrand.Seed(seed) -} - -func InitDebugTest(i int64) { - seed = i - mrand.Seed(seed) -} - -type FilterTestCase struct { - f *Filter - id string - alive bool - msgCnt int -} - -func generateFilter(t *testing.T, symmetric bool) (*Filter, error) { - var f Filter - f.Messages = make(map[common.Hash]*ReceivedMessage) - - const topicNum = 8 - f.Topics = make([][]byte, topicNum) - for i := 0; i < topicNum; i++ { - f.Topics[i] = make([]byte, 4) - mrand.Read(f.Topics[i][:]) - f.Topics[i][0] = 0x01 - } - - key, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("generateFilter 1 failed with seed %d.", seed) - return nil, err - } - f.Src = &key.PublicKey - - if symmetric { - f.KeySym = make([]byte, aesKeyLength) - mrand.Read(f.KeySym) - f.SymKeyHash = crypto.Keccak256Hash(f.KeySym) - } else { - f.KeyAsym, err = crypto.GenerateKey() - if err != nil { - t.Fatalf("generateFilter 2 failed with seed %d.", seed) - return nil, err - } - } - - // AcceptP2P & PoW are not set - return &f, nil -} - -func generateTestCases(t *testing.T, SizeTestFilters int) []FilterTestCase { - cases := make([]FilterTestCase, SizeTestFilters) - for i := 0; i < SizeTestFilters; i++ { - f, _ := generateFilter(t, true) - cases[i].f = f - cases[i].alive = mrand.Int()&int(1) == 0 - } - return cases -} - -func TestInstallFilters(t *testing.T) { - InitSingleTest() - - const SizeTestFilters = 256 - w := New(&Config{}) - filters := NewFilters(w) - tst := generateTestCases(t, SizeTestFilters) - - var err error - var j string - for i := 0; i < SizeTestFilters; i++ { - j, err = filters.Install(tst[i].f) - if err != nil { - t.Fatalf("seed %d: failed to install filter: %s", seed, err) - } - tst[i].id = j - if len(j) != keyIdSize*2 { - t.Fatalf("seed %d: wrong filter id size [%d]", seed, len(j)) - } - } - - for _, testCase := range tst { - if !testCase.alive { - filters.Uninstall(testCase.id) - } - } - - for i, testCase := range tst { - fil := filters.Get(testCase.id) - exist := fil != nil - if exist != testCase.alive { - t.Fatalf("seed %d: failed alive: %d, %v, %v", seed, i, exist, testCase.alive) - } - if exist && fil.PoW != testCase.f.PoW { - t.Fatalf("seed %d: failed Get: %d, %v, %v", seed, i, exist, testCase.alive) - } - } -} - -func TestInstallSymKeyGeneratesHash(t *testing.T) { - InitSingleTest() - - w := New(&Config{}) - filters := NewFilters(w) - filter, _ := generateFilter(t, true) - - // save the current SymKeyHash for comparison - initialSymKeyHash := filter.SymKeyHash - - // ensure the SymKeyHash is invalid, for Install to recreate it - var invalid common.Hash - filter.SymKeyHash = invalid - - _, err := filters.Install(filter) - - if err != nil { - t.Fatalf("Error installing the filter: %s", err) - } - - for i, b := range filter.SymKeyHash { - if b != initialSymKeyHash[i] { - t.Fatalf("The filter's symmetric key hash was not properly generated by Install") - } - } -} - -func TestInstallIdenticalFilters(t *testing.T) { - InitSingleTest() - - w := New(&Config{}) - filters := NewFilters(w) - filter1, _ := generateFilter(t, true) - - // Copy the first filter since some of its fields - // are randomly gnerated. - filter2 := &Filter{ - KeySym: filter1.KeySym, - Topics: filter1.Topics, - PoW: filter1.PoW, - AllowP2P: filter1.AllowP2P, - Messages: make(map[common.Hash]*ReceivedMessage), - } - - _, err := filters.Install(filter1) - - if err != nil { - t.Fatalf("Error installing the first filter with seed %d: %s", seed, err) - } - - _, err = filters.Install(filter2) - - if err != nil { - t.Fatalf("Error installing the second filter with seed %d: %s", seed, err) - } - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("Error generating message parameters with seed %d: %s", seed, err) - } - - params.KeySym = filter1.KeySym - params.Topic = BytesToTopic(filter1.Topics[0]) - - filter1.Src = ¶ms.Src.PublicKey - filter2.Src = ¶ms.Src.PublicKey - - sentMessage, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := sentMessage.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - msg := env.Open(filter1) - if msg == nil { - t.Fatalf("failed to Open with filter1") - } - - if !filter1.MatchEnvelope(env) { - t.Fatalf("failed matching with the first filter") - } - - if !filter2.MatchEnvelope(env) { - t.Fatalf("failed matching with the first filter") - } - - if !filter1.MatchMessage(msg) { - t.Fatalf("failed matching with the second filter") - } - - if !filter2.MatchMessage(msg) { - t.Fatalf("failed matching with the second filter") - } -} - -func TestComparePubKey(t *testing.T) { - InitSingleTest() - - key1, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed to generate first key with seed %d: %s.", seed, err) - } - key2, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed to generate second key with seed %d: %s.", seed, err) - } - if IsPubKeyEqual(&key1.PublicKey, &key2.PublicKey) { - t.Fatalf("public keys are equal, seed %d.", seed) - } - - // generate key3 == key1 - mrand.Seed(seed) - key3, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed to generate third key with seed %d: %s.", seed, err) - } - if IsPubKeyEqual(&key1.PublicKey, &key3.PublicKey) { - t.Fatalf("key1 == key3, seed %d.", seed) - } -} - -func TestMatchEnvelope(t *testing.T) { - InitSingleTest() - - fsym, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateFilter with seed %d: %s.", seed, err) - } - - fasym, err := generateFilter(t, false) - if err != nil { - t.Fatalf("failed generateFilter() with seed %d: %s.", seed, err) - } - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - params.Topic[0] = 0xFF // ensure mismatch - - // mismatch with pseudo-random data - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - match := fsym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope symmetric with seed %d.", seed) - } - match = fasym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope asymmetric with seed %d.", seed) - } - - // encrypt symmetrically - i := mrand.Int() % 4 - fsym.Topics[i] = params.Topic[:] - fasym.Topics[i] = params.Topic[:] - msg, err = NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err = msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap() with seed %d: %s.", seed, err) - } - - // symmetric + matching topic: match - match = fsym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope() symmetric with seed %d.", seed) - } - - // asymmetric + matching topic: mismatch - match = fasym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope() asymmetric with seed %d.", seed) - } - - // symmetric + matching topic + insufficient PoW: mismatch - fsym.PoW = env.PoW() + 1.0 - match = fsym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope(symmetric + matching topic + insufficient PoW) asymmetric with seed %d.", seed) - } - - // symmetric + matching topic + sufficient PoW: match - fsym.PoW = env.PoW() / 2 - match = fsym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(symmetric + matching topic + sufficient PoW) with seed %d.", seed) - } - - // symmetric + topics are nil (wildcard): match - prevTopics := fsym.Topics - fsym.Topics = nil - match = fsym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(symmetric + topics are nil) with seed %d.", seed) - } - fsym.Topics = prevTopics - - // encrypt asymmetrically - key, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - params.KeySym = nil - params.Dst = &key.PublicKey - msg, err = NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err = msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap() with seed %d: %s.", seed, err) - } - - // encryption method mismatch - match = fsym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed) - } - - // asymmetric + mismatching topic: mismatch - match = fasym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(asymmetric + mismatching topic) with seed %d.", seed) - } - - // asymmetric + matching topic: match - fasym.Topics[i] = fasym.Topics[i+1] - match = fasym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope(asymmetric + matching topic) with seed %d.", seed) - } - - // asymmetric + filter without topic (wildcard): match - fasym.Topics = nil - match = fasym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(asymmetric + filter without topic) with seed %d.", seed) - } - - // asymmetric + insufficient PoW: mismatch - fasym.PoW = env.PoW() + 1.0 - match = fasym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope(asymmetric + insufficient PoW) with seed %d.", seed) - } - - // asymmetric + sufficient PoW: match - fasym.PoW = env.PoW() / 2 - match = fasym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(asymmetric + sufficient PoW) with seed %d.", seed) - } - - // filter without topic + envelope without topic: match - env.Topic = TopicType{} - match = fasym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(filter without topic + envelope without topic) with seed %d.", seed) - } - - // filter with topic + envelope without topic: mismatch - fasym.Topics = fsym.Topics - match = fasym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope(filter without topic + envelope without topic) with seed %d.", seed) - } -} - -func TestMatchMessageSym(t *testing.T) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - f, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateFilter with seed %d: %s.", seed, err) - } - - const index = 1 - params.KeySym = f.KeySym - params.Topic = BytesToTopic(f.Topics[index]) - - sentMessage, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := sentMessage.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - msg := env.Open(f) - if msg == nil { - t.Fatalf("failed Open with seed %d.", seed) - } - - // Src: match - *f.Src.X = *params.Src.PublicKey.X - *f.Src.Y = *params.Src.PublicKey.Y - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(src match) with seed %d.", seed) - } - - // insufficient PoW: mismatch - f.PoW = msg.PoW + 1.0 - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(insufficient PoW) with seed %d.", seed) - } - - // sufficient PoW: match - f.PoW = msg.PoW / 2 - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(sufficient PoW) with seed %d.", seed) - } - - // topic mismatch - f.Topics[index][0]++ - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed) - } - f.Topics[index][0]-- - - // key mismatch - f.SymKeyHash[0]++ - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(key mismatch) with seed %d.", seed) - } - f.SymKeyHash[0]-- - - // Src absent: match - f.Src = nil - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(src absent) with seed %d.", seed) - } - - // key hash mismatch - h := f.SymKeyHash - f.SymKeyHash = common.Hash{} - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(key hash mismatch) with seed %d.", seed) - } - f.SymKeyHash = h - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(key hash match) with seed %d.", seed) - } - - // encryption method mismatch - f.KeySym = nil - f.KeyAsym, err = crypto.GenerateKey() - if err != nil { - t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed) - } -} - -func TestMatchMessageAsym(t *testing.T) { - InitSingleTest() - - f, err := generateFilter(t, false) - if err != nil { - t.Fatalf("failed generateFilter with seed %d: %s.", seed, err) - } - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - const index = 1 - params.Topic = BytesToTopic(f.Topics[index]) - params.Dst = &f.KeyAsym.PublicKey - keySymOrig := params.KeySym - params.KeySym = nil - - sentMessage, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := sentMessage.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - msg := env.Open(f) - if msg == nil { - t.Fatalf("failed to open with seed %d.", seed) - } - - // Src: match - *f.Src.X = *params.Src.PublicKey.X - *f.Src.Y = *params.Src.PublicKey.Y - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchMessage(src match) with seed %d.", seed) - } - - // insufficient PoW: mismatch - f.PoW = msg.PoW + 1.0 - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(insufficient PoW) with seed %d.", seed) - } - - // sufficient PoW: match - f.PoW = msg.PoW / 2 - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(sufficient PoW) with seed %d.", seed) - } - - // topic mismatch - f.Topics[index][0]++ - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed) - } - f.Topics[index][0]-- - - // key mismatch - prev := *f.KeyAsym.PublicKey.X - zero := *big.NewInt(0) - *f.KeyAsym.PublicKey.X = zero - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(key mismatch) with seed %d.", seed) - } - *f.KeyAsym.PublicKey.X = prev - - // Src absent: match - f.Src = nil - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(src absent) with seed %d.", seed) - } - - // encryption method mismatch - f.KeySym = keySymOrig - f.KeyAsym = nil - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed) - } -} - -func cloneFilter(orig *Filter) *Filter { - var clone Filter - clone.Messages = make(map[common.Hash]*ReceivedMessage) - clone.Src = orig.Src - clone.KeyAsym = orig.KeyAsym - clone.KeySym = orig.KeySym - clone.Topics = orig.Topics - clone.PoW = orig.PoW - clone.AllowP2P = orig.AllowP2P - clone.SymKeyHash = orig.SymKeyHash - return &clone -} - -func generateCompatibeEnvelope(t *testing.T, f *Filter) *Envelope { - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - return nil - } - - params.KeySym = f.KeySym - params.Topic = BytesToTopic(f.Topics[2]) - sentMessage, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := sentMessage.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - return nil - } - return env -} - -func TestWatchers(t *testing.T) { - InitSingleTest() - - const NumFilters = 16 - const NumMessages = 256 - var i int - var j uint32 - var e *Envelope - var x, firstID string - var err error - - w := New(&Config{}) - filters := NewFilters(w) - tst := generateTestCases(t, NumFilters) - for i = 0; i < NumFilters; i++ { - tst[i].f.Src = nil - x, err = filters.Install(tst[i].f) - if err != nil { - t.Fatalf("failed to install filter with seed %d: %s.", seed, err) - } - tst[i].id = x - if len(firstID) == 0 { - firstID = x - } - } - - lastID := x - - var envelopes [NumMessages]*Envelope - for i = 0; i < NumMessages; i++ { - j = mrand.Uint32() % NumFilters - e = generateCompatibeEnvelope(t, tst[j].f) - envelopes[i] = e - tst[j].msgCnt++ - } - - for i = 0; i < NumMessages; i++ { - filters.NotifyWatchers(envelopes[i], false) - } - - var total int - var mail []*ReceivedMessage - var count [NumFilters]int - - for i = 0; i < NumFilters; i++ { - mail = tst[i].f.Retrieve() - count[i] = len(mail) - total += len(mail) - } - - if total != NumMessages { - t.Fatalf("failed with seed %d: total = %d, want: %d.", seed, total, NumMessages) - } - - for i = 0; i < NumFilters; i++ { - mail = tst[i].f.Retrieve() - if len(mail) != 0 { - t.Fatalf("failed with seed %d: i = %d.", seed, i) - } - - if tst[i].msgCnt != count[i] { - t.Fatalf("failed with seed %d: count[%d]: get %d, want %d.", seed, i, tst[i].msgCnt, count[i]) - } - } - - // another round with a cloned filter - - clone := cloneFilter(tst[0].f) - filters.Uninstall(lastID) - total = 0 - last := NumFilters - 1 - tst[last].f = clone - filters.Install(clone) - for i = 0; i < NumFilters; i++ { - tst[i].msgCnt = 0 - count[i] = 0 - } - - // make sure that the first watcher receives at least one message - e = generateCompatibeEnvelope(t, tst[0].f) - envelopes[0] = e - tst[0].msgCnt++ - for i = 1; i < NumMessages; i++ { - j = mrand.Uint32() % NumFilters - e = generateCompatibeEnvelope(t, tst[j].f) - envelopes[i] = e - tst[j].msgCnt++ - } - - for i = 0; i < NumMessages; i++ { - filters.NotifyWatchers(envelopes[i], false) - } - - for i = 0; i < NumFilters; i++ { - mail = tst[i].f.Retrieve() - count[i] = len(mail) - total += len(mail) - } - - combined := tst[0].msgCnt + tst[last].msgCnt - if total != NumMessages+count[0] { - t.Fatalf("failed with seed %d: total = %d, count[0] = %d.", seed, total, count[0]) - } - - if combined != count[0] { - t.Fatalf("failed with seed %d: combined = %d, count[0] = %d.", seed, combined, count[0]) - } - - if combined != count[last] { - t.Fatalf("failed with seed %d: combined = %d, count[last] = %d.", seed, combined, count[last]) - } - - for i = 1; i < NumFilters-1; i++ { - mail = tst[i].f.Retrieve() - if len(mail) != 0 { - t.Fatalf("failed with seed %d: i = %d.", seed, i) - } - - if tst[i].msgCnt != count[i] { - t.Fatalf("failed with seed %d: i = %d, get %d, want %d.", seed, i, tst[i].msgCnt, count[i]) - } - } - - // test AcceptP2P - - total = 0 - filters.NotifyWatchers(envelopes[0], true) - - for i = 0; i < NumFilters; i++ { - mail = tst[i].f.Retrieve() - total += len(mail) - } - - if total != 0 { - t.Fatalf("failed with seed %d: total: got %d, want 0.", seed, total) - } - - f := filters.Get(firstID) - if f == nil { - t.Fatalf("failed to get the filter with seed %d.", seed) - } - f.AllowP2P = true - total = 0 - filters.NotifyWatchers(envelopes[0], true) - - for i = 0; i < NumFilters; i++ { - mail = tst[i].f.Retrieve() - total += len(mail) - } - - if total != 1 { - t.Fatalf("failed with seed %d: total: got %d, want 1.", seed, total) - } -} - -func TestVariableTopics(t *testing.T) { - InitSingleTest() - - const lastTopicByte = 3 - var match bool - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - f, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateFilter with seed %d: %s.", seed, err) - } - - for i := 0; i < 4; i++ { - env.Topic = BytesToTopic(f.Topics[i]) - match = f.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope symmetric with seed %d, step %d.", seed, i) - } - - f.Topics[i][lastTopicByte]++ - match = f.MatchEnvelope(env) - if match { - t.Fatalf("MatchEnvelope symmetric with seed %d, step %d: false positive.", seed, i) - } - } -} - -func TestMatchSingleTopic_ReturnTrue(t *testing.T) { - bt := []byte("test") - topic := BytesToTopic(bt) - - if !matchSingleTopic(topic, bt) { - t.FailNow() - } -} - -func TestMatchSingleTopic_WithTail_ReturnTrue(t *testing.T) { - bt := []byte("test with tail") - topic := BytesToTopic([]byte("test")) - - if !matchSingleTopic(topic, bt) { - t.FailNow() - } -} - -func TestMatchSingleTopic_NotEquals_ReturnFalse(t *testing.T) { - bt := []byte("tes") - topic := BytesToTopic(bt) - - if matchSingleTopic(topic, bt) { - t.FailNow() - } -} - -func TestMatchSingleTopic_InsufficientLength_ReturnFalse(t *testing.T) { - bt := []byte("test") - topic := BytesToTopic([]byte("not_equal")) - - if matchSingleTopic(topic, bt) { - t.FailNow() - } -} diff --git a/whisper/whisperv5/gen_criteria_json.go b/whisper/whisperv5/gen_criteria_json.go deleted file mode 100644 index 3a0766954c9f..000000000000 --- a/whisper/whisperv5/gen_criteria_json.go +++ /dev/null @@ -1,64 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package whisperv5 - -import ( - "encoding/json" - - "github.com/XinFinOrg/XDPoSChain/common/hexutil" -) - -var _ = (*criteriaOverride)(nil) - -func (c Criteria) MarshalJSON() ([]byte, error) { - type Criteria struct { - SymKeyID string `json:"symKeyID"` - PrivateKeyID string `json:"privateKeyID"` - Sig hexutil.Bytes `json:"sig"` - MinPow float64 `json:"minPow"` - Topics []TopicType `json:"topics"` - AllowP2P bool `json:"allowP2P"` - } - var enc Criteria - enc.SymKeyID = c.SymKeyID - enc.PrivateKeyID = c.PrivateKeyID - enc.Sig = c.Sig - enc.MinPow = c.MinPow - enc.Topics = c.Topics - enc.AllowP2P = c.AllowP2P - return json.Marshal(&enc) -} - -func (c *Criteria) UnmarshalJSON(input []byte) error { - type Criteria struct { - SymKeyID *string `json:"symKeyID"` - PrivateKeyID *string `json:"privateKeyID"` - Sig *hexutil.Bytes `json:"sig"` - MinPow *float64 `json:"minPow"` - Topics []TopicType `json:"topics"` - AllowP2P *bool `json:"allowP2P"` - } - var dec Criteria - if err := json.Unmarshal(input, &dec); err != nil { - return err - } - if dec.SymKeyID != nil { - c.SymKeyID = *dec.SymKeyID - } - if dec.PrivateKeyID != nil { - c.PrivateKeyID = *dec.PrivateKeyID - } - if dec.Sig != nil { - c.Sig = *dec.Sig - } - if dec.MinPow != nil { - c.MinPow = *dec.MinPow - } - if dec.Topics != nil { - c.Topics = dec.Topics - } - if dec.AllowP2P != nil { - c.AllowP2P = *dec.AllowP2P - } - return nil -} diff --git a/whisper/whisperv5/gen_message_json.go b/whisper/whisperv5/gen_message_json.go deleted file mode 100644 index 16e95a6e143f..000000000000 --- a/whisper/whisperv5/gen_message_json.go +++ /dev/null @@ -1,82 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package whisperv5 - -import ( - "encoding/json" - - "github.com/XinFinOrg/XDPoSChain/common/hexutil" -) - -var _ = (*messageOverride)(nil) - -func (m Message) MarshalJSON() ([]byte, error) { - type Message struct { - Sig hexutil.Bytes `json:"sig,omitempty"` - TTL uint32 `json:"ttl"` - Timestamp uint32 `json:"timestamp"` - Topic TopicType `json:"topic"` - Payload hexutil.Bytes `json:"payload"` - Padding hexutil.Bytes `json:"padding"` - PoW float64 `json:"pow"` - Hash hexutil.Bytes `json:"hash"` - Dst hexutil.Bytes `json:"recipientPublicKey,omitempty"` - } - var enc Message - enc.Sig = m.Sig - enc.TTL = m.TTL - enc.Timestamp = m.Timestamp - enc.Topic = m.Topic - enc.Payload = m.Payload - enc.Padding = m.Padding - enc.PoW = m.PoW - enc.Hash = m.Hash - enc.Dst = m.Dst - return json.Marshal(&enc) -} - -func (m *Message) UnmarshalJSON(input []byte) error { - type Message struct { - Sig *hexutil.Bytes `json:"sig,omitempty"` - TTL *uint32 `json:"ttl"` - Timestamp *uint32 `json:"timestamp"` - Topic *TopicType `json:"topic"` - Payload *hexutil.Bytes `json:"payload"` - Padding *hexutil.Bytes `json:"padding"` - PoW *float64 `json:"pow"` - Hash *hexutil.Bytes `json:"hash"` - Dst *hexutil.Bytes `json:"recipientPublicKey,omitempty"` - } - var dec Message - if err := json.Unmarshal(input, &dec); err != nil { - return err - } - if dec.Sig != nil { - m.Sig = *dec.Sig - } - if dec.TTL != nil { - m.TTL = *dec.TTL - } - if dec.Timestamp != nil { - m.Timestamp = *dec.Timestamp - } - if dec.Topic != nil { - m.Topic = *dec.Topic - } - if dec.Payload != nil { - m.Payload = *dec.Payload - } - if dec.Padding != nil { - m.Padding = *dec.Padding - } - if dec.PoW != nil { - m.PoW = *dec.PoW - } - if dec.Hash != nil { - m.Hash = *dec.Hash - } - if dec.Dst != nil { - m.Dst = *dec.Dst - } - return nil -} diff --git a/whisper/whisperv5/gen_newmessage_json.go b/whisper/whisperv5/gen_newmessage_json.go deleted file mode 100644 index dabe66c0b63d..000000000000 --- a/whisper/whisperv5/gen_newmessage_json.go +++ /dev/null @@ -1,88 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package whisperv5 - -import ( - "encoding/json" - - "github.com/XinFinOrg/XDPoSChain/common/hexutil" -) - -var _ = (*newMessageOverride)(nil) - -func (n NewMessage) MarshalJSON() ([]byte, error) { - type NewMessage struct { - SymKeyID string `json:"symKeyID"` - PublicKey hexutil.Bytes `json:"pubKey"` - Sig string `json:"sig"` - TTL uint32 `json:"ttl"` - Topic TopicType `json:"topic"` - Payload hexutil.Bytes `json:"payload"` - Padding hexutil.Bytes `json:"padding"` - PowTime uint32 `json:"powTime"` - PowTarget float64 `json:"powTarget"` - TargetPeer string `json:"targetPeer"` - } - var enc NewMessage - enc.SymKeyID = n.SymKeyID - enc.PublicKey = n.PublicKey - enc.Sig = n.Sig - enc.TTL = n.TTL - enc.Topic = n.Topic - enc.Payload = n.Payload - enc.Padding = n.Padding - enc.PowTime = n.PowTime - enc.PowTarget = n.PowTarget - enc.TargetPeer = n.TargetPeer - return json.Marshal(&enc) -} - -func (n *NewMessage) UnmarshalJSON(input []byte) error { - type NewMessage struct { - SymKeyID *string `json:"symKeyID"` - PublicKey *hexutil.Bytes `json:"pubKey"` - Sig *string `json:"sig"` - TTL *uint32 `json:"ttl"` - Topic *TopicType `json:"topic"` - Payload *hexutil.Bytes `json:"payload"` - Padding *hexutil.Bytes `json:"padding"` - PowTime *uint32 `json:"powTime"` - PowTarget *float64 `json:"powTarget"` - TargetPeer *string `json:"targetPeer"` - } - var dec NewMessage - if err := json.Unmarshal(input, &dec); err != nil { - return err - } - if dec.SymKeyID != nil { - n.SymKeyID = *dec.SymKeyID - } - if dec.PublicKey != nil { - n.PublicKey = *dec.PublicKey - } - if dec.Sig != nil { - n.Sig = *dec.Sig - } - if dec.TTL != nil { - n.TTL = *dec.TTL - } - if dec.Topic != nil { - n.Topic = *dec.Topic - } - if dec.Payload != nil { - n.Payload = *dec.Payload - } - if dec.Padding != nil { - n.Padding = *dec.Padding - } - if dec.PowTime != nil { - n.PowTime = *dec.PowTime - } - if dec.PowTarget != nil { - n.PowTarget = *dec.PowTarget - } - if dec.TargetPeer != nil { - n.TargetPeer = *dec.TargetPeer - } - return nil -} diff --git a/whisper/whisperv5/message.go b/whisper/whisperv5/message.go deleted file mode 100644 index 70745aa9f14b..000000000000 --- a/whisper/whisperv5/message.go +++ /dev/null @@ -1,352 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains the Whisper protocol Message element. - -package whisperv5 - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/ecdsa" - crand "crypto/rand" - "encoding/binary" - "errors" - "strconv" - - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/ecies" - "github.com/XinFinOrg/XDPoSChain/log" -) - -// Options specifies the exact way a message should be wrapped into an Envelope. -type MessageParams struct { - TTL uint32 - Src *ecdsa.PrivateKey - Dst *ecdsa.PublicKey - KeySym []byte - Topic TopicType - WorkTime uint32 - PoW float64 - Payload []byte - Padding []byte -} - -// SentMessage represents an end-user data packet to transmit through the -// Whisper protocol. These are wrapped into Envelopes that need not be -// understood by intermediate nodes, just forwarded. -type sentMessage struct { - Raw []byte -} - -// ReceivedMessage represents a data packet to be received through the -// Whisper protocol. -type ReceivedMessage struct { - Raw []byte - - Payload []byte - Padding []byte - Signature []byte - - PoW float64 // Proof of work as described in the Whisper spec - Sent uint32 // Time when the message was posted into the network - TTL uint32 // Maximum time to live allowed for the message - Src *ecdsa.PublicKey // Message recipient (identity used to decode the message) - Dst *ecdsa.PublicKey // Message recipient (identity used to decode the message) - Topic TopicType - - SymKeyHash common.Hash // The Keccak256Hash of the key, associated with the Topic - EnvelopeHash common.Hash // Message envelope hash to act as a unique id - EnvelopeVersion uint64 -} - -func isMessageSigned(flags byte) bool { - return (flags & signatureFlag) != 0 -} - -func (msg *ReceivedMessage) isSymmetricEncryption() bool { - return msg.SymKeyHash != common.Hash{} -} - -func (msg *ReceivedMessage) isAsymmetricEncryption() bool { - return msg.Dst != nil -} - -// NewMessage creates and initializes a non-signed, non-encrypted Whisper message. -func NewSentMessage(params *MessageParams) (*sentMessage, error) { - msg := sentMessage{} - msg.Raw = make([]byte, 1, len(params.Payload)+len(params.Padding)+signatureLength+padSizeLimit) - msg.Raw[0] = 0 // set all the flags to zero - err := msg.appendPadding(params) - if err != nil { - return nil, err - } - msg.Raw = append(msg.Raw, params.Payload...) - return &msg, nil -} - -// getSizeOfLength returns the number of bytes necessary to encode the entire size padding (including these bytes) -func getSizeOfLength(b []byte) (sz int, err error) { - sz = intSize(len(b)) // first iteration - sz = intSize(len(b) + sz) // second iteration - if sz > 3 { - err = errors.New("oversized padding parameter") - } - return sz, err -} - -// sizeOfIntSize returns minimal number of bytes necessary to encode an integer value -func intSize(i int) (s int) { - for s = 1; i >= 256; s++ { - i /= 256 - } - return s -} - -// appendPadding appends the pseudorandom padding bytes and sets the padding flag. -// The last byte contains the size of padding (thus, its size must not exceed 256). -func (msg *sentMessage) appendPadding(params *MessageParams) error { - rawSize := len(params.Payload) + 1 - if params.Src != nil { - rawSize += signatureLength - } - odd := rawSize % padSizeLimit - - if len(params.Padding) != 0 { - padSize := len(params.Padding) - padLengthSize, err := getSizeOfLength(params.Padding) - if err != nil { - return err - } - totalPadSize := padSize + padLengthSize - buf := make([]byte, 8) - binary.LittleEndian.PutUint32(buf, uint32(totalPadSize)) - buf = buf[:padLengthSize] - msg.Raw = append(msg.Raw, buf...) - msg.Raw = append(msg.Raw, params.Padding...) - msg.Raw[0] |= byte(padLengthSize) // number of bytes indicating the padding size - } else if odd != 0 { - totalPadSize := padSizeLimit - odd - if totalPadSize > 255 { - // this algorithm is only valid if padSizeLimit < 256. - // if padSizeLimit will ever change, please fix the algorithm - // (please see also ReceivedMessage.extractPadding() function). - panic("please fix the padding algorithm before releasing new version") - } - buf := make([]byte, totalPadSize) - _, err := crand.Read(buf[1:]) - if err != nil { - return err - } - if totalPadSize > 6 && !validateSymmetricKey(buf) { - return errors.New("failed to generate random padding of size " + strconv.Itoa(totalPadSize)) - } - buf[0] = byte(totalPadSize) - msg.Raw = append(msg.Raw, buf...) - msg.Raw[0] |= byte(0x1) // number of bytes indicating the padding size - } - return nil -} - -// sign calculates and sets the cryptographic signature for the message, -// also setting the sign flag. -func (msg *sentMessage) sign(key *ecdsa.PrivateKey) error { - if isMessageSigned(msg.Raw[0]) { - // this should not happen, but no reason to panic - log.Error("failed to sign the message: already signed") - return nil - } - - msg.Raw[0] |= signatureFlag - hash := crypto.Keccak256(msg.Raw) - signature, err := crypto.Sign(hash, key) - if err != nil { - msg.Raw[0] &= ^signatureFlag // clear the flag - return err - } - msg.Raw = append(msg.Raw, signature...) - return nil -} - -// encryptAsymmetric encrypts a message with a public key. -func (msg *sentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error { - if !ValidatePublicKey(key) { - return errors.New("invalid public key provided for asymmetric encryption") - } - encrypted, err := ecies.Encrypt(crand.Reader, ecies.ImportECDSAPublic(key), msg.Raw, nil, nil) - if err == nil { - msg.Raw = encrypted - } - return err -} - -// encryptSymmetric encrypts a message with a topic key, using AES-GCM-256. -// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize). -func (msg *sentMessage) encryptSymmetric(key []byte) (nonce []byte, err error) { - if !validateSymmetricKey(key) { - return nil, errors.New("invalid key provided for symmetric encryption") - } - - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - aesgcm, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } - - // never use more than 2^32 random nonces with a given key - nonce = make([]byte, aesgcm.NonceSize()) - _, err = crand.Read(nonce) - if err != nil { - return nil, err - } else if !validateSymmetricKey(nonce) { - return nil, errors.New("crypto/rand failed to generate nonce") - } - - msg.Raw = aesgcm.Seal(nil, nonce, msg.Raw, nil) - return nonce, nil -} - -// Wrap bundles the message into an Envelope to transmit over the network. -func (msg *sentMessage) Wrap(options *MessageParams) (envelope *Envelope, err error) { - if options.TTL == 0 { - options.TTL = DefaultTTL - } - if options.Src != nil { - if err = msg.sign(options.Src); err != nil { - return nil, err - } - } - var nonce []byte - if options.Dst != nil { - err = msg.encryptAsymmetric(options.Dst) - } else if options.KeySym != nil { - nonce, err = msg.encryptSymmetric(options.KeySym) - } else { - err = errors.New("unable to encrypt the message: neither symmetric nor assymmetric key provided") - } - if err != nil { - return nil, err - } - - envelope = NewEnvelope(options.TTL, options.Topic, nonce, msg) - if err = envelope.Seal(options); err != nil { - return nil, err - } - return envelope, nil -} - -// decryptSymmetric decrypts a message with a topic key, using AES-GCM-256. -// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize). -func (msg *ReceivedMessage) decryptSymmetric(key []byte, nonce []byte) error { - block, err := aes.NewCipher(key) - if err != nil { - return err - } - aesgcm, err := cipher.NewGCM(block) - if err != nil { - return err - } - if len(nonce) != aesgcm.NonceSize() { - log.Error("decrypting the message", "AES nonce size", len(nonce)) - return errors.New("wrong AES nonce size") - } - decrypted, err := aesgcm.Open(nil, nonce, msg.Raw, nil) - if err != nil { - return err - } - msg.Raw = decrypted - return nil -} - -// decryptAsymmetric decrypts an encrypted payload with a private key. -func (msg *ReceivedMessage) decryptAsymmetric(key *ecdsa.PrivateKey) error { - decrypted, err := ecies.ImportECDSA(key).Decrypt(msg.Raw, nil, nil) - if err == nil { - msg.Raw = decrypted - } - return err -} - -// Validate checks the validity and extracts the fields in case of success -func (msg *ReceivedMessage) Validate() bool { - end := len(msg.Raw) - if end < 1 { - return false - } - - if isMessageSigned(msg.Raw[0]) { - end -= signatureLength - if end <= 1 { - return false - } - msg.Signature = msg.Raw[end:] - msg.Src = msg.SigToPubKey() - if msg.Src == nil { - return false - } - } - - padSize, ok := msg.extractPadding(end) - if !ok { - return false - } - - msg.Payload = msg.Raw[1+padSize : end] - return true -} - -// extractPadding extracts the padding from raw message. -// although we don't support sending messages with padding size -// exceeding 255 bytes, such messages are perfectly valid, and -// can be successfully decrypted. -func (msg *ReceivedMessage) extractPadding(end int) (int, bool) { - paddingSize := 0 - sz := int(msg.Raw[0] & paddingMask) // number of bytes indicating the entire size of padding (including these bytes) - // could be zero -- it means no padding - if sz != 0 { - paddingSize = int(bytesToUintLittleEndian(msg.Raw[1 : 1+sz])) - if paddingSize < sz || paddingSize+1 > end { - return 0, false - } - msg.Padding = msg.Raw[1+sz : 1+paddingSize] - } - return paddingSize, true -} - -// Recover retrieves the public key of the message signer. -func (msg *ReceivedMessage) SigToPubKey() *ecdsa.PublicKey { - defer func() { recover() }() // in case of invalid signature - - pub, err := crypto.SigToPub(msg.hash(), msg.Signature) - if err != nil { - log.Error("failed to recover public key from signature", "err", err) - return nil - } - return pub -} - -// hash calculates the SHA3 checksum of the message flags, payload and padding. -func (msg *ReceivedMessage) hash() []byte { - if isMessageSigned(msg.Raw[0]) { - sz := len(msg.Raw) - signatureLength - return crypto.Keccak256(msg.Raw[:sz]) - } - return crypto.Keccak256(msg.Raw) -} diff --git a/whisper/whisperv5/message_test.go b/whisper/whisperv5/message_test.go deleted file mode 100644 index c1ebcf4d89d2..000000000000 --- a/whisper/whisperv5/message_test.go +++ /dev/null @@ -1,415 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -import ( - "bytes" - mrand "math/rand" - "testing" - - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/rlp" -) - -func generateMessageParams() (*MessageParams, error) { - // set all the parameters except p.Dst and p.Padding - - buf := make([]byte, 4) - mrand.Read(buf) - sz := mrand.Intn(400) - - var p MessageParams - p.PoW = 0.01 - p.WorkTime = 1 - p.TTL = uint32(mrand.Intn(1024)) - p.Payload = make([]byte, sz) - p.KeySym = make([]byte, aesKeyLength) - mrand.Read(p.Payload) - mrand.Read(p.KeySym) - p.Topic = BytesToTopic(buf) - - var err error - p.Src, err = crypto.GenerateKey() - if err != nil { - return nil, err - } - - return &p, nil -} - -func singleMessageTest(t *testing.T, symmetric bool) { - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - key, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - - if !symmetric { - params.KeySym = nil - params.Dst = &key.PublicKey - } - - text := make([]byte, 0, 512) - text = append(text, params.Payload...) - - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - var decrypted *ReceivedMessage - if symmetric { - decrypted, err = env.OpenSymmetric(params.KeySym) - } else { - decrypted, err = env.OpenAsymmetric(key) - } - - if err != nil { - t.Fatalf("failed to encrypt with seed %d: %s.", seed, err) - } - - if !decrypted.Validate() { - t.Fatalf("failed to validate with seed %d.", seed) - } - - if !bytes.Equal(text, decrypted.Payload) { - t.Fatalf("failed with seed %d: compare payload.", seed) - } - if !isMessageSigned(decrypted.Raw[0]) { - t.Fatalf("failed with seed %d: unsigned.", seed) - } - if len(decrypted.Signature) != signatureLength { - t.Fatalf("failed with seed %d: signature len %d.", seed, len(decrypted.Signature)) - } - if !IsPubKeyEqual(decrypted.Src, ¶ms.Src.PublicKey) { - t.Fatalf("failed with seed %d: signature mismatch.", seed) - } -} - -func TestMessageEncryption(t *testing.T) { - InitSingleTest() - - var symmetric bool - for i := 0; i < 256; i++ { - singleMessageTest(t, symmetric) - symmetric = !symmetric - } -} - -func TestMessageWrap(t *testing.T) { - seed = int64(1777444222) - mrand.Seed(seed) - target := 128.0 - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.TTL = 1 - params.WorkTime = 12 - params.PoW = target - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - pow := env.PoW() - if pow < target { - t.Fatalf("failed Wrap with seed %d: pow < target (%f vs. %f).", seed, pow, target) - } - - // set PoW target too high, expect error - msg2, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.TTL = 1000000 - params.WorkTime = 1 - params.PoW = 10000000.0 - _, err = msg2.Wrap(params) - if err == nil { - t.Fatalf("unexpectedly reached the PoW target with seed %d.", seed) - } -} - -func TestMessageSeal(t *testing.T) { - // this test depends on deterministic choice of seed (1976726903) - seed = int64(1976726903) - mrand.Seed(seed) - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.TTL = 1 - aesnonce := make([]byte, 12) - mrand.Read(aesnonce) - - env := NewEnvelope(params.TTL, params.Topic, aesnonce, msg) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - env.Expiry = uint32(seed) // make it deterministic - target := 32.0 - params.WorkTime = 4 - params.PoW = target - env.Seal(params) - - env.calculatePoW(0) - pow := env.PoW() - if pow < target { - t.Fatalf("failed Wrap with seed %d: pow < target (%f vs. %f).", seed, pow, target) - } - - params.WorkTime = 1 - params.PoW = 1000000000.0 - env.Seal(params) - env.calculatePoW(0) - pow = env.PoW() - if pow < 2*target { - t.Fatalf("failed Wrap with seed %d: pow too small %f.", seed, pow) - } -} - -func TestEnvelopeOpen(t *testing.T) { - InitSingleTest() - - var symmetric bool - for i := 0; i < 256; i++ { - singleEnvelopeOpenTest(t, symmetric) - symmetric = !symmetric - } -} - -func singleEnvelopeOpenTest(t *testing.T, symmetric bool) { - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - key, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - - if !symmetric { - params.KeySym = nil - params.Dst = &key.PublicKey - } - - text := make([]byte, 0, 512) - text = append(text, params.Payload...) - - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - f := Filter{KeyAsym: key, KeySym: params.KeySym} - decrypted := env.Open(&f) - if decrypted == nil { - t.Fatalf("failed to open with seed %d.", seed) - } - - if !bytes.Equal(text, decrypted.Payload) { - t.Fatalf("failed with seed %d: compare payload.", seed) - } - if !isMessageSigned(decrypted.Raw[0]) { - t.Fatalf("failed with seed %d: unsigned.", seed) - } - if len(decrypted.Signature) != signatureLength { - t.Fatalf("failed with seed %d: signature len %d.", seed, len(decrypted.Signature)) - } - if !IsPubKeyEqual(decrypted.Src, ¶ms.Src.PublicKey) { - t.Fatalf("failed with seed %d: signature mismatch.", seed) - } - if decrypted.isAsymmetricEncryption() == symmetric { - t.Fatalf("failed with seed %d: asymmetric %v vs. %v.", seed, decrypted.isAsymmetricEncryption(), symmetric) - } - if decrypted.isSymmetricEncryption() != symmetric { - t.Fatalf("failed with seed %d: symmetric %v vs. %v.", seed, decrypted.isSymmetricEncryption(), symmetric) - } - if !symmetric { - if decrypted.Dst == nil { - t.Fatalf("failed with seed %d: dst is nil.", seed) - } - if !IsPubKeyEqual(decrypted.Dst, &key.PublicKey) { - t.Fatalf("failed with seed %d: Dst.", seed) - } - } -} - -func TestEncryptWithZeroKey(t *testing.T) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.KeySym = make([]byte, aesKeyLength) - _, err = msg.Wrap(params) - if err == nil { - t.Fatalf("wrapped with zero key, seed: %d.", seed) - } - - params, err = generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, err = NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.KeySym = make([]byte, 0) - _, err = msg.Wrap(params) - if err == nil { - t.Fatalf("wrapped with empty key, seed: %d.", seed) - } - - params, err = generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, err = NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.KeySym = nil - _, err = msg.Wrap(params) - if err == nil { - t.Fatalf("wrapped with nil key, seed: %d.", seed) - } -} - -func TestRlpEncode(t *testing.T) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("wrapped with zero key, seed: %d.", seed) - } - - raw, err := rlp.EncodeToBytes(env) - if err != nil { - t.Fatalf("RLP encode failed: %s.", err) - } - - var decoded Envelope - rlp.DecodeBytes(raw, &decoded) - if err != nil { - t.Fatalf("RLP decode failed: %s.", err) - } - - he := env.Hash() - hd := decoded.Hash() - - if he != hd { - t.Fatalf("Hashes are not equal: %x vs. %x", he, hd) - } -} - -func singlePaddingTest(t *testing.T, padSize int) { - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d and sz=%d: %s.", seed, padSize, err) - } - params.Padding = make([]byte, padSize) - params.PoW = 0.0000000001 - pad := make([]byte, padSize) - _, err = mrand.Read(pad) - if err != nil { - t.Fatalf("padding is not generated (seed %d): %s", seed, err) - } - n := copy(params.Padding, pad) - if n != padSize { - t.Fatalf("padding is not copied (seed %d): %s", seed, err) - } - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed to wrap, seed: %d and sz=%d.", seed, padSize) - } - f := Filter{KeySym: params.KeySym} - decrypted := env.Open(&f) - if decrypted == nil { - t.Fatalf("failed to open, seed and sz=%d: %d.", seed, padSize) - } - if !bytes.Equal(pad, decrypted.Padding) { - t.Fatalf("padding is not retireved as expected with seed %d and sz=%d:\n[%x]\n[%x].", seed, padSize, pad, decrypted.Padding) - } -} - -func TestPadding(t *testing.T) { - InitSingleTest() - - for i := 1; i < 260; i++ { - singlePaddingTest(t, i) - } - - lim := 256 * 256 - for i := lim - 5; i < lim+2; i++ { - singlePaddingTest(t, i) - } - - for i := 0; i < 256; i++ { - n := mrand.Intn(256*254) + 256 - singlePaddingTest(t, n) - } - - for i := 0; i < 256; i++ { - n := mrand.Intn(256*1024) + 256*256 - singlePaddingTest(t, n) - } -} diff --git a/whisper/whisperv5/peer.go b/whisper/whisperv5/peer.go deleted file mode 100644 index 59da72a8e4b4..000000000000 --- a/whisper/whisperv5/peer.go +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -import ( - "fmt" - "time" - - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/log" - "github.com/XinFinOrg/XDPoSChain/p2p" - "github.com/XinFinOrg/XDPoSChain/rlp" - mapset "github.com/deckarep/golang-set" -) - -// peer represents a whisper protocol peer connection. -type Peer struct { - host *Whisper - peer *p2p.Peer - ws p2p.MsgReadWriter - trusted bool - - known mapset.Set // Messages already known by the peer to avoid wasting bandwidth - - quit chan struct{} -} - -// newPeer creates a new whisper peer object, but does not run the handshake itself. -func newPeer(host *Whisper, remote *p2p.Peer, rw p2p.MsgReadWriter) *Peer { - return &Peer{ - host: host, - peer: remote, - ws: rw, - trusted: false, - known: mapset.NewSet(), - quit: make(chan struct{}), - } -} - -// start initiates the peer updater, periodically broadcasting the whisper packets -// into the network. -func (p *Peer) start() { - go p.update() - log.Trace("start", "peer", p.ID()) -} - -// stop terminates the peer updater, stopping message forwarding to it. -func (p *Peer) stop() { - close(p.quit) - log.Trace("stop", "peer", p.ID()) -} - -// handshake sends the protocol initiation status message to the remote peer and -// verifies the remote status too. -func (p *Peer) handshake() error { - // Send the handshake status message asynchronously - errc := make(chan error, 1) - go func() { - errc <- p2p.Send(p.ws, statusCode, ProtocolVersion) - }() - // Fetch the remote status packet and verify protocol match - packet, err := p.ws.ReadMsg() - if err != nil { - return err - } - if packet.Code != statusCode { - return fmt.Errorf("peer [%x] sent packet %x before status packet", p.ID(), packet.Code) - } - s := rlp.NewStream(packet.Payload, uint64(packet.Size)) - peerVersion, err := s.Uint() - if err != nil { - return fmt.Errorf("peer [%x] sent bad status message: %v", p.ID(), err) - } - if peerVersion != ProtocolVersion { - return fmt.Errorf("peer [%x]: protocol version mismatch %d != %d", p.ID(), peerVersion, ProtocolVersion) - } - // Wait until out own status is consumed too - if err := <-errc; err != nil { - return fmt.Errorf("peer [%x] failed to send status packet: %v", p.ID(), err) - } - return nil -} - -// update executes periodic operations on the peer, including message transmission -// and expiration. -func (p *Peer) update() { - // Start the tickers for the updates - expire := time.NewTicker(expirationCycle) - transmit := time.NewTicker(transmissionCycle) - - // Loop and transmit until termination is requested - for { - select { - case <-expire.C: - p.expire() - - case <-transmit.C: - if err := p.broadcast(); err != nil { - log.Trace("broadcast failed", "reason", err, "peer", p.ID()) - return - } - - case <-p.quit: - return - } - } -} - -// mark marks an envelope known to the peer so that it won't be sent back. -func (peer *Peer) mark(envelope *Envelope) { - peer.known.Add(envelope.Hash()) -} - -// marked checks if an envelope is already known to the remote peer. -func (peer *Peer) marked(envelope *Envelope) bool { - return peer.known.Contains(envelope.Hash()) -} - -// expire iterates over all the known envelopes in the host and removes all -// expired (unknown) ones from the known list. -func (peer *Peer) expire() { - unmark := make(map[common.Hash]struct{}) - peer.known.Each(func(v interface{}) bool { - if !peer.host.isEnvelopeCached(v.(common.Hash)) { - unmark[v.(common.Hash)] = struct{}{} - } - return true - }) - // Dump all known but no longer cached - for hash := range unmark { - peer.known.Remove(hash) - } -} - -// broadcast iterates over the collection of envelopes and transmits yet unknown -// ones over the network. -func (p *Peer) broadcast() error { - var cnt int - envelopes := p.host.Envelopes() - for _, envelope := range envelopes { - if !p.marked(envelope) { - err := p2p.Send(p.ws, messagesCode, envelope) - if err != nil { - return err - } else { - p.mark(envelope) - cnt++ - } - } - } - if cnt > 0 { - log.Trace("broadcast", "num. messages", cnt) - } - return nil -} - -func (p *Peer) ID() []byte { - id := p.peer.ID() - return id[:] -} diff --git a/whisper/whisperv5/peer_test.go b/whisper/whisperv5/peer_test.go deleted file mode 100644 index 0ed18fc0186c..000000000000 --- a/whisper/whisperv5/peer_test.go +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -import ( - "bytes" - "crypto/ecdsa" - "fmt" - "net" - "sync" - "testing" - "time" - - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/p2p" - "github.com/XinFinOrg/XDPoSChain/p2p/discover" - "github.com/XinFinOrg/XDPoSChain/p2p/nat" -) - -var keys []string = []string{ - "d49dcf37238dc8a7aac57dc61b9fee68f0a97f062968978b9fafa7d1033d03a9", - "73fd6143c48e80ed3c56ea159fe7494a0b6b393a392227b422f4c3e8f1b54f98", - "119dd32adb1daa7a4c7bf77f847fb28730785aa92947edf42fdd997b54de40dc", - "deeda8709dea935bb772248a3144dea449ffcc13e8e5a1fd4ef20ce4e9c87837", - "5bd208a079633befa349441bdfdc4d85ba9bd56081525008380a63ac38a407cf", - "1d27fb4912002d58a2a42a50c97edb05c1b3dffc665dbaa42df1fe8d3d95c9b5", - "15def52800c9d6b8ca6f3066b7767a76afc7b611786c1276165fbc61636afb68", - "51be6ab4b2dc89f251ff2ace10f3c1cc65d6855f3e083f91f6ff8efdfd28b48c", - "ef1ef7441bf3c6419b162f05da6037474664f198b58db7315a6f4de52414b4a0", - "09bdf6985aabc696dc1fbeb5381aebd7a6421727343872eb2fadfc6d82486fd9", - "15d811bf2e01f99a224cdc91d0cf76cea08e8c67905c16fee9725c9be71185c4", - "2f83e45cf1baaea779789f755b7da72d8857aeebff19362dd9af31d3c9d14620", - "73f04e34ac6532b19c2aae8f8e52f38df1ac8f5cd10369f92325b9b0494b0590", - "1e2e07b69e5025537fb73770f483dc8d64f84ae3403775ef61cd36e3faf162c1", - "8963d9bbb3911aac6d30388c786756b1c423c4fbbc95d1f96ddbddf39809e43a", - "0422da85abc48249270b45d8de38a4cc3c02032ede1fcf0864a51092d58a2f1f", - "8ae5c15b0e8c7cade201fdc149831aa9b11ff626a7ffd27188886cc108ad0fa8", - "acd8f5a71d4aecfcb9ad00d32aa4bcf2a602939b6a9dd071bab443154184f805", - "a285a922125a7481600782ad69debfbcdb0316c1e97c267aff29ef50001ec045", - "28fd4eee78c6cd4bf78f39f8ab30c32c67c24a6223baa40e6f9c9a0e1de7cef5", - "c5cca0c9e6f043b288c6f1aef448ab59132dab3e453671af5d0752961f013fc7", - "46df99b051838cb6f8d1b73f232af516886bd8c4d0ee07af9a0a033c391380fd", - "c6a06a53cbaadbb432884f36155c8f3244e244881b5ee3e92e974cfa166d793f", - "783b90c75c63dc72e2f8d11b6f1b4de54d63825330ec76ee8db34f06b38ea211", - "9450038f10ca2c097a8013e5121b36b422b95b04892232f930a29292d9935611", - "e215e6246ed1cfdcf7310d4d8cdbe370f0d6a8371e4eb1089e2ae05c0e1bc10f", - "487110939ed9d64ebbc1f300adeab358bc58875faf4ca64990fbd7fe03b78f2b", - "824a70ea76ac81366da1d4f4ac39de851c8ac49dca456bb3f0a186ceefa269a5", - "ba8f34fa40945560d1006a328fe70c42e35cc3d1017e72d26864cd0d1b150f15", - "30a5dfcfd144997f428901ea88a43c8d176b19c79dde54cc58eea001aa3d246c", - "de59f7183aca39aa245ce66a05245fecfc7e2c75884184b52b27734a4a58efa2", - "92629e2ff5f0cb4f5f08fffe0f64492024d36f045b901efb271674b801095c5a", - "7184c1701569e3a4c4d2ddce691edd983b81e42e09196d332e1ae2f1e062cff4", -} - -const NumNodes = 16 // must not exceed the number of keys (32) - -type TestData struct { - counter [NumNodes]int - mutex sync.RWMutex -} - -type TestNode struct { - shh *Whisper - id *ecdsa.PrivateKey - server *p2p.Server - filerId string -} - -var result TestData -var nodes [NumNodes]*TestNode -var sharedKey []byte = []byte("some arbitrary data here") -var sharedTopic TopicType = TopicType{0xF, 0x1, 0x2, 0} -var expectedMessage []byte = []byte("per rectum ad astra") - -// This test does the following: -// 1. creates a chain of whisper nodes, -// 2. installs the filters with shared (predefined) parameters, -// 3. each node sends a number of random (undecryptable) messages, -// 4. first node sends one expected (decryptable) message, -// 5. checks if each node have received and decrypted exactly one message. -func TestSimulation(t *testing.T) { - t.Skip("TODO: PR-136 Broken test due to EVM upgrade!") - initialize(t) - - for i := 0; i < NumNodes; i++ { - sendMsg(t, false, i) - } - - sendMsg(t, true, 0) - checkPropagation(t) - stopServers() -} - -func initialize(t *testing.T) { - var err error - ip := net.IPv4(127, 0, 0, 1) - port0 := 30303 - - for i := 0; i < NumNodes; i++ { - var node TestNode - node.shh = New(&DefaultConfig) - err = node.shh.SetMinimumPoW(0.00000001) - if err != nil { - t.Fatal(err) - } - err = node.shh.Start(nil) - if err != nil { - t.Fatal(err) - } - topics := make([]TopicType, 0) - topics = append(topics, sharedTopic) - f := Filter{KeySym: sharedKey} - f.Topics = [][]byte{topics[0][:]} - node.filerId, err = node.shh.Subscribe(&f) - if err != nil { - t.Fatalf("failed to install the filter: %s.", err) - } - node.id, err = crypto.HexToECDSA(keys[i]) - if err != nil { - t.Fatalf("failed convert the key: %s.", keys[i]) - } - port := port0 + i - addr := fmt.Sprintf(":%d", port) // e.g. ":30303" - name := common.MakeName("whisper-go", "2.0") - var peers []*discover.Node - if i > 0 { - peerNodeId := nodes[i-1].id - peerPort := uint16(port - 1) - peerNode := discover.PubkeyID(&peerNodeId.PublicKey) - peer := discover.NewNode(peerNode, ip, peerPort, peerPort) - peers = append(peers, peer) - } - - node.server = &p2p.Server{ - Config: p2p.Config{ - PrivateKey: node.id, - MaxPeers: NumNodes/2 + 1, - Name: name, - Protocols: node.shh.Protocols(), - ListenAddr: addr, - NAT: nat.Any(), - BootstrapNodes: peers, - StaticNodes: peers, - TrustedNodes: peers, - }, - } - - err = node.server.Start() - if err != nil { - t.Fatalf("failed to start server %d.", i) - } - - nodes[i] = &node - } -} - -func stopServers() { - for i := 0; i < NumNodes; i++ { - n := nodes[i] - if n != nil { - n.shh.Unsubscribe(n.filerId) - n.shh.Stop() - n.server.Stop() - } - } -} - -func checkPropagation(t *testing.T) { - if t.Failed() { - return - } - - const cycle = 100 - const iterations = 100 - - for j := 0; j < iterations; j++ { - time.Sleep(cycle * time.Millisecond) - - for i := 0; i < NumNodes; i++ { - f := nodes[i].shh.GetFilter(nodes[i].filerId) - if f == nil { - t.Fatalf("failed to get filterId %s from node %d.", nodes[i].filerId, i) - } - - mail := f.Retrieve() - if !validateMail(t, i, mail) { - return - } - - if isTestComplete() { - return - } - } - } - - t.Fatalf("Test was not complete: timeout %d seconds.", iterations*cycle/1000) -} - -func validateMail(t *testing.T, index int, mail []*ReceivedMessage) bool { - var cnt int - for _, m := range mail { - if bytes.Equal(m.Payload, expectedMessage) { - cnt++ - } - } - - if cnt == 0 { - // no messages received yet: nothing is wrong - return true - } - if cnt > 1 { - t.Fatalf("node %d received %d.", index, cnt) - return false - } - - if cnt > 0 { - result.mutex.Lock() - defer result.mutex.Unlock() - result.counter[index] += cnt - if result.counter[index] > 1 { - t.Fatalf("node %d accumulated %d.", index, result.counter[index]) - } - } - return true -} - -func isTestComplete() bool { - result.mutex.RLock() - defer result.mutex.RUnlock() - - for i := 0; i < NumNodes; i++ { - if result.counter[i] < 1 { - return false - } - } - - for i := 0; i < NumNodes; i++ { - envelopes := nodes[i].shh.Envelopes() - if len(envelopes) < 2 { - return false - } - } - - return true -} - -func sendMsg(t *testing.T, expected bool, id int) { - if t.Failed() { - return - } - - opt := MessageParams{KeySym: sharedKey, Topic: sharedTopic, Payload: expectedMessage, PoW: 0.00000001, WorkTime: 1} - if !expected { - opt.KeySym[0]++ - opt.Topic[0]++ - opt.Payload = opt.Payload[1:] - } - - msg, err := NewSentMessage(&opt) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - envelope, err := msg.Wrap(&opt) - if err != nil { - t.Fatalf("failed to seal message: %s", err) - } - - err = nodes[id].shh.Send(envelope) - if err != nil { - t.Fatalf("failed to send message: %s", err) - } -} - -func TestPeerBasic(t *testing.T) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d.", seed) - } - - params.PoW = 0.001 - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d.", seed) - } - - p := newPeer(nil, nil, nil) - p.mark(env) - if !p.marked(env) { - t.Fatalf("failed mark with seed %d.", seed) - } -} diff --git a/whisper/whisperv5/topic.go b/whisper/whisperv5/topic.go deleted file mode 100644 index b4c5b27a6099..000000000000 --- a/whisper/whisperv5/topic.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains the Whisper protocol Topic element. - -package whisperv5 - -import ( - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/common/hexutil" -) - -// Topic represents a cryptographically secure, probabilistic partial -// classifications of a message, determined as the first (left) 4 bytes of the -// SHA3 hash of some arbitrary data given by the original author of the message. -type TopicType [TopicLength]byte - -func BytesToTopic(b []byte) (t TopicType) { - sz := TopicLength - if x := len(b); x < TopicLength { - sz = x - } - for i := 0; i < sz; i++ { - t[i] = b[i] - } - return t -} - -// String converts a topic byte array to a string representation. -func (t *TopicType) String() string { - return common.ToHex(t[:]) -} - -// MarshalText returns the hex representation of t. -func (t TopicType) MarshalText() ([]byte, error) { - return hexutil.Bytes(t[:]).MarshalText() -} - -// UnmarshalText parses a hex representation to a topic. -func (t *TopicType) UnmarshalText(input []byte) error { - return hexutil.UnmarshalFixedText("Topic", input, t[:]) -} diff --git a/whisper/whisperv5/topic_test.go b/whisper/whisperv5/topic_test.go deleted file mode 100644 index 54bbeaf85ec2..000000000000 --- a/whisper/whisperv5/topic_test.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -import ( - "encoding/json" - "testing" -) - -var topicStringTests = []struct { - topic TopicType - str string -}{ - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, str: "0x00000000"}, - {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, str: "0x007f80ff"}, - {topic: TopicType{0xff, 0x80, 0x7f, 0x00}, str: "0xff807f00"}, - {topic: TopicType{0xf2, 0x6e, 0x77, 0x79}, str: "0xf26e7779"}, -} - -func TestTopicString(t *testing.T) { - for i, tst := range topicStringTests { - s := tst.topic.String() - if s != tst.str { - t.Fatalf("failed test %d: have %s, want %s.", i, s, tst.str) - } - } -} - -var bytesToTopicTests = []struct { - data []byte - topic TopicType -}{ - {topic: TopicType{0x8f, 0x9a, 0x2b, 0x7d}, data: []byte{0x8f, 0x9a, 0x2b, 0x7d}}, - {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, data: []byte{0x00, 0x7f, 0x80, 0xff}}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{0x00, 0x00, 0x00, 0x00}}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{0x00, 0x00, 0x00}}, - {topic: TopicType{0x01, 0x00, 0x00, 0x00}, data: []byte{0x01}}, - {topic: TopicType{0x00, 0xfe, 0x00, 0x00}, data: []byte{0x00, 0xfe}}, - {topic: TopicType{0xea, 0x1d, 0x43, 0x00}, data: []byte{0xea, 0x1d, 0x43}}, - {topic: TopicType{0x6f, 0x3c, 0xb0, 0xdd}, data: []byte{0x6f, 0x3c, 0xb0, 0xdd, 0x0f, 0x00, 0x90}}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{}}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: nil}, -} - -var unmarshalTestsGood = []struct { - topic TopicType - data []byte -}{ - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x00000000"`)}, - {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, data: []byte(`"0x007f80ff"`)}, - {topic: TopicType{0xff, 0x80, 0x7f, 0x00}, data: []byte(`"0xff807f00"`)}, - {topic: TopicType{0xf2, 0x6e, 0x77, 0x79}, data: []byte(`"0xf26e7779"`)}, -} - -var unmarshalTestsBad = []struct { - topic TopicType - data []byte -}{ - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x000000"`)}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x0000000"`)}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x000000000"`)}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x0000000000"`)}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"000000"`)}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0000000"`)}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"000000000"`)}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0000000000"`)}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"abcdefg0"`)}, -} - -var unmarshalTestsUgly = []struct { - topic TopicType - data []byte -}{ - {topic: TopicType{0x01, 0x00, 0x00, 0x00}, data: []byte(`"0x00000001"`)}, -} - -func TestBytesToTopic(t *testing.T) { - for i, tst := range bytesToTopicTests { - top := BytesToTopic(tst.data) - if top != tst.topic { - t.Fatalf("failed test %d: have %v, want %v.", i, t, tst.topic) - } - } -} - -func TestUnmarshalTestsGood(t *testing.T) { - for i, tst := range unmarshalTestsGood { - var top TopicType - err := json.Unmarshal(tst.data, &top) - if err != nil { - t.Errorf("failed test %d. input: %v. err: %v", i, tst.data, err) - } else if top != tst.topic { - t.Errorf("failed test %d: have %v, want %v.", i, t, tst.topic) - } - } -} - -func TestUnmarshalTestsBad(t *testing.T) { - // in this test UnmarshalJSON() is supposed to fail - for i, tst := range unmarshalTestsBad { - var top TopicType - err := json.Unmarshal(tst.data, &top) - if err == nil { - t.Fatalf("failed test %d. input: %v.", i, tst.data) - } - } -} - -func TestUnmarshalTestsUgly(t *testing.T) { - // in this test UnmarshalJSON() is NOT supposed to fail, but result should be wrong - for i, tst := range unmarshalTestsUgly { - var top TopicType - err := json.Unmarshal(tst.data, &top) - if err != nil { - t.Errorf("failed test %d. input: %v.", i, tst.data) - } else if top == tst.topic { - t.Errorf("failed test %d: have %v, want %v.", i, top, tst.topic) - } - } -} diff --git a/whisper/whisperv5/whisper.go b/whisper/whisperv5/whisper.go deleted file mode 100644 index 929f24ceaca5..000000000000 --- a/whisper/whisperv5/whisper.go +++ /dev/null @@ -1,858 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -import ( - "bytes" - "crypto/ecdsa" - crand "crypto/rand" - "crypto/sha256" - "fmt" - "runtime" - "sync" - "time" - - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/log" - "github.com/XinFinOrg/XDPoSChain/p2p" - "github.com/XinFinOrg/XDPoSChain/rpc" - mapset "github.com/deckarep/golang-set" - "github.com/syndtr/goleveldb/leveldb/errors" - "golang.org/x/crypto/pbkdf2" - "golang.org/x/sync/syncmap" -) - -type Statistics struct { - messagesCleared int - memoryCleared int - memoryUsed int - cycles int - totalMessagesCleared int -} - -const ( - minPowIdx = iota // Minimal PoW required by the whisper node - maxMsgSizeIdx = iota // Maximal message length allowed by the whisper node - overflowIdx = iota // Indicator of message queue overflow -) - -// Whisper represents a dark communication interface through the Ethereum -// network, using its very own P2P communication layer. -type Whisper struct { - protocol p2p.Protocol // Protocol description and parameters - filters *Filters // Message filters installed with Subscribe function - - privateKeys map[string]*ecdsa.PrivateKey // Private key storage - symKeys map[string][]byte // Symmetric key storage - keyMu sync.RWMutex // Mutex associated with key storages - - poolMu sync.RWMutex // Mutex to sync the message and expiration pools - envelopes map[common.Hash]*Envelope // Pool of envelopes currently tracked by this node - expirations map[uint32]mapset.Set // Message expiration pool - - peerMu sync.RWMutex // Mutex to sync the active peer set - peers map[*Peer]struct{} // Set of currently active peers - - messageQueue chan *Envelope // Message queue for normal whisper messages - p2pMsgQueue chan *Envelope // Message queue for peer-to-peer messages (not to be forwarded any further) - quit chan struct{} // Channel used for graceful exit - - settings syncmap.Map // holds configuration settings that can be dynamically changed - - statsMu sync.Mutex // guard stats - stats Statistics // Statistics of whisper node - - mailServer MailServer // MailServer interface -} - -// New creates a Whisper client ready to communicate through the Ethereum P2P network. -func New(cfg *Config) *Whisper { - if cfg == nil { - cfg = &DefaultConfig - } - - whisper := &Whisper{ - privateKeys: make(map[string]*ecdsa.PrivateKey), - symKeys: make(map[string][]byte), - envelopes: make(map[common.Hash]*Envelope), - expirations: make(map[uint32]mapset.Set), - peers: make(map[*Peer]struct{}), - messageQueue: make(chan *Envelope, messageQueueLimit), - p2pMsgQueue: make(chan *Envelope, messageQueueLimit), - quit: make(chan struct{}), - } - - whisper.filters = NewFilters(whisper) - - whisper.settings.Store(minPowIdx, cfg.MinimumAcceptedPOW) - whisper.settings.Store(maxMsgSizeIdx, cfg.MaxMessageSize) - whisper.settings.Store(overflowIdx, false) - - // p2p whisper sub protocol handler - whisper.protocol = p2p.Protocol{ - Name: ProtocolName, - Version: uint(ProtocolVersion), - Length: NumberOfMessageCodes, - Run: whisper.HandlePeer, - NodeInfo: func() interface{} { - return map[string]interface{}{ - "version": ProtocolVersionStr, - "maxMessageSize": whisper.MaxMessageSize(), - "minimumPoW": whisper.MinPow(), - } - }, - } - - return whisper -} - -func (w *Whisper) MinPow() float64 { - val, _ := w.settings.Load(minPowIdx) - return val.(float64) -} - -// MaxMessageSize returns the maximum accepted message size. -func (w *Whisper) MaxMessageSize() uint32 { - val, _ := w.settings.Load(maxMsgSizeIdx) - return val.(uint32) -} - -// Overflow returns an indication if the message queue is full. -func (w *Whisper) Overflow() bool { - val, _ := w.settings.Load(overflowIdx) - return val.(bool) -} - -// APIs returns the RPC descriptors the Whisper implementation offers -func (w *Whisper) APIs() []rpc.API { - return []rpc.API{ - { - Namespace: ProtocolName, - Version: ProtocolVersionStr, - Service: NewPublicWhisperAPI(w), - Public: true, - }, - } -} - -// RegisterServer registers MailServer interface. -// MailServer will process all the incoming messages with p2pRequestCode. -func (w *Whisper) RegisterServer(server MailServer) { - w.mailServer = server -} - -// Protocols returns the whisper sub-protocols ran by this particular client. -func (w *Whisper) Protocols() []p2p.Protocol { - return []p2p.Protocol{w.protocol} -} - -// Version returns the whisper sub-protocols version number. -func (w *Whisper) Version() uint { - return w.protocol.Version -} - -// SetMaxMessageSize sets the maximal message size allowed by this node -func (w *Whisper) SetMaxMessageSize(size uint32) error { - if size > MaxMessageSize { - return fmt.Errorf("message size too large [%d>%d]", size, MaxMessageSize) - } - w.settings.Store(maxMsgSizeIdx, size) - return nil -} - -// SetMinimumPoW sets the minimal PoW required by this node -func (w *Whisper) SetMinimumPoW(val float64) error { - if val <= 0.0 { - return fmt.Errorf("invalid PoW: %f", val) - } - w.settings.Store(minPowIdx, val) - return nil -} - -// getPeer retrieves peer by ID -func (w *Whisper) getPeer(peerID []byte) (*Peer, error) { - w.peerMu.Lock() - defer w.peerMu.Unlock() - for p := range w.peers { - id := p.peer.ID() - if bytes.Equal(peerID, id[:]) { - return p, nil - } - } - return nil, fmt.Errorf("could not find peer with ID: %x", peerID) -} - -// AllowP2PMessagesFromPeer marks specific peer trusted, -// which will allow it to send historic (expired) messages. -func (w *Whisper) AllowP2PMessagesFromPeer(peerID []byte) error { - p, err := w.getPeer(peerID) - if err != nil { - return err - } - p.trusted = true - return nil -} - -// RequestHistoricMessages sends a message with p2pRequestCode to a specific peer, -// which is known to implement MailServer interface, and is supposed to process this -// request and respond with a number of peer-to-peer messages (possibly expired), -// which are not supposed to be forwarded any further. -// The whisper protocol is agnostic of the format and contents of envelope. -func (w *Whisper) RequestHistoricMessages(peerID []byte, envelope *Envelope) error { - p, err := w.getPeer(peerID) - if err != nil { - return err - } - p.trusted = true - return p2p.Send(p.ws, p2pRequestCode, envelope) -} - -// SendP2PMessage sends a peer-to-peer message to a specific peer. -func (w *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error { - p, err := w.getPeer(peerID) - if err != nil { - return err - } - return w.SendP2PDirect(p, envelope) -} - -// SendP2PDirect sends a peer-to-peer message to a specific peer. -func (w *Whisper) SendP2PDirect(peer *Peer, envelope *Envelope) error { - return p2p.Send(peer.ws, p2pCode, envelope) -} - -// NewKeyPair generates a new cryptographic identity for the client, and injects -// it into the known identities for message decryption. Returns ID of the new key pair. -func (w *Whisper) NewKeyPair() (string, error) { - key, err := crypto.GenerateKey() - if err != nil || !validatePrivateKey(key) { - key, err = crypto.GenerateKey() // retry once - } - if err != nil { - return "", err - } - if !validatePrivateKey(key) { - return "", errors.New("failed to generate valid key") - } - - id, err := GenerateRandomID() - if err != nil { - return "", fmt.Errorf("failed to generate ID: %s", err) - } - - w.keyMu.Lock() - defer w.keyMu.Unlock() - - if w.privateKeys[id] != nil { - return "", errors.New("failed to generate unique ID") - } - w.privateKeys[id] = key - return id, nil -} - -// DeleteKeyPair deletes the specified key if it exists. -func (w *Whisper) DeleteKeyPair(key string) bool { - w.keyMu.Lock() - defer w.keyMu.Unlock() - - if w.privateKeys[key] != nil { - delete(w.privateKeys, key) - return true - } - return false -} - -// AddKeyPair imports a asymmetric private key and returns it identifier. -func (w *Whisper) AddKeyPair(key *ecdsa.PrivateKey) (string, error) { - id, err := GenerateRandomID() - if err != nil { - return "", fmt.Errorf("failed to generate ID: %s", err) - } - - w.keyMu.Lock() - w.privateKeys[id] = key - w.keyMu.Unlock() - - return id, nil -} - -// HasKeyPair checks if the the whisper node is configured with the private key -// of the specified public pair. -func (w *Whisper) HasKeyPair(id string) bool { - w.keyMu.RLock() - defer w.keyMu.RUnlock() - return w.privateKeys[id] != nil -} - -// GetPrivateKey retrieves the private key of the specified identity. -func (w *Whisper) GetPrivateKey(id string) (*ecdsa.PrivateKey, error) { - w.keyMu.RLock() - defer w.keyMu.RUnlock() - key := w.privateKeys[id] - if key == nil { - return nil, errors.New("invalid id") - } - return key, nil -} - -// GenerateSymKey generates a random symmetric key and stores it under id, -// which is then returned. Will be used in the future for session key exchange. -func (w *Whisper) GenerateSymKey() (string, error) { - key := make([]byte, aesKeyLength) - _, err := crand.Read(key) - if err != nil { - return "", err - } else if !validateSymmetricKey(key) { - return "", errors.New("error in GenerateSymKey: crypto/rand failed to generate random data") - } - - id, err := GenerateRandomID() - if err != nil { - return "", fmt.Errorf("failed to generate ID: %s", err) - } - - w.keyMu.Lock() - defer w.keyMu.Unlock() - - if w.symKeys[id] != nil { - return "", errors.New("failed to generate unique ID") - } - w.symKeys[id] = key - return id, nil -} - -// AddSymKeyDirect stores the key, and returns its id. -func (w *Whisper) AddSymKeyDirect(key []byte) (string, error) { - if len(key) != aesKeyLength { - return "", fmt.Errorf("wrong key size: %d", len(key)) - } - - id, err := GenerateRandomID() - if err != nil { - return "", fmt.Errorf("failed to generate ID: %s", err) - } - - w.keyMu.Lock() - defer w.keyMu.Unlock() - - if w.symKeys[id] != nil { - return "", errors.New("failed to generate unique ID") - } - w.symKeys[id] = key - return id, nil -} - -// AddSymKeyFromPassword generates the key from password, stores it, and returns its id. -func (w *Whisper) AddSymKeyFromPassword(password string) (string, error) { - id, err := GenerateRandomID() - if err != nil { - return "", fmt.Errorf("failed to generate ID: %s", err) - } - if w.HasSymKey(id) { - return "", errors.New("failed to generate unique ID") - } - - derived, err := deriveKeyMaterial([]byte(password), EnvelopeVersion) - if err != nil { - return "", err - } - - w.keyMu.Lock() - defer w.keyMu.Unlock() - - // double check is necessary, because deriveKeyMaterial() is very slow - if w.symKeys[id] != nil { - return "", errors.New("critical error: failed to generate unique ID") - } - w.symKeys[id] = derived - return id, nil -} - -// HasSymKey returns true if there is a key associated with the given id. -// Otherwise returns false. -func (w *Whisper) HasSymKey(id string) bool { - w.keyMu.RLock() - defer w.keyMu.RUnlock() - return w.symKeys[id] != nil -} - -// DeleteSymKey deletes the key associated with the name string if it exists. -func (w *Whisper) DeleteSymKey(id string) bool { - w.keyMu.Lock() - defer w.keyMu.Unlock() - if w.symKeys[id] != nil { - delete(w.symKeys, id) - return true - } - return false -} - -// GetSymKey returns the symmetric key associated with the given id. -func (w *Whisper) GetSymKey(id string) ([]byte, error) { - w.keyMu.RLock() - defer w.keyMu.RUnlock() - if w.symKeys[id] != nil { - return w.symKeys[id], nil - } - return nil, errors.New("non-existent key ID") -} - -// Subscribe installs a new message handler used for filtering, decrypting -// and subsequent storing of incoming messages. -func (w *Whisper) Subscribe(f *Filter) (string, error) { - return w.filters.Install(f) -} - -// GetFilter returns the filter by id. -func (w *Whisper) GetFilter(id string) *Filter { - return w.filters.Get(id) -} - -// Unsubscribe removes an installed message handler. -func (w *Whisper) Unsubscribe(id string) error { - ok := w.filters.Uninstall(id) - if !ok { - return errors.New("Unsubscribe: Invalid ID") - } - return nil -} - -// Send injects a message into the whisper send queue, to be distributed in the -// network in the coming cycles. -func (w *Whisper) Send(envelope *Envelope) error { - ok, err := w.add(envelope) - if err != nil { - return err - } - if !ok { - return errors.New("failed to add envelope") - } - return err -} - -// Start implements node.Service, starting the background data propagation thread -// of the Whisper protocol. -func (w *Whisper) Start(*p2p.Server) error { - log.Info("started whisper v." + ProtocolVersionStr) - go w.update() - - numCPU := runtime.NumCPU() - for i := 0; i < numCPU; i++ { - go w.processQueue() - } - - return nil -} - -// Stop implements node.Service, stopping the background data propagation thread -// of the Whisper protocol. -func (w *Whisper) Stop() error { - close(w.quit) - log.Info("whisper stopped") - return nil -} - -// HandlePeer is called by the underlying P2P layer when the whisper sub-protocol -// connection is negotiated. -func (wh *Whisper) HandlePeer(peer *p2p.Peer, rw p2p.MsgReadWriter) error { - // Create the new peer and start tracking it - whisperPeer := newPeer(wh, peer, rw) - - wh.peerMu.Lock() - wh.peers[whisperPeer] = struct{}{} - wh.peerMu.Unlock() - - defer func() { - wh.peerMu.Lock() - delete(wh.peers, whisperPeer) - wh.peerMu.Unlock() - }() - - // Run the peer handshake and state updates - if err := whisperPeer.handshake(); err != nil { - return err - } - whisperPeer.start() - defer whisperPeer.stop() - - return wh.runMessageLoop(whisperPeer, rw) -} - -// runMessageLoop reads and processes inbound messages directly to merge into client-global state. -func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error { - for { - // fetch the next packet - packet, err := rw.ReadMsg() - if err != nil { - log.Warn("message loop", "peer", p.peer.ID(), "err", err) - return err - } - if packet.Size > wh.MaxMessageSize() { - log.Warn("oversized message received", "peer", p.peer.ID()) - return errors.New("oversized message received") - } - - switch packet.Code { - case statusCode: - // this should not happen, but no need to panic; just ignore this message. - log.Warn("unxepected status message received", "peer", p.peer.ID()) - case messagesCode: - // decode the contained envelopes - var envelope Envelope - if err := packet.Decode(&envelope); err != nil { - log.Warn("failed to decode envelope, peer will be disconnected", "peer", p.peer.ID(), "err", err) - return errors.New("invalid envelope") - } - cached, err := wh.add(&envelope) - if err != nil { - log.Warn("bad envelope received, peer will be disconnected", "peer", p.peer.ID(), "err", err) - return errors.New("invalid envelope") - } - if cached { - p.mark(&envelope) - } - case p2pCode: - // peer-to-peer message, sent directly to peer bypassing PoW checks, etc. - // this message is not supposed to be forwarded to other peers, and - // therefore might not satisfy the PoW, expiry and other requirements. - // these messages are only accepted from the trusted peer. - if p.trusted { - var envelope Envelope - if err := packet.Decode(&envelope); err != nil { - log.Warn("failed to decode direct message, peer will be disconnected", "peer", p.peer.ID(), "err", err) - return errors.New("invalid direct message") - } - wh.postEvent(&envelope, true) - } - case p2pRequestCode: - // Must be processed if mail server is implemented. Otherwise ignore. - if wh.mailServer != nil { - var request Envelope - if err := packet.Decode(&request); err != nil { - log.Warn("failed to decode p2p request message, peer will be disconnected", "peer", p.peer.ID(), "err", err) - return errors.New("invalid p2p request") - } - wh.mailServer.DeliverMail(p, &request) - } - default: - // New message types might be implemented in the future versions of Whisper. - // For forward compatibility, just ignore. - } - - packet.Discard() - } -} - -// add inserts a new envelope into the message pool to be distributed within the -// whisper network. It also inserts the envelope into the expiration pool at the -// appropriate time-stamp. In case of error, connection should be dropped. -func (wh *Whisper) add(envelope *Envelope) (bool, error) { - now := uint32(time.Now().Unix()) - sent := envelope.Expiry - envelope.TTL - - if sent > now { - if sent-SynchAllowance > now { - return false, fmt.Errorf("envelope created in the future [%x]", envelope.Hash()) - } else { - // recalculate PoW, adjusted for the time difference, plus one second for latency - envelope.calculatePoW(sent - now + 1) - } - } - - if envelope.Expiry < now { - if envelope.Expiry+SynchAllowance*2 < now { - return false, errors.New("very old message") - } else { - log.Debug("expired envelope dropped", "hash", envelope.Hash().Hex()) - return false, nil // drop envelope without error - } - } - - if uint32(envelope.size()) > wh.MaxMessageSize() { - return false, fmt.Errorf("huge messages are not allowed [%x]", envelope.Hash()) - } - - if len(envelope.Version) > 4 { - return false, fmt.Errorf("oversized version [%x]", envelope.Hash()) - } - - aesNonceSize := len(envelope.AESNonce) - if aesNonceSize != 0 && aesNonceSize != AESNonceLength { - // the standard AES GCM nonce size is 12 bytes, - // but constant gcmStandardNonceSize cannot be accessed (not exported) - return false, fmt.Errorf("wrong size of AESNonce: %d bytes [env: %x]", aesNonceSize, envelope.Hash()) - } - - if envelope.PoW() < wh.MinPow() { - log.Debug("envelope with low PoW dropped", "PoW", envelope.PoW(), "hash", envelope.Hash().Hex()) - return false, nil // drop envelope without error - } - - hash := envelope.Hash() - - wh.poolMu.Lock() - _, alreadyCached := wh.envelopes[hash] - if !alreadyCached { - wh.envelopes[hash] = envelope - if wh.expirations[envelope.Expiry] == nil { - wh.expirations[envelope.Expiry] = mapset.NewThreadUnsafeSet() - } - if !wh.expirations[envelope.Expiry].Contains(hash) { - wh.expirations[envelope.Expiry].Add(hash) - } - } - wh.poolMu.Unlock() - - if alreadyCached { - log.Trace("whisper envelope already cached", "hash", envelope.Hash().Hex()) - } else { - log.Trace("cached whisper envelope", "hash", envelope.Hash().Hex()) - wh.statsMu.Lock() - wh.stats.memoryUsed += envelope.size() - wh.statsMu.Unlock() - wh.postEvent(envelope, false) // notify the local node about the new message - if wh.mailServer != nil { - wh.mailServer.Archive(envelope) - } - } - return true, nil -} - -// postEvent queues the message for further processing. -func (w *Whisper) postEvent(envelope *Envelope, isP2P bool) { - // if the version of incoming message is higher than - // currently supported version, we can not decrypt it, - // and therefore just ignore this message - if envelope.Ver() <= EnvelopeVersion { - if isP2P { - w.p2pMsgQueue <- envelope - } else { - w.checkOverflow() - w.messageQueue <- envelope - } - } -} - -// checkOverflow checks if message queue overflow occurs and reports it if necessary. -func (w *Whisper) checkOverflow() { - queueSize := len(w.messageQueue) - - if queueSize == messageQueueLimit { - if !w.Overflow() { - w.settings.Store(overflowIdx, true) - log.Warn("message queue overflow") - } - } else if queueSize <= messageQueueLimit/2 { - if w.Overflow() { - w.settings.Store(overflowIdx, false) - log.Warn("message queue overflow fixed (back to normal)") - } - } -} - -// processQueue delivers the messages to the watchers during the lifetime of the whisper node. -func (w *Whisper) processQueue() { - var e *Envelope - for { - select { - case <-w.quit: - return - - case e = <-w.messageQueue: - w.filters.NotifyWatchers(e, false) - - case e = <-w.p2pMsgQueue: - w.filters.NotifyWatchers(e, true) - } - } -} - -// update loops until the lifetime of the whisper node, updating its internal -// state by expiring stale messages from the pool. -func (w *Whisper) update() { - // Start a ticker to check for expirations - expire := time.NewTicker(expirationCycle) - - // Repeat updates until termination is requested - for { - select { - case <-expire.C: - w.expire() - - case <-w.quit: - return - } - } -} - -// expire iterates over all the expiration timestamps, removing all stale -// messages from the pools. -func (w *Whisper) expire() { - w.poolMu.Lock() - defer w.poolMu.Unlock() - - w.statsMu.Lock() - defer w.statsMu.Unlock() - w.stats.reset() - now := uint32(time.Now().Unix()) - for expiry, hashSet := range w.expirations { - if expiry < now { - // Dump all expired messages and remove timestamp - hashSet.Each(func(v interface{}) bool { - sz := w.envelopes[v.(common.Hash)].size() - delete(w.envelopes, v.(common.Hash)) - w.stats.messagesCleared++ - w.stats.memoryCleared += sz - w.stats.memoryUsed -= sz - return true - }) - w.expirations[expiry].Clear() - delete(w.expirations, expiry) - } - } -} - -// Stats returns the whisper node statistics. -func (w *Whisper) Stats() Statistics { - w.statsMu.Lock() - defer w.statsMu.Unlock() - - return w.stats -} - -// Envelopes retrieves all the messages currently pooled by the node. -func (w *Whisper) Envelopes() []*Envelope { - w.poolMu.RLock() - defer w.poolMu.RUnlock() - - all := make([]*Envelope, 0, len(w.envelopes)) - for _, envelope := range w.envelopes { - all = append(all, envelope) - } - return all -} - -// Messages iterates through all currently floating envelopes -// and retrieves all the messages, that this filter could decrypt. -func (w *Whisper) Messages(id string) []*ReceivedMessage { - result := make([]*ReceivedMessage, 0) - w.poolMu.RLock() - defer w.poolMu.RUnlock() - - if filter := w.filters.Get(id); filter != nil { - for _, env := range w.envelopes { - msg := filter.processEnvelope(env) - if msg != nil { - result = append(result, msg) - } - } - } - return result -} - -// isEnvelopeCached checks if envelope with specific hash has already been received and cached. -func (w *Whisper) isEnvelopeCached(hash common.Hash) bool { - w.poolMu.Lock() - defer w.poolMu.Unlock() - - _, exist := w.envelopes[hash] - return exist -} - -// reset resets the node's statistics after each expiry cycle. -func (s *Statistics) reset() { - s.cycles++ - s.totalMessagesCleared += s.messagesCleared - - s.memoryCleared = 0 - s.messagesCleared = 0 -} - -// ValidatePublicKey checks the format of the given public key. -func ValidatePublicKey(k *ecdsa.PublicKey) bool { - return k != nil && k.X != nil && k.Y != nil && k.X.Sign() != 0 && k.Y.Sign() != 0 -} - -// validatePrivateKey checks the format of the given private key. -func validatePrivateKey(k *ecdsa.PrivateKey) bool { - if k == nil || k.D == nil || k.D.Sign() == 0 { - return false - } - return ValidatePublicKey(&k.PublicKey) -} - -// validateSymmetricKey returns false if the key contains all zeros -func validateSymmetricKey(k []byte) bool { - return len(k) > 0 && !containsOnlyZeros(k) -} - -// containsOnlyZeros checks if the data contain only zeros. -func containsOnlyZeros(data []byte) bool { - for _, b := range data { - if b != 0 { - return false - } - } - return true -} - -// bytesToUintLittleEndian converts the slice to 64-bit unsigned integer. -func bytesToUintLittleEndian(b []byte) (res uint64) { - mul := uint64(1) - for i := 0; i < len(b); i++ { - res += uint64(b[i]) * mul - mul *= 256 - } - return res -} - -// BytesToUintBigEndian converts the slice to 64-bit unsigned integer. -func BytesToUintBigEndian(b []byte) (res uint64) { - for i := 0; i < len(b); i++ { - res *= 256 - res += uint64(b[i]) - } - return res -} - -// deriveKeyMaterial derives symmetric key material from the key or password. -// pbkdf2 is used for security, in case people use password instead of randomly generated keys. -func deriveKeyMaterial(key []byte, version uint64) (derivedKey []byte, err error) { - if version == 0 { - // kdf should run no less than 0.1 seconds on average compute, - // because it's a once in a session experience - derivedKey := pbkdf2.Key(key, nil, 65356, aesKeyLength, sha256.New) - return derivedKey, nil - } else { - return nil, unknownVersionError(version) - } -} - -// GenerateRandomID generates a random string, which is then returned to be used as a key id -func GenerateRandomID() (id string, err error) { - buf := make([]byte, keyIdSize) - _, err = crand.Read(buf) - if err != nil { - return "", err - } - if !validateSymmetricKey(buf) { - return "", errors.New("error in generateRandomID: crypto/rand failed to generate random data") - } - id = common.Bytes2Hex(buf) - return id, err -} diff --git a/whisper/whisperv5/whisper_test.go b/whisper/whisperv5/whisper_test.go deleted file mode 100644 index 64c0d9b49410..000000000000 --- a/whisper/whisperv5/whisper_test.go +++ /dev/null @@ -1,851 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -import ( - "bytes" - "crypto/ecdsa" - mrand "math/rand" - "testing" - "time" - - "github.com/XinFinOrg/XDPoSChain/common" -) - -func TestWhisperBasic(t *testing.T) { - w := New(&DefaultConfig) - p := w.Protocols() - shh := p[0] - if shh.Name != ProtocolName { - t.Fatalf("failed Protocol Name: %v.", shh.Name) - } - if uint64(shh.Version) != ProtocolVersion { - t.Fatalf("failed Protocol Version: %v.", shh.Version) - } - if shh.Length != NumberOfMessageCodes { - t.Fatalf("failed Protocol Length: %v.", shh.Length) - } - if shh.Run == nil { - t.Fatalf("failed shh.Run.") - } - if uint64(w.Version()) != ProtocolVersion { - t.Fatalf("failed whisper Version: %v.", shh.Version) - } - if w.GetFilter("non-existent") != nil { - t.Fatalf("failed GetFilter.") - } - - peerID := make([]byte, 64) - mrand.Read(peerID) - peer, _ := w.getPeer(peerID) - if peer != nil { - t.Fatal("found peer for random key.") - } - if err := w.AllowP2PMessagesFromPeer(peerID); err == nil { - t.Fatalf("failed MarkPeerTrusted.") - } - exist := w.HasSymKey("non-existing") - if exist { - t.Fatalf("failed HasSymKey.") - } - key, err := w.GetSymKey("non-existing") - if err == nil { - t.Fatalf("failed GetSymKey(non-existing): false positive.") - } - if key != nil { - t.Fatalf("failed GetSymKey: false positive.") - } - mail := w.Envelopes() - if len(mail) != 0 { - t.Fatalf("failed w.Envelopes().") - } - m := w.Messages("non-existent") - if len(m) != 0 { - t.Fatalf("failed w.Messages.") - } - - var derived []byte - ver := uint64(0xDEADBEEF) - if _, err := deriveKeyMaterial(peerID, ver); err != unknownVersionError(ver) { - t.Fatalf("failed deriveKeyMaterial with param = %v: %s.", peerID, err) - } - derived, err = deriveKeyMaterial(peerID, 0) - if err != nil { - t.Fatalf("failed second deriveKeyMaterial with param = %v: %s.", peerID, err) - } - if !validateSymmetricKey(derived) { - t.Fatalf("failed validateSymmetricKey with param = %v.", derived) - } - if containsOnlyZeros(derived) { - t.Fatalf("failed containsOnlyZeros with param = %v.", derived) - } - - buf := []byte{0xFF, 0xE5, 0x80, 0x2, 0} - le := bytesToUintLittleEndian(buf) - be := BytesToUintBigEndian(buf) - if le != uint64(0x280e5ff) { - t.Fatalf("failed bytesToIntLittleEndian: %d.", le) - } - if be != uint64(0xffe5800200) { - t.Fatalf("failed BytesToIntBigEndian: %d.", be) - } - - id, err := w.NewKeyPair() - if err != nil { - t.Fatalf("failed to generate new key pair: %s.", err) - } - pk, err := w.GetPrivateKey(id) - if err != nil { - t.Fatalf("failed to retrieve new key pair: %s.", err) - } - if !validatePrivateKey(pk) { - t.Fatalf("failed validatePrivateKey: %v.", pk) - } - if !ValidatePublicKey(&pk.PublicKey) { - t.Fatalf("failed ValidatePublicKey: %v.", pk) - } -} - -func TestWhisperAsymmetricKeyImport(t *testing.T) { - var ( - w = New(&DefaultConfig) - privateKeys []*ecdsa.PrivateKey - ) - - for i := 0; i < 50; i++ { - id, err := w.NewKeyPair() - if err != nil { - t.Fatalf("could not generate key: %v", err) - } - - pk, err := w.GetPrivateKey(id) - if err != nil { - t.Fatalf("could not export private key: %v", err) - } - - privateKeys = append(privateKeys, pk) - - if !w.DeleteKeyPair(id) { - t.Fatalf("could not delete private key") - } - } - - for _, pk := range privateKeys { - if _, err := w.AddKeyPair(pk); err != nil { - t.Fatalf("could not import private key: %v", err) - } - } -} - -func TestWhisperIdentityManagement(t *testing.T) { - w := New(&DefaultConfig) - id1, err := w.NewKeyPair() - if err != nil { - t.Fatalf("failed to generate new key pair: %s.", err) - } - id2, err := w.NewKeyPair() - if err != nil { - t.Fatalf("failed to generate new key pair: %s.", err) - } - pk1, err := w.GetPrivateKey(id1) - if err != nil { - t.Fatalf("failed to retrieve the key pair: %s.", err) - } - pk2, err := w.GetPrivateKey(id2) - if err != nil { - t.Fatalf("failed to retrieve the key pair: %s.", err) - } - - if !w.HasKeyPair(id1) { - t.Fatalf("failed HasIdentity(pk1).") - } - if !w.HasKeyPair(id2) { - t.Fatalf("failed HasIdentity(pk2).") - } - if pk1 == nil { - t.Fatalf("failed GetIdentity(pk1).") - } - if pk2 == nil { - t.Fatalf("failed GetIdentity(pk2).") - } - - if !validatePrivateKey(pk1) { - t.Fatalf("pk1 is invalid.") - } - if !validatePrivateKey(pk2) { - t.Fatalf("pk2 is invalid.") - } - - // Delete one identity - done := w.DeleteKeyPair(id1) - if !done { - t.Fatalf("failed to delete id1.") - } - pk1, err = w.GetPrivateKey(id1) - if err == nil { - t.Fatalf("retrieve the key pair: false positive.") - } - pk2, err = w.GetPrivateKey(id2) - if err != nil { - t.Fatalf("failed to retrieve the key pair: %s.", err) - } - if w.HasKeyPair(id1) { - t.Fatalf("failed DeleteIdentity(pub1): still exist.") - } - if !w.HasKeyPair(id2) { - t.Fatalf("failed DeleteIdentity(pub1): pub2 does not exist.") - } - if pk1 != nil { - t.Fatalf("failed DeleteIdentity(pub1): first key still exist.") - } - if pk2 == nil { - t.Fatalf("failed DeleteIdentity(pub1): second key does not exist.") - } - - // Delete again non-existing identity - done = w.DeleteKeyPair(id1) - if done { - t.Fatalf("delete id1: false positive.") - } - pk1, err = w.GetPrivateKey(id1) - if err == nil { - t.Fatalf("retrieve the key pair: false positive.") - } - pk2, err = w.GetPrivateKey(id2) - if err != nil { - t.Fatalf("failed to retrieve the key pair: %s.", err) - } - if w.HasKeyPair(id1) { - t.Fatalf("failed delete non-existing identity: exist.") - } - if !w.HasKeyPair(id2) { - t.Fatalf("failed delete non-existing identity: pub2 does not exist.") - } - if pk1 != nil { - t.Fatalf("failed delete non-existing identity: first key exist.") - } - if pk2 == nil { - t.Fatalf("failed delete non-existing identity: second key does not exist.") - } - - // Delete second identity - done = w.DeleteKeyPair(id2) - if !done { - t.Fatalf("failed to delete id2.") - } - pk1, err = w.GetPrivateKey(id1) - if err == nil { - t.Fatalf("retrieve the key pair: false positive.") - } - pk2, err = w.GetPrivateKey(id2) - if err == nil { - t.Fatalf("retrieve the key pair: false positive.") - } - if w.HasKeyPair(id1) { - t.Fatalf("failed delete second identity: first identity exist.") - } - if w.HasKeyPair(id2) { - t.Fatalf("failed delete second identity: still exist.") - } - if pk1 != nil { - t.Fatalf("failed delete second identity: first key exist.") - } - if pk2 != nil { - t.Fatalf("failed delete second identity: second key exist.") - } -} - -func TestWhisperSymKeyManagement(t *testing.T) { - InitSingleTest() - - var err error - var k1, k2 []byte - w := New(&DefaultConfig) - id1 := string("arbitrary-string-1") - id2 := string("arbitrary-string-2") - - id1, err = w.GenerateSymKey() - if err != nil { - t.Fatalf("failed GenerateSymKey with seed %d: %s.", seed, err) - } - - k1, err = w.GetSymKey(id1) - if err != nil { - t.Fatalf("failed GetSymKey(id1).") - } - k2, err = w.GetSymKey(id2) - if err == nil { - t.Fatalf("failed GetSymKey(id2): false positive.") - } - if !w.HasSymKey(id1) { - t.Fatalf("failed HasSymKey(id1).") - } - if w.HasSymKey(id2) { - t.Fatalf("failed HasSymKey(id2): false positive.") - } - if k1 == nil { - t.Fatalf("first key does not exist.") - } - if k2 != nil { - t.Fatalf("second key still exist.") - } - - // add existing id, nothing should change - randomKey := make([]byte, aesKeyLength) - mrand.Read(randomKey) - id1, err = w.AddSymKeyDirect(randomKey) - if err != nil { - t.Fatalf("failed AddSymKey with seed %d: %s.", seed, err) - } - - k1, err = w.GetSymKey(id1) - if err != nil { - t.Fatalf("failed w.GetSymKey(id1).") - } - k2, err = w.GetSymKey(id2) - if err == nil { - t.Fatalf("failed w.GetSymKey(id2): false positive.") - } - if !w.HasSymKey(id1) { - t.Fatalf("failed w.HasSymKey(id1).") - } - if w.HasSymKey(id2) { - t.Fatalf("failed w.HasSymKey(id2): false positive.") - } - if k1 == nil { - t.Fatalf("first key does not exist.") - } - if !bytes.Equal(k1, randomKey) { - t.Fatalf("k1 != randomKey.") - } - if k2 != nil { - t.Fatalf("second key already exist.") - } - - id2, err = w.AddSymKeyDirect(randomKey) - if err != nil { - t.Fatalf("failed AddSymKey(id2) with seed %d: %s.", seed, err) - } - k1, err = w.GetSymKey(id1) - if err != nil { - t.Fatalf("failed w.GetSymKey(id1).") - } - k2, err = w.GetSymKey(id2) - if err != nil { - t.Fatalf("failed w.GetSymKey(id2).") - } - if !w.HasSymKey(id1) { - t.Fatalf("HasSymKey(id1) failed.") - } - if !w.HasSymKey(id2) { - t.Fatalf("HasSymKey(id2) failed.") - } - if k1 == nil { - t.Fatalf("k1 does not exist.") - } - if k2 == nil { - t.Fatalf("k2 does not exist.") - } - if !bytes.Equal(k1, k2) { - t.Fatalf("k1 != k2.") - } - if !bytes.Equal(k1, randomKey) { - t.Fatalf("k1 != randomKey.") - } - if len(k1) != aesKeyLength { - t.Fatalf("wrong length of k1.") - } - if len(k2) != aesKeyLength { - t.Fatalf("wrong length of k2.") - } - - w.DeleteSymKey(id1) - k1, err = w.GetSymKey(id1) - if err == nil { - t.Fatalf("failed w.GetSymKey(id1): false positive.") - } - if k1 != nil { - t.Fatalf("failed GetSymKey(id1): false positive.") - } - k2, err = w.GetSymKey(id2) - if err != nil { - t.Fatalf("failed w.GetSymKey(id2).") - } - if w.HasSymKey(id1) { - t.Fatalf("failed to delete first key: still exist.") - } - if !w.HasSymKey(id2) { - t.Fatalf("failed to delete first key: second key does not exist.") - } - if k1 != nil { - t.Fatalf("failed to delete first key.") - } - if k2 == nil { - t.Fatalf("failed to delete first key: second key is nil.") - } - - w.DeleteSymKey(id1) - w.DeleteSymKey(id2) - k1, err = w.GetSymKey(id1) - if err == nil { - t.Fatalf("failed w.GetSymKey(id1): false positive.") - } - k2, err = w.GetSymKey(id2) - if err == nil { - t.Fatalf("failed w.GetSymKey(id2): false positive.") - } - if k1 != nil || k2 != nil { - t.Fatalf("k1 or k2 is not nil") - } - if w.HasSymKey(id1) { - t.Fatalf("failed to delete second key: first key exist.") - } - if w.HasSymKey(id2) { - t.Fatalf("failed to delete second key: still exist.") - } - if k1 != nil { - t.Fatalf("failed to delete second key: first key is not nil.") - } - if k2 != nil { - t.Fatalf("failed to delete second key: second key is not nil.") - } - - randomKey = make([]byte, aesKeyLength+1) - mrand.Read(randomKey) - _, err = w.AddSymKeyDirect(randomKey) - if err == nil { - t.Fatalf("added the key with wrong size, seed %d.", seed) - } - - const password = "arbitrary data here" - id1, err = w.AddSymKeyFromPassword(password) - if err != nil { - t.Fatalf("failed AddSymKeyFromPassword(id1) with seed %d: %s.", seed, err) - } - id2, err = w.AddSymKeyFromPassword(password) - if err != nil { - t.Fatalf("failed AddSymKeyFromPassword(id2) with seed %d: %s.", seed, err) - } - k1, err = w.GetSymKey(id1) - if err != nil { - t.Fatalf("failed w.GetSymKey(id1).") - } - k2, err = w.GetSymKey(id2) - if err != nil { - t.Fatalf("failed w.GetSymKey(id2).") - } - if !w.HasSymKey(id1) { - t.Fatalf("HasSymKey(id1) failed.") - } - if !w.HasSymKey(id2) { - t.Fatalf("HasSymKey(id2) failed.") - } - if k1 == nil { - t.Fatalf("k1 does not exist.") - } - if k2 == nil { - t.Fatalf("k2 does not exist.") - } - if !bytes.Equal(k1, k2) { - t.Fatalf("k1 != k2.") - } - if len(k1) != aesKeyLength { - t.Fatalf("wrong length of k1.") - } - if len(k2) != aesKeyLength { - t.Fatalf("wrong length of k2.") - } - if !validateSymmetricKey(k2) { - t.Fatalf("key validation failed.") - } -} - -func TestExpiry(t *testing.T) { - InitSingleTest() - - w := New(&DefaultConfig) - w.SetMinimumPoW(0.0000001) - defer w.SetMinimumPoW(DefaultMinimumPoW) - w.Start(nil) - defer w.Stop() - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - params.TTL = 1 - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - err = w.Send(env) - if err != nil { - t.Fatalf("failed to send envelope with seed %d: %s.", seed, err) - } - - // wait till received or timeout - var received, expired bool - for j := 0; j < 20; j++ { - time.Sleep(100 * time.Millisecond) - if len(w.Envelopes()) > 0 { - received = true - break - } - } - - if !received { - t.Fatalf("did not receive the sent envelope, seed: %d.", seed) - } - - // wait till expired or timeout - for j := 0; j < 20; j++ { - time.Sleep(100 * time.Millisecond) - if len(w.Envelopes()) == 0 { - expired = true - break - } - } - - if !expired { - t.Fatalf("expire failed, seed: %d.", seed) - } -} - -func TestCustomization(t *testing.T) { - InitSingleTest() - - w := New(&DefaultConfig) - defer w.SetMinimumPoW(DefaultMinimumPoW) - defer w.SetMaxMessageSize(DefaultMaxMessageSize) - w.Start(nil) - defer w.Stop() - - const smallPoW = 0.00001 - - f, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - params.KeySym = f.KeySym - params.Topic = BytesToTopic(f.Topics[2]) - params.PoW = smallPoW - params.TTL = 3600 * 24 // one day - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - err = w.Send(env) - if err == nil { - t.Fatalf("successfully sent envelope with PoW %.06f, false positive (seed %d).", env.PoW(), seed) - } - - w.SetMinimumPoW(smallPoW / 2) - err = w.Send(env) - if err != nil { - t.Fatalf("failed to send envelope with seed %d: %s.", seed, err) - } - - params.TTL++ - msg, err = NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err = msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - w.SetMaxMessageSize(uint32(env.size() - 1)) - err = w.Send(env) - if err == nil { - t.Fatalf("successfully sent oversized envelope (seed %d): false positive.", seed) - } - - w.SetMaxMessageSize(DefaultMaxMessageSize) - err = w.Send(env) - if err != nil { - t.Fatalf("failed to send second envelope with seed %d: %s.", seed, err) - } - - // wait till received or timeout - var received bool - for j := 0; j < 20; j++ { - time.Sleep(100 * time.Millisecond) - if len(w.Envelopes()) > 1 { - received = true - break - } - } - - if !received { - t.Fatalf("did not receive the sent envelope, seed: %d.", seed) - } - - // check w.messages() - id, err := w.Subscribe(f) - if err != nil { - t.Fatalf("failed subscribe with seed %d: %s.", seed, err) - } - time.Sleep(5 * time.Millisecond) - mail := f.Retrieve() - if len(mail) > 0 { - t.Fatalf("received premature mail") - } - - mail = w.Messages(id) - if len(mail) != 2 { - t.Fatalf("failed to get whisper messages") - } -} - -func TestSymmetricSendCycle(t *testing.T) { - InitSingleTest() - - w := New(&DefaultConfig) - defer w.SetMinimumPoW(DefaultMinimumPoW) - defer w.SetMaxMessageSize(DefaultMaxMessageSize) - w.Start(nil) - defer w.Stop() - - filter1, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - filter1.PoW = DefaultMinimumPoW - - // Copy the first filter since some of its fields - // are randomly gnerated. - filter2 := &Filter{ - KeySym: filter1.KeySym, - Topics: filter1.Topics, - PoW: filter1.PoW, - AllowP2P: filter1.AllowP2P, - Messages: make(map[common.Hash]*ReceivedMessage), - } - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - filter1.Src = ¶ms.Src.PublicKey - filter2.Src = ¶ms.Src.PublicKey - - params.KeySym = filter1.KeySym - params.Topic = BytesToTopic(filter1.Topics[2]) - params.PoW = filter1.PoW - params.WorkTime = 10 - params.TTL = 50 - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - _, err = w.Subscribe(filter1) - if err != nil { - t.Fatalf("failed subscribe 1 with seed %d: %s.", seed, err) - } - - _, err = w.Subscribe(filter2) - if err != nil { - t.Fatalf("failed subscribe 2 with seed %d: %s.", seed, err) - } - - err = w.Send(env) - if err != nil { - t.Fatalf("Failed sending envelope with PoW %.06f (seed %d): %s", env.PoW(), seed, err) - } - - // wait till received or timeout - var received bool - for j := 0; j < 200; j++ { - time.Sleep(10 * time.Millisecond) - if len(w.Envelopes()) > 0 { - received = true - break - } - } - - if !received { - t.Fatalf("did not receive the sent envelope, seed: %d.", seed) - } - - // check w.messages() - time.Sleep(5 * time.Millisecond) - mail1 := filter1.Retrieve() - mail2 := filter2.Retrieve() - if len(mail2) == 0 { - t.Fatalf("did not receive any email for filter 2") - } - if len(mail1) == 0 { - t.Fatalf("did not receive any email for filter 1") - } - -} - -func TestSymmetricSendWithoutAKey(t *testing.T) { - InitSingleTest() - - w := New(&DefaultConfig) - defer w.SetMinimumPoW(DefaultMinimumPoW) - defer w.SetMaxMessageSize(DefaultMaxMessageSize) - w.Start(nil) - defer w.Stop() - - filter, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - filter.PoW = DefaultMinimumPoW - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - filter.Src = nil - - params.KeySym = filter.KeySym - params.Topic = BytesToTopic(filter.Topics[2]) - params.PoW = filter.PoW - params.WorkTime = 10 - params.TTL = 50 - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - _, err = w.Subscribe(filter) - if err != nil { - t.Fatalf("failed subscribe 1 with seed %d: %s.", seed, err) - } - - err = w.Send(env) - if err != nil { - t.Fatalf("Failed sending envelope with PoW %.06f (seed %d): %s", env.PoW(), seed, err) - } - - // wait till received or timeout - var received bool - for j := 0; j < 200; j++ { - time.Sleep(10 * time.Millisecond) - if len(w.Envelopes()) > 0 { - received = true - break - } - } - - if !received { - t.Fatalf("did not receive the sent envelope, seed: %d.", seed) - } - - // check w.messages() - time.Sleep(5 * time.Millisecond) - mail := filter.Retrieve() - if len(mail) == 0 { - t.Fatalf("did not receive message in spite of not setting a public key") - } -} - -func TestSymmetricSendKeyMismatch(t *testing.T) { - InitSingleTest() - - w := New(&DefaultConfig) - defer w.SetMinimumPoW(DefaultMinimumPoW) - defer w.SetMaxMessageSize(DefaultMaxMessageSize) - w.Start(nil) - defer w.Stop() - - filter, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - filter.PoW = DefaultMinimumPoW - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - params.KeySym = filter.KeySym - params.Topic = BytesToTopic(filter.Topics[2]) - params.PoW = filter.PoW - params.WorkTime = 10 - params.TTL = 50 - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - _, err = w.Subscribe(filter) - if err != nil { - t.Fatalf("failed subscribe 1 with seed %d: %s.", seed, err) - } - - err = w.Send(env) - if err != nil { - t.Fatalf("Failed sending envelope with PoW %.06f (seed %d): %s", env.PoW(), seed, err) - } - - // wait till received or timeout - var received bool - for j := 0; j < 200; j++ { - time.Sleep(10 * time.Millisecond) - if len(w.Envelopes()) > 0 { - received = true - break - } - } - - if !received { - t.Fatalf("did not receive the sent envelope, seed: %d.", seed) - } - - // check w.messages() - time.Sleep(5 * time.Millisecond) - mail := filter.Retrieve() - if len(mail) > 0 { - t.Fatalf("received a message when keys weren't matching") - } -} From 749037a97cb10e7c3af4909b1014b3ade1715d6c Mon Sep 17 00:00:00 2001 From: JukLee0ira Date: Wed, 23 Oct 2024 11:12:14 +0800 Subject: [PATCH 194/479] whisper: remove whisper (#21487) --- cmd/XDC/config.go | 26 +- cmd/XDC/usage.go | 2 +- cmd/utils/flags.go | 16 +- cmd/utils/utils.go | 10 - cmd/wnode/main.go | 773 ---------------- mobile/geth.go | 12 - whisper/mailserver/mailserver.go | 195 ---- whisper/mailserver/server_test.go | 209 ----- whisper/shhclient/client.go | 194 ---- whisper/whisperv6/api.go | 585 ------------ whisper/whisperv6/api_test.go | 78 -- whisper/whisperv6/benchmarks_test.go | 208 ----- whisper/whisperv6/config.go | 29 - whisper/whisperv6/doc.go | 97 -- whisper/whisperv6/envelope.go | 279 ------ whisper/whisperv6/envelope_test.go | 64 -- whisper/whisperv6/filter.go | 280 ------ whisper/whisperv6/filter_test.go | 867 ------------------ whisper/whisperv6/gen_criteria_json.go | 66 -- whisper/whisperv6/gen_message_json.go | 84 -- whisper/whisperv6/gen_newmessage_json.go | 90 -- whisper/whisperv6/message.go | 355 -------- whisper/whisperv6/message_test.go | 471 ---------- whisper/whisperv6/peer.go | 251 ------ whisper/whisperv6/peer_test.go | 493 ---------- whisper/whisperv6/topic.go | 57 -- whisper/whisperv6/topic_test.go | 134 --- whisper/whisperv6/whisper.go | 1049 ---------------------- whisper/whisperv6/whisper_test.go | 885 ------------------ 29 files changed, 13 insertions(+), 7846 deletions(-) delete mode 100644 cmd/wnode/main.go delete mode 100644 whisper/mailserver/mailserver.go delete mode 100644 whisper/mailserver/server_test.go delete mode 100644 whisper/shhclient/client.go delete mode 100644 whisper/whisperv6/api.go delete mode 100644 whisper/whisperv6/api_test.go delete mode 100644 whisper/whisperv6/benchmarks_test.go delete mode 100644 whisper/whisperv6/config.go delete mode 100644 whisper/whisperv6/doc.go delete mode 100644 whisper/whisperv6/envelope.go delete mode 100644 whisper/whisperv6/envelope_test.go delete mode 100644 whisper/whisperv6/filter.go delete mode 100644 whisper/whisperv6/filter_test.go delete mode 100644 whisper/whisperv6/gen_criteria_json.go delete mode 100644 whisper/whisperv6/gen_message_json.go delete mode 100644 whisper/whisperv6/gen_newmessage_json.go delete mode 100644 whisper/whisperv6/message.go delete mode 100644 whisper/whisperv6/message_test.go delete mode 100644 whisper/whisperv6/peer.go delete mode 100644 whisper/whisperv6/peer_test.go delete mode 100644 whisper/whisperv6/topic.go delete mode 100644 whisper/whisperv6/topic_test.go delete mode 100644 whisper/whisperv6/whisper.go delete mode 100644 whisper/whisperv6/whisper_test.go diff --git a/cmd/XDC/config.go b/cmd/XDC/config.go index 26908a0b2c25..28d422d7122f 100644 --- a/cmd/XDC/config.go +++ b/cmd/XDC/config.go @@ -37,7 +37,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/node" "github.com/XinFinOrg/XDPoSChain/params" - whisper "github.com/XinFinOrg/XDPoSChain/whisper/whisperv6" "github.com/naoina/toml" ) @@ -91,7 +90,6 @@ type Bootnodes struct { type XDCConfig struct { Eth ethconfig.Config - Shh whisper.Config Node node.Config Ethstats ethstatsConfig XDCX XDCx.Config @@ -130,7 +128,6 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, XDCConfig) { // Load defaults. cfg := XDCConfig{ Eth: ethconfig.Defaults, - Shh: whisper.DefaultConfig, XDCX: XDCx.DefaultConfig, Node: defaultNodeConfig(), StakeEnable: true, @@ -212,7 +209,7 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, XDCConfig) { cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name) } - utils.SetShhConfig(ctx, stack, &cfg.Shh) + utils.SetShhConfig(ctx, stack) utils.SetXDCXConfig(ctx, &cfg.XDCX, cfg.Node.DataDir) return stack, cfg } @@ -230,14 +227,13 @@ func applyValues(values []string, params *[]string) { } -// enableWhisper returns true in case one of the whisper flags is set. -func enableWhisper(ctx *cli.Context) bool { +// checkWhisper returns true in case one of the whisper flags is set. +func checkWhisper(ctx *cli.Context) { for _, flag := range whisperFlags { if ctx.GlobalIsSet(flag.GetName()) { - return true + log.Warn("deprecated whisper flag detected. Whisper has been moved to github.com/ethereum/whisper") } } - return false } func makeFullNode(ctx *cli.Context) (*node.Node, XDCConfig) { @@ -248,19 +244,7 @@ func makeFullNode(ctx *cli.Context) (*node.Node, XDCConfig) { utils.RegisterXDCXService(stack, &cfg.XDCX) utils.RegisterEthService(stack, &cfg.Eth) - // Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev mode - shhEnabled := enableWhisper(ctx) - shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DeveloperFlag.Name) - if shhEnabled || shhAutoEnabled { - if ctx.GlobalIsSet(utils.WhisperMaxMessageSizeFlag.Name) { - cfg.Shh.MaxMessageSize = uint32(ctx.Int(utils.WhisperMaxMessageSizeFlag.Name)) - } - if ctx.GlobalIsSet(utils.WhisperMinPOWFlag.Name) { - cfg.Shh.MinimumAcceptedPOW = ctx.Float64(utils.WhisperMinPOWFlag.Name) - } - utils.RegisterShhService(stack, &cfg.Shh) - } - + checkWhisper(ctx) // Add the Ethereum Stats daemon if requested. if cfg.Ethstats.URL != "" { utils.RegisterEthStatsService(stack, cfg.Ethstats.URL) diff --git a/cmd/XDC/usage.go b/cmd/XDC/usage.go index 39a7c4367320..9c1fb8193e05 100644 --- a/cmd/XDC/usage.go +++ b/cmd/XDC/usage.go @@ -218,7 +218,7 @@ var AppHelpFlagGroups = []flagGroup{ }, debug.Flags...), }, //{ - // Name: "WHISPER (EXPERIMENTAL)", + // Name: "WHISPER (deprecated)", // Flags: whisperFlags, //}, { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index a4e7174859c1..25a9d7275851 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -58,7 +58,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/p2p/netutil" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rpc" - whisper "github.com/XinFinOrg/XDPoSChain/whisper/whisperv6" gopsutil "github.com/shirou/gopsutil/mem" "gopkg.in/urfave/cli.v1" ) @@ -598,12 +597,12 @@ var ( WhisperMaxMessageSizeFlag = cli.IntFlag{ Name: "shh.maxmessagesize", Usage: "Max message size accepted", - Value: int(whisper.DefaultMaxMessageSize), + Value: 1024 * 1024, } WhisperMinPOWFlag = cli.Float64Flag{ Name: "shh.pow", Usage: "Minimum POW accepted", - Value: whisper.DefaultMinimumPoW, + Value: 0.2, } XDCXDataDirFlag = DirectoryFlag{ Name: "XDCx.datadir", @@ -1145,12 +1144,11 @@ func checkExclusive(ctx *cli.Context, args ...interface{}) { } // SetShhConfig applies shh-related command line flags to the config. -func SetShhConfig(ctx *cli.Context, stack *node.Node, cfg *whisper.Config) { - if ctx.GlobalIsSet(WhisperMaxMessageSizeFlag.Name) { - cfg.MaxMessageSize = uint32(ctx.GlobalUint(WhisperMaxMessageSizeFlag.Name)) - } - if ctx.GlobalIsSet(WhisperMinPOWFlag.Name) { - cfg.MinimumAcceptedPOW = ctx.GlobalFloat64(WhisperMinPOWFlag.Name) +func SetShhConfig(ctx *cli.Context, stack *node.Node) { + if ctx.GlobalIsSet(WhisperEnabledFlag.Name) || + ctx.GlobalIsSet(WhisperMaxMessageSizeFlag.Name) || + ctx.GlobalIsSet(WhisperMinPOWFlag.Name) { + log.Warn("Whisper support has been deprecated and the code has been moved to github.com/ethereum/whisper") } } diff --git a/cmd/utils/utils.go b/cmd/utils/utils.go index a329fc8c4d2e..bc676c7b08ff 100644 --- a/cmd/utils/utils.go +++ b/cmd/utils/utils.go @@ -9,7 +9,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/ethstats" "github.com/XinFinOrg/XDPoSChain/les" "github.com/XinFinOrg/XDPoSChain/node" - whisper "github.com/XinFinOrg/XDPoSChain/whisper/whisperv6" ) // RegisterEthService adds an Ethereum client to the stack. @@ -38,15 +37,6 @@ func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) { } } -// RegisterShhService configures Whisper and adds it to the given node. -func RegisterShhService(stack *node.Node, cfg *whisper.Config) { - if err := stack.Register(func(n *node.ServiceContext) (node.Service, error) { - return whisper.New(cfg), nil - }); err != nil { - Fatalf("Failed to register the Whisper service: %v", err) - } -} - // RegisterEthStatsService configures the Ethereum Stats daemon and adds it to the node. func RegisterEthStatsService(stack *node.Node, url string) { if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { diff --git a/cmd/wnode/main.go b/cmd/wnode/main.go deleted file mode 100644 index 5fa29ab96c54..000000000000 --- a/cmd/wnode/main.go +++ /dev/null @@ -1,773 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . - -// This is a simple Whisper node. It could be used as a stand-alone bootstrap node. -// Also, could be used for different test and diagnostics purposes. - -package main - -import ( - "bufio" - "crypto/ecdsa" - crand "crypto/rand" - "crypto/sha512" - "encoding/binary" - "encoding/hex" - "flag" - "fmt" - "os" - "path/filepath" - "strconv" - "strings" - "time" - - "github.com/XinFinOrg/XDPoSChain/cmd/utils" - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/console" - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/log" - "github.com/XinFinOrg/XDPoSChain/p2p" - "github.com/XinFinOrg/XDPoSChain/p2p/discover" - "github.com/XinFinOrg/XDPoSChain/p2p/nat" - "github.com/XinFinOrg/XDPoSChain/whisper/mailserver" - whisper "github.com/XinFinOrg/XDPoSChain/whisper/whisperv6" - "golang.org/x/crypto/pbkdf2" -) - -const quitCommand = "~Q" -const entropySize = 32 - -// singletons -var ( - server *p2p.Server - shh *whisper.Whisper - done chan struct{} - mailServer mailserver.WMailServer - entropy [entropySize]byte - - input = bufio.NewReader(os.Stdin) -) - -// encryption -var ( - symKey []byte - pub *ecdsa.PublicKey - asymKey *ecdsa.PrivateKey - nodeid *ecdsa.PrivateKey - topic whisper.TopicType - - asymKeyID string - asymFilterID string - symFilterID string - symPass string - msPassword string -) - -// cmd arguments -var ( - bootstrapMode = flag.Bool("standalone", false, "boostrap node: don't initiate connection to peers, just wait for incoming connections") - forwarderMode = flag.Bool("forwarder", false, "forwarder mode: only forward messages, neither encrypt nor decrypt messages") - mailServerMode = flag.Bool("mailserver", false, "mail server mode: delivers expired messages on demand") - requestMail = flag.Bool("mailclient", false, "request expired messages from the bootstrap server") - asymmetricMode = flag.Bool("asym", false, "use asymmetric encryption") - generateKey = flag.Bool("generatekey", false, "generate and show the private key") - fileExMode = flag.Bool("fileexchange", false, "file exchange mode") - fileReader = flag.Bool("filereader", false, "load and decrypt messages saved as files, display as plain text") - testMode = flag.Bool("test", false, "use of predefined parameters for diagnostics (password, etc.)") - echoMode = flag.Bool("echo", false, "echo mode: prints some arguments for diagnostics") - - argVerbosity = flag.Int("verbosity", int(log.LvlError), "log verbosity level") - argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds") - argWorkTime = flag.Uint("work", 5, "work time in seconds") - argMaxSize = flag.Uint("maxsize", uint(whisper.DefaultMaxMessageSize), "max size of message") - argPoW = flag.Float64("pow", whisper.DefaultMinimumPoW, "PoW for normal messages in float format (e.g. 2.7)") - argServerPoW = flag.Float64("mspow", whisper.DefaultMinimumPoW, "PoW requirement for Mail Server request") - - argIP = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:30303)") - argPub = flag.String("pub", "", "public key for asymmetric encryption") - argDBPath = flag.String("dbpath", "", "path to the server's DB directory") - argIDFile = flag.String("idfile", "", "file name with node id (private key)") - argEnode = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)") - argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)") - argSaveDir = flag.String("savedir", "", "directory where all incoming messages will be saved as files") -) - -func main() { - processArgs() - initialize() - run() - shutdown() -} - -func processArgs() { - flag.Parse() - - if len(*argIDFile) > 0 { - var err error - nodeid, err = crypto.LoadECDSA(*argIDFile) - if err != nil { - utils.Fatalf("Failed to load file [%s]: %s.", *argIDFile, err) - } - } - - const enodePrefix = "enode://" - if len(*argEnode) > 0 { - if (*argEnode)[:len(enodePrefix)] != enodePrefix { - *argEnode = enodePrefix + *argEnode - } - } - - if len(*argTopic) > 0 { - x, err := hex.DecodeString(*argTopic) - if err != nil { - utils.Fatalf("Failed to parse the topic: %s", err) - } - topic = whisper.BytesToTopic(x) - } - - if *asymmetricMode && len(*argPub) > 0 { - var err error - if pub, err = crypto.UnmarshalPubkey(common.FromHex(*argPub)); err != nil { - utils.Fatalf("invalid public key") - } - } - - if len(*argSaveDir) > 0 { - if _, err := os.Stat(*argSaveDir); os.IsNotExist(err) { - utils.Fatalf("Download directory '%s' does not exist", *argSaveDir) - } - } else if *fileExMode { - utils.Fatalf("Parameter 'savedir' is mandatory for file exchange mode") - } - - if *echoMode { - echo() - } -} - -func echo() { - fmt.Printf("ttl = %d \n", *argTTL) - fmt.Printf("workTime = %d \n", *argWorkTime) - fmt.Printf("pow = %f \n", *argPoW) - fmt.Printf("mspow = %f \n", *argServerPoW) - fmt.Printf("ip = %s \n", *argIP) - fmt.Printf("pub = %s \n", common.ToHex(crypto.FromECDSAPub(pub))) - fmt.Printf("idfile = %s \n", *argIDFile) - fmt.Printf("dbpath = %s \n", *argDBPath) - fmt.Printf("boot = %s \n", *argEnode) -} - -func initialize() { - log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*argVerbosity), log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) - - done = make(chan struct{}) - var peers []*discover.Node - var err error - - if *generateKey { - key, err := crypto.GenerateKey() - if err != nil { - utils.Fatalf("Failed to generate private key: %s", err) - } - k := hex.EncodeToString(crypto.FromECDSA(key)) - fmt.Printf("Random private key: %s \n", k) - os.Exit(0) - } - - if *testMode { - symPass = "wwww" // ascii code: 0x77777777 - msPassword = "wwww" - } - - if *bootstrapMode { - if len(*argIP) == 0 { - argIP = scanLineA("Please enter your IP and port (e.g. 127.0.0.1:30348): ") - } - } else if *fileReader { - *bootstrapMode = true - } else { - if len(*argEnode) == 0 { - argEnode = scanLineA("Please enter the peer's enode: ") - } - peer := discover.MustParseNode(*argEnode) - peers = append(peers, peer) - } - - if *mailServerMode { - if len(msPassword) == 0 { - msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ") - if err != nil { - utils.Fatalf("Failed to read Mail Server password: %s", err) - } - } - } - - cfg := &whisper.Config{ - MaxMessageSize: uint32(*argMaxSize), - MinimumAcceptedPOW: *argPoW, - } - - shh = whisper.New(cfg) - - if *argPoW != whisper.DefaultMinimumPoW { - err := shh.SetMinimumPoW(*argPoW) - if err != nil { - utils.Fatalf("Failed to set PoW: %s", err) - } - } - - if uint32(*argMaxSize) != whisper.DefaultMaxMessageSize { - err := shh.SetMaxMessageSize(uint32(*argMaxSize)) - if err != nil { - utils.Fatalf("Failed to set max message size: %s", err) - } - } - - asymKeyID, err = shh.NewKeyPair() - if err != nil { - utils.Fatalf("Failed to generate a new key pair: %s", err) - } - - asymKey, err = shh.GetPrivateKey(asymKeyID) - if err != nil { - utils.Fatalf("Failed to retrieve a new key pair: %s", err) - } - - if nodeid == nil { - tmpID, err := shh.NewKeyPair() - if err != nil { - utils.Fatalf("Failed to generate a new key pair: %s", err) - } - - nodeid, err = shh.GetPrivateKey(tmpID) - if err != nil { - utils.Fatalf("Failed to retrieve a new key pair: %s", err) - } - } - - maxPeers := 80 - if *bootstrapMode { - maxPeers = 800 - } - - _, err = crand.Read(entropy[:]) - if err != nil { - utils.Fatalf("crypto/rand failed: %s", err) - } - - if *mailServerMode { - shh.RegisterServer(&mailServer) - mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW) - } - - server = &p2p.Server{ - Config: p2p.Config{ - PrivateKey: nodeid, - MaxPeers: maxPeers, - Name: common.MakeName("wnode", "6.0"), - Protocols: shh.Protocols(), - ListenAddr: *argIP, - NAT: nat.Any(), - BootstrapNodes: peers, - StaticNodes: peers, - TrustedNodes: peers, - }, - } -} - -func startServer() error { - err := server.Start() - if err != nil { - fmt.Printf("Failed to start Whisper peer: %s.", err) - return err - } - - fmt.Printf("my public key: %s \n", common.ToHex(crypto.FromECDSAPub(&asymKey.PublicKey))) - fmt.Println(server.NodeInfo().Enode) - - if *bootstrapMode { - configureNode() - fmt.Println("Bootstrap Whisper node started") - } else { - fmt.Println("Whisper node started") - // first see if we can establish connection, then ask for user input - waitForConnection(true) - configureNode() - } - - if *fileExMode { - fmt.Printf("Please type the file name to be send. To quit type: '%s'\n", quitCommand) - } else if *fileReader { - fmt.Printf("Please type the file name to be decrypted. To quit type: '%s'\n", quitCommand) - } else if !*forwarderMode { - fmt.Printf("Please type the message. To quit type: '%s'\n", quitCommand) - } - return nil -} - -func isKeyValid(k *ecdsa.PublicKey) bool { - return k.X != nil && k.Y != nil -} - -func configureNode() { - var err error - var p2pAccept bool - - if *forwarderMode { - return - } - - if *asymmetricMode { - if len(*argPub) == 0 { - s := scanLine("Please enter the peer's public key: ") - b := common.FromHex(s) - if b == nil { - utils.Fatalf("Error: can not convert hexadecimal string") - } - if pub, err = crypto.UnmarshalPubkey(b); err != nil { - utils.Fatalf("Error: invalid peer public key") - } - } - } - - if *requestMail { - p2pAccept = true - if len(msPassword) == 0 { - msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ") - if err != nil { - utils.Fatalf("Failed to read Mail Server password: %s", err) - } - } - } - - if !*asymmetricMode && !*forwarderMode { - if len(symPass) == 0 { - symPass, err = console.Stdin.PromptPassword("Please enter the password for symmetric encryption: ") - if err != nil { - utils.Fatalf("Failed to read passphrase: %v", err) - } - } - - symKeyID, err := shh.AddSymKeyFromPassword(symPass) - if err != nil { - utils.Fatalf("Failed to create symmetric key: %s", err) - } - symKey, err = shh.GetSymKey(symKeyID) - if err != nil { - utils.Fatalf("Failed to save symmetric key: %s", err) - } - if len(*argTopic) == 0 { - generateTopic([]byte(symPass)) - } - - fmt.Printf("Filter is configured for the topic: %x \n", topic) - } - - if *mailServerMode { - if len(*argDBPath) == 0 { - argDBPath = scanLineA("Please enter the path to DB file: ") - } - } - - symFilter := whisper.Filter{ - KeySym: symKey, - Topics: [][]byte{topic[:]}, - AllowP2P: p2pAccept, - } - symFilterID, err = shh.Subscribe(&symFilter) - if err != nil { - utils.Fatalf("Failed to install filter: %s", err) - } - - asymFilter := whisper.Filter{ - KeyAsym: asymKey, - Topics: [][]byte{topic[:]}, - AllowP2P: p2pAccept, - } - asymFilterID, err = shh.Subscribe(&asymFilter) - if err != nil { - utils.Fatalf("Failed to install filter: %s", err) - } -} - -func generateTopic(password []byte) { - x := pbkdf2.Key(password, password, 4096, 128, sha512.New) - for i := 0; i < len(x); i++ { - topic[i%whisper.TopicLength] ^= x[i] - } -} - -func waitForConnection(timeout bool) { - var cnt int - var connected bool - for !connected { - time.Sleep(time.Millisecond * 50) - connected = server.PeerCount() > 0 - if timeout { - cnt++ - if cnt > 1000 { - utils.Fatalf("Timeout expired, failed to connect") - } - } - } - - fmt.Println("Connected to peer.") -} - -func run() { - err := startServer() - if err != nil { - return - } - defer server.Stop() - shh.Start(nil) - defer shh.Stop() - - if !*forwarderMode { - go messageLoop() - } - - if *requestMail { - requestExpiredMessagesLoop() - } else if *fileExMode { - sendFilesLoop() - } else if *fileReader { - fileReaderLoop() - } else { - sendLoop() - } -} - -func shutdown() { - close(done) - mailServer.Close() -} - -func sendLoop() { - for { - s := scanLine("") - if s == quitCommand { - fmt.Println("Quit command received") - return - } - sendMsg([]byte(s)) - if *asymmetricMode { - // print your own message for convenience, - // because in asymmetric mode it is impossible to decrypt it - timestamp := time.Now().Unix() - from := crypto.PubkeyToAddress(asymKey.PublicKey) - fmt.Printf("\n%d <%x>: %s\n", timestamp, from, s) - } - } -} - -func sendFilesLoop() { - for { - s := scanLine("") - if s == quitCommand { - fmt.Println("Quit command received") - return - } - b, err := os.ReadFile(s) - if err != nil { - fmt.Printf(">>> Error: %s \n", err) - } else { - h := sendMsg(b) - if (h == common.Hash{}) { - fmt.Printf(">>> Error: message was not sent \n") - } else { - timestamp := time.Now().Unix() - from := crypto.PubkeyToAddress(asymKey.PublicKey) - fmt.Printf("\n%d <%x>: sent message with hash %x\n", timestamp, from, h) - } - } - } -} - -func fileReaderLoop() { - watcher1 := shh.GetFilter(symFilterID) - watcher2 := shh.GetFilter(asymFilterID) - if watcher1 == nil && watcher2 == nil { - fmt.Println("Error: neither symmetric nor asymmetric filter is installed") - return - } - - for { - s := scanLine("") - if s == quitCommand { - fmt.Println("Quit command received") - return - } - raw, err := os.ReadFile(s) - if err != nil { - fmt.Printf(">>> Error: %s \n", err) - } else { - env := whisper.Envelope{Data: raw} // the topic is zero - msg := env.Open(watcher1) // force-open envelope regardless of the topic - if msg == nil { - msg = env.Open(watcher2) - } - if msg == nil { - fmt.Printf(">>> Error: failed to decrypt the message \n") - } else { - printMessageInfo(msg) - } - } - } -} - -func scanLine(prompt string) string { - if len(prompt) > 0 { - fmt.Print(prompt) - } - txt, err := input.ReadString('\n') - if err != nil { - utils.Fatalf("input error: %s", err) - } - txt = strings.TrimRight(txt, "\n\r") - return txt -} - -func scanLineA(prompt string) *string { - s := scanLine(prompt) - return &s -} - -func scanUint(prompt string) uint32 { - s := scanLine(prompt) - i, err := strconv.Atoi(s) - if err != nil { - utils.Fatalf("Fail to parse the lower time limit: %s", err) - } - return uint32(i) -} - -func sendMsg(payload []byte) common.Hash { - params := whisper.MessageParams{ - Src: asymKey, - Dst: pub, - KeySym: symKey, - Payload: payload, - Topic: topic, - TTL: uint32(*argTTL), - PoW: *argPoW, - WorkTime: uint32(*argWorkTime), - } - - msg, err := whisper.NewSentMessage(¶ms) - if err != nil { - utils.Fatalf("failed to create new message: %s", err) - } - - envelope, err := msg.Wrap(¶ms) - if err != nil { - fmt.Printf("failed to seal message: %v \n", err) - return common.Hash{} - } - - err = shh.Send(envelope) - if err != nil { - fmt.Printf("failed to send message: %v \n", err) - return common.Hash{} - } - - return envelope.Hash() -} - -func messageLoop() { - sf := shh.GetFilter(symFilterID) - if sf == nil { - utils.Fatalf("symmetric filter is not installed") - } - - af := shh.GetFilter(asymFilterID) - if af == nil { - utils.Fatalf("asymmetric filter is not installed") - } - - ticker := time.NewTicker(time.Millisecond * 50) - - for { - select { - case <-ticker.C: - m1 := sf.Retrieve() - m2 := af.Retrieve() - messages := append(m1, m2...) - for _, msg := range messages { - reportedOnce := false - if !*fileExMode && len(msg.Payload) <= 2048 { - printMessageInfo(msg) - reportedOnce = true - } - - // All messages are saved upon specifying argSaveDir. - // fileExMode only specifies how messages are displayed on the console after they are saved. - // if fileExMode == true, only the hashes are displayed, since messages might be too big. - if len(*argSaveDir) > 0 { - writeMessageToFile(*argSaveDir, msg, !reportedOnce) - } - } - case <-done: - return - } - } -} - -func printMessageInfo(msg *whisper.ReceivedMessage) { - timestamp := fmt.Sprintf("%d", msg.Sent) // unix timestamp for diagnostics - text := string(msg.Payload) - - var address common.Address - if msg.Src != nil { - address = crypto.PubkeyToAddress(*msg.Src) - } - - if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) { - fmt.Printf("\n%s <%x>: %s\n", timestamp, address, text) // message from myself - } else { - fmt.Printf("\n%s [%x]: %s\n", timestamp, address, text) // message from a peer - } -} - -func writeMessageToFile(dir string, msg *whisper.ReceivedMessage, show bool) { - if len(dir) == 0 { - return - } - - timestamp := fmt.Sprintf("%d", msg.Sent) - name := fmt.Sprintf("%x", msg.EnvelopeHash) - - var address common.Address - if msg.Src != nil { - address = crypto.PubkeyToAddress(*msg.Src) - } - - env := shh.GetEnvelope(msg.EnvelopeHash) - if env == nil { - fmt.Printf("\nUnexpected error: envelope not found: %x\n", msg.EnvelopeHash) - return - } - - // this is a sample code; uncomment if you don't want to save your own messages. - //if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) { - // fmt.Printf("\n%s <%x>: message from myself received, not saved: '%s'\n", timestamp, address, name) - // return - //} - - fullpath := filepath.Join(dir, name) - err := os.WriteFile(fullpath, env.Data, 0644) - if err != nil { - fmt.Printf("\n%s {%x}: message received but not saved: %s\n", timestamp, address, err) - } else if show { - fmt.Printf("\n%s {%x}: message received and saved as '%s' (%d bytes)\n", timestamp, address, name, len(env.Data)) - } -} - -func requestExpiredMessagesLoop() { - var key, peerID, bloom []byte - var timeLow, timeUpp uint32 - var t string - var xt whisper.TopicType - - keyID, err := shh.AddSymKeyFromPassword(msPassword) - if err != nil { - utils.Fatalf("Failed to create symmetric key for mail request: %s", err) - } - key, err = shh.GetSymKey(keyID) - if err != nil { - utils.Fatalf("Failed to save symmetric key for mail request: %s", err) - } - peerID = extractIDFromEnode(*argEnode) - shh.AllowP2PMessagesFromPeer(peerID) - - for { - timeLow = scanUint("Please enter the lower limit of the time range (unix timestamp): ") - timeUpp = scanUint("Please enter the upper limit of the time range (unix timestamp): ") - t = scanLine("Enter the topic (hex). Press enter to request all messages, regardless of the topic: ") - if len(t) == whisper.TopicLength*2 { - x, err := hex.DecodeString(t) - if err != nil { - fmt.Printf("Failed to parse the topic: %s \n", err) - continue - } - xt = whisper.BytesToTopic(x) - bloom = whisper.TopicToBloom(xt) - obfuscateBloom(bloom) - } else if len(t) == 0 { - bloom = whisper.MakeFullNodeBloom() - } else { - fmt.Println("Error: topic is invalid, request aborted") - continue - } - - if timeUpp == 0 { - timeUpp = 0xFFFFFFFF - } - - data := make([]byte, 8, 8+whisper.BloomFilterSize) - binary.BigEndian.PutUint32(data, timeLow) - binary.BigEndian.PutUint32(data[4:], timeUpp) - data = append(data, bloom...) - - var params whisper.MessageParams - params.PoW = *argServerPoW - params.Payload = data - params.KeySym = key - params.Src = asymKey - params.WorkTime = 5 - - msg, err := whisper.NewSentMessage(¶ms) - if err != nil { - utils.Fatalf("failed to create new message: %s", err) - } - env, err := msg.Wrap(¶ms) - if err != nil { - utils.Fatalf("Wrap failed: %s", err) - } - - err = shh.RequestHistoricMessages(peerID, env) - if err != nil { - utils.Fatalf("Failed to send P2P message: %s", err) - } - - time.Sleep(time.Second * 5) - } -} - -func extractIDFromEnode(s string) []byte { - n, err := discover.ParseNode(s) - if err != nil { - utils.Fatalf("Failed to parse enode: %s", err) - } - return n.ID[:] -} - -// obfuscateBloom adds 16 random bits to the the bloom -// filter, in order to obfuscate the containing topics. -// it does so deterministically within every session. -// despite additional bits, it will match on average -// 32000 times less messages than full node's bloom filter. -func obfuscateBloom(bloom []byte) { - const half = entropySize / 2 - for i := 0; i < half; i++ { - x := int(entropy[i]) - if entropy[half+i] < 128 { - x += 256 - } - - bloom[x/8] = 1 << uint(x%8) // set the bit number X - } -} diff --git a/mobile/geth.go b/mobile/geth.go index fd62ea004971..065d8a1a3374 100644 --- a/mobile/geth.go +++ b/mobile/geth.go @@ -34,7 +34,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/p2p" "github.com/XinFinOrg/XDPoSChain/p2p/nat" "github.com/XinFinOrg/XDPoSChain/params" - whisper "github.com/XinFinOrg/XDPoSChain/whisper/whisperv6" ) // NodeConfig represents the collection of configuration values to fine tune the Geth @@ -69,9 +68,6 @@ type NodeConfig struct { // // It has the form "nodename:secret@host:port" EthereumNetStats string - - // WhisperEnabled specifies whether the node should run the Whisper protocol. - WhisperEnabled bool } // defaultNodeConfig contains the default node configuration values to use if all @@ -166,14 +162,6 @@ func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) { } } } - // Register the Whisper protocol if requested - if config.WhisperEnabled { - if err := rawStack.Register(func(*node.ServiceContext) (node.Service, error) { - return whisper.New(&whisper.DefaultConfig), nil - }); err != nil { - return nil, fmt.Errorf("whisper init: %v", err) - } - } return &Node{rawStack}, nil } diff --git a/whisper/mailserver/mailserver.go b/whisper/mailserver/mailserver.go deleted file mode 100644 index 49682454e26a..000000000000 --- a/whisper/mailserver/mailserver.go +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package mailserver - -import ( - "encoding/binary" - "fmt" - - "github.com/XinFinOrg/XDPoSChain/cmd/utils" - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/log" - "github.com/XinFinOrg/XDPoSChain/rlp" - whisper "github.com/XinFinOrg/XDPoSChain/whisper/whisperv6" - "github.com/syndtr/goleveldb/leveldb" - "github.com/syndtr/goleveldb/leveldb/util" -) - -type WMailServer struct { - db *leveldb.DB - w *whisper.Whisper - pow float64 - key []byte -} - -type DBKey struct { - timestamp uint32 - hash common.Hash - raw []byte -} - -func NewDbKey(t uint32, h common.Hash) *DBKey { - const sz = common.HashLength + 4 - var k DBKey - k.timestamp = t - k.hash = h - k.raw = make([]byte, sz) - binary.BigEndian.PutUint32(k.raw, k.timestamp) - copy(k.raw[4:], k.hash[:]) - return &k -} - -func (s *WMailServer) Init(shh *whisper.Whisper, path string, password string, pow float64) { - var err error - if len(path) == 0 { - utils.Fatalf("DB file is not specified") - } - - if len(password) == 0 { - utils.Fatalf("Password is not specified for MailServer") - } - - s.db, err = leveldb.OpenFile(path, nil) - if err != nil { - utils.Fatalf("Failed to open DB file: %s", err) - } - - s.w = shh - s.pow = pow - - MailServerKeyID, err := s.w.AddSymKeyFromPassword(password) - if err != nil { - utils.Fatalf("Failed to create symmetric key for MailServer: %s", err) - } - s.key, err = s.w.GetSymKey(MailServerKeyID) - if err != nil { - utils.Fatalf("Failed to save symmetric key for MailServer") - } -} - -func (s *WMailServer) Close() { - if s.db != nil { - s.db.Close() - } -} - -func (s *WMailServer) Archive(env *whisper.Envelope) { - key := NewDbKey(env.Expiry-env.TTL, env.Hash()) - rawEnvelope, err := rlp.EncodeToBytes(env) - if err != nil { - log.Error(fmt.Sprintf("rlp.EncodeToBytes failed: %s", err)) - } else { - err = s.db.Put(key.raw, rawEnvelope, nil) - if err != nil { - log.Error(fmt.Sprintf("Writing to DB failed: %s", err)) - } - } -} - -func (s *WMailServer) DeliverMail(peer *whisper.Peer, request *whisper.Envelope) { - if peer == nil { - log.Error("Whisper peer is nil") - return - } - - ok, lower, upper, bloom := s.validateRequest(peer.ID(), request) - if ok { - s.processRequest(peer, lower, upper, bloom) - } -} - -func (s *WMailServer) processRequest(peer *whisper.Peer, lower, upper uint32, bloom []byte) []*whisper.Envelope { - ret := make([]*whisper.Envelope, 0) - var err error - var zero common.Hash - kl := NewDbKey(lower, zero) - ku := NewDbKey(upper, zero) - i := s.db.NewIterator(&util.Range{Start: kl.raw, Limit: ku.raw}, nil) - defer i.Release() - - for i.Next() { - var envelope whisper.Envelope - err = rlp.DecodeBytes(i.Value(), &envelope) - if err != nil { - log.Error(fmt.Sprintf("RLP decoding failed: %s", err)) - } - - if whisper.BloomFilterMatch(bloom, envelope.Bloom()) { - if peer == nil { - // used for test purposes - ret = append(ret, &envelope) - } else { - err = s.w.SendP2PDirect(peer, &envelope) - if err != nil { - log.Error(fmt.Sprintf("Failed to send direct message to peer: %s", err)) - return nil - } - } - } - } - - err = i.Error() - if err != nil { - log.Error(fmt.Sprintf("Level DB iterator error: %s", err)) - } - - return ret -} - -func (s *WMailServer) validateRequest(peerID []byte, request *whisper.Envelope) (bool, uint32, uint32, []byte) { - if s.pow > 0.0 && request.PoW() < s.pow { - return false, 0, 0, nil - } - - f := whisper.Filter{KeySym: s.key} - decrypted := request.Open(&f) - if decrypted == nil { - log.Warn("Failed to decrypt p2p request") - return false, 0, 0, nil - } - - src := crypto.FromECDSAPub(decrypted.Src) - if len(src)-len(peerID) == 1 { - src = src[1:] - } - - // if you want to check the signature, you can do it here. e.g.: - // if !bytes.Equal(peerID, src) { - if src == nil { - log.Warn("Wrong signature of p2p request") - return false, 0, 0, nil - } - - var bloom []byte - payloadSize := len(decrypted.Payload) - if payloadSize < 8 { - log.Warn("Undersized p2p request") - return false, 0, 0, nil - } else if payloadSize == 8 { - bloom = whisper.MakeFullNodeBloom() - } else if payloadSize < 8+whisper.BloomFilterSize { - log.Warn("Undersized bloom filter in p2p request") - return false, 0, 0, nil - } else { - bloom = decrypted.Payload[8 : 8+whisper.BloomFilterSize] - } - - lower := binary.BigEndian.Uint32(decrypted.Payload[:4]) - upper := binary.BigEndian.Uint32(decrypted.Payload[4:8]) - return true, lower, upper, bloom -} diff --git a/whisper/mailserver/server_test.go b/whisper/mailserver/server_test.go deleted file mode 100644 index 6925d2999af0..000000000000 --- a/whisper/mailserver/server_test.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package mailserver - -import ( - "bytes" - "crypto/ecdsa" - "encoding/binary" - "math/rand" - "os" - "testing" - "time" - - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/crypto" - whisper "github.com/XinFinOrg/XDPoSChain/whisper/whisperv6" -) - -const powRequirement = 0.00001 - -var keyID string -var shh *whisper.Whisper -var seed = time.Now().Unix() - -type ServerTestParams struct { - topic whisper.TopicType - low uint32 - upp uint32 - key *ecdsa.PrivateKey -} - -func assert(statement bool, text string, t *testing.T) { - if !statement { - t.Fatal(text) - } -} - -func TestDBKey(t *testing.T) { - var h common.Hash - i := uint32(time.Now().Unix()) - k := NewDbKey(i, h) - assert(len(k.raw) == common.HashLength+4, "wrong DB key length", t) - assert(byte(i%0x100) == k.raw[3], "raw representation should be big endian", t) - assert(byte(i/0x1000000) == k.raw[0], "big endian expected", t) -} - -func generateEnvelope(t *testing.T) *whisper.Envelope { - h := crypto.Keccak256Hash([]byte("test sample data")) - params := &whisper.MessageParams{ - KeySym: h[:], - Topic: whisper.TopicType{0x1F, 0x7E, 0xA1, 0x7F}, - Payload: []byte("test payload"), - PoW: powRequirement, - WorkTime: 2, - } - - msg, err := whisper.NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed to wrap with seed %d: %s.", seed, err) - } - return env -} - -func TestMailServer(t *testing.T) { - const password = "password_for_this_test" - const dbPath = "whisper-server-test" - - dir, err := os.MkdirTemp("", dbPath) - if err != nil { - t.Fatal(err) - } - - var server WMailServer - shh = whisper.New(&whisper.DefaultConfig) - shh.RegisterServer(&server) - - server.Init(shh, dir, password, powRequirement) - defer server.Close() - - keyID, err = shh.AddSymKeyFromPassword(password) - if err != nil { - t.Fatalf("Failed to create symmetric key for mail request: %s", err) - } - - rand.Seed(seed) - env := generateEnvelope(t) - server.Archive(env) - deliverTest(t, &server, env) -} - -func deliverTest(t *testing.T, server *WMailServer, env *whisper.Envelope) { - id, err := shh.NewKeyPair() - if err != nil { - t.Fatalf("failed to generate new key pair with seed %d: %s.", seed, err) - } - testPeerID, err := shh.GetPrivateKey(id) - if err != nil { - t.Fatalf("failed to retrieve new key pair with seed %d: %s.", seed, err) - } - birth := env.Expiry - env.TTL - p := &ServerTestParams{ - topic: env.Topic, - low: birth - 1, - upp: birth + 1, - key: testPeerID, - } - - singleRequest(t, server, env, p, true) - - p.low, p.upp = birth+1, 0xffffffff - singleRequest(t, server, env, p, false) - - p.low, p.upp = 0, birth-1 - singleRequest(t, server, env, p, false) - - p.low = birth - 1 - p.upp = birth + 1 - p.topic[0] = 0xFF - singleRequest(t, server, env, p, false) -} - -func singleRequest(t *testing.T, server *WMailServer, env *whisper.Envelope, p *ServerTestParams, expect bool) { - request := createRequest(t, p) - src := crypto.FromECDSAPub(&p.key.PublicKey) - ok, lower, upper, bloom := server.validateRequest(src, request) - if !ok { - t.Fatalf("request validation failed, seed: %d.", seed) - } - if lower != p.low { - t.Fatalf("request validation failed (lower bound), seed: %d.", seed) - } - if upper != p.upp { - t.Fatalf("request validation failed (upper bound), seed: %d.", seed) - } - expectedBloom := whisper.TopicToBloom(p.topic) - if !bytes.Equal(bloom, expectedBloom) { - t.Fatalf("request validation failed (topic), seed: %d.", seed) - } - - var exist bool - mail := server.processRequest(nil, p.low, p.upp, bloom) - for _, msg := range mail { - if msg.Hash() == env.Hash() { - exist = true - break - } - } - - if exist != expect { - t.Fatalf("error: exist = %v, seed: %d.", exist, seed) - } - - src[0]++ - ok, lower, upper, _ = server.validateRequest(src, request) - if !ok { - // request should be valid regardless of signature - t.Fatalf("request validation false negative, seed: %d (lower: %d, upper: %d).", seed, lower, upper) - } -} - -func createRequest(t *testing.T, p *ServerTestParams) *whisper.Envelope { - bloom := whisper.TopicToBloom(p.topic) - data := make([]byte, 8) - binary.BigEndian.PutUint32(data, p.low) - binary.BigEndian.PutUint32(data[4:], p.upp) - data = append(data, bloom...) - - key, err := shh.GetSymKey(keyID) - if err != nil { - t.Fatalf("failed to retrieve sym key with seed %d: %s.", seed, err) - } - - params := &whisper.MessageParams{ - KeySym: key, - Topic: p.topic, - Payload: data, - PoW: powRequirement * 2, - WorkTime: 2, - Src: p.key, - } - - msg, err := whisper.NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed to wrap with seed %d: %s.", seed, err) - } - return env -} diff --git a/whisper/shhclient/client.go b/whisper/shhclient/client.go deleted file mode 100644 index f77bae020785..000000000000 --- a/whisper/shhclient/client.go +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package shhclient - -import ( - "context" - - "github.com/XinFinOrg/XDPoSChain" - "github.com/XinFinOrg/XDPoSChain/common/hexutil" - "github.com/XinFinOrg/XDPoSChain/rpc" - whisper "github.com/XinFinOrg/XDPoSChain/whisper/whisperv6" -) - -// Client defines typed wrappers for the Whisper v6 RPC API. -type Client struct { - c *rpc.Client -} - -// Dial connects a client to the given URL. -func Dial(rawurl string) (*Client, error) { - c, err := rpc.Dial(rawurl) - if err != nil { - return nil, err - } - return NewClient(c), nil -} - -// NewClient creates a client that uses the given RPC client. -func NewClient(c *rpc.Client) *Client { - return &Client{c} -} - -// Version returns the Whisper sub-protocol version. -func (sc *Client) Version(ctx context.Context) (string, error) { - var result string - err := sc.c.CallContext(ctx, &result, "shh_version") - return result, err -} - -// Info returns diagnostic information about the whisper node. -func (sc *Client) Info(ctx context.Context) (whisper.Info, error) { - var info whisper.Info - err := sc.c.CallContext(ctx, &info, "shh_info") - return info, err -} - -// SetMaxMessageSize sets the maximal message size allowed by this node. Incoming -// and outgoing messages with a larger size will be rejected. Whisper message size -// can never exceed the limit imposed by the underlying P2P protocol (10 Mb). -func (sc *Client) SetMaxMessageSize(ctx context.Context, size uint32) error { - var ignored bool - return sc.c.CallContext(ctx, &ignored, "shh_setMaxMessageSize", size) -} - -// SetMinimumPoW (experimental) sets the minimal PoW required by this node. - -// This experimental function was introduced for the future dynamic adjustment of -// PoW requirement. If the node is overwhelmed with messages, it should raise the -// PoW requirement and notify the peers. The new value should be set relative to -// the old value (e.g. double). The old value could be obtained via shh_info call. -func (sc *Client) SetMinimumPoW(ctx context.Context, pow float64) error { - var ignored bool - return sc.c.CallContext(ctx, &ignored, "shh_setMinPoW", pow) -} - -// Marks specific peer trusted, which will allow it to send historic (expired) messages. -// Note This function is not adding new nodes, the node needs to exists as a peer. -func (sc *Client) MarkTrustedPeer(ctx context.Context, enode string) error { - var ignored bool - return sc.c.CallContext(ctx, &ignored, "shh_markTrustedPeer", enode) -} - -// NewKeyPair generates a new public and private key pair for message decryption and encryption. -// It returns an identifier that can be used to refer to the key. -func (sc *Client) NewKeyPair(ctx context.Context) (string, error) { - var id string - return id, sc.c.CallContext(ctx, &id, "shh_newKeyPair") -} - -// AddPrivateKey stored the key pair, and returns its ID. -func (sc *Client) AddPrivateKey(ctx context.Context, key []byte) (string, error) { - var id string - return id, sc.c.CallContext(ctx, &id, "shh_addPrivateKey", hexutil.Bytes(key)) -} - -// DeleteKeyPair delete the specifies key. -func (sc *Client) DeleteKeyPair(ctx context.Context, id string) (string, error) { - var ignored bool - return id, sc.c.CallContext(ctx, &ignored, "shh_deleteKeyPair", id) -} - -// HasKeyPair returns an indication if the node has a private key or -// key pair matching the given ID. -func (sc *Client) HasKeyPair(ctx context.Context, id string) (bool, error) { - var has bool - return has, sc.c.CallContext(ctx, &has, "shh_hasKeyPair", id) -} - -// PublicKey return the public key for a key ID. -func (sc *Client) PublicKey(ctx context.Context, id string) ([]byte, error) { - var key hexutil.Bytes - return []byte(key), sc.c.CallContext(ctx, &key, "shh_getPublicKey", id) -} - -// PrivateKey return the private key for a key ID. -func (sc *Client) PrivateKey(ctx context.Context, id string) ([]byte, error) { - var key hexutil.Bytes - return []byte(key), sc.c.CallContext(ctx, &key, "shh_getPrivateKey", id) -} - -// NewSymmetricKey generates a random symmetric key and returns its identifier. -// Can be used encrypting and decrypting messages where the key is known to both parties. -func (sc *Client) NewSymmetricKey(ctx context.Context) (string, error) { - var id string - return id, sc.c.CallContext(ctx, &id, "shh_newSymKey") -} - -// AddSymmetricKey stores the key, and returns its identifier. -func (sc *Client) AddSymmetricKey(ctx context.Context, key []byte) (string, error) { - var id string - return id, sc.c.CallContext(ctx, &id, "shh_addSymKey", hexutil.Bytes(key)) -} - -// GenerateSymmetricKeyFromPassword generates the key from password, stores it, and returns its identifier. -func (sc *Client) GenerateSymmetricKeyFromPassword(ctx context.Context, passwd []byte) (string, error) { - var id string - return id, sc.c.CallContext(ctx, &id, "shh_generateSymKeyFromPassword", hexutil.Bytes(passwd)) -} - -// HasSymmetricKey returns an indication if the key associated with the given id is stored in the node. -func (sc *Client) HasSymmetricKey(ctx context.Context, id string) (bool, error) { - var found bool - return found, sc.c.CallContext(ctx, &found, "shh_hasSymKey", id) -} - -// GetSymmetricKey returns the symmetric key associated with the given identifier. -func (sc *Client) GetSymmetricKey(ctx context.Context, id string) ([]byte, error) { - var key hexutil.Bytes - return []byte(key), sc.c.CallContext(ctx, &key, "shh_getSymKey", id) -} - -// DeleteSymmetricKey deletes the symmetric key associated with the given identifier. -func (sc *Client) DeleteSymmetricKey(ctx context.Context, id string) error { - var ignored bool - return sc.c.CallContext(ctx, &ignored, "shh_deleteSymKey", id) -} - -// Post a message onto the network. -func (sc *Client) Post(ctx context.Context, message whisper.NewMessage) error { - var ignored bool - return sc.c.CallContext(ctx, &ignored, "shh_post", message) -} - -// SubscribeMessages subscribes to messages that match the given criteria. This method -// is only supported on bi-directional connections such as websockets and IPC. -// NewMessageFilter uses polling and is supported over HTTP. -func (sc *Client) SubscribeMessages(ctx context.Context, criteria whisper.Criteria, ch chan<- *whisper.Message) (XDPoSChain.Subscription, error) { - return sc.c.ShhSubscribe(ctx, ch, "messages", criteria) -} - -// NewMessageFilter creates a filter within the node. This filter can be used to poll -// for new messages (see FilterMessages) that satisfy the given criteria. A filter can -// timeout when it was polled for in whisper.filterTimeout. -func (sc *Client) NewMessageFilter(ctx context.Context, criteria whisper.Criteria) (string, error) { - var id string - return id, sc.c.CallContext(ctx, &id, "shh_newMessageFilter", criteria) -} - -// DeleteMessageFilter removes the filter associated with the given id. -func (sc *Client) DeleteMessageFilter(ctx context.Context, id string) error { - var ignored bool - return sc.c.CallContext(ctx, &ignored, "shh_deleteMessageFilter", id) -} - -// FilterMessages retrieves all messages that are received between the last call to -// this function and match the criteria that where given when the filter was created. -func (sc *Client) FilterMessages(ctx context.Context, id string) ([]*whisper.Message, error) { - var messages []*whisper.Message - return messages, sc.c.CallContext(ctx, &messages, "shh_getFilterMessages", id) -} diff --git a/whisper/whisperv6/api.go b/whisper/whisperv6/api.go deleted file mode 100644 index 8711d6b19538..000000000000 --- a/whisper/whisperv6/api.go +++ /dev/null @@ -1,585 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "context" - "crypto/ecdsa" - "errors" - "fmt" - "sync" - "time" - - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/common/hexutil" - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/log" - "github.com/XinFinOrg/XDPoSChain/p2p/discover" - "github.com/XinFinOrg/XDPoSChain/rpc" -) - -const ( - filterTimeout = 300 // filters are considered timeout out after filterTimeout seconds -) - -// List of errors -var ( - ErrSymAsym = errors.New("specify either a symmetric or an asymmetric key") - ErrInvalidSymmetricKey = errors.New("invalid symmetric key") - ErrInvalidPublicKey = errors.New("invalid public key") - ErrInvalidSigningPubKey = errors.New("invalid signing public key") - ErrTooLowPoW = errors.New("message rejected, PoW too low") - ErrNoTopics = errors.New("missing topic(s)") -) - -// PublicWhisperAPI provides the whisper RPC service that can be -// use publicly without security implications. -type PublicWhisperAPI struct { - w *Whisper - - mu sync.Mutex - lastUsed map[string]time.Time // keeps track when a filter was polled for the last time. -} - -// NewPublicWhisperAPI create a new RPC whisper service. -func NewPublicWhisperAPI(w *Whisper) *PublicWhisperAPI { - api := &PublicWhisperAPI{ - w: w, - lastUsed: make(map[string]time.Time), - } - return api -} - -// Version returns the Whisper sub-protocol version. -func (api *PublicWhisperAPI) Version(ctx context.Context) string { - return ProtocolVersionStr -} - -// Info contains diagnostic information. -type Info struct { - Memory int `json:"memory"` // Memory size of the floating messages in bytes. - Messages int `json:"messages"` // Number of floating messages. - MinPow float64 `json:"minPow"` // Minimal accepted PoW - MaxMessageSize uint32 `json:"maxMessageSize"` // Maximum accepted message size -} - -// Info returns diagnostic information about the whisper node. -func (api *PublicWhisperAPI) Info(ctx context.Context) Info { - stats := api.w.Stats() - return Info{ - Memory: stats.memoryUsed, - Messages: len(api.w.messageQueue) + len(api.w.p2pMsgQueue), - MinPow: api.w.MinPow(), - MaxMessageSize: api.w.MaxMessageSize(), - } -} - -// SetMaxMessageSize sets the maximum message size that is accepted. -// Upper limit is defined by MaxMessageSize. -func (api *PublicWhisperAPI) SetMaxMessageSize(ctx context.Context, size uint32) (bool, error) { - return true, api.w.SetMaxMessageSize(size) -} - -// SetMinPoW sets the minimum PoW, and notifies the peers. -func (api *PublicWhisperAPI) SetMinPoW(ctx context.Context, pow float64) (bool, error) { - return true, api.w.SetMinimumPoW(pow) -} - -// SetBloomFilter sets the new value of bloom filter, and notifies the peers. -func (api *PublicWhisperAPI) SetBloomFilter(ctx context.Context, bloom hexutil.Bytes) (bool, error) { - return true, api.w.SetBloomFilter(bloom) -} - -// MarkTrustedPeer marks a peer trusted, which will allow it to send historic (expired) messages. -// Note: This function is not adding new nodes, the node needs to exists as a peer. -func (api *PublicWhisperAPI) MarkTrustedPeer(ctx context.Context, enode string) (bool, error) { - n, err := discover.ParseNode(enode) - if err != nil { - return false, err - } - return true, api.w.AllowP2PMessagesFromPeer(n.ID[:]) -} - -// NewKeyPair generates a new public and private key pair for message decryption and encryption. -// It returns an ID that can be used to refer to the keypair. -func (api *PublicWhisperAPI) NewKeyPair(ctx context.Context) (string, error) { - return api.w.NewKeyPair() -} - -// AddPrivateKey imports the given private key. -func (api *PublicWhisperAPI) AddPrivateKey(ctx context.Context, privateKey hexutil.Bytes) (string, error) { - key, err := crypto.ToECDSA(privateKey) - if err != nil { - return "", err - } - return api.w.AddKeyPair(key) -} - -// DeleteKeyPair removes the key with the given key if it exists. -func (api *PublicWhisperAPI) DeleteKeyPair(ctx context.Context, key string) (bool, error) { - if ok := api.w.DeleteKeyPair(key); ok { - return true, nil - } - return false, fmt.Errorf("key pair %s not found", key) -} - -// HasKeyPair returns an indication if the node has a key pair that is associated with the given id. -func (api *PublicWhisperAPI) HasKeyPair(ctx context.Context, id string) bool { - return api.w.HasKeyPair(id) -} - -// GetPublicKey returns the public key associated with the given key. The key is the hex -// encoded representation of a key in the form specified in section 4.3.6 of ANSI X9.62. -func (api *PublicWhisperAPI) GetPublicKey(ctx context.Context, id string) (hexutil.Bytes, error) { - key, err := api.w.GetPrivateKey(id) - if err != nil { - return hexutil.Bytes{}, err - } - return crypto.FromECDSAPub(&key.PublicKey), nil -} - -// GetPrivateKey returns the private key associated with the given key. The key is the hex -// encoded representation of a key in the form specified in section 4.3.6 of ANSI X9.62. -func (api *PublicWhisperAPI) GetPrivateKey(ctx context.Context, id string) (hexutil.Bytes, error) { - key, err := api.w.GetPrivateKey(id) - if err != nil { - return hexutil.Bytes{}, err - } - return crypto.FromECDSA(key), nil -} - -// NewSymKey generate a random symmetric key. -// It returns an ID that can be used to refer to the key. -// Can be used encrypting and decrypting messages where the key is known to both parties. -func (api *PublicWhisperAPI) NewSymKey(ctx context.Context) (string, error) { - return api.w.GenerateSymKey() -} - -// AddSymKey import a symmetric key. -// It returns an ID that can be used to refer to the key. -// Can be used encrypting and decrypting messages where the key is known to both parties. -func (api *PublicWhisperAPI) AddSymKey(ctx context.Context, key hexutil.Bytes) (string, error) { - return api.w.AddSymKeyDirect([]byte(key)) -} - -// GenerateSymKeyFromPassword derive a key from the given password, stores it, and returns its ID. -func (api *PublicWhisperAPI) GenerateSymKeyFromPassword(ctx context.Context, passwd string) (string, error) { - return api.w.AddSymKeyFromPassword(passwd) -} - -// HasSymKey returns an indication if the node has a symmetric key associated with the given key. -func (api *PublicWhisperAPI) HasSymKey(ctx context.Context, id string) bool { - return api.w.HasSymKey(id) -} - -// GetSymKey returns the symmetric key associated with the given id. -func (api *PublicWhisperAPI) GetSymKey(ctx context.Context, id string) (hexutil.Bytes, error) { - return api.w.GetSymKey(id) -} - -// DeleteSymKey deletes the symmetric key that is associated with the given id. -func (api *PublicWhisperAPI) DeleteSymKey(ctx context.Context, id string) bool { - return api.w.DeleteSymKey(id) -} - -// MakeLightClient turns the node into light client, which does not forward -// any incoming messages, and sends only messages originated in this node. -func (api *PublicWhisperAPI) MakeLightClient(ctx context.Context) bool { - api.w.lightClient = true - return api.w.lightClient -} - -// CancelLightClient cancels light client mode. -func (api *PublicWhisperAPI) CancelLightClient(ctx context.Context) bool { - api.w.lightClient = false - return !api.w.lightClient -} - -//go:generate gencodec -type NewMessage -field-override newMessageOverride -out gen_newmessage_json.go - -// NewMessage represents a new whisper message that is posted through the RPC. -type NewMessage struct { - SymKeyID string `json:"symKeyID"` - PublicKey []byte `json:"pubKey"` - Sig string `json:"sig"` - TTL uint32 `json:"ttl"` - Topic TopicType `json:"topic"` - Payload []byte `json:"payload"` - Padding []byte `json:"padding"` - PowTime uint32 `json:"powTime"` - PowTarget float64 `json:"powTarget"` - TargetPeer string `json:"targetPeer"` -} - -type newMessageOverride struct { - PublicKey hexutil.Bytes - Payload hexutil.Bytes - Padding hexutil.Bytes -} - -// Post a message on the Whisper network. -func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (bool, error) { - var ( - symKeyGiven = len(req.SymKeyID) > 0 - pubKeyGiven = len(req.PublicKey) > 0 - err error - ) - - // user must specify either a symmetric or an asymmetric key - if (symKeyGiven && pubKeyGiven) || (!symKeyGiven && !pubKeyGiven) { - return false, ErrSymAsym - } - - params := &MessageParams{ - TTL: req.TTL, - Payload: req.Payload, - Padding: req.Padding, - WorkTime: req.PowTime, - PoW: req.PowTarget, - Topic: req.Topic, - } - - // Set key that is used to sign the message - if len(req.Sig) > 0 { - if params.Src, err = api.w.GetPrivateKey(req.Sig); err != nil { - return false, err - } - } - - // Set symmetric key that is used to encrypt the message - if symKeyGiven { - if params.Topic == (TopicType{}) { // topics are mandatory with symmetric encryption - return false, ErrNoTopics - } - if params.KeySym, err = api.w.GetSymKey(req.SymKeyID); err != nil { - return false, err - } - if !validateDataIntegrity(params.KeySym, aesKeyLength) { - return false, ErrInvalidSymmetricKey - } - } - - // Set asymmetric key that is used to encrypt the message - if pubKeyGiven { - if params.Dst, err = crypto.UnmarshalPubkey(req.PublicKey); err != nil { - return false, ErrInvalidPublicKey - } - } - - // encrypt and sent message - whisperMsg, err := NewSentMessage(params) - if err != nil { - return false, err - } - - env, err := whisperMsg.Wrap(params) - if err != nil { - return false, err - } - - // send to specific node (skip PoW check) - if len(req.TargetPeer) > 0 { - n, err := discover.ParseNode(req.TargetPeer) - if err != nil { - return false, fmt.Errorf("failed to parse target peer: %s", err) - } - return true, api.w.SendP2PMessage(n.ID[:], env) - } - - // ensure that the message PoW meets the node's minimum accepted PoW - if req.PowTarget < api.w.MinPow() { - return false, ErrTooLowPoW - } - - return true, api.w.Send(env) -} - -//go:generate gencodec -type Criteria -field-override criteriaOverride -out gen_criteria_json.go - -// Criteria holds various filter options for inbound messages. -type Criteria struct { - SymKeyID string `json:"symKeyID"` - PrivateKeyID string `json:"privateKeyID"` - Sig []byte `json:"sig"` - MinPow float64 `json:"minPow"` - Topics []TopicType `json:"topics"` - AllowP2P bool `json:"allowP2P"` -} - -type criteriaOverride struct { - Sig hexutil.Bytes -} - -// Messages set up a subscription that fires events when messages arrive that match -// the given set of criteria. -func (api *PublicWhisperAPI) Messages(ctx context.Context, crit Criteria) (*rpc.Subscription, error) { - var ( - symKeyGiven = len(crit.SymKeyID) > 0 - pubKeyGiven = len(crit.PrivateKeyID) > 0 - err error - ) - - // ensure that the RPC connection supports subscriptions - notifier, supported := rpc.NotifierFromContext(ctx) - if !supported { - return nil, rpc.ErrNotificationsUnsupported - } - - // user must specify either a symmetric or an asymmetric key - if (symKeyGiven && pubKeyGiven) || (!symKeyGiven && !pubKeyGiven) { - return nil, ErrSymAsym - } - - filter := Filter{ - PoW: crit.MinPow, - Messages: make(map[common.Hash]*ReceivedMessage), - AllowP2P: crit.AllowP2P, - } - - if len(crit.Sig) > 0 { - if filter.Src, err = crypto.UnmarshalPubkey(crit.Sig); err != nil { - return nil, ErrInvalidSigningPubKey - } - } - - for i, bt := range crit.Topics { - if len(bt) == 0 || len(bt) > 4 { - return nil, fmt.Errorf("subscribe: topic %d has wrong size: %d", i, len(bt)) - } - filter.Topics = append(filter.Topics, bt[:]) - } - - // listen for message that are encrypted with the given symmetric key - if symKeyGiven { - if len(filter.Topics) == 0 { - return nil, ErrNoTopics - } - key, err := api.w.GetSymKey(crit.SymKeyID) - if err != nil { - return nil, err - } - if !validateDataIntegrity(key, aesKeyLength) { - return nil, ErrInvalidSymmetricKey - } - filter.KeySym = key - filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym) - } - - // listen for messages that are encrypted with the given public key - if pubKeyGiven { - filter.KeyAsym, err = api.w.GetPrivateKey(crit.PrivateKeyID) - if err != nil || filter.KeyAsym == nil { - return nil, ErrInvalidPublicKey - } - } - - id, err := api.w.Subscribe(&filter) - if err != nil { - return nil, err - } - - // create subscription and start waiting for message events - rpcSub := notifier.CreateSubscription() - go func() { - // for now poll internally, refactor whisper internal for channel support - ticker := time.NewTicker(250 * time.Millisecond) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - if filter := api.w.GetFilter(id); filter != nil { - for _, rpcMessage := range toMessage(filter.Retrieve()) { - if err := notifier.Notify(rpcSub.ID, rpcMessage); err != nil { - log.Error("Failed to send notification", "err", err) - } - } - } - case <-rpcSub.Err(): - api.w.Unsubscribe(id) - return - case <-notifier.Closed(): - api.w.Unsubscribe(id) - return - } - } - }() - - return rpcSub, nil -} - -//go:generate gencodec -type Message -field-override messageOverride -out gen_message_json.go - -// Message is the RPC representation of a whisper message. -type Message struct { - Sig []byte `json:"sig,omitempty"` - TTL uint32 `json:"ttl"` - Timestamp uint32 `json:"timestamp"` - Topic TopicType `json:"topic"` - Payload []byte `json:"payload"` - Padding []byte `json:"padding"` - PoW float64 `json:"pow"` - Hash []byte `json:"hash"` - Dst []byte `json:"recipientPublicKey,omitempty"` -} - -type messageOverride struct { - Sig hexutil.Bytes - Payload hexutil.Bytes - Padding hexutil.Bytes - Hash hexutil.Bytes - Dst hexutil.Bytes -} - -// ToWhisperMessage converts an internal message into an API version. -func ToWhisperMessage(message *ReceivedMessage) *Message { - msg := Message{ - Payload: message.Payload, - Padding: message.Padding, - Timestamp: message.Sent, - TTL: message.TTL, - PoW: message.PoW, - Hash: message.EnvelopeHash.Bytes(), - Topic: message.Topic, - } - - if message.Dst != nil { - b := crypto.FromECDSAPub(message.Dst) - if b != nil { - msg.Dst = b - } - } - - if isMessageSigned(message.Raw[0]) { - b := crypto.FromECDSAPub(message.SigToPubKey()) - if b != nil { - msg.Sig = b - } - } - - return &msg -} - -// toMessage converts a set of messages to its RPC representation. -func toMessage(messages []*ReceivedMessage) []*Message { - msgs := make([]*Message, len(messages)) - for i, msg := range messages { - msgs[i] = ToWhisperMessage(msg) - } - return msgs -} - -// GetFilterMessages returns the messages that match the filter criteria and -// are received between the last poll and now. -func (api *PublicWhisperAPI) GetFilterMessages(id string) ([]*Message, error) { - api.mu.Lock() - f := api.w.GetFilter(id) - if f == nil { - api.mu.Unlock() - return nil, errors.New("filter not found") - } - api.lastUsed[id] = time.Now() - api.mu.Unlock() - - receivedMessages := f.Retrieve() - messages := make([]*Message, 0, len(receivedMessages)) - for _, msg := range receivedMessages { - messages = append(messages, ToWhisperMessage(msg)) - } - - return messages, nil -} - -// DeleteMessageFilter deletes a filter. -func (api *PublicWhisperAPI) DeleteMessageFilter(id string) (bool, error) { - api.mu.Lock() - defer api.mu.Unlock() - - delete(api.lastUsed, id) - return true, api.w.Unsubscribe(id) -} - -// NewMessageFilter creates a new filter that can be used to poll for -// (new) messages that satisfy the given criteria. -func (api *PublicWhisperAPI) NewMessageFilter(req Criteria) (string, error) { - var ( - src *ecdsa.PublicKey - keySym []byte - keyAsym *ecdsa.PrivateKey - topics [][]byte - - symKeyGiven = len(req.SymKeyID) > 0 - asymKeyGiven = len(req.PrivateKeyID) > 0 - - err error - ) - - // user must specify either a symmetric or an asymmetric key - if (symKeyGiven && asymKeyGiven) || (!symKeyGiven && !asymKeyGiven) { - return "", ErrSymAsym - } - - if len(req.Sig) > 0 { - if src, err = crypto.UnmarshalPubkey(req.Sig); err != nil { - return "", ErrInvalidSigningPubKey - } - } - - if symKeyGiven { - if keySym, err = api.w.GetSymKey(req.SymKeyID); err != nil { - return "", err - } - if !validateDataIntegrity(keySym, aesKeyLength) { - return "", ErrInvalidSymmetricKey - } - } - - if asymKeyGiven { - if keyAsym, err = api.w.GetPrivateKey(req.PrivateKeyID); err != nil { - return "", err - } - } - - if len(req.Topics) > 0 { - topics = make([][]byte, len(req.Topics)) - for i, topic := range req.Topics { - topics[i] = make([]byte, TopicLength) - copy(topics[i], topic[:]) - } - } - - f := &Filter{ - Src: src, - KeySym: keySym, - KeyAsym: keyAsym, - PoW: req.MinPow, - AllowP2P: req.AllowP2P, - Topics: topics, - Messages: make(map[common.Hash]*ReceivedMessage), - } - - id, err := api.w.Subscribe(f) - if err != nil { - return "", err - } - - api.mu.Lock() - api.lastUsed[id] = time.Now() - api.mu.Unlock() - - return id, nil -} diff --git a/whisper/whisperv6/api_test.go b/whisper/whisperv6/api_test.go deleted file mode 100644 index 99e36673569c..000000000000 --- a/whisper/whisperv6/api_test.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "bytes" - "crypto/ecdsa" - "testing" - "time" - - "github.com/XinFinOrg/XDPoSChain/common" - mapset "github.com/deckarep/golang-set" -) - -func TestMultipleTopicCopyInNewMessageFilter(t *testing.T) { - w := &Whisper{ - privateKeys: make(map[string]*ecdsa.PrivateKey), - symKeys: make(map[string][]byte), - envelopes: make(map[common.Hash]*Envelope), - expirations: make(map[uint32]mapset.Set), - peers: make(map[*Peer]struct{}), - messageQueue: make(chan *Envelope, messageQueueLimit), - p2pMsgQueue: make(chan *Envelope, messageQueueLimit), - quit: make(chan struct{}), - syncAllowance: DefaultSyncAllowance, - } - w.filters = NewFilters(w) - - keyID, err := w.GenerateSymKey() - if err != nil { - t.Fatalf("Error generating symmetric key: %v", err) - } - api := PublicWhisperAPI{ - w: w, - lastUsed: make(map[string]time.Time), - } - - t1 := [4]byte{0xde, 0xea, 0xbe, 0xef} - t2 := [4]byte{0xca, 0xfe, 0xde, 0xca} - - crit := Criteria{ - SymKeyID: keyID, - Topics: []TopicType{TopicType(t1), TopicType(t2)}, - } - - _, err = api.NewMessageFilter(crit) - if err != nil { - t.Fatalf("Error creating the filter: %v", err) - } - - found := false - candidates := w.filters.getWatchersByTopic(TopicType(t1)) - for _, f := range candidates { - if len(f.Topics) == 2 { - if bytes.Equal(f.Topics[0], t1[:]) && bytes.Equal(f.Topics[1], t2[:]) { - found = true - } - } - } - - if !found { - t.Fatalf("Could not find filter with both topics") - } -} diff --git a/whisper/whisperv6/benchmarks_test.go b/whisper/whisperv6/benchmarks_test.go deleted file mode 100644 index 89cf40e8b39c..000000000000 --- a/whisper/whisperv6/benchmarks_test.go +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "crypto/sha256" - "testing" - - "github.com/XinFinOrg/XDPoSChain/crypto" - "golang.org/x/crypto/pbkdf2" -) - -func BenchmarkDeriveKeyMaterial(b *testing.B) { - for i := 0; i < b.N; i++ { - pbkdf2.Key([]byte("test"), nil, 65356, aesKeyLength, sha256.New) - } -} - -func BenchmarkEncryptionSym(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - for i := 0; i < b.N; i++ { - msg, _ := NewSentMessage(params) - _, err := msg.Wrap(params) - if err != nil { - b.Errorf("failed Wrap with seed %d: %s.", seed, err) - b.Errorf("i = %d, len(msg.Raw) = %d, params.Payload = %d.", i, len(msg.Raw), len(params.Payload)) - return - } - } -} - -func BenchmarkEncryptionAsym(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - key, err := crypto.GenerateKey() - if err != nil { - b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - params.KeySym = nil - params.Dst = &key.PublicKey - - for i := 0; i < b.N; i++ { - msg, _ := NewSentMessage(params) - _, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - } -} - -func BenchmarkDecryptionSymValid(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, _ := NewSentMessage(params) - env, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - f := Filter{KeySym: params.KeySym} - - for i := 0; i < b.N; i++ { - msg := env.Open(&f) - if msg == nil { - b.Fatalf("failed to open with seed %d.", seed) - } - } -} - -func BenchmarkDecryptionSymInvalid(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, _ := NewSentMessage(params) - env, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - f := Filter{KeySym: []byte("arbitrary stuff here")} - - for i := 0; i < b.N; i++ { - msg := env.Open(&f) - if msg != nil { - b.Fatalf("opened envelope with invalid key, seed: %d.", seed) - } - } -} - -func BenchmarkDecryptionAsymValid(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - key, err := crypto.GenerateKey() - if err != nil { - b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - f := Filter{KeyAsym: key} - params.KeySym = nil - params.Dst = &key.PublicKey - msg, _ := NewSentMessage(params) - env, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - for i := 0; i < b.N; i++ { - msg := env.Open(&f) - if msg == nil { - b.Fatalf("fail to open, seed: %d.", seed) - } - } -} - -func BenchmarkDecryptionAsymInvalid(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - key, err := crypto.GenerateKey() - if err != nil { - b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - params.KeySym = nil - params.Dst = &key.PublicKey - msg, _ := NewSentMessage(params) - env, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - key, err = crypto.GenerateKey() - if err != nil { - b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - f := Filter{KeyAsym: key} - - for i := 0; i < b.N; i++ { - msg := env.Open(&f) - if msg != nil { - b.Fatalf("opened envelope with invalid key, seed: %d.", seed) - } - } -} - -func increment(x []byte) { - for i := 0; i < len(x); i++ { - x[i]++ - if x[i] != 0 { - break - } - } -} - -func BenchmarkPoW(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - params.Payload = make([]byte, 32) - params.PoW = 10.0 - params.TTL = 1 - - for i := 0; i < b.N; i++ { - increment(params.Payload) - msg, _ := NewSentMessage(params) - _, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - } -} diff --git a/whisper/whisperv6/config.go b/whisper/whisperv6/config.go deleted file mode 100644 index 61419de00758..000000000000 --- a/whisper/whisperv6/config.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -// Config represents the configuration state of a whisper node. -type Config struct { - MaxMessageSize uint32 `toml:",omitempty"` - MinimumAcceptedPOW float64 `toml:",omitempty"` -} - -// DefaultConfig represents (shocker!) the default configuration. -var DefaultConfig = Config{ - MaxMessageSize: DefaultMaxMessageSize, - MinimumAcceptedPOW: DefaultMinimumPoW, -} diff --git a/whisper/whisperv6/doc.go b/whisper/whisperv6/doc.go deleted file mode 100644 index 066a9766d4d8..000000000000 --- a/whisper/whisperv6/doc.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -/* -Package whisper implements the Whisper protocol (version 6). - -Whisper combines aspects of both DHTs and datagram messaging systems (e.g. UDP). -As such it may be likened and compared to both, not dissimilar to the -matter/energy duality (apologies to physicists for the blatant abuse of a -fundamental and beautiful natural principle). - -Whisper is a pure identity-based messaging system. Whisper provides a low-level -(non-application-specific) but easily-accessible API without being based upon -or prejudiced by the low-level hardware attributes and characteristics, -particularly the notion of singular endpoints. -*/ - -// Contains the Whisper protocol constant definitions - -package whisperv6 - -import ( - "fmt" - "time" -) - -// Whisper protocol parameters -const ( - ProtocolVersion = uint64(6) // Protocol version number - ProtocolVersionStr = "6.0" // The same, as a string - ProtocolName = "shh" // Nickname of the protocol in geth - - // whisper protocol message codes, according to EIP-627 - statusCode = 0 // used by whisper protocol - messagesCode = 1 // normal whisper message - powRequirementCode = 2 // PoW requirement - bloomFilterExCode = 3 // bloom filter exchange - p2pRequestCode = 126 // peer-to-peer message, used by Dapp protocol - p2pMessageCode = 127 // peer-to-peer message (to be consumed by the peer, but not forwarded any further) - NumberOfMessageCodes = 128 - - SizeMask = byte(3) // mask used to extract the size of payload size field from the flags - signatureFlag = byte(4) - - TopicLength = 4 // in bytes - signatureLength = 65 // in bytes - aesKeyLength = 32 // in bytes - aesNonceLength = 12 // in bytes; for more info please see cipher.gcmStandardNonceSize & aesgcm.NonceSize() - keyIDSize = 32 // in bytes - BloomFilterSize = 64 // in bytes - flagsLength = 1 - - EnvelopeHeaderLength = 20 - - MaxMessageSize = uint32(10 * 1024 * 1024) // maximum accepted size of a message. - DefaultMaxMessageSize = uint32(1024 * 1024) - DefaultMinimumPoW = 0.2 - - padSizeLimit = 256 // just an arbitrary number, could be changed without breaking the protocol - messageQueueLimit = 1024 - - expirationCycle = time.Second - transmissionCycle = 300 * time.Millisecond - - DefaultTTL = 50 // seconds - DefaultSyncAllowance = 10 // seconds -) - -type unknownVersionError uint64 - -func (e unknownVersionError) Error() string { - return fmt.Sprintf("invalid envelope version %d", uint64(e)) -} - -// MailServer represents a mail server, capable of -// archiving the old messages for subsequent delivery -// to the peers. Any implementation must ensure that both -// functions are thread-safe. Also, they must return ASAP. -// DeliverMail should use directMessagesCode for delivery, -// in order to bypass the expiry checks. -type MailServer interface { - Archive(env *Envelope) - DeliverMail(whisperPeer *Peer, request *Envelope) -} diff --git a/whisper/whisperv6/envelope.go b/whisper/whisperv6/envelope.go deleted file mode 100644 index 5bcc59c99b8b..000000000000 --- a/whisper/whisperv6/envelope.go +++ /dev/null @@ -1,279 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains the Whisper protocol Envelope element. - -package whisperv6 - -import ( - "crypto/ecdsa" - "encoding/binary" - "fmt" - gmath "math" - "math/big" - "time" - - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/common/math" - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/ecies" - "github.com/XinFinOrg/XDPoSChain/rlp" -) - -// Envelope represents a clear-text data packet to transmit through the Whisper -// network. Its contents may or may not be encrypted and signed. -type Envelope struct { - Expiry uint32 - TTL uint32 - Topic TopicType - Data []byte - Nonce uint64 - - pow float64 // Message-specific PoW as described in the Whisper specification. - - // the following variables should not be accessed directly, use the corresponding function instead: Hash(), Bloom() - hash common.Hash // Cached hash of the envelope to avoid rehashing every time. - bloom []byte -} - -// size returns the size of envelope as it is sent (i.e. public fields only) -func (e *Envelope) size() int { - return EnvelopeHeaderLength + len(e.Data) -} - -// rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce. -func (e *Envelope) rlpWithoutNonce() []byte { - res, _ := rlp.EncodeToBytes([]interface{}{e.Expiry, e.TTL, e.Topic, e.Data}) - return res -} - -// NewEnvelope wraps a Whisper message with expiration and destination data -// included into an envelope for network forwarding. -func NewEnvelope(ttl uint32, topic TopicType, msg *sentMessage) *Envelope { - env := Envelope{ - Expiry: uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()), - TTL: ttl, - Topic: topic, - Data: msg.Raw, - Nonce: 0, - } - - return &env -} - -// Seal closes the envelope by spending the requested amount of time as a proof -// of work on hashing the data. -func (e *Envelope) Seal(options *MessageParams) error { - if options.PoW == 0 { - // PoW is not required - return nil - } - - var target, bestBit int - if options.PoW < 0 { - // target is not set - the function should run for a period - // of time specified in WorkTime param. Since we can predict - // the execution time, we can also adjust Expiry. - e.Expiry += options.WorkTime - } else { - target = e.powToFirstBit(options.PoW) - } - - buf := make([]byte, 64) - h := crypto.Keccak256(e.rlpWithoutNonce()) - copy(buf[:32], h) - - finish := time.Now().Add(time.Duration(options.WorkTime) * time.Second).UnixNano() - for nonce := uint64(0); time.Now().UnixNano() < finish; { - for i := 0; i < 1024; i++ { - binary.BigEndian.PutUint64(buf[56:], nonce) - d := new(big.Int).SetBytes(crypto.Keccak256(buf)) - firstBit := math.FirstBitSet(d) - if firstBit > bestBit { - e.Nonce, bestBit = nonce, firstBit - if target > 0 && bestBit >= target { - return nil - } - } - nonce++ - } - } - - if target > 0 && bestBit < target { - return fmt.Errorf("failed to reach the PoW target, specified pow time (%d seconds) was insufficient", options.WorkTime) - } - - return nil -} - -// PoW computes (if necessary) and returns the proof of work target -// of the envelope. -func (e *Envelope) PoW() float64 { - if e.pow == 0 { - e.calculatePoW(0) - } - return e.pow -} - -func (e *Envelope) calculatePoW(diff uint32) { - buf := make([]byte, 64) - h := crypto.Keccak256(e.rlpWithoutNonce()) - copy(buf[:32], h) - binary.BigEndian.PutUint64(buf[56:], e.Nonce) - d := new(big.Int).SetBytes(crypto.Keccak256(buf)) - firstBit := math.FirstBitSet(d) - x := gmath.Pow(2, float64(firstBit)) - x /= float64(e.size()) - x /= float64(e.TTL + diff) - e.pow = x -} - -func (e *Envelope) powToFirstBit(pow float64) int { - x := pow - x *= float64(e.size()) - x *= float64(e.TTL) - bits := gmath.Log2(x) - bits = gmath.Ceil(bits) - res := int(bits) - if res < 1 { - res = 1 - } - return res -} - -// Hash returns the SHA3 hash of the envelope, calculating it if not yet done. -func (e *Envelope) Hash() common.Hash { - if (e.hash == common.Hash{}) { - encoded, _ := rlp.EncodeToBytes(e) - e.hash = crypto.Keccak256Hash(encoded) - } - return e.hash -} - -// DecodeRLP decodes an Envelope from an RLP data stream. -func (e *Envelope) DecodeRLP(s *rlp.Stream) error { - raw, err := s.Raw() - if err != nil { - return err - } - // The decoding of Envelope uses the struct fields but also needs - // to compute the hash of the whole RLP-encoded envelope. This - // type has the same structure as Envelope but is not an - // rlp.Decoder (does not implement DecodeRLP function). - // Only public members will be encoded. - type rlpenv Envelope - if err := rlp.DecodeBytes(raw, (*rlpenv)(e)); err != nil { - return err - } - e.hash = crypto.Keccak256Hash(raw) - return nil -} - -// OpenAsymmetric tries to decrypt an envelope, potentially encrypted with a particular key. -func (e *Envelope) OpenAsymmetric(key *ecdsa.PrivateKey) (*ReceivedMessage, error) { - message := &ReceivedMessage{Raw: e.Data} - err := message.decryptAsymmetric(key) - switch err { - case nil: - return message, nil - case ecies.ErrInvalidPublicKey: // addressed to somebody else - return nil, err - default: - return nil, fmt.Errorf("unable to open envelope, decrypt failed: %v", err) - } -} - -// OpenSymmetric tries to decrypt an envelope, potentially encrypted with a particular key. -func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) { - msg = &ReceivedMessage{Raw: e.Data} - err = msg.decryptSymmetric(key) - if err != nil { - msg = nil - } - return msg, err -} - -// Open tries to decrypt an envelope, and populates the message fields in case of success. -func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) { - if watcher == nil { - return nil - } - - // The API interface forbids filters doing both symmetric and asymmetric encryption. - if watcher.expectsAsymmetricEncryption() && watcher.expectsSymmetricEncryption() { - return nil - } - - if watcher.expectsAsymmetricEncryption() { - msg, _ = e.OpenAsymmetric(watcher.KeyAsym) - if msg != nil { - msg.Dst = &watcher.KeyAsym.PublicKey - } - } else if watcher.expectsSymmetricEncryption() { - msg, _ = e.OpenSymmetric(watcher.KeySym) - if msg != nil { - msg.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym) - } - } - - if msg != nil { - ok := msg.ValidateAndParse() - if !ok { - return nil - } - msg.Topic = e.Topic - msg.PoW = e.PoW() - msg.TTL = e.TTL - msg.Sent = e.Expiry - e.TTL - msg.EnvelopeHash = e.Hash() - } - return msg -} - -// Bloom maps 4-bytes Topic into 64-byte bloom filter with 3 bits set (at most). -func (e *Envelope) Bloom() []byte { - if e.bloom == nil { - e.bloom = TopicToBloom(e.Topic) - } - return e.bloom -} - -// TopicToBloom converts the topic (4 bytes) to the bloom filter (64 bytes) -func TopicToBloom(topic TopicType) []byte { - b := make([]byte, BloomFilterSize) - var index [3]int - for j := 0; j < 3; j++ { - index[j] = int(topic[j]) - if (topic[3] & (1 << uint(j))) != 0 { - index[j] += 256 - } - } - - for j := 0; j < 3; j++ { - byteIndex := index[j] / 8 - bitIndex := index[j] % 8 - b[byteIndex] = (1 << uint(bitIndex)) - } - return b -} - -// GetEnvelope retrieves an envelope from the message queue by its hash. -// It returns nil if the envelope can not be found. -func (w *Whisper) GetEnvelope(hash common.Hash) *Envelope { - w.poolMu.RLock() - defer w.poolMu.RUnlock() - return w.envelopes[hash] -} diff --git a/whisper/whisperv6/envelope_test.go b/whisper/whisperv6/envelope_test.go deleted file mode 100644 index 4d0330a1f176..000000000000 --- a/whisper/whisperv6/envelope_test.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains the tests associated with the Whisper protocol Envelope object. - -package whisperv6 - -import ( - mrand "math/rand" - "testing" - - "github.com/XinFinOrg/XDPoSChain/crypto" -) - -func TestEnvelopeOpenAcceptsOnlyOneKeyTypeInFilter(t *testing.T) { - symKey := make([]byte, aesKeyLength) - mrand.Read(symKey) - - asymKey, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - - params := MessageParams{ - PoW: 0.01, - WorkTime: 1, - TTL: uint32(mrand.Intn(1024)), - Payload: make([]byte, 50), - KeySym: symKey, - Dst: nil, - } - - mrand.Read(params.Payload) - - msg, err := NewSentMessage(¶ms) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - - e, err := msg.Wrap(¶ms) - if err != nil { - t.Fatalf("Failed to Wrap the message in an envelope with seed %d: %s", seed, err) - } - - f := Filter{KeySym: symKey, KeyAsym: asymKey} - - decrypted := e.Open(&f) - if decrypted != nil { - t.Fatalf("Managed to decrypt a message with an invalid filter, seed %d", seed) - } -} diff --git a/whisper/whisperv6/filter.go b/whisper/whisperv6/filter.go deleted file mode 100644 index 801954c7c309..000000000000 --- a/whisper/whisperv6/filter.go +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "crypto/ecdsa" - "errors" - "fmt" - "sync" - - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/log" -) - -// Filter represents a Whisper message filter -type Filter struct { - Src *ecdsa.PublicKey // Sender of the message - KeyAsym *ecdsa.PrivateKey // Private Key of recipient - KeySym []byte // Key associated with the Topic - Topics [][]byte // Topics to filter messages with - PoW float64 // Proof of work as described in the Whisper spec - AllowP2P bool // Indicates whether this filter is interested in direct peer-to-peer messages - SymKeyHash common.Hash // The Keccak256Hash of the symmetric key, needed for optimization - id string // unique identifier - - Messages map[common.Hash]*ReceivedMessage - mutex sync.RWMutex -} - -// Filters represents a collection of filters -type Filters struct { - watchers map[string]*Filter - - topicMatcher map[TopicType]map[*Filter]struct{} // map a topic to the filters that are interested in being notified when a message matches that topic - allTopicsMatcher map[*Filter]struct{} // list all the filters that will be notified of a new message, no matter what its topic is - - whisper *Whisper - mutex sync.RWMutex -} - -// NewFilters returns a newly created filter collection -func NewFilters(w *Whisper) *Filters { - return &Filters{ - watchers: make(map[string]*Filter), - topicMatcher: make(map[TopicType]map[*Filter]struct{}), - allTopicsMatcher: make(map[*Filter]struct{}), - whisper: w, - } -} - -// Install will add a new filter to the filter collection -func (fs *Filters) Install(watcher *Filter) (string, error) { - if watcher.KeySym != nil && watcher.KeyAsym != nil { - return "", errors.New("filters must choose between symmetric and asymmetric keys") - } - - if watcher.Messages == nil { - watcher.Messages = make(map[common.Hash]*ReceivedMessage) - } - - id, err := GenerateRandomID() - if err != nil { - return "", err - } - - fs.mutex.Lock() - defer fs.mutex.Unlock() - - if fs.watchers[id] != nil { - return "", errors.New("failed to generate unique ID") - } - - if watcher.expectsSymmetricEncryption() { - watcher.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym) - } - - watcher.id = id - fs.watchers[id] = watcher - fs.addTopicMatcher(watcher) - return id, err -} - -// Uninstall will remove a filter whose id has been specified from -// the filter collection -func (fs *Filters) Uninstall(id string) bool { - fs.mutex.Lock() - defer fs.mutex.Unlock() - if fs.watchers[id] != nil { - fs.removeFromTopicMatchers(fs.watchers[id]) - delete(fs.watchers, id) - return true - } - return false -} - -// addTopicMatcher adds a filter to the topic matchers. -// If the filter's Topics array is empty, it will be tried on every topic. -// Otherwise, it will be tried on the topics specified. -func (fs *Filters) addTopicMatcher(watcher *Filter) { - if len(watcher.Topics) == 0 { - fs.allTopicsMatcher[watcher] = struct{}{} - } else { - for _, t := range watcher.Topics { - topic := BytesToTopic(t) - if fs.topicMatcher[topic] == nil { - fs.topicMatcher[topic] = make(map[*Filter]struct{}) - } - fs.topicMatcher[topic][watcher] = struct{}{} - } - } -} - -// removeFromTopicMatchers removes a filter from the topic matchers -func (fs *Filters) removeFromTopicMatchers(watcher *Filter) { - delete(fs.allTopicsMatcher, watcher) - for _, topic := range watcher.Topics { - delete(fs.topicMatcher[BytesToTopic(topic)], watcher) - } -} - -// getWatchersByTopic returns a slice containing the filters that -// match a specific topic -func (fs *Filters) getWatchersByTopic(topic TopicType) []*Filter { - res := make([]*Filter, 0, len(fs.allTopicsMatcher)) - for watcher := range fs.allTopicsMatcher { - res = append(res, watcher) - } - for watcher := range fs.topicMatcher[topic] { - res = append(res, watcher) - } - return res -} - -// Get returns a filter from the collection with a specific ID -func (fs *Filters) Get(id string) *Filter { - fs.mutex.RLock() - defer fs.mutex.RUnlock() - return fs.watchers[id] -} - -// NotifyWatchers notifies any filter that has declared interest -// for the envelope's topic. -func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) { - var msg *ReceivedMessage - - fs.mutex.RLock() - defer fs.mutex.RUnlock() - - candidates := fs.getWatchersByTopic(env.Topic) - for _, watcher := range candidates { - if p2pMessage && !watcher.AllowP2P { - log.Trace(fmt.Sprintf("msg [%x], filter [%s]: p2p messages are not allowed", env.Hash(), watcher.id)) - continue - } - - var match bool - if msg != nil { - match = watcher.MatchMessage(msg) - } else { - match = watcher.MatchEnvelope(env) - if match { - msg = env.Open(watcher) - if msg == nil { - log.Trace("processing message: failed to open", "message", env.Hash().Hex(), "filter", watcher.id) - } - } else { - log.Trace("processing message: does not match", "message", env.Hash().Hex(), "filter", watcher.id) - } - } - - if match && msg != nil { - log.Trace("processing message: decrypted", "hash", env.Hash().Hex()) - if watcher.Src == nil || IsPubKeyEqual(msg.Src, watcher.Src) { - watcher.Trigger(msg) - } - } - } -} - -func (f *Filter) expectsAsymmetricEncryption() bool { - return f.KeyAsym != nil -} - -func (f *Filter) expectsSymmetricEncryption() bool { - return f.KeySym != nil -} - -// Trigger adds a yet-unknown message to the filter's list of -// received messages. -func (f *Filter) Trigger(msg *ReceivedMessage) { - f.mutex.Lock() - defer f.mutex.Unlock() - - if _, exist := f.Messages[msg.EnvelopeHash]; !exist { - f.Messages[msg.EnvelopeHash] = msg - } -} - -// Retrieve will return the list of all received messages associated -// to a filter. -func (f *Filter) Retrieve() (all []*ReceivedMessage) { - f.mutex.Lock() - defer f.mutex.Unlock() - - all = make([]*ReceivedMessage, 0, len(f.Messages)) - for _, msg := range f.Messages { - all = append(all, msg) - } - - f.Messages = make(map[common.Hash]*ReceivedMessage) // delete old messages - return all -} - -// MatchMessage checks if the filter matches an already decrypted -// message (i.e. a Message that has already been handled by -// MatchEnvelope when checked by a previous filter). -// Topics are not checked here, since this is done by topic matchers. -func (f *Filter) MatchMessage(msg *ReceivedMessage) bool { - if f.PoW > 0 && msg.PoW < f.PoW { - return false - } - - if f.expectsAsymmetricEncryption() && msg.isAsymmetricEncryption() { - return IsPubKeyEqual(&f.KeyAsym.PublicKey, msg.Dst) - } else if f.expectsSymmetricEncryption() && msg.isSymmetricEncryption() { - return f.SymKeyHash == msg.SymKeyHash - } - return false -} - -// MatchEnvelope checks if it's worth decrypting the message. If -// it returns `true`, client code is expected to attempt decrypting -// the message and subsequently call MatchMessage. -// Topics are not checked here, since this is done by topic matchers. -func (f *Filter) MatchEnvelope(envelope *Envelope) bool { - return f.PoW <= 0 || envelope.pow >= f.PoW -} - -func matchSingleTopic(topic TopicType, bt []byte) bool { - if len(bt) > TopicLength { - bt = bt[:TopicLength] - } - - if len(bt) < TopicLength { - return false - } - - for j, b := range bt { - if topic[j] != b { - return false - } - } - return true -} - -// IsPubKeyEqual checks that two public keys are equal -func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool { - if !ValidatePublicKey(a) { - return false - } else if !ValidatePublicKey(b) { - return false - } - // the curve is always the same, just compare the points - return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0 -} diff --git a/whisper/whisperv6/filter_test.go b/whisper/whisperv6/filter_test.go deleted file mode 100644 index 20ef3c050381..000000000000 --- a/whisper/whisperv6/filter_test.go +++ /dev/null @@ -1,867 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "math/big" - mrand "math/rand" - "testing" - "time" - - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/crypto" -) - -var seed int64 - -// InitSingleTest should be called in the beginning of every -// test, which uses RNG, in order to make the tests -// reproduciblity independent of their sequence. -func InitSingleTest() { - seed = time.Now().Unix() - mrand.Seed(seed) -} - -func InitDebugTest(i int64) { - seed = i - mrand.Seed(seed) -} - -type FilterTestCase struct { - f *Filter - id string - alive bool - msgCnt int -} - -func generateFilter(t *testing.T, symmetric bool) (*Filter, error) { - var f Filter - f.Messages = make(map[common.Hash]*ReceivedMessage) - - const topicNum = 8 - f.Topics = make([][]byte, topicNum) - for i := 0; i < topicNum; i++ { - f.Topics[i] = make([]byte, 4) - mrand.Read(f.Topics[i][:]) - f.Topics[i][0] = 0x01 - } - - key, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("generateFilter 1 failed with seed %d.", seed) - return nil, err - } - f.Src = &key.PublicKey - - if symmetric { - f.KeySym = make([]byte, aesKeyLength) - mrand.Read(f.KeySym) - f.SymKeyHash = crypto.Keccak256Hash(f.KeySym) - } else { - f.KeyAsym, err = crypto.GenerateKey() - if err != nil { - t.Fatalf("generateFilter 2 failed with seed %d.", seed) - return nil, err - } - } - - // AcceptP2P & PoW are not set - return &f, nil -} - -func generateTestCases(t *testing.T, SizeTestFilters int) []FilterTestCase { - cases := make([]FilterTestCase, SizeTestFilters) - for i := 0; i < SizeTestFilters; i++ { - f, _ := generateFilter(t, true) - cases[i].f = f - cases[i].alive = mrand.Int()&int(1) == 0 - } - return cases -} - -func TestInstallFilters(t *testing.T) { - InitSingleTest() - - const SizeTestFilters = 256 - w := New(&Config{}) - filters := NewFilters(w) - tst := generateTestCases(t, SizeTestFilters) - - var err error - var j string - for i := 0; i < SizeTestFilters; i++ { - j, err = filters.Install(tst[i].f) - if err != nil { - t.Fatalf("seed %d: failed to install filter: %s", seed, err) - } - tst[i].id = j - if len(j) != keyIDSize*2 { - t.Fatalf("seed %d: wrong filter id size [%d]", seed, len(j)) - } - } - - for _, testCase := range tst { - if !testCase.alive { - filters.Uninstall(testCase.id) - } - } - - for i, testCase := range tst { - fil := filters.Get(testCase.id) - exist := fil != nil - if exist != testCase.alive { - t.Fatalf("seed %d: failed alive: %d, %v, %v", seed, i, exist, testCase.alive) - } - if exist && fil.PoW != testCase.f.PoW { - t.Fatalf("seed %d: failed Get: %d, %v, %v", seed, i, exist, testCase.alive) - } - } -} - -func TestInstallSymKeyGeneratesHash(t *testing.T) { - InitSingleTest() - - w := New(&Config{}) - filters := NewFilters(w) - filter, _ := generateFilter(t, true) - - // save the current SymKeyHash for comparison - initialSymKeyHash := filter.SymKeyHash - - // ensure the SymKeyHash is invalid, for Install to recreate it - var invalid common.Hash - filter.SymKeyHash = invalid - - _, err := filters.Install(filter) - - if err != nil { - t.Fatalf("Error installing the filter: %s", err) - } - - for i, b := range filter.SymKeyHash { - if b != initialSymKeyHash[i] { - t.Fatalf("The filter's symmetric key hash was not properly generated by Install") - } - } -} - -func TestInstallIdenticalFilters(t *testing.T) { - InitSingleTest() - - w := New(&Config{}) - filters := NewFilters(w) - filter1, _ := generateFilter(t, true) - - // Copy the first filter since some of its fields - // are randomly gnerated. - filter2 := &Filter{ - KeySym: filter1.KeySym, - Topics: filter1.Topics, - PoW: filter1.PoW, - AllowP2P: filter1.AllowP2P, - Messages: make(map[common.Hash]*ReceivedMessage), - } - - _, err := filters.Install(filter1) - - if err != nil { - t.Fatalf("Error installing the first filter with seed %d: %s", seed, err) - } - - _, err = filters.Install(filter2) - - if err != nil { - t.Fatalf("Error installing the second filter with seed %d: %s", seed, err) - } - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("Error generating message parameters with seed %d: %s", seed, err) - } - - params.KeySym = filter1.KeySym - params.Topic = BytesToTopic(filter1.Topics[0]) - - filter1.Src = ¶ms.Src.PublicKey - filter2.Src = ¶ms.Src.PublicKey - - sentMessage, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := sentMessage.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - msg := env.Open(filter1) - if msg == nil { - t.Fatalf("failed to Open with filter1") - } - - if !filter1.MatchEnvelope(env) { - t.Fatalf("failed matching with the first filter") - } - - if !filter2.MatchEnvelope(env) { - t.Fatalf("failed matching with the first filter") - } - - if !filter1.MatchMessage(msg) { - t.Fatalf("failed matching with the second filter") - } - - if !filter2.MatchMessage(msg) { - t.Fatalf("failed matching with the second filter") - } -} - -func TestInstallFilterWithSymAndAsymKeys(t *testing.T) { - InitSingleTest() - - w := New(&Config{}) - filters := NewFilters(w) - filter1, _ := generateFilter(t, true) - - asymKey, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("Unable to create asymetric keys: %v", err) - } - - // Copy the first filter since some of its fields - // are randomly gnerated. - filter := &Filter{ - KeySym: filter1.KeySym, - KeyAsym: asymKey, - Topics: filter1.Topics, - PoW: filter1.PoW, - AllowP2P: filter1.AllowP2P, - Messages: make(map[common.Hash]*ReceivedMessage), - } - - _, err = filters.Install(filter) - - if err == nil { - t.Fatalf("Error detecting that a filter had both an asymmetric and symmetric key, with seed %d", seed) - } -} - -func TestComparePubKey(t *testing.T) { - InitSingleTest() - - key1, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed to generate first key with seed %d: %s.", seed, err) - } - key2, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed to generate second key with seed %d: %s.", seed, err) - } - if IsPubKeyEqual(&key1.PublicKey, &key2.PublicKey) { - t.Fatalf("public keys are equal, seed %d.", seed) - } - - // generate key3 == key1 - mrand.Seed(seed) - key3, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed to generate third key with seed %d: %s.", seed, err) - } - if IsPubKeyEqual(&key1.PublicKey, &key3.PublicKey) { - t.Fatalf("key1 == key3, seed %d.", seed) - } -} - -func TestMatchEnvelope(t *testing.T) { - InitSingleTest() - - fsym, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateFilter with seed %d: %s.", seed, err) - } - - fasym, err := generateFilter(t, false) - if err != nil { - t.Fatalf("failed generateFilter() with seed %d: %s.", seed, err) - } - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - params.Topic[0] = 0xFF // topic mismatch - - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - _, err = msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - // encrypt symmetrically - i := mrand.Int() % 4 - fsym.Topics[i] = params.Topic[:] - fasym.Topics[i] = params.Topic[:] - msg, err = NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap() with seed %d: %s.", seed, err) - } - - // symmetric + matching topic: match - match := fsym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope() symmetric with seed %d.", seed) - } - - // symmetric + matching topic + insufficient PoW: mismatch - fsym.PoW = env.PoW() + 1.0 - match = fsym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope(symmetric + matching topic + insufficient PoW) asymmetric with seed %d.", seed) - } - - // symmetric + matching topic + sufficient PoW: match - fsym.PoW = env.PoW() / 2 - match = fsym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(symmetric + matching topic + sufficient PoW) with seed %d.", seed) - } - - // symmetric + topics are nil (wildcard): match - prevTopics := fsym.Topics - fsym.Topics = nil - match = fsym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(symmetric + topics are nil) with seed %d.", seed) - } - fsym.Topics = prevTopics - - // encrypt asymmetrically - key, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - params.KeySym = nil - params.Dst = &key.PublicKey - msg, err = NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err = msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap() with seed %d: %s.", seed, err) - } - - // encryption method mismatch - match = fsym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed) - } - - // asymmetric + mismatching topic: mismatch - match = fasym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(asymmetric + mismatching topic) with seed %d.", seed) - } - - // asymmetric + matching topic: match - fasym.Topics[i] = fasym.Topics[i+1] - match = fasym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(asymmetric + matching topic) with seed %d.", seed) - } - - // asymmetric + filter without topic (wildcard): match - fasym.Topics = nil - match = fasym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(asymmetric + filter without topic) with seed %d.", seed) - } - - // asymmetric + insufficient PoW: mismatch - fasym.PoW = env.PoW() + 1.0 - match = fasym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope(asymmetric + insufficient PoW) with seed %d.", seed) - } - - // asymmetric + sufficient PoW: match - fasym.PoW = env.PoW() / 2 - match = fasym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(asymmetric + sufficient PoW) with seed %d.", seed) - } - - // filter without topic + envelope without topic: match - env.Topic = TopicType{} - match = fasym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(filter without topic + envelope without topic) with seed %d.", seed) - } - - // filter with topic + envelope without topic: mismatch - fasym.Topics = fsym.Topics - match = fasym.MatchEnvelope(env) - if !match { - // topic mismatch should have no affect, as topics are handled by topic matchers - t.Fatalf("failed MatchEnvelope(filter without topic + envelope without topic) with seed %d.", seed) - } -} - -func TestMatchMessageSym(t *testing.T) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - f, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateFilter with seed %d: %s.", seed, err) - } - - const index = 1 - params.KeySym = f.KeySym - params.Topic = BytesToTopic(f.Topics[index]) - - sentMessage, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := sentMessage.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - msg := env.Open(f) - if msg == nil { - t.Fatalf("failed Open with seed %d.", seed) - } - - // Src: match - *f.Src.X = *params.Src.PublicKey.X - *f.Src.Y = *params.Src.PublicKey.Y - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(src match) with seed %d.", seed) - } - - // insufficient PoW: mismatch - f.PoW = msg.PoW + 1.0 - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(insufficient PoW) with seed %d.", seed) - } - - // sufficient PoW: match - f.PoW = msg.PoW / 2 - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(sufficient PoW) with seed %d.", seed) - } - - // topic mismatch - f.Topics[index][0]++ - if !f.MatchMessage(msg) { - // topic mismatch should have no affect, as topics are handled by topic matchers - t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed) - } - f.Topics[index][0]-- - - // key mismatch - f.SymKeyHash[0]++ - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(key mismatch) with seed %d.", seed) - } - f.SymKeyHash[0]-- - - // Src absent: match - f.Src = nil - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(src absent) with seed %d.", seed) - } - - // key hash mismatch - h := f.SymKeyHash - f.SymKeyHash = common.Hash{} - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(key hash mismatch) with seed %d.", seed) - } - f.SymKeyHash = h - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(key hash match) with seed %d.", seed) - } - - // encryption method mismatch - f.KeySym = nil - f.KeyAsym, err = crypto.GenerateKey() - if err != nil { - t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed) - } -} - -func TestMatchMessageAsym(t *testing.T) { - InitSingleTest() - - f, err := generateFilter(t, false) - if err != nil { - t.Fatalf("failed generateFilter with seed %d: %s.", seed, err) - } - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - const index = 1 - params.Topic = BytesToTopic(f.Topics[index]) - params.Dst = &f.KeyAsym.PublicKey - keySymOrig := params.KeySym - params.KeySym = nil - - sentMessage, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := sentMessage.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - msg := env.Open(f) - if msg == nil { - t.Fatalf("failed to open with seed %d.", seed) - } - - // Src: match - *f.Src.X = *params.Src.PublicKey.X - *f.Src.Y = *params.Src.PublicKey.Y - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchMessage(src match) with seed %d.", seed) - } - - // insufficient PoW: mismatch - f.PoW = msg.PoW + 1.0 - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(insufficient PoW) with seed %d.", seed) - } - - // sufficient PoW: match - f.PoW = msg.PoW / 2 - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(sufficient PoW) with seed %d.", seed) - } - - // topic mismatch - f.Topics[index][0]++ - if !f.MatchMessage(msg) { - // topic mismatch should have no affect, as topics are handled by topic matchers - t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed) - } - f.Topics[index][0]-- - - // key mismatch - prev := *f.KeyAsym.PublicKey.X - zero := *big.NewInt(0) - *f.KeyAsym.PublicKey.X = zero - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(key mismatch) with seed %d.", seed) - } - *f.KeyAsym.PublicKey.X = prev - - // Src absent: match - f.Src = nil - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(src absent) with seed %d.", seed) - } - - // encryption method mismatch - f.KeySym = keySymOrig - f.KeyAsym = nil - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed) - } -} - -func cloneFilter(orig *Filter) *Filter { - var clone Filter - clone.Messages = make(map[common.Hash]*ReceivedMessage) - clone.Src = orig.Src - clone.KeyAsym = orig.KeyAsym - clone.KeySym = orig.KeySym - clone.Topics = orig.Topics - clone.PoW = orig.PoW - clone.AllowP2P = orig.AllowP2P - clone.SymKeyHash = orig.SymKeyHash - return &clone -} - -func generateCompatibeEnvelope(t *testing.T, f *Filter) *Envelope { - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - return nil - } - - params.KeySym = f.KeySym - params.Topic = BytesToTopic(f.Topics[2]) - sentMessage, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := sentMessage.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - return nil - } - return env -} - -func TestWatchers(t *testing.T) { - InitSingleTest() - - const NumFilters = 16 - const NumMessages = 256 - var i int - var j uint32 - var e *Envelope - var x, firstID string - var err error - - w := New(&Config{}) - filters := NewFilters(w) - tst := generateTestCases(t, NumFilters) - for i = 0; i < NumFilters; i++ { - tst[i].f.Src = nil - x, err = filters.Install(tst[i].f) - if err != nil { - t.Fatalf("failed to install filter with seed %d: %s.", seed, err) - } - tst[i].id = x - if len(firstID) == 0 { - firstID = x - } - } - - lastID := x - - var envelopes [NumMessages]*Envelope - for i = 0; i < NumMessages; i++ { - j = mrand.Uint32() % NumFilters - e = generateCompatibeEnvelope(t, tst[j].f) - envelopes[i] = e - tst[j].msgCnt++ - } - - for i = 0; i < NumMessages; i++ { - filters.NotifyWatchers(envelopes[i], false) - } - - var total int - var mail []*ReceivedMessage - var count [NumFilters]int - - for i = 0; i < NumFilters; i++ { - mail = tst[i].f.Retrieve() - count[i] = len(mail) - total += len(mail) - } - - if total != NumMessages { - t.Fatalf("failed with seed %d: total = %d, want: %d.", seed, total, NumMessages) - } - - for i = 0; i < NumFilters; i++ { - mail = tst[i].f.Retrieve() - if len(mail) != 0 { - t.Fatalf("failed with seed %d: i = %d.", seed, i) - } - - if tst[i].msgCnt != count[i] { - t.Fatalf("failed with seed %d: count[%d]: get %d, want %d.", seed, i, tst[i].msgCnt, count[i]) - } - } - - // another round with a cloned filter - - clone := cloneFilter(tst[0].f) - filters.Uninstall(lastID) - total = 0 - last := NumFilters - 1 - tst[last].f = clone - filters.Install(clone) - for i = 0; i < NumFilters; i++ { - tst[i].msgCnt = 0 - count[i] = 0 - } - - // make sure that the first watcher receives at least one message - e = generateCompatibeEnvelope(t, tst[0].f) - envelopes[0] = e - tst[0].msgCnt++ - for i = 1; i < NumMessages; i++ { - j = mrand.Uint32() % NumFilters - e = generateCompatibeEnvelope(t, tst[j].f) - envelopes[i] = e - tst[j].msgCnt++ - } - - for i = 0; i < NumMessages; i++ { - filters.NotifyWatchers(envelopes[i], false) - } - - for i = 0; i < NumFilters; i++ { - mail = tst[i].f.Retrieve() - count[i] = len(mail) - total += len(mail) - } - - combined := tst[0].msgCnt + tst[last].msgCnt - if total != NumMessages+count[0] { - t.Fatalf("failed with seed %d: total = %d, count[0] = %d.", seed, total, count[0]) - } - - if combined != count[0] { - t.Fatalf("failed with seed %d: combined = %d, count[0] = %d.", seed, combined, count[0]) - } - - if combined != count[last] { - t.Fatalf("failed with seed %d: combined = %d, count[last] = %d.", seed, combined, count[last]) - } - - for i = 1; i < NumFilters-1; i++ { - mail = tst[i].f.Retrieve() - if len(mail) != 0 { - t.Fatalf("failed with seed %d: i = %d.", seed, i) - } - - if tst[i].msgCnt != count[i] { - t.Fatalf("failed with seed %d: i = %d, get %d, want %d.", seed, i, tst[i].msgCnt, count[i]) - } - } - - // test AcceptP2P - - total = 0 - filters.NotifyWatchers(envelopes[0], true) - - for i = 0; i < NumFilters; i++ { - mail = tst[i].f.Retrieve() - total += len(mail) - } - - if total != 0 { - t.Fatalf("failed with seed %d: total: got %d, want 0.", seed, total) - } - - f := filters.Get(firstID) - if f == nil { - t.Fatalf("failed to get the filter with seed %d.", seed) - } - f.AllowP2P = true - total = 0 - filters.NotifyWatchers(envelopes[0], true) - - for i = 0; i < NumFilters; i++ { - mail = tst[i].f.Retrieve() - total += len(mail) - } - - if total != 1 { - t.Fatalf("failed with seed %d: total: got %d, want 1.", seed, total) - } -} - -func TestVariableTopics(t *testing.T) { - InitSingleTest() - - const lastTopicByte = 3 - var match bool - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - f, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateFilter with seed %d: %s.", seed, err) - } - - for i := 0; i < 4; i++ { - env.Topic = BytesToTopic(f.Topics[i]) - match = f.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope symmetric with seed %d, step %d.", seed, i) - } - - f.Topics[i][lastTopicByte]++ - match = f.MatchEnvelope(env) - if !match { - // topic mismatch should have no affect, as topics are handled by topic matchers - t.Fatalf("MatchEnvelope symmetric with seed %d, step %d.", seed, i) - } - } -} - -func TestMatchSingleTopic_ReturnTrue(t *testing.T) { - bt := []byte("test") - topic := BytesToTopic(bt) - - if !matchSingleTopic(topic, bt) { - t.FailNow() - } -} - -func TestMatchSingleTopic_WithTail_ReturnTrue(t *testing.T) { - bt := []byte("test with tail") - topic := BytesToTopic([]byte("test")) - - if !matchSingleTopic(topic, bt) { - t.FailNow() - } -} - -func TestMatchSingleTopic_NotEquals_ReturnFalse(t *testing.T) { - bt := []byte("tes") - topic := BytesToTopic(bt) - - if matchSingleTopic(topic, bt) { - t.FailNow() - } -} - -func TestMatchSingleTopic_InsufficientLength_ReturnFalse(t *testing.T) { - bt := []byte("test") - topic := BytesToTopic([]byte("not_equal")) - - if matchSingleTopic(topic, bt) { - t.FailNow() - } -} diff --git a/whisper/whisperv6/gen_criteria_json.go b/whisper/whisperv6/gen_criteria_json.go deleted file mode 100644 index ba942516db6d..000000000000 --- a/whisper/whisperv6/gen_criteria_json.go +++ /dev/null @@ -1,66 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package whisperv6 - -import ( - "encoding/json" - - "github.com/XinFinOrg/XDPoSChain/common/hexutil" -) - -var _ = (*criteriaOverride)(nil) - -// MarshalJSON marshals type Criteria to a json string -func (c Criteria) MarshalJSON() ([]byte, error) { - type Criteria struct { - SymKeyID string `json:"symKeyID"` - PrivateKeyID string `json:"privateKeyID"` - Sig hexutil.Bytes `json:"sig"` - MinPow float64 `json:"minPow"` - Topics []TopicType `json:"topics"` - AllowP2P bool `json:"allowP2P"` - } - var enc Criteria - enc.SymKeyID = c.SymKeyID - enc.PrivateKeyID = c.PrivateKeyID - enc.Sig = c.Sig - enc.MinPow = c.MinPow - enc.Topics = c.Topics - enc.AllowP2P = c.AllowP2P - return json.Marshal(&enc) -} - -// UnmarshalJSON unmarshals type Criteria to a json string -func (c *Criteria) UnmarshalJSON(input []byte) error { - type Criteria struct { - SymKeyID *string `json:"symKeyID"` - PrivateKeyID *string `json:"privateKeyID"` - Sig *hexutil.Bytes `json:"sig"` - MinPow *float64 `json:"minPow"` - Topics []TopicType `json:"topics"` - AllowP2P *bool `json:"allowP2P"` - } - var dec Criteria - if err := json.Unmarshal(input, &dec); err != nil { - return err - } - if dec.SymKeyID != nil { - c.SymKeyID = *dec.SymKeyID - } - if dec.PrivateKeyID != nil { - c.PrivateKeyID = *dec.PrivateKeyID - } - if dec.Sig != nil { - c.Sig = *dec.Sig - } - if dec.MinPow != nil { - c.MinPow = *dec.MinPow - } - if dec.Topics != nil { - c.Topics = dec.Topics - } - if dec.AllowP2P != nil { - c.AllowP2P = *dec.AllowP2P - } - return nil -} diff --git a/whisper/whisperv6/gen_message_json.go b/whisper/whisperv6/gen_message_json.go deleted file mode 100644 index 754941919f3a..000000000000 --- a/whisper/whisperv6/gen_message_json.go +++ /dev/null @@ -1,84 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package whisperv6 - -import ( - "encoding/json" - - "github.com/XinFinOrg/XDPoSChain/common/hexutil" -) - -var _ = (*messageOverride)(nil) - -// MarshalJSON marshals type Message to a json string -func (m Message) MarshalJSON() ([]byte, error) { - type Message struct { - Sig hexutil.Bytes `json:"sig,omitempty"` - TTL uint32 `json:"ttl"` - Timestamp uint32 `json:"timestamp"` - Topic TopicType `json:"topic"` - Payload hexutil.Bytes `json:"payload"` - Padding hexutil.Bytes `json:"padding"` - PoW float64 `json:"pow"` - Hash hexutil.Bytes `json:"hash"` - Dst hexutil.Bytes `json:"recipientPublicKey,omitempty"` - } - var enc Message - enc.Sig = m.Sig - enc.TTL = m.TTL - enc.Timestamp = m.Timestamp - enc.Topic = m.Topic - enc.Payload = m.Payload - enc.Padding = m.Padding - enc.PoW = m.PoW - enc.Hash = m.Hash - enc.Dst = m.Dst - return json.Marshal(&enc) -} - -// UnmarshalJSON unmarshals type Message to a json string -func (m *Message) UnmarshalJSON(input []byte) error { - type Message struct { - Sig *hexutil.Bytes `json:"sig,omitempty"` - TTL *uint32 `json:"ttl"` - Timestamp *uint32 `json:"timestamp"` - Topic *TopicType `json:"topic"` - Payload *hexutil.Bytes `json:"payload"` - Padding *hexutil.Bytes `json:"padding"` - PoW *float64 `json:"pow"` - Hash *hexutil.Bytes `json:"hash"` - Dst *hexutil.Bytes `json:"recipientPublicKey,omitempty"` - } - var dec Message - if err := json.Unmarshal(input, &dec); err != nil { - return err - } - if dec.Sig != nil { - m.Sig = *dec.Sig - } - if dec.TTL != nil { - m.TTL = *dec.TTL - } - if dec.Timestamp != nil { - m.Timestamp = *dec.Timestamp - } - if dec.Topic != nil { - m.Topic = *dec.Topic - } - if dec.Payload != nil { - m.Payload = *dec.Payload - } - if dec.Padding != nil { - m.Padding = *dec.Padding - } - if dec.PoW != nil { - m.PoW = *dec.PoW - } - if dec.Hash != nil { - m.Hash = *dec.Hash - } - if dec.Dst != nil { - m.Dst = *dec.Dst - } - return nil -} diff --git a/whisper/whisperv6/gen_newmessage_json.go b/whisper/whisperv6/gen_newmessage_json.go deleted file mode 100644 index 04dc4af7c0dd..000000000000 --- a/whisper/whisperv6/gen_newmessage_json.go +++ /dev/null @@ -1,90 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package whisperv6 - -import ( - "encoding/json" - - "github.com/XinFinOrg/XDPoSChain/common/hexutil" -) - -var _ = (*newMessageOverride)(nil) - -// MarshalJSON marshals type NewMessage to a json string -func (n NewMessage) MarshalJSON() ([]byte, error) { - type NewMessage struct { - SymKeyID string `json:"symKeyID"` - PublicKey hexutil.Bytes `json:"pubKey"` - Sig string `json:"sig"` - TTL uint32 `json:"ttl"` - Topic TopicType `json:"topic"` - Payload hexutil.Bytes `json:"payload"` - Padding hexutil.Bytes `json:"padding"` - PowTime uint32 `json:"powTime"` - PowTarget float64 `json:"powTarget"` - TargetPeer string `json:"targetPeer"` - } - var enc NewMessage - enc.SymKeyID = n.SymKeyID - enc.PublicKey = n.PublicKey - enc.Sig = n.Sig - enc.TTL = n.TTL - enc.Topic = n.Topic - enc.Payload = n.Payload - enc.Padding = n.Padding - enc.PowTime = n.PowTime - enc.PowTarget = n.PowTarget - enc.TargetPeer = n.TargetPeer - return json.Marshal(&enc) -} - -// UnmarshalJSON unmarshals type NewMessage to a json string -func (n *NewMessage) UnmarshalJSON(input []byte) error { - type NewMessage struct { - SymKeyID *string `json:"symKeyID"` - PublicKey *hexutil.Bytes `json:"pubKey"` - Sig *string `json:"sig"` - TTL *uint32 `json:"ttl"` - Topic *TopicType `json:"topic"` - Payload *hexutil.Bytes `json:"payload"` - Padding *hexutil.Bytes `json:"padding"` - PowTime *uint32 `json:"powTime"` - PowTarget *float64 `json:"powTarget"` - TargetPeer *string `json:"targetPeer"` - } - var dec NewMessage - if err := json.Unmarshal(input, &dec); err != nil { - return err - } - if dec.SymKeyID != nil { - n.SymKeyID = *dec.SymKeyID - } - if dec.PublicKey != nil { - n.PublicKey = *dec.PublicKey - } - if dec.Sig != nil { - n.Sig = *dec.Sig - } - if dec.TTL != nil { - n.TTL = *dec.TTL - } - if dec.Topic != nil { - n.Topic = *dec.Topic - } - if dec.Payload != nil { - n.Payload = *dec.Payload - } - if dec.Padding != nil { - n.Padding = *dec.Padding - } - if dec.PowTime != nil { - n.PowTime = *dec.PowTime - } - if dec.PowTarget != nil { - n.PowTarget = *dec.PowTarget - } - if dec.TargetPeer != nil { - n.TargetPeer = *dec.TargetPeer - } - return nil -} diff --git a/whisper/whisperv6/message.go b/whisper/whisperv6/message.go deleted file mode 100644 index e1d2a46c85d6..000000000000 --- a/whisper/whisperv6/message.go +++ /dev/null @@ -1,355 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains the Whisper protocol Message element. - -package whisperv6 - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/ecdsa" - crand "crypto/rand" - "encoding/binary" - "errors" - mrand "math/rand" - "strconv" - - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/ecies" - "github.com/XinFinOrg/XDPoSChain/log" -) - -// MessageParams specifies the exact way a message should be wrapped -// into an Envelope. -type MessageParams struct { - TTL uint32 - Src *ecdsa.PrivateKey - Dst *ecdsa.PublicKey - KeySym []byte - Topic TopicType - WorkTime uint32 - PoW float64 - Payload []byte - Padding []byte -} - -// SentMessage represents an end-user data packet to transmit through the -// Whisper protocol. These are wrapped into Envelopes that need not be -// understood by intermediate nodes, just forwarded. -type sentMessage struct { - Raw []byte -} - -// ReceivedMessage represents a data packet to be received through the -// Whisper protocol and successfully decrypted. -type ReceivedMessage struct { - Raw []byte - - Payload []byte - Padding []byte - Signature []byte - Salt []byte - - PoW float64 // Proof of work as described in the Whisper spec - Sent uint32 // Time when the message was posted into the network - TTL uint32 // Maximum time to live allowed for the message - Src *ecdsa.PublicKey // Message recipient (identity used to decode the message) - Dst *ecdsa.PublicKey // Message recipient (identity used to decode the message) - Topic TopicType - - SymKeyHash common.Hash // The Keccak256Hash of the key - EnvelopeHash common.Hash // Message envelope hash to act as a unique id -} - -func isMessageSigned(flags byte) bool { - return (flags & signatureFlag) != 0 -} - -func (msg *ReceivedMessage) isSymmetricEncryption() bool { - return msg.SymKeyHash != common.Hash{} -} - -func (msg *ReceivedMessage) isAsymmetricEncryption() bool { - return msg.Dst != nil -} - -// NewSentMessage creates and initializes a non-signed, non-encrypted Whisper message. -func NewSentMessage(params *MessageParams) (*sentMessage, error) { - const payloadSizeFieldMaxSize = 4 - msg := sentMessage{} - msg.Raw = make([]byte, 1, - flagsLength+payloadSizeFieldMaxSize+len(params.Payload)+len(params.Padding)+signatureLength+padSizeLimit) - msg.Raw[0] = 0 // set all the flags to zero - msg.addPayloadSizeField(params.Payload) - msg.Raw = append(msg.Raw, params.Payload...) - err := msg.appendPadding(params) - return &msg, err -} - -// addPayloadSizeField appends the auxiliary field containing the size of payload -func (msg *sentMessage) addPayloadSizeField(payload []byte) { - fieldSize := getSizeOfPayloadSizeField(payload) - field := make([]byte, 4) - binary.LittleEndian.PutUint32(field, uint32(len(payload))) - field = field[:fieldSize] - msg.Raw = append(msg.Raw, field...) - msg.Raw[0] |= byte(fieldSize) -} - -// getSizeOfPayloadSizeField returns the number of bytes necessary to encode the size of payload -func getSizeOfPayloadSizeField(payload []byte) int { - s := 1 - for i := len(payload); i >= 256; i /= 256 { - s++ - } - return s -} - -// appendPadding appends the padding specified in params. -// If no padding is provided in params, then random padding is generated. -func (msg *sentMessage) appendPadding(params *MessageParams) error { - if len(params.Padding) != 0 { - // padding data was provided by the Dapp, just use it as is - msg.Raw = append(msg.Raw, params.Padding...) - return nil - } - - rawSize := flagsLength + getSizeOfPayloadSizeField(params.Payload) + len(params.Payload) - if params.Src != nil { - rawSize += signatureLength - } - odd := rawSize % padSizeLimit - paddingSize := padSizeLimit - odd - pad := make([]byte, paddingSize) - _, err := crand.Read(pad) - if err != nil { - return err - } - if !validateDataIntegrity(pad, paddingSize) { - return errors.New("failed to generate random padding of size " + strconv.Itoa(paddingSize)) - } - msg.Raw = append(msg.Raw, pad...) - return nil -} - -// sign calculates and sets the cryptographic signature for the message, -// also setting the sign flag. -func (msg *sentMessage) sign(key *ecdsa.PrivateKey) error { - if isMessageSigned(msg.Raw[0]) { - // this should not happen, but no reason to panic - log.Error("failed to sign the message: already signed") - return nil - } - - msg.Raw[0] |= signatureFlag // it is important to set this flag before signing - hash := crypto.Keccak256(msg.Raw) - signature, err := crypto.Sign(hash, key) - if err != nil { - msg.Raw[0] &= (0xFF ^ signatureFlag) // clear the flag - return err - } - msg.Raw = append(msg.Raw, signature...) - return nil -} - -// encryptAsymmetric encrypts a message with a public key. -func (msg *sentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error { - if !ValidatePublicKey(key) { - return errors.New("invalid public key provided for asymmetric encryption") - } - encrypted, err := ecies.Encrypt(crand.Reader, ecies.ImportECDSAPublic(key), msg.Raw, nil, nil) - if err == nil { - msg.Raw = encrypted - } - return err -} - -// encryptSymmetric encrypts a message with a topic key, using AES-GCM-256. -// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize). -func (msg *sentMessage) encryptSymmetric(key []byte) (err error) { - if !validateDataIntegrity(key, aesKeyLength) { - return errors.New("invalid key provided for symmetric encryption, size: " + strconv.Itoa(len(key))) - } - block, err := aes.NewCipher(key) - if err != nil { - return err - } - aesgcm, err := cipher.NewGCM(block) - if err != nil { - return err - } - salt, err := generateSecureRandomData(aesNonceLength) // never use more than 2^32 random nonces with a given key - if err != nil { - return err - } - encrypted := aesgcm.Seal(nil, salt, msg.Raw, nil) - msg.Raw = append(encrypted, salt...) - return nil -} - -// generateSecureRandomData generates random data where extra security is required. -// The purpose of this function is to prevent some bugs in software or in hardware -// from delivering not-very-random data. This is especially useful for AES nonce, -// where true randomness does not really matter, but it is very important to have -// a unique nonce for every message. -func generateSecureRandomData(length int) ([]byte, error) { - x := make([]byte, length) - y := make([]byte, length) - res := make([]byte, length) - - _, err := crand.Read(x) - if err != nil { - return nil, err - } else if !validateDataIntegrity(x, length) { - return nil, errors.New("crypto/rand failed to generate secure random data") - } - _, err = mrand.Read(y) - if err != nil { - return nil, err - } else if !validateDataIntegrity(y, length) { - return nil, errors.New("math/rand failed to generate secure random data") - } - for i := 0; i < length; i++ { - res[i] = x[i] ^ y[i] - } - if !validateDataIntegrity(res, length) { - return nil, errors.New("failed to generate secure random data") - } - return res, nil -} - -// Wrap bundles the message into an Envelope to transmit over the network. -func (msg *sentMessage) Wrap(options *MessageParams) (envelope *Envelope, err error) { - if options.TTL == 0 { - options.TTL = DefaultTTL - } - if options.Src != nil { - if err = msg.sign(options.Src); err != nil { - return nil, err - } - } - if options.Dst != nil { - err = msg.encryptAsymmetric(options.Dst) - } else if options.KeySym != nil { - err = msg.encryptSymmetric(options.KeySym) - } else { - err = errors.New("unable to encrypt the message: neither symmetric nor assymmetric key provided") - } - if err != nil { - return nil, err - } - - envelope = NewEnvelope(options.TTL, options.Topic, msg) - if err = envelope.Seal(options); err != nil { - return nil, err - } - return envelope, nil -} - -// decryptSymmetric decrypts a message with a topic key, using AES-GCM-256. -// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize). -func (msg *ReceivedMessage) decryptSymmetric(key []byte) error { - // symmetric messages are expected to contain the 12-byte nonce at the end of the payload - if len(msg.Raw) < aesNonceLength { - return errors.New("missing salt or invalid payload in symmetric message") - } - salt := msg.Raw[len(msg.Raw)-aesNonceLength:] - - block, err := aes.NewCipher(key) - if err != nil { - return err - } - aesgcm, err := cipher.NewGCM(block) - if err != nil { - return err - } - decrypted, err := aesgcm.Open(nil, salt, msg.Raw[:len(msg.Raw)-aesNonceLength], nil) - if err != nil { - return err - } - msg.Raw = decrypted - msg.Salt = salt - return nil -} - -// decryptAsymmetric decrypts an encrypted payload with a private key. -func (msg *ReceivedMessage) decryptAsymmetric(key *ecdsa.PrivateKey) error { - decrypted, err := ecies.ImportECDSA(key).Decrypt(msg.Raw, nil, nil) - if err == nil { - msg.Raw = decrypted - } - return err -} - -// ValidateAndParse checks the message validity and extracts the fields in case of success. -func (msg *ReceivedMessage) ValidateAndParse() bool { - end := len(msg.Raw) - if end < 1 { - return false - } - - if isMessageSigned(msg.Raw[0]) { - end -= signatureLength - if end <= 1 { - return false - } - msg.Signature = msg.Raw[end : end+signatureLength] - msg.Src = msg.SigToPubKey() - if msg.Src == nil { - return false - } - } - - beg := 1 - payloadSize := 0 - sizeOfPayloadSizeField := int(msg.Raw[0] & SizeMask) // number of bytes indicating the size of payload - if sizeOfPayloadSizeField != 0 { - payloadSize = int(bytesToUintLittleEndian(msg.Raw[beg : beg+sizeOfPayloadSizeField])) - if payloadSize+1 > end { - return false - } - beg += sizeOfPayloadSizeField - msg.Payload = msg.Raw[beg : beg+payloadSize] - } - - beg += payloadSize - msg.Padding = msg.Raw[beg:end] - return true -} - -// SigToPubKey returns the public key associated to the message's -// signature. -func (msg *ReceivedMessage) SigToPubKey() *ecdsa.PublicKey { - defer func() { recover() }() // in case of invalid signature - - pub, err := crypto.SigToPub(msg.hash(), msg.Signature) - if err != nil { - log.Error("failed to recover public key from signature", "err", err) - return nil - } - return pub -} - -// hash calculates the SHA3 checksum of the message flags, payload size field, payload and padding. -func (msg *ReceivedMessage) hash() []byte { - if isMessageSigned(msg.Raw[0]) { - sz := len(msg.Raw) - signatureLength - return crypto.Keccak256(msg.Raw[:sz]) - } - return crypto.Keccak256(msg.Raw) -} diff --git a/whisper/whisperv6/message_test.go b/whisper/whisperv6/message_test.go deleted file mode 100644 index 8485be63edf5..000000000000 --- a/whisper/whisperv6/message_test.go +++ /dev/null @@ -1,471 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - mrand "math/rand" - "testing" - - "github.com/XinFinOrg/XDPoSChain/common/hexutil" - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/rlp" -) - -func generateMessageParams() (*MessageParams, error) { - // set all the parameters except p.Dst and p.Padding - - buf := make([]byte, 4) - mrand.Read(buf) - sz := mrand.Intn(400) - - var p MessageParams - p.PoW = 0.01 - p.WorkTime = 1 - p.TTL = uint32(mrand.Intn(1024)) - p.Payload = make([]byte, sz) - p.KeySym = make([]byte, aesKeyLength) - mrand.Read(p.Payload) - mrand.Read(p.KeySym) - p.Topic = BytesToTopic(buf) - - var err error - p.Src, err = crypto.GenerateKey() - if err != nil { - return nil, err - } - - return &p, nil -} - -func singleMessageTest(t *testing.T, symmetric bool) { - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - key, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - - if !symmetric { - params.KeySym = nil - params.Dst = &key.PublicKey - } - - text := make([]byte, 0, 512) - text = append(text, params.Payload...) - - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - var decrypted *ReceivedMessage - if symmetric { - decrypted, err = env.OpenSymmetric(params.KeySym) - } else { - decrypted, err = env.OpenAsymmetric(key) - } - - if err != nil { - t.Fatalf("failed to encrypt with seed %d: %s.", seed, err) - } - - if !decrypted.ValidateAndParse() { - t.Fatalf("failed to validate with seed %d, symmetric = %v.", seed, symmetric) - } - - if !bytes.Equal(text, decrypted.Payload) { - t.Fatalf("failed with seed %d: compare payload.", seed) - } - if !isMessageSigned(decrypted.Raw[0]) { - t.Fatalf("failed with seed %d: unsigned.", seed) - } - if len(decrypted.Signature) != signatureLength { - t.Fatalf("failed with seed %d: signature len %d.", seed, len(decrypted.Signature)) - } - if !IsPubKeyEqual(decrypted.Src, ¶ms.Src.PublicKey) { - t.Fatalf("failed with seed %d: signature mismatch.", seed) - } -} - -func TestMessageEncryption(t *testing.T) { - InitSingleTest() - - var symmetric bool - for i := 0; i < 256; i++ { - singleMessageTest(t, symmetric) - symmetric = !symmetric - } -} - -func TestMessageWrap(t *testing.T) { - seed = int64(1777444222) - mrand.Seed(seed) - target := 128.0 - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.TTL = 1 - params.WorkTime = 12 - params.PoW = target - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - pow := env.PoW() - if pow < target { - t.Fatalf("failed Wrap with seed %d: pow < target (%f vs. %f).", seed, pow, target) - } - - // set PoW target too high, expect error - msg2, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.TTL = 1000000 - params.WorkTime = 1 - params.PoW = 10000000.0 - _, err = msg2.Wrap(params) - if err == nil { - t.Fatalf("unexpectedly reached the PoW target with seed %d.", seed) - } -} - -func TestMessageSeal(t *testing.T) { - // this test depends on deterministic choice of seed (1976726903) - seed = int64(1976726903) - mrand.Seed(seed) - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.TTL = 1 - - env := NewEnvelope(params.TTL, params.Topic, msg) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - env.Expiry = uint32(seed) // make it deterministic - target := 32.0 - params.WorkTime = 4 - params.PoW = target - env.Seal(params) - - env.calculatePoW(0) - pow := env.PoW() - if pow < target { - t.Fatalf("failed Wrap with seed %d: pow < target (%f vs. %f).", seed, pow, target) - } - - params.WorkTime = 1 - params.PoW = 1000000000.0 - env.Seal(params) - env.calculatePoW(0) - pow = env.PoW() - if pow < 2*target { - t.Fatalf("failed Wrap with seed %d: pow too small %f.", seed, pow) - } -} - -func TestEnvelopeOpen(t *testing.T) { - InitSingleTest() - - var symmetric bool - for i := 0; i < 32; i++ { - singleEnvelopeOpenTest(t, symmetric) - symmetric = !symmetric - } -} - -func singleEnvelopeOpenTest(t *testing.T, symmetric bool) { - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - key, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - - if !symmetric { - params.KeySym = nil - params.Dst = &key.PublicKey - } - - text := make([]byte, 0, 512) - text = append(text, params.Payload...) - - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - var f Filter - if symmetric { - f = Filter{KeySym: params.KeySym} - } else { - f = Filter{KeyAsym: key} - } - decrypted := env.Open(&f) - if decrypted == nil { - t.Fatalf("failed to open with seed %d.", seed) - } - - if !bytes.Equal(text, decrypted.Payload) { - t.Fatalf("failed with seed %d: compare payload.", seed) - } - if !isMessageSigned(decrypted.Raw[0]) { - t.Fatalf("failed with seed %d: unsigned.", seed) - } - if len(decrypted.Signature) != signatureLength { - t.Fatalf("failed with seed %d: signature len %d.", seed, len(decrypted.Signature)) - } - if !IsPubKeyEqual(decrypted.Src, ¶ms.Src.PublicKey) { - t.Fatalf("failed with seed %d: signature mismatch.", seed) - } - if decrypted.isAsymmetricEncryption() == symmetric { - t.Fatalf("failed with seed %d: asymmetric %v vs. %v.", seed, decrypted.isAsymmetricEncryption(), symmetric) - } - if decrypted.isSymmetricEncryption() != symmetric { - t.Fatalf("failed with seed %d: symmetric %v vs. %v.", seed, decrypted.isSymmetricEncryption(), symmetric) - } - if !symmetric { - if decrypted.Dst == nil { - t.Fatalf("failed with seed %d: dst is nil.", seed) - } - if !IsPubKeyEqual(decrypted.Dst, &key.PublicKey) { - t.Fatalf("failed with seed %d: Dst.", seed) - } - } -} - -func TestEncryptWithZeroKey(t *testing.T) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.KeySym = make([]byte, aesKeyLength) - _, err = msg.Wrap(params) - if err == nil { - t.Fatalf("wrapped with zero key, seed: %d.", seed) - } - - params, err = generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, err = NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.KeySym = make([]byte, 0) - _, err = msg.Wrap(params) - if err == nil { - t.Fatalf("wrapped with empty key, seed: %d.", seed) - } - - params, err = generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, err = NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.KeySym = nil - _, err = msg.Wrap(params) - if err == nil { - t.Fatalf("wrapped with nil key, seed: %d.", seed) - } -} - -func TestRlpEncode(t *testing.T) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("wrapped with zero key, seed: %d.", seed) - } - - raw, err := rlp.EncodeToBytes(env) - if err != nil { - t.Fatalf("RLP encode failed: %s.", err) - } - - var decoded Envelope - rlp.DecodeBytes(raw, &decoded) - if err != nil { - t.Fatalf("RLP decode failed: %s.", err) - } - - he := env.Hash() - hd := decoded.Hash() - - if he != hd { - t.Fatalf("Hashes are not equal: %x vs. %x", he, hd) - } -} - -func singlePaddingTest(t *testing.T, padSize int) { - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d and sz=%d: %s.", seed, padSize, err) - } - params.Padding = make([]byte, padSize) - params.PoW = 0.0000000001 - pad := make([]byte, padSize) - _, err = mrand.Read(pad) - if err != nil { - t.Fatalf("padding is not generated (seed %d): %s", seed, err) - } - n := copy(params.Padding, pad) - if n != padSize { - t.Fatalf("padding is not copied (seed %d): %s", seed, err) - } - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed to wrap, seed: %d and sz=%d.", seed, padSize) - } - f := Filter{KeySym: params.KeySym} - decrypted := env.Open(&f) - if decrypted == nil { - t.Fatalf("failed to open, seed and sz=%d: %d.", seed, padSize) - } - if !bytes.Equal(pad, decrypted.Padding) { - t.Fatalf("padding is not retireved as expected with seed %d and sz=%d:\n[%x]\n[%x].", seed, padSize, pad, decrypted.Padding) - } -} - -func TestPadding(t *testing.T) { - InitSingleTest() - - for i := 1; i < 260; i++ { - singlePaddingTest(t, i) - } - - lim := 256 * 256 - for i := lim - 5; i < lim+2; i++ { - singlePaddingTest(t, i) - } - - for i := 0; i < 256; i++ { - n := mrand.Intn(256*254) + 256 - singlePaddingTest(t, n) - } - - for i := 0; i < 256; i++ { - n := mrand.Intn(256*1024) + 256*256 - singlePaddingTest(t, n) - } -} - -func TestPaddingAppendedToSymMessagesWithSignature(t *testing.T) { - params := &MessageParams{ - Payload: make([]byte, 246), - KeySym: make([]byte, aesKeyLength), - } - - pSrc, err := crypto.GenerateKey() - - if err != nil { - t.Fatalf("Error creating the signature key %v", err) - return - } - params.Src = pSrc - - // Simulate a message with a payload just under 256 so that - // payload + flag + signature > 256. Check that the result - // is padded on the next 256 boundary. - msg := sentMessage{} - const payloadSizeFieldMinSize = 1 - msg.Raw = make([]byte, flagsLength+payloadSizeFieldMinSize+len(params.Payload)) - - err = msg.appendPadding(params) - - if err != nil { - t.Fatalf("Error appending padding to message %v", err) - return - } - - if len(msg.Raw) != 512-signatureLength { - t.Errorf("Invalid size %d != 512", len(msg.Raw)) - } -} - -func TestAesNonce(t *testing.T) { - key := hexutil.MustDecode("0x03ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31") - block, err := aes.NewCipher(key) - if err != nil { - t.Fatalf("NewCipher failed: %s", err) - } - aesgcm, err := cipher.NewGCM(block) - if err != nil { - t.Fatalf("NewGCM failed: %s", err) - } - // This is the most important single test in this package. - // If it fails, whisper will not be working. - if aesgcm.NonceSize() != aesNonceLength { - t.Fatalf("Nonce size is wrong. This is a critical error. Apparently AES nonce size have changed in the new version of AES GCM package. Whisper will not be working until this problem is resolved.") - } -} diff --git a/whisper/whisperv6/peer.go b/whisper/whisperv6/peer.go deleted file mode 100644 index f5bbf00d5e37..000000000000 --- a/whisper/whisperv6/peer.go +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "fmt" - "math" - "sync" - "time" - - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/log" - "github.com/XinFinOrg/XDPoSChain/p2p" - "github.com/XinFinOrg/XDPoSChain/rlp" - mapset "github.com/deckarep/golang-set" -) - -// Peer represents a whisper protocol peer connection. -type Peer struct { - host *Whisper - peer *p2p.Peer - ws p2p.MsgReadWriter - - trusted bool - powRequirement float64 - bloomMu sync.Mutex - bloomFilter []byte - fullNode bool - - known mapset.Set // Messages already known by the peer to avoid wasting bandwidth - - quit chan struct{} -} - -// newPeer creates a new whisper peer object, but does not run the handshake itself. -func newPeer(host *Whisper, remote *p2p.Peer, rw p2p.MsgReadWriter) *Peer { - return &Peer{ - host: host, - peer: remote, - ws: rw, - trusted: false, - powRequirement: 0.0, - known: mapset.NewSet(), - quit: make(chan struct{}), - bloomFilter: MakeFullNodeBloom(), - fullNode: true, - } -} - -// start initiates the peer updater, periodically broadcasting the whisper packets -// into the network. -func (peer *Peer) start() { - go peer.update() - log.Trace("start", "peer", peer.ID()) -} - -// stop terminates the peer updater, stopping message forwarding to it. -func (peer *Peer) stop() { - close(peer.quit) - log.Trace("stop", "peer", peer.ID()) -} - -// handshake sends the protocol initiation status message to the remote peer and -// verifies the remote status too. -func (peer *Peer) handshake() error { - // Send the handshake status message asynchronously - errc := make(chan error, 1) - go func() { - pow := peer.host.MinPow() - powConverted := math.Float64bits(pow) - bloom := peer.host.BloomFilter() - errc <- p2p.SendItems(peer.ws, statusCode, ProtocolVersion, powConverted, bloom) - }() - - // Fetch the remote status packet and verify protocol match - packet, err := peer.ws.ReadMsg() - if err != nil { - return err - } - if packet.Code != statusCode { - return fmt.Errorf("peer [%x] sent packet %x before status packet", peer.ID(), packet.Code) - } - s := rlp.NewStream(packet.Payload, uint64(packet.Size)) - _, err = s.List() - if err != nil { - return fmt.Errorf("peer [%x] sent bad status message: %v", peer.ID(), err) - } - peerVersion, err := s.Uint() - if err != nil { - return fmt.Errorf("peer [%x] sent bad status message (unable to decode version): %v", peer.ID(), err) - } - if peerVersion != ProtocolVersion { - return fmt.Errorf("peer [%x]: protocol version mismatch %d != %d", peer.ID(), peerVersion, ProtocolVersion) - } - - // only version is mandatory, subsequent parameters are optional - powRaw, err := s.Uint() - if err == nil { - pow := math.Float64frombits(powRaw) - if math.IsInf(pow, 0) || math.IsNaN(pow) || pow < 0.0 { - return fmt.Errorf("peer [%x] sent bad status message: invalid pow", peer.ID()) - } - peer.powRequirement = pow - - var bloom []byte - err = s.Decode(&bloom) - if err == nil { - sz := len(bloom) - if sz != BloomFilterSize && sz != 0 { - return fmt.Errorf("peer [%x] sent bad status message: wrong bloom filter size %d", peer.ID(), sz) - } - peer.setBloomFilter(bloom) - } - } - - if err := <-errc; err != nil { - return fmt.Errorf("peer [%x] failed to send status packet: %v", peer.ID(), err) - } - return nil -} - -// update executes periodic operations on the peer, including message transmission -// and expiration. -func (peer *Peer) update() { - // Start the tickers for the updates - expire := time.NewTicker(expirationCycle) - transmit := time.NewTicker(transmissionCycle) - - // Loop and transmit until termination is requested - for { - select { - case <-expire.C: - peer.expire() - - case <-transmit.C: - if err := peer.broadcast(); err != nil { - log.Trace("broadcast failed", "reason", err, "peer", peer.ID()) - return - } - - case <-peer.quit: - return - } - } -} - -// mark marks an envelope known to the peer so that it won't be sent back. -func (peer *Peer) mark(envelope *Envelope) { - peer.known.Add(envelope.Hash()) -} - -// marked checks if an envelope is already known to the remote peer. -func (peer *Peer) marked(envelope *Envelope) bool { - return peer.known.Contains(envelope.Hash()) -} - -// expire iterates over all the known envelopes in the host and removes all -// expired (unknown) ones from the known list. -func (peer *Peer) expire() { - unmark := make(map[common.Hash]struct{}) - peer.known.Each(func(v interface{}) bool { - if !peer.host.isEnvelopeCached(v.(common.Hash)) { - unmark[v.(common.Hash)] = struct{}{} - } - return true - }) - // Dump all known but no longer cached - for hash := range unmark { - peer.known.Remove(hash) - } -} - -// broadcast iterates over the collection of envelopes and transmits yet unknown -// ones over the network. -func (peer *Peer) broadcast() error { - envelopes := peer.host.Envelopes() - bundle := make([]*Envelope, 0, len(envelopes)) - for _, envelope := range envelopes { - if !peer.marked(envelope) && envelope.PoW() >= peer.powRequirement && peer.bloomMatch(envelope) { - bundle = append(bundle, envelope) - } - } - - if len(bundle) > 0 { - // transmit the batch of envelopes - if err := p2p.Send(peer.ws, messagesCode, bundle); err != nil { - return err - } - - // mark envelopes only if they were successfully sent - for _, e := range bundle { - peer.mark(e) - } - - log.Trace("broadcast", "num. messages", len(bundle)) - } - return nil -} - -// ID returns a peer's id -func (peer *Peer) ID() []byte { - id := peer.peer.ID() - return id[:] -} - -func (peer *Peer) notifyAboutPowRequirementChange(pow float64) error { - i := math.Float64bits(pow) - return p2p.Send(peer.ws, powRequirementCode, i) -} - -func (peer *Peer) notifyAboutBloomFilterChange(bloom []byte) error { - return p2p.Send(peer.ws, bloomFilterExCode, bloom) -} - -func (peer *Peer) bloomMatch(env *Envelope) bool { - peer.bloomMu.Lock() - defer peer.bloomMu.Unlock() - return peer.fullNode || BloomFilterMatch(peer.bloomFilter, env.Bloom()) -} - -func (peer *Peer) setBloomFilter(bloom []byte) { - peer.bloomMu.Lock() - defer peer.bloomMu.Unlock() - peer.bloomFilter = bloom - peer.fullNode = isFullNode(bloom) - if peer.fullNode && peer.bloomFilter == nil { - peer.bloomFilter = MakeFullNodeBloom() - } -} - -func MakeFullNodeBloom() []byte { - bloom := make([]byte, BloomFilterSize) - for i := 0; i < BloomFilterSize; i++ { - bloom[i] = 0xFF - } - return bloom -} diff --git a/whisper/whisperv6/peer_test.go b/whisper/whisperv6/peer_test.go deleted file mode 100644 index 8c8249748109..000000000000 --- a/whisper/whisperv6/peer_test.go +++ /dev/null @@ -1,493 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "bytes" - "crypto/ecdsa" - "fmt" - mrand "math/rand" - "net" - "sync" - "testing" - "time" - - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/common/hexutil" - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/p2p" - "github.com/XinFinOrg/XDPoSChain/p2p/discover" - "github.com/XinFinOrg/XDPoSChain/p2p/nat" -) - -var keys = []string{ - "d49dcf37238dc8a7aac57dc61b9fee68f0a97f062968978b9fafa7d1033d03a9", - "73fd6143c48e80ed3c56ea159fe7494a0b6b393a392227b422f4c3e8f1b54f98", - "119dd32adb1daa7a4c7bf77f847fb28730785aa92947edf42fdd997b54de40dc", - "deeda8709dea935bb772248a3144dea449ffcc13e8e5a1fd4ef20ce4e9c87837", - "5bd208a079633befa349441bdfdc4d85ba9bd56081525008380a63ac38a407cf", - "1d27fb4912002d58a2a42a50c97edb05c1b3dffc665dbaa42df1fe8d3d95c9b5", - "15def52800c9d6b8ca6f3066b7767a76afc7b611786c1276165fbc61636afb68", - "51be6ab4b2dc89f251ff2ace10f3c1cc65d6855f3e083f91f6ff8efdfd28b48c", - "ef1ef7441bf3c6419b162f05da6037474664f198b58db7315a6f4de52414b4a0", - "09bdf6985aabc696dc1fbeb5381aebd7a6421727343872eb2fadfc6d82486fd9", - "15d811bf2e01f99a224cdc91d0cf76cea08e8c67905c16fee9725c9be71185c4", - "2f83e45cf1baaea779789f755b7da72d8857aeebff19362dd9af31d3c9d14620", - "73f04e34ac6532b19c2aae8f8e52f38df1ac8f5cd10369f92325b9b0494b0590", - "1e2e07b69e5025537fb73770f483dc8d64f84ae3403775ef61cd36e3faf162c1", - "8963d9bbb3911aac6d30388c786756b1c423c4fbbc95d1f96ddbddf39809e43a", - "0422da85abc48249270b45d8de38a4cc3c02032ede1fcf0864a51092d58a2f1f", - "8ae5c15b0e8c7cade201fdc149831aa9b11ff626a7ffd27188886cc108ad0fa8", - "acd8f5a71d4aecfcb9ad00d32aa4bcf2a602939b6a9dd071bab443154184f805", - "a285a922125a7481600782ad69debfbcdb0316c1e97c267aff29ef50001ec045", - "28fd4eee78c6cd4bf78f39f8ab30c32c67c24a6223baa40e6f9c9a0e1de7cef5", - "c5cca0c9e6f043b288c6f1aef448ab59132dab3e453671af5d0752961f013fc7", - "46df99b051838cb6f8d1b73f232af516886bd8c4d0ee07af9a0a033c391380fd", - "c6a06a53cbaadbb432884f36155c8f3244e244881b5ee3e92e974cfa166d793f", - "783b90c75c63dc72e2f8d11b6f1b4de54d63825330ec76ee8db34f06b38ea211", - "9450038f10ca2c097a8013e5121b36b422b95b04892232f930a29292d9935611", - "e215e6246ed1cfdcf7310d4d8cdbe370f0d6a8371e4eb1089e2ae05c0e1bc10f", - "487110939ed9d64ebbc1f300adeab358bc58875faf4ca64990fbd7fe03b78f2b", - "824a70ea76ac81366da1d4f4ac39de851c8ac49dca456bb3f0a186ceefa269a5", - "ba8f34fa40945560d1006a328fe70c42e35cc3d1017e72d26864cd0d1b150f15", - "30a5dfcfd144997f428901ea88a43c8d176b19c79dde54cc58eea001aa3d246c", - "de59f7183aca39aa245ce66a05245fecfc7e2c75884184b52b27734a4a58efa2", - "92629e2ff5f0cb4f5f08fffe0f64492024d36f045b901efb271674b801095c5a", - "7184c1701569e3a4c4d2ddce691edd983b81e42e09196d332e1ae2f1e062cff4", -} - -type TestData struct { - counter [NumNodes]int - mutex sync.RWMutex -} - -type TestNode struct { - shh *Whisper - id *ecdsa.PrivateKey - server *p2p.Server - filerID string -} - -const NumNodes = 8 // must not exceed the number of keys (32) - -var result TestData -var nodes [NumNodes]*TestNode -var sharedKey = hexutil.MustDecode("0x03ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31") -var wrongKey = hexutil.MustDecode("0xf91156714d7ec88d3edc1c652c2181dbb3044e8771c683f3b30d33c12b986b11") -var sharedTopic = TopicType{0xF, 0x1, 0x2, 0} -var wrongTopic = TopicType{0, 0, 0, 0} -var expectedMessage = []byte("per aspera ad astra") -var unexpectedMessage = []byte("per rectum ad astra") -var masterBloomFilter []byte -var masterPow = 0.00000001 -var round = 1 -var debugMode = false -var prevTime time.Time -var cntPrev int - -func TestSimulation(t *testing.T) { - t.Skip("TODO: PR-136 Broken test due to EVM upgrade!") - // create a chain of whisper nodes, - // installs the filters with shared (predefined) parameters - initialize(t) - - // each node sends one random (not decryptable) message - for i := 0; i < NumNodes; i++ { - sendMsg(t, false, i) - } - - // node #0 sends one expected (decryptable) message - sendMsg(t, true, 0) - - // check if each node have received and decrypted exactly one message - checkPropagation(t, true) - - // check if Status message was correctly decoded - checkBloomFilterExchange(t) - checkPowExchange(t) - - // send new pow and bloom exchange messages - resetParams(t) - - // node #1 sends one expected (decryptable) message - sendMsg(t, true, 1) - - // check if each node (except node #0) have received and decrypted exactly one message - checkPropagation(t, false) - - // check if corresponding protocol-level messages were correctly decoded - checkPowExchangeForNodeZero(t) - checkBloomFilterExchange(t) - - stopServers() -} - -func resetParams(t *testing.T) { - // change pow only for node zero - masterPow = 7777777.0 - nodes[0].shh.SetMinimumPoW(masterPow) - - // change bloom for all nodes - masterBloomFilter = TopicToBloom(sharedTopic) - for i := 0; i < NumNodes; i++ { - nodes[i].shh.SetBloomFilter(masterBloomFilter) - } - - round++ -} - -func initBloom(t *testing.T) { - masterBloomFilter = make([]byte, BloomFilterSize) - _, err := mrand.Read(masterBloomFilter) - if err != nil { - t.Fatalf("rand failed: %s.", err) - } - - msgBloom := TopicToBloom(sharedTopic) - masterBloomFilter = addBloom(masterBloomFilter, msgBloom) - for i := 0; i < 32; i++ { - masterBloomFilter[i] = 0xFF - } - - if !BloomFilterMatch(masterBloomFilter, msgBloom) { - t.Fatalf("bloom mismatch on initBloom.") - } -} - -func initialize(t *testing.T) { - initBloom(t) - - var err error - ip := net.IPv4(127, 0, 0, 1) - port0 := 30303 - - for i := 0; i < NumNodes; i++ { - var node TestNode - b := make([]byte, BloomFilterSize) - copy(b, masterBloomFilter) - node.shh = New(&DefaultConfig) - node.shh.SetMinimumPoW(masterPow) - node.shh.SetBloomFilter(b) - if !bytes.Equal(node.shh.BloomFilter(), masterBloomFilter) { - t.Fatalf("bloom mismatch on init.") - } - node.shh.Start(nil) - topics := make([]TopicType, 0) - topics = append(topics, sharedTopic) - f := Filter{KeySym: sharedKey} - f.Topics = [][]byte{topics[0][:]} - node.filerID, err = node.shh.Subscribe(&f) - if err != nil { - t.Fatalf("failed to install the filter: %s.", err) - } - node.id, err = crypto.HexToECDSA(keys[i]) - if err != nil { - t.Fatalf("failed convert the key: %s.", keys[i]) - } - port := port0 + i - addr := fmt.Sprintf(":%d", port) // e.g. ":30303" - name := common.MakeName("whisper-go", "2.0") - var peers []*discover.Node - if i > 0 { - peerNodeID := nodes[i-1].id - peerPort := uint16(port - 1) - peerNode := discover.PubkeyID(&peerNodeID.PublicKey) - peer := discover.NewNode(peerNode, ip, peerPort, peerPort) - peers = append(peers, peer) - } - - node.server = &p2p.Server{ - Config: p2p.Config{ - PrivateKey: node.id, - MaxPeers: NumNodes/2 + 1, - Name: name, - Protocols: node.shh.Protocols(), - ListenAddr: addr, - NAT: nat.Any(), - BootstrapNodes: peers, - StaticNodes: peers, - TrustedNodes: peers, - }, - } - - startServer(t, node.server) - nodes[i] = &node - } -} - -func startServer(t *testing.T, s *p2p.Server) { - err := s.Start() - if err != nil { - t.Fatalf("failed to start the fisrt server.") - } -} - -func stopServers() { - for i := 0; i < NumNodes; i++ { - n := nodes[i] - if n != nil { - n.shh.Unsubscribe(n.filerID) - n.shh.Stop() - n.server.Stop() - } - } -} - -func checkPropagation(t *testing.T, includingNodeZero bool) { - if t.Failed() { - return - } - - prevTime = time.Now() - // (cycle * iterations) should not exceed 50 seconds, since TTL=50 - const cycle = 200 // time in milliseconds - const iterations = 250 - - first := 0 - if !includingNodeZero { - first = 1 - } - - for j := 0; j < iterations; j++ { - for i := first; i < NumNodes; i++ { - f := nodes[i].shh.GetFilter(nodes[i].filerID) - if f == nil { - t.Fatalf("failed to get filterId %s from node %d, round %d.", nodes[i].filerID, i, round) - } - - mail := f.Retrieve() - validateMail(t, i, mail) - - if isTestComplete() { - checkTestStatus() - return - } - } - - checkTestStatus() - time.Sleep(cycle * time.Millisecond) - } - - if !includingNodeZero { - f := nodes[0].shh.GetFilter(nodes[0].filerID) - if f != nil { - t.Fatalf("node zero received a message with low PoW.") - } - } - - t.Fatalf("Test was not complete (%d round): timeout %d seconds. nodes=%v", round, iterations*cycle/1000, nodes) -} - -func validateMail(t *testing.T, index int, mail []*ReceivedMessage) { - var cnt int - for _, m := range mail { - if bytes.Equal(m.Payload, expectedMessage) { - cnt++ - } - } - - if cnt == 0 { - // no messages received yet: nothing is wrong - return - } - if cnt > 1 { - t.Fatalf("node %d received %d.", index, cnt) - } - - if cnt == 1 { - result.mutex.Lock() - defer result.mutex.Unlock() - result.counter[index] += cnt - if result.counter[index] > 1 { - t.Fatalf("node %d accumulated %d.", index, result.counter[index]) - } - } -} - -func checkTestStatus() { - var cnt int - var arr [NumNodes]int - - for i := 0; i < NumNodes; i++ { - arr[i] = nodes[i].server.PeerCount() - envelopes := nodes[i].shh.Envelopes() - if len(envelopes) >= NumNodes { - cnt++ - } - } - - if debugMode { - if cntPrev != cnt { - fmt.Printf(" %v \t number of nodes that have received all msgs: %d, number of peers per node: %v \n", - time.Since(prevTime), cnt, arr) - prevTime = time.Now() - cntPrev = cnt - } - } -} - -func isTestComplete() bool { - result.mutex.RLock() - defer result.mutex.RUnlock() - - for i := 0; i < NumNodes; i++ { - if result.counter[i] < 1 { - return false - } - } - - for i := 0; i < NumNodes; i++ { - envelopes := nodes[i].shh.Envelopes() - if len(envelopes) < NumNodes+1 { - return false - } - } - - return true -} - -func sendMsg(t *testing.T, expected bool, id int) { - if t.Failed() { - return - } - - opt := MessageParams{KeySym: sharedKey, Topic: sharedTopic, Payload: expectedMessage, PoW: 0.00000001, WorkTime: 1} - if !expected { - opt.KeySym = wrongKey - opt.Topic = wrongTopic - opt.Payload = unexpectedMessage - opt.Payload[0] = byte(id) - } - - msg, err := NewSentMessage(&opt) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - envelope, err := msg.Wrap(&opt) - if err != nil { - t.Fatalf("failed to seal message: %s", err) - } - - err = nodes[id].shh.Send(envelope) - if err != nil { - t.Fatalf("failed to send message: %s", err) - } -} - -func TestPeerBasic(t *testing.T) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d.", seed) - } - - params.PoW = 0.001 - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d.", seed) - } - - p := newPeer(nil, nil, nil) - p.mark(env) - if !p.marked(env) { - t.Fatalf("failed mark with seed %d.", seed) - } -} - -func checkPowExchangeForNodeZero(t *testing.T) { - const iterations = 200 - for j := 0; j < iterations; j++ { - lastCycle := (j == iterations-1) - ok := checkPowExchangeForNodeZeroOnce(t, lastCycle) - if ok { - break - } - time.Sleep(50 * time.Millisecond) - } -} - -func checkPowExchangeForNodeZeroOnce(t *testing.T, mustPass bool) bool { - cnt := 0 - for i, node := range nodes { - for peer := range node.shh.peers { - if peer.peer.ID() == discover.PubkeyID(&nodes[0].id.PublicKey) { - cnt++ - if peer.powRequirement != masterPow { - if mustPass { - t.Fatalf("node %d: failed to set the new pow requirement for node zero.", i) - } else { - return false - } - } - } - } - } - if cnt == 0 { - t.Fatalf("looking for node zero: no matching peers found.") - } - return true -} - -func checkPowExchange(t *testing.T) { - for i, node := range nodes { - for peer := range node.shh.peers { - if peer.peer.ID() != discover.PubkeyID(&nodes[0].id.PublicKey) { - if peer.powRequirement != masterPow { - t.Fatalf("node %d: failed to exchange pow requirement in round %d; expected %f, got %f", - i, round, masterPow, peer.powRequirement) - } - } - } - } -} - -func checkBloomFilterExchangeOnce(t *testing.T, mustPass bool) bool { - for i, node := range nodes { - for peer := range node.shh.peers { - peer.bloomMu.Lock() - equals := bytes.Equal(peer.bloomFilter, masterBloomFilter) - peer.bloomMu.Unlock() - if !equals { - if mustPass { - t.Fatalf("node %d: failed to exchange bloom filter requirement in round %d. \n%x expected \n%x got", - i, round, masterBloomFilter, peer.bloomFilter) - } else { - return false - } - } - } - } - - return true -} - -func checkBloomFilterExchange(t *testing.T) { - const iterations = 200 - for j := 0; j < iterations; j++ { - lastCycle := (j == iterations-1) - ok := checkBloomFilterExchangeOnce(t, lastCycle) - if ok { - break - } - time.Sleep(50 * time.Millisecond) - } -} diff --git a/whisper/whisperv6/topic.go b/whisper/whisperv6/topic.go deleted file mode 100644 index 21941f69afa5..000000000000 --- a/whisper/whisperv6/topic.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains the Whisper protocol Topic element. - -package whisperv6 - -import ( - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/common/hexutil" -) - -// TopicType represents a cryptographically secure, probabilistic partial -// classifications of a message, determined as the first (left) 4 bytes of the -// SHA3 hash of some arbitrary data given by the original author of the message. -type TopicType [TopicLength]byte - -// BytesToTopic converts from the byte array representation of a topic -// into the TopicType type. -func BytesToTopic(b []byte) (t TopicType) { - sz := TopicLength - if x := len(b); x < TopicLength { - sz = x - } - for i := 0; i < sz; i++ { - t[i] = b[i] - } - return t -} - -// String converts a topic byte array to a string representation. -func (t *TopicType) String() string { - return common.ToHex(t[:]) -} - -// MarshalText returns the hex representation of t. -func (t TopicType) MarshalText() ([]byte, error) { - return hexutil.Bytes(t[:]).MarshalText() -} - -// UnmarshalText parses a hex representation to a topic. -func (t *TopicType) UnmarshalText(input []byte) error { - return hexutil.UnmarshalFixedText("Topic", input, t[:]) -} diff --git a/whisper/whisperv6/topic_test.go b/whisper/whisperv6/topic_test.go deleted file mode 100644 index 454afe0de17d..000000000000 --- a/whisper/whisperv6/topic_test.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "encoding/json" - "testing" -) - -var topicStringTests = []struct { - topic TopicType - str string -}{ - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, str: "0x00000000"}, - {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, str: "0x007f80ff"}, - {topic: TopicType{0xff, 0x80, 0x7f, 0x00}, str: "0xff807f00"}, - {topic: TopicType{0xf2, 0x6e, 0x77, 0x79}, str: "0xf26e7779"}, -} - -func TestTopicString(t *testing.T) { - for i, tst := range topicStringTests { - s := tst.topic.String() - if s != tst.str { - t.Fatalf("failed test %d: have %s, want %s.", i, s, tst.str) - } - } -} - -var bytesToTopicTests = []struct { - data []byte - topic TopicType -}{ - {topic: TopicType{0x8f, 0x9a, 0x2b, 0x7d}, data: []byte{0x8f, 0x9a, 0x2b, 0x7d}}, - {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, data: []byte{0x00, 0x7f, 0x80, 0xff}}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{0x00, 0x00, 0x00, 0x00}}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{0x00, 0x00, 0x00}}, - {topic: TopicType{0x01, 0x00, 0x00, 0x00}, data: []byte{0x01}}, - {topic: TopicType{0x00, 0xfe, 0x00, 0x00}, data: []byte{0x00, 0xfe}}, - {topic: TopicType{0xea, 0x1d, 0x43, 0x00}, data: []byte{0xea, 0x1d, 0x43}}, - {topic: TopicType{0x6f, 0x3c, 0xb0, 0xdd}, data: []byte{0x6f, 0x3c, 0xb0, 0xdd, 0x0f, 0x00, 0x90}}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{}}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: nil}, -} - -var unmarshalTestsGood = []struct { - topic TopicType - data []byte -}{ - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x00000000"`)}, - {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, data: []byte(`"0x007f80ff"`)}, - {topic: TopicType{0xff, 0x80, 0x7f, 0x00}, data: []byte(`"0xff807f00"`)}, - {topic: TopicType{0xf2, 0x6e, 0x77, 0x79}, data: []byte(`"0xf26e7779"`)}, -} - -var unmarshalTestsBad = []struct { - topic TopicType - data []byte -}{ - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x000000"`)}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x0000000"`)}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x000000000"`)}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x0000000000"`)}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"000000"`)}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0000000"`)}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"000000000"`)}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0000000000"`)}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"abcdefg0"`)}, -} - -var unmarshalTestsUgly = []struct { - topic TopicType - data []byte -}{ - {topic: TopicType{0x01, 0x00, 0x00, 0x00}, data: []byte(`"0x00000001"`)}, -} - -func TestBytesToTopic(t *testing.T) { - for i, tst := range bytesToTopicTests { - top := BytesToTopic(tst.data) - if top != tst.topic { - t.Fatalf("failed test %d: have %v, want %v.", i, t, tst.topic) - } - } -} - -func TestUnmarshalTestsGood(t *testing.T) { - for i, tst := range unmarshalTestsGood { - var top TopicType - err := json.Unmarshal(tst.data, &top) - if err != nil { - t.Errorf("failed test %d. input: %v. err: %v", i, tst.data, err) - } else if top != tst.topic { - t.Errorf("failed test %d: have %v, want %v.", i, t, tst.topic) - } - } -} - -func TestUnmarshalTestsBad(t *testing.T) { - // in this test UnmarshalJSON() is supposed to fail - for i, tst := range unmarshalTestsBad { - var top TopicType - err := json.Unmarshal(tst.data, &top) - if err == nil { - t.Fatalf("failed test %d. input: %v.", i, tst.data) - } - } -} - -func TestUnmarshalTestsUgly(t *testing.T) { - // in this test UnmarshalJSON() is NOT supposed to fail, but result should be wrong - for i, tst := range unmarshalTestsUgly { - var top TopicType - err := json.Unmarshal(tst.data, &top) - if err != nil { - t.Errorf("failed test %d. input: %v.", i, tst.data) - } else if top == tst.topic { - t.Errorf("failed test %d: have %v, want %v.", i, top, tst.topic) - } - } -} diff --git a/whisper/whisperv6/whisper.go b/whisper/whisperv6/whisper.go deleted file mode 100644 index 28fed9f676ac..000000000000 --- a/whisper/whisperv6/whisper.go +++ /dev/null @@ -1,1049 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "bytes" - "crypto/ecdsa" - "crypto/sha256" - "fmt" - "math" - "runtime" - "sync" - "time" - - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/log" - "github.com/XinFinOrg/XDPoSChain/p2p" - "github.com/XinFinOrg/XDPoSChain/rlp" - "github.com/XinFinOrg/XDPoSChain/rpc" - mapset "github.com/deckarep/golang-set" - "github.com/syndtr/goleveldb/leveldb/errors" - "golang.org/x/crypto/pbkdf2" - "golang.org/x/sync/syncmap" -) - -// Statistics holds several message-related counter for analytics -// purposes. -type Statistics struct { - messagesCleared int - memoryCleared int - memoryUsed int - cycles int - totalMessagesCleared int -} - -const ( - maxMsgSizeIdx = iota // Maximal message length allowed by the whisper node - overflowIdx // Indicator of message queue overflow - minPowIdx // Minimal PoW required by the whisper node - minPowToleranceIdx // Minimal PoW tolerated by the whisper node for a limited time - bloomFilterIdx // Bloom filter for topics of interest for this node - bloomFilterToleranceIdx // Bloom filter tolerated by the whisper node for a limited time -) - -// Whisper represents a dark communication interface through the Ethereum -// network, using its very own P2P communication layer. -type Whisper struct { - protocol p2p.Protocol // Protocol description and parameters - filters *Filters // Message filters installed with Subscribe function - - privateKeys map[string]*ecdsa.PrivateKey // Private key storage - symKeys map[string][]byte // Symmetric key storage - keyMu sync.RWMutex // Mutex associated with key storages - - poolMu sync.RWMutex // Mutex to sync the message and expiration pools - envelopes map[common.Hash]*Envelope // Pool of envelopes currently tracked by this node - expirations map[uint32]mapset.Set // Message expiration pool - - peerMu sync.RWMutex // Mutex to sync the active peer set - peers map[*Peer]struct{} // Set of currently active peers - - messageQueue chan *Envelope // Message queue for normal whisper messages - p2pMsgQueue chan *Envelope // Message queue for peer-to-peer messages (not to be forwarded any further) - quit chan struct{} // Channel used for graceful exit - - settings syncmap.Map // holds configuration settings that can be dynamically changed - - syncAllowance int // maximum time in seconds allowed to process the whisper-related messages - - lightClient bool // indicates is this node is pure light client (does not forward any messages) - - statsMu sync.Mutex // guard stats - stats Statistics // Statistics of whisper node - - mailServer MailServer // MailServer interface -} - -// New creates a Whisper client ready to communicate through the Ethereum P2P network. -func New(cfg *Config) *Whisper { - if cfg == nil { - cfg = &DefaultConfig - } - - whisper := &Whisper{ - privateKeys: make(map[string]*ecdsa.PrivateKey), - symKeys: make(map[string][]byte), - envelopes: make(map[common.Hash]*Envelope), - expirations: make(map[uint32]mapset.Set), - peers: make(map[*Peer]struct{}), - messageQueue: make(chan *Envelope, messageQueueLimit), - p2pMsgQueue: make(chan *Envelope, messageQueueLimit), - quit: make(chan struct{}), - syncAllowance: DefaultSyncAllowance, - } - - whisper.filters = NewFilters(whisper) - - whisper.settings.Store(minPowIdx, cfg.MinimumAcceptedPOW) - whisper.settings.Store(maxMsgSizeIdx, cfg.MaxMessageSize) - whisper.settings.Store(overflowIdx, false) - - // p2p whisper sub protocol handler - whisper.protocol = p2p.Protocol{ - Name: ProtocolName, - Version: uint(ProtocolVersion), - Length: NumberOfMessageCodes, - Run: whisper.HandlePeer, - NodeInfo: func() interface{} { - return map[string]interface{}{ - "version": ProtocolVersionStr, - "maxMessageSize": whisper.MaxMessageSize(), - "minimumPoW": whisper.MinPow(), - } - }, - } - - return whisper -} - -// MinPow returns the PoW value required by this node. -func (whisper *Whisper) MinPow() float64 { - val, exist := whisper.settings.Load(minPowIdx) - if !exist || val == nil { - return DefaultMinimumPoW - } - v, ok := val.(float64) - if !ok { - log.Error("Error loading minPowIdx, using default") - return DefaultMinimumPoW - } - return v -} - -// MinPowTolerance returns the value of minimum PoW which is tolerated for a limited -// time after PoW was changed. If sufficient time have elapsed or no change of PoW -// have ever occurred, the return value will be the same as return value of MinPow(). -func (whisper *Whisper) MinPowTolerance() float64 { - val, exist := whisper.settings.Load(minPowToleranceIdx) - if !exist || val == nil { - return DefaultMinimumPoW - } - return val.(float64) -} - -// BloomFilter returns the aggregated bloom filter for all the topics of interest. -// The nodes are required to send only messages that match the advertised bloom filter. -// If a message does not match the bloom, it will tantamount to spam, and the peer will -// be disconnected. -func (whisper *Whisper) BloomFilter() []byte { - val, exist := whisper.settings.Load(bloomFilterIdx) - if !exist || val == nil { - return nil - } - return val.([]byte) -} - -// BloomFilterTolerance returns the bloom filter which is tolerated for a limited -// time after new bloom was advertised to the peers. If sufficient time have elapsed -// or no change of bloom filter have ever occurred, the return value will be the same -// as return value of BloomFilter(). -func (whisper *Whisper) BloomFilterTolerance() []byte { - val, exist := whisper.settings.Load(bloomFilterToleranceIdx) - if !exist || val == nil { - return nil - } - return val.([]byte) -} - -// MaxMessageSize returns the maximum accepted message size. -func (whisper *Whisper) MaxMessageSize() uint32 { - val, _ := whisper.settings.Load(maxMsgSizeIdx) - return val.(uint32) -} - -// Overflow returns an indication if the message queue is full. -func (whisper *Whisper) Overflow() bool { - val, _ := whisper.settings.Load(overflowIdx) - return val.(bool) -} - -// APIs returns the RPC descriptors the Whisper implementation offers -func (whisper *Whisper) APIs() []rpc.API { - return []rpc.API{ - { - Namespace: ProtocolName, - Version: ProtocolVersionStr, - Service: NewPublicWhisperAPI(whisper), - Public: true, - }, - } -} - -// RegisterServer registers MailServer interface. -// MailServer will process all the incoming messages with p2pRequestCode. -func (whisper *Whisper) RegisterServer(server MailServer) { - whisper.mailServer = server -} - -// Protocols returns the whisper sub-protocols ran by this particular client. -func (whisper *Whisper) Protocols() []p2p.Protocol { - return []p2p.Protocol{whisper.protocol} -} - -// Version returns the whisper sub-protocols version number. -func (whisper *Whisper) Version() uint { - return whisper.protocol.Version -} - -// SetMaxMessageSize sets the maximal message size allowed by this node -func (whisper *Whisper) SetMaxMessageSize(size uint32) error { - if size > MaxMessageSize { - return fmt.Errorf("message size too large [%d>%d]", size, MaxMessageSize) - } - whisper.settings.Store(maxMsgSizeIdx, size) - return nil -} - -// SetBloomFilter sets the new bloom filter -func (whisper *Whisper) SetBloomFilter(bloom []byte) error { - if len(bloom) != BloomFilterSize { - return fmt.Errorf("invalid bloom filter size: %d", len(bloom)) - } - - b := make([]byte, BloomFilterSize) - copy(b, bloom) - - whisper.settings.Store(bloomFilterIdx, b) - whisper.notifyPeersAboutBloomFilterChange(b) - - go func() { - // allow some time before all the peers have processed the notification - time.Sleep(time.Duration(whisper.syncAllowance) * time.Second) - whisper.settings.Store(bloomFilterToleranceIdx, b) - }() - - return nil -} - -// SetMinimumPoW sets the minimal PoW required by this node -func (whisper *Whisper) SetMinimumPoW(val float64) error { - if val < 0.0 { - return fmt.Errorf("invalid PoW: %f", val) - } - - whisper.settings.Store(minPowIdx, val) - whisper.notifyPeersAboutPowRequirementChange(val) - - go func() { - // allow some time before all the peers have processed the notification - time.Sleep(time.Duration(whisper.syncAllowance) * time.Second) - whisper.settings.Store(minPowToleranceIdx, val) - }() - - return nil -} - -// SetMinimumPowTest sets the minimal PoW in test environment -func (whisper *Whisper) SetMinimumPowTest(val float64) { - whisper.settings.Store(minPowIdx, val) - whisper.notifyPeersAboutPowRequirementChange(val) - whisper.settings.Store(minPowToleranceIdx, val) -} - -func (whisper *Whisper) notifyPeersAboutPowRequirementChange(pow float64) { - arr := whisper.getPeers() - for _, p := range arr { - err := p.notifyAboutPowRequirementChange(pow) - if err != nil { - // allow one retry - err = p.notifyAboutPowRequirementChange(pow) - } - if err != nil { - log.Warn("failed to notify peer about new pow requirement", "peer", p.ID(), "error", err) - } - } -} - -func (whisper *Whisper) notifyPeersAboutBloomFilterChange(bloom []byte) { - arr := whisper.getPeers() - for _, p := range arr { - err := p.notifyAboutBloomFilterChange(bloom) - if err != nil { - // allow one retry - err = p.notifyAboutBloomFilterChange(bloom) - } - if err != nil { - log.Warn("failed to notify peer about new bloom filter", "peer", p.ID(), "error", err) - } - } -} - -func (whisper *Whisper) getPeers() []*Peer { - arr := make([]*Peer, len(whisper.peers)) - i := 0 - whisper.peerMu.Lock() - for p := range whisper.peers { - arr[i] = p - i++ - } - whisper.peerMu.Unlock() - return arr -} - -// getPeer retrieves peer by ID -func (whisper *Whisper) getPeer(peerID []byte) (*Peer, error) { - whisper.peerMu.Lock() - defer whisper.peerMu.Unlock() - for p := range whisper.peers { - id := p.peer.ID() - if bytes.Equal(peerID, id[:]) { - return p, nil - } - } - return nil, fmt.Errorf("could not find peer with ID: %x", peerID) -} - -// AllowP2PMessagesFromPeer marks specific peer trusted, -// which will allow it to send historic (expired) messages. -func (whisper *Whisper) AllowP2PMessagesFromPeer(peerID []byte) error { - p, err := whisper.getPeer(peerID) - if err != nil { - return err - } - p.trusted = true - return nil -} - -// RequestHistoricMessages sends a message with p2pRequestCode to a specific peer, -// which is known to implement MailServer interface, and is supposed to process this -// request and respond with a number of peer-to-peer messages (possibly expired), -// which are not supposed to be forwarded any further. -// The whisper protocol is agnostic of the format and contents of envelope. -func (whisper *Whisper) RequestHistoricMessages(peerID []byte, envelope *Envelope) error { - p, err := whisper.getPeer(peerID) - if err != nil { - return err - } - p.trusted = true - return p2p.Send(p.ws, p2pRequestCode, envelope) -} - -// SendP2PMessage sends a peer-to-peer message to a specific peer. -func (whisper *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error { - p, err := whisper.getPeer(peerID) - if err != nil { - return err - } - return whisper.SendP2PDirect(p, envelope) -} - -// SendP2PDirect sends a peer-to-peer message to a specific peer. -func (whisper *Whisper) SendP2PDirect(peer *Peer, envelope *Envelope) error { - return p2p.Send(peer.ws, p2pMessageCode, envelope) -} - -// NewKeyPair generates a new cryptographic identity for the client, and injects -// it into the known identities for message decryption. Returns ID of the new key pair. -func (whisper *Whisper) NewKeyPair() (string, error) { - key, err := crypto.GenerateKey() - if err != nil || !validatePrivateKey(key) { - key, err = crypto.GenerateKey() // retry once - } - if err != nil { - return "", err - } - if !validatePrivateKey(key) { - return "", errors.New("failed to generate valid key") - } - - id, err := GenerateRandomID() - if err != nil { - return "", fmt.Errorf("failed to generate ID: %s", err) - } - - whisper.keyMu.Lock() - defer whisper.keyMu.Unlock() - - if whisper.privateKeys[id] != nil { - return "", errors.New("failed to generate unique ID") - } - whisper.privateKeys[id] = key - return id, nil -} - -// DeleteKeyPair deletes the specified key if it exists. -func (whisper *Whisper) DeleteKeyPair(key string) bool { - whisper.keyMu.Lock() - defer whisper.keyMu.Unlock() - - if whisper.privateKeys[key] != nil { - delete(whisper.privateKeys, key) - return true - } - return false -} - -// AddKeyPair imports a asymmetric private key and returns it identifier. -func (whisper *Whisper) AddKeyPair(key *ecdsa.PrivateKey) (string, error) { - id, err := GenerateRandomID() - if err != nil { - return "", fmt.Errorf("failed to generate ID: %s", err) - } - - whisper.keyMu.Lock() - whisper.privateKeys[id] = key - whisper.keyMu.Unlock() - - return id, nil -} - -// HasKeyPair checks if the the whisper node is configured with the private key -// of the specified public pair. -func (whisper *Whisper) HasKeyPair(id string) bool { - whisper.keyMu.RLock() - defer whisper.keyMu.RUnlock() - return whisper.privateKeys[id] != nil -} - -// GetPrivateKey retrieves the private key of the specified identity. -func (whisper *Whisper) GetPrivateKey(id string) (*ecdsa.PrivateKey, error) { - whisper.keyMu.RLock() - defer whisper.keyMu.RUnlock() - key := whisper.privateKeys[id] - if key == nil { - return nil, errors.New("invalid id") - } - return key, nil -} - -// GenerateSymKey generates a random symmetric key and stores it under id, -// which is then returned. Will be used in the future for session key exchange. -func (whisper *Whisper) GenerateSymKey() (string, error) { - key, err := generateSecureRandomData(aesKeyLength) - if err != nil { - return "", err - } else if !validateDataIntegrity(key, aesKeyLength) { - return "", errors.New("error in GenerateSymKey: crypto/rand failed to generate random data") - } - - id, err := GenerateRandomID() - if err != nil { - return "", fmt.Errorf("failed to generate ID: %s", err) - } - - whisper.keyMu.Lock() - defer whisper.keyMu.Unlock() - - if whisper.symKeys[id] != nil { - return "", errors.New("failed to generate unique ID") - } - whisper.symKeys[id] = key - return id, nil -} - -// AddSymKeyDirect stores the key, and returns its id. -func (whisper *Whisper) AddSymKeyDirect(key []byte) (string, error) { - if len(key) != aesKeyLength { - return "", fmt.Errorf("wrong key size: %d", len(key)) - } - - id, err := GenerateRandomID() - if err != nil { - return "", fmt.Errorf("failed to generate ID: %s", err) - } - - whisper.keyMu.Lock() - defer whisper.keyMu.Unlock() - - if whisper.symKeys[id] != nil { - return "", errors.New("failed to generate unique ID") - } - whisper.symKeys[id] = key - return id, nil -} - -// AddSymKeyFromPassword generates the key from password, stores it, and returns its id. -func (whisper *Whisper) AddSymKeyFromPassword(password string) (string, error) { - id, err := GenerateRandomID() - if err != nil { - return "", fmt.Errorf("failed to generate ID: %s", err) - } - if whisper.HasSymKey(id) { - return "", errors.New("failed to generate unique ID") - } - - // kdf should run no less than 0.1 seconds on an average computer, - // because it's an once in a session experience - derived := pbkdf2.Key([]byte(password), nil, 65356, aesKeyLength, sha256.New) - - whisper.keyMu.Lock() - defer whisper.keyMu.Unlock() - - // double check is necessary, because deriveKeyMaterial() is very slow - if whisper.symKeys[id] != nil { - return "", errors.New("critical error: failed to generate unique ID") - } - whisper.symKeys[id] = derived - return id, nil -} - -// HasSymKey returns true if there is a key associated with the given id. -// Otherwise returns false. -func (whisper *Whisper) HasSymKey(id string) bool { - whisper.keyMu.RLock() - defer whisper.keyMu.RUnlock() - return whisper.symKeys[id] != nil -} - -// DeleteSymKey deletes the key associated with the name string if it exists. -func (whisper *Whisper) DeleteSymKey(id string) bool { - whisper.keyMu.Lock() - defer whisper.keyMu.Unlock() - if whisper.symKeys[id] != nil { - delete(whisper.symKeys, id) - return true - } - return false -} - -// GetSymKey returns the symmetric key associated with the given id. -func (whisper *Whisper) GetSymKey(id string) ([]byte, error) { - whisper.keyMu.RLock() - defer whisper.keyMu.RUnlock() - if whisper.symKeys[id] != nil { - return whisper.symKeys[id], nil - } - return nil, errors.New("non-existent key ID") -} - -// Subscribe installs a new message handler used for filtering, decrypting -// and subsequent storing of incoming messages. -func (whisper *Whisper) Subscribe(f *Filter) (string, error) { - s, err := whisper.filters.Install(f) - if err == nil { - whisper.updateBloomFilter(f) - } - return s, err -} - -// updateBloomFilter recalculates the new value of bloom filter, -// and informs the peers if necessary. -func (whisper *Whisper) updateBloomFilter(f *Filter) { - aggregate := make([]byte, BloomFilterSize) - for _, t := range f.Topics { - top := BytesToTopic(t) - b := TopicToBloom(top) - aggregate = addBloom(aggregate, b) - } - - if !BloomFilterMatch(whisper.BloomFilter(), aggregate) { - // existing bloom filter must be updated - aggregate = addBloom(whisper.BloomFilter(), aggregate) - whisper.SetBloomFilter(aggregate) - } -} - -// GetFilter returns the filter by id. -func (whisper *Whisper) GetFilter(id string) *Filter { - return whisper.filters.Get(id) -} - -// Unsubscribe removes an installed message handler. -func (whisper *Whisper) Unsubscribe(id string) error { - ok := whisper.filters.Uninstall(id) - if !ok { - return errors.New("Unsubscribe: Invalid ID") - } - return nil -} - -// Send injects a message into the whisper send queue, to be distributed in the -// network in the coming cycles. -func (whisper *Whisper) Send(envelope *Envelope) error { - ok, err := whisper.add(envelope, false) - if err == nil && !ok { - return errors.New("failed to add envelope") - } - return err -} - -// Start implements node.Service, starting the background data propagation thread -// of the Whisper protocol. -func (whisper *Whisper) Start(*p2p.Server) error { - log.Info("started whisper v." + ProtocolVersionStr) - go whisper.update() - - numCPU := runtime.NumCPU() - for i := 0; i < numCPU; i++ { - go whisper.processQueue() - } - - return nil -} -func (whisper *Whisper) SaveData() { -} - -// Stop implements node.Service, stopping the background data propagation thread -// of the Whisper protocol. -func (whisper *Whisper) Stop() error { - close(whisper.quit) - log.Info("whisper stopped") - return nil -} - -// HandlePeer is called by the underlying P2P layer when the whisper sub-protocol -// connection is negotiated. -func (whisper *Whisper) HandlePeer(peer *p2p.Peer, rw p2p.MsgReadWriter) error { - // Create the new peer and start tracking it - whisperPeer := newPeer(whisper, peer, rw) - - whisper.peerMu.Lock() - whisper.peers[whisperPeer] = struct{}{} - whisper.peerMu.Unlock() - - defer func() { - whisper.peerMu.Lock() - delete(whisper.peers, whisperPeer) - whisper.peerMu.Unlock() - }() - - // Run the peer handshake and state updates - if err := whisperPeer.handshake(); err != nil { - return err - } - whisperPeer.start() - defer whisperPeer.stop() - - return whisper.runMessageLoop(whisperPeer, rw) -} - -// runMessageLoop reads and processes inbound messages directly to merge into client-global state. -func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error { - for { - // fetch the next packet - packet, err := rw.ReadMsg() - if err != nil { - log.Warn("message loop", "peer", p.peer.ID(), "err", err) - return err - } - if packet.Size > whisper.MaxMessageSize() { - log.Warn("oversized message received", "peer", p.peer.ID()) - return errors.New("oversized message received") - } - - switch packet.Code { - case statusCode: - // this should not happen, but no need to panic; just ignore this message. - log.Warn("unxepected status message received", "peer", p.peer.ID()) - case messagesCode: - // decode the contained envelopes - var envelopes []*Envelope - if err := packet.Decode(&envelopes); err != nil { - log.Warn("failed to decode envelopes, peer will be disconnected", "peer", p.peer.ID(), "err", err) - return errors.New("invalid envelopes") - } - - trouble := false - for _, env := range envelopes { - cached, err := whisper.add(env, whisper.lightClient) - if err != nil { - trouble = true - log.Error("bad envelope received, peer will be disconnected", "peer", p.peer.ID(), "err", err) - } - if cached { - p.mark(env) - } - } - - if trouble { - return errors.New("invalid envelope") - } - case powRequirementCode: - s := rlp.NewStream(packet.Payload, uint64(packet.Size)) - i, err := s.Uint() - if err != nil { - log.Warn("failed to decode powRequirementCode message, peer will be disconnected", "peer", p.peer.ID(), "err", err) - return errors.New("invalid powRequirementCode message") - } - f := math.Float64frombits(i) - if math.IsInf(f, 0) || math.IsNaN(f) || f < 0.0 { - log.Warn("invalid value in powRequirementCode message, peer will be disconnected", "peer", p.peer.ID(), "err", err) - return errors.New("invalid value in powRequirementCode message") - } - p.powRequirement = f - case bloomFilterExCode: - var bloom []byte - err := packet.Decode(&bloom) - if err == nil && len(bloom) != BloomFilterSize { - err = fmt.Errorf("wrong bloom filter size %d", len(bloom)) - } - - if err != nil { - log.Warn("failed to decode bloom filter exchange message, peer will be disconnected", "peer", p.peer.ID(), "err", err) - return errors.New("invalid bloom filter exchange message") - } - p.setBloomFilter(bloom) - case p2pMessageCode: - // peer-to-peer message, sent directly to peer bypassing PoW checks, etc. - // this message is not supposed to be forwarded to other peers, and - // therefore might not satisfy the PoW, expiry and other requirements. - // these messages are only accepted from the trusted peer. - if p.trusted { - var envelope Envelope - if err := packet.Decode(&envelope); err != nil { - log.Warn("failed to decode direct message, peer will be disconnected", "peer", p.peer.ID(), "err", err) - return errors.New("invalid direct message") - } - whisper.postEvent(&envelope, true) - } - case p2pRequestCode: - // Must be processed if mail server is implemented. Otherwise ignore. - if whisper.mailServer != nil { - var request Envelope - if err := packet.Decode(&request); err != nil { - log.Warn("failed to decode p2p request message, peer will be disconnected", "peer", p.peer.ID(), "err", err) - return errors.New("invalid p2p request") - } - whisper.mailServer.DeliverMail(p, &request) - } - default: - // New message types might be implemented in the future versions of Whisper. - // For forward compatibility, just ignore. - } - - packet.Discard() - } -} - -// add inserts a new envelope into the message pool to be distributed within the -// whisper network. It also inserts the envelope into the expiration pool at the -// appropriate time-stamp. In case of error, connection should be dropped. -// param isP2P indicates whether the message is peer-to-peer (should not be forwarded). -func (whisper *Whisper) add(envelope *Envelope, isP2P bool) (bool, error) { - now := uint32(time.Now().Unix()) - sent := envelope.Expiry - envelope.TTL - - if sent > now { - if sent-DefaultSyncAllowance > now { - return false, fmt.Errorf("envelope created in the future [%x]", envelope.Hash()) - } - // recalculate PoW, adjusted for the time difference, plus one second for latency - envelope.calculatePoW(sent - now + 1) - } - - if envelope.Expiry < now { - if envelope.Expiry+DefaultSyncAllowance*2 < now { - return false, errors.New("very old message") - } - log.Debug("expired envelope dropped", "hash", envelope.Hash().Hex()) - return false, nil // drop envelope without error - } - - if uint32(envelope.size()) > whisper.MaxMessageSize() { - return false, fmt.Errorf("huge messages are not allowed [%x]", envelope.Hash()) - } - - if envelope.PoW() < whisper.MinPow() { - // maybe the value was recently changed, and the peers did not adjust yet. - // in this case the previous value is retrieved by MinPowTolerance() - // for a short period of peer synchronization. - if envelope.PoW() < whisper.MinPowTolerance() { - return false, fmt.Errorf("envelope with low PoW received: PoW=%f, hash=[%v]", envelope.PoW(), envelope.Hash().Hex()) - } - } - - if !BloomFilterMatch(whisper.BloomFilter(), envelope.Bloom()) { - // maybe the value was recently changed, and the peers did not adjust yet. - // in this case the previous value is retrieved by BloomFilterTolerance() - // for a short period of peer synchronization. - if !BloomFilterMatch(whisper.BloomFilterTolerance(), envelope.Bloom()) { - return false, fmt.Errorf("envelope does not match bloom filter, hash=[%v], bloom: \n%x \n%x \n%x", - envelope.Hash().Hex(), whisper.BloomFilter(), envelope.Bloom(), envelope.Topic) - } - } - - hash := envelope.Hash() - - whisper.poolMu.Lock() - _, alreadyCached := whisper.envelopes[hash] - if !alreadyCached { - whisper.envelopes[hash] = envelope - if whisper.expirations[envelope.Expiry] == nil { - whisper.expirations[envelope.Expiry] = mapset.NewThreadUnsafeSet() - } - if !whisper.expirations[envelope.Expiry].Contains(hash) { - whisper.expirations[envelope.Expiry].Add(hash) - } - } - whisper.poolMu.Unlock() - - if alreadyCached { - log.Trace("whisper envelope already cached", "hash", envelope.Hash().Hex()) - } else { - log.Trace("cached whisper envelope", "hash", envelope.Hash().Hex()) - whisper.statsMu.Lock() - whisper.stats.memoryUsed += envelope.size() - whisper.statsMu.Unlock() - whisper.postEvent(envelope, isP2P) // notify the local node about the new message - if whisper.mailServer != nil { - whisper.mailServer.Archive(envelope) - } - } - return true, nil -} - -// postEvent queues the message for further processing. -func (whisper *Whisper) postEvent(envelope *Envelope, isP2P bool) { - if isP2P { - whisper.p2pMsgQueue <- envelope - } else { - whisper.checkOverflow() - whisper.messageQueue <- envelope - } -} - -// checkOverflow checks if message queue overflow occurs and reports it if necessary. -func (whisper *Whisper) checkOverflow() { - queueSize := len(whisper.messageQueue) - - if queueSize == messageQueueLimit { - if !whisper.Overflow() { - whisper.settings.Store(overflowIdx, true) - log.Warn("message queue overflow") - } - } else if queueSize <= messageQueueLimit/2 { - if whisper.Overflow() { - whisper.settings.Store(overflowIdx, false) - log.Warn("message queue overflow fixed (back to normal)") - } - } -} - -// processQueue delivers the messages to the watchers during the lifetime of the whisper node. -func (whisper *Whisper) processQueue() { - var e *Envelope - for { - select { - case <-whisper.quit: - return - - case e = <-whisper.messageQueue: - whisper.filters.NotifyWatchers(e, false) - - case e = <-whisper.p2pMsgQueue: - whisper.filters.NotifyWatchers(e, true) - } - } -} - -// update loops until the lifetime of the whisper node, updating its internal -// state by expiring stale messages from the pool. -func (whisper *Whisper) update() { - // Start a ticker to check for expirations - expire := time.NewTicker(expirationCycle) - - // Repeat updates until termination is requested - for { - select { - case <-expire.C: - whisper.expire() - - case <-whisper.quit: - return - } - } -} - -// expire iterates over all the expiration timestamps, removing all stale -// messages from the pools. -func (whisper *Whisper) expire() { - whisper.poolMu.Lock() - defer whisper.poolMu.Unlock() - - whisper.statsMu.Lock() - defer whisper.statsMu.Unlock() - whisper.stats.reset() - now := uint32(time.Now().Unix()) - for expiry, hashSet := range whisper.expirations { - if expiry < now { - // Dump all expired messages and remove timestamp - hashSet.Each(func(v interface{}) bool { - sz := whisper.envelopes[v.(common.Hash)].size() - delete(whisper.envelopes, v.(common.Hash)) - whisper.stats.messagesCleared++ - whisper.stats.memoryCleared += sz - whisper.stats.memoryUsed -= sz - return true - }) - whisper.expirations[expiry].Clear() - delete(whisper.expirations, expiry) - } - } -} - -// Stats returns the whisper node statistics. -func (whisper *Whisper) Stats() Statistics { - whisper.statsMu.Lock() - defer whisper.statsMu.Unlock() - - return whisper.stats -} - -// Envelopes retrieves all the messages currently pooled by the node. -func (whisper *Whisper) Envelopes() []*Envelope { - whisper.poolMu.RLock() - defer whisper.poolMu.RUnlock() - - all := make([]*Envelope, 0, len(whisper.envelopes)) - for _, envelope := range whisper.envelopes { - all = append(all, envelope) - } - return all -} - -// isEnvelopeCached checks if envelope with specific hash has already been received and cached. -func (whisper *Whisper) isEnvelopeCached(hash common.Hash) bool { - whisper.poolMu.Lock() - defer whisper.poolMu.Unlock() - - _, exist := whisper.envelopes[hash] - return exist -} - -// reset resets the node's statistics after each expiry cycle. -func (s *Statistics) reset() { - s.cycles++ - s.totalMessagesCleared += s.messagesCleared - - s.memoryCleared = 0 - s.messagesCleared = 0 -} - -// ValidatePublicKey checks the format of the given public key. -func ValidatePublicKey(k *ecdsa.PublicKey) bool { - return k != nil && k.X != nil && k.Y != nil && k.X.Sign() != 0 && k.Y.Sign() != 0 -} - -// validatePrivateKey checks the format of the given private key. -func validatePrivateKey(k *ecdsa.PrivateKey) bool { - if k == nil || k.D == nil || k.D.Sign() == 0 { - return false - } - return ValidatePublicKey(&k.PublicKey) -} - -// validateDataIntegrity returns false if the data have the wrong or contains all zeros, -// which is the simplest and the most common bug. -func validateDataIntegrity(k []byte, expectedSize int) bool { - if len(k) != expectedSize { - return false - } - if expectedSize > 3 && containsOnlyZeros(k) { - return false - } - return true -} - -// containsOnlyZeros checks if the data contain only zeros. -func containsOnlyZeros(data []byte) bool { - for _, b := range data { - if b != 0 { - return false - } - } - return true -} - -// bytesToUintLittleEndian converts the slice to 64-bit unsigned integer. -func bytesToUintLittleEndian(b []byte) (res uint64) { - mul := uint64(1) - for i := 0; i < len(b); i++ { - res += uint64(b[i]) * mul - mul *= 256 - } - return res -} - -// BytesToUintBigEndian converts the slice to 64-bit unsigned integer. -func BytesToUintBigEndian(b []byte) (res uint64) { - for i := 0; i < len(b); i++ { - res *= 256 - res += uint64(b[i]) - } - return res -} - -// GenerateRandomID generates a random string, which is then returned to be used as a key id -func GenerateRandomID() (id string, err error) { - buf, err := generateSecureRandomData(keyIDSize) - if err != nil { - return "", err - } - if !validateDataIntegrity(buf, keyIDSize) { - return "", errors.New("error in generateRandomID: crypto/rand failed to generate random data") - } - id = common.Bytes2Hex(buf) - return id, err -} - -func isFullNode(bloom []byte) bool { - if bloom == nil { - return true - } - for _, b := range bloom { - if b != 255 { - return false - } - } - return true -} - -func BloomFilterMatch(filter, sample []byte) bool { - if filter == nil { - return true - } - - for i := 0; i < BloomFilterSize; i++ { - f := filter[i] - s := sample[i] - if (f | s) != f { - return false - } - } - - return true -} - -func addBloom(a, b []byte) []byte { - c := make([]byte, BloomFilterSize) - for i := 0; i < BloomFilterSize; i++ { - c[i] = a[i] | b[i] - } - return c -} diff --git a/whisper/whisperv6/whisper_test.go b/whisper/whisperv6/whisper_test.go deleted file mode 100644 index b7d17a32cb43..000000000000 --- a/whisper/whisperv6/whisper_test.go +++ /dev/null @@ -1,885 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "bytes" - "crypto/ecdsa" - "crypto/sha256" - mrand "math/rand" - "testing" - "time" - - "github.com/XinFinOrg/XDPoSChain/common" - "golang.org/x/crypto/pbkdf2" -) - -func TestWhisperBasic(t *testing.T) { - w := New(&DefaultConfig) - p := w.Protocols() - shh := p[0] - if shh.Name != ProtocolName { - t.Fatalf("failed Protocol Name: %v.", shh.Name) - } - if uint64(shh.Version) != ProtocolVersion { - t.Fatalf("failed Protocol Version: %v.", shh.Version) - } - if shh.Length != NumberOfMessageCodes { - t.Fatalf("failed Protocol Length: %v.", shh.Length) - } - if shh.Run == nil { - t.Fatalf("failed shh.Run.") - } - if uint64(w.Version()) != ProtocolVersion { - t.Fatalf("failed whisper Version: %v.", shh.Version) - } - if w.GetFilter("non-existent") != nil { - t.Fatalf("failed GetFilter.") - } - - peerID := make([]byte, 64) - mrand.Read(peerID) - peer, _ := w.getPeer(peerID) - if peer != nil { - t.Fatal("found peer for random key.") - } - if err := w.AllowP2PMessagesFromPeer(peerID); err == nil { - t.Fatalf("failed MarkPeerTrusted.") - } - exist := w.HasSymKey("non-existing") - if exist { - t.Fatalf("failed HasSymKey.") - } - key, err := w.GetSymKey("non-existing") - if err == nil { - t.Fatalf("failed GetSymKey(non-existing): false positive.") - } - if key != nil { - t.Fatalf("failed GetSymKey: false positive.") - } - mail := w.Envelopes() - if len(mail) != 0 { - t.Fatalf("failed w.Envelopes().") - } - - derived := pbkdf2.Key([]byte(peerID), nil, 65356, aesKeyLength, sha256.New) - if !validateDataIntegrity(derived, aesKeyLength) { - t.Fatalf("failed validateSymmetricKey with param = %v.", derived) - } - if containsOnlyZeros(derived) { - t.Fatalf("failed containsOnlyZeros with param = %v.", derived) - } - - buf := []byte{0xFF, 0xE5, 0x80, 0x2, 0} - le := bytesToUintLittleEndian(buf) - be := BytesToUintBigEndian(buf) - if le != uint64(0x280e5ff) { - t.Fatalf("failed bytesToIntLittleEndian: %d.", le) - } - if be != uint64(0xffe5800200) { - t.Fatalf("failed BytesToIntBigEndian: %d.", be) - } - - id, err := w.NewKeyPair() - if err != nil { - t.Fatalf("failed to generate new key pair: %s.", err) - } - pk, err := w.GetPrivateKey(id) - if err != nil { - t.Fatalf("failed to retrieve new key pair: %s.", err) - } - if !validatePrivateKey(pk) { - t.Fatalf("failed validatePrivateKey: %v.", pk) - } - if !ValidatePublicKey(&pk.PublicKey) { - t.Fatalf("failed ValidatePublicKey: %v.", pk) - } -} - -func TestWhisperAsymmetricKeyImport(t *testing.T) { - var ( - w = New(&DefaultConfig) - privateKeys []*ecdsa.PrivateKey - ) - - for i := 0; i < 50; i++ { - id, err := w.NewKeyPair() - if err != nil { - t.Fatalf("could not generate key: %v", err) - } - - pk, err := w.GetPrivateKey(id) - if err != nil { - t.Fatalf("could not export private key: %v", err) - } - - privateKeys = append(privateKeys, pk) - - if !w.DeleteKeyPair(id) { - t.Fatalf("could not delete private key") - } - } - - for _, pk := range privateKeys { - if _, err := w.AddKeyPair(pk); err != nil { - t.Fatalf("could not import private key: %v", err) - } - } -} - -func TestWhisperIdentityManagement(t *testing.T) { - w := New(&DefaultConfig) - id1, err := w.NewKeyPair() - if err != nil { - t.Fatalf("failed to generate new key pair: %s.", err) - } - id2, err := w.NewKeyPair() - if err != nil { - t.Fatalf("failed to generate new key pair: %s.", err) - } - pk1, err := w.GetPrivateKey(id1) - if err != nil { - t.Fatalf("failed to retrieve the key pair: %s.", err) - } - pk2, err := w.GetPrivateKey(id2) - if err != nil { - t.Fatalf("failed to retrieve the key pair: %s.", err) - } - - if !w.HasKeyPair(id1) { - t.Fatalf("failed HasIdentity(pk1).") - } - if !w.HasKeyPair(id2) { - t.Fatalf("failed HasIdentity(pk2).") - } - if pk1 == nil { - t.Fatalf("failed GetIdentity(pk1).") - } - if pk2 == nil { - t.Fatalf("failed GetIdentity(pk2).") - } - - if !validatePrivateKey(pk1) { - t.Fatalf("pk1 is invalid.") - } - if !validatePrivateKey(pk2) { - t.Fatalf("pk2 is invalid.") - } - - // Delete one identity - done := w.DeleteKeyPair(id1) - if !done { - t.Fatalf("failed to delete id1.") - } - pk1, err = w.GetPrivateKey(id1) - if err == nil { - t.Fatalf("retrieve the key pair: false positive.") - } - pk2, err = w.GetPrivateKey(id2) - if err != nil { - t.Fatalf("failed to retrieve the key pair: %s.", err) - } - if w.HasKeyPair(id1) { - t.Fatalf("failed DeleteIdentity(pub1): still exist.") - } - if !w.HasKeyPair(id2) { - t.Fatalf("failed DeleteIdentity(pub1): pub2 does not exist.") - } - if pk1 != nil { - t.Fatalf("failed DeleteIdentity(pub1): first key still exist.") - } - if pk2 == nil { - t.Fatalf("failed DeleteIdentity(pub1): second key does not exist.") - } - - // Delete again non-existing identity - done = w.DeleteKeyPair(id1) - if done { - t.Fatalf("delete id1: false positive.") - } - pk1, err = w.GetPrivateKey(id1) - if err == nil { - t.Fatalf("retrieve the key pair: false positive.") - } - pk2, err = w.GetPrivateKey(id2) - if err != nil { - t.Fatalf("failed to retrieve the key pair: %s.", err) - } - if w.HasKeyPair(id1) { - t.Fatalf("failed delete non-existing identity: exist.") - } - if !w.HasKeyPair(id2) { - t.Fatalf("failed delete non-existing identity: pub2 does not exist.") - } - if pk1 != nil { - t.Fatalf("failed delete non-existing identity: first key exist.") - } - if pk2 == nil { - t.Fatalf("failed delete non-existing identity: second key does not exist.") - } - - // Delete second identity - done = w.DeleteKeyPair(id2) - if !done { - t.Fatalf("failed to delete id2.") - } - pk1, err = w.GetPrivateKey(id1) - if err == nil { - t.Fatalf("retrieve the key pair: false positive.") - } - pk2, err = w.GetPrivateKey(id2) - if err == nil { - t.Fatalf("retrieve the key pair: false positive.") - } - if w.HasKeyPair(id1) { - t.Fatalf("failed delete second identity: first identity exist.") - } - if w.HasKeyPair(id2) { - t.Fatalf("failed delete second identity: still exist.") - } - if pk1 != nil { - t.Fatalf("failed delete second identity: first key exist.") - } - if pk2 != nil { - t.Fatalf("failed delete second identity: second key exist.") - } -} - -func TestWhisperSymKeyManagement(t *testing.T) { - InitSingleTest() - - var err error - var k1, k2 []byte - w := New(&DefaultConfig) - id1 := string("arbitrary-string-1") - id2 := string("arbitrary-string-2") - - id1, err = w.GenerateSymKey() - if err != nil { - t.Fatalf("failed GenerateSymKey with seed %d: %s.", seed, err) - } - - k1, err = w.GetSymKey(id1) - if err != nil { - t.Fatalf("failed GetSymKey(id1).") - } - k2, err = w.GetSymKey(id2) - if err == nil { - t.Fatalf("failed GetSymKey(id2): false positive.") - } - if !w.HasSymKey(id1) { - t.Fatalf("failed HasSymKey(id1).") - } - if w.HasSymKey(id2) { - t.Fatalf("failed HasSymKey(id2): false positive.") - } - if k1 == nil { - t.Fatalf("first key does not exist.") - } - if k2 != nil { - t.Fatalf("second key still exist.") - } - - // add existing id, nothing should change - randomKey := make([]byte, aesKeyLength) - mrand.Read(randomKey) - id1, err = w.AddSymKeyDirect(randomKey) - if err != nil { - t.Fatalf("failed AddSymKey with seed %d: %s.", seed, err) - } - - k1, err = w.GetSymKey(id1) - if err != nil { - t.Fatalf("failed w.GetSymKey(id1).") - } - k2, err = w.GetSymKey(id2) - if err == nil { - t.Fatalf("failed w.GetSymKey(id2): false positive.") - } - if !w.HasSymKey(id1) { - t.Fatalf("failed w.HasSymKey(id1).") - } - if w.HasSymKey(id2) { - t.Fatalf("failed w.HasSymKey(id2): false positive.") - } - if k1 == nil { - t.Fatalf("first key does not exist.") - } - if !bytes.Equal(k1, randomKey) { - t.Fatalf("k1 != randomKey.") - } - if k2 != nil { - t.Fatalf("second key already exist.") - } - - id2, err = w.AddSymKeyDirect(randomKey) - if err != nil { - t.Fatalf("failed AddSymKey(id2) with seed %d: %s.", seed, err) - } - k1, err = w.GetSymKey(id1) - if err != nil { - t.Fatalf("failed w.GetSymKey(id1).") - } - k2, err = w.GetSymKey(id2) - if err != nil { - t.Fatalf("failed w.GetSymKey(id2).") - } - if !w.HasSymKey(id1) { - t.Fatalf("HasSymKey(id1) failed.") - } - if !w.HasSymKey(id2) { - t.Fatalf("HasSymKey(id2) failed.") - } - if k1 == nil { - t.Fatalf("k1 does not exist.") - } - if k2 == nil { - t.Fatalf("k2 does not exist.") - } - if !bytes.Equal(k1, k2) { - t.Fatalf("k1 != k2.") - } - if !bytes.Equal(k1, randomKey) { - t.Fatalf("k1 != randomKey.") - } - if len(k1) != aesKeyLength { - t.Fatalf("wrong length of k1.") - } - if len(k2) != aesKeyLength { - t.Fatalf("wrong length of k2.") - } - - w.DeleteSymKey(id1) - k1, err = w.GetSymKey(id1) - if err == nil { - t.Fatalf("failed w.GetSymKey(id1): false positive.") - } - if k1 != nil { - t.Fatalf("failed GetSymKey(id1): false positive.") - } - k2, err = w.GetSymKey(id2) - if err != nil { - t.Fatalf("failed w.GetSymKey(id2).") - } - if w.HasSymKey(id1) { - t.Fatalf("failed to delete first key: still exist.") - } - if !w.HasSymKey(id2) { - t.Fatalf("failed to delete first key: second key does not exist.") - } - if k1 != nil { - t.Fatalf("failed to delete first key.") - } - if k2 == nil { - t.Fatalf("failed to delete first key: second key is nil.") - } - - w.DeleteSymKey(id1) - w.DeleteSymKey(id2) - k1, err = w.GetSymKey(id1) - if err == nil { - t.Fatalf("failed w.GetSymKey(id1): false positive.") - } - k2, err = w.GetSymKey(id2) - if err == nil { - t.Fatalf("failed w.GetSymKey(id2): false positive.") - } - if k1 != nil || k2 != nil { - t.Fatalf("k1 or k2 is not nil") - } - if w.HasSymKey(id1) { - t.Fatalf("failed to delete second key: first key exist.") - } - if w.HasSymKey(id2) { - t.Fatalf("failed to delete second key: still exist.") - } - if k1 != nil { - t.Fatalf("failed to delete second key: first key is not nil.") - } - if k2 != nil { - t.Fatalf("failed to delete second key: second key is not nil.") - } - - randomKey = make([]byte, aesKeyLength+1) - mrand.Read(randomKey) - _, err = w.AddSymKeyDirect(randomKey) - if err == nil { - t.Fatalf("added the key with wrong size, seed %d.", seed) - } - - const password = "arbitrary data here" - id1, err = w.AddSymKeyFromPassword(password) - if err != nil { - t.Fatalf("failed AddSymKeyFromPassword(id1) with seed %d: %s.", seed, err) - } - id2, err = w.AddSymKeyFromPassword(password) - if err != nil { - t.Fatalf("failed AddSymKeyFromPassword(id2) with seed %d: %s.", seed, err) - } - k1, err = w.GetSymKey(id1) - if err != nil { - t.Fatalf("failed w.GetSymKey(id1).") - } - k2, err = w.GetSymKey(id2) - if err != nil { - t.Fatalf("failed w.GetSymKey(id2).") - } - if !w.HasSymKey(id1) { - t.Fatalf("HasSymKey(id1) failed.") - } - if !w.HasSymKey(id2) { - t.Fatalf("HasSymKey(id2) failed.") - } - if !validateDataIntegrity(k2, aesKeyLength) { - t.Fatalf("key validation failed.") - } - if !bytes.Equal(k1, k2) { - t.Fatalf("k1 != k2.") - } -} - -func TestExpiry(t *testing.T) { - InitSingleTest() - - w := New(&DefaultConfig) - w.SetMinimumPowTest(0.0000001) - defer w.SetMinimumPowTest(DefaultMinimumPoW) - w.Start(nil) - defer w.Stop() - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - params.TTL = 1 - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - err = w.Send(env) - if err != nil { - t.Fatalf("failed to send envelope with seed %d: %s.", seed, err) - } - - // wait till received or timeout - var received, expired bool - for j := 0; j < 20; j++ { - time.Sleep(100 * time.Millisecond) - if len(w.Envelopes()) > 0 { - received = true - break - } - } - - if !received { - t.Fatalf("did not receive the sent envelope, seed: %d.", seed) - } - - // wait till expired or timeout - for j := 0; j < 20; j++ { - time.Sleep(100 * time.Millisecond) - if len(w.Envelopes()) == 0 { - expired = true - break - } - } - - if !expired { - t.Fatalf("expire failed, seed: %d.", seed) - } -} - -func TestCustomization(t *testing.T) { - InitSingleTest() - - w := New(&DefaultConfig) - defer w.SetMinimumPowTest(DefaultMinimumPoW) - defer w.SetMaxMessageSize(DefaultMaxMessageSize) - w.Start(nil) - defer w.Stop() - - const smallPoW = 0.00001 - - f, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - params.KeySym = f.KeySym - params.Topic = BytesToTopic(f.Topics[2]) - params.PoW = smallPoW - params.TTL = 3600 * 24 // one day - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - err = w.Send(env) - if err == nil { - t.Fatalf("successfully sent envelope with PoW %.06f, false positive (seed %d).", env.PoW(), seed) - } - - w.SetMinimumPowTest(smallPoW / 2) - err = w.Send(env) - if err != nil { - t.Fatalf("failed to send envelope with seed %d: %s.", seed, err) - } - - params.TTL++ - msg, err = NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err = msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - w.SetMaxMessageSize(uint32(env.size() - 1)) - err = w.Send(env) - if err == nil { - t.Fatalf("successfully sent oversized envelope (seed %d): false positive.", seed) - } - - w.SetMaxMessageSize(DefaultMaxMessageSize) - err = w.Send(env) - if err != nil { - t.Fatalf("failed to send second envelope with seed %d: %s.", seed, err) - } - - // wait till received or timeout - var received bool - for j := 0; j < 20; j++ { - time.Sleep(100 * time.Millisecond) - if len(w.Envelopes()) > 1 { - received = true - break - } - } - - if !received { - t.Fatalf("did not receive the sent envelope, seed: %d.", seed) - } - - // check w.messages() - _, err = w.Subscribe(f) - if err != nil { - t.Fatalf("failed subscribe with seed %d: %s.", seed, err) - } - time.Sleep(5 * time.Millisecond) - mail := f.Retrieve() - if len(mail) > 0 { - t.Fatalf("received premature mail") - } -} - -func TestSymmetricSendCycle(t *testing.T) { - InitSingleTest() - - w := New(&DefaultConfig) - defer w.SetMinimumPowTest(DefaultMinimumPoW) - defer w.SetMaxMessageSize(DefaultMaxMessageSize) - w.Start(nil) - defer w.Stop() - - filter1, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - filter1.PoW = DefaultMinimumPoW - - // Copy the first filter since some of its fields - // are randomly gnerated. - filter2 := &Filter{ - KeySym: filter1.KeySym, - Topics: filter1.Topics, - PoW: filter1.PoW, - AllowP2P: filter1.AllowP2P, - Messages: make(map[common.Hash]*ReceivedMessage), - } - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - filter1.Src = ¶ms.Src.PublicKey - filter2.Src = ¶ms.Src.PublicKey - - params.KeySym = filter1.KeySym - params.Topic = BytesToTopic(filter1.Topics[2]) - params.PoW = filter1.PoW - params.WorkTime = 10 - params.TTL = 50 - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - _, err = w.Subscribe(filter1) - if err != nil { - t.Fatalf("failed subscribe 1 with seed %d: %s.", seed, err) - } - - _, err = w.Subscribe(filter2) - if err != nil { - t.Fatalf("failed subscribe 2 with seed %d: %s.", seed, err) - } - - err = w.Send(env) - if err != nil { - t.Fatalf("Failed sending envelope with PoW %.06f (seed %d): %s", env.PoW(), seed, err) - } - - // wait till received or timeout - var received bool - for j := 0; j < 200; j++ { - time.Sleep(10 * time.Millisecond) - if len(w.Envelopes()) > 0 { - received = true - break - } - } - - if !received { - t.Fatalf("did not receive the sent envelope, seed: %d.", seed) - } - - // check w.messages() - time.Sleep(5 * time.Millisecond) - mail1 := filter1.Retrieve() - mail2 := filter2.Retrieve() - if len(mail2) == 0 { - t.Fatalf("did not receive any email for filter 2") - } - if len(mail1) == 0 { - t.Fatalf("did not receive any email for filter 1") - } - -} - -func TestSymmetricSendWithoutAKey(t *testing.T) { - InitSingleTest() - - w := New(&DefaultConfig) - defer w.SetMinimumPowTest(DefaultMinimumPoW) - defer w.SetMaxMessageSize(DefaultMaxMessageSize) - w.Start(nil) - defer w.Stop() - - filter, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - filter.PoW = DefaultMinimumPoW - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - filter.Src = nil - - params.KeySym = filter.KeySym - params.Topic = BytesToTopic(filter.Topics[2]) - params.PoW = filter.PoW - params.WorkTime = 10 - params.TTL = 50 - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - _, err = w.Subscribe(filter) - if err != nil { - t.Fatalf("failed subscribe 1 with seed %d: %s.", seed, err) - } - - err = w.Send(env) - if err != nil { - t.Fatalf("Failed sending envelope with PoW %.06f (seed %d): %s", env.PoW(), seed, err) - } - - // wait till received or timeout - var received bool - for j := 0; j < 200; j++ { - time.Sleep(10 * time.Millisecond) - if len(w.Envelopes()) > 0 { - received = true - break - } - } - - if !received { - t.Fatalf("did not receive the sent envelope, seed: %d.", seed) - } - - // check w.messages() - time.Sleep(5 * time.Millisecond) - mail := filter.Retrieve() - if len(mail) == 0 { - t.Fatalf("did not receive message in spite of not setting a public key") - } -} - -func TestSymmetricSendKeyMismatch(t *testing.T) { - InitSingleTest() - - w := New(&DefaultConfig) - defer w.SetMinimumPowTest(DefaultMinimumPoW) - defer w.SetMaxMessageSize(DefaultMaxMessageSize) - w.Start(nil) - defer w.Stop() - - filter, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - filter.PoW = DefaultMinimumPoW - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - params.KeySym = filter.KeySym - params.Topic = BytesToTopic(filter.Topics[2]) - params.PoW = filter.PoW - params.WorkTime = 10 - params.TTL = 50 - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - _, err = w.Subscribe(filter) - if err != nil { - t.Fatalf("failed subscribe 1 with seed %d: %s.", seed, err) - } - - err = w.Send(env) - if err != nil { - t.Fatalf("Failed sending envelope with PoW %.06f (seed %d): %s", env.PoW(), seed, err) - } - - // wait till received or timeout - var received bool - for j := 0; j < 200; j++ { - time.Sleep(10 * time.Millisecond) - if len(w.Envelopes()) > 0 { - received = true - break - } - } - - if !received { - t.Fatalf("did not receive the sent envelope, seed: %d.", seed) - } - - // check w.messages() - time.Sleep(5 * time.Millisecond) - mail := filter.Retrieve() - if len(mail) > 0 { - t.Fatalf("received a message when keys weren't matching") - } -} - -func TestBloom(t *testing.T) { - topic := TopicType{0, 0, 255, 6} - b := TopicToBloom(topic) - x := make([]byte, BloomFilterSize) - x[0] = byte(1) - x[32] = byte(1) - x[BloomFilterSize-1] = byte(128) - if !BloomFilterMatch(x, b) || !BloomFilterMatch(b, x) { - t.Fatalf("bloom filter does not match the mask") - } - - _, err := mrand.Read(b) - if err != nil { - t.Fatalf("math rand error") - } - _, err = mrand.Read(x) - if err != nil { - t.Fatalf("math rand error") - } - if !BloomFilterMatch(b, b) { - t.Fatalf("bloom filter does not match self") - } - x = addBloom(x, b) - if !BloomFilterMatch(x, b) { - t.Fatalf("bloom filter does not match combined bloom") - } - if !isFullNode(nil) { - t.Fatalf("isFullNode did not recognize nil as full node") - } - x[17] = 254 - if isFullNode(x) { - t.Fatalf("isFullNode false positive") - } - for i := 0; i < BloomFilterSize; i++ { - b[i] = byte(255) - } - if !isFullNode(b) { - t.Fatalf("isFullNode false negative") - } - if BloomFilterMatch(x, b) { - t.Fatalf("bloomFilterMatch false positive") - } - if !BloomFilterMatch(b, x) { - t.Fatalf("bloomFilterMatch false negative") - } - - w := New(&DefaultConfig) - f := w.BloomFilter() - if f != nil { - t.Fatalf("wrong bloom on creation") - } - err = w.SetBloomFilter(x) - if err != nil { - t.Fatalf("failed to set bloom filter: %s", err) - } - f = w.BloomFilter() - if !BloomFilterMatch(f, x) || !BloomFilterMatch(x, f) { - t.Fatalf("retireved wrong bloom filter") - } -} From 995db1892b6397f0f45a98b51fe59af2777866a3 Mon Sep 17 00:00:00 2001 From: JukLee0ira Date: Wed, 13 Nov 2024 11:32:34 +0800 Subject: [PATCH 195/479] .github: remove whisper from CODEOWNERS (#21527) --- .github/CODEOWNERS | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 79c7a5301453..a7b617655235 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -9,4 +9,3 @@ les/ @zsfelfoldi light/ @zsfelfoldi mobile/ @karalabe p2p/ @fjl @zsfelfoldi -whisper/ @gballet @gluk256 From cfb72501bf12ea766e25328727fc27ce084217f4 Mon Sep 17 00:00:00 2001 From: JukLee0ira Date: Wed, 13 Nov 2024 11:35:41 +0800 Subject: [PATCH 196/479] cmd/geth: print warning when whisper config is present in toml (#21544) --- cmd/XDC/config.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cmd/XDC/config.go b/cmd/XDC/config.go index 28d422d7122f..f2c0b525f6f4 100644 --- a/cmd/XDC/config.go +++ b/cmd/XDC/config.go @@ -88,8 +88,19 @@ type Bootnodes struct { Testnet []string } +// whisper has been deprecated, but clients out there might still have [Shh] +// in their config, which will crash. Cut them some slack by keeping the +// config, and displaying a message that those config switches are ineffectual. +// To be removed circa Q1 2021 -- @gballet. +type whisperDeprecatedConfig struct { + MaxMessageSize uint32 `toml:",omitempty"` + MinimumAcceptedPOW float64 `toml:",omitempty"` + RestrictConnectionBetweenLightClients bool `toml:",omitempty"` +} + type XDCConfig struct { Eth ethconfig.Config + Shh whisperDeprecatedConfig Node node.Config Ethstats ethstatsConfig XDCX XDCx.Config @@ -139,6 +150,10 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, XDCConfig) { if err := loadConfig(file, &cfg); err != nil { utils.Fatalf("%v", err) } + + if cfg.Shh != (whisperDeprecatedConfig{}) { + log.Warn("Deprecated whisper config detected. Whisper has been moved to github.com/ethereum/whisper") + } } if ctx.GlobalIsSet(utils.StakingEnabledFlag.Name) { cfg.StakeEnable = ctx.GlobalBool(utils.StakingEnabledFlag.Name) From 85ede14a92e49e3ab2d2360f4e8530d7d7d47c65 Mon Sep 17 00:00:00 2001 From: JukLee0ira Date: Wed, 13 Nov 2024 11:42:32 +0800 Subject: [PATCH 197/479] cmd: retire whisper flags (#22421) --- cmd/XDC/config.go | 28 +--------------------------- cmd/XDC/consolecmd.go | 2 +- cmd/XDC/main.go | 7 ------- cmd/XDC/usage.go | 4 ---- cmd/utils/flags.go | 23 ----------------------- 5 files changed, 2 insertions(+), 62 deletions(-) diff --git a/cmd/XDC/config.go b/cmd/XDC/config.go index f2c0b525f6f4..dacc11b497c7 100644 --- a/cmd/XDC/config.go +++ b/cmd/XDC/config.go @@ -46,7 +46,7 @@ var ( Name: "dumpconfig", Usage: "Show configuration values", ArgsUsage: "", - Flags: append(append(nodeFlags, rpcFlags...), whisperFlags...), + Flags: append(nodeFlags, rpcFlags...), Category: "MISCELLANEOUS COMMANDS", Description: `The dumpconfig command shows configuration values.`, } @@ -88,19 +88,8 @@ type Bootnodes struct { Testnet []string } -// whisper has been deprecated, but clients out there might still have [Shh] -// in their config, which will crash. Cut them some slack by keeping the -// config, and displaying a message that those config switches are ineffectual. -// To be removed circa Q1 2021 -- @gballet. -type whisperDeprecatedConfig struct { - MaxMessageSize uint32 `toml:",omitempty"` - MinimumAcceptedPOW float64 `toml:",omitempty"` - RestrictConnectionBetweenLightClients bool `toml:",omitempty"` -} - type XDCConfig struct { Eth ethconfig.Config - Shh whisperDeprecatedConfig Node node.Config Ethstats ethstatsConfig XDCX XDCx.Config @@ -150,10 +139,6 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, XDCConfig) { if err := loadConfig(file, &cfg); err != nil { utils.Fatalf("%v", err) } - - if cfg.Shh != (whisperDeprecatedConfig{}) { - log.Warn("Deprecated whisper config detected. Whisper has been moved to github.com/ethereum/whisper") - } } if ctx.GlobalIsSet(utils.StakingEnabledFlag.Name) { cfg.StakeEnable = ctx.GlobalBool(utils.StakingEnabledFlag.Name) @@ -224,7 +209,6 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, XDCConfig) { cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name) } - utils.SetShhConfig(ctx, stack) utils.SetXDCXConfig(ctx, &cfg.XDCX, cfg.Node.DataDir) return stack, cfg } @@ -242,15 +226,6 @@ func applyValues(values []string, params *[]string) { } -// checkWhisper returns true in case one of the whisper flags is set. -func checkWhisper(ctx *cli.Context) { - for _, flag := range whisperFlags { - if ctx.GlobalIsSet(flag.GetName()) { - log.Warn("deprecated whisper flag detected. Whisper has been moved to github.com/ethereum/whisper") - } - } -} - func makeFullNode(ctx *cli.Context) (*node.Node, XDCConfig) { stack, cfg := makeConfigNode(ctx) @@ -259,7 +234,6 @@ func makeFullNode(ctx *cli.Context) (*node.Node, XDCConfig) { utils.RegisterXDCXService(stack, &cfg.XDCX) utils.RegisterEthService(stack, &cfg.Eth) - checkWhisper(ctx) // Add the Ethereum Stats daemon if requested. if cfg.Ethstats.URL != "" { utils.RegisterEthStatsService(stack, cfg.Ethstats.URL) diff --git a/cmd/XDC/consolecmd.go b/cmd/XDC/consolecmd.go index 6eb2b52fc9e8..e36dda489cc9 100644 --- a/cmd/XDC/consolecmd.go +++ b/cmd/XDC/consolecmd.go @@ -38,7 +38,7 @@ var ( Action: utils.MigrateFlags(localConsole), Name: "console", Usage: "Start an interactive JavaScript environment", - Flags: append(append(append(nodeFlags, rpcFlags...), consoleFlags...), whisperFlags...), + Flags: append(append(nodeFlags, rpcFlags...), consoleFlags...), Category: "CONSOLE COMMANDS", Description: ` The XDC console is an interactive shell for the JavaScript runtime environment diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go index dcaad38cd9c2..88d5a4bd7832 100644 --- a/cmd/XDC/main.go +++ b/cmd/XDC/main.go @@ -160,12 +160,6 @@ var ( utils.IPCPathFlag, utils.RPCGlobalTxFeeCap, } - - whisperFlags = []cli.Flag{ - utils.WhisperEnabledFlag, - utils.WhisperMaxMessageSizeFlag, - utils.WhisperMinPOWFlag, - } ) func init() { @@ -198,7 +192,6 @@ func init() { app.Flags = append(app.Flags, rpcFlags...) app.Flags = append(app.Flags, consoleFlags...) app.Flags = append(app.Flags, debug.Flags...) - app.Flags = append(app.Flags, whisperFlags...) app.Before = func(ctx *cli.Context) error { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/cmd/XDC/usage.go b/cmd/XDC/usage.go index 9c1fb8193e05..2fdfdd9576c2 100644 --- a/cmd/XDC/usage.go +++ b/cmd/XDC/usage.go @@ -217,10 +217,6 @@ var AppHelpFlagGroups = []flagGroup{ //utils.NoCompactionFlag, }, debug.Flags...), }, - //{ - // Name: "WHISPER (deprecated)", - // Flags: whisperFlags, - //}, { Name: "DEPRECATED", Flags: []cli.Flag{ diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 25a9d7275851..ef937b9d727f 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -590,20 +590,6 @@ var ( Usage: "Gas price below which gpo will ignore transactions", Value: ethconfig.Defaults.GPO.IgnorePrice.Int64(), } - WhisperEnabledFlag = cli.BoolFlag{ - Name: "shh", - Usage: "Enable Whisper", - } - WhisperMaxMessageSizeFlag = cli.IntFlag{ - Name: "shh.maxmessagesize", - Usage: "Max message size accepted", - Value: 1024 * 1024, - } - WhisperMinPOWFlag = cli.Float64Flag{ - Name: "shh.pow", - Usage: "Minimum POW accepted", - Value: 0.2, - } XDCXDataDirFlag = DirectoryFlag{ Name: "XDCx.datadir", Usage: "Data directory for the XDCX databases", @@ -1143,15 +1129,6 @@ func checkExclusive(ctx *cli.Context, args ...interface{}) { } } -// SetShhConfig applies shh-related command line flags to the config. -func SetShhConfig(ctx *cli.Context, stack *node.Node) { - if ctx.GlobalIsSet(WhisperEnabledFlag.Name) || - ctx.GlobalIsSet(WhisperMaxMessageSizeFlag.Name) || - ctx.GlobalIsSet(WhisperMinPOWFlag.Name) { - log.Warn("Whisper support has been deprecated and the code has been moved to github.com/ethereum/whisper") - } -} - func SetXDCXConfig(ctx *cli.Context, cfg *XDCx.Config, XDCDataDir string) { if ctx.GlobalIsSet(XDCXDataDirFlag.Name) { cfg.DataDir = ctx.GlobalString(XDCXDataDirFlag.Name) From 643f02356d0cb54e9623702699424894e7253aa9 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:41 +0800 Subject: [PATCH 198/479] log: changed if-else blocks to conform with golint (#16661) --- log/handler.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/log/handler.go b/log/handler.go index d5594b853de8..41d5718ddb24 100644 --- a/log/handler.go +++ b/log/handler.go @@ -234,9 +234,8 @@ func FailoverHandler(hs ...Handler) Handler { err = h.Log(r) if err == nil { return nil - } else { - r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err) } + r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err) } return err @@ -320,13 +319,12 @@ func evaluateLazy(lz Lazy) (interface{}, error) { results := value.Call([]reflect.Value{}) if len(results) == 1 { return results[0].Interface(), nil - } else { - values := make([]interface{}, len(results)) - for i, v := range results { - values[i] = v.Interface() - } - return values, nil } + values := make([]interface{}, len(results)) + for i, v := range results { + values[i] = v.Interface() + } + return values, nil } // DiscardHandler reports success for all writes but does nothing. From f391463457ccb10b154288d92ecbd997e8da9282 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:41 +0800 Subject: [PATCH 199/479] log: fixes for golint warnings (#16775) --- log/README.md | 2 +- log/doc.go | 6 +++--- log/format.go | 16 ++++++++-------- log/handler.go | 10 +++++----- log/logger.go | 7 ++++--- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/log/README.md b/log/README.md index 0951b21cb53f..b4476577b63b 100644 --- a/log/README.md +++ b/log/README.md @@ -45,7 +45,7 @@ srvlog.SetHandler(log.MultiHandler( log.StreamHandler(os.Stderr, log.LogfmtFormat()), log.LvlFilterHandler( log.LvlError, - log.Must.FileHandler("errors.json", log.JsonFormat())))) + log.Must.FileHandler("errors.json", log.JSONFormat())))) ``` Will result in output that looks like this: diff --git a/log/doc.go b/log/doc.go index 83ad8c54f64c..bff2f4966a4b 100644 --- a/log/doc.go +++ b/log/doc.go @@ -86,7 +86,7 @@ from the rpc package in logfmt to standard out. The other prints records at Erro or above in JSON formatted output to the file /var/log/service.json handler := log.MultiHandler( - log.LvlFilterHandler(log.LvlError, log.Must.FileHandler("/var/log/service.json", log.JsonFormat())), + log.LvlFilterHandler(log.LvlError, log.Must.FileHandler("/var/log/service.json", log.JSONFormat())), log.MatchFilterHandler("pkg", "app/rpc" log.StdoutHandler()) ) @@ -304,8 +304,8 @@ For all Handler functions which can return an error, there is a version of that function which will return no error but panics on failure. They are all available on the Must object. For example: - log.Must.FileHandler("/path", log.JsonFormat) - log.Must.NetHandler("tcp", ":1234", log.JsonFormat) + log.Must.FileHandler("/path", log.JSONFormat) + log.Must.NetHandler("tcp", ":1234", log.JSONFormat) Inspiration and Credit diff --git a/log/format.go b/log/format.go index 67859351178b..bd8272e03ef8 100644 --- a/log/format.go +++ b/log/format.go @@ -196,16 +196,16 @@ func logfmt(buf *bytes.Buffer, ctx []interface{}, color int, term bool) { buf.WriteByte('\n') } -// JsonFormat formats log records as JSON objects separated by newlines. -// It is the equivalent of JsonFormatEx(false, true). -func JsonFormat() Format { - return JsonFormatEx(false, true) +// JSONFormat formats log records as JSON objects separated by newlines. +// It is the equivalent of JSONFormatEx(false, true). +func JSONFormat() Format { + return JSONFormatEx(false, true) } -// JsonFormatEx formats log records as JSON objects. If pretty is true, +// JSONFormatEx formats log records as JSON objects. If pretty is true, // records will be pretty-printed. If lineSeparated is true, records // will be logged with a new line between each record. -func JsonFormatEx(pretty, lineSeparated bool) Format { +func JSONFormatEx(pretty, lineSeparated bool) Format { jsonMarshal := json.Marshal if pretty { jsonMarshal = func(v interface{}) ([]byte, error) { @@ -225,7 +225,7 @@ func JsonFormatEx(pretty, lineSeparated bool) Format { if !ok { props[errorKey] = fmt.Sprintf("%+v is not a string key", r.Ctx[i]) } - props[k] = formatJsonValue(r.Ctx[i+1]) + props[k] = formatJSONValue(r.Ctx[i+1]) } b, err := jsonMarshal(props) @@ -270,7 +270,7 @@ func formatShared(value interface{}) (result interface{}) { } } -func formatJsonValue(value interface{}) interface{} { +func formatJSONValue(value interface{}) interface{} { value = formatShared(value) switch value.(type) { case int, int8, int16, int32, int64, float32, float64, uint, uint8, uint16, uint32, uint64, string: diff --git a/log/handler.go b/log/handler.go index 41d5718ddb24..3c99114dcb2f 100644 --- a/log/handler.go +++ b/log/handler.go @@ -11,8 +11,8 @@ import ( "github.com/go-stack/stack" ) +// Handler defines where and how log records are written. // A Logger prints its log records by writing to a Handler. -// The Handler interface defines where and how log records are written. // Handlers are composable, providing you great flexibility in combining // them to achieve the logging structure that suits your applications. type Handler interface { @@ -193,7 +193,7 @@ func LvlFilterHandler(maxLvl Lvl, h Handler) Handler { }, h) } -// A MultiHandler dispatches any write to each of its handlers. +// MultiHandler dispatches any write to each of its handlers. // This is useful for writing different types of log information // to different locations. For example, to log to a file and // standard error: @@ -212,7 +212,7 @@ func MultiHandler(hs ...Handler) Handler { }) } -// A FailoverHandler writes all log records to the first handler +// FailoverHandler writes all log records to the first handler // specified, but will failover and write to the second handler if // the first handler has failed, and so on for all handlers specified. // For example you might want to log to a network socket, but failover @@ -220,7 +220,7 @@ func MultiHandler(hs ...Handler) Handler { // standard out if the file write fails: // // log.FailoverHandler( -// log.Must.NetHandler("tcp", ":9090", log.JsonFormat()), +// log.Must.NetHandler("tcp", ":9090", log.JSONFormat()), // log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), // log.StdoutHandler) // @@ -336,7 +336,7 @@ func DiscardHandler() Handler { }) } -// The Must object provides the following Handler creation functions +// Must provides the following Handler creation functions // which instead of returning an error parameter only return a Handler // and panic on failure: FileHandler, NetHandler, SyslogHandler, SyslogNetHandler var Must muster diff --git a/log/logger.go b/log/logger.go index 1a04e4ee9bde..f678a13dcf1d 100644 --- a/log/logger.go +++ b/log/logger.go @@ -24,7 +24,7 @@ const ( LvlTrace ) -// Aligned returns a 5-character string containing the name of a Lvl. +// AlignedString returns a 5-character string containing the name of a Lvl. func (l Lvl) AlignedString() string { switch l { case LvlTrace: @@ -44,7 +44,7 @@ func (l Lvl) AlignedString() string { } } -// Strings returns the name of a Lvl. +// String returns the name of a Lvl. func (l Lvl) String() string { switch l { case LvlTrace: @@ -64,7 +64,7 @@ func (l Lvl) String() string { } } -// Returns the appropriate Lvl from a string name. +// LvlFromString returns the appropriate Lvl from a string name. // Useful for parsing command line args and configuration files. func LvlFromString(lvlString string) (Lvl, error) { switch lvlString { @@ -95,6 +95,7 @@ type Record struct { KeyNames RecordKeyNames } +// RecordKeyNames gets stored in a Record when the write function is executed. type RecordKeyNames struct { Time string Msg string From 38e5efbfe4dd0d5c53aa26a2a10c205fdfb9787e Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:41 +0800 Subject: [PATCH 200/479] log: Change time format (#17054) - Keep the tailing zeros. - Limit precision to milliseconds. --- log/format.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log/format.go b/log/format.go index bd8272e03ef8..be350c5a6d36 100644 --- a/log/format.go +++ b/log/format.go @@ -15,7 +15,7 @@ import ( const ( timeFormat = "2006-01-02T15:04:05-0700" - termTimeFormat = "01-02|15:04:05" + termTimeFormat = "01-02|15:04:05.000" floatFormat = 'f' termMsgJust = 40 ) From 49e889eb7c5854242e4298a5aef3182601c4b2fa Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:41 +0800 Subject: [PATCH 201/479] log: logging feature (#17097) --- log/format.go | 46 +++++++++++++- log/handler.go | 147 +++++++++++++++++++++++++++++++++++++------- log/handler_glog.go | 17 +++-- log/logger.go | 3 + 4 files changed, 184 insertions(+), 29 deletions(-) diff --git a/log/format.go b/log/format.go index be350c5a6d36..0a633e5cc73f 100644 --- a/log/format.go +++ b/log/format.go @@ -77,11 +77,11 @@ type TerminalStringer interface { // a terminal with color-coded level output and terser human friendly timestamp. // This format should only be used for interactive programs or while developing. // -// [TIME] [LEVEL] MESAGE key=value key=value ... +// [LEVEL] [TIME] MESAGE key=value key=value ... // // Example: // -// [May 16 20:58:45] [DBUG] remove route ns=haproxy addr=127.0.0.1:50002 +// [DBUG] [May 16 20:58:45] remove route ns=haproxy addr=127.0.0.1:50002 // func TerminalFormat(usecolor bool) Format { return FormatFunc(func(r *Record) []byte { @@ -202,6 +202,48 @@ func JSONFormat() Format { return JSONFormatEx(false, true) } +// JSONFormatOrderedEx formats log records as JSON arrays. If pretty is true, +// records will be pretty-printed. If lineSeparated is true, records +// will be logged with a new line between each record. +func JSONFormatOrderedEx(pretty, lineSeparated bool) Format { + jsonMarshal := json.Marshal + if pretty { + jsonMarshal = func(v interface{}) ([]byte, error) { + return json.MarshalIndent(v, "", " ") + } + } + return FormatFunc(func(r *Record) []byte { + props := make(map[string]interface{}) + + props[r.KeyNames.Time] = r.Time + props[r.KeyNames.Lvl] = r.Lvl.String() + props[r.KeyNames.Msg] = r.Msg + + ctx := make([]string, len(r.Ctx)) + for i := 0; i < len(r.Ctx); i += 2 { + k, ok := r.Ctx[i].(string) + if !ok { + props[errorKey] = fmt.Sprintf("%+v is not a string key,", r.Ctx[i]) + } + ctx[i] = k + ctx[i+1] = formatLogfmtValue(r.Ctx[i+1], true) + } + props[r.KeyNames.Ctx] = ctx + + b, err := jsonMarshal(props) + if err != nil { + b, _ = jsonMarshal(map[string]string{ + errorKey: err.Error(), + }) + return b + } + if lineSeparated { + b = append(b, '\n') + } + return b + }) +} + // JSONFormatEx formats log records as JSON objects. If pretty is true, // records will be pretty-printed. If lineSeparated is true, records // will be logged with a new line between each record. diff --git a/log/handler.go b/log/handler.go index 3c99114dcb2f..6a0a25c15e0d 100644 --- a/log/handler.go +++ b/log/handler.go @@ -3,9 +3,13 @@ package log import ( "fmt" "io" + "io/ioutil" "net" "os" + "path/filepath" "reflect" + "regexp" + "strings" "sync" "github.com/go-stack/stack" @@ -70,6 +74,111 @@ func FileHandler(path string, fmtr Format) (Handler, error) { return closingHandler{f, StreamHandler(f, fmtr)}, nil } +// countingWriter wraps a WriteCloser object in order to count the written bytes. +type countingWriter struct { + w io.WriteCloser // the wrapped object + count uint // number of bytes written +} + +// Write increments the byte counter by the number of bytes written. +// Implements the WriteCloser interface. +func (w *countingWriter) Write(p []byte) (n int, err error) { + n, err = w.w.Write(p) + w.count += uint(n) + return n, err +} + +// Close implements the WriteCloser interface. +func (w *countingWriter) Close() error { + return w.w.Close() +} + +// prepFile opens the log file at the given path, and cuts off the invalid part +// from the end, because the previous execution could have been finished by interruption. +// Assumes that every line ended by '\n' contains a valid log record. +func prepFile(path string) (*countingWriter, error) { + f, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND, 0600) + if err != nil { + return nil, err + } + _, err = f.Seek(-1, io.SeekEnd) + if err != nil { + return nil, err + } + buf := make([]byte, 1) + var cut int64 + for { + if _, err := f.Read(buf); err != nil { + return nil, err + } + if buf[0] == '\n' { + break + } + if _, err = f.Seek(-2, io.SeekCurrent); err != nil { + return nil, err + } + cut++ + } + fi, err := f.Stat() + if err != nil { + return nil, err + } + ns := fi.Size() - cut + if err = f.Truncate(ns); err != nil { + return nil, err + } + return &countingWriter{w: f, count: uint(ns)}, nil +} + +// RotatingFileHandler returns a handler which writes log records to file chunks +// at the given path. When a file's size reaches the limit, the handler creates +// a new file named after the timestamp of the first log record it will contain. +func RotatingFileHandler(path string, limit uint, formatter Format) (Handler, error) { + if err := os.MkdirAll(path, 0700); err != nil { + return nil, err + } + files, err := ioutil.ReadDir(path) + if err != nil { + return nil, err + } + re := regexp.MustCompile(`\.log$`) + last := len(files) - 1 + for last >= 0 && (!files[last].Mode().IsRegular() || !re.MatchString(files[last].Name())) { + last-- + } + var counter *countingWriter + if last >= 0 && files[last].Size() < int64(limit) { + // Open the last file, and continue to write into it until it's size reaches the limit. + if counter, err = prepFile(filepath.Join(path, files[last].Name())); err != nil { + return nil, err + } + } + if counter == nil { + counter = new(countingWriter) + } + h := StreamHandler(counter, formatter) + + return FuncHandler(func(r *Record) error { + if counter.count > limit { + counter.Close() + counter.w = nil + } + if counter.w == nil { + f, err := os.OpenFile( + filepath.Join(path, fmt.Sprintf("%s.log", strings.Replace(r.Time.Format("060102150405.00"), ".", "", 1))), + os.O_CREATE|os.O_APPEND|os.O_WRONLY, + 0600, + ) + if err != nil { + return err + } + counter.w = f + counter.count = 0 + } + return h.Log(r) + }), nil +} + // NetHandler opens a socket to the given address and writes records // over the connection. func NetHandler(network, addr string, fmtr Format) (Handler, error) { @@ -135,15 +244,14 @@ func CallerStackHandler(format string, h Handler) Handler { // wrapped Handler if the given function evaluates true. For example, // to only log records where the 'err' key is not nil: // -// logger.SetHandler(FilterHandler(func(r *Record) bool { -// for i := 0; i < len(r.Ctx); i += 2 { -// if r.Ctx[i] == "err" { -// return r.Ctx[i+1] != nil -// } -// } -// return false -// }, h)) -// +// logger.SetHandler(FilterHandler(func(r *Record) bool { +// for i := 0; i < len(r.Ctx); i += 2 { +// if r.Ctx[i] == "err" { +// return r.Ctx[i+1] != nil +// } +// } +// return false +// }, h)) func FilterHandler(fn func(r *Record) bool, h Handler) Handler { return FuncHandler(func(r *Record) error { if fn(r) { @@ -158,8 +266,7 @@ func FilterHandler(fn func(r *Record) bool, h Handler) Handler { // context matches the value. For example, to only log records // from your ui package: // -// log.MatchFilterHandler("pkg", "app/ui", log.StdoutHandler) -// +// log.MatchFilterHandler("pkg", "app/ui", log.StdoutHandler) func MatchFilterHandler(key string, value interface{}, h Handler) Handler { return FilterHandler(func(r *Record) (pass bool) { switch key { @@ -185,8 +292,7 @@ func MatchFilterHandler(key string, value interface{}, h Handler) Handler { // level to the wrapped Handler. For example, to only // log Error/Crit records: // -// log.LvlFilterHandler(log.LvlError, log.StdoutHandler) -// +// log.LvlFilterHandler(log.LvlError, log.StdoutHandler) func LvlFilterHandler(maxLvl Lvl, h Handler) Handler { return FilterHandler(func(r *Record) (pass bool) { return r.Lvl <= maxLvl @@ -198,10 +304,9 @@ func LvlFilterHandler(maxLvl Lvl, h Handler) Handler { // to different locations. For example, to log to a file and // standard error: // -// log.MultiHandler( -// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), -// log.StderrHandler) -// +// log.MultiHandler( +// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), +// log.StderrHandler) func MultiHandler(hs ...Handler) Handler { return FuncHandler(func(r *Record) error { for _, h := range hs { @@ -219,10 +324,10 @@ func MultiHandler(hs ...Handler) Handler { // to writing to a file if the network fails, and then to // standard out if the file write fails: // -// log.FailoverHandler( -// log.Must.NetHandler("tcp", ":9090", log.JSONFormat()), -// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), -// log.StdoutHandler) +// log.FailoverHandler( +// log.Must.NetHandler("tcp", ":9090", log.JSONFormat()), +// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), +// log.StdoutHandler) // // All writes that do not go to the first handler will add context with keys of // the form "failover_err_{idx}" which explain the error encountered while diff --git a/log/handler_glog.go b/log/handler_glog.go index f8b932fd1b64..b372323082ae 100644 --- a/log/handler_glog.go +++ b/log/handler_glog.go @@ -57,6 +57,11 @@ func NewGlogHandler(h Handler) *GlogHandler { } } +// SetHandler updates the handler to write records to the specified sub-handler. +func (h *GlogHandler) SetHandler(nh Handler) { + h.origin = nh +} + // pattern contains a filter for the Vmodule option, holding a verbosity level // and a file pattern to match. type pattern struct { @@ -77,14 +82,14 @@ func (h *GlogHandler) Verbosity(level Lvl) { // // For instance: // -// pattern="gopher.go=3" -// sets the V level to 3 in all Go files named "gopher.go" +// pattern="gopher.go=3" +// sets the V level to 3 in all Go files named "gopher.go" // -// pattern="foo=3" -// sets V to 3 in all files of any packages whose import path ends in "foo" +// pattern="foo=3" +// sets V to 3 in all files of any packages whose import path ends in "foo" // -// pattern="foo/*=3" -// sets V to 3 in all files of any packages whose import path contains "foo" +// pattern="foo/*=3" +// sets V to 3 in all files of any packages whose import path contains "foo" func (h *GlogHandler) Vmodule(ruleset string) error { var filter []pattern for _, rule := range strings.Split(ruleset, ",") { diff --git a/log/logger.go b/log/logger.go index f678a13dcf1d..1d5e845db444 100644 --- a/log/logger.go +++ b/log/logger.go @@ -11,6 +11,7 @@ import ( const timeKey = "t" const lvlKey = "lvl" const msgKey = "msg" +const ctxKey = "ctx" const errorKey = "LOG15_ERROR" type Lvl int @@ -100,6 +101,7 @@ type RecordKeyNames struct { Time string Msg string Lvl string + Ctx string } // A Logger writes key/value pairs to a Handler @@ -138,6 +140,7 @@ func (l *logger) write(msg string, lvl Lvl, ctx []interface{}) { Time: timeKey, Msg: msgKey, Lvl: lvlKey, + Ctx: ctxKey, }, }) } From 7865057840848d5bb2d02a8ccccff2121860e6f1 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:41 +0800 Subject: [PATCH 202/479] internal/debug: support color terminal for cygwin/msys2 (#17740) --- internal/debug/flags.go | 4 ++-- log/term/LICENSE | 21 --------------------- log/term/terminal_appengine.go | 13 ------------- log/term/terminal_darwin.go | 13 ------------- log/term/terminal_freebsd.go | 18 ------------------ log/term/terminal_linux.go | 14 -------------- log/term/terminal_netbsd.go | 7 ------- log/term/terminal_notwindows.go | 20 -------------------- log/term/terminal_openbsd.go | 7 ------- log/term/terminal_solaris.go | 9 --------- log/term/terminal_windows.go | 26 -------------------------- 11 files changed, 2 insertions(+), 150 deletions(-) delete mode 100644 log/term/LICENSE delete mode 100644 log/term/terminal_appengine.go delete mode 100644 log/term/terminal_darwin.go delete mode 100644 log/term/terminal_freebsd.go delete mode 100644 log/term/terminal_linux.go delete mode 100644 log/term/terminal_netbsd.go delete mode 100644 log/term/terminal_notwindows.go delete mode 100644 log/term/terminal_openbsd.go delete mode 100644 log/term/terminal_solaris.go delete mode 100644 log/term/terminal_windows.go diff --git a/internal/debug/flags.go b/internal/debug/flags.go index 16a87c9810c1..57738d60e208 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -25,10 +25,10 @@ import ( "runtime" "github.com/XinFinOrg/XDPoSChain/log" - "github.com/XinFinOrg/XDPoSChain/log/term" "github.com/XinFinOrg/XDPoSChain/metrics" "github.com/XinFinOrg/XDPoSChain/metrics/exp" colorable "github.com/mattn/go-colorable" + "github.com/mattn/go-isatty" "gopkg.in/urfave/cli.v1" ) @@ -113,7 +113,7 @@ var Flags = []cli.Flag{ var Glogger *log.GlogHandler func init() { - usecolor := term.IsTty(os.Stderr.Fd()) && os.Getenv("TERM") != "dumb" + usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" output := io.Writer(os.Stderr) if usecolor { output = colorable.NewColorableStderr() diff --git a/log/term/LICENSE b/log/term/LICENSE deleted file mode 100644 index f090cb42f370..000000000000 --- a/log/term/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Simon Eskildsen - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/log/term/terminal_appengine.go b/log/term/terminal_appengine.go deleted file mode 100644 index c1b5d2a3b1ad..000000000000 --- a/log/term/terminal_appengine.go +++ /dev/null @@ -1,13 +0,0 @@ -// Based on ssh/terminal: -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build appengine - -package term - -// IsTty always returns false on AppEngine. -func IsTty(fd uintptr) bool { - return false -} diff --git a/log/term/terminal_darwin.go b/log/term/terminal_darwin.go deleted file mode 100644 index d8f351b1b1aa..000000000000 --- a/log/term/terminal_darwin.go +++ /dev/null @@ -1,13 +0,0 @@ -// Based on ssh/terminal: -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// +build !appengine - -package term - -import "syscall" - -const ioctlReadTermios = syscall.TIOCGETA - -type Termios syscall.Termios diff --git a/log/term/terminal_freebsd.go b/log/term/terminal_freebsd.go deleted file mode 100644 index cfaceab337a2..000000000000 --- a/log/term/terminal_freebsd.go +++ /dev/null @@ -1,18 +0,0 @@ -package term - -import ( - "syscall" -) - -const ioctlReadTermios = syscall.TIOCGETA - -// Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin. -type Termios struct { - Iflag uint32 - Oflag uint32 - Cflag uint32 - Lflag uint32 - Cc [20]uint8 - Ispeed uint32 - Ospeed uint32 -} diff --git a/log/term/terminal_linux.go b/log/term/terminal_linux.go deleted file mode 100644 index 5290468d698a..000000000000 --- a/log/term/terminal_linux.go +++ /dev/null @@ -1,14 +0,0 @@ -// Based on ssh/terminal: -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !appengine - -package term - -import "syscall" - -const ioctlReadTermios = syscall.TCGETS - -type Termios syscall.Termios diff --git a/log/term/terminal_netbsd.go b/log/term/terminal_netbsd.go deleted file mode 100644 index f9bb9e1c23b5..000000000000 --- a/log/term/terminal_netbsd.go +++ /dev/null @@ -1,7 +0,0 @@ -package term - -import "syscall" - -const ioctlReadTermios = syscall.TIOCGETA - -type Termios syscall.Termios diff --git a/log/term/terminal_notwindows.go b/log/term/terminal_notwindows.go deleted file mode 100644 index c9af534f62f2..000000000000 --- a/log/term/terminal_notwindows.go +++ /dev/null @@ -1,20 +0,0 @@ -// Based on ssh/terminal: -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build linux,!appengine darwin freebsd openbsd netbsd - -package term - -import ( - "syscall" - "unsafe" -) - -// IsTty returns true if the given file descriptor is a terminal. -func IsTty(fd uintptr) bool { - var termios Termios - _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) - return err == 0 -} diff --git a/log/term/terminal_openbsd.go b/log/term/terminal_openbsd.go deleted file mode 100644 index f9bb9e1c23b5..000000000000 --- a/log/term/terminal_openbsd.go +++ /dev/null @@ -1,7 +0,0 @@ -package term - -import "syscall" - -const ioctlReadTermios = syscall.TIOCGETA - -type Termios syscall.Termios diff --git a/log/term/terminal_solaris.go b/log/term/terminal_solaris.go deleted file mode 100644 index 033c163246ee..000000000000 --- a/log/term/terminal_solaris.go +++ /dev/null @@ -1,9 +0,0 @@ -package term - -import "golang.org/x/sys/unix" - -// IsTty returns true if the given file descriptor is a terminal. -func IsTty(fd uintptr) bool { - _, err := unix.IoctlGetTermios(int(fd), unix.TCGETA) - return err == nil -} diff --git a/log/term/terminal_windows.go b/log/term/terminal_windows.go deleted file mode 100644 index df3c30c15892..000000000000 --- a/log/term/terminal_windows.go +++ /dev/null @@ -1,26 +0,0 @@ -// Based on ssh/terminal: -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build windows - -package term - -import ( - "syscall" - "unsafe" -) - -var kernel32 = syscall.NewLazyDLL("kernel32.dll") - -var ( - procGetConsoleMode = kernel32.NewProc("GetConsoleMode") -) - -// IsTty returns true if the given file descriptor is a terminal. -func IsTty(fd uintptr) bool { - var st uint32 - r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) - return r != 0 && e == 0 -} From 81f59cb0ee38c2470a1de368151329e6f48ebc8e Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:41 +0800 Subject: [PATCH 203/479] log: do not pad values longer than 40 characters (#19592) * log: Do not pad too long values * log: gofmt --- log/format.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/log/format.go b/log/format.go index 0a633e5cc73f..866c6ebe97d0 100644 --- a/log/format.go +++ b/log/format.go @@ -14,10 +14,11 @@ import ( ) const ( - timeFormat = "2006-01-02T15:04:05-0700" - termTimeFormat = "01-02|15:04:05.000" - floatFormat = 'f' - termMsgJust = 40 + timeFormat = "2006-01-02T15:04:05-0700" + termTimeFormat = "01-02|15:04:05.000" + floatFormat = 'f' + termMsgJust = 40 + termCtxMaxPadding = 40 ) // locationTrims are trimmed for display to avoid unwieldy log lines. @@ -175,7 +176,7 @@ func logfmt(buf *bytes.Buffer, ctx []interface{}, color int, term bool) { fieldPaddingLock.RUnlock() length := utf8.RuneCountInString(v) - if padding < length { + if padding < length && length <= termCtxMaxPadding { padding = length fieldPaddingLock.Lock() @@ -189,7 +190,7 @@ func logfmt(buf *bytes.Buffer, ctx []interface{}, color int, term bool) { buf.WriteByte('=') } buf.WriteString(v) - if i < len(ctx)-2 { + if i < len(ctx)-2 && padding > length { buf.Write(bytes.Repeat([]byte{' '}, padding-length)) } } From 6873ca0687b52694fdde743be9400b6283735cc3 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:41 +0800 Subject: [PATCH 204/479] log: change http:// to https:// on links in README.md (#20178) docs: change http to https on links in log/README.md --- log/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/log/README.md b/log/README.md index b4476577b63b..47426806dd95 100644 --- a/log/README.md +++ b/log/README.md @@ -1,8 +1,8 @@ -![obligatory xkcd](http://imgs.xkcd.com/comics/standards.png) +![obligatory xkcd](https://imgs.xkcd.com/comics/standards.png) # log15 [![godoc reference](https://godoc.org/github.com/inconshreveable/log15?status.png)](https://godoc.org/github.com/inconshreveable/log15) [![Build Status](https://travis-ci.org/inconshreveable/log15.svg?branch=master)](https://travis-ci.org/inconshreveable/log15) -Package log15 provides an opinionated, simple toolkit for best-practice logging in Go (golang) that is both human and machine readable. It is modeled after the Go standard library's [`io`](http://golang.org/pkg/io/) and [`net/http`](http://golang.org/pkg/net/http/) packages and is an alternative to the standard library's [`log`](http://golang.org/pkg/log/) package. +Package log15 provides an opinionated, simple toolkit for best-practice logging in Go (golang) that is both human and machine readable. It is modeled after the Go standard library's [`io`](https://golang.org/pkg/io/) and [`net/http`](https://golang.org/pkg/net/http/) packages and is an alternative to the standard library's [`log`](https://golang.org/pkg/log/) package. ## Features - A simple, easy-to-understand API From 4d9936574b916a019f45e5a296878e02dcb22383 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:41 +0800 Subject: [PATCH 205/479] log: fix staticcheck warnings (#20388) --- log/handler_glog.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/log/handler_glog.go b/log/handler_glog.go index b372323082ae..b5186d4b27ec 100644 --- a/log/handler_glog.go +++ b/log/handler_glog.go @@ -207,7 +207,7 @@ func (h *GlogHandler) Log(r *Record) error { } // Check callsite cache for previously calculated log levels h.lock.RLock() - lvl, ok := h.siteCache[r.Call.PC()] + lvl, ok := h.siteCache[r.Call.Frame().PC] h.lock.RUnlock() // If we didn't cache the callsite yet, calculate it @@ -215,13 +215,13 @@ func (h *GlogHandler) Log(r *Record) error { h.lock.Lock() for _, rule := range h.patterns { if rule.pattern.MatchString(fmt.Sprintf("%+s", r.Call)) { - h.siteCache[r.Call.PC()], lvl, ok = rule.level, rule.level, true + h.siteCache[r.Call.Frame().PC], lvl, ok = rule.level, rule.level, true break } } // If no rule matched, remember to drop log the next time if !ok { - h.siteCache[r.Call.PC()] = 0 + h.siteCache[r.Call.Frame().PC] = 0 } h.lock.Unlock() } From 25a7e096ff60763ffbd63a36d74f840173d3c636 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:41 +0800 Subject: [PATCH 206/479] log: delete RotatingFileHandler (#20586) --- log/handler.go | 109 ------------------------------------------------- 1 file changed, 109 deletions(-) diff --git a/log/handler.go b/log/handler.go index 6a0a25c15e0d..13328ad268cd 100644 --- a/log/handler.go +++ b/log/handler.go @@ -3,13 +3,9 @@ package log import ( "fmt" "io" - "io/ioutil" "net" "os" - "path/filepath" "reflect" - "regexp" - "strings" "sync" "github.com/go-stack/stack" @@ -74,111 +70,6 @@ func FileHandler(path string, fmtr Format) (Handler, error) { return closingHandler{f, StreamHandler(f, fmtr)}, nil } -// countingWriter wraps a WriteCloser object in order to count the written bytes. -type countingWriter struct { - w io.WriteCloser // the wrapped object - count uint // number of bytes written -} - -// Write increments the byte counter by the number of bytes written. -// Implements the WriteCloser interface. -func (w *countingWriter) Write(p []byte) (n int, err error) { - n, err = w.w.Write(p) - w.count += uint(n) - return n, err -} - -// Close implements the WriteCloser interface. -func (w *countingWriter) Close() error { - return w.w.Close() -} - -// prepFile opens the log file at the given path, and cuts off the invalid part -// from the end, because the previous execution could have been finished by interruption. -// Assumes that every line ended by '\n' contains a valid log record. -func prepFile(path string) (*countingWriter, error) { - f, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND, 0600) - if err != nil { - return nil, err - } - _, err = f.Seek(-1, io.SeekEnd) - if err != nil { - return nil, err - } - buf := make([]byte, 1) - var cut int64 - for { - if _, err := f.Read(buf); err != nil { - return nil, err - } - if buf[0] == '\n' { - break - } - if _, err = f.Seek(-2, io.SeekCurrent); err != nil { - return nil, err - } - cut++ - } - fi, err := f.Stat() - if err != nil { - return nil, err - } - ns := fi.Size() - cut - if err = f.Truncate(ns); err != nil { - return nil, err - } - return &countingWriter{w: f, count: uint(ns)}, nil -} - -// RotatingFileHandler returns a handler which writes log records to file chunks -// at the given path. When a file's size reaches the limit, the handler creates -// a new file named after the timestamp of the first log record it will contain. -func RotatingFileHandler(path string, limit uint, formatter Format) (Handler, error) { - if err := os.MkdirAll(path, 0700); err != nil { - return nil, err - } - files, err := ioutil.ReadDir(path) - if err != nil { - return nil, err - } - re := regexp.MustCompile(`\.log$`) - last := len(files) - 1 - for last >= 0 && (!files[last].Mode().IsRegular() || !re.MatchString(files[last].Name())) { - last-- - } - var counter *countingWriter - if last >= 0 && files[last].Size() < int64(limit) { - // Open the last file, and continue to write into it until it's size reaches the limit. - if counter, err = prepFile(filepath.Join(path, files[last].Name())); err != nil { - return nil, err - } - } - if counter == nil { - counter = new(countingWriter) - } - h := StreamHandler(counter, formatter) - - return FuncHandler(func(r *Record) error { - if counter.count > limit { - counter.Close() - counter.w = nil - } - if counter.w == nil { - f, err := os.OpenFile( - filepath.Join(path, fmt.Sprintf("%s.log", strings.Replace(r.Time.Format("060102150405.00"), ".", "", 1))), - os.O_CREATE|os.O_APPEND|os.O_WRONLY, - 0600, - ) - if err != nil { - return err - } - counter.w = f - counter.count = 0 - } - return h.Log(r) - }), nil -} - // NetHandler opens a socket to the given address and writes records // over the connection. func NetHandler(network, addr string, fmtr Format) (Handler, error) { From cd9e18984eb99f62b67a15755b63699ab549da82 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:41 +0800 Subject: [PATCH 207/479] log: properly escape character sequences (#20987) --- log/format.go | 54 +++++++++++---------------------------------------- 1 file changed, 11 insertions(+), 43 deletions(-) diff --git a/log/format.go b/log/format.go index 866c6ebe97d0..b2c8b05198e8 100644 --- a/log/format.go +++ b/log/format.go @@ -78,12 +78,11 @@ type TerminalStringer interface { // a terminal with color-coded level output and terser human friendly timestamp. // This format should only be used for interactive programs or while developing. // -// [LEVEL] [TIME] MESAGE key=value key=value ... +// [LEVEL] [TIME] MESAGE key=value key=value ... // // Example: // -// [DBUG] [May 16 20:58:45] remove route ns=haproxy addr=127.0.0.1:50002 -// +// [DBUG] [May 16 20:58:45] remove route ns=haproxy addr=127.0.0.1:50002 func TerminalFormat(usecolor bool) Format { return FormatFunc(func(r *Record) []byte { var color = 0 @@ -148,7 +147,6 @@ func TerminalFormat(usecolor bool) Format { // format for key/value pairs. // // For more details see: http://godoc.org/github.com/kr/logfmt -// func LogfmtFormat() Format { return FormatFunc(func(r *Record) []byte { common := []interface{}{r.KeyNames.Time, r.Time, r.KeyNames.Lvl, r.Lvl, r.KeyNames.Msg, r.Msg} @@ -358,49 +356,19 @@ func formatLogfmtValue(value interface{}, term bool) string { } } -var stringBufPool = sync.Pool{ - New: func() interface{} { return new(bytes.Buffer) }, -} - +// escapeString checks if the provided string needs escaping/quoting, and +// calls strconv.Quote if needed func escapeString(s string) string { - needsQuotes := false - needsEscape := false + needsQuoting := false for _, r := range s { - if r <= ' ' || r == '=' || r == '"' { - needsQuotes = true - } - if r == '\\' || r == '"' || r == '\n' || r == '\r' || r == '\t' { - needsEscape = true + // We quote everything below " (0x34) and above~ (0x7E), plus equal-sign + if r <= '"' || r > '~' || r == '=' { + needsQuoting = true + break } } - if !needsEscape && !needsQuotes { + if !needsQuoting { return s } - e := stringBufPool.Get().(*bytes.Buffer) - e.WriteByte('"') - for _, r := range s { - switch r { - case '\\', '"': - e.WriteByte('\\') - e.WriteByte(byte(r)) - case '\n': - e.WriteString("\\n") - case '\r': - e.WriteString("\\r") - case '\t': - e.WriteString("\\t") - default: - e.WriteRune(r) - } - } - e.WriteByte('"') - var ret string - if needsQuotes { - ret = e.String() - } else { - ret = string(e.Bytes()[1 : e.Len()-1]) - } - e.Reset() - stringBufPool.Put(e) - return ret + return strconv.Quote(s) } From 3d216311456f6eb275a56e1e2670f943c9e0e9ad Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:41 +0800 Subject: [PATCH 208/479] log: fix typos in comments (#21118) --- log/doc.go | 2 +- log/format.go | 2 +- log/handler.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/log/doc.go b/log/doc.go index bff2f4966a4b..993743c0fd5c 100644 --- a/log/doc.go +++ b/log/doc.go @@ -65,7 +65,7 @@ This will output a log line that includes the path context that is attached to t Handlers -The Handler interface defines where log lines are printed to and how they are formated. Handler is a +The Handler interface defines where log lines are printed to and how they are formatted. Handler is a single interface that is inspired by net/http's handler interface: type Handler interface { diff --git a/log/format.go b/log/format.go index b2c8b05198e8..cb6048177035 100644 --- a/log/format.go +++ b/log/format.go @@ -78,7 +78,7 @@ type TerminalStringer interface { // a terminal with color-coded level output and terser human friendly timestamp. // This format should only be used for interactive programs or while developing. // -// [LEVEL] [TIME] MESAGE key=value key=value ... +// [LEVEL] [TIME] MESSAGE key=value key=value ... // // Example: // diff --git a/log/handler.go b/log/handler.go index 13328ad268cd..5fc727d02e4d 100644 --- a/log/handler.go +++ b/log/handler.go @@ -117,7 +117,7 @@ func formatCall(format string, c stack.Call) string { } // CallerStackHandler returns a Handler that adds a stack trace to the context -// with key "stack". The stack trace is formated as a space separated list of +// with key "stack". The stack trace is formatted as a space separated list of // call sites inside matching []'s. The most recent call site is listed first. // Each call site is formatted according to format. See the documentation of // package github.com/go-stack/stack for the list of supported formats. From aedfea681be6f9edc523b25213606c239017310f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:41 +0800 Subject: [PATCH 209/479] all: make logs a bit easier on the eye to digest (#22665) --- accounts/url.go | 9 ++-- common/types.go | 6 +-- core/blockchain.go | 8 +-- core/blockchain_test.go | 4 +- core/chain_indexer.go | 2 +- core/headerchain.go | 2 +- log/format.go | 106 ++++++++++++++++++++++++++++++++++++++-- log/format_test.go | 75 ++++++++++++++++++++++++++++ 8 files changed, 193 insertions(+), 19 deletions(-) create mode 100644 log/format_test.go diff --git a/accounts/url.go b/accounts/url.go index 47f9d8ee4b20..d5c9c645c725 100644 --- a/accounts/url.go +++ b/accounts/url.go @@ -64,7 +64,7 @@ func (u URL) String() string { func (u URL) TerminalString() string { url := u.String() if len(url) > 32 { - return url[:31] + "…" + return url[:31] + ".." } return url } @@ -76,10 +76,9 @@ func (u URL) MarshalJSON() ([]byte, error) { // Cmp compares x and y and returns: // -// -1 if x < y -// 0 if x == y -// +1 if x > y -// +// -1 if x < y +// 0 if x == y +// +1 if x > y func (u URL) Cmp(url URL) int { if u.Scheme == url.Scheme { return strings.Compare(u.Path, url.Path) diff --git a/common/types.go b/common/types.go index 162d2c4083ef..ffd3d42d8475 100644 --- a/common/types.go +++ b/common/types.go @@ -107,7 +107,7 @@ func (h Hash) Cmp(other Hash) int { func (h Hash) IsZero() bool { return h == Hash{} } // Get the string representation of the underlying hash -func (h Hash) Str() string { return string(h[:]) } +func (h Hash) Str() string { return string(h[:]) } // Bytes gets the byte representation of the underlying hash. func (h Hash) Bytes() []byte { return h[:] } @@ -116,12 +116,12 @@ func (h Hash) Bytes() []byte { return h[:] } func (h Hash) Big() *big.Int { return new(big.Int).SetBytes(h[:]) } // Hex converts a hash to a hex string. -func (h Hash) Hex() string { return hexutil.Encode(h[:]) } +func (h Hash) Hex() string { return hexutil.Encode(h[:]) } // TerminalString implements log.TerminalStringer, formatting a string for console // output during logging. func (h Hash) TerminalString() string { - return fmt.Sprintf("%x…%x", h[:3], h[29:]) + return fmt.Sprintf("%x..%x", h[:3], h[29:]) } // String implements the stringer interface and is used also by the logger when diff --git a/core/blockchain.go b/core/blockchain.go index 845b939f657c..2f731331decc 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -440,7 +440,7 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error { // Make sure that both the block as well at its state trie exists block := bc.GetBlockByHash(hash) if block == nil { - return fmt.Errorf("non existent block [%x…]", hash[:4]) + return fmt.Errorf("non existent block [%x..]", hash[:4]) } if _, err := trie.NewSecure(block.Root(), bc.stateCache.TrieDB()); err != nil { return err @@ -1055,7 +1055,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ if blockChain[i].NumberU64() != blockChain[i-1].NumberU64()+1 || blockChain[i].ParentHash() != blockChain[i-1].Hash() { log.Error("Non contiguous receipt insert", "number", blockChain[i].Number(), "hash", blockChain[i].Hash(), "parent", blockChain[i].ParentHash(), "prevnumber", blockChain[i-1].Number(), "prevhash", blockChain[i-1].Hash()) - return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, blockChain[i-1].NumberU64(), + return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, blockChain[i-1].NumberU64(), blockChain[i-1].Hash().Bytes()[:4], i, blockChain[i].NumberU64(), blockChain[i].Hash().Bytes()[:4], blockChain[i].ParentHash().Bytes()[:4]) } } @@ -1075,7 +1075,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ blockHash, blockNumber := block.Hash(), block.NumberU64() // Short circuit if the owner header is unknown if !bc.HasHeader(blockHash, blockNumber) { - return i, fmt.Errorf("containing header #%d [%x…] unknown", blockNumber, blockHash.Bytes()[:4]) + return i, fmt.Errorf("containing header #%d [%x..] unknown", blockNumber, blockHash.Bytes()[:4]) } // Skip if the entire data is already known if bc.HasBlock(blockHash, blockNumber) { @@ -1422,7 +1422,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, [] log.Error("Non contiguous block insert", "number", chain[i].Number(), "hash", chain[i].Hash(), "parent", chain[i].ParentHash(), "prevnumber", chain[i-1].Number(), "prevhash", chain[i-1].Hash()) - return 0, nil, nil, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, chain[i-1].NumberU64(), + return 0, nil, nil, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, chain[i-1].NumberU64(), chain[i-1].Hash().Bytes()[:4], i, chain[i].NumberU64(), chain[i].Hash().Bytes()[:4], chain[i].ParentHash().Bytes()[:4]) } } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index ebf05f31d277..a3c59d27b011 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -1221,13 +1221,13 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) { t.Fatalf("block %d: failed to insert into chain: %v", i, err) } if chain.CurrentBlock().Hash() != chain.CurrentHeader().Hash() { - t.Errorf("block %d: current block/header mismatch: block #%d [%x…], header #%d [%x…]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4]) + t.Errorf("block %d: current block/header mismatch: block #%d [%x..], header #%d [%x..]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4]) } if _, err := chain.InsertChain(forks[i : i+1]); err != nil { t.Fatalf(" fork %d: failed to insert into chain: %v", i, err) } if chain.CurrentBlock().Hash() != chain.CurrentHeader().Hash() { - t.Errorf(" fork %d: current block/header mismatch: block #%d [%x…], header #%d [%x…]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4]) + t.Errorf(" fork %d: current block/header mismatch: block #%d [%x..], header #%d [%x..]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4]) } } } diff --git a/core/chain_indexer.go b/core/chain_indexer.go index dd6466ab3770..77cfac232bd7 100644 --- a/core/chain_indexer.go +++ b/core/chain_indexer.go @@ -356,7 +356,7 @@ func (c *ChainIndexer) processSection(section uint64, lastHead common.Hash) (com } header := GetHeader(c.chainDb, hash, number) if header == nil { - return common.Hash{}, fmt.Errorf("block #%d [%x…] not found", number, hash[:4]) + return common.Hash{}, fmt.Errorf("block #%d [%x..] not found", number, hash[:4]) } else if header.ParentHash != lastHead { return common.Hash{}, errors.New("chain reorged during section processing") } diff --git a/core/headerchain.go b/core/headerchain.go index 424bfb687d6c..b3cdc10f68d5 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -205,7 +205,7 @@ func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int) log.Error("Non contiguous header insert", "number", chain[i].Number, "hash", chain[i].Hash(), "parent", chain[i].ParentHash, "prevnumber", chain[i-1].Number, "prevhash", chain[i-1].Hash()) - return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, chain[i-1].Number, + return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, chain[i-1].Number, chain[i-1].Hash().Bytes()[:4], i, chain[i].Number, chain[i].Hash().Bytes()[:4], chain[i].ParentHash[:4]) } } diff --git a/log/format.go b/log/format.go index cb6048177035..b26fe69f3d17 100644 --- a/log/format.go +++ b/log/format.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "fmt" + "math/big" "reflect" "strconv" "strings" @@ -327,11 +328,20 @@ func formatLogfmtValue(value interface{}, term bool) string { return "nil" } - if t, ok := value.(time.Time); ok { + switch v := value.(type) { + case time.Time: // Performance optimization: No need for escaping since the provided // timeFormat doesn't have any escape characters, and escaping is // expensive. - return t.Format(timeFormat) + return v.Format(timeFormat) + + case *big.Int: + // Big ints get consumed by the Stringer clause so we need to handle + // them earlier on. + if v == nil { + return "" + } + return formatLogfmtBigInt(v) } if term { if s, ok := value.(TerminalStringer); ok { @@ -347,8 +357,24 @@ func formatLogfmtValue(value interface{}, term bool) string { return strconv.FormatFloat(float64(v), floatFormat, 3, 64) case float64: return strconv.FormatFloat(v, floatFormat, 3, 64) - case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + case int8, uint8: return fmt.Sprintf("%d", value) + case int: + return FormatLogfmtInt64(int64(v)) + case int16: + return FormatLogfmtInt64(int64(v)) + case int32: + return FormatLogfmtInt64(int64(v)) + case int64: + return FormatLogfmtInt64(v) + case uint: + return FormatLogfmtUint64(uint64(v)) + case uint16: + return FormatLogfmtUint64(uint64(v)) + case uint32: + return FormatLogfmtUint64(uint64(v)) + case uint64: + return FormatLogfmtUint64(v) case string: return escapeString(v) default: @@ -356,6 +382,80 @@ func formatLogfmtValue(value interface{}, term bool) string { } } +// FormatLogfmtInt64 formats a potentially big number in a friendlier split format. +func FormatLogfmtInt64(n int64) string { + if n < 0 { + return formatLogfmtUint64(uint64(-n), true) + } + return formatLogfmtUint64(uint64(n), false) +} + +// FormatLogfmtUint64 formats a potentially big number in a friendlier split format. +func FormatLogfmtUint64(n uint64) string { + return formatLogfmtUint64(n, false) +} + +func formatLogfmtUint64(n uint64, neg bool) string { + // Small numbers are fine as is + if n < 100000 { + if neg { + return strconv.Itoa(-int(n)) + } else { + return strconv.Itoa(int(n)) + } + } + // Large numbers should be split + const maxLength = 26 + + var ( + out = make([]byte, maxLength) + i = maxLength - 1 + comma = 0 + ) + for ; n > 0; i-- { + if comma == 3 { + comma = 0 + out[i] = ',' + } else { + comma++ + out[i] = '0' + byte(n%10) + n /= 10 + } + } + if neg { + out[i] = '-' + i-- + } + return string(out[i+1:]) +} + +var big1000 = big.NewInt(1000) + +// formatLogfmtBigInt formats a potentially gigantic number in a friendlier split +// format. +func formatLogfmtBigInt(n *big.Int) string { + // Most number don't need fancy handling, just downcast + if n.IsUint64() { + return FormatLogfmtUint64(n.Uint64()) + } + if n.IsInt64() { + return FormatLogfmtInt64(n.Int64()) + } + // Ok, huge number needs huge effort + groups := make([]string, 0, 8) // random initial size to cover most cases + for n.Cmp(big1000) >= 0 { + _, mod := n.DivMod(n, big1000, nil) + groups = append(groups, fmt.Sprintf("%03d", mod)) + } + groups = append(groups, n.String()) + + last := len(groups) - 1 + for i := 0; i < len(groups)/2; i++ { + groups[i], groups[last-i] = groups[last-i], groups[i] + } + return strings.Join(groups, ",") +} + // escapeString checks if the provided string needs escaping/quoting, and // calls strconv.Quote if needed func escapeString(s string) string { diff --git a/log/format_test.go b/log/format_test.go new file mode 100644 index 000000000000..348b265c9b27 --- /dev/null +++ b/log/format_test.go @@ -0,0 +1,75 @@ +package log + +import ( + "math" + "math/rand" + "testing" +) + +func TestPrettyInt64(t *testing.T) { + tests := []struct { + n int64 + s string + }{ + {0, "0"}, + {10, "10"}, + {-10, "-10"}, + {100, "100"}, + {-100, "-100"}, + {1000, "1000"}, + {-1000, "-1000"}, + {10000, "10000"}, + {-10000, "-10000"}, + {99999, "99999"}, + {-99999, "-99999"}, + {100000, "100,000"}, + {-100000, "-100,000"}, + {1000000, "1,000,000"}, + {-1000000, "-1,000,000"}, + {math.MaxInt64, "9,223,372,036,854,775,807"}, + {math.MinInt64, "-9,223,372,036,854,775,808"}, + } + for i, tt := range tests { + if have := FormatLogfmtInt64(tt.n); have != tt.s { + t.Errorf("test %d: format mismatch: have %s, want %s", i, have, tt.s) + } + } +} + +func TestPrettyUint64(t *testing.T) { + tests := []struct { + n uint64 + s string + }{ + {0, "0"}, + {10, "10"}, + {100, "100"}, + {1000, "1000"}, + {10000, "10000"}, + {99999, "99999"}, + {100000, "100,000"}, + {1000000, "1,000,000"}, + {math.MaxUint64, "18,446,744,073,709,551,615"}, + } + for i, tt := range tests { + if have := FormatLogfmtUint64(tt.n); have != tt.s { + t.Errorf("test %d: format mismatch: have %s, want %s", i, have, tt.s) + } + } +} + +var sink string + +func BenchmarkPrettyInt64Logfmt(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + sink = FormatLogfmtInt64(rand.Int63()) + } +} + +func BenchmarkPrettyUint64Logfmt(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + sink = FormatLogfmtUint64(rand.Uint64()) + } +} From 07443431d3bcc68a6af056feb3dd7d8ca05f1491 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:41 +0800 Subject: [PATCH 210/479] log: fix formatting of big.Int (#22679) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * log: fix formatting of big.Int The implementation of formatLogfmtBigInt had two issues: it crashed when the number was actually large enough to hit the big integer case, and modified the big.Int while formatting it. * log: don't call FormatLogfmtInt64 for int16 * log: separate from decimals back, not front Co-authored-by: Péter Szilágyi --- log/format.go | 58 +++++++++++++++++++++++++++------------------- log/format_test.go | 20 ++++++++++++++++ 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/log/format.go b/log/format.go index b26fe69f3d17..5a175ec40218 100644 --- a/log/format.go +++ b/log/format.go @@ -357,11 +357,16 @@ func formatLogfmtValue(value interface{}, term bool) string { return strconv.FormatFloat(float64(v), floatFormat, 3, 64) case float64: return strconv.FormatFloat(v, floatFormat, 3, 64) - case int8, uint8: - return fmt.Sprintf("%d", value) - case int: - return FormatLogfmtInt64(int64(v)) + case int8: + return strconv.FormatInt(int64(v), 10) + case uint8: + return strconv.FormatInt(int64(v), 10) case int16: + return strconv.FormatInt(int64(v), 10) + case uint16: + return strconv.FormatInt(int64(v), 10) + // Larger integers get thousands separators. + case int: return FormatLogfmtInt64(int64(v)) case int32: return FormatLogfmtInt64(int64(v)) @@ -369,8 +374,6 @@ func formatLogfmtValue(value interface{}, term bool) string { return FormatLogfmtInt64(v) case uint: return FormatLogfmtUint64(uint64(v)) - case uint16: - return FormatLogfmtUint64(uint64(v)) case uint32: return FormatLogfmtUint64(uint64(v)) case uint64: @@ -382,7 +385,7 @@ func formatLogfmtValue(value interface{}, term bool) string { } } -// FormatLogfmtInt64 formats a potentially big number in a friendlier split format. +// FormatLogfmtInt64 formats n with thousand separators. func FormatLogfmtInt64(n int64) string { if n < 0 { return formatLogfmtUint64(uint64(-n), true) @@ -390,7 +393,7 @@ func FormatLogfmtInt64(n int64) string { return formatLogfmtUint64(uint64(n), false) } -// FormatLogfmtUint64 formats a potentially big number in a friendlier split format. +// FormatLogfmtUint64 formats n with thousand separators. func FormatLogfmtUint64(n uint64) string { return formatLogfmtUint64(n, false) } @@ -429,31 +432,38 @@ func formatLogfmtUint64(n uint64, neg bool) string { return string(out[i+1:]) } -var big1000 = big.NewInt(1000) - -// formatLogfmtBigInt formats a potentially gigantic number in a friendlier split -// format. +// formatLogfmtBigInt formats n with thousand separators. func formatLogfmtBigInt(n *big.Int) string { - // Most number don't need fancy handling, just downcast if n.IsUint64() { return FormatLogfmtUint64(n.Uint64()) } if n.IsInt64() { return FormatLogfmtInt64(n.Int64()) } - // Ok, huge number needs huge effort - groups := make([]string, 0, 8) // random initial size to cover most cases - for n.Cmp(big1000) >= 0 { - _, mod := n.DivMod(n, big1000, nil) - groups = append(groups, fmt.Sprintf("%03d", mod)) - } - groups = append(groups, n.String()) - last := len(groups) - 1 - for i := 0; i < len(groups)/2; i++ { - groups[i], groups[last-i] = groups[last-i], groups[i] + var ( + text = n.String() + buf = make([]byte, len(text)+len(text)/3) + comma = 0 + i = len(buf) - 1 + ) + for j := len(text) - 1; j >= 0; j, i = j-1, i-1 { + c := text[j] + + switch { + case c == '-': + buf[i] = c + case comma == 3: + buf[i] = ',' + i-- + comma = 0 + fallthrough + default: + buf[i] = c + comma++ + } } - return strings.Join(groups, ",") + return string(buf[i+1:]) } // escapeString checks if the provided string needs escaping/quoting, and diff --git a/log/format_test.go b/log/format_test.go index 348b265c9b27..d7e0a9576805 100644 --- a/log/format_test.go +++ b/log/format_test.go @@ -2,6 +2,7 @@ package log import ( "math" + "math/big" "math/rand" "testing" ) @@ -58,6 +59,25 @@ func TestPrettyUint64(t *testing.T) { } } +func TestPrettyBigInt(t *testing.T) { + tests := []struct { + int string + s string + }{ + {"111222333444555678999", "111,222,333,444,555,678,999"}, + {"-111222333444555678999", "-111,222,333,444,555,678,999"}, + {"11122233344455567899900", "11,122,233,344,455,567,899,900"}, + {"-11122233344455567899900", "-11,122,233,344,455,567,899,900"}, + } + + for _, tt := range tests { + v, _ := new(big.Int).SetString(tt.int, 10) + if have := formatLogfmtBigInt(v); have != tt.s { + t.Errorf("invalid output %s, want %s", have, tt.s) + } + } +} + var sink string func BenchmarkPrettyInt64Logfmt(b *testing.B) { From 0902dcf37c48e19100aade8bfb3c5aafa8a43438 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:41 +0800 Subject: [PATCH 211/479] log: modify lock defer unlock order in sync handler (#24667) This modifies the order of Lock() defer Unlock() to follow the more typically used pattern. --- log/handler.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/log/handler.go b/log/handler.go index 5fc727d02e4d..892cfcc3e1ac 100644 --- a/log/handler.go +++ b/log/handler.go @@ -52,8 +52,9 @@ func StreamHandler(wr io.Writer, fmtr Format) Handler { func SyncHandler(h Handler) Handler { var mu sync.Mutex return FuncHandler(func(r *Record) error { - defer mu.Unlock() mu.Lock() + defer mu.Unlock() + return h.Log(r) }) } From dce80d77b7c12e2e9b2715cbc99ceec72502989b Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 212/479] log: better sanitation (#26556) --- log/format.go | 38 ++++++++++++++++++++++++++++++++------ log/format_test.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/log/format.go b/log/format.go index 5a175ec40218..6c78d6da7e40 100644 --- a/log/format.go +++ b/log/format.go @@ -86,6 +86,7 @@ type TerminalStringer interface { // [DBUG] [May 16 20:58:45] remove route ns=haproxy addr=127.0.0.1:50002 func TerminalFormat(usecolor bool) Format { return FormatFunc(func(r *Record) []byte { + msg := escapeMessage(r.Msg) var color = 0 if usecolor { switch r.Lvl { @@ -122,19 +123,19 @@ func TerminalFormat(usecolor bool) Format { // Assemble and print the log heading if color > 0 { - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s|%s]%s %s ", color, lvl, r.Time.Format(termTimeFormat), location, padding, r.Msg) + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s|%s]%s %s ", color, lvl, r.Time.Format(termTimeFormat), location, padding, msg) } else { - fmt.Fprintf(b, "%s[%s|%s]%s %s ", lvl, r.Time.Format(termTimeFormat), location, padding, r.Msg) + fmt.Fprintf(b, "%s[%s|%s]%s %s ", lvl, r.Time.Format(termTimeFormat), location, padding, msg) } } else { if color > 0 { - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), r.Msg) + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), msg) } else { - fmt.Fprintf(b, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), r.Msg) + fmt.Fprintf(b, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), msg) } } // try to justify the log output for short messages - length := utf8.RuneCountInString(r.Msg) + length := utf8.RuneCountInString(msg) if len(r.Ctx) > 0 && length < termMsgJust { b.Write(bytes.Repeat([]byte{' '}, termMsgJust-length)) } @@ -167,6 +168,8 @@ func logfmt(buf *bytes.Buffer, ctx []interface{}, color int, term bool) { v := formatLogfmtValue(ctx[i+1], term) if !ok { k, v = errorKey, formatLogfmtValue(k, term) + } else { + k = escapeString(k) } // XXX: we should probably check that all of your key bytes aren't invalid @@ -471,7 +474,7 @@ func formatLogfmtBigInt(n *big.Int) string { func escapeString(s string) string { needsQuoting := false for _, r := range s { - // We quote everything below " (0x34) and above~ (0x7E), plus equal-sign + // We quote everything below " (0x22) and above~ (0x7E), plus equal-sign if r <= '"' || r > '~' || r == '=' { needsQuoting = true break @@ -482,3 +485,26 @@ func escapeString(s string) string { } return strconv.Quote(s) } + +// escapeMessage checks if the provided string needs escaping/quoting, similarly +// to escapeString. The difference is that this method is more lenient: it allows +// for spaces and linebreaks to occur without needing quoting. +func escapeMessage(s string) string { + needsQuoting := false + for _, r := range s { + // Carriage return and Line feed are ok + if r == 0xa || r == 0xd { + continue + } + // We quote everything below (0x20) and above~ (0x7E), + // plus equal-sign + if r < ' ' || r > '~' || r == '=' { + needsQuoting = true + break + } + } + if !needsQuoting { + return s + } + return strconv.Quote(s) +} diff --git a/log/format_test.go b/log/format_test.go index d7e0a9576805..cfcfe85802a4 100644 --- a/log/format_test.go +++ b/log/format_test.go @@ -1,9 +1,11 @@ package log import ( + "fmt" "math" "math/big" "math/rand" + "strings" "testing" ) @@ -93,3 +95,47 @@ func BenchmarkPrettyUint64Logfmt(b *testing.B) { sink = FormatLogfmtUint64(rand.Uint64()) } } + +func TestSanitation(t *testing.T) { + msg := "\u001b[1G\u001b[K\u001b[1A" + msg2 := "\u001b \u0000" + msg3 := "NiceMessage" + msg4 := "Space Message" + msg5 := "Enter\nMessage" + + for i, tt := range []struct { + msg string + want string + }{ + { + msg: msg, + want: fmt.Sprintf("] %q %q=%q\n", msg, msg, msg), + }, + { + msg: msg2, + want: fmt.Sprintf("] %q %q=%q\n", msg2, msg2, msg2), + }, + { + msg: msg3, + want: fmt.Sprintf("] %s %s=%s\n", msg3, msg3, msg3), + }, + { + msg: msg4, + want: fmt.Sprintf("] %s %q=%q\n", msg4, msg4, msg4), + }, + { + msg: msg5, + want: fmt.Sprintf("] %s %q=%q\n", msg5, msg5, msg5), + }, + } { + var ( + logger = New() + out = new(strings.Builder) + ) + logger.SetHandler(LvlFilterHandler(LvlInfo, StreamHandler(out, TerminalFormat(false)))) + logger.Info(tt.msg, tt.msg, tt.msg) + if have := out.String()[24:]; tt.want != have { + t.Fatalf("test %d: want / have: \n%v\n%v", i, tt.want, have) + } + } +} From ed4f989ff3710ee495d337254ff7202b9efb2dfa Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 213/479] log: allow tabs in log messages (#26630) * log: allow tabs in log messages This fixes a regression where panic reports in RPC handlers were quoted because they contain tab characters. * Update format.go --- log/format.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/log/format.go b/log/format.go index 6c78d6da7e40..4fe163247d35 100644 --- a/log/format.go +++ b/log/format.go @@ -492,8 +492,8 @@ func escapeString(s string) string { func escapeMessage(s string) string { needsQuoting := false for _, r := range s { - // Carriage return and Line feed are ok - if r == 0xa || r == 0xd { + // Allow CR/LF/TAB. This is to make multi-line messages work. + if r == '\r' || r == '\n' || r == '\t' { continue } // We quote everything below (0x20) and above~ (0x7E), From 5eca853e52c229126a38d8b337844b59d330040c Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 214/479] log: improve documentation (#26753) Add usage examples --- log/logger.go | 48 +++++++++++++++++++++++++++++++++++++++++++++++- log/root.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/log/logger.go b/log/logger.go index 1d5e845db444..6d89a17cd879 100644 --- a/log/logger.go +++ b/log/logger.go @@ -115,12 +115,58 @@ type Logger interface { // SetHandler updates the logger to write records to the specified handler. SetHandler(h Handler) - // Log a message at the given level with context key/value pairs + // Log a message at the trace level with context key/value pairs + // + // # Usage + // + // log.Trace("msg") + // log.Trace("msg", "key1", val1) + // log.Trace("msg", "key1", val1, "key2", val2) Trace(msg string, ctx ...interface{}) + + // Log a message at the debug level with context key/value pairs + // + // # Usage Examples + // + // log.Debug("msg") + // log.Debug("msg", "key1", val1) + // log.Debug("msg", "key1", val1, "key2", val2) Debug(msg string, ctx ...interface{}) + + // Log a message at the info level with context key/value pairs + // + // # Usage Examples + // + // log.Info("msg") + // log.Info("msg", "key1", val1) + // log.Info("msg", "key1", val1, "key2", val2) Info(msg string, ctx ...interface{}) + + // Log a message at the warn level with context key/value pairs + // + // # Usage Examples + // + // log.Warn("msg") + // log.Warn("msg", "key1", val1) + // log.Warn("msg", "key1", val1, "key2", val2) Warn(msg string, ctx ...interface{}) + + // Log a message at the error level with context key/value pairs + // + // # Usage Examples + // + // log.Error("msg") + // log.Error("msg", "key1", val1) + // log.Error("msg", "key1", val1, "key2", val2) Error(msg string, ctx ...interface{}) + + // Log a message at the crit level with context key/value pairs, and then exit. + // + // # Usage Examples + // + // log.Crit("msg") + // log.Crit("msg", "key1", val1) + // log.Crit("msg", "key1", val1, "key2", val2) Crit(msg string, ctx ...interface{}) } diff --git a/log/root.go b/log/root.go index 71b8cef6d4bf..66ffaaa3a0d8 100644 --- a/log/root.go +++ b/log/root.go @@ -30,31 +30,79 @@ func Root() Logger { // runtime.Caller(2) always refers to the call site in client code. // Trace is a convenient alias for Root().Trace +// +// Log a message at the trace level with context key/value pairs +// +// # Usage +// +// log.Trace("msg") +// log.Trace("msg", "key1", val1) +// log.Trace("msg", "key1", val1, "key2", val2) func Trace(msg string, ctx ...interface{}) { root.write(msg, LvlTrace, ctx) } // Debug is a convenient alias for Root().Debug +// +// Log a message at the debug level with context key/value pairs +// +// # Usage Examples +// +// log.Debug("msg") +// log.Debug("msg", "key1", val1) +// log.Debug("msg", "key1", val1, "key2", val2) func Debug(msg string, ctx ...interface{}) { root.write(msg, LvlDebug, ctx) } // Info is a convenient alias for Root().Info +// +// Log a message at the info level with context key/value pairs +// +// # Usage Examples +// +// log.Info("msg") +// log.Info("msg", "key1", val1) +// log.Info("msg", "key1", val1, "key2", val2) func Info(msg string, ctx ...interface{}) { root.write(msg, LvlInfo, ctx) } // Warn is a convenient alias for Root().Warn +// +// Log a message at the warn level with context key/value pairs +// +// # Usage Examples +// +// log.Warn("msg") +// log.Warn("msg", "key1", val1) +// log.Warn("msg", "key1", val1, "key2", val2) func Warn(msg string, ctx ...interface{}) { root.write(msg, LvlWarn, ctx) } // Error is a convenient alias for Root().Error +// +// Log a message at the error level with context key/value pairs +// +// # Usage Examples +// +// log.Error("msg") +// log.Error("msg", "key1", val1) +// log.Error("msg", "key1", val1, "key2", val2) func Error(msg string, ctx ...interface{}) { root.write(msg, LvlError, ctx) } // Crit is a convenient alias for Root().Crit +// +// Log a message at the crit level with context key/value pairs, and then exit. +// +// # Usage Examples +// +// log.Crit("msg") +// log.Crit("msg", "key1", val1) +// log.Crit("msg", "key1", val1, "key2", val2) func Crit(msg string, ctx ...interface{}) { root.write(msg, LvlCrit, ctx) os.Exit(1) From 2359a9ed34dda1f9f45745c0704e8477ab362033 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 215/479] log: add special casing of uint256 into the logger (#26936) --- log/format.go | 42 +++++++++++++++++++++++++++++++++++++++++- log/format_test.go | 20 ++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/log/format.go b/log/format.go index 4fe163247d35..fd358833da45 100644 --- a/log/format.go +++ b/log/format.go @@ -12,6 +12,8 @@ import ( "sync/atomic" "time" "unicode/utf8" + + "github.com/holiman/uint256" ) const ( @@ -339,12 +341,20 @@ func formatLogfmtValue(value interface{}, term bool) string { return v.Format(timeFormat) case *big.Int: - // Big ints get consumed by the Stringer clause so we need to handle + // Big ints get consumed by the Stringer clause, so we need to handle // them earlier on. if v == nil { return "" } return formatLogfmtBigInt(v) + + case *uint256.Int: + // Uint256s get consumed by the Stringer clause, so we need to handle + // them earlier on. + if v == nil { + return "" + } + return formatLogfmtUint256(v) } if term { if s, ok := value.(TerminalStringer); ok { @@ -469,6 +479,36 @@ func formatLogfmtBigInt(n *big.Int) string { return string(buf[i+1:]) } +// formatLogfmtUint256 formats n with thousand separators. +func formatLogfmtUint256(n *uint256.Int) string { + if n.IsUint64() { + return FormatLogfmtUint64(n.Uint64()) + } + var ( + text = n.Dec() + buf = make([]byte, len(text)+len(text)/3) + comma = 0 + i = len(buf) - 1 + ) + for j := len(text) - 1; j >= 0; j, i = j-1, i-1 { + c := text[j] + + switch { + case c == '-': + buf[i] = c + case comma == 3: + buf[i] = ',' + i-- + comma = 0 + fallthrough + default: + buf[i] = c + comma++ + } + } + return string(buf[i+1:]) +} + // escapeString checks if the provided string needs escaping/quoting, and // calls strconv.Quote if needed func escapeString(s string) string { diff --git a/log/format_test.go b/log/format_test.go index cfcfe85802a4..e08c1d1a4a9c 100644 --- a/log/format_test.go +++ b/log/format_test.go @@ -7,6 +7,8 @@ import ( "math/rand" "strings" "testing" + + "github.com/holiman/uint256" ) func TestPrettyInt64(t *testing.T) { @@ -80,6 +82,24 @@ func TestPrettyBigInt(t *testing.T) { } } +func TestPrettyUint256(t *testing.T) { + tests := []struct { + int string + s string + }{ + {"111222333444555678999", "111,222,333,444,555,678,999"}, + {"11122233344455567899900", "11,122,233,344,455,567,899,900"}, + } + + for _, tt := range tests { + v := new(uint256.Int) + v.SetFromDecimal(tt.int) + if have := formatLogfmtUint256(v); have != tt.s { + t.Errorf("invalid output %s, want %s", have, tt.s) + } + } +} + var sink string func BenchmarkPrettyInt64Logfmt(b *testing.B) { From f75958c0ac9551a4d2e61d315b5a726d3ca90b24 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 216/479] log: report error when ctx key is non-string (#27226) * log/format.go : invalid string cast fix * log: some polish --------- Co-authored-by: Martin Holst Swende --- log/format.go | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/log/format.go b/log/format.go index fd358833da45..ba556b62ee64 100644 --- a/log/format.go +++ b/log/format.go @@ -169,7 +169,7 @@ func logfmt(buf *bytes.Buffer, ctx []interface{}, color int, term bool) { k, ok := ctx[i].(string) v := formatLogfmtValue(ctx[i+1], term) if !ok { - k, v = errorKey, formatLogfmtValue(k, term) + k, v = errorKey, fmt.Sprintf("%+T is not a string key", ctx[i]) } else { k = escapeString(k) } @@ -218,20 +218,20 @@ func JSONFormatOrderedEx(pretty, lineSeparated bool) Format { } } return FormatFunc(func(r *Record) []byte { - props := make(map[string]interface{}) - - props[r.KeyNames.Time] = r.Time - props[r.KeyNames.Lvl] = r.Lvl.String() - props[r.KeyNames.Msg] = r.Msg + props := map[string]interface{}{ + r.KeyNames.Time: r.Time, + r.KeyNames.Lvl: r.Lvl.String(), + r.KeyNames.Msg: r.Msg, + } ctx := make([]string, len(r.Ctx)) for i := 0; i < len(r.Ctx); i += 2 { - k, ok := r.Ctx[i].(string) - if !ok { - props[errorKey] = fmt.Sprintf("%+v is not a string key,", r.Ctx[i]) + if k, ok := r.Ctx[i].(string); ok { + ctx[i] = k + ctx[i+1] = formatLogfmtValue(r.Ctx[i+1], true) + } else { + props[errorKey] = fmt.Sprintf("%+T is not a string key,", r.Ctx[i]) } - ctx[i] = k - ctx[i+1] = formatLogfmtValue(r.Ctx[i+1], true) } props[r.KeyNames.Ctx] = ctx @@ -261,18 +261,19 @@ func JSONFormatEx(pretty, lineSeparated bool) Format { } return FormatFunc(func(r *Record) []byte { - props := make(map[string]interface{}) - - props[r.KeyNames.Time] = r.Time - props[r.KeyNames.Lvl] = r.Lvl.String() - props[r.KeyNames.Msg] = r.Msg + props := map[string]interface{}{ + r.KeyNames.Time: r.Time, + r.KeyNames.Lvl: r.Lvl.String(), + r.KeyNames.Msg: r.Msg, + } for i := 0; i < len(r.Ctx); i += 2 { k, ok := r.Ctx[i].(string) if !ok { - props[errorKey] = fmt.Sprintf("%+v is not a string key", r.Ctx[i]) + props[errorKey] = fmt.Sprintf("%+T is not a string key", r.Ctx[i]) + } else { + props[k] = formatJSONValue(r.Ctx[i+1]) } - props[k] = formatJSONValue(r.Ctx[i+1]) } b, err := jsonMarshal(props) From 6b81e685378affffe931b0c29680c42f5977cf30 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 217/479] log: use atomic types (#27763) Co-authored-by: Felix Lange --- log/format.go | 16 ++++++---------- log/handler_glog.go | 18 +++++++++--------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/log/format.go b/log/format.go index ba556b62ee64..fd14d6a9752b 100644 --- a/log/format.go +++ b/log/format.go @@ -32,20 +32,16 @@ var locationTrims = []string{ // PrintOrigins sets or unsets log location (file:line) printing for terminal // format output. func PrintOrigins(print bool) { - if print { - atomic.StoreUint32(&locationEnabled, 1) - } else { - atomic.StoreUint32(&locationEnabled, 0) - } + locationEnabled.Store(print) } // locationEnabled is an atomic flag controlling whether the terminal formatter // should append the log locations too when printing entries. -var locationEnabled uint32 +var locationEnabled atomic.Bool // locationLength is the maxmimum path length encountered, which all logs are // padded to to aid in alignment. -var locationLength uint32 +var locationLength atomic.Uint32 // fieldPadding is a global map with maximum field value lengths seen until now // to allow padding log contexts in a bit smarter way. @@ -109,17 +105,17 @@ func TerminalFormat(usecolor bool) Format { b := &bytes.Buffer{} lvl := r.Lvl.AlignedString() - if atomic.LoadUint32(&locationEnabled) != 0 { + if locationEnabled.Load() { // Log origin printing was requested, format the location path and line number location := fmt.Sprintf("%+v", r.Call) for _, prefix := range locationTrims { location = strings.TrimPrefix(location, prefix) } // Maintain the maximum location length for fancyer alignment - align := int(atomic.LoadUint32(&locationLength)) + align := int(locationLength.Load()) if align < len(location) { align = len(location) - atomic.StoreUint32(&locationLength, uint32(align)) + locationLength.Store(uint32(align)) } padding := strings.Repeat(" ", align-len(location)) diff --git a/log/handler_glog.go b/log/handler_glog.go index b5186d4b27ec..6db5f1a4c9ba 100644 --- a/log/handler_glog.go +++ b/log/handler_glog.go @@ -39,9 +39,9 @@ var errTraceSyntax = errors.New("expect file.go:234") type GlogHandler struct { origin Handler // The origin handler this wraps - level uint32 // Current log level, atomically accessible - override uint32 // Flag whether overrides are used, atomically accessible - backtrace uint32 // Flag whether backtrace location is set + level atomic.Uint32 // Current log level, atomically accessible + override atomic.Bool // Flag whether overrides are used, atomically accessible + backtrace atomic.Bool // Flag whether backtrace location is set patterns []pattern // Current list of patterns to override with siteCache map[uintptr]Lvl // Cache of callsite pattern evaluations @@ -72,7 +72,7 @@ type pattern struct { // Verbosity sets the glog verbosity ceiling. The verbosity of individual packages // and source files can be raised using Vmodule. func (h *GlogHandler) Verbosity(level Lvl) { - atomic.StoreUint32(&h.level, uint32(level)) + h.level.Store(uint32(level)) } // Vmodule sets the glog verbosity pattern. @@ -138,7 +138,7 @@ func (h *GlogHandler) Vmodule(ruleset string) error { h.patterns = filter h.siteCache = make(map[uintptr]Lvl) - atomic.StoreUint32(&h.override, uint32(len(filter))) + h.override.Store(len(filter) != 0) return nil } @@ -171,7 +171,7 @@ func (h *GlogHandler) BacktraceAt(location string) error { defer h.lock.Unlock() h.location = location - atomic.StoreUint32(&h.backtrace, uint32(len(location))) + h.backtrace.Store(len(location) > 0) return nil } @@ -180,7 +180,7 @@ func (h *GlogHandler) BacktraceAt(location string) error { // and backtrace filters, finally emitting it if either allow it through. func (h *GlogHandler) Log(r *Record) error { // If backtracing is requested, check whether this is the callsite - if atomic.LoadUint32(&h.backtrace) > 0 { + if h.backtrace.Load() { // Everything below here is slow. Although we could cache the call sites the // same way as for vmodule, backtracing is so rare it's not worth the extra // complexity. @@ -198,11 +198,11 @@ func (h *GlogHandler) Log(r *Record) error { } } // If the global log level allows, fast track logging - if atomic.LoadUint32(&h.level) >= uint32(r.Lvl) { + if h.level.Load() >= uint32(r.Lvl) { return h.origin.Log(r) } // If no local overrides are present, fast track skipping - if atomic.LoadUint32(&h.override) == 0 { + if !h.override.Load() { return nil } // Check callsite cache for previously calculated log levels From 41c4c9ba888f9badb8daaeb14ef453ea6ae48c9e Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 218/479] log: avoid stack lookups when not needed/used (#28069) --- common/types_test.go | 12 ++++++++ log/format.go | 8 ++++++ log/handler_glog.go | 7 ++++- log/logger.go | 9 ++++-- log/logger_test.go | 67 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 log/logger_test.go diff --git a/common/types_test.go b/common/types_test.go index fc52878f1565..7c2822854ab8 100644 --- a/common/types_test.go +++ b/common/types_test.go @@ -21,6 +21,7 @@ import ( "math/big" "strings" "testing" + "time" ) func TestBytesConversion(t *testing.T) { @@ -203,3 +204,14 @@ func TestStringToBinaryAddress(t *testing.T) { } } } + +func BenchmarkPrettyDuration(b *testing.B) { + var x = PrettyDuration(time.Duration(int64(1203123912312))) + b.Logf("Pre %s", time.Duration(x).String()) + var a string + b.ResetTimer() + for i := 0; i < b.N; i++ { + a = x.String() + } + b.Logf("Post %s", a) +} diff --git a/log/format.go b/log/format.go index fd14d6a9752b..bce4f439ad16 100644 --- a/log/format.go +++ b/log/format.go @@ -33,8 +33,16 @@ var locationTrims = []string{ // format output. func PrintOrigins(print bool) { locationEnabled.Store(print) + if print { + stackEnabled.Store(true) + } } +// stackEnabled is an atomic flag controlling whether the log handler needs +// to store the callsite stack. This is needed in case any handler wants to +// print locations (locationEnabled), use vmodule, or print full stacks (BacktraceAt). +var stackEnabled atomic.Bool + // locationEnabled is an atomic flag controlling whether the terminal formatter // should append the log locations too when printing entries. var locationEnabled atomic.Bool diff --git a/log/handler_glog.go b/log/handler_glog.go index 6db5f1a4c9ba..1dd4764bda11 100644 --- a/log/handler_glog.go +++ b/log/handler_glog.go @@ -139,6 +139,10 @@ func (h *GlogHandler) Vmodule(ruleset string) error { h.patterns = filter h.siteCache = make(map[uintptr]Lvl) h.override.Store(len(filter) != 0) + // Enable location storage (globally) + if len(h.patterns) > 0 { + stackEnabled.Store(true) + } return nil } @@ -172,7 +176,8 @@ func (h *GlogHandler) BacktraceAt(location string) error { h.location = location h.backtrace.Store(len(location) > 0) - + // Enable location storage (globally) + stackEnabled.Store(true) return nil } diff --git a/log/logger.go b/log/logger.go index 6d89a17cd879..9bbfdc912c61 100644 --- a/log/logger.go +++ b/log/logger.go @@ -176,19 +176,22 @@ type logger struct { } func (l *logger) write(msg string, lvl Lvl, ctx []interface{}) { - l.h.Log(&Record{ + record := &Record{ Time: time.Now(), Lvl: lvl, Msg: msg, Ctx: newContext(l.ctx, ctx), - Call: stack.Caller(2), KeyNames: RecordKeyNames{ Time: timeKey, Msg: msgKey, Lvl: lvlKey, Ctx: ctxKey, }, - }) + } + if stackEnabled.Load() { + record.Call = stack.Caller(2) + } + l.h.Log(record) } func (l *logger) New(ctx ...interface{}) Logger { diff --git a/log/logger_test.go b/log/logger_test.go new file mode 100644 index 000000000000..2e59b3fdf0b1 --- /dev/null +++ b/log/logger_test.go @@ -0,0 +1,67 @@ +package log + +import ( + "bytes" + "os" + "strings" + "testing" +) + +// TestLoggingWithTrace checks that if BackTraceAt is set, then the +// gloghandler is capable of spitting out a stacktrace +func TestLoggingWithTrace(t *testing.T) { + defer stackEnabled.Store(stackEnabled.Load()) + out := new(bytes.Buffer) + logger := New() + { + glog := NewGlogHandler(StreamHandler(out, TerminalFormat(false))) + glog.Verbosity(LvlTrace) + if err := glog.BacktraceAt("logger_test.go:24"); err != nil { + t.Fatal(err) + } + logger.SetHandler(glog) + } + logger.Trace("a message", "foo", "bar") // Will be bumped to INFO + have := out.String() + if !strings.HasPrefix(have, "INFO") { + t.Fatalf("backtraceat should bump level to info: %s", have) + } + // The timestamp is locale-dependent, so we want to trim that off + // "INFO [01-01|00:00:00.000] a messag ..." -> "a messag..." + have = strings.Split(have, "]")[1] + wantPrefix := " a message\n\ngoroutine" + if !strings.HasPrefix(have, wantPrefix) { + t.Errorf("\nhave: %q\nwant: %q\n", have, wantPrefix) + } +} + +// TestLoggingWithVmodule checks that vmodule works. +func TestLoggingWithVmodule(t *testing.T) { + defer stackEnabled.Store(stackEnabled.Load()) + out := new(bytes.Buffer) + logger := New() + { + glog := NewGlogHandler(StreamHandler(out, TerminalFormat(false))) + glog.Verbosity(LvlCrit) + logger.SetHandler(glog) + logger.Warn("This should not be seen", "ignored", "true") + glog.Vmodule("logger_test.go=5") + } + logger.Trace("a message", "foo", "bar") + have := out.String() + // The timestamp is locale-dependent, so we want to trim that off + // "INFO [01-01|00:00:00.000] a messag ..." -> "a messag..." + have = strings.Split(have, "]")[1] + want := " a message foo=bar\n" + if have != want { + t.Errorf("\nhave: %q\nwant: %q\n", have, want) + } +} + +func BenchmarkTraceLogging(b *testing.B) { + Root().SetHandler(LvlFilterHandler(LvlInfo, StreamHandler(os.Stderr, TerminalFormat(true)))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + Trace("a message", "v", i) + } +} From f9cae3b9aa37bf683422629e67073976ea0ba8ef Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 219/479] internal, log: remove code for old unsupported go-versions (#28090) --- internal/debug/loudpanic.go | 2 -- internal/debug/loudpanic_fallback.go | 24 --------------------- internal/debug/trace.go | 2 -- internal/debug/trace_fallback.go | 31 ---------------------------- log/handler.go | 19 +++++++++++++++++ log/handler_go13.go | 26 ----------------------- log/handler_go14.go | 23 --------------------- 7 files changed, 19 insertions(+), 108 deletions(-) delete mode 100644 internal/debug/loudpanic_fallback.go delete mode 100644 internal/debug/trace_fallback.go delete mode 100644 log/handler_go13.go delete mode 100644 log/handler_go14.go diff --git a/internal/debug/loudpanic.go b/internal/debug/loudpanic.go index 572ebcefa14f..a7296e7b3f33 100644 --- a/internal/debug/loudpanic.go +++ b/internal/debug/loudpanic.go @@ -14,8 +14,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// +build go1.6 - package debug import "runtime/debug" diff --git a/internal/debug/loudpanic_fallback.go b/internal/debug/loudpanic_fallback.go deleted file mode 100644 index 4ce4985da7c9..000000000000 --- a/internal/debug/loudpanic_fallback.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// +build !go1.6 - -package debug - -// LoudPanic panics in a way that gets all goroutine stacks printed on stderr. -func LoudPanic(x interface{}) { - panic(x) -} diff --git a/internal/debug/trace.go b/internal/debug/trace.go index 74e3a9b22f05..eb5f801c2f1d 100644 --- a/internal/debug/trace.go +++ b/internal/debug/trace.go @@ -14,8 +14,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -//+build go1.5 - package debug import ( diff --git a/internal/debug/trace_fallback.go b/internal/debug/trace_fallback.go deleted file mode 100644 index 4118ff4087ee..000000000000 --- a/internal/debug/trace_fallback.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -//+build !go1.5 - -// no-op implementation of tracing methods for Go < 1.5. - -package debug - -import "errors" - -func (*HandlerT) StartGoTrace(string) error { - return errors.New("tracing is not supported on Go < 1.5") -} - -func (*HandlerT) StopGoTrace() error { - return errors.New("tracing is not supported on Go < 1.5") -} diff --git a/log/handler.go b/log/handler.go index 892cfcc3e1ac..4a0cf578f6cd 100644 --- a/log/handler.go +++ b/log/handler.go @@ -7,6 +7,7 @@ import ( "os" "reflect" "sync" + "sync/atomic" "github.com/go-stack/stack" ) @@ -354,3 +355,21 @@ func (m muster) FileHandler(path string, fmtr Format) Handler { func (m muster) NetHandler(network, addr string, fmtr Format) Handler { return must(NetHandler(network, addr, fmtr)) } + +// swapHandler wraps another handler that may be swapped out +// dynamically at runtime in a thread-safe fashion. +type swapHandler struct { + handler atomic.Value +} + +func (h *swapHandler) Log(r *Record) error { + return (*h.handler.Load().(*Handler)).Log(r) +} + +func (h *swapHandler) Swap(newHandler Handler) { + h.handler.Store(&newHandler) +} + +func (h *swapHandler) Get() Handler { + return *h.handler.Load().(*Handler) +} diff --git a/log/handler_go13.go b/log/handler_go13.go deleted file mode 100644 index 0843ed0e5f38..000000000000 --- a/log/handler_go13.go +++ /dev/null @@ -1,26 +0,0 @@ -// +build !go1.4 - -package log - -import ( - "sync/atomic" - "unsafe" -) - -// swapHandler wraps another handler that may be swapped out -// dynamically at runtime in a thread-safe fashion. -type swapHandler struct { - handler unsafe.Pointer -} - -func (h *swapHandler) Log(r *Record) error { - return h.Get().Log(r) -} - -func (h *swapHandler) Get() Handler { - return *(*Handler)(atomic.LoadPointer(&h.handler)) -} - -func (h *swapHandler) Swap(newHandler Handler) { - atomic.StorePointer(&h.handler, unsafe.Pointer(&newHandler)) -} diff --git a/log/handler_go14.go b/log/handler_go14.go deleted file mode 100644 index 05dedbf2a70e..000000000000 --- a/log/handler_go14.go +++ /dev/null @@ -1,23 +0,0 @@ -// +build go1.4 - -package log - -import "sync/atomic" - -// swapHandler wraps another handler that may be swapped out -// dynamically at runtime in a thread-safe fashion. -type swapHandler struct { - handler atomic.Value -} - -func (h *swapHandler) Log(r *Record) error { - return (*h.handler.Load().(*Handler)).Log(r) -} - -func (h *swapHandler) Swap(newHandler Handler) { - h.handler.Store(&newHandler) -} - -func (h *swapHandler) Get() Handler { - return *h.handler.Load().(*Handler) -} From b04ce3213ad091090fcc97e2981b68f4e384291d Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 220/479] log: test for logging-output (#28373) --- log/format.go | 8 +++ log/format_test.go | 139 --------------------------------------------- 2 files changed, 8 insertions(+), 139 deletions(-) diff --git a/log/format.go b/log/format.go index bce4f439ad16..b7c6baa570b1 100644 --- a/log/format.go +++ b/log/format.go @@ -24,6 +24,14 @@ const ( termCtxMaxPadding = 40 ) +// ResetGlobalState resets the fieldPadding, which is useful for producing +// predictable output. +func ResetGlobalState() { + fieldPaddingLock.Lock() + fieldPadding = make(map[string]int) + fieldPaddingLock.Unlock() +} + // locationTrims are trimmed for display to avoid unwieldy log lines. var locationTrims = []string{ "github.com/XinFinOrg/XDPoSChain/", diff --git a/log/format_test.go b/log/format_test.go index e08c1d1a4a9c..41e1809c38cd 100644 --- a/log/format_test.go +++ b/log/format_test.go @@ -1,105 +1,10 @@ package log import ( - "fmt" - "math" - "math/big" "math/rand" - "strings" "testing" - - "github.com/holiman/uint256" ) -func TestPrettyInt64(t *testing.T) { - tests := []struct { - n int64 - s string - }{ - {0, "0"}, - {10, "10"}, - {-10, "-10"}, - {100, "100"}, - {-100, "-100"}, - {1000, "1000"}, - {-1000, "-1000"}, - {10000, "10000"}, - {-10000, "-10000"}, - {99999, "99999"}, - {-99999, "-99999"}, - {100000, "100,000"}, - {-100000, "-100,000"}, - {1000000, "1,000,000"}, - {-1000000, "-1,000,000"}, - {math.MaxInt64, "9,223,372,036,854,775,807"}, - {math.MinInt64, "-9,223,372,036,854,775,808"}, - } - for i, tt := range tests { - if have := FormatLogfmtInt64(tt.n); have != tt.s { - t.Errorf("test %d: format mismatch: have %s, want %s", i, have, tt.s) - } - } -} - -func TestPrettyUint64(t *testing.T) { - tests := []struct { - n uint64 - s string - }{ - {0, "0"}, - {10, "10"}, - {100, "100"}, - {1000, "1000"}, - {10000, "10000"}, - {99999, "99999"}, - {100000, "100,000"}, - {1000000, "1,000,000"}, - {math.MaxUint64, "18,446,744,073,709,551,615"}, - } - for i, tt := range tests { - if have := FormatLogfmtUint64(tt.n); have != tt.s { - t.Errorf("test %d: format mismatch: have %s, want %s", i, have, tt.s) - } - } -} - -func TestPrettyBigInt(t *testing.T) { - tests := []struct { - int string - s string - }{ - {"111222333444555678999", "111,222,333,444,555,678,999"}, - {"-111222333444555678999", "-111,222,333,444,555,678,999"}, - {"11122233344455567899900", "11,122,233,344,455,567,899,900"}, - {"-11122233344455567899900", "-11,122,233,344,455,567,899,900"}, - } - - for _, tt := range tests { - v, _ := new(big.Int).SetString(tt.int, 10) - if have := formatLogfmtBigInt(v); have != tt.s { - t.Errorf("invalid output %s, want %s", have, tt.s) - } - } -} - -func TestPrettyUint256(t *testing.T) { - tests := []struct { - int string - s string - }{ - {"111222333444555678999", "111,222,333,444,555,678,999"}, - {"11122233344455567899900", "11,122,233,344,455,567,899,900"}, - } - - for _, tt := range tests { - v := new(uint256.Int) - v.SetFromDecimal(tt.int) - if have := formatLogfmtUint256(v); have != tt.s { - t.Errorf("invalid output %s, want %s", have, tt.s) - } - } -} - var sink string func BenchmarkPrettyInt64Logfmt(b *testing.B) { @@ -115,47 +20,3 @@ func BenchmarkPrettyUint64Logfmt(b *testing.B) { sink = FormatLogfmtUint64(rand.Uint64()) } } - -func TestSanitation(t *testing.T) { - msg := "\u001b[1G\u001b[K\u001b[1A" - msg2 := "\u001b \u0000" - msg3 := "NiceMessage" - msg4 := "Space Message" - msg5 := "Enter\nMessage" - - for i, tt := range []struct { - msg string - want string - }{ - { - msg: msg, - want: fmt.Sprintf("] %q %q=%q\n", msg, msg, msg), - }, - { - msg: msg2, - want: fmt.Sprintf("] %q %q=%q\n", msg2, msg2, msg2), - }, - { - msg: msg3, - want: fmt.Sprintf("] %s %s=%s\n", msg3, msg3, msg3), - }, - { - msg: msg4, - want: fmt.Sprintf("] %s %q=%q\n", msg4, msg4, msg4), - }, - { - msg: msg5, - want: fmt.Sprintf("] %s %q=%q\n", msg5, msg5, msg5), - }, - } { - var ( - logger = New() - out = new(strings.Builder) - ) - logger.SetHandler(LvlFilterHandler(LvlInfo, StreamHandler(out, TerminalFormat(false)))) - logger.Info(tt.msg, tt.msg, tt.msg) - if have := out.String()[24:]; tt.want != have { - t.Fatalf("test %d: want / have: \n%v\n%v", i, tt.want, have) - } - } -} From ec4ca1ed6a8050a164b30dfca62ad7ce3793b4d6 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 221/479] all: replace log15 with slog (#28187) --- cmd/XDC/config.go | 8 +- cmd/XDC/main.go | 2 + cmd/bootnode/main.go | 6 +- cmd/evm/main.go | 8 + cmd/evm/runner.go | 18 +- cmd/evm/staterunner.go | 13 +- cmd/faucet/faucet.go | 2 +- cmd/puppeth/puppeth.go | 2 +- cmd/utils/flags.go | 17 + contracts/tests/Inherited_test.go | 7 +- eth/protocol_test.go | 2 +- go.mod | 27 +- go.sum | 176 +--------- internal/debug/api.go | 11 +- internal/debug/flags.go | 203 +++++++++-- internal/testlog/testlog.go | 200 +++++++++++ log/CONTRIBUTORS | 11 - log/LICENSE | 13 - log/README.md | 77 ----- log/README_ETHEREUM.md | 5 - log/doc.go | 333 ------------------ log/format.go | 337 ++++-------------- log/handler.go | 470 +++++++++----------------- log/handler_glog.go | 150 ++++---- log/logger.go | 357 ++++++++----------- log/logger_test.go | 52 ++- log/root.go | 44 ++- log/syslog.go | 57 ---- mobile/init.go | 2 +- mobile/logger.go | 2 +- p2p/server_test.go | 2 +- p2p/simulations/adapters/exec.go | 61 +++- p2p/simulations/adapters/types.go | 33 +- p2p/simulations/examples/ping-pong.go | 2 +- 34 files changed, 999 insertions(+), 1711 deletions(-) create mode 100644 internal/testlog/testlog.go delete mode 100644 log/CONTRIBUTORS delete mode 100644 log/LICENSE delete mode 100644 log/README.md delete mode 100644 log/README_ETHEREUM.md delete mode 100644 log/doc.go delete mode 100644 log/syslog.go diff --git a/cmd/XDC/config.go b/cmd/XDC/config.go index dacc11b497c7..a8ceadb894a6 100644 --- a/cmd/XDC/config.go +++ b/cmd/XDC/config.go @@ -33,8 +33,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/cmd/utils" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/eth/ethconfig" - "github.com/XinFinOrg/XDPoSChain/internal/debug" - "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/node" "github.com/XinFinOrg/XDPoSChain/params" "github.com/naoina/toml" @@ -143,9 +141,9 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, XDCConfig) { if ctx.GlobalIsSet(utils.StakingEnabledFlag.Name) { cfg.StakeEnable = ctx.GlobalBool(utils.StakingEnabledFlag.Name) } - if !ctx.GlobalIsSet(debug.VerbosityFlag.Name) { - debug.Glogger.Verbosity(log.Lvl(cfg.Verbosity)) - } + // if !ctx.GlobalIsSet(debug.VerbosityFlag.Name) { + // debug.Verbosity(log.Lvl(cfg.Verbosity)) + // } if !ctx.GlobalIsSet(utils.NATFlag.Name) && cfg.NAT != "" { ctx.Set(utils.NATFlag.Name, cfg.NAT) diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go index 88d5a4bd7832..57c77e6f29ae 100644 --- a/cmd/XDC/main.go +++ b/cmd/XDC/main.go @@ -136,6 +136,8 @@ var ( utils.GpoIgnoreGasPriceFlag, //utils.ExtraDataFlag, configFileFlag, + utils.LogDebugFlag, + utils.LogBacktraceAtFlag, utils.AnnounceTxsFlag, utils.StoreRewardFlag, utils.RollbackFlag, diff --git a/cmd/bootnode/main.go b/cmd/bootnode/main.go index c78a1a6c5238..eaf91dc51ae2 100644 --- a/cmd/bootnode/main.go +++ b/cmd/bootnode/main.go @@ -51,10 +51,10 @@ func main() { ) flag.Parse() - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.Lvl(*verbosity)) + glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false)) + glogger.Verbosity(log.FromLegacyLevel(*verbosity)) glogger.Vmodule(*vmodule) - log.Root().SetHandler(glogger) + log.SetDefault(log.NewLogger(glogger)) natm, err := nat.Parse(*natdesc) if err != nil { diff --git a/cmd/evm/main.go b/cmd/evm/main.go index b5dfbf528b9d..18de3710d2c5 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -110,6 +110,14 @@ var ( Name: "nostack", Usage: "disable stack output", } + DisableStorageFlag = &cli.BoolFlag{ + Name: "nostorage", + Usage: "disable storage output", + } + DisableReturnDataFlag = &cli.BoolFlag{ + Name: "noreturndata", + Usage: "enable return data output", + } ) func init() { diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 3c9e7ac00f6e..643f53a02c4c 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -22,21 +22,18 @@ import ( "fmt" "io" "os" + goruntime "runtime" "runtime/pprof" "time" - "github.com/XinFinOrg/XDPoSChain/core/rawdb" - - goruntime "runtime" - "github.com/XinFinOrg/XDPoSChain/cmd/evm/internal/compiler" "github.com/XinFinOrg/XDPoSChain/cmd/utils" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/core/vm/runtime" - "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" cli "gopkg.in/urfave/cli.v1" ) @@ -71,12 +68,12 @@ func readGenesis(genesisPath string) *core.Genesis { } func runCmd(ctx *cli.Context) error { - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name))) - log.Root().SetHandler(glogger) logconfig := &vm.LogConfig{ - EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name), - DisableStack: ctx.GlobalBool(DisableStackFlag.Name), + EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name), + DisableStack: ctx.GlobalBool(DisableStackFlag.Name), + DisableStorage: ctx.Bool(DisableStorageFlag.Name), + EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name), + Debug: ctx.Bool(DebugFlag.Name), } var ( @@ -95,6 +92,7 @@ func runCmd(ctx *cli.Context) error { } else { debugLogger = vm.NewStructLogger(logconfig) } + if ctx.GlobalString(GenesisFlag.Name) != "" { gen := readGenesis(ctx.GlobalString(GenesisFlag.Name)) db := rawdb.NewMemoryDatabase() diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go index 1a236c0001ee..891d6b1e6664 100644 --- a/cmd/evm/staterunner.go +++ b/cmd/evm/staterunner.go @@ -24,9 +24,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/vm" - "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/tests" - cli "gopkg.in/urfave/cli.v1" ) @@ -49,16 +47,15 @@ func stateTestCmd(ctx *cli.Context) error { if len(ctx.Args().First()) == 0 { return errors.New("path-to-test argument required") } - // Configure the go-ethereum logger - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name))) - log.Root().SetHandler(glogger) // Configure the EVM logger config := &vm.LogConfig{ - EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name), - DisableStack: ctx.GlobalBool(DisableStackFlag.Name), + EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name), + DisableStack: ctx.GlobalBool(DisableStackFlag.Name), + DisableStorage: ctx.Bool(DisableStorageFlag.Name), + EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name), } + var ( tracer vm.EVMLogger debugger *vm.StructLogger diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go index 050fc7f80bc9..dcee12c32b4c 100644 --- a/cmd/faucet/faucet.go +++ b/cmd/faucet/faucet.go @@ -94,7 +94,7 @@ var ( func main() { // Parse the flags and set up the logger to print everything requested flag.Parse() - log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*logFlag), log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) + log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.FromLegacyLevel(*logFlag), true))) // Construct the payout tiers amounts := make([]string, *tiersFlag) diff --git a/cmd/puppeth/puppeth.go b/cmd/puppeth/puppeth.go index 5fa0addb2110..51a60e0cb5a6 100644 --- a/cmd/puppeth/puppeth.go +++ b/cmd/puppeth/puppeth.go @@ -45,7 +45,7 @@ func main() { } app.Action = func(c *cli.Context) error { // Set up the logger to print everything and the random generator - log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int("loglevel")), log.StreamHandler(os.Stdout, log.TerminalFormat(true)))) + log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stdout, log.FromLegacyLevel(c.Int("loglevel")), true))) rand.Seed(time.Now().UnixNano()) network := c.String("network") diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index ef937b9d727f..700695516d90 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -618,6 +618,16 @@ var ( Name: "slave", Usage: "Enable slave mode", } + // Deprecated November 2023 + LogBacktraceAtFlag = &cli.StringFlag{ + Name: "log-backtrace", + Usage: "Request a stack trace at a specific logging statement (deprecated)", + Value: "", + } + LogDebugFlag = &cli.BoolFlag{ + Name: "log-debug", + Usage: "Prepends log messages with call-site location (deprecated)", + } ) // MakeDataDir retrieves the currently requested data directory, terminating @@ -1015,6 +1025,13 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) { if ctx.GlobalIsSet(AnnounceTxsFlag.Name) { cfg.AnnounceTxs = ctx.GlobalBool(AnnounceTxsFlag.Name) } + // deprecation notice for log debug flags (TODO: find a more appropriate place to put these?) + if ctx.IsSet(LogBacktraceAtFlag.Name) { + log.Warn("log.backtrace flag is deprecated") + } + if ctx.IsSet(LogDebugFlag.Name) { + log.Warn("log.debug flag is deprecated") + } } func setGPO(ctx *cli.Context, cfg *gasprice.Config, light bool) { diff --git a/contracts/tests/Inherited_test.go b/contracts/tests/Inherited_test.go index a1e4a47203a8..94298fc0e3b4 100644 --- a/contracts/tests/Inherited_test.go +++ b/contracts/tests/Inherited_test.go @@ -19,9 +19,10 @@ var ( ) func TestPriceFeed(t *testing.T) { - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.LvlTrace) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false)) + glogger.Verbosity(log.LevelTrace) + log.SetDefault(log.NewLogger(glogger)) + common.TIPXDCXCancellationFee = big.NewInt(0) // init genesis contractBackend := backends.NewSimulatedBackend(core.GenesisAlloc{ diff --git a/eth/protocol_test.go b/eth/protocol_test.go index 8c5283cd8b18..858e1da5aab7 100644 --- a/eth/protocol_test.go +++ b/eth/protocol_test.go @@ -32,7 +32,7 @@ import ( ) func init() { - // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) + // log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, false))) } var testAccount, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") diff --git a/go.mod b/go.mod index 9e5d403aa9a5..a519dba12ac1 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/XinFinOrg/XDPoSChain go 1.21 require ( - bazil.org/fuse v0.0.0-20180421153158-65cc252bf669 github.com/VictoriaMetrics/fastcache v1.12.2 github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98 github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 @@ -12,9 +11,7 @@ require ( github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf github.com/edsrzf/mmap-go v1.0.0 github.com/fatih/color v1.13.0 - github.com/gizak/termui v2.2.0+incompatible github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 - github.com/go-stack/stack v1.8.1 github.com/golang/protobuf v1.5.3 github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb github.com/gorilla/websocket v1.4.2 @@ -38,7 +35,6 @@ require ( github.com/stretchr/testify v1.8.4 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 golang.org/x/crypto v0.15.0 - golang.org/x/net v0.17.0 golang.org/x/sync v0.4.0 golang.org/x/sys v0.24.0 golang.org/x/tools v0.14.0 @@ -48,37 +44,32 @@ require ( gopkg.in/urfave/cli.v1 v1.20.0 ) -require github.com/deckarep/golang-set v1.8.0 +require ( + github.com/deckarep/golang-set v1.8.0 + github.com/dop251/goja v0.0.0-20200106141417-aaec0e7bde29 + github.com/kylelemons/godebug v1.1.0 + github.com/mattn/go-isatty v0.0.17 + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible + golang.org/x/exp v0.0.0-20231006140011-7918f672742d + gopkg.in/natefinch/lumberjack.v2 v2.2.1 +) require ( github.com/StackExchange/wmi v1.2.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/dlclark/regexp2 v1.10.0 // indirect - github.com/dop251/goja v0.0.0-20200106141417-aaec0e7bde29 // indirect - github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa // indirect github.com/go-ole/go-ole v1.2.5 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect github.com/google/uuid v1.3.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect - github.com/kylelemons/godebug v1.1.0 // indirect - github.com/maruel/panicparse v0.0.0-20160720141634-ad661195ed0e // indirect - github.com/maruel/ut v1.0.2 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect - github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect github.com/naoina/go-stringutil v0.1.0 // indirect - github.com/nsf/termbox-go v0.0.0-20170211012700-3540b76b9c77 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect - github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect - github.com/tklauser/go-sysconf v0.3.12 // indirect - github.com/tklauser/numcpus v0.6.1 // indirect - golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/mod v0.13.0 // indirect golang.org/x/term v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect diff --git a/go.sum b/go.sum index 98d3a63aed14..d21c1f26a13a 100644 --- a/go.sum +++ b/go.sum @@ -1,113 +1,44 @@ -bazil.org/fuse v0.0.0-20180421153158-65cc252bf669 h1:FNCRpXiquG1aoyqcIWVFmpTSKVcx2bQD38uZZeGtdlw= -bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= -github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= -github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= -github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= -github.com/VictoriaMetrics/fastcache v1.5.3/go.mod h1:+jv9Ckb+za/P1ZRg/sulP5Ni1v49daAVERr0H3CuscE= -github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= -github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98 h1:7buXGE+m4OPjyo8rUJgA8RmARNMq+m99JJLR+Z+ZWN0= github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98/go.mod h1:DLTg9Gp4FAXF5EpqYBQnUeBbRsNLY7b2HR94TE5XQtE= -github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 h1:Eey/GGQ/E5Xp1P2Lyx1qj007hLZfbi0+CoVeJruGCtI= github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= -github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.0.1-0.20190104013014-3767db7a7e18/go.mod h1:HD5P3vAIAh+Y2GAxg0PrPN1P8WkepXGpjbUPDHJqqKM= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= -github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= -github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf h1:sh8rkQZavChcmakYiSlqu2425CHyFXLZZnvm7PDpU8M= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/dop251/goja v0.0.0-20200106141417-aaec0e7bde29 h1:Ewd9K+mC725sITA12QQHRqWj78NU4t7EhlFVVgdlzJg= github.com/dop251/goja v0.0.0-20200106141417-aaec0e7bde29/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= -github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= -github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 h1:qwcF+vdFrvPSEUDSX5RVoRccG8a5DhOdWdQ4zN62zzo= -github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= -github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= -github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= -github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa h1:XKAhUk/dtp+CV0VO6mhG2V7jA9vbcGcnYF/Ay9NjZrY= -github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= -github.com/ethereum/go-ethereum v1.13.5 h1:U6TCRciCqZRe4FPXmy1sMGxTfuk8P7u2UoinF3VbaFk= -github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0= -github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/gizak/termui v2.2.0+incompatible h1:qvZU9Xll/Xd/Xr/YO+HfBKXhy8a8/94ao6vV9DSXzUE= -github.com/gizak/termui v2.2.0+incompatible/go.mod h1:PkJoWUt/zacQKysNfQtcw1RW+eK2SxkieVBtl+4ovLA= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= -github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2-0.20190517061210-b285ee9cfc6c/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -117,7 +48,6 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -125,48 +55,29 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U= -github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= -github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= -github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= -github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= -github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= github.com/influxdata/influxdb v1.7.9 h1:uSeBTNO4rBkbp1Be5FKRsAmglM9nlx25TzVQRQt1An4= github.com/influxdata/influxdb v1.7.9/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= -github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/karalabe/hid v1.0.0 h1:+/CIMNXhSU/zIJgnIvBD2nKHxS/bnRHhhs9xBryLpPo= github.com/karalabe/hid v1.0.0/go.mod h1:Vr51f8rUOLYrfrWDFlV12GGQgM5AT8sVh+2fY4MPeu8= -github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -175,145 +86,86 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/maruel/panicparse v0.0.0-20160720141634-ad661195ed0e h1:e2z/lz9pvtRrEOgKWaLW2Dw02Nqd3/fqv0qWTQ8ByZE= -github.com/maruel/panicparse v0.0.0-20160720141634-ad661195ed0e/go.mod h1:nty42YY5QByNC5MM7q/nj938VbgPU7avs45z6NClpxI= -github.com/maruel/ut v1.0.2 h1:mQTlQk3jubTbdTcza+hwoZQWhzcvE4L6K6RTtAFlA1k= -github.com/maruel/ut v1.0.2/go.mod h1:RV8PwPD9dd2KFlnlCc/DB2JVvkXmyaalfc5xvmSrRSs= -github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= -github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= -github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= -github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks= github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0= github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= -github.com/nsf/termbox-go v0.0.0-20170211012700-3540b76b9c77 h1:gKl78uP/I7JZ56OFtRf7nc4m1icV38hwV0In5pEGzeA= -github.com/nsf/termbox-go v0.0.0-20170211012700-3540b76b9c77/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/prometheus v1.7.2-0.20170814170113-3101606756c5 h1:K2PKeDFZidfjUWpXk05Gbxhwm8Rnz1l4O+u/bbbcCvc= github.com/prometheus/prometheus v1.7.2-0.20170814170113-3101606756c5/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s= -github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8= github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spaolacci/murmur3 v1.0.1-0.20190317074736-539464a789e9/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE= github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw= github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639dk2kqnCPPv+wNjq7Xb6EfUxe/oX0/NM= github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= -github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= -github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= -github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -323,39 +175,21 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -369,24 +203,20 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772 h1:hhsSf/5z74Ck/DJYc+R8zpq8KGm7uJvpdLRQED/IedA= gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= -gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/internal/debug/api.go b/internal/debug/api.go index 4702f7907667..6a11bfded457 100644 --- a/internal/debug/api.go +++ b/internal/debug/api.go @@ -35,6 +35,7 @@ import ( "time" "github.com/XinFinOrg/XDPoSChain/log" + "golang.org/x/exp/slog" ) // Handler is the global debugging handler. @@ -55,19 +56,13 @@ type HandlerT struct { // Verbosity sets the log verbosity ceiling. The verbosity of individual packages // and source files can be raised using Vmodule. func (*HandlerT) Verbosity(level int) { - Glogger.Verbosity(log.Lvl(level)) + glogger.Verbosity(slog.Level(level)) } // Vmodule sets the log verbosity pattern. See package log for details on the // pattern syntax. func (*HandlerT) Vmodule(pattern string) error { - return Glogger.Vmodule(pattern) -} - -// BacktraceAt sets the log backtrace location. See package log for details on -// the pattern syntax. -func (*HandlerT) BacktraceAt(location string) error { - return Glogger.BacktraceAt(location) + return glogger.Vmodule(pattern) } // MemStats returns detailed runtime memory statistics. diff --git a/internal/debug/flags.go b/internal/debug/flags.go index 57738d60e208..7e266847c766 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -22,35 +22,70 @@ import ( "net/http" _ "net/http/pprof" "os" + "path/filepath" "runtime" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/metrics" "github.com/XinFinOrg/XDPoSChain/metrics/exp" - colorable "github.com/mattn/go-colorable" + "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" + "golang.org/x/exp/slog" + "gopkg.in/natefinch/lumberjack.v2" "gopkg.in/urfave/cli.v1" ) var ( - VerbosityFlag = cli.IntFlag{ + verbosityFlag = cli.IntFlag{ Name: "verbosity", Usage: "Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail", Value: 3, } + logVmoduleFlag = &cli.StringFlag{ + Name: "log-vmodule", + Usage: "Per-module verbosity: comma-separated list of = (e.g. eth/*=5,p2p=4)", + Value: "", + } vmoduleFlag = cli.StringFlag{ Name: "vmodule", Usage: "Per-module verbosity: comma-separated list of = (e.g. eth/*=5,p2p=4)", Value: "", } - backtraceAtFlag = cli.StringFlag{ - Name: "backtrace", - Usage: "Request a stack trace at a specific logging statement (e.g. \"block.go:271\")", - Value: "", + logjsonFlag = &cli.BoolFlag{ + Name: "log-json", + Usage: "Format logs with JSON", + Hidden: true, + } + logFormatFlag = &cli.StringFlag{ + Name: "log-format", + Usage: "Log format to use (json|logfmt|terminal)", + } + logFileFlag = &cli.StringFlag{ + Name: "log-file", + Usage: "Write logs to a file", + } + logRotateFlag = &cli.BoolFlag{ + Name: "log-rotate", + Usage: "Enables log file rotation", + } + logMaxSizeMBsFlag = &cli.IntFlag{ + Name: "log-maxsize", + Usage: "Maximum size in MBs of a single log file", + Value: 100, + } + logMaxBackupsFlag = &cli.IntFlag{ + Name: "log-maxbackups", + Usage: "Maximum number of log files to retain", + Value: 10, + } + logMaxAgeFlag = &cli.IntFlag{ + Name: "log-maxage", + Usage: "Maximum number of days to retain a log file", + Value: 30, } - debugFlag = cli.BoolFlag{ - Name: "debug", - Usage: "Prepends log messages with call-site location (file and line number)", + logCompressFlag = &cli.BoolFlag{ + Name: "log-compress", + Usage: "Compress the log files", } pprofFlag = cli.BoolFlag{ Name: "pprof", @@ -95,10 +130,17 @@ var ( // Flags holds all command-line flags required for debugging. var Flags = []cli.Flag{ - VerbosityFlag, - //vmoduleFlag, - //backtraceAtFlag, - debugFlag, + verbosityFlag, + logVmoduleFlag, + vmoduleFlag, + logjsonFlag, + logFormatFlag, + logFileFlag, + logRotateFlag, + logMaxSizeMBsFlag, + logMaxBackupsFlag, + logMaxAgeFlag, + logCompressFlag, pprofFlag, pprofAddrFlag, pprofPortFlag, @@ -110,26 +152,117 @@ var Flags = []cli.Flag{ debugDataDirFlag, } -var Glogger *log.GlogHandler +var ( + glogger *log.GlogHandler + logOutputFile io.WriteCloser + defaultTerminalHandler *log.TerminalHandler +) func init() { - usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" - output := io.Writer(os.Stderr) - if usecolor { - output = colorable.NewColorableStderr() + defaultTerminalHandler = log.NewTerminalHandler(os.Stderr, false) + glogger = log.NewGlogHandler(defaultTerminalHandler) + glogger.Verbosity(log.LvlInfo) + log.SetDefault(log.NewLogger(glogger)) +} + +func ResetLogging() { + if defaultTerminalHandler != nil { + defaultTerminalHandler.ResetFieldPadding() } - Glogger = log.NewGlogHandler(log.StreamHandler(output, log.TerminalFormat(usecolor))) } // Setup initializes profiling and logging based on the CLI flags. // It should be called as early as possible in the program. func Setup(ctx *cli.Context) error { + var ( + handler slog.Handler + terminalOutput = io.Writer(os.Stderr) + output io.Writer + logFmtFlag = ctx.String(logFormatFlag.Name) + ) + var ( + logFile = ctx.String(logFileFlag.Name) + rotation = ctx.Bool(logRotateFlag.Name) + ) + if len(logFile) > 0 { + if err := validateLogLocation(filepath.Dir(logFile)); err != nil { + return fmt.Errorf("failed to initiatilize file logger: %v", err) + } + } + context := []interface{}{"rotate", rotation} + if len(logFmtFlag) > 0 { + context = append(context, "format", logFmtFlag) + } else { + context = append(context, "format", "terminal") + } + if rotation { + // Lumberjack uses -lumberjack.log in is.TempDir() if empty. + // so typically /tmp/geth-lumberjack.log on linux + if len(logFile) > 0 { + context = append(context, "location", logFile) + } else { + context = append(context, "location", filepath.Join(os.TempDir(), "geth-lumberjack.log")) + } + logOutputFile = &lumberjack.Logger{ + Filename: logFile, + MaxSize: ctx.Int(logMaxSizeMBsFlag.Name), + MaxBackups: ctx.Int(logMaxBackupsFlag.Name), + MaxAge: ctx.Int(logMaxAgeFlag.Name), + Compress: ctx.Bool(logCompressFlag.Name), + } + output = io.MultiWriter(terminalOutput, logOutputFile) + } else if logFile != "" { + var err error + if logOutputFile, err = os.OpenFile(logFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644); err != nil { + return err + } + output = io.MultiWriter(logOutputFile, terminalOutput) + context = append(context, "location", logFile) + } else { + output = terminalOutput + } + + switch { + case ctx.Bool(logjsonFlag.Name): + // Retain backwards compatibility with `--log-json` flag if `--log-format` not set + defer log.Warn("The flag '--log-json' is deprecated, please use '--log-format=json' instead") + handler = log.JSONHandler(output) + case logFmtFlag == "json": + handler = log.JSONHandler(output) + case logFmtFlag == "logfmt": + handler = log.LogfmtHandler(output) + case logFmtFlag == "", logFmtFlag == "terminal": + useColor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" + if useColor { + terminalOutput = colorable.NewColorableStderr() + if logOutputFile != nil { + output = io.MultiWriter(logOutputFile, terminalOutput) + } else { + output = terminalOutput + } + } + handler = log.NewTerminalHandler(output, useColor) + default: + // Unknown log format specified + return fmt.Errorf("unknown log format: %v", ctx.String(logFormatFlag.Name)) + } + + glogger = log.NewGlogHandler(handler) + // logging - log.PrintOrigins(ctx.GlobalBool(debugFlag.Name)) - Glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name))) - Glogger.Vmodule(ctx.GlobalString(vmoduleFlag.Name)) - Glogger.BacktraceAt(ctx.GlobalString(backtraceAtFlag.Name)) - log.Root().SetHandler(Glogger) + verbosity := log.FromLegacyLevel(ctx.Int(verbosityFlag.Name)) + glogger.Verbosity(verbosity) + vmodule := ctx.String(logVmoduleFlag.Name) + if vmodule == "" { + // Retain backwards compatibility with `--vmodule` flag if `--log-vmodule` not set + vmodule = ctx.String(vmoduleFlag.Name) + if vmodule != "" { + defer log.Warn("The flag '--vmodule' is deprecated, please use '--log-vmodule' instead") + } + } + glogger.Vmodule(vmodule) + + log.SetDefault(log.NewLogger(glogger)) // profiling, tracing runtime.MemProfileRate = ctx.GlobalInt(memprofilerateFlag.Name) @@ -164,6 +297,11 @@ func Setup(ctx *cli.Context) error { } }() } + + if len(logFile) > 0 || rotation { + log.Info("Logging configured", context...) + } + return nil } @@ -172,4 +310,21 @@ func Setup(ctx *cli.Context) error { func Exit() { Handler.StopCPUProfile() Handler.StopGoTrace() + if logOutputFile != nil { + logOutputFile.Close() + } +} + +func validateLogLocation(path string) error { + if err := os.MkdirAll(path, os.ModePerm); err != nil { + return fmt.Errorf("error creating the directory: %w", err) + } + // Check if the path is writable by trying to create a temporary file + tmp := filepath.Join(path, "tmp") + if f, err := os.Create(tmp); err != nil { + return err + } else { + f.Close() + } + return os.Remove(tmp) } diff --git a/internal/testlog/testlog.go b/internal/testlog/testlog.go new file mode 100644 index 000000000000..426f110a41dd --- /dev/null +++ b/internal/testlog/testlog.go @@ -0,0 +1,200 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package testlog provides a log handler for unit tests. +package testlog + +import ( + "bytes" + "context" + "fmt" + "sync" + "testing" + + "github.com/XinFinOrg/XDPoSChain/log" + "golang.org/x/exp/slog" +) + +const ( + termTimeFormat = "01-02|15:04:05.000" +) + +// logger implements log.Logger such that all output goes to the unit test log via +// t.Logf(). All methods in between logger.Trace, logger.Debug, etc. are marked as test +// helpers, so the file and line number in unit test output correspond to the call site +// which emitted the log message. +type logger struct { + t *testing.T + l log.Logger + mu *sync.Mutex + h *bufHandler +} + +type bufHandler struct { + buf []slog.Record + attrs []slog.Attr + level slog.Level +} + +func (h *bufHandler) Handle(_ context.Context, r slog.Record) error { + h.buf = append(h.buf, r) + return nil +} + +func (h *bufHandler) Enabled(_ context.Context, lvl slog.Level) bool { + return lvl <= h.level +} + +func (h *bufHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + records := make([]slog.Record, len(h.buf)) + copy(records[:], h.buf[:]) + return &bufHandler{ + records, + append(h.attrs, attrs...), + h.level, + } +} + +func (h *bufHandler) WithGroup(_ string) slog.Handler { + panic("not implemented") +} + +// Logger returns a logger which logs to the unit test log of t. +func Logger(t *testing.T, level slog.Level) log.Logger { + handler := bufHandler{ + []slog.Record{}, + []slog.Attr{}, + level, + } + return &logger{ + t: t, + l: log.NewLogger(&handler), + mu: new(sync.Mutex), + h: &handler, + } +} + +// LoggerWithHandler returns +func LoggerWithHandler(t *testing.T, handler slog.Handler) log.Logger { + var bh bufHandler + return &logger{ + t: t, + l: log.NewLogger(handler), + mu: new(sync.Mutex), + h: &bh, + } +} + +func (l *logger) Write(level slog.Level, msg string, ctx ...interface{}) {} + +func (l *logger) Trace(msg string, ctx ...interface{}) { + l.t.Helper() + l.mu.Lock() + defer l.mu.Unlock() + l.l.Trace(msg, ctx...) + l.flush() +} + +func (l *logger) Log(level slog.Level, msg string, ctx ...interface{}) { + l.t.Helper() + l.mu.Lock() + defer l.mu.Unlock() + l.l.Log(level, msg, ctx...) + l.flush() +} + +func (l *logger) Debug(msg string, ctx ...interface{}) { + l.t.Helper() + l.mu.Lock() + defer l.mu.Unlock() + l.l.Debug(msg, ctx...) + l.flush() +} + +func (l *logger) Info(msg string, ctx ...interface{}) { + l.t.Helper() + l.mu.Lock() + defer l.mu.Unlock() + l.l.Info(msg, ctx...) + l.flush() +} + +func (l *logger) Warn(msg string, ctx ...interface{}) { + l.t.Helper() + l.mu.Lock() + defer l.mu.Unlock() + l.l.Warn(msg, ctx...) + l.flush() +} + +func (l *logger) Error(msg string, ctx ...interface{}) { + l.t.Helper() + l.mu.Lock() + defer l.mu.Unlock() + l.l.Error(msg, ctx...) + l.flush() +} + +func (l *logger) Crit(msg string, ctx ...interface{}) { + l.t.Helper() + l.mu.Lock() + defer l.mu.Unlock() + l.l.Crit(msg, ctx...) + l.flush() +} + +func (l *logger) With(ctx ...interface{}) log.Logger { + return &logger{l.t, l.l.With(ctx...), l.mu, l.h} +} + +func (l *logger) New(ctx ...interface{}) log.Logger { + return l.With(ctx...) +} + +// terminalFormat formats a message similarly to the NewTerminalHandler in the log package. +// The difference is that terminalFormat does not escape messages/attributes and does not pad attributes. +func (h *bufHandler) terminalFormat(r slog.Record) string { + buf := &bytes.Buffer{} + lvl := log.LevelAlignedString(r.Level) + attrs := []slog.Attr{} + r.Attrs(func(attr slog.Attr) bool { + attrs = append(attrs, attr) + return true + }) + + attrs = append(h.attrs, attrs...) + + fmt.Fprintf(buf, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), r.Message) + if length := len(r.Message); length < 40 { + buf.Write(bytes.Repeat([]byte{' '}, 40-length)) + } + + for _, attr := range attrs { + rawVal := attr.Value.Any() + fmt.Fprintf(buf, " %s=%s", attr.Key, log.FormatLogfmtValue(rawVal, true)) + } + buf.WriteByte('\n') + return buf.String() +} + +// flush writes all buffered messages and clears the buffer. +func (l *logger) flush() { + l.t.Helper() + for _, r := range l.h.buf { + l.t.Logf("%s", l.h.terminalFormat(r)) + } + l.h.buf = nil +} diff --git a/log/CONTRIBUTORS b/log/CONTRIBUTORS deleted file mode 100644 index a0866713be09..000000000000 --- a/log/CONTRIBUTORS +++ /dev/null @@ -1,11 +0,0 @@ -Contributors to log15: - -- Aaron L -- Alan Shreve -- Chris Hines -- Ciaran Downey -- Dmitry Chestnykh -- Evan Shaw -- Péter Szilágyi -- Trevor Gattis -- Vincent Vanackere diff --git a/log/LICENSE b/log/LICENSE deleted file mode 100644 index 5f0d1fb6a7bb..000000000000 --- a/log/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright 2014 Alan Shreve - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/log/README.md b/log/README.md deleted file mode 100644 index 47426806dd95..000000000000 --- a/log/README.md +++ /dev/null @@ -1,77 +0,0 @@ -![obligatory xkcd](https://imgs.xkcd.com/comics/standards.png) - -# log15 [![godoc reference](https://godoc.org/github.com/inconshreveable/log15?status.png)](https://godoc.org/github.com/inconshreveable/log15) [![Build Status](https://travis-ci.org/inconshreveable/log15.svg?branch=master)](https://travis-ci.org/inconshreveable/log15) - -Package log15 provides an opinionated, simple toolkit for best-practice logging in Go (golang) that is both human and machine readable. It is modeled after the Go standard library's [`io`](https://golang.org/pkg/io/) and [`net/http`](https://golang.org/pkg/net/http/) packages and is an alternative to the standard library's [`log`](https://golang.org/pkg/log/) package. - -## Features -- A simple, easy-to-understand API -- Promotes structured logging by encouraging use of key/value pairs -- Child loggers which inherit and add their own private context -- Lazy evaluation of expensive operations -- Simple Handler interface allowing for construction of flexible, custom logging configurations with a tiny API. -- Color terminal support -- Built-in support for logging to files, streams, syslog, and the network -- Support for forking records to multiple handlers, buffering records for output, failing over from failed handler writes, + more - -## Versioning -The API of the master branch of log15 should always be considered unstable. If you want to rely on a stable API, -you must vendor the library. - -## Importing - -```go -import log "github.com/inconshreveable/log15" -``` - -## Examples - -```go -// all loggers can have key/value context -srvlog := log.New("module", "app/server") - -// all log messages can have key/value context -srvlog.Warn("abnormal conn rate", "rate", curRate, "low", lowRate, "high", highRate) - -// child loggers with inherited context -connlog := srvlog.New("raddr", c.RemoteAddr()) -connlog.Info("connection open") - -// lazy evaluation -connlog.Debug("ping remote", "latency", log.Lazy{pingRemote}) - -// flexible configuration -srvlog.SetHandler(log.MultiHandler( - log.StreamHandler(os.Stderr, log.LogfmtFormat()), - log.LvlFilterHandler( - log.LvlError, - log.Must.FileHandler("errors.json", log.JSONFormat())))) -``` - -Will result in output that looks like this: - -``` -WARN[06-17|21:58:10] abnormal conn rate module=app/server rate=0.500 low=0.100 high=0.800 -INFO[06-17|21:58:10] connection open module=app/server raddr=10.0.0.1 -``` - -## Breaking API Changes -The following commits broke API stability. This reference is intended to help you understand the consequences of updating to a newer version -of log15. - -- 57a084d014d4150152b19e4e531399a7145d1540 - Added a `Get()` method to the `Logger` interface to retrieve the current handler -- 93404652ee366648fa622b64d1e2b67d75a3094a - `Record` field `Call` changed to `stack.Call` with switch to `github.com/go-stack/stack` -- a5e7613673c73281f58e15a87d2cf0cf111e8152 - Restored `syslog.Priority` argument to the `SyslogXxx` handler constructors - -## FAQ - -### The varargs style is brittle and error prone! Can I have type safety please? -Yes. Use `log.Ctx`: - -```go -srvlog := log.New(log.Ctx{"module": "app/server"}) -srvlog.Warn("abnormal conn rate", log.Ctx{"rate": curRate, "low": lowRate, "high": highRate}) -``` - -## License -Apache diff --git a/log/README_ETHEREUM.md b/log/README_ETHEREUM.md deleted file mode 100644 index f6c42ccc03da..000000000000 --- a/log/README_ETHEREUM.md +++ /dev/null @@ -1,5 +0,0 @@ -This package is a fork of https://github.com/inconshreveable/log15, with some -minor modifications required by the go-ethereum codebase: - - * Support for log level `trace` - * Modified behavior to exit on `critical` failure diff --git a/log/doc.go b/log/doc.go deleted file mode 100644 index 993743c0fd5c..000000000000 --- a/log/doc.go +++ /dev/null @@ -1,333 +0,0 @@ -/* -Package log15 provides an opinionated, simple toolkit for best-practice logging that is -both human and machine readable. It is modeled after the standard library's io and net/http -packages. - -This package enforces you to only log key/value pairs. Keys must be strings. Values may be -any type that you like. The default output format is logfmt, but you may also choose to use -JSON instead if that suits you. Here's how you log: - - log.Info("page accessed", "path", r.URL.Path, "user_id", user.id) - -This will output a line that looks like: - - lvl=info t=2014-05-02T16:07:23-0700 msg="page accessed" path=/org/71/profile user_id=9 - -Getting Started - -To get started, you'll want to import the library: - - import log "github.com/inconshreveable/log15" - - -Now you're ready to start logging: - - func main() { - log.Info("Program starting", "args", os.Args()) - } - - -Convention - -Because recording a human-meaningful message is common and good practice, the first argument to every -logging method is the value to the *implicit* key 'msg'. - -Additionally, the level you choose for a message will be automatically added with the key 'lvl', and so -will the current timestamp with key 't'. - -You may supply any additional context as a set of key/value pairs to the logging function. log15 allows -you to favor terseness, ordering, and speed over safety. This is a reasonable tradeoff for -logging functions. You don't need to explicitly state keys/values, log15 understands that they alternate -in the variadic argument list: - - log.Warn("size out of bounds", "low", lowBound, "high", highBound, "val", val) - -If you really do favor your type-safety, you may choose to pass a log.Ctx instead: - - log.Warn("size out of bounds", log.Ctx{"low": lowBound, "high": highBound, "val": val}) - - -Context loggers - -Frequently, you want to add context to a logger so that you can track actions associated with it. An http -request is a good example. You can easily create new loggers that have context that is automatically included -with each log line: - - requestlogger := log.New("path", r.URL.Path) - - // later - requestlogger.Debug("db txn commit", "duration", txnTimer.Finish()) - -This will output a log line that includes the path context that is attached to the logger: - - lvl=dbug t=2014-05-02T16:07:23-0700 path=/repo/12/add_hook msg="db txn commit" duration=0.12 - - -Handlers - -The Handler interface defines where log lines are printed to and how they are formatted. Handler is a -single interface that is inspired by net/http's handler interface: - - type Handler interface { - Log(r *Record) error - } - - -Handlers can filter records, format them, or dispatch to multiple other Handlers. -This package implements a number of Handlers for common logging patterns that are -easily composed to create flexible, custom logging structures. - -Here's an example handler that prints logfmt output to Stdout: - - handler := log.StreamHandler(os.Stdout, log.LogfmtFormat()) - -Here's an example handler that defers to two other handlers. One handler only prints records -from the rpc package in logfmt to standard out. The other prints records at Error level -or above in JSON formatted output to the file /var/log/service.json - - handler := log.MultiHandler( - log.LvlFilterHandler(log.LvlError, log.Must.FileHandler("/var/log/service.json", log.JSONFormat())), - log.MatchFilterHandler("pkg", "app/rpc" log.StdoutHandler()) - ) - -Logging File Names and Line Numbers - -This package implements three Handlers that add debugging information to the -context, CallerFileHandler, CallerFuncHandler and CallerStackHandler. Here's -an example that adds the source file and line number of each logging call to -the context. - - h := log.CallerFileHandler(log.StdoutHandler) - log.Root().SetHandler(h) - ... - log.Error("open file", "err", err) - -This will output a line that looks like: - - lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" caller=data.go:42 - -Here's an example that logs the call stack rather than just the call site. - - h := log.CallerStackHandler("%+v", log.StdoutHandler) - log.Root().SetHandler(h) - ... - log.Error("open file", "err", err) - -This will output a line that looks like: - - lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" stack="[pkg/data.go:42 pkg/cmd/main.go]" - -The "%+v" format instructs the handler to include the path of the source file -relative to the compile time GOPATH. The github.com/go-stack/stack package -documents the full list of formatting verbs and modifiers available. - -Custom Handlers - -The Handler interface is so simple that it's also trivial to write your own. Let's create an -example handler which tries to write to one handler, but if that fails it falls back to -writing to another handler and includes the error that it encountered when trying to write -to the primary. This might be useful when trying to log over a network socket, but if that -fails you want to log those records to a file on disk. - - type BackupHandler struct { - Primary Handler - Secondary Handler - } - - func (h *BackupHandler) Log (r *Record) error { - err := h.Primary.Log(r) - if err != nil { - r.Ctx = append(ctx, "primary_err", err) - return h.Secondary.Log(r) - } - return nil - } - -This pattern is so useful that a generic version that handles an arbitrary number of Handlers -is included as part of this library called FailoverHandler. - -Logging Expensive Operations - -Sometimes, you want to log values that are extremely expensive to compute, but you don't want to pay -the price of computing them if you haven't turned up your logging level to a high level of detail. - -This package provides a simple type to annotate a logging operation that you want to be evaluated -lazily, just when it is about to be logged, so that it would not be evaluated if an upstream Handler -filters it out. Just wrap any function which takes no arguments with the log.Lazy type. For example: - - func factorRSAKey() (factors []int) { - // return the factors of a very large number - } - - log.Debug("factors", log.Lazy{factorRSAKey}) - -If this message is not logged for any reason (like logging at the Error level), then -factorRSAKey is never evaluated. - -Dynamic context values - -The same log.Lazy mechanism can be used to attach context to a logger which you want to be -evaluated when the message is logged, but not when the logger is created. For example, let's imagine -a game where you have Player objects: - - type Player struct { - name string - alive bool - log.Logger - } - -You always want to log a player's name and whether they're alive or dead, so when you create the player -object, you might do: - - p := &Player{name: name, alive: true} - p.Logger = log.New("name", p.name, "alive", p.alive) - -Only now, even after a player has died, the logger will still report they are alive because the logging -context is evaluated when the logger was created. By using the Lazy wrapper, we can defer the evaluation -of whether the player is alive or not to each log message, so that the log records will reflect the player's -current state no matter when the log message is written: - - p := &Player{name: name, alive: true} - isAlive := func() bool { return p.alive } - player.Logger = log.New("name", p.name, "alive", log.Lazy{isAlive}) - -Terminal Format - -If log15 detects that stdout is a terminal, it will configure the default -handler for it (which is log.StdoutHandler) to use TerminalFormat. This format -logs records nicely for your terminal, including color-coded output based -on log level. - -Error Handling - -Becasuse log15 allows you to step around the type system, there are a few ways you can specify -invalid arguments to the logging functions. You could, for example, wrap something that is not -a zero-argument function with log.Lazy or pass a context key that is not a string. Since logging libraries -are typically the mechanism by which errors are reported, it would be onerous for the logging functions -to return errors. Instead, log15 handles errors by making these guarantees to you: - -- Any log record containing an error will still be printed with the error explained to you as part of the log record. - -- Any log record containing an error will include the context key LOG15_ERROR, enabling you to easily -(and if you like, automatically) detect if any of your logging calls are passing bad values. - -Understanding this, you might wonder why the Handler interface can return an error value in its Log method. Handlers -are encouraged to return errors only if they fail to write their log records out to an external source like if the -syslog daemon is not responding. This allows the construction of useful handlers which cope with those failures -like the FailoverHandler. - -Library Use - -log15 is intended to be useful for library authors as a way to provide configurable logging to -users of their library. Best practice for use in a library is to always disable all output for your logger -by default and to provide a public Logger instance that consumers of your library can configure. Like so: - - package yourlib - - import "github.com/inconshreveable/log15" - - var Log = log.New() - - func init() { - Log.SetHandler(log.DiscardHandler()) - } - -Users of your library may then enable it if they like: - - import "github.com/inconshreveable/log15" - import "example.com/yourlib" - - func main() { - handler := // custom handler setup - yourlib.Log.SetHandler(handler) - } - -Best practices attaching logger context - -The ability to attach context to a logger is a powerful one. Where should you do it and why? -I favor embedding a Logger directly into any persistent object in my application and adding -unique, tracing context keys to it. For instance, imagine I am writing a web browser: - - type Tab struct { - url string - render *RenderingContext - // ... - - Logger - } - - func NewTab(url string) *Tab { - return &Tab { - // ... - url: url, - - Logger: log.New("url", url), - } - } - -When a new tab is created, I assign a logger to it with the url of -the tab as context so it can easily be traced through the logs. -Now, whenever we perform any operation with the tab, we'll log with its -embedded logger and it will include the tab title automatically: - - tab.Debug("moved position", "idx", tab.idx) - -There's only one problem. What if the tab url changes? We could -use log.Lazy to make sure the current url is always written, but that -would mean that we couldn't trace a tab's full lifetime through our -logs after the user navigate to a new URL. - -Instead, think about what values to attach to your loggers the -same way you think about what to use as a key in a SQL database schema. -If it's possible to use a natural key that is unique for the lifetime of the -object, do so. But otherwise, log15's ext package has a handy RandId -function to let you generate what you might call "surrogate keys" -They're just random hex identifiers to use for tracing. Back to our -Tab example, we would prefer to set up our Logger like so: - - import logext "github.com/inconshreveable/log15/ext" - - t := &Tab { - // ... - url: url, - } - - t.Logger = log.New("id", logext.RandId(8), "url", log.Lazy{t.getUrl}) - return t - -Now we'll have a unique traceable identifier even across loading new urls, but -we'll still be able to see the tab's current url in the log messages. - -Must - -For all Handler functions which can return an error, there is a version of that -function which will return no error but panics on failure. They are all available -on the Must object. For example: - - log.Must.FileHandler("/path", log.JSONFormat) - log.Must.NetHandler("tcp", ":1234", log.JSONFormat) - -Inspiration and Credit - -All of the following excellent projects inspired the design of this library: - -code.google.com/p/log4go - -github.com/op/go-logging - -github.com/technoweenie/grohl - -github.com/Sirupsen/logrus - -github.com/kr/logfmt - -github.com/spacemonkeygo/spacelog - -golang's stdlib, notably io and net/http - -The Name - -https://xkcd.com/927/ - -*/ -package log diff --git a/log/format.go b/log/format.go index b7c6baa570b1..5cbbe3341ed0 100644 --- a/log/format.go +++ b/log/format.go @@ -2,18 +2,15 @@ package log import ( "bytes" - "encoding/json" "fmt" "math/big" "reflect" "strconv" - "strings" - "sync" - "sync/atomic" "time" "unicode/utf8" "github.com/holiman/uint256" + "golang.org/x/exp/slog" ) const ( @@ -24,61 +21,19 @@ const ( termCtxMaxPadding = 40 ) -// ResetGlobalState resets the fieldPadding, which is useful for producing -// predictable output. -func ResetGlobalState() { - fieldPaddingLock.Lock() - fieldPadding = make(map[string]int) - fieldPaddingLock.Unlock() -} - -// locationTrims are trimmed for display to avoid unwieldy log lines. -var locationTrims = []string{ - "github.com/XinFinOrg/XDPoSChain/", -} - -// PrintOrigins sets or unsets log location (file:line) printing for terminal -// format output. -func PrintOrigins(print bool) { - locationEnabled.Store(print) - if print { - stackEnabled.Store(true) - } -} - -// stackEnabled is an atomic flag controlling whether the log handler needs -// to store the callsite stack. This is needed in case any handler wants to -// print locations (locationEnabled), use vmodule, or print full stacks (BacktraceAt). -var stackEnabled atomic.Bool - -// locationEnabled is an atomic flag controlling whether the terminal formatter -// should append the log locations too when printing entries. -var locationEnabled atomic.Bool - -// locationLength is the maxmimum path length encountered, which all logs are -// padded to to aid in alignment. -var locationLength atomic.Uint32 - -// fieldPadding is a global map with maximum field value lengths seen until now -// to allow padding log contexts in a bit smarter way. -var fieldPadding = make(map[string]int) - -// fieldPaddingLock is a global mutex protecting the field padding map. -var fieldPaddingLock sync.RWMutex - type Format interface { - Format(r *Record) []byte + Format(r slog.Record) []byte } // FormatFunc returns a new Format object which uses // the given function to perform record formatting. -func FormatFunc(f func(*Record) []byte) Format { +func FormatFunc(f func(slog.Record) []byte) Format { return formatFunc(f) } -type formatFunc func(*Record) []byte +type formatFunc func(slog.Record) []byte -func (f formatFunc) Format(r *Record) []byte { +func (f formatFunc) Format(r slog.Record) []byte { return f(r) } @@ -89,263 +44,100 @@ type TerminalStringer interface { TerminalString() string } -// TerminalFormat formats log records optimized for human readability on -// a terminal with color-coded level output and terser human friendly timestamp. -// This format should only be used for interactive programs or while developing. -// -// [LEVEL] [TIME] MESSAGE key=value key=value ... -// -// Example: -// -// [DBUG] [May 16 20:58:45] remove route ns=haproxy addr=127.0.0.1:50002 -func TerminalFormat(usecolor bool) Format { - return FormatFunc(func(r *Record) []byte { - msg := escapeMessage(r.Msg) - var color = 0 - if usecolor { - switch r.Lvl { - case LvlCrit: - color = 35 - case LvlError: - color = 31 - case LvlWarn: - color = 33 - case LvlInfo: - color = 32 - case LvlDebug: - color = 36 - case LvlTrace: - color = 34 - } +func (h *TerminalHandler) TerminalFormat(r slog.Record, usecolor bool) []byte { + msg := escapeMessage(r.Message) + var color = 0 + if usecolor { + switch r.Level { + case LevelCrit: + color = 35 + case slog.LevelError: + color = 31 + case slog.LevelWarn: + color = 33 + case slog.LevelInfo: + color = 32 + case slog.LevelDebug: + color = 36 + case LevelTrace: + color = 34 } + } - b := &bytes.Buffer{} - lvl := r.Lvl.AlignedString() - if locationEnabled.Load() { - // Log origin printing was requested, format the location path and line number - location := fmt.Sprintf("%+v", r.Call) - for _, prefix := range locationTrims { - location = strings.TrimPrefix(location, prefix) - } - // Maintain the maximum location length for fancyer alignment - align := int(locationLength.Load()) - if align < len(location) { - align = len(location) - locationLength.Store(uint32(align)) - } - padding := strings.Repeat(" ", align-len(location)) + b := &bytes.Buffer{} + lvl := LevelAlignedString(r.Level) + if color > 0 { + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), msg) + } else { + fmt.Fprintf(b, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), msg) + } + // try to justify the log output for short messages + length := utf8.RuneCountInString(msg) + if r.NumAttrs() > 0 && length < termMsgJust { + b.Write(bytes.Repeat([]byte{' '}, termMsgJust-length)) + } + // print the keys logfmt style + h.logfmt(b, r, color) - // Assemble and print the log heading - if color > 0 { - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s|%s]%s %s ", color, lvl, r.Time.Format(termTimeFormat), location, padding, msg) - } else { - fmt.Fprintf(b, "%s[%s|%s]%s %s ", lvl, r.Time.Format(termTimeFormat), location, padding, msg) - } - } else { - if color > 0 { - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), msg) - } else { - fmt.Fprintf(b, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), msg) - } - } - // try to justify the log output for short messages - length := utf8.RuneCountInString(msg) - if len(r.Ctx) > 0 && length < termMsgJust { - b.Write(bytes.Repeat([]byte{' '}, termMsgJust-length)) - } - // print the keys logfmt style - logfmt(b, r.Ctx, color, true) - return b.Bytes() - }) + return b.Bytes() } -// LogfmtFormat prints records in logfmt format, an easy machine-parseable but human-readable -// format for key/value pairs. -// -// For more details see: http://godoc.org/github.com/kr/logfmt -func LogfmtFormat() Format { - return FormatFunc(func(r *Record) []byte { - common := []interface{}{r.KeyNames.Time, r.Time, r.KeyNames.Lvl, r.Lvl, r.KeyNames.Msg, r.Msg} - buf := &bytes.Buffer{} - logfmt(buf, append(common, r.Ctx...), 0, false) - return buf.Bytes() +func (h *TerminalHandler) logfmt(buf *bytes.Buffer, r slog.Record, color int) { + attrs := []slog.Attr{} + r.Attrs(func(attr slog.Attr) bool { + attrs = append(attrs, attr) + return true }) -} -func logfmt(buf *bytes.Buffer, ctx []interface{}, color int, term bool) { - for i := 0; i < len(ctx); i += 2 { + attrs = append(h.attrs, attrs...) + + for i, attr := range attrs { if i != 0 { buf.WriteByte(' ') } - k, ok := ctx[i].(string) - v := formatLogfmtValue(ctx[i+1], term) - if !ok { - k, v = errorKey, fmt.Sprintf("%+T is not a string key", ctx[i]) - } else { - k = escapeString(k) - } + key := escapeString(attr.Key) + rawVal := attr.Value.Any() + val := FormatLogfmtValue(rawVal, true) // XXX: we should probably check that all of your key bytes aren't invalid - fieldPaddingLock.RLock() - padding := fieldPadding[k] - fieldPaddingLock.RUnlock() + // TODO (jwasinger) above comment was from log15 code. what does it mean? check that key bytes are ascii characters? + padding := h.fieldPadding[key] - length := utf8.RuneCountInString(v) + length := utf8.RuneCountInString(val) if padding < length && length <= termCtxMaxPadding { padding = length - - fieldPaddingLock.Lock() - fieldPadding[k] = padding - fieldPaddingLock.Unlock() + h.fieldPadding[key] = padding } if color > 0 { - fmt.Fprintf(buf, "\x1b[%dm%s\x1b[0m=", color, k) + fmt.Fprintf(buf, "\x1b[%dm%s\x1b[0m=", color, key) } else { - buf.WriteString(k) + buf.WriteString(key) buf.WriteByte('=') } - buf.WriteString(v) - if i < len(ctx)-2 && padding > length { + buf.WriteString(val) + if i < r.NumAttrs()-1 && padding > length { buf.Write(bytes.Repeat([]byte{' '}, padding-length)) } } buf.WriteByte('\n') } -// JSONFormat formats log records as JSON objects separated by newlines. -// It is the equivalent of JSONFormatEx(false, true). -func JSONFormat() Format { - return JSONFormatEx(false, true) -} - -// JSONFormatOrderedEx formats log records as JSON arrays. If pretty is true, -// records will be pretty-printed. If lineSeparated is true, records -// will be logged with a new line between each record. -func JSONFormatOrderedEx(pretty, lineSeparated bool) Format { - jsonMarshal := json.Marshal - if pretty { - jsonMarshal = func(v interface{}) ([]byte, error) { - return json.MarshalIndent(v, "", " ") - } - } - return FormatFunc(func(r *Record) []byte { - props := map[string]interface{}{ - r.KeyNames.Time: r.Time, - r.KeyNames.Lvl: r.Lvl.String(), - r.KeyNames.Msg: r.Msg, - } - - ctx := make([]string, len(r.Ctx)) - for i := 0; i < len(r.Ctx); i += 2 { - if k, ok := r.Ctx[i].(string); ok { - ctx[i] = k - ctx[i+1] = formatLogfmtValue(r.Ctx[i+1], true) - } else { - props[errorKey] = fmt.Sprintf("%+T is not a string key,", r.Ctx[i]) - } - } - props[r.KeyNames.Ctx] = ctx - - b, err := jsonMarshal(props) - if err != nil { - b, _ = jsonMarshal(map[string]string{ - errorKey: err.Error(), - }) - return b - } - if lineSeparated { - b = append(b, '\n') - } - return b - }) -} - -// JSONFormatEx formats log records as JSON objects. If pretty is true, -// records will be pretty-printed. If lineSeparated is true, records -// will be logged with a new line between each record. -func JSONFormatEx(pretty, lineSeparated bool) Format { - jsonMarshal := json.Marshal - if pretty { - jsonMarshal = func(v interface{}) ([]byte, error) { - return json.MarshalIndent(v, "", " ") - } +// formatValue formats a value for serialization +func FormatLogfmtValue(value interface{}, term bool) (result string) { + if value == nil { + return "" } - - return FormatFunc(func(r *Record) []byte { - props := map[string]interface{}{ - r.KeyNames.Time: r.Time, - r.KeyNames.Lvl: r.Lvl.String(), - r.KeyNames.Msg: r.Msg, - } - - for i := 0; i < len(r.Ctx); i += 2 { - k, ok := r.Ctx[i].(string) - if !ok { - props[errorKey] = fmt.Sprintf("%+T is not a string key", r.Ctx[i]) - } else { - props[k] = formatJSONValue(r.Ctx[i+1]) - } - } - - b, err := jsonMarshal(props) - if err != nil { - b, _ = jsonMarshal(map[string]string{ - errorKey: err.Error(), - }) - return b - } - - if lineSeparated { - b = append(b, '\n') - } - - return b - }) -} - -func formatShared(value interface{}) (result interface{}) { defer func() { if err := recover(); err != nil { if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr && v.IsNil() { - result = "nil" + result = "" } else { panic(err) } } }() - switch v := value.(type) { - case time.Time: - return v.Format(timeFormat) - - case error: - return v.Error() - - case fmt.Stringer: - return v.String() - - default: - return v - } -} - -func formatJSONValue(value interface{}) interface{} { - value = formatShared(value) - switch value.(type) { - case int, int8, int16, int32, int64, float32, float64, uint, uint8, uint16, uint32, uint64, string: - return value - default: - return fmt.Sprintf("%+v", value) - } -} - -// formatValue formats a value for serialization -func formatLogfmtValue(value interface{}, term bool) string { - if value == nil { - return "nil" - } - switch v := value.(type) { case time.Time: // Performance optimization: No need for escaping since the provided @@ -375,8 +167,11 @@ func formatLogfmtValue(value interface{}, term bool) string { return escapeString(s.TerminalString()) } } - value = formatShared(value) switch v := value.(type) { + case error: + return escapeString(v.Error()) + case fmt.Stringer: + return escapeString(v.String()) case bool: return strconv.FormatBool(v) case float32: diff --git a/log/handler.go b/log/handler.go index 4a0cf578f6cd..d3e73ada3268 100644 --- a/log/handler.go +++ b/log/handler.go @@ -1,375 +1,223 @@ package log import ( + "context" "fmt" "io" - "net" - "os" + "math/big" "reflect" "sync" - "sync/atomic" + "time" - "github.com/go-stack/stack" + "github.com/holiman/uint256" + "golang.org/x/exp/slog" ) -// Handler defines where and how log records are written. -// A Logger prints its log records by writing to a Handler. -// Handlers are composable, providing you great flexibility in combining -// them to achieve the logging structure that suits your applications. -type Handler interface { - Log(r *Record) error -} - -// FuncHandler returns a Handler that logs records with the given -// function. -func FuncHandler(fn func(r *Record) error) Handler { - return funcHandler(fn) -} - -type funcHandler func(r *Record) error - -func (h funcHandler) Log(r *Record) error { - return h(r) -} - -// StreamHandler writes log records to an io.Writer -// with the given format. StreamHandler can be used -// to easily begin writing log records to other -// outputs. +// Lazy allows you to defer calculation of a logged value that is expensive +// to compute until it is certain that it must be evaluated with the given filters. // -// StreamHandler wraps itself with LazyHandler and SyncHandler -// to evaluate Lazy objects and perform safe concurrent writes. -func StreamHandler(wr io.Writer, fmtr Format) Handler { - h := FuncHandler(func(r *Record) error { - _, err := wr.Write(fmtr.Format(r)) - return err - }) - return LazyHandler(SyncHandler(h)) +// You may wrap any function which takes no arguments to Lazy. It may return any +// number of values of any type. +type Lazy struct { + Fn interface{} } -// SyncHandler can be wrapped around a handler to guarantee that -// only a single Log operation can proceed at a time. It's necessary -// for thread-safe concurrent writes. -func SyncHandler(h Handler) Handler { - var mu sync.Mutex - return FuncHandler(func(r *Record) error { - mu.Lock() - defer mu.Unlock() - - return h.Log(r) - }) -} +func evaluateLazy(lz Lazy) (interface{}, error) { + t := reflect.TypeOf(lz.Fn) -// FileHandler returns a handler which writes log records to the give file -// using the given format. If the path -// already exists, FileHandler will append to the given file. If it does not, -// FileHandler will create the file with mode 0644. -func FileHandler(path string, fmtr Format) (Handler, error) { - f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) - if err != nil { - return nil, err + if t.Kind() != reflect.Func { + return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn) } - return closingHandler{f, StreamHandler(f, fmtr)}, nil -} -// NetHandler opens a socket to the given address and writes records -// over the connection. -func NetHandler(network, addr string, fmtr Format) (Handler, error) { - conn, err := net.Dial(network, addr) - if err != nil { - return nil, err + if t.NumIn() > 0 { + return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn) } - return closingHandler{conn, StreamHandler(conn, fmtr)}, nil -} - -// XXX: closingHandler is essentially unused at the moment -// it's meant for a future time when the Handler interface supports -// a possible Close() operation -type closingHandler struct { - io.WriteCloser - Handler -} + if t.NumOut() == 0 { + return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn) + } -func (h *closingHandler) Close() error { - return h.WriteCloser.Close() + value := reflect.ValueOf(lz.Fn) + results := value.Call([]reflect.Value{}) + if len(results) == 1 { + return results[0].Interface(), nil + } + values := make([]interface{}, len(results)) + for i, v := range results { + values[i] = v.Interface() + } + return values, nil } -// CallerFileHandler returns a Handler that adds the line number and file of -// the calling function to the context with key "caller". -func CallerFileHandler(h Handler) Handler { - return FuncHandler(func(r *Record) error { - r.Ctx = append(r.Ctx, "caller", fmt.Sprint(r.Call)) - return h.Log(r) - }) -} +type discardHandler struct{} -// CallerFuncHandler returns a Handler that adds the calling function name to -// the context with key "fn". -func CallerFuncHandler(h Handler) Handler { - return FuncHandler(func(r *Record) error { - r.Ctx = append(r.Ctx, "fn", formatCall("%+n", r.Call)) - return h.Log(r) - }) +// DiscardHandler returns a no-op handler +func DiscardHandler() slog.Handler { + return &discardHandler{} } -// This function is here to please go vet on Go < 1.8. -func formatCall(format string, c stack.Call) string { - return fmt.Sprintf(format, c) +func (h *discardHandler) Handle(_ context.Context, r slog.Record) error { + return nil } -// CallerStackHandler returns a Handler that adds a stack trace to the context -// with key "stack". The stack trace is formatted as a space separated list of -// call sites inside matching []'s. The most recent call site is listed first. -// Each call site is formatted according to format. See the documentation of -// package github.com/go-stack/stack for the list of supported formats. -func CallerStackHandler(format string, h Handler) Handler { - return FuncHandler(func(r *Record) error { - s := stack.Trace().TrimBelow(r.Call).TrimRuntime() - if len(s) > 0 { - r.Ctx = append(r.Ctx, "stack", fmt.Sprintf(format, s)) - } - return h.Log(r) - }) +func (h *discardHandler) Enabled(_ context.Context, level slog.Level) bool { + return false } -// FilterHandler returns a Handler that only writes records to the -// wrapped Handler if the given function evaluates true. For example, -// to only log records where the 'err' key is not nil: -// -// logger.SetHandler(FilterHandler(func(r *Record) bool { -// for i := 0; i < len(r.Ctx); i += 2 { -// if r.Ctx[i] == "err" { -// return r.Ctx[i+1] != nil -// } -// } -// return false -// }, h)) -func FilterHandler(fn func(r *Record) bool, h Handler) Handler { - return FuncHandler(func(r *Record) error { - if fn(r) { - return h.Log(r) - } - return nil - }) +func (h *discardHandler) WithGroup(name string) slog.Handler { + panic("not implemented") } -// MatchFilterHandler returns a Handler that only writes records -// to the wrapped Handler if the given key in the logged -// context matches the value. For example, to only log records -// from your ui package: -// -// log.MatchFilterHandler("pkg", "app/ui", log.StdoutHandler) -func MatchFilterHandler(key string, value interface{}, h Handler) Handler { - return FilterHandler(func(r *Record) (pass bool) { - switch key { - case r.KeyNames.Lvl: - return r.Lvl == value - case r.KeyNames.Time: - return r.Time == value - case r.KeyNames.Msg: - return r.Msg == value - } - - for i := 0; i < len(r.Ctx); i += 2 { - if r.Ctx[i] == key { - return r.Ctx[i+1] == value - } - } - return false - }, h) +func (h *discardHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + return &discardHandler{} } -// LvlFilterHandler returns a Handler that only writes -// records which are less than the given verbosity -// level to the wrapped Handler. For example, to only -// log Error/Crit records: -// -// log.LvlFilterHandler(log.LvlError, log.StdoutHandler) -func LvlFilterHandler(maxLvl Lvl, h Handler) Handler { - return FilterHandler(func(r *Record) (pass bool) { - return r.Lvl <= maxLvl - }, h) +type TerminalHandler struct { + mu sync.Mutex + wr io.Writer + lvl slog.Level + useColor bool + attrs []slog.Attr + // fieldPadding is a map with maximum field value lengths seen until now + // to allow padding log contexts in a bit smarter way. + fieldPadding map[string]int } -// MultiHandler dispatches any write to each of its handlers. -// This is useful for writing different types of log information -// to different locations. For example, to log to a file and -// standard error: +// NewTerminalHandler returns a handler which formats log records at all levels optimized for human readability on +// a terminal with color-coded level output and terser human friendly timestamp. +// This format should only be used for interactive programs or while developing. // -// log.MultiHandler( -// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), -// log.StderrHandler) -func MultiHandler(hs ...Handler) Handler { - return FuncHandler(func(r *Record) error { - for _, h := range hs { - // what to do about failures? - h.Log(r) - } - return nil - }) -} - -// FailoverHandler writes all log records to the first handler -// specified, but will failover and write to the second handler if -// the first handler has failed, and so on for all handlers specified. -// For example you might want to log to a network socket, but failover -// to writing to a file if the network fails, and then to -// standard out if the file write fails: +// [LEVEL] [TIME] MESSAGE key=value key=value ... // -// log.FailoverHandler( -// log.Must.NetHandler("tcp", ":9090", log.JSONFormat()), -// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), -// log.StdoutHandler) +// Example: // -// All writes that do not go to the first handler will add context with keys of -// the form "failover_err_{idx}" which explain the error encountered while -// trying to write to the handlers before them in the list. -func FailoverHandler(hs ...Handler) Handler { - return FuncHandler(func(r *Record) error { - var err error - for i, h := range hs { - err = h.Log(r) - if err == nil { - return nil - } - r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err) - } - - return err - }) +// [DBUG] [May 16 20:58:45] remove route ns=haproxy addr=127.0.0.1:50002 +func NewTerminalHandler(wr io.Writer, useColor bool) *TerminalHandler { + return NewTerminalHandlerWithLevel(wr, levelMaxVerbosity, useColor) +} + +// NewTerminalHandlerWithLevel returns the same handler as NewTerminalHandler but only outputs +// records which are less than or equal to the specified verbosity level. +func NewTerminalHandlerWithLevel(wr io.Writer, lvl slog.Level, useColor bool) *TerminalHandler { + return &TerminalHandler{ + wr: wr, + lvl: lvl, + useColor: useColor, + fieldPadding: make(map[string]int), + } } -// ChannelHandler writes all records to the given channel. -// It blocks if the channel is full. Useful for async processing -// of log messages, it's used by BufferedHandler. -func ChannelHandler(recs chan<- *Record) Handler { - return FuncHandler(func(r *Record) error { - recs <- r - return nil - }) +func (h *TerminalHandler) Handle(_ context.Context, r slog.Record) error { + h.mu.Lock() + defer h.mu.Unlock() + h.wr.Write(h.TerminalFormat(r, h.useColor)) + return nil } -// BufferedHandler writes all records to a buffered -// channel of the given size which flushes into the wrapped -// handler whenever it is available for writing. Since these -// writes happen asynchronously, all writes to a BufferedHandler -// never return an error and any errors from the wrapped handler are ignored. -func BufferedHandler(bufSize int, h Handler) Handler { - recs := make(chan *Record, bufSize) - go func() { - for m := range recs { - _ = h.Log(m) - } - }() - return ChannelHandler(recs) +func (h *TerminalHandler) Enabled(_ context.Context, level slog.Level) bool { + return level >= h.lvl } -// LazyHandler writes all values to the wrapped handler after evaluating -// any lazy functions in the record's context. It is already wrapped -// around StreamHandler and SyslogHandler in this library, you'll only need -// it if you write your own Handler. -func LazyHandler(h Handler) Handler { - return FuncHandler(func(r *Record) error { - // go through the values (odd indices) and reassign - // the values of any lazy fn to the result of its execution - hadErr := false - for i := 1; i < len(r.Ctx); i += 2 { - lz, ok := r.Ctx[i].(Lazy) - if ok { - v, err := evaluateLazy(lz) - if err != nil { - hadErr = true - r.Ctx[i] = err - } else { - if cs, ok := v.(stack.CallStack); ok { - v = cs.TrimBelow(r.Call).TrimRuntime() - } - r.Ctx[i] = v - } - } - } - - if hadErr { - r.Ctx = append(r.Ctx, errorKey, "bad lazy") - } - - return h.Log(r) - }) +func (h *TerminalHandler) WithGroup(name string) slog.Handler { + panic("not implemented") } -func evaluateLazy(lz Lazy) (interface{}, error) { - t := reflect.TypeOf(lz.Fn) - - if t.Kind() != reflect.Func { - return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn) - } - - if t.NumIn() > 0 { - return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn) +func (h *TerminalHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + return &TerminalHandler{ + wr: h.wr, + lvl: h.lvl, + useColor: h.useColor, + attrs: append(h.attrs, attrs...), + fieldPadding: make(map[string]int), } - - if t.NumOut() == 0 { - return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn) - } - - value := reflect.ValueOf(lz.Fn) - results := value.Call([]reflect.Value{}) - if len(results) == 1 { - return results[0].Interface(), nil - } - values := make([]interface{}, len(results)) - for i, v := range results { - values[i] = v.Interface() - } - return values, nil } -// DiscardHandler reports success for all writes but does nothing. -// It is useful for dynamically disabling logging at runtime via -// a Logger's SetHandler method. -func DiscardHandler() Handler { - return FuncHandler(func(r *Record) error { - return nil - }) +// ResetFieldPadding zeroes the field-padding for all attribute pairs. +func (h *TerminalHandler) ResetFieldPadding() { + h.mu.Lock() + h.fieldPadding = make(map[string]int) + h.mu.Unlock() } -// Must provides the following Handler creation functions -// which instead of returning an error parameter only return a Handler -// and panic on failure: FileHandler, NetHandler, SyslogHandler, SyslogNetHandler -var Must muster +type leveler struct{ minLevel slog.Level } -func must(h Handler, err error) Handler { - if err != nil { - panic(err) - } - return h +func (l *leveler) Level() slog.Level { + return l.minLevel } -type muster struct{} - -func (m muster) FileHandler(path string, fmtr Format) Handler { - return must(FileHandler(path, fmtr)) +func JSONHandler(wr io.Writer) slog.Handler { + return slog.NewJSONHandler(wr, &slog.HandlerOptions{ + ReplaceAttr: builtinReplaceJSON, + }) } -func (m muster) NetHandler(network, addr string, fmtr Format) Handler { - return must(NetHandler(network, addr, fmtr)) +// LogfmtHandler returns a handler which prints records in logfmt format, an easy machine-parseable but human-readable +// format for key/value pairs. +// +// For more details see: http://godoc.org/github.com/kr/logfmt +func LogfmtHandler(wr io.Writer) slog.Handler { + return slog.NewTextHandler(wr, &slog.HandlerOptions{ + ReplaceAttr: builtinReplaceLogfmt, + }) } -// swapHandler wraps another handler that may be swapped out -// dynamically at runtime in a thread-safe fashion. -type swapHandler struct { - handler atomic.Value +// LogfmtHandlerWithLevel returns the same handler as LogfmtHandler but it only outputs +// records which are less than or equal to the specified verbosity level. +func LogfmtHandlerWithLevel(wr io.Writer, level slog.Level) slog.Handler { + return slog.NewTextHandler(wr, &slog.HandlerOptions{ + ReplaceAttr: builtinReplaceLogfmt, + Level: &leveler{level}, + }) } -func (h *swapHandler) Log(r *Record) error { - return (*h.handler.Load().(*Handler)).Log(r) +func builtinReplaceLogfmt(_ []string, attr slog.Attr) slog.Attr { + return builtinReplace(nil, attr, true) } -func (h *swapHandler) Swap(newHandler Handler) { - h.handler.Store(&newHandler) +func builtinReplaceJSON(_ []string, attr slog.Attr) slog.Attr { + return builtinReplace(nil, attr, false) } -func (h *swapHandler) Get() Handler { - return *h.handler.Load().(*Handler) +func builtinReplace(_ []string, attr slog.Attr, logfmt bool) slog.Attr { + switch attr.Key { + case slog.TimeKey: + if attr.Value.Kind() == slog.KindTime { + if logfmt { + return slog.String("t", attr.Value.Time().Format(timeFormat)) + } else { + return slog.Attr{Key: "t", Value: attr.Value} + } + } + case slog.LevelKey: + if l, ok := attr.Value.Any().(slog.Level); ok { + attr = slog.Any("lvl", LevelString(l)) + return attr + } + } + + switch v := attr.Value.Any().(type) { + case time.Time: + if logfmt { + attr = slog.String(attr.Key, v.Format(timeFormat)) + } + case *big.Int: + if v == nil { + attr.Value = slog.StringValue("") + } else { + attr.Value = slog.StringValue(v.String()) + } + case *uint256.Int: + if v == nil { + attr.Value = slog.StringValue("") + } else { + attr.Value = slog.StringValue(v.Dec()) + } + case fmt.Stringer: + if v == nil || (reflect.ValueOf(v).Kind() == reflect.Pointer && reflect.ValueOf(v).IsNil()) { + attr.Value = slog.StringValue("") + } else { + attr.Value = slog.StringValue(v.String()) + } + } + return attr } diff --git a/log/handler_glog.go b/log/handler_glog.go index 1dd4764bda11..fb1e03c5b532 100644 --- a/log/handler_glog.go +++ b/log/handler_glog.go @@ -17,6 +17,7 @@ package log import ( + "context" "errors" "fmt" "regexp" @@ -25,54 +26,47 @@ import ( "strings" "sync" "sync/atomic" + + "golang.org/x/exp/slog" ) // errVmoduleSyntax is returned when a user vmodule pattern is invalid. var errVmoduleSyntax = errors.New("expect comma-separated list of filename=N") -// errTraceSyntax is returned when a user backtrace pattern is invalid. -var errTraceSyntax = errors.New("expect file.go:234") - // GlogHandler is a log handler that mimics the filtering features of Google's // glog logger: setting global log levels; overriding with callsite pattern // matches; and requesting backtraces at certain positions. type GlogHandler struct { - origin Handler // The origin handler this wraps + origin slog.Handler // The origin handler this wraps - level atomic.Uint32 // Current log level, atomically accessible - override atomic.Bool // Flag whether overrides are used, atomically accessible - backtrace atomic.Bool // Flag whether backtrace location is set + level atomic.Int32 // Current log level, atomically accessible + override atomic.Bool // Flag whether overrides are used, atomically accessible - patterns []pattern // Current list of patterns to override with - siteCache map[uintptr]Lvl // Cache of callsite pattern evaluations - location string // file:line location where to do a stackdump at - lock sync.RWMutex // Lock protecting the override pattern list + patterns []pattern // Current list of patterns to override with + siteCache map[uintptr]slog.Level // Cache of callsite pattern evaluations + location string // file:line location where to do a stackdump at + lock sync.RWMutex // Lock protecting the override pattern list } // NewGlogHandler creates a new log handler with filtering functionality similar // to Google's glog logger. The returned handler implements Handler. -func NewGlogHandler(h Handler) *GlogHandler { +func NewGlogHandler(h slog.Handler) *GlogHandler { return &GlogHandler{ origin: h, } } -// SetHandler updates the handler to write records to the specified sub-handler. -func (h *GlogHandler) SetHandler(nh Handler) { - h.origin = nh -} - // pattern contains a filter for the Vmodule option, holding a verbosity level // and a file pattern to match. type pattern struct { pattern *regexp.Regexp - level Lvl + level slog.Level } // Verbosity sets the glog verbosity ceiling. The verbosity of individual packages // and source files can be raised using Vmodule. -func (h *GlogHandler) Verbosity(level Lvl) { - h.level.Store(uint32(level)) +func (h *GlogHandler) Verbosity(level slog.Level) { + h.level.Store(int32(level)) } // Vmodule sets the glog verbosity pattern. @@ -108,11 +102,13 @@ func (h *GlogHandler) Vmodule(ruleset string) error { return errVmoduleSyntax } // Parse the level and if correct, assemble the filter rule - level, err := strconv.Atoi(parts[1]) + l, err := strconv.Atoi(parts[1]) if err != nil { return errVmoduleSyntax } - if level <= 0 { + level := FromLegacyLevel(l) + + if level == LevelCrit { continue // Ignore. It's harmless but no point in paying the overhead. } // Compile the rule pattern into a regular expression @@ -130,108 +126,84 @@ func (h *GlogHandler) Vmodule(ruleset string) error { matcher = matcher + "$" re, _ := regexp.Compile(matcher) - filter = append(filter, pattern{re, Lvl(level)}) + filter = append(filter, pattern{re, level}) } // Swap out the vmodule pattern for the new filter system h.lock.Lock() defer h.lock.Unlock() h.patterns = filter - h.siteCache = make(map[uintptr]Lvl) + h.siteCache = make(map[uintptr]slog.Level) h.override.Store(len(filter) != 0) - // Enable location storage (globally) - if len(h.patterns) > 0 { - stackEnabled.Store(true) - } return nil } -// BacktraceAt sets the glog backtrace location. When set to a file and line -// number holding a logging statement, a stack trace will be written to the Info -// log whenever execution hits that statement. -// -// Unlike with Vmodule, the ".go" must be present. -func (h *GlogHandler) BacktraceAt(location string) error { - // Ensure the backtrace location contains two non-empty elements - parts := strings.Split(location, ":") - if len(parts) != 2 { - return errTraceSyntax - } - parts[0] = strings.TrimSpace(parts[0]) - parts[1] = strings.TrimSpace(parts[1]) - if len(parts[0]) == 0 || len(parts[1]) == 0 { - return errTraceSyntax - } - // Ensure the .go prefix is present and the line is valid - if !strings.HasSuffix(parts[0], ".go") { - return errTraceSyntax +func (h *GlogHandler) Enabled(ctx context.Context, lvl slog.Level) bool { + // fast-track skipping logging if override not enabled and the provided verbosity is above configured + return h.override.Load() || slog.Level(h.level.Load()) <= lvl +} + +func (h *GlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + h.lock.RLock() + siteCache := make(map[uintptr]slog.Level) + for k, v := range h.siteCache { + siteCache[k] = v } - if _, err := strconv.Atoi(parts[1]); err != nil { - return errTraceSyntax + h.lock.RUnlock() + + patterns := []pattern{} + patterns = append(patterns, h.patterns...) + + res := GlogHandler{ + origin: h.origin.WithAttrs(attrs), + patterns: patterns, + siteCache: siteCache, + location: h.location, } - // All seems valid - h.lock.Lock() - defer h.lock.Unlock() - h.location = location - h.backtrace.Store(len(location) > 0) - // Enable location storage (globally) - stackEnabled.Store(true) - return nil + res.level.Store(h.level.Load()) + res.override.Store(h.override.Load()) + return &res +} + +func (h *GlogHandler) WithGroup(name string) slog.Handler { + panic("not implemented") } // Log implements Handler.Log, filtering a log record through the global, local // and backtrace filters, finally emitting it if either allow it through. -func (h *GlogHandler) Log(r *Record) error { - // If backtracing is requested, check whether this is the callsite - if h.backtrace.Load() { - // Everything below here is slow. Although we could cache the call sites the - // same way as for vmodule, backtracing is so rare it's not worth the extra - // complexity. - h.lock.RLock() - match := h.location == r.Call.String() - h.lock.RUnlock() - - if match { - // Callsite matched, raise the log level to info and gather the stacks - r.Lvl = LvlInfo - - buf := make([]byte, 1024*1024) - buf = buf[:runtime.Stack(buf, true)] - r.Msg += "\n\n" + string(buf) - } - } +func (h *GlogHandler) Handle(_ context.Context, r slog.Record) error { // If the global log level allows, fast track logging - if h.level.Load() >= uint32(r.Lvl) { - return h.origin.Log(r) - } - // If no local overrides are present, fast track skipping - if !h.override.Load() { - return nil + if slog.Level(h.level.Load()) <= r.Level { + return h.origin.Handle(context.Background(), r) } + // Check callsite cache for previously calculated log levels h.lock.RLock() - lvl, ok := h.siteCache[r.Call.Frame().PC] + lvl, ok := h.siteCache[r.PC] h.lock.RUnlock() // If we didn't cache the callsite yet, calculate it if !ok { h.lock.Lock() + + fs := runtime.CallersFrames([]uintptr{r.PC}) + frame, _ := fs.Next() + for _, rule := range h.patterns { - if rule.pattern.MatchString(fmt.Sprintf("%+s", r.Call)) { - h.siteCache[r.Call.Frame().PC], lvl, ok = rule.level, rule.level, true - break + if rule.pattern.MatchString(fmt.Sprintf("%+s", frame.File)) { + h.siteCache[r.PC], lvl, ok = rule.level, rule.level, true } } // If no rule matched, remember to drop log the next time if !ok { - h.siteCache[r.Call.Frame().PC] = 0 + h.siteCache[r.PC] = 0 } h.lock.Unlock() } - if lvl >= r.Lvl { - return h.origin.Log(r) + if lvl <= r.Level { + return h.origin.Handle(context.Background(), r) } return nil } diff --git a/log/logger.go b/log/logger.go index 9bbfdc912c61..b5b83cd71f9c 100644 --- a/log/logger.go +++ b/log/logger.go @@ -1,293 +1,222 @@ package log import ( - "fmt" + "context" + "math" "os" + "runtime" "time" - "github.com/go-stack/stack" + "golang.org/x/exp/slog" ) -const timeKey = "t" -const lvlKey = "lvl" -const msgKey = "msg" -const ctxKey = "ctx" -const errorKey = "LOG15_ERROR" +const errorKey = "LOG_ERROR" -type Lvl int +const ( + legacyLevelCrit = iota + legacyLevelError + legacyLevelWarn + legacyLevelInfo + legacyLevelDebug + legacyLevelTrace +) const ( - LvlCrit Lvl = iota - LvlError - LvlWarn - LvlInfo - LvlDebug - LvlTrace + levelMaxVerbosity slog.Level = math.MinInt + LevelTrace slog.Level = -8 + LevelDebug = slog.LevelDebug + LevelInfo = slog.LevelInfo + LevelWarn = slog.LevelWarn + LevelError = slog.LevelError + LevelCrit slog.Level = 12 + + // for backward-compatibility + LvlTrace = LevelTrace + LvlInfo = LevelInfo + LvlDebug = LevelDebug ) -// AlignedString returns a 5-character string containing the name of a Lvl. -func (l Lvl) AlignedString() string { +// convert from old Geth verbosity level constants +// to levels defined by slog +func FromLegacyLevel(lvl int) slog.Level { + switch lvl { + case legacyLevelCrit: + return LevelCrit + case legacyLevelError: + return slog.LevelError + case legacyLevelWarn: + return slog.LevelWarn + case legacyLevelInfo: + return slog.LevelInfo + case legacyLevelDebug: + return slog.LevelDebug + case legacyLevelTrace: + return LevelTrace + default: + break + } + + // TODO: should we allow use of custom levels or force them to match existing max/min if they fall outside the range as I am doing here? + if lvl > legacyLevelTrace { + return LevelTrace + } + return LevelCrit +} + +// LevelAlignedString returns a 5-character string containing the name of a Lvl. +func LevelAlignedString(l slog.Level) string { switch l { - case LvlTrace: + case LevelTrace: return "TRACE" - case LvlDebug: + case slog.LevelDebug: return "DEBUG" - case LvlInfo: + case slog.LevelInfo: return "INFO " - case LvlWarn: + case slog.LevelWarn: return "WARN " - case LvlError: + case slog.LevelError: return "ERROR" - case LvlCrit: + case LevelCrit: return "CRIT " default: - panic("bad level") + return "unknown level" } } -// String returns the name of a Lvl. -func (l Lvl) String() string { +// LevelString returns a 5-character string containing the name of a Lvl. +func LevelString(l slog.Level) string { switch l { - case LvlTrace: - return "trce" - case LvlDebug: - return "dbug" - case LvlInfo: + case LevelTrace: + return "trace" + case slog.LevelDebug: + return "debug" + case slog.LevelInfo: return "info" - case LvlWarn: + case slog.LevelWarn: return "warn" - case LvlError: + case slog.LevelError: return "eror" - case LvlCrit: + case LevelCrit: return "crit" default: - panic("bad level") + return "unknown" } } -// LvlFromString returns the appropriate Lvl from a string name. -// Useful for parsing command line args and configuration files. -func LvlFromString(lvlString string) (Lvl, error) { - switch lvlString { - case "trace", "trce": - return LvlTrace, nil - case "debug", "dbug": - return LvlDebug, nil - case "info": - return LvlInfo, nil - case "warn": - return LvlWarn, nil - case "error", "eror": - return LvlError, nil - case "crit": - return LvlCrit, nil - default: - return LvlDebug, fmt.Errorf("unknown level: %v", lvlString) - } -} - -// A Record is what a Logger asks its handler to write -type Record struct { - Time time.Time - Lvl Lvl - Msg string - Ctx []interface{} - Call stack.Call - KeyNames RecordKeyNames -} - -// RecordKeyNames gets stored in a Record when the write function is executed. -type RecordKeyNames struct { - Time string - Msg string - Lvl string - Ctx string -} - // A Logger writes key/value pairs to a Handler type Logger interface { - // New returns a new Logger that has this logger's context plus the given context - New(ctx ...interface{}) Logger + // With returns a new Logger that has this logger's attributes plus the given attributes + With(ctx ...interface{}) Logger - // GetHandler gets the handler associated with the logger. - GetHandler() Handler + // With returns a new Logger that has this logger's attributes plus the given attributes. Identical to 'With'. + New(ctx ...interface{}) Logger - // SetHandler updates the logger to write records to the specified handler. - SetHandler(h Handler) + // Log logs a message at the specified level with context key/value pairs + Log(level slog.Level, msg string, ctx ...interface{}) - // Log a message at the trace level with context key/value pairs - // - // # Usage - // - // log.Trace("msg") - // log.Trace("msg", "key1", val1) - // log.Trace("msg", "key1", val1, "key2", val2) + // Trace log a message at the trace level with context key/value pairs Trace(msg string, ctx ...interface{}) - // Log a message at the debug level with context key/value pairs - // - // # Usage Examples - // - // log.Debug("msg") - // log.Debug("msg", "key1", val1) - // log.Debug("msg", "key1", val1, "key2", val2) + // Debug logs a message at the debug level with context key/value pairs Debug(msg string, ctx ...interface{}) - // Log a message at the info level with context key/value pairs - // - // # Usage Examples - // - // log.Info("msg") - // log.Info("msg", "key1", val1) - // log.Info("msg", "key1", val1, "key2", val2) + // Info logs a message at the info level with context key/value pairs Info(msg string, ctx ...interface{}) - // Log a message at the warn level with context key/value pairs - // - // # Usage Examples - // - // log.Warn("msg") - // log.Warn("msg", "key1", val1) - // log.Warn("msg", "key1", val1, "key2", val2) + // Warn logs a message at the warn level with context key/value pairs Warn(msg string, ctx ...interface{}) - // Log a message at the error level with context key/value pairs - // - // # Usage Examples - // - // log.Error("msg") - // log.Error("msg", "key1", val1) - // log.Error("msg", "key1", val1, "key2", val2) + // Error logs a message at the error level with context key/value pairs Error(msg string, ctx ...interface{}) - // Log a message at the crit level with context key/value pairs, and then exit. - // - // # Usage Examples - // - // log.Crit("msg") - // log.Crit("msg", "key1", val1) - // log.Crit("msg", "key1", val1, "key2", val2) + // Crit logs a message at the crit level with context key/value pairs, and exits Crit(msg string, ctx ...interface{}) + + // Write logs a message at the specified level + Write(level slog.Level, msg string, attrs ...any) } type logger struct { - ctx []interface{} - h *swapHandler + inner *slog.Logger } -func (l *logger) write(msg string, lvl Lvl, ctx []interface{}) { - record := &Record{ - Time: time.Now(), - Lvl: lvl, - Msg: msg, - Ctx: newContext(l.ctx, ctx), - KeyNames: RecordKeyNames{ - Time: timeKey, - Msg: msgKey, - Lvl: lvlKey, - Ctx: ctxKey, - }, - } - if stackEnabled.Load() { - record.Call = stack.Caller(2) +// NewLogger returns a logger with the specified handler set +func NewLogger(h slog.Handler) Logger { + return &logger{ + slog.New(h), } - l.h.Log(record) } -func (l *logger) New(ctx ...interface{}) Logger { - child := &logger{newContext(l.ctx, ctx), new(swapHandler)} - child.SetHandler(l.h) - return child -} +// Write logs a message at the specified level. +func (l *logger) Write(level slog.Level, msg string, attrs ...any) { + if !l.inner.Enabled(context.Background(), level) { + return + } -func newContext(prefix []interface{}, suffix []interface{}) []interface{} { - normalizedSuffix := normalize(suffix) - newCtx := make([]interface{}, len(prefix)+len(normalizedSuffix)) - n := copy(newCtx, prefix) - copy(newCtx[n:], normalizedSuffix) - return newCtx -} + var pcs [1]uintptr + runtime.Callers(3, pcs[:]) -func (l *logger) Trace(msg string, ctx ...interface{}) { - l.write(msg, LvlTrace, ctx) -} + if len(attrs)%2 != 0 { + attrs = append(attrs, nil, errorKey, "Normalized odd number of arguments by adding nil") + } -func (l *logger) Debug(msg string, ctx ...interface{}) { - l.write(msg, LvlDebug, ctx) -} + // evaluate lazy values + var hadErr bool + for i := 1; i < len(attrs); i += 2 { + lz, ok := attrs[i].(Lazy) + if ok { + v, err := evaluateLazy(lz) + if err != nil { + hadErr = true + attrs[i] = err + } else { + attrs[i] = v + } + } + } -func (l *logger) Info(msg string, ctx ...interface{}) { - l.write(msg, LvlInfo, ctx) -} + if hadErr { + attrs = append(attrs, errorKey, "bad lazy") + } -func (l *logger) Warn(msg string, ctx ...interface{}) { - l.write(msg, LvlWarn, ctx) + r := slog.NewRecord(time.Now(), level, msg, pcs[0]) + r.Add(attrs...) + l.inner.Handler().Handle(context.Background(), r) } -func (l *logger) Error(msg string, ctx ...interface{}) { - l.write(msg, LvlError, ctx) +func (l *logger) Log(level slog.Level, msg string, attrs ...any) { + l.Write(level, msg, attrs...) } -func (l *logger) Crit(msg string, ctx ...interface{}) { - l.write(msg, LvlCrit, ctx) - os.Exit(1) +func (l *logger) With(ctx ...interface{}) Logger { + return &logger{l.inner.With(ctx...)} } -func (l *logger) GetHandler() Handler { - return l.h.Get() +func (l *logger) New(ctx ...interface{}) Logger { + return l.With(ctx...) } -func (l *logger) SetHandler(h Handler) { - l.h.Swap(h) +func (l *logger) Trace(msg string, ctx ...interface{}) { + l.Write(LevelTrace, msg, ctx...) } -func normalize(ctx []interface{}) []interface{} { - // if the caller passed a Ctx object, then expand it - if len(ctx) == 1 { - if ctxMap, ok := ctx[0].(Ctx); ok { - ctx = ctxMap.toArray() - } - } - - // ctx needs to be even because it's a series of key/value pairs - // no one wants to check for errors on logging functions, - // so instead of erroring on bad input, we'll just make sure - // that things are the right length and users can fix bugs - // when they see the output looks wrong - if len(ctx)%2 != 0 { - ctx = append(ctx, nil, errorKey, "Normalized odd number of arguments by adding nil") - } - - return ctx +func (l *logger) Debug(msg string, ctx ...interface{}) { + l.Write(slog.LevelDebug, msg, ctx...) } -// Lazy allows you to defer calculation of a logged value that is expensive -// to compute until it is certain that it must be evaluated with the given filters. -// -// Lazy may also be used in conjunction with a Logger's New() function -// to generate a child logger which always reports the current value of changing -// state. -// -// You may wrap any function which takes no arguments to Lazy. It may return any -// number of values of any type. -type Lazy struct { - Fn interface{} +func (l *logger) Info(msg string, ctx ...interface{}) { + l.Write(slog.LevelInfo, msg, ctx...) } -// Ctx is a map of key/value pairs to pass as context to a log function -// Use this only if you really need greater safety around the arguments you pass -// to the logging functions. -type Ctx map[string]interface{} - -func (c Ctx) toArray() []interface{} { - arr := make([]interface{}, len(c)*2) +func (l *logger) Warn(msg string, ctx ...any) { + l.Write(slog.LevelWarn, msg, ctx...) +} - i := 0 - for k, v := range c { - arr[i] = k - arr[i+1] = v - i += 2 - } +func (l *logger) Error(msg string, ctx ...interface{}) { + l.Write(slog.LevelError, msg, ctx...) +} - return arr +func (l *logger) Crit(msg string, ctx ...interface{}) { + l.Write(LevelCrit, msg, ctx...) + os.Exit(1) } diff --git a/log/logger_test.go b/log/logger_test.go index 2e59b3fdf0b1..fca1f1680f05 100644 --- a/log/logger_test.go +++ b/log/logger_test.go @@ -5,61 +5,47 @@ import ( "os" "strings" "testing" + + "golang.org/x/exp/slog" ) -// TestLoggingWithTrace checks that if BackTraceAt is set, then the -// gloghandler is capable of spitting out a stacktrace -func TestLoggingWithTrace(t *testing.T) { - defer stackEnabled.Store(stackEnabled.Load()) +// TestLoggingWithVmodule checks that vmodule works. +func TestLoggingWithVmodule(t *testing.T) { out := new(bytes.Buffer) - logger := New() - { - glog := NewGlogHandler(StreamHandler(out, TerminalFormat(false))) - glog.Verbosity(LvlTrace) - if err := glog.BacktraceAt("logger_test.go:24"); err != nil { - t.Fatal(err) - } - logger.SetHandler(glog) - } - logger.Trace("a message", "foo", "bar") // Will be bumped to INFO + glog := NewGlogHandler(NewTerminalHandlerWithLevel(out, LevelTrace, false)) + glog.Verbosity(LevelCrit) + logger := NewLogger(glog) + logger.Warn("This should not be seen", "ignored", "true") + glog.Vmodule("logger_test.go=5") + logger.Trace("a message", "foo", "bar") have := out.String() - if !strings.HasPrefix(have, "INFO") { - t.Fatalf("backtraceat should bump level to info: %s", have) - } // The timestamp is locale-dependent, so we want to trim that off // "INFO [01-01|00:00:00.000] a messag ..." -> "a messag..." have = strings.Split(have, "]")[1] - wantPrefix := " a message\n\ngoroutine" - if !strings.HasPrefix(have, wantPrefix) { - t.Errorf("\nhave: %q\nwant: %q\n", have, wantPrefix) + want := " a message foo=bar\n" + if have != want { + t.Errorf("\nhave: %q\nwant: %q\n", have, want) } } -// TestLoggingWithVmodule checks that vmodule works. -func TestLoggingWithVmodule(t *testing.T) { - defer stackEnabled.Store(stackEnabled.Load()) +func TestTerminalHandlerWithAttrs(t *testing.T) { out := new(bytes.Buffer) - logger := New() - { - glog := NewGlogHandler(StreamHandler(out, TerminalFormat(false))) - glog.Verbosity(LvlCrit) - logger.SetHandler(glog) - logger.Warn("This should not be seen", "ignored", "true") - glog.Vmodule("logger_test.go=5") - } + glog := NewGlogHandler(NewTerminalHandlerWithLevel(out, LevelTrace, false).WithAttrs([]slog.Attr{slog.String("baz", "bat")})) + glog.Verbosity(LevelTrace) + logger := NewLogger(glog) logger.Trace("a message", "foo", "bar") have := out.String() // The timestamp is locale-dependent, so we want to trim that off // "INFO [01-01|00:00:00.000] a messag ..." -> "a messag..." have = strings.Split(have, "]")[1] - want := " a message foo=bar\n" + want := " a message baz=bat foo=bar\n" if have != want { t.Errorf("\nhave: %q\nwant: %q\n", have, want) } } func BenchmarkTraceLogging(b *testing.B) { - Root().SetHandler(LvlFilterHandler(LvlInfo, StreamHandler(os.Stderr, TerminalFormat(true)))) + SetDefault(NewLogger(NewTerminalHandler(os.Stderr, true))) b.ResetTimer() for i := 0; i < b.N; i++ { Trace("a message", "v", i) diff --git a/log/root.go b/log/root.go index 66ffaaa3a0d8..71040fff47c2 100644 --- a/log/root.go +++ b/log/root.go @@ -2,31 +2,33 @@ package log import ( "os" -) + "sync/atomic" -var ( - root = &logger{[]interface{}{}, new(swapHandler)} - StdoutHandler = StreamHandler(os.Stdout, LogfmtFormat()) - StderrHandler = StreamHandler(os.Stderr, LogfmtFormat()) + "golang.org/x/exp/slog" ) +var root atomic.Value + func init() { - root.SetHandler(DiscardHandler()) + defaultLogger := &logger{slog.New(DiscardHandler())} + SetDefault(defaultLogger) } -// New returns a new logger with the given context. -// New is a convenient alias for Root().New -func New(ctx ...interface{}) Logger { - return root.New(ctx...) +// SetDefault sets the default global logger +func SetDefault(l Logger) { + root.Store(l) + if lg, ok := l.(*logger); ok { + slog.SetDefault(lg.inner) + } } // Root returns the root logger func Root() Logger { - return root + return root.Load().(Logger) } // The following functions bypass the exported logger methods (logger.Debug, -// etc.) to keep the call depth the same for all paths to logger.write so +// etc.) to keep the call depth the same for all paths to logger.Write so // runtime.Caller(2) always refers to the call site in client code. // Trace is a convenient alias for Root().Trace @@ -39,7 +41,7 @@ func Root() Logger { // log.Trace("msg", "key1", val1) // log.Trace("msg", "key1", val1, "key2", val2) func Trace(msg string, ctx ...interface{}) { - root.write(msg, LvlTrace, ctx) + Root().Write(LevelTrace, msg, ctx...) } // Debug is a convenient alias for Root().Debug @@ -52,7 +54,7 @@ func Trace(msg string, ctx ...interface{}) { // log.Debug("msg", "key1", val1) // log.Debug("msg", "key1", val1, "key2", val2) func Debug(msg string, ctx ...interface{}) { - root.write(msg, LvlDebug, ctx) + Root().Write(slog.LevelDebug, msg, ctx...) } // Info is a convenient alias for Root().Info @@ -65,7 +67,7 @@ func Debug(msg string, ctx ...interface{}) { // log.Info("msg", "key1", val1) // log.Info("msg", "key1", val1, "key2", val2) func Info(msg string, ctx ...interface{}) { - root.write(msg, LvlInfo, ctx) + Root().Write(slog.LevelInfo, msg, ctx...) } // Warn is a convenient alias for Root().Warn @@ -78,7 +80,7 @@ func Info(msg string, ctx ...interface{}) { // log.Warn("msg", "key1", val1) // log.Warn("msg", "key1", val1, "key2", val2) func Warn(msg string, ctx ...interface{}) { - root.write(msg, LvlWarn, ctx) + Root().Write(slog.LevelWarn, msg, ctx...) } // Error is a convenient alias for Root().Error @@ -91,7 +93,7 @@ func Warn(msg string, ctx ...interface{}) { // log.Error("msg", "key1", val1) // log.Error("msg", "key1", val1, "key2", val2) func Error(msg string, ctx ...interface{}) { - root.write(msg, LvlError, ctx) + Root().Write(slog.LevelError, msg, ctx...) } // Crit is a convenient alias for Root().Crit @@ -104,6 +106,12 @@ func Error(msg string, ctx ...interface{}) { // log.Crit("msg", "key1", val1) // log.Crit("msg", "key1", val1, "key2", val2) func Crit(msg string, ctx ...interface{}) { - root.write(msg, LvlCrit, ctx) + Root().Write(LevelCrit, msg, ctx...) os.Exit(1) } + +// New returns a new logger with the given context. +// New is a convenient alias for Root().New +func New(ctx ...interface{}) Logger { + return Root().With(ctx...) +} diff --git a/log/syslog.go b/log/syslog.go deleted file mode 100644 index 71a17b30b3e0..000000000000 --- a/log/syslog.go +++ /dev/null @@ -1,57 +0,0 @@ -// +build !windows,!plan9 - -package log - -import ( - "log/syslog" - "strings" -) - -// SyslogHandler opens a connection to the system syslog daemon by calling -// syslog.New and writes all records to it. -func SyslogHandler(priority syslog.Priority, tag string, fmtr Format) (Handler, error) { - wr, err := syslog.New(priority, tag) - return sharedSyslog(fmtr, wr, err) -} - -// SyslogNetHandler opens a connection to a log daemon over the network and writes -// all log records to it. -func SyslogNetHandler(net, addr string, priority syslog.Priority, tag string, fmtr Format) (Handler, error) { - wr, err := syslog.Dial(net, addr, priority, tag) - return sharedSyslog(fmtr, wr, err) -} - -func sharedSyslog(fmtr Format, sysWr *syslog.Writer, err error) (Handler, error) { - if err != nil { - return nil, err - } - h := FuncHandler(func(r *Record) error { - var syslogFn = sysWr.Info - switch r.Lvl { - case LvlCrit: - syslogFn = sysWr.Crit - case LvlError: - syslogFn = sysWr.Err - case LvlWarn: - syslogFn = sysWr.Warning - case LvlInfo: - syslogFn = sysWr.Info - case LvlDebug: - syslogFn = sysWr.Debug - case LvlTrace: - syslogFn = func(m string) error { return nil } // There's no syslog level for trace - } - - s := strings.TrimSpace(string(fmtr.Format(r))) - return syslogFn(s) - }) - return LazyHandler(&closingHandler{sysWr, h}), nil -} - -func (m muster) SyslogHandler(priority syslog.Priority, tag string, fmtr Format) Handler { - return must(SyslogHandler(priority, tag, fmtr)) -} - -func (m muster) SyslogNetHandler(net, addr string, priority syslog.Priority, tag string, fmtr Format) Handler { - return must(SyslogNetHandler(net, addr, priority, tag, fmtr)) -} diff --git a/mobile/init.go b/mobile/init.go index ff1463ca9b6e..52e2de10f7fb 100644 --- a/mobile/init.go +++ b/mobile/init.go @@ -27,7 +27,7 @@ import ( func init() { // Initialize the logger - log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) + log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, false))) // Initialize the goroutine count runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/mobile/logger.go b/mobile/logger.go index 28295f71f192..444b106b377e 100644 --- a/mobile/logger.go +++ b/mobile/logger.go @@ -24,5 +24,5 @@ import ( // SetVerbosity sets the global verbosity level (between 0 and 6 - see logger/verbosity.go). func SetVerbosity(level int) { - log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(level), log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) + log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.FromLegacyLevel(level), false))) } diff --git a/p2p/server_test.go b/p2p/server_test.go index a22a47147b51..0affd51f583f 100644 --- a/p2p/server_test.go +++ b/p2p/server_test.go @@ -32,7 +32,7 @@ import ( ) func init() { - // log.Root().SetHandler(log.LvlFilterHandler(log.LvlError, log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) + // log.SetDefault(log.LvlFilterHandler(log.LvlError, log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) } type testTransport struct { diff --git a/p2p/simulations/adapters/exec.go b/p2p/simulations/adapters/exec.go index 1ad3961fea8b..027cc6fec1fb 100644 --- a/p2p/simulations/adapters/exec.go +++ b/p2p/simulations/adapters/exec.go @@ -42,6 +42,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/rpc" "github.com/docker/docker/pkg/reexec" "github.com/gorilla/websocket" + "golang.org/x/exp/slog" ) // ExecAdapter is a NodeAdapter which runs simulation nodes by executing the @@ -155,8 +156,7 @@ func (n *ExecNode) Client() (*rpc.Client, error) { var wsAddrPattern = regexp.MustCompile(`ws://[\d.:]+`) // Start exec's the node passing the ID and service as command line arguments -// and the node config encoded as JSON in the _P2P_NODE_CONFIG environment -// variable +// and the node config encoded as JSON in an environment variable. func (n *ExecNode) Start(snapshots map[string][]byte) (err error) { if n.Cmd != nil { return errors.New("already started") @@ -189,7 +189,7 @@ func (n *ExecNode) Start(snapshots map[string][]byte) (err error) { cmd := n.newCmd() cmd.Stdout = os.Stdout cmd.Stderr = stderr - cmd.Env = append(os.Environ(), fmt.Sprintf("_P2P_NODE_CONFIG=%s", confData)) + cmd.Env = append(os.Environ(), envNodeConfig+"="+string(confData)) if err := cmd.Start(); err != nil { return fmt.Errorf("error starting node: %s", err) } @@ -344,25 +344,58 @@ type execNodeConfig struct { PeerAddrs map[string]string `json:"peer_addrs,omitempty"` } -// execP2PNode starts a devp2p node when the current binary is executed with +func initLogging() { + // Initialize the logging by default first. + var innerHandler slog.Handler + innerHandler = slog.NewTextHandler(os.Stderr, nil) + glogger := log.NewGlogHandler(innerHandler) + glogger.Verbosity(log.LevelInfo) + log.SetDefault(log.NewLogger(glogger)) + + confEnv := os.Getenv(envNodeConfig) + if confEnv == "" { + return + } + var conf execNodeConfig + if err := json.Unmarshal([]byte(confEnv), &conf); err != nil { + return + } + var writer = os.Stderr + if conf.Node.LogFile != "" { + logWriter, err := os.Create(conf.Node.LogFile) + if err != nil { + return + } + writer = logWriter + } + var verbosity = log.LevelInfo + if conf.Node.LogVerbosity <= log.LevelTrace && conf.Node.LogVerbosity >= log.LevelCrit { + verbosity = log.FromLegacyLevel(int(conf.Node.LogVerbosity)) + } + // Reinitialize the logger + innerHandler = log.NewTerminalHandler(writer, true) + glogger = log.NewGlogHandler(innerHandler) + glogger.Verbosity(verbosity) + log.SetDefault(log.NewLogger(glogger)) +} + +// execP2PNode starts a simulation node when the current binary is executed with // argv[0] being "p2p-node", reading the service / ID from argv[1] / argv[2] -// and the node config from the _P2P_NODE_CONFIG environment variable +// and the node config from an environment variable. func execP2PNode() { - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.LogfmtFormat())) - glogger.Verbosity(log.LvlInfo) - log.Root().SetHandler(glogger) - + initLogging() + // read the services from argv serviceNames := strings.Split(os.Args[1], ",") // decode the config - confEnv := os.Getenv("_P2P_NODE_CONFIG") + confEnv := os.Getenv(envNodeConfig) if confEnv == "" { - log.Crit("missing _P2P_NODE_CONFIG") + log.Crit("missing " + envNodeConfig) } var conf execNodeConfig if err := json.Unmarshal([]byte(confEnv), &conf); err != nil { - log.Crit("error decoding _P2P_NODE_CONFIG", "err", err) + log.Crit("error decoding " + envNodeConfig, "err", err) } conf.Stack.P2P.PrivateKey = conf.Node.PrivateKey conf.Stack.Logger = log.New("node.id", conf.Node.ID.String()) @@ -477,6 +510,10 @@ func (s *snapshotService) Stop() error { return nil } +const ( + envNodeConfig = "_P2P_NODE_CONFIG" +) + // SnapshotAPI provides an RPC method to create snapshots of services type SnapshotAPI struct { services map[string]node.Service diff --git a/p2p/simulations/adapters/types.go b/p2p/simulations/adapters/types.go index 0d7cf62ca406..cbc4d4b25ea4 100644 --- a/p2p/simulations/adapters/types.go +++ b/p2p/simulations/adapters/types.go @@ -30,6 +30,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/rpc" "github.com/docker/docker/pkg/reexec" "github.com/gorilla/websocket" + "golang.org/x/exp/slog" ) // Node represents a node in a simulation network which is created by a @@ -38,7 +39,6 @@ import ( // * SimNode - An in-memory node // * ExecNode - A child process node // * DockerNode - A Docker container node -// type Node interface { // Addr returns the node's address (e.g. an Enode URL) Addr() []byte @@ -97,24 +97,39 @@ type NodeConfig struct { // function to sanction or prevent suggesting a peer Reachable func(id discover.NodeID) bool + + // LogFile is the log file name of the p2p node at runtime. + // + // The default value is empty so that the default log writer + // is the system standard output. + LogFile string + + // LogVerbosity is the log verbosity of the p2p node at runtime. + // + // The default verbosity is INFO. + LogVerbosity slog.Level } // nodeConfigJSON is used to encode and decode NodeConfig as JSON by encoding // all fields as strings type nodeConfigJSON struct { - ID string `json:"id"` - PrivateKey string `json:"private_key"` - Name string `json:"name"` - Services []string `json:"services"` + ID string `json:"id"` + PrivateKey string `json:"private_key"` + Name string `json:"name"` + Services []string `json:"services"` + LogFile string `json:"logfile"` + LogVerbosity int `json:"log_verbosity"` } // MarshalJSON implements the json.Marshaler interface by encoding the config // fields as strings func (n *NodeConfig) MarshalJSON() ([]byte, error) { confJSON := nodeConfigJSON{ - ID: n.ID.String(), - Name: n.Name, - Services: n.Services, + ID: n.ID.String(), + Name: n.Name, + Services: n.Services, + LogFile: n.LogFile, + LogVerbosity: int(n.LogVerbosity), } if n.PrivateKey != nil { confJSON.PrivateKey = hex.EncodeToString(crypto.FromECDSA(n.PrivateKey)) @@ -152,6 +167,8 @@ func (n *NodeConfig) UnmarshalJSON(data []byte) error { n.Name = confJSON.Name n.Services = confJSON.Services + n.LogFile = confJSON.LogFile + n.LogVerbosity = slog.Level(confJSON.LogVerbosity) return nil } diff --git a/p2p/simulations/examples/ping-pong.go b/p2p/simulations/examples/ping-pong.go index 488abfb8208d..450ab047906e 100644 --- a/p2p/simulations/examples/ping-pong.go +++ b/p2p/simulations/examples/ping-pong.go @@ -42,7 +42,7 @@ func main() { flag.Parse() // set the log level to Trace - log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) + log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, false))) // register a single ping-pong service services := map[string]adapters.ServiceFunc{ From 9ae7402e357152935a871bc62989998231e8811f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 222/479] slog: faster and less memory-consumption (#28621) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These changes improves the performance of the non-coloured terminal formatting, _quite a lot_. ``` name old time/op new time/op delta TerminalHandler-8 10.2µs ±15% 5.4µs ± 9% -47.02% (p=0.008 n=5+5) name old alloc/op new alloc/op delta TerminalHandler-8 2.17kB ± 0% 0.40kB ± 0% -81.46% (p=0.008 n=5+5) name old allocs/op new allocs/op delta TerminalHandler-8 33.0 ± 0% 5.0 ± 0% -84.85% (p=0.008 n=5+5) ``` I tried to _somewhat_ organize the commits, but the it might still be a bit chaotic. Some core insights: - The function `terminalHandler.Handl` uses a mutex, and writes all output immediately to 'upstream'. Thus, it can reuse a scratch-buffer every time. - This buffer can be propagated internally, making all the internal formatters either write directly to it, - OR, make use of the `tmp := buf.AvailableBuffer()` in some cases, where a byte buffer "extra capacity" can be temporarily used. - The `slog` package uses `Attr` by value. It makes sense to minimize operating on them, since iterating / collecting into a new slice, iterating again etc causes copy-on-heap. Better to operate on them only once. - If we want to do padding, it's better to copy from a constant `space`-buffer than to invoke `bytes.Repeat` every single time. --- internal/testlog/testlog.go | 3 +- log/format.go | 350 ++++++++++++++++++++---------------- log/format_test.go | 8 +- log/handler.go | 6 +- log/logger_test.go | 121 +++++++++++++ 5 files changed, 328 insertions(+), 160 deletions(-) diff --git a/internal/testlog/testlog.go b/internal/testlog/testlog.go index 426f110a41dd..59aa7e0041f9 100644 --- a/internal/testlog/testlog.go +++ b/internal/testlog/testlog.go @@ -183,8 +183,7 @@ func (h *bufHandler) terminalFormat(r slog.Record) string { } for _, attr := range attrs { - rawVal := attr.Value.Any() - fmt.Fprintf(buf, " %s=%s", attr.Key, log.FormatLogfmtValue(rawVal, true)) + fmt.Fprintf(buf, " %s=%s", attr.Key, string(log.FormatSlogValue(attr.Value, true, nil))) } buf.WriteByte('\n') return buf.String() diff --git a/log/format.go b/log/format.go index 5cbbe3341ed0..a2bbcce9c056 100644 --- a/log/format.go +++ b/log/format.go @@ -15,12 +15,14 @@ import ( const ( timeFormat = "2006-01-02T15:04:05-0700" - termTimeFormat = "01-02|15:04:05.000" floatFormat = 'f' termMsgJust = 40 termCtxMaxPadding = 40 ) +// 40 spaces +var spaces = []byte(" ") + type Format interface { Format(r slog.Record) []byte } @@ -44,37 +46,47 @@ type TerminalStringer interface { TerminalString() string } -func (h *TerminalHandler) TerminalFormat(r slog.Record, usecolor bool) []byte { +func (h *TerminalHandler) TerminalFormat(buf []byte, r slog.Record, usecolor bool) []byte { msg := escapeMessage(r.Message) - var color = 0 + var color = "" if usecolor { switch r.Level { case LevelCrit: - color = 35 + color = "\x1b[35m" case slog.LevelError: - color = 31 + color = "\x1b[31m" case slog.LevelWarn: - color = 33 + color = "\x1b[33m" case slog.LevelInfo: - color = 32 + color = "\x1b[32m" case slog.LevelDebug: - color = 36 + color = "\x1b[36m" case LevelTrace: - color = 34 + color = "\x1b[34m" } } + if buf == nil { + buf = make([]byte, 0, 30+termMsgJust) + } + b := bytes.NewBuffer(buf) - b := &bytes.Buffer{} - lvl := LevelAlignedString(r.Level) - if color > 0 { - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), msg) + if color != "" { // Start color + b.WriteString(color) + b.WriteString(LevelAlignedString(r.Level)) + b.WriteString("\x1b[0m") } else { - fmt.Fprintf(b, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), msg) + b.WriteString(LevelAlignedString(r.Level)) } + b.WriteString("[") + writeTimeTermFormat(b, r.Time) + b.WriteString("] ") + b.WriteString(msg) + // try to justify the log output for short messages - length := utf8.RuneCountInString(msg) - if r.NumAttrs() > 0 && length < termMsgJust { - b.Write(bytes.Repeat([]byte{' '}, termMsgJust-length)) + //length := utf8.RuneCountInString(msg) + length := len(msg) + if (r.NumAttrs()+len(h.attrs)) > 0 && length < termMsgJust { + b.Write(spaces[:termMsgJust-length]) } // print the keys logfmt style h.logfmt(b, r, color) @@ -82,150 +94,139 @@ func (h *TerminalHandler) TerminalFormat(r slog.Record, usecolor bool) []byte { return b.Bytes() } -func (h *TerminalHandler) logfmt(buf *bytes.Buffer, r slog.Record, color int) { - attrs := []slog.Attr{} - r.Attrs(func(attr slog.Attr) bool { - attrs = append(attrs, attr) - return true - }) - - attrs = append(h.attrs, attrs...) - - for i, attr := range attrs { - if i != 0 { - buf.WriteByte(' ') +func (h *TerminalHandler) logfmt(buf *bytes.Buffer, r slog.Record, color string) { + // tmp is a temporary buffer we use, until bytes.Buffer.AvailableBuffer() (1.21) + // can be used. + var tmp = make([]byte, 40) + writeAttr := func(attr slog.Attr, first, last bool) { + buf.WriteByte(' ') + + if color != "" { + buf.WriteString(color) + //buf.Write(appendEscapeString(buf.AvailableBuffer(), attr.Key)) + buf.Write(appendEscapeString(tmp[:0], attr.Key)) + buf.WriteString("\x1b[0m=") + } else { + //buf.Write(appendEscapeString(buf.AvailableBuffer(), attr.Key)) + buf.Write(appendEscapeString(tmp[:0], attr.Key)) + buf.WriteByte('=') } + //val := FormatSlogValue(attr.Value, true, buf.AvailableBuffer()) + val := FormatSlogValue(attr.Value, true, tmp[:0]) - key := escapeString(attr.Key) - rawVal := attr.Value.Any() - val := FormatLogfmtValue(rawVal, true) + padding := h.fieldPadding[attr.Key] - // XXX: we should probably check that all of your key bytes aren't invalid - // TODO (jwasinger) above comment was from log15 code. what does it mean? check that key bytes are ascii characters? - padding := h.fieldPadding[key] - - length := utf8.RuneCountInString(val) + length := utf8.RuneCount(val) if padding < length && length <= termCtxMaxPadding { padding = length - h.fieldPadding[key] = padding + h.fieldPadding[attr.Key] = padding } - if color > 0 { - fmt.Fprintf(buf, "\x1b[%dm%s\x1b[0m=", color, key) - } else { - buf.WriteString(key) - buf.WriteByte('=') - } - buf.WriteString(val) - if i < r.NumAttrs()-1 && padding > length { - buf.Write(bytes.Repeat([]byte{' '}, padding-length)) + buf.Write(val) + if !last && padding > length { + buf.Write(spaces[:padding-length]) } } + var n = 0 + var nAttrs = len(h.attrs) + r.NumAttrs() + for _, attr := range h.attrs { + writeAttr(attr, n == 0, n == nAttrs-1) + n++ + } + r.Attrs(func(attr slog.Attr) bool { + writeAttr(attr, n == 0, n == nAttrs-1) + n++ + return true + }) buf.WriteByte('\n') } -// formatValue formats a value for serialization -func FormatLogfmtValue(value interface{}, term bool) (result string) { - if value == nil { - return "" - } +// FormatSlogValue formats a slog.Value for serialization +func FormatSlogValue(v slog.Value, term bool, tmp []byte) (result []byte) { + var value any defer func() { if err := recover(); err != nil { if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr && v.IsNil() { - result = "" + result = []byte("") } else { panic(err) } } }() - switch v := value.(type) { - case time.Time: + switch v.Kind() { + case slog.KindString: + return appendEscapeString(tmp, v.String()) + case slog.KindAny: + value = v.Any() + case slog.KindInt64: // All int-types (int8 ,int16 etc) wind up here + return appendInt64(tmp, v.Int64()) + case slog.KindUint64: // All uint-types (int8 ,int16 etc) wind up here + return appendUint64(tmp, v.Uint64(), false) + case slog.KindFloat64: + return strconv.AppendFloat(tmp, v.Float64(), floatFormat, 3, 64) + case slog.KindBool: + return strconv.AppendBool(tmp, v.Bool()) + case slog.KindDuration: + value = v.Duration() + case slog.KindTime: // Performance optimization: No need for escaping since the provided // timeFormat doesn't have any escape characters, and escaping is // expensive. - return v.Format(timeFormat) - + return v.Time().AppendFormat(tmp, timeFormat) + default: + value = v.Any() + } + if value == nil { + return []byte("") + } + switch v := value.(type) { case *big.Int: // Big ints get consumed by the Stringer clause, so we need to handle // them earlier on. if v == nil { - return "" + return append(tmp, []byte("")...) } - return formatLogfmtBigInt(v) + return appendBigInt(tmp, v) case *uint256.Int: // Uint256s get consumed by the Stringer clause, so we need to handle // them earlier on. if v == nil { - return "" - } - return formatLogfmtUint256(v) - } - if term { - if s, ok := value.(TerminalStringer); ok { - // Custom terminal stringer provided, use that - return escapeString(s.TerminalString()) + return append(tmp, []byte("")...) } - } - switch v := value.(type) { + return appendU256(tmp, v) case error: - return escapeString(v.Error()) + return appendEscapeString(tmp, v.Error()) + case TerminalStringer: + if term { + return appendEscapeString(tmp, v.TerminalString()) // Custom terminal stringer provided, use that + } case fmt.Stringer: - return escapeString(v.String()) - case bool: - return strconv.FormatBool(v) - case float32: - return strconv.FormatFloat(float64(v), floatFormat, 3, 64) - case float64: - return strconv.FormatFloat(v, floatFormat, 3, 64) - case int8: - return strconv.FormatInt(int64(v), 10) - case uint8: - return strconv.FormatInt(int64(v), 10) - case int16: - return strconv.FormatInt(int64(v), 10) - case uint16: - return strconv.FormatInt(int64(v), 10) - // Larger integers get thousands separators. - case int: - return FormatLogfmtInt64(int64(v)) - case int32: - return FormatLogfmtInt64(int64(v)) - case int64: - return FormatLogfmtInt64(v) - case uint: - return FormatLogfmtUint64(uint64(v)) - case uint32: - return FormatLogfmtUint64(uint64(v)) - case uint64: - return FormatLogfmtUint64(v) - case string: - return escapeString(v) - default: - return escapeString(fmt.Sprintf("%+v", value)) + return appendEscapeString(tmp, v.String()) } + + // We can use the 'tmp' as a scratch-buffer, to first format the + // value, and in a second step do escaping. + internal := fmt.Appendf(tmp, "%+v", value) + return appendEscapeString(tmp, string(internal)) } -// FormatLogfmtInt64 formats n with thousand separators. -func FormatLogfmtInt64(n int64) string { +// appendInt64 formats n with thousand separators and writes into buffer dst. +func appendInt64(dst []byte, n int64) []byte { if n < 0 { - return formatLogfmtUint64(uint64(-n), true) + return appendUint64(dst, uint64(-n), true) } - return formatLogfmtUint64(uint64(n), false) -} - -// FormatLogfmtUint64 formats n with thousand separators. -func FormatLogfmtUint64(n uint64) string { - return formatLogfmtUint64(n, false) + return appendUint64(dst, uint64(n), false) } -func formatLogfmtUint64(n uint64, neg bool) string { +// appendUint64 formats n with thousand separators and writes into buffer dst. +func appendUint64(dst []byte, n uint64, neg bool) []byte { // Small numbers are fine as is if n < 100000 { if neg { - return strconv.Itoa(-int(n)) + return strconv.AppendInt(dst, -int64(n), 10) } else { - return strconv.Itoa(int(n)) + return strconv.AppendInt(dst, int64(n), 10) } } // Large numbers should be split @@ -250,16 +251,21 @@ func formatLogfmtUint64(n uint64, neg bool) string { out[i] = '-' i-- } - return string(out[i+1:]) + return append(dst, out[i+1:]...) } -// formatLogfmtBigInt formats n with thousand separators. -func formatLogfmtBigInt(n *big.Int) string { +// FormatLogfmtUint64 formats n with thousand separators. +func FormatLogfmtUint64(n uint64) string { + return string(appendUint64(nil, n, false)) +} + +// appendBigInt formats n with thousand separators and writes to dst. +func appendBigInt(dst []byte, n *big.Int) []byte { if n.IsUint64() { - return FormatLogfmtUint64(n.Uint64()) + return appendUint64(dst, n.Uint64(), false) } if n.IsInt64() { - return FormatLogfmtInt64(n.Int64()) + return appendInt64(dst, n.Int64()) } var ( @@ -284,54 +290,48 @@ func formatLogfmtBigInt(n *big.Int) string { comma++ } } - return string(buf[i+1:]) + return append(dst, buf[i+1:]...) } -// formatLogfmtUint256 formats n with thousand separators. -func formatLogfmtUint256(n *uint256.Int) string { +// appendU256 formats n with thousand separators. +func appendU256(dst []byte, n *uint256.Int) []byte { if n.IsUint64() { - return FormatLogfmtUint64(n.Uint64()) + return appendUint64(dst, n.Uint64(), false) } - var ( - text = n.Dec() - buf = make([]byte, len(text)+len(text)/3) - comma = 0 - i = len(buf) - 1 - ) - for j := len(text) - 1; j >= 0; j, i = j-1, i-1 { - c := text[j] - - switch { - case c == '-': - buf[i] = c - case comma == 3: - buf[i] = ',' - i-- - comma = 0 - fallthrough - default: - buf[i] = c - comma++ - } - } - return string(buf[i+1:]) + res := []byte(n.PrettyDec(',')) + return append(dst, res...) } -// escapeString checks if the provided string needs escaping/quoting, and -// calls strconv.Quote if needed -func escapeString(s string) string { +// appendEscapeString writes the string s to the given writer, with +// escaping/quoting if needed. +func appendEscapeString(dst []byte, s string) []byte { needsQuoting := false + needsEscaping := false for _, r := range s { - // We quote everything below " (0x22) and above~ (0x7E), plus equal-sign - if r <= '"' || r > '~' || r == '=' { + // If it contains spaces or equal-sign, we need to quote it. + if r == ' ' || r == '=' { needsQuoting = true + continue + } + // We need to escape it, if it contains + // - character " (0x22) and lower (except space) + // - characters above ~ (0x7E), plus equal-sign + if r <= '"' || r > '~' { + needsEscaping = true break } } - if !needsQuoting { - return s + if needsEscaping { + return strconv.AppendQuote(dst, s) } - return strconv.Quote(s) + // No escaping needed, but we might have to place within quote-marks, in case + // it contained a space + if needsQuoting { + dst = append(dst, '"') + dst = append(dst, []byte(s)...) + return append(dst, '"') + } + return append(dst, []byte(s)...) } // escapeMessage checks if the provided string needs escaping/quoting, similarly @@ -356,3 +356,45 @@ func escapeMessage(s string) string { } return strconv.Quote(s) } + +// writeTimeTermFormat writes on the format "01-02|15:04:05.000" +func writeTimeTermFormat(buf *bytes.Buffer, t time.Time) { + _, month, day := t.Date() + writePosIntWidth(buf, int(month), 2) + buf.WriteByte('-') + writePosIntWidth(buf, day, 2) + buf.WriteByte('|') + hour, min, sec := t.Clock() + writePosIntWidth(buf, hour, 2) + buf.WriteByte(':') + writePosIntWidth(buf, min, 2) + buf.WriteByte(':') + writePosIntWidth(buf, sec, 2) + ns := t.Nanosecond() + buf.WriteByte('.') + writePosIntWidth(buf, ns/1e6, 3) +} + +// writePosIntWidth writes non-negative integer i to the buffer, padded on the left +// by zeroes to the given width. Use a width of 0 to omit padding. +// Adapted from golang.org/x/exp/slog/internal/buffer/buffer.go +func writePosIntWidth(b *bytes.Buffer, i, width int) { + // Cheap integer to fixed-width decimal ASCII. + // Copied from log/log.go. + if i < 0 { + panic("negative int") + } + // Assemble decimal in reverse order. + var bb [20]byte + bp := len(bb) - 1 + for i >= 10 || width > 1 { + width-- + q := i / 10 + bb[bp] = byte('0' + i - q*10) + bp-- + i = q + } + // i < 10 + bb[bp] = byte('0' + i) + b.Write(bb[bp:]) +} diff --git a/log/format_test.go b/log/format_test.go index 41e1809c38cd..d4c1df4abcf9 100644 --- a/log/format_test.go +++ b/log/format_test.go @@ -5,18 +5,20 @@ import ( "testing" ) -var sink string +var sink []byte func BenchmarkPrettyInt64Logfmt(b *testing.B) { + buf := make([]byte, 100) b.ReportAllocs() for i := 0; i < b.N; i++ { - sink = FormatLogfmtInt64(rand.Int63()) + sink = appendInt64(buf, rand.Int63()) } } func BenchmarkPrettyUint64Logfmt(b *testing.B) { + buf := make([]byte, 100) b.ReportAllocs() for i := 0; i < b.N; i++ { - sink = FormatLogfmtUint64(rand.Uint64()) + sink = appendUint64(buf, rand.Uint64(), false) } } diff --git a/log/handler.go b/log/handler.go index d3e73ada3268..513c230c9e7a 100644 --- a/log/handler.go +++ b/log/handler.go @@ -81,6 +81,8 @@ type TerminalHandler struct { // fieldPadding is a map with maximum field value lengths seen until now // to allow padding log contexts in a bit smarter way. fieldPadding map[string]int + + buf []byte } // NewTerminalHandler returns a handler which formats log records at all levels optimized for human readability on @@ -110,7 +112,9 @@ func NewTerminalHandlerWithLevel(wr io.Writer, lvl slog.Level, useColor bool) *T func (h *TerminalHandler) Handle(_ context.Context, r slog.Record) error { h.mu.Lock() defer h.mu.Unlock() - h.wr.Write(h.TerminalFormat(r, h.useColor)) + buf := h.TerminalFormat(h.buf, r, h.useColor) + h.wr.Write(buf) + h.buf = buf[:0] return nil } diff --git a/log/logger_test.go b/log/logger_test.go index fca1f1680f05..27e90c5fd203 100644 --- a/log/logger_test.go +++ b/log/logger_test.go @@ -2,10 +2,15 @@ package log import ( "bytes" + "fmt" + "io" + "math/big" "os" "strings" "testing" + "time" + "github.com/holiman/uint256" "golang.org/x/exp/slog" ) @@ -51,3 +56,119 @@ func BenchmarkTraceLogging(b *testing.B) { Trace("a message", "v", i) } } + +func BenchmarkTerminalHandler(b *testing.B) { + l := NewLogger(NewTerminalHandler(io.Discard, false)) + benchmarkLogger(b, l) +} +func BenchmarkLogfmtHandler(b *testing.B) { + l := NewLogger(LogfmtHandler(io.Discard)) + benchmarkLogger(b, l) +} + +func BenchmarkJSONHandler(b *testing.B) { + l := NewLogger(JSONHandler(io.Discard)) + benchmarkLogger(b, l) +} + +func benchmarkLogger(b *testing.B, l Logger) { + var ( + bb = make([]byte, 10) + tt = time.Now() + bigint = big.NewInt(100) + nilbig *big.Int + err = fmt.Errorf("Oh nooes it's crap") + ) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + l.Info("This is a message", + "foo", int16(i), + "bytes", bb, + "bonk", "a string with text", + "time", tt, + "bigint", bigint, + "nilbig", nilbig, + "err", err) + } + b.StopTimer() +} + +func TestLoggerOutput(t *testing.T) { + type custom struct { + A string + B int8 + } + var ( + customA = custom{"Foo", 12} + customB = custom{"Foo\nLinebreak", 122} + bb = make([]byte, 10) + tt = time.Time{} + bigint = big.NewInt(100) + nilbig *big.Int + err = fmt.Errorf("Oh nooes it's crap") + lazy = Lazy{Fn: func() interface{} { return "lazy value" }} + smallUint = uint256.NewInt(500_000) + bigUint = &uint256.Int{0xff, 0xff, 0xff, 0xff} + ) + + out := new(bytes.Buffer) + glogHandler := NewGlogHandler(NewTerminalHandler(out, false)) + glogHandler.Verbosity(LevelInfo) + NewLogger(glogHandler).Info("This is a message", + "foo", int16(123), + "bytes", bb, + "bonk", "a string with text", + "time", tt, + "bigint", bigint, + "nilbig", nilbig, + "err", err, + "struct", customA, + "struct", customB, + "ptrstruct", &customA, + "lazy", lazy, + "smalluint", smallUint, + "bigUint", bigUint) + + have := out.String() + t.Logf("output %v", out.String()) + want := `INFO [11-07|19:14:33.821] This is a message foo=123 bytes="[0 0 0 0 0 0 0 0 0 0]" bonk="a string with text" time=0001-01-01T00:00:00+0000 bigint=100 nilbig= err="Oh nooes it's crap" struct="{A:Foo B:12}" struct="{A:Foo\nLinebreak B:122}" ptrstruct="&{A:Foo B:12}" lazy="lazy value" smalluint=500,000 bigUint=1,600,660,942,523,603,594,864,898,306,482,794,244,293,965,082,972,225,630,372,095 +` + if !bytes.Equal([]byte(have)[25:], []byte(want)[25:]) { + t.Errorf("Error\nhave: %q\nwant: %q", have, want) + } +} + +const termTimeFormat = "01-02|15:04:05.000" + +func BenchmarkAppendFormat(b *testing.B) { + var now = time.Now() + b.Run("fmt time.Format", func(b *testing.B) { + for i := 0; i < b.N; i++ { + fmt.Fprintf(io.Discard, "%s", now.Format(termTimeFormat)) + } + }) + b.Run("time.AppendFormat", func(b *testing.B) { + for i := 0; i < b.N; i++ { + now.AppendFormat(nil, termTimeFormat) + } + }) + var buf = new(bytes.Buffer) + b.Run("time.Custom", func(b *testing.B) { + for i := 0; i < b.N; i++ { + writeTimeTermFormat(buf, now) + buf.Reset() + } + }) +} + +func TestTermTimeFormat(t *testing.T) { + var now = time.Now() + want := now.AppendFormat(nil, termTimeFormat) + var b = new(bytes.Buffer) + writeTimeTermFormat(b, now) + have := b.Bytes() + if !bytes.Equal(have, want) { + t.Errorf("have != want\nhave: %q\nwant: %q\n", have, want) + } +} From 14acdf2dd194ee30fb8c8501069fd81d9fd32f81 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 223/479] log: remove lazy, remove unused interfaces, unexport methods (#28622) This change - Removes interface `log.Format`, - Removes method `log.FormatFunc`, - unexports `TerminalHandler.TerminalFormat` formatting methods (renamed to `TerminalHandler.format`) - removes the notion of `log.Lazy` values The lazy handler was useful in the old log package, since it could defer the evaluation of costly attributes until later in the log pipeline: thus, if the logging was done at 'Trace', we could skip evaluation if logging only was set to 'Info'. With the move to slog, this way of deferring evaluation is no longer needed, since slog introduced 'Enabled': the caller can thus do the evaluate-or-not decision at the callsite, which is much more straight-forward than dealing with lazy reflect-based evaluation. Also, lazy evaluation would not work with 'native' slog, as in, these two statements would be evaluated differently: ```golang log.Info("foo", "my lazy", lazyObj) slog.Info("foo", "my lazy", lazyObj) ``` --- internal/testlog/testlog.go | 6 +++- light/txpool.go | 5 +++- log/format.go | 55 ++++++++----------------------------- log/handler.go | 39 ++------------------------ log/logger.go | 28 ++++++------------- log/logger_test.go | 4 +-- log/root.go | 5 ++++ p2p/discover/table.go | 30 ++++++++++++-------- p2p/discover/udp.go | 3 ++ p2p/discv5/net.go | 23 ++++++++-------- 10 files changed, 70 insertions(+), 128 deletions(-) diff --git a/internal/testlog/testlog.go b/internal/testlog/testlog.go index 59aa7e0041f9..8f68792d3750 100644 --- a/internal/testlog/testlog.go +++ b/internal/testlog/testlog.go @@ -100,6 +100,10 @@ func LoggerWithHandler(t *testing.T, handler slog.Handler) log.Logger { func (l *logger) Write(level slog.Level, msg string, ctx ...interface{}) {} +func (l *logger) Enabled(ctx context.Context, level slog.Level) bool { + return l.l.Enabled(ctx, level) +} + func (l *logger) Trace(msg string, ctx ...interface{}) { l.t.Helper() l.mu.Lock() @@ -183,7 +187,7 @@ func (h *bufHandler) terminalFormat(r slog.Record) string { } for _, attr := range attrs { - fmt.Fprintf(buf, " %s=%s", attr.Key, string(log.FormatSlogValue(attr.Value, true, nil))) + fmt.Fprintf(buf, " %s=%s", attr.Key, string(log.FormatSlogValue(attr.Value, nil))) } buf.WriteByte('\n') return buf.String() diff --git a/light/txpool.go b/light/txpool.go index 578323914429..a1d9190a3874 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -450,7 +450,10 @@ func (p *TxPool) add(ctx context.Context, tx *types.Transaction) error { } // Print a log message if low enough level is set - log.Debug("Pooled new transaction", "hash", hash, "from", log.Lazy{Fn: func() common.Address { from, _ := types.Sender(p.signer, tx); return from }}, "to", tx.To()) + if log.Enabled(log.LevelDebug) { + from, _ := types.Sender(p.signer, tx) + log.Debug("Pooled new transaction", "hash", hash, "from", from, "to", tx.To()) + } return nil } diff --git a/log/format.go b/log/format.go index a2bbcce9c056..6447f3c1f1e9 100644 --- a/log/format.go +++ b/log/format.go @@ -23,22 +23,6 @@ const ( // 40 spaces var spaces = []byte(" ") -type Format interface { - Format(r slog.Record) []byte -} - -// FormatFunc returns a new Format object which uses -// the given function to perform record formatting. -func FormatFunc(f func(slog.Record) []byte) Format { - return formatFunc(f) -} - -type formatFunc func(slog.Record) []byte - -func (f formatFunc) Format(r slog.Record) []byte { - return f(r) -} - // TerminalStringer is an analogous interface to the stdlib stringer, allowing // own types to have custom shortened serialization formats when printed to the // screen. @@ -46,7 +30,7 @@ type TerminalStringer interface { TerminalString() string } -func (h *TerminalHandler) TerminalFormat(buf []byte, r slog.Record, usecolor bool) []byte { +func (h *TerminalHandler) format(buf []byte, r slog.Record, usecolor bool) []byte { msg := escapeMessage(r.Message) var color = "" if usecolor { @@ -88,13 +72,13 @@ func (h *TerminalHandler) TerminalFormat(buf []byte, r slog.Record, usecolor boo if (r.NumAttrs()+len(h.attrs)) > 0 && length < termMsgJust { b.Write(spaces[:termMsgJust-length]) } - // print the keys logfmt style - h.logfmt(b, r, color) + // print the attributes + h.formatAttributes(b, r, color) return b.Bytes() } -func (h *TerminalHandler) logfmt(buf *bytes.Buffer, r slog.Record, color string) { +func (h *TerminalHandler) formatAttributes(buf *bytes.Buffer, r slog.Record, color string) { // tmp is a temporary buffer we use, until bytes.Buffer.AvailableBuffer() (1.21) // can be used. var tmp = make([]byte, 40) @@ -112,7 +96,7 @@ func (h *TerminalHandler) logfmt(buf *bytes.Buffer, r slog.Record, color string) buf.WriteByte('=') } //val := FormatSlogValue(attr.Value, true, buf.AvailableBuffer()) - val := FormatSlogValue(attr.Value, true, tmp[:0]) + val := FormatSlogValue(attr.Value, tmp[:0]) padding := h.fieldPadding[attr.Key] @@ -140,8 +124,8 @@ func (h *TerminalHandler) logfmt(buf *bytes.Buffer, r slog.Record, color string) buf.WriteByte('\n') } -// FormatSlogValue formats a slog.Value for serialization -func FormatSlogValue(v slog.Value, term bool, tmp []byte) (result []byte) { +// FormatSlogValue formats a slog.Value for serialization to terminal. +func FormatSlogValue(v slog.Value, tmp []byte) (result []byte) { var value any defer func() { if err := recover(); err != nil { @@ -156,11 +140,9 @@ func FormatSlogValue(v slog.Value, term bool, tmp []byte) (result []byte) { switch v.Kind() { case slog.KindString: return appendEscapeString(tmp, v.String()) - case slog.KindAny: - value = v.Any() - case slog.KindInt64: // All int-types (int8 ,int16 etc) wind up here + case slog.KindInt64: // All int-types (int8, int16 etc) wind up here return appendInt64(tmp, v.Int64()) - case slog.KindUint64: // All uint-types (int8 ,int16 etc) wind up here + case slog.KindUint64: // All uint-types (uint8, uint16 etc) wind up here return appendUint64(tmp, v.Uint64(), false) case slog.KindFloat64: return strconv.AppendFloat(tmp, v.Float64(), floatFormat, 3, 64) @@ -180,27 +162,14 @@ func FormatSlogValue(v slog.Value, term bool, tmp []byte) (result []byte) { return []byte("") } switch v := value.(type) { - case *big.Int: - // Big ints get consumed by the Stringer clause, so we need to handle - // them earlier on. - if v == nil { - return append(tmp, []byte("")...) - } + case *big.Int: // Need to be before fmt.Stringer-clause return appendBigInt(tmp, v) - - case *uint256.Int: - // Uint256s get consumed by the Stringer clause, so we need to handle - // them earlier on. - if v == nil { - return append(tmp, []byte("")...) - } + case *uint256.Int: // Need to be before fmt.Stringer-clause return appendU256(tmp, v) case error: return appendEscapeString(tmp, v.Error()) case TerminalStringer: - if term { - return appendEscapeString(tmp, v.TerminalString()) // Custom terminal stringer provided, use that - } + return appendEscapeString(tmp, v.TerminalString()) case fmt.Stringer: return appendEscapeString(tmp, v.String()) } diff --git a/log/handler.go b/log/handler.go index 513c230c9e7a..88efc491c816 100644 --- a/log/handler.go +++ b/log/handler.go @@ -13,42 +13,6 @@ import ( "golang.org/x/exp/slog" ) -// Lazy allows you to defer calculation of a logged value that is expensive -// to compute until it is certain that it must be evaluated with the given filters. -// -// You may wrap any function which takes no arguments to Lazy. It may return any -// number of values of any type. -type Lazy struct { - Fn interface{} -} - -func evaluateLazy(lz Lazy) (interface{}, error) { - t := reflect.TypeOf(lz.Fn) - - if t.Kind() != reflect.Func { - return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn) - } - - if t.NumIn() > 0 { - return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn) - } - - if t.NumOut() == 0 { - return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn) - } - - value := reflect.ValueOf(lz.Fn) - results := value.Call([]reflect.Value{}) - if len(results) == 1 { - return results[0].Interface(), nil - } - values := make([]interface{}, len(results)) - for i, v := range results { - values[i] = v.Interface() - } - return values, nil -} - type discardHandler struct{} // DiscardHandler returns a no-op handler @@ -112,7 +76,7 @@ func NewTerminalHandlerWithLevel(wr io.Writer, lvl slog.Level, useColor bool) *T func (h *TerminalHandler) Handle(_ context.Context, r slog.Record) error { h.mu.Lock() defer h.mu.Unlock() - buf := h.TerminalFormat(h.buf, r, h.useColor) + buf := h.format(h.buf, r, h.useColor) h.wr.Write(buf) h.buf = buf[:0] return nil @@ -149,6 +113,7 @@ func (l *leveler) Level() slog.Level { return l.minLevel } +// JSONHandler returns a handler which prints records in JSON format. func JSONHandler(wr io.Writer) slog.Handler { return slog.NewJSONHandler(wr, &slog.HandlerOptions{ ReplaceAttr: builtinReplaceJSON, diff --git a/log/logger.go b/log/logger.go index b5b83cd71f9c..717ec94caaed 100644 --- a/log/logger.go +++ b/log/logger.go @@ -134,6 +134,9 @@ type Logger interface { // Write logs a message at the specified level Write(level slog.Level, msg string, attrs ...any) + + // Enabled reports whether l emits log records at the given context and level. + Enabled(ctx context.Context, level slog.Level) bool } type logger struct { @@ -159,26 +162,6 @@ func (l *logger) Write(level slog.Level, msg string, attrs ...any) { if len(attrs)%2 != 0 { attrs = append(attrs, nil, errorKey, "Normalized odd number of arguments by adding nil") } - - // evaluate lazy values - var hadErr bool - for i := 1; i < len(attrs); i += 2 { - lz, ok := attrs[i].(Lazy) - if ok { - v, err := evaluateLazy(lz) - if err != nil { - hadErr = true - attrs[i] = err - } else { - attrs[i] = v - } - } - } - - if hadErr { - attrs = append(attrs, errorKey, "bad lazy") - } - r := slog.NewRecord(time.Now(), level, msg, pcs[0]) r.Add(attrs...) l.inner.Handler().Handle(context.Background(), r) @@ -196,6 +179,11 @@ func (l *logger) New(ctx ...interface{}) Logger { return l.With(ctx...) } +// Enabled reports whether l emits log records at the given context and level. +func (l *logger) Enabled(ctx context.Context, level slog.Level) bool { + return l.inner.Enabled(ctx, level) +} + func (l *logger) Trace(msg string, ctx ...interface{}) { l.Write(LevelTrace, msg, ctx...) } diff --git a/log/logger_test.go b/log/logger_test.go index 27e90c5fd203..a633f5ad7a4c 100644 --- a/log/logger_test.go +++ b/log/logger_test.go @@ -107,7 +107,6 @@ func TestLoggerOutput(t *testing.T) { bigint = big.NewInt(100) nilbig *big.Int err = fmt.Errorf("Oh nooes it's crap") - lazy = Lazy{Fn: func() interface{} { return "lazy value" }} smallUint = uint256.NewInt(500_000) bigUint = &uint256.Int{0xff, 0xff, 0xff, 0xff} ) @@ -126,13 +125,12 @@ func TestLoggerOutput(t *testing.T) { "struct", customA, "struct", customB, "ptrstruct", &customA, - "lazy", lazy, "smalluint", smallUint, "bigUint", bigUint) have := out.String() t.Logf("output %v", out.String()) - want := `INFO [11-07|19:14:33.821] This is a message foo=123 bytes="[0 0 0 0 0 0 0 0 0 0]" bonk="a string with text" time=0001-01-01T00:00:00+0000 bigint=100 nilbig= err="Oh nooes it's crap" struct="{A:Foo B:12}" struct="{A:Foo\nLinebreak B:122}" ptrstruct="&{A:Foo B:12}" lazy="lazy value" smalluint=500,000 bigUint=1,600,660,942,523,603,594,864,898,306,482,794,244,293,965,082,972,225,630,372,095 + want := `INFO [11-07|19:14:33.821] This is a message foo=123 bytes="[0 0 0 0 0 0 0 0 0 0]" bonk="a string with text" time=0001-01-01T00:00:00+0000 bigint=100 nilbig= err="Oh nooes it's crap" struct="{A:Foo B:12}" struct="{A:Foo\nLinebreak B:122}" ptrstruct="&{A:Foo B:12}" smalluint=500,000 bigUint=1,600,660,942,523,603,594,864,898,306,482,794,244,293,965,082,972,225,630,372,095 ` if !bytes.Equal([]byte(have)[25:], []byte(want)[25:]) { t.Errorf("Error\nhave: %q\nwant: %q", have, want) diff --git a/log/root.go b/log/root.go index 71040fff47c2..69b9bccc370b 100644 --- a/log/root.go +++ b/log/root.go @@ -1,6 +1,7 @@ package log import ( + "context" "os" "sync/atomic" @@ -115,3 +116,7 @@ func Crit(msg string, ctx ...interface{}) { func New(ctx ...interface{}) Logger { return Root().With(ctx...) } + +func Enabled(level slog.Level) bool { + return Root().Enabled(context.Background(), level) +} diff --git a/p2p/discover/table.go b/p2p/discover/table.go index dff614c1d66e..b930cdf0b8ed 100644 --- a/p2p/discover/table.go +++ b/p2p/discover/table.go @@ -23,6 +23,7 @@ package discover import ( + "context" crand "crypto/rand" "encoding/binary" "errors" @@ -72,7 +73,10 @@ type Table struct { rand *mrand.Rand // source of randomness, periodically reseeded ips netutil.DistinctNetSet - db *nodeDB // database of known nodes + db *nodeDB // database of known nodes + log log.Logger + + // loop channels refreshReq chan chan struct{} initDone chan struct{} closeReq chan struct{} @@ -118,9 +122,11 @@ func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr, nodeDBPath string if err != nil { return nil, err } + tab := &Table{ net: t, db: db, + log: log.Root(), self: NewNode(ourID, ourAddr.IP, uint16(ourAddr.Port), uint16(ourAddr.Port)), bonding: make(map[NodeID]*bondproc), bondslots: make(chan struct{}, maxBondingPingPongs), @@ -322,10 +328,10 @@ func (tab *Table) lookup(targetID NodeID, refreshIfEmpty bool) []*Node { // Bump the failure counter to detect and evacuate non-bonded entries fails := tab.db.findFails(n.ID) + 1 tab.db.updateFindFails(n.ID, fails) - log.Trace("Bumping findnode failure counter", "id", n.ID, "failcount", fails) + tab.log.Trace("Bumping findnode failure counter", "id", n.ID, "failcount", fails) if fails >= maxFindnodeFailures { - log.Trace("Too many findnode failures, dropping", "id", n.ID, "failcount", fails) + tab.log.Trace("Too many findnode failures, dropping", "id", n.ID, "failcount", fails) tab.delete(n) } } @@ -455,8 +461,10 @@ func (tab *Table) loadSeedNodes(bond bool) { } for i := range seeds { seed := seeds[i] - age := log.Lazy{Fn: func() interface{} { return time.Since(tab.db.bondTime(seed.ID)) }} - log.Debug("Found seed node in database", "id", seed.ID, "addr", seed.addr(), "age", age) + if tab.log.Enabled(context.Background(), log.LevelTrace) { + age := time.Since(tab.db.bondTime(seed.ID)) + tab.log.Debug("Found seed node in database", "id", seed.ID, "addr", seed.addr(), "age", age) + } tab.add(seed) } } @@ -480,16 +488,16 @@ func (tab *Table) doRevalidate(done chan<- struct{}) { b := tab.buckets[bi] if err == nil { // The node responded, move it to the front. - log.Debug("Revalidated node", "b", bi, "id", last.ID) + tab.log.Debug("Revalidated node", "b", bi, "id", last.ID) b.bump(last) return } // No reply received, pick a replacement or delete the node if there aren't // any replacements. if r := tab.replace(b, last); r != nil { - log.Debug("Replaced dead node", "b", bi, "id", last.ID, "ip", last.IP, "r", r.ID, "rip", r.IP) + tab.log.Debug("Replaced dead node", "b", bi, "id", last.ID, "ip", last.IP, "r", r.ID, "rip", r.IP) } else { - log.Debug("Removed dead node", "b", bi, "id", last.ID, "ip", last.IP) + tab.log.Debug("Removed dead node", "b", bi, "id", last.ID, "ip", last.IP) } } @@ -599,7 +607,7 @@ func (tab *Table) bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16 age := time.Since(tab.db.bondTime(id)) var result error if fails > 0 || age > nodeDBNodeExpiration { - log.Trace("Starting bonding ping/pong", "id", id, "known", node != nil, "failcount", fails, "age", age) + tab.log.Trace("Starting bonding ping/pong", "id", id, "known", node != nil, "failcount", fails, "age", age) tab.bondmu.Lock() w := tab.bonding[id] @@ -724,11 +732,11 @@ func (tab *Table) addIP(b *bucket, ip net.IP) bool { return true } if !tab.ips.Add(ip) { - log.Debug("IP exceeds table limit", "ip", ip) + tab.log.Debug("IP exceeds table limit", "ip", ip) return false } if !b.ips.Add(ip) { - log.Debug("IP exceeds bucket limit", "ip", ip) + tab.log.Debug("IP exceeds bucket limit", "ip", ip) tab.ips.Remove(ip) return false } diff --git a/p2p/discover/udp.go b/p2p/discover/udp.go index 701747bb0442..bb541ab07a65 100644 --- a/p2p/discover/udp.go +++ b/p2p/discover/udp.go @@ -228,6 +228,9 @@ type Config struct { NetRestrict *netutil.Netlist // network whitelist Bootnodes []*Node // list of bootstrap nodes Unhandled chan<- ReadPacket // unhandled packets are sent on this channel + + // The options below are useful in very specific cases, like in unit tests. + Log log.Logger // if set, log messages go here } // ListenUDP returns a new table that listens for UDP packets on laddr. diff --git a/p2p/discv5/net.go b/p2p/discv5/net.go index 54981a4f7b12..6c5c0b11743e 100644 --- a/p2p/discv5/net.go +++ b/p2p/discv5/net.go @@ -420,7 +420,6 @@ loop: // Ingress packet handling. case pkt := <-net.read: - //fmt.Println("read", pkt.ev) log.Trace("<-net.read") n := net.internNode(&pkt) prestate := n.state @@ -428,10 +427,10 @@ loop: if err := net.handle(n, pkt.ev, &pkt); err != nil { status = err.Error() } - log.Trace("", "msg", log.Lazy{Fn: func() string { - return fmt.Sprintf("<<< (%d) %v from %x@%v: %v -> %v (%v)", - net.tab.count, pkt.ev, pkt.remoteID[:8], pkt.remoteAddr, prestate, n.state, status) - }}) + if log.Enabled(log.LevelTrace) { + msg := fmt.Sprintf("<<< (%d) %v from %x@%v: %v -> %v (%v)", net.tab.count, pkt.ev, pkt.remoteID[:8], pkt.remoteAddr, prestate, n.state, status) + log.Trace("", "msg", msg) + } // TODO: persist state if n.state goes >= known, delete if it goes <= known // State transition timeouts. @@ -447,10 +446,10 @@ loop: if err := net.handle(timeout.node, timeout.ev, nil); err != nil { status = err.Error() } - log.Trace("", "msg", log.Lazy{Fn: func() string { - return fmt.Sprintf("--- (%d) %v for %x@%v: %v -> %v (%v)", - net.tab.count, timeout.ev, timeout.node.ID[:8], timeout.node.addr(), prestate, timeout.node.state, status) - }}) + if log.Enabled(log.LevelTrace) { + msg := fmt.Sprintf("--- (%d) %v for %x@%v: %v -> %v (%v)", net.tab.count, timeout.ev, timeout.node.ID[:8], timeout.node.addr(), prestate, timeout.node.state, status) + log.Trace("", "msg", msg) + } // Querying. case q := <-net.queryReq: @@ -683,15 +682,15 @@ func (net *Network) refresh(done chan<- struct{}) { return } for _, n := range seeds { - log.Debug("", "msg", log.Lazy{Fn: func() string { + if log.Enabled(log.LevelDebug) { var age string if net.db != nil { age = time.Since(net.db.lastPong(n.ID)).String() } else { age = "unknown" } - return fmt.Sprintf("seed node (age %s): %v", age, n) - }}) + log.Debug("", "msg", fmt.Sprintf("seed node (age %s): %v", age, n)) + } n = net.internNodeFromDB(n) if n.state == unknown { net.transition(n, verifyinit) From 31ceca5ef1e026b6eae5d289dd01cf24acfb4e8a Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 224/479] log: avoid setting default slog logger in init (#28747) slog.SetDefault has undesirable side effects. It also sets the default logger destination, for example. So we should not call it by default in init. --- log/root.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/log/root.go b/log/root.go index 69b9bccc370b..99663e58afcf 100644 --- a/log/root.go +++ b/log/root.go @@ -11,8 +11,7 @@ import ( var root atomic.Value func init() { - defaultLogger := &logger{slog.New(DiscardHandler())} - SetDefault(defaultLogger) + root.Store(&logger{slog.New(DiscardHandler())}) } // SetDefault sets the default global logger From 966a03f297a860a1d4b333fc3c5707916f2734a4 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 225/479] log: emit error level string as "error", not "eror" (#28774) --- log/logger.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/log/logger.go b/log/logger.go index 717ec94caaed..351d17c53126 100644 --- a/log/logger.go +++ b/log/logger.go @@ -83,7 +83,7 @@ func LevelAlignedString(l slog.Level) string { } } -// LevelString returns a 5-character string containing the name of a Lvl. +// LevelString returns a string containing the name of a Lvl. func LevelString(l slog.Level) string { switch l { case LevelTrace: @@ -95,7 +95,7 @@ func LevelString(l slog.Level) string { case slog.LevelWarn: return "warn" case slog.LevelError: - return "eror" + return "error" case LevelCrit: return "crit" default: From fb25ddfa57671014aaebf67701d6a6b31cb61c70 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 226/479] log: fix docstring names (#28923) --- log/handler_glog.go | 2 +- log/logger_test.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/log/handler_glog.go b/log/handler_glog.go index fb1e03c5b532..f51bae2a4a5b 100644 --- a/log/handler_glog.go +++ b/log/handler_glog.go @@ -192,7 +192,7 @@ func (h *GlogHandler) Handle(_ context.Context, r slog.Record) error { frame, _ := fs.Next() for _, rule := range h.patterns { - if rule.pattern.MatchString(fmt.Sprintf("%+s", frame.File)) { + if rule.pattern.MatchString(fmt.Sprintf("+%s", frame.File)) { h.siteCache[r.PC], lvl, ok = rule.level, rule.level, true } } diff --git a/log/logger_test.go b/log/logger_test.go index a633f5ad7a4c..ff981fd018ca 100644 --- a/log/logger_test.go +++ b/log/logger_test.go @@ -2,6 +2,7 @@ package log import ( "bytes" + "errors" "fmt" "io" "math/big" @@ -77,7 +78,7 @@ func benchmarkLogger(b *testing.B, l Logger) { tt = time.Now() bigint = big.NewInt(100) nilbig *big.Int - err = fmt.Errorf("Oh nooes it's crap") + err = errors.New("Oh nooes it's crap") ) b.ReportAllocs() b.ResetTimer() @@ -106,7 +107,7 @@ func TestLoggerOutput(t *testing.T) { tt = time.Time{} bigint = big.NewInt(100) nilbig *big.Int - err = fmt.Errorf("Oh nooes it's crap") + err = errors.New("Oh nooes it's crap") smallUint = uint256.NewInt(500_000) bigUint = &uint256.Int{0xff, 0xff, 0xff, 0xff} ) From 20a62e47435db836798dc684e76c972c70570c81 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 227/479] log: add Handler getter to Logger interface (#28793) log: Add Handler getter to Logger interface --- internal/testlog/testlog.go | 4 ++++ log/logger.go | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/internal/testlog/testlog.go b/internal/testlog/testlog.go index 8f68792d3750..9270e9a55f85 100644 --- a/internal/testlog/testlog.go +++ b/internal/testlog/testlog.go @@ -98,6 +98,10 @@ func LoggerWithHandler(t *testing.T, handler slog.Handler) log.Logger { } } +func (l *logger) Handler() slog.Handler { + return l.l.Handler() +} + func (l *logger) Write(level slog.Level, msg string, ctx ...interface{}) {} func (l *logger) Enabled(ctx context.Context, level slog.Level) bool { diff --git a/log/logger.go b/log/logger.go index 351d17c53126..53ce8614d746 100644 --- a/log/logger.go +++ b/log/logger.go @@ -137,6 +137,9 @@ type Logger interface { // Enabled reports whether l emits log records at the given context and level. Enabled(ctx context.Context, level slog.Level) bool + + // Handler returns the underlying handler of the inner logger. + Handler() slog.Handler } type logger struct { @@ -150,6 +153,10 @@ func NewLogger(h slog.Handler) Logger { } } +func (l *logger) Handler() slog.Handler { + return l.inner.Handler() +} + // Write logs a message at the specified level. func (l *logger) Write(level slog.Level, msg string, attrs ...any) { if !l.inner.Enabled(context.Background(), level) { From 40fd68a1249c65667e6899c8d17f6f47e086b204 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 228/479] log: replace tmp with bytes.Buffer.AvailableBuffer (#29287) --- log/format.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/log/format.go b/log/format.go index 6447f3c1f1e9..391e9a8dbbba 100644 --- a/log/format.go +++ b/log/format.go @@ -79,24 +79,18 @@ func (h *TerminalHandler) format(buf []byte, r slog.Record, usecolor bool) []byt } func (h *TerminalHandler) formatAttributes(buf *bytes.Buffer, r slog.Record, color string) { - // tmp is a temporary buffer we use, until bytes.Buffer.AvailableBuffer() (1.21) - // can be used. - var tmp = make([]byte, 40) writeAttr := func(attr slog.Attr, first, last bool) { buf.WriteByte(' ') if color != "" { buf.WriteString(color) - //buf.Write(appendEscapeString(buf.AvailableBuffer(), attr.Key)) - buf.Write(appendEscapeString(tmp[:0], attr.Key)) + buf.Write(appendEscapeString(buf.AvailableBuffer(), attr.Key)) buf.WriteString("\x1b[0m=") } else { - //buf.Write(appendEscapeString(buf.AvailableBuffer(), attr.Key)) - buf.Write(appendEscapeString(tmp[:0], attr.Key)) + buf.Write(appendEscapeString(buf.AvailableBuffer(), attr.Key)) buf.WriteByte('=') } - //val := FormatSlogValue(attr.Value, true, buf.AvailableBuffer()) - val := FormatSlogValue(attr.Value, tmp[:0]) + val := FormatSlogValue(attr.Value, buf.AvailableBuffer()) padding := h.fieldPadding[attr.Key] From 946c085f9fd6be349e588d0c1d8d7a058dc8ee2a Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 229/479] log: use native log/slog instead of golang/exp (#29302) --- internal/debug/api.go | 2 +- internal/debug/flags.go | 2 +- internal/testlog/testlog.go | 2 +- log/format.go | 2 +- log/handler.go | 2 +- log/handler_glog.go | 3 +-- log/logger.go | 3 +-- log/logger_test.go | 2 +- log/root.go | 3 +-- p2p/simulations/adapters/exec.go | 6 +++--- p2p/simulations/adapters/types.go | 2 +- 11 files changed, 13 insertions(+), 16 deletions(-) diff --git a/internal/debug/api.go b/internal/debug/api.go index 6a11bfded457..8f1b60ddb37e 100644 --- a/internal/debug/api.go +++ b/internal/debug/api.go @@ -23,6 +23,7 @@ package debug import ( "errors" "io" + "log/slog" "os" "os/user" "path/filepath" @@ -35,7 +36,6 @@ import ( "time" "github.com/XinFinOrg/XDPoSChain/log" - "golang.org/x/exp/slog" ) // Handler is the global debugging handler. diff --git a/internal/debug/flags.go b/internal/debug/flags.go index 7e266847c766..8dc380c4b5de 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -19,6 +19,7 @@ package debug import ( "fmt" "io" + "log/slog" "net/http" _ "net/http/pprof" "os" @@ -30,7 +31,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/metrics/exp" "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" - "golang.org/x/exp/slog" "gopkg.in/natefinch/lumberjack.v2" "gopkg.in/urfave/cli.v1" ) diff --git a/internal/testlog/testlog.go b/internal/testlog/testlog.go index 9270e9a55f85..855e8c9b7f56 100644 --- a/internal/testlog/testlog.go +++ b/internal/testlog/testlog.go @@ -21,11 +21,11 @@ import ( "bytes" "context" "fmt" + "log/slog" "sync" "testing" "github.com/XinFinOrg/XDPoSChain/log" - "golang.org/x/exp/slog" ) const ( diff --git a/log/format.go b/log/format.go index 391e9a8dbbba..515ae66e98fc 100644 --- a/log/format.go +++ b/log/format.go @@ -3,6 +3,7 @@ package log import ( "bytes" "fmt" + "log/slog" "math/big" "reflect" "strconv" @@ -10,7 +11,6 @@ import ( "unicode/utf8" "github.com/holiman/uint256" - "golang.org/x/exp/slog" ) const ( diff --git a/log/handler.go b/log/handler.go index 88efc491c816..326859b1383c 100644 --- a/log/handler.go +++ b/log/handler.go @@ -4,13 +4,13 @@ import ( "context" "fmt" "io" + "log/slog" "math/big" "reflect" "sync" "time" "github.com/holiman/uint256" - "golang.org/x/exp/slog" ) type discardHandler struct{} diff --git a/log/handler_glog.go b/log/handler_glog.go index f51bae2a4a5b..608d955572ad 100644 --- a/log/handler_glog.go +++ b/log/handler_glog.go @@ -20,14 +20,13 @@ import ( "context" "errors" "fmt" + "log/slog" "regexp" "runtime" "strconv" "strings" "sync" "sync/atomic" - - "golang.org/x/exp/slog" ) // errVmoduleSyntax is returned when a user vmodule pattern is invalid. diff --git a/log/logger.go b/log/logger.go index 53ce8614d746..e8cc94b4ae2f 100644 --- a/log/logger.go +++ b/log/logger.go @@ -2,12 +2,11 @@ package log import ( "context" + "log/slog" "math" "os" "runtime" "time" - - "golang.org/x/exp/slog" ) const errorKey = "LOG_ERROR" diff --git a/log/logger_test.go b/log/logger_test.go index ff981fd018ca..d23e16e57241 100644 --- a/log/logger_test.go +++ b/log/logger_test.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "log/slog" "math/big" "os" "strings" @@ -12,7 +13,6 @@ import ( "time" "github.com/holiman/uint256" - "golang.org/x/exp/slog" ) // TestLoggingWithVmodule checks that vmodule works. diff --git a/log/root.go b/log/root.go index 99663e58afcf..aaf2fe56f60c 100644 --- a/log/root.go +++ b/log/root.go @@ -2,10 +2,9 @@ package log import ( "context" + "log/slog" "os" "sync/atomic" - - "golang.org/x/exp/slog" ) var root atomic.Value diff --git a/p2p/simulations/adapters/exec.go b/p2p/simulations/adapters/exec.go index 027cc6fec1fb..1ab534f8d07f 100644 --- a/p2p/simulations/adapters/exec.go +++ b/p2p/simulations/adapters/exec.go @@ -24,6 +24,7 @@ import ( "errors" "fmt" "io" + "log/slog" "net" "os" "os/exec" @@ -42,7 +43,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/rpc" "github.com/docker/docker/pkg/reexec" "github.com/gorilla/websocket" - "golang.org/x/exp/slog" ) // ExecAdapter is a NodeAdapter which runs simulation nodes by executing the @@ -384,7 +384,7 @@ func initLogging() { // and the node config from an environment variable. func execP2PNode() { initLogging() - + // read the services from argv serviceNames := strings.Split(os.Args[1], ",") @@ -395,7 +395,7 @@ func execP2PNode() { } var conf execNodeConfig if err := json.Unmarshal([]byte(confEnv), &conf); err != nil { - log.Crit("error decoding " + envNodeConfig, "err", err) + log.Crit("error decoding "+envNodeConfig, "err", err) } conf.Stack.P2P.PrivateKey = conf.Node.PrivateKey conf.Stack.Logger = log.New("node.id", conf.Node.ID.String()) diff --git a/p2p/simulations/adapters/types.go b/p2p/simulations/adapters/types.go index cbc4d4b25ea4..59ae2e8ad3df 100644 --- a/p2p/simulations/adapters/types.go +++ b/p2p/simulations/adapters/types.go @@ -21,6 +21,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "log/slog" "os" "github.com/XinFinOrg/XDPoSChain/crypto" @@ -30,7 +31,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/rpc" "github.com/docker/docker/pkg/reexec" "github.com/gorilla/websocket" - "golang.org/x/exp/slog" ) // Node represents a node in a simulation network which is created by a From 6831a49fea50b88188894bcd52743ec3a3a86528 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 230/479] log: replace the outdated link (#29412) --- log/format.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log/format.go b/log/format.go index 515ae66e98fc..54c071b908c7 100644 --- a/log/format.go +++ b/log/format.go @@ -340,7 +340,7 @@ func writeTimeTermFormat(buf *bytes.Buffer, t time.Time) { // writePosIntWidth writes non-negative integer i to the buffer, padded on the left // by zeroes to the given width. Use a width of 0 to omit padding. -// Adapted from golang.org/x/exp/slog/internal/buffer/buffer.go +// Adapted from pkg.go.dev/log/slog/internal/buffer func writePosIntWidth(b *bytes.Buffer, i, width int) { // Cheap integer to fixed-width decimal ASCII. // Copied from log/log.go. From 368b2781aeb54858b08e80f1527a3ea39f8cbb76 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 231/479] log: using maps.Clone (#29392) --- log/handler_glog.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/log/handler_glog.go b/log/handler_glog.go index 608d955572ad..625a03640381 100644 --- a/log/handler_glog.go +++ b/log/handler_glog.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "log/slog" + "maps" "regexp" "runtime" "strconv" @@ -145,10 +146,7 @@ func (h *GlogHandler) Enabled(ctx context.Context, lvl slog.Level) bool { func (h *GlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler { h.lock.RLock() - siteCache := make(map[uintptr]slog.Level) - for k, v := range h.siteCache { - siteCache[k] = v - } + siteCache := maps.Clone(h.siteCache) h.lock.RUnlock() patterns := []pattern{} From 9e034475c8e8b0fbab47e1591974af1d9fc16249 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:42 +0800 Subject: [PATCH 232/479] log: default JSON log handler should log all verbosity levels (#29471) Co-authored-by: lightclient --- internal/debug/flags.go | 4 ++-- log/handler.go | 7 +++++++ log/logger_test.go | 29 ++++++++++++++++++++++++----- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/internal/debug/flags.go b/internal/debug/flags.go index 8dc380c4b5de..223e9c6fa551 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -226,9 +226,9 @@ func Setup(ctx *cli.Context) error { case ctx.Bool(logjsonFlag.Name): // Retain backwards compatibility with `--log-json` flag if `--log-format` not set defer log.Warn("The flag '--log-json' is deprecated, please use '--log-format=json' instead") - handler = log.JSONHandler(output) + handler = log.JSONHandlerWithLevel(output, log.LevelInfo) case logFmtFlag == "json": - handler = log.JSONHandler(output) + handler = log.JSONHandlerWithLevel(output, log.LevelInfo) case logFmtFlag == "logfmt": handler = log.LogfmtHandler(output) case logFmtFlag == "", logFmtFlag == "terminal": diff --git a/log/handler.go b/log/handler.go index 326859b1383c..56eff6671f1b 100644 --- a/log/handler.go +++ b/log/handler.go @@ -115,8 +115,15 @@ func (l *leveler) Level() slog.Level { // JSONHandler returns a handler which prints records in JSON format. func JSONHandler(wr io.Writer) slog.Handler { + return JSONHandlerWithLevel(wr, levelMaxVerbosity) +} + +// JSONHandlerWithLevel returns a handler which prints records in JSON format that are less than or equal to +// the specified verbosity level. +func JSONHandlerWithLevel(wr io.Writer, level slog.Level) slog.Handler { return slog.NewJSONHandler(wr, &slog.HandlerOptions{ ReplaceAttr: builtinReplaceJSON, + Level: &leveler{level}, }) } diff --git a/log/logger_test.go b/log/logger_test.go index d23e16e57241..f1a9a93bce7a 100644 --- a/log/logger_test.go +++ b/log/logger_test.go @@ -26,7 +26,7 @@ func TestLoggingWithVmodule(t *testing.T) { logger.Trace("a message", "foo", "bar") have := out.String() // The timestamp is locale-dependent, so we want to trim that off - // "INFO [01-01|00:00:00.000] a messag ..." -> "a messag..." + // "INFO [01-01|00:00:00.000] a message ..." -> "a message..." have = strings.Split(have, "]")[1] want := " a message foo=bar\n" if have != want { @@ -42,7 +42,7 @@ func TestTerminalHandlerWithAttrs(t *testing.T) { logger.Trace("a message", "foo", "bar") have := out.String() // The timestamp is locale-dependent, so we want to trim that off - // "INFO [01-01|00:00:00.000] a messag ..." -> "a messag..." + // "INFO [01-01|00:00:00.000] a message ..." -> "a message..." have = strings.Split(have, "]")[1] want := " a message baz=bat foo=bar\n" if have != want { @@ -50,6 +50,25 @@ func TestTerminalHandlerWithAttrs(t *testing.T) { } } +// Make sure the default json handler outputs debug log lines +func TestJSONHandler(t *testing.T) { + out := new(bytes.Buffer) + handler := JSONHandler(out) + logger := slog.New(handler) + logger.Debug("hi there") + if len(out.String()) == 0 { + t.Error("expected non-empty debug log output from default JSON Handler") + } + + out.Reset() + handler = JSONHandlerWithLevel(out, slog.LevelInfo) + logger = slog.New(handler) + logger.Debug("hi there") + if len(out.String()) != 0 { + t.Errorf("expected empty debug log output, but got: %v", out.String()) + } +} + func BenchmarkTraceLogging(b *testing.B) { SetDefault(NewLogger(NewTerminalHandler(os.Stderr, true))) b.ResetTimer() @@ -78,7 +97,7 @@ func benchmarkLogger(b *testing.B, l Logger) { tt = time.Now() bigint = big.NewInt(100) nilbig *big.Int - err = errors.New("Oh nooes it's crap") + err = errors.New("oh nooes it's crap") ) b.ReportAllocs() b.ResetTimer() @@ -107,7 +126,7 @@ func TestLoggerOutput(t *testing.T) { tt = time.Time{} bigint = big.NewInt(100) nilbig *big.Int - err = errors.New("Oh nooes it's crap") + err = errors.New("oh nooes it's crap") smallUint = uint256.NewInt(500_000) bigUint = &uint256.Int{0xff, 0xff, 0xff, 0xff} ) @@ -131,7 +150,7 @@ func TestLoggerOutput(t *testing.T) { have := out.String() t.Logf("output %v", out.String()) - want := `INFO [11-07|19:14:33.821] This is a message foo=123 bytes="[0 0 0 0 0 0 0 0 0 0]" bonk="a string with text" time=0001-01-01T00:00:00+0000 bigint=100 nilbig= err="Oh nooes it's crap" struct="{A:Foo B:12}" struct="{A:Foo\nLinebreak B:122}" ptrstruct="&{A:Foo B:12}" smalluint=500,000 bigUint=1,600,660,942,523,603,594,864,898,306,482,794,244,293,965,082,972,225,630,372,095 + want := `INFO [11-07|19:14:33.821] This is a message foo=123 bytes="[0 0 0 0 0 0 0 0 0 0]" bonk="a string with text" time=0001-01-01T00:00:00+0000 bigint=100 nilbig= err="oh nooes it's crap" struct="{A:Foo B:12}" struct="{A:Foo\nLinebreak B:122}" ptrstruct="&{A:Foo B:12}" smalluint=500,000 bigUint=1,600,660,942,523,603,594,864,898,306,482,794,244,293,965,082,972,225,630,372,095 ` if !bytes.Equal([]byte(have)[25:], []byte(want)[25:]) { t.Errorf("Error\nhave: %q\nwant: %q", have, want) From 54f8e028ca939716ae5873f62b19f7f7fcb6191c Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:43 +0800 Subject: [PATCH 233/479] log: fix some functions comments (#29907) updates some docstrings --------- Co-authored-by: rjl493456442 --- log/handler_glog.go | 12 ++++++++++-- log/logger.go | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/log/handler_glog.go b/log/handler_glog.go index 625a03640381..739f8c5b427d 100644 --- a/log/handler_glog.go +++ b/log/handler_glog.go @@ -139,11 +139,15 @@ func (h *GlogHandler) Vmodule(ruleset string) error { return nil } +// Enabled implements slog.Handler, reporting whether the handler handles records +// at the given level. func (h *GlogHandler) Enabled(ctx context.Context, lvl slog.Level) bool { // fast-track skipping logging if override not enabled and the provided verbosity is above configured return h.override.Load() || slog.Level(h.level.Load()) <= lvl } +// WithAttrs implements slog.Handler, returning a new Handler whose attributes +// consist of both the receiver's attributes and the arguments. func (h *GlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler { h.lock.RLock() siteCache := maps.Clone(h.siteCache) @@ -164,12 +168,16 @@ func (h *GlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler { return &res } +// WithGroup implements slog.Handler, returning a new Handler with the given +// group appended to the receiver's existing groups. +// +// Note, this function is not implemented. func (h *GlogHandler) WithGroup(name string) slog.Handler { panic("not implemented") } -// Log implements Handler.Log, filtering a log record through the global, local -// and backtrace filters, finally emitting it if either allow it through. +// Handle implements slog.Handler, filtering a log record through the global, +// local and backtrace filters, finally emitting it if either allow it through. func (h *GlogHandler) Handle(_ context.Context, r slog.Record) error { // If the global log level allows, fast track logging if slog.Level(h.level.Load()) <= r.Level { diff --git a/log/logger.go b/log/logger.go index e8cc94b4ae2f..016856c8348d 100644 --- a/log/logger.go +++ b/log/logger.go @@ -35,7 +35,7 @@ const ( LvlDebug = LevelDebug ) -// convert from old Geth verbosity level constants +// FromLegacyLevel converts from old Geth verbosity level constants // to levels defined by slog func FromLegacyLevel(lvl int) slog.Level { switch lvl { @@ -107,7 +107,7 @@ type Logger interface { // With returns a new Logger that has this logger's attributes plus the given attributes With(ctx ...interface{}) Logger - // With returns a new Logger that has this logger's attributes plus the given attributes. Identical to 'With'. + // New returns a new Logger that has this logger's attributes plus the given attributes. Identical to 'With'. New(ctx ...interface{}) Logger // Log logs a message at the specified level with context key/value pairs From 5bfaf068a4d2ae417d4fb2ce0d9a1277f2558c90 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:02:43 +0800 Subject: [PATCH 234/479] log: fix issues with benchmarks (#30667) --- log/logger_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/log/logger_test.go b/log/logger_test.go index f1a9a93bce7a..3ec6d2e19c81 100644 --- a/log/logger_test.go +++ b/log/logger_test.go @@ -7,7 +7,6 @@ import ( "io" "log/slog" "math/big" - "os" "strings" "testing" "time" @@ -70,7 +69,7 @@ func TestJSONHandler(t *testing.T) { } func BenchmarkTraceLogging(b *testing.B) { - SetDefault(NewLogger(NewTerminalHandler(os.Stderr, true))) + SetDefault(NewLogger(NewTerminalHandler(io.Discard, true))) b.ResetTimer() for i := 0; i < b.N; i++ { Trace("a message", "v", i) From cba5782884fc56f99d14733111c2289adfbf413c Mon Sep 17 00:00:00 2001 From: JukLee0ira Date: Thu, 14 Nov 2024 15:17:12 +0800 Subject: [PATCH 235/479] accounts, build, mobile: remove Android and iOS support (#26599) --- .github/CODEOWNERS | 1 - accounts/abi/bind/bind.go | 76 +------ accounts/abi/bind/template.go | 105 +--------- cmd/abigen/main.go | 6 +- mobile/accounts.go | 221 --------------------- mobile/android_test.go | 266 ------------------------- mobile/big.go | 112 ----------- mobile/bind.go | 191 ------------------ mobile/common.go | 230 --------------------- mobile/context.go | 80 -------- mobile/discover.go | 104 ---------- mobile/doc.go | 61 ------ mobile/ethclient.go | 312 ----------------------------- mobile/ethereum.go | 148 -------------- mobile/geth.go | 196 ------------------ mobile/geth_android.go | 22 --- mobile/geth_ios.go | 22 --- mobile/geth_other.go | 22 --- mobile/init.go | 34 ---- mobile/interface.go | 148 -------------- mobile/logger.go | 28 --- mobile/p2p.go | 74 ------- mobile/params.go | 61 ------ mobile/primitives.go | 54 ----- mobile/types.go | 362 ---------------------------------- mobile/vm.go | 56 ------ 26 files changed, 10 insertions(+), 2982 deletions(-) delete mode 100644 mobile/accounts.go delete mode 100644 mobile/android_test.go delete mode 100644 mobile/big.go delete mode 100644 mobile/bind.go delete mode 100644 mobile/common.go delete mode 100644 mobile/context.go delete mode 100644 mobile/discover.go delete mode 100644 mobile/doc.go delete mode 100644 mobile/ethclient.go delete mode 100644 mobile/ethereum.go delete mode 100644 mobile/geth.go delete mode 100644 mobile/geth_android.go delete mode 100644 mobile/geth_ios.go delete mode 100644 mobile/geth_other.go delete mode 100644 mobile/init.go delete mode 100644 mobile/interface.go delete mode 100644 mobile/logger.go delete mode 100644 mobile/p2p.go delete mode 100644 mobile/params.go delete mode 100644 mobile/primitives.go delete mode 100644 mobile/types.go delete mode 100644 mobile/vm.go diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a7b617655235..1a954e8ab585 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -7,5 +7,4 @@ core/ @karalabe @holiman eth/ @karalabe les/ @zsfelfoldi light/ @zsfelfoldi -mobile/ @karalabe p2p/ @fjl @zsfelfoldi diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go index 1fd65acef5e6..44de4cc74aed 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/bind/bind.go @@ -37,8 +37,6 @@ type Lang int const ( LangGo Lang = iota - LangJava - LangObjC ) // Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant @@ -160,15 +158,15 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La // bindType is a set of type binders that convert Solidity types to some supported // programming language types. var bindType = map[Lang]func(kind abi.Type) string{ - LangGo: bindTypeGo, - LangJava: bindTypeJava, + LangGo: bindTypeGo, } // Helper function for the binding generators. // It reads the unmatched characters after the inner type-match, -// (since the inner type is a prefix of the total type declaration), -// looks for valid arrays (possibly a dynamic one) wrapping the inner type, -// and returns the sizes of these arrays. +// +// (since the inner type is a prefix of the total type declaration), +// looks for valid arrays (possibly a dynamic one) wrapping the inner type, +// and returns the sizes of these arrays. // // Returned array sizes are in the same order as solidity signatures; inner array size first. // Array sizes may also be "", indicating a dynamic array. @@ -244,15 +242,6 @@ func arrayBindingJava(inner string, arraySizes []string) string { return inner + strings.Repeat("[]", len(arraySizes)) } -// bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping -// from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly -// mapped will use an upscaled type (e.g. BigDecimal). -func bindTypeJava(kind abi.Type) string { - stringKind := kind.String() - innerLen, innerMapping := bindUnnestedTypeJava(stringKind) - return arrayBindingJava(wrapArray(stringKind, innerLen, innerMapping)) -} - // The inner function of bindTypeJava, this finds the inner type of stringKind. // (Or just the type itself if it is not an array or slice) // The length of the matched part is returned, with the the translated type. @@ -311,8 +300,7 @@ func bindUnnestedTypeJava(stringKind string) (int, string) { // bindTopicType is a set of type binders that convert Solidity types to some // supported programming language topic types. var bindTopicType = map[Lang]func(kind abi.Type) string{ - LangGo: bindTopicTypeGo, - LangJava: bindTopicTypeJava, + LangGo: bindTopicTypeGo, } // bindTypeGo converts a Solidity topic type to a Go one. It is almost the same @@ -325,64 +313,16 @@ func bindTopicTypeGo(kind abi.Type) string { return bound } -// bindTypeGo converts a Solidity topic type to a Java one. It is almost the same -// funcionality as for simple types, but dynamic types get converted to hashes. -func bindTopicTypeJava(kind abi.Type) string { - bound := bindTypeJava(kind) - if bound == "String" || bound == "Bytes" { - bound = "Hash" - } - return bound -} - // namedType is a set of functions that transform language specific types to // named versions that my be used inside method names. var namedType = map[Lang]func(string, abi.Type) string{ - LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") }, - LangJava: namedTypeJava, -} - -// namedTypeJava converts some primitive data types to named variants that can -// be used as parts of method names. -func namedTypeJava(javaKind string, solKind abi.Type) string { - switch javaKind { - case "byte[]": - return "Binary" - case "byte[][]": - return "Binaries" - case "string": - return "String" - case "string[]": - return "Strings" - case "boolean": - return "Bool" - case "boolean[]": - return "Bools" - case "BigInt[]": - return "BigInts" - default: - parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String()) - if len(parts) != 4 { - return javaKind - } - switch parts[2] { - case "8", "16", "32", "64": - if parts[3] == "" { - return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2])) - } - return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2])) - - default: - return javaKind - } - } + LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") }, } // methodNormalizer is a name transformer that modifies Solidity method names to // conform to target language naming concentions. var methodNormalizer = map[Lang]func(string) string{ - LangGo: capitalise, - LangJava: decapitalise, + LangGo: capitalise, } // capitalise makes a camel-case string which starts with an upper case character. diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go index eca7a6882eb3..61951dcd7c00 100644 --- a/accounts/abi/bind/template.go +++ b/accounts/abi/bind/template.go @@ -52,8 +52,7 @@ type tmplEvent struct { // tmplSource is language to template mapping containing all the supported // programming languages the package can generate to. var tmplSource = map[Lang]string{ - LangGo: tmplSourceGo, - LangJava: tmplSourceJava, + LangGo: tmplSourceGo, } // tmplSourceGo is the Go source template use to generate the contract binding @@ -418,105 +417,3 @@ package {{.Package}} {{end}} {{end}} ` - -// tmplSourceJava is the Java source template use to generate the contract binding -// based on. -const tmplSourceJava = ` -// This file is an automatically generated Java binding. Do not modify as any -// change will likely be lost upon the next re-generation! - -package {{.Package}}; - -import org.ethereum.geth.*; -import org.ethereum.geth.internal.*; - -{{range $contract := .Contracts}} - public class {{.Type}} { - // ABI is the input ABI used to generate the binding from. - public final static String ABI = "{{.InputABI}}"; - - {{if .InputBin}} - // BYTECODE is the compiled bytecode used for deploying new contracts. - public final static byte[] BYTECODE = "{{.InputBin}}".getBytes(); - - // deploy deploys a new Ethereum contract, binding an instance of {{.Type}} to it. - public static {{.Type}} deploy(TransactOpts auth, EthereumClient client{{range .Constructor.Inputs}}, {{bindtype .Type}} {{.Name}}{{end}}) throws Exception { - Interfaces args = Geth.newInterfaces({{(len .Constructor.Inputs)}}); - {{range $index, $element := .Constructor.Inputs}} - args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}}); - {{end}} - return new {{.Type}}(Geth.deployContract(auth, ABI, BYTECODE, client, args)); - } - - // Internal constructor used by contract deployment. - private {{.Type}}(BoundContract deployment) { - this.Address = deployment.getAddress(); - this.Deployer = deployment.getDeployer(); - this.Contract = deployment; - } - {{end}} - - // Ethereum address where this contract is located at. - public final Address Address; - - // Ethereum transaction in which this contract was deployed (if known!). - public final Transaction Deployer; - - // Contract instance bound to a blockchain address. - private final BoundContract Contract; - - // Creates a new instance of {{.Type}}, bound to a specific deployed contract. - public {{.Type}}(Address address, EthereumClient client) throws Exception { - this(Geth.bindContract(address, ABI, client)); - } - - {{range .Calls}} - {{if gt (len .Normalized.Outputs) 1}} - // {{capitalise .Normalized.Name}}Results is the output of a call to {{.Normalized.Name}}. - public class {{capitalise .Normalized.Name}}Results { - {{range $index, $item := .Normalized.Outputs}}public {{bindtype .Type}} {{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}}; - {{end}} - } - {{end}} - - // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}. - // - // Solidity: {{.Original.String}} - public {{if gt (len .Normalized.Outputs) 1}}{{capitalise .Normalized.Name}}Results{{else}}{{range .Normalized.Outputs}}{{bindtype .Type}}{{end}}{{end}} {{.Normalized.Name}}(CallOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type}} {{.Name}}{{end}}) throws Exception { - Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}}); - {{range $index, $item := .Normalized.Inputs}}args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}}); - {{end}} - - Interfaces results = Geth.newInterfaces({{(len .Normalized.Outputs)}}); - {{range $index, $item := .Normalized.Outputs}}Interface result{{$index}} = Geth.newInterface(); result{{$index}}.setDefault{{namedtype (bindtype .Type) .Type}}(); results.set({{$index}}, result{{$index}}); - {{end}} - - if (opts == null) { - opts = Geth.newCallOpts(); - } - this.Contract.call(opts, results, "{{.Original.Name}}", args); - {{if gt (len .Normalized.Outputs) 1}} - {{capitalise .Normalized.Name}}Results result = new {{capitalise .Normalized.Name}}Results(); - {{range $index, $item := .Normalized.Outputs}}result.{{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}} = results.get({{$index}}).get{{namedtype (bindtype .Type) .Type}}(); - {{end}} - return result; - {{else}}{{range .Normalized.Outputs}}return results.get(0).get{{namedtype (bindtype .Type) .Type}}();{{end}} - {{end}} - } - {{end}} - - {{range .Transacts}} - // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}. - // - // Solidity: {{.Original.String}} - public Transaction {{.Normalized.Name}}(TransactOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type}} {{.Name}}{{end}}) throws Exception { - Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}}); - {{range $index, $item := .Normalized.Inputs}}args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}}); - {{end}} - - return this.Contract.transact(opts, "{{.Original.Name}}" , args); - } - {{end}} - } -{{end}} -` diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index 8d91b3ddc63d..a8c3ebfc8cf9 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -38,7 +38,7 @@ var ( pkgFlag = flag.String("pkg", "", "Package name to generate the binding into") outFlag = flag.String("out", "", "Output file for the generated binding (default = stdout)") - langFlag = flag.String("lang", "go", "Destination language for the bindings (go, java, objc)") + langFlag = flag.String("lang", "go", "Destination language for the bindings (go)") ) func main() { @@ -60,10 +60,6 @@ func main() { switch *langFlag { case "go": lang = bind.LangGo - case "java": - lang = bind.LangJava - case "objc": - lang = bind.LangObjC default: fmt.Printf("Unsupported destination language \"%s\" (--lang)\n", *langFlag) os.Exit(-1) diff --git a/mobile/accounts.go b/mobile/accounts.go deleted file mode 100644 index 48b1225f623a..000000000000 --- a/mobile/accounts.go +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the accounts package to support client side key -// management on mobile platforms. - -package geth - -import ( - "errors" - "time" - - "github.com/XinFinOrg/XDPoSChain/accounts" - "github.com/XinFinOrg/XDPoSChain/accounts/keystore" - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/crypto" -) - -const ( - // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB - // memory and taking approximately 1s CPU time on a modern processor. - StandardScryptN = int(keystore.StandardScryptN) - - // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB - // memory and taking approximately 1s CPU time on a modern processor. - StandardScryptP = int(keystore.StandardScryptP) - - // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB - // memory and taking approximately 100ms CPU time on a modern processor. - LightScryptN = int(keystore.LightScryptN) - - // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB - // memory and taking approximately 100ms CPU time on a modern processor. - LightScryptP = int(keystore.LightScryptP) -) - -// Account represents a stored key. -type Account struct{ account accounts.Account } - -// Accounts represents a slice of accounts. -type Accounts struct{ accounts []accounts.Account } - -// Size returns the number of accounts in the slice. -func (a *Accounts) Size() int { - return len(a.accounts) -} - -// Get returns the account at the given index from the slice. -func (a *Accounts) Get(index int) (account *Account, _ error) { - if index < 0 || index >= len(a.accounts) { - return nil, errors.New("index out of bounds") - } - return &Account{a.accounts[index]}, nil -} - -// Set sets the account at the given index in the slice. -func (a *Accounts) Set(index int, account *Account) error { - if index < 0 || index >= len(a.accounts) { - return errors.New("index out of bounds") - } - a.accounts[index] = account.account - return nil -} - -// GetAddress retrieves the address associated with the account. -func (a *Account) GetAddress() *Address { - return &Address{a.account.Address} -} - -// GetURL retrieves the canonical URL of the account. -func (a *Account) GetURL() string { - return a.account.URL.String() -} - -// KeyStore manages a key storage directory on disk. -type KeyStore struct{ keystore *keystore.KeyStore } - -// NewKeyStore creates a keystore for the given directory. -func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore { - return &KeyStore{keystore: keystore.NewKeyStore(keydir, scryptN, scryptP)} -} - -// HasAddress reports whether a key with the given address is present. -func (ks *KeyStore) HasAddress(address *Address) bool { - return ks.keystore.HasAddress(address.address) -} - -// GetAccounts returns all key files present in the directory. -func (ks *KeyStore) GetAccounts() *Accounts { - return &Accounts{ks.keystore.Accounts()} -} - -// DeleteAccount deletes the key matched by account if the passphrase is correct. -// If a contains no filename, the address must match a unique key. -func (ks *KeyStore) DeleteAccount(account *Account, passphrase string) error { - return ks.keystore.Delete(account.account, passphrase) -} - -// SignHash calculates a ECDSA signature for the given hash. The produced signature -// is in the [R || S || V] format where V is 0 or 1. -func (ks *KeyStore) SignHash(address *Address, hash []byte) (signature []byte, _ error) { - return ks.keystore.SignHash(accounts.Account{Address: address.address}, common.CopyBytes(hash)) -} - -// SignTx signs the given transaction with the requested account. -func (ks *KeyStore) SignTx(account *Account, tx *Transaction, chainID *BigInt) (*Transaction, error) { - if chainID == nil { // Null passed from mobile app - chainID = new(BigInt) - } - signed, err := ks.keystore.SignTx(account.account, tx.tx, chainID.bigint) - if err != nil { - return nil, err - } - return &Transaction{signed}, nil -} - -// SignHashPassphrase signs hash if the private key matching the given address can -// be decrypted with the given passphrase. The produced signature is in the -// [R || S || V] format where V is 0 or 1. -func (ks *KeyStore) SignHashPassphrase(account *Account, passphrase string, hash []byte) (signature []byte, _ error) { - return ks.keystore.SignHashWithPassphrase(account.account, passphrase, common.CopyBytes(hash)) -} - -// SignTxPassphrase signs the transaction if the private key matching the -// given address can be decrypted with the given passphrase. -func (ks *KeyStore) SignTxPassphrase(account *Account, passphrase string, tx *Transaction, chainID *BigInt) (*Transaction, error) { - if chainID == nil { // Null passed from mobile app - chainID = new(BigInt) - } - signed, err := ks.keystore.SignTxWithPassphrase(account.account, passphrase, tx.tx, chainID.bigint) - if err != nil { - return nil, err - } - return &Transaction{signed}, nil -} - -// Unlock unlocks the given account indefinitely. -func (ks *KeyStore) Unlock(account *Account, passphrase string) error { - return ks.keystore.TimedUnlock(account.account, passphrase, 0) -} - -// Lock removes the private key with the given address from memory. -func (ks *KeyStore) Lock(address *Address) error { - return ks.keystore.Lock(address.address) -} - -// TimedUnlock unlocks the given account with the passphrase. The account stays -// unlocked for the duration of timeout (nanoseconds). A timeout of 0 unlocks the -// account until the program exits. The account must match a unique key file. -// -// If the account address is already unlocked for a duration, TimedUnlock extends or -// shortens the active unlock timeout. If the address was previously unlocked -// indefinitely the timeout is not altered. -func (ks *KeyStore) TimedUnlock(account *Account, passphrase string, timeout int64) error { - return ks.keystore.TimedUnlock(account.account, passphrase, time.Duration(timeout)) -} - -// NewAccount generates a new key and stores it into the key directory, -// encrypting it with the passphrase. -func (ks *KeyStore) NewAccount(passphrase string) (*Account, error) { - account, err := ks.keystore.NewAccount(passphrase) - if err != nil { - return nil, err - } - return &Account{account}, nil -} - -// UpdateAccount changes the passphrase of an existing account. -func (ks *KeyStore) UpdateAccount(account *Account, passphrase, newPassphrase string) error { - return ks.keystore.Update(account.account, passphrase, newPassphrase) -} - -// ExportKey exports as a JSON key, encrypted with newPassphrase. -func (ks *KeyStore) ExportKey(account *Account, passphrase, newPassphrase string) (key []byte, _ error) { - return ks.keystore.Export(account.account, passphrase, newPassphrase) -} - -// ImportKey stores the given encrypted JSON key into the key directory. -func (ks *KeyStore) ImportKey(keyJSON []byte, passphrase, newPassphrase string) (account *Account, _ error) { - acc, err := ks.keystore.Import(common.CopyBytes(keyJSON), passphrase, newPassphrase) - if err != nil { - return nil, err - } - return &Account{acc}, nil -} - -// ImportECDSAKey stores the given encrypted JSON key into the key directory. -func (ks *KeyStore) ImportECDSAKey(key []byte, passphrase string) (account *Account, _ error) { - privkey, err := crypto.ToECDSA(common.CopyBytes(key)) - if err != nil { - return nil, err - } - acc, err := ks.keystore.ImportECDSA(privkey, passphrase) - if err != nil { - return nil, err - } - return &Account{acc}, nil -} - -// ImportPreSaleKey decrypts the given Ethereum presale wallet and stores -// a key file in the key directory. The key file is encrypted with the same passphrase. -func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (ccount *Account, _ error) { - account, err := ks.keystore.ImportPreSaleKey(common.CopyBytes(keyJSON), passphrase) - if err != nil { - return nil, err - } - return &Account{account}, nil -} diff --git a/mobile/android_test.go b/mobile/android_test.go deleted file mode 100644 index 8cc4daacb8a8..000000000000 --- a/mobile/android_test.go +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package geth - -import ( - "os" - "os/exec" - "path/filepath" - "runtime" - "testing" - "time" - - "github.com/XinFinOrg/XDPoSChain/internal/build" -) - -// androidTestClass is a Java class to do some lightweight tests against the Android -// bindings. The goal is not to test each individual functionality, rather just to -// catch breaking API and/or implementation changes. -const androidTestClass = ` -package go; - -import android.test.InstrumentationTestCase; -import android.test.MoreAsserts; - -import java.math.BigInteger; -import java.util.Arrays; - -import org.ethereum.geth.*; - -public class AndroidTest extends InstrumentationTestCase { - public AndroidTest() {} - - public void testAccountManagement() { - // Create an encrypted keystore with light crypto parameters. - KeyStore ks = new KeyStore(getInstrumentation().getContext().getFilesDir() + "/keystore", Geth.LightScryptN, Geth.LightScryptP); - - try { - // Create a new account with the specified encryption passphrase. - Account newAcc = ks.newAccount("Creation password"); - - // Export the newly created account with a different passphrase. The returned - // data from this method invocation is a JSON encoded, encrypted key-file. - byte[] jsonAcc = ks.exportKey(newAcc, "Creation password", "Export password"); - - // Update the passphrase on the account created above inside the local keystore. - ks.updateAccount(newAcc, "Creation password", "Update password"); - - // Delete the account updated above from the local keystore. - ks.deleteAccount(newAcc, "Update password"); - - // Import back the account we've exported (and then deleted) above with yet - // again a fresh passphrase. - Account impAcc = ks.importKey(jsonAcc, "Export password", "Import password"); - - // Create a new account to sign transactions with - Account signer = ks.newAccount("Signer password"); - - Transaction tx = new Transaction( - 1, new Address("0x0000000000000000000000000000000000000000"), - new BigInt(0), 0, new BigInt(1), null); // Random empty transaction - BigInt chain = new BigInt(1); // Chain identifier of the main net - - // Sign a transaction with a single authorization - Transaction signed = ks.signTxPassphrase(signer, "Signer password", tx, chain); - - // Sign a transaction with multiple manually cancelled authorizations - ks.unlock(signer, "Signer password"); - signed = ks.signTx(signer, tx, chain); - ks.lock(signer.getAddress()); - - // Sign a transaction with multiple automatically cancelled authorizations - ks.timedUnlock(signer, "Signer password", 1000000000); - signed = ks.signTx(signer, tx, chain); - } catch (Exception e) { - fail(e.toString()); - } - } - - public void testInprocNode() { - Context ctx = new Context(); - - try { - // Start up a new inprocess node - Node node = new Node(getInstrumentation().getContext().getFilesDir() + "/.ethereum", new NodeConfig()); - node.start(); - - // Retrieve some data via function calls (we don't really care about the results) - NodeInfo info = node.getNodeInfo(); - info.getName(); - info.getListenerAddress(); - info.getProtocols(); - - // Retrieve some data via the APIs (we don't really care about the results) - EthereumClient ec = node.getEthereumClient(); - ec.getBlockByNumber(ctx, -1).getNumber(); - - NewHeadHandler handler = new NewHeadHandler() { - @Override public void onError(String error) {} - @Override public void onNewHead(final Header header) {} - }; - ec.subscribeNewHead(ctx, handler, 16); - } catch (Exception e) { - fail(e.toString()); - } - } - - // Tests that recovering transaction signers works for both Homestead and EIP155 - // signatures too. Regression test for go-ethereum issue #14599. - public void testIssue14599() { - try { - byte[] preEIP155RLP = new BigInteger("f901fc8032830138808080b901ae60056013565b6101918061001d6000396000f35b3360008190555056006001600060e060020a6000350480630a874df61461003a57806341c0e1b514610058578063a02b161e14610066578063dbbdf0831461007757005b610045600435610149565b80600160a060020a031660005260206000f35b610060610161565b60006000f35b6100716004356100d4565b60006000f35b61008560043560243561008b565b60006000f35b600054600160a060020a031632600160a060020a031614156100ac576100b1565b6100d0565b8060018360005260205260406000208190555081600060005260206000a15b5050565b600054600160a060020a031633600160a060020a031614158015610118575033600160a060020a0316600182600052602052604060002054600160a060020a031614155b61012157610126565b610146565b600060018260005260205260406000208190555080600060005260206000a15b50565b60006001826000526020526040600020549050919050565b600054600160a060020a031633600160a060020a0316146101815761018f565b600054600160a060020a0316ff5b561ca0c5689ed1ad124753d54576dfb4b571465a41900a1dff4058d8adf16f752013d0a01221cbd70ec28c94a3b55ec771bcbc70778d6ee0b51ca7ea9514594c861b1884", 16).toByteArray(); - preEIP155RLP = Arrays.copyOfRange(preEIP155RLP, 1, preEIP155RLP.length); - - byte[] postEIP155RLP = new BigInteger("f86b80847735940082520894ef5bbb9bba2e1ca69ef81b23a8727d889f3ef0a1880de0b6b3a7640000802ba06fef16c44726a102e6d55a651740636ef8aec6df3ebf009e7b0c1f29e4ac114aa057e7fbc69760b522a78bb568cfc37a58bfdcf6ea86cb8f9b550263f58074b9cc", 16).toByteArray(); - postEIP155RLP = Arrays.copyOfRange(postEIP155RLP, 1, postEIP155RLP.length); - - Transaction preEIP155 = new Transaction(preEIP155RLP); - Transaction postEIP155 = new Transaction(postEIP155RLP); - - preEIP155.getFrom(null); // Homestead should accept homestead - preEIP155.getFrom(new BigInt(4)); // EIP155 should accept homestead (missing chain ID) - postEIP155.getFrom(new BigInt(4)); // EIP155 should accept EIP 155 - - try { - postEIP155.getFrom(null); - fail("EIP155 transaction accepted by Homestead"); - } catch (Exception e) {} - } catch (Exception e) { - fail(e.toString()); - } - } -} -` - -// TestAndroid runs the Android java test class specified above. -// -// This requires the gradle command in PATH and the Android SDK whose path is available -// through ANDROID_HOME environment variable. To successfully run the tests, an Android -// device must also be available with debugging enabled. -// -// This method has been adapted from golang.org/x/mobile/bind/java/seq_test.go/runTest -func TestAndroid(t *testing.T) { - t.Skip("skip this test since it's not being used") - // Skip tests on Windows altogether - if runtime.GOOS == "windows" { - t.Skip("cannot test Android bindings on Windows, skipping") - } - // Make sure all the Android tools are installed - if _, err := exec.Command("which", "gradle").CombinedOutput(); err != nil { - t.Skip("command gradle not found, skipping") - } - if sdk := os.Getenv("ANDROID_HOME"); sdk == "" { - // Android SDK not explicitly given, try to auto-resolve - autopath := filepath.Join(os.Getenv("HOME"), "Android", "Sdk") - if _, err := os.Stat(autopath); err != nil { - t.Skip("ANDROID_HOME environment var not set, skipping") - } - os.Setenv("ANDROID_HOME", autopath) - } - if _, err := exec.Command("which", "gomobile").CombinedOutput(); err != nil { - t.Log("gomobile missing, installing it...") - if out, err := exec.Command("go", "get", "golang.org/x/mobile/cmd/gomobile").CombinedOutput(); err != nil { - t.Fatalf("install failed: %v\n%s", err, string(out)) - } - t.Log("initializing gomobile...") - start := time.Now() - if _, err := exec.Command("gomobile", "init").CombinedOutput(); err != nil { - t.Fatalf("initialization failed: %v", err) - } - t.Logf("initialization took %v", time.Since(start)) - } - // Create and switch to a temporary workspace - workspace, err := os.MkdirTemp("", "geth-android-") - if err != nil { - t.Fatalf("failed to create temporary workspace: %v", err) - } - defer os.RemoveAll(workspace) - - pwd, err := os.Getwd() - if err != nil { - t.Fatalf("failed to get current working directory: %v", err) - } - if err := os.Chdir(workspace); err != nil { - t.Fatalf("failed to switch to temporary workspace: %v", err) - } - defer os.Chdir(pwd) - - // Create the skeleton of the Android project - for _, dir := range []string{"src/main", "src/androidTest/java/org/ethereum/gethtest", "libs"} { - err = os.MkdirAll(dir, os.ModePerm) - if err != nil { - t.Fatal(err) - } - } - // Generate the mobile bindings for Geth and add the tester class - gobind := exec.Command("gomobile", "bind", "-javapkg", "org.ethereum", "github.com/XinFinOrg/XDPoSChain/mobile") - if output, err := gobind.CombinedOutput(); err != nil { - t.Logf("%s", output) - t.Fatalf("failed to run gomobile bind: %v", err) - } - build.CopyFile(filepath.Join("libs", "geth.aar"), "geth.aar", os.ModePerm) - - if err = os.WriteFile(filepath.Join("src", "androidTest", "java", "org", "ethereum", "gethtest", "AndroidTest.java"), []byte(androidTestClass), os.ModePerm); err != nil { - t.Fatalf("failed to write Android test class: %v", err) - } - // Finish creating the project and run the tests via gradle - if err = os.WriteFile(filepath.Join("src", "main", "AndroidManifest.xml"), []byte(androidManifest), os.ModePerm); err != nil { - t.Fatalf("failed to write Android manifest: %v", err) - } - if err = os.WriteFile("build.gradle", []byte(gradleConfig), os.ModePerm); err != nil { - t.Fatalf("failed to write gradle build file: %v", err) - } - if output, err := exec.Command("gradle", "connectedAndroidTest").CombinedOutput(); err != nil { - t.Logf("%s", output) - t.Errorf("failed to run gradle test: %v", err) - } -} - -const androidManifest = ` - - - -` - -const gradleConfig = `buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' - } -} -allprojects { - repositories { jcenter() } -} -apply plugin: 'com.android.library' -android { - compileSdkVersion 'android-19' - buildToolsVersion '21.1.2' - defaultConfig { minSdkVersion 15 } -} -repositories { - flatDir { dirs 'libs' } -} -dependencies { - compile 'com.android.support:appcompat-v7:19.0.0' - compile(name: "geth", ext: "aar") -} -` diff --git a/mobile/big.go b/mobile/big.go deleted file mode 100644 index 1f1537322af8..000000000000 --- a/mobile/big.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the math/big package. - -package geth - -import ( - "errors" - "math/big" - - "github.com/XinFinOrg/XDPoSChain/common" -) - -// A BigInt represents a signed multi-precision integer. -type BigInt struct { - bigint *big.Int -} - -// NewBigInt allocates and returns a new BigInt set to x. -func NewBigInt(x int64) *BigInt { - return &BigInt{big.NewInt(x)} -} - -// GetBytes returns the absolute value of x as a big-endian byte slice. -func (bi *BigInt) GetBytes() []byte { - return bi.bigint.Bytes() -} - -// String returns the value of x as a formatted decimal string. -func (bi *BigInt) String() string { - return bi.bigint.String() -} - -// GetInt64 returns the int64 representation of x. If x cannot be represented in -// an int64, the result is undefined. -func (bi *BigInt) GetInt64() int64 { - return bi.bigint.Int64() -} - -// SetBytes interprets buf as the bytes of a big-endian unsigned integer and sets -// the big int to that value. -func (bi *BigInt) SetBytes(buf []byte) { - bi.bigint.SetBytes(common.CopyBytes(buf)) -} - -// SetInt64 sets the big int to x. -func (bi *BigInt) SetInt64(x int64) { - bi.bigint.SetInt64(x) -} - -// Sign returns: -// -// -1 if x < 0 -// 0 if x == 0 -// +1 if x > 0 -// -func (bi *BigInt) Sign() int { - return bi.bigint.Sign() -} - -// SetString sets the big int to x. -// -// The string prefix determines the actual conversion base. A prefix of "0x" or -// "0X" selects base 16; the "0" prefix selects base 8, and a "0b" or "0B" prefix -// selects base 2. Otherwise the selected base is 10. -func (bi *BigInt) SetString(x string, base int) { - bi.bigint.SetString(x, base) -} - -// BigInts represents a slice of big ints. -type BigInts struct{ bigints []*big.Int } - -// Size returns the number of big ints in the slice. -func (bi *BigInts) Size() int { - return len(bi.bigints) -} - -// Get returns the bigint at the given index from the slice. -func (bi *BigInts) Get(index int) (bigint *BigInt, _ error) { - if index < 0 || index >= len(bi.bigints) { - return nil, errors.New("index out of bounds") - } - return &BigInt{bi.bigints[index]}, nil -} - -// Set sets the big int at the given index in the slice. -func (bi *BigInts) Set(index int, bigint *BigInt) error { - if index < 0 || index >= len(bi.bigints) { - return errors.New("index out of bounds") - } - bi.bigints[index] = bigint.bigint - return nil -} - -// GetString returns the value of x as a formatted string in some number base. -func (bi *BigInt) GetString(base int) string { - return bi.bigint.Text(base) -} diff --git a/mobile/bind.go b/mobile/bind.go deleted file mode 100644 index 23893e6d9b6b..000000000000 --- a/mobile/bind.go +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the bind package. - -package geth - -import ( - "math/big" - "strings" - - "github.com/XinFinOrg/XDPoSChain/accounts/abi" - "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/core/types" -) - -// Signer is an interaface defining the callback when a contract requires a -// method to sign the transaction before submission. -type Signer interface { - Sign(*Address, *Transaction) (tx *Transaction, _ error) -} - -type signer struct { - sign bind.SignerFn -} - -func (s *signer) Sign(addr *Address, unsignedTx *Transaction) (signedTx *Transaction, _ error) { - sig, err := s.sign(addr.address, unsignedTx.tx) - if err != nil { - return nil, err - } - return &Transaction{sig}, nil -} - -// CallOpts is the collection of options to fine tune a contract call request. -type CallOpts struct { - opts bind.CallOpts -} - -// NewCallOpts creates a new option set for contract calls. -func NewCallOpts() *CallOpts { - return new(CallOpts) -} - -func (opts *CallOpts) IsPending() bool { return opts.opts.Pending } -func (opts *CallOpts) GetGasLimit() int64 { return 0 /* TODO(karalabe) */ } - -// GetContext cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876) -// Even then it's awkward to unpack the subtleties of a Go context out to Java. -// func (opts *CallOpts) GetContext() *Context { return &Context{opts.opts.Context} } - -func (opts *CallOpts) SetPending(pending bool) { opts.opts.Pending = pending } -func (opts *CallOpts) SetGasLimit(limit int64) { /* TODO(karalabe) */ } -func (opts *CallOpts) SetContext(context *Context) { opts.opts.Context = context.context } - -// TransactOpts is the collection of authorization data required to create a -// valid Ethereum transaction. -type TransactOpts struct { - opts bind.TransactOpts -} - -func (opts *TransactOpts) GetFrom() *Address { return &Address{opts.opts.From} } -func (opts *TransactOpts) GetNonce() int64 { return opts.opts.Nonce.Int64() } -func (opts *TransactOpts) GetValue() *BigInt { return &BigInt{opts.opts.Value} } -func (opts *TransactOpts) GetGasPrice() *BigInt { return &BigInt{opts.opts.GasPrice} } -func (opts *TransactOpts) GetGasLimit() int64 { return int64(opts.opts.GasLimit) } - -// GetSigner cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876) -// func (opts *TransactOpts) GetSigner() Signer { return &signer{opts.opts.Signer} } - -// GetContext cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876) -// Even then it's awkward to unpack the subtleties of a Go context out to Java. -//func (opts *TransactOpts) GetContext() *Context { return &Context{opts.opts.Context} } - -func (opts *TransactOpts) SetFrom(from *Address) { opts.opts.From = from.address } -func (opts *TransactOpts) SetNonce(nonce int64) { opts.opts.Nonce = big.NewInt(nonce) } -func (opts *TransactOpts) SetSigner(s Signer) { - opts.opts.Signer = func(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { - sig, err := s.Sign(&Address{addr}, &Transaction{tx}) - if err != nil { - return nil, err - } - return sig.tx, nil - } -} -func (opts *TransactOpts) SetValue(value *BigInt) { opts.opts.Value = value.bigint } -func (opts *TransactOpts) SetGasPrice(price *BigInt) { opts.opts.GasPrice = price.bigint } -func (opts *TransactOpts) SetGasLimit(limit int64) { opts.opts.GasLimit = uint64(limit) } -func (opts *TransactOpts) SetContext(context *Context) { opts.opts.Context = context.context } - -// BoundContract is the base wrapper object that reflects a contract on the -// Ethereum network. It contains a collection of methods that are used by the -// higher level contract bindings to operate. -type BoundContract struct { - contract *bind.BoundContract - address common.Address - deployer *types.Transaction -} - -// DeployContract deploys a contract onto the Ethereum blockchain and binds the -// deployment address with a wrapper. -func DeployContract(opts *TransactOpts, abiJSON string, bytecode []byte, client *EthereumClient, args *Interfaces) (contract *BoundContract, _ error) { - // Deploy the contract to the network - parsed, err := abi.JSON(strings.NewReader(abiJSON)) - if err != nil { - return nil, err - } - addr, tx, bound, err := bind.DeployContract(&opts.opts, parsed, common.CopyBytes(bytecode), client.client, args.objects...) - if err != nil { - return nil, err - } - return &BoundContract{ - contract: bound, - address: addr, - deployer: tx, - }, nil -} - -// BindContract creates a low level contract interface through which calls and -// transactions may be made through. -func BindContract(address *Address, abiJSON string, client *EthereumClient) (contract *BoundContract, _ error) { - parsed, err := abi.JSON(strings.NewReader(abiJSON)) - if err != nil { - return nil, err - } - return &BoundContract{ - contract: bind.NewBoundContract(address.address, parsed, client.client, client.client, client.client), - address: address.address, - }, nil -} - -func (c *BoundContract) GetAddress() *Address { return &Address{c.address} } -func (c *BoundContract) GetDeployer() *Transaction { - if c.deployer == nil { - return nil - } - return &Transaction{c.deployer} -} - -// Call invokes the (constant) contract method with params as input values and -// sets the output to result. -func (c *BoundContract) Call(opts *CallOpts, out *Interfaces, method string, args *Interfaces) error { - if len(out.objects) == 1 { - result := out.objects[0] - if err := c.contract.Call(&opts.opts, result, method, args.objects...); err != nil { - return err - } - out.objects[0] = result - } else { - results := make([]interface{}, len(out.objects)) - copy(results, out.objects) - if err := c.contract.Call(&opts.opts, &results, method, args.objects...); err != nil { - return err - } - copy(out.objects, results) - } - return nil -} - -// Transact invokes the (paid) contract method with params as input values. -func (c *BoundContract) Transact(opts *TransactOpts, method string, args *Interfaces) (tx *Transaction, _ error) { - rawTx, err := c.contract.Transact(&opts.opts, method, args.objects...) - if err != nil { - return nil, err - } - return &Transaction{rawTx}, nil -} - -// Transfer initiates a plain transaction to move funds to the contract, calling -// its default method if one is available. -func (c *BoundContract) Transfer(opts *TransactOpts) (tx *Transaction, _ error) { - rawTx, err := c.contract.Transfer(&opts.opts) - if err != nil { - return nil, err - } - return &Transaction{rawTx}, nil -} diff --git a/mobile/common.go b/mobile/common.go deleted file mode 100644 index 6e69110a91e2..000000000000 --- a/mobile/common.go +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the common package. - -package geth - -import ( - "encoding/hex" - "errors" - "fmt" - "strings" - - "github.com/XinFinOrg/XDPoSChain/common" -) - -// Hash represents the 32 byte Keccak256 hash of arbitrary data. -type Hash struct { - hash common.Hash -} - -// NewHashFromBytes converts a slice of bytes to a hash value. -func NewHashFromBytes(binary []byte) (hash *Hash, _ error) { - h := new(Hash) - if err := h.SetBytes(common.CopyBytes(binary)); err != nil { - return nil, err - } - return h, nil -} - -// NewHashFromHex converts a hex string to a hash value. -func NewHashFromHex(hex string) (hash *Hash, _ error) { - h := new(Hash) - if err := h.SetHex(hex); err != nil { - return nil, err - } - return h, nil -} - -// SetBytes sets the specified slice of bytes as the hash value. -func (h *Hash) SetBytes(hash []byte) error { - if length := len(hash); length != common.HashLength { - return fmt.Errorf("invalid hash length: %v != %v", length, common.HashLength) - } - copy(h.hash[:], hash) - return nil -} - -// GetBytes retrieves the byte representation of the hash. -func (h *Hash) GetBytes() []byte { - return h.hash[:] -} - -// SetHex sets the specified hex string as the hash value. -func (h *Hash) SetHex(hash string) error { - hash = strings.ToLower(hash) - if len(hash) >= 2 && hash[:2] == "0x" { - hash = hash[2:] - } - if length := len(hash); length != 2*common.HashLength { - return fmt.Errorf("invalid hash hex length: %v != %v", length, 2*common.HashLength) - } - bin, err := hex.DecodeString(hash) - if err != nil { - return err - } - copy(h.hash[:], bin) - return nil -} - -// GetHex retrieves the hex string representation of the hash. -func (h *Hash) GetHex() string { - return h.hash.Hex() -} - -// Hashes represents a slice of hashes. -type Hashes struct{ hashes []common.Hash } - -// NewHashes creates a slice of uninitialized Hashes. -func NewHashes(size int) *Hashes { - return &Hashes{ - hashes: make([]common.Hash, size), - } -} - -// NewHashesEmpty creates an empty slice of Hashes values. -func NewHashesEmpty() *Hashes { - return NewHashes(0) -} - -// Size returns the number of hashes in the slice. -func (h *Hashes) Size() int { - return len(h.hashes) -} - -// Get returns the hash at the given index from the slice. -func (h *Hashes) Get(index int) (hash *Hash, _ error) { - if index < 0 || index >= len(h.hashes) { - return nil, errors.New("index out of bounds") - } - return &Hash{h.hashes[index]}, nil -} - -// Set sets the Hash at the given index in the slice. -func (h *Hashes) Set(index int, hash *Hash) error { - if index < 0 || index >= len(h.hashes) { - return errors.New("index out of bounds") - } - h.hashes[index] = hash.hash - return nil -} - -// Append adds a new Hash element to the end of the slice. -func (h *Hashes) Append(hash *Hash) { - h.hashes = append(h.hashes, hash.hash) -} - -// Address represents the 20 byte address of an Ethereum account. -type Address struct { - address common.Address -} - -// NewAddressFromBytes converts a slice of bytes to a hash value. -func NewAddressFromBytes(binary []byte) (address *Address, _ error) { - a := new(Address) - if err := a.SetBytes(common.CopyBytes(binary)); err != nil { - return nil, err - } - return a, nil -} - -// NewAddressFromHex converts a hex string to a address value. -func NewAddressFromHex(hex string) (address *Address, _ error) { - a := new(Address) - if err := a.SetHex(hex); err != nil { - return nil, err - } - return a, nil -} - -// SetBytes sets the specified slice of bytes as the address value. -func (a *Address) SetBytes(address []byte) error { - if length := len(address); length != common.AddressLength { - return fmt.Errorf("invalid address length: %v != %v", length, common.AddressLength) - } - copy(a.address[:], address) - return nil -} - -// GetBytes retrieves the byte representation of the address. -func (a *Address) GetBytes() []byte { - return a.address[:] -} - -// SetHex sets the specified hex string as the address value. -func (a *Address) SetHex(address string) error { - address = strings.ToLower(address) - if len(address) >= 2 && address[:2] == "0x" { - address = address[2:] - } - if length := len(address); length != 2*common.AddressLength { - return fmt.Errorf("invalid address hex length: %v != %v", length, 2*common.AddressLength) - } - bin, err := hex.DecodeString(address) - if err != nil { - return err - } - copy(a.address[:], bin) - return nil -} - -// GetHex retrieves the hex string representation of the address. -func (a *Address) GetHex() string { - return a.address.Hex() -} - -// Addresses represents a slice of addresses. -type Addresses struct{ addresses []common.Address } - -// NewAddresses creates a slice of uninitialized addresses. -func NewAddresses(size int) *Addresses { - return &Addresses{ - addresses: make([]common.Address, size), - } -} - -// NewAddressesEmpty creates an empty slice of Addresses values. -func NewAddressesEmpty() *Addresses { - return NewAddresses(0) -} - -// Size returns the number of addresses in the slice. -func (a *Addresses) Size() int { - return len(a.addresses) -} - -// Get returns the address at the given index from the slice. -func (a *Addresses) Get(index int) (address *Address, _ error) { - if index < 0 || index >= len(a.addresses) { - return nil, errors.New("index out of bounds") - } - return &Address{a.addresses[index]}, nil -} - -// Set sets the address at the given index in the slice. -func (a *Addresses) Set(index int, address *Address) error { - if index < 0 || index >= len(a.addresses) { - return errors.New("index out of bounds") - } - a.addresses[index] = address.address - return nil -} - -// Append adds a new address element to the end of the slice. -func (a *Addresses) Append(address *Address) { - a.addresses = append(a.addresses, address.address) -} diff --git a/mobile/context.go b/mobile/context.go deleted file mode 100644 index f1fff9011471..000000000000 --- a/mobile/context.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the golang.org/x/net/context package to support -// client side context management on mobile platforms. - -package geth - -import ( - "context" - "time" -) - -// Context carries a deadline, a cancelation signal, and other values across API -// boundaries. -type Context struct { - context context.Context - cancel context.CancelFunc -} - -// NewContext returns a non-nil, empty Context. It is never canceled, has no -// values, and has no deadline. It is typically used by the main function, -// initialization, and tests, and as the top-level Context for incoming requests. -func NewContext() *Context { - return &Context{ - context: context.Background(), - } -} - -// WithCancel returns a copy of the original context with cancellation mechanism -// included. -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this Context complete. -func (c *Context) WithCancel() *Context { - child, cancel := context.WithCancel(c.context) - return &Context{ - context: child, - cancel: cancel, - } -} - -// WithDeadline returns a copy of the original context with the deadline adjusted -// to be no later than the specified time. -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this Context complete. -func (c *Context) WithDeadline(sec int64, nsec int64) *Context { - child, cancel := context.WithDeadline(c.context, time.Unix(sec, nsec)) - return &Context{ - context: child, - cancel: cancel, - } -} - -// WithTimeout returns a copy of the original context with the deadline adjusted -// to be no later than now + the duration specified. -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this Context complete. -func (c *Context) WithTimeout(nsec int64) *Context { - child, cancel := context.WithTimeout(c.context, time.Duration(nsec)) - return &Context{ - context: child, - cancel: cancel, - } -} diff --git a/mobile/discover.go b/mobile/discover.go deleted file mode 100644 index b46c1d6f2517..000000000000 --- a/mobile/discover.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the accounts package to support client side enode -// management on mobile platforms. - -package geth - -import ( - "errors" - - "github.com/XinFinOrg/XDPoSChain/p2p/discv5" -) - -// Enode represents a host on the network. -type Enode struct { - node *discv5.Node -} - -// NewEnode parses a node designator. -// -// There are two basic forms of node designators -// - incomplete nodes, which only have the public key (node ID) -// - complete nodes, which contain the public key and IP/Port information -// -// For incomplete nodes, the designator must look like one of these -// -// enode:// -// -// -// For complete nodes, the node ID is encoded in the username portion -// of the URL, separated from the host by an @ sign. The hostname can -// only be given as an IP address, DNS domain names are not allowed. -// The port in the host name section is the TCP listening port. If the -// TCP and UDP (discovery) ports differ, the UDP port is specified as -// query parameter "discport". -// -// In the following example, the node URL describes -// a node with IP address 10.3.58.6, TCP listening port 30303 -// and UDP discovery port 30301. -// -// enode://@10.3.58.6:30303?discport=30301 -func NewEnode(rawurl string) (enode *Enode, _ error) { - node, err := discv5.ParseNode(rawurl) - if err != nil { - return nil, err - } - return &Enode{node}, nil -} - -// Enodes represents a slice of accounts. -type Enodes struct{ nodes []*discv5.Node } - -// NewEnodes creates a slice of uninitialized enodes. -func NewEnodes(size int) *Enodes { - return &Enodes{ - nodes: make([]*discv5.Node, size), - } -} - -// NewEnodesEmpty creates an empty slice of Enode values. -func NewEnodesEmpty() *Enodes { - return NewEnodes(0) -} - -// Size returns the number of enodes in the slice. -func (e *Enodes) Size() int { - return len(e.nodes) -} - -// Get returns the enode at the given index from the slice. -func (e *Enodes) Get(index int) (enode *Enode, _ error) { - if index < 0 || index >= len(e.nodes) { - return nil, errors.New("index out of bounds") - } - return &Enode{e.nodes[index]}, nil -} - -// Set sets the enode at the given index in the slice. -func (e *Enodes) Set(index int, enode *Enode) error { - if index < 0 || index >= len(e.nodes) { - return errors.New("index out of bounds") - } - e.nodes[index] = enode.node - return nil -} - -// Append adds a new enode element to the end of the slice. -func (e *Enodes) Append(enode *Enode) { - e.nodes = append(e.nodes, enode.node) -} diff --git a/mobile/doc.go b/mobile/doc.go deleted file mode 100644 index 64d47bec2a2a..000000000000 --- a/mobile/doc.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package geth contains the simplified mobile APIs to go-ethereum. -// -// The scope of this package is *not* to allow writing a custom Ethereum client -// with pieces plucked from go-ethereum, rather to allow writing native dapps on -// mobile platforms. Keep this in mind when using or extending this package! -// -// API limitations -// -// Since gomobile cannot bridge arbitrary types between Go and Android/iOS, the -// exposed APIs need to be manually wrapped into simplified types, with custom -// constructors and getters/setters to ensure that they can be meaninfully used -// from Java/ObjC too. -// -// With this in mind, please try to limit the scope of this package and only add -// essentials without which mobile support cannot work, especially since manually -// syncing the code will be unwieldy otherwise. In the long term we might consider -// writing custom library generators, but those are out of scope now. -// -// Content wise each file in this package corresponds to an entire Go package -// from the go-ethereum repository. Please adhere to this scoping to prevent this -// package getting unmaintainable. -// -// Wrapping guidelines: -// -// Every type that is to be exposed should be wrapped into its own plain struct, -// which internally contains a single field: the original go-ethereum version. -// This is needed because gomobile cannot expose named types for now. -// -// Whenever a method argument or a return type is a custom struct, the pointer -// variant should always be used as value types crossing over between language -// boundaries might have strange behaviors. -// -// Slices of types should be converted into a single multiplicative type wrapping -// a go slice with the methods `Size`, `Get` and `Set`. Further slice operations -// should not be provided to limit the remote code complexity. Arrays should be -// avoided as much as possible since they complicate bounds checking. -// -// If a method has multiple return values (e.g. some return + an error), those -// are generated as output arguments in ObjC. To avoid weird generated names like -// ret_0 for them, please always assign names to output variables if tuples. -// -// Note, a panic *cannot* cross over language boundaries, instead will result in -// an undebuggable SEGFAULT in the process. For error handling only ever use error -// returns, which may be the only or the second return. -package geth diff --git a/mobile/ethclient.go b/mobile/ethclient.go deleted file mode 100644 index 8a06b1f38cde..000000000000 --- a/mobile/ethclient.go +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains a wrapper for the Ethereum client. - -package geth - -import ( - "math/big" - - "github.com/XinFinOrg/XDPoSChain/core/types" - "github.com/XinFinOrg/XDPoSChain/ethclient" -) - -// EthereumClient provides access to the Ethereum APIs. -type EthereumClient struct { - client *ethclient.Client -} - -// NewEthereumClient connects a client to the given URL. -func NewEthereumClient(rawurl string) (client *EthereumClient, _ error) { - rawClient, err := ethclient.Dial(rawurl) - return &EthereumClient{rawClient}, err -} - -// GetBlockByHash returns the given full block. -func (ec *EthereumClient) GetBlockByHash(ctx *Context, hash *Hash) (block *Block, _ error) { - rawBlock, err := ec.client.BlockByHash(ctx.context, hash.hash) - return &Block{rawBlock}, err -} - -// GetBlockByNumber returns a block from the current canonical chain. If number is <0, the -// latest known block is returned. -func (ec *EthereumClient) GetBlockByNumber(ctx *Context, number int64) (block *Block, _ error) { - if number < 0 { - rawBlock, err := ec.client.BlockByNumber(ctx.context, nil) - return &Block{rawBlock}, err - } - rawBlock, err := ec.client.BlockByNumber(ctx.context, big.NewInt(number)) - return &Block{rawBlock}, err -} - -// GetHeaderByHash returns the block header with the given hash. -func (ec *EthereumClient) GetHeaderByHash(ctx *Context, hash *Hash) (header *Header, _ error) { - rawHeader, err := ec.client.HeaderByHash(ctx.context, hash.hash) - return &Header{rawHeader}, err -} - -// GetHeaderByNumber returns a block header from the current canonical chain. If number is <0, -// the latest known header is returned. -func (ec *EthereumClient) GetHeaderByNumber(ctx *Context, number int64) (header *Header, _ error) { - if number < 0 { - rawHeader, err := ec.client.HeaderByNumber(ctx.context, nil) - return &Header{rawHeader}, err - } - rawHeader, err := ec.client.HeaderByNumber(ctx.context, big.NewInt(number)) - return &Header{rawHeader}, err -} - -// GetTransactionByHash returns the transaction with the given hash. -func (ec *EthereumClient) GetTransactionByHash(ctx *Context, hash *Hash) (tx *Transaction, _ error) { - // TODO(karalabe): handle isPending - rawTx, _, err := ec.client.TransactionByHash(ctx.context, hash.hash) - return &Transaction{rawTx}, err -} - -// GetTransactionSender returns the sender address of a transaction. The transaction must -// be included in blockchain at the given block and index. -func (ec *EthereumClient) GetTransactionSender(ctx *Context, tx *Transaction, blockhash *Hash, index int) (sender *Address, _ error) { - addr, err := ec.client.TransactionSender(ctx.context, tx.tx, blockhash.hash, uint(index)) - return &Address{addr}, err -} - -// GetTransactionCount returns the total number of transactions in the given block. -func (ec *EthereumClient) GetTransactionCount(ctx *Context, hash *Hash) (count int, _ error) { - rawCount, err := ec.client.TransactionCount(ctx.context, hash.hash) - return int(rawCount), err -} - -// GetTransactionInBlock returns a single transaction at index in the given block. -func (ec *EthereumClient) GetTransactionInBlock(ctx *Context, hash *Hash, index int) (tx *Transaction, _ error) { - rawTx, err := ec.client.TransactionInBlock(ctx.context, hash.hash, uint(index)) - return &Transaction{rawTx}, err - -} - -// GetTransactionReceipt returns the receipt of a transaction by transaction hash. -// Note that the receipt is not available for pending transactions. -func (ec *EthereumClient) GetTransactionReceipt(ctx *Context, hash *Hash) (receipt *Receipt, _ error) { - rawReceipt, err := ec.client.TransactionReceipt(ctx.context, hash.hash) - return &Receipt{rawReceipt}, err -} - -// SyncProgress retrieves the current progress of the sync algorithm. If there's -// no sync currently running, it returns nil. -func (ec *EthereumClient) SyncProgress(ctx *Context) (progress *SyncProgress, _ error) { - rawProgress, err := ec.client.SyncProgress(ctx.context) - if rawProgress == nil { - return nil, err - } - return &SyncProgress{*rawProgress}, err -} - -// NewHeadHandler is a client-side subscription callback to invoke on events and -// subscription failure. -type NewHeadHandler interface { - OnNewHead(header *Header) - OnError(failure string) -} - -// SubscribeNewHead subscribes to notifications about the current blockchain head -// on the given channel. -func (ec *EthereumClient) SubscribeNewHead(ctx *Context, handler NewHeadHandler, buffer int) (sub *Subscription, _ error) { - // Subscribe to the event internally - ch := make(chan *types.Header, buffer) - rawSub, err := ec.client.SubscribeNewHead(ctx.context, ch) - if err != nil { - return nil, err - } - // Start up a dispatcher to feed into the callback - go func() { - for { - select { - case header := <-ch: - handler.OnNewHead(&Header{header}) - - case err := <-rawSub.Err(): - handler.OnError(err.Error()) - return - } - } - }() - return &Subscription{rawSub}, nil -} - -// State Access - -// GetBalanceAt returns the wei balance of the given account. -// The block number can be <0, in which case the balance is taken from the latest known block. -func (ec *EthereumClient) GetBalanceAt(ctx *Context, account *Address, number int64) (balance *BigInt, _ error) { - if number < 0 { - rawBalance, err := ec.client.BalanceAt(ctx.context, account.address, nil) - return &BigInt{rawBalance}, err - } - rawBalance, err := ec.client.BalanceAt(ctx.context, account.address, big.NewInt(number)) - return &BigInt{rawBalance}, err -} - -// GetStorageAt returns the value of key in the contract storage of the given account. -// The block number can be <0, in which case the value is taken from the latest known block. -func (ec *EthereumClient) GetStorageAt(ctx *Context, account *Address, key *Hash, number int64) (storage []byte, _ error) { - if number < 0 { - return ec.client.StorageAt(ctx.context, account.address, key.hash, nil) - } - return ec.client.StorageAt(ctx.context, account.address, key.hash, big.NewInt(number)) -} - -// GetCodeAt returns the contract code of the given account. -// The block number can be <0, in which case the code is taken from the latest known block. -func (ec *EthereumClient) GetCodeAt(ctx *Context, account *Address, number int64) (code []byte, _ error) { - if number < 0 { - return ec.client.CodeAt(ctx.context, account.address, nil) - } - return ec.client.CodeAt(ctx.context, account.address, big.NewInt(number)) -} - -// GetNonceAt returns the account nonce of the given account. -// The block number can be <0, in which case the nonce is taken from the latest known block. -func (ec *EthereumClient) GetNonceAt(ctx *Context, account *Address, number int64) (nonce int64, _ error) { - if number < 0 { - rawNonce, err := ec.client.NonceAt(ctx.context, account.address, nil) - return int64(rawNonce), err - } - rawNonce, err := ec.client.NonceAt(ctx.context, account.address, big.NewInt(number)) - return int64(rawNonce), err -} - -// Filters - -// FilterLogs executes a filter query. -func (ec *EthereumClient) FilterLogs(ctx *Context, query *FilterQuery) (logs *Logs, _ error) { - rawLogs, err := ec.client.FilterLogs(ctx.context, query.query) - if err != nil { - return nil, err - } - // Temp hack due to vm.Logs being []*vm.Log - res := make([]*types.Log, len(rawLogs)) - for i := range rawLogs { - res[i] = &rawLogs[i] - } - return &Logs{res}, nil -} - -// FilterLogsHandler is a client-side subscription callback to invoke on events and -// subscription failure. -type FilterLogsHandler interface { - OnFilterLogs(log *Log) - OnError(failure string) -} - -// SubscribeFilterLogs subscribes to the results of a streaming filter query. -func (ec *EthereumClient) SubscribeFilterLogs(ctx *Context, query *FilterQuery, handler FilterLogsHandler, buffer int) (sub *Subscription, _ error) { - // Subscribe to the event internally - ch := make(chan types.Log, buffer) - rawSub, err := ec.client.SubscribeFilterLogs(ctx.context, query.query, ch) - if err != nil { - return nil, err - } - // Start up a dispatcher to feed into the callback - go func() { - for { - select { - case log := <-ch: - handler.OnFilterLogs(&Log{&log}) - - case err := <-rawSub.Err(): - handler.OnError(err.Error()) - return - } - } - }() - return &Subscription{rawSub}, nil -} - -// Pending State - -// GetPendingBalanceAt returns the wei balance of the given account in the pending state. -func (ec *EthereumClient) GetPendingBalanceAt(ctx *Context, account *Address) (balance *BigInt, _ error) { - rawBalance, err := ec.client.PendingBalanceAt(ctx.context, account.address) - return &BigInt{rawBalance}, err -} - -// GetPendingStorageAt returns the value of key in the contract storage of the given account in the pending state. -func (ec *EthereumClient) GetPendingStorageAt(ctx *Context, account *Address, key *Hash) (storage []byte, _ error) { - return ec.client.PendingStorageAt(ctx.context, account.address, key.hash) -} - -// GetPendingCodeAt returns the contract code of the given account in the pending state. -func (ec *EthereumClient) GetPendingCodeAt(ctx *Context, account *Address) (code []byte, _ error) { - return ec.client.PendingCodeAt(ctx.context, account.address) -} - -// GetPendingNonceAt returns the account nonce of the given account in the pending state. -// This is the nonce that should be used for the next transaction. -func (ec *EthereumClient) GetPendingNonceAt(ctx *Context, account *Address) (nonce int64, _ error) { - rawNonce, err := ec.client.PendingNonceAt(ctx.context, account.address) - return int64(rawNonce), err -} - -// GetPendingTransactionCount returns the total number of transactions in the pending state. -func (ec *EthereumClient) GetPendingTransactionCount(ctx *Context) (count int, _ error) { - rawCount, err := ec.client.PendingTransactionCount(ctx.context) - return int(rawCount), err -} - -// Contract Calling - -// CallContract executes a message call transaction, which is directly executed in the VM -// of the node, but never mined into the blockchain. -// -// blockNumber selects the block height at which the call runs. It can be <0, in which -// case the code is taken from the latest known block. Note that state from very old -// blocks might not be available. -func (ec *EthereumClient) CallContract(ctx *Context, msg *CallMsg, number int64) (output []byte, _ error) { - if number < 0 { - return ec.client.CallContract(ctx.context, msg.msg, nil) - } - return ec.client.CallContract(ctx.context, msg.msg, big.NewInt(number)) -} - -// PendingCallContract executes a message call transaction using the EVM. -// The state seen by the contract call is the pending state. -func (ec *EthereumClient) PendingCallContract(ctx *Context, msg *CallMsg) (output []byte, _ error) { - return ec.client.PendingCallContract(ctx.context, msg.msg) -} - -// SuggestGasPrice retrieves the currently suggested gas price to allow a timely -// execution of a transaction. -func (ec *EthereumClient) SuggestGasPrice(ctx *Context) (price *BigInt, _ error) { - rawPrice, err := ec.client.SuggestGasPrice(ctx.context) - return &BigInt{rawPrice}, err -} - -// EstimateGas tries to estimate the gas needed to execute a specific transaction based on -// the current pending state of the backend blockchain. There is no guarantee that this is -// the true gas limit requirement as other transactions may be added or removed by miners, -// but it should provide a basis for setting a reasonable default. -func (ec *EthereumClient) EstimateGas(ctx *Context, msg *CallMsg) (gas int64, _ error) { - rawGas, err := ec.client.EstimateGas(ctx.context, msg.msg) - return int64(rawGas), err -} - -// SendTransaction injects a signed transaction into the pending pool for execution. -// -// If the transaction was a contract creation use the TransactionReceipt method to get the -// contract address after the transaction has been mined. -func (ec *EthereumClient) SendTransaction(ctx *Context, tx *Transaction) error { - return ec.client.SendTransaction(ctx.context, tx.tx) -} diff --git a/mobile/ethereum.go b/mobile/ethereum.go deleted file mode 100644 index ac21afdd6b56..000000000000 --- a/mobile/ethereum.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the go-ethereum root package. - -package geth - -import ( - "errors" - - ethereum "github.com/XinFinOrg/XDPoSChain" - "github.com/XinFinOrg/XDPoSChain/common" -) - -// Subscription represents an event subscription where events are -// delivered on a data channel. -type Subscription struct { - sub ethereum.Subscription -} - -// Unsubscribe cancels the sending of events to the data channel -// and closes the error channel. -func (s *Subscription) Unsubscribe() { - s.sub.Unsubscribe() -} - -// CallMsg contains parameters for contract calls. -type CallMsg struct { - msg ethereum.CallMsg -} - -// NewCallMsg creates an empty contract call parameter list. -func NewCallMsg() *CallMsg { - return new(CallMsg) -} - -func (msg *CallMsg) GetFrom() *Address { return &Address{msg.msg.From} } -func (msg *CallMsg) GetGas() int64 { return int64(msg.msg.Gas) } -func (msg *CallMsg) GetGasPrice() *BigInt { return &BigInt{msg.msg.GasPrice} } -func (msg *CallMsg) GetValue() *BigInt { return &BigInt{msg.msg.Value} } -func (msg *CallMsg) GetData() []byte { return msg.msg.Data } -func (msg *CallMsg) GetTo() *Address { - if to := msg.msg.To; to != nil { - return &Address{*msg.msg.To} - } - return nil -} - -func (msg *CallMsg) SetFrom(address *Address) { msg.msg.From = address.address } -func (msg *CallMsg) SetGas(gas int64) { msg.msg.Gas = uint64(gas) } -func (msg *CallMsg) SetGasPrice(price *BigInt) { msg.msg.GasPrice = price.bigint } -func (msg *CallMsg) SetValue(value *BigInt) { msg.msg.Value = value.bigint } -func (msg *CallMsg) SetData(data []byte) { msg.msg.Data = common.CopyBytes(data) } - -func (msg *CallMsg) SetTo(address *Address) { - if address == nil { - msg.msg.To = nil - } else { - msg.msg.To = &address.address - } -} - -// SyncProgress gives progress indications when the node is synchronising with -// the Ethereum network. -type SyncProgress struct { - progress ethereum.SyncProgress -} - -func (p *SyncProgress) GetStartingBlock() int64 { return int64(p.progress.StartingBlock) } -func (p *SyncProgress) GetCurrentBlock() int64 { return int64(p.progress.CurrentBlock) } -func (p *SyncProgress) GetHighestBlock() int64 { return int64(p.progress.HighestBlock) } -func (p *SyncProgress) GetPulledStates() int64 { return int64(p.progress.PulledStates) } -func (p *SyncProgress) GetKnownStates() int64 { return int64(p.progress.KnownStates) } - -// Topics is a set of topic lists to filter events with. -type Topics struct{ topics [][]common.Hash } - -// NewTopics creates a slice of uninitialized Topics. -func NewTopics(size int) *Topics { - return &Topics{ - topics: make([][]common.Hash, size), - } -} - -// NewTopicsEmpty creates an empty slice of Topics values. -func NewTopicsEmpty() *Topics { - return NewTopics(0) -} - -// Size returns the number of topic lists inside the set -func (t *Topics) Size() int { - return len(t.topics) -} - -// Get returns the topic list at the given index from the slice. -func (t *Topics) Get(index int) (hashes *Hashes, _ error) { - if index < 0 || index >= len(t.topics) { - return nil, errors.New("index out of bounds") - } - return &Hashes{t.topics[index]}, nil -} - -// Set sets the topic list at the given index in the slice. -func (t *Topics) Set(index int, topics *Hashes) error { - if index < 0 || index >= len(t.topics) { - return errors.New("index out of bounds") - } - t.topics[index] = topics.hashes - return nil -} - -// Append adds a new topic list to the end of the slice. -func (t *Topics) Append(topics *Hashes) { - t.topics = append(t.topics, topics.hashes) -} - -// FilterQuery contains options for contact log filtering. -type FilterQuery struct { - query ethereum.FilterQuery -} - -// NewFilterQuery creates an empty filter query for contact log filtering. -func NewFilterQuery() *FilterQuery { - return new(FilterQuery) -} - -func (fq *FilterQuery) GetFromBlock() *BigInt { return &BigInt{fq.query.FromBlock} } -func (fq *FilterQuery) GetToBlock() *BigInt { return &BigInt{fq.query.ToBlock} } -func (fq *FilterQuery) GetAddresses() *Addresses { return &Addresses{fq.query.Addresses} } -func (fq *FilterQuery) GetTopics() *Topics { return &Topics{fq.query.Topics} } - -func (fq *FilterQuery) SetFromBlock(fromBlock *BigInt) { fq.query.FromBlock = fromBlock.bigint } -func (fq *FilterQuery) SetToBlock(toBlock *BigInt) { fq.query.ToBlock = toBlock.bigint } -func (fq *FilterQuery) SetAddresses(addresses *Addresses) { fq.query.Addresses = addresses.addresses } -func (fq *FilterQuery) SetTopics(topics *Topics) { fq.query.Topics = topics.topics } diff --git a/mobile/geth.go b/mobile/geth.go deleted file mode 100644 index 065d8a1a3374..000000000000 --- a/mobile/geth.go +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the node package to support client side node -// management on mobile platforms. - -package geth - -import ( - "encoding/json" - "fmt" - "path/filepath" - - "github.com/XinFinOrg/XDPoSChain/core" - "github.com/XinFinOrg/XDPoSChain/eth/downloader" - "github.com/XinFinOrg/XDPoSChain/eth/ethconfig" - "github.com/XinFinOrg/XDPoSChain/ethclient" - "github.com/XinFinOrg/XDPoSChain/ethstats" - "github.com/XinFinOrg/XDPoSChain/les" - "github.com/XinFinOrg/XDPoSChain/node" - "github.com/XinFinOrg/XDPoSChain/p2p" - "github.com/XinFinOrg/XDPoSChain/p2p/nat" - "github.com/XinFinOrg/XDPoSChain/params" -) - -// NodeConfig represents the collection of configuration values to fine tune the Geth -// node embedded into a mobile process. The available values are a subset of the -// entire API provided by go-ethereum to reduce the maintenance surface and dev -// complexity. -type NodeConfig struct { - // Bootstrap nodes used to establish connectivity with the rest of the network. - BootstrapNodes *Enodes - - // MaxPeers is the maximum number of peers that can be connected. If this is - // set to zero, then only the configured static and trusted peers can connect. - MaxPeers int - - // EthereumEnabled specifies whether the node should run the Ethereum protocol. - EthereumEnabled bool - - // EthereumNetworkID is the network identifier used by the Ethereum protocol to - // decide if remote peers should be accepted or not. - EthereumNetworkID int64 // uint64 in truth, but Java can't handle that... - - // EthereumGenesis is the genesis JSON to use to seed the blockchain with. An - // empty genesis state is equivalent to using the mainnet's state. - EthereumGenesis string - - // EthereumDatabaseCache is the system memory in MB to allocate for database caching. - // A minimum of 16MB is always reserved. - EthereumDatabaseCache int - - // EthereumNetStats is a netstats connection string to use to report various - // chain, transaction and node stats to a monitoring server. - // - // It has the form "nodename:secret@host:port" - EthereumNetStats string -} - -// defaultNodeConfig contains the default node configuration values to use if all -// or some fields are missing from the user's specified list. -var defaultNodeConfig = &NodeConfig{ - BootstrapNodes: FoundationBootnodes(), - MaxPeers: 25, - EthereumEnabled: true, - EthereumNetworkID: 1, - EthereumDatabaseCache: 16, -} - -// NewNodeConfig creates a new node option set, initialized to the default values. -func NewNodeConfig() *NodeConfig { - config := *defaultNodeConfig - return &config -} - -// Node represents a Geth Ethereum node instance. -type Node struct { - node *node.Node -} - -// NewNode creates and configures a new Geth node. -func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) { - // If no or partial configurations were specified, use defaults - if config == nil { - config = NewNodeConfig() - } - if config.MaxPeers == 0 { - config.MaxPeers = defaultNodeConfig.MaxPeers - } - if config.BootstrapNodes == nil || config.BootstrapNodes.Size() == 0 { - config.BootstrapNodes = defaultNodeConfig.BootstrapNodes - } - // Create the empty networking stack - nodeConf := &node.Config{ - Name: clientIdentifier, - Version: params.Version, - DataDir: datadir, - KeyStoreDir: filepath.Join(datadir, "keystore"), // Mobile should never use internal keystores! - P2P: p2p.Config{ - NoDiscovery: true, - DiscoveryV5: true, - BootstrapNodesV5: config.BootstrapNodes.nodes, - ListenAddr: ":0", - NAT: nat.Any(), - MaxPeers: config.MaxPeers, - }, - } - rawStack, err := node.New(nodeConf) - if err != nil { - return nil, err - } - - var genesis *core.Genesis - if config.EthereumGenesis != "" { - // Parse the user supplied genesis spec if not mainnet - genesis = new(core.Genesis) - if err := json.Unmarshal([]byte(config.EthereumGenesis), genesis); err != nil { - return nil, fmt.Errorf("invalid genesis spec: %v", err) - } - // If we have the testnet, hard code the chain configs too - if config.EthereumGenesis == TestnetGenesis() { - genesis.Config = params.TestnetChainConfig - if config.EthereumNetworkID == 1 { - config.EthereumNetworkID = 3 - } - } - } - // Register the Ethereum protocol if requested - if config.EthereumEnabled { - ethConf := ethconfig.Defaults - ethConf.Genesis = genesis - ethConf.SyncMode = downloader.LightSync - ethConf.NetworkId = uint64(config.EthereumNetworkID) - ethConf.DatabaseCache = config.EthereumDatabaseCache - if err := rawStack.Register(func(ctx *node.ServiceContext) (node.Service, error) { - return les.New(ctx, ðConf) - }); err != nil { - return nil, fmt.Errorf("ethereum init: %v", err) - } - // If netstats reporting is requested, do it - if config.EthereumNetStats != "" { - if err := rawStack.Register(func(ctx *node.ServiceContext) (node.Service, error) { - var lesServ *les.LightEthereum - ctx.Service(&lesServ) - - return ethstats.New(config.EthereumNetStats, nil, lesServ) - }); err != nil { - return nil, fmt.Errorf("netstats init: %v", err) - } - } - } - return &Node{rawStack}, nil -} - -// Start creates a live P2P node and starts running it. -func (n *Node) Start() error { - return n.node.Start() -} - -// Stop terminates a running node along with all it's services. In the node was -// not started, an error is returned. -func (n *Node) Stop() error { - return n.node.Stop() -} - -// GetEthereumClient retrieves a client to access the Ethereum subsystem. -func (n *Node) GetEthereumClient() (client *EthereumClient, _ error) { - rpc, err := n.node.Attach() - if err != nil { - return nil, err - } - return &EthereumClient{ethclient.NewClient(rpc)}, nil -} - -// GetNodeInfo gathers and returns a collection of metadata known about the host. -func (n *Node) GetNodeInfo() *NodeInfo { - return &NodeInfo{n.node.Server().NodeInfo()} -} - -// GetPeersInfo returns an array of metadata objects describing connected peers. -func (n *Node) GetPeersInfo() *PeerInfos { - return &PeerInfos{n.node.Server().PeersInfo()} -} diff --git a/mobile/geth_android.go b/mobile/geth_android.go deleted file mode 100644 index 8e4ebe638f8c..000000000000 --- a/mobile/geth_android.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// +build android - -package geth - -// clientIdentifier is a hard coded identifier to report into the network. -var clientIdentifier = "GethDroid" diff --git a/mobile/geth_ios.go b/mobile/geth_ios.go deleted file mode 100644 index 307cd0858044..000000000000 --- a/mobile/geth_ios.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// +build ios - -package geth - -// clientIdentifier is a hard coded identifier to report into the network. -var clientIdentifier = "iGeth" diff --git a/mobile/geth_other.go b/mobile/geth_other.go deleted file mode 100644 index 6f0c5dda683c..000000000000 --- a/mobile/geth_other.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// +build !android,!ios - -package geth - -// clientIdentifier is a hard coded identifier to report into the network. -var clientIdentifier = "GethMobile" diff --git a/mobile/init.go b/mobile/init.go deleted file mode 100644 index 52e2de10f7fb..000000000000 --- a/mobile/init.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains initialization code for the mbile library. - -package geth - -import ( - "os" - "runtime" - - "github.com/XinFinOrg/XDPoSChain/log" -) - -func init() { - // Initialize the logger - log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, false))) - - // Initialize the goroutine count - runtime.GOMAXPROCS(runtime.NumCPU()) -} diff --git a/mobile/interface.go b/mobile/interface.go deleted file mode 100644 index 42ec9bb50a0d..000000000000 --- a/mobile/interface.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains perverted wrappers to allow crossing over empty interfaces. - -package geth - -import ( - "errors" - "math/big" - - "github.com/XinFinOrg/XDPoSChain/common" -) - -// Interface represents a wrapped version of Go's interface{}, with the capacity -// to store arbitrary data types. -// -// Since it's impossible to get the arbitrary-ness converted between Go and mobile -// platforms, we're using explicit getters and setters for the conversions. There -// is of course no point in enumerating everything, just enough to support the -// contract bindins requiring client side generated code. -type Interface struct { - object interface{} -} - -// NewInterface creates a new empty interface that can be used to pass around -// generic types. -func NewInterface() *Interface { - return new(Interface) -} - -func (i *Interface) SetBool(b bool) { i.object = &b } -func (i *Interface) SetBools(bs []bool) { i.object = &bs } -func (i *Interface) SetString(str string) { i.object = &str } -func (i *Interface) SetStrings(strs *Strings) { i.object = &strs.strs } -func (i *Interface) SetBinary(binary []byte) { b := common.CopyBytes(binary); i.object = &b } -func (i *Interface) SetBinaries(binaries [][]byte) { i.object = &binaries } -func (i *Interface) SetAddress(address *Address) { i.object = &address.address } -func (i *Interface) SetAddresses(addrs *Addresses) { i.object = &addrs.addresses } -func (i *Interface) SetHash(hash *Hash) { i.object = &hash.hash } -func (i *Interface) SetHashes(hashes *Hashes) { i.object = &hashes.hashes } -func (i *Interface) SetInt8(n int8) { i.object = &n } -func (i *Interface) SetInt16(n int16) { i.object = &n } -func (i *Interface) SetInt32(n int32) { i.object = &n } -func (i *Interface) SetInt64(n int64) { i.object = &n } -func (i *Interface) SetUint8(bigint *BigInt) { n := uint8(bigint.bigint.Uint64()); i.object = &n } -func (i *Interface) SetUint16(bigint *BigInt) { n := uint16(bigint.bigint.Uint64()); i.object = &n } -func (i *Interface) SetUint32(bigint *BigInt) { n := uint32(bigint.bigint.Uint64()); i.object = &n } -func (i *Interface) SetUint64(bigint *BigInt) { n := bigint.bigint.Uint64(); i.object = &n } -func (i *Interface) SetBigInt(bigint *BigInt) { i.object = &bigint.bigint } -func (i *Interface) SetBigInts(bigints *BigInts) { i.object = &bigints.bigints } - -func (i *Interface) SetDefaultBool() { i.object = new(bool) } -func (i *Interface) SetDefaultBools() { i.object = new([]bool) } -func (i *Interface) SetDefaultString() { i.object = new(string) } -func (i *Interface) SetDefaultStrings() { i.object = new([]string) } -func (i *Interface) SetDefaultBinary() { i.object = new([]byte) } -func (i *Interface) SetDefaultBinaries() { i.object = new([][]byte) } -func (i *Interface) SetDefaultAddress() { i.object = new(common.Address) } -func (i *Interface) SetDefaultAddresses() { i.object = new([]common.Address) } -func (i *Interface) SetDefaultHash() { i.object = new(common.Hash) } -func (i *Interface) SetDefaultHashes() { i.object = new([]common.Hash) } -func (i *Interface) SetDefaultInt8() { i.object = new(int8) } -func (i *Interface) SetDefaultInt16() { i.object = new(int16) } -func (i *Interface) SetDefaultInt32() { i.object = new(int32) } -func (i *Interface) SetDefaultInt64() { i.object = new(int64) } -func (i *Interface) SetDefaultUint8() { i.object = new(uint8) } -func (i *Interface) SetDefaultUint16() { i.object = new(uint16) } -func (i *Interface) SetDefaultUint32() { i.object = new(uint32) } -func (i *Interface) SetDefaultUint64() { i.object = new(uint64) } -func (i *Interface) SetDefaultBigInt() { i.object = new(*big.Int) } -func (i *Interface) SetDefaultBigInts() { i.object = new([]*big.Int) } - -func (i *Interface) GetBool() bool { return *i.object.(*bool) } -func (i *Interface) GetBools() []bool { return *i.object.(*[]bool) } -func (i *Interface) GetString() string { return *i.object.(*string) } -func (i *Interface) GetStrings() *Strings { return &Strings{*i.object.(*[]string)} } -func (i *Interface) GetBinary() []byte { return *i.object.(*[]byte) } -func (i *Interface) GetBinaries() [][]byte { return *i.object.(*[][]byte) } -func (i *Interface) GetAddress() *Address { return &Address{*i.object.(*common.Address)} } -func (i *Interface) GetAddresses() *Addresses { return &Addresses{*i.object.(*[]common.Address)} } -func (i *Interface) GetHash() *Hash { return &Hash{*i.object.(*common.Hash)} } -func (i *Interface) GetHashes() *Hashes { return &Hashes{*i.object.(*[]common.Hash)} } -func (i *Interface) GetInt8() int8 { return *i.object.(*int8) } -func (i *Interface) GetInt16() int16 { return *i.object.(*int16) } -func (i *Interface) GetInt32() int32 { return *i.object.(*int32) } -func (i *Interface) GetInt64() int64 { return *i.object.(*int64) } -func (i *Interface) GetUint8() *BigInt { - return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint8)))} -} -func (i *Interface) GetUint16() *BigInt { - return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint16)))} -} -func (i *Interface) GetUint32() *BigInt { - return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint32)))} -} -func (i *Interface) GetUint64() *BigInt { - return &BigInt{new(big.Int).SetUint64(*i.object.(*uint64))} -} -func (i *Interface) GetBigInt() *BigInt { return &BigInt{*i.object.(**big.Int)} } -func (i *Interface) GetBigInts() *BigInts { return &BigInts{*i.object.(*[]*big.Int)} } - -// Interfaces is a slices of wrapped generic objects. -type Interfaces struct { - objects []interface{} -} - -// NewInterfaces creates a slice of uninitialized interfaces. -func NewInterfaces(size int) *Interfaces { - return &Interfaces{ - objects: make([]interface{}, size), - } -} - -// Size returns the number of interfaces in the slice. -func (i *Interfaces) Size() int { - return len(i.objects) -} - -// Get returns the bigint at the given index from the slice. -func (i *Interfaces) Get(index int) (iface *Interface, _ error) { - if index < 0 || index >= len(i.objects) { - return nil, errors.New("index out of bounds") - } - return &Interface{i.objects[index]}, nil -} - -// Set sets the big int at the given index in the slice. -func (i *Interfaces) Set(index int, object *Interface) error { - if index < 0 || index >= len(i.objects) { - return errors.New("index out of bounds") - } - i.objects[index] = object.object - return nil -} diff --git a/mobile/logger.go b/mobile/logger.go deleted file mode 100644 index 444b106b377e..000000000000 --- a/mobile/logger.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package geth - -import ( - "os" - - "github.com/XinFinOrg/XDPoSChain/log" -) - -// SetVerbosity sets the global verbosity level (between 0 and 6 - see logger/verbosity.go). -func SetVerbosity(level int) { - log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.FromLegacyLevel(level), false))) -} diff --git a/mobile/p2p.go b/mobile/p2p.go deleted file mode 100644 index 87c6e341ed8f..000000000000 --- a/mobile/p2p.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains wrappers for the p2p package. - -package geth - -import ( - "errors" - - "github.com/XinFinOrg/XDPoSChain/p2p" -) - -// NodeInfo represents pi short summary of the information known about the host. -type NodeInfo struct { - info *p2p.NodeInfo -} - -func (ni *NodeInfo) GetID() string { return ni.info.ID } -func (ni *NodeInfo) GetName() string { return ni.info.Name } -func (ni *NodeInfo) GetEnode() string { return ni.info.Enode } -func (ni *NodeInfo) GetIP() string { return ni.info.IP } -func (ni *NodeInfo) GetDiscoveryPort() int { return ni.info.Ports.Discovery } -func (ni *NodeInfo) GetListenerPort() int { return ni.info.Ports.Listener } -func (ni *NodeInfo) GetListenerAddress() string { return ni.info.ListenAddr } -func (ni *NodeInfo) GetProtocols() *Strings { - protos := []string{} - for proto := range ni.info.Protocols { - protos = append(protos, proto) - } - return &Strings{protos} -} - -// PeerInfo represents pi short summary of the information known about pi connected peer. -type PeerInfo struct { - info *p2p.PeerInfo -} - -func (pi *PeerInfo) GetID() string { return pi.info.ID } -func (pi *PeerInfo) GetName() string { return pi.info.Name } -func (pi *PeerInfo) GetCaps() *Strings { return &Strings{pi.info.Caps} } -func (pi *PeerInfo) GetLocalAddress() string { return pi.info.Network.LocalAddress } -func (pi *PeerInfo) GetRemoteAddress() string { return pi.info.Network.RemoteAddress } - -// PeerInfos represents a slice of infos about remote peers. -type PeerInfos struct { - infos []*p2p.PeerInfo -} - -// Size returns the number of peer info entries in the slice. -func (pi *PeerInfos) Size() int { - return len(pi.infos) -} - -// Get returns the peer info at the given index from the slice. -func (pi *PeerInfos) Get(index int) (info *PeerInfo, _ error) { - if index < 0 || index >= len(pi.infos) { - return nil, errors.New("index out of bounds") - } - return &PeerInfo{pi.infos[index]}, nil -} diff --git a/mobile/params.go b/mobile/params.go deleted file mode 100644 index f57d83784534..000000000000 --- a/mobile/params.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the params package. - -package geth - -import ( - "encoding/json" - - "github.com/XinFinOrg/XDPoSChain/core" - "github.com/XinFinOrg/XDPoSChain/p2p/discv5" - "github.com/XinFinOrg/XDPoSChain/params" -) - -// MainnetGenesis returns the JSON spec to use for the main Ethereum network. It -// is actually empty since that defaults to the hard coded binary genesis block. -func MainnetGenesis() string { - return "" -} - -// TestnetGenesis returns the JSON spec to use for the Ethereum test network. -func TestnetGenesis() string { - enc, err := json.Marshal(core.DefaultTestnetGenesisBlock()) - if err != nil { - panic(err) - } - return string(enc) -} - -// RinkebyGenesis returns the JSON spec to use for the Rinkeby test network -func RinkebyGenesis() string { - enc, err := json.Marshal(core.DefaultRinkebyGenesisBlock()) - if err != nil { - panic(err) - } - return string(enc) -} - -// FoundationBootnodes returns the enode URLs of the P2P bootstrap nodes operated -// by the foundation running the V5 discovery protocol. -func FoundationBootnodes() *Enodes { - nodes := &Enodes{nodes: make([]*discv5.Node, len(params.DiscoveryV5Bootnodes))} - for i, url := range params.DiscoveryV5Bootnodes { - nodes.nodes[i] = discv5.MustParseNode(url) - } - return nodes -} diff --git a/mobile/primitives.go b/mobile/primitives.go deleted file mode 100644 index 5c6617fa475e..000000000000 --- a/mobile/primitives.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains various wrappers for primitive types. - -package geth - -import ( - "errors" - "fmt" -) - -// Strings represents s slice of strs. -type Strings struct{ strs []string } - -// Size returns the number of strs in the slice. -func (s *Strings) Size() int { - return len(s.strs) -} - -// Get returns the string at the given index from the slice. -func (s *Strings) Get(index int) (str string, _ error) { - if index < 0 || index >= len(s.strs) { - return "", errors.New("index out of bounds") - } - return s.strs[index], nil -} - -// Set sets the string at the given index in the slice. -func (s *Strings) Set(index int, str string) error { - if index < 0 || index >= len(s.strs) { - return errors.New("index out of bounds") - } - s.strs[index] = str - return nil -} - -// String implements the Stringer interface. -func (s *Strings) String() string { - return fmt.Sprintf("%v", s.strs) -} diff --git a/mobile/types.go b/mobile/types.go deleted file mode 100644 index 24ba9b4ddfc1..000000000000 --- a/mobile/types.go +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the core/types package. - -package geth - -import ( - "encoding/json" - "errors" - "fmt" - - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/core/types" - "github.com/XinFinOrg/XDPoSChain/rlp" -) - -// A Nonce is a 64-bit hash which proves (combined with the mix-hash) that -// a sufficient amount of computation has been carried out on a block. -type Nonce struct { - nonce types.BlockNonce -} - -// GetBytes retrieves the byte representation of the block nonce. -func (n *Nonce) GetBytes() []byte { - return n.nonce[:] -} - -// GetHex retrieves the hex string representation of the block nonce. -func (n *Nonce) GetHex() string { - return fmt.Sprintf("%#x", n.nonce[:]) -} - -// Bloom represents a 256 bit bloom filter. -type Bloom struct { - bloom types.Bloom -} - -// GetBytes retrieves the byte representation of the bloom filter. -func (b *Bloom) GetBytes() []byte { - return b.bloom[:] -} - -// GetHex retrieves the hex string representation of the bloom filter. -func (b *Bloom) GetHex() string { - return fmt.Sprintf("%#x", b.bloom[:]) -} - -// Header represents a block header in the Ethereum blockchain. -type Header struct { - header *types.Header -} - -// NewHeaderFromRLP parses a header from an RLP data dump. -func NewHeaderFromRLP(data []byte) (*Header, error) { - h := &Header{ - header: new(types.Header), - } - if err := rlp.DecodeBytes(common.CopyBytes(data), h.header); err != nil { - return nil, err - } - return h, nil -} - -// EncodeRLP encodes a header into an RLP data dump. -func (h *Header) EncodeRLP() ([]byte, error) { - return rlp.EncodeToBytes(h.header) -} - -// NewHeaderFromJSON parses a header from an JSON data dump. -func NewHeaderFromJSON(data string) (*Header, error) { - h := &Header{ - header: new(types.Header), - } - if err := json.Unmarshal([]byte(data), h.header); err != nil { - return nil, err - } - return h, nil -} - -// EncodeJSON encodes a header into an JSON data dump. -func (h *Header) EncodeJSON() (string, error) { - data, err := json.Marshal(h.header) - return string(data), err -} - -// String implements the fmt.Stringer interface to print some semi-meaningful -// data dump of the header for debugging purposes. -func (h *Header) String() string { - return h.header.String() -} - -func (h *Header) GetParentHash() *Hash { return &Hash{h.header.ParentHash} } -func (h *Header) GetUncleHash() *Hash { return &Hash{h.header.UncleHash} } -func (h *Header) GetCoinbase() *Address { return &Address{h.header.Coinbase} } -func (h *Header) GetRoot() *Hash { return &Hash{h.header.Root} } -func (h *Header) GetTxHash() *Hash { return &Hash{h.header.TxHash} } -func (h *Header) GetReceiptHash() *Hash { return &Hash{h.header.ReceiptHash} } -func (h *Header) GetBloom() *Bloom { return &Bloom{h.header.Bloom} } -func (h *Header) GetDifficulty() *BigInt { return &BigInt{h.header.Difficulty} } -func (h *Header) GetNumber() int64 { return h.header.Number.Int64() } -func (h *Header) GetGasLimit() int64 { return int64(h.header.GasLimit) } -func (h *Header) GetGasUsed() int64 { return int64(h.header.GasUsed) } -func (h *Header) GetTime() int64 { return h.header.Time.Int64() } -func (h *Header) GetExtra() []byte { return h.header.Extra } -func (h *Header) GetMixDigest() *Hash { return &Hash{h.header.MixDigest} } -func (h *Header) GetNonce() *Nonce { return &Nonce{h.header.Nonce} } -func (h *Header) GetHash() *Hash { return &Hash{h.header.Hash()} } - -// Headers represents a slice of headers. -type Headers struct{ headers []*types.Header } - -// Size returns the number of headers in the slice. -func (h *Headers) Size() int { - return len(h.headers) -} - -// Get returns the header at the given index from the slice. -func (h *Headers) Get(index int) (header *Header, _ error) { - if index < 0 || index >= len(h.headers) { - return nil, errors.New("index out of bounds") - } - return &Header{h.headers[index]}, nil -} - -// Block represents an entire block in the Ethereum blockchain. -type Block struct { - block *types.Block -} - -// NewBlockFromRLP parses a block from an RLP data dump. -func NewBlockFromRLP(data []byte) (*Block, error) { - b := &Block{ - block: new(types.Block), - } - if err := rlp.DecodeBytes(common.CopyBytes(data), b.block); err != nil { - return nil, err - } - return b, nil -} - -// EncodeRLP encodes a block into an RLP data dump. -func (b *Block) EncodeRLP() ([]byte, error) { - return rlp.EncodeToBytes(b.block) -} - -// NewBlockFromJSON parses a block from an JSON data dump. -func NewBlockFromJSON(data string) (*Block, error) { - b := &Block{ - block: new(types.Block), - } - if err := json.Unmarshal([]byte(data), b.block); err != nil { - return nil, err - } - return b, nil -} - -// EncodeJSON encodes a block into an JSON data dump. -func (b *Block) EncodeJSON() (string, error) { - data, err := json.Marshal(b.block) - return string(data), err -} - -// String implements the fmt.Stringer interface to print some semi-meaningful -// data dump of the block for debugging purposes. -func (b *Block) String() string { - return b.block.String() -} - -func (b *Block) GetParentHash() *Hash { return &Hash{b.block.ParentHash()} } -func (b *Block) GetUncleHash() *Hash { return &Hash{b.block.UncleHash()} } -func (b *Block) GetCoinbase() *Address { return &Address{b.block.Coinbase()} } -func (b *Block) GetRoot() *Hash { return &Hash{b.block.Root()} } -func (b *Block) GetTxHash() *Hash { return &Hash{b.block.TxHash()} } -func (b *Block) GetReceiptHash() *Hash { return &Hash{b.block.ReceiptHash()} } -func (b *Block) GetBloom() *Bloom { return &Bloom{b.block.Bloom()} } -func (b *Block) GetDifficulty() *BigInt { return &BigInt{b.block.Difficulty()} } -func (b *Block) GetNumber() int64 { return b.block.Number().Int64() } -func (b *Block) GetGasLimit() int64 { return int64(b.block.GasLimit()) } -func (b *Block) GetGasUsed() int64 { return int64(b.block.GasUsed()) } -func (b *Block) GetTime() int64 { return b.block.Time().Int64() } -func (b *Block) GetExtra() []byte { return b.block.Extra() } -func (b *Block) GetMixDigest() *Hash { return &Hash{b.block.MixDigest()} } -func (b *Block) GetNonce() int64 { return int64(b.block.Nonce()) } - -func (b *Block) GetHash() *Hash { return &Hash{b.block.Hash()} } -func (b *Block) GetHashNoNonce() *Hash { return &Hash{b.block.HashNoNonce()} } - -func (b *Block) GetHeader() *Header { return &Header{b.block.Header()} } -func (b *Block) GetUncles() *Headers { return &Headers{b.block.Uncles()} } -func (b *Block) GetTransactions() *Transactions { return &Transactions{b.block.Transactions()} } -func (b *Block) GetTransaction(hash *Hash) *Transaction { - return &Transaction{b.block.Transaction(hash.hash)} -} - -// Transaction represents a single Ethereum transaction. -type Transaction struct { - tx *types.Transaction -} - -// NewTransaction creates a new transaction with the given properties. -func NewTransaction(nonce int64, to *Address, amount *BigInt, gasLimit int64, gasPrice *BigInt, data []byte) *Transaction { - return &Transaction{types.NewTransaction(uint64(nonce), to.address, amount.bigint, uint64(gasLimit), gasPrice.bigint, common.CopyBytes(data))} -} - -// NewTransactionFromRLP parses a transaction from an RLP data dump. -func NewTransactionFromRLP(data []byte) (*Transaction, error) { - tx := &Transaction{ - tx: new(types.Transaction), - } - if err := rlp.DecodeBytes(common.CopyBytes(data), tx.tx); err != nil { - return nil, err - } - return tx, nil -} - -// EncodeRLP encodes a transaction into an RLP data dump. -func (tx *Transaction) EncodeRLP() ([]byte, error) { - return rlp.EncodeToBytes(tx.tx) -} - -// NewTransactionFromJSON parses a transaction from an JSON data dump. -func NewTransactionFromJSON(data string) (*Transaction, error) { - tx := &Transaction{ - tx: new(types.Transaction), - } - if err := json.Unmarshal([]byte(data), tx.tx); err != nil { - return nil, err - } - return tx, nil -} - -// EncodeJSON encodes a transaction into an JSON data dump. -func (tx *Transaction) EncodeJSON() (string, error) { - data, err := json.Marshal(tx.tx) - return string(data), err -} - -// String implements the fmt.Stringer interface to print some semi-meaningful -// data dump of the transaction for debugging purposes. -func (tx *Transaction) String() string { - return tx.tx.String() -} - -func (tx *Transaction) GetData() []byte { return tx.tx.Data() } -func (tx *Transaction) GetGas() int64 { return int64(tx.tx.Gas()) } -func (tx *Transaction) GetGasPrice() *BigInt { return &BigInt{tx.tx.GasPrice()} } -func (tx *Transaction) GetValue() *BigInt { return &BigInt{tx.tx.Value()} } -func (tx *Transaction) GetNonce() int64 { return int64(tx.tx.Nonce()) } - -func (tx *Transaction) GetHash() *Hash { return &Hash{tx.tx.Hash()} } -func (tx *Transaction) GetCost() *BigInt { return &BigInt{tx.tx.Cost()} } - -// Deprecated: GetSigHash cannot know which signer to use. -func (tx *Transaction) GetSigHash() *Hash { return &Hash{types.HomesteadSigner{}.Hash(tx.tx)} } - -// Deprecated: use EthereumClient.TransactionSender -func (tx *Transaction) GetFrom(chainID *BigInt) (address *Address, _ error) { - var signer types.Signer = types.HomesteadSigner{} - if chainID != nil { - signer = types.NewEIP155Signer(chainID.bigint) - } - from, err := types.Sender(signer, tx.tx) - return &Address{from}, err -} - -func (tx *Transaction) GetTo() *Address { - if to := tx.tx.To(); to != nil { - return &Address{*to} - } - return nil -} - -func (tx *Transaction) WithSignature(sig []byte, chainID *BigInt) (signedTx *Transaction, _ error) { - var signer types.Signer = types.HomesteadSigner{} - if chainID != nil { - signer = types.NewEIP155Signer(chainID.bigint) - } - rawTx, err := tx.tx.WithSignature(signer, common.CopyBytes(sig)) - return &Transaction{rawTx}, err -} - -// Transactions represents a slice of transactions. -type Transactions struct{ txs types.Transactions } - -// Size returns the number of transactions in the slice. -func (txs *Transactions) Size() int { - return len(txs.txs) -} - -// Get returns the transaction at the given index from the slice. -func (txs *Transactions) Get(index int) (tx *Transaction, _ error) { - if index < 0 || index >= len(txs.txs) { - return nil, errors.New("index out of bounds") - } - return &Transaction{txs.txs[index]}, nil -} - -// Receipt represents the results of a transaction. -type Receipt struct { - receipt *types.Receipt -} - -// NewReceiptFromRLP parses a transaction receipt from an RLP data dump. -func NewReceiptFromRLP(data []byte) (*Receipt, error) { - r := &Receipt{ - receipt: new(types.Receipt), - } - if err := rlp.DecodeBytes(common.CopyBytes(data), r.receipt); err != nil { - return nil, err - } - return r, nil -} - -// EncodeRLP encodes a transaction receipt into an RLP data dump. -func (r *Receipt) EncodeRLP() ([]byte, error) { - return rlp.EncodeToBytes(r.receipt) -} - -// NewReceiptFromJSON parses a transaction receipt from an JSON data dump. -func NewReceiptFromJSON(data string) (*Receipt, error) { - r := &Receipt{ - receipt: new(types.Receipt), - } - if err := json.Unmarshal([]byte(data), r.receipt); err != nil { - return nil, err - } - return r, nil -} - -// EncodeJSON encodes a transaction receipt into an JSON data dump. -func (r *Receipt) EncodeJSON() (string, error) { - data, err := rlp.EncodeToBytes(r.receipt) - return string(data), err -} - -// String implements the fmt.Stringer interface to print some semi-meaningful -// data dump of the transaction receipt for debugging purposes. -func (r *Receipt) String() string { - return r.receipt.String() -} - -func (r *Receipt) GetPostState() []byte { return r.receipt.PostState } -func (r *Receipt) GetCumulativeGasUsed() int64 { return int64(r.receipt.CumulativeGasUsed) } -func (r *Receipt) GetBloom() *Bloom { return &Bloom{r.receipt.Bloom} } -func (r *Receipt) GetLogs() *Logs { return &Logs{r.receipt.Logs} } -func (r *Receipt) GetTxHash() *Hash { return &Hash{r.receipt.TxHash} } -func (r *Receipt) GetContractAddress() *Address { return &Address{r.receipt.ContractAddress} } -func (r *Receipt) GetGasUsed() int64 { return int64(r.receipt.GasUsed) } diff --git a/mobile/vm.go b/mobile/vm.go deleted file mode 100644 index daca706c317c..000000000000 --- a/mobile/vm.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the core/types package. - -package geth - -import ( - "errors" - - "github.com/XinFinOrg/XDPoSChain/core/types" -) - -// Log represents a contract log event. These events are generated by the LOG -// opcode and stored/indexed by the node. -type Log struct { - log *types.Log -} - -func (l *Log) GetAddress() *Address { return &Address{l.log.Address} } -func (l *Log) GetTopics() *Hashes { return &Hashes{l.log.Topics} } -func (l *Log) GetData() []byte { return l.log.Data } -func (l *Log) GetBlockNumber() int64 { return int64(l.log.BlockNumber) } -func (l *Log) GetTxHash() *Hash { return &Hash{l.log.TxHash} } -func (l *Log) GetTxIndex() int { return int(l.log.TxIndex) } -func (l *Log) GetBlockHash() *Hash { return &Hash{l.log.BlockHash} } -func (l *Log) GetIndex() int { return int(l.log.Index) } - -// Logs represents a slice of VM logs. -type Logs struct{ logs []*types.Log } - -// Size returns the number of logs in the slice. -func (l *Logs) Size() int { - return len(l.logs) -} - -// Get returns the log at the given index from the slice. -func (l *Logs) Get(index int) (log *Log, _ error) { - if index < 0 || index >= len(l.logs) { - return nil, errors.New("index out of bounds") - } - return &Log{l.logs[index]}, nil -} From ed242b47636b150b726b148bb26e80597e9b33d3 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 19:39:18 +0800 Subject: [PATCH 236/479] all: implement EIP-1153 transient storage (#26003) --- consensus/tests/engine_v1_tests/helper.go | 2 +- consensus/tests/engine_v2_tests/helper.go | 2 +- core/blockchain_test.go | 97 +++++++++++++++++++++++ core/chain_makers.go | 50 ++++++++---- core/state/journal.go | 9 +++ core/state/statedb.go | 88 +++++++++++++++----- core/state/statedb_test.go | 44 ++++++++++ core/state/transient_storage.go | 55 +++++++++++++ core/state_processor.go | 5 +- core/state_transition.go | 7 +- core/vm/eips.go | 40 ++++++++++ core/vm/instructions.go | 3 +- core/vm/instructions_test.go | 53 +++++++++++++ core/vm/interface.go | 6 +- core/vm/opcodes.go | 6 ++ core/vm/runtime/runtime.go | 36 +++++---- eth/api_tracer.go | 8 +- miner/worker.go | 4 +- 18 files changed, 447 insertions(+), 68 deletions(-) create mode 100644 core/state/transient_storage.go diff --git a/consensus/tests/engine_v1_tests/helper.go b/consensus/tests/engine_v1_tests/helper.go index 7904ea32ca42..afc1a22cf079 100644 --- a/consensus/tests/engine_v1_tests/helper.go +++ b/consensus/tests/engine_v1_tests/helper.go @@ -381,7 +381,7 @@ func createBlockFromHeader(bc *core.BlockChain, customHeader *types.Header, txs var gasUsed = new(uint64) var receipts types.Receipts for i, tx := range txs { - statedb.Prepare(tx.Hash(), i) + statedb.SetTxContext(tx.Hash(), i) receipt, _, err, _ := core.ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{}) if err != nil { return nil, fmt.Errorf("%v when applying transaction", err) diff --git a/consensus/tests/engine_v2_tests/helper.go b/consensus/tests/engine_v2_tests/helper.go index 15e3ee77cd69..1e9b6d14b95f 100644 --- a/consensus/tests/engine_v2_tests/helper.go +++ b/consensus/tests/engine_v2_tests/helper.go @@ -698,7 +698,7 @@ func createBlockFromHeader(bc *core.BlockChain, customHeader *types.Header, txs var gasUsed = new(uint64) var receipts types.Receipts for i, tx := range txs { - statedb.Prepare(tx.Hash(), i) + statedb.SetTxContext(tx.Hash(), i) receipt, _, err, _ := core.ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{}) if err != nil { return nil, fmt.Errorf("%v when applying transaction", err) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index a3c59d27b011..b680f7f185bf 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -1512,3 +1512,100 @@ func TestEIP2718Transition(t *testing.T) { t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expected, block.GasUsed()) } } + +// TestTransientStorageReset ensures the transient storage is wiped correctly +// between transactions. +func TestTransientStorageReset(t *testing.T) { + var ( + engine = ethash.NewFaker() + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address = crypto.PubkeyToAddress(key.PublicKey) + destAddress = crypto.CreateAddress(address, 0) + funds = big.NewInt(1000000000000000) + vmConfig = vm.Config{ + ExtraEips: []int{1153}, // Enable transient storage EIP + } + ) + code := append([]byte{ + // TLoad value with location 1 + byte(vm.PUSH1), 0x1, + byte(vm.TLOAD), + + // PUSH location + byte(vm.PUSH1), 0x1, + + // SStore location:value + byte(vm.SSTORE), + }, make([]byte, 32-6)...) + initCode := []byte{ + // TSTORE 1:1 + byte(vm.PUSH1), 0x1, + byte(vm.PUSH1), 0x1, + byte(vm.TSTORE), + + // Get the runtime-code on the stack + byte(vm.PUSH32)} + initCode = append(initCode, code...) + initCode = append(initCode, []byte{ + byte(vm.PUSH1), 0x0, // offset + byte(vm.MSTORE), + byte(vm.PUSH1), 0x6, // size + byte(vm.PUSH1), 0x0, // offset + byte(vm.RETURN), // return 6 bytes of zero-code + }...) + gspec := &Genesis{ + Config: params.TestChainConfig, + Alloc: GenesisAlloc{ + address: {Balance: funds}, + }, + } + nonce := uint64(0) + signer := types.HomesteadSigner{} + _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) { + fee := big.NewInt(1) + if b.header.BaseFee != nil { + fee = b.header.BaseFee + } + b.SetCoinbase(common.Address{1}) + tx, _ := types.SignNewTx(key, signer, &types.LegacyTx{ + Nonce: nonce, + GasPrice: new(big.Int).Set(fee), + Gas: 100000, + Data: initCode, + }) + nonce++ + b.AddTxWithVMConfig(tx, vmConfig) + + tx, _ = types.SignNewTx(key, signer, &types.LegacyTx{ + Nonce: nonce, + GasPrice: new(big.Int).Set(fee), + Gas: 100000, + To: &destAddress, + }) + b.AddTxWithVMConfig(tx, vmConfig) + nonce++ + }) + + diskdb := rawdb.NewMemoryDatabase() + gspec.MustCommit(diskdb) + + // Initialize the blockchain with 1153 enabled. + chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vmConfig) + if err != nil { + t.Fatalf("failed to create tester chain: %v", err) + } + // Import the blocks + if _, err := chain.InsertChain(blocks); err != nil { + t.Fatalf("failed to insert into chain: %v", err) + } + // Check the storage + state, err := chain.StateAt(chain.CurrentHeader().Root) + if err != nil { + t.Fatalf("Failed to load state %v", err) + } + loc := common.BytesToHash([]byte{1}) + slot := state.GetState(destAddress, loc) + if slot != (common.Hash{}) { + t.Fatalf("Unexpected dirty storage slot") + } +} diff --git a/core/chain_makers.go b/core/chain_makers.go index 156a54ad0f7c..1b6633e53537 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -75,6 +75,31 @@ func (b *BlockGen) SetExtra(data []byte) { b.header.Extra = data } +// addTx adds a transaction to the generated block. If no coinbase has +// been set, the block's coinbase is set to the zero address. +// +// There are a few options can be passed as well in order to run some +// customized rules. +// - bc: enables the ability to query historical block hashes for BLOCKHASH +// - vmConfig: extends the flexibility for customizing evm rules, e.g. enable extra EIPs +func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transaction) { + if b.gasPool == nil { + b.SetCoinbase(common.Address{}) + } + feeCapacity := state.GetTRC21FeeCapacityFromState(b.statedb) + b.statedb.SetTxContext(tx.Hash(), len(b.txs)) + receipt, gas, err, tokenFeeUsed := ApplyTransaction(b.config, feeCapacity, bc, &b.header.Coinbase, b.gasPool, b.statedb, nil, b.header, tx, &b.header.GasUsed, vmConfig) + if err != nil { + panic(err) + } + b.txs = append(b.txs, tx) + b.receipts = append(b.receipts, receipt) + if tokenFeeUsed { + fee := common.GetGasFee(b.header.Number.Uint64(), gas) + state.UpdateTRC21Fee(b.statedb, map[common.Address]*big.Int{*tx.To(): new(big.Int).Sub(feeCapacity[*tx.To()], new(big.Int).SetUint64(gas))}, fee) + } +} + // AddTx adds a transaction to the generated block. If no coinbase has // been set, the block's coinbase is set to the zero address. // @@ -84,7 +109,7 @@ func (b *BlockGen) SetExtra(data []byte) { // added. Notably, contract code relying on the BLOCKHASH instruction // will panic during execution. func (b *BlockGen) AddTx(tx *types.Transaction) { - b.AddTxWithChain(nil, tx) + b.addTx(nil, vm.Config{}, tx) } // AddTxWithChain adds a transaction to the generated block. If no coinbase has @@ -96,21 +121,14 @@ func (b *BlockGen) AddTx(tx *types.Transaction) { // added. If contract code relies on the BLOCKHASH instruction, // the block in chain will be returned. func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) { - if b.gasPool == nil { - b.SetCoinbase(common.Address{}) - } - feeCapacity := state.GetTRC21FeeCapacityFromState(b.statedb) - b.statedb.Prepare(tx.Hash(), len(b.txs)) - receipt, gas, err, tokenFeeUsed := ApplyTransaction(b.config, feeCapacity, bc, &b.header.Coinbase, b.gasPool, b.statedb, nil, b.header, tx, &b.header.GasUsed, vm.Config{}) - if err != nil { - panic(err) - } - b.txs = append(b.txs, tx) - b.receipts = append(b.receipts, receipt) - if tokenFeeUsed { - fee := common.GetGasFee(b.header.Number.Uint64(), gas) - state.UpdateTRC21Fee(b.statedb, map[common.Address]*big.Int{*tx.To(): new(big.Int).Sub(feeCapacity[*tx.To()], new(big.Int).SetUint64(gas))}, fee) - } + b.addTx(bc, vm.Config{}, tx) +} + +// AddTxWithVMConfig adds a transaction to the generated block. If no coinbase has +// been set, the block's coinbase is set to the zero address. +// The evm interpreter can be customized with the provided vm config. +func (b *BlockGen) AddTxWithVMConfig(tx *types.Transaction, config vm.Config) { + b.addTx(nil, config, tx) } // AddUncheckedTx forcefully adds a transaction to the block without any diff --git a/core/state/journal.go b/core/state/journal.go index b9348f7bf26b..d8f748fa64d7 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -88,6 +88,11 @@ type ( address *common.Address slot *common.Hash } + + transientStorageChange struct { + account *common.Address + key, prevalue common.Hash + } ) func (ch createObjectChange) undo(s *StateDB) { @@ -134,6 +139,10 @@ func (ch storageChange) undo(s *StateDB) { s.getStateObject(*ch.account).setState(ch.key, ch.prevalue) } +func (ch transientStorageChange) undo(s *StateDB) { + s.setTransientState(*ch.account, ch.key, ch.prevalue) +} + func (ch refundChange) undo(s *StateDB) { s.refund = ch.prev } diff --git a/core/state/statedb.go b/core/state/statedb.go index fc8de74793fa..1cee95087fb8 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -27,6 +27,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/log" + "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rlp" "github.com/XinFinOrg/XDPoSChain/trie" ) @@ -69,6 +70,9 @@ type StateDB struct { // Per-transaction access list accessList *accessList + // Transient storage + transientStorage transientStorage + // Journal of state modifications. This is the backbone of // Snapshot and RevertToSnapshot. journal journal @@ -117,6 +121,7 @@ func New(root common.Hash, db Database) (*StateDB, error) { logs: make(map[common.Hash][]*types.Log), preimages: make(map[common.Hash][]byte), accessList: newAccessList(), + transientStorage: newTransientStorage(), }, nil } @@ -406,6 +411,35 @@ func (s *StateDB) Suicide(addr common.Address) bool { return true } +// SetTransientState sets transient storage for a given account. It +// adds the change to the journal so that it can be rolled back +// to its previous value if there is a revert. +func (s *StateDB) SetTransientState(addr common.Address, key, value common.Hash) { + prev := s.GetTransientState(addr, key) + if prev == value { + return + } + + s.journal = append(s.journal, transientStorageChange{ + account: &addr, + key: key, + prevalue: prev, + }) + + s.setTransientState(addr, key, value) +} + +// setTransientState is a lower level setter for transient storage. It +// is called during a revert to prevent modifications to the journal. +func (s *StateDB) setTransientState(addr common.Address, key, value common.Hash) { + s.transientStorage.Set(addr, key, value) +} + +// GetTransientState gets transient storage for a given account. +func (s *StateDB) GetTransientState(addr common.Address, key common.Hash) common.Hash { + return s.transientStorage.Get(addr, key) +} + // // Setting, updating & deleting state object methods. // @@ -577,6 +611,9 @@ func (s *StateDB) Copy() *StateDB { // However, it doesn't cost us much to copy an empty list, so we do it anyway // to not blow up if we ever decide copy it in the middle of a transaction state.accessList = s.accessList.Copy() + + state.transientStorage = s.transientStorage.Copy() + return state } @@ -638,9 +675,10 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { return s.trie.Hash() } -// Prepare sets the current transaction hash and index and block hash which is -// used when the EVM emits new state logs. -func (s *StateDB) Prepare(thash common.Hash, ti int) { +// SetTxContext sets the current transaction hash and index which are +// used when the EVM emits new state logs. It should be invoked before +// transaction execution. +func (s *StateDB) SetTxContext(thash common.Hash, ti int) { s.thash = thash s.txIndex = ti } @@ -717,33 +755,39 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) return root, err } -// PrepareAccessList handles the preparatory steps for executing a state transition with -// regards to both EIP-2929 and EIP-2930: +// Prepare handles the preparatory steps for executing a state transition with. +// This method must be invoked before state transition. // +// Berlin fork: // - Add sender to access list (2929) // - Add destination to access list (2929) // - Add precompiles to access list (2929) // - Add the contents of the optional tx access list (2930) // -// This method should only be called if Yolov3/Berlin/2929+2930 is applicable at the current number. -func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) { - // Clear out any leftover from previous executions - s.accessList = newAccessList() - - s.AddAddressToAccessList(sender) - if dst != nil { - s.AddAddressToAccessList(*dst) - // If it's a create-tx, the destination will be added inside evm.create - } - for _, addr := range precompiles { - s.AddAddressToAccessList(addr) - } - for _, el := range list { - s.AddAddressToAccessList(el.Address) - for _, key := range el.StorageKeys { - s.AddSlotToAccessList(el.Address, key) +// Potential EIPs: +// - Reset transient storage(1153) +func (s *StateDB) Prepare(rules params.Rules, sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) { + if rules.IsEIP1559 { + // Clear out any leftover from previous executions + s.accessList = newAccessList() + + s.AddAddressToAccessList(sender) + if dst != nil { + s.AddAddressToAccessList(*dst) + // If it's a create-tx, the destination will be added inside evm.create + } + for _, addr := range precompiles { + s.AddAddressToAccessList(addr) + } + for _, el := range list { + s.AddAddressToAccessList(el.Address) + for _, key := range el.StorageKeys { + s.AddSlotToAccessList(el.Address, key) + } } } + // Reset transient storage at the beginning of transaction execution + s.transientStorage = newTransientStorage() } // AddAddressToAccessList adds the given address to the access list diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 35e7affafda8..5166224b352f 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -297,6 +297,16 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction { }, args: make([]int64, 1), }, + { + name: "SetTransientState", + fn: func(a testAction, s *StateDB) { + var key, val common.Hash + binary.BigEndian.PutUint16(key[:], uint16(a.args[0])) + binary.BigEndian.PutUint16(val[:], uint16(a.args[1])) + s.SetTransientState(addr, key, val) + }, + args: make([]int64, 2), + }, } action := actions[r.Intn(len(actions))] var nameargs []string @@ -615,3 +625,37 @@ func TestStateDBAccessList(t *testing.T) { t.Fatalf("expected empty, got %d", got) } } + +func TestStateDBTransientStorage(t *testing.T) { + memDb := rawdb.NewMemoryDatabase() + db := NewDatabase(memDb) + state, _ := New(common.Hash{}, db) + + key := common.Hash{0x01} + value := common.Hash{0x02} + addr := common.Address{} + + state.SetTransientState(addr, key, value) + if exp, got := 1, state.journal.length(); exp != got { + t.Fatalf("journal length mismatch: have %d, want %d", got, exp) + } + // the retrieved value should equal what was set + if got := state.GetTransientState(addr, key); got != value { + t.Fatalf("transient storage mismatch: have %x, want %x", got, value) + } + + // revert the transient state being set and then check that the + // value is now the empty hash + state.journal[0].undo(state) + if got, exp := state.GetTransientState(addr, key), (common.Hash{}); exp != got { + t.Fatalf("transient storage mismatch: have %x, want %x", got, exp) + } + + // set transient state and then copy the statedb and ensure that + // the transient state is copied + state.SetTransientState(addr, key, value) + cpy := state.Copy() + if got := cpy.GetTransientState(addr, key); got != value { + t.Fatalf("transient storage mismatch: have %x, want %x", got, value) + } +} diff --git a/core/state/transient_storage.go b/core/state/transient_storage.go new file mode 100644 index 000000000000..1cc9548e89b0 --- /dev/null +++ b/core/state/transient_storage.go @@ -0,0 +1,55 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "github.com/XinFinOrg/XDPoSChain/common" +) + +// transientStorage is a representation of EIP-1153 "Transient Storage". +type transientStorage map[common.Address]Storage + +// newTransientStorage creates a new instance of a transientStorage. +func newTransientStorage() transientStorage { + return make(transientStorage) +} + +// Set sets the transient-storage `value` for `key` at the given `addr`. +func (t transientStorage) Set(addr common.Address, key, value common.Hash) { + if _, ok := t[addr]; !ok { + t[addr] = make(Storage) + } + t[addr][key] = value +} + +// Get gets the transient storage for `key` at the given `addr`. +func (t transientStorage) Get(addr common.Address, key common.Hash) common.Hash { + val, ok := t[addr] + if !ok { + return common.Hash{} + } + return val[key] +} + +// Copy does a deep copy of the transientStorage +func (t transientStorage) Copy() transientStorage { + storage := make(transientStorage) + for key, value := range t { + storage[key] = value.Copy() + } + return storage +} diff --git a/core/state_processor.go b/core/state_processor.go index 3779b13c8079..8234e36c8f7d 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -117,7 +117,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra return nil, nil, 0, err } } - statedb.Prepare(tx.Hash(), i) + statedb.SetTxContext(tx.Hash(), i) receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, gp, statedb, coinbaseOwner, blockNumber, header.BaseFee, blockHash, tx, usedGas, vmenv) if err != nil { return nil, nil, 0, err @@ -197,7 +197,7 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated return nil, nil, 0, err } } - statedb.Prepare(tx.Hash(), i) + statedb.SetTxContext(tx.Hash(), i) receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, gp, statedb, coinbaseOwner, blockNumber, header.BaseFee, blockHash, tx, usedGas, vmenv) if err != nil { return nil, nil, 0, err @@ -466,6 +466,7 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* blockContext := NewEVMBlockContext(header, bc, author) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, XDCxState, config, cfg) coinbaseOwner := getCoinbaseOwner(bc, statedb, header, author) + // return applyTransaction(config, tokensFee, gp, statedb, coinbaseOwner, header.Number, header.BaseFee, header.Hash(), tx, usedGas, vmenv) return applyTransaction(config, tokensFee, gp, statedb, coinbaseOwner, header.Number, header.BaseFee, header.Hash(), tx, usedGas, vmenv) } diff --git a/core/state_transition.go b/core/state_transition.go index 506d12a95767..257950622f28 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -329,9 +329,10 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG return nil, 0, false, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(st.data), params.MaxInitCodeSize), nil } - if rules.IsEIP1559 { - st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList()) - } + // Execute the preparatory steps for state transition which includes: + // - prepare accessList(post-berlin) + // - reset transient storage(eip 1153) + st.state.Prepare(rules, msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList()) var ( evm = st.evm diff --git a/core/vm/eips.go b/core/vm/eips.go index 95e4379b0bca..532611dc940a 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -33,6 +33,7 @@ var activators = map[int]func(*JumpTable){ 2200: enable2200, 1884: enable1884, 1344: enable1344, + 1153: enable1153, } // EnableEIP enables the given EIP on the config. @@ -157,6 +158,45 @@ func enable3198(jt *JumpTable) { } } +// enable1153 applies EIP-1153 "Transient Storage" +// - Adds TLOAD that reads from transient storage +// - Adds TSTORE that writes to transient storage +func enable1153(jt *JumpTable) { + jt[TLOAD] = &operation{ + execute: opTload, + constantGas: params.WarmStorageReadCostEIP2929, + minStack: minStack(1, 1), + maxStack: maxStack(1, 1), + } + + jt[TSTORE] = &operation{ + execute: opTstore, + constantGas: params.WarmStorageReadCostEIP2929, + minStack: minStack(2, 0), + maxStack: maxStack(2, 0), + } +} + +// opTload implements TLOAD opcode +func opTload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + loc := scope.Stack.peek() + hash := common.Hash(loc.Bytes32()) + val := interpreter.evm.StateDB.GetTransientState(scope.Contract.Address(), hash) + loc.SetBytes(val.Bytes()) + return nil, nil +} + +// opTstore implements TSTORE opcode +func opTstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + if interpreter.readOnly { + return nil, ErrWriteProtection + } + loc := scope.Stack.pop() + val := scope.Stack.pop() + interpreter.evm.StateDB.SetTransientState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32()) + return nil, nil +} + // opBaseFee implements BASEFEE opcode func opBaseFee(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) { baseFee, _ := uint256.FromBig(common.BaseFee) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index d26e5dfad6c4..8914bf94badb 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -539,8 +539,7 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b } loc := scope.Stack.pop() val := scope.Stack.pop() - interpreter.evm.StateDB.SetState(scope.Contract.Address(), - loc.Bytes32(), val.Bytes32()) + interpreter.evm.StateDB.SetState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32()) return nil, nil } diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 73c1139152c8..e0c934cdef84 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -26,6 +26,8 @@ import ( "testing" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" + "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/params" "github.com/holiman/uint256" @@ -46,6 +48,14 @@ var alphabetSoup = "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffff var commonParams []*twoOperandParams var twoOpMethods map[string]executionFunc +type contractRef struct { + addr common.Address +} + +func (c contractRef) Address() common.Address { + return c.addr +} + func init() { // Params is a list of common edgecases that should be used for some common tests params := []string{ @@ -574,6 +584,49 @@ func BenchmarkOpMstore(bench *testing.B) { } } +func TestOpTstore(t *testing.T) { + var ( + statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) + env = NewEVM(BlockContext{}, TxContext{}, statedb, nil, params.TestChainConfig, Config{}) + stack = newstack() + mem = NewMemory() + evmInterpreter = NewEVMInterpreter(env, env.Config) + caller = common.Address{} + to = common.Address{1} + contractRef = contractRef{caller} + contract = NewContract(contractRef, AccountRef(to), new(big.Int), 0) + scopeContext = ScopeContext{mem, stack, contract} + value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700") + ) + + // Add a stateObject for the caller and the contract being called + statedb.CreateAccount(caller) + statedb.CreateAccount(to) + + env.interpreter = evmInterpreter + pc := uint64(0) + // push the value to the stack + stack.push(new(uint256.Int).SetBytes(value)) + // push the location to the stack + stack.push(new(uint256.Int)) + opTstore(&pc, evmInterpreter, &scopeContext) + // there should be no elements on the stack after TSTORE + if stack.len() != 0 { + t.Fatal("stack wrong size") + } + // push the location to the stack + stack.push(new(uint256.Int)) + opTload(&pc, evmInterpreter, &scopeContext) + // there should be one element on the stack after TLOAD + if stack.len() != 1 { + t.Fatal("stack wrong size") + } + val := stack.peek() + if !bytes.Equal(val.Bytes(), value) { + t.Fatal("incorrect element read from transient storage") + } +} + func BenchmarkOpKeccak256(bench *testing.B) { var ( env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) diff --git a/core/vm/interface.go b/core/vm/interface.go index 5c695ffed703..cd0333e57af2 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -21,6 +21,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/params" ) // StateDB is an EVM database for full state querying. @@ -47,6 +48,9 @@ type StateDB interface { GetState(common.Address, common.Hash) common.Hash SetState(common.Address, common.Hash, common.Hash) + GetTransientState(addr common.Address, key common.Hash) common.Hash + SetTransientState(addr common.Address, key, value common.Hash) + Suicide(common.Address) bool HasSuicided(common.Address) bool @@ -57,7 +61,6 @@ type StateDB interface { // is defined according to EIP161 (balance = nonce = code = 0). Empty(common.Address) bool - PrepareAccessList(sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) AddressInAccessList(addr common.Address) bool SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) // AddAddressToAccessList adds the given address to the access list. This operation is safe to perform @@ -66,6 +69,7 @@ type StateDB interface { // AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform // even if the feature/fork is not active yet AddSlotToAccessList(addr common.Address, slot common.Hash) + Prepare(rules params.Rules, sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) RevertToSnapshot(int) Snapshot() int diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 26523e53ce42..c050f64e79e4 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -116,6 +116,8 @@ const ( MSIZE OpCode = 0x59 GAS OpCode = 0x5a JUMPDEST OpCode = 0x5b + TLOAD OpCode = 0x5c + TSTORE OpCode = 0x5d PUSH0 OpCode = 0x5f ) @@ -297,6 +299,8 @@ var opCodeToString = [256]string{ MSIZE: "MSIZE", GAS: "GAS", JUMPDEST: "JUMPDEST", + TLOAD: "TLOAD", + TSTORE: "TSTORE", PUSH0: "PUSH0", // 0x60 range - push. @@ -460,6 +464,8 @@ var stringToOp = map[string]OpCode{ "MSIZE": MSIZE, "GAS": GAS, "JUMPDEST": JUMPDEST, + "TLOAD": TLOAD, + "TSTORE": TSTORE, "PUSH0": PUSH0, "PUSH1": PUSH1, "PUSH2": PUSH2, diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 14964385c2de..600ae1199a4c 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -120,10 +120,13 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { address = common.BytesToAddress([]byte("contract")) vmenv = NewEnv(cfg) sender = vm.AccountRef(cfg.Origin) + rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber) ) - if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsEIP1559 { - cfg.State.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil) - } + // Execute the preparatory steps for state transition which includes: + // - prepare accessList(post-berlin) + // - reset transient storage(eip 1153) + cfg.State.Prepare(rules, cfg.Origin, &address, vm.ActivePrecompiles(rules), nil) + cfg.State.CreateAccount(address) // set the receiver's (the executing contract) code for execution. cfg.State.SetCode(address, code) @@ -135,7 +138,6 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { cfg.GasLimit, cfg.Value, ) - return ret, cfg.State, err } @@ -153,10 +155,13 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { var ( vmenv = NewEnv(cfg) sender = vm.AccountRef(cfg.Origin) + rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber) ) - if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsEIP1559 { - cfg.State.PrepareAccessList(cfg.Origin, nil, vm.ActivePrecompiles(rules), nil) - } + // Execute the preparatory steps for state transition which includes: + // - prepare accessList(post-berlin) + // - reset transient storage(eip 1153) + cfg.State.Prepare(rules, cfg.Origin, nil, vm.ActivePrecompiles(rules), nil) + // Call the code with the given configuration. code, address, leftOverGas, err := vmenv.Create( sender, @@ -175,13 +180,16 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, error) { setDefaults(cfg) - vmenv := NewEnv(cfg) - - sender := cfg.State.GetOrNewStateObject(cfg.Origin) - statedb := cfg.State - if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsEIP1559 { - statedb.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil) - } + var ( + vmenv = NewEnv(cfg) + sender = cfg.State.GetOrNewStateObject(cfg.Origin) + statedb = cfg.State + rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber) + ) + // Execute the preparatory steps for state transition which includes: + // - prepare accessList(post-berlin) + // - reset transient storage(eip 1153) + statedb.Prepare(rules, cfg.Origin, &address, vm.ActivePrecompiles(rules), nil) // Call the code with the given configuration. ret, leftOverGas, err := vmenv.Call( diff --git a/eth/api_tracer.go b/eth/api_tracer.go index 4be1c89fb60a..88d2cf4881b4 100644 --- a/eth/api_tracer.go +++ b/eth/api_tracer.go @@ -522,7 +522,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, header := block.Header() msg, _ := tx.AsMessage(signer, balance, header.Number, header.BaseFee) txContext := core.NewEVMTxContext(msg) - statedb.Prepare(tx.Hash(), i) + statedb.SetTxContext(tx.Hash(), i) vmenv := vm.NewEVM(blockCtx, txContext, statedb, XDCxState, api.config, vm.Config{}) owner := common.Address{} @@ -746,8 +746,8 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, t // Run the transaction with tracing enabled. vmenv := vm.NewEVM(vmctx, txContext, statedb, nil, api.config, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true}) - // Call Prepare to clear out the statedb access list - statedb.Prepare(txctx.TxHash, txctx.TxIndex) + // Call SetTxContext to clear out the statedb access list + statedb.SetTxContext(txctx.TxHash, txctx.TxIndex) owner := common.Address{} ret, gas, failed, err, _ := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), owner) @@ -799,7 +799,7 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree usedGas := new(uint64) // Iterate over and process the individual transactions for idx, tx := range block.Transactions() { - statedb.Prepare(tx.Hash(), idx) + statedb.SetTxContext(tx.Hash(), idx) if idx == txIndex { var balanceFee *big.Int if tx.To() != nil { diff --git a/miner/worker.go b/miner/worker.go index 5395733990ac..9aa9d4589eea 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -911,7 +911,7 @@ func (w *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Addr } } // Start executing the transaction - w.state.Prepare(hash, w.tcount) + w.state.SetTxContext(hash, w.tcount) nonce := w.state.GetNonce(from) if nonce != tx.Nonce() && !tx.IsSkipNonceTransaction() { @@ -1012,7 +1012,7 @@ func (w *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Addr continue } // Start executing the transaction - w.state.Prepare(hash, w.tcount) + w.state.SetTxContext(hash, w.tcount) nonce := w.state.GetNonce(from) if nonce > tx.Nonce() { // New head notification data race between the transaction pool and miner, shift From 4cac1865bafd5347ed1416dd78e344ce52312684 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 15 Nov 2024 10:58:29 +0800 Subject: [PATCH 237/479] all: remove noop vm config flags (#23111) --- core/vm/evm.go | 46 +++++++----------------------------- core/vm/instructions_test.go | 4 ++-- core/vm/interpreter.go | 31 ------------------------ tests/state_test.go | 10 +++++--- 4 files changed, 17 insertions(+), 74 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index be90bece714c..609ad525c050 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -17,7 +17,6 @@ package vm import ( - "errors" "math/big" "sync/atomic" "time" @@ -61,29 +60,6 @@ func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { return p, ok } -// run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter. -func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) { - if evm.ChainConfig().IsTIPXDCXCancellationFee(evm.Context.BlockNumber) { - for _, interpreter := range evm.interpreters { - if interpreter.CanRun(contract.Code) { - if evm.interpreter != interpreter { - // Ensure that the interpreter pointer is set back - // to its current value upon return. - defer func(i Interpreter) { - evm.interpreter = i - }(evm.interpreter) - evm.interpreter = interpreter - } - return interpreter.Run(contract, input, readOnly) - } - } - } else { - return evm.interpreter.Run(contract, input, false) - } - - return nil, errors.New("no compatible interpreter") -} - // BlockContext provides the EVM with auxiliary information. Once provided // it shouldn't be modified. type BlockContext struct { @@ -143,8 +119,7 @@ type EVM struct { Config Config // global (to this context) ethereum virtual machine // used throughout the execution of the tx. - interpreters []Interpreter - interpreter Interpreter + interpreter *EVMInterpreter // abort is used to abort the EVM calling operations // NOTE: must be set atomically abort int32 @@ -165,14 +140,9 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, tradingStat Config: config, chainConfig: chainConfig, chainRules: chainConfig.Rules(blockCtx.BlockNumber), - interpreters: make([]Interpreter, 0, 1), } - // vmConfig.EVMInterpreter will be used by EVM-C, it won't be checked here - // as we always want to have the built-in EVM as the failover option. - evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, config)) - evm.interpreter = evm.interpreters[0] - + evm.interpreter = NewEVMInterpreter(evm, config) return evm } @@ -195,7 +165,7 @@ func (evm *EVM) Cancelled() bool { } // Interpreter returns the current interpreter -func (evm *EVM) Interpreter() Interpreter { +func (evm *EVM) Interpreter() *EVMInterpreter { return evm.interpreter } @@ -264,7 +234,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // The depth-check is already done, and precompiles handled above contract := NewContract(caller, AccountRef(addrCopy), value, gas) contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code) - ret, err = run(evm, contract, input, false) + ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas } } @@ -321,7 +291,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // The contract is a scoped environment for this execution context only. contract := NewContract(caller, AccountRef(caller.Address()), value, gas) contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) - ret, err = run(evm, contract, input, false) + ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas } if err != nil { @@ -361,7 +331,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by // Initialise a new contract and make initialise the delegate values contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate() contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) - ret, err = run(evm, contract, input, false) + ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas } if err != nil { @@ -418,7 +388,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in Homestead this also counts for code storage gas errors. readOnly := evm.ChainConfig().IsTIPXDCXCancellationFee(evm.Context.BlockNumber) - ret, err = run(evm, contract, input, readOnly) + ret, err = evm.interpreter.Run(contract, input, readOnly) gas = contract.Gas } if err != nil { @@ -489,7 +459,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, } start := time.Now() - ret, err := run(evm, contract, nil, false) + ret, err := evm.interpreter.Run(contract, nil, false) // Check whether the max code size has been exceeded, assign err if the case. if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize { diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index e0c934cdef84..c98a4488d5b2 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -106,7 +106,7 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) - evmInterpreter = env.interpreter.(*EVMInterpreter) + evmInterpreter = env.interpreter ) for i, test := range tests { @@ -249,7 +249,7 @@ func TestWriteExpectedValues(t *testing.T) { env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) - interpreter = env.interpreter.(*EVMInterpreter) + interpreter = env.interpreter ) result := make([]TwoOperandTestcase, len(args)) for i, param := range args { diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 0df6c427e41c..a7aa8dde094c 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -32,34 +32,9 @@ type Config struct { JumpTable *JumpTable // EVM instruction table, automatically populated if unset - EWASMInterpreter string // External EWASM interpreter options - EVMInterpreter string // External EVM interpreter options - ExtraEips []int // Additional EIPS that are to be enabled } -// Interpreter is used to run Ethereum based contracts and will utilise the -// passed environment to query external sources for state information. -// The Interpreter will run the byte code VM based on the passed -// configuration. -type Interpreter interface { - // Run loops and evaluates the contract's code with the given input data and returns - // the return byte-slice and an error if one occurred. - Run(contract *Contract, input []byte, static bool) ([]byte, error) - // CanRun tells if the contract, passed as an argument, can be - // run by the current interpreter. This is meant so that the - // caller can do something like: - // - // ```golang - // for _, interpreter := range interpreters { - // if interpreter.CanRun(contract.code) { - // interpreter.Run(contract.code, input) - // } - // } - // ``` - CanRun([]byte) bool -} - // ScopeContext contains the things that are per-call, such as stack and memory, // but not transients like pc and gas type ScopeContext struct { @@ -274,9 +249,3 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( return res, err } - -// CanRun tells if the contract, passed as an argument, can be -// run by the current interpreter. -func (in *EVMInterpreter) CanRun(code []byte) bool { - return true -} diff --git a/tests/state_test.go b/tests/state_test.go index 1b77c6df5265..8bf60af26407 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -74,17 +74,21 @@ func TestState(t *testing.T) { const traceErrorLimit = 400000 func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) { - err := test(vm.Config{}) + // Use config from command line arguments. + config := vm.Config{} + err := test(config) if err == nil { return } - t.Error(err) + + // Test failed, re-run with tracing enabled. if gasLimit > traceErrorLimit { t.Log("gas limit too high for EVM trace") return } tracer := vm.NewStructLogger(nil) - err2 := test(vm.Config{Debug: true, Tracer: tracer}) + config.Debug, config.Tracer = true, tracer + err2 := test(config) if !reflect.DeepEqual(err, err2) { t.Errorf("different error for second run: %v", err2) } From 370c6b62e7733c8aa1aee9a38dac56bc856ab1f2 Mon Sep 17 00:00:00 2001 From: benjamin202410 Date: Sun, 17 Nov 2024 21:23:19 -0800 Subject: [PATCH 238/479] update devnet 1559 block number (#736) * update devnet 1559 block number * update devnet 1559 block number --------- Co-authored-by: liam.lai --- common/constants/constants.go.devnet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/constants/constants.go.devnet b/common/constants/constants.go.devnet index a18316600246..81e8c0487cd2 100644 --- a/common/constants/constants.go.devnet +++ b/common/constants/constants.go.devnet @@ -51,7 +51,7 @@ var BerlinBlock = big.NewInt(16832700) var LondonBlock = big.NewInt(16832700) var MergeBlock = big.NewInt(16832700) var ShanghaiBlock = big.NewInt(16832700) -var Eip1559Block = big.NewInt(23580000) +var Eip1559Block = big.NewInt(23035500) var TIPXDCXTestnet = big.NewInt(0) var IsTestnet bool = false From 4855f19261af0f7f33a0916495bbc35420ca99a3 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 19 Nov 2024 14:19:57 +0800 Subject: [PATCH 239/479] core: fix preCheck for RandomizeSMC after EIP-1559 --- core/state_transition.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/state_transition.go b/core/state_transition.go index 257950622f28..90401eb817a5 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -267,7 +267,7 @@ func (st *StateTransition) preCheck() error { } // This will panic if baseFee is nil, but basefee presence is verified // as part of header validation. - if st.gasFeeCap.Cmp(st.evm.Context.BaseFee) < 0 { + if (msg.To() == nil || *msg.To() != common.RandomizeSMCBinary) && st.gasFeeCap.Cmp(st.evm.Context.BaseFee) < 0 { return fmt.Errorf("%w: address %v, maxFeePerGas: %s baseFee: %s", ErrFeeCapTooLow, msg.From().Hex(), st.gasFeeCap, st.evm.Context.BaseFee) } From bd916d100a40d6fe47adde2c0907fd3578145b58 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 21 Nov 2024 17:13:50 +0800 Subject: [PATCH 240/479] cmd/evm, internal/debug: use global functions when cli v1 --- cmd/evm/runner.go | 6 +++--- cmd/evm/staterunner.go | 4 ++-- internal/debug/flags.go | 24 ++++++++++++------------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 643f53a02c4c..cc71f4214ac8 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -71,9 +71,9 @@ func runCmd(ctx *cli.Context) error { logconfig := &vm.LogConfig{ EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name), DisableStack: ctx.GlobalBool(DisableStackFlag.Name), - DisableStorage: ctx.Bool(DisableStorageFlag.Name), - EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name), - Debug: ctx.Bool(DebugFlag.Name), + DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name), + EnableReturnData: !ctx.GlobalBool(DisableReturnDataFlag.Name), + Debug: ctx.GlobalBool(DebugFlag.Name), } var ( diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go index 891d6b1e6664..197a1c61c382 100644 --- a/cmd/evm/staterunner.go +++ b/cmd/evm/staterunner.go @@ -52,8 +52,8 @@ func stateTestCmd(ctx *cli.Context) error { config := &vm.LogConfig{ EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name), DisableStack: ctx.GlobalBool(DisableStackFlag.Name), - DisableStorage: ctx.Bool(DisableStorageFlag.Name), - EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name), + DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name), + EnableReturnData: !ctx.GlobalBool(DisableReturnDataFlag.Name), } var ( diff --git a/internal/debug/flags.go b/internal/debug/flags.go index 223e9c6fa551..6534e2be1c9c 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -178,11 +178,11 @@ func Setup(ctx *cli.Context) error { handler slog.Handler terminalOutput = io.Writer(os.Stderr) output io.Writer - logFmtFlag = ctx.String(logFormatFlag.Name) + logFmtFlag = ctx.GlobalString(logFormatFlag.Name) ) var ( - logFile = ctx.String(logFileFlag.Name) - rotation = ctx.Bool(logRotateFlag.Name) + logFile = ctx.GlobalString(logFileFlag.Name) + rotation = ctx.GlobalBool(logRotateFlag.Name) ) if len(logFile) > 0 { if err := validateLogLocation(filepath.Dir(logFile)); err != nil { @@ -205,10 +205,10 @@ func Setup(ctx *cli.Context) error { } logOutputFile = &lumberjack.Logger{ Filename: logFile, - MaxSize: ctx.Int(logMaxSizeMBsFlag.Name), - MaxBackups: ctx.Int(logMaxBackupsFlag.Name), - MaxAge: ctx.Int(logMaxAgeFlag.Name), - Compress: ctx.Bool(logCompressFlag.Name), + MaxSize: ctx.GlobalInt(logMaxSizeMBsFlag.Name), + MaxBackups: ctx.GlobalInt(logMaxBackupsFlag.Name), + MaxAge: ctx.GlobalInt(logMaxAgeFlag.Name), + Compress: ctx.GlobalBool(logCompressFlag.Name), } output = io.MultiWriter(terminalOutput, logOutputFile) } else if logFile != "" { @@ -223,7 +223,7 @@ func Setup(ctx *cli.Context) error { } switch { - case ctx.Bool(logjsonFlag.Name): + case ctx.GlobalBool(logjsonFlag.Name): // Retain backwards compatibility with `--log-json` flag if `--log-format` not set defer log.Warn("The flag '--log-json' is deprecated, please use '--log-format=json' instead") handler = log.JSONHandlerWithLevel(output, log.LevelInfo) @@ -244,18 +244,18 @@ func Setup(ctx *cli.Context) error { handler = log.NewTerminalHandler(output, useColor) default: // Unknown log format specified - return fmt.Errorf("unknown log format: %v", ctx.String(logFormatFlag.Name)) + return fmt.Errorf("unknown log format: %v", ctx.GlobalString(logFormatFlag.Name)) } glogger = log.NewGlogHandler(handler) // logging - verbosity := log.FromLegacyLevel(ctx.Int(verbosityFlag.Name)) + verbosity := log.FromLegacyLevel(ctx.GlobalInt(verbosityFlag.Name)) glogger.Verbosity(verbosity) - vmodule := ctx.String(logVmoduleFlag.Name) + vmodule := ctx.GlobalString(logVmoduleFlag.Name) if vmodule == "" { // Retain backwards compatibility with `--vmodule` flag if `--log-vmodule` not set - vmodule = ctx.String(vmoduleFlag.Name) + vmodule = ctx.GlobalString(vmoduleFlag.Name) if vmodule != "" { defer log.Warn("The flag '--vmodule' is deprecated, please use '--log-vmodule' instead") } From 809242223a3849f58fa557bae1b468248033aa86 Mon Sep 17 00:00:00 2001 From: "liam.lai" Date: Fri, 22 Nov 2024 00:04:39 -0800 Subject: [PATCH 241/479] bug fix use right block to count vote theshold --- consensus/XDPoS/engines/engine_v2/vote.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/consensus/XDPoS/engines/engine_v2/vote.go b/consensus/XDPoS/engines/engine_v2/vote.go index 138e69a7691b..592a5741cb91 100644 --- a/consensus/XDPoS/engines/engine_v2/vote.go +++ b/consensus/XDPoS/engines/engine_v2/vote.go @@ -76,7 +76,7 @@ func (x *XDPoS_v2) voteHandler(chain consensus.ChainReader, voteMsg *types.Vote) go x.ForensicsProcessor.DetectEquivocationInVotePool(voteMsg, x.votePool) go x.ForensicsProcessor.ProcessVoteEquivocation(chain, x, voteMsg) - epochInfo, err := x.getEpochSwitchInfo(chain, chain.CurrentHeader(), chain.CurrentHeader().Hash()) + epochInfo, err := x.getEpochSwitchInfo(chain, nil, voteMsg.ProposedBlockInfo.Hash) if err != nil { log.Error("[voteHandler] Error when getting epoch switch Info", "error", err) return errors.New("fail on voteHandler due to failure in getting epoch switch info") @@ -175,7 +175,7 @@ func (x *XDPoS_v2) onVotePoolThresholdReached(chain consensus.ChainReader, poole } } - epochInfo, err := x.getEpochSwitchInfo(chain, chain.CurrentHeader(), chain.CurrentHeader().Hash()) + epochInfo, err := x.getEpochSwitchInfo(chain, nil, currentVoteMsg.(*types.Vote).ProposedBlockInfo.Hash) if err != nil { log.Error("[voteHandler] Error when getting epoch switch Info", "error", err) return errors.New("fail on voteHandler due to failure in getting epoch switch info") From 6f9fb9d1da2f4575eeb87a5d056991d45d8dcba8 Mon Sep 17 00:00:00 2001 From: JukLee0ira Date: Mon, 18 Nov 2024 12:03:32 +0800 Subject: [PATCH 242/479] node, p2p/simulations: fix node.Node AccountsManager leak (#19004) --- cmd/XDC/chaincmd.go | 16 +++++++++++++++- cmd/XDC/consolecmd.go | 4 ++-- cmd/XDC/main.go | 1 + cmd/faucet/faucet.go | 2 +- console/console_test.go | 4 ++-- node/config_test.go | 18 +++++++++++++++--- node/node.go | 23 +++++++++++++++++++++++ node/node_example_test.go | 10 ++++++---- node/node_test.go | 24 ++++++++++++++++++++++++ node/service_test.go | 1 + p2p/simulations/adapters/inproc.go | 6 ++++++ p2p/simulations/network.go | 11 +++++++++-- 12 files changed, 105 insertions(+), 15 deletions(-) diff --git a/cmd/XDC/chaincmd.go b/cmd/XDC/chaincmd.go index ae4221e4b211..54795af0dbb1 100644 --- a/cmd/XDC/chaincmd.go +++ b/cmd/XDC/chaincmd.go @@ -203,6 +203,8 @@ func initGenesis(ctx *cli.Context) error { // Open an initialise both full and light databases stack, _ := makeFullNode(ctx) + defer stack.Close() + for _, name := range []string{"chaindata", "lightchaindata"} { chaindb, err := stack.OpenDatabase(name, 0, 0, "") if err != nil { @@ -228,6 +230,8 @@ func importChain(ctx *cli.Context) error { go metrics.CollectProcessMetrics(3 * time.Second) stack, _ := makeFullNode(ctx) + defer stack.Close() + chain, chainDb := utils.MakeChain(ctx, stack) defer chainDb.Close() @@ -319,6 +323,8 @@ func exportChain(ctx *cli.Context) error { utils.Fatalf("This command requires an argument.") } stack, _ := makeFullNode(ctx) + defer stack.Close() + chain, db := utils.MakeChain(ctx, stack) defer db.Close() start := time.Now() @@ -353,6 +359,8 @@ func importPreimages(ctx *cli.Context) error { utils.Fatalf("This command requires an argument.") } stack, _ := makeFullNode(ctx) + defer stack.Close() + diskdb := utils.MakeChainDatabase(ctx, stack) defer diskdb.Close() @@ -370,6 +378,8 @@ func exportPreimages(ctx *cli.Context) error { utils.Fatalf("This command requires an argument.") } stack, _ := makeFullNode(ctx) + defer stack.Close() + diskdb := utils.MakeChainDatabase(ctx, stack) defer diskdb.Close() @@ -388,6 +398,8 @@ func copyDb(ctx *cli.Context) error { } // Initialize a new chain for the running node to sync into stack, _ := makeFullNode(ctx) + defer stack.Close() + chain, chainDb := utils.MakeChain(ctx, stack) defer chainDb.Close() @@ -461,9 +473,11 @@ func removeDB(ctx *cli.Context) error { func dump(ctx *cli.Context) error { stack, _ := makeFullNode(ctx) + defer stack.Close() + chain, chainDb := utils.MakeChain(ctx, stack) defer chainDb.Close() - + for _, arg := range ctx.Args() { var block *types.Block if hashish(arg) { diff --git a/cmd/XDC/consolecmd.go b/cmd/XDC/consolecmd.go index e36dda489cc9..c9bf6b2982f2 100644 --- a/cmd/XDC/consolecmd.go +++ b/cmd/XDC/consolecmd.go @@ -79,7 +79,7 @@ func localConsole(ctx *cli.Context) error { // Create and start the node based on the CLI flags node, cfg := makeFullNode(ctx) startNode(ctx, node, cfg) - defer node.Stop() + defer node.Close() // Attach to the newly started node and start the JavaScript console client, err := node.Attach() @@ -181,7 +181,7 @@ func ephemeralConsole(ctx *cli.Context) error { // Create and start the node based on the CLI flags node, cfg := makeFullNode(ctx) startNode(ctx, node, cfg) - defer node.Stop() + defer node.Close() // Attach to the newly started node and start the JavaScript console client, err := node.Attach() diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go index 57c77e6f29ae..dcf2613ab986 100644 --- a/cmd/XDC/main.go +++ b/cmd/XDC/main.go @@ -226,6 +226,7 @@ func main() { // blocking mode, waiting for it to be shut down. func XDC(ctx *cli.Context) error { node, cfg := makeFullNode(ctx) + defer node.Close() startNode(ctx, node, cfg) node.Wait() return nil diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go index dcee12c32b4c..0d94389243d6 100644 --- a/cmd/faucet/faucet.go +++ b/cmd/faucet/faucet.go @@ -287,7 +287,7 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network u // close terminates the Ethereum connection and tears down the faucet. func (f *faucet) close() error { - return f.stack.Stop() + return f.stack.Close() } // listenAndServe registers the HTTP handlers for the faucet and boots it up diff --git a/console/console_test.go b/console/console_test.go index 9433026db868..0d5c94a75497 100644 --- a/console/console_test.go +++ b/console/console_test.go @@ -153,8 +153,8 @@ func (env *tester) Close(t *testing.T) { if err := env.console.Stop(false); err != nil { t.Errorf("failed to stop embedded console: %v", err) } - if err := env.stack.Stop(); err != nil { - t.Errorf("failed to stop embedded node: %v", err) + if err := env.stack.Close(); err != nil { + t.Errorf("failed to tear down embedded node: %v", err) } os.RemoveAll(env.workspace) } diff --git a/node/config_test.go b/node/config_test.go index 8c187323b39c..321a7bad6e63 100644 --- a/node/config_test.go +++ b/node/config_test.go @@ -37,14 +37,22 @@ func TestDatadirCreation(t *testing.T) { } defer os.RemoveAll(dir) - if _, err := New(&Config{DataDir: dir}); err != nil { + node, err := New(&Config{DataDir: dir}) + if err != nil { t.Fatalf("failed to create stack with existing datadir: %v", err) } + if err := node.Close(); err != nil { + t.Fatalf("failed to close node: %v", err) + } // Generate a long non-existing datadir path and check that it gets created by a node dir = filepath.Join(dir, "a", "b", "c", "d", "e", "f") - if _, err := New(&Config{DataDir: dir}); err != nil { + node, err = New(&Config{DataDir: dir}) + if err != nil { t.Fatalf("failed to create stack with creatable datadir: %v", err) } + if err := node.Close(); err != nil { + t.Fatalf("failed to close node: %v", err) + } if _, err := os.Stat(dir); err != nil { t.Fatalf("freshly created datadir not accessible: %v", err) } @@ -56,8 +64,12 @@ func TestDatadirCreation(t *testing.T) { defer os.Remove(file.Name()) dir = filepath.Join(file.Name(), "invalid/path") - if _, err := New(&Config{DataDir: dir}); err == nil { + node, err = New(&Config{DataDir: dir}) + if err == nil { t.Fatalf("protocol stack created with an invalid datadir") + if err := node.Close(); err != nil { + t.Fatalf("failed to close node: %v", err) + } } } diff --git a/node/node.go b/node/node.go index dfa261640c18..40cef4bab342 100644 --- a/node/node.go +++ b/node/node.go @@ -131,6 +131,29 @@ func New(conf *Config) (*Node, error) { }, nil } +// Close stops the Node and releases resources acquired in +// Node constructor New. +func (n *Node) Close() error { + var errs []error + + // Terminate all subsystems and collect any errors + if err := n.Stop(); err != nil && err != ErrNodeStopped { + errs = append(errs, err) + } + if err := n.accman.Close(); err != nil { + errs = append(errs, err) + } + // Report any errors that might have occurred + switch len(errs) { + case 0: + return nil + case 1: + return errs[0] + default: + return fmt.Errorf("%v", errs) + } +} + // Register injects a new service into the node's stack. The service created by // the passed constructor must be unique in its type with regard to sibling ones. func (n *Node) Register(constructor ServiceConstructor) error { diff --git a/node/node_example_test.go b/node/node_example_test.go index f88267371860..b2ad1a8ed3e0 100644 --- a/node/node_example_test.go +++ b/node/node_example_test.go @@ -29,10 +29,10 @@ import ( // life cycle management. // // The following methods are needed to implement a node.Service: -// - Protocols() []p2p.Protocol - devp2p protocols the service can communicate on -// - APIs() []rpc.API - api methods the service wants to expose on rpc channels -// - Start() error - method invoked when the node is ready to start the service -// - Stop() error - method invoked when the node terminates the service +// - Protocols() []p2p.Protocol - devp2p protocols the service can communicate on +// - APIs() []rpc.API - api methods the service wants to expose on rpc channels +// - Start() error - method invoked when the node is ready to start the service +// - Stop() error - method invoked when the node terminates the service type SampleService struct{} func (s *SampleService) Protocols() []p2p.Protocol { return nil } @@ -47,6 +47,8 @@ func ExampleService() { if err != nil { log.Fatalf("Failed to create network node: %v", err) } + defer stack.Close() + // Create and register a simple network service. This is done through the definition // of a node.ServiceConstructor that will instantiate a node.Service. The reason for // the factory method approach is to support service restarts without relying on the diff --git a/node/node_test.go b/node/node_test.go index 4b4eec464d7d..df97c66c71ca 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -47,6 +47,8 @@ func TestNodeLifeCycle(t *testing.T) { if err != nil { t.Fatalf("failed to create protocol stack: %v", err) } + defer stack.Close() + // Ensure that a stopped node can be stopped again for i := 0; i < 3; i++ { if err := stack.Stop(); err != ErrNodeStopped { @@ -89,6 +91,8 @@ func TestNodeUsedDataDir(t *testing.T) { if err != nil { t.Fatalf("failed to create original protocol stack: %v", err) } + defer original.Close() + if err := original.Start(); err != nil { t.Fatalf("failed to start original protocol stack: %v", err) } @@ -99,6 +103,8 @@ func TestNodeUsedDataDir(t *testing.T) { if err != nil { t.Fatalf("failed to create duplicate protocol stack: %v", err) } + defer duplicate.Close() + if err := duplicate.Start(); err != ErrDatadirUsed { t.Fatalf("duplicate datadir failure mismatch: have %v, want %v", err, ErrDatadirUsed) } @@ -110,6 +116,8 @@ func TestServiceRegistry(t *testing.T) { if err != nil { t.Fatalf("failed to create protocol stack: %v", err) } + defer stack.Close() + // Register a batch of unique services and ensure they start successfully services := []ServiceConstructor{NewNoopServiceA, NewNoopServiceB, NewNoopServiceC} for i, constructor := range services { @@ -142,6 +150,8 @@ func TestServiceLifeCycle(t *testing.T) { if err != nil { t.Fatalf("failed to create protocol stack: %v", err) } + defer stack.Close() + // Register a batch of life-cycle instrumented services services := map[string]InstrumentingWrapper{ "A": InstrumentedServiceMakerA, @@ -192,6 +202,8 @@ func TestServiceRestarts(t *testing.T) { if err != nil { t.Fatalf("failed to create protocol stack: %v", err) } + defer stack.Close() + // Define a service that does not support restarts var ( running bool @@ -240,6 +252,8 @@ func TestServiceConstructionAbortion(t *testing.T) { if err != nil { t.Fatalf("failed to create protocol stack: %v", err) } + defer stack.Close() + // Define a batch of good services services := map[string]InstrumentingWrapper{ "A": InstrumentedServiceMakerA, @@ -287,6 +301,8 @@ func TestServiceStartupAbortion(t *testing.T) { if err != nil { t.Fatalf("failed to create protocol stack: %v", err) } + defer stack.Close() + // Register a batch of good services services := map[string]InstrumentingWrapper{ "A": InstrumentedServiceMakerA, @@ -340,6 +356,8 @@ func TestServiceTerminationGuarantee(t *testing.T) { if err != nil { t.Fatalf("failed to create protocol stack: %v", err) } + defer stack.Close() + // Register a batch of good services services := map[string]InstrumentingWrapper{ "A": InstrumentedServiceMakerA, @@ -415,6 +433,8 @@ func TestServiceRetrieval(t *testing.T) { if err != nil { t.Fatalf("failed to create protocol stack: %v", err) } + defer stack.Close() + if err := stack.Register(NewNoopService); err != nil { t.Fatalf("noop service registration failed: %v", err) } @@ -450,6 +470,8 @@ func TestProtocolGather(t *testing.T) { if err != nil { t.Fatalf("failed to create protocol stack: %v", err) } + defer stack.Close() + // Register a batch of services with some configured number of protocols services := map[string]struct { Count int @@ -506,6 +528,8 @@ func TestAPIGather(t *testing.T) { if err != nil { t.Fatalf("failed to create protocol stack: %v", err) } + defer stack.Close() + // Register a batch of services with some configured APIs calls := make(chan string, 1) makeAPI := func(result string) *OneMethodAPI { diff --git a/node/service_test.go b/node/service_test.go index 23dc80708411..1c275e2ecbeb 100644 --- a/node/service_test.go +++ b/node/service_test.go @@ -67,6 +67,7 @@ func TestContextServices(t *testing.T) { if err != nil { t.Fatalf("failed to create protocol stack: %v", err) } + defer stack.Close() // Define a verifier that ensures a NoopA is before it and NoopB after verifier := func(ctx *ServiceContext) (Service, error) { var objA *NoopServiceA diff --git a/p2p/simulations/adapters/inproc.go b/p2p/simulations/adapters/inproc.go index 1f64941dfa96..4363e5eaab66 100644 --- a/p2p/simulations/adapters/inproc.go +++ b/p2p/simulations/adapters/inproc.go @@ -160,6 +160,12 @@ type SimNode struct { connected map[discover.NodeID]bool } +// Close closes the underlaying node.Node to release +// acquired resources. +func (sn *SimNode) Close() error { + return sn.node.Close() +} + // Addr returns the node's discovery address func (sn *SimNode) Addr() []byte { return []byte(sn.Node().String()) diff --git a/p2p/simulations/network.go b/p2p/simulations/network.go index 4c992fd1d5ed..2c4a3d5a3f41 100644 --- a/p2p/simulations/network.go +++ b/p2p/simulations/network.go @@ -21,6 +21,7 @@ import ( "context" "encoding/json" "fmt" + "io" "sync" "time" @@ -497,12 +498,18 @@ func (net *Network) Shutdown() { if err := node.Stop(); err != nil { log.Warn(fmt.Sprintf("error stopping node %s", node.ID().TerminalString()), "err", err) } + // If the node has the close method, call it. + if closer, ok := node.Node.(io.Closer); ok { + if err := closer.Close(); err != nil { + log.Warn("Can't close node", "id", node.ID(), "err", err) + } + } } close(net.quitc) } -//Reset resets all network properties: -//emtpies the nodes and the connection list +// Reset resets all network properties: +// emtpies the nodes and the connection list func (net *Network) Reset() { net.lock.Lock() defer net.lock.Unlock() From dcab7e8efb598cc608807b649708acb31c6825ba Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 25 Nov 2024 16:39:29 +0800 Subject: [PATCH 243/479] cmd: migrate to urfave/cli/v2 (#24751) --- cmd/XDC/accountcmd.go | 49 +- cmd/XDC/accountcmd_test.go | 95 +- cmd/XDC/bugcmd.go | 9 +- cmd/XDC/chaincmd.go | 90 +- cmd/XDC/config.go | 44 +- cmd/XDC/consolecmd.go | 47 +- cmd/XDC/consolecmd_test.go | 24 +- cmd/XDC/dao_test.go | 5 +- cmd/XDC/main.go | 25 +- cmd/XDC/misccmd.go | 26 +- cmd/XDC/run_test.go | 8 +- cmd/XDC/usage.go | 329 ---- cmd/abigen/main.go | 121 +- cmd/ethkey/generate.go | 16 +- cmd/ethkey/inspect.go | 16 +- cmd/ethkey/main.go | 16 +- cmd/ethkey/message.go | 14 +- cmd/ethkey/utils.go | 2 +- cmd/evm/compiler.go | 7 +- cmd/evm/disasm.go | 27 +- cmd/evm/main.go | 193 ++- cmd/evm/runner.go | 65 +- cmd/evm/staterunner.go | 24 +- cmd/p2psim/main.go | 175 +- cmd/puppeth/puppeth.go | 6 +- cmd/utils/customflags.go | 240 --- cmd/utils/flags.go | 1534 +++++++++-------- cmd/utils/flags_legacy.go | 84 + go.mod | 6 +- go.sum | 12 +- internal/cmdtest/test_cmd.go | 21 +- internal/debug/flags.go | 204 ++- internal/flags/categories.go | 45 + internal/flags/flags.go | 340 ++++ .../flags/flags_test.go | 16 +- internal/flags/helpers.go | 176 ++ 36 files changed, 2220 insertions(+), 1891 deletions(-) delete mode 100644 cmd/XDC/usage.go delete mode 100644 cmd/utils/customflags.go create mode 100644 cmd/utils/flags_legacy.go create mode 100644 internal/flags/categories.go create mode 100644 internal/flags/flags.go rename cmd/utils/customflags_test.go => internal/flags/flags_test.go (61%) create mode 100644 internal/flags/helpers.go diff --git a/cmd/XDC/accountcmd.go b/cmd/XDC/accountcmd.go index d54828ab8e27..46909bdf9409 100644 --- a/cmd/XDC/accountcmd.go +++ b/cmd/XDC/accountcmd.go @@ -26,31 +26,31 @@ import ( "github.com/XinFinOrg/XDPoSChain/console" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/log" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli/v2" ) var ( - walletCommand = cli.Command{ + walletCommand = &cli.Command{ Name: "wallet", Usage: "Manage XDPoSChain presale wallets", ArgsUsage: "", - Category: "ACCOUNT COMMANDS", Description: ` XDC wallet import /path/to/my/presale.wallet will prompt for your password and imports your ether presale account. It can be used non-interactively with the --password option taking a passwordfile as argument containing the wallet password in plaintext.`, - Subcommands: []cli.Command{ + Subcommands: []*cli.Command{ { Name: "import", Usage: "Import XDPoSChain presale wallet", ArgsUsage: "", - Action: utils.MigrateFlags(importWallet), + Action: importWallet, Category: "ACCOUNT COMMANDS", Flags: []cli.Flag{ utils.DataDirFlag, + utils.XDCXDataDirFlag, utils.KeyStoreDirFlag, utils.PasswordFileFlag, utils.LightKDFFlag, @@ -65,10 +65,9 @@ passwordfile as argument containing the wallet password in plaintext.`, }, } - accountCommand = cli.Command{ - Name: "account", - Usage: "Manage accounts", - Category: "ACCOUNT COMMANDS", + accountCommand = &cli.Command{ + Name: "account", + Usage: "Manage accounts", Description: ` Manage accounts, list all existing accounts, import a private key into a new @@ -89,13 +88,14 @@ It is safe to transfer the entire directory or the individual keys therein between ethereum nodes by simply copying. Make sure you backup your keys regularly.`, - Subcommands: []cli.Command{ + Subcommands: []*cli.Command{ { Name: "list", Usage: "Print summary of existing accounts", - Action: utils.MigrateFlags(accountList), + Action: accountList, Flags: []cli.Flag{ utils.DataDirFlag, + utils.XDCXDataDirFlag, utils.KeyStoreDirFlag, }, Description: ` @@ -104,9 +104,10 @@ Print a short summary of all accounts`, { Name: "new", Usage: "Create a new account", - Action: utils.MigrateFlags(accountCreate), + Action: accountCreate, Flags: []cli.Flag{ utils.DataDirFlag, + utils.XDCXDataDirFlag, utils.KeyStoreDirFlag, utils.PasswordFileFlag, utils.LightKDFFlag, @@ -129,10 +130,11 @@ password to file or expose in any other way. { Name: "update", Usage: "Update an existing account", - Action: utils.MigrateFlags(accountUpdate), + Action: accountUpdate, ArgsUsage: "
", Flags: []cli.Flag{ utils.DataDirFlag, + utils.XDCXDataDirFlag, utils.KeyStoreDirFlag, utils.LightKDFFlag, }, @@ -158,9 +160,10 @@ changing your password is only possible interactively. { Name: "import", Usage: "Import a private key into a new account", - Action: utils.MigrateFlags(accountImport), + Action: accountImport, Flags: []cli.Flag{ utils.DataDirFlag, + utils.XDCXDataDirFlag, utils.KeyStoreDirFlag, utils.PasswordFileFlag, utils.LightKDFFlag, @@ -293,7 +296,7 @@ func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrErr func accountCreate(ctx *cli.Context) error { cfg := XDCConfig{Node: defaultNodeConfig()} // Load config file. - if file := ctx.GlobalString(configFileFlag.Name); file != "" { + if file := ctx.String(configFileFlag.Name); file != "" { if err := loadConfig(file, &cfg); err != nil { utils.Fatalf("%v", err) } @@ -319,13 +322,13 @@ func accountCreate(ctx *cli.Context) error { // accountUpdate transitions an account from a previous format to the current // one, also providing the possibility to change the pass-phrase. func accountUpdate(ctx *cli.Context) error { - if len(ctx.Args()) == 0 { + if ctx.Args().Len() == 0 { utils.Fatalf("No accounts specified to update") } stack, _ := makeConfigNode(ctx) ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) - for _, addr := range ctx.Args() { + for _, addr := range ctx.Args().Slice() { account, oldPassword := unlockAccount(ctx, ks, addr, 0, nil) newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil) if err := ks.Update(account, oldPassword, newPassword); err != nil { @@ -336,10 +339,10 @@ func accountUpdate(ctx *cli.Context) error { } func importWallet(ctx *cli.Context) error { - keyfile := ctx.Args().First() - if len(keyfile) == 0 { - utils.Fatalf("keyfile must be given as argument") + if ctx.Args().Len() != 1 { + utils.Fatalf("keyfile must be given as the only argument") } + keyfile := ctx.Args().First() keyJson, err := os.ReadFile(keyfile) if err != nil { utils.Fatalf("Could not read wallet file: %v", err) @@ -358,10 +361,10 @@ func importWallet(ctx *cli.Context) error { } func accountImport(ctx *cli.Context) error { - keyfile := ctx.Args().First() - if len(keyfile) == 0 { - utils.Fatalf("keyfile must be given as argument") + if ctx.Args().Len() != 1 { + utils.Fatalf("keyfile must be given as the only argument") } + keyfile := ctx.Args().First() key, err := crypto.LoadECDSA(keyfile) if err != nil { utils.Fatalf("Failed to load the private key: %v", err) diff --git a/cmd/XDC/accountcmd_test.go b/cmd/XDC/accountcmd_test.go index 4577ce5d65b6..668739594d95 100644 --- a/cmd/XDC/accountcmd_test.go +++ b/cmd/XDC/accountcmd_test.go @@ -43,26 +43,31 @@ func tmpDatadirWithKeystore(t *testing.T) string { } func TestAccountListEmpty(t *testing.T) { - XDC := runXDC(t, "account", "list") + datadir := tmpdir(t) + defer os.RemoveAll(datadir) + XDC := runXDC(t, "account", "list", "--datadir", datadir) XDC.ExpectExit() } func TestAccountList(t *testing.T) { datadir := tmpDatadirWithKeystore(t) - XDC := runXDC(t, "account", "list", "--datadir", datadir) - defer XDC.ExpectExit() + defer os.RemoveAll(datadir) + var want = ` +Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} keystore://{{.Datadir}}/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8 +Account #1: {f466859ead1932d743d622cb74fc058882e8648a} keystore://{{.Datadir}}/keystore/aaa +Account #2: {289d485d9771714cce91d3393d764e1311907acc} keystore://{{.Datadir}}/keystore/zzz +` if runtime.GOOS == "windows" { - XDC.Expect(` + want = ` Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} keystore://{{.Datadir}}\keystore\UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8 Account #1: {f466859ead1932d743d622cb74fc058882e8648a} keystore://{{.Datadir}}\keystore\aaa Account #2: {289d485d9771714cce91d3393d764e1311907acc} keystore://{{.Datadir}}\keystore\zzz -`) - } else { - XDC.Expect(` -Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} keystore://{{.Datadir}}/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8 -Account #1: {f466859ead1932d743d622cb74fc058882e8648a} keystore://{{.Datadir}}/keystore/aaa -Account #2: {289d485d9771714cce91d3393d764e1311907acc} keystore://{{.Datadir}}/keystore/zzz -`) +` + } + { + geth := runXDC(t, "account", "list", "--datadir", datadir) + geth.Expect(want) + geth.ExpectExit() } } @@ -92,9 +97,8 @@ Fatal: Passphrases do not match func TestAccountUpdate(t *testing.T) { datadir := tmpDatadirWithKeystore(t) - XDC := runXDC(t, "account", "update", - "--datadir", datadir, "--lightkdf", - "f466859ead1932d743d622cb74fc058882e8648a") + defer os.RemoveAll(datadir) + XDC := runXDC(t, "account", "update", "--datadir", datadir, "--lightkdf", "f466859ead1932d743d622cb74fc058882e8648a") defer XDC.ExpectExit() XDC.Expect(` Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 @@ -107,7 +111,9 @@ Repeat passphrase: {{.InputLine "foobar2"}} } func TestWalletImport(t *testing.T) { - XDC := runXDC(t, "wallet", "import", "--lightkdf", "testdata/guswallet.json") + datadir := tmpdir(t) + defer os.RemoveAll(datadir) + XDC := runXDC(t, "wallet", "import", "--datadir", datadir, "--lightkdf", "testdata/guswallet.json") defer XDC.ExpectExit() XDC.Expect(` !! Unsupported terminal, password will be echoed. @@ -121,6 +127,36 @@ Address: {xdcd4584b5f6229b7be90727b0fc8c6b91bb427821f} } } +func TestAccountHelp(t *testing.T) { + geth := runXDC(t, "account", "-h") + geth.WaitExit() + if have, want := geth.ExitStatus(), 0; have != want { + t.Errorf("exit error, have %d want %d", have, want) + } + + geth = runXDC(t, "account", "import", "-h") + geth.WaitExit() + if have, want := geth.ExitStatus(), 0; have != want { + t.Errorf("exit error, have %d want %d", have, want) + } +} + +func importAccountWithExpect(t *testing.T, key string, expected string) { + dir := t.TempDir() + defer os.RemoveAll(dir) + keyfile := filepath.Join(dir, "key.prv") + if err := os.WriteFile(keyfile, []byte(key), 0600); err != nil { + t.Error(err) + } + passwordFile := filepath.Join(dir, "password.txt") + if err := os.WriteFile(passwordFile, []byte("foobar"), 0600); err != nil { + t.Error(err) + } + geth := runXDC(t, "account", "import", "--lightkdf", "-password", passwordFile, keyfile) + defer geth.ExpectExit() + geth.Expect(expected) +} + func TestWalletImportBadPassword(t *testing.T) { XDC := runXDC(t, "wallet", "import", "--lightkdf", "testdata/guswallet.json") defer XDC.ExpectExit() @@ -133,10 +169,11 @@ Fatal: could not decrypt key with given passphrase func TestUnlockFlag(t *testing.T) { datadir := tmpDatadirWithKeystore(t) + defer os.RemoveAll(datadir) XDC := runXDC(t, - "--datadir", datadir, "--nat", "none", "--nodiscover", "--maxpeers", "0", "--port", "0", - "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", - "js", "testdata/empty.js") + "js", "--datadir", datadir, "--nat", "none", "--nodiscover", "--maxpeers", "0", + "--port", "0", "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", + "testdata/empty.js") XDC.Expect(` Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 !! Unsupported terminal, password will be echoed. @@ -157,6 +194,7 @@ Passphrase: {{.InputLine "foobar"}} func TestUnlockFlagWrongPassword(t *testing.T) { datadir := tmpDatadirWithKeystore(t) + defer os.RemoveAll(datadir) XDC := runXDC(t, "--datadir", datadir, "--nat", "none", "--nodiscover", "--maxpeers", "0", "--port", "0", "--unlock", "f466859ead1932d743d622cb74fc058882e8648a") @@ -176,10 +214,11 @@ Fatal: Failed to unlock account f466859ead1932d743d622cb74fc058882e8648a (could // https://github.com/XinFinOrg/XDPoSChain/issues/1785 func TestUnlockFlagMultiIndex(t *testing.T) { datadir := tmpDatadirWithKeystore(t) + defer os.RemoveAll(datadir) XDC := runXDC(t, - "--datadir", datadir, "--nat", "none", "--nodiscover", "--maxpeers", "0", "--port", "0", - "--unlock", "0,2", - "js", "testdata/empty.js") + "js", "--datadir", datadir, "--nat", "none", "--nodiscover", + "--maxpeers", "0", "--port", "0", "--unlock", "0,2", + "testdata/empty.js") XDC.Expect(` Unlocking account 0 | Attempt 1/3 !! Unsupported terminal, password will be echoed. @@ -203,10 +242,11 @@ Passphrase: {{.InputLine "foobar"}} func TestUnlockFlagPasswordFile(t *testing.T) { datadir := tmpDatadirWithKeystore(t) + defer os.RemoveAll(datadir) XDC := runXDC(t, - "--datadir", datadir, "--nat", "none", "--nodiscover", "--maxpeers", "0", "--port", "0", - "--password", "testdata/passwords.txt", "--unlock", "0,2", - "js", "testdata/empty.js") + "js", "--datadir", datadir, "--nat", "none", "--nodiscover", "--maxpeers", "0", + "--port", "0", "--password", "testdata/passwords.txt", "--unlock", "0,2", + "testdata/empty.js") XDC.ExpectExit() wantMessages := []string{ @@ -223,6 +263,7 @@ func TestUnlockFlagPasswordFile(t *testing.T) { func TestUnlockFlagPasswordFileWrongPassword(t *testing.T) { datadir := tmpDatadirWithKeystore(t) + defer os.RemoveAll(datadir) XDC := runXDC(t, "--datadir", datadir, "--nat", "none", "--nodiscover", "--maxpeers", "0", "--port", "0", "--password", "testdata/wrong-passwords.txt", "--unlock", "0,2") @@ -235,9 +276,9 @@ Fatal: Failed to unlock account 0 (could not decrypt key with given passphrase) func TestUnlockFlagAmbiguous(t *testing.T) { store := filepath.Join("..", "..", "accounts", "keystore", "testdata", "dupes") XDC := runXDC(t, - "--keystore", store, "--nat", "none", "--nodiscover", "--maxpeers", "0", "--port", "0", - "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", - "js", "testdata/empty.js") + "js", "--keystore", store, "--nat", "none", "--nodiscover", "--maxpeers", "0", + "--port", "0", "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", + "testdata/empty.js") defer XDC.ExpectExit() // Helper for the expect template, returns absolute keystore path. diff --git a/cmd/XDC/bugcmd.go b/cmd/XDC/bugcmd.go index c4bb574946a3..201883f8c99e 100644 --- a/cmd/XDC/bugcmd.go +++ b/cmd/XDC/bugcmd.go @@ -28,17 +28,14 @@ import ( "github.com/XinFinOrg/XDPoSChain/cmd/internal/browser" "github.com/XinFinOrg/XDPoSChain/params" - - "github.com/XinFinOrg/XDPoSChain/cmd/utils" - cli "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli/v2" ) -var bugCommand = cli.Command{ - Action: utils.MigrateFlags(reportBug), +var bugCommand = &cli.Command{ + Action: reportBug, Name: "bug", Usage: "opens a window to report a bug on the XDC repo", ArgsUsage: " ", - Category: "MISCELLANEOUS COMMANDS", } const issueUrl = "https://github.com/XinFinOrg/XDPoSChain/issues/new" diff --git a/cmd/XDC/chaincmd.go b/cmd/XDC/chaincmd.go index 54795af0dbb1..31603d52c1f6 100644 --- a/cmd/XDC/chaincmd.go +++ b/cmd/XDC/chaincmd.go @@ -34,23 +34,24 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/eth/downloader" "github.com/XinFinOrg/XDPoSChain/event" + "github.com/XinFinOrg/XDPoSChain/internal/flags" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/metrics" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli/v2" ) var ( - initCommand = cli.Command{ - Action: utils.MigrateFlags(initGenesis), + initCommand = &cli.Command{ + Action: initGenesis, Name: "init", Usage: "Bootstrap and initialize a new genesis block", ArgsUsage: "", Flags: []cli.Flag{ utils.DataDirFlag, + utils.XDCXDataDirFlag, utils.LightModeFlag, utils.XDCTestnetFlag, }, - Category: "BLOCKCHAIN COMMANDS", Description: ` The init command initializes a new genesis block and definition for the network. This is a destructive action and changes the network in which you will be @@ -58,20 +59,20 @@ participating. It expects the genesis file as argument.`, } - importCommand = cli.Command{ - Action: utils.MigrateFlags(importChain), + importCommand = &cli.Command{ + Action: importChain, Name: "import", Usage: "Import a blockchain file", ArgsUsage: " ( ... ) ", Flags: []cli.Flag{ utils.DataDirFlag, + utils.XDCXDataDirFlag, utils.CacheFlag, utils.LightModeFlag, utils.GCModeFlag, utils.CacheDatabaseFlag, utils.CacheGCFlag, }, - Category: "BLOCKCHAIN COMMANDS", Description: ` The import command imports blocks from an RLP-encoded form. The form can be one file with several RLP-encoded blocks, or several files can be used. @@ -79,30 +80,31 @@ with several RLP-encoded blocks, or several files can be used. If only one file is used, import error will result in failure. If several files are used, processing will proceed even if an individual RLP-file import failure occurs.`, } - exportCommand = cli.Command{ - Action: utils.MigrateFlags(exportChain), + exportCommand = &cli.Command{ + Action: exportChain, Name: "export", Usage: "Export blockchain into file", ArgsUsage: " [ ]", Flags: []cli.Flag{ utils.DataDirFlag, + utils.XDCXDataDirFlag, utils.CacheFlag, utils.LightModeFlag, }, - Category: "BLOCKCHAIN COMMANDS", Description: ` Requires a first argument of the file to write to. Optional second and third arguments control the first and last block to write. In this mode, the file will be appended if already existing.`, } - importPreimagesCommand = cli.Command{ - Action: utils.MigrateFlags(importPreimages), + importPreimagesCommand = &cli.Command{ + Action: importPreimages, Name: "import-preimages", Usage: "Import the preimage database from an RLP stream", ArgsUsage: "", Flags: []cli.Flag{ utils.DataDirFlag, + utils.XDCXDataDirFlag, utils.CacheFlag, utils.LightModeFlag, }, @@ -110,57 +112,58 @@ if already existing.`, Description: ` The import-preimages command imports hash preimages from an RLP encoded stream.`, } - exportPreimagesCommand = cli.Command{ - Action: utils.MigrateFlags(exportPreimages), + exportPreimagesCommand = &cli.Command{ + Action: exportPreimages, Name: "export-preimages", Usage: "Export the preimage database into an RLP stream", ArgsUsage: "", Flags: []cli.Flag{ utils.DataDirFlag, + utils.XDCXDataDirFlag, utils.CacheFlag, utils.LightModeFlag, }, - Category: "BLOCKCHAIN COMMANDS", Description: ` The export-preimages command export hash preimages to an RLP encoded stream`, } - copydbCommand = cli.Command{ - Action: utils.MigrateFlags(copyDb), + copydbCommand = &cli.Command{ + Action: copyDb, Name: "copydb", Usage: "Create a local chain from a target chaindata folder", ArgsUsage: "", Flags: []cli.Flag{ utils.DataDirFlag, + utils.XDCXDataDirFlag, utils.CacheFlag, utils.SyncModeFlag, utils.FakePoWFlag, utils.TestnetFlag, utils.RinkebyFlag, }, - Category: "BLOCKCHAIN COMMANDS", Description: ` The first argument must be the directory containing the blockchain to download from`, } - removedbCommand = cli.Command{ - Action: utils.MigrateFlags(removeDB), + removedbCommand = &cli.Command{ + Action: removeDB, Name: "removedb", Usage: "Remove blockchain and state databases", ArgsUsage: " ", Flags: []cli.Flag{ utils.DataDirFlag, + utils.XDCXDataDirFlag, utils.LightModeFlag, }, - Category: "BLOCKCHAIN COMMANDS", Description: ` Remove blockchain and state databases`, } - dumpCommand = cli.Command{ - Action: utils.MigrateFlags(dump), + dumpCommand = &cli.Command{ + Action: dump, Name: "dump", Usage: "Dump a specific block from storage", ArgsUsage: "[ | ]...", Flags: []cli.Flag{ utils.DataDirFlag, + utils.XDCXDataDirFlag, utils.CacheFlag, utils.LightModeFlag, }, @@ -174,12 +177,10 @@ Use "ethereum dump 0" to dump the genesis block.`, // initGenesis will initialise the given JSON format genesis file and writes it as // the zero'd block (i.e. genesis) or will fail hard if it can't succeed. func initGenesis(ctx *cli.Context) error { - // Make sure we have a valid genesis JSON - genesisPath := ctx.Args().First() genesis := new(core.Genesis) - if ctx.GlobalBool(utils.XDCTestnetFlag.Name) { - if len(genesisPath) > 0 { + if ctx.Bool(utils.XDCTestnetFlag.Name) { + if ctx.Args().Len() > 0 { utils.Fatalf("Flags --apothem and genesis file can't be used at the same time") } err := json.Unmarshal(apothemGenesis, &genesis) @@ -187,8 +188,12 @@ func initGenesis(ctx *cli.Context) error { utils.Fatalf("invalid genesis json: %v", err) } } else { + if ctx.Args().Len() != 1 { + utils.Fatalf("need genesis.json file as the only argument") + } + genesisPath := ctx.Args().First() if len(genesisPath) == 0 { - utils.Fatalf("Must supply path to genesis JSON file") + utils.Fatalf("invalid path to genesis file") } file, err := os.Open(genesisPath) if err != nil { @@ -220,10 +225,9 @@ func initGenesis(ctx *cli.Context) error { } func importChain(ctx *cli.Context) error { - if len(ctx.Args()) < 1 { + if ctx.Args().Len() < 1 { utils.Fatalf("This command requires an argument.") } - // Start metrics export if enabled utils.SetupMetrics(ctx) // Start system runtime metrics collection @@ -253,12 +257,12 @@ func importChain(ctx *cli.Context) error { // Import the chain start := time.Now() - if len(ctx.Args()) == 1 { + if ctx.Args().Len() == 1 { if err := utils.ImportChain(chain, ctx.Args().First()); err != nil { log.Error("Import error", "err", err) } } else { - for _, arg := range ctx.Args() { + for _, arg := range ctx.Args().Slice() { if err := utils.ImportChain(chain, arg); err != nil { log.Error("Import error", "file", arg, "err", err) } @@ -291,7 +295,7 @@ func importChain(ctx *cli.Context) error { fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000) fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs)) - if ctx.GlobalIsSet(utils.NoCompactionFlag.Name) { + if ctx.IsSet(utils.NoCompactionFlag.Name) { return nil } @@ -319,9 +323,10 @@ func importChain(ctx *cli.Context) error { } func exportChain(ctx *cli.Context) error { - if len(ctx.Args()) < 1 { + if ctx.Args().Len() < 1 { utils.Fatalf("This command requires an argument.") } + stack, _ := makeFullNode(ctx) defer stack.Close() @@ -331,7 +336,7 @@ func exportChain(ctx *cli.Context) error { var err error fp := ctx.Args().First() - if len(ctx.Args()) < 3 { + if ctx.Args().Len() < 3 { err = utils.ExportChain(chain, fp) } else { // This can be improved to allow for numbers larger than 9223372036854775807 @@ -355,9 +360,10 @@ func exportChain(ctx *cli.Context) error { // importPreimages imports preimage data from the specified file. func importPreimages(ctx *cli.Context) error { - if len(ctx.Args()) < 1 { + if ctx.Args().Len() < 1 { utils.Fatalf("This command requires an argument.") } + stack, _ := makeFullNode(ctx) defer stack.Close() @@ -374,7 +380,7 @@ func importPreimages(ctx *cli.Context) error { // exportPreimages dumps the preimage data to specified json file in streaming way. func exportPreimages(ctx *cli.Context) error { - if len(ctx.Args()) < 1 { + if ctx.Args().Len() < 1 { utils.Fatalf("This command requires an argument.") } stack, _ := makeFullNode(ctx) @@ -393,21 +399,21 @@ func exportPreimages(ctx *cli.Context) error { func copyDb(ctx *cli.Context) error { // Ensure we have a source chain directory to copy - if len(ctx.Args()) != 1 { + if ctx.Args().Len() != 1 { utils.Fatalf("Source chaindata directory path argument missing") } // Initialize a new chain for the running node to sync into stack, _ := makeFullNode(ctx) defer stack.Close() - + chain, chainDb := utils.MakeChain(ctx, stack) defer chainDb.Close() - syncmode := *utils.GlobalTextMarshaler(ctx, utils.SyncModeFlag.Name).(*downloader.SyncMode) + syncmode := *flags.GlobalTextMarshaler(ctx, utils.SyncModeFlag.Name).(*downloader.SyncMode) dl := downloader.New(syncmode, chainDb, new(event.TypeMux), chain, nil, nil, nil) // Create a source peer to satisfy downloader requests from - db, err := rawdb.NewLevelDBDatabase(ctx.Args().First(), ctx.GlobalInt(utils.CacheFlag.Name), 256, "") + db, err := rawdb.NewLevelDBDatabase(ctx.Args().First(), ctx.Int(utils.CacheFlag.Name), 256, "") if err != nil { return err } @@ -478,7 +484,7 @@ func dump(ctx *cli.Context) error { chain, chainDb := utils.MakeChain(ctx, stack) defer chainDb.Close() - for _, arg := range ctx.Args() { + for _, arg := range ctx.Args().Slice() { var block *types.Block if hashish(arg) { block = chain.GetBlockByHash(common.HexToHash(arg)) diff --git a/cmd/XDC/config.go b/cmd/XDC/config.go index a8ceadb894a6..5f9cbab571f9 100644 --- a/cmd/XDC/config.go +++ b/cmd/XDC/config.go @@ -27,31 +27,31 @@ import ( "strings" "unicode" - "gopkg.in/urfave/cli.v1" - "github.com/XinFinOrg/XDPoSChain/XDCx" "github.com/XinFinOrg/XDPoSChain/cmd/utils" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/eth/ethconfig" + "github.com/XinFinOrg/XDPoSChain/internal/flags" "github.com/XinFinOrg/XDPoSChain/node" "github.com/XinFinOrg/XDPoSChain/params" "github.com/naoina/toml" + "github.com/urfave/cli/v2" ) var ( - dumpConfigCommand = cli.Command{ - Action: utils.MigrateFlags(dumpConfig), + dumpConfigCommand = &cli.Command{ + Action: dumpConfig, Name: "dumpconfig", Usage: "Show configuration values", ArgsUsage: "", - Flags: append(nodeFlags, rpcFlags...), - Category: "MISCELLANEOUS COMMANDS", + Flags: utils.GroupFlags(nodeFlags, rpcFlags), Description: `The dumpconfig command shows configuration values.`, } - configFileFlag = cli.StringFlag{ - Name: "config", - Usage: "TOML configuration file", + configFileFlag = &cli.StringFlag{ + Name: "config", + Usage: "TOML configuration file", + Category: flags.EthCategory, } ) @@ -133,24 +133,24 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, XDCConfig) { NAT: "", } // Load config file. - if file := ctx.GlobalString(configFileFlag.Name); file != "" { + if file := ctx.String(configFileFlag.Name); file != "" { if err := loadConfig(file, &cfg); err != nil { utils.Fatalf("%v", err) } } - if ctx.GlobalIsSet(utils.StakingEnabledFlag.Name) { - cfg.StakeEnable = ctx.GlobalBool(utils.StakingEnabledFlag.Name) + if ctx.IsSet(utils.StakingEnabledFlag.Name) { + cfg.StakeEnable = ctx.Bool(utils.StakingEnabledFlag.Name) } - // if !ctx.GlobalIsSet(debug.VerbosityFlag.Name) { + // if !ctx.IsSet(debug.VerbosityFlag.Name) { // debug.Verbosity(log.Lvl(cfg.Verbosity)) // } - if !ctx.GlobalIsSet(utils.NATFlag.Name) && cfg.NAT != "" { + if !ctx.IsSet(utils.NATFlag.Name) && cfg.NAT != "" { ctx.Set(utils.NATFlag.Name, cfg.NAT) } // Check testnet is enable. - if ctx.GlobalBool(utils.XDCTestnetFlag.Name) { + if ctx.Bool(utils.XDCTestnetFlag.Name) { common.IsTestnet = true common.TRC21IssuerSMC = common.TRC21IssuerSMCTestNet cfg.Eth.NetworkId = 51 @@ -159,24 +159,24 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, XDCConfig) { common.TIPXDCXCancellationFee = common.TIPXDCXCancellationFeeTestnet } - if ctx.GlobalBool(utils.EnableXDCPrefixFlag.Name) { + if ctx.Bool(utils.EnableXDCPrefixFlag.Name) { common.Enable0xPrefix = false } // Rewound - if rewound := ctx.GlobalInt(utils.RewoundFlag.Name); rewound != 0 { + if rewound := ctx.Int(utils.RewoundFlag.Name); rewound != 0 { common.Rewound = uint64(rewound) } // Check rollback hash exist. - if rollbackHash := ctx.GlobalString(utils.RollbackFlag.Name); rollbackHash != "" { + if rollbackHash := ctx.String(utils.RollbackFlag.Name); rollbackHash != "" { common.RollbackHash = common.HexToHash(rollbackHash) } // Check GasPrice common.MinGasPrice = big.NewInt(common.DefaultMinGasPrice) - if ctx.GlobalIsSet(utils.GasPriceFlag.Name) { - if gasPrice := int64(ctx.GlobalInt(utils.GasPriceFlag.Name)); gasPrice > common.DefaultMinGasPrice { + if ctx.IsSet(utils.GasPriceFlag.Name) { + if gasPrice := int64(ctx.Int(utils.GasPriceFlag.Name)); gasPrice > common.DefaultMinGasPrice { common.MinGasPrice = big.NewInt(gasPrice) } } @@ -203,8 +203,8 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, XDCConfig) { utils.Fatalf("Failed to create the protocol stack: %v", err) } utils.SetEthConfig(ctx, stack, &cfg.Eth) - if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) { - cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name) + if ctx.IsSet(utils.EthStatsURLFlag.Name) { + cfg.Ethstats.URL = ctx.String(utils.EthStatsURLFlag.Name) } utils.SetXDCXConfig(ctx, &cfg.XDCX, cfg.Node.DataDir) diff --git a/cmd/XDC/consolecmd.go b/cmd/XDC/consolecmd.go index c9bf6b2982f2..a5fad4b3673b 100644 --- a/cmd/XDC/consolecmd.go +++ b/cmd/XDC/consolecmd.go @@ -28,31 +28,29 @@ import ( "github.com/XinFinOrg/XDPoSChain/console" "github.com/XinFinOrg/XDPoSChain/node" "github.com/XinFinOrg/XDPoSChain/rpc" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli/v2" ) var ( consoleFlags = []cli.Flag{utils.JSpathFlag, utils.ExecFlag, utils.PreloadJSFlag} - consoleCommand = cli.Command{ - Action: utils.MigrateFlags(localConsole), - Name: "console", - Usage: "Start an interactive JavaScript environment", - Flags: append(append(nodeFlags, rpcFlags...), consoleFlags...), - Category: "CONSOLE COMMANDS", + consoleCommand = &cli.Command{ + Action: localConsole, + Name: "console", + Usage: "Start an interactive JavaScript environment", + Flags: utils.GroupFlags(nodeFlags, rpcFlags, consoleFlags), Description: ` The XDC console is an interactive shell for the JavaScript runtime environment which exposes a node admin interface as well as the Ðapp JavaScript API. See https://github.com/XinFinOrg/XDPoSChain/wiki/JavaScript-Console.`, } - attachCommand = cli.Command{ - Action: utils.MigrateFlags(remoteConsole), + attachCommand = &cli.Command{ + Action: remoteConsole, Name: "attach", Usage: "Start an interactive JavaScript environment (connect to node)", ArgsUsage: "[endpoint]", - Flags: append(consoleFlags, utils.DataDirFlag), - Category: "CONSOLE COMMANDS", + Flags: utils.GroupFlags([]cli.Flag{utils.DataDirFlag}, consoleFlags), Description: ` The XDC console is an interactive shell for the JavaScript runtime environment which exposes a node admin interface as well as the Ðapp JavaScript API. @@ -60,13 +58,12 @@ See https://github.com/XinFinOrg/XDPoSChain/wiki/JavaScript-Console. This command allows to open a console on a running XDC node.`, } - javascriptCommand = cli.Command{ - Action: utils.MigrateFlags(ephemeralConsole), + javascriptCommand = &cli.Command{ + Action: ephemeralConsole, Name: "js", Usage: "Execute the specified JavaScript files", ArgsUsage: " [jsfile...]", - Flags: append(nodeFlags, consoleFlags...), - Category: "CONSOLE COMMANDS", + Flags: utils.GroupFlags(nodeFlags, consoleFlags), Description: ` The JavaScript VM exposes a node admin interface as well as the Ðapp JavaScript API. See https://github.com/XinFinOrg/XDPoSChain/wiki/JavaScript-Console`, @@ -88,7 +85,7 @@ func localConsole(ctx *cli.Context) error { } config := console.Config{ DataDir: utils.MakeDataDir(ctx), - DocRoot: ctx.GlobalString(utils.JSpathFlag.Name), + DocRoot: ctx.String(utils.JSpathFlag.Name), Client: client, Preload: utils.MakeConsolePreloads(ctx), } @@ -100,7 +97,7 @@ func localConsole(ctx *cli.Context) error { defer console.Stop(false) // If only a short execution was requested, evaluate and return - if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" { + if script := ctx.String(utils.ExecFlag.Name); script != "" { console.Evaluate(script) return nil } @@ -118,13 +115,13 @@ func remoteConsole(ctx *cli.Context) error { endpoint := ctx.Args().First() if endpoint == "" { path := node.DefaultDataDir() - if ctx.GlobalIsSet(utils.DataDirFlag.Name) { - path = ctx.GlobalString(utils.DataDirFlag.Name) + if ctx.IsSet(utils.DataDirFlag.Name) { + path = ctx.String(utils.DataDirFlag.Name) } if path != "" { - if ctx.GlobalBool(utils.TestnetFlag.Name) { + if ctx.Bool(utils.TestnetFlag.Name) { path = filepath.Join(path, "testnet") - } else if ctx.GlobalBool(utils.RinkebyFlag.Name) { + } else if ctx.Bool(utils.RinkebyFlag.Name) { path = filepath.Join(path, "rinkeby") } } @@ -137,7 +134,7 @@ func remoteConsole(ctx *cli.Context) error { } config := console.Config{ DataDir: utils.MakeDataDir(ctx), - DocRoot: ctx.GlobalString(utils.JSpathFlag.Name), + DocRoot: ctx.String(utils.JSpathFlag.Name), Client: client, Preload: utils.MakeConsolePreloads(ctx), } @@ -148,7 +145,7 @@ func remoteConsole(ctx *cli.Context) error { } defer console.Stop(false) - if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" { + if script := ctx.String(utils.ExecFlag.Name); script != "" { console.Evaluate(script) return nil } @@ -190,7 +187,7 @@ func ephemeralConsole(ctx *cli.Context) error { } config := console.Config{ DataDir: utils.MakeDataDir(ctx), - DocRoot: ctx.GlobalString(utils.JSpathFlag.Name), + DocRoot: ctx.String(utils.JSpathFlag.Name), Client: client, Preload: utils.MakeConsolePreloads(ctx), } @@ -202,7 +199,7 @@ func ephemeralConsole(ctx *cli.Context) error { defer console.Stop(false) // Evaluate each of the specified JavaScript files - for _, file := range ctx.Args() { + for _, file := range ctx.Args().Slice() { if err = console.Execute(file); err != nil { utils.Fatalf("Failed to execute %s: %v", file, err) } diff --git a/cmd/XDC/consolecmd_test.go b/cmd/XDC/consolecmd_test.go index ecbd11762aee..9ca2e0b73ed4 100644 --- a/cmd/XDC/consolecmd_test.go +++ b/cmd/XDC/consolecmd_test.go @@ -39,13 +39,14 @@ const ( // then terminated by closing the input stream. func TestConsoleWelcome(t *testing.T) { coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" + datadir := tmpdir(t) + defer os.RemoveAll(datadir) // Start a XDC console, make sure it's cleaned up and terminate the console XDC := runXDC(t, - "--XDCx.datadir", tmpdir(t)+"XDCx/"+time.Now().String(), + "console", "--datadir", datadir, "--XDCx.datadir", datadir+"/XDCx/"+time.Now().String(), "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", - "--etherbase", coinbase, - "console") + "--etherbase", coinbase) // Gather all the infos the welcome message needs to contain XDC.SetTemplateFunc("goos", func() string { return runtime.GOOS }) @@ -76,16 +77,16 @@ at block: 0 ({{niltime}}) func TestIPCAttachWelcome(t *testing.T) { // Configure the instance for IPC attachement coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" + datadir := tmpdir(t) + defer os.RemoveAll(datadir) var ipc string if runtime.GOOS == "windows" { ipc = `\\.\pipe\XDC` + strconv.Itoa(trulyRandInt(100000, 999999)) } else { - ws := tmpdir(t) - defer os.RemoveAll(ws) - ipc = filepath.Join(ws, "XDC.ipc") + ipc = filepath.Join(datadir, "XDC.ipc") } XDC := runXDC(t, - "--XDCx.datadir", tmpdir(t)+"XDCx/"+time.Now().String(), + "--datadir", datadir, "--XDCx.datadir", datadir+"/XDCx/"+time.Now().String(), "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--etherbase", coinbase, "--ipcpath", ipc) @@ -99,8 +100,10 @@ func TestIPCAttachWelcome(t *testing.T) { func TestHTTPAttachWelcome(t *testing.T) { coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" port := strconv.Itoa(trulyRandInt(1024, 65536)) // Yeah, sometimes this will fail, sorry :P + datadir := tmpdir(t) + defer os.RemoveAll(datadir) XDC := runXDC(t, - "--XDCx.datadir", tmpdir(t)+"XDCx/"+time.Now().String(), + "--datadir", datadir, "--XDCx.datadir", datadir+"/XDCx/"+time.Now().String(), "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--etherbase", coinbase, "--rpc", "--rpcport", port) @@ -114,9 +117,10 @@ func TestHTTPAttachWelcome(t *testing.T) { func TestWSAttachWelcome(t *testing.T) { coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" port := strconv.Itoa(trulyRandInt(1024, 65536)) // Yeah, sometimes this will fail, sorry :P - + datadir := tmpdir(t) + defer os.RemoveAll(datadir) XDC := runXDC(t, - "--XDCx.datadir", tmpdir(t)+"XDCx/"+time.Now().String(), + "--datadir", datadir, "--XDCx.datadir", datadir+"/XDCx/"+time.Now().String(), "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--etherbase", coinbase, "--ws", "--wsport", port) diff --git a/cmd/XDC/dao_test.go b/cmd/XDC/dao_test.go index 030e255517c3..5444a4b13761 100644 --- a/cmd/XDC/dao_test.go +++ b/cmd/XDC/dao_test.go @@ -111,11 +111,10 @@ func testDAOForkBlockNewChain(t *testing.T, test int, genesis string, expectBloc if err := os.WriteFile(json, []byte(genesis), 0600); err != nil { t.Fatalf("test %d: failed to write genesis file: %v", test, err) } - runXDC(t, "--datadir", datadir, "init", json).WaitExit() + runXDC(t, "init", "--datadir", datadir, json).WaitExit() } else { // Force chain initialization - args := []string{"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--datadir", datadir} - XDC := runXDC(t, append(args, []string{"--exec", "2+2", "console"}...)...) + XDC := runXDC(t, "console", "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--datadir", datadir, "--exec", "2+2") XDC.WaitExit() } // Retrieve the DAO config flag from the database diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go index dcf2613ab986..a736c81e27d9 100644 --- a/cmd/XDC/main.go +++ b/cmd/XDC/main.go @@ -33,14 +33,14 @@ import ( "github.com/XinFinOrg/XDPoSChain/eth" "github.com/XinFinOrg/XDPoSChain/ethclient" "github.com/XinFinOrg/XDPoSChain/internal/debug" + "github.com/XinFinOrg/XDPoSChain/internal/flags" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/metrics" "github.com/XinFinOrg/XDPoSChain/node" + "github.com/urfave/cli/v2" // Force-load the native, to trigger registration _ "github.com/XinFinOrg/XDPoSChain/eth/tracers/native" - - "gopkg.in/urfave/cli.v1" ) const ( @@ -51,7 +51,7 @@ var ( // Git SHA1 commit hash of the release (set via linker flags) gitCommit = "" // The app that holds all commands and flags. - app = utils.NewApp(gitCommit, "the XDPoSChain command line interface") + app = flags.NewApp(gitCommit, "the XDPoSChain command line interface") // flags that configure the node nodeFlags = []cli.Flag{ utils.IdentityFlag, @@ -168,8 +168,8 @@ func init() { // Initialize the CLI app and start XDC app.Action = XDC app.HideVersion = true // we have a command to print the version - app.Copyright = "Copyright (c) 2018 XDPoSChain" - app.Commands = []cli.Command{ + app.Copyright = "Copyright (c) 2024 XDPoSChain" + app.Commands = []*cli.Command{ // See chaincmd.go: initCommand, importCommand, @@ -197,6 +197,7 @@ func init() { app.Before = func(ctx *cli.Context) error { runtime.GOMAXPROCS(runtime.NumCPU()) + flags.MigrateGlobalFlags(ctx) if err := debug.Setup(ctx); err != nil { return err } @@ -242,11 +243,11 @@ func startNode(ctx *cli.Context, stack *node.Node, cfg XDCConfig) { // Unlock any account specifically requested ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) - if ctx.GlobalIsSet(utils.UnlockedAccountFlag.Name) { - cfg.Account.Unlocks = strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",") + if ctx.IsSet(utils.UnlockedAccountFlag.Name) { + cfg.Account.Unlocks = strings.Split(ctx.String(utils.UnlockedAccountFlag.Name), ",") } - if ctx.GlobalIsSet(utils.PasswordFileFlag.Name) { + if ctx.IsSet(utils.PasswordFileFlag.Name) { cfg.Account.Passwords = utils.MakePasswordList(ctx) } @@ -299,7 +300,7 @@ func startNode(ctx *cli.Context, stack *node.Node, cfg XDCConfig) { // Start auxiliary services if enabled // Mining only makes sense if a full Ethereum node is running - if ctx.GlobalBool(utils.LightModeFlag.Name) || ctx.GlobalString(utils.SyncModeFlag.Name) == "light" { + if ctx.Bool(utils.LightModeFlag.Name) || ctx.String(utils.SyncModeFlag.Name) == "light" { utils.Fatalf("Light clients do not support staking") } // Start metrics export if enabled @@ -315,7 +316,7 @@ func startNode(ctx *cli.Context, stack *node.Node, cfg XDCConfig) { go func() { started := false ok := false - slaveMode := ctx.GlobalIsSet(utils.XDCSlaveModeFlag.Name) + slaveMode := ctx.IsSet(utils.XDCSlaveModeFlag.Name) var err error ok, err = ethereum.ValidateMasternode() if err != nil { @@ -328,7 +329,7 @@ func startNode(ctx *cli.Context, stack *node.Node, cfg XDCConfig) { } else { log.Info("Masternode found. Enabling staking mode...") // Use a reduced number of threads if requested - if threads := ctx.GlobalInt(utils.StakerThreadsFlag.Name); threads > 0 { + if threads := ctx.Int(utils.StakerThreadsFlag.Name); threads > 0 { type threaded interface { SetThreads(threads int) } @@ -370,7 +371,7 @@ func startNode(ctx *cli.Context, stack *node.Node, cfg XDCConfig) { } else { log.Info("Masternode found. Enabling staking mode...") // Use a reduced number of threads if requested - if threads := ctx.GlobalInt(utils.StakerThreadsFlag.Name); threads > 0 { + if threads := ctx.Int(utils.StakerThreadsFlag.Name); threads > 0 { type threaded interface { SetThreads(threads int) } diff --git a/cmd/XDC/misccmd.go b/cmd/XDC/misccmd.go index e86f7c27cccd..65a0ae0d80e5 100644 --- a/cmd/XDC/misccmd.go +++ b/cmd/XDC/misccmd.go @@ -28,16 +28,15 @@ import ( "github.com/XinFinOrg/XDPoSChain/eth" "github.com/XinFinOrg/XDPoSChain/eth/ethconfig" "github.com/XinFinOrg/XDPoSChain/params" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli/v2" ) var ( - makecacheCommand = cli.Command{ - Action: utils.MigrateFlags(makecache), + makecacheCommand = &cli.Command{ + Action: makecache, Name: "makecache", Usage: "Generate ethash verification cache (for testing)", ArgsUsage: " ", - Category: "MISCELLANEOUS COMMANDS", Description: ` The makecache command generates an ethash cache in . @@ -45,12 +44,11 @@ This command exists to support the system testing project. Regular users do not need to execute it. `, } - makedagCommand = cli.Command{ - Action: utils.MigrateFlags(makedag), + makedagCommand = &cli.Command{ + Action: makedag, Name: "makedag", Usage: "Generate ethash mining DAG (for testing)", ArgsUsage: " ", - Category: "MISCELLANEOUS COMMANDS", Description: ` The makedag command generates an ethash DAG in . @@ -58,28 +56,26 @@ This command exists to support the system testing project. Regular users do not need to execute it. `, } - versionCommand = cli.Command{ - Action: utils.MigrateFlags(version), + versionCommand = &cli.Command{ + Action: version, Name: "version", Usage: "Print version numbers", ArgsUsage: " ", - Category: "MISCELLANEOUS COMMANDS", Description: ` The output of this command is supposed to be machine-readable. `, } - licenseCommand = cli.Command{ - Action: utils.MigrateFlags(license), + licenseCommand = &cli.Command{ + Action: license, Name: "license", Usage: "Display license information", ArgsUsage: " ", - Category: "MISCELLANEOUS COMMANDS", } ) // makecache generates an ethash verification cache into the provided folder. func makecache(ctx *cli.Context) error { - args := ctx.Args() + args := ctx.Args().Slice() if len(args) != 2 { utils.Fatalf(`Usage: XDC makecache `) } @@ -94,7 +90,7 @@ func makecache(ctx *cli.Context) error { // makedag generates an ethash mining DAG into the provided folder. func makedag(ctx *cli.Context) error { - args := ctx.Args() + args := ctx.Args().Slice() if len(args) != 2 { utils.Fatalf(`Usage: XDC makedag `) } diff --git a/cmd/XDC/run_test.go b/cmd/XDC/run_test.go index 82b4c8fd5862..231385e1ac19 100644 --- a/cmd/XDC/run_test.go +++ b/cmd/XDC/run_test.go @@ -66,12 +66,12 @@ func runXDC(t *testing.T, args ...string) *testXDC { tt := &testXDC{} tt.TestCmd = cmdtest.NewTestCmd(t, tt) for i, arg := range args { - switch { - case arg == "-datadir" || arg == "--datadir": + switch arg { + case "--datadir": if i < len(args)-1 { tt.Datadir = args[i+1] } - case arg == "-etherbase" || arg == "--etherbase": + case "--etherbase": if i < len(args)-1 { tt.Etherbase = args[i+1] } @@ -80,7 +80,7 @@ func runXDC(t *testing.T, args ...string) *testXDC { if tt.Datadir == "" { tt.Datadir = tmpdir(t) tt.Cleanup = func() { os.RemoveAll(tt.Datadir) } - args = append([]string{"-datadir", tt.Datadir}, args...) + args = append([]string{"--datadir", tt.Datadir}, args...) // Remove the temporary datadir if something fails below. defer func() { if t.Failed() { diff --git a/cmd/XDC/usage.go b/cmd/XDC/usage.go deleted file mode 100644 index 2fdfdd9576c2..000000000000 --- a/cmd/XDC/usage.go +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . - -// Contains the XDC command usage template and generator. - -package main - -import ( - "io" - "sort" - - "github.com/XinFinOrg/XDPoSChain/cmd/utils" - "github.com/XinFinOrg/XDPoSChain/internal/debug" - "gopkg.in/urfave/cli.v1" -) - -// AppHelpTemplate is the test template for the default, global app help topic. -var AppHelpTemplate = `NAME: - {{.App.Name}} - {{.App.Usage}} - - Copyright (c) 2018 XDPoSChain - -USAGE: - {{.App.HelpName}} [options]{{if .App.Commands}} command [command options]{{end}} {{if .App.ArgsUsage}}{{.App.ArgsUsage}}{{else}}[arguments...]{{end}} - {{if .App.Version}} -VERSION: - {{.App.Version}} - {{end}}{{if len .App.Authors}} -AUTHOR(S): - {{range .App.Authors}}{{ . }}{{end}} - {{end}}{{if .App.Commands}} -COMMANDS: - {{range .App.Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}} - {{end}}{{end}}{{if .FlagGroups}} -{{range .FlagGroups}}{{.Name}} OPTIONS: - {{range .Flags}}{{.}} - {{end}} -{{end}}{{end}}{{if .App.Copyright }} -COPYRIGHT: - {{.App.Copyright}} - {{end}} -` - -// flagGroup is a collection of flags belonging to a single topic. -type flagGroup struct { - Name string - Flags []cli.Flag -} - -// AppHelpFlagGroups is the application flags, grouped by functionality. -var AppHelpFlagGroups = []flagGroup{ - { - Name: "XDPoSChain", - Flags: []cli.Flag{ - configFileFlag, - utils.DataDirFlag, - utils.KeyStoreDirFlag, - //utils.NoUSBFlag, - utils.NetworkIdFlag, - //utils.TestnetFlag, - //utils.RinkebyFlag, - utils.SyncModeFlag, - utils.GCModeFlag, - utils.EthStatsURLFlag, - utils.IdentityFlag, - //utils.LightServFlag, - //utils.LightPeersFlag, - //utils.LightKDFFlag, - }, - }, - //{Name: "DEVELOPER CHAIN", - // Flags: []cli.Flag{ - // utils.DeveloperFlag, - // utils.DeveloperPeriodFlag, - // }, - //}, - //{ - // Name: "ETHASH", - // Flags: []cli.Flag{ - // utils.EthashCacheDirFlag, - // utils.EthashCachesInMemoryFlag, - // utils.EthashCachesOnDiskFlag, - // utils.EthashDatasetDirFlag, - // utils.EthashDatasetsInMemoryFlag, - // utils.EthashDatasetsOnDiskFlag, - // }, - //}, - //{ - // Name: "DASHBOARD", - // Flags: []cli.Flag{ - // utils.DashboardEnabledFlag, - // utils.DashboardAddrFlag, - // utils.DashboardPortFlag, - // utils.DashboardRefreshFlag, - // utils.DashboardAssetsFlag, - // }, - //}, - //{ - // Name: "TRANSACTION POOL", - // Flags: []cli.Flag{ - // utils.TxPoolNoLocalsFlag, - // utils.TxPoolJournalFlag, - // utils.TxPoolRejournalFlag, - // utils.TxPoolPriceLimitFlag, - // utils.TxPoolPriceBumpFlag, - // utils.TxPoolAccountSlotsFlag, - // utils.TxPoolGlobalSlotsFlag, - // utils.TxPoolAccountQueueFlag, - // utils.TxPoolGlobalQueueFlag, - // utils.TxPoolLifetimeFlag, - // }, - //}, - { - Name: "PERFORMANCE TUNING", - Flags: []cli.Flag{ - utils.CacheFlag, - utils.CacheDatabaseFlag, - // utils.CacheGCFlag, - // utils.TrieCacheGenFlag, - utils.FDLimitFlag, - }, - }, - { - Name: "ACCOUNT", - Flags: []cli.Flag{ - utils.UnlockedAccountFlag, - utils.PasswordFileFlag, - }, - }, - { - Name: "API AND CONSOLE", - Flags: []cli.Flag{ - utils.RPCEnabledFlag, - utils.RPCGlobalGasCapFlag, - utils.RPCListenAddrFlag, - utils.RPCPortFlag, - utils.RPCHttpReadTimeoutFlag, - utils.RPCHttpWriteTimeoutFlag, - utils.RPCHttpIdleTimeoutFlag, - utils.RPCApiFlag, - utils.WSEnabledFlag, - utils.WSListenAddrFlag, - utils.WSPortFlag, - utils.WSApiFlag, - utils.WSAllowedOriginsFlag, - utils.IPCDisabledFlag, - utils.IPCPathFlag, - utils.RPCCORSDomainFlag, - utils.RPCVirtualHostsFlag, - utils.RPCGlobalTxFeeCap, - utils.JSpathFlag, - utils.ExecFlag, - utils.PreloadJSFlag, - }, - }, - { - Name: "NETWORKING", - Flags: []cli.Flag{ - utils.BootnodesFlag, - utils.BootnodesV4Flag, - utils.BootnodesV5Flag, - utils.ListenPortFlag, - utils.MaxPeersFlag, - utils.MaxPendingPeersFlag, - utils.NATFlag, - utils.NoDiscoverFlag, - //utils.DiscoveryV5Flag, - //utils.NetrestrictFlag, - utils.NodeKeyFileFlag, - utils.NodeKeyHexFlag, - }, - }, - { - Name: "STAKER", - Flags: []cli.Flag{ - utils.StakingEnabledFlag, - utils.StakerThreadsFlag, - utils.EtherbaseFlag, - utils.TargetGasLimitFlag, - utils.GasPriceFlag, - utils.ExtraDataFlag, - }, - }, - //{ - // Name: "GAS PRICE ORACLE", - // Flags: []cli.Flag{ - // utils.GpoBlocksFlag, - // utils.GpoPercentileFlag, - // utils.GpoMaxGasPriceFlag, - // utils.GpoIgnoreGasPriceFlag, - // }, - //}, - //{ - // Name: "VIRTUAL MACHINE", - // Flags: []cli.Flag{ - // utils.VMEnableDebugFlag, - // }, - //}, - { - Name: "LOGGING AND DEBUGGING", - Flags: append([]cli.Flag{ - utils.MetricsEnabledFlag, - //utils.FakePoWFlag, - //utils.NoCompactionFlag, - }, debug.Flags...), - }, - { - Name: "DEPRECATED", - Flags: []cli.Flag{ - utils.FastSyncFlag, - utils.LightModeFlag, - }, - }, - { - Name: "MISC", - }, -} - -// byCategory sorts an array of flagGroup by Name in the order -// defined in AppHelpFlagGroups. -type byCategory []flagGroup - -func (a byCategory) Len() int { return len(a) } -func (a byCategory) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a byCategory) Less(i, j int) bool { - iCat, jCat := a[i].Name, a[j].Name - iIdx, jIdx := len(AppHelpFlagGroups), len(AppHelpFlagGroups) // ensure non categorized flags come last - - for i, group := range AppHelpFlagGroups { - if iCat == group.Name { - iIdx = i - } - if jCat == group.Name { - jIdx = i - } - } - - return iIdx < jIdx -} - -func flagCategory(flag cli.Flag) string { - for _, category := range AppHelpFlagGroups { - for _, flg := range category.Flags { - if flg.GetName() == flag.GetName() { - return category.Name - } - } - } - return "MISC" -} - -func init() { - // Override the default app help template - cli.AppHelpTemplate = AppHelpTemplate - - // Define a one shot struct to pass to the usage template - type helpData struct { - App interface{} - FlagGroups []flagGroup - } - - // Override the default app help printer, but only for the global app help - originalHelpPrinter := cli.HelpPrinter - cli.HelpPrinter = func(w io.Writer, tmpl string, data interface{}) { - if tmpl == AppHelpTemplate { - // Iterate over all the flags and add any uncategorized ones - categorized := make(map[string]struct{}) - for _, group := range AppHelpFlagGroups { - for _, flag := range group.Flags { - categorized[flag.String()] = struct{}{} - } - } - uncategorized := []cli.Flag{} - for _, flag := range data.(*cli.App).Flags { - if _, ok := categorized[flag.String()]; !ok { - uncategorized = append(uncategorized, flag) - } - } - if len(uncategorized) > 0 { - // Append all ungategorized options to the misc group - miscs := len(AppHelpFlagGroups[len(AppHelpFlagGroups)-1].Flags) - AppHelpFlagGroups[len(AppHelpFlagGroups)-1].Flags = append(AppHelpFlagGroups[len(AppHelpFlagGroups)-1].Flags, uncategorized...) - - // Make sure they are removed afterwards - defer func() { - AppHelpFlagGroups[len(AppHelpFlagGroups)-1].Flags = AppHelpFlagGroups[len(AppHelpFlagGroups)-1].Flags[:miscs] - }() - } - // Render out custom usage screen - originalHelpPrinter(w, tmpl, helpData{data, AppHelpFlagGroups}) - } else if tmpl == utils.CommandHelpTemplate { - // Iterate over all command specific flags and categorize them - categorized := make(map[string][]cli.Flag) - for _, flag := range data.(cli.Command).Flags { - if _, ok := categorized[flag.String()]; !ok { - categorized[flagCategory(flag)] = append(categorized[flagCategory(flag)], flag) - } - } - - // sort to get a stable ordering - sorted := make([]flagGroup, 0, len(categorized)) - for cat, flgs := range categorized { - sorted = append(sorted, flagGroup{cat, flgs}) - } - sort.Sort(byCategory(sorted)) - - // add sorted array to data and render with default printer - originalHelpPrinter(w, tmpl, map[string]interface{}{ - "cmd": data, - "categorizedFlags": sorted, - }) - } else { - originalHelpPrinter(w, tmpl, data) - } - } -} diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index a8c3ebfc8cf9..9a19efe96fca 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -18,50 +18,101 @@ package main import ( "encoding/json" - "flag" "fmt" "os" "strings" "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" "github.com/XinFinOrg/XDPoSChain/common/compiler" + "github.com/XinFinOrg/XDPoSChain/internal/flags" + "github.com/XinFinOrg/XDPoSChain/log" + "github.com/urfave/cli/v2" ) var ( - abiFlag = flag.String("abi", "", "Path to the Ethereum contract ABI json to bind") - binFlag = flag.String("bin", "", "Path to the Ethereum contract bytecode (generate deploy method)") - typFlag = flag.String("type", "", "Struct name for the binding (default = package name)") + // Git SHA1 commit hash of the release (set via linker flags) + gitCommit = "" - solFlag = flag.String("sol", "", "Path to the Ethereum contract Solidity source to build and bind") - solcFlag = flag.String("solc", "solc", "Solidity compiler to use if source builds are requested") - excFlag = flag.String("exc", "", "Comma separated types to exclude from binding") + app *cli.App +) - pkgFlag = flag.String("pkg", "", "Package name to generate the binding into") - outFlag = flag.String("out", "", "Output file for the generated binding (default = stdout)") - langFlag = flag.String("lang", "go", "Destination language for the bindings (go)") +var ( + // Flags needed by abigen + abiFlag = &cli.StringFlag{ + Name: "abi", + Usage: "Path to the Ethereum contract ABI json to bind", + } + binFlag = &cli.StringFlag{ + Name: "bin", + Usage: "Path to the Ethereum contract bytecode (generate deploy method)", + } + typeFlag = &cli.StringFlag{ + Name: "type", + Usage: "Struct name for the binding (default = package name)", + } + solFlag = &cli.StringFlag{ + Name: "sol", + Usage: "Path to the Ethereum contract Solidity source to build and bind", + } + solcFlag = &cli.StringFlag{ + Name: "solc", + Usage: "Solidity compiler to use if source builds are requested", + Value: "solc", + } + excFlag = &cli.StringFlag{ + Name: "exc", + Usage: "Comma separated types to exclude from binding", + } + pkgFlag = &cli.StringFlag{ + Name: "pkg", + Usage: "Package name to generate the binding into", + } + outFlag = &cli.StringFlag{ + Name: "out", + Usage: "Output file for the generated binding (default = stdout)", + } + langFlag = &cli.StringFlag{ + Name: "lang", + Usage: "Destination language for the bindings (go)", + Value: "go", + } ) -func main() { - // Parse and ensure all needed inputs are specified - flag.Parse() +func init() { + app = flags.NewApp(gitCommit, "ethereum checkpoint helper tool") + app.Name = "abigen" + app.Flags = []cli.Flag{ + abiFlag, + binFlag, + typeFlag, + solFlag, + solcFlag, + excFlag, + pkgFlag, + outFlag, + langFlag, + } + app.Action = abigen +} - if *abiFlag == "" && *solFlag == "" { +func abigen(c *cli.Context) error { + if c.String(abiFlag.Name) == "" && c.String(solFlag.Name) == "" { fmt.Printf("No contract ABI (--abi) or Solidity source (--sol) specified\n") os.Exit(-1) - } else if (*abiFlag != "" || *binFlag != "" || *typFlag != "") && *solFlag != "" { + } else if (c.String(abiFlag.Name) != "" || c.String(binFlag.Name) != "" || c.String(typeFlag.Name) != "") && c.String(solFlag.Name) != "" { fmt.Printf("Contract ABI (--abi), bytecode (--bin) and type (--type) flags are mutually exclusive with the Solidity source (--sol) flag\n") os.Exit(-1) } - if *pkgFlag == "" { + if c.String(pkgFlag.Name) == "" { fmt.Printf("No destination package specified (--pkg)\n") os.Exit(-1) } var lang bind.Lang - switch *langFlag { + switch c.String(langFlag.Name) { case "go": lang = bind.LangGo default: - fmt.Printf("Unsupported destination language \"%s\" (--lang)\n", *langFlag) + fmt.Printf("Unsupported destination language \"%s\" (--lang)\n", c.String(langFlag.Name)) os.Exit(-1) } // If the entire solidity code was specified, build and bind based on that @@ -70,13 +121,13 @@ func main() { bins []string types []string ) - if *solFlag != "" { + if c.String(solFlag.Name) != "" { // Generate the list of types to exclude from binding exclude := make(map[string]bool) - for _, kind := range strings.Split(*excFlag, ",") { + for _, kind := range strings.Split(c.String(excFlag.Name), ",") { exclude[strings.ToLower(kind)] = true } - contracts, err := compiler.CompileSolidity(*solcFlag, *solFlag) + contracts, err := compiler.CompileSolidity(c.String(solcFlag.Name), c.String(solFlag.Name)) if err != nil { fmt.Printf("Failed to build Solidity contract: %v\n", err) os.Exit(-1) @@ -95,7 +146,7 @@ func main() { } } else { // Otherwise load up the ABI, optional bytecode and type name from the parameters - abi, err := os.ReadFile(*abiFlag) + abi, err := os.ReadFile(c.String(abiFlag.Name)) if err != nil { fmt.Printf("Failed to read input ABI: %v\n", err) os.Exit(-1) @@ -103,33 +154,43 @@ func main() { abis = append(abis, string(abi)) bin := []byte{} - if *binFlag != "" { - if bin, err = os.ReadFile(*binFlag); err != nil { + if c.String(binFlag.Name) != "" { + if bin, err = os.ReadFile(c.String(binFlag.Name)); err != nil { fmt.Printf("Failed to read input bytecode: %v\n", err) os.Exit(-1) } } bins = append(bins, string(bin)) - kind := *typFlag + kind := c.String(typeFlag.Name) if kind == "" { - kind = *pkgFlag + kind = c.String(pkgFlag.Name) } types = append(types, kind) } // Generate the contract binding - code, err := bind.Bind(types, abis, bins, *pkgFlag, lang) + code, err := bind.Bind(types, abis, bins, c.String(pkgFlag.Name), lang) if err != nil { fmt.Printf("Failed to generate ABI binding: %v\n", err) os.Exit(-1) } // Either flush it out to a file or display on the standard output - if *outFlag == "" { + if c.String(outFlag.Name) == "" { fmt.Printf("%s\n", code) - return + return nil } - if err := os.WriteFile(*outFlag, []byte(code), 0600); err != nil { + if err := os.WriteFile(c.String(outFlag.Name), []byte(code), 0600); err != nil { fmt.Printf("Failed to write ABI binding: %v\n", err) os.Exit(-1) } + return nil +} + +func main() { + log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true))) + + if err := app.Run(os.Args); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } } diff --git a/cmd/ethkey/generate.go b/cmd/ethkey/generate.go index 2f61c7a8f108..6da3f8beb6af 100644 --- a/cmd/ethkey/generate.go +++ b/cmd/ethkey/generate.go @@ -26,7 +26,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/cmd/utils" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/pborman/uuid" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli/v2" ) type outputGenerate struct { @@ -34,7 +34,14 @@ type outputGenerate struct { AddressEIP55 string } -var commandGenerate = cli.Command{ +var ( + privateKeyFlag = &cli.StringFlag{ + Name: "privatekey", + Usage: "file containing a raw private key to encrypt", + } +) + +var commandGenerate = &cli.Command{ Name: "generate", Usage: "generate new keyfile", ArgsUsage: "[ ]", @@ -47,10 +54,7 @@ If you want to encrypt an existing private key, it can be specified by setting Flags: []cli.Flag{ passphraseFlag, jsonFlag, - cli.StringFlag{ - Name: "privatekey", - Usage: "file containing a raw private key to encrypt", - }, + privateKeyFlag, }, Action: func(ctx *cli.Context) error { // Check if keyfile path given and make sure it doesn't already exist. diff --git a/cmd/ethkey/inspect.go b/cmd/ethkey/inspect.go index efd6b4589dcf..f8baa953c9eb 100644 --- a/cmd/ethkey/inspect.go +++ b/cmd/ethkey/inspect.go @@ -24,7 +24,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/accounts/keystore" "github.com/XinFinOrg/XDPoSChain/cmd/utils" "github.com/XinFinOrg/XDPoSChain/crypto" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli/v2" ) type outputInspect struct { @@ -33,7 +33,14 @@ type outputInspect struct { PrivateKey string } -var commandInspect = cli.Command{ +var ( + privateFlag = &cli.BoolFlag{ + Name: "private", + Usage: "include the private key in the output", + } +) + +var commandInspect = &cli.Command{ Name: "inspect", Usage: "inspect a keyfile", ArgsUsage: "", @@ -45,10 +52,7 @@ make sure to use this feature with great caution!`, Flags: []cli.Flag{ passphraseFlag, jsonFlag, - cli.BoolFlag{ - Name: "private", - Usage: "include the private key in the output", - }, + privateFlag, }, Action: func(ctx *cli.Context) error { keyfilepath := ctx.Args().First() diff --git a/cmd/ethkey/main.go b/cmd/ethkey/main.go index 323bb9e1aba6..f1840227a364 100644 --- a/cmd/ethkey/main.go +++ b/cmd/ethkey/main.go @@ -20,8 +20,8 @@ import ( "fmt" "os" - "github.com/XinFinOrg/XDPoSChain/cmd/utils" - "gopkg.in/urfave/cli.v1" + "github.com/XinFinOrg/XDPoSChain/internal/flags" + "github.com/urfave/cli/v2" ) const ( @@ -34,8 +34,8 @@ var gitCommit = "" var app *cli.App func init() { - app = utils.NewApp(gitCommit, "an Ethereum key manager") - app.Commands = []cli.Command{ + app = flags.NewApp(gitCommit, "an Ethereum key manager") + app.Commands = []*cli.Command{ commandGenerate, commandInspect, commandSignMessage, @@ -45,15 +45,15 @@ func init() { // Commonly used command line flags. var ( - passphraseFlag = cli.StringFlag{ + passphraseFlag = &cli.StringFlag{ Name: "passwordfile", - Usage: "the file that contains the passphrase for the keyfile", + Usage: "the file that contains the password for the keyfile", } - jsonFlag = cli.BoolFlag{ + jsonFlag = &cli.BoolFlag{ Name: "json", Usage: "output JSON instead of human-readable format", } - messageFlag = cli.StringFlag{ + messageFlag = &cli.StringFlag{ Name: "message", Usage: "the file that contains the message to sign/verify", } diff --git a/cmd/ethkey/message.go b/cmd/ethkey/message.go index 9442785f6882..3566475c7cda 100644 --- a/cmd/ethkey/message.go +++ b/cmd/ethkey/message.go @@ -25,19 +25,19 @@ import ( "github.com/XinFinOrg/XDPoSChain/cmd/utils" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/crypto" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli/v2" ) type outputSign struct { Signature string } -var msgfileFlag = cli.StringFlag{ +var msgfileFlag = &cli.StringFlag{ Name: "msgfile", Usage: "file containing the message to sign/verify", } -var commandSignMessage = cli.Command{ +var commandSignMessage = &cli.Command{ Name: "signmessage", Usage: "sign a message", ArgsUsage: " ", @@ -88,7 +88,7 @@ type outputVerify struct { RecoveredPublicKey string } -var commandVerifyMessage = cli.Command{ +var commandVerifyMessage = &cli.Command{ Name: "verifymessage", Usage: "verify the signature of a signed message", ArgsUsage: "
", @@ -143,7 +143,7 @@ It is possible to refer to a file containing the message.`, func getMessage(ctx *cli.Context, msgarg int) []byte { if file := ctx.String("msgfile"); file != "" { - if len(ctx.Args()) > msgarg { + if ctx.NArg() > msgarg { utils.Fatalf("Can't use --msgfile and message argument at the same time.") } msg, err := os.ReadFile(file) @@ -151,9 +151,9 @@ func getMessage(ctx *cli.Context, msgarg int) []byte { utils.Fatalf("Can't read message file: %v", err) } return msg - } else if len(ctx.Args()) == msgarg+1 { + } else if ctx.NArg() == msgarg+1 { return []byte(ctx.Args().Get(msgarg)) } - utils.Fatalf("Invalid number of arguments: want %d, got %d", msgarg+1, len(ctx.Args())) + utils.Fatalf("Invalid number of arguments: want %d, got %d", msgarg+1, ctx.NArg()) return nil } diff --git a/cmd/ethkey/utils.go b/cmd/ethkey/utils.go index cd32c5d80104..6f1b681ef03b 100644 --- a/cmd/ethkey/utils.go +++ b/cmd/ethkey/utils.go @@ -25,7 +25,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/cmd/utils" "github.com/XinFinOrg/XDPoSChain/console" "github.com/XinFinOrg/XDPoSChain/crypto" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli/v2" ) // getPassPhrase obtains a passphrase given by the user. It first checks the diff --git a/cmd/evm/compiler.go b/cmd/evm/compiler.go index 0206fed0efdd..b2f45e2f911f 100644 --- a/cmd/evm/compiler.go +++ b/cmd/evm/compiler.go @@ -22,11 +22,10 @@ import ( "os" "github.com/XinFinOrg/XDPoSChain/cmd/evm/internal/compiler" - - cli "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli/v2" ) -var compileCommand = cli.Command{ +var compileCommand = &cli.Command{ Action: compileCmd, Name: "compile", Usage: "compiles easm source to evm binary", @@ -34,7 +33,7 @@ var compileCommand = cli.Command{ } func compileCmd(ctx *cli.Context) error { - debug := ctx.GlobalBool(DebugFlag.Name) + debug := ctx.Bool(DebugFlag.Name) if len(ctx.Args().First()) == 0 { return errors.New("filename required") diff --git a/cmd/evm/disasm.go b/cmd/evm/disasm.go index 72ca7683d676..2e09f5aab693 100644 --- a/cmd/evm/disasm.go +++ b/cmd/evm/disasm.go @@ -23,10 +23,10 @@ import ( "strings" "github.com/XinFinOrg/XDPoSChain/core/asm" - cli "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli/v2" ) -var disasmCommand = cli.Command{ +var disasmCommand = &cli.Command{ Action: disasmCmd, Name: "disasm", Usage: "disassembles evm binary", @@ -34,17 +34,22 @@ var disasmCommand = cli.Command{ } func disasmCmd(ctx *cli.Context) error { - if len(ctx.Args().First()) == 0 { - return errors.New("filename required") + var in string + switch { + case len(ctx.Args().First()) > 0: + fn := ctx.Args().First() + input, err := os.ReadFile(fn) + if err != nil { + return err + } + in = string(input) + case ctx.IsSet(InputFlag.Name): + in = ctx.String(InputFlag.Name) + default: + return errors.New("missing filename or --input value") } - fn := ctx.Args().First() - in, err := os.ReadFile(fn) - if err != nil { - return err - } - - code := strings.TrimSpace(string(in[:])) + code := strings.TrimSpace(in) fmt.Printf("%v\n", code) return asm.PrintDisassembled(code) } diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 18de3710d2c5..0ac8e896e2bd 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -22,101 +22,126 @@ import ( "math/big" "os" - "github.com/XinFinOrg/XDPoSChain/cmd/utils" - "gopkg.in/urfave/cli.v1" + "github.com/XinFinOrg/XDPoSChain/internal/flags" + "github.com/urfave/cli/v2" ) -var gitCommit = "" // Git SHA1 commit hash of the release (set via linker flags) - var ( - app = utils.NewApp(gitCommit, "the evm command line interface") + gitCommit = "" // Git SHA1 commit hash of the release (set via linker flags) - DebugFlag = cli.BoolFlag{ - Name: "debug", - Usage: "output full trace logs", - } - MemProfileFlag = cli.StringFlag{ - Name: "memprofile", - Usage: "creates a memory profile at the given path", - } - CPUProfileFlag = cli.StringFlag{ - Name: "cpuprofile", - Usage: "creates a CPU profile at the given path", - } - StatDumpFlag = cli.BoolFlag{ - Name: "statdump", - Usage: "displays stack and heap memory information", - } - CodeFlag = cli.StringFlag{ - Name: "code", - Usage: "EVM code", - } - CodeFileFlag = cli.StringFlag{ - Name: "codefile", - Usage: "File containing EVM code. If '-' is specified, code is read from stdin ", - } - GasFlag = cli.Uint64Flag{ - Name: "gas", - Usage: "gas limit for the evm", - Value: 10000000000, - } - PriceFlag = utils.BigFlag{ - Name: "price", - Usage: "price set for the evm", - Value: new(big.Int), - } - ValueFlag = utils.BigFlag{ - Name: "value", - Usage: "value set for the evm", - Value: new(big.Int), - } - DumpFlag = cli.BoolFlag{ - Name: "dump", - Usage: "dumps the state after the run", - } - InputFlag = cli.StringFlag{ - Name: "input", - Usage: "input for the EVM", - } - VerbosityFlag = cli.IntFlag{ - Name: "verbosity", - Usage: "sets the verbosity level", - } - CreateFlag = cli.BoolFlag{ - Name: "create", - Usage: "indicates the action should be create rather than call", - } - GenesisFlag = cli.StringFlag{ - Name: "prestate", - Usage: "JSON file with prestate (genesis) config", - } - MachineFlag = cli.BoolFlag{ - Name: "json", - Usage: "output trace logs in machine readable format (json)", - } - SenderFlag = cli.StringFlag{ - Name: "sender", - Usage: "The transaction origin", - } - ReceiverFlag = cli.StringFlag{ - Name: "receiver", - Usage: "The transaction receiver (execution context)", - } - DisableMemoryFlag = cli.BoolFlag{ - Name: "nomemory", - Usage: "disable memory output", - } - DisableStackFlag = cli.BoolFlag{ - Name: "nostack", - Usage: "disable stack output", + app = flags.NewApp(gitCommit, "the evm command line interface") +) + +var ( + DebugFlag = &cli.BoolFlag{ + Name: "debug", + Usage: "output full trace logs", + Category: flags.VMCategory, + } + MemProfileFlag = &cli.StringFlag{ + Name: "memprofile", + Usage: "creates a memory profile at the given path", + Category: flags.VMCategory, + } + CPUProfileFlag = &cli.StringFlag{ + Name: "cpuprofile", + Usage: "creates a CPU profile at the given path", + Category: flags.VMCategory, + } + StatDumpFlag = &cli.BoolFlag{ + Name: "statdump", + Usage: "displays stack and heap memory information", + Category: flags.VMCategory, + } + CodeFlag = &cli.StringFlag{ + Name: "code", + Usage: "EVM code", + Category: flags.VMCategory, + } + CodeFileFlag = &cli.StringFlag{ + Name: "codefile", + Usage: "File containing EVM code. If '-' is specified, code is read from stdin ", + Category: flags.VMCategory, + } + GasFlag = &cli.Uint64Flag{ + Name: "gas", + Usage: "gas limit for the evm", + Value: 10000000000, + Category: flags.VMCategory, + } + PriceFlag = &flags.BigFlag{ + Name: "price", + Usage: "price set for the evm", + Value: new(big.Int), + Category: flags.VMCategory, + } + ValueFlag = &flags.BigFlag{ + Name: "value", + Usage: "value set for the evm", + Value: new(big.Int), + Category: flags.VMCategory, + } + DumpFlag = &cli.BoolFlag{ + Name: "dump", + Usage: "dumps the state after the run", + Category: flags.VMCategory, + } + InputFlag = &cli.StringFlag{ + Name: "input", + Usage: "input for the EVM", + Category: flags.VMCategory, + } + VerbosityFlag = &cli.IntFlag{ + Name: "verbosity", + Usage: "sets the verbosity level", + Category: flags.VMCategory, + } + CreateFlag = &cli.BoolFlag{ + Name: "create", + Usage: "indicates the action should be create rather than call", + Category: flags.VMCategory, + } + GenesisFlag = &cli.StringFlag{ + Name: "prestate", + Usage: "JSON file with prestate (genesis) config", + Category: flags.VMCategory, + } + MachineFlag = &cli.BoolFlag{ + Name: "json", + Usage: "output trace logs in machine readable format (json)", + Category: flags.VMCategory, + } + SenderFlag = &cli.StringFlag{ + Name: "sender", + Usage: "The transaction origin", + Category: flags.VMCategory, + } + ReceiverFlag = &cli.StringFlag{ + Name: "receiver", + Usage: "The transaction receiver (execution context)", + Category: flags.VMCategory, + } + DisableMemoryFlag = &cli.BoolFlag{ + Name: "nomemory", + Value: true, + Usage: "disable memory output", + Category: flags.VMCategory, + } + DisableStackFlag = &cli.BoolFlag{ + Name: "nostack", + Usage: "disable stack output", + Category: flags.VMCategory, } DisableStorageFlag = &cli.BoolFlag{ Name: "nostorage", Usage: "disable storage output", + Category: flags.VMCategory, } DisableReturnDataFlag = &cli.BoolFlag{ Name: "noreturndata", + Value: true, Usage: "enable return data output", + Category: flags.VMCategory, } ) @@ -142,7 +167,7 @@ func init() { DisableMemoryFlag, DisableStackFlag, } - app.Commands = []cli.Command{ + app.Commands = []*cli.Command{ compileCommand, disasmCommand, runCommand, diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index cc71f4214ac8..05adeae2be9f 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -34,11 +34,12 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/core/vm/runtime" + "github.com/XinFinOrg/XDPoSChain/internal/flags" "github.com/XinFinOrg/XDPoSChain/params" - cli "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli/v2" ) -var runCommand = cli.Command{ +var runCommand = &cli.Command{ Action: runCmd, Name: "run", Usage: "run arbitrary evm binary", @@ -69,11 +70,11 @@ func readGenesis(genesisPath string) *core.Genesis { func runCmd(ctx *cli.Context) error { logconfig := &vm.LogConfig{ - EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name), - DisableStack: ctx.GlobalBool(DisableStackFlag.Name), - DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name), - EnableReturnData: !ctx.GlobalBool(DisableReturnDataFlag.Name), - Debug: ctx.GlobalBool(DebugFlag.Name), + EnableMemory: !ctx.Bool(DisableMemoryFlag.Name), + DisableStack: ctx.Bool(DisableStackFlag.Name), + DisableStorage: ctx.Bool(DisableStorageFlag.Name), + EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name), + Debug: ctx.Bool(DebugFlag.Name), } var ( @@ -84,17 +85,17 @@ func runCmd(ctx *cli.Context) error { sender = common.StringToAddress("sender") receiver = common.StringToAddress("receiver") ) - if ctx.GlobalBool(MachineFlag.Name) { + if ctx.Bool(MachineFlag.Name) { tracer = vm.NewJSONLogger(logconfig, os.Stdout) - } else if ctx.GlobalBool(DebugFlag.Name) { + } else if ctx.Bool(DebugFlag.Name) { debugLogger = vm.NewStructLogger(logconfig) tracer = debugLogger } else { debugLogger = vm.NewStructLogger(logconfig) } - if ctx.GlobalString(GenesisFlag.Name) != "" { - gen := readGenesis(ctx.GlobalString(GenesisFlag.Name)) + if ctx.String(GenesisFlag.Name) != "" { + gen := readGenesis(ctx.String(GenesisFlag.Name)) db := rawdb.NewMemoryDatabase() genesis := gen.ToBlock(db) statedb, _ = state.New(genesis.Root(), state.NewDatabase(db)) @@ -103,13 +104,13 @@ func runCmd(ctx *cli.Context) error { db := rawdb.NewMemoryDatabase() statedb, _ = state.New(common.Hash{}, state.NewDatabase(db)) } - if ctx.GlobalString(SenderFlag.Name) != "" { - sender = common.HexToAddress(ctx.GlobalString(SenderFlag.Name)) + if ctx.String(SenderFlag.Name) != "" { + sender = common.HexToAddress(ctx.String(SenderFlag.Name)) } statedb.CreateAccount(sender) - if ctx.GlobalString(ReceiverFlag.Name) != "" { - receiver = common.HexToAddress(ctx.GlobalString(ReceiverFlag.Name)) + if ctx.String(ReceiverFlag.Name) != "" { + receiver = common.HexToAddress(ctx.String(ReceiverFlag.Name)) } var ( @@ -118,11 +119,11 @@ func runCmd(ctx *cli.Context) error { err error ) // The '--code' or '--codefile' flag overrides code in state - if ctx.GlobalString(CodeFileFlag.Name) != "" { + if ctx.String(CodeFileFlag.Name) != "" { var hexcode []byte var err error // If - is specified, it means that code comes from stdin - if ctx.GlobalString(CodeFileFlag.Name) == "-" { + if ctx.String(CodeFileFlag.Name) == "-" { //Try reading from stdin if hexcode, err = io.ReadAll(os.Stdin); err != nil { fmt.Printf("Could not load code from stdin: %v\n", err) @@ -130,15 +131,15 @@ func runCmd(ctx *cli.Context) error { } } else { // Codefile with hex assembly - if hexcode, err = os.ReadFile(ctx.GlobalString(CodeFileFlag.Name)); err != nil { + if hexcode, err = os.ReadFile(ctx.String(CodeFileFlag.Name)); err != nil { fmt.Printf("Could not load code from file: %v\n", err) os.Exit(1) } } code = common.Hex2Bytes(string(bytes.TrimRight(hexcode, "\n"))) - } else if ctx.GlobalString(CodeFlag.Name) != "" { - code = common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)) + } else if ctx.String(CodeFlag.Name) != "" { + code = common.Hex2Bytes(ctx.String(CodeFlag.Name)) } else if fn := ctx.Args().First(); len(fn) > 0 { // EASM-file to compile src, err := os.ReadFile(fn) @@ -152,20 +153,20 @@ func runCmd(ctx *cli.Context) error { code = common.Hex2Bytes(bin) } - initialGas := ctx.GlobalUint64(GasFlag.Name) + initialGas := ctx.Uint64(GasFlag.Name) runtimeConfig := runtime.Config{ Origin: sender, State: statedb, GasLimit: initialGas, - GasPrice: utils.GlobalBig(ctx, PriceFlag.Name), - Value: utils.GlobalBig(ctx, ValueFlag.Name), + GasPrice: flags.GlobalBig(ctx, PriceFlag.Name), + Value: flags.GlobalBig(ctx, ValueFlag.Name), EVMConfig: vm.Config{ Tracer: tracer, - Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name), + Debug: ctx.Bool(DebugFlag.Name) || ctx.Bool(MachineFlag.Name), }, } - if cpuProfilePath := ctx.GlobalString(CPUProfileFlag.Name); cpuProfilePath != "" { + if cpuProfilePath := ctx.String(CPUProfileFlag.Name); cpuProfilePath != "" { f, err := os.Create(cpuProfilePath) if err != nil { fmt.Println("could not create CPU profile: ", err) @@ -183,23 +184,23 @@ func runCmd(ctx *cli.Context) error { } tstart := time.Now() var leftOverGas uint64 - if ctx.GlobalBool(CreateFlag.Name) { - input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...) + if ctx.Bool(CreateFlag.Name) { + input := append(code, common.Hex2Bytes(ctx.String(InputFlag.Name))...) ret, _, leftOverGas, err = runtime.Create(input, &runtimeConfig) } else { if len(code) > 0 { statedb.SetCode(receiver, code) } - ret, leftOverGas, err = runtime.Call(receiver, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtimeConfig) + ret, leftOverGas, err = runtime.Call(receiver, common.Hex2Bytes(ctx.String(InputFlag.Name)), &runtimeConfig) } execTime := time.Since(tstart) - if ctx.GlobalBool(DumpFlag.Name) { + if ctx.Bool(DumpFlag.Name) { statedb.IntermediateRoot(true) fmt.Println(string(statedb.Dump())) } - if memProfilePath := ctx.GlobalString(MemProfileFlag.Name); memProfilePath != "" { + if memProfilePath := ctx.String(MemProfileFlag.Name); memProfilePath != "" { f, err := os.Create(memProfilePath) if err != nil { fmt.Println("could not create memory profile: ", err) @@ -212,7 +213,7 @@ func runCmd(ctx *cli.Context) error { f.Close() } - if ctx.GlobalBool(DebugFlag.Name) { + if ctx.Bool(DebugFlag.Name) { if debugLogger != nil { fmt.Fprintln(os.Stderr, "#### TRACE ####") vm.WriteTrace(os.Stderr, debugLogger.StructLogs()) @@ -221,7 +222,7 @@ func runCmd(ctx *cli.Context) error { vm.WriteLogs(os.Stderr, statedb.Logs()) } - if ctx.GlobalBool(StatDumpFlag.Name) { + if ctx.Bool(StatDumpFlag.Name) { var mem goruntime.MemStats goruntime.ReadMemStats(&mem) fmt.Fprintf(os.Stderr, `evm execution time: %v diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go index 197a1c61c382..80dfdddf402c 100644 --- a/cmd/evm/staterunner.go +++ b/cmd/evm/staterunner.go @@ -25,10 +25,10 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/tests" - cli "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli/v2" ) -var stateTestCommand = cli.Command{ +var stateTestCommand = &cli.Command{ Action: stateTestCmd, Name: "statetest", Usage: "executes the given state tests", @@ -50,10 +50,10 @@ func stateTestCmd(ctx *cli.Context) error { // Configure the EVM logger config := &vm.LogConfig{ - EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name), - DisableStack: ctx.GlobalBool(DisableStackFlag.Name), - DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name), - EnableReturnData: !ctx.GlobalBool(DisableReturnDataFlag.Name), + EnableMemory: !ctx.Bool(DisableMemoryFlag.Name), + DisableStack: ctx.Bool(DisableStackFlag.Name), + DisableStorage: ctx.Bool(DisableStorageFlag.Name), + EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name), } var ( @@ -61,10 +61,10 @@ func stateTestCmd(ctx *cli.Context) error { debugger *vm.StructLogger ) switch { - case ctx.GlobalBool(MachineFlag.Name): + case ctx.Bool(MachineFlag.Name): tracer = vm.NewJSONLogger(config, os.Stderr) - case ctx.GlobalBool(DebugFlag.Name): + case ctx.Bool(DebugFlag.Name): debugger = vm.NewStructLogger(config) tracer = debugger @@ -83,7 +83,7 @@ func stateTestCmd(ctx *cli.Context) error { // Iterate over all the tests, run them and aggregate the results cfg := vm.Config{ Tracer: tracer, - Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name), + Debug: ctx.Bool(DebugFlag.Name) || ctx.Bool(MachineFlag.Name), } results := make([]StatetestResult, 0, len(tests)) for key, test := range tests { @@ -94,20 +94,20 @@ func stateTestCmd(ctx *cli.Context) error { if err != nil { // Test failed, mark as so and dump any state to aid debugging result.Pass, result.Error = false, err.Error() - if ctx.GlobalBool(DumpFlag.Name) && state != nil { + if ctx.Bool(DumpFlag.Name) && state != nil { dump := state.RawDump() result.State = &dump } } // print state root for evmlab tracing (already committed above, so no need to delete objects again - if ctx.GlobalBool(MachineFlag.Name) && state != nil { + if ctx.Bool(MachineFlag.Name) && state != nil { fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%x\"}\n", state.IntermediateRoot(false)) } results = append(results, *result) // Print any structured logs collected - if ctx.GlobalBool(DebugFlag.Name) { + if ctx.Bool(DebugFlag.Name) { if debugger != nil { fmt.Fprintln(os.Stderr, "#### TRACE ####") vm.WriteTrace(os.Stderr, debugger.StructLogs()) diff --git a/cmd/p2psim/main.go b/cmd/p2psim/main.go index 539b29cb7331..6536d6657df6 100644 --- a/cmd/p2psim/main.go +++ b/cmd/p2psim/main.go @@ -19,21 +19,20 @@ // Here is an example of creating a 2 node network with the first node // connected to the second: // -// $ p2psim node create -// Created node01 +// $ p2psim node create +// Created node01 // -// $ p2psim node start node01 -// Started node01 +// $ p2psim node start node01 +// Started node01 // -// $ p2psim node create -// Created node02 +// $ p2psim node create +// Created node02 // -// $ p2psim node start node02 -// Started node02 -// -// $ p2psim node connect node01 node02 -// Connected node01 to node02 +// $ p2psim node start node02 +// Started node02 // +// $ p2psim node connect node01 node02 +// Connected node01 to node02 package main import ( @@ -46,32 +45,77 @@ import ( "text/tabwriter" "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/internal/flags" "github.com/XinFinOrg/XDPoSChain/p2p" "github.com/XinFinOrg/XDPoSChain/p2p/discover" "github.com/XinFinOrg/XDPoSChain/p2p/simulations" "github.com/XinFinOrg/XDPoSChain/p2p/simulations/adapters" "github.com/XinFinOrg/XDPoSChain/rpc" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli/v2" ) var client *simulations.Client +var ( + // global command flags + apiFlag = &cli.StringFlag{ + Name: "api", + Value: "http://localhost:8888", + Usage: "simulation API URL", + EnvVars: []string{"P2PSIM_API_URL"}, + } + + // events subcommand flags + currentFlag = &cli.BoolFlag{ + Name: "current", + Usage: "get existing nodes and conns first", + } + filterFlag = &cli.StringFlag{ + Name: "filter", + Value: "", + Usage: "message filter", + } + + // node create subcommand flags + nameFlag = &cli.StringFlag{ + Name: "name", + Value: "", + Usage: "node name", + } + servicesFlag = &cli.StringFlag{ + Name: "services", + Value: "", + Usage: "node services (comma separated)", + } + keyFlag = &cli.StringFlag{ + Name: "key", + Value: "", + Usage: "node private key (hex encoded)", + } + + // node rpc subcommand flags + subscribeFlag = &cli.BoolFlag{ + Name: "subscribe", + Usage: "method is a subscription", + } +) + +var ( + // Git information set by linker when building with ci.go. + gitCommit string +) + func main() { - app := cli.NewApp() + app := flags.NewApp(gitCommit, "devp2p simulation command-line client") app.Usage = "devp2p simulation command-line client" app.Flags = []cli.Flag{ - cli.StringFlag{ - Name: "api", - Value: "http://localhost:8888", - Usage: "simulation API URL", - EnvVar: "P2PSIM_API_URL", - }, + apiFlag, } app.Before = func(ctx *cli.Context) error { - client = simulations.NewClient(ctx.GlobalString("api")) + client = simulations.NewClient(ctx.String("api")) return nil } - app.Commands = []cli.Command{ + app.Commands = []*cli.Command{ { Name: "show", Usage: "show network information", @@ -82,15 +126,8 @@ func main() { Usage: "stream network events", Action: streamNetwork, Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "current", - Usage: "get existing nodes and conns first", - }, - cli.StringFlag{ - Name: "filter", - Value: "", - Usage: "message filter", - }, + currentFlag, + filterFlag, }, }, { @@ -107,7 +144,7 @@ func main() { Name: "node", Usage: "manage simulation nodes", Action: listNodes, - Subcommands: []cli.Command{ + Subcommands: []*cli.Command{ { Name: "list", Usage: "list nodes", @@ -118,21 +155,9 @@ func main() { Usage: "create a node", Action: createNode, Flags: []cli.Flag{ - cli.StringFlag{ - Name: "name", - Value: "", - Usage: "node name", - }, - cli.StringFlag{ - Name: "services", - Value: "", - Usage: "node services (comma separated)", - }, - cli.StringFlag{ - Name: "key", - Value: "", - Usage: "node private key (hex encoded)", - }, + nameFlag, + servicesFlag, + keyFlag, }, }, { @@ -171,10 +196,7 @@ func main() { Usage: "call a node RPC method", Action: rpcNode, Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "subscribe", - Usage: "method is a subscription", - }, + subscribeFlag, }, }, }, @@ -184,7 +206,7 @@ func main() { } func showNetwork(ctx *cli.Context) error { - if len(ctx.Args()) != 0 { + if ctx.NArg() != 0 { return cli.ShowCommandHelp(ctx, ctx.Command.Name) } network, err := client.GetNetwork() @@ -199,7 +221,7 @@ func showNetwork(ctx *cli.Context) error { } func streamNetwork(ctx *cli.Context) error { - if len(ctx.Args()) != 0 { + if ctx.NArg() != 0 { return cli.ShowCommandHelp(ctx, ctx.Command.Name) } events := make(chan *simulations.Event) @@ -225,7 +247,7 @@ func streamNetwork(ctx *cli.Context) error { } func createSnapshot(ctx *cli.Context) error { - if len(ctx.Args()) != 0 { + if ctx.NArg() != 0 { return cli.ShowCommandHelp(ctx, ctx.Command.Name) } snap, err := client.CreateSnapshot() @@ -236,7 +258,7 @@ func createSnapshot(ctx *cli.Context) error { } func loadSnapshot(ctx *cli.Context) error { - if len(ctx.Args()) != 0 { + if ctx.NArg() != 0 { return cli.ShowCommandHelp(ctx, ctx.Command.Name) } snap := &simulations.Snapshot{} @@ -247,7 +269,7 @@ func loadSnapshot(ctx *cli.Context) error { } func listNodes(ctx *cli.Context) error { - if len(ctx.Args()) != 0 { + if ctx.NArg() != 0 { return cli.ShowCommandHelp(ctx, ctx.Command.Name) } nodes, err := client.GetNodes() @@ -272,7 +294,7 @@ func protocolList(node *p2p.NodeInfo) []string { } func createNode(ctx *cli.Context) error { - if len(ctx.Args()) != 0 { + if ctx.NArg() != 0 { return cli.ShowCommandHelp(ctx, ctx.Command.Name) } config := &adapters.NodeConfig{ @@ -298,11 +320,10 @@ func createNode(ctx *cli.Context) error { } func showNode(ctx *cli.Context) error { - args := ctx.Args() - if len(args) != 1 { + if ctx.NArg() != 1 { return cli.ShowCommandHelp(ctx, ctx.Command.Name) } - nodeName := args[0] + nodeName := ctx.Args().First() node, err := client.GetNode(nodeName) if err != nil { return err @@ -323,11 +344,10 @@ func showNode(ctx *cli.Context) error { } func startNode(ctx *cli.Context) error { - args := ctx.Args() - if len(args) != 1 { + if ctx.NArg() != 1 { return cli.ShowCommandHelp(ctx, ctx.Command.Name) } - nodeName := args[0] + nodeName := ctx.Args().First() if err := client.StartNode(nodeName); err != nil { return err } @@ -336,11 +356,10 @@ func startNode(ctx *cli.Context) error { } func stopNode(ctx *cli.Context) error { - args := ctx.Args() - if len(args) != 1 { + if ctx.NArg() != 1 { return cli.ShowCommandHelp(ctx, ctx.Command.Name) } - nodeName := args[0] + nodeName := ctx.Args().First() if err := client.StopNode(nodeName); err != nil { return err } @@ -349,12 +368,12 @@ func stopNode(ctx *cli.Context) error { } func connectNode(ctx *cli.Context) error { - args := ctx.Args() - if len(args) != 2 { + if ctx.NArg() != 2 { return cli.ShowCommandHelp(ctx, ctx.Command.Name) } - nodeName := args[0] - peerName := args[1] + args := ctx.Args() + nodeName := args.Get(0) + peerName := args.Get(1) if err := client.ConnectNode(nodeName, peerName); err != nil { return err } @@ -364,11 +383,11 @@ func connectNode(ctx *cli.Context) error { func disconnectNode(ctx *cli.Context) error { args := ctx.Args() - if len(args) != 2 { + if args.Len() != 2 { return cli.ShowCommandHelp(ctx, ctx.Command.Name) } - nodeName := args[0] - peerName := args[1] + nodeName := args.Get(0) + peerName := args.Get(1) if err := client.DisconnectNode(nodeName, peerName); err != nil { return err } @@ -378,21 +397,21 @@ func disconnectNode(ctx *cli.Context) error { func rpcNode(ctx *cli.Context) error { args := ctx.Args() - if len(args) < 2 { + if args.Len() < 2 { return cli.ShowCommandHelp(ctx, ctx.Command.Name) } - nodeName := args[0] - method := args[1] + nodeName := args.Get(0) + method := args.Get(1) rpcClient, err := client.RPCClient(context.Background(), nodeName) if err != nil { return err } if ctx.Bool("subscribe") { - return rpcSubscribe(rpcClient, ctx.App.Writer, method, args[3:]...) + return rpcSubscribe(rpcClient, ctx.App.Writer, method, args.Slice()[3:]...) } var result interface{} - params := make([]interface{}, len(args[3:])) - for i, v := range args[3:] { + params := make([]interface{}, len(args.Slice()[3:])) + for i, v := range args.Slice()[3:] { params[i] = v } if err := rpcClient.Call(&result, method, params...); err != nil { diff --git a/cmd/puppeth/puppeth.go b/cmd/puppeth/puppeth.go index 51a60e0cb5a6..6989f95f62d5 100644 --- a/cmd/puppeth/puppeth.go +++ b/cmd/puppeth/puppeth.go @@ -24,7 +24,7 @@ import ( "time" "github.com/XinFinOrg/XDPoSChain/log" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli/v2" ) // main is just a boring entry point to set up the CLI app. @@ -33,11 +33,11 @@ func main() { app.Name = "puppeth" app.Usage = "assemble and maintain private Ethereum networks" app.Flags = []cli.Flag{ - cli.StringFlag{ + &cli.StringFlag{ Name: "network", Usage: "name of the network to administer (no spaces or hyphens, please)", }, - cli.IntFlag{ + &cli.IntFlag{ Name: "loglevel", Value: 3, Usage: "log level to emit to the screen", diff --git a/cmd/utils/customflags.go b/cmd/utils/customflags.go deleted file mode 100644 index 2304678344f0..000000000000 --- a/cmd/utils/customflags.go +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . - -package utils - -import ( - "encoding" - "errors" - "flag" - "fmt" - "math/big" - "os" - "os/user" - "path" - "strings" - - "github.com/XinFinOrg/XDPoSChain/common/math" - "gopkg.in/urfave/cli.v1" -) - -// Custom type which is registered in the flags library which cli uses for -// argument parsing. This allows us to expand Value to an absolute path when -// the argument is parsed -type DirectoryString struct { - Value string -} - -func (ds *DirectoryString) String() string { - return ds.Value -} - -func (ds *DirectoryString) Set(value string) error { - ds.Value = expandPath(value) - return nil -} - -// Custom cli.Flag type which expand the received string to an absolute path. -// e.g. ~/.ethereum -> /home/username/.ethereum -type DirectoryFlag struct { - Name string - Value DirectoryString - Usage string -} - -func (df DirectoryFlag) String() string { - fmtString := "%s %v\t%v" - if len(df.Value.Value) > 0 { - fmtString = "%s \"%v\"\t%v" - } - return fmt.Sprintf(fmtString, prefixedNames(df.Name), df.Value.Value, df.Usage) -} - -func eachName(longName string, fn func(string)) { - parts := strings.Split(longName, ",") - for _, name := range parts { - name = strings.Trim(name, " ") - fn(name) - } -} - -// called by cli library, grabs variable from environment (if in env) -// and adds variable to flag set for parsing. -func (df DirectoryFlag) Apply(set *flag.FlagSet) { - eachName(df.Name, func(name string) { - set.Var(&df.Value, df.Name, df.Usage) - }) -} - -type TextMarshaler interface { - encoding.TextMarshaler - encoding.TextUnmarshaler -} - -// textMarshalerVal turns a TextMarshaler into a flag.Value -type textMarshalerVal struct { - v TextMarshaler -} - -func (t textMarshalerVal) String() string { - if t.v == nil { - return "" - } - text, _ := t.v.MarshalText() - return string(text) -} - -func (t textMarshalerVal) Set(s string) error { - return t.v.UnmarshalText([]byte(s)) -} - -// TextMarshalerFlag wraps a TextMarshaler value. -type TextMarshalerFlag struct { - Name string - Value TextMarshaler - Usage string -} - -func (t TextMarshalerFlag) GetName() string { - return t.Name -} - -func (t TextMarshalerFlag) String() string { - return fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(t.Name), t.Value, t.Usage) -} - -func (t TextMarshalerFlag) Apply(set *flag.FlagSet) { - eachName(t.Name, func(name string) { - set.Var(textMarshalerVal{t.Value}, t.Name, t.Usage) - }) -} - -// GlobalTextMarshaler returns the value of a TextMarshalerFlag from the global flag set. -func GlobalTextMarshaler(ctx *cli.Context, name string) TextMarshaler { - val := ctx.GlobalGeneric(name) - if val == nil { - return nil - } - return val.(textMarshalerVal).v -} - -// BigFlag is a command line flag that accepts 256 bit big integers in decimal or -// hexadecimal syntax. -type BigFlag struct { - Name string - Value *big.Int - Usage string -} - -// bigValue turns *big.Int into a flag.Value -type bigValue big.Int - -func (bv *bigValue) String() string { - if bv == nil { - return "" - } - return (*big.Int)(bv).String() -} - -func (bv *bigValue) Set(s string) error { - int, ok := math.ParseBig256(s) - if !ok { - return errors.New("invalid integer syntax") - } - *bv = (bigValue)(*int) - return nil -} - -func (bf BigFlag) GetName() string { - return bf.Name -} - -func (bf BigFlag) String() string { - fmtString := "%s %v\t%v" - if bf.Value != nil { - fmtString = "%s \"%v\"\t%v" - } - return fmt.Sprintf(fmtString, prefixedNames(bf.Name), bf.Value, bf.Usage) -} - -func (bf BigFlag) Apply(set *flag.FlagSet) { - eachName(bf.Name, func(name string) { - set.Var((*bigValue)(bf.Value), bf.Name, bf.Usage) - }) -} - -// GlobalBig returns the value of a BigFlag from the global flag set. -func GlobalBig(ctx *cli.Context, name string) *big.Int { - val := ctx.GlobalGeneric(name) - if val == nil { - return nil - } - return (*big.Int)(val.(*bigValue)) -} - -func prefixFor(name string) (prefix string) { - if len(name) == 1 { - prefix = "-" - } else { - prefix = "--" - } - - return -} - -func prefixedNames(fullName string) (prefixed string) { - parts := strings.Split(fullName, ",") - for i, name := range parts { - name = strings.Trim(name, " ") - prefixed += prefixFor(name) + name - if i < len(parts)-1 { - prefixed += ", " - } - } - return -} - -func (df DirectoryFlag) GetName() string { - return df.Name -} - -func (df *DirectoryFlag) Set(value string) { - df.Value.Value = value -} - -// Expands a file path -// 1. replace tilde with users home dir -// 2. expands embedded environment variables -// 3. cleans the path, e.g. /a/b/../c -> /a/c -// Note, it has limitations, e.g. ~someuser/tmp will not be expanded -func expandPath(p string) string { - if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") { - if home := homeDir(); home != "" { - p = home + p[1:] - } - } - return path.Clean(os.ExpandEnv(p)) -} - -func homeDir() string { - if home := os.Getenv("HOME"); home != "" { - return home - } - if usr, err := user.Current(); err == nil { - return usr.HomeDir - } - return "" -} diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 700695516d90..db42606da594 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -47,6 +47,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/eth/gasprice" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/internal/ethapi" + "github.com/XinFinOrg/XDPoSChain/internal/flags" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/metrics" "github.com/XinFinOrg/XDPoSChain/metrics/exp" @@ -59,54 +60,9 @@ import ( "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rpc" gopsutil "github.com/shirou/gopsutil/mem" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli/v2" ) -var ( - CommandHelpTemplate = `{{.cmd.Name}}{{if .cmd.Subcommands}} command{{end}}{{if .cmd.Flags}} [command options]{{end}} [arguments...] -{{if .cmd.Description}}{{.cmd.Description}} -{{end}}{{if .cmd.Subcommands}} -SUBCOMMANDS: - {{range .cmd.Subcommands}}{{.cmd.Name}}{{with .cmd.ShortName}}, {{.cmd}}{{end}}{{ "\t" }}{{.cmd.Usage}} - {{end}}{{end}}{{if .categorizedFlags}} -{{range $idx, $categorized := .categorizedFlags}}{{$categorized.Name}} OPTIONS: -{{range $categorized.Flags}}{{"\t"}}{{.}} -{{end}} -{{end}}{{end}}` -) - -func init() { - cli.AppHelpTemplate = `{{.Name}} {{if .Flags}}[global options] {{end}}command{{if .Flags}} [command options]{{end}} [arguments...] - -VERSION: - {{.Version}} - -COMMANDS: - {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} - {{end}}{{if .Flags}} -GLOBAL OPTIONS: - {{range .Flags}}{{.}} - {{end}}{{end}} -` - - cli.CommandHelpTemplate = CommandHelpTemplate -} - -// NewApp creates an app with sane defaults. -func NewApp(gitCommit, usage string) *cli.App { - app := cli.NewApp() - app.Name = filepath.Base(os.Args[0]) - app.Author = "" - //app.Authors = nil - app.Email = "" - app.Version = params.Version - if len(gitCommit) >= 8 { - app.Version += "-" + gitCommit[:8] - } - app.Usage = usage - return app -} - // These are all the command line flags we support. // If you add to this list, please remember to include the // flag in the appropriate command definition. @@ -115,530 +71,644 @@ func NewApp(gitCommit, usage string) *cli.App { // are the same for all commands. var ( - // XDC flags. - RollbackFlag = cli.StringFlag{ - Name: "rollback", - Usage: "Rollback chain at hash", - Value: "", - } - Enable0xPrefixFlag = cli.BoolFlag{ - Name: "enable-0x-prefix", - Usage: "Addres use 0x-prefix (Deprecated: this is on by default, to use xdc prefix use --enable-xdc-prefix)", - } - EnableXDCPrefixFlag = cli.BoolFlag{ - Name: "enable-xdc-prefix", - Usage: "Addres use xdc-prefix (default = false)", - } // General settings - AnnounceTxsFlag = cli.BoolFlag{ - Name: "announce-txs", - Usage: "Always commit transactions", - } - StoreRewardFlag = cli.BoolFlag{ - Name: "store-reward", - Usage: "Store reward to file", - } - DataDirFlag = DirectoryFlag{ - Name: "datadir", - Usage: "Data directory for the databases and keystore", - Value: DirectoryString{node.DefaultDataDir()}, - } - KeyStoreDirFlag = DirectoryFlag{ - Name: "keystore", - Usage: "Directory for the keystore (default = inside the datadir)", - } - NoUSBFlag = cli.BoolFlag{ - Name: "nousb", - Usage: "Disables monitoring for and managing USB hardware wallets", - } - NetworkIdFlag = cli.Uint64Flag{ - Name: "networkid", - Usage: "Network identifier (integer, 89=XDPoSChain)", - Value: ethconfig.Defaults.NetworkId, - } - TestnetFlag = cli.BoolFlag{ - Name: "testnet", - Usage: "Ropsten network: pre-configured proof-of-work test network", - } - XDCTestnetFlag = cli.BoolFlag{ - Name: "apothem", - Usage: "XDC Apothem Network", - } - RinkebyFlag = cli.BoolFlag{ - Name: "rinkeby", - Usage: "Rinkeby network: pre-configured proof-of-authority test network", - } - DeveloperFlag = cli.BoolFlag{ - Name: "dev", - Usage: "Ephemeral proof-of-authority network with a pre-funded developer account, mining enabled", - } - DeveloperPeriodFlag = cli.IntFlag{ - Name: "dev.period", - Usage: "Block period to use in developer mode (0 = mine only if transaction pending)", - } - IdentityFlag = cli.StringFlag{ - Name: "identity", - Usage: "Custom node name", - } - DocRootFlag = DirectoryFlag{ - Name: "docroot", - Usage: "Document Root for HTTPClient file scheme", - Value: DirectoryString{homeDir()}, - } - FastSyncFlag = cli.BoolFlag{ - Name: "fast", - Usage: "Enable fast syncing through state downloads", - } - LightModeFlag = cli.BoolFlag{ - Name: "light", - Usage: "Enable light client mode", + DataDirFlag = &flags.DirectoryFlag{ + Name: "datadir", + Usage: "Data directory for the databases and keystore", + Value: flags.DirectoryString(node.DefaultDataDir()), + Category: flags.EthCategory, + } + KeyStoreDirFlag = &flags.DirectoryFlag{ + Name: "keystore", + Usage: "Directory for the keystore (default = inside the datadir)", + Category: flags.AccountCategory, + } + NetworkIdFlag = &cli.Uint64Flag{ + Name: "networkid", + Usage: "Network identifier (integer, 89=XDPoSChain)", + Value: ethconfig.Defaults.NetworkId, + Category: flags.EthCategory, + } + TestnetFlag = &cli.BoolFlag{ + Name: "testnet", + Usage: "Ropsten network: pre-configured proof-of-work test network", + Category: flags.EthCategory, + } + XDCTestnetFlag = &cli.BoolFlag{ + Name: "apothem", + Usage: "XDC Apothem Network", + Category: flags.EthCategory, + } + RinkebyFlag = &cli.BoolFlag{ + Name: "rinkeby", + Usage: "Rinkeby network: pre-configured proof-of-authority test network", + Category: flags.EthCategory, + } + + // Dev mode + DeveloperFlag = &cli.BoolFlag{ + Name: "dev", + Usage: "Ephemeral proof-of-authority network with a pre-funded developer account, mining enabled", + Category: flags.DevCategory, + } + DeveloperPeriodFlag = &cli.IntFlag{ + Name: "dev-period", + Usage: "Block period to use in developer mode (0 = mine only if transaction pending)", + Category: flags.DevCategory, + } + IdentityFlag = &cli.StringFlag{ + Name: "identity", + Usage: "Custom node name", + Category: flags.NetworkingCategory, + } + DocRootFlag = &flags.DirectoryFlag{ + Name: "docroot", + Usage: "Document Root for HTTPClient file scheme", + Value: flags.DirectoryString(flags.HomeDir()), + Category: flags.APICategory, } + defaultSyncMode = ethconfig.Defaults.SyncMode - SyncModeFlag = TextMarshalerFlag{ - Name: "syncmode", - Usage: `Blockchain sync mode ("fast", "full", or "light")`, - Value: &defaultSyncMode, - } - GCModeFlag = cli.StringFlag{ - Name: "gcmode", - Usage: `Blockchain garbage collection mode ("full", "archive")`, - Value: "full", - } - LightServFlag = cli.IntFlag{ - Name: "lightserv", - Usage: "Maximum percentage of time allowed for serving LES requests (0-90)", - Value: 0, - } - LightPeersFlag = cli.IntFlag{ - Name: "lightpeers", - Usage: "Maximum number of LES client peers", - Value: ethconfig.Defaults.LightPeers, - } - LightKDFFlag = cli.BoolFlag{ - Name: "lightkdf", - Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength", - } - // XDCX settings - XDCXEnabledFlag = cli.BoolFlag{ - Name: "XDCx", - Usage: "Enable the XDCX protocol", + SyncModeFlag = &flags.TextMarshalerFlag{ + Name: "syncmode", + Usage: `Blockchain sync mode ("fast", "full", or "light")`, + Value: &defaultSyncMode, + Category: flags.EthCategory, + } + GCModeFlag = &cli.StringFlag{ + Name: "gcmode", + Usage: `Blockchain garbage collection mode ("full", "archive")`, + Value: "full", + Category: flags.EthCategory, + } + LightKDFFlag = &cli.BoolFlag{ + Name: "lightkdf", + Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength", + Category: flags.AccountCategory, + } + + // Light server and client settings + LightServFlag = &cli.IntFlag{ + Name: "lightserv", + Usage: "Maximum percentage of time allowed for serving LES requests (0-90)", + Value: ethconfig.Defaults.LightServ, + Category: flags.LightCategory, + } + LightPeersFlag = &cli.IntFlag{ + Name: "lightpeers", + Usage: "Maximum number of LES client peers", + Value: ethconfig.Defaults.LightPeers, + Category: flags.LightCategory, } + // Ethash settings - EthashCacheDirFlag = DirectoryFlag{ - Name: "ethash.cachedir", - Usage: "Directory to store the ethash verification caches (default = inside the datadir)", - } - EthashCachesInMemoryFlag = cli.IntFlag{ - Name: "ethash.cachesinmem", - Usage: "Number of recent ethash caches to keep in memory (16MB each)", - Value: ethconfig.Defaults.Ethash.CachesInMem, - } - EthashCachesOnDiskFlag = cli.IntFlag{ - Name: "ethash.cachesondisk", - Usage: "Number of recent ethash caches to keep on disk (16MB each)", - Value: ethconfig.Defaults.Ethash.CachesOnDisk, - } - EthashDatasetDirFlag = DirectoryFlag{ - Name: "ethash.dagdir", - Usage: "Directory to store the ethash mining DAGs (default = inside home folder)", - Value: DirectoryString{ethconfig.Defaults.Ethash.DatasetDir}, - } - EthashDatasetsInMemoryFlag = cli.IntFlag{ - Name: "ethash.dagsinmem", - Usage: "Number of recent ethash mining DAGs to keep in memory (1+GB each)", - Value: ethconfig.Defaults.Ethash.DatasetsInMem, - } - EthashDatasetsOnDiskFlag = cli.IntFlag{ - Name: "ethash.dagsondisk", - Usage: "Number of recent ethash mining DAGs to keep on disk (1+GB each)", - Value: ethconfig.Defaults.Ethash.DatasetsOnDisk, + EthashCacheDirFlag = &flags.DirectoryFlag{ + Name: "ethash.cachedir", + Usage: "Directory to store the ethash verification caches (default = inside the datadir)", + Category: flags.EthashCategory, + } + EthashCachesInMemoryFlag = &cli.IntFlag{ + Name: "ethash.cachesinmem", + Usage: "Number of recent ethash caches to keep in memory (16MB each)", + Value: ethconfig.Defaults.Ethash.CachesInMem, + Category: flags.EthashCategory, + } + EthashCachesOnDiskFlag = &cli.IntFlag{ + Name: "ethash.cachesondisk", + Usage: "Number of recent ethash caches to keep on disk (16MB each)", + Value: ethconfig.Defaults.Ethash.CachesOnDisk, + Category: flags.EthashCategory, + } + EthashDatasetDirFlag = &flags.DirectoryFlag{ + Name: "ethash.dagdir", + Usage: "Directory to store the ethash mining DAGs (default = inside home folder)", + Value: flags.DirectoryString(ethconfig.Defaults.Ethash.DatasetDir), + Category: flags.EthashCategory, + } + EthashDatasetsInMemoryFlag = &cli.IntFlag{ + Name: "ethash.dagsinmem", + Usage: "Number of recent ethash mining DAGs to keep in memory (1+GB each)", + Value: ethconfig.Defaults.Ethash.DatasetsInMem, + Category: flags.EthashCategory, + } + EthashDatasetsOnDiskFlag = &cli.IntFlag{ + Name: "ethash.dagsondisk", + Usage: "Number of recent ethash mining DAGs to keep on disk (1+GB each)", + Value: ethconfig.Defaults.Ethash.DatasetsOnDisk, + Category: flags.EthashCategory, } + // Transaction pool settings - TxPoolNoLocalsFlag = cli.BoolFlag{ - Name: "txpool.nolocals", - Usage: "Disables price exemptions for locally submitted transactions", - } - TxPoolJournalFlag = cli.StringFlag{ - Name: "txpool.journal", - Usage: "Disk journal for local transaction to survive node restarts", - Value: txpool.DefaultConfig.Journal, - } - TxPoolRejournalFlag = cli.DurationFlag{ - Name: "txpool.rejournal", - Usage: "Time interval to regenerate the local transaction journal", - Value: txpool.DefaultConfig.Rejournal, - } - TxPoolPriceLimitFlag = cli.Uint64Flag{ - Name: "txpool.pricelimit", - Usage: "Minimum gas price limit to enforce for acceptance into the pool", - Value: ethconfig.Defaults.TxPool.PriceLimit, - } - TxPoolPriceBumpFlag = cli.Uint64Flag{ - Name: "txpool.pricebump", - Usage: "Price bump percentage to replace an already existing transaction", - Value: ethconfig.Defaults.TxPool.PriceBump, - } - TxPoolAccountSlotsFlag = cli.Uint64Flag{ - Name: "txpool.accountslots", - Usage: "Minimum number of executable transaction slots guaranteed per account", - Value: ethconfig.Defaults.TxPool.AccountSlots, - } - TxPoolGlobalSlotsFlag = cli.Uint64Flag{ - Name: "txpool.globalslots", - Usage: "Maximum number of executable transaction slots for all accounts", - Value: ethconfig.Defaults.TxPool.GlobalSlots, - } - TxPoolAccountQueueFlag = cli.Uint64Flag{ - Name: "txpool.accountqueue", - Usage: "Maximum number of non-executable transaction slots permitted per account", - Value: ethconfig.Defaults.TxPool.AccountQueue, - } - TxPoolGlobalQueueFlag = cli.Uint64Flag{ - Name: "txpool.globalqueue", - Usage: "Maximum number of non-executable transaction slots for all accounts", - Value: ethconfig.Defaults.TxPool.GlobalQueue, - } - TxPoolLifetimeFlag = cli.DurationFlag{ - Name: "txpool.lifetime", - Usage: "Maximum amount of time non-executable transaction are queued", - Value: ethconfig.Defaults.TxPool.Lifetime, + TxPoolNoLocalsFlag = &cli.BoolFlag{ + Name: "txpool.nolocals", + Usage: "Disables price exemptions for locally submitted transactions", + Category: flags.TxPoolCategory, + } + TxPoolJournalFlag = &cli.StringFlag{ + Name: "txpool.journal", + Usage: "Disk journal for local transaction to survive node restarts", + Value: txpool.DefaultConfig.Journal, + Category: flags.TxPoolCategory, + } + TxPoolRejournalFlag = &cli.DurationFlag{ + Name: "txpool.rejournal", + Usage: "Time interval to regenerate the local transaction journal", + Value: txpool.DefaultConfig.Rejournal, + Category: flags.TxPoolCategory, + } + TxPoolPriceLimitFlag = &cli.Uint64Flag{ + Name: "txpool.pricelimit", + Usage: "Minimum gas price limit to enforce for acceptance into the pool", + Value: ethconfig.Defaults.TxPool.PriceLimit, + Category: flags.TxPoolCategory, + } + TxPoolPriceBumpFlag = &cli.Uint64Flag{ + Name: "txpool.pricebump", + Usage: "Price bump percentage to replace an already existing transaction", + Value: ethconfig.Defaults.TxPool.PriceBump, + Category: flags.TxPoolCategory, + } + TxPoolAccountSlotsFlag = &cli.Uint64Flag{ + Name: "txpool.accountslots", + Usage: "Minimum number of executable transaction slots guaranteed per account", + Value: ethconfig.Defaults.TxPool.AccountSlots, + Category: flags.TxPoolCategory, + } + TxPoolGlobalSlotsFlag = &cli.Uint64Flag{ + Name: "txpool.globalslots", + Usage: "Maximum number of executable transaction slots for all accounts", + Value: ethconfig.Defaults.TxPool.GlobalSlots, + Category: flags.TxPoolCategory, + } + TxPoolAccountQueueFlag = &cli.Uint64Flag{ + Name: "txpool.accountqueue", + Usage: "Maximum number of non-executable transaction slots permitted per account", + Value: ethconfig.Defaults.TxPool.AccountQueue, + Category: flags.TxPoolCategory, + } + TxPoolGlobalQueueFlag = &cli.Uint64Flag{ + Name: "txpool.globalqueue", + Usage: "Maximum number of non-executable transaction slots for all accounts", + Value: ethconfig.Defaults.TxPool.GlobalQueue, + Category: flags.TxPoolCategory, + } + TxPoolLifetimeFlag = &cli.DurationFlag{ + Name: "txpool.lifetime", + Usage: "Maximum amount of time non-executable transaction are queued", + Value: ethconfig.Defaults.TxPool.Lifetime, + Category: flags.TxPoolCategory, } + // Performance tuning settings - CacheFlag = cli.IntFlag{ - Name: "cache", - Usage: "Megabytes of memory allocated to internal caching", - Value: 1024, - } - CacheDatabaseFlag = cli.IntFlag{ - Name: "cache.database", - Usage: "Percentage of cache memory allowance to use for database io", - Value: 75, - } - CacheGCFlag = cli.IntFlag{ - Name: "cache.gc", - Usage: "Percentage of cache memory allowance to use for trie pruning", - Value: 25, + CacheFlag = &cli.IntFlag{ + Name: "cache", + Usage: "Megabytes of memory allocated to internal caching", + Value: 1024, + Category: flags.PerfCategory, + } + CacheDatabaseFlag = &cli.IntFlag{ + Name: "cache.database", + Usage: "Percentage of cache memory allowance to use for database io", + Value: 50, + Category: flags.PerfCategory, + } + CacheGCFlag = &cli.IntFlag{ + Name: "cache.gc", + Usage: "Percentage of cache memory allowance to use for trie pruning", + Value: 25, + Category: flags.PerfCategory, } CacheLogSizeFlag = &cli.IntFlag{ - Name: "cache.blocklogs", - Usage: "Size (in number of blocks) of the log cache for filtering", - Value: ethconfig.Defaults.FilterLogCacheSize, + Name: "cache.blocklogs", + Usage: "Size (in number of blocks) of the log cache for filtering", + Value: ethconfig.Defaults.FilterLogCacheSize, + Category: flags.PerfCategory, } - FDLimitFlag = cli.IntFlag{ - Name: "fdlimit", - Usage: "Raise the open file descriptor resource limit (default = system fd limit)", + FDLimitFlag = &cli.IntFlag{ + Name: "fdlimit", + Usage: "Raise the open file descriptor resource limit (default = system fd limit)", + Category: flags.PerfCategory, } + // Miner settings - StakingEnabledFlag = cli.BoolFlag{ - Name: "mine", - Usage: "Enable staking", - } - StakerThreadsFlag = cli.IntFlag{ - Name: "minerthreads", - Usage: "Number of CPU threads to use for staking", - Value: runtime.NumCPU(), - } - TargetGasLimitFlag = cli.Uint64Flag{ - Name: "targetgaslimit", - Usage: "Target gas limit sets the artificial target gas floor for the blocks to mine", - Value: params.XDCGenesisGasLimit, - } - EtherbaseFlag = cli.StringFlag{ - Name: "etherbase", - Usage: "Public address for block mining rewards (default = first account created)", - Value: "0", - } - GasPriceFlag = BigFlag{ - Name: "gasprice", - Usage: "Minimal gas price to accept for mining a transactions", - Value: ethconfig.Defaults.GasPrice, - } - ExtraDataFlag = cli.StringFlag{ - Name: "extradata", - Usage: "Block extra data set by the miner (default = client version)", + StakingEnabledFlag = &cli.BoolFlag{ + Name: "mine", + Usage: "Enable staking", + Category: flags.MinerCategory, + } + StakerThreadsFlag = &cli.IntFlag{ + Name: "minerthreads", + Usage: "Number of CPU threads to use for staking", + Value: runtime.NumCPU(), + Category: flags.MinerCategory, + } + TargetGasLimitFlag = &cli.Uint64Flag{ + Name: "targetgaslimit", + Usage: "Target gas limit sets the artificial target gas floor for the blocks to mine", + Value: params.XDCGenesisGasLimit, + Category: flags.MinerCategory, + } + EtherbaseFlag = &cli.StringFlag{ + Name: "etherbase", + Usage: "Public address for block mining rewards (default = first account created)", + Value: "0", + Category: flags.MinerCategory, + } + GasPriceFlag = &flags.BigFlag{ + Name: "gasprice", + Usage: "Minimal gas price to accept for mining a transactions", + Value: ethconfig.Defaults.GasPrice, + Category: flags.MinerCategory, + } + ExtraDataFlag = &cli.StringFlag{ + Name: "extradata", + Usage: "Block extra data set by the miner (default = client version)", + Category: flags.MinerCategory, } + // Account settings - UnlockedAccountFlag = cli.StringFlag{ - Name: "unlock", - Usage: "Comma separated list of accounts to unlock", - Value: "", + UnlockedAccountFlag = &cli.StringFlag{ + Name: "unlock", + Usage: "Comma separated list of accounts to unlock", + Value: "", + Category: flags.AccountCategory, } - PasswordFileFlag = cli.StringFlag{ - Name: "password", - Usage: "Password file to use for non-interactive password input", - Value: "", + PasswordFileFlag = &cli.StringFlag{ + Name: "password", + Usage: "Password file to use for non-interactive password input", + Value: "", + Category: flags.AccountCategory, } - VMEnableDebugFlag = cli.BoolFlag{ - Name: "vmdebug", - Usage: "Record information useful for VM and contract debugging", + // EVM settings + VMEnableDebugFlag = &cli.BoolFlag{ + Name: "vmdebug", + Usage: "Record information useful for VM and contract debugging", + Category: flags.VMCategory, } - RPCGlobalGasCapFlag = cli.Uint64Flag{ - Name: "rpc-gascap", - Usage: "Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite)", - Value: ethconfig.Defaults.RPCGasCap, + + // API options + RPCGlobalGasCapFlag = &cli.Uint64Flag{ + Name: "rpc-gascap", + Usage: "Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite)", + Value: ethconfig.Defaults.RPCGasCap, + Category: flags.APICategory, } - RPCGlobalTxFeeCap = cli.Float64Flag{ - Name: "rpc.txfeecap", - Usage: "Sets a cap on transaction fee (in ether) that can be sent via the RPC APIs (0 = no cap)", - Value: ethconfig.Defaults.RPCTxFeeCap, + RPCGlobalTxFeeCap = &cli.Float64Flag{ + Name: "rpc.txfeecap", + Usage: "Sets a cap on transaction fee (in ether) that can be sent via the RPC APIs (0 = no cap)", + Value: ethconfig.Defaults.RPCTxFeeCap, + Category: flags.APICategory, } + // Logging and debug settings - EthStatsURLFlag = cli.StringFlag{ - Name: "ethstats", - Usage: "Reporting URL of a ethstats service (nodename:secret@host:port)", + EthStatsURLFlag = &cli.StringFlag{ + Name: "ethstats", + Usage: "Reporting URL of a ethstats service (nodename:secret@host:port)", + Category: flags.MetricsCategory, } - // MetricsHTTPFlag defines the endpoint for a stand-alone metrics HTTP endpoint. - // Since the pprof service enables sensitive/vulnerable behavior, this allows a user - // to enable a public-OK metrics endpoint without having to worry about ALSO exposing - // other profiling behavior or information. - MetricsHTTPFlag = cli.StringFlag{ - Name: "metrics.addr", - Usage: "Enable stand-alone metrics HTTP server listening interface", - Value: metrics.DefaultConfig.HTTP, - } - MetricsPortFlag = cli.IntFlag{ - Name: "metrics.port", - Usage: "Metrics HTTP server listening port", - Value: metrics.DefaultConfig.Port, + FakePoWFlag = &cli.BoolFlag{ + Name: "fakepow", + Usage: "Disables proof-of-work verification", + Category: flags.LoggingCategory, } - MetricsEnabledFlag = cli.BoolFlag{ - Name: metrics.MetricsEnabledFlag, - Usage: "Enable metrics collection and reporting", - } - FakePoWFlag = cli.BoolFlag{ - Name: "fakepow", - Usage: "Disables proof-of-work verification", - } - NoCompactionFlag = cli.BoolFlag{ - Name: "nocompaction", - Usage: "Disables db compaction after import", + NoCompactionFlag = &cli.BoolFlag{ + Name: "nocompaction", + Usage: "Disables db compaction after import", + Category: flags.LoggingCategory, } + // RPC settings - RPCEnabledFlag = cli.BoolFlag{ - Name: "rpc", - Usage: "Enable the HTTP-RPC server", - } - RPCListenAddrFlag = cli.StringFlag{ - Name: "rpcaddr", - Usage: "HTTP-RPC server listening interface", - Value: node.DefaultHTTPHost, - } - RewoundFlag = cli.IntFlag{ - Name: "rewound", - Usage: "Rewound blocks", - Value: 0, - } - RPCPortFlag = cli.IntFlag{ - Name: "rpcport", - Usage: "HTTP-RPC server listening port", - Value: node.DefaultHTTPPort, - } - RPCHttpReadTimeoutFlag = cli.DurationFlag{ - Name: "rpcreadtimeout", - Usage: "HTTP-RPC server read timeout", - Value: rpc.DefaultHTTPTimeouts.ReadTimeout, - } - RPCHttpWriteTimeoutFlag = cli.DurationFlag{ - Name: "rpcwritetimeout", - Usage: "HTTP-RPC server write timeout", - Value: rpc.DefaultHTTPTimeouts.WriteTimeout, - } - RPCHttpIdleTimeoutFlag = cli.DurationFlag{ - Name: "rpcidletimeout", - Usage: "HTTP-RPC server idle timeout", - Value: rpc.DefaultHTTPTimeouts.IdleTimeout, - } - RPCCORSDomainFlag = cli.StringFlag{ - Name: "rpccorsdomain", - Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)", - Value: "", - } - RPCVirtualHostsFlag = cli.StringFlag{ - Name: "rpcvhosts", - Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.", - Value: strings.Join(node.DefaultConfig.HTTPVirtualHosts, ","), - } - RPCApiFlag = cli.StringFlag{ - Name: "rpcapi", - Usage: "API's offered over the HTTP-RPC interface", - Value: "", - } - IPCDisabledFlag = cli.BoolFlag{ - Name: "ipcdisable", - Usage: "Disable the IPC-RPC server", - } - IPCPathFlag = DirectoryFlag{ - Name: "ipcpath", - Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)", - } - WSEnabledFlag = cli.BoolFlag{ - Name: "ws", - Usage: "Enable the WS-RPC server", - } - WSListenAddrFlag = cli.StringFlag{ - Name: "wsaddr", - Usage: "WS-RPC server listening interface", - Value: node.DefaultWSHost, - } - WSPortFlag = cli.IntFlag{ - Name: "wsport", - Usage: "WS-RPC server listening port", - Value: node.DefaultWSPort, - } - WSApiFlag = cli.StringFlag{ - Name: "wsapi", - Usage: "API's offered over the WS-RPC interface", - Value: "", - } - WSAllowedOriginsFlag = cli.StringFlag{ - Name: "wsorigins", - Usage: "Origins from which to accept websockets requests", - Value: "", - } - ExecFlag = cli.StringFlag{ - Name: "exec", - Usage: "Execute JavaScript statement", - } - PreloadJSFlag = cli.StringFlag{ - Name: "preload", - Usage: "Comma separated list of JavaScript files to preload into the console", + IPCDisabledFlag = &cli.BoolFlag{ + Name: "ipcdisable", + Usage: "Disable the IPC-RPC server", + Category: flags.APICategory, + } + IPCPathFlag = &flags.DirectoryFlag{ + Name: "ipcpath", + Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)", + Category: flags.APICategory, + } + RPCEnabledFlag = &cli.BoolFlag{ + Name: "rpc", + Usage: "Enable the HTTP-RPC server", + Category: flags.APICategory, + } + RPCListenAddrFlag = &cli.StringFlag{ + Name: "rpcaddr", + Usage: "HTTP-RPC server listening interface", + Value: node.DefaultHTTPHost, + Category: flags.APICategory, + } + RPCPortFlag = &cli.IntFlag{ + Name: "rpcport", + Usage: "HTTP-RPC server listening port", + Value: node.DefaultHTTPPort, + Category: flags.APICategory, + } + RPCCORSDomainFlag = &cli.StringFlag{ + Name: "rpccorsdomain", + Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)", + Value: "", + Category: flags.APICategory, + } + RPCVirtualHostsFlag = &cli.StringFlag{ + Name: "rpcvhosts", + Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.", + Value: strings.Join(node.DefaultConfig.HTTPVirtualHosts, ","), + Category: flags.APICategory, + } + RPCApiFlag = &cli.StringFlag{ + Name: "rpcapi", + Usage: "API's offered over the HTTP-RPC interface", + Value: "", + Category: flags.APICategory, + } + WSEnabledFlag = &cli.BoolFlag{ + Name: "ws", + Usage: "Enable the WS-RPC server", + Category: flags.APICategory, + } + WSListenAddrFlag = &cli.StringFlag{ + Name: "wsaddr", + Usage: "WS-RPC server listening interface", + Value: node.DefaultWSHost, + Category: flags.APICategory, + } + WSPortFlag = &cli.IntFlag{ + Name: "wsport", + Usage: "WS-RPC server listening port", + Value: node.DefaultWSPort, + Category: flags.APICategory, + } + WSApiFlag = &cli.StringFlag{ + Name: "wsapi", + Usage: "API's offered over the WS-RPC interface", + Value: "", + Category: flags.APICategory, + } + WSAllowedOriginsFlag = &cli.StringFlag{ + Name: "wsorigins", + Usage: "Origins from which to accept websockets requests", + Value: "", + Category: flags.APICategory, + } + ExecFlag = &cli.StringFlag{ + Name: "exec", + Usage: "Execute JavaScript statement", + Category: flags.APICategory, + } + PreloadJSFlag = &cli.StringFlag{ + Name: "preload", + Usage: "Comma separated list of JavaScript files to preload into the console", + Category: flags.APICategory, + } + RPCHttpReadTimeoutFlag = &cli.DurationFlag{ + Name: "rpcreadtimeout", + Usage: "HTTP-RPC server read timeout", + Value: rpc.DefaultHTTPTimeouts.ReadTimeout, + Category: flags.APICategory, + } + RPCHttpWriteTimeoutFlag = &cli.DurationFlag{ + Name: "rpcwritetimeout", + Usage: "HTTP-RPC server write timeout", + Value: rpc.DefaultHTTPTimeouts.WriteTimeout, + Category: flags.APICategory, + } + RPCHttpIdleTimeoutFlag = &cli.DurationFlag{ + Name: "rpcidletimeout", + Usage: "HTTP-RPC server idle timeout", + Value: rpc.DefaultHTTPTimeouts.IdleTimeout, + Category: flags.APICategory, } // Network Settings - MaxPeersFlag = cli.IntFlag{ - Name: "maxpeers", - Usage: "Maximum number of network peers (network disabled if set to 0)", - Value: node.DefaultConfig.P2P.MaxPeers, - } - MaxPendingPeersFlag = cli.IntFlag{ - Name: "maxpendpeers", - Usage: "Maximum number of pending connection attempts (defaults used if set to 0)", - Value: node.DefaultConfig.P2P.MaxPendingPeers, - } - ListenPortFlag = cli.IntFlag{ - Name: "port", - Usage: "Network listening port", - Value: 30303, + MaxPeersFlag = &cli.IntFlag{ + Name: "maxpeers", + Usage: "Maximum number of network peers (network disabled if set to 0)", + Value: node.DefaultConfig.P2P.MaxPeers, + Category: flags.NetworkingCategory, + } + MaxPendingPeersFlag = &cli.IntFlag{ + Name: "maxpendpeers", + Usage: "Maximum number of pending connection attempts (defaults used if set to 0)", + Value: node.DefaultConfig.P2P.MaxPendingPeers, + Category: flags.NetworkingCategory, + } + ListenPortFlag = &cli.IntFlag{ + Name: "port", + Usage: "Network listening port", + Value: 30303, + Category: flags.NetworkingCategory, + } + BootnodesFlag = &cli.StringFlag{ + Name: "bootnodes", + Usage: "Comma separated enode URLs for P2P discovery bootstrap (set v4+v5 instead for light servers)", + Value: "", + Category: flags.NetworkingCategory, + } + BootnodesV4Flag = &cli.StringFlag{ + Name: "bootnodesv4", + Usage: "Comma separated enode URLs for P2P v4 discovery bootstrap (light server, full nodes)", + Value: "", + Category: flags.NetworkingCategory, + } + BootnodesV5Flag = &cli.StringFlag{ + Name: "bootnodesv5", + Usage: "Comma separated enode URLs for P2P v5 discovery bootstrap (light server, light nodes)", + Value: "", + Category: flags.NetworkingCategory, + } + NodeKeyFileFlag = &cli.StringFlag{ + Name: "nodekey", + Usage: "P2P node key file", + Category: flags.NetworkingCategory, + } + NodeKeyHexFlag = &cli.StringFlag{ + Name: "nodekeyhex", + Usage: "P2P node key as hex (for testing)", + Category: flags.NetworkingCategory, + } + NATFlag = &cli.StringFlag{ + Name: "nat", + Usage: "NAT port mapping mechanism (any|none|upnp|pmp|extip:)", + Value: "any", + Category: flags.NetworkingCategory, + } + NoDiscoverFlag = &cli.BoolFlag{ + Name: "nodiscover", + Usage: "Disables the peer discovery mechanism (manual peer addition)", + Category: flags.NetworkingCategory, + } + DiscoveryV5Flag = &cli.BoolFlag{ + Name: "v5disc", + Usage: "Enables the experimental RLPx V5 (Topic Discovery) mechanism", + Category: flags.NetworkingCategory, + } + NetrestrictFlag = &cli.StringFlag{ + Name: "netrestrict", + Usage: "Restricts network communication to the given IP networks (CIDR masks)", + Category: flags.NetworkingCategory, + } + + // Console + JSpathFlag = &cli.StringFlag{ + Name: "jspath", + Usage: "JavaScript root path for `loadScript`", + Value: ".", + Category: flags.APICategory, } - BootnodesFlag = cli.StringFlag{ - Name: "bootnodes", - Usage: "Comma separated enode URLs for P2P discovery bootstrap (set v4+v5 instead for light servers)", - Value: "", - } - BootnodesV4Flag = cli.StringFlag{ - Name: "bootnodesv4", - Usage: "Comma separated enode URLs for P2P v4 discovery bootstrap (light server, full nodes)", - Value: "", - } - BootnodesV5Flag = cli.StringFlag{ - Name: "bootnodesv5", - Usage: "Comma separated enode URLs for P2P v5 discovery bootstrap (light server, light nodes)", - Value: "", - } - NodeKeyFileFlag = cli.StringFlag{ - Name: "nodekey", - Usage: "P2P node key file", - } - NodeKeyHexFlag = cli.StringFlag{ - Name: "nodekeyhex", - Usage: "P2P node key as hex (for testing)", - } - NATFlag = cli.StringFlag{ - Name: "nat", - Usage: "NAT port mapping mechanism (any|none|upnp|pmp|extip:)", - Value: "any", - } - NoDiscoverFlag = cli.BoolFlag{ - Name: "nodiscover", - Usage: "Disables the peer discovery mechanism (manual peer addition)", - } - DiscoveryV5Flag = cli.BoolFlag{ - Name: "v5disc", - Usage: "Enables the experimental RLPx V5 (Topic Discovery) mechanism", - } - NetrestrictFlag = cli.StringFlag{ - Name: "netrestrict", - Usage: "Restricts network communication to the given IP networks (CIDR masks)", + + // Gas price oracle settings + GpoBlocksFlag = &cli.IntFlag{ + Name: "gpoblocks", + Usage: "Number of recent blocks to check for gas prices", + Value: ethconfig.Defaults.GPO.Blocks, + Category: flags.GasPriceCategory, + } + GpoPercentileFlag = &cli.IntFlag{ + Name: "gpopercentile", + Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices", + Value: ethconfig.Defaults.GPO.Percentile, + Category: flags.GasPriceCategory, + } + GpoMaxGasPriceFlag = &cli.Int64Flag{ + Name: "gpo.maxprice", + Usage: "Maximum gas price will be recommended by gpo", + Value: ethconfig.Defaults.GPO.MaxPrice.Int64(), + Category: flags.GasPriceCategory, + } + GpoIgnoreGasPriceFlag = &cli.Int64Flag{ + Name: "gpo.ignoreprice", + Usage: "Gas price below which gpo will ignore transactions", + Value: ethconfig.Defaults.GPO.IgnorePrice.Int64(), + Category: flags.GasPriceCategory, + } + + // Metrics flags + MetricsEnabledFlag = &cli.BoolFlag{ + Name: "metrics", + Usage: "Enable metrics collection and reporting", + Category: flags.MetricsCategory, } - // ATM the url is left to the user and deployment to - JSpathFlag = cli.StringFlag{ - Name: "jspath", - Usage: "JavaScript root path for `loadScript`", - Value: ".", + // MetricsHTTPFlag defines the endpoint for a stand-alone metrics HTTP endpoint. + // Since the pprof service enables sensitive/vulnerable behavior, this allows a user + // to enable a public-OK metrics endpoint without having to worry about ALSO exposing + // other profiling behavior or information. + MetricsHTTPFlag = &cli.StringFlag{ + Name: "metrics.addr", + Usage: "Enable stand-alone metrics HTTP server listening interface", + Value: metrics.DefaultConfig.HTTP, + Category: flags.MetricsCategory, + } + MetricsPortFlag = &cli.IntFlag{ + Name: "metrics.port", + Usage: "Metrics HTTP server listening port", + Value: metrics.DefaultConfig.Port, + Category: flags.MetricsCategory, + } + + // MISC settings + RollbackFlag = &cli.StringFlag{ + Name: "rollback", + Usage: "Rollback chain at hash", + Value: "", + Category: flags.MiscCategory, + } + AnnounceTxsFlag = &cli.BoolFlag{ + Name: "announce-txs", + Usage: "Always commit transactions", + Value: false, + Category: flags.MiscCategory, + } + StoreRewardFlag = &cli.BoolFlag{ + Name: "store-reward", + Usage: "Store reward to file", + Value: false, + Category: flags.MiscCategory, + } + RewoundFlag = &cli.IntFlag{ + Name: "rewound", + Usage: "Rewound blocks", + Value: 0, + Category: flags.MiscCategory, + } + + // XDC settings + Enable0xPrefixFlag = &cli.BoolFlag{ + Name: "enable-0x-prefix", + Usage: "Addres use 0x-prefix (Deprecated: this is on by default, to use xdc prefix use --enable-xdc-prefix)", + Value: true, + Category: flags.XdcCategory, + } + EnableXDCPrefixFlag = &cli.BoolFlag{ + Name: "enable-xdc-prefix", + Usage: "Addres use xdc-prefix (default = false)", + Value: false, + Category: flags.XdcCategory, + } + XDCSlaveModeFlag = &cli.BoolFlag{ + Name: "slave", + Usage: "Enable slave mode", + Category: flags.XdcCategory, } - // Gas price oracle settings - GpoBlocksFlag = cli.IntFlag{ - Name: "gpoblocks", - Usage: "Number of recent blocks to check for gas prices", - Value: ethconfig.Defaults.GPO.Blocks, - } - GpoPercentileFlag = cli.IntFlag{ - Name: "gpopercentile", - Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices", - Value: ethconfig.Defaults.GPO.Percentile, - } - GpoMaxGasPriceFlag = cli.Int64Flag{ - Name: "gpo.maxprice", - Usage: "Maximum gas price will be recommended by gpo", - Value: ethconfig.Defaults.GPO.MaxPrice.Int64(), - } - GpoIgnoreGasPriceFlag = cli.Int64Flag{ - Name: "gpo.ignoreprice", - Usage: "Gas price below which gpo will ignore transactions", - Value: ethconfig.Defaults.GPO.IgnorePrice.Int64(), - } - XDCXDataDirFlag = DirectoryFlag{ - Name: "XDCx.datadir", - Usage: "Data directory for the XDCX databases", - Value: DirectoryString{filepath.Join(DataDirFlag.Value.String(), "XDCx")}, - } - XDCXDBEngineFlag = cli.StringFlag{ - Name: "XDCx.dbengine", - Usage: "Database engine for XDCX (leveldb, mongodb)", - Value: "leveldb", - } - XDCXDBNameFlag = cli.StringFlag{ - Name: "XDCx.dbName", - Usage: "Database name for XDCX", - Value: "XDCdex", - } - XDCXDBConnectionUrlFlag = cli.StringFlag{ - Name: "XDCx.dbConnectionUrl", - Usage: "ConnectionUrl to database if dbEngine is mongodb. Host:port. If there are multiple instances, separated by comma. Eg: localhost:27017,localhost:27018", - Value: "localhost:27017", - } - XDCXDBReplicaSetNameFlag = cli.StringFlag{ - Name: "XDCx.dbReplicaSetName", - Usage: "ReplicaSetName if Master-Slave is setup", - } - XDCSlaveModeFlag = cli.BoolFlag{ - Name: "slave", - Usage: "Enable slave mode", - } - // Deprecated November 2023 - LogBacktraceAtFlag = &cli.StringFlag{ - Name: "log-backtrace", - Usage: "Request a stack trace at a specific logging statement (deprecated)", - Value: "", - } - LogDebugFlag = &cli.BoolFlag{ - Name: "log-debug", - Usage: "Prepends log messages with call-site location (deprecated)", + // XDCX settings + XDCXEnabledFlag = &cli.BoolFlag{ + Name: "XDCx", + Usage: "Enable the XDCX protocol", + Category: flags.XdcxCategory, + } + XDCXDataDirFlag = &flags.DirectoryFlag{ + Name: "XDCx.datadir", + Usage: "Data directory for the XDCX databases", + Value: flags.DirectoryString(filepath.Join(DataDirFlag.Value.String(), "XDCx")), + Category: flags.XdcxCategory, + } + XDCXDBEngineFlag = &cli.StringFlag{ + Name: "XDCx.dbengine", + Usage: "Database engine for XDCX (leveldb, mongodb)", + Value: "leveldb", + Category: flags.XdcxCategory, + } + XDCXDBNameFlag = &cli.StringFlag{ + Name: "XDCx.dbName", + Usage: "Database name for XDCX", + Value: "XDCdex", + Category: flags.XdcxCategory, + } + XDCXDBConnectionUrlFlag = &cli.StringFlag{ + Name: "XDCx.dbConnectionUrl", + Usage: "ConnectionUrl to database if dbEngine is mongodb. Host:port. If there are multiple instances, separated by comma. Eg: localhost:27017,localhost:27018", + Value: "localhost:27017", + Category: flags.XdcxCategory, + } + XDCXDBReplicaSetNameFlag = &cli.StringFlag{ + Name: "XDCx.dbReplicaSetName", + Usage: "ReplicaSetName if Master-Slave is setup", + Category: flags.XdcxCategory, } ) +// GroupFlags combines the given flag slices together and returns the merged one. +func GroupFlags(groups ...[]cli.Flag) []cli.Flag { + var ret []cli.Flag + for _, group := range groups { + ret = append(ret, group...) + } + return ret +} + // MakeDataDir retrieves the currently requested data directory, terminating // if none (or the empty string) is specified. If the node is starting a testnet, // the a subdirectory of the specified datadir will be used. func MakeDataDir(ctx *cli.Context) string { - if path := ctx.GlobalString(DataDirFlag.Name); path != "" { - if ctx.GlobalBool(TestnetFlag.Name) { + if path := ctx.String(DataDirFlag.Name); path != "" { + if ctx.Bool(TestnetFlag.Name) { return filepath.Join(path, "testnet") } - if ctx.GlobalBool(RinkebyFlag.Name) { + if ctx.Bool(RinkebyFlag.Name) { return filepath.Join(path, "rinkeby") } return path @@ -652,8 +722,8 @@ func MakeDataDir(ctx *cli.Context) string { // method returns nil and an emphemeral key is to be generated. func setNodeKey(ctx *cli.Context, cfg *p2p.Config) { var ( - hex = ctx.GlobalString(NodeKeyHexFlag.Name) - file = ctx.GlobalString(NodeKeyFileFlag.Name) + hex = ctx.String(NodeKeyHexFlag.Name) + file = ctx.String(NodeKeyFileFlag.Name) key *ecdsa.PrivateKey err error ) @@ -675,7 +745,7 @@ func setNodeKey(ctx *cli.Context, cfg *p2p.Config) { // setNodeUserIdent creates the user identifier from CLI flags. func setNodeUserIdent(ctx *cli.Context, cfg *node.Config) { - if identity := ctx.GlobalString(IdentityFlag.Name); len(identity) > 0 { + if identity := ctx.String(IdentityFlag.Name); len(identity) > 0 { cfg.UserIdent = identity } } @@ -685,21 +755,21 @@ func setNodeUserIdent(ctx *cli.Context, cfg *node.Config) { func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) { urls := []string{} switch { - case ctx.GlobalIsSet(BootnodesFlag.Name) || ctx.GlobalIsSet(BootnodesV4Flag.Name): - if ctx.GlobalIsSet(BootnodesV4Flag.Name) { - urls = strings.Split(ctx.GlobalString(BootnodesV4Flag.Name), ",") + case ctx.IsSet(BootnodesFlag.Name) || ctx.IsSet(BootnodesV4Flag.Name): + if ctx.IsSet(BootnodesV4Flag.Name) { + urls = strings.Split(ctx.String(BootnodesV4Flag.Name), ",") } else { - urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",") + urls = strings.Split(ctx.String(BootnodesFlag.Name), ",") } - // case ctx.GlobalBool(TestnetFlag.Name): + // case ctx.Bool(TestnetFlag.Name): // urls = params.TestnetBootnodes - // case ctx.GlobalBool(RinkebyFlag.Name): + // case ctx.Bool(RinkebyFlag.Name): // urls = params.RinkebyBootnodes case cfg.BootstrapNodes != nil: return // already set, don't apply defaults. - case !ctx.GlobalIsSet(BootnodesFlag.Name): + case !ctx.IsSet(BootnodesFlag.Name): urls = params.MainnetBootnodes - case ctx.GlobalBool(XDCTestnetFlag.Name): + case ctx.Bool(XDCTestnetFlag.Name): urls = params.TestnetBootnodes } cfg.BootstrapNodes = make([]*discover.Node, 0, len(urls)) @@ -718,13 +788,13 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) { func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) { urls := params.DiscoveryV5Bootnodes switch { - case ctx.GlobalIsSet(BootnodesFlag.Name) || ctx.GlobalIsSet(BootnodesV5Flag.Name): - if ctx.GlobalIsSet(BootnodesV5Flag.Name) { - urls = strings.Split(ctx.GlobalString(BootnodesV5Flag.Name), ",") + case ctx.IsSet(BootnodesFlag.Name) || ctx.IsSet(BootnodesV5Flag.Name): + if ctx.IsSet(BootnodesV5Flag.Name) { + urls = strings.Split(ctx.String(BootnodesV5Flag.Name), ",") } else { - urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",") + urls = strings.Split(ctx.String(BootnodesFlag.Name), ",") } - case ctx.GlobalBool(RinkebyFlag.Name): + case ctx.Bool(RinkebyFlag.Name): urls = params.RinkebyBootnodes case cfg.BootstrapNodesV5 != nil: return // already set, don't apply defaults. @@ -744,16 +814,16 @@ func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) { // setListenAddress creates a TCP listening address string from set command // line flags. func setListenAddress(ctx *cli.Context, cfg *p2p.Config) { - if ctx.GlobalIsSet(ListenPortFlag.Name) { - cfg.ListenAddr = fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)) + if ctx.IsSet(ListenPortFlag.Name) { + cfg.ListenAddr = fmt.Sprintf(":%d", ctx.Int(ListenPortFlag.Name)) } } // setNAT creates a port mapper from command line flags. func setNAT(ctx *cli.Context, cfg *p2p.Config) { - if ctx.GlobalIsSet(NATFlag.Name) { - log.Info("NAT is setted", "value", ctx.GlobalString(NATFlag.Name)) - natif, err := nat.Parse(ctx.GlobalString(NATFlag.Name)) + if ctx.IsSet(NATFlag.Name) { + log.Info("NAT is setted", "value", ctx.String(NATFlag.Name)) + natif, err := nat.Parse(ctx.String(NATFlag.Name)) if err != nil { Fatalf("Option %s: %v", NATFlag.Name, err) } @@ -777,54 +847,54 @@ func splitAndTrim(input string) (ret []string) { // setHTTP creates the HTTP RPC listener interface string from the set // command line flags, returning empty if the HTTP endpoint is disabled. func setHTTP(ctx *cli.Context, cfg *node.Config) { - if ctx.GlobalBool(RPCEnabledFlag.Name) && cfg.HTTPHost == "" { + if ctx.Bool(RPCEnabledFlag.Name) && cfg.HTTPHost == "" { cfg.HTTPHost = "127.0.0.1" - if ctx.GlobalIsSet(RPCListenAddrFlag.Name) { - cfg.HTTPHost = ctx.GlobalString(RPCListenAddrFlag.Name) + if ctx.IsSet(RPCListenAddrFlag.Name) { + cfg.HTTPHost = ctx.String(RPCListenAddrFlag.Name) } } - if ctx.GlobalIsSet(RPCPortFlag.Name) { - cfg.HTTPPort = ctx.GlobalInt(RPCPortFlag.Name) + if ctx.IsSet(RPCPortFlag.Name) { + cfg.HTTPPort = ctx.Int(RPCPortFlag.Name) } - if ctx.GlobalIsSet(RPCHttpReadTimeoutFlag.Name) { - cfg.HTTPTimeouts.ReadTimeout = ctx.GlobalDuration(RPCHttpReadTimeoutFlag.Name) + if ctx.IsSet(RPCHttpReadTimeoutFlag.Name) { + cfg.HTTPTimeouts.ReadTimeout = ctx.Duration(RPCHttpReadTimeoutFlag.Name) } - if ctx.GlobalIsSet(RPCHttpWriteTimeoutFlag.Name) { - cfg.HTTPTimeouts.WriteTimeout = ctx.GlobalDuration(RPCHttpWriteTimeoutFlag.Name) + if ctx.IsSet(RPCHttpWriteTimeoutFlag.Name) { + cfg.HTTPTimeouts.WriteTimeout = ctx.Duration(RPCHttpWriteTimeoutFlag.Name) } - if ctx.GlobalIsSet(RPCHttpIdleTimeoutFlag.Name) { - cfg.HTTPTimeouts.IdleTimeout = ctx.GlobalDuration(RPCHttpIdleTimeoutFlag.Name) + if ctx.IsSet(RPCHttpIdleTimeoutFlag.Name) { + cfg.HTTPTimeouts.IdleTimeout = ctx.Duration(RPCHttpIdleTimeoutFlag.Name) } - if ctx.GlobalIsSet(RPCCORSDomainFlag.Name) { - cfg.HTTPCors = splitAndTrim(ctx.GlobalString(RPCCORSDomainFlag.Name)) + if ctx.IsSet(RPCCORSDomainFlag.Name) { + cfg.HTTPCors = splitAndTrim(ctx.String(RPCCORSDomainFlag.Name)) } - if ctx.GlobalIsSet(RPCApiFlag.Name) { - cfg.HTTPModules = splitAndTrim(ctx.GlobalString(RPCApiFlag.Name)) + if ctx.IsSet(RPCApiFlag.Name) { + cfg.HTTPModules = splitAndTrim(ctx.String(RPCApiFlag.Name)) } - if ctx.GlobalIsSet(RPCVirtualHostsFlag.Name) { - cfg.HTTPVirtualHosts = splitAndTrim(ctx.GlobalString(RPCVirtualHostsFlag.Name)) + if ctx.IsSet(RPCVirtualHostsFlag.Name) { + cfg.HTTPVirtualHosts = splitAndTrim(ctx.String(RPCVirtualHostsFlag.Name)) } } // setWS creates the WebSocket RPC listener interface string from the set // command line flags, returning empty if the HTTP endpoint is disabled. func setWS(ctx *cli.Context, cfg *node.Config) { - if ctx.GlobalBool(WSEnabledFlag.Name) && cfg.WSHost == "" { + if ctx.Bool(WSEnabledFlag.Name) && cfg.WSHost == "" { cfg.WSHost = "127.0.0.1" - if ctx.GlobalIsSet(WSListenAddrFlag.Name) { - cfg.WSHost = ctx.GlobalString(WSListenAddrFlag.Name) + if ctx.IsSet(WSListenAddrFlag.Name) { + cfg.WSHost = ctx.String(WSListenAddrFlag.Name) } } - if ctx.GlobalIsSet(WSPortFlag.Name) { - cfg.WSPort = ctx.GlobalInt(WSPortFlag.Name) + if ctx.IsSet(WSPortFlag.Name) { + cfg.WSPort = ctx.Int(WSPortFlag.Name) } - if ctx.GlobalIsSet(WSAllowedOriginsFlag.Name) { - cfg.WSOrigins = splitAndTrim(ctx.GlobalString(WSAllowedOriginsFlag.Name)) + if ctx.IsSet(WSAllowedOriginsFlag.Name) { + cfg.WSOrigins = splitAndTrim(ctx.String(WSAllowedOriginsFlag.Name)) } - if ctx.GlobalIsSet(WSApiFlag.Name) { - cfg.WSModules = splitAndTrim(ctx.GlobalString(WSApiFlag.Name)) + if ctx.IsSet(WSApiFlag.Name) { + cfg.WSModules = splitAndTrim(ctx.String(WSApiFlag.Name)) } } @@ -833,10 +903,10 @@ func setWS(ctx *cli.Context, cfg *node.Config) { func setIPC(ctx *cli.Context, cfg *node.Config) { checkExclusive(ctx, IPCDisabledFlag, IPCPathFlag) switch { - case ctx.GlobalBool(IPCDisabledFlag.Name): + case ctx.Bool(IPCDisabledFlag.Name): cfg.IPCPath = "" - case ctx.GlobalIsSet(IPCPathFlag.Name): - cfg.IPCPath = ctx.GlobalString(IPCPathFlag.Name) + case ctx.IsSet(IPCPathFlag.Name): + cfg.IPCPath = ctx.String(IPCPathFlag.Name) } } @@ -899,8 +969,8 @@ func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error // setEtherbase retrieves the etherbase either from the directly specified // command line flags or from the keystore if CLI indexed. func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *ethconfig.Config) { - if ctx.GlobalIsSet(EtherbaseFlag.Name) { - account, err := MakeAddress(ks, ctx.GlobalString(EtherbaseFlag.Name)) + if ctx.IsSet(EtherbaseFlag.Name) { + account, err := MakeAddress(ks, ctx.String(EtherbaseFlag.Name)) if err != nil { Fatalf("Option %q: %v", EtherbaseFlag.Name, err) } @@ -910,7 +980,7 @@ func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *ethconfig.Config // MakePasswordList reads password lines from the file specified by the global --password flag. func MakePasswordList(ctx *cli.Context) []string { - path := ctx.GlobalString(PasswordFileFlag.Name) + path := ctx.String(PasswordFileFlag.Name) if path == "" { return nil } @@ -933,20 +1003,20 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) { setBootstrapNodes(ctx, cfg) // setBootstrapNodesV5(ctx, cfg) - lightClient := ctx.GlobalBool(LightModeFlag.Name) || ctx.GlobalString(SyncModeFlag.Name) == "light" - lightServer := ctx.GlobalInt(LightServFlag.Name) != 0 - lightPeers := ctx.GlobalInt(LightPeersFlag.Name) + lightClient := ctx.Bool(LightModeFlag.Name) || ctx.String(SyncModeFlag.Name) == "light" + lightServer := ctx.Int(LightServFlag.Name) != 0 + lightPeers := ctx.Int(LightPeersFlag.Name) - if ctx.GlobalIsSet(MaxPeersFlag.Name) { - cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name) - if lightServer && !ctx.GlobalIsSet(LightPeersFlag.Name) { + if ctx.IsSet(MaxPeersFlag.Name) { + cfg.MaxPeers = ctx.Int(MaxPeersFlag.Name) + if lightServer && !ctx.IsSet(LightPeersFlag.Name) { cfg.MaxPeers += lightPeers } } else { if lightServer { cfg.MaxPeers += lightPeers } - if lightClient && ctx.GlobalIsSet(LightPeersFlag.Name) && cfg.MaxPeers < lightPeers { + if lightClient && ctx.IsSet(LightPeersFlag.Name) && cfg.MaxPeers < lightPeers { cfg.MaxPeers = lightPeers } } @@ -959,24 +1029,24 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) { } log.Info("Maximum peer count", "ETH", ethPeers, "LES", lightPeers, "total", cfg.MaxPeers) - if ctx.GlobalIsSet(MaxPendingPeersFlag.Name) { - cfg.MaxPendingPeers = ctx.GlobalInt(MaxPendingPeersFlag.Name) + if ctx.IsSet(MaxPendingPeersFlag.Name) { + cfg.MaxPendingPeers = ctx.Int(MaxPendingPeersFlag.Name) } - if ctx.GlobalIsSet(NoDiscoverFlag.Name) || lightClient { + if ctx.IsSet(NoDiscoverFlag.Name) || lightClient { cfg.NoDiscovery = true } // if we're running a light client or server, force enable the v5 peer discovery // unless it is explicitly disabled with --nodiscover note that explicitly specifying // --v5disc overrides --nodiscover, in which case the later only disables v4 discovery - forceV5Discovery := (lightClient || lightServer) && !ctx.GlobalBool(NoDiscoverFlag.Name) - if ctx.GlobalIsSet(DiscoveryV5Flag.Name) { - cfg.DiscoveryV5 = ctx.GlobalBool(DiscoveryV5Flag.Name) + forceV5Discovery := (lightClient || lightServer) && !ctx.Bool(NoDiscoverFlag.Name) + if ctx.IsSet(DiscoveryV5Flag.Name) { + cfg.DiscoveryV5 = ctx.Bool(DiscoveryV5Flag.Name) } else if forceV5Discovery { cfg.DiscoveryV5 = true } - if netrestrict := ctx.GlobalString(NetrestrictFlag.Name); netrestrict != "" { + if netrestrict := ctx.String(NetrestrictFlag.Name); netrestrict != "" { list, err := netutil.ParseNetlist(netrestrict) if err != nil { Fatalf("Option %q: %v", NetrestrictFlag.Name, err) @@ -984,7 +1054,7 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) { cfg.NetRestrict = list } - if ctx.GlobalBool(DeveloperFlag.Name) { + if ctx.Bool(DeveloperFlag.Name) { // --dev mode can't use p2p networking. cfg.MaxPeers = 0 cfg.ListenAddr = ":0" @@ -1003,27 +1073,27 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) { setPrefix(ctx, cfg) switch { - case ctx.GlobalIsSet(DataDirFlag.Name): - cfg.DataDir = ctx.GlobalString(DataDirFlag.Name) - case ctx.GlobalBool(DeveloperFlag.Name): + case ctx.IsSet(DataDirFlag.Name): + cfg.DataDir = ctx.String(DataDirFlag.Name) + case ctx.Bool(DeveloperFlag.Name): cfg.DataDir = "" // unless explicitly requested, use memory databases - case ctx.GlobalBool(TestnetFlag.Name): + case ctx.Bool(TestnetFlag.Name): cfg.DataDir = filepath.Join(node.DefaultDataDir(), "testnet") - case ctx.GlobalBool(RinkebyFlag.Name): + case ctx.Bool(RinkebyFlag.Name): cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby") } - if ctx.GlobalIsSet(KeyStoreDirFlag.Name) { - cfg.KeyStoreDir = ctx.GlobalString(KeyStoreDirFlag.Name) + if ctx.IsSet(KeyStoreDirFlag.Name) { + cfg.KeyStoreDir = ctx.String(KeyStoreDirFlag.Name) } - if ctx.GlobalIsSet(LightKDFFlag.Name) { - cfg.UseLightweightKDF = ctx.GlobalBool(LightKDFFlag.Name) + if ctx.IsSet(LightKDFFlag.Name) { + cfg.UseLightweightKDF = ctx.Bool(LightKDFFlag.Name) } - if ctx.GlobalIsSet(NoUSBFlag.Name) { - cfg.NoUSB = ctx.GlobalBool(NoUSBFlag.Name) + if ctx.IsSet(NoUSBFlag.Name) { + cfg.NoUSB = ctx.Bool(NoUSBFlag.Name) } - if ctx.GlobalIsSet(AnnounceTxsFlag.Name) { - cfg.AnnounceTxs = ctx.GlobalBool(AnnounceTxsFlag.Name) + if ctx.IsSet(AnnounceTxsFlag.Name) { + cfg.AnnounceTxs = ctx.Bool(AnnounceTxsFlag.Name) } // deprecation notice for log debug flags (TODO: find a more appropriate place to put these?) if ctx.IsSet(LogBacktraceAtFlag.Name) { @@ -1040,71 +1110,71 @@ func setGPO(ctx *cli.Context, cfg *gasprice.Config, light bool) { if light { *cfg = ethconfig.LightClientGPO } - if ctx.GlobalIsSet(GpoBlocksFlag.Name) { - cfg.Blocks = ctx.GlobalInt(GpoBlocksFlag.Name) + if ctx.IsSet(GpoBlocksFlag.Name) { + cfg.Blocks = ctx.Int(GpoBlocksFlag.Name) } - if ctx.GlobalIsSet(GpoPercentileFlag.Name) { - cfg.Percentile = ctx.GlobalInt(GpoPercentileFlag.Name) + if ctx.IsSet(GpoPercentileFlag.Name) { + cfg.Percentile = ctx.Int(GpoPercentileFlag.Name) } - if ctx.GlobalIsSet(GpoMaxGasPriceFlag.Name) { - cfg.MaxPrice = big.NewInt(ctx.GlobalInt64(GpoMaxGasPriceFlag.Name)) + if ctx.IsSet(GpoMaxGasPriceFlag.Name) { + cfg.MaxPrice = big.NewInt(ctx.Int64(GpoMaxGasPriceFlag.Name)) } - if ctx.GlobalIsSet(GpoIgnoreGasPriceFlag.Name) { - cfg.IgnorePrice = big.NewInt(ctx.GlobalInt64(GpoIgnoreGasPriceFlag.Name)) + if ctx.IsSet(GpoIgnoreGasPriceFlag.Name) { + cfg.IgnorePrice = big.NewInt(ctx.Int64(GpoIgnoreGasPriceFlag.Name)) } } func setTxPool(ctx *cli.Context, cfg *txpool.Config) { - if ctx.GlobalIsSet(TxPoolNoLocalsFlag.Name) { - cfg.NoLocals = ctx.GlobalBool(TxPoolNoLocalsFlag.Name) + if ctx.IsSet(TxPoolNoLocalsFlag.Name) { + cfg.NoLocals = ctx.Bool(TxPoolNoLocalsFlag.Name) } - if ctx.GlobalIsSet(TxPoolJournalFlag.Name) { - cfg.Journal = ctx.GlobalString(TxPoolJournalFlag.Name) + if ctx.IsSet(TxPoolJournalFlag.Name) { + cfg.Journal = ctx.String(TxPoolJournalFlag.Name) } - if ctx.GlobalIsSet(TxPoolRejournalFlag.Name) { - cfg.Rejournal = ctx.GlobalDuration(TxPoolRejournalFlag.Name) + if ctx.IsSet(TxPoolRejournalFlag.Name) { + cfg.Rejournal = ctx.Duration(TxPoolRejournalFlag.Name) } - if ctx.GlobalIsSet(TxPoolPriceLimitFlag.Name) { - cfg.PriceLimit = ctx.GlobalUint64(TxPoolPriceLimitFlag.Name) + if ctx.IsSet(TxPoolPriceLimitFlag.Name) { + cfg.PriceLimit = ctx.Uint64(TxPoolPriceLimitFlag.Name) } - if ctx.GlobalIsSet(TxPoolPriceBumpFlag.Name) { - cfg.PriceBump = ctx.GlobalUint64(TxPoolPriceBumpFlag.Name) + if ctx.IsSet(TxPoolPriceBumpFlag.Name) { + cfg.PriceBump = ctx.Uint64(TxPoolPriceBumpFlag.Name) } - if ctx.GlobalIsSet(TxPoolAccountSlotsFlag.Name) { - cfg.AccountSlots = ctx.GlobalUint64(TxPoolAccountSlotsFlag.Name) + if ctx.IsSet(TxPoolAccountSlotsFlag.Name) { + cfg.AccountSlots = ctx.Uint64(TxPoolAccountSlotsFlag.Name) } - if ctx.GlobalIsSet(TxPoolGlobalSlotsFlag.Name) { - cfg.GlobalSlots = ctx.GlobalUint64(TxPoolGlobalSlotsFlag.Name) + if ctx.IsSet(TxPoolGlobalSlotsFlag.Name) { + cfg.GlobalSlots = ctx.Uint64(TxPoolGlobalSlotsFlag.Name) } - if ctx.GlobalIsSet(TxPoolAccountQueueFlag.Name) { - cfg.AccountQueue = ctx.GlobalUint64(TxPoolAccountQueueFlag.Name) + if ctx.IsSet(TxPoolAccountQueueFlag.Name) { + cfg.AccountQueue = ctx.Uint64(TxPoolAccountQueueFlag.Name) } - if ctx.GlobalIsSet(TxPoolGlobalQueueFlag.Name) { - cfg.GlobalQueue = ctx.GlobalUint64(TxPoolGlobalQueueFlag.Name) + if ctx.IsSet(TxPoolGlobalQueueFlag.Name) { + cfg.GlobalQueue = ctx.Uint64(TxPoolGlobalQueueFlag.Name) } - if ctx.GlobalIsSet(TxPoolLifetimeFlag.Name) { - cfg.Lifetime = ctx.GlobalDuration(TxPoolLifetimeFlag.Name) + if ctx.IsSet(TxPoolLifetimeFlag.Name) { + cfg.Lifetime = ctx.Duration(TxPoolLifetimeFlag.Name) } } func setEthash(ctx *cli.Context, cfg *ethconfig.Config) { - if ctx.GlobalIsSet(EthashCacheDirFlag.Name) { - cfg.Ethash.CacheDir = ctx.GlobalString(EthashCacheDirFlag.Name) + if ctx.IsSet(EthashCacheDirFlag.Name) { + cfg.Ethash.CacheDir = ctx.String(EthashCacheDirFlag.Name) } - if ctx.GlobalIsSet(EthashDatasetDirFlag.Name) { - cfg.Ethash.DatasetDir = ctx.GlobalString(EthashDatasetDirFlag.Name) + if ctx.IsSet(EthashDatasetDirFlag.Name) { + cfg.Ethash.DatasetDir = ctx.String(EthashDatasetDirFlag.Name) } - if ctx.GlobalIsSet(EthashCachesInMemoryFlag.Name) { - cfg.Ethash.CachesInMem = ctx.GlobalInt(EthashCachesInMemoryFlag.Name) + if ctx.IsSet(EthashCachesInMemoryFlag.Name) { + cfg.Ethash.CachesInMem = ctx.Int(EthashCachesInMemoryFlag.Name) } - if ctx.GlobalIsSet(EthashCachesOnDiskFlag.Name) { - cfg.Ethash.CachesOnDisk = ctx.GlobalInt(EthashCachesOnDiskFlag.Name) + if ctx.IsSet(EthashCachesOnDiskFlag.Name) { + cfg.Ethash.CachesOnDisk = ctx.Int(EthashCachesOnDiskFlag.Name) } - if ctx.GlobalIsSet(EthashDatasetsInMemoryFlag.Name) { - cfg.Ethash.DatasetsInMem = ctx.GlobalInt(EthashDatasetsInMemoryFlag.Name) + if ctx.IsSet(EthashDatasetsInMemoryFlag.Name) { + cfg.Ethash.DatasetsInMem = ctx.Int(EthashDatasetsInMemoryFlag.Name) } - if ctx.GlobalIsSet(EthashDatasetsOnDiskFlag.Name) { - cfg.Ethash.DatasetsOnDisk = ctx.GlobalInt(EthashDatasetsOnDiskFlag.Name) + if ctx.IsSet(EthashDatasetsOnDiskFlag.Name) { + cfg.Ethash.DatasetsOnDisk = ctx.Int(EthashDatasetsOnDiskFlag.Name) } } @@ -1120,13 +1190,13 @@ func checkExclusive(ctx *cli.Context, args ...interface{}) { panic(fmt.Sprintf("invalid argument, not cli.Flag type: %T", args[i])) } // Check if next arg extends current and expand its name if so - name := flag.GetName() + name := flag.Names()[0] if i+1 < len(args) { switch option := args[i+1].(type) { case string: // Extended flag, expand the name and shift the arguments - if ctx.GlobalString(flag.GetName()) == option { + if ctx.String(flag.Names()[0]) == option { name += "=" + option } i++ @@ -1137,7 +1207,7 @@ func checkExclusive(ctx *cli.Context, args ...interface{}) { } } // Mark the flag if it's set - if ctx.GlobalIsSet(flag.GetName()) { + if ctx.IsSet(flag.Names()[0]) { set = append(set, "--"+name) } } @@ -1147,8 +1217,8 @@ func checkExclusive(ctx *cli.Context, args ...interface{}) { } func SetXDCXConfig(ctx *cli.Context, cfg *XDCx.Config, XDCDataDir string) { - if ctx.GlobalIsSet(XDCXDataDirFlag.Name) { - cfg.DataDir = ctx.GlobalString(XDCXDataDirFlag.Name) + if ctx.IsSet(XDCXDataDirFlag.Name) { + cfg.DataDir = ctx.String(XDCXDataDirFlag.Name) } else { // default XDCx datadir: DATADIR/XDCx defaultXDCXDataDir := filepath.Join(XDCDataDir, "XDCx") @@ -1162,23 +1232,23 @@ func SetXDCXConfig(ctx *cli.Context, cfg *XDCx.Config, XDCDataDir string) { } } log.Info("XDCX datadir", "path", cfg.DataDir) - if ctx.GlobalIsSet(XDCXDBEngineFlag.Name) { - cfg.DBEngine = ctx.GlobalString(XDCXDBEngineFlag.Name) + if ctx.IsSet(XDCXDBEngineFlag.Name) { + cfg.DBEngine = ctx.String(XDCXDBEngineFlag.Name) } else { cfg.DBEngine = XDCXDBEngineFlag.Value } - if ctx.GlobalIsSet(XDCXDBNameFlag.Name) { - cfg.DBName = ctx.GlobalString(XDCXDBNameFlag.Name) + if ctx.IsSet(XDCXDBNameFlag.Name) { + cfg.DBName = ctx.String(XDCXDBNameFlag.Name) } else { cfg.DBName = XDCXDBNameFlag.Value } - if ctx.GlobalIsSet(XDCXDBConnectionUrlFlag.Name) { - cfg.ConnectionUrl = ctx.GlobalString(XDCXDBConnectionUrlFlag.Name) + if ctx.IsSet(XDCXDBConnectionUrlFlag.Name) { + cfg.ConnectionUrl = ctx.String(XDCXDBConnectionUrlFlag.Name) } else { cfg.ConnectionUrl = XDCXDBConnectionUrlFlag.Value } - if ctx.GlobalIsSet(XDCXDBReplicaSetNameFlag.Name) { - cfg.ReplicaSetName = ctx.GlobalString(XDCXDBReplicaSetNameFlag.Name) + if ctx.IsSet(XDCXDBReplicaSetNameFlag.Name) { + cfg.ReplicaSetName = ctx.String(XDCXDBReplicaSetNameFlag.Name) } } @@ -1192,7 +1262,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) setEtherbase(ctx, ks, cfg) - setGPO(ctx, &cfg.GPO, ctx.GlobalString(SyncModeFlag.Name) == "light") + setGPO(ctx, &cfg.GPO, ctx.String(SyncModeFlag.Name) == "light") setTxPool(ctx, &cfg.TxPool) setEthash(ctx, cfg) @@ -1217,70 +1287,70 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { godebug.SetGCPercent(int(gogc)) switch { - case ctx.GlobalIsSet(SyncModeFlag.Name): - cfg.SyncMode = *GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode) - case ctx.GlobalBool(FastSyncFlag.Name): + case ctx.IsSet(SyncModeFlag.Name): + cfg.SyncMode = *flags.GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode) + case ctx.Bool(FastSyncFlag.Name): cfg.SyncMode = downloader.FastSync - case ctx.GlobalBool(LightModeFlag.Name): + case ctx.Bool(LightModeFlag.Name): cfg.SyncMode = downloader.LightSync } - if ctx.GlobalIsSet(LightServFlag.Name) { - cfg.LightServ = ctx.GlobalInt(LightServFlag.Name) + if ctx.IsSet(LightServFlag.Name) { + cfg.LightServ = ctx.Int(LightServFlag.Name) } - if ctx.GlobalIsSet(LightPeersFlag.Name) { - cfg.LightPeers = ctx.GlobalInt(LightPeersFlag.Name) + if ctx.IsSet(LightPeersFlag.Name) { + cfg.LightPeers = ctx.Int(LightPeersFlag.Name) } - if ctx.GlobalIsSet(NetworkIdFlag.Name) { - cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name) + if ctx.IsSet(NetworkIdFlag.Name) { + cfg.NetworkId = ctx.Uint64(NetworkIdFlag.Name) } - if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheDatabaseFlag.Name) { - cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100 + if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheDatabaseFlag.Name) { + cfg.DatabaseCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheDatabaseFlag.Name) / 100 } - cfg.DatabaseHandles = MakeDatabaseHandles(ctx.GlobalInt(FDLimitFlag.Name)) + cfg.DatabaseHandles = MakeDatabaseHandles(ctx.Int(FDLimitFlag.Name)) - if gcmode := ctx.GlobalString(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" { + if gcmode := ctx.String(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" { Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name) } - cfg.NoPruning = ctx.GlobalString(GCModeFlag.Name) == "archive" + cfg.NoPruning = ctx.String(GCModeFlag.Name) == "archive" - if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheGCFlag.Name) { - cfg.TrieCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100 + if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheGCFlag.Name) { + cfg.TrieCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheGCFlag.Name) / 100 } - if ctx.GlobalIsSet(StakerThreadsFlag.Name) { - cfg.MinerThreads = ctx.GlobalInt(StakerThreadsFlag.Name) + if ctx.IsSet(StakerThreadsFlag.Name) { + cfg.MinerThreads = ctx.Int(StakerThreadsFlag.Name) } - if ctx.GlobalIsSet(DocRootFlag.Name) { - cfg.DocRoot = ctx.GlobalString(DocRootFlag.Name) + if ctx.IsSet(DocRootFlag.Name) { + cfg.DocRoot = ctx.String(DocRootFlag.Name) } - if ctx.GlobalIsSet(RPCGlobalGasCapFlag.Name) { - cfg.RPCGasCap = ctx.GlobalUint64(RPCGlobalGasCapFlag.Name) + if ctx.IsSet(RPCGlobalGasCapFlag.Name) { + cfg.RPCGasCap = ctx.Uint64(RPCGlobalGasCapFlag.Name) } - if ctx.GlobalIsSet(RPCGlobalTxFeeCap.Name) { - cfg.RPCTxFeeCap = ctx.GlobalFloat64(RPCGlobalTxFeeCap.Name) + if ctx.IsSet(RPCGlobalTxFeeCap.Name) { + cfg.RPCTxFeeCap = ctx.Float64(RPCGlobalTxFeeCap.Name) } - if ctx.GlobalIsSet(RPCGlobalGasCapFlag.Name) { - cfg.RPCGasCap = ctx.GlobalUint64(RPCGlobalGasCapFlag.Name) + if ctx.IsSet(RPCGlobalGasCapFlag.Name) { + cfg.RPCGasCap = ctx.Uint64(RPCGlobalGasCapFlag.Name) } - if ctx.GlobalIsSet(ExtraDataFlag.Name) { - cfg.ExtraData = []byte(ctx.GlobalString(ExtraDataFlag.Name)) + if ctx.IsSet(ExtraDataFlag.Name) { + cfg.ExtraData = []byte(ctx.String(ExtraDataFlag.Name)) } - if ctx.GlobalIsSet(GasPriceFlag.Name) { - cfg.GasPrice = GlobalBig(ctx, GasPriceFlag.Name) + if ctx.IsSet(GasPriceFlag.Name) { + cfg.GasPrice = flags.GlobalBig(ctx, GasPriceFlag.Name) } if ctx.IsSet(CacheLogSizeFlag.Name) { cfg.FilterLogCacheSize = ctx.Int(CacheLogSizeFlag.Name) } - if ctx.GlobalIsSet(VMEnableDebugFlag.Name) { + if ctx.IsSet(VMEnableDebugFlag.Name) { // TODO(fjl): force-enable this in --dev mode - cfg.EnablePreimageRecording = ctx.GlobalBool(VMEnableDebugFlag.Name) + cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name) } if cfg.RPCGasCap != 0 { log.Info("Set global gas cap", "cap", cfg.RPCGasCap) } else { log.Info("Global gas cap disabled") } - if ctx.GlobalIsSet(StoreRewardFlag.Name) { + if ctx.IsSet(StoreRewardFlag.Name) { common.StoreRewardFolder = filepath.Join(stack.DataDir(), "XDC", "rewards") if _, err := os.Stat(common.StoreRewardFolder); os.IsNotExist(err) { os.Mkdir(common.StoreRewardFolder, os.ModePerm) @@ -1288,17 +1358,17 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { } // Override any default configs for hard coded networks. switch { - case ctx.GlobalBool(TestnetFlag.Name): - if !ctx.GlobalIsSet(NetworkIdFlag.Name) { + case ctx.Bool(TestnetFlag.Name): + if !ctx.IsSet(NetworkIdFlag.Name) { cfg.NetworkId = 3 } cfg.Genesis = core.DefaultTestnetGenesisBlock() - case ctx.GlobalBool(RinkebyFlag.Name): - if !ctx.GlobalIsSet(NetworkIdFlag.Name) { + case ctx.Bool(RinkebyFlag.Name): + if !ctx.IsSet(NetworkIdFlag.Name) { cfg.NetworkId = 4 } cfg.Genesis = core.DefaultRinkebyGenesisBlock() - case ctx.GlobalBool(DeveloperFlag.Name): + case ctx.Bool(DeveloperFlag.Name): // Create new developer account or reuse existing one var ( developer accounts.Account @@ -1317,8 +1387,8 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { } log.Info("Using developer account", "address", developer.Address) - cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.GlobalInt(DeveloperPeriodFlag.Name)), developer.Address) - if !ctx.GlobalIsSet(GasPriceFlag.Name) { + cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.Int(DeveloperPeriodFlag.Name)), developer.Address) + if !ctx.IsSet(GasPriceFlag.Name) { cfg.GasPrice = big.NewInt(1) } } @@ -1328,17 +1398,17 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { // SetupNetwork configures the system for either the main net or some test network. func SetupNetwork(ctx *cli.Context) { // TODO(fjl): move target gas limit into config - params.TargetGasLimit = ctx.GlobalUint64(TargetGasLimitFlag.Name) + params.TargetGasLimit = ctx.Uint64(TargetGasLimitFlag.Name) } // MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails. func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database { var ( - cache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100 - handles = MakeDatabaseHandles(ctx.GlobalInt(FDLimitFlag.Name)) + cache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheDatabaseFlag.Name) / 100 + handles = MakeDatabaseHandles(ctx.Int(FDLimitFlag.Name)) ) name := "chaindata" - if ctx.GlobalBool(LightModeFlag.Name) { + if ctx.Bool(LightModeFlag.Name) { name = "lightchaindata" } chainDb, err := stack.OpenDatabase(name, cache, handles, "") @@ -1351,11 +1421,11 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database { func MakeGenesis(ctx *cli.Context) *core.Genesis { var genesis *core.Genesis switch { - case ctx.GlobalBool(TestnetFlag.Name): + case ctx.Bool(TestnetFlag.Name): genesis = core.DefaultTestnetGenesisBlock() - case ctx.GlobalBool(RinkebyFlag.Name): + case ctx.Bool(RinkebyFlag.Name): genesis = core.DefaultRinkebyGenesisBlock() - case ctx.GlobalBool(DeveloperFlag.Name): + case ctx.Bool(DeveloperFlag.Name): Fatalf("Developer chains are ephemeral") } return genesis @@ -1375,7 +1445,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai engine = XDPoS.New(config, chainDb) } else { engine = ethash.NewFaker() - if !ctx.GlobalBool(FakePoWFlag.Name) { + if !ctx.Bool(FakePoWFlag.Name) { engine = ethash.New(ethash.Config{ CacheDir: stack.ResolvePath(ethconfig.Defaults.Ethash.CacheDir), CachesInMem: ethconfig.Defaults.Ethash.CachesInMem, @@ -1387,18 +1457,18 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai } Fatalf("Only support XDPoS consensus") } - if gcmode := ctx.GlobalString(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" { + if gcmode := ctx.String(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" { Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name) } cache := &core.CacheConfig{ - Disabled: ctx.GlobalString(GCModeFlag.Name) == "archive", + Disabled: ctx.String(GCModeFlag.Name) == "archive", TrieNodeLimit: ethconfig.Defaults.TrieCache, TrieTimeLimit: ethconfig.Defaults.TrieTimeout, } - if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheGCFlag.Name) { - cache.TrieNodeLimit = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100 + if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheGCFlag.Name) { + cache.TrieNodeLimit = ctx.Int(CacheFlag.Name) * ctx.Int(CacheGCFlag.Name) / 100 } - vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)} + vmcfg := vm.Config{EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name)} chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg) if err != nil { Fatalf("Can't create BlockChain: %v", err) @@ -1410,43 +1480,19 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai // scripts to preload before starting. func MakeConsolePreloads(ctx *cli.Context) []string { // Skip preloading if there's nothing to preload - if ctx.GlobalString(PreloadJSFlag.Name) == "" { + if ctx.String(PreloadJSFlag.Name) == "" { return nil } // Otherwise resolve absolute paths and return them preloads := []string{} - assets := ctx.GlobalString(JSpathFlag.Name) - for _, file := range strings.Split(ctx.GlobalString(PreloadJSFlag.Name), ",") { + assets := ctx.String(JSpathFlag.Name) + for _, file := range strings.Split(ctx.String(PreloadJSFlag.Name), ",") { preloads = append(preloads, common.AbsolutePath(assets, strings.TrimSpace(file))) } return preloads } -// MigrateFlags sets the global flag from a local flag when it's set. -// This is a temporary function used for migrating old command/flags to the -// new format. -// -// e.g. XDC account new --keystore /tmp/mykeystore --lightkdf -// -// is equivalent after calling this method with: -// -// XDC --keystore /tmp/mykeystore --lightkdf account new -// -// This allows the use of the existing configuration functionality. -// When all flags are migrated this function can be removed and the existing -// configuration functionality must be changed that is uses local flags -func MigrateFlags(action func(ctx *cli.Context) error) func(*cli.Context) error { - return func(ctx *cli.Context) error { - for _, name := range ctx.FlagNames() { - if ctx.IsSet(name) { - ctx.GlobalSet(name, ctx.String(name)) - } - } - return action(ctx) - } -} - // find all filenames match the given pattern in the given root directory func WalkMatch(root, pattern string) ([]string, error) { matches := []string{} @@ -1487,8 +1533,8 @@ func SetupMetrics(ctx *cli.Context) { if metrics.Enabled { log.Info("Enabling metrics collection") - if ctx.GlobalIsSet(MetricsHTTPFlag.Name) { - address := fmt.Sprintf("%s:%d", ctx.GlobalString(MetricsHTTPFlag.Name), ctx.GlobalInt(MetricsPortFlag.Name)) + if ctx.IsSet(MetricsHTTPFlag.Name) { + address := fmt.Sprintf("%s:%d", ctx.String(MetricsHTTPFlag.Name), ctx.Int(MetricsPortFlag.Name)) log.Info("Enabling stand-alone metrics HTTP endpoint", "address", address) exp.Setup(address) } diff --git a/cmd/utils/flags_legacy.go b/cmd/utils/flags_legacy.go new file mode 100644 index 000000000000..b47a74595613 --- /dev/null +++ b/cmd/utils/flags_legacy.go @@ -0,0 +1,84 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package utils + +import ( + "fmt" + + "github.com/XinFinOrg/XDPoSChain/internal/flags" + "github.com/urfave/cli/v2" +) + +var ShowDeprecated = &cli.Command{ + Action: showDeprecated, + Name: "show-deprecated-flags", + Usage: "Show flags that have been deprecated", + ArgsUsage: " ", + Description: "Show flags that have been deprecated and will soon be removed", +} + +var DeprecatedFlags = []cli.Flag{ + FastSyncFlag, + LightModeFlag, + NoUSBFlag, + LogBacktraceAtFlag, + LogDebugFlag, +} + +var ( + FastSyncFlag = &cli.BoolFlag{ + Name: "fast", + Usage: "Enable fast syncing through state downloads", + Category: flags.DeprecatedCategory, + } + LightModeFlag = &cli.BoolFlag{ + Name: "light", + Usage: "Enable light client mode", + Category: flags.DeprecatedCategory, + } + // (Deprecated May 2020, shown in aliased flags section) + NoUSBFlag = &cli.BoolFlag{ + Name: "nousb", + Usage: "Disables monitoring for and managing USB hardware wallets (deprecated)", + Category: flags.DeprecatedCategory, + } + // Deprecated November 2023 + LogBacktraceAtFlag = &cli.StringFlag{ + Name: "log-backtrace", + Usage: "Request a stack trace at a specific logging statement (deprecated)", + Value: "", + Category: flags.DeprecatedCategory, + } + LogDebugFlag = &cli.BoolFlag{ + Name: "log-debug", + Usage: "Prepends log messages with call-site location (deprecated)", + Category: flags.DeprecatedCategory, + } +) + +// showDeprecated displays deprecated flags that will be soon removed from the codebase. +func showDeprecated(*cli.Context) error { + fmt.Println("--------------------------------------------------------------------") + fmt.Println("The following flags are deprecated and will be removed in the future!") + fmt.Println("--------------------------------------------------------------------") + fmt.Println() + for _, flag := range DeprecatedFlags { + fmt.Println(flag.String()) + } + fmt.Println() + return nil +} diff --git a/go.mod b/go.mod index a519dba12ac1..11576fdce6b5 100644 --- a/go.mod +++ b/go.mod @@ -41,7 +41,6 @@ require ( gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772 - gopkg.in/urfave/cli.v1 v1.20.0 ) require ( @@ -50,13 +49,14 @@ require ( github.com/kylelemons/godebug v1.1.0 github.com/mattn/go-isatty v0.0.17 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible - golang.org/x/exp v0.0.0-20231006140011-7918f672742d + github.com/urfave/cli/v2 v2.27.5 gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) require ( github.com/StackExchange/wmi v1.2.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/dlclark/regexp2 v1.10.0 // indirect github.com/go-ole/go-ole v1.2.5 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect @@ -69,7 +69,9 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect golang.org/x/mod v0.13.0 // indirect golang.org/x/term v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect diff --git a/go.sum b/go.sum index d21c1f26a13a..782d2654a694 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,8 @@ github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -132,6 +134,8 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE= @@ -144,12 +148,14 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= +github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -215,8 +221,6 @@ gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772 h1:hhsSf/5z74 gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= -gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/internal/cmdtest/test_cmd.go b/internal/cmdtest/test_cmd.go index 45d10897ef4e..5369678adb86 100644 --- a/internal/cmdtest/test_cmd.go +++ b/internal/cmdtest/test_cmd.go @@ -26,6 +26,7 @@ import ( "regexp" "strings" "sync" + "syscall" "testing" "text/template" "time" @@ -49,6 +50,8 @@ type TestCmd struct { stdout *bufio.Reader stdin io.WriteCloser stderr *testlogger + // Err will contain the process exit error or interrupt signal error + Err error } // Run exec's the current binary using name as argv[0] which will trigger the @@ -181,11 +184,25 @@ func (tt *TestCmd) ExpectExit() { } func (tt *TestCmd) WaitExit() { - tt.cmd.Wait() + tt.Err = tt.cmd.Wait() } func (tt *TestCmd) Interrupt() { - tt.cmd.Process.Signal(os.Interrupt) + tt.Err = tt.cmd.Process.Signal(os.Interrupt) +} + +// ExitStatus exposes the process' OS exit code +// It will only return a valid value after the process has finished. +func (tt *TestCmd) ExitStatus() int { + if tt.Err != nil { + exitErr := tt.Err.(*exec.ExitError) + if exitErr != nil { + if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { + return status.ExitStatus() + } + } + } + return 0 } // StderrText returns any stderr output written so far. diff --git a/internal/debug/flags.go b/internal/debug/flags.go index 6534e2be1c9c..6a366778c141 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -26,105 +26,127 @@ import ( "path/filepath" "runtime" + "github.com/XinFinOrg/XDPoSChain/internal/flags" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/metrics" "github.com/XinFinOrg/XDPoSChain/metrics/exp" "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" + "github.com/urfave/cli/v2" "gopkg.in/natefinch/lumberjack.v2" - "gopkg.in/urfave/cli.v1" ) var ( - verbosityFlag = cli.IntFlag{ - Name: "verbosity", - Usage: "Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail", - Value: 3, + verbosityFlag = &cli.IntFlag{ + Name: "verbosity", + Usage: "Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail", + Value: 3, + Category: flags.LoggingCategory, } logVmoduleFlag = &cli.StringFlag{ - Name: "log-vmodule", - Usage: "Per-module verbosity: comma-separated list of = (e.g. eth/*=5,p2p=4)", - Value: "", + Name: "log-vmodule", + Usage: "Per-module verbosity: comma-separated list of = (e.g. eth/*=5,p2p=4)", + Value: "", + Category: flags.LoggingCategory, } - vmoduleFlag = cli.StringFlag{ - Name: "vmodule", - Usage: "Per-module verbosity: comma-separated list of = (e.g. eth/*=5,p2p=4)", - Value: "", + vmoduleFlag = &cli.StringFlag{ + Name: "vmodule", + Usage: "Per-module verbosity: comma-separated list of = (e.g. eth/*=5,p2p=4)", + Value: "", + Category: flags.LoggingCategory, } logjsonFlag = &cli.BoolFlag{ - Name: "log-json", - Usage: "Format logs with JSON", - Hidden: true, + Name: "log-json", + Usage: "Format logs with JSON", + Hidden: true, + Category: flags.LoggingCategory, } logFormatFlag = &cli.StringFlag{ - Name: "log-format", - Usage: "Log format to use (json|logfmt|terminal)", + Name: "log-format", + Usage: "Log format to use (json|logfmt|terminal)", + Category: flags.LoggingCategory, } logFileFlag = &cli.StringFlag{ - Name: "log-file", - Usage: "Write logs to a file", + Name: "log-file", + Usage: "Write logs to a file", + Category: flags.LoggingCategory, } logRotateFlag = &cli.BoolFlag{ - Name: "log-rotate", - Usage: "Enables log file rotation", + Name: "log-rotate", + Usage: "Enables log file rotation", + Category: flags.LoggingCategory, } logMaxSizeMBsFlag = &cli.IntFlag{ - Name: "log-maxsize", - Usage: "Maximum size in MBs of a single log file", - Value: 100, + Name: "log-maxsize", + Usage: "Maximum size in MBs of a single log file", + Value: 100, + Category: flags.LoggingCategory, } logMaxBackupsFlag = &cli.IntFlag{ - Name: "log-maxbackups", - Usage: "Maximum number of log files to retain", - Value: 10, + Name: "log-maxbackups", + Usage: "Maximum number of log files to retain", + Value: 10, + Category: flags.LoggingCategory, } logMaxAgeFlag = &cli.IntFlag{ - Name: "log-maxage", - Usage: "Maximum number of days to retain a log file", - Value: 30, + Name: "log-maxage", + Usage: "Maximum number of days to retain a log file", + Value: 30, + Category: flags.LoggingCategory, } logCompressFlag = &cli.BoolFlag{ - Name: "log-compress", - Usage: "Compress the log files", - } - pprofFlag = cli.BoolFlag{ - Name: "pprof", - Usage: "Enable the pprof HTTP server", - } - pprofPortFlag = cli.IntFlag{ - Name: "pprofport", - Usage: "pprof HTTP server listening port", - Value: 6060, - } - pprofAddrFlag = cli.StringFlag{ - Name: "pprofaddr", - Usage: "pprof HTTP server listening interface", - Value: "127.0.0.1", - } - memprofilerateFlag = cli.IntFlag{ - Name: "memprofilerate", - Usage: "Turn on memory profiling with the given rate", - Value: runtime.MemProfileRate, - } - blockprofilerateFlag = cli.IntFlag{ - Name: "blockprofilerate", - Usage: "Turn on block profiling with the given rate", - } - cpuprofileFlag = cli.StringFlag{ - Name: "cpuprofile", - Usage: "Write CPU profile to the given file", - } - traceFlag = cli.StringFlag{ - Name: "trace", - Usage: "Write execution trace to the given file", - } - periodicProfilingFlag = cli.BoolFlag{ - Name: "periodicprofile", - Usage: "Periodically profile cpu and memory status", - } - debugDataDirFlag = cli.StringFlag{ - Name: "debugdatadir", - Usage: "Debug Data directory for profiling output", + Name: "log-compress", + Usage: "Compress the log files", + Value: false, + Category: flags.LoggingCategory, + } + pprofFlag = &cli.BoolFlag{ + Name: "pprof", + Usage: "Enable the pprof HTTP server", + Category: flags.LoggingCategory, + } + pprofPortFlag = &cli.IntFlag{ + Name: "pprofport", + Usage: "pprof HTTP server listening port", + Value: 6060, + Category: flags.LoggingCategory, + } + pprofAddrFlag = &cli.StringFlag{ + Name: "pprofaddr", + Usage: "pprof HTTP server listening interface", + Value: "127.0.0.1", + Category: flags.LoggingCategory, + } + memprofilerateFlag = &cli.IntFlag{ + Name: "memprofilerate", + Usage: "Turn on memory profiling with the given rate", + Value: runtime.MemProfileRate, + Category: flags.LoggingCategory, + } + blockprofilerateFlag = &cli.IntFlag{ + Name: "blockprofilerate", + Usage: "Turn on block profiling with the given rate", + Category: flags.LoggingCategory, + } + cpuprofileFlag = &cli.StringFlag{ + Name: "cpuprofile", + Usage: "Write CPU profile to the given file", + Category: flags.LoggingCategory, + } + traceFlag = &cli.StringFlag{ + Name: "trace", + Usage: "Write execution trace to the given file", + Category: flags.LoggingCategory, + } + periodicProfilingFlag = &cli.BoolFlag{ + Name: "periodicprofile", + Usage: "Periodically profile cpu and memory status", + Category: flags.LoggingCategory, + } + debugDataDirFlag = &cli.StringFlag{ + Name: "debugdatadir", + Usage: "Debug Data directory for profiling output", + Category: flags.LoggingCategory, } ) @@ -178,11 +200,11 @@ func Setup(ctx *cli.Context) error { handler slog.Handler terminalOutput = io.Writer(os.Stderr) output io.Writer - logFmtFlag = ctx.GlobalString(logFormatFlag.Name) + logFmtFlag = ctx.String(logFormatFlag.Name) ) var ( - logFile = ctx.GlobalString(logFileFlag.Name) - rotation = ctx.GlobalBool(logRotateFlag.Name) + logFile = ctx.String(logFileFlag.Name) + rotation = ctx.Bool(logRotateFlag.Name) ) if len(logFile) > 0 { if err := validateLogLocation(filepath.Dir(logFile)); err != nil { @@ -205,10 +227,10 @@ func Setup(ctx *cli.Context) error { } logOutputFile = &lumberjack.Logger{ Filename: logFile, - MaxSize: ctx.GlobalInt(logMaxSizeMBsFlag.Name), - MaxBackups: ctx.GlobalInt(logMaxBackupsFlag.Name), - MaxAge: ctx.GlobalInt(logMaxAgeFlag.Name), - Compress: ctx.GlobalBool(logCompressFlag.Name), + MaxSize: ctx.Int(logMaxSizeMBsFlag.Name), + MaxBackups: ctx.Int(logMaxBackupsFlag.Name), + MaxAge: ctx.Int(logMaxAgeFlag.Name), + Compress: ctx.Bool(logCompressFlag.Name), } output = io.MultiWriter(terminalOutput, logOutputFile) } else if logFile != "" { @@ -223,7 +245,7 @@ func Setup(ctx *cli.Context) error { } switch { - case ctx.GlobalBool(logjsonFlag.Name): + case ctx.Bool(logjsonFlag.Name): // Retain backwards compatibility with `--log-json` flag if `--log-format` not set defer log.Warn("The flag '--log-json' is deprecated, please use '--log-format=json' instead") handler = log.JSONHandlerWithLevel(output, log.LevelInfo) @@ -244,18 +266,18 @@ func Setup(ctx *cli.Context) error { handler = log.NewTerminalHandler(output, useColor) default: // Unknown log format specified - return fmt.Errorf("unknown log format: %v", ctx.GlobalString(logFormatFlag.Name)) + return fmt.Errorf("unknown log format: %v", ctx.String(logFormatFlag.Name)) } glogger = log.NewGlogHandler(handler) // logging - verbosity := log.FromLegacyLevel(ctx.GlobalInt(verbosityFlag.Name)) + verbosity := log.FromLegacyLevel(ctx.Int(verbosityFlag.Name)) glogger.Verbosity(verbosity) - vmodule := ctx.GlobalString(logVmoduleFlag.Name) + vmodule := ctx.String(logVmoduleFlag.Name) if vmodule == "" { // Retain backwards compatibility with `--vmodule` flag if `--log-vmodule` not set - vmodule = ctx.GlobalString(vmoduleFlag.Name) + vmodule = ctx.String(vmoduleFlag.Name) if vmodule != "" { defer log.Warn("The flag '--vmodule' is deprecated, please use '--log-vmodule' instead") } @@ -265,31 +287,31 @@ func Setup(ctx *cli.Context) error { log.SetDefault(log.NewLogger(glogger)) // profiling, tracing - runtime.MemProfileRate = ctx.GlobalInt(memprofilerateFlag.Name) - Handler.SetBlockProfileRate(ctx.GlobalInt(blockprofilerateFlag.Name)) - if traceFile := ctx.GlobalString(traceFlag.Name); traceFile != "" { + runtime.MemProfileRate = ctx.Int(memprofilerateFlag.Name) + Handler.SetBlockProfileRate(ctx.Int(blockprofilerateFlag.Name)) + if traceFile := ctx.String(traceFlag.Name); traceFile != "" { if err := Handler.StartGoTrace(traceFile); err != nil { return err } } - if cpuFile := ctx.GlobalString(cpuprofileFlag.Name); cpuFile != "" { + if cpuFile := ctx.String(cpuprofileFlag.Name); cpuFile != "" { if err := Handler.StartCPUProfile(cpuFile); err != nil { return err } } - Handler.filePath = ctx.GlobalString(debugDataDirFlag.Name) + Handler.filePath = ctx.String(debugDataDirFlag.Name) - if periodicProfiling := ctx.GlobalBool(periodicProfilingFlag.Name); periodicProfiling { + if periodicProfiling := ctx.Bool(periodicProfilingFlag.Name); periodicProfiling { Handler.PeriodicComputeProfiling() } // pprof server - if ctx.GlobalBool(pprofFlag.Name) { + if ctx.Bool(pprofFlag.Name) { // Hook go-metrics into expvar on any /debug/metrics request, load all vars // from the registry into expvar, and execute regular expvar handler. exp.Exp(metrics.DefaultRegistry) - address := fmt.Sprintf("%s:%d", ctx.GlobalString(pprofAddrFlag.Name), ctx.GlobalInt(pprofPortFlag.Name)) + address := fmt.Sprintf("%s:%d", ctx.String(pprofAddrFlag.Name), ctx.Int(pprofPortFlag.Name)) go func() { log.Info("Starting pprof server", "addr", fmt.Sprintf("http://%s/debug/pprof", address)) if err := http.ListenAndServe(address, nil); err != nil { diff --git a/internal/flags/categories.go b/internal/flags/categories.go new file mode 100644 index 000000000000..d58534c0b6bc --- /dev/null +++ b/internal/flags/categories.go @@ -0,0 +1,45 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package flags + +import "github.com/urfave/cli/v2" + +const ( + EthCategory = "ETHEREUM" + LightCategory = "LIGHT CLIENT" + DevCategory = "DEVELOPER CHAIN" + EthashCategory = "ETHASH" + TxPoolCategory = "TRANSACTION POOL" + PerfCategory = "PERFORMANCE TUNING" + AccountCategory = "ACCOUNT" + APICategory = "API AND CONSOLE" + NetworkingCategory = "NETWORKING" + MinerCategory = "MINER" + GasPriceCategory = "GAS PRICE ORACLE" + VMCategory = "VIRTUAL MACHINE" + LoggingCategory = "LOGGING AND DEBUGGING" + MetricsCategory = "METRICS AND STATS" + MiscCategory = "MISC" + DeprecatedCategory = "ALIASED (deprecated)" + XdcCategory = "XDC" + XdcxCategory = "XDCx" +) + +func init() { + cli.HelpFlag.(*cli.BoolFlag).Category = MiscCategory + cli.VersionFlag.(*cli.BoolFlag).Category = MiscCategory +} diff --git a/internal/flags/flags.go b/internal/flags/flags.go new file mode 100644 index 000000000000..350f274455a3 --- /dev/null +++ b/internal/flags/flags.go @@ -0,0 +1,340 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package flags + +import ( + "encoding" + "errors" + "flag" + "math/big" + "os" + "os/user" + "path" + "strings" + + "github.com/XinFinOrg/XDPoSChain/common/math" + "github.com/urfave/cli/v2" +) + +// DirectoryString is custom type which is registered in the flags library which cli uses for +// argument parsing. This allows us to expand Value to an absolute path when +// the argument is parsed +type DirectoryString string + +func (s *DirectoryString) String() string { + return string(*s) +} + +func (s *DirectoryString) Set(value string) error { + *s = DirectoryString(expandPath(value)) + return nil +} + +var ( + _ cli.Flag = (*DirectoryFlag)(nil) + _ cli.RequiredFlag = (*DirectoryFlag)(nil) + _ cli.VisibleFlag = (*DirectoryFlag)(nil) + _ cli.DocGenerationFlag = (*DirectoryFlag)(nil) + _ cli.CategorizableFlag = (*DirectoryFlag)(nil) +) + +// DirectoryFlag is custom cli.Flag type which expand the received string to an absolute path. +// e.g. ~/.ethereum -> /home/username/.ethereum +type DirectoryFlag struct { + Name string + + Category string + DefaultText string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value DirectoryString + + Aliases []string +} + +// For cli.Flag: + +func (f *DirectoryFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) } +func (f *DirectoryFlag) IsSet() bool { return f.HasBeenSet } +func (f *DirectoryFlag) String() string { return cli.FlagStringer(f) } + +// Apply called by cli library, grabs variable from environment (if in env) +// and adds variable to flag set for parsing. +func (f *DirectoryFlag) Apply(set *flag.FlagSet) error { + eachName(f, func(name string) { + set.Var(&f.Value, f.Name, f.Usage) + }) + return nil +} + +// For cli.RequiredFlag: + +func (f *DirectoryFlag) IsRequired() bool { return f.Required } + +// For cli.VisibleFlag: + +func (f *DirectoryFlag) IsVisible() bool { return !f.Hidden } + +// For cli.CategorizableFlag: + +func (f *DirectoryFlag) GetCategory() string { return f.Category } + +// For cli.DocGenerationFlag: + +func (f *DirectoryFlag) TakesValue() bool { return true } +func (f *DirectoryFlag) GetUsage() string { return f.Usage } +func (f *DirectoryFlag) GetValue() string { return f.Value.String() } +func (f *DirectoryFlag) GetEnvVars() []string { return nil } // env not supported + +func (f *DirectoryFlag) GetDefaultText() string { + if f.DefaultText != "" { + return f.DefaultText + } + return f.GetValue() +} + +type TextMarshaler interface { + encoding.TextMarshaler + encoding.TextUnmarshaler +} + +// textMarshalerVal turns a TextMarshaler into a flag.Value +type textMarshalerVal struct { + v TextMarshaler +} + +func (v textMarshalerVal) String() string { + if v.v == nil { + return "" + } + text, _ := v.v.MarshalText() + return string(text) +} + +func (v textMarshalerVal) Set(s string) error { + return v.v.UnmarshalText([]byte(s)) +} + +var ( + _ cli.Flag = (*TextMarshalerFlag)(nil) + _ cli.RequiredFlag = (*TextMarshalerFlag)(nil) + _ cli.VisibleFlag = (*TextMarshalerFlag)(nil) + _ cli.DocGenerationFlag = (*TextMarshalerFlag)(nil) + _ cli.CategorizableFlag = (*TextMarshalerFlag)(nil) +) + +// TextMarshalerFlag wraps a TextMarshaler value. +type TextMarshalerFlag struct { + Name string + + Category string + DefaultText string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value TextMarshaler + + Aliases []string +} + +// For cli.Flag: + +func (f *TextMarshalerFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) } +func (f *TextMarshalerFlag) IsSet() bool { return f.HasBeenSet } +func (f *TextMarshalerFlag) String() string { return cli.FlagStringer(f) } + +func (f *TextMarshalerFlag) Apply(set *flag.FlagSet) error { + eachName(f, func(name string) { + set.Var(textMarshalerVal{f.Value}, f.Name, f.Usage) + }) + return nil +} + +// For cli.RequiredFlag: + +func (f *TextMarshalerFlag) IsRequired() bool { return f.Required } + +// For cli.VisibleFlag: + +func (f *TextMarshalerFlag) IsVisible() bool { return !f.Hidden } + +// For cli.CategorizableFlag: + +func (f *TextMarshalerFlag) GetCategory() string { return f.Category } + +// For cli.DocGenerationFlag: + +func (f *TextMarshalerFlag) TakesValue() bool { return true } +func (f *TextMarshalerFlag) GetUsage() string { return f.Usage } +func (f *TextMarshalerFlag) GetEnvVars() []string { return nil } // env not supported + +func (f *TextMarshalerFlag) GetValue() string { + t, err := f.Value.MarshalText() + if err != nil { + return "(ERR: " + err.Error() + ")" + } + return string(t) +} + +func (f *TextMarshalerFlag) GetDefaultText() string { + if f.DefaultText != "" { + return f.DefaultText + } + return f.GetValue() +} + +// GlobalTextMarshaler returns the value of a TextMarshalerFlag from the global flag set. +func GlobalTextMarshaler(ctx *cli.Context, name string) TextMarshaler { + val := ctx.Generic(name) + if val == nil { + return nil + } + return val.(textMarshalerVal).v +} + +var ( + _ cli.Flag = (*BigFlag)(nil) + _ cli.RequiredFlag = (*BigFlag)(nil) + _ cli.VisibleFlag = (*BigFlag)(nil) + _ cli.DocGenerationFlag = (*BigFlag)(nil) + _ cli.CategorizableFlag = (*BigFlag)(nil) +) + +// BigFlag is a command line flag that accepts 256 bit big integers in decimal or +// hexadecimal syntax. +type BigFlag struct { + Name string + + Category string + DefaultText string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value *big.Int + + Aliases []string +} + +// For cli.Flag: + +func (f *BigFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) } +func (f *BigFlag) IsSet() bool { return f.HasBeenSet } +func (f *BigFlag) String() string { return cli.FlagStringer(f) } + +func (f *BigFlag) Apply(set *flag.FlagSet) error { + eachName(f, func(name string) { + f.Value = new(big.Int) + set.Var((*bigValue)(f.Value), f.Name, f.Usage) + }) + + return nil +} + +// For cli.RequiredFlag: + +func (f *BigFlag) IsRequired() bool { return f.Required } + +// For cli.VisibleFlag: + +func (f *BigFlag) IsVisible() bool { return !f.Hidden } + +// For cli.CategorizableFlag: + +func (f *BigFlag) GetCategory() string { return f.Category } + +// For cli.DocGenerationFlag: + +func (f *BigFlag) TakesValue() bool { return true } +func (f *BigFlag) GetUsage() string { return f.Usage } +func (f *BigFlag) GetValue() string { return f.Value.String() } +func (f *BigFlag) GetEnvVars() []string { return nil } // env not supported + +func (f *BigFlag) GetDefaultText() string { + if f.DefaultText != "" { + return f.DefaultText + } + return f.GetValue() +} + +// bigValue turns *big.Int into a flag.Value +type bigValue big.Int + +func (b *bigValue) String() string { + if b == nil { + return "" + } + return (*big.Int)(b).String() +} + +func (b *bigValue) Set(s string) error { + intVal, ok := math.ParseBig256(s) + if !ok { + return errors.New("invalid integer syntax") + } + *b = (bigValue)(*intVal) + return nil +} + +// GlobalBig returns the value of a BigFlag from the global flag set. +func GlobalBig(ctx *cli.Context, name string) *big.Int { + val := ctx.Generic(name) + if val == nil { + return nil + } + return (*big.Int)(val.(*bigValue)) +} + +// Expands a file path +// 1. replace tilde with users home dir +// 2. expands embedded environment variables +// 3. cleans the path, e.g. /a/b/../c -> /a/c +// Note, it has limitations, e.g. ~someuser/tmp will not be expanded +func expandPath(p string) string { + if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") { + if home := HomeDir(); home != "" { + p = home + p[1:] + } + } + return path.Clean(os.ExpandEnv(p)) +} + +func HomeDir() string { + if home := os.Getenv("HOME"); home != "" { + return home + } + if usr, err := user.Current(); err == nil { + return usr.HomeDir + } + return "" +} + +func eachName(f cli.Flag, fn func(string)) { + for _, name := range f.Names() { + name = strings.Trim(name, " ") + fn(name) + } +} diff --git a/cmd/utils/customflags_test.go b/internal/flags/flags_test.go similarity index 61% rename from cmd/utils/customflags_test.go rename to internal/flags/flags_test.go index de39ca36a116..a0d4af7ca360 100644 --- a/cmd/utils/customflags_test.go +++ b/internal/flags/flags_test.go @@ -1,20 +1,20 @@ // Copyright 2015 The go-ethereum Authors -// This file is part of go-ethereum. +// This file is part of the go-ethereum library. // -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // -// go-ethereum is distributed in the hope that it will be useful, +// The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// GNU Lesser General Public License for more details. // -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . -package utils +package flags import ( "os" diff --git a/internal/flags/helpers.go b/internal/flags/helpers.go new file mode 100644 index 000000000000..e121aa544c40 --- /dev/null +++ b/internal/flags/helpers.go @@ -0,0 +1,176 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package flags + +import ( + "fmt" + "strings" + + "github.com/XinFinOrg/XDPoSChain/params" + "github.com/urfave/cli/v2" +) + +// NewApp creates an app with sane defaults. +func NewApp(gitCommit, usage string) *cli.App { + app := cli.NewApp() + app.EnableBashCompletion = true + app.Version = params.VersionWithCommit(gitCommit) + app.Usage = usage + app.Copyright = "Copyright 2024 XDPoSChain" + app.Before = func(ctx *cli.Context) error { + MigrateGlobalFlags(ctx) + return nil + } + return app +} + +var migrationApplied = map[*cli.Command]struct{}{} + +// MigrateGlobalFlags makes all global flag values available in the +// context. This should be called as early as possible in app.Before. +// +// Example: +// +// geth account new --keystore /tmp/mykeystore --lightkdf +// +// is equivalent after calling this method with: +// +// geth --keystore /tmp/mykeystore --lightkdf account new +// +// i.e. in the subcommand Action function of 'account new', ctx.Bool("lightkdf) +// will return true even if --lightkdf is set as a global option. +// +// This function may become unnecessary when https://github.com/urfave/cli/pull/1245 is merged. +func MigrateGlobalFlags(ctx *cli.Context) { + var iterate func(cs []*cli.Command, fn func(*cli.Command)) + iterate = func(cs []*cli.Command, fn func(*cli.Command)) { + for _, cmd := range cs { + if _, ok := migrationApplied[cmd]; ok { + continue + } + migrationApplied[cmd] = struct{}{} + fn(cmd) + iterate(cmd.Subcommands, fn) + } + } + + // This iterates over all commands and wraps their action function. + iterate(ctx.App.Commands, func(cmd *cli.Command) { + action := cmd.Action + cmd.Action = func(ctx *cli.Context) error { + doMigrateFlags(ctx) + return action(ctx) + } + }) +} + +func doMigrateFlags(ctx *cli.Context) { + for _, name := range ctx.FlagNames() { + for _, parent := range ctx.Lineage()[1:] { + if parent.IsSet(name) { + ctx.Set(name, parent.String(name)) + break + } + } + } +} + +func init() { + cli.FlagStringer = FlagString +} + +// FlagString prints a single flag in help. +func FlagString(f cli.Flag) string { + df, ok := f.(cli.DocGenerationFlag) + if !ok { + return "" + } + + needsPlaceholder := df.TakesValue() + placeholder := "" + if needsPlaceholder { + placeholder = "value" + } + + namesText := pad(cli.FlagNamePrefixer(df.Names(), placeholder), 30) + + defaultValueString := "" + if s := df.GetDefaultText(); s != "" { + defaultValueString = " (default: " + s + ")" + } + + usage := strings.TrimSpace(df.GetUsage()) + envHint := strings.TrimSpace(cli.FlagEnvHinter(df.GetEnvVars(), "")) + if len(envHint) > 0 { + usage += " " + envHint + } + + usage = wordWrap(usage, 80) + usage = indent(usage, 10) + + return fmt.Sprintf("\n %s%s\n%s", namesText, defaultValueString, usage) +} + +func pad(s string, length int) string { + if len(s) < length { + s += strings.Repeat(" ", length-len(s)) + } + return s +} + +func indent(s string, nspace int) string { + ind := strings.Repeat(" ", nspace) + return ind + strings.ReplaceAll(s, "\n", "\n"+ind) +} + +func wordWrap(s string, width int) string { + var ( + output strings.Builder + lineLength = 0 + ) + + for { + sp := strings.IndexByte(s, ' ') + var word string + if sp == -1 { + word = s + } else { + word = s[:sp] + } + wlen := len(word) + over := lineLength+wlen >= width + if over { + output.WriteByte('\n') + lineLength = 0 + } else { + if lineLength != 0 { + output.WriteByte(' ') + lineLength++ + } + } + + output.WriteString(word) + lineLength += wlen + + if sp == -1 { + break + } + s = s[wlen+1:] + } + + return output.String() +} From b883b0b5932206ddf5353fe2af4887271f151bed Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 25 Nov 2024 16:39:29 +0800 Subject: [PATCH 244/479] internal/flags: fix flag redefined bug for cli v2 aliases (#30796) --- internal/flags/flags.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 350f274455a3..c3cc3098d6b5 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -80,7 +80,7 @@ func (f *DirectoryFlag) String() string { return cli.FlagStringer(f) } // and adds variable to flag set for parsing. func (f *DirectoryFlag) Apply(set *flag.FlagSet) error { eachName(f, func(name string) { - set.Var(&f.Value, f.Name, f.Usage) + set.Var(&f.Value, name, f.Usage) }) return nil } @@ -166,7 +166,7 @@ func (f *TextMarshalerFlag) String() string { return cli.FlagStringer(f) } func (f *TextMarshalerFlag) Apply(set *flag.FlagSet) error { eachName(f, func(name string) { - set.Var(textMarshalerVal{f.Value}, f.Name, f.Usage) + set.Var(textMarshalerVal{f.Value}, name, f.Usage) }) return nil } @@ -248,7 +248,7 @@ func (f *BigFlag) String() string { return cli.FlagStringer(f) } func (f *BigFlag) Apply(set *flag.FlagSet) error { eachName(f, func(name string) { f.Value = new(big.Int) - set.Var((*bigValue)(f.Value), f.Name, f.Usage) + set.Var((*bigValue)(f.Value), name, f.Usage) }) return nil From 369d69649f64355f02d8461f6eb3cd70afeeca4b Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 25 Nov 2024 16:39:29 +0800 Subject: [PATCH 245/479] all: normalize flag's name --- cicd/devnet/start-local-devnet.sh | 14 +- cicd/devnet/start.sh | 14 +- cicd/mainnet/start.sh | 14 +- cicd/testnet/start.sh | 14 +- cmd/XDC/config.go | 8 +- cmd/XDC/consolecmd_test.go | 16 +- cmd/XDC/main.go | 32 ++-- cmd/XDC/run_test.go | 2 +- cmd/XDC/testdata/config.toml | 20 +-- cmd/puppeth/module_node.go | 4 +- cmd/puppeth/module_wallet.go | 2 +- cmd/utils/flags.go | 276 ++++++++++++++++++------------ docker/XDPoSChain/entrypoint.sh | 26 +-- internal/debug/flags.go | 15 +- 14 files changed, 257 insertions(+), 200 deletions(-) diff --git a/cicd/devnet/start-local-devnet.sh b/cicd/devnet/start-local-devnet.sh index c0a9448a8657..230e09f8215d 100755 --- a/cicd/devnet/start-local-devnet.sh +++ b/cicd/devnet/start-local-devnet.sh @@ -53,10 +53,10 @@ echo "Running a node with wallet: ${wallet} at local" ../../build/bin/XDC --ethstats ${netstats} --gcmode=archive \ --bootnodes ${bootnodes} --syncmode full \ --datadir ./tmp/xdcchain --networkid 551 \ --port 30303 --rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 \ ---rpcport 8545 \ ---rpcapi db,eth,debug,miner,net,shh,txpool,personal,web3,XDPoS \ ---rpcvhosts "*" --unlock "${wallet}" --password ./tmp/.pwd --mine \ ---gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \ ---ws --wsaddr=0.0.0.0 --wsport 8555 \ ---wsorigins "*" 2>&1 >>./tmp/xdc.log +--port 30303 --http --http-corsdomain "*" --http-addr 0.0.0.0 \ +--http-port 8545 \ +--http-api db,eth,debug,miner,net,shh,txpool,personal,web3,XDPoS \ +--http-vhosts "*" --unlock "${wallet}" --password ./tmp/.pwd --mine \ +--miner-gasprice "1" --miner-gaslimit "420000000" --verbosity ${log_level} \ +--ws --ws-addr=0.0.0.0 --ws-port 8555 \ +--ws-origins "*" 2>&1 >>./tmp/xdc.log diff --git a/cicd/devnet/start.sh b/cicd/devnet/start.sh index 707b8b32a5ad..dcc6e86ee78b 100755 --- a/cicd/devnet/start.sh +++ b/cicd/devnet/start.sh @@ -75,11 +75,11 @@ XDC --ethstats ${netstats} --gcmode archive \ --nat extip:${INSTANCE_IP} \ --bootnodes ${bootnodes} --syncmode full \ --datadir /work/xdcchain --networkid 551 \ --port $port --rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 \ ---rpcport $rpc_port \ ---rpcapi db,eth,debug,net,shh,txpool,personal,web3,XDPoS \ ---rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \ ---gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \ +--port $port --http --http-corsdomain "*" --http-addr 0.0.0.0 \ +--http-port $rpc_port \ +--http-api db,eth,debug,net,shh,txpool,personal,web3,XDPoS \ +--http-vhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \ +--miner-gasprice "1" --miner-gaslimit "420000000" --verbosity ${log_level} \ --debugdatadir /work/xdcchain \ ---ws --wsaddr=0.0.0.0 --wsport $ws_port \ ---wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log +--ws --ws-addr=0.0.0.0 --ws-port $ws_port \ +--ws-origins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log diff --git a/cicd/mainnet/start.sh b/cicd/mainnet/start.sh index 0cfbe26e272b..b6bca08f794e 100755 --- a/cicd/mainnet/start.sh +++ b/cicd/mainnet/start.sh @@ -74,11 +74,11 @@ XDC --ethstats ${netstats} --gcmode archive \ --nat extip:${INSTANCE_IP} \ --bootnodes ${bootnodes} --syncmode full \ --datadir /work/xdcchain --networkid 50 \ --port $port --rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 \ ---rpcport $rpc_port \ ---rpcapi db,eth,debug,net,shh,txpool,personal,web3,XDPoS \ ---rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \ ---gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \ +--port $port --http --http-corsdomain "*" --http-addr 0.0.0.0 \ +--http-port $rpc_port \ +--http-api db,eth,debug,net,shh,txpool,personal,web3,XDPoS \ +--http-vhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \ +--miner-gasprice "1" --miner-gaslimit "420000000" --verbosity ${log_level} \ --debugdatadir /work/xdcchain \ ---ws --wsaddr=0.0.0.0 --wsport $ws_port \ ---wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log +--ws --ws-addr=0.0.0.0 --ws-port $ws_port \ +--ws-origins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log diff --git a/cicd/testnet/start.sh b/cicd/testnet/start.sh index abfd91cadfba..e506d1df45ec 100755 --- a/cicd/testnet/start.sh +++ b/cicd/testnet/start.sh @@ -76,11 +76,11 @@ XDC --ethstats ${netstats} --gcmode archive \ --nat extip:${INSTANCE_IP} \ --bootnodes ${bootnodes} --syncmode full \ --datadir /work/xdcchain --networkid 51 \ --port $port --rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 \ ---rpcport $rpc_port \ ---rpcapi db,eth,debug,net,shh,txpool,personal,web3,XDPoS \ ---rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \ ---gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \ +--port $port --http --http-corsdomain "*" --http-addr 0.0.0.0 \ +--http-port $rpc_port \ +--http-api db,eth,debug,net,shh,txpool,personal,web3,XDPoS \ +--http-vhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \ +--miner-gasprice "1" --miner-gaslimit "420000000" --verbosity ${log_level} \ --debugdatadir /work/xdcchain \ ---ws --wsaddr=0.0.0.0 --wsport $ws_port \ ---wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log +--ws --ws-addr=0.0.0.0 --ws-port $ws_port \ +--ws-origins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log diff --git a/cmd/XDC/config.go b/cmd/XDC/config.go index 5f9cbab571f9..d5e60c932180 100644 --- a/cmd/XDC/config.go +++ b/cmd/XDC/config.go @@ -138,8 +138,8 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, XDCConfig) { utils.Fatalf("%v", err) } } - if ctx.IsSet(utils.StakingEnabledFlag.Name) { - cfg.StakeEnable = ctx.Bool(utils.StakingEnabledFlag.Name) + if ctx.IsSet(utils.MiningEnabledFlag.Name) { + cfg.StakeEnable = ctx.Bool(utils.MiningEnabledFlag.Name) } // if !ctx.IsSet(debug.VerbosityFlag.Name) { // debug.Verbosity(log.Lvl(cfg.Verbosity)) @@ -175,8 +175,8 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, XDCConfig) { // Check GasPrice common.MinGasPrice = big.NewInt(common.DefaultMinGasPrice) - if ctx.IsSet(utils.GasPriceFlag.Name) { - if gasPrice := int64(ctx.Int(utils.GasPriceFlag.Name)); gasPrice > common.DefaultMinGasPrice { + if ctx.IsSet(utils.MinerGasPriceFlag.Name) { + if gasPrice := int64(ctx.Int(utils.MinerGasPriceFlag.Name)); gasPrice > common.DefaultMinGasPrice { common.MinGasPrice = big.NewInt(gasPrice) } } diff --git a/cmd/XDC/consolecmd_test.go b/cmd/XDC/consolecmd_test.go index 9ca2e0b73ed4..23988bc2c283 100644 --- a/cmd/XDC/consolecmd_test.go +++ b/cmd/XDC/consolecmd_test.go @@ -44,9 +44,9 @@ func TestConsoleWelcome(t *testing.T) { // Start a XDC console, make sure it's cleaned up and terminate the console XDC := runXDC(t, - "console", "--datadir", datadir, "--XDCx.datadir", datadir+"/XDCx/"+time.Now().String(), + "console", "--datadir", datadir, "--XDCx-datadir", datadir+"/XDCx/"+time.Now().String(), "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", - "--etherbase", coinbase) + "--miner-etherbase", coinbase) // Gather all the infos the welcome message needs to contain XDC.SetTemplateFunc("goos", func() string { return runtime.GOOS }) @@ -86,9 +86,9 @@ func TestIPCAttachWelcome(t *testing.T) { ipc = filepath.Join(datadir, "XDC.ipc") } XDC := runXDC(t, - "--datadir", datadir, "--XDCx.datadir", datadir+"/XDCx/"+time.Now().String(), + "--datadir", datadir, "--XDCx-datadir", datadir+"/XDCx/"+time.Now().String(), "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", - "--etherbase", coinbase, "--ipcpath", ipc) + "--miner-etherbase", coinbase, "--ipcpath", ipc) time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open testAttachWelcome(t, XDC, "ipc:"+ipc, ipcAPIs) @@ -103,9 +103,9 @@ func TestHTTPAttachWelcome(t *testing.T) { datadir := tmpdir(t) defer os.RemoveAll(datadir) XDC := runXDC(t, - "--datadir", datadir, "--XDCx.datadir", datadir+"/XDCx/"+time.Now().String(), + "--datadir", datadir, "--XDCx-datadir", datadir+"/XDCx/"+time.Now().String(), "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", - "--etherbase", coinbase, "--rpc", "--rpcport", port) + "--miner-etherbase", coinbase, "--http", "--http-port", port) time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open testAttachWelcome(t, XDC, "http://localhost:"+port, httpAPIs) @@ -120,9 +120,9 @@ func TestWSAttachWelcome(t *testing.T) { datadir := tmpdir(t) defer os.RemoveAll(datadir) XDC := runXDC(t, - "--datadir", datadir, "--XDCx.datadir", datadir+"/XDCx/"+time.Now().String(), + "--datadir", datadir, "--XDCx-datadir", datadir+"/XDCx/"+time.Now().String(), "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", - "--etherbase", coinbase, "--ws", "--wsport", port) + "--miner-etherbase", coinbase, "--ws", "--ws-port", port) time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open testAttachWelcome(t, XDC, "ws://localhost:"+port, httpAPIs) diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go index a736c81e27d9..0ba6fc781b1a 100644 --- a/cmd/XDC/main.go +++ b/cmd/XDC/main.go @@ -101,11 +101,11 @@ var ( utils.ListenPortFlag, utils.MaxPeersFlag, utils.MaxPendingPeersFlag, - utils.EtherbaseFlag, - utils.GasPriceFlag, - utils.StakerThreadsFlag, - utils.StakingEnabledFlag, - utils.TargetGasLimitFlag, + utils.MinerEtherbaseFlag, + utils.MinerGasPriceFlag, + utils.MinerThreadsFlag, + utils.MiningEnabledFlag, + utils.MinerGasLimitFlag, utils.NATFlag, utils.NoDiscoverFlag, //utils.DiscoveryV5Flag, @@ -122,8 +122,8 @@ var ( utils.EnableXDCPrefixFlag, utils.RewoundFlag, utils.NetworkIdFlag, - utils.RPCCORSDomainFlag, - utils.RPCVirtualHostsFlag, + utils.HTTPCORSDomainFlag, + utils.HTTPVirtualHostsFlag, utils.EthStatsURLFlag, utils.MetricsEnabledFlag, utils.MetricsHTTPFlag, @@ -145,14 +145,14 @@ var ( } rpcFlags = []cli.Flag{ - utils.RPCEnabledFlag, + utils.HTTPEnabledFlag, utils.RPCGlobalGasCapFlag, - utils.RPCListenAddrFlag, - utils.RPCPortFlag, - utils.RPCHttpReadTimeoutFlag, - utils.RPCHttpWriteTimeoutFlag, - utils.RPCHttpIdleTimeoutFlag, - utils.RPCApiFlag, + utils.HTTPListenAddrFlag, + utils.HTTPPortFlag, + utils.HTTPReadTimeoutFlag, + utils.HTTPWriteTimeoutFlag, + utils.HTTPIdleTimeoutFlag, + utils.HTTPApiFlag, utils.WSEnabledFlag, utils.WSListenAddrFlag, utils.WSPortFlag, @@ -329,7 +329,7 @@ func startNode(ctx *cli.Context, stack *node.Node, cfg XDCConfig) { } else { log.Info("Masternode found. Enabling staking mode...") // Use a reduced number of threads if requested - if threads := ctx.Int(utils.StakerThreadsFlag.Name); threads > 0 { + if threads := ctx.Int(utils.MinerThreadsFlag.Name); threads > 0 { type threaded interface { SetThreads(threads int) } @@ -371,7 +371,7 @@ func startNode(ctx *cli.Context, stack *node.Node, cfg XDCConfig) { } else { log.Info("Masternode found. Enabling staking mode...") // Use a reduced number of threads if requested - if threads := ctx.Int(utils.StakerThreadsFlag.Name); threads > 0 { + if threads := ctx.Int(utils.MinerThreadsFlag.Name); threads > 0 { type threaded interface { SetThreads(threads int) } diff --git a/cmd/XDC/run_test.go b/cmd/XDC/run_test.go index 231385e1ac19..98f6092fed32 100644 --- a/cmd/XDC/run_test.go +++ b/cmd/XDC/run_test.go @@ -71,7 +71,7 @@ func runXDC(t *testing.T, args ...string) *testXDC { if i < len(args)-1 { tt.Datadir = args[i+1] } - case "--etherbase": + case "--miner-etherbase": if i < len(args)-1 { tt.Etherbase = args[i+1] } diff --git a/cmd/XDC/testdata/config.toml b/cmd/XDC/testdata/config.toml index 939c87c12c72..b94576dddbd7 100644 --- a/cmd/XDC/testdata/config.toml +++ b/cmd/XDC/testdata/config.toml @@ -5,24 +5,24 @@ NAT = "" # flag --nat [Eth] NetworkId = 89 # flag --networkid SyncMode = "full" # flag --syncmode -GasPrice = 1 # flag --gasprice +GasPrice = 1 # flag --miner-gasprice [Shh] [Node] DataDir = "node1/" # flag --datadir -HTTPPort = 8501 # flag --rpcport -HTTPHost = "localhost" # flags --rpcaddr & --rpc +HTTPPort = 8501 # flag --http-port +HTTPHost = "localhost" # flags --http-addr & --http # in 3 cases : - # HTTPHost is "" == --rpc & --rpcaddr is not set - # HTTPHost is "localhost" or "127.0.0.1" == only set --rpc - # HTTPHost is other IP (ex : 192.168.1.1) = set 2 flags --rpc & --rpcaddr -WSHost = "localhost" # flags --wsaddr & --ws . same option HTTPHost -WSPort = 8546 # flag --wsport -WSModules = ["eth","ssh"] #flag --wsapi + # HTTPHost is "" == --http & --http-addr is not set + # HTTPHost is "localhost" or "127.0.0.1" == only set --http + # HTTPHost is other IP (ex : 192.168.1.1) = set 2 flags --http & --http-addr +WSHost = "localhost" # flags --ws-addr & --ws . same option HTTPHost +WSPort = 8546 # flag --ws-port +WSModules = ["eth","ssh"] #flag --ws-api -HTTPModules = ["personal","db","eth","net","web3","txpool","miner"] # flag --rpcapi +HTTPModules = ["personal","db","eth","net","web3","txpool","miner"] # flag --http-api KeyStoreDir = "" # flag --keystore UserIdent = "" # flag --identity diff --git a/cmd/puppeth/module_node.go b/cmd/puppeth/module_node.go index a52a959ced22..c592e59d31d6 100644 --- a/cmd/puppeth/module_node.go +++ b/cmd/puppeth/module_node.go @@ -42,7 +42,7 @@ ADD genesis.json /genesis.json RUN \ echo 'XDC --cache 512 init /genesis.json' > XDC.sh && \{{if .Unlock}} echo 'mkdir -p /root/.ethereum/keystore/ && cp /signer.json /root/.ethereum/keystore/' >> XDC.sh && \{{end}} - echo $'XDC --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .Bootnodes}}--bootnodes {{.Bootnodes}}{{end}} {{if .Etherbase}}--etherbase {{.Etherbase}} --mine --minerthreads 1{{end}} {{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}} --targetgaslimit {{.GasTarget}} --gasprice {{.GasPrice}}' >> XDC.sh + echo $'XDC --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .Bootnodes}}--bootnodes {{.Bootnodes}}{{end}} {{if .Etherbase}}--miner-etherbase {{.Etherbase}} --mine --miner-threads 1{{end}} {{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}} --miner-gaslimit {{.GasTarget}} --miner-gasprice {{.GasPrice}}' >> XDC.sh ENTRYPOINT ["/bin/sh", "XDC.sh"] ` @@ -92,7 +92,7 @@ func deployNode(client *sshClient, network string, bootnodes []string, config *n lightFlag := "" if config.peersLight > 0 { - lightFlag = fmt.Sprintf("--lightpeers=%d --lightserv=50", config.peersLight) + lightFlag = fmt.Sprintf("--light-peers=%d --light-serv=50", config.peersLight) } dockerfile := new(bytes.Buffer) template.Must(template.New("").Parse(nodeDockerfile)).Execute(dockerfile, map[string]interface{}{ diff --git a/cmd/puppeth/module_wallet.go b/cmd/puppeth/module_wallet.go index 6dcbdfbec5db..c714072e6e67 100644 --- a/cmd/puppeth/module_wallet.go +++ b/cmd/puppeth/module_wallet.go @@ -37,7 +37,7 @@ ADD genesis.json /genesis.json RUN \ echo 'node server.js &' > wallet.sh && \ echo 'XDC --cache 512 init /genesis.json' >> wallet.sh && \ - echo $'XDC --networkid {{.NetworkID}} --port {{.NodePort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --rpc --rpcaddr=0.0.0.0 --rpccorsdomain "*" --rpcvhosts "*"' >> wallet.sh + echo $'XDC --networkid {{.NetworkID}} --port {{.NodePort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --http --http-addr=0.0.0.0 --http-corsdomain "*" --http-vhosts "*"' >> wallet.sh RUN \ sed -i 's/PuppethNetworkID/{{.NetworkID}}/g' dist/js/etherwallet-master.js && \ diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index db42606da594..a6b54f96447f 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -113,6 +113,7 @@ var ( } DeveloperPeriodFlag = &cli.IntFlag{ Name: "dev-period", + Aliases: []string{"dev.period"}, Usage: "Block period to use in developer mode (0 = mine only if transaction pending)", Category: flags.DevCategory, } @@ -149,13 +150,15 @@ var ( // Light server and client settings LightServFlag = &cli.IntFlag{ - Name: "lightserv", + Name: "light-serv", + Aliases: []string{"lightserv"}, Usage: "Maximum percentage of time allowed for serving LES requests (0-90)", Value: ethconfig.Defaults.LightServ, Category: flags.LightCategory, } LightPeersFlag = &cli.IntFlag{ - Name: "lightpeers", + Name: "light-peers", + Aliases: []string{"lightpeers"}, Usage: "Maximum number of LES client peers", Value: ethconfig.Defaults.LightPeers, Category: flags.LightCategory, @@ -163,36 +166,42 @@ var ( // Ethash settings EthashCacheDirFlag = &flags.DirectoryFlag{ - Name: "ethash.cachedir", + Name: "ethash-cachedir", + Aliases: []string{"ethash.cachedir"}, Usage: "Directory to store the ethash verification caches (default = inside the datadir)", Category: flags.EthashCategory, } EthashCachesInMemoryFlag = &cli.IntFlag{ - Name: "ethash.cachesinmem", + Name: "ethash-cachesinmem", + Aliases: []string{"ethash.cachesinmem"}, Usage: "Number of recent ethash caches to keep in memory (16MB each)", Value: ethconfig.Defaults.Ethash.CachesInMem, Category: flags.EthashCategory, } EthashCachesOnDiskFlag = &cli.IntFlag{ - Name: "ethash.cachesondisk", + Name: "ethash-cachesondisk", + Aliases: []string{"ethash.cachesondisk"}, Usage: "Number of recent ethash caches to keep on disk (16MB each)", Value: ethconfig.Defaults.Ethash.CachesOnDisk, Category: flags.EthashCategory, } EthashDatasetDirFlag = &flags.DirectoryFlag{ - Name: "ethash.dagdir", + Name: "ethash-dagdir", + Aliases: []string{"ethash.dagdir"}, Usage: "Directory to store the ethash mining DAGs (default = inside home folder)", Value: flags.DirectoryString(ethconfig.Defaults.Ethash.DatasetDir), Category: flags.EthashCategory, } EthashDatasetsInMemoryFlag = &cli.IntFlag{ - Name: "ethash.dagsinmem", + Name: "ethash-dagsinmem", + Aliases: []string{"ethash.dagsinmem"}, Usage: "Number of recent ethash mining DAGs to keep in memory (1+GB each)", Value: ethconfig.Defaults.Ethash.DatasetsInMem, Category: flags.EthashCategory, } EthashDatasetsOnDiskFlag = &cli.IntFlag{ - Name: "ethash.dagsondisk", + Name: "ethash-dagsondisk", + Aliases: []string{"ethash.dagsondisk"}, Usage: "Number of recent ethash mining DAGs to keep on disk (1+GB each)", Value: ethconfig.Defaults.Ethash.DatasetsOnDisk, Category: flags.EthashCategory, @@ -200,60 +209,70 @@ var ( // Transaction pool settings TxPoolNoLocalsFlag = &cli.BoolFlag{ - Name: "txpool.nolocals", + Name: "txpool-nolocals", + Aliases: []string{"txpool.nolocals"}, Usage: "Disables price exemptions for locally submitted transactions", Category: flags.TxPoolCategory, } TxPoolJournalFlag = &cli.StringFlag{ - Name: "txpool.journal", + Name: "txpool-journal", + Aliases: []string{"txpool.journal"}, Usage: "Disk journal for local transaction to survive node restarts", Value: txpool.DefaultConfig.Journal, Category: flags.TxPoolCategory, } TxPoolRejournalFlag = &cli.DurationFlag{ - Name: "txpool.rejournal", + Name: "txpool-rejournal", + Aliases: []string{"txpool.rejournal"}, Usage: "Time interval to regenerate the local transaction journal", Value: txpool.DefaultConfig.Rejournal, Category: flags.TxPoolCategory, } TxPoolPriceLimitFlag = &cli.Uint64Flag{ - Name: "txpool.pricelimit", + Name: "txpool-pricelimit", + Aliases: []string{"txpool.pricelimit"}, Usage: "Minimum gas price limit to enforce for acceptance into the pool", Value: ethconfig.Defaults.TxPool.PriceLimit, Category: flags.TxPoolCategory, } TxPoolPriceBumpFlag = &cli.Uint64Flag{ - Name: "txpool.pricebump", + Name: "txpool-pricebump", + Aliases: []string{"txpool.pricebump"}, Usage: "Price bump percentage to replace an already existing transaction", Value: ethconfig.Defaults.TxPool.PriceBump, Category: flags.TxPoolCategory, } TxPoolAccountSlotsFlag = &cli.Uint64Flag{ - Name: "txpool.accountslots", + Name: "txpool-accountslots", + Aliases: []string{"txpool.accountslots"}, Usage: "Minimum number of executable transaction slots guaranteed per account", Value: ethconfig.Defaults.TxPool.AccountSlots, Category: flags.TxPoolCategory, } TxPoolGlobalSlotsFlag = &cli.Uint64Flag{ - Name: "txpool.globalslots", + Name: "txpool-globalslots", + Aliases: []string{"txpool.globalslots"}, Usage: "Maximum number of executable transaction slots for all accounts", Value: ethconfig.Defaults.TxPool.GlobalSlots, Category: flags.TxPoolCategory, } TxPoolAccountQueueFlag = &cli.Uint64Flag{ - Name: "txpool.accountqueue", + Name: "txpool-accountqueue", + Aliases: []string{"txpool.accountqueue"}, Usage: "Maximum number of non-executable transaction slots permitted per account", Value: ethconfig.Defaults.TxPool.AccountQueue, Category: flags.TxPoolCategory, } TxPoolGlobalQueueFlag = &cli.Uint64Flag{ - Name: "txpool.globalqueue", + Name: "txpool-globalqueue", + Aliases: []string{"txpool.globalqueue"}, Usage: "Maximum number of non-executable transaction slots for all accounts", Value: ethconfig.Defaults.TxPool.GlobalQueue, Category: flags.TxPoolCategory, } TxPoolLifetimeFlag = &cli.DurationFlag{ - Name: "txpool.lifetime", + Name: "txpool-lifetime", + Aliases: []string{"txpool.lifetime"}, Usage: "Maximum amount of time non-executable transaction are queued", Value: ethconfig.Defaults.TxPool.Lifetime, Category: flags.TxPoolCategory, @@ -267,19 +286,22 @@ var ( Category: flags.PerfCategory, } CacheDatabaseFlag = &cli.IntFlag{ - Name: "cache.database", + Name: "cache-database", + Aliases: []string{"cache.database"}, Usage: "Percentage of cache memory allowance to use for database io", Value: 50, Category: flags.PerfCategory, } CacheGCFlag = &cli.IntFlag{ - Name: "cache.gc", + Name: "cache-gc", + Aliases: []string{"cache.gc"}, Usage: "Percentage of cache memory allowance to use for trie pruning", Value: 25, Category: flags.PerfCategory, } CacheLogSizeFlag = &cli.IntFlag{ - Name: "cache.blocklogs", + Name: "cache-blocklogs", + Aliases: []string{"cache.blocklogs"}, Usage: "Size (in number of blocks) of the log cache for filtering", Value: ethconfig.Defaults.FilterLogCacheSize, Category: flags.PerfCategory, @@ -291,37 +313,42 @@ var ( } // Miner settings - StakingEnabledFlag = &cli.BoolFlag{ + MiningEnabledFlag = &cli.BoolFlag{ Name: "mine", - Usage: "Enable staking", + Usage: "Enable mining", Category: flags.MinerCategory, } - StakerThreadsFlag = &cli.IntFlag{ - Name: "minerthreads", - Usage: "Number of CPU threads to use for staking", + MinerThreadsFlag = &cli.IntFlag{ + Name: "miner-threads", + Aliases: []string{"minerthreads"}, + Usage: "Number of CPU threads to use for mining", Value: runtime.NumCPU(), Category: flags.MinerCategory, } - TargetGasLimitFlag = &cli.Uint64Flag{ - Name: "targetgaslimit", + MinerGasLimitFlag = &cli.Uint64Flag{ + Name: "miner-gaslimit", + Aliases: []string{"targetgaslimit"}, Usage: "Target gas limit sets the artificial target gas floor for the blocks to mine", Value: params.XDCGenesisGasLimit, Category: flags.MinerCategory, } - EtherbaseFlag = &cli.StringFlag{ - Name: "etherbase", - Usage: "Public address for block mining rewards (default = first account created)", - Value: "0", - Category: flags.MinerCategory, - } - GasPriceFlag = &flags.BigFlag{ - Name: "gasprice", + MinerGasPriceFlag = &flags.BigFlag{ + Name: "miner-gasprice", + Aliases: []string{"gasprice"}, Usage: "Minimal gas price to accept for mining a transactions", Value: ethconfig.Defaults.GasPrice, Category: flags.MinerCategory, } - ExtraDataFlag = &cli.StringFlag{ - Name: "extradata", + MinerEtherbaseFlag = &cli.StringFlag{ + Name: "miner-etherbase", + Aliases: []string{"etherbase"}, + Usage: "Public address for block mining rewards (default = first account created)", + Value: "0", + Category: flags.MinerCategory, + } + MinerExtraDataFlag = &cli.StringFlag{ + Name: "miner-extradata", + Aliases: []string{"extradata"}, Usage: "Block extra data set by the miner (default = client version)", Category: flags.MinerCategory, } @@ -355,7 +382,8 @@ var ( Category: flags.APICategory, } RPCGlobalTxFeeCap = &cli.Float64Flag{ - Name: "rpc.txfeecap", + Name: "rpc-txfeecap", + Aliases: []string{"rpc.txfeecap"}, Usage: "Sets a cap on transaction fee (in ether) that can be sent via the RPC APIs (0 = no cap)", Value: ethconfig.Defaults.RPCTxFeeCap, Category: flags.APICategory, @@ -389,66 +417,97 @@ var ( Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)", Category: flags.APICategory, } - RPCEnabledFlag = &cli.BoolFlag{ - Name: "rpc", + HTTPEnabledFlag = &cli.BoolFlag{ + Name: "http", + Aliases: []string{"rpc"}, Usage: "Enable the HTTP-RPC server", Category: flags.APICategory, } - RPCListenAddrFlag = &cli.StringFlag{ - Name: "rpcaddr", + HTTPListenAddrFlag = &cli.StringFlag{ + Name: "http-addr", + Aliases: []string{"rpcaddr"}, Usage: "HTTP-RPC server listening interface", Value: node.DefaultHTTPHost, Category: flags.APICategory, } - RPCPortFlag = &cli.IntFlag{ - Name: "rpcport", + HTTPPortFlag = &cli.IntFlag{ + Name: "http-port", + Aliases: []string{"rpcport"}, Usage: "HTTP-RPC server listening port", Value: node.DefaultHTTPPort, Category: flags.APICategory, } - RPCCORSDomainFlag = &cli.StringFlag{ - Name: "rpccorsdomain", + HTTPCORSDomainFlag = &cli.StringFlag{ + Name: "http-corsdomain", + Aliases: []string{"rpccorsdomain"}, Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)", Value: "", Category: flags.APICategory, } - RPCVirtualHostsFlag = &cli.StringFlag{ - Name: "rpcvhosts", + HTTPVirtualHostsFlag = &cli.StringFlag{ + Name: "http-vhosts", + Aliases: []string{"rpcvhosts"}, Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.", Value: strings.Join(node.DefaultConfig.HTTPVirtualHosts, ","), Category: flags.APICategory, } - RPCApiFlag = &cli.StringFlag{ - Name: "rpcapi", + HTTPApiFlag = &cli.StringFlag{ + Name: "http-api", + Aliases: []string{"rpcapi"}, Usage: "API's offered over the HTTP-RPC interface", Value: "", Category: flags.APICategory, } + HTTPReadTimeoutFlag = &cli.DurationFlag{ + Name: "http-readtimeout", + Aliases: []string{"rpcreadtimeout"}, + Usage: "HTTP-RPC server read timeout", + Value: rpc.DefaultHTTPTimeouts.ReadTimeout, + Category: flags.APICategory, + } + HTTPWriteTimeoutFlag = &cli.DurationFlag{ + Name: "http-writetimeout", + Aliases: []string{"rpcwritetimeout"}, + Usage: "HTTP-RPC server write timeout", + Value: rpc.DefaultHTTPTimeouts.WriteTimeout, + Category: flags.APICategory, + } + HTTPIdleTimeoutFlag = &cli.DurationFlag{ + Name: "http-idletimeout", + Aliases: []string{"rpcidletimeout"}, + Usage: "HTTP-RPC server idle timeout", + Value: rpc.DefaultHTTPTimeouts.IdleTimeout, + Category: flags.APICategory, + } WSEnabledFlag = &cli.BoolFlag{ Name: "ws", Usage: "Enable the WS-RPC server", Category: flags.APICategory, } WSListenAddrFlag = &cli.StringFlag{ - Name: "wsaddr", + Name: "ws-addr", + Aliases: []string{"wsaddr"}, Usage: "WS-RPC server listening interface", Value: node.DefaultWSHost, Category: flags.APICategory, } WSPortFlag = &cli.IntFlag{ - Name: "wsport", + Name: "ws-port", + Aliases: []string{"wsport"}, Usage: "WS-RPC server listening port", Value: node.DefaultWSPort, Category: flags.APICategory, } WSApiFlag = &cli.StringFlag{ - Name: "wsapi", + Name: "ws-api", + Aliases: []string{"wsapi"}, Usage: "API's offered over the WS-RPC interface", Value: "", Category: flags.APICategory, } WSAllowedOriginsFlag = &cli.StringFlag{ - Name: "wsorigins", + Name: "ws-origins", + Aliases: []string{"wsorigins"}, Usage: "Origins from which to accept websockets requests", Value: "", Category: flags.APICategory, @@ -463,24 +522,6 @@ var ( Usage: "Comma separated list of JavaScript files to preload into the console", Category: flags.APICategory, } - RPCHttpReadTimeoutFlag = &cli.DurationFlag{ - Name: "rpcreadtimeout", - Usage: "HTTP-RPC server read timeout", - Value: rpc.DefaultHTTPTimeouts.ReadTimeout, - Category: flags.APICategory, - } - RPCHttpWriteTimeoutFlag = &cli.DurationFlag{ - Name: "rpcwritetimeout", - Usage: "HTTP-RPC server write timeout", - Value: rpc.DefaultHTTPTimeouts.WriteTimeout, - Category: flags.APICategory, - } - RPCHttpIdleTimeoutFlag = &cli.DurationFlag{ - Name: "rpcidletimeout", - Usage: "HTTP-RPC server idle timeout", - Value: rpc.DefaultHTTPTimeouts.IdleTimeout, - Category: flags.APICategory, - } // Network Settings MaxPeersFlag = &cli.IntFlag{ @@ -561,25 +602,29 @@ var ( // Gas price oracle settings GpoBlocksFlag = &cli.IntFlag{ - Name: "gpoblocks", + Name: "gpo-blocks", + Aliases: []string{"gpoblocks"}, Usage: "Number of recent blocks to check for gas prices", Value: ethconfig.Defaults.GPO.Blocks, Category: flags.GasPriceCategory, } GpoPercentileFlag = &cli.IntFlag{ - Name: "gpopercentile", + Name: "gpo-percentile", + Aliases: []string{"gpopercentile"}, Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices", Value: ethconfig.Defaults.GPO.Percentile, Category: flags.GasPriceCategory, } GpoMaxGasPriceFlag = &cli.Int64Flag{ - Name: "gpo.maxprice", + Name: "gpo-maxprice", + Aliases: []string{"gpo.maxprice"}, Usage: "Maximum gas price will be recommended by gpo", Value: ethconfig.Defaults.GPO.MaxPrice.Int64(), Category: flags.GasPriceCategory, } GpoIgnoreGasPriceFlag = &cli.Int64Flag{ - Name: "gpo.ignoreprice", + Name: "gpo-ignoreprice", + Aliases: []string{"gpo.ignoreprice"}, Usage: "Gas price below which gpo will ignore transactions", Value: ethconfig.Defaults.GPO.IgnorePrice.Int64(), Category: flags.GasPriceCategory, @@ -597,13 +642,15 @@ var ( // to enable a public-OK metrics endpoint without having to worry about ALSO exposing // other profiling behavior or information. MetricsHTTPFlag = &cli.StringFlag{ - Name: "metrics.addr", + Name: "metrics-addr", + Aliases: []string{"metrics.addr"}, Usage: "Enable stand-alone metrics HTTP server listening interface", Value: metrics.DefaultConfig.HTTP, Category: flags.MetricsCategory, } MetricsPortFlag = &cli.IntFlag{ - Name: "metrics.port", + Name: "metrics-port", + Aliases: []string{"metrics.port"}, Usage: "Metrics HTTP server listening port", Value: metrics.DefaultConfig.Port, Category: flags.MetricsCategory, @@ -661,31 +708,36 @@ var ( Category: flags.XdcxCategory, } XDCXDataDirFlag = &flags.DirectoryFlag{ - Name: "XDCx.datadir", + Name: "XDCx-datadir", + Aliases: []string{"XDCx.datadir"}, Usage: "Data directory for the XDCX databases", Value: flags.DirectoryString(filepath.Join(DataDirFlag.Value.String(), "XDCx")), Category: flags.XdcxCategory, } XDCXDBEngineFlag = &cli.StringFlag{ - Name: "XDCx.dbengine", + Name: "XDCx-dbengine", + Aliases: []string{"XDCx.dbengine"}, Usage: "Database engine for XDCX (leveldb, mongodb)", Value: "leveldb", Category: flags.XdcxCategory, } XDCXDBNameFlag = &cli.StringFlag{ - Name: "XDCx.dbName", + Name: "XDCx-dbName", + Aliases: []string{"XDCx.dbName"}, Usage: "Database name for XDCX", Value: "XDCdex", Category: flags.XdcxCategory, } XDCXDBConnectionUrlFlag = &cli.StringFlag{ - Name: "XDCx.dbConnectionUrl", + Name: "XDCx-dbConnectionUrl", + Aliases: []string{"XDCx.dbConnectionUrl"}, Usage: "ConnectionUrl to database if dbEngine is mongodb. Host:port. If there are multiple instances, separated by comma. Eg: localhost:27017,localhost:27018", Value: "localhost:27017", Category: flags.XdcxCategory, } XDCXDBReplicaSetNameFlag = &cli.StringFlag{ - Name: "XDCx.dbReplicaSetName", + Name: "XDCx-dbReplicaSetName", + Aliases: []string{"XDCx.dbReplicaSetName"}, Usage: "ReplicaSetName if Master-Slave is setup", Category: flags.XdcxCategory, } @@ -847,33 +899,33 @@ func splitAndTrim(input string) (ret []string) { // setHTTP creates the HTTP RPC listener interface string from the set // command line flags, returning empty if the HTTP endpoint is disabled. func setHTTP(ctx *cli.Context, cfg *node.Config) { - if ctx.Bool(RPCEnabledFlag.Name) && cfg.HTTPHost == "" { + if ctx.Bool(HTTPEnabledFlag.Name) && cfg.HTTPHost == "" { cfg.HTTPHost = "127.0.0.1" - if ctx.IsSet(RPCListenAddrFlag.Name) { - cfg.HTTPHost = ctx.String(RPCListenAddrFlag.Name) + if ctx.IsSet(HTTPListenAddrFlag.Name) { + cfg.HTTPHost = ctx.String(HTTPListenAddrFlag.Name) } } - if ctx.IsSet(RPCPortFlag.Name) { - cfg.HTTPPort = ctx.Int(RPCPortFlag.Name) + if ctx.IsSet(HTTPPortFlag.Name) { + cfg.HTTPPort = ctx.Int(HTTPPortFlag.Name) } - if ctx.IsSet(RPCHttpReadTimeoutFlag.Name) { - cfg.HTTPTimeouts.ReadTimeout = ctx.Duration(RPCHttpReadTimeoutFlag.Name) + if ctx.IsSet(HTTPReadTimeoutFlag.Name) { + cfg.HTTPTimeouts.ReadTimeout = ctx.Duration(HTTPReadTimeoutFlag.Name) } - if ctx.IsSet(RPCHttpWriteTimeoutFlag.Name) { - cfg.HTTPTimeouts.WriteTimeout = ctx.Duration(RPCHttpWriteTimeoutFlag.Name) + if ctx.IsSet(HTTPWriteTimeoutFlag.Name) { + cfg.HTTPTimeouts.WriteTimeout = ctx.Duration(HTTPWriteTimeoutFlag.Name) } - if ctx.IsSet(RPCHttpIdleTimeoutFlag.Name) { - cfg.HTTPTimeouts.IdleTimeout = ctx.Duration(RPCHttpIdleTimeoutFlag.Name) + if ctx.IsSet(HTTPIdleTimeoutFlag.Name) { + cfg.HTTPTimeouts.IdleTimeout = ctx.Duration(HTTPIdleTimeoutFlag.Name) } - if ctx.IsSet(RPCCORSDomainFlag.Name) { - cfg.HTTPCors = splitAndTrim(ctx.String(RPCCORSDomainFlag.Name)) + if ctx.IsSet(HTTPCORSDomainFlag.Name) { + cfg.HTTPCors = splitAndTrim(ctx.String(HTTPCORSDomainFlag.Name)) } - if ctx.IsSet(RPCApiFlag.Name) { - cfg.HTTPModules = splitAndTrim(ctx.String(RPCApiFlag.Name)) + if ctx.IsSet(HTTPApiFlag.Name) { + cfg.HTTPModules = splitAndTrim(ctx.String(HTTPApiFlag.Name)) } - if ctx.IsSet(RPCVirtualHostsFlag.Name) { - cfg.HTTPVirtualHosts = splitAndTrim(ctx.String(RPCVirtualHostsFlag.Name)) + if ctx.IsSet(HTTPVirtualHostsFlag.Name) { + cfg.HTTPVirtualHosts = splitAndTrim(ctx.String(HTTPVirtualHostsFlag.Name)) } } @@ -969,10 +1021,10 @@ func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error // setEtherbase retrieves the etherbase either from the directly specified // command line flags or from the keystore if CLI indexed. func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *ethconfig.Config) { - if ctx.IsSet(EtherbaseFlag.Name) { - account, err := MakeAddress(ks, ctx.String(EtherbaseFlag.Name)) + if ctx.IsSet(MinerEtherbaseFlag.Name) { + account, err := MakeAddress(ks, ctx.String(MinerEtherbaseFlag.Name)) if err != nil { - Fatalf("Option %q: %v", EtherbaseFlag.Name, err) + Fatalf("Option %q: %v", MinerEtherbaseFlag.Name, err) } cfg.Etherbase = account.Address } @@ -1317,8 +1369,8 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheGCFlag.Name) { cfg.TrieCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheGCFlag.Name) / 100 } - if ctx.IsSet(StakerThreadsFlag.Name) { - cfg.MinerThreads = ctx.Int(StakerThreadsFlag.Name) + if ctx.IsSet(MinerThreadsFlag.Name) { + cfg.MinerThreads = ctx.Int(MinerThreadsFlag.Name) } if ctx.IsSet(DocRootFlag.Name) { cfg.DocRoot = ctx.String(DocRootFlag.Name) @@ -1332,11 +1384,11 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { if ctx.IsSet(RPCGlobalGasCapFlag.Name) { cfg.RPCGasCap = ctx.Uint64(RPCGlobalGasCapFlag.Name) } - if ctx.IsSet(ExtraDataFlag.Name) { - cfg.ExtraData = []byte(ctx.String(ExtraDataFlag.Name)) + if ctx.IsSet(MinerExtraDataFlag.Name) { + cfg.ExtraData = []byte(ctx.String(MinerExtraDataFlag.Name)) } - if ctx.IsSet(GasPriceFlag.Name) { - cfg.GasPrice = flags.GlobalBig(ctx, GasPriceFlag.Name) + if ctx.IsSet(MinerGasPriceFlag.Name) { + cfg.GasPrice = flags.GlobalBig(ctx, MinerGasPriceFlag.Name) } if ctx.IsSet(CacheLogSizeFlag.Name) { cfg.FilterLogCacheSize = ctx.Int(CacheLogSizeFlag.Name) @@ -1388,7 +1440,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { log.Info("Using developer account", "address", developer.Address) cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.Int(DeveloperPeriodFlag.Name)), developer.Address) - if !ctx.IsSet(GasPriceFlag.Name) { + if !ctx.IsSet(MinerGasPriceFlag.Name) { cfg.GasPrice = big.NewInt(1) } } @@ -1398,7 +1450,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { // SetupNetwork configures the system for either the main net or some test network. func SetupNetwork(ctx *cli.Context) { // TODO(fjl): move target gas limit into config - params.TargetGasLimit = ctx.Uint64(TargetGasLimitFlag.Name) + params.TargetGasLimit = ctx.Uint64(MinerGasLimitFlag.Name) } // MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails. diff --git a/docker/XDPoSChain/entrypoint.sh b/docker/XDPoSChain/entrypoint.sh index 3ce0db757ccd..d080d1fb79a2 100755 --- a/docker/XDPoSChain/entrypoint.sh +++ b/docker/XDPoSChain/entrypoint.sh @@ -56,7 +56,7 @@ if [[ ! -z $NETWORK_ID ]]; then ;; 89 ) genesisPath="testnet.json" - params="$params --apothem --gcmode archive --rpcapi db,eth,net,web3,debug,XDPoS" + params="$params --apothem --gcmode archive --http-api db,eth,net,web3,debug,XDPoS" ;; 90 ) genesisPath="devnet.json" @@ -158,7 +158,7 @@ fi # debug mode if [[ ! -z $DEBUG_MODE ]]; then - params="$params --gcmode archive --rpcapi db,eth,net,web3,debug,XDPoS" + params="$params --gcmode archive --http-api db,eth,net,web3,debug,XDPoS" fi # maxpeers @@ -179,18 +179,18 @@ exec XDC $params \ --maxpeers $MAXPEERS \ --password ./password \ --port 30303 \ - --txpool.globalqueue 5000 \ - --txpool.globalslots 5000 \ - --rpc \ - --rpccorsdomain "*" \ - --rpcaddr 0.0.0.0 \ - --rpcport 8545 \ - --rpcvhosts "*" \ + --txpool-globalqueue 5000 \ + --txpool-globalslots 5000 \ + --http \ + --http-corsdomain "*" \ + --http-addr 0.0.0.0 \ + --http-port 8545 \ + --http-vhosts "*" \ --ws \ - --wsaddr 0.0.0.0 \ - --wsport 8546 \ - --wsorigins "*" \ + --ws-addr 0.0.0.0 \ + --ws-port 8546 \ + --ws-origins "*" \ --mine \ --gasprice "250000000" \ - --targetgaslimit "84000000" \ + --miner-gaslimit "84000000" \ "$@" diff --git a/internal/debug/flags.go b/internal/debug/flags.go index 6a366778c141..de8d587f0852 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -106,30 +106,35 @@ var ( Category: flags.LoggingCategory, } pprofPortFlag = &cli.IntFlag{ - Name: "pprofport", + Name: "pprof-port", + Aliases: []string{"pprofport"}, Usage: "pprof HTTP server listening port", Value: 6060, Category: flags.LoggingCategory, } pprofAddrFlag = &cli.StringFlag{ - Name: "pprofaddr", + Name: "pprof-addr", + Aliases: []string{"pprofaddr"}, Usage: "pprof HTTP server listening interface", Value: "127.0.0.1", Category: flags.LoggingCategory, } memprofilerateFlag = &cli.IntFlag{ - Name: "memprofilerate", + Name: "pprof-memprofilerate", + Aliases: []string{"memprofilerate"}, Usage: "Turn on memory profiling with the given rate", Value: runtime.MemProfileRate, Category: flags.LoggingCategory, } blockprofilerateFlag = &cli.IntFlag{ - Name: "blockprofilerate", + Name: "pprof-blockprofilerate", + Aliases: []string{"blockprofilerate"}, Usage: "Turn on block profiling with the given rate", Category: flags.LoggingCategory, } cpuprofileFlag = &cli.StringFlag{ - Name: "cpuprofile", + Name: "pprof-cpuprofile", + Aliases: []string{"cpuprofile"}, Usage: "Write CPU profile to the given file", Category: flags.LoggingCategory, } From 3e1ea87ca2b06e80acf2f0f90b861bea6c326dec Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 25 Nov 2024 16:39:29 +0800 Subject: [PATCH 246/479] cmd/XDC: fix some cli parsing issues (#25234) --- cmd/XDC/consolecmd.go | 5 ++++- internal/flags/helpers.go | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/cmd/XDC/consolecmd.go b/cmd/XDC/consolecmd.go index a5fad4b3673b..f6b0630293df 100644 --- a/cmd/XDC/consolecmd.go +++ b/cmd/XDC/consolecmd.go @@ -111,7 +111,10 @@ func localConsole(ctx *cli.Context) error { // remoteConsole will connect to a remote XDC instance, attaching a JavaScript // console to it. func remoteConsole(ctx *cli.Context) error { - // Attach to a remotely running XDC instance and start the JavaScript console + if ctx.Args().Len() > 1 { + utils.Fatalf("invalid command-line: too many arguments") + } + endpoint := ctx.Args().First() if endpoint == "" { path := node.DefaultDataDir() diff --git a/internal/flags/helpers.go b/internal/flags/helpers.go index e121aa544c40..4642b82c45a7 100644 --- a/internal/flags/helpers.go +++ b/internal/flags/helpers.go @@ -70,6 +70,10 @@ func MigrateGlobalFlags(ctx *cli.Context) { // This iterates over all commands and wraps their action function. iterate(ctx.App.Commands, func(cmd *cli.Command) { + if cmd.Action == nil { + return + } + action := cmd.Action cmd.Action = func(ctx *cli.Context) error { doMigrateFlags(ctx) From 9b8a82b91f62ee904e22d3723301b9e4349b5731 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 25 Nov 2024 16:39:29 +0800 Subject: [PATCH 247/479] internal/flags: fix issue with stringslice migration (#25830) This fixes a cornercase bug where the flag migration would mess up the value of StringSlice flags. --- internal/flags/helpers.go | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/internal/flags/helpers.go b/internal/flags/helpers.go index 4642b82c45a7..60dceb980be1 100644 --- a/internal/flags/helpers.go +++ b/internal/flags/helpers.go @@ -83,10 +83,34 @@ func MigrateGlobalFlags(ctx *cli.Context) { } func doMigrateFlags(ctx *cli.Context) { + // Figure out if there are any aliases of commands. If there are, we want + // to ignore them when iterating over the flags. + var aliases = make(map[string]bool) + for _, fl := range ctx.Command.Flags { + for _, alias := range fl.Names()[1:] { + aliases[alias] = true + } + } for _, name := range ctx.FlagNames() { for _, parent := range ctx.Lineage()[1:] { if parent.IsSet(name) { - ctx.Set(name, parent.String(name)) + // When iterating across the lineage, we will be served both + // the 'canon' and alias formats of all commmands. In most cases, + // it's fine to set it in the ctx multiple times (one for each + // name), however, the Slice-flags are not fine. + // The slice-flags accumulate, so if we set it once as + // "foo" and once as alias "F", then both will be present in the slice. + if _, isAlias := aliases[name]; isAlias { + continue + } + // If it is a string-slice, we need to set it as + // "alfa, beta, gamma" instead of "[alfa beta gamma]", in order + // for the backing StringSlice to parse it properly. + if result := parent.StringSlice(name); len(result) > 0 { + ctx.Set(name, strings.Join(result, ",")) + } else { + ctx.Set(name, parent.String(name)) + } break } } From fa699007374631a7272381de67649227158e302e Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 25 Nov 2024 16:39:29 +0800 Subject: [PATCH 248/479] Use filepath.clean instead of path.clean (#26404) * internal/flags: use filepath.Clean instead of path.Clean * internal/flags: fix windows pipe issue * internal/flags: modify test for windows * internal/flags: use backticks, fix test --- internal/flags/flags.go | 8 ++++++-- internal/flags/flags_test.go | 36 ++++++++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/internal/flags/flags.go b/internal/flags/flags.go index c3cc3098d6b5..808092ad1716 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -23,7 +23,7 @@ import ( "math/big" "os" "os/user" - "path" + "path/filepath" "strings" "github.com/XinFinOrg/XDPoSChain/common/math" @@ -314,12 +314,16 @@ func GlobalBig(ctx *cli.Context, name string) *big.Int { // 3. cleans the path, e.g. /a/b/../c -> /a/c // Note, it has limitations, e.g. ~someuser/tmp will not be expanded func expandPath(p string) string { + // Named pipes are not file paths on windows, ignore + if strings.HasPrefix(p, `\\.\pipe`) { + return p + } if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") { if home := HomeDir(); home != "" { p = home + p[1:] } } - return path.Clean(os.ExpandEnv(p)) + return filepath.Clean(os.ExpandEnv(p)) } func HomeDir() string { diff --git a/internal/flags/flags_test.go b/internal/flags/flags_test.go index a0d4af7ca360..681586b46c74 100644 --- a/internal/flags/flags_test.go +++ b/internal/flags/flags_test.go @@ -19,23 +19,43 @@ package flags import ( "os" "os/user" + "runtime" "testing" ) func TestPathExpansion(t *testing.T) { user, _ := user.Current() - tests := map[string]string{ - "/home/someuser/tmp": "/home/someuser/tmp", - "~/tmp": user.HomeDir + "/tmp", - "~thisOtherUser/b/": "~thisOtherUser/b", - "$DDDXXX/a/b": "/tmp/a/b", - "/a/b/": "/a/b", + var tests map[string]string + + if runtime.GOOS == "windows" { + tests = map[string]string{ + `/home/someuser/tmp`: `\home\someuser\tmp`, + `~/tmp`: user.HomeDir + `\tmp`, + `~thisOtherUser/b/`: `~thisOtherUser\b`, + `$DDDXXX/a/b`: `\tmp\a\b`, + `/a/b/`: `\a\b`, + `C:\Documents\Newsletters\`: `C:\Documents\Newsletters`, + `C:\`: `C:\`, + `\\.\pipe\\pipe\geth621383`: `\\.\pipe\\pipe\geth621383`, + } + } else { + tests = map[string]string{ + `/home/someuser/tmp`: `/home/someuser/tmp`, + `~/tmp`: user.HomeDir + `/tmp`, + `~thisOtherUser/b/`: `~thisOtherUser/b`, + `$DDDXXX/a/b`: `/tmp/a/b`, + `/a/b/`: `/a/b`, + `C:\Documents\Newsletters\`: `C:\Documents\Newsletters\`, + `C:\`: `C:\`, + `\\.\pipe\\pipe\geth621383`: `\\.\pipe\\pipe\geth621383`, + } } - os.Setenv("DDDXXX", "/tmp") + + os.Setenv(`DDDXXX`, `/tmp`) for test, expected := range tests { got := expandPath(test) if got != expected { - t.Errorf("test %s, got %s, expected %s\n", test, got, expected) + t.Errorf(`test %s, got %s, expected %s\n`, test, got, expected) } } } From bda88affe68d82a92c135f2671f99afd03459340 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 25 Nov 2024 16:39:29 +0800 Subject: [PATCH 249/479] cmd/XDC: add version in --help output (#26895) --- cmd/XDC/main.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go index 0ba6fc781b1a..0f19780283c1 100644 --- a/cmd/XDC/main.go +++ b/cmd/XDC/main.go @@ -167,7 +167,6 @@ var ( func init() { // Initialize the CLI app and start XDC app.Action = XDC - app.HideVersion = true // we have a command to print the version app.Copyright = "Copyright (c) 2024 XDPoSChain" app.Commands = []*cli.Command{ // See chaincmd.go: From 197551c7290a74263a591602cdf3c20b00fa2cf2 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 25 Nov 2024 16:39:29 +0800 Subject: [PATCH 250/479] cmd/geth, internal/flags, go.mod: colorize cli help, support env vars (#28103) --- cmd/XDC/main.go | 1 + internal/flags/flags.go | 9 +++-- internal/flags/helpers.go | 78 ++++++++++++++++++++++++++++++++------- 3 files changed, 71 insertions(+), 17 deletions(-) diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go index 0f19780283c1..db3e4e68f6fa 100644 --- a/cmd/XDC/main.go +++ b/cmd/XDC/main.go @@ -193,6 +193,7 @@ func init() { app.Flags = append(app.Flags, rpcFlags...) app.Flags = append(app.Flags, consoleFlags...) app.Flags = append(app.Flags, debug.Flags...) + flags.AutoEnvVars(app.Flags, "XDC") app.Before = func(ctx *cli.Context) error { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 808092ad1716..306ae72174a3 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -68,6 +68,7 @@ type DirectoryFlag struct { Value DirectoryString Aliases []string + EnvVars []string } // For cli.Flag: @@ -102,7 +103,7 @@ func (f *DirectoryFlag) GetCategory() string { return f.Category } func (f *DirectoryFlag) TakesValue() bool { return true } func (f *DirectoryFlag) GetUsage() string { return f.Usage } func (f *DirectoryFlag) GetValue() string { return f.Value.String() } -func (f *DirectoryFlag) GetEnvVars() []string { return nil } // env not supported +func (f *DirectoryFlag) GetEnvVars() []string { return f.EnvVars } func (f *DirectoryFlag) GetDefaultText() string { if f.DefaultText != "" { @@ -156,6 +157,7 @@ type TextMarshalerFlag struct { Value TextMarshaler Aliases []string + EnvVars []string } // For cli.Flag: @@ -187,7 +189,7 @@ func (f *TextMarshalerFlag) GetCategory() string { return f.Category } func (f *TextMarshalerFlag) TakesValue() bool { return true } func (f *TextMarshalerFlag) GetUsage() string { return f.Usage } -func (f *TextMarshalerFlag) GetEnvVars() []string { return nil } // env not supported +func (f *TextMarshalerFlag) GetEnvVars() []string { return f.EnvVars } func (f *TextMarshalerFlag) GetValue() string { t, err := f.Value.MarshalText() @@ -237,6 +239,7 @@ type BigFlag struct { Value *big.Int Aliases []string + EnvVars []string } // For cli.Flag: @@ -271,7 +274,7 @@ func (f *BigFlag) GetCategory() string { return f.Category } func (f *BigFlag) TakesValue() bool { return true } func (f *BigFlag) GetUsage() string { return f.Usage } func (f *BigFlag) GetValue() string { return f.Value.String() } -func (f *BigFlag) GetEnvVars() []string { return nil } // env not supported +func (f *BigFlag) GetEnvVars() []string { return f.EnvVars } func (f *BigFlag) GetDefaultText() string { if f.DefaultText != "" { diff --git a/internal/flags/helpers.go b/internal/flags/helpers.go index 60dceb980be1..eb9844272ea0 100644 --- a/internal/flags/helpers.go +++ b/internal/flags/helpers.go @@ -18,12 +18,19 @@ package flags import ( "fmt" + "os" + "regexp" "strings" "github.com/XinFinOrg/XDPoSChain/params" + "github.com/mattn/go-isatty" "github.com/urfave/cli/v2" ) +// usecolor defines whether the CLI help should use colored output or normal dumb +// colorless terminal formatting. +var usecolor = (isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())) && os.Getenv("TERM") != "dumb" + // NewApp creates an app with sane defaults. func NewApp(gitCommit, usage string) *cli.App { app := cli.NewApp() @@ -118,6 +125,14 @@ func doMigrateFlags(ctx *cli.Context) { } func init() { + if usecolor { + // Annotate all help categories with colors + cli.AppHelpTemplate = regexp.MustCompile("[A-Z ]+:").ReplaceAllString(cli.AppHelpTemplate, "\u001B[33m$0\u001B[0m") + + // Annotate flag categories with colors (private template, so need to + // copy-paste the entire thing here...) + cli.AppHelpTemplate = strings.ReplaceAll(cli.AppHelpTemplate, "{{template \"visibleFlagCategoryTemplate\" .}}", "{{range .VisibleFlagCategories}}\n {{if .Name}}\u001B[33m{{.Name}}\u001B[0m\n\n {{end}}{{$flglen := len .Flags}}{{range $i, $e := .Flags}}{{if eq (subtract $flglen $i) 1}}{{$e}}\n{{else}}{{$e}}\n {{end}}{{end}}{{end}}") + } cli.FlagStringer = FlagString } @@ -127,37 +142,31 @@ func FlagString(f cli.Flag) string { if !ok { return "" } - needsPlaceholder := df.TakesValue() placeholder := "" if needsPlaceholder { placeholder = "value" } - namesText := pad(cli.FlagNamePrefixer(df.Names(), placeholder), 30) + namesText := cli.FlagNamePrefixer(df.Names(), placeholder) defaultValueString := "" if s := df.GetDefaultText(); s != "" { defaultValueString = " (default: " + s + ")" } - - usage := strings.TrimSpace(df.GetUsage()) envHint := strings.TrimSpace(cli.FlagEnvHinter(df.GetEnvVars(), "")) - if len(envHint) > 0 { - usage += " " + envHint + if envHint != "" { + envHint = " (" + envHint[1:len(envHint)-1] + ")" } - + usage := strings.TrimSpace(df.GetUsage()) usage = wordWrap(usage, 80) usage = indent(usage, 10) - return fmt.Sprintf("\n %s%s\n%s", namesText, defaultValueString, usage) -} - -func pad(s string, length int) string { - if len(s) < length { - s += strings.Repeat(" ", length-len(s)) + if usecolor { + return fmt.Sprintf("\n \u001B[32m%-35s%-35s\u001B[0m%s\n%s", namesText, defaultValueString, envHint, usage) + } else { + return fmt.Sprintf("\n %-35s%-35s%s\n%s", namesText, defaultValueString, envHint, usage) } - return s } func indent(s string, nspace int) string { @@ -202,3 +211,44 @@ func wordWrap(s string, width int) string { return output.String() } + +// AutoEnvVars extends all the specific CLI flags with automatically generated +// env vars by capitalizing the flag, replacing . with _ and prefixing it with +// the specified string. +// +// Note, the prefix should *not* contain the separator underscore, that will be +// added automatically. +func AutoEnvVars(flags []cli.Flag, prefix string) { + for _, flag := range flags { + envvar := strings.ToUpper(prefix + "_" + strings.ReplaceAll(strings.ReplaceAll(flag.Names()[0], ".", "_"), "-", "_")) + + switch flag := flag.(type) { + case *cli.StringFlag: + flag.EnvVars = append(flag.EnvVars, envvar) + + case *cli.BoolFlag: + flag.EnvVars = append(flag.EnvVars, envvar) + + case *cli.IntFlag: + flag.EnvVars = append(flag.EnvVars, envvar) + + case *cli.Uint64Flag: + flag.EnvVars = append(flag.EnvVars, envvar) + + case *cli.DurationFlag: + flag.EnvVars = append(flag.EnvVars, envvar) + + case *cli.PathFlag: + flag.EnvVars = append(flag.EnvVars, envvar) + + case *BigFlag: + flag.EnvVars = append(flag.EnvVars, envvar) + + case *TextMarshalerFlag: + flag.EnvVars = append(flag.EnvVars, envvar) + + case *DirectoryFlag: + flag.EnvVars = append(flag.EnvVars, envvar) + } + } +} From 87ddf502ced68c964b2531ba36f9351446d67dc4 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 25 Nov 2024 16:39:29 +0800 Subject: [PATCH 251/479] internal/flags: fix loading env vars for custom flags (#28117) --- internal/flags/flags.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 306ae72174a3..fc83af0c166c 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -20,11 +20,13 @@ import ( "encoding" "errors" "flag" + "fmt" "math/big" "os" "os/user" "path/filepath" "strings" + "syscall" "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/urfave/cli/v2" @@ -80,6 +82,14 @@ func (f *DirectoryFlag) String() string { return cli.FlagStringer(f) } // Apply called by cli library, grabs variable from environment (if in env) // and adds variable to flag set for parsing. func (f *DirectoryFlag) Apply(set *flag.FlagSet) error { + for _, envVar := range f.EnvVars { + envVar = strings.TrimSpace(envVar) + if value, found := syscall.Getenv(envVar); found { + f.Value.Set(value) + f.HasBeenSet = true + break + } + } eachName(f, func(name string) { set.Var(&f.Value, name, f.Usage) }) @@ -167,6 +177,16 @@ func (f *TextMarshalerFlag) IsSet() bool { return f.HasBeenSet } func (f *TextMarshalerFlag) String() string { return cli.FlagStringer(f) } func (f *TextMarshalerFlag) Apply(set *flag.FlagSet) error { + for _, envVar := range f.EnvVars { + envVar = strings.TrimSpace(envVar) + if value, found := syscall.Getenv(envVar); found { + if err := f.Value.UnmarshalText([]byte(value)); err != nil { + return fmt.Errorf("could not parse %q from environment variable %q for flag %s: %s", value, envVar, f.Name, err) + } + f.HasBeenSet = true + break + } + } eachName(f, func(name string) { set.Var(textMarshalerVal{f.Value}, name, f.Usage) }) @@ -249,6 +269,16 @@ func (f *BigFlag) IsSet() bool { return f.HasBeenSet } func (f *BigFlag) String() string { return cli.FlagStringer(f) } func (f *BigFlag) Apply(set *flag.FlagSet) error { + for _, envVar := range f.EnvVars { + envVar = strings.TrimSpace(envVar) + if value, found := syscall.Getenv(envVar); found { + if _, ok := f.Value.SetString(value, 10); !ok { + return fmt.Errorf("could not parse %q from environment variable %q for flag %s", value, envVar, f.Name) + } + f.HasBeenSet = true + break + } + } eachName(f, func(name string) { f.Value = new(big.Int) set.Var((*bigValue)(f.Value), name, f.Usage) From 4e192a51723dff7002bcc5b130130655026e821d Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 25 Nov 2024 16:39:29 +0800 Subject: [PATCH 252/479] cmd/XDC, internal/flags: print envvar config source and bad names (#28119) --- cmd/XDC/main.go | 1 + internal/flags/helpers.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go index db3e4e68f6fa..d57040b8cc9b 100644 --- a/cmd/XDC/main.go +++ b/cmd/XDC/main.go @@ -201,6 +201,7 @@ func init() { if err := debug.Setup(ctx); err != nil { return err } + flags.CheckEnvVars(ctx, app.Flags, "XDC") // Start system runtime metrics collection go metrics.CollectProcessMetrics(3 * time.Second) diff --git a/internal/flags/helpers.go b/internal/flags/helpers.go index eb9844272ea0..a35b9f609f5d 100644 --- a/internal/flags/helpers.go +++ b/internal/flags/helpers.go @@ -20,8 +20,10 @@ import ( "fmt" "os" "regexp" + "sort" "strings" + "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" "github.com/mattn/go-isatty" "github.com/urfave/cli/v2" @@ -252,3 +254,37 @@ func AutoEnvVars(flags []cli.Flag, prefix string) { } } } + +// CheckEnvVars iterates over all the environment variables and checks if any of +// them look like a CLI flag but is not consumed. This can be used to detect old +// or mistyped names. +func CheckEnvVars(ctx *cli.Context, flags []cli.Flag, prefix string) { + known := make(map[string]string) + for _, flag := range flags { + docflag, ok := flag.(cli.DocGenerationFlag) + if !ok { + continue + } + for _, envvar := range docflag.GetEnvVars() { + known[envvar] = flag.Names()[0] + } + } + keyvals := os.Environ() + sort.Strings(keyvals) + + for _, keyval := range keyvals { + key := strings.Split(keyval, "=")[0] + if !strings.HasPrefix(key, prefix) { + continue + } + if flag, ok := known[key]; ok { + if ctx.Count(flag) > 0 { + log.Info("Config environment variable found", "envvar", key, "shadowedby", "--"+flag) + } else { + log.Info("Config environment variable found", "envvar", key) + } + } else { + log.Warn("Unknown config environment variable", "envvar", key) + } + } +} From ac611170d1e48968d21ea17f9d3becbd4cb3c654 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 25 Nov 2024 16:39:29 +0800 Subject: [PATCH 253/479] internal/flags: add missing flag types for auto-env-var generation (#28692) Certain flags, such as `--rpc.txfeecap` currently do not have an env-var auto-generated for them. This change adds three missing cli flag types to the auto env-var helper function to fix this. --- internal/flags/helpers.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/internal/flags/helpers.go b/internal/flags/helpers.go index a35b9f609f5d..f10dd183200b 100644 --- a/internal/flags/helpers.go +++ b/internal/flags/helpers.go @@ -94,7 +94,7 @@ func MigrateGlobalFlags(ctx *cli.Context) { func doMigrateFlags(ctx *cli.Context) { // Figure out if there are any aliases of commands. If there are, we want // to ignore them when iterating over the flags. - var aliases = make(map[string]bool) + aliases := make(map[string]bool) for _, fl := range ctx.Command.Flags { for _, alias := range fl.Names()[1:] { aliases[alias] = true @@ -228,15 +228,24 @@ func AutoEnvVars(flags []cli.Flag, prefix string) { case *cli.StringFlag: flag.EnvVars = append(flag.EnvVars, envvar) + case *cli.StringSliceFlag: + flag.EnvVars = append(flag.EnvVars, envvar) + case *cli.BoolFlag: flag.EnvVars = append(flag.EnvVars, envvar) case *cli.IntFlag: flag.EnvVars = append(flag.EnvVars, envvar) + case *cli.Int64Flag: + flag.EnvVars = append(flag.EnvVars, envvar) + case *cli.Uint64Flag: flag.EnvVars = append(flag.EnvVars, envvar) + case *cli.Float64Flag: + flag.EnvVars = append(flag.EnvVars, envvar) + case *cli.DurationFlag: flag.EnvVars = append(flag.EnvVars, envvar) From a6192a59803cfd6102ddcc29d2264d79047a3ff8 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 25 Nov 2024 16:39:29 +0800 Subject: [PATCH 254/479] internal/flags: fix --miner.gasprice default listing (#28932) --- internal/flags/flags.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/internal/flags/flags.go b/internal/flags/flags.go index fc83af0c166c..7d48383749e8 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -256,7 +256,8 @@ type BigFlag struct { Hidden bool HasBeenSet bool - Value *big.Int + Value *big.Int + defaultValue *big.Int Aliases []string EnvVars []string @@ -269,6 +270,10 @@ func (f *BigFlag) IsSet() bool { return f.HasBeenSet } func (f *BigFlag) String() string { return cli.FlagStringer(f) } func (f *BigFlag) Apply(set *flag.FlagSet) error { + // Set default value so that environment wont be able to overwrite it + if f.Value != nil { + f.defaultValue = new(big.Int).Set(f.Value) + } for _, envVar := range f.EnvVars { envVar = strings.TrimSpace(envVar) if value, found := syscall.Getenv(envVar); found { @@ -283,7 +288,6 @@ func (f *BigFlag) Apply(set *flag.FlagSet) error { f.Value = new(big.Int) set.Var((*bigValue)(f.Value), name, f.Usage) }) - return nil } @@ -310,7 +314,7 @@ func (f *BigFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - return f.GetValue() + return f.defaultValue.String() } // bigValue turns *big.Int into a flag.Value From ae56946690abbcb5dcfb6f3f3de5bb295607e6f8 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 25 Nov 2024 16:39:29 +0800 Subject: [PATCH 255/479] internal/flags: remove low-use type TextMarshalerFlag (#30707) --- cmd/XDC/chaincmd.go | 6 +- cmd/utils/flags.go | 9 +-- internal/flags/flags.go | 114 -------------------------------------- internal/flags/helpers.go | 3 - 4 files changed, 9 insertions(+), 123 deletions(-) diff --git a/cmd/XDC/chaincmd.go b/cmd/XDC/chaincmd.go index 31603d52c1f6..5f12048f2cac 100644 --- a/cmd/XDC/chaincmd.go +++ b/cmd/XDC/chaincmd.go @@ -34,7 +34,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/eth/downloader" "github.com/XinFinOrg/XDPoSChain/event" - "github.com/XinFinOrg/XDPoSChain/internal/flags" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/metrics" "github.com/urfave/cli/v2" @@ -409,7 +408,10 @@ func copyDb(ctx *cli.Context) error { chain, chainDb := utils.MakeChain(ctx, stack) defer chainDb.Close() - syncmode := *flags.GlobalTextMarshaler(ctx, utils.SyncModeFlag.Name).(*downloader.SyncMode) + var syncmode downloader.SyncMode + if err := syncmode.UnmarshalText([]byte(ctx.String(utils.SyncModeFlag.Name))); err != nil { + utils.Fatalf("invalid --syncmode flag: %v", err) + } dl := downloader.New(syncmode, chainDb, new(event.TypeMux), chain, nil, nil, nil) // Create a source peer to satisfy downloader requests from diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index a6b54f96447f..fc8505b16562 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -129,11 +129,10 @@ var ( Category: flags.APICategory, } - defaultSyncMode = ethconfig.Defaults.SyncMode - SyncModeFlag = &flags.TextMarshalerFlag{ + SyncModeFlag = &cli.StringFlag{ Name: "syncmode", Usage: `Blockchain sync mode ("fast", "full", or "light")`, - Value: &defaultSyncMode, + Value: ethconfig.Defaults.SyncMode.String(), Category: flags.EthCategory, } GCModeFlag = &cli.StringFlag{ @@ -1340,7 +1339,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { switch { case ctx.IsSet(SyncModeFlag.Name): - cfg.SyncMode = *flags.GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode) + if err = cfg.SyncMode.UnmarshalText([]byte(ctx.String(SyncModeFlag.Name))); err != nil { + Fatalf("invalid --syncmode flag: %v", err) + } case ctx.Bool(FastSyncFlag.Name): cfg.SyncMode = downloader.FastSync case ctx.Bool(LightModeFlag.Name): diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 7d48383749e8..427442710ad9 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -17,7 +17,6 @@ package flags import ( - "encoding" "errors" "flag" "fmt" @@ -122,119 +121,6 @@ func (f *DirectoryFlag) GetDefaultText() string { return f.GetValue() } -type TextMarshaler interface { - encoding.TextMarshaler - encoding.TextUnmarshaler -} - -// textMarshalerVal turns a TextMarshaler into a flag.Value -type textMarshalerVal struct { - v TextMarshaler -} - -func (v textMarshalerVal) String() string { - if v.v == nil { - return "" - } - text, _ := v.v.MarshalText() - return string(text) -} - -func (v textMarshalerVal) Set(s string) error { - return v.v.UnmarshalText([]byte(s)) -} - -var ( - _ cli.Flag = (*TextMarshalerFlag)(nil) - _ cli.RequiredFlag = (*TextMarshalerFlag)(nil) - _ cli.VisibleFlag = (*TextMarshalerFlag)(nil) - _ cli.DocGenerationFlag = (*TextMarshalerFlag)(nil) - _ cli.CategorizableFlag = (*TextMarshalerFlag)(nil) -) - -// TextMarshalerFlag wraps a TextMarshaler value. -type TextMarshalerFlag struct { - Name string - - Category string - DefaultText string - Usage string - - Required bool - Hidden bool - HasBeenSet bool - - Value TextMarshaler - - Aliases []string - EnvVars []string -} - -// For cli.Flag: - -func (f *TextMarshalerFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) } -func (f *TextMarshalerFlag) IsSet() bool { return f.HasBeenSet } -func (f *TextMarshalerFlag) String() string { return cli.FlagStringer(f) } - -func (f *TextMarshalerFlag) Apply(set *flag.FlagSet) error { - for _, envVar := range f.EnvVars { - envVar = strings.TrimSpace(envVar) - if value, found := syscall.Getenv(envVar); found { - if err := f.Value.UnmarshalText([]byte(value)); err != nil { - return fmt.Errorf("could not parse %q from environment variable %q for flag %s: %s", value, envVar, f.Name, err) - } - f.HasBeenSet = true - break - } - } - eachName(f, func(name string) { - set.Var(textMarshalerVal{f.Value}, name, f.Usage) - }) - return nil -} - -// For cli.RequiredFlag: - -func (f *TextMarshalerFlag) IsRequired() bool { return f.Required } - -// For cli.VisibleFlag: - -func (f *TextMarshalerFlag) IsVisible() bool { return !f.Hidden } - -// For cli.CategorizableFlag: - -func (f *TextMarshalerFlag) GetCategory() string { return f.Category } - -// For cli.DocGenerationFlag: - -func (f *TextMarshalerFlag) TakesValue() bool { return true } -func (f *TextMarshalerFlag) GetUsage() string { return f.Usage } -func (f *TextMarshalerFlag) GetEnvVars() []string { return f.EnvVars } - -func (f *TextMarshalerFlag) GetValue() string { - t, err := f.Value.MarshalText() - if err != nil { - return "(ERR: " + err.Error() + ")" - } - return string(t) -} - -func (f *TextMarshalerFlag) GetDefaultText() string { - if f.DefaultText != "" { - return f.DefaultText - } - return f.GetValue() -} - -// GlobalTextMarshaler returns the value of a TextMarshalerFlag from the global flag set. -func GlobalTextMarshaler(ctx *cli.Context, name string) TextMarshaler { - val := ctx.Generic(name) - if val == nil { - return nil - } - return val.(textMarshalerVal).v -} - var ( _ cli.Flag = (*BigFlag)(nil) _ cli.RequiredFlag = (*BigFlag)(nil) diff --git a/internal/flags/helpers.go b/internal/flags/helpers.go index f10dd183200b..32935e87f925 100644 --- a/internal/flags/helpers.go +++ b/internal/flags/helpers.go @@ -255,9 +255,6 @@ func AutoEnvVars(flags []cli.Flag, prefix string) { case *BigFlag: flag.EnvVars = append(flag.EnvVars, envvar) - case *TextMarshalerFlag: - flag.EnvVars = append(flag.EnvVars, envvar) - case *DirectoryFlag: flag.EnvVars = append(flag.EnvVars, envvar) } From a8560300a420a1869830fc02ea9e125d2e63fbeb Mon Sep 17 00:00:00 2001 From: benjamin202410 Date: Fri, 29 Nov 2024 03:14:25 -0800 Subject: [PATCH 256/479] fix vote test and optimize log (#750) Co-authored-by: liam.lai --- consensus/XDPoS/engines/engine_v2/vote.go | 8 ++++++-- consensus/XDPoS/utils/errors.go | 13 +++++++++++++ consensus/tests/engine_v2_tests/vote_test.go | 11 +++++------ eth/bft/bft_handler.go | 4 ++++ eth/handler.go | 2 +- eth/hooks/engine_v2_hooks.go | 1 - 6 files changed, 29 insertions(+), 10 deletions(-) diff --git a/consensus/XDPoS/engines/engine_v2/vote.go b/consensus/XDPoS/engines/engine_v2/vote.go index 592a5741cb91..f31e95c86284 100644 --- a/consensus/XDPoS/engines/engine_v2/vote.go +++ b/consensus/XDPoS/engines/engine_v2/vote.go @@ -78,8 +78,12 @@ func (x *XDPoS_v2) voteHandler(chain consensus.ChainReader, voteMsg *types.Vote) epochInfo, err := x.getEpochSwitchInfo(chain, nil, voteMsg.ProposedBlockInfo.Hash) if err != nil { - log.Error("[voteHandler] Error when getting epoch switch Info", "error", err) - return errors.New("fail on voteHandler due to failure in getting epoch switch info") + return &utils.ErrIncomingMessageBlockNotFound{ + Type: "vote", + IncomingBlockHash: voteMsg.ProposedBlockInfo.Hash, + IncomingBlockNumber: voteMsg.ProposedBlockInfo.Number, + Err: err, + } } certThreshold := x.config.V2.Config(uint64(voteMsg.ProposedBlockInfo.Round)).CertThreshold diff --git a/consensus/XDPoS/utils/errors.go b/consensus/XDPoS/utils/errors.go index da83ec706474..eec246b54b7b 100644 --- a/consensus/XDPoS/utils/errors.go +++ b/consensus/XDPoS/utils/errors.go @@ -3,7 +3,9 @@ package utils import ( "errors" "fmt" + "math/big" + "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/types" ) @@ -120,3 +122,14 @@ type ErrIncomingMessageRoundTooFarFromCurrentRound struct { func (e *ErrIncomingMessageRoundTooFarFromCurrentRound) Error() string { return fmt.Sprintf("%s message round number: %v is too far away from currentRound: %v", e.Type, e.IncomingRound, e.CurrentRound) } + +type ErrIncomingMessageBlockNotFound struct { + Type string + IncomingBlockHash common.Hash + IncomingBlockNumber *big.Int + Err error +} + +func (e *ErrIncomingMessageBlockNotFound) Error() string { + return fmt.Sprintf("%s proposed block is not found hash: %v, block number: %v, error: %s", e.Type, e.IncomingBlockHash.Hex(), e.IncomingBlockNumber, e.Err) +} diff --git a/consensus/tests/engine_v2_tests/vote_test.go b/consensus/tests/engine_v2_tests/vote_test.go index 4a58ad08abda..afaf5e818dfe 100644 --- a/consensus/tests/engine_v2_tests/vote_test.go +++ b/consensus/tests/engine_v2_tests/vote_test.go @@ -10,7 +10,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/accounts" "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" - "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/params" @@ -195,11 +194,11 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) { } func TestThrowErrorIfVoteMsgRoundIsMoreThanOneRoundAwayFromCurrentRound(t *testing.T) { - blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 blockInfo := &types.BlockInfo{ - Hash: common.HexToHash("0x1"), + Hash: currentBlock.Hash(), Round: types.Round(6), Number: big.NewInt(999), } @@ -397,7 +396,7 @@ func TestVoteMessageShallNotThrowErrorIfBlockNotYetExist(t *testing.T) { } err := engineV2.VoteHandler(blockchain, voteMsg) - assert.Nil(t, err) + assert.Contains(t, err.Error(), "proposed block is not found") voteMsg = &types.Vote{ ProposedBlockInfo: blockInfo, @@ -405,7 +404,7 @@ func TestVoteMessageShallNotThrowErrorIfBlockNotYetExist(t *testing.T) { GapNumber: 450, } err = engineV2.VoteHandler(blockchain, voteMsg) - assert.Nil(t, err) + assert.Contains(t, err.Error(), "proposed block is not found") // Create a vote message that should trigger vote pool hook, but it shall not produce any QC yet voteMsg = &types.Vote{ @@ -415,7 +414,7 @@ func TestVoteMessageShallNotThrowErrorIfBlockNotYetExist(t *testing.T) { } err = engineV2.VoteHandler(blockchain, voteMsg) - assert.Nil(t, err) + assert.Contains(t, err.Error(), "proposed block is not found") currentRound, lockQuorumCert, highestQuorumCert, _, _, _ := engineV2.GetPropertiesFaker() // Still using the initlised value because we did not yet go to the next round assert.Nil(t, lockQuorumCert) diff --git a/eth/bft/bft_handler.go b/eth/bft/bft_handler.go index 44d7c9b42378..267153dfb55d 100644 --- a/eth/bft/bft_handler.go +++ b/eth/bft/bft_handler.go @@ -101,6 +101,10 @@ func (b *Bfter) Vote(peer string, vote *types.Vote) error { log.Debug("vote round not equal", "error", err, "vote", vote.Hash()) return err } + if _, ok := err.(*utils.ErrIncomingMessageBlockNotFound); ok { + log.Debug("vote proposed block not found", "error", err, "vote", vote.Hash()) + return err + } log.Error("handle BFT Vote", "error", err) return err } diff --git a/eth/handler.go b/eth/handler.go index 3bb563ddcdaa..503671eb5280 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -854,7 +854,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if !exist { go pm.bft.Vote(p.id, &vote) } else { - log.Debug("Discarded vote, known vote", "vote hash", vote.Hash(), "voted block hash", vote.ProposedBlockInfo.Hash.Hex(), "number", vote.ProposedBlockInfo.Number, "round", vote.ProposedBlockInfo.Round) + log.Trace("Discarded vote, known vote", "vote hash", vote.Hash(), "voted block hash", vote.ProposedBlockInfo.Hash.Hex(), "number", vote.ProposedBlockInfo.Number, "round", vote.ProposedBlockInfo.Round) } case msg.Code == TimeoutMsg: diff --git a/eth/hooks/engine_v2_hooks.go b/eth/hooks/engine_v2_hooks.go index 8cb27cdd49c4..fde8e924879f 100644 --- a/eth/hooks/engine_v2_hooks.go +++ b/eth/hooks/engine_v2_hooks.go @@ -64,7 +64,6 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf parentNumber-- parentHash = parentHeader.ParentHash listBlockHash = append(listBlockHash, parentHash) - log.Debug("[HookPenalty] listBlockHash", "i", i, "len", len(listBlockHash), "parentHash", parentHash, "parentNumber", parentNumber) } // add list not miner to penalties From 7d41e834e4deab5d58dc219b51df9a4a923d0980 Mon Sep 17 00:00:00 2001 From: wanwiset25 Date: Fri, 29 Nov 2024 10:54:57 +0400 Subject: [PATCH 257/479] new devnet bootnodes --- cicd/devnet/bootnodes.list | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/cicd/devnet/bootnodes.list b/cicd/devnet/bootnodes.list index 72592bff40f8..618052572b94 100644 --- a/cicd/devnet/bootnodes.list +++ b/cicd/devnet/bootnodes.list @@ -1,5 +1,2 @@ -enode://207f812067141e038439acbd18a6e3b5f38fcff7de362d8fe0eb5f153f828ae99cae3270ed653beaa197e3946223d9e2f4a22a5e948177209d046fd095b89626@66.94.121.151:30301 -enode://ec569f5d52cefee5c5405a0c5db720dc7061f3085e0682dd8321413430ddda6a177b85db75b0daf83d2e68760ba3f5beb4ba9e333e7d52072fba4d39b05a0451@194.233.77.19:30301 -enode://cab457c58bc9b94ea9afa428d018179b4773fe90fec16ef958951f5a23adb9627350cea8b56709351766183d85574fc54ba504816b200fa31675a04c25226e9f@66.94.98.186:30301 -enode://751de9e39fe0b8f18585bb4a8b50aa5631fe262e14f579753709631eddcac30be901ca0b5a3878b7686838da7ddca2c61e6d2c5eb3745e6b855289e692068e8d@144.126.140.3:30301 -enode://6bd073ee1085c1dc09427d9f65f0125a75393ac89f0db36f884cc22d61f441403a4c06d8f9a9d3c8e8c08368122bceeeac5f48c83f3d2e87c0da6d5c0eb7cd7e@194.163.167.177:30301 \ No newline at end of file +enode://00d49d72a48164681906ad61924568da0d3049937efdbaed0b7533e34a99f55814f1839d909cdc82f78e04a36ac04737d80b41b22905c7d6cac3c80bb5cdbbc4@66.94.98.186:30301 +enode://d6793b02a478f13ed6d01c30778935f6f8f7461a75aebedcb310def4ed9b066f995a0dca046d0c7ea7f5ffdd8e3f1f53c6b6dce909d1693650504921aad62f1a@194.163.167.177:30301 From c369253b5cf4de4fd38fa604a32b53b14c24676a Mon Sep 17 00:00:00 2001 From: wanwiset25 Date: Fri, 29 Nov 2024 11:29:35 +0400 Subject: [PATCH 258/479] update bootnode check script --- cicd/devnet/start.sh | 7 +++++++ cicd/mainnet/start.sh | 7 +++++++ cicd/testnet/start.sh | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/cicd/devnet/start.sh b/cicd/devnet/start.sh index dcc6e86ee78b..5a002b96da4b 100755 --- a/cicd/devnet/start.sh +++ b/cicd/devnet/start.sh @@ -24,6 +24,13 @@ do bootnodes="${bootnodes},$line" fi done < "$input" +#check last line since it's not included in "read" command https://stackoverflow.com/questions/12916352/shell-script-read-missing-last-line +if [ -z "${bootnodes}" ] +then + bootnodes=$line +else + bootnodes="${bootnodes},$line" +fi log_level=3 if test -z "$LOG_LEVEL" diff --git a/cicd/mainnet/start.sh b/cicd/mainnet/start.sh index b6bca08f794e..6bb03e14ffc5 100755 --- a/cicd/mainnet/start.sh +++ b/cicd/mainnet/start.sh @@ -24,6 +24,13 @@ do bootnodes="${bootnodes},$line" fi done < "$input" +#check last line since it's not included in "read" command https://stackoverflow.com/questions/12916352/shell-script-read-missing-last-line +if [ -z "${bootnodes}" ] +then + bootnodes=$line +else + bootnodes="${bootnodes},$line" +fi log_level=3 if test -z "$LOG_LEVEL" diff --git a/cicd/testnet/start.sh b/cicd/testnet/start.sh index e506d1df45ec..6918c7a1ef07 100755 --- a/cicd/testnet/start.sh +++ b/cicd/testnet/start.sh @@ -25,6 +25,13 @@ do bootnodes="${bootnodes},$line" fi done < "$input" +#check last line since it's not included in "read" command https://stackoverflow.com/questions/12916352/shell-script-read-missing-last-line +if [ -z "${bootnodes}" ] +then + bootnodes=$line +else + bootnodes="${bootnodes},$line" +fi log_level=3 if test -z "$LOG_LEVEL" From f646ddd2dbb6ec96a159601e56ab7e466e923619 Mon Sep 17 00:00:00 2001 From: wanwiset25 Date: Tue, 26 Nov 2024 14:26:57 +0400 Subject: [PATCH 259/479] add store-reward flag by default --- cicd/devnet/start.sh | 1 + cicd/mainnet/start.sh | 1 + cicd/testnet/start.sh | 1 + 3 files changed, 3 insertions(+) diff --git a/cicd/devnet/start.sh b/cicd/devnet/start.sh index dcc6e86ee78b..fc826632d131 100755 --- a/cicd/devnet/start.sh +++ b/cicd/devnet/start.sh @@ -81,5 +81,6 @@ XDC --ethstats ${netstats} --gcmode archive \ --http-vhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \ --miner-gasprice "1" --miner-gaslimit "420000000" --verbosity ${log_level} \ --debugdatadir /work/xdcchain \ +--store-reward \ --ws --ws-addr=0.0.0.0 --ws-port $ws_port \ --ws-origins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log diff --git a/cicd/mainnet/start.sh b/cicd/mainnet/start.sh index b6bca08f794e..22e21150748f 100755 --- a/cicd/mainnet/start.sh +++ b/cicd/mainnet/start.sh @@ -80,5 +80,6 @@ XDC --ethstats ${netstats} --gcmode archive \ --http-vhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \ --miner-gasprice "1" --miner-gaslimit "420000000" --verbosity ${log_level} \ --debugdatadir /work/xdcchain \ +--store-reward \ --ws --ws-addr=0.0.0.0 --ws-port $ws_port \ --ws-origins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log diff --git a/cicd/testnet/start.sh b/cicd/testnet/start.sh index e506d1df45ec..bfdf2a806eba 100755 --- a/cicd/testnet/start.sh +++ b/cicd/testnet/start.sh @@ -82,5 +82,6 @@ XDC --ethstats ${netstats} --gcmode archive \ --http-vhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \ --miner-gasprice "1" --miner-gaslimit "420000000" --verbosity ${log_level} \ --debugdatadir /work/xdcchain \ +--store-reward \ --ws --ws-addr=0.0.0.0 --ws-port $ws_port \ --ws-origins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log From e257b521cea4118b927481cda775f9ae4025cdb5 Mon Sep 17 00:00:00 2001 From: wanwiset25 Date: Mon, 25 Nov 2024 22:17:04 +0400 Subject: [PATCH 260/479] change devnetstats url --- cicd/devnet/start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cicd/devnet/start.sh b/cicd/devnet/start.sh index fc826632d131..e7dcb24c2005 100755 --- a/cicd/devnet/start.sh +++ b/cicd/devnet/start.sh @@ -62,7 +62,7 @@ else fi INSTANCE_IP=$(curl https://checkip.amazonaws.com) -netstats="${NODE_NAME}-${wallet}-${INSTANCE_IP}:xinfin_xdpos_hybrid_network_stats@devnetstats.apothem.network:2000" +netstats="${NODE_NAME}-${wallet}-${INSTANCE_IP}:xinfin_xdpos_hybrid_network_stats@devnetstats.hashlab.apothem.network:1999" echo "Running a node with wallet: ${wallet} at IP: ${INSTANCE_IP}" From 448fe9a746aadeb008c4f5c8bb1171dabd1411a1 Mon Sep 17 00:00:00 2001 From: wanwiset25 Date: Tue, 3 Dec 2024 02:29:40 +0400 Subject: [PATCH 261/479] fix typo and local script --- cicd/devnet/start-local-devnet.sh | 2 +- cicd/devnet/start.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cicd/devnet/start-local-devnet.sh b/cicd/devnet/start-local-devnet.sh index 230e09f8215d..af2b09f6fcda 100755 --- a/cicd/devnet/start-local-devnet.sh +++ b/cicd/devnet/start-local-devnet.sh @@ -46,7 +46,7 @@ else log_level=$LOG_LEVEL fi -netstats="${NODE_NAME}-${wallet}-local:xinfin_xdpos_hybrid_network_stats@devnetstats.apothem.network:2000" +netstats="${NODE_NAME}-${wallet}-local:xinfin_xdpos_hybrid_network_stats@devnetstats.hashlabs.apothem.network:1999" echo "Running a node with wallet: ${wallet} at local" diff --git a/cicd/devnet/start.sh b/cicd/devnet/start.sh index e027944cf3a8..8e52b1c92380 100755 --- a/cicd/devnet/start.sh +++ b/cicd/devnet/start.sh @@ -69,7 +69,7 @@ else fi INSTANCE_IP=$(curl https://checkip.amazonaws.com) -netstats="${NODE_NAME}-${wallet}-${INSTANCE_IP}:xinfin_xdpos_hybrid_network_stats@devnetstats.hashlab.apothem.network:1999" +netstats="${NODE_NAME}-${wallet}-${INSTANCE_IP}:xinfin_xdpos_hybrid_network_stats@devnetstats.hashlabs.apothem.network:1999" echo "Running a node with wallet: ${wallet} at IP: ${INSTANCE_IP}" From d55dca04b511319c394c931a7fdc07da4f7006a1 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 5 Dec 2024 10:59:42 +0800 Subject: [PATCH 262/479] all: use http package to replace http method names (26535) --- cmd/faucet/faucet.go | 2 +- node/rpcstack_test.go | 2 +- p2p/simulations/http.go | 8 ++++---- rpc/http.go | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go index 0d94389243d6..36388d79962a 100644 --- a/cmd/faucet/faucet.go +++ b/cmd/faucet/faucet.go @@ -657,7 +657,7 @@ func sendSuccess(conn *wsConn, msg string) error { func authGitHub(url string) (string, string, common.Address, error) { // Retrieve the gist from the GitHub Gist APIs parts := strings.Split(url, "/") - req, _ := http.NewRequest("GET", "https://api.github.com/gists/"+parts[len(parts)-1], nil) + req, _ := http.NewRequest(http.MethodGet, "https://api.github.com/gists/"+parts[len(parts)-1], nil) if *githubUser != "" { req.SetBasicAuth(*githubUser, *githubToken) } diff --git a/node/rpcstack_test.go b/node/rpcstack_test.go index db08bd40cb43..38f486e52a89 100644 --- a/node/rpcstack_test.go +++ b/node/rpcstack_test.go @@ -39,7 +39,7 @@ func TestNewWebsocketUpgradeHandler_websocket(t *testing.T) { // TestIsWebsocket tests if an incoming websocket upgrade request is handled properly. func TestIsWebsocket(t *testing.T) { - r, _ := http.NewRequest("GET", "/", nil) + r, _ := http.NewRequest(http.MethodGet, "/", nil) assert.False(t, isWebsocket(r)) r.Header.Set("upgrade", "websocket") diff --git a/p2p/simulations/http.go b/p2p/simulations/http.go index 84f3af61cd0c..f24695882be9 100644 --- a/p2p/simulations/http.go +++ b/p2p/simulations/http.go @@ -100,7 +100,7 @@ type SubscribeOpts struct { // nodes and connections and filtering message events func (c *Client) SubscribeNetwork(events chan *Event, opts SubscribeOpts) (event.Subscription, error) { url := fmt.Sprintf("%s/events?current=%t&filter=%s", c.URL, opts.Current, opts.Filter) - req, err := http.NewRequest("GET", url, nil) + req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { return nil, err } @@ -213,18 +213,18 @@ func (c *Client) RPCClient(ctx context.Context, nodeID string) (*rpc.Client, err // Get performs a HTTP GET request decoding the resulting JSON response // into "out" func (c *Client) Get(path string, out interface{}) error { - return c.Send("GET", path, nil, out) + return c.Send(http.MethodGet, path, nil, out) } // Post performs a HTTP POST request sending "in" as the JSON body and // decoding the resulting JSON response into "out" func (c *Client) Post(path string, in, out interface{}) error { - return c.Send("POST", path, in, out) + return c.Send(http.MethodPost, path, in, out) } // Delete performs a HTTP DELETE request func (c *Client) Delete(path string) error { - return c.Send("DELETE", path, nil, nil) + return c.Send(http.MethodDelete, path, nil, nil) } // Send performs a HTTP request, sending "in" as the JSON request body and diff --git a/rpc/http.go b/rpc/http.go index ee23c79a1a95..41c168d219bb 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -186,7 +186,7 @@ func (hc *httpConn) doRequest(ctx context.Context, msg interface{}) (io.ReadClos if err != nil { return nil, err } - req, err := http.NewRequestWithContext(ctx, "POST", hc.url, io.NopCloser(bytes.NewReader(body))) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, hc.url, io.NopCloser(bytes.NewReader(body))) if err != nil { return nil, err } From 02a59e8d84508148a0b539ae8879080bb236d2e6 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 4 Dec 2024 10:52:46 +0800 Subject: [PATCH 263/479] all: replace strings.Replace with string.ReplaceAll (#24835) --- accounts/abi/bind/bind.go | 2 +- cmd/faucet/website.go | 10 +++++----- eth/tracers/internal/tracers/assets.go | 10 +++++----- ethstats/ethstats.go | 2 +- internal/jsre/deps/bindata.go | 8 ++++---- p2p/nat/natupnp_test.go | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go index 44de4cc74aed..79d05afb445d 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/bind/bind.go @@ -117,7 +117,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La } contracts[types[i]] = &tmplContract{ Type: capitalise(types[i]), - InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1), + InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""), InputBin: strings.TrimSpace(bytecodes[i]), Constructor: evmABI.Constructor, Calls: calls, diff --git a/cmd/faucet/website.go b/cmd/faucet/website.go index 2b204731167a..0404f38e9a26 100644 --- a/cmd/faucet/website.go +++ b/cmd/faucet/website.go @@ -92,7 +92,7 @@ func faucetHtml() (*asset, error) { // It returns an error if the asset could not be found or // could not be loaded. func Asset(name string) ([]byte, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) + canonicalName := strings.ReplaceAll(name, "\\", "/") if f, ok := _bindata[canonicalName]; ok { a, err := f() if err != nil { @@ -130,7 +130,7 @@ func MustAssetString(name string) string { // It returns an error if the asset could not be found or // could not be loaded. func AssetInfo(name string) (os.FileInfo, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) + canonicalName := strings.ReplaceAll(name, "\\", "/") if f, ok := _bindata[canonicalName]; ok { a, err := f() if err != nil { @@ -144,7 +144,7 @@ func AssetInfo(name string) (os.FileInfo, error) { // AssetDigest returns the digest of the file with the given name. It returns an // error if the asset could not be found or the digest could not be loaded. func AssetDigest(name string) ([sha256.Size]byte, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) + canonicalName := strings.ReplaceAll(name, "\\", "/") if f, ok := _bindata[canonicalName]; ok { a, err := f() if err != nil { @@ -203,7 +203,7 @@ const AssetDebug = false func AssetDir(name string) ([]string, error) { node := _bintree if len(name) != 0 { - canonicalName := strings.Replace(name, "\\", "/", -1) + canonicalName := strings.ReplaceAll(name, "\\", "/") pathList := strings.Split(canonicalName, "/") for _, p := range pathList { node = node.Children[p] @@ -270,6 +270,6 @@ func RestoreAssets(dir, name string) error { } func _filePath(dir, name string) string { - canonicalName := strings.Replace(name, "\\", "/", -1) + canonicalName := strings.ReplaceAll(name, "\\", "/") return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...) } diff --git a/eth/tracers/internal/tracers/assets.go b/eth/tracers/internal/tracers/assets.go index 1f0398e02407..da9c9832439f 100644 --- a/eth/tracers/internal/tracers/assets.go +++ b/eth/tracers/internal/tracers/assets.go @@ -302,7 +302,7 @@ func unigram_tracerJs() (*asset, error) { // It returns an error if the asset could not be found or // could not be loaded. func Asset(name string) ([]byte, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) + canonicalName := strings.ReplaceAll(name, "\\", "/") if f, ok := _bindata[canonicalName]; ok { a, err := f() if err != nil { @@ -340,7 +340,7 @@ func MustAssetString(name string) string { // It returns an error if the asset could not be found or // could not be loaded. func AssetInfo(name string) (os.FileInfo, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) + canonicalName := strings.ReplaceAll(name, "\\", "/") if f, ok := _bindata[canonicalName]; ok { a, err := f() if err != nil { @@ -354,7 +354,7 @@ func AssetInfo(name string) (os.FileInfo, error) { // AssetDigest returns the digest of the file with the given name. It returns an // error if the asset could not be found or the digest could not be loaded. func AssetDigest(name string) ([sha256.Size]byte, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) + canonicalName := strings.ReplaceAll(name, "\\", "/") if f, ok := _bindata[canonicalName]; ok { a, err := f() if err != nil { @@ -421,7 +421,7 @@ const AssetDebug = false func AssetDir(name string) ([]string, error) { node := _bintree if len(name) != 0 { - canonicalName := strings.Replace(name, "\\", "/", -1) + canonicalName := strings.ReplaceAll(name, "\\", "/") pathList := strings.Split(canonicalName, "/") for _, p := range pathList { node = node.Children[p] @@ -498,6 +498,6 @@ func RestoreAssets(dir, name string) error { } func _filePath(dir, name string) string { - canonicalName := strings.Replace(name, "\\", "/", -1) + canonicalName := strings.ReplaceAll(name, "\\", "/") return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...) } diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go index d4872e6362e5..4510478bb0e5 100644 --- a/ethstats/ethstats.go +++ b/ethstats/ethstats.go @@ -367,7 +367,7 @@ func (s *Service) readLoop(conn *connWrapper) { // If the network packet is a system ping, respond to it directly var ping string if err := json.Unmarshal(blob, &ping); err == nil && strings.HasPrefix(ping, "primus::ping::") { - if err := conn.WriteJSON(strings.Replace(ping, "ping", "pong", -1)); err != nil { + if err := conn.WriteJSON(strings.ReplaceAll(ping, "ping", "pong")); err != nil { log.Warn("Failed to respond to system ping message", "err", err) return } diff --git a/internal/jsre/deps/bindata.go b/internal/jsre/deps/bindata.go index bae4115a6c00..89e83b60b5a9 100644 --- a/internal/jsre/deps/bindata.go +++ b/internal/jsre/deps/bindata.go @@ -122,7 +122,7 @@ func web3Js() (*asset, error) { // It returns an error if the asset could not be found or // could not be loaded. func Asset(name string) ([]byte, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) + cannonicalName := strings.ReplaceAll(name, "\\", "/") if f, ok := _bindata[cannonicalName]; ok { a, err := f() if err != nil { @@ -148,7 +148,7 @@ func MustAsset(name string) []byte { // It returns an error if the asset could not be found or // could not be loaded. func AssetInfo(name string) (os.FileInfo, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) + cannonicalName := strings.ReplaceAll(name, "\\", "/") if f, ok := _bindata[cannonicalName]; ok { a, err := f() if err != nil { @@ -192,7 +192,7 @@ var _bindata = map[string]func() (*asset, error){ func AssetDir(name string) ([]string, error) { node := _bintree if len(name) != 0 { - cannonicalName := strings.Replace(name, "\\", "/", -1) + cannonicalName := strings.ReplaceAll(name, "\\", "/") pathList := strings.Split(cannonicalName, "/") for _, p := range pathList { node = node.Children[p] @@ -264,6 +264,6 @@ func RestoreAssets(dir, name string) error { } func _filePath(dir, name string) string { - cannonicalName := strings.Replace(name, "\\", "/", -1) + cannonicalName := strings.ReplaceAll(name, "\\", "/") return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) } diff --git a/p2p/nat/natupnp_test.go b/p2p/nat/natupnp_test.go index 79f6d25ae87f..258bd4fdb937 100644 --- a/p2p/nat/natupnp_test.go +++ b/p2p/nat/natupnp_test.go @@ -218,7 +218,7 @@ func (dev *fakeIGD) ServeHTTP(w http.ResponseWriter, r *http.Request) { } func (dev *fakeIGD) replaceListenAddr(resp string) string { - return strings.Replace(resp, "{{listenAddr}}", dev.listener.Addr().String(), -1) + return strings.ReplaceAll(resp, "{{listenAddr}}", dev.listener.Addr().String()) } func (dev *fakeIGD) listen() (err error) { From 0d0ea46bfac137e7007bc43a015207a78bac29db Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 21 Nov 2024 18:34:32 +0800 Subject: [PATCH 264/479] core: implement EIP-3651, warm coinbase (#25819) --- core/blockchain_test.go | 112 +++++++++++++++++++++++++++++++++++++ core/state/statedb.go | 21 ++++--- core/state_transition.go | 2 +- core/vm/interface.go | 2 +- core/vm/runtime/runtime.go | 6 +- 5 files changed, 130 insertions(+), 13 deletions(-) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index b680f7f185bf..de15d9ca6c5a 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -1609,3 +1609,115 @@ func TestTransientStorageReset(t *testing.T) { t.Fatalf("Unexpected dirty storage slot") } } + +func TestEIP3651(t *testing.T) { + var ( + ConstantinopleBlockReward = big.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Constantinople + + aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa") + bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb") + engine = ethash.NewFaker() + + // A sender who makes transactions, has some funds + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + addr2 = crypto.PubkeyToAddress(key2.PublicKey) + funds = big.NewInt(8000000000000000) + gspec = &Genesis{ + Config: params.TestChainConfig, + Alloc: GenesisAlloc{ + addr1: {Balance: funds}, + addr2: {Balance: funds}, + // The address 0xAAAA sloads 0x00 and 0x01 + aa: { + Code: []byte{ + byte(vm.PC), + byte(vm.PC), + byte(vm.SLOAD), + byte(vm.SLOAD), + }, + Nonce: 0, + Balance: big.NewInt(0), + }, + // The address 0xBBBB calls 0xAAAA + bb: { + Code: []byte{ + byte(vm.PUSH1), 0, // out size + byte(vm.DUP1), // out offset + byte(vm.DUP1), // out insize + byte(vm.DUP1), // in offset + byte(vm.PUSH2), // address + byte(0xaa), + byte(0xaa), + byte(vm.GAS), // gas + byte(vm.DELEGATECALL), + }, + Nonce: 0, + Balance: big.NewInt(0), + }, + }, + } + ) + + gspec.Config.Eip1559Block = common.Big0 + signer := types.LatestSigner(gspec.Config) + + _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) { + b.SetCoinbase(aa) + // One transaction to Coinbase + txdata := &types.DynamicFeeTx{ + ChainID: gspec.Config.ChainId, + Nonce: 0, + To: &bb, + Gas: 500000, + GasFeeCap: big.NewInt(12500000000), + GasTipCap: big.NewInt(0), + AccessList: nil, + Data: []byte{}, + } + tx := types.NewTx(txdata) + tx, _ = types.SignTx(tx, signer, key1) + + b.AddTx(tx) + }) + + diskdb := rawdb.NewMemoryDatabase() + gspec.MustCommit(diskdb) + + chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}) + if err != nil { + t.Fatalf("failed to create tester chain: %v", err) + } + if n, err := chain.InsertChain(blocks); err != nil { + t.Fatalf("block %d: failed to insert into chain: %v", n, err) + } + + block := chain.GetBlockByNumber(1) + + // 1+2: Ensure EIP-1559 access lists are accounted for via gas usage. + innerGas := vm.GasQuickStep*2 + params.ColdSloadCostEIP2929*2 + expectedGas := params.TxGas + 5*vm.GasFastestStep + vm.GasQuickStep + 100 + innerGas // 100 because 0xaaaa is in access list + if block.GasUsed() != expectedGas { + t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expectedGas, block.GasUsed()) + } + + state, _ := chain.State() + + // 3: Ensure that miner received only the tx's tip. + actual := state.GetBalance(block.Coinbase()) + expected := new(big.Int).Add( + new(big.Int).SetUint64(block.GasUsed()*block.Transactions()[0].GasTipCap().Uint64()), + ConstantinopleBlockReward, + ) + if actual.Cmp(expected) != 0 { + t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual) + } + + // 4: Ensure the tx sender paid for the gasUsed * (tip + block baseFee). + actual = new(big.Int).Sub(funds, state.GetBalance(addr1)) + expected = new(big.Int).SetUint64(block.GasUsed() * (block.Transactions()[0].GasTipCap().Uint64() + block.BaseFee().Uint64())) + if actual.Cmp(expected) != 0 { + t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual) + } +} diff --git a/core/state/statedb.go b/core/state/statedb.go index 1cee95087fb8..7782643f8a0e 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -765,26 +765,31 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) // - Add the contents of the optional tx access list (2930) // // Potential EIPs: -// - Reset transient storage(1153) -func (s *StateDB) Prepare(rules params.Rules, sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) { +// - Reset access list (Berlin) +// - Add coinbase to access list (EIP-3651) +// - Reset transient storage (EIP-1153) +func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) { if rules.IsEIP1559 { // Clear out any leftover from previous executions - s.accessList = newAccessList() + al := newAccessList() + s.accessList = al - s.AddAddressToAccessList(sender) + al.AddAddress(sender) if dst != nil { - s.AddAddressToAccessList(*dst) + al.AddAddress(*dst) // If it's a create-tx, the destination will be added inside evm.create } for _, addr := range precompiles { - s.AddAddressToAccessList(addr) + al.AddAddress(addr) } for _, el := range list { - s.AddAddressToAccessList(el.Address) + al.AddAddress(el.Address) for _, key := range el.StorageKeys { - s.AddSlotToAccessList(el.Address, key) + al.AddSlot(el.Address, key) } } + // EIP-3651: warm coinbase + al.AddAddress(coinbase) } // Reset transient storage at the beginning of transaction execution s.transientStorage = newTransientStorage() diff --git a/core/state_transition.go b/core/state_transition.go index 90401eb817a5..05db0e7d9501 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -332,7 +332,7 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) // - reset transient storage(eip 1153) - st.state.Prepare(rules, msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList()) + st.state.Prepare(rules, msg.From(), st.evm.Context.Coinbase, msg.To(), vm.ActivePrecompiles(rules), msg.AccessList()) var ( evm = st.evm diff --git a/core/vm/interface.go b/core/vm/interface.go index cd0333e57af2..c6c3ee1cfd98 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -69,7 +69,7 @@ type StateDB interface { // AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform // even if the feature/fork is not active yet AddSlotToAccessList(addr common.Address, slot common.Hash) - Prepare(rules params.Rules, sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) + Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) RevertToSnapshot(int) Snapshot() int diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 600ae1199a4c..5e7d7b8f5545 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -125,7 +125,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) // - reset transient storage(eip 1153) - cfg.State.Prepare(rules, cfg.Origin, &address, vm.ActivePrecompiles(rules), nil) + cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil) cfg.State.CreateAccount(address) // set the receiver's (the executing contract) code for execution. @@ -160,7 +160,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) // - reset transient storage(eip 1153) - cfg.State.Prepare(rules, cfg.Origin, nil, vm.ActivePrecompiles(rules), nil) + cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, nil, vm.ActivePrecompiles(rules), nil) // Call the code with the given configuration. code, address, leftOverGas, err := vmenv.Create( @@ -189,7 +189,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) // - reset transient storage(eip 1153) - statedb.Prepare(rules, cfg.Origin, &address, vm.ActivePrecompiles(rules), nil) + statedb.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil) // Call the code with the given configuration. ret, leftOverGas, err := vmenv.Call( From 733c298b0ee573216900ae4750b6cae577b896c4 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 21 Nov 2024 18:35:01 +0800 Subject: [PATCH 265/479] core/vm: improve EVM reusability (#26341) --- core/vm/evm.go | 8 ++- core/vm/instructions.go | 6 +- core/vm/instructions_test.go | 14 ++--- core/vm/interpreter.go | 114 ++++++++++++++++------------------- 4 files changed, 70 insertions(+), 72 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index 609ad525c050..1eff3428dbe9 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -142,7 +142,7 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, tradingStat chainRules: chainConfig.Rules(blockCtx.BlockNumber), } - evm.interpreter = NewEVMInterpreter(evm, config) + evm.interpreter = NewEVMInterpreter(evm) return evm } @@ -169,6 +169,12 @@ func (evm *EVM) Interpreter() *EVMInterpreter { return evm.interpreter } +// SetBlockContext updates the block context of the EVM. +func (evm *EVM) SetBlockContext(blockCtx BlockContext) { + evm.Context = blockCtx + evm.chainRules = evm.chainConfig.Rules(blockCtx.BlockNumber) +} + // Call executes the contract associated with the addr with the given input as // parameters. It also handles any necessary value transfer required and takes // the necessary steps to create accounts and reverses the state in case of an diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 8914bf94badb..b51bb870d79d 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -838,9 +838,9 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance) interpreter.evm.StateDB.Suicide(scope.Contract.Address()) - if interpreter.cfg.Debug { - interpreter.cfg.Tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) - interpreter.cfg.Tracer.CaptureExit([]byte{}, 0, nil) + if interpreter.evm.Config.Debug { + interpreter.evm.Config.Tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) + interpreter.evm.Config.Tracer.CaptureExit([]byte{}, 0, nil) } return nil, errStopToken } diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index c98a4488d5b2..1ccc7ff50519 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -204,7 +204,7 @@ func TestAddMod(t *testing.T) { var ( env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env) pc = uint64(0) ) tests := []struct { @@ -300,7 +300,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) { env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() scope = &ScopeContext{nil, stack, nil} - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env) ) env.interpreter = evmInterpreter @@ -541,7 +541,7 @@ func TestOpMstore(t *testing.T) { env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env) ) env.interpreter = evmInterpreter @@ -567,7 +567,7 @@ func BenchmarkOpMstore(bench *testing.B) { env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env) ) env.interpreter = evmInterpreter @@ -590,7 +590,7 @@ func TestOpTstore(t *testing.T) { env = NewEVM(BlockContext{}, TxContext{}, statedb, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env) caller = common.Address{} to = common.Address{1} contractRef = contractRef{caller} @@ -632,7 +632,7 @@ func BenchmarkOpKeccak256(bench *testing.B) { env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env) ) env.interpreter = evmInterpreter mem.Resize(32) @@ -736,7 +736,7 @@ func TestRandom(t *testing.T) { env = NewEVM(BlockContext{Random: &tt.random}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env) ) opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) if len(stack.data) != 1 { diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index a7aa8dde094c..b64b679b915e 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -29,10 +29,7 @@ type Config struct { Tracer EVMLogger // Opcode logger NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls) EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages - - JumpTable *JumpTable // EVM instruction table, automatically populated if unset - - ExtraEips []int // Additional EIPS that are to be enabled + ExtraEips []int // Additional EIPS that are to be enabled } // ScopeContext contains the things that are per-call, such as stack and memory, @@ -45,8 +42,8 @@ type ScopeContext struct { // EVMInterpreter represents an EVM interpreter type EVMInterpreter struct { - evm *EVM - cfg Config + evm *EVM + table *JumpTable hasher crypto.KeccakState // Keccak256 hasher instance shared across opcodes hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes @@ -56,55 +53,50 @@ type EVMInterpreter struct { } // NewEVMInterpreter returns a new instance of the Interpreter. -func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { +func NewEVMInterpreter(evm *EVM) *EVMInterpreter { // If jump table was not initialised we set the default one. - if cfg.JumpTable == nil { - switch { - case evm.chainRules.IsEIP1559: - cfg.JumpTable = &eip1559InstructionSet - case evm.chainRules.IsShanghai: - cfg.JumpTable = &shanghaiInstructionSet - case evm.chainRules.IsMerge: - cfg.JumpTable = &mergeInstructionSet - case evm.chainRules.IsLondon: - cfg.JumpTable = &londonInstructionSet - case evm.chainRules.IsBerlin: - cfg.JumpTable = &berlinInstructionSet - case evm.chainRules.IsIstanbul: - cfg.JumpTable = &istanbulInstructionSet - case evm.chainRules.IsConstantinople: - cfg.JumpTable = &constantinopleInstructionSet - case evm.chainRules.IsByzantium: - cfg.JumpTable = &byzantiumInstructionSet - case evm.chainRules.IsEIP158: - cfg.JumpTable = &spuriousDragonInstructionSet - case evm.chainRules.IsEIP150: - cfg.JumpTable = &tangerineWhistleInstructionSet - case evm.chainRules.IsHomestead: - cfg.JumpTable = &homesteadInstructionSet - default: - cfg.JumpTable = &frontierInstructionSet - } - var extraEips []int - if len(cfg.ExtraEips) > 0 { - // Deep-copy jumptable to prevent modification of opcodes in other tables - cfg.JumpTable = copyJumpTable(cfg.JumpTable) - } - for _, eip := range cfg.ExtraEips { - if err := EnableEIP(eip, cfg.JumpTable); err != nil { - // Disable it, so caller can check if it's activated or not - log.Error("EIP activation failed", "eip", eip, "error", err) - } else { - extraEips = append(extraEips, eip) - } - } - cfg.ExtraEips = extraEips + var table *JumpTable + switch { + case evm.chainRules.IsEIP1559: + table = &eip1559InstructionSet + case evm.chainRules.IsShanghai: + table = &shanghaiInstructionSet + case evm.chainRules.IsMerge: + table = &mergeInstructionSet + case evm.chainRules.IsLondon: + table = &londonInstructionSet + case evm.chainRules.IsBerlin: + table = &berlinInstructionSet + case evm.chainRules.IsIstanbul: + table = &istanbulInstructionSet + case evm.chainRules.IsConstantinople: + table = &constantinopleInstructionSet + case evm.chainRules.IsByzantium: + table = &byzantiumInstructionSet + case evm.chainRules.IsEIP158: + table = &spuriousDragonInstructionSet + case evm.chainRules.IsEIP150: + table = &tangerineWhistleInstructionSet + case evm.chainRules.IsHomestead: + table = &homesteadInstructionSet + default: + table = &frontierInstructionSet } - - return &EVMInterpreter{ - evm: evm, - cfg: cfg, + var extraEips []int + if len(evm.Config.ExtraEips) > 0 { + // Deep-copy jumptable to prevent modification of opcodes in other tables + table = copyJumpTable(table) + } + for _, eip := range evm.Config.ExtraEips { + if err := EnableEIP(eip, table); err != nil { + // Disable it, so caller can check if it's activated or not + log.Error("EIP activation failed", "eip", eip, "error", err) + } else { + extraEips = append(extraEips, eip) + } } + evm.Config.ExtraEips = extraEips + return &EVMInterpreter{evm: evm, table: table} } // Run loops and evaluates the contract's code with the given input data and returns @@ -162,13 +154,13 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( }() contract.Input = input - if in.cfg.Debug { + if in.evm.Config.Debug { defer func() { if err != nil { if !logged { - in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) + in.evm.Config.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) } else { - in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err) + in.evm.Config.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err) } } }() @@ -178,7 +170,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // the execution of one of the operations or until the done flag is set by the // parent context. for { - if in.cfg.Debug { + if in.evm.Config.Debug { // Capture pre-execution values for tracing. logged, pcCopy, gasCopy = false, pc, contract.Gas } @@ -186,7 +178,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // Get the operation from the jump table and validate the stack to ensure there are // enough stack items available to perform the operation. op = contract.GetOp(pc) - operation := in.cfg.JumpTable[op] + operation := in.table[op] cost = operation.constantGas // For tracing // Validate stack if sLen := stack.len(); sLen < operation.minStack { @@ -224,15 +216,15 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( return nil, ErrOutOfGas } // Do tracing before memory expansion - if in.cfg.Debug { - in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) + if in.evm.Config.Debug { + in.evm.Config.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) logged = true } if memorySize > 0 { mem.Resize(memorySize) } - } else if in.cfg.Debug { - in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) + } else if in.evm.Config.Debug { + in.evm.Config.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) logged = true } // execute the operation From d3fc08db1dc50a6852b1bd452d14a244356e5300 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:30 +0800 Subject: [PATCH 266/479] update golang.org/x/crypto to v0.29.0 --- go.mod | 15 +++++++-------- go.sum | 32 ++++++++++++++++---------------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index 11576fdce6b5..5f25a2232fd2 100644 --- a/go.mod +++ b/go.mod @@ -34,10 +34,10 @@ require ( github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 github.com/stretchr/testify v1.8.4 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 - golang.org/x/crypto v0.15.0 - golang.org/x/sync v0.4.0 - golang.org/x/sys v0.24.0 - golang.org/x/tools v0.14.0 + golang.org/x/crypto v0.29.0 + golang.org/x/sync v0.9.0 + golang.org/x/sys v0.27.0 + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772 @@ -60,7 +60,6 @@ require ( github.com/dlclark/regexp2 v1.10.0 // indirect github.com/go-ole/go-ole v1.2.5 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect - github.com/google/go-cmp v0.6.0 // indirect github.com/google/uuid v1.3.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect @@ -72,9 +71,9 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/term v0.14.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/term v0.26.0 // indirect + golang.org/x/text v0.20.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools v2.2.0+incompatible // indirect diff --git a/go.sum b/go.sum index 782d2654a694..32e70b783a58 100644 --- a/go.sum +++ b/go.sum @@ -154,20 +154,20 @@ github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGC github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= +golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -184,18 +184,18 @@ golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= -golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= +golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 92fc843683d41c6b493c38c4d4927bbdf725f642 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:30 +0800 Subject: [PATCH 267/479] crypto: switch over to upstream sha3 package (#18390) --- XDCx/tradingstate/trade.go | 4 +- XDCxlending/lendingstate/lendingitem.go | 4 +- XDCxlending/lendingstate/lendingitem_test.go | 6 +- XDCxlending/lendingstate/trade.go | 6 +- bmt/bmt_test.go | 20 +- common/types.go | 4 +- consensus/XDPoS/engines/engine_v1/utils.go | 4 +- consensus/XDPoS/engines/engine_v2/utils.go | 4 +- consensus/XDPoS/utils/utils.go | 4 +- consensus/clique/clique.go | 4 +- consensus/ethash/algorithm.go | 10 +- core/database_util_test.go | 8 +- core/txpool/lending_pool_test.go | 6 +- core/types/lending_signing.go | 14 +- core/types/order_signing.go | 12 +- crypto/sha3/LICENSE | 27 -- crypto/sha3/PATENTS | 22 - crypto/sha3/doc.go | 66 --- crypto/sha3/hashes.go | 71 ---- crypto/sha3/keccakf.go | 412 ------------------- crypto/sha3/keccakf_amd64.go | 13 - crypto/sha3/keccakf_amd64.s | 390 ------------------ crypto/sha3/register.go | 18 - crypto/sha3/sha3.go | 192 --------- crypto/sha3/sha3_test.go | 297 ------------- crypto/sha3/shake.go | 60 --- crypto/sha3/testdata/keccakKats.json.deflate | Bin 521342 -> 0 bytes crypto/sha3/xor.go | 16 - crypto/sha3/xor_generic.go | 28 -- crypto/sha3/xor_unaligned.go | 58 --- eth/downloader/statesync.go | 4 +- p2p/discv5/net.go | 4 +- p2p/enr/enr.go | 6 +- p2p/rlpx.go | 6 +- p2p/rlpx_test.go | 10 +- p2p/server_test.go | 6 +- tests/state_test_util.go | 4 +- 37 files changed, 74 insertions(+), 1746 deletions(-) delete mode 100644 crypto/sha3/LICENSE delete mode 100644 crypto/sha3/PATENTS delete mode 100644 crypto/sha3/doc.go delete mode 100644 crypto/sha3/hashes.go delete mode 100644 crypto/sha3/keccakf.go delete mode 100644 crypto/sha3/keccakf_amd64.go delete mode 100644 crypto/sha3/keccakf_amd64.s delete mode 100644 crypto/sha3/register.go delete mode 100644 crypto/sha3/sha3.go delete mode 100644 crypto/sha3/sha3_test.go delete mode 100644 crypto/sha3/shake.go delete mode 100644 crypto/sha3/testdata/keccakKats.json.deflate delete mode 100644 crypto/sha3/xor.go delete mode 100644 crypto/sha3/xor_generic.go delete mode 100644 crypto/sha3/xor_unaligned.go diff --git a/XDCx/tradingstate/trade.go b/XDCx/tradingstate/trade.go index 22f6e30ea18b..f8a4e5cd27ed 100644 --- a/XDCx/tradingstate/trade.go +++ b/XDCx/tradingstate/trade.go @@ -5,8 +5,8 @@ import ( "time" "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" "github.com/globalsign/mgo/bson" + "golang.org/x/crypto/sha3" ) const ( @@ -136,7 +136,7 @@ func (t *Trade) SetBSON(raw bson.Raw) error { // The OrderHash, Amount, Taker and TradeNonce attributes must be // set before attempting to compute the trade orderBookHash func (t *Trade) ComputeHash() common.Hash { - sha := sha3.NewKeccak256() + sha := sha3.NewLegacyKeccak256() sha.Write(t.MakerOrderHash.Bytes()) sha.Write(t.TakerOrderHash.Bytes()) return common.BytesToHash(sha.Sum(nil)) diff --git a/XDCxlending/lendingstate/lendingitem.go b/XDCxlending/lendingstate/lendingitem.go index 3ce001f83216..4d8e074b48fb 100644 --- a/XDCxlending/lendingstate/lendingitem.go +++ b/XDCxlending/lendingstate/lendingitem.go @@ -10,8 +10,8 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" "github.com/globalsign/mgo/bson" + "golang.org/x/crypto/sha3" ) const ( @@ -308,7 +308,7 @@ func (l *LendingItem) VerifyLendingStatus() error { } func (l *LendingItem) ComputeHash() common.Hash { - sha := sha3.NewKeccak256() + sha := sha3.NewLegacyKeccak256() if l.Status == LendingStatusNew { sha.Write(l.Relayer.Bytes()) sha.Write(l.UserAddress.Bytes()) diff --git a/XDCxlending/lendingstate/lendingitem_test.go b/XDCxlending/lendingstate/lendingitem_test.go index 2fcd615012c3..24ad0a48b439 100644 --- a/XDCxlending/lendingstate/lendingitem_test.go +++ b/XDCxlending/lendingstate/lendingitem_test.go @@ -12,8 +12,8 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/rpc" + "golang.org/x/crypto/sha3" ) func TestLendingItem_VerifyLendingSide(t *testing.T) { @@ -568,9 +568,8 @@ func sendOrder(nonce uint64) { } func computeHash(l *LendingOrderMsg) common.Hash { - sha := sha3.NewKeccak256() + sha := sha3.NewLegacyKeccak256() if l.Status == LendingStatusCancelled { - sha := sha3.NewKeccak256() sha.Write(l.Hash.Bytes()) sha.Write(common.BigToHash(big.NewInt(int64(l.AccountNonce))).Bytes()) sha.Write(l.UserAddress.Bytes()) @@ -593,5 +592,4 @@ func computeHash(l *LendingOrderMsg) common.Hash { sha.Write(common.BigToHash(big.NewInt(int64(l.AccountNonce))).Bytes()) } return common.BytesToHash(sha.Sum(nil)) - } diff --git a/XDCxlending/lendingstate/trade.go b/XDCxlending/lendingstate/trade.go index 0416ca4c2661..89542381fe0b 100644 --- a/XDCxlending/lendingstate/trade.go +++ b/XDCxlending/lendingstate/trade.go @@ -2,14 +2,14 @@ package lendingstate import ( "fmt" - "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" "math/big" "strconv" "time" + "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate" "github.com/XinFinOrg/XDPoSChain/common" "github.com/globalsign/mgo/bson" + "golang.org/x/crypto/sha3" ) const ( @@ -183,7 +183,7 @@ func (t *LendingTrade) SetBSON(raw bson.Raw) error { } func (t *LendingTrade) ComputeHash() common.Hash { - sha := sha3.NewKeccak256() + sha := sha3.NewLegacyKeccak256() sha.Write(t.InvestingOrderHash.Bytes()) sha.Write(t.BorrowingOrderHash.Bytes()) return common.BytesToHash(sha.Sum(nil)) diff --git a/bmt/bmt_test.go b/bmt/bmt_test.go index ac762993c5e1..3030cea6402b 100644 --- a/bmt/bmt_test.go +++ b/bmt/bmt_test.go @@ -29,7 +29,7 @@ import ( "testing" "time" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" + "golang.org/x/crypto/sha3" ) const ( @@ -39,7 +39,7 @@ const ( // TestRefHasher tests that the RefHasher computes the expected BMT hash for // all data lengths between 0 and 256 bytes func TestRefHasher(t *testing.T) { - hashFunc := sha3.NewKeccak256 + hashFunc := sha3.NewLegacyKeccak256 sha3 := func(data ...[]byte) []byte { h := hashFunc() @@ -212,7 +212,7 @@ func testHasher(f func(BaseHasher, []byte, int, int) error) error { tdata := testDataReader(4128) data := make([]byte, 4128) tdata.Read(data) - hasher := sha3.NewKeccak256 + hasher := sha3.NewLegacyKeccak256 size := hasher().Size() counts := []int{1, 2, 3, 4, 5, 8, 16, 32, 64, 128} @@ -239,7 +239,7 @@ func TestHasherReuseWithRelease(t *testing.T) { } func testHasherReuse(i int, t *testing.T) { - hasher := sha3.NewKeccak256 + hasher := sha3.NewLegacyKeccak256 pool := NewTreePool(hasher, 128, i) defer pool.Drain(0) bmt := New(pool) @@ -258,7 +258,7 @@ func testHasherReuse(i int, t *testing.T) { } func TestHasherConcurrency(t *testing.T) { - hasher := sha3.NewKeccak256 + hasher := sha3.NewLegacyKeccak256 pool := NewTreePool(hasher, 128, maxproccnt) defer pool.Drain(0) wg := sync.WaitGroup{} @@ -379,7 +379,7 @@ func benchmarkBMTBaseline(n int, t *testing.B) { tdata := testDataReader(64) data := make([]byte, 64) tdata.Read(data) - hasher := sha3.NewKeccak256 + hasher := sha3.NewLegacyKeccak256 t.ReportAllocs() t.ResetTimer() @@ -409,7 +409,7 @@ func benchmarkHasher(n int, t *testing.B) { tdata.Read(data) size := 1 - hasher := sha3.NewKeccak256 + hasher := sha3.NewLegacyKeccak256 segmentCount := 128 pool := NewTreePool(hasher, segmentCount, size) bmt := New(pool) @@ -428,7 +428,7 @@ func benchmarkHasherReuse(poolsize, n int, t *testing.B) { data := make([]byte, n) tdata.Read(data) - hasher := sha3.NewKeccak256 + hasher := sha3.NewLegacyKeccak256 segmentCount := 128 pool := NewTreePool(hasher, segmentCount, poolsize) cycles := 200 @@ -455,7 +455,7 @@ func benchmarkSHA3(n int, t *testing.B) { data := make([]byte, n) tdata := testDataReader(n) tdata.Read(data) - hasher := sha3.NewKeccak256 + hasher := sha3.NewLegacyKeccak256 h := hasher() t.ReportAllocs() @@ -471,7 +471,7 @@ func benchmarkRefHasher(n int, t *testing.B) { data := make([]byte, n) tdata := testDataReader(n) tdata.Read(data) - hasher := sha3.NewKeccak256 + hasher := sha3.NewLegacyKeccak256 rbmt := NewRefHasher(hasher, 128) t.ReportAllocs() diff --git a/common/types.go b/common/types.go index ffd3d42d8475..533aa562d915 100644 --- a/common/types.go +++ b/common/types.go @@ -25,7 +25,7 @@ import ( "reflect" "github.com/XinFinOrg/XDPoSChain/common/hexutil" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" + "golang.org/x/crypto/sha3" ) const ( @@ -240,7 +240,7 @@ func (a Address) Hash() Hash { return BytesToHash(a[:]) } // Hex returns an EIP55-compliant hex string representation of the address. func (a Address) Hex() string { unchecksummed := hex.EncodeToString(a[:]) - sha := sha3.NewKeccak256() + sha := sha3.NewLegacyKeccak256() sha.Write([]byte(unchecksummed)) hash := sha.Sum(nil) diff --git a/consensus/XDPoS/engines/engine_v1/utils.go b/consensus/XDPoS/engines/engine_v1/utils.go index 13334fb7e16c..b07a5e0f69b6 100644 --- a/consensus/XDPoS/engines/engine_v1/utils.go +++ b/consensus/XDPoS/engines/engine_v1/utils.go @@ -7,10 +7,10 @@ import ( "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rlp" lru "github.com/hashicorp/golang-lru" + "golang.org/x/crypto/sha3" ) // Get masternodes address from checkpoint Header. @@ -59,7 +59,7 @@ func getM1M2(masternodes []common.Address, validators []int64, currentHeader *ty } func sigHash(header *types.Header) (hash common.Hash) { - hasher := sha3.NewKeccak256() + hasher := sha3.NewLegacyKeccak256() enc := []interface{}{ header.ParentHash, diff --git a/consensus/XDPoS/engines/engine_v2/utils.go b/consensus/XDPoS/engines/engine_v2/utils.go index 9c99b636a9d4..b95d12c5f222 100644 --- a/consensus/XDPoS/engines/engine_v2/utils.go +++ b/consensus/XDPoS/engines/engine_v2/utils.go @@ -11,14 +11,14 @@ import ( "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/rlp" lru "github.com/hashicorp/golang-lru" + "golang.org/x/crypto/sha3" ) func sigHash(header *types.Header) (hash common.Hash) { - hasher := sha3.NewKeccak256() + hasher := sha3.NewLegacyKeccak256() enc := []interface{}{ header.ParentHash, diff --git a/consensus/XDPoS/utils/utils.go b/consensus/XDPoS/utils/utils.go index 62b394c836d3..28ab0f2f64eb 100644 --- a/consensus/XDPoS/utils/utils.go +++ b/consensus/XDPoS/utils/utils.go @@ -9,9 +9,9 @@ import ( "strconv" "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/rlp" + "golang.org/x/crypto/sha3" ) func Position(list []common.Address, x common.Address) int { @@ -91,7 +91,7 @@ func DecodeBytesExtraFields(b []byte, val interface{}) error { } func rlpHash(x interface{}) (h common.Hash) { - hw := sha3.NewKeccak256() + hw := sha3.NewLegacyKeccak256() err := rlp.Encode(hw, x) if err != nil { log.Error("[rlpHash] Fail to hash item", "Error", err) diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index fd3f56a958dd..899649eb84f3 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -34,13 +34,13 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rlp" "github.com/XinFinOrg/XDPoSChain/rpc" lru "github.com/hashicorp/golang-lru" + "golang.org/x/crypto/sha3" ) const ( @@ -145,7 +145,7 @@ type SignerFn func(accounts.Account, []byte) ([]byte, error) // panics. This is done to avoid accidentally using both forms (signature present // or not), which could be abused to produce different hashes for the same header. func sigHash(header *types.Header) (hash common.Hash) { - hasher := sha3.NewKeccak256() + hasher := sha3.NewLegacyKeccak256() enc := []interface{}{ header.ParentHash, diff --git a/consensus/ethash/algorithm.go b/consensus/ethash/algorithm.go index f8dca9c4083b..0b47abb9bb33 100644 --- a/consensus/ethash/algorithm.go +++ b/consensus/ethash/algorithm.go @@ -30,8 +30,8 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/bitutil" "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/log" + "golang.org/x/crypto/sha3" ) const ( @@ -112,7 +112,7 @@ func seedHash(block uint64) []byte { if block < epochLength { return seed } - keccak256 := makeHasher(sha3.NewKeccak256()) + keccak256 := makeHasher(sha3.NewLegacyKeccak256()) for i := 0; i < int(block/epochLength); i++ { keccak256(seed, seed) } @@ -170,7 +170,7 @@ func generateCache(dest []uint32, epoch uint64, seed []byte) { } }() // Create a hasher to reuse between invocations - keccak512 := makeHasher(sha3.NewKeccak512()) + keccak512 := makeHasher(sha3.NewLegacyKeccak512()) // Sequentially produce the initial dataset keccak512(cache, seed) @@ -303,7 +303,7 @@ func generateDataset(dest []uint32, epoch uint64, cache []uint32) { defer pend.Done() // Create a hasher to reuse between invocations - keccak512 := makeHasher(sha3.NewKeccak512()) + keccak512 := makeHasher(sha3.NewLegacyKeccak512()) // Calculate the data segment this thread should generate batch := uint32((size + hashBytes*uint64(threads) - 1) / (hashBytes * uint64(threads))) @@ -377,7 +377,7 @@ func hashimoto(hash []byte, nonce uint64, size uint64, lookup func(index uint32) // in-memory cache) in order to produce our final value for a particular header // hash and nonce. func hashimotoLight(size uint64, cache []uint32, hash []byte, nonce uint64) ([]byte, []byte) { - keccak512 := makeHasher(sha3.NewKeccak512()) + keccak512 := makeHasher(sha3.NewLegacyKeccak512()) lookup := func(index uint32) []uint32 { rawData := generateDatasetItem(cache, index, keccak512) diff --git a/core/database_util_test.go b/core/database_util_test.go index a0d5a9ec8371..1f29908b963f 100644 --- a/core/database_util_test.go +++ b/core/database_util_test.go @@ -24,7 +24,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/types" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" + "golang.org/x/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/rlp" ) @@ -47,7 +47,7 @@ func TestHeaderStorage(t *testing.T) { if entry := GetHeaderRLP(db, header.Hash(), header.Number.Uint64()); entry == nil { t.Fatalf("Stored header RLP not found") } else { - hasher := sha3.NewKeccak256() + hasher := sha3.NewLegacyKeccak256() hasher.Write(entry) if hash := common.BytesToHash(hasher.Sum(nil)); hash != header.Hash() { @@ -68,7 +68,7 @@ func TestBodyStorage(t *testing.T) { // Create a test body to move around the database and make sure it's really new body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}} - hasher := sha3.NewKeccak256() + hasher := sha3.NewLegacyKeccak256() rlp.Encode(hasher, body) hash := common.BytesToHash(hasher.Sum(nil)) @@ -85,7 +85,7 @@ func TestBodyStorage(t *testing.T) { if entry := GetBodyRLP(db, hash, 0); entry == nil { t.Fatalf("Stored body RLP not found") } else { - hasher := sha3.NewKeccak256() + hasher := sha3.NewLegacyKeccak256() hasher.Write(entry) if calc := common.BytesToHash(hasher.Sum(nil)); calc != hash { diff --git a/core/txpool/lending_pool_test.go b/core/txpool/lending_pool_test.go index 0df24cf5a4aa..ceb6970b7ade 100644 --- a/core/txpool/lending_pool_test.go +++ b/core/txpool/lending_pool_test.go @@ -14,9 +14,9 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/ethclient" "github.com/XinFinOrg/XDPoSChain/rpc" + "golang.org/x/crypto/sha3" ) type LendingMsg struct { @@ -63,7 +63,7 @@ func getLendingNonce(userAddress common.Address) (uint64, error) { func (l *LendingMsg) computeHash() common.Hash { borrowing := l.Side == lendingstate.Borrowing - sha := sha3.NewKeccak256() + sha := sha3.NewLegacyKeccak256() if l.Type == lendingstate.Repay { sha.Write(common.BigToHash(big.NewInt(int64(l.AccountNonce))).Bytes()) sha.Write([]byte(l.Status)) @@ -83,7 +83,7 @@ func (l *LendingMsg) computeHash() common.Hash { sha.Write(common.BigToHash(l.Quantity).Bytes()) } else { if l.Status == lendingstate.LendingStatusCancelled { - sha := sha3.NewKeccak256() + sha := sha3.NewLegacyKeccak256() sha.Write(l.Hash.Bytes()) sha.Write(common.BigToHash(big.NewInt(int64(l.AccountNonce))).Bytes()) sha.Write(l.UserAddress.Bytes()) diff --git a/core/types/lending_signing.go b/core/types/lending_signing.go index 6aaaedb481ea..8fc37815d33f 100644 --- a/core/types/lending_signing.go +++ b/core/types/lending_signing.go @@ -23,8 +23,8 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/log" + "golang.org/x/crypto/sha3" ) // LendingSigner interface for lending signer transaction @@ -86,7 +86,7 @@ func LendingSignTx(tx *LendingTransaction, s LendingSigner, prv *ecdsa.PrivateKe return tx.WithSignature(s, sig) } -//LendingTxSigner signer +// LendingTxSigner signer type LendingTxSigner struct{} // Equal compare two signer @@ -95,7 +95,7 @@ func (lendingsign LendingTxSigner) Equal(s2 LendingSigner) bool { return ok } -//SignatureValues returns signature values. This signature needs to be in the [R || S || V] format where V is 0 or 1. +// SignatureValues returns signature values. This signature needs to be in the [R || S || V] format where V is 0 or 1. func (lendingsign LendingTxSigner) SignatureValues(tx *LendingTransaction, sig []byte) (r, s, v *big.Int, err error) { if len(sig) != 65 { panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig))) @@ -112,7 +112,7 @@ func (lendingsign LendingTxSigner) LendingCreateHash(tx *LendingTransaction) com "collateral", tx.CollateralToken().Hex(), "lending", tx.LendingToken().Hex(), "quantity", tx.Quantity(), "term", tx.Term(), "interest", tx.Interest(), "side", tx.Side, "status", tx.Status(), "type", tx.Type(), "nonce", tx.Nonce()) borrowing := tx.Side() == LendingSideBorrow - sha := sha3.NewKeccak256() + sha := sha3.NewLegacyKeccak256() sha.Write(tx.RelayerAddress().Bytes()) sha.Write(tx.UserAddress().Bytes()) if borrowing { @@ -140,7 +140,7 @@ func (lendingsign LendingTxSigner) LendingCreateHash(tx *LendingTransaction) com // LendingCancelHash hash of cancelled lending transaction func (lendingsign LendingTxSigner) LendingCancelHash(tx *LendingTransaction) common.Hash { - sha := sha3.NewKeccak256() + sha := sha3.NewLegacyKeccak256() sha.Write(common.BigToHash(big.NewInt(int64(tx.Nonce()))).Bytes()) sha.Write([]byte(tx.Status())) sha.Write(tx.RelayerAddress().Bytes()) @@ -153,7 +153,7 @@ func (lendingsign LendingTxSigner) LendingCancelHash(tx *LendingTransaction) com // LendingRepayHash hash of cancelled lending transaction func (lendingsign LendingTxSigner) LendingRepayHash(tx *LendingTransaction) common.Hash { - sha := sha3.NewKeccak256() + sha := sha3.NewLegacyKeccak256() sha.Write(common.BigToHash(big.NewInt(int64(tx.Nonce()))).Bytes()) sha.Write([]byte(tx.Status())) sha.Write(tx.RelayerAddress().Bytes()) @@ -167,7 +167,7 @@ func (lendingsign LendingTxSigner) LendingRepayHash(tx *LendingTransaction) comm // LendingTopUpHash hash of cancelled lending transaction func (lendingsign LendingTxSigner) LendingTopUpHash(tx *LendingTransaction) common.Hash { - sha := sha3.NewKeccak256() + sha := sha3.NewLegacyKeccak256() sha.Write(common.BigToHash(big.NewInt(int64(tx.Nonce()))).Bytes()) sha.Write([]byte(tx.Status())) sha.Write(tx.RelayerAddress().Bytes()) diff --git a/core/types/order_signing.go b/core/types/order_signing.go index 50f5591f3b8c..6a7f40025536 100644 --- a/core/types/order_signing.go +++ b/core/types/order_signing.go @@ -24,7 +24,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" + "golang.org/x/crypto/sha3" ) // OrderSigner interface for order transaction @@ -86,7 +86,7 @@ func OrderSignTx(tx *OrderTransaction, s OrderSigner, prv *ecdsa.PrivateKey) (*O return tx.WithSignature(s, sig) } -//OrderTxSigner signer +// OrderTxSigner signer type OrderTxSigner struct{} // Equal compare two signer @@ -95,7 +95,7 @@ func (ordersign OrderTxSigner) Equal(s2 OrderSigner) bool { return ok } -//SignatureValues returns signature values. This signature needs to be in the [R || S || V] format where V is 0 or 1. +// SignatureValues returns signature values. This signature needs to be in the [R || S || V] format where V is 0 or 1. func (ordersign OrderTxSigner) SignatureValues(tx *OrderTransaction, sig []byte) (r, s, v *big.Int, err error) { if len(sig) != 65 { panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig))) @@ -108,7 +108,7 @@ func (ordersign OrderTxSigner) SignatureValues(tx *OrderTransaction, sig []byte) // OrderCreateHash hash of new order func (ordersign OrderTxSigner) OrderCreateHash(tx *OrderTransaction) common.Hash { - sha := sha3.NewKeccak256() + sha := sha3.NewLegacyKeccak256() sha.Write(tx.ExchangeAddress().Bytes()) sha.Write(tx.UserAddress().Bytes()) sha.Write(tx.BaseToken().Bytes()) @@ -128,7 +128,7 @@ func (ordersign OrderTxSigner) OrderCreateHash(tx *OrderTransaction) common.Hash // OrderCancelHash hash of cancelled order func (ordersign OrderTxSigner) OrderCancelHash(tx *OrderTransaction) common.Hash { - sha := sha3.NewKeccak256() + sha := sha3.NewLegacyKeccak256() sha.Write(tx.OrderHash().Bytes()) sha.Write(common.BigToHash(big.NewInt(int64(tx.Nonce()))).Bytes()) sha.Write(tx.UserAddress().Bytes()) @@ -150,7 +150,7 @@ func (ordersign OrderTxSigner) Hash(tx *OrderTransaction) common.Hash { return ordersign.OrderCreateHash(tx) } -//MarshalSignature encode signature +// MarshalSignature encode signature func MarshalSignature(R, S, V *big.Int) ([]byte, error) { sigBytes1 := common.BigToHash(R).Bytes() sigBytes2 := common.BigToHash(S).Bytes() diff --git a/crypto/sha3/LICENSE b/crypto/sha3/LICENSE deleted file mode 100644 index 6a66aea5eafe..000000000000 --- a/crypto/sha3/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/crypto/sha3/PATENTS b/crypto/sha3/PATENTS deleted file mode 100644 index 733099041f84..000000000000 --- a/crypto/sha3/PATENTS +++ /dev/null @@ -1,22 +0,0 @@ -Additional IP Rights Grant (Patents) - -"This implementation" means the copyrightable works distributed by -Google as part of the Go project. - -Google hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this section) -patent license to make, have made, use, offer to sell, sell, import, -transfer and otherwise run, modify and propagate the contents of this -implementation of Go, where such license applies only to those patent -claims, both currently owned or controlled by Google and acquired in -the future, licensable by Google that are necessarily infringed by this -implementation of Go. This grant does not include claims that would be -infringed only as a consequence of further modification of this -implementation. If you or your agent or exclusive licensee institute or -order or agree to the institution of patent litigation against any -entity (including a cross-claim or counterclaim in a lawsuit) alleging -that this implementation of Go or any code incorporated within this -implementation of Go constitutes direct or contributory patent -infringement, or inducement of patent infringement, then any patent -rights granted to you under this License for this implementation of Go -shall terminate as of the date such litigation is filed. diff --git a/crypto/sha3/doc.go b/crypto/sha3/doc.go deleted file mode 100644 index 3dab530f8626..000000000000 --- a/crypto/sha3/doc.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package sha3 implements the SHA-3 fixed-output-length hash functions and -// the SHAKE variable-output-length hash functions defined by FIPS-202. -// -// Both types of hash function use the "sponge" construction and the Keccak -// permutation. For a detailed specification see http://keccak.noekeon.org/ -// -// -// Guidance -// -// If you aren't sure what function you need, use SHAKE256 with at least 64 -// bytes of output. The SHAKE instances are faster than the SHA3 instances; -// the latter have to allocate memory to conform to the hash.Hash interface. -// -// If you need a secret-key MAC (message authentication code), prepend the -// secret key to the input, hash with SHAKE256 and read at least 32 bytes of -// output. -// -// -// Security strengths -// -// The SHA3-x (x equals 224, 256, 384, or 512) functions have a security -// strength against preimage attacks of x bits. Since they only produce "x" -// bits of output, their collision-resistance is only "x/2" bits. -// -// The SHAKE-256 and -128 functions have a generic security strength of 256 and -// 128 bits against all attacks, provided that at least 2x bits of their output -// is used. Requesting more than 64 or 32 bytes of output, respectively, does -// not increase the collision-resistance of the SHAKE functions. -// -// -// The sponge construction -// -// A sponge builds a pseudo-random function from a public pseudo-random -// permutation, by applying the permutation to a state of "rate + capacity" -// bytes, but hiding "capacity" of the bytes. -// -// A sponge starts out with a zero state. To hash an input using a sponge, up -// to "rate" bytes of the input are XORed into the sponge's state. The sponge -// is then "full" and the permutation is applied to "empty" it. This process is -// repeated until all the input has been "absorbed". The input is then padded. -// The digest is "squeezed" from the sponge in the same way, except that output -// output is copied out instead of input being XORed in. -// -// A sponge is parameterized by its generic security strength, which is equal -// to half its capacity; capacity + rate is equal to the permutation's width. -// Since the KeccakF-1600 permutation is 1600 bits (200 bytes) wide, this means -// that the security strength of a sponge instance is equal to (1600 - bitrate) / 2. -// -// -// Recommendations -// -// The SHAKE functions are recommended for most new uses. They can produce -// output of arbitrary length. SHAKE256, with an output length of at least -// 64 bytes, provides 256-bit security against all attacks. The Keccak team -// recommends it for most applications upgrading from SHA2-512. (NIST chose a -// much stronger, but much slower, sponge instance for SHA3-512.) -// -// The SHA-3 functions are "drop-in" replacements for the SHA-2 functions. -// They produce output of the same length, with the same security strengths -// against all attacks. This means, in particular, that SHA3-256 only has -// 128-bit collision resistance, because its output length is 32 bytes. -package sha3 diff --git a/crypto/sha3/hashes.go b/crypto/sha3/hashes.go deleted file mode 100644 index fa0d7b436245..000000000000 --- a/crypto/sha3/hashes.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sha3 - -// This file provides functions for creating instances of the SHA-3 -// and SHAKE hash functions, as well as utility functions for hashing -// bytes. - -import ( - "hash" -) - -// NewKeccak256 creates a new Keccak-256 hash. -func NewKeccak256() hash.Hash { return &state{rate: 136, outputLen: 32, dsbyte: 0x01} } - -// NewKeccak512 creates a new Keccak-512 hash. -func NewKeccak512() hash.Hash { return &state{rate: 72, outputLen: 64, dsbyte: 0x01} } - -// New224 creates a new SHA3-224 hash. -// Its generic security strength is 224 bits against preimage attacks, -// and 112 bits against collision attacks. -func New224() hash.Hash { return &state{rate: 144, outputLen: 28, dsbyte: 0x06} } - -// New256 creates a new SHA3-256 hash. -// Its generic security strength is 256 bits against preimage attacks, -// and 128 bits against collision attacks. -func New256() hash.Hash { return &state{rate: 136, outputLen: 32, dsbyte: 0x06} } - -// New384 creates a new SHA3-384 hash. -// Its generic security strength is 384 bits against preimage attacks, -// and 192 bits against collision attacks. -func New384() hash.Hash { return &state{rate: 104, outputLen: 48, dsbyte: 0x06} } - -// New512 creates a new SHA3-512 hash. -// Its generic security strength is 512 bits against preimage attacks, -// and 256 bits against collision attacks. -func New512() hash.Hash { return &state{rate: 72, outputLen: 64, dsbyte: 0x06} } - -// Sum224 returns the SHA3-224 digest of the data. -func Sum224(data []byte) (digest [28]byte) { - h := New224() - h.Write(data) - h.Sum(digest[:0]) - return -} - -// Sum256 returns the SHA3-256 digest of the data. -func Sum256(data []byte) (digest [32]byte) { - h := New256() - h.Write(data) - h.Sum(digest[:0]) - return -} - -// Sum384 returns the SHA3-384 digest of the data. -func Sum384(data []byte) (digest [48]byte) { - h := New384() - h.Write(data) - h.Sum(digest[:0]) - return -} - -// Sum512 returns the SHA3-512 digest of the data. -func Sum512(data []byte) (digest [64]byte) { - h := New512() - h.Write(data) - h.Sum(digest[:0]) - return -} diff --git a/crypto/sha3/keccakf.go b/crypto/sha3/keccakf.go deleted file mode 100644 index 46d03ed385df..000000000000 --- a/crypto/sha3/keccakf.go +++ /dev/null @@ -1,412 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !amd64 appengine gccgo - -package sha3 - -// rc stores the round constants for use in the ι step. -var rc = [24]uint64{ - 0x0000000000000001, - 0x0000000000008082, - 0x800000000000808A, - 0x8000000080008000, - 0x000000000000808B, - 0x0000000080000001, - 0x8000000080008081, - 0x8000000000008009, - 0x000000000000008A, - 0x0000000000000088, - 0x0000000080008009, - 0x000000008000000A, - 0x000000008000808B, - 0x800000000000008B, - 0x8000000000008089, - 0x8000000000008003, - 0x8000000000008002, - 0x8000000000000080, - 0x000000000000800A, - 0x800000008000000A, - 0x8000000080008081, - 0x8000000000008080, - 0x0000000080000001, - 0x8000000080008008, -} - -// keccakF1600 applies the Keccak permutation to a 1600b-wide -// state represented as a slice of 25 uint64s. -func keccakF1600(a *[25]uint64) { - // Implementation translated from Keccak-inplace.c - // in the keccak reference code. - var t, bc0, bc1, bc2, bc3, bc4, d0, d1, d2, d3, d4 uint64 - - for i := 0; i < 24; i += 4 { - // Combines the 5 steps in each round into 2 steps. - // Unrolls 4 rounds per loop and spreads some steps across rounds. - - // Round 1 - bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20] - bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21] - bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22] - bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23] - bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24] - d0 = bc4 ^ (bc1<<1 | bc1>>63) - d1 = bc0 ^ (bc2<<1 | bc2>>63) - d2 = bc1 ^ (bc3<<1 | bc3>>63) - d3 = bc2 ^ (bc4<<1 | bc4>>63) - d4 = bc3 ^ (bc0<<1 | bc0>>63) - - bc0 = a[0] ^ d0 - t = a[6] ^ d1 - bc1 = t<<44 | t>>(64-44) - t = a[12] ^ d2 - bc2 = t<<43 | t>>(64-43) - t = a[18] ^ d3 - bc3 = t<<21 | t>>(64-21) - t = a[24] ^ d4 - bc4 = t<<14 | t>>(64-14) - a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i] - a[6] = bc1 ^ (bc3 &^ bc2) - a[12] = bc2 ^ (bc4 &^ bc3) - a[18] = bc3 ^ (bc0 &^ bc4) - a[24] = bc4 ^ (bc1 &^ bc0) - - t = a[10] ^ d0 - bc2 = t<<3 | t>>(64-3) - t = a[16] ^ d1 - bc3 = t<<45 | t>>(64-45) - t = a[22] ^ d2 - bc4 = t<<61 | t>>(64-61) - t = a[3] ^ d3 - bc0 = t<<28 | t>>(64-28) - t = a[9] ^ d4 - bc1 = t<<20 | t>>(64-20) - a[10] = bc0 ^ (bc2 &^ bc1) - a[16] = bc1 ^ (bc3 &^ bc2) - a[22] = bc2 ^ (bc4 &^ bc3) - a[3] = bc3 ^ (bc0 &^ bc4) - a[9] = bc4 ^ (bc1 &^ bc0) - - t = a[20] ^ d0 - bc4 = t<<18 | t>>(64-18) - t = a[1] ^ d1 - bc0 = t<<1 | t>>(64-1) - t = a[7] ^ d2 - bc1 = t<<6 | t>>(64-6) - t = a[13] ^ d3 - bc2 = t<<25 | t>>(64-25) - t = a[19] ^ d4 - bc3 = t<<8 | t>>(64-8) - a[20] = bc0 ^ (bc2 &^ bc1) - a[1] = bc1 ^ (bc3 &^ bc2) - a[7] = bc2 ^ (bc4 &^ bc3) - a[13] = bc3 ^ (bc0 &^ bc4) - a[19] = bc4 ^ (bc1 &^ bc0) - - t = a[5] ^ d0 - bc1 = t<<36 | t>>(64-36) - t = a[11] ^ d1 - bc2 = t<<10 | t>>(64-10) - t = a[17] ^ d2 - bc3 = t<<15 | t>>(64-15) - t = a[23] ^ d3 - bc4 = t<<56 | t>>(64-56) - t = a[4] ^ d4 - bc0 = t<<27 | t>>(64-27) - a[5] = bc0 ^ (bc2 &^ bc1) - a[11] = bc1 ^ (bc3 &^ bc2) - a[17] = bc2 ^ (bc4 &^ bc3) - a[23] = bc3 ^ (bc0 &^ bc4) - a[4] = bc4 ^ (bc1 &^ bc0) - - t = a[15] ^ d0 - bc3 = t<<41 | t>>(64-41) - t = a[21] ^ d1 - bc4 = t<<2 | t>>(64-2) - t = a[2] ^ d2 - bc0 = t<<62 | t>>(64-62) - t = a[8] ^ d3 - bc1 = t<<55 | t>>(64-55) - t = a[14] ^ d4 - bc2 = t<<39 | t>>(64-39) - a[15] = bc0 ^ (bc2 &^ bc1) - a[21] = bc1 ^ (bc3 &^ bc2) - a[2] = bc2 ^ (bc4 &^ bc3) - a[8] = bc3 ^ (bc0 &^ bc4) - a[14] = bc4 ^ (bc1 &^ bc0) - - // Round 2 - bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20] - bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21] - bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22] - bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23] - bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24] - d0 = bc4 ^ (bc1<<1 | bc1>>63) - d1 = bc0 ^ (bc2<<1 | bc2>>63) - d2 = bc1 ^ (bc3<<1 | bc3>>63) - d3 = bc2 ^ (bc4<<1 | bc4>>63) - d4 = bc3 ^ (bc0<<1 | bc0>>63) - - bc0 = a[0] ^ d0 - t = a[16] ^ d1 - bc1 = t<<44 | t>>(64-44) - t = a[7] ^ d2 - bc2 = t<<43 | t>>(64-43) - t = a[23] ^ d3 - bc3 = t<<21 | t>>(64-21) - t = a[14] ^ d4 - bc4 = t<<14 | t>>(64-14) - a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+1] - a[16] = bc1 ^ (bc3 &^ bc2) - a[7] = bc2 ^ (bc4 &^ bc3) - a[23] = bc3 ^ (bc0 &^ bc4) - a[14] = bc4 ^ (bc1 &^ bc0) - - t = a[20] ^ d0 - bc2 = t<<3 | t>>(64-3) - t = a[11] ^ d1 - bc3 = t<<45 | t>>(64-45) - t = a[2] ^ d2 - bc4 = t<<61 | t>>(64-61) - t = a[18] ^ d3 - bc0 = t<<28 | t>>(64-28) - t = a[9] ^ d4 - bc1 = t<<20 | t>>(64-20) - a[20] = bc0 ^ (bc2 &^ bc1) - a[11] = bc1 ^ (bc3 &^ bc2) - a[2] = bc2 ^ (bc4 &^ bc3) - a[18] = bc3 ^ (bc0 &^ bc4) - a[9] = bc4 ^ (bc1 &^ bc0) - - t = a[15] ^ d0 - bc4 = t<<18 | t>>(64-18) - t = a[6] ^ d1 - bc0 = t<<1 | t>>(64-1) - t = a[22] ^ d2 - bc1 = t<<6 | t>>(64-6) - t = a[13] ^ d3 - bc2 = t<<25 | t>>(64-25) - t = a[4] ^ d4 - bc3 = t<<8 | t>>(64-8) - a[15] = bc0 ^ (bc2 &^ bc1) - a[6] = bc1 ^ (bc3 &^ bc2) - a[22] = bc2 ^ (bc4 &^ bc3) - a[13] = bc3 ^ (bc0 &^ bc4) - a[4] = bc4 ^ (bc1 &^ bc0) - - t = a[10] ^ d0 - bc1 = t<<36 | t>>(64-36) - t = a[1] ^ d1 - bc2 = t<<10 | t>>(64-10) - t = a[17] ^ d2 - bc3 = t<<15 | t>>(64-15) - t = a[8] ^ d3 - bc4 = t<<56 | t>>(64-56) - t = a[24] ^ d4 - bc0 = t<<27 | t>>(64-27) - a[10] = bc0 ^ (bc2 &^ bc1) - a[1] = bc1 ^ (bc3 &^ bc2) - a[17] = bc2 ^ (bc4 &^ bc3) - a[8] = bc3 ^ (bc0 &^ bc4) - a[24] = bc4 ^ (bc1 &^ bc0) - - t = a[5] ^ d0 - bc3 = t<<41 | t>>(64-41) - t = a[21] ^ d1 - bc4 = t<<2 | t>>(64-2) - t = a[12] ^ d2 - bc0 = t<<62 | t>>(64-62) - t = a[3] ^ d3 - bc1 = t<<55 | t>>(64-55) - t = a[19] ^ d4 - bc2 = t<<39 | t>>(64-39) - a[5] = bc0 ^ (bc2 &^ bc1) - a[21] = bc1 ^ (bc3 &^ bc2) - a[12] = bc2 ^ (bc4 &^ bc3) - a[3] = bc3 ^ (bc0 &^ bc4) - a[19] = bc4 ^ (bc1 &^ bc0) - - // Round 3 - bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20] - bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21] - bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22] - bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23] - bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24] - d0 = bc4 ^ (bc1<<1 | bc1>>63) - d1 = bc0 ^ (bc2<<1 | bc2>>63) - d2 = bc1 ^ (bc3<<1 | bc3>>63) - d3 = bc2 ^ (bc4<<1 | bc4>>63) - d4 = bc3 ^ (bc0<<1 | bc0>>63) - - bc0 = a[0] ^ d0 - t = a[11] ^ d1 - bc1 = t<<44 | t>>(64-44) - t = a[22] ^ d2 - bc2 = t<<43 | t>>(64-43) - t = a[8] ^ d3 - bc3 = t<<21 | t>>(64-21) - t = a[19] ^ d4 - bc4 = t<<14 | t>>(64-14) - a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+2] - a[11] = bc1 ^ (bc3 &^ bc2) - a[22] = bc2 ^ (bc4 &^ bc3) - a[8] = bc3 ^ (bc0 &^ bc4) - a[19] = bc4 ^ (bc1 &^ bc0) - - t = a[15] ^ d0 - bc2 = t<<3 | t>>(64-3) - t = a[1] ^ d1 - bc3 = t<<45 | t>>(64-45) - t = a[12] ^ d2 - bc4 = t<<61 | t>>(64-61) - t = a[23] ^ d3 - bc0 = t<<28 | t>>(64-28) - t = a[9] ^ d4 - bc1 = t<<20 | t>>(64-20) - a[15] = bc0 ^ (bc2 &^ bc1) - a[1] = bc1 ^ (bc3 &^ bc2) - a[12] = bc2 ^ (bc4 &^ bc3) - a[23] = bc3 ^ (bc0 &^ bc4) - a[9] = bc4 ^ (bc1 &^ bc0) - - t = a[5] ^ d0 - bc4 = t<<18 | t>>(64-18) - t = a[16] ^ d1 - bc0 = t<<1 | t>>(64-1) - t = a[2] ^ d2 - bc1 = t<<6 | t>>(64-6) - t = a[13] ^ d3 - bc2 = t<<25 | t>>(64-25) - t = a[24] ^ d4 - bc3 = t<<8 | t>>(64-8) - a[5] = bc0 ^ (bc2 &^ bc1) - a[16] = bc1 ^ (bc3 &^ bc2) - a[2] = bc2 ^ (bc4 &^ bc3) - a[13] = bc3 ^ (bc0 &^ bc4) - a[24] = bc4 ^ (bc1 &^ bc0) - - t = a[20] ^ d0 - bc1 = t<<36 | t>>(64-36) - t = a[6] ^ d1 - bc2 = t<<10 | t>>(64-10) - t = a[17] ^ d2 - bc3 = t<<15 | t>>(64-15) - t = a[3] ^ d3 - bc4 = t<<56 | t>>(64-56) - t = a[14] ^ d4 - bc0 = t<<27 | t>>(64-27) - a[20] = bc0 ^ (bc2 &^ bc1) - a[6] = bc1 ^ (bc3 &^ bc2) - a[17] = bc2 ^ (bc4 &^ bc3) - a[3] = bc3 ^ (bc0 &^ bc4) - a[14] = bc4 ^ (bc1 &^ bc0) - - t = a[10] ^ d0 - bc3 = t<<41 | t>>(64-41) - t = a[21] ^ d1 - bc4 = t<<2 | t>>(64-2) - t = a[7] ^ d2 - bc0 = t<<62 | t>>(64-62) - t = a[18] ^ d3 - bc1 = t<<55 | t>>(64-55) - t = a[4] ^ d4 - bc2 = t<<39 | t>>(64-39) - a[10] = bc0 ^ (bc2 &^ bc1) - a[21] = bc1 ^ (bc3 &^ bc2) - a[7] = bc2 ^ (bc4 &^ bc3) - a[18] = bc3 ^ (bc0 &^ bc4) - a[4] = bc4 ^ (bc1 &^ bc0) - - // Round 4 - bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20] - bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21] - bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22] - bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23] - bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24] - d0 = bc4 ^ (bc1<<1 | bc1>>63) - d1 = bc0 ^ (bc2<<1 | bc2>>63) - d2 = bc1 ^ (bc3<<1 | bc3>>63) - d3 = bc2 ^ (bc4<<1 | bc4>>63) - d4 = bc3 ^ (bc0<<1 | bc0>>63) - - bc0 = a[0] ^ d0 - t = a[1] ^ d1 - bc1 = t<<44 | t>>(64-44) - t = a[2] ^ d2 - bc2 = t<<43 | t>>(64-43) - t = a[3] ^ d3 - bc3 = t<<21 | t>>(64-21) - t = a[4] ^ d4 - bc4 = t<<14 | t>>(64-14) - a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+3] - a[1] = bc1 ^ (bc3 &^ bc2) - a[2] = bc2 ^ (bc4 &^ bc3) - a[3] = bc3 ^ (bc0 &^ bc4) - a[4] = bc4 ^ (bc1 &^ bc0) - - t = a[5] ^ d0 - bc2 = t<<3 | t>>(64-3) - t = a[6] ^ d1 - bc3 = t<<45 | t>>(64-45) - t = a[7] ^ d2 - bc4 = t<<61 | t>>(64-61) - t = a[8] ^ d3 - bc0 = t<<28 | t>>(64-28) - t = a[9] ^ d4 - bc1 = t<<20 | t>>(64-20) - a[5] = bc0 ^ (bc2 &^ bc1) - a[6] = bc1 ^ (bc3 &^ bc2) - a[7] = bc2 ^ (bc4 &^ bc3) - a[8] = bc3 ^ (bc0 &^ bc4) - a[9] = bc4 ^ (bc1 &^ bc0) - - t = a[10] ^ d0 - bc4 = t<<18 | t>>(64-18) - t = a[11] ^ d1 - bc0 = t<<1 | t>>(64-1) - t = a[12] ^ d2 - bc1 = t<<6 | t>>(64-6) - t = a[13] ^ d3 - bc2 = t<<25 | t>>(64-25) - t = a[14] ^ d4 - bc3 = t<<8 | t>>(64-8) - a[10] = bc0 ^ (bc2 &^ bc1) - a[11] = bc1 ^ (bc3 &^ bc2) - a[12] = bc2 ^ (bc4 &^ bc3) - a[13] = bc3 ^ (bc0 &^ bc4) - a[14] = bc4 ^ (bc1 &^ bc0) - - t = a[15] ^ d0 - bc1 = t<<36 | t>>(64-36) - t = a[16] ^ d1 - bc2 = t<<10 | t>>(64-10) - t = a[17] ^ d2 - bc3 = t<<15 | t>>(64-15) - t = a[18] ^ d3 - bc4 = t<<56 | t>>(64-56) - t = a[19] ^ d4 - bc0 = t<<27 | t>>(64-27) - a[15] = bc0 ^ (bc2 &^ bc1) - a[16] = bc1 ^ (bc3 &^ bc2) - a[17] = bc2 ^ (bc4 &^ bc3) - a[18] = bc3 ^ (bc0 &^ bc4) - a[19] = bc4 ^ (bc1 &^ bc0) - - t = a[20] ^ d0 - bc3 = t<<41 | t>>(64-41) - t = a[21] ^ d1 - bc4 = t<<2 | t>>(64-2) - t = a[22] ^ d2 - bc0 = t<<62 | t>>(64-62) - t = a[23] ^ d3 - bc1 = t<<55 | t>>(64-55) - t = a[24] ^ d4 - bc2 = t<<39 | t>>(64-39) - a[20] = bc0 ^ (bc2 &^ bc1) - a[21] = bc1 ^ (bc3 &^ bc2) - a[22] = bc2 ^ (bc4 &^ bc3) - a[23] = bc3 ^ (bc0 &^ bc4) - a[24] = bc4 ^ (bc1 &^ bc0) - } -} diff --git a/crypto/sha3/keccakf_amd64.go b/crypto/sha3/keccakf_amd64.go deleted file mode 100644 index de035c550f40..000000000000 --- a/crypto/sha3/keccakf_amd64.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build amd64,!appengine,!gccgo - -package sha3 - -// This function is implemented in keccakf_amd64.s. - -//go:noescape - -func keccakF1600(state *[25]uint64) diff --git a/crypto/sha3/keccakf_amd64.s b/crypto/sha3/keccakf_amd64.s deleted file mode 100644 index f88533accd44..000000000000 --- a/crypto/sha3/keccakf_amd64.s +++ /dev/null @@ -1,390 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build amd64,!appengine,!gccgo - -// This code was translated into a form compatible with 6a from the public -// domain sources at https://github.com/gvanas/KeccakCodePackage - -// Offsets in state -#define _ba (0*8) -#define _be (1*8) -#define _bi (2*8) -#define _bo (3*8) -#define _bu (4*8) -#define _ga (5*8) -#define _ge (6*8) -#define _gi (7*8) -#define _go (8*8) -#define _gu (9*8) -#define _ka (10*8) -#define _ke (11*8) -#define _ki (12*8) -#define _ko (13*8) -#define _ku (14*8) -#define _ma (15*8) -#define _me (16*8) -#define _mi (17*8) -#define _mo (18*8) -#define _mu (19*8) -#define _sa (20*8) -#define _se (21*8) -#define _si (22*8) -#define _so (23*8) -#define _su (24*8) - -// Temporary registers -#define rT1 AX - -// Round vars -#define rpState DI -#define rpStack SP - -#define rDa BX -#define rDe CX -#define rDi DX -#define rDo R8 -#define rDu R9 - -#define rBa R10 -#define rBe R11 -#define rBi R12 -#define rBo R13 -#define rBu R14 - -#define rCa SI -#define rCe BP -#define rCi rBi -#define rCo rBo -#define rCu R15 - -#define MOVQ_RBI_RCE MOVQ rBi, rCe -#define XORQ_RT1_RCA XORQ rT1, rCa -#define XORQ_RT1_RCE XORQ rT1, rCe -#define XORQ_RBA_RCU XORQ rBa, rCu -#define XORQ_RBE_RCU XORQ rBe, rCu -#define XORQ_RDU_RCU XORQ rDu, rCu -#define XORQ_RDA_RCA XORQ rDa, rCa -#define XORQ_RDE_RCE XORQ rDe, rCe - -#define mKeccakRound(iState, oState, rc, B_RBI_RCE, G_RT1_RCA, G_RT1_RCE, G_RBA_RCU, K_RT1_RCA, K_RT1_RCE, K_RBA_RCU, M_RT1_RCA, M_RT1_RCE, M_RBE_RCU, S_RDU_RCU, S_RDA_RCA, S_RDE_RCE) \ - /* Prepare round */ \ - MOVQ rCe, rDa; \ - ROLQ $1, rDa; \ - \ - MOVQ _bi(iState), rCi; \ - XORQ _gi(iState), rDi; \ - XORQ rCu, rDa; \ - XORQ _ki(iState), rCi; \ - XORQ _mi(iState), rDi; \ - XORQ rDi, rCi; \ - \ - MOVQ rCi, rDe; \ - ROLQ $1, rDe; \ - \ - MOVQ _bo(iState), rCo; \ - XORQ _go(iState), rDo; \ - XORQ rCa, rDe; \ - XORQ _ko(iState), rCo; \ - XORQ _mo(iState), rDo; \ - XORQ rDo, rCo; \ - \ - MOVQ rCo, rDi; \ - ROLQ $1, rDi; \ - \ - MOVQ rCu, rDo; \ - XORQ rCe, rDi; \ - ROLQ $1, rDo; \ - \ - MOVQ rCa, rDu; \ - XORQ rCi, rDo; \ - ROLQ $1, rDu; \ - \ - /* Result b */ \ - MOVQ _ba(iState), rBa; \ - MOVQ _ge(iState), rBe; \ - XORQ rCo, rDu; \ - MOVQ _ki(iState), rBi; \ - MOVQ _mo(iState), rBo; \ - MOVQ _su(iState), rBu; \ - XORQ rDe, rBe; \ - ROLQ $44, rBe; \ - XORQ rDi, rBi; \ - XORQ rDa, rBa; \ - ROLQ $43, rBi; \ - \ - MOVQ rBe, rCa; \ - MOVQ rc, rT1; \ - ORQ rBi, rCa; \ - XORQ rBa, rT1; \ - XORQ rT1, rCa; \ - MOVQ rCa, _ba(oState); \ - \ - XORQ rDu, rBu; \ - ROLQ $14, rBu; \ - MOVQ rBa, rCu; \ - ANDQ rBe, rCu; \ - XORQ rBu, rCu; \ - MOVQ rCu, _bu(oState); \ - \ - XORQ rDo, rBo; \ - ROLQ $21, rBo; \ - MOVQ rBo, rT1; \ - ANDQ rBu, rT1; \ - XORQ rBi, rT1; \ - MOVQ rT1, _bi(oState); \ - \ - NOTQ rBi; \ - ORQ rBa, rBu; \ - ORQ rBo, rBi; \ - XORQ rBo, rBu; \ - XORQ rBe, rBi; \ - MOVQ rBu, _bo(oState); \ - MOVQ rBi, _be(oState); \ - B_RBI_RCE; \ - \ - /* Result g */ \ - MOVQ _gu(iState), rBe; \ - XORQ rDu, rBe; \ - MOVQ _ka(iState), rBi; \ - ROLQ $20, rBe; \ - XORQ rDa, rBi; \ - ROLQ $3, rBi; \ - MOVQ _bo(iState), rBa; \ - MOVQ rBe, rT1; \ - ORQ rBi, rT1; \ - XORQ rDo, rBa; \ - MOVQ _me(iState), rBo; \ - MOVQ _si(iState), rBu; \ - ROLQ $28, rBa; \ - XORQ rBa, rT1; \ - MOVQ rT1, _ga(oState); \ - G_RT1_RCA; \ - \ - XORQ rDe, rBo; \ - ROLQ $45, rBo; \ - MOVQ rBi, rT1; \ - ANDQ rBo, rT1; \ - XORQ rBe, rT1; \ - MOVQ rT1, _ge(oState); \ - G_RT1_RCE; \ - \ - XORQ rDi, rBu; \ - ROLQ $61, rBu; \ - MOVQ rBu, rT1; \ - ORQ rBa, rT1; \ - XORQ rBo, rT1; \ - MOVQ rT1, _go(oState); \ - \ - ANDQ rBe, rBa; \ - XORQ rBu, rBa; \ - MOVQ rBa, _gu(oState); \ - NOTQ rBu; \ - G_RBA_RCU; \ - \ - ORQ rBu, rBo; \ - XORQ rBi, rBo; \ - MOVQ rBo, _gi(oState); \ - \ - /* Result k */ \ - MOVQ _be(iState), rBa; \ - MOVQ _gi(iState), rBe; \ - MOVQ _ko(iState), rBi; \ - MOVQ _mu(iState), rBo; \ - MOVQ _sa(iState), rBu; \ - XORQ rDi, rBe; \ - ROLQ $6, rBe; \ - XORQ rDo, rBi; \ - ROLQ $25, rBi; \ - MOVQ rBe, rT1; \ - ORQ rBi, rT1; \ - XORQ rDe, rBa; \ - ROLQ $1, rBa; \ - XORQ rBa, rT1; \ - MOVQ rT1, _ka(oState); \ - K_RT1_RCA; \ - \ - XORQ rDu, rBo; \ - ROLQ $8, rBo; \ - MOVQ rBi, rT1; \ - ANDQ rBo, rT1; \ - XORQ rBe, rT1; \ - MOVQ rT1, _ke(oState); \ - K_RT1_RCE; \ - \ - XORQ rDa, rBu; \ - ROLQ $18, rBu; \ - NOTQ rBo; \ - MOVQ rBo, rT1; \ - ANDQ rBu, rT1; \ - XORQ rBi, rT1; \ - MOVQ rT1, _ki(oState); \ - \ - MOVQ rBu, rT1; \ - ORQ rBa, rT1; \ - XORQ rBo, rT1; \ - MOVQ rT1, _ko(oState); \ - \ - ANDQ rBe, rBa; \ - XORQ rBu, rBa; \ - MOVQ rBa, _ku(oState); \ - K_RBA_RCU; \ - \ - /* Result m */ \ - MOVQ _ga(iState), rBe; \ - XORQ rDa, rBe; \ - MOVQ _ke(iState), rBi; \ - ROLQ $36, rBe; \ - XORQ rDe, rBi; \ - MOVQ _bu(iState), rBa; \ - ROLQ $10, rBi; \ - MOVQ rBe, rT1; \ - MOVQ _mi(iState), rBo; \ - ANDQ rBi, rT1; \ - XORQ rDu, rBa; \ - MOVQ _so(iState), rBu; \ - ROLQ $27, rBa; \ - XORQ rBa, rT1; \ - MOVQ rT1, _ma(oState); \ - M_RT1_RCA; \ - \ - XORQ rDi, rBo; \ - ROLQ $15, rBo; \ - MOVQ rBi, rT1; \ - ORQ rBo, rT1; \ - XORQ rBe, rT1; \ - MOVQ rT1, _me(oState); \ - M_RT1_RCE; \ - \ - XORQ rDo, rBu; \ - ROLQ $56, rBu; \ - NOTQ rBo; \ - MOVQ rBo, rT1; \ - ORQ rBu, rT1; \ - XORQ rBi, rT1; \ - MOVQ rT1, _mi(oState); \ - \ - ORQ rBa, rBe; \ - XORQ rBu, rBe; \ - MOVQ rBe, _mu(oState); \ - \ - ANDQ rBa, rBu; \ - XORQ rBo, rBu; \ - MOVQ rBu, _mo(oState); \ - M_RBE_RCU; \ - \ - /* Result s */ \ - MOVQ _bi(iState), rBa; \ - MOVQ _go(iState), rBe; \ - MOVQ _ku(iState), rBi; \ - XORQ rDi, rBa; \ - MOVQ _ma(iState), rBo; \ - ROLQ $62, rBa; \ - XORQ rDo, rBe; \ - MOVQ _se(iState), rBu; \ - ROLQ $55, rBe; \ - \ - XORQ rDu, rBi; \ - MOVQ rBa, rDu; \ - XORQ rDe, rBu; \ - ROLQ $2, rBu; \ - ANDQ rBe, rDu; \ - XORQ rBu, rDu; \ - MOVQ rDu, _su(oState); \ - \ - ROLQ $39, rBi; \ - S_RDU_RCU; \ - NOTQ rBe; \ - XORQ rDa, rBo; \ - MOVQ rBe, rDa; \ - ANDQ rBi, rDa; \ - XORQ rBa, rDa; \ - MOVQ rDa, _sa(oState); \ - S_RDA_RCA; \ - \ - ROLQ $41, rBo; \ - MOVQ rBi, rDe; \ - ORQ rBo, rDe; \ - XORQ rBe, rDe; \ - MOVQ rDe, _se(oState); \ - S_RDE_RCE; \ - \ - MOVQ rBo, rDi; \ - MOVQ rBu, rDo; \ - ANDQ rBu, rDi; \ - ORQ rBa, rDo; \ - XORQ rBi, rDi; \ - XORQ rBo, rDo; \ - MOVQ rDi, _si(oState); \ - MOVQ rDo, _so(oState) \ - -// func keccakF1600(state *[25]uint64) -TEXT ·keccakF1600(SB), 0, $200-8 - MOVQ state+0(FP), rpState - - // Convert the user state into an internal state - NOTQ _be(rpState) - NOTQ _bi(rpState) - NOTQ _go(rpState) - NOTQ _ki(rpState) - NOTQ _mi(rpState) - NOTQ _sa(rpState) - - // Execute the KeccakF permutation - MOVQ _ba(rpState), rCa - MOVQ _be(rpState), rCe - MOVQ _bu(rpState), rCu - - XORQ _ga(rpState), rCa - XORQ _ge(rpState), rCe - XORQ _gu(rpState), rCu - - XORQ _ka(rpState), rCa - XORQ _ke(rpState), rCe - XORQ _ku(rpState), rCu - - XORQ _ma(rpState), rCa - XORQ _me(rpState), rCe - XORQ _mu(rpState), rCu - - XORQ _sa(rpState), rCa - XORQ _se(rpState), rCe - MOVQ _si(rpState), rDi - MOVQ _so(rpState), rDo - XORQ _su(rpState), rCu - - mKeccakRound(rpState, rpStack, $0x0000000000000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x0000000000008082, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x800000000000808a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x8000000080008000, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x000000000000808b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x0000000080000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x8000000080008081, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x8000000000008009, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x000000000000008a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x0000000000000088, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x0000000080008009, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x000000008000000a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x000000008000808b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x800000000000008b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x8000000000008089, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x8000000000008003, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x8000000000008002, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x8000000000000080, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x000000000000800a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x800000008000000a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x8000000080008081, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x8000000000008080, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x0000000080000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x8000000080008008, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP) - - // Revert the internal state to the user state - NOTQ _be(rpState) - NOTQ _bi(rpState) - NOTQ _go(rpState) - NOTQ _ki(rpState) - NOTQ _mi(rpState) - NOTQ _sa(rpState) - - RET diff --git a/crypto/sha3/register.go b/crypto/sha3/register.go deleted file mode 100644 index 3cf6a22e093a..000000000000 --- a/crypto/sha3/register.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build go1.4 - -package sha3 - -import ( - "crypto" -) - -func init() { - crypto.RegisterHash(crypto.SHA3_224, New224) - crypto.RegisterHash(crypto.SHA3_256, New256) - crypto.RegisterHash(crypto.SHA3_384, New384) - crypto.RegisterHash(crypto.SHA3_512, New512) -} diff --git a/crypto/sha3/sha3.go b/crypto/sha3/sha3.go deleted file mode 100644 index b12a35c87fc8..000000000000 --- a/crypto/sha3/sha3.go +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sha3 - -// spongeDirection indicates the direction bytes are flowing through the sponge. -type spongeDirection int - -const ( - // spongeAbsorbing indicates that the sponge is absorbing input. - spongeAbsorbing spongeDirection = iota - // spongeSqueezing indicates that the sponge is being squeezed. - spongeSqueezing -) - -const ( - // maxRate is the maximum size of the internal buffer. SHAKE-256 - // currently needs the largest buffer. - maxRate = 168 -) - -type state struct { - // Generic sponge components. - a [25]uint64 // main state of the hash - buf []byte // points into storage - rate int // the number of bytes of state to use - - // dsbyte contains the "domain separation" bits and the first bit of - // the padding. Sections 6.1 and 6.2 of [1] separate the outputs of the - // SHA-3 and SHAKE functions by appending bitstrings to the message. - // Using a little-endian bit-ordering convention, these are "01" for SHA-3 - // and "1111" for SHAKE, or 00000010b and 00001111b, respectively. Then the - // padding rule from section 5.1 is applied to pad the message to a multiple - // of the rate, which involves adding a "1" bit, zero or more "0" bits, and - // a final "1" bit. We merge the first "1" bit from the padding into dsbyte, - // giving 00000110b (0x06) and 00011111b (0x1f). - // [1] http://csrc.nist.gov/publications/drafts/fips-202/fips_202_draft.pdf - // "Draft FIPS 202: SHA-3 Standard: Permutation-Based Hash and - // Extendable-Output Functions (May 2014)" - dsbyte byte - storage [maxRate]byte - - // Specific to SHA-3 and SHAKE. - outputLen int // the default output size in bytes - state spongeDirection // whether the sponge is absorbing or squeezing -} - -// BlockSize returns the rate of sponge underlying this hash function. -func (d *state) BlockSize() int { return d.rate } - -// Size returns the output size of the hash function in bytes. -func (d *state) Size() int { return d.outputLen } - -// Reset clears the internal state by zeroing the sponge state and -// the byte buffer, and setting Sponge.state to absorbing. -func (d *state) Reset() { - // Zero the permutation's state. - for i := range d.a { - d.a[i] = 0 - } - d.state = spongeAbsorbing - d.buf = d.storage[:0] -} - -func (d *state) clone() *state { - ret := *d - if ret.state == spongeAbsorbing { - ret.buf = ret.storage[:len(ret.buf)] - } else { - ret.buf = ret.storage[d.rate-cap(d.buf) : d.rate] - } - - return &ret -} - -// permute applies the KeccakF-1600 permutation. It handles -// any input-output buffering. -func (d *state) permute() { - switch d.state { - case spongeAbsorbing: - // If we're absorbing, we need to xor the input into the state - // before applying the permutation. - xorIn(d, d.buf) - d.buf = d.storage[:0] - keccakF1600(&d.a) - case spongeSqueezing: - // If we're squeezing, we need to apply the permutatin before - // copying more output. - keccakF1600(&d.a) - d.buf = d.storage[:d.rate] - copyOut(d, d.buf) - } -} - -// pads appends the domain separation bits in dsbyte, applies -// the multi-bitrate 10..1 padding rule, and permutes the state. -func (d *state) padAndPermute(dsbyte byte) { - if d.buf == nil { - d.buf = d.storage[:0] - } - // Pad with this instance's domain-separator bits. We know that there's - // at least one byte of space in d.buf because, if it were full, - // permute would have been called to empty it. dsbyte also contains the - // first one bit for the padding. See the comment in the state struct. - d.buf = append(d.buf, dsbyte) - zerosStart := len(d.buf) - d.buf = d.storage[:d.rate] - for i := zerosStart; i < d.rate; i++ { - d.buf[i] = 0 - } - // This adds the final one bit for the padding. Because of the way that - // bits are numbered from the LSB upwards, the final bit is the MSB of - // the last byte. - d.buf[d.rate-1] ^= 0x80 - // Apply the permutation - d.permute() - d.state = spongeSqueezing - d.buf = d.storage[:d.rate] - copyOut(d, d.buf) -} - -// Write absorbs more data into the hash's state. It produces an error -// if more data is written to the ShakeHash after writing -func (d *state) Write(p []byte) (written int, err error) { - if d.state != spongeAbsorbing { - panic("sha3: write to sponge after read") - } - if d.buf == nil { - d.buf = d.storage[:0] - } - written = len(p) - - for len(p) > 0 { - if len(d.buf) == 0 && len(p) >= d.rate { - // The fast path; absorb a full "rate" bytes of input and apply the permutation. - xorIn(d, p[:d.rate]) - p = p[d.rate:] - keccakF1600(&d.a) - } else { - // The slow path; buffer the input until we can fill the sponge, and then xor it in. - todo := d.rate - len(d.buf) - if todo > len(p) { - todo = len(p) - } - d.buf = append(d.buf, p[:todo]...) - p = p[todo:] - - // If the sponge is full, apply the permutation. - if len(d.buf) == d.rate { - d.permute() - } - } - } - - return -} - -// Read squeezes an arbitrary number of bytes from the sponge. -func (d *state) Read(out []byte) (n int, err error) { - // If we're still absorbing, pad and apply the permutation. - if d.state == spongeAbsorbing { - d.padAndPermute(d.dsbyte) - } - - n = len(out) - - // Now, do the squeezing. - for len(out) > 0 { - n := copy(out, d.buf) - d.buf = d.buf[n:] - out = out[n:] - - // Apply the permutation if we've squeezed the sponge dry. - if len(d.buf) == 0 { - d.permute() - } - } - - return -} - -// Sum applies padding to the hash state and then squeezes out the desired -// number of output bytes. -func (d *state) Sum(in []byte) []byte { - // Make a copy of the original hash so that caller can keep writing - // and summing. - dup := d.clone() - hash := make([]byte, dup.outputLen) - dup.Read(hash) - return append(in, hash...) -} diff --git a/crypto/sha3/sha3_test.go b/crypto/sha3/sha3_test.go deleted file mode 100644 index 0e33676ce64b..000000000000 --- a/crypto/sha3/sha3_test.go +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sha3 - -// Tests include all the ShortMsgKATs provided by the Keccak team at -// https://github.com/gvanas/KeccakCodePackage -// -// They only include the zero-bit case of the bitwise testvectors -// published by NIST in the draft of FIPS-202. - -import ( - "bytes" - "compress/flate" - "encoding/hex" - "encoding/json" - "hash" - "os" - "strings" - "testing" -) - -const ( - testString = "brekeccakkeccak koax koax" - katFilename = "testdata/keccakKats.json.deflate" -) - -// Internal-use instances of SHAKE used to test against KATs. -func newHashShake128() hash.Hash { - return &state{rate: 168, dsbyte: 0x1f, outputLen: 512} -} -func newHashShake256() hash.Hash { - return &state{rate: 136, dsbyte: 0x1f, outputLen: 512} -} - -// testDigests contains functions returning hash.Hash instances -// with output-length equal to the KAT length for both SHA-3 and -// SHAKE instances. -var testDigests = map[string]func() hash.Hash{ - "SHA3-224": New224, - "SHA3-256": New256, - "SHA3-384": New384, - "SHA3-512": New512, - "SHAKE128": newHashShake128, - "SHAKE256": newHashShake256, -} - -// testShakes contains functions that return ShakeHash instances for -// testing the ShakeHash-specific interface. -var testShakes = map[string]func() ShakeHash{ - "SHAKE128": NewShake128, - "SHAKE256": NewShake256, -} - -// structs used to marshal JSON test-cases. -type KeccakKats struct { - Kats map[string][]struct { - Digest string `json:"digest"` - Length int64 `json:"length"` - Message string `json:"message"` - } -} - -func testUnalignedAndGeneric(t *testing.T, testf func(impl string)) { - xorInOrig, copyOutOrig := xorIn, copyOut - xorIn, copyOut = xorInGeneric, copyOutGeneric - testf("generic") - if xorImplementationUnaligned != "generic" { - xorIn, copyOut = xorInUnaligned, copyOutUnaligned - testf("unaligned") - } - xorIn, copyOut = xorInOrig, copyOutOrig -} - -// TestKeccakKats tests the SHA-3 and Shake implementations against all the -// ShortMsgKATs from https://github.com/gvanas/KeccakCodePackage -// (The testvectors are stored in keccakKats.json.deflate due to their length.) -func TestKeccakKats(t *testing.T) { - testUnalignedAndGeneric(t, func(impl string) { - // Read the KATs. - deflated, err := os.Open(katFilename) - if err != nil { - t.Errorf("error opening %s: %s", katFilename, err) - } - file := flate.NewReader(deflated) - dec := json.NewDecoder(file) - var katSet KeccakKats - err = dec.Decode(&katSet) - if err != nil { - t.Errorf("error decoding KATs: %s", err) - } - - // Do the KATs. - for functionName, kats := range katSet.Kats { - d := testDigests[functionName]() - for _, kat := range kats { - d.Reset() - in, err := hex.DecodeString(kat.Message) - if err != nil { - t.Errorf("error decoding KAT: %s", err) - } - d.Write(in[:kat.Length/8]) - got := strings.ToUpper(hex.EncodeToString(d.Sum(nil))) - if got != kat.Digest { - t.Errorf("function=%s, implementation=%s, length=%d\nmessage:\n %s\ngot:\n %s\nwanted:\n %s", - functionName, impl, kat.Length, kat.Message, got, kat.Digest) - t.Logf("wanted %+v", kat) - t.FailNow() - } - continue - } - } - }) -} - -// TestUnalignedWrite tests that writing data in an arbitrary pattern with -// small input buffers. -func TestUnalignedWrite(t *testing.T) { - testUnalignedAndGeneric(t, func(impl string) { - buf := sequentialBytes(0x10000) - for alg, df := range testDigests { - d := df() - d.Reset() - d.Write(buf) - want := d.Sum(nil) - d.Reset() - for i := 0; i < len(buf); { - // Cycle through offsets which make a 137 byte sequence. - // Because 137 is prime this sequence should exercise all corner cases. - offsets := [17]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1} - for _, j := range offsets { - if v := len(buf) - i; v < j { - j = v - } - d.Write(buf[i : i+j]) - i += j - } - } - got := d.Sum(nil) - if !bytes.Equal(got, want) { - t.Errorf("Unaligned writes, implementation=%s, alg=%s\ngot %q, want %q", impl, alg, got, want) - } - } - }) -} - -// TestAppend checks that appending works when reallocation is necessary. -func TestAppend(t *testing.T) { - testUnalignedAndGeneric(t, func(impl string) { - d := New224() - - for capacity := 2; capacity <= 66; capacity += 64 { - // The first time around the loop, Sum will have to reallocate. - // The second time, it will not. - buf := make([]byte, 2, capacity) - d.Reset() - d.Write([]byte{0xcc}) - buf = d.Sum(buf) - expected := "0000DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39" - if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected { - t.Errorf("got %s, want %s", got, expected) - } - } - }) -} - -// TestAppendNoRealloc tests that appending works when no reallocation is necessary. -func TestAppendNoRealloc(t *testing.T) { - testUnalignedAndGeneric(t, func(impl string) { - buf := make([]byte, 1, 200) - d := New224() - d.Write([]byte{0xcc}) - buf = d.Sum(buf) - expected := "00DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39" - if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected { - t.Errorf("%s: got %s, want %s", impl, got, expected) - } - }) -} - -// TestSqueezing checks that squeezing the full output a single time produces -// the same output as repeatedly squeezing the instance. -func TestSqueezing(t *testing.T) { - testUnalignedAndGeneric(t, func(impl string) { - for functionName, newShakeHash := range testShakes { - d0 := newShakeHash() - d0.Write([]byte(testString)) - ref := make([]byte, 32) - d0.Read(ref) - - d1 := newShakeHash() - d1.Write([]byte(testString)) - var multiple []byte - for range ref { - one := make([]byte, 1) - d1.Read(one) - multiple = append(multiple, one...) - } - if !bytes.Equal(ref, multiple) { - t.Errorf("%s (%s): squeezing %d bytes one at a time failed", functionName, impl, len(ref)) - } - } - }) -} - -// sequentialBytes produces a buffer of size consecutive bytes 0x00, 0x01, ..., used for testing. -func sequentialBytes(size int) []byte { - result := make([]byte, size) - for i := range result { - result[i] = byte(i) - } - return result -} - -// BenchmarkPermutationFunction measures the speed of the permutation function -// with no input data. -func BenchmarkPermutationFunction(b *testing.B) { - b.SetBytes(int64(200)) - var lanes [25]uint64 - for i := 0; i < b.N; i++ { - keccakF1600(&lanes) - } -} - -// benchmarkHash tests the speed to hash num buffers of buflen each. -func benchmarkHash(b *testing.B, h hash.Hash, size, num int) { - b.StopTimer() - h.Reset() - data := sequentialBytes(size) - b.SetBytes(int64(size * num)) - b.StartTimer() - - var state []byte - for i := 0; i < b.N; i++ { - for j := 0; j < num; j++ { - h.Write(data) - } - state = h.Sum(state[:0]) - } - b.StopTimer() - h.Reset() -} - -// benchmarkShake is specialized to the Shake instances, which don't -// require a copy on reading output. -func benchmarkShake(b *testing.B, h ShakeHash, size, num int) { - b.StopTimer() - h.Reset() - data := sequentialBytes(size) - d := make([]byte, 32) - - b.SetBytes(int64(size * num)) - b.StartTimer() - - for i := 0; i < b.N; i++ { - h.Reset() - for j := 0; j < num; j++ { - h.Write(data) - } - h.Read(d) - } -} - -func BenchmarkSha3_512_MTU(b *testing.B) { benchmarkHash(b, New512(), 1350, 1) } -func BenchmarkSha3_384_MTU(b *testing.B) { benchmarkHash(b, New384(), 1350, 1) } -func BenchmarkSha3_256_MTU(b *testing.B) { benchmarkHash(b, New256(), 1350, 1) } -func BenchmarkSha3_224_MTU(b *testing.B) { benchmarkHash(b, New224(), 1350, 1) } - -func BenchmarkShake128_MTU(b *testing.B) { benchmarkShake(b, NewShake128(), 1350, 1) } -func BenchmarkShake256_MTU(b *testing.B) { benchmarkShake(b, NewShake256(), 1350, 1) } -func BenchmarkShake256_16x(b *testing.B) { benchmarkShake(b, NewShake256(), 16, 1024) } -func BenchmarkShake256_1MiB(b *testing.B) { benchmarkShake(b, NewShake256(), 1024, 1024) } - -func BenchmarkSha3_512_1MiB(b *testing.B) { benchmarkHash(b, New512(), 1024, 1024) } - -func Example_sum() { - buf := []byte("some data to hash") - // A hash needs to be 64 bytes long to have 256-bit collision resistance. - h := make([]byte, 64) - // Compute a 64-byte hash of buf and put it in h. - ShakeSum256(h, buf) -} - -func Example_mac() { - k := []byte("this is a secret key; you should generate a strong random key that's at least 32 bytes long") - buf := []byte("and this is some data to authenticate") - // A MAC with 32 bytes of output has 256-bit security strength -- if you use at least a 32-byte-long key. - h := make([]byte, 32) - d := NewShake256() - // Write the key into the hash. - d.Write(k) - // Now write the data. - d.Write(buf) - // Read 32 bytes of output from the hash into h. - d.Read(h) -} diff --git a/crypto/sha3/shake.go b/crypto/sha3/shake.go deleted file mode 100644 index 841f9860f031..000000000000 --- a/crypto/sha3/shake.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sha3 - -// This file defines the ShakeHash interface, and provides -// functions for creating SHAKE instances, as well as utility -// functions for hashing bytes to arbitrary-length output. - -import ( - "io" -) - -// ShakeHash defines the interface to hash functions that -// support arbitrary-length output. -type ShakeHash interface { - // Write absorbs more data into the hash's state. It panics if input is - // written to it after output has been read from it. - io.Writer - - // Read reads more output from the hash; reading affects the hash's - // state. (ShakeHash.Read is thus very different from Hash.Sum) - // It never returns an error. - io.Reader - - // Clone returns a copy of the ShakeHash in its current state. - Clone() ShakeHash - - // Reset resets the ShakeHash to its initial state. - Reset() -} - -func (d *state) Clone() ShakeHash { - return d.clone() -} - -// NewShake128 creates a new SHAKE128 variable-output-length ShakeHash. -// Its generic security strength is 128 bits against all attacks if at -// least 32 bytes of its output are used. -func NewShake128() ShakeHash { return &state{rate: 168, dsbyte: 0x1f} } - -// NewShake256 creates a new SHAKE128 variable-output-length ShakeHash. -// Its generic security strength is 256 bits against all attacks if -// at least 64 bytes of its output are used. -func NewShake256() ShakeHash { return &state{rate: 136, dsbyte: 0x1f} } - -// ShakeSum128 writes an arbitrary-length digest of data into hash. -func ShakeSum128(hash, data []byte) { - h := NewShake128() - h.Write(data) - h.Read(hash) -} - -// ShakeSum256 writes an arbitrary-length digest of data into hash. -func ShakeSum256(hash, data []byte) { - h := NewShake256() - h.Write(data) - h.Read(hash) -} diff --git a/crypto/sha3/testdata/keccakKats.json.deflate b/crypto/sha3/testdata/keccakKats.json.deflate deleted file mode 100644 index 62e85ae24236b46c09e5cfa84c71c69f5cc33cf6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 521342 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%0DdSPIES;LF z+8sZyomb#0C9Jq)u2x6cYW7ssU38nCiMbmZ_ips1JnZMdYR~SLvm(8T;k1=BSX$vOLIgo);70zU8JaZ^INt17Kp7mAk3Mp38|4 zlY)0_G-;`+KZ;j){?M@%YRrVA#q_AQe1+?6OD)}Id(UI&L*Rnsn7Cr+lIIj!5BI@} zoTW42%A!`FYY_6#+3~cxJza=93P(Y|^LNA}xN>KX!b7&&vqyC=74{g^`q}TotG$N}&EXKHW1Rv^{Jlsu zt56{HGhB;zWMR4aI2crroB~wlS(V79g9@-v~{GPjJjDxE)Ogp{x1}R;h<=lpzzFoTXb=R3w0hyv<1T5)c;Y zmAb3Um4V`UH^6Dsx~^zG(|l$d!t4siT*9x|+f%ePNrW}i#9mWORz?|X2JbrMdrksu z4H&yQv6dDi@RUwDvIqCb=DcRJfI=k`8hX_p62G?~h=q|dgl{j5VAVYxx~8oXTL2yz zpqkcjW?87oxtwVki{D!vqt{9fROmqmO~r!Gktrwk9>tj9s@toD@Gfrw>}QYs>Fxxo zltvCiuB0+eIJ8wezf03nkSldJniYC2fxQq%7^Yclcgkn>O2)Q?pK3M0yZMpN(p<=U zHJClO?fgOn)RDl2-?9c7C*Dk<4kJkdZXlS8AV=;@*%DItjQ4}c!p0f_J(_0*Ohd$CK30Gfm zUUEx3^C`LAoL$|=Gyp8&W^v$Kk@T<47Db>9?eonjQtU6C~*VpDrSbO&5Mk=uYL8VMQ zU8kn@v2#Uq9VWk*l`V@W$Co6V@3#25Tp+9LR2bC|U5wKn^cHT)aK1gU$QDP99-!GP z550o*1`C_WN7|UFp<9@X0$a4bQ5;5ctxYa@K1l(-JMl^jP>{-D;nlS9Mhdy}%O0w@ z*?L0spc-r9;DWcLn0CaxbhW2k_VeGn#Lx1BTxEq-5_r@ zdjhH7yHvw<+q-4p(UImBx<74O?`X9$)k-*t%~Nt6&^w?6>sJW(#xR#O{4Sx%>SL7Xj zvPC;MTu+s8-puUKIH@;60FZMzvOWn7J|2ie)7=t*GNo9}c)C!%&9${dp5m$RY|4=3wM?v_2IO{)MOj3Y5XY3$RwObR3CXJ<;#9SsGr7N^e3 z#}D5#ah8e_;(E?>2{R}P^+{{!7p=gIun)m0*~GaR^g!^1r)I73@}xwSp37=+tS6ow zbpp&)3M)1BP~EJT@)hA2H26EjhhCy7H1Nu?vt_p_wjS01n3tlcxlX>(@qQ1&Tbiv( zEn9gC8CCLBidmS)L1$Ku-A10hwhIetir7%svK1#>YhPmVhf)z*qGl7MlPy;7h$h`$ zDgC8F5eJV=XvJj&e^6^7KY*lF4+qB7u$^PVYL6C}yFWP)P3X}Uo1YuPjo z)`7QAN>$@M&17qFiLE=Q_g=#@#+rh7@J#wKnH&{$Zu#pQ@=%#XI)zl$U7=@W_GW<< zU%r`LQeA?DY+x%0QdlUBBM1J5%9R`(0U*)1^{i)ysT#&-%i^^pRW0qwaXEC(U}Pqx59#etDQc z;LBrYGU?VQ{mvfsgF=xd5}~2mrclcRAoVwguBX-uM59?R60Sm*JgHF6cPF`YDOQ8=wbDyw9r(nQMKV{ zHxKrjn-!W4gz=D~h!)*hBVW_7 z#u1l1Ab`8vp>EmnxhfYhiz(@oe`W7(fkm=yIA^r;#c zVWxOxN+Oot`WLPU{DrHR<3{Do0_h=7NCQTg0ddFTo3fN1!i$Y-4je;8<*Mvy1BG!X zMHxoq=eY@@{TO;K%*_3C(FW0HMS#>rE*=@M(clew#o9VjxlJq4@p9 zE5pMwYfAU5$cOpFRH$SdonCA*-JaA7K+&-jNNgM@O(6;^)TnlFBPoDX!}E78di^ZR zn|)=Fmdr%lLl5I-1)ev&49RCockFYyJjz@F>@3rjh%Df|TJ}({ON{9rK9UC>OxF*` zj#W;ZM_p^f+-IL%iKTY6T6e!V2!EJeSU3&{B)s_W&JqM39z0xqCVjQT40SN7XF!-i zmO~shug#Yvbh>%@uEht=-ox6F>LZSJ2=Ygb)&|%rHHp*F0v>Fn1+0oESA1JyFfz~V z?R!Jh6Jm{qJNj@rThfoIo|B(>B|Opx5<27Mtdw*lD?mjuiN-b#7dm-&Pj$^LA45VO zzufW(8Br;$@v-wMzT`GNIcK8PCWiN--`x7};9HzBmT^L=zNJT*(t=UjeaiJ5ToA(K zE!_$rUu>?L;^-*ACP*LT>hJ?-Q-e!%j`v=by(oTZz2XtReE#@BYzT;p?f}7-6Nnhj zyRLf~^H7u1OZVjqYaoCy>kL^J&fF&{;@IxY9;%3R*MQ`wX(h851{mWHS~14J;g!)m z^fA8#wW-cgRu{YRPAR>s9$VNqK&69k6R4se>jD5YvHNxE`nvkfu6f`kQbC9-6k=Hm zOR&m7l%~~lpQzKFf{I|*^bU^cB7WHId-&X-3ME7fjX`D**h)vZ+jwAjr{A15JD(-5ugdRn;USaAYlvlt)e5Q5k#?hqH%k*ZBHHB>OUEH>G$CM35PW|hMdfvik2>n*CzHPIOntHj+)NEF4y<29@v@LrQBw-Ye-Wbn}!jPc$)>k zD;CjyN)uh_bqFuF0v=bUZ+cxt^ZBYV^}+}>$EI%>-+Q4*>kl^8GMWNZ{sQ)438rz` z3o7KLo8Tq(wJS3Qd3vSm-YMV%7@*qPbN(uiQ0aIEO=@h-AL(JrXwA!*C~@Ov;|5ox zoqq9Tm$*jxCFIN66(wzc8&TKxHufy_eiq1v*H* zS;K`xu80cSxxjmdYxQj1v8fR}7M#?3Sr&#Q^VInjOQyh4UDx?71zwS{lo@o5iFZM| zwqNoKk2mp3R1FZ*x9+iA3P>C%O&SfX^kcNDd97J@LA<5a#-}q-59-*R9nsdDAUiy0 z8-1>@L`{*D?4n6r9@C%-qYoi=+9fCEs36WOb$QW8Q*_!dxe-LnMbH+6JhiU?w1gdg zxP}!x*E2!(FgI>V&X*6y$CJ!bQ^F7iD&1gC=qMpjbEm)!LSGX|9Y<>t32p{0T@+_j z%!OC238hSv_==?0bKyD+w|5xe8&dLYmJJ?8?^E`wl!WJl*lxvFG1sx(iM8_r8;0FU z3W)I06zSy~v{49`kpV?~@-DRCHJA9qtEh%%#KC}g@k-bpmqelLk|u@_KW}#(#g@w) z#MoLF)64)q^sB{a;m8_;11hWypU1Bkz^O|CWJ)kK5wc_j(qotTo&aTGj8X&Zv+R-P zm^D91fl%XI-`oJzcsGs+3u6MOC`tqF3*|PG7kX_by#a7kiHxp8D+a7Iw9jNHz+Xz0zB@sfDM_B2GJMOYgXi z#bvStVh+onB8alEC{t14HtSn-V85ovX01h;@I->Q=RFI|(>(5KN<`JH^ilE_TLU{N zSU0fF3+!R8sX$qv&;<+_(RputW)VtC;hFjKu-7=juZecvll_y}Zo8pO9rh=DyHdN}vWp`eKrcjA!~jU$GEK#a924?*2XnDDE2r#RaCO1m$Jx3lDCVxVBQ zfSFfmLZC}i-Rsz(<;8MDjtYx@em!++%=~beIuAvFd_#K?LeE9lLB{w=7ETJ0fivPavHnFYq1pi1j9SwA>G5hQTez`<9Lll1@%gFptCKoUL5@H#5G|K48ua#5j z9N*iD<{EjUoz{;{fIV+B=Emu2Rut0VQ4G8+IUc1a zzAE!{a~Xo&VI5(BNGQ^SB|B=JL^|8RuldPCNHaz!A+gFDgLSU;1c>NVHQ~l5Z2A}u z`8hosYa;81Pu@H?U$sORb@f)2KHyl?%c4t7dZ-}JkoxU9xG4p}wIkQN_w>#?oVAM| z6S4RizwCbD7eu?srcSSWBeRlyP@+X@diCkh!$Uugjxw;|tHY`0GaR=HwCxzo5n)MY zLkV(!qwfOy@@$nI^oVrb<@xGu_=o1<9Sq;79i%zZk6=NT$=)F#sacQqy_=@a=BLGQVZ3%Rd9VP zZYZa@PHHuO!SneQh@@T;)gj|0%j%pfg~Y|NWnj%3N?xi!gxEU-rT5<54nH^O?aO3( zH$;f%^gL=D4i4TczC;RTW_Q*V$)!rx{2@WqL_Q{o^*zTv{hVXD>=LT6nKw8J#}sWI z78^m=TBlg+B_BiZ{Hm(9&ttolPeXkfl;;fQ{AsccwBdVCOMtwd#Dk&Ovb2koyvUJ0 zW;B+{?Wh9e-o2A#tdz!HaJj8;sI2I-I70D-w`Z$wDf;=Cqo(ul87`c#mt8+paLIk1 zpl}!Ev;Fh}b7HF@rL^DkuJTirn8%dFPrdIiWY>^k~30iWcy2CKEgZE}f($ z&8lR{_R+3w;Q?*JJob`jw0jE4lFsEkERC)qWN>{Ac5gdm0g~=E09QJ8-{dF&*~^2m zcej0c$k?y+-Micj(DwpuNx{W>uz3f=uZB(DIwcd5-_Kj+l4MBLMWt7c4qWpNO^GDD z?!|SAjkgahEp4+T@8L@-Eyx~Sb@NK8+#L(uSC7M=OOCC(m~MhEvTJy)v(y`7f6uV^ zT}C|_b4dX5O6P+18%HFHC({tFv7j<+6GCqDepKstb$Xo)Pv@(h!pt$)d?ne#-qJv~ z^Pt#38-b@$Vly-h*N>Ul1+dDTW|{y+Lx~zMd_CSd!5{}JUo@*UV@CL0iDF-m)!RpQ z=Ao!hF=!OUCmqtlP2Ow3rkUpuNR8Qt{2r9c#_I(+qUSl#m>>~S`Y@}buht+tMP(zg zXP9{2`ONK1!@PMYOCnd{Tc4yL@)gVN!vHOCZXRh~c#HS%#2@aS@!96~#3- zzwO5)hcw;YaG4=RtLNM6%_xe*9dmV;W=g!0oFVcKQB9n=UmmQZ_FnhfO@1)e_Mi`+ z+-W{uVOoJBxvr^u34-^c3&@Y4eDqw1uxxr3U~E~+TqyLpfM?Nam5;laP^Weni>#t3 zH{E5cuXs?+&Q;EJy90U#l|(q0_}eJ29Hj>u?6*So!us5YV3@(f!DOEr-$1a&;7wK0 z6EMLeT=rVyqXJ$_WGB}fk_?EDk_<7_QOgq5GJnCwI_dmK%U|EI(>cvWdDq;Vpa>c` z5Hgu(dGC6-mk=}9VRDS6w5!=XIshe?-eA8Z8BpuUI9o-`0VyBmV8~ITX153AWFF}+ z^5PBH6C>%`jcKNb>t?TXavq{N$OGgbggTtIt&fO=86NY$w;njym+Lb_ZUiJ5~f+xYBvqT!!XC z(NKn+*+b>UVtWa#hTGkn%F}B1QeG0SU|Dcpvpme@WAyMPfp$=$MS4YcNxo$VHe7tG zZ%kbEYQ`Rva0Z2G*a_nmQ-E_0ziEx`wb$BcB(K>q0Vv0I5pTgvgp;dSvXeK)%iGr( z%vBo@%Q*T?E_yyufypglG0=az>y6@8Aj?NSZ3swvqK%^19<*mG) z(gz}>IMz{AFKPm^`;icI)zl<4-?)oT^)A}OM@edd;gjbNz%oi&P!MxDOtmd2DTLs; zGxOHol9TAwd&8u_FS^oR23za`w7$Qn6*x8Tr7&pcO~k3mdlB*^HE1q`)E+~=U_~{B zN~g+kl?#G+G`%VVhftD-^~}$-YC(FueNk78V1?|FUfnZEIqr?ZL6%5HMBa5jelatk ztN;wO^vs`FSaGe|UTkAV~a(UR04A05A z(YPLmxeFA~vWz)G^KB9FzFBCGUX22s+l^kzTF)zjSPBfzE^Q&*Yz`R75cH9>#m2Ng zd)z@{F{pIpN?Qv?v^2f)=5C!+C|RPSaUj(!84C_GSh6H+Yh0M~rZ6w88}|X(Ho46y zZQ?p^U>`q{A)uKorG;39;LCbsJ$*}+dZy5HXJZkR?@CP??98U5ax_4+YF-K?B=MYb z+RN(K`x*})uC2D|!4-j>j!p@;50W3Xml`|)1sW?zA(Y2L-9fv2=yEIu46?#qA$_gQan^|2=hD^*=)?E$0G5$Z87Us6zh}XD1cr|``9XmZ;O>}n z(^XywXoy|K*`ja7O@ST=GDdaVsAW&Z%Q$nPm?(M7xv%-U@0vlMfU_-;*AjHRA~Jvpo2SVTWrU#ANclf~AqC_h!$nQeL{F1((C#?zu?K6CmAX$7~byu5} zut_o-7jk*;G@s-Q(M!b{V3v4F(cq-;oUM+^4lEf_rm@}f;LL?Z-xm5^`V#L#EJfJ! zyWzvN0F3M9WpOIdw~hCdnF?Ia>8>J%A&O6f(ziA;`$#+yrKdRaleG~M%`8pFXR@P-hEHHUIvbaFVBmYwXnejTw(_x6JYI-O2*n+3`C; zR<0QG-txkzdwlR3I|irC(r?+M9tR1oWmliP! ziqz!`Hhol#+r_(4EvwO0#=0a(;W{trwH=FGtsHt_8v@qN%e3b$?Oct)CT~Iw%kS;3 zdV^pJS@46@`a_2~`(wO50Q?`fdEm6CbYLhKx8uhkE*t-ahkl;2|MxzUWQ8;AyH z&*amQ#PX2LMBPqB;C51XdDLFFXaObDal9?JA(cDN7Khww4S^pXuB`7ONHx>WvoS7Q zk#MfVhPc(lOMH8;LN7OWG$}Cz5()pFZ!taOskm_(96ew+FMN+H6JpQ96BO(o@neKN z;6hI4fW~-d0Uk>iBjyFFbwy2<$BRKxQMN4-EMo~^LFXzE zxMu*prR0Lh0@03YbB#pxUeB8|#e0@d?4`*`qh``Gtw#n)Dnzb$gi+;Bqx(;SXLAIA5YECWm4G`pMR9KdeQUgJ3!Ci<2bGY@B(A_u48hK|q6)PoMfO z&#NWQ8SRxa*hfBh3l99mtzQtn8P!w|xXTgOi^A6jKFjQn$$Wt)=>lg#NNgdmnM*38 z?ohh*Z1;h(lL61dl|#kLS(Rr`yhvz3p!V1+l2|)UMyYp$fQ%}d3-sp2P5R{UV7~&) zZW#fih+&=?cszs-w0POEKxu6~1#rsOX2D7F3Tqme`U#_ME9ZMbHxiKlD_bn0G+6GmjM`3azckqKf@%IQhmG*nS_3s5)so=X-tOtFS;=^O>aKVEt9uUxn z<4`z9s}AndiohXJ2_ybSTsFi!3vwI!o;Eu_*%I1`EUxOcl&n>Y4$GyfeX`z-!&0C# z`T7`LRa&fTm#d2Hk~`1*cvnSlo1WeBZdG0q=;9vLz%J(+*1$&5318r3&f#s{7(9B8 zqfg7;o32-9dWF37@(xdZ=S}sQbW}v^k^Vj7UMo^O$@CX!QA4Wsgg7^=B`#HS_zVL< zoph8&bQcIPREAZ;&4L`<1}uVoxZd4rmq-;Mtphn&LdEo2YbSMr^%Dzz9{4LU%_Omx z-|xhFy}q*y+o}+grwd3H*zy*k008!0`5Xo~<=Z@IXec3e)Kaao0`JmNA+Uk5cIHQU z2q{8yOmbax87fA;M-|3b5}>kMg&>A8B#%>^pQzv%zYe^_kVhhCW_@}=^Bzl9-`Y`P}Ux60xYcL)(?y%}5c~E8kUJeU}gfG!Nf1^=wLx_d4jI z#HipXss$z{)okFVJbSvS#R*}xYjCg^{AP$1o%eDR&?{z_nlGo~t!2DrAbTb8T-j0+ zVIC$t;n@aHc=4=bCEM_Xrr-ol2UJm`;jQ~)(>+1juBAbU5kN_-Q-w`u;-{dH-T7#p zRgu4J?yhW{0et6nEhi3}&_$YKMY{Ewra)exOo2M6z_y|h)#^wZv>+o65sAxL~>lM}s z>L~3xRb~VQ!X9^{m%+Vr4JXsrk zuLJOTt~_|}(UiRg3wm5^EPg$EpY?Y?*#q)KLNy~*Es<+p7 zy1I(#M;32`RV5Q2i?Td&YMZj{K5L;GsUA#AAGmEu8I5VfEA#a`@U|Rdf`(3^c33i= zs8)LNaG%L7BR?$$rM)&nVHK07FR%iKUvJx96h;Lg`YQuh-K1tl1;TtU%E^YFl0Jtq z%Jh7rTEmMNMMJ`yOnO)h-J(1N3bKG=_5y4sl-bY_*0vr$Y3;TUtkoPQuw!5sjdx<( z#W-rl=?u#uoj5(Uc!+e0=ZjAciRB6cBxZ6MNWhiaqV`(&Q3;fnC-;H_=R-0LtGV|c z&g+~_9F{mySvbnZ_vkSyoFk&C&cpO*|(*m-`aQJm3N8sou^oK4THD z#S)i+*U}=B9sCsM$aT2pMO&A{vz=F@R$%>Dm6SOKx}{)-`iSssxgT>R*G7cP(8#LQ z9Rgv|MJ<^UF*32}3Pc5 ztjOf0f)?cIAh-pdJ|uLN(gEdOq-EBIGK_T5LS>ugN_>heAQSa6kkRDXLLtI{s|h@k zY)=g3@_brZft88KIK8ffYKgl%nShg8Z`)1sLZ6a)PlU{b!(1twHdNP)^8;4O6s@aG z4SX=ePZ^fpO!GvsMhaodd(#=~iDCGlUxvVY@ixTMtfww6-gRmalD`h!ecrYk)OKKw zxuMkR2?ofct@QG}=ZT3ys6mqaC?gElPM>N{8Q@;?Fas~p91C5fR^;}gXDYU6XOTED z#PS+rK`&bCojdM3fjD`3e(Ff-YnYq*Oik@%IOZO`XcaTTh*EFo=P@{(Oqz`_Ou%}h zxeGMBaO{<|*W2e0A6QU?I^ZgoqlFQ2(oRYdg^EDp+vjVO_w?c@tvniMAVVZzsmvIq zUp}eWEsxrW_udo15%MRuPbYBb2;`LX8eTVFJlExMl+*G;@{Gop$M9U`dET(23hcS- zyqU5BGa{j~w{gs4PShR2BDaX%ItcJ?iSGfIztuKn3?V!0ZCU9QI}1b+9u$-W(iM9X z!*z0J@{~<-kZa00p!dmJ+_^4zKGozH!#np#M*FeFQNIFOwQqL)i`{vv(iPN~7OHfb z$MHr(6Ri6lW#)A69q8IypE$jMAc$2pl+g`yb8n8l3UDhl*uIzPIy5|(pppxF?LmA` zWz@7@S1i?bpx=ZJ6u;T^XJj22uTHd+idG`0(nUjsH5~=it}V^sg;ER$`=Hob!8qw) zov$N3t1Nqj59ub8M+lAi!hK3_4p=w_ZaQDQ(HCtLd4{^tUX|>B!Fwn#?~KFy=^NlX zuPqGR7wkf1al-1jKJX5>9#+FM1gh)dwK0f%Zt2lBgT&W>K_#wdIc6gLG<{^QOJ`g} z4JjNz_nyU2R}E6PGzhvHTE1|R;KR7$Y^7Fvy{+?%5t$6OAFVe8Yd?6Lo88yX%UH(5 zEzp@Fq;?7-+03g)fHM^PW*>nYd5I0NW>rT_>!YL>nkn@-Z20hkuc!l35>Ifh?=mYD zp^dEVd_5^Kt9$gxu*PTS9zl{EWehH`xqFT7VzobZ<41Qn50l zY$xqgrB0jczI~2jhf+_~i3m@n^is$VsR;y`E;&s1V9%`sbu~%KF zYt3wr@)_Lh%j0Pl8laz4wQ%yn5odO(^D-vSgizzQopzh=3B;pk2RqoQy-JBhnAdIq z1O=UhG4NKhZ;vEsP6G8d!%%pY*d?FuCC?GOT?a2D4Fcs>h@;qq)=&_QMIa=~Hr|Mg zpnKMs5RegMvsn@T;4K@wEd_!pq>cioy*3|J!k04CKDAu)Ghu5 zSY9a6*cqS@JHJ&IS6 z&6T+}C#`D)xM*i+S)z;7Ic!?Xq2M_*0uN$s*oVev`BFUl^=;nnvNvSxwmXOK+iSrS zo(C{aJ~Q1F0|hz*gh|mST~Z{Vfl^zZh!I|1?z(VMmJS;>foTIs1{Z0D_7}awjL(OJ zF>ZRRjNd~ZQk$1W{-Wmr?G)%N>}^T;v%?tcuJyzt3L<`X&07*!MQ@iR51<;W-nAaT z5#o_@X3I;q2~}n|B^Z`|y;kgW-DB#@Pjh#rNjCbp-;E;&M=2IV3U|7ilT=cQtoBkKv}{z* zM(w()lV&}~@AnE2fsr1owo={)WVHg z)8<)t=_xQetXm*94OhUv$yd9)l9(zFAG%1wo1*J}aT-3zLw*ze6dj$^mWZ-C-M8S8 z9;?wbK9D>xS|}4Wme5pG2AqqOH_RjNQYg}zyw^t3u*2NJNJcQl6IAdiJf41IpodS{ zQ$34Sf5{jbxP&|A!THs%nd>FKA$!iAuS&$Lx+LNO7n& zsj)7Pt6Ommr-3S4^MQw3`w$dV#Li#3T_G5h`U{R&o~N)hPZf*>+6b~djZQx4CH|sU zn+p;QL+q^>v8&>S-_|}-F}z6ZbbRylxjf7zFLs5a*DqgsHZ}>s<5Y#EF3D26{(=@zcG%`i8Htw1K zi7&hzdl9SGOq{392W}|E5S7C8tgpJc$dV?^5fA(F{MHlKsEj%q`WNi+n3z|r?6#9H zo9#_Mk|j2~8{~U7$+`MGsgM`pEoH~j1*g|?6!{b*^U|KD+GC_5gJ zOd|YJFc7NimC!~(%8^v20pyzna~iHs`F*|Gdb!9;_&oYnXK8~&7?&H9!Q*q1p><(+HmbyWr0*gsdN(1XG%o3qLZw8 z>BeASho;|AhL>dTbbd#e2V2-yNKZ> ztfYe=cOXUa9(dZM#|uA!vZn)#`0n|>Er(Zmpa&d7V2h>uIxz4>rDpJ6m~x=tBP&vl zvs~&pH}j17HL^No7Zj4UYLoLl#*xbD z>*x51@1rk#1-V4y1xCg&% zu{!|KfFp`Lro4BhJ@V8j%dVtezUo@GTD*BlWZt=+a--PD@14yPP?H%#hi9yv(<#RI z-~b5#h+7{+7Iage@ew^%jeEQS5PP&0?DR&d9x~W*CF^10ZZj(5I**x=mBbord8%RU z#nS_+bpn&P{EJ^{a)eha)a2r^t>v5Yw%9Z>1N+L;PuwJx0BiZ+T}NVP`ifH`-nDkz za96&QeMcx9g4}h^2BJQfw7o=(j^!###ODXrnVjH()2Xn#W8F9OQpp~AP~zUB5foIZ zZg!Ul%Nh5R7(n8Zx5#RC-h6jqu@T{Ex|RhOI4dafVt@@ngrK_$SXQhs;y(T zbUVg#eH*uUWpv5)4#)R}*udQ6-#f+an%Q}jH*4caEb5zXBu&+r{CzivlutuL= zlW7+K0EF~0W97Rf_*-q?nbx#V5w2>Z**7?FJ% zq9S+e7DINYeS7f8O?lkTTu-Zwdl-tn1!|DVt5-5Zlg&1P%3#pmdv|Dhv<9VHRQ1a*wKo@SfwLlM(Xqh_NPvh=cNQtg!P4Gt(I(*OAPy20D%qvr?A5ig}nuF?9tx z35s%c%i$UmKA-$DeDUi~A|~F6D6SLM045tf*J`@sy&BFPg%KxQ=gc)82ScE6gp(!L z^Fvd?90(sE@QSc?2j0_b8_j=yev z6qFBTN<`iRX(gY5KxCD$zW|0ze2TiU3(fuHmMRDlz15JVoW)nsf*-CU%cN5z?TwWC z!vZyiVE-l5RmyubG~}wNusIj63y_7OoayB1Z8A)sHR>IO6pFd)qd4Nq^DUX58UffF1 zx;{g&?p~}nv|J6(GE2DL`P_ zlHxAgGhx}XXK@En${bJ6x5G>}4$%`sd~I3Xnn0L%IUhE7rDL2rnF{X?vEXSEF`Z*( zy^P$sG~{P1+2mf89RgBd`+%ya5aOm|sZ1U}%2ol&cX_cbTsMimFG3Ung$UvG2rT5!0L*Ri$d0HZ*n-ME9AKgWy5BR0J6em zm#V%-eDx0H*5@2lJL+Cobk&>c59F_$bHt-l#8jPD?wkke{hnU{ABhl^4&4RuTJ=Eq|*jcAqcm6i(r312y zzyo^-Au#Ybm0UYs;`Vw_tUi~PeO0>YX$@C8`X22gSi^4KVx^>1TB=*{gYKMot5`cF zGf?K5MxLdr2eu%lgB~4J7gq$+$&pR-LUh=k%hF#2FIi!~O0sG8BdAc2=DS-sMYs!y zVy7W|{kS*|t_wJW?4YoDs5MJFs#@2kH1up4FVRf~JXMx(fF;mixjIDFRzfoY+fN9| z*2UFrx^!We;b8Yjx0!&Eiys?>IWN|k3C$3!sp@ZS2=8dxC> zGl8$NCNtg|69sJCnLlGLI8n0R#R{C|hjY_oxzjzhz`Hp*t$VT20{HOuS*d~ToF@?0 zo2V{0rL6bx__afE+?$acw*KWVsaR4FZu^PX5Mt5QftF5hIpxEE8ptAV>%6{7=0^zB z`9O^T<-JiJ=IQm+M@_)p+3H2?2RA*!weL7Sjk4{`ozo?+Ji`F>Y*8L z?P#4d`W_^bfL9etF;2tLuA~P9N>0(h>5}gd*shn#Q$Q)xmkNm)+7T?d#fqx<61MV{ zTLWF;Bwl2q#vZ41PpGXU*7+MU@^Y_|K1#W51p<85s=mudxVbuH5(mOrC=wUMxJ#wd z_cQ~{2-2o|RpjzYRvvH8qhw>rV5`*TA|Qqtyk__$SoDkFPSbgbBq8Z&>ZE7@>g;MF zZ1dJnpotJJ@&dGBtyiF!70~t8fW<1o>)sK zjcve7fUNUWbL+Xh+KJhyy$#XG_RP&^8CQlc5);!#+SnB&efEmNTw`lz3`bE?Ch2kL zATDB76_OT#UP79M?b2vA_OgoaWw*WHS&U3e^6r{_)`tW*o94w$hh+1@vSg>gQ$WtB4N6hXbHQ=(#hK#N zh%wGV&j62P07^rugvr_z#6(0~nLXmCx<^!{@5oFq()=aq>wPHx23+A@1*R&&L&{Eg zn@#<^QmLck5(*TO&%5DzIv0nLAHAbdj`WRl@GGFH3(`rtdD_a`-M|5{^uUK2o?3#& zjPlal+>WJWFb1M zV4GJ^9QK5D-K8QL_HAO15uaC|=->bhX1?-?Vz*FUv-cjT%!NV2pd57%EyHJRcSlNt@t!i7P&B|G(sb!>&Cf){2L}06@ zw9C(T^pe#Wyc2lYTiz46LL{g47{+|ER}b5v(5sG@ym!US!Yhn4$$(hy$z{r$B$=*Q zW=Urn=Ql}s7$fMauSPT^qVbRmYf*H^T{QCn^~sp3FO@alEMVS zt6FbFz{VWW>@hlwspy!gSyC$k!?fj!ItF_*41(6LQY{?7N`j~_zvp1(FNDvuT&C5S z5J*OO<3Z@)7*bi~*%bg9>H*DrMX1o!?$i%q4_}IDVbeU=^E2P`p<@*QLx{|Ycg9_@ zHdcGqApz@ouSl&u8fA)})*@uUuAcF0tG{ZZz^PWN*z5h1`= zi?-N~{YbkP?@snn5r<3C5)u{|1yBuLYcB|!X4lb>VIX-H_LGaZ=R^T1DohE?MIlMr zF3XkY<-+W!2bIkxZy2Pb7btHR>LHbEdk#{9)x1Ho<7&&>(}kd+*EY;@UW8K;9jWUW zd67387+E5)n5qn+HWWHlrb;Cvn&p&2S*O1fYq{IR!3nL4EehoBv4=|O zrC2V6IDn8>p0~5U8Ortw95#H*k`4garEYa`ZFOm;F#y-B6{cRQG@Yziu2DfJSC%eq zGn~@Bx2|%)5`L<;$nfG8Ud!CGco>Y28ShOgj!~?L`VG z=9D7=zls{b+bCY)sNL8H96tpQWLUCyhS4hO*%2}$o=Gv;(nc?G>2&gCLAA#Nz%EM= zibf_}DI?6N3wSuF+}eskSI?U$+J)G-D`zx_yFD3Dm8Q_%*mYb3kavKHu>)Gola*S(H6!r@EmH7j#S2#=?O$DKSGLFw3ad!3YnLH3_vny&2sIyo)Yxd!wl_(iw z3r~cQi5hr&#yx1biZb(@g(Ff}>ojsmUt>8-ft1widh`T)0k;q;&M8( zA)MF(N=!gq(pxP`*gR?*Mlb6eR`~fv?ZBi@i`ly(Z0@6ni>R+2jGCr;CGc5m-AZPX zgQZY9FAkE6Y+X^!G56yfXQg;!xUWD4t#KHEM%v8e9t^&73TThHbJxN_QhzL{H!r)L zy~2Hmn-5s)MJLqJp(VI!$)-7>!fc%+8AQ$VYdEcYgG;Vu`8Xx==D18Ga*N)B2^Q08 zggO}{w%tN17C~(yw^4fz4x(`zXi@%>>;MexF?riV%?{(K3157+W_WGKJ6LAXuhoo` z@@YzE4VJ~GeuM)Np;%q?`&blgBILKI)Xf^NVIZSFh-%)@atb~GHY5(~cc zhuIyR#6qUg{s>;tIZIx+wPV!)Wd-rcih&xy-ubMJSr319*3O+@Hh?COlC z^oo=>2wre5dlmOfsS3TT_Jfso50)rIbF!LD<;1Q`aglAi&P7e$o6;3%PdpEP2pPO{ z4m#tVny|>&aA_TvtF5^zhu-nhg<^H3L|&c9k<`*UWVln!vqP5KuB|uBmalnPn^1_m zMS4c*gfJ^8z0#;WEJB;3itJ%{Sz*WiMKM^9FB}HykV+bR`es)<7O)HeUn1~j;yx3q z$is(E?G8Ha^i8p{L#Z``Y@=>b5Uql$0eVH&1YR!?`&WVwRV4TRmT#-QQS?jrgcs~?+Pbff*dO3XI2j%k(5AUapWDioJZyo&8y8FWNLAF zsER_46Y$4})vEdnzVYBRuL;reR z(ky(vV>inniL)bdE(+@N6eBl-%wv5Y)~Zc49RuCW_S?4ta+Uc)t4rrvTyZ&UPfTwa zQ1*;pHIKy8^Aut$Q?-1AiYzZ`c&=_N?o|h@b}u-uXKF`IqctrW@bc1@TTJn2TB;Iu z80`x8iqk!Y*QRyvRw3t*hj5giBkOjM+AAJ1bCSbKL21{=(oGIGstKU^+}OQ( zBPqfvja3TMM-uM5(T{)^JQH@`ozBkK6FGSM9=#g| z*Tn#SQ+T#x7kPOd>SYh#ts02h>wfs$;>iUyX&y2RWma32>4oWbQR?Xo4y?*dgqc} zRXpFI6x)#3<&cG9q-Oe@+IVV8E_YM(n_~Z>n3shrnUHL{Lv)YN@5zjM7m-}qE2*}{ zZAlNWN{WY~Z;dbSUa39gBgSox<1|p*R|AAE*GVhuIUFAHWtJE0&5Yb&700vZ@)G8>hAd9!T5N6|?&y9bQH>I*U>C}Q31(2$`RP}R+2wtYv~sx`;1EHO-W;mQc- z{?Z|(x*s*c0h?Dvh9#0-%Z0GuG8_dZD{?#-5N;RD9>D0nzc5Da%m8X&CLXy;=6X4k zk00soozmzN-nSNXyf9v&Zg@QdJjuHyPs}lX}M@G+H(Uo zd1*Tj&xvaNEid>{zI!u5!!_p+FgXDUC>!XUkATA1!$wGsUh?J*rl^!DBaWB(oUrFm z?LL2SnfcaphNfoOf}ZBk9Fj9Ck(+Jtu7@P#g+35k!^b0x_^2G4r%Tx6d;2DKe@zeS>!CJRGY-?Pl6G-*3Cubff2 zMXkt%_L7wH3gS3cpnQtVcL-%~@!9<@e%p3${ zT_O?8uKX7FTAA9~&iFCTa`=y_a3O1(uC z;vKpi*Rpr-*sG-isqZ1VHitiuGVAuU5x&PS;<9mgeCeWwb2d2_ zk$SfqmM<$JPz_I%0z-hq9XO2LTrIVkwG_bZvLoT>x_w^}Z`d+DmWo$$HctbcV%~Z8 zaMe>rESGC~HvG#(}OP}_7&2b@qt^w!!Twx8jsE{pEdK2oM*WS2g z=gCvzC!5<{Lib$Js9ERkS3|62&=Cyd^ydsC=c)5V&IO$12h$2p=tX>0Ntd`(wSrY(7xGP+it#=gGHvV^dRiZ0b)m^BMh1wnCK zEqJk}$E=SG-BatasEj>7%pje7wWxi%HF{@f?l+ay5Q-@N2={T564>@)BGK9#xlog& z>!M{hyaslYtEs5pHEz{St>KLFtJEr5>OR)N#<`f*1~Xer45!yPBB|`wt!aNCYf<$V1Jk&gp-b6%XYRz^h z;PTb&d#=#-gJepWk*0!dj>bcWJkm`9pIOk`sXiLYCE#4$bSccpp{69Gn94W0dRa;u zv zO;}{BXKI$g7r3?WkY6d+0sv4$J3Z zm9& z+=A1Kpo_F7F~UXgu-g~bG;2=YB)+HMPna_c+b$)q%Zlv!*(%T6I+^>sgy&Jd=Axz@ z^g{bBHZCeInW;SQTP|G!m3LyG2akhd6M%xWp{{hIAG%mo?Fn{5@MUURM{7Nv2|}kO z%w!9C9pIM$igvMquv>Rj`!uw)YS?3p1i={&OqRGEPhSd|QF+Nd3T-=skm4PCbTZw0 z`Y4|rFvc_AITf5EH$aT!!!77g%(bAgDZFj>N~Bt3AyD@0*XCmn_<@wkyuwUnr)raC|eaY@Pdr#{@K4p?-ihaYC>$vTNC0@FVEmzSo z@giO~_G1h}3$w6!(Jl1mQMhfvm^7>aozNcM)tg303{!1FG$aCqOrv?5w{~-plj^7U@c4#AFy8)vSScG|ADA+qhNQ zA5^RN({sC6F$beuwNz+&sabG93!fk(ea^=^(p1{DRxrAJBG_QFIyVaDE=aO5U5Td*&1n++ec8N zZjif!_G%iTfJq+~F7)lxqgjby+j_!4SDS@mpj55vvCx(4UagJ+C3uI&vP0t~u~!NW&oY)K{9YbLbdfJ_YQ5TuS%);B z_A+RAQIE<-UwcsM6qepf#*#i?f6U95&_^f)Pn+k6SE9jV-wp1k<_aMsOnhe%Uem;9;z*SFyfh%p)ssU!og)unvy+qKY zN|ShS4^F|NrT|5KS@}t));aX%y@PS{6on2A(nmn)n;`&Y_%u4i?b*0gzq;anER<~B zk(E3eVvrk2%+c88+{t!C%7#quAWK+KS-k1(!#OyIi+o$p3f6eChKsBQXG%Z?I_L&4 zCujKuvm^))>$_K=zHlDU?@e4(aX(CyU;zh1>u%^NLyRcvQ?R@_*-w%QzDVwKQ7Wt* z4-Xo(naq2$Tja#bq8mn!bgHMM?_H5cb^@;Yq>O}z?n_0LCthG!4|p`|4WI{etpLZ! z!cvuEWS?3pfFdGzteP4=8lxGP+L^ARQRdJo=ne{zEN-R8hr3C9G=l8!XbECjJnBL) zE?cm8Kye)qNmfD5Azqb6ST*V!5tPtd&a%E-zlsbv4F`7Ug(iv{R1=1%G504tc@LEs z`WEuyZT*EZ#i2+HX1|q_(R=DbtCyg;Mo{tM-Y)VT)f4WhU`##*fB0@tg^XR7r={W1 z0GU&mg>^|_Io!j5Z8*FHKerT$Euti1Tf+mn;=&z9vRcAed*xzukxTeQ$D3HLUJlQ` zm&Q+?&bZK#8A-@c=g7JQc_~KtgH7|%0U})+>gy+Yj9VoEgtClEfGIu^kj{=I#Fm); zF!aHc-q^_<_Yp^2@L>Zm=5?5IQ_WGqWoKBujPnz4R06XrLgC08fskxC8e;~P3aIE# zb5sVxS1$;PoLh5s!_A&*Hfu;>RNJiky;a0_s~ouI4~l!vc;2GH*bA5_V0b~Vy_5~N zWc0<70nh8FaJ1S$6L zBm-ONuu=p>1A8VTd2oBkX1A#{&tDVS(iHbdl*_WDW3ni%3F9<5^3e}UAn){qjYfX1 zsqBx{!>4AS(M~i(G9=~cLbfp0LRMut7tLtWO?GTnmABi8&xBN^O@%VNiW(y0r^YYL z1TiLUtGt@^Dk%lpgoXFH_A`!wr-7#ivIaSZ3L#w&9%Zlpy@wO^UUTvz zHGhMhxmbK)ps;sgzHF>@(6$eH6oH{ew#itF!u4(0A-t_gMQ*0Rbdb{Ic0#5HqMRUN z)rLR|HE)NWVUW-nKN;mAgk8$K7MI$9cgeUI>Md|1BTp?z=PW_?B~u^Rs_0$Nti6J# zeO?$I-q7_}#2Ol@X}p{_5FPVat=c4G@Gq9z-uPvf3>1$l)Cf2&bHC0GTSaBBg#<1{ zHR9FNQZaQ_RUO|Qqd}5cg0I=7^z+#J~y~B8kK>Ktp!h)vYZ6rfm zM0Ey|Qm`+Yf`H1^>GLawM6qJ&vp`IfH!uV;=WM7chDfzp%%=o+XOcM!l~*ai#?rU+ zmRDib_C-7;${l-&Gn%YY!}4ZcOQj9<+&i2X&otGV-ipXc_K{RCuE;$w5~|2-YRDJu zE^W(v1l;9>4f8CCW?vfKOALrYEJMd;s*N(wgn>k1=6eqSOoXei8^&+kM)~1YyteM= zrj{b}V4kpuLd|oS^5%w}j2Ch0=fcWNs*PnKUSzN)Wr2?~0oSGvS8qxua%F(J*L!{0 zflW3Pt(z$W8+;;9R}io~Tl{fOQLBdAF{LS|A=hrU`aBUkHnENkD@!lF8yt z)%hhvE7>GSE?->7>;^@H4nQz!Lr&v_TaS_?L3Sl~yPeS~f~)mQ%OKB}@GR7H{g%WK zEQ?MW9@G#!>>G4_qO^De7C;W~y`31Dk(B;q`RNzSFg_5ugK-G>LZesKwt~)I5=5?P z?ZY-igVw3bu-zlqog^rKPy8fe z2$-u5#NTE4*e`OokP?di=t0Y#uP~FEz~)gmFU7o?foE%v$MrSUgn1M<@qg&9w?XCR>^`4b&21kN0XH(w59D_Zc|w6^UodmD7k1IGSbav55lO-N|1tbAlZ0 zVdlskKTxLFhQk16FuPK&TbyozGwyf5vXRkmmh+W_zT7jz0uDqhD^`XjD6DkevAo?! zB8J)b*!CgbdyT_{M>whvRGKGeYS3I-CL2bCd#5m2^3jqb%Z4Av?2=2pH;dfw=^%u_ zc0UN!vRGGqcUxsZ*A0-^Oae2{C9C9FlJf?F4{GUwA1}f?YS~z6x9AcGk;f>wuOlDo zfKIQxvI8t8o@Y{`e5?LqaPR<Rfa|!nZn2UVD@5$T6## z$Eh%^D7B+Dh!%Q^_WDg9zJO=-aJM6!f#<*^teRBkwM}eAT4$YcTrn@h80)~sTuFP? z7RkOtMH+bXwxCgXmWvF$`8+*LY0>bES)|dwRQxuA(H!OL9d&+;zS{2 zUpI#Gv*+YC2_WQj@7n3&1Txj}=tOSOAv%^`zLZ$OruE=6^k;N&iK1L=S?~BA7+a$G zi;ef5MyZ*DyhT$!p_Y3X`(AsIsQDSaNBdrPhPC=(EQqf`KV*bCp0x!>jZp|KE8b)$ z!!32b-MID=#eTO-)9Y^_sF10)kFK z^B2th=xN!TEmNhscW*Mq51-W&uT%t4*fSO}myFt*f+q>88(_c}6g8n9y$8LoCMElr z9jdo{AO@izJjVrKq7)40`XzFnwwWM~YTIC=1P5)_jlj#(prxRE|K!2XAow*V{~5A12RIl zb&-l=5SO=Fz8G;}c(84bwLaGr8;{(N=oH`qu}Lqg;Jhw1OwcEl`6l*!p9bCUaR7&F^5^i@GE_JC)K ztBp?}v}zFH+9Q5XL0L~l-e4z4I~U#i2kb z*dfhR92D5VkWPWW1;m+^za&8F%Uk*rVD_vFcNlOiNN1vER;3RP_pp)I(G!Teo zS*!wMx_5-5_VV#YZUklBalW^AtJu_*Jp-lkOsvCg8c$5h&~YscsGah86wIum&#df* zC8nS}l>xrT`0DOqzl$THx=I4Yh}R}cnP!S;aw9nGwRs!7sM(LvqHonL@GeK+6L*&; zkA}gKj+kaDn_cmV9SP|Z6dxV|kuKtZUyw62AkjNmVr(4|%RTeikk)ffs{z`e*Oa|8 zwGUJ8(X;MMGaor?!_Lq(TErlDW^FFH8S^g(SsW@3s7$CyR$3)04Em>MbJ zn$7LT0BtN4je%p6Ux`_u#-6cJP1OYBbU9ty%WRhP4DL9pd2DuyH?J)8fD+3|ISIM- zHjT?bu!yvc2qL!4{Us}vztX%J1_)v=PkMF=aw`dbYvaZ82$^uJQmw`yp#Wy;f-AG^ zp2W!DZX(E$Phb7TbAVs#yWLpCVP31=>}pgbjSP)=gMg%RPExLoV6E6Uh+FykrXc)P zN99Y$nHm?gp?&xwEW4IH*>aD)UNxrZZqsFxLx)b}l!z7bd)(S0a?d#P;zAT0Wy{EX zsvXmO-!vn0xdmeMLr5Zjt|Z0-B0~k^BabkeE!LY(C;Opmf+3tPCE$U%%tw{c%HYIZ-g-M;x#?jD}+_2tt7X5CRU!@dlK%NnXqyO9?s>J zZ8sjchpC`Xi)xmyud_ll2xN^EuZ=tn@G$mSV9E0;%=JZnk}hZKXBCpkGGvlx1~+l% zqNl3bF5ybrCCtEA`J{y2iEM}A87#s=To$DJzUag?bSQQTmv!3gaqJpuSCjJ^Mh)z% zd_m;Hwy!)x}b5WvYU= zY4S94CCm{w>Nu@f-p)R}uwqR)*b_`JOsW^?bEt(vf|2{o^x|+Qi<|P9^`oE-W$-64 zPw_eP$kvMo0OW5Z=P_VD^g_BpHZE7Bqbh+bO|Mv>m%?K+OWyoO$6dI6Vh}~*Nn?Z~ zPF=fgEP&AlY`Lprw+V$IpI>=W$GD4H&Q%!i5=cg!m9zf^G&uAN+67J+-diBJ)Fz;( zYmF2D?saw>@%4 zu7d*!v;p#v!jJd?JEyA60un9^)C*FJoIv%u;)4N#wQbC80Ih-b6u~OngEGMZFgZnO zAT#OkcPxaod@p!S9VX6^g!y4z#hTF`bqXuM(BbhE(&!XNRpcDJO6@>eNUzHxXgHoX zsU{iVWF|!Ip`5)ShtCIt%98fRZ&lwF7qRPmAtIUBPbSgFJpwu2i^FU-5djA!>z8{b zlkunu^xeG4E?oeOwwV?sGgA_P_g0T>OX5L*T{j+I^QL$AX*b-j?AhhLfcSxh?veF>9u=z_l*kp#{OC-ewxg@mB(SuYYvRb>$8_yKD0F z9iua6)g>#SCS2VdjR#gZJ<)n7(`XY{N`kfz^<})w14*QBpiRGkE`*jadi$nxM#p5% zZ6Hw`)*il>Q8oZwA=?-y!UklIUabj=7vedTE;6;Ixjb~&31BPm6~Q%GCB?Ncm!~=g z#5L9veUE6O)<*0~6+z>O9owo*qQ|hfo_10-&FQ>+0^WU|43JybaQ;-B;J?wc@^Qb`(R1 zwH~wRg>w2A(JtrId6;i>%GX+ZI9kJ|A98M{#e=x*%qF~J)fch*L#2e-&p7-`vEa2kOz~*2q!rL01t=rNh{Y~bcG-_#4XFgD0zd4J#!2as^>;rCHWZV0ZZ{Emsc871>2Tt zMg|fldwE~^E5;5;OEpTas6HCZN2J<*__kT+cpRQ{BU*~0!wVlG^>%>h6W6RNW1&F2 zvDeX$IRH2jbBD1+EZ-Y*l4cT|Op^hfvu=ee zxTSQsVqq-rjFFgL7PIFW^TtmZ^Ss}UPNLE@>NQ|y3t4gki4+e#Y^W7D=7T9H!AO ze)(pGD6`sfiq)b0E*AfWyd|}g73a3h@})j9dfP{c72-2aX5}&Oj@Y1Q zS-@=8dOJ=+af*P!JbZOqfK7*T5kzIyjytE5_} zA~GJ2klipF@T>|y90z@KB+K`%JI_e4 z#w)fO@-=H46-YKuF|P}#JK`P)95B`5+O{)J``H$yO$;@{)>C*FLi@nTqfsBXO{}u_ z(=xe-n}KaeGFvS*?+nhmGu{Js&MHZJLJ#mw3)tedsE4zUhRfM=)g9#Dg&tvb_&lj5 zCeh{##D2rQ{LI4n;i<1}#wqw(P59WH2Z>{mi6Ur9n*^Npdx%=rq%oWi5Dp1*3kpG1 z^JcaNJumLWkQjGgEFY~oP`Al!jCN9p6jBt>MFTzV<>{ zW}y_W0UC{cz-9MHQ8Q-(CTK z4le=G1Nx}Zbr)qlHh^Io+ma-4?dOg3gs9 zvD&9;NW2INjRtr57t>hzR@AA45Sj$uQ|k-52CCHCOKpCAMl!LGk`{WwQKujs3^+

;TQ8)#7l>4Snz*J)FMZ0Q-2>;ACWq)?04 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 z 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

nJLYnt<$PQi86UnxvK+ceSbB#mC5$ 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 diff --git a/crypto/sha3/xor.go b/crypto/sha3/xor.go deleted file mode 100644 index 46a0d63a6dfe..000000000000 --- a/crypto/sha3/xor.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !amd64,!386,!ppc64le appengine - -package sha3 - -var ( - xorIn = xorInGeneric - copyOut = copyOutGeneric - xorInUnaligned = xorInGeneric - copyOutUnaligned = copyOutGeneric -) - -const xorImplementationUnaligned = "generic" diff --git a/crypto/sha3/xor_generic.go b/crypto/sha3/xor_generic.go deleted file mode 100644 index fd35f02ef6e0..000000000000 --- a/crypto/sha3/xor_generic.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sha3 - -import "encoding/binary" - -// xorInGeneric xors the bytes in buf into the state; it -// makes no non-portable assumptions about memory layout -// or alignment. -func xorInGeneric(d *state, buf []byte) { - n := len(buf) / 8 - - for i := 0; i < n; i++ { - a := binary.LittleEndian.Uint64(buf) - d.a[i] ^= a - buf = buf[8:] - } -} - -// copyOutGeneric copies ulint64s to a byte buffer. -func copyOutGeneric(d *state, b []byte) { - for i := 0; len(b) >= 8; i++ { - binary.LittleEndian.PutUint64(b, d.a[i]) - b = b[8:] - } -} diff --git a/crypto/sha3/xor_unaligned.go b/crypto/sha3/xor_unaligned.go deleted file mode 100644 index 929a486a79d6..000000000000 --- a/crypto/sha3/xor_unaligned.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build amd64 386 ppc64le -// +build !appengine - -package sha3 - -import "unsafe" - -func xorInUnaligned(d *state, buf []byte) { - bw := (*[maxRate / 8]uint64)(unsafe.Pointer(&buf[0])) - n := len(buf) - if n >= 72 { - d.a[0] ^= bw[0] - d.a[1] ^= bw[1] - d.a[2] ^= bw[2] - d.a[3] ^= bw[3] - d.a[4] ^= bw[4] - d.a[5] ^= bw[5] - d.a[6] ^= bw[6] - d.a[7] ^= bw[7] - d.a[8] ^= bw[8] - } - if n >= 104 { - d.a[9] ^= bw[9] - d.a[10] ^= bw[10] - d.a[11] ^= bw[11] - d.a[12] ^= bw[12] - } - if n >= 136 { - d.a[13] ^= bw[13] - d.a[14] ^= bw[14] - d.a[15] ^= bw[15] - d.a[16] ^= bw[16] - } - if n >= 144 { - d.a[17] ^= bw[17] - } - if n >= 168 { - d.a[18] ^= bw[18] - d.a[19] ^= bw[19] - d.a[20] ^= bw[20] - } -} - -func copyOutUnaligned(d *state, buf []byte) { - ab := (*[maxRate]uint8)(unsafe.Pointer(&d.a[0])) - copy(buf, ab[:]) -} - -var ( - xorIn = xorInUnaligned - copyOut = copyOutUnaligned -) - -const xorImplementationUnaligned = "unaligned" diff --git a/eth/downloader/statesync.go b/eth/downloader/statesync.go index d27092e02fce..608546873dc1 100644 --- a/eth/downloader/statesync.go +++ b/eth/downloader/statesync.go @@ -25,11 +25,11 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/state" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/ethdb/memorydb" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/trie" + "golang.org/x/crypto/sha3" ) // stateReq represents a batch of state fetch requests groupped together into @@ -242,7 +242,7 @@ func newStateSync(d *Downloader, root common.Hash) *stateSync { return &stateSync{ d: d, sched: state.NewStateSync(root, d.stateDB, trie.NewSyncBloom(1, memorydb.New())), - keccak: sha3.NewKeccak256(), + keccak: sha3.NewLegacyKeccak256(), tasks: make(map[common.Hash]*stateTask), deliver: make(chan *stateReq), cancel: make(chan struct{}), diff --git a/p2p/discv5/net.go b/p2p/discv5/net.go index 6c5c0b11743e..13e9cca70ea7 100644 --- a/p2p/discv5/net.go +++ b/p2p/discv5/net.go @@ -27,10 +27,10 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/mclock" "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/p2p/netutil" "github.com/XinFinOrg/XDPoSChain/rlp" + "golang.org/x/crypto/sha3" ) var ( @@ -1236,7 +1236,7 @@ func (net *Network) checkTopicRegister(data *topicRegister) (*pong, error) { } func rlpHash(x interface{}) (h common.Hash) { - hw := sha3.NewKeccak256() + hw := sha3.NewLegacyKeccak256() rlp.Encode(hw, x) hw.Sum(h[:0]) return h diff --git a/p2p/enr/enr.go b/p2p/enr/enr.go index 5aca3ab25a6c..04d6ed4a6659 100644 --- a/p2p/enr/enr.go +++ b/p2p/enr/enr.go @@ -36,8 +36,8 @@ import ( "sort" "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/rlp" + "golang.org/x/crypto/sha3" ) const SizeLimit = 300 // maximum encoded size of a node record in bytes @@ -243,7 +243,7 @@ func (r *Record) signAndEncode(privkey *ecdsa.PrivateKey) error { list = r.appendPairs(list) // Sign the tail of the list. - h := sha3.NewKeccak256() + h := sha3.NewLegacyKeccak256() rlp.Encode(h, list[1:]) sig, err := crypto.Sign(h.Sum(nil), privkey) if err != nil { @@ -281,7 +281,7 @@ func (r *Record) verifySignature() error { // Verify the signature. list := make([]interface{}, 0, len(r.pairs)*2+1) list = r.appendPairs(list) - h := sha3.NewKeccak256() + h := sha3.NewLegacyKeccak256() rlp.Encode(h, list) if !crypto.VerifySignature(entry, h.Sum(nil), r.signature) { return errInvalidSig diff --git a/p2p/rlpx.go b/p2p/rlpx.go index a76454528372..6544ac8c3c13 100644 --- a/p2p/rlpx.go +++ b/p2p/rlpx.go @@ -37,7 +37,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/crypto/ecies" "github.com/XinFinOrg/XDPoSChain/crypto/secp256k1" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" + "golang.org/x/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/p2p/discover" "github.com/XinFinOrg/XDPoSChain/rlp" "github.com/golang/snappy" @@ -255,10 +255,10 @@ func (h *encHandshake) secrets(auth, authResp []byte) (secrets, error) { } // setup sha3 instances for the MACs - mac1 := sha3.NewKeccak256() + mac1 := sha3.NewLegacyKeccak256() mac1.Write(xor(s.MAC, h.respNonce)) mac1.Write(auth) - mac2 := sha3.NewKeccak256() + mac2 := sha3.NewLegacyKeccak256() mac2.Write(xor(s.MAC, h.initNonce)) mac2.Write(authResp) if h.initiator { diff --git a/p2p/rlpx_test.go b/p2p/rlpx_test.go index 903a91c7699e..83eb97ad0358 100644 --- a/p2p/rlpx_test.go +++ b/p2p/rlpx_test.go @@ -31,10 +31,10 @@ import ( "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/crypto/ecies" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/p2p/discover" "github.com/XinFinOrg/XDPoSChain/rlp" "github.com/davecgh/go-spew/spew" + "golang.org/x/crypto/sha3" ) func TestSharedSecret(t *testing.T) { @@ -335,8 +335,8 @@ func TestRLPXFrameRW(t *testing.T) { s1 := secrets{ AES: aesSecret, MAC: macSecret, - EgressMAC: sha3.NewKeccak256(), - IngressMAC: sha3.NewKeccak256(), + EgressMAC: sha3.NewLegacyKeccak256(), + IngressMAC: sha3.NewLegacyKeccak256(), } s1.EgressMAC.Write(egressMACinit) s1.IngressMAC.Write(ingressMACinit) @@ -345,8 +345,8 @@ func TestRLPXFrameRW(t *testing.T) { s2 := secrets{ AES: aesSecret, MAC: macSecret, - EgressMAC: sha3.NewKeccak256(), - IngressMAC: sha3.NewKeccak256(), + EgressMAC: sha3.NewLegacyKeccak256(), + IngressMAC: sha3.NewLegacyKeccak256(), } s2.EgressMAC.Write(ingressMACinit) s2.IngressMAC.Write(egressMACinit) diff --git a/p2p/server_test.go b/p2p/server_test.go index 0affd51f583f..49739f3b8486 100644 --- a/p2p/server_test.go +++ b/p2p/server_test.go @@ -26,9 +26,9 @@ import ( "time" "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/p2p/discover" + "golang.org/x/crypto/sha3" ) func init() { @@ -47,8 +47,8 @@ func newTestTransport(id discover.NodeID, fd net.Conn) transport { wrapped.rw = newRLPXFrameRW(fd, secrets{ MAC: zero16, AES: zero16, - IngressMAC: sha3.NewKeccak256(), - EgressMAC: sha3.NewKeccak256(), + IngressMAC: sha3.NewLegacyKeccak256(), + EgressMAC: sha3.NewLegacyKeccak256(), }) return &testTransport{id: id, rlpx: wrapped} } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 30750b973c73..756eda3bf38f 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -33,10 +33,10 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rlp" + "golang.org/x/crypto/sha3" ) // StateTest checks transaction processing without block context. @@ -287,7 +287,7 @@ func (tx *stTransaction) toMessage(ps stPostState, number *big.Int, baseFee *big } func rlpHash(x interface{}) (h common.Hash) { - hw := sha3.NewKeccak256() + hw := sha3.NewLegacyKeccak256() rlp.Encode(hw, x) hw.Sum(h[:0]) return h From 9d9bd65a01c37fb7f287e0d141f54ed7b2235427 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 268/479] crypto/ecies: remove unused function (#19096) --- crypto/ecies/ecies_test.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/crypto/ecies/ecies_test.go b/crypto/ecies/ecies_test.go index 0d3fb3bbaccc..fefb974d9ba6 100644 --- a/crypto/ecies/ecies_test.go +++ b/crypto/ecies/ecies_test.go @@ -91,19 +91,6 @@ func cmpPublic(pub1, pub2 PublicKey) bool { return bytes.Equal(pub1Out, pub2Out) } -// cmpPrivate returns true if the two private keys are the same. -func cmpPrivate(prv1, prv2 *PrivateKey) bool { - if prv1 == nil || prv1.D == nil { - return false - } else if prv2 == nil || prv2.D == nil { - return false - } else if prv1.D.Cmp(prv2.D) != 0 { - return false - } else { - return cmpPublic(prv1.PublicKey, prv2.PublicKey) - } -} - // Validate the ECDH component. func TestSharedKey(t *testing.T) { prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil) From d2ac7d098704976d7413350614cb3dc38ed6cd7e Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 269/479] crypto: fix build when CGO_ENABLED=0 (#19121) Package crypto works with or without cgo, which is great. However, to make it work without cgo required setting the build tag `nocgo`. It's common to disable cgo by instead just setting the environment variable `CGO_ENABLED=0`. Setting this environment variable does _not_ implicitly set the build tag `nocgo`. So projects that try to build the crypto package with `CGO_ENABLED=0` will fail. I have done this myself several times. Until today, I had just assumed that this meant that this package requires cgo. But a small build tag change will make this case work. Instead of using `nocgo` and `!nocgo`, we can use `!cgo` and `cgo`, respectively. The `cgo` build tag is automatically set if cgo is enabled, and unset if it is disabled. --- crypto/signature_cgo.go | 2 +- crypto/signature_nocgo.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto/signature_cgo.go b/crypto/signature_cgo.go index 665a20429aa2..ad5bd7c247d1 100644 --- a/crypto/signature_cgo.go +++ b/crypto/signature_cgo.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// +build !nacl,!js,!nocgo +// +build !nacl,!js,cgo package crypto diff --git a/crypto/signature_nocgo.go b/crypto/signature_nocgo.go index f636b2377287..908b04e4f881 100644 --- a/crypto/signature_nocgo.go +++ b/crypto/signature_nocgo.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// +build nacl js nocgo +// +build nacl js !cgo package crypto From 66dd846d4679a43a3236e037f156a41f535e1dbf Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 270/479] crypto/bn256/cloudflare: fix comments to describe the updated curve parameters (#19577) * Removed comment section referring to Cloudflare's bn curve parameters * Added comment to clarify the nature of the parameters * Changed value of xi to i+9 --- crypto/bn256/cloudflare/constants.go | 5 ++++- crypto/bn256/cloudflare/gfp6.go | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/crypto/bn256/cloudflare/constants.go b/crypto/bn256/cloudflare/constants.go index 5122aae64f1e..f7d2c7c001cc 100644 --- a/crypto/bn256/cloudflare/constants.go +++ b/crypto/bn256/cloudflare/constants.go @@ -13,10 +13,13 @@ func bigFromBase10(s string) *big.Int { return n } -// u is the BN parameter that determines the prime: 1868033³. +// u is the BN parameter. var u = bigFromBase10("4965661367192848881") // Order is the number of elements in both G₁ and G₂: 36u⁴+36u³+18u²+6u+1. +// Needs to be highly 2-adic for efficient SNARK key and proof generation. +// Order - 1 = 2^28 * 3^2 * 13 * 29 * 983 * 11003 * 237073 * 405928799 * 1670836401704629 * 13818364434197438864469338081. +// Refer to https://eprint.iacr.org/2013/879.pdf and https://eprint.iacr.org/2013/507.pdf for more information on these parameters. var Order = bigFromBase10("21888242871839275222246405745257275088548364400416034343698204186575808495617") // P is a prime over which we form a basic field: 36u⁴+36u³+24u²+6u+1. diff --git a/crypto/bn256/cloudflare/gfp6.go b/crypto/bn256/cloudflare/gfp6.go index 83d61b781f39..a42734911c64 100644 --- a/crypto/bn256/cloudflare/gfp6.go +++ b/crypto/bn256/cloudflare/gfp6.go @@ -5,7 +5,7 @@ package bn256 // http://eprint.iacr.org/2006/471.pdf. // gfP6 implements the field of size p⁶ as a cubic extension of gfP2 where τ³=ξ -// and ξ=i+3. +// and ξ=i+9. type gfP6 struct { x, y, z gfP2 // value is xτ² + yτ + z } From b9054b220c7a9b284e1da806f96e4b55befc1b2f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 271/479] crypto: replace fmt.Println calls with t.Log in tests (#19670) --- crypto/ecies/ecies_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crypto/ecies/ecies_test.go b/crypto/ecies/ecies_test.go index fefb974d9ba6..32f75421e5f5 100644 --- a/crypto/ecies/ecies_test.go +++ b/crypto/ecies/ecies_test.go @@ -101,24 +101,24 @@ func TestSharedKey(t *testing.T) { prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil) if err != nil { - fmt.Println(err.Error()) + t.Log(err.Error()) t.FailNow() } sk1, err := prv1.GenerateShared(&prv2.PublicKey, skLen, skLen) if err != nil { - fmt.Println(err.Error()) + t.Log(err.Error()) t.FailNow() } sk2, err := prv2.GenerateShared(&prv1.PublicKey, skLen, skLen) if err != nil { - fmt.Println(err.Error()) + t.Log(err.Error()) t.FailNow() } if !bytes.Equal(sk1, sk2) { - fmt.Println(ErrBadSharedKeys.Error()) + t.Log(ErrBadSharedKeys.Error()) t.FailNow() } } From 8dcea8ddf8bc400dd0992de103d568a4e65b497e Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 272/479] crypto: replace t.Log(); t.FailNow() with t.Fatal() (#19849) --- crypto/ecies/ecies_test.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/crypto/ecies/ecies_test.go b/crypto/ecies/ecies_test.go index 32f75421e5f5..3fe7ba0af8c6 100644 --- a/crypto/ecies/ecies_test.go +++ b/crypto/ecies/ecies_test.go @@ -101,25 +101,21 @@ func TestSharedKey(t *testing.T) { prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil) if err != nil { - t.Log(err.Error()) - t.FailNow() + t.Fatal(err) } sk1, err := prv1.GenerateShared(&prv2.PublicKey, skLen, skLen) if err != nil { - t.Log(err.Error()) - t.FailNow() + t.Fatal(err) } sk2, err := prv2.GenerateShared(&prv1.PublicKey, skLen, skLen) if err != nil { - t.Log(err.Error()) - t.FailNow() + t.Fatal(err) } if !bytes.Equal(sk1, sk2) { - t.Log(ErrBadSharedKeys.Error()) - t.FailNow() + t.Fatal(err) } } From ad5e7d6db35c134dc315afee152d11be4c36e561 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 273/479] crypto: add SignatureLength constant and use it everywhere (#19996) --- accounts/abi/bind/backends/simulated.go | 3 ++- accounts/usbwallet/ledger.go | 5 +++-- accounts/usbwallet/trezor.go | 3 ++- cmd/puppeth/wizard_genesis.go | 4 ++-- consensus/XDPoS/engines/engine_v1/utils.go | 2 +- consensus/clique/clique.go | 6 +++--- core/genesis.go | 3 ++- core/types/lending_signing.go | 2 +- core/types/order_signing.go | 2 +- core/types/transaction_signing.go | 2 +- core/vm/contracts.go | 2 +- crypto/signature_cgo.go | 17 +++++++++-------- crypto/signature_nocgo.go | 5 +++-- internal/ethapi/api.go | 4 ++-- p2p/rlpx.go | 10 +++++----- 15 files changed, 38 insertions(+), 32 deletions(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 4de5a89885dd..f6b4a13da6fc 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -42,6 +42,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" + "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/eth/filters" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/event" @@ -101,7 +102,7 @@ func NewXDCSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64, chainConfi GasLimit: gasLimit, // need this big, support initial smart contract Config: chainConfig, Alloc: alloc, - ExtraData: append(make([]byte, 32), make([]byte, 65)...), + ExtraData: append(make([]byte, 32), make([]byte, crypto.SignatureLength)...), } genesis.MustCommit(database) consensus := XDPoS.NewFaker(database, chainConfig) diff --git a/accounts/usbwallet/ledger.go b/accounts/usbwallet/ledger.go index a1091add84e0..f9d06aa032dc 100644 --- a/accounts/usbwallet/ledger.go +++ b/accounts/usbwallet/ledger.go @@ -32,6 +32,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/hexutil" "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/rlp" ) @@ -341,7 +342,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction op = ledgerP1ContTransactionData } // Extract the Ethereum signature and do a sanity validation - if len(reply) != 65 { + if len(reply) != crypto.SignatureLength { return common.Address{}, nil, errors.New("reply lacks signature") } signature := append(reply[1:], reply[0]) @@ -352,7 +353,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction signer = new(types.HomesteadSigner) } else { signer = types.NewEIP155Signer(chainID) - signature[64] = signature[64] - byte(chainID.Uint64()*2+35) + signature[crypto.RecoveryIDOffset] = signature[crypto.RecoveryIDOffset] - byte(chainID.Uint64()*2+35) } signed, err := tx.WithSignature(signer, signature) if err != nil { diff --git a/accounts/usbwallet/trezor.go b/accounts/usbwallet/trezor.go index 04e57292bafe..8bc51bb83538 100644 --- a/accounts/usbwallet/trezor.go +++ b/accounts/usbwallet/trezor.go @@ -32,6 +32,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/hexutil" "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/log" "github.com/golang/protobuf/proto" ) @@ -222,7 +223,7 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction } else { // Trezor backend does not support typed transactions yet. signer = types.NewEIP155Signer(chainID) - signature[64] = signature[64] - byte(chainID.Uint64()*2+35) + signature[crypto.RecoveryIDOffset] = signature[crypto.RecoveryIDOffset] - byte(chainID.Uint64()*2+35) } // Inject the final signature into the transaction and sanity check the sender diff --git a/cmd/puppeth/wizard_genesis.go b/cmd/puppeth/wizard_genesis.go index c1e077c59513..d7a8f93cec02 100644 --- a/cmd/puppeth/wizard_genesis.go +++ b/cmd/puppeth/wizard_genesis.go @@ -105,7 +105,7 @@ func (w *wizard) makeGenesis() { } } } - genesis.ExtraData = make([]byte, 32+len(signers)*common.AddressLength+65) + genesis.ExtraData = make([]byte, 32+len(signers)*common.AddressLength+crypto.SignatureLength) for i, signer := range signers { copy(genesis.ExtraData[32+i*common.AddressLength:], signer[:]) } @@ -179,7 +179,7 @@ func (w *wizard) makeGenesis() { validatorCap := new(big.Int) validatorCap.SetString("50000000000000000000000", 10) var validatorCaps []*big.Int - genesis.ExtraData = make([]byte, 32+len(signers)*common.AddressLength+65) + genesis.ExtraData = make([]byte, 32+len(signers)*common.AddressLength+crypto.SignatureLength) for i, signer := range signers { validatorCaps = append(validatorCaps, validatorCap) copy(genesis.ExtraData[32+i*common.AddressLength:], signer[:]) diff --git a/consensus/XDPoS/engines/engine_v1/utils.go b/consensus/XDPoS/engines/engine_v1/utils.go index b07a5e0f69b6..a0128331220c 100644 --- a/consensus/XDPoS/engines/engine_v1/utils.go +++ b/consensus/XDPoS/engines/engine_v1/utils.go @@ -74,7 +74,7 @@ func sigHash(header *types.Header) (hash common.Hash) { header.GasLimit, header.GasUsed, header.Time, - header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short + header.Extra[:len(header.Extra)-crypto.SignatureLength], // Yes, this will panic if extra is too short header.MixDigest, header.Nonce, } diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 899649eb84f3..bb1e8ef1189c 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -56,8 +56,8 @@ var ( epochLength = uint64(30000) // Default number of blocks after which to checkpoint and reset the pending votes blockPeriod = uint64(15) // Default minimum difference between two consecutive block's timestamps - extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity - extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal + extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity + extraSeal = crypto.SignatureLength // Fixed number of extra-data suffix bytes reserved for signer seal nonceAuthVote = hexutil.MustDecode("0xffffffffffffffff") // Magic nonce number to vote on adding a new signer nonceDropVote = hexutil.MustDecode("0x0000000000000000") // Magic nonce number to vote on removing a signer. @@ -160,7 +160,7 @@ func sigHash(header *types.Header) (hash common.Hash) { header.GasLimit, header.GasUsed, header.Time, - header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short + header.Extra[:len(header.Extra)-crypto.SignatureLength], // Yes, this will panic if extra is too short header.MixDigest, header.Nonce, } diff --git a/core/genesis.go b/core/genesis.go index a005b3a14a58..d14b1c73d153 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -26,6 +26,7 @@ import ( "strings" "github.com/XinFinOrg/XDPoSChain/core/rawdb" + "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/hexutil" @@ -376,7 +377,7 @@ func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis { // Assemble and return the genesis with the precompiles and faucet pre-funded return &Genesis{ Config: &config, - ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, 65)...), + ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, crypto.SignatureLength)...), GasLimit: 6283185, BaseFee: big.NewInt(params.InitialBaseFee), Difficulty: big.NewInt(1), diff --git a/core/types/lending_signing.go b/core/types/lending_signing.go index 8fc37815d33f..cce6e607af52 100644 --- a/core/types/lending_signing.go +++ b/core/types/lending_signing.go @@ -97,7 +97,7 @@ func (lendingsign LendingTxSigner) Equal(s2 LendingSigner) bool { // SignatureValues returns signature values. This signature needs to be in the [R || S || V] format where V is 0 or 1. func (lendingsign LendingTxSigner) SignatureValues(tx *LendingTransaction, sig []byte) (r, s, v *big.Int, err error) { - if len(sig) != 65 { + if len(sig) != crypto.SignatureLength { panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig))) } r = new(big.Int).SetBytes(sig[:32]) diff --git a/core/types/order_signing.go b/core/types/order_signing.go index 6a7f40025536..8fa562552999 100644 --- a/core/types/order_signing.go +++ b/core/types/order_signing.go @@ -97,7 +97,7 @@ func (ordersign OrderTxSigner) Equal(s2 OrderSigner) bool { // SignatureValues returns signature values. This signature needs to be in the [R || S || V] format where V is 0 or 1. func (ordersign OrderTxSigner) SignatureValues(tx *OrderTransaction, sig []byte) (r, s, v *big.Int, err error) { - if len(sig) != 65 { + if len(sig) != crypto.SignatureLength { panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig))) } r = new(big.Int).SetBytes(sig[:32]) diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index b835e45af1ba..ff8c4b530ada 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -477,7 +477,7 @@ func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool) (commo } // encode the snature in uncompressed format r, s := R.Bytes(), S.Bytes() - sig := make([]byte, 65) + sig := make([]byte, crypto.SignatureLength) copy(sig[32-len(r):32], r) copy(sig[64-len(s):64], s) sig[64] = V diff --git a/core/vm/contracts.go b/core/vm/contracts.go index be9df1adfecd..7da6949ded53 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -200,7 +200,7 @@ func (c *ecrecover) Run(input []byte) ([]byte, error) { } // We must make sure not to modify the 'input', so placing the 'v' along with // the signature needs to be done on a new allocation - sig := make([]byte, 65) + sig := make([]byte, crypto.SignatureLength) copy(sig, input[64:128]) sig[64] = v // v needs to be at the end for libsecp256k1 diff --git a/crypto/signature_cgo.go b/crypto/signature_cgo.go index ad5bd7c247d1..fcf0fa9d8ad4 100644 --- a/crypto/signature_cgo.go +++ b/crypto/signature_cgo.go @@ -14,6 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . +//go:build !nacl && !js && cgo // +build !nacl,!js,cgo package crypto @@ -47,24 +48,24 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { // // This function is susceptible to chosen plaintext attacks that can leak // information about the private key that is used for signing. Callers must -// be aware that the given hash cannot be chosen by an adversery. Common +// be aware that the given digest cannot be chosen by an adversery. Common // solution is to hash any input before calculating the signature. // // The produced signature is in the [R || S || V] format where V is 0 or 1. -func Sign(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) { - if len(hash) != 32 { - return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash)) +func Sign(digestHash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) { + if len(digestHash) != DigestLength { + return nil, fmt.Errorf("hash is required to be exactly %d bytes (%d)", DigestLength, len(digestHash)) } seckey := math.PaddedBigBytes(prv.D, prv.Params().BitSize/8) defer zeroBytes(seckey) - return secp256k1.Sign(hash, seckey) + return secp256k1.Sign(digestHash, seckey) } -// VerifySignature checks that the given public key created signature over hash. +// VerifySignature checks that the given public key created signature over digest. // The public key should be in compressed (33 bytes) or uncompressed (65 bytes) format. // The signature should have the 64 byte [R || S] format. -func VerifySignature(pubkey, hash, signature []byte) bool { - return secp256k1.VerifySignature(pubkey, hash, signature) +func VerifySignature(pubkey, digestHash, signature []byte) bool { + return secp256k1.VerifySignature(pubkey, digestHash, signature) } // DecompressPubkey parses a public key in the 33-byte compressed format. diff --git a/crypto/signature_nocgo.go b/crypto/signature_nocgo.go index 908b04e4f881..e52f50409c49 100644 --- a/crypto/signature_nocgo.go +++ b/crypto/signature_nocgo.go @@ -14,6 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . +//go:build nacl || js || !cgo // +build nacl js !cgo package crypto @@ -41,7 +42,7 @@ func Ecrecover(hash, sig []byte) ([]byte, error) { // SigToPub returns the public key that created the given signature. func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { // Convert to btcec input format with 'recovery id' v at the beginning. - btcsig := make([]byte, 65) + btcsig := make([]byte, SignatureLength) btcsig[0] = sig[64] + 27 copy(btcsig[1:], sig) @@ -58,7 +59,7 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { // // The produced signature is in the [R || S || V] format where V is 0 or 1. func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) { - if len(hash) != 32 { + if len(hash) != DigestLength { return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash)) } if prv.Curve != btcec.S256() { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index ac7ac6739b3e..273021e4f0a6 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -542,7 +542,7 @@ func (s *PrivateAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr c if err != nil { return nil, err } - signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper + signature[crypto.RecoveryIDOffset] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper return signature, nil } @@ -3264,7 +3264,7 @@ func (s *PublicTransactionPoolAPI) Sign(addr common.Address, data hexutil.Bytes) // Sign the requested hash with the wallet signature, err := wallet.SignHash(account, signHash(data)) if err == nil { - signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper + signature[crypto.RecoveryIDOffset] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper } return signature, err } diff --git a/p2p/rlpx.go b/p2p/rlpx.go index 6544ac8c3c13..640e7ce3f21e 100644 --- a/p2p/rlpx.go +++ b/p2p/rlpx.go @@ -37,19 +37,19 @@ import ( "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/crypto/ecies" "github.com/XinFinOrg/XDPoSChain/crypto/secp256k1" - "golang.org/x/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/p2p/discover" "github.com/XinFinOrg/XDPoSChain/rlp" "github.com/golang/snappy" + "golang.org/x/crypto/sha3" ) const ( maxUint24 = ^uint32(0) >> 8 - sskLen = 16 // ecies.MaxSharedKeyLength(pubKey) / 2 - sigLen = 65 // elliptic S256 - pubLen = 64 // 512 bit pubkey in uncompressed representation without format byte - shaLen = 32 // hash length (for nonce etc) + sskLen = 16 // ecies.MaxSharedKeyLength(pubKey) / 2 + sigLen = crypto.SignatureLength // elliptic S256 + pubLen = 64 // 512 bit pubkey in uncompressed representation without format byte + shaLen = 32 // hash length (for nonce etc) authMsgLen = sigLen + shaLen + pubLen + shaLen + 1 authRespLen = pubLen + shaLen + 1 From e1eb00ef99b4471948ae2722e7bd60cb92fad994 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 274/479] crypto: make unit tests work with Go 1.13 (#20053) --- crypto/ecies/ecies_test.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/crypto/ecies/ecies_test.go b/crypto/ecies/ecies_test.go index 3fe7ba0af8c6..5fc6bbe007a5 100644 --- a/crypto/ecies/ecies_test.go +++ b/crypto/ecies/ecies_test.go @@ -43,14 +43,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/crypto" ) -//var dumpEnc bool -// -//func init() { -// flDump := flag.Bool("dump", false, "write encrypted test message to file") -// flag.Parse() -// dumpEnc = *flDump -//} - // Ensure the KDF generates appropriately sized keys. func TestKDF(t *testing.T) { msg := []byte("Hello, world") From 6033722b7214d23f6f19180195d2ed3c8b971a22 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 275/479] crypto: use golangci-lint (#20295) --- crypto/bn256/bn256_fast.go | 11 ++++++----- crypto/ecies/ecies_test.go | 17 ----------------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/crypto/bn256/bn256_fast.go b/crypto/bn256/bn256_fast.go index be0264a730db..ef452857db27 100644 --- a/crypto/bn256/bn256_fast.go +++ b/crypto/bn256/bn256_fast.go @@ -19,17 +19,18 @@ // Package bn256 implements the Optimal Ate pairing over a 256-bit Barreto-Naehrig curve. package bn256 -import "github.com/XinFinOrg/XDPoSChain/crypto/bn256/cloudflare" - +import ( + bn256cf "github.com/XinFinOrg/XDPoSChain/crypto/bn256/cloudflare" +) // G1 is an abstract cyclic group. The zero value is suitable for use as the // output of an operation, but cannot be used as an input. -type G1 = bn256.G1 +type G1 = bn256cf.G1 // G2 is an abstract cyclic group. The zero value is suitable for use as the // output of an operation, but cannot be used as an input. -type G2 = bn256.G2 +type G2 = bn256cf.G2 // PairingCheck calculates the Optimal Ate pairing for a set of points. func PairingCheck(a []*G1, b []*G2) bool { - return bn256.PairingCheck(a, b) + return bn256cf.PairingCheck(a, b) } diff --git a/crypto/ecies/ecies_test.go b/crypto/ecies/ecies_test.go index 5fc6bbe007a5..235c2b59a887 100644 --- a/crypto/ecies/ecies_test.go +++ b/crypto/ecies/ecies_test.go @@ -36,7 +36,6 @@ import ( "crypto/sha256" "encoding/hex" "errors" - "fmt" "math/big" "testing" @@ -67,22 +66,6 @@ func cmpParams(p1, p2 *ECIESParams) bool { p1.BlockSize == p2.BlockSize } -// cmpPublic returns true if the two public keys represent the same pojnt. -func cmpPublic(pub1, pub2 PublicKey) bool { - if pub1.X == nil || pub1.Y == nil { - fmt.Println(ErrInvalidPublicKey.Error()) - return false - } - if pub2.X == nil || pub2.Y == nil { - fmt.Println(ErrInvalidPublicKey.Error()) - return false - } - pub1Out := elliptic.Marshal(pub1.Curve, pub1.X, pub1.Y) - pub2Out := elliptic.Marshal(pub2.Curve, pub2.X, pub2.Y) - - return bytes.Equal(pub1Out, pub2Out) -} - // Validate the ECDH component. func TestSharedKey(t *testing.T) { prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil) From b2be754647da6a318a16dd07c4d178a45536061f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 276/479] crypto/bn256: fix import line (#20723) --- crypto/bn256/bn256_slow.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crypto/bn256/bn256_slow.go b/crypto/bn256/bn256_slow.go index cf1053562de6..273c248d14c0 100644 --- a/crypto/bn256/bn256_slow.go +++ b/crypto/bn256/bn256_slow.go @@ -14,12 +14,13 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . +//go:build !amd64 && !arm64 // +build !amd64,!arm64 // Package bn256 implements the Optimal Ate pairing over a 256-bit Barreto-Naehrig curve. package bn256 -import "github.com/XinFinOrg/XDPoSChain/crypto/bn256/google" +import bn256 "github.com/XinFinOrg/XDPoSChain/crypto/bn256/google" // G1 is an abstract cyclic group. The zero value is suitable for use as the // output of an operation, but cannot be used as an input. From ee303c9f3df7d5f60e33ed32301f221adff77a2a Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 277/479] crypto/ecies: improve concatKDF (#20836) --- crypto/ecies/ecies.go | 144 ++++++++++++------------------------- crypto/ecies/ecies_test.go | 39 ++++++---- crypto/ecies/params.go | 20 ++++++ 3 files changed, 93 insertions(+), 110 deletions(-) diff --git a/crypto/ecies/ecies.go b/crypto/ecies/ecies.go index bb1c8d2ff4ac..738bb8f584aa 100644 --- a/crypto/ecies/ecies.go +++ b/crypto/ecies/ecies.go @@ -35,8 +35,8 @@ import ( "crypto/elliptic" "crypto/hmac" "crypto/subtle" + "encoding/binary" "errors" - "fmt" "hash" "io" "math/big" @@ -45,7 +45,6 @@ import ( var ( ErrImport = errors.New("ecies: failed to import key") ErrInvalidCurve = errors.New("ecies: invalid elliptic curve") - ErrInvalidParams = errors.New("ecies: invalid ECIES parameters") ErrInvalidPublicKey = errors.New("ecies: invalid public key") ErrSharedKeyIsPointAtInfinity = errors.New("ecies: shared key is point at infinity") ErrSharedKeyTooBig = errors.New("ecies: shared key params are too big") @@ -139,57 +138,39 @@ func (prv *PrivateKey) GenerateShared(pub *PublicKey, skLen, macLen int) (sk []b } var ( - ErrKeyDataTooLong = errors.New("ecies: can't supply requested key data") ErrSharedTooLong = errors.New("ecies: shared secret is too long") ErrInvalidMessage = errors.New("ecies: invalid message") ) -var ( - big2To32 = new(big.Int).Exp(big.NewInt(2), big.NewInt(32), nil) - big2To32M1 = new(big.Int).Sub(big2To32, big.NewInt(1)) -) - -func incCounter(ctr []byte) { - if ctr[3]++; ctr[3] != 0 { - return - } - if ctr[2]++; ctr[2] != 0 { - return - } - if ctr[1]++; ctr[1] != 0 { - return - } - if ctr[0]++; ctr[0] != 0 { - return - } -} - // NIST SP 800-56 Concatenation Key Derivation Function (see section 5.8.1). -func concatKDF(hash hash.Hash, z, s1 []byte, kdLen int) (k []byte, err error) { - if s1 == nil { - s1 = make([]byte, 0) - } - - reps := ((kdLen + 7) * 8) / (hash.BlockSize() * 8) - if big.NewInt(int64(reps)).Cmp(big2To32M1) > 0 { - fmt.Println(big2To32M1) - return nil, ErrKeyDataTooLong - } - - counter := []byte{0, 0, 0, 1} - k = make([]byte, 0) - - for i := 0; i <= reps; i++ { - hash.Write(counter) +func concatKDF(hash hash.Hash, z, s1 []byte, kdLen int) []byte { + counterBytes := make([]byte, 4) + k := make([]byte, 0, roundup(kdLen, hash.Size())) + for counter := uint32(1); len(k) < kdLen; counter++ { + binary.BigEndian.PutUint32(counterBytes, counter) + hash.Reset() + hash.Write(counterBytes) hash.Write(z) hash.Write(s1) - k = append(k, hash.Sum(nil)...) - hash.Reset() - incCounter(counter) + k = hash.Sum(k) } + return k[:kdLen] +} - k = k[:kdLen] - return +// roundup rounds size up to the next multiple of blocksize. +func roundup(size, blocksize int) int { + return size + blocksize - (size % blocksize) +} + +// deriveKeys creates the encryption and MAC keys using concatKDF. +func deriveKeys(hash hash.Hash, z, s1 []byte, keyLen int) (Ke, Km []byte) { + K := concatKDF(hash, z, s1, 2*keyLen) + Ke = K[:keyLen] + Km = K[keyLen:] + hash.Reset() + hash.Write(Km) + Km = hash.Sum(Km[:0]) + return Ke, Km } // messageTag computes the MAC of a message (called the tag) as per @@ -210,7 +191,6 @@ func generateIV(params *ECIESParams, rand io.Reader) (iv []byte, err error) { } // symEncrypt carries out CTR encryption using the block cipher specified in the -// parameters. func symEncrypt(rand io.Reader, params *ECIESParams, key, m []byte) (ct []byte, err error) { c, err := params.Cipher(key) if err != nil { @@ -250,36 +230,27 @@ func symDecrypt(params *ECIESParams, key, ct []byte) (m []byte, err error) { // ciphertext. s1 is fed into key derivation, s2 is fed into the MAC. If the // shared information parameters aren't being used, they should be nil. func Encrypt(rand io.Reader, pub *PublicKey, m, s1, s2 []byte) (ct []byte, err error) { - params := pub.Params - if params == nil { - if params = ParamsFromCurve(pub.Curve); params == nil { - err = ErrUnsupportedECIESParameters - return - } + params, err := pubkeyParams(pub) + if err != nil { + return nil, err } + R, err := GenerateKey(rand, pub.Curve, params) if err != nil { - return + return nil, err } - hash := params.Hash() z, err := R.GenerateShared(pub, params.KeyLen, params.KeyLen) if err != nil { - return - } - K, err := concatKDF(hash, z, s1, params.KeyLen+params.KeyLen) - if err != nil { - return + return nil, err } - Ke := K[:params.KeyLen] - Km := K[params.KeyLen:] - hash.Write(Km) - Km = hash.Sum(nil) - hash.Reset() + + hash := params.Hash() + Ke, Km := deriveKeys(hash, z, s1, params.KeyLen) em, err := symEncrypt(rand, params, Ke, m) if err != nil || len(em) <= params.BlockSize { - return + return nil, err } d := messageTag(params.Hash, Km, em, s2) @@ -289,7 +260,7 @@ func Encrypt(rand io.Reader, pub *PublicKey, m, s1, s2 []byte) (ct []byte, err e copy(ct, Rb) copy(ct[len(Rb):], em) copy(ct[len(Rb)+len(em):], d) - return + return ct, nil } // Decrypt decrypts an ECIES ciphertext. @@ -297,13 +268,11 @@ func (prv *PrivateKey) Decrypt(c, s1, s2 []byte) (m []byte, err error) { if len(c) == 0 { return nil, ErrInvalidMessage } - params := prv.PublicKey.Params - if params == nil { - if params = ParamsFromCurve(prv.PublicKey.Curve); params == nil { - err = ErrUnsupportedECIESParameters - return - } + params, err := pubkeyParams(&prv.PublicKey) + if err != nil { + return nil, err } + hash := params.Hash() var ( @@ -317,12 +286,10 @@ func (prv *PrivateKey) Decrypt(c, s1, s2 []byte) (m []byte, err error) { case 2, 3, 4: rLen = (prv.PublicKey.Curve.Params().BitSize + 7) / 4 if len(c) < (rLen + hLen + 1) { - err = ErrInvalidMessage - return + return nil, ErrInvalidMessage } default: - err = ErrInvalidPublicKey - return + return nil, ErrInvalidPublicKey } mStart = rLen @@ -332,36 +299,19 @@ func (prv *PrivateKey) Decrypt(c, s1, s2 []byte) (m []byte, err error) { R.Curve = prv.PublicKey.Curve R.X, R.Y = elliptic.Unmarshal(R.Curve, c[:rLen]) if R.X == nil { - err = ErrInvalidPublicKey - return - } - if !R.Curve.IsOnCurve(R.X, R.Y) { - err = ErrInvalidCurve - return + return nil, ErrInvalidPublicKey } z, err := prv.GenerateShared(R, params.KeyLen, params.KeyLen) if err != nil { - return + return nil, err } - - K, err := concatKDF(hash, z, s1, params.KeyLen+params.KeyLen) - if err != nil { - return - } - - Ke := K[:params.KeyLen] - Km := K[params.KeyLen:] - hash.Write(Km) - Km = hash.Sum(nil) - hash.Reset() + Ke, Km := deriveKeys(hash, z, s1, params.KeyLen) d := messageTag(params.Hash, Km, c[mStart:mEnd], s2) if subtle.ConstantTimeCompare(c[mEnd:], d) != 1 { - err = ErrInvalidMessage - return + return nil, ErrInvalidMessage } - m, err = symDecrypt(params, Ke, c[mStart:mEnd]) - return + return symDecrypt(params, Ke, c[mStart:mEnd]) } diff --git a/crypto/ecies/ecies_test.go b/crypto/ecies/ecies_test.go index 235c2b59a887..a1a7604ba8e2 100644 --- a/crypto/ecies/ecies_test.go +++ b/crypto/ecies/ecies_test.go @@ -42,17 +42,23 @@ import ( "github.com/XinFinOrg/XDPoSChain/crypto" ) -// Ensure the KDF generates appropriately sized keys. func TestKDF(t *testing.T) { - msg := []byte("Hello, world") - h := sha256.New() - - k, err := concatKDF(h, msg, nil, 64) - if err != nil { - t.Fatal(err) - } - if len(k) != 64 { - t.Fatalf("KDF: generated key is the wrong size (%d instead of 64\n", len(k)) + tests := []struct { + length int + output []byte + }{ + {6, decode("858b192fa2ed")}, + {32, decode("858b192fa2ed4395e2bf88dd8d5770d67dc284ee539f12da8bceaa45d06ebae0")}, + {48, decode("858b192fa2ed4395e2bf88dd8d5770d67dc284ee539f12da8bceaa45d06ebae0700f1ab918a5f0413b8140f9940d6955")}, + {64, decode("858b192fa2ed4395e2bf88dd8d5770d67dc284ee539f12da8bceaa45d06ebae0700f1ab918a5f0413b8140f9940d6955f3467fd6672cce1024c5b1effccc0f61")}, + } + + for _, test := range tests { + h := sha256.New() + k := concatKDF(h, []byte("input"), nil, test.length) + if !bytes.Equal(k, test.output) { + t.Fatalf("KDF: generated key %x does not match expected output %x", k, test.output) + } } } @@ -293,8 +299,8 @@ func TestParamSelection(t *testing.T) { func testParamSelection(t *testing.T, c testCase) { params := ParamsFromCurve(c.Curve) - if params == nil && c.Expected != nil { - t.Fatalf("%s (%s)\n", ErrInvalidParams.Error(), c.Name) + if params == nil { + t.Fatal("ParamsFromCurve returned nil") } else if params != nil && !cmpParams(params, c.Expected) { t.Fatalf("ecies: parameters should be invalid (%s)\n", c.Name) } @@ -328,7 +334,6 @@ func testParamSelection(t *testing.T, c testCase) { if err == nil { t.Fatalf("ecies: encryption should not have succeeded (%s)\n", c.Name) } - } // Ensure that the basic public key validation in the decryption operation @@ -414,3 +419,11 @@ func hexKey(prv string) *PrivateKey { } return ImportECDSA(key) } + +func decode(s string) []byte { + bytes, err := hex.DecodeString(s) + if err != nil { + panic(err) + } + return bytes +} diff --git a/crypto/ecies/params.go b/crypto/ecies/params.go index bd5969f1a91d..293f2e83b117 100644 --- a/crypto/ecies/params.go +++ b/crypto/ecies/params.go @@ -40,6 +40,7 @@ import ( "crypto/sha256" "crypto/sha512" "errors" + "fmt" "hash" ethcrypto "github.com/XinFinOrg/XDPoSChain/crypto" @@ -49,8 +50,14 @@ var ( DefaultCurve = ethcrypto.S256() ErrUnsupportedECDHAlgorithm = errors.New("ecies: unsupported ECDH algorithm") ErrUnsupportedECIESParameters = errors.New("ecies: unsupported ECIES parameters") + ErrInvalidKeyLen = fmt.Errorf("ecies: invalid key size (> %d) in ECIESParams", maxKeyLen) ) +// KeyLen is limited to prevent overflow of the counter +// in concatKDF. While the theoretical limit is much higher, +// no known cipher uses keys larger than 512 bytes. +const maxKeyLen = 512 + type ECIESParams struct { Hash func() hash.Hash // hash function hashAlgo crypto.Hash @@ -115,3 +122,16 @@ func AddParamsForCurve(curve elliptic.Curve, params *ECIESParams) { func ParamsFromCurve(curve elliptic.Curve) (params *ECIESParams) { return paramsFromCurve[curve] } + +func pubkeyParams(key *PublicKey) (*ECIESParams, error) { + params := key.Params + if params == nil { + if params = ParamsFromCurve(key.Curve); params == nil { + return nil, ErrUnsupportedECIESParameters + } + } + if params.KeyLen > maxKeyLen { + return nil, ErrInvalidKeyLen + } + return params, nil +} From f9484669760e53015b265e0c12ef411b6245a678 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 278/479] crypto: less allocations when hashing and tx handling (#21265) --- crypto/crypto.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto/crypto.go b/crypto/crypto.go index 22849aa6ea30..29d2b75ecd89 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -75,7 +75,7 @@ func HashData(kh KeccakState, data []byte) (h common.Hash) { // Keccak256 calculates and returns the Keccak256 hash of the input data. func Keccak256(data ...[]byte) []byte { b := make([]byte, 32) - d := NewKeccakState() + d := sha3.NewLegacyKeccak256().(KeccakState) for _, b := range data { d.Write(b) } @@ -86,7 +86,7 @@ func Keccak256(data ...[]byte) []byte { // Keccak256Hash calculates and returns the Keccak256 hash of the input data, // converting it to an internal Hash data structure. func Keccak256Hash(data ...[]byte) (h common.Hash) { - d := NewKeccakState() + d := sha3.NewLegacyKeccak256().(KeccakState) for _, b := range data { d.Write(b) } From a2eb8553e85b070900e4544d009da25009c0ee96 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 279/479] crypto/bn256: better comments for u, P and Order (#21836) --- crypto/bn256/google/constants.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crypto/bn256/google/constants.go b/crypto/bn256/google/constants.go index ab649d7f3f7c..0db432c9cfe1 100644 --- a/crypto/bn256/google/constants.go +++ b/crypto/bn256/google/constants.go @@ -13,13 +13,14 @@ func bigFromBase10(s string) *big.Int { return n } -// u is the BN parameter that determines the prime: 1868033³. +// u is the BN parameter that determines the prime. var u = bigFromBase10("4965661367192848881") -// p is a prime over which we form a basic field: 36u⁴+36u³+24u²+6u+1. +// P is a prime over which we form a basic field: 36u⁴+36u³+24u²+6u+1. var P = bigFromBase10("21888242871839275222246405745257275088696311157297823662689037894645226208583") // Order is the number of elements in both G₁ and G₂: 36u⁴+36u³+18u²+6u+1. +// Order - 1 = 2^28 * 3^2 * 13 * 29 * 983 * 11003 * 237073 * 405928799 * 1670836401704629 * 13818364434197438864469338081. var Order = bigFromBase10("21888242871839275222246405745257275088548364400416034343698204186575808495617") // xiToPMinus1Over6 is ξ^((p-1)/6) where ξ = i+9. From a64e42463ab73ceb52a416cfe9cc47abe374b6b8 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 280/479] crypto/bn256: refine comments according to #19577, #21595, and #21836 (#21847) --- crypto/bn256/cloudflare/bn256.go | 9 +++++++-- crypto/bn256/google/bn256.go | 5 +++-- crypto/bn256/google/constants.go | 2 ++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/crypto/bn256/cloudflare/bn256.go b/crypto/bn256/cloudflare/bn256.go index a6dd972ba8b2..4f607af2ad35 100644 --- a/crypto/bn256/cloudflare/bn256.go +++ b/crypto/bn256/cloudflare/bn256.go @@ -9,8 +9,13 @@ // // This package specifically implements the Optimal Ate pairing over a 256-bit // Barreto-Naehrig curve as described in -// http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is compatible -// with the implementation described in that paper. +// http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is not +// compatible with the implementation described in that paper, as different +// parameters are chosen. +// +// (This package previously claimed to operate at a 128-bit security level. +// However, recent improvements in attacks mean that is no longer true. See +// https://moderncrypto.org/mail-archive/curves/2016/000740.html.) package bn256 import ( diff --git a/crypto/bn256/google/bn256.go b/crypto/bn256/google/bn256.go index e0402e51f0e2..0a9d5cd35dce 100644 --- a/crypto/bn256/google/bn256.go +++ b/crypto/bn256/google/bn256.go @@ -12,8 +12,9 @@ // // This package specifically implements the Optimal Ate pairing over a 256-bit // Barreto-Naehrig curve as described in -// http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is compatible -// with the implementation described in that paper. +// http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is not +// compatible with the implementation described in that paper, as different +// parameters are chosen. // // (This package previously claimed to operate at a 128-bit security level. // However, recent improvements in attacks mean that is no longer true. See diff --git a/crypto/bn256/google/constants.go b/crypto/bn256/google/constants.go index 0db432c9cfe1..2990bd951282 100644 --- a/crypto/bn256/google/constants.go +++ b/crypto/bn256/google/constants.go @@ -20,7 +20,9 @@ var u = bigFromBase10("4965661367192848881") var P = bigFromBase10("21888242871839275222246405745257275088696311157297823662689037894645226208583") // Order is the number of elements in both G₁ and G₂: 36u⁴+36u³+18u²+6u+1. +// Needs to be highly 2-adic for efficient SNARK key and proof generation. // Order - 1 = 2^28 * 3^2 * 13 * 29 * 983 * 11003 * 237073 * 405928799 * 1670836401704629 * 13818364434197438864469338081. +// Refer to https://eprint.iacr.org/2013/879.pdf and https://eprint.iacr.org/2013/507.pdf for more information on these parameters. var Order = bigFromBase10("21888242871839275222246405745257275088548364400416034343698204186575808495617") // xiToPMinus1Over6 is ξ^((p-1)/6) where ξ = i+9. From ddc5e618a752fdeec51fca50620df4c007843636 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 281/479] crypto/bn256: fix bn256Mul fuzzer to not hang on large input (#21872) * crypto/bn256: fix bn256Mul fuzzer to not hang on large input * Update crypto/bn256/bn256_fuzz.go Co-authored-by: ligi Co-authored-by: ligi --- tests/fuzzers/bn256/bn256_fuzz.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/fuzzers/bn256/bn256_fuzz.go b/tests/fuzzers/bn256/bn256_fuzz.go index d735745185b3..91ff35daaa89 100644 --- a/tests/fuzzers/bn256/bn256_fuzz.go +++ b/tests/fuzzers/bn256/bn256_fuzz.go @@ -90,6 +90,12 @@ func FuzzMul(data []byte) int { if remaining == 0 { return 0 } + if remaining > 128 { + // The evm only ever uses 32 byte integers, we need to cap this otherwise + // we run into slow exec. A 236Kb byte integer cause oss-fuzz to report it as slow. + // 128 bytes should be fine though + return 0 + } buf := make([]byte, remaining) input.Read(buf) From b1c719029ca719568b29137df44a62ab3309414b Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 282/479] crypto: improve trie updates (#21047) --- crypto/crypto.go | 4 ++-- crypto/crypto_test.go | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/crypto/crypto.go b/crypto/crypto.go index 29d2b75ecd89..22849aa6ea30 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -75,7 +75,7 @@ func HashData(kh KeccakState, data []byte) (h common.Hash) { // Keccak256 calculates and returns the Keccak256 hash of the input data. func Keccak256(data ...[]byte) []byte { b := make([]byte, 32) - d := sha3.NewLegacyKeccak256().(KeccakState) + d := NewKeccakState() for _, b := range data { d.Write(b) } @@ -86,7 +86,7 @@ func Keccak256(data ...[]byte) []byte { // Keccak256Hash calculates and returns the Keccak256 hash of the input data, // converting it to an internal Hash data structure. func Keccak256Hash(data ...[]byte) (h common.Hash) { - d := sha3.NewLegacyKeccak256().(KeccakState) + d := NewKeccakState() for _, b := range data { d.Write(b) } diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index 696f403e02ab..e0e66185ce12 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -41,6 +41,13 @@ func TestKeccak256Hash(t *testing.T) { checkhash(t, "Sha3-256-array", func(in []byte) []byte { h := Keccak256Hash(in); return h[:] }, msg, exp) } +func TestKeccak256Hasher(t *testing.T) { + msg := []byte("abc") + exp, _ := hex.DecodeString("4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45") + hasher := NewKeccakState() + checkhash(t, "Sha3-256-array", func(in []byte) []byte { h := HashData(hasher, in); return h[:] }, msg, exp) +} + func TestToECDSAErrors(t *testing.T) { if _, err := HexToECDSA("0000000000000000000000000000000000000000000000000000000000000000"); err == nil { t.Fatal("HexToECDSA should've returned error") From ae95ceac4104ffc76acfe22f3a3238b6cfeb9092 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 283/479] bn256: added consensys/gurvy bn256 implementation (#21812) --- go.mod | 8 +- go.sum | 364 +++++++++++++++++++++++++++++- tests/fuzzers/bn256/bn256_fuzz.go | 71 ++++-- 3 files changed, 415 insertions(+), 28 deletions(-) diff --git a/go.mod b/go.mod index 5f25a2232fd2..0b3c9d62fe31 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb github.com/gorilla/websocket v1.4.2 - github.com/hashicorp/golang-lru v0.5.3 + github.com/hashicorp/golang-lru v0.5.4 github.com/holiman/uint256 v1.2.4 github.com/huin/goupnp v1.3.0 github.com/influxdata/influxdb v1.7.9 @@ -40,12 +40,13 @@ require ( golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce - gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772 + gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 ) require ( + github.com/consensys/gurvy v0.3.8 github.com/deckarep/golang-set v1.8.0 - github.com/dop251/goja v0.0.0-20200106141417-aaec0e7bde29 + github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 github.com/kylelemons/godebug v1.1.0 github.com/mattn/go-isatty v0.0.17 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible @@ -76,5 +77,4 @@ require ( golang.org/x/text v0.20.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gotest.tools v2.2.0+incompatible // indirect ) diff --git a/go.sum b/go.sum index 32e70b783a58..dc4d1cfba1a2 100644 --- a/go.sum +++ b/go.sum @@ -1,46 +1,132 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= +github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= +github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8= github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98 h1:7buXGE+m4OPjyo8rUJgA8RmARNMq+m99JJLR+Z+ZWN0= github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98/go.mod h1:DLTg9Gp4FAXF5EpqYBQnUeBbRsNLY7b2HR94TE5XQtE= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 h1:Eey/GGQ/E5Xp1P2Lyx1qj007hLZfbi0+CoVeJruGCtI= github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= +github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U= +github.com/consensys/bavard v0.1.8-0.20210105233146-c16790d2aa8b/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= +github.com/consensys/goff v0.3.10/go.mod h1:xTldOBEHmFiYS0gPXd3NsaEqZWlnmeWcRLWgD3ba3xc= +github.com/consensys/gurvy v0.3.8 h1:H2hvjvT2OFMgdMn5ZbhXqHt+F8DJ2clZW7Vmc0kFFxc= +github.com/consensys/gurvy v0.3.8/go.mod h1:sN75xnsiD593XnhbhvG2PkOy194pZBzqShWF/kwuW/g= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf h1:sh8rkQZavChcmakYiSlqu2425CHyFXLZZnvm7PDpU8M= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/dop251/goja v0.0.0-20200106141417-aaec0e7bde29 h1:Ewd9K+mC725sITA12QQHRqWj78NU4t7EhlFVVgdlzJg= -github.com/dop251/goja v0.0.0-20200106141417-aaec0e7bde29/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= +github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 h1:Y9vTBSsV4hSwPSj4bacAU/eSnV3dAxVpepaghAdhGoQ= +github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= +github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= +github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/ethereum/go-ethereum v1.9.25/go.mod h1:vMkFiYLHI4tgPw4k2j4MHKoovchFE8plZ0M9VMk4/oM= +github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -50,35 +136,94 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= -github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= github.com/influxdata/influxdb v1.7.9 h1:uSeBTNO4rBkbp1Be5FKRsAmglM9nlx25TzVQRQt1An4= github.com/influxdata/influxdb v1.7.9/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= +github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/karalabe/hid v1.0.0 h1:+/CIMNXhSU/zIJgnIvBD2nKHxS/bnRHhhs9xBryLpPo= github.com/karalabe/hid v1.0.0/go.mod h1:Vr51f8rUOLYrfrWDFlV12GGQgM5AT8sVh+2fY4MPeu8= +github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/kilic/bls12-381 v0.0.0-20201226121925-69dacb279461/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -88,24 +233,51 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leanovate/gopter v0.2.8/go.mod h1:gNcbPWNEWRe4lm+bycKqxUYoH5uoVje5SkOJ3uoLer8= +github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= +github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks= github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0= github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -115,63 +287,180 @@ github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/prometheus v1.7.2-0.20170814170113-3101606756c5 h1:K2PKeDFZidfjUWpXk05Gbxhwm8Rnz1l4O+u/bbbcCvc= github.com/prometheus/prometheus v1.7.2-0.20170814170113-3101606756c5/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s= +github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8= github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE= github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw= github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639dk2kqnCPPv+wNjq7Xb6EfUxe/oX0/NM= github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -181,6 +470,10 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210105210732-16f7687f5001/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -189,16 +482,60 @@ golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -209,19 +546,29 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= -gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772 h1:hhsSf/5z74Ck/DJYc+R8zpq8KGm7uJvpdLRQED/IedA= -gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= +gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 h1:a6cXbcDDUkSBlpnkWV1bJ+vv3mOgQEltEJ2rPxroVu0= +gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= @@ -229,3 +576,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/tests/fuzzers/bn256/bn256_fuzz.go b/tests/fuzzers/bn256/bn256_fuzz.go index 91ff35daaa89..3b785b3fdffd 100644 --- a/tests/fuzzers/bn256/bn256_fuzz.go +++ b/tests/fuzzers/bn256/bn256_fuzz.go @@ -24,42 +24,51 @@ import ( cloudflare "github.com/XinFinOrg/XDPoSChain/crypto/bn256/cloudflare" google "github.com/XinFinOrg/XDPoSChain/crypto/bn256/google" + gurvy "github.com/consensys/gurvy/bn256" ) -func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1) { +func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1, *gurvy.G1Affine) { _, xc, err := cloudflare.RandomG1(input) if err != nil { // insufficient input - return nil, nil + return nil, nil, nil } xg := new(google.G1) if _, err := xg.Unmarshal(xc.Marshal()); err != nil { - panic(fmt.Sprintf("Could not marshal cloudflare -> google: %v", err)) + panic(fmt.Sprintf("Could not marshal cloudflare -> google:", err)) } - return xc, xg + xs := new(gurvy.G1Affine) + if err := xs.Unmarshal(xc.Marshal()); err != nil { + panic(fmt.Sprintf("Could not marshal cloudflare -> consensys:", err)) + } + return xc, xg, xs } -func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2) { +func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2, *gurvy.G2Affine) { _, xc, err := cloudflare.RandomG2(input) if err != nil { // insufficient input - return nil, nil + return nil, nil, nil } xg := new(google.G2) if _, err := xg.Unmarshal(xc.Marshal()); err != nil { - panic(fmt.Sprintf("Could not marshal cloudflare -> google: %v", err)) + panic(fmt.Sprintf("Could not marshal cloudflare -> google:", err)) + } + xs := new(gurvy.G2Affine) + if err := xs.Unmarshal(xc.Marshal()); err != nil { + panic(fmt.Sprintf("Could not marshal cloudflare -> consensys:", err)) } - return xc, xg + return xc, xg, xs } // FuzzAdd fuzzez bn256 addition between the Google and Cloudflare libraries. func FuzzAdd(data []byte) int { input := bytes.NewReader(data) - xc, xg := getG1Points(input) + xc, xg, xs := getG1Points(input) if xc == nil { return 0 } - yc, yg := getG1Points(input) + yc, yg, ys := getG1Points(input) if yc == nil { return 0 } @@ -71,8 +80,16 @@ func FuzzAdd(data []byte) int { rg := new(google.G1) rg.Add(xg, yg) + tmpX := new(gurvy.G1Jac).FromAffine(xs) + tmpY := new(gurvy.G1Jac).FromAffine(ys) + rs := new(gurvy.G1Affine).FromJacobian(tmpX.AddAssign(tmpY)) + if !bytes.Equal(rc.Marshal(), rg.Marshal()) { - panic("add mismatch") + panic("add mismatch: cloudflare/google") + } + + if !bytes.Equal(rc.Marshal(), rs.Marshal()) { + panic("add mismatch: cloudflare/consensys") } return 1 } @@ -81,7 +98,7 @@ func FuzzAdd(data []byte) int { // libraries. func FuzzMul(data []byte) int { input := bytes.NewReader(data) - pc, pg := getG1Points(input) + pc, pg, ps := getG1Points(input) if pc == nil { return 0 } @@ -105,25 +122,43 @@ func FuzzMul(data []byte) int { rg := new(google.G1) rg.ScalarMult(pg, new(big.Int).SetBytes(buf)) + rs := new(gurvy.G1Jac) + psJac := new(gurvy.G1Jac).FromAffine(ps) + rs.ScalarMultiplication(psJac, new(big.Int).SetBytes(buf)) + rsAffine := new(gurvy.G1Affine).FromJacobian(rs) + if !bytes.Equal(rc.Marshal(), rg.Marshal()) { - panic("scalar mul mismatch") + panic("scalar mul mismatch: cloudflare/google") + } + if !bytes.Equal(rc.Marshal(), rsAffine.Marshal()) { + panic("scalar mul mismatch: cloudflare/consensys") } return 1 } func FuzzPair(data []byte) int { input := bytes.NewReader(data) - pc, pg := getG1Points(input) + pc, pg, ps := getG1Points(input) if pc == nil { return 0 } - tc, tg := getG2Points(input) + tc, tg, ts := getG2Points(input) if tc == nil { return 0 } - // Pair the two points and ensure thet result in the same output - if cloudflare.PairingCheck([]*cloudflare.G1{pc}, []*cloudflare.G2{tc}) != google.PairingCheck([]*google.G1{pg}, []*google.G2{tg}) { - panic("pair mismatch") + // Pair the two points and ensure they result in the same output + clPair := cloudflare.PairingCheck([]*cloudflare.G1{pc}, []*cloudflare.G2{tc}) + if clPair != google.PairingCheck([]*google.G1{pg}, []*google.G2{tg}) { + panic("pairing mismatch: cloudflare/google") + } + + coPair, err := gurvy.PairingCheck([]gurvy.G1Affine{*ps}, []gurvy.G2Affine{*ts}) + if err != nil { + panic(fmt.Sprintf("gurvy encountered error: %v", err)) + } + if clPair != coPair { + panic("pairing mismatch: cloudflare/consensys") } + return 1 } From 7711f4b76d630c14e3129c7d77a3cca637b19bac Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 284/479] tests/fuzzers: crypto/bn256 tests against gnark-crypto (#22755) --- go.mod | 3 +- go.sum | 359 ++---------------------------- tests/fuzzers/bn256/bn256_fuzz.go | 48 ++-- 3 files changed, 44 insertions(+), 366 deletions(-) diff --git a/go.mod b/go.mod index 0b3c9d62fe31..59e1a70eda55 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( ) require ( - github.com/consensys/gurvy v0.3.8 + github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f github.com/deckarep/golang-set v1.8.0 github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 github.com/kylelemons/godebug v1.1.0 @@ -77,4 +77,5 @@ require ( golang.org/x/text v0.20.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + gotest.tools v2.2.0+incompatible // indirect ) diff --git a/go.sum b/go.sum index dc4d1cfba1a2..87ecde5aabb8 100644 --- a/go.sum +++ b/go.sum @@ -1,132 +1,50 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= -github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= -github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= -github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8= github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98 h1:7buXGE+m4OPjyo8rUJgA8RmARNMq+m99JJLR+Z+ZWN0= github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98/go.mod h1:DLTg9Gp4FAXF5EpqYBQnUeBbRsNLY7b2HR94TE5XQtE= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 h1:Eey/GGQ/E5Xp1P2Lyx1qj007hLZfbi0+CoVeJruGCtI= github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= -github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U= -github.com/consensys/bavard v0.1.8-0.20210105233146-c16790d2aa8b/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= -github.com/consensys/goff v0.3.10/go.mod h1:xTldOBEHmFiYS0gPXd3NsaEqZWlnmeWcRLWgD3ba3xc= -github.com/consensys/gurvy v0.3.8 h1:H2hvjvT2OFMgdMn5ZbhXqHt+F8DJ2clZW7Vmc0kFFxc= -github.com/consensys/gurvy v0.3.8/go.mod h1:sN75xnsiD593XnhbhvG2PkOy194pZBzqShWF/kwuW/g= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= +github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f h1:C43yEtQ6NIf4ftFXD/V55gnGFgPbMQobd//YlnLjUJ8= +github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf h1:sh8rkQZavChcmakYiSlqu2425CHyFXLZZnvm7PDpU8M= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 h1:Y9vTBSsV4hSwPSj4bacAU/eSnV3dAxVpepaghAdhGoQ= github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= -github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= -github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/ethereum/go-ethereum v1.9.25/go.mod h1:vMkFiYLHI4tgPw4k2j4MHKoovchFE8plZ0M9VMk4/oM= -github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -136,94 +54,36 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= -github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= github.com/influxdata/influxdb v1.7.9 h1:uSeBTNO4rBkbp1Be5FKRsAmglM9nlx25TzVQRQt1An4= github.com/influxdata/influxdb v1.7.9/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= -github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/karalabe/hid v1.0.0 h1:+/CIMNXhSU/zIJgnIvBD2nKHxS/bnRHhhs9xBryLpPo= github.com/karalabe/hid v1.0.0/go.mod h1:Vr51f8rUOLYrfrWDFlV12GGQgM5AT8sVh+2fY4MPeu8= -github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= -github.com/kilic/bls12-381 v0.0.0-20201226121925-69dacb279461/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -233,51 +93,26 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/leanovate/gopter v0.2.8/go.mod h1:gNcbPWNEWRe4lm+bycKqxUYoH5uoVje5SkOJ3uoLer8= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= -github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks= github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0= github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -287,180 +122,73 @@ github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/prometheus v1.7.2-0.20170814170113-3101606756c5 h1:K2PKeDFZidfjUWpXk05Gbxhwm8Rnz1l4O+u/bbbcCvc= github.com/prometheus/prometheus v1.7.2-0.20170814170113-3101606756c5/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s= -github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8= github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE= github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw= github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639dk2kqnCPPv+wNjq7Xb6EfUxe/oX0/NM= github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= -github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -470,44 +198,26 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210105210732-16f7687f5001/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -515,27 +225,6 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -546,29 +235,19 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 h1:a6cXbcDDUkSBlpnkWV1bJ+vv3mOgQEltEJ2rPxroVu0= gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= @@ -576,8 +255,4 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= diff --git a/tests/fuzzers/bn256/bn256_fuzz.go b/tests/fuzzers/bn256/bn256_fuzz.go index 3b785b3fdffd..24b7b9e23892 100644 --- a/tests/fuzzers/bn256/bn256_fuzz.go +++ b/tests/fuzzers/bn256/bn256_fuzz.go @@ -24,10 +24,10 @@ import ( cloudflare "github.com/XinFinOrg/XDPoSChain/crypto/bn256/cloudflare" google "github.com/XinFinOrg/XDPoSChain/crypto/bn256/google" - gurvy "github.com/consensys/gurvy/bn256" + "github.com/consensys/gnark-crypto/ecc/bn254" ) -func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1, *gurvy.G1Affine) { +func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1, *bn254.G1Affine) { _, xc, err := cloudflare.RandomG1(input) if err != nil { // insufficient input @@ -35,16 +35,16 @@ func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1, *gurvy.G1Affine) } xg := new(google.G1) if _, err := xg.Unmarshal(xc.Marshal()); err != nil { - panic(fmt.Sprintf("Could not marshal cloudflare -> google:", err)) + panic(fmt.Sprintf("Could not marshal cloudflare -> google: %v", err)) } - xs := new(gurvy.G1Affine) + xs := new(bn254.G1Affine) if err := xs.Unmarshal(xc.Marshal()); err != nil { - panic(fmt.Sprintf("Could not marshal cloudflare -> consensys:", err)) + panic(fmt.Sprintf("Could not marshal cloudflare -> gnark: %v", err)) } return xc, xg, xs } -func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2, *gurvy.G2Affine) { +func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2, *bn254.G2Affine) { _, xc, err := cloudflare.RandomG2(input) if err != nil { // insufficient input @@ -52,11 +52,11 @@ func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2, *gurvy.G2Affine) } xg := new(google.G2) if _, err := xg.Unmarshal(xc.Marshal()); err != nil { - panic(fmt.Sprintf("Could not marshal cloudflare -> google:", err)) + panic(fmt.Sprintf("Could not marshal cloudflare -> google: %v", err)) } - xs := new(gurvy.G2Affine) + xs := new(bn254.G2Affine) if err := xs.Unmarshal(xc.Marshal()); err != nil { - panic(fmt.Sprintf("Could not marshal cloudflare -> consensys:", err)) + panic(fmt.Sprintf("Could not marshal cloudflare -> gnark: %v", err)) } return xc, xg, xs } @@ -80,16 +80,16 @@ func FuzzAdd(data []byte) int { rg := new(google.G1) rg.Add(xg, yg) - tmpX := new(gurvy.G1Jac).FromAffine(xs) - tmpY := new(gurvy.G1Jac).FromAffine(ys) - rs := new(gurvy.G1Affine).FromJacobian(tmpX.AddAssign(tmpY)) + tmpX := new(bn254.G1Jac).FromAffine(xs) + tmpY := new(bn254.G1Jac).FromAffine(ys) + rs := new(bn254.G1Affine).FromJacobian(tmpX.AddAssign(tmpY)) if !bytes.Equal(rc.Marshal(), rg.Marshal()) { panic("add mismatch: cloudflare/google") } if !bytes.Equal(rc.Marshal(), rs.Marshal()) { - panic("add mismatch: cloudflare/consensys") + panic("add mismatch: cloudflare/gnark") } return 1 } @@ -122,16 +122,16 @@ func FuzzMul(data []byte) int { rg := new(google.G1) rg.ScalarMult(pg, new(big.Int).SetBytes(buf)) - rs := new(gurvy.G1Jac) - psJac := new(gurvy.G1Jac).FromAffine(ps) + rs := new(bn254.G1Jac) + psJac := new(bn254.G1Jac).FromAffine(ps) rs.ScalarMultiplication(psJac, new(big.Int).SetBytes(buf)) - rsAffine := new(gurvy.G1Affine).FromJacobian(rs) + rsAffine := new(bn254.G1Affine).FromJacobian(rs) if !bytes.Equal(rc.Marshal(), rg.Marshal()) { panic("scalar mul mismatch: cloudflare/google") } if !bytes.Equal(rc.Marshal(), rsAffine.Marshal()) { - panic("scalar mul mismatch: cloudflare/consensys") + panic("scalar mul mismatch: cloudflare/gnark") } return 1 } @@ -146,18 +146,20 @@ func FuzzPair(data []byte) int { if tc == nil { return 0 } + // Pair the two points and ensure they result in the same output - clPair := cloudflare.PairingCheck([]*cloudflare.G1{pc}, []*cloudflare.G2{tc}) - if clPair != google.PairingCheck([]*google.G1{pg}, []*google.G2{tg}) { + clPair := cloudflare.Pair(pc, tc).Marshal() + gPair := google.Pair(pg, tg).Marshal() + if !bytes.Equal(clPair, gPair) { panic("pairing mismatch: cloudflare/google") } - coPair, err := gurvy.PairingCheck([]gurvy.G1Affine{*ps}, []gurvy.G2Affine{*ts}) + cPair, err := bn254.Pair([]bn254.G1Affine{*ps}, []bn254.G2Affine{*ts}) if err != nil { - panic(fmt.Sprintf("gurvy encountered error: %v", err)) + panic(fmt.Sprintf("gnark/bn254 encountered error: %v", err)) } - if clPair != coPair { - panic("pairing mismatch: cloudflare/consensys") + if !bytes.Equal(clPair, cPair.Marshal()) { + panic("pairing mismatch: cloudflare/gnark") } return 1 From 5ee26e04ed4eb02f453880f68abf69ee1bc194d9 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 285/479] crypto/secp256k1: fix undefined behavior in BitCurve.Add (#22621) --- go.mod | 1 + go.sum | 2 ++ tests/fuzzers/secp256k1/secp_fuzzer.go | 50 ++++++++++++++++++++++++++ tests/fuzzers/secp256k1/secp_test.go | 8 +++++ 4 files changed, 61 insertions(+) create mode 100644 tests/fuzzers/secp256k1/secp_fuzzer.go create mode 100644 tests/fuzzers/secp256k1/secp_test.go diff --git a/go.mod b/go.mod index 59e1a70eda55..ded932aea1db 100644 --- a/go.mod +++ b/go.mod @@ -47,6 +47,7 @@ require ( github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f github.com/deckarep/golang-set v1.8.0 github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 + github.com/google/gofuzz v1.2.0 github.com/kylelemons/godebug v1.1.0 github.com/mattn/go-isatty v0.0.17 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible diff --git a/go.sum b/go.sum index 87ecde5aabb8..8861e9e11bc0 100644 --- a/go.sum +++ b/go.sum @@ -63,6 +63,8 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= diff --git a/tests/fuzzers/secp256k1/secp_fuzzer.go b/tests/fuzzers/secp256k1/secp_fuzzer.go new file mode 100644 index 000000000000..b65fec52ed4c --- /dev/null +++ b/tests/fuzzers/secp256k1/secp_fuzzer.go @@ -0,0 +1,50 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// build +gofuzz + +package secp256k1 + +import ( + "fmt" + + "github.com/btcsuite/btcd/btcec" + "github.com/XinFinOrg/XDPoSChain/crypto/secp256k1" + fuzz "github.com/google/gofuzz" +) + +func Fuzz(input []byte) int { + var ( + fuzzer = fuzz.NewFromGoFuzz(input) + curveA = secp256k1.S256() + curveB = btcec.S256() + dataP1 []byte + dataP2 []byte + ) + // first point + fuzzer.Fuzz(&dataP1) + x1, y1 := curveB.ScalarBaseMult(dataP1) + // second point + fuzzer.Fuzz(&dataP2) + x2, y2 := curveB.ScalarBaseMult(dataP2) + resAX, resAY := curveA.Add(x1, y1, x2, y2) + resBX, resBY := curveB.Add(x1, y1, x2, y2) + if resAX.Cmp(resBX) != 0 || resAY.Cmp(resBY) != 0 { + fmt.Printf("%s %s %s %s\n", x1, y1, x2, y2) + panic(fmt.Sprintf("Addition failed: geth: %s %s btcd: %s %s", resAX, resAY, resBX, resBY)) + } + return 0 +} diff --git a/tests/fuzzers/secp256k1/secp_test.go b/tests/fuzzers/secp256k1/secp_test.go new file mode 100644 index 000000000000..76bae87086f5 --- /dev/null +++ b/tests/fuzzers/secp256k1/secp_test.go @@ -0,0 +1,8 @@ +package secp256k1 + +import "testing" + +func TestFuzzer(t *testing.T) { + test := "00000000N0000000/R00000000000000000U0000S0000000mkhP000000000000000U" + Fuzz([]byte(test)) +} From 9dab065373b6adbf8c1a942a62800dfb17c0ed71 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 286/479] crypto: gofuzz build directives (#23137) --- crypto/signature_cgo.go | 4 ++-- crypto/signature_nocgo.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crypto/signature_cgo.go b/crypto/signature_cgo.go index fcf0fa9d8ad4..53f014e87576 100644 --- a/crypto/signature_cgo.go +++ b/crypto/signature_cgo.go @@ -14,8 +14,8 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -//go:build !nacl && !js && cgo -// +build !nacl,!js,cgo +//go:build !nacl && !js && cgo && !gofuzz +// +build !nacl,!js,cgo,!gofuzz package crypto diff --git a/crypto/signature_nocgo.go b/crypto/signature_nocgo.go index e52f50409c49..66836e5f41cf 100644 --- a/crypto/signature_nocgo.go +++ b/crypto/signature_nocgo.go @@ -14,8 +14,8 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -//go:build nacl || js || !cgo -// +build nacl js !cgo +//go:build nacl || js || !cgo || gofuzz +// +build nacl js !cgo gofuzz package crypto From 146bc2be7be3cf49841f5eb4894d57ca56c076f6 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 287/479] crypto: add go:build lines (#23468) --- crypto/blake2b/blake2bAVX2_amd64.go | 1 + crypto/blake2b/blake2b_amd64.go | 1 + crypto/blake2b/blake2b_f_fuzz.go | 4 +++- crypto/blake2b/blake2b_ref.go | 1 + crypto/blake2b/register.go | 1 + crypto/bn256/bn256_fast.go | 2 ++ crypto/bn256/cloudflare/gfp_generic.go | 1 + 7 files changed, 10 insertions(+), 1 deletion(-) diff --git a/crypto/blake2b/blake2bAVX2_amd64.go b/crypto/blake2b/blake2bAVX2_amd64.go index 0d52b186990b..3a85d0e73a6b 100644 --- a/crypto/blake2b/blake2bAVX2_amd64.go +++ b/crypto/blake2b/blake2bAVX2_amd64.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.7 && amd64 && !gccgo && !appengine // +build go1.7,amd64,!gccgo,!appengine package blake2b diff --git a/crypto/blake2b/blake2b_amd64.go b/crypto/blake2b/blake2b_amd64.go index 4dbe90da8f1a..a318b2b61767 100644 --- a/crypto/blake2b/blake2b_amd64.go +++ b/crypto/blake2b/blake2b_amd64.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !go1.7 && amd64 && !gccgo && !appengine // +build !go1.7,amd64,!gccgo,!appengine package blake2b diff --git a/crypto/blake2b/blake2b_f_fuzz.go b/crypto/blake2b/blake2b_f_fuzz.go index ab73342803f8..2a1c80ee093b 100644 --- a/crypto/blake2b/blake2b_f_fuzz.go +++ b/crypto/blake2b/blake2b_f_fuzz.go @@ -1,4 +1,6 @@ -// +build gofuzz +// Only enable fuzzer on platforms with AVX enabled +//go:build go1.7 && amd64 && !gccgo && !appengine +// +build go1.7,amd64,!gccgo,!appengine package blake2b diff --git a/crypto/blake2b/blake2b_ref.go b/crypto/blake2b/blake2b_ref.go index 9d0ade473a14..095c71a648c6 100644 --- a/crypto/blake2b/blake2b_ref.go +++ b/crypto/blake2b/blake2b_ref.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !amd64 || appengine || gccgo // +build !amd64 appengine gccgo package blake2b diff --git a/crypto/blake2b/register.go b/crypto/blake2b/register.go index efd689af4b49..9d8633963cb6 100644 --- a/crypto/blake2b/register.go +++ b/crypto/blake2b/register.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.9 // +build go1.9 package blake2b diff --git a/crypto/bn256/bn256_fast.go b/crypto/bn256/bn256_fast.go index ef452857db27..9ea5d8fbd577 100644 --- a/crypto/bn256/bn256_fast.go +++ b/crypto/bn256/bn256_fast.go @@ -14,6 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . +//go:build amd64 || arm64 // +build amd64 arm64 // Package bn256 implements the Optimal Ate pairing over a 256-bit Barreto-Naehrig curve. @@ -22,6 +23,7 @@ package bn256 import ( bn256cf "github.com/XinFinOrg/XDPoSChain/crypto/bn256/cloudflare" ) + // G1 is an abstract cyclic group. The zero value is suitable for use as the // output of an operation, but cannot be used as an input. type G1 = bn256cf.G1 diff --git a/crypto/bn256/cloudflare/gfp_generic.go b/crypto/bn256/cloudflare/gfp_generic.go index 8e6be9596194..7742dda4c945 100644 --- a/crypto/bn256/cloudflare/gfp_generic.go +++ b/crypto/bn256/cloudflare/gfp_generic.go @@ -1,3 +1,4 @@ +//go:build (!amd64 && !arm64) || generic // +build !amd64,!arm64 generic package bn256 From 949fa6358e86db81b0ae84715e776375a335011f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 288/479] crypto/ecies: use AES-192 for curve P384 (#24139) Using curve P384 for encryption causes the error "ecies: shared key params are too big". Also, readme.md says curve P384 should use AES192 not AES256. Co-authored-by: Marius van der Wijden --- crypto/ecies/ecies_test.go | 2 +- crypto/ecies/params.go | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/crypto/ecies/ecies_test.go b/crypto/ecies/ecies_test.go index a1a7604ba8e2..83c08f3e57a7 100644 --- a/crypto/ecies/ecies_test.go +++ b/crypto/ecies/ecies_test.go @@ -279,7 +279,7 @@ var testCases = []testCase{ { Curve: elliptic.P384(), Name: "P384", - Expected: ECIES_AES256_SHA384, + Expected: ECIES_AES192_SHA384, }, { Curve: elliptic.P521(), diff --git a/crypto/ecies/params.go b/crypto/ecies/params.go index 293f2e83b117..49bc44735599 100644 --- a/crypto/ecies/params.go +++ b/crypto/ecies/params.go @@ -81,6 +81,14 @@ var ( KeyLen: 16, } + ECIES_AES192_SHA384 = &ECIESParams{ + Hash: sha512.New384, + hashAlgo: crypto.SHA384, + Cipher: aes.NewCipher, + BlockSize: aes.BlockSize, + KeyLen: 24, + } + ECIES_AES256_SHA256 = &ECIESParams{ Hash: sha256.New, hashAlgo: crypto.SHA256, @@ -109,7 +117,7 @@ var ( var paramsFromCurve = map[elliptic.Curve]*ECIESParams{ ethcrypto.S256(): ECIES_AES128_SHA256, elliptic.P256(): ECIES_AES128_SHA256, - elliptic.P384(): ECIES_AES256_SHA384, + elliptic.P384(): ECIES_AES192_SHA384, elliptic.P521(): ECIES_AES256_SHA512, } From cb3edac2c7bf7f60068750dd21938df07028cea7 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 289/479] crypto: use btcec/v2 for no-cgo (#24533) --- crypto/signature_nocgo.go | 72 +++++++++++++++++++------- go.mod | 2 + go.sum | 6 +++ tests/fuzzers/secp256k1/secp_fuzzer.go | 2 +- 4 files changed, 62 insertions(+), 20 deletions(-) diff --git a/crypto/signature_nocgo.go b/crypto/signature_nocgo.go index 66836e5f41cf..3e48e51e84eb 100644 --- a/crypto/signature_nocgo.go +++ b/crypto/signature_nocgo.go @@ -24,55 +24,72 @@ import ( "crypto/elliptic" "errors" "fmt" - "math/big" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" + btc_ecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa" ) // Ecrecover returns the uncompressed public key that created the given signature. func Ecrecover(hash, sig []byte) ([]byte, error) { - pub, err := SigToPub(hash, sig) + pub, err := sigToPub(hash, sig) if err != nil { return nil, err } - bytes := (*btcec.PublicKey)(pub).SerializeUncompressed() + bytes := pub.SerializeUncompressed() return bytes, err } -// SigToPub returns the public key that created the given signature. -func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { +func sigToPub(hash, sig []byte) (*btcec.PublicKey, error) { + if len(sig) != SignatureLength { + return nil, errors.New("invalid signature") + } // Convert to btcec input format with 'recovery id' v at the beginning. btcsig := make([]byte, SignatureLength) - btcsig[0] = sig[64] + 27 + btcsig[0] = sig[RecoveryIDOffset] + 27 copy(btcsig[1:], sig) - pub, _, err := btcec.RecoverCompact(btcec.S256(), btcsig, hash) - return (*ecdsa.PublicKey)(pub), err + pub, _, err := btc_ecdsa.RecoverCompact(btcsig, hash) + return pub, err +} + +// SigToPub returns the public key that created the given signature. +func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { + pub, err := sigToPub(hash, sig) + if err != nil { + return nil, err + } + return pub.ToECDSA(), nil } // Sign calculates an ECDSA signature. // // This function is susceptible to chosen plaintext attacks that can leak // information about the private key that is used for signing. Callers must -// be aware that the given hash cannot be chosen by an adversery. Common +// be aware that the given hash cannot be chosen by an adversary. Common // solution is to hash any input before calculating the signature. // // The produced signature is in the [R || S || V] format where V is 0 or 1. func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) { - if len(hash) != DigestLength { + if len(hash) != 32 { return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash)) } if prv.Curve != btcec.S256() { return nil, fmt.Errorf("private key curve is not secp256k1") } - sig, err := btcec.SignCompact(btcec.S256(), (*btcec.PrivateKey)(prv), hash, false) + // ecdsa.PrivateKey -> btcec.PrivateKey + var priv btcec.PrivateKey + if overflow := priv.Key.SetByteSlice(prv.D.Bytes()); overflow || priv.Key.IsZero() { + return nil, fmt.Errorf("invalid private key") + } + defer priv.Zero() + sig, err := btc_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey if err != nil { return nil, err } // Convert to Ethereum signature format with 'recovery id' v at the end. v := sig[0] - 27 copy(sig, sig[1:]) - sig[64] = v + sig[RecoveryIDOffset] = v return sig, nil } @@ -83,13 +100,20 @@ func VerifySignature(pubkey, hash, signature []byte) bool { if len(signature) != 64 { return false } - sig := &btcec.Signature{R: new(big.Int).SetBytes(signature[:32]), S: new(big.Int).SetBytes(signature[32:])} - key, err := btcec.ParsePubKey(pubkey, btcec.S256()) + var r, s btcec.ModNScalar + if r.SetByteSlice(signature[:32]) { + return false // overflow + } + if s.SetByteSlice(signature[32:]) { + return false + } + sig := btc_ecdsa.NewSignature(&r, &s) + key, err := btcec.ParsePubKey(pubkey) if err != nil { return false } // Reject malleable signatures. libsecp256k1 does this check but btcec doesn't. - if sig.S.Cmp(secp256k1_halfN) > 0 { + if s.IsOverHalfOrder() { return false } return sig.Verify(hash, key) @@ -100,16 +124,26 @@ func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) { if len(pubkey) != 33 { return nil, errors.New("invalid compressed public key length") } - key, err := btcec.ParsePubKey(pubkey, btcec.S256()) + key, err := btcec.ParsePubKey(pubkey) if err != nil { return nil, err } return key.ToECDSA(), nil } -// CompressPubkey encodes a public key to the 33-byte compressed format. +// CompressPubkey encodes a public key to the 33-byte compressed format. The +// provided PublicKey must be valid. Namely, the coordinates must not be larger +// than 32 bytes each, they must be less than the field prime, and it must be a +// point on the secp256k1 curve. This is the case for a PublicKey constructed by +// elliptic.Unmarshal (see UnmarshalPubkey), or by ToECDSA and ecdsa.GenerateKey +// when constructing a PrivateKey. func CompressPubkey(pubkey *ecdsa.PublicKey) []byte { - return (*btcec.PublicKey)(pubkey).SerializeCompressed() + // NOTE: the coordinates may be validated with + // btcec.ParsePubKey(FromECDSAPub(pubkey)) + var x, y btcec.FieldVal + x.SetByteSlice(pubkey.X.Bytes()) + y.SetByteSlice(pubkey.Y.Bytes()) + return btcec.NewPublicKey(&x, &y).SerializeCompressed() } // S256 returns an instance of the secp256k1 curve. diff --git a/go.mod b/go.mod index ded932aea1db..daa1f2ce7fd4 100644 --- a/go.mod +++ b/go.mod @@ -44,6 +44,7 @@ require ( ) require ( + github.com/btcsuite/btcd/btcec/v2 v2.3.4 github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f github.com/deckarep/golang-set v1.8.0 github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 @@ -59,6 +60,7 @@ require ( github.com/StackExchange/wmi v1.2.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/dlclark/regexp2 v1.10.0 // indirect github.com/go-ole/go-ole v1.2.5 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect diff --git a/go.sum b/go.sum index 8861e9e11bc0..5ffc6217fb07 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,8 @@ github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98 h1:7buXGE+ github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98/go.mod h1:DLTg9Gp4FAXF5EpqYBQnUeBbRsNLY7b2HR94TE5XQtE= github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 h1:Eey/GGQ/E5Xp1P2Lyx1qj007hLZfbi0+CoVeJruGCtI= github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= +github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= +github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -25,6 +27,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf h1:sh8rkQZavChcmakYiSlqu2425CHyFXLZZnvm7PDpU8M= diff --git a/tests/fuzzers/secp256k1/secp_fuzzer.go b/tests/fuzzers/secp256k1/secp_fuzzer.go index b65fec52ed4c..84a7eebe1bf7 100644 --- a/tests/fuzzers/secp256k1/secp_fuzzer.go +++ b/tests/fuzzers/secp256k1/secp_fuzzer.go @@ -21,8 +21,8 @@ package secp256k1 import ( "fmt" - "github.com/btcsuite/btcd/btcec" "github.com/XinFinOrg/XDPoSChain/crypto/secp256k1" + "github.com/btcsuite/btcd/btcec/v2" fuzz "github.com/google/gofuzz" ) From 797efe74afc2ec1f4816c55daf09a89283a2ef71 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 290/479] crypto: more linters and fix typo (#24783) --- crypto/blake2b/blake2b.go | 2 ++ crypto/blake2b/blake2b_generic.go | 1 + crypto/blake2b/blake2b_test.go | 8 -------- crypto/signature_cgo.go | 2 +- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/crypto/blake2b/blake2b.go b/crypto/blake2b/blake2b.go index 5da50cab6f00..7ecaab813999 100644 --- a/crypto/blake2b/blake2b.go +++ b/crypto/blake2b/blake2b.go @@ -302,6 +302,7 @@ func appendUint64(b []byte, x uint64) []byte { return append(b, a[:]...) } +//nolint:unused,deadcode func appendUint32(b []byte, x uint32) []byte { var a [4]byte binary.BigEndian.PutUint32(a[:], x) @@ -313,6 +314,7 @@ func consumeUint64(b []byte) ([]byte, uint64) { return b[8:], x } +//nolint:unused,deadcode func consumeUint32(b []byte) ([]byte, uint32) { x := binary.BigEndian.Uint32(b) return b[4:], x diff --git a/crypto/blake2b/blake2b_generic.go b/crypto/blake2b/blake2b_generic.go index 35c40cc924f8..61e678fdf576 100644 --- a/crypto/blake2b/blake2b_generic.go +++ b/crypto/blake2b/blake2b_generic.go @@ -25,6 +25,7 @@ var precomputed = [10][16]byte{ {10, 8, 7, 1, 2, 4, 6, 5, 15, 9, 3, 13, 11, 14, 12, 0}, } +// nolint:unused,deadcode func hashBlocksGeneric(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) { var m [16]uint64 c0, c1 := c[0], c[1] diff --git a/crypto/blake2b/blake2b_test.go b/crypto/blake2b/blake2b_test.go index 9e7297da160f..9d24444a27b7 100644 --- a/crypto/blake2b/blake2b_test.go +++ b/crypto/blake2b/blake2b_test.go @@ -14,14 +14,6 @@ import ( "testing" ) -func fromHex(s string) []byte { - b, err := hex.DecodeString(s) - if err != nil { - panic(err) - } - return b -} - func TestHashes(t *testing.T) { defer func(sse4, avx, avx2 bool) { useSSE4, useAVX, useAVX2 = sse4, avx, avx2 diff --git a/crypto/signature_cgo.go b/crypto/signature_cgo.go index 53f014e87576..c33cd0094b33 100644 --- a/crypto/signature_cgo.go +++ b/crypto/signature_cgo.go @@ -48,7 +48,7 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { // // This function is susceptible to chosen plaintext attacks that can leak // information about the private key that is used for signing. Callers must -// be aware that the given digest cannot be chosen by an adversery. Common +// be aware that the given digest cannot be chosen by an adversary. Common // solution is to hash any input before calculating the signature. // // The produced signature is in the [R || S || V] format where V is 0 or 1. From bedd5710916a8f1a75f46cc8cdd138a7c14ff3f9 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 291/479] crypto/kzg4844: pull in the C and Go libs for KZG cryptography (#27155) --- cmd/XDC/main.go | 1 + cmd/utils/flags.go | 16 +- crypto/kzg4844/kzg4844.go | 110 + crypto/kzg4844/kzg4844_ckzg_cgo.go | 119 + crypto/kzg4844/kzg4844_ckzg_nocgo.go | 62 + crypto/kzg4844/kzg4844_gokzg.go | 98 + crypto/kzg4844/kzg4844_test.go | 187 + crypto/kzg4844/trusted_setup.json | 8265 ++++++++++++++++++++++++++ go.mod | 9 +- go.sum | 43 +- 10 files changed, 8883 insertions(+), 27 deletions(-) create mode 100644 crypto/kzg4844/kzg4844.go create mode 100644 crypto/kzg4844/kzg4844_ckzg_cgo.go create mode 100644 crypto/kzg4844/kzg4844_ckzg_nocgo.go create mode 100644 crypto/kzg4844/kzg4844_gokzg.go create mode 100644 crypto/kzg4844/kzg4844_test.go create mode 100644 crypto/kzg4844/trusted_setup.json diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go index d57040b8cc9b..00f87d32f69b 100644 --- a/cmd/XDC/main.go +++ b/cmd/XDC/main.go @@ -98,6 +98,7 @@ var ( //utils.TrieCacheGenFlag, utils.CacheLogSizeFlag, utils.FDLimitFlag, + utils.CryptoKZGFlag, utils.ListenPortFlag, utils.MaxPeersFlag, utils.MaxPendingPeersFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index fc8505b16562..0efc867a273e 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -41,6 +41,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/txpool" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/crypto/kzg4844" "github.com/XinFinOrg/XDPoSChain/eth/downloader" "github.com/XinFinOrg/XDPoSChain/eth/ethconfig" "github.com/XinFinOrg/XDPoSChain/eth/filters" @@ -310,6 +311,12 @@ var ( Usage: "Raise the open file descriptor resource limit (default = system fd limit)", Category: flags.PerfCategory, } + CryptoKZGFlag = &cli.StringFlag{ + Name: "crypto-kzg", + Usage: "KZG library implementation to use; gokzg (recommended) or ckzg", + Value: "gokzg", + Category: flags.PerfCategory, + } // Miner settings MiningEnabledFlag = &cli.BoolFlag{ @@ -1445,7 +1452,14 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { cfg.GasPrice = big.NewInt(1) } } - // TODO(fjl): move trie cache generations into config + // Set any dangling config values + if ctx.String(CryptoKZGFlag.Name) != "gokzg" && ctx.String(CryptoKZGFlag.Name) != "ckzg" { + Fatalf("--%s flag must be 'gokzg' or 'ckzg'", CryptoKZGFlag.Name) + } + log.Info("Initializing the KZG library", "backend", ctx.String(CryptoKZGFlag.Name)) + if err := kzg4844.UseCKZG(ctx.String(CryptoKZGFlag.Name) == "ckzg"); err != nil { + Fatalf("Failed to set KZG library implementation to %s: %v", ctx.String(CryptoKZGFlag.Name), err) + } } // SetupNetwork configures the system for either the main net or some test network. diff --git a/crypto/kzg4844/kzg4844.go b/crypto/kzg4844/kzg4844.go new file mode 100644 index 000000000000..d54376965fe2 --- /dev/null +++ b/crypto/kzg4844/kzg4844.go @@ -0,0 +1,110 @@ +// Copyright 2023 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package kzg4844 implements the KZG crypto for EIP-4844. +package kzg4844 + +import ( + "embed" + "errors" + "sync/atomic" +) + +//go:embed trusted_setup.json +var content embed.FS + +// Blob represents a 4844 data blob. +type Blob [131072]byte + +// Commitment is a serialized commitment to a polynomial. +type Commitment [48]byte + +// Proof is a serialized commitment to the quotient polynomial. +type Proof [48]byte + +// Point is a BLS field element. +type Point [32]byte + +// Claim is a claimed evaluation value in a specific point. +type Claim [32]byte + +// useCKZG controls whether the cryptography should use the Go or C backend. +var useCKZG atomic.Bool + +// UseCKZG can be called to switch the default Go implementation of KZG to the C +// library if fo some reason the user wishes to do so (e.g. consensus bug in one +// or the other). +func UseCKZG(use bool) error { + if use && !ckzgAvailable { + return errors.New("CKZG unavailable on your platform") + } + useCKZG.Store(use) + + // Initializing the library can take 2-4 seconds - and can potentially crash + // on CKZG and non-ADX CPUs - so might as well so it now and don't wait until + // a crpyto operation is actually needed live. + if use { + ckzgIniter.Do(ckzgInit) + } else { + gokzgIniter.Do(gokzgInit) + } + return nil +} + +// BlobToCommitment creates a small commitment out of a data blob. +func BlobToCommitment(blob Blob) (Commitment, error) { + if useCKZG.Load() { + return ckzgBlobToCommitment(blob) + } + return gokzgBlobToCommitment(blob) +} + +// ComputeProof computes the KZG proof at the given point for the polynomial +// represented by the blob. +func ComputeProof(blob Blob, point Point) (Proof, Claim, error) { + if useCKZG.Load() { + return ckzgComputeProof(blob, point) + } + return gokzgComputeProof(blob, point) +} + +// VerifyProof verifies the KZG proof that the polynomial represented by the blob +// evaluated at the given point is the claimed value. +func VerifyProof(commitment Commitment, point Point, claim Claim, proof Proof) error { + if useCKZG.Load() { + return ckzgVerifyProof(commitment, point, claim, proof) + } + return gokzgVerifyProof(commitment, point, claim, proof) +} + +// ComputeBlobProof returns the KZG proof that is used to verify the blob against +// the commitment. +// +// This method does not verify that the commitment is correct with respect to blob. +func ComputeBlobProof(blob Blob, commitment Commitment) (Proof, error) { + if useCKZG.Load() { + return ckzgComputeBlobProof(blob, commitment) + } + return gokzgComputeBlobProof(blob, commitment) +} + +// VerifyBlobProof verifies that the blob data corresponds to the provided commitment. +func VerifyBlobProof(blob Blob, commitment Commitment, proof Proof) error { + if useCKZG.Load() { + return ckzgVerifyBlobProof(blob, commitment, proof) + } + return gokzgVerifyBlobProof(blob, commitment, proof) +} diff --git a/crypto/kzg4844/kzg4844_ckzg_cgo.go b/crypto/kzg4844/kzg4844_ckzg_cgo.go new file mode 100644 index 000000000000..9ba9e610b72a --- /dev/null +++ b/crypto/kzg4844/kzg4844_ckzg_cgo.go @@ -0,0 +1,119 @@ +// Copyright 2023 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//go:build ckzg && !nacl && !js && cgo && !gofuzz + +package kzg4844 + +import ( + "encoding/json" + "errors" + "sync" + + gokzg4844 "github.com/crate-crypto/go-kzg-4844" + ckzg4844 "github.com/ethereum/c-kzg-4844/bindings/go" + "github.com/XinFinOrg/XDPoSChain/common/hexutil" +) + +// ckzgAvailable signals whether the library was compiled into Geth. +const ckzgAvailable = true + +// ckzgIniter ensures that we initialize the KZG library once before using it. +var ckzgIniter sync.Once + +// ckzgInit initializes the KZG library with the provided trusted setup. +func ckzgInit() { + config, err := content.ReadFile("trusted_setup.json") + if err != nil { + panic(err) + } + params := new(gokzg4844.JSONTrustedSetup) + if err = json.Unmarshal(config, params); err != nil { + panic(err) + } + if err = gokzg4844.CheckTrustedSetupIsWellFormed(params); err != nil { + panic(err) + } + g1s := make([]byte, len(params.SetupG1)*(len(params.SetupG1[0])-2)/2) + for i, g1 := range params.SetupG1 { + copy(g1s[i*(len(g1)-2)/2:], hexutil.MustDecode(g1)) + } + g2s := make([]byte, len(params.SetupG2)*(len(params.SetupG2[0])-2)/2) + for i, g2 := range params.SetupG2 { + copy(g2s[i*(len(g2)-2)/2:], hexutil.MustDecode(g2)) + } + if err = ckzg4844.LoadTrustedSetup(g1s, g2s); err != nil { + panic(err) + } +} + +// ckzgBlobToCommitment creates a small commitment out of a data blob. +func ckzgBlobToCommitment(blob Blob) (Commitment, error) { + ckzgIniter.Do(ckzgInit) + + commitment, err := ckzg4844.BlobToKZGCommitment((ckzg4844.Blob)(blob)) + if err != nil { + return Commitment{}, err + } + return (Commitment)(commitment), nil +} + +// ckzgComputeProof computes the KZG proof at the given point for the polynomial +// represented by the blob. +func ckzgComputeProof(blob Blob, point Point) (Proof, Claim, error) { + proof, claim, err := ckzg4844.ComputeKZGProof((ckzg4844.Blob)(blob), (ckzg4844.Bytes32)(point)) + if err != nil { + return Proof{}, Claim{}, err + } + return (Proof)(proof), (Claim)(claim), nil +} + +// ckzgVerifyProof verifies the KZG proof that the polynomial represented by the blob +// evaluated at the given point is the claimed value. +func ckzgVerifyProof(commitment Commitment, point Point, claim Claim, proof Proof) error { + valid, err := ckzg4844.VerifyKZGProof((ckzg4844.Bytes48)(commitment), (ckzg4844.Bytes32)(point), (ckzg4844.Bytes32)(claim), (ckzg4844.Bytes48)(proof)) + if err != nil { + return err + } + if !valid { + return errors.New("invalid proof") + } + return nil +} + +// ckzgComputeBlobProof returns the KZG proof that is used to verify the blob against +// the commitment. +// +// This method does not verify that the commitment is correct with respect to blob. +func ckzgComputeBlobProof(blob Blob, commitment Commitment) (Proof, error) { + proof, err := ckzg4844.ComputeBlobKZGProof((ckzg4844.Blob)(blob), (ckzg4844.Bytes48)(commitment)) + if err != nil { + return Proof{}, err + } + return (Proof)(proof), nil +} + +// ckzgVerifyBlobProof verifies that the blob data corresponds to the provided commitment. +func ckzgVerifyBlobProof(blob Blob, commitment Commitment, proof Proof) error { + valid, err := ckzg4844.VerifyBlobKZGProof((ckzg4844.Blob)(blob), (ckzg4844.Bytes48)(commitment), (ckzg4844.Bytes48)(proof)) + if err != nil { + return err + } + if !valid { + return errors.New("invalid proof") + } + return nil +} diff --git a/crypto/kzg4844/kzg4844_ckzg_nocgo.go b/crypto/kzg4844/kzg4844_ckzg_nocgo.go new file mode 100644 index 000000000000..ed840c75bb68 --- /dev/null +++ b/crypto/kzg4844/kzg4844_ckzg_nocgo.go @@ -0,0 +1,62 @@ +// Copyright 2023 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//go:build !ckzg || nacl || js || !cgo || gofuzz + +package kzg4844 + +import "sync" + +// ckzgAvailable signals whether the library was compiled into Geth. +const ckzgAvailable = false + +// ckzgIniter ensures that we initialize the KZG library once before using it. +var ckzgIniter sync.Once + +// ckzgInit initializes the KZG library with the provided trusted setup. +func ckzgInit() { + panic("unsupported platform") +} + +// ckzgBlobToCommitment creates a small commitment out of a data blob. +func ckzgBlobToCommitment(blob Blob) (Commitment, error) { + panic("unsupported platform") +} + +// ckzgComputeProof computes the KZG proof at the given point for the polynomial +// represented by the blob. +func ckzgComputeProof(blob Blob, point Point) (Proof, Claim, error) { + panic("unsupported platform") +} + +// ckzgVerifyProof verifies the KZG proof that the polynomial represented by the blob +// evaluated at the given point is the claimed value. +func ckzgVerifyProof(commitment Commitment, point Point, claim Claim, proof Proof) error { + panic("unsupported platform") +} + +// ckzgComputeBlobProof returns the KZG proof that is used to verify the blob against +// the commitment. +// +// This method does not verify that the commitment is correct with respect to blob. +func ckzgComputeBlobProof(blob Blob, commitment Commitment) (Proof, error) { + panic("unsupported platform") +} + +// ckzgVerifyBlobProof verifies that the blob data corresponds to the provided commitment. +func ckzgVerifyBlobProof(blob Blob, commitment Commitment, proof Proof) error { + panic("unsupported platform") +} diff --git a/crypto/kzg4844/kzg4844_gokzg.go b/crypto/kzg4844/kzg4844_gokzg.go new file mode 100644 index 000000000000..3f03bb52738e --- /dev/null +++ b/crypto/kzg4844/kzg4844_gokzg.go @@ -0,0 +1,98 @@ +// Copyright 2023 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package kzg4844 + +import ( + "encoding/json" + "sync" + + gokzg4844 "github.com/crate-crypto/go-kzg-4844" +) + +// context is the crypto primitive pre-seeded with the trusted setup parameters. +var context *gokzg4844.Context + +// gokzgIniter ensures that we initialize the KZG library once before using it. +var gokzgIniter sync.Once + +// gokzgInit initializes the KZG library with the provided trusted setup. +func gokzgInit() { + config, err := content.ReadFile("trusted_setup.json") + if err != nil { + panic(err) + } + params := new(gokzg4844.JSONTrustedSetup) + if err = json.Unmarshal(config, params); err != nil { + panic(err) + } + context, err = gokzg4844.NewContext4096(params) + if err != nil { + panic(err) + } +} + +// gokzgBlobToCommitment creates a small commitment out of a data blob. +func gokzgBlobToCommitment(blob Blob) (Commitment, error) { + gokzgIniter.Do(gokzgInit) + + commitment, err := context.BlobToKZGCommitment((gokzg4844.Blob)(blob), 0) + if err != nil { + return Commitment{}, err + } + return (Commitment)(commitment), nil +} + +// gokzgComputeProof computes the KZG proof at the given point for the polynomial +// represented by the blob. +func gokzgComputeProof(blob Blob, point Point) (Proof, Claim, error) { + gokzgIniter.Do(gokzgInit) + + proof, claim, err := context.ComputeKZGProof((gokzg4844.Blob)(blob), (gokzg4844.Scalar)(point), 0) + if err != nil { + return Proof{}, Claim{}, err + } + return (Proof)(proof), (Claim)(claim), nil +} + +// gokzgVerifyProof verifies the KZG proof that the polynomial represented by the blob +// evaluated at the given point is the claimed value. +func gokzgVerifyProof(commitment Commitment, point Point, claim Claim, proof Proof) error { + gokzgIniter.Do(gokzgInit) + + return context.VerifyKZGProof((gokzg4844.KZGCommitment)(commitment), (gokzg4844.Scalar)(point), (gokzg4844.Scalar)(claim), (gokzg4844.KZGProof)(proof)) +} + +// gokzgComputeBlobProof returns the KZG proof that is used to verify the blob against +// the commitment. +// +// This method does not verify that the commitment is correct with respect to blob. +func gokzgComputeBlobProof(blob Blob, commitment Commitment) (Proof, error) { + gokzgIniter.Do(gokzgInit) + + proof, err := context.ComputeBlobKZGProof((gokzg4844.Blob)(blob), (gokzg4844.KZGCommitment)(commitment), 0) + if err != nil { + return Proof{}, err + } + return (Proof)(proof), nil +} + +// gokzgVerifyBlobProof verifies that the blob data corresponds to the provided commitment. +func gokzgVerifyBlobProof(blob Blob, commitment Commitment, proof Proof) error { + gokzgIniter.Do(gokzgInit) + + return context.VerifyBlobKZGProof((gokzg4844.Blob)(blob), (gokzg4844.KZGCommitment)(commitment), (gokzg4844.KZGProof)(proof)) +} diff --git a/crypto/kzg4844/kzg4844_test.go b/crypto/kzg4844/kzg4844_test.go new file mode 100644 index 000000000000..0d35667a9ce6 --- /dev/null +++ b/crypto/kzg4844/kzg4844_test.go @@ -0,0 +1,187 @@ +// Copyright 2023 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package kzg4844 + +import ( + "crypto/rand" + "testing" + + "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" + gokzg4844 "github.com/crate-crypto/go-kzg-4844" +) + +func randFieldElement() [32]byte { + bytes := make([]byte, 32) + _, err := rand.Read(bytes) + if err != nil { + panic("failed to get random field element") + } + var r fr.Element + r.SetBytes(bytes) + + return gokzg4844.SerializeScalar(r) +} + +func randBlob() Blob { + var blob Blob + for i := 0; i < len(blob); i += gokzg4844.SerializedScalarSize { + fieldElementBytes := randFieldElement() + copy(blob[i:i+gokzg4844.SerializedScalarSize], fieldElementBytes[:]) + } + return blob +} + +func TestCKZGWithPoint(t *testing.T) { testKZGWithPoint(t, true) } +func TestGoKZGWithPoint(t *testing.T) { testKZGWithPoint(t, false) } + +func testKZGWithPoint(t *testing.T, ckzg bool) { + if ckzg && !ckzgAvailable { + t.Skip("CKZG unavailable in this test build") + } + defer func(old bool) { useCKZG.Store(old) }(useCKZG.Load()) + useCKZG.Store(ckzg) + + blob := randBlob() + + commitment, err := BlobToCommitment(blob) + if err != nil { + t.Fatalf("failed to create KZG commitment from blob: %v", err) + } + point := randFieldElement() + proof, claim, err := ComputeProof(blob, point) + if err != nil { + t.Fatalf("failed to create KZG proof at point: %v", err) + } + if err := VerifyProof(commitment, point, claim, proof); err != nil { + t.Fatalf("failed to verify KZG proof at point: %v", err) + } +} + +func TestCKZGWithBlob(t *testing.T) { testKZGWithBlob(t, true) } +func TestGoKZGWithBlob(t *testing.T) { testKZGWithBlob(t, false) } + +func testKZGWithBlob(t *testing.T, ckzg bool) { + if ckzg && !ckzgAvailable { + t.Skip("CKZG unavailable in this test build") + } + defer func(old bool) { useCKZG.Store(old) }(useCKZG.Load()) + useCKZG.Store(ckzg) + + blob := randBlob() + + commitment, err := BlobToCommitment(blob) + if err != nil { + t.Fatalf("failed to create KZG commitment from blob: %v", err) + } + proof, err := ComputeBlobProof(blob, commitment) + if err != nil { + t.Fatalf("failed to create KZG proof for blob: %v", err) + } + if err := VerifyBlobProof(blob, commitment, proof); err != nil { + t.Fatalf("failed to verify KZG proof for blob: %v", err) + } +} + +func BenchmarkCKZGBlobToCommitment(b *testing.B) { benchmarkBlobToCommitment(b, true) } +func BenchmarkGoKZGBlobToCommitment(b *testing.B) { benchmarkBlobToCommitment(b, false) } +func benchmarkBlobToCommitment(b *testing.B, ckzg bool) { + if ckzg && !ckzgAvailable { + b.Skip("CKZG unavailable in this test build") + } + defer func(old bool) { useCKZG.Store(old) }(useCKZG.Load()) + useCKZG.Store(ckzg) + + blob := randBlob() + for i := 0; i < b.N; i++ { + BlobToCommitment(blob) + } +} + +func BenchmarkCKZGComputeProof(b *testing.B) { benchmarkComputeProof(b, true) } +func BenchmarkGoKZGComputeProof(b *testing.B) { benchmarkComputeProof(b, false) } +func benchmarkComputeProof(b *testing.B, ckzg bool) { + if ckzg && !ckzgAvailable { + b.Skip("CKZG unavailable in this test build") + } + defer func(old bool) { useCKZG.Store(old) }(useCKZG.Load()) + useCKZG.Store(ckzg) + + var ( + blob = randBlob() + point = randFieldElement() + ) + for i := 0; i < b.N; i++ { + ComputeProof(blob, point) + } +} + +func BenchmarkCKZGVerifyProof(b *testing.B) { benchmarkVerifyProof(b, true) } +func BenchmarkGoKZGVerifyProof(b *testing.B) { benchmarkVerifyProof(b, false) } +func benchmarkVerifyProof(b *testing.B, ckzg bool) { + if ckzg && !ckzgAvailable { + b.Skip("CKZG unavailable in this test build") + } + defer func(old bool) { useCKZG.Store(old) }(useCKZG.Load()) + useCKZG.Store(ckzg) + + var ( + blob = randBlob() + point = randFieldElement() + commitment, _ = BlobToCommitment(blob) + proof, claim, _ = ComputeProof(blob, point) + ) + for i := 0; i < b.N; i++ { + VerifyProof(commitment, point, claim, proof) + } +} + +func BenchmarkCKZGComputeBlobProof(b *testing.B) { benchmarkComputeBlobProof(b, true) } +func BenchmarkGoKZGComputeBlobProof(b *testing.B) { benchmarkComputeBlobProof(b, false) } +func benchmarkComputeBlobProof(b *testing.B, ckzg bool) { + if ckzg && !ckzgAvailable { + b.Skip("CKZG unavailable in this test build") + } + defer func(old bool) { useCKZG.Store(old) }(useCKZG.Load()) + useCKZG.Store(ckzg) + + var ( + blob = randBlob() + commitment, _ = BlobToCommitment(blob) + ) + for i := 0; i < b.N; i++ { + ComputeBlobProof(blob, commitment) + } +} + +func BenchmarkCKZGVerifyBlobProof(b *testing.B) { benchmarkVerifyBlobProof(b, true) } +func BenchmarkGoKZGVerifyBlobProof(b *testing.B) { benchmarkVerifyBlobProof(b, false) } +func benchmarkVerifyBlobProof(b *testing.B, ckzg bool) { + if ckzg && !ckzgAvailable { + b.Skip("CKZG unavailable in this test build") + } + defer func(old bool) { useCKZG.Store(old) }(useCKZG.Load()) + useCKZG.Store(ckzg) + + var ( + blob = randBlob() + commitment, _ = BlobToCommitment(blob) + proof, _ = ComputeBlobProof(blob, commitment) + ) + for i := 0; i < b.N; i++ { + VerifyBlobProof(blob, commitment, proof) + } +} diff --git a/crypto/kzg4844/trusted_setup.json b/crypto/kzg4844/trusted_setup.json new file mode 100644 index 000000000000..37108fee3e62 --- /dev/null +++ b/crypto/kzg4844/trusted_setup.json @@ -0,0 +1,8265 @@ +{ + "setup_G1": [ + "0x97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "0x854262641262cb9e056a8512808ea6864d903dbcad713fd6da8dddfa5ce40d85612c912063ace060ed8c4bf005bab839", + "0x86f708eee5ae0cf40be36993e760d9cb3b2371f22db3209947c5d21ea68e55186b30871c50bf11ef29e5248bf42d5678", + "0x94f9c0bafb23cbbf34a93a64243e3e0f934b57593651f3464de7dc174468123d9698f1b9dfa22bb5b6eb96eae002f29f", + "0x82b8775b874067bdd4479ac237f8d56036a742c17901354caaf38bf8c70e696650fbec76f0cd941ed8c658f44ea359ff", + "0xa7ce299c79c7d7e4f1adecd754c5aff8a720728ab27f5737b7b399f72724407ac54965088596375b8c876665ed8e4ff1", + "0x81ca4c808a76a6f217f8b0540ff400199295da69b2587b7be6aeb56447fa4fac08d154a27c4aa6082bc40660078d36e9", + "0xa70bad5311c97f1f3fea5d3375da1a11ba948aca41609ea28666dd343e07af766834e1256dc685ac1dcd915073250864", + "0xa91c2911a658ba79f56abe30716a3398387630e785b351b07344022a04b2f5c90de5573bd6e8048fe8878dde19336c5b", + "0xa8c560283fce9813bcbaddfb78cff93efcbc39b33025cfad94ebd40942a9fa605d2a947dc3a1f03c2e454075892e96bf", + "0xaa14f07fbd2c1ce7bd995e335c69b5f675ea573517c1834e787b30ab4fa10aecc62ecc5e617ac8a539af1aff114dc9ec", + "0x87f03429aff126b7c5a918423da278e17b5f48a4cdd6d34dba77a75f4f99e26a417e65d6a8579bcb2eaaf1d4d8c64dce", + "0xb1ac81ba91ede78315f712d524e9d821a152203f04141ba77f4e481ad5881473dff14a71788ce941f0905b429e7ee5b2", + "0x8f5c2af611ddfa3edf7e442d00e56a24d615bac848c05070c908c741ba18b67eb2e82e6651c9b3c70fb8edbf051810c4", + "0xaa4115b19221e4d17cc335d4f9b0aad22df566231f2286d550e97ff2875cbc419edfa189c4ecb24001123b95c6aaa2da", + "0xb363ba913969df0debd4e2712ae6e9177ce82e169ce7e0ff1d7616ef8e352aff3efb40fffbf7bff1b21cb8a33e19b455", + "0xb1013d778727d20466778cea47e1bf56a77168a8ce1b33bb1265f66438ab2bf4a7df4f4142b8681f2993ea9baf798d17", + "0x83b7250ee17d8529207db97b73c1c4a92ac076951a577ce2fe3a2cd633b461c1820c139ab36a895a5962e143c6198386", + "0x86d180bd2f0a4919764e6f4e846ec0d5ebe44060ec1e529ed15101d7e531bf1b8f9412916ea5aeb93b37b2d7c6bfb408", + "0x827451462c79d74b504c01bda199481b3c70416f66a95b2216686ca4d48da48932b0d323d0cd630a1e86e8033e832e5f", + "0xb789d217cb12c334fedff0ae01f85b96e968fb98761755d1ba3ee17934e8fbd83172225df7c0c6cb99432a30a0ef8c24", + "0xb730e5412dfbd646b0d2fe084a1a32eb5596c3fe8a5bc0f430151804f9e67f05f70b522e9aef08082b0afdc652a1d213", + "0x9987653bacd9bc1659b17f6964aec06ea63b787813d4601bee0479706aed5595ac82c87ed4f96f0cd30c19e1d9510a91", + "0x9506a9ba38f1d26c35a17c7e2554e28eb347a19cef523846a2559fb80fb40306b2f85bdc2c9fb98c2267df21c1ee3589", + "0x98dda58de74c0cdaef97b2826b4a9d53c9e9ea592dc0a755ccf5b3fbc1264979578563f5979aaa246e679918053c5f83", + "0xb00aaa16841ab53883af481e2f925050f5f7bf7d8088bc696f55f30593bdbbaf434f5d2b46095ed097b6cdb96c8fbc3b", + "0xb463d656480f89335d3a840a7b9398877003985388871b148ba708c60f9857c7585ef17c8b2ae67fbb191c04ad61e692", + "0x80af54f3d0584126e23635276d650292baf7e3e12bb06468708107bcd80937d36575721ee7472c5f085ffa71dbf438ad", + "0x94ccb8ade84e834685110c96908b42e10d2184208f434d7f98d96cc158e0c0c95135979600e5e9f465d5846b0bb3c787", + "0x8e13674b00c633d7cceb4f6ecd61e4f99420d6cccf9db5e81f8c90f6c661bc76e10939b83b56c225fce8565f525d4fa4", + "0xa46a15b2e671c1a1df2490768dec1093caf537e1a21fbc11ff8ba8b21b9f2be8d50257027d9357df20d9fbb1307d7249", + "0xb8ed532d48b0533a3084d7a5eea7b401255c5825e9a1b80ed81fd530cd69e347d152b1ad8a899acff7d68e0103bbfbde", + "0xad6b7df980ebaa24177d830c4aa522d6179a9a489257f60ee6604cccc2cbe90fb1f72aa9d5bee0d3a88f73b179485f56", + "0xa56855e9fcf62ceef3043991a93ec24f8f6b5667ef5fb7ad1771249ece68a73580ec3cf3e58a009ca4650c01241ad172", + "0xab2f25517d4b0b33d317eb78d091d3c3f98dc352b8a3e4650f7005f9327129e23d95f38eaeda5e9b51c50a31d20a4c20", + "0xa2d4071385b8a421da86f39739eaadcdea5685466feb6ac083cba0ea4c71dbbdf655032097276d703f9a77a4ca6fab03", + "0xa8681d7c258984f01e92e92c95738692b7bbd59c3a802adf4dda8d34add69590b080391c09e98e3b75c085c9f191e5e5", + "0x97685643da6c07b5e5fe91393122813ba11c8ef3dbd43a03b3a22a7a1603201fd516c1929418eafb14039270694c239a", + "0xa7bb3b85d6101e4fb0bcf540f52041cdb3e819d517465e342b832f0e91346a9a18bdb38845ea4d2b71ab87ef3bf59f96", + "0x8afc90b7d35336fdcf8f81cd024e921e244520ecfcb5a3994f2bbd595366b68bfa792a8dceb11e1e889b11432c9dad6b", + "0x94d9db7bd04f962d8d6caa3b7aa0f19acbd58a09d35ae187158d77e537d2fc65215f51f1afd62d4ba378e7d176a680f9", + "0xad62d7c01b14b6f97e6084ec9f9d084f106a7ff3d603032e6e34c027cdce4b0fe3c20ac7931f1209439a59c9fede4815", + "0xa5b44a87bd0ada7498e011e495a2818a8694746c4e7dc9d24c0c1096f54be6439e08c1b11c10d7c4bf68fe392000e971", + "0x828626c6609acc599f1bf216e9a4310fc3cb227e0d2e47bfe3360239427c8b0cc300cddf92456a5c36620596a6d37027", + "0x8380f91baac6447dd39886455ec5d99b874ac114a3c6a6ded18fc4ef69c2208ec19732428d8329d200a69f31792b852e", + "0x85a8389b522b0a686234450165514127006baaa3907f6eb29c976522591a542ffb681b3b88c4886814fd7ba3cc8110f7", + "0xb8ae7949ddafad37c0bc4d48325a7cbcd3096fb92c04a027c650a03cc609c7eac250d6a7ba341616bc36f64f1b4c8be4", + "0x8f9b9d2c2ab5c85abe946ed9986e0f304281b277d4d48c7760ea2331b55a9e9a1c4d53a6bdd83fa6294f421ca7431e29", + "0x9464b906ea8bc994b31e03c5f2af2be0724a43293fd42cbd2263b2de75a2ec04832d1100ce62ac2c0708f05fb6bb3ce6", + "0x93d923f6805e7cf972d8387b352d77215724a3e1f5489c4114fcf0b25fc2231963eda872387a1369a02b2e8b888d6427", + "0xaba4af392884eb7283fc5611ddc1cebfecf9477462e951bdae650e311606e129b4424452a3a14717146f954a7fa1cfc3", + "0xa8d0bab694d116e4f21fa19ff8fa4c6fe4061dbb54cbceda8235a7e05159737f87e008beccb90df0bac9c7d572281903", + "0x85743e3ecbac7ae04a81a09c2961397aa4bd20401533cd24d5fc0693cbfbdd2b37bbee6dec2ae5d0a66250d1fcba6944", + "0x80ae913447d7d3f6c54f9cb584aa1603308146daeb3024c8e06ede66ddc97821df09f9591453e9b617b828a02d54c719", + "0x803c2a64bb1c68890b5f1909be3aa180830ee3ef316d3aac38bfd909e2b19d890525e18e8fc2f405ee70ac14f5569b3f", + "0x964d2968724eb790f2f42263fcaaa1869c373b57b3eeee904f8b36f13169f07d5e29cb2b03c74d3a7adb772e91d5a59a", + "0x98a72ce71a57262aa058643a5cd39af64cc9eee88bef7edb003143983f29d87c7f9658b1ec89712f79f6f79dc24a6a45", + "0x91f3479c5d7c76acd2d51883961179efc975c714720187cc9c0aa7aeff71ca1b3e2db5b0a90fd3ff6abf880ebc49fe36", + "0x84312757edd09f111420bfede10ed3c1fa39d1723ddb9bd4d0004c829f0c1eb060e9648fd75f2e5427a67a5b37945a9f", + "0x95edd726cf4042a082d786262304c51d8d5e6a89b1b58e825a11febe5f861d5ce076bdcb2fc0a5dfa95eb2e5b0ffc32e", + "0x96500da38f942871d78fcc46cda1e72944c7888b538b82e2a979f149e5061a20c7602860f82b76510d02efdf3a911f5a", + "0x8ac62eda98bef8864df243696b53651a02a391b898535d2d76ac5a8e9322e0178a290c83f5afe72ffe80ad56183469e3", + "0x8ab2d4427fb6d3da5cf6c59835bdb39fb0c2de82c213b5de77edae3304458ea505511bd98fda95bdbbb9058bd5e92c34", + "0xab67c4344a5080930029ca3b803883ad05ca004ddefb48d5164e71a1c6dd96b27aaec70f62b39bb126ce1a57bbff1453", + "0x86c6bf91686bff714a873a78b0fe449db5317a5172a0a14eb3a96b2997b888d5d3f440de8baa32a6966fe44c3625b014", + "0x81d4f1e9d9e550125290d993a4886d46aac8cb29dbbba1e608aefc3432569c5faf14d8b49fcb485d9b75b649ad6b2fa5", + "0x8594140f253ced6fa98dd90ab4f38899916bcc6f1052572f182e46c00752f3053c390512338a0bc8f8c27a91916b855f", + "0x911284d4fad4999bb37590206d582b9e62ffbb815f414fd829f5b2843e6f0e1a132cd64464c131d5a0f476469a37daa1", + "0x8631a6a4987410982db9c0ba632023a5b613f553b6b8ffd3cfd501b2417523ba8cf06741c62f24b405554bd93e39e626", + "0x906ac35d22794a10a7273fdbca499fd921799b1ce9414643779dce9e1ec37920a5aa2caceb4b70a0eaf56c6032ef1b43", + "0x87374cdb8b7a1ce3c182b31eec465d435e35df782fe3a11f421462b48cf56c6fef2a9cb8ee4fe89672ba7804156d9e3a", + "0xa1f825e0246eee506c8ce40f849a17f75e8a0d6fc3f68b6a4dd431173b4fe997d30dca53005829e4e2422a4077ce35c7", + "0x875ad0379abd9873f6634692e33e9b36353e1a0d15b13d3215eb591244e1f236eb2f8f75274ca7f096179d1714fa68b7", + "0xb87b4e1acc09c5701fd9d75375ab896f178c1b3648fb9a2e2c6e1478778156decc32cd390766f3e80b36beb1e3a6bdec", + "0x836ca80949269eb52395776ac5ceb35b7df717a981c5cbbbb627f73c274aa8164e973a7b01012fa72a02404e878a9918", + "0xa770b13a8f07f74e5a75842b18f2520f6d0be42c865a29dd81bfe485e69a83c40ad10ce229afce276ccc9cb46c54b912", + "0xb4b919322bba2866baeed38bf0e2389d4fe6ab6166597e87dbfee75acac7c2f5ad3bef55293b56957c103d5825051bb5", + "0xb6171f1bbeedb3ee1af368c9c9f327d1dc3e55aeaffbe15f14db8038cd72540b62fe65f44ad0b5486dcf4023f0e91af8", + "0x8e42d0c1e8e8c2ccaf06edcc3c686aed56b8c987f9d68f20937fc088120a410cb92fb0ab45bba5db70b86876015a6b72", + "0x937bcff1af9685fd0d1f6616acf91d97ac9fcb1eb96d49d1c880c9945c1fcf1414f63d59fb78348d08a8546f6e83e407", + "0xa6eeb4873c0531fbcd407c2e702c68e4980fa77c9c032b9913b89031702cfa56f335fc413576c37ac4d523357a841203", + "0xb3962b5eed69cfa27fb94edba74b6cedd7569352ea71861494dd579da96d9743655b6308e54f8a42ee6d7e805c1bc0f9", + "0x8eea944dce7202b033ce734c9e88e82dd760c916e00b217cf1f00bf6ec5f20e21885d5fe95d6138871d167de4c46359e", + "0x81e6c7b356e2703ee333a9dfeb2b54260636422b9bda118e0523a20ce83b30fefc2f019e8291a8db05d207f0fa7332fb", + "0x83817f6164dc9e8e2506252511cb9871a8c9b595dde45f67e75ce3505f947b3fb3b804c18c054ad13b1518a98f59f008", + "0xa9ab4dbe7699e7982cd750d7effe031f273fab6b2e024a0b4f8beccb5c280903bcd3f2400b9cac7e8c94e157b4658ab6", + "0x84d2e3bc66fc6b59a1ee98b8981ebca0901d846c40d207e5bb5004ec9339d28956d16f054af52453f6a7ff3fc66c346b", + "0xb24bf0f69c3e86f610b6d27885ac5f4556fbb14e8286681538ddbb0b4921aa0d5604fedef0daf4a514ae15268a640174", + "0xa4be967f7f31995562669bf9587f5463bd1d9873fe9169687790e75961efa5ce2252fd21276d022f580de23527282838", + "0xa3f3c4e673b302bdb91fa3cbdec88499752e6ffe37e2215d69b8a426f4da98c3a10e4c659e77c656172e4e8b1b1a41bb", + "0xb704ffbb3434563bbbce69ca7e812a8bd30757b1e90630bf3261735f9ea221524b36f97dec324ffd209bef45bdf6f2b4", + "0x959dde49f15c663a2de000195e182a11d8c396c1380f98322cbe5521b697bc3bec3223ca9e94ee2734c4ffdfb6a19e8c", + "0xa469685143cd82b78d7b1854c350da63819d9d86670e9b35a72381d0362cf5c3f1d24e22ef2ea6a12176c9dad39fd51c", + "0xadb97ef4463e5e13d91b75a3086d72a841a60be278e9651d9ac5f76c9537bac5eac33424a1ea00522b3357fcefea0738", + "0xa4597b2ced7566576e71b4f105b5ee48aa4ffca93901e9b626f7059824f53be3e8f3560e6861285f3d97fe88054fee83", + "0xa18d9b1b81564447f798ce5966bf10c823aedb14b143972eb4dbbba9312fc79f46635aa016cd20c53be90f170f06fb84", + "0xac4724069177d3c6ac1b72ea2a7d6bc5ac3d4b2a4dbad124152fbd170c9c1038cdcf255d162a25c14ae8df11a3849945", + "0x892683f64179ba84f6a447c5c7489e3cdf02474d2837dd7bf3b82a4dd05a6461ce94fff28d65b9539cacaf47dddedbc1", + "0xa68ad797bbc1b2909e216a0b3f39aa6c3e4dfc7a49f81a206b530ec0c6ba30f871e4a0053625aeb99448026ae2e0a6eb", + "0x964ff8badf35b6b93be6d97209d87f4ac8847be1c2ac4bcafa1db5c3f604f61834c85b3dcf58af50d09bd03ff8d78f27", + "0xb76dc9ec64b1fab7be269097a18a77144623d37bc656934fa1562817c922485e69b18ef40413ee309e100fde645fa7b2", + "0xb2a812be6e69f284580ebdec5ae2cdffd587bc7eae10989e9d2f290498b1eaa934b148ec7783edec300be5d7a9b34af0", + "0x85ffcabc623f8ffc58c5f640f857e27b7c105359315a3969f346e1366acb2af88f4acc025b299b9c324a8535c380a2c5", + "0x8d0140f79fb8ef02d13b1d51c4ba1af5b5ffb19322f88912215d4198f9a592f7ec6800c8a3ca853a3b68f9bf0353a13a", + "0xb3174deb53c1ebb6a1e16c915cac287573b70fe4e0036e8e971e8e807a77362ede632f6e3f29cb87a80a318213946ff1", + "0x8c58d603f6420e3f55522ec2853065125af4e7773a909e28296552f7f8ec4691ada9211d834dca38e118f432b6cfe03b", + "0xaa7ac268e155ff074bfc197844e21fc2a9f9aec9b50d9cda63f50d3c4fbbf9221e6fac3a6ba0f7e4cde71fecd493a15d", + "0xa191337721bc9fd2d3ec2ca6f6f97ca2462ef5e207464bf9e746a650a67d69abb5f578a8238521cee3f704b275845e47", + "0x93521abea8f38c103ebed3313a3af8f27f03c9a54681847f4201bf9f72f1f63064b18175986fca64f80b4380905e894c", + "0xa1b9d063d6538885f9826b84944123d7d6027dd030aef29fd6229f4cf5d32404f7dd0e899a0c8f4b6bdf4649e8a8966f", + "0xa15d5497f0fd2fd0b2c2e5df58a25a72a9d99df8215951ea58c15569d312c6f096f78034f6a8502f808e649f6cb9283a", + "0xb3c275306852612362e1073d0f4da3ce598dc5fac3f3eefa22ccee35dd57a4caae347b43342cd1f6a6e068d3ea9fd30c", + "0x94eb678e0700bf39caf428c65bbf2fbf7f601c39e382570a4df9186ff1dd5a958d78e051a5fd084e4f75536a14b7690b", + "0x97b13995bbcb8e824bec28488994a830a9c1f34ae4c1a16d5528d57f09e4c8b5d81677ea9f979f0acb8cac629ee09c85", + "0x817c99ad48bc05bd4fd29f952dbdc5ef56bb02f3442c18e3b91cb6d72ac2d2a5df901c099165ded1bee62c3ed13c41e8", + "0xa884acf980f6470e11cff347692d8a7cb7860d4822112f7bfeb02efb05948ea98c837d5d98dd7a104aa36eb8f016a0f4", + "0x95debd2ed23a23a16a393f59f666cfc864f63751238b73981faec4a85b4c04cfa11520c9e4cbe4e23fe80e86c260093a", + "0x937b4691c59453bc6cf6468ed5b17dbb25496bfa3817798562cd5fd86ab5ee35745991afea2fe271ce0fbe5a990c41c7", + "0xb4da98c879e6b475c540ff2c5501299f0e3e97b7b93beb45faef1253f7de96968898505e672cfc4a3ee7e20c1f72c256", + "0x8ec9d806f344d0c675bb5ecd47c54defb5f059a5233dfb2d459632b9b22edd6c4b8c47fd7899ab13e35f37ede9b124f8", + "0xaab4408410abb4d2cd98694f71b5452e6fab2690daa3066b3f9747e7dc40b57259d52e6fddeaeeca227b733d049b9694", + "0xb85a12f39808961c331038159255140a61dedc56050345a2eb13b1f7d140ae07b353d68d22f2cf60925fe66e598213e9", + "0xb61bc3bd68bffdbe9731f48fcd523491da04dab83add49fde390070513b9ad87a489010f1ccfe6f54e9a48edaf88b5f9", + "0x8f50f6d8235824cf25031f09e4b139bd89c1090269dae95a5aa0dacaf5f9b59c329a5a3cdddf9efe6c77cd61f481dcbc", + "0x91a543b85e18f34361d7df5ece8504d456627fbce65abff437007e9109359538a03996e35393a40f0586f962921eccaf", + "0xb7557bc52931324dd4c58d0e63c89a8dbdd2d00d0baf79d81d7a062aedd2de9dd743ea30fb031b18c806ba03175d7e1d", + "0x8e056b842a9af7aeb6c0d113a3acc8bfb5c6a8980fa81869747f75abef76b7fd20cb67694e70016e3de6e7821cde030b", + "0x966c00fd6472bb13ffa531d8eedc145ffb7733114e0f4a6a9fddb34ab7601f6cfb056460f757636230b692453d8b31d6", + "0xa25d85947c6939547fbee088e0131988053c5bb23aa2bd48ca764f4ef2b29235a817b8918d1de6865695977a95711e9d", + "0x958567f217ce7a6d74861777801663d7175eeeca8ff62e240582fb603ac91dc402331034fb4855632352df2328fe0233", + "0x85e53f3802a7d32dec2db84fad7f8c8fc856037cc0cd4ef9a8988e97ab580d4b929023f1fcde7633828b5e8bcdab08c7", + "0x878d1fbbedee7f7ff72eaa3848d7f6bc3cd13b40149278b3afe5e3621e6d1f0386f8ede32971d3f33be189c927bef6f7", + "0xb041e880e4ecb254f6f8d92635a1ef3be3d5d885c751f247bec2d8a016aada6a7fd2f7c599f458ee466886abe721bba9", + "0x920747dac9f35ba0b2670f82c762a71ee9bfb9e490825fb7ed613bf2548ef4ea00bc01e9d2c952dd9c56f3586a3ffb49", + "0x800005cefda1ddb860fd8974342fe315d227902dcb5f3736f8b9ad1fa2f8fbeff8c8ba0eb3f0c21a6706f288ef4bb13b", + "0x91f2b822b728fc5d1f15b69a303985bab14c08df5e929decbfa5aa5689f3cd93ccfe19ab10499d31df9d38c84039e492", + "0x957a909486abd85b1e627a4739c7d212cd03f2b62952045b704c415acdf2e6c0cc627af93f382836603f33d1a716ac7d", + "0x9733ec7a30ed833cc1e7e0ada4badddb1cd1908bcbd3d4e4694576421c94090a9297aacd7f42d9d305b87d711828304a", + "0xac2785a0dadfd246fe12b63f759e9f021006cff4f06b2b5a9986f0b02a40f29513feb1c9044af6e27d1c5029b1e1db35", + "0x948b22bddf55f4b4bc26892e83f70b882a0458582ed87fbbc81bbd037c946d833c19162327354240c42e05cfef55394b", + "0xa49c5d81544028d56f4caf8699477bcda589c65f6754dd40a487ef88d93925008dc7fefa6d458619d51a54b3edb5e5c4", + "0xac57b8ca2d0623f5c4137cada67afd6935fb75fd82567f2c57cb22e89a0562d3c0716d5e903fc06694a8c2edbc9a6f1c", + "0xad52af6a0cf838bbca5a97aec5d87fee1aec4fcf5e802b8bbad1b110c31ed777de0b0ebf74384bae68289af20e351bb3", + "0xb0c7c48d734e5a1b37674465eb07a629dbdf8f9080c44a578f3dd687261d9d1cc5cbdc084488c745c9114fd998bfefb2", + "0x8a2b2ccd4c52d15bf7aa4a8847b8015bd53f58ee484589339b4510ef08a27db56178c15b4d79a9c6eba1ac0b641eaa61", + "0x98f659a37bffd7a9b7759bb111412ea9e9eec483645511590f683064eaf15e1b101b5eac3b98f79ea38662b1956a06d2", + "0xaf6cda3fb2479b6f2d959f2d03e52b49afd12bdccd7a65a1bf6b91e345387924d5e355363f79bbe32a4624287cf4c1ac", + "0xa24d325d8c2dbf9d2e346e3504154018937efb74246ee0658e68d148d9ad0f4bfe348ea9bdca77d4467ea1b3dc2fae5f", + "0x81a729dad3798121027c29e9310d56e36a48c1c479cffe674cbf9131c562f541d7e6c52c2718025d3470b05b67cdd321", + "0x95bd5cd6d9895c775e58cd4296ebefa51ab9e324418208c3c4d073be59410497a4d0daddba6c1e7373abc08e13d32b89", + "0x809fa97a229b056def6b548902d8d90c873e496db6cb1b2d448709b9ae08d9b9762559666cd96b6bba396eebbab4ea4e", + "0x8bcae63cc680494606e44037a3bf6dc7bae2e723e5ec3ac0451550b8ca7914ee1d4bed0f40adc3dfa45f8f80a36c11a5", + "0xb3474711a0f933cf269e97e4e1e98762ddbbf49dd72e468f1e8a2f89514c1c35cb8db32d08dff50f93e50db43bed54f2", + "0x9788a37c3d95310627deec58ba6d9e0324618469275276632a3fa7841fb127c8fefc1b7392064f2eecb508056bd346c7", + "0x8d031fdb156023e185fe5fcac67b966baf9c098fddead4a6f1a3cef54d8e912d0de2d1e1d3f3f05da538eac4af5b6973", + "0xa5efe72b86a714dbbae40fa69fbccf41042e0647d177cd60275700257aa583708130a64b2f9dcacde4fb636b5cbd5aac", + "0x824092ea32eb7a8c619182d926f292cedce7ac3d3fc64f60d00fcd767649e1d6cffc20dd9c1d1c8ef6f64be315d1e2b3", + "0x900ad22d3b63376b1ac80c7343a58df23c03c4e7d6e5740dc10d8cdee793be07fec09cfbdf29e1d1c6484d3077630d6a", + "0x826815005550844ac5a6e831de0e25fadc49aff808cd601d70743d4873a341e3f0cd40d490422c87df3f3c40921fa723", + "0xb39d189aea740c52b03660c0abc8e796cab72193ed44a4b1f59fd1ec0e283ef7d5e157ed23741eaf289cf968597c0900", + "0x968ed61662d1e656e039900912ab61173b49d2e24aa6b7d3af07c3b04a2c89c4516540934aa543bb48ee14153780d10a", + "0xa433b8b689007ecae7f1df15d442b0570664a2db6318de66c6e5fd68884615778d296bd260ab7d07469bfb5f6d8c94ca", + "0xa69ed4a0f39920d1a62a01214daec143fb7596212e2439583df9ba508784ef4d2fe86334f0f9a6b7a3342ec0a72ef15f", + "0x96f260d9cd88757e7c45201d57bd816f1cfd37587ba17a64997bf7716ca1c2cfe16a7119c36acf1754231f303058a9cf", + "0xa51f2bb09d30028eeb0860e2de38094623e5b5514fd5d591a7d4a9731cd4b9c4c12c5dd6ef409e009dafb10d185d5346", + "0x8abe821036140ccb3ff9063dcb5e8b8724cff1cf0784b8f44486c8380fc51715cf55b443cc20870f426c4874be93caeb", + "0xacd73facb964d9012ad12405dc866beb52d8de3ef81fe966cfdb14d22a19bbd2e7ad3a29cf029617b9d6510ed323c6a2", + "0x8f18f6883c8e4741cd6c52e0d3335dd71b5571446ee28e8c27cb0625f77a9f5bd0720d960e5e8970257907f503d58a9a", + "0xb66457a91e7ddcf56c8ce4936a209c69ee53d71236b72ea386f7719c8b8c9b4ba4ea19039a8de17a0a869da427da42e7", + "0x80b1de58bb3ac5f264e0273061f485e49413de604b5ade73ef81bc249f5e89ce97dbec7d99b088b5a2ff65c0bb93fa76", + "0x8bdf276c88f80371ef0ef7e1224929681629aaebc8cba3c0db0b258be3c26cd17268f56369832f564b1679be33e98c69", + "0x943cf6fc88678816da42e4f337c730eb2dd59f8d738ea638a799e8b77214ad7e74723056bae96b100f9a972155885d26", + "0x91c8c1a8a61f47119005869c11edf0b69d0bcf40534b82e46aa96bb6107f940e582b6733f855144accb8dc21d79acc39", + "0x96ba98bd291faa0904ca0398d6c50eb5bc2ab5a389c359ca42b8909f41f4fc37dcedc370ece777d5035074a597da503e", + "0xb4598e6f889d319713a9896161a6c9bd8575ca30c21d3fdd37cff15dc0141ce28dc536f73957e6fc8f6185fc0adb731d", + "0xaf1ed593a0547c26ff729c159ef14bd0818f25e7c1c6c51ce8ce5425bd2526086eff9fa3341279daf82e480bfe431230", + "0x8c02b9ad3aebf156c80fec9b012241f3794d736adfbe4a272faf505ab818cb121ad2ad7c2eb1716e252d0a2e7ee6b246", + "0x8d2a8a31784c446eff4c2ed7b004009f08b86c87a4786a0b7be3df36ca9130a0ec42a58d09dfede1279a4a6d3d37b501", + "0xa78b61be13005b1718a3aa3deba103ce71e1ff73163c76815f9cbcc101d993f119ca128a25c51a12fa52f46550c4b609", + "0xb990d81d7aec9fc50d798eb8c38b40b162004f78730e9ed4a103faeea0995bb654893e557e5eee9b74046ddcaa70617b", + "0xad56d68777d0ed53d3331b0cfd44503b27435278416ac2268965d8ef711fdd819c16ef5499d8d7fddadd229c3d0d4bd6", + "0xb5110140b9ee542ec03c945cd6180ab1af205537704fd408fc4490d799d87a3f3aa0f1f0ae9c8daa55c1757f7bb53cbd", + "0xb7d8a4080c5eeb00be4540a00e65e744f4c7792b518c9fd2dbdd25abd0dd89e76563618cdb92e4cda0fe3ba4597508dd", + "0xa880b33af98cc0bd1065767a2600145e6e326c3cee25602dd22d531c26c4b8543f846fadf679e26749c929310779d858", + "0x941f124078990e03310cacd79e4a38667f4dac4dda4dfa3173a03c14aafbf538fdaa01b940fd5be770c1cde0a84bfefd", + "0xb234e9d0f04da6efc5aa5c77bf71cb05001cd193178fdd546e4ec81875830113d3d4f1512e7405b16d0b3aead285999d", + "0xb857bf6f62c4b19ca9441f700ea6676ffa3b0c7138299533ede59a9b9cf9b94295046e7eafcf1d4ecaf0341898ed6b15", + "0xa2b0d74f17d3387566bb4f17dfef214cdc6b61dc50698fbbe298a7e2f4a82d20aefd719c5e82bbf4ba4fee0e2e35b4c6", + "0xb5ffae433aafad3fd51ac137976e2c22661d8a286b221e0107baf19f5c8f2f6c7eac1e785f145bf7c16a497310fbf51d", + "0xa69e9dfb14f8c6cda716166cb69f06879718656c9f46730d94f194e2888fec371a11c9701071bf8690e57996fa91d213", + "0xa1f51ecd5c5d73155013dcf02b327cdbae9f9c2fbc62f73959050cd3a0bd66192213d1f4bb56a85cd21343722ff3f57c", + "0xab3e54b8f4829f1115694a4be7d16e8214c94387ae783263cfe145f965705d51943176191c706f6211c8be2699dc79a9", + "0x8cd6a64c5d30149ca4dae4fb7e8974dce1200aba9be9c8cf9af5d43e40098746ecff7bcde7ff84a0072138dcd04c2771", + "0xa52f6fe24305bcff685f2d047c9a8d9a1f70c2b416cfe55fc137c6b5b185029f3644890418873665712dba4886e3fc07", + "0xb2e8e3d2ba2d64815bafb678dfc1180534186eca415bd8cd32b303bbac6cfb637b17200aa7cacb953e656ad19dd5c9b4", + "0xb5412d1073b3b80bf0d18f791a6d02727cd9c40a86ab0f13ccfd847bf4e767b8b79aba3935194398da2c9cf17c6bfc8a", + "0x8bbaee84aca9089585d5ff385dc2ee6e653d0fcb9088f98bc5fb1c6c83db026d9a73d77c33c6cae3268be90805d862fa", + "0x9691343d1a8b7fcebefe2584b28ab5808764ed79d8f28168a65ca90b0094e7920fa43e56b960331209f4b8465cb8a5bd", + "0x8ea475e12088d558f5cf6dea2da71837791093a622d5cbee608a95b4c8a61296255c1545d454562e371ea0e2cb2e0b1f", + "0x951d6b404667ccca099d01328562790d1e8446231d7d22bc2b1c4c6b15785bf59f2099accc58817a06d24d59ff4e6a2f", + "0xa5d012687f341eb9c783c1c2040388eb7ad662cfb2b84cd94d270bcc99116939aea80440d7ab726f9abcad22fcd90274", + "0x818fb57b7a8cc59f06af054ce09dfef812f8f115eb2473d06c8f20fc50cf17423331aae1f843bcae57fe4e2926ad5aaa", + "0xaad27bde8eaa2e7fb1a9a5ab531eb41f475afdc89b7f02085f7289f8f84d172fe516d0104560a40c89e97db7e5e836ee", + "0xb8cd923efac1b09d9c6b1d97a0c1bce9fe4eba1d872eaa3c0df34dcff2e7ea2354f1b31b69c6b266944ec8cae2a16865", + "0xaf628e772d609224aa7cd3eddbbfe965fdae6a05cf6d14959c5c26c4806043afd5fef803091bec710c6854ec095ba18e", + "0xb662e1d32704d96919f5dbefc3cc40e7d41d4124c5865b46408c2ee5c40926eed71fa3df71fa7ad349d476d9a525d7fc", + "0xae4c5512396f9c26381394ff8e22b1d6267e3d3a5d3fe48457450694520c5e173716572b68fc1dc352761991abd262b4", + "0x86b530978a7e549e6ca014168fa4aeda9438bcd3a29f7edb1f4e9c712c16faa58b81b028c25a8e90b421b61a1766d7d7", + "0x97b32f1371f76dac7a449295365780d1bd03f290a041b3d19db3f14bee6365a614ca356e7cbd6f966260420b451f6117", + "0x8be97569ea08d0b6c4d46e9565ae14f79d1990f192a26ec935a865cedd6bb5f69f608b069f7d645877c5081fb4a68c54", + "0x9733488f48de05f57f623752b80b37c91f6c9afc0f9b4df4cf400f3f82b137bdf06fee82190f2a4ad4aad20e964cc214", + "0xa794f6dbf155666056529748a792be13011bee6ca10e0d55c82c3e84c5dfa1f370c8e8abf2971a75c73a4ddef3da3319", + "0x95ff5d16c0d9bd679738257a1f7f309f257c20469f2fa41bcfadc671ad65acb249793defab591f515bb3d8072e2e05f3", + "0x8d849150bf8dc3452839256ec4eb65cc9ef87aa0f90dfea4d1d486f16ee855d6c480a8fa4b6cf8d536e435f9fb7bf507", + "0xb61c29121dca2bbc6024ad2f487bb57b926786ae60a9e7a721440787752432ba9c7e1df86ef0d74c2592d23f0e89326e", + "0x819630a678e4a5e6adbde9b292f5c8f2b6e3f2ecc9bcec60ba0f8502e503f697b0ded4f0f7157b60ddc976ded66713aa", + "0xb3525b071e26babf669ae2b98319b3516c083e797d74bd5b9b0e1f67792a2e8ab2c60921812690b5928de66290ff7b86", + "0xa344c6670718b9824ae62b309813bd31984eefb5efee38052cd06812308edcc39fdee165f8164629267bc0e98fb50ba6", + "0x81d78d54738817dadee7bf70a468a51728de0e9775f8779fea5d0d95e55b2004377b4e2db595d420f017af18a384d9aa", + "0x848c97b9413ba6ede751ece925ba57b8f8ae27168c5d46347d39e0232a5eb42069a85f1ee2d30d8b94fde574642be5d1", + "0xb020048c5a5a2d186df628550c6f61a204f16e6eb991283e975de520c4f916badc999b3b7e9402ccc39db5c0b510e2d4", + "0x9298c1aec9664ab3fe328f532428e9b037efe8412ccfdd15e33c9c82dc3631e49f84e0d2d75dced85e3a4e0fd0f3f2dc", + "0x8c4a78841f51e2f8b91defb0a3844933999f9884e2b324bd89a01e482756758b1b5a278289163409947c73106bf934f7", + "0xb328a9db915c4bea1783218c7668e2bd7a8fa62e52d3a005a43590041d34ff388c0555b044ec5ff85368352a3643b7eb", + "0x8a395d89469d374c1ec472c4d571ae670849549d05124907faae5a086519686683c1773d22d290ebdcfb8dab926d10b5", + "0xaec52b8a883f4ff68fa5f418cc91c8e9d08ef324544356b0ac56a7f0980fab6b376b8f567e357ba96b408652b8e568ed", + "0xaf80f0c5d50ab23d8ad99c7fba504f2f02b7307b5ae5ff529142bead28d69c3d55f4e2226c44549548fdf761ce30cff2", + "0xaf73700803caf7b06f5453a620253731de33a458da01f5790106e9418fb59e7aecf6fc1d1b650e1c7b3456f7a55d4301", + "0x8be3ee3caa86cbe21ce9998fe1c0de73ba6481125ef28e312002377676317b5ac4c27180086fb83794efbf86438ad27e", + "0xa0439d051d06a7fbd5ab83f32f0f95364bc043d9d934ac64df7031223e731d7992206879d567e77f35badcb7623f03fc", + "0xb99de1a16670fbbe3ec26ccd37399e2a23c96813c26428deda4f74dd3afdbd28cbe47e074379f6094b85176f8ab349fc", + "0x8a943a039aa33f38d3887de4e77566d446e87225bb8333e3ea991466c15c6487077c6decb9cc10e5de6af03e6b81a10f", + "0x80b109fb49ab810121fd411e4cb85773a1004af2d257e85ab5b4c99aad8d66e5803a8ca7b95587197e88abaaef0b8d42", + "0x892148bd190b042fe9b7914b8aab073c0d19001158087077a5946690dd60d99a1ef372ac01e372a434d00b0568a75fd7", + "0xa266dcc9ccbda054e396e1605eabde6cf79a028b697898090e9f34a4a4e0b771c121b8d470b14130a79cebc19f8d6e58", + "0xb1ab30b97c76392712b173460c227247cac50597c036f674361c63c3638a4c03420fa5b7efdacd0496a9b83956cf5d06", + "0x8a33c46084f669455ba089b369b9c8493a97c131f09c66f9347873504f35d6b94a09483b2775656ab32a12c7b9766ab1", + "0xb77a7c1402edd9ae448b7a606ba2eed192a9bb8f852b647b6ed689b0a3ccb81a4632edbca4c113750f62643a0626e2a2", + "0x8586e85e3bb07b07a39ecbd822d2adbfbf1fc66cf2377fbe6b1bc38369f86292c6cfdb5b405a0bc4d584c0600178321f", + "0x80cfe5b1b032d5a28662d13772fe112e9b73c997f8ef0fc796576bb39e02189c3ec0228d192c981061dcccb9dd3c4f39", + "0x873c085029b900d1fcbe93f8789d635e3a8fa558766701ba9fee76dcf05abb6cef518f2b56c4ca5e26f3847cf23bfd72", + "0xae8075937a23505f51a1a26f7f54e35caadff44ffc43465368daa9c330b553cb4548adbdb04e24c3977e35a08841c36a", + "0xb1c7076afec527912f7648bedef633ea0e3b02e5fc3fc495779b93e8a9f64eb503f46a1372c8dcd8fc2572c198112da2", + "0xb5233c4545bae360b07c4411776218a1d9040bad1e788e099f90149c58258ecdf01dbf246ddea48ac8fc2dcde6f34f20", + "0xb62655a8376ce1ca225dba04cb29f1a95d09e1a20b58f0330c478c6acf931ae52268779d6cab87d9074a362b9e82b205", + "0x9684e676088b409052773bb740bd3577bf0dc15d0392ea792393a158e643b165f8cbdd91cf355d5425682c77f2a91f34", + "0xa892744cc0c428c97bc929913ada86c36f280f49bd1603e21bf6b6abf8ed195cb05b22e586f0c841ee02f234731985cd", + "0xa62c089a73c6dcf3f7d957719c7d452962ee851d6ed8c4b57ade8a1e57156762c348fe5f20adf6d6ce47b4e98f93d60d", + "0x91b29be6022d43937df9c597d19e23cbb83cb6f5b764e1f7db6cf60dd9b3e9c79f1f617c3101c60fe6c7af9b5719fd5d", + "0x91d13fe99d7dd7b4744fa2fde41bb51f4edbefb2189ef3ca5d337ee84ca3f728e300aec19b96dee18aec090669c85400", + "0xb17a5328808ca929b794dbf0bf3a3fc318f8df144a892ec0ac2163a0f7c3a4614d7ec433b66bc491c05a286fe986d986", + "0x84a9e84bbecfc2aaf8bd623d79bd4240c630b81ecd55a50198de21758255207238179a345700e65d9bc6eec1a1a1985a", + "0x8d428be451efbe630740449ab3677ce6f69d94d75c5a9d91d14b2493a838260d6418be3d4658fd15218eabe3adfe455d", + "0xaf11126224f6ff0e88a09dbc0de6db3c70e3db3f6e154deb448d044100f989ea41c6c0259a8ecefdcf531f892a957d82", + "0xa51716b900a00277aa932bb03fb61eab3bd8e74edfad6153a06f85aece6f832af710f1477d883dd8e48931deca12bae9", + "0x9542a82039c2d3c538f15da884f090622c5af556c16370d21bdd6544208cb17e0a30e511b0af4a618e8ef70d0c43af07", + "0xaf76f93250bd7bda2b5e30e6f88416ef6fc8ce0cb614515a1f8d81dec717077209493cb47b79e8b1a62e09e394038338", + "0x8fa8d657f1d584b06d5bf41a52bc2c73853e4092290789df04eb8952c6eb236df601a1c6cc81de420a424d8e748dfc38", + "0xa6e93e27421b9e32b170d240b4cf2710c97b49dabfc0ea25759c5f61937eb3da8d45a6400f8bcfbb42bc9a7ae9b66ef1", + "0x81848c8d66d34d274b21dfc10bb36fb9497a1b152aad64a8f7c874e58d31d5dd5f39e30e6000b6d7b444e573da1e043f", + "0xb85692a84154f87869d97cb5f16c81fb0559e097fc37473bb11dc9cbd311ab91a46b01aa9debcada590992c2473ef0fe", + "0xb565371692ab0f0d899d8139d3eaacd213e7d23d6f5df6ac3409c961aca019ce861fb4ca8317f462be01e8c1dc7af154", + "0x82ae2bda0228d36343f6153fbc41fc5c79fafbc03c99a7926c624dfa28ed0a1d215e11ab83cfd438fe5d85d7fee50363", + "0x923f38a2f839e165fd197e1711ad52673deed9774e0590ff63ff9a9985f99612aabe003b9a98db2407c2878abc6d9b0a", + "0xaf8d5e1048de3b813308544705eeb0facbd604a0ed03e66c1d221be64cad35d71748d2a55d1ff3049e1e5053c7b1f712", + "0xa90a4b3b9d3b7c87c34f85c7643fd67dc771caa940c9e2ea81294ce6c072eaed698368a0e8056d7b819ce3d73de4424e", + "0x93a106e914d2c6892fee866602edfbf8d03dea1918d82d511e528b99c8423c260c0d103bfaf9992e0e24638b913af737", + "0x864cb44b1adf5a59ce7baeda0ddec3a0ecedd42923205dfabf30dcdb216a7b760d8895dedab52ee09bb09e999486b521", + "0xacb5f2bc1257c49c7df89837502e699bcb9652567c1716513f258f021755092954f2dc65b9766ffd9a10584bba424c7c", + "0x86653b3a479bf6e10e781e316e61437af1abc988f59399bed8fb4ff128f5f6d53f50a293da58774acd42b8d342e52429", + "0x926b7b90eb7d81fdad2a8a59e13b1573970e15c10515954b7c232c37955755b6758178314439ee6c3b0c881d4092c838", + "0xac05f011011a354f0e16fbbfb7e9dff03b3cf403dcc449eb5c71067128e314badf4d4dc5dca4b8616994ecdb15909c93", + "0x8e063c6601e553f33abc64f9553db5a19ea794a1f254d5a5f7b8ff2db4ed9d180f68ec919a0f83142c5710813baef4a7", + "0xb6e891dd4d44fd54120b7b8716292c27d4bc8744d96253a841433cf4b07895606db4a3cc5872c480616b863073bf77e1", + "0x8dc623d7928234bfbb8cd0b4fce5c8d9a9db848ab0af967ba9c49daffdf719cf8b55e1dad0b7e421571b8770cdfe9df0", + "0xb5b53f7d6b5d1af75e5a1720281feefb8c9039ef7f1e1969d83bed5a2f73cfbca91dbf4fb8179d9b0d3bd06d1207089b", + "0xa5dbce9e6db637e053b4b4d3df07b724b50d11eacd3327ddfc5aa8f37b9a5bf628cc9b428328e16cacc552c1dba505c9", + "0xacb82d6c9af9af0dd426a07b1aec81b388b61042bd601546cde248730ef85a09016bdc66dd014447fbb56fdcc23011a7", + "0xa41692e96f1d775b3a9378b3634495a8350dcfa52b4b2b7773b39d36f7d349fd5ee9a2b3e72769ca98f2319319890216", + "0xa0b4bd6a68ac5735539cbbdd78ee4faaef7d6488eb7a11e091d94e315cfcc49a90f204f636dd8033857378ddd67cc153", + "0xac3dab32427b0583159482f73f94236980d69f9f8f781b93f44aeb43dbeaa740c77898c38c57677b42c248b9bbb1d673", + "0xa6cd1090b97826486f59a056ed90cde29f2ed821211391f2f16e66f1e8914398348cf6f0df6d3acaadab31f0382bb5bb", + "0xabd1252b722aa56010e3bd4119f2a28a852e9ac1a8ce68c96b6da9d00fac0c9fa70e67cd4afd45e0a8042a810b8e0a91", + "0x9194b629ca80b3bfefc0144553017343d0915aab59faa3d0e2bb3720dd3c8fe07804be6e582c6d57c764be96cd40f2c9", + "0xb6bece03ae1c5935eb38b14f0f64d9d0b4410c02ac309e085a233c74bc3e67ce63edea56ea37f4532e8b864aecacadd0", + "0xb753eb9184f5b30e77bcb5d3487323e0f1178f1ab3e15130953381272209a97c3e8084e810dcebf1ea7b22f0a90b9c77", + "0x87dd4a76955bc98326823cffd653fb7b7eda5df1a971b72ec2a4d25fb3958b9d6092369539361069e7e4f1dc9343d929", + "0xb0f1e8b25a2687d98cc037272473b4e3f33cc8d49a3c83a335d48b8a0d3ca5f84e8e2bde304ade6f7d50e5f3539d639b", + "0xafce1c0205adad1ce52fcca71a99cd6df9da5b748209c2ed1013b5b7d484b937bfbb26db9e9f8e77c081e0a0384114b4", + "0xb363d31209c075b94441d1a8ddcc6bcf9eaee78f8adbf0992d5c7e7d638a02d58e19203247443c35d700fc8ac8a1b7ef", + "0xa0aac7dbb08a10f6cc2c6a4d37aea6bc3dc034d933f49de3dcc79bc0b7a011b1e27df7cb882d155287436b72092a1da7", + "0x86dde01fb7090c80fb404afdc9ec64ac40909b64c4e16460a4c862c3a3f857ebfc0c3122250306c266cb1e9f9f245934", + "0x8b3ebbbb0ccc466c72afb4c27ad99d2d4e98b5aee9c05bc283ea3332e5f67a3d9263b71d16b20db31ad4d8f48174b0d7", + "0x8610c492ce130e76c06b7e0204617087ebd8f285cc3f007897c253a0e1af50f80a825ea7fa3167f882e146402fd342b7", + "0xb17f04a257d3142005b8517dfb57d28661604eea9050ce39c60ba9a05d23897114c59c55af199ed186034e456e111cb2", + "0xa04cd806847686ffe023db1721fffbc26160582c239d5bdef08f4730e2fbb64c341fbabf1fd831af6eb84a113ad7e2f7", + "0x879018340deed1fc762e1b8f3a8b78a40539d6f917215621b725c0a3aa347eeff60765e5ad6f4a36bbea51ab77f88726", + "0xb421e65891dd0c6641e8ddf479b065163897a418d723fc6dce19046501e01c616bd19c9d5fd6b395e65abe0ef956d53b", + "0x89350af1d432a8c209b69f937d2aa20a24d5eb95c5b4cec097ca3dbbb3ea9efcde2a8c56c58f8d7901b96a627c45df9e", + "0xa32d6b31cc9efbad4bcffd8b0ffa46b8fa97ddf3453ed151d7de1d03a02cf233f07415584893155d2d7e14b9534921d1", + "0x8efad47caa32277eb04137c92def618e0715c1e77b5053b0cdd60fa03256fa1c9fba9aa86fdf1c04cda9c5450863d58f", + "0x8dff9d309f7294ba750158e70474c978d1dd98739df654945f5f29fedc607caa06b2866c93a0c7b830ff9b26955833a6", + "0x84bb00fbaa4358a2563abf96d2434e4a26acda87b189cd8d2aabde1323dc1eb2eefcdaba3b90e9ad1215ee469745b72e", + "0xb75acb924159ecdcf49df587d5ac1b1b04291600a4f530fb7cb6231e7fd1029f8cfc957c891a59182518480c2947f472", + "0x8d2c671ad0d442664a0cf267b39be742b1d3760935137e4c50195695bdb99254c4a4d5830634059d96dfb2b4e214b067", + "0xac27b31843caa8140e729a01e7d0229d7c221feccc89ffc173c11a419af3db0f8a47a41cac358e15ef41f679a3f0b96b", + "0xb0b3e33c590bc00faeb83f4b160748fea4fad3e21dfa324bc14a138ee8c5e18743b6bb27cd0ad7c7c28c2b3e92040b0e", + "0xb0d2882c5a0a21fe05b522f2e8a4f43a402bfc961874deec962a1e3d039e411d43bd3d95a26d930c2509aec8ed69e2e0", + "0xaded1e47b3ea6ea7276736fbd1896297b9ead21dc634d68ee56c20fae3da90170f30ad0642be10929ecfe7de5ad8ce5e", + "0xaefe525c0dd24d6c0a66b43ebc6403ac75bfc322d1a22f76340948cf3536d2ae87290ca80acd3e55d2df9aaf0fe6bfcf", + "0x979d1510d3271ff1f06d9cefe30badaece436fae8de70b01ac843850f678aa5f48821dea48ce1c363fa35eec37283f3e", + "0xb8e8d10692f1bad943052fc366291c134a0fc7ca4696feb216aed46eb32de7333a9ba4f553389e7e58c8fa96ba023f58", + "0x913353bc585c0248a54d4705b5e29cc778f304472446eb4baaf30bafa30f2ad0643aaf21196a6c4d177b11eb4e2ad5b2", + "0xb25a0e3b9f983c47b8faaae8549fa7d00d12d7145e1b232d1813ff94058ed603957a340beff25711075cefacde767661", + "0x8515151729ce9a7984af3b94f74880a2402ff853b99f924d581fd3935d8ecfc80e2a1185918a5b1c4902106bd1561ff8", + "0x88e4282ded5e2163874f6464236d5bdcc3c484a0fef8ed0da8d0177973e8175432b75afcde2a4d7d6aefeaed52fbeaa7", + "0x81c31113f2a5ff37250f395d6722a43cebe2a267a0ee40ac06faccaffd7d6eb522103f0431a325aa46a54e606b14de84", + "0x9302ade30ccd62a803b9610a397661298941a644b1ee9d293c63a6c3368fa3557dcf6bfd0c9b44c5c2a6be06d1baf663", + "0xb4ff9f1f6a2a64c50b0a16980ca7cdcc297c6f93e11c580019de52f58381fd0f60a66d3e010fa7ab56bdd250e7b2df2b", + "0x8e57eb61ed3c919dfa0f0cbca2cf559cbede5bbb1e89ae4849b380339cb1c567c98fc2c671211fff4df1a058d46a42bc", + "0xb3d5b45b4096eb088523d16bda1c6aacda01473533314961e6a8de36ccfb35d4b717eeb1ee1bce47ad3b80e9e5084d4e", + "0xb933ff4d3c5a77cd7cd32926266d4f05198178ce350f7215e512e71b07177ac1ff89ba183e424138e1fbf088ecf86c24", + "0x8cf430a6e4eafd23bcb5ec8ca3d711bb56ae719c8621ecee964ef3bae7c53044f7ab3d5d0b911e09c7543e56c1e82e11", + "0x8b3c34f5321c9ed48024196e1e941fb7a5975a045a5a9de88d7f200fc7ffaa0b3e500ab7b535e02bc5c35fbe420e2c3a", + "0xb3c235b65fbdd5c4c2aa45271b9e51674f9a0383a8ac383b0de53125a67c87261540a95b8f81ffe67ecdbf3955b13814", + "0xaaa93ce79ed6e7084fe906c9a1002435ed6829ee3d1380681b902d35dc9e5a23a214ae12dd4fb76691b0016f28d43651", + "0xb4c9533e50ec58f75ea82e2aa7f735c4257bdc1ecd0da0b6521d1442fa61f19e4f73cc90972b47a762f5cd9af591d957", + "0xae0255dd70befe7eb979d41f9a7407040937e7a879daa64353c66d524d3d3cf1d5e854886a6c32c142f4673c56a4df1d", + "0x805fc5ea840d1c2e6b35ce586309698530f056b41de7a403d9e7d81efc2d7068976e8e23bc0b9ee256f39b15bc4f7ecd", + "0xa8de5055b6d2310b6ccb211a397077b211683b05c7e68e55ff05b546c5c81522e6097a3c3b4b4c21fe06667071beaa4c", + "0xa4014d39b23c13efb4326956c5ee476b1d474663950c9e3e45aa1345037be862cfa14aa1d03bb388085bdb4ba9d70a59", + "0xaebe9a9ba34d6cd3692a8bc0b0aff5648e16b36d6c123e636e9260386642e29d52ba71ef7778481c1b1cfeca7fe6acba", + "0xb59706380c9271918ee16a04e84e91046caf99623a0120aeb37a7a98d4c954d3d880960086de6cb180c8b922ca1d7405", + "0x8dc0713371808850f2137a89c33fd55ec2df6a028e22b2679e09f7084d5c471451187f6488fbd9b5100b84593540e5f3", + "0xb492c55e470c35c7a7efa536f3e7c1e586b623c6669ba6eceeebaa1f81fe3b8b927c2e522fb12e603ae246d9566e4d23", + "0xa5148eadcedab9ae08f5db6265326fa415aef46d0b24155910210338500be6d77bc9fa6f6e284a4c2552dac09167e450", + "0xa0af7b66c8a1319ffbe7a0180795b442cffde153f9a871046d6bdef959378c3068813c516e280371825af06ef2320b15", + "0x95479ffc4903f252fe58632e833d63d963469e89744d5c91315d38eca21b98f1ad6fb3ca77d620a6f97d9ca3aefa1f7e", + "0x84861bdb5880f663a5d9b5e89b59a940611a233d82a9895a330464f7e9b7a6965c2420704f3adc61f876584d06771f03", + "0x933c374f176381a3a63fa98d238d3b7d337aa745528e271168f5b550fb703664c3128097b927b5922b4ae8fad30d5e40", + "0xa3ed2c5080c52ad1235fd93c9bbf128b48ba8abe364247104bbf298582930bf3faaa4f4b6103062a4696e68c44f79555", + "0x94668bae91eccfa8ad459588f927bd1a169af834a76132b2f2d5cda26a91094cb94661e3c59f7547b290f827eb43125f", + "0xb704404a487a7dce87ea8207dd5d813378a345375e8e2c07de349c1448a39af8672bb4436779b3485adc46df2212f409", + "0x9347dacaf6dd678574a4f1a95df79369e3f5543c565b1580f907ecfd17b5d6e1ee3322d83601cbbc6d6ffe0bd2833a83", + "0x92841abd813bd9934bfe945e428193e33ae6d4dd235a16edfecd6e4184abefb8a1f85015ee83caf9532dda380fd678b6", + "0x95c14a1d3a1e1ea18f8a61f34b85ee8a794c95d3b4b0ce6ffc89013c9a80291a9a2487b00bb3de51ca2e4290fead7482", + "0x962fb52a2134123ca31d91027fe9fb62dff4e0542c66b55899a163e50f6ff2c4c4b9c1f5b5b3d6c6dbda40e757c0bd3a", + "0x8aa06ae95b0ff361dea2792e465436d810b86f803ba6121ff93fddd9ba60ce47e846eb2d248b28f2c47bccc9457c1ece", + "0x81adde02ddc49b6cc89561716a839fdee2879c78d1ea0fc0418a6cd4a2a8189a2bc245bf2d1e535dde07e93b8a5e18c0", + "0xa7a5713055455728d6d982a6650d1edf1a3b4612c9072ee8ee0bdaa3992963a6fe91ca242fe36f839595d09f6a47aaa5", + "0x93900cefff6f918dfb12ccbb256adec89fb8da019324b811398eea03f6fd34f28a6eac2ce5580904cdb289879bd4b4d1", + "0x820262cbf7864213e768b5a38f39d27dcfa7baa5abca557ab575b07c33917f7b0f06f0a6abd81222fe8a5a69d95d774f", + "0xa33114d4cc3cc84258fdf252e754c8bb1feb6a130785d35a78b4b05d0f782424a5ce0f34be3c1a14e3bb1bc0246bf0b6", + "0xb966ca0a11f0361e611ab2a8907f78a3d639980cae405d380f3a080125c734059acb08431a42ef3a60ae9331a07e6a5b", + "0x9305d107311654ee727182a1683f322a78fc637bc214eae311f8778773e5bc52063bb0a902a5a962a4a26fa0cba3b06c", + "0xb3dc808231c75e681aa2bc4358c41f01e371bfa5bd504e7bd2282e35e72a2889a51747cc008dd4d8b2a070c8e4c2d7a5", + "0x8f05cc76848367abf2020461b6bcc1ecc412ae9f114d44715875f25f34d8cd26b84b01fd6c0640648676b8d2d5120814", + "0x8798c23f0ca8a7b23152ce17086f31e2a80533067f15ab4a7b43c127a5d8af3297115a3cd7978ace811fcc38805abccb", + "0x99a917f54253059a45103e15e92e1bbdb03e80584a85b658f231aa5947b29388f5b04588de1ed6de998741d360015313", + "0x8b0ce3f6afb5aa83ff229ae1ee0f986ec4d31b113070c7ef3c1ca3613d74e3f23cc1cc81957bddc709a5c5bd85cc64f1", + "0x9035b71e4cbdc7c410fc03a85543aed34a1c0a98e07ddc933e64886f1796416ff3a0f7754b5246ec93d617aad0f53d5d", + "0x87478f69c45394f94c67b7196f60aca42823ad92ea86a427d705576fa6a9bead40b1a4106767b8a20411e757f8762b68", + "0xb36901adf577f159b4263821a48fc5888e7bbd6c9f3857968a9cd02e1a1a788c08a566b7bd5bb6be294fa5ab92b4ff6f", + "0x8a738b1392aecb35a5a1f12920522997c9016a0455354e41d2e1b81d8ec9b30a90f71492c7bc122505b2ecb0654545ec", + "0xa5a422515f17f2bf4b9b6c4b5b94df27ce80826cc3ad2a8579eb6666c67a96355e60bf227b36e1f082d749ade7a38a92", + "0xb6d0e36a98e0518b14728bfd79db76c408f58220111e8c4dbf5bcfbd7a85bc68022456196f07b9f40158037a3c3eb90b", + "0x82ad91b812d08bfa815a93b47bd3656b493853bad52656450eb408fc915e430192ae123fb9daf4aeef4608800e818b74", + "0xb8ae5b30118dda7b972464e14a96853147c4b362e9cde22130250447575c0d8d05053202db4c650467dc16330cb54b36", + "0x835d913a3d15ff205497b98107eca77058beebe1aa35ffc20241bbc2a9b4d2019ba41fa3c9b43fe2265a1110b5c2fbe7", + "0xa283d88acbddb50983356f2aed99c2f153b6a8f489b0597d8db08ff7e3b04392609e01aceb37fe985f59773327258195", + "0xb6927dc3318931eac59c6e21def3ca79154beeaa4c57e11ec1f3362aeb33445366dae770e533aaf33c273eaa4f54275e", + "0xa6033a62119e077b438e0170f27835597e21c1d6e4acbd53fec7df69bd1372148f90966732fc5c004857cdd44b8a03c2", + "0xacc764a116e31d63f534b3e0e42a3f899d817d3ec32fb4504045bce7ba3a952ddc81a33d48c5b0499eacbef4268bd5ae", + "0xaf5d1f6a67dc6361e19f222a24163be388033a3fd0d33ad204f4411302668436f933c4a91c6472fd4262397417e3c588", + "0xa2b1fe93eb481d4fec6fccbd64945a12cfeca85aa8b8bbadc4e4ecab2f3ef65616294dc168d6c955744b7c6acd712012", + "0xacb6d3e123572ec20d0ecceaf4916401874f0298218b36a0ce089acef90329204611968c7397c2a518c0a78d02a9285e", + "0x88e4457b1c9b56957b76a08e98c569fb588b081e0e420a0d859b70296f648a8d64ff35ca61a39d1b8ac3613ea5fdc2eb", + "0xa7d1643b3bbef49b2f9fff326061cc27a7f65228e40929562de73e1c66a9d164d42bfcc3dae9103b2acf27606f18b031", + "0xa66e3b97efb7ce4e81534453d3d41ecd4b5b6e9bb829b07b5afbf11fc6ea30382a0059c33c97afd906656ec19432830d", + "0xae9a17d0044abbf3e6aa2e388a986754d6b0fa35d115e410f69ad4aa114db1af5dd0389222b838cfd859d436aded1b5c", + "0xa4a66a163365528b08333f15c6673ca48d7a9b6d17822f1e5390fecad122bcf7ec5656eed2f22fbc6ccb6dd96ee260f3", + "0xb7dd42c938c2ec50c3b3fde92ff629a571e46f8ce128fde7c2d8f18796ba1b1d7eaf7337212f55cf5cfc540c7d2dbf31", + "0xa36bcad22f3408b3bfd45d272f3387cdfbff57e014226dcd1db54bf3f8d1d896fc4fd16640b5d1484c9567ab9322a37d", + "0x8c9831fd5f74ffac203aa6b6ce03acfde8a2fd939b79236a01931d28b424fd8f6b6e44522d28e086aa12f0b110e5688c", + "0xb48bc95abd331d901610335299580ecec02a263d2b03bb0579cae3aa87ebf5e93dd110e7fa4306d31974099fe6e8f58b", + "0xa15e27a87bcd8ba69ebfb6228c3c48e19d79b22978d3a63af553b3083ad13e48dca496896cec195e63b8a4e2c40cae7e", + "0x96f3de6fa492dd2d653888311bc918ab832d6342dc7af9155bc7070004e89ca940b7672dce0a1b4976a7c3018f13e49b", + "0x81a022bee3593997f556ea53e2ee484188cfba0be4b831ccc048ae4b5a6df9e3b4d57c436caae5cba92486abb71813b0", + "0xb9d8e46df67e481c84d5520a9613aa92750c8e8a1e8f376b8ad7b71a3ebd95d2d191ce538e6f7fde3ac5943f70c670a9", + "0x8f0b52365d944f59d2ed445b6ecc4f88c687fd281c1913912c8e6075c3a59667060b42f2c1589a5955e9f3791e23aa02", + "0xad07429bab813045fd909b928ba4eaf262b6ea40b353aa43157e1e683b2752c5bf19eea7ab6ebb8daa8ee91241fbe84f", + "0xb90a99ec1f31c43060ef649e047bf24f2fa7fa9daf904136c6a5846d9479966b54090ded7093e481c52d872c6377eb65", + "0x8cb05fab3ee23db24c9bac109db52895b200dd115209bfa41fde510b29d9871907d44f689fa0f5474d12314a711f6fa4", + "0xb00d8f280ee21866b01ba3de3bf943a7d0825ed67db03d13a0b69f54a4ab389df1cb10909e831ec0af8f1675fa7dc399", + "0xb383d14fdc47df80be46390420603e7f505052b1a44ebf595354726f2b487f7f18d4243709d347e1e584c28167a0e988", + "0xaa951f60d1e069304222a8eb0338a94c8b3b4515d7cee833864b6c222ad76f6c48e0346c5603c35a3b52edb6f9381911", + "0xb887070ecae2884109eed80ff9341f5fc514d59158f5dc755ea46ba396f6783b8a86ffd2fae4419cec2ed57f4dfd4327", + "0xb1a6f1e4d25f4aade76714e52bc426beaa7592b975f07d0a6b372a3f94e7a3ab0e8829575bccc953195ba0c9bf46e68c", + "0xaa64bc4e0d9502d294f0d3e6a1400dc38f28e87c85d3429ab3575c821e1229f1dc8e2c13f03080006bc897e8fc3555c8", + "0x8f215476d94bc2af7d2e0eb68783292e314c9a4f812f3065cf064f427aae165990dc9665011af502f5713f3664317989", + "0xa578c8991e9e29bf3ad7be44bce3817e1c4af3e4a8ba3d82643378da78538787f581b9caea7602b87619e5f8cfb337fc", + "0xabe5453b650106cf65bf2b7faf8ff973b7b3be0e6f42983daaa5359dd4ca225edb7228bcca3d71bcb8d77241b320fa90", + "0xb7ed1d027dfa91d0ca5d797295e359bdb1b0221b1f5eabd2ef76ea3bf456f9aa9788dd00ea24fe0add9e3d9b09ae2428", + "0x96ba0f0c5ac0eae3f0031f8b7a87543ac369c22122681cade0ea33a6ca370cafd360ea6b80758476ab94cb07ad6820e6", + "0x966f6191951b998202b8a63e3b10ece69616b989e9695cda84a450cb953acaf9c4f902200b7492eb66cb9ae0cdc8ecf0", + "0x8d7bf21f76ca0e3b3758c293e66e977f83533d918dc445a09f4f38975ccf7220855627de6460d318290daa03a5f5c68f", + "0xb10dcd91d6602852783bb76b0a286523a0942e8eaaca4e0ee5bc76cf19d33bc631f6d0fda1c1ca51bb3d5d5c7dd43728", + "0x884d502d934e2b045357e981506900849e6eb051ca3ecf3079b485b348372496db97da384f8d2b5a52216b4d223c90ea", + "0xb074162e5d33171477ed48f2f185b1c83e8fc2e7906681f96ed97da8ee86be7476d65e61648383c2766ad9853ead35b5", + "0x90bd3d8b475da20c6e32324e30bab475f2059cd81fa67840a6c831026cf3d5806496a3a25192128da4b819c1b7cd6bd8", + "0x8da4889258cd6ffdf1608af8325230f74abe6a2a511872c2dd10123b491cb09407fb979d80fb1185ebedf421ba22d0fc", + "0x96fe1d9137c24fba18b1ac431ccffc01ef1792623bc334ec362751b7bac73c4d4f7e9bdc2d595ad4731c71808adea15e", + "0xac816ee0b9103f0bbdb50cc05f9c5c8f7ff2f14bb827541c51ae5d788f979c00fe4796b86eb9e3ba5d810925c1f34a17", + "0xb231e98ecb3a534dfda5b40916fd4fda270e316399c9d514dd510f0602cbc29e51c5ed60107b73e3c9721f7ada779f91", + "0x80115e104f22ff2653ba7c4e1cc417dc054663d488f861a9bbec4b9e907dedbb985e6e78f31dc16defa3aaf4f88dabe8", + "0xa0dbc25dde933e6114f2ec22445f1e209836585997b14100f3f8b7e62f5fdc6aa2a85ba5ec39a5197c9d4dabc9a5c452", + "0x8d2deffdeb1f0abed8ba62187f5e1cc06a1e2bc49b3e15f73c3d8e574dfba7efdfb762ab512cce53d7db790a7354c56b", + "0xb73f4897e221927feedbbf209e3d5b9c08f52bb732dc0d710822576abb7ba5ef0e728d2d95c802a00eba925ce99d734a", + "0x970761c7ee891b3ed08253d2c0d28478145d0776e2429c85b4734e5eb7a6416d197d0b1ad3392b37ce8d86fcaf9de7ec", + "0xb4c9e2acb4c05236357be37609abc437612244bb4421d69486050e390d5ddb52887a1b3e1bfe968a90f1695d892ba8cd", + "0x87caac2c93e192c34b5dabc36abe26a844a33bf63e9b01a668c90b70701360a0417ae3248173450c64034685d913f4f1", + "0xa16ac64cd1a7ad46cde1c93024fdeff724afe228818b72bb66176c7daa090acf58e7fc0aabc582ad22486e46f0b96c5c", + "0x936bdd6d67d666274c29765690f4ad9c4b9203e9bc9dd5af558a8d908dfe8d6d4346f6fbbfa69158cdaccb0058ed0193", + "0xb39af8d43ce9d120497888fba0dc95ceeabdd3d84421c1a30fea226e03b78cadca0eee57db524f6ccf1f6235fadd1470", + "0x847da75509ca07fde2277aac9e7622c5874256903a92f7a56382ad3f79d1b3b0cc0b06b2a6b2bd1749ed567e68816d31", + "0x969407bab3f8106a49be63f17ddd603e185afc1c9fc0ca0e90ac415f53923e3c6a69fe488d33403521231c5008bc11e4", + "0x82e25ef35abbd9b98c55a45e7a71791925639afd92780e64a154ad8a94e9807f2643854250f30bff1c5e8806632778f7", + "0x8e6da5cb8cd80d6b8e2321ba3f034ece1813a7b6ee3afac73371a51434a3e66221188162cd9b9ec035326e7e04e74b25", + "0x9868bc3e60478fd0ce37d35e0e4f7695f1ffb7cf2e05842b3a09e832af33c7ba48448935d425196fdaea9c3e8a5122e7", + "0xac7733adfeba1da388eee6540a127d0eadcbd23770f2deec39edc0bfb1002beacb9a8c7106baedb22e664f37771c1410", + "0x912581c23e3ad0d7eb886cfc22633fc704e530b6b4977086f68f1d9f839bbca3bf0162acede87c853e8ad8137b5cf345", + "0xa0315fee6285a33d4ec60f6c1557ebe4473e8990ade0feff7e008d3be1a709f5f486abe784398734d9ea1193929697e8", + "0xa44a08d6fe0a22849a8f518ed9b30b78367de205c3301fc8159ea273076488299b35c362530436dbb7e21b6b9f36835c", + "0xa591ea6ef83f2ec78a402a86ae5b82e330998e18ce66126a89046f169dee58634dfc531b1286277eed49f571df5202a8", + "0xa60d86619b41f59b48c800a302775656266725b44ff8318014fb668f331bec82b3b543ca848a7d40b2718f29e5ce6cd1", + "0x9420d0219d407583fff43c560964e1da06b105043187ea156771b1e4dfb5d5851d06fcfd819c7d8bb6568fa1bdacd916", + "0x97ba0b6731c78eed331530be7cc374a7f4a7cb2144ac73b7c000ca36036f68754d4edccf73ce373dd6c6be55177d89d0", + "0xb4e07b5c1376900fa2dfef8fd1a5a4b6152df7b805d5efc29057d1df2343f8bc841284ed23d2bab5cd1431fb95f71b60", + "0x8017de31e62a24bed74460dbdde1717f3a9cc17e2e2ca9848d77c3b5c364e7e1d58ac0eabb3daa2b7336edcc8a418b01", + "0xab6e409231b778bbc1ab74c3062a376c5287c0cbd7d19d4ac1d5da1a8d0571864d0723944da72581783cd7b6b0d529a6", + "0xb5f2fd4ef29a2ac847358abf2b3e7a3567b8653a4b9ed8da70809f2affc6ab44c65cd17f255db0cd8315e4801bb1a408", + "0x91b61d5d047e9c672d7312f563b8da90d9c2c1c1268913656f061028748a351e116f524593b1be7117a46f168b3e829a", + "0xb6c10b09ecfb92168906191756cb824694caa32c6f2f9b19c51658d44dc330dcd344e7b04333392a8a93c73346a3845b", + "0x9431d01a121e6ffa15c32e724dadcebff65f806c11717b050c106c0c80e43e622130f41224533d13be4a8d14a66ae1e7", + "0xa1248085c85855b4df6eb5a02df0dbd5de5a8a82656e1a5f61214885fcb75428647c8545a848960701d61c3002840647", + "0x9867caba8f4be9483df9b48e2bfa024e79e6797adc2198f2b5115d7283931fe4cefc382323edfa1e850c3970bd1a2d53", + "0x89e88c50c43d7e966e60d49b3afea792429563c93550b10584c91e4a827a3617971eb286c39205e2af4e7dfbc523fd8e", + "0x8ed261502f95814410fb081e7348eb09f3a3df22cc3ca82a2f071abca0190e9f041e8714b811418caf7e1753cf284e9e", + "0x87ac65370073b6bb85a945e138e4d0a5d71ed88739f72b9ba747d2a03b5d4877e8e526026348d2578c752bc4102055ed", + "0xb07de38d07906dc2838be840c291f467d9b695c62175c5afa46d80f34674d061107d6fec6847ba5f17f2d8729f31f5f5", + "0x899348bd385a7c3d38f9d740001c9a543dd8496b58807a6a73180c94f3aa5c15a56cbb85cd7124458e2ae44a454a8a58", + "0x91b70c3543b8e21cbcc8a40cbe00cf2ee0372ba9ddc7f610b711a070110508159e6a52e8778b20f0194ca09b109881bb", + "0x8ab84d75831ec1e708ec74eb6d6de2b13bf97e2d2262ece39e5ba5a4a3049c8303023e40fce5e87b237bb1dabfff5246", + "0x914ac70dd91ccb6d7b83a5ed0a9250c66e77a815aca17416f1796fc0e1c26bee7acec5de11e65061a44d2d9c35f5d19a", + "0x8867260f8024f533fcb75d9c3f2ab113d349504b40f650a2c92bb46aebae3a55de6a113cb6387bf00eeb2bd4719d87ea", + "0x9976dd4e56b16fe11533dce2734e2903a3ec986dca6540bd9ca8b758a59a1e45b1e69c0b8f094d42cf7e015363ce37ff", + "0xb48c840786653a0f3ed6b07f8f980284c5eb2dd22e9ecd5a0566754a1534300e129b39a8a6d4fc48bd403b351e714f05", + "0xb1633aae7c5e5c51a82aa4e4bf9f92c0cd30cc1067b03364825ecc492fa43391ea075195f2f73b99a11dc49f670c0e89", + "0x8769a592f503bf8ab03d767524d9ec2223c502ebf15b69eb4b3d53325ab366888afbb668bcb380230b5bd74b32d90a44", + "0x87439671fda66bf5989fe1fa2aa32519ef908aa6ab3eb34eb5b7d908e9a7db2d679170cf3fa0e0a388a355b8c51d306c", + "0xae1ca219832c90554a91a7258ca5598f8bcaaa7059c574803b2688d8026df9083985c2f8f4ad3aa9b122efe64e0b2481", + "0x94916e6dca309d9c7afb9aa4c0bc89a3de875a8537cae1fd32258b34782994e5be5c4987577d697ddc86b8d68dbbcbaa", + "0x8c5361b85176adf77ab1949d34edd562d8c16979e33b59d09548ad372b8c913ef385166bae53c8fef814a529fceafaef", + "0xb968172a6a831c6ae53e876dc4ef8686879cdadff0aef4147c4dc3ccbc173f89748b840a30ad393eaab69e422363bb86", + "0x8fabda060f8bb2bfcd675803ff0a3f834e2356152f88bc79c23f58fbfa6b0c82850f281f7b8fd2a5e16230aeb4077320", + "0x8e5c887c318335c5561e63fd3c3f64edc669c0b03b217e3ae40ea29245885442864dde15751d7c6ab177a91fdc1f7235", + "0xb2f67f9d64650c6b51b88e7ee6d6a796b453131c93a7791cdb2d0a4922d3c913a4ac988bac5b4b9bfe61469886e1e7a4", + "0x96b836824dc2a12ffecc6a053f7549b7faad9808e98bf20f3c9146fab05098df56fc2833a6002eb39c935fd8757d4716", + "0xa4aa33fa77b62605f751bcad91333659e9345967845226371e5f38d5a7f72405d0e30777b485b730e6c62d8216790cba", + "0xa041bf3467320df4bb7baee569cd685a65c9d0e431824b7de93ee47ab8b3ab20298d60746fea7fefb5bc82d3f7e25dd6", + "0xa85842f11f490bda22e9f73409de0909a2e61efc6d8be0c3f561d881988b4d2e6924ffaf0a4c40843481892b272943cc", + "0x94de0ecf58ef27228f5afb12496c53b075bb347f900b2df98f47ceda8675bc2941aec04d1c8ca0dec0233430f2759824", + "0xb1795a70651be509c0955b07d58a1b8655a2e6c292b939b6c156f0c0983abd7e416cb0cf14afac6ceec85f2c46b83a28", + "0xb6beb936ea1f1639ae59eaf53015dc1855ca0f798d9ed72607edbc6c119741e10af5354c29571af8befd83b8255a8f58", + "0x9424188ceb15c1b470c4bb17c71a37af56c87625e7b7fa752099802673c3a5a99d16e7d6dd8f8b680e89b75cbe7920f9", + "0xb9e22b5df8318bc0ff81003e8208ff3217ba1a84edf6a0b326b7180208d3a9144c6fa54c57ce6d6071ccb1a40eaf4509", + "0x8e5fb55da49feb7a9152528ad6a6766af75cce249eadaaf4806c6d4162f65f3c7311bcf8da72b96f6636cc019546c05e", + "0xa55f751de82aed5842f94d1ba1e29976c3d0146267b11eacaa4fc765da8d2acf617d3a65a2a74aa983130851f8c57d05", + "0x9647758fc596b78fb52db58f2ec31cea186d9d4f68692f56e474961b277396af4a51781b0a358a6a6aa8138e46443a43", + "0x9461f6dc72988b44c662865cdc01c0f970f078637859cbe6314fb17d2cfb3451b222cfb93a5c6eecafd1ddb36de075ef", + "0x93b30bbf4fa0926cc5483ba9803c8b001aa80322addcc866bc514f2a10aa43bbd86008e4671ea26d8e0d2ffd4bb8f2f1", + "0xb44020d0f062a001bd6dca2bc3ce61b17efc7a121a9035709f01a8c34708ed0c1c85cfe98c534189e0669eea719c88fb", + "0xafabce43f35e0d3201b60226c72c30177c4c5d75bac654fd2b58b3ce9de7d83ef01be60514817f1e7bdb525c910b8bca", + "0xa97bbab394253ebb02ba47ad391db3aec1b4d03e88ab3e7505730640558c11fbfce42d53b7f85787cb564208d3dc826f", + "0x805a34cb0c8c7ade28c69dfdde46b7a283e539977602aab165316e973c62bc65396b6fe2c96750ba028c550de03100ea", + "0xa0be38fdba281e0c248933ed73f1119f90e34d5b4435bb704a5fb7c20805e195518a2a424bb483f16500d74f440d4a53", + "0xabbabc7db0a20030c6e687b89162e704720a010d7ac53b9766a9ccb7e02d4ea1926792f5263d715cb97d67f2010288c2", + "0xb9e471a7a433a678090fe4324739dffe238ed7e9a867159e0b43fa80c9c0798cac6b58bc09a389223f94f22fec43e18b", + "0x9818e9a42ebf415c6d970c87261645f876d709751c8629d1ffbcba4abc8e3a2a1db8c4c6a6324dbf433c43fff62803d1", + "0x8290ed53eecdb61157cc458dd081b9e890bed5e4cfb643d11b549b2c65fe68fb981d4311473510781945b0ee763a84aa", + "0xae730a7c69866f22d8f9b0d8e17d7564c25763cc77a5eb718d5651b9c5198b2b9d3eed1c066f4985b2f6d7edb0a109d2", + "0x88325e421a1be440175293efd498cd167dcd0914c8827ebf64ad86788f1fdeb3c16d3de7a681f958b0f49046c54fd804", + "0xa8f592d6ba7fc3ab8ce8260f13f9c4886191530cb1d7505d0beae54d4c97d09712930b8f34ad74f1ac5ebedcea25dc8b", + "0x81c0853b0310a96674a92a144a14c48fcee0d72a772451ed046c284f16fd6447f67389ff7841d752a025da172d62e73e", + "0xb9f50526ce4bee12fc3fd8f3582f3829b90840f6eba06f37b53febc1d0987bbf58107d73fe4373d79e53827270bcd817", + "0xa2ca28f619d4821f450b9431bdcdb129d4f35dbc2a4976e4d416dbd14e378d4d60a517457aa0343f7a5e60a7e246e22f", + "0xb9576225cf7e13374d3975703b3850251d53ccafc6feeedd07be2b0bdea63b899139a1fb446dcf76f62f3c03beea0486", + "0xa88df9f6e95df995345c6265af546158499fc0d89447d3b387e7708fa037f95ac9c4e20ed35b749b8d8a7471dedeea87", + "0xa853ec333af8f35d51ddd6c4d45972b68fb36219e34278efa6cce02bf8193d72c6014ba6961f8448785b0a43a31a688d", + "0xa1ead9282496e590bb43908dc64341595cd22b844624e02f2daf597f708ab0d336bcacb5862bce3ce23d1a9616fc6179", + "0xb97398d8ebb52535a1ce3a10b2255d358142ff653def823ad9e9ce4ca5f372c6e7c9301588ae5d914b2b921a0fac7442", + "0x8d0d292c7e9122b8d001b3a3323f9d37dca61de5a595f9402ab0e53e941c83f80237a853abe4aaf012a35cf59df48c68", + "0x830535a5a8268d5ce4e7462fca4f809348908ae7ee117312244e0a9c30b09d91b6f798654d8064091078400346614e04", + "0xa44a90d3d307ee3a3c3838ce43a873311789a9b9292c62a01622bb813a02f6defd21b0889cb6dda6d7225009cc6d2641", + "0xa219afe00a9327f2c937afabdf5f10bca0687f48d8f7a2a046a52e6924af613f38cf82454df4f412f5991ba20b7db74e", + "0xb448ed4b15ced4de098781793a6e850ea1547d514646fb8f1c137c86e14231ac4340b308bf07813fb813cd02e47c015e", + "0x905fb68b8f5bc14834a06d61f3da686bee77b3b590a33c640c82f34e68ab993f8c4897df463973d6d9f0d53f9ac5cf5e", + "0x991cb6857dd0b3ee6597aa2fb1f4ccc962cb038228615466964680795587240e6ccf7861ec220a53ede1e2e9752e1cb7", + "0xb823dc0249ae72e2de91108cd4ae6d6af3e464f12a53a46ca583727c7351a67f2d12c911534e234ee187389fcbf1f621", + "0x981ba6bda1816036e75a864f635629a141905a4805c706260e7a5e12b82dfa9de4f4058143065b10a1012adca6b7d083", + "0x8bd8ec0e77a6867057e5393d82132474eba9fcc4bbe025544bab0ada4ebad3d296ceffa3788acfea0a44406e2ab637bc", + "0x93eaca3a7f9a0dc809eb9f604905b0cab18750a9bfa42d98d99728a6de6e0f1e05b6e98bb3b0d9862a69eb57ee2e18f3", + "0x90b077d7b7b1651ac0d723978b3e408191c2b8b08247fe2a7fd69afe0615dec09e43771cd845c2cd064b56295e53f634", + "0x847e8f607332129e95eb1f3e00003b087e92ebf1ac9477304b0d54ea38419fe8581122d21bef8d034f788a9c92f4ec00", + "0xb0301becb003dc7cd56ea7d830bf0fb4b85bdb33606d8d9ab2b70c6415ab5c8f4934bb7079ced16081b8f6d16b77c0c0", + "0x9068fbbfcc95fff7ef79ab64063dd9bff0c40b4855eedb39bfced9250cc351b5b3b1bc6c2d038cb6d59a12a41b3db664", + "0x84857e081fa1c6c08bf7b0bcfe7c6d74b57cbad1b67676e99686bcca0b17715ede19f826517dce3f84cfa014e11909b0", + "0x98fbfd6a94ac3e4b53b811e4d275b865486a52884352ff514889313c7a15b07822f76d428533a0f8d3cb42f1e6f72356", + "0xb4faa1b1245aa6339b5bb987f3423d187f6e7e5d4b4b341de87ebdea53b124932cd0e586470cf4a3b1060a126e4ce7e1", + "0x973e88d073071c2cf5ed643d770a45f6be7b230896caf72a2cef10e56ff0a4e032d6ae1ff4c19bba2cc29f29ba70cc19", + "0x8d40b3285879fb9ac0b6c9d92199afaf4716fe21edcd56b1a1fcb6ed298b5ec5b3b64222eb6f0cd1086d41872911068a", + "0xb5e338a02076ad851778d590ada4af1c217d035c2505b891163689a554e5a957219410bbb435bbb38c8a1515146f8789", + "0xb1d3e990d027a38fc8a38579e39e199d9984dc6d857bf51e2ed5fae061c8723fed3c74662405378c29342bc4f1fff7ca", + "0x8679f10f866804b19dd0b14b24068c1d32908a52149d33ab03394990cc60c0f388eef02bc0db819f92f8197b1fc60c17", + "0xaee5157db1cb7ca8013b0c19201ea1e7af32e4117896b3f8ec0ef0b2a4ded6a5e7c893281865cdae7deff4532a6a3fe0", + "0x950315818b710d3903b679dd0de0619059bea7dac3bf4edc8fd4a6dba81b7aff9bca7cf1972940b789458f287609439b", + "0xade345a6171b8e8afce7a455cb98024d0d91dfa347632e1a5a40721868bfed1c1959300f1e1e39a551d99a4e1abb563a", + "0xadde1719c13b3ec224bdb6b44dc2c5f2daad54e7ee736209653a0198a769180019d87fe6bdc37ec1b48f0212ea5a8927", + "0xa3397eba3ed2ea491e8d0328333689f66b2bbed0e1892d7b14b2aa45460a12e4d592d78a5d0ac20bd6d34c88b8f1f7a3", + "0x8613160aca85f0154e170b1b3f1052ba984f5c422c4c25e0771a53469c274130a31f875a0ba9650f77fabd910cb10467", + "0xa91ae4d048c56d5b2383a9d8f6396837543b609d8b0be139ebd5fd31fe4a90071656442ca7f14136cb8205734d929b5b", + "0x8e42732269c77887f105d1c326488025f9938cbade678bc6b39941311360408ea6baf274bbf5ffff996756cd2390bf1d", + "0xb96e1ca66d51a186237fef402bc4e14f8f96a138db377b7e2c3243954b6f47ca75cf4fb5dd081aaee634b5e2efe2a065", + "0x81d1c20d76ed054923c17b362b50205050f185137ea10559e35ee7e191bd89383b68179c0aa4531eb61abdc239ae6891", + "0xa350b5778e26ee808466619f73900e09bd387849d072c0c014517d16adb4e3394673238c4f4e705d30b4ec2edfe5a695", + "0xa13657433e39c0241d48075ae8ab1efe3680c96d078685c5dc0ac3c49d468db98f2094dd4204f44e8e90bf54059b5807", + "0xa96255abe489be9d42ce6fa76ee90e4bb6a36421fb78068432cc935632ea5b5bb2ab70790ef79422f93d35d1034568b0", + "0xb745d643480edb577b1f92ded38a522236fa1be2944ad8102ca64c3d55f6297b7e0aa1beb082261af1cc334f5a723614", + "0xb235ccbf94e2bbd3c794bcaf84266141c0e04ecdcd7d2def83a7eeb86a2ff4dd3ddbd8245296b27344770f3d5d332f90", + "0x935f3e4e9dceb4f58404ba1a489985001827e96bf6be227a8ac4e2eb8a950d4a446320ce3a245d09d2d74776c7033a3e", + "0x99cb7f3d6256ee8918f40642f5cb788f0047a04c482146e70687c3298629bf082dd98d4a4c222fbfea3afa3d7d806f00", + "0xad6abd2fcc67af691e76792432b83b8cd9b0a9e5e73de21f89ab54081ea002ffd904d77ab8efb6906790987e29c53ff9", + "0xb6de4c3a45ed7898abc037a47507f46f7327c057a911529d3a671286f98e79a421f4586a7ff3235f1892d0cbbd0e7bff", + "0x9120311b071d38214e39f4b48ce6299ae9297c7b76ab364353d3816669cba56592fe4c7f1f93507bec7ddc1df471f0f1", + "0xa6daf71681485d01ae7fd4bb81a326d3d2764bbed5d3be45efcbc04aed190163ce8f9d04a84bacf25ec151790f8fe917", + "0x9534da45c2a497607f7440f61943f4c16878a18f0bbce00dd644de88383470705b489225f5be4428d1f988256b70c926", + "0xb2d1b633b4832dab1a530a1d85415e7fa3e4a1fd383ddb898a79c7ad028f2dd8fbd56b83600cf481eb14a073cd65431a", + "0x8c43dc994dfeb5f22df9560518df32deb1af43f254acb8e6f93eec3fb3ac80081b39610800d0822246e130e8c5f7a067", + "0xa18174ffb85d13b7edde5822f22872ece12383d79fbbdb8c02bcc9f654cea904ed8c03b8709d70736dd4b308ecc1607c", + "0xa54e4bb27d6d561261a3fc48705781399f337448c0afa68c074918d2c14ea7d51263199b01070b7161c3db8b9949717d", + "0xa7457cba2c5b455584980ab6d0bb5253dbf2cafea4efe5bd769020b970dc35fba4109d002f5934610b8b4a158252ebdc", + "0x877d4111f50f77463b60e07843b4521b2c44629a7deff20dbabd412206a4fe03f976de1a3897b9df7eed16217f03e2c2", + "0x84d1ab99732fed1470f69fdb499dd3de795b055354e89d522c6a7df4d6a5375052c8befa4dc7615d29b3d92ce7df2f24", + "0x93bd139c343d8b83403e04547072c3e546c67445220afd06c119f7119730288629439640302d0628e74fa596e305c0e0", + "0x8157b5ab48d026684f6b51b802b4d8e7f85ef82583d1e8dfeca042b47a0e0f58e30cfdf4738e6d51394b260a4ca7e19f", + "0x8f03d5c1720540c29a1dee44ef5c7f8b209094ba8376d8e5eb9b52537d9843912b68562eff742f0a7a07f5faf6abd1ba", + "0xa15e4999a0028b8b083c2afbf4968a1f0397c26cda8dd7f6c134c6a860e740ac4bf1a1936849a4f2080e0cc9f8e44387", + "0x8b71fb85363158c7afc3c41422e9a32ecb2d1f9d3c01fff00b77e0ec6a8661e95b552a7f05f4acebee751448ed750684", + "0xb34125432d0704c0638090fc4566780d2d8038d803f96e19ff748325f8b5579cb8867e12491921feaf3c0df949f36aab", + "0x968196e10bcdc6cba28331a229acd54b59edaa83cad0f8d14f39d787467bd5ea725a3dc3d50accc334e74c81fd762cff", + "0x968abfa40af365986e68c47b4eb3562a72793fbd66a7d1b3804a5bac8137f0a3cbbf5cd306097cbf1a3b95c3414fb061", + "0x85395fa84223dcc16b7e620a7ef6f902f7b29dce7760f57baafb37d985755e65623768b8bd745c8de7d00e2035aba7ab", + "0xb57ad86ab3f5cb00ca0855088921865893b6e539edbbd504238df2f9b2fa7c7bdbf2d6eec6ba8e2a70a4c4fa3f459a97", + "0xa2f203ed1f07cca3f8c0d35ccf7a63216ab98c9e71557e829dea45e2c723583bfbaa7a83d66521b08a0718c63973a6b2", + "0x99a3522974525f4ed10623bae83dddace6f9495687cb9cf4ef52c8530b05672c2b226d3fc5058c56462ab3737a068baf", + "0xa4a50d127ad06067f1eac2d61c0a1e813fceba2e5e895467b5e6045c9b7308d3678bed9212b98e44c19a1783e0f57bef", + "0xa62d103ecc1d5e1d5cb98a0bbf9682ad65774d63f67f95bcbfb0cdb5e2437f2279043e4426d490f534961a2487782cce", + "0xb12fdaa5ca77456e6e96eccf97a303ee2d73f547916ed67378835402136c2aa03e63912edf5a67785f7ac1636f6ddb51", + "0x91315750043c4e08c7e4359b9cba25309eedc9c85672851f05a0651dd9b9329bef00a79cfe73ddc308d97cf548486b47", + "0x947115aa6cb3c635bda7f3c5fc3dd0e4881500d74db4c0579e4b9039b75b131eb5db54174b1bb970064740551e6cd1c7", + "0xaff091a9c7e86c80646cfffbf154ecbcfeb66877c5b773b6e8759649ada1094270e57970cbf2b0a4bcde9bbfa9689b1c", + "0x81e3cb9116f81e583b7579f9af06931a5337fae0d57d9ef777003f32e0eb619b75b74137385f9e30dfe0e10c2120b02e", + "0x81ab49647db2a5a6f47ec757d8427325fe721142377a287c547fbe04ea280acb32d71f3dedf7ec0f67b43ffc5d748342", + "0x84b0e16d8478b798694503ac4a87ff31affe0ef6d2bad47abe0fcb3a2571fc8e4e9c966276a5f4968b2675827a115818", + "0x9567b2edd65974393cf2181d235f877f5827a6d5ca16e77165ef35f6c66370f0c55a2dca5387c02ae73a66b81f01798c", + "0xaf19f841026271e284548b2cfe9fe7d6f9acdb4759ca76fc566de7a8d835408f86627185fe32e705f94e6a719e463cd3", + "0x83883e1c9d215c90948d066d2210528552093a726f0a27b8342b611e4b8639f6d2a5f95bef8cfea4312c1f2203f34986", + "0xa48019b2da37a232b7999f6b668e2758f82132e15ea93608bb2350d3188297c8ff8c791977f2a083ad9773570bb560db", + "0xa1fcc29974eb065a350cdcb4283b2a813f02421b872eb3c15056ef96e2d5ffe2fba0e10ba19a4d271937cf08838e4106", + "0x86f9ec59a1f5a5796e498247c0ef1457ea7ab098247f363329a336a1ee57afb31cc18d35e008a5263e7c401fad5719eb", + "0xa903f95675c14cc618b02f7a0401ab67170b4a143925979791d76aacc90ad1faab828fe904f13d155425b2ffd79c008e", + "0x8f652c4982220b8e9868a621a91eee85279b13b0c2974472fbba11775e6bb1d8d53309f500fbdacdd432170bc76c93a8", + "0xa9b02cfa052b5808c1c9ee65ade446a6ce20174bd2e9d9c7388a1973b0290debbb6fe82697f09afee6ed01c9dd99b905", + "0x8b4c700fdbcb13854c7b1d257a781fb7449a9e3236b962871f11b31b1f2e69ecfa6039e2d168ebdf2f142f93b91f5882", + "0xa9ba2295980603515f80f0130993f1be434281fd4442ce7e68b2fee12b24e440bc0282df67707e460bc67a4706bdf8b8", + "0xa382b85dd64b70296a2d16d1d15d6de80687dec9cc074445fac8de7bad616a95972ec399bda7c2cffa4247bd04413b76", + "0xb6adb37da1c6cba5ddfaafa3718aa66fe2821b43923ec371cd4eb9e974ebf3d0e94dff1ffc1347cee5c9e19af7c76be9", + "0xb5b531ea7f93c4756e5799118654ebc478a3ab57ea51125fd31c012053c759c8a52c8830b53208f74215e437d059eda6", + "0x89c88a5ecee1931dc027d1553b5aa82dbc5fed2a4bed329809467f79f2712fa5529c0f80ce6891817927d0b66d356db6", + "0xb4ad1964f73d3b7bc338909df2ab8889c4faad9b3b8a5959ea81f44c6c4bec95f0fb6e8fea1fb7e09789c690423e2b85", + "0xb573bcbd8f484e350db04eb263187ae4e99ecd03494058e68221aad8d044db82957f4bf23f71a9634b2ef9612a78ecc8", + "0x93c3dd86f7c3105fe482f62b0a56fe43338aef50f0d10f237ca774f834151273aa653e17bf919e54aeb35343ed790c0e", + "0x9069c429e7c6507a755871b301b31c3b4233006c51bb66ea2c9051c6caa52e933ad81a8e879129e0c1b099a124bcb295", + "0xa22203e5bb65593bd22cd5bc6e95a2f5c9a9aac1e14d948a7e0aebce4f009a56623026e0980bd194a633b42f15822ad5", + "0xb1585de69b3014634da2ba76218321ff4ce8476b653ea985a7330290b1bb1445db3e1f3c510f9ae7c22940157e2df36f", + "0x802a70ea7fa057a03d12538c3ad6b348a8e694bc6b483cd62c97af8809627a161223557f1d5196e23f13eddce15c814f", + "0xafe8b0e94d8d9b44652602c5ad15bb0140456d90c95af4ba58cff528e2834e0036572af867488f27cb2d27d81cf02e30", + "0x93bb332d924bcacc41b4b9bf726647d7cbb642847fee5ee7dbf3d2a0489d71802d959a3e905a80ab1f34097328632f00", + "0x8caad1d29fe712bf09d505ccfc724574c8edaf5fc743953b2771cdae006ad9792a889e0c8136409b8f92e2cab5ba09f9", + "0x8678be67412da4d43d74660df98744c54365cf10aa59e522c59afc3836d115380416cb1ae497ba4b50ad31a23ece8b92", + "0xa48e64a5447ebeb5f6b0e0fea29fd5845b378e83f6b06b79b604081e5e723930a0d4c6025627382f6baba8d47425cd27", + "0xb8914eefa2f5613dfe99f11212912dd53d678ed349fe871781074d5b6eed1fc7f2e5bbfad3356a685c52a3c8a26e7963", + "0x836ba66155facd2a1839f603644aa5520cecaad130fcd5cf379139056d3e163bf35f172a4a1f015924b89137f83d366a", + "0x835b70cc340b57a09b1fecac678be381ffa4c4951f6742322c2751cf1c748ffc2b9bee8f155c007d88ca69c12bd9db20", + "0x8e98b4ae7c68941a48a70f703c3d5bc9a4cf6c20c61eb4c1338095920c4f23aa9eeb474a0430dc28d355b15dc6e83b22", + "0xb24be8171a105f203c5bf2ab0797dca8ce61ee07307e1d82fd26fcc064bd8a8a5b6bcae8dd611f8ab650176e694da677", + "0xb057bec8ca008dbfd4982ce4516a4925a61bd68e7a36b182575c6a4044c7a413ecd1dffa66ae3cfe2213763dd0f55a01", + "0x8d270924c541120a18d587cee51711486f09a39444182800355c4193a76789614c6925e6a448f46c1891106f866f08db", + "0xa0ebf85c44453153764bfc817364493166833b0f84b7a7c505a955cf3a7d4c1b4d2dd00145220d8a3207758a82dd8e4c", + "0xa56fbc83a3f1034337ca0d5aa89a0a18f900c3654d171d47ee86b0720c6a965c09c9b06678e3f25b151b115d129ff7bb", + "0x833618f5d13b7919206c8e9666997ef26c04a74844f57150e7268bea540e30b93eb785803535566765bdc899d4f10667", + "0x987daa13c00dcacdfb1f0eb13c38ddf773e7e8e19af125604ede42c6d0907f9ed1e4b8b8c9118b14f9449026802a6200", + "0x99b6e669cd7532b435d01b20dfed29211042beea6de58acd68b6eba26baa1687d80aadff901b5607a2553df047ac51d0", + "0x82c81899cb76ae21838558a1946425c719cf68d07950b0f106b859048107c13e4e83b0f2762ac8590cdd044c3e731f6f", + "0x8f1c5f634e38f47cc6967f2a80a449f5bf69585622c333d784263e3f6f027bccf8910da76435a84155a6fbe9a8adc4cc", + "0x92d3b5515744115dd20742be1a72a455c6d481855f4366a0e960104665db4ecae8925182f32d4e1d9dd7fb9aa246726c", + "0xac86e14775cc4ef22cafa8ac3298bff27fbefa9b7004ccb16d2937128492a2c1319641062f609d27b9314aa225301d14", + "0xa07e1ac19f4c374d68084415fa4a8068c0be540c8b9d81c0837347fe096547d8318bbd804b7642820e43c284af663258", + "0x839266a2fe6dddc446d4b515eb538a27b5a3a5e1a8246f6df77c2de8267e172bb7522aa7985e0503c68db9cf93399b95", + "0x8a381fa29e553fb57e3780f915a86048aa82a8a09059c80154df9490271aa6b99baf6bb217df43c8ea1265e85f07adfc", + "0x8d8806db0093161d7f83aaa2cbf0bfb8cabf823cb54bec094f886da6461397f41d54c39f216d7ff4a8262d12aa8ebfc7", + "0x90aff3f98394674791e194b57c3f4e6e019471df1a74dc47bed725d4c47399e91c88a955612be47e89002f451ebacb55", + "0x8bce2d60f3e82042ba94cddd02543b46cebb8770e9b7833b4e79289d4c491df7f4da0ab69778cef92dd81e5a6f0eb71d", + "0x8246fc9424b5d5ae0a3344acd7d6962fba6b68cde09332c262d7b3f379cac2c650d80cb5ed4baeea16a5557efb6878d9", + "0x92ea8547fedbf440517522c687f1d652ae4637cd072147ef31338a40e11017bfdeac42a32808d33522a71136cc3bf26b", + "0x84f6a64600184c54d3d5c320498282947b8a8166f09ccfdfd6d285cff374312da57087fec3838a49eac5b93315f03b80", + "0x86dfa1485e343c861286c057109119ce8e20abc646a4411696a3bf4718ce03d37fe14b6ea1600d8a7b172fcca6d08ea1", + "0x8dd3404facfe49c2f096d2e74641c474c9c54cd6121771061db3c1757cdb1cd6813d3ffd79e3b839b348d34b7b4f1ba4", + "0x8870cf255b342ffbaa2dcff41910a37afb29ca6a721774953dec182d95b426a481eac7bc107c4c2ef3df9f70e04e0b88", + "0xb0b843ccc630209b9ab35a69f3aad58c76b2cd3cbe94579b5757350460633217246b342fd098e365fb3ae88d5b7d13f0", + "0x804fe307b2d477085f8d9800c8a11c2dbf6f662d684d6a0d2fd415cbe4a09255e47535a08796a805188e1bad779ce121", + "0x93d91029bce430ecc5f41a460c02cefd3fdcb8c3e761ba26a020e108e06520cbe2eb0c04139aad0c0fe58ed34d8b2215", + "0x830867ec984210b314e7f23dc5b10e6d9ca53789cc447e29ebca229f4c79c9120618a540a9d21e4ba2ed8a811d6c456b", + "0x8d7a89ae9d7318d6578c1fa75b3babfa7c9df7099eefc2a9983ffa96627f4e7fc99dfde21b92fef5e0034dfaee35e97b", + "0x8eb68f5875dac63cdbbeb5df2fad7c1426939ecb6e3b6a48f737bbac1179ed4cf5a1e6919529878169d6d8552fa5ad56", + "0x861e26c9a31d21839735cca8a384b981f7346b026cab7d60fa95a7ad7a4a370cfb409812ca285090c3f1c3a95e5194b0", + "0xa02ab98589d48b2240209f54b0be78edb56b614b1aa02095ab5a9cec6a04faf065eb7b81bfe45aead551b1f774c60161", + "0x88124374273a2425bd5932a6b446986756379c7eb93d3ba0c5d7cbc3477e6267d9c67e5e956cf6df841bb263d1a8e224", + "0x91a766128a4c718a45db571606867bfe6e1b1049f0ccf71a01138d5443014c9758000a8be4dae0caca56321e3f992e99", + "0x8dbfc433e2477b9d86f221e9c49fb8db67c85438fd54b670ce44b68b62d4c0a9cd56c37a2127fb2adef22c07643fdd3d", + "0x880cb650f01191db0dbfe63215d208f70f924380fa22baa0e5bcab60f61ece3c6d4cca0e4363291f6a10aca9649da69d", + "0x8532214650619e201bd330865a3228e9ffaf1f64ddd33d206be5616c691b1965814f8bc507fc8a695c8291c2f8713dae", + "0x90e81d5a9d8fc976a3bf6ee6d3022107d3a9441ff212305cbc7c35bc9163321cadb352632181ccdc1450f91f51872b00", + "0x94d656836edd68384df1fe61239d40a36a0fadd59abead673e4a2ae58de5e2a6bcc4b980dd9b517e7212726b8ac94ee7", + "0xafa70edfed2d81326f26f111982aafad55f510de95555a4d05d9916a600f3ca6c1e3f66d6b092c91c1fce6c407e022a8", + "0x95cfbd616c2a59acde8152578737d3ed329aa82a950dcbb9378bebc3ec8beef9be2759a937381ed5aec1a46d486d1afc", + "0xa0a1ae94bcd07ba44c30bf50cbe0ddca2fdb5db82ae73e53c2efe9446c2464fea8e5de31da4afb99c6405798f0f9a59c", + "0x848e10f6c12a6adcf711ae3af8382178c46b90b9ff9970350f14b1a1b565a7efd91eb96871277b86040d26561acee099", + "0x815e749e4a56c3b982b50ef5ed249c4defee558647a5c6062022c3ef42b5ebb219ba770f0de74869bea14a98eec02360", + "0xa4d88794689a0f2e194988114ab96d28f77a29cfff606228ebe030a62eb4fba25cefd59d3d5f2fb66acaeda866f5c24c", + "0xad59a8541eb9641c3045d5cea6e3930b35886da4c96906f701ed3ef90cf74431df3c444174d9071a1657efc8cebdc739", + "0x97ae83289d535707039e9df8ebc73262f881ee8e288f73b9f0d6fd209385d3e2b761fb87ca852e10cc4818384ee155de", + "0xb47983e11702462a23e26c8d6407b01b67ad532bce3f1e0626fe3164886603bbc803c688729a64a69d119b15235389bd", + "0xb447011409a07a2d9074e08502e882098799f3b649e947de44c79ecf86a63045a19985857ec500638a3baa2b228a79c7", + "0x870f506356aa4f8df7d61449a7c7a8689705388b8b81dfe08fd79e8a734c998a7ba71f1f6e9df085b8aa5813a4ec4adc", + "0xa07abf6abcacd7612338b455c1461ff484dccda7430d4e9c5f9b4e5c1cb65055f4be650e6d67179b2c62709cd52a9b07", + "0x988b73c2a71f3b1d6b4734d231c089ad6cb07f7ea6f4b8fcfdd34aa33f09feab6cda91232c06b47e90ae9930ea46beeb", + "0x886443bb8d7d6c7634f55da1c5695f1691750fbf9ad2d63621589f91a0205ed4adbd4b905c62effaab235e740a172040", + "0xb66caf1ac38a8a66c43767e8597ddb66fbefd888989ca1ed56abb96ab9fb41937927a792ce422577c68286e53bb4856b", + "0xa84be3b37007cc932429ba2b4064ab7fabbd0b77400bbeaff09f8c6b818b5cd127ff8497e131dd8bf4323e092c690219", + "0xa99e9898b6f9b7b1b9ef6f28f60fe2ea71e961b64b262cceae41003f6aaa16fa3dc1c2ab63bf63534718ad812e882a35", + "0xa1cea8f3f5605a5c60144fed53943d3f259e3e33545eb0dfeb211a9dad8d99cb3cd3b2cf5031b85778ef6520700eac4f", + "0x8b979026924097a06b3827ad28f3efd7f0e5aaf7920ebe5347fabc61b000631f0ee973b61b7468fcc60ba6e4381ee478", + "0xb5dd7393dcff33d6d337328167ceaa7a04a98e0acf1dcbaf454247e85793fcc9a7d280ab14693cf2cee01afdf44506d4", + "0x8580c90d72c0c83c6c003dcc340553ea547eca5989780493c2551ea9f04225d77ea76acc1bde20fef1a0bb7ec01685c4", + "0x8c77db66f09e76ebf7ac14fe2fadabd41291f7ec5971060580b317f6af0daabe099f9db2c3d09c4c6edfa41211da0c4a", + "0xb6dec051200c25f150d3b9a7802f5b7c361b074528c79dccefa77d26ea2f67562a6d9fb8246369c6a60f832fec6b7636", + "0x8620173e19eac12fdc7796df12bd3648c66f78fb83a8e6f6c9077c34027a3acd0884ef2e3455a3de0fbfd4ca130ed545", + "0xb44e3ae4047f917fe1af378cacae2813f8774307c20d54c565b674de197fdf90e1a6da0733e948c3218353c613d23fbc", + "0xb330af874ac5d749a4ce1a23f4fbfa67f71e8fd16f6da07c714218be431b2a30cc4ad2594994a7a35f5aa06bf87ea3ff", + "0xa5be67aad05a965685aadfe03d66ea1136e6979cef00605e92912fe8f84be7351a6acf6b73c567a20ce6045a703cf557", + "0xa1672ed63df30aabe34e8eb81209ff31f4e5eee620b58074d92d9cf2687e40217169df59be8af7374aa5a9107c5f51c1", + "0xac01de17b74e2dacfe3db539910b6c370de94e646b6f2dd2a828a381b04f2979f8a62bac473659fe7b6e126f15ed7aed", + "0xb978099cd3aec49300ef9ce5561aa30da4d37cb5c697e0b5cbc3c42ccf2f96e53e948fc579cbd24605101176a353a962", + "0x8c8c439d9da3627e9f74da784bab8191552b945bb5bf9abb673659c939a60903e11f37300dddcbc8a495adf5c038234c", + "0x8b4570ac55ea349560a4e7043fa17f264dbaae15a2f3dbc5ef8a6579e1f9b5a440aeda94122982fe564f78b615de3e1f", + "0xa76bbb163db2ba26f5dcae8267d1a890815a76196af10444d3a04c1debeaa3c7cd51102fd0bff8944710c743f5393745", + "0x8d3ba2494b612f93b4ebab77e6f207b636e2d09a3e4a9666d4ddd5859fdbb9747a88eddb7749356b141a071584677ec5", + "0xa8bfd973dee352ae653f7c7bc7df2b32d790653a3f1f2b239d71677992938cabe941fa609e915e607809b5fa954c9073", + "0xaeb4c1ccee15753d4fbba545ec4ebb05c7428427f087fdc0852a18439b19b1669a3c744a0ae2e7f74c46905f520c3231", + "0x8fffac3ff9de863257a836aff3cdb705fe7f4bf604c2cbe10180d81c0956f723b69438bb8a3aa094fc755e386234dbf9", + "0xa583153b241d31223ebec9a95e11ebc4a657b14056b8ca052aebdd9866140dc4669bef4f02b5ffdf667ddc9a87e0bac4", + "0x93177005082ccf2143f24c063d20068fda393948bfac34af57ca58cfbcd0bf9a0de46f8f41312e83a502b7ad69b8f2ce", + "0xa79b0967599894340ef2408b48f42e6ba4f406e5ccaff13b46414ee38e5329ffc145f6c34d8e8acc6aba41c23e57e7f8", + "0x809a356a76d54a05e5006f2cddf0decf73e5392b57ead32ab56bea9fe13c1ad090cd69a8e297fa6e017b39361906360f", + "0xb051226cb44ab1bf94a9cc0e4f246751d68f32ffd12f1d077d3318de642f3997fbfb0f2ae1dd103264542c2bd0293e57", + "0x8cac28256b1a82d0be373d884d00e9ff2e384d5afbeedda706f942b1d222694f126ad44f9453fc8a985cf69fe11ad70d", + "0xa13b073290de7a2f01a65e429e1adb78cd37eb23c24d6fd5a1632cce2275496179e3c22e0b7f59fb51d526402c0f3f7a", + "0x92dab68d1dbf07e5b058120422ae610806809ddecd2aeb9d11d8fcac738c72eca584b88ff52c95817b79b9e0369e3ba6", + "0xb24267fbee28883cc8649c243b13905874e5d97a285b9c6abec749a53e106db0a6fd6fd8671d5b7c9a1851da75a4ac5a", + "0x99cdf977dbfc10084b698c81cffb431a9eabb55b1323e1b15baed5984a1ed212ec5f6c58372f965fe18de0100292e26c", + "0xb021c697c56989bc8c06636cd623c3672e8885598fd2014f5e560fa2f721f9487cfdbcf4adfa34c178ac84771fbb77a1", + "0x8fd7e3ad3330d4eb1a0bd42801d95ce40a82b43c366abc823e25311aa1ed882446d60b6309e1a1e201e725326736257a", + "0xb1b3c641ef4cbd5e9c69955217f53373cbd104916e04d012eb40a24d798e76bf05ed0a218862ce02619ef694c42be170", + "0xa376d0296c0105789e9fe539a5d22bf62ee36a2de4c9aa0f5e57210ae49e2cfc5209fe0f467ed19dc95b9746595255e0", + "0x8a0ec125a145e373929ae33efb978bdaf16041eba684ada612c244bc3e28c7027886e6308373a5ea53c9c3d8e868ce1b", + "0x93fde45cbf04cc386507b03eeb93c885da12bfe519df9fbdac5ada735934ea6e1a6cce066d033be66163b078e96e2100", + "0x80c1839ee1d2ddcae1fed77d5f8091ae3074409461e04153db801e05b44a7658c6ccadd33ad682e51e211dd9e3c80f72", + "0x87112961553b4a243875ac8d46bb6e274325699ccbdc40d7d9b7f7e58d3fd164f86b0b1df5df5f980785cb3918dc9b33", + "0xa011463964a319c1ea11c7c55c607bffe0116fc834b8a1d3684df33f77f6e51dbe16a891307c9f51d5b4d205c4530072", + "0xb316c4be33abd10400a4925f9d20ba02ab1feb50af39b6f6120d6dbcf1bde0a8dff7e08c64bd1f5c43543b013e242483", + "0x9555b696d428c4b74806a7d08b9ff17c8512a86cbb13040360ce248de241facc42c042d3779c28fe98dc3ca96a47b2fa", + "0x819f54bcfc58a7b793d185d8ffe411bde6207b77cf22b0d5e1b3d9843e4638009c907fdec1966b485f95870da57f131a", + "0x82c3f9623bfb8a8ff3573197497c175fcb314addafadd025528f805b7a63c87b0e54b48d46c0322110b0043f7f77153c", + "0xabc023b35318fd97ec81933ce55799d8c36c3d55cf59b9efb302b276a76a37c517d5c690287f216ffc5d1fc082e116c3", + "0xa6579226d602a7ceec06d402d38f217b836c8804e9da202bfaf1f3f4f15c24762ad6a2414ac022d8de68fb76ba8a725f", + "0xb701d6d60387d4e2308a77cebd210e868eaec10d86532ea18c0c6393475b3976a3eddd79e469458bae4f496da7398fcc", + "0xab202a2acd4ff874cfc147ad1b02d4515ace254f8b828f619df2537698f4e1b2687e004c70a152d605a73ab1ae40fb3c", + "0xa7e09ef6c86ec7475eb3ed69e57e0cbe86114ca5c0748069d00b6e3a1e2ed74e4366adfcb4d938023560fd91d0f64612", + "0xa9fc42b05ceaff4312d5dacd78fd2394dfb8dc87d52efb0529595877727747827c1c7e3a3da81255356033fce1f97513", + "0xb0150a1dadde09cd60ec3686256b4378f47dc6a55c092c60a3a3f0bbf586013dc37ed53ba7a91c72791c0d52e4c49c2e", + "0xac88e91b48f031df297c29fbb2cd0d2bcc767be5e0a7db87acc87fcc0f4300cce6deffc0b1cb6fc7e51c6ab13ec2ea24", + "0xa8fb1542a956fdb1dcf90da2672d40c90a4aaa2f1232318b4112816bab660657eb97e3d0fee9f327793f6ba9bf8df2cd", + "0xb78191d1ec4615b03b21d7730d48fd9643c78c31feea19866429073f4cbb0d1a67f7d7ed210ab62b760c679515b20acb", + "0x967c20d53d46011f59ae675a26aaadbb7512d9f7fe87b7a20c3a84c76569d23920121063235e37cee2692bca3af69039", + "0x9766abf0251cefbcfbf85ab1322f62267c22e6556b7fb909413a7819f635e3ac1670da6f5f72d3bb4b739e12eae5ccc6", + "0xb0e9c5c327fba5347474366eed1ff60b986a41aabab00abe18a91dec69aa54197d3f5680603057f05d5efa0a48dbc92b", + "0xae2f5defdbd14e2c7eaf595b017b4a97edf521f561ca649b6bc2e66382478b5323aaf84f0b90f0147e20ad078d185248", + "0xb841bb6e04d2409a419dff4bf97dd3d4f06f6fa4e5e23e4c85f23533b7f25fe3da9285ba033c6eae7e5e447e35329c0c", + "0x85e26db850536cb6d3de259f662a88d23577fd56d1f99b3113ef1bb166456324d3f141a7ff63dbccc639cff68e7ae5a5", + "0x8cc36d89424da80bcc2b9d9969bbd75bab038c0cf836f561080e14bb691e8e0c17306fd6d42522030d4640a01d5c0704", + "0x817e72d50f68dfbdfc9d5611eef7c6b490ef1509559801fe1ff916050429a5f79c8d03c60d2bcb02e72310b3c4c9d068", + "0xa15ed72881c49b545413102975fc69649fd5417f5b7ea9091f8209974024785496fa0682352c879953cd1e9edb3fbee7", + "0xadafd20b962921334f4be2188f9ced4a5914389d0afcdbb485096d3848db85152e2881aed0fdfca11f9c8a9858a745eb", + "0x8d8aaea706815f1ec45d9ee470698ff199c40b1ff2d75bb54afd4a29250b094335538dd41637eb862e822c4cf0e2bebf", + "0xb8480d2a79cb6ada254435dd19d793598adda44f44a386ccb1a90d32cd13fe129a8d66d8babd67044de375ee59d8db51", + "0x97c17d6594ccefd8f17944fb760fd32cc41a9b046f87893bb7ab2260090de291e8260ffc63e774a4b6b1dfe0e5107ef8", + "0xb5b7e1d4d9683de7193120be850395762ac9a5669cded9226f5ca2a3de13eb13b2900af083645ec35345894de349433f", + "0x9405d473872cc9f9b9c57bb9976d3ec6892ea429cbd1b12f22962b74d88448d4ccdfcc6d5c6ffa068d560d7bdc3208a1", + "0xb99cca139a3733b365f4718beb4ff4a5fd6aada0173471156640d8be2cc69f2a70d959b57688f927bca2329c3b30477a", + "0x94872ec872f19279fd26abfb132b4a7fd8c485fbdf04515c7b416fc564e61a7b0fc5da9f1a380d2b3db989f1832ac1b4", + "0x92aba716538bd66e35a7bb877cd364c1b8dc3055a9cba2da23c7d9c0a010209ba8afab455da99747fb4bcc8fd3144cd8", + "0x95ec4c205be3dd5df181220c96bba3f4e3b526fe5369035edfcf706c1eca43f29a4c95cfcf94cecfc974e01547e55125", + "0xb48c719d7cbda1e79b3f7ee9c17c13bbac197bb015b344f79bc6785b28a1e9484e2349178178a2fe94c46132c54983c3", + "0x908c495c355a0555544ec6f0b8e0dd0926ef2c5c640fcb89049e6178697625b85492722d42bb5c966aee2cee9981607e", + "0x98ded9cdfa92bc8e3664ae90af87f88759503715f6eaccfc983e3ab53f7b3af6802999da93caa7eb3530402ec4d8901e", + "0x993266bb366ba46d532973391af7204aab46a3189c83ce7cfd2713bc11e99066b1a5a012bead2fedb09274e7b362e8be", + "0x88d462a3a17f84326b1e4177799d6e9c5f4ef57152cb83ffff4353a8382ac8be7d50381723aeca77d33d8f07fccf69f7", + "0x80438d9eadea15c90008ccf4758d4e3fd5a7bd02809eed5b683f2c96a15d24524ffe75683b7167d42a47161c65d533a2", + "0xb9e7dbbd3d3d0d86e347831cf99657fb269930087920637ac6cdf185d5eded3f09cf3eb27759ce3f4b46f41411e2fdce", + "0x8f0215f23b4945470f74b99607c12c36eca41aaaf99747f522d8531244b668d6ab8c1096b9b5697208c3931e1fefaed4", + "0xb2c8d8515ff16beae04c855b4365e450e0ebfb423acf5da2501fea76259f862bf29738a858a4093b98c2a444396249f6", + "0xb27364a7258c30a59d1f13d358eb49dcef298a92bfa699b3b91817d2f324be8fff91c0b71cabf26747802a92582e7dea", + "0xaee7d6f71fd674cdd8dd1f22195981e7160990c16647c871835d988e881a3d4c52345e74f7a54768fd97a65fdbd84567", + "0x91356cb2024f7703ccd662f50baee33409c28ff13bb5eb92fa93f303913e9bf31bf83b0babff4b5e3649003ae95492e6", + "0xb744e4754043d3ed85c3bf6ccda60e665568dd087548ac70670b90328509d0d5013cbdd07bf603949067e54d8094fc2a", + "0x8146cbea5899401a80676850d0b43b02d376b4b8f02ed63a7d92532d13689e2c02846df79cffa0f33ff81c3bf492339a", + "0x94bba8a1508c6296d3dd5d2e609d6d732ab2541849deea5436a4a9034e1e6f1c8d26f6b781fa34dcdae7cbf8899d006b", + "0x80260b321d932e1179667de4916428c1b77ee1ea537a569dc64a12da5ddc85d09896939718ce08ea7e0fe8f8b115c408", + "0x89d4640cbbca5d105dd67250f3bbfaa96d7ce19a89f8d6e188353f3a9b8737f2db1707c506f8ffe1d3144dd1da371920", + "0x92f5962946ef7190fbb7bd3935427157ffc815a52ef44397ead3aaddddc82e5f85b1edcca1e9082a500960e19b492614", + "0x8b89240c9b7257cbbfcd6e415fd035ce33bb46c773569d217c82ecee5dc2d66eedc9333e0b043616b0cbf21744909b60", + "0xa3d23484916d2c0ad1b81fc7df70c97d711040799cab076223e0ee02a45a0fe9ab564faf7a225982468f3e62e32424d0", + "0xb31751386bcd471b5858d001fee15d566215e34d2d62556c51ddc60a834d3f1acf18c415c23a36b581cdf4791f461ce1", + "0x860a99003b841221dc5ea2bd7e226e5aad72db8a5959d5d4dae8a86114d30b9e8915b2314ef867e9c2a477d9424a2d94", + "0xac925b330cafddc7d95d115a9e62b2c135acd22b5e35a4aa789f4318f03aabef818805845f2532e9504bb19f69171809", + "0x95d8180cae0815d33bf8854f4590be652f95f72fc29f0c519ca9bf3f490ba4a724b23d9054e08e3d31bd61d609a8f0dc", + "0x994f223740ff95764fb88de1ad6dd90c9c58c0dfbf8482e1dd9bafc20c099a6772acf40569c54143f6697fab72772296", + "0x971d93cb1e7aec5defa52815bf202b11de6a2ac9c5d4c0eb236cf2c4941460731e12b718f4a5b980ec6f4c54c3d17deb", + "0xa341095fe5adb96dec2be367f09804ef4fe77d553102ddf7d643b7277992708e84be9a7748a97a56f065002a97dd7cbe", + "0x843709280fba29d446779b1ac6e31bc3ec8ab8082e8b063ef8d2f3733ee77a4191b55772302964bf867fe1044dbfad65", + "0xb7ccc71fd0d0c9642c32d292ae88ca369d1fb5cabb97b1745c621aee41da8f94bb580c1ab23664c1baee65e97b21f0b0", + "0xa9b41f31be84f8ba061570633bd9e5f4d8af6fcc5276c25d9ab67b2b88c1f8c2a87eb19280cd4fe7b4c04da8b2d02d7e", + "0x93eb14ce0632cd325429e1c23340da9655d3d7c2b42a4594bfd5a4e07815afc9eb1ac737228771492020f6528c0b7c61", + "0x959aedea532471b9610150657b895c5f51ca950aaca910df137dbda2d17184173cf2638a2a0efea3f61d82b6ef8a7c3e", + "0x8ebfb50bd48fbf9a6f782454ea900acf0c7143164de9b5b46c1cd072c69b76143ac4c99bd43139b5e55f847841fa6a1c", + "0x851499b3a1eae6da530a47d3e8bc068e6e7144b744d5eca5394f96253df65094e5f3c34abfaf7c7d78c4d5a5d4863da4", + "0xa8d68bf15b900cc47197739856557b43a5eb233b6c095f21a14a90ac8c36caaa1a54690c95840f0a4d2e2ffad0874a2d", + "0x81a6ff8fb1dc4d4042089d4cfc10cf826e39083aa5983e53f4866f8f4c10cf06cd8608c4cb1b785f8d309bdb9b2dda63", + "0x82f658bd1a95fac0b65d337efc95d856aa65541d49aa993b094d70e527e7d478970eeb3daa2904a1309d755e1d677691", + "0xb46ba4f3d8f287eb92390e5d930df4f1a40abe500c9aebf62e2eeeb2e5ecfe5296b09fa22d6c9cfdae28d431fd10a00a", + "0xb5b38508befa4623166f6213cfd160782fae5b7c3c7ec279b42a83d43a7adcfaa6c5b34cedbf98bba357fa663eec896c", + "0x89b8a0fb37a0c45eb1f234ae9c7be65c8a708f08d431728572169b33f8288b1e17b7d4b18de9fb76afc37ae609290623", + "0xa7d1f5779c043900f3ddf29b6b7ae4301699c0ee9e70314fcd3bb2643f912fb1225a0164f45c47419ab762420bf8e5ad", + "0x89d2a69fc014068aa6d0b79784b8953f3519f563b5c9f774f4b148334d822aa645b662d5efe7dc6f9cccc2f67268c3fa", + "0xa698d3f0b1b6b72b72358d5fd5e49e928cfde69bfda10e163b9b43bb9604362b32af1909d28da5e0364abcf5e96cc226", + "0x91c12dc25c48aee56484172de8c6aba0d9f5eae8db848a7b53d76001c292d115ec57d816c2cf10bb9e901b2707dcb71d", + "0xb0740219e084d56db4829daa30b2812115b2e95ae85ee96a140b7c4012860e8017e19b482e981547e50e25bd4ba76716", + "0x8c84d4fa255e2de7cd23b0bbd110687edc47ed7fa87bd42658fbaf3831c6d68cde3ef403ed6c585f8654d0cd32074bad", + "0xa530d3272aa1740a73e15cb9b31c5e2e54c404db72274b0840c19b164642389acdab4514b9b2bf9688ce51392d8b6793", + "0xa601f52bf7b3226fcab93c67dccd95c1d6673270671c4a099b867bd5578d5711fe9acc9b935b867ca780ba4a394279ef", + "0x8a238082dc8ae33314fe1257e7bec69e905c194ded6f894267bce97443166fb443628490755e1d453545f389b5beaa2f", + "0x88a9737f3e9ded874681fb6cc8abe0f6e1ce18a05ab599b2f855f73e6fe5bf804de5c5dddeb11057aeca6613bba72c8c", + "0x8a5cf70293eb99ad3c34992c47299646c8702d1035b75e4784cbec67b28cd4c88eb8c721f4cb8982d3c6a42d1b9f7fae", + "0x8a62228b84fa7463a6a8392a7af767b661382175633c5e00b36979d816a53b388f31afedfc47a5d8cbcb645e8d5928b7", + "0x92836b5a41900a1c1ceec83cf4f15c6177dc20f95eed23a203810116ede2a072a8d6c96532ef32c93ee21acfb14448b9", + "0xb4e538d7bf40c263dd1ede65c81883dd31f9237a0fc8d134a2b480a1a681dd89cd2edb19e63070ee69e96cd12069ce3f", + "0x913eceddd4c9939cf82c7e9ca5ac300cd79dc5a72b8458cd69e9f8929168eb19e5f21eac12a3b09eb8d3998e28e3801f", + "0x81f4a3e7195661b174aa2059796dd88d3206bedeb7d7cfbb7e61aee335a01ac50bb8edeb258a68949492d4ac6215d95f", + "0x913a393eba8eb88d1076effa8d2a30258d83635ccb346f1bfe099fb5fcc69d0457ce5a79363a618f9e8b43f53728433b", + "0xb11d721b08be428254665bd64a8864d78c5112e252feccca113631b2818fb729129fcff1e739178507ece41b807ffafd", + "0x92603fb7d50d11b59fe376720aa57412b866fcd5da90195a5a401e6222201b30c29f8797dcc1b41ee2cbc6349bd5ee1d", + "0xa466c5d41cd4a8d1f47a650ca67b529ad3873ba3fd3a36db27f7a5869b74b42381788bb1a1c100ed184118839b9879e5", + "0x85c50607a86d4f76826220286784fa9b6ccbaadccb661fb3489fd35a3a8917d6999ac891190f2297afac3c37abba2967", + "0x966320c2762b266cf7eac7aae39221599df4fd608036f6101cb8c68192fcbfd5f61c7f93172aa2be0934486fdf4816f6", + "0xab69525f1c77b6706592cdd5b98f840184b49efc6fc2687d6dad3b014f6a12c4d5cbcb5120d8869246da010823534d8b", + "0xaa2c9df15c06b58d7b9bdf617df8bcda83ccaaf6ddeb8074db931f7f03dc06a7914e322777e297226ee51dc8268e80af", + "0x97035b62f8db4df6e787cc2c940f2298c7d26c2127c7a76e4660d132a14f43c8bac8dd4e261605611b2e9c08929f2bac", + "0x8ace33e696953806f594427f137e84ea6b22ca9b48c3bdf3830b3e951e5a463d4a7067c68d2033eff452295a741fa1cb", + "0xb621fe49b12580bc8ec68fa39d5133875da23524a5ebc793c35040fa3436350d0f3e4bb4e53eaa23d312a7847e2eb2d6", + "0xab7d6ccc0de9c7ddea145e83fb423a535cf26d470af3326d1d6a9a579592b32ededb078bae4613028557877a9fe71642", + "0x97528eef76389dd77d33ee7daebbb290814911beb725ef1326c3160b9003c3043be09bf3f84e4818bc2a7960ce1acef5", + "0xa408eaf5c675b50dc1c655f83b9595dabed68e74e6d2eca5a4948252667413cfffb46358400df5d35f92657811ae56e2", + "0xb36537726b26b474e184dce0ad868a66f0944b4105ff6d35a2cae0f3a520fd14a66630388aeba178d400b5fe104e521b", + "0xb2b88518d10bdcb111c82a261076367e34718f1d0a1a06b421891b4eca1e3c1f904b66e65dc914ff1ea5991f6a638a02", + "0xaa3172531879a5c8f594ce96277b2c8c8d4a2d0f4bbe567ae40d5b36fa6108e00f0b1dc94b81f36c9eb6d1e9ee1896ca", + "0xa53975587f10667a9474ae2756faefe43e7f81bf9e051049de175a8ec085530fdee3d5e3db15d4be874ecacf49f31691", + "0xa1abdc58bff4fad0f6562338daeacdac8e37f9f3212aa252b17389bd9c54db58706129a63bd0695d299d043b5ef0e2d3", + "0xb8588fa1090597fe0f6275e5779da11a4d128c52fb8954e475c4940f1a3e10fc23ce1f61e9aabe8a75e82824f718a94c", + "0x8a1981c536747d4cc06315c794f1536db7ab3c9dfa024a0df854b948d93bee72083b6c9c4c4a7ce999c98b904813a659", + "0x95b2b1ed525d629eed454bd6bd059b01869423c3463a56689a7c39cffbd3453c962426a1126ed631b25ae8cd7538302c", + "0x8032c60f083477693f533c2d8ae391d62ea754b8eb41ce9cd59bc469b980dd959a8ac840ccac54b404a9d08a6f9e4b98", + "0xa72ccc14eeed758d3d43c51d68341fd7e98880c3687e122238d77dac8d987c8edb3067bb63baf13a0e57fe02334545c7", + "0xaac3eb536a5061a8ec788ce131582dea691957ce8b9c6af5ab7224bdf0fd15c77bc6bc63ad037bd83e0ae52fda738361", + "0x97dfa193800e57e6b19d1b7fbab40da6dd1463f043eeec34b316ba6bee21b6bb633ec0c4fe107c9dab6e06e07e0acdce", + "0x966ee3cf2f54777968fbc34f08c8de121ae7c1d6b2cdf1f1f9c675828d22ccb909bfdffa2e3f2ce51b0cc85bb29f8504", + "0xa9df6dfd12f8c43c28b929280355cb23ab0ddd2cc2e4fe76603a2e5dc2ef5d1aca2edf89b304a27345cbb1f24a86cad6", + "0xabbceef80c744e5a1194313f7b84b5dee1c9861cd4bd3d0d12c433e5f2e8c6ef6f10b860abf3b788aa04896f708426bf", + "0xb1dffdd81711e9782c992c4b14583ad9d6c39ef88974682a72e717e21923da6892490d7efd121423fdc638467e62e064", + "0x817f30dd799c422da33e13ac2bada8cce3930233ddad495f714a1c789b7aa8f41ff6e688bbffc5f2e8dfc72e5243b645", + "0x96760a79e4414ff1d19fee65b6e65b2dd6665323981ce8b4ee93d0a9c410b018ac086c08fcbc7a71720e1e3a676f2b3f", + "0x95445cabb75909262975a5b06381af2bff5c4c6cf51cc84adbc0b7f2a985117f35c014e33672cd5216a9737d3f37e067", + "0xa279c905fd9d49482d213f5eb98256d020c2b90bebac45004d6f152ee4ddcfc72a7e6b188ce3b6c93ebb9ba9b8be587f", + "0x8591e8379a78af8860e6a0e002be5b25aa4b93c5e556f5ae2e40400f828dfa19d93a4910823e230a51e2c1ea5464d437", + "0xa6fde17d41fd9f03605ab6ddfc992e36535a23b2c39608d30cd6d72588f1ec6afb9db193e88eb609e104e73ddde779a7", + "0x93e2cb6352a5eec063151e5c9a822f6fd475a072dfde2464af4afaf6a730a6af1fd74c424c09727328a7f23505b91407", + "0xa7b1e4f703386fdd16f5fc9b59ef1dd682bfe5a23bd42b3c4b1385bff894e758ab09674dd6d0ded5b32a0a0526aa6d98", + "0xaa7f01c450e619c4bb42d6cb1a90a94dfe132a641728a642997b71e2c3b02d09a7695b544712b2e14416e7de0252fb11", + "0xae840b870a938668d0d4404b76f5d4d252d8ae1e3619661df0890ccbab900e3d8dbd5dc9f96013413e3f1e30dc541db3", + "0xab7552930ab07b0f5d50edea3a2e5ea3ac1a05cc985246ca066fc3350bc58949dfb99d4f6a6408d1bba64d3de47a3c2b", + "0x8053634d4c730b5e90d68c2830a73e93f1c9e522ae0e00a04e2ba15a1b7b4fffb8b25516ceea61719f886c7763d46219", + "0x880c39ca4cafa622bc767d3127d62143434d0a1d7de8dce1a2f94cdcaa023a7096641a46e6b97e1b1ce9c233c873a519", + "0xab9d46e46cb2f382ee7d21b6da01578b786b7998e0fc2b5d5a4e1a0d93aaab997b5c481c2d9a741865d6460ceef57a5b", + "0x857a5957adc3a888cf93f144aa809c70a211932742a258978af5e657f4f57fcb6d9e39dbe8d3128fac6c592dd5bc4ddb", + "0x8c98656861fb8c8a03d491db45077f1d136a759177842ecf6c1ca36923088237e928558433d5b631590f395db56f96c1", + "0xabddacadd7d536e91d36609fd0047f5db113be0f4d84abc7631ffc5c00df919c085c049c013a05131150b0043d51f892", + "0xa8b14af12cfdd0e11c8487334efbfdd22c8b4fe6bf350333d42ac8c704efe54f50a4bb51d9d802e5185ce72e4b21aa58", + "0xa8badc2bb3cad0143db1bb3cc81751f9974ff3f0e2ee35921d04985409def84ac2803a657571699eba34767b773666e5", + "0xa6739a05d270efdab18462e8536f43dad577158e1c1655fa130da97e469adce9bb7cda6f9ac26f4a9ba3f9b22329b066", + "0x842ed6efb4395603e7fef0bf92326c0c63992da4ce7912f850c4960f7a19e0b2ecc720d9510f15ba6f73a2c5ada8ea71", + "0x8502ede859944047898d533e1923ef90e1b5c17d985c9fb4c6aa39d50636de4c5a4df278f2f62cfd3ad08bba4c5ca6cb", + "0x8c738573226dd5617b3ca1dec8780000a77f3fa8de241cac99b0d9b1b6c90cbb8aa2009668005f2c5c7abb09c0ab3f99", + "0xb101335c403d769313bd05c755a9196769465f7068fd6f9e00937f3cc843d48f013f5931f999bb5c0082d4315134f5d5", + "0x925ace190259b321981fcf8bcf52c6852b206099f25c0f278439ef6edc4320d6f926cd6fccf1b4cd224bc52e5c681612", + "0x95f5855ad1bf14224e51f7d5e0d229683c0d38fa324b1abe9d595685d3497955e30289618c4775f6083bbf923ff3a37d", + "0xa3d3c7100962c8b60c40b830af834ddc48858e7eba5ebe2874ebf74e505c25cf52e661b49d7619f2f2a039e1df02f5c8", + "0xaf7e66c1d5dca63e6be117b210c616efd533e77199d67d8f694e4278841963e0a46e4e44f0416e69bce6a7156e1872ca", + "0xab796760166d1e1fceb20f9bf19b1b7cfcd327650cc7cc35c161ddbb3cd4846e9a971b541f303cf62fdc0124688fbd41", + "0xb920211c5b440b3567942dedf62a65ffbcad1e3516f58d14d8f8dbe86f45c4b9745fbce43f5219b7052b27a3a04df12b", + "0xab6d5d25b9fc46b0824df1628993e44535febd7c62185b6795550388185035ae12bab60fa34848f465fb4f4a8add3544", + "0xa6539b67dfd6f3976cb6b304f4d152532b3f08c02bb97730c03c746da0f7b38ba65536faa43953d46e5e7687a86c356e", + "0x95bb225586b1c82d894ababea7e5dfa8468bc0e10a2ef34e5f736fd1611114cddaf1a5c58bc9d664b667adef68b5c25c", + "0xa16eefa4e6161a8e7bac63cffb2dd5cefcae57144e63b3fded581abf7ce32016a654aaa582fc25bfa51c83f352e09372", + "0x8b742428f6af81261a47a0df061e480ef9176694d361ecb57967bea67e11cd44df686e38e35b7d4a6ee02ebd520aa1c0", + "0xa2a4f2307f646384a0238a711c2dcf7000b4747b8df1d46c5da962fdb106c5339790b48682e8ec2532b8d319ccafae5f", + "0x81910c1d72f6731d27d3a4059ccb0316faf51fa58e0fb3d1287b798ea8f9b00bbbde31fac03f93c7e9a1cdbc9502d5df", + "0xb846b933c2acd71e9f9845f1013cea14d35cd4b8f7a371b9be9bec9d4b3c37a2d0da315ba766c3a126f8e2893f10af4b", + "0x8ffad59284b41b75064c277ab01c5b4b3a4f3c4b355bf9128160b1a55ed6b0d91366f7804006b4e6991525d3435d5235", + "0x82ff36a72533fd5d6745d0c3a346fce4f62b6aca0b8eccd11399b482f91cdf6a5a4135c627043008cb137ef4ccd935d0", + "0xa11c27f6eefe54cf32fd86333d9ccb59477a655bb0c35dcd028eea58d4cc40ef9a26cf3432fad4e9d058a27b419b8f04", + "0x96642ce0eea3c2c0fd155a75bec3b5cd573d41e8081632c56528464cd69a1141be3180c457213128bcd37f5fae47f7f2", + "0x8349a9e390e05150bbab2351b77a3674f1af000b6eb6752927ef838b6f0a1200e6fd7201dad8565e3caf3802f204246c", + "0xb8ae7fea6275ea61935d3047d8156e8fbc4a95c9fefd1c36439b2111b9ebeb7ccc306e0f8c875fa772f7b433cff848aa", + "0xb366f056e23905bae10ef7ce1728b317b83f504d128f5bd34701ecb0d25ec08491969625e23d5a2fcf0048af610664df", + "0xa3d88d506ba46b73bf07729aafe9698e788fd688647a6b4145761275257d262cc450c7889b8a40d698455baca55e3da4", + "0x891ebaac7a7a408aee4ba61605f44f9ca5a6d5e046eebfd8f5108b6dc4479482806dd01686045b4c7760051f22bce468", + "0xa6ddb74e3e3725e6f2d9025532ee3f357ee35289e1cb38dcd5b2ea8ebc0bb697416fb3aa73e1eba632d593d40fdb030c", + "0xa7dc097f440ebd31ec1a005648468c702bb77073ac8cfa32b050e90a9e1cf388f138abdd18f07951c752f7e19f706af1", + "0xa200f25299f9a0542c196adc2e00289f453411066b88b125d3f0e6b17e98efe9da8096312a2f1841e01837da90a65440", + "0x97cd3a9d4185d77d4c7bd4ee80928def7b660d8b949b0face798c62a7cadce1000997af29504d28ccf9070fc3016dc56", + "0xb9ebaba1a15eecae6b1998ae6d08233d05610dc0933b16922076b2dc4418cbeb4e5cbe099bbded3139d8a47f2b2eae10", + "0x86f5fe8fb36b419fe6fece1c5c4b9d64468b4aa0154bb5dca466a243b6fb1227c3b8bdaf7ce5c2d4fd05c061979f87df", + "0x8050e011011e7918ebc25825d9863c91046fc3756703bdedf936dec2815cbd10c2403ce6f4a0b4f576cdfa1347efdb85", + "0xac22132a482d2950be9442167be214ed9d24519073bf5ef1c8e3e6f4a77065da198a851950330fe4d62b2a1272835015", + "0x819e2e8e3ac43b6ae4885899346f3b558bd7658ef7d380070588154694957596695a925a001a9fec7cf3655326c50c2c", + "0xb00f40c084d2eafa36811e0d822ffef874a0d4bebd4817690408a737624be05c920a08307cfa0c1195505c5e7a5fd878", + "0x8355768c09515a593c8fc8289baa3b6cf7fc10d302abc93f72090ad99a70a1ef1107eccf839be722132259500a565d68", + "0x8bf0615d2cd11b03546ab7a0c90c0c938776aca8a8b989a709c367f0f5eea7b0a7cdd78f96050cdd5d0a123d01b99c53", + "0x827c2cce458464fdc716a2198fc67b3cf2ed7802a1f53f7a2793b2314789998b13ea61343c723da8863cb63def6a285c", + "0xb609cfe6acfccd632759700bbb0a06fc7903a6c0c5875c2c3bd85c65bfae7b29b03e77092f29d565a6a89b85012396fc", + "0xb73ddbc330e872363bed36578b245b666d42923393a1341816769ce0af24b700c19ea0f579e4f9aff1c3ff369e63da8b", + "0x976d658085e5978807e13b150c7aa539b44ab8855a386bb58a52d9ec9b5e21ddaf89a18b043394d6cf47bd589d04b240", + "0xa213897312aa28cbb2c572e643d3aed003c84bc2ca571dc5fbea8a0b642313be94db0047e293078d975fbc6800751a87", + "0xb54f2914f6a7508b6686280d3cc955730458ff035978be29645fba161ed54ef3d4086f956e68d2a48c49afe904edff5a", + "0xaf99e470055062390904673e18d04427c16afeb7b9f13ad83bc2599e9a92314bd91d6f1f81b55419a4d668bd889ec8c5", + "0x946ff0cff4030b73a1342a9173fe697ab20cc5e43ea6158573f2def601e12a174da431f8170bd31ceed4be48c90b4f6b", + "0xabc51f8bb5f74cee819ee383cbab739026c453bb55336fdf423af2c2ac6712ba90006d62dd72d8cc1b2ff6cac900c8b6", + "0xb43623a56c5fd1bf28bc356fb4a875d72dd4cbb00c9c863646a3376937088f9932a4a0aa26afe2ad69840b06242ec76c", + "0xb0f371952f99eabf7ed368a142ee07d06bf2b7ec1ff852fd948b8c53eaa52300753fb9ff6765201e35873b5167583f3a", + "0xb3906488172c09e148c571ef0712f88bc9f1ecae0db95380f61901660fc1aa090d0740378d5b9b76883507bed100093c", + "0x945373b5e6ffce11d39a722df7b24eb929b14a967d211be3b969f48fe1ad3dd4280317d1ca772a69b033f3bf26c02c4f", + "0xb2ad3490389fe5bfdd5ac7eb5bd61facff8d57a8f9969f4938ea56f4a85eaa2c1179a2e5b4f87d9e6409925c75b61828", + "0xa4d61547e405319cbc20cad16a2bfd9e6d093a064522c332dd22134ab05e893bc84786b21b4c71a6265bbd06da2ef4b1", + "0x86749c26715d22b185e1b25dd34818e96aad319d7add22a98486ef9f9808b5e4b938c6320d391dc4e0fb5d57bd41778c", + "0xacc554d5b866693a453a9ec46d422c8b410458fe8397384b927a62bf5f2b1fb9706c8c21af9845050fea8a91786e3577", + "0x8eb7e763d297cd93a7a54dbe4654c39c0ebfd73fcc34d3f1338de0c347676f445d32f270664fcb7b33347bd377c9f867", + "0xa1b469e3f9dabd36b13149c83aa5b7b5987eb0ecc1ce6b68c72acb39ed503a11ab4451e658576a92df4aa51d1bc709f6", + "0xb1ef105cd0259486be8f265a73ea089d5b7fab7bd7547932134539963467fb917b2206aa72446e2fed5a8185b04d345d", + "0xb3e211c1a14925f6de451271728a3c1e555ebebecd4bae29bf666927868039d4ec99d9f9aa98d835da7845a5c863dfaf", + "0xa416632a50500f29b6bb471bf00b37558975ac91e5c5b5004b67e130be1acc954a8ebaee7efcaf6883187ee9173d1ccb", + "0x8c655a85f66b5f28ab8760c94b6cf01cdc36fedd19a09c261e432fa7eda7928c3c88355384e689f1d2715d419fd8d898", + "0xb1fa9f82c9866d4f296755bef5b7c39fadd09374f38ef9954aa57b1431a1ea4cc17a9750da844fa1f5848f0ab7ca295c", + "0xb45cdf1a9eaaf85c0b07bfe239da618ee649ce90b417d90b08eb518b1fd88c0d25cd29fa7a0d8058d6616627a3dda306", + "0xa2be1552d3c4142755e0371a9543032ee82ad669d7edd24c4e2941bde3b78c5c6df427228fc45812a55943b3663cdbda", + "0xa28feb053e86dd9e2f9ccbb7c38467e2425fd580ba0f63190036fb47d01eb198ba8590b5cf68d1c0f47638e9dbdaec74", + "0xae06b849e080efcdba86fa03a0c9dacb38a15ba911aaec624d15787c3e11ada6909b1e33a2e3de928a23818d833eade4", + "0xb4888445d86bcf4d1f6a9c2d253f277596795084c3d45a4591b307b7ae4ba177d6ce871c2cacdcf9457f9c132f244722", + "0x87a568aa2f5471214f63932b0d48e589898e82a1f4c1055a9e73120763430537c233e9a3cb6cc178df53768e4c58c993", + "0x81e0ec97cdf91ae66d065234492a1119198c396e2db204b7edf192c88eb4238e0a45bf7e245f3714bd864244cba0ebed", + "0xa954a3785588d4bb3cfd7cb27df45c82e6958051f916594d76cdb35bb07e4f88e2831a5cda35fe1f3c99f32a275f0668", + "0xa9c9f4d54339d414342e87b03679baf29c219d28b6548f01891cf94d0313a64d3384658d82373d6e838d886235ac446d", + "0x8ef46cb24432b419b4cc803e60b3ef5872db8ea614dc37643e4592fbb2891cdff61f6b2a10653d9e99e6c7359ca4c590", + "0xb23eeb458c05ffa5d58be21cd0699974694dc61a9a928fb1eb509954a3dfe7d8a71620a2d4046a448de0fb213be7e97d", + "0xad631be8e17285f6310fb72ba913c564fc66d14460c4e8c4b0c68c572a5c2a45b088ef60eaa9d317403bacf534d57a23", + "0xb7130f5607f236374f5e023fd43cc6dee38286ca47d504c9e75c6504957ac2bb9134fd59d8bb1010d545c56ad9c71c4b", + "0xb83cb511757d80781e26b5e9b3e0597c4cf9a976a3fb60c84efeab2b6793e46282612da45b1bb8b45af4b7f39877feb2", + "0xa0c5f8b0027ee11cd5f86515698f689ad514cfa890ac4ead5502b5ede9d7d7ad12285f5806c9c85ab58f89bd9f188938", + "0xaa8e8f9335c6e34bca3472b5f412ce93ab1ed5f9e51c3affdf986a5badd2ba3ca1ee69eae53ba8144927f082371b4cf3", + "0xb2a4f775a10cd9caa776123771f08e928ecdb22dcb91efc440c69e37c6b467acfa6112c2776d4a530bfd6df3b04fd50d", + "0xa0c553d5d2a9b0525f71a5a0a539d579d937275df9220a0c3c322d6c0ac7fbd2fc55335a1a283e687856e2b30398e4b6", + "0x8ab800ab4c810e8f6a9d42d2dae9be89841bc7328bab06b88bbe1256f720ca99c056fbe4e1378d7cf805586ae18dcc55", + "0xb9a8766f4f4bf796e2517a8a7a05bafaa6d3ec601a85c466d33b8a7e0498fa1dd4e2a9e42161fe2362c81d4c8ee1fbf3", + "0x8cb7d054162e9f41245b0914e7dcf6108ec11456b39b473ecf6c40e56b172fe5be4e7b0753a3685667436796a977b977", + "0x9131d0395897f5591ad56b62ef83a3ed9e7b3951080b33ea606a15742f78a283f924373e813b877f32762dd69884658e", + "0x8d784d7f0884cce988305d314896dc6dac2d2934cf5d650904e1397f9b9dca397eb7f3accad60ab5e34cb2e494bb640b", + "0x8819629608ca1535bfc156c1e17f8fce5821d81e6661bca75a1754a5919d0404e31e65bd509387383a4111535e949f5a", + "0x820a6f46e251a1e6d92784aee18fb0d265d7e2f0a5b7e0b15180273eabdefb34f1d575e1d8e93dfc2be1114d10abf31c", + "0x8d10d0e0557beb8db344c2d8bcada724e720823fc37ee7c51b322c3269559ae932bb2ea07e50d7ada88ede788839dc8f", + "0x911a333e2f7578a0ff6533284176cf235036047a11534acb649a0043a326723662bccddaf1970b7c37b5146977277b88", + "0xa4be2104cc5d6fce4a46de5de8d210559a6b743b6347b8d9990315bb56cbf80695ff936afadfdcc415d88b23ce6863ce", + "0x87ec5877ea8f1123371c49263dd9fedfbde41846a23e12073ef80f7afddf5a0ddab298cc02e861a90188ef1282139ecf", + "0xa3f1dae70745b8284b1353aa6902ebe3cf5580e24e02490d42b2f509ffec7e8e777fdce4f1a92d83bbb23cbaeaddac57", + "0x8ed5a0733b42482d88da7c24e85a841ece65f6066dec060bb267a8d1f1ec165ad5f7964c2908d3fbdc2999c580eb8990", + "0xb124a1db23f4875e0caff1c7f4b9a411564b93a9ec3ad8143bc7a70b8305d380b934c194de8201f7e3699e905a1f0115", + "0x8af58886d4ac5578a29c2e309a68f19a62edef5285d0757c42f0ec2550c633c0e991c4cd7a60df4523cdde40c3909345", + "0xa63fbdbde883f54667c6cacb356db1fb976bad147b790064ff25ae72be53bb6f4d74b22ca803996e0d95d216caa3fa81", + "0xb99fc9012ad938b36246a4471d29f0a2b37b2a3be6fbfae7ec9fdccbfd14d48fdbede0d88ef3b6cc273f2488f4cab55f", + "0xacb6cd4e1672eabf530d38f50ae651db8bc4025c2557c59ac4f1a278b4741f1e2cda978e5d1337f9e5aae77c95ccb872", + "0x8f8f6964534e4a9294c61c76206674d836d4d56970e9c14ad6835adc6b0d256402742d8a4879764569d9082ea6a750cb", + "0x969607ac6ca9bbef4fbc2fac22b12714a31f5d6103dfb998c3b6f2776283ebc7346e81a22da168af40752f28ff60d97b", + "0xb633f60cf6eb8ed588c545c04972ff156cee767edf31720c9715be3cda8c8de0290b623b22cb8fadb1690bf3665a7be6", + "0x8235bc2e818e4d259bf2c9fcc9646ccf83b7e238044e26be417d1d0dd5283d7b38c86e8c88a5447645be191516e2993c", + "0xb503052246ea840a4083bb4a8978029af3e242e831518bcca015f2c2df504e98a48c9002b6b9fbb97e861a0a3c5b4b5c", + "0xa145ac57d7c028c3cbd2a2bfea25caa35a9b5d69cb491b13eaadc2b0d927a590decb7c4995541f8f29089a2cbde6429a", + "0x80b4c0938058fa5d03c948777f13c70f46fc025d4d6c2f2051915b476eb0c0bef902374d784df57ac368c01e1fd51c00", + "0x92eb253e3b1770b36c4b2869a944caeed7b5c8a5b8356b25dcd4102df79fab8dd2c9d01e3253070f1206d149c43f64e2", + "0xb7979ad6187f7921e725787b0a99050f4c98762c63fa64a467f7f110932f6d07556453a95e3a2c0162bf1c9c41424c90", + "0x8808ae4c7cb38202c8c8bca0321e827580155197a700fa54b6a15b0f14b001327d4c9a0923168bb5afdd1b45d6a78367", + "0xb16a4ceee9de5f49a99430e18aefc192f3c1ffdc4b41392069f690893bccdca760e6dadf4127539a763e4f60aef37dde", + "0x8ac113da7ca59ca97d6bf7d6e03f1e9570867bed27230515475f965ce9ce0b424c85810e18a584ae5a3d5c2c80c6d4a0", + "0x847ae1b0ef5cb11be37320f3ab5e30f59d7910ba3d7cbf8265c74df25f4b8f56f1ac96cf49fd166c3b6985d1e8091e6f", + "0xaaa9b04f50ed6778e2481842cda30c7dbc7d462b40c7602a438ca9f2c1599e83fe6423f30d7789fd240d2e3166836f5d", + "0x8c18492569faa8cfa1c2a05a0edeea3f63d003e38d9ce23c4a5b31cde993a4ec88c9db83011ae15b578e0d0f6b72ddb8", + "0x838b400217af9241755032c21a3ac4610f77f3ad76abc43f0c59a59f9bd52f2251e46fcf1552b6ee0220f4f2902e54e5", + "0x8675f8de084c6c05644deeed1ff45090096c72c0db6bb2ceaf1c0d070bd10ff1e83b2dcd89b6f99bf132d3e131ef6d0f", + "0x89611bc63c83d56131bc2a8653278b234b4635aa7a05033d71a8377a5d188ffed7506a50a5c37a33d199a42b9e55fea4", + "0x90c290c17f1687a87023fadf74b1e10ad0c0414cf08629b2a313347f0f6913bbe511e5d18d1c3264b47f65dee7887d4a", + "0xa590bcb6391506035466dea82617f11dd9417c9f379d32b4c3bbf723840e1a3124d2327deb28849aacac278470d7ae20", + "0x97c55f459ebdf94ade7bc3bb18b329bbe2bccea345f0b4dc38cfff2839749b8f9365e8a1cf31722649c165e265344c35", + "0x8159d02fd03c1d0b3c928658b3df1a27a57699ed8a573e0c3a179e97f50b6c1a6467b7055e42f9f9c6c858459eed517f", + "0x84d4f009c052f3bf76b2b972b3d8f7a4b2d78605a566478670c33016aab06828a1737a36d3c9173583e7bed0aee84fcc", + "0xb99d7558944ac2d61f5a800c24ee47fca719e69f7284956be94596623cf434a214c042aa46d54019de3556540ea53236", + "0x8d1efbad46f69b80efc5776d8afe95dc0a8182d57318b9f2d6fb5b7d5c48e7181e6bd61a8446a553c58f7899ea7a7c78", + "0x84a9cf6a9d64cee7e7d8f0b678d3606c9080ab3ecf62fe0d6f994a681de68b30534ded61db1445a257b2c5427e97b36c", + "0xb6a5d2c55a23841a4263b10cdf784be6fdfe1b25350a4af510ca294949716711363ca19f9c44ab1c347aa3fcd60f0573", + "0xb1b5b6dbe6945db539fe7e2de07d222c88d7b91753118593ad9890c55c4c3d83b4194f886ea7f66ccbb348f5a23a2a22", + "0xa8a58169edd3e58f87fe8529f5cf7da7679807467ec707ab96faedf75085185a78f2ef912d9180a5e820adfad32ae4ae", + "0x874c1f416f866756ae3e93360342848afdea0048a575f977fb1f8a57325e50da122d3e9f423e308f0acb1b28fd47a6eb", + "0x95cbe8b47ec42a5c72ef7b1f91e3de0b1f648ae8069416c48d5529c9cffb104ba4dcbe87cc06e4e798a1b23bf1595f9a", + "0xa1b6e9c5d63ab1262559727872d1140b74a4f01c12366ed2d401c64007faf7917ec591b631c6bb4dd44b39aa43c7f965", + "0x89e6f4a05679c95d45b54e760056378a5eeacc72624eec8b5f19aecf8ef0d8acfb2d807d3b88c6b1206827203f219905", + "0xb7f7b30cdea6377d5f16d200b987e3b4a6f28387faa701dc579cf7b3c6887d74ca43609c5bc36414a6dfd0317ec75448", + "0x83474b58135f3e2c5e8355e31ae44a77721db71cb2919c3f3403f44903622d4116e812ea9ee9ca073938dee780f4aa22", + "0xa3e4cbbec770630c5e2f3b67059a55b1217435bb70ba5b5010244e241ad6a3e6b8d9261d8a0765c4b42bf795fa4e96d4", + "0x87d3ebf0fc03ad67299f3b9cf9c9ff0890b1d0d2d1a0ca2a62147444922d207663329e49898d79bd8e09ee48a1560fa5", + "0xa1d33282cb17c7a4c5cfeab4dee8875d324aca8d0513567c4e5eae180d1e8ac98b2ef16b31afa7c3f2ec25cf3e8bbd11", + "0xb10b6cfe3ba563b41ae0d66813105948416ce0848ba3b34b8e96547e8842086b632a52904e56eb61d93e0cbdd402d305", + "0x84c4feb35c8d3583ca17245e6f7e73cb488aed515c2ef671b09a04d8eebe6b7579e5b1fc8634fcd4c3bf8100d2cb98de", + "0x918d8fa2f52a9b3957ba412c24cc579dbd1f0b0834b909a6ac0da5dc602ceec17046f61b3d4a2658f724757ca8041fb9", + "0x87296e4775fb887bb00dd3265f202f31a8fdeae5c6ad8ec63508476cc57d330827d0d241c68091bb724a2ba921694a7a", + "0xa8908019d96c506b314c84b22c475157daa36016a9b94feecc4571e869918e4e5a9e39fb7c9ae0f73f9f868bdc50e2af", + "0xabedfabf75a93e7521eb339ce2e22e0e887f94ea28d3adfa42d1e0523686c6cbee4c96b2bbab3b8393feda1099b24d4b", + "0xa464d6bb17386cb431520cdbb3818beb3951b0255d72f58c300fd780aea1fe4dbce5532f5321e80e16db2f9b9bfe8a1b", + "0x8cb8fe0df930e1e19446ff0183c7034e35e33442da346df8a802160120a5f4d8abac236763114a650dcb1a1d38bafb37", + "0x975c47ea6412bfa97db9cf12c2b4c07ebbda436716aaa7253b2343138b36de6c897386833849f539bad7659d9319abce", + "0x8cf94457a5a708cc91bca9615e599b0c0afa92a7f2d9c83704e05a3dba56a90c4eedebb6d2d25b3080786e16c27194c6", + "0x950d02a5e41c8f704184c7c59715fdf3b48d86b53b04dff7c21738c7c38c9f4f75349ac1e70ca18a0744b01fb8b13504", + "0x9458faad893db4458b330ee283d6a90f68346332c99cbe8e121c890bfca908f0c91168072aa221c3c078d7fd5e4b44d9", + "0xb0262948c113fa2a122dc6208250b62ff35b12d3aa1e5735e95198424cf16a4829e9211c9edad83989c537572c5b41ad", + "0xabed7125de7dc52b0b42cd34fb350d4c6c45016319ab776b52289bc8c2b341a15d48165c0eb09511a1a5a5ed7ff39e4e", + "0xb4c352b4a127afb5b0833d210dc2c216bea666e7c5a940a3372988c0b02dfd236e4ac7c124664bcbf353132d6f061f3f", + "0xa334c5919909dadca50f3124de06400df660082b527f1f32b386b9216d021d38685f1839bafbaa7950eea6c1cb14bf53", + "0xa52f4534e9de29f91039af3fce055f2f6726fd9b10595a43ae41f7b466cc4ea6314487081e867ff4b5e35cd622fb428a", + "0xa68c6ba9673896bf49ed145935773fa50d95ec0103f97a6f1ed698d93b4dd78111325f797e47fe153fb3852f4590ee89", + "0xa5c456d516a557aaca80441705cda63d081181199097e83b22e9cf7b9947a8bb78cc476642f04a5ca3b13032319591eb", + "0x8a359a3dacc7b45da2b826dc27700178553f6a52e9705451f24c6d6026a0c597328acaa10b3b5a883b6353eee4eca594", + "0x807217b435d73c1374bca84d2d3e069db756176220a01607b81438a70f69232b82099c676fff361dd909271be8d5d555", + "0x965d0f46eb0804f19dd700d8721349287335c70e992efdfe89058ec424b87acccb3fbb18d84b727ff5ccb6f6783e9065", + "0xaeb5f2a0bff1e6115bc2fa73093019f8c679efec91d03398e24651be187265f7ca80369a1dfa61e8701385dc0ce9a0a8", + "0x85732f872228dd5d691f1507ba00cc94e054baa59a764565401e9e9b3287d2d0cd0f2af290b28b5e3c80da9cf23ded63", + "0x8e9a315c5b40e7cdb866b8a7e6ec01eeb27a52a76a88d5956ac3e66fd9ade3ec954acce816227b57fea6ae9244f1303c", + "0x80436457879607efd008f959cfd7507fbe22e417c701f59b5a36e878a04e51e87eb38c48c0992333656b24a4e671bfb3", + "0xa012f6d166cd1d98098544bcddfbdfa956ce60011694b640b012da3a0a22ac8a054a9e205aa9fae4df764ad60c65a6f2", + "0xb8225afd6e4d45520678e243d97bf48f87c2b8d2cbc24b43f94bf6e7f60b7768d4c3b30d28a490e7c8a1c3a104ac8317", + "0x8437fc2ab6d90716419f544a1d16c607173fae5bdc242d8224d7714c115cc54f2246d1062ecd77d5a9cd3ebed3a8adc9", + "0xb113c6c63125930882c18f548c1baa69a26f9f3dcfbedf5be41aecd61adb896ff9622ce038f0ed27a5ac602b6020740e", + "0xb893aee6291a3962fe17ea41322de7edbea6ebd51d2c564fe23ba8a4cf4b6270b7ac72c87f2cbca209be1ba607ecab75", + "0x92e6a7494114cb4dcf2b86ba61f57f6db7e4d52895ba6c896433139eb2ec9c9604f3e9100c690e1949e32f5b7e29de93", + "0x881a323e772a639553cbb401e2b6a255094412addcece2c99ec9e1346aea2f4e9eb247552435eab74799ee4c7a927b6b", + "0x8d5d3ec378922311374fcb998fe5a42176448b629a6475abe494fa56abd5faa5835af37624c138beeba649f7803a4855", + "0xb1a082ba449e93cc15fb4dc5114351437599fbd4d28eb6b4746d1bd242172518f94b2ca8b1f76c08d9f6ef260d9cfbb2", + "0x8fd2b7728a3c61cd8e0c607cf40e935dc45d52d040ef1259f62e3eeb30bd3a6cd030fcf407fa0b21423b23a795a02b90", + "0x9214aee5787f4666c3e2aff70949dd679d4203a2c3e7b6f88c548b80a3e52d7763f2bc2b7df714eef053f60eda4db331", + "0xb15df25b62c6f4ac9edc414ecacfe8eec055bb07a1220e327bf35c5e452da7620df03416a449197bfc8d948445c5f734", + "0xb41ff69731e7f4308fa18ad286d3ecd7be21afef3d32f5133a0bae877a347f8773c6e9d9b3b850d054236a6f186e6913", + "0x8d9d13d1b7d9df41cf5d30dd62b9d1d2c4933d62b6cf8d1830bd1ae4dd5fa3de36bfa1fc4d57681ae13996f85ad2551e", + "0x8011a7fd7534b248db40050edd9752c960ffd89b0300a91520759ad51da1698454affb4aa8907946605a02ca09a7f340", + "0x9159054fbc10164fa19f68736c2a683d374681e6e9d5e56f7496aeebb0969b8eb1a91e377b3a2928879147a7fb60b3e2", + "0xafd4980aa4661fe05bf9040f6551d980af562da69ec5072104d8ea34a8ebd28baa0b70e0fe3c11f631005693fb99213e", + "0xa92879cac7940c6d363ab3d0ba7f7f24bad0b16142c78969a737c27ebb09a62071540bec1822ae6224d943d02804da50", + "0x89338d27ba29343279dd83827ae17a53e7d634bc77bbd848f3b6a352fe92f6021dc1c81ea6693b3cbcb1f24188edc757", + "0xa2490a856c273b6eb5242672f817e60a157a1dfdf25b1d32e0f4836a9c2371fae72c93b94d78267b3cb142b4f4d7148b", + "0x8efcf5d06107554f896084e32e8dc95c49fc5da3f8c4be4ef6f2ed89914233eaacfea886040bfff14759ce28a1eeaf3b", + "0xa3516280b169a6832e997a4a45daf46aeaec1d8953387f493cacc2835a5791d4dcb24a0c0ad5de79988d76f843d79994", + "0x95eb7531a46bdc51acacf7fd9e7210bf6d5ca59b0efe58f79422394447adcca6f4ea991600e8558da8e19e029701c5d7", + "0xb1fcb4177f16187c76b421c29f715f1551ff365bdce9fe17b74425f76dd90fb4ebe828ffff3d20f75ac620abeb9381a8", + "0x886246027be4062258b232926cc82b6a51591138561ddd0173ec6e4b7ff750e15d9ba175f569c266148c653ac905d498", + "0x952c089dd09dbe531f2fd4137c971622fc1d85a78ff07de634f63853f62110dbae3646564addef8f2a070f5a16396ef4", + "0x812ed85f4559fb28732d17c8fd7c6b09a70da454a2318a0276949df0a5dd2714b14096656b7b5b6398f54c74eb9ca49a", + "0x9340db62e43e43144e1afb1da748e81a1b00f7b0600e8eed117e92ffcf801b9d89b494ffb003b4ebd5bb4e0eb96c9374", + "0x9287c0745b4bbe24b56784ac28bec43ed2abb6bb15bf11ba2b18b01801da7d162aef88e967d2f10fb9f52f6645d7702e", + "0x9615bc232ba6053fe86c6328eead899bd62c4f975273f72595407fe36ea43e30eeac7524bc17dbe78b4692d42ae81c04", + "0xa387899b521b1a89e860756bd0986b302f3c06271ece653425d6c697e0b330a3ed7789efe0e5a1b32e60257a12fa0147", + "0xb4c99909fbb92b1f39e9b2fabe05abf58af834b6c15ab0f62304ccfc5047f187a3ce35388ef293d2857b777f9938bd55", + "0x97dcb90d2dd9291366b557936931550d665cd05bb1b19a7a53a31c2a39d264789477a47ae14f6bdeb171e78941a9d9e2", + "0x81417b4a3e61ab9b48e0ff1afa8b523bf63ef95a6d6980092408b61f4293fb202395b10a5d12dcc54961370c134d5b0d", + "0x9135da893ef0a9d45a719207659cad4a0590218303d0e02016bcc5d14f54de5fb8de642efc7826b3b3212f714114600e", + "0xa00d0f8e2ea06b13f5a75a6dbd1f2ba7ce3f3bb3e62cd3b53f8b6ab39431fd2ce156a1aa4a1988613d4a2b6d91550147", + "0xa3f8f17dfdda07166a7e5503366dbef45ea6b6eaa1dbe02b8051dff58453f1ac24762c82f6db6de4370869f9b25d6d51", + "0x847c2b79076f9284d9a866a72f74f62fd73cccbe2df18c0fe34a35416d4825d364e24f95f728bc0e6a5215b08b6f0d2a", + "0x9816284cd6b8b35e1f5409d3a5899af5f4524a4826470fd164fcfe863994ee3aac77cbc16831f0866b9f0ae561903d61", + "0x8ab1f9feaa8ba2e1691acbfbd5460a4bab531344ce4accbabdbe5ba8cedb5d5fc0967def4365d755ecb62d83b7ffa4bc", + "0xb0cb477aee9bd113959ff7b7675f81ef251b76cccbb67cf68ba571fc08561736e32c18aae93fc8d1912e7eb2fc0ecca2", + "0x8cc41304caf0357d13a25ecf66336bece67d5d319bc5a50328a96199d7ca4fad05dbd7b5edda58be73141bb06e269c8e", + "0xa7b4d91a884abad5337925c34d7fd5f2aea5a09ff3c027cac98c646b5058f7fe2cbf47208930509e2a4eef1468f64c89", + "0x97d942e97efe46594e8fc86828ad3ed1c9133a8067f9b11bc0f4ee3815affbc0c7c46a91c40f989d50f1d8df96982ada", + "0x95a7d369f3ce7f7ad7ddf85bc994667ca25a0c2f11b9312d06654599410d5325ca3ea74f33f21b5aeedfb582a9e40c62", + "0xb0a05b564a754b46fc7aa4f5289f02bd9f19708b5ecb9db5c36bb7505c8b56ec22b53fedefc1df289c0f636c97e8ec47", + "0xab6e2801ea8bc600f9159d05a3b39e8b0973fb9c2696b3f2685424757a6953a9f8ddf5e29c97399c4821b8d7fd9f1bc4", + "0xa6fbbad2ad3ce8e4f9b939080e9e7049eba9f76b8ffb57f7cac2aa46793a064743239ce287e156d49cf4936517632290", + "0xa606632b62194aec737403ce5a9b6316178c1d27baffdac83981baab63e75d51caa414ea92465ef37d6d687b4fd90141", + "0xa5a99b7bf8f4c109af04c31af9b5f3148370319c8483796cbb5ef555ee1d4858b2c1acb82ab5e26180254399fd7a0625", + "0xab2b00f64355ad294436339636e7764403b821d4dd4fd74a6bbdc2aae450f14d7dbe8423336e793a393f4580f1b9e35b", + "0xa6c98a6ad7f36f16633fc216c12ca34e596b292524753ca1067eb75ab52facd28ed3a7c55e0a0cf1d3c9115a2a0d6524", + "0x84acda31e618eaf0424a37cb3c386585a3870b2c24020550a16134ad8802d427c918e2854c98e5def58a2363a8e1a314", + "0x9911ec15af39af1a18003ae120da8d909ad4bd43ff03078091d54de71de70e19786b2aaebaa5d55d9b2877004da2c271", + "0x8cb5a148f065e36b67a219bdb347a625a7a4be8f20dfb1cffbb38fd4d843c2b1b1886c1f015793bbcb02af04ed91b170", + "0x815d9adf22a36533fd4a3efae3c4326213ba2aad48724ef958cdd6f0dd5059b519e12d91ed5d92f1418a07b62b108bfe", + "0xae5c244f309467ada13e2fcd8942886f563bd996a5c65aee73a364c2ecab49be3ba6bc8a387f3baad44776f4f1042eb8", + "0xa47d93b35f57ad890239a6f2f69ef8760268adbe614d5877802db4b6cc75cc093baf101f75be0f7b4d71ad8724dbb9f7", + "0xa0d089701b965df9fea938e337016ab20e0e567e736e6652955f1a93760b4a9f128be5a594e71df8e7db47c3f88c2fa7", + "0xa9d9a7170a860e2860f785edbe18ad909ecfa489cd3a2abc580869c7eb8e9a2db93c1c473a5f1474ec0d51dfdedf95e1", + "0xb665abdd084abd292548c336e3e6fa1c5ed1a53d2e61a10ad6a4c66487d8a9e101632ff468b012506135907f0896156e", + "0xa10ccb363b26beb9622e1d91021d08a3bf02bec96a059ead01961ad51610992ef03558c5f77e074442836c9d2ff44e0a", + "0x96d6476066264eb3090ba3544dbfec7c8a0d90985a1697985db0d04773f6d37d5899a9d4fb5a3207c320ca78c37492e6", + "0xb4290ff9213e2ecd30d303b2b4ecc66c2614b8df246e70ece4e55bea9a1f5a0bae9df6dcbd8efdcf8c4b0f2f4cb44d48", + "0x8ef10b2e53e6770a36b6403678ffb86f5d85e3e87bb1b3ce9f1f0cb0cf32f1fe991c565595389ad83d8c8d54a47dcc82", + "0x91f950ef60014e3dd28f7661e6275ab6f085c803988b7d6dbb2cab25f10b0372e271267245761e1af97da6f48c230205", + "0x97c626e7114396daa337ada4f08da5129464d8e8c68a407c8798949817337578733fbcabf454a22b57926485c28d9d62", + "0xb596984b609a9858b1adefd15a546d4b8a417c8b54504efadffcc805caf8935b9c7f55d9e6b34592241195f513453572", + "0xa3fdd36f3eefffe0cd2a9e6cbfc4eb9c3a499eec25230df8786b23f5eb71efddde062940ac23d5b2885081da48d3c1c1", + "0xaa1822db9ee136d0a51910f0a59bf0d2af6819e4ec0b859b790e01bb08c1def87e9613b355525d4ab7d088b520a6a3dc", + "0xa9089edfa96fdb7204a68c4ffcb7e0a875106886a0c589dbc57a6709e7822747affb07035b99d056baf11d0852720489", + "0x85664ab9d32ab0cc2d2e61901b2682f88a7259c2da4ae6263b917ae8afc232614b4ee56539a868a24940eab74142198f", + "0xb90e06a1a117659b52b364359e2265daaa8981954e9a9c37e3256cbabf133dd4900974a895dde6ec6b394fb36b5bc1c8", + "0xb414aefaa4833283dce85add23d1cfd776567735f2ba9018cd791d652bab55bb0cc0cb38b88fe47e3b4b877e63edbd75", + "0xae579eae9c0b09c906cc2824eeebe5b4ea031547055c8ad635194f3e864c7a184dc21a3eca9c43c01d9a2f272cb2ce81", + "0xa7b1d13997c283c13f770d5203cb09b5d3ca7d45324ec89c069928e1ed1a17c57510e0ebaaf54a21d27b0f9f057bccec", + "0xb15d4555520565b76ec21d87e094ece2e04c7c4bbbf560264da37604f1a484ecc3ce8143b04759fe716411293876d0a6", + "0x810bb0773c06caae8cc06ffc92303d51eadca1e1b0acd57ed23f5feda70378e180619f68b8db98e61d792568f49a8316", + "0x87dee32807e2e5f2c884822b31098e5be2a4d950ae728e3281a39e661937c4b7e9fc025b50f437f01d69e5c33dd751a0", + "0xb46810bd73d077a6b73757d22b5939c02a3632e81287073b00ebee30cdd402e89c318e0b03d01fa331193842f3a1ae53", + "0x95a136a7bdca77f764d2c2d4795a8fc9e5b9097d73bb3956b7a45b42185a99c949db8ac5627ca263206cab9cbecbc31c", + "0x967eee3c3afc138a482bd120050dcb9b45a9fe258e5e4b678b1d67b4691f4c5d89cd260210fb50f9cf2d3e2e2802968b", + "0xb2d59a9ed0448b88f8eb26d8017a129ebaf27f11e0a031130266796e5f777bce93cf2c7e0fba8f8ccc997315db9aeb9a", + "0xaec708d3093b12caf29efbd8afe3ace1de24496cee72270223aeaefe4f0ba3a7acea7f2f5f85c1f274aaf5188616133f", + "0x8563ec52704c1c7ab515451a8f89f87201d30a12c95812ac95fde2af033e5019615a07f28b540a92781ed35786b5614b", + "0xb1c8f819a4ceb17d35ab997c14f81ae2af9d4510caffc61d4a19e9129e0bf7264482a10f329054908f99909999b6f538", + "0x8a65668637ba24358800076d8edc90979d6e614e6a683dff7859ce7d686014e6de85298f523ab060c9a9a4c4b8862cfd", + "0xb4df02dd6f4d3908142654a42af60fef034379b1526c12be66afcfc4f1177991811646495aa85702f3461060732cce80", + "0x8991bef253f0bb9b86e68e81f78116c51097004b0309e199025e45ac7ea55f8f6b2bdc58886899d275424ebd405ffac0", + "0xa74f1048548fb41e57f679d632280fd2e4cc6ab88c81675c59fe143b74dc7ccf050db53dac5611ed6b45b6a0b1b7f3dc", + "0x92011c668bff7ea995a71e4774e3fb5d521ee2552bdc33d9a65afd9677572c2a303a940751ffea470af898b01b9285ad", + "0x881a0e6042771492633b46b6101f96a48a93aa3860533dc207cdc90783fbe52b4a9ade1eea9117cea004bae802cd3fbd", + "0xb3e578bfd77a3a13368ecf8139b69f729cc720aab25853cc9e2f505c2e03e75cb779d685698af8cc4aba8d1c17f5ec29", + "0xa025b6e8dbeb68e7ac4a595b34089fed0d24eb29a7be235048205e35a97634d6015ab24c21a017b5012c3175677fd0bb", + "0xb751acd86ead936ed0f22d770872cdb5aeca3b1ec75a5a1e65748b665f8d1c859b5620d761d5f0a2a86331188e82b2a7", + "0xa05faf0bdb81caada6c662ed2fd145eff5db5c423258d6609bfd4c467edf3ddba6480ab95ac9f4dbc932f4887b070c82", + "0x8fd1faccaa7cf1d59be37bad69b7a99b7641cbfe930d778e0f712ae1fe9e78d53f37d7d5d3aafde48452eaeb65d980b8", + "0x86042bc710953f0042940625d8b69ef57c615f9631fc49aae169ca595446e9d55e149c92994d4bce7b544877d7b6f22a", + "0xb396047f716c5fa8ca9234c7026f1772d83f41be03410b4a32a376e5a038d252b8f36cb813bc3684f1b50326994c31cb", + "0xa2eece2d76db005f5d95f5f480bb3353ec67a9c27896fe54a2cd5cc7f802507d8d518596601bb3d2798842b96fc03df2", + "0xb738c1264d094f7b7edd27b0ddd8e29716c73bcf7b450ad7715fd21e1052998675873ccbec486fe45a8f72d9b006f239", + "0x826c4c5fea1596e353f6c15d91a9bbacd9ea592aba4d22e735263062eac44f073e5defb794f8ae4afb7d4dbcd1ace959", + "0xa8f1d170f63ae3b05ca9996347a1b3987136e7bafd02774698829986d48da3d421d269d31743bfd3e7917c5ace7ce729", + "0xae6871a8278f24d816657889ccdef509df0fb941fe6c5839cbfb704e81b942ea2a324fe0ac9881b385bc97410fd94b0f", + "0x8aa6bb564b6a0354be89c4ac10309f941162fb3a546259c5d789d4608cc628f69ecf814b59bb8bce364162f7552e628e", + "0x8ed85481cdc58fc540384213dd1b86f80af8908683d7d2c63ef5f8c4ac2e90f0e5f4e07b1b841eaecaab1f7e091423bf", + "0x88741d9c9d875e2c1ee5b95bafa4d8a22d72a728260297d048e4f0cd1c5f1eaa94fc233be3fa15a69163f218d62ab17a", + "0x8a99655974ad5c0f27b49d88a9c52a5375e16b9ac4f22b1e1bde53ce0a21589022c0ea926a4c2d7c432a53656ccffa37", + "0x8e2628878858764824471fd613cf40d1bbb3fa84ed081a762da0d6d491d54688723273d87a587ed1d3067976ab74fe1b", + "0x8f1a6162bd6cbd2353265bb348311073bcfca5a86f41cd0c63ab91b14aabbeffade5ae8a94f8e91faa386223fc2bf849", + "0xaabe8cd92f0193d12b032a9bab4bf4f02ebc0b24d1ac09f8ca8906621d6c7d4bb436b2dd879a1a1cca2b44ebb5642995", + "0x91cd27988ae8100d48ace10ac9cac4cf1cc8539bb492521a8a6489f8575a737f2a1d37fcdbe88dd651179145a59af920", + "0x8baefbda554bc0a0b425f2e132c7de061fdd120ebd452ecff0d78cc5bc5b15401997231727a37e9bc4abf1a553a4cbd8", + "0x971b12e25b989511477c04602f48f584485a0a0773b46643190263c0288c2434969bdddb1e55dc1f5b1b028c1c53eb32", + "0xa0e47f42444a16e51323af6f519c0dd2271a85746882818d02373ba33c2e2f7bd6a1c321497377e4781f72427fa34224", + "0xb52bc02de867d7b20cd247cbf496e03d940be2d7ca5755145e9a0168889db345fa9ab17c41635ab275a459fc9d02ff16", + "0xb01db7077e9f01e675c62f5095400cdc68a059e1a5005027033ac535a0505f45f89faae4fb9831f7ff9cbad3b55db02d", + "0x81ae065f1d55f4643a2ee120bc1245b9730455ad9e5402df8d6fcbb1bec71e40f1bfe7b8e67f96fff76d1478cd3973ca", + "0xa1be3723920044be80f398279e2f8432aaed45a36cc4fc71c87f5dbfd52225379e94600793f40aedaac2391caa57d155", + "0xb682f74fe46d4b647196b7c14804dc0b35e36cdff59671d7164ece874107964ff9f76c29b23c190796a9a3aa2df822fb", + "0xb8152e458970ab53f6b5bf6101008c5c31d2f58993474eed6bccda074555f7ad2351810d78676b62612e7eba2d86247d", + "0x9132a8fab2010360ca80adcc08b3a01658dc8ba8f60bbc45e1144c1219f69b985436c36c65cd7910a8aebd91ea1d3d38", + "0x805cd373a0919de801b6bb7a6ebf55530037fa41a1993c159e90213c492165c42b5642dda5fe7283ac4e3ade6e63a155", + "0x91f20d77fb7a8276174989faed41fa6da841d35b074c4a756c2b4730a7efb9b124ea6c7d5eb150a8b1126636cdb2ff0b", + "0x8cda3ffbd0ab6846dbee6cb8c0360842837a65f83b6ba17085161a7371a4466172354e494a8614cf2f1f4726d0a7262b", + "0xadc603e61dc36ee605dd7f2761ed568bf91b9dd3d40903eb7d77b11d10e4f762694fbbbcece72a7ec26976054139c768", + "0xa6accdb3df5029f19273a39bc30cb622f87522ca5a63372dfe61d993dd783ca5e918218b5c519d25e535d8b8238339a2", + "0xa188897269053f2494bd0de8cf098e41010fdd01f5a49d7ddd7b294ea748f1139e0d92fa7841dda9f8dc923ed6f02615", + "0xb26ad5dde632259293d91109fad4f742ab74de91f68ed2416ff53c060d1ea4377a875b2ce960cb7962c37a5fd47e85c8", + "0x82cfa86a17b27f375172d66b389df727734480a224b91585fb4782401d6c49d4dd347b8d1e8df6b9c0c1d2f8ae658de6", + "0x82911748e1471bf5d7fe3ff111ac06dcaf5b8a43c76f6583ca491e0aa845b61cdd443613c5728863c163952d86bfd482", + "0xb7b0d4ff87df02b5481183066f6ac0d1636718fbddc19889e92a71a168fbe338ffe780a792ec5642aaa4024d0964db69", + "0x8ec21f08594ad38e9ac365e5246aa5c2c8e34ae66382ac483b47771c33390ccace4d906695b1ac0f1c9204c46576946b", + "0xb9617d746596b26b84f2709a03b64fe77e9a10d0c85535d92d28dae9de3bbf6455a247f775dd9f67061792cb924e3925", + "0xabb2ff3f16309fcfe0a3b1bc928ca5cf618706cad3645b029bd54e5305682754e6ca47e364ff21b1750f45041eeeb358", + "0x867abcb8029b35a54552c57346024ae7eea38e9ae4bdbd68bb3c1de3935126880f237d9aa95d6644dba8ddce67e343e7", + "0x86eb4283147a9e595d639f29a967310acbed9ff09d9043868fd18f0b735d8619eb4ee0250764f35a51e00b58543bcc66", + "0xaf1779d2115ca7021533bcf55a100b4d3ff4e45f8ce6a6d98df22881526a429d97818fa1867ede09918a438957a03534", + "0xb10b36d0b69b0dbecb6f7efb6c612b0462c346079109970a26541a21aa2b5b81c1e121ed0d5c81af00ea8eb709a83dfd", + "0x911f81ed75fed55f1fabc5f86f9f38490e006820e5380963a739ebc0f87a1dd3b7da8c69dff1e580c5ad2246bc08e2cc", + "0x8379449499da9159cac2c09c61777955e61c63378d051bd28b59c78409ee5d09c43e7a6c246572bf34233a314511bbdf", + "0x84b48ec8895049bd03dc3256bd0d63f6e9abb178221f7d47703b447c709fc5fda47b19a3439f30f10d2670194f390915", + "0xab3bb5afe824d8aa20f97ead4c40aaa93350f33d980b5783cf56c8552a4298c989b7b188d023711a2eb79631f3a8c317", + "0xababba2722186a3b2272feebaf2ff46c93883b7265a6a4fba039d5fc0e7fe81b7d4dc2cef7738406f156f693ba3a55eb", + "0xad50302a51eeebe63085d3c1705eee9142bf8717d07c5d87e0e4ef5a12207dd5432994c72b9493f9ceb558a20929c9f6", + "0x8bcc3d83a6b8998e1a1066347c647ab122eac80c9c505d5cfbc370f466349671d8da4d500201226c15c1f62162efc62f", + "0xaad6946b5d5df34ee6f7422fbefc6de33dcf4461868ed7ee7f47fe9b8eb2f7a89759c73b7a029d422b02afd0f550e722", + "0xb0fe1d9a30759d83084b4c567b586e5a8f5a080bfa93b4a3feba59edaec33b6a2ebc98ccd82aa9d8cf0bd254d5f03baa", + "0xb993c4c2b77fcfbdb213bfd5f8d655d1d41a52583de63b432e2732df2f9d88c4c6779f314848417c06a089fcb970c0f2", + "0x842ea3aa645e5852695405b6ff2184e55bdfcf50be2319761e717b7b52d904ec47ad3abf986850c643003442e302ef30", + "0x8093b0ef1f6c84a8253d086a6fda6be8376f925f416a9d1f44ea72489f60fbd8b53cee616cc5ece43e2a202653c0640d", + "0x8c75f10b6aa848d84baa4120e75d3edb7f8471473851326cbd9ed7b29b22c5403028f49430bfe4320c3f4227827e667c", + "0xb4fde4f20ab98f76f55afd533f1b09ee4ffbac9486399714514fd694fecd0ad1fdafe13b2b80721829c7a59e4c951a76", + "0x843b2ed867cd8edc2eee84497dbd49f3dc481e7ece69310d06225325ef032a4e72907e16e7b6215ca775f88983d55e5c", + "0x9881e5caa9706e4d7ba6ab81525090e29ecdf1808931f3f2b11ff9ff5cc97f83f3e14fcf18abf18159c3fcf4cbc27042", + "0xb6c4acc868c05c955eb36a24652314be37004bfc14283600523729d466c56018c99a45a41ec0389449fcc3f8aa745638", + "0xb6820864d07715dcf4a9ece336464aeef9ce381ca7dba25acd48f60af056a3405c22792cdc57c641e782896c0ea05b25", + "0xa1bb482e35f71772486675cb4ee0fa5709b757083d18a29d4f4344e6ce901b2edb2889b7eac92c498b90c7d3844c450c", + "0x8cd8d8d47de859d0c68bdbe1834a1c9a34e92636600fc592a08f96d66426c5f41f388138f42c9b8ad72c596f4bf85496", + "0x801cc0631310656864b25d980c9e99a98fec2316414819afeaf182d3e7ff93b32a989e2ce63f5ea9301745080854188c", + "0x8fcc6b2b656f7960d9ad48c091c1ea71b6f0f61553f7695049c770afd509ee58ca8e1dcb403aa2c5acfbbba58676bd44", + "0xb997b9a6b994e3eb2de8723ec485d8181fd674de19ac9c2f50704785d9f5a28fe3ad194eb052b5ce122ab5e6e6968a70", + "0xa909e7002b82b371952ca9d0832f531db15882180e97c12c56da649fd65334904fbbc3f097b6a954469221d181e718bf", + "0xacfc712e1a61504814e37b3aad0d7a5cafce5901ffa43c13bc5f70507800ff03ed261367ccd09db7429cc5dbb892a7e6", + "0x8d634a07b69ad87e41d941aca08550ae9cd72fe31f3075511d030c364fd6578a36f3f0f3785d19305a1e772486ca097a", + "0x9746ce2d890248002c1bfb755e06f4f4570cefa7636e10319bf491c654b83608766e95fe9c77f1a6a630f5add77b71f8", + "0xa9dfa56bf82297f709f1b4bdbe4bc194bf22c0424815bafa6c1a536f2d15f35bfdebe0867ff20781a49274075622861e", + "0xa723af2702c6b473caa4a64142464f201bd1e2f765454fb0236082fe3ad77f22b4353e5981e6bc37e974c7ef797f875e", + "0xa42a1a0c50befa6864fa35c25a17f5309684c53257376f8111fe96c84a5e09376fad9c8545e1946f360e16e1e4c941e3", + "0x84231f6bc3038320dc13f3ac014977326dd13e5b2ba112c084d366b5255729b2abe665aca8a41d7aa6645412765887ca", + "0xa64e21d651bed6dce8dcfcb4caa60791b9345cd7b6a100f5bb78f7423fba5ea0d0cb3668f3415c27af29ac35e5dab0ae", + "0xb8eeb2128ea14d81fec5b1103d8511a3dfdab925212363c75c5cc01515fd94be8db2335bb84e221654380e58e9f2be67", + "0xa92e9cb287981b33a5e697eb1e757bd44f45efdda1759122fb27dd4bd4ce3694f1b6b2082ce4e6e3919d9d7a0b7c8a12", + "0x88f22b83fd9dad63e800b0bef709759f380c6dd9af7058100413e7b09c7517eba258d6367e0cb1a41b7762b86b2ef137", + "0x8353d45a2096fb4bde82ca22381bd2ed93fb58b236b16e68bb37df3024672067c4378d7f04a4da4d116e7d57a2211f7d", + "0x9076205bf231de091fcba7f5a4fe1d4a359f07236efa39f5715f206e5cb7eb3d9adb56af8181f63a9d3e965dc909556c", + "0x93ab7f56e8d37b47d3a8cbd222f2dab4bdbf94a1152302752f0a731294f4dc214fdba17977f11aaff2eea9517fdd5789", + "0x96d9883ee108c88342befc358325356dfe5d72c521d71e4b3a58d6773ea3d1a1de1a20572aa96ca0e8483eba62466504", + "0x950e0d61ce4e76fe0cdc3d59c5bf23d8e1cfa9d6ee13b9fe41e6ddc0fd52081bb16bcdd973d319c20709ec517fe15626", + "0x88809c1e272b552d46137165e5396917d107547b65059fa646b742489e8892acebeccbb3eb8f2d676e3836c985cb1756", + "0x945f13ff081b74403a19dbb04173780f04766f7624ac6b77f46464df5f4f3b547c459f41fb1842164d8f1c126ad6be65", + "0xabfbadc599bcab1c2b7cf1fc5aac7798d9f617d6afa0469ee23230c0d004fcd3de0ea645feddc74e676ecab1fcdcd8a2", + "0x83ea1571b064d05e1b7f4527b20ada121024a4b2dd8f7d551945488ccfddd671ed2ed3895578afcb3cf958f9a2c75c29", + "0x8fa75050bda001409f2bc0a275d8dc0fefaa47b3a0ae132758bd711eaed0851d6bf3e4b7f355377a93fb8eb02b3ac6f5", + "0xb2fff49083bb30e2661e2d8978149e0d0588dc972222f46d5d120d01dc5c9978830c442827c8fa295f6b8e6d8c786198", + "0xa352c2dbe4f18b311bf0690d77fbc9439a1b8088c806a9d89071b3ea04ff387325cdc04a091d2bde5fd087bcd0f4f482", + "0x948ea89408826ded81549cce823dfd7605ffc2279ca7d0964b1ab3d5f35f4b174e81575291edeb9eaa4baad3610ba3a4", + "0x998073b618140b04ec394ffe4af02df044d923a5cbc8f06f26c9eb4ece17abedd4f72e10c9738bd16863327c0f6ee20b", + "0xb3bfdda0d6960af897ab508bd9312d9c166157f78b45157b46fd2e38ab2e430e8a19335d8a611366cf74642bda77bc78", + "0xb8dae3e2ec5eb97ce3b5e9be719bb747e6e8f28dfb1a6b7bf5063822b502a5422cd586bacd87ef83c0af081ea4d30a57", + "0x859713ddf0ae843ba690fd8177ce6c08e2fe5fc1c8893d829d39a199e04758719bd3046034926de40973a992ecbfeda2", + "0x866f150d4b6a015b03ce8ad93a70644b55ca1818a0f50d24795698c62f3abe59d3b8abe4c11ffcbef20127d3b7afb970", + "0x9145367ce9e2a5a6140db58cb097767b5a6e19eb36d1c03acadef612af95eba80048f2b02c6fb46eaf38c75288e3e4eb", + "0x8c298aee778f4af13329975754e9b428e127680f26be139307d43268dc63892ac98284d78ced0ecd384301e26d5b63e2", + "0xb4c2cc9256fc33ed09531abd7c3e34f8f24830a8a2cf2d684cdde46155f43ff2715c94e7dfc7377765ec0cdefb21cd2d", + "0xb9193113b81bba4ebfe40e97be436515254bc67a94939220e5e69a197765bba40dac3369e5cde115d1bbb65e1c826038", + "0x8474d72b7cb52768c484ff92d014d7733003b511c0c915649f65dfceced47ecd933ce876eae254cdf2f6357ea865580e", + "0x808e9a59f947b2b39af51deab4c164878e02d95773dddf1123091e27de87cfffc07aecd7c9cf3e08c0b9f525bd87fff8", + "0xa8e0049eec8eb70c12446596ba5c8a29823704be3753312c34cb271000b6c154b1022812dd02d1352cd263b655437d6d", + "0xab7894a75e40d888a4d0539582cfd6b458da009a5017e561c14d312335a75745ce134b57466fd30c250ca07e0529c8a4", + "0xb30c5c0abfd35ded7a3da8f9c95e3e1c320857be1af317f6ff5e35101d3f31de3735ff8741f6460ae1e63cee543081fc", + "0xb15557ec268b4eba9628ccec0a5f3c947e624b61edc876e2ad8c36ada061fda76f69c8afb95270b85f4672171678d078", + "0xb7ec103d6695fa64107f66622148902019ff3acbff7b77ad80993bdf209b73990b0fef92dddc5fb66aed77cdb59af9d3", + "0xb3d002f0a35808e3785d58d0074be620416ee9381bdbdc889805ec2acfd169e1ccb60045d87cae3e90d5da94cd58bf80", + "0xa17c44ade6eca0942742edd237661ed406a129a968fdab28a58d19308d207a1e7853099a4a3c1c181695fcf265107a55", + "0x91fe5c0d672fce368e229e735eef43868e31265502e2876e54aa44470a257d1c126ed73d6df860f42d8e1dd425d8987c", + "0x8434fa331278fcdff2c8c07596a051847425fd7cf09af31bb235d208ef6e282cae173d6ffb73c0475307453d6133ae7e", + "0x940188d6c20924edf1d9343ea85ef9e08d9d87d2a188f8b69514a22cae10aa2d3ea8e662d43d60b8b77183b3c6e8cb1e", + "0xa89f57a730437fc511e1873830b300df7a417493a468afeed2f837f31641cba04924effe11be92d3bfabbad0bbb7d04c", + "0xa561550cb347fc9178c875ebd8dbf5d14c0afbefa79f7b93b893a25ca8fcdeb0293de5a350ef63413aa70745cbce9a5e", + "0x89fe7dcaa6a10cdbeee9d0d3bc8dfeacd47e1490a6c3b591f66d3a64ed668e6034381e0ea9f5f04fd2a5d9ad5044b8b4", + "0xaac54b334514d41665b80b2cf18285391f47be820446e2272d69edce022f6d7689c8e137e2e9579d0846bf5440d768c8", + "0xa231a04b942d471b32cdd12eac3eba00b8910fca0812c9470802246c479050d6c860f64bcdc6b6e39ed0e9609df9239c", + "0xa6bf6eca52b5f3ffd89b79be6edc4f517fe9c9bc67051179157734689fd63649e321d1fabda916a9c4666b64ed60bb4c", + "0xa7c4f791a1d77cfcdf34c3b73ec7a43aa1c8ec81c39ce81d12c51973ddb0bfacc79e1a128ce17afc5838982f66cede6a", + "0xa1644b337c4398f00e9ebfed20d9b2c900ccb667be036abba0c4d372939f881df2bdb5d40b64354f65c8f2ad9ffcd656", + "0x84f6e86481d3322de791ad01d8c1556e5480534e52970fa601b295a40270882476779301d78bc2ebc323323ad0b62253", + "0xb32eb2beaaeab27e190c9d381b9f3446038391da552db5ded0f5b58d070694f07c737315a465175da29e2a236c539e9b", + "0x857029d97cb9fcbb67e194d9aeadf5b25cf8184b3b704ff5da424fb4b39abdf3f7f317b3f79c762605bd9bdd5823e7aa", + "0x883926170997ba84cf45691c117912f6be5c691abab77fd18fe114577e6dcba18f8c0a6641ef59affcba1b2c92e093cf", + "0x945be3febcff77b4238500054a053c983add7a96ef43cd91921dad908c20d4ae08857fb93a5bb588e9b441aa9a536567", + "0xb9efb8be322722302d1c06640f772596fc362586d8f2e49c41810f4bd2b59e8e9abf3d5369b2421e1ce6949c067f07be", + "0x920ad6d5cacbdb46af424141391817da2fe3d463bab8db760026f98e50bb51aa4f3668520c133ccf9622d66eb8a60e86", + "0xa1a9ca07d8d3a44fe372aceda194f15a2dc3d29267aedcfc3fdbadff0bab1c4397da1049bc0feb9097afdcf1cd1ab603", + "0x935eb5fe97d580c10766bfc2fbff71d8584e00e1a321018540c25f6b04791b63a0d6992257fe110b0d17712f334c9b49", + "0x9530bde6dc33e48e05d98b77844766afc0d5581922e382a2fc1c183adf998c8137df29e56b868c7892b2c1af56edeeac", + "0xa8cd3698276c2bb8d39ebf7fb5fec139580755adbf81bf362e1cc19f4a8be750707bdf4e1fde3064873495cce5cf5171", + "0xac5a83c82004728b34677bc6b1fa507687992b5b78745e5820de08f3fd99e35c905608936ccab62ae39f0408334b3c6c", + "0x927b0077386a5055b499cb5a597ec3c9934767343fd91214fbbb5487faa4339837eab52c75a627d7addc5cda5ee35108", + "0xa8acc2ea4a548d9a2fc2738abcf75cc0efa189b92a99296c0635d53f2c0d7ee40ccc8ae410d2779f95ac6f2027c81d06", + "0xa74c24b8c695920b12a86ed6da6ecff72f8e19fb06fdfee9cd1c1e8e5f1c202d26fbf2fbedc9a5deaeb2d986425477ce", + "0x871251e8d69de5c3117f364bb95d876fb89974428bc167666088d5ff1b83328b675ac2efa2d0e215831e69ee254623fa", + "0x946f7a6d3d6700f65088c817636ed3c1349e4f5122fbc22723d131d8ccd055931dec977cd0cb8dd888c6abc51a5f4194", + "0x82f7c1dc3f133725570c7b64e31b0397fc3a82cb4966948803de210182b9716ccd19e59c0e0382c0c970d05c5e13509e", + "0x8bc45b43102e0df4767156b1e8ec635cc07fd629793d289be1f2470297e8a084bc9af0d76566cc485a8ac898c0493fc5", + "0x85000f8c8130abca642ae94b4feb3448390745decb1f443c34fd06575f1d0de35bbe649b46251df0a4bdc7a8bc133b2b", + "0xad1ef07d34c59afa37fd5147646c24c03622ae4884c163b80d45ebfb5fa994699ad9166ce1ef727c22be3c28e0838cbf", + "0x8d1dd5500229f463f94c611bb2674640d20f2d34dd40b28c4d2a21d3e64ba7355fae55228f1c70095d1b288828a1950e", + "0x834cf56a4f2c2eb04b89383213b84bc6ba554a4715c3c1547278e5501102f6ff2af27cce0f876a2aa2da57b5ac6f3b3f", + "0xa468d06083d770bb4e484718d1c147b49770757b5b296fc6d6035ecb3c2f5c4155176f12ccbe6616184789350403f387", + "0x8abe730d80ea895705bf67ac4f6b6a36fef7403702d8458a383d04e4859b4c8c7a75598721cc75793d29276afea27ccc", + "0xa3890145fa43e6b5c7b8aa0a73a62c39d623c9a75d17c5a05bdddec08d114ab5b0a865c9edb2be6ef31c3dc9544119ea", + "0xb2b7c1cd0aed6b776515a12a0f3a86353fa3d3a3b6027422bf7f2c21e6917dab543e189e860c8fd3aab65484b77efbe5", + "0x95215b7d3d504ff83ae2bff789feb6b5919287d354d567141bae68a0f0d27b3e898edd8a9be5a51c04dd28ce9d4ab937", + "0xa93a3da0e101797c690c38a5bf5bc14e10842e48a18c9888807b2233809ea8a34a76d20a8ece0b682d36c086853cee40", + "0x849a7fee901a9279dcc36fe8f276ea6dfc37c30f75b679ddca2cae9c283de19c4df56790e6ae12c4bde33e837fcbc324", + "0xb5c1587d84b0826e64438d8ee7c103119b164bede8d243a0256b5b798240259dd63281b81bfc613a4874a6732d05e143", + "0x97600c536388c942e0a72ba3bc33b3af48045994a3ad0948fe0741391c1eb99693d072d1efdb644abcb08e10474b7885", + "0x94c2120a5b4743496e7ab9bb2e474580ed27d7cf5b6fb132efcdd7bf934434d2be8d6f0af009c637b31727b3ad5d2280", + "0x8a5ff1e7f552fa8b34b22a220eb1cb018c9c9430f0f14a634121923497cdb4a69fbb8b60eb33e5fdf9b0feb3e9f5afe6", + "0x8b4c9032f25181e6fb9f60eb07e3d6cfa2b14ffdd6a0fc1b309b078f8290901e229a5a6ed96dda74e1a9a894224ff588", + "0xa5e04e164ffc46da1dfe026ffdcd99332874a110cd168c44762c461a5560b5c098ec71673d509fc053f6d9064d4ba255", + "0x97d21cf8327a81385fd3915c7e8efac7662f4b39a9785b4a936fe1b581d630678f42a3e9ea7e02bb4413da7ca9a6f35f", + "0x806d8462bbf148eb4cff812cab11b3d819669ef5f0d76b228fa166b83727c92fdac98ff3afe946855685b050d9d4c6aa", + "0x8a9899b0ddbcf4ba3f16bb006218022efca867a5b32e1de9c7efe1d7039c8e200a406bfd09ebb8921bf1997185e9266c", + "0x8fad2d8629c546c5de443b36927b068cfa333c8c4c1328e1221a1f6af7be5363ab8981fee54307532f239eda7656e6f2", + "0x930146a1f6c3decf40198955059f70c98de7c5bb1b25bdc97fc72de3a84db1b121430cf7a7456a692d8bbb6b325b6001", + "0x82987887016fdb90f79f045c16629c5b2b17b1b4702cd89d06b70086e5922cd10c5763cba6f3d30a2c33bc84be36c6f5", + "0xa6fd7e4834f7f29da41170c13d29acbba86c74d5924cd361588cdda26a3ea7f11ec34c31869537ff7ee0b57a24555e9c", + "0x97b2474cbfb632148869a6b911c2ab91e4af9eff6c181566a1eb34a05d2ef3fa9da4fdf14e8fd8746a7c3123e20d572e", + "0x99ea177bb7d98dce25d300b09bf6ce08a7061360c4ed9a54e30c1aa5a467be6225737b62ae921e91547b5b9d39b800d9", + "0xb9dae836e37d51c9611e6522aa6aa8bccf2644f23113584c74c963d79af0a7ae533af823215fdcbbd8df62f00ec1505a", + "0xb1a7165aa1ac480b4eb1f0b3d4284c69907d1b5056a343a2da84b3863c9a2ec4d757493f5daf9ef252a253bb3b2b6745", + "0xa1322eec41b38b8bf3f4566bd12f9c230dd04d085e0526218489e986d59895d471bd8bb08351edf40021efab1d29b2d7", + "0x96d559df46015e62d8876f4d8679f9a9867dff31eb151238cd75b3a10bbb2ab0f51c804a2f5adec1decbfa355042a6c6", + "0xab55e38cd273bffaa94400bf4913ce0ec1c1c848e8c53be1808d4ce5338ec92b4a4160b8faf0d1d8ee8b71ae751d0ae7", + "0xb61c2987e2b402a52670abe305f8a9976efa9720ad0d7c5c1d0d6d9ec6f1569f51621b6edae84d9bb3fef32bae31a088", + "0xb5234aa19fd9e714c7a9f3ea33d39a5c49f42e7a8edabd8f306083669df4898711d4b50b049dfb91815588ca60052673", + "0x8e98a7b90baa4693c6a1e1c2e556d018c3408bbbb5dcf2c32d120f797fd8ed1373f1f112dbca114863801ec6efc1a5d0", + "0xa7e1e77cbd6274f8c74b37a607cc20596bb7fc35ff1ab4358de15b07952aea397e409b30188c8516676cdd05d4919f3b", + "0xa5f2336ed9338772b71e490b1b3916d33df8b013e4d38dd57185b7314ec9aedaa34eda2733c38e06e656a8cec74080ab", + "0xb5de079ec867af3a3910fe47628c7d793c7d70b79e25a9a436e0a75405e2c58b740c1b86e1b073842d475e0b717d0bd9", + "0xabcadb7a09173f1eda179ab7e3a5722f020402eaeafb9d604641645c21f1e009b758f2a6fd262f115d80e23f8baf7328", + "0x8694ad59d4cc328b064884d147f66095605d9bf339d09e45652d68de765f2b09d45558d45daf9b4b36dcf881df8d4fb8", + "0xa2cc7b2e812041f17b450b5fa7429cf62e2da06a7bb3c08a63d6f802ddf13e8b73d2056bcd6407476dd322fa35b9b065", + "0xa97b0e7e22214f329fc57b6d7ba882ca563f863c06f1afcb60c0bbc81ef08ec866d39c81a80a7843889fc957d532cc0e", + "0xa8a809392dbf35911df8566dc20e2373e2fb3272bd9eaf9f474588a9132f06b5a1433ba9f36a738c6cd3fee403188fca", + "0xa3fb0038f83116eef1d6b023e2e17ba2795f7f90ed7c857d9f04337cb4e0c2e7d691bcea54aa72ac5e4383125b74b755", + "0xa80ada835fede8d121162aabfc8c349f685775406693d599e3c288364097b02d96c10ddc20e72fd308fc882e5b70c064", + "0xb6e6c4b24731a2895b7513ad97c0928efeeb0c645dac9fc8cbb0a6419221807073f6996f2b778e1dcdde63acc3a6b2cd", + "0x880a2e8fc2eb57f44b08cf4db5cf1751bf9f4aa688708039007d2a198f4e7f0f808aa566b36b15b971e804835102400c", + "0x8b3baeb4e1c1d7493bd885dde7873afdc235b58e45b515cf51ebcd02a9b81911c5ca182a9e340575585186c99e71d2bd", + "0xa6248e1bef3c6c6ddc155dfe95631a3f00308fa77b1c1779935e76401e750f151b7377f9376c08e8273680e924382af1", + "0x800133df4ea65de3935d98b0249e335a918c44167a34a16c0a4adaa4654f458c376eaa76ef088672d39aec4c7d951833", + "0x8317a6e0667fb524f35672e070f047db29450b06348604319765e4db09f966ad995098cf38acd30346c7fef5dd62528a", + "0x81fc2ef2ee0e6f21f406c51f02b9b7be8d99d30a054df918cf89c708d64c34d8b0dd060dff4383de858c0dbff25d71d3", + "0xa28611f96138fe6974e3e1925b582cba76166259c32b39e95702fa0c4957ef2ca32d575b1c08cc8dbe96ddc0eb56a9f2", + "0x86c6773f4e0261413d6d3944e0f7e498a6dae518120e3940d2f45054a912e706b3b615fd160e6143a7e54942406f9af5", + "0xae91e3db099d165b198d80b6d9af894203949d87cb980f4db97dd43ee55fbe1a45df156b72e3c3e9306975f9e5e62d77", + "0xad00ceaea52dcef616be9f9815548f8e9b800bc9c1a8832a4d8acca6c8779317d1951e5700e54db070a23db41266c934", + "0x94426f78470aea2d82eded320b45bea09b7cbdf02a3d7c2af4ae4567a3493b352b36f43c3669237879910dcefcc82fe0", + "0x8aad924eb1a30d2844654c9829d82c65fefe964d815572b6c9f902c6a826c247257a7d0d4967e2bae331d52fb3b7c0ed", + "0xac9489ec928e4f43f8d194b8f3ab83382b66b045f18efdfcb05c1d4e67af7b3745ffbb7f52cab4b8895550d10132e2a8", + "0xaf8f390c7cc40a08c0143b467634c10e8046ce40466006a4b4297c76a6c16309b50f41a4a022fc838738c4c72edfb34e", + "0x923b0384e87a2ddfb7a2c47f628172e8dee76fe812c44a756c67cb20527d8e9029a561bd4ef446a013d4be7db7259f6b", + "0x856316b53f09a90af770bafb5c9ea7deb921687fdfcf512840e96fb83df08820c42263c9ccf51465da33f1b03db04d09", + "0x92e8823b523f90ab75ac6e30869dcb257d232b55a3e167769ab5b54cbb83be94cf5d84eed4b1653db17f3f1350ab5e53", + "0x8d0d05fac92079a3df86a72fa399e606fec7e56f81d3443cdf0cd373b3330235b76890197ae61f24d17de39dd1aadd06", + "0x8a801fc71b9b6988a829044060679a7cc3d40630fba81f72bcd15c0e5728867f4bfe938066e68cbb54b042a39600fde2", + "0xb40a6a786ca1a21159b72990b4d3ae8729722cdace4e8124f8cbcc3fa96005563535d28e9d92cda02e91d979d27f8f97", + "0x914f30250d79829919c8ed184c2e471c0d9835f2348e628164dbfe39a51dcdc3f8bf99c945b1f413e65fc5424014e5c2", + "0x8ab8b347b7846fbc7ffe69c89ff67dafd522bec708b7ffea312b3a7eac47fb9d6006cb9038962a07dd89d4688ee6a18b", + "0x8e755f8cde0750700252e41f6d16b825e7f02748a13744c004a52b19e52d58c42d1ac32cd5ed1d6ad14cee5174b4ddf4", + "0x88d6192d72e1fefbbc9ab400e5b0018bd300839cf604cfc1034657f62fe8fcfc52acd86c207dad0fa6383361d338b2bc", + "0x971fa2ab593578b341076d98c49c71dc7d9eb4ca706efe252441499037cc86fea49af681d8a4d324d302526b2a3e5c18", + "0xb2deac648501d7e284a85c19f514f8744c48d2b5516c993c2111128a9fa042aed34dc371a0cc3f00e918531dbf16c0fb", + "0xb63fab8600fa531d7f48f8d207298544d2e03d4da23cfb43d99b0612f1a20441526de63b7609f5969429e763147ee5e2", + "0xa8f30d9b4ac3675d61199e8e624f88b9dc52658a2ba26a2bda5f9cd3780f0b1e32b56c825d9dbc3a059d6c61fd37e261", + "0x8a6f8e963dccbf1db9c839c21a4e832c7a218b00fc31400346b5379fdb8394142bf8f8b981fca3f4d3c43d4e34dd3e31", + "0xb4883e6a4213c799abb2a9b6998ebd4c89aeadfbabbe4c363b22beaff46939dfbe4dd20d113688a293a41daf5cd82c8d", + "0xaedb55058fb467ee9556a3b601af86962f99fc06f7eaf837b4deda030b1899f565da07ddc7108e9f5e7024e11c723ed0", + "0xa8185aafdbd22a2df2ea0f0cf67fc88c4c3f8e64040da08cfa9e8075b792406c20d3155d6ea6fdcbe9f5502c44125545", + "0xb2b27ff20d24cff756e8edbd6f8686d202d687016c561e56dcffebc78f404ff544c4d3ae8802b91bed0487792d6dfd05", + "0xb6fba06a70d8b1000555b8c6d791b1db3fb7f57a0f8b1fa8dd00b2ee14242877e1e836cef89be3f9e0565e61a6b4c275", + "0x92b3dd6e18600ab856c276bc787429d42b8c02abf5243f7919625aa1f4e8cc3eca61cbe106b81d0e4909393a5efc021a", + "0xa508e1a1d4375f5130c95a169fd1d4df51cecd84822dc28b18e464c2189d464e6dc6a5855e0cbb94500d041319749ef7", + "0x84b3e9a6b5d1a7bc7df44ce760b5b686fba006945f6e1f3f67ea2c90dfa6ed70bc1f021828a0461fe158ece87deb1e30", + "0xadd83e686118fc5eb56d79199d33cf0c90fb2a5996c6f453fcd9b9eb3a273a466776adba1cccd6be62a4ea154480fe17", + "0xa1fb58d9a323dcd7862ad4bc6359ab2bae35a608276a3053d40bb3abdaf3e8827027284d964e51ae7b61dbf299f2bea3", + "0xac901ece7cf087c782f75f1c61371f77ba061bb752ad680c9b1012768e5ebb6241b492bafd9e016e989cea1ff51aaf5c", + "0x961b9ef616b7faa3befd807772893c7c66ab6990a9405cf4345ec29cf13d75dbb6da41ec87af5b5c4bddc8787b88b480", + "0xb386f7ba0b94ced118691d883549d70ecd28d1c0d1b718cb82a92a246e61de4ba80b6a76d6039c261e342f9ac136941c", + "0xb6415848092dd93da62b5a5307d356d968bd7c935d3626f40e9446573e5794f37a23ca072fe8af2a9355a4b04ad35e58", + "0x843b3e3221bb08122a1e649e81759297d985c7f393c36cc3bc707a7aaf2f53b9cdd449e7a4384981c5976fb3955871d4", + "0x94083ab99a73dc5cd463b5259a0f4e99847bf32ae03739a440f8f48e12f078602c76b3fe4e7ecd31d52a7aa31168c5ee", + "0xb6f994b5482aabe833e388b24b9445c01e47fd6e354c3684094237189001290aa77a327181e7e7e756682a04b8b3c56a", + "0x8366f418a3fb2dbc9ffb5b798adb968aab991fa689ec24a4c4bde6f046989b1815e1bce5e846f3554028e16799e17281", + "0xb8e5680915eb37153daa9a3a977b47c88b4f30fd358901888a1056e07d2a7070d28a47acac7aa7856ede16bd0c93ff2a", + "0x871cc7a122cd7b9ae2199801e6a0974ba8cea64e5866a5130ee0ec926adda24f91b3ff2785932cb55537030bb5ad811e", + "0x9370ff1ba27d33080efb22836147f766c60f0a8ca250ac6b2a82bb464ffa543da056284b712dc3cac53dfd1680a4cf87", + "0x8614d8029df5058f5a072716489f734131b228972ea9b2b952ab1150bc50b6637543aec1c35763f8dc578275f7c9df3d", + "0xb8efd01dd0016a27a0e2df65b571d405be4dc8e0df5dc0d8354fb187b96589e95847ba0c2856613924125d21193753ca", + "0xa86e524431247115ee497c07ca2a73387eb820d293e8bb74e1ef1ae7ffdb21a9dd8ef1a6e3f391e6f02ee0b51fae2a06", + "0x9151e2dcc0b928573421ffbe43b1761b6ccefa4ecd58be7fbc8ea8e975e18d52c264f682104480d590e6f8c0b8b9f63d", + "0x85ac8cb79fb8916f7eb5431b7e81606b38afba15895909873f85d9577c87ed2c1d0fd489fe058362f20ac05626681346", + "0xa076dd75ed807bb7afcae8bb9821ed46758c1a8d00e7f3d3c91a18e6b95dff3958ed70441a1f4691ac3268d95e243614", + "0x89d8dbe170b9804de3fff5b6512d04643ea0041c3f9bedd7432b171ced1577b0c0a7bb911852c6bafe154ba36cd30320", + "0x809a63ba788e618a281804ef97a75df39c7115900078a6bdb203bd79d3df87e863c631e934dcee62e28a16cb8735acfd", + "0x9727e6720f8b73b6ccad519d8ca1d4f90c2db33ab536f399e2c4ce269be15d99e22504ef153aa26c40d4cfbc450f25f6", + "0x83e77918ba6e28ee01ba6b8dbdd84c53faf65446a90bcef46f262f341dace2e237b1ff8f8d566fdfefc6973deafde716", + "0xb5a4d3fff76905bbb229d579b8433e76f2f070108230f20a30e4f974f12f29ed017aa66e9b298a4de0fd535a0e1a44dd", + "0x876d3a0bb439e7da26539b98abd0f7e0b7e8035eafed08df623a77fdac30ac85ab4d58984396319a88e072dd7a5149a9", + "0x98923e83be5b2877ac18415f9391ea792933db718b29b6970001682cc8434ae9fc640427c0a27f6d62af5f78f3901bcc", + "0x805c675a34443a14c0098613d11b4c015264e038a8d1adf083844f2e3e3f2414689788423dd0ff77c02130331d511068", + "0x8d8cd51d4146bfa48492e9d3f3e4b845d4ad1442ce6bbd95979f9778ffeb108c641c9ffc2ebbba532f922237e5849222", + "0x839862454707a99eef931335e5c5ed80805ba06bab0337c5301fe9fb92fd59c9ff6620e66de7369352b079dc52bf2113", + "0xb3cf3bd867f60b345a0b91314b34ce1c02e64dfbaabd70782614208d32fcb5d4448102bd54728fb05d1ed18a750e88e1", + "0x8207a421d010e1c5854b8e41460c6a13035ee77f7add0df83c5c31bb00d7acdbb676478a7dfc738b9aef5c29d345ab63", + "0xad2b14f87281ad6e1d2b713e6e8303f1a45cefe097820d6a1bdf4652364e70d28ca92193e2bc3d0a1e69da5a51c90ff2", + "0x98025be2d7e59ffd3f6c3c2b28b27ec42206968c0f96d09330598fe17a207baa6574aa22cc26555139766cc284224fe7", + "0x8e80fe898b7fee849f7dc8e5eac668c76f1fe18d159c51eaf4ddd8d4d600c852dbf6c2abcb878c64f37db7fba3d56968", + "0x871c0e2dd929ba4e157ed606741a6301aef759e10a3f919166faab23e599d3409b232240e3afe9c0e1622a11cd453c1a", + "0x919f7e465b399e2819ec17aacc199421d267ff2979ea8dc8962542ddbae51e2bbdf6cac92f8a35e05e4d95a4a8315cd4", + "0xa6e6667e6127ee4f0224a9a94be3c22831a1ab3b16f57462562b11473c425e7112b33bbbb6af860c81bd6e84bdbd3b86", + "0x87eaa9e3515f2d94acf113d77dc085609d06cb038f5e8e90ed29bd04bd4814e95ed0d6db5a1d65572dfaf73ab2e50ba9", + "0x90b30c66ebc16f767f3f0bc1d8bb17ca1951a616292297ca8dd06d54cc53e5fb5fd6321ce158c04cb4c91a04c01f7fbb", + "0xb5fda3715566188630f96207c4253315a9cd166ef96651afa0ae1d6f0aa8856e7642e2f8ef3b1fb1eb2c14a7331f6592", + "0xa54143f662a6946da901ddaa9e514a0e96bd6397020cf5d88084a1e1edc092b94facc150b1c029a508fb3995acee50b7", + "0x8dfdb813296bd105d5813657c98337a24c8bea19bf0d119efca052c018ff5c88f31e05e110fa12f306ae4b0a8498f113", + "0x8b7429599915ffec755060d9cfc2c445df9184ba6bf298bfff5b54c2ec8747a9b65bdc6c73746a94a54b0a62d93b6a28", + "0x8a1d1108174d383465a57ab4b1a6811ab86dc007de4f342d37f4cd311650382e0352d3664ef09cf1626c0b74e2f21ace", + "0x98cb860aee0b7251da2d114b2253daf977badf82027a018c956fd59c6c93b716bfe69a132a4778ee4b7168fbfe390ad2", + "0x94d5a0d33a0aa590fe76c71e80b21246dd9bd8c2f5ecc647e47a423c2dddd743010484cf2fa363ea73bb217247429066", + "0xa082b7a109fad08e2c01dd7322625c18f47497b32269ae4e529b1681aeeb3c4a813cc6088ebb4427b486320fbc4b7872", + "0x86c23e2d3b23244c7763c123ad67a41a2dad8e4556cac23696906d1acf5f4cd7f661281b8ab2027d268405b08eee6771", + "0x801522a5c211e49eb96294a9113022d86c84bb8741e44fa7328122836a39ba7e11e27d0d6773550b234531400ba1e7eb", + "0x9683d154b18ed641867fe67b2dc70e8b8afba79f73fdeafdf9015d85aa0c74d270b290952683c3667c0202a83626687e", + "0x994febc16f8d216a20774955523262966e955cf964950b4b2831a3483f818c20ee6f51cd24f499dda0d3191910a9fd35", + "0xaaa8f12184525e89ce980468fd24e1a9af846246297546655763ecabf0b5b5047394543f1791ba1c70e21637cd815877", + "0x9193a37d5692ff1bacb0265bd7825c479624d2adf33a419b0a71c8a744ca1b0c9828127831302ffea4fcceb1a53ccd54", + "0xb9f3213d5d588ad73b86365cbcf0fabcec5c30cddad418281ff2408dc140e3f6a25afcb6bb569605191665706c675e35", + "0x96aa280b2f0ae5c3ac51edaea4435ecff8ecf8f2536a3400d8c4c9b12c64d16418838dd7ffc1b815656109ca63261050", + "0x8486373d67804e9832bddca04a0084d1976d324d85c22a52ce2bcf7518f014ad00e4795e61c71e0dcad1f23316288dcc", + "0xb4f2e7f7e2ed7917e7c5036681e1ceff18b688c1abbd203c2bda0731ab56701a847cef4f753f68119110680913c2dd4c", + "0x87dc2336d88edd81b94ef78e7bcb6d3876257c326d28b3f4484465d6c65faa6c17aa7a2f85c6b94ddece39f6736751aa", + "0xb4b3502ebe175820f53da8e3fa28160579c4150d79d932923739aab545af537b3301d5b21f5138ab4100e737fb61a084", + "0x88063af42d5845267d979df07be0735cbb42d9b57d3625eb5d0aa7e4ee90ca88fa52aed480a4d60eaf0ab8dbc4f444fe", + "0x85cb81247c09e21de6deec42e668b72f513c7b105f60ed478b08b85fdc8a886a97bb7e39eca0cab09b294e4b1490b0c1", + "0x9920fcfcf836faafd211fa1ca78302aa6feffcda98aadb6302300c250fe8621b60d9c214ea92087c44996ae0999eae78", + "0xa1f91af5b378d61ea277e5dac81cb71d71a4ac35322aaf42b3a8aab1641fd51d8da1783bae0e8ccb66d73db8e1003478", + "0x87507b427d381ce3906e372a12f4e61514ad7a102334826266df14542adcbc8bb7c8450a1fe110069d9dc2e9bf0687c7", + "0xb7581b0cb549d71201583e0987e9e9bc6cd36585c96664f836e1b7326e5375ce8d0a450343fe0b106dcc581b77de88f9", + "0xb26504a6a7a64c44d7f97d0402bf752740934ea4c6e101ec131666deaf574d55fd7f96c8807473722b6629dbda2ca3b5", + "0xb90accb5c6b78322ef88d017fee2ae1cf87194f4b3f6f4ba6510c0adf4c11b20870043cdaf45372844f5e801464bb682", + "0xa904dfa6e1f813b4aa0b242f3eaaf893da7ea854efe514487a237a01fe244721482476b81ed75ef1a951fc54802b29a1", + "0xa00373aa8d98f4dedf9cec4d227b5fab00f3af2a7bb4c8b0dcedecb5a04244321d5f25a81d57ed0ddcf293c701d290f5", + "0x91bedcb316698e73f43e9dbe0229772c856f34901fa4c1e018e96eb898e4ae02b19d900e87d01501099163be56db57ae", + "0xb84dd6b9a61cfc0817da422380b0dcc5221deb600b4b6a6f6c5ad934110a3b66c59f7407ad68bf8642b2bcb5427e8050", + "0x8507c172e499856675ba69fc1b0389a08e58f8e5658c9268172b926dabb4a67b7c836a44d865f736e8fcb14aa2809529", + "0x86609a1d82d90a971786da9ad342035ae4865136e513559069b6dc8ba82ec0bd1ac695fe8afa5f61f85c2310194014ed", + "0x94914f127a645594ed372855550ec0817663224208c127a08bff3d5c4f463b7939cf13a45dee68586b678ae453c6d60d", + "0x80b55565972213814afd6ad9b1884a4d8143ae90c148ba730ca77b0937c2faabb23a6f985dd0bbbe05705fada4cb1a00", + "0x930f5fe58dabae91c26c6fcbb61c3e336678dcc35d028e5c958d2ee7d50b80e1693c0693b82d719dfd9fbe2c03b52c10", + "0xa45053c493da932896d95d5fb158869c1051e1bf99658b183c9cf4415fc8d4fa1b6a8752b8bb26e8b706a03a57fc05d2", + "0xaf7434b48d2ebe639c8082be09060422f662317bdc136d534b76ee3e3aba5ea8f234cd4936aa2b928f6eafdbe5165a6b", + "0xa57a073bbbb3020a92497f0ce854666997a182f2b437e7b06c9888db8acb2fd2128e3959f45c391f0548a3de49e37e76", + "0xa0ea8131b2d8cfb799e806d8cb92cb02d32de37869cf2ac3c82f7c5d9a963d562755b16d25c4b60f4ca214e323790a9c", + "0x82f920aed42eb630281919b9c1fa4acc02b05ef34020cad3583a29375bdaee167a47ca3366ef065cd8e658301942dbfd", + "0x8415ef32a93820618abb91329224bc46d478ee8749ef42e372ae4ea29b6c05a65d5ef515ffc7d720b2f41ccbc040f176", + "0xa0fbbb0113daceaa05478163fa835b070be5898dd9bbfa9abc582409a7b671c0e41a5070de4cb6dd2072888b11825acf", + "0xadfc99221d7f044b57ed40f4ef8a9e47e57265ef8eac654043cf5e777950af6fbdc2c2d5a5b916048fab1c19acd69dbb", + "0xb3d8e85fccf623fb3848e4886d580469bd41ec0533975298bfbedc7a1a9b4e554714991ec4238d8ff976a83cab6383b7", + "0x8b09702f3789ae1f7799ce58a0ffc2327b3ebf2b56cd870f2be66c0d6781cc1f34c2d721d0de63e0fe9db85bee842fbe", + "0xa935864851b73676cb49f509a198caab467e5dfe4358e7088d2a76e9b8c13e5d20b01eb7c0cb9e51ee98c90cfc393c71", + "0xb5035d76a5a8251bcb18f33968b077d43403c69492b809eaa3e202eef174a5649aee30f701ef0be050ba5026957093ab", + "0xb1cedb563cfb09713009b2263975a56abb9932b8cdebf10f7836c5c34785149e9875ff590fe1414ad2d21da977b7ba26", + "0x98a718c23d44b24ac295b328d91ab7a40b23ffbccaa90bc5888efbd32b6a95c530bf5e999ccbd4f1c85263104f336ce9", + "0x8d9d2ee952d5b135eac2f06f0478faaac175f23cb3789144f3a490f2ed34c885ae4d8ad7ed48db85cc6c2bd70b38c6c2", + "0x8155763582ff6c68d7071ba842b6543361cd5f65b7c70d5bb838da2dab2c02f3363e2324307e7d2149b12700d96bde38", + "0xb18b277334ef7f24706b7d48fb764a487bc4e21fcbfb01627b7524e9a5d3253be99d84c417084fea769b550b3ecb4574", + "0xb80db9d83cb1ae861a3f61197a1f14b6c5004a2b3d031fb207adda94d725f3e265535ed7b69b9c801f2e95e1d64c1901", + "0x82cb673ac9c0c124fc546c59505fe4fdbc05a1fece0fa579f6a6df96f74bfa877ad82b6fa768cb678ff04ae4cec58d1e", + "0xb2e190b785a4a882939489b86d0a06cb637b7be8b14204645bdd9d6c37626e8623e35e1e4eab5c8fdec0f8349ede8918", + "0xa82237c64f15d306365be19085e1c725cd148702fb66658c7974b02051b685715fb9e35fd4a596ec24d532df4711f82d", + "0xad6f7e3992518ba04b510b705fa6b28e3733e0000a5480e8a3c30fe71394de2bfa43333c69e750bdc3e7092b9e0f7ffe", + "0x8c0ee358f37c28f3b80cb9ad5487c342fab734886e31e30c667e616f3aba737a3a07bac4da552d8405ad8b00c03e09f0", + "0xb7851e0b88486b0a858a218f4307e0c0c8c314fc69e2b90cce8ba86d3fdb796b572e50eb4e82f83f73c7f048484b45ac", + "0xa7c35abc2e15723a9f395d16d2484b798d098be5414ddef083c8283b0c29823226fbc4727d9cccf96e33b27fc40e032a", + "0x8ec5ff2ba7c3ca8a2d18df81d46e93a3bc94ceca88134ea75cc8ec2ec4b1ba3d0de49dcd4d385083c648a63483377fdd", + "0x80ca7ee722c3253e7b534b42a8947e38741c542dee1d671b603a9a743f5ba2fa95f193ace46c01000ed20ea05ad0639b", + "0xac14edc2d803b28a169154364dac5360cf0926d911a615077a94858fb4cbbe31bae2f30a6a68b248cd8bed015e0f3b29", + "0xa4bdb63e91fa72995316d03cd117347cbefd14eb1b19a0adea1c9d39f49d82ca1ceeb2a4184187e1dade109d10b83090", + "0xac8f528e9e8fafde00e66a75d4bb68c99029456ae9b3b7cc76ea4816e89aca2b8b7d094db214bad1e87dd4e84d1c1a5e", + "0x8a8d090a01aff14383419735840fc5286e71a5feefb98c563b2d7ee593b518c3aef6654f10da8a77f40feb52e1d31fac", + "0xac4259562982b355fe5e57e1cef574a6a40a7144598c13a6bf07cdd8000bfda95b0b0b44f215e9dbc71be114a1857441", + "0xb53741dc30b11fdc6c9778555c1f714fde60890c191a0effe419fe2b6100228d07cd0738d0dd73057cfc7e340c75f0c4", + "0x80ff52fdfae53dd2410ea556ea6504696439919687d2dcce1e952d9d17b6e3699816ee623b0153bb0e0588e36b6f56b1", + "0xa92b34d785a71d10e6796ad07df788c6878717cef4f1f0623898370725006d46fa00a0a22a3934fc5cf323be85fc7767", + "0xac1cc08cd1a8fd6c946bbe14662b18e89725933a79965c663b73ae3cf5f5ab87e794559ed579564884e430e108385e18", + "0x88b8b2264d84106d38c321c3a4927b9b41cac172ae27f6292ea44cd9ce11d185d0061a59148e50474d4dad3c9e940476", + "0xb7ac9f257b4f676d69899a181b45f40358dcaa70fa2dad38870d52838aad9001f3a3145f6550fa2826018952431e4cd4", + "0xade67b3d1602ab0af6a256f25a65b621dded7a0adca65c526ab34c5ca3088a549b7ccf76c586993cef0d2d38af541617", + "0x8fcd8bdc44ab42a70c174682a1e8b929004834d4962a902de460eaf8649883c868cde1cd660d14d7d3ce589fe3aa83ab", + "0xb914f6ec60f1767a12fa34a4b400ce102564dac4c1c42f1497c7bb824bfb9000c9e23ed7cadaa16ad79d5ac906070710", + "0xabb1683b313612b583e87228384eddc3e2e7539e0aa26e825f5c27da222941b6a37ec47127cb0f11b6b8e0d02a6f66e9", + "0xb01efb31962345a2fc71b7c370e7d3117bb1d1e1a9b6984ce11bd83c898dc127fec2e821669deca7c74d406e4678a736", + "0x92439394c6c811d908b05c626f1afeda3a0f8c925747bedf66a4a5895ee76e7445a1982e99d8658117128df5866eb64e", + "0x956bfdcb00837be56d44f159bab9bcc2292295ec1ca7424615e3b163b5d14f7143e214609c0b65ab74a0dbddbed4d782", + "0x880b9a8dc9bf6499f1f71828e6c906e2ae59660c9aaa824a6f36116746406351b4e364b6fa26c45e9d90018555bc7dd4", + "0x83f4a0dcf523d414e023075ce0dde10161d65c0abdba522c811f0e446980cbc21eb0bb42737136bce30fcaae3c673b6a", + "0xabfc5593e02dff15161c4da67a806af3170bb2bbc65e3a0457b4bd994ecf5e001d02bdd417655c2b4433dec270a6273c", + "0x99c6d8bab7d937a4cb5c272c4bc3856a3cb8295cd77ec9e2fcc6a50e0545999cac4c413c3ca8e5408afdb60388c82ae9", + "0xb08f5d230713639ec98a7afcb2a25b9b2d1c48820447d28b6a3ef448aedc4b9a90b6c5ffc6613a70ff1766b51992074f", + "0x99d4b54e35dd3f844088155f114ef93507372ed32a6898b9954d5a6d0743e55d8e7de20d67671454d26561ed5e4fb05c", + "0xb7cad70deba1622c79f1ecfdb2612e380e9048fb6146760ba61cb62e98cef129d3944c5f442b15fc11c102fcc6e2adb4", + "0x95feea870c86525ed214e3e0ecca9f66c5e0babf6da8473e5cc5e2f305c26939f6afda0207bf5855b6a6c928815577ea", + "0xad6e77ec226053ab331f3f871d7fb770ae78227a85096d263bb42915299147a7a7b57a4f8f929765cfb323267b94865d", + "0x82339f53ab7344f8dad554fd0270c2aedb34f7b0c630f0a56ca9217c04f0e4a38781eec769354a44fa90f556b388ad01", + "0x837d4672d73588f19b872d81b7993e5e0628139f5685d0520b1b766d40e71b9d83a8d2bd65a03987eef89b3d5c254683", + "0xb3c27e19f579133f1ded8c066dbc3e4edaf449a1edcb1aaf215939d63a7f2b250b9b7afb62d4cd7cf37c28da81898a67", + "0x91f669f9db8fbc6d7a5ee92cb67c2fc1ccef6dde622efa455dd7535b11f506f4e309a8878b859d6605a3917f6d7d67e8", + "0x8332dc636222829a83501a8312904096c2984cc0c5dc077e067d8962bd87666226e3324a9e5057c1cbc3ba700a3b22f3", + "0x97e81e20bf33baa4412d6b81c5fbd406dccbe70973bd73e956d8ce85c99d2199daee5fa6e99fc6d40071b352b5044865", + "0xb716066fb9e470cca4546a401048c0e6c6408c8c9f4cd80aca6778d3f4121378e11cccf8a005845fcc8dea2e1b9f16df", + "0xa7b340eb603da43f2aa542dfad1ef3d3357f583c46040f2dab234c8246d7c55d6885f9f7a14f319e22355ad498c22a04", + "0x8281ea97a28ade9a0cdc73a077c72a92810b70912006611a00df8e7d2ee1036af73c0f062b367f3d4d75be4b9bf78aa4", + "0xa481ffa0813a4f2110c6ac535fb446282dce73c182eb99baf786ad42b804ef12df078b2f534e3cd8210973880bba6a63", + "0xb71a581ae08eda0437f9e9274c1f9431d6b357e4866e40d4c2470252f0888978497af823dbf464785479e5f35eb89aa8", + "0xa07c9010308bcfb0c97a1059d5213980000841ca0565697d45aa46e82fb36494e4940aa435ede417856d24f73d374757", + "0x8fc353fa8733947ba067ca2bf5e14a6c334e4ff30efdfa67829dc86f49424f4548e879b153e79dc75f1ec00afd6693c6", + "0xa663faca50e1fe5d00f62abb0b7828d6b761fde9f5a54f27c0b726d8d53281f83ac165b3d3db87f970913350a7dd07f2", + "0x970535269744905640d6ab238930dff375ea7efb2f391db324724166f0c436e7a3eab7ef6eb2e5d6724c58d588a4c592", + "0x800f33f5936498e16fd0f58210a5a5c104074039db7d9d5d92dc62cc00d796ea0a3a22e5d368fe269cedcf30bf6149fd", + "0xb4b921cc901a7775df7ae73e97cdd108d98c54534015a1469f0ca6b07989827e0d3f9bea2ec015fabe9d309054aef802", + "0x93295c8a7e5c0bd9decd99ee2d704d814cb6bd0061404fe00984a8afc337e78af11965a8560288529c2a722e8b54b488", + "0xaf43d382ff7951bea94f4540a3a2dbb53ed527e966d0dcd117d5212f36112976e1fa00a47bb9870d3841cb01621c5d7e", + "0xb4d106b21e4676556bedc6e7f5a7eb5c2ad0d5fe8004a1d968bc7806ba871e241d38892b1fa73e9648b23158802ab57b", + "0xa96cbe38f86165288a365efa796b0e2076ae9fa94bb6377cb80c7d5db9d376e9c18164a8a3667dddb3f5b847f52fd319", + "0xa0bde83e1f3e925561c481ceb58c7575027f9641e69f14242b886e7fbc532a2bc54aeeb94ca39bd7da3ac984bfe8cced", + "0x8211c4a70d08fe052246d3ccda60c9e9677910a93d9262d572606d99e273c1ade353eeeadf5b1e3c1ac3c4b9019d5f61", + "0x954ba6744e3f991580b6633e5d184550e44400f20f00149d899d97bc4b51b01d09bb4f82ad975cd55189320523fd60f6", + "0xb7e3f17ae79c2faaf5f3cbe0dc528c6aab0035eb3f38954820556bdf7c3546585fb9814717302c5f45fde7170748ff63", + "0x880446589f33ffe7ff5e105fa1c380d401d6c46e80526948fbf4edcb779753a594f3891461f52eeb3f5f2f6051c361b2", + "0xa26c06cf79c412d49f39e0e27e37c82c4cf0c8648638ee66a97d22d822e064a9a7cbb0b1ede46806ea0430639769cb88", + "0xa968341c5e4a3e6d2a2116222e3c58c2e558f5bb0a2877a27c69fdbd38dc3892f9ed7d7c114f557e52a351c73614fedb", + "0xae9b8bf4774ce3b84185be77723ec62b9a415e21cd60e86513c1500916c96d62519ee8cc061d81ac9db9709d6e191649", + "0x83a30c1ebc046c9a1ba911ecf6f147644f58f54e32357dc395388e6bab66d71fb9b691754b11bf414d43816af8058828", + "0xab5b804fcfb68b6439f311d0420005b083a84da15a8415cc4013898806e67c47698a9d594263fd9be42bf48efdfbe2fd", + "0xa41c18185f8111ddd551ecc8f6dcb87036cebb6eabbce7faba40c6c5c8af2ab59ef027c6fb2dc523eb0159335a1ab189", + "0xb24cd94b7c6e161e651107769d863fe5a3d7a847b9c60c7c803846bd782cec0bd54e6278a318ed23b90cd7ad25933fa2", + "0xa5ba23ead78d1678414d4e986b448e7a24b23a5c0f529ba604a51e4ee0f87baee450fd121b43a954be50bff6c0d7908a", + "0xb89c17de4809e722527832b90b810d9691b437f19db9cb88ca5cdb67bbc6946ec1d454dc0990b66093ebeb6eeb6896a6", + "0x914f436fe0ac7540129c3deb04d51bc61192ab5d0d16eda77ef70ecf8cab5f55a13492f54e8052f2f214186a113d8949", + "0x8e0b3d1dd756a9008894028d0443083c21e99de69b8d8f4e7eb3ca7fc52ad540355d4a1081774a6d51a093110f4bc838", + "0xa9c1730eb5c0a42deda9d9b39390661717479e29007f5f8499d0645b8b85bc0ff12cea2ac4328f6588a12126f56284ee", + "0xa2318a42c99f7613ac78cb110656c6e470cac6903a5bfdc1bb182af21e0f0f409bd39324a13e2790f0facba04459d3c0", + "0xa11ba34521434cb718f1b2015bbf451ba1a7c60e59b1620ea843835c7e75bb42b6ad29263cd3705f7f6f1e40a0ebdfe7", + "0x90705112b625973e1cb35e30f9e15e3c752b2e972231b4caf53518f44b4a40b8a6bd15c4af2adbce5dc194169b860cba", + "0x828035b0e70af8db1294379b4b70e56624e1138ef49f7be81d938e8b25aa5dcc03655e045a95a79e0143c23a77407004", + "0xa7abb1836282917d1eb9886c79b6a36d720612e3b823d9420a4a705e8add6c6bfff2f682e6f992a6af10ae2f71ca8828", + "0x81e97c7f980dbbe93df9efdd9c0a8172ba0f00378e9375c926b9e24758e8b827037ba67e06e994fa9d05942320353d71", + "0xafa640b2a7fb997cffc5db74a91dece901be4a36415786190dfd17a77ac837a2fb2d73e973b8e60582e71824c57104cc", + "0xae860a6850068f2b0e1e5a03afbd08b667f44c4f06e431f1f83269e754f37e18a764b00e100dcdbd1c1d18af9d6304a5", + "0x9443fd7e1263d5ab9baa8b1a3c893765da1dbed0bdf62ac9c886425ea9f05876df1920889b707a2cf248e7a029883588", + "0xacb38feff88de8db3477ea9ae3b33e0c5715cfc91cc71926dce26f4f290dc4f437461a186cf1bdcfcd6d121e087bba33", + "0x942882666a9f49ac24d9099facbf1e65484ee76cfdd2eacef25e0f30260654a7b5c0cb7dc37aa1601980877f945c51dc", + "0xab2c9035b2ee9c5e57d8de70b24329cfbd247324309eb30ac78c404ced268dbe2aaea8d417300c90d87924a48702b793", + "0x80aedcea9c5a9911731ebb444500eb95b519e2d4650c1d465afc61f4997879d60750ae3fe049e54654a06eaa2db7d8c2", + "0xa63e1ba5fac918c8bc0f4364b5fc8d26214deee825aa1bff111e03c0ed43baad47e8bae154ad580b851a0f66be85c88e", + "0xaea7f5f8c387c21cf671246803cd5baac61cd6359848ad4fd685b1350ed6298a129ed74dace279fe7846001bd6577dfb", + "0x906ad36bbec72813b368bd2b79c1c9624966dcbe94ca9dbacc297d0d8af86edbd80cd702ed04f0adebb913a6a7bc1a62", + "0xa46201c20560ef2ded1ed3047fc196bfaef445c4a716890d9235f3a06d6993a8ab29e816eba54c6a2e2590dc8dd61216", + "0xb37eb2c0d765b044ed2fa2923160a19e11509e764025e43a62b4ccbe38e534ab59e68c2cc92cc5aff9d97154b8210c50", + "0x91f93b1404a4bfd3fc8ea019d76230637ceee315da0faf366c712c3ba19088cd3efa2dd30172dcdac11e636f8473a26d", + "0xb6b905abc4a795bf95d055ea09c3f9d0a8a9ba0014e288492a3751d2aef60cd3b7846e1ca8366635a94988b2e197191f", + "0x847529bf842d7623150a3bb91fc4ccbdc66010bf008179a32359f98bd007330bbfabfdc487f4b98691ad65680af67a8e", + "0xb3d37a8098d02b5ee69ed060527f3d924c727016fd92b21d6a52fb1c1ca18c7eaf0caf8144e9e6bb5b6a039ca85cb1e8", + "0x98cef893dbcec865cceae01138613de146d563f13853ae34bed5f142da716673c105ecbf4f2aa7d187bdee20702d8582", + "0x97f60078d18928c4d7dee1ab244b2b7540928e20cf7ccbbf6684148611afdd9cce60dbf412c1fc544ab8c356fda8fe11", + "0x872a6758004e6c87c3788c5c11bcc74db78f076efaeb75127f0baec28febd02528c65b227b7619fb8c29cc92d7c8e799", + "0x8d72cf1191629440d7af8daf3b76b6b1bcdaa8d6ddcde52603dc8b092c2ac78d6e24bec32e1223eeda15dd17ba2c26d5", + "0x89dcc8c10be08277a1e394de336bb1b135bcc5131dee5eece80973ef364a305235936a3b6dc40f2eeec2aaf227a86376", + "0x972c4ee3b4b3b028ab683415bdfecb2454d326a19d274f499e48bb2cfd55165b928bdfa7f97c4fb6d27082cb88b73dd5", + "0xab5438a8af3acf2eb75bea0ae71d8aeae363d6644c54e3b020082c80809ef86faf5811808adc8240c7693515ed8bf199", + "0xb594133dc9f71f72e448796316ff3ce2f8a03c21ef9c54e551d23723d5f197f7fb0bf1c33e9cb3f51188db7dca51bf49", + "0xaee981b45d570a666d0d0b2c7aeaca3cc22d4873812b4424d1f91144142393fd64c49401dfb970c7d5ae91233676cacd", + "0x8f978d21de1e264178f88cad7213463a5efd139c30dfce81a7eecb46942870a3c1971f6e6e6a50e0a8b20c379ac084e6", + "0x9153701c8b82ab43fa4635cf677789c9c9911efcf23250bd393301c0be51f14fd0acc4e467ec9682acc89085b94641d7", + "0x8681989a1be217d77cc8e012c95128557de70b362442e7f1e6162bd52ec6e4ebb0ab28f9ad3f67c1d35ff00216ceeb74", + "0x8e85421256fc71a82d35de9645a6da9cbe4dabb9670758c4eafbcf42b26fb99866bb2b4c374601749738ad34e51dba6a", + "0x976774296281bbe1e8dabaee7453613d0a615cc6abaeffd8e15ca4484b5a743e298522b2dfbdcaa697e1eea2b2bff736", + "0xa585501faf955b6acfb328d801cfec5b59be8ff2fe46ef0bd73b86ba4c19c1dbfcc1df844d61a5acc64bb5e8a68f6cc5", + "0xa776217e5073714b36bd2ff0621246a48799eb5ae3ca438d1efff6f9f9beb13779bc18ae5ddb77c838732e8925018118", + "0x992d726bd4889f4e7565bcdc31c7b4a58ba44da5f361e3b46e0a67a6e4f00c25e3503c94e7b2bece737d7efd47ff9beb", + "0xb277f124d5dd8dd669ef1f6840276c0bb0b60379ca3a0aaf00ca337c40f478d511b1a73e73df6c3b600e6bfaf37a8fa9", + "0xb037e78617c235e6528e535bf13bf5e82c70588d1d0bd08de754d089bd47a4fdcfee79b5666b95698cd98c0e32164afb", + "0xaefef9e398e0edb60615713d7c1334005b21844d3f1401903e09af2db20d7b342b8d80796fccab583c8607c533c9b735", + "0xaad20eec7cf4f0b518007ec1df7dbf4935f6f9ecb36a11d148dbf9e5281aab43feebcc8ce9001374be40776c5ffde825", + "0xa4ebd6018e004ac8b5d022cfbb7c5b3833456faff4f198a3d9dbbd077c8752087bda1ea060466fde4a5f31cb8a50a7b0", + "0xa56ebb8ac9901915400234c1c6c8502905765a7224de56b084f9b0a3468a065e78b4daea27d9887b4f44a72fa61a15fa", + "0xb0269890863c63203dd4da3a08a1bf06621cca212acb49799bfc48be7e41c951d807f85dd4171ed57c372914dbd2ffee", + "0xae11fc0f5fd5ba488104bfc07fed50799f51ceab4768afdab300325e9a913b1f257fea067d357e54950c8d08af5ecf59", + "0xaefce65396c61e835ffa38857df426f64508de6e93f966cc46b54dcbc5e2bfd72df927b00489fc4460414569ce99e610", + "0xa5a1fed75677dc956c000b9135c4b6138e0cff53770399ffbc3b12ff0c1677ace264aef2058aea535ee1a7195afb034d", + "0x8071def0890d01f0d10dab3afb13125f0194e79608b9ff129572b5daffb49cde5bf6d9f24da3f84483612aaac3cb8eb1", + "0xb5e5bb8c0be22349ea51e249cf2159189fb9aee615dd62c5f67cc9f43745676e703abfa6561df4f5f1d79b86c459b11c", + "0x978dfc57cf0d3538ef336a25ca7a2cf373f84b71bc06d1c74907464e3e816d834087ee126bbbbd5090a09ed063f87a46", + "0xa2ff4b59b3e7fef169835e67d47218eff5368aed3e6e2f1cacd29a5efe6c1c2e7e1839d87759bad8ad1871b39c481bf3", + "0x96de49b44bcd2f5ac3d07d6f5270af081776d8631fefbaf9fec6771e13d40a4e5158767067297029bd38e8c6847971b6", + "0x8f2f820e8e3645f2ab9a27b3c23b5f656b681264d08e298ec546c5aaf51119893e0dc8e04d6f64fef48d3cece89692f0", + "0x8de2eeac7dd4b53119d02f0ec99f127cbd8f6a57120d94a9a554c04467fa74ecbdfebbb111d9f15cdc1be2be8c2396db", + "0xb6616f68b00ea0fb78a25ecd51d3018b9ef13664a7da42663d1bfd6fe71fab615624af863f3b41e625b36a607bb42dc4", + "0xabab5be2ab033afd6d110a340c658fb512bb53368886d8a5ea29e3c916a6b1bc46decb2cd0f508b5667f9dd88033ef7d", + "0x8872d0cb09df44c2a75895d46588316a4c9c743080f7a03a384bf4d4be80d341f8dcf0e208383bf3587a3509f3324fe5", + "0xa3f57fda2e8c06fa7ce9de223f5ff56d53ce9fbc48486d88d2845e7011dc038b6f2f270dcfd46ef5222ae9a1557070f8", + "0xa82c4e46f0d1962cb48d6c3d8ed3976c4fd4c174d119470479d9770619a45e6e16e30693b2804a82b516ccdd400508c5", + "0xb53188c6b2907abcfe47fab98f23ac602525e05a5ac6b4421c437025819c80529e9d2d63f8a3c10cb9dced196e572506", + "0x951934cad4c2772aa0ffdfc4f12a55f490824e104f669e4dffc70d9c14239570c87eb998dbb2a6d423bdfe1ab50f4377", + "0xa276bddb27d86e1e70ebb96103a239ae4848ad20c4c5b7de85f480c3f293c934ebe35792361d9767de4333ac6de11643", + "0xb9c8eccc03d7270779a87dd7c52a42c7bd632b9bdf94274b1dc864bc7a59e13eb30870ab740066040aff0beeefe14d2a", + "0x8e0908e4d15aaa582dc028e015c4b2bd97c82b8086737cdd1f2820641e65d88166d1fc763bc483f8fb4643339182473a", + "0x810c6c46945ad5b4f699c51130bf204e47c62066fbe54fd099c3567ca79aa8aa8b04dc5321c09e03df4bb7c9b93857ad", + "0x916d4b23adf202ccfaea7dd124d28573c73b39ebd74bf4dfe32a366f9dd48f4160b8cb0e687e7dca887c4b4f19570cb8", + "0xb1b8fff52dbbd5b9bc6915ba20f3185fa8e23fe52c026a41cdedea5301dfcf6c79c4fe1058f3abf280a00c7b2cbb20a0", + "0x95f9623510e12ddc6f4ae59d06448f496cc911c99a4d5f5c6ff7e434b807fcd4b35ec1ec976a40208ee1a505a892e38d", + "0xac7217596d42d40380fddef22e83db9e6d6b2d0d2e912f868d7fc07bacfb83e8e6f01af544e8f450d31db014fb094c9a", + "0xb10855b8ff1a81ac32d81773ce8a6391169902290af0637038b58ab59fc84e3403d515ba7c99e26b7382c2e2d0edcedc", + "0x89eebe9789a333f5db0aa9e8604798b15a934ff45e19699c2e7fdb46b6863ce02defcef9f6dbd0cb799ffe2b669428c8", + "0xb9cc540b405c5ec78a2d8fc17ee4a08690e347cc1d860885205bc19cba09e62f25b94ffc2cab1f638c87caf217f7b6e3", + "0xb16d06b120906f085cb183a96a2b635334afda4272ac650259f23059407fdcc8b83e91f2521223f79769ba45428c04bb", + "0x83e0a2d9d9f6654d916a822ab1725d58a10efd64e889a17f44860db4d2c77ec1bdde7d0ec8deabc12f8ffa5af879d4e5", + "0x98cef31d7ee167d9c4248e29402ea8d5546288d1b7ca54a5370e80a9ce371bc4aa3f5c7a24c2e4805d8c99af059b4156", + "0x8fd55a0dc38b65c2b0b45c9127c14b9396db4898f14e1559e428a2951cb5076bff9e3f202a83236f15c1d2530539e5ad", + "0xb3252594c3060118acb12eb91d002a74c068c0b8f9bd735a9ecb082f787c7e046dd6e40ddf4b3ba56bf89f223bb5d76b", + "0xa88446262600f605fc4f067dca855ebc56990a9ea050c708961e486fe685707d9e9ca734068b92778a144c0f3c23b4bf", + "0x97beed96ba821515996045a40f17ad46f8f4d927cd9a2c7ce134a60d19ec4a5819a19aab1bb0df886d9cafcff872bcea", + "0x98ce98dc7908161ceefa0ac132b63c860ec2e53f7ba28e66c6c5e45c5945e459797c65668e58c0a5b8a26811f17c3f41", + "0xb0419cef96d4d44fff0338132d53d2c03e7e9b4618dc2c6b9f4475368e21700fc08b844a2f140158fff81f56aef83b7e", + "0xae1eba4a4a715f6d077e90e9efb59852b7025adced47fd9f705c2745e6734f2fd2f2f86f07ce24695a06e24e63f00b03", + "0x86db2fd15dd3cef1e504fb057136f0405758f6fcadc391e6f64b3080f92bfbd4537a0d8f59cd1a0e913b2b188093feb6", + "0xb418cff26800f8793b083a879c8b1823285f7a3cac6fa34cf48ac5355f04f6ba74255eaf436739c4d26d0d80d2607129", + "0x8eda3c25b5699569c03b85bc585acf25bc3f9539e9dc3e8707b34520ae5ac53920f45528f0870d93f84647cae36b6aeb", + "0xa2622af11642fb6cd60cddcd4c242cf13045f4ce20539d11727e8942b4f9a7fd1ea2192e83596a35c096fec3658c0c2a", + "0x80735f92d09dc0af19f593ea118bf52146143c1d2a7343f6e2ab95e00debfbd329d4e887f7421e4a361d815dc1a27973", + "0xa7eff30a31db635e239c8632f7f84263c9a9d82511422f49077823aeb124e6ee3c995ceb846902fcd2cff0f5f219db51", + "0x99129aedaac32b3ec18d689a2589e35fc9715fb3f1a72d28a09ad95e39a68ea939ec5721c501a9e35c60cecb3f4379df", + "0xb9995d65636ce1e70967a8ffdf45e50eb264eb64f15ee887781455c5472459cbb309ab58b1645bd6e8f2bd29e69d81b0", + "0xb8049f4c3ddc22405880bf55b5d5d94a6dbb071485f25a49a6457db0446663f8d4fabcf14106b9cabb1b3222d8786773", + "0xb581027c7d9bf7b97f6eb085934b9caa43a46368cc6740139e33e4cb2c94683411710a52d5933a27c9d12a43e75163ae", + "0xb5dfce672e670158c259f36fa549aaacb0699da2f13702c81f5a93afb00361f9ca22d02dcebeaceaee6813a3c9bf7aa5", + "0xb8184f3eb809be1986530dffd7464d84750df02196274955769a0afa02b65e87686d915ecdc7e75a0a76be8b7ad8d064", + "0xb7ab837f300f4aa2ebd2d770f7a36dedaaa68e1d601eb36a28fada4dc73dbd55e7f31c88ab2835aeb57ff113a14c5f32", + "0xa72013c811ca674c3e909064777df1484190fffb0643b6b1435892f5dd0f1d09579189fe00c862bcd18d03309b958b72", + "0x87fb528e03f1b6a000141f4a6ee24a9738d9d2efa795cc262203fec10d76adcd0f89968a46fdebac99af8d048300b8ee", + "0xb2a1ca5d5d16c7addb73341ebed1f8e832250c2f8e03915a417064750d7deec3289e646c06a09c6a3ae40ea2817636a4", + "0xa90cba4d0928da2a5d8c6935790e1a1f026073632a4c1460fe686d06c3f2933661c2b3c49bb0bbeef386f2bcc4d08485", + "0xa5b684d544500be25136b0b5b95d9f363103a6d08cf49f4934d6c96d43720a79cdffe66698de0ffe5b02bb3c2e30286f", + "0xb246952dcdc38a500e64ccf4f312bc7c690d33a3a951fde5f839f6eec77ac78147f1fcf26ff7b990e8868f5cefe1c4eb", + "0x981ed33458e8ead67d4adeb884153bb0fee0ad98ebd9010ee706ea1da7975c290f82c492cf16fb42d1b739632e66e50e", + "0x88bdec223786c894fbd8f964ab2c92c5ad7fa7ed2b97a6bf31423a6ad5bbb5a946ae3cebccce8cc97af9e788d03f547b", + "0xae852b074e5716e3190593e11fb17f1135d7a5d888986d2be53973fa14c1d4a9887381e648a10a4725291ff062c9d88b", + "0xb87050f914c4f09e2dfef845ace5a06504b6fdb815f685921710c7e82a9fac11f864e3e6023ed5807256d6269271d051", + "0x8cbd11617ab819680cfa68e70e205f3ffecf6e469d88dbdb1d9b0c9c7c38746dd6e64bd526306a8ab59cb7e66841a757", + "0xa1c51cbc1a91618b1ede5cdd77fce26b04971081e5cbf83be20c22b9b30cc9197b9bfd5998fd9ade9b665c8218afe94c", + "0xb5cdb2091d114847dc14a4c922bfe944021549df2d75cfc08ccacc2d740726e90e20a0bc2bb73303e9f0bbb5192fb982", + "0x8e60327955c5de97f56838cdebd24c2ed4021d9e3d74ab9eefd4543a286c1be82a1e8455f8cfc0a17f03358c4648683b", + "0x87f9c1c0987493c631279112fbc79c5f5d7dbf46544119492785f444d063fcb0da4f2d1129735ab77663a9000d9e18ee", + "0xa970df3d50c4ef3d76d53dd2b887e9274fdedced7a83560eb1950fed2075879d9fe1d5af811f04ec92d557a0be0380f7", + "0x95a69bf4092567f5b55a401329d5a08220ae65825f05d56043974fb7b7090372e941a85e2d197c46c9165031b3bd36fd", + "0x8e62c98171e54ff549ccac5d6d381291d0861439dd24e584d356a862d22942e0ff17cdc0d1faab07e496374a547ee812", + "0xab62d0eed8422a3172269de0e325eae9294914fa67f1ed8e5d0609afa2991a26b1e1b9a04ccda8436d04ec085957b110", + "0xa3292bc88e2a9dec7b55ae4c27a3a8ea46a7b2dfe3a817675eb3712f95264c08668703771b65afcdf6d305e396d5f005", + "0xafbaf9cc19adf63a0716cb868a970a372d7a1e24a4c78718a114ced412a12fda6fdf42f701ca1492a8f8c1ef0466f7a3", + "0xb41a5f064f9d900d1534a68c74796927e4018e23f949d86eb76dd5b26e5b686115d63d858a49b545924b3941bcec2341", + "0xb4e1ef520119f9a238fc4988ab2f1266606f53079744b92c1039541aee78b67ac570d7839fc9b2331244d734ad4637ed", + "0xb0ce754a33a506174d5feaff4e9a79295c743b2a122c8a1788c1427482585b398a750b7bd93cc53c38bd3e557caed172", + "0x9842cd13ee9490d9ca7ddc83d1f7d79495afb7301d1f51f4b007dd2b2eaf15abbff18666126adc25df5ae26b98a80f41", + "0xa976af142268d20a248c4b71304a878efec29b5022199cfc88bf82c081f55d06a89f178606d50bd3f8576f0c5c01a6ad", + "0x985ac6f315ab1d2db1b4f2b107eb1652810e63e36b8c14e8852f072d2c8b14922f20d1374a57d75cec62db0d050a0c7c", + "0x8c1be9e8317fdf847a8131ac14cedda922bbfbe15cf95537493c4e7eccc7f2f1a56ddd1a8832e6300734d6019d8b128b", + "0xb55d129c88d252556fe688f84982becce253736ef3b1fb88328e41300ed0713465c8bd15918386844c725fe7a94e8364", + "0xa96384d2d81cf6a79614c7fd6bb68fec6e74064435a1a79dd8b1533e9c7e578da5ecf03e979969d983da893f42adcd84", + "0x8c2b3c06b7249ef5ecedeb4f2c65c0925cda8877bb4b672afb7a15bb5a7b5818748d6b022c6ab8fe9c5a1499e2037c69", + "0x91c8b2b8b204897741124a37f85ddc45c3ef94ceb5dff681b13771e712f2ba5ac95cb1bd2d3e94a84625d384b51b099b", + "0x8bf852945910e9a773120c5ad975f080c07c8fa37c2158e1138162a82983211da70f27e22876741d58c20a6c9dd770da", + "0xb9e907d9176a0fcba87a2797651765c814df756bbd1d0a86a9b7b06d9d886d1908d4e74ab27d618129dcde81e7d969d1", + "0xac4d3b156db2570c349e21f07fd17df935872f9687842035b533c6e4773ad5752f4ba8f9ea4501953f6b8c4232a4562d", + "0xad91c4a7ea0a314d7d1ed7a69a74adf6ad810586c1bf907ae9878ee5f6528437c048c6ae785cc255707ea3e58a4b452b", + "0x8013b76604bda0c429e37006b01750999414100d0ff59ff5ab7b233399adaacb34906ee65054abb94db80fc92ac6d2e8", + "0xb26a2a660af34a4b9b8910463d0dd439a3dc563494f5ec280dd5eec0b14b0e9426a0422f3c75370201299d394c4d90ad", + "0x8e1c7ea11dd513fb8527fa99b899444bf89a1188089d3bb65e3eb87025de9a48e8b4a3068a955fe752f2416de282ca20", + "0xb6cbdbf2b143330db09841aa0e7d22d32772ee62006e7cee13d8c4ac911ff4a59a9dba3d84bc46ace1760353d847bbd3", + "0xb8f5aa3ee213a44c41f63c11f685e754997cac37b27e91d07bcb69947344d94f3b86284b3b1655e168befc01c880d550", + "0x89f93b37bda703494263b10768118ce998ac1f395d422c0ae840e47c6d649a3ec59b404c164a1ad5ed14ccc2408fc662", + "0x97255607a1aaae89530a3bdbb7f2b7ba3fb9d5dc93509991021152dde08a638bb3152503cf0c896c9c19d61f8eea36d7", + "0x909c7ecafb798e6aa45867976f59cdc9d219aca6fd0881f82f296a83a2a3cc5ed47f08794e6e3009f8847f16345f5f4b", + "0x9560fbc2c531571eee5b7389855117644f156ddb00b23a7c2189205d4cc613ec83952b96e941cc1e725c2b574c46ee9c", + "0xaaa69f68b6086bd369fd92355f3a0bc632c1b1b4284529c18a7cd4d71d827291bc997ce74bc92dcd6900419be68efb37", + "0xaf9ab7e6a27e61a99f37b89fc816974ff916b6a24ec3aa31d76579204bdd5ff01a2eea26e76188976c033db4af167db5", + "0xb026dc8850af970d2ffd300dce6ae07db0ca2d21978e4f3a6797b6e3e81f1d9680465080a983c31d473a77ffb62acb5c", + "0x8f82f92ca992ac352ed1e8fe31d24f8090ce6a7f02d6086720422b9bab20f3e3c38a5f63c7fdb193e30d63f08e53c900", + "0x8b896a2ae84c66109c8501cf6070c4da65c43ca8ef9b6b06fc85b6cd92bf2e5397d492796c528c7b2cf29ba93341a87b", + "0x961bf4c0b8068c8406a864595e156004d427138e06b390519cef53af8eb00c748bdfdd480521c6aa0d53a78e8f806217", + "0xa6fa456250d20c6842dde55d3884eaecfe8a39f546cc5e4a77f57907192e849a956a33a81369b0f2633c55bd6608eb63", + "0xb1d1d2f3e3e058ee97c9b6246cf073236438ed5e782bb21c68cd0d77b44f29745dc24d01edbce4437d93071b6fa6e0a4", + "0x81a0bec80ecd1b1e72256ed5be7de8deb11046ead7a96e1d150573f4d896e642b4af095735343f6831bb6b7f4037cfca", + "0xb48d8e15fa8e0b46937637de3c727157f8073eb8a9a04bf127e68977758385a791da2e9c69fedb89b334fc638ece78d3", + "0xafdee0774369653bf371b8820e285e1b48b40745a44d22cf2098b630b8ac95796a74f79337cb97fc60b6d6b903a61321", + "0x8fcd9ff2991902149db29cd4674d60387d4f65397891fbf91b7699a42f579f6b0afdaccec70e5e82d1abd81de859183a", + "0x8af5c73367a8439b2e3e5f1b65e00ebef2eda640bfba2eae48582cdfb244e1b1cc540bc0ef72f9e24399affce1c3e222", + "0xb58cad4da101363bb8d6e8cd0ec7c078f7719462856d7ea573e2bf95e00cc23020031901bd1f2112ffb90d847241e5a1", + "0xa671f7fe2ad81e9e0d5e3260a9dd7808125dcebd970877b000bdaa3207ca45ae1e5458d5ab7bd69b2adfca8b6abd88d0", + "0xa8411cde9eefe73fbceec3e5e3628b159ca4e4c19385ab50b8d7a482f4258f405c47051a89f11dbedb2b15e84d8bfcc9", + "0xb5dd09d5ebb26e341b6df80e836c6de2305ce4941238e3e96da549857ec314b1658f8b03ef069633625b6e4bc13b531c", + "0x81bc9bc924039fcca8892b40aa9fe8f5d6f305343f6054e36647d5f14cad3e4d754dd6ce9ded67ae65825adb4e16df31", + "0x935ec74c2dba94b1c5ef2060c31bb5c1426965f68d9f4125cdd891f20495da9d5dca513f65bf3e8c599f1562e81a0c1b", + "0xb9581e11f361097620130e753d134cce6d40ddc7c516388fe4c881fceadf738f314d241dc14d4f87be8ff0481e898c4b", + "0xb7be50ea49e09d10cbcf21b6f717e0cdca582d57935d72d17e62cdd7bf2071e5d5c91ad7bea79476537e515f0d2fa5af", + "0xab467b7fd32a795411e991417be57af8b62ca199983efc1f744799136ae5339173111465e91083dbce60e77f9f2c0fc6", + "0xb99afb338f747ae89e7cebf069612e22f9704f247d66548d305aacdfae395609a57d4d5405ff0f1eb1045dca4c3827ce", + "0x99a5e52374e1c55f65e44951f68cc3d607157e60d52cd088125a81bc60f2009d1b894eff8e1efb175509aa4b57af7276", + "0x87e3323cf6f11b595ed745a9475a6d99d11333043d512bb61d5f9d8c3f0cb6957aa8c3f041688f63ac13a51df29fa061", + "0x96a5f9ed28056138439eedba186b754f5f7693c09422f42ef82a315b7413b418c4971112f4261e1b9793ec9066c3641c", + "0xb9b5fd36d2d861d40b947c3c879a42fff24b9ee346163e544ce6c3301d0003cdb47218644fd5f1f7f0d6f19bf647ceed", + "0xa8899296b58e5d56d7da438ea48bd76310364ffe666d698c86f20683343663d742a0b3f8c1255e33f1d424cbf61bf1e6", + "0xac4be82ca78df2a367f13c8bd1cb73a28015853f2745e025626c325a10b778cf4bd9942439e35015cb38504bc02993c8", + "0xae5d6b99ef56cebd5e25a9c002e9e80c1d3e8e5fb5dcefc8ea7b7798c7e09b02147da2ba14e42e2b6db2b2a6a738f598", + "0x8c94abefc71d245b0bf04f34085da0a9b8d4d798ee7441596c5166ac353425175dfcab0f76bdabab8f0ef5a2b453255d", + "0x960ab6939b1185806e9f985c9381206c7032ea8a7a99eae5a66f276ad5cf450e654a6f1e956a2a63f33d6f715064d051", + "0xa4c7c7d0fce514db07bae5582f5e4f7a05d79f7605b33fe2a1ae980bc388b31c056438616bc8391ddc7dd5f98810c74e", + "0xad5df00f96ee6e9e1ee65b562d6311c38bc2a0a25aa9ee36f39766a2a03141e95285dd2850a598385f45b9935d63b78c", + "0xb051de656e37ccdf3844a6e095d3b42ea9c5a545e0dc2a5234e2016570375bff6b55ee0dff04ece5713ba8e85629a7da", + "0xac01fad1ac299567a22da6949a011f429bd9775de956dcdc247d5c186ec577fbc12a482ebff3a4ab18a8e35f3e2218c2", + "0x9654db9c6b5e58e0b68fc49718773d44129a0e77bfeee3fb56d27c282de6b75fe9c10f4f3b5d3374443a9fad45c400ce", + "0xa556631390e6cecc2ebe390e605e6fd754f1961e4bbc063c31c08812e0993eff5b5b7449b9732bfd3a22c87f9c528743", + "0xb41b7abb971e253dfec3aaec4443e875d73373c70c33e9ea19c1176f8cf1278c7716a76a4eeb641c142b2c6c1ace5db7", + "0x8bf37cbe29245c5e217a48140d7f0374f46596f2e82c1144ceb41c9801211869b96d7f1d0f7345233abcfead0309cc3e", + "0xa380a799b80f1309ba326f26ee46ba3081b12b5a1143f8289b2fa067aa3ba80c3690fcefded8534a80368799b71ee9c1", + "0x93dce0a2aee4d67efec1b284142d890d1e0d7abdbbfac82f90dcbaea94eef829645675cf17050af7b2e504a46d1bd288", + "0xb8e90f54bc57ff52b84fa3fc3c3047f379c5587ca18d9988c613a3bfe614fd5fc381106729bd62eda298faaf17b10210", + "0x8d8e4f508c284c52a6f907ec39950235c9443c5c6046762911f4818b98293d7d60a2c3f94c5cf60ccfeaeb8f283d8ce1", + "0xa513b66299ba5104ba633cd68121b9ec848e0c8c5252d04a0bdbab5e3bfe6ceac93ebb1ee6f0274920d84eae27df1520", + "0x80e2db8b919dd2ca33e833270738b1f437ae312b1c53a73106b6d12672a395fc3b941292fbb019d40e31b8e96bcb85c5", + "0xa4c28fba416985d47c947b0669cc22153ce887ec54535a18cf457622d03120b6aca71a45fd8704166f6f7a9ea2e9d608", + "0x850b05b9c7e168a83b0e0e77d16181a52d78aa96f4026c4420824cbd44dea9f27f3336b1736bd545bfdf548eb3f4276c", + "0x8efabbd63f3b9ae6111dceb1cffe45dd23f1500f87382816d4192161a77dd0776da2a4463d32da85b802ba7299fa726b", + "0x9426e75c6f7fb77072773a2ee03e1e3f1d90878fdb5d8c294265262f5c1cdd74a7aca339b46af8a5c43823dac7e57edd", + "0xa1c4d2ed335a3c92d867c5cb999b2b807dfb1d45e35b3960dfab19da43e2d1ca9a8748738380cefd137088d8b80d3006", + "0x987a7e22092931f39f05f5a6b38f419750370a71157d4443510b61fe07ac5aa31cd7f88ea04121947b1c0d0419d2a25f", + "0xae73cbce7cda7cd90404302388d41b49ed7d7f505a9a406f0317fccb29e32a5be61a6eb0951657f2d93abbb497be62ad", + "0xa1c7cb4056984c22a57ce76272428a50fd33f0f7a68c29c9438af05a87bec23d8de72062fb4829adafe597a278de0c01", + "0xb72c81a9a747a83a650b58ee01015a8882789983b67ac4f2fbedbbf47dbe30f04f686877d8f118b4634289866aecf9da", + "0x91ba1797d6913270ac1cb9c87d9d8440a651e294c45b2301ff8c40416e58126318f0f2d411b7d9c09c8e19f4da8ca0ef", + "0x864107657717124339cb2ec06cdfa75fb9c4a7ad5155cbdd03d155a7f9e9026e237d7cf5f4cbf07239e7bfbd79900957", + "0x87af853a334b8cdd10bf5f78753b27a0c9aac9f55db7570e2d9d42f13d0e2f8bfc4ca64b77b21e478f23385f17eb4f6d", + "0x8658227bb8733d6c7608d66a748caba761f28da4d95e70506dcfdc18300a559b4a84d11a9a048e82b292eb1b5d88bbf9", + "0xb078413570ead3243b9666c109a17678fe60dd1240caf01d1d344de09e346015cba7a40560b0d68b18df82a0a37ca529", + "0xaf6dd12875a891eea9d846aa660a207a527d08f5959976f6cb7585a98b1133f341f4ae29157f6ea8e0500fb6b49fb9c1", + "0xabc0fb42239fa531cf09f7288fb00f1d1587f2a86503593d481bb19b1159a6a9d6f4794565fe923a545d45b058d3a74b", + "0xb95966d42c59bb12029aef1da7fd50e9e8aa9ea287649ec3ba44247b185b485260af077e0d755f322ee4ecf8e2c8137b", + "0x8b1a2350f9bb0d6de377c00f0897081bfbaac5d47cac852a22dd8a427fd2e3029a1708f452e958a07236c7f35ddeb565", + "0xacaff21e9740b831fee42d80a9a80cffa6673e39f85b815b4f546f538dcd803320f90f4f25436796721c8a11f5a1b25e", + "0xa0dd42f019eedba19f4345553965508aa9d2eb1499a363056d95e27f7083c2343e74a0e7dfb101567250148ee1bec1d7", + "0xa08d1b1863e594bfcfa2e21ef4edee8535c8ee69490a4113787899ad8cf2f2ebbdea54de193ded85af82fde074ccd0fc", + "0x960912b621ff08e27781a4f9b80ef1014a4064fa3c96f534b67e5a094a5c11d9cadb2b69cd2011cdddb463f2936c7ff5", + "0xb3437f1e0872f6b9ec071a951f26120f27425789e00c1a8d3183879ed02e3b017406c051f32580b78b4d0f090474b42a", + "0xa90e6d1b11ebd1f1dec54d7b3fb336b9a53c821f295a592e147d5fd453d66e63295a96ce827c4ad64c37d4bc0df2c7e7", + "0xb357a785f3dc1f9bc1034da77033c0c64b29b78c7381ca59ef81e24ab14448d67dbf84756ea233b9e3539b5ed517d9c3", + "0x9360adb42210abb9d7644bb95532e1f461464446e94cb5047bf8ed5513398414130630866b6980b6afec5401e608f6f5", + "0x9145a7f8b2cf1bdd90b9a860051eacdb937189e8d68793e52bed202fa1e23a87db9c51a18f0bc050dfc3c600780099c3", + "0xae086e289e16608f02281bbde5a6fb2479e3151a2464b86ea737f8a43e15af4fe781312d0e5620a42a096cfbec885b0a", + "0x92b57fb14a0c567a16567f83e72b03b8b564ff6d830a5776014167cea06205579dd10715071097710dbf50b660b9143b", + "0x83e6a3f027163e635c2a1a397d2661a2d6c72c25082df129572082db29b1587c78dc3d2e5999112983a040ca46bc983c", + "0xb1667d022c8099dac5af4ce3b4ed6f524819240275725c7580a2386f067fdc9b3a49b74195cc6f661212fb07ff133463", + "0xaa2eb0c44df0a80047eec28a80440ed5f363e3d42908506bf8418bf04e9c17a5e9f550bec9c8ab8dc9979736ce325780", + "0xa2c1d257de1a55e4c10879eadd49af8950b0cf25121e8d7de30049360470aeecfbef263739262bf1f97020c6b025f9cd", + "0xaf29d1afc9f76417e4396c54300773fd283f1bc2cb00308da5e6b1deac7a48cb117c0e8c87b03076c7a1b8414d25dc97", + "0xa44d4f2186f5d728fdb224f10b496c9b57d96204325c452842423cbd29bbb2d07e98013a3880c7dfd63ede725d15953a", + "0xa30c45d1cdc68a5d5ab65b57d60c8b386be836c5bfda7e2f0347229b7807f6a97b632bf54ba3711066bcbd5e0831e5bb", + "0xa8c3c93d6a3526270ae47bc2628da82bbdb8b2c8e4d6a4cb5e9cf70b49999a963f3e856ff9db12cfd2575187bec668c7", + "0xa03566f1a99f5b82e8243678d0bb033441cb8a2f160c0c66dcebd0b6922a56f895a69b94a9c65f4adc9ed73420fd30dd", + "0xa4e3c839a6f4f4317e7bd06f25c5236e42fb0e54bb975f18f0240bdc214780049f0258dae24fba6301aad508ef9abf69", + "0xb7e0349d89616156679d06d1626f45dbc9683ad73ed91f0d92f8f82cb0ea2ae8d3ba3a752e73a39da70569d41e84015e", + "0x8c9ec5ff6be4b0d9337c5336b467c6d4f552af691bf083a23f1f9856e18b5a13852143dabf03869009febc443b2edbef", + "0xa12ff782575aca7b48844f0402a311bcb3e19514dd4d2ba5b39694c66846b22dc9ba25ea39c3c1bc325eda3afa1f00b1", + "0xb55bb586ebf5c9a3c83a04bae254e22547f37b9090151d96f5d8aa81be17bb38d2763a08cf0519a91878633ced6ce0f4", + "0xb3957203932032fe180ba9cb5347c2c1865a3094d03f6611148af4094fa6a8eae522f2651780d9bc49b41f5c36054eab", + "0xa0c865b498e30180c48fcab93342a50ca1cddd8759d6e0bb54e9f92e7b60c51c373f7ab1432aeb5e5c2d3ffcd79e8180", + "0x9503ffb3529c3415c07247211c2a4f35d8ecef98ce9f921e67438ffd538caa54520fc6d248a081f46221a0f1165011bb", + "0x906deaabf6e8dd0c24a4b22757b7681bf88268d9b4ff97f2844f9de825af511155d0bbc48dc4c03b87007be94f835d92", + "0x96c2a7f48990ecffccbefe128a28cd5b26c664b8dc9bbae16d857f7efc1b7711c734ba7d1476945d09ace569297ea96b", + "0xa37ea083b0a61f400b498ac5ba2360c22e40b688428ff4a02e3cc80206b61061bde037cd52d97eeca175394dc675e216", + "0x89b15c3af439769829ca930fa83c47afe070f6e2d7a7df88e5a4f3a2c0630f9d143bb3cc43ebf9bbc1b91be03d35ffda", + "0x8eca6996ba407886d3b9d2e4b1aae1983023dbb1c9ae47b6637458c73ffb7f422b0a893eb0b07fea2c5172ba335595b4", + "0x81df4d7f576930b2865af5ee1525718a09b65d9a013feafd19cad335e4e425485531807078b9564c8db3bad95d23bb0f", + "0xb6635aa3ca31c851a0283c0c6356235a5d8de9d1db9780e62087be32089c1c081bdc642f067224e88c14252efb960e3d", + "0xa0120e81025ba07848ef24ca9a94699db5274a8c85eb9c2f3b41a81f630d09d100127154ddc3270525961613a41ed81e", + "0xaaa8dd063f9f4f73f5a7c440671e1375ca8c224f8f869af736edcc435329487902249c68ef646fbf71c33a8bd1a04d9d", + "0xa36bfb14bbf3956c317e01fe744bd9c6c6f526a3881f6800592501ca1d9caba7f81b3b54f53b2ee1b13aa6de42ba06ec", + "0x819cd123fd793c0c9aba75aa96293268a4731c68c0a26a52561a695fc4acc409752de84ebd19494bae70849ce538138a", + "0xad4e50ce325477621b6eb4d453b087c3d7df6e3d019ab41239f2ad0615c6030aeaf85e0e020f3e6c89e46b8586b4a347", + "0xa4327072fbcf33be1e57ee4bd5db4c079c5ec11694a25fa2fb30932f8a2a35a63183b24d3ded7f6c8a8d0ad111586dbf", + "0x9454f17aa8fbdd2b15dfa6600ad305936a37b205eb554c915adc43aceb4dff6b0d1414e61584d5b15265f2ec0c85abea", + "0x80eed3725282c83dde575620bc0d86e50412df5dac3b3556d1e3bd9e7ef6f56dab202f4dfe4ce542babd49c1fa7dea5a", + "0xb90d1a07ff760daa23b7408b067c322f126023389beb7bf373f0c68b85ba0ea8a2c03e77e6d3339a01ed3ff8ba51f1f6", + "0x92789ad894995ba07f36a0814fc3289810136f9dbc6c70c57ea80db464772d760b57d5b059d4ed458f256af7603fa2c3", + "0x96a4ae1ca46d3b26029767e02fcf2f623d32c952712badf2a2af721226473f4875c40d5b14e66bf961a5a56aaced3aeb", + "0x8c5073f4846df9a0e057f52fdefe01a9b8c9ace91ef5ac253e823e165ae698e733eb936ad9cb04d2c54cd8570f328c4e", + "0xa9f36450b5ca66a20e52bc196620852a41f1f40262a2e12c278818b6071e6972c3cc6fdf83a9ccf586db6cc177173cae", + "0x8f101df23aa7e353ac1034c38adab8f20b8753aacabd10d70acb41d0fd0b1f34277546b30f64d0a861f448f112e38acf", + "0xb45b0779ef1ffbfa86d7e02e89bba0316c5ce60742b350296eff0d04246f1c8b1bf5bff68bc97792c85f1e5d4dcabacf", + "0xb7e89d015f6c7122a2f35f1c48b43eb0076ac4269158d52e38bf2a11de11cf2928175f717ee5c1bf543ea38945658558", + "0xade2a57ebd7600929dcdacc290168443437bc288371ef40580df515012350f3453b09aad8ae9e64bbc3fe6a3456f2c31", + "0x91c2f8de02bd8dfed1eeebc40a422d444e3459f9c33476b55de3e950d2c38d8463c4edf4d4f95347b0599a48cb2d47e5", + "0x8f6e77d9ceec539e0407a8d75d4e855e376838c0f886b36615a9c7715bce56a8669586f6d7cef75812d84b8be91380bd", + "0x87637da91b051ad92081e682e289bb904c51d95ee1a6ae2b8956982093a7bb4f8a66d91874265dc32229f9db5bd51ba0", + "0x94691811eb74f2970a95e9a2d64435952145f1d0caa76040f9811c9ea1ed7327750d57d6e8dd63c6378f336421d11093", + "0x884cff4ebea1bb48c0d651bcf0a710ebccab9062c96364aa64aa1275e9364a4c261e40a4b9f7e1e135572681a5a7a965", + "0x93f21d4b6b53cdc1dd41cb1b80ff73c0f1620db41c35aeccc059128704e9d1d7da5fd3240e7d075a2503273e7525664c", + "0xb9afe0a9b64dc43fa78f607cdcfe337ac952fccfde41c2e88abe3a8dbb36a51b3445d724908e552ba74bf67ea2cab56d", + "0x910280ba145bcb6a99d89d1526f10632206d2ca9e1a8596e5d181dfa37e5f407e1264b9c71c39530caa59894c10b371b", + "0xa5f583c9fbed59f99cf5e21b9a734de6d5685b9c33931325dd4b581bcf5aa4764c2a250924e7b6f7931dc5278bd17152", + "0xa87267f2ad292572a1cfc89308c96aec0d12e5f0fc2b4135ff8df7cf83bb1e71d619906d415db5841bbbeb173868ca82", + "0x899d7ff8d7f8d0daf62ec8d28adbfe4e7856582a23e62dee175e3bb9461f38bf8e4f73dffe10654a046573896f6de690", + "0xa8f3601e6787e788d46a9d7592dd4bdd8ea8b5136e3c897d79ce560e9511f6236e67a85a35c59295428c1f9c019a0841", + "0xb180a16448f085227a6f3e363b0dbcab285bf419d438a13be2cac1ac9f97973ff6b8aee38294f70a8d72bb4ff474577f", + "0x869038341a2f68ba85f5b2de58d2d794584a3c00a76ad0dda5aec31d4e3ee433be20c197b40618f89f7c8f1692ea3cc9", + "0x8366f825dabdf4f7714c5d089443d0de315198e23fb93c3ed063c4b8fca0727b05665c04beca145dc4c02f333e300c18", + "0x93291da32b501cdfa3624b39f6e38ed982c75c1209cd85630cf83288204032c0a90f013f1dfb4dcedee7aaf0fd95566a", + "0x96c95a1e73016fecc3483fc94dfaceea376ac700fd4804b24e9eda7135048e521daf96f8f63d5a1439950a64296d8124", + "0x866429fba47fb691a4c39460031a7e614096abbca3073e9246babd23075e8e5f6051e424e47d860296ac8ac646f8a283", + "0xb817f3d9985cf9f9657fa800ebd36a9622566697ce68f91c509d9ad7df8146532e24ad85c07f399908f87d1206c7642c", + "0x8761c3755cf5440775fe00081f79dbf59829f8d400adf7448188b97f756ad35658295649294ac9626c2569ab21a5df86", + "0xaad65ace72ef89783507c9feb5555275d70a421a95f306b7613c894bc24e978be809410b519e9314ac56fdae0c71d326", + "0x8ed16ed07d0e989061db5087d50cebfcd6983fd54be5062e333bfb8f6f609bf1b7b840c91ffe4b66fd674eeae2dd1558", + "0xaf3919bbc0df42b1e2e8f62e931701f7c35cfefe3ac3f1985ddb70212476112e8a19d51c673da931777ffa28944306f2", + "0x99a364d8819b5ea0f6d900167b60063f40f9afcf291ded7adaa2d0e46f344751cb312df1c2113bad8d84a028f680b41b", + "0x8d970bad8f95ced0b0323f4b7b087efd0624ce21834b3c9ed435dc0a394cc2c7ce58f1741c1a64265c81654eeb6801ee", + "0xa5f96a4d794f6f844b38f9b82ee15c2441cce293b6b2ba26b25643165236db05ffa918ebbe20aa89ed2a8ffc8df393fa", + "0x8ca69e0006f6a72e5abcc32c3961aeeebb8c0a76d877fdd8a093467485c19662b75f2ad8c750acc9cc12c8fcbfbe9b0c", + "0xb5378b855f6ed3eec19546cc21c947dd12e98783164d95a95d3cac36c89a840bcb9f7c99b191fa7730ec28d57e7326dc", + "0x884e50d5e20bebca96dda539daeb0e15edaac7fc88bca254a7239f30aaec47a64f29b69fb2d90041b82f8ad8e3f13d3c", + "0xabcce1f6149037ac8d27497831acb867cd5e05f637b7579736ba5c384b8145f127c56b82b1876881b782b94a84d32d04", + "0x8747985d53fac369c4a23224d50bdc556c00f406e7ab3e38427aec317ae7c0feee5b48b9386c5764de883cf296ed1daa", + "0xa153c77887f271316d5a7185fe0d2bb7359cad86ba80b03434bee8f21b3a5e52263d28cb9d3d2e6d5b7443196e03cf80", + "0xa77b16b2b7b6e999144af6c919e0a74b9a6ff70de41a133f7f820befc1261bf261142717133dd4a99e168a5cca4791e5", + "0xb89beb83489db9fb62fa32d1a8ecb66fe9ed41d318820d13c3e07e1c97802dfd7b05d34652a478a1deb3b17b4243a499", + "0xa80200902da696d0d3974ab29676f0eb67d15166b173fd63b247a17cc49f56b6ffa28d9690841ed4865229248650601f", + "0x8210103eccfd1f4be55e33991a831c50260bbabc1f311564fc1c52c3b2755d3e4a11ad69cd95e398dffdb9a0f5b77df0", + "0x9958745d00d8f29d05d97875746d863007b1c05d3ae920794e6c65adb47ec208734fdaed1b49982c4f4cdd1d3043c369", + "0x94a4f28dc7a9d2dd01ebc2f3ed11a5bb01a2095e7c772d2753c022d991da7b2e4c80c2170209bcc4771d68ef8cf007c0", + "0xa6b5c5543ae3de57e074fac82221590a8d771e93e22fffc2029b44e8a1c2c8c9cb0362416de54d00fd5420e5b1375eb3", + "0x875e801265871509c71dce38005ad6423fd027206e6ab4c58d2978ab4812d5720401c1310b56ce9ecd95241a17ce0e7a", + "0xb6819bc6497ed57feb41bd82f56216b513085b6d1a560a958adcc06a6da304424ee34ab2580604b0e59f6b0091ffe6ad", + "0x93bef0806f21f8bac88a5d6e2e6d1adda06f9daad5cc3c8de61162495d8fcc3889b767a3e2f3380f162166ce40a0ce80", + "0xa1f699cd7446cdb1321a05f970bc70cc98593aaf0145a0d097e60e5897aa311b00d019e09cd533d0c0b7cc5c00a753e5", + "0x89ae140ad75a83db2903a93a3711be90986d08dcfe962aec5ea4ee69656026dce77821993c1defc4464442bfe7d44734", + "0xa4110c80ba92f545a1a7545cbeef997d6c0242fd4d771977192269d626b35c88c361df53bb36dfa8ea7e40da68e45f81", + "0x906786f38eb7e98c431fa2464048ac3f1f1df8f908a25262978327224bc82168f564b2f3e6da77f49457ce49c1a72c2b", + "0xb28d92b3228547f03a3f489e09070ad9a1e20a73e49f7ada96ce41c19cd6416ad809b3a3a01f141b3698e85c641d795d", + "0xa25b9df9b377baafc8c735a772e0ed9ac007c0b6ebac2cc0f8f2e799e5e6038a616968c9896cea862e99b1750224ffe7", + "0x8085eaabc79a2faf1ed0b9fdd017fba1e46c671c6d8ed78fb089494f792765b0617f790016d8f55697dd0f45d17de4b1", + "0xa0e81b557af74efb95cf94054264d30396121312c643052070ab53eac8e75075f1fd0b384cdf1d96bd39cc98681b2d92", + "0xb8e0ffc7548969ae28beaa9d8bd65872840a03150e2140dd799d9924249f92d962a0089171bf4b311520ab527198668f", + "0xa6188827a500b99af6eb91094a0e464e394c8c0a6d80cfcc5d8be89e8810732a03ca75b2befd00d07d1dfbe7dbe89be5", + "0xa4e5a47c656e74107e6007199b940d8381f706d5bb4226a0b2fb13eda725a556530b8d4876dc49c5f9631dc6bfcc4c9f", + "0x90330a50442db9a9c459e06d42cf7a69e009332976c3950ae7d9981d99066fd2af22f22ac429850b998f1ec929c82bfd", + "0x89dcc51fb717212b2dcbd0fa0da189e194b4ad5bf7f43ab2cc2c96f11c186d0872bd930aeaae01661ce2dd9f94eefce9", + "0xadee914ece15575cc34ab485f2dbdf3979406ce7cd8cd82197f156f373beee6d80e5e3623d79a2fef14b0b4ed1678a51", + "0x87e97e8866002364bbe9b49c5f2b5eb729c7018ec61dff7b8bcee1c1ea349e5e04a3f3781617d46d8fe0e62afe55d62b", + "0xb6b7bd0bc652a0bf79aeeea1767f0f17dd543b9de582531bb3e14ba2bfe1b720a6c6b613cfc295372eab9202f5e2d340", + "0xa6f9cd96d8e422d9897d50bf36288bf4c09d28cb0f5c4e13ef7f76cef6c75bb594d0ca954ff7339590cdece16414fdba", + "0xb9bc319dc5e55630d1ee8cb48978a256b69c96aaabb5269bed8c5366add03a2c38da11cb03a44e150a5c7f34bb49bcd5", + "0x868c36924f0056b3464bff8831543a280ced62be748d60f82ac860c32025c4589e9354984e1cedf24678374c959383a8", + "0xa6244602362c09b382926dabae5793ca4fc50600193c69e645fe229a471f7cf9e58c4a59124d6d2dabaecf50f1e1fd1d", + "0xb42df58ee9e20fce589837d5ed8a938eb83a00c6ffe2f6afc973f6ce26559b8d220976ea1fc18ffbafe739c92dda6618", + "0x90c0b2ed8ed7cd6f6ff812c84ed297b3231f6e2106f2df6d5e4b4bbf5378231025582cf39f35dc9344d9fad3adf04685", + "0xa968386bf1221425cee0d0b926689426fd77e8e8bca5ad3bd07298fbbeef4fc676e0cf7a4f29cf981c682a78a54a2d1e", + "0xa3a46bb7db36e0294b509036a40875850ea5ce4e8853cc0a7d85e8455fc2bd7d5b593879408ef2f3b2b2bfa44aca2276", + "0xaf825963207f046b23534896086a3e56247d752982417047f850bf306d0cce285b537508747afc700dff6472fe3b5569", + "0x8022af88981249b5da08ccc19e4ffbc35feb2cb5308b34064de4d5bfc8ff2b933363988c833ec70723e3b5107f8fbd67", + "0x89687fe6e424c7f0d2751e5f7838e9a3fca4c0bca043806fe511442bbf41cb67d01165ecb662b1ece1b2adede5a9537e", + "0x99c925763420fdac4149a02131831449c1df8be4867a6d2d09e6b14abb821d46bc1fc4fc9aacfa4e9de1a93f9b56fbcc", + "0xb819ee6a0724de9c944ce2ca51ffd3f1d93c77ff25e39de8be2a612abe732dddbf2219e839686a4373609a560041291f", + "0xb5eabf12513e91139025f1236c7ec235368eb8586522dce04d370acd3d854c1e6676d92014b60ea3e4e21e9d6f063f2a", + "0xb82e94f1013db6cc682032c7760aca2a1082826d280801aad9c6564704362e61a61cb52c6f35f769bd8ca191e68e0b0a", + "0x95dcb02a676b17f20b75632c7a9060f990e44b0c1fba84ec8e633554f875ebcf6e54caeb9816267e84a11808d68728af", + "0xb0c7c401dcc019d2108eab7e87d6494e06399f6eb4fd95b8ff9ba4a56e549a3d3a4aff13771229f4c456283fc3cbc53c", + "0xb1a8e3e500e3ed74bacf91a82b39f2b870963dec0b98b7d5ccefa3212fc9f3ef923101887572e14d08145aaafa8da5ba", + "0xb2caf72c47870ce9f0524c4b3df6ab3eb3695765c010a27c0f3cda0ee1c1f5bee64e5392ef8b3f0f11e66bd8c9d4630d", + "0xa8fb4864bce5f1c48d681eb37efe7d9ed1a83ed36bdc1f2627539b92c90e100d4dd64ab664e404b0eb7b645a8f95642e", + "0xa1b6164a4f0467444fd56a1f4668c8d1f295f6e6f5191355dcfd004c34153317202823d72162b621f677c970a3f0bfd0", + "0xb2cc59a2f6f3b7e18064720f93b28801fb684d98ee808ec5c04a5235dc40372aa0e0521410d8f736161470443bd97ed7", + "0xb5d9a823649c09151b214406189d75d7f1ca150cc7431d79b7d60348b6d7405014a44bb7840e35f9c0a634b4c6785561", + "0xaf6b8229fe035cbd6a5da3a3aad93e7ca5ed233dea5fe4477dce46ed17bac9243ebf25a8439ac2896c41baa671c0fdfc", + "0xb42d9023551d999d2be3ee51f6ca82c3b2d41fce51e1dab52095af6d4b59edcad70a1f9b1e71eddff894e3fe35a1f11c", + "0xb868543c09fa9b9b990b276ddc5b68a2415965d3de71b9ac538c26a6333543a7c33d0b432f57756ac0077d0021878944", + "0x846577a8c877461a58a94c5829f2ed9d7ed107fa63a48ee77a1ef1f1d1f940b2605fc742cb5ef849e3cbfc86942488fc", + "0x967ca22cc8c21382b15d73b4dd4f6f0a0bdb2056c21e3c75eb3d9c13dd41336672ceca03065d8cd1062389afa4726974", + "0x8e0b872d766c439f3f868f18ef0c173896eac883783dcc58917f76d5a2e8c291967a032d254450fa7f9a12fa7d7a4cf9", + "0xa0236eb36a4ce3b7d649ff02de9279d364ecd5059932328230314ecdce3278c42cb836f547bb9da9de0fc96cda2fbc7c", + "0x92eac5a5a88648e6d821d3bb51b280fc106f751d85a1742a6a1ceed071eaaa215a0a0238492ddbefbdcdf2e38e4149fc", + "0x88e1036f9b20a2c4b3534175c93d59c1ade3fa6652a4c5c490f21f6c3340769c7f8147d53a92fbfd84c23d7c4295cdd2", + "0x8b094165ad429a339f12696bc8967ca89ec47a4778f387e42e273a1863a38199dd795d120d198d3cbd93203604c6914c", + "0x8f8013229eb6bc6a8f93c17d3b4a1b206c258f14091c6dc39cb1ec492d403cdf5f696070ef5a6c0ab9ed4ec141b08d73", + "0x81c7ad27bd7a48b444b2be3d4b5d4845743d6ac4857b061e659d7ed48ebacdeac29cabd0cd163f3fe6c5cc28753148cc", + "0x91c8a92749183e3e6f3499d3b0e9b080109d5e88ce8acb03b35f3d04591e13b4c489ae323a149def1edaaf62f93bbbe4", + "0xa6a2d69f012d877460c33095924771065fdcdddc30670ea84576b72dd3f7769f90d1735f8914b6841c7d938a2046ff4d", + "0xa8ad4b976a5e4477a97d48a3cfcce16b358fd3dc1ed1df301fad6d6f0e188782c518796faf1465e52312b47bd713e2d4", + "0xafa2bab9363187473a85f7020106b176903bc3a3e3df1f4938feed5145b79b66db8aa608cdda554166ec47e60fb34b95", + "0xaf691bf473160cfb84ea517702f3c01daa6155f31393d807c897b39523448c5af09be581ad713c76aba194f90895cd9e", + "0xb74f3cbc198c9e4b2c7316fffd57fc749e367b7d1cf81b3f5311d266c9a3ab9598075ffb9230dceee230d5f1bbe3f796", + "0x8c28d21c49a15299f7ff3eff7568b8450e6404a168554b8965a291c03fdbbd3dae9ea6b9760869cb1f2e8c7206183195", + "0xa496a0df4e79827cf3bec117b92b5b248dfe129d783841935363362aee4822399974e6c03a92797b3ecde80b207fd7c0", + "0xb39fa07fc8f4be41588ff5560ed68a33c3020bceaf172fd11e0c1288ea885c6dcfb56a151e4773e57d864dce06fdbea0", + "0x990cd050ab056ea447c114217219d9c0c7526803f63952e22ae60a3996608bfa3c6119a56befc597592761e3a90ef448", + "0xb6f02dff3dc330daf82d1edbd4e6964d2e9c38481e74cde8d9d85a9e602ed22c4fe6c9b6f41ec76582f0a4e4414bf300", + "0x84440e4a7146ec2f34e8099e85c09b8d7bf505a15638aa34cd2b42a20f1f335cbc9f0e4fdaf2e53fa0ebb2dcb00519e7", + "0xaf389aed116fe58580810fc474eb15518dcd9746f04a7efd2de44c9774824db79f8ce4c4fa108e7396e1fc016132a402", + "0xb202985e01c62d0de1f6807fe600a3b81fd11f30f5aa033b1e7baf7a62f34fa5342d42ad6a6e309560e3e9ebc662920c", + "0x8a07641140db9701c676b2c094c24cd663a5a34d3534fd4f5f1e38ca0c46772d141679730b5d0cd71d056c257d9a125c", + "0x99dc01e76174370a741e8e9ef5654a3a7769a010da85de41dd315b674ba8786e6a697b74a79ea782a1fcf74a48e51775", + "0x93fc897841609670a1eb88d4e4498c54e286e25238309fc95389b16e4edfb82b8ee8447a436893c7180827a996b9a0f7", + "0x8e2dd561acc8954a53635c0108ff964774fe98d12b28a0c6ea8b5ec5ea3523a45b81ec642c1453e3b2a1c0e0749562be", + "0xa95b0b7f9e53720f4b0394bb6ae8222aa5be00a2050f59ccb595d50e0dd9100e397af9ea77b0335be02d1713c361357c", + "0x8e21dcb67da3eaff5b950f989939237e3735a31e346e1bec8e6ca11edff5223e33c1c6f2f79da975de2fd86dea286e1c", + "0xac02cadeba36143767bdb8cd4e1caf8cb287296b53955f33ed07f771a1fea521fd64b7e153c90d5e270c12ab959cfd24", + "0xaf95bca4016b2ddbca61c9c854cf999ed59ab4b5d619dd55460f20cde5ecc86081a2586a7eb37f15c20280dd06b65809", + "0xb7d7c81261e8c6a8983442e1e801f5072bbada1eb2e49b8e90759dcad653c52c0afdff9cbec41bf21cfe832e49ef8db8", + "0x97fe8c6d071dc80355bf2a74c15ecb16c59bc042eff323e999f4fdc39e1209803d32622c642ad25673c84761f0d357bf", + "0xb37da716119c00a0955a7fee59b93185a6e325bc5cb2a7fb35681fca0688d0ad2d25a0e40dfdbec1a11deadb1cc69d47", + "0xafb8091548179fd2a17d95ca47909d97866e4fe54099736e6414682ad083fce300e0a20dfe3a017c1ee4ee7d271bc470", + "0x9306ba1f3f2f74964dfcbcf9b87bafa44b5e013853c46cb501e10409f3c2af7269aa17c8cab261fe82e52a188ce0d18a", + "0x82430e3c25970411f40aa72ef1cda5b2b51bbc7e243a1b4951e92cb56a2f5b200a039f5554d0d1bb44330d89d1ef8840", + "0xaabfccb8f3dfbd4012b9d196448e83f17bd1ddb8c857dbf98e80ffc60c1af3493ac5c70e3a2f1f26352b1ead143dee87", + "0x832cd6dc83380d068c068d815ad0f4677de0ef602890835b8d32b73223490a6f753092d651968cb3d798cbf2a227960d", + "0x80e3e7f0c46fe5d962322f3fb2535de40dc078db80e7ef57923d46b742a8e4d6dd35ef74234f2b1637a317364d57abbf", + "0x9306bcc29d6f8a478ec085b144161850afa29d282cec756d0d3fcce6f4860f4a4b8c8a5952cce54ea893cf84abd6c4fb", + "0x9234c03bebfe6b47aedc7c5452058ca6a8def3c368bdbc9019ef121ad44171d6b31d9bda9c82300b5b396187324684ec", + "0xabc2ec6016ee252f5693558b694eeeddeabf4579b7e03d37504c26ecc29263e455ce8f0158fbfc54135600b72dc54315", + "0xb46fe7b51df64cf46888a810365f891d43db5b34ac4d3505f0692603adef04b1d08eadb3e31d039817e7b89bf0789802", + "0x988e0dd101bba7d7e4094cde99eeeb6d4411341e684fc06ae78d163d30c4b585375a868eda7ba1e5495ee7f0a7d509e1", + "0x94d3033ee1926aef656b31192653d3da96d5c533ac2436d68fcbaebf827475778689ecf14fc53042a523e4652fb9d713", + "0x993b598555bd2a35e9a03f99950d09f55a48ba63f9e0e65802ecb95602d045001f82f25c3bb60221adcb8ab4e2709ba1", + "0xa0acd921ea7db9870716acb595c65a934a5a06a07c6e54cd26efc86c97eadaae1522a4a26c8f93b7b7cbc4746ecfc21d", + "0x8dbd8f492764bee920e0224dbe39d650be6732b56976a5e1b636b2e7371c1509431175b66c6ca879ba8f915f9df8fa36", + "0xa01b24c1e3aa044cd2598032950755763345534f95f6f71d50565d25cbbbdf9c42e35253e35b683f6c3156f5c998ca4d", + "0xb895522dee1ec9c5289e6fec652093519cbbdca7a2936fd1df3ef956eb404f1a24272c9ae6ce58eceeceff36d76d34d5", + "0xb91cea120e200858457a64a60aa876f167b1b88c1dacd9988700b9f0f0d1bd1dfdd8dab56c2e7197a174b7b8bb8422e0", + "0x8406767e4f7cee2e12431b093ce82f633ffc76b451ac8414716fc74fbadff30c52a22869607d5de465d0f4df8a740343", + "0xa2cf431d18b2fa526291c7027d59b18cbd73a9b48d68cfd6e4b745d27774941af809edba06c8534b1864045d6fc1bc20", + "0xab3fe23aa8c45ab2efb2ca0c593c8644d3f47f748c2f753626289b0b9c761add755e3b52521ef37fd609429b2f8770ff", + "0xaf4530dfc5b3f37888900d9fd08554bef4e47c4c09a8c82bb48c4b9c6c9089465f98762d81ba4272b6861121b65f3c5d", + "0x80f61d086511b9b8b2033921336a68adde99cd25fac71d8f8fd0e476dd30cdfba49363784f0d0578c1f648f93ae23f8f", + "0x82ca682cc254952330d1be8c0e53da24aa943ffe0209b00bbf046e1e4f9425886a01d6582e2853137a9c256316e6f737", + "0xad1d508d2ea2806c351d5bd1098c46ae7ef83f4e49e4e87f83fa2c63f715ec56109996284a541c2005693687b4813623", + "0x9061817ee94bd2895064f4af04777b499a1fedd9688ed64bdba848202c3cf9286b699c92400ed456db926ee23a34f90a", + "0xa8bda55cf6f3f9edb78b43a52b7fe76e5cc2cde21e08487ea597cc266e54700ddcea1a287ec6d8f16b738b67caa27152", + "0xb605576e55d1fa4fd9d7fac2ce549dfe23fd6ade41fa859bf809baa3f1497d078cab06a257ccfd6cd59f67f17eb22f5f", + "0xa92d22ff5b5ec6dbb1d57db1b740521e82b4bef84dec3e130cab63d0641c3a8fec1f6f86141fb1918dc0f3fcfcbd8cb6", + "0xa0165df8dfd7b3cb58883768471cf485b886ece529d5bb78b26acf9ef6c44314cf9f34914233c93b10b1918533dcb8c7", + "0x88b79c9c721c1936fdbe22d68459d1033fdc986d3e52f39341ab06cc85a3f230ecf0965ee8d2dd54496981fd08a02657", + "0x939b77fcd53a523240bee730c2d7b8dae0b32bc3dbbd31428c7b5fdb4c3d34afe7f2a377b2918497574606bc06cac750", + "0xabbf82d0156439761b36a913b661e3d452dfa57e443ddb61613f80e110acf52765139fe3d1dd59c9e7773b262140cb90", + "0xaba28324844cd19b2d5d07a87e6f3180a3c02c7326bca846c1e7a7c131c7ddbefeabbd6787b4e1e910449f3cd1249ed6", + "0xab2f71af8596c10351f7ce9c3a9bec08a5c7837cee92a7400826284752c98531a0199e2a7f9ba7ccccc8fa0a2207aa43", + "0xa71d5a4f8af3a16ec9c3c110ca2135c68103109d4384a299cb7ed09d96231c90b04ce34ce12de02a40924d84947f7f31", + "0xb9dd79bf3286ea08c9b779910c84fdd02a33dbff7adc2d6612cd58e81aaff3f64ba021f875ea9e1201243ce353510350", + "0x9838fce2f70e7c47dca7239883229c1573ea97d469f120e4af659b18bca31cb68d12220fbd6e4e9e952b28eb29c1e5ee", + "0x8dd341e67e4c567a4ea95252854cfff8a7631c228ac852b33b2ea9211b2a6c606e4b0db28afec61a1a55e6b5f0a6604f", + "0xae9b02d60441859e3e6f3866a9bab8895f0cd6168f8e84dda7c9b1cd7917f1c454f10aff9a8de39909e36576bc0b4828", + "0x89fba7834469a06cb0da39c39a288245e577fd956c241707c432c2590e18e956e8ea3f67e4bee5a5562377617af53334", + "0xb7ab26d79ee65eb9612e54f41f75e22abd83db45010e1a94ce5026a24675bdf670e806c71f0964a33d6ed277d464732b", + "0x8a25bae10ef86d7e91a7d686965d17fe16ed635d787d4d6ca337b10ea32082938f4354620a72b5aa43ae62c7a0e751b9", + "0xb18fd9213bf3b2d7d191266c7bc1c31f683fc7da7dc5ddb4c600e1ebf5fa80a399af9e31b4ae747581a07ccb736b4b32", + "0x9968346d8a867eb57f628e2ba00f69e9d6aa8e713377a69413323b1b9b26218f527c0e719dcc1027daf10c3392f59733", + "0x831ee266686776eae4e3de1f2bc37761a5e1b918d4bf0bbeeb20b490902ae97722bcb1c98c485407491f248eecb841fd", + "0xb0e949d7c50b852055f38f3542a974bbfe7a33409d67c557d70c1204f87265bd7478e1751251792435fa22097d1762e4", + "0x8b0bee83715e20f2ef832347c926249b5b168e4ad87b2e5a9149ea4e07513e4790f60b1769ddd1816d7126a0f6fdbac3", + "0x84edc35061dbe8f3de90c2f9ace94be5ab4170b66c42583a0643ff776256217bbc6fa31612e68bfb9ab678f8e8e49457", + "0xafb4ca7a4781dd31a7d81ba8a739eb65c43f3374e76b4ffeb2c7048b055f837e6853b14ed2d3224a40dea35799f0e4a4", + "0x9945fd5ecdda5ac952785310e87917126917fd4f504fc5565c236db9b96f9666934766f46a1989c1aa176e543c6e33af", + "0xa6d4466b53c48d7facb9cc33ced1bec98897e545b10586857e896d35c850f2cdda65e19bb934a8c74f6def805b1df4f2", + "0x81e3fe4330948c279d99a8a1a4e4e141a039b3ccb7287aaba6f9041c3a8a41db1a4763fe04a36bdadd3d3295becb9d41", + "0xb6be2ef16b60a78b17991d27463e401eca731129843021e302830c2fd665726547240ec3a3240586b01a05ca8206dba1", + "0xb9d7fe5671b220a3da83bfccdc16c0b6f5e9e5c87810db14f070dfee582fa190a360c62acff13cd877c818d705a8a872", + "0x86867f22bf6b859e7f0ae7724a1174a65c4902cdcf74bdb22415875d72b67f49c62ea8bf9ed0d6883ab76512ebb951f1", + "0xab728a8167b9e82d608d4939a0712f82843f624d08d4013dfd3de41bc526e9d495cbfd40c443f67ac59dc4b5f30ff217", + "0xa5c4d10a04452c1ad12c18ce8ed7eadea1f3cdb34fa5ce0cbd804f5dd92eae2551b771523e711e8037770cb66d1951e4", + "0x8808f69b975f363bc08f8578729a6e68445138dada78d5818d33fb83a7af6cc6e7030f7b76286829861a4534e0b30248", + "0xa280773d32e1ce3544d3ba5025896d21e358592504737de72ae76d164009fdad05c8a1e5e1f8658ca6374b347d47c29b", + "0xace91a3971be87b1ca8e737802918d86375088e74380c444751c65978afba2b017cbd8fdcd3f9a0c19c0782b0034a589", + "0xb5445d816d65ea36c9bc6a3d5ec44ce6b76dcc18343d7084567dcf2603d2af93fa8469a1c493e19f1853c96f89621fce", + "0xa238867fce5b09e8695240f936a3f3cb12a715511b7516de995543b2e15aed8860a12754ac8d1c5ca2364e4471a9c5ac", + "0x9467528341f5b93b89c7f37c5dac8bafd0af620230a9f7de3e809f01cf73b8ddf70c38c5023a631a1978ac05ca35c318", + "0x8e5f1c3c411f0939ce4b6a5ced42172fc5c3774f596a114e7c5c8ba433c4efd94ca84affc0bfa89a1c5ace5090276a43", + "0xa6351818f7553d446cbe8d3a318841b0607d1f1890ebf9c6220a092bad3ece9ef8acad4d17935e437377af8f9309606e", + "0x86630d0fb2bc104d8cf840b0e545c0c149c1a8e4dd6d460dd15a52a5935c8ea5c934ef099653d783894a6d1f68414a84", + "0xb357b5d9cc645b645fbce2020db583cdb68772751d6d11d635f1e3ecf995a55bc374be7750b6e8bd4968a55600ca9806", + "0xa9b659b8cacb73a81093eeec42dd7f4fc5d955f9fc543037f31bbcf456af6476f303aaf0ef960a2df88365c2704bb61a", + "0x8b6ff5201c15cffe64bdeb818422fa10dc503ef2a6a4d686364afd0f35b6473e4463719173550d234639f6077e19542d", + "0x98efe45bca5ac679cadc25ad0bdb1f8deffba13d2d7eb14c6149d5addfac06b82fbba6d24b323d615eeee1465b3cc30d", + "0x8c2329c976d78f1d5e30ac34a3fab1f96436947d85f0dd190301a1868e5dcbe4ce60f48fdeffc3e6a05ee34a461d7dd9", + "0xaec012ad25d99ce014101d7da512fe032673399526435f6e1faca4b63759e8f6694a46ad01672da9eaaa4634f61ce89b", + "0xb8d52e530c942c3c7a67bbd0366f4cfdc6a1a075471878516b7a2258aa073eba50a113cf433879a0e15462e82087d17b", + "0xb40c5ce16f94837c86e81d98e2130a9e1dd229da5aea52e79cb42217d3b5908a53d76782cbe3934fa8769db58b00dee8", + "0x877300304eb69720f7cfb4f907b4a7e238920fda129a38516dffcbdaae2e46633d31080590d6df05756781224d532fe8", + "0x973632dc791a5214516c3e59b2b48169470678b7dab66d513e35a0fd1df86b992e27ffe6050a5233af20b5d4998d283c", + "0xa8ae0e723a8ea6e95d721337465a388b60b92b1d9b1deb0b9f59ea30842de356184fd55d9b8331d8a29ef473c1ac2315", + "0x92ed6cca30f76135c4b7e7893c3460501e92592f7d2d6409c1e1d80074120243a5b9ec14d801991204f5ec4f94ff1daa", + "0xa9f575b8518dacdbc5cae766389ab2ec01c876038414b7796f640f633367a5281cb49b48b5e80f6416a33b401c21309a", + "0xb9793588283cfdd47cc4547cecfd987f9f8f92c2b408725f39c1d879199d695e87675fa7e5a190ab3bbc97683a0b9587", + "0x8329a844dd67dfd48546791c4330af65501baf9524ecf8ed4fec9ea87067d0afbd33099052c1c2df819ca1afcf25dfc6", + "0xb908eba1b40edc300b63ff6e20e87b17e6dfe975c37ca63c92e8866968070a2c07204264646bbc9318145fcb90c23555", + "0x8123871ed78f46e9eff4fc7af9f490594fd7c20fb814e505481ac5c7bc7588c1706a79b14b85d29bd7b97d7c82b2ae79", + "0x833ed8928f154fe0a88ae98e5d8c74f816e3ad679c1c4ac1322604093e85ed4b9b9c4361ac188f0da5443c72ee4bf3d4", + "0xb9fcbb8a422bd8d996e713d176b7e63edcc6d73b3d1fe3f2c4b59da637a168accb5fb4d227b709f979742cc0af8c0ea8", + "0xad3759a6a6bac3047935443347e3c63819905f6c01f58f0ba76aab422d723cee10c769663be9554473e668bffde1d500", + "0xa60c1909703211a93d7b5e8b8ec1cf4ca06ada653c27696a7dc9a2ff75cb712918888c6b61b8f792ce9b413aac09f48d", + "0x91f05985ff17f9ae20498185f6558f9f38b67966876dcc6981af4d179cd055661adc63155f4afa6167ad61b7038ac49f", + "0x95c5add9bab6b9792517772f9f8b21bf7cc325dfd13a43177b0bd982d0f620185d8596c2cba46a5e10aae597129870ce", + "0xac0b4b6e2b3e417166ad9b17de0b3ba775df6ad3a78ad13a1892c0992735ae54c06b1e6123b0c0bc90544441630c6a1b", + "0xb0135c25f74ae776c241faa6c91a3f7ed6138d19a2100928b7ede64b79e177d92c5cf921dcce3c614e32de34975fa6ca", + "0xb2215b560d5a36f045de7257098e9d75a40122919d4726990b4395eb2bf1ec789cd0c64c46b775f6a8be28f23958e17a", + "0x870dc7f7a513728f2b428a3c08b15a6af88a288824e790f41b1190fbe02b59dce2914a1339f7203cdb7f2f9c98d8d721", + "0x8e3895f03952cdab36f602418cd746bc0b6a07629eab0a20bbd8de6c993030c5287fc146fc45fe97a06c992e0a9ddf02", + "0xa4cea15ebc0dfad9feb3d18168fd33768e8ac69e263263ceffcdfa35e8638711c2971697b7d5b2aaa0fd8c5440f3e164", + "0x8cfaf5369781a59f4117283fd3f290b81816abd3124a9486ab1faf7018d36a73c1630efc4ad648ce462e541827d51975", + "0x82b420eb25736126ef18d91e91ca2ecaea8983b8091df88343e8e54ca5ea7a3da6918c97695cc0cd5c2df95afb1e3cb7", + "0xb3c13923a3d46d990aaa6a1eff3ad32f162ccc5186e16a549dc29ad4d63de6287cd05579452785cab32e2485636d568a", + "0xad8a43ad6195e08a36f755dd536842ec88a7d920bc302451c860444a3fdaf294e5b5dc5a122423474d322af5de8cd8a1", + "0xae40d1a90a77965366b5b5ce87d6fe86eb255cc3d127526930d128ef7763455adb82475ebfb7be31f9c512394f2a22fb", + "0x9763bb9459fd4c0de2534767bd99f98b859030b6af5739a7081d889d6875f5c23f0154c30d00b7240baf6450b4459987", + "0x94aace9e9318d79d3c7ab533baca31724bfec839b01187e326b1fdef846968b1b29882f2520a9e237dc41ada01bc3761", + "0xb6084f9e0051be76244ead401e8d2758717e93c4cdac58443261b3603cfee0eaec7d758b2e4357650d2c1f5391edf798", + "0x8c656a798fea470163e70869a13edd30d138bc148460d122a2275df8cb43f2b45a14e0d8a8a49eeb7c1afd02484b6ffe", + "0x8ec317e63df2881f49401eb2f6a82e261b07474006fc293bbb54e0fb7437697b16ec1d6ea101fcd56543bf4d69374cf4", + "0xb27d9b3b8c3cc59d08159c765d24fd4660bd0a54b2b7fa9fa00b47e6770e6e8d3ca353d305fd772c8171e20765c8a66c", + "0x863ca045abc38ceee09c4a21a3dd18f1c0f70c0289437957aaa39ff764760bc422b748bef8ef133ee28d88c46e6be1c3", + "0xb0de194caa68f5288dc365faf9e9ca3c69b0a8376cdb532cd6f1cc3478671a1e755d0e8afbde4e3a88440fd9cff4e8f6", + "0x8a259f48cf5a45773522f3c5f283a6c01a0febdae09f873e009e4635c57fe5060b01243b2e5e1c9d2ff7490f2dd3b334", + "0x8c4398e1e579778c88976ba12feaeac0c96fc97b4e26a133ae74fca1b9c315c1112ce3977d20fbe9ae5866ca6544fdcf", + "0xb54b25aeebf1917bb4981b43f39491918773bacce37e994b74f877d4a636f1b3f4a2f164b858f95259f285ca0c294f24", + "0xa9db33b15331e852da3693f6328bde30b8cdd79c9b9b63107cf78dedcf14da68446c462720b9ffa5a1bfdaa68f5d931e", + "0x9966b6bea54405df1dc4edfde9f8c3ed7c0733d5a73bcd8b349035744d5eabbad0d19801a678d48cec84c0335346af33", + "0xa3d0c32b5e3036c4a4b222c13f7db23924bc2b2f724bd908a38db3b8f9c95cf5034c4cda0c5083c0967d34061a216b57", + "0x92ca6b883b2b20015fbb56cac4c4b5ef22e555a9b75f4f020822fba9167eebff8f9fe5c729c574cfa5ac27bae1a83fdd", + "0xb72b58d6ddf54c2d37bdc1599ac966c54cb4926c8d2f70d1bd4cdc383c6eec5e4b87efc59466682f8db964d80a4b740a", + "0x89ba63ee57a1e6f13d7c66150a8d6721329b385eed92be3ea784eed89c76a1ea88595725612b109a9e4aae41d3f6c972", + "0x8727bb53bb62fb714e4e5de461c6cb298730641e38a0b49b3b3d4a29fa24167c7c6f4ff47f4f3b91e464a581a4181853", + "0x816699bc7c3ed65747d34786b7fca4e35e79907f459f2df0918669adee54a70c03580c4e7d2e410ceb45c71fcadd44e5", + "0x979688c14ce623dd17344e67373e5852bc1d3ea12d37f7b28095e5d578d8c9c646e4b97a3a69a97764ed0a88f62c99c7", + "0xb4539a9eb6578ed3b8dd54cbf57419e99b69c0ae1ca3ae3b4a21f204813b2a78438d6c72f86c13dfa06a0b9244b98688", + "0xa5d957181c30701fe6eabe3e65a53a33dc43df364c45f0c4d882ab88a069024bf04b71015f1c2fbf03f368e63bd82fe9", + "0xb9ce9a54d9b17d4da41ba3135d077c546cf39dc83230506a4ee88cfe39e76f7e35664ff1b571e231054cf1b764b9267f", + "0xae6bf2eec8046137016ba94442a7a0aaed0924ec1558885135fd339d2996aeff31ac29f1de07e84f7b7391fc5355f429", + "0x85c7c247766a4ca44278be81752f4170dcc069f76992b236b40e71e31e08f30de6a5ecaddc44debe4f94151cdd8d735f", + "0xa19d41fcac394b750248e575c300b9a96dfc5b3dca07ad6e1d68dd3f8ab94d10aaf8edf500e3fc7774e7ee52935f73ea", + "0xb3c959a22fddce5a2e199bc8724e825a6d9776455c033299b5cdc9a9d184be169d807829d5df5e747476d172b5701cca", + "0x916aa7bc58f34bb8f32808858cecd3e90ea26c3ec1f80a40e863ba18fe9af6e67c0b2664a2274eca6d36ed72e59a9341", + "0x864d945b7be551926f747406d72057c7a141110f5d269fb6657cf347cfad7178670dd294f6a98c19dc0943a68d7ed45f", + "0xb3480f8a42ba0e8eb020c2e1c1284a8a9102fa68b43f6eaf28e031621b9f68bc399899e35a1a283fb52530c8574484a3", + "0xa8cd1cb93974d1a6072ed51f356449ac19b04539517cde34bb7b2ba55949d213ee07d387ce7b5534175bd8a044556ff3", + "0x8e81fcc5fa5579f2479011caaa393f47a4e12828e2e82072736d85ba1bf70ffef9fe3b2c22fd11ce8eaeccdfa2579758", + "0x897f935b4542b9ccf8c0660c8fb1a570a8ba108fe8440e17e6c50e01affc2a8597b7f7cde5244c7026013b52c7331b5d", + "0xb9a20f612c74821da05f48d8bcfa7a4a550979e35b49d52031be8bc9cf717fff21db0142b633465c5edafc42b7c73c84", + "0xb88caeb2157d636fe26d3b221143443940427e8722596746bc337679e10ae6e5a9b33c456ac271f8b01db2f5d1b00a62", + "0xb23bbd978725aae647ca2778e801235f605dde17897d4d56914b0d2241eb31f930028904a6555581ad5b2b74ec3c9587", + "0x97a331ffcd02eda1d6e0e15deb110ad6106d3159ea641cfbf424d2e3065bf65c9b14f72a27ff3f576dc51eb068bfb22f", + "0xa9317840cd8f437ea97d80a3f445a99eef463a5e2beba3c986da8fa67def4ae9a0e8d1a675a35e5616ee90986366bb70", + "0x8c26dd7451b12c65351d5ede6a00ac7b9316f9e28be8c692d20709c3b4a5dbc76fb914667a2f1e9a654f8d2850b7dc3a", + "0x8bf4aa18a988f82dfc54668bd4ad5161f276e31567c949b7857cec331c74c6b68849afe852892816c802736cf7c547c4", + "0x836fd166bb9689520cefd6f23905e4c1260f97167b17534930923107fe934d4afb1216e4b89679a564433dc952a77b0c", + "0x94d6a5a4a11f41887eb814acf9b5a031d013d614621642384504eb78e65b6a07c50326632af47b408d8ccf43faf8399a", + "0xa213812713128750bbc5311dc317992bfb5124fa067072891f452880183d64d6fdfac8825552cb809178a3f3a641c9b5", + "0x976d1290308868c5e41dd3766447b29ab8c3b72047a0b7de85d3ee5b1e13d522147a02572cc0d1ed8976d411faff5b9a", + "0x82a4494a95738ebe56578e1e4c0e486eea66d5cc44141f478bfc5a6b3ebbae6f32063725284df81b438603aa564a2b6e", + "0x8a6f4dee79baf71a4a40843437c16b2f304785f3e56b32d9ab2474666fce2c7749c776bd898a65f4a4d542a497cb6d6d", + "0xa04a3484be07c2d60f1a90f9dd8d4170270a808cfdb863864377c2515dd71c152920b65fcd5f47004d27d14d7ee7eaf2", + "0xa984f6633ce3d42c75083ef7732e5d0ea15d91e73cf893be3ebac5e56defb8db97088c5cb3acb661e26bbb354ad91ce8", + "0xa5ab5b4b0dab86706d68c9ad921d4917215c4fbcadc8adacef7309c0c853bc3c2ea34b3868d8f03cda6f504793832594", + "0x88f03e55eb028353b70352dbe91f298ade322951ca115972f1207744254fdd01ccf899aa40ca747da8812dda5bd5f985", + "0xa4bab627f7de273f8085169cf05413bc368c5d9e5f58bf10995a8bbd95e511b1ce15d008405728ae8e8a83621efb56f1", + "0x8ed518d0f225b90fe7f01b0fe4c451589390325044f0d18a8c47bf13e24eae8627feb0c9e9514397536f73f33f67a044", + "0x97c73837e77d965f401b4e4f089ef4de7aed1126bef6be4e9002b2b68014b98997213e492f7aabfd2e47cd0917a11d6a", + "0xa99e8a55ed0385bd279e11a80255b375f2d59bf8b0879bf2337ab5e3be450a2ec05d1bd8867a633e359a02cece4dc1e4", + "0x82a74b5efaf3c217ee2bb56c9b8e76b3eedfc553c73177e59d982f503a5b0572b5cc0d1292820823307eec956c42b28d", + "0x9800ad3e10e8a19d65d5963673c183bd536b65e14ec18dca45e881ff3bc74eac32bef2ef845515ac4fd6caf558a6926b", + "0xa2933c78a67cb40489ffb8096c021ca017b99feda1f9c5d702227d7f0a2ff66a539d68a47ad90ffdfb5c31c774946f87", + "0x947b29715258ca20da5b17a8e3d99665b7e599aa5bcdc5d2d7830a2e3cd78364d51a3d7c0d8bce48a1992b27d1ac4980", + "0x86f2e2d3e160d3ff979ca70c456785b4b2437eb64e58adcb78c4aebc96b470f1b8b999a3ce8ce20e3d3f030d163cd138", + "0x958f4435d35932a91eaad0dc476bfc2761a85f336ad2ca6fe0c6830fe54e8f417434616df9e6f07a9454a4403b00b64d", + "0x8b1755af961e0f9f59651d56b538ea59af489e859a1c93726cee62649da0e304093d62db9a2c5854c8da1be61bde990b", + "0xa5e11042f73f979c8649592f6cd01dafb319344e379a65aa9200d3b636abc569edf822c2bc12b3db5c30b9ee74f2c981", + "0x92ac5584de1adcd38a2ebe361225f224e9b498344521be519faff77f87c1f22fe8e112f9df7cf960b16e358efca0db08", + "0x81db84f05f75a218045d7d5fd4620648bd4a95cf468cbd69787011d615778ba7300b729163e7c8abd1a5b0ea66fffbf7", + "0xac2f522e9f030a7c576fbe19041f5db3913af58da75b87e8ad64b93bb34850a79b852804dc68ad5e7de66d90878544cb", + "0xade9763d1c7e9f68b5f817cdfeebf31bb3ec1391dad04576c55fbe4bb13cf0d45abced3d51b5512c73b2d0f403906340", + "0xa0b431bdd9641595fe1eb8d96ba4fe86a447a31ccf36cd2f7d94c5c86a7d96bbc95b204fcfe7c69c1385997b1daea3b1", + "0xb3b093bd8fbd84414609ec9a108507f97d7f77833b93b15439423d2a2928e40b192247c8471cdbc12891d83c765cc6e2", + "0x8531a5ce8e0c44e887ebf4beac65352c8a9673c51b6a1edc439e08bda1354d359e1ab2e27b82636c6dc0daa3aade931a", + "0xb22c2f3a77ae4813a75004dc2c9593cb2a51c430c559bc7d07d83e95592883b99fbd0f9ad24d2d80d86c871cfaad2721", + "0x8b6dc7d5b8cb6bf36352fb19e42aa37647505436e1442eb1f228b0804916d569643102b2282ef66bc9a4442520521dee", + "0xb29a811ab81dba820242a990dc774cd937cd299495cf721cd11971b9f1dd9441ac687dfff0e91656b9764963a56e4625", + "0x805b280e31664008fdd874bc38e870db271027da70fc2246fa82c499742a9a8de1152275e0be61f307dc8f7a918e270c", + "0x929f690538a500d238208930b55caa9c489bfd3476f6be2d385c36df3159dc3d8bdeb24a1ffd7b028ff4d881551e2888", + "0xa92bbf103ad851a41e5230e1e37ec7802e08f4610c0db9706806afc4a247679b9525f9a534c70d970a1acb47fec9bcdb", + "0xb9f2698a39d6d7aa8aca181fc5d95dec796ed6eec002557d4b63369bd90aa4438c27ab90da4f3ce81168cb42f7400070", + "0xb08703bc97292c56833d8e61105f1431c334f98a7946850c6175f37f703ff790d9a1522c0003f08dd111eeb083235073", + "0x9355141cfadf46f37afb73414c8803f9094b06952c9fccb24a1f8c18a13fa7b1197321b19cb832de3f83ebdf8deee53f", + "0xb7c23f7cd8e212108906b7809df90db58d2c2f3a8e1f775274181bd81c74fd7c2f8d68bc7d4aef639ff4e19f86243f98", + "0x92728e009fc3faa08e81c36c268b3ac18627da7618c96c97598b13242286645789c15c99518a07e658d92eb8d2b89c79", + "0x8fbe36d4f2f08cd6245e8999728884c636a264451e4ed894d2116375f3d9eafcaa72ee59cf7923ed8ddacb53cc478761", + "0xa6b2bffd6bf8f54231fabe46ab2c1d014ddaa797d08e5914f13988140bf804019fff3ad07ac2cb31283fc3e74e28d0fb", + "0x886387540b5a7acc8b2bd107124bd17d6515697e09c85c4e932a6421965c872f014d11d1ddf321651e4b3564eed4f252", + "0x8b81f3ebc962e9ecd13a11e919d86ce14dd89d373cffa158b807fc91555a4ec1d7164504fb67edd9599b10fac5e32aa5", + "0x91e3213ded5f82e34389408e95d4f7fcd0f50ecbdef9726a289238e4159c6d3cd2f401479a1f785865e91ca213d2f8b3", + "0x99154b88ca5462f62031300177e571708821348e1027cad4867eebe42a6fe92a58ee1dc21da9031002f1b051351b3785", + "0xb5c2b7cfd87f2f65df07b39f8a26dccb16946fef6b89268b9300c8529d730a1469ba565a480d7c5ae9df8600ac50e90d", + "0x87df32def37370bf8c4c3a22a670bf5605c78f240eccf8dba13bf19c8a3a9d0560f8899259c4e51c6b0fa64d7d1e4c76", + "0x980a20e5cd352786bffeca1b8a31930d8898eff9f4a6b2570829248410bbe1c78105b6a61cce7e3ed1642e5e2af127e9", + "0xb18b8dbb9eda5cf333ea29fad7734235ac9e7234b49fd04f178136b15d97595d5b415a92455a319ab594b81200cb17d5", + "0xb713a71be9bd22ef6a2747d0bc8f4d008cdf6182e287c1e0274689e915a68150d6083268188c1f4a7fc76d21a219ec85", + "0xb86ff129a981359972bb793a81fd422e0b37f89e76fea70da012fad160b9eb7b029ced81c7e34679f6897a45b4e8da4e", + "0xa74a4cb9707156e21caa20b95a2a4b4eae8f773cf679e2073fca2cd3b1e502ef06de8a3c010833d525a7f8bb6bd24601", + "0xb51f06da38a76c2728cd01f6073f402fc49cf4bc5c60113a2700b5bb0ca500e465e541c467013a2804bd7641604bd2d4", + "0x9855dd73307d8671b6f9ebcf676de3ab7e37e7ac1544447c7ff34a213da46123b57ce23bb0f381da8fdefbcbe6c35645", + "0x8fb382c63f4c935462d013a0d3e2321d72fb4781c10afe6e31ac51766832218a05addc6dbb1f644aa61b5da9bccfd5ae", + "0x855dcff23e0ebbaa3562fd27c43957cfb35d492837aa71f27cfd1bf65a59a12d2beded9d09f3ddb4f801aca8cc34d2af", + "0xb7e7b317f10cdd13bc879c2fb0bfcd137af23e0cb70917e48d53b2bcf8c157ed7e5f58cdb966383ece9d3a4c92012746", + "0x80d2f84c39422afcb449aa68b34fa9d72e9de79a473c3ea5897f6f3576d2bb6fa2d49f0b44aebe5e68b11e85e066e028", + "0xa35b083749f8a5551f0dcf529e845aee189cdcc6ba779f4e88765adc49cc4779cdc2290598908ccedd8dccfdce29d53f", + "0xa30c412f4bbc2de80fe5c577b4f94442255cb3061a40649b0ee5357977503c6fe54821ecc8cc92d5056b6977c4695e70", + "0xa2ed0d90ab612fa3526f7450a43d45a2d9e886f2e5888ccb8405adeb8ca3e41c6a94d18a54b3cb1eab5b8f3851841ebf", + "0x8d4dd3f8f8a3d69bb217d338e757c814eb69e6a776f55cf51fa7c1b2f1ce5f8e9bce8353dd335e793d68eef676cf7c36", + "0x880d1ca33d5d3bb47b788a7ec64b9130752610816facec99af53b6e58a7e414616e9c815b1bad870d426380085f6b5cd", + "0xa287578293da4354f2c3c46d637aa77b91526f9618799dec4bc602305ffd8336d373786eb67eef01dbaab88f07f292c6", + "0xa86d3fad257a64c84954a7530822346da0215ebf4ad9c583f35cdbe16a02fd70d58ab34c93681fbf55d6075db6425cbc", + "0xa7bd884d343a6bde5f6c2512d81ba701fae7afa6389389e4776eacc0698a54c3ab1a0e1652c1a7a23d3a1d2a63cde8c6", + "0x8e0653c8b7279d5c958ab1b53dd77b73fd30d9781630a870d0a75681d38cde4fb7c2183b9c5758596ac556578b43fef3", + "0xb76a00c6f5093e7b28703df85bf968dffb70c455c91e75cc81189598df052244f7549d18e45dc70d98d3d86e0094ab2a", + "0xb270f2ad3dbc8b43ee2603c4e641be76820f07a4757cfa96be2be9c310b7e39b574572103253594a51fa9243298cbd94", + "0x977b8b86841ab8be7d1d50da7369e2bf71f24360aab8448d7748d59e010ce81bfe79530ee6f6644b987fc0d83df3ed15", + "0x8e18bc59841b7d56f8d9eff8818eee06288cd6ca86200eee7b5e6b230070debaf254a2198b4cd7dfbda8a1d55a916c8f", + "0x8e7a328ada969ed6289972b7f43eb5958d23688603ee6d118b6ccd8978378dce2d733ff64c30519b19007a78340fafa9", + "0x98a0fea70a219292584c69546d6d242cebb2f1d84f69c5aa275a257a87de652e721078b983ed67410e3a5eb0cfbb2bdb", + "0xa09fbecfd05772a9989008281a9585accba3850831485802f042413da533b1c7ee45a8cc679804340bd9142b2f9e0069", + "0x99890a6b273a2787fcfdd8e8500134efd60df99410e8432664a3e5325e55e78942f4bb11024c90e4c3618a70729a277b", + "0xa5f3eb1617a77f2d5c76bbd1bc3546ad1628be90fafa9a8b62c605d04e599ab2eb74b25afe0e68fd020daf4868dadcfb", + "0x8b53517d93f42b833f7669c131dc67f14c3b0639c46d3b02bfdb24cc9e642133e0c665236a7ba851c100ca733d673341", + "0x849fd288217bdb154213e79abe1a78672903e15429e37f6846019986e1cc8dd2b3ed28e4cb52dee1762a4dddb9ca95de", + "0x954d839198c3dd2ea1ffddf98050e2c52ee81b89f38d967bd30c6863672e43bfc32e1030bb12f5aa424983bfa31dbf5b", + "0xb52fe86414a98d0896d7a427d57739da35cac4ee24be565956d15a5c1cf5b4b95e5425dd2607fb9f6d6024549b59a4ec", + "0x9586070415a6bf1e11304d2819330eda88e81a88b9347aa866692c163e1af772be9fb747d9281d7aabaf5c9934596934", + "0xa5b78e5bea362df26a89df682df61287763ca1b87ab9618609c99e52e6ba047fba7ec828c0552ee26279aa8a48751334", + "0xaabf36b9dd465ae03551dc82bed9cbf1d22a2236ded28964334f7ad474f317f4fb8515b853354bc06181fc9af82714a4", + "0x910f0b2efc608cae8cdd39df7a5ef9e570592b31df2331baa7721708057188ae96e1b43e2f2f2c8cb360b961d687b60f", + "0xa5c5b131205c21ca68d6103f8499279621da337a743e4a08547c3b4507d52d2d6e5014fa5d920b351a6f53a195687766", + "0xa6898dac2d8748b8bae155a7d8c169e7eded73cace1e382c4dae8633f19463151399c5cf877f8ba344a698a98228864e", + "0x92919d8be671b4f490efb49bae145f419c84a1e81d3ef78761fa326f67d749ff3530f5de04f984a018065f42e852e1e3", + "0x81083de978e025f0b5995550fa17915d02489344cabf8a79248352d78dd6e893d28a5c5204a65a8873756a34ee3c0120", + "0xa6de92ecef84d188cefe29a03b564b1e7bef2a6afd785b58897f7f97a958573a35aa0767bef12a49b352de30b4f0dc18", + "0x985cb3475c7a9f582c11784cf61a1988240d74e49084a4c0f55f3f6068c4da0b08b136f8fa62e9001e0a265bf65fa3d4", + "0x97e6d360b504991d51119a78c5b647f25d5fcc1298631209d82c2ca40ead0380835fe3cbf8b82148b0b01b8157e884e8", + "0xb313df44b2c47126b58064599a0dd6ea49e5ace9ffa663de03ad30c1e95301cc68eed67d37ae6238469e45124c59bd39", + "0x8a58f70545db2242cbdbb12492cc11ec4d2b2ab0ed8450d21ceb573558d7bda91ab03c98736e13d041bcab84fd8248b9", + "0x9077880ac352a5ab0e5e15ac89b14d173cda0b41b6f7fa66bb357195f10cfcf491fad6bdb49d71cc20d99cc6c8e28d04", + "0xa09b2930fb3b1a60af8c5214e8c3f6deecb3fd3d0a5662f3885948f48d1836b5ad3dc74affc54dbeb5b522b90a17dc4d", + "0x9163bd2e5f58fb1d81007422b91147685542fb1c7e2c8421af284c7cbfdcd2d2b399a37123b58a2a349f27b31bfa47ab", + "0x8a3d859f141457f9d63818634f81deb5c858ac48bfbf2e1da21f4f0dcd66b3e1d2d8fe99c4cad38206b1e15dad94934d", + "0x86d3fec476b59782d0477ff333fa79922fb9fe3d6d6b6c5be9da9e88b006b46b2a0f8f86ba4159c5085e66e32fba67a3", + "0x8041cd57335bcdddd37651de2c3e92edc600ac23041d0e383baf55651b1b0960b6a601491608307160f0d7d48ce395f9", + "0x805c284059f8c03b2bf006b1af95ef726874c5548e93ea965b402931f42b189f9f674b6b52ff09df35320085172973c5", + "0x8acf781a0b40cc56b1013cc1fc3bc43036545ce35591f3b905543c09cb1ac1a70a074202b6d5ce3680be913200c58879", + "0xae670c448996156c80d063f1dfb03d7770201a35c71cf8e70b38d52dcb5e2bf73d5286d63ba2f561525d62cd67d43125", + "0xb0fcd0150fc0005ca438d6b0fdd6a70b121d35ecd74e62bc119bb0187cdf6bf674ce9fe01eeac5d46a68ff4d4210ad09", + "0xb752c6850985ab13a057028887bc84674697c012e9da0265dd5ce1e48f0aeddce5e07e3e7cb68ae17a648cd1207eef19", + "0xa6a5c71915a980fd0225847b45e2e9f3731c6b2a627cefb1e2c6a0cd7f1d0555dd32b6b601a7ae9cfc4b9d06a56a578a", + "0xb7d96f59a988a7a810c25018f7f85cd6e81b335a84504ec76c97d7257f9cbfe88215ec89553f0dbf39507d990b3a7f84", + "0xa7cea7b3ba43cf6ecc488c34511b17fc7b97150b2d265785c09c676ad3123b322db32e043c5961384ed6d90d20c63061", + "0x809dc467b304e9bda732cd92b15c0f9b363cc707432788971508b8d60844911ed4edfca96d8cc20b9874f1e38a2d1685", + "0xa5b6a089e022fe460d62c4c5228e1381902c9a796ad92c03211c855541a7fe27c5a39d9123b001b0b892ffdf0a1fa065", + "0x95d67a21154a49bcdc79ed5f2773b651c81fba1ad82bd373239f09a67a50371a147310623fcbc1211ac57aa154e8b300", + "0xa4a4f0ca8073407575dfd5d04ebf76f8bb467598824f2ce7fa74756803d9645d63c9eb3ed39aa202dabafa4ff0a0bf34", + "0x8a77374f6e449d94a443f2d4593a0c3e4925527e0653e873dc20756396a9a4e5696fe44fc1b49e456711259deeb3f037", + "0x82585a825011d6eefa85cd530685b103862aa0777510d22942d8f77a0a7f489f5d10e5b36ee38f66cc96dc57d13f5893", + "0x98e24625c31d5d97c789eacb91c3d51cc6edb38cedcc474deee459f55de557c42e4d0754ca4ce472d0123638eeafb55b", + "0xad4351c76d96c35ee37362f2384ffb809bf6a47213863330aeac1ff9be2c6cc7275f0f974e46bfb716a89ce1bdbd0710", + "0xafc8f5af4f9c38ae672d20e7bc3796aba23a41eb033619b4c0a06e07884e1e0c7a7326f069068dd22e69fa5f672efece", + "0x983d5af05af31f9082f381378fca3526f88309bbe51d0cea5860813bb0fcf6b32a3be110336bd728952dcd6ff8a26361", + "0xad3b55b67b64b188447a1fb10d027bf7f86ce0a0fac966d709e8b6ccdbb7333964045f0c4719c45c36b7f3c9ff73944b", + "0xb410fde230d8dd24b9f1bdbce8338b05110b130591913f23a34c5fd092cdd3f747c383f6967cdb529ade1a264a3ece39", + "0xb3e4f0a046f93c332be07058db00c5182a498987759315bcc3a58d9334e09a59333031c3144b59d03596925703491cd6", + "0xb77e58619c8c471531d9b2e5dce8f82bb8794223bc9459599a911440e64e0b5be1d37e289807733ddbc2858bded1c34c", + "0xb450945bc3e290df96a196083a45aa929ee080bf45112e678eac0a939db2ba67334ef782c855b9b354caccd94b3babb4", + "0x9794d81e968770a6e12add60b32ccbbe80cb2680b157d125461cc3db998691e836d98cb3b3cfff4f156b2800d426b955", + "0x98d1284b4c035e93b4ea0431884d91d5a7855ac6c5b1ea2a994e653cf77f0ac1a771dc75899bd1485066da17e40ee341", + "0xb1da89b14efc14d15b2bc967ffab85c41dc447b6a7922b619b5d5b06dcda725bc4530959b70355ee20eee7c1802601b9", + "0xb8e50ae98515dbd9ccaf27192e58a5c34def86b1d0d181e63e64439107c30254267969f6069e0b863c254440c3480de3", + "0x915f0c7dc95f630bf1114b02e7d9936b0911a69c932950ecb7f800cb1aa1a4e1f1b6bef6ff4a23301cfd904c39025863", + "0x85392fe0edd316031c69d90b106b6685bed56a0d8d814db2cd5d77d07b18fadb632694a214a176ef60aa0f82ea14b00e", + "0xae4cdff23859b7570179586549165c728de4ca254a5da29668cfda259d43a387b3caea8537888d43f713d458da6bd4e8", + "0xaa0b6a3e0555d64a5cd1201fdff7ba7ff3019e9ada1d86c90c626a710df3d97d2ed62d2b63e5632963e09cfbedf83732", + "0xadd726d97dcff922dfd748eb897e540a2b4b8bdbb4eac1feb74717bf086b1760a957f83586a57b5345bf4c73d791ab9e", + "0x9721889b6fd55cf9a914e5aeefdfbfb94d379c6312001ba50ec4bb1dcd03f95fdb45041330da8871cf3dc3c6a6b5e330", + "0x8eb9417573ec6af24a610da5260639efcdfc802a95aba8efa829dd70ff179dec061da9facac95b6af02cba6a8646f7bb", + "0xa477ad7d2885e1f081556a98b3904cd75a4ac7a8c27fb0ccf15d117feca59f891a677fb4ff4fbf38203055a9436ebd1d", + "0x95b3b2ff92e8a0bace130d165984966637a74280d0e056cebdefa6f825b1d55c9bc6e13cc8f263e657dba3dc7fa68627", + "0xb096fc33c038b425a7a922a4274d01eb366a488fc969497a575587ada74b9452a607992aa2d8b9de66705fe20b4abb39", + "0xa813ef1053ea6ae8a37f4da722f16b6ad0213b0ec7829998362292aef68c28357ee27a406b567a629592447db8ea6085", + "0x84248425c3201ed389fa1b64b9e1d151b5a6f5fcb8f5e28ebd665db57156ecf9b2fa77bca857200df9f54383b7c5eae5", + "0x86d0a3c7fa1e64111115469ed0373dc3dbd448e1098250e9e8c5c7e775fd1f267d49b4123c347af07a28e686d5f357fa", + "0x8340b2ef4fc2afab3a3d51b6c0361cef4aec3d5e1d0f779f9fcb258711cb79ba4083508644e2bd182fb25b21523557c1", + "0xb840749c259b5af5874750853b4de6f4d7a274e18fb77f774f5f454c82efc5979a431e28bc8e43bb831715c7fda96db4", + "0xb168d333cf20b053c1b2a915c3485200a7590c3c3661507990390800fb95d3772ec6815d53aec5e2964eaec19833e787", + "0x8f1bb538dd5005384f38f88cd2588228aeb0c6313aede14ccc12affa9715cdb938ed4573c391572f0a7ba6e33a1ace89", + "0xae4a8ec2eb938eec00e6608c087471128b14a773d75a99634671f6fed95f7b24b14f04b3271d1c32faff7f0f2d98547c", + "0xa4ad66552924a6831b657f8b318f303225b2cf29b09790a48285b028bb1420c56dfa2ca0df2e823f694e8e3b27952a01", + "0x8af4eed962eeff534234d7c34f1033c68e8cf798c99880a67eabf38b533570a3776399b883f8658265cd14277b060790", + "0xab2c6406132413cba89a951d919bbe123fe4f220364ec2282d8ee0c140ad8d48ded0df7ab56f8f18ec7526ea2f1cbbd4", + "0x9154df8800e26020155b98f630e640be97a3ac41b182fcdbcf31a3e4f233810e34e224c97df8ef0f39ccca29a9921fb5", + "0x8f306dfc5b8376a88a104cdf67eab54f93e478ca09036eb780050ba2e8112b400bcc09d49665ab37d21b5a2d8440b3c8", + "0xb768260e94bbabaa527b2af8be423577cec3bf4aec3c569a4fb69e1fb997a2157c59f1169065d24a8aa3625d89d988fd", + "0xaf06139ca7d240f2495314d941890c078d504b2bc09d98a6156c373de29781e7581f33adfc738650cad0da3f6e07af88", + "0x849a6e458ab2f4101167cbf75bf47ec1f9e481f556b1b9d297a6b4737584011d7881695bbf3ba31e3e4180696fff6407", + "0xb107e7aff27aa19a4a92d1a65679bf40e85ac6f08d4e5f14859d97c170ceb431858fa4c46d00131527c605164b5f7bfd", + "0xa00666055e18f34ce02e8b67b6f181327ec0a11547c0795bee61802aabef9a3a76ea138b905cebcff9c4c86391763e6c", + "0xa65cd8dec5166949696dcccf031c300895c5fdd53709a1897c61d795dc22bae2f7717e7ae52a9950f9d00471ba6257e7", + "0x8b49aeac3550ef28b5de37576a5d4e2e43bcce82de09f491984171251e26c27fd0a884daa6f3d30dda107dde4544b34f", + "0x91666b88be09799c7de9a5d9a9d4c1bc1b6fbc44c664adb15a2eb27229be910226514c2ce22818fd38b850c89291a7fb", + "0x85abf4084c735b20333b1c2145571b793f96188850bae161160b47dea7c48b0f588adcbe9cf80e05d17851cfe3400f1d", + "0xaedaee73c52d71d7ac3854fa41199615ecf49cb0c35d8203f95175d1ddf565499a8e9cb8d31d89e7cd9cb75a9fb56f9d", + "0x9413589f0746d3b81e2f88b280e354fbd63ac164369dec353e6259a2c4acc6bbcc10f2a851901f39f90da7e523d77848", + "0x826121abbcefe3ad431c713a1a2cef336a0f06f69980a14d0a8adae5640e9aeebf4eb82be4621165ba32ce5e16de4880", + "0xadbff68221279985891e9f3fdb7b1dc71db3e20213b7c8e1931e6f75c6f02e7a1f6f05ec0687885de55ac85440f372ae", + "0x99ce8b064f874cf028e85281bbfa43145893f80a8b12813d047bedbf88699266652de6ae9e4ef9ce575e67065854fdb4", + "0xa809a71a663b0a9719c0327d33215b63c6ebb12da3477da8534d7e8f79fb81e06adfdad79686e40efb2c75abde559a34", + "0xb26c4cd057118f9b12c9b86e77d370b3fdbf2654a5d80a7763ae98c68cc2769a7cb293ea89b3a08250c2f699b8d76e22", + "0x867c56da9a2ed672f47924cce82c9d7e801d6a1fd18cdfdbbe07c82091c70ba0ebc6008b0b9d505632a97aa23c45b8c2", + "0x8cf14633888f2ba0b02fc8ca7536f39fa290678c7e0840c58c53a9d2fe10628be343a86acd74b2fc01b0c03af0996f59", + "0x86696802e4f27928dd6b0287d0188f8067283496d154060383c5ee295a468df32a2e8e24648d93ba868120ac429b68cc", + "0xb15439762d0f7b6c98e6946b3c0a7ea0521845fc68b47fe9c673194d81a6cb375c79b0122e81a027f21a7fa4cd6bbf56", + "0xb1bc19c9a3756098c02bfe36429c0f0d8166a5c9274edc7f80ce65ae7d6c67864a457f19cfde6924d204b81f2a195fe6", + "0x997f1cc78d707f29e3eea0952b5514b34c2cf0720f33a3244cc466df62b13031bea13df2296270eed42b3667c53d6c26", + "0x94f599c9995caffc9b47543b822dd8f84f921fe2a31e82d5d0fc79dd93a4da0b87a0906b82fe7c2a8c23c7829c21dc2d", + "0xa7fc8a6ed802660bcc07d3ca454c415da18d798719dc2688eeafeb8971910377ce909de68721fd97c4d9fe439f37a8d7", + "0xab16f93e6df2464018be01fe040fea08c67e0b032fe1950fa37c7593c8ecbca24dcf0fdb9e1209d5b0def622f3f6e92d", + "0xaeaf19b49843e3fac538075dccbb29a63d55d12f8c4150185b1ae62de778c983632542eb495808ba629cd4cbd629e07e", + "0x85614d537efaee823452d0427ea3a2f7d5a3c988b10cf7adef8715becaa519a9b5174b63e401946362176dc0d65667d4", + "0xaa08d8365e78efc1919cbbe562be7b28c57eb05c36e8da89378cfcad9f21e134eed923559530aa3f62bec758b00c70ff", + "0xb4c2760454170276885d66f03e9fc4e6a4254547b04fea3c233c11dfbf71ab05dd755b9697b442ec419aca000152f2a8", + "0xb814059b189c0ed46f9dab604fca25d881a12fdfaf834a75cc2c0e1d8454ce0ed9f2a79b34bc5e27004903a48c6ace90", + "0x847707b0aeb4fe91c12ea8570cf0d16caece8946951360433c8a375a69fa4c01136172ff2acab6d5164ff6d3b5340858", + "0xa7a9304ecc5ff6fdaaba6e774556bcd7c5dfe8ee7580a677301dece55c6a8564e7c64b60fc4efe89ff73954f3c3f5b0f", + "0xa1a86fc5648edd58cc7eb61cc30c62edb5314caca5551ffedf088fc9c1b92ec5b487f670c5bcd2127067e8fd5faff03c", + "0x9086a31715283fd525034d59d4ba3465d6c30059b967b1eeb7d537f3bf8caf6879481ada2849167e997212f3300f8ff3", + "0x99c11903cebf722e1cfd63a46b0ae93312439ff2f014b6653fc61927ba430c432b4955b30b7f078c340f5aad4ae24313", + "0x934b7a8b7bcf0108ed31d35a645d73f661c064a6fc6a5d1ad417ccf1b8864623b0cfb54707f10baa86643afb5c5ec980", + "0x89d5a69ae8cc18ad77995ae92d30236d5a5ef00cc63274e318d18abcf9d936453d18a8e6392b52d2d66b51c18d904d6f", + "0xad2448cea1948f0a4915ab054273bdae33a08c494203d11f46888f852d0abefa310b50367c80cacfb602cbc249b31a71", + "0x807274fbe6f08c332a5d2e2ae12cfabccfb53511b8d83bdc875856cf15ab52c2d01cf706c9be428307ea62fbfd67f87a", + "0xb2f4fee9f32c0ea7fae306605b62d983b130e4d423e2de286bf9f4343b79e5c4545214250cd1348402d8278140c61c00", + "0x8a36f79ab3ee0063098a39382061ec3e1234e67087b9519d0b762aa9cad54a7e0bd5d24e2b0a57a690993e3182f3e83c", + "0x86668e6743a7b6d1ee62e70e6031fc8639ecffed38afdb1afb41d64ec402a308fe0438a22387d9b0c130ed301c39acb4", + "0xb816309d1730cb39b1ab00c5333c6962fd5f5d8b22f3c3ba987b1e0a0065334d206141dcf0e68eba717a4eea533aa6f0", + "0x8754e190b8f751aaf9f8e7076d21bd31db8d9ebbee6b26517b190f624b3a892050312cee9d73cf3d7245446c6a376437", + "0x87826589ac28f442c608faeaf3d63ff057af7724f9d412d1f2cce8c58fad0adde325aa496c6e4e8441775c02d8a74c2c", + "0xaf30e5e32fcb17226edc54030f1eff8af619c207cd9e42a2ded7f15cd29fe52f140901f0925ebe4e997b56f34d3f406a", + "0xa62a4e5b6591d336744481a0797eb23ccd0f580d04cfacbb3e415ae3f273761042b8901b0312f93a6eafc42a50f81cc6", + "0x968a9ccc95e8c124f4475c348a33ad2a52a42e191a93bab3d7f0d211df999aa081efa935391a8289cdc4a5a8f7433822", + "0x93350cd99ab7d3e51756eb01c89172cb406c1debd3f0001d2fa8a01018be5609d73df671e1ff43e612ddbfe7076d9ecb", + "0x8df26dbc565ea7e758ce4c2656b65c1f0396761c9360d7092d12c121d3bc1c293ed28d82f1057f4eb5375b15443e9258", + "0x80a0dc22fb4a12b06cf05ce39f76537eb3db9691ca466ca89b2585237c03d13fe3fcd311ce2b3dbd1b7382044b803782", + "0x818b79cab08e11dff3d55bb0f55333f6340c5b462609d43334c14fd878b0f310b77c542c74d3674a94c692de704e88a9", + "0xad1bda19b1bc3f6d757fe4d189ca82bdcd0a9c1ef509c43e3f49700f84be33bb9b8b8e70f7a09bc6bc00a78cad0cf9e0", + "0xa22ab44c676ba2b3889341fb137dfa14cfc5491ce4c3c1fbe2cb7103fdf720ff2b77806a40109dea9a68d8f072e1c167", + "0x8eba6af1659b6145676d3663b04ebe58c199a1c24837ac4969793f07ed97165d20bb0410421e561cb9283faafd9eb51c", + "0x81b216cf08a29dfc3e16b2865e712e15f494b914cb24526a96799a3078f200a3fd403767119732ca4de07203b479ce8c", + "0xa023ac601c8e0c22553068ce4a7b8361b0b37bef5705fa68a71c3cfa80510041cef3640bec2cdb4f317904521e99443e", + "0xaaaab84c8aea75303fec31694114b3ee10fc1a67357cdd675ac9d0e33c3279e3117d389e9ab017882d517131b14e6088", + "0x8bf9a44b3df3d7e0c776e7ea5eb76f16f1870960f32e7c5b63aee9b432a0adeebbd378c574ed60e15a3abadb409376f4", + "0xa93faee621d930f336f4fd952954ffcbdb261c9dcc4e60cb848362223374010c555a73c0563e7933d1596b0526bf75cb", + "0x88753d0e35e87f7572f2012a40bb757364af5cf6e5dc0dfd16d082e698d3fedfab3c671bd58edbf11cedca247e9fa55a", + "0xb7de5f03681634991d2aa8a0ffdafd223b1a0d1ff70fbd9c00d03f228c6772d93c388c02045461d51326483af97bca37", + "0x81f96d4fbef3cf00da423a1c48ab8acc222016c21f6be3df778342c1d1aa1a420faa8ce906bfcdf955be045efa4b447e", + "0x8dc75ec37122afaf0aafdbea333291ebb735792b4d5934fd16bf28b536fa759dd851e1de448c3efac3d2b0097e0b349c", + "0x9186f66655fc1c551d0233b761c6982a3b8539085ca9a2baebb826091e179026b90f7ba6a825f38c6a09b190a31bace1", + "0xa1cf319c9ed31ffdb2108b684bc21cb495e77c853e6c502e03f2ea08e88a0c2b4e31958004d9879242df420b628acd8f", + "0xb3d3e5a75c34640bb2fbc7b62f8aced8dcb4b9b165992717fdffdf765bfc81fb4e67f3e737e6f70f24d3c24812ec0ed2", + "0x86ee6ce0480f73cc89ce7959b4af52351317cb6406cc368e889472ee5567e8a98560dc1f13b87442c9a8c5d6b31fc446", + "0x9478256948d960e3148acec3487da232fc2ae6818ac2c6eba491adf130c55badfe83f9a519379fc5ed0b63366de86a02", + "0x898a8130718ac6f98ef673fa8b725af6012ef28be3f2320359a5c2c40e479969e5926f1864624ebec10f27594b24f618", + "0x906f45d4ec3f647d0c49deb95884629a04fa65cf91a075bcde67940634cdc98f76fea8717fc1e714ecebb337e9fd6998", + "0x874c5a55bca05fe52a5d1743b8254b642431b720eaa74f73b0faacff2225f448ef94e12585b2d3bcf12c140ee3e81510", + "0x96f76cf34b14263a30df2135131dea00074f2ee853677b94fc32e04cd9872424dd93b32c55026b89c18bdb4e58bfd19d", + "0xb62e2ebd543f3e9a11b72f45275cadf77b1033713625c7374c4d2284d63acaeb64977fd2fdc90145066146c311a68737", + "0xb1759d3b667af9f15da8d4e77440fba4193d0db159a0bf73df32215b2d292bfed7cbaf41c07c7a94ae1f04bab23cefb6", + "0x88423607f005af97b5f8131bdb1fd6d7cdfc4c2da4a4a14bb818b3ecf50c2ae6d3b8cf55e23632354537f5c0dcb0f48a", + "0x8ba63acf22ffc1576935467af19f555a0c27a4b56e5bf752163038f0010fbdbff8a2131124f4cf36a326dfc188740e77", + "0x8b1996a0cdac9c6d896111671ac4dfa84a3a3738c43db6d6788f1a7b8ccd6df16a31606db00cf0107eedab28af05cd7c", + "0x912a604a97457a6b46d48731fb44dbaca26e7cc70a4628dcf553b43a9efddc4e5fb040a1b89e31902888a7cbbf709333", + "0x86eaf5b2fa873bb56b94eb7fc823527ae50364c1bce87e36fc13de149f1fc937af858a25cc477277dc6eddbf9efd5480", + "0xa0169e6e915e7216b83b00b31eeda207a02c9db6825b5ea44134368eae5bd009b7c95005c621e0d258c33c59085cb66c", + "0x8c8ac664946b5e69b4e34ffaa486b745ac8afc8ac702e4a4cc36c59f420a81b31ebf8b875b1f572dad8e4ef1f547a1af", + "0xaa6fd75ca832fe60eda078fc81a1a529364cfa8a4b4fac071d89e33cdbafa7d88ff3df611720b48e6fcdca2e3eeea0da", + "0x8d30857ada34991ce6faa82b4326bc353691ca32aa25511cf3d52cebefb262d6db8d93521020a2d11b3ea085287ad54d", + "0xb78bd8ea8bd6a2fd5741228502b9777177039ac8f033071c82ae11fed7f0a51d8bc64fa9aee44df25eb4b3822d571144", + "0x90904aeb1a99c4818ef21498a583848f4d1ee9253d70c10b03ed7d669b587f8712fd26d4409f00fafc3e26b5d72b4c5e", + "0x87cc8ebf78ff2ad752843792e11aeddbfdc628e03e13e0db598e08b496313f463f481f3a17ec889a3acfd128fb89aa81", + "0xb4fd122c4830f339fc019da6372286d3a0565ac04d4f5ac4f28b2c066ed507316e1b7beb7b552f60060825977a2db9c5", + "0x86e709d48d03738ca97d6140f13effa03137570c43ef00469eb0310909f66061d9fb933fbcf30bf04f13839e36d45a4d", + "0xb4a595cdd219aff5b8d0f80b679e58d9a7ab9cc389b47784484704e7d2c5249981b2b86be4c37ccb11b9afbcc8070214", + "0x97c6bf26c8b28b982b7a56ff867b2f5785b37260b90e0ae680920f368478a3c88f4a47bc394c07bbe88fa1aa1776f255", + "0xaa48418728684c9a10992d1851b69e54529dbc3548fe46721758ac6b33f82254d56738b351d146268fcc56a9b7f05df5", + "0x962a282caf6f08a63aaaf7ed2146dd61d527144f3fdacf1beef36b34356df50302330598b8602f1447f6beb4439a1048", + "0xb55d325499ce03c9b1c35e6aea30622841aff2a2c225276d677338579ce83177c0d64d78e7d11eac657a30648ef702c3", + "0x8a91b9296e5633b3b9144f61e5436654cffaf04623a864ccbcdd21c8f981618a908e890f61c74df19ce5b6995bc358c2", + "0xa7b6b32333377df24c0b0194393a1487a72a8783e06b1cd00ce6bc39337b34ff58ace57c8dee5b7f0ea2c9a54048a61f", + "0x97db4494e4208c9f297b484cb8159e8f600c61a44e1d878b07d29f0406fd32a0c12ebccd42ee7ac4c0bf33ff54a582e8", + "0x8697bc039265f7b6e73c133823dcac9041d18634c68fe16412b4af41286a4164dc86f7e71ab7a493223a84e185cb6f1b", + "0xb18a66cf37f93ca0189201811e7de02ee029445132f0fd4209e5efbcef46ba6a28aaaee42b30cc7e97a25b08f4bbb43d", + "0x8b69f189f3cfc34cc3968a07e13d1cab0f5c7e093027a9fac38504acdf12e2defced4261a686a2fc850336187e017957", + "0x96afba402124d9ff7048200acf329ccb4e35dabcd609e62d04d25140729e110a674849037e4b8aedfc99c889b132cfab", + "0xb75a809fa3b1c17139962bc22ddfce47d38d017d585a4e76ae1eb8f02849551ff7bdae178cb4546067bbab45b7041ddd", + "0x89196f1fe0869f2fd18f5c01118853503d71c4073aed8bd9cfaf694ca4a9e87974a9ad6e37449bafd391a2045ef5cd2b", + "0xae52921b5d8eb5df7d4923aed1afb125cb98aa6606f8cbc2129cfee56ba3cdb7225a30d98ca9271cca67fe39c763d508", + "0x99f1cfd27833fb64905f8678a532aa984329b2369ade3860025ad334131a9550214297bb2f7d3569eed7a9cc558a5922", + "0xa77fabcb76e8c6ac2a5196666e0c75c7f6c73fd8a0a5fca32a454a9457870689c83f5821f90f28dfd91abc3bc62ee761", + "0x92a4b97b7c14ec14c74e06363b0ab2e263d0d7d84125e2cfbf659bbee996a4d8561992e19789e507f4c24e5afbb91b2d", + "0xa2387e7857600a93de57faa0484650289c7553b9ae5fb001d011f43e5bf31c010c9c8b5bb82e7000465b546236e79066", + "0x8641b6f2dbe9f0b83e0a7ad8098b0836af158fa2ee6ff1bcdf3e2ac8b3d25d2e5a24d515e9d549feab4e82b49e468fa3", + "0x937306770a47ab2d5d2eec4bd6d9b3a8ffbb8c8067504571609a7e7a85c665b34ad2662701b67858e01530907172768f", + "0xb6b1b89f261e56b0cee15e2f5284c76789db26a6ca4762500745e260bda40b00b65add4826be6131775202c8c6c4247d", + "0xb1caac20a1b2aeaf287d38d42987e2c381e74495d9e880eda3ff59821d5974d01c7e3c611f4773a13ff41bef0f2ad44c", + "0x81ef049b849d7b0a732579299a86f1cfeb85f27ecee4280066dedf6024159fd47f311f1ebc46b58f63f71735a05480c9", + "0xb3b6b657e64fc154eb33b6056b8279ef736839b56f2c8f8ca438cdaceeb5398b8d3625676cd393c196f664d7baa3a615", + "0xa450678001e8db1ebd8fbd5c808c99945bb3549e834a346cdff316ef8d3b49b818cf9642e5b8097181cf40583ce901b0", + "0xaf3edcbfae3c8f368958cd11c95df4682ed10f894f770783e967fac1eed533ac427c1d4eee51f968ffdef080593ca262", + "0x8348eee6ec1102884929736d6768477029961c3d6d09e9ebf84d2fbe55c0501165f274fc1c0549ab831388d431e051ef", + "0x8d799492659dc44aa38262f8a4ae37b6ba6eb10dd20481f652a1c77ee9a4529efe042ea873c13bb2ba3ec4792b167c14", + "0xb4d3962f574c3298ffb0958ac999367db8207dacf2ca9d563cc1efb42fc889e19b7f00db15ffa91d145ff05eed97c3bf", + "0xa3a7c0e45dc8ae816d8765bbf097502b56651c0c11a03f476e362b64ddaee223128defbcec5629f4d7f1f9c3e4cb9f2f", + "0x951036c2878582d84d90dff79ecaca673df4760fbf9e09e63d35facf3e3257be6e1bd504f3c3daf8ac1e91d306e80d6a", + "0x8ae85094b13d349e60c8f303550cf4b01e96e24fa3a9f12d44c9822c004f1b3e9cbd772a2b4699e54023176074778993", + "0xa7292b61d2667d74cf62a47aeb559499f19dfab2a9f41f16e7b8d6e77909457eb2aeefadd9d3d3f6db18a438ae53ea0d", + "0x804310f5d2ce8bcf9095945f931eecff79f999ffdd24abb9e91d92f6e405decccffe4a8d9e731c4553de79baf7a5dd98", + "0xa77d3af0fb79b6f5b6cb640d04f4e13a28f8aaad1f60e732b88f86de547b33117386636d1afc7bfb7bd1d4e527812365", + "0xa431f239ffc68f6b1ea13bbd45675f0323cacb279e11a14f664acbb15d1673b99cf3603b335a100a0e297c305d743383", + "0xa64f4c28cc36b86dca65359cfdb50ed3dcc06fdb22ad567c7e0f833c880e76a53c330720fc2b96235cb0638394bae41e", + "0xb6fcd2c047de58003e9af3a416a2cdb143899441d82c691fa46d89045a12d3b087ee4603b401287a0f2629154bfc9bdc", + "0xa06e3b863bd183d8f91dea6d0211913663b3924f1e3476cfe0f328ff7c388aeb8e5c97757bcb56992c104ce0ab6ff27c", + "0xaea78204081cf5d24162686a824ff8e72fc0f88388525d646af7739265f60695b7d80b53cd1ddfd046bfcf59aa25f5cb", + "0xa89f556d42541a655864adcc1d5d67459ab488143e1b4eb48c67af30a8e753541fbcb479558ac26e1fa498f74a59025e", + "0xafc385b6b08c355a05fdc75e9360f4ffb384fcd74e8c9db34bbae9e0c67e0d1fa7efbff4160b387428ed58e129fcc027", + "0x9428d05e17e5525fae515e1ba3f04742fad1a43baa2ee166d2f9431dabb46895b7345ad833d495c99939f0c57cbaf1c3", + "0xb7a62d36ae55e681d48c911e1a433b568871c65a97916f939bfd638a054d6f1136a78c96640779ce1e3afcf90e3bb23f", + "0xa45b6d24930d91fc610e57ee78c6dc7557cb2ad976cb92e2157769447cd7c9a6a040f1008be9eb5dda2a7b8c9e524774", + "0x8b24eddad804790df3ed82db7c0ba05082c61a81763c44c98ad436dcc7e1e89a2800ff9c2deaf350f6222cf4278fdf9b", + "0x895409dc0aba4d29ff322d2414b33c1458126c023a3d53b25b9038bb90372b7c20d3e9f6b791fcf8f76449fa0aafa758", + "0xb22767ed218b575f397ad8306ec48fe07e8dc3a9f2f090fbaee411b6ba673a1258785d61adcba007d748cb019c458fd3", + "0xad4b9e4164010c4ba05a23f9a46957c8625fd4281a4e76f76ef7b4d6040d2228dbd2e6faf22b4a966ab42f32467a4655", + "0x92340f1051f88c25a915d0504c1413146f37f709ab060e3859b14aff9be7f8c91352dcc3fc866910a84192d301029cc1", + "0xb4e19bae926db3e1e295ba856984b32b796d86cbc81e81c6978e989f5331f27ce9004f90536a741ca996d19f998541c8", + "0x91502e2a69aeac8e709553501311b4392dea3d5b6f14e7523bf780b8af246e1f2bdc4b29fc4ec3ceb725fafa31bf51e0", + "0xb20607db1bdd6136130ba9683d581f5f45d8623ec4a2d35946723e0d8768654bdd9aeed55ba38303d8d1e312bc4f2442", + "0x8fec23ac3b4cde8c18346dda1afb2b72d4af1a6c013dcea36cd8cbf7223626690ce933b920bd9137f673d0985b64d54f", + "0x996bba551ae3b76c5aafadfadfcf80fcb554ff26e6a9e14e60440b3864239129734115d11a89ba79c19e452525cb5a39", + "0xa632f25ec68f02f7758103caf613511a1fa2e529e0861f286b4e490e8fca6874af2c13e3aa6ca97c63f3c621c197ae24", + "0xb332292c6213c7216bb78612457de615da878619024626383914f9c28f835f1289818514038c30eb2bc3566d2da470b4", + "0xb5bd5ed7e990ed8abf7de268aa1ef7ccf5562cf9c92486c2472051c1b5506bc9e72594380e7bd00c91771ed4e9707851", + "0x8781393278ffd5c522ec450220698328e60294ae1e35f60b25baa290a125cc47fbf7435eaf9b22ea819d431de0656f38", + "0x80a308c1acc4363f9bc54e6831c5aebca2b2af47d699a17ae2fba24495984acd4a25c7c95b96aeae3027f0fef9549284", + "0x94a55b36389e05b848c6d0e6426a400d1596195c2cfb4a972b6bf8abde2cf86a932b769a90b62a65d0aaf388e66d516f", + "0x8d29a5db4ab3a1199946a79ebaee9de225284f0523637f90e4ac16fc609dd3dd5a71072c30e869fdf6f057b7806ec254", + "0x99caa565547b13953b91f0468b78551784d947b5a3fe1b7278e4a45b294f074a93281e9ee084647d1b24c83b39a0cc90", + "0xaeee1c88769e7bae12f163a056d19b0090c7fd866d451963bc855bda2736c41500bb97a8d72a1a077357419ca94bc3a5", + "0xa94bd8b793a57b4fd79a84daf1f7fed5820bfeb44cfec0248f6aef130fb3219e1bbce68a6a55d332b124e1cc55224c51", + "0x8528607774d780b31417bf85fa3e54a94e4ef6e8cc233ad2a1dc795c68c299abae209c46ba77c33ba74c6ae75ee004a1", + "0x930f2c302a87d6bd159bd6b4db43212e7c806e17f572277ab14dd9715a435bd67b3624a9e72d9a2777f9b2080ef5cc36", + "0xb50d97fd2fbe60105dd1dd44cd12d8ad62b8a3127329f969be917fbf10132f1c6c6fda8029deb990fa1ed26e8c220c39", + "0xb685aea07aa1a45941f5eb2a593c0d97ecb5a803fd2977783488fb00fe6580c41ab83ab6cdd678704311c5542129c510", + "0x8cec65b68f4b3b10d032d39ec4c448e6d76e7615560bb754a53c4c6929c2470a884e7d39d9f3e58a2a9f121ad4175a34", + "0x96279388cc3e91dba49763ef50faa7550c3b4c277b2a0b0ae3541a2f990f9352748db75755a7b13efaffc9b8df40c74e", + "0xa7599c33614456b1b02b57921cb76b01109811a82f230f9e7e82675d57757f06021ac3f514d557ed9f2dec025364284c", + "0x869684197084f42dfd95350f8a54b0c7d940ceae2bbe49ec18fcfd178b6b0d21903447509e0ef356aa3d2aee83701bb3", + "0x85e9ab73165878b93e0229e3384f048e9651ae29980f9c5e26492c45e180e09a3af9058fada434d1c398b43d99d13056", + "0xa453a46ae96e6330c1b315d1b5f37d160731309d49d13d6c38c5d7f0b4f23ff1d18c985c471564afb54e4477c5d28d19", + "0xa5999c704320d4468f94d647d83c9e8720c19782d2a03677143c7216dc434b3160d193389b0115dc638f6e2e12f2d441", + "0xabc7a466cd848304616b2eca049c4b7509c5260c9236dc1432044ebe3e912afcc3a6ffe3e27d5d79d3ad4636ecda09a4", + "0x89ca07faeef1118c6b840a2c328fd32a5709b31850057302a7e607891e11f3f9f62e4fafd420564ff10a35b9a44c0f06", + "0xb0002f9d2a8aa850b9f22dd8d3b7881e8656cfc53e6c2ae6a913d88f6934e0062f30da2702dcebfbfafe36785203cefd", + "0xb8527c70bc791c87f5fbc67e2856e45b7254c5a0b673d4a5d3e9b79fe0715b608a2f35d88a61eb1d8d7cb615fea650bc", + "0xb9be558dbe778ba11fac7080789522fc004510f7b740c42023d850946933362a173267106aea046f338533e4cb29aea6", + "0xb021f9e635e64d3c9b4ecc8075fb74cf0e5727ecbacad15f822c8608f0d981ad2c300fe6e47c6148a6b1a13cf920d85d", + "0xae59f2a83a1384ef0b5613e8843cc9a934f7126430df7cd7f5a8508e3d83aba83bf3d18be7380570b24ba0e00e05e0e8", + "0xb403e4d0495a0137a710c43393798593bf131cb8d49beb0f3b3d344554dfc3355ebee14e884f543bb94bf9aae40aac59", + "0xa73b722287df7558c503f89d113fe0c017765c73181eeaa9ebe6de5c8a15ffe76fdb85ab93051a6f565653046624216a", + "0xa7d1a28fe1d36b17e37cf5eac7e27549ce9f6eddcb36203b58797d3372371f3b195cd3432db54aae4bf99768969f5b60", + "0xa3447ece13c415c457b899d4a8b8ff388ba25bc920b5711f8687cc86e9c1b3f3af42c490ec6352fa8609b044e642e3f3", + "0xb12f2ac1e033b6a627e7f7822317f629c896c8f8dd94ad91512855882dbb10b8e80a1e29c3e39138402f1f7e0de673bc", + "0xa7c65988996741bf59888415fc2264495050cb13500b6597d9d0e034898121b605784f681962cfdc80b0af291c316e7e", + "0x8c40cfc07dd7a4bcf514f2e87a1830c911e8168b0b8531a2838d2a14e790922b76c4642ae237b7547d8a3625decc7f0a", + "0xb480d70b57434467a40d6dd066f51b9e637abd2f49dcfa6450460aeec2bc895347e21aa82baa1bec7589b6a5a694fa73", + "0xa919a033c24e96af1eb0cb1ede3684e9a3bc338c7ef37b67cc9e9982586f74072cc540981e2d1a2524e99144bb21a64c", + "0x921e0b350907e9993a596b80f827b2d40aad60e9c62f4b65a67d3fa4c0acfa924c93352dad6eb3e868264bb24904e3a9", + "0x8d5419cea0bfebaa9c1509cd748c8af3869aedc3ae27fdbca3a0f08b3751a3b870e8dd3640f4abd4b46a2a1e745758bc", + "0x8b25e6eb600de81fdd03584fb9db9a7bf4c154ef1482553d7bef880bdc5baa7b64abac6db96fcfc4408329adf8fa351b", + "0x88cdb72bee7a6768b7c24d124dd5e8b29f0c866a0624e5a7c4759962ce1d71de7faa97f7baa56d5f51e35bca43862bee", + "0xaf1d59add7df3b3ba234b0b4f758349225b9cee65691c102294eb7e6fb683d7588fca33ed97eda361060253acfdc36af", + "0xb19370b8fe123f1dd2ea6d5bc75e151b0d1514224f5824437166fce77ac41ac5ecc1e7c1e75b75e948acf04c420efea3", + "0xa1ebfe84f1c012524cb475e68ae6c7cec79fb3372f1380321a0e306d15828613589567efe8bb5784360aed568e26db49", + "0xa0f964e3cb594c359e2308defd3eaec476a638b6e1c216157009e11f7c7d0c33fb9e62c4243057cbca49ba315d4b508f", + "0x9391e5087374e45f03d36f6919463c473938a653adf3880571850374ef0a0e521b25ef84b6012a19a02ec88f0ca3891c", + "0xaeb86d4426d2836e6e10c3277583a37b6684ba35f4f30d2d073043f0a0148f763b99fc42c3935026b56c32e5cd0cecfe", + "0xaa98c07dcfb1b0a708486d83763511c7004896856e851bd83d25a9551efc28f059c3fb8752ece0296964e8c13ec829b0", + "0xa466fd8dc1aea7022a86e12a119b16de35412a1b461680f6a1cec408e9b9c1418a8e406fd4a5656c73488adddf17dfba", + "0x8c9b0e18a033c27731fb3d22b7c83ba7a86fdc2234e8f2a19d7659aa67bad7a85ef25264e8eb81af529feb3fa9340ef3", + "0xa371feccc2f1a1b96ad8a9a7d8db0c06fefb1f2800933134299027459b0eb8cd101b9a37c76c22dcbded01a74b13d465", + "0xaeb34fc2758d8b68d17f15ab3c299344ed630f7351c498a5fe7986f7e14d62e74ac9a8f5d2de7c6289771210539383d2", + "0xaff9e961d0acc71a077e3af52ced373bc694f9154302abc908710e500e908f33bdd10b3c41bb8fa8066758a18d64c667", + "0x98bd5a8751e598896e9aec90649294934f81c36d2d0fb60070e9b96eb47d0988f71d9b68f4c475477eb4c996a9265c13", + "0xb25a92c6260f389f6443a572960e0a52ab9c9250d8760ed148082584b2347ec7d103358c033266bec02374e69d0102fd", + "0xb876968bedba7f4712f5e5eea605c1e5fc40bc5773c61f08c32e0c0f3ec575eed3e13e48809983153beccdbca2123edb", + "0x8c4091ef8946c9b27490099d5c0b47c404b5a1113500592515deab1c3f2778bbe933b09c9824a3a7ccad2141f9b5dcc4", + "0xab85f95d318ce235929531e2e397d09b9906c58958fdff1209a514624a099d3b8c103a51b2fcfa0b17a8f008744b5d71", + "0x9016714cbe49fac5e7b3e493574078c462e18f6363f413270c23da6327731f71e2dba5dbf1da6bbe0e29f57f0c33f869", + "0x8c90df700c0e2d104ce7b76be7899209136498999f78195cd888aec6f069778d657e5032ad7db56381470dd1f519dcf9", + "0x83dea8472e8418aa069a0837a5c44835aa1e00979a217f6295aa35548f509fbafc7db5b31b8767621e4f89957892e8f4", + "0x80a1d673220144973ab70d977b94cd3d6b8fff7f82f23bd4b30ea393952951d2f07c24e6d411b2ec19f3bec13583d9fe", + "0x804864b58f9747bb3ae54c588dff46eb6e16b6d98e0f711828e97d9f019297b743aa2202f823e3153ef5bc4b95da3501", + "0xb08eaae2eca2c64001e1da7d0e345f96dbd3e09888f9ab86f178718ea5a04321a8b8633e72dea68cc05687042808e3b3", + "0xb962f91819dc570c2cf131b89882fb2a44a999b94fd1ea8b83f400e9b66075a35c89f0fe0e8dbc3a597cdd1aa3135888", + "0xa5f33e8f04a2d7aab44e832f8ab4640519aa4ef88b58e0a398e45347492b040043e494de4b355f07cb4bc728b67f1ac9", + "0x8ed80bfb4cd15bb87175cff427c6a1bfc3e6292bc5c2d04dd42b497bc068baac5602d41366448ee7f37d85a5d8437750", + "0x83441e746afadf64583571a9918ba5122ca987e76a6e37f98514b1a8a178380366d10ded5c70d4feb08be6fa6d4bc25a", + "0x8807fb8adb2aaa6833960f435ace162c01a9cd0692a4cf038c89ef7405600868efe7bdb3e8a3db48901367ebafb0a1c0", + "0x82c64b1f77fb78dec00cab089cb7a88ae16c72c94d0870bc92df11587feb62277eb941d2f7d3d2fb033d7bfee12013bb", + "0xab2f1e3f1fcde3b8b2c07135acf3a492ae7675d9bc971ba57e06c99fdfb39e1f68d1c826cd9bba872749cab375e44009", + "0xb4a25f1f5a2aeabc29870ab9a815721f3cc031ab1a55417b457ca6504e5e96e4fd0d2d364ae17738726c8f40cae9c36b", + "0x9519efa4774cb4de4ea834376d6213d946fe6882e2b36342f683762fe50d754765dc301569a836febb2c7c9dbcf44f64", + "0xa75de0d0320e8cee962d6ed4b07db718615e75543fb25f0d28ec5e76f56d72b18d648ae42d7bd3da18f54ec1e4497a08", + "0xa2a17aac11e732097b25c0b9f7b97d807dd78ecd33d88aea5ee0a46a42198d379a241e888ddba940b3307e9c560ec45e", + "0x936ebfc2234d46282ec4de88958553759d766f682d6f9669d2b77a2cb0cf9cea9b1ac02014ac3f5cd47dc5d8af2da314", + "0xb33def3135e7ad61a660ef1266d61216220c7e0bdd867b727ff3deea904072e33a195e4febe64ee1e263349fc9096cdc", + "0x94337e4f14752676a703fab8544ea0ab7acea0ef924b85b05ffb84e4476f1087acc9a6d6250893a32b82f02651a179e2", + "0x8f22942bbeca0118747a22d0aa13438e40bd6a383e310eafacbffa1490f5758504da4a11e6320e1c55b3daabc72c63f9", + "0x86e3ed934fc613d0b3269cf368e32e67f4add59e4dc1ecb1f016fbdc6c53101c2435f95fc36625aa8c69c596acd9b0bc", + "0x86f04807460e1d93f8eea2a284119d889659b5a6b124d41dfb2825b31685361e8163fc3a253a49cf878e316463c9ace8", + "0xb043b2a99b94661ef8b270842fe4d3c51891ec23ba749d9c999982553ecade6f658242b373982c9a3669a886889e4f33", + "0x8b6a33a68ba7b5932ce11b3f0e23c3da580510fa37668f2154c59c3bf788dd2276a2a8c66a6bba1a68084e8b9bbf378e", + "0xb54581c88d4880fa4a0ec6d3c17b6f0ba339e8f7100242efd3b820ac942d75d1f898259d6f1e64a3870fc301d9dea2b5", + "0x9449dc9bce23c7e3b41eb34789dc7765c2f7855f9670c1d145bbd1b2d1b47a9318862ef3738511b4f89cb16669c0af18", + "0x926245ae9d4eb213ebcb88ab2d7e2a7d198557721051fef4cc966cd11be3490a3f83d4ff48f5fb60cbad9c5de4b98d1c", + "0x8518dab07ab15887c68d0de9fe3c0c09ea6bfddb99c145b3f6ff84659e7799da93e97bdd17884b228772398caa8c2ed3", + "0x9969575cbd7953b6308391e9ce2cf4da466b3e730c9cec0e88522258639be35fd31abdedd94b445d7075919482513103", + "0x8b1f28002c19b17d6ac1a6f50afc3448f390b3209b1a76a9a024ceaa274de4588ce82a891a03e878ea08747ae5d98211", + "0xa611963d1bc45b60ffe6756a743ab379e4022bb3fb263f5f305a615c92432199c7e1060a79aa42f7662fa89a0812a4d3", + "0xa3c7706ab74e976464fc341e5a9f7284264c1610fbff02fc36b88e15d6859fbf40fd8c5e93c8237b97acaa0900a03764", + "0xaa623fb8892dbbf4fc02004a44e07c21a422e5553e4b02fcca24dc1f416a54eed36f2f7376dc1e66218e850772676e99", + "0x8133cccf10b1686bf53143bd3520515ec72e7295f6945c43bcef7304de597b767265a3a9f7b281fa353acbc3cf6997f1", + "0x852e4aaf4da9dafc988d0da13a7f31fe8403f6bdab88dec363eb8cb8d3e64c48ff34102f6660642749d11d69b613f8de", + "0xa616028c6cd54a6514fd9f7aa9ff13000eaaf39f582441f73a3ed8208a513b580eb7874b5cd0b1e9a542c40c5887bdef", + "0xa48ec58bc3bd4b512c21d3d55618e9c51836efa97cad42bf79e748542804114714db23d79ad03e410e0989055c9bd46b", + "0xab480f3750420119ccfcf8d32c4a18ca580ce88bffe81433c1d6999c221c8aac482de5c0e41a5531806bd17897698d6c", + "0x8522bf3b7157cd29e948afc8f479d6192364a11f85dd5c58d4ea0443aa6b655f55a80e6a3152fc02a8eea4c0815fcf19", + "0x86c91a6021e738103031c1ece906ff43227eb23088e5ce1b6a1cd58664d4a80d7bbcb0d56c3b0e02cba1e1c2ca22e058", + "0x8ee51a59ce6becf098256e19c9aae5ef0c2c9e66c587d9a32cb4ba1ee0b64c13e2e008908e35f43314316508956654ce", + "0xb94766a0fb91c8de2338a68c4ab08ce5bcf62f6efa221067807dc647b595fe5a342d7122111540a1ca6ea7743b6ee772", + "0x83f917b8f6aaeb9eb2eb742546e3f2dfc9cfe00cfec60051010113d55dba2421974098c157dc2601902d8f40bc84693b", + "0x996e489890dad3c4dc35faf53d870bf1cd76f1dc24e0cc8a1f899bdb44e89dbfc77fb11f7b33c270a1394c909f7a27f5", + "0xa89936283190b2d1ce8d166b36694afddb4c3df01bfb1fa7bae69c55d1acb4e68e5e29867ea33eee8031029b3c6409b1", + "0xb08e5a5d6797ca252d12428b2086e528a6e5c3965d2e5ff2bf83bc71ae9c0346a4ceb3bb2f2e3f8a1685fc343f36997e", + "0xa05bd12a7a6d52d234a1b3e9ddea7b18d6d41026a0d18251b1761f1cc863064dacf821707cfeef2dd1c02536f584ed94", + "0x87c638feef9c88a9f89d10b56fe4bef6406c1d734cd1f01006e2f2b331196a49c7184c10786e855b3de8978927df42bb", + "0xaa194f3e4d0fc1d3107f9564b13e6274bbbfc7b8c1e73ce6677cc66d9319dc34b5a0e790d6d44c614c11feb50530a252", + "0xb2ab7be7ee9d72d1015e94d006020e758b73f200dde81e89e52cd33f25aced0cd84b8c300413d32565c253edbcd2fb1f", + "0x8ec08b22265aaaf27a84a6cca5f0875a3ebc70fb36c4f5e59d60c55bdf2a4fe11ab7ba4b387f5d668e67682a0978fa46", + "0x93643b9541db11b48e0c84caccc8da9ff7696717aa176ce6d863446ef8d887f3159b0ab6fe1f79fac883a371f6736e93", + "0x8325654fd8388ac96935149165fa3238d0848151a04be57f2386c3304056013efb49febee0a871cfc2ee3c11bb029042", + "0xa2c15cbe5d5167f55f2a454390b61d99601614037fd67fd198968531ca2f84f3c214b971ef300a20a114fabc6c67db0f", + "0xb40ed63b0367174b5b4b08396afe2385b0f75ec2569fa3cf60f87e1b17fdee888dd66057be2cfb185e9f32df59b7a8eb", + "0xa466d2c8052a115f121177979620385bb07148e202631979f4ffb01e7e0f6fbce28747df9bf70b2168653096aa704fbc", + "0x99395136290cd020cfba0ca896642c245182e2020ca2299be8ebb2f62e2fc62fe0be593838f62681f6632fbdffd640c9", + "0x8e4f081d9a724bb54fafb66297a32f84687493464550c09259cc6f8abf770d076a514ae1d6726cb29349e27ef69a74b8", + "0xa8d5c941e7c03dba0232c763590e93e3d99fa519b0a65996d20dd20deed1d0192738f3b339edac68ad42016223733582", + "0x877baee9ee979be8ce3bef02422e57799dcadc34fefd8bf2baaf945f267883f67211ac5c06246f7b49f1ea5c99550a63", + "0xb6fcc2a73dbbba54760d244bc13e1564a3c61097e9b525b247cc8687ca08625a7330fc6b15e45a3ee508b4d34853d852", + "0xadf720dde6e9b5c63e361d69a2ab46ed73e0deb82f8e30f27ca2b19c2d8fc43e18ac04b4fa029f553f8d7dd79457ecda", + "0x8956c9038f3338f541bae9ef1f5bfad039d532dbbbe7814e3a3d5442d393ea6114aa666559d8a7e3a026c758a17c79d6", + "0x8d6de7f95f30a5a4b3d441781c7f819a0265852ab78b8416227089b489787c8ae9dffbb0bf88acf1b4c4d6b8a29c1a53", + "0x81d4efd71c9d08e9f6d7f7d7a2fa5089e80cc3f8dcc685686aabf3b4c8bd531b4aa07e328c0fde32b638f23eb78de588", + "0xa30053b681ed8328b5d64587b0d38edef0e366a2762cf5068dae177e4f4084c4333f9a5fa5fede93db80f7a8fd5fbf57", + "0xb340ddfaab2dcded58930e5dc2b72cbedd0e79ef652f34356fcf72054a87fc2373bd3aaf8a88af8d4633f73dfa7d9a28", + "0xb9f3a7809be0bf834bd7affa2059d9371b848dd5e5fa93e83e90d9e078a2fd3aea64410a72457c32d33ff1ca11dc9300", + "0xa9a8ce26a38dcf277ed66d75e111b07348101e93d03f446ea72bd903198122f8a08569f7125f6d4ecaeda8c093a00ec4", + "0x81e78b705b44533e2e997f549f46723a5e6b88241d7a86ca20448ae3ab140e967347abaeb8700594a0cddf1e82285abe", + "0x84724094dae5b7ece30cc01b5f2acc8787de57dc0c37a437c3e8e26fc03069b6e8562302a0f1c95de85937f07fe63d3e", + "0x97a715861e5bb715a17a948d6b6a389b89744e8ccd3699fdea9ac3d890fad027b78d436f8012b0abeedd078a20ba91e1", + "0xb710b2e7d87771416aa34ba2d93a044bb118f279fff62c1224c150ebc30f21abff212019f0f38c334daa5a96598ab900", + "0x853034af5ad08c563ed096ab2d0590ea644d372cb400bfb03867092768d90b7432d35c5506378d001f986c59769d6d56", + "0xb340ab52f751e9d516348faddb45f0115ba0619ec9db820f870007e3a4d305ba2bd0b2a58a7576296531fb78886b16f8", + "0xb8ed8feff520009743ca3313899a118df025a61e6e03bd5fd27898a23beab472746ca3636c22ea3835e9526e17c06dc9", + "0x87af435e3e4ef611d6da74c8d98e8d3f3de64ac8748105dc20287a7dc866f57d10a2b854f7e0e09235eee647dae1ab86", + "0x84108b1f0f0ff73a179cb1be1b2ecb4268e7fd2fac3dfc7f6f99889c90a33b4310946909b9eef31b256b8d0e3ba56bf8", + "0xa6b9fe966293e60bd384a1e4d472b0a72544aba41b31172ac8bfc3e19beaf51da54a66625d73a9ae22c7c4d1b0840a30", + "0x92e82e92aa615e198ba3c83c039b0adcf4393b3fbf9721b2e47ab17a84bded2bc8bc2bfe257d2d76162a87e8bc7ce759", + "0xb9286dd48800606b7ff9c3fe2abf5c49ef0a6b981711b5ba1f62952d6fc4a9999bfdf061c4664a019120f15e341925d0", + "0xb5da5dbceaa7e82f30fa5fde88b03ea88e7003a50eeb53e3f3aeaa63aa586900525b42fe1b699451b5d915d1b83c3705", + "0xb06072869fb8526d3077cc61a3c55d54a7a1197bbbcc875aeaf617d7d1eff3dd3ac243e2c76caf57dcdfe306edcab4d7", + "0xb132db9ee3ed16e6d76db9e6e3dcdc2b142cd70b9582518bbdf5415b3bb476ad900d50004dc0ab6b87ba697c6314b4c9", + "0xadca92336f3546ea50b034525fdf548a36049ca82d9d3cec10073e7cca186227cd662d4d66673e7214a6ed58cf75da6f", + "0x81bbb3fa241f9514575fb3f6cba8e34301187681354c94e7976a4205c0bb238dab52b29a76a5f0e0d4cb1bc82f8857c7", + "0x91008dda2bb7dfffd6746e3544ef540d9a1ac7ee9c68ca9984a1d81041a18fa9f35b8c4bdb44ef3a860c37481d5e9a14", + "0x8224195cf18ca0d8f01521a0ea92c9c598c556746c825a4dda49ecbe324d570a96775eb81dde1d3a14aa3660d50e27a4", + "0x8b355eeadef5fc7cececee71aec3ed30349df8f43f25da1d75d62ab00fc73702b405fab6d422053c2b0fbc7469ace9a3", + "0xa4d657dbf2bb30c1e57e0b63960663bd86ce17204979a9ab82624943ea370119f040b58b067a05ff6d1867a22a58698a", + "0x9379a367c918b2be61a9a42a495ec03f0168a4ec36f753dd37eac6e9f58a26c8510ae7b579a89afdee1d192edefb4bb3", + "0x85b37bddc80754f0432573204a1a4b86a550bfe9689f6c710a61810aa94dedeb28763ece40f28fb3a6f3791ca4c86b8b", + "0xb41c3269b96e190e40cc16e6c7cc8054cd0b7902a43c69b79d8ce471a417d3096b2271badfcdc59deb6271ad3e5a35b4", + "0x941185020a227b7a995f59805c8900f7f6ecff1e7b948a8b714f85a54449a0d41e28db5e17874e018eab72ade20eede0", + "0x8a0795ce082f74e4633acb1649b52b46ea2b4360860fef6ec107910e245b30466bfee8ce59a6854f866f55ec5cc7bbd1", + "0x931fa63550530af5a7ee24964b8b4d0c66c2bd59108131f375c7de86bce59cf52890191ec8540666c895e832dc312360", + "0x8fb86918190a3455014a5cbd15c7b490d68c10cb7b505e9233b3eacdf52e63299d49ded75fd74f8c2bcb3632a9c29d14", + "0x92c896826c9d871a83c4609f9988cec0db6fc980c8b88a7baeea2856ec2a0a56c3d5a846a87d03393dea966b534aa8c4", + "0xa9d4c780c94384f5a13cab61c734836f5729482cde62f2888648a44317b749135b511668834d49296ed47c0a3b9fa8b8", + "0xb7c26da09c3998367063fad19340f53217e8545535d376815773e201ef49e9e1b6bf1423b0b6bb363586f5f05307fc89", + "0x8c445b3655f1f554c2a7f6f7d035121939a8987837dcb1a1663586614dcf2cf47f73633950d8803e2781baaac52c12c8", + "0x8764f924f41d8c5c91fcd77de26ee3bbb86d5a5bfbcc45188be453c8dbe4b875fbc5ef5b01ea3a26b889d7b45417f173", + "0x8605a8186d5716dd5f955a7125619bc72ff385cdecb187a9a646a4bdf6595d67f00e777836261f3a69c19d2e2cae27d6", + "0xa97dca2185e4fcd7583b1e695333d55f54edd751da436b8982de8c344b5f57e35ddb61ad4a611dcde08e287c78c757c9", + "0xb11c576a049f93e0731652f1a1ade62b0124cb7b4e2b13f6505206c27ebf7998ebdb3d887bed01e43ce5c24714903aff", + "0xa46dc516b8ab4aabe35f38af1236052564b01d66c558e7107175064a5226713e8550912867eafe4da133f56950df57c8", + "0xa13e75bca5bd3b08030205cef4faa56a49e5d7da94bc41c708deb2f65343c1687aff26368915a490b89006185f18fda4", + "0x8ef5135a6f1f635a4966aa540cb877dc98c6a88fe462be3226c1a270c82cad8e091aa49ad39862f012edb3c93d15fb4c", + "0x99158ace79ceed67b6d8e884050c6fb7c7a1509e41f0d2b9069ce8dea392f17f88303d0942cf3c0af3ea52d3194123a3", + "0x8805c76ada9dc7e57545a5e1a874e6105592601213e22c1601b0b157b622e51f004a1da754a8fccc8f2a2241c14e21a6", + "0xac3dfe87e17ccda6196f621008716a14be4b983d187265eabb8f5eba7268cf770a70ffa19d1c7e77fab0373eca7a4045", + "0xad78a31ad6f2c84f6e5348f33631d876daa3d5978f6d1a77db80aa219e12c9ea656e9c18e6316f899bbf6c2469cdee37", + "0x8c8726f8f6fdc40516bb64b6c624a6eb4caa931e3a9ca8ce2c31c282ad59f0624ea290b804ba84e339e83422070df419", + "0x9303d1906cf416a184e15f13cf7dbdca5fb296b078079782c9044b9cbfdf06b0c965305a8d88678b53f0a10220e56f4f", + "0x99b9735a77cdc1c675988e613b3e8843e2b0469030a33f5c14383803a1b20e328d45d2fde6ff0d15f6bc2eb8da4f4d88", + "0x892a18f4ceae3fe7cde8f32b84c6bd3d9ca867143a30fab4f939281cec12587929faf07225725bf33ddf154b90972214", + "0xa100a35a2865bb465830ce2f68406d8a92bdeb21056bcba28c0ce8ce5ddfec6e293e926d764499e53facbbacd3f72994", + "0xb797ab22a57520a0584edff499cd1aa1663d8b3f411faa542022c5f1a645a3f952f9164f61d200e4500673a8d95a938c", + "0xb1a457d100def2e26b2b30617ee866264a3ea649bcd9edc7be132f5cad02f3209f5dccb02b95a462b5af9a71fb88a341", + "0x84c1f6d4f29869a359cf89118b1a80224cb574393fb557d1c61730a1fb1884895c4cb07f23c52165975b89fe9d6f5a77", + "0xb6d53e49025bcd1d7960ce46d4f64ff8f29e4239fde1b19e5167d506b086152da3bc3b86fec8ea531a20afe1c785fa59", + "0x9635b053c03d1be0bdf81e9876c63e8541b793ddeeb2a8f3ab0e44fb78f81a9e61f8c68ce393c7c959b62b67f9724409", + "0xa19ca9ac5a345c96a607f979a958d83eef4350ebc9cea0e0aa11469dc554fcc39d9b22f8a3c92de599ca08ff4152ec23", + "0x8e7d45d35f6fb95799846fab51b0ff2415857bb54b049694c1ebf93f45167b8497c3341b656f194edd5804195a7c96bd", + "0x87c05c7d5834394507ad3d363dd0ca5132a7763644e354c3b7a803fa594d951084d37942f59211660f10098cf49adcdd", + "0xb276246af578557aad38190878111e804db0f29846185d7033c913a31e7657d035114448ddfed2f3d75c04c79ee01e79", + "0x868bbcf14f96547192053823e4f85b50fb988da5c4cf73f5cbf23953252b665ef7aea4421c8baec90522f58f027a2b19", + "0xac2be3dcb8082b64a3745ce0d2b97cf341483713e1bcbb37369123d6723968d3bad1410467aac7fcd3b623bfb1d90d9b", + "0xb1e5cf361e0857373814e8db7fc275ccc1dbac8559e8487cc892bf82d4c6be00d1b2ffe40289692a70072c5f80dbacf6", + "0x98e16a5854635c72bce6e263bb57c071194df076e1ddd81e645884367b730d4d557ebb8c74b3c582e09046d2b9ad8078", + "0xa0016bfaa348d44a3ef814b348f7d56fa83b78baeed4a7b58617b6f4772dfa990e912ebf91c2321307884be85dbf81fa", + "0x85690a2c5cec392b6f98cd2d03e4204cc51868612543c7a3112066ebeefd4304c5c8b21da44534224398648b413634f8", + "0xa3a1d00d0fdd8c8cfee153347d590ed78cce48eeeb7ad42032a95baa73cc458d46882d0e9707f3dd519b1844f236bcdb", + "0xaaf2774fb26da59c115a28d86f0c6320368fc6d2c0bc2b7e4516cdfce3058cb423b0026b6c75030ddace9ccb7f058227", + "0xaf507cef7320bd003526fdf43c04af46beaaca5b6ddcad835ae14da60a2ce732b453d8164553e95f2b776df55ddb5efa", + "0xb2656c07a8ba2a2248d0313a7795b99f5acc120648c08e3a77fff5cb9b861827c94d4f2f99a2f2dec1d1667ca3ab26af", + "0xb426b97a51f0439f2da2d0d934693aaf52482bbb48893de12fbdbed1b2155e30791e7098baa18f93ecc45f8dea4f22aa", + "0xa71a7e08426518ef7307c2a1be7aaacd843794601c0d49f4f0e474098ea0faff74fb5ae2bee416aab849afe04be434cb", + "0xb6d510022dd3b9ca35e93ddd2ae77877967dd6966706f339b2197d2891bf523b5d55b7cdc80274368333f9249b62a7fb", + "0x95d2f6cec1b4038f56c571ee0f5aa14fe5fe7b9a2efab89eab4c51a696d2ada549a42095245bea14d7f7ffc69ade417b", + "0x89147eec9de685483d0a5e21b877cb550518a1bbcba0ee65e9519c294fb0c422a729bb0f5a8c8e3fe77070e1a89fcdb2", + "0xa66e7116eb277ba900c06fa48baf274e2a6865977698a504dcc1d0c20f90a7030bb2a841fdbfaa5c8ef6d81aac4fced7", + "0x815053a8483ce2a84a34f81909bc3eabefdce59140f0fda6da77ec005e5dcfdbc6f289d0f0513efbbeef0358daf94025", + "0xb480d2b6320ebf29f3781f04dd88e835ad81d2c63b716f6f244fd2b113ba3781001a34189df586cd629e70c2baa0e5cb", + "0xa74281bddc3a93503a695f0375121b3bdf98db4b2b053eb2cf0773647f6f69d1d98a61efcf82e2a823035ce803b82001", + "0xb84fb99a6943447cad21bfe2b34dd8da43d349e53e85b73fba8a5fd0fe3f41e7dc629960b3325d08af1544e5dc66de28", + "0xa8d11ccfb0dec31b39efeee74c58536f29abb02d06dfa11acb7134cac626a17ff4e204d1d138a472c63c629b6f8406c4", + "0xb5017d42a2388d90bcf4d0b6e015c63612a0864b2b379e9cebcf2e869e5fd45d2713bc549ea472d77e82fa8750f364b7", + "0x83c8e090de4ab6ed169a033aa4ab84f7f3e2b54186106790b30741e9d87d9a5d61bd6a285447e0d1a8e1865ee618a91d", + "0x8db64f3a1680cf461f9afaed4e714709d37559071bcee52e13feb5627c1fa7c093fc8923ede3e70db07563d2d1eae69f", + "0xb6d20dce2f50b78b094949e64edc2ce1b077a3258692ecc2cdaa01ec19add246d0832a319bb0d4153198e3a35091d86e", + "0xa61e585ed55dedfad57352d2abbf8ab336a999a5abbaefeb5d0da9fb0d5bb791119e52034844ffeecca9655675d17228", + "0x8ff58b27196f589ce0d3461e0c00d695da47a79809719b4bd4a229ea7bc9319469744f2254be4469092b1a27532439e8", + "0xb5edaf7c3f9dad7a54908da0e7a153d69a6bdb99fde07fc42928a0dd38031e32dec81c864147066412a8ca240e7dfd0d", + "0xade064bb3f87431a32b361074a89dd280cc1160a57fb3cf21eea5066e886c7bfc3655fe39099a1913b8b53242b23b2ff", + "0x9169621f97887db46384b43ca24b1447e23fcf5abf141e70fcd1834e9d691b9bfc6e8059d060bebdf9922608593bb972", + "0x8727bb06fadf0633fb8137a54d6912cedda0bbeb0f93af97deef3490b1b47e58fdb37a972dbab1534a5172ff0c840114", + "0x91991b98243bd7c138bcb60cf703a9d0828f6791eff5c2c1c5cc7e8edda258d3cf72680bff2c563c8e964f87450a3037", + "0xa1bddb74f5892597ac687451b932449305d6deba20e97e10989bae311d532a7b72a3fab08dd832589e6a22c0fcb548dc", + "0xafc52ed64208e4beb029d1428697fea6add02210f613551d1e5ba6011c5d13f66ce26b3dd2a39b30186c566b1af66c06", + "0x929bb88a9e30862be5f45c002c11537780d151f9836edeadcaa4a617b0bf958046ce331e15bee646f9eeb4d9ff854661", + "0xb3376241793ab9f1732997cdf515b9114f88bb2c25c0bd3f3b22e5b665e1ae94fa3f6a9f88de37b7792c3aafddc682a2", + "0x88fef7680a7fb665043264c9733dcbd23e20628909278711aad2e54f2eb8fa3d07011f593069b6ba7ed312d9ddc3a950", + "0xb031434d514d0878b7011ce2840e23e94a4386034dce422f37fde539aa35cedad1511f9eec39fc23c7396f43ec22cf92", + "0xa4a32f1e58c4ccb2cb4ac6c2dd8acafa292810c77126844f33287c8d522bb8c32dd89ce8f7c1dc9a273165b0879a45ba", + "0x82e5b11b9fad7c7d5e2a8abf03943aef271ffa43ed8127dfd85c7957b59d7cea56039116edd0b0b992262751c347f75f", + "0xa650327144db1806cefedd1daec1de3164b77c02a0aa652371ca0401b50ec3b7a392ef6a80de6d4724892d71cf48eb07", + "0xa88d8370d88379b52bcaaf596c32faba155db4857bbc7eccf89b5d67a97ae481e53e81de6c9461a6719d179f3ffbaf16", + "0xaae8b3d1b1bb0d71f19e37867885a1fd550f7805fd1306881515d77e5f6a990e0bb40c685e350ed09eb4a55298f3a393", + "0xac024fdd79688628ee188a7a9d39cd1306883c260dbda9e79eaf2d2f57051b5379834dccfc641302cd12a8a24fa2224b", + "0x90cda91b9e71b7bbc091b9e8e28d79f0fce42255e24a7a3bbf3348651405c46d3b6e2e33c5fb5e99fb4d0fbc326f77a7", + "0x91325730bf7d71855ce0462a2fd0840f3753964b296f7901e1ad761f48fd133371fcb805c315c4d8cb2ffe34e98ab9cb", + "0xb9e1a298ce9efdc004903b21e573c113c771b1bb5b25e2e88baac6dd7bded130016b2f09e50a4346c59adee607a71760", + "0xa703a60c430e365bdf9023c923a04fd9db427ca0da2da8dad6b0f0d5d352524943839d859c15dca64b590ace6cb1ca89", + "0x995a5ef468a38caf66d58880245064a7c0ab520ebf3c9e9f956110a9dd68658baae22ae274a48d59464c8d37d2f8b643", + "0x889c6e4516ece0e0fdb8c99aa482f367f2cef0ae2ce0987b6b602f6c856b02fab27114a6f4b82050738bc98a48ef5837", + "0xb432ce5f638aa48ba952b9c2e06ce822d531c9a24011d14650cac0722a4c5ad1bf22109a2f429cbdd22a567ce6f03094", + "0x86fe41234d309118d1256a9ac79b7bf01da1fdfcfd579b655f31b7c4cdab6f687d46855d56bb11bedd4b0be17e892b2d", + "0x905ec536f23dfdcc4f8128fc1c00daa877eb3caded7637dc911aff0e6279eab12f1748949e4bf015e4f8e30626d3177a", + "0xb6b9f47cb82244d7b1102b37cb52f5c9336e4c05e4c90f5e448fa92444bef12d2fbcfc39af9e1fd05811f5f864f12047", + "0xab56e7c534ee1f921351dfed3f3eaa127869849b44540b39b0dc021b3dc4dc94027e4161f7f3ed40bf42a1d08315264e", + "0xb9c62b4e679dbb3405733bbe0740450e72ccf39bf953142cce65fe014f132d5af5864ad96167027012c98dc8b8889e8f", + "0x82b8036a3fb6f648c6fb0492334fb3dc8f57c32779d4eef78ac2becb0b93f046dd68c2fea3b5039c21ce8e1efefcc685", + "0x8525738182748d6f901650cc328ae498cc3c712300441042441f66c683e06dd741b644e8e98732552e55839b66f86b82", + "0xb625cca7bf4ce510f21e8197b223dc49e7ce245c5a5d1e901438eecf7160a0bd37d0196191b1d934779f4b6a387b6db4", + "0xb63d753d728670f3b63d4c24acc4a3d4859e5f15ad775e502fc50d7ca42b0d2484a8649eaaef9eb22cef28a23e10d5e3", + "0x8e951028c0b4c5a691a219a6dbf348ef66edef60796094d5f6abaff1ad5802b53a5abec9b8b3b3b98f8b5858672847ee", + "0xb6b71004d898a3bddbcf7f730b8d5c0d8bba0f3b508155412446732ed9abbc1d03a90864f4689e6ab207aed495830e1b", + "0x98f33a74e36c035d9476b198dbf3a75573856264d45313e5bdd89db291dceaf4084917a2242b0a30d3b1ba4ee3016c42", + "0x912fdb4358fe617d7981bf9a9986dade7fe279a0445d7b14951ed77eb88c77c4aff4162467e40fdaa9dafe78da0ab4f1", + "0xb17bdf7a896480ae70b3696cffefbca468b57493d5db59362dd85a3da296e1162356358080c8b0a7f3fde798a3ad1d15", + "0xb47ebba84e62bf453ab223496a892fea2244ba6c37615c3db31c2ecc16a5f9519dd79aa710ec1220a2cebd254f7690f2", + "0xb3361190434ab75e46a40e0ce21ccc251fd0139bce90664bd33d9eb6400317c3210509e4ffeef604c7b05b260544e19f", + "0x966916b3966d7d33be49fa4eba925aa2f92adc2d0228d1144ef633dc5d67fd8231087c488b492688fa142a8cdb45ca70", + "0x8ffb1491d4448af82b7cab5409ad26d99ef6ef08158c73a9ee9626c5a84d2fc6d852e2c786c94b47b5931c7194d5b82a", + "0xa2d4a5bb458688b8f593f39cce2b27fc05f8ee3985f4c5be453706e8f174d5a6585c2070c0bdbb54aa1d8e79b5ab40e9", + "0xac180389d0432699bafff42a4c3da59bd32ab1bd1c4b4a4829580577fb3c5eaf8aed4dc61a93262f23ac44255e6c2b11", + "0x87f8fe99acc93080e2a2ae51eba24f0b146c1355855a202dedb7deb8e1cb5c6ad8664ba0e93ded5ce253597fe015fdc1", + "0xa554d88dcef521dbf5e4823bcc9145c8ea98c598cab56c85f101ca7be82297dd7f361d457966bc69584adda6d40ecab5", + "0x86ee126cc839d869c7e91f0f8d919929f66c1f67675ae8c5eaf6bc35866580c49d45ec8edf0891b546ec2fe7bebbd304", + "0x970d74575be6cabcd2e33a8dacf25b378ce750478bb44086e1821c97b6b27055b7f00cc8ca189954f0150de7381c80c6", + "0x963badd0cac713d8a23dabb8ac7da5e9a83ca7d580ec81dbbe3e5d53c5c9370b86222ca685215eb282c8f67a977b4b66", + "0x8d2735c85136625b3f8c4196a8f892e276845ca7c876648498143f1897637807a9a5162bb90773099a7b0cdfaa292263", + "0xa1a8507bb8a300e1df882651b0155e46a0f58399375f4e5f993251663b5935a76a02e60999a4851fa082a89d5cec2e63", + "0xb712dd139d791a95486d8fe48e35bb8bbddf890435dbf8dbb670699dcfb143fc582d4bdc8a6751f6bf58a13dd8c2871c", + "0x8f108fcadbaa43dff904a23c89d809746a9f732be817c2c882ac3493624aa5e49af7dd9b46de7d9d01ae982bb78761cf", + "0x80e270c6620756d3d127457fa2e51592604f85479a1004d63c184d7d2ffe2eea4ff75faa436f24bd1494f4eaf90543be", + "0x81f039fce432a5d3bf9649ad0fc2d93de831f5b9c0d0e5fa92d35b5bf4a52c739d478289c2386efc964026134f91ac0a", + "0x89401011d51b6106855487a37459351f18c39f08ce90b15e52a876cf36e969a9c9fa6cad94a55b844ad45fcf1807f705", + "0xad66c149ad105ce8b53d38c410d73a3cb3ec910a9f0ae798f3aa5207501c7ee39b85f10e91b4cd91e6b280f3912c492d", + "0xb709445e56d02a558a1496bd2b9115d2635855b18984cfb908cbd54cd279d29ecab21cce704cd9ebcf34456dd1195d79", + "0x851059069d9fef4eadf6ba508ca330ecb7436ccb91d09f5d0416874f9fbcdc56472d2adbaebc63a63f190b6abe7650d9", + "0xa933c1b614e6d5a58c599b7189d06bfa3683995f449d636805c8307195d8e98b62ced873997898d6b1b01f6e6a52b743", + "0xa692ba436613db22bc30c3f36a516971158d5489bf2c50c39d0627a74048a6d0b229606823f37a0832913425ddc30d06", + "0x830999596d203b96329185c100bb7360189a50f7930286c36544d20e57b8418c71d8db331e4352a98f380c68a49b2005", + "0xa56d7c262bb3d443fc0cacb2b61f24554ce35b8984fa3418bb4e271d5fe4f5378ef7b12c6cd02f537820040bcee95f98", + "0x844a4e9a8c9eea0b6f929a80da9f4e4e273e999fbe182d3855b0a40577afaced6f8ea285595573e99e13b6a71b985c03", + "0xb34df6205fc429c9b7cec189b2634d49a4877f22bb8060b9f7baf8c2eac4e1d476ed1f30fff1f4c019c65fce96abc554", + "0xb3a97648b3b79cc513246d3d1722afdf7645e7216af233645fca6a2756686592635facec913d19acf99ee586436cb58f", + "0xb9cac906123f2a4aa13d5d7eaac84e84eeb0a1b7919526de5198b5475fb815ce556f8451c953bb0bc910c93c6fb3fab7", + "0xa5e441019d492897de73d31a44a0055fd04e8cac894d626d0457ffe9de5394d0bf851dc5941790cba388b403b86864ab", + "0x8e3081cc7999d91d787e4c0937c9e22c959d2ba4be6fa04eb97471997ef150836a910ef28455f117dd54fa9ec655148d", + "0x98eb793d88faa691ecac3a7c78b25eb3a833ccfd0275186a63b1b1517bd2b984d9908c84e55f044b31c2dc5e251d0414", + "0xb38b5454c2debaf1a4e9e467c6205cfe26d52d1c1dde5356c089abfd6a90dbae89525442419f108c7c8e82e34ec3d5a8", + "0x942545089077b9f27304d2d6ceb3d549e983f100417e88332bf05bebfe8d42b75a96171ab3bcd049acc859f3cc9ad1fc", + "0xb9d444777403590be63076b5dbd9325ad58c1eb244dde2c9628234b62ba74f6b0e956642af2d08cc65f82a1b2e24bfbd", + "0xaee8deefc7ac67882ed7ee6c01c08d7739b6642deb2614064c69ea38c5c65e06cf609bcaf7db74545199cfa6122f23eb", + "0xb3e476268770abfe0cd64a4f878c58c027ff352569d8cf571bb067368e777eba6c003d344746fd006c8bbd474fc3360d", + "0x858137d63f90f66b9ef2a38d7ebfdae1bb89e5bc1d9032c96d699ef276aa2d7461366c00de8c47de9231d9ec436572b6", + "0xa3dc8fe541c9cdf89d83753347d8c573c49e8471dc07b5d41bc48ad1b10a3fdc218adaeb72bda0f362c8af8e1194df45", + "0xac75940ae476a6ff07cacf70a379096786d10a5a5244fa5c466bdd8af69b1f98e97a3a27877739dd4b223627e0ce6d55", + "0x8c6809f893c5fd03ca80d845147a82d8d54bb7dc6a688733b1404dafc360c45d5ea742f98f6a70ac2decfcead05d876e", + "0xb0818eee75f08ab207832c591aa783193aee5742147eebf75cf7f1eee6a6d8855b309db4f7ab51a16ab77bf619e14fef", + "0xb339ac167debc92cc9132dce076bce281e7f1b9c88978d36e1b5b9bdeabc974af318ff142f746319681764bc4db191e3", + "0xa51dc040c75a8a8bc3b0ecef47ca313ae13d9560c782ee014257ee728a09432c4486a3f87b5ebab690231735fceadf80", + "0x802500a52dc271c52f893b620952604b79d25ad243489dca7cd679b22907fa85947c88dc04463268d25dcccc8a6c34fd", + "0x97b136a881f500b07e5b0b79fccb84b93dd108074f92a1cd76e441239041ff389dbf03483fe76cf7c22a5f40b73b51f3", + "0x9155dfb5d7f7915e50da7a715d1a5ac5b43d7093546d6d342ec8b69d47a86cfcb9dc11d8079f123854033b8d3e1ec928", + "0x9423ac1e11f70b5d0cbbae48b7a5be8350387460631126ebda095b3b33a7ee2845776aa20ad60e2bfaf975722d43064d", + "0xafa907dc76e03d10cfbcc226e50e3bcee56baa4acd8db2cef8e484ee7b7bc536e1765e764180663710c4396e22fb4dc0", + "0x8b6fb4bc641fe2147d3394322418e2e8e091718e3b54dab8d0d6bba687bc300d73cf1b17f81e2812f0943a8bbc1de538", + "0xa8bb533bf42f56edf112b03d34eb64f6dccd57251244f39daeb6531af650d0368f6e4a0f9171aaf4f5a5b4a17debeb56", + "0x8d763490dbc9a9b73bd571833afce20654348cd553a69678ec89454c4cdac044ed3ef0458cabdb60ff35af5e63405961", + "0x8d3ebac80c55b7ce726f4cdac41c7e2f6a5ff4ffcd5f1803c463ae524243f136dcd15f9bc74f8b271ce90a4776c94868", + "0xab63cd85311fb9889041e692bc9d5c1153b26a805b511721154d28f11dc8ab84733387fd20cfa30c566ab2f8e066af4c", + "0xa506ba11063b14f25c26c92667dbd9eb67c8585d05d3980284aa19a09ae97599a1cf8d7cf45b70a32063f1fa3174d3bc", + "0xb834434632307602d9e046de6f625af5de673996108911c6b05d6bd3e2aee17246b2d860f01dc2d6415fa61c73110e13", + "0x8248b69f51196ce1e15fcdc25d487153896d1f74818a5617500cf0bedd5180028e6567533536919156860e34ba275f1e", + "0x86a5ed8b6a1e9d8d17b69640220bb80c9065198c8f7610d4ee6a60d2d808508771a84d6bc679ee4db34f43f94315e0ff", + "0x8fde55abc106b2afdac3b8796f83c8ce1b90405532fd586d349340c4d7a4f4c46e2a56fe2663fba770a8004dc7b9d523", + "0x82489db9dccdd13293499194068bb4ee8fff51f74f1b504d203c5deb5216287a6d614a2e0a769d4c929bc103582c92b8", + "0x82b2d71281cf886e80e09ff907c1f9213dc444c058e965f964bd17fd36dc0382da2449fdbc3aa7b6d07004d6722a5848", + "0xb0729dd38dd64c441e81a94fac0c8b5b3588081e43a5b0298bb576b16a9713acbdf09b9bc2499c677064619cb3a172c8", + "0x97c4bd5c97182e80f55e82648e387c4a3362c6088381e96b67cf0f04bcdac3dc670890904180a5388b97002c70481235", + "0x98d99f80ae9c59c921c6ff71ef01c2ba283f531ec32666cca1fe7dfd9bbfb09f197e9112af1761068cba8d6319af5d74", + "0xb0569d892ce82d87a3d809f4c86a88ce627ed420dd106ae49b88b8c470ddb081a3dbdbd92d7fc032a7082650e4197ed2", + "0x8ff68d42ec2dc5b13ff5c7ef506c619c4bbb0f62fd4c08e320953e5cddded2aa34624c6c5768b546cc2f00add0dda58f", + "0x8b53131206c80638dcff21d7f2dabdbc6faec545f19ab1f4f2bb858d6b01d87adf886072c3a744d58124b8a7a0c87573", + "0x8b9c9aa127ddb950cad4fc21cd7c8eb802cef6db7290664b1773b9744836450e48af503009d4bb266ceac83d765b3b9c", + "0xac61e051add512e749588e2549ff55f3e6fee5378443cbf64c80cfd7b260cfa63f16fc3e242aa140ea243435be28179b", + "0x9240700fdcde974f319a90ec4a9b92a0323424fe39e513c7412c621cb33072d193476118636bd2655867ed2816e03034", + "0xb6b05975d0653079034f9792d5d8cf5743e1737e1b3860e431a1e159199efa5a55b2d3283f6d270c9ed3156a233e858c", + "0xa2ea8fc31294943a3a6d02509cf8b75a7b5d94de917ced468fa64a6c24ead4edef11c34782eed848792b0570219fb77b", + "0xad0b54dc5dceb242c05a7f7c529289c8caed93ebe743f6609df653aedffbd7eaffceb53a18dfd109f28d14c80e1f7935", + "0x81e4d4667900eb5a8434e2153503b2318f63708499534a8d58382931791eb0ad0522b41cecc7eb0e6ddf99002bd0127c", + "0xa4c5c329fe159bdeeaecbaf479c60c8f43a58ce613e135e9e9eed4af6bf5b6116bdbfea31c82bf0ba87c3f651e1464f8", + "0xb95eaf48a9128df7f970754af926f9865c2078cabb4da4918d8b45e95d72748750ffd12f1d8d3f76cac0936ad0097d16", + "0x8567385d52e6f6dceeee52f6b690781f7c05c26f0d20912bacc38c23afe8f64925ba18f8b6464d4a0557670ed0cea232", + "0x8f7483cacd15fb7e49b2f8deb7ab05e64bac18ac9dba475666649c2cdbc5d6df0d5e789fdaaaa997a3b524521f0470ae", + "0x9252efa0698c0cb30dd431a72a0f5f2f14429f6ba50bb60f7039df45777557afe3ae732b9283b4a814d2146a8cd8b7b9", + "0xa54da5287928a02cd5eedabe70cff80e56db252e2811842545beb14f25ab67788460a71ab8ee47cf0c1a5f8d01635256", + "0x991a80279c622565a03929c94590f33cf0621a79b70a2168a41a4376bb3f0dd12a9ed9b16c0b6a4a59c50b5802449874", + "0x924ff5d3a6f0ff4ee58c3674319971257543d2e19f0ce3fd0b0edb214faee920f8d6199ca794a173363a9fa06c96d7b4", + "0x96b136b8df76ba24e4dcd68065c650fdc224fdfc9c1ab6410e008fa5b9580680c3c85801fa217917c620c86dcb5ce3eb", + "0x95934e64af642e7d45ada1bbe8b9fe972877a674252005afc34ec2e857f755ea0d77e7759ddb24255f21252d6c739305", + "0xab14c6bdd6d1ccaf69e0dfc6c832751afb70f89e4800c6fafd22db2e7e5d6f2addab8b1267c8f3fb85cee51c761e69f0", + "0x87e2edb8dec1253547cece2a7e6934b0299715e634d599316af0f076c61726c7f2aec83eaddcc9add1c397cbc9fed0ca", + "0x91170baea88ba00fe00db375e8d948f58061f9e7b36a4573031b9996757afcc2c7e9c2d9642bc51402aa586569f0a398", + "0x89d99b120e4565b0538b2ef4f8d8c05997cdbdf61840e068054e7f21677cdc1dc2f63adab1b6814225d14275c737b0e0", + "0x880c2b79bff714665e9b3a0a647773a212ec5f0dea37ee6b29ed6850692055013e108a86affbe44d1abd0ae80a748950", + "0xb564181f9ea65ca25b1ae7f25eee84b73f9db109ad1939e6b9351663ac0b083fc13e6518ad8eaafa3caba9ab959bf7c5", + "0x93cd91391deaa726320574bb46706fd8e30ffc2308290c79abfe2d234d0f0f59ee4c38791e3bbd8c3f840a920489ebaf", + "0x8e846d48e7b120b59c6556a0394d25f744dfda0cd58d4e70029837753a82afb63a015e79157fe8c810cc68bb481d19d6", + "0xb36904e7dd71bada7c9b9172e4a6748287cfa0cb6960ccfb7202a36c57bc28d351e1f5371c2b449437cd266f2d22e7f7", + "0x8947c11af34a42f314983ba9c673e62fcf44c6c1f733a697351e1b8422a75338a85bb19149fc130d01492ee18b3c9492", + "0x905afc0103e34fa9787102fbb80967b8c917bd03abb02731fe49ba1acff1e96059227616cd21080563e92dd021117a84", + "0x88c7acdc65e6373e4c8ac6a13d1bce1d534aeef2965a4d9f887b2e823c7ee7921db1397df5cb5e7f12030e310172d6e7", + "0xb028c19082411efe8a46c8abfb9936c005e766e2ad3120be774172f16419e2b04ba3f31132ed2bc209e7214c2d7b2b61", + "0xb6b3a561d583870193226391ebf51ef47185ab6efb3556ae59106b6f266776064e5cdb66f0c93748e60d557db64e8f84", + "0x93732aa1473dc2e50610eab2c8152f2d96992fea840ac2d82c6e2c5760d8c1c05e8ecbd69e14d03713f43e77ced9d3bd", + "0x9734c433ad41a8fd91e161de033a2a55189ae31e2af406d1fae443a182bf1977dddff93f6fe2ac7d9c4fb955c26ed59e", + "0xa1f305d17c36c06c515d30fdfb560f899e80a2e2461d0bd947032e5ec764116c7ccbd528ea42a3b9351e3c9b45904431", + "0xb517f46b582655e551f766930637e8dc2a762dd7a2c54fce429fdc4cd503e9fe4bfbf323f50860be2c18b3a17d528654", + "0xb395b5c48b1cb0daa8c156188b390a78441c8f16ecc8650520f9f2914bd1d992b83849bb11ec17a47f9f2d40d138e3d1", + "0x9147b715b62fd50e59bc96d210e10f1062c87a90263b5586746325deeea89e759464be55a09b0548766e13bc910c4d3f", + "0xa7dfe5e7a39767d14d531d371b92fc4979d326ed0f966eeb7b4b4252d78117bf5295b3c17d1fd636dc2c0097cac901c2", + "0xaa3f9fb858b30675e9e57170a1835717521eafe4bd0a0690b10020c7a753951576b4c7dc80cf9f042894fd5741b41b1a", + "0xa1f11dec034733e862cdd4aefaf0252a9e8175b6d4c834b7a7d30ab884bb6ed6a1d92bb0e958f0082085cd80157a0e40", + "0xa1751d7452b7c5596fb801466d8d07a70831e568b8ca66fdd75e5898739709795a5768726ebe13c469b1d51092d86a60", + "0x80acf49051b7caa6efe78318792d05401f5246c5b3bef25170b2a49adfeec8048ad5a5e6d50cc498b23896176a9d9669", + "0x94156df9959c678578ec6e12ac068f3a28d69a981443fc35161d14b1f0327b8424746d62869ea9377a86ca6fd2c95b5e", + "0x95dd91b1e9b457de913a65f198dcdceb1fca75692853bd5ed44eda6343f32126e6aa2a309411e019dbdb9519c865b96d", + "0xb2516bc36a726cf2dd5553e319c64fc508682c7446a2a5ae696e43f1a8c129ca9602f5a41bfbb57865a9dad1d56728d3", + "0x90cd63b4f9216fb70635e4dcbc9a6c5874cabeabe4f9ea83bb923146d03366d9befa48b20a64f3a2cfdb0c3a84007ab2", + "0xa55bfe9b33781501f10d5632e8f5330841eba2d0a64b0aaaa92db56f014b5e44dbeda3b1f5b2e4c17eb6a243977b2a82", + "0xb9e84b3c617708971f5e174fb8718906f9bd353f8b0fec8fa03d1a6e4bec20430212396a5406595343cd15777c5a3f8b", + "0x97deb79dd82185555442f91fb9a70cbd30a564751528fa0df0a681315b8a71bab5073716908ee0546d70dc41efa3b53c", + "0xac77c2fe555584b9cba7438a4e3904958f671c49536f185cf1f3b25c5a57ea65e15554de22def94c5c623e8c99e47a9a", + "0xa27c62d39508552d79d2899bac6138783f308e3befab65a96a1ae4ab108b799628cf37db1ec72859a0ce1ac68f68b106", + "0xa2aa287741f03e31f2c87fc37e228279b1acb886f32c6438b3e9807b8126da875fca7f194295c45531e939a13048a882", + "0x84df8999c4c5ecc807819248957d68909d16ef64d94a820dd0d266cddb6775c9c7464f0b2385b7bdde8fc0f2169a4177", + "0x8388e1a1babb941e03806b392fdc1bbe1a01438292ea1db4087b010de0805be78cfa56d20e9ef7c8b6be5a04bab1b1e0", + "0x8cb6ec409cec27e7c4537ee2e5bcf82a33e7cd4761d19059e902b6068a9744e897a6010e2ab42ce72625cbc433892ec5", + "0xb6e71cf74455b0f506e03eecc0976831ec9a56eb8fd0e39e5e12ae199180a4c6e5123174ddea6ce6cfd7a414cf0afc5f", + "0x815dd267d9f67b4d229a798a499b70ea2a611f3bf2a9d3698d1105890a2b6462fcc7c6ebff0d5d709707ee4ffa981689", + "0xb4e5b7fbab4d8a66d1b167a5acaa4d53949e1fbdb00107e62b727b4b4b2cc70e2685cd4a16266e8d13ab176f9be09c10", + "0x8d1bae7566ff551f06baacd8c640d0d04accdd49fbfedda0841914aa1bceaf9f3f27344b80bdf5f9b93ada438a4e6d68", + "0xadb054123e27afd4a691d2cd808a3232ab58f56fbd514935caf47b8193b4c64aaafed4d08a7a10ec4deb66be8c292e64", + "0x8ab5255246e01478ba7dc6807c84850308a719f8f8433eb049d5b11cbc361c08930722e7e5878ad99fe1586b3d11cb1f", + "0x90e862be1e3d0824106da33aec437a87dbd2599aeb58d46b4a39a5f651097d49943c3248a154e09e309eaa7abff3d500", + "0xabf16f35e3b2b29a72cd96802c900fbc54100484299198b2d40cc6071945781cc9bb3eb43f6ebe433a14c1aeb172929c", + "0x867a0f396374cca7303845d8a1e4bcebaa53cc0fc2e790dd58cdd0b5ff2b9a99e18ad4e57aa2b061826545935a5607b5", + "0xa6b6a2e22932d7c9ba8f27b1e1de8559631a81effc77ed2cd7c45c48e49ea7d2f68c59d07a155757493ad82f733d93ee", + "0x885e4c3904c545c0eecc9cd02e16d359ce69a78e3a355e7fbe6485762d4523f2604f2f663a4521152a8bdb6fd4a9d4be", + "0xa668f417391b07a35c5d40ee5212cb7bdaffcf040a4f20a3d7e70e9d715bd908d4f8fca87a7dbf7b676e088ac8651ee8", + "0xa70d67f3379e1ee0708c34c4c7a7f552267ff679460b9d8891549077c724becb99ff79b35bd80420a4290f293ed4133f", + "0xa523cca782ced0d8a3f7e19707f9c64ff38495f739e035bcfb5483f202b209c07c50c764eb28d3bd8cf68ae093c46f19", + "0x8ce98e5f96889ebada090449ae198208cae5c247cc5f6fe7800b4c2254b0e7f2475b632cbd5021a0871b466c5b943dc8", + "0xa69cfdeb27ce1163ae6b6b4b5d46b49507c7e62789f2f90f7f5a0fdce79de988c755cc9afd8397b1c02976e03589f985", + "0xacbffc94dc0445f7797a0d83e5107ad3ec8bf61620fa83e73a999ce4f9b6bbabb00245a619aa6f9b082a2711bad5ce8a", + "0xb64162794503c86e478c23f060228105bab4f3f5d46582bd455a94526aa6d71f4c9630d8d63854c8c67aff3904681e0c", + "0xb1288073c012a0b2b7e31708e874106031a8cc98b2c94ad0ef1d7b9df42f429f58caef5494f6d581baf12970cded2a17", + "0x8d7ad217c3c1cb74cc301540a0e43be6d74d5a3c0383ab7c9dae57e25f8725781735b58301ebc014476171725299782a", + "0x924a33c759249af270617767101385910494724a51fc63600836ca00d06f0ca86a4a0a85e5e87cc29e404ff8e04d036c", + "0xa7b21ad39bcacc96cd857328a83e5d26cddd0a5bb2326da9a8f593927ae7b5927704acda9ee217176618c964d0452d54", + "0xa5c3616c308bef98807a852e16f146859b0b1f31ea8a721941d90abcbe37eeacb4403c6568480b6d6e773bbb94a89307", + "0xaefaa1033e47673ca2b68e4c945e6ed892e223146d4fd24219304c2667777c1b18a19488b73053cf7b0e6e09ba1278e3", + "0xb308c690176bc43051f51839d3ae1636f6de5a57c626e8def464820ce2f96ca09ff26294a3dbc9b4573cfc42dd03bbb0", + "0x8f7b1253ea9e257195ee92c54de41f2e7a310c90602a628ba3180e059e5bba79d6bb8110d1964c59daf4b65cd9735704", + "0xa387f003f7731b81bace54c8501a3a2a25d8a910cbb28dd603ed16ce61ef1df34e233dc8579071856d7198a071efedf6", + "0x955ad5523828c0fbe8ad6a77a191661ee9c8005b741b7b0439b76711b6992795758d76133399d266df5e494e4f86cd67", + "0xa44441964f5cad7b54d0105f162ed3ec40d12870fe8c5c30bf16238440143b330ba986d6adb00c9626747e99449f765c", + "0xa52df726de07cccbc77e81abf4f1712657c9261f65feee8815ef0e8a4ca61b8e470503801f1da8a23fe6d52f3306807c", + "0xb5d239222c3d852f2c49997e76d97b70bcfe73d85e81258d9781f5f7de87f9c81648bcf58cfffd554e4685b2f860e6d8", + "0x96f0193aecbeb1540678f1a3369401e916ee75d2a85f7124c555466a3def91a5d8b5f774e3156a163e1010690d457c5d", + "0x886b9f4965120d942b076d053571837780232e139c3efcc6bd6c64eabddbed2d55c3a9a06001bd7a2ccebb36135edf4b", + "0x897a1e4e9f4eaf755807bed984ef0bfea251740386a168061f4386819acaa337fa6d3f038b4cff9a11926e68f7888f90", + "0x989d9706f8396ba422a34b55897b9e261ac1ba0c7a7a11a30562ebfab92473b9e9b604ea8baa6067137a4ded070fda10", + "0x96376812651020f68c6a1f0aecd04591fdb628051f01daae179f7008ae33af5abb42e8f304662c9b6e2584e8b02ba6a6", + "0x9344e6f3ce42ada6281d0fff654f408e61f0acce81e23ce47466bf1145a99cf60dfba9a22304efbb1f428c92357d644e", + "0xb90c5463445156c8de69d8c35db656a76f3e195c325808396a829c11c06a7503f3c092816b3f23a263d56d3f2c075ff7", + "0xb4dc6d948f4b67b513ce27fd12bc8efe43813c119d01b2da391d01c1cb0abb7d51350a5446e0a72a6f8bbbde2ee4b0c4", + "0x84d208ab983941bde208fd71d58c7f9335e14db237cec42df453155a3a8dcb21dec8696a1334cfe5d035c192fc44e88f", + "0x9577996c78372d2d6c9de27d497afb29c918bd894bfefad9059bd85cf2ab520ce1d517994724e1567f12e385c126f26a", + "0xb778b9054776a2b8ee81be356050b977bc8aca0d0a202be91d56ba89d8a384bd29c5c652ea084709d4fb365b107962b9", + "0xb7ea99f8c841678dc854527ad0c8ffc700b43b5b36b3d18303e51175b3901b144c53e22eea6ce7cd500f6879a80a8c21", + "0xb466aa7d1a5ae3d9aea240c8114b3dc3af38f7d8f1e323800a6382de5766f19626d07cd6ca6eddfc4d71a43d2d49a07a", + "0x8a72b1ee7993f16400396982b6a5198f0de08821431bc66489189d5b364b0e36daff5077b48aff1d55c9a88580cd1dc2", + "0xa7c4dd6095f8cf61f42c5901ab67e9d1ad21a42d1eae9ca5e147a9396507c7a21747c2794f71ac66002840f4fa4e1dd0", + "0xabe40e33cca787e7c521e2e97fb5f95cd4ca7ad6148a505afdc94e0c003e4903b1524164a1df2b2a1330fd800ac33b7d", + "0xab8e1930b1e592aa2379cff636e7fda9fd7f05b358f47d9cbadcfe35fbdee5bf06469fefc052f62159c10942ea2bc5af", + "0xb28edfbfdcc27c3892d64e7e05a2aebb173808c020186c225590b03d91dacb866108370f2c14ac97a6d20d95a8e32f8a", + "0x97d4841704bacb06bce2778104e4437c930fdd9320d85cac383d11ce9246525ad5167cbd63ef04a8ea39c8fbe3d88169", + "0xb4b178a1c3ccd3344831936b784203919cffb611cd18def1a52ffa2a8e4286f9f9681bd48dff9b2abfe62da5fd619fa7", + "0xafb01a4777a128b02fc22e282e0c4ab1d86246d8e0813a7e85c51907bce079766ae40c31d3c440d5f99c92e89d3a683e", + "0x91cd070a607c20140c1f35b25057bfa20290b1435e99c5b33068c4e5755ff8f1aa2be61fba28dcfc131cf881aa1c39ec", + "0xaaac82ccda92c6090970f60a56668c011ac20dcab26347ad40585a60b5a9b5a9af883307c55526d4eca1b110a079fd3d", + "0xa7480de83b4cbb2eedece3d3b27b8d934e9183f448d56d5f49e0b63e24575014a94e09d406d7ca5afda08da9f4eafbc1", + "0x8e568ae356775b06d963b440f75bad9b5977b7bcfb8fbd1dbb4daad5192521bd189674654d4ab86ded4a6be5fee27ef7", + "0xa501a84cd0b4138383572fdd09242e3a748e593f55400fa7600698d4f916a2fc1feb96557a34f4ef0f13eee580fe9f54", + "0x8be0f6b98d52b83e9deccf09d05fc4a7b4ae1cb11972f01baee4fabdb804cee2b0e9b44a1385238f755d2c1ce395cfa5", + "0xafd01e3658ed9204d39fcdda2d79239b8c45dcf80fda8a680a8797b6be294e7e8bf56ce345896c3f08446e9a2a053a61", + "0x851f0def025a50910bfb6c2fbe5ca62a31426747d9cf4634c8faa714a014fa22586c3eabde84e23ca77371ae25d720d9", + "0x90a1aa7bbe7842cd361d0ab2e16203a56318885d2d32409370ffb64ef0ffd3c8c57658573a0714b04dd1595aabfc8f21", + "0xaf56f30bbd796de5cbf6f3d041c2f649d0f36e0a1335ba923eb1487e0b29d0ab307a1143e6cabb79674ddc01dd9a5cd9", + "0x8429afa5476d0f3a4eed4104fdeafb79f80e94e709b59aa44b4caf0a94bf75fb3efadf76e96389179eafc389fe896efa", + "0x91d8399bcc3b82f0044b9a697b2bc402285f6d2e7b76eec25ffecab769f3fbdd45d20897d28a8676f090edf847eb3c70", + "0xa06f8d37404ae58c35732db58c4c6270e4311c691ecaa7d89b3e9b2bb1421ee3c3cde555d066340c0f2479faea1ae162", + "0x8011fcbb711ba6511960245c69a73fa99167eeb4d238426bc31ce359a90a2339d5936042b281f3ff3eb976357db96885", + "0x8dff2bc19830b4a58d2cc3c1598d060da34c8fde11635062dd4222c96dcbf2bef79b339c63fefdb1653153ef9af79c48", + "0x84ae7869e2405e326bd167249f89c2e018817d3edf59f3db8adc25f64836ea4606c78158cb30020a54551283bcd9f39e", + "0xb7be6cfbb7cbb7788fd60fbfcae3504d441b0af3b46317944e00a23079c605c08fd116311432be5b378ed8a11da219e7", + "0xa3656ce4a79484e365b6b7f81a9dd72a09746da16175a61794bc5fcc0c3dd608751ea2cfcf7bb0c14421e0b05d94df75", + "0x929d5603a936bedc69ede2d1277692012d0c820a23915ac6e776b832b9f4e0e6276fb3b257c7abbca32ea166d4482149", + "0x82d47138de8b6ed4bdaf69526ace4f6fdc50fe5abee63f1c6d4447fe4948a84a63b7963c8a65858442856e282fabaf26", + "0x8f8b2d05e77e9e4e2cc5229ea98c5c02ef9d9b6939ce6663d98d8e2dbed73af3d41628662c354972c1b48157f8d3c459", + "0x9353ee31f477b51558f4ba5ca437d801f59d01ed995a8801552f8c578d1937997bd76c31aedab99fb5532789e72469b0", + "0x941f777fc9115fe948f3a979e1ab87f133238935acdc19d33e1d86a1a64924eb9008e91bdff8d650f5e3ad06db931234", + "0x8ee79ecb7d07e3a5fb80ec15c085898e97876448685891e09ebee9aacd5cd0147715dc60b6f75b601fbe83598f1a939b", + "0xa96a50de4fa25367706c99abe9dba95ce1717483f50692bde7c8c3a6b427d02d59ef6e8bee82762fe774f5afa528f0d0", + "0xa451ff58246340157fd94d028ce1ebe5ce49e5ed87d9b61884c8ad903ef9b697c4ab9e5acf66180a89a17749046c99fe", + "0xb12739d77fb96e9e93065fe279307eafb11c795da2b9efba4cb239425faf687b9762235700d9f2cd5df9cd9fb2b36e3f", + "0xa665e34895d02e68f1dee7ad8090558514720ff3e809cf46cc06d1e1274d219fd08781fd430367a3f09e2c591dfd7cf4", + "0xa262410cb60409720ce68935e91c743aed5eccb4a0427449f32a4acca103f9d65413290ffe2cbc895e2e1cef96ba7c6e", + "0x9597cf4d49db896796132aed4bdfbec71ebba08a617a611d1fece75bbfcce902e8ba228b662d0ec5fb927429f082eb38", + "0x80a045d2bd30aff2267a5385be12c646b65b24a586c4f5cb1bdb81387e3ff8edd2358cc8e3c4c5c60cab88d4dce61d90", + "0x80323f4a9fc78bc89aaa172f739bbd4f57f9321a7b8e9eddb74ee5c99d6c7b8dfe847f7103110f0a44d4e7c20ed29114", + "0x943b749ab77112be7593bb2ac11094c66c94bb89d5ee2cc05316ad623a3997a38aec741ec93c24770adc670b6ad12e91", + "0xa8e1b4935aad8a84112a99fd5a4d3786ccf1c985aca0b256c46d52a8801a132024391431cc2cfee760c25eb18289041e", + "0x8abbe403bf13bad914a4d5bb0c8709f5b264a7a81ba0542850cb89c3c45bc294f62b22a36d7f829ca80830a3be5832aa", + "0x9084defe85d170967c08d1f5e74ad1dd592c2b013b01b84b5fe3f2ceb260bde2e52ca22e4006b07f01e3dc7a36774e94", + "0xa34cf1cfca622dda528589b5e5e36a4a68cee7e18cc621736e3e165b06a5df9a8e9f3ddc406838c1fe94ebdc44bfaa18", + "0x8c5f5d7e98828d0a063d90d5f78bc493c46316fec3245d47ef8900241fffd5316e0d6d6f1653cb3b22bbf05286736c06", + "0xae7f2beef46b6645a6d0b3ca664c826be205ca6de35bd2809a4871f19368bd2c009ad7de0cb4c269c2934357e87c7f64", + "0xabae2cd1ff7320d0803b6b5345ef9dd364fcc001d91fa456199dde24f474ff3d0ce94d460be9659caffe7ae0a6814217", + "0xb86710fd69a6eeca8a813c7c1d643b487a32cadd70013a4aff4b7331ec08d1469fb17a06d06926e68f546e7f5238e1f5", + "0xb42e9dd8d0f12f95a16112ef7ea60e6f4c76a39cb64e7f6bb4fde6ed1fc54fe8317e93160d736d97d87ff7af69ac2a41", + "0x86e5561a7b621e68afda9d63945dc69bcd615cc099c84ac51ebf6350b25c9c07ab371ed5b160a86488e8213d143335fe", + "0x831c730524214b8368bdc619e5c7e55a0731b6c5ddd035e9d7cd90577a472a429e779efb0ce47320c8d3b62997eec0de", + "0xa3bcbb6c321b329ea2bb944f48ac984f8bb6cbcd38a5f80e8780257765756cd902d252a51804879104527bc7b251c1b5", + "0x8b1a0ee0219a010653f816de88b05116269325c42811d717544468b3bf477953900394a71d56b6dea13e4e6ef9c9c5cf", + "0xa5d06e2a43d965e47d400343c093d64bd5d4adcbe3928093c80439f815938b9e952bf59da7fb26f459a5439fe60fd49c", + "0xb92df54cd0515bb9868a8dadb2a808d3e62fec12be3c708fa6c845c833c3321017e2f8d71f10b064fdde02b098e22962", + "0xafd8fb1d8ced274650ecb6c370c5bbe8f09d263391af7c2f2290b5c99196ddeaeedc8b9b6173b6fa349872f58c83149e", + "0xb359418883d3425b1bb896a9a9e2a3068c19abbb18ebaccadb85dee713b14bca5b83992cf239cfbb73adbe2f07c63f04", + "0xb8cb045dcb0735b02d6e81d9aa9906ab2f19df2e2adb5bff0533022c03a9a530bb27fcd45173faac63a8d13bf0f41565", + "0xb8b8ed443891d3ecd807d3f92d8c2afe714a423b97019cec3690c24002cd0444548ba6b454e1f9934f01a419206896b8", + "0xa3c28de7e71c54dfba701b7e1053a1719032bf48d0e520bf8d513d155d588d08d14af3cf1e9ba3083f8e59dc947ef99b", + "0xa94d1569107e659db2ca58864eb8feb03c83ca083c75a6d949805faaf7095a4f981cbd1f89a464aa46107a891ba758f7", + "0xa9c98b0501a8c5102ec0daffddce83ab40bd097c4ccce66a8f8a61a3fc28564ce5dec37940092640b36a0ef6efbea4a2", + "0xa473b784935b52ce73755894177ead52cd9f2a10521e9c034298fc0461aa6cfb03d499812189eddbce4b3cfb58774a3f", + "0x8c7a7984739a3db7b28b7ef10f081e2cbec93e1da39738d082898fc73e83db206fb52cbec476c9809c7de61ff5871b71", + "0x88b87148a573e576d0a8fa912858b7a9876001d8364bdaa7dd2759dd908555119f6f580c0d3a663ff5c2a2bcb05fef99", + "0xb169b58fa10256b2753221aa33dc4f0f3308a294b98300528347ea4e89128a1a1da502990c9f2d266fcc10031b3c5a07", + "0x85b1f0e49528ec8e51112771686b5b9f18e4cab091f6f97dc9a327221fde48be87f59cb46e6caac6e1af1a8c70135e66", + "0x954021598c24f5889a642b9d20488a04e3c78c5b04bafcd0a01f377cf24b49f64b1d820ee8a73f8cc193e1de9a106a6f", + "0x8954b280ae11638d6e9c27f826fe66c0ec584fccefda8665f75e0699ed640e8e74fb1945753f84baf606d2fcc804b1a4", + "0x899303d3bfcf48d28aa49e915ddfe263117ab87384f611bf7d555ed141dd728a39b97eca74b6b623a20d44458f35a157", + "0x8d792116aaba18c94069cbaf0da373c4e61662189e8bd0f24dd675531ee6f99d574f91844ace86e3d018c4603ff0e2c6", + "0x876c457846f27161c796f2d063aac7f73c2197ce707872c828af81ffabe91a6f353c6e887464c921718425d046c0a722", + "0xa0382a36d4f8007d444643bd5d85d0b6c3c892c7ef8158f51c878b13af6a5b7c8b098ac7a6e3241a6e13b4ae987addc9", + "0x81d668e292ae20a5a39b55e6798255c39368d9b83ca46e986c343ff9cf4f3140e3f07115995b8fc2554dc0372e4acfdf", + "0x85e58c950d8089ebd5d0a9d852c9e78d1e066c4cf1f1e64b4e76356745d3eddc13f1abf177dd32f0aede2f190053f8c9", + "0x9420d1c470588085057549b7e6544aca2ca329ac9b232187d8ac894b7a878d6d3ea351357174089943b43a83f057ab8e", + "0xb6ea12644c6ae73b8b9752f8eb8bf06312ca14d93fddeb5f79b92167ed78338964d620379387ffc9e12ac0e323f3500e", + "0x82767d1ca19c7672d38216bf17a8ca0a52aed5dca77684da56419430f9129ed25b6c547fce50c834746cab666ddd43cc", + "0xb1864c611fdb1b641708a5be8140ca0ac52d59d7c3fa3eaa10bd815f7f5e34413751f829f5fc0faa259064b73d43f4c8", + "0x92f67f02d17a1ead3b01478687cf26b40fb32f055f3b34feff21de083852460e02afb834f61c37fb91100709891379ac", + "0xb640a52bf00e4b29623c9b150635391b4dd42f0016e827daaad7aeff6e6a64fae4d67193664bc5bb368c72b138c76efe", + "0x941c8aed9a85a005059b83d35f6a70dae2e2b5f645719b567de0da3bbf1d2b85704ac977970a7206bd98609182090160", + "0xaa976af6c9809550721103fc8bb8359cc4313f672421a4ddd760bc4ddd86a036c1b4145049d9c8165335309fb608d6e4", + "0xafb11dfe01bb6a9d2cc2c040e18152645b4aa972fa01b6cb4285312bcb11a317107e743efb53aeb4bb6f8a7fb7741f50", + "0x95f8f780fae2878792aa3f60eab8954ecb107942bf07f0e2854173595eb2d4b914f4aa208f98a63b0ebcfbca46840123", + "0xb1dbec7871209fea98676e68d7a02dd82179a74e389bb9dc0eaeb2ac2d446d26810146586b637869ddec4caac8281bcb", + "0x931c9d571e50dfd2e1bee0c36f42085e4aa4e7d80a1c3bf99573d9d09ff710f6fa27f30712daba107d43d263b226d130", + "0xb080bc730ed34724851d00be3bba84093a296d6320fe7671a83364ab1faf922189ffe997eca0e1ce4ac2c4435d7b7f10", + "0x8dbbdb4f82398c891d16dbd4169716e078de5d677d3d550fd3853ff6ac8d15d278f17a2950333545bab823fad09a4922", + "0xa71bb5b71699082cc45037805fcd95e410c29575d229a556a7c5f2969fb6f957f0c63861048c65d5b73fc4680a5c3c70", + "0xb5bc06a742016a20c60d61cf55006cd7c2c7b8f367968f279815087b2bda7009c1773b9c91b8a4b78717b2bdf6a5e96e", + "0x91aa31c68634a81c9784d0d6adf4dc85c981e122635d4e1f25604c8184da860b1292d65324d4bb9bd8a002572cc96bff", + "0x85484fa47e235410e2ebc49f4dbbea9847ea064f6f6776dceb0d868280fe88bf6c5bb72e24c0ed3cb0a3f1d96ef8c9ce", + "0x88ab35f32986f0bbd8502dc81506cb18638298e856934fa374480dc455463482ca780385537e7ea73c4c863107b74f7a", + "0xb3022847a668b6d5d52d0af14d594c3e842afaab5416e3ffef21224bede0e1bbecb0799ddb7e095623a3a6f28b6d5f43", + "0x8802d0e6e5203d0018d243301c57934ca85a002f91e5763b2f7372816c7b3ddf719c3b743f2530d9b7330f4f35d69d83", + "0x85709fddeaaddead7a27d3f75e5ac568b0c9691c797f1505f5b33678158f5dff96ab98b921bfbc83368c6763420bf949", + "0xa45ddf8ed1c273d61578bf6830fabd4927f82e3efe7732d64a1c82887b9693dcabdad1e7a65f09bde459fef89c0eef82", + "0x970fb837063e059b1c2b4ec88011131e8cdc459daa1e704095bd327b7c94115c57cc1d9e8b4a29d6cc4f20895e309c61", + "0xb789aabda019356bc5c5dcb015f8e7c5da25150988af0d44cfb11d8702da22fbb43f69c4af889dddc0647745d162d91e", + "0x8ccd44696d8c52454c833b0b256ed0073527616ce49ef25a113cb9f16d41f39d27e3bf169ef0d3b2fe78f2a5910ec83a", + "0x9846a3ae6a2c339b09f53b6cb1262114d1ce2fa3ea43d77b04816eea6451e8620f0030ba428eff80d72d8e907c8f9e3d", + "0x80c18de89a31e2c8309353be974e42ca97dcebefc1a914e76b57609b9cb7c1c6298e2ee1bb35ab9d587f195010d24337", + "0xa43ac7ac3798af0923ef5bcf2342550aef8551c198a31b0bc9015ecb24fd2633bdcffd84a2c84f9eb72b4e67084caed4", + "0x8cc1551213a33114c8e6b3e00c68dd26b9cb3728376b498c95aeec60e7506a3346641ed5297fd4ead33c0e42b85079be", + "0xafb54536b43e311eef9f584b8f1074866f6d33cfc75a3294aad5aea870cdbc3c97ab6e849ef719e2e1e4386a8a360fe2", + "0xa2c5a2240256c64673372b63359b646dcadb108d785b4fb16a165f4b3b0ab3dc3dd5058582b25ed7b728d56d5aa45649", + "0xb35e3e4407b63edf3eb567fdbe03eef00dadddcf41b799c80a9c9e26ddcf0c6b0b9dc4df0a0c5d54bf31ac8273740a32", + "0xa3ce737baa7e1c1c69931a5b2fe1493a06fa0dcfc0b819ef8242b4fdae8d63bec8d15561d4fa24ef6d6c3a326d0abafa", + "0x910a67b377fb17d3f9cd1f994db52eb5f35a4aa002bc1b7208b259b12c64c095e4dd65ffe54772f8e2773923a890bc97", + "0x908c5ee131dea3f444a9ee2052c93a657d28f2f58e604bf08e51500a306decb2db964f68e87d5ac2f8207cc4e92adb09", + "0x8f3de5e756409b575ac2786590fc85de506f0adb51450f5c24669bb3a688f080c1cc37cb8e7a3c8db8e25c49a4bd76cc", + "0xaa62ceaef91fdf09d2ac2edbc07fcc651584a7e7d7d93e7bd4bb4c42105144c2da32326b3ae320b36a2df8aed07e5610", + "0x959fc29ce63dcac2b4dbe8180577cecf9bfbb6db6505d76aada43ddfde5f48ec8f6fed14fac19389f6c9ed3555ef7882", + "0x984cbe54156763d6ae078d6a8205cb6f9d63eee390dc5990f5d8e85b9a19fef563653d3dcc190c9b18c2232a916b1409", + "0x923b448808d9ac04488e8345d3fbf9aa57cc3b3f375af138b674daa0e5a864faaeabed08f94010478543f3e1248c699c", + "0x8c0823bf2706d9aa4c074673e9d221eb021de2baffe8b703e64e676b6801da73440b7793254fe4c8c48d2ff395e44bfd", + "0x93c9cb050494824aba0d57320e2d1dfc95c988bec46dc8d73f7036be9ce0d7de02e56ad1ea3dd8fc129100800aa639bd", + "0x9339fa01caba0f4837efca7a3d983fda1f6a479f63890db7f7beb837e3f6535b1f1d0788884dbeb73fa657410a4ad308", + "0x953f213ec904d4540b356d53eb88f646a98581a6deeebdf99a6646cf612e5b07110839d46c57b76545f6879f12371b10", + "0x99a4576f12de20fbecd3906e48dcc784cdbdf7fa0843c570c6f59f13cf3a559cc1f4882fc1d31015304090f83306280b", + "0xb07fb8b73793a236e58b7181df5a0a2e8d50c1d3069c475c6e178e32d14b6e75c45af60a8b54823c23ffbb316bd4a98e", + "0x98781507866499ce396730ee91a08e91d3be337690f7195750bd43a601a8f78e9475d5ebb43e347934429a4ff3db58b3", + "0x972a5a21354beadf80d8a6e449cc4f072d6b747de293f075b8e0925c89660db9195a30188dfc8b73dba02467ae02913f", + "0x827dd2e21ca88891b9b37e10f0d6b6304438cd6aaf9cb125ea7ed822078a233f3e1b49a8bc65f843e9551691b46cf91f", + "0xad3a4ebaccc157a7b880db6990a937e2d230875f509ce864fb0b7ba5febc7f4668191bf3aa55b85f3c17ce8b7d75b349", + "0x976672c981d106fe1835188e202adf6ce77314f8d7c8f797aacf25f00d57f8cfea31b057f6afcb40b9f97df6ea387979", + "0x8c77ba87e3e8fd5de85802a764b719d67f4edbdace75433af7fe966d3d18a94778c4414572b84f98bc6b5993a3225212", + "0x84ca9b0435001c246143e50487a55349bf38300cde86219d29199e2e9137e262a1619ee7d6f6c44b9551319f1ea8526f", + "0xab06e3313800a7dbb970145c1e89b7849db8a1e3169560fe2c64956a1f9d1d3157d531713a8d7c2213356b22fd3014ed", + "0xa0d04163ae987227aaba1ae82f74fd1b44387501fa47fa61b66728a0697155f48bb44b9eb5e87050a5bdb7605038db12", + "0x8e76d3e50853ba5945610251dd18736b8541bf72bd643f6b561cab1c028dd044c820fcf79a5b45882e7dde0ba6db164d", + "0x967ec8fdee2e6d61f0ca2cc255f4e72c41a9c1a62150318be0fa508b424487d9254ad135fbe8dcda55caa51b9901eda1", + "0xae25c496f872f7380d9c64fc9bee0dfdc0f05cc1d2d6ea7747e166cae7e67c17a72a24a9e351de15f52baad355625d7c", + "0xb8a95f3bc67ad2a2d3cfbbf2de2003b7bc894b3f99f0364fd181eb11d154a7193b1df9b671a3a8eb8bbabafeee2d1a86", + "0xb79996f818d94842175b06650a1e7819cb12c96b6ba37e61fa14b67684c2879e7d3884fa6bae06faba009329db2b0d1c", + "0x856e1478ef99338f144757fe4be68d935f0069a05b0a6209be2fac9ebc5cc669c6a80825d3c74801a54ff8b1a3777da8", + "0x8024798b150aa722dc599f288cdf455479763a9bf776da74d5f9cf76026500e5a0282d840e5ae5451a0e28d507b397a5", + "0x97cb767ebfc0a6cfe25666089f85e5a3705c108096a38151baa22308276ebf7cb3c04079ecd130cb8cae1689508d4bcb", + "0x874ff07def0f8d32f2ffce7cf31a43e8bc5e633b279acd7138ae938e46889e486c092ac34924aed9a4e1f93a91994211", + "0xab5b6bec8c81133b6edddcd048fbd526d59fc8a1f5acd7aa72d07852696faf5e8d305e85077450188cddd43d6c1aad27", + "0x8402f5173327a95438797cee3b107407e8b277759c942bf1b1f975dc63ab89b8c43f0f4ce2a11de6e5727e9952b8923b", + "0xa5179a16297f7a0913ba61d69879014b9adb5e41813ac33acb8973e2b43cbc17a2f9a7d98210b39471a47b534f0eea23", + "0x8f7cf3928b51b0b1bce18a34da935e7e2558595e4ebc50cc1cb698f0bf3c1ea0050aadbcec33786118d791218e1734b1", + "0x81552a8927942591572429892e5a1572c8bc4fa7901395a5a2de3ce9f1ead122e4e5ffef6cc8434b3b18af1aa62e45b3", + "0x8999a1bf4f22fdc884f9310e7a3f5baa0d32c04e887c51a20736cff3216f3dac5bbede43632d29704536d7f275b0be9b", + "0x85d9952816412a890a3e479025d1c0c8858337498ae28731ae23332c16a788cfe51fa9836bee73d03b253803096100a9", + "0xb6a736447acaa6f4d83998973cd2bc3e12d34c6c076880e6765513c04087eeee5b5dfe9394c120a85bec8fbe127f1f54", + "0x89302db4ea39662024c748ff5b22108c0f34850f0fda476051a89a3eba0e02a2294a4511666987d89f3b3bbcc334fdf3", + "0x88ef018d32e6b379cea9ce35d1c12af698d8241c4c7468a5d558099f5101f04ac8e49d09b6bf031a811970faf02ed0ac", + "0xb33afb11f73195a7132430dc3961252251aef42150e9aa63a8c7cae724f467543a4afec289bf27e10ccabcad2f7f812a", + "0xb706315deef0529725fa6c4520e5d320a028b4607d47fa6c4c8ca3558afd68ed65dc072a354d263d20405bb13ca119f0", + "0x8ba035d75939c1a3cfc72a9ad3aa4ade105880345eaad9286a182950368e688a182f6175357a2e62d977ff7ae08959cf", + "0xb47ca04b80107eefd3a596be2e990f5f021cafc6b7fb64cbb78802f9bb7bd2cec4f37755c451bb1fc8786a076e90bad9", + "0xb6fb1676fbdf6cf95add7173c264b820042511e34dbcafa76273ef5e4500ad1208b274855985f0eff5196e7200e5a8b5", + "0x8c7493894853f4e9fef5a0143dc134f03eeeaa10c80c5a72afb12f10ca5207df1c7bcefba6728d61f3f89d3978902629", + "0x97d14d9debd4228be04f2352e57d9c8138d4e40228c269100930e2a3f6eb6e66f2f99807be0c9857082ff8b9a089049e", + "0x86e327360a19f6ddc8d0362cf92fa84677737064a94d9d0c1031bae92b85abed36193428199b0f66720be0d6edb0d28c", + "0xac79bf758fe91d47d1ddfba62bba87f5e64d93f82309d4d07b62d78ad6ae95908e1989299db99ec52c5ad8c8f3d7132f", + "0x804712afd93328864a52a9f9ca1ae148de26fdec7d9f51d1bf8c0385959ddfb639ae0904c855180dd418ac21f9a8a7d0", + "0xa789e15cf3c1e911fca7f6759a2c5d0a281c6ab744f29709b8d0623c1fc197ed9bf56b89fb0953baf261ffc4bd8d1552", + "0xb738474bd1788f326c5145ca2a468d914ead6dbc338680f62ee21b1e5fed49fa501236d70dce5363a72147b0a8974c8c", + "0xa34019db5e8d5cb680a78c1692978ce0f3f8b21c1615ff65f3d103ed5a1e32884680c90d1dc18f0edcd8a506b1003806", + "0xb1b1f26ed57a7bf77257e2ab1bf314b22e47f8a4f4c5cd154beaafdc34b257e9b976b26c8d9f1508498b6e8c9e9fd2ff", + "0xa5f645d7a75741f536e5218d4a38ac75f5f2d759e62bde2c9b628a4cac299b477a833bca98010b6c2a8f30b85f407250", + "0xb3947ca7df831d68107713bbd52fa300411bc14362c37c2617780f5d621e211b8bcf5fb7099d903748420818d840234a", + "0xad16157ac24899485e7eae71eabf9e4262d01b8f8bde30d7c79fd00ffb4c347d765bf2b4a30c260c9fe2e5153a3e1c69", + "0xb1bcde4588570576872430e704a64d7130f921667c13101c3fb771efc1e0bd72c4ad4b8e35cbb98d709062257f0ef59f", + "0xab45dce0e29201e79cb1da61cc4353840eb8b47db99319ff7802d2e18f00e3fa8d5e70aa6a797684b4a6741277ae033e", + "0xb6977424f2f5b968a4eaa9dc1ac1018ca51e6158a6a4c189f0adc94ea1c2f30bb33c57960a5c972a1800cca2650e2f6e", + "0x899f48fedeee4badd5b69632f78a244756458529f27c75d05e9c54cb29579abcbe4ff7567445ccef96274c8cf5b7d31e", + "0xa8225095071acb2610d28d9ce2645280a84c702f5f5040df7a4134de1144fe1a1b07d3e28d4ff5e2517b4b2bbae674f9", + "0xb48316873f8245854568a37ad9c5fe9d5e6d9ebd60c9cbbf9e6f63c512bd8568e3a736039422d21d383378c77d8f10b7", + "0x8b40afa65e47ba365e723b9e24bd4a13335455e059346187356ff9abe99cf71eae383ee33bc184a9ec17b32d0800f158", + "0x96c3b7ad1e31b8d4ac0e14358655e21e687beac6f6b7b48dd3750641315ac7088352976e9804b9c625a712f9d4fcfc4e", + "0x914dcb36d621753286340077d16b36bdaa1414eac7a8e7ee84404a37f8fadda837bf4c5a932e8b0f3e8e673832e9b3f6", + "0xb20a438985a4bdaea41b98e831537027f4bf19ea9c1ac5fd37546eef117cd3d41b9c1b125d2302ae3d41176ab5d9e9dd", + "0x94a4cf3cc42d7055b55cf58959a7715232a273e66ec6f83fbcdb79d01769f7e6b1e328f6b0a910d1f8cf7a5ba4934779", + "0xa62b07dc466c2f83dcac7fa98215ce5bece548164e32b4bb3aac055b3c0aa68ef5cad58bf7d392e3b1d54ea6f0d9f0d7", + "0x9870784890da6cb0223daa367163cdd41ead23c300d246d62debe980fc3e7de0b42576309ae35da914474b8ed2c5acdf", + "0xb0f28a74169391fbb179ffe8647f3e6228e75b409c49ba81d34ce780b12d408d2db5968e9664b9de6a7416d2f6d1c1cc", + "0x857697b0222cce1458ff591e1add39f5632cb3aa2e589a64166738d8c00855e354c2ed44c4cee8dd707188790fffe6b1", + "0xb3566bb224742d0871ec5d15ee890755d7e6727aa7e2f374abe965ef8380b49422897545e2cf8fd3f58bc81f05abf173", + "0x88271995f9c647da82820b042e59011121ac723b4d0a2e461cfc1351d89cc10eb7d18830adf1c7b9fca55ed3e910aedf", + "0x863a43548db29c9cf35f24c1d5f7aa984ba21bb924dd9e09210a1feadb1e0ddca98df47e970c230879faa5e7434b118b", + "0xaf5c71b27157a2391247622a5029ba11d17ab4329001b01b3236f38d67ddd6b8902aebb48ee9c963983c16f6d8c53d26", + "0x97abbcd4fff0d1ee2ea777788cc146c1b32601fd93a5ff9908fdc2de225b954d8fc0c9874c258dcb90ecc7fd169823c3", + "0x94129bc418ff5d00ba3a3389b62986fcda5714ad25d25091db12a66e138a35a9e38999c4cf38fe7cdb1340c099c467ab", + "0x8a785f303473e763578a5bff75a069764e346224fa2dd8ee3105ca779cccd5939ed8c21f7369bab9947a4ca92d3b605e", + "0xb37d1644a00401b213f29191a238f4c9c32ba550db2ab3b4c9d1f02021a8f6972ab0fc76d0bc5b9c6291d5edb9632658", + "0x8e42a2c87d7feadf1a2dad9dc822b40029eeb8afb785ce574a340929c4c6ddfe4d750bd3a482e62bfef1bdfdc36f5bd9", + "0x8837b0408f48c8b975ae777b0516c569dad0daf607da51f187bc2c67d3f67315340848fabf7ca78dfa46b05e3fe33005", + "0x96d53e8e9b14e602dec666fcbff8ac2a7ca0474605b294365bab5f5228d8cf0a17a772cf2f37f7de3607f7ea6127d0e0", + "0xb286888ab9afd161a714fcb1954f6046754c1e3e398cf639bc215327057ae68ed69113481da88627839b551cb9660be3", + "0xae5747c882d3ad685e481b0b42907f0934a717ef5b0bcf110fe3125d40628959b879186464f95bc4a69d90754034c141", + "0xb1ca38e7b1f87e4c878d4b878afbca750fdc2509f625a06871da830c1f68a6cb20dde7d51ec73a78454ffdf54451ed47", + "0x82225700e9b32f416618b074479302629114761fc721ff492d792d4d1a4d6fec887185aa81395703fc8d07a67fa4d87d", + "0xa132ead3cac6087bc7bf5153f29ea858a115249d779419e5c52f4d1f2180f4631caf45ab7cf90129af68bf67665b8dd6", + "0xafd4778ab2921b6c9c3d3e2d8ab7e33882f7fde3c8f66d6704f0859f3bec468178eb25a103b78ab092de5b694f2d2ff6", + "0xaa0123ab3e8af146e974c9fc72dce94554cbab212336c8aebe618ea082c15ef02c6b47b6c575d262d1cc6b0cf2d11f47", + "0xa5e35113393e82c0ff3a39afc99a60f90993d5e4995e5d21a9a395ae31074ed5e2244214d4dd877c3d89e52fac6c4252", + "0xb2f476cd5d9df15e624713f22716ff01790f2fe16957b526677bdd3d203fa8af98ae58daaffca10f84a7c61e29ba1d79", + "0x82d6d062828337677ae19ce13d27ef45ee55270a27e674946c7c1c762bf43af6391d77454dda4dc613b519f4cde43636", + "0x8e86b1803d4ee07791269ec9175dc3d3b595197c089551e5bec3edc55c77532235e64848aba62e43936d3e9438635f5a", + "0x845b7233e40eab725c241853013d1884d782457ec188ff7ea535926c36da0893882fea2c9609f96b6d85392471b71d2c", + "0xa2090ef73e125c0809f2bddcdd7b74b4f4eae452d76afebdf47691d2afacd1db7c6a3032e9a4c4ca744bb496258b8ead", + "0x98e759616bf468bb4feedbebaa8df381d01cb4b0009a5ca5fc980426e160698abd6fcd2095cf964eca6f8d92fe1bfc42", + "0x8a29df48ccec0ecb8b3d904078897d996ecea1d2db6b40b79fe51bc5dad04358d7f7edb6543d7d1cf0c1f54544c3d85e", + "0x9422e88414d88e5d84b17f9d2f1c50fb48e9c5b8de215dcd7c52bb26a6ea71cf92c90f3004c4fcb34040eacf5b60b06b", + "0xa643123915445bf0e528d36dd7f2da9a3b993f93a7fc9f6148049fe14eb5a0063575d971ec955aeffbdce069d0bc2937", + "0x81741f92a157bfe12aaabf0d81121e5a8c7df2dae86f5fdba826167c4558103363c653a928babf4ad7e3e80634d26375", + "0x904fe8e258be2500bc5566c3890a9372c9404935ba19396e8cd30289cf02bda13ff3d776bef56dd87ce57aba0a8539bf", + "0x811997c1d70feed33ae3684eee512a46ea91400b39638d405a8bd6f1d0169706f48d1c04beb1c5afc5b10879390a1a0f", + "0xa4fff30378dcf1f04eb97951b85abc0f5257b9e53b7bee814a5acf060919d73504db14d55edaf54e4030b4c1d7278c57", + "0xac84f2568084ee7a715b2387e3fa3b15e6940a27ea99b4fc9889c828179c55f29992b68d719323c2ede8ded3a4e16553", + "0x8fa542c15bd29bcf72a34b3c56eac1e7d4e4f3b15b82672cd956d23a9b9863233816ffbcc6738a225c86d9dd13d1c3d8", + "0x90d94517e7f1236e49ed6903db72c0de3098b42fbc76afae7abc1b09a903cf92cb1bb6a6ec8c29331e05b8946c2e9e2b", + "0x916c0d6b1fb7c74c0001599211ca37812f30c309cb6cae529c876221c5e7187876d37268776451df2aa44f91a3a17a11", + "0xb9ae0c4f0c00e8b07b489e015711346caedfc7cbbcb36acf3a2ffadf2a8f11118f93cb70181c27443d42634b2f2f6d90", + "0x97a51eb35da8b67e82d55fed968ac9aa42cf2d1348ac146d534344c4c7535c29ce25dacf9901abcd7e8c43a83e34e25f", + "0xb2f035822c552cfe3325da99f28aa31b847477a644402d43379947ee219fed751497cfffd3536c91f2474a94bf758203", + "0xaa2fc0777c208a2efb2884dff33c459f2f6c9dd4cba360a65918c8604cb02fd493c8e5d26069953bba56039f8bb694ea", + "0x84c63bbbea15e06775bd39f39995afc613586fcbaf32c9ada1410dfdeff09b8e7f3dd0c99b23c678ee72e44543ee6443", + "0x8259332662ff222d4d2f817bb94033d458e650e5f6e2c31ca84c6f3a4b3d2e8d1f40593083337a17193cddd960ea86c7", + "0x899fc292aafc17d01c06cac50a78edf1f11c8c3253f4b32116625537351a1e82ee3cac67725973e3563fdd73781985b1", + "0x92d3b9aab29854556666588d94c3b49d159c9ba9046487583469ace7a6b8ffa61164839dee24d28dc2fd36b9387b6225", + "0xb54f62077e1e42e18586a03b3d3fbe3fd680dda6988bee8aadc95dcde46c215167b261433d6cfaad8e2b3b6207662af8", + "0xa6c021aa10019319f944f8a77455ad5b153a502dc9eabd9d306be3830a4fa2539e3cb380355953c3581f12348b919214", + "0x8cdbc2c995699cc83768dd23383fe492a1bebcdfa55fc4b9d1113e510a6f4432ae55fd57db732eb56265dba6ad452c46", + "0xaa474f1710bf6556538fe389694b4fb737713dbbc9c93d8a879dd3aee8e004c2441dd14b5f4cdd4a98e804d031ce00ca", + "0x95448d62b1503e71d47ef4f5a01c60c938fc3cfd9280d7b6d3490ef331131130630425adcc53c9c96f262a80c3251e4e", + "0xa4535757aedbf6d7b9bbea99f4bb7bdfd1c99d5d976bd8d4f8c69ee09c9902ea81884d8b6f4fc084e12702fcbb9e4b3d", + "0x87796bbc38d5c2d9a56a65ca91a40530b57fc2a768e9e08a2081734bde163f39e069edc99e87a84b539606164993f30b", + "0x8cb7647e60f023066c4835c956186b9e997a7425cc38465e95be568ab448b7303977c7ddaca73b78f6bc137f25e5e020", + "0x90584dbd8f672a349682effe2f775f2bccb1911b06d20cd02f3a6e30311c6678e5082ab87ee47af72e0c064a43592bea", + "0x8886147e87a552c74767faa64516438d6473ae275e72b4cdc174825696a4d7878297b1ecd0fe1a62fa4559ed232e9e26", + "0xb739745959c324a62943a225140daa51faa8e41c8e20ebd68d6f000351101a89341641933dcb2ac5b3a45ebbbf7fb26c", + "0x814f858b4c709694472eae1c82cfb7370191ad6d0cc5aad69084fb8e9d81e90ac2fae52b4051af25f1b806c273f61e0c", + "0xa00426131acb84ee08684f2fc2a3ef01290e48e6b5f96bcb0459adb62f4190a4b2616eff2a2712991c48adc551ddaf64", + "0xb37a1e92b72e3ba42b79dd997bbeb031a392e42606254965597ea4b8a2ca51f8c324619fc2b9f886e17b632ea3bee629", + "0x90817db93eed264f49445d1d3a14ddc0d5ca93191b6baae278b4c48231a56b25725ba6f7ac0e9c7326755f0082b79587", + "0x95b7f470ef1630dee768698a31398e8cb407df3b651a15493c38f6be6c7eb387148124a2cb1fe1237117617017c12203", + "0xac49be639391aa5dc08e8678cc690ff617e9a0ab40166285f90c2d889c87ac70c455a972e61cfc339db59be4394a0ad1", + "0xa6f5a698508f8047edc45bd605ad4e88245de20013e7a4e51994e99fc60d81dc945504b24f23f7241f28059f4b5d6756", + "0xa4d30a6db06153074871c6adb0ef4e562c1491c1f9841c110359dc41a3bc0bfcba3b49fa53c29b8258a814b8ba1ba328", + "0xb25a500efa7d38f797395cbec660250f4a00d104459cdf7a15b541db3917e26bb7568526444d469d363040fd094680ab", + "0x8444d11f8a0c686e2b22642ba1b28cc556ab7311686028e3fb4040fcce22959b7b6cf244b77c711ba86e350e17411823", + "0x8ce90bfdfa93cbe58421be78e30e471b2c6e6beb1f9b3f85031cbe269305e18d25a2170819f2699346bdd735b6f5d664", + "0xb73970a3dc993e28b71bc236b3391acbd85a8cc622b79e669109f9d3ad7ce7a01a8686e75d85408c54bb70ff9771ca80", + "0xa64cebe05fd027069a18f152a18be155ed65b6b563696e395e045c8b2f0455fa75c2ff41c1247e596451b36ddf258460", + "0xafec84a7a480b09cecdeafd025ee3ee02e3b3338b02d26cb3b7575ecb895057650f0955978d1d732ca2e6b391ed97728", + "0x8caaf53038bfad6e0651e61e9a44a39027d456ff3ea46ee9d8e190698d5a66938d5c5723dd7bc75f0ddab660e178383f", + "0xa91607e39108d2540b4b5c9d33d96328f56ce9574ac9d1d4a81ab5c938443c3d7014e19f77cd55ef7be0a408e44efa43", + "0xa3f4c6629a3c0f34ea060a8b976096e6fd3a91c24d2b056e9b6b60088bb0c706e25dfb31079f42e0ec031aa840f46afa", + "0x96b9c7d3f47ec35ab0270cc57841e9f3b3f5bce3d26faf6abf6cf657b6e949ce0bd1ccdcf9d490beebce722aea48caef", + "0xabd2433b4003b7d861b35e99b51e2eedaea4831776e7c289beae2b561ad69a771233e3d6bc4a7f869d0744c5be61b5a9", + "0xa989e5080d39d4031aea86c03b77abe069ea9b7fbc515c6a79c825eedd6a9bf6a0ced1891eed20edc605f9e25a691f74", + "0x93ca5b311d28e4dfbf4de84a1e1530a9153599e0853c9abd3671a1ce04995e00f7d3092895461137fd78c72d24a99494", + "0x8acebb0309595f4eeb990b7a1543f0633690b7469ce89884d5654a7bd2d2543f09232693a04e1e1b445e6e0041c8b242", + "0xabe3858cea5a873a7576d641571965736d55d46f9040fec219803740dc2a5b43c72689e94c9b61d3c3c44dd3a821b694", + "0x947cd395aef4faeca9b78b6cfcc8b2f8f361de884b29181266fd95b21ca6176e7944058e20cc77c7757fbca4fe445394", + "0x8c2e50234c75d645f3c887693e2439ef42433eff328111b9c37aa3ad5a3b21678ee44ee2959a91610006b27a0f5363b2", + "0x967253e02e34069ac676063aae9a493bc6d52b8bcbf1da6243bfeaa9fe05f8c840ada0a282df9c0180d05eb866402441", + "0xa16a4c9a11686a5294d8329983c8a4aa0e6e5ad0003ab319b493842e8d072aaef45c3335d9a64bfde6bba120a48a72a3", + "0x85187b866fbc72e5b42b91d76e7ec2647b93bedecb060b7475236d7d152d17f901c778b704f7c2c1d3d269341890c233", + "0x83b192d925e3f4a1fafcf22cb48083b2f88632ba93c1d498539bbc4997f61f74a0a3b8d4947253a0daaca8999c407b87", + "0x8338eb3e7f549988435f4f618f4ae1942c6756bdc28a80dba7ccc530bef161c0bbd20f77c8c4d63c94e60bc12f7cd387", + "0xadc869c5acec5e58459eb804c2141e03e3582ce1fef7d40fc1dffa3ca0f923919e291a2ca4a60129e2a470cdb395be31", + "0x9279068c28840f2c34e48e9a7e7e9406907ac14bdf4eec7b8c28ebcfe16a18fcb047015e4309f19e6fd73d6e6c454157", + "0x98c4fb637a868f161f2f4222f93f3bdf13a61ec1f4e4c20435c150fca1bc0c01c790da91afb6227ed2a6aa540d70366c", + "0x9209fc7b307f40294bd9cce166277a7ade9c64277c600b3ff09579fbfffa471a887061e9cb5fac97c423eada30a8a52c", + "0xb1d63569d5d47d052f3a6e8b2c390bfac1e92098291a2adb209f20af875ebb2a8669533155b1d15b232690e26d399ab2", + "0xa2c975c004e69e7b0f22636141d34adfb2dd1396c7355e95fcd0493e931eb7eb99b4df0f0f202945d7bf837809a29ed2", + "0x818f48e65e337913c52e9256af134f4311be84dc332e3ac4cb5ef659b9c6e9cb34f04b0bcc0e2a3a574c9c3cc51d7368", + "0xb92b63d0b363a368a348a4abb10661c38ced99a3132afa6cf002b81e6cac26f862c9d0a6886aede555d7bc453753cd37", + "0xb4051275cef361cdebd254115275b0b86692d3802241cae5e2c75accee7df98d3165cd1de86226f382e736b12d9dbac3", + "0xad89d85749c23e045bcb95c3433eb8038139a51c8edaf06b5cb235549a2f9ad17589097ff8a350e934c8662a8879a3d4", + "0x802010e6dbf4265cdb5b5362c0b197317f2435253237561a3a7bc6766f98b129ee06d370382998ae70080624fd65831e", + "0x8ed6a5b601a5ee11e983035f3109075444b063aff693b3601f87c0d76d2ac253459de48d0fee32330c3785d38eab5cc2", + "0xa6c8bee787c4b87137f70c2c54ad3ad0955269c7ea57ddabb1a215e613e250944cada7f241430c0ef09f8eee29fadaa7", + "0xa3fc6a643e1ce110b08344f8913ea7f8c9e44bdf1a02978df8dcd3671d9b357397df9857fb11ba220521d1ce40064ee5", + "0x94089626bd9c81247f45e25e573bd6bf727a0e1a7dcd630dd5e661f65d4b6f35bdc16b64da648dfda404b5eab39d9152", + "0x88362a160a95f01026a2e52aee3521e8496340f96a35351892034198740d8b6159175c60b910a4ee05af488dfa578c8b", + "0xb55a5b875f5594bf41949c212543517bb1ce34db3a896f93d0216813261aa95f73663c789ea0ceb2bf8815255bd328ca", + "0x8f9acdca0158df5ecea4d574e0ef0c256ab271d9d3d3bb4100761f5062f0a1a5d2b8a23685097a1a2b2a08287a2e2c94", + "0xb6d4e3bd49a17fe7d929b41fb223eaf93141453f7dc233eaa74424290014a63ca6a099174b687048d59cefd41fc720db", + "0xac0fa8aeca20a0b4189e96c57c85a2174338550855f9d0ff0c553e773a1a1c32fe3f8db7c8362bddf601e41380c9177a", + "0x82f05710f08f12b206b2ad6a2d06161c884b2511ad90b43fbfcdf54933c2360b7c85dfa4f598b5bdce8809a803d483a0", + "0xa2ca711642fd498cfeb897e4072d13e43b5cdb2480449975188fdfbd4b471070cad941af03a2dd8938d3c376366fd199", + "0x90c27a1df934339bd0821cacaac41fa70496900044aadfccf6e5fe28ceaebae5cbc500fd6f2f88c5552b7fafea79d06e", + "0x818651b7c7a6f691fc47a61ae4960bba7239007e14930f3a8cc9c95dcc0b03643047671f819e30d89c2d1891640fc13e", + "0xa88f01062ded714e7f2f1523644222cd8e8cb8e535eda88738f4b4b19079f4f7be664abedcdb618ad1de3e74689042df", + "0x8174282a183f3f393667352fdd60460d2199de16752c372a44465f8b71ca134c410d1d81f15afac839748447875f8643", + "0xa358c3e53dd70e1a608f36a1fdbe225e28c13b5817dba890ed8e82adcb7ae86fa68ff6cbda7e02e8116c11587ae1ded1", + "0x8aa0bc208a84d5a58b0206a8fe5ee3c8d224ccb86b11b7c9d924e16b2853a6c3623502dd60b94f8d720810e0079078b8", + "0x8bca870eb6cc5f7b5f6b84f88b49d9a3994e61ca3f2ad963f28f925e58430887f5362ed4bdc2a2a38b5fb9e774a75cbb", + "0xab86840fe84b1eab81675eeee17f85a500dfcc95dc4872e57b39919ccc71b702585ab9ac66146d264d2bc8fa39338a72", + "0x87c46966a4bbf2523dde607852a40b26cf3431d0bde9b2c609997c0f29c5932d28014026862abb7d4107b28ab8e2ba70", + "0xa91666a8c846a9944ee7ab243ab535e4124ca8bbb756777609aad848527b354060c484acc19c333459c09012670f03f7", + "0xb7145784894c6df87d2ce6a06cbaa713e46097b5f83db60e5449e62ed5bf382a7fc3136e5599226a2fe7951847527c4c", + "0x951bdbaaa06ba8b427fc4ec6bb44e93e70692bcef6369fa06c7a6882227d27f09465f37f0a5868ce43ade188a5f37f8c", + "0xb69662dd5dcc9ce7bf24be8a0e85e80c8e5af9b030e740796f91de548569bafa2fbcb19d98e13900c76cae3fb601a8ca", + "0x9630a7eb15718a2324518f78f26a71e3c801a8e2eab3236be7623807321c128ccd79c74ab657ea8e115d6ff3078a6887", + "0xa2f98c2084f8cd556cc1bab19398e98921ef56f6445f63444384efe5d7c895690c57d0d94cfd24e99f63f5e31859e34c", + "0x8c3994d3cb76fc6ac22ba2049ea4547db92ef78f009d24f08695b282c95e395f2c1477bd52d3f569d64551aa5e259b5b", + "0xb58571076faaaa547df9522b48c684b310500850339d79d2349dd8211bc2c8307d13cd5bb7571e0b5baaa013b502e410", + "0x93e07feb14f691e66be756b37467f290da9a6677b8ff565964f010fc20ed9c58d8c712c4abaf012c787bbb22cd1473d9", + "0xb4bc6159db1578111190b19aa678281eb2fcf7a82c7f699da7473720493e66e0ab54429da7af24315ed9f7399863c954", + "0x93cfc98563f25b45c15a07780ae0a38c4ada52ffc1350233a3b45417c16cef92e7926354b761d0e0de55aea4c1314406", + "0x820c37c923807790d77d2cec39f0eca63fa3ac6eaf0a1978522f0b1d293a5c46af3a0b4ca542cf39e796afc1fb3d7195", + "0xb87fec722faec6a739355fd30a2757e5d184c07b5bbab8581b74eabc2da413faa6d11ccd65cc93f886c788239b1eefb7", + "0xa183bac7f647a0c15b14089879a8aadb712f079bcf2078d3c65851137a00dd3ed7e47263c064feb19362f98180aa425b", + "0x996233b2010c20e0246295735b6d5b3e932f2aeaf0b35aa3dee66b6296f39e2e7ee95a7e1a15838ff3389ecc8052e315", + "0x85c943e09a6c77e15d49ef4fe57d89744fcdb705ca370cdf70b3d84aeeccbf2155868f6790333f88fe36e08042ce195d", + "0xb88f82b35ae14a3e6fb972c47123236bb7db08b9f9f3828033fbf5a895b09b9b0de423f1caa04b3e8e754409b21f3a52", + "0xa12c957409b6dd335964532ce3c045aabd280188b4d6ee809cef479e51dba030cbecc86b0ea8777cc8828c087006c5ec", + "0x87227fb4299efa535240793cf0079e952e971a18ee62cd71a62d6a5db921da669c5d8eb1bbda318ed5f3b03b38798a73", + "0x84b5c7585fb1c98d031a0bf6fa8ad5484c7766025af552cdd72e7ae59247deb845f8678862c44ebe640a7333cef8391b", + "0xa94cdb0f42ae3afb4b1878f960669bd99008c7ddc24f2fed45ca521c60472e5587fa9bf97b315efee1f74619a4d9b762", + "0x969a9bd21a6a90aa30fea44e397cc88118fd5abeb68839556194f9ab0076806aa320928a8ec94a47c4eade15498f5175", + "0xb2fb215bbe7acc3baa04b0aa9be654afdc450faabe2702a0c9fa760c9e9389a58aa5e3a4c6af4f6f5c934640d90b59d0", + "0x8be6a43071464e6c7dfb0e9a791a658214c1a95adc88f144d8349ecaa0e76b8ea5f88cfe95e82694bc170e49a43ec4cd", + "0xb75d56cfa1f3b61428d24784d51dd65b78b977bbb52cd71401ac7d3c2643f3dc097d6e7668104af402cf7e7e6ddfbaaf", + "0x811005c49d1be377ebd2fd3bea9771089a0f5675c55e9da5f31fe13cfc9d1ff8520f085918279ccbdb0363eda16f8056", + "0xa487f7000c16429f2b7bd7e8bf4990bf26f666f8aeb11a99114d33e24f946cb0e3e025ec8c0b0721f9be101504c8a1ca", + "0x99b72e711ba7b97083976b2db7b97346000a78bff9b20ed910eaad02f6c03b45fb3f0f1217b328c3e2d87b481eaab52b", + "0x828429d387a0b83ac8e377b32db1c893a4555ca253b8e3159399cd053c5de726655a2ad39348c8e7ef11b37b0bca78e6", + "0x835de10c73da7f0c07295a3306ffb18991334c86e5fa4c6df2d8091e8989f6454c8b43721b13696e1f665480a64284de", + "0xa4ea48f0cc5915993c83309df99247dcd7df4c15c723d168759175010fbe8d84adab8393707cb338fb90a6a77b69745e", + "0x9976bc842b06ffbc5afb309eef8964908802e9a5c733de4a8292d5d5773ecafb6daeecc63a8dc8847d76b77d4c3915ef", + "0xaae89156b013e4adb4bd8e7b6007937f0ece09af077fd407798e4155dc09a129d44fe8f8b5f6cf6b3c84746181d7f4a3", + "0x81891cf2d70a8c326c6870a8158edb79babf302b4f9d51229bbafdf038cee39b97f01694eb719df99a62478bbf909a85", + "0x97bdcb526108ef3cc2205aac074ef3001d528f56b4889573e0f4a3a2504232adf23880f7fa2f57bb787ff30b89599da9", + "0x9778949a95fc663c742e70596faf91ccaf8b5b93b78bc2d4993480722ffe10bab3c8df3ae50535d258b6e749a0abb86e", + "0x88bffdb927dd88c1ba3eefe7da3fd6a42ae494bf282e094893e144330cf08e3f518f47aa1dd76d6f249cf83e6bb9d4a7", + "0xb53effa345fe59917f4f1ae754e9f6b8fec9bd34cee85872b3fc47e52fee29c418b9406aa50c8d5a2e853d6f2056a49c", + "0xa616775e7e77e846066fcea413f0023dd491d8176dc450b0941368680571cdd236f0f83883d268842fa61dcbf3e4864a", + "0x8b5ae13dbbd07ad19bd2c7bdb48eb3c760244fe0caa73d28df3f0c31f5418f8b06862526f5a84bb16b2a92eb2c7ebc28", + "0xa62294830750dbf43ea497155b185d611d75e767aafa8c2c30256f8a4875b6fdadaac429e8363848b39e964cab2aaabb", + "0x94b6973fb87c2efef5efc0e7dd7ecff5ffbe76320fed8a20d730f9e3751efe9e5db39981f45477ddfe037e18cb971930", + "0xb931b6f789947b5298c258c8f0b221ca927c447f190f0d8afe2f18ce9b90979eb3247e77e128a1d6c57d3bf5523e787c", + "0x968259d9d001a08c0329bc19b2438b48dceb5942bc6ff9892d78fc36014f1b60a5ce7deecc7a808e41aeb4e26143aa41", + "0xa52c1906f103e3fbee8c12fecd93f7b7d6f37eb733147bed841b32caabc880fd6e72884380a3cf93129d9800ee7877a7", + "0x969dd12f0f6ef0b868e21539dcba5dc7327033eb546570f5bbf91b13f9c2ba6070da430538c40bc53a2ace4794514038", + "0xa853a74380d78710c212bcfa92d3f9c433b8ccc6936356f3bdf923b8e420e1017bc530ce73bb8d04bf7a25b20594c753", + "0xa84dfbbd3d9f409badc8ac2da5a0db98124df9d01bd71b1cf5b2b9c32866309304848a4bc1fcad1130bddfb9636c1b56", + "0xa9599f55173e77dad54cfce6ddc77bc48588f36b79a98c156963a2f5397262ae07634a98ab9bfe1aa6357f78aaf89d89", + "0x91e429b5ad0bafc09b5eefe600e179ef56f1ee045765ab3d5ecbd73eb201305a6de4382038b1350abc70bd1435151a0d", + "0x8785056b83a726622c565985e815847b63745fb66b138d24c985d6f42d5762c61ccd5172d4a3237222c881e5f036b98d", + "0x85869796ef180f500dae84f669b76a9b245e2ff4614a58d74820c22e439837b7d9866f480b72d88f44682be54c6dafb8", + "0xa118baf9c17d85e22ac3315f5ba9aa4e230ca2a031906f99bc43fc750a0f96aaa5e6774d1cf16b492726a37db7b51327", + "0xac8e33f32c1cd14c6de14e75f83b8518bf1bf6f0a70e23ea0e5a29f096e2992f1259a121bbccc5252b9668c605240435", + "0x97babe93e2016d29af74f776e167d82f1cf2242202bdcbaac4a1eba2b3fbd9e7ce57cdfbfe799a0f6a06a0e6838c4e99", + "0xa70acd7e1f159adf7381d3f3ec2cc42b56232601f18ee62fb650e13a80954cd06d39a57217ebf4d8927e28c910671ae0", + "0xb33ef5c10d0588df0b9d2d963912b294a2375a26bd25225f002cdc206a1cc058465c64180d348cccc899baf3d677033f", + "0x93086926eb1be21ab929b0098767611bdf1a853b6b67045c14f00714f806f8655be37150be1da05c5d2e6d9c66057bf9", + "0x8890aad532a6c9b818ddb9f1ea12d627010b4120fd4969bd239a9654a05116272d4cf783ff8256de337bc01f9b4154d5", + "0xb878977630f647a5ed7c99f59ca5eb653cd647277b899b918e5f667eb17b6dc433b56c2f3a2a18a785a4b5a9ae95f372", + "0x975582fadbc276c9afc4d8ef767a66684df5f56e898d2a8749cbc2763982c013e5fd0ad0ca7ebc67758124a609b59663", + "0xac45e154a651857f0464db38afb2fb25853e8bb1eb476df31908b13b4fc86491d4f831c0a15ed6bed0c873b3dcff55e3", + "0xa778d373e361753964a7fe4e1d28702c62a919e5203b941b04b0e74cdd3b4e838cd9b6dac3c39dd523f3227f1b5e6766", + "0xb1bab7994941f8de4235e2e188b302bba847c1618ebdec7fb782241e9eca8d32dd506d254d865e0319c66396535cc484", + "0x8c4ae5b346325f1d1609328e41d20108c4388bbe021361a86a1f9681caf1e6fd64896d72641ba8c984e153537317420a", + "0x8cd312c6a23e56657624d21f230a2c22d102badbfb2e38a8c067186abc5a459d0c78433ae7b54334591862c18864d7fd", + "0x8739d71181c5a657c6fcfee1df71756c3b6b8c48e8d97460fb64eb891abfd23433ccd08574a677fff600ffa5519a2363", + "0xad3c8d1e9eaa6f9122fb14d323318bb0338c5f9f29c719715cbeb15a2738493930542769b596098a5f505359c0314381", + "0xa6d78b78227f8c1203e502caab1213092f320e77a6e9729e1659cf81e981cf905170e99b56c4eed1326320acc6aa60fe", + "0x8e5ba0e69e0f08a49ea4fa28ce0792f7ff6c032844ceef11be90b2215940d4b0f3e4acd5e4b189b189b0a0ef8045aa26", + "0xb7b31957e7a85a640b851d4241c7b2f6af64f59ac221783b88c1b51cc4185f5ae6446a3c7137ee072c2eeb97c929d7ce", + "0xb066bb41c5818d6008349dc6015ab780633cd961b5d947062e14618c1ee1abfe42139c86b77e7f5be0c275fc3f5b8909", + "0xa6597158957e1a0af153183151fbc4c73bbf8156c77f7b409d0f97470b5e127beee6d9246bde770127d3e3ad400cddd4", + "0x82a6de6344e5bd0c5ca95f3be1ccd42fc974403269874603944c08ae1cd3ca887e66fc51ed61da8b7af3cce02f152e6a", + "0x89fd363aea11ddb2dc71288bb534a4a6e32feb5b9e4b88d132f4181f2579f8f8f39d19fcdb3d3d7ea118b9f4431520ba", + "0xb70c945986c8227d239201857e12cc7cebc932c3bda8913c82b62c872b18f866313738788e11bddd630bb5953492fec4", + "0xb4e3a1e8f82d988c85cbb58d9cec69bc63fadb4c1c9a09f36b5a61f1ee96baac1a9458bfd0f3250e1734ab3fc6c0a0d6", + "0x8d01d1eff496e8bdad1e6fb4b60e4bef0ada39a378c8b57cce2c115e611e0c4fa54f9b599e4c34dac925bc06e940eceb", + "0x90857123505746f7bff08e03b1a90f67051a71ba47b49e7bc63f1a8ec30e02a98aecf56465d3b4746aae166081099da8", + "0x98b9d3b7fe1d5449bf6498c30036e3f60c8b90962fe04ede9ebf605d07497f617d99d51f0f0c0c14381377de765ecfd4", + "0x891e7867e70582ade85730a21c19f6fc239760f76f8bbd8c6dafeddfaabd6fa4845f62d649b454fd3af8ae7028ee5f9c", + "0x945136f71f290d8cc6bf282b554cdf8ff92200feb7901987a1348f2d2edd3bd7b7bff6f57ec25fa302a27121a1a959af", + "0xb574d8379842467c5f3cdabc2da9a45e5a6083efd7298b077ccef2c4c3bab03abf1dc11507f4c896d745ffd41e4dd319", + "0x946fea5c1b1d840c10a5a732c7dc23c2bc5eeeedba6956f85ad78fc1ee4a95b309c0d4a4919d1f7155814e3f36fe322e", + "0x98befb2f7d0a860de662f28968fb6200cee5a33cd7a6c376405a9cc6f0718b90fcc5cd5b9142e84e95223a3dfbd10f29", + "0x8c5f208ca23aeae44a056bc21e04b570c13bfd49b14681cc085d5b318e62e4c088f0bea9dde52376967649919b59829b", + "0xb14478442f8e985618272d4c56d43a28e10112ea200b52fbb32b19a4d1eae965fd5ee1e9772416d52dc0e2db57d3ecd6", + "0xaa308b19a57159ff702bceeb69a385f91339db7e683db991b1414bf3af9916d8e52dec4c492d7e8b5a5a011680defc1b", + "0xa8ac18a1adeeaadc192e15b7e92b611c936ba9cc9aee01f03c2a236827ba527039c719f16149d7aa5fb297cd63878766", + "0xaa09af54f9a5fab6a61552421c13ca62f99fae212a9606f8a5a050997271ab6dbc763390bb98d90b1af3bbe9e8d9513f", + "0x96b8ce26b346a0d3fc99b6e463f0c619196cd85340b795fe1c1c0cd4f1b3a9f2bef5454e0bc7d51d75ce26f217460602", + "0xa3efa46273c392704ba0718a44f306edfea03b1a6be0bc1e5c67c62c89671c650eb8ac9bacc35372ade6bed12321f1ff", + "0xb484881108a43a1dbc16a6e7369a04286f896aaa1dae847b4019fa287c18e9d82c8ba4ad22cea2837bc220524a9a7a17", + "0x827b63d83e15ef61d54dfc365ed8a4f9e200d526884ec4b1d35032227245410ad7e1b1dd3c1d0ad48ddc4720f0fb5e1c", + "0xb513c3ddafb01b6189590b74d20348db74e400c106202dacd9ea9552ee2c642909a7a73ed7ab45a986eda3a0701be79d", + "0x831f4030463c84cc6cced28dfce0b3e6b6ead01afa200ddffd807f31ddd4ab93a8699ccc9d26970407628d521118ba6c", + "0x86312e006a800720329b82f6feb2934e2cc156958ba394278caa6766ee10800d2fb8907aa92346dcf6d389c4f66f5e1f", + "0xab6841da372a286fde1dbbc57cfe967cb4bebd6fe2ab9e317cb9f9eda04a4db0d5844ffa8db72eb9cc6bf311667ff6e5", + "0xb8238dca3f2be29bfc4aa65a9f59bd4e2c17fae78114a69bba1170782b993afacee3755e768317a923fd32d860f6a74f", + "0x923c1b60c052a3ed4736da7e84e52b0e9e154627cd90cae335dbdf05af109ceeaa016954d6e47fbfc40d9d5649c198d9", + "0x96a69d18c838512d95d97735263a8cde752b2bc790b3827ce048e177a063dd11e2a69b86b3184873503a68170b2ec255", + "0xaed7c3af469a93c22afb47a904bc70b7d88262ecdad48ea6a6c07eba7398410bf5a97a786beb11843cf40ddea9a37098", + "0xa6b50f6369ae558dda3ceb8cc9d99382a1e62d0d9804b903086845479b9381fadf8d4595c2f342307c94d09e02e0ba2c", + "0x89fd703d457580a76544bbaecf65f93d3335d7a22e93d14afbaa61e5236d9c8d8b005e403e9f5e7a103b0386971a5e65", + "0x8e909a3638208c8f885820af8bca6ae839128ce0d902a2b7b6f9713d21da8c943a7984d9aeee7fb104df4cbd1092967d", + "0xb41e2d7a1a0082eef43e886eab5e781bd961a83155d6a14d62756ab7144a208f4e4574d47d2ea094a7fb50d0ddd7a241", + "0xacc6c63450d124014a1db846bf578c44e910436c83190fae428fc3125ff2065d4c6a1165aea799b46093a86126d4c483", + "0x8dc63127435cf2f269a83024b562b3f4da18aee3399ed5255c295e6b80c357cd8f1887de94bcea29f84db3193570c417", + "0x8c4cc72a98d42b9c5807322f596ac0b48b08b16ec956ea4a70c641a16a70895644e5b14aee85a4046673849249115abf", + "0x992afaccf05d79a3147d2985970c1793459130ddfb18a9d31f3036c260790109c9ee6a19a812f5d55779c2facf87785c", + "0x91394d3e84649cbfe018d9c709604f6aeed53e91cd57e2b29d0e84aca3c413f1e0135c6bcbc70784dc8985a30b9f3fb5", + "0xa33fc126a8f9652c6905b1f522bee45848ce42d7f4c3a4cb3f6ce0e6e64c82de74e175c2ab6b5a867a8d42322d257ea8", + "0x962d5fb816010a14140767c2486cd629f7793b304a96cb82ab85a867bd9a407bc8ed44131f87238c34a1e4ba41adb1f4", + "0xb09048879ce26a34f56e1d4b2cbd6eb2a55d4ddcf3738c462329ba6726fc0a21b8c1bb55169cb8338b2debf20dc0927f", + "0xa9f9ddcb86b7427e268973bc7f3239212716df394394fa535b9fa5225b6191d234a44a126766eb470ade18c469a2d166", + "0x87cba6afb115c0b3a515b08cc842e7cc2c705880c82184174837c0a06e1687ef668609c2ca080840fff80282caec7390", + "0xada109548c449990dd8f1bd42c9cbf194a62625d165416ca469c334222446fad7a743b1f584ec3f20526c3c44d870696", + "0xa69a0c58fdfac715311dbd37c4464f0475120362234f5366ffc65227e8178e037ae91efa5a52cda5fe28243f47390175", + "0x98e726cf884c6f706fa25fe25be154afaecc0c3bcfe682a85afed225bb44ea07cd1767b4d9f2201358ef19770330f0bb", + "0x988ad5bc57f0621e3ce1256720f1161e785371fd478c467c39e554e2de27df5ab8176508658aa90ed7179bc212ed0dac", + "0xad0ff6dbfb397da51fa4d9d959ba5819adbf1a1ee31f68fbd62ae07a9cbce0641588cb1008dcd0056c17d74e83c7114b", + "0x94c703cd32b9f52c41b07aee3e3c19b8c2b4182da80487ed7991d568ea8827f0cdbd1e977d482dbc102c4de2058903c9", + "0x906fc2a06cda5d82445e50bf475dc4ff2c17e64c982539c26545798f9e4dce0bd4daa8d55b027cc0a8e1b26c3e45cb08", + "0xb09a51f22a9a24cde25f805cb18754e27d3d168e64db4ff13a202839a81c69dee8372b5872faa0d23fea283609cf4391", + "0x93c908f514e97502d170310bc495d02948d99eca51e71f9a4779ebabae1431e1f7ba465af485546a9fc97c025877df50", + "0x8337859db710ed7e276a47e90cb3912c829151cc2bd3dbbd4dd04acc90c9cb4870c44f4233b407db8240a93aaaf7302a", + "0xb13b16ea0943e235f8cb064d6dfaba9bd8dac67e9b6275a4c3f54a1770b9c905d8855517415ef6489843848108dc85ff", + "0xb28489f0de1a76839a898b8d2f302c95610f17e893a6e7f2e824fec773cde6f2b219038a3f1fa212bed98c97daa66d5d", + "0xaf13afb48d56caffa32f941ac5542ec2b7fc0d6dbc3b16e51bd2a8b633f0a041ba1e098d9a68c470da00e630f08f24bc", + "0x81465afadc45ec24825cba7c9edbb36858bd2ca8f8b0b9d7240152b58a411b08503b530932e7b6ec3b0f8612869cb244", + "0xb2e6b7438fb6f70b52b8726aa870f18191920bcb213a375817d100297b507908d79567d0c1780b3f25be807a8ddcb853", + "0xaa7ed2b0b2bb2070b5f001578efb3d1085950c19f9d06584a2d31e1c235e6d6d1d7f081ca6fa2b0b0616b01b9a467005", + "0x91a245f1aa8c8ffe03f7db1412e5466f0345196727eb8e6f98b80c01672e4260e90724a538d26b094e678a3d85f2dda6", + "0xb9ecde963c8176d6a726b129f229d803d1a6259533e432eecd7404703c972ec7296ba35015acb1f4b5ab2653a3991902", + "0x8cf535bff6e98f75d98c5d2a691a5d1aa645c7ea18d677d07d3a11a9cfa222a7b8edd048529d163120a5aca411161314", + "0xad2e51afe96dd0e942a7da5a37587ca1359fc17cf66ab70cf37ab70ea34f90054fa52503d6527e89e464f8058f5cde79", + "0x97337d62f83ecbaa1f402c3964dabfaeb279b916ca08430a61fad6c31d60087c7e3a9decd541651a2b6e68fb2816bf45", + "0x898b7581288bc7f97987138b7481d29e2cfd5255ebef133177d9060790a0973ba07de62cdf38568c336c237cb084b7c5", + "0xab53c0759663bd976de62f9f98fc82fa4f58c146b8a6a3053d4dad836c762063ad69a54d51b5499e9def86f8d4bd7ce5", + "0xb35ba58109d44c14be159333b999c1e471fb61f5ed48f9d2a6bc689eb045864f3fe88a6ecae12315183703e2b1fc1ae3", + "0x858a20e233f2860c24c5a3f4a820cac7544eb3ce91a2d8284f12013b13120121fea3c4f25427c3524a1e883aead429e6", + "0x965be1a56adffa51f5d80761327cf69656e6c37577225b36a34afc2f8a959d8799ad0ecc3eff4470d49eb22ebf8f198b", + "0x8e575ee39077bd865d70fca2d93473f51dbc99ef4f715f4a3b1d9eb21deb2afcd0873b4dc53035b77e05f52029f069e0", + "0xa5c670a73da241f5888c5cb44c27eff2b8ad3f491e0b128e5f1d498aa6d76640c9e625f3c5399ad8e99b666e4b2a9759", + "0x920e1524255b03cbe500edb230696c55b7774963535c063121c9e9987ab74d504f2f1cfa14ba7ca82a6f66745fb0b392", + "0x8a0bb7cb267b8e1e0cddee208734632b28313b3ad89f9c2168f341be5390bea3f09eaa51f8923b87697799a53201dc26", + "0x859ab9b3cd602e78dbee8d8d5c3a9eb4270f130ea4a1b417ca5612be753d20106cb2724097840ca8919a9a96e73f96b9", + "0xa76126d9a997fb0e7e2b27ac682dda1c6b99067313371387084be1f6e7a9a59bfac395e91f089e14cecafd151674a446", + "0x8aeb466c58e2829790975fa08dd31f18a51a63777070d2e24605adb1a77b5e0e5c5e0bcb483076d23a6fddee5f402f8d", + "0xa8959f312f2ce0d7d974a4998bb709bb98ff6456413ef4ae9bcaa8d687b8b5ecad91414bce8f704aa44a83d8a0c86462", + "0xb9545c8567827fb28d070624579702ab96af4f06fce515ad880893b97ad9a374c4c91d6288e2a584ef14b1ce4930a6bc", + "0xace41f9c2756ced611da16e21704a367b122ee1c2feb83145103203ace1a5cce0ebd3bf295caaeff05281672c92574cf", + "0x93b90e75f56601191e3b568368bf1d24f97512cd42cac1da8b42f0019e07fa04cd5f835b7e9503fe4702632df27ddc19", + "0x973c8feba289eb473448c32b141ab4a6f861222626b3f2fa265a431a54675dfe5eb479100a33c172ff935464d6e85f90", + "0xa6b0798ce89512644490d65ce3d0441ad950f9a25f6fe2c9a766a58b9c8222fa6cba753f314cc7ad6b6e3da881c67abf", + "0xa79c560dfa179075e0d1506adf5082318915c15048328b15ddca5748ebc6ed8b10fc5d7a50bfaf8942cf9ddc6912be0b", + "0x8841b34df170519d07edffc4d33a0e70c518dcf53ea8d0a9f13563822312a65d16f99cf596bb95eb0daf85435d4bc0a9", + "0x88527539258323edc2c296dc742cc26b9a4a984ca299a81705c702a425ebc7345a79b4df84b2d440a1e73a09fa18b5d4", + "0x88026753926a068e1cbf49a9a0fa545846cc7ca9abc0778e44f5b7510c1b6b73e9a9b1aff754199379522b2a985c0881", + "0xaa2c306ccf91f967b5cdcb50808771ede36acb9a6cd84fa15c6de4c873cc2d0a64e79778a2822a109b41f5205fccc30f", + "0x9751fd8bc2a07ffe0566e5587e652d3d8d5250517278bcf8754e14717b8941c07c194f14fa757f9a2f3461ca6376bdee", + "0x919746e5eaa18b734ef59c0de09ee8ec65e348efa659219d9eb6a3a77b8edd9449a6dab727e9860ca0d3837b161c0352", + "0xa271c146794d6a65c6fb87797262c22b1d938ecb2624e03809233469296d33ac0de6909183c8efa438b5f3971c8c3eed", + "0x82fbadd9049248914a15755dff692bf689eb2650fdc6e44e8f3ae003b8f15a0f2858c7a2a8dd137b3448420c5f434229", + "0xb90992cad6df98d2fd1c75bf90978205280d993d439c44d6721cb95d52eb042e01b418e45c3c48ed667aad577f3fd1c1", + "0xa0c3d1e8b80ed4a979a22d6a9647bd57f22ac8d73c37ec3d56d06dc178a5c9d5ad3ffd6dba9eb7844c1f40b8c89d3d33", + "0xb513aaf2f0a07fff3543d8687426d07773677ca4d23554ca517e50bcb891164d1f5651944a2f6e0a634475f6d33bf0dc", + "0xa0b179aa2ecf572ac4a3ed898aa34679be3cf3d8d9bc74e33609345cf1743e915124a59ffcff41bec012ed2a99447e6a", + "0x8e991c5549126d64e0b515a03d266e19097eee38d08121d942973d568f8ae23a15b037337cead0686f7c944b9fda3e39", + "0x93cab29e1bb116a39ce1a82897776da1bcac06ea131a7dd141a688ecd4089c5a0b8616d6721b1c6d92309ae0820a367a", + "0x8d4e0159fd3534172b2481764cae7746b1a47e9b7b9465fcec0824ef234674fc567c16ca7116dc90ba9b2ac3eef28d60", + "0x88cbd9ff6ca551a7efca30b5f59fedaca8f9efaacd4e9bdd17ef0dcfe4f3449b52c7d3066716f3e7fd478f264d10714e", + "0x873c71b2feef5230c31f13d8425f8b0eb0171eacb9225f484a36da3cc796d62594fa4771af9ce1e7ba951f5377e5db76", + "0x939eb99d7fefc9fd5b3dabaaa5b331365121374a4ced862b8cbe0cb3c574fb1f0cf4932106088a1d87522acc31ba7f77", + "0xb283f1d54bcc72e31ef572498821ded40485f38d2ffc67f70bac68a100612b020a538b36001d94228a4dc97da8fdaf17", + "0xb2e4c2be605c8ab3038b4e91bca7e77e127c5c3106913ec1341102e138bc8aa1d218c3d3c2ec1d36fb8e044b4bc211a5", + "0x82e73cb5b2cfd78c17131e871e92026643bb56916ae64f009a009555903df878fa3a2019b82f7e71a3ef7eb503c792d1", + "0xa6d828a5b7de0e7818975b65244f6efeefc712c29f2f17b27f3264e19856d869c350ab39750ba63d6d46afa3aeb369fd", + "0x865b17027e9d5bdf2de0afa2f524f67b6defed87b14e0af5f4b6b1966c2de45550fd2b6b39b1be88ee9cb06e755f917d", + "0xac8b47f9b7e675b556445d103271e6bd3b39b94d15ee1f3108fd1b777d439c75437c78ec3b281f7104af6d0efbf0ecbd", + "0x85c2f71ae18105fe499aa4da0a835de3e92ce05d0f28ccbcffdd2860898ae9909e1c05831ca4fed96545133bb3170302", + "0x8bdb4a72b06562591ee44799bd7400ebe71f6737290420dd4ba2bffe0050d8ea4d586b7e329541a52611e945ff1b16b8", + "0xaee4843c9ab02026ae723531112170bc7464f51460bd4ba5166fed54ecda0f53342cdf94f4354a5bc1b941e8ab085a80", + "0x84de368006db07c89a7a43b7de54a63637ed72379a41d029430f6b4ebe404866896d2e84996998f7b2c20324143649f8", + "0xa8375f69c01289cebbc97843f417d0146f68c6416981032bc1f42d3e09845d5131eb9b4d68fd0ba7f5b1223b83e51bab", + "0xb1ae126dda1a88fee9265ed8e5bccb810014044d83c70e01e7f80059a685067f4204cd15809b230caf5dd77738a64e38", + "0x8177544c7b1f77181735c660102da20fbf9a2ca4efa79b21c92f1cd2b912630aa6c830b7513980656bd275097be59d1b", + "0x874fe8038905065ff3b77f1e53904854fa4fcbdc4c8959fd2df2e3967b3b84100c6f63fc44338c01fb26c042c454991a", + "0xb19324d737364cabef3d2ee4785e8f19cae399afc06fedff8fdc120e0ce525b3755161183a1f5ad11ee758104081a49b", + "0x8e7525bffe35c1f5c2db63ee911e7e0197951ebd25868660e6672a3e7d4fb309738268b36677921be3be2f323ca718cd", + "0x846c51c7d22e7d43f6e2addb7fb25113c25ddaa51252a898fc1d7d5b510f772534c4f5d37ed3074d124cb259e2bf8110", + "0xaafe2a16cbc901730178841c39803ed84d3a78f92f42f84a1c01be4aa3b63ed7ad9d054ceaa8a2d56eadddecb287e8b2", + "0x8781c9810ffe3d93fbee3b576a15b8786c3efd6b5a96b75819b1f93daf413d4fd0f56d1ec277e8f5adcb784b17f08371", + "0xad66011f0e2927ee1924725bcf8a296069f74df78ec66ef6aa8792f68425e48e9d7f717d022f68a079501770ce192fce", + "0xacd0ef46fafb06f336565d86e0b22f9e5500d2f73d047c827d6a207af40b706440afdaceb32e6571deaa1a79f5e6fe27", + "0x8f65bb98baaae22e84a3ff375e7598b5c61ebec676fbb5a4f79c8463c427eaa96ebc51b1fb504840b7b0206ca6c2c72c", + "0xa4078341325d7debf766e43679b8b68331dc13651455a73912afe062525d2ea909d8829ac08771d9b32f2eea28b64022", + "0x88eb29841b022f2ec9029ecd1a137173cfb79addde1c7cd4be425e5806ea6ee295b11a0459a940ba79f789689a8fdb81", + "0xb762b9923a40a1965847bc7d046723c3b8f0d63323303aa3b25e93b4af8e527f1afb3dafda831f50baaf79926d1b1e78", + "0xa21551dffcdb069cb8f30b625c8404dfe5efec83227e3a1a67ef0c9c9b49c658bbb31240c3ff6f8c68e02f67974c732c", + "0xb4735a6610c371754001425772aa5314b759c24da50b38a9390969c27e215ad9d463a76762323b7954756a8d5ee7936f", + "0x81bd78e545938f8a3e53ecc2e88dc26bfbc30941cbfd009572d9b38f8eee47a85209a318cafe8cbe055eccd5e62d50e4", + "0x82ea5495db9dd48da97723bcfce02788549c6006773eb9f4aa4f0f3ae13414430edfecb5cd095259179ec2014b6ee1d9", + "0x8493147b8f0818c2d5e75acda498139f95fa6f904b47f87a8c07e258c60f39bb1faa1d29cf0834c8a1ef1d6015d37b42", + "0xa491233ab353f9daad86e60fd48b6f70dce60dbe36775958d8e270725cbbda96578b17a0c4925ba1298e630c6b9ca9a3", + "0xa8c148b9e1373afa54778b6d4f76cb12f40eb6e07404a7f27b271fbce0d18d621675f1dfcb30e3908d7df1d962de2e5f", + "0x9826a45c29ee22cc26ae399be25cabd16180a291669fc822a44d14cfac81aa7ce85112d7f2c30effc7e83d131c9937cf", + "0xa793c75e716aed4048a2893f11eeba78ec565ac250bdae16594d056f06f4aa7d2a156e1617fc26def4e39984fb28936d", + "0xb6c454d822343cd1b1ef7161cd2ddc28145134d4e59e6d69634c010ad1bd44120aa8458eafc28f6103ece7e34b368e1f", + "0xa3340a0edc3fa82fe4f31ca2d19d295aa09c74cda3bfc3534c66eb71bbb7903843bafa92f7120de4505c7ec819a89664", + "0xa18e5218cd4349985f412ffc7741b5db21bb14c6e00431daba194771300e740f75fd46aef1876543967e8719bc6517de", + "0x885ce63a88617bee05144bc67d08f1c7072d8c4e09b23b7359f020995aa8cc9654378d382de6340ddf0803717687eddf", + "0x8d8a0b614be7df01a12e534bac102b1881905a9d084146b3d0cf2086dc7d400129e0de8e48fc966adf9d8ec8c1336429", + "0x8baad19f604bad656398a4010b20ffb6ec6131681d120220dbf2cc5779de1ee146d0b773bdbdf4e8e30aa0f464f2b18b", + "0xa39ae3d204491871c2e88d7772055b35af341ba66531ce3575f47c749eb8e380d63a7939d3408cd51356cca29c76d4b3", + "0x813afd593876667ebf0fff2b8a8a5bfd0f42a4fe2e4a0b7c78b6183605706c97dfc40b627340e1d9527f618719d60e88", + "0xa013e458d678fb302bcb6f002a52e3e0ace443009eecc9113ab5b78f4663acadb8ca9cd757a7cab1e850aa23f09ed698", + "0xb6e14f351fc47b9e46a83984756812cfac795cac5ebbc6f00d673ee23209d0d91a6bd7d576c7d35ec3c7e7cafb758a46", + "0xb94246a346966caf6fc1e0081a211f27b38f058dbb9dff915e3e65391dd36d66c51324667e3d7469a865c0cc064589ab", + "0xa1bf4bcc7420bd17acba90ee67af96e73502777e1723255a73b1ae3e92fc77e80a287ce7c3d4088040e0edd64577c8c7", + "0x8b6f5eb9b6bc7320349b19876864baa6cd8e07da4f70653d7369740184ad416c40b4395c04750f5d8b54b3b3ba68295f", + "0x83250b957d920b1b738f4d0f44f9eefc01b5b0582128f5ddb5a282a11ee207ba1ea7867f00588f8b891bbde2e56b4c43", + "0x8eab312cac9de78c9fece9d67a6b26d58c4e15d5e0668ca2cca2d9c51636eea5210a893f9321c2fb232e09f9d0b40fa6", + "0xb4d1e5f284d50360dffd2a0d21c4b31d81f521158aa21bf333a525cc68e690ce8ce4f0eff8e71a0c2d5853e2fed18231", + "0xb1f194c28bbe217a2c98ca8212fdca744f0806d60e51f9da81548155cfb97a39e2a98e753be8b2196c83f7db8caad2e9", + "0xa7905cbb59722d9463c6317ae59adc10d5bcd6e9788f2a54f4ff4a4de03df3f830d6b8faebcda541d429a7e42d158c9b", + "0x8a3b31d0d0b33e7011dafe25ba5c3b7629cdb5dd5b31385d786fd80888fb8409812b96d28fedf6a401a93310b045c620", + "0x86e4990bf50b27bac76926dbc65a1ca34a759d005e56eca56fd0e00ce82635dffed6f3052740cac2f1f37204699bba9d", + "0x8f0b6a0b66f1f5fa3d12af444963c6a45785a36dbd9e1a5f42830b5534ca8773a05fb618292e718cfe8a066b8fea7980", + "0xb7f206827d715b33989de5c02f0886d3a457d0ae34931ddfdfe2dbab121b69ccb560a71fdafcc3ff204217611674f5d3", + "0xa6e2ffb0c5f29043984c54f5fe6449ac4b7a86f2982c909606b88316ef1f0a54638d565f44d2fe8cf00279e0eee435a9", + "0x8cdde76212e73f9430cac210b52507583e81aae0bea491f6cbe27e5e2c8fdda373ce8c7a5f16bcf8c6238b282d06639d", + "0x8030955eecc9b8e2161d40f1f514d58024d25e07c6710916824ed8982b8bcf7ebebc5643f0260e1ef6150b6901dc2dbc", + "0x8938fc90e836d7bdf1cfefb2716cc25aff34e7c4dcf66b349d1fc6681580de7f58665faac29494b30bfa0c743d6f33e3", + "0xb182f9b4a5d838e9d534e41ecbf06330c56a8a64800eee561de1fc2dd9486565ae6099f40d0f1901066f45047641bd65", + "0x81f98b85de7b6c581613f4a42e0cb0dd9e6336399b39d38a4751c2a9f195b65c9e5592fa1728b91d83cac0ebfec7d675", + "0x94681572da95137ce41d913360cd567b570a87c9a439f2b67b90d623561b75bd3dd0486a90a63d49eaeb412cb97768da", + "0x8e64922606ce05375556901b8c042d4f41a18fafeca24c1d56998e0bc9054bcee7ab079c3729a67d908d0d7967a85edb", + "0x8e10e8952b24125321d0cd9ba922affc93908b3abdce47eed22fb2d44cd556842c31c36de6d4c28b4a1b5dd84e07df81", + "0xb6d464020a51bbb53670c81d5f1474ef439e006851d5d5a3fcf74219614a2a9c983737f20b254d38a2fc7333b35fb3a6", + "0x91801712ba264cc2eb65e8a3d5a032674a89f4c2dff95ef9d80d3a9285f3c2cc108e775dc326881484756814c2a03637", + "0x986e5a00f13326735bfc6b41b086623101f01dd55f2a88bf995a3b81054da86bb2f97fcf647d58e90428e8e9555eb020", + "0xb2875b4ebbab678fcafd270a0238a208b19803012fdb3c23f06c74bfd45929a9856b7a0f9881b75c7e97fa9d35e49d1a", + "0xb3d1acb9c844d8d2232834a81862c59548cfa849e8e5408ee66b4c8b86ddac0fc231b2538a752eb6c1ceee92ca443d1f", + "0xad0b1b5d6bb50c43f5f3b692c5d3569d2117b01caa7f0ffff502d5ab727f7702a2d458b89d77d218d3f92351b4c2b92c", + "0x95b1b99dc260ae6ac7c387bedd43fba793274b15768d93df13c88ff6cf637732cb6b1719467919b444c3b5166f4f0107", + "0xa0c3c8b59016056742145e7f4ca6568d4393124efac6540645152bf71173dea3d0058bb11b3093228ca4729cdd5b3033", + "0x9278afba60643257d9f21a4033df3b57743c3b43d66d96ccf413154a63db054fbc3a36f2ef378169f4f19987964c0bce", + "0xb558754c97f9824a72644de1725127dd36e284fc07ce89006b990f09db77c48ad6728e5c1891a691460bd5416ad65faa", + "0x833a02af76172f868a25e850d35f4d164889bab8381fa9c8d9880ab0564a3769ce3961cde72bc94ed73a1723daa35cef", + "0xaca496f3e528a2e3eceee138291107ddddd68bb088b2e49ea76d0c4136c6924b3251d7661ff467a36dff29f07ed09102", + "0xa9367961ae88a19038c446df3eadb280da005d120c16f48ffeabbe4cb8e5e2784902cfa1192876ab934bc90606baf2cf", + "0xb43feb49373dc36cb46e050e3cea43e636a64289efa3af790dd3fe960446492b858f51b3be62c6b75b510d8e2b985573", + "0x8cf24955965468125fba2c5a5799672845ea6ce97cd307b09236ef1a3cfe55c88958ffa311e8bc8335bf261a84275d93", + "0x88ceac98b512e5bb915554af92318a5d07a494e0b8734c4415e192e7405d6b06d170fbbe254e3bf387759f6d4961c34c", + "0x8a9044ddde945daf3e0cb3f897ca00d0d4e6a5f7c99aaa3929f0beb9a44d2ed23c191e37c57140ebf3ec759f50f84d57", + "0x8b2a2c0fb51e7c5fa51e8c593bcf118696b8411bc93e35cfe5de6c5465c6e80bba64398d7c6b71badda616b918bcc7d0", + "0xad8bba2b7d5577f971a1a561b17a9d8f6b7c35fba55e3e421a0d8d77b520eccd52122f02afaf3899218b652980986a92", + "0xa8d743b56896d44bec838e10ac1ba5a43f58c26655c71be0a5417d936260453a8e91752c87334676c5dd1dcdeef4fbd7", + "0xb0b0540f8d2d1ebdcd74d6e4007324de8f8bdea0531880520d79773c0b8eda65ed49e672c5a294fca6b4560442085829", + "0x96da830d1c1625d002008e9a364005b2ef16cf56f5aa4a758ee963388493cbf90aa75c25dd67d496af17212537ad44ab", + "0x89e819577a95195056b872f8f790d001fde3253a23120e2c41b3ced7fe8e9bae0df74907b7d65ddf9bbd6d2efa18eba3", + "0x90a139ffc7dc0992c023651517db4c195aa2f618dc3002f4a0a43013b6c6022d8d9844a49cfbaca543c9cf5d9b2210f3", + "0xa2061f543b216fc9c801d885ed681f9253f67cac40528b23aa8a709f24e0992fa42a10f1bddc7f10af2c22209343ca26", + "0xb5f53715b9146966f386f214477743e2fd2b771bcf90b192a5863c06d7225be34edb6bf71389085edf344e60afd88561", + "0x9634ce27272f3c687035789fa4eaea2aaa71db5b5531b21b8e029645827b40561a5901b33afd80a3aeb5777aa89850f8", + "0x9674736cdb4a823bf982d54876794e99c7672eaea7455be90e815abd03ac06ce1fd9e73bb987a515863c6cb4ae597835", + "0x90379303e285b19fd7816a6d02c0b8f94e6291b56c196d76aa389cbf813dee7ebf64e45555ebe8a281daeecfd7aa5b00", + "0x8a1f759f6cd6e5134f67b96e0edce7170e4be1b39afaa7af1c2de989116a6ec9d38a2c077c8e6e65ce0bdf729f20f1c7", + "0xb416f9937a51a298548e91cbe8fff71585335c00e69602423adc9cd72d18821987b8fb5ede32fd8bd2166e2ba9aaa792", + "0xa423073148046c81f840a481d57909f7ef621a51827e44706da9e1f0e27fccb8f88652097a9880ca64c41f6386aa9069", + "0xa173305a5aa2a17349eca704fee25593f5c2fdc5cd8cb932a1bbc0ef34bf54ec2f867ca93d8e6aa33679cbb71fe11083", + "0x87c6756d14d815ac8237ed4a75fb11206f615585ed527ad582841526371366ab19f602c7448a21722adbf2d987d89b81", + "0x8a1a6f06d6375d2bfbdc7531e9177a45330458da2581f65ad129367c400cd77f548aa748bb470bc560c0b02ee5b802ab", + "0xa24a05c30d0fcc8334f6974c30d13a5593bd3b388e2146ba006f232bcd6886edffaf7e48ed6126efd3e651997dcceb12", + "0xb35c5f8a5842d97cbe19105305cae1f971da5662c52eb979975efa0753bb60a050206fc0babac5b5083799e9ce8a68e0", + "0x939ca5532c922d00d08ec5914e6c58f8a1302a1214a1cbd5c844b334ddc84e694768edaf1a2af02289ad74970800198a", + "0x911d6104a240f84e0f6502597405b47a7faf5e68717f6d389baca62bf82fbb7207ce8d0c584fd9d57d3afe1f957b7cc6", + "0x88777ba7a4bdaecee78d42687cb4fd6dcf04402b43524e2ae67506d93acfdc29d9dae640c05d01c90caee1d04cf3d35a", + "0x9226e684606f8169e38dc21a14911d0306b1c9ce5244500e4b108eb1a0c0783486acaafd2e0b3b60c413bb003448ff21", + "0xb2f527adbb9feef9553bf508f192b5ca211d0e491925a2331bb294fcde7d8e0fd72b441e9f07c838640dd35fba03e1a7", + "0xb474e6d6ce22ea272a93a3c078197f40c01b9121c6f3083a8e587c367200b8c97ad94e156883475603f0a66d0340fa52", + "0x95c4d9896df11d2b5a8205a19d6331ea02a2de038aded8e6fea6d79bf5a6648d5d986bd29430e4cb5a6afde8b07a9a48", + "0xa12bc53ba6b6f8350b400fde04518a741a1d755123a6ad1d435c7642492c7df28f7091f17b254e793561923de781eae8", + "0x8a0578ac03070bc920a3b5a7a33d976b3133501309af5339b0cc70536965465b4f7288af70db3d5be16bc2a1e5c26a86", + "0xa66e27284ce6114e48ab56d7f623dc37a6e79cc5f487cb2bdf0acee099cae744cf3a9de53b111492b5ef99b0deaae0a7", + "0x832a338951022c80444ad8c6d0285e86db54254d2689defecac2ed87f5eb4d876373af6d76e3d613523e32c3966142f2", + "0x81e83f01bac3ac3fb67e780b28de30b32247a774aaaae118db3d45c8e74d1d4f1defbf9c2a7ffdf176f5c1cf4ae4167e", + "0xa1b214ba7265f692b4637352c6139bae8bcaf3e7db5806fad0014ded93048fa4a36ac9c9e0b7cca0a86cd45bbbba2fe1", + "0xa7ab6f470a421e72fb703a9d153362056ce80c40264a3ee5698168130cab8e827df5ce3e6321ce9a669c87a2e5c67499", + "0xaefafd219f2d062a378474c48d2650b51901b6bce00e4ba0b509395a6fb39699037577da353cbde187e65de87ad01575", + "0x93db16a0a77d1b181f33ef10300112fd8db5b2eea26732bffa3b1fbebb792c6ecdf2899cf6f26b505dfb46deb81b217d", + "0xa63b6d9d1cc2f31ac5f836133ae66bc9de3e07ced5026f5bc90116599461dbdc03cd7680c1bb43dade9218ebfe1bc1fc", + "0x984b49ca86d38a486f6315f4f9e6ad521901a06f8862ce1fc095d9c66bb2164e334718c71d7472ed765367db5fede105", + "0xab49ae93955a38f45f756afc4248a37773ba8d0a19779253fca3b744854715b9c9b10c09a37d3426614ffd3a8ced7bcb", + "0xb22166dd64c83fe16feecc09d4b1df2d967ce7f4ab526ae39799dd5a5a4a9ebb1d4a432c5efb90e0875a4eb6b079e2fd", + "0xaabad619d887b69b9562066fba2179c69c684b8cc9318c9e39646f4a5381535c024ab277a0f0be46abc95283b337212a", + "0x99f5d484db149e9f8dc9c6758647c4e3702d88986600a3226874d612bb4b5e92a76b1dfbdb0909b8f21afc773ec67c7b", + "0xadc8bb04eb8c56dc4ce97c3fc1670da10db134cff2edc214ee3221079251b968e2dbc087c56c01c9260b49506958a6ac", + "0xad625ddf5cd211102543e0943a7770a9b45cf3550d12dbb484cb5522b70cb626f9ac795b07a305be3e6949d7ad475f66", + "0x8f9f5b2b43624e89e8535dc73fc54b744f247572b2920679bdf6a3ef346e654ec40fe8f81a0f7c7ce7cd5b48f3975359", + "0xb70b1642f28bad56bb24b342eeddf5c3782e0cf6e0d5007c252413bb44b32586da1e3b4d8b45a72c91e44e07334da68b", + "0x81b0311e557c47ec22c5f5d1f757c6193cfffae357dd2460019247178b13733484dc8630fe2e13037a1a3d681c69066d", + "0x951c9f1504b19acdac1c04aaf535d3cd3e39c431b2b5d9def9b374468e93d378ecc3f5aa02c91ddb93eea431b327ca4b", + "0xa85e1f4c292723d18a49cc9323dc7af12bb5a8d0c95d71881ae235ce123c50018907f46bfc846dda1a01b14ec45dce14", + "0x8a46c8b86bf9890df60de4c210cd7865892d0c11fdf2747620289d73bad597e6b482c208dc310c25955dae8392d8f278", + "0xab65408622c63b67842a80c4ed665258ab617ccd07871fa3f0fde2e5ddfeec49f01d7501790a60b3a05d7579b087b787", + "0x8706913d42b557d9ea4d7b70697069281504b3c4e1172a2291e3b3e0a0305c8d0bff6b7721356d971d2fe58e32d4556f", + "0x8d9b8f3c113ca1215dcd15d4c37913d023c8c5d04f617319af76bb7bab72fb756c5bd992db6b9e765cd7695c316360f3", + "0x942d4d3351b2a9bfaab2500b27d24fc2d7237e791993a7d0335f36fe6456c5a1a8bd28dde9228fb139e95288d6de5bbb", + "0xab014e9cc7d3ca08f1d3d24473ddbd693331f4bf21ebdee0fc997aa2faadb43db6a1195644c459b52a969f3d98a85b8b", + "0x8b679da80561955990c91da9093837953f4ff7fdc658b51639c462b578a2b31443421712c6b7742fddbe0ced48c21cb9", + "0xa9132ac18b1bce93e815f6d2f8a0d2f433ae4d6fa04269eb0f5f25864a8009b01531c7c3ebe87f07454927a010ab6dbc", + "0x8ab02c113149efc877967c92621a8ef618bf423017e78b9cd30cbb13c51200c6ce27c46be75e19ba843d64a3050d4467", + "0xa881043298341efc28c60d850d90d90279fa6d8428953337ba57b74eefd858e362c6118a82ebb025c9c102c91b4aeafc", + "0x92e4a587479c64b8df955c6bf1abf1d7979a978e45d96f05bc1b9648f10428d77890be9ee03bc1b1982f5ae7b926f0a3", + "0x90c21a22826e2e9978dd7522f51353fb33224cb65603779de41db3ba41e01d664e131233bf873e28d6c71294b565c533", + "0x88e8ccbdc54ff06380c2243203d3f8c8a75fcfe638d6e6a010c0b3a39d5cda31f8d2cc416ee5264267aad2b457c94e50", + "0xa256198394b458f6468dc91c35f579da0ef02a55fd93e98b25e43b1bcb650ff889df4899236765c1a6b35cf49da940bb", + "0xb5c7d9c03c36cbca068abc6778053727e77d9b58c5dc33b11629f1ade1c228b1c964f5a7d8dea16057e76662c4d79f18", + "0x9588e133517f0d49622222b4de5c124b1aa4260971e43e4aa767fba8055540f2848954886b7f245583ea527fe2fd1de7", + "0xb66025d75169bfc7ea366cd32419e24fbff829709e3e9587d7d59620b3a7b72034d3303106f965f5f7a71d66b7f314f8", + "0x891357bbe44e60627b975c10c872a34b78d6b264380e351f3a86dbf99abf8e2dd8d20c52dd6073086e48e1ca782e2ac1", + "0x8a066a3482526a92476bb8c3e5caf07575c725d72203d67ce98f654f5ee8b7f979187416fe3d7ae0128800b253d7209d", + "0x80a9e3d8900046b71fcd5b7034d1e0f57d95d2756da8307a11aec0553e5715518a125a653d356f399409545256a1984c", + "0x924a13fb2da7a899cebf2ac09c8c0a183491777100de1aa056a6c2bceffd5a63e255f16a9066e4ed89ef28096a1230bd", + "0x866cfc8116d2e0216d8049d5ae2ef0e3fffd377028850716a4bc2cfe16c5a6be023334bd6ddafa0c77913dd4ff0a34ff", + "0x95eb74bebbbc59d793e3fbae8e98c258451bf9bc5097df4edd832e9f1c30a1446a59e1f75a44832d0658d5ecc13dfc86", + "0x972517b2d72ab53193db5d682db2de7790a418ce4952c29d64e1f9107d51a782f4084591b7c775648f103445b797e8e5", + "0xa14ad2cb69da568f2f958ef4253d7a6daf574c6976f4f5d771ae7673853ca22eca81e20400092bac84453b6eedf5aea2", + "0xad95bfcec6c06cdc11d316b7ad33fe65555e985bb33b15c9f481a09caba1e5990601ed6a88038c0ae2e04b1607e2da48", + "0xb7e3bf3a585af1029d83f12cf75acda894fc4441cd7b3d56efb6991ea91b07512bcd7d6d68738557a48f0446b2cb21af", + "0xa57efb1e2d2e10e41f356768385375a21d9f78bdb34d618117581bf7a15024eba43570c3956ddb85a025d39476f831d2", + "0xa66d3622b1cdd472a2a4491881de035c2eb4f1c94927902a3bb9f10739f900130907c6b002982e03785c43ac30b8109d", + "0xa79f2417d32fd772e46f3bca61ac788af8fab174e1e1e48a84ac557f7e80a9cb4e2d7b467365ad18f9777f4cb5bb2b8f", + "0xb952b976e3b6660326c0ed357ff25ee1291b74891f3eb7bcea39dec2ebb11e287d6e26ae0506425a20e5e445273cc63b", + "0x8c23929e9740ab51d9b82c6b7840067e7163e6c7b9b9441e1bf867ca2e532926981c98641e6c798ef12d35108abc1dd6", + "0xa519578772c9ed2d691a8c423d360e4bad76afa422f1a5218a7a08ed52c9a5935ce2ae4c0be182eac0712259a43f849d", + "0xb1529dd189cbf3bcca50e97199bfb85b42f2b26edd95b35758d988d1d3740f5d0d2e249763874fdfadcefad9ea1b3d02", + "0xaa3fed8d14a4f38df75b9eed7f187a31cbb7a748bd3225dacd8325a71dfb680729fcc91ad8cf0b67ce314e1fa8ba02c4", + "0xb77c28abce17732a08e682491182f63fb55640e60384932f6a9c6d3d7886508c9e67a841cb93e59448d2d59fceec4620", + "0xb7a24c58e3b85d60d654ed14d78993a9cc78c130442c8cca42921ade8ec94bbd0653c9fe5c69ad1fb2aa46ffba04da39", + "0xb7d08f3ce97901261514a5dbae582848e75515c5f9f41f5e70ec17a8d0db3067ddb19aa1c86803bdbb757230b148bb21", + "0xa5b8a6818be4d59079d88f72d7aa4957c48ff5898f3fd01def48ff6bc7aaf9840aa91f2f05617d340092dd9299115c2e", + "0x8e548db6b871fb23ca1cb8538d44b77ad02f4cae4d33c8c43228b820abee1aa913ff9acf2483725b195b4e65e2e92063", + "0x9509189e063812fa04f4e26f87b33a2289a05c229ed1038fde0dacecd87aa55ae0fdc678a1c86bf13b81f4b3a872426a", + "0xb355f24a5dfb7a8f3ea717111a038487632bf00d67cc2cfa2ab61e1cace7bc7f5bc9e04b190aa6be0652627ee219bf76", + "0xa9b335f235df51b92f40f44f19150e182a938b9abb3bdd8e8c447b2b128050d228e0115a268af4c1bc2ca49552b4e0a6", + "0xb306d3e6cd7ab56f5f7572fe51175ac6b29b189220fe2d380b959d131a35804da5ce95adcfa51d799f18e27d8d5eee0c", + "0xaa49cd2bd34c37ce1f05e192fa6837f964c068170ab97989e1cb22ea7e13c2400417a51282519e74d8fb6983ba89a549", + "0xb1d4fff41d95613e30427ae2ae1d3df8c9d06389e1e0f404f8cd40199d6c4277b7a898d06f1579be107fc5744247c36f", + "0x99d220454889f476931b0cba3570eb1a8eae30b4c3617513833a551aab0a2630125f72dafc64a766b1a322dd42dc385a", + "0x8267ae38c9c8532c7d4ec4455279a5ed4f2e48746cb0f2619937157534b0e5466c5f4b99b7c342c095f71f3b77fd5882", + "0x8bba0794cc4ca00eac50309a92878084a6a22e4c23206c68b8d7268f9e7f615da4b9d0f3e006d9dd84bc3dcf32261e27", + "0xadc965bd7c7bb2a52cd3f4d2cd3fbd72a196876a678863c6a67a25b4a2330d1d3be603220de22c8c3f60c1411df57b7d", + "0xa7d5f38a3c4ca0541d5ab101af9c27b04c5bfaa42a1715e882c5e7715e460c4666aac4b6272b9fc54514346fc49d0560", + "0xaf94b91ad9b0f01df1d41a459f16ffbe30710325617651cf1da000eec43876161957f079a27b70018ba34d1d5d68cf6f", + "0xa0e2a492da4614f41741157d3a1d19a2370ecc8e178d813e22b902cf7454b8237f1ce3c16270eb6f3ead1f92797e36f2", + "0x8dfcd39155d7b8073b0a1a9a617fa75218f569520d4817f3ead375850ea8a3e3dca64c44e83f54afc37173d927070601", + "0x98302358e5b740b73e1a6c568b99affc6de3c7245ae96d9c712d377fd363d8b8f49dbb714aa8d39b5b947b6de341ece7", + "0xa2fe0f9fad663cbbf4bb05f61edfc90716564d5ee5a9529ac3cb8f06f96329248cda85c43f24a2382a9056e9a53520ac", + "0xac50b0727ca2ba80692c0b7f564417916695ea3760ce9fd71593050912bb97366d29ae5ed05ce52984e52218854b8e3e", + "0x86f56bea946a4516336a90328fb4b24cc7f82d8710d0d1e34c2e27b6af73c4f4a9d6a848dcc56a87d6259a99ac444557", + "0xb33d0244948c430a58b210943e41aa3cfecc9a823dd3e160634ccc45ea2680987db2912ab2a173ab6cb9cc2b7e16f7d5", + "0x8808f8c2c2377cf52e7314820d88234d7819a6108fe9e1c6a675dc47cd59f81f95594ba2f5fa10e3719580f53edda641", + "0xad34a814be6019931972a76b3300a4fc9ce763d6f4fa1ea131a67d575c00c827b9ae7260d88577b4b3689e90a845137e", + "0x9370abc67ad0fedf30b929d1613e336c6e99e4bf83ce969e61e5d77061b48a1a493f28fe2eff436d4a979af700a83b5d", + "0xb0db136c8f4ba2fb7148b1451b18f694769f5e53650d68342f15817b04734ef8ae59681a5754df617d755a687b6ee45e", + "0x9149909d24382054a05fc0b057613d059721f132a19017a92198b30e48fbbc5f8f0b5f5db55347dbd9d190ca88f9a28e", + "0x883d1d170fb0fa95b55b10b32ebed24b1232dbfb5c783148a63a765fda200e796aaec52747441704967914433a01a323", + "0x8f55fd5ea11c4fac277112d72489ac1de28fe163a756b125f27acb78aa6651c70d1cd8c45e0daae417bf894149ed2d57", + "0x8d08685f99aa8525b008b868f5486e24a08568a5afba9b729f7d26370fb1b162937db28b935d67e4d22f7fda69a3a6a4", + "0xb1882e23d784ab48b2f9e58114c5920bc9d0c4c01d2d7fa5111561df0cf2d738e31a32963cfa58939af87e79428659da", + "0xa3eba902d376063e48634c9436802cdc6b89d3a7c7cd03b26a3fccc7218dca85a3ed939eb53956d2e001805aa5c2d63c", + "0xb97330c40d51a4b71f91f56292b628379ba735509a66c7df054112578b9df40d3aa32598bc64c03c78a3311a17997bd1", + "0xb84f3d2af2aae2aefdfec9a0693f6bd71eaf4d477cd72d80f4919235a471607c5483b354c9d46628a76d6b6fe7c586af", + "0x8a1c39bea7fa580de967d8ced7e3860a9031b07842d71f8c5941b8877cd55ba15ef7aec6116ba38ba290b887b4530685", + "0xb120fccf939e7d7959c2c1e70d7a7aa3b84684dd1ca8e5cfa9d281fd06d23eb67a629b1a27052614c3ba639ff9c90dde", + "0x827a8e0dc841af0e2c4a9ca36c84a0ea60099aecfa40294344f82878b6909f5581f7b34fa9510883113795bd09b5e4bb", + "0x88c24cc54dac5a2982be5ac49684d99f95574bb8cc44afae4f6e18231ebea0f2ab65b49870840bd3e8f2c9247f62c7c0", + "0xb91fc3f2cf743f4ed42e49007514d43dea1d7bab388a18de6f71367fb8f2e9b8e88ed9f7492b647e548396ef3e3d7765", + "0xa175000c4765a57c57b219b21f8302cfd85aedbc3340fa1690119bbe7cd93dac4fd0ba676b1784ebac83efe3e78d4bf6", + "0x881a373630ebc24dfe17e27b3f176de6651347ae741d55675675e9e6904ebf157e787d86eec42ecebfe4eb8f28de6fc7", + "0xa47c8b155c8ce8e16f38deb345a051fe0c9b217cb7a266fce78d7694134247887789645a82c0ac24341f51da8ee6ef00", + "0xadfa5bcc682d4449adcc436649b444dc61157154e24d68615b0ceab50eced1ab55e15b45562dd8e00785806e9ef2b7e7", + "0xb7d2ecddf47e9fd25dcb283eb80e323300bf5c3ee3344abbc3a1f2a3296c631577a1fadfbf685abb336d5d7059d17166", + "0x8833f6b388e46e1f8fef1086777466277cd418051ac0323e2cdac5902d7ae45eefef93ce90b088bbd618e0381c1ada78", + "0xb6abf44c5aee5d0fbfdbcbf1e77354d5a2ccc239b894e1e06d7ffa76584683f707384319ab0e0d17afd93a854d7d26b2", + "0xa8c61859a9553a83bac398c14c987b20c8dc27d63112115b8aad26bca275cf98913783c802ebe3b7c3d878c130407b34", + "0xa5de7a519f8de4daad9137f2c2838544219834cd70457ef09467d869f4dc32098b7a8d4fa85e1eb283632f6d09971318", + "0x98c33a315a66cd8ab9ca8a58c87e5ec588107a6416c4ea498d0b91bf7597f53a405e437ca0a9d9c6acea27ad0ddbf4cf", + "0xb2909b1f8752f4eec25180a17163ab215fc20c4a931d4471d3be0ab64207a65c7e462fc0707791286a92ff2f2b7dcb0f", + "0x8b96c2fec34cda02e98510a3ed80a980b0cbf4ec03e3c4260f84027cc7453acfedb5f708c401d26db137032c6cb4a31b", + "0xaff645dd6ffe8b5076c83a823daca4149f0769bea3293b61330ebd97a17fe16758e4fbbcb5bea7449595c6e261127b34", + "0xa45f8b3b7196449f9952cadc8d87a787a28b4ed89f8c7599e7db361cd0f0aac6bfa464024ded5c0ffc660e417594fd41", + "0x85016b5f7ea9863557eccb0e742cfbf0b09630f0bad3de55aec92b95d4645055cac60d03602586b34f774bd356dd5554", + "0x94fd89dff2fc6099e5ab90149458a4c794eb1857f1dd9a2c84b88099412477dccfc2996cca2abee68d23a05265dcf271", + "0x945a52621ec19d26f7c8abb5d01e4f5630924b75a349ce74219377a137f4a0d386172da523edaa522d27902444023cd9", + "0xafbd452dcc57f5db6b3fdd55368807320459c16559d944ee8ecd1af6acfe9d58c13f37961f78030883f8ad7dbfac66e7", + "0x8ce96b3be871a1f33d559a6e55e4d86a0b92ec3954417f8d98676264596c3296296532097b9b20c83c341527a0c929b6", + "0xac6a4dcd58486d25a4db1751a60ca4d02b80c939b39ca165a37d9a0a52d8675b3753719f136a59ac400bde3efd036c8c", + "0xac87a37a14a5d48842d30432935929a0e9dce5642142a8c5b95e377ad1bf52120dc64697f0508b7c258af24a0ef484ae", + "0x859f0ba02d496861455d9c39c269a1ae5bd224319918fdc3648311c93303c0e13301ae7f3f77eab4ae43f1184a912b64", + "0x96d9b1d2d2fe70b8fcac136a65b62a4ded85aad9d350c19bb955750a0b24f93174e9cd00c0e0a1987793e1180dfdf66c", + "0xa7f5135873a1c08c7c8d46adfed19d0ed0e33168d463ca74f75116168355318ad588ebcca1946d7669c5106bc9f5a8f1", + "0x830b0587587b80df078ecfe0857a4b4cfc05b722c0f4f3e1217048ee18749e9940cd0200c1f7a0f60de832a5a44e9f1a", + "0xb6625ed0199097acc9aae20611f02d2fb837e4695762cdeeb4dd722517ba5a344e5011f14d5076783f3c32bb5c4a027f", + "0xa17be2e528c463aa4ce4bba2df5b005f88e363b87be7324239413ecd5bd68e350d290370e1080ab9911a0d54856536da", + "0x834064460f0e5f38950cf5ec197818712f01950ee1f32b1987dcf7f4098d20e1d91fae6d48e8a054390693a2e572f888", + "0x86217b9bd269408ac92b5cffda5716bb3bf8674b7e222668d72939a626f4ab64f30efddf85108c0764127cdbcbad7d69", + "0x8d7cf47b0648be0bcbd3ad1062d90010e5ee84e397895ce98160d5a568d60a19582c985944ec27bb284459789ad8f6eb", + "0xac056e3ed3487427142b3a4e4f9db53f1a752e1994f178577c46dad71be5fad4d03d24ae7019804c41232705a4bffaa1", + "0x94b83d67af6735e81b2e392e6af8ee4dbafb0071d84486389f36f222dfd015da718c621acdc4360630403762dffcbe3f", + "0x8ad27bb51c6cb860c21954f5d09dfefcbe3a9a0bff3e24fd1f74850edcbcc76b5b389a616ea0c0796b239b0c22357a44", + "0xaf9990dc4c9f536385811528f207a8352b083a4abe6dc016eb5eece0ad74da65b2c6c475a78cd0ecce0b2b550e4412cc", + "0x816dcb8ff8556540b54dcc1efbd2242dada0acc1e3d3da13ae581d905a9106bdfb8c138eee93992a23e7740593e8ad80", + "0xb8fcf8e11e5924d3d38643b2a4bed4b54e69f816f40d4020e76655eba8ffee758c16cdc2d970d3c8c1163cf501044c03", + "0xa50e0ef4ddfba6d969e7dd864a20cafc7fa6aa232fa7a806c3d53c3e029cf110828c5a9c354ea42aca5688896f27e6fb", + "0xa560435900c48879ff3f89067daa8e512482f061c68474d951c608ebb5a69c7863a28fd1e216eb4b140e32124e50fc73", + "0xb9202d152b7b708ee61c4fde6cf423b481854538d2580bc43462610f12141b89ce779c7398a35c27ea6ed0afa5332bb2", + "0xa9b3f8be28f9546bc70f680dfb9b08c1eea6fc381cb6f3ebfbe33bcab48294347d4e64004c11dde5eb409ecb19941ad1", + "0x8cb3086d265060f8e52a96fcecddfd261886002c1821a8f59a1ddde19a6bb1354b17cd19a9cbec19149dc219a4c394c5", + "0x906e8dea406ba0f0ef43ff623f8521039a9455a2290cae4ca9bb6494ee0aa812528267d1349bd5d339113dc9d1616b28", + "0xb9b5212b76d5824d66b8df7cdd5effcb05ccab5df6ce67558872c99d1e484ab8d21037bc0e22f5c4082b192972b80acc", + "0xa1fe817596bbb5bed93a5dc4c03e14eab627484cdc7ab7e4fba569ad0aaa93b34c4fc8680c4f8180d8190113218d26fc", + "0x82fe7a20fe93564cfaf7eade8d4d1394d1b4e36048cb8632bf366d3d8084ee52c74d65c4c69d9d24208f7916278aa592", + "0x81f42f9a3b8007e5f02c26770947f884c715bce1e600f38f164a390f159e2e5b6f8522ef566bf36422b14340bb6d3556", + "0xb53d3c89bf2a4b29bdd8f1bfc001c2533f86d869fbdb383fe9cd93ef0c49da0692361baa9f537094e1af662a3461f8af", + "0x8fbeee613823ebfd514e991d81babc05176d5c115907ec36dbf83a69eaaacd622f1f36be2e47b984cd6ac66a6b35816d", + "0xa9068ba463ac13d4dba25f9bbe3c93baa35828563f357c53a7009cf0c809a23502e023a32f651e29f14424c5daab2884", + "0x87468aa4c942476b3ac3000e740c4dc72d320884357dd99eb25e81d7b52a859b9ebeb55f3070022bcea3855a9a198e9a", + "0xa5f1219eb902234ffe8ba809df590080ce8329ee574eb346f6b4372892d66b0725f048465221655b70b3d4c2deba9fa0", + "0x8d9663d4b48cb86201d343b20a8e7a6ec47a4bce0e85a905be31121a01fbef95d9f29d83530faf79dda163c6c76ec514", + "0x9921ea9176744e15f64b20ac6e95ec132052eb853ef47e9334108778fee60d9d9b53fa0b8011c6a4aaae472eb11cc61f", + "0xa04c2c5e2c5a7673652919aecbc5fe09a636fcae2d06003ca6775018112b606e50bd2d6ae6ec6131d2a9999837186bd0", + "0xa00ddb29776d2747e3a6e68eb51a7cb00ca0066a9aac5a2da632f355db515b32e2c441fde870c9731a9dcc8d9834557b", + "0x85afeeae8bfd92c51522320cded430c2fef57b1950f9f966f47ce6354e492e9c40f950a7ef6d5202fc79fc020f7a6260", + "0xb047d214201744cf7e675af5fbd29579c3b26020c5e0a53e2ce078778b3d3a673f0fd87eae8af8f0fba3bf0f8341b63c", + "0xb8aa5364d914020158d11fe82c2b77197ed2b1a12492435200204e20a9209d3c0b4fdb6fd3f0b1db71ee3b986400ff46", + "0xa59a903fcafaa8b5876a3eb1d79a7db17c37457dca018e393324d8db3be7c2aa3ed2303eb3530d6fe1612fd75dd92e08", + "0xb1929c1711ce44465daada15808099234c0c5c8f43b050b2792b6ef9b77825996a74abdcd84d6ef08d648e77cf804357", + "0x85bdc33f8dda0d853074e0657688899befb6356c38f0ec2ac27c46c39fff06617edbb1c5cd220314335bd1b792f1e240", + "0x862047e51f9119f5a0a607469496c0574b0087d566bc58cb5b61a9a841a3cb693b89837a7c927c542ca03d0106055438", + "0x84ba54c002150e5989f59064b68989413abb5f289f3ccba215b923f86f76c19718be51d503ce3bcec68322a7c7d5446d", + "0xadc9ea06c11bf3f0d704b321005020917e731e6706f18a5aeb1b56dab3de39a94fe8aca3c248a47565ca5ce82face9f8", + "0x868324c4ef80bae55510316f3a8b13aa40e60c8a3d55f4994439d1dca6f5928c4cb202769d78c21597d8737e391536d2", + "0xa6e3b57e9909b5fbea2114c352b34235a4d4147417e480580c291308b4b9cd696b36278480893667e8ba01fe3bce571f", + "0xb92e1d6ba0a2a244ac5ae2e7b20e152591c1c466c9b4c658c72cc5985ded0392b04ec00e32291f1652d21dcb633919a6", + "0xa3e2bb4dc07ffb1e8dc9055ab45bf22864980f64b612548ca7feac85ecdc426f773d6d48bb7e6c7a578025bfe99307e8", + "0xaf764cdb70d5afdbb49dddd519451218db4e97ef3ee622585880425c3d85a8df88613f4b51ad40a1f6635e45b2efa5f5", + "0xa426230b8ed77eca3d1ef7f4735fcfe0e51ae37efea5b96ea3bf313c241bd703b91a592f035e98056315c9822ffe8c26", + "0x96a3ae7f1b80690f97372d086d2d13ea2b40802bd053980f73cddfd37045364ebe38064a8cf3531e9bcbfed421040f20", + "0x8cdfbf0663bde624b703d7e6c36c5753282487147e10e5a24fdec75836f7034e4c38f3fa3df373476af969a4f835cec9", + "0xb7f7a549cdfcca30b78349b831ea5173bf5b91d56dbb999b2dbf6b85d8c22ca8a9a62b38e37dcad7ee5136a32edd5743", + "0x82ca90321c43d616670a7d85447afaa9034459b796b51792c970fd5b8f124e47a13ef661291a4ea58a495e68aa36dd87", + "0xa824a36e4e2db2bbc513d39e4e2a841fa81106437eeb4fca9ebd78050667d0b284b7217a35ee3eac67d8be58c0af317a", + "0x9370dd0c0f9c7585761eb5f06e7899d75eac07e13c140c64d4c506b90495fb8ea914f222608c478708d4b47163dc9907", + "0x88c07e19252e905faf129e3e877dff8dfe93e81b3903b150aa33a93a7eda2820a8471be935825d709dc662d06c9f99b7", + "0x81e936c00425f7db8f0dd88b16c3c5208e8d95a5072e69524f3b5de45f4e2dd25f0aba8ef17016bd914bc8f5a42fcb6b", + "0xb23227dceec18d6dda92a15b7dc8623d9928d545db93b3547fb068c99cacb3fcf3d7f88e4357801de8a652b919dd907a", + "0xb23f1627219587773c17070bbb190e1280ab27c5d7e98b43adea0e1f5017790149b71f90c3691301bd514d20238c5e6c", + "0x821b7bff6349c204ce50e00e296982536baff68031165ae4c639122195e7295ea0c82ce66fe32a1b762f6a311aec384c", + "0xa26c15bf1ef4d5543c4a006e4ad2a450d44c93c62c0f0b035698530cbbf925f6705d375e1dc8b2c6fd9a2c69f4126b77", + "0xb5c5bfff4697fe13a5177fd87a8e293fd1c6782cfb3d1f95c5ddcb13c309dd1ddbeb14cd359c9f3029b57ba52996c9a1", + "0x87a0d37f04155bc22ade44f567dd8a81445facff15d643886cbe6534aa44505e331bb75c9ea2f27624154a5890aaa2cf", + "0xad85c0e6345e2333a0ff76b769592f2b24fd0661984498dec6fbd2d9b0cec5f139bd71331a28b13aa490baa7fe27b635", + "0xa9e6298b90aa8d3f4385858e08f393b3bd61376ac3dc44a0907ccfb372813bbfab1388d544c1a4907aac38a87dab2abc", + "0xb5cfc8bbe4cd3ac1a66b1c8138c5c68e643f7f4c310cbf1483f6e48d4f7e2d1cf24b2704fc687032eb03978f18239072", + "0x9493895ce0c815b60b0ab3a989f63c6ba4c752976160f3e52290a724ddaac9075e07dfa913e113807e0e57725b1cd593", + "0xb1e800c2aa32d34d34b24dcf890f6ccde7da60b98c4646a5471fea7cc6df8862b7a9c4c40f38d0554e33e2984fd564ae", + "0x90a18f877f149a314767f5dc15c8726efe5d20a8e15ad4922c6042420a2cd82018be813debf02c6d69b96e8a27c0c5dc", + "0x8fe35142442c103e7bca602445b87cb017c76befc83d66894d4f810e343b3a571f3fba14d94521340ee7c5ccb13338dc", + "0xb43547cfaaae899fc6295f496f213916e5adf9b0d75805c32df0f969fbc1b4f8584759b2a06b81546b48004d72f2e8d9", + "0x9410d55865098325c7b559eb4e84fef8a3ae890e1d6053b3f173ce22e60ec6563041ad8cedaa2dedbb59f3dd645dd1b1", + "0xb127d9e4b8280e10434d53207a7191782464ae83b4463cd8a32026e5d8a7a8c5306ba43ed9b7ea637d65f64d6a08bcec", + "0x87de8fe67524c7d107d7033d4107659206c347c47cbbdf85e3441b53c933417feedcfb049465c67f4c4156219a4f63ac", + "0xa582f976e77b861731595ea8450c6b525e371c6548cbf7911f05560d4c7a4b62a425d5c785190628d1aa1f8f27c43b51", + "0xa195e358742d924fe2a7f955eb21ced7b211cfcd5dc3e598e0d2713c3639b72f986aa586b7a22a75f8547bfb46cd52a4", + "0x97c249b70ca2f9da728a256d18d600bb923005ebad3e1d541ebd580af3fe07123fdf87f4e8f98fdf9dc8ddd826ab7344", + "0x8fc7891e2f540d8f20464a36056f227ac2ef3bcf2b6edd4cd2d9024a48fce19480fba36afc7f5c4bd7234787b8d17f31", + "0x9047512fa27e2d8d901516b5714133c1845494b6f2aeb2a0570dd8533852f00a8d9a8ca64979310e83ac73fbeccc33ef", + "0xa1be9cba454617af0dd38865ec29e7d0777d7c68968c856f90b5bd63a7cc4274fd8b179be54143bed972b921864424df", + "0xb086ccc8a705999184f51e9b45c76975ca8b108b32a3955e122711fc1ee007d8417a85c9cef217f28d6c7799b60aae4a", + "0xab0938a72118ee2980b28dbea9f7100c6f54fa82d93fba8bfa81b6bc34f9d2033a987e5d6d3816fe0bad53cb88bb8c2b", + "0x90fca0bddc14f70282f11998fb4c289fad5c0e78c8e8f9e7a811f20413459a05c9d107ae771e9da501854022d827f2b8", + "0x84cc69b7200f63c2214375a7a0a5ccc14bc02ae45bb6f3b27f67ac538d01e628c66b40e5c40cee38bc8634f1a3c3cc6d", + "0x8a07a1cc0a96e6c6da0d27a540e235c2ab6a95d087e624c90cdccd781a9bea6abc0456d896574950a9e21e7d87fdc582", + "0x87f2084a2f2218d7f9eb7725617ea37db0a19fb0bcfd7c95501f62fec0bb6bde3950690420a40d93e26f426fc622c825", + "0x8c9fc9b470dcf8e576af943edaad28c29f53ac7e24657618c21d910eeba6d7b16f16c418bdd5cea3d639c3919e93b7dc", + "0x8f026883d9d8c7c2a5c04e4c7220ba7061a48392a8a7794a3e290a94967d14caf040a3da3513fd9b4e695376e706006b", + "0x83bef852b9f387a2aed0d3537e77c895799c639310cac98e7b699e9f5d74b2b21cbca58ef910c6583e0b849d665ad379", + "0xb08a03accdc64474490706edce9df7853b78b719ee731c195f70871b7586ed274778d99b84ec3cb8cc0b5e38c102bce0", + "0x99fada688669b2ea8d9b7cd730b057292ec3fabd30cb733ea3f7cb76f756b244cfb26df03b9c087b6d9c58f5233dd1b1", + "0x8eb0fc7ab6b4238f2317620191dbe835d4ebaad0882e22e8f0857053d25d6d9077754251202472d875303669dbb806ef", + "0x8fac2cb38c3a1e361aae5313ebdc1c7e0b7d1a440482fbbe24389a7fcd381169fb325c79e430be170452326cd4931732", + "0x92bacde1472436209032f0592973a5a40d505a9b2c9de648eed1ce392d0c18e23aed9114a9634ad3a7e6afc4ea80ff21", + "0xa28b394018434be07323a2356fcfd6c70b3a4b1c6b6ea44da1da66c389a659235e0dc941019bc5053ca41f10d9b6db2e", + "0xa6d23d7fe7ef475bfe6486ad4a99ea376c6a6db3e70a0a7af421ef6e6c4d6b9cff68d03a7239a56eac784769f63b2bf0", + "0xa1232e6747573e19df98a088fdba57116745612cfdd4ff21f8df82a66c7d5df7e0a6c0cd73117121a516dfaabd0f5016", + "0x8dc574376016b73f6730103cc45c952c5df5d047d0b4ab3da0303f66f43f7d147b5eba5300750e885c621e72b4a64b43", + "0xa66e9eaec79c958e624655fc2adb7b89ff3da0393898e028bb07cbd6511ca8d9318e1d60dc11cf0265a498290e756ecb", + "0x8e5299b661dc0e088527904d2c2fc0256613a1fc2b92bb92c633acf145edbeeb053e82b468a3877f6f14f0878fab57b6", + "0x969943ce7b54f6e728724b26cfdf4df90faf9f9796bafb910ba66d96cf34062fee6ed9121abd193c9e322950c8eadbcb", + "0xad29ce021d7fc875d1e61ad3a99e112ff092ffd7900a608bad30517e50e2270e0f8dc7fb5cd42f1bb995c17d86268f48", + "0xa55fd82520f4d35434066bf93a9601c96549cb4714d9ac05c32e16803daf8763e23c3125d2005eb229bf5d7e2a91ec3e", + "0xa95eccc21af531c5e1a36ce88eda6b87732f5fa680e851bdeaef73421c1c87c8e66bc314b07ab472ecb67a08ec53cd4c", + "0x8f48b5a0636bd89a1ee259223065449523984cf3bd9be78c9242276c848d2140bd94d1a6670e446b51b178ff694b5c7f", + "0x8a58b340e30f0cbabcba1c565b68eae66405fa2242b43a7f7d3bdce279af42fcb4ef58c85fe89cc2dc56a41a51f058b9", + "0x99103a659e26c9a4d19404db4220dcc5defbfacfdd969eb7d70b4fbf9b2c91c92112c0097e8f0c32ddcfc35741da21ee", + "0xa088cc15a45094cffac52c38df427b7144d621cd1d12ae87d74c00a039d757e36fe3cc2fb35fda9b33b375553585497c", + "0xa0f1d15bc388f6602c975b4b9cb23ab83fe31124acd946195b999620c3c20c6610157a891114a43e3af551d7b8c3e4be", + "0xa571057592f3a9008bdf726254c364975705a71bce4e084a37915c5317f635528088a2f50afdbe7240c14d813e8e979e", + "0xa31e425feee58f8372e2bd4c21c48c299850df34044f27db3e4df55fc5e7c042cd19be59500acb375fd3478379f06053", + "0x94645ca6400f80d9a90f5b1c5b515816d6049ab04a552109c9c16b41366a7f3931d49338d944ee8eaf2ef5c77062c362", + "0xa61fba865027b7ccb03a4ea966081325eb64db5a64c5d765d2893f6a19411d40dd957d8a0b34733aeb3f002a4e0279bf", + "0x8199b89ea13ef8eb2f54d27bdcc781a5a7fe5bfef4ba4444bd651ac6021f4d90250b2f2cd8c63fa3ef237ac6eb1bab36", + "0xb39e1e98d07c95a4fc19ab175147547e5a20e66c044f29e4855818db4a7d0e7e2c24192aa8c89fe378f8d8ab3e9f0e1b", + "0xb807bb0069474e190b40bb2b34165351e73a392ffb0de83879ddb181989a22bccaebfdc90748f54de81c41ea244e7ebd", + "0x8b058266df90032a1a9befc7abb759b64a23ab628edd051da8b81db4211c72fd63093dbd94796b0690ff2b0c0fe16cd9", + "0x865decd95200fe45947a4249d2d8551ca5d7b3d7955adf10f94ada3e69d684e5c5b8939fee9a4457f22d21bbd3ce9670", + "0x95fb5ce7af13976320b36422b5cd9dd46379d13110fce619969308ed6a250cf3eb84c73e8ba1d32edc01aa2f6e367707", + "0xa1a176350aed82d5ac01a072ac7f3cc1535e20fb597ebc7e417921537f9bfc4cfc0d490d4df831f0f8ecedb6be970a15", + "0x974ddd091c1aaab7ed356b65c244748a713e98b133c5606436e531c31b31f6ccdcad2037b12c68fb54af4b19bd1d82ab", + "0x8ae9b7a8cd856087300ca90799ec3265b92f84da8ee9e98c6ede1be378dc040d0fe68b8ffc94b146f2521b9fe3d19e54", + "0xae17df60b83e4530af584991b545bf4b3cc1045416dc15250a6b75a9a04defae4c0f60b8bfbeb54c8a21fa84fee58e69", + "0xaca1e75d4a05282b0cbe6256925c0f269a4a8323888bece4a48aa0b5e7bde7fbf1d3e4f5cc38fe6a38aaa091ccbba4f6", + "0xac19171d3ee2f2e5021418c37a0eb25c083de6a6396290ed35b4771abcd07fda745fd082e3c32c117bbab7d9fec2b67c", + "0xad8a35eebd3bb28e08b9ef64bf2d8b75ead69db29c96544d71686ccc0819ebc6823e49b3b879ce0e5ee3131153900632", + "0x9479f12dab191269b020b70132996cb32059ac087e2a3f3e559d297494189e1d6849c340ace032946e12bd4923a3908e", + "0x8885e680de6c158cd67d66c142b2b4ac42b96e97eab8e2dcb90c3b454dd214bc530fbab6b5d5746064b9813775b6d5a0", + "0xa16d8d27d9b2fa04c7eb8436062a53ee0a4d679bb205d7d7cfc24e5f28e3752a9959847e9e31496bb0cb1c11caadc30d", + "0x951b00c69dfd9fc80b17733b843c440c58095989bb8744fc9db63a4a6116f24d5f224a279478fba8cf57753261bde217", + "0x8a693564994a1dd298f0b279e618b46bed349c53236fed9d8e05ad9383ce55fed02b8a361fb8c09ec5ffa8a271cee015", + "0xa09fbd62995a33904b4a34ac55c80f6d4cbd39a902f5db1038d909f1a2d385c3f5eab04b157b5662558bf35ed29cabc4", + "0x8662373988373409a4b31d45c5110afc32aa008bccbeab39d5b09a0e10370dd879684e722a8856b0da278e2bb91d67a2", + "0x8980d3cb8a82b3a827ba65f44e50efed0a6f37d6c150d70e4dafb67b1db173b46ca29d487ef9db92d37ca8312d246008", + "0xa279558faa11850aa4f0dd9ca8bddf69cb98bcd4edfbb0c19f22d1bff85d808e8f2cc026d95afd09fec2d15c116bcf73", + "0xa3fadf9c3066c93aa6a31d2346ad0a1d012c12ca7a24630aee46a087eafe5fa518d20647856d44ac03576bb3a9f81a76", + "0x8a8a19b09417e1b1607aeb54841fa69f58e094b46971c6a5cd0fbeb2aaa98c56599ac242272e6973ca0a9d2c09ff8d77", + "0x858a636f510b494edc76e86b1718228f076b8a21306b02abd086dc2a96c7a034704d743ca5d89b17903fe7b2e43e6fe7", + "0xb031b789e4073b82bb8c78f9d3fc2b901d75278733a4fa0a5aaf985a298269a735217e85eacc0dd554375d610a425359", + "0xb8603ce7cff755f5e07eaeb4d74dff179cde405234bbd7b3f62fd903054aaa34a9b868b04617d7d407c2b8e377227f07", + "0xaa41829c941acb3f9f0e2008e852fe855e153960cd3c85c4b8ab9f97ca91b7a5aa18f997cd023ba9e03a653f238a4f46", + "0xa35639f920619dff592176aad2b4b071d5c960f149c3a75311b16841d1872f29aeeb7c155cc9bff41ea7ee56f799de78", + "0xb252195aaa52e9a34936ccd1aeb40d28fc262cc4570d4f9685da8c591080e97438edf64d4d4d074491344bb5e86b6b23", + "0xabe2e52d10620b503dd1aa584e005d857294565ad90dd89217a77fcce4bea7b0c72d54dca7a1c31b5a9042a9602557cb", + "0x818085f2f1b525d9b2322c8785bf27a6759af9aeb231b0977cdcc7d7e77cab5de056e522dc791e72b8d9b93a9c41e833", + "0x930f64d40ab26be006e91deb854c5b22bf6951308dc406b2c7c7791d5dcec470529957fbcfd6a3c9655d544d974de7ad", + "0x92b28bdbea8c7588ad3a27992c19d73bd3a478b276f0e11c4e82ee2482e4e167cbcfddd17a1ac6bebdd862be65f54098", + "0xafa6a85fb906f5ffe52b6e9715435dcdf9f7892a396d740d67560fc42248d23bef470989663a73190ac9da442cfe6a82", + "0x82d3338e58fb316d66694ff4674a5d99bf0b13204dd251fdec95d48382f2d2ac60176a19e5ecbaab5e00af2a39a338b9", + "0xb30cd35eb15b3910b8b8f91cf04c223d79d587a7ef713030f0ab93f446caeef52c60ada365f8d3d645b477e7fca61d94", + "0x89554d2a9a11dd7e56f0b568f2a50c72d86362d95eab5d94a2386397012e18bef7c9e01a2d71fd325c0692e4d316dd16", + "0xad58326fea1c00e0f8aa92923661be4b3ecc79164d68e91c4d1366c9894b6d049a4f31c9bef6e5f21466ec014ba6b94a", + "0x8915a16afb0e68a84fd12a9203f8f348954920126d88136ec027a81f541b67c421b84ebb3d6e8f085c38c2499c28ea61", + "0x8e246e1acf655572863481367da007e94bc1bdc1f28aeaa1fb163dc05a51c3526a2bb9bda0a14fc6d658d85a9322e44d", + "0xaf83f9ad3c7c1504fcf60084e0948624fccfe3a9892dbcba8f166d0d67b475ce57ba008f286069da20a0da0cffe3b4ae", + "0xaec86d2d803612e8d27a01e3382e0a876164baaf2f3b8c4e9455ea00bc2e525378018e6a41ed9686c6408148e852bec7", + "0x871bdd8c84abeb1456ef73595360de6cf9f92ca9e6a8b6b816ec7497be60a9f509ef2c91332d17cb5fbd347bb0113d2d", + "0x9503ce513df28b61d721fd5e8667272a28f210ef787bee58538f786acd16f04a412387c6f5e6313c43f426a70aab65b3", + "0xb2cb0526e7e524ca9fe918e951c19460aca911d2b5ebf97c2bc74aeb69778a28316dec8916a4e3628b46bc51586c1bd9", + "0x98f52ee1896b632dff5153e3d1fe389c6200b14cdda6b27e12d4a4182763b63e0f587386aed78c97a32114dc286b975b", + "0xabbea974929c9ba70551231e3833d5cecc71c60988826771f792f190ca77c80efee7607dc1d6bf01a53796d8d9b73017", + "0xa4cfea1d06cf840bd599b14c011b6b204b2cf6f57fc7d7f310052062a4fe8870f02504e6c837c2b556c925921e543556", + "0xb957529d7e5d1fc45c5a822a6e0e480e46af2f5cc3801c31996b9b1acacfdd8d142265148b3e1453a0df0c5e6cffc5e6", + "0xb7411aaebb1b6a6a75568f81d052e60fa7752a64c20dd7cd5457f999f0185807987de8fb72ed94ca9d1148c19ecbe1d6", + "0x84be67a5ca80a1fd0f43cce4c00a465f167445e42232c2d2cad5e1097a62d3ad564041a55f0c76a340387503f15e0ac4", + "0x98803688f8e7b445c7ad14277b9f5f12acfba4f9a4ba6df9e2b7dadb726f1bee5098fd15e0b5308b6686a38864f84912", + "0xb085eaa421e566276bcd45d8b9fb71475c4530d63e90914eb2a33c17333d5628c1ec8a45691cbae82ccad97d4addcc94", + "0xa08ff7dc59dadb183dd0b5d336b6174464604bb2b49315e0c42f34ea08a8bca9dc9207750638bb7ebb6387257411956a", + "0x94d72607cd8a75b2fe2e9333959bb9d5b54d74ec36fb8c123c157b19a17f01f310b3311116b34bcfac305e9deabc79db", + "0x85fa61a796226ce555f8195c792ff6f3d483f62dac41c17b7e8295bd49ae6039574896548728fad4ce966be84a62a6ca", + "0x829ab1087ebb61db05c59e3c9d03e7010f8c546db117a6409bb813f6fa04061833771c8aa4c5e2981bd1ee551df0ea59", + "0x97f5c5261db0b130bb8352fbcf65002327bd6d8a7d4fee2a9bc293173c8c54be37ae229c5488c1983bc1f7857c66188c", + "0x8756439e5978ba19e2cef95dc55f706d53a05d1fa964c64d89b0e95470b5344b2f8d44680080626c37c77a00ff0e6b00", + "0x915d33f90980089c33f403ba4fc5c689ea7f1656f5c4e1110db987c59eb981b6a46dd9fe82c8efe7d1e3504f1d2c4d2b", + "0xab5bbb84884ef036c9b00a84f7d5ffa2931854e2afa5a63121fe64d548531af4203495b977bfb9301bb1e4679d42665a", + "0x9830b846a9041e4539eb858a179b4db6da89b507424e6d858ca4334d973ddae255bbfb04ae25c3276ccbe97c46f5816d", + "0x8e35f4563b8a5c9a76cc1da87ab21cd894de393dd61bc977cf22d3de454de350836e032ccf7d6ea55e2e6b83c4424146", + "0xb6338ced0f05806c625905cc51b7e772c5db3bac144e897339f67b6949f4d648d41b7d23bd3f299f4879507951ec031a", + "0xb3afa470fc71b92f415b879a814feb0702b6adfa08e395cee4f7d8b0e3537288f16c83b28ad4e2db02c1fd6d39e6afad", + "0xb4fcf7af3196bec84fe1f6e3bbebb8abadbcd46de02a37966d0ebe20972fd890803d570e4a201f2a89f479e09f19191d", + "0xa21fe1f8f57691165d0c7d8436765562cc935288f24fe765351be335f906c6c4dd1d0714b134c51255b14511c957319e", + "0x880a3a8f6b4ba410be06628a011e6bfd38d86919cf8014b4b4e1c930f8e3035749579389690f21bddc4d4699de8a4b1c", + "0x907d93a7666d847a140367c0a0ff80a96d6a8295b07cc4ee52d3be987f431d8dcb95d3717dfd248a5643c5395ec2891a", + "0xb8f38c78b8a2c487874c1a6a92d15cf0dcfd26319d4cf65c2f4fa9432203ba5ffefb02b7324022c34bfe0da369d96d65", + "0x8bd4ebb6d720fe52d626a621670a06c8a304faefca3846df1f619f4d456e14f8bdc547fa7210b8639b89c6584ea5c5d3", + "0x8ebdaa288a71a2d3188d6294ad0948a4f72c1eb6a2e921ec82cecda4d315a86e3e6233b5ffdc7219f34a99e9b4555317", + "0x83320fb9dc62119655bb0055192471ae06b7641dd4af64670a4d9475155733555ad06a93ad2fae72e029049601780654", + "0x80b3d022738318238dd32f122bd88cf2f734a61e315ece521e9e038f4a9bd7b54b5e67784f5949fbcc5fa911dd4b048a", + "0x891a23b4bf5cb8558b4540b66fb6b9fa54e9d0b2c084f660c8bc77af5ddb97cb5d8042b538f61330d9fa8ccbee6c8a41", + "0x8e5651d9c95aee23835bb1a06eea76efc9d5c881cf87ee847ee5149fdbf3d67dcd8ad0675dec8fca6cef25368348abaa", + "0x86bf1d094bc4fc7c21b21cfc7adbc750db0b27c35bb160d306b26fefb2699cbbb1fe624df1b9e7f6f895f1b81a829361", + "0xaebc3cb2623344315875029378c71ab7ed3cdc9d3d42d4b835b373c8420adefd177a44e532f3f06f74f0a40d53713e5a", + "0x9527f659e93a740b4c50d0d3d9aaf1a85936f04866ffde1aed30ab2fa1c1d565b46bec5fdfa510fc3ea934137bbd46df", + "0x8488673a4bc29c3ce9133cbf41c546fab4ff28c5d46048a21e710a8df4f2bd1c77d0ee242dfd962a30d646e5ebee8c01", + "0x8cf29773c0e0fdb75bf6f52d7066e7d6e9a3ef056bbb70a98026464b32316189addb5766822f57df63bb68b78c85e1de", + "0x810c6c1aa53f9c3fd0018651b1bf25215fe24687b568f21a121e0bebee576a75e5f0d08ac9c6c21085e52228b314c6f8", + "0xb529a87fe47402aa9ddaceac63a060a6640418891f931036c6e4098a1b308821a6f1a244fd5c1c22a6ed5f72f6bcf825", + "0xace9284ce89b5c81049d329db2376a85feeacdd9f735cf00038adc51865bb78bd9bd5d060efd0b727c509ec19988f99b", + "0xa2e7a949c951bddc99e68d80b3f3fc4ab960b682229fdd794f9eadc80bee91dfd5eda0052149d05c74fa33bb40d75ecb", + "0x86bac22daefca9143e0b1d25534900b1f7711ade4437642043c4a8c65f0d963cd1f0f958c7391e5a663dd3c59ed9de60", + "0xb7d2a6e2d44edcaad19498ab3368bfb87f9ab039cf2249d6e76091dc3db0c3bf45012779c02811cc818e95796e6ad9c3", + "0xab474f74e1ebb3dc696e7a6bfd8845ef15fb6411fa28426c486f7b0f789a6af3016ed5f7da2a36215729f5cca0b36b4d", + "0x86616a1a9dcb50d1896f3eb937bde67f213558feb401aae9898e41cf1fe33b443170c7c2dbd1648c9e3cdd0c24289286", + "0xa466169a2d95a5fadb6a69c7061cd2911c3eabc0b1a2488e216f8cdbd2c5bd87e80908b002b9efa51a67f02d7af2155b", + "0x8368af8b7c0f55f3c4f7036fbefc9d6a0ee9ff61197cea8ce48546753bdbc0b61eab604b8fe2c1aa91ced7a879e5899c", + "0x996c91779ff9767232ae603c5b1da5bbe0e303c4c2c72ad2d5944ee1297af3535f6bb3548fd1fe8a92cf4b281e1d83ab", + "0xad4a93d1ceecedd27389c658b38dd71cb13c729b27e490381d8c3ed4815b11ecbc37b1f82c0656e0ebf77e5bc35196b1", + "0xa3538c7ea3dddfbae80d67caa9fc547938bf77114559f9fc5180d9d0ab837d7fb1b27bc37405686f212f2e98b0028e59", + "0x8abc9fe135fbd48414f2ba28344d9f49ec2d5ce94fcb314ab8dc31c754f3ab7e6ab268184a67dafe8b1fb811a762c112", + "0x99ace100d8db88a83f1727b7b48baa1cf45b971d08112e452f5003566815ccba0ac3f8b1df6504f55a392efac8e3e70a", + "0x91ff50978ce629651f1501708908d75b490c18615e933191cd37613a83d4b605b0b48d024d27807637e662056d76276e", + "0x8e4104331ff1a40cbee9f489a814cf5bbd6fe4eaa1cbe1e13625fc3e6697b27d933265e5ef8728cfa8fc4ba5b19a614d", + "0xa442360d49bc9ce3e75eb40bf2ba05e9437fa594e8b8de34bbc822cc7b706dfa0dd10bd6bccb702d8556cd1320924821", + "0xb6ed6cb0aa34d5793e929e0d9b9651e41da3457a0b20c1bfa93a8f65bbb65bc07c7507482d71c1c285f5f663ae60019e", + "0x86d64df2dcd36d0c7348c77480c8af33dfd889bae7bb045888eecbd317cf3e4731b96ac18f410a99ed33a3f22d448f77", + "0xb8dd25415275d5ef8260bf5195ddb9b15b78a8799e4d15cca7d6317a18eab7bcb8fc759be61360915a28a8fcb5d6ddfe", + "0xa325a334c84dc1d9acc0a9315b399d4be93351c7049f474702ab58b4cccfd69aa50b8731ffd598ef0144ca96477c273a", + "0x9012a2dfedda5147cb9ceac681fa9e75e2901eeb3c94d87465a44d11250de4bc66d1e00ff674f2da1d800b43f686df9e", + "0xa1049d59da2a942d4d2aabfc0d972ebf3babef9c5d8fc5598ea23a048c2e58f7f17b4d860e437276bdae221d9e41e3b5", + "0x8c9d9a8826c812943d74c4d4f0fd2f1c8087135c37bcd9647b722b59801b01775a644e38c24b43e8e70f83bccc4afa27", + "0xb9cebd7bc7b041c18bd35b970f87e9b5183e4ace034e21117001fff8a05b2a7f9ab65cf6ab8b818b8192d1db5649312c", + "0x826710d6432ef97625db25104fc8dc3225bea594a10cdd4473d5ab72be57b74928ff356d210032a61ca590bc68509880", + "0xa18422ceb8c61af305277628e154d3a9c49f47e230a44c6216128d73db7c3ca9eca9f87e29cb2126f1c312f423c61463", + "0x919d357886de9eaddcfc46cd43e2b3dda3f82e926a3aedf02ebda9159faa00736bd2cd7aa044c76ae238a3a95a5bef38", + "0xa822d5a726f5c38e9d4a750ddec80bb965a6e5374a3d87757e2e48a18421f3142c3985450d1833f3ff4ca36e3b838c89", + "0x86bfb86eece6f6ea8f51985e312171b9bc821e0c3ab4cace556da28dd7bf89cfd5be3fbdadcacc19f2371c6a11c564d5", + "0x91b42643b297d8eb2c1bb3f16b57ab2964de99dd22bcfa07db1d0010715ebde94d11851df575f4f1ae602644e454fe0b", + "0xa5e444ed3d5fb3c5afd2c9c24d676adbf396f5d1d47bd532edbc72c83845970582ec49ed026b3b982c9c1ea725192cfd", + "0x8448387a14d84aac6afef682a27be67e5b05d31b59346748d2940072eec771adb53339f335daf4463f555da2d8543f18", + "0xa5034b66a26bad0f753be56dec722fc98a072bcdaeab0bb9cf35a56a573d9424cfbadbbaa8ae30690f7c6c6495331fc8", + "0x9317ac32da1772099f41564ddd8247e3532528b240db753a1fa6fb35cc039c6a4ac4546597bb2fb28721684bdfebdb88", + "0x8b4b0001a6234335502c8b17c4de274b83b0610960b5c46b9075c6e41f357ef0d8c94e9b14bff8be7849435512626ce7", + "0xb1aa903511fe4219acabf8761a8e4316cc4f8955ac8640c68a7b547cfc65365a8fe0843a4098f9f17a4c9beb75624393", + "0x8384f4953395aba4939b24b0669853df78f2fcc01b2145c08d3fc333ee2a7d4adc12f2d81c37d0cc187ef45b5f69f59d", + "0x92beb5a3c14637f84ee7a3c9b4d9b305b10af8963c087b86047e9fa959f41ff362d56eaccfe887bad1ccbedc488abe2e", + "0x8c60e16dbdfed2d1c8cf3f1bb0b0f462489293892f9d2e0221b1691321a771b163fbb599daec4cbd917da75f5f662de7", + "0xa8a6e3041a0c2a12c76f51139b221b03ccd1afaea3b72ba2c3533b797d5f67d8b90d3474b4f6f8e19a77894fb90842e4", + "0x966aaf74560bd4d164ee46c7d393b2c628e307019ca4289dbfb6a9a991608ad80efc1ee6e9847a19382ff8f3004aac8e", + "0xadeaec475d4bfb6075be90cc37d61d45ce14da77f8a9a508b9f829ddf2abf91683aa2fd0372d3025a660c94b0f612685", + "0xb3392bd1ad0c202d4a95912e0e06d8c64d7e2a8818dba8f895abcd0f6932efa9a0bff8a2aac107046d3478782fe42d33", + "0xab38804443da16d32f11c0e364449ed351dd36b7c82b5c7ababcc33a930acefe09fdb5261da04f6dfab29421fb1cc017", + "0xa34e0df9e953841bc44c09e16d69235a26ff390a6d128339ac97aaae5616865f86153d8d7466519dec6c52ad592dd3ad", + "0x99581db106391e6816403b1e9d13747aa05bfbfa5b46696cdfdedd1627b60e1ddb92215d138e007770512e93bc6184f7", + "0xae60c3b1ae3594aa4e3f08eeba3951157921aa6511148c6d32003d42157654d4a3a39efb1bb317135620f09729d134d0", + "0xadab0bc35ca3fefb14729259b16907a34e10ddb6d78a23f28596d3d9b244709651be7719537df33bcf003c0e43bb1a66", + "0xa31b7b2f3411f986b3415870ae42f90bb678a9fc44c942f6613cc4f90f3dbffa4b5fb8bf3abfa4361dd8e396d9a3c5ab", + "0xa69b188a8662eee48fc98201fde6f0d14f6b54db83ab79c2ec2f4b6be809773231704fae2cb281fed8b05107c63f2fda", + "0xb79e1e7a9045af6537981f54dfeed0a1335606301b73eff001880798f01ae9c0fef6e427e171afbb1d0a78135ba912cc", + "0xb1b883fbe379995b3741836a849516a0f21b18f42a34db2c8cba01f86711a2baa5d14910a110f1058e36431dec163cbf", + "0x87bc463b90123cd9e177f2284d72a7f4a1d4151659e1e4e8900bc21986f641af2f9a3386aba56601e6fb64da630b84a1", + "0x97a51bb7d717495f943db162837d3bf700ee0653da9a94b36153443584602156e168fde97d77407d0861641d8d373094", + "0x8b02561709564d0721b5247775dc66c6c09cf68a8ea62fd7dd361905ebcd98bdbb2c554fa272de71c6d22b04d33e6902", + "0xa914b9406e71c09deae875bbd628af3f54de5ccf811365cf229dfc69541d996689d05679eb02d42a0adda02be6e32d2d", + "0x85dcc5f3f77f72cf0818bd04c037cef560f0b0eece3191e06fcbb54228d11f7afbb8d9f8675b404bb39ffd04a3b65bae", + "0xb15bcb96a98bc6cc7b802dc29b79a903223b1712a10a22e776f45c530a4f767665dab1a3c6d1b52157f4b79055d5ac81", + "0x965e353e665b3734042b61951e105c1800718eb3c46759952755321ff5c639327d045c58fe90befa896e96b826910298", + "0x96776a5cd26b69f08a68af0201b2f739cdfb9553b466271063a6c8b8307f2a3f51294ea12c7e8118c0e6b884886e1bd9", + "0xa369453bfbe7ef0b2445231704abba25527b073bf735a968758975fad789c74110a573bc7ec50001368209a0ff385500", + "0x8e54dc4f2a557703b2d8bdb74ff107bbb239034ed363818197b2569c03572c14cff21273e94802159563d50205edd651", + "0xa1c66a1a82c60dcbd139b8ef4de62be423e7641a6b94ce0d0468e60bb1b000d268755946a028d3961d8b4d3722016ad1", + "0xb14b3c26dd9d17d6fd8eeefc7f706c177ebbee9b8d05f9b01174deb37649f77f97ef1a1abc0cd4ca7a13618a4036067d", + "0x8fe8f9754c5ee102bf96ba6b6f29a14fbf83cfe3c5f81b5358ccd4db87fd8c5d88760172373bdfaba7eaac98ab1fa863", + "0xa8c308c15242bd9c7b28e110715315a1f9818ebe03662027a6f1feac13a5dc9bb111d29444d13546d8e441e49960b0a6", + "0x85d87035d74a1f4662f42a8c6d26782daceded0aecee9232b78139b1b50fb764e87cdc5d1ca9d6905735dd9c3dd00dbb", + "0x986c31370f594d4c8a9096c091cb1484c1c0755804819a9462ad1b67937c6b378d97f1e4c27900405b21de2646be70ca", + "0x832b4b427f3347b0073c24f54e17ac16d5a80d04261c1d464f89dce89f42750d69dc8a83ee0481e410f94cf1d108c0ab", + "0xb13e54d91d5d414223caf43a7fad36930300594b8cb3ba97c0c873cfefedc165d05f05deec8d74a0412d5f0932813088", + "0x89c931d595849c8e54f50d550ae4a5d71c4bc74af436965bc32adbfe829a48ab15c856a69070b4a4602e0113131ce4cf", + "0xb03731793db466b74112a1b9dec3344fa952e81bfcc7fb2bef3cb20f566c3b2bf60c16a93f84f77f4c83d8f2a535a2d2", + "0x92e8fc80d49001139363e3201c93db8326c41322db51937ab641ee7f1b2f7d03089e20eab19afd27abc23de039ab4b0f", + "0xb27d95c90dfa91886aa91c9c8c85ce09fc875279028bef49abfeaf44437a0528ade620c8c2b3d712ab594e73c5c032f5", + "0xa42e2598731a792975feb5b24bf00b1e7cba1620922f8c2319dd5878419ce6099663b448299c0623ce400875c48e12a1", + "0xb062840f63b555a254e3bc36e9075d57c816ed2e9cb0e262f9de0f3692456d94eef702489e5b11c9746b949b5e84c06b", + "0x886226745d906664c476615dd41deef6c338ee10380657fdb75cf9ef28b4d9f56e69c8d0ef01e9cb80eeb42f3e5773ba", + "0x854a3649dd5b22def4f246eb0d1f1a206d3dfe42b5e44b5fa963a7c5b8bdaaf7f35b542b3e9cc53187e66a2315ed9f9e", + "0xb5a48cef68a056955ef4c080c85e4044e9f8a562f2beac9fbb5e19f8d618718c86794338c6dae8f94b6f5e9f8e98404b", + "0x8f8bea7304cab80d0009b417c198bfffd166eed6f6db19f28b7616e8b0733cf0a4d54d204361d7f8f179985c8c3a16ad", + "0x8af81f10339e2f75f6b6fe22a641298bf90c8676260abeeef90bcd52f46ef013f5aa4bd9d0b5ec15be61b7c3e0f32350", + "0xb0397c64034598c825f9ef653ff16f680325546695ee6e9c2957d3c87593161a063c5219304ce6a16b7db75f1a2c5f7f", + "0x8d2e7677ab6fbe2b0f5ab6dc356190bb3ecd7fc468c698d512a6c69f22ea97b71fa961c88635897a5b539ea51b70b4a0", + "0xb4e91a693cca1007fdaeb7e679c6837bb8eae0bf61aae447560ca6eb5ba918cbd9952b41769657978413106b359e169d", + "0xa8331a907ba7d95a5e4090a7680d1bce3cd803db49fb84a48996e96514701de1602c4eeb4b5e0b1c2a106c4f678a72a7", + "0xb54dd28a97a5f934a34c2817af91a41e57f88d7eb5fb584d3b6692f2d1c4b2a4e273c4de5fa33a0fd1fa02c9d7cd1fb1", + "0xb8b780e0f6059ea27aec9f3693ac9adf0b93f75fe7fac5230deee1e7471df0bce9b5b2f260a6a0a346afa723860471b2", + "0x980e9847ec83d61442a86cf8c7464b357694dbe67aa5de3a8c88ccd1a038256453101366dcdfe11a565065d78ce89807", + "0x9350a17e397bdc3d2bfbb84ddc79a48bdc6ef5c3d8c1ea39251e762fddf9962b69cdd42c563d03f64615b72c9dab07bd", + "0xa34d24b69089cb5ffc5f06eb2acfeba13c96a1096d1af9620aea28753baf3d0aad0bcb30037ef3a5ac36b178816e878d", + "0xa7c8b9108fceb4e0377eed48af9846530114df986cbdd35f6d54026104fe6bfb3b58e57fa2b3a750225528f8dcb8bb9b", + "0xb0f71f6a04cc7119db96033f89530853d58a445565de2efd269b1e3956397c35a49c328102325b780fa5d0cf5adc2a4a", + "0x92be082f04722fdf3abca7ebfd162b7062939c3410ec204d5478dc8de2bae2b25e2654441d29fe2c350895512d333ab0", + "0x95e7afbcac22dc2d04c5635d7a8c6293f6ce29bc6c932850d24ab5216b449251bdf7aaea838ef40e0e4eee1de8f63bfe", + "0xae0a877b488865f21194470677e12ea7e357c5d63f6bc454f608e33df9a3b20e9aaea5b6aa42e8999779b8b445831c39", + "0x98df977479667e23b897b91f2db8f4cdee7ece7fc3ecf8a07d752efae090d8bd34d781353ec1394550d8a207bffe582c", + "0xaaa0f1bfece62a63f3bc76862b8789e2593b4bb40b3c413956e9e5c4eec604e39d722cbe1db210396eca7c2293489099", + "0xb362703d2b72909d06407d139531fc144e68ba94e55643cc3cbb9ed24145223aff460b1627b41eb9a3b709978aee5a58", + "0xb020025128804bd642fdb1d2b70b07d181e5ba30a5ee16f6bd00d7e2d0c6af782e454cec107304823be61647e65221fd", + "0xa409894c0030081a2c7f8fba27bd0ac53997a31b35a33498d78bbcfa0b7ec0a89b9efa99dc1b8770ba889060f39d56e2", + "0x862f9eace3f54288749ca8402c22ddd7829f0454d17ff4891727c86eace899cbf72d302065f5f581169f00186c23b4dc", + "0x91432f2a823c3ce95bdeb5854e8dc7faea5031fd65c82dc69e4adbc5ead2e5a5b58a9cd1428d3f526cf94a217f37d7de", + "0x9543a9038fdecaffecc4d3023fd67f7976dcdbc7014e82edb4573479b1789b4c610c3964643e031f69ac7a3e3dfbe241", + "0xb4f31d580987f47c550eabd2d276678a294a612ac26806a06634b8812a571391151d84c29b6b82218cd84dac85bdcc88", + "0x8d922ae4eecb45ebc23eb1a8404aa1524b281d0f0ceda58ea93a0cfd4184efb894c047f0a46e2d007704f5506544907e", + "0x98973979672d1d52e561cae7331b730a577c422258c22720edc344aae35ce071be1b017816d58bb29b9cf5c433fd64b4", + "0xa46be974ea72c5e5bd16de591bf27087d97b9030fb4a74743bde5e480052a0de58bd962dbbf0e0fbb0559566c3d9780b", + "0xb2b4464973322d865207949afa4dadbd888c9b0230737561c3b76a1953a85ea9439fbb1db9d0d42083c62419db512450", + "0xae811a9eac5f4ee6cb3a4dab456a3e5d95cb1ab303c19e76fc4b36ef6b4c83ec0b2803ab8680ad1663bdec0ea2f19aaf", + "0x95a426f3d2ae6c6069f888010bb20c392bcbb65d0986125e0f0066d4206f4f443f70dcba8a789da042b57a36980e75be", + "0xa9ec01a5777d10275153ba7441f2e27ba3d6f1a875f204469220ad999bb8a0372369278bf5a11640ac0709771b814a31", + "0xadf1091e24bdf10d848f1a0920eabca0a2087220fa0c3f8e5b4c72ca0424ff3e0c77ad4c259c12c3cd1c0eb0cf32c67f", + "0xb9a57eb8642729541088164b9974775934d7a4c56a3a3ff2a190d549b294fa87002810f31251170b0407c7e9695cfba2", + "0x8625501e5c56948812b72b3495747412e03ede90096be089cb8040069e49cddfe697766ee72505bf715802fc77c08fa3", + "0x8a745aeeddd1be100474d96aedc737208ef19a93a8ad72c10bdc0218073fde6209510048eb46e271553b04d8e6529f46", + "0x8b8d9ac3b91ac0333094c85e81fe2b8cd5c2207073a33f66bb1939e8f1c84ef064a8b2ee984a9f450f0a6e54bb56ccc4", + "0x8cace31444da99fa5dadc7c46f689fa427949d8c089af3d90c604fbdbe0dab052392fbad4b5aeab707e4caa9e739f366", + "0x8750c8bd1f1abe5acfb29ecab0923008cb4455ae8a1db01bf3317df05e1e02f9df3c74e879d9c57b8f59877591939ab4", + "0x8904a39ad86cb42c06692d4801b3718bb63a07a2dc5ef13de16f668b08968db34966457ff2e4cb770dc61a216f4abc5e", + "0x967d1750b0db53e977bb9ba65aa820d7970f8c75d5355cf12a3f4c509dee7e9b6c0f7a828474b167c25b15d74f0e9cb3", + "0xb37297bb6c2d9175e0a7654c5bc6d248f47f7183c3b10375f07e21e9f3e66f6581caebfcf468dc0f8c04132a2a0ede55", + "0x803862e6fbca945cb6c0ba202257df5c7e1e1fadd78b842970206575f70c9757d4a54e9b1a0a269dd37c4f830a40d2d6", + "0xa7a27f2fc7a1e6d276522177f0ae6630dcf5205d08c19319c315bacb165b687d125832d97ed689960885bb7cf42fdf36", + "0x87fbc08506fdf980cdd534d4ecc4dcfbd381f3937dafa09db402e07a67e1cde579e680d3f77865b5669f35fc00901530", + "0x8fab8bd57f76d187f1cc22e40b51736b1b0234e70813ca02559ded9c7835cb3dc71a24c8f679081510c32f330d6ca45b", + "0x8fb917b7dd71e1728bbf32fcb55343890aa6fc890740f41f42e9620b5bc3ef8b1ec67d9c047e4a9de0863a5eec18e5f9", + "0xb7429e758850bb7f69db000d49763df43d18af11460ee0f158b741dd6b7575527c5c8068cf54f7f78098f9ddb92a82db", + "0x8bd3c73c1b6f88ed2696d53d2a0617f74bfada964d2eef2afb5e1cf00bfb646f552643c55d5453cc622c9ecfb23ad492", + "0x8e025e91b30b0f328cd6b79df9603698f1715eb6209e64ef8705cdde5ee1c4ec737a61d9b8a4e72e83b2157c621e1590", + "0xac0b91bbb5ce5bbc8e8d6c0d9d4e51b3960169c608b6220a352aeb13072133aa9d934b4e69d7c5c39fde68d467fa8069", + "0x88255d08bde3b967dfb1dd338dfbdec12a2079851aa796be070a1d70204048c34f2739b7461840136b03429a8b83b1f8", + "0x97a83477e765f3f17eef0d3243ba9bbdcc50fc581f904e92a853a076adeba917279fc0e01aeca42de1aed8af9579bca1", + "0xb0d9f1afb807e0e6f839632393edef25731ab2141cfa1cd965e986940a4916c8788733a39def0cf67afedc516dcc6ce4", + "0xb563e9ed9ba2134011d7bea6314af5d71f63caa1bcbf878c26d7162dfc76343c38308955215252962fd0c9c87200f1f7", + "0x838d4e97bd67822c22cda558f0d35f971a0ab7debd3da3f15c27f7d4b3301b2c17b60cdbca0da8e067f23fc97d136ae7", + "0xa7bccea326cccbbc4773de402fdf2cbc21a028197be98cebf6e691a7679fc948e6bc4981a14fbf493a254eedc444dd7a", + "0x8b2687732f7aebb153bd6030dfca0b6d257b8f2945eb8221ffd36ede50d463172cfc4bb17dc30bd21d8572aae2540d6f", + "0xa4a3e87ec5984c3a755cb39fb04472583a0d2c771552b0701788f03b4618c87690a13a905420358701777e8b5ff2d190", + "0x904c4dee5dfff129de3fb8cd0a6e0576c18ed3d77f49b8f0601997013cdd4ecadb86690389555ebe8a436d8084024f2f", + "0xad1d9c7a6236b22474fe8d45fde2e1f072101b5cb7018ac73c0541c0f9ebec4d5c4469e0570cc188cb5f5ba1d8958be1", + "0x87fc8ca6f737cfdedee973f1586b94d971564a1fada0e4d65894222edcca6552764f1ca0b02782066f19f871ba5842d8", + "0x851829a8b39eb6b91523548ad600bb876408cabed98d30958056367c686bdedbc876e1903b8af8ffa3d3618e3580e3db", + "0xb99270859bfe7d8a4c1a22e2d88a644dfd2f100c54070ffd9c4e1044140fc0d21d62c61214a2b81a9cfadf944327ef8e", + "0xb89a2ddc91c59dc2ed9b8d11e4838002e731b0bcc576e0e18106238cd3282264b14cebebd8a64f004521cbdc690c4182", + "0x8be96bb11a025d265b7b3ff3151e9e227a8416e6118595ac29bf836ef751504eaa3a9cc05bbdcdeabde11f2dc3d20c3d", + "0x87185ed410be571fb532e32d0ff4ef68e98ba2d3d5fbe92180cf1fe8ddfbcc89fd1e03694a9fde1a12ab905db89323d6", + "0x97ef023f71850ddb282f244b3f39579eab69ce5bf3fe5dd2159329b7def4982cdbdb4e71476471acfea0f7ba5a7fd061", + "0x9944324d804fd3978e7e137e6e65348d7473ea23c591136d55779b5a31f45f9e4d866d8a18c76a3a0e8cf2ee61cdd041", + "0xb9930c9aff260105d4d15fb749aa33436f6fb52cf9d50e39dca19d9cc7938d752773f06756af86369e1f5fd5aa71d5ea", + "0xa85ac6bc027ade2a9bbbab2b231241cbbe56e562fe621ea19208a8ea36e1baced89ec9ab8e2f83b602539e5c053f5764", + "0x9917d40d37549caae646848e18ffcb49f5c6c4e396ebe7e74129a41b0cfe2726b4dad34d51f4bc706063e654da898824", + "0xa25f8a4d8ab34724a732dacd2b316c80a6544d4b8c1f45115c4f55c3efae6129b83623ffb31da80e2601f70ca51ead16", + "0x932b54b2bd26670936843a92346d231f2f3e3659542f4d4def73fb36ac0350733853130a5e5e9d8e386d34f817f5a91d", + "0x871bf29d7263bce62a02690681d4e1c3c2f9c2751de4e35810ece13c9480eab93b80a00230ef0ffb858a829ee6bd96e2", + "0xab9643bb1c32dc2e8c05ef49bbde9937072af214c19c3932be137b7b75268edbcdd81d1370089be44462b8032bba3c57", + "0xb67d510c460a2f14b7cebaf9a15642a14b2542c13ebb1d1690596447ddfce6a86327ffa377c28891f6bbd8febc2c17ca", + "0x93a5ad5019a8e680bd053a524e0ffaf8cb18adfcdb22ccb6cbed67012316bcebed65294bcc0cf4f4e2915dbf19ff0948", + "0xac7a7fc1140b1197f2aa424b053e8ceaf48abf41819efaff87a2e63cd6e962c278942c2b97742ffbbedc5cd426a8df50", + "0xaf0115d9c2f887ff97ee15a1116ab06af1920f2f42700b75cc010d4c8038eea941c9bcc8e7cf4a41036813143ab3e8eb", + "0x90c768d880b6ac17ed7ff9bcf76cbd5c1c4879247a757d8cc8b31c4c7bb0ec763d746e6e06e361afa8ee158e36ccaffc", + "0xb3f10561432a97c95d02c1a6f317bb1ab5b98cc88cf5d56e1492ca84eb2ae1db92e9e31fa454de562e719b71442e69f0", + "0x8d94729b5fb0afc196064991f9b3c8e04c0858199aa951f49421ab890079804179fe00707978f15637b8d16246794001", + "0x968515d07a0f0eb52adf439d8f70ecd1f6655072abbeea45431dad26c8937f4aaeda552a22a420598d2136f576a966d9", + "0x91f50e6f292e2bbbe226b221cedb9db36bcd459bfd74fd6356b0620766d96869266315e8503435af1719d8ff765467ea", + "0x968b328d79e183ec560e8f0de113298755cb23a893a631406defdd26ecd52e4b6f28934ad2e219382837fbb8f87f4360", + "0x94c126a9035059d2d817c4fb16cb13fe6047c356fc495aeb369acb1c45e89306554631f50d313707e82624b6107d2fa0", + "0x90ee85deb494043a1cb280d687e3f55345085e576484308755df1bdb6f734e7dd25fd2489cea746be5d2c6384e7986e0", + "0x92a4f64d98e7e633271bdafb1eb88474013b5ed2c65137c243729df0d79ccdc6b210118ed3587ad00d3f0f757400e47b", + "0xaf31031fcc867a53760216cc1f467901aeaa3b28438fb3ec90d6a1c8a46590062c40fac939bc3c7e7a7deff8f83c262f", + "0x94306afe09f20d5de9ea26f37f5fc8df1e29b3a6963caa94df031efd428545493d53e0d8d7af12ee525e2e21cba23324", + "0xab6285371b981d5443ecea699af9da916f9320b3ed0a11c15503f3b10eada3ff5dc95d24a54f5aaab97d3312de5b985b", + "0x8e9735364ae128f790dfcbedcfe0e11b991602dce0c90ab3cfd4aac74526c30a798fcb51a8ebcc5528d88c724e9c2b02", + "0x89a3c12bcc68129b14fdc1d9b5db8d26d11c6c9467f5bff7c40abb8ec23b193746697421ea4400d5ebe53bb3fbfe59d9", + "0x8368e44144947f9ecfa5d126f4a57bb1d3728fe3c5d3bf83310174d638a10cea02ae79fca91d5489ecc9fa679feab49c", + "0xa0474ff532e1a6a3dc8f16ae27e77d6ab72b62535ba0d3ed09da5c692c6fd34424141cd68470922e1e147fb7f7479d5e", + "0xb9ae0e47fa8d999135f78c733cdcad786b96087a340f86e4cc2bdf019b07fd4a76f9b4b041eb397f61bda20c31d27838", + "0xa7262ca18a7179924d28161d64e6b6cec5da35b7eaf695642dbc127a7bf4a52bffad82b8d3fcd010b308dd72eb567f26", + "0xa23ecfac8a3f978f9ca8810458973f528846de6bb92fa6422b9547d55d29d7db7d8bdc5181e9ee2257a458466f714449", + "0xb04c32403400f82677d831314956acd3cb507520ff14d856cf8ec4fab37a4428a5d24ecfabfd2c6086e4ea6d26b005e5", + "0x9669b2725cd5965305c6ea48331e693086f4c1c4ca7dec26bc6290e9a8e70f9f0bedca6e36985c39ea35b412abc7f4b5", + "0xa6f68cecace45317a758658463c5fc1f005283d8c3d3de9364e7dea453007d8d4bc849a21205d61ef81019e0d25858fa", + "0x8ee19ccc1c83b2c4d7c7b712bb370c129201bfb340c5b49d991207c995f870de2d0efaa88e05bc9eac567c4c36e20175", + "0x8a530ece1992d1de92c4e845e71a1ab24e53a8a0679aa5bdeefc60fd890ca3cee2121f66c6f4b29c437440e7644e65d0", + "0x924338d7f356da9b8477b3aeaad6f754a8d8f6a791d70c3ff23c2a6d4488efde9b9fc351319f3ea1f545dd11cd23ab76", + "0x8eb76f86e057cfe9f655ba29bac89cc97db07f0487c86e7b41555b5549897bd3d042cd2ede35e312cbea357df622c6c2", + "0xa2c0da965489d15ced574f5e27cd4781a1dce8fa4f17762a25fef1320096b9eddd92a007d58a194ef57def3aaf4e925b", + "0xa3fc89753e8896d796859c9e5a00d184be7d37c4d5741ae8a60cae9a7a30c5d840325d6479701e1f61e37065fce81870", + "0x8b2f90cdb3add567b94f4c7fc89a8a57a0f06877639c33df2697f7c39e52c1869aadc98a2f8b85a58fbb02bb1bc1a441", + "0xaeb2c22d9186725ea40d3a4bf551482bddeef42c0ad33801e35361d3695769429449c2a13955cccab55778d3ff29b664", + "0x80bce007abd8ebe2238d465a312c2d125d1a695184b93108d728075595c7716786f9188e90ae37fea89009d087e71b07", + "0x86f5df2b83383c737bb6db4e435f496ebfd56b51300612c402bea9ac2f439ee7e98cbc2655d31646472ef983aa6ccbbe", + "0x880e8a19af5ad76f14cdf94396b8dacf061e02eeaba02d4c29ddf0d07c6d2a737c987d69ea2eee52f0db5a4dec318932", + "0x8b82368968f9b5bb175c95862ad403beee68d199a20d5dd618395daf11ff0c2e1fbf3a31c23d3e582556276b44e70b99", + "0x94a062abbdc5ba740077fb9de722ad2ccf3f6ffc8b4a9dfbb0bf2ff789bd529e7b9d8da520d0342af91808fc00431638", + "0x890b4ee1e9837a4c215616819dadbd3c6ed7586ad391498012a54d735c6df0b72c2dc3969d1b24cf6fe822f37f9c10e7", + "0xa7dfcf43c9c22fd22f47db490e8f0b8f42597a5b4ae3e7bc4a9b12252b32f89412a2aed946eec19b133cee89b4a70322", + "0xacbd9e85b9d9c3b068220f893d7b6368984f6cdb1cd06a784cc9571f0c632775ef890dbd51371e8701894cbf667d04f2", + "0xa9b1f84f053ef6f41c59b1758836a82d53932cc4b8ee9c2cafe705150e1c717e3f5c15fc21a2532c071e9dd9bccb4dac", + "0xb2c63345748a28d04680e3e451d1f7d430bc8ff2031b6bd4237a1f55dfadaec20d1854ac158cd6a1466dae525c1b9b06", + "0xa23e7b2e5b8f3e3b0e350e1a461708be9c1434d49fe2e51473e2e360bb0be140a96f8ddac99e3b804557cc25d3e44776", + "0xa4c4729a38f5f32f155ca4d1994b61802ee418b276486e2dcd681fec13316f3b6d4a8e76eb9f48e2df0339543b11326c", + "0x93be67dbdec2655edfe40dcdcc0a7e761b7259a9d909ebb12fd7c9a5d4efa10de065d2eb049660ed01ede2f26388d43e", + "0x932480849f97e32fb14d4a69af4073c377e949af7293951b3ca371a306d9e2096157f51c8e5036a44eb73c7c842c5aa9", + "0x8b5e79ddafd675ff88d8f65176321a08183429d42d7fc1e7cc3cfccdef0dc5824ee40f279a05edbf4d50418a4cab2126", + "0x962bd6fcf7c7f2a9c569d584658a735bd16440de2ffae236c68ccbf2ddc5e13836efb163690062537d52f7d8bbb24222", + "0xaf80793655c0b3ec3029673c50a7f212d297f9f80d7d1c7cb1409d292f3bd7dbb8b24581017d9f3964e3432f46e79ca1", + "0x94c8cf3c737c102e9e91216752c82b17e4e42074e08ce44e701c2f8ac7c08902b911cabf38c4c5bd41400eeb1fc97acb", + "0x8708ea7af8c86b2a1964edf64a9e9c56c7febffa742c3ff2e3088a61d3ccd63e135811212878ba7ad8a819e1859f4e95", + "0xab8e726d468417c168c892c10c7e2297e50c67e4283e5b48c3f3b014981ec482e211374f779faa0c1ead906f5dd4114d", + "0xa93911e672aa3d8dd686280cf062f128bd8eefc058fbaea52cc0a9bb255fda84e65ea546f662fc75fee4c5b24bdc61fd", + "0x8aae6d9289d8adf0f81e7990cc79cb704d0a975f03b9ec02be66089d62954fd9a8b005c5ba8179cede366d25ccf40869", + "0x91e44ca55de8ad3ab42816504813cd9ed9c6d64abf6373e8891f909cb49c8a951ee823cd1f947058d542f0bf6290a11c", + "0xa377f97e075b66e740b8476f085d50ce8ac21f206802384e2e072f6b9700a5f9cf0e6f2236307775c0e0d6ae8459d864", + "0x953c08d9f2a9d6ccb22cab906efda69ec1c228aa5c2ab93822b6f71c007fa3bced68c6a70ac605c6145e4af770b60de0", + "0x86d8dcf5a9ba81cf6a3149b2fff96e36639767e9de461bbd3ccc870634e8db331b98a888d7e8d3d70b6ed241d8ce54da", + "0x88db73952866ec07c49b484c6b18de70d439e67d971c1b436684d489253cb96d793cc4d9a4362b51dffce837dbd03bf6", + "0x970b7aa9070334b0649bea1f0b4e53fded64665f87e055e3527e0e567cb57a0e97d369aa16a005155cb69000073d7695", + "0x928c8aaf72b3f51e38c866ab457f75cbd7131b676817a3c6d522fb8f876b01a9ab3a84238eaadaa0a095ccd6c1ac060b", + "0x9561e78d16061b5361ba0be11387c3f6029415f83bcc8477b8729e88c55f4bfe74b59c1b24bec0eebd9779cdfcfbc96c", + "0xaef133788d1e04ac64f573f3ffab473209dfdcaf2c675fddcff83724d17b91d6340830409b391a94405d6ade005cd01b", + "0xb8ad4ab0a1ad6383e4cb12d479cde732f202687ebf886184507371ac277446b3bd1648c49c89343920e5d57fa6b255c3", + "0xa8d00257e331f342b79b3d25b74d300b070326b358f690edbaad5e325293d8b55078605a43ecd9dfd27206013db4c286", + "0xaa71abee2720052cce7a5a0c3968e64c2c540cc852dfe08b742fefe005dbfd10397f66386744c9bfbbaa40779c2ae190", + "0x80c6680857b88afd3ae8801677720100f0fdcb9e49c82f74b8ca5a27aef34e6df03462cf9ef5f80169c72da15be567b2", + "0x8c2f2865e132869fca5232ba5e6844ac0540a53a66971ad54ff91f186e011e5412450df9099fbe0b987be443867dfdb6", + "0x89cf681e0506baaa528043a15ab3bae09b8817853e889f0b3de138992e52612fa3e50d54b3686cbca6428a7644f58208", + "0x89ddf69b72b9ddf7d535f742bd3c942000061a5a54987f2ccc7a09e035be978cb32f653df9568526c5857a5df4321f59", + "0x9908a3288b9a9972c3f3f0d5923d9840756b74f31ae0b24ef2188465efaa5247b1ed13b774991bbe4c065c7e71b487ea", + "0x9454ea9a664390fb1ba79fbb5c0cc765d8ccd32a02d946a14353290fa2a1ba911605ff2e302c340e9ed6fbe8543ee6a9", + "0xaa4f4d9ef843ca3ba334d73af634a0ee800b3393f8f7701cd152274f4296eb79d63869d452b5e83976eca246203d6f03", + "0x8fce1e2e59dfc4fb46f3741d27772579fbf2f48acf1a38c49b0e5dae7d35f2624af3a46a48b89bd835b7d452ab0cec80", + "0x810ec0e58504ed556e788e23067296a8e4b4ef31257d508f05e5245bfe6d2c2f658fca8c81538c6c9ea6ed05a8f249a9", + "0xb6667bad0a7d49cd2dc60af85e373fdaac2af0d34fdee51a9fbc1fe8b77470c162a04da38228fe68b7d5247d43026734", + "0x8982971d57bdf35e0f34e867fecbe0c140d94101484ef4ea01b796633beba184f980c3ced28b24ff42de1dc504dbc854", + "0x86d8d1f3edef9e61058a58d966169a05f07fed0d93bd4f4a7cfca5a872b2aad0d1a78f8ec7784828e5813c8da577469c", + "0xb491624c3d5e517c9019258db6284d7533778e44b1a0060dec5f655a7b79057141079115f5cb1d8d97a90af33cd7563e", + "0x856e1cd4f9ab7cf323f5988bb5d272857d2fa90527f800362569a39defd93e37be2a60c11f498c482654f55560356f7c", + "0xa08884d0e642c479fc8e5a9837d1babbe63f3165c02a57b19d0547fa1fdc18ee382ea82a86cfd3135dec8f2aff793f53", + "0xb1a4de5ea703fa5ac8a70ec515bc65203a9415f6da109b67fa32843a39d7fa6232c9c13920d78c0f16e99fa5f6a27e83", + "0x931a2ee3220ac7888157c426d1b33b8a56f8879fecf1461af4cd6c85f94e193bd6ae6f8dc3946fc689e42bee213f0027", + "0xa844a78e65ea6f75bb55a5db1e78b88896caa1d54b624f218eeb302397dc98a084a2ff4b964acd0650667160928ceea4", + "0xb9c214280a15b423654a36b11646c928fb42ed2a692aedc01441c67522760df29c6ae7bbcb9237938a823188ad4d83f4", + "0xa19575f9bbdfccf970bb3754818e49c709d1bf0af015541182fc7203f7aab51cad31544072d52c0234a3b649d03d9a52", + "0x8cd1127b7485ea7f349e2c89a4b78fab3e5fabe5a95ff0cee10a3f4fd48940e431ca5e526f6342f7da93e32e8eaa1448", + "0x9906abc725e445092dd7dd0aef90f989e3c78aee96f3c0a67ccb62fb2a51363c71d783490fa5fdda0ff9ea69f5b9233b", + "0x8996df92e014c226e5ac724075c19d19a9204b2e086ed5e75a9bfa1f8391c4c77fd5c0b85a29f28b302a4af29d38735e", + "0x90225c9490b39d151a80a9f4d9a7f2595961c44779a54d5e195ec95096f77e30157c6c629cb1c36d995f6c3ee129ad20", + "0x85925b1dfe3884ae3a0e993b67b6c49685deeab6cf0d9997309961b7f727cd6133797bf04d21ef7b620d1d6392450b64", + "0x88a6c518e577a820d83f87e9d5f236f43d262756b1bae0fde72af241fcc192417ca9724428d17a3f9dd813712a772cac", + "0x8f501fd5634fddd00a8411c0e9c6947bab0dded26339821bc3543a64c519d9575c3129f6619c6079d5e95237c11cfeac", + "0xaf2b42945d7c81bc422a1bcdeb80027a1a243f89c65c173294d6c92e4cb3cd650740cac17408e7ba1a34d37d865b9bc5", + "0xabfa5e76f1112602ddf152aceaa9f588beda3aba1115d0823d6a6b074d28389fd4c8178e8a845262200b9af024a33a88", + "0x9732a0e3efcef5ad4d43811bcaffaa1418c791d3fd6ca4489d6cbbb7c44b55156d218f0fe86f2ec96ac306fefab2e694", + "0x8837a6c4e60839ffb0b59e94b37d81bf1ea392d44cc81717c1c9104468d31fb5fc3c35b1efd9991b5e7e9819c66a7221", + "0xb6545fd0b455748ac3482e0ead3b0157563cea7bf6bdd5ae2af7afe1ade921e5ba80466885ba73a89657a735c92658a2", + "0xb72fc49fd3be541bc26cb968ba4eb3078ce7c71fe0ac06340f7ac25c0befb86af03c4cf8f31c857f9e5d946401e86bb0", + "0x929f424548e29c3b47fbbd59ec00d17b00ee1c4f6b966c1fa7e0f8528d52078278f2852da976b8931fe813b0c3b71ac9", + "0xb37861ba981001aa6192cff06c13f041410aa60f965ea03dd48068b4295d61d2fa276c3f477f985f50189e33308c1876", + "0xa73c7cdffd646cffb255d2519d8e08dd8d9a9eca0610211177e259230b8f8c7ec8727015853197a0f11eec8b59d4f2bc", + "0x8da1260ce51220ad107c3127e871715bd738639cd90824d1c9f5b6181304f363b8bdbdb42c21e4e360cbdee496b573a9", + "0xaac6bbc35bce8b54820ef8d7219a4092c49aa5d4fbb187968cb91ac04bc44fa119766f8c630a727ba184cad19278d9c8", + "0xb964de0bd31847ada13dc3f6e1bdc679f421e262c03353e39f0ef1df720ba05e6d806dba15b6e10df559519ca125fc39", + "0xa62e4336b61f85eaa415f57e21cebc7d54c68f6febab02de76bc04a69658ab1d2f7cf0104da79448e32e2b7c92b684c8", + "0x897c6ca595bb2884b643ce8e69078431979d7e6e1b2dcc6effaf5a62fc906db6466f85020bf5930597adbd99e2ff90d3", + "0x932956e0ba09f6499f1ed231732a444b0adf17080237d9345d06d4262fe8a5fb0f704c919513ed42473751069c57dafe", + "0xa24b9cb4ea9c2203a95b0056bb95342c4fa0d91bcc25595fea0161e7d6f45595f7ea171e0ac1bbde13a6d8ca6ad10bf5", + "0xa7714728bc3318f6ac005e350de94f59495ef3972b328c673c5e608fa9059be3277b48f03a5a9634c3d03397af7d089f", + "0xb98732aec7a0a9a7998ba51e2b76e5232379482d0047f4876cd39918119776ae2683590f7fe5e44d12b3b3efdd916e8a", + "0x87700c3fe20cad8fa3041976c87ee761941d323f2d64a9818f20fcdf0259f796a11e55cdee31446bd19307cbe8becf09", + "0xa37cd03fd348694b2ea5cf081696d12dc4ae108da8d48695bf74c921b90612d18c1aa71b1071bbcc02829e05ba1363ab", + "0x830e4e7ac24fb3f64294e5c64563ab5708ebf0e133540b35b985390d68c420a6d680d779fc87245bb1f5c58e59c5ff39", + "0xb5922242a82565753dd2c1438008462d531f820af1b565756d4d27a30e3406ecc503b1e5b628012ea0329fd75561dd7b", + "0x91068438d2bfbb0666824d3cc2be488f2eaf3a8a9f21805838f9f2d582ca6bcb103b2f0f313b57bc86f87704aad7c7d1", + "0xa9a2133fe55e99114e526904f5fb3e2e141f31963562887a9fe6a262496dc405c756bf6dfdd6acb8853ef5a0a5146037", + "0x8e48e79f9eb1f8757b4c4afc4e3d6da4d368bb25b4d54e3a1f34f3af13d8037b0d465b29894f68272b79cc60fa676071", + "0x9378b90495b0e6468dce3102a97e9965a5d21fa4a6494d401888b8777bd58616b99d49177f2eb2796476ae84d20b67b7", + "0xb0aea247d7d7c7767519b87dd66f56c306d9eec88b0db8060bb97370099892957e2c950fa2e05f24f8ad097889cab087", + "0x89d0d48769ad81699d5b83f26ac49a29c3e835caee03469e93c11e5f4b8470eb02b52290bb2c37f06afb0746630803fb", + "0x94de42d8554583b24317d9ea283dad5849e2f124f659d0afa11414898ffdc4347a9c4ebe783dded21679337b58b67f4d", + "0xb76c3047eaecaf4a4e6fb6176c7f4a1d393fec3a360f4c711d6293a993aee39d5aea654fc6429c2e4d4955b12fea5c8e", + "0xa307fcef0915e3e3a27b94ddb9561e5d210a091714b73afbc0b3fa5e8140e8c3818f4914903975e8f78d0492d7784c25", + "0x95079c4a5008fb6ae0d653c00ad901a108df0b8c442a68492740eacd15048106b7c4cb5ee88bc6b1dc089987935bdba1", + "0xb65a354aa8e92d6ca2e11f4ed3c1ed011852bab8f0e5b8157a10c26db2748be688512423c11d582b3dc1da57b9d6a826", + "0xa32c2fc62c38eb19dea24b545d2537dfe596423f8ae530e562ba7eaac34139fb443d88f18f39d65d36a65ed1277973ef", + "0x81b83b37927e9a6a7c34cfe587dc9cfbd560db3ac57a8a88161fe4ae9a7c66843d32f6f568c927e2ff8f21d8b4299475", + "0x8b6993ef73c2021842060ec0424464412242aeb711da2c43d3985f9d15e4d936eb7a1b5098bfe892fcd3b6ba8bf42369", + "0x965535b46a18f94a1203fafa4dee5963742511ab77e98e471e03376847850357d543dc6ef2dbb765cbc1f03f66ebbc14", + "0xa9386ef496b4f96bd591847baf6dcf8520f7cb5aaf1713025ee894b40b10f243aef06c553376663488377fb8b1b0a022", + "0xa6bae4486fc16ec1f12817f2d47871c8bb61f5f1a2db5f828c6e2c06bca64b1ff7cf4c059a10d6bc2f561fc3a12aa38d", + "0xa2b6cda6a75fac16f324935cc1820bfdf013ae02c209802befecac0288d90263a7f84762dfb7c9aa1351415c03288714", + "0xaac87216619a8c50b5d54432ed5681b1cbb2c7084f33e9a91889bfbb94fd18c8071b79ebdb403ad81fea495bc1e37dcc", + "0x8bb3b3a7ceca82e4268ab52c00322d5d0822427e43c1d8b88b2f43c3dfae7100f6a29832d16454e093579cbaa1074062", + "0xa2363b4506b1464391a194412a73d47d0cd4ea1ffa541cf8b936c97a46bfeaebd1fec409c6aa2109d277bfae0ea7f0fb", + "0xb56911be2bbf1e564715191a526c2ae73bb6e85c45e3dc22bd9dd87cde620de93875c48b11e02ea66eebb68f533f353e", + "0x81609eacf4b2e78a9d7f469e0882ad24c86ad98dd18f466d321aa32a762171cfc334dcc049962ef5e63248ef48244229", + "0x866b26d3dbab7837edec84217c85653c6abaa617e0ba2657d67757fd1c7dfc0c7f83f6198fb85a49b402108d6fedeea6", + "0x9771f5796d5d47d22100c7ff7d191795677d53796f4a1e1aada949b372ec12decb6c49e28f2662e729d44f0e09eac063", + "0xa9fdfbfbe114c7f93806b988c50f8ae4e13a4d433f2e40c72b81d0ed7fe879db5e89216a0b0c8392a6d9d54f57760ecc", + "0x965336222244229fac41336464c36dac8700d5289c0aba78016db76e436289a0797af8c96d52583618f8c6dbe7b3562d", + "0x99719ac482b72d54fa515395847e9a65b733da84f7d10a0be82f34afc20159d64411aacca15041726251fd90ae06a9f4", + "0xab96b7ac88842ad0ab61f7550b7b4697d6a3b651cfa3c10ad404e7505c742e2c1364bbfd08ad0039ca3b81ffa9d6a6e5", + "0xae96088cf12f76140888582f6f6404b6f2666c048950166e37bbe46c1398fec343fcacd3e8f332f7afa222ca13fbdb87", + "0xb5b5c1ad493b2e72ce8ba698351f596cb85841f7f7055e31325cadbb4fec3e8045b335643190d6b97c3049d10551764c", + "0x85f066c7ffd2bfc4519f42f0778ce0e46195466768322a22673a073ebb66cd77c7b8b3a14157845cdb369d3f40911421", + "0x99f4f10397cb7ff47a2d9d2f29021d1ca96f0da01f8afd76f72457cba6e6376f925fcee28ce77475b90c9466042ac414", + "0x85116606b18f6e5404e9176570bf6d7a9d86116e5a29721a1b20d6b28a733886e2085a7563cbff45d1f11bf3d552ea12", + "0xa17d81b236fb138ed820d335dde2640ac3a44cccb5f11fc6bea5fe3132c4a9247b874e75fba55bdf8093f0f56310a999", + "0x8a16a5cfe10c5dbecb4fd9f4b0c370162071f88198e016111937199b87d006d1b24f3f412d853d7c6541e1c68076b70a", + "0x8cb83fd2b1afbad7c454430fb9dbf6530230b782c7dfb01443c2c16563e833c5b230f4c4268dc37a55a681a5f0bef420", + "0xb8851a8dd6a3a17619e7c84b18f29ac9680b456c03e8c8489376e6de9a22ea75d1730787ca5d269af44eeae47f87bc24", + "0xa8f990c9290456e849ae4cc0c320580fcfd50263af8945d01b00baddf801aa0a7bef2ac119d4d1b4be6290615c781656", + "0xb0fa1c28c8c67ff87427691047c362aa35de0be9b0121d83b116b23170ad2b712a0b5bdf6a57a25c59201ba165d5f0d6", + "0xafcd2f5e66a277cef775b636abb598ee9d7e3bc1b48b521da787dc561cea8d7ad963f593c3ac6f23a66a27c15876b775", + "0x92888863568ef01b40d51f467e8364cb1b09808238644bbee5ed118b392475e90c6a1e03a0ef826dff5ada8d10be716c", + "0xa8ddad388f2dc94294371d0ebbce02016c463a65bcf3a5366419a7a910d3d24748fb5716ddd81cbab44a2362ee3c077e", + "0x8b8ef4f818ca3de1683064ea7e968edc8d9fe2675b8bb2ae637a5784a20cd909d18eed45140189eb9f080c53c06376fd", + "0xa52d9c49db4819cf6280c220a6cd306a5851b771de3032f28c7f8750c20e80cbfda57323a55a8c03085b41f4f236b5ba", + "0xb01fbfa0f80ef574a1d6733899002a8672cc309e1014fec8e81ea1e96a7be9c247a570f825b7862e814e1f006a8227ac", + "0xb07e163eb0f96a51d74aa8a7fab5d23e44e37b1b1027ae9c4155280d8d159f0cdeecd3258c098a7358c5bf2fcf1eb7e2", + "0x80c4512a5bb5e8255488fed7b7e297988732473f0ccc1192cab716a88d035e23cc374a937fca7da87e18048ab026d9f7", + "0xb3e343b13c1d4c98b7706edbf362eab12b1fa87510d5cf168e510844b24c8a9624f1e7e0babf455c6d425741c23e1ca6", + "0x83e4b53953ef683c512756b3fea37756b3c562c88a15cddd902eeecf0de82d0345fb05feeba511e8a6de91aa1f722ef7", + "0x922512dd5ce444df62fded2c53a73385570804e7305cde401116c06dff5ec7812b776b8cccdfdafe422f1ba53b2b56f5", + "0x8d1f7feee880abfe9f09708ccf72f376013b2910886edcceb76018759b88b95cab9c0e8f176faf042729b405a10242f5", + "0xabb7cd087d0cea2cdbb47cdf9be2c6a0c6ec113e1ad5fac083f66a027697d477ec69f46b9aff40c239ad9959b9854e11", + "0xb10592443daa8708f8c882da795da07465efb9829305170bc3bdd781cb6651b503d3b21eca027486d413f1748f40f068", + "0xb14dcb895ab22335373d2b736628c1ed0e815072fd3844867ae24638aec60f8591c6885869ad0bfe509fa3fa3101a5f0", + "0x89631708996651bba6b2113626a2fe1ef0f2ea2f21857b2a1e5544ad31e8a53e755b6d611546ebbba4b2213acde65e72", + "0x82e9436700fcc5b842ac2f0482de4248ec9d1f206db3dd36917c00c7749bda257fedaec513d8a9ef3765057bf5aff25e", + "0xb1c2b26d93658451fb4e9cfcd77209dbfea909b2212c000fcc576ef29b808061c9f58827682cfa09e357c1722c3215b1", + "0x8be32f59768777a785d8b257f941215f37db8912183aef4a39a856b88cc680ae7124789c58cb3c6c6f06a951dc96a1ce", + "0x8cb60a3d0c9a1efb89f89f78e6f0e4bcf5eabeae6cb215e98cd7f9eb58699ed70dabed73a8b95daf32a5e4bf0d411d3f", + "0x8ec7156d6b672e631ebd88467f40caa9ba5411ab727602f3146b468bc00ae54fe44b3228572670215a0dbd59feb66e2d", + "0x97b7162101d740aedc894bd5f74b8cfa7ca7e7fe8363b05491c15e8cd54f21b0b09eb41f756b9089c379ea0ab189c468", + "0x8524c9de6be47cb6808df761ed03c505932ba715e757dfb3c97b6deb462433d98953ee6cbc7a576b6837e68eb16d3188", + "0xb024c8fc3fa4f602ab73448418548d9896200065a95e8a001f6c8d4cc3f53f18ec8b85524377fd93e2d2a18eb4c48b57", + "0xb344dc93d3057465592460b7f35dc015f4f8025fbcb44a645dcc3dfb37044d5681d8abd81bd544272dc57cd50048f29a", + "0xa7b270b94d9870f8afec3bf2ed58afb76f4ea576a2175502630d0d3f92f9152c1ab0c019f175f566eed29713dd97712d", + "0xb86dd953c40d4f5574bc7489323d71e9798f7c6f2dff8d41f6295655c5a275179ffb4bb8d2408b88226c98583a7c26b1", + "0xb73074289a5b08aa695de03ce2f5b106107c6cf2bee8061e3195056e799b0bd8b4172deff7f413ce8e477391ee6294cd", + "0x98b801a58ac7e083da541ba058c64b00ba709d4d0ba1683e5d83dfb80a29272fc2a33a18f32351b103b227abd5123da1", + "0xa7cf232c6ec6b9dfb32d729b9d4216688f6d2b6e68053ddfb293ebd5774218c69133baaccec7ba3da9b221af619c2ed1", + "0x8cc1d33ffedcea05f3c593e5b63dbfebdf26d05a5719cbf642997be929336b92457fd9df0d6be6c063918ada8fa2d322", + "0x8d273497dd9f822984f1d8dffd471cc703d03c342f022b2bb24492209a3889f010c4f7ec124f9fb9f884a1a11f84a414", + "0xb62cd013944d8d9d72fbe54897a94e804c93eb84a24beb0880cd98fd5d48fccf5dedf5076abcb1b857adcc986b729cb1", + "0xa1bc703a67ee709f7776b2871f2a88d8574c9e2910690c9242c162ad926ef2263d5260f5c19015ddd5ee1c8ad1a444ae", + "0x87de434e8ab5b1d067188cb9c12ed936c26ddb0ee76c4c9cee9bd1ea916e411a354bfab2ce77ed8c8ab5d8c62038f933", + "0xab128e9de30bad31dc2eaad851da1e39741ea61bd203b48e5671e37f7b4e3db86687574d3cea1f561bbea84f68cd17c2", + "0xb54576c9c4bc3b43270b83b89eb75cb7e89057c99e14021ca42237dce393dc6a8614c5af5c2f69160001b2ecbb407c9f", + "0x93adf38f161ea886f41e4af8e42c69c53a51074db9ecd7b7e4e36c858426237167aa49b79737625c9f9826dfd22f39ed", + "0xa6907c8dc4073d3d4d40df8302c1637c15f9197aad8511dc95c210f6a60b06f3aab2622b826d16596af27e42f2c9d5b2", + "0xa8b0c4a3a5d3dd5b6a85802039f48fc80350f6f0be2e55bdf75e3197a22f6547ff4a7dce38ef3667006128141364625b", + "0x8a5f4c17c729509309b2ac7e0dbadfbf0baabbcfb1fab02f91d055238faa3b66aae850ac9b8d7b7245f0a26bc5253c99", + "0x8bfc5d594700287da2a85a78630c616af8e555cbd7864ea604ba38eb75742fabf6aca12ed99a2439e2e046d8f048a29d", + "0xb0f91b7546613341cd95ea112e04b0963fbf7795f118c393fbdc37e37dc25244d10d987c13d6fa6eff3c4721fd0a328c", + "0xa70b6fdc66ce4c2e7f1d830b7765b7f0640ceb7306cc85778488964cbcc336ac378b74b9c4ec27358f27378380b3dec1", + "0x87387cd6b643721aac8e1a8533c27602d9632168d3a3532059163dc5e4613838bb4f07803e6852b23c58f7103d134f92", + "0x888722a5a56f5b6b00daba53991ab6fccc32a029a7f1748f779b57732285e56e09ecdb7d4848abb3dbf3e167cf8033c7", + "0xb7f5b9ffa8ba66f54cac387c197058eb9025cb3761550c78429db95f9e1e3b49c208ce86b6126c162a62939e1080895a", + "0xa53f38c068233b584211157c66d9d2452c811bcd340d8cfafd32b070c326169306975e558745d63e1617f4b4528a2399", + "0xb1c3e9b0f19993f973f038bc45be6a834b1cd3d56f112c857711c8e6c30303eeb0b205bd5dfe75e46b1f4d4bbb68fabb", + "0xa81fc28620e640ccb57dedd40c79b73b0c51565dc61097527b2341bbaa3e1c9ccf20f9d8da1c16704e881b24df0b7335", + "0x910a7f4960a0ec2aae66cbe2ac98f43646b017de84ef3d486c19b7809aa16813400bc2dccfc80e09c63422a9d4d88f56", + "0xa463868e3a8c2d2a7c49850be2740e01c7892c83063d381f405282b4c521cb6e3317361abaa92042c38bb68695c10bb9", + "0x991957100ea0f66cd4ebd23d9f6bc7aa62220f6ecb71ac947cbffc6f36f7891173670977bc58a6f57b9a1e8766100c2c", + "0x961dcbd2e6cb94574a33fd48b5d87e0623411574666d5d37f4ff7dc587980e2644cf056e002746088c3c3b0ee7044510", + "0xa27cdb374cdbff217370015d78c7f5c4674ec9948339475cc80a5805979a4c6a2614b6904f943374e96bb838549ea517", + "0xa567bd4a59f9df9f5f30a70cd9f6cea7dc0e19b7fca37fef5846aeb1697dcf7925a4735367be0828f2ded5007f711f03", + "0x823937a900e3b8705b657d40470357d91eeb339324f0fed58695ad72dda7c64f2a6b7bb7ae4a20cd1b2016cb9edbdd1a", + "0xb07f2248802ba7dce15b2698a60a4896323d37ecae6666a71cdf85878094bbd4e9c0e4733bd8bc6e9c850e57727e1d86", + "0xadfcdea69c5608f02630db045e5679f9f0768fbfa9c8e97bc9cf9cafe1f747d3813e7bb1adc6085cd57102afd71db133", + "0x908153d3eb2eb2b93c15aa606531b08981bcfc8b76684c2483bf869f335f9d8773a9aa3986ee54d9392856daaf82b684", + "0x8fbb2acf533e7d6e96e9b68e77f7a1df2ea6c652cd8862b946c93c084436d7349ef4a0c453197a9769e986322e9174b5", + "0xb83cf4ddee6140c9df0a08a39bfda79c0d55516fd799c1c24b59397b87a33ea5a0885b2998dadc354cb6f65a4bd946a5", + "0x957a52cb24f19106d80d4115a8a0843d047d157c4a8535775593c1dba9be24318dd434bf43a82aa7755897f895d2ed15", + "0xad93dbc2c055f9d7e42717391cfae64962a78bddbb9fd102a05cea520654d4a9cb6634234d3a188693c87c5b4c78959e", + "0x8dc4b8e49de9b05c33d2a98973e223c01ed5745eeaada3a4c0e474cc22430644a53a60c3d6efb1212ca298c4331763f7", + "0x948b0172df27db83e70fbfdc896ed82696876ac4c51842d270d9ce1e7f1fcc9487d781eab97f40074861401b809dd7a0", + "0xace190f75cc102a79412fceebc013bda8cf329798db4b4dba658e63228ca7f141bf0849d30139ffdededf98986f3066e", + "0x8f968dd6d7e06008a1374743b965a6204c11a610ad92705e8dbe6df3c58baf36b47c5d9988e4a417c80ffd5e7725da7f", + "0xb8ba0d5b36cc41f6839543d43166a08bf512f7b56040891ab80efefc774db28c833ecd444a703c75547fa1404fa1ec22", + "0xa29944dd0e5c861eb37c744c429a0dce596cdb2e5b0b2d2438a259a0faaf3d261faee1434bd28ebb2e6adab59ff3951d", + "0x85c70422fde0ac6e7a0574512eff2a19df8049757bf78b5a7d7870848626850f17e8b0a5661e5292f3df0f613886306e", + "0xa5ff5c3ca2c70b88d15633f9c38d2e065bcfb0e447adca33301a0d4f05b761049c8f795444f11e39357fe6bc0d7f1880", + "0xa2167cdb114d7707f1010e0be9cad488fe56cef65941c35a5878a100adbe522a8abdf7eab7bc689b8727fafb174032c2", + "0xad3f526ef9ed367b2a25c95453135510472581a758760d47eb9f9b57b04f8c061152e5a792584d6ca7124dfeb7e21703", + "0x86443033ece13fd386485115765aa95673be72b0543fac2138e1488d22419591176423213ec06e5e4549a025eb6aafd8", + "0x887e4ccd58603e6c9cc99bd2740bb1db2fc4127e8d3ec9cf41bcfa3589b0fe1931ed2a6140ae1199d323d2870882ef6b", + "0xb701f7d7637662ea7024d31e94245a5f745c7ca889f4f7a8362537df82b0164eae83da5a107a21c0ca889926aa50de49", + "0xab6bc11d6049cc5945011d3973eb2dbd5a3d786b3824bc125786e734887254a6ed61fdc2a97ea34e6b37b13cd97eb781", + "0x9901a1f44122bf5aec7cea28e9405c33567eb118752edc60f3cf6c80642370f86557cbd76478d6b0ea33be92a22c746f", + "0xb9f453650996f14629642bef8fea66c90105c8523f3875d86694100f8022d4fff2915ac9f9b9efd6f425751b113d5623", + "0xa5bf9385a1c94c09ec326c49b6b603f2de987b2878faf0263ed109410536543095c07320f762fb6fe56ee72a082daed6", + "0xab003c86dd62c801cb16b727fbd1037aeacbec0f76e8abda4c6d87066cf0a33dc1c61721f2134c3b78975efe013cddb7", + "0x8dd8c580c288168f896fd7ffbcf5c8167a55d34f17b2c362d0ada0f33a08cc0568b37b01cf0cef1fd5e2c2e250fcdf7b", + "0xacfe675aca83a774d3f526ad461f0deeebfc73a15ab3d37b224f8740ac2d9df238373e6cd1f03ca78a9daa0a796c96f0", + "0xa45cf3242600fb9733dd5e0dda1994e8d37fc483885a34a76cc16bd245f6d9c8d15bef360ef59d0a2c3cd59114280b87", + "0xb64097145d97cdc8b7a84edd1da7e84f8aa44c3c2a4823e6e8485fc3a44d94cde7d7ce8bfb3da5d583386461ccb42afe", + "0xa10ec5859c274c0972ec39ac80e461c29995b35d03603dc97dc30ff826ef24c5e52d5dc9296319ffc672b9e1d57d7936", + "0x9702ee805b70a1bfac7318e8470667ee20739e3db3030bbcb9a08568e96c9c8d2af2cbeb26837c43e04997457c623486", + "0xacb3f5a663333d1b4d54dd76a23c6601fd4540e2b050ec2a7fbf0b850b6f592837155e6bee5ca942406643f98bb2ca83", + "0xa577b96723f64d2671f1253fca72df86ef3247006c92cedcfb26eba4b4f4ba71bfffe1d5eb87b0192378d0303177fdba", + "0x8c397ac56cb723df015d0ef202fe486d1acb42f8129d3e4b37125a7ff9e81aefb1e19f477d528be1e6b18e7bced27ba3", + "0xa7a6e6994324a80ee0a85e8e8cf818f5f8d16d299f21b2fca8a9f65231982584afe921e76723701abea873275ce0c15f", + "0x82c8ee7a39e49528efa60ce1cbcb3017904de6beaeb751c406b64a94aa73af72932e32b63c1d3fa22619498fc46de6bf", + "0xa1d0193ac8bdd44ffcd37092a5dcf6e775824e5dee4c0aea5bd46f2e25b312fe58e1e6b9dccf9dd14f240d7ced4fe071", + "0x82c48967f22b8aa1dc63dbda0f66ff2f40f3ca9a7b3e752e1a950dd7daadf9afd81ae1fe3d0e440259dccbc1520d4e22", + "0xa69d43e6f962b728d223f6d474a923dd13c04eb8858f7fdd2df2c25dd4d13a0a69e71132f450613e8e2d9a65938f31f5", + "0xa613b731fe0d23ebf376cb1f3707ab9b2d428d1ea3a95faca9988a1ff4fcbde0a077b38b5942590e594307acf88c9db8", + "0xa7d2f249ec666f59dc51f9c31db6168f33a94b17ab95123d4b19aa00dbe9e1cdf340dc6f64bffc6dabb11912e10edbba", + "0x8e64b8f99ada5f317c6e2fd64ac17c4d6e5314c82848efe1eb97a5a52e6bf08923360dcb44c05d3fa59a82119610a898", + "0x865d9512ec4a18ab31e4062b2ea6c43ef32c7c58d89bb0afdad9fe57dadaddd2150f78a0e85086454812855bf09f31ef", + "0xb2d23f01a0d182abcd6862ab6f4bf924ccaac399ec143fe2614908dddec102e2feb8555479bfb71ec3476cbdd71b1137", + "0xb50d176e628e06258b518be78c6dcbc3c9b2b4a1ed4ba10ee822b3ebfeaedc4fa69c61c1985e1bb20ea9f3d6df7a27e5", + "0x8174953f4023e31e39f1cc3bad674bf2f1605ec9fc053948bb60dbf2cabade885376f8c76f45b638c95fdb14f5bc562c", + "0x92b95a12d1fb1ec489943b3a2a1c8e3c8c6a30d0767125b87fb491f9d4f8de0629afa39fb5c8a84078b08bcc26e88c4c", + "0x93f4b80d76689d5936aff6cf195d579ff5328ccd0f04db42522a225f96b0bde2088706356675f185186548006940898e", + "0xa5f7f4577943741def19df611c2ad3d459c386a5e3c765eaa5a0cb6c192873675cccbe62845418dbe47d7a9822e4038b", + "0xb59bdb196d59928326572807b2ff2edfc93a28632542b270ed462567d32bc36cefc40300619aafe4cd1e91c38d6c9c30", + "0x90df4b921e13ca1e63e8a5c9968ff64bbcc5b829e3421d74bf7f678aa1dccc1db9ed9dfe5aff05539bcc5379dd59e575", + "0x837b0b6813249c456631b2f2fea9402a2303a454a114149bc35efb400813397366eabeb4477f2cfe037f722d78a5849a", + "0xab5b33ae561312d9791bcafc8faf6d65f2c4260f126f11ab5c20c7626d88f2c00177588ec62ca763a7ca44c6ed60eb0f", + "0xb0ed2e48cf650a4267c3da1378b8164cf6b774200a5898155716f85f7abda093a60b802ce549811644e5f075d2b26067", + "0x8d47a4e27f448773fa2d592f052bbdbdf30cbef152db6d8cbeb3d7b1a0dc0f2c820ed7572eacddcb51c19a8268426b33", + "0xa56ccd0961bf238ccd197e5bbf430d7c637ff6e01590febab3529776403682ee32d0a776c3dbc6581f60002dac86c38a", + "0x9163bbdbf468be88a391698ab1f40a919517beb6c780062d4bab3bf8fd42eed6546a8c743e249fd61c3c347ea60ee185", + "0x8d59f46606f063e68198457917004ae50ebb99cccb07755825782ddb25b96c3cf8973a6c5092c9db71a4b8ed075876af", + "0x8ebffeae4fef7a83d81f31a88589e05f885dd0c0b4360545b22a18369a3e6762f187ea6a173f25419e70031861936463", + "0x96013c6b47119e017c8bf473b3d643d0bea1cc12d84d412c2b9f6f55f22543a6e15ff7e818e9799af43984ca2ec3bfb3", + "0xaf46ef7696d9908fb343a6754de37e31cbb41dc1a4ab8203d2a2483d1cb0dd3183e5015d8048ff146ec34a6c3f2eae21", + "0xae047ec4584a962a7ae9359292c98f4d8e0916dd98a414e2e15429ff30ffadb3e0296282f0f7e257495e8ec4bc0e5328", + "0xa16de787896a056d31e3f174418aa3882c03c879a292246a43dafb81f8e0e05564f1cd3ecfa251cdb159f63777fc6346", + "0x97d1c4a94182ada88aa3cac95520711802cd3889e3e057e99a72a69589fd222b241d35a54b04f42503756ec3c1a3d701", + "0x86be4ebe8b92f5bfceba757e1e2eb649f9803c8cb10130b88b13caab6bc04dac4e26d538b7adef68413b047ab9031252", + "0x95d4c0b08caa283ffa9e307f78db15470fca5b577189a33bcdf14c36d4ae3f571d004c5aa1e69172a4068e72b7dc25d3", + "0x965b7053a1d14f9091de5df8bf033a07b9f8d39a6d66979ab5424bbfa32b803075afc2d74e71235a0f881bacb6704689", + "0xa93e72836e2efc704f87065dac0463ddd4b063eab125d834df583d8833873f575a0179781b72aeb2a35533a34a395481", + "0xa2997d7c377060d910654550316ea7374a0329fcf30e743d613e2ebaa15b1bc6c936c2529f5466ef0e60ff53aa2b709f", + "0xaf5259d4d08617d9be068d1b79a8209497972910938831a435487395512187691d0cb021bd57eff0f694f32efc1487ab", + "0xa78b8318838b1049f308200782c4409fc6c97ca5bb6af28996eb191027c8935b7a43a41961ec046e6c8539376c1aa293", + "0xa4a6a9ec652d1c95883d21d3767b13a7e1dee73be907dacad197cfee025755db3cc7a8fb9f40146912f8a3f4c2c49c14", + "0xa8a8ab62334a3c67793fa0691a0d2e80ac1681ce64a02df93b78e4a2f6fbf3af9b160d9ca6b4e835d58ed60d8ce627d1", + "0x980c32e492464a6f36ce12ed06541e3b2eb098934c0ebccdcc984cdbfee4a506d15afe1a30a84d642322c2987d9d51a6", + "0x8ea8c1adfd73747db98705e9fe1aec441484d2e4862b84817cdf1262fcce91f56cd26073d4dd23b04f836e0962428593", + "0xb0f20edb8552d2b08613cb574e9de1c4dce1eae55ba9ab05dd7f2ca3590a7496d63d55af88b3dff881e16d8bf9147037", + "0x915af4e9a28b12ea126668db7de6ff0c2cc9935b138022fadbb1f385f327fdc927388c945b93d252cb51803c242f7e1f", + "0xa553e08f67c61ecc5c8955f7251cfe18cde02e6170845e70db1761bc00f42a31cc10de26d4c904200166311f32a3e56a", + "0x99f4b066a805512e16addb0bcb08d76f017213ca6aa6afb5c2fc621805c4e123bbe0aa85eb5a0f89d3112635905093e0", + "0x9236c5b0f4d2e58033735d7bd5d53ccbe82c05aa290149286a16a318043ffedfdca9d2d07817601d4216fed50c1082f0", + "0x90a4c7898c58c9af83f94095f6afd5ca65664f16c0af4c8121407cf0864fdeb09958500b2bd0b78950aa9051e3480928", + "0xa589666688e6e7f8e4d99b84d21a1f9ebfe681fad346a237de20a11a2b210eb99c4d3e2f645b23a85c93bcccd51f63f8", + "0xa010849ed4df0e3a8eb61f7fd114d05a8669bfa36cb95d089bb1964ea8f5fa26be0cd10fcd9b38b259722c5a14ba3a1f", + "0xb21f974a10a2dfe9987370ef4b6af294cbe8f4bbe35ce9400d0538c5f71287498054d73606e26f93e2f19584aa18e285", + "0x81fea77bad05c3bfa8d5d8409b189fd5c759613cd69ddb19b2d46673d4df944b2c7293989f79580d229d20959c74b18f", + "0xac962b0819a03d2a2fa42c492f54c3d293c6d5ead403c50f7a1ccc2faad58beeb0dfe888a928e505fea9e09807e13a23", + "0xb78b913f2ad9622d20c175ed23f80f235b5336343b0353f82383fa6aab99aef77cb489df822bb168e56496c1854f623d", + "0x8c06abf72913ffcb6b59bb8201c00034b447011880733aa6b563acc423e90bdae19f2a7a286943b55488fc863d09269c", + "0xb34168972fcd90c78286bfc6078ce559e3c216d8d1885ecd5044bf9f23a4ad15bfc9830aabb4273472c43e2980048586", + "0x88350e0ffe9b5576dd0afabc6d8445d25b2b9a0945c71e6b9a5587649ac5d95cbd722db5ea1e65d3fb8230c794fda5fc", + "0xa3bec1fc362a33f38795158f1b869e9ee857a7f2e1acb92c6a7dcfffa64443a5b7f6dffb656452e7f855051ae849be3e", + "0xa21f64c49334720883e1243a27575648f53637a644c308ff24f5c26bfe65cc690a5e46b8e432171f31c4229aff4db416", + "0x85dcd8ebef8f7f44372912b4a3a0dfe66a56f16c3757a8ec01b71aa81eeda9f8e5082f92e3ae8cbf3c1eddf5e6ffed03", + "0xaf3c1a770f34f2acc504f38ffa7a18cc4b38f8f84f310cdf2d7346b18824ebc7c7663cc0e00b44cfb5494fe6081aff84", + "0xa5dc7c5989fb5cea87c2d878d8436d858458171b667ab5200dc2cafd8af2d9c2bfe2515b0c002cdc9c3e61e4cfe4b444", + "0xb136dcd4577ef3a3a8bc946cf2ec79d3fab301114ee2a692a6c219e252c9128db63fedebc6bd6695a8ae58e7d28501e8", + "0x91d3a1ba625632d59dc963ed54c0310d0447df3e1d9151299722d10f8b26161bb73e0020d408b748fa6fd1db51adabd3", + "0xb89f1a2497b04b3f1b018dc88b52133e1d7470f97f846965fbc934d34dbc8d38f2d8b07d218e62c609de33b61831cc9c", + "0x92fec43fc5af23fda5dfab78234f5ea7813d8de34f8ec269c5fa35dd916b9143ff0033d35e7a284c0ef4f41047e98fe4", + "0x8a0b89cd35ecf5b6db26c39705b416a4b950aafaf3b00a9d75f864955e9074aac03826ff9393456871107563eacc024a", + "0xb04db63ebce71161fd42bb878e89155bc9e887329e164481077c6a1db092477370a362810d291444f5463437e0ec5906", + "0x88ecd5275592f8b133928770e2273a0e0c23424d72b9e505130b0599ba28d1c11eceb2318a49dee054a8ba0971874357", + "0x8eb0271197fb9f1eeedaadd8eb603b8753ada11abf04ce90950034f51f756ed6ec6a6182a47e1f3ae51e3a1f3ecdf467", + "0x81cc996bc6b12ac56a1ae3add4483ae4f2e2284e9d304f5fa1723231d0e5b196813b6dbbc20b70f5d51fcbb65bf284bd", + "0x8e1d94ecca2928c4c68fbc13199b6281f8782c75c119b763e0eb122f81c35f8fd079d1bd76b498393371a08dac95dd1d", + "0xa92f98bc09f8a91fd165bb8d05e3b5ec50121d760b353d7e4ea23c0e04ff29614ad9028a4a16bdfe323f2af647e515ce", + "0x82e8dc99a14da065200699e458150dc6d49ec0e098bbd91ab8f1fc1767e8732f53855499c8f24da7b9dd681100633be0", + "0xa67b6cb4eeab4fe5f4ebdf5649b7d61bf5fbf7b6cd2d357fdf348ba32dbfa9d6830b1265ea76a1c666b266e30d119182", + "0xa64e3af1d0e600bde18d7f53a4e8d89d296eab4bcd9cc3a9f476c5b8425e6e8082066948fbf40689f626e27e4830edfd", + "0x8f66b59782cbccdb31cb1bb2d6385307633ba4db31c375c0a8424a497b2fdf309e7ec1c95490324b9a909bb43041998d", + "0xb93f4817eb1d91ac78eb650c110f7c29df40df47ed1d5d3209c3abe5cf59a5e7aee3d1cd232bcce77e157b1a9daa2557", + "0x864b6cd72029640fc041fd3efa71bb210edb40589a26981724b944192c3c2543352b4b757836a7b0b13bf830f22b8374", + "0x9064a0ac94f2f133e287b796363f6d27e9646a8b531cd9ac0eb45b99fa73f327238161a43f7c4fc914036d69abd1473f", + "0xa40e60d4aaf9f50f7bfebd0e714fcfeba64e0f7ccaa0f4829144a7efeaf15a7cda2d62d771a76f98a45cda9196b0522b" + ], + "setup_G2": [ + "0x93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8", + "0x99aca9fb2f7760cecb892bf7262c176b334824f5727f680bba701a33e322cb6667531410dfc7c8e4321a3f0ea8af48cb1436638a2093123f046f0f504cc2a864825542873edbbc5d7ed17af125a4f2cf6433c6f4f61b81173726981dd989761d", + "0x88e2e982982bf8231e747e9dfcd14c05bd02623d1332734d2af26246c6869fb56ee6c994843f593178a040495ba61f4a083b0e18110b1d9f5224783d8f9a895e8ee744e87929430e9ba96bd29251cbf61240b256d1525600f3d562894d93d659", + "0xa2d33775e3d9e6af0d1b27d389e6c021a578e617a3d6627686db6288d4b3dffd7a847a00f7ef01828b7f42885b660e4204923402aca18fbae74ccd4e9c50dd8c2281b38dc09c022342ed1ac695d53f7081cb21f05fdfc0a3508c04759196fcd3", + "0xaf565445d2ad54c83a75c40e8895f5ad7219a8c728bce9d58d7a83716e095432993ebbd3f6911c66415a6f920d1a4d171478509b54a114308a020b33bf4487a7a8d0aa76ae4676a9b54e765a680f562d3a4fcb2e92c58b14b49b5b2917cc258f", + "0x8aa99cfaf514cef4801599cadd780d222194ca1ad69a34779c2bcfda93e5dbeb931e13914421b5809a6c81f12cf7038b04a35257cc9e94c33761e68565b1274aa6a6f9d66477229747a66b308b138f92aa4326a3bf23df65a1fe33b3b289bfe1", + "0x99ba36d8b4f56bde026099278548b1afc0a987cbd7c9baa51fc8e6cbb8237a17636f1a44a385cec69b05a5802059956a11fe793cabb939c38800f9c239ca2518e898ade1ec2513c9ee492071a35aabd78182392a09123d28dbc233313c9120c4", + "0xa7dc40c36afccb30a2eaff250860b28b227c195cf05674704c567d77d6655c446ae835f8fc8667e71147ab02afcb2dad0babe60cbfa37d7c2cddc68d2dec54f28a4142f8353590a3902d5ddaa22066ab563dd1435dda83f276387b9767d69120", + "0x939e6cc97a8b88572852a5b7f25e4838556307f60aeafb5d2b6961edbcafd4b48cb6ac980ffbacf4be963f324ba81e3d12de4f1459d8c746d0762c66ae1b166027f7fbe641d9c48f3c7d97b06d956b0be51dcc9aab65f3e99e1388e63bdd79f9", + "0xb391e156541dfd4003d1697cdb7ec815b309807320574906b2e652ef0175828b356d215cd374b1b34d9f470b3fa0e643113e67b2273268f922f04f072cfb89008358185b25cd631f82911a3f20f90f75758ffb99bebb8076458ae1e9d1ae898c", + "0xb9ac9c84934cc2a85c876eff65577e1dfce1935cd6392c877dd881a7d2f5c3e9344f28c04f90c62a6db4237ca00f9e0d00cb5f63e3f060fc7303916e19273b6fe455f331cabbe2fe5a22d584484f0d4176120fec9819fbb0a01e6d38695acfcd", + "0x88209eb030c5d78734bf2c2a5c539653fd3c24b4c08e624f9ddc4a6550efbdc1054a56eb0c807595aad6de56fda326aa196d032a8b4b48d40140a2d77df3c7243eda6507936389a321a5811eb38e32ee433c788deeae1eb928b00940e2944bcc", + "0xa8632ddc9cf7cbc1e8b74a05b7d4a89618c64afe30367ca0c9550ae7d320bf4e51c5a69e1501a1d8bee4240d13d7835501aa39fdc401a74f4d5734e268a7ce29a1fcfdb0a8bc64e0dd4a9e8578d6985bc2bc6a3764ce7a3703f6fb2e52557a2b", + "0xa037ac67e8bb6f4193ac967e05d080a489f58ef8d3d30a89798246f3e4936121ee445b03e410a09e8ebc0db2e2477d110aad0ade99b0887f1eb016e750f42135866907f150bd6f4f99a8cb94281474166874808ebe03b118c5daab16dafdc38b", + "0xa50d9143116bffa3b237da8e1805327e81e9cd25e658289bd727d5f9e0020172cc8690dcfe31a240e5cbc48353b88c4908baa1dd7320165556e0aa633f62fcbe7870222d345a3bbcdb7ab6c07f0fd86be559964afabf56f0a8cbc0b4b91d477e", + "0xafa988ea6fa4f40c5ad07d2d580d29025ddf56d6ef1171a8b8de3464203f70b97d6f5ace72747345204b35150e06154d1477516a989ce8eea7871cc0d0de00a077c0fb23ad4837e409d0b885bf3f2dde11a30fa6273d662e68e09f461e52932f", + "0x97fa1a943ed8b81574304a3d03f4f15907f6e6e0cd36a66bd2ad2c75afafc70a61d3ff69b77ebe4dae9ca0fcedef80081062705e60bbb6ea0f1f398c84d2f8e4a3ac142ac66426c21ad5e9994ebbcc406af474c4aec5e32fadcb21875af7c9f1", + "0xb30a564614493886f14a5dd71c89457504f8c59a7ac01b665ed167e9a8f9ee5832198fd319ecd234196ee57031bdf3840bd5a923e203a1938bc795c704b5285389750e1fd10d7050061ba19db00a60a2c0384a7d661d7d48ebe6962272230859", + "0x84c8dea942cfae71cb02e705ec496d967425793ce8812e7ee53c2f23713abeaff566a658cd1c73dfd18187d16253a6ee0a623e82cd18e31cd1a1875d19c078835dc9292e141686150a88065226ada264740143e87c03a0f6c4da8c187438ebf4", + "0x8c3abae8aed60338f8c4ff80aab22f8a2ae56756a93566c906f490a97151d34a1c3318054e1c494c60cc53327ad86a2d02c6c76a406726ce4f88635bc32eff0db0b61762dc518b95fa8da82e87e4bf3de54f1d72180ef53ed7bc5413e6a9a510", + "0xa328230c92a6b1cef6a444bcb64edb992f71e3d7b93f0b6b8b408ba7c908db746d92ddb2c7588bab438ef3bc61be1c2f0dfc86ba2ff514b42b35c80f89b2e780f813ea1dfb977fbded2cd9b553b747fa952e227ebd8f071163d421fc337f04c9", + "0xb482cab423cd5f1c5df036070aade7aa016283d69619d664025c3feab866a0a5691d344b2ee2bedc5dedd1f9a73eae16003a3827c9e5bbe22ded32d848fba840ffad1141ad158f5c40bc8ae0d03781b9705d851a7f1391b096c576c0f4f2a6b0", + "0x919ee1df27fabcb21237a1b7b98f53d41d849e1b6a8f9e28c3fae2841c6b5a250e4041c737e6725476e5cd715e34d3880f58d80f61efaabc261bdc703e8750f48a923e9bf8980931b9fd9e40014c66c54b3e7c98241d76d1aa47af43313a65a1", + "0xac94830145dbe9a8f7e6e0fc1f5fb454502d22abcafdc2dd96c6933c604461fa83b2b37385f4bc454875a02a6d4157841250956783515d11c7456e7f11b745f12856d89f5feedaf6a61a483a6c33a21cd2ba0c18eb41a1a2e7fc33bb53e4c570", + "0xb209c699f1233735c5bb4bce848e4365fd76651ae2184d2279a90df0c2f69ffa2a24d84a9b9f274021072953c0d65e1a0202d490d6c37186af240114e445d87bff754b4824937e4f2c90a574061b1c4910fed88d90f698025a2a264e656cb8a4", + "0x93320dc0576b0d069de63c40e5582b4486d9adf5e69e77e3ebaf3da26976fe42147a65051501bc8383f99e7ba75479c70a6726c2cd08bf98c7481f1f819712292d833a879f21a1221a9610bc748fb5e911055122fdb4055cdc84e8bfe0f4df9b", + "0xa4380b240e998cdf668591f71a0c88ed143b0185a920787627ce65095f8223dc606fa5bce93377af100de92d663e675c0736d7f1973603a84a5c4162fb5e01c88c7493503ae1d7e9fbe8ece9b418397d68c21eeb88dae226e09875d372c646dd", + "0xaab48517d69135a16b36b685adfe9b2544a709135a21ba3e75981a2cba4ec81d1fe28ac0f72fde0c0001c15300ed6a810f58d3117bdd58d0149751d6508cf8a1a1ff7b63dd02d2730a9d6fe96c77c502fe8ed46d50a181ec4bb35e37dfbd6af4", + "0x8277265fe75ab89ce4ec65b33fb4084bec0a56d81faf2f7a9070d2ca3065678e03a790350eba56323a54e0285bc32fe8007d5259740fde226e16cbde8354eacd562294eb9b7f727ed72ffbdad86f467cf057c737b34b80a41deb92634ed866f5", + "0xaa40a24cb2ebe606d969392c03020070f044c95088d80f57f771b837c048342d2cd3474600d7660441090ffb8d2ffb7f0eddd67eb378e3e1477a6ba0bc38096d5d2d3355bc8b60f605f57f0c1899da591457440352381d2b38c0aa9acc7fe419", + "0x80815d10685808cb630820629bcd2fa9041c9b74433630c0b9c1b7f7e8edf1440b520217f76ec9a50c125cf4438aa66006a1928a9ed2321da7ea325c3d56b65462b72118ca2c99a0ea733aa11da9abbeda6cc71ffeed301ae70213a29e697dcd", + "0xac235d079f91b00b1fead7523da8f73a5409fa8970907af0c5d5e4c6a0996dccfcdb0d822d08c7fbc0c24799457d011d04312d20831825f23cf988141056a6814c8a1cac9efe37bdcbfa272aed24cd92810fea7c49b0d07683a5c53643872179", + "0xb8aa59534d75fa5ac1c2c3f963bf73899aff5210059dbde8a8635561c6249e5143affee3bd2fd57575213b52d9a73d5702525867a7dcbb1d0a49b98c2925556fc5463ff0209742046a24ab29e74257d6419401093cc4371944d811cc300b6a67", + "0x80bbfc5b816eea29a6d84e2217dee4d547306994d39e5592515e1b0807b67fe960d1d5addb0ff1a20c158bdb294c04bf093d28996121845a2c9268e2c9ac0f4067e889c6aaca62f8535d35b45036954bd069e3afa84f04721538c26003304c20", + "0xa535c17d0e151d0e03d42dd58ba8c715bee3fabca2890e0e016071d34184b6b34e770d2be29c8ec76b69bcc471d50f4d043c2c240e9b93a81cff7ee2724e02018dfd9b534e40be641fdb4884abcd83b76f517557ffba508f1ba2f56313f4de94", + "0xb237eb7465df0d325a3aa58269be2627e4978f9863f4f100ed4c303cb1f6549e606f2e3c9180824d8049191965c8dacd0a0c76cc56cb22cf1bcfdb39372c8aa29b4f7b34582b1719e6bd59c930d87d5ccd838743b585d6e229d5ed42337315c0", + "0x805c335a2a9d2de30809cf30808ef836d88e9453c510716f01696f14c72dd60505eca8f128970edc8e63a9aa1f8792ac0dd50dcc84fbf4cc8b32349c682a6a27bc7551c7aa273a94c1606d07710188d93579afe3be1781bded15a34ed6047922", + "0xb25dadf385ddd3c39bcb0a014d3d4f66127946b1aceae8809e3a03d66cc25e27142ca108316391f857fe82fdea4db2520cc73793b695eafbf3ade00ef7ec747b0457e49303f5e1a370f5263b436566fe24a0876e5fe088238c7be37a0718d65f", + "0xb0f753081cabe2c8fce73aba82ff67dbc9842598b3e7fa3ce2a1f534536f8ac63c532fe66552ac6b7adb28c73ed4c8a4184849be7c1756a4681ce29ebf5e1c3aa806b667ee6bd68f6397aba3215dc1caec6742f21d681e32cd1160d6a3b1d7ee", + "0xb798771eeb3d7a17c62ba5916cc034bba870da6b1ac14c2e1cae71af3ad4e0c0d1ff983f691e0e55289d5a33b131f2ec12430c9566dd71f4d8be9c79155357a5c30c5efcfd75bbe1bb6d5ada4d50604ea49ed838d3641f268ca6e25c9c4b6b72", + "0xb52554c017388b099804abbe565346591a086d9979e10140ddaccc0a3680e506db775d7cbeafde67563adf0f09f5c2420caf19629f4e8f03e6fe02e9416ecd5269989e482b90004a083967d1141387eb74865bac6bd17e7a6d5f58225e52d4b7", + "0xb520ff694520919023d44d53f98a7de2f78ff37b2d9193dcaa35556a6a0febf767781a4c961dce7c804bfdf81935f8f0082865253da52e79dfa1c5ff74d61495b2da76e167d46114709e877a7791a3a95e33a42f56b83f5f5afe271c67ae997c", + "0xb721401983440797a03d5b99f2088a0b249aa911969c34dd6c615b0060325da555d2ad99d931170c0868b0488a2234a4114cc0013d5163b833f5c45c5eb536421c016cf85788390176bb2dc4c196d6be26bbbfceae048b82f0d8039222e71c94", + "0xacd9d833ba0a8cbd8d1ba939a11ea0fa5607e1bc6e693ec318bdb097aedd042d76e695dcebebd142e2e4ac30b1905dff03ec36d9cc70577e4dbe5e9ed7c20c7afb13a7f0155f203c6b83b9f1ad3d20a0d4aef0fbbbcf466ffc1bcd482bc2f5e0", + "0x8cc1795de015f2b0e72116f169f3b4624b7738ceebea354e0bd9051c27b86f647ea36cad57ea6884c1a8adf9b45cd83514fa687e68878bbd613d793aa10986d5a0411f081689229e0d72133b3667b9f3f1a02211d0e680564eb1ea43393e1f36", + "0xaa9281c61113c343a108de1036570feefc72fb7a96ff11f73024de12b83f29631f5a8a5900e6f10b15227c6f7462881511271bf785ebdf95ce288100e5dab391f664f6ff76c72b65b34479a4f43e5e8eba292209d6654157286ad3242ac342db", + "0xaaf16866275082e59d415db317aa874267d048ee405a553e852e6d175711d31a1fee99912345915bce121f43bc3e00d81338e5fcd3c8a1012fb4f172a9fe15622dd368b4d9d5cb60d189f423b071791fe26cea7676aca8df07965cacf80b0cd0", + "0xaccc80b3d8a6ffa648487a3d3c0ce1aeeb5401edf3cf2e385ea4a6d5fc110054fcce38f01f1da7141bbed30eb7a0a6810c82212bbb9da75d6033082dbcf6bc6a5791f85aa0f045a10da5de015edbf369b4d23b32b0c058962d2ee88e6911f994", + "0x83f1089395a16077738cc7c9a6d6a3dc9033aac4abc508af5a1f007ca92e1a80b2e6f2dbda7fdcf0d5646de790a6201d0a9cfbcb6620a1426600e3a6a425ec004384f49fb9dcd166691a47177d45dcbcb761a11d46220b0aa09fc946131f7aa5", + "0x9246bb586d43cb817c2e15ed609156e9f1cd284ba2f4797bbfa51c0341e1ba382eaac059aa9f63fb88d228a1a932839a171e7c7d00199dc7c4d6c5ea038a02cbc3cc5297c70401520e70ebbcffacd6a703f62896f3c788f94dde3c33ab0ecbdb", + "0xa316cb7c74feb0563c56cc79015e2774fbeca458bf8e9fb07894f9d6bcd73f7fb9428e87c816e5629e4bf7f3ec567fbc091549471b75492dde08217cb334b716b4582b24384586e53388873a78a90ec01bd7c3bace9cfc52161467df16e27c33", + "0xade18c74bbe60d1d69f4a570f8e5fd8696c26cc9e02829040b6b14cb9c49a4b3263b5bd5e16ec0b29010b4be054c16ab09304e23442af7d7f5fcc60bc6c5634ab6e4aed7ef334b2785e4c7672d59a687278e42d310342db5e5975d716e6d1595", + "0xb7728800bb2039acf228fa3d8028569c426cb85d28b2b5820bbef938d5ca8c4df981d3e01a309e26ca101e8295d0f6990c03b8c239798323575874a4ee5bfe46cfe99b9657189142aacd8f8d1f26cf4c0e73c6397c31ba8f18102b9ea315b638", + "0x8fb14f2a9be193f54977ecd3021663108ea143627b9a9d9faff85d1a86b855f6c437eab435fad3304f245bd7732af07f1173494cdb802fb96e85d2db89e1643206e183f3b228ca8d3f586e71aa9308eaf0223100bf07942fc39e465016d1f775", + "0xac1e025e53d98fdb3380489dce82d9d4bd3a6c98f0a523b841cb09a6f26ddd4d22efd98776e78d10fd996995fd00e81e08d3c25dd14a54b25a9d483677a24bbb8d1cb41a443b2c71038e6893b1b30f70758424e0f2039a48060191389033ef55", + "0xa4c017311b9e930868132527a9849072b91db04fd36c619ae39c98da9e2174e6201d3c2ff1246c06b1b6815bbf3ea4a1116564f55ee2fe4c4d655e2294c0ded842cba209c255ca3d7b7f82d162f97890dfdeed087aa2f87cbfc61d61815da39d", + "0x89516315a3956b455843c2555248bd94dcb19993060fe75fdd51f7aa9c9147ab13997d8a98036a8f04bee5c91d78d2990907e35a52537a8ab3ed15f1a71afdcd38044a5b6e93f662b9d36c16933a881927cacae668c4c06ee6f004c9e3989bad", + "0xa1e78a011e210400c68ca76045f7da74119bff3cbe382efd2bd2ac76567c52d68d75536a91999d084043e1ce2d07d02e0b69fb99924101d2543521747536fbc51b0454aa9a4cbbec101121f597863a5c0fee2ca5eab35dff9b9085bef8b2b0d0", + "0x830fd8d083e39153ecab43cabb22e29d7b44a55fba467af4ddd3f069439d2972ef53c3518de788f96b3f4f64963987d0155ba27afc28643af3de8e476ff515a68285728167408f45d99e574680bda6bacdd4322e587e4aa99386e035c0e931ad", + "0xb89584da22237e3061d991b1a55a5e55dc637b8b671130d304587729348138ef87885180310efe9f9f6d3580b9d7fdcf0649e8a79d2dec8c25a9f53df0fac5d517db999029cbfdd7c2cbd3e9a5503e5d267d3d8ad752335915c92b850b14bafb", + "0x959b8030733799882c5e3735479924b013756e57b893f9792bab4043e2d362d77cf308166d782e3989caa771b8a0c0a01302cb7b5e8ca12e2d6cebd59d4cd173c9dc25f438bac597fab17b4ff44997a489c168e7204b7d7c21d0938f0a2e3b51", + "0xa0a9e5503d9afe0027891dab890c687fd5f5fac5741418490c64d7c15f59533dd603a50163c79402afa61bd02de486761983c94501da17e6bbe78c497f2122210071602f578adc0ebe7a4679f87fe77e09c8c122de69105f13455fea25f08e6f", + "0x9811487283ad620cd7c9b303ae2f348d0e6f5ee17b504baaa817ae207adb912a00d3cc36dbf48745eb899e6b6e22f09f0f9ba29d949ecd7350fbbfe87a8c7cdd5d0e687fc807751d07634aaf7c38baf3b24a0670c38fa6ccd7431436fc95525f", + "0x8a13aa5071c526e560def7d8583393942f07d88c9d8d26c98738fd65f57af2e3326dbb1edff0f39fe98eda4a13ed4fd71844254b954690154c4804e1c4a53df9dc4643f4b7b09d0860070f6b2318d0d63d28fb56bf5b6ff456a18dfc72fdfbbe", + "0xb9c90ff6bff5dd97d90aee27ea1c61c1afe64b054c258b097709561fe00710e9e616773fc4bdedcbf91fbd1a6cf139bf14d20db07297418694c12c6c9b801638eeb537cb3741584a686d69532e3b6c12d8a376837f712032421987f1e770c258" + ], + "setup_G1_lagrange": [ + "0x8d0c6eeadd3f8529d67246f77404a4ac2d9d7fd7d50cf103d3e6abb9003e5e36d8f322663ebced6707a7f46d97b7566d", + "0xa0d2392f030681c61c2a867862917e10f7678d882034bb89af3db87e6ab3883a304034643dc9688a04e41a5b831582bc", + "0x94298073048d70c74f36685e547d04b7311479daa05912e18ead64b2099a194bf48ec344273d58daf0b86b1d8f1d318d", + "0x85c4063d13499013dc2ccaa98c1606763e6b1e8cca20922d4cec12ecbaf006ea81ffabe6596d1ac7ba1daf7e63e30898", + "0x84c64bce36c6b5145c6880113366025ab9a8f88e3948d374e27be8b8f9f87402c70fec9b3c621a2d1d26764a84370d0c", + "0x8b206c823acf5294552ee54579fac0f45ea15bd273dbacd63b88cd7cddbcce23b56e52f8ea352e1e1d7dcd9b3991b413", + "0xb70aaa4038ba3f5ff306c647b4392d004950c53ad8f6713b5c9c21ac99f5c56cf57323dac500a1f4e9507c4746b07a2f", + "0x895f6d1fc70b52f838d81b24f4840729cd5988b649e9d6e6f6dbac4281d8818f39ebdae7e6ea139d7f98a832bd6f29f1", + "0xa71a2832bbaade974c9ef7505dfa24e1ba466a9951b7c2db56886be31c9c7b871f3ee76cb1fcc1aab4b906d6502bc9b5", + "0x9530ba64a21e27834609c00616bc63e8fc2dc7800e478ad728ec39c624f65bbc62cb48f59decb7fbf605ce1920d02622", + "0x8d0609affaf8619bb2f6c80699e5bc7783becbd5973630cdd227ae52d6d701c45f4270becca97701b40279fab588cf64", + "0x8f5d5b4c3bb8dc9a19e5a0f84df6322a79a00c7783c86254197d313a5b35d3965a1f7c0b9c4e39ec1e8f5d02d3aa0862", + "0x96aa47a3ba20b1cfe81eb26bef503225037fdf4c9df53bea1b520841875cd1db6aa8e0f34685da08b55a3ce7289e6de0", + "0xb4c27ee3f4b8c0031837160f0a75632f5b51b5850d52b530096443f54c2b264aeccc5c61b4fcc8de7074475f354fa0d8", + "0xacfd735cda20be1d6f425a7886629c91732fbb5a4e0350ca740a8fb5b39f2001071cec0b2a0f6ca35e1f35a5ea18d00f", + "0xae44d87b1d16d59504c602cbacde2c2791f1520391ca50154e6036d3953ca466cf93d6537da2adb729e6f9f4ffa87853", + "0x97b492872ce44941ea4668ffca83b82fac0f4021bd47e0a5ffeaaacb1b3fc924ee4d53b99f7bcafe0985caf0fbe5d1d3", + "0xb3fbe2f9103d293f49c6c6016d5913f041c9113295397388111a0fdf4245d8edd6e63b9a1a1c9c8f868d6e1988116880", + "0x805efa08fd2046c44c427b225c17bed8a1eb3320cdf94026fdc24c6d345a6cfebfd7475f85d2d1bf22018ca72d2761d3", + "0x9888bae0d83077d1dfde82fdffb1195565c31c519b80cba1e21aba58ee9ccb5677f74bfde13fa5723026514a7d839661", + "0x922e19d2646ba90c9f56278bddf74621cc4518ae2f042fb8245843e87cd82724c6d7c9a99907ac6de5f2187fd2e77cbe", + "0xa38f0e1faf97dd1e0804b44e4d150dbfa48318442d1c5255eb0c14ea56b50502f3c7cb216a0336e7c140398088dc01cf", + "0x93598ea391c8735799a1d4cd0456f34994ccdf4883fad57419f634f30fee595938bc66b066dade9ae52578818c00d899", + "0xa528dc920734cfaee9feacbc0baa5b73befb1ec6fbd422fcad09a9c1f8f8c40b5ea332b2cf04dc1d6d921e9da9ddfeb4", + "0xb38d45316bf78d11e796a34ee535814e6cde0e642f14108329c5b21f4fec18cd61f84a3025824bb8dc4cbd26b2ecc9bf", + "0x8eec35a7404c9a35dc6ad0260b7f0f7fd1bfe92a2e08bc72548b99ed9acdc378728a8ea9c6879a6e47e37edb0d28c193", + "0xa68a4446274ccd947c61bf736c5219dad680b99c6085a26719793e0d9dab26d5f8a0b28e71be6e1b9ea4ae39139f7f57", + "0xa0acb543f41ad12e3b2e096629ccdd719a001d0ff53bb151e9a37aa57852f7275a7bbd06dc2a06af9144524548164af5", + "0xb271e74cdbcf8b9143f8472174bdb068c23308ea807c60a554c185f7be6f231aac13347139837514171a876dfac5baa5", + "0x8195a460719000cd1df379ebbf7918f71301a50a2fa587505cc5b8c4534c3d2343f63d28e7ee991d7a1cebb15d380696", + "0x96202b60426773e8731dcbedbf613477f65940a19fb4be0f4f742b0c76ae9d88ecdb6d36cd4f12bb404dd5d360c819e2", + "0xb0a80fe60b71ca9e80157138de8787b8a786326179604b8a15a744e52662645987e5f859ef5c76492d560daf4624b9a7", + "0xa331ea8adf87daa5e2d458d0113c307edae1a84927bca7d484aca5f8c1b6378ab42981c44b0d916d7249f4b475f926f1", + "0xaa1a8f59ae0912abf191ea7e209ff401628278dfb2269db6d87cf33bd52af3dbffbe96513a8b210e965c853a554b787a", + "0xac4f4a0e1b1a155e1f22a9085b0b047fe54c8437dbbb8e9720fd6b0cdd76557d19ca2e885a48890f0247b1a72be0e287", + "0xa428465505eac7b9660eb0d495a7a00c8cc238de3a02ebbd2eb07e502e9868086e9584b59953cf1480c0b781295db339", + "0xb7b77e21e08f6357cbd3dcd3035c3e8ec84cdfa13c7baef6c67e0ef43095e61fd549694263d7def8b8adc3a0fdcc7987", + "0xabb991d17c5bdd264c592c55101e265cb3210c4157aee4079173fd51da1e0199eed1d6c890aab95817ec078561d771af", + "0x846a8e4f801faf5fbec078b09c362ee30a00b2b58a4871744d03cd118b913464233ff926e52b0c75fbfcf098ad25a1e6", + "0x947e91ffa32f38c1ccb72cca4bfabaee9e63ab74a16f034cabba25e462f7331ebe5a7ba393f69e91830415fa75b1b52e", + "0x8dc5e26adc693f4e300cab7385edca1a2fe14c8ee6dc0cd6d013cb5aa154dc380e9e81e259cbc59c1f38f7c4a57f1c7d", + "0x9818ef6605d6ea3b7bf4da5c6d6d8ed540bb94df4d14c974e1b79ed2fd1a0b897b8cf1ff671a181a697effd66b1644a5", + "0xb5eab6baf03af994fc32cc9dce388394c18c01cdafe7909fde948f3e00a72dc8f30d15977d0f114bd7c140f5f94cf005", + "0x83b2e9858d3b929f9a2ad66a91a2c0c44d15d288c17c12a1614301a6f2d61d31eaa540ca7781520fe4420afae0ec0208", + "0xab338fbd38bce4d1b7a759f71e5e5673746c52846eff3d0b6825e390aeeca8f9f123ee88c78fe4d520cc415cbae32bf1", + "0x81adb6322b8db95d1711304e5b59f37640ca88c03e6c7e15de932be5267dff7351fa17664113ecc528e8920f5bfdc0d1", + "0x89e2e0c0d769e4107232df741678a6bacb041d0154385450aaca8be9c3c18c42f817373962e7569d33935c35666a8a6a", + "0x8f0756fea8b34a2b471ec39e4448a6a6935e5432ec2859d222964a4c82777a340e1d702777aeb946fa405afc0438221a", + "0xa2bf90c505a6f03b3dd09d04e1e7cf301fe3415b273e263f15fdfe5d0e40f619b95e8bf00916d3eaa7d7f8c0bae41c8e", + "0x91d5c76b5542637588cd47279d0bd74a25dbda0d8ec0ff68b62d7e01e34a63fc3e06d116ee75c803864b1cf330f6c360", + "0xa9958c388d25315a979566174b0622446335cb559aff1992bd71910c47497536019c6854d31c0e22df07505963fc44ff", + "0x91d82b09d5726077eed6c19bcb398abe79d87ce16c413df6bf5932b8fd64b4c0fd19c9bf0fa8db657a4a4d4c0d8f5a2d", + "0xac6e0a86e0ee416855c3e9eef2526c43835f5245527ed0038bc83b4fcadb4ea5beb91143cc674486681a9f0e63f856b1", + "0xaaf00d6efd0c6efb9f7d6a42555abec05c5af8f324e2e579fc2ac83bdc937cc682d9bc2ffd250619c8bb098b8c84db80", + "0x963f5fcd8476d0dbeb03a62cde40e3deee25f55e7ded7572d8884975f38eddc5406fc4b0adff602a1cca90f7205a7fdc", + "0xa3805ee01512f644d2679511bd8607890ee9721e75ac9a85ab9fd6fceb1308d5b9b0e9907686b4e683b34aed0f34cd81", + "0xa483d7708465cd4e33b4407fe82c84ef6bc7fa21475d961fe2e99802d0c999b6474ef7a46dd615b219c9c7e9faec45ee", + "0xb6b5f9456f12d6781c41f17cdc9d259f9515994d5dee49bb701a33fa2e8dcbb2c8c13f822b51ad232fc5e05bff2f68ef", + "0x8766b721b0cf9b1a42614c7d29aad2d89da4996dc9e2a3baeba4b33ca74100ab0b83f55c546c963e3b6af1dcf9ca067c", + "0xac5e8da1154cf4be8df2bbd2e212b7f8077099b2010c99e739441198f65337c6f7ef0d9136453a7668fde6e1389c32c7", + "0xa9d6d2c8845e5f1fec183c5153f1f6e23421e28ce0c86b0ce993b30b87869065acad9e6d9927d9f03c590852821b2f9c", + "0xa320ca07c44f7ea3ff858fe18395a86f59559617f13ec96d1e8b4a3f01d9c066a45c8d8cf8f1f14a360bb774d55f5f18", + "0xb3adb00e1312dce73b74fbd2ea16f0fb0085bd0db10772e9c260e9ed9f8829ff690e3dfffacaddc8233d484bb69778b3", + "0x87b0c8d8a167d5199d0b0743c20fb83ec8a1c442f0204bcc53bf292ba382bef58a58a6d1e2467920e32c290fdc6dae7c", + "0xa74fa436a5adc280a68e0c56b28ac33647bdfc8c5326f4c99db6dbd1b98d91afb1f41f5fffd6bcc31c1f8789c148e2db", + "0x8a37349e4ba7558965077f7f9d839c61b7dcb857fcc7965c76a64a75e377bfea8cd09b7a269ce602cc4472affc483b69", + "0x8af813f62c5962ff96bf73e33f47fd5a8e3e55651d429e77d2ce64a63c535ecc5cfc749bb120c489b7ea1d9b2a5d233c", + "0x833021445b7d9817caa33d6853fa25efc38e9d62494d209627d26799432ea7b87a96de4694967151abc1252dd2d04dfc", + "0x8f78a715107e0ace3a41bff0385fd75c13bf1250f9e5ddecf39e81bacc1244b978e3464892f7fb2596957855b8bf9fc7", + "0xaed144134dc1cc6c671f70ebe71a3aadf7511eea382969bc5d499a678d2d8ce249ebf1a06b51183f61413eba0517012b", + "0xb39a53e82c5553943a5e45bc5116d8672ec44bed96b3541dead40344b287a7b02dbf7107372effb067edd946f47de500", + "0xb383844c3b20a8bc06098046ec6b406df9419ad86fac4a000905c01325426903a5e369af856d71ccd52fea362ed29db5", + "0x83815a7098283723eec6aa6451b5d99578bf28a02971375a1fe90c15a20963e129372ac4af7b306ee2e7316472c5d66d", + "0xb426b4e185806a31febd745fa8d26b6397832a04e33c9a7eb460cbf302b4c134a8a01d4e5e40bc9b73296c539e60b3ca", + "0xa6cabf8205711457e6363ef4379ebc1226001e1aaea3002b25bfd9e173f4368002f4461e79eeb9f4aa46f1b56c739ab9", + "0xa6e88ab01282313269cd2d8c0df1a79dada5b565d6623900af9e7e15351de2b0105cc55d3e9080e1e41efe48be32a622", + "0xb2b106db3d56d189ea57afa133ae4941b4eb1dc168357af488e46811c687713fc66bbd6f8500bbd13cdb45cb82c14d1d", + "0xb3a74780ff949d19e6438db280e53632c60dc544f41320d40297fe5bb7fcee7e7931111053c30fb1ed9019ab28965b44", + "0x8c67f32b9fdc04ec291cc0d928841ab09b08e87356e43fbbf7ac3ff0f955642628f661b6f0c8e2192a887489fddf07bb", + "0xb3be58bd628383352e6473fe9a1a27cf17242df0b1273f5867e9119e908969b9e9e7e294a83b9ea14825003cb652d80c", + "0xa867acf6ab03e50936c19a21d4040bfd97eb5a89852bd9967da0e326d67ce839937cab4e910d1149ecef9d5f1b2d8f08", + "0x8006b19126bd49cbb40d73a99a37c2e02d6d37065bbe0cfcee888280176184964bd8f222f85960667c5b36dfaee0ee35", + "0xac50967b8b7840bf9d51216d68a274f1d3431c7d4031fbac75a754befbbb707c2bb184867db6b9d957f3ba0fd0a26231", + "0xb5a794c928aff0c4271674eb0a02143ed9b4d3bc950584c7cd97b7d3c3f2e323798fd5ccc6fcc0eb2e417d87f4c542a2", + "0xa2ca3d6509f04b37091ce6697672ee6495b42d986d75bd2d2058faa100d09fd0a145350f2d280d2cb36516171bd97dbf", + "0x92cfa293469967a9207b37cd70392312faf81b52963bfbad5f9f3da00817d26e10faf469e0e720c3bb195f23dda8c696", + "0xa0dd5135da0a0e33fa922c623263b29518d7fa000e5beefc66faa4d6201516d058f155475c4806917a3259db4377c38a", + "0x8fc3ae8ea6231aa9afb245a0af437e88ebca2c9ab76850c731981afba90d5add0ea254053449355eccf39df55bd912ed", + "0x9727afe1f0804297717cec9dc96d2d27024a6ae6d352fee5d25377ee858ee801593df6124b79cb62ddc9235ec1ade4ac", + "0x8bcb2c53fcaa38e8e2e0fd0929bc4d9ddce73c0282c8675676950ff806cb9f56ebd398b269f9a8c2a6265b15faf25fca", + "0xa8bd9007fbbdd4b8c049d0eb7d3649bd6a3e5097372fa8ea4b8821ba955c9ef3f39ac8b19f39d3af98640c74b9595005", + "0x92c7e851c8bd6b09dfcbfdb644725c4f65e1c3dbd111df9d85d14a0bb2d7b657eb0c7db796b42bf447b3912ef1d3b8c3", + "0x98c499b494d5b2b8bea97d00ac3a6d826ab3045bb35424575c87117fc2a1958f3829813e266630749caf0fa6eeb76819", + "0x8df190d71e432fe8691d843f6eb563445805c372eb5b6b064ec4e939be3e07526b5b7f5a289ede44ae6116a91357b8b1", + "0xb5010243f7c760fb52a935f6d8ed8fc12c0c2f57db3de8bb01fdeedf7e1c87b08f3dd3c649b65751f9fd27afa6be34c7", + "0x889c8057402cc18649f5f943aed38d6ef609b66c583f75584f3b876c1f50c5dc7d738dc7642135742e1f13fa87be46c1", + "0x996087337f69a19a4ebe8e764acf7af8170a7ad733cd201b0e4efde6ea11039a1853e115ad11387e0fb30ab655a666d8", + "0x902732c429e767ab895f47b2e72f7facad5ef05a72c36a5f9762c2194eb559f22845bbb87c1acc985306ecb4b4fbbf79", + "0x8519b62a150ea805cdfc05788b8d4e797d8396a7306b41777c438c2e8b5c38839cfec5e7dc5d546b42b7b76e062982a7", + "0x862a53ba169e6842a72763f9082ff48fbfbb63129d5a26513917c2bca9ad6362c624ce6fc973cf464f2eb4892131eb04", + "0xb86cd67c809d75fdb9f1c9453a39870f448b138f2b4058d07a707b88bb37f29d42e33ce444f4fbe50d6be13339cae8a6", + "0x8cf5d8365dbbafc0af192feb4fc00c181e2c3babc5d253268ef5564934555fb1e9b1d85ec46f0ca4709b7d5b27169b89", + "0xb48f11a1809ec780bf6181fae3b8d14f8d4dc7d1721128854354be691c7fc7695d60624f84016c1cea29a02aaf28bfbc", + "0x8b46b695a08cb9a2f29ab9dd79ab8a39ec7f0086995b8685568e007cd73aa2cd650d4fae6c3fb109c35612f751ba225e", + "0x8d2f9f0a5a7de894d6c50baceb8d75c96082df1dcf893ac95f420a93acbbf910204903d2eb6012b1b0495f08aaf9992f", + "0xb334db00a770394a84ec55c1bd5440b7d9f2521029030ef3411b0c2e0a34c75c827fd629c561ea76bd21cd6cf47027f4", + "0x96e9ff76c42bcb36f2fb7819e9123420ed5608132f7c791f95cb657a61b13041e9ba2b36f798a0fdb484878cbe015905", + "0x99f8d701e889abd7815d43ba99e0a85776ec48311fa7cb719d049f73b5d530fa950746ffbbb7beb9e30c39d864891dc2", + "0x98169c20df7c15d7543991f9c68e40ac66607cbd43fc6195416e40009917039357e932d6e807f3a40bc4503ad01ae80a", + "0x84bd97dd9e4e2ba75d0dee7d4418c720d4746203d847ce2bdd6ed17d492023df48d7b1de27e3f5cb8660c4bb9519ae1b", + "0xa54319e06db7f5f826277a54734a875c5b3fd2fa09d36d8b73594137aa62774b7356560157bc9e3fdf1046dc57b6006a", + "0x90cfff7cd4e7c73b84f63455d31b0d428cb5eee53e378028591478511985bcc95eb94f79ad28af5b3bed864e422d7b06", + "0xa11c23cc8dce26ac35aea9abe911905a32616a259fa7da3a20f42dc853ad31b2634007aa110c360d3771ff19851f4fb4", + "0x9856fbee9095074ad0568498ff45f13fe81e84ea5edaf04127d9ee7e35e730c6d23fa7f8f49d092cf06b222f94ab7f36", + "0x818862dec89f0dc314629fffbca9b96f24dfde2d835fa8bde21b30dc99fe46d837d8f745e41b39b8cf26bfe7f338f582", + "0x831819d41524c50d19f7720bf48f65346b42fb7955ee6ecc192f7e9fed2e7010abccdfdeac2b0c7c599bc83ac70be371", + "0xb367e588eb96aa8a908d8cc354706fee97e092d1bc7a836dbcc97c6ed4de349643a783fb4ddf0dec85a32060318efa85", + "0xb7aaef729befd4ab2be5ec957d7d1dbe6178de1d05c2b230d8c4b0574a3363e2d51bc54ea0279a49cc7adffa15a5a43a", + "0xae2891d848822794ecb641e12e30701f571431821d281ceecbccaaa69b8cd8242495dc5dbf38f7d8ed98f6c6919038aa", + "0x872cf2f230d3fffce17bf6f70739084876dc13596415644d151e477ce04170d6ab5a40773557eeb3600c1ad953a0bfce", + "0xb853d0a14cef7893ba1efb8f4c0fdb61342d30fa66f8e3d2ca5208826ce1db5c8a99aa5b64c97e9d90857d53beb93d67", + "0x910b434536cec39a2c47ca396e279afdbc997a1c0192a7d8be2ba24126b4d762b4525a94cea593a7c1f707ba39f17c0c", + "0xb6511e9dea1fbccedd7b8bb0a790a71db3999bd4e3db91be2f1e25062fae9bb4e94e50d8ec0dcc67b7a0abce985200b2", + "0x936885c90ebe5a231d9c2eb0dfd8d08a55ecaa8e0db31c28b7416869b3cc0371448168cbec968d4d26d1cb5a16ebe541", + "0xb71c2ac873b27fe3da67036ca546d31ca7f7a3dc13070f1530fce566e7a707daeb22b80423d505f1835fe557173754f8", + "0x85acb64140915c940b078478b7d4dadd4d8504cde595e64f60bd6c21e426b4e422608df1ed2dd94709c190e8592c22d7", + "0xb5831c7d7c413278070a4ef1653cec9c4c029ee27a209a6ea0ad09b299309dea70a7aef4ff9c6bdeda87dcda8fa0c318", + "0xaa0e56e3205751b4b8f8fa2b6d68b25121f2b2468df9f1bd4ef55f236b031805a7d9fd6f3bba876c69cdba8c5ea5e05f", + "0xb021f5ae4ed50f9b53f66dd326e3f49a96f4314fc7986ace23c1f4be9955ec61d8f7c74961b5fdeabcd0b9bccbf92ce8", + "0x88df439f485c297469e04a1d407e738e4e6ac09a7a0e14e2df66681e562fdb637a996df4b9df4e185faab8914a5cef76", + "0x8e7ae06baa69cb23ca3575205920cb74ac3cda9eb316f4eef7b46e2bff549175a751226d5b5c65fe631a35c3f8e34d61", + "0x99b26ff174418d1efc07dfbed70be8e0cb86ac0cec84e7524677161f519977d9ca3e2bbe76face8fe9016f994dafc0ff", + "0xa5f17fe28992be57abd2d2dcaa6f7c085522795bfdf87ba9d762a0070ad4630a42aa1e809801bc9f2a5daf46a03e0c22", + "0x8d673c7934d0e072b9d844994f30c384e55cec8d37ce88d3ad21f8bb1c90ecc770a0eaf2945851e5dab697c3fc2814a9", + "0xa003ed4eb401cfe08d56405442ca572f29728cfff8f682ef4d0e56dd06557750f6a9f28a20c033bc6bbb792cc76cc1a8", + "0x8010408f845cf1185b381fed0e03c53b33b86ea4912426819d431477bd61c534df25b6d3cf40042583543093e5f4bb44", + "0x9021a1ae2eb501134e0f51093c9f9ac7d276d10b14471b14f4a9e386256e8c155bef59973a3d81c38bdab683cd5c10e0", + "0xa5abf269ceabbb1cf0b75d5b9c720a3d230d38f284ed787b6a05145d697a01909662a5b095269996e6fa021849d0f41f", + "0xb4b260af0a005220deb2266518d11dbc36d17e59fc7b4780ab20a813f2412ebd568b1f8adc45bf045fcbe0e60c65fd24", + "0xb8c4cb93bedbb75d058269dfccda44ae92fe37b3ab2ef3d95c4a907e1fadf77c3db0fa5869c19843e14b122e01e5c1f4", + "0xac818f7cdecc7b495779d8d0ff487f23ab36a61d0cf073e11000349747537b5b77044203585a55214bb34f67ef76f2d2", + "0x86215799c25356904611e71271327ca4882f19a889938839c80a30d319ddbe6c0f1dfa9d5523813a096048c4aef338cd", + "0xa9204889b9388bf713ca59ea35d288cd692285a34e4aa47f3751453589eb3b03a9cc49a40d82ec2c913c736752d8674d", + "0x893aecf973c862c71602ffb9f5ac7bf9c256db36e909c95fe093d871aab2499e7a248f924f72dea604de14abfc00e21c", + "0xb8882ee51cfe4acba958fa6f19102aa5471b1fbaf3c00292e474e3e2ec0d5b79af3748b7eea7489b17920ce29efc4139", + "0x8350813d2ec66ef35f1efa6c129e2ebaedc082c5160507bcf04018e170fc0731858ad417a017dadbd9ade78015312e7f", + "0x83f6829532be8cd92f3bf1fef264ee5b7466b96e2821d097f56cbb292d605a6fb26cd3a01d4037a3b1681d8143ae54d7", + "0x87d6258777347e4c1428ba3dcbf87fdd5113d5c30cf329e89fa3c9c1d954d031e8acacb4eed9dca8d44507c65e47e7cd", + "0xa05669a1e561b1c131b0f70e3d9fc846dc320dc0872334d07347e260d40b2e51fdbabeb0d1ae1fb89fba70af51f25a1a", + "0x819925c23fd4d851ea0eecc8c581f4a0047f5449c821d34eccc59a2911f1bd4c319dab6ece19411d028b7fdedece366b", + "0xb831b762254afd35364a04966d07b3c97e0b883c27444ff939c2ab1b649dc21ac8915b99dc6903623ed7adaae44870ac", + "0x93ec0190f47deffe74179879d3df8113a720423f5ca211d56db9654db20afe10371f3f8ec491d4e166609b9b9a82d0d4", + "0x8f4aa6313719bcfad7ca1ed0af2d2ee10424ea303177466915839f17d2c5df84cc28fcef192cbb91bb696dd383efd3b2", + "0x8d9c9fdf4b8b6a0a702959cf784ad43d550834e5ab2cd3bebede7773c0c755417ad2de7d25b7ff579f377f0800234b44", + "0x99d9427c20752f89049195a91cf85e7082f9150c3b5cb66b267be44c89d41e7cc269a66dacabacadab62f2fa00cc03be", + "0xb37709d1aca976cbbf3dc4f08d9c35924d1b8b0f1c465bd92e4c8ff9708e7d045c423183b04a0e0ab4c29efd99ef6f0e", + "0xa163f42fb371b138d59c683c2a4db4ca8cbc971ae13f9a9cc39d7f253b7ee46a207b804360e05e8938c73bf3193bab55", + "0x87a037aa558508773fc9a0b9ba18e3d368ffe47dfaf1afacee4748f72e9d3decc2f7c44b7bf0b0268873a9c2ef5fe916", + "0xa1f20cb535cc3aebd6e738491fe3446478f7609d210af56a4004d72500b3ec2236e93446783fe628c9337bcd89c1e8e1", + "0x9757aa358dfbba4f7116da00fe9af97f7ac6d390792ea07682b984aa853379ac525222ac8a83de802859c6dec9182ef7", + "0x815daca1eded189ec7cb7cbc8ad443f38e6ddb3fb1301d1e5a1b02586f1329035209b7c9232dc4dff3fc546cb5ac7835", + "0xaed86dfaf9c4f0a4b2a183f70f9041172002a773482a8ebf3d9d5f97d37ee7c6767badfda15476b3b243931235c7831c", + "0x8d032e681e89e41b29f26be02f80030fa888f6967061d2204c1ebb2279a3211d759d187bce6408c6830affa1337fb4e0", + "0x877bff5c2db06116f918a722b26422c920aeade1efa02fa61773fca77f0ea4a7e4ee0ecaaa5cfe98044c0ff91b627588", + "0xb9ee5310d0996a10a242738d846565bdb343a4049a24cd4868db318ea6168a32548efaf4ab84edfbf27ce8aec1be2d1c", + "0xb59f6928167323037c6296dd7697846e80a7a4b81320cfae9073ebd2002a03bdf6933e887f33ad83eda8468876c2c4fb", + "0x8167686245149dc116a175331c25301e18bb48a6627e2835ae3dd80dd373d029129c50ab2aebeaf2c2ccddc58dcc72ec", + "0x82b7dcc29803f916effb67c5ba96a1c067ed8ca43ad0e8d61a510ab067baefd4d6b49e3886b863da2de1d8f2979a4baa", + "0xb43824cd6f6872a576d64372dde466fef6decdbb5ad5db55791249fde0a483e4e40c6e1c221e923e096a038fe47dab5e", + "0xab1e9884cf5a8444140cf4a22b9a4311a266db11b392e06c89843ac9d027729fee410560bcd35626fd8de3aad19afc4a", + "0xa0dbd92a8d955eb1d24887ca739c639bdee8493506d7344aadb28c929f9eb3b4ebaae6bd7fd9ffe8abb83d0d29091e43", + "0x8352a47a70e343f21b55da541b8c0e35cd88731276a1550d45792c738c4d4d7dc664f447c3933daabd4dbb29bb83be4a", + "0x8ce4a1e3c4370346d6f58528a5ef1a85360d964f89e54867ba09c985c1e6c07e710a32cdda8da9fa0e3b26622d866874", + "0xb5e356d67dd70b6f01dd6181611d89f30ea00b179ae1fa42c7eadb0b077fb52b19212b0b9a075ebd6dc62c74050b2d2f", + "0xb68f2cd1db8e4ad5efdba3c6eaa60bfcc7b51c2b0ce8bb943a4bc6968995abe8a45fe7f12434e5b0076f148d942786be", + "0xb5c7b07f80cd05c0b0840a9f634845928210433b549fb0f84a36c87bf5f7d7eb854736c4083445c952348482a300226a", + "0x8cfd9ea5185ff9779dee35efe0252957d6a74693104fb7c2ea989252a1aa99d19abaab76b2d7416eb99145c6fdb89506", + "0x8cc8e2c5c6ddee7ef720052a39cab1ecc5e1d4c5f00fb6989731a23f6d87ac4b055abb47da7202a98c674684d103152a", + "0x8c95394c9ed45e1bf1b7cfe93b2694f6a01ff5fed8f6064e673ba3e67551829949f6885963d11860d005e6fabd5ac32c", + "0xadf00b86f4a295b607df157f14195d6b51e18e2757778fde0006289fabba8c0a4ab8fad5e3e68ddbb16ccb196cc5973f", + "0xb1714b95c4885aac0ee978e6bbabbc9596f92b8858cb953df077511d178527c462cbe1d97fdc898938bae2cd560f7b66", + "0xadf103f4344feb6b9c8104105d64475abc697e5f805e9b08aa874e4953d56605677ef7ff4b0b97987dc47257168ae94d", + "0xb0ce6ede9edb272d8769aed7c9c7a7c9df2fb83d31cc16771f13173bcdc209daf2f35887dcca85522d5fdae39f7b8e36", + "0xad698d1154f7eda04e2e65f66f7fcdb7b0391f248ba37d210a18db75dafd10aedc8a4d6f9299d5b6a77964c58b380126", + "0x904856cd3ecdbb1742239441f92d579beb5616a6e46a953cf2f1dd4a83a147679fc45270dcac3e9e3d346b46ab061757", + "0xb600b5b521af51cdfcb75581e1eccc666a7078d6a7f49f4fdb0d73c9b2dab4ce0ecafcbd71f6dd22636e135c634ee055", + "0xa170c5d31f6657f85078c48c7bbf11687ce032ab2ff4b9b3aee5af742baecf41ea1c2db83bcba00bccc977af7d0c5c8e", + "0xa9ef1cbb6a7acb54faf1bcbd4676cdeba36013ca5d1ac1914c3ff353954f42e152b16da2bdf4a7d423b986d62b831974", + "0xaa706d88d3bd2ce9e992547e285788295fd3e2bbf88e329fae91e772248aa68fdfdb52f0b766746a3d7991308c725f47", + "0x911a837dfff2062bae6bcd1fe41032e889eb397e8206cedadf888c9a427a0afe8c88dcb24579be7bfa502a40f6a8c1cc", + "0xae80382929b7a9b6f51fe0439528a7b1a78f97a8565ba8cddb9ee4ba488f2ab710e7923443f8759a10f670087e1292c4", + "0xb8962de382aaa844d45a882ffb7cd0cd1ab2ef073bce510a0d18a119f7a3f9088a7e06d8864a69b13dc2f66840af35ae", + "0x954538ffff65191538dca17ec1df5876cb2cd63023ff2665cc3954143e318ece7d14d64548929e939b86038f6c323fc1", + "0x89efa770de15201a41f298020d1d6880c032e3fb8de3690d482843eb859e286acabb1a6dc001c94185494759f47a0c83", + "0xa7a22d95b97c7c07b555764069adaa31b00b6738d853a5da0fe7dc47297d4912a0add87b14fa7db0a087a9de402ea281", + "0x9190d60740c0813ba2ae1a7a1400fa75d6db4d5ce88b4db0626922647f0c50796a4e724e9cc67d635b8a03c5f41978f7", + "0xab07c30b95477c65f35dc4c56d164e9346d393ad1c2f989326763a4cc04b2cb0386e263007cc5d0125631a09ad3b874c", + "0x9398d8e243147de3f70ce60f162c56c6c75f29feb7bc913512420ee3f992e3c3fb964d84ef8de70ef2c118db7d6d7fd5", + "0xb161b15b38cbd581f51ca991d1d897e0710cd6fdf672b9467af612cd26ec30e770c2553469de587af44b17e3d7fea9f7", + "0x8c5d0260b6eb71375c7ad2e243257065e4ea15501190371e9c33721a121c8111e68387db278e8f1a206c0cce478aaa2b", + "0xb54ac06a0fb7711d701c0cd25c01ef640e60e3cb669f76e530a97615680905b5c5eac3c653ce6f97ceca2b04f6248e46", + "0xb5c7f76e3ed6dc6c5d45494f851fa1b5eaf3b89adac7c34ad66c730e10488928f6ef0c399c4c26cbeb231e6e0d3d5022", + "0xb6cd90bdd011ac1370a7bbc9c111489da2968d7b50bf1c40330375d1a405c62a31e338e89842fe67982f8165b03480c7", + "0xb0afcaf8d01f5b57cdeb54393f27b27dc81922aa9eaccc411de3b03d920ae7b45295b090ef65685457b1f8045c435587", + "0xb2786c0460e5057f94d346c8ebe194f994f6556ab2904a1d1afd66c0ff36391b56f72ed769dcc58558ee5efaa2ed6785", + "0x965dbb0cb671be339afcb2d6f56e3c386fb5d28536d61d6073b420ee15dee79c205af2f089fbb07514a03c71bf54b4e2", + "0x90f2003e2286bba9cebff3a6791637ca83b6509201c6aed1d47f27097d383d5c2d8532bff9e3541d2c34259841cf26ab", + "0x902142d1224e1888ebbfef66aaf8d5b98c27927a00b950753a41d1d28a687a8286b51655da9a60db285b20dc81d5ea89", + "0xa5d364448bf0d0849e5104bdaef9cb2cc8c555f5d6d34239c68671fbe1252f7c8c75b83cea10159dee4da73298f39a12", + "0xb013a54c5b99e296d9419ad5c2aaf4545acd34405e57d13cb764e92132cc20d1a14b33e10caf22d898b608670c04f273", + "0xb92976dceda373331804d48a7847f508cafde8d15949df53dbda09d03908678db1e61ee637baad5f05b2b03ea6f5a870", + "0x968bcb308c7ad0813dc9b3170f23f419aecd7b42176f27fac698811795bf42659fea6b04dab4ef43595dcc990622041b", + "0xa9d0a20e9367ea831dccd37f4d97ea75e9aeec952947a7946d95e0d249c94024183ef79a624bdea782469824df0ee4e4", + "0x8521b9667453c3658703e5db365b13f0e0d2331ce611ff1e708f8124d8a81bb5e82871de4a66d45c1a6b0a3901bd901e", + "0xb9c88e76e69b0722c0a2f97e57dbc4a6f7456434cd694e2ff67f4e24740cffa4db03e2b18f07f22954ae7db2286e1fa2", + "0x8400e55aa9ab01d4cc0affd611127b5d8d9a9dbd897f3cb8e2050379983aa54249be17d7b7891977b2515bb44a483f65", + "0x8cbb967b4ed31dc40ea06822a94d54cbfc8845c66fbafa3474c8f5fe1ada97299ed4ca955d9d7a39af8821eabf711854", + "0xb4d266ee3fea264a6c563fd6bed46f958c2d7bd328225f6e47faf41a0916aef3b697574322f8b814dfb2f5c242022bf6", + "0x8f7c72d69a919450215ead660ffa9637642c5306354888d549fd4a42e11c649b389f67cc802a0184d10fdb261351140c", + "0xa5f9e494ea9b2393ec32c48aac76c04158ccef436d4e70ad930cba20c55fbf61e8f239f70b9d75462405c4b6317c71a1", + "0xb3befb259b52a44a6f44345859e315c20efa48c0c992b0b1621d903164a77667a93f13859790a5e4acb9f3ec6c5a3c6e", + "0xb9e4ca259b4ee490d0824207d4d05baf0910d3fe5561ff8b514d8aa5c646417ca76f36ab7c6a9d0fb04c279742f6167a", + "0x98fa8c32a39092edb3c2c65c811d2a553931010ccb18d2124d5b96debd8b637d42b8a80111289f2079d9ebca2131a6dc", + "0xa65e5aa4631ab168b0954e404006ce05ac088fd3d8692d48af2de5fd47edbf306c80e1c7529697754dbbba1b54164ba0", + "0xb94b7d37e4d970b4bb67bf324ebf80961a1b5a1fa7d9531286ab81a71d6c5f79886f8ef59d38ae35b518a10ed8176dcc", + "0xb5ed2f4b0a9ae9ace2e8f6a7fd6560d17c90ae11a74fa8bef2c6c0e38bfd2b9dd2984480633bca276cb73137467e2ce3", + "0xa18556fe291d87a2358e804ee62ddff2c1d53569858b8ae9b4949d117e3bfb4aefce1950be8b6545277f112bebeeb93d", + "0xa0d60b9def5d3c05856dff874b4b66ec6e6f0a55c7b33060cc26206c266017cdcf79b1d6f6be93ed7005a932f9c6a0b9", + "0x801fced58a3537c69c232ce846b7517efd958e57c4d7cd262dbec9038d71246dafad124aa48e47fe84ecc786433747c7", + "0xa5e9a8ea302524323aa64a7c26274f08d497df3d570676ecc86bd753c96a487a650389a85f0bc8f5ea94fe6819dc14e5", + "0xa8a2963dc9238a268045d103db101adc3b2f3ab4651b7703b2fe40ece06f66bf60af91369c712aa176df6ed3d64a82fa", + "0xa4a8ff0a9a98442357bcdd9a44665919c5d9da6a7d7d21ccdbbd8f3079b1e01125af054b43b37fc303941d0a2e7baee0", + "0x90ef893350f50d6f61ee13dfab6e3121f4a06a1908a707b5f0036cdc2fe483614de3b1445df663934036784342b0106f", + "0x84e74d5bc40aaab2cc1d52946b7e06781fbef9d8de6f8b50cd74955d6bdb724864c0e31d5ac57bf271a521db6a352bd6", + "0x832cdf653bbbd128e2e36e7360354a9e82813737c8ab194303d76667a27aa95252756c1514b9e4257db1875f70f73eb4", + "0xa0af8660ed32e6dbcc4d5d21b0a79a25ff49394224f14e6e47604cf3b00136de8f9ab92e82814a595bf65340271c16c3", + "0x9040b5caf5e4dc4118572a2df6176716b5b79d510877bbb4a1211b046596899ea193be4d889e11e464ffb445ab71907b", + "0xb9bf8354c70238ab084b028f59e379b8a65c21604034d1b8c9b975f35a476e3c0ba09dd25bf95c5d8ffb25832537319b", + "0xa7b492cc1df2a8f62c935d49770d5078586bd0fefda262eb5622033e867e0b9dc0ffc2ce61cd678136a3878d4cbb2b56", + "0x95a5ef06f38743bba187a7a977023b1d9d5ec9ef95ba4343ad149a7b8b0db0e8e528bfb268dc7e5c708bc614dc3d02c8", + "0x99dcf7f123df6c55aeff0a20885a73e84d861ec95cf9208ba90494f37a2dcaacebc8344f392547d3046616d9753c7217", + "0xb3e14f309281a3685ceb14f8921c1e021b7e93c9e9595596b9fb627e60d09ed9e5534733fcbdf2fbc8c981698f5e62ac", + "0x816a5e0463074f8c7fb2998e0f0cf89b55790bdbbb573715f6268afb0492453bd640dd07a9953d0400169d555fdf4ac8", + "0x8356d68f3fe7e02a751f579813bd888c9f4edcc568142307d1c9259caef692800e1581d14225e3a3585dac667928fa94", + "0x8d70ea3314c91bfc3f7c1dcf08328ae96f857d98c6aac12ad9eebc2f77e514afdbaf728dfcb192ed29e7ce9a0623ecbb", + "0xb68280e7f62ced834b55bc2fcc38d9ea0b1fbcd67cc1682622231894d707c51478ed5edf657d68e0b1b734d9f814b731", + "0xb712dd539e1d79a6222328615d548612eab564ace9737d0249aa2eefed556bbcf3101eba35a8d429d4a5f9828c2ac1fe", + "0x8da42ca096419f267f0680fd3067a5dbb790bc815606800ae87fe0263cae47c29a9a1d8233b19fe89f8cc8df6f64697e", + "0x8cb2ffd647e07a6754b606bde29582c0665ac4dde30ebdda0144d3479998948dae9eb0f65f82a6c5630210449fbd59f7", + "0x8064c3ef96c8e04398d49e665d6de714de6ee0fced836695baa2aa31139373fad63a7fc3d40600d69799c9df1374a791", + "0xaec99bea8ab4e6d4b246c364b5edc27631c0acc619687941d83fa5ba087dd41f8eaec024c7e5c97cf83b141b6fb135da", + "0x8db6051f48901308b08bb1feb8fd2bceaedde560548e79223bd87e485ea45d28c6dcec58030537406ed2b7a9e94e60cc", + "0xa5b812c92d0081833dcf9e54f2e1979a919b01302535d10b03b779330c6d25d2de1f374b77fe357db65d24f9cbcd5572", + "0x967d442485c44cf94971d035040e090c98264e3348f55deabd9b48366ec8fe0d5a52e4b2c9a96780a94fc1340338484e", + "0xa4b4110bef27f55d70f2765fc3f83c5ddcdfe7f8c341ea9d7c5bcee2f6341bcfbf7b170b52e51480e9b5509f3b52048f", + "0xa0d39e4eb013da967a6ac808625122a1c69bf589e3855482dedb6847bb78adc0c8366612c1886d485b31cda7304ec987", + "0xa92f756b44d44b4e22ad265b688b13c9358114557489b8fb0d9720a35e1773b3f0fa7805ac59b35d119a57fe0f596692", + "0xaa27e4b979af6742b49db8bf73c064afd83a9cfe9016131a10381f35a46169e8cfd1a466f295fcc432c217c7c9fa44a5", + "0x845961319cc10bcfbb1f3cb414a5c6a6d008fb3aac42c7d5d74e892cc998af97bc9a9120c3f794e4078135e16a416e38", + "0xa18dbe3015c26ae3e95034c01d7898e3c884d49cc82e71ddb2cf89d11cec34cc2a3dff0fafb464e8e59b82ce1a0a7a11", + "0xa954aed6d7124fa5bd5074bd65be4d28547a665fb4fe5a31c75a5313b77d1c6fc3c978e24c9591a2774f97f76632bdde", + "0x8f983b2da584bdff598fcb83c4caa367b4542f4417cc9fa05265ff11d6e12143c384b4398d3745a2d826235c72186a79", + "0xb2caa17d434982d8dd59a9427307dfe4416b0efc8df627dd5fc20d2c11046c93461d669cab2862c094eec6a9845990c6", + "0x8c2baa5a97ee3154cce9fa24f6b54b23e9d073e222220fdd0e83e210c0058fb45ce844382828b0cb21438cf4cad76ee6", + "0xb93437406e4755ccf1de89f5cbe89e939490a2a5cf1585d4363c21ae35b986cb0b981dec02be2940b4ec429cc7a64d4c", + "0xa90ac36c97b7ea2eddb65e98e0d08a61e5253019eeb138b9f68f82bb61cdbadf06245b9dfffe851dfa3aa0667c6ac4b8", + "0x8bcdd7b92f43b721ddbfd7596e104bc30b8b43bdaee098aac11222903c37f860df29d888a44aa19f6041da8400ddd062", + "0x98f62d96bdf4e93ed25b2184598081f77732795b06b3041515aa95ffda18eb2af5da1db0e7cfed3899143e4a5d5e7d6c", + "0xad541e3d7f24e4546b4ae1160c1c359f531099dab4be3c077e446c82cb41b9e20b35fa7569798a9f72c1fae312b140b4", + "0x8844a1471ff3f868c6465459a5e0f2fb4d93c65021641760f1bb84f792b151bc04b5a0421bbc72cf978e038edc046b8f", + "0xaf895aebe27f8357ae6d991c2841572c2063b8d0b05a2a35e51d9b58944c425c764f45a3f3b13f50b1b1f3d9025e52ad", + "0xadf85265bb8ee7fead68d676a8301129a6b4984149f0eb4701eae82ec50120ddad657d8798af533e2295877309366e9c", + "0x962e157fe343d7296b45f88d9495d2e5481e05ea44ca7661c1fdf8cc0ac87c403753ca81101c1294f248e09089c090eb", + "0xa7c8959548c7ae2338b083172fee07543dc14b25860538b48c76ef98ab8f2f126ecb53f8576b8a2b5813ecb152867f18", + "0xae71680366e11471e1c9a0bc7ea3095bc4d6ceb6cf15b51f1b6061b043f6d5941c9f869be7cb5513e8450dca16df2547", + "0x831290201f42ebf21f611ca769477b767cf0ee58d549fcd9e993fae39d07745813c5ce66afa61b55bb5b4664f400ece7", + "0xaf5879e992f86de4787f1bc6decbc4de7d340367b420a99a6c34ac4650d2a40cbe1cef5c6470fc6c72de8ee1fe6bcce4", + "0x8d3c27e1b2ef88d76ac0b1441d327567c761962779c8b1f746e3c976acb63b21d03e5e76589ce9bb0d9ba6e849ed3d53", + "0xab23b09c9f4151e22654d43c1523f009623b01fe1953d343107cef38b95bd10afd898964946d3cb8521bcbe893e1c84d", + "0x8a6acade9520e7a8c07f33d60a87fd53faa6fbf7f018735bffcbbb757c3bafb26f547ceb68e7b8b6bca74819bfcd521a", + "0x94db50080d557440a46b6b45ee8083bc90e9267d40489040cbed6234bebf350c788ec51557b969f95194102fde8e9713", + "0x8be8031f32504e0c44958d893649f76cec17af79efcd22bbedb78378f0a150845467e59f79a3f2a3b6a66bdf0d71d13c", + "0xa69a4ac47fd92e1926b5e14adcbebbef049848e8a00d4bb387340892e5a9333cae512f447201728d3b53c6cf980a5fdc", + "0x8fc713825277c5a8d9ef0a1f6219d141def6d8b30aff0d901026280a17d1265d563ff5192a0817e0e1a04ff447fb6643", + "0x8bf0a85569c4f0770ff09db30b8b2ea6c687630c7801302c17986c69a57c30f0781d14b3f98a10b50c4ecebc16a5b5ec", + "0x896baa4135d5621fd6b6a19c6d20b47415923c6e10f76c03a8879fd8354e853b0b98993aa44e334623d60166ba3e3ca9", + "0xb82cde1c2e75a519ef727b17f1e76f4a858857261be9d866a4429d9facf9ea71d16b8af53c26bde34739fe6ea99edc73", + "0xb1a9e1f2e34895a7c5711b983220580589713306837c14073d952fe2aef0297135de0be4b25cbfaed5e2566727fb32ef", + "0xb42ed0e9eaf02312d1dba19a044702038cf72d02944d3018960077effc6da86c5753036a85d93cd7233671f03d78d49a", + "0xa402e34849e911dbf0981328b9fe6fff834c1b8683591efd3b85aa7d249811d6b460a534d95e7a96fdd7f821a201c2c4", + "0xa774417470c1532f39923d499566af762fa176c9d533767efd457cc5e4a27f60e9217f4b84a9343ecb133d9a9aab96b7", + "0x83dc340541b9ef2eb8394d957cd07b996d2b52ac6eb5562cbba8f1a3312f941c424c12d1341a6dc19d18d289c681ef40", + "0xb2906c32d5756b5712e45dec53782494a81e80f887c6e1ef76e79c737625eccecb8fd17b20e6f84890d322b6ffde6eab", + "0xb89705c30cec4d50691bc9f4d461c902d6a4d147cf75ee2f1c542ad73e5f0dabe3d04cd41c6c04ab1422be4134cf1ad7", + "0x8c3293651f4c4fac688bf5837c208b15e5a19ce51b20dd80ffc7fca12d3e615b2773cfc3ed62a1b39c66808a116bde06", + "0x8fceb8ef481163527d1fc3abc7e1a5b3b6de2f654c3fe116d1367b177dcba2e0d2124a7216803513a3d53fc1e30435b9", + "0xb2a42c827da630aaa3eb20ed07d136aa11ba01b4c8efc0a57ebab7d5b851a15daa6ba118bcffbc20703916e430e30a87", + "0xa86340153abb3fe97414e2fde857e15aac27c9bb9b61258eea6766024f426ed0753f08f07f6b02b5375e1587ea3afcab", + "0xb006465e258e646f91ba889765113d3dc9bd657246c533cab6516d55ba054baa9d7276a3b0fa31730c3bd824845bf107", + "0xa08aadc09428719cde0050d064c0f42c5b7c4f6c158227d7636f870957d6cfe821b4c62d39279a7c98f5a75fcb7bbfba", + "0x885e7d47ce9b50d21b95116be195be25f15223a6a189387575cc76740174c3e9044f1196986d82856b3fb25cdd562049", + "0xb18c3780362d822cc06910743c4cbcef044823a22d12987fe2e56f3801e417f2e9cd31574ea1c5c6ee7673a14aa56e3e", + "0xa625570ef7d31c042d968018865aeeba34ee65a059ab1ec079c7a8ba1be9e24bce6afb7036c07d9d6c96ab014f95d661", + "0x8fc9bd4764adc4c300b5bd49a06dce885d1d8aff9bae68a47976d0cd42110aa6afa2d7b90b64e81c0f14de729f2fb851", + "0x91d88714cb669f5f00241aa5ab80dffb04109492ea9c72b59645eb1f85f3539c61db2ab418af986f42241df8b35445e9", + "0xb98f14e664df2590dd2d00b5b5c817e388e5d9fb074f718637c33b3d4969c89e82fdd12db8997f5ff3bf5bb5ca5dd839", + "0x86cb3d9f148cb2170317a4c22af7092155aa66ecff7ab1299b102fbbaa33ed2a284b97b08f529d2da9faea63fb98972c", + "0x92449f6b8a7c737ecef291c947cbd602c47d7fe47dc3426c2b413f3019169aa56e14c2a7216adce713e1c7bd5c08a83f", + "0xb08c1b9080bba88b44a65070948142d73c00730715fbdd01e13fc3415c5b4f3248ef514fa3ade4a918c9a820cccae97c", + "0xb0a05297da76e37c22be7383e60bba1cbc4f98ba650e12d4afcfcea569842003644a10ad73c9148958f7bf1ffa0a27d0", + "0x839092c1f4e9fb1ec0dde8176f013b0d706ab275079f00f8e774287dd658d1b5638d5fe206f5f2a141911a74bb120f75", + "0xa36bd669bdc055ece4b17ff6eac4c60a2f23324a5eb6d0d6c16a2fce44c39cfd52d1fa2b67f3f5e83504e36426fbfc40", + "0x8aa428323512cf769645e2913a72976d32da4c0062ffe468a6062fd009340f0f23c6b63285848a0e7631a907adb032a0", + "0x944800f7d43f41283eb56115ac39ccc5bf107ae5db6abcaba6936b896260cd09428a6b828c0bccebeb00541073dbf38e", + "0x8e700ca7c9e1538cf64e161dd8d16af56fc29d53c79648150d6d8c268b0c95c76acded723e29918690d66252bd75f5b3", + "0xb9c4ce35b5b16b4c39b6e85800c76b26e8d0999500fabc1e5b6234a7f8da18c621266ac0d5ebc085354297ff21ac89a5", + "0xa0c706d32063f1877f7e903048ce885f5d012008d4a8019dd00261a8bbc30834bffeba56cdeddc59167d54cc9e65f8fa", + "0x839813b736225087cbbcf24506ea7bf69138605036b764ec0514055ac174bbc67c786a405708eb39a6c14c8d7e0ec6ee", + "0xb1a5fef055a7e921c664f1a6d3cb8b21943c89b7e61524a307d8e45aa432e5765a27c32efdb32d88062cd80800a260de", + "0xb17f8202d9ed42f0f5cb1b1dbda60711de3b917a77f6069546fa3f86d21f372b8dd5cb86f1994b873ba9982404e08daf", + "0xb5211d54bd02d44d4d808ad57067606f3e9fa2cad244a5f2acef0edf82de3c496d2b800f7c05f175d01fa6ace28b44d1", + "0xaa9c6f8f489b35fdb7544116fe5102a34ff542de29262f156df4db4ea6e064f5ea20c4bd877d40377ed5d58114b68f19", + "0x826668b1f32e85844ff85dd7e2a8e7f4e0fd349162428bc9d91626b5ab21bdbacd1c9e30cf16f5809b8bf5da4f4fe364", + "0xb30d14917b49437f9fdbae13d50aee3d8a18da3a7f247b39e5d3e975c60bd269da32da4e4cc8844666fca0d65f4e3640", + "0x8c6918d8d94b36c6b9e772e9a432e66df16724e3b0660bde5ea397e6ef88028bb7d26184fbe266a1e86aef4a0dfe5faa", + "0x906d80ffd692c1dd03ab89be52e0a5a9e90a9cdbfc523d2b99c138ae81f45d24c34703f9cb5a666b67416e3bb6272bc4", + "0x8b07e8ba22b436e64f011cacf5e89c55cd3bfb72ae8b32a3a8922c4fccb29de6f73662d6e330da6aa6e732a2187ef3c9", + "0x9547466b4553a49adf59cc65d4c3c9401b2178947ebe3bd33c6e63cfb67d6be8729033158594f6f244b272c4487d6958", + "0xaafcccea41e05cb47223fa8dfec0dd55964268bd4d05e24469614077668655ac8a51d2ac2bfb22862f8f4fa817048c2f", + "0x870f8c1173e8fd365b0a2e55c66eea3ab55355990c311f3042377803d37e68d712edcc5a0a2e2f5a46df0c1c8e6310c2", + "0xb4288f792008f342935f18d8d9447fe4ddcfea350566e13dba451f58c68e27241af1367f2603a9dff6748e7fe0c53de4", + "0x91c58c0e537d3afdcf7783601dd9cda2aa9956e11f711b15403760cf15fc6dffb40ed643886854571da8c0f84e17adfe", + "0xa43fec8ee92febed32e7cdd4e6314a62d9d3052c7a9504057dfba6c71fdfbeff1cef945d8f087bd106b5bec7478ad51f", + "0x99cf5e0e3593a92f2ec12eb71d00eccec3eec8662333471b2cb3a7826b7daca2c4d57ffba18299189cf7364e2af5df6d", + "0xaf50f9ab890b7517ff1f1194c5b3b6f7f82eabc607687a8380be371a6a67b117aeb9b6f725556551b81f8117971706a2", + "0xaa352430887053602a54403bd0d24d6b5181b44aa976dfa190e21851699a88127dcc904c90a48ec44610056b5dcd36c4", + "0x964c821ea1902354736fa382a929c156bd67b9468d6920d47c27b9d0d304b6144118888d124c1f6785da596435ed2410", + "0xb2284a67af26b5f5aff87b4d8e12c78ab37c5eb6e92718fca8549f86f4f001b660fc4520456aff72c9bcddd686603942", + "0x83c54cbb997ea493dc75df4023071dce6da94268feaa2352373789616f012098270ba4fd60c791796a6f5062fb2cd35e", + "0x9143e8fee0b8f0f34c65c7750858093dcf165c6a83c026bfac2d5ffa746361eb4b6a14fdb43e403add901ac3735735a3", + "0x97d7748a5b278ee47b18c9e60689b12a0a05be47e58e78bf8c04b9e8b34e2e2f2d3ac3c25c76ab2e0a75e8a54777b7c8", + "0xb4e68f6f2d978a5411414c164c81ddb2a141b01ebe18c65a8626ca75d6432e5988310b50a888a78c3a0a242353525af5", + "0x8976f4cc3eaf2684718cf584712c4adaf00a4d9c521f395f937e13233b30329658b3deacfe7e29fac84c496047f2d36b", + "0xa40bcdf4b6e95f1535c88dddcbf2074ef2e746b7fd232bdfd2b88f2f6d4bbf21c6b263cf5fd3e12a03476f2f5ffe00d2", + "0x88c7b6337ee705acd8358ef6d2242d36b140afff0579a7784b3928a0c49698bd39c1f400e8a2e3eda5fbfb2e8f28fe51", + "0xa98612ba8b450a71d2075d51617ebeb7ca401ad3cbd9b8554850c65ef4f093ba78defb00638428c9f1f6f850d619287f", + "0xb7e71d3ffa18b185c1a6bd75668ff65d985efc0a0c19f3812cafde9adbfb59ffd108abeb376e6a8877fdf5061562f82b", + "0x8a3e5fd776cc26908a108a22b1b122d60cb8c4f483cbedcd8af78a85217bb5a887df3efed2b8b4ec66e68eb02a56ca93", + "0xb0d92b28b169d9422c75f9d5cb0a701e2e47b051e4eacd2fd1aa46e25581a711c16caf32f40de7c7721f5bf19f48b3f5", + "0x88895739d5152282f23e5909cf4beebda0425116eb45fc5a6a162e19207686d164506c53b745fb2e051bb493f6dbad74", + "0xadbccfed12085cd3930bd97534980888ee564dda49e510c4e3ca0c088894855ef6178d5b060bca8a8a1a427afdbec8a8", + "0x87d00674abd3d2e7047a07ed82d887e1d8b8155635887f232dd50d6a0de3fb8e45b80b5a05bc2ec0dea9497b4aa783ac", + "0x806e1d3dfadd91cbf10e0d6a5e61738d0dbff83407b523720dce8f21f8468b8a3fc8102acf6ba3cf632ca1cb2af54675", + "0x95a9dff67cf30e993071edede12623d60031fa684dfbe1654f278a1eb1eb7e1be47886d3f8a46c29b032da3176c0d857", + "0x9721973288384c70a9b191436029e85be57970ad001717edc76d44cbfa0dff74f8af61d5279c5cd5c92c9d0f6c793f63", + "0x95c22d1d9b51ef36ba30ee059dcd61d22be3c65f245d0a5179186874219c08e1a4266f687fc973e71f3e33df2b0f7fd3", + "0xb53ec083dd12cc42ae2bae46883a71f2a35443c9ce4ed43aa341eb5f616a53b64211ed5aac717fe09ef1d50f551ed9f0", + "0xa103dab6695c682400f60be8d5851ce07f12e4bd9f454d83b39c41ddcf1443bb14c719b00b4da477a03f341aa1e920cb", + "0xb522236988518e5363b1c4bb3f641ff91d3d4c4d64c5f065415b738160b4ce4b0c22e1e054a876aa6c6a52fa4a21dfa2", + "0xa6a00562f0879702cdba5befd256a09f44bf48e61780e0677ff8c3fda81d8e6dc76ba1b05e3494ca9a4cef057eba6610", + "0xb974a2ae631e0b348421f0cda5bd4ce7d73c22dd0fc30404c28852c33499818cab89fbf5c95436d56a0aab3bf2bbab51", + "0x9148cf2a7b7e773245d4df5a9d34cf6d9d42b1a26a4ca6bc3013feca6f3941d6c44f29ba9328b7fe6ce6d7f6565f8e4a", + "0xa34035c4a63e98528a135cc53bbbcfcda75572bc4c765f212507f33ac1a4f55563c1a2991624f7133c77b748bbe1a6da", + "0xa0c45923cfb7bd272ee113aecb21ae8c94dda7ad1fe051ddb37ab13d3bb7da5d52d86fff9f807273476c24f606a21521", + "0x81ec2ca57f4e7d47897d0c5b232c59d7b56fe9ce0a204be28256a7472808de93d99b43c824a0cd26391e6cac59171daa", + "0x8373852f14a3366d46c7a4fc470199f4eebe8ee40379bd5aae36e9dd3336decaead2a284975ba8c84d08236e6b87c369", + "0xb47e878a93779f71773af471ba372cb998f43baca1ae85ea7ff1b93a4dee9327e2fb79691c468ec6e61ab0eae7ceb9f1", + "0x8fc8f260f74303f26360464cfef5ee7eebcbb06073cef3b1b71dab806d7c22f6b3244ce21d0945b35c41f032f7929683", + "0x87e3c4e1dab00596e051ce780b9a8dba02ecdc358f6ddaeb4ec03c326e4b7da248404745392658eb1defff75b1ba25c8", + "0xaac95d8e3b7fe236a7ca347d12a13ec33073f2b2b5a220ecfd1986ca5c3889f0e6a9d9c377a721949aa8991c1821953a", + "0x91a483679437ae126a16f5dc3bba6e9bb199dfbba417f0dc479f22819b018c420edc79b602db6183c6591b1909df4488", + "0x94a4b2c663aa87a2417cad4daf21a88b84983a7b212ffcd18048a297b98e07dd4c059617136976fac1d9e94c8c25b8d2", + "0x83e2a690bfa93c79f878a63c0f69f57aabdd8bede16b5966ffba7903dc6ad76775df1fd5347e6f2825f6cd7640f45a45", + "0xa316af7ac11b7780d15312dc729499a1a63b61c4283e103ecce43c3b0cbb0f4bce6ff04e403f5c7cb670dee80c75ab99", + "0x8d0a911c54ee1f9f7e7794732ad87b434c3f356294d196a5e35eac871727fd32a49c27c2dfa10833f9e6f9c7ccbe0064", + "0x8b8db09028298a1f6362b346c8bfeced7cb5d13165a67c0559a9798a95b7a4a9810c02bb852289d47c59f507bd24ce77", + "0x962d57305c518f175ed5d0847fb52ddc4258ca0e4c9ddfc8c333a2ee9f8b4e48d25a3d7e644b785a5953e2e4063da224", + "0x92e0799491898271769250fe88b0cb9dadec98ac92f79de58c418d23ef8c47fcf21ddc90e0cd68bb8f1deb5da82da183", + "0x99855067125f6a6c3a3e58d3bd2700a73ef558926bd8320d2c805a68e94207b63eda6bdc5a925ec36556045900802d51", + "0xa724ae105ab4364a17ddb43d93da1e3fc6b50213f99b7be60954b24dc375c4f93a0737f4a10b4499b6f52667d5f3a64e", + "0x82070fb43a63fb50869b118f8940108f0a3e4cc5e4618948417e5cc3801996f2c869d22f90ca4ca1fdbef83c4778421a", + "0xb25c04365d6f24d5d3296c10d85a5de87d52a139ddbcbf9e0142074bc18b63a8bc5f5d135bd1e06c111702a4db4cee28", + "0x851093282dcda93e5c98d687a17a7ee828cf868f6c85d372d9ae87f55d0593d8f9f0c273d31f7afa031cf6aea6a7ef93", + "0x93f04f086fa48578210ed207065d80a40abcc82d8bfc99386a4044561d35748ff6c3da6489933c23644ad4b60726da8a", + "0x84b1b50d1e876ca5fc341bbedab5b3cc0f6a3f43ea7dd72605f74d0d9c781297b2f12b7872dd600924f1659a4cdf8089", + "0x81b0ba88c582d3956f6b49ca3e031c6400f2ec7e1cd73684f380f608101e9807f54866be0bb9a09c03953c4c74fbb3c8", + "0xa641af6ac644c41a55dee2ef55d3c37abdb19d52bc1835d88e7adda6b6ccd13987c5fd9cba9d318cabb541aa6a0c652e", + "0xa7b75b0624d04ad0901070e691eb2d2645b60f87e9d6b26e77a5fb843f846c32fc26e76ae93fd33fe3b857f87bc25162", + "0xa81ba3e2ed0f94c67cd02ba7360e134f8becf7ed2ed2db09b9f5ef0942f7073bfee74ca446067db6092f7b38f74ccc11", + "0xab80edcabab5830a24210420f880ebac4e41bf7650c11ba230f4889634dbf8e8e2309f36be892b071c67a3bab8fc7ed6", + "0x94d69b64675076fecad40fae4887fb13a8b991b325fa84e9d2d66e3b57646de71a58ad8fd8700fefb46975b18289250b", + "0xb44fc0df480cd753a041620fa655be9df74963ae03d4625847d5bb025ceb37f48d19c8c9c444546fba5fe5abb2868506", + "0xb56e2c51324d6200b3d9781b68b5b5e1617a68afccd28b3a12a4be498d2e3aafcd86514c373a9f3a001db733010c29cf", + "0xa359a0c172e5cd7ce25080dd2652d863d7c95a4a502ae277ac47f613be5991300f05978404a0acb3bcda93524dcf36e4", + "0xb01427a3dfdf8888727c0c9b01590b8ae372b7b4080d61e17ccb581bac21e61c4a58c75db7a410d1b2a367304e1e4943", + "0x95cb08be4a96c18fbf9d32a4bbf632242029d039a5fdea811488d3634cd86520d4f9806250a8c01855ee2481210f542a", + "0xb8594fe6c0717164058f08aedeed1853523f56cec5edbf0d2be271fa5e8bfd61f2974b0f3988d70f5baa2e7888c7ec1f", + "0x8f64ee89f59daf74fa1056803247c9d678783ee3917b12a201f30f7523957763e979ceaddb38bae20de40b9885728049", + "0xb6093ee4bdb837bcc59172e236f4bdbd439c0a5a50e2aa16636cbff81b51e92989eb5f80a3f75c37ae7b5b942e55b3d2", + "0x913b6fbb7b43e3e5c49e96cd8e82ed25c655e51c7b8ca82e8fbf92b01ac83c39d52f6f4efab5d39b0591a0538601a86f", + "0x81f42668479ca0bec589678dc0973bf716b632578690efe1a0f13de630f306fb4a189a98c2302572fd85d3877ee030b5", + "0x90ff89c38a9a7189f28d35a088657f52283670e7fec842fa91c265660ea2e73b0ad6c46703d649f406f787490b7a7e4b", + "0x9077b8b5f1e083183f3152ceb9c5491b5d4b86525a08879f7fb6d5e27f9f1a6867cf0d81b669a4a2d1f1654b67fa8d9c", + "0xa7a0275cf5b894adbf2e54a972310cfe113e811872111d6ee497d03750d9f6ffa5517b6c13a99b111a4a91e8e4dfeeee", + "0xa08976bf8125b7538313a584bbe710741d630cab067a204ad4501cc4938874ce7aa6a1a826259c2e82ef10a66f1f36fa", + "0x8aa45385b5b97f1f3e45f2bbf7a4f3e8ef068e628608484971c97adeb610ebd5deec31317e03eb6536808921062c04db", + "0x945b106b8f3ae85e60dfd34ef3dcc079bc6f0aab6df279ed000856efd51321462038ac0a1ca5db3ebf6379bc341e7c55", + "0xa4199c87a96f98cc9d8776fe6de131d2c706b481eb9e9a3bbc50a93d492d7fd724ea469f723fbcfb94920cb5b32c1d76", + "0xa5347b1b2f6149805de67546c5ed72253311099bf1473dbc63edcf14a0a5e68d401f5341338623fbe2e2715b8257e386", + "0xaf5dcd03ddc3769e83351d6b958d47a06d4e5224bd5b0ec40ffe6b319763fab8572002f4da294a9673d47762fd0e6e1d", + "0x82ec1031b7430419d83b3eea10a4af4c7027f32b91c3ae723de043233b4a2e0c022c9e0f5a1ac49753800f119159112d", + "0x8a744d911b67d03b69811f72e9b40d77084547e4da5c05ff33893468b029a08266fc07303f7005fd6099683ca42b3db4", + "0x93ab566bd62d3439b8fc620f3313ef0d4cb369f0f0c352cdaf8e5c9e50b9950ac3540b72f4bf5adcb9635f9f7ce74219", + "0xb2a211d72e314799bc2ac7030b8bbb8ef4c38ebd0ebb09d6cbd43bd40c6c61d80a3aad02cc73f5775a08b9657da20a48", + "0x98d60f0a98d28718e0c6dcccc35a53521ea7f2d8fe08ea474374a336b44cea4cd1c63b31f2ad10186822bfb54aca53e6", + "0x831f89cb94627cfe554d46ae1aad8c1cde7ebe86c4bd8fac4ef73ac2d5b491f5efa5dc4198cb8ffbec563e0606b91d89", + "0x8f8552583bc6cb3fb176b7202236ee4128faf0c8ec608f9150f8e011d8c80b42aab5242c434d622b6d43510eaef752c0", + "0x897bf27baaee0f9a8445200c3d688ae04789c380d1b795557841606a2031092328eb4c47fef31c27fdd64ba841d9d691", + "0xb57589a4af8184b4a8ceb6d8657a35522672229b91692c1cec3ac632951e707922a00086d55d7550d699c4828bcfaab1", + "0x98c2fe98095e026aa34074bcff1215e5a8595076167b6023311176e1c314b92b5a6d5faa9599d28fca286fadd4e3b26c", + "0xa034992e563bd31ede3360efd9987ecddc289bc31046aa8680903bb82345724805e6f6cf30f7889b6b95cf7319c3aea1", + "0x85c33d9f10cc7185f54d53c24095e621966065e0ff2689a9aa6bb3d63706796c37a95021738df990c2c19493c0d44b64", + "0xa8c1247d6de2215f45b50dd2dc24945ff9b93184bcc2159b69703b0bba246adcd1a70a12659f34c4ca4ba27dea6e3df5", + "0x83ebdad2834c97bf92aac8717bab2f5cb1f01026b964d78e2f3b44e99d7908e419165b345d2b2f125b903096584e6683", + "0xb0af6f7f81780ceb6e70adfd98e7702ec930c8ca854b50704c4a0fc8b887b9df60a6fe9038b487f3ed0eb8eb457307ea", + "0x933ec7e53882453898617f842ab2efae4756eb6f6ea0161cced5b62a0cdde4c08c7700d52f7546d4dd11a4c9e25d624e", + "0xadf6e6d4706025f85eb734f506dde66459c9537a1abf6189199cf219ae583b461e11c6242fce5f0795e4d9025270fabf", + "0x89e4316319483098761b0b065df4cfb542963b7a2556ba5425b6442fb0e596eb2a4f03e2dc8c617eebe8f243a12e7d10", + "0x90c5a147555759ebc4d0e15e957a548315f9994ef0c7a3f53f2d18da44fb93bf051d96ba8551597a6f3e701b926fd791", + "0xa151a9a5199c72c697b771cd81e550fc6f9596c752ae686ad988b316a7548360cf9785ab4645164d96cfdf9069a94020", + "0x80cba11a3977729d7948db5bcc186159f4cae7c0a835bb38bb781e287dd6c238508e748f23454405c9d5eed28e77df02", + "0xae4b92ea03cb8ad12ad3ec76869ad05acb09f9d07a3c9a87dec0e50d9a276fe5d3d515a8c446f3aa35cd7d340a22c369", + "0x8630062709a1f180f952de9f1ca3f41acce5420677f43d9619097e905a6237f1908d66db7a4dfdf1b2b92fb087e9944f", + "0x81defc33dd383d984c902c014424bddd5e53b013f67f791a919446daa103b09b972fa5242aba1b1dbe4a93149373f6c3", + "0x963891ecaea97e661bac2594642327a54f5a0beb38fcb1c642c44b0b61faab9c87b0c9f544a3369171b533d3ab22f8f1", + "0x932fadbff5f922ddcd4da942d57fe3e6da45c3d230808d800a3ca55f39b0b62f159be31a5924b395d577a259f48c6400", + "0x992ce13bd037723447f88aeb6c7722fd9510c7474192b174ea914ed57c195c44c298aec9a8cabac103f0a5b50051c70b", + "0xb032157b3e4fe69db6ce6bb10bdf706a853fbd0bee08c2ab89da51ad827425df5df498b90e7a30247a7f9e954ca986e5", + "0xb2478d4874578da3d5000893736bb65712e6aafe96e6fa5cf5878ae59ba0ce640dbe5d76ec2b5baca75af57def471719", + "0xa387c17b14dd54910fecf472f760e67cf71a95e9e965cc09484e19581ada65e79938b86136a93e287e615fbd4908e080", + "0x98f02be271d0f8841d8d561163f9e55e99b57aff121a93fba7a4654bcf15a0899811f00f5bcbfbebd98e365a0e332e97", + "0xa3c34f01d54cab52a8890391b8cf152cc9cdc16e7e53794ed11aa7b1a21e9a84d39ddcfbcb36c5df6891c12307efc2e0", + "0xa940331f491ec7ad4a9236ca581b280688d7015eb839ee6a64415827693d82d01710dc4bbd5352396be22781fea7a900", + "0xb10874ed88423731535094031c40c4b82af407160dfade4229ac8f4ef09d57b3db95c4a9d73c1a35704f6bd0d5f6c561", + "0xa9c5a4a7680261c1b0596f8ab631d73d4a7881b01e6559c628b5cdafa6dd2b6db2db64f3f2ab5841413a8a52b966a0da", + "0x8fc154564a61d5e799badc98b43a3587f804385a850adce9a115cbd2ad911f3fd4072b8e6b22fc6c025a6b7e7ea5a49f", + "0xb9caf7c6dcce3d378aa62c182b50bc9c6f651eb791d20fffa37ef4c9925962335fe0b3bc90190539312aa9ccf596b3b9", + "0x90c5b7acf5cb37596d1f64fc91dee90f625f4219fa05e03e29aebea416c8e13384f2996f8d56791bcf44ae67dc808945", + "0xab8d311fc78f8a1b98830555a447c230c03981f59089e3d8a73069d402a3c7485abe3db82faf6304aaca488a12dbe921", + "0x8a74fda6100c1f8810a8cacc41b62875dd46d5c4a869e3db46202d45a8d9c733b9299dda17ce2ad3e159122412a29372", + "0x8769dcacba90e6fc8cab8592f996c95a9991a3efecfb8646555f93c8e208af9b57cf15569e1d6e603edac0148a94eb87", + "0x854fd65eea71247df6963499bafc7d0e4e9649f970716d5c02fbd8708346dcde878253febb5797a0690bd45a2779fa04", + "0x83e12dc75ef79fd4cc0c89c99d2dace612956723fb2e888432ec15b858545f94c16fae6230561458ceee658738db55ba", + "0x8416ef9ac4e93deff8a571f10ed05588bef96a379a4bdcc1d4b31891a922951fa9580e032610ac1bb694f01cb78e099b", + "0x93aea6e5561c9470b69d6a3a1801c7eef59d792d2795a428970185c0d59b883ab12e5e30612d5b6cde60323d8b6a4619", + "0x91d383035aa4ec3d71e84675be54f763f03427d26c83afb229f9a59e748fb1919a81aca9c049f2f2b69c17207b0fb410", + "0xb1c438956f015aef0d89304beb1477a82aed7b01703c89372b0e6f114c1d6e02a1b90d961b4acbb411cd730e8cacc022", + "0xa1ee864a62ca6007681d1f859d868e0bcd9e0d27d1da220a983106dc695cb440980cfdb286e31768b0324b39ae797f18", + "0xb57881eba0712599d588258ceada1f9e59c246cc38959747d86e5a286d5780d72d09e77fd1284614122e73da30d5cf5c", + "0xa48f9ae05ba0e3a506ba2e8bbce0d04e10c9238fa3dffa273ef3ffe9ec2ed929198a46507c0c9d9b54653427f12160f9", + "0x8db18da7426c7779756790c62daf32ae40d4b797073cd07d74e5a7a3858c73850a3060f5a3506aae904c3219a149e35d", + "0xa2bf815f1a18d7be8ce0c452dfc421da00dcd17e794300cdd536e4c195b8c5b7ccc9729f78936940a527672ac538c470", + "0xa34c6f1f2398c5712acc84e2314f16d656055adcafad765575ae909f80ab706cf526d59e5a43074d671c55b3a4c3c718", + "0xb19357c82069a51a856f74cbb848d99166ce37bd9aca993467d5c480a1b54e6122ebddb6aa86d798188ea9f3087f7534", + "0xb440eac6f24d12c293d21f88e7c57c17be2bdb2a0569a593766ae90d43eccf813a884f09d45a0fb044ee0b74ff54146a", + "0xb585d42ef5c7f8d5a1f47aa1329f3b1a566c38bf812af522aa26553010a02bfd6e9cc78fdb940ef413e163c836396a5f", + "0xaca213b27f3718348e5496342c89fffc7335f6792283084458c4a1aa5fe0a1e534fcec8e7c002f36141308faae73ef2a", + "0xb24c07359769f8ffc33bb60c1f463ea2baad440687ef83d8b7c77931592d534b2c44953c405914ace5b90b65646c1913", + "0xb53dfaf381205a87ca4347328ff14a27541fa6436538f697824071d02d4a737ceb76a38dcc6e8dadef3b5bc6442f5109", + "0xb55972d8ed5197215c0a9144fc76f2cd562ca5f4e28c33a4df913363fd1388978b224c44814adb4c065c588a4ac1fe10", + "0xa3303bc650e120c2e9b8e964ad550eb6ac65ffe6b520768b3e8735565ae37eafdc00e3c15fae766d812f66956a460733", + "0xb11e53912ea0e40c3636d81d7637e10c94cc7ed9330a7e78171a66d02b7603f4cb9b3f6968104b158de254e65b81640f", + "0xb076bb9f6d396aa09c2f4706ea553b426fdfd87d7d69e438285b74d334e82f73973cb4dbd6cb1647493433dad65dbc41", + "0x9415828b1632175f0b733541e32c26a9c88fe12c721c23e595f2efceaa7f867f359e32564b7c032185686587ac935cf4", + "0x89579a112c306181c79aabdbf683e7806357febcb73bf5e8883862ae29618ef89498b62634404bb612d618fcd16da415", + "0x8761bcd55d04297c4f24899e8fb9f7c1fcd7449ae86371ee985b6a262e228f561c2584980694d9bf354bdf01543edb6a", + "0x9100c88bf5f6f00305de0c9cf73555f16a2016d71c50cb77438e8062bd549fa5407793a8a6a7e06398756777680a2069", + "0x9235dfef45aeff9c174898b0755881b7171ed86362854f0eabc3bc9256176c05a5dc27ca527c91c3fa70c0ec5fd5e160", + "0xac53b1d677cebab6a99381dd9072b8ac1abae9870ec04a1f8d2a59b6f1de797c1492b59af6948f5cf2b20599170f5bba", + "0x946542936b0c59156e8fd5c1623b41369bc2cbcc46ece80360dcb5e7cce718a3dd8a021f0b9c223062a4e43d910b634f", + "0xb1e9939b34e1fcc026e820fcfa9ce748b79499f8e81d24a3ef0457b3f507fe5fa37b975a47c143e92eb695623b4e253b", + "0x9382d9b5766f6ae960d8a8435e8b5666e57ef8e5f56219e7bfd02857afe5cb16f44d70a9e444cfb1008649ae9b863857", + "0x91770ed1215ed97dca1282b60b960be69c78e1473edb17cd833e712632f4338ff74bf435c3b257439497c72d535ae31f", + "0x8eb2cbe8681bb289781bf5250e8fa332141548234c5c428ff648700103a7cd31fdc2f17230992516c674aa0ab211af02", + "0xa823b71c82481bc6ac4f157d5c7f84b893a326bbb498c74222427ded463d231bc6e0240d572ab96266e60eb7c8486aea", + "0xa13ce4f482089d867e5babcd11c39fa9a9facd41a2c34ee2577de9ce9c249187e16f2b3a984cc55f9e45b9343462d6d2", + "0x8d80e7bc706059cf5151f9f90e761b033db35d16b80b34dc8b538adc8709d305a0c06933dcd391e96629cf3888c8bf87", + "0xabcd36cdd86c0fb57fb7c0d7a3b9af5fd9aed14e9f4e7e84b0796c5c0ad18c41585e8c46e511cef73dc486fe43f6a014", + "0xa947a5b6916f416fa5a69c31aba94add48584791148b27d0b3ed32c02a05dfc06f7fdc5006e3b2503bdf6e410e30f2fb", + "0xb158e621580659f1fa061d976b8591ac03b53ecd23d9eb2b08c1a20353d78438287749664d196020d469ef44b3b8752e", + "0x90a5a9540281e481ac4b8d29968f477cb006b56bd145529da855d65d7db0cf610062418c41a1d80c4a5a880c0abe62a0", + "0xb2c91808b6289d08a395204a5c416d4e50a8bb1a8d04a4117c596c4ad8f4dd9e3fb9ce5336d745fc6566086ae2b8e94f", + "0xaf6767c9b4a444b90aeb69dfddae5ee05d73b5d96e307ce0f3c12bccca7bc16475b237ba3bc401d8dafb413865edf71e", + "0x8dcecf624419f6517ef038748ac50797623b771d6111aa29194f7d44cfb30097ced26879e24f1b12a1f6b4591af4639b", + "0x954437559d082a718b0d6d7cec090532104ab4e85088e1fc8ee781d42e1a7f4cdb99960429707d72f195ff5d00928793", + "0x80f0b7d190baa6e6ab859dc5baab355e277b00ddcca32e5cebe192877ad1b90ead9e4e846ca0c94c26315465aeb21108", + "0xb8c29f181ed0bb6ac5f6a8d9016980303bb9a6e3bd63ce7a1a03b73829ac306d4fab306ac21c4d285e0d9acb289c8f2a", + "0xa7685079fe73ecaeabf2a0ef56bad8b8afb6aeca50f550c97bf27e6b4a8b6866601427fcd741dc9cb4ce67a223d52990", + "0xada2ebf6f2a05708d3757fbf91365ec4d8747eb4c9d7a8728de3198ceac5694516ab6fd6235568aecd8d6d21fef5ef48", + "0x846bc5da33d969c53ab98765396cab8dcdbb73b9836c9bda176470582a3427cb6de26d9732fab5395d042a66bdba704c", + "0x800a3a7ea83ce858b5ebc80820f4117efa5e3927a7350d9771cad9cb38b8299a5ad6d1593682bba281c23a48d8b2aa71", + "0xa002b18595dec90b5b7103a5e3ec55bdd7a5602ee2d3e5bd4d635730483d42745d339521c824128423dfe7571e66cbaf", + "0xb6b4e2067ac00a32f74b71007d8ab058c2ef6b7f57249cb02301085e1a1e71d5de8f24f79b463376fd5c848f2ab1c5bc", + "0xa3e03036db1b6117efe995bf238b0353ad6f12809630dca51f7daaaf69f7db18702e6b265208944bfb1e8d3897878a51", + "0xadd16712f66d48aab0885bd8f0f1fb8230227b8e0ffca751951c97077888e496d6bfab678cb8f9ffba34cee7a8027634", + "0xad211af2dd0748f85a9701b68c19edd4a7c420e497cb2e20afdc9df0e79663841e03b3c52b66d4474736f50d66c713ce", + "0x8c8a899ce0f16d797b342dc03c2212dda9ee02244c73c7511626dba845d11a0feb138441da5459c42f97209bf758cd9b", + "0xa17efc75c7d34326564ec2fdc3b7450e08ad5d1de4eb353de9d1cd919d90f4be99f7d8e236908b1f29cf07ae1ffe0f84", + "0x862d4a8b844e1b0dd9f4deff180456ebed5333b54290b84f23c0ddb2725ac20307e21cbb7343feac598756fe36d39053", + "0x9187fbb19e728a95629deda66a59e178f3fcd6e9d7877465aa5a02cea3baba2b684bd247b4afbf4aa466b64cb6460485", + "0x85ae5636688d06eab3be16e44fe148515d9448c6123af2365d2c997f511764f16830610a58d747adab6db5031bea3981", + "0x8aa8a82891f4e041ce6df3d6d5d7e5c9aaaffe08e0a345ac0a34df218272664c1b7be2450abb9bc428bd4077e6e5dcc4", + "0x8c3bcc85ea574dfe1b9ca8748565c88024e94374434612925b4e9a09fa9d49c0a56b8d0e44de7bd49a587ef71c4bff5f", + "0x9524f9dd866fe62faf8049a0a3f1572b024120d2e27d1be90ad8b8805b4e2c14a58614516281cc646c19460a6b75587c", + "0x84580d9c72cfa6726ff07e8d9628f0382dc84ce586d616c0c1bd1fd193d0a49305893eae97388de45ba79afe88052ee9", + "0xb5573e7b9e5f0e423548f0583423a5db453790ab4869bd83d4d860167e13fd78f49f9a1ffe93ddddf5d7cd6ec1402bc4", + "0xaff658033db3dad70170decb471aee2cf477cf4d7e03267a45f1af5fd18200f5505c7ce75516d70af0b0804ec5868a05", + "0x84a0eab4e732a0484c6c9ed51431e80cea807702fa99c8209f4371e55551088a12e33a11a7ef69012202b0bc2b063159", + "0xa68f8e730f8eb49420fe9d7d39bb986f0584c1775817e35bb3f7dae02fd860cddf44f1788dc9e10d5bf837886b51947f", + "0x946002dd6cf7a4fd3be4bf451440e3f3fd7e9b09f609fa4e64767180b43146095dfc4b6994287f8cfa6d1390d144be71", + "0xb7f19777d0da06f2ab53d6382751dc5e415249d2c96fce94ef971401935c1d1f7d3b678501e785cf04b237efe2fe736e", + "0x81e5c66dd404fc8ffd3ac5fe5e69ead7b32a5a7bc8605a2c19185efcc65c5073e7817be41e1c49143e191c63f35239c1", + "0xb5f49c523532dfa897034977b9151d753e8a0fc834fa326d0f3d6dacc7c7370a53fc6e80f6d5a90a3fbec9bbb61b4b7c", + "0x8fc8e78c07319877adfaa154a339e408a4ae7572c4fb33c8c5950376060667fbfc8ede31e1b067933d47e3fdbf8564d7", + "0x859cfef032a1a044532e2346975679545fbb3993a34497ce81bdcc312e8d51b021f153090724e4b08214f38276ee1e0d", + "0xae476722f456c79a9c9dfdc1c501efa37f2bff19ab33a049908409c7309d8dd2c2912aa138a57a8d5cb3790ca3c0ba2f", + "0x89acbbeffb37a19d89cfe8ed9aa8b6acf332767a4c54900428dd9ab3bf223b97315aca399c6971fe3b73a10a5e95a325", + "0x90a4a00418fdf4420a4f48e920622aae6feb5bf41fd21a54e44039378e24f0d93ccc858d2d8a302200c199987d7cb5e4", + "0xa3f316b0bd603143eba4c3d2f8efe51173c48afe3c25b4ca69d862c44922c441bd50d9a5040b7b42ba5685b44071c272", + "0xa22f4dc96fedd62b9a9f51812349e04d42d81d0103465c09295a26544e394a34abdc6ded37902d913d7f99752dbfb627", + "0xa49f51baf32d0b228f76796a0fef0fe48a0c43ec5d6af1aa437603d7332505be8b57b1c5e133bc5d413739f5ae2ce9d0", + "0xa9e4fe133057a0cd991898e119b735b31a79811307625277c97491ff5d864c428cfa42ae843601d7bb05c0313472d086", + "0xb987edfe0add1463a797ff3de10492b2b6b7ef0da67c221ab6f0f2b259445768a73fbe495de238c4abbe4d328e817c49", + "0xb7f0e4532a379a4c306bbef98b45af3b82b17175dfe0f884222ed954c12f27d8a5bdd0cdeb1df27ff5832ba42a6dd521", + "0x9471bc5ad5ec554acfd61b2eb97b752cb754536f95ae54ca2cbd1dc2b32eb618881f6d8a8b2802c1a4e58c927067d6cf", + "0xb4c84f09225cf963c7cc9d082efe51afbbbe33469dd90b072807438e6bde71db8352a31bb0efde6cd3529619812ef067", + "0x8f08005a83e716062d6659c7e86c7d3b51e27b22be70371c125046de08f10ea51db12d616fbf43e47a52e546e7acaac7", + "0xa8937e66a23f9d9b353224491f06e98750b04eca14a88021ee72caf41bdce17d128957c78127fba8ef3dc47598d768a7", + "0x80ad991de9bd3ad543cddeaa1d69ca4e749aaefb461644de9fc4bd18c3b4376c6555fc73517a8b1268d0e1e1628d3c1f", + "0xb22f98bca8fe5a048ba0e155c03e7df3e3cee2bfe8d50e110159abdb16b316d6948f983c056991a737b646b4d1807866", + "0xb0bb925c19ca875cf8cdbefa8879b950016cc98b1deb59df8b819018e8c0ad71ea7413733286f9a1db457066965ce452", + "0x95a991e66d00dd99a1f4753f6171046a5ab4f4d5d4fe0adfe9842795348a772d5a4a714dba06b4264b30f22dafa1322f", + "0xad91e781fa68527a37c7d43dd242455752da9c3f6065cd954c46ae23ce2db08f9df9fec3917e80912f391c7a7f2f7ffa", + "0xa202d3becbf28d899fe28f09a58a0a742617c1b9b03209eca1be7f072a8ada1f7eac2cc47e08788d85e1908eb9d3d8ee", + "0xa360ccb27e40d774d5a07b4ebed713e59a0d71b3ee3f02374e7582b59ec4a5ce22cc69c55e89742ba036dd9b4edd8f34", + "0xa10b897a946882b7c9e28abbb512a603ffa18f9274369843eb3491524a321df1f572eea349099ac6e749ea253c901ea0", + "0xb782a672cd344da368732ecd7e0a1476c2af04613d3eb6da0e322f80438af932bd6d49be7a6f69f7c877512731723d89", + "0xaeccee8dfd764e1adcfc4bf669e0fa87a94e7c79324333e958df47888bff5cec358b8b5bbb48db54822b54d11bbb4bc6", + "0xad4953913662a9ee8753a354864339f43916f2c2390d0a3f847c712b42718ee00ee14158d730709971941e8680d54560", + "0x92ccb31d6c9e8940c7e8a4873e7eb9de9fb2fa2bac344fa367062ea451fd49a6920a45218dca3ee968711397d2a01536", + "0x9448d9b2b3d12dde9b702f53373db8b8595f9d1f9de2ebee76de292f966f375316953aadf6bfc0e4e853e1fa12d8f02c", + "0x8919230878a7219da8c80a4b7d00b9169fb503e72d79789dd53863c243b8d0fb0a819d46fa636d805d0b9b1d15d1f2d9", + "0xb6581ab01215aac023f5e6f57419b6aa63c0743c07caf57d4e146b56b02d90ce1423f70489ac3a11e5c968cb924f937c", + "0xa793ec1b1fe56a76920296af06073caadfd6f1d7e30950f8ca13de3de45fe275ca4b361f5249d9405264c3a06ebb5502", + "0x86385b4a4e1bfb5efe7bfef8fd0dfeba7f4400852237cab60febb1dfa409e497a649e81284b5a15fe680b78927256756", + "0x85d10600de96103daa7c90657174b6cb4a1286df5379f1eda9f11c97f9df57043c290eb1ae83658530fe0fd264867b86", + "0xae01b2396d0f598c21659cd854c15edd4904a34d22278aef97c9260a14a8b250b52d972d304ac4b187c24d08795d5355", + "0xb91b3e4b6fc06e88081fe023ef1b773d82c628eb0f73a2731a9aa05b0dc89b7aeef2eea60125d302e696f45c407aeac2", + "0x986d0f478e33af7568eab6bb26a55c13ffd7cae27525b4abe2f3a994bdb11bbc73d59bdb9a2f6b6ba420a26f8f620ba6", + "0x9746f4fdeef35feaff1def0ea5366b64f21ed29749ae6349f9cb75987e7f931952f913f446100f2a6b182561f382e8eb", + "0xa34a116cfde1acbce0d7de037f72a7ca30ab126d8f4815b2b8bcb88e0e6c89015a4daaf4d4ce8eae23eb5d059cf9a5cf", + "0x80c3ea37f6a44f07cc9c9c881990f2a5deb9f9489a382718b18a287aa3c50ee6ebe8fd1b3afb84a3cf87f06556f4ca15", + "0x97cff3bc88cfc72ce5e561f7eeb95d4ffb32697e290190c7902e9570c56b3854753777fc417fd27536fc398c8fefb63b", + "0xb8807232455833e4072df9bffa388ae6e8099758c2a739194719af7d9ed4041974a6cd9605f089de8b43f0e12f181358", + "0x96f79fca72f75dc182c71f2343f0c43b06d98563fd02d2e1fbc031b96601608d8a726c811a74bb51ab8b0a3ce3632dc4", + "0xb5262761680a4235a8c1257de4735cdcadf08d5d12c6e9d4f628464d5c05dfff3884a9ef2af3b7724b5a8c97e6be74eb", + "0xb6ce0eada73433d98f8fae7d55e4ea2b9d9d7a0ae850d328dd06991f27b1f03e470868fb102800ff3efe4ee1698531b9", + "0xa37b7d9fe9d3fdfbc72c59cf6cacc7e7a89d534dea3d73121f7483331aec8ab3fbff58ffabb943b75d6f86df0ba43262", + "0x93fce9be8a27fcaa1283d90d3e87265a6221ee302ec708161a42bd00ffe8e726743d9e187e1bf4307c0e3f25afbb1d44", + "0xa4ea919021346ae7ea69d5e8f46d860b24c35c676b62f4e577c90e0c05c5646fe73721b143b7c38835dd4b443e6c3676", + "0xb79983a5948453f70dfa4c396ce1945204498fe79f40c0667291bd0fdd96ed0b9ea424571f7ade342275c854c9f03d9e", + "0x866f8e395ed730b614b70bf999cad6e87e9086c1f5aea8d69020b562ee285dd0fb93afaca0dd13a0713f74a3f9340f01", + "0xa3fef158782292c6139f9a0d01711aa4ed6f5cac11d4c499e9e65c60469ae3afbde44fb059845973a4b3bbca627b7eb7", + "0xb4a2c0321b68f056e7d8051beede396fa2f0704d8aa34224f79f7b7a62eb485fc81889cb617019622fd5b5fa604516f5", + "0x8f0e3edddbaead9059df94de4139e3a70693c9ea9bc6baaa5695dddfd67263b33926670159846292801941b9a0c6545b", + "0x9804e850f961e091dadd985d43d526ba8054d1bf9c573ed38f24bbd87aeaad4dcba4c321480abc515a16b3b28f27bb2a", + "0x95f330da28af29e362da3776f153f391703a0595323585220712dae2b54362cc6222070edd2f0dd970acfbe2e3147d5c", + "0x82d03b771231179cc31b29fe1e53379d77b5273b5c0a68d973accd7a757c7584dbb37f0507cdfde8807313ec733a6393", + "0x81b3c39a9f632086e97b7c1f0ec7e2eaf9dc3cb0d84dec18a4441dbdc9fe9878fde4bcfa686bca1a9522632a353a5566", + "0xa2db124ab2b493d5f9a1e4ca6b3144593c2fc8bfac129fd79da11dfbb7ef410a234fda9273a50a5ca05d7b37cc2088a2", + "0xaa8550633c9449228702690cc505c0fc4837ea40862058e8f9713622b34d49fdc3a979b9317993c5da53b5bb5b7f4974", + "0xae783bcf7a736fdc815d0205b4c2c2b2fee0a854765228f76c39638ba503e2d37f1e28f6bdf263923f96fead76b4187b", + "0xb5ec86092c1d250251e93bab2f24e321afd2cd24cf49adfcbed9e8bc5142343ae750206c556320551e50fc972142f0da", + "0xb3b5791b590a6e9b3f473d5148624014aa244495249322a5d75cde2c64117ff9d32f4b0698b0e4382e5e7f72933061f8", + "0x876c6a9162c17b16d6b35e6ce1ba32e26aec7dd1368bceab261ab880ad845c91e54b96a52c7d3aafbfbafc0e37139dca", + "0x902ddb5774d20b0707a704486457c29048776a5b88c377b14af6616c8ddf6cd34f49807df9c9d8866d6b39685cfb0f19", + "0x8b87f71f94bc96de927d77a5d7123fa9cdda8c76aff64a5e6112cbc2eca43b07f8376db3e330f8af6a1db9b948908a6a", + "0xa69a5922e572b13d6778218e3657f1e1eea9a9682f6eb1b731d676d03563e14a37ff69bc5e673c74090ecb0969a593f7", + "0xaff3510d78ba72f3cf5e3101847b7c4a956815aa77148689c07864e8a12dd0ef33d5f6c8cb486e0ea55850161f6afed0", + "0xaa9c459cb2a008d94cbee2c6b561d18b0d7c6ffa8a65cbf86ae2c14eec070ee9d5324f5d38f25a945ddcd70307e964c4", + "0x8310e15b050b1e40ece7530b22964bde0fd04f48dfffdec5a0d1fb8af0799a7fdc1d878139fb7cb8d043d3a52c2d1605", + "0xb8f0856ce2c4034ee4041d0383f25fb0eeefc00b82443311a466fc18608313683af2e70e333eb87e7c687e8498e8a1ce", + "0xa8200a75c158fbb78474cab8a543caecd430b5d8b9964fc45d2d494dd938021cd00c7c33413ad53aa437d508f460a42a", + "0xa310091472b5b42b02176b72d5f8120bdb173025de24b420e3ca3fb9a386c39092a1d1bb591c6f68ee97a268a7ff9e95", + "0xb23f1bf8bcec9cb5232b407115eead855fd06f5bf86ba322ad61d45460c84f0f36911aba303de788c9a0878207eac288", + "0xae4c129ad6d08be44690bb84370e48bfd92c5d87940750ee2c98c9a2604456f7f42727ab211989657bb202f6d907df04", + "0x95992057d654f3e189a859346aa9aa009f074cb193b7f5720fa70c2b7c9ce887d886f6cff93fa57c1f7c8eaa187603f6", + "0xad12d560273963da94151dd6be49c665d7624011c67d54ab41447452a866bc997e92a80bdd9ca56a03528e72c456dc76", + "0x8e4eda72e9cfcaa07265bb6a66d88e9ce3390ae1a6b8831045b36ea4156b53d23724824d0f0bca250ce850c5926fa38f", + "0x980fe29c1a267c556532c46130fb54a811944bdfea263f1afcdab248fa85591c22ac26167f4133372b18d9f5cce83707", + "0xa7da9f99ddde16c0eac63d534a6b6776ad89b48a5b9718a2f2331dce903a100a2b7855cf7b257565a326ddc76adc71a5", + "0x8ca854c55e256efd790940cb01125f293e60a390b5bd3e7a60e13ac11a24f350a7eb5ebddfa0a2890905ca0f1980b315", + "0x9440335818859b5e8f180893a8acedceabaaa44e320286506721c639a489b5bfb80b42b28902ee87237b0bd3dd49552a", + "0xb9da545a20a5e7d60fd0c376dcaf4b144f5c5a62c8ffa7b250c53ce44be69c4e0d5e4e11422ef90593ae58ae1df0e5d3", + "0xb75852a850687f477849fc51e0479703cd44428671c71bfdd27fe3e7930b97d2fc55f20348ca4e5bc08db2fc16a4f23c", + "0xb515081d8d099e4b6253c991ca2d3e42633f5832c64aa8f9cde23cb42c097c2c3717c46c5f178f16c58295f97b2b3fe7", + "0x9506c9902419243e73d3197e407985dd5113f16c6be492651bbbf9576621942710aea74522d6fb56d5b52c6ccdaa4307", + "0x952673ae27462a0f6c9545eede245c2f8e2fd6077b72a71f5672f1a5a02c263bc2a66f24f0e30376feb7a8187b715f08", + "0xa8f1e2085ed666a8f86b474d9589dc309d5c83bd53e745f8e09abe0dfbaf53e5384c68580672990344d4aa739438b4d8", + "0xad6e04d4a67a5a5529ceaf7de6e19416be5b4c436610aa576ac04aee3b73317da88f891121f966393a37f52b775a2dd8", + "0xa35a884736f08c7f76923ae7adb17fdac04e6c505178bca9502eaa2ed16d4d93fa953fb6dcf99e9e9962a6eb3eeead00", + "0xb8af72273360bab4b3ca302cf0659717cbfb335fbc9ad4ffdd3340113ece9e63b2bdbd611e5f6b740a4689286f9a452d", + "0xb1a1f4ba2640800c3ed3892e049f6e10f8a571efa3bbe21fe2d6cee8fded171c675a3bb8aa121e2d1d715de84bad2e2b", + "0x8102a6c3598b40da4d6e8eccfdd5dadc8d6262e38b69c5b211b0732f4c6e3045d79fba12770a0b2b66f1e9f4664b1510", + "0x90979587d75bf12819f63832beea7dcbef101f6814bf88db4575bfcd9cf0ea8eceba76d4d6db17630b73b46c1acfe011", + "0x8dd98f14d2beb5b5b79cc30f6825ec11ed76bd5a8864593ffc0c2baffab6872bad182e1c64b93aab8dd5adb465fa5cec", + "0x8083334dadc49c84f936c603a2857f174eda5659ab2b7214572f318aba3ebd7b1c50e7cbea57272b9edf106bd016df3b", + "0xa634d08d2e8641b852e89d7ccab1bab700c32fb143bcbea132f2a5fb2968d74ded2af4107f69818798f0128cc245a8cb", + "0x94fc2dccf746d5b3027f7cf4547edf97097cd11db8d6a304c1c2ca6b3aba28c1af17c08d2bbb66f88c14472e0196a45e", + "0xb257a6fb01424b35e414c1c002e60487abb3b889d74c60cbdbf591e222739c6f97b95f6962842401f5e2009e91b28c55", + "0x81955bdbf25741f3b85d5044898dc76ae51b1b805a51f7c72a389d3b4d94b2e3e0aa1ec271685bbcf192ed80db7367ab", + "0x86eb229b66c542514e42b113b9de7d4f146861a60f2a253264873e7de7da2ac206e156ff11f2de88491b9897174fe2f4", + "0x8b8db00533afbb56b3d7d7a9a4a6af3cebb523699ffcb974603e54f268b3ef739c41cd11850b9651d9640d72217c3402", + "0x8b7cbb72a6c4408d5f1b61001e65de459790444530245d47d4ee8e2d17716695283f21540bd7ac4f5a793a0d00bdf1d4", + "0x875920b9bab4bc1712e6af89ae2e58e9928c22095026070b07e338421b554d9f96e549ac3706c6c8d73f502913a27553", + "0x9455d192db7b039b3e8f0bc186c25ff07dfbe90dab911e3c62e3bd636db8019ed712cbb0ecd5cbb9a36c11034e102aba", + "0x8cb0b28e5d3838d69f6c12274d6b1250f8843938065d0665b347977fa3c1c685caef6930bae9483ed0d0a67005baad76", + "0x94df2e14aae1ae2882ab22a7baf3dc768c4a72b346c2d46bfd93d394458398f91315e85dc68be371f35d5720d6ca8e11", + "0xaacd94b416bfbeb5334032701214dd453ad6be312f303b7bec16a9b7d46ab95432a14c0fbf21a90f26aafb50ec7bb887", + "0xb43d26963665244633cbb9b3c000cacce068c688119e94cc0dac7df0e6ee30188e53befff255977788be888a74c60fc2", + "0xb40d67c9ad0078f61e8744be175e19c659a12065fe4363b0e88482b098b2431612e7c2fa7e519a092965de09ceafe25c", + "0x82cd4a4e547c798f89ce8b59687614aa128877e6d38b761646d03dc78f6cdd28054649fb3441bcd95c59b65a6d0dd158", + "0xa058e9700f05cef6e40c88b154d66a818298e71ae9c2cf23e2af99a0a7dc8f57fbe529d566cb4247432e3c1dee839b08", + "0x95c6f84406466346c0b4a2a7331ac266177fb08c493d9febb284c5ca0b141ccc17aa32407f579666b208fb187c0227dd", + "0x905d1d47a26b154f44d7531c53efbc3743ff70bd7dba50c9b9d26636767b0ae80de3963c56d4604399126f4ad41a0574", + "0x83dfa11c520b4abaefe1b2bc1ce117806e222f373cd4fb724f3c037c228e3379d27a364e68faa73984ba73a0845f1b9a", + "0xa16e54786ba308a9c0241aff8f1bf785dece387d93bd74aa31de0969e3431479e2c0abebff9939a6644d2b0af44f80bb", + "0x81ac565212365176f5be1c0217f4e7c9fdbc9fe90f16161367635d52edcf57af79290531d2e8b585e1223d33febd957d", + "0xa296f4b09915e5d80ff7274dc3ffc9b04f0427e049ea4ef83dca91095275e8a260ef0335c7b6585953b62682da8c8e99", + "0xa9150626208168a21ae871192ca9f11c1f7f6e41e8e02de00732de2324d0d69fe52f8762155c9913ee408a034552e49a", + "0xa42a56008ca340c6e9ff5a68c8778bb899ba5de9e7508c0cac355c157979a7ff6a6bd64f98b182114d3831cfa97ee72b", + "0xa4f05adf22c051812279258eea9eb00956b04ef095f2ca175f775ff53c710fb0020266adabd1dacaee814c4f1d965299", + "0x967492e78ac0bceb8ad726ea0d2292b760043d16d64a6b1bb896e32630a7bf405c2b20e4e00842ae519a21697ff8db2d", + "0xadbf05e9b5931ae3dd24d105b5c523c221a486a4123c727069b9e295a5bc94f3e647a3c2cde1f9f45dbd89df411453c9", + "0xa1759c0ebebd146ee3be0e5461a642938a8e6d0cdd2253ebd61645b227624c10c711e12615cd1e7ea9de9b83d63d1a25", + "0xa4c5945d635b9efc89ad51f5428862aefe3d868d8fb8661911338a6d9e12b6c4e5c15a25e8cb4a7edc889b9fa2b57592", + "0xaff127675ea6ad99cb51c6e17c055c9f8fd6c40130c195a78afdf4f9f7bc9c21eed56230adb316d681fc5cacc97187da", + "0x9071294e8ff05b246ff4526105742c8bf2d97a7e7913f4541080838ecfd2dbc67c7be664a8521af48dbc417c1b466a85", + "0x990880b0dd576b04f4b4ce6f0c5d9ff4606ec9d3f56743ac2f469ac6a78c33d25c3105cf54f675e300ac68073b61b97a", + "0xa8d1a62ce47a4648988633ed1f22b6dea50a31d11fdddf490c81de08599f6b665e785d9d2a56be05844bd27e6d2e0933", + "0x8ea5a6c06f2096ded450c9538da7d9e402a27d070f43646533c69de8ea7993545673a469c0e59c31520e973de71db1b4", + "0x99d3a098782520612b98a5b1862ae91bcb338ab97d1a75536e44b36a22885f1450a50af05c76da3dd5ca3c718e69fdd4", + "0xb987451526e0389b5fe94c8be92f4e792405745b0a76acd6f777053d0809868657ba630aa5945f4bd7ce51319f8996f7", + "0xafffccc5ddd41313888a4f9fee189f3d20d8b2918aa5ad0617009ea6d608e7968063c71bd5e6a1d7557880d9a639328d", + "0x8ac51a02505d5cadfd158dde44932ab33984c420aeceb032ed1ee3a72770d268f9e60ccf80ce8494dfc7434b440daafd", + "0xb6543e50bd9c6f8e0862850c3d89835ddd96231527681d4ab7ae039c4a3a5a0b133a6d40cdb35c8a6c8dbb8d421d3e2b", + "0xa2ba901f4fde2b62274d0c5b4dbbea8f89518571d8f95ec0705b303b91832f7027704790a30f7d9d2cdafde92f241b3e", + "0xa6974b09280591c86998a6854a7d790f2a6fbe544770e062845cfc8f25eb48c58f5dfb1b325b21f049d81998029ad221", + "0x890baeb336bbf6c16a65c839ffaab7b13dd3e55a3e7189f7732dbcb281b2901b6d8ba896650a55caa71f0c2219d9b70e", + "0xb694211e0556aebbe4baf9940326e648c34fda17a34e16aa4cefd0133558c8513ffb3b35e4ee436d9d879e11a44ec193", + "0x97cf9eb2611d467421a3e0bfe5c75382696b15346f781311e4c9192b7bca5eb8eaf24fa16156f91248053d44de8c7c6f", + "0x8247f88605bd576e97128d4115a53ab1f33a730dc646c40d76c172ca2aa8641c511dddad60ee3a6fbe1bb15cac94a36c", + "0xae7ecd1c4a5e9e6b46b67366bc85b540915623a63ab67e401d42ca1d34ae210a0d5487f2eef96d0021ebecfd8d4cd9a8", + "0xaec5123fff0e5d395babe3cb7c3813e2888eb8d9056ad4777097e4309fb9d0928f5c224c00260a006f0e881be6a3bf8f", + "0x8101724fa0ce7c40ea165e81f3c8d52aa55951cc49b4da0696d98c9fafd933e7b6c28119aa33f12928d9f2339a1075d1", + "0xa8360843bab19590e6f20694cdd8c15717a8539616f2c41a3e1690f904b5575adb0849226502a305baefb2ead2024974", + "0xade5cad933e6ed26bba796c9997b057c68821e87645c4079e38e3048ea75d8372758f8819cde85a3ab3ab8e44a7d9742", + "0xab1fe373fb2454174bd2bd1fe15251c6140b4ac07bda1a15e5eabf74b6f9a5b47581ef5f0dbd99fdf4d1c8c56a072af7", + "0xb425e1af8651e2be3891213ff47a4d92df7432b8d8ea045bb6670caf37800a4cd563931a4eb13bff77575cbcae8bc14f", + "0xb274799fe9dd410e7aed7436f0c562010b3da9106dc867405822b1e593f56478645492dbc101a871f1d20acf554c3be6", + "0xb01a62a9d529cc3156bc3e07f70e7a5614b8d005646c0d193c4feb68be0b449d02b8f0000da3404e75dbdfa9ca655186", + "0x878b95e692d938573cdb8c3a5841de0b05e5484a61e36ea14042f4eadb8b54a24038d2f09745455715d7562b38a8e0df", + "0xa89e998e979dba65c5b1a9000ad0fd9bb1b2e1c168970f2744982781306bbe338857e2fac49c8cafda23f7cc7c22f945", + "0x85880fdf30faed6acce9973225e8fe160e680a55fc77a31daacf9df185453ad0c0552eb3fd874698ad8e33c224f7f615", + "0xac28d20d4bbb35ba77366272474f90f0ed1519a0e4d5de737adee2de774ccd5f115949e309e85c5883dbc63daaa6e27b", + "0xa1758ac86db859e323f5231ad82d78acbe11d53d3ebf7e644e581b646eede079d86f90dc23b54e5de55f5b75f7ea7758", + "0xae4c0b84903f89353bf9a462370f0bf22c04628c38bb0caae23d6e2d91699a58bd064e3c2b1cbda7f0a675d129f67930", + "0x95f21a099ffc21a0f9064d9b94ce227b3ff0a8c5a2af06ff5ee6b7f3248a17a8ca2f78cd7929ef1d0784f81eddefcd48", + "0x8d06fbc1b468f12b381fd1e6108c63c0d898ddf123ea4e2e1247af115043c4f90b52796076277b722dd2b92708f80c21", + "0xa300f39039d8b2452e63b272c6d1f6d14a808b2cd646e04476545da65b71a6e29060f879409f6941c84bde9abe3c7d01", + "0xadecce1ccc5373072ba73930e47b17298e16d19dbb512eed88ad58d3046bb7eec9d90b3e6c9ba6b51e9119cf27ce53f2", + "0x941a7e03a64a2885d9e7bee604ddc186f93ff792877a04209bbee2361ab4cb2aed3291f51a39be10900a1a11479282ca", + "0xacbcb1ab19f3add61d4544c5e3c1f6022e5cc20672b5dc28586e0e653819bdae18cda221bb9017dfaa89c217f9394f63", + "0xb8d92cea7766d3562772b0f287df4d2e486657b7ab743ed31ec48fdc15b271c2b41d6264697282b359f5cb4d91200195", + "0x957360ecb5d242f06d13c1b6d4fcd19897fb50a9a27eb1bd4882b400dc3851d0871c0c52716c05c6c6cf3dee3d389002", + "0xabd2a23abbc903fbb00454c44b9fb4a03554a5ef04101b2f66b259101125058346d44d315b903c6d8d678132f30b1393", + "0xae9572beff080dd51d3c132006107a99c4271210af8fbe78beb98d24a40b782537c89308c5a2bddfdfe770f01f482550", + "0x82c7e5a5e723938eb698602dc84d629042c1999938ebd0a55411be894bccfb2c0206ac1644e11fddd7f7ab5ee3de9fdc", + "0xaba22f23c458757dc71adb1ce7ef158f50fdd1917b24d09cfc2fbbcbe430b2d60785ab141cf35ad9f3d0a2b3e2c7f058", + "0x8eff41278e6c512c7552469b74abedf29efa4632f800f1a1058a0b7a9d23da55d21d07fdbb954acb99de3a3e56f12df6", + "0x8abd591e99b7e0169459861a3c2429d1087b4f5c7b3814e8cee12ecc527a14a3bdda3472409f62f49a1eb4b473f92dbf", + "0x82dcbff4c49a9970893afc965f1264fcab9bae65e8fb057f883d4417b09e547924123493501c3d6c23a5160277d22a8e", + "0xb5a919fcb448a8203ad3a271c618e7824a33fd523ed638c9af7cfe2c23e3290e904d2cd217a7f1f7170a5545f7e49264", + "0x96d6834b592ddb9cf999ad314c89c09bedc34545eeda4698507676674b62c06cc9b5256483f4f114cd1ed9aaec2fba5e", + "0xa4e878cf4976eb5ff3b0c8f19b87de0ef10cd8ec06fe3cd0677bd6be80ba052ff721a4b836841bdffb1df79639d0446c", + "0x8e15787a8075fd45ab92503120de67beb6d37c1cc0843c4d3774e1f939ac5ed0a85dad7090d92fa217bd9d831319021b", + "0x8506c7fea5a90cd12b68fdbbae4486a630372e6fd97a96eea83a31863905def661c5cdead3cf8819515afe258dbcd4d9", + "0x952ef3bc16a93714d611072a6d54008b5e1bf138fd92e57f40a6efb1290d6a1ffcc0e55ff7e1a6f5d106702bd06807cd", + "0xa5f7761fa0be1e160470e3e9e6ab4715992587c0a81b028c9e2cf89d6f9531c2f83c31d42b71fca4cc873d85eba74f33", + "0xb4811f0df11ff05bf4c2c108a48eece601109304f48cde358400d4d2fa5c1fdaaf3627f31cb3a1bdd3c98862b221720d", + "0x9207ad280b0832f8687def16ad8686f6ce19beb1ca20c01b40dd49b1313f486f2cb837cfbbf243be64d1c2ab9d497c3f", + "0xb18a8c1e6363fadd881efb638013e980e4edb68c1313f3744e781ce38730e7777f0cba70ea97440318d93a77059d4a2b", + "0x901faf777867995aac092f23c99c61f97eeadf4ac6bcb7791c67fa3c495947baef494b2aace77077c966c5d427abbf92", + "0xa123281aca1c4f98f56cff7ff2ae36862449f234d1723b2f54ebfccd2740d83bd768f9f4008b4771e56c302d7bfc764f", + "0x8cffe1266468cad1075652d0765ff9b89f19b3d385e29b40f5395b5a3ad4b157eed62e94279ac3ec5090a6bad089d8b3", + "0x8d39870719bc4ebbcecba2c54322111b949a6ed22bda28a6cea4b150272e98c9ded48cc58fc5c6e3a6002327856726ec", + "0xb3d482c00301f6e7667aaeaf261150b322164a5a19a2fa3d7e7c7bf77dc12fa74f5b5685228ab8bf0daf4b87d9092447", + "0x801acb8e2204afb513187936d30eb7cab61f3fbb87bfd4cd69d7f3b3ddba8e232b93050616c5a2e6daa0e64cef6d106f", + "0xac11e18adda82d2a65e1363eb21bda612414b20202ecc0e2e80cc95679a9efa73029034b38fd8745ce7f85172a9ab639", + "0xb631d6990d0f975a3394f800f3df1174a850b60111567784f1c4d5bba709739d8af934acfa4efc784b8fc151e3e4e423", + "0xaeda6279b136b043415479a18b3bbff83f50e4207b113e30a9ccfd16bd1756065fc3b97553a97998a66013c6ac28f3d8", + "0x8840b305dc893f1cb7ad9dd288f40774ec29ea7545477573a6f1b23eaee11b20304939797fd4bcab8703567929ce93ad", + "0x963cc84505a28571b705166592bffa4ea5c4eeafe86be90b3e4ae7b699aaaca968a151fe3d1e89709fe0a3f0edf5d61a", + "0x8e1ec0d0e51f89afea325051fc2fa69ab77d6c7363cc762e470a9dfa28d4827de5e50f0b474c407b8c8713bad85c4acd", + "0x909f313420403cb36c11d392cf929a4c20514aa2cb2d9c80565f79029121efd5410ef74e51faba4e9ba6d06fcf9f1bd1", + "0xb2992b45da467e9c327ac4d8815467cf4d47518fc2094870d4355eb941534d102354fbda5ab7f53fbf9defa7e767ca13", + "0x9563b50feb99df160946da0b435ac26f9c8b26f4470c88a62755cdf57faebeefffff41c7bdc6711511b1f33e025f6870", + "0xa2a364d9536cd5537a4add24867deec61e38d3f5eb3490b649f61c72b20205a17545e61403d1fb0d3a6f382c75da1eb3", + "0x89b6d7c56251304b57b1d1a4255cb588bd7a851e33bf9070ee0b1d841d5c35870f359bc0fdc0c69afe4e0a99f3b16ec2", + "0xa8ae1ee0484fe46b13a627741ddcdae6a71c863b78aafe3852b49775a0e44732eaf54d81715b1dca06bb0f51a604b7e2", + "0xb814ecbfbc9645c46fc3d81c7917268e86314162d270aed649171db8c8603f2bd01370f181f77dbcbcc5caf263bedc6c", + "0x8e5d7cc8aad908f3b4e96af00e108754915fecebdb54f0d78d03153d63267b67682e72cd9b427839dca94902d2f3cda7", + "0x8fc5ff6d61dd5b1de8c94053aef5861009cb6781efcca5050172ef9502e727d648838f43df567f2e777b7d3a47c235dd", + "0x8788eea19d09e42b0e3e35eb9bcd14f643751c80c6e69a6ff3a9f1711e8031bbe82ccd854a74a5cfcf25dda663a49a62", + "0x95d441d8cd715596343182ddcecb8566d47eaa2d957d8aea1313bbed9d643a52b954443deb90a8037a7fa51c88eec942", + "0xa15efd36ef72783ccdc6336ef22a68cc46b1ecec0f660cfe8a055952a974342bf30f08cb808214bce69e516ff94c14c5", + "0xacc084d36907a16de09a5299f183391e597beaf9fa27d905f74dc227701a7678a0f5a5d1be83657de45c9270a287ec69", + "0xb3fd385764356346061570beb760ccf3808619618fd7521eb0feadc55b8153ef4986ff0cbfcbd4153ad4ea566989d72a", + "0x91ec6b26725532e8edfda109daa7ce578235f33bd858238dfa2eb6f3cd214115b44cce262a0f2f46727a96b7311d32e1", + "0x96b867ccddb73afe1049bda018c96cfe4083fff5bb499e6a4d9fd1a88a325144f9a08cb0aee310e1bb4f6a5793777e80", + "0xad10c18465910152676f1bc6a40986119607b5c272488e6422cfda2eb31da741af13a50f5de84037348014a869c8e686", + "0x86ade2dbc4cceb52b84afe1c874d1e3644691284c189761febc4804b520adf60b25817e46f3f3c08d2ab227d00b93076", + "0x998b949af82065c709fc8f63113a9fecdd1367fc84fc3b88857d92321ba795e630ce1396a39c2e056b5acd206ee011d8", + "0x8dec440bbd17b47dfd04e566c2d1b46f9133023b982fdc5eaeae51404bc83a593f8d10c30b24e13aec709549137cae47", + "0x89436ff47431b99f037cddaee08bb199be836587a7db6ed740317888638e5f4bebbb86b80549edff89678fc137dfb40a", + "0xa8e9960746769b3f76246c82cd722d46d66625e124d99a1f71a790c01cec842bcf6c23c19cc7011ec972cedf54dc8a4c", + "0x980979dafedfd75ff235b37e09e17361cfdda14a5ac3db0b90ed491abfd551916016b2254538da7f4b86ece3038b1b1c", + "0x8ec340ca7654720bb9d2f209985439ebbc3f9990ef27e7d7ae366e0c45b4ed973316943122119604ea9a87fc41ebd29f", + "0xab24440a40ab238d8cd811edb3ef99948ae0f33bf3d257b22c445204016cce22b6f06a1ca979fa72a36c4ddedc2b3195", + "0xa1bcd2473ac7cfebfa61c10e56cae5422c6b261a4a1be60b763fcbcdf2eae4ccf80695f09b062b6cf5654dfab0ee62a5", + "0x9027a613ce7bd827110a3a0e63e83f652e9bc7f4ce8da26c38b28ee893fd0c38bdb20f63a33470a73cb77f776244ab4a", + "0x86911cc8aeb628197a22bf44d95a0b49afb8332c38857fba8e390c27c527b8b45335e22b0f2e0a3395c16ced3c1ed2e8", + "0x8f0529a330a3e9967dce09357d774715fd305bd9e47b53b8b71a2a1303d390942a835aa02fb865a14cfed4f6f2f33fe6", + "0xb71ec81a64c834e7e6ef75b7f321a308943b4bad55b92f4dbaf46658613cebf7e4b5b1bc7f1cdc5d50d1a2a0690e2766", + "0x98d66aaed9fb92f4c7bb1b488ccbca5e570aa14433028867562a561d84f673ac72e971cbe2cb3cbbb0a702797dc45a7e", + "0x8380aa94d96c6b3efd178de39f92f12ca4edd49fe3fe098b2b7781e7f3e5f81ee71d196fb8e260d1d52f2e300e72e7bc", + "0x8c36296ff907893ac58cecadd957b29f5508ae75c6cc61b15ae147b789e38c0eace67963ae62eff556221b3d64a257a2", + "0x97e17676cbc0f62a93555375e82422ee49bc7cf56ad6c3d69bb1989d1dc043f9f7113d0ed84616dde310441b795db843", + "0xa952229615534c7e9a715409d68e33086cdaddf0aec51f4369c4017a94ec3d7113a045054d695fb9d7fd335527259012", + "0x817b90958246f15cbd73a9679e10192ca7f5325b41af6388b666d8436706dea94eafffbc3b8d53057f67ad726dbcd528", + "0x95776e378c8abd9223c55cd6a2608e42e851c827b6f71ad3d4dc255c400f9eccf4847c43155f2d56af0c881abef4acfa", + "0x8476c254f4b82858ecbe128ed7d4d69a6563fd9c5f7d4defc3c67e0bfa44e41cfd78b8e2a63b0773ce3076e01d3f6a7d", + "0xa64b0b189063d31bcae1d13931e92d5ab0cfc23bf40566ac34b5b8b711d0e7d941102e6beb140547512e1fe2d9342e6c", + "0x9678460acff1f6eae81a14d5c8049cdcd50779a8719b5c5861762a035b07f7fa1b1ada8b6173f9decf051fd5a55bebd8", + "0x88398758ce86ed0388b13413a73062adb8a026d6b044cd1e7f52142758bed397befee46f161f8a99900ae6a2b8f6b89f", + "0xa7dfaf40637c81d8b28358b6135bd7ad9cc59177bd9bc8e42ba54d687d974cdf56be0457638c46b6a18ceaa02d3c53f3", + "0xb0e885e5d48aa8d7af498c5e00b7862ed4be1dad52002f2135d98e8f2e89ca0b36cf95b3218aad71d5b4ada403b7045b", + "0x803b0e69a89e8de138123f8da76f6c3e433402d80d2baba98cde3b775a8eda4168530a49345962c4b25a57257ba9f0a7", + "0x8ce6ef80dadb4b1790167fbc48be10ef24248536834ff2b74887b1716c75cb5480c30aa8439c20474477f1ac69734e61", + "0x824764396e2b1e8dcc9f83827a665ef493faec007276f118b5a1f32526340b117c0df12bea630030a131bf389ec78fc3", + "0x874edb379ce4cc8247d071ef86e6efbd8890ba6fcb41ea7427942c140347ebf93e8cf369d1c91bd5f486eb69b45bce70", + "0xadadcb6eb4cafa1e2a9aef3efb5b09ffa2a5cf3ce21f886d96a136336be680dabc0a7c96ec327d172072f66d6dcdbb39", + "0xb993591b280e1f3527f083d238a8f7cf516d3cf00c3690d384881911c1495192a419b8e37872a565ce8007eb04ebe1b6", + "0xb125faaeca3f0b9af7cb51bb30a7c446adbb9a993b11600c8b533bff43c1278de5cdda8cb46a4df46f2e42adb995bce8", + "0xa7efe1b57326b57c2c01720d4fdf348d6a84d35f229d32a8f2eb5d2be4e561ef8aea4d4d0bcfcbf17da10a8e49835031", + "0xa6bd4f5a87574b90a37b44f778d5c7117d78eb38f3d7874bad15ae141b60eed4ab0a7281ed747297f92e0b3fe5f9cafa", + "0x94b5e3067ca1db3c4e82daf6189d7d00246b0360cb863940840358daa36cb33857fde4c01acd0457a90e15accee7d764", + "0xa5ff3ab12197b8a07dd80222a709271ab3b07beba453aacbaf225cfb055d729e5a17a20f0ff9e08febf307823cba4383", + "0xa76dd8aa2b6a957ed82ecec49b72085394af22843272f19360a5b5f700910c6ec65bf2a832e1d70aa53fd6baa43c24f6", + "0x8dfcbe4143ae63c6515f151e78e6690078a349a69bb1602b79f59dc51dea7d00d808cf3e9a88b3f390f29aaae6e69834", + "0x8c6134b95946a1dd54126952e805aeb682bc634c17fe642d5d3d8deffffd7693c90c4cd7d112890abfd874aa26736a93", + "0x933531875561d327c181a2e89aaaac0b53e7f506d59ef2dfc930c166446565bd3df03bab8f7d0da7c65624949cfbae2f", + "0xac6937c5e2193395e5bb69fd45aa6a9ae76b336ea7b6fd3e6aeac124365edcba7e918ec2c663fb5142df2f3ad03411a6", + "0xa8f0f968f2a61d61d2cf01625e6ac423b447d3e48378ea70d6ff38bc98c42e222fe3cbcb04662b19973a160dc9f868a2", + "0x94100a36f63d5c3a6cfb903c25a228389921684cc84f123390f38f90859f37ec9714942ffe6766f9b615101a3c009e43", + "0xb5321b07f5b1eb2c1c20b0c8ab407f72f9705b55a761ec5176c5bcc6e585a01cae78546c54117ca3428b2b63793f2e65", + "0x9922f61ed6763d1c4d12485c142b8ff02119066b5011c43e78da1ee51f10a1cf514329874061e67b55597ca01a7b92ab", + "0xa212eb2d72af0c45c9ef547d7c34ac5c4f81a4f5ec41459c4abd83d06ec6b09fdab52f801a2209b79612ae797fa4507b", + "0x8577d2d8f17c7d90a90bab477a432602d6918ca3d2af082fbb9e83644b93e21ca0bced7f90f6e9279eaa590f4e41dc4d", + "0x9002d424e3bebd908b95c5e6a47180b7e1d83e507bfb81d6ad7903aa106df4808c55f10aa34d1dccad3fab4d3f7a453e", + "0xb9050299bf9163f6ebeff57c748cb86f587aea153c2e06e334b709a7c48c4cbfba427babf6188786a0387b0c4f50b5ce", + "0x852ae1195cc657c4d4690d4b9a5dea8e0baaa59c8de363ba5fccd9e39ec50c6aa8d2087c8b7589b19248c84608f5d0a8", + "0xa02ff5781417ca0c476d82cf55b35615f9995dc7a482124bc486e29b0b06a215fbe3e79228c04547c143d32cd3bac645", + "0x8d7bc95e34bc914642e514a401448b23cf58bce767bab1277697327eb47c4a99214a78b04c92d2e3f99a654308b96e34", + "0xadb28445d3b1cc7d4e4dd1f8b992a668f6b6f777810465fdab231fd42f06b5bada290ba9ae0472110366fad033da514e", + "0xa0c72b15a609f56ff71da17b5b744d8701af24b99fbc24a88588213864f511bfa592775e9ab4d11959f4c8538dc015b8", + "0x933205a40379d5f5a7fb62cda17873fbbd99a0aaa8773ddf4cd2707966d8f3b93a107ebfe98b2bb222fe0de33ef68d03", + "0x90690c1a4635e2e165773249477fc07bf48b1fd4d27c1b41a8f83a898c8d3763efb289867f8d6b0d354d7f4c3f5c7320", + "0x99858d8c4f1be5a462e17a349b60991cb8ce9990895d6e42ae762ce144abc65b5a6f6e14df6592a4a07a680e0f103b2a", + "0xb354a7da06bd93fb5269e44925295b7c5049467b5cacce68cbb3cab60135b15e2010037a889cb927e6065053af9ccb77", + "0xaf01fc4ac396d9b15a4bbd8cc4fe7b30c32a9f544d39e88cdcb9b20c1c3056f56d92583a9781ddb039ec2eeda31fb653", + "0xa8d889fb7155f7900982cf2a65eb2121eb1cc8525bbee48fae70e5f6275c5b554e923d29ebbd9772b62109ff48fb7c99", + "0xb80edae6e26364c28749fd17c7c10eb96787053c7744a5cc6c44082ae96c5d3a4008c899a284f2747d25b72ecb9cb3d0", + "0xb495b37503d77e7aafc226fca575e974b7bb6af2b7488372b32055feecc465a9f2909729e6114b52a69d8726e08739cb", + "0xa877f18b1144ff22e10a4879539968a01321cecde898894cbe0c34348b5e6faa85e1597105c49653faed631b1e913ec7", + "0x8c235c558a065f64e06b4bb4f876fe549aab73302a25d8c06a60df9fad05843915ac91b507febca6fe78c69b51b597de", + "0xb4c31398b854ccc3847065e79329a3fdae960f200c1cce020234778d9c519a244ff1988c1fbc12eb3da2540a5fa33327", + "0xb7bd134b3460cb05abf5aed0bc3f9d0ccbfac4647324bedbdf5011da18d8b85dc4178dd128f6ddbe9d56ea58f59d0b5d", + "0x92594c786c810cf3b5d24c433c8a947f9277fe6c669e51ceb359f0ae8a2c4e513a6dad1ae71b7ded3cdca823a51e849b", + "0xb178535e043f1efcce10fbec720c05458e459fdda727753e0e412ef0114db957dc9793e58ec2c031008e8fb994145d59", + "0xb31da7189abf3e66042053f0261c248d4da142861bfd76a9aced19559be5284523d3e309ef69843772b05e03741a13fe", + "0xb190a8c1a477e4187fecff2a93033e77e02de20aae93dda1e154598814b78fdf8b9ff574c5f63047d97e736e69621462", + "0x98234bd1d079c52f404bf5e7f68b349a948ec1f770c999c3c98888a55d370982bfa976e7e32848a1ebb4c7694acc1740", + "0x99b9eeb33a6fb104bba5571a3822ebe612bf4b07d720d46bde17f0db0b8e8b52165f9b569be9356a302614e43df3e087", + "0xa1e3915b0dd90625b424303860d78e243dda73eecd01cba7c33100b30471d0a1ec378c29da0f5a297008b115be366160", + "0x975118bf6ca718671335a427b6f2946ee7ece2d09ccfb1df08aa1e98ff8863b6c8b174c608b6b2f4b1176fb3cbc1e30d", + "0x903cb1e469694b99360a5850e2ca4201cad23cfccce15de9441e9065eb3e6e87f51cba774ab9015852abd51194c25e57", + "0x821f7ff4d0b133e3be4e91d7ff241fa46c649ff61fc25a9fdcf23d685fe74cf6fade5729763f206876764a3d1a8e9b24", + "0xa1ee8db859439c17e737b4b789023d8b3ce15f3294ec39684f019e1ea94b234ec8a5402bc6e910c2ed1cd22ff3add4de", + "0xaf27383148757bdf6631c0ea8a5c382f65fc6ab09f3d342a808ca7e18401e437cd1df3b4383190fdf437a3b35cbcc069", + "0x8310551d240750cef8232cd935869bad092b81add09e2e638e41aa8a50042ce25742120b25fb54ebece0b9f9bdb3f255", + "0x8b1954e0761a6397e8da47dc07133434ebe2f32c1c80cd1f7f941f9965acdf3d0c0b1eb57f7ff45a55697d8b804e1d03", + "0x8c11612381c6be93df17851d9f516395a14a13c7816c8556d9510472b858184bf3cc5b9d14ded8d72e8fb4729f0b23ba", + "0xb413ac49121c7e8731e536b59d5f40d73a200c4e8300f8b9f2b01df95a3dc5fe85404027fc79b0e52946e8679b3a8e43", + "0x8451e5c1c83df9b590ec53d1f1717d44229ed0f0b6e7011d01ea355d8b351f572866b88032030af372bd9104124df55a", + "0x8d0a5c848ec43299bc3ea106847ed418876bc3cd09b2280c2a9b798c469661505ed147a8f4ffba33af0e1167fdb17508", + "0xa6aa97a1f10709582471000b54ec046925a6ad72f2b37c4435621c9f48026d3e332b8e205b6518f11b90b476405960a9", + "0x97696635b5a2a6c51de823eea97d529f6c94846abb0bd4c322b108825589eba9af97762484efaac04ee4847fb2fb7439", + "0x92fd142181fe6ca8d648736866fed8bc3a158af2a305084442155ba8ce85fa1dfb31af7610c1c52a1d38686ac1306b70", + "0xae3da824ecc863b5229a1a683145be51dd5b81c042b3910a5409ca5009ba63330e4983020271aa4a1304b63b2a2df69e", + "0xaecc0fe31432c577c3592110c2f4058c7681c1d15cd8ed8ffb137da4de53188a5f34ca3593160936119bdcf3502bff7c", + "0x821eac5545e7f345a865a65e54807e66de3b114a31ddeb716f38fe76fdd9d117bee0d870dd37f34b91d4c070a60d81f4", + "0x91a02abb7923f37d9d8aa9e22ded576c558188c5f6093c891c04d98ab9886893f82b25b962e9b87f3bf93d2c37a53cb9", + "0x99a96f5d6c612ee68e840d5f052bf6a90fecfd61891d8a973e64be2e2bdd5de555b1d8bffbd2d3c66621f6e8a5072106", + "0xb1d5ec8f833d8fbb0e320ff03141868d4a8fff09d6a401c22dbefadbb64323e6d65932879291090daf25658844c91f2e", + "0xa06afd66ebc68af507c7cf5ab514947ca7d6ccc89fb2e2e8cb6e5ae0f471473e5fba40bb84d05f2c0f97c87f9a50cb73", + "0x83de3ca182bcf1eac0cc1db6ad9b1c2a1ecd5e394e78add7faa36e039a1b13cb0d1d2639892489df080fbf43e5cef8d5", + "0xadf77fc7b342ff67a2eddaa4be2f04b4e6ceaca8ea89a9fc45cc892fcce8ac3cf8646cfa5aab10ac9d9706ce4c48a636", + "0x8509a430ef8dc9a0abc30ef8f8ccdb349d66d40390fb39f0d3281f3f44acb034625361270162822ef0743d458a82b836", + "0x8350fc09e8617826f708e8154a3280d8753e7dbbcf87e852f9b789fdbeb10bf3fed84fb76edd7b8239a920c449e2f4b7", + "0xa2e7a29da8391a5b2d762bf86cb6ae855cdfad49821175f83f4713dd0c342a0784beba98d4948356985a44d9b8b9d0f7", + "0xa99c50a1a88b8efe540e0f246439db73263648546d199ef0d5bc941524a07d7e02b3ef6e5b08dc9e316b0b4c6966823e", + "0xb34ba55136c341f4ca2927080a07476915b86aa820066230903f1f503afebd79f2acf52a0bc8589b148d3a9a4a99f536", + "0xaf637be5a3e71c172af1f2644d3674e022bc49c393df565ea5b05ce6401a27718c38a9232049dd18cbd5bf4f2ce65b32", + "0xa2972ba7bfa7f40c2e175bb35048a8ef9bc296d5e5a6c4ca7ab3728f4264d64f2d81d29dce518dc86849485ff9703d7d", + "0x8c9db203e8726299adeb331d6f4c235dc3873a8022138d35796fb7098887e95e06dcfad5d766ceaa2c4fb0f8857f37fa", + "0xa82bfbaa9a6379442109e89aad0c0cfc6a27d4a5db5480741a509d549c229cb847b46a974dde9f1398c6b3010530f612", + "0xb2d8ef6e091a76dfc04ab85a24dbe8b5a611c85f0ed529a752c2e4c04500de5b305c539d807184e05f120be2c4a05fc3", + "0x8c6ffc66a87d38cea485d16ee6c63ce79c56b64ae413b7593f99cc9c6d3cd78ef3fa2ab8a7943d2f0e182176642adadb", + "0xacbc92de68b2b04e3dc128109511a1cbe07518042f365d5634e8b651cb1ac435ea48eeeb2b921876239183096ef6edee", + "0x979c4e1165e0ecfa17ed59fb33f70797e000ddbb64acf5fc478cccde940451df051e51b6449c5b11a36afa7868af82e3", + "0xa5a017c5a94952aeae473976027124231abe50460cec4db3ebeb8b1290525776be7c15d108b749c2a1e4b018de827915", + "0x8b6922ab1db925eed24b2586e95f5c709b79d2408a8fa2a71057045ead3ebdd0cc72bee23d9064cd824166eda1e29318", + "0x89a991087a0b5805fcc5c6c5f6ac27e100da0d3713645aa9c90114e68ca9f185f21155eb7645a2c6c0616a47291fe129", + "0xae6ef954c942cbfd37f8f2dc58a649e2584d6777e7eb09ae6992ccde283ac4f4ec39e3a5cda7f7c60f467fb308d37f08", + "0x9335ca5ccac59b39eb2bcef09c54b778ebb690415ba13fe5c8e4b6091d9343a01cc9baa6228cefd8dba98f0710f714da", + "0xa0211c9328be2b46f90ff13614eeffb4c1285e55580db3874610653219926af1d83bda5b089fd37a7c7440a0f1d94984", + "0xa82e097dfa782c40808fac5d8ed1c4fccf6b95ef92e22276fd8d285303fcf18c46d8f752595a658ee5294088b9dc6fc0", + "0xad108fcd0ead65f7f839a1337d520f5bd0cb665ee7100fc3f0563ff1d2959eb01617de8eb7a67c9b98b7b4892082acdb", + "0xb89e6aeabcb3ee3cbf12e3c836bab29e59d49676bcf17a922f861d63141076833f4149fe9e9c3beed24edfacdf1e248b", + "0x8477501bd91211e3b1f66c3bfd399ef785271511bc9366366ce95ec5ea95d9288ab0928a6b7887aba62de4da754d3eaf", + "0xaeec40c04b279096946b743ad8171bf27988405e1321c04894d9a34e2cbd71f444ff0d14da6cda47e93aa6fe9c780d50", + "0xa703bd2d8a5c3521a8aad92afef5162aed64e9e6343d5b0096ca87b5b5d05e28ed31ba235ab1a626943533a57872dd01", + "0xb52d9dfc12c359efb548d7e2b36ddedaefdec0ef78eda8ac49a990b3eb0ed7668690a98d4d3c7bec4748a43df73f0271", + "0xaf887c008bad761ee267b9c1600054c9f17f9fc71acfe0d26d3b9b55536bca5c8aebe403a80aa66a1e3748bb150b20ef", + "0xad2f7a545ef2c2a2978f25cf2402813665c156bab52c9e436d962e54913c85d815f0ba1ce57f61e944f84d9835ce05ea", + "0x91a0a9b3cfd05baf9b7df8e1fb42577ec873f8a46bb69a777a6ac9f702735d6e75e66c9257822c781c47b9f78993a46b", + "0x939fdc380fb527f9a1ddecf9c9460f37e406cd06c59ce988e361404acbfcb6379f2664a078531705dbc0c375d724137b", + "0x8bbbe5d5a0d102b8e0c8a62e7542e13c8c8a6acb88859e78d8e1d01ec0ddff71d429fcb98099e09ff0aa673c8b399dc4", + "0xb67a70e4ef138f48258f7d905af753c962c3cc21b7b8ae8b311a2356c4753f8cd42fdee09ac5ed6de31296ead88c351a", + "0x8d21539e7dca02a271ce7d16431773bbe30e6a03f5aff517132d34cdd215ad0da2f06aa4a2a595be489234b233e0852e", + "0x892ae11513f572cc5dc8b734b716bb38c0876e50e5e942631bb380b754e9114c34b0606740301e29b27d88439fb32071", + "0xa8780dc9faa485f51b6f93a986bc4e15b166986b13d22ec2fefc6b25403b8b81c15cc9ac0025acc09d84932b15afa09b", + "0xb01af013360cd9f2bb9789a2b909c5e010fe6ff179f15997dee1a2ba9ef1ccec19545afdecfcb476f92fcdd482bb2b5a", + "0xb5202e5d5053d3af21375d50ad1ccd92538ef9916d17c60eb55c164767c3c74681886297b6f52e258c98d0304d195d3d", + "0x8f6adbcfbb0734bf3a4609d75cf2e10f74ed855a8b07cf04ac89a73d23b2e3e5cf270a1f2547b3d73e9da033a3c514b0", + "0x8abe529cd31e4cb2bd75fa2a5e45bd92cbe3b281e90ffc7dea01ba0df17c9a3df97a3fde373cce5d25b5814cf1128fed", + "0xb8bbf51187bb3bb124da3870e2dfecb326f25a9383e5cc3323813487457010b9055811669c3da87105050825dc98a743", + "0xa5c83875fe61ebbdd3fd478540d7e5a1ad0f8c790bad0b7dd3a44831e2c376c4fffbc6b988667afa1b67bfaa2dbbb256", + "0xa0606b3062e4beba9031ba2a8e6e90aa5a43ba7321003976e721fd4eedb56486f2c5b10ba7a7f5383272f4022092eacb", + "0xb485cc5e001de6bd1bbc9cd8d777098e426d88275aaa659232f317352e1ddff3478262d06b46a573c45409bc461883e1", + "0x916449580b64a9d8510e2f8c7aee0b467a0e93b11edc3d50725bcbc3ca53c2b8bb231fdc0fc0ed5270bf2df3f64750d9", + "0xb2e687caa9f148c2b20a27a91bada01a88bff47faaf6ed87815db26bb6cdd93672199661654763a6b8b4b2012f59dcca", + "0xb6933f7f9dabc8fb69197571366ac61295160d25881adf2fcc8aaabc9c5ed7cf229a493fd9e2f1c2f84facd1f55fee84", + "0xb01eb8b2cf88c75c3e31807cfc7a4d5cafded88b1974ba0a9d5aaeda95a788030898239e12843eda02873b0cabe30e2b", + "0xa3ca290fa6ce064514a3431b44ecdb390ef500629270202041f23bc2f74038147f338189c497949fb3126bae3a6e3524", + "0x93b0f8d02bd08af74918b1c22131865aa82aba9429dc47f6b51354ba72e33a8b56684b335a44661aa87774931eb85974", + "0x81eebeb9bd92546c37c98e0a5deba012c159f69331a89615cf40c5b95c73dcdbf3ceb46b8620d94ff44fcdad88020c1e", + "0xb350e497932382c453a27bb33d2a9e0dbadf4cd8a858b6b72d1f3a0921afc571371e22b051b97da3bb08694c4ca3a4e8", + "0x8c7052f63ba16f14fa85d885aa857d52f04b3a899a4108493799c90c0410de7549be85bec1f539f1608924668df48e5a", + "0xb397574d1fb43de0faaea67d1d9348d67b712b1adce300d6dc497bca94e0994eef8707c285c5c9ac0a66022655a8420b", + "0xa934661d2168ae1bd95b1143c2e5c19261708aeb795abad8ec87f23dc1b352fa436de997ebb4903d97cb875adb40dc2b", + "0xacf535fa1b77255210e1b8975e0e195624c9e9ffd150286ccd531a276cadc12047a4ded6362977891e145a2bd765e6b9", + "0x8cc32356015d7fd29738dcc13c8008cdbe487755dd87d449ab569c85d0556a1ec520dbce6c3698fc413d470c93cb0c92", + "0x8787c7b3b890e0d3734ac1c196588cacf0a3bde65e2cf42e961e23dbf784eef14c07337d3300ed430f518b03037bd558", + "0x99da90994030cbc2fb8a057350765acac66129a62514bbd3f4ec29d5aab8acdd5f4d69ca83efe7f62b96b36116181e79", + "0xa306424f71e8b58dfa0a0564b2b249f0d02c795c30eee5b0ad276db60423210bba33380fb45dbe2c7fedd6ee83794819", + "0xb207a35d31ce966282348792d53d354bbd29ac1f496f16f3d916e9adbf321dc8a14112ca44965eb67370a42f64ca1850", + "0x89e62e208147a7f57e72290eefccb9d681baa505d615ca33325dfa7b91919214646ca9bdc7749d89c9a2ce78c1b55936", + "0xac2d0ec2b26552335c6c30f56925baa7f68886a0917e41cfbc6358a7c82c1cb1b536246f59638fb2de84b9e66d2e57eb", + "0x8f1487659ecc3b383cebc23a1dc417e5e1808e5c8ae77c7c9d86d5ab705e8041ce5a906a700d1e06921f899f9f0ee615", + "0xa58f1d414f662f4b78b86cae7b0e85dfddae33c15431af47352b6e7168a96c1d307d8b93f9888871fc859f3ed61c6efc", + "0x94f3626a225ac8e38a592b9c894e3b9168f9cf7116d5e43e570368ee6ee4ab76e725a59029006a9b12d5c19ddce8f811", + "0xb5986e2601ad9b3260e691c34f78e1a015c3286fdd55101dcef7921f6cbcc910c79025d5b2b336d2b2f6fd86ee4e041e", + "0xb6e6798ddd0255fbe5cb04a551a32d4c5d21bdfd8444ff2c879afe722af8878d0a3a2fe92d63936f1f63fea2d213febf", + "0x86bea9bfffef8bc11758f93928c9fdfae916703b110c61fa7d8fe65653f8c62c6fecd4ff66a1f1a7f3c5e349492e334c", + "0x9595a4606284569f4b41d88111320840159fd3b446e00ec8afd7ddaa53dd5268db523f011074a092f8e931fc301a8081", + "0x83b540a6bc119bf604a7db5f6c0665c33b41c365c12c72ca4fa7b0724115bbb0ff1ae38532c3356e8bb3ac551285929f", + "0x92c6daf961ca4eb25293e1794cf85cda4333cf1c128207af8a434e7e0b45d365f0f5baaefc4ebd5cd9720c245139c6e2", + "0xb71465f3d7dba67990afc321384a8bb17f6d59243098dbed5abd9a6ffc7a3133b301dd0c6ca3843abbaa51d0953abbed", + "0xb15d93482d2ee5b1fec7921fcc5e218c1f4a9105a554220a4fb1895c7b1d7a41f90bbf8463d195eecf919fcbe8738c51", + "0xa79c98e70931ffd64f4dcf7157fbae601a358261e280fe607eb70cef7d87f03efa44cf6ba0f17fbb283a9c8a437d2fdb", + "0x9019d51a6873331f8fe04cb45e728a0c8724a93d904522a9915c748360ddf5cdbf426a47b24abf2005295ed2a676cbf0", + "0xb34cc339fec9a903a0c92ce265e64626029497762ff4dcaaf9bb3994298400ce80f4fb7dbe9ec55fe0c4a522c495cb69", + "0x8fda9be7abfe3b2033cad31661432300e2905aef45a6f9a884e97729224887a6ec13368075df88bd75c11d05247bef15", + "0x9417d120e70d6d5ca4b9369cba255805b5083c84d62dc8afec1a716ead1f874c71a98ad102dac4224467178fe3228f62", + "0xa0a06b64867eebb70d3ce8aaa62908a767fb55438a0af3edf9a8249cd115879cde9f7425778b66bb6778cb0afeb44512", + "0xa44309d3e1624b62754a3a4de28b4421f1969870f005ac5dc7e15183fa5b3ad182bcd09cca44924e03fbdb22f92f8cf8", + "0xaea80f1c3a8fc36cfb5c9357d59470915370b2bec05f51f1d0e1d4437657e2303ba2d1ac3f64cf88f2df412dff158160", + "0xb3f1557883d91b24485123d2f3ae0fce65caa533c09345ae6b30d2ac49953acee61c880c57975be7b4f5558d3a081305", + "0xb52cb1e56f0d147cfb58528b29c7a40bab7cfc9365f2409df7299bfc92614269ff9de3cb2500bbc4909f6a56cf4b9984", + "0xaa4f8fd0f5f87c177ee7242f7da76d352db161846cd31523a2100c069d9e4464170eec0bffc6d4da4f9e87017b415dbd", + "0xb5b61f52242985c718461a34504f82495d73cbb4bc51f9554b7fe9799491f26826d773656225f52a1531cd5bd6103cde", + "0xad12ba9697804ede96001181c048f95b24ba60761c93fb41f4b4a27e0f361e6b1434e9b61391bacaf0705fdaa4a3a90e", + "0x9319286cbda236f19192ae9eb8177e5a57a195c261082ba1385b20328fb83ed438f29d263dddae2f5278c09548830c4a", + "0x88b01ee88c3a7ae2c9f80317dddbaa2b7b0c3a3c23828f03ff196e244500410c9ac81c2e2d3e1f609d4b36ee1732738c", + "0x8e31f30600a9d629488d44a008c821c3c57f13734eaee5a19f0182a2de9e538fff7d982980d7fcc725c969f29f7c2572", + "0xb215740eea98b4bb14197a803a8975700ad2f25a25ef3628eae10166d56c823301f6dd62ce3f9ebf2d42d1f33d535004", + "0x8fb0fdb253d4bcc6693642779be13a5b816189532763dfd7da868cfacfdb87cb5ebe53b18b69dfd721f8d4baf3c1d22d", + "0x8cdd050a447f431ff792156d10381aaf83c6634a94b614dd5b428274538a9cc1f830073533b4fd0a734d6dd4f8d9c4ce", + "0x81b01ee8c72ac668ad9dd19ead2d69cac28c3525e613e036e87aa455c2da9651cc8fcc97c451a8c8a071a4eb69623cd1", + "0x8d9e02dc9ac83f861b3745bd69216232144c47cb468a7dbc49083ed961f978e34265b3f42c400339120bdc4644fe5711", + "0x89e9410455b34cba9db0a5ea738e150fae54dd000d61e614f3274a6c8102ba7cd05b0936f484a85711ad9da7946f51ea", + "0x91f9d4949678f8e6f4b8499899818bdd0f510da552b5d79d8e09bf3b69d706ab36524b5e86d3251318899b9223debf6b", + "0x8b3c38eec7e1926a4be5e6863038c2d38ab41057bcfa20f2b494e9a0c13bc74c3a44c653402eb62a98e934928d0ebccb", + "0xa5cfe465bfbf6e8bfbd19d5e2da2fc434bd71acd651371087450c041aa55e3c4f822361e113c6c3d58646ed3ba89d6da", + "0x918665b8810bcb8d573ca88b02a02c62eaa5a4a689efb5c564b0c9183f78144e75d91fd1603e17d2c77586cbe5932954", + "0x997dace0b739aeb52ba786faae5bdf1d48630a90321f9ceebfa9e86d189a3d79d7b04e459ac8e4adcfe83a5ce964eb1c", + "0xa5a1ca9f0ccc88017a616d481d912aab3f0e154b673f1131c5d9c9c3f5f147d25b6392b2c31e49f7bb7eb2697d05dbec", + "0xa76e99bec509eff01bf6767a06ac97ebc6671cb58bc3d4acc2803580a874885453dbba2e1bba26e45f8d2bda5f688860", + "0x956c1362c8123c5d9ebff7049e851235d69fa645f211ef98e2b6564f2871114a12224e0ec676738d77d23c709dd28a6c", + "0x885efede83b1a3e96417e9f2858ab0c7a576fc420e8f1f26cabf3b1abeec36bcaa63e535da177847f5e0afdb211bf347", + "0xaffca2257f292a2db52f8b1bab350093f16f27ef17e724728eeaec324e2513cd576f6d2e003cc1c6e881334cb2e8bf22", + "0x8dac963d34dcc9d479207a586715e938c232612107bb2d0af534d8da57ad678555d7c1887fadca6551c4f736ffa61739", + "0xb55e600a6bbde81f5a0384f17679d3facb93a7c62ca50c81a1d520cf6e8008ac0160e9763cb2ca6f2e65d93ca458783b", + "0x9485e6c5ab2ebfb51498017e3823547b6ab297d818521ceac85cd6c3aa2d85ae075a0a264ae748fc76ce96a601462ffa", + "0xb4d8abca786c0db304a6634fba9b2a40d055c737ed0f933e1739354befdae138dae3c8620a44138f50ebeaf13b91929f", + "0x8bde7ca39c7bda95b1677a206b16c3a752db76869ea23c4b445c2ff320f2ee01f7358d67a514982ee3d1fb92b7bd7229", + "0x8f8cd0acc689b6403ee401383e36cae5db2ff36fc2311bbadf8ebb6c31cbcc2ca4ffac4c049da5ba387761ef5ec93b02", + "0xa06f42d5f69a566ff959139c707355bbf7aa033c08d853dce43f74a9933e6d7b90e72010ef3fcb3d12e25852343d1d31", + "0xb10ece7cf6b69a76dba453b41049db0cdf13d116cf09c625312b150ee7437abd71d921eda872403d7d7ce7af1e6dccb7", + "0xa3d820318e0f3b54fba7a4567912a82d6e6adf22b67cfc39784683a8e75f77538e793d9708aae228fa48a71abb596195", + "0x8758fad55b68a260bea3bd113e078fd58d64a92f7935ff877f9f77d8adc0994b27040cfc850126c7777cfdfb2428a3e5", + "0xb504913ee96c10f00b848cd417c555a24bc549bf5c7306140eff0af2ada8cb5e76bed1adb188e494332b210fbf24e781", + "0xa00e019a40acc7aab84c1cc27c69920ad7205c2a3dc9e908a7ef59383695c9cb7093c4bcbc2945aab2655119552e3810", + "0xb1000b4c4f306672e39d634e5e2026886a99930d81b8670a5d4046db9621e44997c4b78f583374a09c60995f18a6fd4f", + "0xa6c5053c4e748540ad2b622c28896c9d4ca3978ca4784ac8f09da5314a245f5cdc5d6203c84e6e0bcb3081829720a56d", + "0x8e37e67a70205a5c7da95de94ac4d0ebd287c1c9922d60c18eec1705030dfcbf74ae179e377c008bf5a8bc29c7c07cce", + "0xa66bd7c0243319b553d5cb7013f17e3504216e8b51ba4f0947b008c53bcb6b4979286b614a4a828ee40d58b5ef83e527", + "0x97e2110b0fb485508a2d82ecc2ce1fbe9e12e188f06c7ef2ac81caeeb3aca2c00e5e6c031243b5ca870a9692e1c4e69b", + "0x8734ce8bbc862e12bea5f18d8a8d941d7b16a56ef714792fed912ca9c087497e69b6481fdf14efe1f9d1af0a77dac9b1", + "0xb441dddac94a6a6ae967e0e8d7ab9a52eb9525fb7039e42665e33d697e9a39c7dcef19c28932fb3736e5651d56944756", + "0x918b8997f2d99a3a6150d738daed2ff9eb1f5ed4a1c432d18eab4a898297f7ffbffd1e4ae9037acf589b1cd9e1185ef6", + "0xa0247b8ac4d708cf6b398dc2d5c127a291d98e8bef5f195f820c4fddb490574ba4f62647c2d725237a3e4856eec73af0", + "0xb45636e7e0a823c2a32e8529bb06fcccfd88e9964f61201ee116279223ed77458811d1b23bcb6b70508d16d4570a7afb", + "0xa99c1188fa22b30b04fda180d2733586ea6ef414618f1f766d240c71f66b453900d3645541c019361027aebe0a0f305f", + "0xb4c2f758e27fe233f7e590e8e0c6de88441164da3fcd5211a228318d3066dfdafc1d40246dd194f2b597f6fe9600b3d7", + "0x972530819445b11374c3043d7855d5f1d3c4922b3b205d0bf40162c51605375dd0b61f49cd7f3d39a533a86a13005989", + "0x992b533a13e5d790259bfdfdf1074f84a5e5a0a0d7be9cd6568cdc1662524f1a6666a46da36cea3792ba6707850f4d86", + "0x9875d130457e04dc6ea2607309bfbb900ad3cb5f3e0574f808d27b20cbf6f88389d87dca19998680c5bc30d1df30a41b", + "0xadea8494a69e83221edf360ab847272b5c47eba5404665fb743d98c0682732c30085ae3ec82bc1e8e4aba8454c9b1849", + "0x887d4c624ce05e224216c5f6fa13c5741012ac33330bc291754782f0bfe668decdc98c0e43a1ce28323effe6b639f477", + "0xab6b167aeb5e93ab155990b94895e7e7ff6dea91384854a42cc8a3b9983495b4b3c33ab1b60b2b6450ccf0418fada158", + "0xa7588d0b7c6a6bc32fc474aa0f4e51dfb8e6e010346ad32c59d6f99e6f0522424111a03a4f56ba4075da8009ee7a63e9", + "0x94d645cc3936db1563568193639badfc064dd5bda8d0631804ee00b09e141b200619e07506b5a8225130541436327194", + "0x8d695c03cc51530bdc01ee8afcd424e1460d2c009e1d7765c335368e5c563cf01a2373c32a36400c10e2bf23c185ed19", + "0xad824a0a7ed5528e1f9992cbb2050785e092b1ea73edd7fb92b174849794a5b04059e276f2941e945bc0f3e46172f2af", + "0xad6ed2af077a495d84f8eeed7d340b75c0d1c8b7c5a854dfc63ab40a3d0c2b0d45016d30b3373a13f0caae549f657976", + "0x82454126c666023c5028599a24be76d8776d49951dfe403ebf9a5739b8eb2480c6934a34010d32cd384c91c62a9aa251", + "0xb57070006793eca9fa2f5237453ed853994ad22c21deb9b835e1fb3fbc5ac73aec265a4a08de7afae1610dc8c42b7745", + "0xad94667c791cf58875eb77eb17b6ad02de44e4ba2ddc2efe4d0ff22a5e1a090c670354437847349fd61edc4ba5606f07", + "0xb2aac0c345ffc00badaab331c12a22019617b004d32c099c78fa406d683744d96d51d1237ad0842f9f54655186f8f95b", + "0x8fed51076cc939b354e3b69034a594e6c9c98425ccf546154ab087a195375128444732388d2eb28f82877de971ec2f58", + "0x8e521c0093deb9dff37888893db8ffebc139984e7701e68b94d053c544c1be0d85f0f98d84b2657933647b17e10a474c", + "0xa2c6c9a307aff9b1dea85f90fa9e3b8057fd854835055edeb73842a7ef7c5ae63d97c51fec19dd8f15d696a18a0424a6", + "0xa3390b25a9c11344ed1e8a0de44c848313026067a0f289481673c2c0e7883a8fc9f6cab6ccd9129729a6d8d0a2498dc2", + "0x82770c42b1c67bbd8698c7fe84dd38cc5f2ad69a898097a33b5d7c5638928eb1520df2cb29853d1fa86a0f1bcc1187e8", + "0xa6fdf7a4af67bc4708b1d589135df81607332a410741f6e1cc87b92362a4d7a1a791b191e145be915aa2d8531ee7a150", + "0xaecac69574188afc5b6394f48ba39607fe5bb2aa1bd606bc0848128a3630d7d27101eb2cea1fb3e6f9380353a1bb2acc", + "0xa23fd0c52c95d0dffb7c17ec45b79bf48ed3f760a3a035626f00b6fe151af2e8b83561d0b9f042eaae99fde4cbd0788d", + "0xa5f98068525cdd9b9af60e0353beb3ac5ac61e6d3bac1322e55c94b3d29909d414f7f3a3f897d5ae61f86226219215c6", + "0xb2a4d724faac0adf0637c303ff493a1d269b2cdbec5f514c027d2d81af0d740de04fb40c07344e224908f81f5e303c61", + "0xadeadb3521e1f32ef7def50512854b5d99552e540ec0a58ea8e601008de377538c44e593e99060af76f6126d40477641", + "0xa18b7fc2fcd78404fed664272e0fef08766a3e2bc2a46301451df158bd6c1c8aa8cf674dd4d5b3dedfaceb9dd8a68ae3", + "0x83bcfb49313d6db08b58c6827486224115ceef01ca96c620e105f06954298e301399cdd657a5ff6df0b0c696feec1a08", + "0x8c94391eba496e53428ec76dfe5fa38f773c55c0f34a567823316522a0664a3d92bff38ec21cf62ac061d7d1030650c5", + "0xb1fa196ccfd7d5f1535b2e1c002b5cde01165c444757c606b9848bc5f11b7960973038fb7cc3da24300fc1848e34c9af", + "0xb139f6c6449449638de220c9d294e53fc09865a171756d63bbf28ec7916bf554f587c24bddf51dd44372d15260d8fe25", + "0xb716242299d4ee72b5b218781b38ca5e005dcf52333364f85130615d1dbf56216af8ee2c9c652d82f7aab5345356538c", + "0x9909f24e4ad561aa31afd3a3b9456b2bd13a1d2e21e809a66af62fec5f95b504507ac50e81d2233da2b223f5443e7585", + "0xae863530a02cf3a757f72b945c8c0725d9f634d2ff26233478d1883595ff9a1eef69e8babffdbfa161452fc204f5b5a1", + "0x8eb82bde283b6a6e692b30236cbf41433b03eda8dad121282772edd56f144b1ebf5fb489d18c6ce8776135771cbb91e2", + "0x9296141fadf8dadc885fff4999c36efa25ec76c5637a8300a1a7dc9cf55bcedfe159e0ef33f90eee9be8c4f085734e10", + "0xb6c07f2e6fcbd6c42a8b51e52fbcd5df3aa9f7c3f0b3c31021de1aec2111d0a1c36b5ab489ba126af44fd43cf31c2594", + "0xa70ca669c357535b363d16b240fd9cb9c5ba1b648510afc21218ea034e9bf5f22717ae31ff43ef89dded95b7132fa58f", + "0xb350721f8f6b4d164fd08aca30cd4dece9b4a81aed0ac12119c9399bab691d5945814306f9a61f0106b76d4d96f7b9d6", + "0xb6886076c9d8c344bf3fb6975173d00fa82866012894f31c17e6fc784fbc0dd2d24d6a1cddd17f7379c74566a23219aa", + "0x87636e4a83ceadc170a4b2517b19525c98e2163900401996b7a995b2f3da8d6ba2ab92f909eade65074fac07cf42f6fa", + "0x8ff61d87c4699a067a54b8540e8642f4c7be09d3783ec18318bcba903c6714fcd61be69165e07e1ca561fe98e07507de", + "0x85485d6b569ac20e6b81a9e97ef724e038f4fee482f0c294c755c7b6dad91293814f143bfcfc157f6cfa50b77b677f37", + "0xa49256cb1970cc1011a7aed489128f9b6981f228c68d53b1214d28fbcfb921386cc7cf5059027e667a18073efa525a74", + "0x87bc710444b0c3e6682d19307bedc99c22952af76e2d851465ee4f60e5e1146a69f9e0f0314f38a18342e04ece8e3ed3", + "0xa671a6cabfd19121a421fdfe7732eccbb5105dfb68e8cbcf2b44ae8465c99e78c31b99730beca5bc47db6fc2f167203a", + "0xa2f3270c184629f6dfc5bf4bdd6e1b8a41e8840a1e4b152253c35c3d9e7ab4b8e3516dc999c31f567e246243e4a92141", + "0xb9795a5a44f3f68a2460be69ecacdbb4664991ebbedffed5c95952147ad739e2874c099029412b9653d980a2d4307462", + "0x959053faec9a966dd5a4a767a3154e4b8e4f56ca540ae53e373c565dda99fb626f725e5a5e3721c82918f8c5f2e9e0a3", + "0xb3ef9d6a1b3cd44a3e5112819fa91cb8a7becc3f5b164c6f759f93171d568497b01c8e743f4727b341a1296a0dbadf4f", + "0xb852dfdfbe2b8c77d938fad45f00737e14eacf71d5fecbb3e4f60052ec9efb502c38c1fcecaf71da69eabe8b33852a67", + "0x921c7007f26bdd4139e919dfe27d87b489a0bc5bd6fb341e949e4451f14c74add0489b108c9c9666a54c5455ac914a9f", + "0x86b63d73ba31c02e5337f4138e1684eccdc45ab5e4f30e952fb37d638b54ecec11010414d7a4b7aa91f7cc658f638845", + "0x853c55e0720b66708a648933407795571fc11ad5c234e97f92faabce9e592983dfb97a1705047ee803648ecf9fbb2e5c", + "0x995fe7d1dc09bb0c3c3f9557c4146534778f5ea9c1d731c57440fdcf8094f82debf19090b5d23298da1ed71c283b3ae5", + "0xb9c49c911a0c4d716b7baec130f9e615bfa7d504aa8766ed38878a93c22b1f6353503d4f7f425d4902239fb4689429df", + "0x80504d964246789a09dcd5c0298680afb6fe50bca3bb9c43d088f044df2424a1828de10e0dbdc5c0aac114fa6d9cf5d1", + "0x90249351f109f6b23a49a610aaa3b2032189fd50e5e87cdc3b20f23ed4998af3a8b292bf9fbab9bd1cbe0a1371081878", + "0xabb5f0148850f0d80b429c2b9e0038772432340ef0862ccb5dcb7347026ca95bf9a5857f538e295aebd3a6a5027adb4c", + "0xb92ac9c0f7e73150798348265e5f01f3c752480c72613c6894a95e9330bba1c642b21b9cbd8988442b5975476634b4fa", + "0xaf3fbcc825abd92c6d7ea259467f27045e288f27a505e6a3c9ec864aa08fcaca0d4123034513dbd4c82d4814075708ab", + "0xa738232a66030e0e9c78e093a92fcc545b10e62fb0ecb832bbbc71471b28eb6ec422a498c2402e2c6d74983df801e947", + "0xae60194ce2035edd1af253b9eefbb4b1b7609c9678256c89c3cb076c332a9f4442c3441ad2ecc9d73265359bdadc926c", + "0x8b2fd55e686f16725fc0addb4065f696275852320b03221fd22889825d66fae5bb986b03c47452e32b3a32c1fdfc8dfd", + "0x8e2e1a36673b7729b07e7bc5014584e1c03e9552f7440fbfda0a6a7f41953947fcdf8d666f843bfc03dcca5b06a14318", + "0x95a3df04368c069f3fd32a20b627c5f043e952167c9e80bf5914bbf2086879909c60e089bbd488725ab977c0e6051728", + "0x9856403b2211d0152d4eec10db7ec34c16ac35170714b75af3ebc398a676c171b24b6f370361de0f9057ba444293db14", + "0xa2cb484b758af5fd8e2baca7f0406f849c71255e58ef110d685cd0c1137893a25d85a4d8582e3ced7dd14287faa95476", + "0xb0f697b6a42f37916b90ab91994ae4a92c96dc71e4da527af41b9d510bc2db5a9b4f29183a758074b6437a1e62b2d1d7", + "0xb39c49266aae46f257b7ae57322972fb1483125298f9f04c30910a70fe5629dba0ec86b94cc6ba16df3537a55e06f189", + "0x86cd5595b5b769dfd9ceb68b11b451f6c5b2e7a9f6f6958eac8037db1c616e8a9defb68a0d6c2287494d1f18076072c1", + "0xb462e8fa9a372d4c1888fd20708c3bed1cb00c17f7d91a0481238d6584fbbf2d238e25931154f78a17296a12825d7053", + "0xa5ef28286628ba509bac34c9f13158d0013239fdca96b5165161f90b89d6e46295822ebdf63f22d7739911363a0e0e86", + "0xa629a95a24e2545862b41a97ecba61b1efa792fd5555dc0599c175947e9501bffc82b05a605fd5aabc06969ccf14fff4", + "0xaf83467e4b1f23a641630cc00c38d4225ff2b4277612b204d88de12a07d9de52fb4d54a2375a7fd91eb768623c255376", + "0xa630f29fb2e9a9e2096d7f3b2f6814ee046ebc515f6911d4bc54ad8a5a821a41511ff9dcfbe3176f35c444338ecd0288", + "0x950dedc11bd29e01ba9744bec681ad9462127c35e9fcadfacc9405ec86b985a1b1c4f9ac374c0f1fa248212e5e170503", + "0x82e8e7be8011ee0fd9c682d26a0ef992d0191e621d07fd46a3a5640ef93a42e1b98a33cad1f8017341a671d28caebb03", + "0xa075860554e712398dac2fb0375067a48d0e4ca655195cefc5ccb1feb8900d77124aa52a12e4f54f7dab2a8f1c905b5b", + "0x81d2183d868f08714046128df0525653a2dc2ff9e2c3b17900139c9e315b9f4f796e0fb9d1d8cbadbaa439931c0e0879", + "0x81fb1456969579515a75fb66560f873302088cde2edc67659b99a29172165482ca1f563758c750f00086b362ae405322", + "0xa13c15ab19203c89208c6af48d2734bb0873b70edb660d1d5953141f44db9012528d48fb05aa91d16638cbda2ca8f0cc", + "0x8ba46eef93e4ec8d7818124a0b9fcfe2bcf84a98db3545d2b3d0192cfadc81fc667dcc22ab833c3e71508d0f3c621fe4", + "0xb9bd60d2266a7d01e1665631a6ed6d80ffc0cd7f088f115a5d4ea785c518a8f97d955e2115b13c4960302b9825526c92", + "0xb26fa4e87142150250876083a70c229249099331410f0e09096077fdf97b31b88dc57a3e3568d2a66a39af161cf5dfec", + "0xb9d147564124728b813d8660ba15fa030c924f0e381ad51d4e0cf11cc92537c512499d3c2983dd15f2e24ca166070d70", + "0xb6fb44e1a111efb3890306fa911fafda88324335da07f7de729b2239921ef15b481630a89c80e228bec7ab6444a0b719", + "0xa6cd9c7acac052909ef0cf848b6012375486b59b7bac55b42c41f0255b332c1d45a801f6212d735be8341053bd5070b9", + "0x864258d69234786af5de874c02856fc64df51eff16d43bfb351b410402ab28f66895aec4025e370a4864f19ff30fd683", + "0x84370fa1243b64b3669dd62e1e041ff9bd62810752603486aac3cba69978bd5f525c93cbc5f120d6f2af24db31ec3638", + "0xb983c2cdc1a310446de71a7380b916f9866d16837855b7d4a3a6c56c54dab3e373a6fc6563b8309dc3b984d4e09275d6", + "0x914f8587f876470f7812fa11c6f67e2dd38bf3090e8928e91fe2fe5595bee96cbe5f93d26fdced6b4e7b94f75662b35d", + "0x8b47bcb111d91aa3d80e4ceef283824aa00d1faeb6fe4111aecd9819869c0e1f6f4b6fb2018aebb07a0f997412cda031", + "0x95b2befa98f9992450ca7ff715ae4da8c36dd8adcfef3f0097de6e3a0b68674b05cbf98734f9665051bb4562692641e0", + "0x8bcd1651a2bfce390873a958e5ff9ca62aac5edd1b2fd0f414d6bcf2f4cf5fa828e9004a9d0629621b5e80fbbd5edb90", + "0xaf79bed3c4d63239ac050e4fa1516c8ad990e2f3d5cb0930fc9d3ce36c81c1426e6b9fe26ac6a416d148bf5025d29f8b", + "0x881257e86b7ab5af385c567fde5badf67a8e7fff9b7521931b3ce3bac60485c0fe7497339194fb7d40e1fad727c5c558", + "0xa1b40b63482cd5109990dfb5a1f1084b114696cbbf444bf3b4200ab78c51dad62c84731879ea9d5d8d1220e297d6e78a", + "0xb472212baa2a31480791828ca5538c3dcc92e23f561b0412f8cc9e58839d1625ddcaf09c8078d31ac93470436843cd74", + "0x8f516d252b1863cd3608d852a2857052bb2a3570066d4332fa61cb684b10ac8d1a31c8d32f2a0d1c77eee2ad7a49643d", + "0x8d20b75c51daa56117eda2fd5d7a80a62226074b6a3ff201519f2054eecfeff0aa2b2f34b63bea3f53d7d0ce5c036db9", + "0x8282f433229e7948a286ba7f4a25deb0e0a3c5da8870562c3646757bef90ca1e8d3390b0a25b3f2bf45bf259a4569b77", + "0x8a2dbf4b55cc74f0a085d143a88ebc8c2a75a08eab2703d13a00b747eaddc259a3dd57f7330be938131835a6da9a6a68", + "0xaa0bc51617a938ea6a7b0570e98b8a80862dd9e1cf87e572b51b2a973e027bcd444ef08e0d7b5dee642e0da894435e91", + "0xaa7319ca1ac4fe3cc7835e255419eeb7d5b2d9680769cc0ca11283e6147295db75713b71a9312418a8f5505cd45b783d", + "0xab3f9c465663dc90fae327a2ee9cb7b55361a9b6fbe713540a7edd3cff1c716802fb8ad4dd8fb0c945d96b3b44c5795b", + "0x913a2ae88acffab12541fc08920ee13ab949f985a117efe9a5b2c76f69f327f60c5b5ad3fa5afa748034ac14298fc45a", + "0x9008f044183d2237b723b235953e4d8b47bec6a7b300d98075555478da173b599ba9c7c547c2f111ce1fae5ac646e7a3", + "0xa26b4cc42b353e1c18222d2e088d7f705c36be12e01179db440f10fcfa9691d31fc4fc7e7ee47876f1624e6d44be1021", + "0x995e75824f322294336bfa2c5d1a319f0d77f6a0709beabaf1b43015d8a78d62447eab907349524734170f0294d1ca7a", + "0x8b96f04a19dbe4edc71d1f2c6d3475ae77962e070ec5797752453283c027c6b29b6e58e8b7eb5c3f9770557be7e80b67", + "0x8621459865234734bcfaa492ca1b89899525198a7916ccc6f078fb24c8bf01154815bb5b12e1c3d0a10bd4f1e2ea2338", + "0xab52174541185b72650212e10a0fe2e18ccfd4b266a81233706e6988c4af751b89af87de0989875f7b5107d8d34c6108", + "0x966819d637bdd36db686be5a85065071cf17e1b2c53b0e59594897afc29354ecba73bf5fc6fa8d332959607f8c0a9c27", + "0xb7411209b5ab50b3292c3a30e16f50d46351b67b716b0efb7853f75dc4e59ec530a48c121b0b5410854cd830f6c4b3ea", + "0xa5dc04adbadce0af5dc1d6096bad47081110d4233c1bf59a5c48a8e8422858620f4be89bf1f770681be2f4684ee4cce7", + "0xaf77a8f83cffb5f8d17be0ab628dedcad63226c9b13ce4975fb047f44bfef7d85e7179aa485abb581624913eddbb27ec", + "0x82bf28dc58c893c93712ce297cc0d64f70acb73a641cb4954ccf9bf17597f6d85eecf5a77c8984ab9afbe588562a0ee9", + "0x988a7cef9a178e8edb91f3ec12f878fd68af2ac0762fa0a48a2423e24f765ed8f7837429fd8bc0e547e82e6894e63008", + "0xa5d5969311056d84b3ee87f49286fac0bd9a7220c196cea4f9dced3b858dcdba74718eab95b38bd5d38d2d1184679c98", + "0xaf4d51b3ded0aaad8f12bef66c0616e9398fc42618852ac958e6ab2984a720a6111ac55b249d7e4523051740e12b346f", + "0xac635b4a49f6fbb94a5f663660f28431ba9f7c5c18c36ebc84fd51e16077de7753595f64619b10c16510ecbc94c2052d", + "0xae25eb349735ced1fe8952c023a9b186a1f628a7ddf1a4b6f682354a88f98987ac35b80b33189b016182f3428a276936", + "0xae3ab269690fdd94134403691ba4f5ed291c837c1f5fdc56b63b44e716526e18abb54f68ca5d880e2fb7bea38e74c287", + "0xa748b03b2bd3fbc862572bc4ddc0579fa268ee7089bcfd0d07d0c5776afcd721302dbb67cb94128e0b1b25c75f28e09a", + "0x8f09a2aaa9ba3dfe7271f06648aba9cc1ea149e500a7902d94bb9c941a4b01d1bb80226fd0fd2a59ad72c4f85a2a95d0", + "0x853d55ad8446fd7034e67d79e55d73a0afcb5e473ed290e1c3c7aa5497e7f6e9bbf12d513fc29e394a3dc84158a6d630", + "0xb1610417fb404336354f384d0bf9e0eb085073005d236a0b25c515d28235cea5733d6fbd0ac0483d23d4960064306745", + "0x86de805b3d4f6fbb75233b2cf4d22fcc589faa2ac9688b26730cb5f487a3c6800c09bb041b2c6ab0807bfd61b255d4c9", + "0x893b38c72cf2566282ee558d8928588dca01def9ba665fcb9a8d0164ee00dedafbf9d7c6c13bcc6b823294b2e8a6a32c", + "0x8e50de7a70ac9a25b0b5cf4abc188d88141605e60ce16d74a17913a2aff3862dec8fbbf7c242cf956f0caae5bcc4c6bf", + "0xb5cf09886a4fb4ce9ea07d1601d648f9f9d1a435b5e1e216826c75197cd6dafd6b2b07d0425a4397a38d859a13fdb6dc", + "0x859dc05daf98e7f778a7e96591cc344159c1cbe1a7d017d77111db95b491da0a9272866d2638a731923ca559b2345ebe", + "0x8ff1792f77ecdfbd9962f791a89521561c7b82031a4e53725f32fe7d99634a97b43af04cbf3e0b0fdff4afa84c49eb99", + "0x81e2cd8a221b68ae46dd7ce97563bd58767dc4ce1192b50ff385423de92206ff585107865c693c707e9d4ed05f3149fb", + "0x8fce7da7574e915def0d1a3780aa47ef79b6d13c474192bd1f510539359494ddc07e5412f9aac4fc6c8725ade4529173", + "0xac02f5df60242734f5ead3b8a62f712fefdb33f434f019868a0b8ddf286770244e2ddfb35e04e5243ba1e42bcd98a6a5", + "0xa8d69783349a442c4a21ecb3abd478a63e2c24312cb2d2b3e10ea37829eb2226a9b8d05a8c9b56db79ffaa10d1f582d1", + "0xb25b5cca48bf01535aba6d435f0d999282845d07ac168f2ca7d5dba56ee556b37eab9221abdb1809767b2de7c01866c1", + "0x8af7e1d1f4df21857d84e5767c3abe9a04de3256652b882672b056a3ab9528e404a8597b1ad87b6644243f8c4cd3799f", + "0xa6718308dfa6992ae84fcb5361e172dbbb24a1258a6bd108fd7fc78f44cc1d91be36e423204a219a259be4ab030f27ff", + "0xb99cbe3552c1a5259e354c008b58767c53451932162e92231b1bebfc6a962eb97535966a9bd1dfd39010dfcda622d62a", + "0xa8458f6b8b259581f894e4b5ce04d865f80c5a900736ca5b7c303c64eaf11fe9cb75e094eece0424ba871b2aee9f7a46", + "0x914f763e646107b513c88f899335d0c93688ffa6e56c3d76bff6c7d35cb35a09f70dc9f2fe31673a364119c67cd21939", + "0x9210f2d39e04374f39b7650debe4aceeb21508f6110ab6fc0ab105ec7b99b825e65753d4d40f35fad283eeff22a63db0", + "0x98729cf927a4222c643b2aa45b3957b418bce3f20715dd9d07997a3c66daa48dd62355dbd95a73be9f1d1516d1910964", + "0xa602c399f1217264325b82e5467a67afed333651c9f97230baf86aec0dd4edeae1e973cafef2ea2236d6d5b26719954d", + "0xac9632921d45900bf3be122c229ba20b105b84d0f0cba208ccdce867d3e9addfb3ef6ece9955950d41e1b98e9191ef42", + "0xa76ce1f53e1dc82245679077cb3bca622558f2269f2d1a1d76b053896eba1c3fc29d6c94d67523beb38a47998b8c0aa7", + "0xb22b51fcc1b328caa67cc97fb4966cb27d0916488a43248309c745cd6e2225f55ad8736d049250fa0d647e5f8daa713c", + "0xb7645c1923a6243fa652494fe9033fa0da2d32a0fb3ab7fcb40a97d784282a1ffad3646c499961d4b16dadbc3cbb6fd6", + "0xacab12b490da690db77c4efdc8b2fe6c97ac4ba5afb5165d6647fdd743b4edbad4e78d939fc512bebcf73019c73bae40", + "0xad7a0fcd4e4ccb937a20e46232a6938fccf66c48a858cf14c8e3035d63db9d1486e68a6bf113227406087b94a0ece6a0", + "0xa78605beaa50c7db7f81ab5d77a8e64180feea00347c059b15dc44c7274f542dc4c6c3a9c3760240df5f196d40f3e78b", + "0x8763315981c8efa9b8ae531b5b21cfc1bbc3da3d6de8628a11dcc79dee8706bd8309f9524ec84915f234e685dd744b69", + "0xb4a6c48531190219bf11be8336ec32593b58ff8c789ee0b1024414179814df20402c94f5bfd3157f40eb50e4ef30c520", + "0x8dac8a3f152f608ce07b44aee9f0ed6030fa993fd902e3d12f5ac70bf19f9cde2168777d2683952a00b4b3027d7b45ea", + "0x8baf7dfae8a5840c5d94eabfe8960265f6287bb8bc9d0794a6d142266667a48bec99b11d91120907592950a0dddc97d9", + "0xb8595e6ea6b8734d8ae02118da161d3d8d47298d43128a47e13557976032dad8c2ccbfff7080261c741d84d973f65961", + "0x8b93979c51c8d49f4f3825826a5b9121c4351e0241b60434a3c94f2c84f0b46bbf8245f4d03068676166d0280cf4f90c", + "0xaceb0fdaf20bf3be6daebf53719604d3ab865807cc2523285f8fef6f3fc4f86f92a83ad65da39de5bd3d73718a9c4bd2", + "0x814dd41764a7d0f1a14a9c92e585f154a26c8dbf2f9bff7c63ae47f1ac588cec94f601ccc12e8a63a7a7fce75a4287f2", + "0xb47b711848e54fa5c73efc079d0a51a095fa6f176e1e4047e4dac4a1c609e72099df905958421aee0460a645cba14006", + "0xaaf7bd7e1282e9449c0bc3a61a4fca3e8e1f55b1c65b29e9c642bb30a8381ce6451f60c5e0403abc8cee91c121fa001f", + "0xb8b0e16e93b47f7828826e550f68e71a578a567055c83e031033c1b7f854e7fc8359662a32cc5f857b6de4aff49e8828", + "0xb3eb70b8c8743a64e1657be22a0d5aeb093070f85a5795f0c4cb35dc555958b857c6c6b7727f45bf5bedf6e6dc079f40", + "0xae68987acd1666f9d5fa8b51a6d760a7fb9f85bf9413a6c80e5a4837eb8e3651a12e4d1c5105bfb5cfa0d134d0d9cfc2", + "0xacd8fa5742b0bac8bd2e68c037b9a940f62284ff74c717f0db0c033bf8637e4f50774a25eb57f17b2db46e5a05e1d13d", + "0xa98dac386e7b00397f623f5f4b6c742c48ab3c75d619f3eaf87b1a0692baf7cb7deac13f61e7035423e339c5f9ae8abf", + "0x99169bd4d1b4c72852245ebfbc08f18a68fb5bcce6208dd6d78b512b0bc7461f5caf70472b8babf3e6be2b0276e12296", + "0x937d908967f12bf7f728fe7287988c9b3f06c1006d7cd082e079d9820d67080736910bc7e0e458df5bae77adb9a7cbc1", + "0x8c50e90ce67c6b297fd9406c8f9174058c29e861597a0f4ed2126d854a5632fa408dfa62ad9bb8b6b9b6b67b895d5a4d", + "0x8f4840a91b0a198226631a28e7a2e893fc6fed4d5eb3cb87b585aac7f4e780855a353631ad56731803296f931e68a8d0", + "0x96a4b8c64d3d29765e877345383bf0e59f4ac08798ac79dd530acd7f3e693256f85823ad3130fb373d21a546fe3ca883", + "0xb0dce7a6ab5e6e98b362442d6e365f8063ba9fef4b2461809b756b5da6f310839ac19b01d3fd96e6d6b178db4ff90ee1", + "0x8f012cb2be5f7cb842b1ffc5b9137cafef4bd807188c1791936248570138f59f646230a1876f45b38a396cbdd3d02e08", + "0x94a87b5ce36253491739ca5325e84d84aaff9556d83dcb718e93f3ff5d1eecf9ae09d0800a20b9e5c54a95dfebfcecd3", + "0xb993ec5f9e82cc9ceeb7c5755d768bc68af92cc84f109dfaf9cf5feb3aa54881e43c3f598ba74ed98e8d6163377440ca", + "0x92f845d4d06a5b27d16aef942f1e3bcbe479b10fef313f9ca995315983090511701b39ccbb86b62d0c7c90a2d1f0c071", + "0xb6ec6da0f9e7881e57fa3385f712e77f798abc523609a5b23e017bb05acc6898825541aed7fe2416c4873de129feceea", + "0x86b181183655badfe222161d4adf92a59371624a358d0ec10e72ee9fa439d8418f03d635435ec431161b79fd3fa0d611", + "0xb5e28eeed55fb5318b06a0f63dbf23e00128d3b70358f1c6549fd21c08ef34cb1372bc0d4b0906cc18005a2f4cd349bf", + "0x85c4d3fddda61dbfb802214aa0f7fc68e81230fb6a99f312848df76cddc7b6dfd02860e8a4feb085dad1c92d9c6c65e0", + "0x80f7fdec119309b2ac575562854f6c2918f80fc51346de4523ec32154d278f95364fdef6f93c7d3537a298dd88df7be6", + "0x9192c1949d058614c25f99d4db48f97d64e265a15254aa6ed429e1ef61d46aa12355769f1909a5545cd925d455a57dbe", + "0xa0b1e7d928efc4dcbd79db45df026ae59c20c1a4538d650c0415ab7cb0657bc1e9daeacc3053ee547e8f9c01bdbd59c4", + "0x893e84c41d3a56bca35652983c53c906143b9ad8d37b7c57f9dacbeb7b8dd34defc6a841f5b9857ffb90062bbd8e9dee", + "0xa7f89a448349dbc79854cf888980327f92aedc383c7fadd34fdc0eaa4f63d751315b4f979e14f188854ef4d16c9e8107", + "0x833f2774a96187805f8d6b139c22e7476bce93bc5507344d345008080fb01b36d702b96e4c045617a23a8ca1770b4901", + "0x80e46e86d68bd0a48ac6fa0b376d5bb93a5d6b14f08b3a47efa02bb604c8828c2047695f1f88fc5080e5548e1a37130f", + "0x943f42b7b4ad930059a26ad06b62e639f06c1c425d66066c55134e97c49abe412358c7cb994fcc1cf517ea296bca1f68", + "0x8b9d4fe835dc6a2cbf85738937bbfb03f0119ab8df04a7d68860716ce6ee757dbe388a1e8854ddb69fe0c9fa7ed51822", + "0x909030c7fde2591f9ea41ae6b8fa6095e6e1a14180dda478e23f9c1a87b42c082a1ea5489c98702f6ccd2ba5812d1133", + "0xa715ec1beb421b41c5155c7ef065bbb50b691d0fa76d7df7ee47683d9e4eb69b9ea3e62fc65196a405d6e5e29e6c2c60", + "0x8c9e801cb7ef780a535be5c2a59b03e56912acbfdb00447bfa22e8fc4b11dceecc528f848d5fba0eec4237d6f81f4c79", + "0xb96b6af857c3bc0344082bd08ec49a9bed478d4d35b85a2099b1849cd6997521c42225305f414cdd82aef94b9e1007d3", + "0x8764db720b4e44a4d2527f7f9b535a494a46c60e28eac06bf1569d0703c4284aefa6cb81fbba9d967286f9202d4b59ea", + "0xa66fd2f9158e1ffcdd576cba1413081f43eed00c7eb8f5919226f7b423f34ac783c1c06247819b238de150eb5a48d977", + "0x82c52e817ac3bb0833ea055dec58c276c86ca5181811cf7a483b3703a06ea1bee90ae3aeaa2cffeaeba0b15fe5bf99be", + "0x987d07cb276f7f03a492cfb82bba6d841981518286402d3e69e730a9a0e29689a3619298124030da494e2a91974e0258", + "0xb34f2c5740236bc6d4ae940920c5bc2d89ff62a3dd3a3ec9a0d904d812b16f483073db1e53b07f2b62e23f381d7bdbe5", + "0xa1c0679331ab779501516681b3db9eefb7e3c0affb689e33326306ada6d7115fafd2cc8c1c57b2fa6c2072552f90a86e", + "0x94805e30d7852fc746e0c105f36961cc62648e438e8b9182fc0140dbf566ec14a37ad6e7f61cacb82596fc82aed321e5", + "0xa42fb00b29a760141ff0faaeb7aca50b44e7bbc0a3f00e9fb8842da7abfcaae6fae9450abe6ba11e8ecf11d449cbe792", + "0x8fb36ce4cfa6187bfe8080ac86b0fa4994f20575fb853bd8ffa57c696179cc39f58ff3b4bd5a2542ff1c8b09015539df", + "0xa1c54e7aa64df7fb85ce26521ecfc319563b687ffecd7ca9b9da594bbef03f2d39f51f6aaff9a3b5872d59388c0511c6", + "0x855e48fdb8f771d4e824dbedff79f372fd2d9b71aa3c3ecf39e25bf935e2d6e0429934817d7356429d26bf5fd9f3dd79", + "0x8ae6157a8026352a564de5ee76b9abb292ae598694d0ea16c60f9379e3bb9838ce7fd21def755f331482dc1c880f2306", + "0xa78de754e826989de56fe4f52047b3ffd683c6ceaf3e569a7926f51f0a4c4203354f7b5cfa10c4880ba2a034d55a9b0d", + "0x97609477d0a1af746455bbd8cb2216adacc42f22bfd21f0d6124588cd4fec0c74d5bde2cdba04cdbfbff4ac6041b61b1", + "0xa03dc3173417381eb427a4949c2dbfa0835ef6032e038bf4f99297acf4f0ba34a5fc8ccf7e11f95d701f24ee45b70e27", + "0xaad6283e85cd1b873aeb8b5a3759b43343fdadc9c814a5bf2e8cf3137d686b3270f1ec2fb20d155bbfd38c7091f82c44", + "0x92ab94ed989203a283d9c190f84479c2b683615438d37018e9c8de29c2610bb8fccd97bb935dca000d97d91f11a98d65", + "0x8c0444a0b9feb3acb65a53014742e764fa07105e1c1db016aec84f7a3011d9adc168dbba8034da8d0d5db177a244d655", + "0x95a33d25e682f6c542d4e81716cc1c57ef19938409df38bf8f434bc03193b07cedd4e0563414ce00ab1eebbd3256f3e7", + "0x8716c30e3e4b3778f25c021946c6fb5813db765fde55e7e9083a8985c7c815e1b3d3b74925ba108d9a733ddf93b056af", + "0xa186aabc10f1fff820376fa4cc254211c850c23a224f967d602324daec041bbe0996bf359ed26806a8c18e13633a18a8", + "0xa1e8489f3db6487c81be0c93bade31e4d56ec89d1a1b00c7df847f5cd7b878671015f5eaa42ee02165087991936660b9", + "0x8f688c969c1304dfa6c1a370119d1988604026a2ab8e059016c5d33393d149aae6e56f3ee2b5d25edc20d4c6c9666ad9", + "0x91950b651fefd13d2fa383fd0fdc022138ce064ee3b0911157768ad67ed1fb862257c06211cf429fba0865e0b1d06fc8", + "0x86cff4080870d3e94ed5c51226a64d0e30266641272666c2348671a02049ae2e8530f5fb1c866c89b28740a9110e8478", + "0x88732c4d9e165d4bb40fb5f98c6d17744a91ff72ca344bc0623d4b215849a420f23338d571a03dd3e973877228334111", + "0xafcc476ad92f09cf2ac7297c5f2eb24d27896d7648ba3e78e1f538c353ceeb1e569917a2447f03f3d4d7735b92687ba5", + "0xb622aa475e70d9b47b56f8f5026e2304d207684726fb470a0f36da7cb17c30dd952813fab6c7eb9c14579aacca76f391", + "0x802cf5630c0407ae0d3c5cf3bef84e223e9eb81e7c697ea10ec12e029fc4697ce7385b5efab7014976dacc4eb834a841", + "0xa08596493f4cd1b8ac2ec8604496ee66aa77f79454bb8ab6fdf84208dc7607b81406c31845d386f6ac8326a9a90e7fc5", + "0xa54652ca9e6b7515cb16e5e60e9eabbccbc40bb52423d56f0532d0bac068aec659a16103342971f2cc68178f29f695db", + "0xa3ab54875cb4914c3a75b35d47855df50694310c49eb567f12bbc5fa56296e11f4930162700e85ba2dbfdd94c7339f91", + "0x94183a040285259c8f56bef0f03975a75d4def33222cc7f615f0463798f01b1c25756502385020750ac20ae247f649a1", + "0xb0004261cc47b0dc0b554b7c6ebf7adf3a5ece004f06e6db3bbac880634cdf100523b952256a796998a5c25359f12665", + "0xa25dfeb0e18ebe0eb47339190f6a16f8e116509ab2eef4920f0d3ff354e3ead5abe7f5050b2f74f00b0885ea75b4b590", + "0xab10ef2f5dc0ede54e20fa8b0bce4439543db8d8b31e7f8600f926b87ec5b8eea0ac2153685c7585e062ffac9e8633c3", + "0x8386eac1d34d033df85690807251e47d0eaacb5fe219df410ab492e9004e8adabb91de7c3e162de5388f30e03336d922", + "0xb6f44245a7d0cb6b1e1a68f5003a9461c3d950c60b2c802e904bc4bc976d79e051900168b17c5ac70a0aed531e442964", + "0xad12f06af4aa5030b506e6c6f3244f79f139f48aec9fc9e89bbfbd839674cfd5b74cea5b118fb8434ba035bda20180af", + "0x88511306dfe1e480a17dba764de9b11b9126b99f340ceb17598b1c1f1e5acbdd1932301806fe7e7e5e9aa487a35e85de", + "0xa17cdf656e1492e73321134a7678296a144c9c88c9a413932d1e4ca0983e63afc9cdc20fd34b5c6a545436b4db50f699", + "0xb555b11598a76de00df0f83f0a6b8c866c5b07f7ac2325f64fb4a0c2db5b84e0e094d747186c3c698ee4d0af259dc4c7", + "0x88014560587365e1138d5b95c2a69bdae5d64eb475838fee387b7eb4c41d8c11925c4402b33d6360f0da257907aa2650", + "0xb220634e6adee56e250e211e0339701b09bf1ea21cd68a6bd6ee79b37750da4efe9402001ba0b5f5cbbfcb6a29b20b0c", + "0xac5970adc08bc9acec46121b168af1b3f4697fb38a2f90a0fbc53416a2030da4c7e5864321225526662d26f162559230", + "0x97667115b459b270e6e0f42475f5bce4f143188efc886e0e0977fb6a31aba831a8e8149f39bc8f61848e19bcd60ceb52", + "0xb6c456b36c40a0914417dd7395da9ed608b1d09e228c4f0880719549367f6398116bf215db67efe2813aa2d8122048f2", + "0xab7aef0d6cda6b4e5b82d554bd8416a566d38ded953ffd61ef1fcca92df96cdcc75b99a266205ff84180ab1c3de852a4", + "0x81d354c70ce31174888c94e6cf28b426e7d5c4f324dc005cd3b13e22d3080f3881d883ca009800f21b0bb32fa323a0cf", + "0x94f3440965f12bee4916fcc46723135b56773adba612f5ce5400f58e4d4c21435e70518bdef4f81e595fa89e76d08fc6", + "0xa6683e7a1147f87cbeeb5601184cc10f81bca4c3c257fd7b796a2786c83381e7698fb5d1898eb5b5457571619e89e7d6", + "0x8ca29539600f8040793b3e25d28808127f7dc20c191827a26b830fff284739fb3fc111453ff7333d63bce334653a0875", + "0x98a69644048b63e92670e3e460f9587cf545a05882eb5cba0bcbd2d080636a0a48147048a26743509ab3729484b3cc12", + "0x84d40302889c03c3578c93aca9d09a1b072aadd51873a19ef4a371ca4427267615050c320165abece7f37c13a73d4857", + "0x87954271e3de3f0b061c6469d038108aac36f148c3c97aefb24bf1d3563f342ea6c1c1c44c703e1587a801708a5e03f8", + "0x86b6f5367e04c5caa3ec95fd5678c0df650371edac68f8719910adf1c3b9df902cc709a2bddc4b6dde334568ca8f98ac", + "0xa95fed2895a035811a5fee66ca796fdecce1157481dd422f8427033ed50c559692908d05f39cb6bea5b17f78a924633c", + "0x8ba05bdadde08a6592a506ea438dbdc3211b97ea853d1ad995681a1065ececce80f954552b1685ef8def4d2d6a72e279", + "0x90b6b7494687923e9c5eb350e4b4b2e2fa362764d9a9d2ebb60ee2ad15b761e0850c9a293123cf2ef74d087693e41015", + "0x8819ea00c5ea7b960eb96ab56a18c10a41fd77e150ab6c409258bc7f88a8d718d053e8f6cb5879825b86563e8740808d", + "0x91e42031d866a6c7b4fd336a2ae25da28f8bde7ace6ff15dc509708b693327884e270d889fff725e6403914546055c28", + "0x85763642052f21cf1d8bd15fd2dc0c2b91bba076751e4c4f7a31fbdb28787b4c6a74d434d6ef58b10f3ad5cde53ef56d", + "0x8b61c36c7342a1967a1e7b4c01cddf4dce0e2025bc4a4a827c64994825f53e45277550ceb73c34bb277323fb784aa3c6", + "0x80b9634a45c8b3770e993257bd14df6a17709243d5429969ab8b9a4645bf2a94f9b3cd3d759169887b4aa0eb50f4f78c", + "0xb5c44db9439dd8aa4edd151d95e48a25c1154e1525c337f97109f40131db81a4898344c8c3144c270bdc835c269b3477", + "0x863080fcbc227eea32d0dc844f42dc642fbda7efc398ab698be3a3c6f3bf8803dea6ba2b51fed6151f9522b4ab2a8722", + "0x8481e871129e9cb9d2d109c513cbba264053e75192e967f89659dcfcc1499de9ae7a1ac4f88f02289150231c70b4da01", + "0x834d8183698d3d2d1352c22c373222cb78d0f4c8cb15e0ad82073dde273b613515ebcd184aa020f48f8e6fc18f3e223c", + "0xa227e300f0c5bc1b8d9138411413d56c274cc014ae8747ec9713f3314d5fae48bb6f8cc896f232fd066182af12c924e4", + "0xab7242835e91ba273de1c21eb4fca8312bdda5b63b080888b96a67a819b50294a7f17a7dc0cd87fae5e7f34bc24c209a", + "0x86eb27c898a5d6c3618c3b8927acee195d45fe3f27b0991903520a26fb8021b279e2a8015fbbba5352223ae906c7c5d6", + "0xa61b1c200b0af25da8ad8e29f78d000a98683d1508ae92ee7f4326a7c88e0edb645b6cb5dde393ac74d322895e77ba24", + "0x887739318c710aae457b9fe709debff63bfbb3ffbbb48a582c758b45d6bf47a7d563f954b1f085c3bc633ffd68c93902", + "0xaacfcb0e2b0a868b1c41680487dc6600577ce00aa2edeee8c6141f4dc407217ddb4d37b79e7c9182258c750d12a91508", + "0xad8cd2cf5ccd350cd675a17f31b86a0e47499c6c4c11df640a5391bb10989c9c70df0a3ddeba9c89c51e15fedaf67644", + "0x8aba897d32c7ef615c4dfa9498436529c91c488a83efc07ba9600875c90c08b00f66a51469eb901451b6e18e7f38ffd7", + "0xaab8a600609b80e36b4a6772308bac77929a0c5d8d92bbc38e9999186a1c2bfdbef4f7a2b1efba9c17a68dc15a9373ab", + "0xb95811d1454307a30c2ac8588c8104804b06c1aec783fed75a6f12c9df626be57865850100f1ad28073e3867aca941cf", + "0x8b119d3bd4ee644469457df5d8a0020fd99b8b20bd65ab121cf95a7f55e50dd8945fcf1dff9d269d9d0b74b4edbc7726", + "0xa980b912df832ea09353fd755aa3eec9eb4cfd07ca04387f02a27feab26efa036fca54cc290bb0c04a8a42fdfd94ce2f", + "0x91288e84da1d4ee2a4dad2df712544da3a098fdb06a5470c981fb6d6f3dcc1c141b6f426d6196ff3df6f551287179820", + "0x98b0473bcffcbd478fd1b49895c61dd2311dab3cdec84f8e3402f8add126c439ffcb09cae3b7f8523754090d8487b5a9", + "0xabe76988cf3065801f62a1eb3cfe9f8185bd6ab6f126c1b4b4fde497ca9118d02a0db3fadccd4ca98826b30475fa67ef", + "0x94a316a0faa177273574e9e31989576a43e9feb4cc0f67aa14d5c1967c4e10fc99db3ef4fdca2e63800a0b75f4b84256", + "0x975ad39adadc7e69e34981be2e5dc379b325dc24dddacc0bb22311ff4a551a0020a8bdecf8ab8ac5830ca651b7b630ce", + "0x8b3bc73b640dc80ac828541b723a968fb1b51a70fa05872b5db2c2f9b16242c5fe2e8d1d01a1dbeaac67262e0088b7b0", + "0xaa8d892a6c23dbc028aae82c1534acb430a1e7891b2a9337cedb913ff286da5715209cffb4a11008eae2578f072836cb", + "0x8dee9747a3ae8ed43ce47d3b4db24905c651663e0f70e2d6d2ddb84841272848a1106c1aa6ba7800c5a9693c8ac2804e", + "0x81e2c651b8b448f7b2319173ecdc35005c2180a1263e685a7e3a8af05e27d57ec96d1b2af2cae4e16f6382b9f6ec917c", + "0x98a9a47635de61462943f4a9098747a9cf6a9072a6d71903d2173d17c073eff3fc59b2db4168515be31e6867210ecbcd", + "0x912b2398505c45b0bb4a749c3f690b1553b76f580b57007f82f7f6cce4fadd290d6df9048258978c8a95ef9c751a59a2", + "0x8ac8f0893fe642111ef98ae4e7b6378313a12041bbca52141e94d23152f78c2e4747ae50521fc9c5874f5eb06976e5cf", + "0x946b4a8eb05b529aaed56ac05e7abeb307b186a7835623fa4e85ed9eb41a4910663c09ea1bd932a2c467d28776b67811", + "0xa4be51abeddd40e1da6fdb395d1c741244988ff30e10705417b508574b32dce14d08b464486114709339549794df9254", + "0xb33b6b7d66cb013e7afeabbd7ed1e0734eb0364afe4f0f4c3093938eec15f808985fb7f3976969bf059fa95f4d8e335b", + "0xa808adbcf0049f394914482483ee0f711d9a865615ff39b5313ed997f7a0d202ad9ed6e6de5be8a5c1aaafe61df84bca", + "0x8856268be15a78465ad00b495162dc14f28d4ef4dcf2b5cba4f383472363716f66dabc961a6dbdda396e900551411e41", + "0xb16ba931e570e1bf124ea3bd3bdf79aed8aa556697ea333e6a7d3f11d41538f98dcde893d0d9ba7050442f1515fb83b1", + "0x91ecde1864c1a9c950fd28fa4c160958246b6f0aa9dda2a442f7222641433f1592d38763c77d3f036a3dbb535b8c6d8f", + "0x92cda991f69fbf8e55c6bf281b07fff5dbbb79d1222b8c55686480669247b60212aac27aa7cccd12fcee94e7a759b8af", + "0xb1d9b5b4e996b375d505d7250a54c12d32372c004a9cabf1497899054cb8b5584b1cef1105f87b6e97603ccbf2035260", + "0x86e98bde8b484fb809b100f150199f13a70c80813ad8b673bf38e291595e2e362ad1fa6470d07d6fbe2cf7aeac08effc", + "0xaa12f7c39ba0597a8b15405057822e083aca3cee6ed30c4e0861eeb22620823588d96c97bb1c3776b711041c4dc3d85d", + "0xb477b34f29334f3bae69c7781d574342b7c27326088f9a622559ab93075c7357953ae84eb40e3421f453e04e9b4d5877", + "0x9625067cb2120ce8220a469900aa1d1bb10db8fe1609988786b07eb2b88e0ddb35a3eccd4b6741e1fa2365c0db6b1134", + "0x997b92af7765f587d70ea9718e78a05498cd523fc675ad7b0e54a4aae75fbeac55d0c8d72471471439dacd5bfcfae78d", + "0x88b59eaea802e6a2cf0c0075bf3fd6891515adcb9adf480b793f87f1e38d2188c5ed61ac83d16268182771237047ec8a", + "0xa57d078b230b1532c706a81eaabfef190fb3eb2932f4764631e916a0f0d837d6014da84ede100abaf610519b01054976", + "0x94ed5c5b96f6afa9f2d5e57e1c847ae711839126ab6efb4b0cf10c4564ff63c819d206fdc706178eb6a0301df2434c01", + "0x980296511019c86cc32212bad6e4a77bc5668b82a2321a1ecabc759a8bbc516183a4787c7f75f9ee7f1338691dc426cc", + "0xb10ef97db257343474601fd95f9016c205e28bd22bf7b8f9e30c3b14aca1cc9a11e6404ff412ef269c55fb101fee5a37", + "0xb670d5d9c77fc6aa14212dd3eae100620f3510031b11a9625aa40bf31835c9fd717753b555bd159b1aa64a2104929340", + "0x862054fabf6d6d529a7584d1a48f72d2eb216caf959c782ec36c69c26aef4595415d19a28b041946811b34a629105241", + "0xae4bf2ccd7b0f3774653848b5b4d39e5517dcbcff30d8441d78bc387ff42b573f16b7b0a7366e6ca5cef1dd9f0816df9", + "0x8f810527badcb49f1542a0ccd12e3541efa084243f7106eae003458c176f4c1f01daae9d4a073c2cb2aced747e8a4576", + "0x8a32c2067aaf6baf32db67acd4974a22a6da33db5444028a7c8c4135f9c84e102dc3b2c635b15afa6dc907d0270daffb", + "0xb15fc057f306a60b20c8487125b6b334ab749cf70eb8a30c962f625bb203ebd0d2a315949ee3b7a99e3d91acec384806", + "0xa37f145d321359b21cba7be8b64dfae7c67a20b7b324f27c9db172d58e77a49fa02ed3d06d09d7644bf1fd81f4aab44b", + "0xb338d2e39a485ee4297adcf5e58e16c3cc331c5dffeade0be190907c1c5bdfed38537a6d81dc39a2cdfc1bc45e677886", + "0xb69d84d8511b3aedfdc7c7e66f68b24e12e5a2365dbbe014bddd2e99e54143428cf8b74cf12c0e71316038aa5300e87e", + "0xab210cc38661667450561a1857337879633f5d5bf2c434a3df74ff67f5c3ba69a7880872f19ae4dcbbb426462cd7d0fb", + "0x94538ef487a58a5ff93a5e9616494c5f066715d02be5b249d881a00bd0edfe2fe19dd7a5daa27f043d1dbb5ac69cf58d", + "0xafb47a899c1b25fe800241635fa05de9687a69722802ad45434f551971df91d8ca9edda0d835d82eb8f36ff9378ed7e8", + "0x827a10d7536230887283a9b1dedccc6b95ef89cb883c4ee7b9821058b0f559704d1636670c0ada2b253bf60b7cb8a820", + "0x97cc07965065d64409f19fb2c833b89ca3a249694b16b58818a6f49d3800926627ce0f87e5c0853ae868b4699cfdee5e", + "0xae0c93d44780ef48ea537cf4cb8713fd49227f4b233bc074e339d754b5953e637a7289c6f965162701e4b64e4eaec26d", + "0x80953053397c4c0ba9b8e434707f183f9ced2a4c00d5c83b7dc204e247ad7febc1855daeb906c53abfdf3fe3caca30c4", + "0x80f017e87b471b5216ebe25d807be6c027614572337f59f0b19d2d1f3125537478cb58e148f3f29b94985eac526cd92f", + "0x8a8e1c0d49801a8dd97e9e7c6955fc8b2c163a63bd6a4be90bb13e7809bb0dddc7a5025cc7d289a165d24048eac4e496", + "0x8530e5b5c551a2e513d04e046672902c29e3bb3436b54869c6dea21bab872d84c4b90465de25dff58669c87c4c7d2292", + "0xae3589d389766b94428e9bde35e937ed11aac7ead3ce1b8efe4916c9bfff231d83b7e904fe203884825b41022988897a", + "0xac02e629a900438350dd0df7134dfa33e3624169a5386ea7411177b40aa7a638e8d8aef8a528535efdbe1ca549911c0b", + "0xb1ac60b7270e789422c3871db0fa6c52946d709087b3b82e6eba0d54f478520b1dc366bb8b7f00ff4cf76e065c4146eb", + "0xa7465e1f8e57de1a087144d3c735fee2b8213fcbf2b9e987bb33c2d4f811de237bf007402e8d7f895563e88b864f7933", + "0x8ab0007ba8984dee8695ec831d3c07524c5d253e04ec074f4d9f8bd36e076b7160eb150d33d15de5dd6e6fb94f709006", + "0x9605bbe98dadd29504ce13078c1891eca955f08f366e681d8b5c691eadb74d6b1f2620220b823f90ef72eb4ab7098e16", + "0x942a083d07c9cb7f415fedef01e86af4019b14ef72d8ab39fe6bd474f61ba444b9aac7776bea7e975724adb737e6337a", + "0xb9a49a8c4e210022d013b42363ac3609f90ea94b111af014f2c5754fbc2270f6846fa6a8deb81b1513bb8a5d442ea8dc", + "0x99cd62b177d5d7ce922e980cc891b4f0a5a8fa5b96dfc3204673fbef2e7fb2d7553bbacd7b2e6eca4efb5e9a86096e2e", + "0x94e30b65b3edd7472111566dde7fab0e39a17e1f462686050f7134c7d3897e977550faf00174748cbeaec6c9c928baa8", + "0xa32fbcb29f3391d62092f2720e92b6ef4d687d8a3eae39395e0464669a64a38fe21a887f60bc9519d831b9efde27f0f4", + "0x8f1492c4890d8f9deecb4adada35656e078754dcf40b81291e7ef9666d11ba3747a478f9420a17409d7d242cecd2808f", + "0x8942960b319ef65812d74cb1d08a492334db58d41e8437e83ddf32e387d9f3ad36834f59e6a71d1afb31263773c3ec49", + "0x88d692f4976c99e763b027df9c2d95744d224724041dfbe35afc78b1f12626db60b9d0056b3673af3a1741eaf5f61b43", + "0x9920cd37eab256108249a34d3f1cc487829cc5f16d1bce3a2328fe48b4de735ebde56c8b5cf4e532a4d68792387257c5", + "0x87d34c9f5a913b806504a458c843eda9f00ff02ad982142543aa85551208cab36ebf8b3409f1c566a09a60001891a921", + "0xa2ee8339c96f790b3cf86435860219322428b03ea7909784f750fe222bc99128d1da2670ad0b1f45e71a6856c7744e09", + "0x84bd257f755de6e729cc3798777c8e688da0251a2c66d7ba2e0ce5470414db607f94572f5559f55648373ce70e0b560e", + "0x8d0e170714ddf5dde98b670846307ab7346d623f7e504874bfd19fbf2a96c85e91351ba198d09caa63489552b708fbc8", + "0x9484cc95d64f5a913ed15d380c2301a74da3d489b8689f92c03c6109a99f7431feb8a07d9f39905dcef25a8e04bcec9b", + "0xb14685f67dd781f8ef3f20b4370e8a77fef558aa212982f1014f14b1bdd8b375c8a782d1b8c79efc31b41eec5aa10731", + "0xb22fb1541aa7d2b792aa25d335d66e364193fdbf51b24a90677191cae443f0ce40a52faf5983d2cb5f91f0b62a5f20e1", + "0xb06fa9489123ab7209d85e8c34d7122eb0c35c88ee6c4c5e8ae03a5f1ae7c497c859b0d62e0e91f5e549952330aa95a4", + "0xb5cd71617ff848178650e6f54836d83947714d2e074d8954cfb361d9a01e578e8537d4a42eb345031e3566c294813f73", + "0x848d39ea2975d5de89125a5cbe421496d32414032c1e2fbc96af33501d3062745b94e27dfe1798acaf9626eabff66c79", + "0xad35955efd5a7b6d06b15d8738c32067ffa7dd21cf24afc8ea4772e11b79b657af706ce58a7adcc3947e026768d9cdaf", + "0xaff6d7c4861ff06da7cb9252e3bd447309ad553b2f529200df304953f76b712ac8b24925cf4d80a80b1adaa2396f259a", + "0xb4b88d35e03b7404fc14880b029c188feecb4d712057f7ba9dedb77a25d4023e5a2eb29c408fde2c0329718bdaf1ff63", + "0x88e96720e2f7c63236cca923e017ca665b867ba363bc72e653830caf585d802fad485199055b5dba94a4af2c3130a6f6", + "0x982675dc0299aeedba4b122b9b5f523ca06d54dc35da0f21b24f7c56c07f4280265fb64cec2f130993521272c3470504", + "0x95c77d418490e7e28293169cf7a491a7dcc138362f444c65b75d245c1b986d67c9e979a43c6bd8634dae3052df975124", + "0x8fd6c4dff54fb2edc0bdd44ccd1f18238c145859ccd40fbfbc1cf485264445b9d55ffd4089c31a9c7a0543cc411a0398", + "0xb153eb30af9807b5fe05d99735c97471d369c8a1af06b2e2f0b903b991eb787ab5a88c6e406e86225582acf8186ad5ef", + "0x826b55de54496751b0134583b35c0c2049b38de82821177e893feeeeb76ceeb747c7a18312cb79a6fc52f2c18f62f33e", + "0x91650d7205b232c495f1386bea0c36e136a22b645ffd4f5207f5870b9ce329c44524781c983adf2769f4c05b28a8f385", + "0xb8d51a39162ebb38625e341caacc030913f7971f178b3eee62dc96f979495a94763ea52152198919c6dd4733bc234f64", + "0xa1fbd3673f2ae18a61e402fe3129b7506d9142f2baca78f461579a99183c596b17d65821f00d797519e9d3c44884d8a6", + "0xb7c5f5407263398cf0ed3f0cf3e6fcebdd05c4b8fd4656a152cedcdbf9204315f265fd8a34a2206131585fad978a0d6c", + "0x94fa71804e90f0e530a3f2853164bc90929af242e8703671aa33d2baad57928f5336e67c9efdcbd92c5e32a220b4df07", + "0xb75dcea5ad5e3ed9d49062713c158ebc244c2e4455e7a930239998b16836b737dd632a00664fded275abe4f40a286952", + "0xa02f7b37fc30874898618bfcc5b8ff8d85ef19f455f2120c36f4014549d68a60a0473ddfd294530dfd47f87fbd5e992d", + "0x8b48e1626917b8ba70c945fe0d92d65cab0609f0a1371fd6614d262d49fe037f96991c697904d02031ec47aab4b32f48", + "0xb368f02c21d4af59c4d11027e583ca03ef727f2b2b7918ef623f529ceac76753a05a4ce724ce2e018da6ecc5c1c1261b", + "0xa95cba06eeae3b846fc19a36d840cbcf8036c6b0dc8c2a090afcf3434aaf5f51ef5d14b1e9189b1d8f6e4961bf39bbf8", + "0xb32ca4dfbeb1d3114163152361754e97d3300e0647d255c34ec3025d867ed99e36d67ebafe8255b8c29be41864c08edc", + "0x8e4eddefa27d4fe581f331314d203a6a0417c481085134d8376898f9260f133e2bf48576528d62adf29953ad303e63a7", + "0x92b7d5505833f00d5901ae16c87af028de6921c2d1752a4d08a594eb15446756ea905b0036ae6ffe6b8374e85eb49348", + "0xb50e9018d3c4e05ba9b28b74b6634043f622d06aa8123da7cd0bc482b3131912149214d51bdfd887484422e143c3c1c0", + "0xab980a2f5317dfcb92baa4e2b3eb64a9ac2a755da6c11094d57e781ae5cf43e351824f1dd3abb4c6df75065b3784210b", + "0xaaabb009dfcb0bae65a0aee26ed74872c226965c52a6ed0998209e020a9ee806297dba4b15845cf61e1a514de5d125db", + "0xa1fe78f67000ebb6e90fe33e1a9dd5489be6e15fedb93b2a37a961932b77137fe85d46e89a132ecf7bcfb7aa95e16757", + "0x85bc6e7d660180de2803d87b19ed719d3f195ea0a92baf9bfff6113c743f4237f51355b048549913e95be8ddf237864d", + "0x87a167968c4973105710e6d24ad550302ee47fe1f5079d0f9f9d49f829b9f5c1cd65d832d10fe63533e9ad1fa0ad20f5", + "0xb2ad1a7b95b8a89d58e0b05c8b04ae6b21b571d035ae56dc935f673d2813418e21a271cccaf9d03f0d6fa311f512d28c", + "0x8268e555319992d5ac50cb457516bd80c69888d4afa5795fcc693d48a297034f51e79f877487b6f7219cfdd34f373e14", + "0xb235411f1f6d89de3898642f9f110811e82b04ad7e960d1dd66ec7a9bf21de60e00cfabcd3004f3b5c4f89f5d9c7422a", + "0xb6963effcfe883f7ed782a3df3c40edd70f54ceca551859bcccb5d3e28fd2c1fcbdd7acc7af24a104687fd02b53c704d", + "0x862645c944e1e2909b941578cc5071afd7353fed1c2c99517e2de7573037704ef5d35accf6ec79b8269da27564209d50", + "0x90f585eeb1a053e2f18c1280c9d6a561c0bc510b5f43cd68370ed6daac4b3749852b66c371397b6a7c1ece05ee5906c9", + "0x876d9a3686feb79ce781e87ac3e3fbeef747b6ab031285e808c8a73f73f55b44507850dcaa745c0791d2cae8ad61d74e", + "0xa7ecc3b8c10de41a7bd9527228a0d3b695a651a5b5cb552a3664a887077d39ee60e649aecd68ed630da6288d9c3074ad", + "0x83529f1f2b4dc731ea05c1ee602fa2e4c3eebe2f963f3625959ba47657be30716d64e05e8b7e645a98bf71c237d9c189", + "0x834ca6b14428c30a4bc8d5a795596820af6f3606f85bee9f3008f3fb94b3adffa968d21a29e2588d7a473d8b5d3a8b42", + "0xb8d08cd8b73430984fd16e8db0525ae2b76253c92cccd7b3470add4d12d082eafb55a72bde04870924d0bdaf61f76c5d", + "0x96ef32df669690c2391f82136fc720231e4a185c90ba79eef7beaadedf7fbeb56ed264825564bdc7da01829b47f4aa88", + "0x93d637b2f04d71891a80a1ee93fd9c9046d671bc4c15c4e597cfcc36f4ae85a7efc111359628965fd10d36c39129b160", + "0x89f28dd3f7bc43749d0e3750c136385d4ffaf2c40354d3be38341416d755de7886d8108d83721b36f99feb3bccd73c88", + "0xac6392e274659f4c293e5cb19859828f101959c4c0939920a8dfed0e2df24a0cbf89a7aa983e947318c58791c893928e", + "0x83b2d4ce42c2fa0f672cd911365d1f1a3e19f1c38f32bedc82820ad665d83ae5fac4068e4eca6907bd116898966fed92", + "0xb5e0144d6e59a9d178d4ee9f8c5dba18d22747fcdf8dc4d96d4596a6e048e384cd1e211065f34109c9ed6b96010d37e5", + "0xb1a65e6b38c9e84b3937404d5b86c803c2dac2b369a97cbf532cfdd9478ee7972cf42677296ad23a094da748d910bc48", + "0x849d7f012df85c4c881b4d5c5859ab3fb12407b3258799cfc2cb0a48ae07305923d7c984ff168b3e7166698368a0653d", + "0x84d9b7ee22bf4e779c5b1dd5f2d378ef74878899e9dbb475dfdcd30c2d13460f97f71c2e142c4442160b467a84f1c57d", + "0x964e497ef289fac7e67673a6cb0e6f0462cd27fc417479ecb5eb882e83be594977fb0c15a360418886aece1aaf9f4828", + "0xae1226222098a38ce71f88ab72de6ededb2497e30580e7ae63d4829dcc9c093bdd486102b7a7441cb06253cf0df93772", + "0xa72865b66d79009b759022e53b9eedbd647ff4b1aab5d98b188100d01fc6b5d8c02b80eb6f53dc686f1fdda47d4722b8", + "0x93aa8d7d8400bdfa736521133c8485c973d6d989ec0a81db503074fe46957a3999880fd9e4e7f44de92adf6ac0abe99b", + "0xa75e5ab84399962ada1f9ebcfc29f64405a1b17cd0a983950d0595b17f66386393d95a5aa4c6c878408984141625141c", + "0x91b1e5e75f4b55ec2e8f922897537082a1414eedc2bc92608376a626d8752d5d94f22f0e78ea1970eb0e7969874ad203", + "0x83bf9c308424ef4711bfa2324d722f550d95f37d7f7b4de0487ccf952b89d7219ca94e7fa25bee60309efefd9a0e4716", + "0xa42060476c425ff7979456d3c5484bc205fb1ef2d7149554a4d483d48e2a19119f708c263e902943bcf20a47e6c7d605", + "0x8170c45ea126e6367aa5f4a44b27f7489a5dd50202cb7c69f27a2bdf86d22cf6b00613b0080d75fca22439eeaaaa9707", + "0x8e5a82da70617697e42c6b829e1889b550c9d481408fe4cf8dc9d01daccabdec01f9e1b8c27dc84902a615d539bf9bc6", + "0x80606c51401d0bf5f2700ebce694c807ab1f7d668920bdcccef2775e0939472419a8f404567bd4f9355095517eb4d628", + "0xa40314565d60d0ddf8995673e8c643b1baa77a143b3d29433263730a6871032260abc1320e95af8287b90aa316133da0", + "0xa87e07e84435f9e8a51ce155cd3096aa4b20d18e493c9dcbc0ac997ac180f3a255bf68ccd8195f2564d35ec60551a628", + "0x84d2ab98416643c457bf7ddd9f1aa82967ecea189db08f3558f56803fe7001693ed67ec6ca8574c81ec1293b84a7c542", + "0x937c3b955889ceae77f28054ce53d75f33cfe3a04f28e049cea8b8ade2a0440d5e2e8c4f377e6c1ae2115d68cc95fc16", + "0x885a911f16845fe587b15ce7cd18cc2a84295bf609732340f74e0f5275b698cffed3e9aa1440e19e6940a7fa8f24c89c", + "0xad90059a50c399996aaa0a10a8f637b7bab0dd5d9100301f0159a2c816596da55c30b2568d1717705fd2826b117a42d6", + "0x828de9ff1e095c189da1f1ee18009afe14613ac696025add6f4e330488e02d5f1a90be69edd9a17bfb3355a0ca77b525", + "0xb7aedb8394064a58dd802be6457555c0cf7b94805ed00cc66f38449773f4b1865feaee3a6f166eb51b2123b89d853a4d", + "0xb09c564ff37ccea34e90f2d50a40919a94c2e10d4fa58ffeaed656f88f9f4ae712d51c751b1b8f443dc6c9506d442301", + "0xb24882d66b2ebb0271ebb939c72308d81f653940e70d6f1bcaae352f829134aff7f37522cc42de9e7fe6243db2c4806f", + "0x8e6f8dd906e0d4eb8d883f527e926ad1d8156b500c4cfa27214450c8112267c319900de2443c87bed1e4bb4466297dd5", + "0xae42f4578e8d79b6aa2dca422ada767e63553a5ee913ff09cb18918116905b68f365720a1a8c54c62cce4475ba5cdd47", + "0xade639bcd5017ea83ec84689874175ed9835c91f4ec858039948010a50c2b62abc46b9aee66a26bf9387ab78f968b73e", + "0x8d310a57aeb123cc895ee2fd37edc3e36ce12743f1a794ad0e1a46d0f5e4c9a68b3f128719ed003e010f717ec8949f43", + "0x8606c086fcf3e2f92c1b483f7e2a4d034f08aef1a9d5db9e8a598718e544b82544268a0a54dfed65b4d0e6027a901d47", + "0x8ccd95dd673d8cfdfa5554c61bcdbe6bb5b026403a320856fe51571e7c59504fe1c035f2ad87d67827339d84c0e1a0c6", + "0x955a7cb4afcf70f2eb78756fc3a82e85ab4330eb89a87117294809beb197d1d474001e25306e8ad71daab6928abf6d64", + "0xae6b44ec6294736ea853ddeb18fc00cce0ac63b38170ff0416a7825cd9a0450e2f2b340d27a7f2e9c5ac479b4cb8a5fe", + "0xa88ec3f12b7020dd593c54376597b056e70c772c0ec62c24c5bfd258b02f772161b66e5dcd95c0c0fceb23433df9ff23", + "0xb4a83933b4de552dba45eedf3711f32714e58ae41d4dab8a6114daeb06e90a5a5732c70384150d04124ac6936ca9804b", + "0xb8b7c4fa549b0fa1dc9c1f0af0750d6573f1648767751882d41f0dd7e430e3934590757e1c8b436ac35381bdde808117", + "0xab598b911234a98cfde07234cfc0d2fddfc5cb9ea760212aa3e175a787ce012965c8fcfdf52d30347f5f1b79cf4a0f54", + "0xa9d354f9dfbd1976e5921dd80cbb56b2e15df53ce099ecb4368eff416998130d7830209282aaf1d4354129845f47eb80", + "0x8c889afff546c721969e4d8aae6e6716ad7c2e9c1914dd650e30419ee77d630efb54dfffb4ec4ff487687b1864bf5667", + "0x94ed2fa79116c7c8c554dc306b1617834dd3eab58baf8f0d085132c4688ca4a6bd38420281283678b38970a3f02b9a94", + "0x944fdc8f0516d22f1672193d183833d3e3b043e26807fb2123729a0216c299785b1c4e24b5aa56e9bbe74fa54d43e22a", + "0xa48521454a3e0c10a13d8e810fad9d0522c68eea841821a8e0e57811362f7064a8f9c50f79c780a02df7df8c277feaef", + "0x8f3d26670ab55e1bd63144e785203373b2b13b76cac305f0363e48a6339fac3364caa3fceb245527161fc2fac9890912", + "0xb4d6fe71001cb4141f6d8174dd7586d617cfccb54471e1fbce30debc2b1dead62cab29565abb140b682811c6231acb03", + "0x91dc8afc4934fcc53ef851462a055cc1c3c87d7d767e128806891738427606d2fbfa832664d2a7f95f8ffe2cf0c44dc6", + "0xb297eb432c74071764272c1b1663547ba753e66bf026643bfc0e42a9c5cdfb05a88083ad67d6ddfe6ab290678c607b29", + "0xb343d1df85be154faeb5b21741a5ac454ca93f70a0b83a98f5901d1be173a1b2969d43e646363c5d4975924e1912599e", + "0xb2d74a66e4dfc41128aee6a3f0ff1e5137a953ed7a2a0ab5a08d7ea75642f12bd150b965c8f786ad0caf55ef7c26be4f", + "0xa54141faa8dd9a567c3cd507e4fc9057535ffe352fa1e8a311538fe17e4a72df073fbf9371523e5390303db02321650e", + "0x8e229a58f1acc641202d2a7c7e120210b9924e048603b9f785a9787ad4688294140ef3f4508c8c332d2dedafff2485be", + "0x9523554c11d39b56e6a38b3b0fadb7a9a32a73c55e455efdcfda923aff1e9f457d1b7cbc859b5ecbb03094eae8b87d38", + "0xa199ffdff1812aaea10cd21a02b3e7bf3d8e80e501aa20bb2105b5f4cb3d37265abcda4fd4c298d6c555e43fa34517f8", + "0x97f1285229b07f6f9acd84559afef5daad4320de633c9898b8068c6cb3b19b4468b4445607559ddf719f97d2410e2872", + "0xa1dfff82908c90fc38ec7108c484735f104e6ce7f06097e1e80f6545702b6a0bc2a2706203cd85162edb7e9294fdedba", + "0xb12a706311c617d6c19e964e296072afce520c2711086b827cff43a18e26577e103434c0086d9d880c709df53947b48c", + "0x88503a6f48cef2f5cd3efa96a5aacc85dc3712a3b9abbb720a2cff582a6ea3c2afc49288b6832c8599f894950843ac11", + "0x83ed63e38dfbe062fe8c7e6bc2eeb5a116f1cc505c6b038990038de6051281f9062e761ea882906ccff69c9c5b8a4a25", + "0x911090d5d0231dde1189408dca939daddcb69a812ac408d1326060f0220781bcc131c9229e6015540f529d9fb33d9b0a", + "0x8a8352f1d9e5c7e80276e4448f997d420d5a7e0e2d5be58ae4106f47f867d1caa478b2e714d9c3263e93e5cc4c7be08b", + "0x9362f1ea9995f9b3850ebb7c8d5bf95927ab5ea25ee00e85d7456b3bf54459798b1fffde049d445c0d0587b0ab0a1694", + "0x8859502b391273f4a00b6c0e87e5cdae676b7baf6c402f12b3360db6a5dfb4931ece4da0e1e4d98c7a71c3d01a183a9b", + "0xa9a5edf474120f9bbec9485d8b1e6f83be68b10de3d765219b0bf3e5d2840e478f1fb2bf806d78a8b8ad22ec50cf7555", + "0x82c75daf983b06e49f0d75a042dfaae8cc92af050293d9059d6e8b01ca3ab2597e7adfc1159ed805513488944e739fa5", + "0xa5cf240f04a9bfa65b811702c923d209e01f9535e217fa55ae3e0d1eb3257d6749e5587e727091e860609d1df29a1305", + "0x95608ab8ade1c9fb814bad78d9cc99a36ad3e9562d5319830e4611ceea508ef76be04639294be9062f938667e33bce6e", + "0x8e44181f35c38b02133473de15560ae6588ac744cfdaf5cdfc34f30ca8e5ff6c85eb67dddc1c7d764f96ed7717c89f06", + "0x8007b6ddece0646b7e9b694931a6a59e65a5660c723ebdffb036cf3eb4564177725b1e858ed8bc8561220e9352f23166", + "0xa2d9d10fa3879de69c2a5325f31d36e26a7fb789dc3058ee12e6ccdda3394b8b33f6287ba1699fce7989d81f51390465", + "0x81993d0806f877ca59d7ffa97bd9b90c4ebf16455ea44b9fe894323c8de036c5cc64eacf3f53b51461f18fa701a5860d", + "0xa20030f457874d903b2940ec32fa482410efecb8a20e93f7406fc55ab444e6c93fa46561786e40e9bf1e3c7d5d130bc8", + "0x80c72d4985346ac71a231e7bbbb3e4a91bf50142af0927e8eb86069303eb4ce7fca1aa5b919d5efc82f2f09b41949acb", + "0x91b857d2f47f1408494940281127ba4b9ac93525788e700889aa43402eedea002e70eded017f5f5263741ed3ee53a36c", + "0x97445d007f08e285ea7f4d25e34890e955dac97448f87d8baa408e826763c06cbd58dd26416ba038d6c28f55bcea2d3a", + "0xa409c89526c2886f6a6439e2cd477351fc7f886d1a48acc221d628e11895a4eedd426112a368a0dbd02440cd577880a8", + "0xa2c6adc7866535f6ffc29e00be4a20fa301357e1b86dff6df5f8b395ad9fb1cdc981ff3f101a1d66672b9b22bd94ec0f", + "0x8887fc53ffc45e4335778325463b3242190f65ae5d086c294a1dd587f62dd0d6dc57ca0c784bf1acaa5bbba996af201c", + "0x9731d3261a7a0e8c7d2b11886cd7c0b6bb1f5c57816944cc146caa518565034cea250eeee44ddffaeb6e818c6b519f4d", + "0xafe91c706efb9ee9e9c871e46abde63573baa8b2ea2b61e426cd70d25de3cc8b46d94c142749094287a71f4dfadd3507", + "0xae7bdf6ecc4fc0d8d8a7fa7159aae063d035f96ca5a06b6438b6562a4eee2b48d9024dbe0a54cfd075eac39b7a517f2b", + "0xa382e5205bfa21a6259f42e9ebc11406b5da2aad47f7a722212fdd6fef39117dd158a9991ff95e82efa0826625168a1c", + "0x862760c80bf44c2d41c2a9a15c887889eaeea32acc894f92167fb6f72593377c228499f445ccb59794415597f038ac9e", + "0xb4e96595a91a611c4563d09f29a136a4c04f07be74dd71a6bbabc836617ecb95494e48971a8229f980b2189fd108d2e5", + "0xb5e7200357317c36244c2e902de660d3c86774f7da348aca126e2fc2e2ba765fa0facd29eebcb3db3d306260e91a6739", + "0xa64c7133156afee0613701189c37c1362e2b4414f7e99408e66370680c554de67832c30c211c2c678dab5cfcdcecb3f7", + "0x88f4cb67b1db497a91a0823ee3541378133eb98777842d73e43ab99efe8aa52fa02dfb611c1691be23684618394988d6", + "0x89a9382a147d7387d0ff9516ee0c75cd1f8ee23333f4a2c9693d1a8cbe03680bc5b10c43c238c2190db746cac409bf39", + "0xad510bcc067373d40b05a830bf96fac5487de1ad5b708a13f62484c09b00fba6c5b00b981004e5ab3f28e55c9a5bce26", + "0x8384156d7117675547279ad40dc6bf81e8f9a57b2d8cfebeea6b9cd1d8534dc0cf704068bc3ba0815010cd8731d93932", + "0xa818fb76e53165b2f86c7f2317d64cf5e45f48405a34560983cd88bfbd48369e258ce2952233a8ce09c464e07afcade6", + "0xab19a4ed90527e30796064634b66cdc023bc5966e2c282468f5abef7879fc52986d5bb873a796b077d10e7b374b60309", + "0xa17dafe2484d633fe295f8533662631b0bb93cdb4e7cd6115271f20336f602f7f8b073983cd23115093c7f9891c4eef5", + "0x804acbc149d0334c0b505a8b04f99c455a01592a12f64d1ec3b82b2f053ccc4107e47f418f813d6f400940c7c8700a4a", + "0x965e097a825d8511d095b247554ec736bcb3701ead3ba785bd425cbabd56f4b989764e0965a437fa63e7e16efd991fc0", + "0xb6701675ca27d7a4084f06f89bd61a250b4a292ee0521b2a857c88c32b75f2a70b97f98abce563a25d57555b631844e0", + "0xabbdf65fcbdf7d6551ccd8d6e5edc556f1ecd275ccd87ee2bda8ea577c74615f725aa66e0911e76661a77f5278e0c2b9", + "0xab715ae372c900239a0758a3524e42063afc605b8fb72f884dc82ab9b0ff16715f3fb2fd06f20f15f9e454f73a34e668", + "0xb45f41ea1d25a90af80a8a67c45dea881775fed000538a15edc72e64c7aa435a5e4375dcdedc5c652397c02b0bc61b16", + "0x86f7be9252f8ed9078e642c31a70a09639899f7ffcd7faaf1a039fec8f37e1fa318fba0ed1097f54fc55d79900265478", + "0xa30e5ed4277dd94007d58d5a3dc2f8d3e729d14d33a83d23c44ddfc31c6eac3c6fe5eb13b5b4be81b6230cfd13517163", + "0x87e723d916f5fcda13fab337af80354e8efe6b1c09ae5a8ceeb52df45bfca618eb4bec95fefef3404671fb21e80bf9db", + "0xa521b8a04dc3abd3e9e0454b9a395b3638e5394dc2d60e97fda61b0a1880d1d73a64a4633f3d7acbd379bde113240d03", + "0x851686c79c5403d5f05fbaac4959fcbfdfb51151bec55e10481b3c16e3be019e449907ae782ca154f76a805543d5755d", + "0x8ec1929e746b6c62b0c3fdd8f4e255e5c707e6e0d8d57ff9e409ae2dd6e76fdb50af923749992cf92d1b5f2f770bafbc", + "0x9175f7b6820d47205c9e44f8c684833e1e81da46c1fdf918a4dcafbc3231173f68370d442a20e45f8902bcab76a4e259", + "0xb4f66c698115333b5ac00c9fe09aa9e1e9c943fbb4cce09c7d8a6ed4f030e5d97b48e944fd6d3e69ac70f1ae49d35332", + "0xb958878b875eead61a4416a4597b1c567ddbb1eaaa971033f4a656f01a277822c1f4ea3972045156c2a5a28d159f5ddf", + "0x8188de8ad5258024d0280137a40909d24748137ac7c045dddd2bc794eac8edd5850b9d38f568fa8174b2c0593bb57e96", + "0x91152c7bafce7a0358152221081bc065796fa4736bfc7d78076a0a6845287cde2ee2a2c9b96f500297c0a00410634888", + "0xa5328ab939a2d3bd4c21e5f3894c02986b6590ad551c7734be3f4e70380eb7bc19629e9031b886ce3b4074ee4edee63a", + "0x97c4d49db40e266bcedaacb55edca4e1ebf50294679b271f3a2332c841705089b5ba96ef2064040fa56c36bb1375a8d9", + "0x85cf0514f340f9d865b32415710d7451b9d50342dbf2c99a91a502a9691c24cd3403cb20d84809101cd534408ddf74e8", + "0x950c3d167f59f03f803dcba3f34fe841d40adc31e5be7eefff2103d84e77a7cbe4f14bd9c3dfa51cde71feb3468a9c00", + "0x96a69624e29c0fde3b92caf75a63ac0f3921e483f52e398652f27a1ec4e3cc3202f17af1f66224731bc736a25638d3e4", + "0xaeac4170cf4b967227f66212f25edc76157eb4fb44c84190b520ecc2946470c37da505790e225fd1b0682bef7fc12657", + "0xa94146a04e3662c50c2580ae1dba969cbb3fb0f43a038729c9e8be6ed45860b2c7de74f248dfa50ccdbe2ecaf3f2b201", + "0x917b8e2880e85b8db723631c539992ec42536146e7091d4a3f87d37f051b5da934d84393523814f19962c78e6cb12ef8", + "0x931f140ff8f7de79e399f5cd8503558d566b5c2ab41671724dd38aed08dd378210f01ac8fa9911f3047993dbc10cf8c4", + "0x859eb9b560bc36273694f8ae1a70d25e7f206013597c4855a11328162ba1254bb736f1ae41240c8ec8dea8db035e08f2", + "0xb4ad2cb2c3a3e6ab1e174f2dbfb1787a8544f3c9109215aa6d33265ef269455e3cde9858738b4fe04711a9cf9050e7d4", + "0x8a3b342b87b19c0cdb866afff60317e722013c02dee458ba71e7123edc8b5a9f308c533b9074c7dd0d684948467502d1", + "0x89185ac5cc5ea8f10a1f2a3eb968bb5867376d3cff98ef7560b9a0060206c4046ff7001be10b9e4d7ad0836178eba7e4", + "0x845f48301f25868f6d0f55b678eab1f8458e3321137dba02b4cfbb782cbc09f736a7585bf62f485e06a4e205b54a10b7", + "0x931a6c523d4a66b51efadb7eefadba15bf639d52d1df5026d81fd1734e7f8d5b51b3f815f4370b618747e3e8eb19699c", + "0x8eb3a64fa83dcd8dd2258942aea3f11e9cf8207f2fdd7617507c6dae5ea603f9c89f19d1a75d56eaa74305a1284ce047", + "0x912a5050ed6058221d780235fb0233207c546236316176a104a9761bc332323cf03786dbac196d80a9084790506e0a88", + "0x945fe10ec8dc5e51aa6f8ba7dace6f489449810f664484e572bfe30c2fe6b64229f3c8801e2eb1a9cb92ff3c4428cdf7", + "0xb62383bf99c7822efd659e3ef667efee67956c5150aea57e412cbd6cd470807dfaad65c857fada374c82fcfca2516ad1", + "0xa727a31c45b2970d08a37e169ea578c21484dde15cb11f9c94eaaf3736652619ce9d3a44e7431d50b0e75b658ebbc1da", + "0x97bf54ea9b84b82e4616027bd903ef6152439f1c6a8e1bae6db1d10fdf016af2cac10ff539845833dfd1ddad1403aa8c", + "0xa08cf36437e010e59b2057aedb7192e04b16f1cc66382cdef3490b7ad1544ae51f03e87cba0fe43a275841c247a2a0cf", + "0xacafab9fa28c1a607df2246490b630ddda1ecf0885ad24c2ecb2c2c1b7b9c7de8066714bf5b9b25f61981d08576789ec", + "0x851f0375128d2782586223467d0a595f4c5baa79616622a32f7d6ce1f08af06f8a109bd6527f88d93367dba17be661e8", + "0xa2f1187c2a7cbf776653ff834ed703dd32e68eaf36f0700709be929f4c0ce5fa1d9930d1e3ea2aa01c7a16239e66cb33", + "0xb3721f4a5d24ca112f020cb3f849543bf0e7f84b470fb00126ae80aaaa6f2c208d8359cd82ad9fbafd3ef2ac70656fb2", + "0x98773ac3ce9528c73cfd8e7b95976ce597f67e146357642ac4fb6cb35046f3f39cf6c4a7b5af5c7740dda358aa0d2d08", + "0x92c883a5d820541692af75be1b25dd4a50a4b91f39f367a551a7d5ad6065a26b60d68221a01e4950559717b559c2626a", + "0xb82e46dd25fd1234dad26fbcd8bb5177d7b87d79d362ffb9c2f6a5c16eb2ff324d135996fcd6274d919634597869d772", + "0x82a53ed356ced5e94d77ee2a7f6e63f2ad8240aff2d17c5012cf5d1f18512c88c24793339b565dfbb659bd7c48dcbcd2", + "0x84d20c7859b35a1cae1ff2b486d50822f9e6858b6a1f089ce4c598970e63e7c0f7dfbcb3337845e897a9dedf9d449dd3", + "0x974892e5cf5ee809e9353d00e9cd5253d04826a8989d30cf488528c5dcdcad7650e23b4d228c3eb81f6647d2035a9e02", + "0xb2327854910dbf3d97fe668da5fc507e179c4bc941f39bdd62e8b6035f004449c467240f656417e501f32dee109f0365", + "0x88888f73475613d45d0b441276b1dd55835b69adfb27e26c4186936dae047b85478cca56be8dc06107b89a28f3bbb707", + "0x836ba22e40511feff81a5dace3df54e2c822b55e66874dd1a73929994ec29909ffc2a8e39bfc2d16e316b621eb4a5ec6", + "0xa754cedcccf4165a8d998f326f3f37d2989f92ca36d9da066a153c4aab5a62bb0011896bcbf90f14c18e00488d4123bd", + "0x86c26fa9584314292c4b7d6fe315f65dadd0f811c699e6e45c95a7a4ea4886c57dc5417b67edd78e597d037c7689568e", + "0xb205589648aa49ef56637712490e6867aa3b85b2b31e91437a249fb51bdb31401bff57b865c9e27293b30014b4604246", + "0xafab0843ede582e5a1898ee266235066b94ea378884eaf34919ceaacc0e2738e1074b6ed41e0a1dd9711563e24f0215d", + "0x996ed65fbcab7611eada5bd0fd592d3e44705098b8b1dfba6dcdbdcfa1382fe893fa55270a0df0be0e1938bd71ab997c", + "0x881bc448a5ef8c3756b67ecb1a378a5792525d0a5adb26cc22a36c5df69e14925f67c9cb747a2f7e5f86ba1435509d7c", + "0xb219303c02c9015c6a9a737b35fb38578ab6b85194950a0695f7d521206e1e12956cd010d4d6c3bc3fafd6415845d5d1", + "0x91748829bbd005d2ec37fc36fee97adaccb015208b74d2f89faa2e4295679f7685298f6a94b42d93c75ca9d256487427", + "0xa41d6fd33b9864ebc404d10a07b82ba9d733e904875f75526d9a1f1c1c08b27160dcdb9023c5d99b8ff8a3461d57281f", + "0xb68978d39c97d34f2b2fea61174e05e05e6e49cde587e818b584201cf59b7096cf1807b68f315119c6db8d6110b28a9f", + "0xb64e66cec798022d64ce52477475d27ea7340817fe7f570617f58c3a9c74071d7ea6b54743d4f520b62aecad9a3a6620", + "0x87b2b9e1c1786b7824f239a857024780a1457e51c64599b858118885833fb87a17d408bc09dcc0607d15ec1e53683a74", + "0x9814799bac07dab4f0c934cc3c051676ca13abd49cf8d4739864e5bb9f2a8474897695113f49239f28832a8658332846", + "0x806931a1526a843a9c2045943d616a8102b02b1f219535a1f1fbda659a1244f1bfead52ca7f1851ff8a97169b91c9ec0", + "0xb8678249595a9641c6404c35f89745b93d8e7b34d9d44da933a1b2f1606972624c5108f1c04eb42e454d0509f441ed9e", + "0x81426714851741045a4332eb32b6dfe6422a4a2e75b094fb7c3f37da85648c47ee8af1e54ba26f4e1b57ebe32d0e8392", + "0xb7a1875ea3f119fe0429fd9068548f65cf2869f8519dbbce0b143e66127cb618c81d7578e8391d676b2f3963e9d87f43", + "0x872220a803ea0c6294cdc55aceea42cfacfd7a482982bcb90c0361c351a900c46736a890609cd78f02fb5c8cc21fa04b", + "0x974f0380197b68205ff4bb2c9efe5626add52c0ad9441d7b83e6e59ddb2ed93ad4e9bbdbf33b3e0a206ed97e114ea0f2", + "0xa840f2d9a74fca343aedb32ac970a30cbb38991f010d015dc76eb38c5bb0bfe97dd8951de925a692057262e28f2b4e9d", + "0xb0913c3ce61f12f9fdc4be3366ed514c3efc438f82fc58c4de60fe76098fbc033a580ec6e4531b9799611c89a8063a66", + "0xa0180d533eee93b070dac618be1496f653a9a0e4e3455b58752bf1703ec68d0be33ec0b786f9431ef4208574b0ad316e", + "0xa4a6b871bc95d3aa57bed90e14a0a1dda6e7b92b7ae50e364593ce6773fbf736672b1f4c44e383af4c3cc33e017a545a", + "0xa3f44cf19fe52bacc4f911cab435a9accbe137bdbe05d34bdd8951531eb20b41d17e3540e8d81e6b3eea92c744562ee5", + "0xae6b6d0ff3b30ff0b7f9984ef741cba27ffb70d558de78b897199d586cf60622ec2d8a9d841712fe719cf0f97628842c", + "0x87abf72f98c81d6d3a57ab1e224fe4b502ab0d8090d8abc71791271550b721c220d4e2e7da3be94a20c0e63d98e39a50", + "0xb2f73ebdfe7133af57353052f4599776e16862905e64d97e1020c4bb84132e476d1ab79a9fb71611410f3f9d56c95433", + "0xae1a928253af2b210d31e1b64c765fcbd20a96b8d53823a6b9b6e7fc62249abf4a66c6a6aedb0b687e7384af9a845e0d", + "0x99c54398627833ca1435718154de171a47c709e4d5c58589fdabe62e72f2a7a11ae561bc31d7cbe92df4aff23e08cd0e", + "0x8a1310bbf1a31fae18189479f470977d324dec6518a5d374ab2ffcc8f64412fb765df57d2ddf69b9a6efaeb2b4c723b8", + "0x898312c6c0d3d3438229b19a8a233eca8f62f680c2897f4dd9bbcacde32c5996d56ac0e63e3e9360158761185491ce93", + "0x81b3f965815b97bc6988d945496a51e4a4d8582679c22d138f3d3bd467ed1f59545da2d66e7b4c2e0373628ae2682686", + "0xb9aca91c6e6f4199beb6976b28e0e35e36e8752618468d436b1cf00d8d23538d0747920e5b2c31f71e34dfe4d5c86a0d", + "0xb908f4aa18293295b8cacfda8f3ea731bc791074902c554764c603ab9a1de1bbc72654fd826bffc632d95ce9f79c27d9", + "0xa7316ae1baf4b1196961d53be7fe36535499287aba9bc5f3bed4323039b4121b65bb0bd15a14c1b9cd8b65ede3566da2", + "0x815e39208f205c5fac25ac9988c14a62ab01657c7737a24472d17b0e765644bc2cbb7ff1e8ea169b8b0b17b6996c4704", + "0x89a451d2b740cdaa83ccaa9efb4d0ff5822140783979a4fee89eda68329a08c018a75d58bd9325bdc648b0d08340b944", + "0x8cd08f768438c76bae6bee1809dd7be38ec42e49eb6a4d6862db7698f338bf6b4b409088e4f3d1c5bee430295b12a71f", + "0xa4bd8c312103a4bfeb25b0cfffec7a1c15e6e6513b35af685286333c1dce818ffeb52826f2f5bada6b67d109c4ab709e", + "0x93afbef5382d89fa539ca527f3e9b4a8e27ab69fd5d5023962cc6d8932b33cb4dfc5f14343e1a3749bfd5e100c9924e5", + "0x8d8e69d046992ec9ff14f21840809166cae8e0e9e7c8f14fb29daf163b05abe6611daa4010960e1141c5ab24373fb58e", + "0x96f8e72e96ba673c9265e9cc312f6b9c3b931745fc62d2444d59404bb08e5fb02ddb60715181feb9971cbd954526a616", + "0x8d444c2b8e4d0baadb79e3147a2ee20f1bfe30d72eb9a02f15d632185fb8f4e8c3116066f7de1ebfe38577aaccacb927", + "0x971410c0b10e3698f4f64148b3d2148fc6a4a22217fcf4253583530a9d6fbec77e2cf6f7bb5e819120a29c44653de3fc", + "0x99e7e1857bd5ee57007b7b99494b1f1c6bf1b0abd70c054770427d59a3c48eda71b7de7a0d7fcf6084a454469a439b41", + "0x8c8a4cd864894f7a870f35b242b01d17133cb5dfdf2e8007cd5f1753decc0d1fd41be04e1e724df89f1d727e760fdb15", + "0x890a24328bdeaaadf901b120497d1efa17d798f6f4406661e46ecdc64951f9d123d724ab1b2b49e0e9a10d532dd6f06c", + "0xa7cbe1f42981c9518608569a133b0b449e9d67c742d62f0d3358112c97e65ee3f08ec0ff4894ce538b64e134d168e5c8", + "0x87c976dea77b3b750c3a50847f25b851af95afbaad635f9bb9f7a6ba8f0c4faeb099dd777cf7eac41072a526474cb594", + "0x9882aa5e9bcc4ea2dd3de4bb5a0878a672bea924b50c58ae077563b6df0268910a60e969d3da1694ae7394ad0d9acd3d", + "0x90d35ce677327c461fb5dcb032202e851af1d205e9d21a34ed2b95635f13f8fb8dfa470ea202ccfa4b08140d0cf1d636", + "0xb3b4cbb521cce2b681e45e30a4d22078267e97ccdbdc611b2c9719705650dd87e0ca6e80cf2e174f8f8160be94232c36", + "0x95892b00478e6b27ed09efe23a2092c08e691b4120336109d51e24efbf8aba31d59abf3cf55c0cdab1c210670b9743ba", + "0x8643018957fb8ef752673ad73102d0b928796c6496e22f47b6454c9ed5df784306f4908641ae23695db46ebfcfb0b62b", + "0xb166ce57669bf0543019ecf832d85164c551c3a3a66c05b17874bccd5d0ae87245925d6f8edc62ac13dbd5db265823a2", + "0x89fb4800ce4b6c5900d58f1a216ad77a170ea186f3aa0e355840aeedcf374e92a15ae442800c9d60334544be020b17a4", + "0x8c65e586215a97bf11ffc591bce5147b4e20750e82486cc868070c7736c3de697debc1f335674aef24b7afdd41922d93", + "0x90f68ce0c97d2661d3df1040ce9c4fa106661a719e97c7b2d7c96f0a958930c57d6b78d823a2d41910261ae1f10e7b0e", + "0xadda85e1287371ccbe752aa2a3c1d5285595027ba4a47b67baf7b105a22fb8548fa2b5b3eb93ca6850ecc3995f76d3dd", + "0xb26535d218f48d6c846828f028c5b733594ce01186e22e412dd4f4a45b3d87d2ac1bfe5d54c987e4e8aaddeb86366d7d", + "0xa081bd86962ea3d4fd13df6481f3aeaabdd7ceae66f7bbb913e601131f95d016cf147d045253d28457a28b56f15643c8", + "0xb3d852cef4c8b4c7a694edbf6f0e103f3ae7f046a45945c77a1a85ec8dad3423636a89058fafc6628aabff4dbb95c2ba", + "0xb424ffc94e06e6addc90a6324e0482814229b5902e2a266d0c2d716e40651b952bc9f00d7dad9b6050377a70a72c7f24", + "0xb2cafd908cae0ca22eaa2d9a96175744897a20eb7b0a6d43b0098cb1c69e3cb55373888201e4ed32816655eb7d8a3dd7", + "0xb61177ecf1ae9d7e7852d98cbf6080d9f1e33c90f2436720b4ea4690437e8c7850c3754768fc1312cb4e838d855c5ccc", + "0x81b486644e1ae22cf0ba3a37e1df34dc186c82a99ab35ad6f475c37babdea574ddfbe5811d4aa020581292a793d66bd2", + "0x97ae848a823ea7a99f91834e537fb47208f616c08fe32c8f8fe06bd35c9b638698c513265d0b4de9e572a2f9692b98e2", + "0x81b8fef4ea5d399c65e78f40e47c559ada86d890777c549ce362e7ab81b3bfb00d5ff4ae4ee30fd7bda7ee90d28f85d8", + "0xaada6912cc748923ea40bf01922c06c84bc81b2ab0bb3664a0579b646f03d47ce88de733ac7f2cb9be4a8200584cdb71", + "0x89b48b9c79332f8f58eac9100ada5bb7decdc4b1555c5d383e2c1ce447efb0ebdff9c50bb52bc3042107f33a61ab2520", + "0xa32ecca8b870b2b6e9d10b5c1d8f925b3d629d271febad65abed316262bb283c60cade0e91047fbd0fac53ac6db372b9", + "0xb829cd1f13409e3573a8e109c9541b0a9546e98b6c879a11152b5564477ada4d8cb4b3079040e05a5cb63d75ef11eaab", + "0x91f3b100baa19e960b170fe9e03b799faac5b9c6f305c56115940bf81f6e64dcb9cda77e8de70ed73a21c0e8a74acc58", + "0xb25b5e872c84065aee04822bbcb4f3bdff57fbd7cea314c383765cc387786c17de3d5bb3de3ae3314bdede14542bfac6", + "0xa89bea9eca1f5a17a3efccfa4987d8e5366b0dba70ef1fef43aaea83c528428d1498c8b056ac27f16e8946ee93f7028e", + "0x818a1f7b0b8b06ea0514d6b4a0296da4f69cb18ac8e48c5579e6ba2880b06215fcbe81672566b8b94fcc3c0cadecb191", + "0x98dd6e6b4b4d63d9aa7464a2be08ae8babac4da7716a3f109340bc9187d59c6ca0c88e6877a67c65096f64a3ced22a4b", + "0xa2069c5bac4f6590042aefb37570cc20908b0df9d0130180f565ed8a53b4ea476a274de993561fb4d009f529fe7aa1cd", + "0x860b7ec2410f033a7b0c5ca08f88a0ad29f951a5ebd5383408a84367e92f1bd33bee3b87adef2466b7e33b47daabf30e", + "0xa408855a8414102c3cb49f47dda104edf0887e414723da59b6b6537ada7433529f6a4d1a4ad4fe311c279213cdd59356", + "0x8ca0d81dcb43b89a4c6742747d29598ede83a185a8301d78c6e7f1c02c938441360a1ab62a5e571e3eb16fe17131cbc0", + "0xaf7875a495cb4201cdb26e23b7c76492f47f8dd4c81251de2397d73d4c8d5f419cdbad69ba88ef0dc3552e460dbcd22e", + "0x80e901e433dca34f3d386f39b975e97f7fc16c7f692808221fb2ee60c1aaa8db079cc48c7d72fd548aaf8dde8d0b8f05", + "0xb6062319e13926416e57a0ffc65668bfa667e708a4e3f5cb26d8a6a32072f5b790d628052d5946c5068dd17cf4a81df8", + "0x90094b569e8975f8799863798912dbf89b12d2c2d62b3e5fac7efc245436fcd33af23b8c509ae28c6591d3f020966e06", + "0xa504f72d3d06a0c9b188a1035c7c6d80047451c378b6c5b2ffa1f8cecdb64871cb6440afb296974c0a528e5e563061a1", + "0x959061c4924e133a419e76e000e7c62204093576ff733ce0b8ae656ec6045ef94c5a1f3c934fb76fa9188c5eb397a548", + "0xa8b9d0b58de38cb86cb88fb039a7c4c0c79e9f07f03954af29013baa18fc2633883f8f9ca847209c61a8da378f9075d3", + "0xb16d8341da4ff003ed6d1bbdb3be4e35654a77277341fe604b4c4e4a1cb95e61362094fb3d20ab8482ea14661c8b9852", + "0x8ea4ca202e3aed58081a208a74b912d1a17f7b99a9aa836cfaa689a4a6aa9d9fbfe48425cad53b972000f23940db4c5c", + "0x96a372f55e9a25652db144ec077f17acc1be6aa8b4891e408f1909100cd62644a1c0296a3ddc38cd63ef46bef4e08462", + "0x87df40018ab3a47c3782e053dbd020f199fda791f3109253334a71be4159f893a197a494de8f94d6f09efa5811a99977", + "0xaff82d2ea6b3ad28d0ca1999a4b390641d727689dc2df6829a53e57d4f6418196f63a18495caf19d31fc23fdff26d5e2", + "0x9091053c4a18a22d13ad309313b6d2133a96df10fe167f96ec367f9b8c789ecca7667f47d486fc5ba8531323b9f035ac", + "0xa4842090515a1faccc3d8cadbb234b7024254eba5fdfcef0d15265c7cec9dc8727c496ad4e46565d1f08504c77e511d2", + "0xb1d8a37b1a97883d5804d0d2adaa8dbf0c2d334ef4b5095170b19613fb05e9c648484093d0c70d545cf9b043b449c707", + "0xb1ea40f3dd1c3d437072f8adf02c32024f32488dd59389d1c3dfe78aca3df0bab7767f6ded5943cc10f50555da6092f5", + "0xad219c6a8149f10391452892b65a3268743baa7402736f810a35d56cdfed83d2172b03f15c205f0dc5446baf855907a5", + "0xafe44c3e1373df9fc53a440807fa6af8ebc53f705e8ee44a162891684970b04fb55d60bc2595626b020532cb455ee868", + "0x859ae154b017eae9be9da5c02d151de747cc23094d8f96d5db7d397e529b12fb55666f55e846e2bbe5e6f5b59c9d8b05", + "0x8aa01354697de23e890fe54869cd3ec371f1be32064616ca3a556d3019541ba8e00d683f1396ca08e48988f7f7df5de4", + "0xb8f682487460b9d825302c40a7d6dd0353ff43bf24cd8807cdfa46c043e3f5a7db182b27a8350b28e91888802a015af4", + "0xb6d4d6c3ac40f8976b50be271cf64539eb66dc5d5b7cec06804dfe486d1e386037b01271cf81ef96dba5ea98a35a4b43", + "0x9385a2fd1cd3549b0056af53f9e4a6c2dfcd229801ffda266610118ade9a568b33e75b6964e52fcc49c8e3b900e1e380", + "0x98f4aa0e4ef039786cbd569536204e02b0b1338568d1d22bb5bc47b5e0633fb7ffe1da93eb9d825b40b9b7f291f84d51", + "0xb7b3460cf706dc270a773c66d50b949dabad07075021d373c41fbb56228355324d120703e523ea3f345ef7249bfff99d", + "0x81b826255f95201987513d7987cdc0ca0529524d0e043b315a47583136dbada23a114d50d885bb3f855fa8313eff801a", + "0xafdc6c35161645a14b54f7b7a799910e2e07c8a5efe1827031a2eecd5d9263b3baa367fdd867360fabc41e85ab687e74", + "0x817b361ce582153f2952f3042e235ee2d229e5a6b51c3d3da7bbe840b5c6ec2f01446125045848d15fd77dc46c8a8fe2", + "0xaeb599265398af6e5613297d97d2b70222534590fcbd534d68b24a0289b6366ac8188b753f6fd1000ee73ef44f8fb7af", + "0xa5a9e528b606557be64460c1ad302a43e741357827b92ddc50766a7e6287740fc23bd528d9faf23345ce8bff527d5bc7", + "0xa8d3b3b438d5f75efaae6ce7b67c2212899ece5b5bdc9bac655e271fd1846ea8560e646fdbded3d9363eefe29473d80d", + "0x984c7976d557e2f591e779c2885f5033da6f90d63a898d515b5da3adbffa526764cd8eb679b771573fdf7eed82c594ec", + "0x8ac748689cc3280e064807e68e27e234609e3cc87cb011f172204e1865ad7fdc78bec1672bd6e6fddcf4e7902b0f38bf", + "0x877bb392059540b1c8f45917254b8cc34fb7e423952bdc927e0a1622efec4113fa88988686b48134eb67ddebcb7c3ef4", + "0xac04b154ccd307ca20428091585e00121b61bae37b22d5d2a1565bc1134be3c81ccf3715fffebe90744164e5091b3d9a", + "0x90745c04278c3a47ceea491d9dc70a21a99d52648149b1ab623b5396b7d968fd3c4d1a2d08fc5638e8790463e0cf934e", + "0x80bf26ca7301e370f101cc69e7921e187cf5315b484fc80a872dec28bb65886569611a939958f4a3d2d3da4350011298", + "0x87cbf4d6f0c06cc5f24e0f173a5f2f9bf2083a619dcce69a8347c1a6cd1d03325544610f2984eb87a13241e6ab9a22b7", + "0x8909368817a515789ff4d19ed26afafa5729a24b303a368ea945a9287bc9facec9e1c8af19cbec8dab4acbb6a6ddf6c7", + "0xad8d2f82b08e0990dfd6b09fd54db3a30fd70aad218275550f173fd862347e1258a4716ca2bf4c40e4963850b2277eab", + "0xa9467ceacf9337cae4f2c7eeb3e03752ac7d77692b07d5e5d75c438fbe7dc2029ff84f7759372a0ddfa953b4ec7e9e38", + "0xa5feb7669e84b977cb1a50ff3a39c28f7ad1ecc33a893fdf1ddae7a0d8a4c5f6fbaff25cc56631b708af038a961f3b55", + "0x8f2e1fa07963ba18db890b44c3b9ae7f8992b702a5148679df69e4d9d4b1c082b2bd2ae53f96a4fe24b54f3dc1588f17", + "0x896778f35cbecb43f001277c306e38a9c637275101f1a09546f87378b10ccc025644bc650b3b6c36e4fd0c09fbb3df35", + "0x91dc702778176a4d089dc65502d703752dd9a766f125ffef26bdc38fe4abcae07cdea14102c3448d10f8dd6c852ee720", + "0xa5df3004cec6b68b937cadded0dd2f48bd3203a903a3e1c22498c1193f4567659ecaaf3deb7ed7cf43796da9188f5dc6", + "0xb18b4c8ffcb8599c24d9851abf8ee43047cbd4c9074c9cfbf88376a170da4554978988f550afde8a45306ca32713c204", + "0x8370bc38c84da04d236e3c5a6c063e1db6613dcc4b47239d23efdcb0cf86846955b60da3e50f17b17cd3f7e0c29302d9", + "0xab7d6bb6be10aa52ef43abbe90945e78e488561afb959dc2fe768f8fd660d267c7203a2b7bdfa1b44cd07898f4849e06", + "0x965c96047d82d76ec2cfe5035fd58d483cd2cb7f65c728ab3049562c5d1943096d6a5014c05babc697d79c07907cf284", + "0x9614f7006aef6f0478ebd37fbf17276fe48db877394590e348c724059f07c3d1da80d357120d3063cd2b2bc56c58d9d6", + "0x819c7b2a1a4bb4915b434b40a4e86dd7863ea85177b47a759bc8ecd8017f78d643982e8a091ee9a9e582f2b0208725a5", + "0x8e159a185b5790a3ed444b6daab45f430f72f4ac4026750cbd5c7cd7947b5e00f2b10eaaf5aadf8d23054c5b29245546", + "0xb48cb6f6c0aaea04833e10d735b67607846158b6663da380ef01c5bca3c9d537611716867dc2259883e5bc9daed57473", + "0x8b48ce8b5ab76b7d662c29d0f874f5eec178baf3f14221bffd5d20e952f54f3ed053182a486da1d1f400e0acef58f673", + "0xb6fd3cba177bfbcb5e7ebb1e3c1967cad5848c09c615ba2a6c277908f8b1f4f1ac5f184c33f2a401e8bdafcaed48bb88", + "0xabd8f44c4a447de8fde1c119f4fd43c75b4cc99de9c817a019d219d4b2ad2a73b60606c27e36e9856a86bf03e7fc861f", + "0xaf9f7e8b3e9e8599c7e355433c503a05171900a5754200520fd2afed072305be0e4aebb9764525d2c37a5a7eede72025", + "0xa0960a58bd2681804edd7684793e3cbb0e20d1d4bd8721b192baf9aee97266be14c4ee8b3a3715845dca157ba2fb2c1d", + "0x949a37213209adfbfa4e67c7bad591c128352efd9b881c1202cf526bf4f657140ef213acf0efeb827a0c51a1f18809c4", + "0x9192fae84a2a256f69a5e4a968d673bebf14ea9a2c3953f69fe0416f7b0fafa5166f3e4588d281f00d6deac1b6ec08bc", + "0xb1a249662f34a88d2798eae20c096268d19f1769d94879b8f1aa40a37b3764349b8e6ab970558436a88a5aa5c37e150d", + "0xaea87086dcd6de0b92886b3da0813ff271a7107ab1a3cb7021b85172c1e816a84dbb1a8fdb47e8a8eb5e6fcddd5b919a", + "0xa586b5078b3f113eec9f074430bcf9aabe4e82752e5b421c6e31d1c2a911512e34154bf8143b5197e820c5af42aa8ac7", + "0xa6eda122e400a6600f025daa383685a10f72f62317a621698bd0106b331077b05ac1afc68ece7a2e285c54a366921a3c", + "0x8875e9ba654ad7b1d57ede84e2b702600416d40f7475fe2df25dd1b95c0178a227ee187547898e5b9d1ce8ce9ebd15c9", + "0xaf2cb289f8c75f4ddae9e3ef9c1977fe4d4d513e411777b03b996f5baa372eb995b5ca96255fad9ace776168806ecc42", + "0x8d24c465d26bd93290f45ef035bb6dde4530d9d7d051baf583b1f8b98e9886de262c88b5709084710cffa7c767b4c27d", + "0x8cf35b1b28a7726645971805170392d522f5e7e6cb94157fe9c122a987051c1c90abe3c5bdb957ef97b1c45dd9bba05c", + "0x93e2bbd82a3cb872cea663f9248b21d4541d981f3f8d5af80a43920db5194857f69e2884753f6ed03b6d748dbfb33620", + "0x8b774b97657db654ebdafce3654d645f849203452e876e49dad7af562491cb6531bd056f51cb5b2e8f0a99e69bd8566b", + "0xb5333c49d3e1c4c52f70f3a52f0ad77165bed6ad9dcbfaf1364e7a8a0f24570e85a218e4c2193f63d58a7dd975ceb7a5", + "0xb4a34c443e4fdaab8e69fcda1fce5e72eaa50cf968f5d3d19084d049c5e005d63ab6e1d63dee038317da36f50ffb6b74", + "0x824a224009c6848b92d6e1c96e77cb913fee098aaac810e2c39a0e64d5adb058e626d6a99be58593d921198edd48b19c", + "0xa86f1fdd2e1ba11ebda82411b75536fc0c7d2cdb99424e0896d7db6cae0743ee9349ffa5bff8a8995e011337fa735a9d", + "0xb406b5b89b8bed7221628b0b24eb23b91f548e9079a3abd18be2ed49baf38536a2c1ec61ab1ddc17928f14b006623e7b", + "0x8a7ea88d1f7420e2aaf06ee90efa4af798e2ec7cd297aacd44141471ed500107fdd93bd43b6de540314ef576646a7535", + "0xa7a8c071e68bbae9aca110394cf56daad89404dff3e91ea3440670cd3d0423b67905e32b1ba7218fd4f24d2f8bd86ce7", + "0xb959830f152e4d31c357be1ded5782aed5d6970e823cf8809434cf4fddd364963bc7cfda15c8f6b53eda16ab20ca3451", + "0xb59232c8396c418238807ce07e0d248ad2045289e032678b811cc52730f99b480eb76f6adf985e6d5e38331d4bb2b9d5", + "0xa14092fddecc1df18847ab659f6cf7c8603769a4e96fbe386d8303b225cebbbe8f61d6ab3dca08e3ed027e7e39f2641f", + "0x941cb0632acd395439f615c6b4b7da9ed5abf39700a8f6e6f3d3b87a58a1a7dbb2478a6c9ff1990637ada7f7d883f103", + "0x951b8805ecb46c68101078847737e579206f2029e24b071bae6013e9dde8efa22bce28aa72c71708caf4e37f9789a803", + "0xb2cbf22e53f6535fa950dd8de4aa6a85e72784dd1b800c7f31ec5030709d93595768748785ff2dd196fbedf3b53cd9d7", + "0x8d84ea3a7eafb014b6bd6d57b02cab5ac3533aa7be4b86d2c5d53ce2d281304409071100d508ed276f09df81db9080ea", + "0xa2204b60836cba8bf29acd33709e6424226ae4d789ef6b280df8a62e30d940bc9f958ff44b5590d12fa99fcde2a4a7a9", + "0x86692c58214f326c70eb2aaf2d8b26eae66fb624f143a3c144fd00f0249e30e0c832733a7822fac05c8fe74293768ace", + "0xb1cb3d64eb5b9ca0e01211128f990506fba602cd1417da02237205aa42879ae2a6457386da5f06434bcb757f745f701d", + "0xb3eb4290a53d5ff9b4596e4854516f05283f2c9f616ec928a0934b81c61afc351835f7eca66704a18a8b6695571adb30", + "0xb0bfb1d44b039d067d7e0e2621e7c4444a648bce4231a6245179a58cd99758ec8c9e3f261d0adb22f9f1551fceb13e4a", + "0xa29320f71a9e23115672ea2b611764fe60df0374e0d3ff83237d78032e69c591a4bdec514e8b34f4b3aeb98181153081", + "0x8a6abe9c8a048002b2ff34154a02c2f13fc6dbae928da47c77f3e5b553ea93d8f763821a6ead3c6069677870fdff7ff3", + "0xb73ab66a62f427e1a5e315239a2e823e2a43550d245cff243c2799eb2e4701fabb7d5f9ce74a601b5ee65f6555dacf64", + "0xb64858e98b9c10de8c9264b841b87e7396ba1da52f0f25029339ca1d13f7f9d97f4de008cfe12a1e27b0a6b0f2c9e1ab", + "0x807d2440d1f79a03f7163f5669021f3518094881f190cb02922eb4e9b17312da5e729316fe7ba9bfffc21ed247b033cb", + "0xa7f06458d47ebe932c2af053823433a8a06061c48f44314fad8c34846261c8c3f7f63d585a7930937327ad7d7ca31a6f", + "0x82ac2215eba9352b37eb8980f03374f5e0a2f439c0508daa7a32cdce398dde2a600e65a36795a4f5cc95bbcf49b01936", + "0xa1882c83a2f946d54d74a008eac4aed70664db969e6799b142e0d0465e5662ba0d224a1cc33be339438d69bdad446ff6", + "0x8009776f7a34a3c8779e21511fa409b0c5a38e172d1331acc29a16114e002f5f2f001381adb5fb3427a100752d775114", + "0xb24441019af4a0df2dc68e3a736f358da0fd930c288398a18bb5a8d9a1e98ea376395f19d8e03a5f020b83fcb709f1af", + "0xac72b4de3920c4f3c9b8ea90035cd7ed74d34b79e79aab392f057c3e992ebe79050cc1c6ccf87120e4162b29419147de", + "0x973e75577cd2a131a0bd568fd44e43554ac5a9ea3bf10f02d1ad3ac6ce9dc7a8a7ea93aacf3325f7d252d094a0de1376", + "0x98a114de2a86f62c86862de37c328bf6a7fccff4d45a124addbe0eb64debe365409fcb72ce763f2a75030e1ff4060c64", + "0xaff753e1dd4707f1a359eaec06ebef1903242889a2cb705d59dd78a79eb5b894731f5a91547479506145ca5768877dec", + "0xb856e4234858b5aa515de843e8bd4141c15a4cc02c51640e98a8aaa1e40344f1ff8ef7c3b913ea2ae7411713daa558d2", + "0x863525eb2f8147a6d1d0d4304881795bfed348913cd7f38d815d929a426788b69e41f022dba5fdcaf56c85720e37fefe", + "0xa14ad76b145a6de2e0f8d4f615288c1512701a7b3010eb8a95941a2171bc23561e9c643764a08c4599040a3b4f5e936a", + "0xa18bfc66f6139dcb0485a193104fec2e7d52043837a4c0cadb95743e229712a05cf9ce4ccb482f36ff1ce021e04b574a", + "0x991c8e6678077d6e5f5733267c1819d8f7594e3b2c468b86a5c6346495a50701b1b05967e9590c15cef2f72bc10a38f9", + "0xa034e7f9b547b047c99b99a0dd45509b0ac520d09130519174611de5bcdb9998259e1543470b74dcd112d0305c058bad", + "0x95ffe0d02317b5c6d5bfddbcec7f3fdfb257b26ad1783bb5634d983012e2ea1c6b9778009e1b6d10564198562f849ac0", + "0xb3db442aa4adb33577583b2a4ad743f41efe0e1f87bfc66091d1d975333ffc00b4afc43057bcb88a7d68b0c9695d38dd", + "0xad2e97d10d7c53d231619e3f2e8155a27ea4f2fb3c0cecf5c7f14f4cfcdd21f62ea46d843b21df748b2892131633fed2", + "0x905d7aad6d3b56bad48694b6b20b27e370ebca8b91d0821e48e2f9cad39910c26cc11c77c266894db3d470485a63ed11", + "0x99bfadefca796ce6af04ede65ba5ef5bf683ff7e2852bb9c406fda77b95ef382289853dfe4d933525071e4cab8ce3936", + "0x94d9905ed4ef92107d0adb9ea38f085a2a24b8f792108bec702d747c215b1f14aafd486ea0c07ed42602b12d8f602b93", + "0xa78dce23ca09dda2d5e7fe923290062546825286d624de35ac5756b6c8ae030e211f4f9c9c8d18a924f5880e3b383d1f", + "0xabce9e2128ff51fa17e73d93e63d7134859b2f328eedbcefb337c39e752d6750d9cffe6abfcd359c135dc5a12018827b", + "0xa9ea7d91e8a3524acb3182bedd7e1614d37b48f8eb2d8f677eb682d38408b8d512786d8bb65811f4d96788b9378e59b3", + "0x912c9f804fb57dd1928f8274be58b42618f589fc72a7e5b6cb4d4b5d78c547f80737cdd77ebe5d2b71eaf60b8fd2b663", + "0xb7227ec9a62d5538974547f717fdd554ab522d8782667fc3e9962e9c79a21134ef168371bf3b67e28d0964e92cf44028", + "0x89440a781c812a19c758172bf722139598023ed0425374fbb0d91f33be7b7f62a36d7aa34696c4fb0da533bd5dd41532", + "0xb31e4a9792d6e9c625c95aa3c0cd3519410dec07940afab820ef9f63017415d237a47f957d0b591b6de399ffc2a8a893", + "0xa66ec47393df2693be161daaa88be0cf07b430c709ca97246d10a6080ae79db55c9e206b69a61f52512b868ba543e96b", + "0x90ca425dee74cc6a7e8eb1755cf9b7b76ba2a36ab851333b0fb7b35e8e6e189702456f2781ad87b4215993d62230ff4f", + "0x88b64741f93a2ae5d7b90b22a5e83c9d56bcee5c6bfcedb86f212acc776cc3ebd0b62cc025f596cd8db4f4b6a7aeebab", + "0xa1b6c7d2358bb201b42264f8fbebaa242ef105450bab21b4a2f16f368048c16ad1f3695841787eb33a0192f1f6b595eb", + "0x8a932f1cd227ceb18389791ed9ea1ff26571715ed1ab56601a994795713a8f7f031d1e8472ec3eb665b7bfbbca8ca623", + "0x8bb2e34a2bf77f9f657dfc51ff296a6279a4d7d15860924f72b184fb7d5680320c7769954b9dac73c4bfe9c698e65e58", + "0xaf54e7367891c09f2cea44cc7d908d37d058162ec40059d32ded3983a4cabfe5057953878cf23bfad5292dbd0e03c0e1", + "0x8a202532b9205385cf79f0299ddcb3156fd9fab09f9197bce762b5623f75c72ab1d74334ee6f0d289007befe222bf588", + "0x83bd0f5896eaad58cfa7c88fc5ed505cd223f815dcfe93881b7b696cdd08b8b5ede03ea5b98e195c1a99c74ac5394c1b", + "0xb4a84d9940e58e3b4f804e4dd506f8c242579cfa19323c6e59047e5a1e35150699a2fab2f4862dba2f0ee4ed1d8970f8", + "0x8c9ec477d057abebc2e2f6df5c4356a4f565bde09f499a131967d803d4bf36940ca2ed9d4a72adbe0a4a8b83fc686176", + "0x8598f43c32623fd5b563d1ec8048ffc36db3d7f9b3a784299811687976f64b60585b2a2707050a3c36523b75d1e26716", + "0xb55eb07014fe5ad3e5c9359259733945799e7429435d9bf5c72b2e0418776e329379433e17206f9f0a892d702a342917", + "0xa5ed942eda7b36a3b0f516fafd43d9133986e4c623b14c0f6405db04e29c2d0f22f1c588150f670dbb501edda6e6dd4b", + "0x92b6abb28cefab2e332c41c98bfa53d065b7d262638389603a43f4431e6caf837b986254c71f7cdacf4d6cc4064b0195", + "0xb01806178a28cc00d1561db03721eef6f6539676d93dd1fa76a13b42a31d38797e99b1848de92fd11821a342b04f3f72", + "0xa2f10303437acfbb5912e186bbff1c15b27ed194c02cbc1c5b482b0b732c41fa809136e8e314e26b5bfe57690fe3b250", + "0x9990207fcc711102e7e941b3ac105547a3e7301390e84f03086c99c6d3e14efff3a2e2b06e26227f496d88d5cdaa3af1", + "0xb903cdb0c2fd578612398c30fe76d435cd1c2bab755478761244abb1e18ba8506fd9c95b326422affbcaf237309959d7", + "0x99e0c12cae23f244f551d649302aac29bfdeb2c7b95578c591f512ad7ac562bd47e7c7317ac9bac52c9ea246617bdb48", + "0xb996d267ab5149c1c06168ee41e403be83f99c385be118928d6e2c042a782de0659d4d837f0c58b26df0ce22049a5836", + "0x989001b8414743765282f7e9517e4b8983a929341b8971d7dd8a87d246f6c8ba5e550c983566ddd932c22948f4fa5402", + "0xa0b006a2c9124375364b8fc5ddb543a7468fa6d321ea046d0fd2bfdaef79e5e3600b3d56190733491ca499add1298c7f", + "0x80881d6f3ee507089b7dfb847fc53dd443d4384ef6fce878d07d9b4a1171eefea98242580e8a6a69664699f31e675cfb", + "0xadc48ef53d88b9d70409ed89cc3be592c4bd5eb65d9b1b28f2167dc4b12406889c00f2465c554f3aff673debc2997ccf", + "0xa62f5d9f167b9f4a4aab40d9cd8c8a48c519f64a1985823e20e233191b037c02e511b0280487112a9f8b1f1503b02db7", + "0xb89aa2d4fb345a1d21133b0bd87f2326eb3285bd4da78b62174bf43d30a36340e4217dbe233afb925ab59e74c90fccf0", + "0x932ba22acdd2f9d9494da90958bf39d8793af22417647d2082d2c3e6a5e17a2d14b0c096139fa8fa3f03967ca2f84963", + "0xb67b107e71d96de1488b4154da83919d990502601c719e89feabe779049ddf7e4fb7e146eb05e754b70bbead4449efb1", + "0x84509de1b8dc35aa2966d8a48501f725d59b4c65f3abf314b2009b9a573365ae3163c1f276708c66af17de180aae0868", + "0x849153fe837a33fcb32c5fa6722c2db9753e984867c112a364eb880d87467782142d1c53a74b41df1dec7e900c877e1f", + "0x903d05c73ae043b69b18e980a058ce2254d008647a8d951175b9c47984164b34fc857108dcc29ad9df0806d7e90405f4", + "0xa6b05917ac32c0b0eeea18f1ef3af5343778c543592078fdf6a1b47165013e2676bfe6a592a24efab9d49c4bd92b8fc0", + "0x8648482f6947a5a8d892a39f098160aae1a648cb93e7724ea9e91b0d1a4f4150b91481f6e67d3bf29ff9d65ba4fa61a8", + "0xa6ecaabc38895013297ae020686f04ea739c4512d2e3d6f2d9caf3f54000fb031f202e804ee615eb3357714a18657bcf", + "0x912f5935acc2dd20d5ef42b2ad5b307c925324a84a3c78ff66bc5885751934bd92f244e9636b60a744d750a2a7621198", + "0xa0d6f261a776c5b114298f5de08d6e3372649b562051ea2470d3edfc376048793e18fc57ec84809b463dc72496d94329", + "0x940744cd3118d1598c248b38503f6f1fbdbe7a147e683e5b3635140aa91679f8d6c1472600f8e9c36117a60203be6b4e", + "0xab81737c839fe340f6f1fb7275811cb0c0d5fe8bbc265f6a56c6c68d0291bc7234eaa581ff26f8929d9a5bed4aac7002", + "0x8df47341160f1c728c3e31be17a32e42b54faaa1286ef2c7946882ca4dd46443b8428f3654616c6e4053f1cda2e11994", + "0xa721067e75c3c791f4d9f58d4810ac9621606e29c6badb593d6bb78c39968b45be1777ddb9bf03696d4d4be95b2dc1bf", + "0xa4e399213d3c4350c2d0cbe30757ba7e1f9680f58e214ff65433b36232323744c866a87d717851ba1dbd6769599f69a6", + "0xb0be851d1e43dee27abe68f85e2330d94521b5f1c1a356ad83fcd09162c0ca9c2e88bccbcc5bacfa59661764361867a3", + "0x86111bdd3dbfca232aa5802a6db41d639502e43a2e24cb06bb5d05c7f9b5ccac334d16b61d1c5eaac4fa0cab91113b46", + "0xa4f805b11c174c34250748b9beebfb7c8c243198fb13463911906ee4effe7d331258a077e374b639a0c5cdcdff166b7f", + "0x87e4cf2c6f46d2dbac726a121127502921decf0195d7165e7bbeec6f976adb2d1c375eaa57f419895a2c70193215dc4c", + "0x8ff06de2c1c4d0744483bb4f7c5c80bf9c97b4df23e86c0bb17f1498ea70e0ee3af20827da5e8cb9d7f279dc50d7bd85", + "0xab112c0116471b4dc3fd1e6d918f99158eb7a08153e891ddbba2fe5bf0eeb188209e3019176e758231c3df937438136c", + "0xa67f89194e99e028a5da57747268e5ef66fefb881144043429920d222d37aaf268ebf73ca1da659fcdac3b4e7a65092a", + "0xb4da1dcc791566140d6abeaa2923cb6b21a6e6aaa30bb4cc70011e931eefa71f96b7e05358c0654bad7ce45191ab9fa8", + "0x8283933231bca359db588c80e043ad6ea765fb0cba5ef233c5d514ba01ddd1b409efbadb368f26763402e4576dc4655f", + "0x97f568ce3edacd06f3e31a15462f5f9818a8c3fdbcf92b1ac5840b0b6e73166a154013dd52e85a18e8ead3fc9e54aca0", + "0xa9cd1601c41e5ab2018f986443914fb703ddb6b06a36c06fb58065f2fee8e1751071ef924ea3ad76f0c19baccb1b5f8b", + "0x92aad71bb7e929cc35a48020d16a5822f4f106a7f59985005a5ae5ba8e8016ec33727610393498f56b4f353b3d5161b8", + "0x89427780aa4e7ac894c681fbe2889153b94db883f17f109bc9caa93f0c259dda42aab502bbefaf572c56f70abbc42db8", + "0xaa8cf76ff847dfe59534432ed8520bb48bf412c28497747dce04d2b2a54ba843c3be1564630cb49ec0217167847ba590", + "0xa1570a6748a2303e74a31c2131d05ab372ec006ee92ef74c42f2e9a250663bebdfb3777e7ad91f50c954889a59c2d434", + "0xa4c2b1bbc48199c31ea8d8196729eab00ce0200350d4aa9f23347a3289355e5828cb2f93036a14d2d9ec575fb3835239", + "0x84819d0bedbaab5bf8afdf23f59a7ec5f50da3063cfdd1ef5fc4ca4c1fe68980b5c80e30a49f38e5816765e81dfc5a57", + "0xa57cfb5e877b88202f589be777605deafbfc85ed1357af03a18709cfb4b668a271199899243cd3750f1cb77ebc40bba7", + "0x8d95934bbb0efaf3339f27cb96de46e4486aa58a2c40dbc77c1c3ac7c27a228062824b9045c046631b2e286e8549603a", + "0xb99a8356abeee69f40cb3bd8c87e8039a1e076897dde430bfbf989dc495c48609a7122bc6c1d1c32ccac687b47d5558a", + "0xaac2edcf2fe5d3f1a84e8f1f27ece920eabe7793bf0ed5290cda380752e55d57a55a362c5253bebb71e4a55f2c437ff6", + "0xaf7c76876072c3b0091e22b9c5b27ce99bf1f0079ea1a7816ad9c06e9e5fc407595c7f4f9953e67d86fb2da656443dc3", + "0x9175b64d104f78d3310c9c02f82e04c8e9878d2044ea5ee9c799846a3d23afa5fa2aa4af7350956136c69a0eed03cb2e", + "0xb3328e953317494a3d976e7f7c3d264258a5d4b2c88e12d06786a9e7b2affd41086762ef6124c6a6e5b6b028db933c14", + "0xa49d166065e19d39299ee870229e4a04be81acd6af3a2201f3a291a025dd5f8bc3e676ee123cd4b9d8455f6a330b395b", + "0x85fa15bc8947ba03681d87b50bd2f8238b1c07849a7ed4e065053fad46aac9dd428186a6dd69dc61b5eba6ffec470831", + "0xb6fcb2f694a47d3879b374b8b2967dcd59bd82a5d67ae6289a7326c18791b1b374e12571e8c8ea16a4bfc5525ced3ec4", + "0xb6115f52566aa90ccac2aab6d2dbf46eca296d047db1eb29a1b8a2bc2eef7a24e90407f8dae528806aceb2a1e684d49e", + "0x9707e66220233f6a48a93e8dec7b253d19075eaa79238e519b82ce1ac5562cca184f8a1c14f708a96c34ad234673d646", + "0xa0822903fb3825eae07ee9d3482277c0b8fc811856dfe4a51cf24b373f603924166fc5485185f99c4547cd6476b62270", + "0x88dac6366c439daaeee2532b2ddbe206132cf6e12befbb8e99870ac684e04e62de150cba0e22e395a0b858948f40808b", + "0xa72dfba9caad3179f43fead0f75e33ba5342470d8c9cb7c86d30d2c7ce7244a8aafd1d558b0ec8e2a9436de2c2e95ccc", + "0x8d696046defcc32cc19954c559213100f0ba273ea12abb55ca7c42818071d853846bd4213af2c41ecd4442f6b4b511b1", + "0x89d6f2d52cf65414da15a2fb1911c53afbfb50bb5f2638844abfc325ff2651cd9130be4beff05dc4046adfc44394a182", + "0xafb91abd7c2a9cfe62855ede3c6960ad037fe8778364a2746ff7c214c55f84e19a474a9a0062b52a380d3170456ee9c6", + "0x87f724a16ec8fdae8c05788fa3f823ecc3613df46581a63fc79b58f7c0dc2519b6b23e3dd441a0ca6946dfe4bc6cd0ce", + "0x86760f90f6bedfba404b234e90fbf981d26c29b87f2fa272c09540afa0f22e6682d08c21627b8a153c0feb27150458e2", + "0xad4d0342f255a232252450ce4209507ba619abfd1ffcb9c5707cfa45f89be41d88f1837acea993a1c47211b110250b4d", + "0xace54b5889bccdf1d46c4ca21ed97cca57f7d12648381411d1b64afdfc64532a12d49655776ea24cf5eabe34145705ad", + "0x936dac693d0c1b1e5de1701f0bc46aef6e439e84bc368a23c0abe942eb539a2950e8929265786fcdb18d40a44bda14b9", + "0x94fafbc544decec1d489b9ad6b23410b9de4779f9f44aabd093d7fab08340a4646a8cba31633e49c04d2690b8369a1d7", + "0x98157e757f1a677c5d9d65c47759727a4dbc49fec2da4d9889c4ea90573fb42e2a8d72eaef92b782ac6f320970f09363", + "0x8eaa0498c191c810c7e1ca7398f7c80dd0a7e7d7829ed07039490f60e7c2ae108843c06fe38fa36d45d63da46cba887c", + "0xa0ae116e5b0d2dccf83f056ad876037225687904e0290fe513fdc6b2dbe4cbf5fac1d828352e64734895895840b3c57c", + "0xb592b318dbbd7ec4872aae5e64bdf2305db2e5e8cfe0ad77b691f542ba5e066dd20b09b0b08ff0d798bd79ad946ddf7f", + "0x879e50c8c3e7f414ad2b38632bc482b71759cd561aeb2215550186ebb4559e4cf744cdf980512d8321954b3458d21e11", + "0xaed5c6c7ce0407d7b2c04785fcb9deadb9b9413e37cef5b1d918f474cccc7de012fe1fa6f5fa93cb7ef9ac974d9fbc20", + "0x892274a9f0afc68fa74be276c2a16de5cec674193f96b27a80bbb9f3add163f85716b531f3c920b98577a0225f84e8ca", + "0x938fb7a53266b997a7669596577af82f5289b160b7fcf06d76eee2a094696f6f12b28c2c65b833a52529a116c42e6c7e", + "0x892083929b6067f5045b1208f3dc8f0ee25bd0533a8831f5c23bb4ff46a82d48f0a34523359df5061d84a86b718d5060", + "0x99159ae9574df6c16273eda66b6d8b79a327940e335b28c75d647f4744a009f4b5f0f385e2017bd3e7fbf59e629cd215", + "0xa03e5757ef7738eba32d396923ff7ef82db2c15bb6adc8770fcb37260b7bda3be62473bc352a9a2ef7ec8ebe0d7688bc", + "0xae3c24a85c9b1fa55158b2acd56d2016f70dca45a23f3ef7e0c6b096f4a7c54c14020d61bec7c7f87be4a595bf254209", + "0xa920a6f9cc803fe31352fca39c13f8ac1e8d494fcf11b206092227c2af38469b1fbc068b8fe014800b70f137107aafc4", + "0xb893853be57519ffa6410da605e7d3a746ebadec4788c7907f6e0dde9f20f5a6a01181148b874b3decf9b4814846a11a", + "0xb46f43918c5195729f6532439f815d1eb519e91005bc641a4a30ae88700982bf4ed07a342e77945780317c297c903755", + "0x8e431bf4497d0ef6538c93c4bdda520179301a0104eebcfd104efa1edea876818d7d31079656f01a5ff76c4f5fcd71df", + "0x92e3dbcb580dfb9cc998f878052b0c3be1c5119e5249ae9bad3538ebb0f0c4ab5a959b04033b96d61836ef07784e6b64", + "0xb712d9d63aa888156f4ec83e939c6bad53de18045f115f54fbf4261fb02f10a8a46a8d716ab43d4acbad3b02283c32fc", + "0xb2334e776988b4f772446a47c87416b4f19f9b44164a5f828424d3f35ef10baa56afe810d49b0b86b786b9c0227681a6", + "0xa3f25ad18e435ef585fa90e6cef65a8ba327e5e33701979e27e64ef7d8e09e2591e52bff9c5749d35643456d18625685", + "0xadcfa48ae43cac6fa9866b4cce10a243969965942c891d5e6c0e5b03bd4763f9b63779fbf40d26ac674534fe7cc478d7", + "0xa0eb3448e045038740e2ee666e88aa0f8b8e24b1b55d7d4964f01bfc0c581f7e9d4c0e79f8cfbfecfa8b024b216c8ea6", + "0x8110aa1d82f11965af4f4eedb4de09ee9c353481b2d7ee7a2bc2f302d2a5ae6c31ebc6451309ba7c305da41070b0f666", + "0xb074fdad419d42783ebda17f19863aa499eec71fda5aab6cdcc389276b7bf08053795d15890175ca3dc89f6d8d17758c", + "0xa14665846d95d7d5f0b5381502080c822776ec0994ccb1ae1ffbb3f19205ce9c7c9bf9c2d2ca098807ce99f29e4f07a0", + "0xb4884842670a333cb5548a842fa2971881e26b442dfab0b91d6bf3b4cbdf99adbbc9d14fe2bb46872cfcabedae85db30", + "0x94549b01cb47ba16c0cf6f7522c833545397de0b3388c25d03e60132eddada6401682f9ffd8c50d1a61b4d2dde37461f", + "0xa790c9b4cec96e4c54777f3e03cea5769b20382cdcaf1de494bac2b9425eaf453eff643c62ab284cc1af33bbd36013be", + "0xb1b45fd298ed11609aa1ae6c5ac655e365bb451de1b9fc92aad40422ba85c6a454f33b8142acabe55171328c13d92edf", + "0xa74cea9e7096e38327064f058a3cdaa34e6eafaa9c7d58f753c40be67998152380fbd612b9dc0751bda7befcdffcc749", + "0xb18978dfc5efb07b7ef992c7b0cf5d1b4ca551578b1dd13057b7aced8b1deb9f2036e1e3116248a803e922659d206545", + "0x8153c07603cdff6622835a9853b795274390abf7197d7a192193bec44acb43e8cd50b56c11a03f4a2a27124c36974f3d", + "0x86b987f30bb9a37cc91d22dffffcd346ec5773e846a6c2b8f9e03b25ffcae859c470c901c4e29695d325dfe4eee927bd", + "0xaf5e980b9507d10d5269c1a5d02bc16f4f009b663e413ea6a7c655250f3a21c608c12f4002269a05d3779907e7be7d69", + "0xa6f737fab2af9f27bfb8ca87f5fdab6ad51e73ccf074e90576db57b309dfa0a95f9624526dfa4feaef39c388802f2ae9", + "0xb7ed51f699f615f58a7ff4f99d52c4ce7a8d662843c1f4d91f1620fa119b80a0f6848f9fb6c4b9822dc019830e7dfd11", + "0xb71f27f291aa6ef0723ed79c13a1c7a1c40198ffb780a129d9d20e250406bc91f459705b2b6674c9bb412a7b5dd9ff07", + "0x9698cf8f638c3d2916fefa5f28c6050784479f84c2ee76a8aeda7e562630a6ae135b445ec4e29af8588ca5ad94a67f49", + "0x9270aa5030966a9990d8bc71b00b9a7a1d7c1ad8f4c7f78a31b3d7f86467332f21407c74a89ba4f574d723acaf0d2042", + "0xb1b82faceed8e2297cd49cc355471d15ff8dc2ccc78f6944c8f7a75d3ad1629a2e2f1d0a2ff7fa2b3c38cd19839aa5e9", + "0x8a8c4ed49dc9bd961773edf8d41d04385b11bbd3577024639a39319cc7068380236bf73fce0b83e6535bd3f95cef0e65", + "0x8d04ec1e7d148b7e66910ab45a0e6bf409612a3b560bfa784e26f2963152821c646a655cf17a0ce3d4ba4c4ebeeb4a1e", + "0x8e9d707f6186d93accb60813715ed1f6b3001ff6d2f87daf8b906bd0b988c1833b2ccd80dee9bdefb45901e81bb82971", + "0x9762317ca6a5e6fe0b2991e0fa54b5fbf419dd0550d70074957d65cd7ebf79ceba607dd40d709ed635c822b3b4da2cac", + "0x82b53cd9a1eca2f5d3256723dc4b6531ca422bd87bab36243c727d1952db58d7288ab11467305d875d172ce165b1e4a5", + "0xb4dbeafa05c87029ae257bee1ed7603645fab41f6ba7ac8b57ced5b4774a72ba3e671c2433a93acc3c498795b5cccc42", + "0xa916d3ab7f0e7cef294e11c97c910a19c338ad8e615406e6d1c8995b4a19c3b2527100cc6b97a950ec5a4f3f6db7d01a", + "0xb9a785c7123609bdc96f8dd74500c6c77831d9d246f73244de964910b4045ce3242c881271bb1a4bc207d67de7b62e97", + "0xb5f94084f695d0821c472e59c0b761e625b537c8ae3a09f11d9a57259e148cfadba1e43bf22c681b6b32390121cec208", + "0x8f91b36d8570f19a90cf3ed6d5bb25f49a3315ddb566280c091fe2795c4e25ed2c6a1ef8d2669b83f2d7bb78fc8c40f5", + "0x80f27359a73ed8fdd52762f0c7b9f676be2398b1f33c67877261480bf375f975f626c2ca3e7a9f59634db176ed672c98", + "0xb96b91e3d5148ca793edefe4ca776b949c9305acb6f3a3cf87767a684014d2c8f2937c2c672eef8510f17d2da5d51385", + "0x99c4e1ca2cabd4388ea2437dbdf809013d19be9bd09ff6088c8c0cfdb9ecf8fd514391a07b4288dd362434638b8834d9", + "0xb6fdfb812e145f74853892c14f77c29b0c877d8b00055fd084b81360425b3660cd42236ecc853eadb25253e1cd8445c4", + "0xa714af044ef500104576898b9409a9a326ef4286a45c3dae440bd9003fdf689c5f498f24a6f6d18502ce705c60a1cf14", + "0xa9444e201be4a4d8c72119b3d3b13098afee6e5d13c5448fa2e9845cc9188239778f29b208749c960571dfa02b484f05", + "0x91c826a6b8425f93ff395d9fdfa60dbfa655534c36c40a295906578540b9a0e6b94fd8d025b8b8611433022fbbc4fb0b", + "0xa355d76bc3cc48ba07026197130f25a593ec730d2ef0d5d2642bfcad745ecbe5c391324bc2485944060ff3100c952557", + "0xb5f9b5a289a6f9a7252cc1f381c892bdb6836a5998f323ee21ae387936148ad1ad7cc6eca37ecece36404b958ae01e8e", + "0xa3c7ae04a6208851f6cc40ff270047283b95218905396c5dedc490e405061cbefd1251ecf77837d08c5ec1c77d2776ce", + "0xaa02ee387dd2cc7a23cf5cd582da0bc84bb33a7158d76545cbd6e06b26a6f30565dc712d7a8594c29f0529a892138802", + "0x8aff025c841f167fadaf77a68284c355ace41d6df3a9f1e41a6e91454b336f0b69ea34cce495839b642a7c43997a8fd9", + "0x82eccf0b6b4b6460f676d677266451d50f775446df313fc89bdf4c96e082340f6811939d215a54ba0fe30c69b3e43e25", + "0xaf324d871b038ff45a04366817c31d2c1e810359776fb57ac44907c6157004e3705476574e676b405d48a48bfb596f59", + "0x9411dcca93ef5620ce375f379fea5c1017a2dd299e288e77b1ab126273631a299d7436f3bf3c860bf795e5faaaefa804", + "0x934fca809e66f582c690c3778ea49de2e7940c0aeb8d7edad68f2edccdfda853d2c4844abd366fbc2215348935e4b2e2", + "0xa1b1fa4c088418f2609d4dea0656b02a8ee664db25f40d53d8f4b1be89a55e5abecbf2c44c0499874abeb3d3a80acf71", + "0xae6ed7a0ba6280c679b0bf86111afad76fc5d930e9fb199df08134ba807f781d7e0b8b9b2c8c03b02d8cc20dbe949a28", + "0x937d200a72fe4ab8d52f6cb849e322bc5959632b85a93c89744b33e832e8dcf1dddd6ffac0c049b03c105afb8930f7f5", + "0xb4b4a46ebe0c5db16004933c08ad039d365db600a13d68be5346b1c840cce154f56c858874e866de8c3711e755c6e5dd", + "0xafcbcb7170c8caa2b77d2b3388dc2f640aeb9eff55798aeceb6eb6494438be05a2ae82f7034b2d439a45ad31d8c64b07", + "0xa2c676273081b8761f58e0b11306ddb6a4cde3d90e7c47b434468700c5b749932819b01efd7637ca820e10fc28dfb427", + "0xb445715162d834c9ee75ac2ff8932ace91c8242d67926b2a650217e4765e0531c2393c9438a52852d63dbbe2cceaafc5", + "0xa0c0ebdc1480fb238a25fbfc77fae0db6e5e74b91809f0ff20a819e56b8c3141549615d1bd7b99829898f6028e8c86be", + "0xb3d11933e9d1db8ca617934261ed26c6f5ca06ba16369e7541482bf99c4f86520d43fbb10f4effb2fdf3cc70a189fdb5", + "0x888ac610f8fd87a36b5646e1016eaf6dbca04aa0cc43f53a1046d74a658c4d2794606e79fb07fae57cf9d71ed339f4b6", + "0x979818dab00c58435dc0d0d21185943f95819d2a13531abd2d798e1773c4bbd90047f4eebe117868743db75604a50227", + "0xa6fbcd2656e475065fe44e995e8e2b5309b286b787a7597117e7acc3bb159e591a3e7304ef26f567b5720799d8ae1836", + "0xa03f0ac08d2101ec4d99ca1443eea0efa767a65448a8ecd73a7818a99e863a04392bec8c5b8e5192834e8f98d4683f13", + "0xb3c4ea8c6c3ee8aab2873d446ad702000b0e927e0991c9e30d83c6fe62a604efdc3ac92453313ff0d5e0ac6952922366", + "0xab25c857f26830631113d50145e961441b5e35d47b9e57f92466654dffebde43e4f78b0867d20929f97c2888c2f06509", + "0x98950aa5a70ef41f274775f021a284d4d801a2efe2dea38460db8a3a8c08c243836d176e69127c2cd17497b0ca393e9e", + "0xa9698113febfb6d87fcb84bad82ce52d85a279d3a2933bdd179d53cfe8d6c6c68770e549a1e2947e7528a0e82c95d582", + "0x832b504513266259db78478bd1b5a3b0f3bf2c6d25f1013e64bf0cfae9dc23da8ecd25f7f1047d2efb90e5f1d9b4b3cc", + "0xb588bba7bcc0d268ab260d5c1db2122cee7fd01583c7cc27a8ae6b48b29f34c6ea8a6acbb71b9b09c6156ec0a0766142", + "0xa73d2223c7afadc381951a2e9e7bcb7b5c232369f27108c9f3c2ced2dc173e0f49531d0ca527eb142fbb70285307433f", + "0x9152cd6b97bd3278465348dde2095892f46342aed0e3d48675848c05b9aee6ef5ad7fe26e0dcd4ab176532289d40eedd", + "0xa7812a95a43b020721f688dd726356dda8ebe4de79b4f0fdef78615795e29681bff7c6ff710ff5b2d6ae3fd81bdb8507", + "0x83724c16049e9eaae3269ea8e65caa212f0592e0190b47159bb3346208ccb9af3cfe8f6c3176fa566377da1046044ab8", + "0x877634ec37c7dcd3b83705b103c31013697012795f11e8abf88d54bc84f2c060f665f0c3b14ef8087d3c6a8a7982d64f", + "0xb3e53aaacef7a20327bdbba8cd84513534d2e12fd5e1dcf2849f43146e098143b539ebd555623d0ecc46f5ebb4051fca", + "0x952d58ecafca9b7ffc25768ee4f05ce138f0289d72978eb5e5d3b23a0daedcb17478890afdce42e30d924d680e13c561", + "0xa10dcc725f9a261de53dd3133858c126f6aa684cf26d92bce63a70e0ff5fff9610ad00d2b87e598b0a7548cfd1ffe713", + "0xb7bc5d0c6b665d5e6f4d0af1c539d8a636550a327e50a0915c898ac494c42b3100e5fae0074c282d1c5073bf4a5456fb", + "0x8adc330d3b49ddf3ed210166afc944491aaedb28cb4e67472aeb496f66ce59184c842aa583bfb1a26d67d03b85065134", + "0xb2df992a1310936394a1ebca94a7885b4c0a785638f92a7b567cfb4e68504ac5966a9e2b14891d0aa67d035a99e6583a", + "0x96f5da525d140739d19cebb706e2e1e0211edea1f518e040d361d5aca4c80f15be797f58cb4cd3908e4c360c18821243", + "0xb2c0d9173a3d4867c8842e9b58feb1fb47f139f25d1e2332d6b70a85a58811ef99324bf8e52e144e839a4fe2d484e37b", + "0xad95a7631ddb4846d9343d16533493524dfd22e8cbfc280a202343fccee86ab14446f6e7dad9bad9b4185c43fd5f862e", + "0x97f38ab82a51a7a792d459a90e7ea71c5a2f02d58e7d542eb3776d82413932737d9431bd6b74ec2a6a8b980d22d55887", + "0xad4e4c57ec3def5350c37659e8c15bd76d4c13d6de5453493123198dda2c2f40df349f20190e84d740a6b05e0b8f3deb", + "0xa691bc10810d11172a6662e46b6bbc48c351df32f325b319553377f525af44a50aaa02790c915b3a49824aa43f17fff0", + "0xa80ccac79bb4014ee366dbf6e380beb61552bd30ef649d4ec39ab307e4139b7775e776fab30831517674ff3d673566f6", + "0xb11e010b855d80e171705ab9e94364c45998e69d9120e4ca4127049b7a620c2eec1377356e7b877874e767f7c44afef4", + "0x96bfab7777769a1e00ce16ada6667a0d21d709e71bd0371c03002427d138d9172640cdd5c529c710fea74bb9d19270c7", + "0xa5bffd2c30e29633b4ecf637c1e792c0378252e2a99b385a093675940b48de2f262c275332ed4765f4a02467f98e3ddd", + "0x8d11929d67a6bd8a835b80660a89496250c766e713bddb2cd7052d67b92c39a38ce49005d38b4877856c4bef30fb9af4", + "0x8e704597a0dba1dbd1ff8c9755ddac3f334eeeb513fd1c6b78366603ebc1778231deb8e18f2889421f0091e2c24d3668", + "0x904fbb3f78a49e391a0544cf1faa96ba9402cba818359582258d00aff5319e3c214156cff8c603fbc53a45ede22443e9", + "0xaf12ac61eaa9c636481a46fd91903c8a16e7647534fc6fd9baa58ae2998c38ffbd9f03182062311c8adfef0a338aa075", + "0x87f2e544b2993349ab305ab8c3bf050e7764f47d3f3031e26e084e907523d49e1d46c63d0c97b790394f25868e12b932", + "0xa279a7bef6de9d4e183e2bedaf8c553fadfc623a9af8785fe7577cadced02b86e3dab1e97b492d4680c060ea0126abeb", + "0x8ece08667ed826f0a239cea72e11359f7e85d891826292b61d4edbdc672f8342e32c66bec3e6498016b8194168ba0e0d", + "0x90a15162586e991b302427bc0307790a957b53ab0e83c8b2216f6e6302bc496cb256f0f054ff2cccdfe042763de00976", + "0x9966c0413b086a983f031a39080efde41a9fedcaf8e92897ce92e0c573b37981f5ea266b39dc4f4fb926a1bce5e95ad7", + "0x9515be2f65a57e6960d71bfb1917d33f3f6d8b06f8f31df30fc76622949770fea90ff20be525ae3294c56bc91efb7654", + "0x86e71c9b4059dc4fd1ce7e28883e4f579a51449cab5899e371118cdb6afe2758b1485961ca637c299896dea7c732151b", + "0x8695b4ff746d573f8d150f564e69fe51c0726c5d14aa1d72d944f4195e96165eca7eba8cac583fd19d26718b0ce3eb61", + "0x813eecf402151c99c1a55b4c931716e95810fc4e6d117dfc44abbf5ef8dcdf3f971d90d7fa5e5def393681b9584637e0", + "0xa9caf7219eed1db14b7b8f626f20294a3305ed1f6c22f6a26962772c2fa3e50b5234f6d9ba7fa5c3448824c2a15271b3", + "0xb2b2ee20de9b334f2d82cbe0d2e426ca1f35f76218737d0069af9b727a1bfc12d40cf8b88d4afcbeaadf317b7f7ad418", + "0xb853960749521a17ff45f16ac46813d249c4e26e3c08fd33d31ef1ed2b2e157c9cb18bd2454fb5c62690bdd090a48f60", + "0x88772297d2972471b3db71f3ddbf5945a90154768ca49fa6729a5e2299f1795445fb3d4d969d1620e87dca618fbc8a6c", + "0xa2bb783fd13aee993e3efd3a963ebc8a8eacfc8450042f018f2040353de88c71ac784b0898bdff27f606c60a3d5ef2c6", + "0x9210903ac619edca0cb8c288ed6dcc93c472f45182cd6614a8e2390801ddea41d48a4ac04a40e2f0adfd48f91aabe2ea", + "0xa621d00f83260c22db9fa28757ea81dabcc78b10eeaaf58b06b401db6cc7a7d9a6831a16f171ead4e8506d0c46a752ca", + "0xb25c525bf6761a18bbd156ac141df2595940c7b011ed849dbb8ac3a2cd2da6b63ba4755324d70dc14c959deb29fb9ad3", + "0xa35111d0db3e862e1b06249d289e0fc6b110877d254f2ae1604fb21292c227a8b6d87dd17a7b31166038d6860b1bd249", + "0x90bf057309867d95f27637bd10ef15ceb788f07d38aca7ad7920042293d7c4a1a13d4ca1d6db202864d86d20a93e16cf", + "0xa88510e110b268d15dcd163ba1e403e44b656771399ac3a049dcb672a1201e88bf60bdd1d303158888a3d30d616cc0bd", + "0xb33b7e1f765e9cbd5eeb925e69c39b0a9ea3348ab17f1dbb84b66f4a4b3233e28cbdeb0903d6cfe49ec4fc2f27378ff9", + "0xb777da64fa64d9bc3d2d81b088933fce0e5fcc29c15536159c82af3622a2604c2b968991edea7b6882c9e6f76b544203", + "0x8ea598e402a056fd8031fbf3b9e392347999adc1bd5b68c5797a791a787d006e96918c799467af9ac7f5f57eb30b4f94", + "0xb6901a389bf3b3045e679d015c714d24f8bbe6183349b7f6b42f43409a09f0d5bd4b794012257d735c5fdf6d1812554b", + "0xb5866426336d1805447e6efc3f3deb629b945b2781f618df9a2cc48c96020846e9108f9d8507a42ba58d7617cb796c31", + "0xa18ccc6ad1caa8462fa9bec79510689dd2a68d2e8b8e0ddbeb50be4d77728e1d6a18748a11e27edd8d3336c212689a4d", + "0xabbd48c48a271b6b7c95518a9352d01a84fb165f7963b87cdc95d5891119a219571a920f0d9ceedc8f9f0de4ab9deb65", + "0x94a4e5f4d7e49229e435530b12a1ff0e9259a44a4f183fb1fe5b7b59970436e19cf932625f83f7b75702fd2456c3b801", + "0xaf0a6f2a0d0af7fc72e8cb690f0c4b4b57b82e1034cca3d627e8ef85415adec8eb5df359932c570b1ee077c1d7a5a335", + "0x9728025e03114b9e37ed43e9dcba54a2d67f1c99c34c6139e03d4f9c57c9e28b6b27941d9fca4051d32f9b89bec6537b", + "0x941601742d1e1ec8426591733a4f1c13785b0a9b0a6b2275909301a6a3c6c1e2fb1ffa5fdcc08d7fb69f836ae641ced5", + "0xb84b90480defd22f309e294379d1ca324a76b8f0ba13b8496b75a6657494e97d48b0ea5cfdb8e8ac7f2065360e4b1048", + "0x95cc438ee8e370fc857fd36c3679c5660cf6a6c870f56ef8adf671e6bf4b25d1dbad78872cc3989fdfe39b29fc30486d", + "0x8aafba32e4a30cad79c5800c8709241b4041b0c13185ea1aa9bc510858709870b931d70b5d9a629f47579b161f1d8af7", + "0x865b0155d9013e80cba57f204c21910edbd4d15e53ae4fee79992cb854dc8b8a73f0a9be92f74893e30eb70f270511bc", + "0xb9a49ce58d40b429ac7192cdbf76da31300efc88c827b1e441dd5bdb2f1c180d57808c48992492a2dc5231008629159f", + "0x8d1438b10f6cd996494d4c7b5a0841617ec7cf237c9e0956eac04fda3f9ded5110ec99776b816e3c78abd24eb4a9c635", + "0xaf2dd18211bb8a3e77c0a49d5773da6e29e4e6fa6632a6eeb56c4be233f6afe81655d977932548de2be16567c54ffbd7", + "0x92b92443f44464f2b48002a966664a4267eae559fa24051983bcf09d81bed5bcc15cb6ff95139d991707697a5d0cc1ab", + "0xa1864a2bac0c0dd5b2fb1a79913dd675fe0a5ae08603a9f69d8ca33268239ac7f2fed4f6bf6182a4775683cb9ccd92a8", + "0x948e8f1cf5bd594c5372845b940db4cb2cb5694f62f687952c73eb77532993de2e2d7d974a2ced58730d12c8255c30a2", + "0xaa825c08284fa74a99fcfc473576e8a9788277f72f8c87f29be1dd41229c286c2753ff7444c753767bd8180226763dfc", + "0x8384d8d51415e1a4d6fe4324504e958c1b86374cc0513ddf5bcbffabb3edcf4b7d401421e5d1aa9da9010f07ef502677", + "0x8b8223a42585409041d8a6e3326342df02b2fe0bcc1758ff950288e8e4677e3dc17b0641286eaf759a68e005791c249c", + "0xa98a98cc2fb14e71928da7f8ce53ab1fb339851c9f1f4bceb5f1d896c46906bd027ef5950ca53b3c8850407439efedd4", + "0x866f44d2e35a4dbffe6cd539b6ef5901924061e37f9a0e7007696fb23526379c9b8d095b417effe1eecda698de744dcb", + "0x91774f44bf15edafdf43957fdf254682a97e493eb49d0779c745cb5dbe5d313bf30b372edd343f6d2220475084430a2e", + "0xab52fc3766c499a5f5c838210aada2c3bcc1a2ec1a82f5227d4243df60809ee7be10026642010869cfbf53b335834608", + "0xa0e613af98f92467339c1f3dc4450b7af396d30cefd35713388ccd600a3d7436620e433bf294285876a92f2e845b90d0", + "0x8a1b5ca60a9ae7adc6999c2143c07a855042013d93b733595d7a78b2dc94a9daa8787e2e41b89197a0043343dbd7610f", + "0xae7e4557bc47b1a9af81667583d30d0da0d4a9bb0c922450c04ec2a4ae796c3f6b0ede7596a7a3d4e8a64c1f9ee8ff36", + "0x8d4e7368b542f9f028309c296b4f84d4bde4837350cf71cfe2fa9d4a71bce7b860f48e556db5e72bc21cf994ffdf8e13", + "0xaf6ed1fbff52dd7d67d6a0edfa193aa0aab1536979d27dba36e348759d3649779f74b559194b56e9378b41e896c4886f", + "0xa069ba90a349ac462cac0b44d02c52a4adf06f40428aef5a2ddff713de31f991f2247fc63426193a3ea1b1e50aa69ded", + "0x8750f5f4baf49a5987470f5022921108abe0ead3829ddef00e61aedd71f11b1cdd4be8c958e169440b6a8f8140f4fbf9", + "0xa0c53cefc08a8d125abd6e9731bd351d3d05f078117ff9c47ae6b71c8b8d8257f0d830481f941f0c349fc469f01c9368", + "0x94eea18c5ed056900c8285b05ba47c940dff0a4593b627fdd8f952c7d0122b2c26200861ef3e5c9688511857535be823", + "0x8e1b7bd80d13460787e5060064c65fbcdac000c989886d43c7244ccb5f62dcc771defc6eb9e00bae91b47e23aeb9a21f", + "0xb4b23f9dd17d12e145e7c9d3c6c0b0665d1b180a7cfdf7f8d1ab40b501c4b103566570dca2d2f837431b4bf698984cad", + "0x847a47c6b225a8eb5325af43026fb9ef737eede996257e63601f80302092516013fde27b93b40ff8a631887e654f7a54", + "0x9582d7afb77429461bd8ebb5781e6390a4dde12a9e710e183581031ccfacd9067686cfaf47584efaafeb1936eae495cc", + "0x8e4fd5dbd9002720202151608f49ef260b2af647bd618eb48ebeceeb903b5d855aa3e3f233632587a88dc4d12a482df9", + "0x87b99fe6a9c1d8413a06a60d110d9e56bb06d9f0268dc12e4ab0f17dd6ca088a16ade8f4fb7f15d3322cbe7bfd319ae1", + "0xb562d23002ed00386db1187f519018edd963a72fca7d2b9fcaab9a2213ac862803101b879d1d8ac28d1ccae3b4868a05", + "0xb4cc8b2acacf2ce7219a17af5d42ce50530300029bc7e8e6e2a3c14ff02a5b33f0a7fecb0bb4a7900ea63befa854a840", + "0x9789f0fe18d832ff72df45befa7cabf0a326b42ada3657d164c821c35ac7ed7b2e0eba3d67856e8c387626770059b0c3", + "0x986c6fe6771418549fa3263fa8203e48552d5ecb4e619d35483cb4e348d849851f09692821c9233ae9f16f36979c30c2", + "0xa9160182a9550c5756f35cea1fe752c647d1b64a12426a0b5b8d48af06a12896833ec5f5d9b90185764db0160905ca01", + "0x82614dbd89d54c1e0af4f6ffe8710e6e871f57ef833cbcb3d3d7c617a75ec31e2a459a89ebb716b18fc77867ff8d5d47", + "0x8fc298ffba280d903a7873d1b5232ce0d302201957226cddff120ffe8df9fee34e08420302c6b301d90e3d58f10beeb9", + "0x898da9ac8494e31705bdf684545eee1c99b564b9601877d226d0def9ec67a20e06f8c8ba2a5202cc57a643487b94af19", + "0x88218478d51c3ed2de35b310beedf2715e30208c18f046ee65e824f5e6fd9def921f6d5f75fd6dde47fa670c9520f91a", + "0x89703ae7dff9b3bc2a93b44cdbab12c3d8496063a3c658e21a7c2078e4c00be0eecae6379ee8c400c67c879748f1d909", + "0xa44d463477dece0d45abb0ebb5f130bfb9c0a3bbcd3be62adf84a47bbd6938568a89bc92a53ca638ff1a2118c1744738", + "0x95df2b4d392143ee4c39ad72f636d0ed72922de492769c6264015776a652f394a688f1d2b5cf46077d01fda8319ba265", + "0xaa989867375710ed07ad6789bfb32f85bdc71d207f6f838bd3bde9da5a169325481ac326076b72358808bd5c763ba5bb", + "0xb859d97d0173920d16bc01eb7d3ddd47273daac72f86c4c30392f8de05fee643e8d6aa8bebdbc5c2d89037bc68a8a105", + "0xb0249ec97411fa39aa06b3d9a6e04bbbcd5e99a7bc527273b6aa95e7ae5f437b495385adaefa4327231562d232c9f822", + "0x8209e156fe525d67e1c83ec2340d50d45eba5363f617f2e5738117cdcc4a829c4cc37639afd7745cbe929c66754fd486", + "0x99fd2728ceb4c62e5f0763337e6d28bf11fbe5df114217f002bc5cd3543c9f62a05a8a41b2e02295360d007eaab796a6", + "0x902ebc68b8372feeaf2e0b40bd6998a0e17981db9cc9d23f932c34fbcc680292a0d8adcea2ad3fb2c9ed89e7019445c2", + "0x8b5653f4770df67f87cb68970555b9131c3d01e597f514e0a399eec8056e4c5a7deed0371a27b3b2be426d8e860bf9f2", + "0x8f5af27fdc98a29c647de60d01b9e9fd0039013003b44ba7aa75a4b9c42c91feb41c8ae06f39e22d3aed0932a137affa", + "0x81babb9c1f5bcc0fd3b97d11dd871b1bbd9a56947794ff70ab4758ae9850122c2e78d53cb30db69ece23538dc4ee033e", + "0xb8b65d972734f8ecae10dd4e072fa73c9a1bf37484abcfa87e0d2fcecac57294695765f63be87e1ba4ec0eb95688403a", + "0xb0fe17d0e53060aef1947d776b06ab5b461a8ef41235b619ca477e3182fadaf9574f12ffc76420f074f82ac4a9aa7071", + "0xae265c0b90bf064d7a938e224cb1cd3b7eca3e348fbc4f50a29ac0930a803b96e0640992354aa14b303ea313cb523697", + "0x8bc10ffde3224e8668700a3450463ab460ec6f198e1deb016e2c9d1643cc2fe1b377319223f41ffeb0b85afd35400d40", + "0x8d5113b43aea2e0cc6f8ec740d6254698aff7881d72a6d77affd6e6b182909b4de8eb5f524714b5971b418627f15d218", + "0xae2ef0a401278b7b5d333f0588773ec62ead58807cdee679f72b1af343c1689c5f314989d9e6c9369f8da9ce76979db6", + "0xb9c1cb996a78d4f7793956daaa8d8825dd43c4c37877bc04026db4866144b1bf37aa804d2fe0a63c374cf89e55e9069f", + "0xa35f73851081f6540e536a24a28808d478a2bb1fd15ee7ff61b1562e44fbafc0004b9c92c9f96328d546b1287e523e48", + "0x82007f34e3383c628c8f490654369744592aa95a63a72be6e90848ad54f8bc2d0434b62f92a7c802c93017214ecf326e", + "0x9127db515b1ed3644c64eaf17a6656e6663838fed4c6612a444a6761636eaaeb6a27b72d0e6d438c863f67b0d3ec25c5", + "0x984c9fcc3deccf83df3bbbb9844204c68f6331f0f8742119ba30634c8c5d786cd708aa99555196cf6563c953816aec44", + "0xa0f9daf900112029474c56ddd9eb3b84af3ed2f52cd83b4eb34531cf5218e7c58b3cab4027b9fc17831e1b6078f3bf4a", + "0x90adbcc921369023866a23f5cea7b0e587d129ad71cab0449e2e2137838cea759dec27b0b922c59ac4870ef6146ea283", + "0x8c5650b6b9293c168af98cf60ad35c945a30f5545992a5a8c05d42e09f43b04d370c4d800f474b2323b4269281ca50f8", + "0x868d95be8b34a337b5da5d886651e843c073f324f9f1b4fbd1db14f74aba6559449f94c599f387856c5f8a7bc83b52a1", + "0x812df0401d299c9e95a8296f9c520ef12d9a3dd88749b51eab8c1b7cc97961608ab9fc241a7e2888a693141962c8fd6d", + "0xabda319119d8a4d089393846830eee19d5d6e65059bf78713b307d0b4aad245673608b0880aa31c27e96c8d02eff39c0", + "0x887f11ae9e488b99cb647506dcaa5e2518b169ee70a55cd49e45882fe5bfb35ffaf11feb2bf460c17d5e0490b7c1c14d", + "0xb36b6e9f95ffff917ca472a38fa7028c38dc650e1e906e384c10fe38a6f55e9b84b56ffa3a429d3b0c3e2cf8169e66a9", + "0xa0450514d20622b7c534f54be3260bab8309632ca21c6093aa0ccc975b8eed33a922cbcc30a730ccc506edf9b188a879", + "0x87cfaf7bcd5d26875ca665ac45f9decd3854701b0443332da0f9b213e69d6f5521ae0217ec375489cd4fad7b4babf724", + "0x842ad67c1baf7a9d4504c10c5c979ce0a4d1b86a263899e2b5757407c2adcdcf7ed58173ad9d156d84075ef8798cb1c4", + "0xac1a05755fe4d3fb2ab5b951bafe65cca7c7842022ca567b32cddf7741782cbf8c4990c1dd4ea05dc087a4712844aebb", + "0xa000c8cecc4fddeb926dc8dd619952bc51d00d7c662e025f973387a3fc8b1ef5c7c10b6a62e963eb785e0ec04cb1ffbe", + "0x8a573c9986dbeb469547dfd09f60078eab252d8ec17351fe373a38068af046b0037967f2b3722fa73ed73512afd038d2", + "0xb8dff15dff931f58ba05b6010716c613631d7dd9562ae5138dbec966630bcdb0e72552e4eefc0351a6a6b7912d785094", + "0x990e81fd459433522e8b475e67e847cb342c4742f0dbf71acc5754244ccd1d9ff75919168588d8f18b8aea17092dd2a4", + "0xb012f8644da2113bef7dd6cdc622a55cfa0734bd267b847d11bba2e257a97a2a465c2bb616c240e197ff7b23e2ce8d8e", + "0xa659bd590fde467766e2091c34a0b070772f79380be069eef1afecc470368a95afd9eed6520d542c09c0d1a9dca23bd0", + "0xb9239f318b849079477d1cf0a60a3d530391adacd95c449373da1c9f83f03c496c42097c3f9aca10c1b9b3dbe5d98923", + "0x851e9a6add6e4a0ee9994962178d06f6d4fbc0def97feef1ba4c86d3bcf027a59bafa0cf25876ca33e515a1e1696e5cc", + "0x803b9c5276eed78092de2f340b2f0d0165349a24d546e495bd275fe16f89a291e4c74c22fdee5185f8fce0c7fbced201", + "0x95915654ca4656d07575168fb7290f50dc5dcbbcdf55a44df9ec25a9754a6571ab8ca8a159bc27d9fa47c35ffd8f7ffd", + "0x88f865919764e8e765948780c4fdd76f79af556cd95e56105d603c257d3bfb28f11efca1dfb2ce77162f9a5b1700bac8", + "0xb1233131f666579b4cc8b37cfa160fc10551b1ec33b784b82685251464d3c095cdde53d0407c73f862520aa8667b1981", + "0xa91115a15cf4a83bda1b46f9b9719cfba14ffb8b6e77add8d5a0b61bea2e4ea8ce208e3d4ed8ca1aab50802b800e763a", + "0x93553b6c92b14546ae6011a34600a46021ce7d5b6fbfcda2a70335c232612205dbe6bfb1cc42db6d49bd4042c8919525", + "0x8c2a498e5d102e80c93786f13ccf3c9cab7f4c538ccf0aee8d8191da0dbca5d07dff4448383e0cf5146f6d7e629d64f8", + "0xa66ab92c0d2c07ea0c36787a86b63ee200499527c93b9048b4180fc77e0bb0aa919f4222c4bec46eeb3f93845ab2f657", + "0x917e4fc34081a400fc413335fdf5a076495ae19705f8542c09db2f55fa913d6958fa6d711f49ad191aec107befc2f967", + "0x940631a5118587291c48ac8576cdc7e4a904dd9272acb79407a7d3549c3742d9b3669338adbc1386724cc17ee0cc1ca3", + "0xae23ae3a531900550671fd10447a35d3653c5f03f65b0fdffe092844c1c95d0e67cab814d36e6388db5f8bd0667cd232", + "0xae545727fca94fd02f43e848f0fbbb1381fd0e568a1a082bf3929434cc73065bfbc9f2c840b270dda8cc2e08cd4d44b0", + "0x8a9bc9b90e98f55007c3a830233c7e5dc3c4760e4e09091ff30ee484b54c5c269e1292ce4e05c303f6462a2a1bd5de33", + "0xa5a2e7515ce5e5c1a05e5f4c42f99835f6fde14d47ecb4a4877b924246038f5bc1b91622e2ff97ed58737ed58319acfa", + "0x8fa9f5edf9153618b72b413586e10aaa6c4b6e5d2d9c3e8693ca6b87804c58dc4bf23a480c0f80cb821ebc3cf20ea4fc", + "0x925134501859a181913aadac9f07f73d82555058d55a7d5aaa305067fbd0c43017178702facc404e952ea5cfd39db59b", + "0x8b5ab1d9b5127cb590d6bddbf698ffe08770b6fc6527023d6c381f39754aecc43f985c47a46be23fe29f6ca170249b44", + "0xaa39c6b9626354c967d93943f4ef09d637e13c505e36352c385b66e996c19c5603b9f0488ad4014bb5fc2e051b2876cc", + "0x8e77399c6e9cb8345002195feb7408eb571e6a81c0418590d2d775af7414fc17e61fe0cd37af8e737b59b89c849d3a28", + "0xa0150aeca2ddc9627c7ea0af0dd4426726583389169bc8174fc1597cc8048299cc594b22d234a4e013dff7232b2d946c", + "0x98659422ef91f193e6104b09ff607d1ed856bb6baed2a6386c9457efbc748bd1bf436573d80465ebc54f8c340b697ea5", + "0x8d6fb015898d3672eb580e1ffdf623fc4b23076664623b66bfb18f450d29522e8cb9c90f00d28ccf00af34f730bff7ac", + "0x996a8538efa9e2937c1caad58dc6564e5c185ada6cdcef07d5ec0056eb1259b0e4cef410252a1b5dbaee0da0b98dac91", + "0xaa0ae2548149d462362a33f96c3ce9b5010ebf202602e81e0ef77e22cfc57ecf03946a3076b6171bea3d3dc9681187d7", + "0xa5ce876b29f6b89050700df46d679bed85690daf7bad5c0df65e6f3bde5673e6055e6c29a4f4dcb82b93ccecf3bad9cc", + "0x81d824bb283c2f55554340c3514e15f7f1db8e9e95dd60a912826b1cccb1096f993a6440834dad3f2a5de70071b4b4b5", + "0x914e7291da286a89dfc923749da8f0bf61a04faa3803d6d10633261a717184065dcc4980114ad852e359f79794877dd9", + "0xae49dc760db497c8e834510fe89419cc81f33fd2a2d33de3e5e680d9a95a0e6a3ccbdf7c0953beeb3d1caf0a08b3e131", + "0xb24f527d83e624d71700a4b238016835a2d06f905f3740f0005105f4b2e49fc62f7e800e33cdc900d805429267e42fc0", + "0xb03471ecaa7a3bf54503347f470a6c611e44a3cee8218ad3fcad61d286cfb7bb6a1113dad18475ec3354a71fcc4ec1e2", + "0x881289b82b30aff4c8f467c2a25fced6064e1eece97c0de083e224b21735da61c51592a60f2913e8c8ba4437801f1a83", + "0xb4ce59c0fc1e0ecad88e79b056c2fd09542d53c40f41dea0f094b7f354ad88db92c560b9aeb3c0ef48137b1a0b1c3f95", + "0xa1ffb30eb8ef0e3ea749b5f300241ebe748ed7cf480e283dfcda7380aa1c15347491be97e65bc96bdf3fe62d8b74b3ae", + "0xb8954a826c59d18c6bfab24719f8730cc901868a95438838cd61dac468a2d79b1d42f77284e86e3382bf4f2a22044927", + "0x818e7e7c59b6b5e22b3c2c19c163f2e787f2ff3758d395a4da02766948935eb44413c3ddd2bf45804a3c19744aa332f3", + "0xa29556e49866e4e6f01d4f042eed803beeda781462884a603927791bd3750331a11bc013138f3270c216ab3aa5d39221", + "0xb40885fa0287dc92859b8b030c7cca4497e96c387dcfe6ed13eb7f596b1eb18fb813e4ae139475d692f196431acb58fe", + "0x89cd634682fd99ee74843ae619832780cf7cd717f230ea30f0b1821caf2f312b41c91f459bdba723f780c7e3eed15676", + "0xb48c550db835750d45a7f3f06c58f8f3bf8766a441265ca80089ead0346f2e17cbb1a5e843557216f5611978235e0f83", + "0x90936ee810039783c09392857164ab732334be3a3b9c6776b8b19f5685379c623b1997fb0cdd43af5061d042247bc72f", + "0xa6258a6bae36525794432f058d4b3b7772ba6a37f74ef1c1106c80a380fc894cbeac4f340674b4e2f7a0f9213b001afd", + "0x8f26943a32cf239c4e2976314e97f2309a1c775777710393c672a4aab042a8c6ee8aa9ac168aed7c408a436965a47aeb", + "0x820f793573ca5cc3084fe5cef86894c5351b6078df9807d4e1b9341f9d5422dd29d19a73b0843a14ad63e8827a75d2da", + "0xa3c4fca786603cd28f2282ba02afe7cf9287529e0e924ca90d6cdfd1a3912478ebb3076b370ee72e00df5517134fe17f", + "0x8f3cdabd0b64a35b9ee9c6384d3a8426cc49ae6063632fb1a56a0ae94affa833955f458976ff309dafd0b2dd540786ae", + "0x945a0630cd8fa111cfd776471075e5d2bbe8eb7512408b5c79c8999bfaeca6c097f988fb1c38fa9c1048bac2bca19f2e", + "0x8a7f6c4e0ba1920c98d0b0235b4dda73b631f511e209b10c05c550f51e91b4ba3893996d1562f04ac7105a141464e0e9", + "0xab3c13d8b78203b4980412edc8a8f579e999bf79569e028993da9138058711d19417cf20b477ef7ed627fa4a234c727a", + "0x82b00d9a3e29ed8d14c366f7bb25b8cfe953b7be275db9590373a7d8a86ea927d56dc3070a09ef7f265f6dd99a7c896e", + "0xb6e48a282de57949821e0c06bc9ba686f79e76fb7cbf50ea8b4651ccd29bc4b6da67efea4662536ba9912d197b78d915", + "0xa749e9edcba6b4f72880d3f84a493f4e8146c845637009f6ff227ff98521dbbe556a3446340483c705a87e40d07364bc", + "0xb9b93c94bd0603ce5922e9c4c29a60066b64a767b3aed81d8f046f48539469f5886f14c09d83b5c4742f1b03f84bb619", + "0xafa70b349988f85ed438faafa982df35f242dd7869bda95ae630b7fd48b5674ef0f2b4d7a1ca8d3a2041eff9523e9333", + "0xa8e7e09b93010982f50bd0930842898c0dcd30cdb9b123923e9d5ef662b31468222fc50f559edc57fcfdc597151ebb6e", + "0x8ce73be5ac29b0c2f5ab17cae32c715a91380288137d7f8474610d2f28d06d458495d42b9cb156fb1b2a7dfdcc437e1c", + "0x85596c1d81f722826d778e62b604eb0867337b0204c9fae636399fa25bb81204b501e5a5912654d215ec28ff48b2cb07", + "0x96ff380229393ea94d9d07e96d15233f76467b43a3e245ca100cbecbdbb6ad8852046ea91b95bb03d8c91750b1dfe6e1", + "0xb7417d9860b09f788eb95ef89deb8e528befcfa24efddbc18deaf0b8b9867b92361662db49db8121aeea85a9396f64fd", + "0x97b07705332a59cdba830cc8490da53624ab938e76869b2ce56452e696dcc18eb63c95da6dffa933fb5ffb7585070e2d", + "0x971f757d08504b154f9fc1c5fd88e01396175b36acf7f7abcfed4fff0e421b859879ed268e2ac13424c043b96fbe99fc", + "0xb9adb5d3605954943a7185bddf847d4dbe7bafe970e55dc0ec84d484967124c26dd60f57800d0a8d38833b91e4da476a", + "0xb4856741667bb45cae466379d9d6e1e4191f319b5001b4f963128b0c4f01819785732d990b2f5db7a3452722a61cd8cc", + "0xa81ec9f2ab890d099fb078a0c430d64e1d06cbbe00b1f140d75fc24c99fe35c13020af22de25bbe3acf6195869429ba5", + "0x99dcea976c093a73c08e574d930d7b2ae49d7fe43064c3c52199307e54db9e048abe3a370b615798b05fe8425a260ba0", + "0xa1f7437c0588f8958b06beb07498e55cd6553429a68cd807082aa4cc031ab2d998d16305a618b3d92221f446e6cd766d", + "0x806e4e0958e0b5217996d6763293f39c4f4f77016b3373b9a88f7b1221728d14227fce01b885a43b916ff6c7a8bc2e06", + "0x8e210b7d1aff606a6fc9e02898168d48ec39bc687086a7fe4be79622dd12284a5991eb53c4adfe848251f20d5bfe9de0", + "0x82810111e10c654a6c07cbfd1aff66727039ebc3226eef8883d570f25117acf259b1683742f916ac287097223afc6343", + "0x92f0e28cca06fd543f2f620cc975303b6e9a3d7c96a760e1d65b740514ccd713dc7a27a356a4be733570ca199edd17ba", + "0x900810aa4f98a0d6e13baf5403761a0aeb6422249361380c52f98b2c79c651e3c72f7807b5b5e3a30d65d6ff7a2a9203", + "0xb0740bfefea7470c4c94e85185dbe6e20685523d870ff3ef4eb2c97735cef41a6ab9d8f074a37a81c35f3f8a7d259f0e", + "0xaf022e98f2f418efbbe2de6fefb2aa133c726174f0f36925a4eafd2c6fd6c744edb91386bafb205ce13561de4294f3a6", + "0x95e4592e21ba97e950abb463e1bc7b0d65f726e84c06a98eb200b1d8bfc75d4b8cff3f55924837009e88272542fd25ec", + "0xb13bd6b18cd8a63f76c9831d547c39bbd553bda66562c3085999c4da5e95b26b74803d7847af86b613a2e80e2f08caae", + "0xa5625658b474a95aba3e4888c57d82fb61c356859a170bc5022077aa6c1245022e94d3a800bf7bd5f2b9ab1348a8834e", + "0xa097ee9e6f1d43e686df800c6ce8cfc1962e5a39bb6de3cf5222b220a41b3d608922dae499bce5c89675c286a98fdabd", + "0x94230ba8e9a5e9749cd476257b3f14a6bf9683e534fb5c33ca21330617533c773cb80e508e96150763699ad6ecd5aee7", + "0xb5fea7e1f4448449c4bc5f9cc01ac32333d05f464d0ed222bf20e113bab0ee7b1b778cd083ceae03fdfd43d73f690728", + "0xa18a41a78a80a7db8860a6352642cdeef8a305714543b857ca53a0ee6bed70a69eeba8cfcf617b11586a5cc66af4fc4f", + "0x85d7f4b3ff9054944ac80a51ef43c04189d491e61a58abed3f0283d041f0855612b714a8a0736d3d25c27239ab08f2ec", + "0xb1da94f1e2aedd357cb35d152e265ccfc43120825d86733fa007fc1e291192e8ff8342306bef0c28183d1df0ccec99d0", + "0x852893687532527d0fbeea7543ac89a37195eadab2f8f0312a77c73bdeed4ad09d0520f008d7611539425f3e1b542cfd", + "0x99e3bd4d26df088fc9019a8c0b82611fd4769003b2a262be6b880651d687257ded4b4d18ccb102cba48c5e53891535e4", + "0x98c407bc3bbc0e8f24bedf7a24510a5d16bce1df22940515a4fbdacd20d06d522ef9405f5f9b9b55964915dd474e2b5c", + "0x80de0a12f917717c6fc9dc3ccc9732c28bae36cff4a9f229d5eaf0d3e43f0581a635ba2e38386442c973f7cb3f0fdfa7", + "0x94f9615f51466ae4bb9c8478200634b9a3d762d63f2a16366849096f9fc57f56b2e68fe0ca5d4d1327a4f737b3c30154", + "0xa3dcbe16499be5ccb822dfcd7c2c8848ba574f73f9912e9aa93d08d7f030b5076ca412ad4bf6225b6c67235e0ab6a748", + "0x98f137bf2e1aea18289750978feb2e379054021e5d574f66ca7b062410dcfe7abb521fab428f5b293bbe2268a9af3aa4", + "0x8f5021c8254ba426f646e2a15b6d96b337a588f4dfb8cbae2d593a4d49652ca2ada438878de5e7c2dbbd69b299506070", + "0x8cc3f67dd0edcdb51dfd0c390586622e4538c7a179512f3a4f84dd7368153a28b1cf343afd848ac167cb3fcaa6aee811", + "0x863690f09ac98484d6189c95bc0d9e8f3b01c489cb3f9f25bf7a13a9b6c1deaf8275ad74a95f519932149d9c2a41db42", + "0x8494e70d629543de6f937b62beca44d10a04875bd782c9a457d510f82c85c52e6d34b9c3d4415dd7a461abbcc916c3c4", + "0x925b5e1e38fbc7f20371b126d76522c0ea1649eb6f8af8efb389764ddcf2653775ef99a58a2dcf1812ce882964909798", + "0x94d0494dcc44893c65152e7d42f4fb0dc46af5dc5674d3c607227160447939a56d9f9ea2b3d3736074eef255f7ec7566", + "0xb0484d33f0ef80ff9b9d693c0721c77e518d0238918498ddf71f14133eb484defb9f9f7b9083d52bc6d6ba2012c7b036", + "0x8979e41e0bb3b501a7ebbd024567ce7f0171acfea8403a530fe9e791e6e859dfbd60b742b3186d7cf5ab264b14d34d04", + "0xaf93185677d39e94a2b5d08867b44be2ba0bb50642edca906066d80facde22df4e6a7a2bd8b2460a22bdf6a6e59c5fdd", + "0x90f0ef0d7e7ab878170a196da1b8523488d33e0fde7481f6351558b312d00fa2b6b725b38539063f035d2a56a0f5e8f1", + "0xa9ca028ccb373f9886574c2d0ea5184bc5b94d519aa07978a4814d649e1b6c93168f77ae9c6aa3872dd0eea17968ec22", + "0x82e7aa6e2b322f9f9c180af585b9213fb9d3ad153281f456a02056f2d31b20d0f1e8807ff0c85e71e7baca8283695403", + "0xaffce186f842c547e9db2dffc0f3567b175be754891f616214e8c341213cbf7345c9ecd2f704bb0f4b6eba8845c8d8a7", + "0xab119eb621fade27536e98c6d1bc596388bb8f5cad65194ea75c893edbe6b4d860006160f1a9053aea2946bd663e5653", + "0x99cd2c1c38ead1676657059dc9b43d104e8bd00ae548600d5fc5094a4d875d5b2c529fac4af601a262045e1af3892b5e", + "0xb531a43b0714cc638123487ef2f03dfb5272ff399ff1aa67e8bc6a307130d996910fb27075cbe53050c0f2902fc32ffe", + "0x923b59ac752c77d16b64a2d0a5f824e718460ef78d732b70c4c776fecc43718ecfaf35f11afbb544016232f445ecab66", + "0xa53439cd05e6e1633cdce4a14f01221efcd3f496ac1a38331365c3cadc30013e5a71600c097965927ee824b9983a79cb", + "0x8af976ffab688d2d3f9e537e2829323dda9abf7f805f973b7e0a01e25c88425b881466dee37b25fda4ea683a0e7b2c03", + "0x92e5f40230a9bfbb078fa965f58912abb753b236f6a5c28676fb35be9b7f525e25428160caeaf0e3645f2be01f1a6599", + "0x8c4e7b04e2f968be527feba16f98428508a157b7b4687399df87666a86583b4446a9f4b86358b153e1660bb80bd92e8b", + "0x97cd622d4d8e94dceb753c7a4d49ea7914f2eb7d70c9f56d1d9a6e5e5cc198a3e3e29809a1d07d563c67c1f8b8a5665a", + "0x967bfa8f411e98bec142c7e379c21f5561f6fd503aaf3af1a0699db04c716c2795d1cb909cccbcb917794916fdb849f1", + "0xb3c18a6caa5ca2be52dd500f083b02a4745e3bcaed47b6a000ce7149cee4ed7a78d2d7012bf3731b1c15c6f04cbd0bd1", + "0xb3f651f1f84026f1936872956a88f39fcfe3e5a767233349123f52af160f6c59f2c908c2b5691255561f0e70620c8998", + "0xae23b59dc2d81cec2aebcaaf607d7d29cf588f0cbf7fa768c422be911985ca1f532bb39405f3653cc5bf0dcba4194298", + "0xa1f4da396f2eec8a9b3252ea0e2d4ca205f7e003695621ae5571f62f5708d51ca3494ac09c824fca4f4d287a18beea9a", + "0xa036fa15e929abed7aac95aa2718e9f912f31e3defd224e5ed379bf6e1b43a3ad75b4b41208c43d7b2c55e8a6fedca72", + "0x80e8372d8a2979ee90afbdb842624ace72ab3803542365a9d1a778219d47f6b01531185f5a573db72213ab69e3ffa318", + "0xaf68b5cdc39e5c4587e491b2e858a728d79ae7e5817a93b1ea39d34aec23dea452687046c8feae4714def4d0ed71da16", + "0xb36658dfb756e7e9eec175918d3fe1f45b398679f296119cd53be6c6792d765ef5c7d5afadc5f3886e3f165042f4667f", + "0xad831da03b759716f51099d7c046c1a8e7bf8bb45a52d2f2bfd769e171c8c6871741ef8474f06e2aca6d2b141cf2971f", + "0x8bae1202dde053c2f59efc1b05cb8268ba9876e4bd3ff1140fa0cc5fa290b13529aede965f5efdff3f72e1a579efc9cc", + "0x86344afbc9fe077021558e43d2a032fcc83b328f72948dba1a074bb1058e8a8faec85b1c019fc9836f0d11d2585d69c8", + "0x831d1fc7aa28f069585d84c46bdc030d6cb12440cfaae28098365577fc911c4b8f566d88f80f3a3381be2ec8088bf119", + "0x899de139797ac1c8f0135f0656f04ad4f9b0fa2c83a264d320eb855a3c0b9a4907fc3dc01521d33c07b5531e6a997064", + "0x855bc752146d3e5b8ba7f382b198d7dc65321b93cdfc76250eabc28dba5bbf0ad1be8ccda1adf2024125107cb52c6a6e", + "0xaf0aeccab48eb35f8986cabf07253c5b876dd103933e1eee0d99dc0105936236b2a6c413228490ed3db4fa69aab51a80", + "0xae62e9d706fbf535319c909855909b3deba3e06eaf560803fa37bce3b5aab5ea6329f7609fea84298b9da48977c00c3b", + "0x823a8d222e8282d653082d55a9508d9eaf9703ce54d0ab7e2b3c661af745a8b6571647ec5bd3809ae6dddae96a220ea7", + "0xa4c87e0ea142fc287092bc994e013c85e884bc7c2dde771df30ca887a07f955325c387b548de3caa9efa97106da8176a", + "0xb55d925e2f614f2495651502cf4c3f17f055041fa305bb20195146d896b7b542b1e45d37fa709ca4bfc6b0d49756af92", + "0xb0ebe8947f8c68dc381d7bd460995340efcbb4a2b89f17077f5fde3a9e76aef4a9a430d1f85b2274993afc0f17fdbead", + "0x8baaa640d654e2652808afd68772f6489df7cad37b7455b9cd9456bdddae80555a3f84b68906cc04185b8462273dcfc9", + "0xadd9aa08f827e7dc292ac80e374c593cd40ac5e34ad4391708b3db2fe89550f293181ea11b5c0a341b5e3f7813512739", + "0x909e31846576c6bdd2c162f0f29eea819b6125098452caad42451491a7cde9fd257689858f815131194200bca54511f4", + "0xabc4b34098db10d71ce7297658ef03edfa7377bd7ed36b2ffbab437f8fd47a60e2bcfbc93ff74c85cfce74ca9f93106c", + "0x857dbecc5879c1b952f847139484ef207cecf80a3d879849080758ef7ac96acfe16a11afffb42daf160dc4b324279d9b", + "0xaab0b49beecbcf3af7c08fbf38a6601c21061bed7c8875d6e3c2b557ecb47fd93e2114a3b09b522a114562467fcd2f7d", + "0x94306dec35e7b93d43ed7f89468b15d3ce7d7723f5179cacc8781f0cf500f66f8c9f4e196607fd14d56257d7df7bf332", + "0x9201784d571da4a96ef5b8764f776a0b86615500d74ec72bc89e49d1e63a3763b867deca07964e2f3914e576e2ca0ded", + "0xaabe1260a638112f4280d3bdea3c84ce3c158b81266d5df480be02942cecf3de1ac1284b9964c93d2db33f3555373dcc", + "0x8ef28607ca2e0075aa07de9af5a0f2d0a97f554897cab8827dfe3623a5e9d007d92755d114b7c390d29e988b40466db9", + "0x87a9b1b097c3a7b5055cd9cb0c35ba6251c50e21c74f6a0bca1e87e6463efc38385d3acc9d839b4698dfa2eb4cb7a2ef", + "0xaee277e90d2ffce9c090295c575e7cd3bafc214d1b5794dd145e6d02d987a015cb807bd89fd6268cd4c59350e7907ee2", + "0x836ad3c9324eaa5e022e9835ff1418c8644a8f4cd8e4378bd4b7be5632b616bb6f6c53399752b96d77472f99ece123cd", + "0x8ffffdb67faa5f56887c834f9d489bb5b4dab613b72eac8abf7e4bcb799ccd0dbd88a2e73077cadf7e761cb159fb5ec5", + "0x9158f6cd4f5e88e6cdb700fddcbc5a99b2d31a7a1b37dce704bd9dd3385cca69607a615483350a2b1153345526c8e05d", + "0xa7ff0958e9f0ccff76742fc6b60d2dd91c552e408c84172c3a736f64acb133633540b2b7f33bc7970220b35ce787cd4e", + "0x8f196938892e2a79f23403e1b1fb4687a62e3a951f69a7874ec0081909eb4627973a7a983f741c65438aff004f03ba6f", + "0x97e3c1981c5cdb0a388f1e4d50b9b5b5f3b86d83417831c27b143698b432bb5dba3f2e590d6d211931ed0f3d80780e77", + "0x903a53430b87a7280d37816946245db03a49e38a789f866fe00469b7613ee7a22d455fb271d42825957282c8a4e159d9", + "0xb78955f686254c3994f610e49f1c089717f5fb030da4f9b66e9a7f82d72381ba77e230764ab593335ff29a1874848a09", + "0x938b6d04356b9d7c8c56be93b0049d0d0c61745af7790edf4ef04e64de2b4740b038069c95be5c91a0ba6a1bb38512a9", + "0xa769073b9648fe21bc66893a9ef3b8848d06f4068805a43f1c180fdd0d37c176b4546f8e5e450f7b09223c2f735b006f", + "0x863c30ebe92427cdd7e72d758f2c645ab422e51ecef6c402eb1a073fd7f715017cd58a2ad1afe7edccdf4ff01309e306", + "0xa617b0213d161964eccfc68a7ad00a3ee4365223b479576e887c41ef658f846f69edf928bd8da8785b6e9887031f6a57", + "0xa699834bf3b20d345082f13f360c5f8a86499e498e459b9e65b5a56ae8a65a9fcb5c1f93c949391b4795ef214c952e08", + "0x9921f1da00130f22e38908dd2e44c5f662ead6c4526ebb50011bc2f2819e8e3fca64c9428b5106fa8924db76b7651f35", + "0x98da928be52eb5b0287912fd1c648f8bbda00f5fd0289baf161b5a7dbda685db6ad6bdc121bc9ffa7ed6ae03a13dbee3", + "0x927b91d95676ff3c99de1312c20f19251e21878bfb47ad9f19c9791bc7fb9d6f5c03e3e61575c0760180d3445be86125", + "0xb8e4977a892100635310dfcb46d8b74931ac59ae687b06469b3cee060888a3b6b52d89de54e173d9e1641234754b32b1", + "0x98f6fd5f81ca6e2184abd7a3a59b764d4953d408cec155b4e5cf87cd1f6245d8bdd58b52e1e024e22903e85ae15273f1", + "0x909aaacbbfe30950cf7587faa190dc36c05e3c8131749cc21a0c92dc4afc4002275762ca7f66f91aa751b630ad3e324d", + "0x91712141592758f0e43398c075aaa7180f245189e5308e6605a6305d01886d2b22d144976b30460d8ce17312bb819e8f", + "0x947d85cb299b189f9116431f1c5449f0f8c3f1a70061aa9ebf962aa159ab76ee2e39b4706365d44a5dbf43120a0ac255", + "0xb39eced3e9a2e293e04d236976e7ee11e2471fe59b43e7b6dd32ab74f51a3d372afee70be1d90af017452ec635574e0e", + "0x8a4ba456491911fc17e1cadcbb3020500587c5b42cf6b538d1cb907f04c65c168add71275fbf21d3875e731404f3f529", + "0x8f6858752363e2a94c295e0448078e9144bf033ccd4d74f4f6b95d582f3a7638b6d3f921e2d89fcd6afd878b12977a9d", + "0xb7f349aa3e8feb844a56a42f82b6b00f2bfe42cab19f5a68579a6e8a57f5cf93e3cdb56cbbb9163ab4d6b599d6c0f6aa", + "0xa4a24dc618a6b4a0857fb96338ac3e10b19336efc26986e801434c8fdde42ca8777420722f45dfe7b67b9ed9d7ce8fb1", + "0xaafe4d415f939e0730512fc2e61e37d65c32e435991fb95fb73017493014e3f8278cd0d213379d2330b06902f21fe4e1", + "0x845cc6f0f0a41cc6a010d5cb938c0ef8183ff5ed623b70f7ea65a8bdbc7b512ea33c0ee8b8f31fdf5f39ec88953f0c1e", + "0x811173b4dd89d761c0bdffe224cd664ef303c4647e6cf5ef0ed665d843ed556b04882c2a4adfc77709e40af1cfdea40b", + "0x93ba1db7c20bfba22da123b6813cb38c12933b680902cef3037f01f03ab003f76260acc12e01e364c0d0cf8d45fca694", + "0xb41694db978b2cf0f4d2aa06fcfc4182d65fb7c9b5e909650705f779b28e47672c47707d0e5308cd680c5746c37e1bc7", + "0xa0e92c4c5be56a4ccf1f94d289e453a5f80e172fc90786e5b03c1c14ce2f3c392c349f76e48a7df02c8ae535326ea8fe", + "0x96cbeb1d0693f4f0b0b71ad30def5ccc7ad9ebe58dbe9d3b077f2ac16256cde10468875e4866d63e88ce82751aaf8ef6", + "0x935b87fd336f0bf366046e10f7c2f7c2a2148fa6f53af5607ad66f91f850894527ecec7d23d81118d3b2ee23351ed6ed", + "0xb7c2c1fa6295735f6b31510777b597bc8a7bfb014e71b4d1b5859be0d8d64f62a1587caafc669dfe865b365eb27bd94f", + "0xb25d93af43d8704ffd53b1e5c16953fd45e57a9a4b7acfcfa6dd4bf30ee2a8e98d2a76f3c8eba8dc7d08d9012b9694c6", + "0xb5a005cd9f891e33882f5884f6662479d5190b7e2aec1aa5a6d15a8cb60c9c983d1e7928e25e4cf43ec804eaea1d97b0", + "0x93f9f0725a06e4a0fb83892102b7375cf5438b5ebc9e7be5a655f3478d18706cf7dbb1cd1adcee7444c575516378aa1b", + "0x900d7cbf43fd6ac64961287fe593c08446874bfc1eb09231fc93de858ac7a8bca496c9c457bced5881f7bf245b6789e0", + "0x90c198526b8b265d75160ef3ed787988e7632d5f3330e8c322b8faf2ac51eef6f0ce5a45f3b3a890b90aecf1244a3436", + "0xb499707399009f9fe7617d8e73939cb1560037ad59ac9f343041201d7cc25379df250219fd73fa012b9ade0b04e92efa", + "0x94415f6c3a0705a9be6a414be19d478181d82752b9af760dda0dbd24a8ff0f873c4d89e61ad2c13ebf01de55892d07fa", + "0x90a9f0b9f1edb87751c696d390e5f253586aae6ebfc31eb3b2125d23877a497b4aa778de8b11ec85efe49969021eaa5a", + "0xa9942c56506e5cd8f9289be8205823b403a2ea233ba211cf72c2b3827064fd34cd9b61ff698a4158e7379891ca4120d8", + "0x83bb2ee8c07be1ab3a488ec06b0c85e10b83a531758a2a6741c17a3ccfa6774b34336926a50e11c8543d30b56a6ac570", + "0x8a08a3e5ebe10353e0b7fff5f887e7e25d09bb65becf7c74a03c60c166132efaada27e5aea242c8b9f43b472561ae3ed", + "0x957c7a24cefaa631fe8a28446bc44b09a3d8274591ade53ba489757b854db54820d98df47c8a0fbee0e094f8ad7a5dc4", + "0xb63556e1f47ed3ee283777ed46b69be8585d5930960d973f8a5a43508fc56000009605662224daec2de54ea52a8dcd82", + "0xabed2b3d16641f0f459113b105f884886d171519b1229758f846a488c7a474a718857323c3e239faa222c1ab24513766", + "0x882d36eed6756d86335de2f7b13d753f91c0a4d42ef50e30195cc3e5e4f1441afa5ff863022434acb66854eda5de8715", + "0xa65ea7f8745bb8a623b44e43f19158fd96e7d6b0a5406290f2c1348fc8674fbfc27beb4f724cc2b217c6042cb82bc178", + "0xa038116a0c76af090a069ca289eb2c3a615b96093efacfe68ea1610890b291a274e26b445d34f414cfec00c333906148", + "0x90294f452f8b80b0a47c3bcb6e30bdd6854e3b01deaf93f5e82a1889a4a1036d17ecb59b48efa7dc41412168d7a523dd", + "0x88faf969c8978a756f48c6114f7f33a1ca3fd7b5865c688aa9cd32578b1f7ba7c06120502f8dc9aee174ecd41597f055", + "0x8883763b2762dfff0d9be9ac19428d9fd00357ac8b805efda213993152b9b7eb7ba3b1b2623015d60778bffda07a724d", + "0xa30a1a5a9213636aa9b0f8623345dc7cf5c563b906e11cc4feb97d530a1480f23211073dcb81105b55193dcde5a381d2", + "0xb45ee93c58139a5f6be82572d6e14e937ef9fcbb6154a2d77cb4bf2e4b63c5aabc3277527ecf4e531fe3c58f521cc5e3", + "0xac5a73e4f686978e06131a333f089932adda6c7614217fcaf0e9423b96e16fd73e913e5e40bf8d7800bed4318b48d4b1", + "0xb6c1e6cdd14a48a7fe27cd370d2e3f7a52a91f3e8d80fb405f142391479f6c6f31aa5c59a4a0fdc9e88247c42688e0cf", + "0xab1760530312380152d05c650826a16c26223960fc8e3bf813161d129c01bac77583eff04ce8678ff52987a69886526b", + "0xa4252dffae7429d4f81dfaeeecc48ab922e60d6a50986cf063964f282e47407b7e9c64cf819da6f93735de000a70f0b2", + "0x94c19f96d5ecf4a15c9c5a24598802d2d21acbbd9ee8780b1bc234b794b8442437c36badc0a24e8d2cff410e892bb1d2", + "0x89fafe1799cf7b48a9ea24f707d912fccb99a8700d7287c6438a8879f3a3ca3e60a0f66640e31744722624139ba30396", + "0xb0108405df25cf421c2f1873b20b28552f4d5d1b4a0bf1c202307673927931cbd59f5781e6b8748ddb1206a5ec332c0b", + "0xaa0f0e7d09f12b48f1e44d55ec3904aa5707e263774126e0b30f912e2f83df9eb933ca073752e6b86876adaf822d14ba", + "0xb0cbe8abb58876d055c8150d9fdbde4fea881a517a2499e7c2ea4d55c518a3c2d00b3494f6a8fd1a660bfca102f86d2a", + "0xb1ef80ec903bac55f58b75933dc00f1751060690fd9dfb54cf448a7a4b779c2a80391f5fda65609274bd9e0d83f36141", + "0x8b52e05b1845498c4879bb12816097be7fc268ce1cf747f83a479c8e08a44159fc7b244cf24d55aca06dccf0b97d11e1", + "0xb632a2fc4fdb178687e983a2876ae23587fd5b7b5e0bb8c0eb4cfe6d921a2c99894762e2aaccdc5da6c48da3c3c72f6c", + "0x953ef80ab5f74274ae70667e41363ae6e2e98ccbd6b7d21f7283f0c1cafb120338b7a8b64e7c189d935a4e5b87651587", + "0xb929cfd311017c9731eed9d08d073f6cf7e9d4cd560cddd3fdcb1149ab20c6610a7674a66a3616785b13500f8f43ee86", + "0x870fb0d02704b6a328e68721fb6a4b0f8647681bfcb0d92ec3e241e94b7a53aecc365ed384e721c747b13fbf251002f1", + "0x979501159833a8ba5422ed9b86f87b5961711f5b474d8b0e891373fe2d0b98ff41a3a7a74a8b154615bb412b662a48be", + "0xb20f9c13cdeceef67f877b3878839ef425f645b16a69c785fe38f687c87a03b9de9ae31ac2edb1e1dd3a9f2c0f09d35d", + "0x8c7705ed93290731b1cf6f3bf87fc4d7159bb2c039d1a9f2246cda462d9cdf2beef62d9f658cfeea2e6aef7869a6fc00", + "0xaa439eb15705ad729b9163daee2598d98a32a8a412777c0d12fd48dc7796d422227a014705e445cc9d66f115c96bbc24", + "0xa32307e16f89749fe98b5df1effef0429801c067e0d8067794e56b01c4fef742ad5e7ab42a1a4cc4741808f47a0b7cb8", + "0xb31e65c549003c1207258a2912a72f5bad9844e18f16b0773ea7af8ff124390eb33b2f715910fc156c104572d4866b91", + "0x85608d918ed7b08a0dc03aee60ea5589713304d85eee7b4c8c762b6b34c9355d9d2e192575af0fd523318ae36e19ae1c", + "0xa6497dbaf0e7035160b7a787150971b19cf5ba272c235b0113542288611ebecefa2b22f08008d3f17db6a70a542c258d", + "0x87862adb1ac0510614ab909457c49f9ec86dc8bdf0e4682f76d2739df11f6ffcfb59975527f279e890d22964a1fba9b6", + "0x8717ac3b483b3094c3b642f3fafe4fbafc52a5d4f2f5d43c29d9cfe02a569daee34c178ee081144494f3a2ca6e67d7b1", + "0x855100ac1ec85c8b437fdd844abaa0ca4ac9830a5bdd065b68dafb37046fcf8625dd482dc0253476926e80a4c438c9ec", + "0xae74821bf265ca3c8702c557cf9ef0732ede7ef6ed658283af669d19c6f6b6055aca807cf2fa1a64785ec91c42b18ae5", + "0x812a745b1419a306f7f20429103d6813cbdea68f82ff635ac59da08630cd61bda6e0fa9a3735bfd4378f58ad179c1332", + "0x867dbbfe0d698f89451c37ca6d0585fd71ee07c3817e362ef6779b7b1d70b27c989cdd5f85ac33a0498db1c4d14521fe", + "0x84db735d3eb4ff7f16502dccc3b604338c3a4a301220ad495991d6f507659db4b9f81bba9c528c5a6114bcdba0160252", + "0xaadc83d1c4e5e32bf786cfb26f2f12a78c8024f1f5271427b086370cdef7a71d8a5bf7cd7690bae40df56c38b1ad2411", + "0xa27860eb0caaea37298095507f54f7729d8930ac1929de3b7a968df9737f4c6da3173bda9d64ff797ed4c6f3a1718092", + "0xa3cdcaa74235c0440a34171506ed03d1f72b150d55904ce60ec7b90fcd9a6f46f0e45feab0f9166708b533836686d909", + "0xb209a30bdac5c62e95924928f9d0d0b4113ebb8b346d7f3a572c024821af7f036222a3bd38bd8efd2ee1dbf9ac9556cd", + "0x83c93987eff8bc56506e7275b6bef0946672621ded641d09b28266657db08f75846dcbde80d8abc9470e1b24db4ca65b", + "0x800c09b3ee5d0251bdaef4a82a7fe8173de997cc1603a2e8df020dd688a0c368ad1ebef016b35136db63e774b266c74c", + "0x93fb52de00d9f799a9bce3e3e31aaf49e0a4fc865473feb728217bd70f1bc8a732ec37ac3582bf30ab60e8c7fdf3cb8d", + "0xa1aff6b4a50d02f079a8895c74443539231bfdf474600910febf52c9151da7b31127242334ac63f3093e83a047769146", + "0x8c4532d8e3abb5f0da851138bfa97599039bcd240d87bbdf4fd6553b2329abb4781074b63caf09bc724ceb4d36cb3952", + "0x8bd9b0ae3da5acda9eb3881172d308b03beec55014cd73b15026299541c42fd38bab4983a85c06894ebb7a2af2a23d4c", + "0x979441e7f5a0e6006812f21b0d236c5f505bb30f7d023cb4eb84ec2aa54a33ac91d87ece704b8069259d237f40901356", + "0xa1c6d2d82e89957d6a3e9fef48deb112eb00519732d66d55aa0f8161e19a01e83b9f7c42ac2b94f337dcc9865f0da837", + "0x97a0b8e04e889d18947d5bf77d06c25bbd62b19ce4be36aaa90ddbeafd93a07353308194199ba138efaadf1b928cd8d2", + "0x822f7fbe9d966b8ec3db0fc8169ab39334e91bf027e35b8cc7e1fe3ead894d8982505c092f15ddfe5d8f726b360ac058", + "0xa6e517eedd216949e3a10bf12c8c8ddbfde43cddcd2c0950565360a38444459191bdbc6c0af0e2e6e98bc6a813601c6d", + "0x858b5f15c46c074adb879b6ba5520966549420cb58721273119f1f8bc335605aeb4aa6dbe64aae9e573ca7cc1c705cdc", + "0xb5191bb105b60deb10466d8114d48fb95c4d72036164dd35939976e41406dff3ee3974c49f00391abfad51b695b3258c", + "0xb1b375353ed33c734f4a366d4afad77168c4809aff1b972a078fd2257036fd6b7a7edad569533abf71bc141144a14d62", + "0xa94c502a9cdd38c0a0e0187de1637178ad4fa0763887f97cc5bdd55cb6a840cb68a60d7dbb7e4e0e51231f7d92addcff", + "0x8fe2082c1b410486a3e24481ae0630f28eb5b488e0bb2546af3492a3d9318c0d4c52db1407e8b9b1d1f23a7ffbaf260a", + "0xb73fe7aa2b73f9cae6001af589bf8a9e73ea2bb3bb01b46743e39390c08d8e1be5e85a3d562857a9c9b802b780c78e6d", + "0x8e347f51330ae62275441ccd60f5ac14e1a925a54ced8a51893d956acc26914df1bb8595385d240aa9b0e5ada7b520ea", + "0x8dc573d6357c0113b026a0191a5807dbe42dcd2e19772d14b2ca735e1e67c70e319ef571db1f2a20e62254ed7fb5bcd6", + "0xa5dacbe51549fe412e64af100b8b5eba5ec2258cc2a7c27a34bc10177d1894baf8707886d2f2ef438f077596a07681e9", + "0x8349153c64961d637a5ff56f49003cb24106de19a5bbcf674016a466bfbe0877f5d1e74ccb7c2920665ef90a437b1b7e", + "0x96ad35429d40a262fdc8f34b379f2e05a411057d7852c3d77b9c6c01359421c71ef8620f23854e0f5d231a1d037e3a0d", + "0xb52385e40af0ed16e31c2154d73d1517e10a01435489fc801fbea65b92b3866ab46dab38d2c25e5fb603b029ae727317", + "0x8e801c7a3e8fa91d9c22ebd3e14a999023a7b5beea13ec0456f7845425d28c92452922ca35ec64012276acb3bbc93515", + "0xa8630870297d415e9b709c7f42aa4a32210b602f03a3015410123f0988aea2688d8bcfc6d07dc3602884abbf6199b23f", + "0x8cd518392e09df2a3771a736f72c05af60efc030d62dbbb9cd68dc6cbbe1fb0854eb78b6ed38337010eb1bb44a5d5d30", + "0x921aa4c66590f6c54bf2fa2b324f08cbe866329cc31f6e3477f97f73e1a1721d5eb50ed4eacc38051fe9eda76ba17632", + "0xa37e595cb63524cb033c5540b6343c3a292569fc115e813979f63fe1a3c384b554cecc2cae76b510b640fe3a18800c81", + "0xb0bb57e4e31ae3ce9f28cef158ed52dabfad5aa612f5fcc75b3f7f344b7cec56b989b5690dacd294e49c922d550ee36b", + "0xa3c618ce4d091e768c7295d37e3f9b11c44c37507ae1f89867441f564bf0108f67bf64b4cf45d73c2afc17a4dc8b2c68", + "0x999e6650eda5455e474c22a8c7a3fd5b547ec2875dc3043077ad70c332f1ccd02135e7b524fcbf3621d386dec9e614fa", + "0xb018f080888dec3c2ca7fcfeb0d3d9984699b8435d8823079fc9e1af4ca44e257fbe8da2f6f641ee6152b5c7110e3e3c", + "0xa2bcd4bcd9b40c341e9bba76b86481842f408166c9a7159205726f0776dcb7f15a033079e7589699e9e94ce24b2a77fd", + "0xb03de48f024a520bb9c54985ca356fd087ca35ac1dd6e95168694d9dae653138c9755e18d5981946a080e32004e238fe", + "0xa6c1a54973c0c32a410092441e20594aa9aa3700513ed90c8854956e98894552944b0b7ee9edf6e62e487dc4565baa2f", + "0x845d7abf577c27c4c1fafc955dcad99a1f2b84b2c978cfe4bd3cd2a6185979491f3f3b0ec693818739ed9184aba52654", + "0x9531bcfc0d3fcd4d7459484d15607d6e6181cee440ba6344b12a21daa62ff1153a4e9a0b5c3c33d373a0a56a7ad18025", + "0xa0bbf49b2dd581be423a23e8939528ceaae7fb8c04b362066fe7d754ca2546304a2a90e6ac25cdf6396bf0096fae9781", + "0xa1ec264c352e34ed2bf49681b4e294ffea7d763846be62b96b234d9a28905cdece4be310a56ec6a00fc0361d615b547c", + "0x87c575e85b5dfbfd215432cb355a86f69256fff5318e8fda457763ac513b53baa90499dc37574bdfad96b117f71cb45e", + "0x9972edfdeec56897bef4123385ee643a1b9dc24e522752b5a197ce6bd2e53d4b6b782b9d529ca50592ee65b60e4c9c3c", + "0xb8bcf8d4ab6ad37bdd6ad9913a1ba0aba160cb83d1d6f33a8524064a27ba74a33984cc64beeee9d834393c2636ff831a", + "0x83082b7ec5b224422d0ff036fbb89dc68918e6fde4077dfc0b8e2ee02595195ecadb60c9ab0ad69deb1bac9be75024fa", + "0x8b061fce6df6a0e5c486fd8d8809f6f3c93bd3378a537ff844970492384fb769d3845d0805edd7f0fcd19efabf32f197", + "0xb9597e717bb53e6afae2278dbc45d98959c7a10c87c1001ed317414803b5f707f3c559be6784119d08f0c06547ec60b1", + "0xb9d990fd7677dd80300714cfd09336e7748bbf26f4bb0597406fcb756d8828c33695743d7a3e3bd6ddf4f508149610ef", + "0xb45f7d2b00ceea3bf6131b230b5b401e13a6c63ba8d583a4795701226bf9eb5c88506f4a93219ac90ccbceef0bfd9d49", + "0xa8ccaa13ca7986bc34e4a4f5e477b11ae91abb45c8f8bf44a1f5e839289681495aba3daa8fb987e321d439bbf00be789", + "0xae0f59f7a94288a0ead9a398fdd088c2f16cccb68624de4e77b70616a17ddf7406ca9dc88769dadeb5673ff9346d6006", + "0xb28e965dcc08c07112ae3817e98f8d8b103a279ad7e1b7c3de59d9dbd14ab5a3e3266775a5b8bbf0868a14ae4ab110f1", + "0x84751c1a945a6db3df997fcbde9d4fe824bc7ba51aa6cb572bb5a8f9561bef144c952198a783b0b5e06f9dd8aa421be8", + "0xa83586db6d90ef7b4fa1cbda1de1df68ee0019f9328aded59b884329b616d888f300abb90e4964021334d6afdea058fd", + "0x8fcea1ce0abf212a56c145f0b8d47376730611e012b443b3d1563498299f55cbcbe8cbd02f10b78224818bb8cbbd9aaa", + "0x8d66c30a40c34f23bae0ea0999754d19c0eb84c6c0aa1b2cf7b0740a96f55dd44b8fee82b625e2dd6c3182c021340ac6", + "0x92c9b35076e2998f1a0f720d5a507a602bd6bd9d44ffc29ede964044b17c710d24ce3c0b4a53c12195de93278f9ec83b", + "0xa37d213913aff0b792ee93da5d7e876f211e10a027883326d582ad7c41deebdfce52f86b57d07868918585908ebd070a", + "0xa03995b4c6863f80dd02ed0169b4f1609dc48174ec736de78be1cdff386648426d031f6d81d1d2a7f2c683b31e7628c0", + "0xb08b628d481302aa68daf0fa31fd909064380d62d8ed23a49037cb38569058e4c16c80e600e84828d37a89a33c323d1f", + "0xa0ee2e2dd8e27661d7b607c61ac36f590909aa97f80bdfd5b42463ca147b610ac31a9f173cbecdd2260f0f9ea9e56033", + "0x967162fba8b69ffce9679aac49214debb691c6d9f604effd6493ce551abacbe4c8cc2b0ccee6c9927c3d3cfbdcb0be11", + "0x8deab0c5ed531ce99dadb98b8d37b3ff017f07438bc6d50840577f0f3b56be3e801181333b4e8a070135f9d82872b7f2", + "0xb1bfa00ec8c9365b3d5b4d77a718cb3a66ed6b6cf1f5cf5c5565d3aa20f63d3c06bb13d47d2524e159debf81325ba623", + "0x90109780e53aeacd540b9fe9fc9b88e83c73eaf3507e2b76edc67f97a656c06a8a9e1ec5bce58bfd98b59a6b9f81b89d", + "0x88a1009a39a40421fdcc0ffc3c78a4fbace96a4e53420b111218091223494e780a998ebecf5a0abd0243e1523df90b28", + "0x90b77146711ee8d91b0346de40eca2823f4e4671a12dad486a8ec104c01ef5ee7ab9bd0398f35b02b8cb62917455f8b3", + "0xb262c5e25f24ae7e0e321b66fdb73b3bf562ded566a2d6a0152cf8bafb56138d87b6a917a82f5ace65efc73cfc177d81", + "0xae65a438c7ea46c82925b5ec5f71314558ca5146f5d90311431d363cfeac0537223c02cbb50fa6535d72fc2d949f4482", + "0x8984208bfc193a6ef4720cc9d40c17f4be2f14595ef887980f2e61fa6927f9d73c00220937013b46290963116cbe66ac", + "0xa8f33a580508f667fac866456dce5d9246562188ad0f568eb1a2f28cf9fd3452dd20dc613adb1d07a5542319a37ecf1a", + "0xaedadd705fc086d8d2b647c62e209e2d499624ab37c8b19af80229f85e64a6e608d9cd414cb95ae38cf147d80ec3f894", + "0xae28077a235cd959f37dc3daedc3706f7a7c2ffe324e695f2e65f454bf5a9fc27b10149a6268ebfaa961ad67bb9b75d7", + "0xa234c7f5a5e0e30f2026d62657bd92d91a9907ec6a2177f91383f86abb919778121ff78afb8f52c473fe6fb731018b52", + "0x816a2ea7826b778f559a815267b6c6eb588558391c0a675d61bb19470d87489ba6c1e2486ea81dd5420a42ee7c35a8de", + "0x9218b61948c14234f549c438105ae98367ef6b727ad185f17ad69a6965c044bb857c585b84d72ef4c5fb46962974eed7", + "0xa628031217a0b1330b497351758cf72d90fb87d8bdf542ea32092e14ff32d5ef4ca700653794bb78514d4b0edfd7a8d7", + "0xab4e977141be639a78eb9ed17366f9642f9335873aca87cce2bae0dddc161621d0e23264a54a7395ae706d748c690ee9", + "0xb1538c4edff59bcf5668557d994bac77d508c757e382512c4368c1ded4242a41f6200b73fe8809fb528a7a0c1fc96feb", + "0x965caabe5590e2ff8c9f1048bbdda2817e7a2847e287944bfab40d94cb48389441ac42ff3a7b559760bfab42ff82e1e0", + "0xa64b7484d22c4b8047c7a8ef54dc88cb8d110c61ef28ba853821b61e87d318b2b4226f7f0d1f3cdf086a0e1666d0212c", + "0x8915ab7e41d974eef9a651b01c2521392e8899e6ab91c22aeee61605c78fb2b052399ba1d03473aa9cfb52d1a8ba4257", + "0x8dd26875d4a1716db2f75a621d01e971983267770e2da92399aecf08f74af1f7e73643ac6f0a9b610eda54e5460f70ed", + "0x83dabcb84c9cbce67e1a24ecbfa4473766b9519588b22288edbaa29aca34cefd9884f7310e7771f8f7a7cbced2e7eea0", + "0x956be00c67987fb4971afca261065a7f6fcef9fb6b1fcb1939f664bbc5b704223253ebfda48565624a68fb249742c2cf", + "0xa374824a24db1ab298bee759cee8d8260e0ac92cd1c196f896600fd57484a9f9be1912ded01203976ac4fab66c0e5091", + "0xa225f2ed0de4e06c500876e68e0c58be49535885378584a1442aae2140c38d3ca35c1bc41936a3baf8a78e7ab516f790", + "0x8e79c8de591a6c70e2ef2de35971888ab0ca6fd926fdb6e845fb4b63eb3831c5839f084201b951984f6d66a214b946b8", + "0x91babc849a9e67ab40192342c3d0d6ce58798101cb85c9bd7fc0ac4509ffc17b5ea19e58045cf1ca09ec0dee0e18c8f9", + "0x8b4897fc2aef5bbe0fa3c3015ca09fc9414fdb2315f54dbecc03b9ae3099be6c0767b636b007a804d8b248c56e670713", + "0x8f63ba42e7459ea191a8ad18de0b90b151d5acbf4751e2c790e7d8328e82c20de518132d6290ff3c23d2601f21c1558e", + "0xa1a035dc9b936587a16665ea25646d0bb2322f81960d9b6468c3234c9137f7c2b1e4f0b9dbe59e290a418007b0e7a138", + "0x81c4904c08f7bb2ac7b6d4ac4577f10dd98c318f35aac92fc31bab05eceb80a0556a7fc82614b8d95357af8a9c85a829", + "0x8c40e44e5e8e65f61e0a01f79057e1cb29966cc5074de790ea9c60454b25d7ea2b04c3e5decb9f27f02a7f3d3cb7014f", + "0xad8709e357094076eb1eb601539b7bcc37247a25fbc6ada5f74bb88b1b371917c2a733522190f076c44e9b8e2ae127fb", + "0x92d43cd82c943fd71b8700977244436c696df808c34d4633f0624700a3445f3ecc15b426c850f9fb60b9aa4708f2c7c0", + "0xb2cb8080697d1524a6dcb640b25e7255ae2e560613dbd27beaa8c5fc5c8d2524b7e6edd6db7ad0bb8a4e2e2735d4a6f7", + "0x971ca6393d9e312bfb5c33955f0325f34946d341ff7077151f0bcafd2e6cbd23e2ad62979454f107edc6a756a443e888", + "0xb6a563f42866afcee0df6c6c2961c800c851aa962d04543541a3cedeb3a6a2a608c1d8391cf405428cd40254e59138f3", + "0x986bd17bad9a8596f372a0185f7f9e0fb8de587cd078ae40f3cd1048305ba00954aff886b18d0d04640b718ea1f0d5a3", + "0xae32dbccfb7be8e9165f4e663b26f57c407f96750e0f3a5e8e27a7c0ca36bc89e925f64ddd116263be90ace4a27872c4", + "0x83725445ec8916c7c2dd46899241a03cf23568ac63ae2d34de3bce6d2db0bc1cfd00055d850b644a059fb26c62ed3585", + "0xa83f7e61c05b1c6797a36ad5ded01bf857a838147f088d33eb19a5f7652b88e55734e8e884d1d1103a50d4393dfcd7a8", + "0xaa010b4ec76260d88855347df9eaf036911d5d178302063d6fd7ecad009e353162177f92240fe5a239acd1704d188a9d", + "0xa88f4ba3cf4aff68ec1e3ded24622d4f1b9812350f6670d2909ea59928eb1d2e8d66935634d218aeac6d1a0fc6cae893", + "0xb819112b310b8372be40b2752c6f08426ef154b53ef2814ae7d67d58586d7023ffa29d6427a044a3b288e0c779866791", + "0xb5d1e728de5daf68e63b0bb1dee5275edae203e53614edeeeefff0f2f7ac4281191a33b7811de83b7f68111361ef42e1", + "0x953fb3ddc6f78045e53eaacfd83c5c769d32608b29391e05612e4e75725e54e82ad4960fbef96da8b2f35ba862968a3e", + "0x936471136fb2c1b3bb986a5207a225a8bf3b206a1a9db54dc3029e408e78c95bfb7539b67006d269c09df6354d7254ac", + "0xac353364b413cae799b13d7dc6fa09c322b47e60b9333e06499155e22d913929b92a45a0ad04ba90b29358f7b792d864", + "0xa0177419ead02ba3f0755a32eee3fd23ec81a13c01eab462f3b0af1e2dba42f81b47b2c8b1a90d8cec5a0afa371b7f11", + "0xb009eeb5db80d4244c130e6e3280af120917bb6fcebac73255c09f3f0c9da3b2aa718cd92d3d40e6b50737dbd23461aa", + "0xb8a43426c3746c1a5445535338c6a10b65474b684a2c81cd2f4b8ebecc91a57e2e0687df4a40add015cd12e351bbb3eb", + "0x94ff3698a6ac6e7df222675a00279c0ea42925dc6b748e3e74a62ea5d1e3fd70d5ab2d0c20b83704d389dd3a6063cf1a", + "0x90e4142e7ce15266144153e21b9893d3e14b3b4d980e5c87ce615ecd27efac87d86fa90354307857f75d7ebaeffe79ef", + "0xa5fd82c3f509ec9a36d72ba204a16f905e1e329f75cfd18aaa14fb00a212d21f3fac17e1a8e3bc5691ab0d07f8ec3cd0", + "0x962e6bfd75ea554f304a5fee1123e5bf2e048ccd3b401716b34c52740384579188ac98bc0d91269fc814de23f4b2dd34", + "0xb50b4e45c180badf9cd842cd769f78f963e077a9a4c016098dc19b18210580ad271ae1ba86de7760dd2e1f299c13f6a0", + "0x84cf08858d08eca6acc86158ffda3fbe920d1d5c04ac6f1fc677760e46e66599df697397373959acf319c31e47db115c", + "0xa697a38ba21caa66b7739ed0e74fe762a3da02144b67971fcad28c1132d7b83e0ac062cc71479f99e2219086d7d23374", + "0xad1f6d01dd7f0de814fe5fbb6f08c1190ff37f4a50754d7b6291fc547c0820506ea629aabacf749fec9c1bbfda22d2d0", + "0xb11fd7f8c120d8a370a223a1adc053a31bef7454b5522b848dec82de5482308fc68fdaf479875b7a4bc3fc94e1ea30eb", + "0x93ecf90ebfc190f30086bcaeee18cda972073a8469cf42a3b19f8c1ec5419dff2d6a5cc8ef412ccd9725b0f0a5f38f88", + "0x911f25aaa5260b56b3009fa5e1346a29f13a085cf8a61b36b2d851791f7bcf8456840eccbfc23797b63ecd312e2d5e12", + "0xa52f17a8b2db66c98291020b1db44ab23827e1790e418e078d1316185df6aa9f78292f43a12cd47131bd4b521d134060", + "0x9646fca10bf7401e91d9a49753c72f3ecb142f5ed13aba2c510a6c5ccb8d07b8e8d1581fc81321ad5e3996b6d81b5538", + "0xaa1da4a5665b91b62dda7f71bb19c8e3f6f49cc079d94fcd07b3604a74547e8334efa5a202822d0078158056bbda2822", + "0xa2432ae5feeaf38252c28aa491e92a68b47d5b4c6f44c1b3d7f3abc2f10b588f64a23c3357e742a0f5e4f216e7ca5827", + "0x83c7b47735cd0ef80658a387f34f259940096ebb9464c67919b278db4109fea294d09ea01a371b79b332cff6777c116d", + "0xa740a2959e86e413c62d6bdd1bc27efe9596ee363c2460535eab89ba1715e808b658bd9581b894b5d5997132b0c9c85c", + "0xb76947237fa9d71c3bece0b4f7119d7f94d2162d0ced52f2eac4de92b41da5b72ad332db9f31ebb2df1c02f400a76481", + "0xa20e1f2b7e9cc1443226d2b1a29696f627c83836116d64d2a5559d08b67e7e4efa9a849f5bb93a0dadb62450f5a9eaab", + "0xb44bff680fba52443a5b3bd25f69c5640006d544fca1d3dc11482ee8e03b4463aae59d1ec9d200aa6711ce72350580fb", + "0xa9490f5643bacec7e5adcda849ab3e7ff1f89026bf7597980b13a09887376f243158d0035e9d24fdee7cb6500e53ef29", + "0x96081010b82c04ad0bfc3605df622db27c10a91494685ef2e6e1839c218b91cbb56e043e9a25c7b18c5ddee7c6769517", + "0xa9522d59bcf887cbbbc130d8de3ff29a86df5d9343a918f5e52c65a28e4c33f6106ac4b48ecd849a33d39eeb2319d85b", + "0xaa5e0cea1a1db2283783788b4d77c09829563b75c503c154fdaa2247c9149918edac7737ef58c079e02dca7d8397b0eb", + "0x8c03f064e777d0c07c4f04c713a86bf581cc85155afe40e9065ead15139b47a50ead5c87ac032f01b142d63ff849758a", + "0xa34d672bf33def02ee7a63e6d6519676c052fa65ca91ed0fe5fdd785c231ba7af19f1e990fc33f5d1d17e75f6af270be", + "0x8680443393e8ac45a0b07c30a82ac18e67dcc8f20254bd5ede7bf99fc03e6123f2fcd64c0ca62f69d240f23acd777482", + "0xa4e00ab43d8ae5b13a6190f8ef5395ec17fbac4aa7dfa25b33e81b7e7bf63a4c28910b3a7dc9204dbc4168b08575a75e", + "0x8249259066ee5672b422c1889ab5ed620bddd1297f70b4197c40bb736afba05d513b91d3a82ee030336c311d952cd60c", + "0xa0651d8cf34fa971bde1ec037158a229e8e9ad4b5ca6c4a41adedb6d306a7772634f703dcfac36f9daf17289f33c23fb", + "0xb02ff6e8abff19969e265395ceaf465f43e7f1c3c9cfc91f1748042d9c352b284e49515a58078c877a37ff6915ee8bf4", + "0x927fb7351ac28254458a1a2ea7388e1fbd831fbc2feedb230818f73cc8c505b7ff61e150898ce1567fcb0d2c40881c7b", + "0xa9d3861f72090bc61382a81286bb71af93cdeefab9a83b3c59537ad21810104e0e054859eeafa13be10f8027b6fc33b8", + "0xa523306656730b1a31b9a370c45224b08baf45773d62952a0bf7d6c4684898ae78914cfafbd3e21406407cc39e12afdc", + "0x947a090e7703a3ea303a4a09b3ab6b6d3fda72912c9f42cc37627557028b4667f5398a6d64b9281fa2efbe16f6c61ed6", + "0xb41d24d40c10239c85d5b9bf1a3886d514a7a06b31ca982ea983e37162293350b12428eabc9f6a460473ad811e61ba40", + "0xb0bb9805724f4ca860e687985c0dc6b8f9017fe71147e5383cfbbbdcb2a42c93c7062ba42acdead9d992b6f48fc1d5ac", + "0xaec775aa97a78851893d3c5c209a91267f1daf4205bfb719c44a9ed2614d71854b95bb523cd04a7f818a4a70aa27d8fc", + "0xb53e52e32ca90b38987610585ad5b77ecd584bd22c55af7d7c9edf5fbcae9c9241b55200b51eaed0fbdb6f7be356368f", + "0xa2c5ac7822c2529f0201717b4922fb30fb037540ab222c97f0cdac341d09ccb1415e7908288fabef60177c0643ed21bf", + "0x92162fda0cbd1dafbed9419ae0837e470451403231ee086b49a21d20de2e3eed7ce64382153272b02cf099106688af70", + "0x8452d5df66682396718a76f219a9333a3559231e5f7f109a1f25c1970eb7c3408a5e32a479357f148af63b7a1d352451", + "0x831ea95d4feb520994bc4904017a557797e7ad455a431d94de03b873a57b24b127fcc9ff5b97c255c6c8d8e18c5c7e12", + "0x93d451d5e0885ccdbb113a267c31701e7c3d9e823d735dc9dfd6cfdcd82767012dc71396af53d3bedd2e0d9210acf57f", + "0xa2126f75a768dcc7ebddf2452aebf20ad790c844442b78e4027c0b511a054c27efb987550fcab877c46f2c7be4883ae0", + "0xaa4d2dcba2ccfc11a002639c30af6beb35e33745ecbab0627cf0f200fdae580e42d5a8569a9c971044405dfdafed4887", + "0xab13616069ef71d308e8bf6724e13737dc98b06a8f2d2631284429787d25d43c04b584793256ed358234e7cd9ad37d1f", + "0x9115ee0edc9f96a10edcafeb9771c74321106e7f74e48652df96e7ca5592a2f448659939291ff613dd41f42170b600ad", + "0x97b10a37243dc897ccc143da8c27e53ccc31f68220bffd344835729942bb5905ae16f71ccaed29ca189432d1c2cc09b1", + "0x875cf9c71ae29c3bde8cdcb9af5c7aca468fbb9243718f2b946e49314221a664959140c1ebc8622e4ed0ba81526302fd", + "0x86b193afbb7ff135ce5fc7eb0ee838a22e04806ceec7e02b3fb010e938fff733fc8e3a1d4b6cba970852d6307018b738", + "0xb3403a94f1483edce5d688e5ed4ab67933430ede39cd57e2cddb4b469479018757d37dd2687f7182b202967da12a6c16", + "0x83edfa0a6f77974c4047b03d7930e10251e939624afa2dcafbd35a9523c6bf684e1bb7915fc2e5b3ded3e6dc78daacf2", + "0x88ff3375fe33942e6d534f76ed0f1dfa35ae1d62c97c84e85f884da76092a83ecd08454096c83c3c67fac4cd966673d7", + "0xaf0726a2a92ee12a9411db66333c347e1a634c0ab8709cc0eab5043a2f4afac08a7ae3a15ce37f5042548c6764ae4cf6", + "0x81cfa33bb702e2f26169a006af0af0dcaa849cec2faf0f4784a06aa3c232d85a85b8123d49a1555cca7498d65e0317e4", + "0x910a16526176b6e01eb8fb2033ffbb8c9b48be6e65f4c52c582909681805b3d9e1c28e3b421be9b9829b32175b8d4d80", + "0x93d23befa411ca1adbdba726f762f2403e1cc740e44c9af3e895962e4047c2782ca7f2f9878512c37afd5a5a0abbd259", + "0x82fcf316027fedfe235905588b7651b41e703836f96cb7ac313b23b4e6c134bff39cd10b3bddb7458d418d2b9b3c471b", + "0x8febc47c5752c513c4e5573428ad0bb40e15a5e12dbfa4c1ef29453f0588f0b75c3591075fef698e5abcf4d50c818a27", + "0x83dab521d58b976dcea1576a8e2808dfaea9fa3e545902d0e0ce184d02dca8245d549134a238ab757950ad8bc11f56eb", + "0x898cfb9bf83c1c424eca817e8d0b99f5e482865070167adab0ecf04f3deeb3c71363b9f155c67b84d5e286c28238bef8", + "0xb845e388cc1a8e8b72a24d48219ac4fd7868ee5e30960f7074b27dada842aa206889122acfce9e28512038547b428225", + "0xb1ce4720e07e6eecc2a652f9edbad6bd5d787fbaff2a72a5ca33fa5a054dd3b4d5952563bc6db6d1ce1757a578bba480", + "0x8db6990dd10741cf5de36e47726d76a12ebe2235fdcb8957ab26dba9466e6707d4a795d4e12ec7400d961bd564bdee7e", + "0xa3ca7afd20e16c2a45f73fc36357763847ed0be11cb05bfd9722f92c7ba3fa708cf10d4e0ae726c3eccae23cc55fd2be", + "0x8701b085c45b36f3afb589207bbf245ef4c5c82aa967ecd0c334daa1f5a54093c5e0fcacd09be540801920f49766aa0f", + "0x84e3736727ba76191d9a6a2a3796f55bb3c3a8bbb6e41f58e892ea282c90530b53ab5490bbf1a066723399bb132160fb", + "0x87c02a01917333c7b8866f6b717b1e727b279894108f70574d1b6e9e8dc978eda8778342baf3c6464d6e0dd507163e76", + "0xb8da532dac81fafaed759e99c3ae011d75f3fda67a8c420c3b9747281fe32e31ac3c81e539940286440704c2c3e3b53e", + "0xa0cc63c3bef75a5c02942977a68a88cd3d103d829b6c0f070f64206da7e3638f10f42452788092de8fbbc626ce17b0d4", + "0xb5c9317b3f6b1d7ee6871506c0430cdf73e28b02c001ba6ca11061c7e121c91152d2b80c4f80e1d8f51ff5653bc0db5b", + "0xb798fb572da977dd3ef2dce64042b012a470d6bd2cb61a16267abe2b8399f74540d7c70462a6b2278d73567447e31994", + "0xb868eda58739effda68c834745cd2cf66a09f0f215607b65685bb5ca3eba71150f43a6e47b81a0c19fb58eeae3da56e8", + "0x9041c93a7e8f2c34812fd6e9744b154e898e1ef69db72bf36242c71e2c251f3db7e86cbd802da603a92cd0b06b62ea63", + "0xa834d648e974230582fc17b3a449f4f65b3297038a3a5401e975b9b60ff79b2006a33e1486d3428106580276993311e1", + "0xa3ce874da6ade9f0f854d7ae7651fc3ff63cec748a847527539fe0d67e6c99eaa3011065a4627c2192af7f9569f7ab57", + "0xae78ad16de150cc0400d3b6b424c608cd2b2d01a7a38ea9c4e504d8463c0af09613774dbefdd5198415b29904e0fbb63", + "0xb966db5a961067e743212d564595ef534e71dcd79b690a5a2c642d787059fc7959b9039b650372461a1f52910f7e857b", + "0x8069904f360af3edfd6cabd9b7f2adf5b61bd7feb0e9a040dc15c2a9d20052c3e5e0158f3065ec3200d19b91db603b71", + "0x9600917dbcd80a47f81c02c3aafecfcef77f031bf612a0f1a8bdef09de9656f4bb0f8e3e95f72ece1c22bd2824f145b6", + "0x834a0767b7b6199496c1faee0e3580c233cc0763e71eebc5d7c112a5a5e5bd95c0cf76a32ea5bb1b74f3cf00fbd2cfb4", + "0x99469a893579ed5da7d34ec228854c4666c58115d3cae86d4fc2d03d38f10d8c5dc8fb693763a96ab6be2045cc8d518b", + "0xa52cc0aecda6594de57d8ca13b146e77212cc55854929c03f2a8a6cdfa46296791c336aebcc2610d98612d5b4c0452df", + "0x97864434d55aa8a7aad0415d36f9558ce6e6c00452923db68a1e738232d0cb2d47e3b0b8f340c709112838adeaee4695", + "0xa4a7f2c45db3661b6af7ec759f9455ba043b0de6fd4787e3372cba215b9f7c641d5d817a0576e7aa28a46349d2fe0ae6", + "0x864e857652d95e1d168c1b9c294777fc9251a4d5b4b00a346b1f1c9c898af9a9b5ec0ac1f3a66f18a370b721dbd77b23", + "0xab8eac458fa8e7eb5539da3964ccd297a216448c3af4e4af0dcfed0ce29e877a85e29b9601dc7508a060b97a05f37e15", + "0xa6fd0782c5629c824fcd89ac80e81d95b97d8374c82010a1c69f30cef16ffc0f19e5da2d0648d2a36a636071cb4b69a7", + "0xad35a75fd8832643989d51d94ee6462d729e15f6444ffdf340dfb222af5d2b6b52e5df86082dbc7728fde7c1f28ac6b4", + "0x8e06831cc8a0c34245732ea610ea6aae6d02950299aa071a1b3df43b474e5baee815648784718b63acfd02a6655e8ea7", + "0x994ac097f913a4ce2a65236339fe523888ee43494499c5abf4ac3bce3e4b090f45d9abd750f4142a9f8f800a0115488c", + "0xa3e6a8e5e924f3a4f93e43f3f5aafb8b5831ce8169cddde7296c319d8964a0b6322a0aa69e1da1778fcc24b7de9d8b93", + "0x81a9bd04f4c6e75517de4b5e2713f746bd7f3f78a81a2d95adc87ba0e266d1f5e89c9cfb04b5159c1ff813f7968a27a4", + "0xb24de8f3a5b480981c6f29607b257ded665ecd8db73e2a69a32fcf44e926fdc7e6610598e10081cf270d2f879414b1ab", + "0xadc1b3f8ed1e7d5a26b0959ffe5afc19e235028b94cb7f364f6e57b6bf7f04742986f923fae9bf3802d163d4d0ebc519", + "0xa9fa5092b6dd0b4e1a338a06900b790abbc25e2f867b9fb319fdcdfb58600315a45a49584c614f0f9f8b844aa59dd785", + "0xb29c06b92b14215e7ef4120562893351ae8bf97cc5c3d64f4ecd0eb365b0e464cf27beec3f3ddac17ed5e725706b6343", + "0xadc0d532ba4c1c033da92ba31aa83c64054de79508d06ee335dcab5cabae204a05e427f6f8c2a556870a8230b4115fd0", + "0x9737150d439e6db2471d51e006891d9687593af4e38ee8e38bfa626abcefa768ca22d39133f865d0a25b8bbf7443d7db", + "0xa10d1e6a760f54d26c923c773b963534e5c2c0826c0a7462db2ea2c34d82890f9c58f0150db00aa2679aa0fdb1afcb08", + "0x816947dc6c08ee779e9c2229d73dbfd42c2b3b6749b98ec76dbad017f4b4d4f77b5916600b576691978287208c025d6f", + "0xa2dc52b6056219d999f07b11869c254e8b3977113fd9ba1a7f322377a5d20e16c2adf46efb7d8149e94989b3f063334a", + "0x8153900aae9cf48ebc7438b75c16f5478960ef9170e251708f0c2457967b7b31521c889b5fe843d2694a07c0e804fa48", + "0xa9e9d8d66c8774972cc1686809ce1fa5f0e16997ef2178b49bcd8654541b5b6e234cb55188f071477ba1cebcf770da45", + "0xb1fa775f9b2a9b05b4b1f0d6ad5635c7d7f4d3af8abaa01e28d32b62684f9921197ba040777711836bc78429bf339977", + "0xb1afbbd522b30e1ae2adf9a22993ab28b72a86a3d68d67b1833115e513632db075d047e21dfe442d6facc7b0a1b856bf", + "0x8779b7d22f42845a06ae31ac434e0044f5f9b4e704847fb93943e118e642a8b21265505ad9d6e418405d0cb529e00691", + "0xab2c6cef1c4e7c410e9e8deb74c84bedeb3c454ae98e3bc228eb13f6b7081b57977b3e849ba66346250e37c86842c10c", + "0x908d6c781d7d96aa2048c83e865896c720a66fdec7b06ab4b172192fe82f9ff6167815ffb66549d72bfb540bb35c36c6", + "0xb790440f205ece489e2703d5d1d01ba8921dd237c8814afb5cb521515ed4c3b0a6df45fd4bd65ba63592c2fe1d008df3", + "0xaec346251f9c78336b388c4e9069a1c6c3afbbb6bfaffdad050a9e70e92fb3cae3609067b4903552936f904c804b0ea6", + "0xa0e528cc2cb84b04cc91b4084e53ead4188682a6050b3857c34280899c8233aa8c1a9c6fa4fd6a7087acf1b36d67734a", + "0xaa8d7632be3e4340712a1461a0ad0ae90ba6d76e2916511c263f484c6c426939fa93ffbb702cd0341eea404d6ddffebb", + "0xa4ea871d8a1d4b925d890aefb9897847599b92e15ce14886b27ce5c879daa9edead26e02ccc33fcf37f40ff0783d4d9e", + "0xab63e4dc0dbdaf2ada03b3733aafe17e719d028b30dc9a7e5783c80933a39935dbe1ef0320bb03f9564cafdf7a4b029b", + "0x8219761bbaa39b96b835f9c2b4cec0bf53801f8e4f4a4498d19638be2fa0a193b2c1fbf94e26c1058d90a9ac145a7a12", + "0xa609ee5561828b0f634640c68a98da47cb872b714df7302ef6b24d253211e770acd0aa888802cd378e7fa036d829cd36", + "0x90793ff0736f3c80b5e0c5098b56cda8b0b2bca5032bb153d7b3aa3def277f2fc6cea60ac03edc82e3a9d06aff7d1c56", + "0x8760085283a479d15a72429971a0a5b885609fd61787a40adb3d3d7c139b97497aa6bcb11b08979e2354f1bc4dbf7a0d", + "0xb168ede8b9a528c60666057f746530fc52327546872dd03c8903f827d02c8313e58c38791fb46e154d4247ea4b859473", + "0x842c1149ca212736ebe7b6b2cb9a7c3b81ae893393c20a2f1a8c8bfef16d0a473ff865a1c130d90cc3626045f9088100", + "0xb41d0e2c7d55108a8526aa0b951a5c8d7e3734e22fe0a6a2dd25361a5d6dea45c4ab4a71440b582a2f9337940238fe20", + "0x8380bd49677e61123506dd482cdf76a8f1877ea54ed023d1deabfc05846103cfd213de2aef331cdf1baf69cfc6767be9", + "0xa026f92030666b723d937f507e5a40e3f3cfd414ad4b2712db0a7a245a31a46002504974ed8ba9d8e714f37353926a4e", + "0xb492e9e9917b29eb04cde0b012df15cbd04f3963d120b63c55dc4369e04f5ac7682b2c7dff8c03410936c26ca73ad34c", + "0x81fd9271b4ee36be0ba8f560d191e1b6616dd53c56d1d8deac8c1be7bc67bbc53d434cf70d04e7fa9de3e63415389693", + "0x835c3711abe85683d2344a3ee5f70e68342fd1aec025ad248efe66aab3e3d5790fad2f45bae0d7a53a80998fde45f0aa", + "0xb46599be80b8f7dbad0b17808dd5ca91d787929c0bef96fbbcf6c767727d07ed6785bad164d733ecb015eb6c8469a16d", + "0xb36bf5c17271d39f5ccb3d82a5e002957207a0cdf9ae7108a4946e6f3ed21a5d353fa940b6fe949c39422b452339bae9", + "0xa12f5444e602d6fb8be51a08b8bc4ec105dfd759d2afe98d51ff4edd673c92e4fc91ff32417ae8070e12169004f8aad3", + "0x892ce3ca0a2961a01f7f0149b8a98fdc0f8871c2d85e76daf7c8aed2a18624b978a4d0a84213f81f9d2a81f7ca4826d0", + "0xb1e6229ebd5b3d85e62d0474d1fed34564f1b5b9c5856fae36164dd0eff378d67d6717dda77536379006fb462bced9da", + "0xac852921dcb81e54e1e315fd6226219932f7b785c2ceb2035710e814899784d7001101f1515d68e3fb74cdbb4baf9e26", + "0x989a42d851123d708a213f3a02cfc926df15af058ec9b5a9df968fe16decbd781b5e65a4c17fbfedd2ac17126084700f", + "0xb1d0fc2f7c948e466445f307da7b64b3070057c79c07c7ebbbe6f8ed300a642b3567aed2e5f28988ac566ba62e0d2a79", + "0x83057263b41775bc29f1d59868a05b0f76d3bdf8a13c1014496feb4c0ee379bfd0d4079785252f51fbeb641e47a89b69", + "0xac9e6a208aa9c557155cf82b389bb4227db5ac4b22a0c7c8d1c3d98946df8b82b0c49d093ba55c8255e024a6d67c14b4", + "0x8294a11cd3f5111b1f8bd135be23b4de337ac45711db9566ebf6e162cd58e7859b1309eba8149b0f0a43e07f62a92411", + "0x8c15f3388b196603c05adec195c1d2cc589e3466da3169e9afd37157fa55cd34bfafbfc5ff10ac0e04aa6a0d0b2ce3db", + "0xb8faf8ba89c3115576ab6b340f6cc09edfea8f7331f5a5e8003960c584e839fcecf401113dfbb9a5c11a13721b35c263", + "0x955c63b1166514c02847402d0e92dccfe3c0dee3bc70d2375669afb061594c85651e6569f471a6969759e5f373277da4", + "0x963bd4f9ae7361d6936d209592a07d9a22cc9ef330cf0c5cb845cb4085d76e114aee66d7599bf5b9f11c6b1c05dade8d", + "0x85509b3c97e06e0db113b8b40022c8989a305cec39acab36ba3a73a4b4719573e5bdb82dc4795699c26d983465cd61b0", + "0xb870cfd7f691f88db8d1dfbe809b7b402eabd3b3299606b7dfdb7ef49415411f01d2a7e4f7ebd919ac82c7094f628166", + "0xa5533e7b58a6a9e5c25589134f501584163551247d36f50666eeb0a0745cf33e65bb8f7a9c2dc7fe7cb392414f1ece4a", + "0xb93d1ade01ff5678fcd5b5b4f06a32b706213748076cae3a375e20a97231133ec37c1c3202cbc4896b66c3410210f446", + "0x86ed3a58000a46fe2c37d4de515430a57d8f54ab4300294685534372fed1d68e192dd43d43ea190accf3dc9b22e1548b", + "0xa8c7d8dc30057bb8ad66b9cfda5e223334407730aeb0f51705922c18e7a07d960c470d463d1781899203e1b1ed1df484", + "0x8d86821d006e957e8544f95a98b110c89941bcc6985562e7a97285f5826b35b690963b2c141ff3f389d92ee18ec76d24", + "0xa4e1108cd3cf01810e74dbbf94340487011b80013b9bfdc04f019188c0d4d077a54b71a3f97a036601aad42a268531e8", + "0xa822cd61db07f64bea00de226102f5fc0adf8fa9f05a6c7478b0ff93e48f6cc3191302d22e1f369b571877d5eb96139c", + "0xb1ad4094d0bb4c325dfe072b17711962247dd7ff7e4bce4612e80a6f3c1bde04880ba1682f60d5f1451318afd4d3ba60", + "0x88e7beb0cfd7361288ea27f6b2cb18870e621152ff47994440c18d45284d21bad80d9806ed7d9d392a5cd791d5150ce2", + "0xaad3724a176cf4476595cdfb9e2c3261c37052324c0b5373a30b6cbeb481bccd303720840c49a84ddca916d470eb6929", + "0xa57983370d159e7078a273746fb22468000a6448b1a31d277272e35c6f548f97928e9015f1daf577511bd9cfee165237", + "0xa54136e9db381cdd6dfb3fff8bdec427d4dc1072f914f6fecfec13d7b8f95bb3b5f30ad7677288c008ce134edfb039a7", + "0xa25dfc4019f165db552f769f9c8e94fa7dbbf5c54a9b7cde76629cc08808c1039ecbd916560c2b6307696dd9db87d030", + "0xa917d25328b0754d70f36e795fe928e71ae77e93166c5e4788716c1ef431115c966f2aad0ce016f4bacc2649f7466647", + "0x842ce5e4ad7d8d4b8c58430e97ff40a9fce1f1c65ecba75fed2e215e101d1b2d7ab32c18df38dab722c329ab724e8866", + "0xa8eb2ed2986ff937a26a72699eb3b87ef88119179719ff1335f53094c690020123f27e44fc6b09f7a3874bf739b97629", + "0x96753c1f9c226f626122dad6981e9810a3cf3bbee15cfc88e617cfd42753e34593610861be147a7b8966bcdec55bba8d", + "0x94119d31606098f5b129931b51b4b42c4e3513a128b9bfb03cfeee78b77b9909b1c2fcf0a292e49d63bc4e5fe823dfef", + "0xa869654f5880d9c21a0af1ff4cfa926e03ec1f2d80fe5524605e04f484e09dc80d6769249f31fd378ff3926ab4cebc69", + "0xb2a539bdd8de4499c5f35cd8824974c2abb1933b3f50d0175dd044563ca829eaa0fc47bdac97eafa98434d1cd05d7c5d", + "0x85f53b2bfcde1986ce7279f3a2f5f841f87d75af5d197c897f261d4874bc6868c575ecf7556a32b7b33f7b2795454591", + "0x964f087ed02228b30f401d8aea35c1a7f76698e4075e1bb343398be74c716884e9ca1a31b81566e1ff7513cf76a2f0cd", + "0xa1c9d9c9bfbc9c4e281a2953d5991e7b22ff1a32ddaace9e8d9a42e080efb802b853d3276973b5189a5745943c9b4389", + "0xb0c45a9852663a427d7f50c608a6419fbd00f90e8452757a45269d25c0386ec29942f48a34aafc0187ef6020e581d290", + "0xaa3ca7b01862d5d2aea714fa06724b7dda7062b6608605cb712588b2c49fc3c7d89a8799e6e7c31e7a9ef28b1ad4d1f7", + "0x88f5e98ae8c5ae7add42f6d358a35667e590aa80e1869593cbf597d7ee466efa35b429f1836ba2199d8280fe7f60ce3a", + "0x8a3bff472e8008f7e50362acc1a0b53c09ac60430942544532722e938470376f0672662261992146765b7c75a380c318", + "0xb9847be7f7aee7532282c279dde928698a892a183ca3047ceda521e9e0a50d96fd3ce59f8e58f31af49508ade6d4ba51", + "0x98065dc23ea3df6d9f8459e81887d88d5752b7e7ba6050ec5c3f0dce93e463e0bf12be3c94ec74c16e2f7ba62e447845", + "0x994aff677b97ee790894dbdb21b1f9210734e008cee2aa2200c8f2579ea650b872f39776a13a8c31e95cc817091bae1c", + "0xb292811674e18912ebe79df1af4a132b04ab702c125c039e0213f735f658fafd36c38e5bbd7cad35842576431f5f3630", + "0x96520d750ec10bb10f75019f8f0e4a93ecbc6b678a710d76cd10aa27a6642ad1461bd58fc2aab8e0391b3f788339ed29", + "0x80d478da7fe246ad0e81a00141229e9d91ffb7fd1b29975c8ec358ed5e864e481bf01b927a9ba002c5ec4aa226d0cb57", + "0xae58049d93a11ae845dc5be2505e95657f83b95d83ff3591a3c565d587157be795ff4481f42d59eda95e6d523444e199", + "0x85f1f5ad988b9f8a7e24b6d6a22b9de9fb3fe408f95711389c444d7ba2243987225b04318aa97a4cde2cb4c30c05508f", + "0x922092d0cb828e764ce62f86cbc55c04dce07233cff041888fae48cfe93818780b4aec9b4ff4718275bb2bfa6bd9e9ba", + "0xa85ba97125feff0590a05fb78f19a7338639ad1748802918af4d59307bc994536c0ad638b97b9acd26a08b6b4370dfbf", + "0x8c46fcaa8d13266d650bd9366180e5ebbfa002c339e4424a030de19ed922e2daa9a353ae54921a42299607ae53feb075", + "0xb8549832230eb1ec6ee3c33c078deb47f556a0907d2a85fde7720391c82d2ed63dd753cf544a6a0a46eed4b8d1ecd9b8", + "0xb7b96f24504c7f8fbed9c1c654a2550feeee068407b809c43f1082c9558c8665806d911d5d244308169d8a531373bf56", + "0x81c483fd9d9ad7af7869d617ac592e7e951e39738da041d8c4110637689108eb29c8acadfc85366c70885cdf77b353c3", + "0xacf33bcfd9080dfdba828727fe36803327a94e8a3ee5b6e445274f0e8267ad3c943994a8dd6d09b8072912b57e1e25b8", + "0xb3475e7456ff96861bc11068198d51b69b899f5ff13022694b501d3adc8bac58a16204b12011d61e880c8459f4badbbb", + "0x8ceb9562026aa96d6e786ec2e5cd49200b5b424349a2214cd3ff5c8f1c2bf1b9872480428f5428e45cc61106cbfbd953", + "0xaf56f7e482c24a1367fd798201a20c464848ece431f2d8a31a6ef4f9bdbaa50991e748dcb4ef0c08fdac0ef8ddda3b80", + "0x896dae8b12549909d512fd5c02a2f72dde4086aef6c8007ddb26bb04dff51a707ae94ff87e45191fc10339967fa28958", + "0x8ed1c606840e07a2ac6ff16ac6e81ed3e1c90872ababfe68d56ed2dc50d9294579b9c3546dc63292874299a3162d59f9", + "0xb4d7a5c0836e419a46942281ce77d0aade8e39eb1bf1190dd274ca5070898a1c02ad9d165855629d6e1c96df1a6bd5f3", + "0xaebad8939ac117deb28b789d9846c2c80359dc260920ac8408dbae0b6228dbf496dac0023a3b4302bb9a53e8ada18e61", + "0x812d07c74a8650dc3f318c9b2dbf265f181041fb432fee989cedabd44b933dc6590e36c71dcf9dbe7b4bbf74ea0d7c50", + "0x87b131dd3489889e090839c392231e0ee198acac65bb2e9e63e7d6da322391d1685cfc8ba60699308054c4b0fd89c90c", + "0x8b12110ece0b99b2e653b4bc840a12bce5b85abf6fb953a2b23483b15b732a0068824f25fcaa100900e742886c7b4a0d", + "0x8765fc9b526a98512e5264c877bdca567e32fdcde95cdbcf4f4c88ce8501e1c7fab755f80b87b9b32d86d18856f1d005", + "0xac806a32a14019337dfdb5f781ecba5cdea8fb69b23e0e57a0f885e0082a9c330ba808621a48e24316604f6c6c550991", + "0xa711970fa40cf067c73e3edee9a111bf00cd927112205e9d36a21897529be9a051be45c336d6b56725dca3aeea0aed15", + "0x908adbc17fc18821f217d46c25656de811d4473779a41eacd70d2a0d7dd3010de4268a562378814e619e13ac594bb0c3", + "0x894251b79be5ae763f44853f6999289b3a9abda64d52797c6c7d6d31ff2a79e9b3906da72f9ebb95b61d6b29479e076f", + "0xaadcf11ea15bcb6d979c3ea320cff8dfcc23c5118ed075f35e77f71459b2141253060e3a90839adbcd3d040ad3bdc5e2", + "0xb4e55d7d2eeaaffb0267448ecce0b75166e4805dc0e261eb5634d4a3f3c08964a597302fd8f6b45ec48178619291dadc", + "0xa8e2a02c93d6bec7f42f9265269660b4b404940c3e3de9515b4d826ea7e71f18c6f90a71ce3fbe452d0713de73cb391e", + "0x8e2467accfe207cb1ba37d60662920f95338ee212927edb706228c25345734217740159310edf17687f58b333754cb65", + "0x90376b88f653381b3bab673c48c2b84fa82a091e18f710a732fef836e0d39043fcd5527aa97a3a385c0a77cf53746993", + "0xb16530e289198c235ab680f86851bcc177f0c16a58483d83a89213077b06d6840600b03834b6b7af0e22b1914f72de43", + "0x8c4fc3854f938ef1c2b5df065e4e75e9f299798afae8205706439491bdf9784c756134922e77af007e349a790afa52b7", + "0xa68aaec4341d29b92b35322f89b1ae3612e7b440c89a86135a07c261dc5799217a651460c92113d099b486817226d8cd", + "0xa653f965feefd2df24156478f0cf3755274ca395afb79e8c72d3b6e1d1f5ba7f3e4f9a4c5ee85355de6f3c81935ff579", + "0xaaf6c8d2717b57f6b14e06c742a11a3bc736bfc0327ca4b8a005b6e924f06871141d231737698a9a59286e44f244a168", + "0x8de32e3c104b4278e27aac695d224f134001c3619f15186466c57c0c46f67e2efe537501d0d9f52f4cdbc724a170b92d", + "0x8e9b5858b6d4ffe811f6498bd80e454f0d6b345d4729c946626c7cdc196c803a349a14515296aadb7258bb7a5b37e930", + "0x82fc711043aaf1d7a9c712d00eafd816a710f82eb10818ba6af09f591447f36814dbff6e6a1cb2b5c7f16c73930dbbca", + "0xb2f0205327fc8ff687f751e7b97788732afaef4fcf51bb17fd7579ed07501915790b70fc36624371fe4fb87a0179d850", + "0xadd87d5b1288d30f3449d3ccfa11cba4dc7756d85cee1cb6171b493680a625a01f273d0bb8e6332d0410250036b3acdd", + "0xa411f75ef7dd8de8062331ea40929db989e4d65ae8f33d3fa6cc19c98fa8a8ec2b7c7534a5c5eee9e5051626a6a2e47c", + "0x89d40a647781e7f2e8ab3a0f7dc7133669944c0cf627376433687a2ea15c137be26f582a6b07ff94b266ac0910009f7c", + "0xb2b5f808c26b40ed507922ed119b0fb95e0d6d8b084bbbba58ca456b4354d03110c99989b93207998334ea5d1b70fe49", + "0x8c8db028671969a1e80e595283ce5e678ee955d785043bb5fd39fdb68a00e4c15b462600a7ab1f41486b6883e725894e", + "0x958087ce0c75fe77b71770c2f645ef3360c1a9c98637693b988c5f6ce731f72b24ab8b734e8eb6258ee8b23914451f0d", + "0xaad6c00df131c1eec6c556bae642e6dcc031e70f63eee18682f711c7b2fcd9afbf1f18cf8a4af562759130add67bd4a3", + "0xb6d23c567291f019cd9008e727704e7e6679b274feb29abba0d92e036f349b1f0fa8c5271ec7384e8d70a2c3977b1f8a", + "0xa942c770e903d4150b5684e4b94bb72d0e171df2c7cae6f46e002c41c6b04d774ac6e2753ba8dccdbba3ad1e297a9ae5", + "0xaa542d1849390f86d797408ed7f6a31504aa65d583481a00e475028af20f8b69248a87a8ffab1dace0377db77fe5f9b2", + "0xa1ed3f9564a97f7cabe7c67e018eaeaa42db73a2f3d2332041ca9a7bea57436d848784d6dc402862c22a47f0692b1286", + "0x925c757750c91db8b1b3c220fcbdd80742b4a060abfb0a402071d215c780ef6b420132ec5a43043b9fd7a06bf1b323db", + "0x94e575daa7fa0bbb35b4386f510fc3877c9df57bcf15349c5923f30ad6a8df95372835cc078216b41a7192921c1e8973", + "0x9346a41174865d9ab31c7fb9a5329f322bfce06002386d3f5a2e2193de9bfff12bd0bd93307928f7b85e1097b2aaddff", + "0xa6e54c9324baa1bff7e9bf39c94fdd308ec6f210aad937112ec727565f8a6141375c04196831873bf506294854f6a20e", + "0x98d47b662504f400f1a0e14e24b43829490d022ade02a56288aaf148d466b45d89b5fc146cef67c9ba548cd37ad5e354", + "0xab690dd59a69904b6b3a4d5a42d17ea4898d9b00c6753aec216d5d4ea564f9a1642697df44d5a62f2c2ab19aaabf1532", + "0x8d0aa8d3c5ec944af49beb99e403cc0d6d1adc6003b960075358a4ff1cbfa02a83d6cb4d848d9e83b34882446a330883", + "0xaf9334b7300780c752f32eaa68f3dcecd07dc50d265083f37f9800b02c2595ba24dab89f5fc27c1ecfdbf5291b4d77bc", + "0x81c4a6aaf7d4ccee9925c512dae5da6d916a6dd59f7a4cc79d216a91201b4d300114a309e3ddb3291bb95f85bec2a8ea", + "0x8c804e810c0785789de26e12b1beff56a163769733be7a31f34f81093782d6410293768a166c9191ef8636fc8724a31e", + "0xa91222b48de238f6dfe79c84080cee618611bd0bdca15cfe44474829e42481f8511a82589e69964e19f8cba04e3f5f3f", + "0xb26a8885aa594b0c8ad4a1711d80bcf687df996442075dd1497db1b446d16c74e28bc6f0e92b2ecea9c3e15c9c7e828a", + "0x85940f45d324ad1d335bd1d7d6f81758f52213e63d5770d9fe0c0c9507d5550795e538b6a2dd463f73d789b5ce377aed", + "0x931a277c78082f416880620df3aeb6d0bff2103d19679dd092ea981f5323e438c50a0d094908034ff8a2cb47b1a44108", + "0x88dd85e4e2aa349a757b98661fc00d4538ec1d3f53daf44b16ffcf7f943dd4f2bba5b8ba3b05c529251dfeed73f6f1e9", + "0xb7fd7182cd33639710b8216c54a11bb02e199bbc54fe33492a809dbe17771a685d6238ea3ebcfc75e3b0d4ea5369bc9f", + "0x85d77194d910f8cdad7330e1bca9087529a40fece17492f1d17cc4790833891b6d01d24f036b6422175c732b438faeb5", + "0x9845265892d672d9517fbd22f88be4f225711b4abafa8327cc059f000656e4737188506051565d97912a0c19c3d063c0", + "0x90a81987aa841c7f640c298b816643a0ae00cd3609c3a31d0b01245283cc785d9bb27763131b31a4f21aeda4e50073e8", + "0x8b1256eb41a600bda8a06ac08b98a220ebfd52f89a0e4fdce32425db7a0481e9b7873ba3b7a24ad9fb782ee217dfdbf6", + "0x870548998deed85c59507cec7e69cc001c279bb2a99c45a4d030a35c107e69feb76afecb9e435e67965051d6d7a88220", + "0xb1504d194a0dd8df48d431ce991f89d7a0f72f573d21bd5bb46474c5005e43820877a44e62db555f194427ac8a4b9168", + "0xa00d7423ec2cf0c9e9da07f3dae092d09e1ff4be852e07e531aa54d62ad937bfb52c8bf44683ac3a70f6dfc125575da1", + "0x8019625ad3d218018803aacc2efcedba3a41c24aca8c5aab2005556e58fdf2ed614831277df7937aa594e97a2fc65e7d", + "0x8595596284f3add0155ecfee3fc0b66a6b6fc7923d82ca8302952e2ed906d119a1c053aed1123b51f73e1d30d93aba57", + "0xa8ba033f5e7d06177e9ae2d99c40ed4e99e14e1c1b61795997f62e21ed8af1531c4720f23d6a39b0f75c6cd91c58c700", + "0xa94f4167c0f6ae214bae75dd92c63299dd954b00b0d8b0416b8af929fe5aec6a259e44f83a183412d7ba4eb3a49728c0", + "0xa73ee3c3a0fd2a369e0a279c3e214fb662d0378eea3c95cfb91412d7213a1f05958bd0de8f2a4f80f9f80d7eef943b41", + "0x8ef6f3e241f6a761c9ab412629a49648c08b70b837c2cd8bea620bc93056ec73754e3e11f0df50f8e9fa67a9867501a9", + "0x80b473ac4ba8cb82b4ae684206cde124d10fcf619f55a6c90d035981e1b08b9e141b4e5fa9a9af0b7f0c281b355dd593", + "0xa566e2be0b41f01978dfffbb32f442b5e6706f5b9901110e645cf390f6a82869e3ca16887ffa35782a004d251d29c26e", + "0xa74e01eefa03546d00afdd24bf17015eee95d36de28c03c9b055e062cd5e8d8f20473c6d7ad21c94f9058fc5e84f9628", + "0xacefc74de146911275dfd19bbe43d72729e89e96da04aff58e5fcb90962856c0b24eb13f43e30329f5477a1b65ae9400", + "0xb5f113ef36e75de6d6d44130f38e460ad3ffc65cb9a5606828c4f7617981fecf76f5e862d7626ccb117aa757cc3c3e52", + "0x96d3aeb1d3a66b136244062b891fc7f93ce745b776478d361a375ae57bdba9b4fcb257becbae228c1a3aff4a1c4fb5e2", + "0xab26c4a110877e5495b674569a32025dad599637b5dafedcfe32f205dfa68cd46f3ddf4f132a8e5765883b5c83214a07", + "0x922a7a738066692193af32ccbab74edef067668ce3253e18a3275afcd5a6df7168deb2f5175c5fb413dc08fdaef63b17", + "0xa47542f8e4a3a35ef6049280d1a9442c920887d5f1a1483149e143ca412318495a36decb804f81c9f5a7672a14965a4c", + "0x8fde57991e72a2aebd3376b4d9fdd795943ba3833431e52b136683567e6ee2cc1c1847dc49dc9534983060c54bf22f7e", + "0xaddb041f01a99e7238ab2f9f2f94579861d0470b93b91cfb29f3a2e4c82386c868b2cfb6f3778b8a9cf908788acafe58", + "0xa8c4e1df726431c43703739776e2cc51f5ebac57051244991baf53582538120133a44ca603d0722a4b5193e1be3c5ec0", + "0x846379125968d1154376c5dc63100bdcd99b9403d182e3566fe48d79099099f51523cd81d21f0d1dcd622b715bdd851a", + "0xb828bf0d936d275abb40e3d73ef57fcd7ce97e9af35e194ae61463317bac6c1b0c3e4b40afe08a1061037bb7149108fc", + "0xabd07c71754973e698fa26c5019afd9551548f8369e2249b9902513f19a097057ee7065a1d88912e8f52e6e0fbfa6d82", + "0xa9e36b6fcc9a3cc98e76d5751c76c50e1f92b7670f8076ab6ca8a30de4ec14c34669e049fd39bd293cde8789b1ca67f0", + "0x8c060835496a04c7b51790790035862b20547e62fa8bb4e8857fb36891ec6309520af5c0f45d5ea46e3d228747d710a4", + "0x8cc472ec62b8dce244373f40a821db585628989b6a7c4d394edffbc6346c8be455f4528d528fff41f91f2c875bd9fc0f", + "0xb4a75571f84f93451f15b3a86479063d7324d2789b6d2f2f4f8af68c66fac32743dc09b51df29608d62aaba78f6904af", + "0x916484984743b5ac16d40d0544faf9184819d92f779254b7fb892eb68cefbe59e75be8a6336a585e120f6ccae0a1eeac", + "0xb906ae585a73119764024e9eb87d92e53ee0c673474fec43fec4d344a3bbf471ce3976d25e37d197604689bbc944f1ab", + "0x8552708487305f16f95db3e01fbbfb969398f5b6d116844cbb000c9befd03f15c767584bf9541a42141949a4dc787a3a", + "0xa6025a2773f78c247f78c0d895ade8a6baa76e5499085f6175935d98a05fc41c1359f7843e0c6c323f1be256c45f45e6", + "0x96dac695dd9288aeb6e32dce50e51ddf1fbd41de6146e3605c7a81f2253b17babf2bfda4f5a9d0c28352b9746c0dfa2c", + "0xa215b21f8eb2290f9d308278f2859a999eb3a31f4888f84a65f9ed05e1151c17777f91054d4d0de759ac5c3547d91929", + "0x8fd7c9a279e9b619acf927d501b35dc551979731a89eab91d38b2356c0d73569baddacb9d1096d20a75c917ecaedadd6", + "0xb985e8baa5195e2f1ea1091122d55aa321178d597f87b732b23eccb12b891638be1a992305a1ffcf5233af34339fa02c", + "0xae1a9604b7f569aa48d2daa1889e76d3d103065fc8c3deb9ae127a6d94145695cab3bef640fa781612e8082c6d616c47", + "0xa8fc67f9069f753360349eb874fa4dcadb2ec48d97c61abe568faee5f370ec3c87786c7faf0f73fc0ae7181a36eb89ca", + "0xa506d13acc3a9f80509fac936aef848cd30698631fff6130ed6217512ed9527d075f653cf6ef91f68e48a24c903eeb3a", + "0xa415093755cc012863043bf586b970bafdd87653ad14d1929672e04949bae4a753d16aa3eb5bd1afe3df3691b80f240f", + "0xace3b792a1960580348b6fae8513149242378a18382741bbc2fb2f785cb8bf87550da4b5e0df2955970ab3a31f99f5d7", + "0xa47d7fa7522664c8f9c404c18102f6f13a1db33ba8b0a56faa31a78a3decba3168c68f410115c5d9f240b3dc046dc9b4", + "0xa9c930db3ea948cd2dd6ea9d0f9a465a5018bbaf6e9958013f151f89a3040cc03ae0b8eaf74b0ff96b4e7a6cd8aa5b4f", + "0x88abd235e3e760166cdedff4be82cf6ba02d68f51c6d53b1de326769f1f635215890f9a4c35b06dd16a9b93f30f3a471", + "0x8f8d7b2fcdb70bfedde1ffd7f0b94108f0fa432f6ae81097988521dd2c4da928c10c5da3c7f33f11bd5331f2da8ec219", + "0xb7abdbd48cece30d8f795a58a94913d76842cb006892485a9382a0502826538ca4ff951cc1ef4493e45de8571360d20d", + "0xb3e7b125f350c52695f7c5ec4a30916ea6c11744f1151a18ea0510e6cf6ed6f6dba4beaa4ca56988d306bd80ec360056", + "0x9a004423c95e1f1714f98fb97ab798d6ab16cb5f6d6cad860635585d4d4b43ffcda63d8e931351189275e5a2cef28c2f", + "0xa8eab6ef917cacdc9b1932eb312309e1f85298d63e55ed9c89ab79da99d3eb60f1643d16be920e82d9285f60c7f7cab3", + "0x934df955485113d10c4dde476ec14a98771145aadf3c8b61af26b09b9948757fa1abcc945ac91466a18c18c2fdce40d0", + "0x99ed9146561597cff8add2196ff3a0f161dd5302685ceb846afca6efb5225f642e8f4a0970eecb01cdf18694fa697095", + "0xb37062dd12a81267bbbf89bc9d6e30784c0e11e713cc49c6c96440f800f2a6a2a7e7f6c7f6c9eed4bc3c8890f2787342", + "0x83a3d70055b6044e0207b3ece4da849755ab5798317b36b20c3555a392c27982f811e1c5007697554eeedc737b37f3ef", + "0xa85392c07ff8658935fbc52acec7221cd916c5fde8537a8444eefd507220e76f600350ae8f5dc3353911087b88b91045", + "0xb1ea23558ad805dde9cc1eade995cd8e7f46d9afa230908b5fbaaa09f48547f49c2bd277bff8ab176f1c240beedd2b09", + "0x8a16a48b9105d94700e8e5706b8d8a1ed14cffda5558a596974ea3191c5c3449da6e7efe2059e7baf4530a15f175ce16", + "0xac5fa54381fc565842417558e131df26e9505027759416165035357816a7e1859a7c14c228c79b4e5ba2ef6758e12ad8", + "0x8475e290c399cc9322c05264a516cf766bf5fdb6b9dec7283961da0b99012d499b244b33fc0eaf94b461ab777f2a9537", + "0xa7922f3c70e6857652805af7d435646c66d94eec174be997c4fe973d8f019990c4f757eeb730b2cfdf8154e6e97f7d5b", + "0xb90deb797fba3150cf265a23ea6bd49a382855cd4efe171cbcb1664683a9f1687cfcadfdca4e39cd971ec13aa5cdc296", + "0x91ca761dd9659007d2fe8970bbd336c19ed0d2845d0d8aaab397116affcc793de2da73d89e6625cf4dae5983cceffa56", + "0x9121ae9b60323ab1301e97555bcc74ddba0f5b1e62bfe9eaa2c239e1d685c4a614d397b32a59febed4db9968db44f38a", + "0x8477b07da4bbfe9087975f30d2c2333fccfcd7149f90e0e6fabecee627eee3ea324df31cf6a680393f5dedf68a35c9de", + "0x946a9c0f02fa6bf9f9d4933e7fc691749f4ac2f82a9b880666b5185189d4f3432da9096d0ea4d6baacbc079e19c887ce", + "0xb24663332914ea519435874d4c42d11842ea84dd3dc55292d5b0f27f64587848d095bacaec235a37003bdb5185daa6f2", + "0xb980f46f84ac21dea75b4650f9412f6123325842758589a9b47caa68545905061f03fcad23cc102e2ce8ffeb1ae634a8", + "0x90e9ebb060182d3043ea4210a2d934858559522a19eab9f0ff81a367484a05ec7cce78ee6a91dfff96145869db6a4e80", + "0xb04228a009c91847693eab29c9ea71d1d6ba07060bc2b0b3bb81c46a125baecb3e1412f6ce4305076a97d316d14e4665", + "0x8d3268370dbf38d378c7228c7b54e91f90f43cbfddc0d8468de11a4312616ca6372619209b89114152b16f334f4d2780", + "0x964a63ffae653e0249685e227d937937b079ec3da9c977dad2b2e052af5eb560ce7d175941f2ae0df90e3d0a20b77e75", + "0x855604c2910be885b14b27896e16d8dc339236b975398c771d29ac74e4278a2305fcf85203050a8faffddf64ea19cf78", + "0x8e0b1d61a4349411eec77cf3490555843187a25a93e1f45bf66ad3982b9cc141b07805f8cb252b0fcc125e0052a7c450", + "0xa03bc9588f971a1257cd0cfd2ca406c76aaeb634001864b0e4dda91e009d3361b33fc39f34922835031a423a13619a82", + "0xb703fa855c2c4e1641d2687717fe8c5061acab71cd2dab55cdb069a6865464c3080f7936ddfd320516b6791b36c64b8c", + "0xaad1cfa7295e463fc3d5374ea4b952020010d67a77c7a86fe2c351a5959cd50df6a0045ad588257567a99bfd0e9400b3", + "0x97906fb82abf5c1d9be8f72add8e6f175a6a5a4300b40295cb5ec8527cc7ec700fa03a7a494122d9605d212457452e41", + "0xa83366cf93ad9a07f617e4002a10b624270f60083559b045ab5a805aaa592ac37b90c1e8b5437158f3bd942cf33bb633", + "0xa585168e157e111bfa329d0ed6651a96509b20b30f6bb0691c6a5875d134d4a284867ab52511cdc19e360d10638e58a1", + "0xb17d480a0b39f2487b7f3878714658fda82f2147c5ecbccd4004eb92d267c4663b42c93bafb95ce24e2f2f0a9ea14b8f", + "0x9362297a1a3951d92db4fd8ea6b48c403d6d8d2f7e7b6310b9cf9b4e4ba9e84cfe1ae025830aab9466c32fd659144474", + "0xb1a62fbadfd4ea4909d8d0714c1e3ee9f95237fde20720f88d5ad25c274a6792158b99966d7b93151f769c832b6a132b", + "0x8d9af736949a33fe929548abe72384281365385862821a584f5198eed63bc5388f89fc574cda35a9eaabed0d336b86b6", + "0x90ee2235f4ec2c6089b5cb7b8a41c9bc39e4a57935022ef28bed490e2ab12680922af7395bda4f708809e2bfc62192c9", + "0x91f3a123d420bca34d3d751119bbebc435630c6605fb59a8d80d16a4895972e56cfe4cf1998e0a527c18ee38c2796617", + "0xa2c4fbb20e7fbaae103b86ca9d8dbc2828e6bf33d1d7ce153bd98e8880fe7ac62abbf7059194b1eee64f4526a36c63a9", + "0x91a7f93310ac74f385f11509f4bea9a4d74f2ce91cf2024fee32a4a44d5e636a73339c6b4027ee4d014a24b90de41ecb", + "0x914a6d405fee0a15e99704efb93fd240105572335f418d95e1f2de9afeb97f5f4b80aaf20bd5bf150b9da9abc2b6d6a5", + "0x9462cf2c7e57e224389269b9fdddc593b31e1b72ab5389346aa9759fad5d218039a4a5bc496f4bf7982481bc0086292a", + "0xb7596132d972e15dc24f2cd0cf55ee4a6cc3f5a0e66dff33021a95e5a742889e811afd1dc0cd465cee6336ad96f25162", + "0x99409bba2548f4ece04751308f815ecee71222869d8548fa142788fb19df5366d093a5131e57560237471bbd5279bbe5", + "0x8e7560988a844b5b844ad460b19c452a5a04346d8c51ca20d3b144a3670ecc60c064b2415c2eeebf140d6ae4ba5c5360", + "0x8cd9e18d311e178e00eb81ca839cfaa8e64e50a197de8461f07135fca28c1d895dd9c2401b923a4175ff711853497317", + "0x91ebf99c95e8f653402b3079ecbd533ed7cd3b6c857a710142354ce8330cebdee7cf0fd0400417883b66055bec9d0552", + "0xa9d0cf8cc6bbdc44426dcb716df667826426b4559056d73738bf3eaa6df373403861b6bbd6fa0454b1d2730e3b0015c4", + "0x928320b452ef21d2443dee360110550f531d7a4275b2cb227814150f3e9e360e05a884d6e3bc4415f202120ea5ac333e", + "0xb9551f2b2e7bb984618f2e7467e33b5b5303b8707f503f2e696e49c2990ea760c31e0944d52257c7a38b553a67cf621c", + "0xb2ec34126fe61345e5c6361fe55b8fb3218cdcc9103bba5b200252d50b758153cd549226b7aabedd265906401e755190", + "0xa8cf814926082a96a921d471036a9919a58e68d02ee671c215ea304759cd92a7c2c9ccebdd5e9ec5572164ad2abb22ad", + "0x8c0563c28c261bbe9a1ec4986f8b277324bf05b4fe5e2b79a862168e646bbea50ce7c4622b2aa7ca899c1a728c226d24", + "0xb558cdc334ea894d3a13347ea9e30f78a0a20621903d6c009c54feceba3ba81d2445a43572e088ae691f65489702e963", + "0xa62ba0b20f46c367cfd409beb300e39f1a6cd5be95e63457b6ad3cb66374aed754fd037b8e4215d651a7d8e1a442f762", + "0x8543e2c6135df471bd7a5c09f1313674c7f6847cb88f15eabf40b2bc9535d0ec606725b97103334a0c162a20d9f5bb53", + "0x8c0367d7058d63b425450f8ee9252e64234c0c2e61878c7c2d4b17bab22a72f40c75ac3bf8b64f264c00d9c5963af041", + "0xacb7207445993d563f1b6e7b179bbd6e87044399f80e6d15980acf7aaccb9d85071fecb22250afb3aba850712fbda240", + "0xb93725e66184bb03f0ab4078c737a7fb2b10294a3a09995958de3dcf5316b476ce9b5cd8d180017196d9482abdfcab88", + "0xafcb52bb7b8f45a945299da6fc6a877ba9f69f7f23d5f94b5f5d9a04c3cf3089333bbd50fc305e3907825003da73b9f6", + "0x961de781cb238cef52d43bc0dc7d8e3a75bca4c27ab37a2e9353137a9aa9403444a5841b595adeca75a3de5485ab97f6", + "0x9408c828d3ed6df40cc167d72ca9882a9c9cf8e765d6f9125e02e0d66ee0ac94f449803afb50bf1b92176feae92473d6", + "0xa85480591e7e033b9087fd0efe5cf3c88c10a75de4a5d7da4443df1cc1fa1aa59b6cde3ce7453fcabe555495e49ef6f7", + "0xa2611bd82344bc5d70d7e6cf3f0d25866b9f709ac4bf6f75d1006da2a11e2cd07a4c0ac71505e5062a04f71db7a3063b", + "0xac466aaa96febb5b810ba350c7a874797ce4bd6c9585f6b9d114d646894a67c9af9526ade4f7ec834d3a69e18ab643af", + "0xb73fc98a79fe77cdbc524c76a09cb9f2d5f8b0a5508846bed1ba5ea9ae3bb62120e01d3b8fb544d90ac9ae0c3d4ccefe", + "0xaed333c3403adc899a870082f70aadc770c9f880dc057f05a46d7400be9d893354121a0a31e5475898f437bf722eefcf", + "0x97f02133c72187178a8c48db26031f0b2c0317a6648d2be5f7450f00c37391cec935bea46b8144ec9fea5327ee959f27", + "0x940b582b41f1d0f09f0c5f51bab471e4eb143e91b1e96dde83e94650421d51f9c9baec10cc802fb83cd63b56d0b907c0", + "0xb1286a55a74a88a75da47671994916be428be1ca3f42783e497d6478eaa6aca69d50a421b210e9ed3283d578b651b8cf", + "0x97cd4e87e21c71d11f1df1c0b6518c00e1610661be4b13cdbdbb026d60fc3f4a2b8549326a648b3fdecb7de8f6aa9fb7", + "0x8f36bbcccee986c35328633bf6ee8f70b5dbf42d0f677c0f4e009d2289976e512af6af91a6ddcd87dc0df93bc4ecd02d", + "0x9253ad44ad182e67ab574d718733a69c05cd5bcc43e6292ef0519a9430460aa6a233fe26269da7298ea88cf406e733c0", + "0xb616b5ea74db0dcf8f10a2db79df6ec3566c06410f68a933eff150194608c591b2b175908d4b4ccaef1018b0fefc5693", + "0x80a712ba89394381cbb83fedcaae914cc4f21ab024b8da8a7bbad7762a22f82940451427b1a3f5d84c246d5ba0c7ccc7", + "0xa806909a5517a970879143ad789c6cb6256b82553b649f6865cdafbbc050b1f86528241b3cb600e784186e1a672b588f", + "0xb6ae801d1f0e4adf3ce57659d7c61f94abd3c8d1635ad28133a79eff0586fc48bdc195615335449e9bfee39e8a955eb2", + "0xb8a000561211844bef72adf3413f3b438a8789fcddf6676402ca6a1c2c63b9deed322030de2ae3a0aeb3cedbb89406c3", + "0x8bc3615b28e33fc24a7c989f8b4f719c914c4c65b35ad3d4cf15e2196e37c62e42ca34e8b1275e0f32589b969bdfc21b", + "0xb2f9637f370a79e7591e5056dac004f56b375f33645ae9f5a192cc6b7b6b3d8a1105cc00f10d8bc8ef250ecc2ac63c39", + "0xb51899978b9c5b737999fee1935a5b0944261e7005bea411b5903d2c16ea045a3b0bcd69395b6733752caed43bc4e343", + "0x873c71a01009dddb9885c48658f83aa6320e74bc152e09de8b631c763c2b4e2e8cbac921418a0d9085ff5c53a2b52d39", + "0x96470f48efd7d2ac2daea8753ef097c09c6fc128a54cc7ef758ff07e32c0b0ac7d122f97b53e88a29cc26874dfee5e0d", + "0x8dd2decbd3504b7961d65edb8d51b96377f4edd2e0d2cd8a4d98333f373c79a8d7ca8f8408718d0e7b5e48255857c339", + "0xb536ae387bdd0f6e40850c71fcaecb1051b2c8f7bf5cf92c6bda030de72a03e9212d00390c53a72a08e9fb2bff1249c0", + "0xb1566076f59064e3545adef74fd1acadc1bee0ae23543c30caf9e1ad1fc20ebe84ee25004c612525b26857253f5345b7", + "0xafd180e25444cb720342923b8897d38a6537bc33a0ca1fc9c6e4d524b280193618f19e2bcfbd07606b78b734fe6114ed", + "0x89b2a6c8811e5a6d07aa74c79dd854bdfc292cc104b525bc37e4c7c1f9485e19d759c8e27cd7cd73c46346f56ce3b189", + "0x8234196e196898b2501b79d0dc016f6df3d5878952cdb8a93735e4ce2ecf77d07924c701e084533a20f0c50a7d1ee376", + "0xadea7ce2efc77711f50138691ef1a2b946aaba08e7e3b21378708dd5a10bae933ed121e71834b43b14e2ea30a7b306e8", + "0xa566d406a35fae703b3d1ea1791d9207116002e5ee008d01e053a1ea4fe5af2feb63605b011ae6a14414028aa054b861", + "0xb83bbb063682386456719179b6f6bbc8cf6f791229600b7d402167737492f99437b45886695b26a28731e952e56f1ee1", + "0xa8f5fffc2c335d3ad5c7593e81f0862351413cc348392afa86d50921dabb929a5a1de20d604666af9e17a13bbc30bc3b", + "0x8d5dcdc1335f01847f6ef650ff64b26e7c4cecb934a7bbce11254e8ced9fa9e4fc87eec55248f69bf499180101c63f5a", + "0x83fec30b8bc62f9fc28301a03ef18158d6364738f1c42de311bbfba2e62b25d4c9ea9d6097698b24c84fff956a6748b9", + "0x96394fbe0c2d03cdaa56e13326aeb62344238ad3043ee2fb4f18ebf0a6f7f090f410032a2d15bfbeca9449202d59f2a0", + "0x94880f5928fe71a797362a37d05849d23e118742697f75bc87173a777e7b9d4383b8796a8a2bbee27fb781f363301dfe", + "0xaf229535896ab86fdf6d2ae676a0dbf44f868f6c7f17bd9a65567631c7aa2e29758f41de050ca5311bd1528bcc811532", + "0x8d4fa4968575b483b3ac16345e7f1ea3f81e8dad72c945a48b7b982054fe1030584be2f89b2f53af84d2490cda551b84", + "0x8052aeb115e4d242078c8726d376a13156cc832705243f14adaa3ef3889e1f2fcdfd46e087acab6fa85a74afde5f5eef", + "0xa1349c8a22788a1937a837fceecfaada9e93a63e582a09c56b53da52c9db1600254dc85f63f5eadfa30b89b31dcbdb30", + "0xa10178cdb263ff1a5e0cc034b6deaa160d00c3c3fe1fd1ff0c55fdf1ecb83d771070c10930f88832b75fef39a10024ea", + "0x938b17e4405934ea5ef29c2187d6787c5ff5d8c9a02665efb453117d462dbc50ef2c202cbc884305cd807a70b5cc177b", + "0x84f01f0da6b58c71788616be71fb3c259ceea7f8bd131a5661c5c03d0205feaff6dac2915919347b0559c381477b3d89", + "0x98787f0a2fac2b04bb7aa247ac77236bbe690aae64203e553be328a2c3bffb772e7a0244e585d27558cc64b089a5ee11", + "0xa14501d8b6b3a84b13b9006d521667e8d168f642ebf154c4e90ec8c75d11985fd0c9d86fc2efa6c7077dafecfdf0ab13", + "0x8215dee75eed04de83a3e910129bee8c48ce01cf1317ea477ff35c09a6f9e9771a8b05aa79e6b0f3e71b9874695e7a2a", + "0x85763c3072c7400a2c5668ef5cc53e6f4b8dff474146028a8be370ca9d8af9bf9ee10cd7d23d33eb6d6e257dd3af38d6", + "0x91bf62245c5a59d514d39bfb74db7f72ca7160c1c5d5be3844fff37e53e99d451e18a6747c65e33f98f48a55f38962c6", + "0x8c68817c6a6ea348d9aedce99929371c440fbad72718c2d239ffcaebb26ecc8a4e8c38c2819d945fdb7f02ffda70a5e0", + "0xa96ce2745866a22267a49faa7ea00ebf009ea8d0b0ca2c233c62759b9d5514306b5822dd2eee0124c9e28380e2f97aa4", + "0x8b18d5757c73843dcd55f0f0dc894bcd17e0ecf4c9fd901eacd38480844a15b4ce5e9598ccee039f9d93185137630cdb", + "0xa5b45c403b6735aaae14389bcee23ca10571f5437f1f5ab0c2b4e573dfd3341c638fff2cc780166af96b118d47ff2299", + "0xac849a0ccd354dd46bf55ea837d509b4ae3eefcbd5b8eb2582d301fd56c27b89950c6eefdd4e98e608ef4a6b75251311", + "0x89f13ac14bb064e9c6b49a482831ecea6344faec490bd18bb44028b83a0f22e21145861558029bd172ba7c5247c2cba7", + "0xaa57b057a2ac32c101e442c33831630c81b2e061a542e3e1d6897b2b7ca8a7241ef717a548b3f751d60d89be384ba5da", + "0x8a43db4e12682b98230364f25c75b49002f5002bd72a1674cf2a9d53197b5ef1b95e48429af98af503b0d5c3e0e017b2", + "0xa10cd7b8e1574d78c4e917cf833d3d845b878e8e8b60312e6a994bd4f391a5e8c38dcd774087b93c9241238f43f80937", + "0x8b61ccb949088286216cd628811df1a362a7f5c333654ce823e63ebd04b069d5b0f627fb6c96d54c7b853de8aab05472", + "0x887b902020ad45f70f2d5bcfa7324fcbe7be09fd2b1bd40f9ae43a89d487986e89867aee0945ea6a0fe8dfd051ffec56", + "0x822fcd260a7876cad31f54987053aab06108de336878b91b7a15d35013d6d4d6de2d4b30397bb6f1d5c1a7b48e9d1ced", + "0x80b89ff95d725858b50e84d825ea99fb6a8866f10b91a5d364671ccbb89cb292bada9537c30dbde56b989c8bdc355baa", + "0xb53cab156006c3a1766a57dd8013f4563a2e8250995dbeda99c5286a447618e8ac33ebf25704b9245266e009a0712dc5", + "0xb6e2da9c1156e68c15861a05cd572976b21773e60fc5f2f58c93f3e19c73ad6c2ee3239e6cb4654040c8e15df75a505d", + "0x8b7e187d473a0bd0b493adcdb91ca07c9310fd915dec46c2c9f36a5144eb7425dd35dfa50feb0e9ef747caed9f199944", + "0x9743ec3917e953e0a420406b53f4daa433adf4ad686207e9f296e7c83d1ffdbf81191b920ba635c85416e580178c16ff", + "0x98d1476fd4504a347c5261012298ca69c8593fec91919d37ddfdf84155b6f1c600cd8dbb92b93f3262da16cf40a0b3c6", + "0x94f50d52982a3c81ac47a7b3032dad505b4e556804f8606d63d821f2c1a4830917614630d943642ba375b30409546385", + "0xb5c0eb5f4cf3f719be1a9ad0103349269e8b798dbffe1b5b132370b9de1188a6d71dcbc3635dfdb4b888400f790b6ea4", + "0xb47fb45ec73392598866d27994c2feb0b0f3d7fc54303a2090757a64b6426d183ae41af16794ced349ede98b9b3fd48c", + "0xb5f45fd0aee6194dd207e11881694191e7538b830bfe10a9666493ae8b971d65bc72214a4d483de17c2530d24687d666", + "0xa50c149ea189387740d717290064a776e2af277deafcf5f0115bbbdc73c0840d630965a4e0214b738d1cb0d75737e822", + "0xb941afc772043928c62e5dbe5aa563fa29882bff9b5811673f72286ac04fddf9a9ed0f9faf348268fa593a57bc00ba6b", + "0x839051a7838937270bdf2f8990fd9aa7d72bfc86cffe0b057aa8eca7393abf16b70d71a6470d877f8ec6771efa5a8f26", + "0x835bc9d049418ab24dd1cbf76ed5811381e2f0b04035f15943327771f574f723b07c2b61a67a6f9ddc1a6a20b01f990d", + "0x8935cf5634d6ae7b21c797a7d56675e50f9d50240cb2461056632420f7f466fdcd944a777437dcb3342841ad4c3834bf", + "0xb5698fe3da1f9d1e176c9919fddd0d4d7376106774aa23a7a699f631566318d59b74ae8c033eba04d06f8cdcb4edbbed", + "0xad11421ba75d74c600e220f4bce2ca7eacb28e082b993b4368d91218e7b96029acfbdf15a2ab0b8133b7c8027b3c785b", + "0x886ef813644599051dafdaa65363795cf34a3009933c469bd66a676fdd47fc0d590c401cc2686d1ba61fce0f693426d4", + "0x8858fdf3e98e36d644257ab6076f7956f2e7eacc8530ec1da7f3e9001036cba7a0855fb5011925cdc95a69600de58b2d", + "0xb59eca7085a2f6dfeaa6a414b5216ff0160fbea28c0e2ad4f4ffd3d388e1cc2c23a32dbe517648221b75a92500af85e3", + "0xabec62d259bcd65b31892badad4ac8d2088366d9591cd0dab408a9b70ad517db39c2ef5df52348ba4334dce06a4e3ba5", + "0xa9acfe8f5a310779509621ed2946166ffb6168e68ecf6d5a3b2f6008df1728c8fceb811636c50d2e419b642a848a9ca9", + "0x9929bb1a3537362848fac3f1bcb7cfb503dac0a0b1bebbfd6ddf14c9a73731e2248cbaf0fbb16c7d9c40cc6737c3a555", + "0x981d06c7431e6f4654e32f1c5b27e7be89e7c38d59c4e2a872a0f0934cb852c6aeff2d2eaee8302131795590b8913f5e", + "0xa6ba9dd43354320f65fd5cdd5446cfa40080bcf3ef4a083a76ad4e6a609b0b088bcf26c4957bfab829dca6064410ca5f", + "0x9367ef28def311c79adfd87e617651fcc41ad8caf047d73ce9a1f327e8871e9b35d5b203fd0c0138e32e2ef91e20ba62", + "0x855d1bb508a9036f42116c8bbb830c576189798baee27c7c3477ef1b1fc5d7b0c2c7203457f1eb48d4b029dd6f646be2", + "0x8539a5d0528d3d601083e162b34cb33b5bf6736b4feeeab4941f10eea127c56b7e0b8d57f34b72f8f674d89c10bf302c", + "0xa3b71a9a9ac2dfcd681bfd8f6a5d9abf5df6950821705bdfb19db25f80d9b8a89fac7a922541cc681325679c629743d2", + "0x8e95929dfd4e5b56e5a8882aad6b7e783337e39055a228b36022646a13a853d574603de5fed12b6c1f2585621ead7afd", + "0x8b05c885575d6894cb67ba737db5915639a6f281bf249480df444ff9f02724e28ed7371ee7ec26d50d25f3966010f763", + "0x90f1a45de0cc0641181d54ee86630b5d182d24e7c30c2615803f16de90ec7c982a00b21f250ccebc2e94ef53a13e77e6", + "0x90f0e97a132092e51a4521c2ecaaa47e4e4f319e67a3cdbd00ed85c2f10dfb69c339bc9498e2abbffcd54b1fdc509a20", + "0xa9995234520cab9d1bdec1897b0b67571b718d5021c0fcf913140206b50ab515273b5f8a77e88fe96f718c80dd9be048", + "0xaebc6495d54d0e45a3c74388891dbcfab767f574fed0581566415af872dc5b3bd5d808c44f6e1fbdde7aa9ffd260b035", + "0xae757f8f4b1000a623a7d8e337a50c3681544520683207e09d05e08a6f39384b7aaadf72018e88b401e4a7bb636f6483", + "0xa626a28d5ce144cc0c6a30b90ec2c1412cbbc464ee96ac49035e5b3a37bb3e4ed74e8934c489b4563f2f7db1caf8b2ad", + "0x8c994e81dfd7a5c2f9d4425636611d5dd72d0b091a5862f8bec609d0cdd3c423eb95b0c999c48faa5dbb31e510c22b61", + "0xa1c0e59e076b908de760d9becff24883c6eb9f968eac356e719c75cce481f2f7bcb1a41ed983a00c1a3b9369a7ff18f9", + "0x8d7e199044fe2e552bc514668fe8171c3416515f7a5019f239c0384f0ade349e88df26cd30f6b67d02b83bf005d85de8", + "0x80190f2255199be690fb502d02ed159aa568c390a684f7840512efc3d2a62f28a49d5d1928ad99a5f975ad81a245acd5", + "0x889d84cefef33f5714e14d558f41d406072ba66b427bf27918b669c5be46261c3de0139610a2c2eadef8e6508e937bcb", + "0xa480a686d5085b854ccf9e261e7f1f2d40d978fc30b62b1a8fa9561127745529405820df21a680ee2258b8cefa5f0201", + "0xae6243400d416a8c13b80b6637726959ef07b8d9b6aff2bd3bb23aaaf97337c7a6b466c5db617bf2798e01d4ccc68e4d", + "0x85e0ff143657e465f3d934ee781de5cbd2bfd24f2fbbe6d65c698cdd93204a845f6ef1fa8941c2578463a06a8a418481", + "0x8f4f8b45f1a9f6c2a711776db70f20149dd6d0e28d125906ba9893c5e74e31c195b0906f04c922c8b556ced7cd3d611d", + "0x877b852c33483b25c4cd8da74b6b589d8aa96e217c3c4d813466c77ef83af95a94a47364aa8421f0396ce631ad87d543", + "0x852cb06bc4222ce125287a7a55a79ad0bf55596f26830dd6d79da3c60f80e3ba7b9a9b42b126dcb99d2cb9ce142783ef", + "0x810cd64c1dfce85d509eeb57a5c84efafe1d671454ef601a040de8d46fb33bc419577f6a6c404e28ffdfe315ffec558a", + "0xb60ff8bc804d101a32079b8ed52285fdbb47fd60c3c15cef17cfe7f6b0567de6b50128b9dbc49a1d9811b62b22c99143", + "0xa9df7068b26a6a58f7a499e67b17d34f2a2e8e5029c6e51e2b4c0d19324fb5cd9734c4c4d5034e1bfc274cd0c74a82d0", + "0xad93c50802ded1e21217a58b874c074ea52322492d589820691572084d8edaede8c2ce8021c6df8c0060f395f3c25ee8", + "0xa17b98e090f7ef5800477132b436c1fccc1802f34956711bfc176e36890c7df95a108e03f34659142434cbd8aee9dccd", + "0xacb14aea5575c293dc0a2b58c5350390801d57e9bcda876d87c56565043ddde1a544a88b48ad0d8ec3d41f690aef801e", + "0x88b8e26cbc83faa053fa247e26c95d1bbb77955b336e1b0e41d080633248238de8adc9b98688c98fdfc67e7286bc5be4", + "0x899f69823cf1b2204c8da91bb4f943c04d943137b08b1c46e160919e3378bd22a666a079a66e63d81c05336c742efdd2", + "0x8d7ffbc0b47a32408c9e88676ac4f87683cf37c37d214163ca630aec2d3cc014d88caff35022ff3b6d036eb8343d52a3", + "0xb7760f27db0704a6742855998a0c31333bb34d60ddebc95588e25b72445ae2030427aab088ec023f94563118980f3b74", + "0xad06ecc0f3745861c266bf93f00b30d41ed89d41e99ab63fedd795c970d3ad40560e57ab7333883a72e5575a059df39c", + "0x8687d28b1cbc8aa34a0e5dbdb540a517da9bda36160daaa7801fce99754f5d16eda3bc8e1df6b0722cfb49e177e9bcb6", + "0xa38332c3ebbd7f734c8e6ab23ae9756f47afbf7d1786fe45daebc8d7d005d6d8fd22f5dbd0fa8741e1bfb2014d3f9df7", + "0xb86f84426dee88188be9c5cc10a41599e53b7733ba6f2402392b0ea985effc7525756ca1b7b92041ae323337618b238f", + "0x958731a6f1881f652d340832728bc7fadd1acebd8daebd772b5acea634e9f7b7254b76d38a7065ea1b2cdea83b18a54f", + "0xadb90bff1f0d7d45b8ba28b536c0e0f7f4dc4b9a0354692ecf29539631d7a57d308db3e438e0f907810234c490b42153", + "0xa5188c775ad76617d3bb6e7f1f3b2449f48b7bb7a84035c316284396529564a227e3b9762a89c7114fa47b3ca7ba418a", + "0xa3826ef63c98793a5c8c5d5159e2e00cc85fb5e5124f06421b165de68c9495e93c2f23cd446adf6e6528967aa3ed3909", + "0x80eab97de89f3824ace5565b540b229adcc6ef9d2940e90de185af309234cd8aa4ae9c7ce1b409b3898c8fd10c8c2896", + "0x8824f5acd4c2330c459fdb9ece9313263a8b20419f50f8d49958dc21754c21a77bcf7fbf3e0041f78d8fb667a3342188", + "0x95091cf06911a997a09b643326c2fadbbe302555ab2521db806a762a5f4492636507ca71d7a093840236ac3c096614f7", + "0xa392c81a546196d7e78b61f3ceaadfb2771d09fe43f862c0af65f5e55ce490a0293b9ab754cb5ab03ff642a9a8213a23", + "0xafd76cce1dfa2c9e4af4f840376674f090af37d8c6541824963373f97b9dd1f405c50b2ff56165e1d4dde760e590738a", + "0x8fc4f513d3b40c10872603e1c29a4b2cf4c99320962644ce89f69ffb57f844344e1d472b2d43559119bdfb5a2c21749a", + "0x9951ca8e13b9a2b4a789e851c04c4f030470772da62f101074ef304612e9653b43b37d2c081b5d0a09196b3a167f5871", + "0xb4f16fc2a113403ab5fc1b6a9afddec77be7406413b70ee126f0e84796168a572940550d61e443e5635591d4b6c46ca9", + "0x8d71452cf39e7345c7298d514b9638a5cbe78af7652f0286d42632c5c6d7953ed284551fb40c77569a7721413cdbf79c", + "0x953625b58d52a308cb00ad87c44a3fd936786ada44000d45bb609ea9db6b156a0d0f9475e13ee5e053eaded19a09990a", + "0xa0983a3baa278ad5f5de734eb1b65a04f668408994e396fb0b054991ad2e56e27ac522b04fe37c9583b754e344f795b3", + "0x8eaa454257f77a6754b2c1c5ff0036fa5b03e214576fabc657902c737fcbf298b1795b43c5006e18894f951f5f7cd203", + "0x90183fdeae2ce2a295a567fa61b997b1f975d1be7b03d0101728cd707bb2a7111c222588ab22e573518fa1ef03719f54", + "0x8abec7f31f6b897a1d497368a42733a6bd14ffbb8b21d3e49fc4cd3c802da70e8886827c1aea0b18d1b44635f81ec461", + "0xa6d1e6fd24b0878ff264b725662e489451c590b2aadaf357d64210a3701fe763f529826fa6e0555267c1f5ecc2c52c05", + "0x8fe6d2a4ea0d91702cb2a8a1d802f5598f26d892f1a929ff056d2b928821e4b172c1c1c0505aa245813fe67074cf9834", + "0x82a026a408003583036f16268113ca6067ce13e89c6e9af0a760f4b2481851c62fadeeef0d361f51dcd9fa5674ec5750", + "0xa489a574b862d4056091ef630e089c163c16c2f104d95eb79a27ae1e898b26d6c1adc23edc1490f73bb545d3a6e3b348", + "0x939d85148547fc7b9894497841bd4430bc670bb670f0efeac424b529a9aebf2c02ac18a9d1402a12e4e590d623de09f0", + "0xa3ab52cf911a2ba7fb0cd242d7778ec0d4fa382960c9bd5b476bb1cd44ff1430a3871bbbcea0a0db2630c39ee639fd1e", + "0xb7629509d8c3a3b88b31f1af137a25c38f536284f11a5bbbe0d05b86a86bc92ebbf70f17c256dc8b0d48374e1985e6f3", + "0x8a8647ff33e0747dd6c6ceddcf7938a542656174a08a31b08337ea49b08d814e75f8363fb51676a2cd2746569e3bc14e", + "0xa7a7f8d94d32b7cee00b3ff272d644b8dca86b8da38c726f632c2bcdfa0afb13fd0a9a5685ddaeb6073df4d9cfa3d878", + "0xb7136eea8d05bfee2265b0e9addb4bdf060270894de30d593627891584b9446b363973de334b6105e0495cf8cb98e8f7", + "0xa9fcd33ea59315ad7611a3e87e8d1fd6730c8cbeeaebd254e4d59ed7d92c97670303a2d22e881ab16c58779331837529", + "0x965fd41741a0d898c2f2048945b2aefc49c735228c25deaf17fed82c4d52cf3f8e93b3fb8825ade632dc4940311b1542", + "0xb9f400a2c7ca7da8b36470ee5d26c672b529b98e6582012cbfc2a3c24b72e73f5633de4265c417c0d47c474155a603c6", + "0x85f333b0b1630a688a385f48bf0175cd13ecdd92fa5499494f4ad5aea0ef1b9d180fad8f936018538d842630ff72884c", + "0x8da95a735a1a98ed8e563099bd87d13a237dd7ec6880cfac56c6416b001e983a56f3d72dda7f68684bb33e4f64cadd30", + "0xa29b66a2095e1acce751f6aec8dfeae1e5b24187dfedb5d1635ca8deae19b580ef09329a18b3385ebb117cd71671f4dd", + "0xb001deeeaf5eaf99ac558c60677b667b9f3d57cf43a2c4d57fd74b125a6da72ea6c9dc81b110655e0df01ca7b8a7a7ed", + "0x912e11dfff77c778969836d5029747b494dd81d9f965f8be2c9db9e8b08f53858eface81862c3ee6a9aa10993d0d23f3", + "0xac166a00e9793cf86753aa002ca274cb6f62328869fe920f5632a69a5d30d8d3ce3f0c5487cb354165763ca41d83495a", + "0xb74df519ae1a8faeff2ccd29892886b327c7434360ab5c5355752667069a77d466a48cb57b1950d10b6c47c88b2a8538", + "0x8751679aeffa39da55f2c2a668f7b26fb8258f70c5454b13e2483e3ad452f3ac7cc4fa075783e72b4a121cd69936c176", + "0xae0cc16848b8bf8fffbb44047d6f1d32b52b19d3551d443a39fb25976a89d1a5d2909a4fc42ee81a98ad09d896bd90a9", + "0xa0c8acd6a2f0d4ab0e0a680fa4a67b076bbbf42b9ec512eb04be05fb2625f6d2ed7b4349eebe61eb9f7bd4f85e9de7fa", + "0x85c629ce0deeb75c18a3b1b4e14577b5666cf25453a89d27f1029a2984133a2b8e7766597e2ff9ee26a65649b816b650", + "0x938dbb477840d3ed27f903d09fd9959f6fec443fbc93324bc28300dd29e602bd3861fd29508da0dfdbb0fff7f09c5a6c", + "0xa7c76cd4a42ab7904d036fe6637471d9836ad15d0d26a07b1803b7fb8988b8c9edf522e0d337a1852131d0f658565ae7", + "0x838a30260cf341ae0cd7a9df84cbc36354c6bc7b8f50c95d154453c9e8ec5435d5f9b23de2a5d91b55adde3dbdb755b9", + "0x8f870b1f798c0516b679273c583c266c2020b8dea7e68be4b0628b85059d49e5a680709c3d6caabe767a0f03975c4626", + "0x89bad0b6499d671b362ae898fee34ad285aa8c77d33ca1d66e8f85b5d637bbd7ae2145caae7d9f47e94c25e9d16b8c4f", + "0xaf963d3dd3d983864c54b0ed1429c52b466383f07a1504215bbf998c071a099a3a1deb08d94b54630ac76d1d40cfc3da", + "0xb5686de207c3d60d4dcfe6a109c0b2f343ed1eb785941301b827b8c07a8f1311e481a56a4baab88edb3ddc4dace6a66a", + "0x95e5978739a3e875e76d927f7c68bdf7ab20966db9fa8859f46a837760dfe529afa9a371a184dfb89d2962c95d5fcf3b", + "0x96d2855e20c37ed7bd7f736e11cfba5f61bb78a68303a7ced418c4c29a889a4798c5680be721a46d548d63525637e6b0", + "0xb134bceb776cd5866e911f8e96016704c9a3caeadcabd7c0f37204497d789bc949e41b93e4c2d597e4c924853f1b21e3", + "0xa1949ff397013acde0303e5d64432bf6dd7f01caa03c5fc38e7c8ae705b9d5c2646b4b02d013004e5eb58e344703260c", + "0x8036a5f79d8aeb6df4810974cf8dbd0ac778906d2f82b969ac9dcfbe7ece832a7e8aad08a4dc520f7abeb24b1610ae84", + "0x982b6b0af8602a992c389232b525d4239edc3ae6ceea77d7729d1fffc829664dd647ff91c4cb9c7f7c25cea507f03167", + "0xb34c7d24fa56ab6acdb8af5b4fa694a1985a1741cc53a2b0c5833611e8ed6fb3b663a4d9a126bb4a1a469f2072199d66", + "0x8166366fec4ee2b3eda097dc200cdfa0533a742dfbe7082dfa14c1c1ecafc9d9fa71f518476634f29d06430869bd5e02", + "0x86c0251ac00b8200618c8b7ce696d1e88c587f91e38580b2d6ae48a3ef904e0ba1b20b7f432719ca40e7995f2281a696", + "0xafd89f3bc7843a1e45ac961e49c1971114c5238d9e21647804b1852b8f476a89c12d1edfb97fff71445e879d6bfd3b70", + "0x911d8bec4d4c3e73a2c35469b2167569f59705404425bd95440408fb788e122f96e9b1bd695f35c6b090f10135b20cd3", + "0xb3f6350ff7afaa0660f9dddd9559db7f164e89351a743fc695d987c88f89fc29136e3c5eb81963edabf2b6f2057120be", + "0xa371229680d1468777862e9c0e864156f9cd7c12ce7313a8de67b7bd34e3d1b6fa45ce891a81f8316f4afcbdecf3b6ca", + "0xa6a9a875ef9efe8ba72523e645b5773aa62c4fb41efd23da3fa38105472308b8d293be766342ee0a2f00758825bd3b6a", + "0xa840d495a184f4499b944ee08f07193a1e1bb8ab21f8ce7aa51d03bd8643f2bc2616c17b68d3fe7c0fb364136926a166", + "0xb55200ae7d6ebb0b04b748051c5907293184b126cf8a1c2f357e024f1a63220b573e2875df83d9b5e0c6e2ace9300c40", + "0xb1e0870f2e3719f42a48256ee58cc27f613308680f2d3645c0f6db0187042dddcfed0cb545423a1a0b851b3a16146d70", + "0xb43a22ff3f838ad43786dc120b7f89a399ed432c7d3aa4e2062ad4152021b6fa01d41b7698da596d6452570c49a62062", + "0x88b1dc50873564560affaa277b1c9d955aebdcdd4117dab1973306893b0e3f090899210102e7e1eef6f7cdf2f4e0e5db", + "0x9223c6246aa320b1b36eb1e28b5f9ccc2977e847850964f9762c7559da9546e508503050e5566ccb67262d570162b7a3", + "0xaeeed21b932752709f43dc0c2c7d27d20263b96a54175dd675677a40a093f02bba80e2e65afe3eb22732a7617bf4ff9d", + "0xb47cae580ae84f4e4303db8f684f559382f075ef6e95698b9a629e92b67bf004f64e7cf47e401768fa170c4259efbda1", + "0x849821e1ead81fe2dc49cd59f2bba305578c4ea0e8f4b8ae8fc275a1c4a6192f8819d5b6d7da786c94dfc16aacf3e236", + "0x8c60d9a8baefc72a3d3f9dd2e24cca40fb5ce36b19d075122391d9b371c904a0a15d2196c0f2ac9da3acf188d15b0fe8", + "0x946edfe168bbe5ddb0fa6c2890bb227d8418bfbebe2bafab84909825484f799407b610d8aab6a900c5ff9eb796cdc4bf", + "0xae7bf8ae71de5d7ea644d9541e49da1ec31eca6ff4c3fbec5480d30e07ef2c2046cc0a486af7b3615a6a908846341e99", + "0xb4d31a6f578463c9a5ccde0ea526c95b1981eb79468665395c0e550829abfdfa86689699d57830856e324092a423f231", + "0x93415ad3a732417cca9771b056ed42db7ce50879aca7c6f71883ad297eaf5a37fd4641d44a0b7e28b90c168834141340", + "0x98960617a413a3ba86d8257a7386355a69258943aa71834166bd624ea93b0af06178e86538e237f88fd039eacf7cb04a", + "0x881335200a487545e38d5b1ffda3080caf5729e1b980603bcdf9ea652cea7848335b83aeeaa321d3476ae4a8d9073582", + "0xb39e84c14666d51895b7a8341fd8319f9e0a58b2a50fc3d7925cce3037f7c75367b5fb5bf25ff4720c9992cab7b8b9f4", + "0x8ea4bab42ee3f0772d6bd24dff3643d8b61147b46ada374414d8d35c0c340e458e449d31023d96e66decf9c58e30cc34", + "0xa5198f6759a045b6a4ba28e4bc3bb638fad44c5a139064327580e285adf38ea82a7570acebf925e81a39d9025f3a6f2e", + "0x80267097e2d27c1b19ecf95d184dcff822d34e03326b9fc139a4f8b75b3f80777bb97a9dd284d9b755f14dd401d63c0e", + "0x946f346220bd3b6f733e94b61a1ad0b44e45c356fa6036dde5882d93b5613c98e23b20e91eddc6b3c5acea38085705af", + "0xa5f559e110cad99bbcae2d9362434aee7db0f3b6d72311291649dbda3f84c10e9760b66b988db3d30067bf18ae2e5238", + "0x8433b38e5c7b293ef532f8c70cef1ed9be7f31f60d5b532e65df7d2885203be78b7ad78ab3011bc54cd9f64c789bf837", + "0xa5a4c0a9b0e0b6bb912cf6ecd30738b0acc0146d77442449b486c3f32d7e60244f643a5cf9cc6da2de5408d0c5f17691", + "0xa81feb329fb51b72464bddcfcf4e02149d995b548d88c64ba143144ce16b652c9913c8ee948ee837596ec97cc43d8cc9", + "0x88e5a7e93a738d61330425bc21ade88d33d7160d124bf174eb3e12a00283654431036977c4f1a47a1bbbf2ef8449ac89", + "0xac75ad7c099383069e662bfd3624b92b64b5838246902e167fc31b9411efda89b2c6bbd1d61b9eb7d304faacf438d70b", + "0x8583bcd1c7cb9bb4bb6bcff803b0a991912b8403a63c0d997761ff77295ccc357d0292318601a8c61329ab28fed7bb83", + "0xa1f9aa0523f1dff00023a44a6c3a9e4e123be0f6722a1c6682ac3c6047efe9e62f4773daf4767e854e1fcbf8ee7339e2", + "0x85f65ebcf5c7e574174b7c4c4166a9a5368e7986b8c0ef846c2e13b75dea7311a87483503149ebfb3cb839b3ef35c82d", + "0xabc55eeb72699031a367b9675a2b91a8434e1f01467660903ced43a0b2a11a85ebdf48f95c13ff67e4e2958065a50ff3", + "0xa4ff77c9b86939a15647499b9412417b984bfb051e5bf27b35392a258a5dac297bbdbcf753a4be6729ffb16be924a2ff", + "0xaf0d41c15b5172efa801cc85ed101b76844dcd06712d0d21160893235a2dbedd15d187a9b31cf0d0ca6c14de6ab2b707", + "0x92661339199f18e5dd9a210783c1d173a26dfa315bd99a33d6f04bf506c871a2b47745c1909faa209d5e6c5c645124a4", + "0xb35813dafb52df709dfa47982bfb44e1bf704f9f46085b2a0e92511dff90e5597110f614f8915830821fc5ed69ae0083", + "0x934a05aa713fa276a4d47f1a28ef06591e5a9a69293c1651c223174df0af4927fc9cd43d374d89c1b4f7c8dc91abe44b", + "0x8f83a0ef05202c0b7170ac96f880135e2256fdf8964dae5aed5dd0f6452a6d8e123321e8c182b3aa6f1f8ab767caa735", + "0xb92db10c21c321cf1349fd34129d7180e5088daf2bbe570de6427299aab68992c011c2e2939a44247396f5427c1d914a", + "0x95ce1892d1ce25ef2bc88a23880055a4d829a3b31f3806635fd49bec32cca4e965b129b6dd3e90f7e3a2eb293ffc548d", + "0x970cf816ee7501ade36b0b59f87c7e352957f67f1f75bbacd8ed52893f9fc40572c76f49c23db44866af7e34a63cd3f9", + "0xa2fcd08581d3569fff699fd7ed1ede5f98f2b95956ecdf975a29af053d9f4f42600b3616ad6161e958c3ce60139c20a4", + "0xb032688b6cc8a7e63dcb82694f71f087b1ee74c4d5fa27323b1ead3ba21722d7fc49eda765725b5553db5260005049c3", + "0xb0b79e4329f1ad25ef6a603390baf889757cab5af10bfa6953a61f89aaace0442b9ef08e57ba778f1e97bf22f16f0ace", + "0xa2e6ac06f8973266cd0df447f82cec16614df65174c756e07f513e2c19aa82c10d8670047860960cfba3c5e4c42768c8", + "0x811e66df0f3721a1ae0293549a0e3cd789f93fb6be2cab8e16015a6d52482af9057b1b75e9456322a5a9e87235e024cd", + "0x8744a80b3d9e37da4c50c536007981a4958d7e531cb93916dbf985cdc22f4ff482a5cc4fe50915c049d2de66530f1881", + "0xb20b6e8c7be654c23c8ca440be2c37cf9cc9f4e81feedfd0cd7c56f37eda8f295fe5d415e9bac93d5f0a237edd8bc465", + "0xb33fd84377f31f7819150d464b5eb3ef66e06cb8712665cf0587d61e1b1c121d11cc647f3753bbc18604941c77edbc1f", + "0x83acb8a3ec5f477b6d44cd49f9e091bc2bf7c9dfee876cde12075a7db9262314cb66ad2e7557114e0c19373e31c6eff1", + "0xacfe4172327832ee207eb07da9cd37da3b009c776f7a8290529f0249f58da213254baddc7c3074fbaa1d226ba1e52b7c", + "0x81911b4dea863424b9d77a981987732382702e0294d8c8e1ec48e89678ecb0e64836b45205a120885fa8f8a3a4b9d4b0", + "0xb11f61b1302579a11077bb2f1f0db371ab943573b261be288dc76172eee8a5102b992a5b526092d160ffd20aac2d4856", + "0xab491f7f1e002a44944c02537f365e525ebb6d5614bba8e5e8e8bd12064c702a1759571ddbeee592a0ba8b73cfce8810", + "0x89211da3d92aed6b111de001b8b5a9231a1c2d09fb1cd2618ec457b635a6c8590fe119acca42fce76dce791c35b889c7", + "0xa5f076c8f7164bcab8af59021ef97a0afa93d0877e52241c3ff5a9a9f81227a55c119ed6a84d34b196e94ec851ca5ca0", + "0x80d91417d0d6c1adb5a3708165da1d54a83caaff482a4f65abf3fb335cbbc738c74ed19a8c451ca98befdf9b2d8b5f90", + "0xaecba33a67f66401614eec5fa945e763da284edb9dc713bad4ac03972630781a09a3e2a291aac0605a9560c5f3444de5", + "0x8a0aa1320bf5217a049b02ad02a4f892bfd6a3f5b48f472041d12f3aaab8dd197307f144f9de5f9e762c6b4971a121b4", + "0xa4120a569e446fe4129f998e51f09c1cc7b29dc2b353d6f6f05daad1a4ef99acfcbaa4950a58aacf7ee1b3fde0af33d0", + "0xaff71370d58b145758a5f24cf3c0c6667d22a1f950b8137c369fa845a5265cd645b422f24fa95e1cd7db1d68686120b6", + "0xa839f075a8a702809a51fbc94595eab4f269a2e7a027aa1f4fc472e77f586138bf5aa4e5570a560e139eb6cda4cca161", + "0x9484f1caa3e35cda0e3d36e43aff3dd8cf45a5a51fc34aafa3a63ed3543047ba9d6af2a9bc7c201c028499e6b4c41b28", + "0x84ddb374c5c9170903bb3e1054fad071b0a147a9ca2ebe2fdb491ebb2431d53b398872a39cc385f973e38579d8e60158", + "0xacaad8babaeaeb52c5b5a16ae689fa5ae15846f2d1f3596a52371bd8681819603822ee8d32ab8cda1bd5290d601e483f", + "0x946b69ca5361b60c3dc31db13669b05e5c0452f3c80e7e185f9667a36f351e9ed83bcb5c6dd2439ecd4490e3a87d260a", + "0x99f457221ac40df86f9b4bef0bf8812720b2f7218273a0aab08c4d4d4fb18a0fb0ef6ba9bf7fa53c116cc6f16742e44f", + "0x8bc0e812d8b718dbe48ead74a6bc7bac68897d01d097422be04110a25589bacd50d336d2c8b70d0dfde6c1b8bc372dc3", + "0x895d118dae2fb35a4b0de22be0d000ec0f0f317b9494db7c12f10d7db81b6f3eaf6d6f3fdfe952f86ec4143d7469368d", + "0x893bf3d7e579e800526bc317438a69590d33759931830daf965cec721baa793ea335e9624a86b84b8fed5effc3e2bbac", + "0xa112d30dda88c749ca15d6dc65bcbc7fe838b2d25329d44410a9a96db195c7ce6a6921196a61ba7c9d40efdb101a164d", + "0xb88b5340af052fc3b8e1a8cf7532206801e79d878f1fb02b32ac4f8e91b64e0ec9252d808b87c4579de15886a20aaef1", + "0x865f76475bb5da18c6a078c720c7b718e55d310876c98017c30ac31882ae347258b508ec34001918324250241d2df5b7", + "0xb6d8a15913eb1714061d5cacbd0bb05edd83ecdb848a89b864e7411598e9f7814d0c039ebe4735437c8370d2ff183751", + "0xa95fedce8351ae9c24d7fa06ebc5cd4e3aef87afaf04a7150e561a6a7f2347bdcec1e56b82d6e5f597fe7124f6cc503b", + "0x8526004ca0c802b073d50b0902ea69975949e7567b2e59ca2cf420bc53d91951d26096f2abb07a2955a51506e86488dd", + "0x99ccecaab68b6e5adadb9c848cb577de7e7ff4afc48d3b6b73bc0872730245b8a1c68cebf467074af6756d6226f4f4a7", + "0xb5497d5c0cd79b7e6022e295642e1f2161254379eb78ef45e47f02c84ef5a3f6b6297718e4fac8093bf017287e456917", + "0xb6943f30012b2093c351413c2b1b648afc14a5c4c0c338179d497e908451d2779919fe806181452ed386c1e8f8e8c25c", + "0xafdb56ce89bcd3247876c918cad68aad8da65d03c7c73ccbee0c4c39f3ad615aab87ffa0db5b3b63b4cc915d0b66deb7", + "0xa44659d7be2f11d4d4949571d7bf84a6f27f874d3281edc34ef1098d321a4dcad9a42632b39633f8f9d20a39f54a2464", + "0xa3e489b4db5832280dd58c62120262471b6fb4355c2ad307bd17c5c246b3f1e1b00f925930f5f5f6987de234fcbb7d16", + "0x87a4e3a190340ed4949597703083d338e9c17263ba8a39b67100589f0dddbc420d9557f9522c17c71ae04b76876f8db0", + "0xa35a3978e928eaac8c182a0a613c611ae7b4827c5e999f938eed06921c0294befdc21d02e68d035a2fc8d03c82641126", + "0xa6898d90265dcf0fb215629f04b07c7918e022667583efe0bfe02f258b446954876c6ca9e369ffe1bb079e2314ebda32", + "0x922fc52e648b6b2b6768c079c67ab425da72907a46add801715f8a2537280869d7071d527b833aa63ef562ce059a392b", + "0x8acbb7c4297196d8d1c131040c34cc7064656a148c2110b19c672abb094b1d084fafe967f7122ba9dd1523a4eaec3b42", + "0x82dbf2cdd581fe3b81b156792228eae2485710e6c21dd5fd14614dc341bb0afbebbc0f32340eda9f094b630afcfc17e8", + "0x907a095dca885da219e4558e9251ec765cf616e995c61546bc010963bf26f2d8adbd9b2ef61f2036e1740a627c20fbed", + "0xa7a83f849691d04640137989a2d0c90a7ed42a42b0ad328435d7e1fba557a27a58eec9170ab3d0099ec97da0c950765a", + "0xb7d435a801c2a5652cb479027f2c172eafa3df8ca0d896bbb9d49a42c42660fb382a8439bfed09ddf7e0214cb6066761", + "0x8bc6b5e79af5512589f90de8e69bc858277055cf7243f592cc4edd193f03f71d16c9300097ddafb79752c63f135c884c", + "0x913264fca800467bee58a429e1f245ef303f5dbeea90f0ce6bb3c7ae6d1bd0f99ea75d3d309634684d2178642c81b5d8", + "0x83ba558f9c23b785a123027c52924a1d7334c853a6165d4f5afd093b0b41951a36860ba0a20fa68f73d7db9df0e3ef38", + "0x875b2df7cb54ecdf7ba31181b9dc7dbe02761ab8ffb61757d42a735c8e20d44bad5b904e76dcec6bb44883fdb9f4ad84", + "0xaf3dc5d2dd29565de8f4c700d5f1ab71dadb4351f06e9ee2eb5ee7a9b5da827d0c6726c6dc780748a26aa3b4d10e6c2d", + "0xa113ff09296b25f550f6d0d3f37dd4517b14cf6d5517293bd3068aa3aea765a8640fcd4bf0ba96db5c00167267fbd574", + "0xa138c5cca485b9180ef091c9e327982bea203c165cb83564f416c36e813bea1ef1f6345f57c8a591df360541b7b758f5", + "0x85793441e917ed520d41dda6e762269fb9f9702e5ef83cee3e90652d324536bf4233425cd05b54a383609076ab84ea13", + "0xb422ac9de53d329e6321a8544c264d63cffc37965d627d7e180a999c3332644e21fedf10cd2f43cf6ba4fc542db91155", + "0xa85d31d4bfa583a493681e57bfccca677ec5b85870a53de37f7be7833b573f8c8dcf029cea4ae548d83048030d77d56d", + "0xab8a0702a371db496715a4ee8fcb6d430641b0f666d7fe3ef80c09df0bf570293cec1aa1675381c6bbd9ecc1f7cdccf9", + "0xb308ef2b87438d35957191294782e9f5014a3394fadad3e2ccaf6ebf20fd889a36dbb8ddb3634baa8e2e131618aa4e70", + "0x919e972e5b67cd65f377e937d67c27b4dd6fd42cfe394a34a70e8c253a1922f62ff36b9dcc7fbbc29b0960ad6a7fde88", + "0xa0e4d4be28301af38a910971c8391ef3ec822ce35757226a7fd96955cd79afa14accba484ef4e7073e46b4b240a5863f", + "0x9422f6d424c1736b4b9bb9762aa62944085e8662c4460319dac4877b1e705aa5cd8b6b3a91268363ec3857c185685f4b", + "0xb7cf9f2053119d284a37df4e4489b632594df64e5dc846652ee26b4715e352e6333118b125021481138e4ec3e9f9987b", + "0xaea983e81c823472df8652654be8a60a8bf40147d599f87e323397f06bf88c98e9c6db0f28414f6ea4091f3eb0f6a96d", + "0xaa20bf03cd8b6ffda09fe0ef693fc0aaa3bb372603e786700e52063a4f7ee742771c41cf5e67e6248f99b7fc73f68dbf", + "0x8748a4978198071d7d5ddc08f8c8f0675d895dc19df0889e70bd86d44c469c719b93f6526c7e7e916c7bfeb9a1379aaf", + "0xb8fcd863d55dab2f7b1c93844306e00056ba17338ddfa3f02689a0b58b30239beb687b64c79b8420ecea8d0d082d9ffa", + "0xabb1a35952dc8a74dd1cdbc8ae7294c6bfd1910edab6f05c879e9ed06c636a949fe0017ec67f8f6f73effcb5817cccae", + "0x8bef43422b1c59e354b7f46c08a8eb78e26c4d01c236a4fe781cefb7465293a4444f2bdc68c6a221cd585a2494d9a1d7", + "0x93527258940feff61befa18fcd6626fcff019d34a3ac8c6886599cbef75b15c15d689e8c1bd2177cc93c4c1792dee8d7", + "0xb7f114eea99c8278841180ec8886ad2bab1826554a1657b9eeb17aa815f31b59c3931913ddec40aa9923bc92f8975635", + "0x91a96446158b194a0a6ada2e37c8a45f3017c34034f757245f6f3b98c65d39d084e74d2a9dc271e5918faa53990ec63f", + "0xaea4ada0a853753db03f9790e20bab80d106f9b09e950f09aeaba5d869f0173bed673b866a96d6b0dd8123a539caac9a", + "0xb8e3e98ff0d3e512441e008a4a6783233045a4639e0c215c81984846b43ff98de99d7925cf717b1ca644f6229b6d16a2", + "0x8987ef81a75213894e11e0310e8ba60fe06e2b264cc61655e5b51bf41cc8c3d6c10696642ea3517770f93be360207621", + "0x8d4eff7335252f74af4a619c78625fd245df640f2086338dbb6c26b059f83fe70f3e81f5b6c12d62c0f784e572d56865", + "0xa56f6389b0bac338f20c615d7d11e16045a76cbea23ced0a9d9067f538421c378200bfd4523b7c96094ab67f47f98d42", + "0x83f5ab0727fd6ce8b3370ce3fac1f3a9c1930ea7ebbd16be61cc26f34aa1291ba4b5f16729d7d4f5924eaa4a1e31a04e", + "0x8cc62366874bf8751067a526ea32927584cef41174e2ec5a53079ee557067bc282f372b831cb2547c5e21a2f178c91b4", + "0xb609e141006dc8d8649457efc03f8710d49abb34bc26a33ed4e173e51b85d7acdf18d74aed161b074f679d88f5aa2bf3", + "0x873c7aa784c17b678443320950e494250baff8766db42619b9fc7ec4c3afa4eee290cd1f822b925d5b9e55c9cdd1af2f", + "0x859ba787f052d3665481c3dd58159ec8c238d918fb6d2787ebe275ef9acd377cb7aaa03a69820c78247bf51afee3d5bf", + "0x8eb1e6d2b0f51a3275b4a8be96957cb2d518b32c815dc0dfd5f75340c7dee73e5edc45db7c7d375c4ffaf8c59767d0c1", + "0x85f3876ff5edbb826a9592e68db3dcc975725bfdda4fcac197758a8b27e4f493e6c531b1342ba0f5a75f965273720345", + "0x8a1272f2678d4ba57e76c8758818965e6849971e8296b60ff85a522feeaaa3d23d3696c040d8bdaf1b380db392e988aa", + "0x85002b31ce31be7cc8757141a59a7cf9228b83144993d325b2241f5bfac09a02aca0c336307257f1a978c0bbf79fa4fe", + "0xb96bd26a6bbbc705c640285fd561943ef659fca73f25e8bf28cfcd21195752b40359d0edca0adc252d6e1784da267197", + "0x936cfe367b83a798ab495b220f19cfe2e5bde1b879c8a130f84516ac07e3e3addcc791dc0e83a69c3afc225bed008542", + "0xb1302f36190e204efd9b1d720bfaec162fcbba1b30400669dbcdd6e302c8c28f8b58b8bbde10f4512467dd78ed70d5e0", + "0x8291b49f56259c8d6b4fd71525725dd1f35b87858606fc3fe7e048ac48b8a23ba3f0b1907b7c0d0c5ef6fa76cddc23f0", + "0x97aca69d8e88ed8d468d538f863e624f6aed86424c6b7a861e3f45c8bf47c03e7b15d35e01f7add0a4157af171d9360c", + "0xb590d896e6b6f2e4dcffebfa67fc087fa518a9c8cb0834a5668cabe44e5c2b6f248f309b9cd74779030e172dba5d9e29", + "0x97e7099bff654bcb37b051a3e8a5a7672d6ab7e93747a97b062fc7ae00c95deef51f5ced2966499217147058e00da4be", + "0x83435b739426f1b57f54ebad423939a68ad3d520db8ca5b7e28d1142ebfb4df93f418b180a6c226c0ca28fa0651163a0", + "0x946c9144d982837c4dbc0b59544bdbc9f57e7c9ef0c82a7ad8cfddea78dedc379dbc97af54ba3ac751d844842a2990a4", + "0x90ba1eff9c25adba8c3e6ef5b0d46c13de304632fec0646ee3a7bee69da2bc29e162dd3fb98a37ed1184ae5da359cf0a", + "0xb17b7a5c0a48eb9784efb5ff8499230b45efeb801cf68e13fe16d0d308511af5aa60e3b9a5610f96d7c2242ae57d455b", + "0x9991245e5617c4ea71575e5b2efe444f09cbbed13b130da08f8e9809d62512e8298a88d41f6aa3dbf3bcbc90654ceb18", + "0xa1190c4cbccf2898a7fe025afd03f8652973a11cef59775fb47d69a6b4dcb9a5a0c554070421a5e10a75e43b63d37b79", + "0x857c0a5f291eb35a76be11543a8c3d798187bd0717e2cdee50d390b66322d0d9529520fd3377136cdc93cfee99b6403f", + "0x944d11e5f9a3493c67786df94f129352d892fbdc43e98206b8dbf83cce240f65305e1768b38e5576048a31dca5c18f31", + "0x818f361c5dae709e067a82b81beffbd9674de8df2bc1bfc3a27ddf326260e124e46b1e36697fb8de539b7736db093e9e", + "0xb07f5b737735a0d628e7ac2d335080b769bdb3acea38ad121e247a6e4307916ba1d029da5d341f079ea61eeaf7d8554e", + "0xa69e338803f3ee0fbbddc7ee481a13f6b64d25d71bae0d76f4b5145b54923cf1616c77ba0fd9ca37a3ae47208f490423", + "0xacaee66b94e226622e28a144f93f6b1b442b9c79d7a8a1740c4d53044d0675a661e7453509b9e716e469fe11ce45ee31", + "0x9402ca799d2e1cce0317ed49453ee0b2669b05e68ff101b89306db215c3941b3786ad3402d00369cb1dee020b56d3142", + "0x849440c539fc0df3c8d06e23e271e6faa50234d5c057b8561e9376415f4396e548351cc677b0abeafe4f51b855a3dc83", + "0x865b99587eb3dbc17e412647673f22b2e89185d1df1ec8ea04515585ad2edfb731be458123118dcd7b41b475026477b9", + "0x9390618833b5adbaf24bd38cf9fc6f25104717f314259bb4da5c7a1f6963ecdc04d07bed391d8cd765c3d53567b2b6b1", + "0x95383e8b1d0a629cec238b5ae2bda236a027f4e3b5f99ceace05f1d5a781ec1e7a43058f44ef0a5aee6b0db5697a0d89", + "0x91739b8946d90db3a5244f7485295cc58143ba0449c9e539df1ba3c166ecf85ff914c9941192963c32d35033ae2f0980", + "0xb5d88848d856d882db5947b9182025f0abf2bc4335b650fa0a48a578e2c87f32cc86d42d3b665ee2eab46d072bf1eccd", + "0x91f4c754549f5a53b1902ef84274ce9acf0bfd2e824e62eb127d67e3214ce05fc2430c05ea51e94dc6e8978f5d076bab", + "0x91fff8c75f8ad86afe78ec301de05e4ca71421d731419a17c747a9a0bf81129422c9499e4749107b168d1695dc90292f", + "0x99fbd7bede9cc1e2974c2a21c70788960c2dbf45a89552da8d73bb1d398b8399590707f2f4ba4b43cb356e703eb01b5e", + "0x80a51cd83e3d748c07b9ac82de1a697b09031e3edc7bf585f06cd0ffa8ea319517fcc2b735614b656677b54b4910814e", + "0x886b27de1f93311d1a31b6d698aa28b54fbd800decd8e25243d89e352ee38cb252d5648b5134a3e1ed021bae46e9da48", + "0x976e70c94db905f83b4ef72188d840874bf005814c0c772f3832aa65b1f21927403125eea7a07b6d3305b1a781b36ab7", + "0xb4adb9d1c49eb31462583580e3ffa625bea4f8b2a7d4927e4ff925c1759d4b3c1e43283d635b54fb0eabfbe1f4c12992", + "0xb66b466bd48485ebeedd47e749d86cbaa3deffbbee2e69cfaa5e9f3bd28b143d7c1c0255a7a1393a2cc1490b2c485571", + "0x8bded5bc0794513947ddb00ff6b780c5cc63a74e2a0b0284153c346a31c82e1eff07c073939da39e6f87a06c14ff1a80", + "0xaceea8c6f799589f6b7070abf69fec724e6679514e60f1eaf9a52c37e9cebb72abcc833a81d8da1a4f5194c1a7eeff63", + "0x89a9f76d053379687fd221ebcaf02c15c2c241bb673ef5298e32640a115d9e0f2331c3e185572cd65946dd6c5bd42412", + "0xa57b6f1e3fdd92eadc6220760f22d0685a82cada1c7a1bda96d36e48e2852f74f3a83c757dd8857e0aee59e978da4919", + "0x9106cf0891bb39ce87433c5f06a5c97a071d08ad44a7cbcd6918c0729c66bb317fbbee8aa45591cee332ad1234c7257d", + "0x96c18cca4a0f0299e0027ff697798085f9f698a7237052c5f191b1dba914e5a015ae356b80c17f0fdd31d08c5a939ebb", + "0xa892103c93df126c024825c07d8769bdac5f1d26ea9509ee26530dc594384b2a5095cc34e0b41ab3db0392a29792c9e8", + "0xb7c2dbc95edb6fc25802ea051803b7bea682f87a99f8a9fdcc3091c81d914b9493dfb18a8894c964805298a6c22b07f2", + "0x8e40948927d560a6840d7fb99802989ce72b43693e9dc7ed9dcda4bca7daedf75271cf656bcc22b3f999a550faad8648", + "0xb354de1c6f0603df3ed9036c610281e55b51a48950ee3ce57a00b4692232de7ca57d19722700e15cbe67a91fcec2f786", + "0xadf987b90737b933436d8036c1d3f0c9104f26c540052e22e703964f72739ac1261e4289b8f27dec47281a0f3f51378a", + "0x8ed5248e9c836fffa7c924178db593e1aaeb54bcf2e93c1983c1f3899cad538deeb2b836430fddc9b2f283e0797ea11e", + "0x907e5410e3bd5d7f55340e2f497bd1ca10bfcb4abed2c66a3cdf94dc40bbd7c43ac98754e0b4b223ea4c61eebf2f27f5", + "0x8e81b441ea0397db28840fb4b3c3bfe6d8e31418816f7bda36f9c1cfe4556daee30c43639d90a2dc9b02a3d65e5f4ab2", + "0x897085c477f5030f9fed06e181b05953a8cd2001d959dd6139738d40f1d673b2c7120b5348f678547acfdc90ffc9fcc6", + "0xb0bf2784c4b3808a04be5a00a0593035ce162b3886e1500247b48365eac8ec3d27c7e5e6372e030c779c75fb79772d0d", + "0xaf3fe6c75f2a1241ac885d5091ff3882cf01695d957d882e940f0c31f7a5b5e269c1a2bae7336e9a7cda2b1d23c03bd1", + "0xa6d94e065f85736d77080a4f775885ccb0dd5efdbe747e4595280bca0ebe12450257c1beadcbec77566ef57508c5d4df", + "0xa5c50fe56b5532bf391da639a2f2b6cbb2634fc6637416fea7c29a522dea024d4adaaa29b6d472b4d2cc3e3b85c72e2a", + "0xafc35f5a03b245a6286318ef489db05d397bbd16c17b4e92eeb56509f875246c0176c01804139eb67dc4247c2a36ff9e", + "0x99ba14ab5a9612c078f9bbaa0e68fd1d52ecceb2ed19bd9abf8f98dd4ed1f9c4fa6e4d41bcef69be2ff020b291749ca8", + "0x8018cdd3d96f331b4c470a4c3904bed44cadecbeec2544ca10e4352cf4ae1a856cf55f6383d666bf997ad3e16816006e", + "0xa9964790c318bb07b8fe61d230dd2161dd3160e186004647a925cfec4c583b4e33530bf5d93d8a14338b090055085b05", + "0xab89d8401df722101c2785cb3ef833017f58376ee82cedd3e9405b2534f259bb76063434a247652c7615a6de5194de65", + "0xa72c3d320a0d40936dee8edfb36703be633aefbb8f89530df04eb6aebe0305ef4f4b6709436f8036d417272a7e47e22a", + "0xb3457661ad62634cc25e2918921a97b0bf5c59ccc7063bc8eb53194783f07659f42f8978c589228af5b12696588d8b2f", + "0x926fa35cd3ed4c8ad78af6284b87ae53b2e25a1ff50398034142a2bbed5b989ba3181ff116838931742c0fbcd8b8a56c", + "0xae57fe506626432f27ae4f8791421c2df9efd9aaabe4b840ccf65fc3d0dd2f83e19eb63ae87bfa6898d37b5da869ddb2", + "0x99c0a26ac74211db77918156d7ae9bea6ecf48da3ce9e53829a9ad5ed41321227c94fbd7449ae2e44aae801811552b1b", + "0xabdd2635b61cb948e51b762a256cf9d159b9fcb39b2fb11ba2fed1cb53475a03fc6e024a6a824a67a689396119a36a7b", + "0xa5ca98b98da8bb8eb07b1e5e3c85a854db42addefacd141771a0c63a8e198421dccc55ef1d94662ca99a7d83b9173fc3", + "0xa821bb5cf1eb3aeae6318c8d554e2ea3137d73bb29d2e4450c9a33f441355ea77bb0e0e0ce7c819abc3ed119110a3a92", + "0x95cdfb19b3f7196c26d60586e2c1efaa93352a712f8c8ef6209f6f318cecd52d7bebdfbfee4be1f5903a1595f73bc985", + "0xaef6e6a400106e217f9888afcef0a1e1299b59017e77dc5453317dec0c32ae96873608bef3f1b504a7e4f45b06edc9c6", + "0x96399ad093299ba26dc09ae85dbec9a1801dea4a338dd5d578bcdcb91246db0059e54098ba8a56cbb24600a40095cf79", + "0xad8b018ac99857ad4b38bdf6d110bbef64029a4d9f08df85a278c6ddc362a5f64e1f3a919f798ccb2f85a7f4ca1260b4", + "0xb211f3b5dd91941d119c4fe05e2b4c7bb0ce0a8d7ef05932a96e850f549a78cd20cded0b3adb3f9f8b7058889ae2cb4e", + "0xab780dd363671765c9c9ab0f4e7096aacf5894e042b75f40a92df8eb272a6229078cd6eadcc500eead3650860aa82177", + "0xa4d96b16ab3abe77ead9b4477c81957e66a028f95557e390352743da53d1a7ba0c81d928a7ea8bc03b9900135ac36a6a", + "0xb4d4e028099bf0f28ac32141cd8de4ee7c3d62d4f519fad6abbb4ba39592750812220a4167d1da4c4f46df965f7cf43d", + "0xaa929c5f0bd8cb44a861bfb3d18340a58c61d82afa642447b71b1470a7b99fe3d5796bdd016b121838cb3594f5a92967", + "0xa038e66f0a28aba19d7079643788db3eed8e412fb9ab4c0f6cacf438af4657cc386a7c22ae97ccc8c33f19a572d6431c", + "0x89c1ff879faa80428910e00b632d31c0cebb0c67e8f5ded333d41f918032282fb59fbcbe26d3156592f9692213667560", + "0x8d899072c9d30e27065d73c79ce3130a09b6a4a4c7d9c4e4488fda4d52ad72bd5f1fd80f3a8936ef79cf362a60817453", + "0x8ffb84a897df9031f9a8e7af06855180562f7ca796489b51bb7cca8d0ca1d9766a4de197a3eb7e298b1dfb39bc6e9778", + "0x836ebd0b37e7ef4ff7b4fc5af157b75fa07a2244045c3852702eaafa119ca1260c654a872f1b3708b65671a2ece66ad2", + "0x9292dfd6d5bfc95f043f4eb9855c10cbcf90fbd03e7a256c163749b23a307b46a331bdbd202236dca0e8ea29e24906de", + "0x8bc37eaa720e293e32b7986061d2ffcbd654d8143e661aabe5602adc832ab535cffbe12a7b571d423675636a74b956e4", + "0x887455f368515340eb6f9b535f16a1cf3e22f0ceda2ead08c5caefccef4087e9f4b5d61c5b110ff3e28e4ab2ad9e97c5", + "0xa6e5ec36e7712056fec00de15b8696952b17891e48ebe2fa90c6f782c7d927b430917b36b4a25b3d8466da3ca2a4985d", + "0x895cae36ba786104ec45740c5dc4f2416b2adce6e806815e3994e98d9e1be372eaec50094fbb7089015684874631ab7e", + "0x9687444fe6250c246b1711a8f73992f15c3cac801e79c54ffd5e243ad539fdd98727043e4f62d36daf866750de1ba926", + "0xb17f75044c8e9ce311bb421a5427006b6fa1428706d04613bd31328f4549decd133e62f4b1917016e36eb02ea316a0ca", + "0x8538a84d2f9079dd272a7383ff03b7674f50b9c220e0399c794a2bcb825d643d0fc8095d972d5186b6f0fe9db0f7084f", + "0xaf07b37644cc216e7083bac1c4e6095fa898f3417699df172c1f6e55d6c13c11f5279edd4c7714d65360b5e4c3c6731e", + "0x87eed8fe7486c0794884c344c07d3964f8fc065aebb0bb3426506ab879b2e0dfaefa5cece213ec16c7b20e6f946c0bd2", + "0x8a4bf42f141d8bc47c9702779d692a72752510ef38e290d36f529f545a2295082a936c8420f59d74b200a8fff55167c4", + "0xa7170e5e00a504a3b37cb19facf399c227497a0b1e9c8a161d541cb553eb8211449c6ac26fe79a7ff7b1c17f33591d74", + "0xa9a2cc7232f07ef9f6d451680648f6b4985ecab5db0125787ac37280e4c07c8210bab254d0b758fd5e8c6bcf2ee2b9ff", + "0x8908d82ebfa78a3de5c56e052d9b5d442af67a510e88a76ba89e4919ae1620c5d15655f663810cfc0ee56c256a420737", + "0xa9d47f3d14047ca86c5db9b71f99568768eaa8a6eb327981203fdb594bdb0a8df2a4a307f22dcea19d74801f4648ea89", + "0xa7c287e0e202ebfc5be261c1279af71f7a2096614ee6526cd8b70e38bb5b0b7aca21a17140d0eddea2f2b849c251656a", + "0x97807451e61557d122f638c3f736ab4dab603538396dca0fcdf99f434a6e1f9def0521816b819b1c57ecdfa93bd077eb", + "0xa8486d60742446396c9d8bc0d4bed868171de4127e9a5a227f24cbf4efbbe5689bbd38f2105498706a6179340b00aed5", + "0xa03b97c2a543dfefa1deb316db9316191ab14e3dd58255ce1027b4e65060d02fb5cb0d6ac1a2bf45bfeac72537b26429", + "0xa7d25060f6861873410c296a4959a058174e9a1681ac41770788191df67fc1391545dab09de06b56cd73a811b676aa1b", + "0x96bb9c9aa85d205e085434d44f5021d8bbafc52cd2727b44e2a66094a4e5467b6294d24146b54c0d964c711e74a258d4", + "0xb07b17f11267e577191e920fa5966880f85ff7089ac59d5d550e46f3a5cdadd94f438a547cd1ec66f20a447e421f96c6", + "0x964e33e1571c97088fe7c8ca3430db60a8119f743a47aa0827e6e2fb9bae5ff3bf6cecd17b11dd34628546b6eb938372", + "0x82a0513a05870b96509a559164e6ff26988ea8a2227ac6da9adc96fe793485a9eb6bdcab09afac7be4aef9a5ae358199", + "0xb1185bc679623e7a37a873d90a2a6393fb5ccc86e74ba4ba6f71277df3623cde632feae4414d6429db6b4babde16dee0", + "0xb3d77504b7032b5593a674d3c0cd2efbf56b2b44ed7fe8669f752828045e4e68202a37bf441f674b9c134886d4cee1df", + "0x95ab31749ff1f7b3f165ce45af943c6ed1f1071448c37009643a5f0281875695c16c28fc8d8011a71a108a2d8758e57d", + "0xb234dee9c56c582084af6546d1853f58e158549b28670b6783b4b5d7d52f00e805e73044a8b8bd44f3d5e10816c57ecc", + "0x86da5d2343f652715c1df58a4581e4010cf4cbe27a8c72bb92e322152000d14e44cc36e37ff6a55db890b29096c599b9", + "0x8b7be904c50f36453eff8c6267edcb4086a2f4803777d4414c5c70c45b97541753def16833e691d6b68d9ef19a15cb23", + "0xb1f4e81b2cdb08bd73404a4095255fa5d28bcd1992a5fd7e5d929cfd5f35645793462805a092ec621946aaf5607ef471", + "0xa7f2ca8dacb03825ef537669baff512baf1ea39a1a0333f6af93505f37ed2e4bbd56cb9c3b246810feee7bacdf4c2759", + "0x996d0c6c0530c44c1599ffdf7042c42698e5e9efee4feb92f2674431bbddf8cf26d109f5d54208071079dfa801e01052", + "0xb99647e7d428f3baa450841f10e2dc704ce8125634cc5e7e72a8aa149bf1b6035adce8979a116a97c58c93e5774f72b7", + "0x95960a7f95ad47b4a917920f1a82fbbecd17a4050e443f7f85b325929c1e1f803cf3d812d2cedeab724d11b135dde7a3", + "0x8f9cd1efdf176b80e961c54090e114324616b2764a147a0d7538efe6b0c406ec09fd6f04a011ff40e0fa0b774dd98888", + "0xb99431d2e946ac4be383b38a49b26e92139b17e6e0f0b0dc0481b59f1ff029fb73a0fc7e6fff3e28d7c3678d6479f5a3", + "0xa888887a4241ce156bedf74f5e72bfa2c6d580a438e206932aefc020678d3d0eb7df4c9fe8142a7c27191837f46a6af6", + "0xab62224ea33b9a66722eb73cfd1434b85b63c121d92e3eebb1dff8b80dd861238acf2003f80f9341bfea6bde0bfcd38c", + "0x9115df3026971dd3efe7e33618449ff94e8fd8c165de0b08d4a9593a906bbed67ec3ed925b921752700f9e54cd00b983", + "0x95de78c37e354decd2b80f8f5a817d153309a6a8e2f0c82a9586a32051a9af03e437a1fb03d1b147f0be489ef76b578b", + "0xa7b8a6e383de7739063f24772460e36209be9e1d367fe42153ffe1bccb788a699e1c8b27336435cd7bf85d51ba6bfdd6", + "0x937a8af7ed18d1a55bf3bbe21e24363ae2cb4c8f000418047bf696501aaeec41f2ddf952fd80ef3373f61566faa276a9", + "0xab5e4931771aeb41c10fa1796d6002b06e512620e9d1c1649c282f296853c913f44e06e377a02f57192b8f09937282eb", + "0x893d88009754c84ec1c523a381d2a443cb6d3879e98a1965e41759420a088a7582e4d0456067b2f90d9d56af4ea94bba", + "0x91b2388a4146ebaaa977fec28ffbfb88ac2a1089a8a258f0451c4152877065f50402a9397ba045b896997208b46f3ebf", + "0x8ce0523192e4cc8348cd0c79354a4930137f6f08063de4a940ea66c0b31d5ea315ce9d9c5c2ec4fa6ee79d4df83840dd", + "0xb72f75c4ab77aca8df1a1b691b6ef1a3ff1c343dd9ed48212542e447d2ed3af3017c9ad6826991e9ef472348c21b72a4", + "0xaf0fa5a960f185326877daf735ad96c6bd8f8f99ab0ab22e0119c22a0939976ece5c6a878c40380497570dc397844dba", + "0xadf9f41393e1196e59b39499623da81be9f76df047ae2472ce5a45f83871bb2a0233e00233b52c5c2fa97a6870fbab0a", + "0x8d9fc3aecd8b9a9fca8951753eea8b3e6b9eb8819a31cca8c85a9606ce1bd3885edb4d8cdbc6f0c54449c12927285996", + "0x901969c1d6cac2adcdc83818d91b41dc29ef39c3d84a6f68740b262657ec9bd7871e09b0a9b156b39fa62065c61dacb1", + "0x9536a48ccd2c98f2dcbff3d81578bbb8f828bf94d8d846d985f575059cd7fb28dfa138b481d305a07b42fcb92bacfa11", + "0x8d336654833833558e01b7213dc0217d7943544d36d25b46ecc1e31a2992439679205b5b3ab36a8410311109daa5aa00", + "0x95113547163e969240701e7414bf38212140db073f90a65708c5970a6aaf3aba029590a94839618fc3f7dd4f23306734", + "0xa959d77a159b07b0d3d41a107c24a39f7514f8ce24efa046cfcf6ace852a1d948747f59c80eb06277dce1a2ba2ec8ea9", + "0x8d2cb52dd7f5c56ef479c0937b83b8519fa49eb19b13ea2ec67266a7b3d227fb8d0c2454c4618d63da1c8e5d4171ac7b", + "0x9941698c5078936d2c402d7db6756cc60c542682977f7e0497906a45df6b8d0ffe540f09a023c9593188ba1b8ce6dfcb", + "0x9631d9b7ec0fc2de8051c0a7b68c831ba5271c17644b815e8428e81bad056abb51b9ca2424d41819e09125baf7aaf2d4", + "0xa0f3d27b29a63f9626e1925eec38047c92c9ab3f72504bf1d45700a612682ad4bf4a4de41d2432e27b745b1613ff22f9", + "0x80e3701acfd01fc5b16ecfa0c6c6fd4c50fe60643c77de513f0ad7a1a2201e49479aa59056fd6c331e44292f820a6a2c", + "0xa758c81743ab68b8895db3d75030c5dd4b2ccc9f4a26e69eb54635378a2abfc21cba6ca431afb3f00be66cffba6ab616", + "0xa397acb2e119d667f1ab5f13796fd611e1813f98f554112c4c478956c6a0ebaceef3afae7ee71f279277df19e8e4543a", + "0xa95df7d52b535044a7c3cf3b95a03bafd4466bdb905f9b5f5290a6e5c2ac0f0e295136da2625df6161ab49abcdacb40f", + "0x8639fc0c48211135909d9e999459568dbdbbc7439933bab43d503e07e796a1f008930e8a8450e8346ab110ec558bcbb9", + "0xa837bcc0524614af9e7b677532fabfb48a50d8bec662578ba22f72462caabda93c35750eed6d77b936636bf165c6f14e", + "0x97d51535c469c867666e0e0d9ed8c2472aa27916370e6c3de7d6b2351a022e2a5330de6d23c112880b0dc5a4e90f2438", + "0xaadb093c06bd86bd450e3eb5aa20f542d450f9f62b4510e196f2659f2e3667b0fe026517c33e268af75a9c1b2bc45619", + "0x860cef2e0310d1a49a9dd6bc18d1ca3841ed1121d96a4f51008799b6e99eb65f48838cd1e0c134f7358a3346332f3c73", + "0xb11c4f9e7ef56db46636474a91d6416bcb4954e34b93abf509f8c3f790b98f04bd0853104ec4a1ff5401a66f27475fce", + "0x87cb52e90a96c5ee581dc8ab241e2fd5df976fe57cc08d9ffda3925a04398e7cffaf5a74c90a7319927f27c8a1f3cef5", + "0xb03831449f658a418a27fd91da32024fdf2b904baf1ba3b17bbf9400eaddc16c3d09ad62cc18a92b780c10b0543c9013", + "0x94e228af11cb38532e7256fa4a293a39ffa8f3920ed1c5ad6f39ce532e789bb262b354273af062add4ca04841f99d3aa", + "0x99eb3aeb61ec15f3719145cf80501f1336f357cc79fca6981ea14320faed1d04ebe0dbce91d710d25c4e4dc5b6461ebf", + "0x920a3c4b0d0fbe379a675e8938047ea3ec8d47b94430399b69dd4f46315ee44bd62089c9a25e7fa5a13a989612fe3d09", + "0xb6414a9a9650100a4c0960c129fa67e765fe42489e50868dd94e315e68d5471e11bfbc86faffb90670e0bec6f4542869", + "0x94b85e0b06580a85d45e57dae1cfd9d967d35bdfcd84169ef48b333c9321f2902278c2594c2e51fecd8dbcd221951e29", + "0xb2c0a0dd75e04a85def2a886ee1fda51f530e33b56f3c2cf61d1605d40217aa549eef3361d05975d565519c6079cc2ac", + "0xabb0ea261116c3f395360d5ac731a7514a3c290f29346dc82bacb024d5455d61c442fefe99cc94dddcae47e30c0e031f", + "0xa32d95ae590baa7956497eddf4c56bff5dfdc08c5817168196c794516610fcc4dbcd82cf9061716d880e151b455b01e0", + "0x8bd589fb6e3041f3ef9b8c50d29aed1a39e90719681f61b75a27489256a73c78c50c09dd9d994c83f0e75dfe40b4de84", + "0x82d01cdaf949d2c7f4db7bfadbf47e80ff9d9374c91512b5a77762488308e013689416c684528a1b16423c6b48406baf", + "0xb23e20deb7e1bbbc328cbe6e11874d6bdbb675704a55af1039b630a2866b53d4b48419db834a89b31ebed2cfc41278dd", + "0xa371559d29262abd4b13df5a6a5c23adab5a483f9a33a8d043163fcb659263322ee94f872f55b67447b0a488f88672d6", + "0x85b33ddf4a6472cacc0ed9b5ec75ed54b3157e73a2d88986c9afa8cb542e662a74797a9a4fec9111c67e5a81c54c82b3", + "0xaf1248bc47a6426c69011694f369dc0ec445f1810b3914a2ff7b830b69c7e4eaa4bafec8b10ed00b5372b0c78655a59b", + "0x94b261ed52d5637fd4c81187000bd0e5c5398ce25797b91c61b30d7b18d614ab9a2ca83d66a51faf4c3f98714e5b0ea5", + "0x953d4571c1b83279f6c5958727aaf9285d8b8cbdbfbaff51527b4a8cfdd73d3439ba862cdb0e2356e74987ff66d2c4d9", + "0xb765dae55d0651aca3b3eaef4ca477f0b0fda8d25c89dccd53a5573dd0c4be7faaadaa4e90029cdd7c09a76d4ce51b91", + "0xb6d7b7c41556c85c3894d0d350510b512a0e22089d3d1dd240ad14c2c2b0ce1f003388100f3154ad80ec50892a033294", + "0xa64561dc4b42289c2edf121f934bc6a6e283d7dce128a703f9a9555e0df7dda2825525dbd3679cd6ba7716de230a3142", + "0xa46c574721e8be4a3b10d41c71057270cca42eec94ca2268ee4ab5426c7ce894efa9fa525623252a6a1b97bcf855a0a5", + "0xa66d37f1999c9c6e071d2a961074c3d9fdcf9c94bf3e6c6ed82693095538dd445f45496e4c83b5333b9c8e0e64233adc", + "0xab13814b227a0043e7d1ff6365360e292aca65d39602d8e0a574d22d25d99ccb94417c9b73095632ff302e3d9a09d067", + "0xb2c445b69cff70d913143b722440d2564a05558d418c8ef847483b5196d7e581c094bae1dbb91c4499501cfa2c027759", + "0x87cbde089962d5f093324b71e2976edbe6ad54fb8834dd6e73da9585b8935fca1c597b4d525949699fdfa79686721616", + "0xa2c7e60966acb09c56cf9ad5bdcc820dcabf21ef7784970d10353048cf3b7df7790a40395561d1064e03109eaac0df98", + "0x8ea7b8af208678178553946b2ee9e68c0e751b34f3652409a5e66c40d3aee3a40ba6ffe2175ce16c6a81b78ecc597d02", + "0x960234239e1e3ea262e53d256ad41b2fe73f506b3d130732d0ee48819eb8a9c85bb5106a304874d8625afae682c34015", + "0x858459694c4e8fdafa6cdaee1184e1305ca6e102222b99b8e283dd9bb3ebf80e55d6c4d8831a072b813c8eceb8124d95", + "0xa30a8ce0f44aeb5590dc618c81c7cac441470ce79fd7881a8f2ea4ca5f9d848ebde762fcaee985cbd3d5990367403351", + "0xa83867643672248b07d3705813b56489453e7bc546cdba570468152d9a1bd04f0656034e7d03736ea156fc97c88dc37f", + "0xa7bb52e0fc58b940dc47ea4d0a583012ee41fad285aba1a60a6c54fa32cfe819146888c5d63222c93f90de15745efb2b", + "0x8627bcc853bdeaad37f1d0f7d6b30ada9b481ccdf79b618803673de8a142e8a4ce3e7e16caed1170a7332119bcdc10a9", + "0x8903d9dc3716b59e8e99e469bd9fde6f4bca857ce24f3a23db817012f1ea415c2b4656c7aeca31d810582bb3e1c08cc6", + "0x875169863a325b16f892ad8a7385be94d35e398408138bd0a8468923c05123d53dba4ce0e572ea48fcdadd9bd9faa47a", + "0xb255b98d46d6cc44235e6ce794cc0c1d3bd074c51d58436a7796ce6dc0ae69f4edaa3771b35d3b8a2a9acd2f6736fab3", + "0x9740c4d0ee40e79715a70890efda3455633ce3a715cbfc26a53e314ebbe61937b0346b4859df5b72eb20bcba96983870", + "0xa44ce22ab5ddc23953b02ec187a0f419db134522306a9078e1e13d5bf45d536450d48016a5e1885a346997003d024db0", + "0x90af81c08afdccd83a33f21d0dc0305898347f8bd77cc29385b9de9d2408434857044aec3b74cb72585338c122e83bb4", + "0x80e162a7656c9ae38efa91ae93e5bd6cb903f921f9f50874694b9a9e0e2d2595411963d0e3f0c2d536b86f83b6e4d6ef", + "0x8b49fa6babe47291f9d290df35e94e83be1946784b9c7867efd8bc97a12be453013939667164b24aeb53d8950288a442", + "0xa1df6435d718915df3da6dda61da1532a86e196dc7632703508679630f5f14d4cb44ce89eff489d7ff3fe599cc193940", + "0xafd44c143dbb94c71acc2a309c9c88b8847ef45d98479fccce9920db9b268e8e36f8db9f02ff4ee3cff01e548f719627", + "0xb2cf33d65d205e944b691292c2d9b0b124c9de546076dd80630742989f1ffd07102813c64d69ba2a902a928a08bce801", + "0xb9f295e9f9eca432b2d5c77d6316186027caca40a6d6713f41356497a507b6e8716fb471faf973aaa4e856983183c269", + "0xb3bd50c4b034473edce4b9be1171376a522899cb0c1a1ae7dc22dd2b52d20537cf4129797235084648ac4a3afc1fa854", + "0x8ef37683d7ca37c950ba4df72564888bedaf681931d942d0ea88ead5cc90f4cbef07985a3c55686a225f76f7d90e137d", + "0x82107855b330bc9d644129cebecf2efbfab90f81792c3928279f110250e727ce12790fd5117501c895057fa76a484fc0", + "0x816a5474c3b545fb0b58d3118cc3088a6d83aad790dbf93025ad8b94a2659cceba4fa6a6b994cb66603cc9aef683a5e3", + "0x8f633f9b31f3bb9b0b01ea1a8830f897ecd79c28f257a6417af6a5f64e6c78b66c586cf8d26586830bd007fb6279cd35", + "0xacb69d55a732b51693d4b11f7d14d21258d3a3af0936385a7ce61e9d7028a8fe0dd902bda09b33fb728bc8a1bc542035", + "0x8d099582ac1f46768c17bf5a39c13015cfe145958d7fc6ddfd2876ad3b1a55a383fbe940e797db2b2b3dc8a232f545dc", + "0x97a4dd488b70bf772348ececaca4cf87bc2875d3846f29fe6ef01190c5b030219b9e4f8137d49ea0cc50ca418024c488", + "0xb4d81148f93fa8ec0656bbfb5f9d96bbf5879fa533004a960faac9fd9f0fe541481935fdf1f9b5dd08dff38469ef81c5", + "0x8e9b2ae4fc57b817f9465610a77966caaff013229018f6c90fa695bd734cb713b78a345b2e9254b1aff87df58c1cd512", + "0x99eb7126e347c636e9a906e6bfdc7c8ca0c1d08580c08e6609889a5d515848c7ca0f32ab3a90c0e346f976a7883611f7", + "0x8ca87944aa3e398492b268bda0d97917f598bc0b28584aa629dfec1c3f5729d2874db422727d82219880577267641baa", + "0x88ab0e290dc9a6878d6b4e98891ff6bfc090e8f621d966493fcbe1336cc6848fcbb958d15abcfa77091d337da4e70e74", + "0x8956a2e1dc3ec5eb21f4f93a5e8f0600a06e409bb5ec54e062a1290dff9ce339b53fbbfc4d42b4eed21accea07b724d6", + "0x8d22220da9dc477af2bddb85c7073c742c4d43b7afee4761eba9346cadbcd522106ed8294281a7ef2e69883c28da0685", + "0x90dafd9a96db7e1d6bde424245305c94251d5d07e682198ae129cd77bd2907a86d34722cbde06683cc2ca67cebe54033", + "0xb5202e62cf8ea8e145b12394bd52fd09bda9145a5f78285b52fda4628c4e2ccfc2c208ecde4951bd0a59ac03fa8bc202", + "0x8959856793ba4acf680fb36438c9722da74d835a9fe25a08cf9e32d7800c890a8299c7d350141d2e6b9feceb2ebb636f", + "0xab0aa23c1cd2d095825a3456861871d298043b615ae03fcd9283f388f0deef3cc76899e7fde15899e3edf362b4b4657f", + "0x9603b333cc48fe39bea8d9824cfee6ac6c4e21668c162c196ecd1ff08ef4052ace96a785c36b8f7906fdcb6bc8802ddd", + "0x93bfecbc3c7cc03c563240e109850a74948f9fa078eb903b322368cda0b50888663a17953579578ba060b14dbf053024", + "0xb01f843b808cf7939a474de155a45462e159eb5044f00c6d77e0f7ec812720a3153209e971a971ccbf5ebee76ec4074f", + "0xb009e0567c3c75ed767247d06fa39049a4d95df3392d35a9808cb114accf934e78f765cd18a2290efef016f1918c7aeb", + "0xad35631df8331da3a12f059813dfa343d831225a392f9c7e641c7d23a6c1ad8df8e021201c9f6afb27c1575948d6bf68", + "0xa89c2a631d84128471c8ef3d24b6c35c97b4b9b5dad905c1a092fb9396ae0370e215a82308e13e90e7bb6ebcc455eb2a", + "0xb59c7f5fbfeb02f8f69e6cedef7ff104982551f842c890a14834f5e834b32de1148cf4b414a11809d53dd3f002b15d6a", + "0xaa6f267305b55fede2f3547bc751ba844ce189d0b4852022712b0aee474de54a257d4abcd95efe7854e33a912c774eba", + "0xafddd668f30cce70904577f49071432c49386ec27389f30a8223b5273b37e6de9db243aceb461a7dc8f1f231517463a9", + "0xb902a09da9157b3efa1d98f644371904397019d0c84915880628a646a3ad464a9d130fdc651315098179e11da643ad2e", + "0xb05f31957364b016c6f299ae4c62eede54cab8ea3871d49534828c8bdc6adbc6a04a708df268f50107d81d1384d983ae", + "0xb4c3f7284802e614ddf1f51640f29e7139aae891467d5f62778310372071793e56fbd770837b97d501191edd0da06572", + "0xb4eddb7c3775fb14fac7f63bb73b3cde0efa2f9a3b70e6a65d200765f6c4b466d3d76fcd4d329baee88e2aba183b8e69", + "0xa83e7dbae5a279f0cfd1c94e9849c58a3d4cecc6d6d44bb9b17508576ca347fca52c2c81371d946b11a09d4ed76ec846", + "0x8018ea17e2381c0233867670f9e04c8a47ace1207fdcf72dce61b6c280ba42d0a65f4b4e0b1070cc19c7bb00734974d9", + "0xaf90b541dfed22e181ff3ef4cf11f5e385fd215c1e99d988e4d247bc9dcee9f04f2182b961797c0bcc5f2aaa05c901a9", + "0xa37046e44cf35944e8b66df80c985b8a1aa7004a2fd0b81ac251638977d2ff1465f23f93ac0ce56296f88fdc591bbdd7", + "0xa735bd94d3be9d41fcd764ec0d8d7e732c9fc5038463f7728fd9d59321277e2c73a45990223bd571dab831545d46e7aa", + "0x94b32dcb86f5d7e83d70a5b48fe42c50f419be2f848f2d3d32ee78bf4181ab18077a7666eedb08607eece4de90f51a46", + "0xa7f0804cafbf513293485afc1b53117f0cbfaea10919e96d9e4eb06f0c96535e87065d93f3def1bbc42044dbb00eb523", + "0xaaaad1166d7f19f08583dd713275a71a856ab89312f84ca8078957664924bb31994b5c9a1210d0c41b085be4058ed52e", + "0xa1757aac9f64f953e68e680985a8d97c5aac8688b7d90f4db860166dd3d6119e8fca7d700a9530a2b9ba3932c5e74e33", + "0x98cada5db4a1430c272bfc1065fb685872e664ed200d84060ee9f797d0a00864f23943e0fb84ba122a961996a73dfb14", + "0xa5e609f716dc7729d1247f40f9368a2e4a15067e1dd6a231fece85eeefb7e7d4a5ac8918fb376debd79d95088750b2ca", + "0xb5365eb8caab8b1118619a626ff18ce6b2e717763f04f6fa8158cdca530c5779204efa440d088083f1a3685454aa0555", + "0xa6e01b8da5f008b3d09e51a5375d3c87c1da82dff337a212223e4d0cdb2d02576d59f4eef0652d6b5f2fc806d8c8149c", + "0xae310f613d81477d413d19084f117248ad756572c22a85b9e4c86b432e6c602c4a6db5edf2976e11f7353743d679e82a", + "0xa1f219c0b8e8bb8a9df2c6c030acbb9bbfa17ba3db0366f547da925a6abb74e1d7eb852bd5a34bae6ac61d033c37e9dc", + "0xa2087fa121c0cdd5ea495e911b4bc0e29f1d5c725aadfb497d84434d2291c350cdaa3dc8c85285f65a7d91b163789b7a", + "0x929c63c266da73d726435fa89d47041cfe39d4efa0edce7fc6eca43638740fbc82532fd44d24c7e7dd3a208536025027", + "0x91c1051dcc5f52ad89720a368dddd2621f470e184e746f5985908ba34e1d3e8078a32e47ab7132be780bea5277afecb0", + "0xae089b90ba99894d5a21016b1ea0b72a6e303d87e59fb0223f12e4bb92262e4d7e64bfdbdb71055d23344bc76e7794b2", + "0x8b69aa29a6970f9e66243494223bad07ac8f7a12845f60c19b1963e55a337171a67bdc27622153016fce9828473a3056", + "0x95ca6b08680f951f6f05fd0d180d5805d25caf7e5bda21c218c1344e661d0c723a4dfc2493642be153793c1b3b2caaa4", + "0xa4789dc0f2a07c794dab7708510d3c893d82ddbd1d7e7e4bbbeca7684d9e6f4520fb019b923a06c7efab0735f94aa471", + "0x93c4f57a3cf75085f5656b08040f4cd49c40f1aab6384a1def4c5c48a9fe4c03514f8e61aabe2cfa399ff1ccac06f869", + "0xb6c37f92c76a96b852cd41445aa46a9c371836dd40176cc92d06666f767695d2284a2780fdfd5efc34cf6b18bcfb5430", + "0x9113e4575e4b363479daa7203be662c13d7de2debcda1c142137228aeead2c1c9bc2d06d93a226302fa63cc75b7353ec", + "0xb70addeb5b842ac78c70272137f6a1cef6b1d3a551d3dd906d9a0e023c8f49f9b6a13029010f3309d0b4c8623a329faf", + "0xb976a5132b7eb42d5b759c2d06f87927ef66ecd6c94b1a08e4c9e02a4ce7feca3ac91f9479daa1f18da3d4a168c2ba77", + "0x8fdab795af64b16a7ddf3fad11ab7a85d10f4057cf7716784184960013baa54e7ba2050b0e036dc978ff8c9a25dc5832", + "0xb2c982ad13be67d5cdc1b8fac555d4d1ec5d25f84e58b0553a9836f8f9e1c37582d69ad52c086a880a08b4efcccd552e", + "0x810661d9075ae6942735215f2ab46d60763412e1f6334e4e00564b6e5f479fc48cf37225512abbccf249c0ca225fc935", + "0xa0c4bf00a20f19feff4004004f08231b4c6c86ac4ed57921eea28d7dea32034f3f4ab5b7ded7184f6c7ffbf5847232ad", + "0xb2bb5a9eea80bf067f3686a488529d9c2abd63fc9e1d4d921b1247ef86d40cd99e0a8b74f750e85c962af84e84e163a6", + "0x887ee493c96d50f619ba190ce23acddc5f31913e7a8f1895e6339d03794ecefd29da5f177d1d25bc8df8337ae963fc7b", + "0xb7966fb07029d040f2228efa2cfcd04341e4666c4cf0b653e6e5708631aa2dd0e8c2ac1a62b50c5a1219a2737b82f4f7", + "0x92234cfd6b07f210b82db868f585953aafbcbc9b07b02ded73ff57295104c6f44a16e2775ca7d7d8ee79babb20160626", + "0x8d3cd7f09c6fd1072bc326ff329e19d856e552ac2a9f20274bc9752527cd3274142aa2e32b65f285fb84bc3adaaea3cc", + "0x8caed1cb90d8cd61e7f66edc132672172f4fa315e594273bb0a7f58a75c30647ec7d52eda0394c86e6477fbc352f4fe8", + "0xae192194b09e9e17f35d8537f947b56f905766c31224e41c632c11cd73764d22496827859c72f4c1ab5fd73e26175a5d", + "0x8b7be56aac76d053969e46882d80a254e89f55c5ab434883cbafc634a2c882375898074a57bc24be3c7b2c56401a7842", + "0x98bc4a7a9b05ba19f6b85f3ee82b08bed0640fd7d24d4542eb7a7f7fde443e880bdb6f5499bd8cb64e1ddd7c5f529b19", + "0xa5a41eaa5e9c1d52b00d64ab72bc9def6b9d41972d80703e9bfe080199d4e476e8833a51079c6b0155b78c3ab195a2a7", + "0xa0823f6f66465fd9be3769c164183f8470c74e56af617f8afd99b742909d1a51f2e0f96a84397597afbd8eeaabb51996", + "0x801da41d47207bdd280cc4c4c9753a0f0e9d655e09e0be5f89aeed4ce875a904f3da952464399bf8efc2398940d5fba2", + "0xa719314085fd8c9beac4706c24875833d59a9a59b55bca5da339037c0a5fc03df46dbecb2b4efcfed67830942e3c4ea1", + "0xa75dde0a56070bb7e9237b144ea79f578d413a1cbbd1821cee04f14f533638b24f46d88a7001e92831843b37ed7a709f", + "0xa6b4ef8847a4b980146e1849e1d8ab38695635e0394ca074589f900ce41fa1bb255938dc5f37027523bac6a291779bef", + "0xb26d84dfd0b7bd60bcfdbea667350462a93dca8ff5a53d6fc226214dcb765fada0f39e446a1a87f18e4e4f4a7133155f", + "0xae7bd66cc0b72f14ac631ff329a5ca4958a80ba7597d6da049b4eb16ac3decde919ca5f6f9083e6e541b303fb336dc2f", + "0xa69306e6bfbbc10de0621cffb13c586e2fcfd1a80935e07c746c95651289aec99066126a6c33cb8eb93e87d843fc631f", + "0xa47e4815585865218d73c68ba47139568ea7ae23bfa863cb914a68454242dd79beaec760616b48eea74ceab6df2298dd", + "0xb2da3cfb07d0721cd226c9513e5f3ace98ed2bc0b198f6626b8d8582268e441fa839f5834f650e2db797655ca2afa013", + "0xb615d0819554f1a301a704d3fc4742bd259d04ad75d50bccee3a949b6226655f7d623301703506253cca464208a56232", + "0x85e06ed5797207f0e7ae85909e31776eb9dae8af2ec39cc7f6a42843d94ea1de8be2a3cdadfcbe779da59394d4ffeb45", + "0x8c3529475b5fdbc636ee21d763f5ec11b8cb040a592116fb609f8e89ca9f032b4fa158dd6e9ceab9aceb28e067419544", + "0xaccddb9c341f32be82b6fa2ef258802c9ae77cd8085c16ec6a5a83db4ab88255231b73a0e100c75b7369a330bfc82e78", + "0x93b8e4c6e7480948fa17444b59545a5b28538b8484a75ad6bc6044a1d2dbd76e7c44970757ca53188d951dc7347d6a37", + "0x90111721d68b29209f4dc4cfb2f75ab31d15c55701922e50a5d786fb01707ab53fcec08567cd366362c898df2d6e0e93", + "0xb60a349767df04bd15881c60be2e5cc5864d00075150d0be3ef8f6b778715bebca8be3be2aa9dbdc49f1a485aeb76cda", + "0xb8d5a967fdd3a9bcf89a774077db39ef72ca9316242f3e5f2a350202102d494b2952e4c22badecd56b72ba1eea25e64b", + "0x8499ebd860f31f44167183b29574447b37a7ee11efcc9e086d56e107b826b64646b1454f40f748ccac93883918c89a91", + "0x99c35e529782db30f7ccab7f31c225858cf2393571690b229ece838ec421a628f678854a1ddbd83fa57103ccebd92c7f", + "0x99817660d8b00cbe03ec363bcdc5a77885586c9e8da9e01a862aca0fc69bf900c09b4e929171bc6681681eae10450541", + "0x8055e130964c3c2ebd980d3dc327a40a416bcdbf29f480480a89a087677a1fb51c823b57392c1db72f4093597100b8d3", + "0x877eaddef845215f8e6f9ed24060c87e3ab6b1b8fbb8037d1a57e6a1e8ed34d00e64abb98d4bf75edb5c9788cbdccbef", + "0xb5432bbff60aeae47f2438b68b123196dfb4a65cc875b8e080501a4a44f834b739e121bec58d39ac36f908881e4aa8ab", + "0xb3c3f859b7d03ff269228c0f9a023b12e1231c73aba71ad1e6d86700b92adc28dfa3757c052bbc0ba2a1d11b7fda4643", + "0xab8a29f7519a465f394ef4a5b3d4924d5419ca1489e4c89455b66a63ac430c8c9d121d9d2e2ed8aa1964e02cd4ebac8c", + "0x866ae1f5c2a6e159f2e9106221402d84c059f40d166fab355d970773189241cd5ee996540d7c6fc4faf6f7bcff967dce", + "0x973a63939e8f1142a82b95e699853c1e78d6e05536782b9bb178c799b884f1bc60177163a79a9d200b5ff4628beeb9e7", + "0xa5fc84798d3e2d7632e91673e89e968f5a67b7c8bb557ea467650d6e05e7fe370e18d9f2bdd44c244978295cf312dc27", + "0xb328fe036bcd0645b0e6a15e79d1dd8a4e2eda128401a4e0a213d9f92d07c88201416fc76193bb5b1fe4cb4203bab194", + "0x99239606b3725695a570ae9b6fb0fb0a34ad2f468460031cfa87aa09a0d555ff606ff204be42c1596c4b3b9e124b8bd6", + "0xaf3432337ca9d6cce3574e23e5b7e4aa8eda11d306dc612918e970cc7e5c756836605a3391f090a630bac0e2c6c42e61", + "0x8a545b3cb962ce5f494f2de3301de99286c4d551eaa93a9a1d6fef86647321834c95bf754c62ec6c77116a21494f380d", + "0x8f9b8ea4c25469c93556f1d91be583a5f0531ac828449b793ba03c0a841c9c73f251f49dd05cbb415f5d26e6f6802c99", + "0xa87199e33628eeffd3aff114e81f53dd54fba61ba9a9a4d7efdbff64503f25bc418969ab76ef1cf9016dd344d556bb29", + "0xa2fda05a566480602274d7ffcaefdd9e94171286e307581142974f57e1db1fa21c30be9e3c1ac4c9f2b167f92e7c7768", + "0xa6235d6a23304b5c797efb2b476ed02cb0f93b6021a719ae5389eb1e1d032944ae4d69aec2f29fcd6cbc71a6d789a3ba", + "0xa7f4a73215f7e99e2182c6157dd0f22e71b288e696a8cff2450689a3998f540cfb82f16b143e90add01b386cb60d8a33", + "0x922d8f9cd55423f5f6a60d26de2f8a396ac4070a6e2dc956e50c2a911906aa364d4718aea29c5b61c12603534e331e7e", + "0x96d7fdf5465f028fc28f21fbfe14c2db2061197baf26849e6a0989a4ea7d5e09ab49a15ba43a5377b9354d01e30ce860", + "0x8f94c4255a0fc1bd0fa60e8178c17f2a8e927cac7941c5547d2f8f539e7c6ed0653cab07e9fb1f2c56cdd03bb876512a", + "0x95984c10a2917bfa6647ebce69bf5252d9e72d9d15921f79b2c6d7c15ee61342b4fb8a6d34838e07132b904f024ded04", + "0x93e65e765a574277d3a4d1d08ca2f2ff46e9921a7806ca8ca3d8055f22d6507744a649db7c78117d9168a1cbdb3bbc61", + "0x8d453b7364662dc6f36faf099aa7cbbe61151d79da7e432deba7c3ed8775cfe51eaf1ba7789779713829dde6828e189a", + "0xacffa3ee6c75160286090162df0a32a123afb1f9b21e17fd8b808c2c4d51a4270cab18fba06c91ef9d22e98a8dc26cdd", + "0xa5597cc458186efa1b3545a3926f6ecaaa6664784190e50eed1feac8de56631bee645c3bac1589fa9d0e85feb2be79d4", + "0x87ba9a898df9dfa7dabc4ab7b28450e4daf6013340e329408d1a305de959415ab7315251bad40511f917dfc43974e5f0", + "0xa598778cf01d6eef2c6aabc2678e1b5194ee8a284ebd18a2a51a3c28a64110d5117bcbf68869147934e600572a9e4c8a", + "0x84c69a4ad95861d48709f93ade5ac3800f811b177feb852ebcd056e35f5af5201f1d8a34ab318da8fe214812d0a7d964", + "0x9638a237e4aed623d80980d91eda45e24ebf48c57a25e389c57bd5f62fa6ffa7ca3fb7ae9887faf46d3e1288af2c153b", + "0x800f975721a942a4b259d913f25404d5b7b4c5bf14d1d7e30eee106a49cb833b92058dab851a32ee41faf4ef9cb0dea4", + "0xb9127a34a59fed9b5b56b6d912a29b0c7d3cb9581afc9bd174fc308b86fdb076f7d436f2abc8f61cef04c4e80cd47f59", + "0x8004eda83f3263a1ccfc8617bc4f76305325c405160fb4f8efeff0662d605e98ba2510155c74840b6fe4323704e903c4", + "0xaa857b771660d6799ff03ccad1ab8479e7f585a1624260418fc66dc3e2b8730cfa491d9e249505141103f9c52f935463", + "0x98b21083942400f34cde9adbe1977dee45ba52743dc54d99404ad9da5d48691ddea4946f08470a2faad347e9535690c7", + "0xa4b766b2faec600a6305d9b2f7317b46f425442da0dc407321fc5a63d4571c26336d2bccedf61097f0172ec90fb01f5f", + "0xb9736619578276f43583de1e4ed8632322ea8a351f3e1506c5977b5031d1c8ad0646fb464010e97c4ddb30499ddc3fb0", + "0x973444ffaff75f84c17f9a4f294a13affd10e2bceed6b4b327e4a32c07595ff891b887a9f1af34d19766d8e6cb42bfd1", + "0xb09ce4964278eff81a976fbc552488cb84fc4a102f004c87179cb912f49904d1e785ecaf5d184522a58e9035875440ef", + "0xb80c2aa3d0e52b4d8b02c0b706e54b70c3dbca80e5e5c6a354976721166ea0ca9f59c490b3e74272ef669179f53cb50d", + "0x8e52fa5096ff960c0d7da1aa4bce80e89527cdc3883eba0c21cb9a531088b9d027aa22e210d58cf7cbc82f1ec71eb44f", + "0x969f85db95f455b03114e4d3dc1f62a58996d19036513e56bee795d57bf4ed18da555722cd77a4f6e6c1a8e5efe2f5d7", + "0xab84b29b04a117e53caea394a9b452338364c45a0c4444e72c44132a71820b96a6754828e7c8b52282ad8dca612d7b6a", + "0x83e97e9ab3d9e453a139c9e856392f4cef3ec1c43bce0a879b49b27a0ce16f9c69063fd8e0debbe8fabafc0621bc200c", + "0x8c138ebdf3914a50be41be8aa8e2530088fb38af087fa5e873b58b4df8e8fd560e8090c7a337a5e36ef65566409ad8f3", + "0xa56da9db2f053516a2141c1a8ed368ae278ab33a572122450249056857376d1dffc76d1b34daf89c86b6fe1ead812a0c", + "0xa3233ea249f07531f5bc6e94e08cea085fd2b2765636d75ff5851f224f41a63085510db26f3419b031eb6b5143735914", + "0xb034bb6767ce818371c719b84066d3583087979ba405d8fbb2090b824633241e1c001b0cb0a7856b1af7a70e9a7b397e", + "0x8722803fe88877d14a4716e59b070dd2c5956bb66b7038f6b331b650e0c31230c8639c0d87ddc3c21efc005d74a4b5cc", + "0x8afe664cb202aacf3bd4810ebf820c2179c11c997f8c396692a93656aa249a0df01207c680157e851a30330a73e386b9", + "0xa999e86319395351d2b73ff3820f49c6516285e459224f82174df57deb3c4d11822fd92cbbed4fc5a0a977d01d241b19", + "0x9619408e1b58b6610d746b058d7b336d178e850065ba73906e08e748651e852f5e3aab17dcadcb47cc21ff61d1f02fcf", + "0x947cf9c2ed3417cd53ea498d3f8ae891efe1f1b5cd777e64cec05aba3d97526b8322b4558749f2d8a8f17836fb6e07aa", + "0xaec2fdae2009fda6852decb6f2ff24e4f8d8ca67c59f92f4b0cf7184be72602f23753ed781cf04495c3c72c5d1056ffe", + "0x8dba3d8c09df49fbfc9506f7a71579348c51c6024430121d1c181cad7c9f7e5e9313c1d151d46d4aa85fb0f68dd45573", + "0xb6334cb2580ae33720ebf91bb616294532a1d1640568745dcda756a3a096786e004c6375728a9c2c0fb320441e7d297a", + "0x9429224c1205d5ecd115c052b701c84c390f4e3915275bb8ce6504e08c2e9b4dd67b764dd2ea99f317b4c714f345b6ff", + "0xabe421db293f0e425cfd1b806686bdfd8fdbac67a33f4490a2dc601e0ddbf69899aa9a119360dad75de78c8c688ca08b", + "0x95c78bffed9ae3fff0f12754e2bd66eb6a9b6d66a9b7faaeb7a1c112015347374c9fe6ce14bf588f8b06a78e9a98f44c", + "0xac08f8b96b52c77d6b48999a32b337c5ad377adf197cda18dbdf6e2a50260b4ee23ca6b983f95e33f639363e11229ee4", + "0x911a0e85815b3b9f3ba417da064f760e84af94712184faeb9957ddd2991dee71c3f17e82a1a8fbeec192b0d73f0ebce7", + "0xaa640bd5cb9f050568a0ad37168f53b2f2b13a91e12b6980ca47ae40289cf14b5b89ddd0b4ca452ce9b1629da0ce4b5d", + "0x907486f31b4ecea0125c1827007ea0ecb1c55cadb638e65adc9810ca331e82bb2fd87e3064045f8d2c5d93dc6c2f5368", + "0x8cbfaf4ce0bbbf89208c980ff8b7bc8f3cfef90f0fe910f463cb1c0f8e17cce18db120142d267045a00ba6b5368f0dd3", + "0x9286f08f4e315df470d4759dec6c9f8eacef345fc0c0b533ad487bb6cfefa8c6c3821a22265c9e77d34170e0bc0d078b", + "0x94a3c088bc1a7301579a092b8ece2cefc9633671bc941904488115cd5cb01bd0e1d2deef7bdccb44553fd123201a7a53", + "0x8f3d0114fbf85e4828f34abb6d6fddfa12789d7029d9f1bb5e28bc161c37509afdab16c32c90ec346bc6a64a0b75726f", + "0xa8ed2d774414e590ec49cb9a3a726fafd674e9595dd8a1678484f2897d6ea0eea1a2ee8525afac097b1f35e5f8b16077", + "0x9878789ff33b11527355a317343f34f70c7c1aa9dc1eca16ca4a21e2e15960be8a050ec616ffb97c76d756ce4bce2e90", + "0x854e47719dae1fe5673cacf583935122139cf71a1e7936cf23e4384fbf546d48e9a7f6b65c3b7bf60028e5aa1234ba85", + "0xaf74bdda2c6772fe9a02d1b95e437787effad834c91c8174720cc6e2ea1f1f6c32a9d73094fc494c0d03eef60b1a0f05", + "0x80a3e22139029b8be32cb167d3bc9e62d16ca446a588b644e53b5846d9d8b7ab1ad921057d99179e41515df22470fb26", + "0x86c393afd9bd3c7f42008bba5fe433ec66c790ebd7aa15d4aeaf9bb39a42af3cfaf8c677f3580932bbd7ada47f406c8c", + "0x90433c95c9bb86a2c2ddcf10adccb521532ebd93db9e072671a4220f00df014e20cd9ce70c4397567a439b24893808dc", + "0x95b2c170f08c51d187270ddc4f619300b5f079bbc89dbca0656eae23eecc6339bf27fa5bf5fd0f5565d4021105e967d2", + "0x8e5eced897e2535199951d4cff8383be81703bca3818837333dd41a130aa8760156af60426ceadb436f5dea32af2814c", + "0xa254a460ebefbe91d6e32394e1c8f9075f3e7a2bb078430ac6922ab14d795b7f2df1397cb8062e667d809b506b0e28d4", + "0xac2062e8ca7b1c6afb68af0ebab31aebd56fc0a0f949ef4ea3e36baf148681619b7a908facf962441905782d26ecbdb5", + "0x8b96af45b283b3d7ffeec0a7585fc6b077ea5fd9e208e18e9f8997221b303ab0ce3b5bafa516666591f412109ce71aa5", + "0xafd73baada5a27e4fa3659f70083bf728d4dc5c882540638f85ea53bf2b1a45ddf50abc2458c79f91fb36d13998c7604", + "0xa5d2fff226e80cb2e9f456099812293333d6be31dd1899546e3ad0cd72b2a8bcb45ec5986e20faa77c2564b93983210c", + "0xa8c9b8de303328fbdaccf60f4de439cf28f5360cf4104581dc2d126bc2e706f49b7281723487ff0eaf92b4cc684bc167", + "0xa5d0d5849102bf1451f40e8261cb71fc57a49e032773cb6cd7b137f71ee32438d9e958077ffafce080a116ccc788a2d4", + "0x80716596f502d1c727d5d2f1469ce35f15e2dbd048d2713aa4975ee757d09c38d20665326bd63303cfe7e820b6de393d", + "0x97baf29b20f3719323cc1d5de23eaa4899dc4f4e58f6c356ec4c3ad3896a89317c612d74e0d3ab623fe73370c5972e2f", + "0xb58bdc9aa5061bf6e5add99a7443d7a8c7ba8f6875b8667d1acbe96fc3ecafbdcc2b4010cb6970a3b849fff84660e588", + "0xb6be68728776d30c8541d743b05a9affc191ad64918fdbd991d2ddd4b32b975c4d3377f9242defef3805c0bfb80fbac7", + "0xb0cddace33333b8a358acad84b9c83382f0569d3854b4b34450fd6f757d63c5bdab090e330b0f86e578f22c934d09c36", + "0x854bd205d6051b87f9914c8c2494075d7620e3d61421cc80f06b13cea64fd1e16c62c01f107a5987d10b8a95a8416ad9", + "0x80351254a353132300ba73a3d23a966f4d10ce9bf6eae82aedb6cdc30d71f9d08a9dd73cb6441e02a7b2ad93ad43159c", + "0x937aae24fb1b636929453fc308f23326b74c810f5755d9a0290652c9c2932ad52cc272b1c83bd3d758ef7da257897eae", + "0xb84d51ef758058d5694ffeac6d8ce70cef8d680a7902f867269c33717f55dd2e57b25347841d3c0872ae5f0d64f64281", + "0xa4b31bb7c878d5585193535b51f04135108134eff860f4eac941053155f053d8f85ff47f16268a986b2853480a6e75e6", + "0x93543f0828835186a4af1c27bdf97b5dd72b6dfa91b4bf5e759ff5327eaf93b0cb55d9797149e465a6b842c02635ffe5", + "0xafdac9e07652bf1668183664f1dd6818ef5109ee9b91827b3d7d5970f6a03e716adcc191e3e78b0c474442a18ad3fc65", + "0x9314077b965aa2977636ae914d4a2d3ce192641a976ffa1624c116828668edbfbe5a09e3a81cb3eed0694566c62a9757", + "0xb395ddcf5082de6e3536825a1c352802c557b3a5118b25c29f4c4e3565ecaaf4bdd543a3794d05156f91fc4ceadc0a11", + "0xb71f774aad394c36609b8730e5be244aaebfff22e0e849acc7ee9d33bedc3ec2e787e0b8b2ffe535560fcd9e15a0897e", + "0x92e9409fa430f943a49bce3371b35ac2efb5bc09c88f70ff7120f5e7da3258a4387dfc45c8b127f2ef2668679aeb314e", + "0x8ef55bef7b71952f05e20864b10f62be45c46e2dca0ef880a092d11069b8a4aa05f2e0251726aca1d5933d7dea98f3f8", + "0xaad3fba9e09fae885cdeef45dfafa901419f5156fb673818f92a4acc59d0e2e9870b025e711de590a63fd481164f3aa8", + "0xb444d52af545dd3a2d3dd94e6613816b154afea0c42b96468aceb0c721395de89e53e81a25db857ca2e692dcb24ba971", + "0x88b279fe173007e64fe58f2c4adba68a1f538dbd3d32d175aa0d026bbb05b72a0c9f5d02b8201a94adb75fe01f6aa8b2", + "0x88494cea4260741c198640a079e584cabfea9fcfb8bcf2520c9becd2419cde469b79021e5578a00d0f7dbc25844d2683", + "0x94f3cce58837c76584b26426b9abdb45f05fee34dd9e5914b6eae08e78b7262ed51c4317031dab1ad716f28b287f9fc2", + "0xb8c7ed564f54df01c0fbd5a0c741beed8183ce0d7842dc3a862a1b335de518810077314aa9d6054bb939663362f496da", + "0x81c153320d85210394d48340619d5eb41304daea65e927266f0262c8a7598321aba82ad6c3f78e5104db2afd2823baca", + "0xab6695a8d48a179e9cd32f205608359cf8f6a9aead016252a35b74287836aa395e76572f21a3839bec6a244aa49573e5", + "0x920ed571539b3002a9cd358095b8360400e7304e9a0717cc8c85ab4a0514a8ad3b9bf5c30cb997647066f93a7e683da9", + "0xa7ec7c194d1e5103bc976e072bf1732d9cb995984d9a8c70a8ee55ce23007f21b8549ad693f118aa974f693ed6da0291", + "0x87a042d6e40c2951a68afc3ccf9646baf031286377f37f6ac47e37a0ec04d5ac69043757d7dff7959e7cd57742017a8d", + "0xb9f054dd8117dd41b6e5b9d3af32ee4a9eebef8e4a5c6daa9b99c30a9024eabeae850ab90dbdb188ca32fd31fd071445", + "0xa8386da875799a84dc519af010eaf47cdbc4a511fe7e0808da844a95a3569ce94054efd32a4d3a371f6aba72c5993902", + "0x8b3343a7cf4ffb261d5f2dbd217fb43590e00feac82510bdf73b34595b10ee51acae878a09efebc5a597465777ef4c05", + "0x8312a5f1ea4f9e93578e0f50169286e97884a5ed17f1780275ab2b36f0a8aa1ab2e45c1de4c8bce87e99e3896af1fa45", + "0xb461198cb7572ac04c484a9454954e157bdd4db457816698b7290f93a10268d75a7e1211e757c6190df6144bbb605d91", + "0x9139764a099580d6f1d462c8bf7d339c537167be92c780e76acb6e638f94d3c54b40ed0892843f6532366861e85a515a", + "0x8bb70acb3c9e041b4fc20e92ba0f3f28f0d5c677bcb017af26f9171e07d28c3c0729bef72457231e3512f909455a13a2", + "0x93301a18e5064c55fcfe8e860fab72da1b89a824ca77c8932023b7c79e4a51df93a89665d308a8d3aa145e46ebe6a0ad", + "0xae3bca496fbd70ce44f916e2db875b2ce2e1ded84edd2cebc0503bdfdec40ec30e1d9afb4eb58c8fa23f7b44e71d88f8", + "0x93cb3a918c95c5d973c0cb7621b66081ed81fba109b09a5e71e81ca01ec6a8bb5657410fdec453585309ef5bf10d6263", + "0x95a50b9b85bb0fc8ff6d5f800d683f0f645e7c2404f7f63228a15b95ce85a1f8100e2e56c0acee19c36ed3346f190e87", + "0x816cc4d9337461caca888809b746ab3713054f5b0eac823b795a1a9de9417c58e32a9f020fef807908fa530cbf35dee8", + "0xa9c2890c2dd0d5d7aedc4cca7f92764086c50f92f0efd2642c59920d807086031bfe2d3ba574318db236c61a8f5f69c2", + "0xad0d5c8c80bddfe14bdaf507da96dc01dc9941aecc8ad3b64513d0a00d67c3f4b4659defb6839b8b18d8775e5344c107", + "0x9047c9fad6ef452e0219e58e52c686b620e2eb769571021e3524bd7eac504f03b84834b16b849d42b3d75c601fd36bb7", + "0xa04dd988fed91fb09cb747a3ac84efe639d7d355524cd7dee5477ecbcdec44d8ac1cec2c181755dcfdb77e9594fb3c5b", + "0xb0ea0c725debd1cec496ced9ce48f456f19af36e8b027094bf38fa37de9b9b2d10282363ea211a93a34a0a5387cace5d", + "0xb5fc46e2bb3e4653ea5e6884dcb3c14e401a6005685ee5a3983644b5b92300b7066289159923118df4332aac52045b8c", + "0x841fc5b26b23226e725e29802da86b35e4f5e3babc8b394f74e30fd5dec6d3840b19a9a096625ce79a4f1edae6369700", + "0x8fd2bbbeea452451def3659bbe0ceb396120ebe8f81eee1ea848691614422c81d7c3e6a7a38032b4120b25c5ffa8f0c2", + "0x9131ce3d25c3d418f50c0ab99e229d4190027ee162b8ba7c6670420ea821831dec1294ac00d66c50fac61c275a9e2c71", + "0x99ec6eafe0eb869d128158cee97b984fb589e1af07699247946e4a85db772289dff3084d224a6f208005c342f32bbd73", + "0xac100fbbe7c2bf00cc56fcd5aa1f27181f82c150c53bbb1e15d2c18a51ed13dcfa7bccab85821b8ddddf493603e38809", + "0xaffd73a458d70c0d9d221e0c2da4348fed731f6b34c0b3e2d5711ba432e85a1ec92e40b83b246a9031b61f5bc824be47", + "0x8ed30ed817816a817e9e07374ef1f94405a7e22dd0096aeaae54504382fc50e7d07b4f1186c1792fc25ea442cd7edc6b", + "0xa52370cfe99a35fa1405aeca9f922ad8d31905e41f390e514ea8d22ee66469637d6c2d4d3a7ee350d59af019ae5a10a4", + "0x8d0b439741c57b82c8e4b994cf3956b5aeaee048b17e0a1edb98253a8d7256f436d8b2f36b7e12504132dbf91f3376b1", + "0x8caac7e1a4486c35109cff63557a0f77d0e4ca94de0817e100678098a72b3787a1c5afc7244991cebcd1f468e18d91d4", + "0xa729a8e64b7405db5ebfb478bb83b51741569331b88de80680e9e283cc8299ba0de07fcf252127750f507e273dc4c576", + "0xa30545a050dad030db5583c768a6e593a7d832145b669ad6c01235813da749d38094a46ac3b965700230b8deacd91f82", + "0x9207e059a9d696c46fa95bd0925983cd8e42aefd6b3fb9d5f05420a413cbc9e7c91213648554228f76f2dd757bde0492", + "0xa83fa862ae3a8d98c1e854a8b17181c1025f4f445fbc3af265dc99e44bbd74cfa5cc25497fb63ee9a7e1f4a624c3202c", + "0x84cdfc490343b3f26b5ad9e1d4dcf2a2d373e05eb9e9c36b6b7b5de1ce29fda51383761a47dbd96deca593a441ccb28e", + "0x881a1aa0c60bb0284a58b0a44d3f9ca914d6d8fa1437315b9ad2a4351c4da3ee3e01068aa128284a8926787ea2a618d1", + "0xaace78e497b32fbff4df81b1b2de69dbc650645e790953d543282cb8d004a59caf17d9d385673a146a9be70bf08a2279", + "0xaa2da4760f1261615bffd1c3771c506965c17e6c8270c0f7c636d90428c0054e092247c3373eca2fb858211fdb17f143", + "0xacb79f291b19e0aa8edb4c4476a172834009c57e0dcc544c7ce95084488c3ad0c63ffd51c2b48855e429b6e1a9555433", + "0x814b58773a18d50a716c40317f8b80362b6c746a531776a9251c831d34fb63e9473197c899c0277838668babc4aa0ecb", + "0xb1f69522b0f7657d78bd1ee3020bcce3447116bf62c146d20684537d36cafb5a7a1531b86932b51a70e6d3ce0808a17e", + "0x8549712c251ef382f7abe5798534f8c8394aa8bcecdca9e7aa1a688dc19dc689dcd017a78b118f3bd585673514832fe4", + "0x912a04463e3240e0293cfc5234842a88513ff930c47bd6b60f22d6bc2d8404e10270d46bf6900fee338d8ac873ebb771", + "0xa327cb7c3fada842e5dd05c2eeedd6fcd8cf2bfb2f90c71c6a8819fb5783c97dd01bd2169018312d33078b2bc57e19f7", + "0xb4794f71d3eceed331024a4cee246cc427a31859c257e0287f5a3507bfbd4d3486cb7781c5c9c5537af3488d389fe03e", + "0x82ffcb418d354ed01688e2e8373a8db07197a2de702272a9f589aed08468eab0c8f14e6d0b3146e2eb8908e40e8389c5", + "0x910b73421298f1315257f19d0dfd47e79d7d2a98310fb293f704e387a4dc84909657f0f236b70b309910271b2f2b5d46", + "0xa15466397302ea22f240eb7316e14d88376677b060c0b0ae9a1c936eb8c62af8530732fc2359cfd64a339a1c564f749b", + "0xa8091975a0d94cdc82fbaff8091d5230a70d6ea461532050abbdfee324c0743d14445cfe6efe6959c89a7c844feaa435", + "0xa677d1af454c7b7731840326589a22c9e81efbbf2baf3fdeaf8ea3f263a522584fbca4405032c4cdf4a2a6109344dfc8", + "0x894e6ffa897b6e0b37237e6587a42bbc7f2dd34fb09c2e8ac79e2b25b18180e158c6dc2dd26761dba0cfed1fb4eb4080", + "0x928d31b87f4fe8fe599d2c9889b0ff837910427ba9132d2fba311685635458041321ae178a6331ed0c398efe9d7912f0", + "0xafc1c4a31f0db24b53ee71946c3c1e1a0884bd46f66b063a238e6b65f4e8a675faa844e4270892035ef0dae1b1442aa0", + "0xa294fcb23d87cf5b1e4237d478cac82ba570649d425b43b1e4feead6da1f031e3af0e4df115ca46689b9315268c92336", + "0x85d12fd4a8fcfd0d61cbf09b22a9325f0b3f41fb5eb4285b327384c9056b05422d535f74d7dc804fb4bab8fb53d556bd", + "0x91b107d9b0ea65c48128e09072acd7c5949a02dd2a68a42ff1d63cf528666966f221005c2e5ca0a4f85df28459cdede6", + "0x89aa5dc255c910f439732fcd4e21341707e8dd6689c67c60551a8b6685bd3547e3f47db4df9dfadd212405f644c4440b", + "0x8c307d6b827fa1adcf0843537f12121d68087d686e9cc283a3907b9f9f36b7b4d05625c33dab2b8e206c7f5aabd0c1e5", + "0x843f48dadf8523d2b4b0db4e01f3c0ea721a54d821098b578fcaa6433e8557cadfea50d16e85133fa78f044a3e8c1e5b", + "0x9942eb8bd88a8afa9c0e3154b3c16554428309624169f66606bfb2814e8bac1c93825780cf68607f3e7cffe7bf9be737", + "0xb7edb0c7637a5beb2332f2ae242ba4732837f9da0a83f00f9e9a77cf35516e6236eb013133ddc2f958ea09218fe260d3", + "0x9655fe4910bc1e0208afbcf0ff977a2e23faded393671218fba0d9927a70d76514a0c45d473a97ecb00cf9031b9d527c", + "0x8434bc8b4c5839d9e4404ff17865ded8dd76af56ef2a24ea194c579d41b40ed3450c4e7d52219807db93e8e6f001f8da", + "0xb6c6d844860353dab49818bed2c80536dbc932425fdaa29915405324a6368277cf94d5f4ab45ea074072fc593318edff", + "0xb2887e04047660aa5c83aad3fa29b79c5555dd4d0628832c84ba7bf1f8619df4c9591fcde122c174de16ca7e5a95d5e3", + "0x953ba5221360444b32911c8b24689078df3fbf58b53f3eec90923f53a22c0fc934db04dd9294e9ec724056076229cf42", + "0x926917529157063e4aade647990577394c34075d1cb682da1acf600639d53a350b33df6a569d5ebb753687374b86b227", + "0xb37894a918d6354dd28f850d723c1c5b839f2456e2a220f64ecadac88ae5c9e9cf9ab64b53aac7d77bf3c6dfa09632dc", + "0xb9d28148c2c15d50d1d13153071d1f6e83c7bb5cb5614adf3eb9edede6f707a36c0fa0eadb6a6135ead3c605dfb75bd1", + "0x9738d73ea0b9154ed38da9e6bd3a741be789ea882d909af93e58aa097edf0df534849f3b1ba03099a61ceb6a11f34c4d", + "0xafabbecbbf73705851382902ec5f1da88b84a06b3abfb4df8d33df6a60993867f853d0d9bd324d49a808503615c7858a", + "0xa9e395ddd855b12c87ba8fdb0ea93c5bd045e4f6f57611b27a2ee1b8129efe111e484abc27cb256ed9dcace58975d311", + "0xb501c2f3d8898934e45e456d36a8a5b0258aeea6ff7ac46f951f36da1ec01bd6d0914c4d83305eb517545f1f35e033cc", + "0x86f79688315241fe619b727b7f426dbd27bcc8f33aef043438c95c0751ada6f4cd0831b25ae3d53bcf61324d69ea01eb", + "0x83237e42fa773a4ccaa811489964f3fab100b9eea48c98bdef05fa119a61bde9efe7d0399369f87c775f4488120b4f2e", + "0xb89f437552cab77d0cd5f87aca52dd827fb6648c033351c00ab6d40ac0b1829b4fcdf8a7dad467d4408c691223987fbe", + "0x8e21061698cb1a233792976c2d8ab2eeb6e84925d59bb34434fff688be2b5b2973d737d9dda164bd407be852d48ef43f", + "0xb17a9e43aa4580f542e00c3212fbf974f1363f433c5502f034dfd5ed8c05ac88b901729d3b822bec391cca24cc9f5348", + "0xaac6d6cda3e207006c042a4d0823770632fc677e312255b4aff5ad1598dc1022cab871234ad3aa40b61dc033a5b0930b", + "0xb25e69f17b36a30dada96a39bc75c0d5b79d63e5088da62be9fcbddfd1230d11654890caa8206711d59836d6abbc3e03", + "0xaf59fe667dd9e7e4a9863c994fc4212de4714d01149a2072e97197f311be1f39e7ad3d472e446dcc439786bf21359ede", + "0x957952988f8c777516527b63e0c717fc637d89b0fd590bcb8c72d0e8a40901598930c5b2506ff7fea371c73a1b12a9be", + "0xa46becd9b541fc37d0857811062ca1c42c96181c7d285291aa48dc2f6d115fcff5f3dfdf4490d8c619da9b5ce7878440", + "0x87168fbd32c01a4e0be2b46fe58b74d6e6586e66bbb4a74ad94d5975ac09aa6fa48fd9d87f1919bd0d37b8ebe02c180c", + "0x895c4aa29de9601fc01298d54cfb62dd7b137e6f4f6c69b15dc3769778bfba5fc9cbd2fc57fd3fad78d6c5a3087f6576", + "0xb9cf19416228230319265557285f8da5b3ca503de586180f68cf055407d1588ecec2e13fc38817064425134f1c92b4d5", + "0x9302aaef005b22f7b41a0527b36d60801ff6e8aa26fe8be74685b5f3545f902012fcade71edca7aaa0560296dac5fca5", + "0xa0ccda9883027f6b29da1aaa359d8f2890ce1063492c875d34ff6bf2e7efea917e7369d0a2b35716e5afd68278e1a93a", + "0xa086ac36beeba9c0e5921f5a8afea87167f59670e72f98e788f72f4546af1e1b581b29fbdd9a83f24f44bd3ec14aee91", + "0x8be471bf799cab98edf179d0718c66bbc2507d3a4dac4b271c2799113ce65645082dc49b3a02a8c490e0ef69d7edbcb1", + "0x8a7f5b50a18baf9e9121e952b65979bda5f1c32e779117e21238fb9e7f49e15008d5c878581ac9660f6f79c73358934a", + "0xb3520a194d42b45cbab66388bee79aad895a7c2503b8d65e6483867036497d3e2e905d4d51f76871d0114ec13280d82f", + "0x8e6ca8342ec64f6dbe6523dc6d87c48065cd044ea45fa74b05fff548539fd2868eb6dd038d38d19c09d81d5a96364053", + "0xb126a0e8263a948ba8813bf5fb95d786ae7d1aa0069a63f3e847957822b5fe79a3a1afa0ce2318b9ba1025f229a92eb7", + "0x8e4461d6708cac53441a3d23ac4b5ff2b9a835b05008c26d7d9c0562a29403847cf760b7e9d0bcb24a6f498d2a8a9dd2", + "0xb280a761bab256dfe7a8d617863999e3b4255ddbdc11fe7fe5b3bb9633fc8f0cb4f28e594d3b5b0b649c8e7082c4666a", + "0xa3e3043bfd7461e38088ee6a165d2ca015de98350f1cb0efc8e39ed4fcdb12a717f0ede7fbf9dadb90496c47652cc0ce", + "0xa4c1f5b1b88ae3c397d171e64395afe0cd13c717677775a01dd0461d44a04ee30ec3da58a54c89a3ca77b19b5e51062c", + "0xa268638e0655b6d5a037061808619b9ae276bb883999d60c33a9f7f872c46d83d795d1f302b4820030c57604fa3686e7", + "0xac20176111c5c6db065668987227658c00a1572ce21fe15f25e62d816b56472c5d847dd9c781fb293c6d49cc33b1f98f", + "0xacc0e22d9b6b45c968c22fd16b4ece85e82a1b0ab72369bdd467857fee1a12b9635f5b339a9236cbd1acc791811d0e29", + "0xb56066e522bee1f31480ff8450f4d469ace8eb32730c55b7c9e8fa160070bdec618454e665b8cbc5483bc30b6cebbfb9", + "0x8c1772bdfacff85f174d35c36f2d2182ae7897ad5e06097511968bbb136b626c0c7e462b08a21aca70f8e456b0204bf8", + "0xb4de3cf4a064bf589be92513b8727df58f2da4cd891580ef79635ac8c195f15a6199327bb41864e2f614c8589b24f67e", + "0x8f3c534125613f2d17bf3e5b667c203cb3eab0dbca0638e222fe552fddf24783965aa111de844e8c3595304bfc41c33b", + "0x8e445b2711987fe0bf260521cb21a5b71db41f19396822059912743bf6ca146100c755c8b6e0e74f1bf2e34c03b19db9", + "0x87ff9adf319adb78c9393003b5bdda08421f95551d81b37520b413fe439e42acf82d47fa3b61476b53166bf4f8544f0e", + "0x83f3c00c55632e1937dcdc1857de4eccd072efa319b3953d737e1d37382b3cf8343d54a435588eb75aa05bf413b4caa0", + "0xb4d8ee1004bac0307030b8605a2e949ca2f8d237e9c1dcf1553bd1eb9b4156e2deb8c79331e84d2936ec5f1224b8b655", + "0x93b2812b6377622e67bf9a624898227b56ebe3c7a1d917487fc9e4941f735f83679f7ac137065eb4098ad1a4cfbc3892", + "0x81943d9eab6dcea8a120dde5356a0a665b1466709ebb18d1cbfa5f213a31819cb3cf2634e6d293b5b13caa158a9bb30b", + "0xa9042aae02efd4535681119e67a60211fc46851319eb389b42ebadcab1229c94199091fb1652beba3434f7b98c90785f", + "0x91db52b27fd9b1715df202106b373c4e63ce8ec7db8c818c9016ace5b08ef5f8c27e67f093395937ba4ce2f16edf9aef", + "0x83cb9b7b94bd6ead3ff2a7d40394f54612c9cb80c4e0adadffea39e301d1052305eb1fe0f7467268b5aba3b423a87246", + "0x8720fd6712a99d92dd3fdaae922743ab53fad50d183e119a59dae47cdac6fbea6064c732d02cb341eaea10723db048fa", + "0x8d40022c1254462a2ac2380a85381c370b1221e5a202d95c75bccba6d1e52972dd5585a1294a1e487bf6ae6651867167", + "0xb7bc06e08d8c72daba143627582f4b4f34cc2234b5cb5cd83536f2ef2e058631a3920468ea4d550aea01cad221d6a8a6", + "0xa6e1a6f70fba42d3b9ce5f04ffdcfca46fc94041840c0066a204030cf75ea9f9856113fea3a9f69ea0037d9a68e3a9d4", + "0x8b064c350083fce9a52da2e2e17bf44c4c9643d2d83667cbd9ad650bbeba55e2c408e746ccf693e56d08826e8a6d57fc", + "0x8d304a5405a0c0696917fcddc6795dd654567ca427f007d9b16be5de98febbf8692374e93f40822f63cf6f143c4d9499", + "0xb968db239efec353a44f20a7cf4c0d0fca4c4c2dc21e6cbb5d669e4fe624356a8341e1eec0955b70afb893f55e9a9e32", + "0x98971f745ce4ce5f1f398b1cd25d1697ada0cc7b329cee11d34b2d171e384b07aeb06ac7896c8283664a06d6dd82ec6b", + "0x881f5a20a80f728354fad9d0a32a79ffe0ba9bed644ed9d6a2d85444cda9821018159a3fa3d3d6b4fadbf6ea97e6aff6", + "0xb7c76cbb82919ec08cf0bd7430b868a74cb4021e43b5e291caa0495ca579798fab1b64855e2d301f3461dd4d153adeb6", + "0xb44c8c69b3df9b4e933fe6550982a6f76e18046e050229bd2456337e02efb75efa0dfe1b297ed9f5d7fa37fec69c8374", + "0xa5bd7781820ba857aee07e38406538b07ab5180317689a58676f77514747672dd525ea64512a0e4958896f8df85e9d4d", + "0xa8443d1dc91b4faa20a2626505b5b4ad49cc5c1fd7a240a0e65d12f52d31df1585ba52c21e604dcec65ec00b81ae21fe", + "0xa157ae42fc6302c54bcdd774e8b8bafc4f5d221717f7bf49668c620e47051b930dce262d55668e546272dd07ca7c8d3f", + "0x8732c10448b63e907ff95f53cd746f970c946fd84fcbfe4cf9ede63afbbfc66b293bbc7c470d691bbd149bb3c78bb351", + "0xa82192f4fd9a0c33489a0486b79d0f6c797c7eccb45f91f7f1e8e1dd1924ca9944b983951025b99ab5861d31841451fe", + "0x839efc6d199ddd43f34f6729b6b63f9ee05f18859bf8fd3f181fa71f4399a48bff7dde89b36e9dc1c572f1b9b6127cca", + "0x992ef084abe57adfd5eb65f880b411d5f4ed34c1aeb0d2cfac84fff4f92a9a855c521a965ba81b5eef2268e9a9e73048", + "0xa2518ab712fa652e6e0bd0840307ef3831094e9a18723fb8ec052adacbb87f488d33778c6ec3fd845003af62e75125d1", + "0xb630ac3c9e71b85dd9e9f2984bb5b762e8491d8edb99cad82c541faf5a22dd96f0fddb49d9a837b1955dea2d91284f28", + "0x8d886d1b7f818391b473deba4a9a01acce1fe2abe9152955e17ba39adc55400590c61582c4fef37a286e2151566576ed", + "0x884f100dc437639247f85e5d638fcc7583d21bf37a66ce11e05bfc12f5dbe78685b0e51b4594e10549c92bb980512e12", + "0x806d7bac2d24cfff6090ba9513698292d411cdea02976daa3c91c352b09f5a80a092cfa31304dcfcd9356eaf5164c81b", + "0x934ed65f8579ee458b9959295f69e4c7333775eb77084db69ad7096f07ad50ad88f65e31818b1942380f5b89e8d12f1b", + "0xaaf50ca5df249f0a7caf493334b6dca1700f34bd0c33fe8844fadd4afedbb87a09673426741ac7cbbb3bf4ab73f2d0f3", + "0xb2868642cfa0a4a8a2553691c2bef41dab9dff87a94d100eaa41645614ab4d0e839ec2f465cc998c50cd203f0c65df22", + "0xa326513112e0b46600d52be9aa04d8e47fe84e57b3b7263e2f3cf1a2c0e73269acb9636a99eb84417f3ae374c56e99b0", + "0x97b93efc047896ddf381e8a3003b9e1229c438cc93a6dbef174bb74be30fac47c2d7e7dc250830459bed61d950e9c924", + "0xb45e4f0a9806e44db75dbb80edc369be45f6e305352293bcae086f2193e3f55e6a75068de08d751151fdf9ebc6094fa1", + "0x87f2161c130e57e8b4bb15616e63fa1f20a1b44d3e1683967a285f0d4f0b810f9202e75af2efa9fc472687c007a163f7", + "0x8f6400a45666142752580a2dce55ef974f59235a209d32d2036c229c33a6189d51435b7ea184db36f765b0db574a9c52", + "0xa0ee079462805f91b2200417da4900227acde0d48c98e92c8011a05b01c9db78fc5c0157d15cb084b947a68588f146f4", + "0xab0612d9bb228b30366b48e8d6ae11026230695f6f0607c7fa7a6e427e520121ff0edea55d1f0880a7478c4a8060872d", + "0xad65dfde48f914de69f255bb58fa095a75afe9624fc8b7b586d23eb6cf34a4905e61186bc978e71ccb2b26b0381778a6", + "0x8c8a4847d138d221c0b6d3194879fd462fb42ed5bd99f34ebe5f5b1e1d7902903ec55e4b52c90217b8b6e65379f005a4", + "0xa41dca4449584353337aef1496b70e751502aeed9d51202de6d9723e155ca13be2d0db059748704653685a98eaa72a07", + "0xae40e5450fd994d1be245a7cd176a98dd26332b78da080159295f38802a7e7c9c17cc95da78d56558d84948cf48242cd", + "0x863878fda80ad64244b7493e3578908d4a804887ad1ad2c26f84404dcad69ea2851846ad2c6f2080e1ed64fe93bbec31", + "0xb262fb990535f162dc2b039057a1d744409a3f41dd4b70f93ff29ba41c264c11cb78a3579aad82f3fa2163b33a8ce0e1", + "0xa7f6eb552b9a1bb7c9cb50bc93d0dda4c7ecf2d4805535f10de0b6f2b3316688c5e19199d5c9ec2968e2d9e2bd0c6205", + "0xa50aa5869412dc7081c8d827299237910ecec3154587692548da73e71fa398ff035656972777950ba84e472f267ba475", + "0x924c3af750afc5dfad99d5f3ed3d6bdd359492cff81abcb6505696bb4c2b4664926cb1078a55851809f630e199955eb3", + "0xa1acffa31323ce6b9c2135fb9b5705664de8949f8235b4889803fbd1b27eb80eb3f6a81e5b7cc44e3a67b288b747cf2f", + "0x8dec9fd48db028c33c03d4d96c5eecea2b27201f2b33d22e08529e1ae06da89449fe260703ac7bb6d794be4c0c6ea432", + "0xaa6642922ccf912d60d678612fffe22ef4f77368a3c53a206c072ed07c024aa9dcde2df068c9821b4c12e5606cfe9be2", + "0xa16ddf02609038fcb9655031b1cb94afe30b801739e02a5743c6cd2f79b04b2524c2085ca32ec3a39df53de0280f555d", + "0xb067d48589e9d3428c6d6129e104c681e4af376a351f502840bbea6c3e11fcbfdf54dadf6f1729621720a75ff89786c3", + "0xb14a24079de311c729750bb4dd318590df1cd7ffc544a0a4b79432c9a2903d36a0d50ecd452b923730ade6d76a75c02c", + "0x97437bac649f70464ace93e9bec49659a7f01651bba762c4e626b5b6aa5746a3f0a8c55b555b1d0dc356d1e81f84c503", + "0xa6f4cb2ffc83564b1170e7a9a34460a58a4d6129bd514ff23371a9e38b7da6a214ac47f23181df104c1619c57dff8fe2", + "0x896d0f31dfc440cc6c8fde8831a2181f7257ffb73e1057fd39f1b7583ea35edf942ad67502cd895a1ad6091991eabc5e", + "0x9838007f920559af0de9c07e348939dfd9afe661b3c42053b4d9f11d79768cba268a2ee83bb07a655f8c970c0ee6844b", + "0xb41b8a47e3a19cadec18bff250068e1b543434ce94a414750852709cd603fc2e57cd9e840609890c8ff69217ea1f7593", + "0xa0fb4396646c0a2272059b5aeb95b513e84265b89e58c87d6103229f489e2e900f4414133ed2458ddf9528461cfa8342", + "0xae026cfa49babc1006a3e8905d6f237a56a3db9ddf7559b0e4de8d47d08c3f172bde117cdf28dfdfd7627bd47d6a3c85", + "0xa6a3f3e7006bc67290c0c40c1680bf9367982eb8aaf17ecb484a58c8e9c2a7c24932e2caa9aacc9b4fbf4c0abd087a46", + "0x9093e05bd814177a01a3b8d7b733db66294e1c688c56def6e1827c0f2d9a97cf202721641bf81fb837f8581ae68cb5ce", + "0x87feef4de24942044f47d193d4efc44e39a8c0f4042fba582f2491a063e3a4640cb81f69579b6f353b9208884a4f7ce6", + "0x975f9b94e78aac55bd4755f475e171e04f6fbddb6fd3d20a89a64a6346754a3ff64ecff8c04b612a1250e1d8d8a9e048", + "0x87cde4d0164922d654cf2dc08df009e923c62f1a2e3b905dfde30f958e9e4dd6070d9f889712acd6c658804f48f3edb1", + "0xae8e22e158dda90a185eec92602831b5d826e5a19aab8c6400dba38b024c7d31c4cf265eb7b206dd45834f020b3f53cd", + "0xa4475807adc28aa086e977b65bbd7c8512119318c89d2619ea03a6739a72c3fb90c9622451896c7113ad4d12a3004de6", + "0x97f1ae1e0d258a94532c7b73fa8ebdbbd53349a4d2d0a217fe56dfdd084dd879960bc6ff45ebb61b5dbf2054642800a4", + "0xb3c832bd3691332a658b0caaa7717db13f5b5df2b5776b38131ac334b5fd80d0b90b6993701e5d74d2b7f6b2fd1f6b9d", + "0xa4b6af590187eb1b2cb5ae2b8cffa45c5e76abdb37cec56fc9b07a457730f5af0706d9ce0a17da792bbece5056d05670", + "0x97b99a73a0e3145bf91f9dd611a67f894d608c954e9b8f5a4c77e07574064b3db47353eba8038062cebaad06a2500bab", + "0x8e5ca5a675de6e6d3916bd9ce5898bb379372afe3f310e70ff031bc8cc8fabfb7f3bfb784f409bb7eb06fdb4511ee477", + "0xaabbbee4da1f16b5bbe001c19debe04745932d36dfbbf023fbf1010a2b1d54eb92fa5e266ac1e9337e26e2ddba752f40", + "0xb13447c77496825f48e35c14f9b501c5056e6d5519f397a2580cea9a383a56a96994d88926aa681142fe2f1589c03185", + "0xb89c55db39ff0e73dde7435b61e8a4d3e10f51dd8096cbc7f678661962e6de3d16f2f17a0e729cc699234cb847f55378", + "0x82c36b7de53698a1bafbb311fefc6007fcefa47a806ebe33a4e7e0fc1c7b6b92a40a1860702cf9295a16c6b1433e3323", + "0x8daeec8c88543d09c494a15cc9a83c0b918d544311fd2a7d09e06cf39cdebfa0cfc0e8fc0e3b5954960b92332f98697c", + "0xb18e55a1a7ae16be3a453d2bfa7659a7ec2d283dd46bdc82decef6d3751eeafc4f86f2416a22955c7e750c0582d4f3eb", + "0xb50c743462e2915bf773848669e50a3bcdb5a9ac5f664e97eaccf568c7d64a6493d321be0225de16142ce82ce1e24f66", + "0xaf69c9643805fb860434424b1608aababc593aaebc6a75fc017f7f62bb2b1da932b0b9bd5e6dcbba328422dafc06efd8", + "0xb5947db4f809fd0d27af838b82eef8ab4fe78687a23ebc61c09c67eb7e8d0e6a310ecb907fd257859d5a2759a07c21cc", + "0x92c7960e163ca5bdf9196c7215102f8e9d88efc718843321c6e2a6170137b8ecec4ea5d5a5ce4c28012b6cdbd777dd01", + "0xb63f9509ed5e798add4db43b562e8f57df50d5844af6e5c7acf6c3b71637c0a2d2433f4a0627b944f0af584892208bb8", + "0x8ef28304a9bfe5220af6a9a6a942d2589606f5dc970d708ef18bc7ed08e433161020d36fb327c525398cd8ecb57002f9", + "0xb722e0410f896c4462d630a84a5a14e94289fc38ed6d513ca88a09005935cec334c480028efa1943c7a5e202ae8c8379", + "0xb56b6672b488e64d4dde43571f9ceaa7e61e336b0fd55bb769a57cd894a6300e724e5f88bad39a68bc307eb7406cb832", + "0x8bf493da411fd41502b61a47827731193652e6ce3810709e70869d9aae49e4b17a40437a7a0dcc0547dbac21f355c0da", + "0x9613b60a144c01f6a0e7d46ddde07402e2133a1fe005c049a56415ff90401765040b2fc55971d24b94c5fd69fec58941", + "0x85e2f02b291563d8eea3768cf6a4602c0ca36568ffcf3d93795d642044196ca6b0b28991ea5898e7974ee02831a0ec70", + "0xb08ef66703dd9ac46e0208487566fbf8d8654d08c00f03e46f112c204782ccc02a880a3f9dffd849088693cee33b7b6d", + "0xa0b19eeda6c71b0e83b1f95dffef4d370318bdea6ea31d0845695e6b48d5c428c3dbba1a0ded80964992c4a0695f12ee", + "0xb052642e5772d2ef6f49dd35c5e765c5f305006b2add3b4bee5909ca572161edf0e9c2bc3bc3bc7f56fd596360ef2201", + "0x8261af164c768fec80d63fca6cd07d1c0449e9ca665fe60c29babdbd8a2b20cf1f556a4b24cd7341712468a731c21b32", + "0x8a17016a1b2fc0fa0d9e3610ea80548fcf514e0a35e327f6b5f8069b425c0f0829af7e206013eab552be92b241be5ac5", + "0x8eea25c680172696f5600271761d27ef4c8cec9ab22f01f72b2c7c313a142fafaec39e6920b96fcace858883e02eff7a", + "0xb8e0c590106e125c5bca7e7a071cc408b93629da0d8d6381f1b73fbdf17024a0cf13f679f5203a99bbbcb664b4a94e88", + "0xb9943b29395258b7afdf1781cfaf131297a4f325540755df73401b2ec4a549f962952e9907413c39a95585c4aff38157", + "0x8286eab4a04f8113fb3f738a9bc9c2deaf3a22bf247151515568703da4efe6450ab3970f5c74e978a2db7e8d795331b7", + "0xa10cf383c8a7e3f0a0a5556b57532170ff46dabdcbb6a31c4617271634b99540aa575786c636d3809207cbf1d2f364d3", + "0xa5af7eb998140d01ba24baa0e8c71625aee6bd37db4c5ff607518f907892219ba8c9a03c326b273bfd7068232809b73c", + "0xaed5f461e38fccc8b3936f1328a9747efcbceb66312f6d6eddce57c59570852767159f1a7d9998f63342515fef4ba9bf", + "0xaec3e94b029aa692bfe2b8dbc6c3b0d132b504242e5ebe0cad79c065085e2fc05550e5cdaa2353892a40ff1a062dd9eb", + "0x87c23703960129396018d0347f5dd034abdbd57232b74195b6a29af34b6197b3cd63c60ac774d525add96ae54d5c0fb4", + "0x97964a7768216e1c84dece71ce9202cc64b6d483650aa6f6d67215f655f66cda14df0a0f251db55832c77bfd9b6316e2", + "0x8167aaf24c8a023d0aea16b8c24d993618b9d0c63619e11a28feab8f14952bafcb0918ed322cbc0ae1b2e1786071819b", + "0xb58318bd62852ffb712fc58f368c21b641dde7b3fa7d7269974c7a7b5b3e1641569fc7b5f32ca49de22f4f993506d92d", + "0xb172e7911d5cd3f53af388af847b928947c711185aebd3328f8e6ed1106c161ae0c1b67d3d9eb237e9e66eb0672edec0", + "0xa6834cf69b2c4433cf6e779bfbb736b12e73e71e149c38101d13dbacf6c5048db53994a6a039381df40bbd67de40fcd0", + "0x882604aa3bb19fffd6db744b5cf4a2431b157dac06d0617e0703684a118ca90b2d22a7758a1de7732a7144e68b11b7f7", + "0xaddc128ba52bf7553b9ba49eff42004d388a02c6b6e9809abe1c0d88f467e5ff6cb0c82a8fd901b80dfc9a001f7b9997", + "0xabf19604a3f0cffefa7a9ced81627f6aacb8d7267b52b825f25d813d9afa24af6d70da21450ed93eaff8b4d2a9b905a9", + "0xa3c67e7bf02dbca183d86924611a7149556ee17cb3469793624da496b6c25617a9071925dd02aab9cb028739cb79043d", + "0xb1cea4284a3ac4d5b1c6f0947c6ec8365b3281ed15495bf328a907a9a02cdd186e7cb1ef080385b3399df786855985a9", + "0xa6edb126314559e6129caf1111dc3c82ff914efce658b11f2c9b48081be1cf3f46bde482469d493379025a158d95ab1b", + "0x9843fd7dd424da1acc6f92f87fac364a8b0d4097d74b6b451386384966c85145d43fc6ecedf04271b0f963ac731fd93f", + "0x83852bedca03a97a2e63053cb102387866cbefe6707ebb6dae2d32a59c343079f1a863f299fd64d0ecbe024d0a1247d5", + "0xa570e645a0679ebc6f0ca03cc8f7367b03c3886f3d9c787992de7f3e93360a170d3ac9ae7720999c727a887b1dc762bb", + "0xad644c40555238f28844eed632c8972b63d2602098031d53b5599d1a874903e0d0c428e0ab12a209ea3fb31225578f1c", + "0xb64e9f92a14812ed31075f9fdd3324659a036ef2f293ef9ca6f6feb87d0c138e1ba74bc36a910afd22ff9b3c8ec7cfa5", + "0x8f2d75a86d517dafac09b65596f4b89c4a9c0a7003632407504153fa297c9e3228e236948a5d5224b8df49a087c8e0e3", + "0xb02d6ab9292ae336c8a74115f33765af2c9f62c331d70c087cf4c2979792bb3c2666f6699c017f8d4c6b378fd4bda86a", + "0xa923d660d2e55228b8bc74f87d966069bd77c34a776fa96f37b48539c85634482e514e2cb76cb8eb20efd85eb9c83fae", + "0x81d7ffb53090a6d512055ecfd582ca92805525a05654e39bb12653a6a8902a16e651ba7b687b36b8bea7186632c7e9e3", + "0x83e9b33e29b57ae53d9f72bd4622ff388252333b4fa32ad360a5b00f3ffc8813b9cb8a1361454d3bb7156c01b94b6a08", + "0xad7d6bffe4d67eb53b58daa3fc8a5a60790c54fa42226ae12847e94c6de3b4365b3be39855a4f6a5f12e4803cdaed96b", + "0xa7709fed85abbee5a2fa49c5238582ec565da08c132d4912821491985bf83b681eb4823634bfe826abd63a6c41a64ea7", + "0xb8fb6ed55741132a1053b6ca77bdf892e96b048488373ba4aa2f2225fae6d578724124eb6975e7518e2bf3d25d215763", + "0x85e0c53089529a09b5bce50f5760af6aeafef9395388aa4b6144ca59953169101783347ee46264ec0163713a25fe7c63", + "0x8f9e47a9c37b678e56c92b38d5b4dab05defc6b9c35b05e28431d54b1d69ac31878c82c1357d016f3e57ca07d82d9c16", + "0xa81f508136ee6ec9122c48584df51637f768ccfe8a0b812af02b122a0fafa9abcc24778bf54143abb79eccebbdde2aac", + "0x931a96d2257a4714d1ef20ac0704438481632647b993467e806b1acc4a381cc5a9dec257e63239ba285deb79f92122dd", + "0x99fb0ff747bcd44b512bf8a963b3183ce3f0e825a7b92ddd179253e65942a79494a515c0c0bc9345db136b774b0a76b0", + "0xa9dbb940b5f8ab92f2d85fc5999e982e3d990fe9df247cfc6f3a3f8934fb7b70e2d0362ba3a71edc5d0b039db2a5f705", + "0x99011a1e2670b1b142ec68b276ff6b38c1687eed310a79e2b902065bc798618c0cdee7b2009ad49623ed7ae0aa2b5219", + "0x9361e9f3aa859c07924c49f3d6e9b5d39a3df2fc1c10769202ec812955d7d3814c9e6982f4df3a8f3bdbfb4550cd1819", + "0xa8aa23f177ddc1e7a7856da3eac559791d8b3f188c0b3ae7021bcb35dfb72b0f043c3699597a9188200408bc3daf6ab7", + "0xa5a502ff673f6dab7ae4591a7b550c04ede22a45a960c6b5499644f721c62b12b9e08248e7f8b8a59a740b058d2a67e6", + "0xad374f80f0b52bc5a9491f79a547ce5e4a3ce4468a35d7dbca8a64083af35ab38eff9aa774ccba2e2e1e006e45cb0b85", + "0xab6851827125e3f869e2b7671a80e2dff3d2d01ce5bfbeb36cbaf30c3d974a2d36fd9f7c3d331bb96d24b33dbd21f307", + "0x96658f6a2d225a82f7ccee7f7a7e476967e31a0cd6c62859d3b13ee89702bb821547f70ffd31cb46a6a0d26a93158883", + "0x878f59ff2590bc3d44fdc674717589800748b78d543d3c0dbb50125b1d6011d6a083f10ab396e36b79f2d89b7cf51cdd", + "0xb8bdfb97829c5d973a15172bfe4cb39620af148d496900969bd7ca35de9b0e98eec87af4e20bef1022e5fb6c73952aa0", + "0xa292a78b452743998aee099f5a0b075e88762222da7a10398761030ffcc01128138d0f32fccf3296fcbea4f07b398b5f", + "0x85da44fdd7b852a766f66ba8804ed53e1fc54d282f9a6410106c45626df5a4380cbea2b76677fdfde32446a4d313742a", + "0x84bebf036073d121e11abc6180cba440465c6eaadc9a0c0853a5f1418f534d21cccf0cfc62533eaeae4653c7b4988046", + "0x923dec006a6af04ef675f5351afffffd2c62a17a98f4144221927c69f4553dd105e4fcc2227b5f493653d758cd7d0352", + "0xa51eda64f4a4410a1cfa080d1f8598e23b59856436eb20a241e11106989fbbb48f14c2251f608cbf9531c7c442b30bf7", + "0xac6d26ae7bab22d49b7fba7fe4b8cf6d70617977008c8290787c9da1a4759c17c5e441efb3dee706d5d64d9d2ace1de5", + "0xab5138b94d23c1bf920b2fb54039e8a3c41960a0fe6173261a5503da11ff7b3afdb43204f84a99e99888618a017aac1b", + "0x8c85647a91e652190eee4e98a1eec13a09a33f6532926427bf09e038f487e483f7930fbe6ff7a2126ccde989690dc668", + "0xa6026ab87cffec3e47b4c9673957d670cb48c9b968d2ad0e3d624d81c1082dcebbc70d0815cbd0325e0a900d703a6909", + "0xac4f6ff6baf8374a3c62bdd5a8d207d184ff993f6055bcee1e6dcc54173d756c37c24570d6462395add6f7871d60b1ae", + "0xa0dd6bc93930d0016557588f2598b7462ca48cbed637c8190be0fb4811e4576217ca9fc3c669c2a4db82e3f8bb24acaf", + "0xa67c1d79f7e7193a23e42928a5cc6a6e8e0c48b6b286607dbcfaaa0f10a7ba29ad62d1d57ca28c486794f0908bece29c", + "0x822f411bab4882202ed24e67c84e0c9a8da5b3389804ed9dfba0f672e3e1457ea76cad0cb935dbb3d7a39500fba5fe12", + "0x8a1198572323689300a9d7db2e2bcb7c519392e5d3d33e83cd64bcf1517a7dde52318a98203727b186597702c0eed258", + "0x8a84141b02f1d037c68d92567d71cda3a0b805d1e200b1d3fff3caf9902457cbfbaac33157b87ab0bb9e4fe3bac882c3", + "0x8070ace16d9eef8658fdcf21bed0d6938f948f31ca9d40b8bdb97fc20432cd2a7ef78eeefc991a87eae7f8c81adf9b19", + "0x9522e7123b733ce9ca58ab364509f308a1ead0915421ccede48071a983fd102e81e1634ffa07a9e03766f167f5c7cb5e", + "0x82cbdf97a755e952304f5a933fd4d74a3038009f242dac149595439130a815e9cc0065597c0b362130183a4c4a444173", + "0x81e904f9b65cd7049c75f64c7261e0cbb0cc15961ffcac063d09399d0d2b0553b19e7c233aca0f209f90cf50c7f5e0b2", + "0x8f5f6ea87429542ea04ad3eb5fc7eeb28fcf69c01c1a5d29b0de219524f6fba90c26069bfc9092379fe18cb46274393a", + "0xa4e5815481eb33b7990d2de1a3a591c1ab545b64fbeb4cff8c71b6bcb04d28940097899062bf43b27c5a8f899616703e", + "0xa7afe6066681e312882b3b181f462a1af2139d9bd2aefffae7976f3fc357bfd8fbd6ddd4e5e321412f107736e77f0cb6", + "0xb8ab102d7ff8d46b055095d8fb0ec2f658c9e18eee523c295b148b37f8342c120798113553b8bfebf2a11f27bc704cc4", + "0x862175ecc7e0e294c304a0352cd0f1d11b2603d326bb0e54e02b6cc8d04d01ac31c8864e9395aa1f3b90b76bc4397f5b", + "0xa4ea51ef3d82509f0e4eb6af705fa7530921cf9512cb5bf030571e69f4504a299297219a0a7e40db1b45165a5ea3a3f2", + "0xa6fb8b573e2ba6db0e8aba53a489e99bebe533c0fcd947dbfa732e00594f03f4e8609ccc44d8215986d38bc3d4e55d48", + "0x93fe8e0bdd5d66df2bd18be5963e864bddfcdcd3298590e7c3b11d99a070a4948fecef46453f19960bbfeada37979613", + "0xacbc45bc55c7080b45c69a3db80cbfc0267006dcf49c47330975aeff2a8ac07b206e1b1c3a515e50866ff510739b92c0", + "0x94a577df0983e4ee3d6b80c73d7e8e3bb78bd8390ff56fea350e51bdf5e0176b8494e7e81dc7b1d842ada961089cd1eb", + "0x81eb1fbe9e9c89f5818d0ef98e694da86e88625f0a37cfe88e6de69f90e58297e67f1d5c9d71263b523b63e42685975a", + "0xa81a2391ea4d0f65ab4325196559d67e2648b3f1e464509430b40d9948d5b0fc01c337d9b51048a93c4d62e6b73e1e8c", + "0x849a026e55ed77135138836c9df67883763e4602357d8566da2ee2505d135d44061de0c070cf333ffb9ac2e55a0894b2", + "0x8e272cc5734374c003c7b2e6ba833eb99b6be608da04e576df471c24705b6b2a790549c53e7971df2d9f0b88d0f570c6", + "0xb0f9e6d985064aa311d4a147f41007fdc576b7b9194aa4b8712bf59a76a71543fec2ee3db21bd3d30d4096f25babc543", + "0x96331837f0d74e2ba6cb1bfaddf4b1fb359bf46cb6c3c664938eb030e56bc85a5ce17bcd60b7fa7b72cb0ba1f3af0b5b", + "0xa0eaab6de4b5a551896e7d26153fb5df4bc22a37833ec864090b57b5115b0f8f1279e855cea456bb844802b294b0dbb7", + "0x955e87d3b966edff34f28137f871881c59bbbc6d69986b739867807680ca22b5e3272ced1d25854ed9700d87f133848b", + "0x9270a6db157a8ce78a1af6bfe2b5bbe7b621d56cc8f9940a03b5a5f600848b87b05d83595b2a3a315d4b7f4687c46085", + "0x9043328f2dd4dd85e14c91237a3478dc1eed239164924b53d1de9364d76c81315afa9639b58eedb1ab2122e2ae2e7cfb", + "0x857fe9f7d00b03bce367de7f789d755911a5f85d78044f18311ecd9b955e821b4a50228347260ba1205aef61219001fe", + "0xa0f878050367a7103fddf380908da66058ef4430eae1758335c46c24f5c22fefb0753991b3a47dba5c7eaafa4d598178", + "0xab5959296b1af14d2878816c7da9926484cbf8896b7eeac8a99dc255013319a67a0209025e1f8266ffd8cd7d960bdc87", + "0xabe53abc57ea46419dbe0ac1f39eee39a4feae265e58b50928eb0695e25938a16a8b00e65c1313837dc3367297e2c258", + "0x93e3e42ed6ba9c45d4e7a4bf21c1e469efafded1f3be9931a683dbb780db2494742fd76c9ad29fd7d12da2b778ede543", + "0xab3e64035c488a6e63496ddb2de9648cc63a670c5d4b610c187d8ceb144fcc50b016046f50b10e93b82937ebe932ac08", + "0xa3a8fa898f489b313d31838ad9f0c7ffe62ef7155de5da9ffe6ecd49a984fac3c6763e8cb64e675e1c4a0e45e7daf078", + "0x8356b26aa7c9fc9734b511480dad07b164cfec1324ad98eec9839a7943f2889d37c188d465515ad4e47c23df641c18c3", + "0x83c4476f829e0fe91da2353d5b58091e9335157941e89ca60ccab1d7fdd014bcf21bd55249805780ddc655c5c8c2536e", + "0x814f6e66505b2cb36de92c0de8004d6d094476522e66b9537787beff8f71a1381ed9f2b7d86778979ad016a7dae6cbac", + "0xb1cd7f6da4a625b82bea475442f65d1caa881b0f7ce0d37d4b12134d3f1beb3ad4c2f25f352811e618c446185486adb6", + "0xa71b918481b9bda667de0533292d81396853a3b7e2504edd63904400511f1a29891564d0091409f1de61276d2aebc12a", + "0xa2cd3d4104ec5fb6d75f5f34762d5e7d2ff0b261bea5f40a00deec08fbdab730721231a214e4df9b47685d5bacfe37c6", + "0x807f2d9de1399093bf284814bc4093f448f56a9bde0169407cdc0e7d2a34ff45052aef18bcb92f0ac7a0a5e54bd843e9", + "0xabeb03010c3ac38587be2547890a8476dd166ac7b2a92c50d442f031eaf273ad97114c38e57fe76d662c3e615334ac0b", + "0xb90a688da4b0bf65ff01bcf8699f0cba995b3397fcbe472e876ae1091a294463e4b94350ae8bd5c63b8441089e0884fd", + "0xad88db4afb177931788fb08eff187e15ad739edc7e1a14c8b777b6bf668aec69ca4749773f94250c1fdda3b59f705f7c", + "0x9886809f9ae952797c6527c6db297d2aa3d5209b360efe6a19970575a9f78aee3c21daadb8e8dfcbeeea5290238d16d9", + "0x930f486e95d7c053c9742e6f0b31e6d4fa2187e41229e46a074b469aafb87880aa8e972719b363049fc9fe2db8f03ce2", + "0x8d229af4fa08bd8aeb5fd9acfee47571eb03fcd2f19073b94cd27e2a6735029d31f123249d557f8d20c32ac881eae3aa", + "0x84576ed5aebe3a9c3449243a25247628993fdb2cc327072418ea2f1d11342756e56e9a82449bc3ea6e8eaecabc62e9b5", + "0xb775cb86cbec9c46a4a93d426379c62872c85dd08bccda39b21cb471222b85b93afd34a53337b6d258f4891c6458e502", + "0x8be1540e6b535b416b8d21e3ecf67dfb27a10fd4010f9f19426422edaeb0a4961d43ff3afd1db0994170056ce4d77aec", + "0xb9c7438e90a5501a4d05bbb8ab68d6db7e9baa8927231a5c58715ee2ab76ca1da0e94910a076958654869148d813d0e9", + "0xaa9bed1c4d2e7cbc2e1a884c8998773f7cc6fa9d6493c8abe8b425114a48305c3a43a1abda2292177ffd39ef02db4163", + "0x897b395356047cd86f576cfc050f7e4546ecd4df30b2c31ed8945797b81dd4ed9b9106cfbe6d7dd8bf91882e3cf1f42e", + "0x949a37e1037d9464b2ccd3ad23eda7089570d6b5ffa18025d2548a9df8829de8d62960f04a603f21eecbca5893d45284", + "0xb8a0642f68ff169ffbcd8cd684fae75d96f9bd76949472775bf155edc55a3d9c3e6f0299ee73a6cfb96289361fdbe9ee", + "0xa1273141510fcddd89b9b92c19a268dadd1528ad85744b8174684c9b56668e6b35dabb05f2b4cc6ef5611eaea6052f27", + "0x97c7415c82de83ecc066eb922268b8205ad7266c65b2b8f7e0aadac87f076c738cea72f9b0f069b8d28cf9d5438b8287", + "0xb32c7005380c848f71092a74297555dc6022369fc2a4f285e586ac8f53f6bd354fbe4b1f8a4cfb406a101103bf87bb64", + "0x91b48eeba52f02d04f536d32112038f8ba70bb34284fbb39e0f7bae2e08b3f45ad32e2f55d1beae94b949c15652d06a1", + "0x99e24f5ea378cb816a4436af2ee7891ac78a2e37c72590be0abd619244a190fee51fc701b6c1c073611b412cb76332c9", + "0x9465d1e73a1a0a5f7b1cd85f4fa4f5dee008b622b14d228d5cd5baeec174451e7ae93c5de688393d37cc24ce15df4139", + "0xa6ac3986ee01debdacb5ddc1e2550cb4f039156df15c7d5752b79f333175b840bdca89c4959a523e58cf97bbd6b2039e", + "0xb7f7a5cc1b1b6145988170d619c170c130231abbe0b5143a9bccaaebeef9ceb1c16e26749bc9dc5650fe91f92fa1b79b", + "0x854cb04f1557457383a401d79a655adfd0a4b706ea2bbc6262949c8d657efcfdc9c7960cbe1a50b5eebb361c5e378f80", + "0x8dd199dccbdc85aeca9ddcb5a78dd741a452f7a0d3ceb6546d76624bad2fce0e7e6c47ee30d60bf773f18d98503e7f9c", + "0x889e1ca9f0582be9bf5f1aede6a7312b30ea9bed45ab02d87182a013430f16007ae477ee6a823ae86c7fef7da016a0ec", + "0x892a60e63edfb3e7a6cf2d0be184413d214401fc1e6c004ca2902c3f1423728bf759a136e6e715d26d5bb229c75cc20a", + "0xa2287cd092261b39d22dcb1fa19512590b244771bb69fb62eda72f12be37d48e408b3e37a47608f68d743834edee7f15", + "0xb3b6afb950bbec0ff631bdf18af433e68adc63d02cb479704f24329ca6b6edd9a3d1d606563dbdce6038b676b85130b9", + "0x847da90f37b294509de51ab6521fdff12d5a1ec3cccaf730aa744da7e54b85fd9c70618787e87c0ba9947ce6c81387fb", + "0xad872153c00bccac75bdb30d1ab7044d814f4f8655ff26421d48fea04fb21d4dc82c1900620a57d13adc45c1062a1817", + "0x90fa5ee98fd7ec719f2a8543bbd0ff45ac69296c2416fc8666d05de3deea1017079a68aba55540a19585925803c8335d", + "0x962ba6d029e9176d0e8c80a21f2413f7322f22a9e9a32c933697a8b0e995ce25bea5264736a75718b3d330e215a58a05", + "0xa446f9530db30c5e9c1b3844d635e5c2cd311cc4537ff277fe83dd1a0382bcfa73beb07aaa0cf5a97d24c67e688086a4", + "0x8766b2053f16c72db387abe18b43d7b357a542916c9b8d530ee264e921c999494d6eb1e491352ecdf53758640c7a246d", + "0x83f32f511f7b0233662acfc14f30df345af99d2d6c777ce0b4bcdc4dd110533f30b45071df17230aaec392cc482355e1", + "0x82e3521bc9519b36f0cc020225586b263e4feb57b533b38d8e89ccf8d03f301d94da90efb4902002732fbf3876697f38", + "0xb5d1ea69c97ceaa34a720bb67af3fcf0c24293df37a5f6d06268b1eabe441531606954ac2598a1513f64231af722b3a3", + "0x956842696b411e6221c5064e6f16739e731497e074326ef9517b095671f52a19e792d93fe1b99b5a99a5dc29782a5deb", + "0xb19b5658e55c279eb4b0c19a0807865858cbec1255acd621f6d60c7e9c50e5d3ee57da76b133580899a97c09f1dd8dac", + "0x89e6a8b916d3fcc8607790e5da7e391f6bc9eae44cc7665eb326a230b02bc4eb4ef66e608ccc6031048fc682529833d0", + "0xb1a210bc8070ed68b79debd0ec8f24ec5241457b2d79fd651e5d12ceb7920e0136c3e0260bc75c7ff23a470da90d8de9", + "0x85b1954278e2c69007ad3ab9be663ad23ae37c8e7fa9bc8bd64143184d51aea913a25b954471b8badc9e49078146f5ac", + "0x98bf63c7a4b200f3ce6bf99e98543925bc02659dc76dfedebe91ec5c8877d1271973a6e75dad1d56c54d5844617313e1", + "0xb7404b6e0f320889e2a0a9c3c8238b918b5eb37bcdab6925c9c8865e22192ba9be2b7d408e1ea921a71af3f4d46806d0", + "0xb73cbbebf1d89801aa838475be27c15b901f27d1052072d8317dcae630ab2af0986e56e755431f1c93f96cd249f2c564", + "0x95b2027302f7f536e009f8a63018da6c91ec2b2733c07f526cc34cbcfa2f895ccfd3cc70be89f4e92c63c7ddc2a93370", + "0x9201d9ff5d0b1222bfa2345394f88ddf4fe9282acf51bee9b18b96bb724fdf8e736d7101acc2795a34e72f9e0545c9a8", + "0xacbff7eb160f427d8de6f29feeddfa8994674e033a0ccdc8e8c73f9243968f1a6379da670a7340f422892d50c97113c7", + "0x97ae8d03352c3729e1623e680dd9664f303b3bcfb844ef80d21e9c773a247967d27b86c9326af29db5eefd0bd3d4fac8", + "0x8e53ae5c22f5bfa5fe4c414dad6a10b28a3e5b82a22e24a94e50ce3b2bf41af31e7ba017d2968811c179017b78741ef0", + "0xb5ac7dd150247eb63dfb7dd28f64b1bf14426dc3c95c941e8e92750c206c4c7f4ad1a6b89e777cfe26ecb680dbf0acb6", + "0x99ae2e4652ea1c1c695e7ea2022fd35bd72b1a0d145c0b050da1be48ad781a413dc20fbda1b0b538881d4421e7609286", + "0xb8abe1fb3a7443f19cd8b687a45e68364842fc8c23d5af5ec85da41d73afb6840ef4b160d022b2dad1a75456d809e80b", + "0x842619c3547e44db805127c462f5964551f296a270ed2b922e271f9dc1074fdf1c5e45bb31686cec55cb816d77853c01", + "0x902dff769391de4e241a98c3ed759436e018e82b2c50b57147552bb94baddd1f66530915555e45404df9e7101b20e607", + "0x82e4f2ee7c7ca1ee8f38afa295d884e0629a509c909a5464eb9ea6b2d089205478120eed7b6049b077b2df685ec8ba48", + "0xaa21a68b0888e4a98b919002a7e71e6876b4eb42227858bf48c82daf664c3870df49e4d5f6363c05878a9a00a0bcf178", + "0xa8420cd71b1d8edd11ebc6a52ba7fc82da87dd0a1af386d5471b8b5362c4f42718338bcbc302d53794204a0a06b0671d", + "0x98c686bd3a994668fbbd80c472eed8aedd3ab5aa730c8d3ce72e63fb70742e58525437be1f260b7ecc6d9d18a43356a0", + "0xaca0b2df9ec8ede0b72f03b121cded5387d9f472b8c1f3a5f1badd5879fb2d5d0bbb6af1a2dd6bdebf758cfceadbe61d", + "0x93b1abd9cb41da1422d171b4dbf6fbcb5421189c48e85c9b8492d0597838f5845198494c13032e631c32456054598e1d", + "0xa246ab3a47f7dc5caedc26c6c2f0f3f303ed24188844ab67a3da1e793d64c7c7fe3e5cc46efafbd791b751e71de0614c", + "0xb9b52095ca98f1f07f3b0f568dd8462b4056c7350c449aa6ce10e5e8e313c2516ac4b303a4fc521fe51faf9bf7766ce9", + "0x8e2e9d26036e847c2a2e4ba25706a465ac9fbb27804a243e3f1da15dd4084f184e37808661ec929479d3c735555085ee", + "0x8b8c4f4ad5c8e57e6a7c55d70ef643083d4b8dac02716ea476d02dbbb16c702a2f2d5dd5efe3aec7704d2b8cdafe3959", + "0xa800afea30d0df333805d295bac25419b7049d70044be00c7c85a92a0503ca471001bc1e6552323f1a719eb96616fc20", + "0x868bced4560e1495b8527058ebc82a538b7cf806f8d8fe8eeed6981aba771de4d5e9f03cbfc7157d38b9f99cdea87b96", + "0x86b86258b0c1feb988cc79f6c4d4b458ff39428eda292f9608a5fc4c3765782c8c23c66f82d7538e78e092cd81d69a56", + "0x9370eac15de2555824c7d48520a678316a7bb672e66f8115ad7dbc7c7b1f35a7718e8fa0c35f37e3ef2df32dfa7ca8d1", + "0xae200bc5be0c1c8c6ec8e9fd28b4d256c6f806c0f270766099e191e256d67b9cceda2cc2fed46dfa2d410971a7408993", + "0xaf2428c77b2b9887ecde1ea835ed53c04891547fb79fe92e92f9c6009cdfffa0cb14de390532ad0ef81348b91798bd47", + "0xa9069eef0316a5d13d1aa4cef0cf9431518f99b916c8d734bd27b789828ae03e5870837163ea6ad0be67c69184b31e8d", + "0xb1b1ce6d529f5a8f80728173b2f873c8357f29644b00f619c15111224377ae31a2efb98f7e0c06f5f868030aab78ed52", + "0xb89c98beef19ee7f300e1c332a91569618ef8bf2c1d3de284fc393d45f036e2335d54917c762f7c2874a03fe4f0f6926", + "0x8264f993dceb202f8426339183157e9e0e026d4e935efe4cf957eb14cd53edcdc866305fb1334cdf0e819b69eafbaccf", + "0xaebd113f73210b11f5ac75b474f70a2005e5c349345003989175dffa19f168abd7f0e28125b18907502fff6fcc6f769b", + "0x9993ad061066ca6c2bb29fe258a645089184c5a5a2ef22c811352749a199be3a3af3a0d5ce963febf20b7d9e63508139", + "0x97952105000c6fc6c2dcae1ebdb2feae64f578d26a5523807d88e6caf1fe944b8185e49222d06a4553b3bdb48c3267a2", + "0x82dd955f208957d74693bed78d479c9663f7d911f68ff033929418eb4a5c5dc467589ca210c1ba3c2e37d18f04afe887", + "0xb816fc4763d4c8a1d64a549c4ef22918e045ea25fa394272c7e8a46dcb0c84d843d323a68cc3b2ef47a5bbb11b3913bc", + "0xa7a87ba4d12a60ee459aad306309b66b935d0c6115a5d62a8738482f89e4f80d533c7bba8503e0d53e9e11a7fd5fe72b", + "0x92b36d8fa2fdee71b7eea62a5cc739be518d0ecf5056f93e30b8169c3729a6a7ed3aa44c329aa1990809142e0e5e2b15", + "0x8835b6cf207b4499529a9034997d2d3bc2054e35937038deb9c3e2f729ebd97125f111c12816d30b716b397016133c52", + "0xacf14cd6d978ba905cf33b9839b386958b7a262b41cbd15e0d3a9d4ef191fcc598c5ab5681cf63bc722fe8acfda25ce6", + "0xb31302881969c5b283c6df90971f4fb2cc8b9a5da8073662da4029f7977fbb4aaa57dd95b003a9e509c817b739f964e7", + "0xb74669e1c3fa7f435e15b5e81f40de6cfb4ad252fcdfb29862724b0a540f373d6e26c3d600471c7421b60a1d43dbeb0f", + "0x861d01615cba6ca4e4ef86b8b90f37fa9a4cc65cef25d12370f7e3313b33bb75de0953c8e69972b3c2a54fe110f2a520", + "0xa58a56820efaf9572fd0f487542aaff37171d5db4a5d25bfb1a5c36ca975eb5df3cb3f427589e1101494abb96b5e4031", + "0xaf13d0a6869ef95cb8025367c0a12350800c6bc4ae5b5856dcb0a3ca495211d4139f30a8682d848cb7c05c14ae9f48cb", + "0x8c385767d49ba85b25a3a00026dd6a3052e09cd28809d5a1374edf4f02dc1beed367055b0dee09102c85985492b90333", + "0xb5129fc2fec76711449f0fcb057f9cf65add01b254900c425e89b593b8d395fc53bb0a83ddbd3166acc6d2c17f7fc2a4", + "0x86bd01b3417d192341518ad4abf1b59190d9c1829041e6f621068bce0bef77ec3b86875b7803cf84ff93c053c2e9aad1", + "0xa74fc276f6af05348b5fabccb03179540858e55594eb8d42417788438c574784919fb6297460f698bd0da31ce84cebfc", + "0x967ed3ec9f1fc51f76f07b956e1568d597f59840ef899472a3138f8af4b4c90861e23690c56b7db536f4dd477f23add6", + "0xb9e678206de4fc1437c62d63814d65f3496be25a7a452e53d719981d09c7e3cae75e6475f00474e7c8a589e2e0c6bfa3", + "0xb028eaffaa4ff2b1b508886ff13c522d0b6881998e60e06b83abe2ac1b69f036eece3ded0f95e9ae721aea02efff17b6", + "0x935f82de9be578c12de99707af6905c04c30a993a70e20c7e9dd2088c05660e361942fa3099db14f55a73097bfd32a44", + "0x96a1cc133997d6420a45555611af8bcd09a4c7dbddf11dbe65aab7688cc5a397485596c21d67d1c60aae9d840f2d8e48", + "0x80d117b25aa1a78e5d92ea50e8f1e932d632d8b37bebf444dcc76cc409322fb8eface74a5dddab101e793ff0a31f0a53", + "0x893229136d5ab555dc3217fb4e8c6d785b5e97a306cdaa62f98c95bad7b5558ed43e9a62a87af39630a1563abd56ec54", + "0xb7ec1973ec60bd61d34201a7f8f7d89d2bc468c8edc772a0ba4b886785f4dadc979e23d37b9f7ef3ff7d2101d3aa8947", + "0xb6080ca201d99205a90953b50fc0d1bd5efd5eadbfe5014db2aeb2e1874d645ab152fb4b0ff836f691b013b98ce7c010", + "0xb546e66ec0c39037bbaa66b2b3f4704a6a72cf1924a561550564b6fcf41fbc2930e708cd5cac1d05e12a4b8ec93ff7eb", + "0x8abeed90a01477260f4b09fff8fa00e93afe727e8eed6f111d225c872a67e6ab61d0472ab6add3fe987744e16f7c5268", + "0x8e02342d5cc1836ed21834b9ef81686172cc730f0412479db5f590b0ff7a729a0e986ffed16d6ecafd6b83d65922ca5e", + "0xb05660605cf8e8a10c8d3c77cccbe4e7179fa27cc829571f6b722a58e65e4e44d7fe977446118e9da2d2f40af146cc2d", + "0x942a00e006baba6d025cbd99297bdb0cbf3d84cddf849b1b5a9fe9ef1745352fad81313cce5d7622d6652096a8fa065c", + "0xaace8212b3d8dbe44ac97460a5938a3b803aca9bd00d8a643a859351daf391b22d1fd2a6b3e0ff83cc9ee272a1ad7686", + "0x965a9885a5259197a75a19707a2f040e0fd62505e00e35ebe5041d8467596752aedf0b7ec12111689eceb3e2e01ecfc8", + "0x81d58270a4e7ee0137cb2bf559c78c4fd5b3a613468a8157b6a9c5c0b6ca20a071b87c127d59cecc3d0359237a66d890", + "0xaf92b6354fbf35674abf005cb109edc5d95845e3d84b968e6001c4b83d548715dffc6723ac754c45a5ace8cd7dd30a24", + "0xb112caa707f9be48fdde27f1649149d9456857f928ea73e05b64bb62d597801daac0b89165fea76074f8b5770043f673", + "0xb6e7380746da358fc429f676b3d800341e7ab3f9072c271310626ae7f67b62562ff76c63bc9f5a1dbc0e0af87752408a", + "0xa45e9e8d0931207ebc75199aa0c983134aa97f771ff546a94a3367bcedf14486f761e7f572cf112e8c412018995fdaf4", + "0x854381128de5bfb79c67b3820f3005555f3ee6f1200046ebbfaee4b61b3b80a9cebf059c363a76b601ff574b8dbf0e6b", + "0xaa1b828a8b015d7c879669d5b729709f20a2614be6af6ff43b9c09b031f725f15b30cde63521edda6cd4cf9e4ab4b840", + "0x8f28f6b62c744084eeddcb756eced786c33725f0f255e5999af32b81d6c6506a3f83b99a46c68fc822643339fe1b91c5", + "0xac584e76a74cafe4298ca4954c5189ccc0cc92840c42f557c40e65a173ea2a5cd4ae9d9f9b4211c9e3dfd6471fc03a1b", + "0xa413365df01db91e6a9933d52ab3e5ed22d7f36a5585ad6054e96753b832e363484fb388c82d808d1e4dfb77f836eab9", + "0x8a68c51006d45bf1454a6c48a2923a6dbeb04bd78b720bb6921a3ca64c007043937498557f0a157262aac906f84f9bf8", + "0xb93ff8b6c8c569cc90ee00cfe2fc3c23cccea2d69cbca98a4007554878311635cb3b6582f91636006c47b97e989fe53d", + "0xb9a8a44d54592511d74c92f6a64d4a8c539a1d8949916ef3773e544f6f72c19a79577de9878433bd35bb5f14d92f411d", + "0x94f066a7e49ae88d497893e4ce6d34edc2dc0b42fe03934da5d4ed264d1620d506fcc0661faa90a6cf5083e1720beaaf", + "0xb42b102adef8f42c1059b5ca90fe3524dcd633cf49893b04b4a97a1b932ca4c7f305cebd89f466d5c79e246bad9c5ced", + "0x86b560d78d3c5fb24a81317c32912b92f6ea644e9bedfdea224a2f0e069f87d59e6680b36c18b3b955c43c52f0a9d040", + "0xa3829fa7e017c934fa999779c50618c6fb5eafb5e6dec0183f7254708a275c94ba6d2226c5ca0c0c357b2f2b053eea93", + "0x9337dda730076da88798fd50faed1efa062f7936a8879ea4658c41d4fcf18cee7120366100d574536e71f2f11271b574", + "0x853d09a30f4342f5a84c4758e4f55517a9c878b9b3f8f19e1362be9ae85ca0d79c2d4a1c0c14f5eff86010ad21476a7a", + "0xb0bc74cb69bdd8fdffca647979e693ad5cbf12a9f4ead139162fa3263bfebef3d085aab424ed8c6220b655228c63c6b1", + "0x88d8dc8faf3aab12ba7180550e6a047f00d63798775b038e4a43a3b40a421a3f5f152a7e09f28ccd7198bb8cefc40c07", + "0x88db2e3b8746415d0c3e9f5706eda69a29d0b9ee5135ad006060be7787f4f1f7069e2e2e693c5e10b7c3d5a949085ae0", + "0xb5bd830d2f1c722188dba2690d21b7b84b92cbdd873a55aaa966f1d08d217bfc8cffe8caea68868f3850b90b4ab68439", + "0xb5ad4be0c9626a33fce6c8501297bdde21b07b88531451912ed41971a4c48fdd1036d8a4994a99a7fbba4a5901a7095e", + "0xb0e1337a2a1772191faa91302f1e562e7cdc69ba5b25139e7728ce778a68a7fa9817f852ec8e04a159122cff62992ec6", + "0xb4fd4a4c1be8bc7e4e2bfd45404c35d65b75f45fb19ce55c213a8035b41f1ccbce9766f3df687c0d7cd6cdfc1abb00a5", + "0x814bf565ece6e9e2a094ffbd101f0b9fea7f315a2f4917abe2bf7d070ed8c64a2987bd288385a42fd336ed0a70a9d132", + "0xaf860af861dc80894ed69f29c8601d986917ec4add3d3f7c933a5e9d540bc8ff8e4e79d0bb01bbc08fa19ef062f2890c", + "0xb66d33fcf3cd28f15111960ffc6ed032c3b33d4bb53d035ab460cc5fa7ce78872f0476d0bb13f1d38f2672347d2d6c4d", + "0x89603ae1a5dd7c526936b86a3b69b7b1d0bdf79ba3cc9cc2e542ec801a6126d1514c075d6ad119fe6b6e95544ffe7fbe", + "0x8a1b097f46a62d85cff354d1e38df19a9619875aad055cc6313fdb17e2866d8f837a369a9ee56d4f57995e2b0a94310e", + "0x8dc165d86c7f80b0fcd4b6f90d96cd11dc62e61d4aae27594e661d5b08ef6c91156c749de8948adfaf3265b1d13e21cf", + "0x98e3173772c3b083b728040b8e0ee01dc717b74c48b79669dd9d2f7da207af64ccd7e9244bc21438a5d4ac79b88e9822", + "0x924d168099b6952d6fe615355851f2b474f6edfcd6a4bd3ad2972e6e45c31bf0a7fb6f7fca5879a0de3ea99830cfb5bc", + "0x95452f0b7efda93c9e7a99348e13f356bad4350f60fcd246a8f2aa5f595a9505d05ec9f88b1fe01b90ecd781027b9856", + "0xb95e8af516bb0941fc0767ecd651ada2bc64cc3e5c67a1f70048c634260c0f2c0e55ed22948e1870c54590b36683a977", + "0x82f7feb71e746d5ca24455e3f3e57e4eade92669ab043e877b836612efd3de82009f0555e5d8811bff9f2b75fc57a01d", + "0x87623c02caf590ea84cf4a84d1be501f89262e26eb463f2f94a2d3042889c051b058823c3367a989498e46ff25edab16", + "0xb88da847b1ef74c66f923773ce8c920ca89751335fde17b3a98c0603862069a2afbf35b1552b43ad64dccea69f040ff8", + "0x96b734758c823e5ce5b44625c252957e16fa09f87f869baac195956052dc92f933f377b288c7f63b8028751cbbdca609", + "0xa23cc5fbbe5cb7c1d33d433cec4e502f6548412e2374e285d307f75e98280b0c0af4f46bba18015be88cdf7db8b1239c", + "0x8bd5bbe04bc929ca8f546e673803ec79602f66ec24298d3e3b6bf6f2c25180fc0032ea6f86c38a6e0ec20ff4eaafc7a1", + "0xb95768ca113e5d57ad887a1cb5ef84ce89007ce34c3156cd80b9aa891f3ebaa52b74c0cb42919cfbcf0cb8bafa8085f9", + "0xa117f99045f65e88acc5a14fc944f8363f466e4a64057eb8fc64569da5dd022a01f2860c8e21b16aff98aebdf89461b7", + "0x895cda6503907c98c43477eaf71dfd26759032523691659f13662ca3a967d93bbc5be342d168223cef7e8a333987d6a0", + "0xa084d77d913d3ec0586ad5df2647610c7ed1f592e06a4993a5914f41994a29c4a8492d9dce2e14d8130c872d20722920", + "0x84a328b73c64137bb97a0a289b56b12060fa186ce178f46fe96648402f1b6a97d1c6c7b75321e4b546046c726add5a08", + "0xb7c35087b2c95127ce1470d97bceb8d873a7ad11a8034cc1cba7b60d56f7e882fc06796048435a9586eab25880787804", + "0xab05e3394375ee617c39c25c0ec76e8a7f2381954650c94fbcd11063ea6772c1823c693d2d9dd18bd540a130d7b92855", + "0x82ba5907051d84b37fd9d28f8b9abebc41fc4aaa334570516ca2e848846644016356d40fa9314543017d4f710d193901", + "0x9170517b6e23ee2b87ff7c930cb02b3e6bd8e2ae446107b5b19e269bf88f08de5ded3d81a2ff71b632ca8b8f933253a0", + "0x93dc0e3f6234b756cdbb3fe473b9214e970972e6bf70803f4e2bf25b195b60075177a1a16382f1dee612a4758aa076ee", + "0xb4b49fac49cdfccda33db991994a8e26ab97366545166cc7140aef3d965529f96a5dac14d038191af4fb9beb020ff6d5", + "0xb826537670acdf7a8a45ef4a422d5ae5a1b5416ad0b938307518d103cc7ba78e495ea200adc5941414a70158a366e8a2", + "0x8ae3588b1fbecbc769c761f0390d888e34773cf521d976ee335f6c813bf06dad38850871ac8a8e16528684f1e093d0c1", + "0xad9c00b8dccdb545315fbf26849135699c6aa3735f89581244281154c906aba80d20c1e7f18f41acc61e0565f8015a33", + "0x954ce68146c05fc1c9e536add3d4f702335d93c1650b8c1fad893722a81f915eee2d38275dad00ce87f3f5bc90ef7341", + "0x8243feaeff9a12f5aeb782e3dd68609ce04ecde897c90fd8a19c9c5dace3cf43bd5bc0f1624bf7fd2607ca0d71adbba8", + "0xa8a1be55259cd27898d9d60a61998d8da2bf2d439ba6eedb61d6d16dacc4a81ec706b9196dfa080ba20701d2cd9fa1f4", + "0xb0eac6212c7a62ef6062c30875fbe24b8e1a9d88854c035686f849a9eed4d17fbc9af27429eb7c3fd60b47a5e29f6783", + "0x878561a88412e95f19f1cb8894be9d0ea4a2cdd44f343387f87dd37445e5777bceb643cebc68c910acb5e588c509cd2e", + "0xa57b6c347955d8b0057a87494223148ff9ff12b88e79dbd9d0aae352fe55e15ea57fcfb9add3d5d269ee0001d8660f20", + "0xa07fa66340d4082585e4d72c77510c59b272e7a3345f4b1de6be7ff4a11ea95d712d035a7355fc8d2e571fa65fe8236f", + "0xb9d84a627462438e8ede6c453e3367bfaf81cff199d3e5157ef2bc582d358b28b5ccc3bc27bb73af98ef45179ea79caf", + "0xb14f26ea7ca558761cb19508e5940fbf5dcf2ad8555c5a03e8ff92481994072f523b1ab6b7176f698e2cfd83d4f8caad", + "0x800cca1cbb14e1fc230c7b420ff06864a934b082321bbf5b71f37340383923f23183d4fdc8fa2913928722b8892db28e", + "0x94790c950b92e971ec39e9396c3f32dee32a8275d78e6ea28a47130651bddc86a189ef404c5e8c210bd291186dee0df4", + "0xad7b3b3e377df64023b8726d43a7b6ec81e5a5e8c0943c5bebe5ab5ddd6597255f434a205c14ba90e9e5e3c462a1fe0c", + "0x86ff8156cc857a416e735009cf656b89da59b766b4c4e5a0c0165282b530c10657cc28cf5cb847696725c37ac48b69d7", + "0x89cb64cf9294f68f01533660a2af2aec0ec34cc0b4a0cc36a128f2e0efb3da244981f69aede962f50590faeeb9a5da01", + "0xa2ea5a94a524bb8e6f767017246cd1af9d87c9abb9894e91c4e90c34c5161be6179b49dafcab9cff877a522c76beb145", + "0xb5d9abf29ed6030a1e0f9dc19be416c45ba8cb5ed21aff5492233e114035715d77405d574cd62f2716285e49f79b9c99", + "0xac441cf6104473420babdfb74c76459cbea901f56938723de7ad3c2d3fadb0c47f19c8d9cb15a3ff374e01480b78a813", + "0xabea34bd2d36c5c15f6f1cdd906eb887f0dd89726279925dbe20546609178afd7c37676c1db9687bc7c7ea794516af03", + "0x8140abfd0ec5ca60ef21ad1f9aabbb41c4198bac0198cb4d220e8d26864eedb77af438349a89ca4c3ff0f732709d41a9", + "0xa5a25abf69f3acd7745facb275d85df23e0f1f4104e7a3d2d533c0b98af80477a26ac3cf5a73117db8954d08f9c67222", + "0xb45ac8d221a7e726ad2233ba66f46e83ed7d84dbe68182a00a0cf10020b6d4872f3707d90a6da85f6440c093914c4efa", + "0x80f586dfd0ceaa8844441c3337195ba5392c1c655403a1d6375f441e89d86ce678b207be5698c120166999576611b157", + "0xb8ce52089e687d77408d69f2d1e4f160a640778466489d93b0ec4281db68564b544ec1228b5ab03e518a12a365915e49", + "0x8990f80bae5f61542cc07cb625d988800954aa6d3b2af1997415f35bd12d3602071503b9483c27db4197f0f1f84a97ac", + "0x8329858a37285249d37225b44b68e4e70efeef45f889d2d62de4e60bd89dde32e98e40e2422f7908e244f5bd4ffc9fe2", + "0x8d70c66ea780c68735283ed8832dc10b99d3daeb18329c8a44a99611a3f49542e215bf4066ff4232d36ad72f1a17ccc3", + "0xa3b2676cc8cdf4cc9e38c6cb8482c088e5e422163357da3b7586a3768030f851ad2a138eeb31584845be9ffb8067fc00", + "0x95b1fa74e9f429c26d84a8e3c500c943c585ad8df3ce3aea1f6ab3d6c5d0ed8bb8fa5c2e50dd395fa8d4d40e30f26947", + "0xb1185f2ac7ada67b63a06d2aa42c4970ca8ef4233d4f87c8ffa14a712a211b1ffde0752916bfafdfa739be30e39af15d", + "0x8705a8f86db7c4ecd3fd8cc42dd8c9844eab06b27d66809dc1e893ece07186c57b615eab957a623a7cf3283ddc880107", + "0xaf6356b372f0280658744c355051f38ff086f5563491fc1b3b1c22cfec41d5c42b47762baeb9ee6c2d9be59efd21d2b7", + "0x86bdd4527b6fe79872740d399bc2ebf6c92c423f629cdfcd5ece58e8ed86e797378a2485ead87cbb5e2f91ba7b3fbda1", + "0xa900f0be1785b7f1fda90b8aedd17172d389c55907f01c2dfb9da07c4dc4743cb385e94f1b0fc907dd0fedb6c52e0979", + "0xa9f59f79829a9e3d9a591e4408eaec68782c30bc148d16eb6ae2efccb0e5478830bbdaa4ae6eac1f1088e7de2a60f542", + "0x99cf54a69ad5e8c8ec2c67880900e0202bcc90c9815531d66de8866c0a06489ea750745cc3e3aa1c4d5cb55dcd1e88f7", + "0x8676246a4710d6d73066f23078e09b3fa19411af067258e0b8790456525c02081727b585d6f428c8be285da4aa775a4b", + "0xb596c7014fe9214529c8e6b7602f501f796b545b8c70dbf3d47acc88e2f5afd65dccef2ef01010df31f03653566b16df", + "0xa12205c6c1780fc8aebdd98611e12180005b57750d40210b9eff0396d06023bd4ff7e45f36777123ff8bed7c5f52e7a3", + "0xae7dbd435bba81685d5eab9abc806e620253da83e56b4170952852d442648a5d8743f494a4b0fc9d606574f87895b0d6", + "0x9786257b1726b7cdc85219ca9eec415f98f5a11e78027c67c7b38f36f29fe7a56443570fdfedc1d9293a50e4c89d89f6", + "0xaaf0515070d1ca92aacdf5fac84193d98473d8eb2592381f391b8599bcd7503dbf23055324399d84f75b4278a601c8b2", + "0xb31654dbf62fbbe24db4055f750f43b47f199a2f03c4d5b7155645276b2e456a218ca133743fb29d6f1a711977323f6e", + "0x8f4d39106ecdca55c1122346bdaaac7f3589d0cf0897a6b4b69e14b4d60550fd017876399401ce7c5d35f27da95f50be", + "0x8a7bfdb48cd47afe94aff705fac65f260b3a3359223cff159b4135565c04b544dd889f6c9a6686f417e6081ad01e0685", + "0x967ba91111e5e08f9befcbaad031c4fb193776320989f8ede4018254be0e94586254432d3dbae1455014f3a2f2549d01", + "0xa9db52352feeb76715a35c8bed49fb3a8774c9c8e58838febf800285fd6c4938ec162eb8457029e6984d8397dc79ea19", + "0x811794e6bfe2539e8f6d5397c6058876e9e30763ad20dad942bb5dbcab2f16d51718ce52bfb4de17889ba91da1b85bcd", + "0xa6db0f65a6dc8b8cc2312a3e0146d8daf520255bb12f74874c05693914e64e92be0cd53d479c72cb2591e7725dfaf8b0", + "0x918d21bfa06d166e9eb5b7875c600663a0f19cc88c8e14412319d7aa982e3365f2dff79c09c915fc45013f6b3a21200d", + "0x9894852b7d5d7f8d335dd5f0f3d455b98f1525ad896fdd54c020eeaf52824cc0277ecbfa242001070dc83368e219b76d", + "0xad00acc47080c31fcc17566b29b9f1f19ccaae9e85a312a8dcc0340965c4db17e6c8bd085b327eaf867f72966bf61452", + "0x965e74649e35696744ecc8bed1589700bae9ca83978966f602cf4d9518074a9aa7c29bc81d36e868a0161293f5a96e95", + "0x961e29a239c2e0e0999b834e430b8edfe481eb024cc54ffaffd14edaf4b8522e6350dc32039465badfff90dcb2ba31cc", + "0x943dda8fa8237418a07e311efde8353c56dd8ec0bfa04889ccdd7faa3dee527e316fdc60d433a3b75a3e36ca2aa9d441", + "0xa0ed4c102e3f1d6ebf52e85a2bc863c1af2f55dc48eb94e40066f96964e4d37fff86db2cff55a8d43d517e47d49b5bd7", + "0x9045770ad4e81345bc6d9a10853ee131232bf5634ef4931b0e4ba56161585b4286876bc8a49b7b1f458d768718cb8ebf", + "0xb0dd430295ff28f81895fde7e96809630d1360009bbe555e3ac10962de217d93ead55a99fd4f84d8cadd1e8d86d7b7ef", + "0x95ced48419b870ea4d478a2c8db699b94292f03303f1bf4560b5b1e49ca9b47e7008514fe0a9cf785717f3824567e1b2", + "0xa7986e0e389e8aef6aac4a7a95e2440a9af877ae2bc5ad4c5f29d198ec66aa0db1d58c451e76ae70275a2e44c3d3fa68", + "0x85a8490faf32d15de12d6794c47cc48e02428af1e32205e0742f8299ea96b64bcd6d3b4655272afa595eec74ecbb047c", + "0xb790d7fb1307aacc2d303d9b6753a9773252b66c6b67763cf8841c690cbccc4866ffb5fec1c068b97601a7953fe0f7e8", + "0xafcc4011f8c53f10d63c29b74d9779cd75c861e01974c28a4ec2cbb909b67a1b2287ead175231343c936ad75dfa416ff", + "0x918058bffdecc1ae8779dccf1d874bb9e28edbe34c8b5954a8da64a848858d2f0776437b423baf4e731f3f5fa05a2841", + "0xab554db549aa36dfa9f966a5ed6be8267e3aa9ced348695f3dafc96333c6dbb48ef031693aafd59d1b746ecd11a89c51", + "0xac4ecf746b46b26a7af49cc9cc1d381e1e49b538dbd7fb773ce6b1df63ae31c916693cca8a90fb89f1e7ec5e0e8dd467", + "0xa8de66d48f16b016f780a25ba25bd6338fd8895a1909aabcfb6e70f04ff66f9866e6e2a339bcbfa4bfba4070a6a8db26", + "0xb4b49374eff6dac622e49b0e9c0e334ecbec513a96297f6369696ad39e5ec0de81a1417f6544be866c9f60957a9ba09a", + "0xb8023968549ebab6c1e7a8e82954a5b213bec50bbf35b36697a8d4fd75f9e12d510b365962aace4c9978c5b04da974a7", + "0x8d4bc016026dd19e4059d1c5784897cefa47f7ae2ed6dfa2b3c14a852fff2b64abc09549d106584e0daed861a2d6d6c2", + "0x85e26f433d0b657a53da4c1353485e0c2efa092484c5b8adb3f63dc72ee00be79197ebef7937b37a6a006571641cd6af", + "0xabb37a917301e68328032ff4715abc0fee32e5f5be68232ca8bf7ffb8732bc47504e75b40bcc0a7c7720b71496fa80af", + "0x9837c8d2660522c0357f5222777559d40321a1377f89ca1717215195bad4a348a14764bd87fa75f08e1f6263e9d08982", + "0x97e06f971b4c56408ed5f1de621d233e6a91c797f96ec912737be29352760a58831aaf1f64e377c3ed9f2f4dc8ad1adb", + "0xa12d211304da7b91101513d57a557b2504069b4383db8ecb88aa91e9e66e46e8139dadc1270620c0982103bc89666215", + "0xaab74ba48991c728ba65213e8c769e6824c594a31a9b73804e53d0fda9429403ff3d9f6ea5ef60884585d46356c87390", + "0x92f19be2b7adf031f73611282ad33e462852f778c5e072f689dd0e9458fa6ebccfae02f2b2dc021802c9225035862468", + "0x953bb843c48d722604576cef297123755cef8daa648c30c3a678eada8718dfdb16e71cc3e042a51fedc80577235c2563", + "0x86f509e3c1b9ee9a3b95e6da8516b47feb8c8a83403984228f4903c7ee1ee4f03addcb8fe86283af1196a54b36b9470c", + "0x903d793a377e98e2562c49de33e3fbf84bf99211925e7002a4f688470db655884e1efe92782bf970ffa55d9c418ef3b5", + "0xa41b65681ed7f10987a7bfdf9e56b010d53683819d845d880fc21b2d525540605c5823e75c434f17b5a0d08a091c1564", + "0x971be802de51cfc0d10a96be7977c037873f19334ed4ed4904b7675aec8bfa1f8956cd0150b07064caf18229ffd1ccd9", + "0xb253ebe4f82cdbefbc3ef816d40c497fe426a9f0f0f170e783fa4a05ae6dabdfa8c448817a24e723a314b43e76a7c422", + "0x86f397c95025489929ce9230b1466b5c330ec7c58a3c7e3153d6d05bcb8348a13398908e192590b8812f5c5ff09c133a", + "0xa0713983a3dc9f10b3833687cd2575de2fc63c4ad8d2f54ff85c6db23dd308daefef1bd1e51eec26732f77c1f37ba793", + "0x8249a1d53ec92f311f4fa77e777800d777f3e9d4d452df740fc767fa7b0f36c8dce603d6e6e25f464c0399b8d0b93c30", + "0xa73d0a206a62922f07b928501940d415e5a95716ee23bf6625b01ff2cd303f777adfa373d70279ba8a30fbb4c99a6f1f", + "0xb1106b407ecf234e73b95ff58ac9fdf6709ad2e763b58f0aacc5d41790226d441b5d41405ac03a0641f577848a4f5e8e", + "0xb009963ccc7b2d42792f09ab7cb0e929503dd1438f33b953104b4de43274ca3ce051554d10d7b37041b6f47d7a2dab6f", + "0xb744512a1b3c7ef9180b095c6a0c5bc16086a50020cf20dc2216bbff24d91ca99b95cb73070444dafc3ab45c3598960d", + "0xa0209669ffeddc074d35cc6aa2dac53acac8e870f8a8a5118e734482245b70c3175f760652e792118fdddac028642259", + "0x8ddd3e0d313da17292fdcc1bbc6e9d81189bb1d768411c6fe99801975eddb48dbf76699dcf785cac20ab2d48e392c8fd", + "0x8392aa285b8b734aa7a6e0f5a1850b631ddf6315922e39314916e627e7078065d705ff63adbc85e281d214ec7567863e", + "0xb655a1fff4dba544a068bf944e9de35eaaa6c9a0672d193c23926776c82bebed8aa6c07c074b352882136b17abdab04b", + "0xaf5095f40d1e345b3d37bebee3eb48c5d7b0547f12c030d5bfe8c0285943e0a7a53a186f33f791decba6a416cba0c5c9", + "0x8223527f9eb3c8ff52708613cd2ee47e64c0da039cea3a0189b211dc25e9bfa3d5367a137f024abe94f98722e5c14b67", + "0xafdb106d279273edc1ee43b4eead697f73cb0d291388f7e3fc70f0dd06513e20cc88b32056567dcc9d05364cb9ca8c58", + "0x9319eac79ff22a2d538dcd451d69bca8aa8e639979b0d1b60d494809dbd184a60e92ad03b889037a1ac29a5547423070", + "0xb79191ce22dbd356044e1777b6373b2d9d55d02b2cc23167642bc26d5f29fd9e2fb67dce5bd5cf81a602c3243bedd55c", + "0x988e0da1e96188ffd7c5460ecdf2321f07bc539d61c74a3292c34cb8c56dbafbca23eb4471a61e8e64e9a771a49fd967", + "0xb0792b6cf4b10f8af89d3401c91c9833736616bb9fe1367b5f561c09d8911fb5a43b7a4fd808927b33ab06e82dd37a28", + "0x862f68ea55206023ca470dbd08b69f0f785fcbabb575a1306ff3453c98ffcad5fd6ead42e8a1f9edf14c6fd165ffd63a", + "0x815ff0898b1330ac70610180c0f909561877888ff10def749a1e65edf9f4f7cea710a757c85241dfb13d0031efb5e54b", + "0xaa6e6ce21776ea4507d452ccdaf43a161a63687aae1cb009d340c9200e5646e9c2de4104dfd66b8e55dfa6de6ee83e4a", + "0x8e8f3d3403e0256ecc254b9b1464edca199cad3f3348002d744721c345a1a3c7f257c3587d2229774cd395e26693d1ba", + "0x90483e28985e4a0f7a3cb4bc5e865b9d408b94cd2146c04aed00b48a7ab80a28deb05efec320817d63578d4f953bd137", + "0x84fb2a762ba29193b07f1dd84b3f69153cedb679b66ad04f8a4adf01c14f115163a107e6db23aaf0f0c9687824ded197", + "0xb4a23922bf4302cc9a6583f252a1afa026c87c132b9ae44cc1f75a972cb6ae473447c500827906f9b677617ddd6fb473", + "0x809bb9edbbe3a2769165f029f2a48b6e10e833eb55d8f9107c4a09ca71f0986dc28f3bf4ead9cab498086eb54c626bbf", + "0xa0459dbb08db4155d16301933ec03df77c4f835db2aa3f9697eeb2bb6fcd03337fab45fa43372a469fecc9a8be2e3119", + "0xa638eaace7f21854de49f4db6e4ea83d2983751645e0fb200c5e56561f599fd37dac70bdbd36566fdd10d4114fbb9c2f", + "0xa3a27bc2728390643a524521bf8ef3b6437cfba6febfd8bb54f2b6ecbafafb96196d3dea279ce782efd97b212f364ef5", + "0xb86693b3ea23ea6b2c4d52554f61ef39c0ef57e514ff6da80c6e54395df8376e2e96b9d50e4ec301c59e022c5c5610db", + "0xaf4d7cd678d79e67ae19789d43331dff99346cd18efff7bab68f6170c111598d32837372e3afe3e881fd1e984648483e", + "0xb8735a555ba7fe294e7adc471145276b6525de31cda8c75aae39182915129025fb572ed10c51392e93c114f3a71bd0be", + "0xb1dfb6dbda4e0faaa90fe0154f4ddaf68ee7da19b03daad1356a8550fca78a7354a58e00adeecb364e2fd475f8242c24", + "0x9044b73c1bd19cd8bb46d778214d047f5dd89b99b42466431b661279220af5c50c0cffecebd2b64c3d0847a9c7a8b1ec", + "0x891f0d162651a0aa0d68fb1cc39fa8d77fd9f41ff98b5d6c056c969c4bac05ba8c52cbfa7fbb6ef9adfe44543a6ec416", + "0x8920ae1d5ac05bf4be6aba843e9fc1bc5b109817381cdd9aa13df53cabea319a34ee122dcb32086d880b20900ff28239", + "0xabb14023142876cbc9301336dced18c7878daa830070b5515ff4ac87b7bf358aa7ff129ebbf6fb78e827570a4142661f", + "0xa74b15e178cf91cde56eab0332e62d5ff84c05fcc849b86f45f94d7978bf9c0fc72a04f24d092a9d795ca3d976467f46", + "0x806829621a908ca9b6433f04557a305814a95d91c13152dca221e4c56bfaa3473d8bb1bacd66e5095a53070f85954278", + "0xb09a3c185e93869aa266a0593456a5d70587712bca81983dbc9eebbb0bd4b9108a38ae1643020ecf60c39c55bb3ac062", + "0xb2bbe8f5361a3ecdb19598dd02e85a4c4c87e009f66fee980b4819a75d61f0a5c5e0bdc882830606cb89554ef1f90ead", + "0x825e16cb54fc2e378187aedae84a037e32903467ac022deb302cf4142da3eda3ead5b9f3e188d44f004824a3b5d94fbe", + "0x8b39d4a11d9b8ba885d36bcdb6446b41da12cfd66cb22705be05ab86936464716954360cc403f8a0fd3db6d8b301cb59", + "0xac19d453106c9121b856c4b327ddb3e3112b3af04793df13f02d760842b93d1b1fbdff5734edc38e53103a6e429a1d1f", + "0xb1cacbb965ec563f9e07d669ffc5e84d4149f1fb9fcfbc505788c073578c8f67956fb8f603e0b9a9d65e2d41803038ce", + "0xb7612d9e7dc930bff29191d1503feb2d6451b368b69fa8ecb06353c959967daccdc262a963f01c7fb95496f1bd50d92e", + "0x93f8fceb65ea9ef2052fa8113fb6720c94f0fed3432d89014ee5ad16260aeb428aadea0d1f1e002d2f670612ba565da3", + "0xb3eb9213752156ed1fced3bca151fd0c630554215c808b9a0938b55fed42b6b89f9b76bc698f3e37c3c348d2395dbed1", + "0xb46ab3553ef172ae40fc21c51d1d7eab8599a67f2f89a32a971aa52c2f031664e268b976dd2f7dc2195458fcf4bf3860", + "0x8fb66f2c67ca5b6fb371c7d04592385a15df0c343857ba8037fe2aa9f2a5d4abc1058323ff9652653261b1c7db0edc24", + "0xa7dfdbbf0b14e4af70fdb017875cdc36ad2108f90deb30bfca49301c92cbf821645a00ade1d1ee59a1a55a346675c904", + "0x856199cad25ec80ee0327869077f272e33d59bf2af66c972e4a5839ec3b2a689e16f7fd0a03a3138bec458fcff8edbea", + "0xa2842ac5a715c2f48394988c6f84a6644c567673806feaa575838e906138c1b25d699e1b6ffdfc9be850b15da34077e4", + "0x814b448ada88f769de33054c3c19f988226317797acacdbe55ed2485b52cd259ac5bcbee13f9de057eee33930a7fa0c0", + "0xb49de8dd90da916ed374ca42665464b6abe89ff4453168921f5a7e5ddd3dcfa69422782e389e586e531fd78a1f236a8b", + "0x851f9d942b4c8ffc020c02c7fbee0f65ef42b1ab210ab4668a3db6aa0f8ab9eedb16f6fd739a542cc7e3cc03172b565b", + "0xa5128c155b8062d7fa0117412f43a6fdc2de98fa5628e1f5fc1175de0fa49fc52d015ec0aff228f060628268359e299c", + "0xb0765849127cc4ce1a1668011556367d22ce46027aa3056f741c7869287abcaccf0da726a5781a03964a9ded1febf67d", + "0x984562c64f3338ffe82f840c6a98a3dc958113f7ed28ee085af6890bbc0cd025723543a126df86f379e9c4771bb69c17", + "0x8087fe60a9a22a4333f6fbe7d070b372c428d8c5df3804bb874b6035e5602c0693757fb30a9cd5a86684b5bca6737106", + "0xa15e195b5850f7d45674cdc3bd74f972768b46fe9473182498263edc401745a8716fc532df8fc8c1375e39e391019226", + "0x858ec10208c14a67c4156ea9c147f36d36c4fa0a232195b647e976ba82c8e16262b2b68d31e3b4702070c3dc701bccb5", + "0x84bf3fb83c003380ee1158e2d6b1dca75cd14c7b2a32aec89d901f0d79e1475aa0827cb07cba1784a6bb0d37f6ca5cd4", + "0x91e69f5392648e7f7c698059a0fc4b8478ab8af166d3842fb382ec5c396daa082ee3b2cb0192da3c9d90f6523c4c039d", + "0x8f7299f451c5e641d6fd961946b7a6ba4755685b2a40164e6276c25aefc66715b92492097a191813d39bb4405dc5da36", + "0xade2cf04ff6c94c1019bfa1e0e8f580696230fa6ee9695c4772e5a44501b2fffdd765ec7cc71ba14b83559ad62cc0fc5", + "0x85fc98ecf469d6f98c8b3e441680816f764de39001a249bc7162f990c5a5354683e849164d4fc9287ee516780cdcd436", + "0x928d118188120d038c37abdbe66c05adaa87f1cf9957dee2783b09fa91c4c43a7b0d0b2b6c5f4dea57e3ec8af230e84f", + "0x8025f71cf8d3085d6ea5104dddea8fa66cdb8527e40db01472469be021632daf22721f4acf1a8698a53439fe2f82596c", + "0x83266fffb12b3c795a6b551ac2aa7d9a29c183f861e78768c11286a04e22bd423bba05a68775bd77273e3ca316a4318e", + "0x95fd0c69c2d9df4e795c7ba71ed71a9d9f2878cd7e3a64be7b671d9611649fd41d29f8bdab642ba19cbd3db660d6a7e7", + "0x92a912cb4d5ef4b639876daf4289500c4ebdbd80aff07fd93dc3eea645f084f910e5c02c10492a37f16acaa7e646d073", + "0xb3d2622c987189a0873932aaea8b92ebb6e9e67ff46e91a96bf733c3b84175fffe950f8f4622cc4fa50f116321c5537f", + "0xa98f9a40054b31023a8f7549a44cae853b379bbfe673c815b8726e43ecd11a96db40b20369d712cbf72ffab064ecfac5", + "0xb4e9a38e371fc21f4b8a3d7ad173c9ffad0554530dc053365d9555ddb60f5c9063c72ff4c65d78b091af631a9e1ee430", + "0x875a31aee4ba19e09f8c2754fab0b5530ec283c7861a4858b239a12432f09ef155a35fedb0bc33eac2117c7e62f1c7ee", + "0x95edd0d1a6e94af718590756b5c5f5492f1c3441ecc7fa22f4e37f4ec256b9fffd2fda4c11fc1a7c220daee096eb1ff8", + "0xb35fdc435adc73e15c5aaf4e2eea795f9e590d3e3ee4066cafa9c489ee5917466c2a4c897a186b2d27b848c8a65fa8a8", + "0x94a5ce56f8d72ec4d0f480cb8f03e52b22f7d43f949a4b50d4a688a928ffd2c9074ecbab37733c0c30759204a54f9a6a", + "0x987562d78ef42228c56074193f80de1b5a9ed625dd7c4c7df3bf5096e7d7b08e2ee864bd12d2ea563e24fa20ad4d30ef", + "0x95a8218405038c991ace2f45980dbb1efa9e4ad0d8153486b0213a89e4d7e3cac6d607100660784627c74f90a8e55482", + "0xb6a29d566f5a924355b7f7912f55140e1b5f99f983c614b8a92814ce261f2750e8db178866651ea3b461fb8f92890b14", + "0xafdacc0a13da0446a92455f57a42b3ba27ba707f24171727aa974d05143fae219de9e2eb7c857235dd9c7568f43be5a8", + "0x862a7dc25f7cfa4a09aeca0ed2c9c5ee66189e119e226720b19344e231981504e37bca179aa7cad238ee3ab1386aa722", + "0xa336364e76635f188e544613a47a85978073f1686e4ee7a8987f54da91c4193540ac448b91d07d1fc5c7a8538b1f1688", + "0x8f1ddca9638decd8247c1ce49c1e6cf494d03d91c4f33e48a84452d12b6736e8bd18c157068dfeff3a90977af19e5b1e", + "0x96ae91b9aaf00e437c18ddfc1aef2113ee278153ba090aedeb3f48f1e66feb8897bb1ac7f5ffeffc3be29376dd51e498", + "0x8230b5bd9067efb6089e50213f1cc84da892e6faf0b79d5e4768c29303a80b1b754cb09d17a21933aba4c5f32070878a", + "0xa79dfe217faec7b4d3cf97d8363949efbc6f3d2c6bbc25df2c7bb8b7fd2521e6d3fa76672bfc06de6f426290d0b3cc45", + "0x8290bd36552609d6b3ac9ccb57ff8668fc8290548eecdcee9a231f1125298c20bd8e60f033214dfbd42cd3c8642c699b", + "0x8945db9e8ec437c5145add028d25936ee8823ceb300a959402d262232ae0cbd9a64c1f0a1be6aed15ff152202ea9a70c", + "0x949e232b48adeaf57bd38eacb035267d3e78333c6b4524cab86651a428a730baf9c27ff42cb172526d925de863132e82", + "0x98917e7a5073a9c93a526399bb74af71c76958a74619caccf47949f8fd25962810a19e399b4efcba0c550c371bea3676", + "0xb5b144e0707aefc853ea5570bd78dedc4e690cf29edc9413080f28335ac78022139bfe7f7d6986eb1f76872bb91e82ad", + "0x949945072a08de6fd5838e9d2c3dc3200d048b5d21183020240fa13e71a1a8d30e6bfee4e6895e91d87b92f1444d0589", + "0xb351a03c7c98506ee92d7fb9476065839baa8ed8ac1dc250f5a095c0d4c8abcfab62690d29d001f0862672da29721f16", + "0xa82d81c136bc5e418d1fba614cb40a11f39dc526e66a8b1d7609f42fea4c02b63196315014400084f31f62c24b177cbd", + "0x87d51c907fdcdf528d01291b28adfee1e5b6221c6da68fd92ab66126247cd8086a6bcffff0ea17e7b57b0ba8d01bb95d", + "0xa2a9a1a91dfd918f36c1bfeeca705ea8e926ee012f8c18d633e50ec6e50f68f3380ef2ee839e5a43cf80fbb75bfb5304", + "0x86f22616caed13c9e9cd5568175b6b0a6a463f9a15c301b8766feca593efa6e5ee4c7066e1cd61b407c0be12b3d8236a", + "0xb57e0a2c42790d2fd0207ef6476a433fca0cf213d70840c4af1ad45833f23fca082d21a484f78af447a19a0b068ea55c", + "0x8ae9bda5d85e6e3600dde26379b7270abd088678098506b72196ac8f9ce5b0173bc9c7ff245c95cbab5b5b967bcb043b", + "0x95c7d11f6c874f59ba632b63ce07a7a9d917a74d0b89cefa043f52aa1a7fe2e81c38dea0b20378264b5b4f64039932bc", + "0xac7dee7479f50722526ea1c9d4e2f1a4578d1b5cce2092a07722069c96bb4da295de1c4f16e21005276e3b3f1624ac5a", + "0x89b8aaa49bd18b09f78fc5a1f3dd85d69b5dfcff28fc6d5a92b1520bc54107b8b71bb71fd6e0bde10e0a5809c633e5d2", + "0x8982cb43fe4d3488c55e8c08b935e6c8d31bb57e4f2aeb76d6319470cce99ebf7dc2f116ac15b9d845ab1bc16aa6a583", + "0xa12c63f48e27b1a1c83a32992642f37fb5b89851a35e80f6d1f9bc483cb25acd0e12b1dcf68781ae0cc861f002368bcb", + "0xaa6da92a4b4fa229afc8007abca257ce0ff5fad3b1ccfe5d836b9b52ff6b72575a0b915a759403b993733b16a47fdb15", + "0x8bf706a92fe54f15d633b9463926b874dd43e28aaeca3fe2353fb58ad7753c8a293c56b0e94176070e8a9ec7401073a1", + "0xb81e86de4bb5c1046e40cca79585c5b98c8673626fd3a28e563c5a3296256c2f7086522ae95cbabfaa8f1a8f7eae6272", + "0xad10f895b05d35cb251f78cc042d3f0969a8b6b3f289ddb4b016e0b8e06bfffc3a3e1afa9b0cc548f8c092832bb766bc", + "0xad993aceb68d5217cfb07f862956cde83d05dec5060fc7a8fbfd37c6bfd5429ba69bdaf478b6cd01c323a06793dcd9fa", + "0x83da9c9a8fcb2775df0777aceabe90642a2df1c6abc646566e954f42d6e43455b00b101ec5ef58850c8d4b3100222ca1", + "0xb55484f339fe7c7d107e70432601f4a34e1cc02ae4de5d18b99e5aa995f7b3710fc745769b85c1af803d457491dd8ce3", + "0x8009d80593e82f3e751cec9e7e495fd29ad6f45f8d3ae513bec998b43c49fed74c44229c6f27c421e80c65413b897644", + "0x9868081bbcc71192f7ff8dcf99a91dcd40f96556fbd6f285bdbfdfc785f604d8bf75c368c59db5ac8cdcc663087db53a", + "0xa04b1e91af025b4387ee0a2d790a1afb842e46f4c3717e355578efd1f84fea78782c6f7944b4961268de7f1ac71fb92b", + "0xa7b6301ddb9738b89b28a36d29d5323264a78d93d369f57ddab4cea399c36018a1fcc2cc1bfadf956a775124ae2925bd", + "0xa6cdb469014b33c590a07a728ce48f15f17c027eb92055e1858a1f9805c8deb58491a471aaa765de86a6bda62a18aef4", + "0x828a23280ec67384a8846376378896037bd0cb5a6927ff9422fca266ee10a6fde5b95d963a4acfa92efbb0309cdb17b4", + "0xb498ec16bcdb50091647ae02d199d70c783d7c91348a1354661b1c42bc1266e5a5309b542ef5fdf5281d426819a671cb", + "0x806533fb603e78b63598ff390375eebe5b68380640f5e020e89a5430037db2e519ab8ae5d0d0ad3fa041921c098448e1", + "0x9104ad119681c54cdee19f0db92ebfe1da2fa6bef4177f5a383df84512d1b0af5cbe7baf6a93ad4b89138cd51c7c5838", + "0xac695cde30d021d9f4f295109890c4013f7e213d2150c9d5c85a36d4abfdca4cdc88faee9891e927a82fc204b988dcd9", + "0xa311c244df546d5dc76eccb91fe4c47055fc9d222d310b974d4c067923a29e7a7f6d5a88bfef72fd6d317471f80d5c82", + "0x89e4518335240479ad041a0915fc4f1afaab660bd4033c5d09c6707f0cc963eb2e6872cabc4a02169893943be7f847d4", + "0xa8ad395b784c83aacf133de50d6b23bd63b4f245bb9e180c11f568faca4c897f8dbda73335ef0f80a8cb548a0c3c48fc" + ] +} \ No newline at end of file diff --git a/go.mod b/go.mod index daa1f2ce7fd4..b8059340f03a 100644 --- a/go.mod +++ b/go.mod @@ -45,9 +45,11 @@ require ( require ( github.com/btcsuite/btcd/btcec/v2 v2.3.4 - github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f + github.com/consensys/gnark-crypto v0.10.0 + github.com/crate-crypto/go-kzg-4844 v0.2.0 github.com/deckarep/golang-set v1.8.0 github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 + github.com/ethereum/c-kzg-4844 v0.1.0 github.com/google/gofuzz v1.2.0 github.com/kylelemons/godebug v1.1.0 github.com/mattn/go-isatty v0.0.17 @@ -58,7 +60,9 @@ require ( require ( github.com/StackExchange/wmi v1.2.1 // indirect + github.com/bits-and-blooms/bitset v1.5.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/consensys/bavard v0.1.13 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/dlclark/regexp2 v1.10.0 // indirect @@ -68,12 +72,14 @@ require ( github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/naoina/go-stringutil v0.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect + github.com/supranational/blst v0.3.11-0.20230406105308-e9dfc5ee724b // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/term v0.26.0 // indirect @@ -81,4 +87,5 @@ require ( google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools v2.2.0+incompatible // indirect + rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/go.sum b/go.sum index 5ffc6217fb07..8ee3f689751e 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,3 @@ -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= @@ -7,6 +6,8 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98 h1:7buXGE+m4OPjyo8rUJgA8RmARNMq+m99JJLR+Z+ZWN0= github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98/go.mod h1:DLTg9Gp4FAXF5EpqYBQnUeBbRsNLY7b2HR94TE5XQtE= +github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8= +github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 h1:Eey/GGQ/E5Xp1P2Lyx1qj007hLZfbi0+CoVeJruGCtI= github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= @@ -16,11 +17,14 @@ github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= -github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f h1:C43yEtQ6NIf4ftFXD/V55gnGFgPbMQobd//YlnLjUJ8= -github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.10.0 h1:zRh22SR7o4K35SoNqouS9J/TKHTyU2QWaj5ldehyXtA= +github.com/consensys/gnark-crypto v0.10.0/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU= github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-kzg-4844 v0.2.0 h1:UVuHOE+5tIWrim4zf/Xaa43+MIsDCPyW76QhUpiMGj4= +github.com/crate-crypto/go-kzg-4844 v0.2.0/go.mod h1:SBP7ikXEgDnUPONgm33HtuDZEDtWa3L4QtN1ocJSEQ4= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -39,6 +43,8 @@ github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 h1:Y9vTBSsV4hSwPSj4bac github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/ethereum/c-kzg-4844 v0.1.0 h1:BR9kUo6zAaFphuoNj39NquE3Sl0sD/oT0+idKZ4mkiI= +github.com/ethereum/c-kzg-4844 v0.1.0/go.mod h1:WI2Nd82DMZAAZI1wV2neKGost9EKjvbpQR9OqE5Qqa8= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -71,6 +77,7 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -91,7 +98,6 @@ github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4d github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/karalabe/hid v1.0.0 h1:+/CIMNXhSU/zIJgnIvBD2nKHxS/bnRHhhs9xBryLpPo= github.com/karalabe/hid v1.0.0/go.mod h1:Vr51f8rUOLYrfrWDFlV12GGQgM5AT8sVh+2fY4MPeu8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -115,6 +121,9 @@ github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks= github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0= @@ -161,35 +170,27 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/supranational/blst v0.3.11-0.20230406105308-e9dfc5ee724b h1:u49mjRnygnB34h8OKbnNJFVUtWSKIKb1KukdV8bILUM= +github.com/supranational/blst v0.3.11-0.20230406105308-e9dfc5ee724b/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -206,16 +207,11 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -224,12 +220,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -263,4 +255,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= From ed03a99770607203131a0bc60f15ccf398666fe8 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 292/479] crypto/kzg4844: upgrade c-kzg-4844 to v0.2.0 (#27257) --- crypto/kzg4844/kzg4844.go | 2 +- crypto/kzg4844/kzg4844_ckzg_cgo.go | 6 +++--- go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/crypto/kzg4844/kzg4844.go b/crypto/kzg4844/kzg4844.go index d54376965fe2..3bd814159db3 100644 --- a/crypto/kzg4844/kzg4844.go +++ b/crypto/kzg4844/kzg4844.go @@ -55,7 +55,7 @@ func UseCKZG(use bool) error { // Initializing the library can take 2-4 seconds - and can potentially crash // on CKZG and non-ADX CPUs - so might as well so it now and don't wait until - // a crpyto operation is actually needed live. + // a crypto operation is actually needed live. if use { ckzgIniter.Do(ckzgInit) } else { diff --git a/crypto/kzg4844/kzg4844_ckzg_cgo.go b/crypto/kzg4844/kzg4844_ckzg_cgo.go index 9ba9e610b72a..8b18ae0d2e7e 100644 --- a/crypto/kzg4844/kzg4844_ckzg_cgo.go +++ b/crypto/kzg4844/kzg4844_ckzg_cgo.go @@ -23,9 +23,9 @@ import ( "errors" "sync" + "github.com/XinFinOrg/XDPoSChain/common/hexutil" gokzg4844 "github.com/crate-crypto/go-kzg-4844" ckzg4844 "github.com/ethereum/c-kzg-4844/bindings/go" - "github.com/XinFinOrg/XDPoSChain/common/hexutil" ) // ckzgAvailable signals whether the library was compiled into Geth. @@ -47,8 +47,8 @@ func ckzgInit() { if err = gokzg4844.CheckTrustedSetupIsWellFormed(params); err != nil { panic(err) } - g1s := make([]byte, len(params.SetupG1)*(len(params.SetupG1[0])-2)/2) - for i, g1 := range params.SetupG1 { + g1s := make([]byte, len(params.SetupG1Lagrange)*(len(params.SetupG1Lagrange[0])-2)/2) + for i, g1 := range params.SetupG1Lagrange { copy(g1s[i*(len(g1)-2)/2:], hexutil.MustDecode(g1)) } g2s := make([]byte, len(params.SetupG2)*(len(params.SetupG2[0])-2)/2) diff --git a/go.mod b/go.mod index b8059340f03a..345a65ca0edf 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( github.com/crate-crypto/go-kzg-4844 v0.2.0 github.com/deckarep/golang-set v1.8.0 github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 - github.com/ethereum/c-kzg-4844 v0.1.0 + github.com/ethereum/c-kzg-4844 v0.2.0 github.com/google/gofuzz v1.2.0 github.com/kylelemons/godebug v1.1.0 github.com/mattn/go-isatty v0.0.17 diff --git a/go.sum b/go.sum index 8ee3f689751e..7b122a317292 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,8 @@ github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 h1:Y9vTBSsV4hSwPSj4bac github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/ethereum/c-kzg-4844 v0.1.0 h1:BR9kUo6zAaFphuoNj39NquE3Sl0sD/oT0+idKZ4mkiI= -github.com/ethereum/c-kzg-4844 v0.1.0/go.mod h1:WI2Nd82DMZAAZI1wV2neKGost9EKjvbpQR9OqE5Qqa8= +github.com/ethereum/c-kzg-4844 v0.2.0 h1:+cUvymlnoDDQgMInp25Bo3OmLajmmY8mLJ/tLjqd77Q= +github.com/ethereum/c-kzg-4844 v0.2.0/go.mod h1:WI2Nd82DMZAAZI1wV2neKGost9EKjvbpQR9OqE5Qqa8= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= From bd93c59baed3a658f6ff29d1af60b8789a4bd9f5 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 293/479] crypto: replace noarg fmt.Errorf with errors.New (#27333) --- crypto/signature_cgo.go | 3 ++- crypto/signature_nocgo.go | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/crypto/signature_cgo.go b/crypto/signature_cgo.go index c33cd0094b33..681165750304 100644 --- a/crypto/signature_cgo.go +++ b/crypto/signature_cgo.go @@ -22,6 +22,7 @@ package crypto import ( "crypto/ecdsa" "crypto/elliptic" + "errors" "fmt" "github.com/XinFinOrg/XDPoSChain/common/math" @@ -72,7 +73,7 @@ func VerifySignature(pubkey, digestHash, signature []byte) bool { func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) { x, y := secp256k1.DecompressPubkey(pubkey) if x == nil { - return nil, fmt.Errorf("invalid public key") + return nil, errors.New("invalid public key") } return &ecdsa.PublicKey{X: x, Y: y, Curve: S256()}, nil } diff --git a/crypto/signature_nocgo.go b/crypto/signature_nocgo.go index 3e48e51e84eb..6d628d758d93 100644 --- a/crypto/signature_nocgo.go +++ b/crypto/signature_nocgo.go @@ -74,12 +74,12 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) { return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash)) } if prv.Curve != btcec.S256() { - return nil, fmt.Errorf("private key curve is not secp256k1") + return nil, errors.New("private key curve is not secp256k1") } // ecdsa.PrivateKey -> btcec.PrivateKey var priv btcec.PrivateKey if overflow := priv.Key.SetByteSlice(prv.D.Bytes()); overflow || priv.Key.IsZero() { - return nil, fmt.Errorf("invalid private key") + return nil, errors.New("invalid private key") } defer priv.Zero() sig, err := btc_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey From 17c048092eb9b3dc3cf34933b20da6d4bd3a6716 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 294/479] tests/fuzzers/bn256: add PairingCheck fuzzer (#27252) * tests/fuzzers/bn256: scale gnark result by constant * tests/fuzzers/bn256: scale gnark result by constant --- tests/fuzzers/bn256/bn256_fuzz.go | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/tests/fuzzers/bn256/bn256_fuzz.go b/tests/fuzzers/bn256/bn256_fuzz.go index 24b7b9e23892..7300c98c13f0 100644 --- a/tests/fuzzers/bn256/bn256_fuzz.go +++ b/tests/fuzzers/bn256/bn256_fuzz.go @@ -153,12 +153,29 @@ func FuzzPair(data []byte) int { if !bytes.Equal(clPair, gPair) { panic("pairing mismatch: cloudflare/google") } - cPair, err := bn254.Pair([]bn254.G1Affine{*ps}, []bn254.G2Affine{*ts}) if err != nil { panic(fmt.Sprintf("gnark/bn254 encountered error: %v", err)) } - if !bytes.Equal(clPair, cPair.Marshal()) { + + // gnark uses a different pairing algorithm which might produce + // different but also correct outputs, we need to scale the output by s + + u, _ := new(big.Int).SetString("0x44e992b44a6909f1", 0) + u_exp2 := new(big.Int).Exp(u, big.NewInt(2), nil) // u^2 + u_6_exp2 := new(big.Int).Mul(big.NewInt(6), u_exp2) // 6*u^2 + u_3 := new(big.Int).Mul(big.NewInt(3), u) // 3*u + inner := u_6_exp2.Add(u_6_exp2, u_3) // 6*u^2 + 3*u + inner.Add(inner, big.NewInt(1)) // 6*u^2 + 3*u + 1 + u_2 := new(big.Int).Mul(big.NewInt(2), u) // 2*u + s := u_2.Mul(u_2, inner) // 2*u(6*u^2 + 3*u + 1) + + gRes := new(bn254.GT) + if err := gRes.SetBytes(clPair); err != nil { + panic(err) + } + gRes = gRes.Exp(*gRes, s) + if !bytes.Equal(cPair.Marshal(), gRes.Marshal()) { panic("pairing mismatch: cloudflare/gnark") } From 541ddee235736dfaae69d415dea7d1abb59a9391 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:31 +0800 Subject: [PATCH 295/479] go.mod: update kzg libraries to use big-endian (#27510) --- go.mod | 5 ++--- go.sum | 10 ++++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 345a65ca0edf..7d33da0bc16c 100644 --- a/go.mod +++ b/go.mod @@ -46,11 +46,10 @@ require ( require ( github.com/btcsuite/btcd/btcec/v2 v2.3.4 github.com/consensys/gnark-crypto v0.10.0 - github.com/crate-crypto/go-kzg-4844 v0.2.0 + github.com/crate-crypto/go-kzg-4844 v0.3.0 github.com/deckarep/golang-set v1.8.0 github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 - github.com/ethereum/c-kzg-4844 v0.2.0 - github.com/google/gofuzz v1.2.0 + github.com/ethereum/c-kzg-4844 v0.3.0 github.com/kylelemons/godebug v1.1.0 github.com/mattn/go-isatty v0.0.17 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible diff --git a/go.sum b/go.sum index 7b122a317292..353d90448351 100644 --- a/go.sum +++ b/go.sum @@ -23,8 +23,8 @@ github.com/consensys/gnark-crypto v0.10.0 h1:zRh22SR7o4K35SoNqouS9J/TKHTyU2QWaj5 github.com/consensys/gnark-crypto v0.10.0/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU= github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/crate-crypto/go-kzg-4844 v0.2.0 h1:UVuHOE+5tIWrim4zf/Xaa43+MIsDCPyW76QhUpiMGj4= -github.com/crate-crypto/go-kzg-4844 v0.2.0/go.mod h1:SBP7ikXEgDnUPONgm33HtuDZEDtWa3L4QtN1ocJSEQ4= +github.com/crate-crypto/go-kzg-4844 v0.3.0 h1:UBlWE0CgyFqqzTI+IFyCzA7A3Zw4iip6uzRv5NIXG0A= +github.com/crate-crypto/go-kzg-4844 v0.3.0/go.mod h1:SBP7ikXEgDnUPONgm33HtuDZEDtWa3L4QtN1ocJSEQ4= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -43,8 +43,8 @@ github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 h1:Y9vTBSsV4hSwPSj4bac github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/ethereum/c-kzg-4844 v0.2.0 h1:+cUvymlnoDDQgMInp25Bo3OmLajmmY8mLJ/tLjqd77Q= -github.com/ethereum/c-kzg-4844 v0.2.0/go.mod h1:WI2Nd82DMZAAZI1wV2neKGost9EKjvbpQR9OqE5Qqa8= +github.com/ethereum/c-kzg-4844 v0.3.0 h1:3Y3hD6l5i0dEYsBL50C+Om644kve3pNqoAcvE26o9zI= +github.com/ethereum/c-kzg-4844 v0.3.0/go.mod h1:WI2Nd82DMZAAZI1wV2neKGost9EKjvbpQR9OqE5Qqa8= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -75,8 +75,6 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= From 247ebd6a6db5c53f5529d69642a3658ab29f5630 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:32 +0800 Subject: [PATCH 296/479] crypto/kzg4844: do lazy init in all ckzg funcs (#27679) * crypto/kzg4844: remove unnecessary init call & fix typo * Fix kzg4844 tests/benchmarks * Make init lazy & revert changes to tests --- crypto/kzg4844/kzg4844.go | 2 +- crypto/kzg4844/kzg4844_ckzg_cgo.go | 8 ++++++++ crypto/kzg4844/kzg4844_test.go | 12 ++++++++++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/crypto/kzg4844/kzg4844.go b/crypto/kzg4844/kzg4844.go index 3bd814159db3..5969d1c2cee1 100644 --- a/crypto/kzg4844/kzg4844.go +++ b/crypto/kzg4844/kzg4844.go @@ -54,7 +54,7 @@ func UseCKZG(use bool) error { useCKZG.Store(use) // Initializing the library can take 2-4 seconds - and can potentially crash - // on CKZG and non-ADX CPUs - so might as well so it now and don't wait until + // on CKZG and non-ADX CPUs - so might as well do it now and don't wait until // a crypto operation is actually needed live. if use { ckzgIniter.Do(ckzgInit) diff --git a/crypto/kzg4844/kzg4844_ckzg_cgo.go b/crypto/kzg4844/kzg4844_ckzg_cgo.go index 8b18ae0d2e7e..df211f94d45d 100644 --- a/crypto/kzg4844/kzg4844_ckzg_cgo.go +++ b/crypto/kzg4844/kzg4844_ckzg_cgo.go @@ -74,6 +74,8 @@ func ckzgBlobToCommitment(blob Blob) (Commitment, error) { // ckzgComputeProof computes the KZG proof at the given point for the polynomial // represented by the blob. func ckzgComputeProof(blob Blob, point Point) (Proof, Claim, error) { + ckzgIniter.Do(ckzgInit) + proof, claim, err := ckzg4844.ComputeKZGProof((ckzg4844.Blob)(blob), (ckzg4844.Bytes32)(point)) if err != nil { return Proof{}, Claim{}, err @@ -84,6 +86,8 @@ func ckzgComputeProof(blob Blob, point Point) (Proof, Claim, error) { // ckzgVerifyProof verifies the KZG proof that the polynomial represented by the blob // evaluated at the given point is the claimed value. func ckzgVerifyProof(commitment Commitment, point Point, claim Claim, proof Proof) error { + ckzgIniter.Do(ckzgInit) + valid, err := ckzg4844.VerifyKZGProof((ckzg4844.Bytes48)(commitment), (ckzg4844.Bytes32)(point), (ckzg4844.Bytes32)(claim), (ckzg4844.Bytes48)(proof)) if err != nil { return err @@ -99,6 +103,8 @@ func ckzgVerifyProof(commitment Commitment, point Point, claim Claim, proof Proo // // This method does not verify that the commitment is correct with respect to blob. func ckzgComputeBlobProof(blob Blob, commitment Commitment) (Proof, error) { + ckzgIniter.Do(ckzgInit) + proof, err := ckzg4844.ComputeBlobKZGProof((ckzg4844.Blob)(blob), (ckzg4844.Bytes48)(commitment)) if err != nil { return Proof{}, err @@ -108,6 +114,8 @@ func ckzgComputeBlobProof(blob Blob, commitment Commitment) (Proof, error) { // ckzgVerifyBlobProof verifies that the blob data corresponds to the provided commitment. func ckzgVerifyBlobProof(blob Blob, commitment Commitment, proof Proof) error { + ckzgIniter.Do(ckzgInit) + valid, err := ckzg4844.VerifyBlobKZGProof((ckzg4844.Blob)(blob), (ckzg4844.Bytes48)(commitment), (ckzg4844.Bytes48)(proof)) if err != nil { return err diff --git a/crypto/kzg4844/kzg4844_test.go b/crypto/kzg4844/kzg4844_test.go index 0d35667a9ce6..fae8a7a76eaf 100644 --- a/crypto/kzg4844/kzg4844_test.go +++ b/crypto/kzg4844/kzg4844_test.go @@ -47,7 +47,6 @@ func randBlob() Blob { func TestCKZGWithPoint(t *testing.T) { testKZGWithPoint(t, true) } func TestGoKZGWithPoint(t *testing.T) { testKZGWithPoint(t, false) } - func testKZGWithPoint(t *testing.T, ckzg bool) { if ckzg && !ckzgAvailable { t.Skip("CKZG unavailable in this test build") @@ -73,7 +72,6 @@ func testKZGWithPoint(t *testing.T, ckzg bool) { func TestCKZGWithBlob(t *testing.T) { testKZGWithBlob(t, true) } func TestGoKZGWithBlob(t *testing.T) { testKZGWithBlob(t, false) } - func testKZGWithBlob(t *testing.T, ckzg bool) { if ckzg && !ckzgAvailable { t.Skip("CKZG unavailable in this test build") @@ -106,6 +104,8 @@ func benchmarkBlobToCommitment(b *testing.B, ckzg bool) { useCKZG.Store(ckzg) blob := randBlob() + + b.ResetTimer() for i := 0; i < b.N; i++ { BlobToCommitment(blob) } @@ -124,6 +124,8 @@ func benchmarkComputeProof(b *testing.B, ckzg bool) { blob = randBlob() point = randFieldElement() ) + + b.ResetTimer() for i := 0; i < b.N; i++ { ComputeProof(blob, point) } @@ -144,6 +146,8 @@ func benchmarkVerifyProof(b *testing.B, ckzg bool) { commitment, _ = BlobToCommitment(blob) proof, claim, _ = ComputeProof(blob, point) ) + + b.ResetTimer() for i := 0; i < b.N; i++ { VerifyProof(commitment, point, claim, proof) } @@ -162,6 +166,8 @@ func benchmarkComputeBlobProof(b *testing.B, ckzg bool) { blob = randBlob() commitment, _ = BlobToCommitment(blob) ) + + b.ResetTimer() for i := 0; i < b.N; i++ { ComputeBlobProof(blob, commitment) } @@ -181,6 +187,8 @@ func benchmarkVerifyBlobProof(b *testing.B, ckzg bool) { commitment, _ = BlobToCommitment(blob) proof, _ = ComputeBlobProof(blob, commitment) ) + + b.ResetTimer() for i := 0; i < b.N; i++ { VerifyBlobProof(blob, commitment, proof) } From 8f57d6caeaa13bddf0a32541a22702b45a8ae121 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:32 +0800 Subject: [PATCH 297/479] go.mod: upgrade c-kzg-4844 (#27907) --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 7d33da0bc16c..30ae287982cf 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( github.com/crate-crypto/go-kzg-4844 v0.3.0 github.com/deckarep/golang-set v1.8.0 github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 - github.com/ethereum/c-kzg-4844 v0.3.0 + github.com/ethereum/c-kzg-4844 v0.3.1 github.com/kylelemons/godebug v1.1.0 github.com/mattn/go-isatty v0.0.17 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible @@ -78,7 +78,7 @@ require ( github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect - github.com/supranational/blst v0.3.11-0.20230406105308-e9dfc5ee724b // indirect + github.com/supranational/blst v0.3.11 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/term v0.26.0 // indirect diff --git a/go.sum b/go.sum index 353d90448351..d7fc489ab42e 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,8 @@ github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 h1:Y9vTBSsV4hSwPSj4bac github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/ethereum/c-kzg-4844 v0.3.0 h1:3Y3hD6l5i0dEYsBL50C+Om644kve3pNqoAcvE26o9zI= -github.com/ethereum/c-kzg-4844 v0.3.0/go.mod h1:WI2Nd82DMZAAZI1wV2neKGost9EKjvbpQR9OqE5Qqa8= +github.com/ethereum/c-kzg-4844 v0.3.1 h1:sR65+68+WdnMKxseNWxSJuAv2tsUrihTpVBTfM/U5Zg= +github.com/ethereum/c-kzg-4844 v0.3.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -168,8 +168,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/supranational/blst v0.3.11-0.20230406105308-e9dfc5ee724b h1:u49mjRnygnB34h8OKbnNJFVUtWSKIKb1KukdV8bILUM= -github.com/supranational/blst v0.3.11-0.20230406105308-e9dfc5ee724b/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= From f7b6ad67a74c60ed2734c269f6854bd7e492b199 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:32 +0800 Subject: [PATCH 298/479] crypto, tests: update fuzzers to native go fuzzing (#28352) --- crypto/blake2b/blake2b_f_fuzz.go | 59 -------- crypto/blake2b/blake2b_f_test.go | 58 +++++++ .../{compress_fuzz.go => compress_test.go} | 44 +++--- tests/fuzzers/bn256/bn256_fuzz.go | 10 +- tests/fuzzers/rlp/rlp_fuzzer.go | 143 ++++++++++++++++++ tests/fuzzers/rlp/rlp_test.go | 25 +++ .../{runtime_fuzz.go => runtime_test.go} | 21 +-- tests/fuzzers/secp256k1/secp_fuzzer.go | 50 ------ tests/fuzzers/secp256k1/secp_test.go | 52 ++++++- 9 files changed, 315 insertions(+), 147 deletions(-) delete mode 100644 crypto/blake2b/blake2b_f_fuzz.go rename tests/fuzzers/bitutil/{compress_fuzz.go => compress_test.go} (63%) create mode 100644 tests/fuzzers/rlp/rlp_fuzzer.go create mode 100644 tests/fuzzers/rlp/rlp_test.go rename tests/fuzzers/runtime/{runtime_fuzz.go => runtime_test.go} (67%) delete mode 100644 tests/fuzzers/secp256k1/secp_fuzzer.go diff --git a/crypto/blake2b/blake2b_f_fuzz.go b/crypto/blake2b/blake2b_f_fuzz.go deleted file mode 100644 index 2a1c80ee093b..000000000000 --- a/crypto/blake2b/blake2b_f_fuzz.go +++ /dev/null @@ -1,59 +0,0 @@ -// Only enable fuzzer on platforms with AVX enabled -//go:build go1.7 && amd64 && !gccgo && !appengine -// +build go1.7,amd64,!gccgo,!appengine - -package blake2b - -import ( - "encoding/binary" -) - -func Fuzz(data []byte) int { - // Make sure the data confirms to the input model - if len(data) != 211 { - return 0 - } - // Parse everything and call all the implementations - var ( - rounds = binary.BigEndian.Uint16(data[0:2]) - - h [8]uint64 - m [16]uint64 - t [2]uint64 - f uint64 - ) - for i := 0; i < 8; i++ { - offset := 2 + i*8 - h[i] = binary.LittleEndian.Uint64(data[offset : offset+8]) - } - for i := 0; i < 16; i++ { - offset := 66 + i*8 - m[i] = binary.LittleEndian.Uint64(data[offset : offset+8]) - } - t[0] = binary.LittleEndian.Uint64(data[194:202]) - t[1] = binary.LittleEndian.Uint64(data[202:210]) - - if data[210]%2 == 1 { // Avoid spinning the fuzzer to hit 0/1 - f = 0xFFFFFFFFFFFFFFFF - } - // Run the blake2b compression on all instruction sets and cross reference - want := h - fGeneric(&want, &m, t[0], t[1], f, uint64(rounds)) - - have := h - fSSE4(&have, &m, t[0], t[1], f, uint64(rounds)) - if have != want { - panic("SSE4 mismatches generic algo") - } - have = h - fAVX(&have, &m, t[0], t[1], f, uint64(rounds)) - if have != want { - panic("AVX mismatches generic algo") - } - have = h - fAVX2(&have, &m, t[0], t[1], f, uint64(rounds)) - if have != want { - panic("AVX2 mismatches generic algo") - } - return 1 -} diff --git a/crypto/blake2b/blake2b_f_test.go b/crypto/blake2b/blake2b_f_test.go index 4e07d131cda6..983c65a749ac 100644 --- a/crypto/blake2b/blake2b_f_test.go +++ b/crypto/blake2b/blake2b_f_test.go @@ -1,6 +1,7 @@ package blake2b import ( + "encoding/binary" "fmt" "reflect" "testing" @@ -57,3 +58,60 @@ var testVectorsF = []testVector{ }, }, } + +func Fuzz(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + fuzz(data) + }) +} + +func fuzz(data []byte) { + // Make sure the data confirms to the input model + if len(data) != 211 { + return + } + // Parse everything and call all the implementations + var ( + rounds = binary.BigEndian.Uint16(data[0:2]) + + h [8]uint64 + m [16]uint64 + t [2]uint64 + f uint64 + ) + + for i := 0; i < 8; i++ { + offset := 2 + i*8 + h[i] = binary.LittleEndian.Uint64(data[offset : offset+8]) + } + for i := 0; i < 16; i++ { + offset := 66 + i*8 + m[i] = binary.LittleEndian.Uint64(data[offset : offset+8]) + } + t[0] = binary.LittleEndian.Uint64(data[194:202]) + t[1] = binary.LittleEndian.Uint64(data[202:210]) + + if data[210]%2 == 1 { // Avoid spinning the fuzzer to hit 0/1 + f = 0xFFFFFFFFFFFFFFFF + } + + // Run the blake2b compression on all instruction sets and cross reference + want := h + fGeneric(&want, &m, t[0], t[1], f, uint64(rounds)) + + have := h + fSSE4(&have, &m, t[0], t[1], f, uint64(rounds)) + if have != want { + panic("SSE4 mismatches generic algo") + } + have = h + fAVX(&have, &m, t[0], t[1], f, uint64(rounds)) + if have != want { + panic("AVX mismatches generic algo") + } + have = h + fAVX2(&have, &m, t[0], t[1], f, uint64(rounds)) + if have != want { + panic("AVX2 mismatches generic algo") + } +} diff --git a/tests/fuzzers/bitutil/compress_fuzz.go b/tests/fuzzers/bitutil/compress_test.go similarity index 63% rename from tests/fuzzers/bitutil/compress_fuzz.go rename to tests/fuzzers/bitutil/compress_test.go index 5f241255248e..0c6ce21c9d35 100644 --- a/tests/fuzzers/bitutil/compress_fuzz.go +++ b/tests/fuzzers/bitutil/compress_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 The go-ethereum Authors +// Copyright 2023 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify @@ -18,41 +18,51 @@ package bitutil import ( "bytes" + "testing" "github.com/XinFinOrg/XDPoSChain/common/bitutil" ) -// Fuzz implements a go-fuzz fuzzer method to test various encoding method -// invocations. -func Fuzz(data []byte) int { - if len(data) == 0 { - return 0 - } - if data[0]%2 == 0 { - return fuzzEncode(data[1:]) - } - return fuzzDecode(data[1:]) +func FuzzEncoder(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + fuzzEncode(data) + }) +} +func FuzzDecoder(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + fuzzDecode(data) + }) } // fuzzEncode implements a go-fuzz fuzzer method to test the bitset encoding and // decoding algorithm. -func fuzzEncode(data []byte) int { +func fuzzEncode(data []byte) { proc, _ := bitutil.DecompressBytes(bitutil.CompressBytes(data), len(data)) if !bytes.Equal(data, proc) { panic("content mismatch") } - return 1 } // fuzzDecode implements a go-fuzz fuzzer method to test the bit decoding and // reencoding algorithm. -func fuzzDecode(data []byte) int { +func fuzzDecode(data []byte) { blob, err := bitutil.DecompressBytes(data, 1024) if err != nil { - return 0 + return + } + // re-compress it (it's OK if the re-compressed differs from the + // original - the first input may not have been compressed at all) + comp := bitutil.CompressBytes(blob) + if len(comp) > len(blob) { + // After compression, it must be smaller or equal + panic("bad compression") + } + // But decompressing it once again should work + decomp, err := bitutil.DecompressBytes(data, 1024) + if err != nil { + panic(err) } - if comp := bitutil.CompressBytes(blob); !bytes.Equal(comp, data) { + if !bytes.Equal(decomp, blob) { panic("content mismatch") } - return 1 } diff --git a/tests/fuzzers/bn256/bn256_fuzz.go b/tests/fuzzers/bn256/bn256_fuzz.go index 7300c98c13f0..972eaf4b5ebe 100644 --- a/tests/fuzzers/bn256/bn256_fuzz.go +++ b/tests/fuzzers/bn256/bn256_fuzz.go @@ -61,8 +61,8 @@ func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2, *bn254.G2Affine) return xc, xg, xs } -// FuzzAdd fuzzez bn256 addition between the Google and Cloudflare libraries. -func FuzzAdd(data []byte) int { +// fuzzAdd fuzzez bn256 addition between the Google and Cloudflare libraries. +func fuzzAdd(data []byte) int { input := bytes.NewReader(data) xc, xg, xs := getG1Points(input) if xc == nil { @@ -94,9 +94,9 @@ func FuzzAdd(data []byte) int { return 1 } -// FuzzMul fuzzez bn256 scalar multiplication between the Google and Cloudflare +// fuzzMul fuzzez bn256 scalar multiplication between the Google and Cloudflare // libraries. -func FuzzMul(data []byte) int { +func fuzzMul(data []byte) int { input := bytes.NewReader(data) pc, pg, ps := getG1Points(input) if pc == nil { @@ -136,7 +136,7 @@ func FuzzMul(data []byte) int { return 1 } -func FuzzPair(data []byte) int { +func fuzzPair(data []byte) int { input := bytes.NewReader(data) pc, pg, ps := getG1Points(input) if pc == nil { diff --git a/tests/fuzzers/rlp/rlp_fuzzer.go b/tests/fuzzers/rlp/rlp_fuzzer.go new file mode 100644 index 000000000000..1ffd845ba055 --- /dev/null +++ b/tests/fuzzers/rlp/rlp_fuzzer.go @@ -0,0 +1,143 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rlp + +import ( + "bytes" + "fmt" + "math/big" + + "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/rlp" + "github.com/holiman/uint256" +) + +func decodeEncode(input []byte, val interface{}, i int) { + if err := rlp.DecodeBytes(input, val); err == nil { + output, err := rlp.EncodeToBytes(val) + if err != nil { + panic(err) + } + if !bytes.Equal(input, output) { + panic(fmt.Sprintf("case %d: encode-decode is not equal, \ninput : %x\noutput: %x", i, input, output)) + } + } +} + +func fuzz(input []byte) int { + if len(input) == 0 { + return 0 + } + if len(input) > 500*1024 { + return 0 + } + + var i int + { + rlp.Split(input) + } + { + if elems, _, err := rlp.SplitList(input); err == nil { + rlp.CountValues(elems) + } + } + + { + rlp.NewStream(bytes.NewReader(input), 0).Decode(new(interface{})) + } + + { + decodeEncode(input, new(interface{}), i) + i++ + } + { + var v struct { + Int uint + String string + Bytes []byte + } + decodeEncode(input, &v, i) + i++ + } + + { + type Types struct { + Bool bool + Raw rlp.RawValue + Slice []*Types + Iface []interface{} + } + var v Types + decodeEncode(input, &v, i) + i++ + } + { + type AllTypes struct { + Int uint + String string + Bytes []byte + Bool bool + Raw rlp.RawValue + Slice []*AllTypes + Array [3]*AllTypes + Iface []interface{} + } + var v AllTypes + decodeEncode(input, &v, i) + i++ + } + { + decodeEncode(input, [10]byte{}, i) + i++ + } + { + var v struct { + Byte [10]byte + Rool [10]bool + } + decodeEncode(input, &v, i) + i++ + } + { + var h types.Header + decodeEncode(input, &h, i) + i++ + var b types.Block + decodeEncode(input, &b, i) + i++ + var t types.Transaction + decodeEncode(input, &t, i) + i++ + var txs types.Transactions + decodeEncode(input, &txs, i) + i++ + var rs types.Receipts + decodeEncode(input, &rs, i) + } + { + i++ + var v struct { + AnIntPtr *big.Int + AnInt big.Int + AnU256Ptr *uint256.Int + AnU256 uint256.Int + NotAnU256 [4]uint64 + } + decodeEncode(input, &v, i) + } + return 1 +} diff --git a/tests/fuzzers/rlp/rlp_test.go b/tests/fuzzers/rlp/rlp_test.go new file mode 100644 index 000000000000..377b3961bf14 --- /dev/null +++ b/tests/fuzzers/rlp/rlp_test.go @@ -0,0 +1,25 @@ +// Copyright 2023 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rlp + +import "testing" + +func Fuzz(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + fuzz(data) + }) +} diff --git a/tests/fuzzers/runtime/runtime_fuzz.go b/tests/fuzzers/runtime/runtime_test.go similarity index 67% rename from tests/fuzzers/runtime/runtime_fuzz.go rename to tests/fuzzers/runtime/runtime_test.go index c05254fab9ef..97d7cdc71fb9 100644 --- a/tests/fuzzers/runtime/runtime_fuzz.go +++ b/tests/fuzzers/runtime/runtime_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 The go-ethereum Authors +// Copyright 2023 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify @@ -17,20 +17,15 @@ package runtime import ( + "testing" + "github.com/XinFinOrg/XDPoSChain/core/vm/runtime" ) -// Fuzz is the basic entry point for the go-fuzz tool -// -// This returns 1 for valid parsable/runable code, 0 -// for invalid opcode. -func Fuzz(input []byte) int { - _, _, err := runtime.Execute(input, input, &runtime.Config{ - GasLimit: 12000000, +func Fuzz(f *testing.F) { + f.Fuzz(func(t *testing.T, code, input []byte) { + runtime.Execute(code, input, &runtime.Config{ + GasLimit: 12000000, + }) }) - // invalid opcode - if err != nil && len(err.Error()) > 6 && err.Error()[:7] == "invalid" { - return 0 - } - return 1 } diff --git a/tests/fuzzers/secp256k1/secp_fuzzer.go b/tests/fuzzers/secp256k1/secp_fuzzer.go deleted file mode 100644 index 84a7eebe1bf7..000000000000 --- a/tests/fuzzers/secp256k1/secp_fuzzer.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2021 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// build +gofuzz - -package secp256k1 - -import ( - "fmt" - - "github.com/XinFinOrg/XDPoSChain/crypto/secp256k1" - "github.com/btcsuite/btcd/btcec/v2" - fuzz "github.com/google/gofuzz" -) - -func Fuzz(input []byte) int { - var ( - fuzzer = fuzz.NewFromGoFuzz(input) - curveA = secp256k1.S256() - curveB = btcec.S256() - dataP1 []byte - dataP2 []byte - ) - // first point - fuzzer.Fuzz(&dataP1) - x1, y1 := curveB.ScalarBaseMult(dataP1) - // second point - fuzzer.Fuzz(&dataP2) - x2, y2 := curveB.ScalarBaseMult(dataP2) - resAX, resAY := curveA.Add(x1, y1, x2, y2) - resBX, resBY := curveB.Add(x1, y1, x2, y2) - if resAX.Cmp(resBX) != 0 || resAY.Cmp(resBY) != 0 { - fmt.Printf("%s %s %s %s\n", x1, y1, x2, y2) - panic(fmt.Sprintf("Addition failed: geth: %s %s btcd: %s %s", resAX, resAY, resBX, resBY)) - } - return 0 -} diff --git a/tests/fuzzers/secp256k1/secp_test.go b/tests/fuzzers/secp256k1/secp_test.go index 76bae87086f5..59bfe07cc20a 100644 --- a/tests/fuzzers/secp256k1/secp_test.go +++ b/tests/fuzzers/secp256k1/secp_test.go @@ -1,8 +1,54 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + package secp256k1 -import "testing" +import ( + "fmt" + "testing" + + "github.com/XinFinOrg/XDPoSChain/crypto/secp256k1" + "github.com/btcsuite/btcd/btcec/v2" +) func TestFuzzer(t *testing.T) { - test := "00000000N0000000/R00000000000000000U0000S0000000mkhP000000000000000U" - Fuzz([]byte(test)) + a, b := "00000000N0000000/R0000000000000000", "0U0000S0000000mkhP000000000000000U" + fuzz([]byte(a), []byte(b)) +} + +func Fuzz(f *testing.F) { + f.Fuzz(func(t *testing.T, a, b []byte) { + fuzz(a, b) + }) +} + +func fuzz(dataP1, dataP2 []byte) int { + var ( + curveA = secp256k1.S256() + curveB = btcec.S256() + ) + // first point + x1, y1 := curveB.ScalarBaseMult(dataP1) + // second points + x2, y2 := curveB.ScalarBaseMult(dataP2) + resAX, resAY := curveA.Add(x1, y1, x2, y2) + resBX, resBY := curveB.Add(x1, y1, x2, y2) + if resAX.Cmp(resBX) != 0 || resAY.Cmp(resBY) != 0 { + fmt.Printf("%s %s %s %s\n", x1, y1, x2, y2) + panic(fmt.Sprintf("Addition failed: geth: %s %s btcd: %s %s", resAX, resAY, resBX, resBY)) + } + return 0 } From b5cc7e6fc023628318065f3895f156b9280f593b Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:32 +0800 Subject: [PATCH 299/479] crypto/blake2b: put architecture-dependent features behind build-tag (#28381) This change to fixes a compilation-flaw on master, by putting architecture-specific functions behind corresponding build tags. --- crypto/blake2b/blake2b_f_fuzz_test.go | 75 +++++++++++++++++++++++++++ crypto/blake2b/blake2b_f_test.go | 58 --------------------- 2 files changed, 75 insertions(+), 58 deletions(-) create mode 100644 crypto/blake2b/blake2b_f_fuzz_test.go diff --git a/crypto/blake2b/blake2b_f_fuzz_test.go b/crypto/blake2b/blake2b_f_fuzz_test.go new file mode 100644 index 000000000000..1de9a62de9d7 --- /dev/null +++ b/crypto/blake2b/blake2b_f_fuzz_test.go @@ -0,0 +1,75 @@ +// Only enable fuzzer on platforms with AVX enabled +//go:build go1.7 && amd64 && !gccgo && !appengine +// +build go1.7,amd64,!gccgo,!appengine + +package blake2b + +import ( + "encoding/binary" + "testing" +) + +func Fuzz(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + fuzz(data) + }) +} + +func fuzz(data []byte) { + // Make sure the data confirms to the input model + if len(data) != 211 { + return + } + // Parse everything and call all the implementations + var ( + rounds = binary.BigEndian.Uint16(data[0:2]) + + h [8]uint64 + m [16]uint64 + t [2]uint64 + f uint64 + ) + + for i := 0; i < 8; i++ { + offset := 2 + i*8 + h[i] = binary.LittleEndian.Uint64(data[offset : offset+8]) + } + for i := 0; i < 16; i++ { + offset := 66 + i*8 + m[i] = binary.LittleEndian.Uint64(data[offset : offset+8]) + } + t[0] = binary.LittleEndian.Uint64(data[194:202]) + t[1] = binary.LittleEndian.Uint64(data[202:210]) + + if data[210]%2 == 1 { // Avoid spinning the fuzzer to hit 0/1 + f = 0xFFFFFFFFFFFFFFFF + } + + // Run the blake2b compression on all instruction sets and cross reference + want := h + fGeneric(&want, &m, t[0], t[1], f, uint64(rounds)) + + have := h + if useSSE4 { + fSSE4(&have, &m, t[0], t[1], f, uint64(rounds)) + if have != want { + panic("SSE4 mismatches generic algo") + } + } + + if useAVX { + have = h + fAVX(&have, &m, t[0], t[1], f, uint64(rounds)) + if have != want { + panic("AVX mismatches generic algo") + } + } + + if useAVX2 { + have = h + fAVX2(&have, &m, t[0], t[1], f, uint64(rounds)) + if have != want { + panic("AVX2 mismatches generic algo") + } + } +} diff --git a/crypto/blake2b/blake2b_f_test.go b/crypto/blake2b/blake2b_f_test.go index 983c65a749ac..4e07d131cda6 100644 --- a/crypto/blake2b/blake2b_f_test.go +++ b/crypto/blake2b/blake2b_f_test.go @@ -1,7 +1,6 @@ package blake2b import ( - "encoding/binary" "fmt" "reflect" "testing" @@ -58,60 +57,3 @@ var testVectorsF = []testVector{ }, }, } - -func Fuzz(f *testing.F) { - f.Fuzz(func(t *testing.T, data []byte) { - fuzz(data) - }) -} - -func fuzz(data []byte) { - // Make sure the data confirms to the input model - if len(data) != 211 { - return - } - // Parse everything and call all the implementations - var ( - rounds = binary.BigEndian.Uint16(data[0:2]) - - h [8]uint64 - m [16]uint64 - t [2]uint64 - f uint64 - ) - - for i := 0; i < 8; i++ { - offset := 2 + i*8 - h[i] = binary.LittleEndian.Uint64(data[offset : offset+8]) - } - for i := 0; i < 16; i++ { - offset := 66 + i*8 - m[i] = binary.LittleEndian.Uint64(data[offset : offset+8]) - } - t[0] = binary.LittleEndian.Uint64(data[194:202]) - t[1] = binary.LittleEndian.Uint64(data[202:210]) - - if data[210]%2 == 1 { // Avoid spinning the fuzzer to hit 0/1 - f = 0xFFFFFFFFFFFFFFFF - } - - // Run the blake2b compression on all instruction sets and cross reference - want := h - fGeneric(&want, &m, t[0], t[1], f, uint64(rounds)) - - have := h - fSSE4(&have, &m, t[0], t[1], f, uint64(rounds)) - if have != want { - panic("SSE4 mismatches generic algo") - } - have = h - fAVX(&have, &m, t[0], t[1], f, uint64(rounds)) - if have != want { - panic("AVX mismatches generic algo") - } - have = h - fAVX2(&have, &m, t[0], t[1], f, uint64(rounds)) - if have != want { - panic("AVX2 mismatches generic algo") - } -} From 824dea669c23cbfe40a55c5cdc41d4aca495a7fd Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:32 +0800 Subject: [PATCH 300/479] crypto/kzg4844: use the new trusted setup file and format (#28383) --- crypto/kzg4844/trusted_setup.json | 12422 ++++++++++------------------ go.mod | 4 +- go.sum | 8 +- 3 files changed, 4168 insertions(+), 8266 deletions(-) diff --git a/crypto/kzg4844/trusted_setup.json b/crypto/kzg4844/trusted_setup.json index 37108fee3e62..c6d724efafdf 100644 --- a/crypto/kzg4844/trusted_setup.json +++ b/crypto/kzg4844/trusted_setup.json @@ -1,8265 +1,4167 @@ { - "setup_G1": [ - "0x97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", - "0x854262641262cb9e056a8512808ea6864d903dbcad713fd6da8dddfa5ce40d85612c912063ace060ed8c4bf005bab839", - "0x86f708eee5ae0cf40be36993e760d9cb3b2371f22db3209947c5d21ea68e55186b30871c50bf11ef29e5248bf42d5678", - "0x94f9c0bafb23cbbf34a93a64243e3e0f934b57593651f3464de7dc174468123d9698f1b9dfa22bb5b6eb96eae002f29f", - "0x82b8775b874067bdd4479ac237f8d56036a742c17901354caaf38bf8c70e696650fbec76f0cd941ed8c658f44ea359ff", - "0xa7ce299c79c7d7e4f1adecd754c5aff8a720728ab27f5737b7b399f72724407ac54965088596375b8c876665ed8e4ff1", - "0x81ca4c808a76a6f217f8b0540ff400199295da69b2587b7be6aeb56447fa4fac08d154a27c4aa6082bc40660078d36e9", - "0xa70bad5311c97f1f3fea5d3375da1a11ba948aca41609ea28666dd343e07af766834e1256dc685ac1dcd915073250864", - "0xa91c2911a658ba79f56abe30716a3398387630e785b351b07344022a04b2f5c90de5573bd6e8048fe8878dde19336c5b", - "0xa8c560283fce9813bcbaddfb78cff93efcbc39b33025cfad94ebd40942a9fa605d2a947dc3a1f03c2e454075892e96bf", - "0xaa14f07fbd2c1ce7bd995e335c69b5f675ea573517c1834e787b30ab4fa10aecc62ecc5e617ac8a539af1aff114dc9ec", - "0x87f03429aff126b7c5a918423da278e17b5f48a4cdd6d34dba77a75f4f99e26a417e65d6a8579bcb2eaaf1d4d8c64dce", - "0xb1ac81ba91ede78315f712d524e9d821a152203f04141ba77f4e481ad5881473dff14a71788ce941f0905b429e7ee5b2", - "0x8f5c2af611ddfa3edf7e442d00e56a24d615bac848c05070c908c741ba18b67eb2e82e6651c9b3c70fb8edbf051810c4", - "0xaa4115b19221e4d17cc335d4f9b0aad22df566231f2286d550e97ff2875cbc419edfa189c4ecb24001123b95c6aaa2da", - "0xb363ba913969df0debd4e2712ae6e9177ce82e169ce7e0ff1d7616ef8e352aff3efb40fffbf7bff1b21cb8a33e19b455", - "0xb1013d778727d20466778cea47e1bf56a77168a8ce1b33bb1265f66438ab2bf4a7df4f4142b8681f2993ea9baf798d17", - "0x83b7250ee17d8529207db97b73c1c4a92ac076951a577ce2fe3a2cd633b461c1820c139ab36a895a5962e143c6198386", - "0x86d180bd2f0a4919764e6f4e846ec0d5ebe44060ec1e529ed15101d7e531bf1b8f9412916ea5aeb93b37b2d7c6bfb408", - "0x827451462c79d74b504c01bda199481b3c70416f66a95b2216686ca4d48da48932b0d323d0cd630a1e86e8033e832e5f", - "0xb789d217cb12c334fedff0ae01f85b96e968fb98761755d1ba3ee17934e8fbd83172225df7c0c6cb99432a30a0ef8c24", - "0xb730e5412dfbd646b0d2fe084a1a32eb5596c3fe8a5bc0f430151804f9e67f05f70b522e9aef08082b0afdc652a1d213", - "0x9987653bacd9bc1659b17f6964aec06ea63b787813d4601bee0479706aed5595ac82c87ed4f96f0cd30c19e1d9510a91", - "0x9506a9ba38f1d26c35a17c7e2554e28eb347a19cef523846a2559fb80fb40306b2f85bdc2c9fb98c2267df21c1ee3589", - "0x98dda58de74c0cdaef97b2826b4a9d53c9e9ea592dc0a755ccf5b3fbc1264979578563f5979aaa246e679918053c5f83", - "0xb00aaa16841ab53883af481e2f925050f5f7bf7d8088bc696f55f30593bdbbaf434f5d2b46095ed097b6cdb96c8fbc3b", - "0xb463d656480f89335d3a840a7b9398877003985388871b148ba708c60f9857c7585ef17c8b2ae67fbb191c04ad61e692", - "0x80af54f3d0584126e23635276d650292baf7e3e12bb06468708107bcd80937d36575721ee7472c5f085ffa71dbf438ad", - "0x94ccb8ade84e834685110c96908b42e10d2184208f434d7f98d96cc158e0c0c95135979600e5e9f465d5846b0bb3c787", - "0x8e13674b00c633d7cceb4f6ecd61e4f99420d6cccf9db5e81f8c90f6c661bc76e10939b83b56c225fce8565f525d4fa4", - "0xa46a15b2e671c1a1df2490768dec1093caf537e1a21fbc11ff8ba8b21b9f2be8d50257027d9357df20d9fbb1307d7249", - "0xb8ed532d48b0533a3084d7a5eea7b401255c5825e9a1b80ed81fd530cd69e347d152b1ad8a899acff7d68e0103bbfbde", - "0xad6b7df980ebaa24177d830c4aa522d6179a9a489257f60ee6604cccc2cbe90fb1f72aa9d5bee0d3a88f73b179485f56", - "0xa56855e9fcf62ceef3043991a93ec24f8f6b5667ef5fb7ad1771249ece68a73580ec3cf3e58a009ca4650c01241ad172", - "0xab2f25517d4b0b33d317eb78d091d3c3f98dc352b8a3e4650f7005f9327129e23d95f38eaeda5e9b51c50a31d20a4c20", - "0xa2d4071385b8a421da86f39739eaadcdea5685466feb6ac083cba0ea4c71dbbdf655032097276d703f9a77a4ca6fab03", - "0xa8681d7c258984f01e92e92c95738692b7bbd59c3a802adf4dda8d34add69590b080391c09e98e3b75c085c9f191e5e5", - "0x97685643da6c07b5e5fe91393122813ba11c8ef3dbd43a03b3a22a7a1603201fd516c1929418eafb14039270694c239a", - "0xa7bb3b85d6101e4fb0bcf540f52041cdb3e819d517465e342b832f0e91346a9a18bdb38845ea4d2b71ab87ef3bf59f96", - "0x8afc90b7d35336fdcf8f81cd024e921e244520ecfcb5a3994f2bbd595366b68bfa792a8dceb11e1e889b11432c9dad6b", - "0x94d9db7bd04f962d8d6caa3b7aa0f19acbd58a09d35ae187158d77e537d2fc65215f51f1afd62d4ba378e7d176a680f9", - "0xad62d7c01b14b6f97e6084ec9f9d084f106a7ff3d603032e6e34c027cdce4b0fe3c20ac7931f1209439a59c9fede4815", - "0xa5b44a87bd0ada7498e011e495a2818a8694746c4e7dc9d24c0c1096f54be6439e08c1b11c10d7c4bf68fe392000e971", - "0x828626c6609acc599f1bf216e9a4310fc3cb227e0d2e47bfe3360239427c8b0cc300cddf92456a5c36620596a6d37027", - "0x8380f91baac6447dd39886455ec5d99b874ac114a3c6a6ded18fc4ef69c2208ec19732428d8329d200a69f31792b852e", - "0x85a8389b522b0a686234450165514127006baaa3907f6eb29c976522591a542ffb681b3b88c4886814fd7ba3cc8110f7", - "0xb8ae7949ddafad37c0bc4d48325a7cbcd3096fb92c04a027c650a03cc609c7eac250d6a7ba341616bc36f64f1b4c8be4", - "0x8f9b9d2c2ab5c85abe946ed9986e0f304281b277d4d48c7760ea2331b55a9e9a1c4d53a6bdd83fa6294f421ca7431e29", - "0x9464b906ea8bc994b31e03c5f2af2be0724a43293fd42cbd2263b2de75a2ec04832d1100ce62ac2c0708f05fb6bb3ce6", - "0x93d923f6805e7cf972d8387b352d77215724a3e1f5489c4114fcf0b25fc2231963eda872387a1369a02b2e8b888d6427", - "0xaba4af392884eb7283fc5611ddc1cebfecf9477462e951bdae650e311606e129b4424452a3a14717146f954a7fa1cfc3", - "0xa8d0bab694d116e4f21fa19ff8fa4c6fe4061dbb54cbceda8235a7e05159737f87e008beccb90df0bac9c7d572281903", - "0x85743e3ecbac7ae04a81a09c2961397aa4bd20401533cd24d5fc0693cbfbdd2b37bbee6dec2ae5d0a66250d1fcba6944", - "0x80ae913447d7d3f6c54f9cb584aa1603308146daeb3024c8e06ede66ddc97821df09f9591453e9b617b828a02d54c719", - "0x803c2a64bb1c68890b5f1909be3aa180830ee3ef316d3aac38bfd909e2b19d890525e18e8fc2f405ee70ac14f5569b3f", - "0x964d2968724eb790f2f42263fcaaa1869c373b57b3eeee904f8b36f13169f07d5e29cb2b03c74d3a7adb772e91d5a59a", - "0x98a72ce71a57262aa058643a5cd39af64cc9eee88bef7edb003143983f29d87c7f9658b1ec89712f79f6f79dc24a6a45", - "0x91f3479c5d7c76acd2d51883961179efc975c714720187cc9c0aa7aeff71ca1b3e2db5b0a90fd3ff6abf880ebc49fe36", - "0x84312757edd09f111420bfede10ed3c1fa39d1723ddb9bd4d0004c829f0c1eb060e9648fd75f2e5427a67a5b37945a9f", - "0x95edd726cf4042a082d786262304c51d8d5e6a89b1b58e825a11febe5f861d5ce076bdcb2fc0a5dfa95eb2e5b0ffc32e", - "0x96500da38f942871d78fcc46cda1e72944c7888b538b82e2a979f149e5061a20c7602860f82b76510d02efdf3a911f5a", - "0x8ac62eda98bef8864df243696b53651a02a391b898535d2d76ac5a8e9322e0178a290c83f5afe72ffe80ad56183469e3", - "0x8ab2d4427fb6d3da5cf6c59835bdb39fb0c2de82c213b5de77edae3304458ea505511bd98fda95bdbbb9058bd5e92c34", - "0xab67c4344a5080930029ca3b803883ad05ca004ddefb48d5164e71a1c6dd96b27aaec70f62b39bb126ce1a57bbff1453", - "0x86c6bf91686bff714a873a78b0fe449db5317a5172a0a14eb3a96b2997b888d5d3f440de8baa32a6966fe44c3625b014", - "0x81d4f1e9d9e550125290d993a4886d46aac8cb29dbbba1e608aefc3432569c5faf14d8b49fcb485d9b75b649ad6b2fa5", - "0x8594140f253ced6fa98dd90ab4f38899916bcc6f1052572f182e46c00752f3053c390512338a0bc8f8c27a91916b855f", - "0x911284d4fad4999bb37590206d582b9e62ffbb815f414fd829f5b2843e6f0e1a132cd64464c131d5a0f476469a37daa1", - "0x8631a6a4987410982db9c0ba632023a5b613f553b6b8ffd3cfd501b2417523ba8cf06741c62f24b405554bd93e39e626", - "0x906ac35d22794a10a7273fdbca499fd921799b1ce9414643779dce9e1ec37920a5aa2caceb4b70a0eaf56c6032ef1b43", - "0x87374cdb8b7a1ce3c182b31eec465d435e35df782fe3a11f421462b48cf56c6fef2a9cb8ee4fe89672ba7804156d9e3a", - "0xa1f825e0246eee506c8ce40f849a17f75e8a0d6fc3f68b6a4dd431173b4fe997d30dca53005829e4e2422a4077ce35c7", - "0x875ad0379abd9873f6634692e33e9b36353e1a0d15b13d3215eb591244e1f236eb2f8f75274ca7f096179d1714fa68b7", - "0xb87b4e1acc09c5701fd9d75375ab896f178c1b3648fb9a2e2c6e1478778156decc32cd390766f3e80b36beb1e3a6bdec", - "0x836ca80949269eb52395776ac5ceb35b7df717a981c5cbbbb627f73c274aa8164e973a7b01012fa72a02404e878a9918", - "0xa770b13a8f07f74e5a75842b18f2520f6d0be42c865a29dd81bfe485e69a83c40ad10ce229afce276ccc9cb46c54b912", - "0xb4b919322bba2866baeed38bf0e2389d4fe6ab6166597e87dbfee75acac7c2f5ad3bef55293b56957c103d5825051bb5", - "0xb6171f1bbeedb3ee1af368c9c9f327d1dc3e55aeaffbe15f14db8038cd72540b62fe65f44ad0b5486dcf4023f0e91af8", - "0x8e42d0c1e8e8c2ccaf06edcc3c686aed56b8c987f9d68f20937fc088120a410cb92fb0ab45bba5db70b86876015a6b72", - "0x937bcff1af9685fd0d1f6616acf91d97ac9fcb1eb96d49d1c880c9945c1fcf1414f63d59fb78348d08a8546f6e83e407", - "0xa6eeb4873c0531fbcd407c2e702c68e4980fa77c9c032b9913b89031702cfa56f335fc413576c37ac4d523357a841203", - "0xb3962b5eed69cfa27fb94edba74b6cedd7569352ea71861494dd579da96d9743655b6308e54f8a42ee6d7e805c1bc0f9", - "0x8eea944dce7202b033ce734c9e88e82dd760c916e00b217cf1f00bf6ec5f20e21885d5fe95d6138871d167de4c46359e", - "0x81e6c7b356e2703ee333a9dfeb2b54260636422b9bda118e0523a20ce83b30fefc2f019e8291a8db05d207f0fa7332fb", - "0x83817f6164dc9e8e2506252511cb9871a8c9b595dde45f67e75ce3505f947b3fb3b804c18c054ad13b1518a98f59f008", - "0xa9ab4dbe7699e7982cd750d7effe031f273fab6b2e024a0b4f8beccb5c280903bcd3f2400b9cac7e8c94e157b4658ab6", - "0x84d2e3bc66fc6b59a1ee98b8981ebca0901d846c40d207e5bb5004ec9339d28956d16f054af52453f6a7ff3fc66c346b", - "0xb24bf0f69c3e86f610b6d27885ac5f4556fbb14e8286681538ddbb0b4921aa0d5604fedef0daf4a514ae15268a640174", - "0xa4be967f7f31995562669bf9587f5463bd1d9873fe9169687790e75961efa5ce2252fd21276d022f580de23527282838", - "0xa3f3c4e673b302bdb91fa3cbdec88499752e6ffe37e2215d69b8a426f4da98c3a10e4c659e77c656172e4e8b1b1a41bb", - "0xb704ffbb3434563bbbce69ca7e812a8bd30757b1e90630bf3261735f9ea221524b36f97dec324ffd209bef45bdf6f2b4", - "0x959dde49f15c663a2de000195e182a11d8c396c1380f98322cbe5521b697bc3bec3223ca9e94ee2734c4ffdfb6a19e8c", - "0xa469685143cd82b78d7b1854c350da63819d9d86670e9b35a72381d0362cf5c3f1d24e22ef2ea6a12176c9dad39fd51c", - "0xadb97ef4463e5e13d91b75a3086d72a841a60be278e9651d9ac5f76c9537bac5eac33424a1ea00522b3357fcefea0738", - "0xa4597b2ced7566576e71b4f105b5ee48aa4ffca93901e9b626f7059824f53be3e8f3560e6861285f3d97fe88054fee83", - "0xa18d9b1b81564447f798ce5966bf10c823aedb14b143972eb4dbbba9312fc79f46635aa016cd20c53be90f170f06fb84", - "0xac4724069177d3c6ac1b72ea2a7d6bc5ac3d4b2a4dbad124152fbd170c9c1038cdcf255d162a25c14ae8df11a3849945", - "0x892683f64179ba84f6a447c5c7489e3cdf02474d2837dd7bf3b82a4dd05a6461ce94fff28d65b9539cacaf47dddedbc1", - "0xa68ad797bbc1b2909e216a0b3f39aa6c3e4dfc7a49f81a206b530ec0c6ba30f871e4a0053625aeb99448026ae2e0a6eb", - "0x964ff8badf35b6b93be6d97209d87f4ac8847be1c2ac4bcafa1db5c3f604f61834c85b3dcf58af50d09bd03ff8d78f27", - "0xb76dc9ec64b1fab7be269097a18a77144623d37bc656934fa1562817c922485e69b18ef40413ee309e100fde645fa7b2", - "0xb2a812be6e69f284580ebdec5ae2cdffd587bc7eae10989e9d2f290498b1eaa934b148ec7783edec300be5d7a9b34af0", - "0x85ffcabc623f8ffc58c5f640f857e27b7c105359315a3969f346e1366acb2af88f4acc025b299b9c324a8535c380a2c5", - "0x8d0140f79fb8ef02d13b1d51c4ba1af5b5ffb19322f88912215d4198f9a592f7ec6800c8a3ca853a3b68f9bf0353a13a", - "0xb3174deb53c1ebb6a1e16c915cac287573b70fe4e0036e8e971e8e807a77362ede632f6e3f29cb87a80a318213946ff1", - "0x8c58d603f6420e3f55522ec2853065125af4e7773a909e28296552f7f8ec4691ada9211d834dca38e118f432b6cfe03b", - "0xaa7ac268e155ff074bfc197844e21fc2a9f9aec9b50d9cda63f50d3c4fbbf9221e6fac3a6ba0f7e4cde71fecd493a15d", - "0xa191337721bc9fd2d3ec2ca6f6f97ca2462ef5e207464bf9e746a650a67d69abb5f578a8238521cee3f704b275845e47", - "0x93521abea8f38c103ebed3313a3af8f27f03c9a54681847f4201bf9f72f1f63064b18175986fca64f80b4380905e894c", - "0xa1b9d063d6538885f9826b84944123d7d6027dd030aef29fd6229f4cf5d32404f7dd0e899a0c8f4b6bdf4649e8a8966f", - "0xa15d5497f0fd2fd0b2c2e5df58a25a72a9d99df8215951ea58c15569d312c6f096f78034f6a8502f808e649f6cb9283a", - "0xb3c275306852612362e1073d0f4da3ce598dc5fac3f3eefa22ccee35dd57a4caae347b43342cd1f6a6e068d3ea9fd30c", - "0x94eb678e0700bf39caf428c65bbf2fbf7f601c39e382570a4df9186ff1dd5a958d78e051a5fd084e4f75536a14b7690b", - "0x97b13995bbcb8e824bec28488994a830a9c1f34ae4c1a16d5528d57f09e4c8b5d81677ea9f979f0acb8cac629ee09c85", - "0x817c99ad48bc05bd4fd29f952dbdc5ef56bb02f3442c18e3b91cb6d72ac2d2a5df901c099165ded1bee62c3ed13c41e8", - "0xa884acf980f6470e11cff347692d8a7cb7860d4822112f7bfeb02efb05948ea98c837d5d98dd7a104aa36eb8f016a0f4", - "0x95debd2ed23a23a16a393f59f666cfc864f63751238b73981faec4a85b4c04cfa11520c9e4cbe4e23fe80e86c260093a", - "0x937b4691c59453bc6cf6468ed5b17dbb25496bfa3817798562cd5fd86ab5ee35745991afea2fe271ce0fbe5a990c41c7", - "0xb4da98c879e6b475c540ff2c5501299f0e3e97b7b93beb45faef1253f7de96968898505e672cfc4a3ee7e20c1f72c256", - "0x8ec9d806f344d0c675bb5ecd47c54defb5f059a5233dfb2d459632b9b22edd6c4b8c47fd7899ab13e35f37ede9b124f8", - "0xaab4408410abb4d2cd98694f71b5452e6fab2690daa3066b3f9747e7dc40b57259d52e6fddeaeeca227b733d049b9694", - "0xb85a12f39808961c331038159255140a61dedc56050345a2eb13b1f7d140ae07b353d68d22f2cf60925fe66e598213e9", - "0xb61bc3bd68bffdbe9731f48fcd523491da04dab83add49fde390070513b9ad87a489010f1ccfe6f54e9a48edaf88b5f9", - "0x8f50f6d8235824cf25031f09e4b139bd89c1090269dae95a5aa0dacaf5f9b59c329a5a3cdddf9efe6c77cd61f481dcbc", - "0x91a543b85e18f34361d7df5ece8504d456627fbce65abff437007e9109359538a03996e35393a40f0586f962921eccaf", - "0xb7557bc52931324dd4c58d0e63c89a8dbdd2d00d0baf79d81d7a062aedd2de9dd743ea30fb031b18c806ba03175d7e1d", - "0x8e056b842a9af7aeb6c0d113a3acc8bfb5c6a8980fa81869747f75abef76b7fd20cb67694e70016e3de6e7821cde030b", - "0x966c00fd6472bb13ffa531d8eedc145ffb7733114e0f4a6a9fddb34ab7601f6cfb056460f757636230b692453d8b31d6", - "0xa25d85947c6939547fbee088e0131988053c5bb23aa2bd48ca764f4ef2b29235a817b8918d1de6865695977a95711e9d", - "0x958567f217ce7a6d74861777801663d7175eeeca8ff62e240582fb603ac91dc402331034fb4855632352df2328fe0233", - "0x85e53f3802a7d32dec2db84fad7f8c8fc856037cc0cd4ef9a8988e97ab580d4b929023f1fcde7633828b5e8bcdab08c7", - "0x878d1fbbedee7f7ff72eaa3848d7f6bc3cd13b40149278b3afe5e3621e6d1f0386f8ede32971d3f33be189c927bef6f7", - "0xb041e880e4ecb254f6f8d92635a1ef3be3d5d885c751f247bec2d8a016aada6a7fd2f7c599f458ee466886abe721bba9", - "0x920747dac9f35ba0b2670f82c762a71ee9bfb9e490825fb7ed613bf2548ef4ea00bc01e9d2c952dd9c56f3586a3ffb49", - "0x800005cefda1ddb860fd8974342fe315d227902dcb5f3736f8b9ad1fa2f8fbeff8c8ba0eb3f0c21a6706f288ef4bb13b", - "0x91f2b822b728fc5d1f15b69a303985bab14c08df5e929decbfa5aa5689f3cd93ccfe19ab10499d31df9d38c84039e492", - "0x957a909486abd85b1e627a4739c7d212cd03f2b62952045b704c415acdf2e6c0cc627af93f382836603f33d1a716ac7d", - "0x9733ec7a30ed833cc1e7e0ada4badddb1cd1908bcbd3d4e4694576421c94090a9297aacd7f42d9d305b87d711828304a", - "0xac2785a0dadfd246fe12b63f759e9f021006cff4f06b2b5a9986f0b02a40f29513feb1c9044af6e27d1c5029b1e1db35", - "0x948b22bddf55f4b4bc26892e83f70b882a0458582ed87fbbc81bbd037c946d833c19162327354240c42e05cfef55394b", - "0xa49c5d81544028d56f4caf8699477bcda589c65f6754dd40a487ef88d93925008dc7fefa6d458619d51a54b3edb5e5c4", - "0xac57b8ca2d0623f5c4137cada67afd6935fb75fd82567f2c57cb22e89a0562d3c0716d5e903fc06694a8c2edbc9a6f1c", - "0xad52af6a0cf838bbca5a97aec5d87fee1aec4fcf5e802b8bbad1b110c31ed777de0b0ebf74384bae68289af20e351bb3", - "0xb0c7c48d734e5a1b37674465eb07a629dbdf8f9080c44a578f3dd687261d9d1cc5cbdc084488c745c9114fd998bfefb2", - "0x8a2b2ccd4c52d15bf7aa4a8847b8015bd53f58ee484589339b4510ef08a27db56178c15b4d79a9c6eba1ac0b641eaa61", - "0x98f659a37bffd7a9b7759bb111412ea9e9eec483645511590f683064eaf15e1b101b5eac3b98f79ea38662b1956a06d2", - "0xaf6cda3fb2479b6f2d959f2d03e52b49afd12bdccd7a65a1bf6b91e345387924d5e355363f79bbe32a4624287cf4c1ac", - "0xa24d325d8c2dbf9d2e346e3504154018937efb74246ee0658e68d148d9ad0f4bfe348ea9bdca77d4467ea1b3dc2fae5f", - "0x81a729dad3798121027c29e9310d56e36a48c1c479cffe674cbf9131c562f541d7e6c52c2718025d3470b05b67cdd321", - "0x95bd5cd6d9895c775e58cd4296ebefa51ab9e324418208c3c4d073be59410497a4d0daddba6c1e7373abc08e13d32b89", - "0x809fa97a229b056def6b548902d8d90c873e496db6cb1b2d448709b9ae08d9b9762559666cd96b6bba396eebbab4ea4e", - "0x8bcae63cc680494606e44037a3bf6dc7bae2e723e5ec3ac0451550b8ca7914ee1d4bed0f40adc3dfa45f8f80a36c11a5", - "0xb3474711a0f933cf269e97e4e1e98762ddbbf49dd72e468f1e8a2f89514c1c35cb8db32d08dff50f93e50db43bed54f2", - "0x9788a37c3d95310627deec58ba6d9e0324618469275276632a3fa7841fb127c8fefc1b7392064f2eecb508056bd346c7", - "0x8d031fdb156023e185fe5fcac67b966baf9c098fddead4a6f1a3cef54d8e912d0de2d1e1d3f3f05da538eac4af5b6973", - "0xa5efe72b86a714dbbae40fa69fbccf41042e0647d177cd60275700257aa583708130a64b2f9dcacde4fb636b5cbd5aac", - "0x824092ea32eb7a8c619182d926f292cedce7ac3d3fc64f60d00fcd767649e1d6cffc20dd9c1d1c8ef6f64be315d1e2b3", - "0x900ad22d3b63376b1ac80c7343a58df23c03c4e7d6e5740dc10d8cdee793be07fec09cfbdf29e1d1c6484d3077630d6a", - "0x826815005550844ac5a6e831de0e25fadc49aff808cd601d70743d4873a341e3f0cd40d490422c87df3f3c40921fa723", - "0xb39d189aea740c52b03660c0abc8e796cab72193ed44a4b1f59fd1ec0e283ef7d5e157ed23741eaf289cf968597c0900", - "0x968ed61662d1e656e039900912ab61173b49d2e24aa6b7d3af07c3b04a2c89c4516540934aa543bb48ee14153780d10a", - "0xa433b8b689007ecae7f1df15d442b0570664a2db6318de66c6e5fd68884615778d296bd260ab7d07469bfb5f6d8c94ca", - "0xa69ed4a0f39920d1a62a01214daec143fb7596212e2439583df9ba508784ef4d2fe86334f0f9a6b7a3342ec0a72ef15f", - "0x96f260d9cd88757e7c45201d57bd816f1cfd37587ba17a64997bf7716ca1c2cfe16a7119c36acf1754231f303058a9cf", - "0xa51f2bb09d30028eeb0860e2de38094623e5b5514fd5d591a7d4a9731cd4b9c4c12c5dd6ef409e009dafb10d185d5346", - "0x8abe821036140ccb3ff9063dcb5e8b8724cff1cf0784b8f44486c8380fc51715cf55b443cc20870f426c4874be93caeb", - "0xacd73facb964d9012ad12405dc866beb52d8de3ef81fe966cfdb14d22a19bbd2e7ad3a29cf029617b9d6510ed323c6a2", - "0x8f18f6883c8e4741cd6c52e0d3335dd71b5571446ee28e8c27cb0625f77a9f5bd0720d960e5e8970257907f503d58a9a", - "0xb66457a91e7ddcf56c8ce4936a209c69ee53d71236b72ea386f7719c8b8c9b4ba4ea19039a8de17a0a869da427da42e7", - "0x80b1de58bb3ac5f264e0273061f485e49413de604b5ade73ef81bc249f5e89ce97dbec7d99b088b5a2ff65c0bb93fa76", - "0x8bdf276c88f80371ef0ef7e1224929681629aaebc8cba3c0db0b258be3c26cd17268f56369832f564b1679be33e98c69", - "0x943cf6fc88678816da42e4f337c730eb2dd59f8d738ea638a799e8b77214ad7e74723056bae96b100f9a972155885d26", - "0x91c8c1a8a61f47119005869c11edf0b69d0bcf40534b82e46aa96bb6107f940e582b6733f855144accb8dc21d79acc39", - "0x96ba98bd291faa0904ca0398d6c50eb5bc2ab5a389c359ca42b8909f41f4fc37dcedc370ece777d5035074a597da503e", - "0xb4598e6f889d319713a9896161a6c9bd8575ca30c21d3fdd37cff15dc0141ce28dc536f73957e6fc8f6185fc0adb731d", - "0xaf1ed593a0547c26ff729c159ef14bd0818f25e7c1c6c51ce8ce5425bd2526086eff9fa3341279daf82e480bfe431230", - "0x8c02b9ad3aebf156c80fec9b012241f3794d736adfbe4a272faf505ab818cb121ad2ad7c2eb1716e252d0a2e7ee6b246", - "0x8d2a8a31784c446eff4c2ed7b004009f08b86c87a4786a0b7be3df36ca9130a0ec42a58d09dfede1279a4a6d3d37b501", - "0xa78b61be13005b1718a3aa3deba103ce71e1ff73163c76815f9cbcc101d993f119ca128a25c51a12fa52f46550c4b609", - "0xb990d81d7aec9fc50d798eb8c38b40b162004f78730e9ed4a103faeea0995bb654893e557e5eee9b74046ddcaa70617b", - "0xad56d68777d0ed53d3331b0cfd44503b27435278416ac2268965d8ef711fdd819c16ef5499d8d7fddadd229c3d0d4bd6", - "0xb5110140b9ee542ec03c945cd6180ab1af205537704fd408fc4490d799d87a3f3aa0f1f0ae9c8daa55c1757f7bb53cbd", - "0xb7d8a4080c5eeb00be4540a00e65e744f4c7792b518c9fd2dbdd25abd0dd89e76563618cdb92e4cda0fe3ba4597508dd", - "0xa880b33af98cc0bd1065767a2600145e6e326c3cee25602dd22d531c26c4b8543f846fadf679e26749c929310779d858", - "0x941f124078990e03310cacd79e4a38667f4dac4dda4dfa3173a03c14aafbf538fdaa01b940fd5be770c1cde0a84bfefd", - "0xb234e9d0f04da6efc5aa5c77bf71cb05001cd193178fdd546e4ec81875830113d3d4f1512e7405b16d0b3aead285999d", - "0xb857bf6f62c4b19ca9441f700ea6676ffa3b0c7138299533ede59a9b9cf9b94295046e7eafcf1d4ecaf0341898ed6b15", - "0xa2b0d74f17d3387566bb4f17dfef214cdc6b61dc50698fbbe298a7e2f4a82d20aefd719c5e82bbf4ba4fee0e2e35b4c6", - "0xb5ffae433aafad3fd51ac137976e2c22661d8a286b221e0107baf19f5c8f2f6c7eac1e785f145bf7c16a497310fbf51d", - "0xa69e9dfb14f8c6cda716166cb69f06879718656c9f46730d94f194e2888fec371a11c9701071bf8690e57996fa91d213", - "0xa1f51ecd5c5d73155013dcf02b327cdbae9f9c2fbc62f73959050cd3a0bd66192213d1f4bb56a85cd21343722ff3f57c", - "0xab3e54b8f4829f1115694a4be7d16e8214c94387ae783263cfe145f965705d51943176191c706f6211c8be2699dc79a9", - "0x8cd6a64c5d30149ca4dae4fb7e8974dce1200aba9be9c8cf9af5d43e40098746ecff7bcde7ff84a0072138dcd04c2771", - "0xa52f6fe24305bcff685f2d047c9a8d9a1f70c2b416cfe55fc137c6b5b185029f3644890418873665712dba4886e3fc07", - "0xb2e8e3d2ba2d64815bafb678dfc1180534186eca415bd8cd32b303bbac6cfb637b17200aa7cacb953e656ad19dd5c9b4", - "0xb5412d1073b3b80bf0d18f791a6d02727cd9c40a86ab0f13ccfd847bf4e767b8b79aba3935194398da2c9cf17c6bfc8a", - "0x8bbaee84aca9089585d5ff385dc2ee6e653d0fcb9088f98bc5fb1c6c83db026d9a73d77c33c6cae3268be90805d862fa", - "0x9691343d1a8b7fcebefe2584b28ab5808764ed79d8f28168a65ca90b0094e7920fa43e56b960331209f4b8465cb8a5bd", - "0x8ea475e12088d558f5cf6dea2da71837791093a622d5cbee608a95b4c8a61296255c1545d454562e371ea0e2cb2e0b1f", - "0x951d6b404667ccca099d01328562790d1e8446231d7d22bc2b1c4c6b15785bf59f2099accc58817a06d24d59ff4e6a2f", - "0xa5d012687f341eb9c783c1c2040388eb7ad662cfb2b84cd94d270bcc99116939aea80440d7ab726f9abcad22fcd90274", - "0x818fb57b7a8cc59f06af054ce09dfef812f8f115eb2473d06c8f20fc50cf17423331aae1f843bcae57fe4e2926ad5aaa", - "0xaad27bde8eaa2e7fb1a9a5ab531eb41f475afdc89b7f02085f7289f8f84d172fe516d0104560a40c89e97db7e5e836ee", - "0xb8cd923efac1b09d9c6b1d97a0c1bce9fe4eba1d872eaa3c0df34dcff2e7ea2354f1b31b69c6b266944ec8cae2a16865", - "0xaf628e772d609224aa7cd3eddbbfe965fdae6a05cf6d14959c5c26c4806043afd5fef803091bec710c6854ec095ba18e", - "0xb662e1d32704d96919f5dbefc3cc40e7d41d4124c5865b46408c2ee5c40926eed71fa3df71fa7ad349d476d9a525d7fc", - "0xae4c5512396f9c26381394ff8e22b1d6267e3d3a5d3fe48457450694520c5e173716572b68fc1dc352761991abd262b4", - "0x86b530978a7e549e6ca014168fa4aeda9438bcd3a29f7edb1f4e9c712c16faa58b81b028c25a8e90b421b61a1766d7d7", - "0x97b32f1371f76dac7a449295365780d1bd03f290a041b3d19db3f14bee6365a614ca356e7cbd6f966260420b451f6117", - "0x8be97569ea08d0b6c4d46e9565ae14f79d1990f192a26ec935a865cedd6bb5f69f608b069f7d645877c5081fb4a68c54", - "0x9733488f48de05f57f623752b80b37c91f6c9afc0f9b4df4cf400f3f82b137bdf06fee82190f2a4ad4aad20e964cc214", - "0xa794f6dbf155666056529748a792be13011bee6ca10e0d55c82c3e84c5dfa1f370c8e8abf2971a75c73a4ddef3da3319", - "0x95ff5d16c0d9bd679738257a1f7f309f257c20469f2fa41bcfadc671ad65acb249793defab591f515bb3d8072e2e05f3", - "0x8d849150bf8dc3452839256ec4eb65cc9ef87aa0f90dfea4d1d486f16ee855d6c480a8fa4b6cf8d536e435f9fb7bf507", - "0xb61c29121dca2bbc6024ad2f487bb57b926786ae60a9e7a721440787752432ba9c7e1df86ef0d74c2592d23f0e89326e", - "0x819630a678e4a5e6adbde9b292f5c8f2b6e3f2ecc9bcec60ba0f8502e503f697b0ded4f0f7157b60ddc976ded66713aa", - "0xb3525b071e26babf669ae2b98319b3516c083e797d74bd5b9b0e1f67792a2e8ab2c60921812690b5928de66290ff7b86", - "0xa344c6670718b9824ae62b309813bd31984eefb5efee38052cd06812308edcc39fdee165f8164629267bc0e98fb50ba6", - "0x81d78d54738817dadee7bf70a468a51728de0e9775f8779fea5d0d95e55b2004377b4e2db595d420f017af18a384d9aa", - "0x848c97b9413ba6ede751ece925ba57b8f8ae27168c5d46347d39e0232a5eb42069a85f1ee2d30d8b94fde574642be5d1", - "0xb020048c5a5a2d186df628550c6f61a204f16e6eb991283e975de520c4f916badc999b3b7e9402ccc39db5c0b510e2d4", - "0x9298c1aec9664ab3fe328f532428e9b037efe8412ccfdd15e33c9c82dc3631e49f84e0d2d75dced85e3a4e0fd0f3f2dc", - "0x8c4a78841f51e2f8b91defb0a3844933999f9884e2b324bd89a01e482756758b1b5a278289163409947c73106bf934f7", - "0xb328a9db915c4bea1783218c7668e2bd7a8fa62e52d3a005a43590041d34ff388c0555b044ec5ff85368352a3643b7eb", - "0x8a395d89469d374c1ec472c4d571ae670849549d05124907faae5a086519686683c1773d22d290ebdcfb8dab926d10b5", - "0xaec52b8a883f4ff68fa5f418cc91c8e9d08ef324544356b0ac56a7f0980fab6b376b8f567e357ba96b408652b8e568ed", - "0xaf80f0c5d50ab23d8ad99c7fba504f2f02b7307b5ae5ff529142bead28d69c3d55f4e2226c44549548fdf761ce30cff2", - "0xaf73700803caf7b06f5453a620253731de33a458da01f5790106e9418fb59e7aecf6fc1d1b650e1c7b3456f7a55d4301", - "0x8be3ee3caa86cbe21ce9998fe1c0de73ba6481125ef28e312002377676317b5ac4c27180086fb83794efbf86438ad27e", - "0xa0439d051d06a7fbd5ab83f32f0f95364bc043d9d934ac64df7031223e731d7992206879d567e77f35badcb7623f03fc", - "0xb99de1a16670fbbe3ec26ccd37399e2a23c96813c26428deda4f74dd3afdbd28cbe47e074379f6094b85176f8ab349fc", - "0x8a943a039aa33f38d3887de4e77566d446e87225bb8333e3ea991466c15c6487077c6decb9cc10e5de6af03e6b81a10f", - "0x80b109fb49ab810121fd411e4cb85773a1004af2d257e85ab5b4c99aad8d66e5803a8ca7b95587197e88abaaef0b8d42", - "0x892148bd190b042fe9b7914b8aab073c0d19001158087077a5946690dd60d99a1ef372ac01e372a434d00b0568a75fd7", - "0xa266dcc9ccbda054e396e1605eabde6cf79a028b697898090e9f34a4a4e0b771c121b8d470b14130a79cebc19f8d6e58", - "0xb1ab30b97c76392712b173460c227247cac50597c036f674361c63c3638a4c03420fa5b7efdacd0496a9b83956cf5d06", - "0x8a33c46084f669455ba089b369b9c8493a97c131f09c66f9347873504f35d6b94a09483b2775656ab32a12c7b9766ab1", - "0xb77a7c1402edd9ae448b7a606ba2eed192a9bb8f852b647b6ed689b0a3ccb81a4632edbca4c113750f62643a0626e2a2", - "0x8586e85e3bb07b07a39ecbd822d2adbfbf1fc66cf2377fbe6b1bc38369f86292c6cfdb5b405a0bc4d584c0600178321f", - "0x80cfe5b1b032d5a28662d13772fe112e9b73c997f8ef0fc796576bb39e02189c3ec0228d192c981061dcccb9dd3c4f39", - "0x873c085029b900d1fcbe93f8789d635e3a8fa558766701ba9fee76dcf05abb6cef518f2b56c4ca5e26f3847cf23bfd72", - "0xae8075937a23505f51a1a26f7f54e35caadff44ffc43465368daa9c330b553cb4548adbdb04e24c3977e35a08841c36a", - "0xb1c7076afec527912f7648bedef633ea0e3b02e5fc3fc495779b93e8a9f64eb503f46a1372c8dcd8fc2572c198112da2", - "0xb5233c4545bae360b07c4411776218a1d9040bad1e788e099f90149c58258ecdf01dbf246ddea48ac8fc2dcde6f34f20", - "0xb62655a8376ce1ca225dba04cb29f1a95d09e1a20b58f0330c478c6acf931ae52268779d6cab87d9074a362b9e82b205", - "0x9684e676088b409052773bb740bd3577bf0dc15d0392ea792393a158e643b165f8cbdd91cf355d5425682c77f2a91f34", - "0xa892744cc0c428c97bc929913ada86c36f280f49bd1603e21bf6b6abf8ed195cb05b22e586f0c841ee02f234731985cd", - "0xa62c089a73c6dcf3f7d957719c7d452962ee851d6ed8c4b57ade8a1e57156762c348fe5f20adf6d6ce47b4e98f93d60d", - "0x91b29be6022d43937df9c597d19e23cbb83cb6f5b764e1f7db6cf60dd9b3e9c79f1f617c3101c60fe6c7af9b5719fd5d", - "0x91d13fe99d7dd7b4744fa2fde41bb51f4edbefb2189ef3ca5d337ee84ca3f728e300aec19b96dee18aec090669c85400", - "0xb17a5328808ca929b794dbf0bf3a3fc318f8df144a892ec0ac2163a0f7c3a4614d7ec433b66bc491c05a286fe986d986", - "0x84a9e84bbecfc2aaf8bd623d79bd4240c630b81ecd55a50198de21758255207238179a345700e65d9bc6eec1a1a1985a", - "0x8d428be451efbe630740449ab3677ce6f69d94d75c5a9d91d14b2493a838260d6418be3d4658fd15218eabe3adfe455d", - "0xaf11126224f6ff0e88a09dbc0de6db3c70e3db3f6e154deb448d044100f989ea41c6c0259a8ecefdcf531f892a957d82", - "0xa51716b900a00277aa932bb03fb61eab3bd8e74edfad6153a06f85aece6f832af710f1477d883dd8e48931deca12bae9", - "0x9542a82039c2d3c538f15da884f090622c5af556c16370d21bdd6544208cb17e0a30e511b0af4a618e8ef70d0c43af07", - "0xaf76f93250bd7bda2b5e30e6f88416ef6fc8ce0cb614515a1f8d81dec717077209493cb47b79e8b1a62e09e394038338", - "0x8fa8d657f1d584b06d5bf41a52bc2c73853e4092290789df04eb8952c6eb236df601a1c6cc81de420a424d8e748dfc38", - "0xa6e93e27421b9e32b170d240b4cf2710c97b49dabfc0ea25759c5f61937eb3da8d45a6400f8bcfbb42bc9a7ae9b66ef1", - "0x81848c8d66d34d274b21dfc10bb36fb9497a1b152aad64a8f7c874e58d31d5dd5f39e30e6000b6d7b444e573da1e043f", - "0xb85692a84154f87869d97cb5f16c81fb0559e097fc37473bb11dc9cbd311ab91a46b01aa9debcada590992c2473ef0fe", - "0xb565371692ab0f0d899d8139d3eaacd213e7d23d6f5df6ac3409c961aca019ce861fb4ca8317f462be01e8c1dc7af154", - "0x82ae2bda0228d36343f6153fbc41fc5c79fafbc03c99a7926c624dfa28ed0a1d215e11ab83cfd438fe5d85d7fee50363", - "0x923f38a2f839e165fd197e1711ad52673deed9774e0590ff63ff9a9985f99612aabe003b9a98db2407c2878abc6d9b0a", - "0xaf8d5e1048de3b813308544705eeb0facbd604a0ed03e66c1d221be64cad35d71748d2a55d1ff3049e1e5053c7b1f712", - "0xa90a4b3b9d3b7c87c34f85c7643fd67dc771caa940c9e2ea81294ce6c072eaed698368a0e8056d7b819ce3d73de4424e", - "0x93a106e914d2c6892fee866602edfbf8d03dea1918d82d511e528b99c8423c260c0d103bfaf9992e0e24638b913af737", - "0x864cb44b1adf5a59ce7baeda0ddec3a0ecedd42923205dfabf30dcdb216a7b760d8895dedab52ee09bb09e999486b521", - "0xacb5f2bc1257c49c7df89837502e699bcb9652567c1716513f258f021755092954f2dc65b9766ffd9a10584bba424c7c", - "0x86653b3a479bf6e10e781e316e61437af1abc988f59399bed8fb4ff128f5f6d53f50a293da58774acd42b8d342e52429", - "0x926b7b90eb7d81fdad2a8a59e13b1573970e15c10515954b7c232c37955755b6758178314439ee6c3b0c881d4092c838", - "0xac05f011011a354f0e16fbbfb7e9dff03b3cf403dcc449eb5c71067128e314badf4d4dc5dca4b8616994ecdb15909c93", - "0x8e063c6601e553f33abc64f9553db5a19ea794a1f254d5a5f7b8ff2db4ed9d180f68ec919a0f83142c5710813baef4a7", - "0xb6e891dd4d44fd54120b7b8716292c27d4bc8744d96253a841433cf4b07895606db4a3cc5872c480616b863073bf77e1", - "0x8dc623d7928234bfbb8cd0b4fce5c8d9a9db848ab0af967ba9c49daffdf719cf8b55e1dad0b7e421571b8770cdfe9df0", - "0xb5b53f7d6b5d1af75e5a1720281feefb8c9039ef7f1e1969d83bed5a2f73cfbca91dbf4fb8179d9b0d3bd06d1207089b", - "0xa5dbce9e6db637e053b4b4d3df07b724b50d11eacd3327ddfc5aa8f37b9a5bf628cc9b428328e16cacc552c1dba505c9", - "0xacb82d6c9af9af0dd426a07b1aec81b388b61042bd601546cde248730ef85a09016bdc66dd014447fbb56fdcc23011a7", - "0xa41692e96f1d775b3a9378b3634495a8350dcfa52b4b2b7773b39d36f7d349fd5ee9a2b3e72769ca98f2319319890216", - "0xa0b4bd6a68ac5735539cbbdd78ee4faaef7d6488eb7a11e091d94e315cfcc49a90f204f636dd8033857378ddd67cc153", - "0xac3dab32427b0583159482f73f94236980d69f9f8f781b93f44aeb43dbeaa740c77898c38c57677b42c248b9bbb1d673", - "0xa6cd1090b97826486f59a056ed90cde29f2ed821211391f2f16e66f1e8914398348cf6f0df6d3acaadab31f0382bb5bb", - "0xabd1252b722aa56010e3bd4119f2a28a852e9ac1a8ce68c96b6da9d00fac0c9fa70e67cd4afd45e0a8042a810b8e0a91", - "0x9194b629ca80b3bfefc0144553017343d0915aab59faa3d0e2bb3720dd3c8fe07804be6e582c6d57c764be96cd40f2c9", - "0xb6bece03ae1c5935eb38b14f0f64d9d0b4410c02ac309e085a233c74bc3e67ce63edea56ea37f4532e8b864aecacadd0", - "0xb753eb9184f5b30e77bcb5d3487323e0f1178f1ab3e15130953381272209a97c3e8084e810dcebf1ea7b22f0a90b9c77", - "0x87dd4a76955bc98326823cffd653fb7b7eda5df1a971b72ec2a4d25fb3958b9d6092369539361069e7e4f1dc9343d929", - "0xb0f1e8b25a2687d98cc037272473b4e3f33cc8d49a3c83a335d48b8a0d3ca5f84e8e2bde304ade6f7d50e5f3539d639b", - "0xafce1c0205adad1ce52fcca71a99cd6df9da5b748209c2ed1013b5b7d484b937bfbb26db9e9f8e77c081e0a0384114b4", - "0xb363d31209c075b94441d1a8ddcc6bcf9eaee78f8adbf0992d5c7e7d638a02d58e19203247443c35d700fc8ac8a1b7ef", - "0xa0aac7dbb08a10f6cc2c6a4d37aea6bc3dc034d933f49de3dcc79bc0b7a011b1e27df7cb882d155287436b72092a1da7", - "0x86dde01fb7090c80fb404afdc9ec64ac40909b64c4e16460a4c862c3a3f857ebfc0c3122250306c266cb1e9f9f245934", - "0x8b3ebbbb0ccc466c72afb4c27ad99d2d4e98b5aee9c05bc283ea3332e5f67a3d9263b71d16b20db31ad4d8f48174b0d7", - "0x8610c492ce130e76c06b7e0204617087ebd8f285cc3f007897c253a0e1af50f80a825ea7fa3167f882e146402fd342b7", - "0xb17f04a257d3142005b8517dfb57d28661604eea9050ce39c60ba9a05d23897114c59c55af199ed186034e456e111cb2", - "0xa04cd806847686ffe023db1721fffbc26160582c239d5bdef08f4730e2fbb64c341fbabf1fd831af6eb84a113ad7e2f7", - "0x879018340deed1fc762e1b8f3a8b78a40539d6f917215621b725c0a3aa347eeff60765e5ad6f4a36bbea51ab77f88726", - "0xb421e65891dd0c6641e8ddf479b065163897a418d723fc6dce19046501e01c616bd19c9d5fd6b395e65abe0ef956d53b", - "0x89350af1d432a8c209b69f937d2aa20a24d5eb95c5b4cec097ca3dbbb3ea9efcde2a8c56c58f8d7901b96a627c45df9e", - "0xa32d6b31cc9efbad4bcffd8b0ffa46b8fa97ddf3453ed151d7de1d03a02cf233f07415584893155d2d7e14b9534921d1", - "0x8efad47caa32277eb04137c92def618e0715c1e77b5053b0cdd60fa03256fa1c9fba9aa86fdf1c04cda9c5450863d58f", - "0x8dff9d309f7294ba750158e70474c978d1dd98739df654945f5f29fedc607caa06b2866c93a0c7b830ff9b26955833a6", - "0x84bb00fbaa4358a2563abf96d2434e4a26acda87b189cd8d2aabde1323dc1eb2eefcdaba3b90e9ad1215ee469745b72e", - "0xb75acb924159ecdcf49df587d5ac1b1b04291600a4f530fb7cb6231e7fd1029f8cfc957c891a59182518480c2947f472", - "0x8d2c671ad0d442664a0cf267b39be742b1d3760935137e4c50195695bdb99254c4a4d5830634059d96dfb2b4e214b067", - "0xac27b31843caa8140e729a01e7d0229d7c221feccc89ffc173c11a419af3db0f8a47a41cac358e15ef41f679a3f0b96b", - "0xb0b3e33c590bc00faeb83f4b160748fea4fad3e21dfa324bc14a138ee8c5e18743b6bb27cd0ad7c7c28c2b3e92040b0e", - "0xb0d2882c5a0a21fe05b522f2e8a4f43a402bfc961874deec962a1e3d039e411d43bd3d95a26d930c2509aec8ed69e2e0", - "0xaded1e47b3ea6ea7276736fbd1896297b9ead21dc634d68ee56c20fae3da90170f30ad0642be10929ecfe7de5ad8ce5e", - "0xaefe525c0dd24d6c0a66b43ebc6403ac75bfc322d1a22f76340948cf3536d2ae87290ca80acd3e55d2df9aaf0fe6bfcf", - "0x979d1510d3271ff1f06d9cefe30badaece436fae8de70b01ac843850f678aa5f48821dea48ce1c363fa35eec37283f3e", - "0xb8e8d10692f1bad943052fc366291c134a0fc7ca4696feb216aed46eb32de7333a9ba4f553389e7e58c8fa96ba023f58", - "0x913353bc585c0248a54d4705b5e29cc778f304472446eb4baaf30bafa30f2ad0643aaf21196a6c4d177b11eb4e2ad5b2", - "0xb25a0e3b9f983c47b8faaae8549fa7d00d12d7145e1b232d1813ff94058ed603957a340beff25711075cefacde767661", - "0x8515151729ce9a7984af3b94f74880a2402ff853b99f924d581fd3935d8ecfc80e2a1185918a5b1c4902106bd1561ff8", - "0x88e4282ded5e2163874f6464236d5bdcc3c484a0fef8ed0da8d0177973e8175432b75afcde2a4d7d6aefeaed52fbeaa7", - "0x81c31113f2a5ff37250f395d6722a43cebe2a267a0ee40ac06faccaffd7d6eb522103f0431a325aa46a54e606b14de84", - "0x9302ade30ccd62a803b9610a397661298941a644b1ee9d293c63a6c3368fa3557dcf6bfd0c9b44c5c2a6be06d1baf663", - "0xb4ff9f1f6a2a64c50b0a16980ca7cdcc297c6f93e11c580019de52f58381fd0f60a66d3e010fa7ab56bdd250e7b2df2b", - "0x8e57eb61ed3c919dfa0f0cbca2cf559cbede5bbb1e89ae4849b380339cb1c567c98fc2c671211fff4df1a058d46a42bc", - "0xb3d5b45b4096eb088523d16bda1c6aacda01473533314961e6a8de36ccfb35d4b717eeb1ee1bce47ad3b80e9e5084d4e", - "0xb933ff4d3c5a77cd7cd32926266d4f05198178ce350f7215e512e71b07177ac1ff89ba183e424138e1fbf088ecf86c24", - "0x8cf430a6e4eafd23bcb5ec8ca3d711bb56ae719c8621ecee964ef3bae7c53044f7ab3d5d0b911e09c7543e56c1e82e11", - "0x8b3c34f5321c9ed48024196e1e941fb7a5975a045a5a9de88d7f200fc7ffaa0b3e500ab7b535e02bc5c35fbe420e2c3a", - "0xb3c235b65fbdd5c4c2aa45271b9e51674f9a0383a8ac383b0de53125a67c87261540a95b8f81ffe67ecdbf3955b13814", - "0xaaa93ce79ed6e7084fe906c9a1002435ed6829ee3d1380681b902d35dc9e5a23a214ae12dd4fb76691b0016f28d43651", - "0xb4c9533e50ec58f75ea82e2aa7f735c4257bdc1ecd0da0b6521d1442fa61f19e4f73cc90972b47a762f5cd9af591d957", - "0xae0255dd70befe7eb979d41f9a7407040937e7a879daa64353c66d524d3d3cf1d5e854886a6c32c142f4673c56a4df1d", - "0x805fc5ea840d1c2e6b35ce586309698530f056b41de7a403d9e7d81efc2d7068976e8e23bc0b9ee256f39b15bc4f7ecd", - "0xa8de5055b6d2310b6ccb211a397077b211683b05c7e68e55ff05b546c5c81522e6097a3c3b4b4c21fe06667071beaa4c", - "0xa4014d39b23c13efb4326956c5ee476b1d474663950c9e3e45aa1345037be862cfa14aa1d03bb388085bdb4ba9d70a59", - "0xaebe9a9ba34d6cd3692a8bc0b0aff5648e16b36d6c123e636e9260386642e29d52ba71ef7778481c1b1cfeca7fe6acba", - "0xb59706380c9271918ee16a04e84e91046caf99623a0120aeb37a7a98d4c954d3d880960086de6cb180c8b922ca1d7405", - "0x8dc0713371808850f2137a89c33fd55ec2df6a028e22b2679e09f7084d5c471451187f6488fbd9b5100b84593540e5f3", - "0xb492c55e470c35c7a7efa536f3e7c1e586b623c6669ba6eceeebaa1f81fe3b8b927c2e522fb12e603ae246d9566e4d23", - "0xa5148eadcedab9ae08f5db6265326fa415aef46d0b24155910210338500be6d77bc9fa6f6e284a4c2552dac09167e450", - "0xa0af7b66c8a1319ffbe7a0180795b442cffde153f9a871046d6bdef959378c3068813c516e280371825af06ef2320b15", - "0x95479ffc4903f252fe58632e833d63d963469e89744d5c91315d38eca21b98f1ad6fb3ca77d620a6f97d9ca3aefa1f7e", - "0x84861bdb5880f663a5d9b5e89b59a940611a233d82a9895a330464f7e9b7a6965c2420704f3adc61f876584d06771f03", - "0x933c374f176381a3a63fa98d238d3b7d337aa745528e271168f5b550fb703664c3128097b927b5922b4ae8fad30d5e40", - "0xa3ed2c5080c52ad1235fd93c9bbf128b48ba8abe364247104bbf298582930bf3faaa4f4b6103062a4696e68c44f79555", - "0x94668bae91eccfa8ad459588f927bd1a169af834a76132b2f2d5cda26a91094cb94661e3c59f7547b290f827eb43125f", - "0xb704404a487a7dce87ea8207dd5d813378a345375e8e2c07de349c1448a39af8672bb4436779b3485adc46df2212f409", - "0x9347dacaf6dd678574a4f1a95df79369e3f5543c565b1580f907ecfd17b5d6e1ee3322d83601cbbc6d6ffe0bd2833a83", - "0x92841abd813bd9934bfe945e428193e33ae6d4dd235a16edfecd6e4184abefb8a1f85015ee83caf9532dda380fd678b6", - "0x95c14a1d3a1e1ea18f8a61f34b85ee8a794c95d3b4b0ce6ffc89013c9a80291a9a2487b00bb3de51ca2e4290fead7482", - "0x962fb52a2134123ca31d91027fe9fb62dff4e0542c66b55899a163e50f6ff2c4c4b9c1f5b5b3d6c6dbda40e757c0bd3a", - "0x8aa06ae95b0ff361dea2792e465436d810b86f803ba6121ff93fddd9ba60ce47e846eb2d248b28f2c47bccc9457c1ece", - "0x81adde02ddc49b6cc89561716a839fdee2879c78d1ea0fc0418a6cd4a2a8189a2bc245bf2d1e535dde07e93b8a5e18c0", - "0xa7a5713055455728d6d982a6650d1edf1a3b4612c9072ee8ee0bdaa3992963a6fe91ca242fe36f839595d09f6a47aaa5", - "0x93900cefff6f918dfb12ccbb256adec89fb8da019324b811398eea03f6fd34f28a6eac2ce5580904cdb289879bd4b4d1", - "0x820262cbf7864213e768b5a38f39d27dcfa7baa5abca557ab575b07c33917f7b0f06f0a6abd81222fe8a5a69d95d774f", - "0xa33114d4cc3cc84258fdf252e754c8bb1feb6a130785d35a78b4b05d0f782424a5ce0f34be3c1a14e3bb1bc0246bf0b6", - "0xb966ca0a11f0361e611ab2a8907f78a3d639980cae405d380f3a080125c734059acb08431a42ef3a60ae9331a07e6a5b", - "0x9305d107311654ee727182a1683f322a78fc637bc214eae311f8778773e5bc52063bb0a902a5a962a4a26fa0cba3b06c", - "0xb3dc808231c75e681aa2bc4358c41f01e371bfa5bd504e7bd2282e35e72a2889a51747cc008dd4d8b2a070c8e4c2d7a5", - "0x8f05cc76848367abf2020461b6bcc1ecc412ae9f114d44715875f25f34d8cd26b84b01fd6c0640648676b8d2d5120814", - "0x8798c23f0ca8a7b23152ce17086f31e2a80533067f15ab4a7b43c127a5d8af3297115a3cd7978ace811fcc38805abccb", - "0x99a917f54253059a45103e15e92e1bbdb03e80584a85b658f231aa5947b29388f5b04588de1ed6de998741d360015313", - "0x8b0ce3f6afb5aa83ff229ae1ee0f986ec4d31b113070c7ef3c1ca3613d74e3f23cc1cc81957bddc709a5c5bd85cc64f1", - "0x9035b71e4cbdc7c410fc03a85543aed34a1c0a98e07ddc933e64886f1796416ff3a0f7754b5246ec93d617aad0f53d5d", - "0x87478f69c45394f94c67b7196f60aca42823ad92ea86a427d705576fa6a9bead40b1a4106767b8a20411e757f8762b68", - "0xb36901adf577f159b4263821a48fc5888e7bbd6c9f3857968a9cd02e1a1a788c08a566b7bd5bb6be294fa5ab92b4ff6f", - "0x8a738b1392aecb35a5a1f12920522997c9016a0455354e41d2e1b81d8ec9b30a90f71492c7bc122505b2ecb0654545ec", - "0xa5a422515f17f2bf4b9b6c4b5b94df27ce80826cc3ad2a8579eb6666c67a96355e60bf227b36e1f082d749ade7a38a92", - "0xb6d0e36a98e0518b14728bfd79db76c408f58220111e8c4dbf5bcfbd7a85bc68022456196f07b9f40158037a3c3eb90b", - "0x82ad91b812d08bfa815a93b47bd3656b493853bad52656450eb408fc915e430192ae123fb9daf4aeef4608800e818b74", - "0xb8ae5b30118dda7b972464e14a96853147c4b362e9cde22130250447575c0d8d05053202db4c650467dc16330cb54b36", - "0x835d913a3d15ff205497b98107eca77058beebe1aa35ffc20241bbc2a9b4d2019ba41fa3c9b43fe2265a1110b5c2fbe7", - "0xa283d88acbddb50983356f2aed99c2f153b6a8f489b0597d8db08ff7e3b04392609e01aceb37fe985f59773327258195", - "0xb6927dc3318931eac59c6e21def3ca79154beeaa4c57e11ec1f3362aeb33445366dae770e533aaf33c273eaa4f54275e", - "0xa6033a62119e077b438e0170f27835597e21c1d6e4acbd53fec7df69bd1372148f90966732fc5c004857cdd44b8a03c2", - "0xacc764a116e31d63f534b3e0e42a3f899d817d3ec32fb4504045bce7ba3a952ddc81a33d48c5b0499eacbef4268bd5ae", - "0xaf5d1f6a67dc6361e19f222a24163be388033a3fd0d33ad204f4411302668436f933c4a91c6472fd4262397417e3c588", - "0xa2b1fe93eb481d4fec6fccbd64945a12cfeca85aa8b8bbadc4e4ecab2f3ef65616294dc168d6c955744b7c6acd712012", - "0xacb6d3e123572ec20d0ecceaf4916401874f0298218b36a0ce089acef90329204611968c7397c2a518c0a78d02a9285e", - "0x88e4457b1c9b56957b76a08e98c569fb588b081e0e420a0d859b70296f648a8d64ff35ca61a39d1b8ac3613ea5fdc2eb", - "0xa7d1643b3bbef49b2f9fff326061cc27a7f65228e40929562de73e1c66a9d164d42bfcc3dae9103b2acf27606f18b031", - "0xa66e3b97efb7ce4e81534453d3d41ecd4b5b6e9bb829b07b5afbf11fc6ea30382a0059c33c97afd906656ec19432830d", - "0xae9a17d0044abbf3e6aa2e388a986754d6b0fa35d115e410f69ad4aa114db1af5dd0389222b838cfd859d436aded1b5c", - "0xa4a66a163365528b08333f15c6673ca48d7a9b6d17822f1e5390fecad122bcf7ec5656eed2f22fbc6ccb6dd96ee260f3", - "0xb7dd42c938c2ec50c3b3fde92ff629a571e46f8ce128fde7c2d8f18796ba1b1d7eaf7337212f55cf5cfc540c7d2dbf31", - "0xa36bcad22f3408b3bfd45d272f3387cdfbff57e014226dcd1db54bf3f8d1d896fc4fd16640b5d1484c9567ab9322a37d", - "0x8c9831fd5f74ffac203aa6b6ce03acfde8a2fd939b79236a01931d28b424fd8f6b6e44522d28e086aa12f0b110e5688c", - "0xb48bc95abd331d901610335299580ecec02a263d2b03bb0579cae3aa87ebf5e93dd110e7fa4306d31974099fe6e8f58b", - "0xa15e27a87bcd8ba69ebfb6228c3c48e19d79b22978d3a63af553b3083ad13e48dca496896cec195e63b8a4e2c40cae7e", - "0x96f3de6fa492dd2d653888311bc918ab832d6342dc7af9155bc7070004e89ca940b7672dce0a1b4976a7c3018f13e49b", - "0x81a022bee3593997f556ea53e2ee484188cfba0be4b831ccc048ae4b5a6df9e3b4d57c436caae5cba92486abb71813b0", - "0xb9d8e46df67e481c84d5520a9613aa92750c8e8a1e8f376b8ad7b71a3ebd95d2d191ce538e6f7fde3ac5943f70c670a9", - "0x8f0b52365d944f59d2ed445b6ecc4f88c687fd281c1913912c8e6075c3a59667060b42f2c1589a5955e9f3791e23aa02", - "0xad07429bab813045fd909b928ba4eaf262b6ea40b353aa43157e1e683b2752c5bf19eea7ab6ebb8daa8ee91241fbe84f", - "0xb90a99ec1f31c43060ef649e047bf24f2fa7fa9daf904136c6a5846d9479966b54090ded7093e481c52d872c6377eb65", - "0x8cb05fab3ee23db24c9bac109db52895b200dd115209bfa41fde510b29d9871907d44f689fa0f5474d12314a711f6fa4", - "0xb00d8f280ee21866b01ba3de3bf943a7d0825ed67db03d13a0b69f54a4ab389df1cb10909e831ec0af8f1675fa7dc399", - "0xb383d14fdc47df80be46390420603e7f505052b1a44ebf595354726f2b487f7f18d4243709d347e1e584c28167a0e988", - "0xaa951f60d1e069304222a8eb0338a94c8b3b4515d7cee833864b6c222ad76f6c48e0346c5603c35a3b52edb6f9381911", - "0xb887070ecae2884109eed80ff9341f5fc514d59158f5dc755ea46ba396f6783b8a86ffd2fae4419cec2ed57f4dfd4327", - "0xb1a6f1e4d25f4aade76714e52bc426beaa7592b975f07d0a6b372a3f94e7a3ab0e8829575bccc953195ba0c9bf46e68c", - "0xaa64bc4e0d9502d294f0d3e6a1400dc38f28e87c85d3429ab3575c821e1229f1dc8e2c13f03080006bc897e8fc3555c8", - "0x8f215476d94bc2af7d2e0eb68783292e314c9a4f812f3065cf064f427aae165990dc9665011af502f5713f3664317989", - "0xa578c8991e9e29bf3ad7be44bce3817e1c4af3e4a8ba3d82643378da78538787f581b9caea7602b87619e5f8cfb337fc", - "0xabe5453b650106cf65bf2b7faf8ff973b7b3be0e6f42983daaa5359dd4ca225edb7228bcca3d71bcb8d77241b320fa90", - "0xb7ed1d027dfa91d0ca5d797295e359bdb1b0221b1f5eabd2ef76ea3bf456f9aa9788dd00ea24fe0add9e3d9b09ae2428", - "0x96ba0f0c5ac0eae3f0031f8b7a87543ac369c22122681cade0ea33a6ca370cafd360ea6b80758476ab94cb07ad6820e6", - "0x966f6191951b998202b8a63e3b10ece69616b989e9695cda84a450cb953acaf9c4f902200b7492eb66cb9ae0cdc8ecf0", - "0x8d7bf21f76ca0e3b3758c293e66e977f83533d918dc445a09f4f38975ccf7220855627de6460d318290daa03a5f5c68f", - "0xb10dcd91d6602852783bb76b0a286523a0942e8eaaca4e0ee5bc76cf19d33bc631f6d0fda1c1ca51bb3d5d5c7dd43728", - "0x884d502d934e2b045357e981506900849e6eb051ca3ecf3079b485b348372496db97da384f8d2b5a52216b4d223c90ea", - "0xb074162e5d33171477ed48f2f185b1c83e8fc2e7906681f96ed97da8ee86be7476d65e61648383c2766ad9853ead35b5", - "0x90bd3d8b475da20c6e32324e30bab475f2059cd81fa67840a6c831026cf3d5806496a3a25192128da4b819c1b7cd6bd8", - "0x8da4889258cd6ffdf1608af8325230f74abe6a2a511872c2dd10123b491cb09407fb979d80fb1185ebedf421ba22d0fc", - "0x96fe1d9137c24fba18b1ac431ccffc01ef1792623bc334ec362751b7bac73c4d4f7e9bdc2d595ad4731c71808adea15e", - "0xac816ee0b9103f0bbdb50cc05f9c5c8f7ff2f14bb827541c51ae5d788f979c00fe4796b86eb9e3ba5d810925c1f34a17", - "0xb231e98ecb3a534dfda5b40916fd4fda270e316399c9d514dd510f0602cbc29e51c5ed60107b73e3c9721f7ada779f91", - "0x80115e104f22ff2653ba7c4e1cc417dc054663d488f861a9bbec4b9e907dedbb985e6e78f31dc16defa3aaf4f88dabe8", - "0xa0dbc25dde933e6114f2ec22445f1e209836585997b14100f3f8b7e62f5fdc6aa2a85ba5ec39a5197c9d4dabc9a5c452", - "0x8d2deffdeb1f0abed8ba62187f5e1cc06a1e2bc49b3e15f73c3d8e574dfba7efdfb762ab512cce53d7db790a7354c56b", - "0xb73f4897e221927feedbbf209e3d5b9c08f52bb732dc0d710822576abb7ba5ef0e728d2d95c802a00eba925ce99d734a", - "0x970761c7ee891b3ed08253d2c0d28478145d0776e2429c85b4734e5eb7a6416d197d0b1ad3392b37ce8d86fcaf9de7ec", - "0xb4c9e2acb4c05236357be37609abc437612244bb4421d69486050e390d5ddb52887a1b3e1bfe968a90f1695d892ba8cd", - "0x87caac2c93e192c34b5dabc36abe26a844a33bf63e9b01a668c90b70701360a0417ae3248173450c64034685d913f4f1", - "0xa16ac64cd1a7ad46cde1c93024fdeff724afe228818b72bb66176c7daa090acf58e7fc0aabc582ad22486e46f0b96c5c", - "0x936bdd6d67d666274c29765690f4ad9c4b9203e9bc9dd5af558a8d908dfe8d6d4346f6fbbfa69158cdaccb0058ed0193", - "0xb39af8d43ce9d120497888fba0dc95ceeabdd3d84421c1a30fea226e03b78cadca0eee57db524f6ccf1f6235fadd1470", - "0x847da75509ca07fde2277aac9e7622c5874256903a92f7a56382ad3f79d1b3b0cc0b06b2a6b2bd1749ed567e68816d31", - "0x969407bab3f8106a49be63f17ddd603e185afc1c9fc0ca0e90ac415f53923e3c6a69fe488d33403521231c5008bc11e4", - "0x82e25ef35abbd9b98c55a45e7a71791925639afd92780e64a154ad8a94e9807f2643854250f30bff1c5e8806632778f7", - "0x8e6da5cb8cd80d6b8e2321ba3f034ece1813a7b6ee3afac73371a51434a3e66221188162cd9b9ec035326e7e04e74b25", - "0x9868bc3e60478fd0ce37d35e0e4f7695f1ffb7cf2e05842b3a09e832af33c7ba48448935d425196fdaea9c3e8a5122e7", - "0xac7733adfeba1da388eee6540a127d0eadcbd23770f2deec39edc0bfb1002beacb9a8c7106baedb22e664f37771c1410", - "0x912581c23e3ad0d7eb886cfc22633fc704e530b6b4977086f68f1d9f839bbca3bf0162acede87c853e8ad8137b5cf345", - "0xa0315fee6285a33d4ec60f6c1557ebe4473e8990ade0feff7e008d3be1a709f5f486abe784398734d9ea1193929697e8", - "0xa44a08d6fe0a22849a8f518ed9b30b78367de205c3301fc8159ea273076488299b35c362530436dbb7e21b6b9f36835c", - "0xa591ea6ef83f2ec78a402a86ae5b82e330998e18ce66126a89046f169dee58634dfc531b1286277eed49f571df5202a8", - "0xa60d86619b41f59b48c800a302775656266725b44ff8318014fb668f331bec82b3b543ca848a7d40b2718f29e5ce6cd1", - "0x9420d0219d407583fff43c560964e1da06b105043187ea156771b1e4dfb5d5851d06fcfd819c7d8bb6568fa1bdacd916", - "0x97ba0b6731c78eed331530be7cc374a7f4a7cb2144ac73b7c000ca36036f68754d4edccf73ce373dd6c6be55177d89d0", - "0xb4e07b5c1376900fa2dfef8fd1a5a4b6152df7b805d5efc29057d1df2343f8bc841284ed23d2bab5cd1431fb95f71b60", - "0x8017de31e62a24bed74460dbdde1717f3a9cc17e2e2ca9848d77c3b5c364e7e1d58ac0eabb3daa2b7336edcc8a418b01", - "0xab6e409231b778bbc1ab74c3062a376c5287c0cbd7d19d4ac1d5da1a8d0571864d0723944da72581783cd7b6b0d529a6", - "0xb5f2fd4ef29a2ac847358abf2b3e7a3567b8653a4b9ed8da70809f2affc6ab44c65cd17f255db0cd8315e4801bb1a408", - "0x91b61d5d047e9c672d7312f563b8da90d9c2c1c1268913656f061028748a351e116f524593b1be7117a46f168b3e829a", - "0xb6c10b09ecfb92168906191756cb824694caa32c6f2f9b19c51658d44dc330dcd344e7b04333392a8a93c73346a3845b", - "0x9431d01a121e6ffa15c32e724dadcebff65f806c11717b050c106c0c80e43e622130f41224533d13be4a8d14a66ae1e7", - "0xa1248085c85855b4df6eb5a02df0dbd5de5a8a82656e1a5f61214885fcb75428647c8545a848960701d61c3002840647", - "0x9867caba8f4be9483df9b48e2bfa024e79e6797adc2198f2b5115d7283931fe4cefc382323edfa1e850c3970bd1a2d53", - "0x89e88c50c43d7e966e60d49b3afea792429563c93550b10584c91e4a827a3617971eb286c39205e2af4e7dfbc523fd8e", - "0x8ed261502f95814410fb081e7348eb09f3a3df22cc3ca82a2f071abca0190e9f041e8714b811418caf7e1753cf284e9e", - "0x87ac65370073b6bb85a945e138e4d0a5d71ed88739f72b9ba747d2a03b5d4877e8e526026348d2578c752bc4102055ed", - "0xb07de38d07906dc2838be840c291f467d9b695c62175c5afa46d80f34674d061107d6fec6847ba5f17f2d8729f31f5f5", - "0x899348bd385a7c3d38f9d740001c9a543dd8496b58807a6a73180c94f3aa5c15a56cbb85cd7124458e2ae44a454a8a58", - "0x91b70c3543b8e21cbcc8a40cbe00cf2ee0372ba9ddc7f610b711a070110508159e6a52e8778b20f0194ca09b109881bb", - "0x8ab84d75831ec1e708ec74eb6d6de2b13bf97e2d2262ece39e5ba5a4a3049c8303023e40fce5e87b237bb1dabfff5246", - "0x914ac70dd91ccb6d7b83a5ed0a9250c66e77a815aca17416f1796fc0e1c26bee7acec5de11e65061a44d2d9c35f5d19a", - "0x8867260f8024f533fcb75d9c3f2ab113d349504b40f650a2c92bb46aebae3a55de6a113cb6387bf00eeb2bd4719d87ea", - "0x9976dd4e56b16fe11533dce2734e2903a3ec986dca6540bd9ca8b758a59a1e45b1e69c0b8f094d42cf7e015363ce37ff", - "0xb48c840786653a0f3ed6b07f8f980284c5eb2dd22e9ecd5a0566754a1534300e129b39a8a6d4fc48bd403b351e714f05", - "0xb1633aae7c5e5c51a82aa4e4bf9f92c0cd30cc1067b03364825ecc492fa43391ea075195f2f73b99a11dc49f670c0e89", - "0x8769a592f503bf8ab03d767524d9ec2223c502ebf15b69eb4b3d53325ab366888afbb668bcb380230b5bd74b32d90a44", - "0x87439671fda66bf5989fe1fa2aa32519ef908aa6ab3eb34eb5b7d908e9a7db2d679170cf3fa0e0a388a355b8c51d306c", - "0xae1ca219832c90554a91a7258ca5598f8bcaaa7059c574803b2688d8026df9083985c2f8f4ad3aa9b122efe64e0b2481", - "0x94916e6dca309d9c7afb9aa4c0bc89a3de875a8537cae1fd32258b34782994e5be5c4987577d697ddc86b8d68dbbcbaa", - "0x8c5361b85176adf77ab1949d34edd562d8c16979e33b59d09548ad372b8c913ef385166bae53c8fef814a529fceafaef", - "0xb968172a6a831c6ae53e876dc4ef8686879cdadff0aef4147c4dc3ccbc173f89748b840a30ad393eaab69e422363bb86", - "0x8fabda060f8bb2bfcd675803ff0a3f834e2356152f88bc79c23f58fbfa6b0c82850f281f7b8fd2a5e16230aeb4077320", - "0x8e5c887c318335c5561e63fd3c3f64edc669c0b03b217e3ae40ea29245885442864dde15751d7c6ab177a91fdc1f7235", - "0xb2f67f9d64650c6b51b88e7ee6d6a796b453131c93a7791cdb2d0a4922d3c913a4ac988bac5b4b9bfe61469886e1e7a4", - "0x96b836824dc2a12ffecc6a053f7549b7faad9808e98bf20f3c9146fab05098df56fc2833a6002eb39c935fd8757d4716", - "0xa4aa33fa77b62605f751bcad91333659e9345967845226371e5f38d5a7f72405d0e30777b485b730e6c62d8216790cba", - "0xa041bf3467320df4bb7baee569cd685a65c9d0e431824b7de93ee47ab8b3ab20298d60746fea7fefb5bc82d3f7e25dd6", - "0xa85842f11f490bda22e9f73409de0909a2e61efc6d8be0c3f561d881988b4d2e6924ffaf0a4c40843481892b272943cc", - "0x94de0ecf58ef27228f5afb12496c53b075bb347f900b2df98f47ceda8675bc2941aec04d1c8ca0dec0233430f2759824", - "0xb1795a70651be509c0955b07d58a1b8655a2e6c292b939b6c156f0c0983abd7e416cb0cf14afac6ceec85f2c46b83a28", - "0xb6beb936ea1f1639ae59eaf53015dc1855ca0f798d9ed72607edbc6c119741e10af5354c29571af8befd83b8255a8f58", - "0x9424188ceb15c1b470c4bb17c71a37af56c87625e7b7fa752099802673c3a5a99d16e7d6dd8f8b680e89b75cbe7920f9", - "0xb9e22b5df8318bc0ff81003e8208ff3217ba1a84edf6a0b326b7180208d3a9144c6fa54c57ce6d6071ccb1a40eaf4509", - "0x8e5fb55da49feb7a9152528ad6a6766af75cce249eadaaf4806c6d4162f65f3c7311bcf8da72b96f6636cc019546c05e", - "0xa55f751de82aed5842f94d1ba1e29976c3d0146267b11eacaa4fc765da8d2acf617d3a65a2a74aa983130851f8c57d05", - "0x9647758fc596b78fb52db58f2ec31cea186d9d4f68692f56e474961b277396af4a51781b0a358a6a6aa8138e46443a43", - "0x9461f6dc72988b44c662865cdc01c0f970f078637859cbe6314fb17d2cfb3451b222cfb93a5c6eecafd1ddb36de075ef", - "0x93b30bbf4fa0926cc5483ba9803c8b001aa80322addcc866bc514f2a10aa43bbd86008e4671ea26d8e0d2ffd4bb8f2f1", - "0xb44020d0f062a001bd6dca2bc3ce61b17efc7a121a9035709f01a8c34708ed0c1c85cfe98c534189e0669eea719c88fb", - "0xafabce43f35e0d3201b60226c72c30177c4c5d75bac654fd2b58b3ce9de7d83ef01be60514817f1e7bdb525c910b8bca", - "0xa97bbab394253ebb02ba47ad391db3aec1b4d03e88ab3e7505730640558c11fbfce42d53b7f85787cb564208d3dc826f", - "0x805a34cb0c8c7ade28c69dfdde46b7a283e539977602aab165316e973c62bc65396b6fe2c96750ba028c550de03100ea", - "0xa0be38fdba281e0c248933ed73f1119f90e34d5b4435bb704a5fb7c20805e195518a2a424bb483f16500d74f440d4a53", - "0xabbabc7db0a20030c6e687b89162e704720a010d7ac53b9766a9ccb7e02d4ea1926792f5263d715cb97d67f2010288c2", - "0xb9e471a7a433a678090fe4324739dffe238ed7e9a867159e0b43fa80c9c0798cac6b58bc09a389223f94f22fec43e18b", - "0x9818e9a42ebf415c6d970c87261645f876d709751c8629d1ffbcba4abc8e3a2a1db8c4c6a6324dbf433c43fff62803d1", - "0x8290ed53eecdb61157cc458dd081b9e890bed5e4cfb643d11b549b2c65fe68fb981d4311473510781945b0ee763a84aa", - "0xae730a7c69866f22d8f9b0d8e17d7564c25763cc77a5eb718d5651b9c5198b2b9d3eed1c066f4985b2f6d7edb0a109d2", - "0x88325e421a1be440175293efd498cd167dcd0914c8827ebf64ad86788f1fdeb3c16d3de7a681f958b0f49046c54fd804", - "0xa8f592d6ba7fc3ab8ce8260f13f9c4886191530cb1d7505d0beae54d4c97d09712930b8f34ad74f1ac5ebedcea25dc8b", - "0x81c0853b0310a96674a92a144a14c48fcee0d72a772451ed046c284f16fd6447f67389ff7841d752a025da172d62e73e", - "0xb9f50526ce4bee12fc3fd8f3582f3829b90840f6eba06f37b53febc1d0987bbf58107d73fe4373d79e53827270bcd817", - "0xa2ca28f619d4821f450b9431bdcdb129d4f35dbc2a4976e4d416dbd14e378d4d60a517457aa0343f7a5e60a7e246e22f", - "0xb9576225cf7e13374d3975703b3850251d53ccafc6feeedd07be2b0bdea63b899139a1fb446dcf76f62f3c03beea0486", - "0xa88df9f6e95df995345c6265af546158499fc0d89447d3b387e7708fa037f95ac9c4e20ed35b749b8d8a7471dedeea87", - "0xa853ec333af8f35d51ddd6c4d45972b68fb36219e34278efa6cce02bf8193d72c6014ba6961f8448785b0a43a31a688d", - "0xa1ead9282496e590bb43908dc64341595cd22b844624e02f2daf597f708ab0d336bcacb5862bce3ce23d1a9616fc6179", - "0xb97398d8ebb52535a1ce3a10b2255d358142ff653def823ad9e9ce4ca5f372c6e7c9301588ae5d914b2b921a0fac7442", - "0x8d0d292c7e9122b8d001b3a3323f9d37dca61de5a595f9402ab0e53e941c83f80237a853abe4aaf012a35cf59df48c68", - "0x830535a5a8268d5ce4e7462fca4f809348908ae7ee117312244e0a9c30b09d91b6f798654d8064091078400346614e04", - "0xa44a90d3d307ee3a3c3838ce43a873311789a9b9292c62a01622bb813a02f6defd21b0889cb6dda6d7225009cc6d2641", - "0xa219afe00a9327f2c937afabdf5f10bca0687f48d8f7a2a046a52e6924af613f38cf82454df4f412f5991ba20b7db74e", - "0xb448ed4b15ced4de098781793a6e850ea1547d514646fb8f1c137c86e14231ac4340b308bf07813fb813cd02e47c015e", - "0x905fb68b8f5bc14834a06d61f3da686bee77b3b590a33c640c82f34e68ab993f8c4897df463973d6d9f0d53f9ac5cf5e", - "0x991cb6857dd0b3ee6597aa2fb1f4ccc962cb038228615466964680795587240e6ccf7861ec220a53ede1e2e9752e1cb7", - "0xb823dc0249ae72e2de91108cd4ae6d6af3e464f12a53a46ca583727c7351a67f2d12c911534e234ee187389fcbf1f621", - "0x981ba6bda1816036e75a864f635629a141905a4805c706260e7a5e12b82dfa9de4f4058143065b10a1012adca6b7d083", - "0x8bd8ec0e77a6867057e5393d82132474eba9fcc4bbe025544bab0ada4ebad3d296ceffa3788acfea0a44406e2ab637bc", - "0x93eaca3a7f9a0dc809eb9f604905b0cab18750a9bfa42d98d99728a6de6e0f1e05b6e98bb3b0d9862a69eb57ee2e18f3", - "0x90b077d7b7b1651ac0d723978b3e408191c2b8b08247fe2a7fd69afe0615dec09e43771cd845c2cd064b56295e53f634", - "0x847e8f607332129e95eb1f3e00003b087e92ebf1ac9477304b0d54ea38419fe8581122d21bef8d034f788a9c92f4ec00", - "0xb0301becb003dc7cd56ea7d830bf0fb4b85bdb33606d8d9ab2b70c6415ab5c8f4934bb7079ced16081b8f6d16b77c0c0", - "0x9068fbbfcc95fff7ef79ab64063dd9bff0c40b4855eedb39bfced9250cc351b5b3b1bc6c2d038cb6d59a12a41b3db664", - "0x84857e081fa1c6c08bf7b0bcfe7c6d74b57cbad1b67676e99686bcca0b17715ede19f826517dce3f84cfa014e11909b0", - "0x98fbfd6a94ac3e4b53b811e4d275b865486a52884352ff514889313c7a15b07822f76d428533a0f8d3cb42f1e6f72356", - "0xb4faa1b1245aa6339b5bb987f3423d187f6e7e5d4b4b341de87ebdea53b124932cd0e586470cf4a3b1060a126e4ce7e1", - "0x973e88d073071c2cf5ed643d770a45f6be7b230896caf72a2cef10e56ff0a4e032d6ae1ff4c19bba2cc29f29ba70cc19", - "0x8d40b3285879fb9ac0b6c9d92199afaf4716fe21edcd56b1a1fcb6ed298b5ec5b3b64222eb6f0cd1086d41872911068a", - "0xb5e338a02076ad851778d590ada4af1c217d035c2505b891163689a554e5a957219410bbb435bbb38c8a1515146f8789", - "0xb1d3e990d027a38fc8a38579e39e199d9984dc6d857bf51e2ed5fae061c8723fed3c74662405378c29342bc4f1fff7ca", - "0x8679f10f866804b19dd0b14b24068c1d32908a52149d33ab03394990cc60c0f388eef02bc0db819f92f8197b1fc60c17", - "0xaee5157db1cb7ca8013b0c19201ea1e7af32e4117896b3f8ec0ef0b2a4ded6a5e7c893281865cdae7deff4532a6a3fe0", - "0x950315818b710d3903b679dd0de0619059bea7dac3bf4edc8fd4a6dba81b7aff9bca7cf1972940b789458f287609439b", - "0xade345a6171b8e8afce7a455cb98024d0d91dfa347632e1a5a40721868bfed1c1959300f1e1e39a551d99a4e1abb563a", - "0xadde1719c13b3ec224bdb6b44dc2c5f2daad54e7ee736209653a0198a769180019d87fe6bdc37ec1b48f0212ea5a8927", - "0xa3397eba3ed2ea491e8d0328333689f66b2bbed0e1892d7b14b2aa45460a12e4d592d78a5d0ac20bd6d34c88b8f1f7a3", - "0x8613160aca85f0154e170b1b3f1052ba984f5c422c4c25e0771a53469c274130a31f875a0ba9650f77fabd910cb10467", - "0xa91ae4d048c56d5b2383a9d8f6396837543b609d8b0be139ebd5fd31fe4a90071656442ca7f14136cb8205734d929b5b", - "0x8e42732269c77887f105d1c326488025f9938cbade678bc6b39941311360408ea6baf274bbf5ffff996756cd2390bf1d", - "0xb96e1ca66d51a186237fef402bc4e14f8f96a138db377b7e2c3243954b6f47ca75cf4fb5dd081aaee634b5e2efe2a065", - "0x81d1c20d76ed054923c17b362b50205050f185137ea10559e35ee7e191bd89383b68179c0aa4531eb61abdc239ae6891", - "0xa350b5778e26ee808466619f73900e09bd387849d072c0c014517d16adb4e3394673238c4f4e705d30b4ec2edfe5a695", - "0xa13657433e39c0241d48075ae8ab1efe3680c96d078685c5dc0ac3c49d468db98f2094dd4204f44e8e90bf54059b5807", - "0xa96255abe489be9d42ce6fa76ee90e4bb6a36421fb78068432cc935632ea5b5bb2ab70790ef79422f93d35d1034568b0", - "0xb745d643480edb577b1f92ded38a522236fa1be2944ad8102ca64c3d55f6297b7e0aa1beb082261af1cc334f5a723614", - "0xb235ccbf94e2bbd3c794bcaf84266141c0e04ecdcd7d2def83a7eeb86a2ff4dd3ddbd8245296b27344770f3d5d332f90", - "0x935f3e4e9dceb4f58404ba1a489985001827e96bf6be227a8ac4e2eb8a950d4a446320ce3a245d09d2d74776c7033a3e", - "0x99cb7f3d6256ee8918f40642f5cb788f0047a04c482146e70687c3298629bf082dd98d4a4c222fbfea3afa3d7d806f00", - "0xad6abd2fcc67af691e76792432b83b8cd9b0a9e5e73de21f89ab54081ea002ffd904d77ab8efb6906790987e29c53ff9", - "0xb6de4c3a45ed7898abc037a47507f46f7327c057a911529d3a671286f98e79a421f4586a7ff3235f1892d0cbbd0e7bff", - "0x9120311b071d38214e39f4b48ce6299ae9297c7b76ab364353d3816669cba56592fe4c7f1f93507bec7ddc1df471f0f1", - "0xa6daf71681485d01ae7fd4bb81a326d3d2764bbed5d3be45efcbc04aed190163ce8f9d04a84bacf25ec151790f8fe917", - "0x9534da45c2a497607f7440f61943f4c16878a18f0bbce00dd644de88383470705b489225f5be4428d1f988256b70c926", - "0xb2d1b633b4832dab1a530a1d85415e7fa3e4a1fd383ddb898a79c7ad028f2dd8fbd56b83600cf481eb14a073cd65431a", - "0x8c43dc994dfeb5f22df9560518df32deb1af43f254acb8e6f93eec3fb3ac80081b39610800d0822246e130e8c5f7a067", - "0xa18174ffb85d13b7edde5822f22872ece12383d79fbbdb8c02bcc9f654cea904ed8c03b8709d70736dd4b308ecc1607c", - "0xa54e4bb27d6d561261a3fc48705781399f337448c0afa68c074918d2c14ea7d51263199b01070b7161c3db8b9949717d", - "0xa7457cba2c5b455584980ab6d0bb5253dbf2cafea4efe5bd769020b970dc35fba4109d002f5934610b8b4a158252ebdc", - "0x877d4111f50f77463b60e07843b4521b2c44629a7deff20dbabd412206a4fe03f976de1a3897b9df7eed16217f03e2c2", - "0x84d1ab99732fed1470f69fdb499dd3de795b055354e89d522c6a7df4d6a5375052c8befa4dc7615d29b3d92ce7df2f24", - "0x93bd139c343d8b83403e04547072c3e546c67445220afd06c119f7119730288629439640302d0628e74fa596e305c0e0", - "0x8157b5ab48d026684f6b51b802b4d8e7f85ef82583d1e8dfeca042b47a0e0f58e30cfdf4738e6d51394b260a4ca7e19f", - "0x8f03d5c1720540c29a1dee44ef5c7f8b209094ba8376d8e5eb9b52537d9843912b68562eff742f0a7a07f5faf6abd1ba", - "0xa15e4999a0028b8b083c2afbf4968a1f0397c26cda8dd7f6c134c6a860e740ac4bf1a1936849a4f2080e0cc9f8e44387", - "0x8b71fb85363158c7afc3c41422e9a32ecb2d1f9d3c01fff00b77e0ec6a8661e95b552a7f05f4acebee751448ed750684", - "0xb34125432d0704c0638090fc4566780d2d8038d803f96e19ff748325f8b5579cb8867e12491921feaf3c0df949f36aab", - "0x968196e10bcdc6cba28331a229acd54b59edaa83cad0f8d14f39d787467bd5ea725a3dc3d50accc334e74c81fd762cff", - "0x968abfa40af365986e68c47b4eb3562a72793fbd66a7d1b3804a5bac8137f0a3cbbf5cd306097cbf1a3b95c3414fb061", - "0x85395fa84223dcc16b7e620a7ef6f902f7b29dce7760f57baafb37d985755e65623768b8bd745c8de7d00e2035aba7ab", - "0xb57ad86ab3f5cb00ca0855088921865893b6e539edbbd504238df2f9b2fa7c7bdbf2d6eec6ba8e2a70a4c4fa3f459a97", - "0xa2f203ed1f07cca3f8c0d35ccf7a63216ab98c9e71557e829dea45e2c723583bfbaa7a83d66521b08a0718c63973a6b2", - "0x99a3522974525f4ed10623bae83dddace6f9495687cb9cf4ef52c8530b05672c2b226d3fc5058c56462ab3737a068baf", - "0xa4a50d127ad06067f1eac2d61c0a1e813fceba2e5e895467b5e6045c9b7308d3678bed9212b98e44c19a1783e0f57bef", - "0xa62d103ecc1d5e1d5cb98a0bbf9682ad65774d63f67f95bcbfb0cdb5e2437f2279043e4426d490f534961a2487782cce", - "0xb12fdaa5ca77456e6e96eccf97a303ee2d73f547916ed67378835402136c2aa03e63912edf5a67785f7ac1636f6ddb51", - "0x91315750043c4e08c7e4359b9cba25309eedc9c85672851f05a0651dd9b9329bef00a79cfe73ddc308d97cf548486b47", - "0x947115aa6cb3c635bda7f3c5fc3dd0e4881500d74db4c0579e4b9039b75b131eb5db54174b1bb970064740551e6cd1c7", - "0xaff091a9c7e86c80646cfffbf154ecbcfeb66877c5b773b6e8759649ada1094270e57970cbf2b0a4bcde9bbfa9689b1c", - "0x81e3cb9116f81e583b7579f9af06931a5337fae0d57d9ef777003f32e0eb619b75b74137385f9e30dfe0e10c2120b02e", - "0x81ab49647db2a5a6f47ec757d8427325fe721142377a287c547fbe04ea280acb32d71f3dedf7ec0f67b43ffc5d748342", - "0x84b0e16d8478b798694503ac4a87ff31affe0ef6d2bad47abe0fcb3a2571fc8e4e9c966276a5f4968b2675827a115818", - "0x9567b2edd65974393cf2181d235f877f5827a6d5ca16e77165ef35f6c66370f0c55a2dca5387c02ae73a66b81f01798c", - "0xaf19f841026271e284548b2cfe9fe7d6f9acdb4759ca76fc566de7a8d835408f86627185fe32e705f94e6a719e463cd3", - "0x83883e1c9d215c90948d066d2210528552093a726f0a27b8342b611e4b8639f6d2a5f95bef8cfea4312c1f2203f34986", - "0xa48019b2da37a232b7999f6b668e2758f82132e15ea93608bb2350d3188297c8ff8c791977f2a083ad9773570bb560db", - "0xa1fcc29974eb065a350cdcb4283b2a813f02421b872eb3c15056ef96e2d5ffe2fba0e10ba19a4d271937cf08838e4106", - "0x86f9ec59a1f5a5796e498247c0ef1457ea7ab098247f363329a336a1ee57afb31cc18d35e008a5263e7c401fad5719eb", - "0xa903f95675c14cc618b02f7a0401ab67170b4a143925979791d76aacc90ad1faab828fe904f13d155425b2ffd79c008e", - "0x8f652c4982220b8e9868a621a91eee85279b13b0c2974472fbba11775e6bb1d8d53309f500fbdacdd432170bc76c93a8", - "0xa9b02cfa052b5808c1c9ee65ade446a6ce20174bd2e9d9c7388a1973b0290debbb6fe82697f09afee6ed01c9dd99b905", - "0x8b4c700fdbcb13854c7b1d257a781fb7449a9e3236b962871f11b31b1f2e69ecfa6039e2d168ebdf2f142f93b91f5882", - "0xa9ba2295980603515f80f0130993f1be434281fd4442ce7e68b2fee12b24e440bc0282df67707e460bc67a4706bdf8b8", - "0xa382b85dd64b70296a2d16d1d15d6de80687dec9cc074445fac8de7bad616a95972ec399bda7c2cffa4247bd04413b76", - "0xb6adb37da1c6cba5ddfaafa3718aa66fe2821b43923ec371cd4eb9e974ebf3d0e94dff1ffc1347cee5c9e19af7c76be9", - "0xb5b531ea7f93c4756e5799118654ebc478a3ab57ea51125fd31c012053c759c8a52c8830b53208f74215e437d059eda6", - "0x89c88a5ecee1931dc027d1553b5aa82dbc5fed2a4bed329809467f79f2712fa5529c0f80ce6891817927d0b66d356db6", - "0xb4ad1964f73d3b7bc338909df2ab8889c4faad9b3b8a5959ea81f44c6c4bec95f0fb6e8fea1fb7e09789c690423e2b85", - "0xb573bcbd8f484e350db04eb263187ae4e99ecd03494058e68221aad8d044db82957f4bf23f71a9634b2ef9612a78ecc8", - "0x93c3dd86f7c3105fe482f62b0a56fe43338aef50f0d10f237ca774f834151273aa653e17bf919e54aeb35343ed790c0e", - "0x9069c429e7c6507a755871b301b31c3b4233006c51bb66ea2c9051c6caa52e933ad81a8e879129e0c1b099a124bcb295", - "0xa22203e5bb65593bd22cd5bc6e95a2f5c9a9aac1e14d948a7e0aebce4f009a56623026e0980bd194a633b42f15822ad5", - "0xb1585de69b3014634da2ba76218321ff4ce8476b653ea985a7330290b1bb1445db3e1f3c510f9ae7c22940157e2df36f", - "0x802a70ea7fa057a03d12538c3ad6b348a8e694bc6b483cd62c97af8809627a161223557f1d5196e23f13eddce15c814f", - "0xafe8b0e94d8d9b44652602c5ad15bb0140456d90c95af4ba58cff528e2834e0036572af867488f27cb2d27d81cf02e30", - "0x93bb332d924bcacc41b4b9bf726647d7cbb642847fee5ee7dbf3d2a0489d71802d959a3e905a80ab1f34097328632f00", - "0x8caad1d29fe712bf09d505ccfc724574c8edaf5fc743953b2771cdae006ad9792a889e0c8136409b8f92e2cab5ba09f9", - "0x8678be67412da4d43d74660df98744c54365cf10aa59e522c59afc3836d115380416cb1ae497ba4b50ad31a23ece8b92", - "0xa48e64a5447ebeb5f6b0e0fea29fd5845b378e83f6b06b79b604081e5e723930a0d4c6025627382f6baba8d47425cd27", - "0xb8914eefa2f5613dfe99f11212912dd53d678ed349fe871781074d5b6eed1fc7f2e5bbfad3356a685c52a3c8a26e7963", - "0x836ba66155facd2a1839f603644aa5520cecaad130fcd5cf379139056d3e163bf35f172a4a1f015924b89137f83d366a", - "0x835b70cc340b57a09b1fecac678be381ffa4c4951f6742322c2751cf1c748ffc2b9bee8f155c007d88ca69c12bd9db20", - "0x8e98b4ae7c68941a48a70f703c3d5bc9a4cf6c20c61eb4c1338095920c4f23aa9eeb474a0430dc28d355b15dc6e83b22", - "0xb24be8171a105f203c5bf2ab0797dca8ce61ee07307e1d82fd26fcc064bd8a8a5b6bcae8dd611f8ab650176e694da677", - "0xb057bec8ca008dbfd4982ce4516a4925a61bd68e7a36b182575c6a4044c7a413ecd1dffa66ae3cfe2213763dd0f55a01", - "0x8d270924c541120a18d587cee51711486f09a39444182800355c4193a76789614c6925e6a448f46c1891106f866f08db", - "0xa0ebf85c44453153764bfc817364493166833b0f84b7a7c505a955cf3a7d4c1b4d2dd00145220d8a3207758a82dd8e4c", - "0xa56fbc83a3f1034337ca0d5aa89a0a18f900c3654d171d47ee86b0720c6a965c09c9b06678e3f25b151b115d129ff7bb", - "0x833618f5d13b7919206c8e9666997ef26c04a74844f57150e7268bea540e30b93eb785803535566765bdc899d4f10667", - "0x987daa13c00dcacdfb1f0eb13c38ddf773e7e8e19af125604ede42c6d0907f9ed1e4b8b8c9118b14f9449026802a6200", - "0x99b6e669cd7532b435d01b20dfed29211042beea6de58acd68b6eba26baa1687d80aadff901b5607a2553df047ac51d0", - "0x82c81899cb76ae21838558a1946425c719cf68d07950b0f106b859048107c13e4e83b0f2762ac8590cdd044c3e731f6f", - "0x8f1c5f634e38f47cc6967f2a80a449f5bf69585622c333d784263e3f6f027bccf8910da76435a84155a6fbe9a8adc4cc", - "0x92d3b5515744115dd20742be1a72a455c6d481855f4366a0e960104665db4ecae8925182f32d4e1d9dd7fb9aa246726c", - "0xac86e14775cc4ef22cafa8ac3298bff27fbefa9b7004ccb16d2937128492a2c1319641062f609d27b9314aa225301d14", - "0xa07e1ac19f4c374d68084415fa4a8068c0be540c8b9d81c0837347fe096547d8318bbd804b7642820e43c284af663258", - "0x839266a2fe6dddc446d4b515eb538a27b5a3a5e1a8246f6df77c2de8267e172bb7522aa7985e0503c68db9cf93399b95", - "0x8a381fa29e553fb57e3780f915a86048aa82a8a09059c80154df9490271aa6b99baf6bb217df43c8ea1265e85f07adfc", - "0x8d8806db0093161d7f83aaa2cbf0bfb8cabf823cb54bec094f886da6461397f41d54c39f216d7ff4a8262d12aa8ebfc7", - "0x90aff3f98394674791e194b57c3f4e6e019471df1a74dc47bed725d4c47399e91c88a955612be47e89002f451ebacb55", - "0x8bce2d60f3e82042ba94cddd02543b46cebb8770e9b7833b4e79289d4c491df7f4da0ab69778cef92dd81e5a6f0eb71d", - "0x8246fc9424b5d5ae0a3344acd7d6962fba6b68cde09332c262d7b3f379cac2c650d80cb5ed4baeea16a5557efb6878d9", - "0x92ea8547fedbf440517522c687f1d652ae4637cd072147ef31338a40e11017bfdeac42a32808d33522a71136cc3bf26b", - "0x84f6a64600184c54d3d5c320498282947b8a8166f09ccfdfd6d285cff374312da57087fec3838a49eac5b93315f03b80", - "0x86dfa1485e343c861286c057109119ce8e20abc646a4411696a3bf4718ce03d37fe14b6ea1600d8a7b172fcca6d08ea1", - "0x8dd3404facfe49c2f096d2e74641c474c9c54cd6121771061db3c1757cdb1cd6813d3ffd79e3b839b348d34b7b4f1ba4", - "0x8870cf255b342ffbaa2dcff41910a37afb29ca6a721774953dec182d95b426a481eac7bc107c4c2ef3df9f70e04e0b88", - "0xb0b843ccc630209b9ab35a69f3aad58c76b2cd3cbe94579b5757350460633217246b342fd098e365fb3ae88d5b7d13f0", - "0x804fe307b2d477085f8d9800c8a11c2dbf6f662d684d6a0d2fd415cbe4a09255e47535a08796a805188e1bad779ce121", - "0x93d91029bce430ecc5f41a460c02cefd3fdcb8c3e761ba26a020e108e06520cbe2eb0c04139aad0c0fe58ed34d8b2215", - "0x830867ec984210b314e7f23dc5b10e6d9ca53789cc447e29ebca229f4c79c9120618a540a9d21e4ba2ed8a811d6c456b", - "0x8d7a89ae9d7318d6578c1fa75b3babfa7c9df7099eefc2a9983ffa96627f4e7fc99dfde21b92fef5e0034dfaee35e97b", - "0x8eb68f5875dac63cdbbeb5df2fad7c1426939ecb6e3b6a48f737bbac1179ed4cf5a1e6919529878169d6d8552fa5ad56", - "0x861e26c9a31d21839735cca8a384b981f7346b026cab7d60fa95a7ad7a4a370cfb409812ca285090c3f1c3a95e5194b0", - "0xa02ab98589d48b2240209f54b0be78edb56b614b1aa02095ab5a9cec6a04faf065eb7b81bfe45aead551b1f774c60161", - "0x88124374273a2425bd5932a6b446986756379c7eb93d3ba0c5d7cbc3477e6267d9c67e5e956cf6df841bb263d1a8e224", - "0x91a766128a4c718a45db571606867bfe6e1b1049f0ccf71a01138d5443014c9758000a8be4dae0caca56321e3f992e99", - "0x8dbfc433e2477b9d86f221e9c49fb8db67c85438fd54b670ce44b68b62d4c0a9cd56c37a2127fb2adef22c07643fdd3d", - "0x880cb650f01191db0dbfe63215d208f70f924380fa22baa0e5bcab60f61ece3c6d4cca0e4363291f6a10aca9649da69d", - "0x8532214650619e201bd330865a3228e9ffaf1f64ddd33d206be5616c691b1965814f8bc507fc8a695c8291c2f8713dae", - "0x90e81d5a9d8fc976a3bf6ee6d3022107d3a9441ff212305cbc7c35bc9163321cadb352632181ccdc1450f91f51872b00", - "0x94d656836edd68384df1fe61239d40a36a0fadd59abead673e4a2ae58de5e2a6bcc4b980dd9b517e7212726b8ac94ee7", - "0xafa70edfed2d81326f26f111982aafad55f510de95555a4d05d9916a600f3ca6c1e3f66d6b092c91c1fce6c407e022a8", - "0x95cfbd616c2a59acde8152578737d3ed329aa82a950dcbb9378bebc3ec8beef9be2759a937381ed5aec1a46d486d1afc", - "0xa0a1ae94bcd07ba44c30bf50cbe0ddca2fdb5db82ae73e53c2efe9446c2464fea8e5de31da4afb99c6405798f0f9a59c", - "0x848e10f6c12a6adcf711ae3af8382178c46b90b9ff9970350f14b1a1b565a7efd91eb96871277b86040d26561acee099", - "0x815e749e4a56c3b982b50ef5ed249c4defee558647a5c6062022c3ef42b5ebb219ba770f0de74869bea14a98eec02360", - "0xa4d88794689a0f2e194988114ab96d28f77a29cfff606228ebe030a62eb4fba25cefd59d3d5f2fb66acaeda866f5c24c", - "0xad59a8541eb9641c3045d5cea6e3930b35886da4c96906f701ed3ef90cf74431df3c444174d9071a1657efc8cebdc739", - "0x97ae83289d535707039e9df8ebc73262f881ee8e288f73b9f0d6fd209385d3e2b761fb87ca852e10cc4818384ee155de", - "0xb47983e11702462a23e26c8d6407b01b67ad532bce3f1e0626fe3164886603bbc803c688729a64a69d119b15235389bd", - "0xb447011409a07a2d9074e08502e882098799f3b649e947de44c79ecf86a63045a19985857ec500638a3baa2b228a79c7", - "0x870f506356aa4f8df7d61449a7c7a8689705388b8b81dfe08fd79e8a734c998a7ba71f1f6e9df085b8aa5813a4ec4adc", - "0xa07abf6abcacd7612338b455c1461ff484dccda7430d4e9c5f9b4e5c1cb65055f4be650e6d67179b2c62709cd52a9b07", - "0x988b73c2a71f3b1d6b4734d231c089ad6cb07f7ea6f4b8fcfdd34aa33f09feab6cda91232c06b47e90ae9930ea46beeb", - "0x886443bb8d7d6c7634f55da1c5695f1691750fbf9ad2d63621589f91a0205ed4adbd4b905c62effaab235e740a172040", - "0xb66caf1ac38a8a66c43767e8597ddb66fbefd888989ca1ed56abb96ab9fb41937927a792ce422577c68286e53bb4856b", - "0xa84be3b37007cc932429ba2b4064ab7fabbd0b77400bbeaff09f8c6b818b5cd127ff8497e131dd8bf4323e092c690219", - "0xa99e9898b6f9b7b1b9ef6f28f60fe2ea71e961b64b262cceae41003f6aaa16fa3dc1c2ab63bf63534718ad812e882a35", - "0xa1cea8f3f5605a5c60144fed53943d3f259e3e33545eb0dfeb211a9dad8d99cb3cd3b2cf5031b85778ef6520700eac4f", - "0x8b979026924097a06b3827ad28f3efd7f0e5aaf7920ebe5347fabc61b000631f0ee973b61b7468fcc60ba6e4381ee478", - "0xb5dd7393dcff33d6d337328167ceaa7a04a98e0acf1dcbaf454247e85793fcc9a7d280ab14693cf2cee01afdf44506d4", - "0x8580c90d72c0c83c6c003dcc340553ea547eca5989780493c2551ea9f04225d77ea76acc1bde20fef1a0bb7ec01685c4", - "0x8c77db66f09e76ebf7ac14fe2fadabd41291f7ec5971060580b317f6af0daabe099f9db2c3d09c4c6edfa41211da0c4a", - "0xb6dec051200c25f150d3b9a7802f5b7c361b074528c79dccefa77d26ea2f67562a6d9fb8246369c6a60f832fec6b7636", - "0x8620173e19eac12fdc7796df12bd3648c66f78fb83a8e6f6c9077c34027a3acd0884ef2e3455a3de0fbfd4ca130ed545", - "0xb44e3ae4047f917fe1af378cacae2813f8774307c20d54c565b674de197fdf90e1a6da0733e948c3218353c613d23fbc", - "0xb330af874ac5d749a4ce1a23f4fbfa67f71e8fd16f6da07c714218be431b2a30cc4ad2594994a7a35f5aa06bf87ea3ff", - "0xa5be67aad05a965685aadfe03d66ea1136e6979cef00605e92912fe8f84be7351a6acf6b73c567a20ce6045a703cf557", - "0xa1672ed63df30aabe34e8eb81209ff31f4e5eee620b58074d92d9cf2687e40217169df59be8af7374aa5a9107c5f51c1", - "0xac01de17b74e2dacfe3db539910b6c370de94e646b6f2dd2a828a381b04f2979f8a62bac473659fe7b6e126f15ed7aed", - "0xb978099cd3aec49300ef9ce5561aa30da4d37cb5c697e0b5cbc3c42ccf2f96e53e948fc579cbd24605101176a353a962", - "0x8c8c439d9da3627e9f74da784bab8191552b945bb5bf9abb673659c939a60903e11f37300dddcbc8a495adf5c038234c", - "0x8b4570ac55ea349560a4e7043fa17f264dbaae15a2f3dbc5ef8a6579e1f9b5a440aeda94122982fe564f78b615de3e1f", - "0xa76bbb163db2ba26f5dcae8267d1a890815a76196af10444d3a04c1debeaa3c7cd51102fd0bff8944710c743f5393745", - "0x8d3ba2494b612f93b4ebab77e6f207b636e2d09a3e4a9666d4ddd5859fdbb9747a88eddb7749356b141a071584677ec5", - "0xa8bfd973dee352ae653f7c7bc7df2b32d790653a3f1f2b239d71677992938cabe941fa609e915e607809b5fa954c9073", - "0xaeb4c1ccee15753d4fbba545ec4ebb05c7428427f087fdc0852a18439b19b1669a3c744a0ae2e7f74c46905f520c3231", - "0x8fffac3ff9de863257a836aff3cdb705fe7f4bf604c2cbe10180d81c0956f723b69438bb8a3aa094fc755e386234dbf9", - "0xa583153b241d31223ebec9a95e11ebc4a657b14056b8ca052aebdd9866140dc4669bef4f02b5ffdf667ddc9a87e0bac4", - "0x93177005082ccf2143f24c063d20068fda393948bfac34af57ca58cfbcd0bf9a0de46f8f41312e83a502b7ad69b8f2ce", - "0xa79b0967599894340ef2408b48f42e6ba4f406e5ccaff13b46414ee38e5329ffc145f6c34d8e8acc6aba41c23e57e7f8", - "0x809a356a76d54a05e5006f2cddf0decf73e5392b57ead32ab56bea9fe13c1ad090cd69a8e297fa6e017b39361906360f", - "0xb051226cb44ab1bf94a9cc0e4f246751d68f32ffd12f1d077d3318de642f3997fbfb0f2ae1dd103264542c2bd0293e57", - "0x8cac28256b1a82d0be373d884d00e9ff2e384d5afbeedda706f942b1d222694f126ad44f9453fc8a985cf69fe11ad70d", - "0xa13b073290de7a2f01a65e429e1adb78cd37eb23c24d6fd5a1632cce2275496179e3c22e0b7f59fb51d526402c0f3f7a", - "0x92dab68d1dbf07e5b058120422ae610806809ddecd2aeb9d11d8fcac738c72eca584b88ff52c95817b79b9e0369e3ba6", - "0xb24267fbee28883cc8649c243b13905874e5d97a285b9c6abec749a53e106db0a6fd6fd8671d5b7c9a1851da75a4ac5a", - "0x99cdf977dbfc10084b698c81cffb431a9eabb55b1323e1b15baed5984a1ed212ec5f6c58372f965fe18de0100292e26c", - "0xb021c697c56989bc8c06636cd623c3672e8885598fd2014f5e560fa2f721f9487cfdbcf4adfa34c178ac84771fbb77a1", - "0x8fd7e3ad3330d4eb1a0bd42801d95ce40a82b43c366abc823e25311aa1ed882446d60b6309e1a1e201e725326736257a", - "0xb1b3c641ef4cbd5e9c69955217f53373cbd104916e04d012eb40a24d798e76bf05ed0a218862ce02619ef694c42be170", - "0xa376d0296c0105789e9fe539a5d22bf62ee36a2de4c9aa0f5e57210ae49e2cfc5209fe0f467ed19dc95b9746595255e0", - "0x8a0ec125a145e373929ae33efb978bdaf16041eba684ada612c244bc3e28c7027886e6308373a5ea53c9c3d8e868ce1b", - "0x93fde45cbf04cc386507b03eeb93c885da12bfe519df9fbdac5ada735934ea6e1a6cce066d033be66163b078e96e2100", - "0x80c1839ee1d2ddcae1fed77d5f8091ae3074409461e04153db801e05b44a7658c6ccadd33ad682e51e211dd9e3c80f72", - "0x87112961553b4a243875ac8d46bb6e274325699ccbdc40d7d9b7f7e58d3fd164f86b0b1df5df5f980785cb3918dc9b33", - "0xa011463964a319c1ea11c7c55c607bffe0116fc834b8a1d3684df33f77f6e51dbe16a891307c9f51d5b4d205c4530072", - "0xb316c4be33abd10400a4925f9d20ba02ab1feb50af39b6f6120d6dbcf1bde0a8dff7e08c64bd1f5c43543b013e242483", - "0x9555b696d428c4b74806a7d08b9ff17c8512a86cbb13040360ce248de241facc42c042d3779c28fe98dc3ca96a47b2fa", - "0x819f54bcfc58a7b793d185d8ffe411bde6207b77cf22b0d5e1b3d9843e4638009c907fdec1966b485f95870da57f131a", - "0x82c3f9623bfb8a8ff3573197497c175fcb314addafadd025528f805b7a63c87b0e54b48d46c0322110b0043f7f77153c", - "0xabc023b35318fd97ec81933ce55799d8c36c3d55cf59b9efb302b276a76a37c517d5c690287f216ffc5d1fc082e116c3", - "0xa6579226d602a7ceec06d402d38f217b836c8804e9da202bfaf1f3f4f15c24762ad6a2414ac022d8de68fb76ba8a725f", - "0xb701d6d60387d4e2308a77cebd210e868eaec10d86532ea18c0c6393475b3976a3eddd79e469458bae4f496da7398fcc", - "0xab202a2acd4ff874cfc147ad1b02d4515ace254f8b828f619df2537698f4e1b2687e004c70a152d605a73ab1ae40fb3c", - "0xa7e09ef6c86ec7475eb3ed69e57e0cbe86114ca5c0748069d00b6e3a1e2ed74e4366adfcb4d938023560fd91d0f64612", - "0xa9fc42b05ceaff4312d5dacd78fd2394dfb8dc87d52efb0529595877727747827c1c7e3a3da81255356033fce1f97513", - "0xb0150a1dadde09cd60ec3686256b4378f47dc6a55c092c60a3a3f0bbf586013dc37ed53ba7a91c72791c0d52e4c49c2e", - "0xac88e91b48f031df297c29fbb2cd0d2bcc767be5e0a7db87acc87fcc0f4300cce6deffc0b1cb6fc7e51c6ab13ec2ea24", - "0xa8fb1542a956fdb1dcf90da2672d40c90a4aaa2f1232318b4112816bab660657eb97e3d0fee9f327793f6ba9bf8df2cd", - "0xb78191d1ec4615b03b21d7730d48fd9643c78c31feea19866429073f4cbb0d1a67f7d7ed210ab62b760c679515b20acb", - "0x967c20d53d46011f59ae675a26aaadbb7512d9f7fe87b7a20c3a84c76569d23920121063235e37cee2692bca3af69039", - "0x9766abf0251cefbcfbf85ab1322f62267c22e6556b7fb909413a7819f635e3ac1670da6f5f72d3bb4b739e12eae5ccc6", - "0xb0e9c5c327fba5347474366eed1ff60b986a41aabab00abe18a91dec69aa54197d3f5680603057f05d5efa0a48dbc92b", - "0xae2f5defdbd14e2c7eaf595b017b4a97edf521f561ca649b6bc2e66382478b5323aaf84f0b90f0147e20ad078d185248", - "0xb841bb6e04d2409a419dff4bf97dd3d4f06f6fa4e5e23e4c85f23533b7f25fe3da9285ba033c6eae7e5e447e35329c0c", - "0x85e26db850536cb6d3de259f662a88d23577fd56d1f99b3113ef1bb166456324d3f141a7ff63dbccc639cff68e7ae5a5", - "0x8cc36d89424da80bcc2b9d9969bbd75bab038c0cf836f561080e14bb691e8e0c17306fd6d42522030d4640a01d5c0704", - "0x817e72d50f68dfbdfc9d5611eef7c6b490ef1509559801fe1ff916050429a5f79c8d03c60d2bcb02e72310b3c4c9d068", - "0xa15ed72881c49b545413102975fc69649fd5417f5b7ea9091f8209974024785496fa0682352c879953cd1e9edb3fbee7", - "0xadafd20b962921334f4be2188f9ced4a5914389d0afcdbb485096d3848db85152e2881aed0fdfca11f9c8a9858a745eb", - "0x8d8aaea706815f1ec45d9ee470698ff199c40b1ff2d75bb54afd4a29250b094335538dd41637eb862e822c4cf0e2bebf", - "0xb8480d2a79cb6ada254435dd19d793598adda44f44a386ccb1a90d32cd13fe129a8d66d8babd67044de375ee59d8db51", - "0x97c17d6594ccefd8f17944fb760fd32cc41a9b046f87893bb7ab2260090de291e8260ffc63e774a4b6b1dfe0e5107ef8", - "0xb5b7e1d4d9683de7193120be850395762ac9a5669cded9226f5ca2a3de13eb13b2900af083645ec35345894de349433f", - "0x9405d473872cc9f9b9c57bb9976d3ec6892ea429cbd1b12f22962b74d88448d4ccdfcc6d5c6ffa068d560d7bdc3208a1", - "0xb99cca139a3733b365f4718beb4ff4a5fd6aada0173471156640d8be2cc69f2a70d959b57688f927bca2329c3b30477a", - "0x94872ec872f19279fd26abfb132b4a7fd8c485fbdf04515c7b416fc564e61a7b0fc5da9f1a380d2b3db989f1832ac1b4", - "0x92aba716538bd66e35a7bb877cd364c1b8dc3055a9cba2da23c7d9c0a010209ba8afab455da99747fb4bcc8fd3144cd8", - "0x95ec4c205be3dd5df181220c96bba3f4e3b526fe5369035edfcf706c1eca43f29a4c95cfcf94cecfc974e01547e55125", - "0xb48c719d7cbda1e79b3f7ee9c17c13bbac197bb015b344f79bc6785b28a1e9484e2349178178a2fe94c46132c54983c3", - "0x908c495c355a0555544ec6f0b8e0dd0926ef2c5c640fcb89049e6178697625b85492722d42bb5c966aee2cee9981607e", - "0x98ded9cdfa92bc8e3664ae90af87f88759503715f6eaccfc983e3ab53f7b3af6802999da93caa7eb3530402ec4d8901e", - "0x993266bb366ba46d532973391af7204aab46a3189c83ce7cfd2713bc11e99066b1a5a012bead2fedb09274e7b362e8be", - "0x88d462a3a17f84326b1e4177799d6e9c5f4ef57152cb83ffff4353a8382ac8be7d50381723aeca77d33d8f07fccf69f7", - "0x80438d9eadea15c90008ccf4758d4e3fd5a7bd02809eed5b683f2c96a15d24524ffe75683b7167d42a47161c65d533a2", - "0xb9e7dbbd3d3d0d86e347831cf99657fb269930087920637ac6cdf185d5eded3f09cf3eb27759ce3f4b46f41411e2fdce", - "0x8f0215f23b4945470f74b99607c12c36eca41aaaf99747f522d8531244b668d6ab8c1096b9b5697208c3931e1fefaed4", - "0xb2c8d8515ff16beae04c855b4365e450e0ebfb423acf5da2501fea76259f862bf29738a858a4093b98c2a444396249f6", - "0xb27364a7258c30a59d1f13d358eb49dcef298a92bfa699b3b91817d2f324be8fff91c0b71cabf26747802a92582e7dea", - "0xaee7d6f71fd674cdd8dd1f22195981e7160990c16647c871835d988e881a3d4c52345e74f7a54768fd97a65fdbd84567", - "0x91356cb2024f7703ccd662f50baee33409c28ff13bb5eb92fa93f303913e9bf31bf83b0babff4b5e3649003ae95492e6", - "0xb744e4754043d3ed85c3bf6ccda60e665568dd087548ac70670b90328509d0d5013cbdd07bf603949067e54d8094fc2a", - "0x8146cbea5899401a80676850d0b43b02d376b4b8f02ed63a7d92532d13689e2c02846df79cffa0f33ff81c3bf492339a", - "0x94bba8a1508c6296d3dd5d2e609d6d732ab2541849deea5436a4a9034e1e6f1c8d26f6b781fa34dcdae7cbf8899d006b", - "0x80260b321d932e1179667de4916428c1b77ee1ea537a569dc64a12da5ddc85d09896939718ce08ea7e0fe8f8b115c408", - "0x89d4640cbbca5d105dd67250f3bbfaa96d7ce19a89f8d6e188353f3a9b8737f2db1707c506f8ffe1d3144dd1da371920", - "0x92f5962946ef7190fbb7bd3935427157ffc815a52ef44397ead3aaddddc82e5f85b1edcca1e9082a500960e19b492614", - "0x8b89240c9b7257cbbfcd6e415fd035ce33bb46c773569d217c82ecee5dc2d66eedc9333e0b043616b0cbf21744909b60", - "0xa3d23484916d2c0ad1b81fc7df70c97d711040799cab076223e0ee02a45a0fe9ab564faf7a225982468f3e62e32424d0", - "0xb31751386bcd471b5858d001fee15d566215e34d2d62556c51ddc60a834d3f1acf18c415c23a36b581cdf4791f461ce1", - "0x860a99003b841221dc5ea2bd7e226e5aad72db8a5959d5d4dae8a86114d30b9e8915b2314ef867e9c2a477d9424a2d94", - "0xac925b330cafddc7d95d115a9e62b2c135acd22b5e35a4aa789f4318f03aabef818805845f2532e9504bb19f69171809", - "0x95d8180cae0815d33bf8854f4590be652f95f72fc29f0c519ca9bf3f490ba4a724b23d9054e08e3d31bd61d609a8f0dc", - "0x994f223740ff95764fb88de1ad6dd90c9c58c0dfbf8482e1dd9bafc20c099a6772acf40569c54143f6697fab72772296", - "0x971d93cb1e7aec5defa52815bf202b11de6a2ac9c5d4c0eb236cf2c4941460731e12b718f4a5b980ec6f4c54c3d17deb", - "0xa341095fe5adb96dec2be367f09804ef4fe77d553102ddf7d643b7277992708e84be9a7748a97a56f065002a97dd7cbe", - "0x843709280fba29d446779b1ac6e31bc3ec8ab8082e8b063ef8d2f3733ee77a4191b55772302964bf867fe1044dbfad65", - "0xb7ccc71fd0d0c9642c32d292ae88ca369d1fb5cabb97b1745c621aee41da8f94bb580c1ab23664c1baee65e97b21f0b0", - "0xa9b41f31be84f8ba061570633bd9e5f4d8af6fcc5276c25d9ab67b2b88c1f8c2a87eb19280cd4fe7b4c04da8b2d02d7e", - "0x93eb14ce0632cd325429e1c23340da9655d3d7c2b42a4594bfd5a4e07815afc9eb1ac737228771492020f6528c0b7c61", - "0x959aedea532471b9610150657b895c5f51ca950aaca910df137dbda2d17184173cf2638a2a0efea3f61d82b6ef8a7c3e", - "0x8ebfb50bd48fbf9a6f782454ea900acf0c7143164de9b5b46c1cd072c69b76143ac4c99bd43139b5e55f847841fa6a1c", - "0x851499b3a1eae6da530a47d3e8bc068e6e7144b744d5eca5394f96253df65094e5f3c34abfaf7c7d78c4d5a5d4863da4", - "0xa8d68bf15b900cc47197739856557b43a5eb233b6c095f21a14a90ac8c36caaa1a54690c95840f0a4d2e2ffad0874a2d", - "0x81a6ff8fb1dc4d4042089d4cfc10cf826e39083aa5983e53f4866f8f4c10cf06cd8608c4cb1b785f8d309bdb9b2dda63", - "0x82f658bd1a95fac0b65d337efc95d856aa65541d49aa993b094d70e527e7d478970eeb3daa2904a1309d755e1d677691", - "0xb46ba4f3d8f287eb92390e5d930df4f1a40abe500c9aebf62e2eeeb2e5ecfe5296b09fa22d6c9cfdae28d431fd10a00a", - "0xb5b38508befa4623166f6213cfd160782fae5b7c3c7ec279b42a83d43a7adcfaa6c5b34cedbf98bba357fa663eec896c", - "0x89b8a0fb37a0c45eb1f234ae9c7be65c8a708f08d431728572169b33f8288b1e17b7d4b18de9fb76afc37ae609290623", - "0xa7d1f5779c043900f3ddf29b6b7ae4301699c0ee9e70314fcd3bb2643f912fb1225a0164f45c47419ab762420bf8e5ad", - "0x89d2a69fc014068aa6d0b79784b8953f3519f563b5c9f774f4b148334d822aa645b662d5efe7dc6f9cccc2f67268c3fa", - "0xa698d3f0b1b6b72b72358d5fd5e49e928cfde69bfda10e163b9b43bb9604362b32af1909d28da5e0364abcf5e96cc226", - "0x91c12dc25c48aee56484172de8c6aba0d9f5eae8db848a7b53d76001c292d115ec57d816c2cf10bb9e901b2707dcb71d", - "0xb0740219e084d56db4829daa30b2812115b2e95ae85ee96a140b7c4012860e8017e19b482e981547e50e25bd4ba76716", - "0x8c84d4fa255e2de7cd23b0bbd110687edc47ed7fa87bd42658fbaf3831c6d68cde3ef403ed6c585f8654d0cd32074bad", - "0xa530d3272aa1740a73e15cb9b31c5e2e54c404db72274b0840c19b164642389acdab4514b9b2bf9688ce51392d8b6793", - "0xa601f52bf7b3226fcab93c67dccd95c1d6673270671c4a099b867bd5578d5711fe9acc9b935b867ca780ba4a394279ef", - "0x8a238082dc8ae33314fe1257e7bec69e905c194ded6f894267bce97443166fb443628490755e1d453545f389b5beaa2f", - "0x88a9737f3e9ded874681fb6cc8abe0f6e1ce18a05ab599b2f855f73e6fe5bf804de5c5dddeb11057aeca6613bba72c8c", - "0x8a5cf70293eb99ad3c34992c47299646c8702d1035b75e4784cbec67b28cd4c88eb8c721f4cb8982d3c6a42d1b9f7fae", - "0x8a62228b84fa7463a6a8392a7af767b661382175633c5e00b36979d816a53b388f31afedfc47a5d8cbcb645e8d5928b7", - "0x92836b5a41900a1c1ceec83cf4f15c6177dc20f95eed23a203810116ede2a072a8d6c96532ef32c93ee21acfb14448b9", - "0xb4e538d7bf40c263dd1ede65c81883dd31f9237a0fc8d134a2b480a1a681dd89cd2edb19e63070ee69e96cd12069ce3f", - "0x913eceddd4c9939cf82c7e9ca5ac300cd79dc5a72b8458cd69e9f8929168eb19e5f21eac12a3b09eb8d3998e28e3801f", - "0x81f4a3e7195661b174aa2059796dd88d3206bedeb7d7cfbb7e61aee335a01ac50bb8edeb258a68949492d4ac6215d95f", - "0x913a393eba8eb88d1076effa8d2a30258d83635ccb346f1bfe099fb5fcc69d0457ce5a79363a618f9e8b43f53728433b", - "0xb11d721b08be428254665bd64a8864d78c5112e252feccca113631b2818fb729129fcff1e739178507ece41b807ffafd", - "0x92603fb7d50d11b59fe376720aa57412b866fcd5da90195a5a401e6222201b30c29f8797dcc1b41ee2cbc6349bd5ee1d", - "0xa466c5d41cd4a8d1f47a650ca67b529ad3873ba3fd3a36db27f7a5869b74b42381788bb1a1c100ed184118839b9879e5", - "0x85c50607a86d4f76826220286784fa9b6ccbaadccb661fb3489fd35a3a8917d6999ac891190f2297afac3c37abba2967", - "0x966320c2762b266cf7eac7aae39221599df4fd608036f6101cb8c68192fcbfd5f61c7f93172aa2be0934486fdf4816f6", - "0xab69525f1c77b6706592cdd5b98f840184b49efc6fc2687d6dad3b014f6a12c4d5cbcb5120d8869246da010823534d8b", - "0xaa2c9df15c06b58d7b9bdf617df8bcda83ccaaf6ddeb8074db931f7f03dc06a7914e322777e297226ee51dc8268e80af", - "0x97035b62f8db4df6e787cc2c940f2298c7d26c2127c7a76e4660d132a14f43c8bac8dd4e261605611b2e9c08929f2bac", - "0x8ace33e696953806f594427f137e84ea6b22ca9b48c3bdf3830b3e951e5a463d4a7067c68d2033eff452295a741fa1cb", - "0xb621fe49b12580bc8ec68fa39d5133875da23524a5ebc793c35040fa3436350d0f3e4bb4e53eaa23d312a7847e2eb2d6", - "0xab7d6ccc0de9c7ddea145e83fb423a535cf26d470af3326d1d6a9a579592b32ededb078bae4613028557877a9fe71642", - "0x97528eef76389dd77d33ee7daebbb290814911beb725ef1326c3160b9003c3043be09bf3f84e4818bc2a7960ce1acef5", - "0xa408eaf5c675b50dc1c655f83b9595dabed68e74e6d2eca5a4948252667413cfffb46358400df5d35f92657811ae56e2", - "0xb36537726b26b474e184dce0ad868a66f0944b4105ff6d35a2cae0f3a520fd14a66630388aeba178d400b5fe104e521b", - "0xb2b88518d10bdcb111c82a261076367e34718f1d0a1a06b421891b4eca1e3c1f904b66e65dc914ff1ea5991f6a638a02", - "0xaa3172531879a5c8f594ce96277b2c8c8d4a2d0f4bbe567ae40d5b36fa6108e00f0b1dc94b81f36c9eb6d1e9ee1896ca", - "0xa53975587f10667a9474ae2756faefe43e7f81bf9e051049de175a8ec085530fdee3d5e3db15d4be874ecacf49f31691", - "0xa1abdc58bff4fad0f6562338daeacdac8e37f9f3212aa252b17389bd9c54db58706129a63bd0695d299d043b5ef0e2d3", - "0xb8588fa1090597fe0f6275e5779da11a4d128c52fb8954e475c4940f1a3e10fc23ce1f61e9aabe8a75e82824f718a94c", - "0x8a1981c536747d4cc06315c794f1536db7ab3c9dfa024a0df854b948d93bee72083b6c9c4c4a7ce999c98b904813a659", - "0x95b2b1ed525d629eed454bd6bd059b01869423c3463a56689a7c39cffbd3453c962426a1126ed631b25ae8cd7538302c", - "0x8032c60f083477693f533c2d8ae391d62ea754b8eb41ce9cd59bc469b980dd959a8ac840ccac54b404a9d08a6f9e4b98", - "0xa72ccc14eeed758d3d43c51d68341fd7e98880c3687e122238d77dac8d987c8edb3067bb63baf13a0e57fe02334545c7", - "0xaac3eb536a5061a8ec788ce131582dea691957ce8b9c6af5ab7224bdf0fd15c77bc6bc63ad037bd83e0ae52fda738361", - "0x97dfa193800e57e6b19d1b7fbab40da6dd1463f043eeec34b316ba6bee21b6bb633ec0c4fe107c9dab6e06e07e0acdce", - "0x966ee3cf2f54777968fbc34f08c8de121ae7c1d6b2cdf1f1f9c675828d22ccb909bfdffa2e3f2ce51b0cc85bb29f8504", - "0xa9df6dfd12f8c43c28b929280355cb23ab0ddd2cc2e4fe76603a2e5dc2ef5d1aca2edf89b304a27345cbb1f24a86cad6", - "0xabbceef80c744e5a1194313f7b84b5dee1c9861cd4bd3d0d12c433e5f2e8c6ef6f10b860abf3b788aa04896f708426bf", - "0xb1dffdd81711e9782c992c4b14583ad9d6c39ef88974682a72e717e21923da6892490d7efd121423fdc638467e62e064", - "0x817f30dd799c422da33e13ac2bada8cce3930233ddad495f714a1c789b7aa8f41ff6e688bbffc5f2e8dfc72e5243b645", - "0x96760a79e4414ff1d19fee65b6e65b2dd6665323981ce8b4ee93d0a9c410b018ac086c08fcbc7a71720e1e3a676f2b3f", - "0x95445cabb75909262975a5b06381af2bff5c4c6cf51cc84adbc0b7f2a985117f35c014e33672cd5216a9737d3f37e067", - "0xa279c905fd9d49482d213f5eb98256d020c2b90bebac45004d6f152ee4ddcfc72a7e6b188ce3b6c93ebb9ba9b8be587f", - "0x8591e8379a78af8860e6a0e002be5b25aa4b93c5e556f5ae2e40400f828dfa19d93a4910823e230a51e2c1ea5464d437", - "0xa6fde17d41fd9f03605ab6ddfc992e36535a23b2c39608d30cd6d72588f1ec6afb9db193e88eb609e104e73ddde779a7", - "0x93e2cb6352a5eec063151e5c9a822f6fd475a072dfde2464af4afaf6a730a6af1fd74c424c09727328a7f23505b91407", - "0xa7b1e4f703386fdd16f5fc9b59ef1dd682bfe5a23bd42b3c4b1385bff894e758ab09674dd6d0ded5b32a0a0526aa6d98", - "0xaa7f01c450e619c4bb42d6cb1a90a94dfe132a641728a642997b71e2c3b02d09a7695b544712b2e14416e7de0252fb11", - "0xae840b870a938668d0d4404b76f5d4d252d8ae1e3619661df0890ccbab900e3d8dbd5dc9f96013413e3f1e30dc541db3", - "0xab7552930ab07b0f5d50edea3a2e5ea3ac1a05cc985246ca066fc3350bc58949dfb99d4f6a6408d1bba64d3de47a3c2b", - "0x8053634d4c730b5e90d68c2830a73e93f1c9e522ae0e00a04e2ba15a1b7b4fffb8b25516ceea61719f886c7763d46219", - "0x880c39ca4cafa622bc767d3127d62143434d0a1d7de8dce1a2f94cdcaa023a7096641a46e6b97e1b1ce9c233c873a519", - "0xab9d46e46cb2f382ee7d21b6da01578b786b7998e0fc2b5d5a4e1a0d93aaab997b5c481c2d9a741865d6460ceef57a5b", - "0x857a5957adc3a888cf93f144aa809c70a211932742a258978af5e657f4f57fcb6d9e39dbe8d3128fac6c592dd5bc4ddb", - "0x8c98656861fb8c8a03d491db45077f1d136a759177842ecf6c1ca36923088237e928558433d5b631590f395db56f96c1", - "0xabddacadd7d536e91d36609fd0047f5db113be0f4d84abc7631ffc5c00df919c085c049c013a05131150b0043d51f892", - "0xa8b14af12cfdd0e11c8487334efbfdd22c8b4fe6bf350333d42ac8c704efe54f50a4bb51d9d802e5185ce72e4b21aa58", - "0xa8badc2bb3cad0143db1bb3cc81751f9974ff3f0e2ee35921d04985409def84ac2803a657571699eba34767b773666e5", - "0xa6739a05d270efdab18462e8536f43dad577158e1c1655fa130da97e469adce9bb7cda6f9ac26f4a9ba3f9b22329b066", - "0x842ed6efb4395603e7fef0bf92326c0c63992da4ce7912f850c4960f7a19e0b2ecc720d9510f15ba6f73a2c5ada8ea71", - "0x8502ede859944047898d533e1923ef90e1b5c17d985c9fb4c6aa39d50636de4c5a4df278f2f62cfd3ad08bba4c5ca6cb", - "0x8c738573226dd5617b3ca1dec8780000a77f3fa8de241cac99b0d9b1b6c90cbb8aa2009668005f2c5c7abb09c0ab3f99", - "0xb101335c403d769313bd05c755a9196769465f7068fd6f9e00937f3cc843d48f013f5931f999bb5c0082d4315134f5d5", - "0x925ace190259b321981fcf8bcf52c6852b206099f25c0f278439ef6edc4320d6f926cd6fccf1b4cd224bc52e5c681612", - "0x95f5855ad1bf14224e51f7d5e0d229683c0d38fa324b1abe9d595685d3497955e30289618c4775f6083bbf923ff3a37d", - "0xa3d3c7100962c8b60c40b830af834ddc48858e7eba5ebe2874ebf74e505c25cf52e661b49d7619f2f2a039e1df02f5c8", - "0xaf7e66c1d5dca63e6be117b210c616efd533e77199d67d8f694e4278841963e0a46e4e44f0416e69bce6a7156e1872ca", - "0xab796760166d1e1fceb20f9bf19b1b7cfcd327650cc7cc35c161ddbb3cd4846e9a971b541f303cf62fdc0124688fbd41", - "0xb920211c5b440b3567942dedf62a65ffbcad1e3516f58d14d8f8dbe86f45c4b9745fbce43f5219b7052b27a3a04df12b", - "0xab6d5d25b9fc46b0824df1628993e44535febd7c62185b6795550388185035ae12bab60fa34848f465fb4f4a8add3544", - "0xa6539b67dfd6f3976cb6b304f4d152532b3f08c02bb97730c03c746da0f7b38ba65536faa43953d46e5e7687a86c356e", - "0x95bb225586b1c82d894ababea7e5dfa8468bc0e10a2ef34e5f736fd1611114cddaf1a5c58bc9d664b667adef68b5c25c", - "0xa16eefa4e6161a8e7bac63cffb2dd5cefcae57144e63b3fded581abf7ce32016a654aaa582fc25bfa51c83f352e09372", - "0x8b742428f6af81261a47a0df061e480ef9176694d361ecb57967bea67e11cd44df686e38e35b7d4a6ee02ebd520aa1c0", - "0xa2a4f2307f646384a0238a711c2dcf7000b4747b8df1d46c5da962fdb106c5339790b48682e8ec2532b8d319ccafae5f", - "0x81910c1d72f6731d27d3a4059ccb0316faf51fa58e0fb3d1287b798ea8f9b00bbbde31fac03f93c7e9a1cdbc9502d5df", - "0xb846b933c2acd71e9f9845f1013cea14d35cd4b8f7a371b9be9bec9d4b3c37a2d0da315ba766c3a126f8e2893f10af4b", - "0x8ffad59284b41b75064c277ab01c5b4b3a4f3c4b355bf9128160b1a55ed6b0d91366f7804006b4e6991525d3435d5235", - "0x82ff36a72533fd5d6745d0c3a346fce4f62b6aca0b8eccd11399b482f91cdf6a5a4135c627043008cb137ef4ccd935d0", - "0xa11c27f6eefe54cf32fd86333d9ccb59477a655bb0c35dcd028eea58d4cc40ef9a26cf3432fad4e9d058a27b419b8f04", - "0x96642ce0eea3c2c0fd155a75bec3b5cd573d41e8081632c56528464cd69a1141be3180c457213128bcd37f5fae47f7f2", - "0x8349a9e390e05150bbab2351b77a3674f1af000b6eb6752927ef838b6f0a1200e6fd7201dad8565e3caf3802f204246c", - "0xb8ae7fea6275ea61935d3047d8156e8fbc4a95c9fefd1c36439b2111b9ebeb7ccc306e0f8c875fa772f7b433cff848aa", - "0xb366f056e23905bae10ef7ce1728b317b83f504d128f5bd34701ecb0d25ec08491969625e23d5a2fcf0048af610664df", - "0xa3d88d506ba46b73bf07729aafe9698e788fd688647a6b4145761275257d262cc450c7889b8a40d698455baca55e3da4", - "0x891ebaac7a7a408aee4ba61605f44f9ca5a6d5e046eebfd8f5108b6dc4479482806dd01686045b4c7760051f22bce468", - "0xa6ddb74e3e3725e6f2d9025532ee3f357ee35289e1cb38dcd5b2ea8ebc0bb697416fb3aa73e1eba632d593d40fdb030c", - "0xa7dc097f440ebd31ec1a005648468c702bb77073ac8cfa32b050e90a9e1cf388f138abdd18f07951c752f7e19f706af1", - "0xa200f25299f9a0542c196adc2e00289f453411066b88b125d3f0e6b17e98efe9da8096312a2f1841e01837da90a65440", - "0x97cd3a9d4185d77d4c7bd4ee80928def7b660d8b949b0face798c62a7cadce1000997af29504d28ccf9070fc3016dc56", - "0xb9ebaba1a15eecae6b1998ae6d08233d05610dc0933b16922076b2dc4418cbeb4e5cbe099bbded3139d8a47f2b2eae10", - "0x86f5fe8fb36b419fe6fece1c5c4b9d64468b4aa0154bb5dca466a243b6fb1227c3b8bdaf7ce5c2d4fd05c061979f87df", - "0x8050e011011e7918ebc25825d9863c91046fc3756703bdedf936dec2815cbd10c2403ce6f4a0b4f576cdfa1347efdb85", - "0xac22132a482d2950be9442167be214ed9d24519073bf5ef1c8e3e6f4a77065da198a851950330fe4d62b2a1272835015", - "0x819e2e8e3ac43b6ae4885899346f3b558bd7658ef7d380070588154694957596695a925a001a9fec7cf3655326c50c2c", - "0xb00f40c084d2eafa36811e0d822ffef874a0d4bebd4817690408a737624be05c920a08307cfa0c1195505c5e7a5fd878", - "0x8355768c09515a593c8fc8289baa3b6cf7fc10d302abc93f72090ad99a70a1ef1107eccf839be722132259500a565d68", - "0x8bf0615d2cd11b03546ab7a0c90c0c938776aca8a8b989a709c367f0f5eea7b0a7cdd78f96050cdd5d0a123d01b99c53", - "0x827c2cce458464fdc716a2198fc67b3cf2ed7802a1f53f7a2793b2314789998b13ea61343c723da8863cb63def6a285c", - "0xb609cfe6acfccd632759700bbb0a06fc7903a6c0c5875c2c3bd85c65bfae7b29b03e77092f29d565a6a89b85012396fc", - "0xb73ddbc330e872363bed36578b245b666d42923393a1341816769ce0af24b700c19ea0f579e4f9aff1c3ff369e63da8b", - "0x976d658085e5978807e13b150c7aa539b44ab8855a386bb58a52d9ec9b5e21ddaf89a18b043394d6cf47bd589d04b240", - "0xa213897312aa28cbb2c572e643d3aed003c84bc2ca571dc5fbea8a0b642313be94db0047e293078d975fbc6800751a87", - "0xb54f2914f6a7508b6686280d3cc955730458ff035978be29645fba161ed54ef3d4086f956e68d2a48c49afe904edff5a", - "0xaf99e470055062390904673e18d04427c16afeb7b9f13ad83bc2599e9a92314bd91d6f1f81b55419a4d668bd889ec8c5", - "0x946ff0cff4030b73a1342a9173fe697ab20cc5e43ea6158573f2def601e12a174da431f8170bd31ceed4be48c90b4f6b", - "0xabc51f8bb5f74cee819ee383cbab739026c453bb55336fdf423af2c2ac6712ba90006d62dd72d8cc1b2ff6cac900c8b6", - "0xb43623a56c5fd1bf28bc356fb4a875d72dd4cbb00c9c863646a3376937088f9932a4a0aa26afe2ad69840b06242ec76c", - "0xb0f371952f99eabf7ed368a142ee07d06bf2b7ec1ff852fd948b8c53eaa52300753fb9ff6765201e35873b5167583f3a", - "0xb3906488172c09e148c571ef0712f88bc9f1ecae0db95380f61901660fc1aa090d0740378d5b9b76883507bed100093c", - "0x945373b5e6ffce11d39a722df7b24eb929b14a967d211be3b969f48fe1ad3dd4280317d1ca772a69b033f3bf26c02c4f", - "0xb2ad3490389fe5bfdd5ac7eb5bd61facff8d57a8f9969f4938ea56f4a85eaa2c1179a2e5b4f87d9e6409925c75b61828", - "0xa4d61547e405319cbc20cad16a2bfd9e6d093a064522c332dd22134ab05e893bc84786b21b4c71a6265bbd06da2ef4b1", - "0x86749c26715d22b185e1b25dd34818e96aad319d7add22a98486ef9f9808b5e4b938c6320d391dc4e0fb5d57bd41778c", - "0xacc554d5b866693a453a9ec46d422c8b410458fe8397384b927a62bf5f2b1fb9706c8c21af9845050fea8a91786e3577", - "0x8eb7e763d297cd93a7a54dbe4654c39c0ebfd73fcc34d3f1338de0c347676f445d32f270664fcb7b33347bd377c9f867", - "0xa1b469e3f9dabd36b13149c83aa5b7b5987eb0ecc1ce6b68c72acb39ed503a11ab4451e658576a92df4aa51d1bc709f6", - "0xb1ef105cd0259486be8f265a73ea089d5b7fab7bd7547932134539963467fb917b2206aa72446e2fed5a8185b04d345d", - "0xb3e211c1a14925f6de451271728a3c1e555ebebecd4bae29bf666927868039d4ec99d9f9aa98d835da7845a5c863dfaf", - "0xa416632a50500f29b6bb471bf00b37558975ac91e5c5b5004b67e130be1acc954a8ebaee7efcaf6883187ee9173d1ccb", - "0x8c655a85f66b5f28ab8760c94b6cf01cdc36fedd19a09c261e432fa7eda7928c3c88355384e689f1d2715d419fd8d898", - "0xb1fa9f82c9866d4f296755bef5b7c39fadd09374f38ef9954aa57b1431a1ea4cc17a9750da844fa1f5848f0ab7ca295c", - "0xb45cdf1a9eaaf85c0b07bfe239da618ee649ce90b417d90b08eb518b1fd88c0d25cd29fa7a0d8058d6616627a3dda306", - "0xa2be1552d3c4142755e0371a9543032ee82ad669d7edd24c4e2941bde3b78c5c6df427228fc45812a55943b3663cdbda", - "0xa28feb053e86dd9e2f9ccbb7c38467e2425fd580ba0f63190036fb47d01eb198ba8590b5cf68d1c0f47638e9dbdaec74", - "0xae06b849e080efcdba86fa03a0c9dacb38a15ba911aaec624d15787c3e11ada6909b1e33a2e3de928a23818d833eade4", - "0xb4888445d86bcf4d1f6a9c2d253f277596795084c3d45a4591b307b7ae4ba177d6ce871c2cacdcf9457f9c132f244722", - "0x87a568aa2f5471214f63932b0d48e589898e82a1f4c1055a9e73120763430537c233e9a3cb6cc178df53768e4c58c993", - "0x81e0ec97cdf91ae66d065234492a1119198c396e2db204b7edf192c88eb4238e0a45bf7e245f3714bd864244cba0ebed", - "0xa954a3785588d4bb3cfd7cb27df45c82e6958051f916594d76cdb35bb07e4f88e2831a5cda35fe1f3c99f32a275f0668", - "0xa9c9f4d54339d414342e87b03679baf29c219d28b6548f01891cf94d0313a64d3384658d82373d6e838d886235ac446d", - "0x8ef46cb24432b419b4cc803e60b3ef5872db8ea614dc37643e4592fbb2891cdff61f6b2a10653d9e99e6c7359ca4c590", - "0xb23eeb458c05ffa5d58be21cd0699974694dc61a9a928fb1eb509954a3dfe7d8a71620a2d4046a448de0fb213be7e97d", - "0xad631be8e17285f6310fb72ba913c564fc66d14460c4e8c4b0c68c572a5c2a45b088ef60eaa9d317403bacf534d57a23", - "0xb7130f5607f236374f5e023fd43cc6dee38286ca47d504c9e75c6504957ac2bb9134fd59d8bb1010d545c56ad9c71c4b", - "0xb83cb511757d80781e26b5e9b3e0597c4cf9a976a3fb60c84efeab2b6793e46282612da45b1bb8b45af4b7f39877feb2", - "0xa0c5f8b0027ee11cd5f86515698f689ad514cfa890ac4ead5502b5ede9d7d7ad12285f5806c9c85ab58f89bd9f188938", - "0xaa8e8f9335c6e34bca3472b5f412ce93ab1ed5f9e51c3affdf986a5badd2ba3ca1ee69eae53ba8144927f082371b4cf3", - "0xb2a4f775a10cd9caa776123771f08e928ecdb22dcb91efc440c69e37c6b467acfa6112c2776d4a530bfd6df3b04fd50d", - "0xa0c553d5d2a9b0525f71a5a0a539d579d937275df9220a0c3c322d6c0ac7fbd2fc55335a1a283e687856e2b30398e4b6", - "0x8ab800ab4c810e8f6a9d42d2dae9be89841bc7328bab06b88bbe1256f720ca99c056fbe4e1378d7cf805586ae18dcc55", - "0xb9a8766f4f4bf796e2517a8a7a05bafaa6d3ec601a85c466d33b8a7e0498fa1dd4e2a9e42161fe2362c81d4c8ee1fbf3", - "0x8cb7d054162e9f41245b0914e7dcf6108ec11456b39b473ecf6c40e56b172fe5be4e7b0753a3685667436796a977b977", - "0x9131d0395897f5591ad56b62ef83a3ed9e7b3951080b33ea606a15742f78a283f924373e813b877f32762dd69884658e", - "0x8d784d7f0884cce988305d314896dc6dac2d2934cf5d650904e1397f9b9dca397eb7f3accad60ab5e34cb2e494bb640b", - "0x8819629608ca1535bfc156c1e17f8fce5821d81e6661bca75a1754a5919d0404e31e65bd509387383a4111535e949f5a", - "0x820a6f46e251a1e6d92784aee18fb0d265d7e2f0a5b7e0b15180273eabdefb34f1d575e1d8e93dfc2be1114d10abf31c", - "0x8d10d0e0557beb8db344c2d8bcada724e720823fc37ee7c51b322c3269559ae932bb2ea07e50d7ada88ede788839dc8f", - "0x911a333e2f7578a0ff6533284176cf235036047a11534acb649a0043a326723662bccddaf1970b7c37b5146977277b88", - "0xa4be2104cc5d6fce4a46de5de8d210559a6b743b6347b8d9990315bb56cbf80695ff936afadfdcc415d88b23ce6863ce", - "0x87ec5877ea8f1123371c49263dd9fedfbde41846a23e12073ef80f7afddf5a0ddab298cc02e861a90188ef1282139ecf", - "0xa3f1dae70745b8284b1353aa6902ebe3cf5580e24e02490d42b2f509ffec7e8e777fdce4f1a92d83bbb23cbaeaddac57", - "0x8ed5a0733b42482d88da7c24e85a841ece65f6066dec060bb267a8d1f1ec165ad5f7964c2908d3fbdc2999c580eb8990", - "0xb124a1db23f4875e0caff1c7f4b9a411564b93a9ec3ad8143bc7a70b8305d380b934c194de8201f7e3699e905a1f0115", - "0x8af58886d4ac5578a29c2e309a68f19a62edef5285d0757c42f0ec2550c633c0e991c4cd7a60df4523cdde40c3909345", - "0xa63fbdbde883f54667c6cacb356db1fb976bad147b790064ff25ae72be53bb6f4d74b22ca803996e0d95d216caa3fa81", - "0xb99fc9012ad938b36246a4471d29f0a2b37b2a3be6fbfae7ec9fdccbfd14d48fdbede0d88ef3b6cc273f2488f4cab55f", - "0xacb6cd4e1672eabf530d38f50ae651db8bc4025c2557c59ac4f1a278b4741f1e2cda978e5d1337f9e5aae77c95ccb872", - "0x8f8f6964534e4a9294c61c76206674d836d4d56970e9c14ad6835adc6b0d256402742d8a4879764569d9082ea6a750cb", - "0x969607ac6ca9bbef4fbc2fac22b12714a31f5d6103dfb998c3b6f2776283ebc7346e81a22da168af40752f28ff60d97b", - "0xb633f60cf6eb8ed588c545c04972ff156cee767edf31720c9715be3cda8c8de0290b623b22cb8fadb1690bf3665a7be6", - "0x8235bc2e818e4d259bf2c9fcc9646ccf83b7e238044e26be417d1d0dd5283d7b38c86e8c88a5447645be191516e2993c", - "0xb503052246ea840a4083bb4a8978029af3e242e831518bcca015f2c2df504e98a48c9002b6b9fbb97e861a0a3c5b4b5c", - "0xa145ac57d7c028c3cbd2a2bfea25caa35a9b5d69cb491b13eaadc2b0d927a590decb7c4995541f8f29089a2cbde6429a", - "0x80b4c0938058fa5d03c948777f13c70f46fc025d4d6c2f2051915b476eb0c0bef902374d784df57ac368c01e1fd51c00", - "0x92eb253e3b1770b36c4b2869a944caeed7b5c8a5b8356b25dcd4102df79fab8dd2c9d01e3253070f1206d149c43f64e2", - "0xb7979ad6187f7921e725787b0a99050f4c98762c63fa64a467f7f110932f6d07556453a95e3a2c0162bf1c9c41424c90", - "0x8808ae4c7cb38202c8c8bca0321e827580155197a700fa54b6a15b0f14b001327d4c9a0923168bb5afdd1b45d6a78367", - "0xb16a4ceee9de5f49a99430e18aefc192f3c1ffdc4b41392069f690893bccdca760e6dadf4127539a763e4f60aef37dde", - "0x8ac113da7ca59ca97d6bf7d6e03f1e9570867bed27230515475f965ce9ce0b424c85810e18a584ae5a3d5c2c80c6d4a0", - "0x847ae1b0ef5cb11be37320f3ab5e30f59d7910ba3d7cbf8265c74df25f4b8f56f1ac96cf49fd166c3b6985d1e8091e6f", - "0xaaa9b04f50ed6778e2481842cda30c7dbc7d462b40c7602a438ca9f2c1599e83fe6423f30d7789fd240d2e3166836f5d", - "0x8c18492569faa8cfa1c2a05a0edeea3f63d003e38d9ce23c4a5b31cde993a4ec88c9db83011ae15b578e0d0f6b72ddb8", - "0x838b400217af9241755032c21a3ac4610f77f3ad76abc43f0c59a59f9bd52f2251e46fcf1552b6ee0220f4f2902e54e5", - "0x8675f8de084c6c05644deeed1ff45090096c72c0db6bb2ceaf1c0d070bd10ff1e83b2dcd89b6f99bf132d3e131ef6d0f", - "0x89611bc63c83d56131bc2a8653278b234b4635aa7a05033d71a8377a5d188ffed7506a50a5c37a33d199a42b9e55fea4", - "0x90c290c17f1687a87023fadf74b1e10ad0c0414cf08629b2a313347f0f6913bbe511e5d18d1c3264b47f65dee7887d4a", - "0xa590bcb6391506035466dea82617f11dd9417c9f379d32b4c3bbf723840e1a3124d2327deb28849aacac278470d7ae20", - "0x97c55f459ebdf94ade7bc3bb18b329bbe2bccea345f0b4dc38cfff2839749b8f9365e8a1cf31722649c165e265344c35", - "0x8159d02fd03c1d0b3c928658b3df1a27a57699ed8a573e0c3a179e97f50b6c1a6467b7055e42f9f9c6c858459eed517f", - "0x84d4f009c052f3bf76b2b972b3d8f7a4b2d78605a566478670c33016aab06828a1737a36d3c9173583e7bed0aee84fcc", - "0xb99d7558944ac2d61f5a800c24ee47fca719e69f7284956be94596623cf434a214c042aa46d54019de3556540ea53236", - "0x8d1efbad46f69b80efc5776d8afe95dc0a8182d57318b9f2d6fb5b7d5c48e7181e6bd61a8446a553c58f7899ea7a7c78", - "0x84a9cf6a9d64cee7e7d8f0b678d3606c9080ab3ecf62fe0d6f994a681de68b30534ded61db1445a257b2c5427e97b36c", - "0xb6a5d2c55a23841a4263b10cdf784be6fdfe1b25350a4af510ca294949716711363ca19f9c44ab1c347aa3fcd60f0573", - "0xb1b5b6dbe6945db539fe7e2de07d222c88d7b91753118593ad9890c55c4c3d83b4194f886ea7f66ccbb348f5a23a2a22", - "0xa8a58169edd3e58f87fe8529f5cf7da7679807467ec707ab96faedf75085185a78f2ef912d9180a5e820adfad32ae4ae", - "0x874c1f416f866756ae3e93360342848afdea0048a575f977fb1f8a57325e50da122d3e9f423e308f0acb1b28fd47a6eb", - "0x95cbe8b47ec42a5c72ef7b1f91e3de0b1f648ae8069416c48d5529c9cffb104ba4dcbe87cc06e4e798a1b23bf1595f9a", - "0xa1b6e9c5d63ab1262559727872d1140b74a4f01c12366ed2d401c64007faf7917ec591b631c6bb4dd44b39aa43c7f965", - "0x89e6f4a05679c95d45b54e760056378a5eeacc72624eec8b5f19aecf8ef0d8acfb2d807d3b88c6b1206827203f219905", - "0xb7f7b30cdea6377d5f16d200b987e3b4a6f28387faa701dc579cf7b3c6887d74ca43609c5bc36414a6dfd0317ec75448", - "0x83474b58135f3e2c5e8355e31ae44a77721db71cb2919c3f3403f44903622d4116e812ea9ee9ca073938dee780f4aa22", - "0xa3e4cbbec770630c5e2f3b67059a55b1217435bb70ba5b5010244e241ad6a3e6b8d9261d8a0765c4b42bf795fa4e96d4", - "0x87d3ebf0fc03ad67299f3b9cf9c9ff0890b1d0d2d1a0ca2a62147444922d207663329e49898d79bd8e09ee48a1560fa5", - "0xa1d33282cb17c7a4c5cfeab4dee8875d324aca8d0513567c4e5eae180d1e8ac98b2ef16b31afa7c3f2ec25cf3e8bbd11", - "0xb10b6cfe3ba563b41ae0d66813105948416ce0848ba3b34b8e96547e8842086b632a52904e56eb61d93e0cbdd402d305", - "0x84c4feb35c8d3583ca17245e6f7e73cb488aed515c2ef671b09a04d8eebe6b7579e5b1fc8634fcd4c3bf8100d2cb98de", - "0x918d8fa2f52a9b3957ba412c24cc579dbd1f0b0834b909a6ac0da5dc602ceec17046f61b3d4a2658f724757ca8041fb9", - "0x87296e4775fb887bb00dd3265f202f31a8fdeae5c6ad8ec63508476cc57d330827d0d241c68091bb724a2ba921694a7a", - "0xa8908019d96c506b314c84b22c475157daa36016a9b94feecc4571e869918e4e5a9e39fb7c9ae0f73f9f868bdc50e2af", - "0xabedfabf75a93e7521eb339ce2e22e0e887f94ea28d3adfa42d1e0523686c6cbee4c96b2bbab3b8393feda1099b24d4b", - "0xa464d6bb17386cb431520cdbb3818beb3951b0255d72f58c300fd780aea1fe4dbce5532f5321e80e16db2f9b9bfe8a1b", - "0x8cb8fe0df930e1e19446ff0183c7034e35e33442da346df8a802160120a5f4d8abac236763114a650dcb1a1d38bafb37", - "0x975c47ea6412bfa97db9cf12c2b4c07ebbda436716aaa7253b2343138b36de6c897386833849f539bad7659d9319abce", - "0x8cf94457a5a708cc91bca9615e599b0c0afa92a7f2d9c83704e05a3dba56a90c4eedebb6d2d25b3080786e16c27194c6", - "0x950d02a5e41c8f704184c7c59715fdf3b48d86b53b04dff7c21738c7c38c9f4f75349ac1e70ca18a0744b01fb8b13504", - "0x9458faad893db4458b330ee283d6a90f68346332c99cbe8e121c890bfca908f0c91168072aa221c3c078d7fd5e4b44d9", - "0xb0262948c113fa2a122dc6208250b62ff35b12d3aa1e5735e95198424cf16a4829e9211c9edad83989c537572c5b41ad", - "0xabed7125de7dc52b0b42cd34fb350d4c6c45016319ab776b52289bc8c2b341a15d48165c0eb09511a1a5a5ed7ff39e4e", - "0xb4c352b4a127afb5b0833d210dc2c216bea666e7c5a940a3372988c0b02dfd236e4ac7c124664bcbf353132d6f061f3f", - "0xa334c5919909dadca50f3124de06400df660082b527f1f32b386b9216d021d38685f1839bafbaa7950eea6c1cb14bf53", - "0xa52f4534e9de29f91039af3fce055f2f6726fd9b10595a43ae41f7b466cc4ea6314487081e867ff4b5e35cd622fb428a", - "0xa68c6ba9673896bf49ed145935773fa50d95ec0103f97a6f1ed698d93b4dd78111325f797e47fe153fb3852f4590ee89", - "0xa5c456d516a557aaca80441705cda63d081181199097e83b22e9cf7b9947a8bb78cc476642f04a5ca3b13032319591eb", - "0x8a359a3dacc7b45da2b826dc27700178553f6a52e9705451f24c6d6026a0c597328acaa10b3b5a883b6353eee4eca594", - "0x807217b435d73c1374bca84d2d3e069db756176220a01607b81438a70f69232b82099c676fff361dd909271be8d5d555", - "0x965d0f46eb0804f19dd700d8721349287335c70e992efdfe89058ec424b87acccb3fbb18d84b727ff5ccb6f6783e9065", - "0xaeb5f2a0bff1e6115bc2fa73093019f8c679efec91d03398e24651be187265f7ca80369a1dfa61e8701385dc0ce9a0a8", - "0x85732f872228dd5d691f1507ba00cc94e054baa59a764565401e9e9b3287d2d0cd0f2af290b28b5e3c80da9cf23ded63", - "0x8e9a315c5b40e7cdb866b8a7e6ec01eeb27a52a76a88d5956ac3e66fd9ade3ec954acce816227b57fea6ae9244f1303c", - "0x80436457879607efd008f959cfd7507fbe22e417c701f59b5a36e878a04e51e87eb38c48c0992333656b24a4e671bfb3", - "0xa012f6d166cd1d98098544bcddfbdfa956ce60011694b640b012da3a0a22ac8a054a9e205aa9fae4df764ad60c65a6f2", - "0xb8225afd6e4d45520678e243d97bf48f87c2b8d2cbc24b43f94bf6e7f60b7768d4c3b30d28a490e7c8a1c3a104ac8317", - "0x8437fc2ab6d90716419f544a1d16c607173fae5bdc242d8224d7714c115cc54f2246d1062ecd77d5a9cd3ebed3a8adc9", - "0xb113c6c63125930882c18f548c1baa69a26f9f3dcfbedf5be41aecd61adb896ff9622ce038f0ed27a5ac602b6020740e", - "0xb893aee6291a3962fe17ea41322de7edbea6ebd51d2c564fe23ba8a4cf4b6270b7ac72c87f2cbca209be1ba607ecab75", - "0x92e6a7494114cb4dcf2b86ba61f57f6db7e4d52895ba6c896433139eb2ec9c9604f3e9100c690e1949e32f5b7e29de93", - "0x881a323e772a639553cbb401e2b6a255094412addcece2c99ec9e1346aea2f4e9eb247552435eab74799ee4c7a927b6b", - "0x8d5d3ec378922311374fcb998fe5a42176448b629a6475abe494fa56abd5faa5835af37624c138beeba649f7803a4855", - "0xb1a082ba449e93cc15fb4dc5114351437599fbd4d28eb6b4746d1bd242172518f94b2ca8b1f76c08d9f6ef260d9cfbb2", - "0x8fd2b7728a3c61cd8e0c607cf40e935dc45d52d040ef1259f62e3eeb30bd3a6cd030fcf407fa0b21423b23a795a02b90", - "0x9214aee5787f4666c3e2aff70949dd679d4203a2c3e7b6f88c548b80a3e52d7763f2bc2b7df714eef053f60eda4db331", - "0xb15df25b62c6f4ac9edc414ecacfe8eec055bb07a1220e327bf35c5e452da7620df03416a449197bfc8d948445c5f734", - "0xb41ff69731e7f4308fa18ad286d3ecd7be21afef3d32f5133a0bae877a347f8773c6e9d9b3b850d054236a6f186e6913", - "0x8d9d13d1b7d9df41cf5d30dd62b9d1d2c4933d62b6cf8d1830bd1ae4dd5fa3de36bfa1fc4d57681ae13996f85ad2551e", - "0x8011a7fd7534b248db40050edd9752c960ffd89b0300a91520759ad51da1698454affb4aa8907946605a02ca09a7f340", - "0x9159054fbc10164fa19f68736c2a683d374681e6e9d5e56f7496aeebb0969b8eb1a91e377b3a2928879147a7fb60b3e2", - "0xafd4980aa4661fe05bf9040f6551d980af562da69ec5072104d8ea34a8ebd28baa0b70e0fe3c11f631005693fb99213e", - "0xa92879cac7940c6d363ab3d0ba7f7f24bad0b16142c78969a737c27ebb09a62071540bec1822ae6224d943d02804da50", - "0x89338d27ba29343279dd83827ae17a53e7d634bc77bbd848f3b6a352fe92f6021dc1c81ea6693b3cbcb1f24188edc757", - "0xa2490a856c273b6eb5242672f817e60a157a1dfdf25b1d32e0f4836a9c2371fae72c93b94d78267b3cb142b4f4d7148b", - "0x8efcf5d06107554f896084e32e8dc95c49fc5da3f8c4be4ef6f2ed89914233eaacfea886040bfff14759ce28a1eeaf3b", - "0xa3516280b169a6832e997a4a45daf46aeaec1d8953387f493cacc2835a5791d4dcb24a0c0ad5de79988d76f843d79994", - "0x95eb7531a46bdc51acacf7fd9e7210bf6d5ca59b0efe58f79422394447adcca6f4ea991600e8558da8e19e029701c5d7", - "0xb1fcb4177f16187c76b421c29f715f1551ff365bdce9fe17b74425f76dd90fb4ebe828ffff3d20f75ac620abeb9381a8", - "0x886246027be4062258b232926cc82b6a51591138561ddd0173ec6e4b7ff750e15d9ba175f569c266148c653ac905d498", - "0x952c089dd09dbe531f2fd4137c971622fc1d85a78ff07de634f63853f62110dbae3646564addef8f2a070f5a16396ef4", - "0x812ed85f4559fb28732d17c8fd7c6b09a70da454a2318a0276949df0a5dd2714b14096656b7b5b6398f54c74eb9ca49a", - "0x9340db62e43e43144e1afb1da748e81a1b00f7b0600e8eed117e92ffcf801b9d89b494ffb003b4ebd5bb4e0eb96c9374", - "0x9287c0745b4bbe24b56784ac28bec43ed2abb6bb15bf11ba2b18b01801da7d162aef88e967d2f10fb9f52f6645d7702e", - "0x9615bc232ba6053fe86c6328eead899bd62c4f975273f72595407fe36ea43e30eeac7524bc17dbe78b4692d42ae81c04", - "0xa387899b521b1a89e860756bd0986b302f3c06271ece653425d6c697e0b330a3ed7789efe0e5a1b32e60257a12fa0147", - "0xb4c99909fbb92b1f39e9b2fabe05abf58af834b6c15ab0f62304ccfc5047f187a3ce35388ef293d2857b777f9938bd55", - "0x97dcb90d2dd9291366b557936931550d665cd05bb1b19a7a53a31c2a39d264789477a47ae14f6bdeb171e78941a9d9e2", - "0x81417b4a3e61ab9b48e0ff1afa8b523bf63ef95a6d6980092408b61f4293fb202395b10a5d12dcc54961370c134d5b0d", - "0x9135da893ef0a9d45a719207659cad4a0590218303d0e02016bcc5d14f54de5fb8de642efc7826b3b3212f714114600e", - "0xa00d0f8e2ea06b13f5a75a6dbd1f2ba7ce3f3bb3e62cd3b53f8b6ab39431fd2ce156a1aa4a1988613d4a2b6d91550147", - "0xa3f8f17dfdda07166a7e5503366dbef45ea6b6eaa1dbe02b8051dff58453f1ac24762c82f6db6de4370869f9b25d6d51", - "0x847c2b79076f9284d9a866a72f74f62fd73cccbe2df18c0fe34a35416d4825d364e24f95f728bc0e6a5215b08b6f0d2a", - "0x9816284cd6b8b35e1f5409d3a5899af5f4524a4826470fd164fcfe863994ee3aac77cbc16831f0866b9f0ae561903d61", - "0x8ab1f9feaa8ba2e1691acbfbd5460a4bab531344ce4accbabdbe5ba8cedb5d5fc0967def4365d755ecb62d83b7ffa4bc", - "0xb0cb477aee9bd113959ff7b7675f81ef251b76cccbb67cf68ba571fc08561736e32c18aae93fc8d1912e7eb2fc0ecca2", - "0x8cc41304caf0357d13a25ecf66336bece67d5d319bc5a50328a96199d7ca4fad05dbd7b5edda58be73141bb06e269c8e", - "0xa7b4d91a884abad5337925c34d7fd5f2aea5a09ff3c027cac98c646b5058f7fe2cbf47208930509e2a4eef1468f64c89", - "0x97d942e97efe46594e8fc86828ad3ed1c9133a8067f9b11bc0f4ee3815affbc0c7c46a91c40f989d50f1d8df96982ada", - "0x95a7d369f3ce7f7ad7ddf85bc994667ca25a0c2f11b9312d06654599410d5325ca3ea74f33f21b5aeedfb582a9e40c62", - "0xb0a05b564a754b46fc7aa4f5289f02bd9f19708b5ecb9db5c36bb7505c8b56ec22b53fedefc1df289c0f636c97e8ec47", - "0xab6e2801ea8bc600f9159d05a3b39e8b0973fb9c2696b3f2685424757a6953a9f8ddf5e29c97399c4821b8d7fd9f1bc4", - "0xa6fbbad2ad3ce8e4f9b939080e9e7049eba9f76b8ffb57f7cac2aa46793a064743239ce287e156d49cf4936517632290", - "0xa606632b62194aec737403ce5a9b6316178c1d27baffdac83981baab63e75d51caa414ea92465ef37d6d687b4fd90141", - "0xa5a99b7bf8f4c109af04c31af9b5f3148370319c8483796cbb5ef555ee1d4858b2c1acb82ab5e26180254399fd7a0625", - "0xab2b00f64355ad294436339636e7764403b821d4dd4fd74a6bbdc2aae450f14d7dbe8423336e793a393f4580f1b9e35b", - "0xa6c98a6ad7f36f16633fc216c12ca34e596b292524753ca1067eb75ab52facd28ed3a7c55e0a0cf1d3c9115a2a0d6524", - "0x84acda31e618eaf0424a37cb3c386585a3870b2c24020550a16134ad8802d427c918e2854c98e5def58a2363a8e1a314", - "0x9911ec15af39af1a18003ae120da8d909ad4bd43ff03078091d54de71de70e19786b2aaebaa5d55d9b2877004da2c271", - "0x8cb5a148f065e36b67a219bdb347a625a7a4be8f20dfb1cffbb38fd4d843c2b1b1886c1f015793bbcb02af04ed91b170", - "0x815d9adf22a36533fd4a3efae3c4326213ba2aad48724ef958cdd6f0dd5059b519e12d91ed5d92f1418a07b62b108bfe", - "0xae5c244f309467ada13e2fcd8942886f563bd996a5c65aee73a364c2ecab49be3ba6bc8a387f3baad44776f4f1042eb8", - "0xa47d93b35f57ad890239a6f2f69ef8760268adbe614d5877802db4b6cc75cc093baf101f75be0f7b4d71ad8724dbb9f7", - "0xa0d089701b965df9fea938e337016ab20e0e567e736e6652955f1a93760b4a9f128be5a594e71df8e7db47c3f88c2fa7", - "0xa9d9a7170a860e2860f785edbe18ad909ecfa489cd3a2abc580869c7eb8e9a2db93c1c473a5f1474ec0d51dfdedf95e1", - "0xb665abdd084abd292548c336e3e6fa1c5ed1a53d2e61a10ad6a4c66487d8a9e101632ff468b012506135907f0896156e", - "0xa10ccb363b26beb9622e1d91021d08a3bf02bec96a059ead01961ad51610992ef03558c5f77e074442836c9d2ff44e0a", - "0x96d6476066264eb3090ba3544dbfec7c8a0d90985a1697985db0d04773f6d37d5899a9d4fb5a3207c320ca78c37492e6", - "0xb4290ff9213e2ecd30d303b2b4ecc66c2614b8df246e70ece4e55bea9a1f5a0bae9df6dcbd8efdcf8c4b0f2f4cb44d48", - "0x8ef10b2e53e6770a36b6403678ffb86f5d85e3e87bb1b3ce9f1f0cb0cf32f1fe991c565595389ad83d8c8d54a47dcc82", - "0x91f950ef60014e3dd28f7661e6275ab6f085c803988b7d6dbb2cab25f10b0372e271267245761e1af97da6f48c230205", - "0x97c626e7114396daa337ada4f08da5129464d8e8c68a407c8798949817337578733fbcabf454a22b57926485c28d9d62", - "0xb596984b609a9858b1adefd15a546d4b8a417c8b54504efadffcc805caf8935b9c7f55d9e6b34592241195f513453572", - "0xa3fdd36f3eefffe0cd2a9e6cbfc4eb9c3a499eec25230df8786b23f5eb71efddde062940ac23d5b2885081da48d3c1c1", - "0xaa1822db9ee136d0a51910f0a59bf0d2af6819e4ec0b859b790e01bb08c1def87e9613b355525d4ab7d088b520a6a3dc", - "0xa9089edfa96fdb7204a68c4ffcb7e0a875106886a0c589dbc57a6709e7822747affb07035b99d056baf11d0852720489", - "0x85664ab9d32ab0cc2d2e61901b2682f88a7259c2da4ae6263b917ae8afc232614b4ee56539a868a24940eab74142198f", - "0xb90e06a1a117659b52b364359e2265daaa8981954e9a9c37e3256cbabf133dd4900974a895dde6ec6b394fb36b5bc1c8", - "0xb414aefaa4833283dce85add23d1cfd776567735f2ba9018cd791d652bab55bb0cc0cb38b88fe47e3b4b877e63edbd75", - "0xae579eae9c0b09c906cc2824eeebe5b4ea031547055c8ad635194f3e864c7a184dc21a3eca9c43c01d9a2f272cb2ce81", - "0xa7b1d13997c283c13f770d5203cb09b5d3ca7d45324ec89c069928e1ed1a17c57510e0ebaaf54a21d27b0f9f057bccec", - "0xb15d4555520565b76ec21d87e094ece2e04c7c4bbbf560264da37604f1a484ecc3ce8143b04759fe716411293876d0a6", - "0x810bb0773c06caae8cc06ffc92303d51eadca1e1b0acd57ed23f5feda70378e180619f68b8db98e61d792568f49a8316", - "0x87dee32807e2e5f2c884822b31098e5be2a4d950ae728e3281a39e661937c4b7e9fc025b50f437f01d69e5c33dd751a0", - "0xb46810bd73d077a6b73757d22b5939c02a3632e81287073b00ebee30cdd402e89c318e0b03d01fa331193842f3a1ae53", - "0x95a136a7bdca77f764d2c2d4795a8fc9e5b9097d73bb3956b7a45b42185a99c949db8ac5627ca263206cab9cbecbc31c", - "0x967eee3c3afc138a482bd120050dcb9b45a9fe258e5e4b678b1d67b4691f4c5d89cd260210fb50f9cf2d3e2e2802968b", - "0xb2d59a9ed0448b88f8eb26d8017a129ebaf27f11e0a031130266796e5f777bce93cf2c7e0fba8f8ccc997315db9aeb9a", - "0xaec708d3093b12caf29efbd8afe3ace1de24496cee72270223aeaefe4f0ba3a7acea7f2f5f85c1f274aaf5188616133f", - "0x8563ec52704c1c7ab515451a8f89f87201d30a12c95812ac95fde2af033e5019615a07f28b540a92781ed35786b5614b", - "0xb1c8f819a4ceb17d35ab997c14f81ae2af9d4510caffc61d4a19e9129e0bf7264482a10f329054908f99909999b6f538", - "0x8a65668637ba24358800076d8edc90979d6e614e6a683dff7859ce7d686014e6de85298f523ab060c9a9a4c4b8862cfd", - "0xb4df02dd6f4d3908142654a42af60fef034379b1526c12be66afcfc4f1177991811646495aa85702f3461060732cce80", - "0x8991bef253f0bb9b86e68e81f78116c51097004b0309e199025e45ac7ea55f8f6b2bdc58886899d275424ebd405ffac0", - "0xa74f1048548fb41e57f679d632280fd2e4cc6ab88c81675c59fe143b74dc7ccf050db53dac5611ed6b45b6a0b1b7f3dc", - "0x92011c668bff7ea995a71e4774e3fb5d521ee2552bdc33d9a65afd9677572c2a303a940751ffea470af898b01b9285ad", - "0x881a0e6042771492633b46b6101f96a48a93aa3860533dc207cdc90783fbe52b4a9ade1eea9117cea004bae802cd3fbd", - "0xb3e578bfd77a3a13368ecf8139b69f729cc720aab25853cc9e2f505c2e03e75cb779d685698af8cc4aba8d1c17f5ec29", - "0xa025b6e8dbeb68e7ac4a595b34089fed0d24eb29a7be235048205e35a97634d6015ab24c21a017b5012c3175677fd0bb", - "0xb751acd86ead936ed0f22d770872cdb5aeca3b1ec75a5a1e65748b665f8d1c859b5620d761d5f0a2a86331188e82b2a7", - "0xa05faf0bdb81caada6c662ed2fd145eff5db5c423258d6609bfd4c467edf3ddba6480ab95ac9f4dbc932f4887b070c82", - "0x8fd1faccaa7cf1d59be37bad69b7a99b7641cbfe930d778e0f712ae1fe9e78d53f37d7d5d3aafde48452eaeb65d980b8", - "0x86042bc710953f0042940625d8b69ef57c615f9631fc49aae169ca595446e9d55e149c92994d4bce7b544877d7b6f22a", - "0xb396047f716c5fa8ca9234c7026f1772d83f41be03410b4a32a376e5a038d252b8f36cb813bc3684f1b50326994c31cb", - "0xa2eece2d76db005f5d95f5f480bb3353ec67a9c27896fe54a2cd5cc7f802507d8d518596601bb3d2798842b96fc03df2", - "0xb738c1264d094f7b7edd27b0ddd8e29716c73bcf7b450ad7715fd21e1052998675873ccbec486fe45a8f72d9b006f239", - "0x826c4c5fea1596e353f6c15d91a9bbacd9ea592aba4d22e735263062eac44f073e5defb794f8ae4afb7d4dbcd1ace959", - "0xa8f1d170f63ae3b05ca9996347a1b3987136e7bafd02774698829986d48da3d421d269d31743bfd3e7917c5ace7ce729", - "0xae6871a8278f24d816657889ccdef509df0fb941fe6c5839cbfb704e81b942ea2a324fe0ac9881b385bc97410fd94b0f", - "0x8aa6bb564b6a0354be89c4ac10309f941162fb3a546259c5d789d4608cc628f69ecf814b59bb8bce364162f7552e628e", - "0x8ed85481cdc58fc540384213dd1b86f80af8908683d7d2c63ef5f8c4ac2e90f0e5f4e07b1b841eaecaab1f7e091423bf", - "0x88741d9c9d875e2c1ee5b95bafa4d8a22d72a728260297d048e4f0cd1c5f1eaa94fc233be3fa15a69163f218d62ab17a", - "0x8a99655974ad5c0f27b49d88a9c52a5375e16b9ac4f22b1e1bde53ce0a21589022c0ea926a4c2d7c432a53656ccffa37", - "0x8e2628878858764824471fd613cf40d1bbb3fa84ed081a762da0d6d491d54688723273d87a587ed1d3067976ab74fe1b", - "0x8f1a6162bd6cbd2353265bb348311073bcfca5a86f41cd0c63ab91b14aabbeffade5ae8a94f8e91faa386223fc2bf849", - "0xaabe8cd92f0193d12b032a9bab4bf4f02ebc0b24d1ac09f8ca8906621d6c7d4bb436b2dd879a1a1cca2b44ebb5642995", - "0x91cd27988ae8100d48ace10ac9cac4cf1cc8539bb492521a8a6489f8575a737f2a1d37fcdbe88dd651179145a59af920", - "0x8baefbda554bc0a0b425f2e132c7de061fdd120ebd452ecff0d78cc5bc5b15401997231727a37e9bc4abf1a553a4cbd8", - "0x971b12e25b989511477c04602f48f584485a0a0773b46643190263c0288c2434969bdddb1e55dc1f5b1b028c1c53eb32", - "0xa0e47f42444a16e51323af6f519c0dd2271a85746882818d02373ba33c2e2f7bd6a1c321497377e4781f72427fa34224", - "0xb52bc02de867d7b20cd247cbf496e03d940be2d7ca5755145e9a0168889db345fa9ab17c41635ab275a459fc9d02ff16", - "0xb01db7077e9f01e675c62f5095400cdc68a059e1a5005027033ac535a0505f45f89faae4fb9831f7ff9cbad3b55db02d", - "0x81ae065f1d55f4643a2ee120bc1245b9730455ad9e5402df8d6fcbb1bec71e40f1bfe7b8e67f96fff76d1478cd3973ca", - "0xa1be3723920044be80f398279e2f8432aaed45a36cc4fc71c87f5dbfd52225379e94600793f40aedaac2391caa57d155", - "0xb682f74fe46d4b647196b7c14804dc0b35e36cdff59671d7164ece874107964ff9f76c29b23c190796a9a3aa2df822fb", - "0xb8152e458970ab53f6b5bf6101008c5c31d2f58993474eed6bccda074555f7ad2351810d78676b62612e7eba2d86247d", - "0x9132a8fab2010360ca80adcc08b3a01658dc8ba8f60bbc45e1144c1219f69b985436c36c65cd7910a8aebd91ea1d3d38", - "0x805cd373a0919de801b6bb7a6ebf55530037fa41a1993c159e90213c492165c42b5642dda5fe7283ac4e3ade6e63a155", - "0x91f20d77fb7a8276174989faed41fa6da841d35b074c4a756c2b4730a7efb9b124ea6c7d5eb150a8b1126636cdb2ff0b", - "0x8cda3ffbd0ab6846dbee6cb8c0360842837a65f83b6ba17085161a7371a4466172354e494a8614cf2f1f4726d0a7262b", - "0xadc603e61dc36ee605dd7f2761ed568bf91b9dd3d40903eb7d77b11d10e4f762694fbbbcece72a7ec26976054139c768", - "0xa6accdb3df5029f19273a39bc30cb622f87522ca5a63372dfe61d993dd783ca5e918218b5c519d25e535d8b8238339a2", - "0xa188897269053f2494bd0de8cf098e41010fdd01f5a49d7ddd7b294ea748f1139e0d92fa7841dda9f8dc923ed6f02615", - "0xb26ad5dde632259293d91109fad4f742ab74de91f68ed2416ff53c060d1ea4377a875b2ce960cb7962c37a5fd47e85c8", - "0x82cfa86a17b27f375172d66b389df727734480a224b91585fb4782401d6c49d4dd347b8d1e8df6b9c0c1d2f8ae658de6", - "0x82911748e1471bf5d7fe3ff111ac06dcaf5b8a43c76f6583ca491e0aa845b61cdd443613c5728863c163952d86bfd482", - "0xb7b0d4ff87df02b5481183066f6ac0d1636718fbddc19889e92a71a168fbe338ffe780a792ec5642aaa4024d0964db69", - "0x8ec21f08594ad38e9ac365e5246aa5c2c8e34ae66382ac483b47771c33390ccace4d906695b1ac0f1c9204c46576946b", - "0xb9617d746596b26b84f2709a03b64fe77e9a10d0c85535d92d28dae9de3bbf6455a247f775dd9f67061792cb924e3925", - "0xabb2ff3f16309fcfe0a3b1bc928ca5cf618706cad3645b029bd54e5305682754e6ca47e364ff21b1750f45041eeeb358", - "0x867abcb8029b35a54552c57346024ae7eea38e9ae4bdbd68bb3c1de3935126880f237d9aa95d6644dba8ddce67e343e7", - "0x86eb4283147a9e595d639f29a967310acbed9ff09d9043868fd18f0b735d8619eb4ee0250764f35a51e00b58543bcc66", - "0xaf1779d2115ca7021533bcf55a100b4d3ff4e45f8ce6a6d98df22881526a429d97818fa1867ede09918a438957a03534", - "0xb10b36d0b69b0dbecb6f7efb6c612b0462c346079109970a26541a21aa2b5b81c1e121ed0d5c81af00ea8eb709a83dfd", - "0x911f81ed75fed55f1fabc5f86f9f38490e006820e5380963a739ebc0f87a1dd3b7da8c69dff1e580c5ad2246bc08e2cc", - "0x8379449499da9159cac2c09c61777955e61c63378d051bd28b59c78409ee5d09c43e7a6c246572bf34233a314511bbdf", - "0x84b48ec8895049bd03dc3256bd0d63f6e9abb178221f7d47703b447c709fc5fda47b19a3439f30f10d2670194f390915", - "0xab3bb5afe824d8aa20f97ead4c40aaa93350f33d980b5783cf56c8552a4298c989b7b188d023711a2eb79631f3a8c317", - "0xababba2722186a3b2272feebaf2ff46c93883b7265a6a4fba039d5fc0e7fe81b7d4dc2cef7738406f156f693ba3a55eb", - "0xad50302a51eeebe63085d3c1705eee9142bf8717d07c5d87e0e4ef5a12207dd5432994c72b9493f9ceb558a20929c9f6", - "0x8bcc3d83a6b8998e1a1066347c647ab122eac80c9c505d5cfbc370f466349671d8da4d500201226c15c1f62162efc62f", - "0xaad6946b5d5df34ee6f7422fbefc6de33dcf4461868ed7ee7f47fe9b8eb2f7a89759c73b7a029d422b02afd0f550e722", - "0xb0fe1d9a30759d83084b4c567b586e5a8f5a080bfa93b4a3feba59edaec33b6a2ebc98ccd82aa9d8cf0bd254d5f03baa", - "0xb993c4c2b77fcfbdb213bfd5f8d655d1d41a52583de63b432e2732df2f9d88c4c6779f314848417c06a089fcb970c0f2", - "0x842ea3aa645e5852695405b6ff2184e55bdfcf50be2319761e717b7b52d904ec47ad3abf986850c643003442e302ef30", - "0x8093b0ef1f6c84a8253d086a6fda6be8376f925f416a9d1f44ea72489f60fbd8b53cee616cc5ece43e2a202653c0640d", - "0x8c75f10b6aa848d84baa4120e75d3edb7f8471473851326cbd9ed7b29b22c5403028f49430bfe4320c3f4227827e667c", - "0xb4fde4f20ab98f76f55afd533f1b09ee4ffbac9486399714514fd694fecd0ad1fdafe13b2b80721829c7a59e4c951a76", - "0x843b2ed867cd8edc2eee84497dbd49f3dc481e7ece69310d06225325ef032a4e72907e16e7b6215ca775f88983d55e5c", - "0x9881e5caa9706e4d7ba6ab81525090e29ecdf1808931f3f2b11ff9ff5cc97f83f3e14fcf18abf18159c3fcf4cbc27042", - "0xb6c4acc868c05c955eb36a24652314be37004bfc14283600523729d466c56018c99a45a41ec0389449fcc3f8aa745638", - "0xb6820864d07715dcf4a9ece336464aeef9ce381ca7dba25acd48f60af056a3405c22792cdc57c641e782896c0ea05b25", - "0xa1bb482e35f71772486675cb4ee0fa5709b757083d18a29d4f4344e6ce901b2edb2889b7eac92c498b90c7d3844c450c", - "0x8cd8d8d47de859d0c68bdbe1834a1c9a34e92636600fc592a08f96d66426c5f41f388138f42c9b8ad72c596f4bf85496", - "0x801cc0631310656864b25d980c9e99a98fec2316414819afeaf182d3e7ff93b32a989e2ce63f5ea9301745080854188c", - "0x8fcc6b2b656f7960d9ad48c091c1ea71b6f0f61553f7695049c770afd509ee58ca8e1dcb403aa2c5acfbbba58676bd44", - "0xb997b9a6b994e3eb2de8723ec485d8181fd674de19ac9c2f50704785d9f5a28fe3ad194eb052b5ce122ab5e6e6968a70", - "0xa909e7002b82b371952ca9d0832f531db15882180e97c12c56da649fd65334904fbbc3f097b6a954469221d181e718bf", - "0xacfc712e1a61504814e37b3aad0d7a5cafce5901ffa43c13bc5f70507800ff03ed261367ccd09db7429cc5dbb892a7e6", - "0x8d634a07b69ad87e41d941aca08550ae9cd72fe31f3075511d030c364fd6578a36f3f0f3785d19305a1e772486ca097a", - "0x9746ce2d890248002c1bfb755e06f4f4570cefa7636e10319bf491c654b83608766e95fe9c77f1a6a630f5add77b71f8", - "0xa9dfa56bf82297f709f1b4bdbe4bc194bf22c0424815bafa6c1a536f2d15f35bfdebe0867ff20781a49274075622861e", - "0xa723af2702c6b473caa4a64142464f201bd1e2f765454fb0236082fe3ad77f22b4353e5981e6bc37e974c7ef797f875e", - "0xa42a1a0c50befa6864fa35c25a17f5309684c53257376f8111fe96c84a5e09376fad9c8545e1946f360e16e1e4c941e3", - "0x84231f6bc3038320dc13f3ac014977326dd13e5b2ba112c084d366b5255729b2abe665aca8a41d7aa6645412765887ca", - "0xa64e21d651bed6dce8dcfcb4caa60791b9345cd7b6a100f5bb78f7423fba5ea0d0cb3668f3415c27af29ac35e5dab0ae", - "0xb8eeb2128ea14d81fec5b1103d8511a3dfdab925212363c75c5cc01515fd94be8db2335bb84e221654380e58e9f2be67", - "0xa92e9cb287981b33a5e697eb1e757bd44f45efdda1759122fb27dd4bd4ce3694f1b6b2082ce4e6e3919d9d7a0b7c8a12", - "0x88f22b83fd9dad63e800b0bef709759f380c6dd9af7058100413e7b09c7517eba258d6367e0cb1a41b7762b86b2ef137", - "0x8353d45a2096fb4bde82ca22381bd2ed93fb58b236b16e68bb37df3024672067c4378d7f04a4da4d116e7d57a2211f7d", - "0x9076205bf231de091fcba7f5a4fe1d4a359f07236efa39f5715f206e5cb7eb3d9adb56af8181f63a9d3e965dc909556c", - "0x93ab7f56e8d37b47d3a8cbd222f2dab4bdbf94a1152302752f0a731294f4dc214fdba17977f11aaff2eea9517fdd5789", - "0x96d9883ee108c88342befc358325356dfe5d72c521d71e4b3a58d6773ea3d1a1de1a20572aa96ca0e8483eba62466504", - "0x950e0d61ce4e76fe0cdc3d59c5bf23d8e1cfa9d6ee13b9fe41e6ddc0fd52081bb16bcdd973d319c20709ec517fe15626", - "0x88809c1e272b552d46137165e5396917d107547b65059fa646b742489e8892acebeccbb3eb8f2d676e3836c985cb1756", - "0x945f13ff081b74403a19dbb04173780f04766f7624ac6b77f46464df5f4f3b547c459f41fb1842164d8f1c126ad6be65", - "0xabfbadc599bcab1c2b7cf1fc5aac7798d9f617d6afa0469ee23230c0d004fcd3de0ea645feddc74e676ecab1fcdcd8a2", - "0x83ea1571b064d05e1b7f4527b20ada121024a4b2dd8f7d551945488ccfddd671ed2ed3895578afcb3cf958f9a2c75c29", - "0x8fa75050bda001409f2bc0a275d8dc0fefaa47b3a0ae132758bd711eaed0851d6bf3e4b7f355377a93fb8eb02b3ac6f5", - "0xb2fff49083bb30e2661e2d8978149e0d0588dc972222f46d5d120d01dc5c9978830c442827c8fa295f6b8e6d8c786198", - "0xa352c2dbe4f18b311bf0690d77fbc9439a1b8088c806a9d89071b3ea04ff387325cdc04a091d2bde5fd087bcd0f4f482", - "0x948ea89408826ded81549cce823dfd7605ffc2279ca7d0964b1ab3d5f35f4b174e81575291edeb9eaa4baad3610ba3a4", - "0x998073b618140b04ec394ffe4af02df044d923a5cbc8f06f26c9eb4ece17abedd4f72e10c9738bd16863327c0f6ee20b", - "0xb3bfdda0d6960af897ab508bd9312d9c166157f78b45157b46fd2e38ab2e430e8a19335d8a611366cf74642bda77bc78", - "0xb8dae3e2ec5eb97ce3b5e9be719bb747e6e8f28dfb1a6b7bf5063822b502a5422cd586bacd87ef83c0af081ea4d30a57", - "0x859713ddf0ae843ba690fd8177ce6c08e2fe5fc1c8893d829d39a199e04758719bd3046034926de40973a992ecbfeda2", - "0x866f150d4b6a015b03ce8ad93a70644b55ca1818a0f50d24795698c62f3abe59d3b8abe4c11ffcbef20127d3b7afb970", - "0x9145367ce9e2a5a6140db58cb097767b5a6e19eb36d1c03acadef612af95eba80048f2b02c6fb46eaf38c75288e3e4eb", - "0x8c298aee778f4af13329975754e9b428e127680f26be139307d43268dc63892ac98284d78ced0ecd384301e26d5b63e2", - "0xb4c2cc9256fc33ed09531abd7c3e34f8f24830a8a2cf2d684cdde46155f43ff2715c94e7dfc7377765ec0cdefb21cd2d", - "0xb9193113b81bba4ebfe40e97be436515254bc67a94939220e5e69a197765bba40dac3369e5cde115d1bbb65e1c826038", - "0x8474d72b7cb52768c484ff92d014d7733003b511c0c915649f65dfceced47ecd933ce876eae254cdf2f6357ea865580e", - "0x808e9a59f947b2b39af51deab4c164878e02d95773dddf1123091e27de87cfffc07aecd7c9cf3e08c0b9f525bd87fff8", - "0xa8e0049eec8eb70c12446596ba5c8a29823704be3753312c34cb271000b6c154b1022812dd02d1352cd263b655437d6d", - "0xab7894a75e40d888a4d0539582cfd6b458da009a5017e561c14d312335a75745ce134b57466fd30c250ca07e0529c8a4", - "0xb30c5c0abfd35ded7a3da8f9c95e3e1c320857be1af317f6ff5e35101d3f31de3735ff8741f6460ae1e63cee543081fc", - "0xb15557ec268b4eba9628ccec0a5f3c947e624b61edc876e2ad8c36ada061fda76f69c8afb95270b85f4672171678d078", - "0xb7ec103d6695fa64107f66622148902019ff3acbff7b77ad80993bdf209b73990b0fef92dddc5fb66aed77cdb59af9d3", - "0xb3d002f0a35808e3785d58d0074be620416ee9381bdbdc889805ec2acfd169e1ccb60045d87cae3e90d5da94cd58bf80", - "0xa17c44ade6eca0942742edd237661ed406a129a968fdab28a58d19308d207a1e7853099a4a3c1c181695fcf265107a55", - "0x91fe5c0d672fce368e229e735eef43868e31265502e2876e54aa44470a257d1c126ed73d6df860f42d8e1dd425d8987c", - "0x8434fa331278fcdff2c8c07596a051847425fd7cf09af31bb235d208ef6e282cae173d6ffb73c0475307453d6133ae7e", - "0x940188d6c20924edf1d9343ea85ef9e08d9d87d2a188f8b69514a22cae10aa2d3ea8e662d43d60b8b77183b3c6e8cb1e", - "0xa89f57a730437fc511e1873830b300df7a417493a468afeed2f837f31641cba04924effe11be92d3bfabbad0bbb7d04c", - "0xa561550cb347fc9178c875ebd8dbf5d14c0afbefa79f7b93b893a25ca8fcdeb0293de5a350ef63413aa70745cbce9a5e", - "0x89fe7dcaa6a10cdbeee9d0d3bc8dfeacd47e1490a6c3b591f66d3a64ed668e6034381e0ea9f5f04fd2a5d9ad5044b8b4", - "0xaac54b334514d41665b80b2cf18285391f47be820446e2272d69edce022f6d7689c8e137e2e9579d0846bf5440d768c8", - "0xa231a04b942d471b32cdd12eac3eba00b8910fca0812c9470802246c479050d6c860f64bcdc6b6e39ed0e9609df9239c", - "0xa6bf6eca52b5f3ffd89b79be6edc4f517fe9c9bc67051179157734689fd63649e321d1fabda916a9c4666b64ed60bb4c", - "0xa7c4f791a1d77cfcdf34c3b73ec7a43aa1c8ec81c39ce81d12c51973ddb0bfacc79e1a128ce17afc5838982f66cede6a", - "0xa1644b337c4398f00e9ebfed20d9b2c900ccb667be036abba0c4d372939f881df2bdb5d40b64354f65c8f2ad9ffcd656", - "0x84f6e86481d3322de791ad01d8c1556e5480534e52970fa601b295a40270882476779301d78bc2ebc323323ad0b62253", - "0xb32eb2beaaeab27e190c9d381b9f3446038391da552db5ded0f5b58d070694f07c737315a465175da29e2a236c539e9b", - "0x857029d97cb9fcbb67e194d9aeadf5b25cf8184b3b704ff5da424fb4b39abdf3f7f317b3f79c762605bd9bdd5823e7aa", - "0x883926170997ba84cf45691c117912f6be5c691abab77fd18fe114577e6dcba18f8c0a6641ef59affcba1b2c92e093cf", - "0x945be3febcff77b4238500054a053c983add7a96ef43cd91921dad908c20d4ae08857fb93a5bb588e9b441aa9a536567", - "0xb9efb8be322722302d1c06640f772596fc362586d8f2e49c41810f4bd2b59e8e9abf3d5369b2421e1ce6949c067f07be", - "0x920ad6d5cacbdb46af424141391817da2fe3d463bab8db760026f98e50bb51aa4f3668520c133ccf9622d66eb8a60e86", - "0xa1a9ca07d8d3a44fe372aceda194f15a2dc3d29267aedcfc3fdbadff0bab1c4397da1049bc0feb9097afdcf1cd1ab603", - "0x935eb5fe97d580c10766bfc2fbff71d8584e00e1a321018540c25f6b04791b63a0d6992257fe110b0d17712f334c9b49", - "0x9530bde6dc33e48e05d98b77844766afc0d5581922e382a2fc1c183adf998c8137df29e56b868c7892b2c1af56edeeac", - "0xa8cd3698276c2bb8d39ebf7fb5fec139580755adbf81bf362e1cc19f4a8be750707bdf4e1fde3064873495cce5cf5171", - "0xac5a83c82004728b34677bc6b1fa507687992b5b78745e5820de08f3fd99e35c905608936ccab62ae39f0408334b3c6c", - "0x927b0077386a5055b499cb5a597ec3c9934767343fd91214fbbb5487faa4339837eab52c75a627d7addc5cda5ee35108", - "0xa8acc2ea4a548d9a2fc2738abcf75cc0efa189b92a99296c0635d53f2c0d7ee40ccc8ae410d2779f95ac6f2027c81d06", - "0xa74c24b8c695920b12a86ed6da6ecff72f8e19fb06fdfee9cd1c1e8e5f1c202d26fbf2fbedc9a5deaeb2d986425477ce", - "0x871251e8d69de5c3117f364bb95d876fb89974428bc167666088d5ff1b83328b675ac2efa2d0e215831e69ee254623fa", - "0x946f7a6d3d6700f65088c817636ed3c1349e4f5122fbc22723d131d8ccd055931dec977cd0cb8dd888c6abc51a5f4194", - "0x82f7c1dc3f133725570c7b64e31b0397fc3a82cb4966948803de210182b9716ccd19e59c0e0382c0c970d05c5e13509e", - "0x8bc45b43102e0df4767156b1e8ec635cc07fd629793d289be1f2470297e8a084bc9af0d76566cc485a8ac898c0493fc5", - "0x85000f8c8130abca642ae94b4feb3448390745decb1f443c34fd06575f1d0de35bbe649b46251df0a4bdc7a8bc133b2b", - "0xad1ef07d34c59afa37fd5147646c24c03622ae4884c163b80d45ebfb5fa994699ad9166ce1ef727c22be3c28e0838cbf", - "0x8d1dd5500229f463f94c611bb2674640d20f2d34dd40b28c4d2a21d3e64ba7355fae55228f1c70095d1b288828a1950e", - "0x834cf56a4f2c2eb04b89383213b84bc6ba554a4715c3c1547278e5501102f6ff2af27cce0f876a2aa2da57b5ac6f3b3f", - "0xa468d06083d770bb4e484718d1c147b49770757b5b296fc6d6035ecb3c2f5c4155176f12ccbe6616184789350403f387", - "0x8abe730d80ea895705bf67ac4f6b6a36fef7403702d8458a383d04e4859b4c8c7a75598721cc75793d29276afea27ccc", - "0xa3890145fa43e6b5c7b8aa0a73a62c39d623c9a75d17c5a05bdddec08d114ab5b0a865c9edb2be6ef31c3dc9544119ea", - "0xb2b7c1cd0aed6b776515a12a0f3a86353fa3d3a3b6027422bf7f2c21e6917dab543e189e860c8fd3aab65484b77efbe5", - "0x95215b7d3d504ff83ae2bff789feb6b5919287d354d567141bae68a0f0d27b3e898edd8a9be5a51c04dd28ce9d4ab937", - "0xa93a3da0e101797c690c38a5bf5bc14e10842e48a18c9888807b2233809ea8a34a76d20a8ece0b682d36c086853cee40", - "0x849a7fee901a9279dcc36fe8f276ea6dfc37c30f75b679ddca2cae9c283de19c4df56790e6ae12c4bde33e837fcbc324", - "0xb5c1587d84b0826e64438d8ee7c103119b164bede8d243a0256b5b798240259dd63281b81bfc613a4874a6732d05e143", - "0x97600c536388c942e0a72ba3bc33b3af48045994a3ad0948fe0741391c1eb99693d072d1efdb644abcb08e10474b7885", - "0x94c2120a5b4743496e7ab9bb2e474580ed27d7cf5b6fb132efcdd7bf934434d2be8d6f0af009c637b31727b3ad5d2280", - "0x8a5ff1e7f552fa8b34b22a220eb1cb018c9c9430f0f14a634121923497cdb4a69fbb8b60eb33e5fdf9b0feb3e9f5afe6", - "0x8b4c9032f25181e6fb9f60eb07e3d6cfa2b14ffdd6a0fc1b309b078f8290901e229a5a6ed96dda74e1a9a894224ff588", - "0xa5e04e164ffc46da1dfe026ffdcd99332874a110cd168c44762c461a5560b5c098ec71673d509fc053f6d9064d4ba255", - "0x97d21cf8327a81385fd3915c7e8efac7662f4b39a9785b4a936fe1b581d630678f42a3e9ea7e02bb4413da7ca9a6f35f", - "0x806d8462bbf148eb4cff812cab11b3d819669ef5f0d76b228fa166b83727c92fdac98ff3afe946855685b050d9d4c6aa", - "0x8a9899b0ddbcf4ba3f16bb006218022efca867a5b32e1de9c7efe1d7039c8e200a406bfd09ebb8921bf1997185e9266c", - "0x8fad2d8629c546c5de443b36927b068cfa333c8c4c1328e1221a1f6af7be5363ab8981fee54307532f239eda7656e6f2", - "0x930146a1f6c3decf40198955059f70c98de7c5bb1b25bdc97fc72de3a84db1b121430cf7a7456a692d8bbb6b325b6001", - "0x82987887016fdb90f79f045c16629c5b2b17b1b4702cd89d06b70086e5922cd10c5763cba6f3d30a2c33bc84be36c6f5", - "0xa6fd7e4834f7f29da41170c13d29acbba86c74d5924cd361588cdda26a3ea7f11ec34c31869537ff7ee0b57a24555e9c", - "0x97b2474cbfb632148869a6b911c2ab91e4af9eff6c181566a1eb34a05d2ef3fa9da4fdf14e8fd8746a7c3123e20d572e", - "0x99ea177bb7d98dce25d300b09bf6ce08a7061360c4ed9a54e30c1aa5a467be6225737b62ae921e91547b5b9d39b800d9", - "0xb9dae836e37d51c9611e6522aa6aa8bccf2644f23113584c74c963d79af0a7ae533af823215fdcbbd8df62f00ec1505a", - "0xb1a7165aa1ac480b4eb1f0b3d4284c69907d1b5056a343a2da84b3863c9a2ec4d757493f5daf9ef252a253bb3b2b6745", - "0xa1322eec41b38b8bf3f4566bd12f9c230dd04d085e0526218489e986d59895d471bd8bb08351edf40021efab1d29b2d7", - "0x96d559df46015e62d8876f4d8679f9a9867dff31eb151238cd75b3a10bbb2ab0f51c804a2f5adec1decbfa355042a6c6", - "0xab55e38cd273bffaa94400bf4913ce0ec1c1c848e8c53be1808d4ce5338ec92b4a4160b8faf0d1d8ee8b71ae751d0ae7", - "0xb61c2987e2b402a52670abe305f8a9976efa9720ad0d7c5c1d0d6d9ec6f1569f51621b6edae84d9bb3fef32bae31a088", - "0xb5234aa19fd9e714c7a9f3ea33d39a5c49f42e7a8edabd8f306083669df4898711d4b50b049dfb91815588ca60052673", - "0x8e98a7b90baa4693c6a1e1c2e556d018c3408bbbb5dcf2c32d120f797fd8ed1373f1f112dbca114863801ec6efc1a5d0", - "0xa7e1e77cbd6274f8c74b37a607cc20596bb7fc35ff1ab4358de15b07952aea397e409b30188c8516676cdd05d4919f3b", - "0xa5f2336ed9338772b71e490b1b3916d33df8b013e4d38dd57185b7314ec9aedaa34eda2733c38e06e656a8cec74080ab", - "0xb5de079ec867af3a3910fe47628c7d793c7d70b79e25a9a436e0a75405e2c58b740c1b86e1b073842d475e0b717d0bd9", - "0xabcadb7a09173f1eda179ab7e3a5722f020402eaeafb9d604641645c21f1e009b758f2a6fd262f115d80e23f8baf7328", - "0x8694ad59d4cc328b064884d147f66095605d9bf339d09e45652d68de765f2b09d45558d45daf9b4b36dcf881df8d4fb8", - "0xa2cc7b2e812041f17b450b5fa7429cf62e2da06a7bb3c08a63d6f802ddf13e8b73d2056bcd6407476dd322fa35b9b065", - "0xa97b0e7e22214f329fc57b6d7ba882ca563f863c06f1afcb60c0bbc81ef08ec866d39c81a80a7843889fc957d532cc0e", - "0xa8a809392dbf35911df8566dc20e2373e2fb3272bd9eaf9f474588a9132f06b5a1433ba9f36a738c6cd3fee403188fca", - "0xa3fb0038f83116eef1d6b023e2e17ba2795f7f90ed7c857d9f04337cb4e0c2e7d691bcea54aa72ac5e4383125b74b755", - "0xa80ada835fede8d121162aabfc8c349f685775406693d599e3c288364097b02d96c10ddc20e72fd308fc882e5b70c064", - "0xb6e6c4b24731a2895b7513ad97c0928efeeb0c645dac9fc8cbb0a6419221807073f6996f2b778e1dcdde63acc3a6b2cd", - "0x880a2e8fc2eb57f44b08cf4db5cf1751bf9f4aa688708039007d2a198f4e7f0f808aa566b36b15b971e804835102400c", - "0x8b3baeb4e1c1d7493bd885dde7873afdc235b58e45b515cf51ebcd02a9b81911c5ca182a9e340575585186c99e71d2bd", - "0xa6248e1bef3c6c6ddc155dfe95631a3f00308fa77b1c1779935e76401e750f151b7377f9376c08e8273680e924382af1", - "0x800133df4ea65de3935d98b0249e335a918c44167a34a16c0a4adaa4654f458c376eaa76ef088672d39aec4c7d951833", - "0x8317a6e0667fb524f35672e070f047db29450b06348604319765e4db09f966ad995098cf38acd30346c7fef5dd62528a", - "0x81fc2ef2ee0e6f21f406c51f02b9b7be8d99d30a054df918cf89c708d64c34d8b0dd060dff4383de858c0dbff25d71d3", - "0xa28611f96138fe6974e3e1925b582cba76166259c32b39e95702fa0c4957ef2ca32d575b1c08cc8dbe96ddc0eb56a9f2", - "0x86c6773f4e0261413d6d3944e0f7e498a6dae518120e3940d2f45054a912e706b3b615fd160e6143a7e54942406f9af5", - "0xae91e3db099d165b198d80b6d9af894203949d87cb980f4db97dd43ee55fbe1a45df156b72e3c3e9306975f9e5e62d77", - "0xad00ceaea52dcef616be9f9815548f8e9b800bc9c1a8832a4d8acca6c8779317d1951e5700e54db070a23db41266c934", - "0x94426f78470aea2d82eded320b45bea09b7cbdf02a3d7c2af4ae4567a3493b352b36f43c3669237879910dcefcc82fe0", - "0x8aad924eb1a30d2844654c9829d82c65fefe964d815572b6c9f902c6a826c247257a7d0d4967e2bae331d52fb3b7c0ed", - "0xac9489ec928e4f43f8d194b8f3ab83382b66b045f18efdfcb05c1d4e67af7b3745ffbb7f52cab4b8895550d10132e2a8", - "0xaf8f390c7cc40a08c0143b467634c10e8046ce40466006a4b4297c76a6c16309b50f41a4a022fc838738c4c72edfb34e", - "0x923b0384e87a2ddfb7a2c47f628172e8dee76fe812c44a756c67cb20527d8e9029a561bd4ef446a013d4be7db7259f6b", - "0x856316b53f09a90af770bafb5c9ea7deb921687fdfcf512840e96fb83df08820c42263c9ccf51465da33f1b03db04d09", - "0x92e8823b523f90ab75ac6e30869dcb257d232b55a3e167769ab5b54cbb83be94cf5d84eed4b1653db17f3f1350ab5e53", - "0x8d0d05fac92079a3df86a72fa399e606fec7e56f81d3443cdf0cd373b3330235b76890197ae61f24d17de39dd1aadd06", - "0x8a801fc71b9b6988a829044060679a7cc3d40630fba81f72bcd15c0e5728867f4bfe938066e68cbb54b042a39600fde2", - "0xb40a6a786ca1a21159b72990b4d3ae8729722cdace4e8124f8cbcc3fa96005563535d28e9d92cda02e91d979d27f8f97", - "0x914f30250d79829919c8ed184c2e471c0d9835f2348e628164dbfe39a51dcdc3f8bf99c945b1f413e65fc5424014e5c2", - "0x8ab8b347b7846fbc7ffe69c89ff67dafd522bec708b7ffea312b3a7eac47fb9d6006cb9038962a07dd89d4688ee6a18b", - "0x8e755f8cde0750700252e41f6d16b825e7f02748a13744c004a52b19e52d58c42d1ac32cd5ed1d6ad14cee5174b4ddf4", - "0x88d6192d72e1fefbbc9ab400e5b0018bd300839cf604cfc1034657f62fe8fcfc52acd86c207dad0fa6383361d338b2bc", - "0x971fa2ab593578b341076d98c49c71dc7d9eb4ca706efe252441499037cc86fea49af681d8a4d324d302526b2a3e5c18", - "0xb2deac648501d7e284a85c19f514f8744c48d2b5516c993c2111128a9fa042aed34dc371a0cc3f00e918531dbf16c0fb", - "0xb63fab8600fa531d7f48f8d207298544d2e03d4da23cfb43d99b0612f1a20441526de63b7609f5969429e763147ee5e2", - "0xa8f30d9b4ac3675d61199e8e624f88b9dc52658a2ba26a2bda5f9cd3780f0b1e32b56c825d9dbc3a059d6c61fd37e261", - "0x8a6f8e963dccbf1db9c839c21a4e832c7a218b00fc31400346b5379fdb8394142bf8f8b981fca3f4d3c43d4e34dd3e31", - "0xb4883e6a4213c799abb2a9b6998ebd4c89aeadfbabbe4c363b22beaff46939dfbe4dd20d113688a293a41daf5cd82c8d", - "0xaedb55058fb467ee9556a3b601af86962f99fc06f7eaf837b4deda030b1899f565da07ddc7108e9f5e7024e11c723ed0", - "0xa8185aafdbd22a2df2ea0f0cf67fc88c4c3f8e64040da08cfa9e8075b792406c20d3155d6ea6fdcbe9f5502c44125545", - "0xb2b27ff20d24cff756e8edbd6f8686d202d687016c561e56dcffebc78f404ff544c4d3ae8802b91bed0487792d6dfd05", - "0xb6fba06a70d8b1000555b8c6d791b1db3fb7f57a0f8b1fa8dd00b2ee14242877e1e836cef89be3f9e0565e61a6b4c275", - "0x92b3dd6e18600ab856c276bc787429d42b8c02abf5243f7919625aa1f4e8cc3eca61cbe106b81d0e4909393a5efc021a", - "0xa508e1a1d4375f5130c95a169fd1d4df51cecd84822dc28b18e464c2189d464e6dc6a5855e0cbb94500d041319749ef7", - "0x84b3e9a6b5d1a7bc7df44ce760b5b686fba006945f6e1f3f67ea2c90dfa6ed70bc1f021828a0461fe158ece87deb1e30", - "0xadd83e686118fc5eb56d79199d33cf0c90fb2a5996c6f453fcd9b9eb3a273a466776adba1cccd6be62a4ea154480fe17", - "0xa1fb58d9a323dcd7862ad4bc6359ab2bae35a608276a3053d40bb3abdaf3e8827027284d964e51ae7b61dbf299f2bea3", - "0xac901ece7cf087c782f75f1c61371f77ba061bb752ad680c9b1012768e5ebb6241b492bafd9e016e989cea1ff51aaf5c", - "0x961b9ef616b7faa3befd807772893c7c66ab6990a9405cf4345ec29cf13d75dbb6da41ec87af5b5c4bddc8787b88b480", - "0xb386f7ba0b94ced118691d883549d70ecd28d1c0d1b718cb82a92a246e61de4ba80b6a76d6039c261e342f9ac136941c", - "0xb6415848092dd93da62b5a5307d356d968bd7c935d3626f40e9446573e5794f37a23ca072fe8af2a9355a4b04ad35e58", - "0x843b3e3221bb08122a1e649e81759297d985c7f393c36cc3bc707a7aaf2f53b9cdd449e7a4384981c5976fb3955871d4", - "0x94083ab99a73dc5cd463b5259a0f4e99847bf32ae03739a440f8f48e12f078602c76b3fe4e7ecd31d52a7aa31168c5ee", - "0xb6f994b5482aabe833e388b24b9445c01e47fd6e354c3684094237189001290aa77a327181e7e7e756682a04b8b3c56a", - "0x8366f418a3fb2dbc9ffb5b798adb968aab991fa689ec24a4c4bde6f046989b1815e1bce5e846f3554028e16799e17281", - "0xb8e5680915eb37153daa9a3a977b47c88b4f30fd358901888a1056e07d2a7070d28a47acac7aa7856ede16bd0c93ff2a", - "0x871cc7a122cd7b9ae2199801e6a0974ba8cea64e5866a5130ee0ec926adda24f91b3ff2785932cb55537030bb5ad811e", - "0x9370ff1ba27d33080efb22836147f766c60f0a8ca250ac6b2a82bb464ffa543da056284b712dc3cac53dfd1680a4cf87", - "0x8614d8029df5058f5a072716489f734131b228972ea9b2b952ab1150bc50b6637543aec1c35763f8dc578275f7c9df3d", - "0xb8efd01dd0016a27a0e2df65b571d405be4dc8e0df5dc0d8354fb187b96589e95847ba0c2856613924125d21193753ca", - "0xa86e524431247115ee497c07ca2a73387eb820d293e8bb74e1ef1ae7ffdb21a9dd8ef1a6e3f391e6f02ee0b51fae2a06", - "0x9151e2dcc0b928573421ffbe43b1761b6ccefa4ecd58be7fbc8ea8e975e18d52c264f682104480d590e6f8c0b8b9f63d", - "0x85ac8cb79fb8916f7eb5431b7e81606b38afba15895909873f85d9577c87ed2c1d0fd489fe058362f20ac05626681346", - "0xa076dd75ed807bb7afcae8bb9821ed46758c1a8d00e7f3d3c91a18e6b95dff3958ed70441a1f4691ac3268d95e243614", - "0x89d8dbe170b9804de3fff5b6512d04643ea0041c3f9bedd7432b171ced1577b0c0a7bb911852c6bafe154ba36cd30320", - "0x809a63ba788e618a281804ef97a75df39c7115900078a6bdb203bd79d3df87e863c631e934dcee62e28a16cb8735acfd", - "0x9727e6720f8b73b6ccad519d8ca1d4f90c2db33ab536f399e2c4ce269be15d99e22504ef153aa26c40d4cfbc450f25f6", - "0x83e77918ba6e28ee01ba6b8dbdd84c53faf65446a90bcef46f262f341dace2e237b1ff8f8d566fdfefc6973deafde716", - "0xb5a4d3fff76905bbb229d579b8433e76f2f070108230f20a30e4f974f12f29ed017aa66e9b298a4de0fd535a0e1a44dd", - "0x876d3a0bb439e7da26539b98abd0f7e0b7e8035eafed08df623a77fdac30ac85ab4d58984396319a88e072dd7a5149a9", - "0x98923e83be5b2877ac18415f9391ea792933db718b29b6970001682cc8434ae9fc640427c0a27f6d62af5f78f3901bcc", - "0x805c675a34443a14c0098613d11b4c015264e038a8d1adf083844f2e3e3f2414689788423dd0ff77c02130331d511068", - "0x8d8cd51d4146bfa48492e9d3f3e4b845d4ad1442ce6bbd95979f9778ffeb108c641c9ffc2ebbba532f922237e5849222", - "0x839862454707a99eef931335e5c5ed80805ba06bab0337c5301fe9fb92fd59c9ff6620e66de7369352b079dc52bf2113", - "0xb3cf3bd867f60b345a0b91314b34ce1c02e64dfbaabd70782614208d32fcb5d4448102bd54728fb05d1ed18a750e88e1", - "0x8207a421d010e1c5854b8e41460c6a13035ee77f7add0df83c5c31bb00d7acdbb676478a7dfc738b9aef5c29d345ab63", - "0xad2b14f87281ad6e1d2b713e6e8303f1a45cefe097820d6a1bdf4652364e70d28ca92193e2bc3d0a1e69da5a51c90ff2", - "0x98025be2d7e59ffd3f6c3c2b28b27ec42206968c0f96d09330598fe17a207baa6574aa22cc26555139766cc284224fe7", - "0x8e80fe898b7fee849f7dc8e5eac668c76f1fe18d159c51eaf4ddd8d4d600c852dbf6c2abcb878c64f37db7fba3d56968", - "0x871c0e2dd929ba4e157ed606741a6301aef759e10a3f919166faab23e599d3409b232240e3afe9c0e1622a11cd453c1a", - "0x919f7e465b399e2819ec17aacc199421d267ff2979ea8dc8962542ddbae51e2bbdf6cac92f8a35e05e4d95a4a8315cd4", - "0xa6e6667e6127ee4f0224a9a94be3c22831a1ab3b16f57462562b11473c425e7112b33bbbb6af860c81bd6e84bdbd3b86", - "0x87eaa9e3515f2d94acf113d77dc085609d06cb038f5e8e90ed29bd04bd4814e95ed0d6db5a1d65572dfaf73ab2e50ba9", - "0x90b30c66ebc16f767f3f0bc1d8bb17ca1951a616292297ca8dd06d54cc53e5fb5fd6321ce158c04cb4c91a04c01f7fbb", - "0xb5fda3715566188630f96207c4253315a9cd166ef96651afa0ae1d6f0aa8856e7642e2f8ef3b1fb1eb2c14a7331f6592", - "0xa54143f662a6946da901ddaa9e514a0e96bd6397020cf5d88084a1e1edc092b94facc150b1c029a508fb3995acee50b7", - "0x8dfdb813296bd105d5813657c98337a24c8bea19bf0d119efca052c018ff5c88f31e05e110fa12f306ae4b0a8498f113", - "0x8b7429599915ffec755060d9cfc2c445df9184ba6bf298bfff5b54c2ec8747a9b65bdc6c73746a94a54b0a62d93b6a28", - "0x8a1d1108174d383465a57ab4b1a6811ab86dc007de4f342d37f4cd311650382e0352d3664ef09cf1626c0b74e2f21ace", - "0x98cb860aee0b7251da2d114b2253daf977badf82027a018c956fd59c6c93b716bfe69a132a4778ee4b7168fbfe390ad2", - "0x94d5a0d33a0aa590fe76c71e80b21246dd9bd8c2f5ecc647e47a423c2dddd743010484cf2fa363ea73bb217247429066", - "0xa082b7a109fad08e2c01dd7322625c18f47497b32269ae4e529b1681aeeb3c4a813cc6088ebb4427b486320fbc4b7872", - "0x86c23e2d3b23244c7763c123ad67a41a2dad8e4556cac23696906d1acf5f4cd7f661281b8ab2027d268405b08eee6771", - "0x801522a5c211e49eb96294a9113022d86c84bb8741e44fa7328122836a39ba7e11e27d0d6773550b234531400ba1e7eb", - "0x9683d154b18ed641867fe67b2dc70e8b8afba79f73fdeafdf9015d85aa0c74d270b290952683c3667c0202a83626687e", - "0x994febc16f8d216a20774955523262966e955cf964950b4b2831a3483f818c20ee6f51cd24f499dda0d3191910a9fd35", - "0xaaa8f12184525e89ce980468fd24e1a9af846246297546655763ecabf0b5b5047394543f1791ba1c70e21637cd815877", - "0x9193a37d5692ff1bacb0265bd7825c479624d2adf33a419b0a71c8a744ca1b0c9828127831302ffea4fcceb1a53ccd54", - "0xb9f3213d5d588ad73b86365cbcf0fabcec5c30cddad418281ff2408dc140e3f6a25afcb6bb569605191665706c675e35", - "0x96aa280b2f0ae5c3ac51edaea4435ecff8ecf8f2536a3400d8c4c9b12c64d16418838dd7ffc1b815656109ca63261050", - "0x8486373d67804e9832bddca04a0084d1976d324d85c22a52ce2bcf7518f014ad00e4795e61c71e0dcad1f23316288dcc", - "0xb4f2e7f7e2ed7917e7c5036681e1ceff18b688c1abbd203c2bda0731ab56701a847cef4f753f68119110680913c2dd4c", - "0x87dc2336d88edd81b94ef78e7bcb6d3876257c326d28b3f4484465d6c65faa6c17aa7a2f85c6b94ddece39f6736751aa", - "0xb4b3502ebe175820f53da8e3fa28160579c4150d79d932923739aab545af537b3301d5b21f5138ab4100e737fb61a084", - "0x88063af42d5845267d979df07be0735cbb42d9b57d3625eb5d0aa7e4ee90ca88fa52aed480a4d60eaf0ab8dbc4f444fe", - "0x85cb81247c09e21de6deec42e668b72f513c7b105f60ed478b08b85fdc8a886a97bb7e39eca0cab09b294e4b1490b0c1", - "0x9920fcfcf836faafd211fa1ca78302aa6feffcda98aadb6302300c250fe8621b60d9c214ea92087c44996ae0999eae78", - "0xa1f91af5b378d61ea277e5dac81cb71d71a4ac35322aaf42b3a8aab1641fd51d8da1783bae0e8ccb66d73db8e1003478", - "0x87507b427d381ce3906e372a12f4e61514ad7a102334826266df14542adcbc8bb7c8450a1fe110069d9dc2e9bf0687c7", - "0xb7581b0cb549d71201583e0987e9e9bc6cd36585c96664f836e1b7326e5375ce8d0a450343fe0b106dcc581b77de88f9", - "0xb26504a6a7a64c44d7f97d0402bf752740934ea4c6e101ec131666deaf574d55fd7f96c8807473722b6629dbda2ca3b5", - "0xb90accb5c6b78322ef88d017fee2ae1cf87194f4b3f6f4ba6510c0adf4c11b20870043cdaf45372844f5e801464bb682", - "0xa904dfa6e1f813b4aa0b242f3eaaf893da7ea854efe514487a237a01fe244721482476b81ed75ef1a951fc54802b29a1", - "0xa00373aa8d98f4dedf9cec4d227b5fab00f3af2a7bb4c8b0dcedecb5a04244321d5f25a81d57ed0ddcf293c701d290f5", - "0x91bedcb316698e73f43e9dbe0229772c856f34901fa4c1e018e96eb898e4ae02b19d900e87d01501099163be56db57ae", - "0xb84dd6b9a61cfc0817da422380b0dcc5221deb600b4b6a6f6c5ad934110a3b66c59f7407ad68bf8642b2bcb5427e8050", - "0x8507c172e499856675ba69fc1b0389a08e58f8e5658c9268172b926dabb4a67b7c836a44d865f736e8fcb14aa2809529", - "0x86609a1d82d90a971786da9ad342035ae4865136e513559069b6dc8ba82ec0bd1ac695fe8afa5f61f85c2310194014ed", - "0x94914f127a645594ed372855550ec0817663224208c127a08bff3d5c4f463b7939cf13a45dee68586b678ae453c6d60d", - "0x80b55565972213814afd6ad9b1884a4d8143ae90c148ba730ca77b0937c2faabb23a6f985dd0bbbe05705fada4cb1a00", - "0x930f5fe58dabae91c26c6fcbb61c3e336678dcc35d028e5c958d2ee7d50b80e1693c0693b82d719dfd9fbe2c03b52c10", - "0xa45053c493da932896d95d5fb158869c1051e1bf99658b183c9cf4415fc8d4fa1b6a8752b8bb26e8b706a03a57fc05d2", - "0xaf7434b48d2ebe639c8082be09060422f662317bdc136d534b76ee3e3aba5ea8f234cd4936aa2b928f6eafdbe5165a6b", - "0xa57a073bbbb3020a92497f0ce854666997a182f2b437e7b06c9888db8acb2fd2128e3959f45c391f0548a3de49e37e76", - "0xa0ea8131b2d8cfb799e806d8cb92cb02d32de37869cf2ac3c82f7c5d9a963d562755b16d25c4b60f4ca214e323790a9c", - "0x82f920aed42eb630281919b9c1fa4acc02b05ef34020cad3583a29375bdaee167a47ca3366ef065cd8e658301942dbfd", - "0x8415ef32a93820618abb91329224bc46d478ee8749ef42e372ae4ea29b6c05a65d5ef515ffc7d720b2f41ccbc040f176", - "0xa0fbbb0113daceaa05478163fa835b070be5898dd9bbfa9abc582409a7b671c0e41a5070de4cb6dd2072888b11825acf", - "0xadfc99221d7f044b57ed40f4ef8a9e47e57265ef8eac654043cf5e777950af6fbdc2c2d5a5b916048fab1c19acd69dbb", - "0xb3d8e85fccf623fb3848e4886d580469bd41ec0533975298bfbedc7a1a9b4e554714991ec4238d8ff976a83cab6383b7", - "0x8b09702f3789ae1f7799ce58a0ffc2327b3ebf2b56cd870f2be66c0d6781cc1f34c2d721d0de63e0fe9db85bee842fbe", - "0xa935864851b73676cb49f509a198caab467e5dfe4358e7088d2a76e9b8c13e5d20b01eb7c0cb9e51ee98c90cfc393c71", - "0xb5035d76a5a8251bcb18f33968b077d43403c69492b809eaa3e202eef174a5649aee30f701ef0be050ba5026957093ab", - "0xb1cedb563cfb09713009b2263975a56abb9932b8cdebf10f7836c5c34785149e9875ff590fe1414ad2d21da977b7ba26", - "0x98a718c23d44b24ac295b328d91ab7a40b23ffbccaa90bc5888efbd32b6a95c530bf5e999ccbd4f1c85263104f336ce9", - "0x8d9d2ee952d5b135eac2f06f0478faaac175f23cb3789144f3a490f2ed34c885ae4d8ad7ed48db85cc6c2bd70b38c6c2", - "0x8155763582ff6c68d7071ba842b6543361cd5f65b7c70d5bb838da2dab2c02f3363e2324307e7d2149b12700d96bde38", - "0xb18b277334ef7f24706b7d48fb764a487bc4e21fcbfb01627b7524e9a5d3253be99d84c417084fea769b550b3ecb4574", - "0xb80db9d83cb1ae861a3f61197a1f14b6c5004a2b3d031fb207adda94d725f3e265535ed7b69b9c801f2e95e1d64c1901", - "0x82cb673ac9c0c124fc546c59505fe4fdbc05a1fece0fa579f6a6df96f74bfa877ad82b6fa768cb678ff04ae4cec58d1e", - "0xb2e190b785a4a882939489b86d0a06cb637b7be8b14204645bdd9d6c37626e8623e35e1e4eab5c8fdec0f8349ede8918", - "0xa82237c64f15d306365be19085e1c725cd148702fb66658c7974b02051b685715fb9e35fd4a596ec24d532df4711f82d", - "0xad6f7e3992518ba04b510b705fa6b28e3733e0000a5480e8a3c30fe71394de2bfa43333c69e750bdc3e7092b9e0f7ffe", - "0x8c0ee358f37c28f3b80cb9ad5487c342fab734886e31e30c667e616f3aba737a3a07bac4da552d8405ad8b00c03e09f0", - "0xb7851e0b88486b0a858a218f4307e0c0c8c314fc69e2b90cce8ba86d3fdb796b572e50eb4e82f83f73c7f048484b45ac", - "0xa7c35abc2e15723a9f395d16d2484b798d098be5414ddef083c8283b0c29823226fbc4727d9cccf96e33b27fc40e032a", - "0x8ec5ff2ba7c3ca8a2d18df81d46e93a3bc94ceca88134ea75cc8ec2ec4b1ba3d0de49dcd4d385083c648a63483377fdd", - "0x80ca7ee722c3253e7b534b42a8947e38741c542dee1d671b603a9a743f5ba2fa95f193ace46c01000ed20ea05ad0639b", - "0xac14edc2d803b28a169154364dac5360cf0926d911a615077a94858fb4cbbe31bae2f30a6a68b248cd8bed015e0f3b29", - "0xa4bdb63e91fa72995316d03cd117347cbefd14eb1b19a0adea1c9d39f49d82ca1ceeb2a4184187e1dade109d10b83090", - "0xac8f528e9e8fafde00e66a75d4bb68c99029456ae9b3b7cc76ea4816e89aca2b8b7d094db214bad1e87dd4e84d1c1a5e", - "0x8a8d090a01aff14383419735840fc5286e71a5feefb98c563b2d7ee593b518c3aef6654f10da8a77f40feb52e1d31fac", - "0xac4259562982b355fe5e57e1cef574a6a40a7144598c13a6bf07cdd8000bfda95b0b0b44f215e9dbc71be114a1857441", - "0xb53741dc30b11fdc6c9778555c1f714fde60890c191a0effe419fe2b6100228d07cd0738d0dd73057cfc7e340c75f0c4", - "0x80ff52fdfae53dd2410ea556ea6504696439919687d2dcce1e952d9d17b6e3699816ee623b0153bb0e0588e36b6f56b1", - "0xa92b34d785a71d10e6796ad07df788c6878717cef4f1f0623898370725006d46fa00a0a22a3934fc5cf323be85fc7767", - "0xac1cc08cd1a8fd6c946bbe14662b18e89725933a79965c663b73ae3cf5f5ab87e794559ed579564884e430e108385e18", - "0x88b8b2264d84106d38c321c3a4927b9b41cac172ae27f6292ea44cd9ce11d185d0061a59148e50474d4dad3c9e940476", - "0xb7ac9f257b4f676d69899a181b45f40358dcaa70fa2dad38870d52838aad9001f3a3145f6550fa2826018952431e4cd4", - "0xade67b3d1602ab0af6a256f25a65b621dded7a0adca65c526ab34c5ca3088a549b7ccf76c586993cef0d2d38af541617", - "0x8fcd8bdc44ab42a70c174682a1e8b929004834d4962a902de460eaf8649883c868cde1cd660d14d7d3ce589fe3aa83ab", - "0xb914f6ec60f1767a12fa34a4b400ce102564dac4c1c42f1497c7bb824bfb9000c9e23ed7cadaa16ad79d5ac906070710", - "0xabb1683b313612b583e87228384eddc3e2e7539e0aa26e825f5c27da222941b6a37ec47127cb0f11b6b8e0d02a6f66e9", - "0xb01efb31962345a2fc71b7c370e7d3117bb1d1e1a9b6984ce11bd83c898dc127fec2e821669deca7c74d406e4678a736", - "0x92439394c6c811d908b05c626f1afeda3a0f8c925747bedf66a4a5895ee76e7445a1982e99d8658117128df5866eb64e", - "0x956bfdcb00837be56d44f159bab9bcc2292295ec1ca7424615e3b163b5d14f7143e214609c0b65ab74a0dbddbed4d782", - "0x880b9a8dc9bf6499f1f71828e6c906e2ae59660c9aaa824a6f36116746406351b4e364b6fa26c45e9d90018555bc7dd4", - "0x83f4a0dcf523d414e023075ce0dde10161d65c0abdba522c811f0e446980cbc21eb0bb42737136bce30fcaae3c673b6a", - "0xabfc5593e02dff15161c4da67a806af3170bb2bbc65e3a0457b4bd994ecf5e001d02bdd417655c2b4433dec270a6273c", - "0x99c6d8bab7d937a4cb5c272c4bc3856a3cb8295cd77ec9e2fcc6a50e0545999cac4c413c3ca8e5408afdb60388c82ae9", - "0xb08f5d230713639ec98a7afcb2a25b9b2d1c48820447d28b6a3ef448aedc4b9a90b6c5ffc6613a70ff1766b51992074f", - "0x99d4b54e35dd3f844088155f114ef93507372ed32a6898b9954d5a6d0743e55d8e7de20d67671454d26561ed5e4fb05c", - "0xb7cad70deba1622c79f1ecfdb2612e380e9048fb6146760ba61cb62e98cef129d3944c5f442b15fc11c102fcc6e2adb4", - "0x95feea870c86525ed214e3e0ecca9f66c5e0babf6da8473e5cc5e2f305c26939f6afda0207bf5855b6a6c928815577ea", - "0xad6e77ec226053ab331f3f871d7fb770ae78227a85096d263bb42915299147a7a7b57a4f8f929765cfb323267b94865d", - "0x82339f53ab7344f8dad554fd0270c2aedb34f7b0c630f0a56ca9217c04f0e4a38781eec769354a44fa90f556b388ad01", - "0x837d4672d73588f19b872d81b7993e5e0628139f5685d0520b1b766d40e71b9d83a8d2bd65a03987eef89b3d5c254683", - "0xb3c27e19f579133f1ded8c066dbc3e4edaf449a1edcb1aaf215939d63a7f2b250b9b7afb62d4cd7cf37c28da81898a67", - "0x91f669f9db8fbc6d7a5ee92cb67c2fc1ccef6dde622efa455dd7535b11f506f4e309a8878b859d6605a3917f6d7d67e8", - "0x8332dc636222829a83501a8312904096c2984cc0c5dc077e067d8962bd87666226e3324a9e5057c1cbc3ba700a3b22f3", - "0x97e81e20bf33baa4412d6b81c5fbd406dccbe70973bd73e956d8ce85c99d2199daee5fa6e99fc6d40071b352b5044865", - "0xb716066fb9e470cca4546a401048c0e6c6408c8c9f4cd80aca6778d3f4121378e11cccf8a005845fcc8dea2e1b9f16df", - "0xa7b340eb603da43f2aa542dfad1ef3d3357f583c46040f2dab234c8246d7c55d6885f9f7a14f319e22355ad498c22a04", - "0x8281ea97a28ade9a0cdc73a077c72a92810b70912006611a00df8e7d2ee1036af73c0f062b367f3d4d75be4b9bf78aa4", - "0xa481ffa0813a4f2110c6ac535fb446282dce73c182eb99baf786ad42b804ef12df078b2f534e3cd8210973880bba6a63", - "0xb71a581ae08eda0437f9e9274c1f9431d6b357e4866e40d4c2470252f0888978497af823dbf464785479e5f35eb89aa8", - "0xa07c9010308bcfb0c97a1059d5213980000841ca0565697d45aa46e82fb36494e4940aa435ede417856d24f73d374757", - "0x8fc353fa8733947ba067ca2bf5e14a6c334e4ff30efdfa67829dc86f49424f4548e879b153e79dc75f1ec00afd6693c6", - "0xa663faca50e1fe5d00f62abb0b7828d6b761fde9f5a54f27c0b726d8d53281f83ac165b3d3db87f970913350a7dd07f2", - "0x970535269744905640d6ab238930dff375ea7efb2f391db324724166f0c436e7a3eab7ef6eb2e5d6724c58d588a4c592", - "0x800f33f5936498e16fd0f58210a5a5c104074039db7d9d5d92dc62cc00d796ea0a3a22e5d368fe269cedcf30bf6149fd", - "0xb4b921cc901a7775df7ae73e97cdd108d98c54534015a1469f0ca6b07989827e0d3f9bea2ec015fabe9d309054aef802", - "0x93295c8a7e5c0bd9decd99ee2d704d814cb6bd0061404fe00984a8afc337e78af11965a8560288529c2a722e8b54b488", - "0xaf43d382ff7951bea94f4540a3a2dbb53ed527e966d0dcd117d5212f36112976e1fa00a47bb9870d3841cb01621c5d7e", - "0xb4d106b21e4676556bedc6e7f5a7eb5c2ad0d5fe8004a1d968bc7806ba871e241d38892b1fa73e9648b23158802ab57b", - "0xa96cbe38f86165288a365efa796b0e2076ae9fa94bb6377cb80c7d5db9d376e9c18164a8a3667dddb3f5b847f52fd319", - "0xa0bde83e1f3e925561c481ceb58c7575027f9641e69f14242b886e7fbc532a2bc54aeeb94ca39bd7da3ac984bfe8cced", - "0x8211c4a70d08fe052246d3ccda60c9e9677910a93d9262d572606d99e273c1ade353eeeadf5b1e3c1ac3c4b9019d5f61", - "0x954ba6744e3f991580b6633e5d184550e44400f20f00149d899d97bc4b51b01d09bb4f82ad975cd55189320523fd60f6", - "0xb7e3f17ae79c2faaf5f3cbe0dc528c6aab0035eb3f38954820556bdf7c3546585fb9814717302c5f45fde7170748ff63", - "0x880446589f33ffe7ff5e105fa1c380d401d6c46e80526948fbf4edcb779753a594f3891461f52eeb3f5f2f6051c361b2", - "0xa26c06cf79c412d49f39e0e27e37c82c4cf0c8648638ee66a97d22d822e064a9a7cbb0b1ede46806ea0430639769cb88", - "0xa968341c5e4a3e6d2a2116222e3c58c2e558f5bb0a2877a27c69fdbd38dc3892f9ed7d7c114f557e52a351c73614fedb", - "0xae9b8bf4774ce3b84185be77723ec62b9a415e21cd60e86513c1500916c96d62519ee8cc061d81ac9db9709d6e191649", - "0x83a30c1ebc046c9a1ba911ecf6f147644f58f54e32357dc395388e6bab66d71fb9b691754b11bf414d43816af8058828", - "0xab5b804fcfb68b6439f311d0420005b083a84da15a8415cc4013898806e67c47698a9d594263fd9be42bf48efdfbe2fd", - "0xa41c18185f8111ddd551ecc8f6dcb87036cebb6eabbce7faba40c6c5c8af2ab59ef027c6fb2dc523eb0159335a1ab189", - "0xb24cd94b7c6e161e651107769d863fe5a3d7a847b9c60c7c803846bd782cec0bd54e6278a318ed23b90cd7ad25933fa2", - "0xa5ba23ead78d1678414d4e986b448e7a24b23a5c0f529ba604a51e4ee0f87baee450fd121b43a954be50bff6c0d7908a", - "0xb89c17de4809e722527832b90b810d9691b437f19db9cb88ca5cdb67bbc6946ec1d454dc0990b66093ebeb6eeb6896a6", - "0x914f436fe0ac7540129c3deb04d51bc61192ab5d0d16eda77ef70ecf8cab5f55a13492f54e8052f2f214186a113d8949", - "0x8e0b3d1dd756a9008894028d0443083c21e99de69b8d8f4e7eb3ca7fc52ad540355d4a1081774a6d51a093110f4bc838", - "0xa9c1730eb5c0a42deda9d9b39390661717479e29007f5f8499d0645b8b85bc0ff12cea2ac4328f6588a12126f56284ee", - "0xa2318a42c99f7613ac78cb110656c6e470cac6903a5bfdc1bb182af21e0f0f409bd39324a13e2790f0facba04459d3c0", - "0xa11ba34521434cb718f1b2015bbf451ba1a7c60e59b1620ea843835c7e75bb42b6ad29263cd3705f7f6f1e40a0ebdfe7", - "0x90705112b625973e1cb35e30f9e15e3c752b2e972231b4caf53518f44b4a40b8a6bd15c4af2adbce5dc194169b860cba", - "0x828035b0e70af8db1294379b4b70e56624e1138ef49f7be81d938e8b25aa5dcc03655e045a95a79e0143c23a77407004", - "0xa7abb1836282917d1eb9886c79b6a36d720612e3b823d9420a4a705e8add6c6bfff2f682e6f992a6af10ae2f71ca8828", - "0x81e97c7f980dbbe93df9efdd9c0a8172ba0f00378e9375c926b9e24758e8b827037ba67e06e994fa9d05942320353d71", - "0xafa640b2a7fb997cffc5db74a91dece901be4a36415786190dfd17a77ac837a2fb2d73e973b8e60582e71824c57104cc", - "0xae860a6850068f2b0e1e5a03afbd08b667f44c4f06e431f1f83269e754f37e18a764b00e100dcdbd1c1d18af9d6304a5", - "0x9443fd7e1263d5ab9baa8b1a3c893765da1dbed0bdf62ac9c886425ea9f05876df1920889b707a2cf248e7a029883588", - "0xacb38feff88de8db3477ea9ae3b33e0c5715cfc91cc71926dce26f4f290dc4f437461a186cf1bdcfcd6d121e087bba33", - "0x942882666a9f49ac24d9099facbf1e65484ee76cfdd2eacef25e0f30260654a7b5c0cb7dc37aa1601980877f945c51dc", - "0xab2c9035b2ee9c5e57d8de70b24329cfbd247324309eb30ac78c404ced268dbe2aaea8d417300c90d87924a48702b793", - "0x80aedcea9c5a9911731ebb444500eb95b519e2d4650c1d465afc61f4997879d60750ae3fe049e54654a06eaa2db7d8c2", - "0xa63e1ba5fac918c8bc0f4364b5fc8d26214deee825aa1bff111e03c0ed43baad47e8bae154ad580b851a0f66be85c88e", - "0xaea7f5f8c387c21cf671246803cd5baac61cd6359848ad4fd685b1350ed6298a129ed74dace279fe7846001bd6577dfb", - "0x906ad36bbec72813b368bd2b79c1c9624966dcbe94ca9dbacc297d0d8af86edbd80cd702ed04f0adebb913a6a7bc1a62", - "0xa46201c20560ef2ded1ed3047fc196bfaef445c4a716890d9235f3a06d6993a8ab29e816eba54c6a2e2590dc8dd61216", - "0xb37eb2c0d765b044ed2fa2923160a19e11509e764025e43a62b4ccbe38e534ab59e68c2cc92cc5aff9d97154b8210c50", - "0x91f93b1404a4bfd3fc8ea019d76230637ceee315da0faf366c712c3ba19088cd3efa2dd30172dcdac11e636f8473a26d", - "0xb6b905abc4a795bf95d055ea09c3f9d0a8a9ba0014e288492a3751d2aef60cd3b7846e1ca8366635a94988b2e197191f", - "0x847529bf842d7623150a3bb91fc4ccbdc66010bf008179a32359f98bd007330bbfabfdc487f4b98691ad65680af67a8e", - "0xb3d37a8098d02b5ee69ed060527f3d924c727016fd92b21d6a52fb1c1ca18c7eaf0caf8144e9e6bb5b6a039ca85cb1e8", - "0x98cef893dbcec865cceae01138613de146d563f13853ae34bed5f142da716673c105ecbf4f2aa7d187bdee20702d8582", - "0x97f60078d18928c4d7dee1ab244b2b7540928e20cf7ccbbf6684148611afdd9cce60dbf412c1fc544ab8c356fda8fe11", - "0x872a6758004e6c87c3788c5c11bcc74db78f076efaeb75127f0baec28febd02528c65b227b7619fb8c29cc92d7c8e799", - "0x8d72cf1191629440d7af8daf3b76b6b1bcdaa8d6ddcde52603dc8b092c2ac78d6e24bec32e1223eeda15dd17ba2c26d5", - "0x89dcc8c10be08277a1e394de336bb1b135bcc5131dee5eece80973ef364a305235936a3b6dc40f2eeec2aaf227a86376", - "0x972c4ee3b4b3b028ab683415bdfecb2454d326a19d274f499e48bb2cfd55165b928bdfa7f97c4fb6d27082cb88b73dd5", - "0xab5438a8af3acf2eb75bea0ae71d8aeae363d6644c54e3b020082c80809ef86faf5811808adc8240c7693515ed8bf199", - "0xb594133dc9f71f72e448796316ff3ce2f8a03c21ef9c54e551d23723d5f197f7fb0bf1c33e9cb3f51188db7dca51bf49", - "0xaee981b45d570a666d0d0b2c7aeaca3cc22d4873812b4424d1f91144142393fd64c49401dfb970c7d5ae91233676cacd", - "0x8f978d21de1e264178f88cad7213463a5efd139c30dfce81a7eecb46942870a3c1971f6e6e6a50e0a8b20c379ac084e6", - "0x9153701c8b82ab43fa4635cf677789c9c9911efcf23250bd393301c0be51f14fd0acc4e467ec9682acc89085b94641d7", - "0x8681989a1be217d77cc8e012c95128557de70b362442e7f1e6162bd52ec6e4ebb0ab28f9ad3f67c1d35ff00216ceeb74", - "0x8e85421256fc71a82d35de9645a6da9cbe4dabb9670758c4eafbcf42b26fb99866bb2b4c374601749738ad34e51dba6a", - "0x976774296281bbe1e8dabaee7453613d0a615cc6abaeffd8e15ca4484b5a743e298522b2dfbdcaa697e1eea2b2bff736", - "0xa585501faf955b6acfb328d801cfec5b59be8ff2fe46ef0bd73b86ba4c19c1dbfcc1df844d61a5acc64bb5e8a68f6cc5", - "0xa776217e5073714b36bd2ff0621246a48799eb5ae3ca438d1efff6f9f9beb13779bc18ae5ddb77c838732e8925018118", - "0x992d726bd4889f4e7565bcdc31c7b4a58ba44da5f361e3b46e0a67a6e4f00c25e3503c94e7b2bece737d7efd47ff9beb", - "0xb277f124d5dd8dd669ef1f6840276c0bb0b60379ca3a0aaf00ca337c40f478d511b1a73e73df6c3b600e6bfaf37a8fa9", - "0xb037e78617c235e6528e535bf13bf5e82c70588d1d0bd08de754d089bd47a4fdcfee79b5666b95698cd98c0e32164afb", - "0xaefef9e398e0edb60615713d7c1334005b21844d3f1401903e09af2db20d7b342b8d80796fccab583c8607c533c9b735", - "0xaad20eec7cf4f0b518007ec1df7dbf4935f6f9ecb36a11d148dbf9e5281aab43feebcc8ce9001374be40776c5ffde825", - "0xa4ebd6018e004ac8b5d022cfbb7c5b3833456faff4f198a3d9dbbd077c8752087bda1ea060466fde4a5f31cb8a50a7b0", - "0xa56ebb8ac9901915400234c1c6c8502905765a7224de56b084f9b0a3468a065e78b4daea27d9887b4f44a72fa61a15fa", - "0xb0269890863c63203dd4da3a08a1bf06621cca212acb49799bfc48be7e41c951d807f85dd4171ed57c372914dbd2ffee", - "0xae11fc0f5fd5ba488104bfc07fed50799f51ceab4768afdab300325e9a913b1f257fea067d357e54950c8d08af5ecf59", - "0xaefce65396c61e835ffa38857df426f64508de6e93f966cc46b54dcbc5e2bfd72df927b00489fc4460414569ce99e610", - "0xa5a1fed75677dc956c000b9135c4b6138e0cff53770399ffbc3b12ff0c1677ace264aef2058aea535ee1a7195afb034d", - "0x8071def0890d01f0d10dab3afb13125f0194e79608b9ff129572b5daffb49cde5bf6d9f24da3f84483612aaac3cb8eb1", - "0xb5e5bb8c0be22349ea51e249cf2159189fb9aee615dd62c5f67cc9f43745676e703abfa6561df4f5f1d79b86c459b11c", - "0x978dfc57cf0d3538ef336a25ca7a2cf373f84b71bc06d1c74907464e3e816d834087ee126bbbbd5090a09ed063f87a46", - "0xa2ff4b59b3e7fef169835e67d47218eff5368aed3e6e2f1cacd29a5efe6c1c2e7e1839d87759bad8ad1871b39c481bf3", - "0x96de49b44bcd2f5ac3d07d6f5270af081776d8631fefbaf9fec6771e13d40a4e5158767067297029bd38e8c6847971b6", - "0x8f2f820e8e3645f2ab9a27b3c23b5f656b681264d08e298ec546c5aaf51119893e0dc8e04d6f64fef48d3cece89692f0", - "0x8de2eeac7dd4b53119d02f0ec99f127cbd8f6a57120d94a9a554c04467fa74ecbdfebbb111d9f15cdc1be2be8c2396db", - "0xb6616f68b00ea0fb78a25ecd51d3018b9ef13664a7da42663d1bfd6fe71fab615624af863f3b41e625b36a607bb42dc4", - "0xabab5be2ab033afd6d110a340c658fb512bb53368886d8a5ea29e3c916a6b1bc46decb2cd0f508b5667f9dd88033ef7d", - "0x8872d0cb09df44c2a75895d46588316a4c9c743080f7a03a384bf4d4be80d341f8dcf0e208383bf3587a3509f3324fe5", - "0xa3f57fda2e8c06fa7ce9de223f5ff56d53ce9fbc48486d88d2845e7011dc038b6f2f270dcfd46ef5222ae9a1557070f8", - "0xa82c4e46f0d1962cb48d6c3d8ed3976c4fd4c174d119470479d9770619a45e6e16e30693b2804a82b516ccdd400508c5", - "0xb53188c6b2907abcfe47fab98f23ac602525e05a5ac6b4421c437025819c80529e9d2d63f8a3c10cb9dced196e572506", - "0x951934cad4c2772aa0ffdfc4f12a55f490824e104f669e4dffc70d9c14239570c87eb998dbb2a6d423bdfe1ab50f4377", - "0xa276bddb27d86e1e70ebb96103a239ae4848ad20c4c5b7de85f480c3f293c934ebe35792361d9767de4333ac6de11643", - "0xb9c8eccc03d7270779a87dd7c52a42c7bd632b9bdf94274b1dc864bc7a59e13eb30870ab740066040aff0beeefe14d2a", - "0x8e0908e4d15aaa582dc028e015c4b2bd97c82b8086737cdd1f2820641e65d88166d1fc763bc483f8fb4643339182473a", - "0x810c6c46945ad5b4f699c51130bf204e47c62066fbe54fd099c3567ca79aa8aa8b04dc5321c09e03df4bb7c9b93857ad", - "0x916d4b23adf202ccfaea7dd124d28573c73b39ebd74bf4dfe32a366f9dd48f4160b8cb0e687e7dca887c4b4f19570cb8", - "0xb1b8fff52dbbd5b9bc6915ba20f3185fa8e23fe52c026a41cdedea5301dfcf6c79c4fe1058f3abf280a00c7b2cbb20a0", - "0x95f9623510e12ddc6f4ae59d06448f496cc911c99a4d5f5c6ff7e434b807fcd4b35ec1ec976a40208ee1a505a892e38d", - "0xac7217596d42d40380fddef22e83db9e6d6b2d0d2e912f868d7fc07bacfb83e8e6f01af544e8f450d31db014fb094c9a", - "0xb10855b8ff1a81ac32d81773ce8a6391169902290af0637038b58ab59fc84e3403d515ba7c99e26b7382c2e2d0edcedc", - "0x89eebe9789a333f5db0aa9e8604798b15a934ff45e19699c2e7fdb46b6863ce02defcef9f6dbd0cb799ffe2b669428c8", - "0xb9cc540b405c5ec78a2d8fc17ee4a08690e347cc1d860885205bc19cba09e62f25b94ffc2cab1f638c87caf217f7b6e3", - "0xb16d06b120906f085cb183a96a2b635334afda4272ac650259f23059407fdcc8b83e91f2521223f79769ba45428c04bb", - "0x83e0a2d9d9f6654d916a822ab1725d58a10efd64e889a17f44860db4d2c77ec1bdde7d0ec8deabc12f8ffa5af879d4e5", - "0x98cef31d7ee167d9c4248e29402ea8d5546288d1b7ca54a5370e80a9ce371bc4aa3f5c7a24c2e4805d8c99af059b4156", - "0x8fd55a0dc38b65c2b0b45c9127c14b9396db4898f14e1559e428a2951cb5076bff9e3f202a83236f15c1d2530539e5ad", - "0xb3252594c3060118acb12eb91d002a74c068c0b8f9bd735a9ecb082f787c7e046dd6e40ddf4b3ba56bf89f223bb5d76b", - "0xa88446262600f605fc4f067dca855ebc56990a9ea050c708961e486fe685707d9e9ca734068b92778a144c0f3c23b4bf", - "0x97beed96ba821515996045a40f17ad46f8f4d927cd9a2c7ce134a60d19ec4a5819a19aab1bb0df886d9cafcff872bcea", - "0x98ce98dc7908161ceefa0ac132b63c860ec2e53f7ba28e66c6c5e45c5945e459797c65668e58c0a5b8a26811f17c3f41", - "0xb0419cef96d4d44fff0338132d53d2c03e7e9b4618dc2c6b9f4475368e21700fc08b844a2f140158fff81f56aef83b7e", - "0xae1eba4a4a715f6d077e90e9efb59852b7025adced47fd9f705c2745e6734f2fd2f2f86f07ce24695a06e24e63f00b03", - "0x86db2fd15dd3cef1e504fb057136f0405758f6fcadc391e6f64b3080f92bfbd4537a0d8f59cd1a0e913b2b188093feb6", - "0xb418cff26800f8793b083a879c8b1823285f7a3cac6fa34cf48ac5355f04f6ba74255eaf436739c4d26d0d80d2607129", - "0x8eda3c25b5699569c03b85bc585acf25bc3f9539e9dc3e8707b34520ae5ac53920f45528f0870d93f84647cae36b6aeb", - "0xa2622af11642fb6cd60cddcd4c242cf13045f4ce20539d11727e8942b4f9a7fd1ea2192e83596a35c096fec3658c0c2a", - "0x80735f92d09dc0af19f593ea118bf52146143c1d2a7343f6e2ab95e00debfbd329d4e887f7421e4a361d815dc1a27973", - "0xa7eff30a31db635e239c8632f7f84263c9a9d82511422f49077823aeb124e6ee3c995ceb846902fcd2cff0f5f219db51", - "0x99129aedaac32b3ec18d689a2589e35fc9715fb3f1a72d28a09ad95e39a68ea939ec5721c501a9e35c60cecb3f4379df", - "0xb9995d65636ce1e70967a8ffdf45e50eb264eb64f15ee887781455c5472459cbb309ab58b1645bd6e8f2bd29e69d81b0", - "0xb8049f4c3ddc22405880bf55b5d5d94a6dbb071485f25a49a6457db0446663f8d4fabcf14106b9cabb1b3222d8786773", - "0xb581027c7d9bf7b97f6eb085934b9caa43a46368cc6740139e33e4cb2c94683411710a52d5933a27c9d12a43e75163ae", - "0xb5dfce672e670158c259f36fa549aaacb0699da2f13702c81f5a93afb00361f9ca22d02dcebeaceaee6813a3c9bf7aa5", - "0xb8184f3eb809be1986530dffd7464d84750df02196274955769a0afa02b65e87686d915ecdc7e75a0a76be8b7ad8d064", - "0xb7ab837f300f4aa2ebd2d770f7a36dedaaa68e1d601eb36a28fada4dc73dbd55e7f31c88ab2835aeb57ff113a14c5f32", - "0xa72013c811ca674c3e909064777df1484190fffb0643b6b1435892f5dd0f1d09579189fe00c862bcd18d03309b958b72", - "0x87fb528e03f1b6a000141f4a6ee24a9738d9d2efa795cc262203fec10d76adcd0f89968a46fdebac99af8d048300b8ee", - "0xb2a1ca5d5d16c7addb73341ebed1f8e832250c2f8e03915a417064750d7deec3289e646c06a09c6a3ae40ea2817636a4", - "0xa90cba4d0928da2a5d8c6935790e1a1f026073632a4c1460fe686d06c3f2933661c2b3c49bb0bbeef386f2bcc4d08485", - "0xa5b684d544500be25136b0b5b95d9f363103a6d08cf49f4934d6c96d43720a79cdffe66698de0ffe5b02bb3c2e30286f", - "0xb246952dcdc38a500e64ccf4f312bc7c690d33a3a951fde5f839f6eec77ac78147f1fcf26ff7b990e8868f5cefe1c4eb", - "0x981ed33458e8ead67d4adeb884153bb0fee0ad98ebd9010ee706ea1da7975c290f82c492cf16fb42d1b739632e66e50e", - "0x88bdec223786c894fbd8f964ab2c92c5ad7fa7ed2b97a6bf31423a6ad5bbb5a946ae3cebccce8cc97af9e788d03f547b", - "0xae852b074e5716e3190593e11fb17f1135d7a5d888986d2be53973fa14c1d4a9887381e648a10a4725291ff062c9d88b", - "0xb87050f914c4f09e2dfef845ace5a06504b6fdb815f685921710c7e82a9fac11f864e3e6023ed5807256d6269271d051", - "0x8cbd11617ab819680cfa68e70e205f3ffecf6e469d88dbdb1d9b0c9c7c38746dd6e64bd526306a8ab59cb7e66841a757", - "0xa1c51cbc1a91618b1ede5cdd77fce26b04971081e5cbf83be20c22b9b30cc9197b9bfd5998fd9ade9b665c8218afe94c", - "0xb5cdb2091d114847dc14a4c922bfe944021549df2d75cfc08ccacc2d740726e90e20a0bc2bb73303e9f0bbb5192fb982", - "0x8e60327955c5de97f56838cdebd24c2ed4021d9e3d74ab9eefd4543a286c1be82a1e8455f8cfc0a17f03358c4648683b", - "0x87f9c1c0987493c631279112fbc79c5f5d7dbf46544119492785f444d063fcb0da4f2d1129735ab77663a9000d9e18ee", - "0xa970df3d50c4ef3d76d53dd2b887e9274fdedced7a83560eb1950fed2075879d9fe1d5af811f04ec92d557a0be0380f7", - "0x95a69bf4092567f5b55a401329d5a08220ae65825f05d56043974fb7b7090372e941a85e2d197c46c9165031b3bd36fd", - "0x8e62c98171e54ff549ccac5d6d381291d0861439dd24e584d356a862d22942e0ff17cdc0d1faab07e496374a547ee812", - "0xab62d0eed8422a3172269de0e325eae9294914fa67f1ed8e5d0609afa2991a26b1e1b9a04ccda8436d04ec085957b110", - "0xa3292bc88e2a9dec7b55ae4c27a3a8ea46a7b2dfe3a817675eb3712f95264c08668703771b65afcdf6d305e396d5f005", - "0xafbaf9cc19adf63a0716cb868a970a372d7a1e24a4c78718a114ced412a12fda6fdf42f701ca1492a8f8c1ef0466f7a3", - "0xb41a5f064f9d900d1534a68c74796927e4018e23f949d86eb76dd5b26e5b686115d63d858a49b545924b3941bcec2341", - "0xb4e1ef520119f9a238fc4988ab2f1266606f53079744b92c1039541aee78b67ac570d7839fc9b2331244d734ad4637ed", - "0xb0ce754a33a506174d5feaff4e9a79295c743b2a122c8a1788c1427482585b398a750b7bd93cc53c38bd3e557caed172", - "0x9842cd13ee9490d9ca7ddc83d1f7d79495afb7301d1f51f4b007dd2b2eaf15abbff18666126adc25df5ae26b98a80f41", - "0xa976af142268d20a248c4b71304a878efec29b5022199cfc88bf82c081f55d06a89f178606d50bd3f8576f0c5c01a6ad", - "0x985ac6f315ab1d2db1b4f2b107eb1652810e63e36b8c14e8852f072d2c8b14922f20d1374a57d75cec62db0d050a0c7c", - "0x8c1be9e8317fdf847a8131ac14cedda922bbfbe15cf95537493c4e7eccc7f2f1a56ddd1a8832e6300734d6019d8b128b", - "0xb55d129c88d252556fe688f84982becce253736ef3b1fb88328e41300ed0713465c8bd15918386844c725fe7a94e8364", - "0xa96384d2d81cf6a79614c7fd6bb68fec6e74064435a1a79dd8b1533e9c7e578da5ecf03e979969d983da893f42adcd84", - "0x8c2b3c06b7249ef5ecedeb4f2c65c0925cda8877bb4b672afb7a15bb5a7b5818748d6b022c6ab8fe9c5a1499e2037c69", - "0x91c8b2b8b204897741124a37f85ddc45c3ef94ceb5dff681b13771e712f2ba5ac95cb1bd2d3e94a84625d384b51b099b", - "0x8bf852945910e9a773120c5ad975f080c07c8fa37c2158e1138162a82983211da70f27e22876741d58c20a6c9dd770da", - "0xb9e907d9176a0fcba87a2797651765c814df756bbd1d0a86a9b7b06d9d886d1908d4e74ab27d618129dcde81e7d969d1", - "0xac4d3b156db2570c349e21f07fd17df935872f9687842035b533c6e4773ad5752f4ba8f9ea4501953f6b8c4232a4562d", - "0xad91c4a7ea0a314d7d1ed7a69a74adf6ad810586c1bf907ae9878ee5f6528437c048c6ae785cc255707ea3e58a4b452b", - "0x8013b76604bda0c429e37006b01750999414100d0ff59ff5ab7b233399adaacb34906ee65054abb94db80fc92ac6d2e8", - "0xb26a2a660af34a4b9b8910463d0dd439a3dc563494f5ec280dd5eec0b14b0e9426a0422f3c75370201299d394c4d90ad", - "0x8e1c7ea11dd513fb8527fa99b899444bf89a1188089d3bb65e3eb87025de9a48e8b4a3068a955fe752f2416de282ca20", - "0xb6cbdbf2b143330db09841aa0e7d22d32772ee62006e7cee13d8c4ac911ff4a59a9dba3d84bc46ace1760353d847bbd3", - "0xb8f5aa3ee213a44c41f63c11f685e754997cac37b27e91d07bcb69947344d94f3b86284b3b1655e168befc01c880d550", - "0x89f93b37bda703494263b10768118ce998ac1f395d422c0ae840e47c6d649a3ec59b404c164a1ad5ed14ccc2408fc662", - "0x97255607a1aaae89530a3bdbb7f2b7ba3fb9d5dc93509991021152dde08a638bb3152503cf0c896c9c19d61f8eea36d7", - "0x909c7ecafb798e6aa45867976f59cdc9d219aca6fd0881f82f296a83a2a3cc5ed47f08794e6e3009f8847f16345f5f4b", - "0x9560fbc2c531571eee5b7389855117644f156ddb00b23a7c2189205d4cc613ec83952b96e941cc1e725c2b574c46ee9c", - "0xaaa69f68b6086bd369fd92355f3a0bc632c1b1b4284529c18a7cd4d71d827291bc997ce74bc92dcd6900419be68efb37", - "0xaf9ab7e6a27e61a99f37b89fc816974ff916b6a24ec3aa31d76579204bdd5ff01a2eea26e76188976c033db4af167db5", - "0xb026dc8850af970d2ffd300dce6ae07db0ca2d21978e4f3a6797b6e3e81f1d9680465080a983c31d473a77ffb62acb5c", - "0x8f82f92ca992ac352ed1e8fe31d24f8090ce6a7f02d6086720422b9bab20f3e3c38a5f63c7fdb193e30d63f08e53c900", - "0x8b896a2ae84c66109c8501cf6070c4da65c43ca8ef9b6b06fc85b6cd92bf2e5397d492796c528c7b2cf29ba93341a87b", - "0x961bf4c0b8068c8406a864595e156004d427138e06b390519cef53af8eb00c748bdfdd480521c6aa0d53a78e8f806217", - "0xa6fa456250d20c6842dde55d3884eaecfe8a39f546cc5e4a77f57907192e849a956a33a81369b0f2633c55bd6608eb63", - "0xb1d1d2f3e3e058ee97c9b6246cf073236438ed5e782bb21c68cd0d77b44f29745dc24d01edbce4437d93071b6fa6e0a4", - "0x81a0bec80ecd1b1e72256ed5be7de8deb11046ead7a96e1d150573f4d896e642b4af095735343f6831bb6b7f4037cfca", - "0xb48d8e15fa8e0b46937637de3c727157f8073eb8a9a04bf127e68977758385a791da2e9c69fedb89b334fc638ece78d3", - "0xafdee0774369653bf371b8820e285e1b48b40745a44d22cf2098b630b8ac95796a74f79337cb97fc60b6d6b903a61321", - "0x8fcd9ff2991902149db29cd4674d60387d4f65397891fbf91b7699a42f579f6b0afdaccec70e5e82d1abd81de859183a", - "0x8af5c73367a8439b2e3e5f1b65e00ebef2eda640bfba2eae48582cdfb244e1b1cc540bc0ef72f9e24399affce1c3e222", - "0xb58cad4da101363bb8d6e8cd0ec7c078f7719462856d7ea573e2bf95e00cc23020031901bd1f2112ffb90d847241e5a1", - "0xa671f7fe2ad81e9e0d5e3260a9dd7808125dcebd970877b000bdaa3207ca45ae1e5458d5ab7bd69b2adfca8b6abd88d0", - "0xa8411cde9eefe73fbceec3e5e3628b159ca4e4c19385ab50b8d7a482f4258f405c47051a89f11dbedb2b15e84d8bfcc9", - "0xb5dd09d5ebb26e341b6df80e836c6de2305ce4941238e3e96da549857ec314b1658f8b03ef069633625b6e4bc13b531c", - "0x81bc9bc924039fcca8892b40aa9fe8f5d6f305343f6054e36647d5f14cad3e4d754dd6ce9ded67ae65825adb4e16df31", - "0x935ec74c2dba94b1c5ef2060c31bb5c1426965f68d9f4125cdd891f20495da9d5dca513f65bf3e8c599f1562e81a0c1b", - "0xb9581e11f361097620130e753d134cce6d40ddc7c516388fe4c881fceadf738f314d241dc14d4f87be8ff0481e898c4b", - "0xb7be50ea49e09d10cbcf21b6f717e0cdca582d57935d72d17e62cdd7bf2071e5d5c91ad7bea79476537e515f0d2fa5af", - "0xab467b7fd32a795411e991417be57af8b62ca199983efc1f744799136ae5339173111465e91083dbce60e77f9f2c0fc6", - "0xb99afb338f747ae89e7cebf069612e22f9704f247d66548d305aacdfae395609a57d4d5405ff0f1eb1045dca4c3827ce", - "0x99a5e52374e1c55f65e44951f68cc3d607157e60d52cd088125a81bc60f2009d1b894eff8e1efb175509aa4b57af7276", - "0x87e3323cf6f11b595ed745a9475a6d99d11333043d512bb61d5f9d8c3f0cb6957aa8c3f041688f63ac13a51df29fa061", - "0x96a5f9ed28056138439eedba186b754f5f7693c09422f42ef82a315b7413b418c4971112f4261e1b9793ec9066c3641c", - "0xb9b5fd36d2d861d40b947c3c879a42fff24b9ee346163e544ce6c3301d0003cdb47218644fd5f1f7f0d6f19bf647ceed", - "0xa8899296b58e5d56d7da438ea48bd76310364ffe666d698c86f20683343663d742a0b3f8c1255e33f1d424cbf61bf1e6", - "0xac4be82ca78df2a367f13c8bd1cb73a28015853f2745e025626c325a10b778cf4bd9942439e35015cb38504bc02993c8", - "0xae5d6b99ef56cebd5e25a9c002e9e80c1d3e8e5fb5dcefc8ea7b7798c7e09b02147da2ba14e42e2b6db2b2a6a738f598", - "0x8c94abefc71d245b0bf04f34085da0a9b8d4d798ee7441596c5166ac353425175dfcab0f76bdabab8f0ef5a2b453255d", - "0x960ab6939b1185806e9f985c9381206c7032ea8a7a99eae5a66f276ad5cf450e654a6f1e956a2a63f33d6f715064d051", - "0xa4c7c7d0fce514db07bae5582f5e4f7a05d79f7605b33fe2a1ae980bc388b31c056438616bc8391ddc7dd5f98810c74e", - "0xad5df00f96ee6e9e1ee65b562d6311c38bc2a0a25aa9ee36f39766a2a03141e95285dd2850a598385f45b9935d63b78c", - "0xb051de656e37ccdf3844a6e095d3b42ea9c5a545e0dc2a5234e2016570375bff6b55ee0dff04ece5713ba8e85629a7da", - "0xac01fad1ac299567a22da6949a011f429bd9775de956dcdc247d5c186ec577fbc12a482ebff3a4ab18a8e35f3e2218c2", - "0x9654db9c6b5e58e0b68fc49718773d44129a0e77bfeee3fb56d27c282de6b75fe9c10f4f3b5d3374443a9fad45c400ce", - "0xa556631390e6cecc2ebe390e605e6fd754f1961e4bbc063c31c08812e0993eff5b5b7449b9732bfd3a22c87f9c528743", - "0xb41b7abb971e253dfec3aaec4443e875d73373c70c33e9ea19c1176f8cf1278c7716a76a4eeb641c142b2c6c1ace5db7", - "0x8bf37cbe29245c5e217a48140d7f0374f46596f2e82c1144ceb41c9801211869b96d7f1d0f7345233abcfead0309cc3e", - "0xa380a799b80f1309ba326f26ee46ba3081b12b5a1143f8289b2fa067aa3ba80c3690fcefded8534a80368799b71ee9c1", - "0x93dce0a2aee4d67efec1b284142d890d1e0d7abdbbfac82f90dcbaea94eef829645675cf17050af7b2e504a46d1bd288", - "0xb8e90f54bc57ff52b84fa3fc3c3047f379c5587ca18d9988c613a3bfe614fd5fc381106729bd62eda298faaf17b10210", - "0x8d8e4f508c284c52a6f907ec39950235c9443c5c6046762911f4818b98293d7d60a2c3f94c5cf60ccfeaeb8f283d8ce1", - "0xa513b66299ba5104ba633cd68121b9ec848e0c8c5252d04a0bdbab5e3bfe6ceac93ebb1ee6f0274920d84eae27df1520", - "0x80e2db8b919dd2ca33e833270738b1f437ae312b1c53a73106b6d12672a395fc3b941292fbb019d40e31b8e96bcb85c5", - "0xa4c28fba416985d47c947b0669cc22153ce887ec54535a18cf457622d03120b6aca71a45fd8704166f6f7a9ea2e9d608", - "0x850b05b9c7e168a83b0e0e77d16181a52d78aa96f4026c4420824cbd44dea9f27f3336b1736bd545bfdf548eb3f4276c", - "0x8efabbd63f3b9ae6111dceb1cffe45dd23f1500f87382816d4192161a77dd0776da2a4463d32da85b802ba7299fa726b", - "0x9426e75c6f7fb77072773a2ee03e1e3f1d90878fdb5d8c294265262f5c1cdd74a7aca339b46af8a5c43823dac7e57edd", - "0xa1c4d2ed335a3c92d867c5cb999b2b807dfb1d45e35b3960dfab19da43e2d1ca9a8748738380cefd137088d8b80d3006", - "0x987a7e22092931f39f05f5a6b38f419750370a71157d4443510b61fe07ac5aa31cd7f88ea04121947b1c0d0419d2a25f", - "0xae73cbce7cda7cd90404302388d41b49ed7d7f505a9a406f0317fccb29e32a5be61a6eb0951657f2d93abbb497be62ad", - "0xa1c7cb4056984c22a57ce76272428a50fd33f0f7a68c29c9438af05a87bec23d8de72062fb4829adafe597a278de0c01", - "0xb72c81a9a747a83a650b58ee01015a8882789983b67ac4f2fbedbbf47dbe30f04f686877d8f118b4634289866aecf9da", - "0x91ba1797d6913270ac1cb9c87d9d8440a651e294c45b2301ff8c40416e58126318f0f2d411b7d9c09c8e19f4da8ca0ef", - "0x864107657717124339cb2ec06cdfa75fb9c4a7ad5155cbdd03d155a7f9e9026e237d7cf5f4cbf07239e7bfbd79900957", - "0x87af853a334b8cdd10bf5f78753b27a0c9aac9f55db7570e2d9d42f13d0e2f8bfc4ca64b77b21e478f23385f17eb4f6d", - "0x8658227bb8733d6c7608d66a748caba761f28da4d95e70506dcfdc18300a559b4a84d11a9a048e82b292eb1b5d88bbf9", - "0xb078413570ead3243b9666c109a17678fe60dd1240caf01d1d344de09e346015cba7a40560b0d68b18df82a0a37ca529", - "0xaf6dd12875a891eea9d846aa660a207a527d08f5959976f6cb7585a98b1133f341f4ae29157f6ea8e0500fb6b49fb9c1", - "0xabc0fb42239fa531cf09f7288fb00f1d1587f2a86503593d481bb19b1159a6a9d6f4794565fe923a545d45b058d3a74b", - "0xb95966d42c59bb12029aef1da7fd50e9e8aa9ea287649ec3ba44247b185b485260af077e0d755f322ee4ecf8e2c8137b", - "0x8b1a2350f9bb0d6de377c00f0897081bfbaac5d47cac852a22dd8a427fd2e3029a1708f452e958a07236c7f35ddeb565", - "0xacaff21e9740b831fee42d80a9a80cffa6673e39f85b815b4f546f538dcd803320f90f4f25436796721c8a11f5a1b25e", - "0xa0dd42f019eedba19f4345553965508aa9d2eb1499a363056d95e27f7083c2343e74a0e7dfb101567250148ee1bec1d7", - "0xa08d1b1863e594bfcfa2e21ef4edee8535c8ee69490a4113787899ad8cf2f2ebbdea54de193ded85af82fde074ccd0fc", - "0x960912b621ff08e27781a4f9b80ef1014a4064fa3c96f534b67e5a094a5c11d9cadb2b69cd2011cdddb463f2936c7ff5", - "0xb3437f1e0872f6b9ec071a951f26120f27425789e00c1a8d3183879ed02e3b017406c051f32580b78b4d0f090474b42a", - "0xa90e6d1b11ebd1f1dec54d7b3fb336b9a53c821f295a592e147d5fd453d66e63295a96ce827c4ad64c37d4bc0df2c7e7", - "0xb357a785f3dc1f9bc1034da77033c0c64b29b78c7381ca59ef81e24ab14448d67dbf84756ea233b9e3539b5ed517d9c3", - "0x9360adb42210abb9d7644bb95532e1f461464446e94cb5047bf8ed5513398414130630866b6980b6afec5401e608f6f5", - "0x9145a7f8b2cf1bdd90b9a860051eacdb937189e8d68793e52bed202fa1e23a87db9c51a18f0bc050dfc3c600780099c3", - "0xae086e289e16608f02281bbde5a6fb2479e3151a2464b86ea737f8a43e15af4fe781312d0e5620a42a096cfbec885b0a", - "0x92b57fb14a0c567a16567f83e72b03b8b564ff6d830a5776014167cea06205579dd10715071097710dbf50b660b9143b", - "0x83e6a3f027163e635c2a1a397d2661a2d6c72c25082df129572082db29b1587c78dc3d2e5999112983a040ca46bc983c", - "0xb1667d022c8099dac5af4ce3b4ed6f524819240275725c7580a2386f067fdc9b3a49b74195cc6f661212fb07ff133463", - "0xaa2eb0c44df0a80047eec28a80440ed5f363e3d42908506bf8418bf04e9c17a5e9f550bec9c8ab8dc9979736ce325780", - "0xa2c1d257de1a55e4c10879eadd49af8950b0cf25121e8d7de30049360470aeecfbef263739262bf1f97020c6b025f9cd", - "0xaf29d1afc9f76417e4396c54300773fd283f1bc2cb00308da5e6b1deac7a48cb117c0e8c87b03076c7a1b8414d25dc97", - "0xa44d4f2186f5d728fdb224f10b496c9b57d96204325c452842423cbd29bbb2d07e98013a3880c7dfd63ede725d15953a", - "0xa30c45d1cdc68a5d5ab65b57d60c8b386be836c5bfda7e2f0347229b7807f6a97b632bf54ba3711066bcbd5e0831e5bb", - "0xa8c3c93d6a3526270ae47bc2628da82bbdb8b2c8e4d6a4cb5e9cf70b49999a963f3e856ff9db12cfd2575187bec668c7", - "0xa03566f1a99f5b82e8243678d0bb033441cb8a2f160c0c66dcebd0b6922a56f895a69b94a9c65f4adc9ed73420fd30dd", - "0xa4e3c839a6f4f4317e7bd06f25c5236e42fb0e54bb975f18f0240bdc214780049f0258dae24fba6301aad508ef9abf69", - "0xb7e0349d89616156679d06d1626f45dbc9683ad73ed91f0d92f8f82cb0ea2ae8d3ba3a752e73a39da70569d41e84015e", - "0x8c9ec5ff6be4b0d9337c5336b467c6d4f552af691bf083a23f1f9856e18b5a13852143dabf03869009febc443b2edbef", - "0xa12ff782575aca7b48844f0402a311bcb3e19514dd4d2ba5b39694c66846b22dc9ba25ea39c3c1bc325eda3afa1f00b1", - "0xb55bb586ebf5c9a3c83a04bae254e22547f37b9090151d96f5d8aa81be17bb38d2763a08cf0519a91878633ced6ce0f4", - "0xb3957203932032fe180ba9cb5347c2c1865a3094d03f6611148af4094fa6a8eae522f2651780d9bc49b41f5c36054eab", - "0xa0c865b498e30180c48fcab93342a50ca1cddd8759d6e0bb54e9f92e7b60c51c373f7ab1432aeb5e5c2d3ffcd79e8180", - "0x9503ffb3529c3415c07247211c2a4f35d8ecef98ce9f921e67438ffd538caa54520fc6d248a081f46221a0f1165011bb", - "0x906deaabf6e8dd0c24a4b22757b7681bf88268d9b4ff97f2844f9de825af511155d0bbc48dc4c03b87007be94f835d92", - "0x96c2a7f48990ecffccbefe128a28cd5b26c664b8dc9bbae16d857f7efc1b7711c734ba7d1476945d09ace569297ea96b", - "0xa37ea083b0a61f400b498ac5ba2360c22e40b688428ff4a02e3cc80206b61061bde037cd52d97eeca175394dc675e216", - "0x89b15c3af439769829ca930fa83c47afe070f6e2d7a7df88e5a4f3a2c0630f9d143bb3cc43ebf9bbc1b91be03d35ffda", - "0x8eca6996ba407886d3b9d2e4b1aae1983023dbb1c9ae47b6637458c73ffb7f422b0a893eb0b07fea2c5172ba335595b4", - "0x81df4d7f576930b2865af5ee1525718a09b65d9a013feafd19cad335e4e425485531807078b9564c8db3bad95d23bb0f", - "0xb6635aa3ca31c851a0283c0c6356235a5d8de9d1db9780e62087be32089c1c081bdc642f067224e88c14252efb960e3d", - "0xa0120e81025ba07848ef24ca9a94699db5274a8c85eb9c2f3b41a81f630d09d100127154ddc3270525961613a41ed81e", - "0xaaa8dd063f9f4f73f5a7c440671e1375ca8c224f8f869af736edcc435329487902249c68ef646fbf71c33a8bd1a04d9d", - "0xa36bfb14bbf3956c317e01fe744bd9c6c6f526a3881f6800592501ca1d9caba7f81b3b54f53b2ee1b13aa6de42ba06ec", - "0x819cd123fd793c0c9aba75aa96293268a4731c68c0a26a52561a695fc4acc409752de84ebd19494bae70849ce538138a", - "0xad4e50ce325477621b6eb4d453b087c3d7df6e3d019ab41239f2ad0615c6030aeaf85e0e020f3e6c89e46b8586b4a347", - "0xa4327072fbcf33be1e57ee4bd5db4c079c5ec11694a25fa2fb30932f8a2a35a63183b24d3ded7f6c8a8d0ad111586dbf", - "0x9454f17aa8fbdd2b15dfa6600ad305936a37b205eb554c915adc43aceb4dff6b0d1414e61584d5b15265f2ec0c85abea", - "0x80eed3725282c83dde575620bc0d86e50412df5dac3b3556d1e3bd9e7ef6f56dab202f4dfe4ce542babd49c1fa7dea5a", - "0xb90d1a07ff760daa23b7408b067c322f126023389beb7bf373f0c68b85ba0ea8a2c03e77e6d3339a01ed3ff8ba51f1f6", - "0x92789ad894995ba07f36a0814fc3289810136f9dbc6c70c57ea80db464772d760b57d5b059d4ed458f256af7603fa2c3", - "0x96a4ae1ca46d3b26029767e02fcf2f623d32c952712badf2a2af721226473f4875c40d5b14e66bf961a5a56aaced3aeb", - "0x8c5073f4846df9a0e057f52fdefe01a9b8c9ace91ef5ac253e823e165ae698e733eb936ad9cb04d2c54cd8570f328c4e", - "0xa9f36450b5ca66a20e52bc196620852a41f1f40262a2e12c278818b6071e6972c3cc6fdf83a9ccf586db6cc177173cae", - "0x8f101df23aa7e353ac1034c38adab8f20b8753aacabd10d70acb41d0fd0b1f34277546b30f64d0a861f448f112e38acf", - "0xb45b0779ef1ffbfa86d7e02e89bba0316c5ce60742b350296eff0d04246f1c8b1bf5bff68bc97792c85f1e5d4dcabacf", - "0xb7e89d015f6c7122a2f35f1c48b43eb0076ac4269158d52e38bf2a11de11cf2928175f717ee5c1bf543ea38945658558", - "0xade2a57ebd7600929dcdacc290168443437bc288371ef40580df515012350f3453b09aad8ae9e64bbc3fe6a3456f2c31", - "0x91c2f8de02bd8dfed1eeebc40a422d444e3459f9c33476b55de3e950d2c38d8463c4edf4d4f95347b0599a48cb2d47e5", - "0x8f6e77d9ceec539e0407a8d75d4e855e376838c0f886b36615a9c7715bce56a8669586f6d7cef75812d84b8be91380bd", - "0x87637da91b051ad92081e682e289bb904c51d95ee1a6ae2b8956982093a7bb4f8a66d91874265dc32229f9db5bd51ba0", - "0x94691811eb74f2970a95e9a2d64435952145f1d0caa76040f9811c9ea1ed7327750d57d6e8dd63c6378f336421d11093", - "0x884cff4ebea1bb48c0d651bcf0a710ebccab9062c96364aa64aa1275e9364a4c261e40a4b9f7e1e135572681a5a7a965", - "0x93f21d4b6b53cdc1dd41cb1b80ff73c0f1620db41c35aeccc059128704e9d1d7da5fd3240e7d075a2503273e7525664c", - "0xb9afe0a9b64dc43fa78f607cdcfe337ac952fccfde41c2e88abe3a8dbb36a51b3445d724908e552ba74bf67ea2cab56d", - "0x910280ba145bcb6a99d89d1526f10632206d2ca9e1a8596e5d181dfa37e5f407e1264b9c71c39530caa59894c10b371b", - "0xa5f583c9fbed59f99cf5e21b9a734de6d5685b9c33931325dd4b581bcf5aa4764c2a250924e7b6f7931dc5278bd17152", - "0xa87267f2ad292572a1cfc89308c96aec0d12e5f0fc2b4135ff8df7cf83bb1e71d619906d415db5841bbbeb173868ca82", - "0x899d7ff8d7f8d0daf62ec8d28adbfe4e7856582a23e62dee175e3bb9461f38bf8e4f73dffe10654a046573896f6de690", - "0xa8f3601e6787e788d46a9d7592dd4bdd8ea8b5136e3c897d79ce560e9511f6236e67a85a35c59295428c1f9c019a0841", - "0xb180a16448f085227a6f3e363b0dbcab285bf419d438a13be2cac1ac9f97973ff6b8aee38294f70a8d72bb4ff474577f", - "0x869038341a2f68ba85f5b2de58d2d794584a3c00a76ad0dda5aec31d4e3ee433be20c197b40618f89f7c8f1692ea3cc9", - "0x8366f825dabdf4f7714c5d089443d0de315198e23fb93c3ed063c4b8fca0727b05665c04beca145dc4c02f333e300c18", - "0x93291da32b501cdfa3624b39f6e38ed982c75c1209cd85630cf83288204032c0a90f013f1dfb4dcedee7aaf0fd95566a", - "0x96c95a1e73016fecc3483fc94dfaceea376ac700fd4804b24e9eda7135048e521daf96f8f63d5a1439950a64296d8124", - "0x866429fba47fb691a4c39460031a7e614096abbca3073e9246babd23075e8e5f6051e424e47d860296ac8ac646f8a283", - "0xb817f3d9985cf9f9657fa800ebd36a9622566697ce68f91c509d9ad7df8146532e24ad85c07f399908f87d1206c7642c", - "0x8761c3755cf5440775fe00081f79dbf59829f8d400adf7448188b97f756ad35658295649294ac9626c2569ab21a5df86", - "0xaad65ace72ef89783507c9feb5555275d70a421a95f306b7613c894bc24e978be809410b519e9314ac56fdae0c71d326", - "0x8ed16ed07d0e989061db5087d50cebfcd6983fd54be5062e333bfb8f6f609bf1b7b840c91ffe4b66fd674eeae2dd1558", - "0xaf3919bbc0df42b1e2e8f62e931701f7c35cfefe3ac3f1985ddb70212476112e8a19d51c673da931777ffa28944306f2", - "0x99a364d8819b5ea0f6d900167b60063f40f9afcf291ded7adaa2d0e46f344751cb312df1c2113bad8d84a028f680b41b", - "0x8d970bad8f95ced0b0323f4b7b087efd0624ce21834b3c9ed435dc0a394cc2c7ce58f1741c1a64265c81654eeb6801ee", - "0xa5f96a4d794f6f844b38f9b82ee15c2441cce293b6b2ba26b25643165236db05ffa918ebbe20aa89ed2a8ffc8df393fa", - "0x8ca69e0006f6a72e5abcc32c3961aeeebb8c0a76d877fdd8a093467485c19662b75f2ad8c750acc9cc12c8fcbfbe9b0c", - "0xb5378b855f6ed3eec19546cc21c947dd12e98783164d95a95d3cac36c89a840bcb9f7c99b191fa7730ec28d57e7326dc", - "0x884e50d5e20bebca96dda539daeb0e15edaac7fc88bca254a7239f30aaec47a64f29b69fb2d90041b82f8ad8e3f13d3c", - "0xabcce1f6149037ac8d27497831acb867cd5e05f637b7579736ba5c384b8145f127c56b82b1876881b782b94a84d32d04", - "0x8747985d53fac369c4a23224d50bdc556c00f406e7ab3e38427aec317ae7c0feee5b48b9386c5764de883cf296ed1daa", - "0xa153c77887f271316d5a7185fe0d2bb7359cad86ba80b03434bee8f21b3a5e52263d28cb9d3d2e6d5b7443196e03cf80", - "0xa77b16b2b7b6e999144af6c919e0a74b9a6ff70de41a133f7f820befc1261bf261142717133dd4a99e168a5cca4791e5", - "0xb89beb83489db9fb62fa32d1a8ecb66fe9ed41d318820d13c3e07e1c97802dfd7b05d34652a478a1deb3b17b4243a499", - "0xa80200902da696d0d3974ab29676f0eb67d15166b173fd63b247a17cc49f56b6ffa28d9690841ed4865229248650601f", - "0x8210103eccfd1f4be55e33991a831c50260bbabc1f311564fc1c52c3b2755d3e4a11ad69cd95e398dffdb9a0f5b77df0", - "0x9958745d00d8f29d05d97875746d863007b1c05d3ae920794e6c65adb47ec208734fdaed1b49982c4f4cdd1d3043c369", - "0x94a4f28dc7a9d2dd01ebc2f3ed11a5bb01a2095e7c772d2753c022d991da7b2e4c80c2170209bcc4771d68ef8cf007c0", - "0xa6b5c5543ae3de57e074fac82221590a8d771e93e22fffc2029b44e8a1c2c8c9cb0362416de54d00fd5420e5b1375eb3", - "0x875e801265871509c71dce38005ad6423fd027206e6ab4c58d2978ab4812d5720401c1310b56ce9ecd95241a17ce0e7a", - "0xb6819bc6497ed57feb41bd82f56216b513085b6d1a560a958adcc06a6da304424ee34ab2580604b0e59f6b0091ffe6ad", - "0x93bef0806f21f8bac88a5d6e2e6d1adda06f9daad5cc3c8de61162495d8fcc3889b767a3e2f3380f162166ce40a0ce80", - "0xa1f699cd7446cdb1321a05f970bc70cc98593aaf0145a0d097e60e5897aa311b00d019e09cd533d0c0b7cc5c00a753e5", - "0x89ae140ad75a83db2903a93a3711be90986d08dcfe962aec5ea4ee69656026dce77821993c1defc4464442bfe7d44734", - "0xa4110c80ba92f545a1a7545cbeef997d6c0242fd4d771977192269d626b35c88c361df53bb36dfa8ea7e40da68e45f81", - "0x906786f38eb7e98c431fa2464048ac3f1f1df8f908a25262978327224bc82168f564b2f3e6da77f49457ce49c1a72c2b", - "0xb28d92b3228547f03a3f489e09070ad9a1e20a73e49f7ada96ce41c19cd6416ad809b3a3a01f141b3698e85c641d795d", - "0xa25b9df9b377baafc8c735a772e0ed9ac007c0b6ebac2cc0f8f2e799e5e6038a616968c9896cea862e99b1750224ffe7", - "0x8085eaabc79a2faf1ed0b9fdd017fba1e46c671c6d8ed78fb089494f792765b0617f790016d8f55697dd0f45d17de4b1", - "0xa0e81b557af74efb95cf94054264d30396121312c643052070ab53eac8e75075f1fd0b384cdf1d96bd39cc98681b2d92", - "0xb8e0ffc7548969ae28beaa9d8bd65872840a03150e2140dd799d9924249f92d962a0089171bf4b311520ab527198668f", - "0xa6188827a500b99af6eb91094a0e464e394c8c0a6d80cfcc5d8be89e8810732a03ca75b2befd00d07d1dfbe7dbe89be5", - "0xa4e5a47c656e74107e6007199b940d8381f706d5bb4226a0b2fb13eda725a556530b8d4876dc49c5f9631dc6bfcc4c9f", - "0x90330a50442db9a9c459e06d42cf7a69e009332976c3950ae7d9981d99066fd2af22f22ac429850b998f1ec929c82bfd", - "0x89dcc51fb717212b2dcbd0fa0da189e194b4ad5bf7f43ab2cc2c96f11c186d0872bd930aeaae01661ce2dd9f94eefce9", - "0xadee914ece15575cc34ab485f2dbdf3979406ce7cd8cd82197f156f373beee6d80e5e3623d79a2fef14b0b4ed1678a51", - "0x87e97e8866002364bbe9b49c5f2b5eb729c7018ec61dff7b8bcee1c1ea349e5e04a3f3781617d46d8fe0e62afe55d62b", - "0xb6b7bd0bc652a0bf79aeeea1767f0f17dd543b9de582531bb3e14ba2bfe1b720a6c6b613cfc295372eab9202f5e2d340", - "0xa6f9cd96d8e422d9897d50bf36288bf4c09d28cb0f5c4e13ef7f76cef6c75bb594d0ca954ff7339590cdece16414fdba", - "0xb9bc319dc5e55630d1ee8cb48978a256b69c96aaabb5269bed8c5366add03a2c38da11cb03a44e150a5c7f34bb49bcd5", - "0x868c36924f0056b3464bff8831543a280ced62be748d60f82ac860c32025c4589e9354984e1cedf24678374c959383a8", - "0xa6244602362c09b382926dabae5793ca4fc50600193c69e645fe229a471f7cf9e58c4a59124d6d2dabaecf50f1e1fd1d", - "0xb42df58ee9e20fce589837d5ed8a938eb83a00c6ffe2f6afc973f6ce26559b8d220976ea1fc18ffbafe739c92dda6618", - "0x90c0b2ed8ed7cd6f6ff812c84ed297b3231f6e2106f2df6d5e4b4bbf5378231025582cf39f35dc9344d9fad3adf04685", - "0xa968386bf1221425cee0d0b926689426fd77e8e8bca5ad3bd07298fbbeef4fc676e0cf7a4f29cf981c682a78a54a2d1e", - "0xa3a46bb7db36e0294b509036a40875850ea5ce4e8853cc0a7d85e8455fc2bd7d5b593879408ef2f3b2b2bfa44aca2276", - "0xaf825963207f046b23534896086a3e56247d752982417047f850bf306d0cce285b537508747afc700dff6472fe3b5569", - "0x8022af88981249b5da08ccc19e4ffbc35feb2cb5308b34064de4d5bfc8ff2b933363988c833ec70723e3b5107f8fbd67", - "0x89687fe6e424c7f0d2751e5f7838e9a3fca4c0bca043806fe511442bbf41cb67d01165ecb662b1ece1b2adede5a9537e", - "0x99c925763420fdac4149a02131831449c1df8be4867a6d2d09e6b14abb821d46bc1fc4fc9aacfa4e9de1a93f9b56fbcc", - "0xb819ee6a0724de9c944ce2ca51ffd3f1d93c77ff25e39de8be2a612abe732dddbf2219e839686a4373609a560041291f", - "0xb5eabf12513e91139025f1236c7ec235368eb8586522dce04d370acd3d854c1e6676d92014b60ea3e4e21e9d6f063f2a", - "0xb82e94f1013db6cc682032c7760aca2a1082826d280801aad9c6564704362e61a61cb52c6f35f769bd8ca191e68e0b0a", - "0x95dcb02a676b17f20b75632c7a9060f990e44b0c1fba84ec8e633554f875ebcf6e54caeb9816267e84a11808d68728af", - "0xb0c7c401dcc019d2108eab7e87d6494e06399f6eb4fd95b8ff9ba4a56e549a3d3a4aff13771229f4c456283fc3cbc53c", - "0xb1a8e3e500e3ed74bacf91a82b39f2b870963dec0b98b7d5ccefa3212fc9f3ef923101887572e14d08145aaafa8da5ba", - "0xb2caf72c47870ce9f0524c4b3df6ab3eb3695765c010a27c0f3cda0ee1c1f5bee64e5392ef8b3f0f11e66bd8c9d4630d", - "0xa8fb4864bce5f1c48d681eb37efe7d9ed1a83ed36bdc1f2627539b92c90e100d4dd64ab664e404b0eb7b645a8f95642e", - "0xa1b6164a4f0467444fd56a1f4668c8d1f295f6e6f5191355dcfd004c34153317202823d72162b621f677c970a3f0bfd0", - "0xb2cc59a2f6f3b7e18064720f93b28801fb684d98ee808ec5c04a5235dc40372aa0e0521410d8f736161470443bd97ed7", - "0xb5d9a823649c09151b214406189d75d7f1ca150cc7431d79b7d60348b6d7405014a44bb7840e35f9c0a634b4c6785561", - "0xaf6b8229fe035cbd6a5da3a3aad93e7ca5ed233dea5fe4477dce46ed17bac9243ebf25a8439ac2896c41baa671c0fdfc", - "0xb42d9023551d999d2be3ee51f6ca82c3b2d41fce51e1dab52095af6d4b59edcad70a1f9b1e71eddff894e3fe35a1f11c", - "0xb868543c09fa9b9b990b276ddc5b68a2415965d3de71b9ac538c26a6333543a7c33d0b432f57756ac0077d0021878944", - "0x846577a8c877461a58a94c5829f2ed9d7ed107fa63a48ee77a1ef1f1d1f940b2605fc742cb5ef849e3cbfc86942488fc", - "0x967ca22cc8c21382b15d73b4dd4f6f0a0bdb2056c21e3c75eb3d9c13dd41336672ceca03065d8cd1062389afa4726974", - "0x8e0b872d766c439f3f868f18ef0c173896eac883783dcc58917f76d5a2e8c291967a032d254450fa7f9a12fa7d7a4cf9", - "0xa0236eb36a4ce3b7d649ff02de9279d364ecd5059932328230314ecdce3278c42cb836f547bb9da9de0fc96cda2fbc7c", - "0x92eac5a5a88648e6d821d3bb51b280fc106f751d85a1742a6a1ceed071eaaa215a0a0238492ddbefbdcdf2e38e4149fc", - "0x88e1036f9b20a2c4b3534175c93d59c1ade3fa6652a4c5c490f21f6c3340769c7f8147d53a92fbfd84c23d7c4295cdd2", - "0x8b094165ad429a339f12696bc8967ca89ec47a4778f387e42e273a1863a38199dd795d120d198d3cbd93203604c6914c", - "0x8f8013229eb6bc6a8f93c17d3b4a1b206c258f14091c6dc39cb1ec492d403cdf5f696070ef5a6c0ab9ed4ec141b08d73", - "0x81c7ad27bd7a48b444b2be3d4b5d4845743d6ac4857b061e659d7ed48ebacdeac29cabd0cd163f3fe6c5cc28753148cc", - "0x91c8a92749183e3e6f3499d3b0e9b080109d5e88ce8acb03b35f3d04591e13b4c489ae323a149def1edaaf62f93bbbe4", - "0xa6a2d69f012d877460c33095924771065fdcdddc30670ea84576b72dd3f7769f90d1735f8914b6841c7d938a2046ff4d", - "0xa8ad4b976a5e4477a97d48a3cfcce16b358fd3dc1ed1df301fad6d6f0e188782c518796faf1465e52312b47bd713e2d4", - "0xafa2bab9363187473a85f7020106b176903bc3a3e3df1f4938feed5145b79b66db8aa608cdda554166ec47e60fb34b95", - "0xaf691bf473160cfb84ea517702f3c01daa6155f31393d807c897b39523448c5af09be581ad713c76aba194f90895cd9e", - "0xb74f3cbc198c9e4b2c7316fffd57fc749e367b7d1cf81b3f5311d266c9a3ab9598075ffb9230dceee230d5f1bbe3f796", - "0x8c28d21c49a15299f7ff3eff7568b8450e6404a168554b8965a291c03fdbbd3dae9ea6b9760869cb1f2e8c7206183195", - "0xa496a0df4e79827cf3bec117b92b5b248dfe129d783841935363362aee4822399974e6c03a92797b3ecde80b207fd7c0", - "0xb39fa07fc8f4be41588ff5560ed68a33c3020bceaf172fd11e0c1288ea885c6dcfb56a151e4773e57d864dce06fdbea0", - "0x990cd050ab056ea447c114217219d9c0c7526803f63952e22ae60a3996608bfa3c6119a56befc597592761e3a90ef448", - "0xb6f02dff3dc330daf82d1edbd4e6964d2e9c38481e74cde8d9d85a9e602ed22c4fe6c9b6f41ec76582f0a4e4414bf300", - "0x84440e4a7146ec2f34e8099e85c09b8d7bf505a15638aa34cd2b42a20f1f335cbc9f0e4fdaf2e53fa0ebb2dcb00519e7", - "0xaf389aed116fe58580810fc474eb15518dcd9746f04a7efd2de44c9774824db79f8ce4c4fa108e7396e1fc016132a402", - "0xb202985e01c62d0de1f6807fe600a3b81fd11f30f5aa033b1e7baf7a62f34fa5342d42ad6a6e309560e3e9ebc662920c", - "0x8a07641140db9701c676b2c094c24cd663a5a34d3534fd4f5f1e38ca0c46772d141679730b5d0cd71d056c257d9a125c", - "0x99dc01e76174370a741e8e9ef5654a3a7769a010da85de41dd315b674ba8786e6a697b74a79ea782a1fcf74a48e51775", - "0x93fc897841609670a1eb88d4e4498c54e286e25238309fc95389b16e4edfb82b8ee8447a436893c7180827a996b9a0f7", - "0x8e2dd561acc8954a53635c0108ff964774fe98d12b28a0c6ea8b5ec5ea3523a45b81ec642c1453e3b2a1c0e0749562be", - "0xa95b0b7f9e53720f4b0394bb6ae8222aa5be00a2050f59ccb595d50e0dd9100e397af9ea77b0335be02d1713c361357c", - "0x8e21dcb67da3eaff5b950f989939237e3735a31e346e1bec8e6ca11edff5223e33c1c6f2f79da975de2fd86dea286e1c", - "0xac02cadeba36143767bdb8cd4e1caf8cb287296b53955f33ed07f771a1fea521fd64b7e153c90d5e270c12ab959cfd24", - "0xaf95bca4016b2ddbca61c9c854cf999ed59ab4b5d619dd55460f20cde5ecc86081a2586a7eb37f15c20280dd06b65809", - "0xb7d7c81261e8c6a8983442e1e801f5072bbada1eb2e49b8e90759dcad653c52c0afdff9cbec41bf21cfe832e49ef8db8", - "0x97fe8c6d071dc80355bf2a74c15ecb16c59bc042eff323e999f4fdc39e1209803d32622c642ad25673c84761f0d357bf", - "0xb37da716119c00a0955a7fee59b93185a6e325bc5cb2a7fb35681fca0688d0ad2d25a0e40dfdbec1a11deadb1cc69d47", - "0xafb8091548179fd2a17d95ca47909d97866e4fe54099736e6414682ad083fce300e0a20dfe3a017c1ee4ee7d271bc470", - "0x9306ba1f3f2f74964dfcbcf9b87bafa44b5e013853c46cb501e10409f3c2af7269aa17c8cab261fe82e52a188ce0d18a", - "0x82430e3c25970411f40aa72ef1cda5b2b51bbc7e243a1b4951e92cb56a2f5b200a039f5554d0d1bb44330d89d1ef8840", - "0xaabfccb8f3dfbd4012b9d196448e83f17bd1ddb8c857dbf98e80ffc60c1af3493ac5c70e3a2f1f26352b1ead143dee87", - "0x832cd6dc83380d068c068d815ad0f4677de0ef602890835b8d32b73223490a6f753092d651968cb3d798cbf2a227960d", - "0x80e3e7f0c46fe5d962322f3fb2535de40dc078db80e7ef57923d46b742a8e4d6dd35ef74234f2b1637a317364d57abbf", - "0x9306bcc29d6f8a478ec085b144161850afa29d282cec756d0d3fcce6f4860f4a4b8c8a5952cce54ea893cf84abd6c4fb", - "0x9234c03bebfe6b47aedc7c5452058ca6a8def3c368bdbc9019ef121ad44171d6b31d9bda9c82300b5b396187324684ec", - "0xabc2ec6016ee252f5693558b694eeeddeabf4579b7e03d37504c26ecc29263e455ce8f0158fbfc54135600b72dc54315", - "0xb46fe7b51df64cf46888a810365f891d43db5b34ac4d3505f0692603adef04b1d08eadb3e31d039817e7b89bf0789802", - "0x988e0dd101bba7d7e4094cde99eeeb6d4411341e684fc06ae78d163d30c4b585375a868eda7ba1e5495ee7f0a7d509e1", - "0x94d3033ee1926aef656b31192653d3da96d5c533ac2436d68fcbaebf827475778689ecf14fc53042a523e4652fb9d713", - "0x993b598555bd2a35e9a03f99950d09f55a48ba63f9e0e65802ecb95602d045001f82f25c3bb60221adcb8ab4e2709ba1", - "0xa0acd921ea7db9870716acb595c65a934a5a06a07c6e54cd26efc86c97eadaae1522a4a26c8f93b7b7cbc4746ecfc21d", - "0x8dbd8f492764bee920e0224dbe39d650be6732b56976a5e1b636b2e7371c1509431175b66c6ca879ba8f915f9df8fa36", - "0xa01b24c1e3aa044cd2598032950755763345534f95f6f71d50565d25cbbbdf9c42e35253e35b683f6c3156f5c998ca4d", - "0xb895522dee1ec9c5289e6fec652093519cbbdca7a2936fd1df3ef956eb404f1a24272c9ae6ce58eceeceff36d76d34d5", - "0xb91cea120e200858457a64a60aa876f167b1b88c1dacd9988700b9f0f0d1bd1dfdd8dab56c2e7197a174b7b8bb8422e0", - "0x8406767e4f7cee2e12431b093ce82f633ffc76b451ac8414716fc74fbadff30c52a22869607d5de465d0f4df8a740343", - "0xa2cf431d18b2fa526291c7027d59b18cbd73a9b48d68cfd6e4b745d27774941af809edba06c8534b1864045d6fc1bc20", - "0xab3fe23aa8c45ab2efb2ca0c593c8644d3f47f748c2f753626289b0b9c761add755e3b52521ef37fd609429b2f8770ff", - "0xaf4530dfc5b3f37888900d9fd08554bef4e47c4c09a8c82bb48c4b9c6c9089465f98762d81ba4272b6861121b65f3c5d", - "0x80f61d086511b9b8b2033921336a68adde99cd25fac71d8f8fd0e476dd30cdfba49363784f0d0578c1f648f93ae23f8f", - "0x82ca682cc254952330d1be8c0e53da24aa943ffe0209b00bbf046e1e4f9425886a01d6582e2853137a9c256316e6f737", - "0xad1d508d2ea2806c351d5bd1098c46ae7ef83f4e49e4e87f83fa2c63f715ec56109996284a541c2005693687b4813623", - "0x9061817ee94bd2895064f4af04777b499a1fedd9688ed64bdba848202c3cf9286b699c92400ed456db926ee23a34f90a", - "0xa8bda55cf6f3f9edb78b43a52b7fe76e5cc2cde21e08487ea597cc266e54700ddcea1a287ec6d8f16b738b67caa27152", - "0xb605576e55d1fa4fd9d7fac2ce549dfe23fd6ade41fa859bf809baa3f1497d078cab06a257ccfd6cd59f67f17eb22f5f", - "0xa92d22ff5b5ec6dbb1d57db1b740521e82b4bef84dec3e130cab63d0641c3a8fec1f6f86141fb1918dc0f3fcfcbd8cb6", - "0xa0165df8dfd7b3cb58883768471cf485b886ece529d5bb78b26acf9ef6c44314cf9f34914233c93b10b1918533dcb8c7", - "0x88b79c9c721c1936fdbe22d68459d1033fdc986d3e52f39341ab06cc85a3f230ecf0965ee8d2dd54496981fd08a02657", - "0x939b77fcd53a523240bee730c2d7b8dae0b32bc3dbbd31428c7b5fdb4c3d34afe7f2a377b2918497574606bc06cac750", - "0xabbf82d0156439761b36a913b661e3d452dfa57e443ddb61613f80e110acf52765139fe3d1dd59c9e7773b262140cb90", - "0xaba28324844cd19b2d5d07a87e6f3180a3c02c7326bca846c1e7a7c131c7ddbefeabbd6787b4e1e910449f3cd1249ed6", - "0xab2f71af8596c10351f7ce9c3a9bec08a5c7837cee92a7400826284752c98531a0199e2a7f9ba7ccccc8fa0a2207aa43", - "0xa71d5a4f8af3a16ec9c3c110ca2135c68103109d4384a299cb7ed09d96231c90b04ce34ce12de02a40924d84947f7f31", - "0xb9dd79bf3286ea08c9b779910c84fdd02a33dbff7adc2d6612cd58e81aaff3f64ba021f875ea9e1201243ce353510350", - "0x9838fce2f70e7c47dca7239883229c1573ea97d469f120e4af659b18bca31cb68d12220fbd6e4e9e952b28eb29c1e5ee", - "0x8dd341e67e4c567a4ea95252854cfff8a7631c228ac852b33b2ea9211b2a6c606e4b0db28afec61a1a55e6b5f0a6604f", - "0xae9b02d60441859e3e6f3866a9bab8895f0cd6168f8e84dda7c9b1cd7917f1c454f10aff9a8de39909e36576bc0b4828", - "0x89fba7834469a06cb0da39c39a288245e577fd956c241707c432c2590e18e956e8ea3f67e4bee5a5562377617af53334", - "0xb7ab26d79ee65eb9612e54f41f75e22abd83db45010e1a94ce5026a24675bdf670e806c71f0964a33d6ed277d464732b", - "0x8a25bae10ef86d7e91a7d686965d17fe16ed635d787d4d6ca337b10ea32082938f4354620a72b5aa43ae62c7a0e751b9", - "0xb18fd9213bf3b2d7d191266c7bc1c31f683fc7da7dc5ddb4c600e1ebf5fa80a399af9e31b4ae747581a07ccb736b4b32", - "0x9968346d8a867eb57f628e2ba00f69e9d6aa8e713377a69413323b1b9b26218f527c0e719dcc1027daf10c3392f59733", - "0x831ee266686776eae4e3de1f2bc37761a5e1b918d4bf0bbeeb20b490902ae97722bcb1c98c485407491f248eecb841fd", - "0xb0e949d7c50b852055f38f3542a974bbfe7a33409d67c557d70c1204f87265bd7478e1751251792435fa22097d1762e4", - "0x8b0bee83715e20f2ef832347c926249b5b168e4ad87b2e5a9149ea4e07513e4790f60b1769ddd1816d7126a0f6fdbac3", - "0x84edc35061dbe8f3de90c2f9ace94be5ab4170b66c42583a0643ff776256217bbc6fa31612e68bfb9ab678f8e8e49457", - "0xafb4ca7a4781dd31a7d81ba8a739eb65c43f3374e76b4ffeb2c7048b055f837e6853b14ed2d3224a40dea35799f0e4a4", - "0x9945fd5ecdda5ac952785310e87917126917fd4f504fc5565c236db9b96f9666934766f46a1989c1aa176e543c6e33af", - "0xa6d4466b53c48d7facb9cc33ced1bec98897e545b10586857e896d35c850f2cdda65e19bb934a8c74f6def805b1df4f2", - "0x81e3fe4330948c279d99a8a1a4e4e141a039b3ccb7287aaba6f9041c3a8a41db1a4763fe04a36bdadd3d3295becb9d41", - "0xb6be2ef16b60a78b17991d27463e401eca731129843021e302830c2fd665726547240ec3a3240586b01a05ca8206dba1", - "0xb9d7fe5671b220a3da83bfccdc16c0b6f5e9e5c87810db14f070dfee582fa190a360c62acff13cd877c818d705a8a872", - "0x86867f22bf6b859e7f0ae7724a1174a65c4902cdcf74bdb22415875d72b67f49c62ea8bf9ed0d6883ab76512ebb951f1", - "0xab728a8167b9e82d608d4939a0712f82843f624d08d4013dfd3de41bc526e9d495cbfd40c443f67ac59dc4b5f30ff217", - "0xa5c4d10a04452c1ad12c18ce8ed7eadea1f3cdb34fa5ce0cbd804f5dd92eae2551b771523e711e8037770cb66d1951e4", - "0x8808f69b975f363bc08f8578729a6e68445138dada78d5818d33fb83a7af6cc6e7030f7b76286829861a4534e0b30248", - "0xa280773d32e1ce3544d3ba5025896d21e358592504737de72ae76d164009fdad05c8a1e5e1f8658ca6374b347d47c29b", - "0xace91a3971be87b1ca8e737802918d86375088e74380c444751c65978afba2b017cbd8fdcd3f9a0c19c0782b0034a589", - "0xb5445d816d65ea36c9bc6a3d5ec44ce6b76dcc18343d7084567dcf2603d2af93fa8469a1c493e19f1853c96f89621fce", - "0xa238867fce5b09e8695240f936a3f3cb12a715511b7516de995543b2e15aed8860a12754ac8d1c5ca2364e4471a9c5ac", - "0x9467528341f5b93b89c7f37c5dac8bafd0af620230a9f7de3e809f01cf73b8ddf70c38c5023a631a1978ac05ca35c318", - "0x8e5f1c3c411f0939ce4b6a5ced42172fc5c3774f596a114e7c5c8ba433c4efd94ca84affc0bfa89a1c5ace5090276a43", - "0xa6351818f7553d446cbe8d3a318841b0607d1f1890ebf9c6220a092bad3ece9ef8acad4d17935e437377af8f9309606e", - "0x86630d0fb2bc104d8cf840b0e545c0c149c1a8e4dd6d460dd15a52a5935c8ea5c934ef099653d783894a6d1f68414a84", - "0xb357b5d9cc645b645fbce2020db583cdb68772751d6d11d635f1e3ecf995a55bc374be7750b6e8bd4968a55600ca9806", - "0xa9b659b8cacb73a81093eeec42dd7f4fc5d955f9fc543037f31bbcf456af6476f303aaf0ef960a2df88365c2704bb61a", - "0x8b6ff5201c15cffe64bdeb818422fa10dc503ef2a6a4d686364afd0f35b6473e4463719173550d234639f6077e19542d", - "0x98efe45bca5ac679cadc25ad0bdb1f8deffba13d2d7eb14c6149d5addfac06b82fbba6d24b323d615eeee1465b3cc30d", - "0x8c2329c976d78f1d5e30ac34a3fab1f96436947d85f0dd190301a1868e5dcbe4ce60f48fdeffc3e6a05ee34a461d7dd9", - "0xaec012ad25d99ce014101d7da512fe032673399526435f6e1faca4b63759e8f6694a46ad01672da9eaaa4634f61ce89b", - "0xb8d52e530c942c3c7a67bbd0366f4cfdc6a1a075471878516b7a2258aa073eba50a113cf433879a0e15462e82087d17b", - "0xb40c5ce16f94837c86e81d98e2130a9e1dd229da5aea52e79cb42217d3b5908a53d76782cbe3934fa8769db58b00dee8", - "0x877300304eb69720f7cfb4f907b4a7e238920fda129a38516dffcbdaae2e46633d31080590d6df05756781224d532fe8", - "0x973632dc791a5214516c3e59b2b48169470678b7dab66d513e35a0fd1df86b992e27ffe6050a5233af20b5d4998d283c", - "0xa8ae0e723a8ea6e95d721337465a388b60b92b1d9b1deb0b9f59ea30842de356184fd55d9b8331d8a29ef473c1ac2315", - "0x92ed6cca30f76135c4b7e7893c3460501e92592f7d2d6409c1e1d80074120243a5b9ec14d801991204f5ec4f94ff1daa", - "0xa9f575b8518dacdbc5cae766389ab2ec01c876038414b7796f640f633367a5281cb49b48b5e80f6416a33b401c21309a", - "0xb9793588283cfdd47cc4547cecfd987f9f8f92c2b408725f39c1d879199d695e87675fa7e5a190ab3bbc97683a0b9587", - "0x8329a844dd67dfd48546791c4330af65501baf9524ecf8ed4fec9ea87067d0afbd33099052c1c2df819ca1afcf25dfc6", - "0xb908eba1b40edc300b63ff6e20e87b17e6dfe975c37ca63c92e8866968070a2c07204264646bbc9318145fcb90c23555", - "0x8123871ed78f46e9eff4fc7af9f490594fd7c20fb814e505481ac5c7bc7588c1706a79b14b85d29bd7b97d7c82b2ae79", - "0x833ed8928f154fe0a88ae98e5d8c74f816e3ad679c1c4ac1322604093e85ed4b9b9c4361ac188f0da5443c72ee4bf3d4", - "0xb9fcbb8a422bd8d996e713d176b7e63edcc6d73b3d1fe3f2c4b59da637a168accb5fb4d227b709f979742cc0af8c0ea8", - "0xad3759a6a6bac3047935443347e3c63819905f6c01f58f0ba76aab422d723cee10c769663be9554473e668bffde1d500", - "0xa60c1909703211a93d7b5e8b8ec1cf4ca06ada653c27696a7dc9a2ff75cb712918888c6b61b8f792ce9b413aac09f48d", - "0x91f05985ff17f9ae20498185f6558f9f38b67966876dcc6981af4d179cd055661adc63155f4afa6167ad61b7038ac49f", - "0x95c5add9bab6b9792517772f9f8b21bf7cc325dfd13a43177b0bd982d0f620185d8596c2cba46a5e10aae597129870ce", - "0xac0b4b6e2b3e417166ad9b17de0b3ba775df6ad3a78ad13a1892c0992735ae54c06b1e6123b0c0bc90544441630c6a1b", - "0xb0135c25f74ae776c241faa6c91a3f7ed6138d19a2100928b7ede64b79e177d92c5cf921dcce3c614e32de34975fa6ca", - "0xb2215b560d5a36f045de7257098e9d75a40122919d4726990b4395eb2bf1ec789cd0c64c46b775f6a8be28f23958e17a", - "0x870dc7f7a513728f2b428a3c08b15a6af88a288824e790f41b1190fbe02b59dce2914a1339f7203cdb7f2f9c98d8d721", - "0x8e3895f03952cdab36f602418cd746bc0b6a07629eab0a20bbd8de6c993030c5287fc146fc45fe97a06c992e0a9ddf02", - "0xa4cea15ebc0dfad9feb3d18168fd33768e8ac69e263263ceffcdfa35e8638711c2971697b7d5b2aaa0fd8c5440f3e164", - "0x8cfaf5369781a59f4117283fd3f290b81816abd3124a9486ab1faf7018d36a73c1630efc4ad648ce462e541827d51975", - "0x82b420eb25736126ef18d91e91ca2ecaea8983b8091df88343e8e54ca5ea7a3da6918c97695cc0cd5c2df95afb1e3cb7", - "0xb3c13923a3d46d990aaa6a1eff3ad32f162ccc5186e16a549dc29ad4d63de6287cd05579452785cab32e2485636d568a", - "0xad8a43ad6195e08a36f755dd536842ec88a7d920bc302451c860444a3fdaf294e5b5dc5a122423474d322af5de8cd8a1", - "0xae40d1a90a77965366b5b5ce87d6fe86eb255cc3d127526930d128ef7763455adb82475ebfb7be31f9c512394f2a22fb", - "0x9763bb9459fd4c0de2534767bd99f98b859030b6af5739a7081d889d6875f5c23f0154c30d00b7240baf6450b4459987", - "0x94aace9e9318d79d3c7ab533baca31724bfec839b01187e326b1fdef846968b1b29882f2520a9e237dc41ada01bc3761", - "0xb6084f9e0051be76244ead401e8d2758717e93c4cdac58443261b3603cfee0eaec7d758b2e4357650d2c1f5391edf798", - "0x8c656a798fea470163e70869a13edd30d138bc148460d122a2275df8cb43f2b45a14e0d8a8a49eeb7c1afd02484b6ffe", - "0x8ec317e63df2881f49401eb2f6a82e261b07474006fc293bbb54e0fb7437697b16ec1d6ea101fcd56543bf4d69374cf4", - "0xb27d9b3b8c3cc59d08159c765d24fd4660bd0a54b2b7fa9fa00b47e6770e6e8d3ca353d305fd772c8171e20765c8a66c", - "0x863ca045abc38ceee09c4a21a3dd18f1c0f70c0289437957aaa39ff764760bc422b748bef8ef133ee28d88c46e6be1c3", - "0xb0de194caa68f5288dc365faf9e9ca3c69b0a8376cdb532cd6f1cc3478671a1e755d0e8afbde4e3a88440fd9cff4e8f6", - "0x8a259f48cf5a45773522f3c5f283a6c01a0febdae09f873e009e4635c57fe5060b01243b2e5e1c9d2ff7490f2dd3b334", - "0x8c4398e1e579778c88976ba12feaeac0c96fc97b4e26a133ae74fca1b9c315c1112ce3977d20fbe9ae5866ca6544fdcf", - "0xb54b25aeebf1917bb4981b43f39491918773bacce37e994b74f877d4a636f1b3f4a2f164b858f95259f285ca0c294f24", - "0xa9db33b15331e852da3693f6328bde30b8cdd79c9b9b63107cf78dedcf14da68446c462720b9ffa5a1bfdaa68f5d931e", - "0x9966b6bea54405df1dc4edfde9f8c3ed7c0733d5a73bcd8b349035744d5eabbad0d19801a678d48cec84c0335346af33", - "0xa3d0c32b5e3036c4a4b222c13f7db23924bc2b2f724bd908a38db3b8f9c95cf5034c4cda0c5083c0967d34061a216b57", - "0x92ca6b883b2b20015fbb56cac4c4b5ef22e555a9b75f4f020822fba9167eebff8f9fe5c729c574cfa5ac27bae1a83fdd", - "0xb72b58d6ddf54c2d37bdc1599ac966c54cb4926c8d2f70d1bd4cdc383c6eec5e4b87efc59466682f8db964d80a4b740a", - "0x89ba63ee57a1e6f13d7c66150a8d6721329b385eed92be3ea784eed89c76a1ea88595725612b109a9e4aae41d3f6c972", - "0x8727bb53bb62fb714e4e5de461c6cb298730641e38a0b49b3b3d4a29fa24167c7c6f4ff47f4f3b91e464a581a4181853", - "0x816699bc7c3ed65747d34786b7fca4e35e79907f459f2df0918669adee54a70c03580c4e7d2e410ceb45c71fcadd44e5", - "0x979688c14ce623dd17344e67373e5852bc1d3ea12d37f7b28095e5d578d8c9c646e4b97a3a69a97764ed0a88f62c99c7", - "0xb4539a9eb6578ed3b8dd54cbf57419e99b69c0ae1ca3ae3b4a21f204813b2a78438d6c72f86c13dfa06a0b9244b98688", - "0xa5d957181c30701fe6eabe3e65a53a33dc43df364c45f0c4d882ab88a069024bf04b71015f1c2fbf03f368e63bd82fe9", - "0xb9ce9a54d9b17d4da41ba3135d077c546cf39dc83230506a4ee88cfe39e76f7e35664ff1b571e231054cf1b764b9267f", - "0xae6bf2eec8046137016ba94442a7a0aaed0924ec1558885135fd339d2996aeff31ac29f1de07e84f7b7391fc5355f429", - "0x85c7c247766a4ca44278be81752f4170dcc069f76992b236b40e71e31e08f30de6a5ecaddc44debe4f94151cdd8d735f", - "0xa19d41fcac394b750248e575c300b9a96dfc5b3dca07ad6e1d68dd3f8ab94d10aaf8edf500e3fc7774e7ee52935f73ea", - "0xb3c959a22fddce5a2e199bc8724e825a6d9776455c033299b5cdc9a9d184be169d807829d5df5e747476d172b5701cca", - "0x916aa7bc58f34bb8f32808858cecd3e90ea26c3ec1f80a40e863ba18fe9af6e67c0b2664a2274eca6d36ed72e59a9341", - "0x864d945b7be551926f747406d72057c7a141110f5d269fb6657cf347cfad7178670dd294f6a98c19dc0943a68d7ed45f", - "0xb3480f8a42ba0e8eb020c2e1c1284a8a9102fa68b43f6eaf28e031621b9f68bc399899e35a1a283fb52530c8574484a3", - "0xa8cd1cb93974d1a6072ed51f356449ac19b04539517cde34bb7b2ba55949d213ee07d387ce7b5534175bd8a044556ff3", - "0x8e81fcc5fa5579f2479011caaa393f47a4e12828e2e82072736d85ba1bf70ffef9fe3b2c22fd11ce8eaeccdfa2579758", - "0x897f935b4542b9ccf8c0660c8fb1a570a8ba108fe8440e17e6c50e01affc2a8597b7f7cde5244c7026013b52c7331b5d", - "0xb9a20f612c74821da05f48d8bcfa7a4a550979e35b49d52031be8bc9cf717fff21db0142b633465c5edafc42b7c73c84", - "0xb88caeb2157d636fe26d3b221143443940427e8722596746bc337679e10ae6e5a9b33c456ac271f8b01db2f5d1b00a62", - "0xb23bbd978725aae647ca2778e801235f605dde17897d4d56914b0d2241eb31f930028904a6555581ad5b2b74ec3c9587", - "0x97a331ffcd02eda1d6e0e15deb110ad6106d3159ea641cfbf424d2e3065bf65c9b14f72a27ff3f576dc51eb068bfb22f", - "0xa9317840cd8f437ea97d80a3f445a99eef463a5e2beba3c986da8fa67def4ae9a0e8d1a675a35e5616ee90986366bb70", - "0x8c26dd7451b12c65351d5ede6a00ac7b9316f9e28be8c692d20709c3b4a5dbc76fb914667a2f1e9a654f8d2850b7dc3a", - "0x8bf4aa18a988f82dfc54668bd4ad5161f276e31567c949b7857cec331c74c6b68849afe852892816c802736cf7c547c4", - "0x836fd166bb9689520cefd6f23905e4c1260f97167b17534930923107fe934d4afb1216e4b89679a564433dc952a77b0c", - "0x94d6a5a4a11f41887eb814acf9b5a031d013d614621642384504eb78e65b6a07c50326632af47b408d8ccf43faf8399a", - "0xa213812713128750bbc5311dc317992bfb5124fa067072891f452880183d64d6fdfac8825552cb809178a3f3a641c9b5", - "0x976d1290308868c5e41dd3766447b29ab8c3b72047a0b7de85d3ee5b1e13d522147a02572cc0d1ed8976d411faff5b9a", - "0x82a4494a95738ebe56578e1e4c0e486eea66d5cc44141f478bfc5a6b3ebbae6f32063725284df81b438603aa564a2b6e", - "0x8a6f4dee79baf71a4a40843437c16b2f304785f3e56b32d9ab2474666fce2c7749c776bd898a65f4a4d542a497cb6d6d", - "0xa04a3484be07c2d60f1a90f9dd8d4170270a808cfdb863864377c2515dd71c152920b65fcd5f47004d27d14d7ee7eaf2", - "0xa984f6633ce3d42c75083ef7732e5d0ea15d91e73cf893be3ebac5e56defb8db97088c5cb3acb661e26bbb354ad91ce8", - "0xa5ab5b4b0dab86706d68c9ad921d4917215c4fbcadc8adacef7309c0c853bc3c2ea34b3868d8f03cda6f504793832594", - "0x88f03e55eb028353b70352dbe91f298ade322951ca115972f1207744254fdd01ccf899aa40ca747da8812dda5bd5f985", - "0xa4bab627f7de273f8085169cf05413bc368c5d9e5f58bf10995a8bbd95e511b1ce15d008405728ae8e8a83621efb56f1", - "0x8ed518d0f225b90fe7f01b0fe4c451589390325044f0d18a8c47bf13e24eae8627feb0c9e9514397536f73f33f67a044", - "0x97c73837e77d965f401b4e4f089ef4de7aed1126bef6be4e9002b2b68014b98997213e492f7aabfd2e47cd0917a11d6a", - "0xa99e8a55ed0385bd279e11a80255b375f2d59bf8b0879bf2337ab5e3be450a2ec05d1bd8867a633e359a02cece4dc1e4", - "0x82a74b5efaf3c217ee2bb56c9b8e76b3eedfc553c73177e59d982f503a5b0572b5cc0d1292820823307eec956c42b28d", - "0x9800ad3e10e8a19d65d5963673c183bd536b65e14ec18dca45e881ff3bc74eac32bef2ef845515ac4fd6caf558a6926b", - "0xa2933c78a67cb40489ffb8096c021ca017b99feda1f9c5d702227d7f0a2ff66a539d68a47ad90ffdfb5c31c774946f87", - "0x947b29715258ca20da5b17a8e3d99665b7e599aa5bcdc5d2d7830a2e3cd78364d51a3d7c0d8bce48a1992b27d1ac4980", - "0x86f2e2d3e160d3ff979ca70c456785b4b2437eb64e58adcb78c4aebc96b470f1b8b999a3ce8ce20e3d3f030d163cd138", - "0x958f4435d35932a91eaad0dc476bfc2761a85f336ad2ca6fe0c6830fe54e8f417434616df9e6f07a9454a4403b00b64d", - "0x8b1755af961e0f9f59651d56b538ea59af489e859a1c93726cee62649da0e304093d62db9a2c5854c8da1be61bde990b", - "0xa5e11042f73f979c8649592f6cd01dafb319344e379a65aa9200d3b636abc569edf822c2bc12b3db5c30b9ee74f2c981", - "0x92ac5584de1adcd38a2ebe361225f224e9b498344521be519faff77f87c1f22fe8e112f9df7cf960b16e358efca0db08", - "0x81db84f05f75a218045d7d5fd4620648bd4a95cf468cbd69787011d615778ba7300b729163e7c8abd1a5b0ea66fffbf7", - "0xac2f522e9f030a7c576fbe19041f5db3913af58da75b87e8ad64b93bb34850a79b852804dc68ad5e7de66d90878544cb", - "0xade9763d1c7e9f68b5f817cdfeebf31bb3ec1391dad04576c55fbe4bb13cf0d45abced3d51b5512c73b2d0f403906340", - "0xa0b431bdd9641595fe1eb8d96ba4fe86a447a31ccf36cd2f7d94c5c86a7d96bbc95b204fcfe7c69c1385997b1daea3b1", - "0xb3b093bd8fbd84414609ec9a108507f97d7f77833b93b15439423d2a2928e40b192247c8471cdbc12891d83c765cc6e2", - "0x8531a5ce8e0c44e887ebf4beac65352c8a9673c51b6a1edc439e08bda1354d359e1ab2e27b82636c6dc0daa3aade931a", - "0xb22c2f3a77ae4813a75004dc2c9593cb2a51c430c559bc7d07d83e95592883b99fbd0f9ad24d2d80d86c871cfaad2721", - "0x8b6dc7d5b8cb6bf36352fb19e42aa37647505436e1442eb1f228b0804916d569643102b2282ef66bc9a4442520521dee", - "0xb29a811ab81dba820242a990dc774cd937cd299495cf721cd11971b9f1dd9441ac687dfff0e91656b9764963a56e4625", - "0x805b280e31664008fdd874bc38e870db271027da70fc2246fa82c499742a9a8de1152275e0be61f307dc8f7a918e270c", - "0x929f690538a500d238208930b55caa9c489bfd3476f6be2d385c36df3159dc3d8bdeb24a1ffd7b028ff4d881551e2888", - "0xa92bbf103ad851a41e5230e1e37ec7802e08f4610c0db9706806afc4a247679b9525f9a534c70d970a1acb47fec9bcdb", - "0xb9f2698a39d6d7aa8aca181fc5d95dec796ed6eec002557d4b63369bd90aa4438c27ab90da4f3ce81168cb42f7400070", - "0xb08703bc97292c56833d8e61105f1431c334f98a7946850c6175f37f703ff790d9a1522c0003f08dd111eeb083235073", - "0x9355141cfadf46f37afb73414c8803f9094b06952c9fccb24a1f8c18a13fa7b1197321b19cb832de3f83ebdf8deee53f", - "0xb7c23f7cd8e212108906b7809df90db58d2c2f3a8e1f775274181bd81c74fd7c2f8d68bc7d4aef639ff4e19f86243f98", - "0x92728e009fc3faa08e81c36c268b3ac18627da7618c96c97598b13242286645789c15c99518a07e658d92eb8d2b89c79", - "0x8fbe36d4f2f08cd6245e8999728884c636a264451e4ed894d2116375f3d9eafcaa72ee59cf7923ed8ddacb53cc478761", - "0xa6b2bffd6bf8f54231fabe46ab2c1d014ddaa797d08e5914f13988140bf804019fff3ad07ac2cb31283fc3e74e28d0fb", - "0x886387540b5a7acc8b2bd107124bd17d6515697e09c85c4e932a6421965c872f014d11d1ddf321651e4b3564eed4f252", - "0x8b81f3ebc962e9ecd13a11e919d86ce14dd89d373cffa158b807fc91555a4ec1d7164504fb67edd9599b10fac5e32aa5", - "0x91e3213ded5f82e34389408e95d4f7fcd0f50ecbdef9726a289238e4159c6d3cd2f401479a1f785865e91ca213d2f8b3", - "0x99154b88ca5462f62031300177e571708821348e1027cad4867eebe42a6fe92a58ee1dc21da9031002f1b051351b3785", - "0xb5c2b7cfd87f2f65df07b39f8a26dccb16946fef6b89268b9300c8529d730a1469ba565a480d7c5ae9df8600ac50e90d", - "0x87df32def37370bf8c4c3a22a670bf5605c78f240eccf8dba13bf19c8a3a9d0560f8899259c4e51c6b0fa64d7d1e4c76", - "0x980a20e5cd352786bffeca1b8a31930d8898eff9f4a6b2570829248410bbe1c78105b6a61cce7e3ed1642e5e2af127e9", - "0xb18b8dbb9eda5cf333ea29fad7734235ac9e7234b49fd04f178136b15d97595d5b415a92455a319ab594b81200cb17d5", - "0xb713a71be9bd22ef6a2747d0bc8f4d008cdf6182e287c1e0274689e915a68150d6083268188c1f4a7fc76d21a219ec85", - "0xb86ff129a981359972bb793a81fd422e0b37f89e76fea70da012fad160b9eb7b029ced81c7e34679f6897a45b4e8da4e", - "0xa74a4cb9707156e21caa20b95a2a4b4eae8f773cf679e2073fca2cd3b1e502ef06de8a3c010833d525a7f8bb6bd24601", - "0xb51f06da38a76c2728cd01f6073f402fc49cf4bc5c60113a2700b5bb0ca500e465e541c467013a2804bd7641604bd2d4", - "0x9855dd73307d8671b6f9ebcf676de3ab7e37e7ac1544447c7ff34a213da46123b57ce23bb0f381da8fdefbcbe6c35645", - "0x8fb382c63f4c935462d013a0d3e2321d72fb4781c10afe6e31ac51766832218a05addc6dbb1f644aa61b5da9bccfd5ae", - "0x855dcff23e0ebbaa3562fd27c43957cfb35d492837aa71f27cfd1bf65a59a12d2beded9d09f3ddb4f801aca8cc34d2af", - "0xb7e7b317f10cdd13bc879c2fb0bfcd137af23e0cb70917e48d53b2bcf8c157ed7e5f58cdb966383ece9d3a4c92012746", - "0x80d2f84c39422afcb449aa68b34fa9d72e9de79a473c3ea5897f6f3576d2bb6fa2d49f0b44aebe5e68b11e85e066e028", - "0xa35b083749f8a5551f0dcf529e845aee189cdcc6ba779f4e88765adc49cc4779cdc2290598908ccedd8dccfdce29d53f", - "0xa30c412f4bbc2de80fe5c577b4f94442255cb3061a40649b0ee5357977503c6fe54821ecc8cc92d5056b6977c4695e70", - "0xa2ed0d90ab612fa3526f7450a43d45a2d9e886f2e5888ccb8405adeb8ca3e41c6a94d18a54b3cb1eab5b8f3851841ebf", - "0x8d4dd3f8f8a3d69bb217d338e757c814eb69e6a776f55cf51fa7c1b2f1ce5f8e9bce8353dd335e793d68eef676cf7c36", - "0x880d1ca33d5d3bb47b788a7ec64b9130752610816facec99af53b6e58a7e414616e9c815b1bad870d426380085f6b5cd", - "0xa287578293da4354f2c3c46d637aa77b91526f9618799dec4bc602305ffd8336d373786eb67eef01dbaab88f07f292c6", - "0xa86d3fad257a64c84954a7530822346da0215ebf4ad9c583f35cdbe16a02fd70d58ab34c93681fbf55d6075db6425cbc", - "0xa7bd884d343a6bde5f6c2512d81ba701fae7afa6389389e4776eacc0698a54c3ab1a0e1652c1a7a23d3a1d2a63cde8c6", - "0x8e0653c8b7279d5c958ab1b53dd77b73fd30d9781630a870d0a75681d38cde4fb7c2183b9c5758596ac556578b43fef3", - "0xb76a00c6f5093e7b28703df85bf968dffb70c455c91e75cc81189598df052244f7549d18e45dc70d98d3d86e0094ab2a", - "0xb270f2ad3dbc8b43ee2603c4e641be76820f07a4757cfa96be2be9c310b7e39b574572103253594a51fa9243298cbd94", - "0x977b8b86841ab8be7d1d50da7369e2bf71f24360aab8448d7748d59e010ce81bfe79530ee6f6644b987fc0d83df3ed15", - "0x8e18bc59841b7d56f8d9eff8818eee06288cd6ca86200eee7b5e6b230070debaf254a2198b4cd7dfbda8a1d55a916c8f", - "0x8e7a328ada969ed6289972b7f43eb5958d23688603ee6d118b6ccd8978378dce2d733ff64c30519b19007a78340fafa9", - "0x98a0fea70a219292584c69546d6d242cebb2f1d84f69c5aa275a257a87de652e721078b983ed67410e3a5eb0cfbb2bdb", - "0xa09fbecfd05772a9989008281a9585accba3850831485802f042413da533b1c7ee45a8cc679804340bd9142b2f9e0069", - "0x99890a6b273a2787fcfdd8e8500134efd60df99410e8432664a3e5325e55e78942f4bb11024c90e4c3618a70729a277b", - "0xa5f3eb1617a77f2d5c76bbd1bc3546ad1628be90fafa9a8b62c605d04e599ab2eb74b25afe0e68fd020daf4868dadcfb", - "0x8b53517d93f42b833f7669c131dc67f14c3b0639c46d3b02bfdb24cc9e642133e0c665236a7ba851c100ca733d673341", - "0x849fd288217bdb154213e79abe1a78672903e15429e37f6846019986e1cc8dd2b3ed28e4cb52dee1762a4dddb9ca95de", - "0x954d839198c3dd2ea1ffddf98050e2c52ee81b89f38d967bd30c6863672e43bfc32e1030bb12f5aa424983bfa31dbf5b", - "0xb52fe86414a98d0896d7a427d57739da35cac4ee24be565956d15a5c1cf5b4b95e5425dd2607fb9f6d6024549b59a4ec", - "0x9586070415a6bf1e11304d2819330eda88e81a88b9347aa866692c163e1af772be9fb747d9281d7aabaf5c9934596934", - "0xa5b78e5bea362df26a89df682df61287763ca1b87ab9618609c99e52e6ba047fba7ec828c0552ee26279aa8a48751334", - "0xaabf36b9dd465ae03551dc82bed9cbf1d22a2236ded28964334f7ad474f317f4fb8515b853354bc06181fc9af82714a4", - "0x910f0b2efc608cae8cdd39df7a5ef9e570592b31df2331baa7721708057188ae96e1b43e2f2f2c8cb360b961d687b60f", - "0xa5c5b131205c21ca68d6103f8499279621da337a743e4a08547c3b4507d52d2d6e5014fa5d920b351a6f53a195687766", - "0xa6898dac2d8748b8bae155a7d8c169e7eded73cace1e382c4dae8633f19463151399c5cf877f8ba344a698a98228864e", - "0x92919d8be671b4f490efb49bae145f419c84a1e81d3ef78761fa326f67d749ff3530f5de04f984a018065f42e852e1e3", - "0x81083de978e025f0b5995550fa17915d02489344cabf8a79248352d78dd6e893d28a5c5204a65a8873756a34ee3c0120", - "0xa6de92ecef84d188cefe29a03b564b1e7bef2a6afd785b58897f7f97a958573a35aa0767bef12a49b352de30b4f0dc18", - "0x985cb3475c7a9f582c11784cf61a1988240d74e49084a4c0f55f3f6068c4da0b08b136f8fa62e9001e0a265bf65fa3d4", - "0x97e6d360b504991d51119a78c5b647f25d5fcc1298631209d82c2ca40ead0380835fe3cbf8b82148b0b01b8157e884e8", - "0xb313df44b2c47126b58064599a0dd6ea49e5ace9ffa663de03ad30c1e95301cc68eed67d37ae6238469e45124c59bd39", - "0x8a58f70545db2242cbdbb12492cc11ec4d2b2ab0ed8450d21ceb573558d7bda91ab03c98736e13d041bcab84fd8248b9", - "0x9077880ac352a5ab0e5e15ac89b14d173cda0b41b6f7fa66bb357195f10cfcf491fad6bdb49d71cc20d99cc6c8e28d04", - "0xa09b2930fb3b1a60af8c5214e8c3f6deecb3fd3d0a5662f3885948f48d1836b5ad3dc74affc54dbeb5b522b90a17dc4d", - "0x9163bd2e5f58fb1d81007422b91147685542fb1c7e2c8421af284c7cbfdcd2d2b399a37123b58a2a349f27b31bfa47ab", - "0x8a3d859f141457f9d63818634f81deb5c858ac48bfbf2e1da21f4f0dcd66b3e1d2d8fe99c4cad38206b1e15dad94934d", - "0x86d3fec476b59782d0477ff333fa79922fb9fe3d6d6b6c5be9da9e88b006b46b2a0f8f86ba4159c5085e66e32fba67a3", - "0x8041cd57335bcdddd37651de2c3e92edc600ac23041d0e383baf55651b1b0960b6a601491608307160f0d7d48ce395f9", - "0x805c284059f8c03b2bf006b1af95ef726874c5548e93ea965b402931f42b189f9f674b6b52ff09df35320085172973c5", - "0x8acf781a0b40cc56b1013cc1fc3bc43036545ce35591f3b905543c09cb1ac1a70a074202b6d5ce3680be913200c58879", - "0xae670c448996156c80d063f1dfb03d7770201a35c71cf8e70b38d52dcb5e2bf73d5286d63ba2f561525d62cd67d43125", - "0xb0fcd0150fc0005ca438d6b0fdd6a70b121d35ecd74e62bc119bb0187cdf6bf674ce9fe01eeac5d46a68ff4d4210ad09", - "0xb752c6850985ab13a057028887bc84674697c012e9da0265dd5ce1e48f0aeddce5e07e3e7cb68ae17a648cd1207eef19", - "0xa6a5c71915a980fd0225847b45e2e9f3731c6b2a627cefb1e2c6a0cd7f1d0555dd32b6b601a7ae9cfc4b9d06a56a578a", - "0xb7d96f59a988a7a810c25018f7f85cd6e81b335a84504ec76c97d7257f9cbfe88215ec89553f0dbf39507d990b3a7f84", - "0xa7cea7b3ba43cf6ecc488c34511b17fc7b97150b2d265785c09c676ad3123b322db32e043c5961384ed6d90d20c63061", - "0x809dc467b304e9bda732cd92b15c0f9b363cc707432788971508b8d60844911ed4edfca96d8cc20b9874f1e38a2d1685", - "0xa5b6a089e022fe460d62c4c5228e1381902c9a796ad92c03211c855541a7fe27c5a39d9123b001b0b892ffdf0a1fa065", - "0x95d67a21154a49bcdc79ed5f2773b651c81fba1ad82bd373239f09a67a50371a147310623fcbc1211ac57aa154e8b300", - "0xa4a4f0ca8073407575dfd5d04ebf76f8bb467598824f2ce7fa74756803d9645d63c9eb3ed39aa202dabafa4ff0a0bf34", - "0x8a77374f6e449d94a443f2d4593a0c3e4925527e0653e873dc20756396a9a4e5696fe44fc1b49e456711259deeb3f037", - "0x82585a825011d6eefa85cd530685b103862aa0777510d22942d8f77a0a7f489f5d10e5b36ee38f66cc96dc57d13f5893", - "0x98e24625c31d5d97c789eacb91c3d51cc6edb38cedcc474deee459f55de557c42e4d0754ca4ce472d0123638eeafb55b", - "0xad4351c76d96c35ee37362f2384ffb809bf6a47213863330aeac1ff9be2c6cc7275f0f974e46bfb716a89ce1bdbd0710", - "0xafc8f5af4f9c38ae672d20e7bc3796aba23a41eb033619b4c0a06e07884e1e0c7a7326f069068dd22e69fa5f672efece", - "0x983d5af05af31f9082f381378fca3526f88309bbe51d0cea5860813bb0fcf6b32a3be110336bd728952dcd6ff8a26361", - "0xad3b55b67b64b188447a1fb10d027bf7f86ce0a0fac966d709e8b6ccdbb7333964045f0c4719c45c36b7f3c9ff73944b", - "0xb410fde230d8dd24b9f1bdbce8338b05110b130591913f23a34c5fd092cdd3f747c383f6967cdb529ade1a264a3ece39", - "0xb3e4f0a046f93c332be07058db00c5182a498987759315bcc3a58d9334e09a59333031c3144b59d03596925703491cd6", - "0xb77e58619c8c471531d9b2e5dce8f82bb8794223bc9459599a911440e64e0b5be1d37e289807733ddbc2858bded1c34c", - "0xb450945bc3e290df96a196083a45aa929ee080bf45112e678eac0a939db2ba67334ef782c855b9b354caccd94b3babb4", - "0x9794d81e968770a6e12add60b32ccbbe80cb2680b157d125461cc3db998691e836d98cb3b3cfff4f156b2800d426b955", - "0x98d1284b4c035e93b4ea0431884d91d5a7855ac6c5b1ea2a994e653cf77f0ac1a771dc75899bd1485066da17e40ee341", - "0xb1da89b14efc14d15b2bc967ffab85c41dc447b6a7922b619b5d5b06dcda725bc4530959b70355ee20eee7c1802601b9", - "0xb8e50ae98515dbd9ccaf27192e58a5c34def86b1d0d181e63e64439107c30254267969f6069e0b863c254440c3480de3", - "0x915f0c7dc95f630bf1114b02e7d9936b0911a69c932950ecb7f800cb1aa1a4e1f1b6bef6ff4a23301cfd904c39025863", - "0x85392fe0edd316031c69d90b106b6685bed56a0d8d814db2cd5d77d07b18fadb632694a214a176ef60aa0f82ea14b00e", - "0xae4cdff23859b7570179586549165c728de4ca254a5da29668cfda259d43a387b3caea8537888d43f713d458da6bd4e8", - "0xaa0b6a3e0555d64a5cd1201fdff7ba7ff3019e9ada1d86c90c626a710df3d97d2ed62d2b63e5632963e09cfbedf83732", - "0xadd726d97dcff922dfd748eb897e540a2b4b8bdbb4eac1feb74717bf086b1760a957f83586a57b5345bf4c73d791ab9e", - "0x9721889b6fd55cf9a914e5aeefdfbfb94d379c6312001ba50ec4bb1dcd03f95fdb45041330da8871cf3dc3c6a6b5e330", - "0x8eb9417573ec6af24a610da5260639efcdfc802a95aba8efa829dd70ff179dec061da9facac95b6af02cba6a8646f7bb", - "0xa477ad7d2885e1f081556a98b3904cd75a4ac7a8c27fb0ccf15d117feca59f891a677fb4ff4fbf38203055a9436ebd1d", - "0x95b3b2ff92e8a0bace130d165984966637a74280d0e056cebdefa6f825b1d55c9bc6e13cc8f263e657dba3dc7fa68627", - "0xb096fc33c038b425a7a922a4274d01eb366a488fc969497a575587ada74b9452a607992aa2d8b9de66705fe20b4abb39", - "0xa813ef1053ea6ae8a37f4da722f16b6ad0213b0ec7829998362292aef68c28357ee27a406b567a629592447db8ea6085", - "0x84248425c3201ed389fa1b64b9e1d151b5a6f5fcb8f5e28ebd665db57156ecf9b2fa77bca857200df9f54383b7c5eae5", - "0x86d0a3c7fa1e64111115469ed0373dc3dbd448e1098250e9e8c5c7e775fd1f267d49b4123c347af07a28e686d5f357fa", - "0x8340b2ef4fc2afab3a3d51b6c0361cef4aec3d5e1d0f779f9fcb258711cb79ba4083508644e2bd182fb25b21523557c1", - "0xb840749c259b5af5874750853b4de6f4d7a274e18fb77f774f5f454c82efc5979a431e28bc8e43bb831715c7fda96db4", - "0xb168d333cf20b053c1b2a915c3485200a7590c3c3661507990390800fb95d3772ec6815d53aec5e2964eaec19833e787", - "0x8f1bb538dd5005384f38f88cd2588228aeb0c6313aede14ccc12affa9715cdb938ed4573c391572f0a7ba6e33a1ace89", - "0xae4a8ec2eb938eec00e6608c087471128b14a773d75a99634671f6fed95f7b24b14f04b3271d1c32faff7f0f2d98547c", - "0xa4ad66552924a6831b657f8b318f303225b2cf29b09790a48285b028bb1420c56dfa2ca0df2e823f694e8e3b27952a01", - "0x8af4eed962eeff534234d7c34f1033c68e8cf798c99880a67eabf38b533570a3776399b883f8658265cd14277b060790", - "0xab2c6406132413cba89a951d919bbe123fe4f220364ec2282d8ee0c140ad8d48ded0df7ab56f8f18ec7526ea2f1cbbd4", - "0x9154df8800e26020155b98f630e640be97a3ac41b182fcdbcf31a3e4f233810e34e224c97df8ef0f39ccca29a9921fb5", - "0x8f306dfc5b8376a88a104cdf67eab54f93e478ca09036eb780050ba2e8112b400bcc09d49665ab37d21b5a2d8440b3c8", - "0xb768260e94bbabaa527b2af8be423577cec3bf4aec3c569a4fb69e1fb997a2157c59f1169065d24a8aa3625d89d988fd", - "0xaf06139ca7d240f2495314d941890c078d504b2bc09d98a6156c373de29781e7581f33adfc738650cad0da3f6e07af88", - "0x849a6e458ab2f4101167cbf75bf47ec1f9e481f556b1b9d297a6b4737584011d7881695bbf3ba31e3e4180696fff6407", - "0xb107e7aff27aa19a4a92d1a65679bf40e85ac6f08d4e5f14859d97c170ceb431858fa4c46d00131527c605164b5f7bfd", - "0xa00666055e18f34ce02e8b67b6f181327ec0a11547c0795bee61802aabef9a3a76ea138b905cebcff9c4c86391763e6c", - "0xa65cd8dec5166949696dcccf031c300895c5fdd53709a1897c61d795dc22bae2f7717e7ae52a9950f9d00471ba6257e7", - "0x8b49aeac3550ef28b5de37576a5d4e2e43bcce82de09f491984171251e26c27fd0a884daa6f3d30dda107dde4544b34f", - "0x91666b88be09799c7de9a5d9a9d4c1bc1b6fbc44c664adb15a2eb27229be910226514c2ce22818fd38b850c89291a7fb", - "0x85abf4084c735b20333b1c2145571b793f96188850bae161160b47dea7c48b0f588adcbe9cf80e05d17851cfe3400f1d", - "0xaedaee73c52d71d7ac3854fa41199615ecf49cb0c35d8203f95175d1ddf565499a8e9cb8d31d89e7cd9cb75a9fb56f9d", - "0x9413589f0746d3b81e2f88b280e354fbd63ac164369dec353e6259a2c4acc6bbcc10f2a851901f39f90da7e523d77848", - "0x826121abbcefe3ad431c713a1a2cef336a0f06f69980a14d0a8adae5640e9aeebf4eb82be4621165ba32ce5e16de4880", - "0xadbff68221279985891e9f3fdb7b1dc71db3e20213b7c8e1931e6f75c6f02e7a1f6f05ec0687885de55ac85440f372ae", - "0x99ce8b064f874cf028e85281bbfa43145893f80a8b12813d047bedbf88699266652de6ae9e4ef9ce575e67065854fdb4", - "0xa809a71a663b0a9719c0327d33215b63c6ebb12da3477da8534d7e8f79fb81e06adfdad79686e40efb2c75abde559a34", - "0xb26c4cd057118f9b12c9b86e77d370b3fdbf2654a5d80a7763ae98c68cc2769a7cb293ea89b3a08250c2f699b8d76e22", - "0x867c56da9a2ed672f47924cce82c9d7e801d6a1fd18cdfdbbe07c82091c70ba0ebc6008b0b9d505632a97aa23c45b8c2", - "0x8cf14633888f2ba0b02fc8ca7536f39fa290678c7e0840c58c53a9d2fe10628be343a86acd74b2fc01b0c03af0996f59", - "0x86696802e4f27928dd6b0287d0188f8067283496d154060383c5ee295a468df32a2e8e24648d93ba868120ac429b68cc", - "0xb15439762d0f7b6c98e6946b3c0a7ea0521845fc68b47fe9c673194d81a6cb375c79b0122e81a027f21a7fa4cd6bbf56", - "0xb1bc19c9a3756098c02bfe36429c0f0d8166a5c9274edc7f80ce65ae7d6c67864a457f19cfde6924d204b81f2a195fe6", - "0x997f1cc78d707f29e3eea0952b5514b34c2cf0720f33a3244cc466df62b13031bea13df2296270eed42b3667c53d6c26", - "0x94f599c9995caffc9b47543b822dd8f84f921fe2a31e82d5d0fc79dd93a4da0b87a0906b82fe7c2a8c23c7829c21dc2d", - "0xa7fc8a6ed802660bcc07d3ca454c415da18d798719dc2688eeafeb8971910377ce909de68721fd97c4d9fe439f37a8d7", - "0xab16f93e6df2464018be01fe040fea08c67e0b032fe1950fa37c7593c8ecbca24dcf0fdb9e1209d5b0def622f3f6e92d", - "0xaeaf19b49843e3fac538075dccbb29a63d55d12f8c4150185b1ae62de778c983632542eb495808ba629cd4cbd629e07e", - "0x85614d537efaee823452d0427ea3a2f7d5a3c988b10cf7adef8715becaa519a9b5174b63e401946362176dc0d65667d4", - "0xaa08d8365e78efc1919cbbe562be7b28c57eb05c36e8da89378cfcad9f21e134eed923559530aa3f62bec758b00c70ff", - "0xb4c2760454170276885d66f03e9fc4e6a4254547b04fea3c233c11dfbf71ab05dd755b9697b442ec419aca000152f2a8", - "0xb814059b189c0ed46f9dab604fca25d881a12fdfaf834a75cc2c0e1d8454ce0ed9f2a79b34bc5e27004903a48c6ace90", - "0x847707b0aeb4fe91c12ea8570cf0d16caece8946951360433c8a375a69fa4c01136172ff2acab6d5164ff6d3b5340858", - "0xa7a9304ecc5ff6fdaaba6e774556bcd7c5dfe8ee7580a677301dece55c6a8564e7c64b60fc4efe89ff73954f3c3f5b0f", - "0xa1a86fc5648edd58cc7eb61cc30c62edb5314caca5551ffedf088fc9c1b92ec5b487f670c5bcd2127067e8fd5faff03c", - "0x9086a31715283fd525034d59d4ba3465d6c30059b967b1eeb7d537f3bf8caf6879481ada2849167e997212f3300f8ff3", - "0x99c11903cebf722e1cfd63a46b0ae93312439ff2f014b6653fc61927ba430c432b4955b30b7f078c340f5aad4ae24313", - "0x934b7a8b7bcf0108ed31d35a645d73f661c064a6fc6a5d1ad417ccf1b8864623b0cfb54707f10baa86643afb5c5ec980", - "0x89d5a69ae8cc18ad77995ae92d30236d5a5ef00cc63274e318d18abcf9d936453d18a8e6392b52d2d66b51c18d904d6f", - "0xad2448cea1948f0a4915ab054273bdae33a08c494203d11f46888f852d0abefa310b50367c80cacfb602cbc249b31a71", - "0x807274fbe6f08c332a5d2e2ae12cfabccfb53511b8d83bdc875856cf15ab52c2d01cf706c9be428307ea62fbfd67f87a", - "0xb2f4fee9f32c0ea7fae306605b62d983b130e4d423e2de286bf9f4343b79e5c4545214250cd1348402d8278140c61c00", - "0x8a36f79ab3ee0063098a39382061ec3e1234e67087b9519d0b762aa9cad54a7e0bd5d24e2b0a57a690993e3182f3e83c", - "0x86668e6743a7b6d1ee62e70e6031fc8639ecffed38afdb1afb41d64ec402a308fe0438a22387d9b0c130ed301c39acb4", - "0xb816309d1730cb39b1ab00c5333c6962fd5f5d8b22f3c3ba987b1e0a0065334d206141dcf0e68eba717a4eea533aa6f0", - "0x8754e190b8f751aaf9f8e7076d21bd31db8d9ebbee6b26517b190f624b3a892050312cee9d73cf3d7245446c6a376437", - "0x87826589ac28f442c608faeaf3d63ff057af7724f9d412d1f2cce8c58fad0adde325aa496c6e4e8441775c02d8a74c2c", - "0xaf30e5e32fcb17226edc54030f1eff8af619c207cd9e42a2ded7f15cd29fe52f140901f0925ebe4e997b56f34d3f406a", - "0xa62a4e5b6591d336744481a0797eb23ccd0f580d04cfacbb3e415ae3f273761042b8901b0312f93a6eafc42a50f81cc6", - "0x968a9ccc95e8c124f4475c348a33ad2a52a42e191a93bab3d7f0d211df999aa081efa935391a8289cdc4a5a8f7433822", - "0x93350cd99ab7d3e51756eb01c89172cb406c1debd3f0001d2fa8a01018be5609d73df671e1ff43e612ddbfe7076d9ecb", - "0x8df26dbc565ea7e758ce4c2656b65c1f0396761c9360d7092d12c121d3bc1c293ed28d82f1057f4eb5375b15443e9258", - "0x80a0dc22fb4a12b06cf05ce39f76537eb3db9691ca466ca89b2585237c03d13fe3fcd311ce2b3dbd1b7382044b803782", - "0x818b79cab08e11dff3d55bb0f55333f6340c5b462609d43334c14fd878b0f310b77c542c74d3674a94c692de704e88a9", - "0xad1bda19b1bc3f6d757fe4d189ca82bdcd0a9c1ef509c43e3f49700f84be33bb9b8b8e70f7a09bc6bc00a78cad0cf9e0", - "0xa22ab44c676ba2b3889341fb137dfa14cfc5491ce4c3c1fbe2cb7103fdf720ff2b77806a40109dea9a68d8f072e1c167", - "0x8eba6af1659b6145676d3663b04ebe58c199a1c24837ac4969793f07ed97165d20bb0410421e561cb9283faafd9eb51c", - "0x81b216cf08a29dfc3e16b2865e712e15f494b914cb24526a96799a3078f200a3fd403767119732ca4de07203b479ce8c", - "0xa023ac601c8e0c22553068ce4a7b8361b0b37bef5705fa68a71c3cfa80510041cef3640bec2cdb4f317904521e99443e", - "0xaaaab84c8aea75303fec31694114b3ee10fc1a67357cdd675ac9d0e33c3279e3117d389e9ab017882d517131b14e6088", - "0x8bf9a44b3df3d7e0c776e7ea5eb76f16f1870960f32e7c5b63aee9b432a0adeebbd378c574ed60e15a3abadb409376f4", - "0xa93faee621d930f336f4fd952954ffcbdb261c9dcc4e60cb848362223374010c555a73c0563e7933d1596b0526bf75cb", - "0x88753d0e35e87f7572f2012a40bb757364af5cf6e5dc0dfd16d082e698d3fedfab3c671bd58edbf11cedca247e9fa55a", - "0xb7de5f03681634991d2aa8a0ffdafd223b1a0d1ff70fbd9c00d03f228c6772d93c388c02045461d51326483af97bca37", - "0x81f96d4fbef3cf00da423a1c48ab8acc222016c21f6be3df778342c1d1aa1a420faa8ce906bfcdf955be045efa4b447e", - "0x8dc75ec37122afaf0aafdbea333291ebb735792b4d5934fd16bf28b536fa759dd851e1de448c3efac3d2b0097e0b349c", - "0x9186f66655fc1c551d0233b761c6982a3b8539085ca9a2baebb826091e179026b90f7ba6a825f38c6a09b190a31bace1", - "0xa1cf319c9ed31ffdb2108b684bc21cb495e77c853e6c502e03f2ea08e88a0c2b4e31958004d9879242df420b628acd8f", - "0xb3d3e5a75c34640bb2fbc7b62f8aced8dcb4b9b165992717fdffdf765bfc81fb4e67f3e737e6f70f24d3c24812ec0ed2", - "0x86ee6ce0480f73cc89ce7959b4af52351317cb6406cc368e889472ee5567e8a98560dc1f13b87442c9a8c5d6b31fc446", - "0x9478256948d960e3148acec3487da232fc2ae6818ac2c6eba491adf130c55badfe83f9a519379fc5ed0b63366de86a02", - "0x898a8130718ac6f98ef673fa8b725af6012ef28be3f2320359a5c2c40e479969e5926f1864624ebec10f27594b24f618", - "0x906f45d4ec3f647d0c49deb95884629a04fa65cf91a075bcde67940634cdc98f76fea8717fc1e714ecebb337e9fd6998", - "0x874c5a55bca05fe52a5d1743b8254b642431b720eaa74f73b0faacff2225f448ef94e12585b2d3bcf12c140ee3e81510", - "0x96f76cf34b14263a30df2135131dea00074f2ee853677b94fc32e04cd9872424dd93b32c55026b89c18bdb4e58bfd19d", - "0xb62e2ebd543f3e9a11b72f45275cadf77b1033713625c7374c4d2284d63acaeb64977fd2fdc90145066146c311a68737", - "0xb1759d3b667af9f15da8d4e77440fba4193d0db159a0bf73df32215b2d292bfed7cbaf41c07c7a94ae1f04bab23cefb6", - "0x88423607f005af97b5f8131bdb1fd6d7cdfc4c2da4a4a14bb818b3ecf50c2ae6d3b8cf55e23632354537f5c0dcb0f48a", - "0x8ba63acf22ffc1576935467af19f555a0c27a4b56e5bf752163038f0010fbdbff8a2131124f4cf36a326dfc188740e77", - "0x8b1996a0cdac9c6d896111671ac4dfa84a3a3738c43db6d6788f1a7b8ccd6df16a31606db00cf0107eedab28af05cd7c", - "0x912a604a97457a6b46d48731fb44dbaca26e7cc70a4628dcf553b43a9efddc4e5fb040a1b89e31902888a7cbbf709333", - "0x86eaf5b2fa873bb56b94eb7fc823527ae50364c1bce87e36fc13de149f1fc937af858a25cc477277dc6eddbf9efd5480", - "0xa0169e6e915e7216b83b00b31eeda207a02c9db6825b5ea44134368eae5bd009b7c95005c621e0d258c33c59085cb66c", - "0x8c8ac664946b5e69b4e34ffaa486b745ac8afc8ac702e4a4cc36c59f420a81b31ebf8b875b1f572dad8e4ef1f547a1af", - "0xaa6fd75ca832fe60eda078fc81a1a529364cfa8a4b4fac071d89e33cdbafa7d88ff3df611720b48e6fcdca2e3eeea0da", - "0x8d30857ada34991ce6faa82b4326bc353691ca32aa25511cf3d52cebefb262d6db8d93521020a2d11b3ea085287ad54d", - "0xb78bd8ea8bd6a2fd5741228502b9777177039ac8f033071c82ae11fed7f0a51d8bc64fa9aee44df25eb4b3822d571144", - "0x90904aeb1a99c4818ef21498a583848f4d1ee9253d70c10b03ed7d669b587f8712fd26d4409f00fafc3e26b5d72b4c5e", - "0x87cc8ebf78ff2ad752843792e11aeddbfdc628e03e13e0db598e08b496313f463f481f3a17ec889a3acfd128fb89aa81", - "0xb4fd122c4830f339fc019da6372286d3a0565ac04d4f5ac4f28b2c066ed507316e1b7beb7b552f60060825977a2db9c5", - "0x86e709d48d03738ca97d6140f13effa03137570c43ef00469eb0310909f66061d9fb933fbcf30bf04f13839e36d45a4d", - "0xb4a595cdd219aff5b8d0f80b679e58d9a7ab9cc389b47784484704e7d2c5249981b2b86be4c37ccb11b9afbcc8070214", - "0x97c6bf26c8b28b982b7a56ff867b2f5785b37260b90e0ae680920f368478a3c88f4a47bc394c07bbe88fa1aa1776f255", - "0xaa48418728684c9a10992d1851b69e54529dbc3548fe46721758ac6b33f82254d56738b351d146268fcc56a9b7f05df5", - "0x962a282caf6f08a63aaaf7ed2146dd61d527144f3fdacf1beef36b34356df50302330598b8602f1447f6beb4439a1048", - "0xb55d325499ce03c9b1c35e6aea30622841aff2a2c225276d677338579ce83177c0d64d78e7d11eac657a30648ef702c3", - "0x8a91b9296e5633b3b9144f61e5436654cffaf04623a864ccbcdd21c8f981618a908e890f61c74df19ce5b6995bc358c2", - "0xa7b6b32333377df24c0b0194393a1487a72a8783e06b1cd00ce6bc39337b34ff58ace57c8dee5b7f0ea2c9a54048a61f", - "0x97db4494e4208c9f297b484cb8159e8f600c61a44e1d878b07d29f0406fd32a0c12ebccd42ee7ac4c0bf33ff54a582e8", - "0x8697bc039265f7b6e73c133823dcac9041d18634c68fe16412b4af41286a4164dc86f7e71ab7a493223a84e185cb6f1b", - "0xb18a66cf37f93ca0189201811e7de02ee029445132f0fd4209e5efbcef46ba6a28aaaee42b30cc7e97a25b08f4bbb43d", - "0x8b69f189f3cfc34cc3968a07e13d1cab0f5c7e093027a9fac38504acdf12e2defced4261a686a2fc850336187e017957", - "0x96afba402124d9ff7048200acf329ccb4e35dabcd609e62d04d25140729e110a674849037e4b8aedfc99c889b132cfab", - "0xb75a809fa3b1c17139962bc22ddfce47d38d017d585a4e76ae1eb8f02849551ff7bdae178cb4546067bbab45b7041ddd", - "0x89196f1fe0869f2fd18f5c01118853503d71c4073aed8bd9cfaf694ca4a9e87974a9ad6e37449bafd391a2045ef5cd2b", - "0xae52921b5d8eb5df7d4923aed1afb125cb98aa6606f8cbc2129cfee56ba3cdb7225a30d98ca9271cca67fe39c763d508", - "0x99f1cfd27833fb64905f8678a532aa984329b2369ade3860025ad334131a9550214297bb2f7d3569eed7a9cc558a5922", - "0xa77fabcb76e8c6ac2a5196666e0c75c7f6c73fd8a0a5fca32a454a9457870689c83f5821f90f28dfd91abc3bc62ee761", - "0x92a4b97b7c14ec14c74e06363b0ab2e263d0d7d84125e2cfbf659bbee996a4d8561992e19789e507f4c24e5afbb91b2d", - "0xa2387e7857600a93de57faa0484650289c7553b9ae5fb001d011f43e5bf31c010c9c8b5bb82e7000465b546236e79066", - "0x8641b6f2dbe9f0b83e0a7ad8098b0836af158fa2ee6ff1bcdf3e2ac8b3d25d2e5a24d515e9d549feab4e82b49e468fa3", - "0x937306770a47ab2d5d2eec4bd6d9b3a8ffbb8c8067504571609a7e7a85c665b34ad2662701b67858e01530907172768f", - "0xb6b1b89f261e56b0cee15e2f5284c76789db26a6ca4762500745e260bda40b00b65add4826be6131775202c8c6c4247d", - "0xb1caac20a1b2aeaf287d38d42987e2c381e74495d9e880eda3ff59821d5974d01c7e3c611f4773a13ff41bef0f2ad44c", - "0x81ef049b849d7b0a732579299a86f1cfeb85f27ecee4280066dedf6024159fd47f311f1ebc46b58f63f71735a05480c9", - "0xb3b6b657e64fc154eb33b6056b8279ef736839b56f2c8f8ca438cdaceeb5398b8d3625676cd393c196f664d7baa3a615", - "0xa450678001e8db1ebd8fbd5c808c99945bb3549e834a346cdff316ef8d3b49b818cf9642e5b8097181cf40583ce901b0", - "0xaf3edcbfae3c8f368958cd11c95df4682ed10f894f770783e967fac1eed533ac427c1d4eee51f968ffdef080593ca262", - "0x8348eee6ec1102884929736d6768477029961c3d6d09e9ebf84d2fbe55c0501165f274fc1c0549ab831388d431e051ef", - "0x8d799492659dc44aa38262f8a4ae37b6ba6eb10dd20481f652a1c77ee9a4529efe042ea873c13bb2ba3ec4792b167c14", - "0xb4d3962f574c3298ffb0958ac999367db8207dacf2ca9d563cc1efb42fc889e19b7f00db15ffa91d145ff05eed97c3bf", - "0xa3a7c0e45dc8ae816d8765bbf097502b56651c0c11a03f476e362b64ddaee223128defbcec5629f4d7f1f9c3e4cb9f2f", - "0x951036c2878582d84d90dff79ecaca673df4760fbf9e09e63d35facf3e3257be6e1bd504f3c3daf8ac1e91d306e80d6a", - "0x8ae85094b13d349e60c8f303550cf4b01e96e24fa3a9f12d44c9822c004f1b3e9cbd772a2b4699e54023176074778993", - "0xa7292b61d2667d74cf62a47aeb559499f19dfab2a9f41f16e7b8d6e77909457eb2aeefadd9d3d3f6db18a438ae53ea0d", - "0x804310f5d2ce8bcf9095945f931eecff79f999ffdd24abb9e91d92f6e405decccffe4a8d9e731c4553de79baf7a5dd98", - "0xa77d3af0fb79b6f5b6cb640d04f4e13a28f8aaad1f60e732b88f86de547b33117386636d1afc7bfb7bd1d4e527812365", - "0xa431f239ffc68f6b1ea13bbd45675f0323cacb279e11a14f664acbb15d1673b99cf3603b335a100a0e297c305d743383", - "0xa64f4c28cc36b86dca65359cfdb50ed3dcc06fdb22ad567c7e0f833c880e76a53c330720fc2b96235cb0638394bae41e", - "0xb6fcd2c047de58003e9af3a416a2cdb143899441d82c691fa46d89045a12d3b087ee4603b401287a0f2629154bfc9bdc", - "0xa06e3b863bd183d8f91dea6d0211913663b3924f1e3476cfe0f328ff7c388aeb8e5c97757bcb56992c104ce0ab6ff27c", - "0xaea78204081cf5d24162686a824ff8e72fc0f88388525d646af7739265f60695b7d80b53cd1ddfd046bfcf59aa25f5cb", - "0xa89f556d42541a655864adcc1d5d67459ab488143e1b4eb48c67af30a8e753541fbcb479558ac26e1fa498f74a59025e", - "0xafc385b6b08c355a05fdc75e9360f4ffb384fcd74e8c9db34bbae9e0c67e0d1fa7efbff4160b387428ed58e129fcc027", - "0x9428d05e17e5525fae515e1ba3f04742fad1a43baa2ee166d2f9431dabb46895b7345ad833d495c99939f0c57cbaf1c3", - "0xb7a62d36ae55e681d48c911e1a433b568871c65a97916f939bfd638a054d6f1136a78c96640779ce1e3afcf90e3bb23f", - "0xa45b6d24930d91fc610e57ee78c6dc7557cb2ad976cb92e2157769447cd7c9a6a040f1008be9eb5dda2a7b8c9e524774", - "0x8b24eddad804790df3ed82db7c0ba05082c61a81763c44c98ad436dcc7e1e89a2800ff9c2deaf350f6222cf4278fdf9b", - "0x895409dc0aba4d29ff322d2414b33c1458126c023a3d53b25b9038bb90372b7c20d3e9f6b791fcf8f76449fa0aafa758", - "0xb22767ed218b575f397ad8306ec48fe07e8dc3a9f2f090fbaee411b6ba673a1258785d61adcba007d748cb019c458fd3", - "0xad4b9e4164010c4ba05a23f9a46957c8625fd4281a4e76f76ef7b4d6040d2228dbd2e6faf22b4a966ab42f32467a4655", - "0x92340f1051f88c25a915d0504c1413146f37f709ab060e3859b14aff9be7f8c91352dcc3fc866910a84192d301029cc1", - "0xb4e19bae926db3e1e295ba856984b32b796d86cbc81e81c6978e989f5331f27ce9004f90536a741ca996d19f998541c8", - "0x91502e2a69aeac8e709553501311b4392dea3d5b6f14e7523bf780b8af246e1f2bdc4b29fc4ec3ceb725fafa31bf51e0", - "0xb20607db1bdd6136130ba9683d581f5f45d8623ec4a2d35946723e0d8768654bdd9aeed55ba38303d8d1e312bc4f2442", - "0x8fec23ac3b4cde8c18346dda1afb2b72d4af1a6c013dcea36cd8cbf7223626690ce933b920bd9137f673d0985b64d54f", - "0x996bba551ae3b76c5aafadfadfcf80fcb554ff26e6a9e14e60440b3864239129734115d11a89ba79c19e452525cb5a39", - "0xa632f25ec68f02f7758103caf613511a1fa2e529e0861f286b4e490e8fca6874af2c13e3aa6ca97c63f3c621c197ae24", - "0xb332292c6213c7216bb78612457de615da878619024626383914f9c28f835f1289818514038c30eb2bc3566d2da470b4", - "0xb5bd5ed7e990ed8abf7de268aa1ef7ccf5562cf9c92486c2472051c1b5506bc9e72594380e7bd00c91771ed4e9707851", - "0x8781393278ffd5c522ec450220698328e60294ae1e35f60b25baa290a125cc47fbf7435eaf9b22ea819d431de0656f38", - "0x80a308c1acc4363f9bc54e6831c5aebca2b2af47d699a17ae2fba24495984acd4a25c7c95b96aeae3027f0fef9549284", - "0x94a55b36389e05b848c6d0e6426a400d1596195c2cfb4a972b6bf8abde2cf86a932b769a90b62a65d0aaf388e66d516f", - "0x8d29a5db4ab3a1199946a79ebaee9de225284f0523637f90e4ac16fc609dd3dd5a71072c30e869fdf6f057b7806ec254", - "0x99caa565547b13953b91f0468b78551784d947b5a3fe1b7278e4a45b294f074a93281e9ee084647d1b24c83b39a0cc90", - "0xaeee1c88769e7bae12f163a056d19b0090c7fd866d451963bc855bda2736c41500bb97a8d72a1a077357419ca94bc3a5", - "0xa94bd8b793a57b4fd79a84daf1f7fed5820bfeb44cfec0248f6aef130fb3219e1bbce68a6a55d332b124e1cc55224c51", - "0x8528607774d780b31417bf85fa3e54a94e4ef6e8cc233ad2a1dc795c68c299abae209c46ba77c33ba74c6ae75ee004a1", - "0x930f2c302a87d6bd159bd6b4db43212e7c806e17f572277ab14dd9715a435bd67b3624a9e72d9a2777f9b2080ef5cc36", - "0xb50d97fd2fbe60105dd1dd44cd12d8ad62b8a3127329f969be917fbf10132f1c6c6fda8029deb990fa1ed26e8c220c39", - "0xb685aea07aa1a45941f5eb2a593c0d97ecb5a803fd2977783488fb00fe6580c41ab83ab6cdd678704311c5542129c510", - "0x8cec65b68f4b3b10d032d39ec4c448e6d76e7615560bb754a53c4c6929c2470a884e7d39d9f3e58a2a9f121ad4175a34", - "0x96279388cc3e91dba49763ef50faa7550c3b4c277b2a0b0ae3541a2f990f9352748db75755a7b13efaffc9b8df40c74e", - "0xa7599c33614456b1b02b57921cb76b01109811a82f230f9e7e82675d57757f06021ac3f514d557ed9f2dec025364284c", - "0x869684197084f42dfd95350f8a54b0c7d940ceae2bbe49ec18fcfd178b6b0d21903447509e0ef356aa3d2aee83701bb3", - "0x85e9ab73165878b93e0229e3384f048e9651ae29980f9c5e26492c45e180e09a3af9058fada434d1c398b43d99d13056", - "0xa453a46ae96e6330c1b315d1b5f37d160731309d49d13d6c38c5d7f0b4f23ff1d18c985c471564afb54e4477c5d28d19", - "0xa5999c704320d4468f94d647d83c9e8720c19782d2a03677143c7216dc434b3160d193389b0115dc638f6e2e12f2d441", - "0xabc7a466cd848304616b2eca049c4b7509c5260c9236dc1432044ebe3e912afcc3a6ffe3e27d5d79d3ad4636ecda09a4", - "0x89ca07faeef1118c6b840a2c328fd32a5709b31850057302a7e607891e11f3f9f62e4fafd420564ff10a35b9a44c0f06", - "0xb0002f9d2a8aa850b9f22dd8d3b7881e8656cfc53e6c2ae6a913d88f6934e0062f30da2702dcebfbfafe36785203cefd", - "0xb8527c70bc791c87f5fbc67e2856e45b7254c5a0b673d4a5d3e9b79fe0715b608a2f35d88a61eb1d8d7cb615fea650bc", - "0xb9be558dbe778ba11fac7080789522fc004510f7b740c42023d850946933362a173267106aea046f338533e4cb29aea6", - "0xb021f9e635e64d3c9b4ecc8075fb74cf0e5727ecbacad15f822c8608f0d981ad2c300fe6e47c6148a6b1a13cf920d85d", - "0xae59f2a83a1384ef0b5613e8843cc9a934f7126430df7cd7f5a8508e3d83aba83bf3d18be7380570b24ba0e00e05e0e8", - "0xb403e4d0495a0137a710c43393798593bf131cb8d49beb0f3b3d344554dfc3355ebee14e884f543bb94bf9aae40aac59", - "0xa73b722287df7558c503f89d113fe0c017765c73181eeaa9ebe6de5c8a15ffe76fdb85ab93051a6f565653046624216a", - "0xa7d1a28fe1d36b17e37cf5eac7e27549ce9f6eddcb36203b58797d3372371f3b195cd3432db54aae4bf99768969f5b60", - "0xa3447ece13c415c457b899d4a8b8ff388ba25bc920b5711f8687cc86e9c1b3f3af42c490ec6352fa8609b044e642e3f3", - "0xb12f2ac1e033b6a627e7f7822317f629c896c8f8dd94ad91512855882dbb10b8e80a1e29c3e39138402f1f7e0de673bc", - "0xa7c65988996741bf59888415fc2264495050cb13500b6597d9d0e034898121b605784f681962cfdc80b0af291c316e7e", - "0x8c40cfc07dd7a4bcf514f2e87a1830c911e8168b0b8531a2838d2a14e790922b76c4642ae237b7547d8a3625decc7f0a", - "0xb480d70b57434467a40d6dd066f51b9e637abd2f49dcfa6450460aeec2bc895347e21aa82baa1bec7589b6a5a694fa73", - "0xa919a033c24e96af1eb0cb1ede3684e9a3bc338c7ef37b67cc9e9982586f74072cc540981e2d1a2524e99144bb21a64c", - "0x921e0b350907e9993a596b80f827b2d40aad60e9c62f4b65a67d3fa4c0acfa924c93352dad6eb3e868264bb24904e3a9", - "0x8d5419cea0bfebaa9c1509cd748c8af3869aedc3ae27fdbca3a0f08b3751a3b870e8dd3640f4abd4b46a2a1e745758bc", - "0x8b25e6eb600de81fdd03584fb9db9a7bf4c154ef1482553d7bef880bdc5baa7b64abac6db96fcfc4408329adf8fa351b", - "0x88cdb72bee7a6768b7c24d124dd5e8b29f0c866a0624e5a7c4759962ce1d71de7faa97f7baa56d5f51e35bca43862bee", - "0xaf1d59add7df3b3ba234b0b4f758349225b9cee65691c102294eb7e6fb683d7588fca33ed97eda361060253acfdc36af", - "0xb19370b8fe123f1dd2ea6d5bc75e151b0d1514224f5824437166fce77ac41ac5ecc1e7c1e75b75e948acf04c420efea3", - "0xa1ebfe84f1c012524cb475e68ae6c7cec79fb3372f1380321a0e306d15828613589567efe8bb5784360aed568e26db49", - "0xa0f964e3cb594c359e2308defd3eaec476a638b6e1c216157009e11f7c7d0c33fb9e62c4243057cbca49ba315d4b508f", - "0x9391e5087374e45f03d36f6919463c473938a653adf3880571850374ef0a0e521b25ef84b6012a19a02ec88f0ca3891c", - "0xaeb86d4426d2836e6e10c3277583a37b6684ba35f4f30d2d073043f0a0148f763b99fc42c3935026b56c32e5cd0cecfe", - "0xaa98c07dcfb1b0a708486d83763511c7004896856e851bd83d25a9551efc28f059c3fb8752ece0296964e8c13ec829b0", - "0xa466fd8dc1aea7022a86e12a119b16de35412a1b461680f6a1cec408e9b9c1418a8e406fd4a5656c73488adddf17dfba", - "0x8c9b0e18a033c27731fb3d22b7c83ba7a86fdc2234e8f2a19d7659aa67bad7a85ef25264e8eb81af529feb3fa9340ef3", - "0xa371feccc2f1a1b96ad8a9a7d8db0c06fefb1f2800933134299027459b0eb8cd101b9a37c76c22dcbded01a74b13d465", - "0xaeb34fc2758d8b68d17f15ab3c299344ed630f7351c498a5fe7986f7e14d62e74ac9a8f5d2de7c6289771210539383d2", - "0xaff9e961d0acc71a077e3af52ced373bc694f9154302abc908710e500e908f33bdd10b3c41bb8fa8066758a18d64c667", - "0x98bd5a8751e598896e9aec90649294934f81c36d2d0fb60070e9b96eb47d0988f71d9b68f4c475477eb4c996a9265c13", - "0xb25a92c6260f389f6443a572960e0a52ab9c9250d8760ed148082584b2347ec7d103358c033266bec02374e69d0102fd", - "0xb876968bedba7f4712f5e5eea605c1e5fc40bc5773c61f08c32e0c0f3ec575eed3e13e48809983153beccdbca2123edb", - "0x8c4091ef8946c9b27490099d5c0b47c404b5a1113500592515deab1c3f2778bbe933b09c9824a3a7ccad2141f9b5dcc4", - "0xab85f95d318ce235929531e2e397d09b9906c58958fdff1209a514624a099d3b8c103a51b2fcfa0b17a8f008744b5d71", - "0x9016714cbe49fac5e7b3e493574078c462e18f6363f413270c23da6327731f71e2dba5dbf1da6bbe0e29f57f0c33f869", - "0x8c90df700c0e2d104ce7b76be7899209136498999f78195cd888aec6f069778d657e5032ad7db56381470dd1f519dcf9", - "0x83dea8472e8418aa069a0837a5c44835aa1e00979a217f6295aa35548f509fbafc7db5b31b8767621e4f89957892e8f4", - "0x80a1d673220144973ab70d977b94cd3d6b8fff7f82f23bd4b30ea393952951d2f07c24e6d411b2ec19f3bec13583d9fe", - "0x804864b58f9747bb3ae54c588dff46eb6e16b6d98e0f711828e97d9f019297b743aa2202f823e3153ef5bc4b95da3501", - "0xb08eaae2eca2c64001e1da7d0e345f96dbd3e09888f9ab86f178718ea5a04321a8b8633e72dea68cc05687042808e3b3", - "0xb962f91819dc570c2cf131b89882fb2a44a999b94fd1ea8b83f400e9b66075a35c89f0fe0e8dbc3a597cdd1aa3135888", - "0xa5f33e8f04a2d7aab44e832f8ab4640519aa4ef88b58e0a398e45347492b040043e494de4b355f07cb4bc728b67f1ac9", - "0x8ed80bfb4cd15bb87175cff427c6a1bfc3e6292bc5c2d04dd42b497bc068baac5602d41366448ee7f37d85a5d8437750", - "0x83441e746afadf64583571a9918ba5122ca987e76a6e37f98514b1a8a178380366d10ded5c70d4feb08be6fa6d4bc25a", - "0x8807fb8adb2aaa6833960f435ace162c01a9cd0692a4cf038c89ef7405600868efe7bdb3e8a3db48901367ebafb0a1c0", - "0x82c64b1f77fb78dec00cab089cb7a88ae16c72c94d0870bc92df11587feb62277eb941d2f7d3d2fb033d7bfee12013bb", - "0xab2f1e3f1fcde3b8b2c07135acf3a492ae7675d9bc971ba57e06c99fdfb39e1f68d1c826cd9bba872749cab375e44009", - "0xb4a25f1f5a2aeabc29870ab9a815721f3cc031ab1a55417b457ca6504e5e96e4fd0d2d364ae17738726c8f40cae9c36b", - "0x9519efa4774cb4de4ea834376d6213d946fe6882e2b36342f683762fe50d754765dc301569a836febb2c7c9dbcf44f64", - "0xa75de0d0320e8cee962d6ed4b07db718615e75543fb25f0d28ec5e76f56d72b18d648ae42d7bd3da18f54ec1e4497a08", - "0xa2a17aac11e732097b25c0b9f7b97d807dd78ecd33d88aea5ee0a46a42198d379a241e888ddba940b3307e9c560ec45e", - "0x936ebfc2234d46282ec4de88958553759d766f682d6f9669d2b77a2cb0cf9cea9b1ac02014ac3f5cd47dc5d8af2da314", - "0xb33def3135e7ad61a660ef1266d61216220c7e0bdd867b727ff3deea904072e33a195e4febe64ee1e263349fc9096cdc", - "0x94337e4f14752676a703fab8544ea0ab7acea0ef924b85b05ffb84e4476f1087acc9a6d6250893a32b82f02651a179e2", - "0x8f22942bbeca0118747a22d0aa13438e40bd6a383e310eafacbffa1490f5758504da4a11e6320e1c55b3daabc72c63f9", - "0x86e3ed934fc613d0b3269cf368e32e67f4add59e4dc1ecb1f016fbdc6c53101c2435f95fc36625aa8c69c596acd9b0bc", - "0x86f04807460e1d93f8eea2a284119d889659b5a6b124d41dfb2825b31685361e8163fc3a253a49cf878e316463c9ace8", - "0xb043b2a99b94661ef8b270842fe4d3c51891ec23ba749d9c999982553ecade6f658242b373982c9a3669a886889e4f33", - "0x8b6a33a68ba7b5932ce11b3f0e23c3da580510fa37668f2154c59c3bf788dd2276a2a8c66a6bba1a68084e8b9bbf378e", - "0xb54581c88d4880fa4a0ec6d3c17b6f0ba339e8f7100242efd3b820ac942d75d1f898259d6f1e64a3870fc301d9dea2b5", - "0x9449dc9bce23c7e3b41eb34789dc7765c2f7855f9670c1d145bbd1b2d1b47a9318862ef3738511b4f89cb16669c0af18", - "0x926245ae9d4eb213ebcb88ab2d7e2a7d198557721051fef4cc966cd11be3490a3f83d4ff48f5fb60cbad9c5de4b98d1c", - "0x8518dab07ab15887c68d0de9fe3c0c09ea6bfddb99c145b3f6ff84659e7799da93e97bdd17884b228772398caa8c2ed3", - "0x9969575cbd7953b6308391e9ce2cf4da466b3e730c9cec0e88522258639be35fd31abdedd94b445d7075919482513103", - "0x8b1f28002c19b17d6ac1a6f50afc3448f390b3209b1a76a9a024ceaa274de4588ce82a891a03e878ea08747ae5d98211", - "0xa611963d1bc45b60ffe6756a743ab379e4022bb3fb263f5f305a615c92432199c7e1060a79aa42f7662fa89a0812a4d3", - "0xa3c7706ab74e976464fc341e5a9f7284264c1610fbff02fc36b88e15d6859fbf40fd8c5e93c8237b97acaa0900a03764", - "0xaa623fb8892dbbf4fc02004a44e07c21a422e5553e4b02fcca24dc1f416a54eed36f2f7376dc1e66218e850772676e99", - "0x8133cccf10b1686bf53143bd3520515ec72e7295f6945c43bcef7304de597b767265a3a9f7b281fa353acbc3cf6997f1", - "0x852e4aaf4da9dafc988d0da13a7f31fe8403f6bdab88dec363eb8cb8d3e64c48ff34102f6660642749d11d69b613f8de", - "0xa616028c6cd54a6514fd9f7aa9ff13000eaaf39f582441f73a3ed8208a513b580eb7874b5cd0b1e9a542c40c5887bdef", - "0xa48ec58bc3bd4b512c21d3d55618e9c51836efa97cad42bf79e748542804114714db23d79ad03e410e0989055c9bd46b", - "0xab480f3750420119ccfcf8d32c4a18ca580ce88bffe81433c1d6999c221c8aac482de5c0e41a5531806bd17897698d6c", - "0x8522bf3b7157cd29e948afc8f479d6192364a11f85dd5c58d4ea0443aa6b655f55a80e6a3152fc02a8eea4c0815fcf19", - "0x86c91a6021e738103031c1ece906ff43227eb23088e5ce1b6a1cd58664d4a80d7bbcb0d56c3b0e02cba1e1c2ca22e058", - "0x8ee51a59ce6becf098256e19c9aae5ef0c2c9e66c587d9a32cb4ba1ee0b64c13e2e008908e35f43314316508956654ce", - "0xb94766a0fb91c8de2338a68c4ab08ce5bcf62f6efa221067807dc647b595fe5a342d7122111540a1ca6ea7743b6ee772", - "0x83f917b8f6aaeb9eb2eb742546e3f2dfc9cfe00cfec60051010113d55dba2421974098c157dc2601902d8f40bc84693b", - "0x996e489890dad3c4dc35faf53d870bf1cd76f1dc24e0cc8a1f899bdb44e89dbfc77fb11f7b33c270a1394c909f7a27f5", - "0xa89936283190b2d1ce8d166b36694afddb4c3df01bfb1fa7bae69c55d1acb4e68e5e29867ea33eee8031029b3c6409b1", - "0xb08e5a5d6797ca252d12428b2086e528a6e5c3965d2e5ff2bf83bc71ae9c0346a4ceb3bb2f2e3f8a1685fc343f36997e", - "0xa05bd12a7a6d52d234a1b3e9ddea7b18d6d41026a0d18251b1761f1cc863064dacf821707cfeef2dd1c02536f584ed94", - "0x87c638feef9c88a9f89d10b56fe4bef6406c1d734cd1f01006e2f2b331196a49c7184c10786e855b3de8978927df42bb", - "0xaa194f3e4d0fc1d3107f9564b13e6274bbbfc7b8c1e73ce6677cc66d9319dc34b5a0e790d6d44c614c11feb50530a252", - "0xb2ab7be7ee9d72d1015e94d006020e758b73f200dde81e89e52cd33f25aced0cd84b8c300413d32565c253edbcd2fb1f", - "0x8ec08b22265aaaf27a84a6cca5f0875a3ebc70fb36c4f5e59d60c55bdf2a4fe11ab7ba4b387f5d668e67682a0978fa46", - "0x93643b9541db11b48e0c84caccc8da9ff7696717aa176ce6d863446ef8d887f3159b0ab6fe1f79fac883a371f6736e93", - "0x8325654fd8388ac96935149165fa3238d0848151a04be57f2386c3304056013efb49febee0a871cfc2ee3c11bb029042", - "0xa2c15cbe5d5167f55f2a454390b61d99601614037fd67fd198968531ca2f84f3c214b971ef300a20a114fabc6c67db0f", - "0xb40ed63b0367174b5b4b08396afe2385b0f75ec2569fa3cf60f87e1b17fdee888dd66057be2cfb185e9f32df59b7a8eb", - "0xa466d2c8052a115f121177979620385bb07148e202631979f4ffb01e7e0f6fbce28747df9bf70b2168653096aa704fbc", - "0x99395136290cd020cfba0ca896642c245182e2020ca2299be8ebb2f62e2fc62fe0be593838f62681f6632fbdffd640c9", - "0x8e4f081d9a724bb54fafb66297a32f84687493464550c09259cc6f8abf770d076a514ae1d6726cb29349e27ef69a74b8", - "0xa8d5c941e7c03dba0232c763590e93e3d99fa519b0a65996d20dd20deed1d0192738f3b339edac68ad42016223733582", - "0x877baee9ee979be8ce3bef02422e57799dcadc34fefd8bf2baaf945f267883f67211ac5c06246f7b49f1ea5c99550a63", - "0xb6fcc2a73dbbba54760d244bc13e1564a3c61097e9b525b247cc8687ca08625a7330fc6b15e45a3ee508b4d34853d852", - "0xadf720dde6e9b5c63e361d69a2ab46ed73e0deb82f8e30f27ca2b19c2d8fc43e18ac04b4fa029f553f8d7dd79457ecda", - "0x8956c9038f3338f541bae9ef1f5bfad039d532dbbbe7814e3a3d5442d393ea6114aa666559d8a7e3a026c758a17c79d6", - "0x8d6de7f95f30a5a4b3d441781c7f819a0265852ab78b8416227089b489787c8ae9dffbb0bf88acf1b4c4d6b8a29c1a53", - "0x81d4efd71c9d08e9f6d7f7d7a2fa5089e80cc3f8dcc685686aabf3b4c8bd531b4aa07e328c0fde32b638f23eb78de588", - "0xa30053b681ed8328b5d64587b0d38edef0e366a2762cf5068dae177e4f4084c4333f9a5fa5fede93db80f7a8fd5fbf57", - "0xb340ddfaab2dcded58930e5dc2b72cbedd0e79ef652f34356fcf72054a87fc2373bd3aaf8a88af8d4633f73dfa7d9a28", - "0xb9f3a7809be0bf834bd7affa2059d9371b848dd5e5fa93e83e90d9e078a2fd3aea64410a72457c32d33ff1ca11dc9300", - "0xa9a8ce26a38dcf277ed66d75e111b07348101e93d03f446ea72bd903198122f8a08569f7125f6d4ecaeda8c093a00ec4", - "0x81e78b705b44533e2e997f549f46723a5e6b88241d7a86ca20448ae3ab140e967347abaeb8700594a0cddf1e82285abe", - "0x84724094dae5b7ece30cc01b5f2acc8787de57dc0c37a437c3e8e26fc03069b6e8562302a0f1c95de85937f07fe63d3e", - "0x97a715861e5bb715a17a948d6b6a389b89744e8ccd3699fdea9ac3d890fad027b78d436f8012b0abeedd078a20ba91e1", - "0xb710b2e7d87771416aa34ba2d93a044bb118f279fff62c1224c150ebc30f21abff212019f0f38c334daa5a96598ab900", - "0x853034af5ad08c563ed096ab2d0590ea644d372cb400bfb03867092768d90b7432d35c5506378d001f986c59769d6d56", - "0xb340ab52f751e9d516348faddb45f0115ba0619ec9db820f870007e3a4d305ba2bd0b2a58a7576296531fb78886b16f8", - "0xb8ed8feff520009743ca3313899a118df025a61e6e03bd5fd27898a23beab472746ca3636c22ea3835e9526e17c06dc9", - "0x87af435e3e4ef611d6da74c8d98e8d3f3de64ac8748105dc20287a7dc866f57d10a2b854f7e0e09235eee647dae1ab86", - "0x84108b1f0f0ff73a179cb1be1b2ecb4268e7fd2fac3dfc7f6f99889c90a33b4310946909b9eef31b256b8d0e3ba56bf8", - "0xa6b9fe966293e60bd384a1e4d472b0a72544aba41b31172ac8bfc3e19beaf51da54a66625d73a9ae22c7c4d1b0840a30", - "0x92e82e92aa615e198ba3c83c039b0adcf4393b3fbf9721b2e47ab17a84bded2bc8bc2bfe257d2d76162a87e8bc7ce759", - "0xb9286dd48800606b7ff9c3fe2abf5c49ef0a6b981711b5ba1f62952d6fc4a9999bfdf061c4664a019120f15e341925d0", - "0xb5da5dbceaa7e82f30fa5fde88b03ea88e7003a50eeb53e3f3aeaa63aa586900525b42fe1b699451b5d915d1b83c3705", - "0xb06072869fb8526d3077cc61a3c55d54a7a1197bbbcc875aeaf617d7d1eff3dd3ac243e2c76caf57dcdfe306edcab4d7", - "0xb132db9ee3ed16e6d76db9e6e3dcdc2b142cd70b9582518bbdf5415b3bb476ad900d50004dc0ab6b87ba697c6314b4c9", - "0xadca92336f3546ea50b034525fdf548a36049ca82d9d3cec10073e7cca186227cd662d4d66673e7214a6ed58cf75da6f", - "0x81bbb3fa241f9514575fb3f6cba8e34301187681354c94e7976a4205c0bb238dab52b29a76a5f0e0d4cb1bc82f8857c7", - "0x91008dda2bb7dfffd6746e3544ef540d9a1ac7ee9c68ca9984a1d81041a18fa9f35b8c4bdb44ef3a860c37481d5e9a14", - "0x8224195cf18ca0d8f01521a0ea92c9c598c556746c825a4dda49ecbe324d570a96775eb81dde1d3a14aa3660d50e27a4", - "0x8b355eeadef5fc7cececee71aec3ed30349df8f43f25da1d75d62ab00fc73702b405fab6d422053c2b0fbc7469ace9a3", - "0xa4d657dbf2bb30c1e57e0b63960663bd86ce17204979a9ab82624943ea370119f040b58b067a05ff6d1867a22a58698a", - "0x9379a367c918b2be61a9a42a495ec03f0168a4ec36f753dd37eac6e9f58a26c8510ae7b579a89afdee1d192edefb4bb3", - "0x85b37bddc80754f0432573204a1a4b86a550bfe9689f6c710a61810aa94dedeb28763ece40f28fb3a6f3791ca4c86b8b", - "0xb41c3269b96e190e40cc16e6c7cc8054cd0b7902a43c69b79d8ce471a417d3096b2271badfcdc59deb6271ad3e5a35b4", - "0x941185020a227b7a995f59805c8900f7f6ecff1e7b948a8b714f85a54449a0d41e28db5e17874e018eab72ade20eede0", - "0x8a0795ce082f74e4633acb1649b52b46ea2b4360860fef6ec107910e245b30466bfee8ce59a6854f866f55ec5cc7bbd1", - "0x931fa63550530af5a7ee24964b8b4d0c66c2bd59108131f375c7de86bce59cf52890191ec8540666c895e832dc312360", - "0x8fb86918190a3455014a5cbd15c7b490d68c10cb7b505e9233b3eacdf52e63299d49ded75fd74f8c2bcb3632a9c29d14", - "0x92c896826c9d871a83c4609f9988cec0db6fc980c8b88a7baeea2856ec2a0a56c3d5a846a87d03393dea966b534aa8c4", - "0xa9d4c780c94384f5a13cab61c734836f5729482cde62f2888648a44317b749135b511668834d49296ed47c0a3b9fa8b8", - "0xb7c26da09c3998367063fad19340f53217e8545535d376815773e201ef49e9e1b6bf1423b0b6bb363586f5f05307fc89", - "0x8c445b3655f1f554c2a7f6f7d035121939a8987837dcb1a1663586614dcf2cf47f73633950d8803e2781baaac52c12c8", - "0x8764f924f41d8c5c91fcd77de26ee3bbb86d5a5bfbcc45188be453c8dbe4b875fbc5ef5b01ea3a26b889d7b45417f173", - "0x8605a8186d5716dd5f955a7125619bc72ff385cdecb187a9a646a4bdf6595d67f00e777836261f3a69c19d2e2cae27d6", - "0xa97dca2185e4fcd7583b1e695333d55f54edd751da436b8982de8c344b5f57e35ddb61ad4a611dcde08e287c78c757c9", - "0xb11c576a049f93e0731652f1a1ade62b0124cb7b4e2b13f6505206c27ebf7998ebdb3d887bed01e43ce5c24714903aff", - "0xa46dc516b8ab4aabe35f38af1236052564b01d66c558e7107175064a5226713e8550912867eafe4da133f56950df57c8", - "0xa13e75bca5bd3b08030205cef4faa56a49e5d7da94bc41c708deb2f65343c1687aff26368915a490b89006185f18fda4", - "0x8ef5135a6f1f635a4966aa540cb877dc98c6a88fe462be3226c1a270c82cad8e091aa49ad39862f012edb3c93d15fb4c", - "0x99158ace79ceed67b6d8e884050c6fb7c7a1509e41f0d2b9069ce8dea392f17f88303d0942cf3c0af3ea52d3194123a3", - "0x8805c76ada9dc7e57545a5e1a874e6105592601213e22c1601b0b157b622e51f004a1da754a8fccc8f2a2241c14e21a6", - "0xac3dfe87e17ccda6196f621008716a14be4b983d187265eabb8f5eba7268cf770a70ffa19d1c7e77fab0373eca7a4045", - "0xad78a31ad6f2c84f6e5348f33631d876daa3d5978f6d1a77db80aa219e12c9ea656e9c18e6316f899bbf6c2469cdee37", - "0x8c8726f8f6fdc40516bb64b6c624a6eb4caa931e3a9ca8ce2c31c282ad59f0624ea290b804ba84e339e83422070df419", - "0x9303d1906cf416a184e15f13cf7dbdca5fb296b078079782c9044b9cbfdf06b0c965305a8d88678b53f0a10220e56f4f", - "0x99b9735a77cdc1c675988e613b3e8843e2b0469030a33f5c14383803a1b20e328d45d2fde6ff0d15f6bc2eb8da4f4d88", - "0x892a18f4ceae3fe7cde8f32b84c6bd3d9ca867143a30fab4f939281cec12587929faf07225725bf33ddf154b90972214", - "0xa100a35a2865bb465830ce2f68406d8a92bdeb21056bcba28c0ce8ce5ddfec6e293e926d764499e53facbbacd3f72994", - "0xb797ab22a57520a0584edff499cd1aa1663d8b3f411faa542022c5f1a645a3f952f9164f61d200e4500673a8d95a938c", - "0xb1a457d100def2e26b2b30617ee866264a3ea649bcd9edc7be132f5cad02f3209f5dccb02b95a462b5af9a71fb88a341", - "0x84c1f6d4f29869a359cf89118b1a80224cb574393fb557d1c61730a1fb1884895c4cb07f23c52165975b89fe9d6f5a77", - "0xb6d53e49025bcd1d7960ce46d4f64ff8f29e4239fde1b19e5167d506b086152da3bc3b86fec8ea531a20afe1c785fa59", - "0x9635b053c03d1be0bdf81e9876c63e8541b793ddeeb2a8f3ab0e44fb78f81a9e61f8c68ce393c7c959b62b67f9724409", - "0xa19ca9ac5a345c96a607f979a958d83eef4350ebc9cea0e0aa11469dc554fcc39d9b22f8a3c92de599ca08ff4152ec23", - "0x8e7d45d35f6fb95799846fab51b0ff2415857bb54b049694c1ebf93f45167b8497c3341b656f194edd5804195a7c96bd", - "0x87c05c7d5834394507ad3d363dd0ca5132a7763644e354c3b7a803fa594d951084d37942f59211660f10098cf49adcdd", - "0xb276246af578557aad38190878111e804db0f29846185d7033c913a31e7657d035114448ddfed2f3d75c04c79ee01e79", - "0x868bbcf14f96547192053823e4f85b50fb988da5c4cf73f5cbf23953252b665ef7aea4421c8baec90522f58f027a2b19", - "0xac2be3dcb8082b64a3745ce0d2b97cf341483713e1bcbb37369123d6723968d3bad1410467aac7fcd3b623bfb1d90d9b", - "0xb1e5cf361e0857373814e8db7fc275ccc1dbac8559e8487cc892bf82d4c6be00d1b2ffe40289692a70072c5f80dbacf6", - "0x98e16a5854635c72bce6e263bb57c071194df076e1ddd81e645884367b730d4d557ebb8c74b3c582e09046d2b9ad8078", - "0xa0016bfaa348d44a3ef814b348f7d56fa83b78baeed4a7b58617b6f4772dfa990e912ebf91c2321307884be85dbf81fa", - "0x85690a2c5cec392b6f98cd2d03e4204cc51868612543c7a3112066ebeefd4304c5c8b21da44534224398648b413634f8", - "0xa3a1d00d0fdd8c8cfee153347d590ed78cce48eeeb7ad42032a95baa73cc458d46882d0e9707f3dd519b1844f236bcdb", - "0xaaf2774fb26da59c115a28d86f0c6320368fc6d2c0bc2b7e4516cdfce3058cb423b0026b6c75030ddace9ccb7f058227", - "0xaf507cef7320bd003526fdf43c04af46beaaca5b6ddcad835ae14da60a2ce732b453d8164553e95f2b776df55ddb5efa", - "0xb2656c07a8ba2a2248d0313a7795b99f5acc120648c08e3a77fff5cb9b861827c94d4f2f99a2f2dec1d1667ca3ab26af", - "0xb426b97a51f0439f2da2d0d934693aaf52482bbb48893de12fbdbed1b2155e30791e7098baa18f93ecc45f8dea4f22aa", - "0xa71a7e08426518ef7307c2a1be7aaacd843794601c0d49f4f0e474098ea0faff74fb5ae2bee416aab849afe04be434cb", - "0xb6d510022dd3b9ca35e93ddd2ae77877967dd6966706f339b2197d2891bf523b5d55b7cdc80274368333f9249b62a7fb", - "0x95d2f6cec1b4038f56c571ee0f5aa14fe5fe7b9a2efab89eab4c51a696d2ada549a42095245bea14d7f7ffc69ade417b", - "0x89147eec9de685483d0a5e21b877cb550518a1bbcba0ee65e9519c294fb0c422a729bb0f5a8c8e3fe77070e1a89fcdb2", - "0xa66e7116eb277ba900c06fa48baf274e2a6865977698a504dcc1d0c20f90a7030bb2a841fdbfaa5c8ef6d81aac4fced7", - "0x815053a8483ce2a84a34f81909bc3eabefdce59140f0fda6da77ec005e5dcfdbc6f289d0f0513efbbeef0358daf94025", - "0xb480d2b6320ebf29f3781f04dd88e835ad81d2c63b716f6f244fd2b113ba3781001a34189df586cd629e70c2baa0e5cb", - "0xa74281bddc3a93503a695f0375121b3bdf98db4b2b053eb2cf0773647f6f69d1d98a61efcf82e2a823035ce803b82001", - "0xb84fb99a6943447cad21bfe2b34dd8da43d349e53e85b73fba8a5fd0fe3f41e7dc629960b3325d08af1544e5dc66de28", - "0xa8d11ccfb0dec31b39efeee74c58536f29abb02d06dfa11acb7134cac626a17ff4e204d1d138a472c63c629b6f8406c4", - "0xb5017d42a2388d90bcf4d0b6e015c63612a0864b2b379e9cebcf2e869e5fd45d2713bc549ea472d77e82fa8750f364b7", - "0x83c8e090de4ab6ed169a033aa4ab84f7f3e2b54186106790b30741e9d87d9a5d61bd6a285447e0d1a8e1865ee618a91d", - "0x8db64f3a1680cf461f9afaed4e714709d37559071bcee52e13feb5627c1fa7c093fc8923ede3e70db07563d2d1eae69f", - "0xb6d20dce2f50b78b094949e64edc2ce1b077a3258692ecc2cdaa01ec19add246d0832a319bb0d4153198e3a35091d86e", - "0xa61e585ed55dedfad57352d2abbf8ab336a999a5abbaefeb5d0da9fb0d5bb791119e52034844ffeecca9655675d17228", - "0x8ff58b27196f589ce0d3461e0c00d695da47a79809719b4bd4a229ea7bc9319469744f2254be4469092b1a27532439e8", - "0xb5edaf7c3f9dad7a54908da0e7a153d69a6bdb99fde07fc42928a0dd38031e32dec81c864147066412a8ca240e7dfd0d", - "0xade064bb3f87431a32b361074a89dd280cc1160a57fb3cf21eea5066e886c7bfc3655fe39099a1913b8b53242b23b2ff", - "0x9169621f97887db46384b43ca24b1447e23fcf5abf141e70fcd1834e9d691b9bfc6e8059d060bebdf9922608593bb972", - "0x8727bb06fadf0633fb8137a54d6912cedda0bbeb0f93af97deef3490b1b47e58fdb37a972dbab1534a5172ff0c840114", - "0x91991b98243bd7c138bcb60cf703a9d0828f6791eff5c2c1c5cc7e8edda258d3cf72680bff2c563c8e964f87450a3037", - "0xa1bddb74f5892597ac687451b932449305d6deba20e97e10989bae311d532a7b72a3fab08dd832589e6a22c0fcb548dc", - "0xafc52ed64208e4beb029d1428697fea6add02210f613551d1e5ba6011c5d13f66ce26b3dd2a39b30186c566b1af66c06", - "0x929bb88a9e30862be5f45c002c11537780d151f9836edeadcaa4a617b0bf958046ce331e15bee646f9eeb4d9ff854661", - "0xb3376241793ab9f1732997cdf515b9114f88bb2c25c0bd3f3b22e5b665e1ae94fa3f6a9f88de37b7792c3aafddc682a2", - "0x88fef7680a7fb665043264c9733dcbd23e20628909278711aad2e54f2eb8fa3d07011f593069b6ba7ed312d9ddc3a950", - "0xb031434d514d0878b7011ce2840e23e94a4386034dce422f37fde539aa35cedad1511f9eec39fc23c7396f43ec22cf92", - "0xa4a32f1e58c4ccb2cb4ac6c2dd8acafa292810c77126844f33287c8d522bb8c32dd89ce8f7c1dc9a273165b0879a45ba", - "0x82e5b11b9fad7c7d5e2a8abf03943aef271ffa43ed8127dfd85c7957b59d7cea56039116edd0b0b992262751c347f75f", - "0xa650327144db1806cefedd1daec1de3164b77c02a0aa652371ca0401b50ec3b7a392ef6a80de6d4724892d71cf48eb07", - "0xa88d8370d88379b52bcaaf596c32faba155db4857bbc7eccf89b5d67a97ae481e53e81de6c9461a6719d179f3ffbaf16", - "0xaae8b3d1b1bb0d71f19e37867885a1fd550f7805fd1306881515d77e5f6a990e0bb40c685e350ed09eb4a55298f3a393", - "0xac024fdd79688628ee188a7a9d39cd1306883c260dbda9e79eaf2d2f57051b5379834dccfc641302cd12a8a24fa2224b", - "0x90cda91b9e71b7bbc091b9e8e28d79f0fce42255e24a7a3bbf3348651405c46d3b6e2e33c5fb5e99fb4d0fbc326f77a7", - "0x91325730bf7d71855ce0462a2fd0840f3753964b296f7901e1ad761f48fd133371fcb805c315c4d8cb2ffe34e98ab9cb", - "0xb9e1a298ce9efdc004903b21e573c113c771b1bb5b25e2e88baac6dd7bded130016b2f09e50a4346c59adee607a71760", - "0xa703a60c430e365bdf9023c923a04fd9db427ca0da2da8dad6b0f0d5d352524943839d859c15dca64b590ace6cb1ca89", - "0x995a5ef468a38caf66d58880245064a7c0ab520ebf3c9e9f956110a9dd68658baae22ae274a48d59464c8d37d2f8b643", - "0x889c6e4516ece0e0fdb8c99aa482f367f2cef0ae2ce0987b6b602f6c856b02fab27114a6f4b82050738bc98a48ef5837", - "0xb432ce5f638aa48ba952b9c2e06ce822d531c9a24011d14650cac0722a4c5ad1bf22109a2f429cbdd22a567ce6f03094", - "0x86fe41234d309118d1256a9ac79b7bf01da1fdfcfd579b655f31b7c4cdab6f687d46855d56bb11bedd4b0be17e892b2d", - "0x905ec536f23dfdcc4f8128fc1c00daa877eb3caded7637dc911aff0e6279eab12f1748949e4bf015e4f8e30626d3177a", - "0xb6b9f47cb82244d7b1102b37cb52f5c9336e4c05e4c90f5e448fa92444bef12d2fbcfc39af9e1fd05811f5f864f12047", - "0xab56e7c534ee1f921351dfed3f3eaa127869849b44540b39b0dc021b3dc4dc94027e4161f7f3ed40bf42a1d08315264e", - "0xb9c62b4e679dbb3405733bbe0740450e72ccf39bf953142cce65fe014f132d5af5864ad96167027012c98dc8b8889e8f", - "0x82b8036a3fb6f648c6fb0492334fb3dc8f57c32779d4eef78ac2becb0b93f046dd68c2fea3b5039c21ce8e1efefcc685", - "0x8525738182748d6f901650cc328ae498cc3c712300441042441f66c683e06dd741b644e8e98732552e55839b66f86b82", - "0xb625cca7bf4ce510f21e8197b223dc49e7ce245c5a5d1e901438eecf7160a0bd37d0196191b1d934779f4b6a387b6db4", - "0xb63d753d728670f3b63d4c24acc4a3d4859e5f15ad775e502fc50d7ca42b0d2484a8649eaaef9eb22cef28a23e10d5e3", - "0x8e951028c0b4c5a691a219a6dbf348ef66edef60796094d5f6abaff1ad5802b53a5abec9b8b3b3b98f8b5858672847ee", - "0xb6b71004d898a3bddbcf7f730b8d5c0d8bba0f3b508155412446732ed9abbc1d03a90864f4689e6ab207aed495830e1b", - "0x98f33a74e36c035d9476b198dbf3a75573856264d45313e5bdd89db291dceaf4084917a2242b0a30d3b1ba4ee3016c42", - "0x912fdb4358fe617d7981bf9a9986dade7fe279a0445d7b14951ed77eb88c77c4aff4162467e40fdaa9dafe78da0ab4f1", - "0xb17bdf7a896480ae70b3696cffefbca468b57493d5db59362dd85a3da296e1162356358080c8b0a7f3fde798a3ad1d15", - "0xb47ebba84e62bf453ab223496a892fea2244ba6c37615c3db31c2ecc16a5f9519dd79aa710ec1220a2cebd254f7690f2", - "0xb3361190434ab75e46a40e0ce21ccc251fd0139bce90664bd33d9eb6400317c3210509e4ffeef604c7b05b260544e19f", - "0x966916b3966d7d33be49fa4eba925aa2f92adc2d0228d1144ef633dc5d67fd8231087c488b492688fa142a8cdb45ca70", - "0x8ffb1491d4448af82b7cab5409ad26d99ef6ef08158c73a9ee9626c5a84d2fc6d852e2c786c94b47b5931c7194d5b82a", - "0xa2d4a5bb458688b8f593f39cce2b27fc05f8ee3985f4c5be453706e8f174d5a6585c2070c0bdbb54aa1d8e79b5ab40e9", - "0xac180389d0432699bafff42a4c3da59bd32ab1bd1c4b4a4829580577fb3c5eaf8aed4dc61a93262f23ac44255e6c2b11", - "0x87f8fe99acc93080e2a2ae51eba24f0b146c1355855a202dedb7deb8e1cb5c6ad8664ba0e93ded5ce253597fe015fdc1", - "0xa554d88dcef521dbf5e4823bcc9145c8ea98c598cab56c85f101ca7be82297dd7f361d457966bc69584adda6d40ecab5", - "0x86ee126cc839d869c7e91f0f8d919929f66c1f67675ae8c5eaf6bc35866580c49d45ec8edf0891b546ec2fe7bebbd304", - "0x970d74575be6cabcd2e33a8dacf25b378ce750478bb44086e1821c97b6b27055b7f00cc8ca189954f0150de7381c80c6", - "0x963badd0cac713d8a23dabb8ac7da5e9a83ca7d580ec81dbbe3e5d53c5c9370b86222ca685215eb282c8f67a977b4b66", - "0x8d2735c85136625b3f8c4196a8f892e276845ca7c876648498143f1897637807a9a5162bb90773099a7b0cdfaa292263", - "0xa1a8507bb8a300e1df882651b0155e46a0f58399375f4e5f993251663b5935a76a02e60999a4851fa082a89d5cec2e63", - "0xb712dd139d791a95486d8fe48e35bb8bbddf890435dbf8dbb670699dcfb143fc582d4bdc8a6751f6bf58a13dd8c2871c", - "0x8f108fcadbaa43dff904a23c89d809746a9f732be817c2c882ac3493624aa5e49af7dd9b46de7d9d01ae982bb78761cf", - "0x80e270c6620756d3d127457fa2e51592604f85479a1004d63c184d7d2ffe2eea4ff75faa436f24bd1494f4eaf90543be", - "0x81f039fce432a5d3bf9649ad0fc2d93de831f5b9c0d0e5fa92d35b5bf4a52c739d478289c2386efc964026134f91ac0a", - "0x89401011d51b6106855487a37459351f18c39f08ce90b15e52a876cf36e969a9c9fa6cad94a55b844ad45fcf1807f705", - "0xad66c149ad105ce8b53d38c410d73a3cb3ec910a9f0ae798f3aa5207501c7ee39b85f10e91b4cd91e6b280f3912c492d", - "0xb709445e56d02a558a1496bd2b9115d2635855b18984cfb908cbd54cd279d29ecab21cce704cd9ebcf34456dd1195d79", - "0x851059069d9fef4eadf6ba508ca330ecb7436ccb91d09f5d0416874f9fbcdc56472d2adbaebc63a63f190b6abe7650d9", - "0xa933c1b614e6d5a58c599b7189d06bfa3683995f449d636805c8307195d8e98b62ced873997898d6b1b01f6e6a52b743", - "0xa692ba436613db22bc30c3f36a516971158d5489bf2c50c39d0627a74048a6d0b229606823f37a0832913425ddc30d06", - "0x830999596d203b96329185c100bb7360189a50f7930286c36544d20e57b8418c71d8db331e4352a98f380c68a49b2005", - "0xa56d7c262bb3d443fc0cacb2b61f24554ce35b8984fa3418bb4e271d5fe4f5378ef7b12c6cd02f537820040bcee95f98", - "0x844a4e9a8c9eea0b6f929a80da9f4e4e273e999fbe182d3855b0a40577afaced6f8ea285595573e99e13b6a71b985c03", - "0xb34df6205fc429c9b7cec189b2634d49a4877f22bb8060b9f7baf8c2eac4e1d476ed1f30fff1f4c019c65fce96abc554", - "0xb3a97648b3b79cc513246d3d1722afdf7645e7216af233645fca6a2756686592635facec913d19acf99ee586436cb58f", - "0xb9cac906123f2a4aa13d5d7eaac84e84eeb0a1b7919526de5198b5475fb815ce556f8451c953bb0bc910c93c6fb3fab7", - "0xa5e441019d492897de73d31a44a0055fd04e8cac894d626d0457ffe9de5394d0bf851dc5941790cba388b403b86864ab", - "0x8e3081cc7999d91d787e4c0937c9e22c959d2ba4be6fa04eb97471997ef150836a910ef28455f117dd54fa9ec655148d", - "0x98eb793d88faa691ecac3a7c78b25eb3a833ccfd0275186a63b1b1517bd2b984d9908c84e55f044b31c2dc5e251d0414", - "0xb38b5454c2debaf1a4e9e467c6205cfe26d52d1c1dde5356c089abfd6a90dbae89525442419f108c7c8e82e34ec3d5a8", - "0x942545089077b9f27304d2d6ceb3d549e983f100417e88332bf05bebfe8d42b75a96171ab3bcd049acc859f3cc9ad1fc", - "0xb9d444777403590be63076b5dbd9325ad58c1eb244dde2c9628234b62ba74f6b0e956642af2d08cc65f82a1b2e24bfbd", - "0xaee8deefc7ac67882ed7ee6c01c08d7739b6642deb2614064c69ea38c5c65e06cf609bcaf7db74545199cfa6122f23eb", - "0xb3e476268770abfe0cd64a4f878c58c027ff352569d8cf571bb067368e777eba6c003d344746fd006c8bbd474fc3360d", - "0x858137d63f90f66b9ef2a38d7ebfdae1bb89e5bc1d9032c96d699ef276aa2d7461366c00de8c47de9231d9ec436572b6", - "0xa3dc8fe541c9cdf89d83753347d8c573c49e8471dc07b5d41bc48ad1b10a3fdc218adaeb72bda0f362c8af8e1194df45", - "0xac75940ae476a6ff07cacf70a379096786d10a5a5244fa5c466bdd8af69b1f98e97a3a27877739dd4b223627e0ce6d55", - "0x8c6809f893c5fd03ca80d845147a82d8d54bb7dc6a688733b1404dafc360c45d5ea742f98f6a70ac2decfcead05d876e", - "0xb0818eee75f08ab207832c591aa783193aee5742147eebf75cf7f1eee6a6d8855b309db4f7ab51a16ab77bf619e14fef", - "0xb339ac167debc92cc9132dce076bce281e7f1b9c88978d36e1b5b9bdeabc974af318ff142f746319681764bc4db191e3", - "0xa51dc040c75a8a8bc3b0ecef47ca313ae13d9560c782ee014257ee728a09432c4486a3f87b5ebab690231735fceadf80", - "0x802500a52dc271c52f893b620952604b79d25ad243489dca7cd679b22907fa85947c88dc04463268d25dcccc8a6c34fd", - "0x97b136a881f500b07e5b0b79fccb84b93dd108074f92a1cd76e441239041ff389dbf03483fe76cf7c22a5f40b73b51f3", - "0x9155dfb5d7f7915e50da7a715d1a5ac5b43d7093546d6d342ec8b69d47a86cfcb9dc11d8079f123854033b8d3e1ec928", - "0x9423ac1e11f70b5d0cbbae48b7a5be8350387460631126ebda095b3b33a7ee2845776aa20ad60e2bfaf975722d43064d", - "0xafa907dc76e03d10cfbcc226e50e3bcee56baa4acd8db2cef8e484ee7b7bc536e1765e764180663710c4396e22fb4dc0", - "0x8b6fb4bc641fe2147d3394322418e2e8e091718e3b54dab8d0d6bba687bc300d73cf1b17f81e2812f0943a8bbc1de538", - "0xa8bb533bf42f56edf112b03d34eb64f6dccd57251244f39daeb6531af650d0368f6e4a0f9171aaf4f5a5b4a17debeb56", - "0x8d763490dbc9a9b73bd571833afce20654348cd553a69678ec89454c4cdac044ed3ef0458cabdb60ff35af5e63405961", - "0x8d3ebac80c55b7ce726f4cdac41c7e2f6a5ff4ffcd5f1803c463ae524243f136dcd15f9bc74f8b271ce90a4776c94868", - "0xab63cd85311fb9889041e692bc9d5c1153b26a805b511721154d28f11dc8ab84733387fd20cfa30c566ab2f8e066af4c", - "0xa506ba11063b14f25c26c92667dbd9eb67c8585d05d3980284aa19a09ae97599a1cf8d7cf45b70a32063f1fa3174d3bc", - "0xb834434632307602d9e046de6f625af5de673996108911c6b05d6bd3e2aee17246b2d860f01dc2d6415fa61c73110e13", - "0x8248b69f51196ce1e15fcdc25d487153896d1f74818a5617500cf0bedd5180028e6567533536919156860e34ba275f1e", - "0x86a5ed8b6a1e9d8d17b69640220bb80c9065198c8f7610d4ee6a60d2d808508771a84d6bc679ee4db34f43f94315e0ff", - "0x8fde55abc106b2afdac3b8796f83c8ce1b90405532fd586d349340c4d7a4f4c46e2a56fe2663fba770a8004dc7b9d523", - "0x82489db9dccdd13293499194068bb4ee8fff51f74f1b504d203c5deb5216287a6d614a2e0a769d4c929bc103582c92b8", - "0x82b2d71281cf886e80e09ff907c1f9213dc444c058e965f964bd17fd36dc0382da2449fdbc3aa7b6d07004d6722a5848", - "0xb0729dd38dd64c441e81a94fac0c8b5b3588081e43a5b0298bb576b16a9713acbdf09b9bc2499c677064619cb3a172c8", - "0x97c4bd5c97182e80f55e82648e387c4a3362c6088381e96b67cf0f04bcdac3dc670890904180a5388b97002c70481235", - "0x98d99f80ae9c59c921c6ff71ef01c2ba283f531ec32666cca1fe7dfd9bbfb09f197e9112af1761068cba8d6319af5d74", - "0xb0569d892ce82d87a3d809f4c86a88ce627ed420dd106ae49b88b8c470ddb081a3dbdbd92d7fc032a7082650e4197ed2", - "0x8ff68d42ec2dc5b13ff5c7ef506c619c4bbb0f62fd4c08e320953e5cddded2aa34624c6c5768b546cc2f00add0dda58f", - "0x8b53131206c80638dcff21d7f2dabdbc6faec545f19ab1f4f2bb858d6b01d87adf886072c3a744d58124b8a7a0c87573", - "0x8b9c9aa127ddb950cad4fc21cd7c8eb802cef6db7290664b1773b9744836450e48af503009d4bb266ceac83d765b3b9c", - "0xac61e051add512e749588e2549ff55f3e6fee5378443cbf64c80cfd7b260cfa63f16fc3e242aa140ea243435be28179b", - "0x9240700fdcde974f319a90ec4a9b92a0323424fe39e513c7412c621cb33072d193476118636bd2655867ed2816e03034", - "0xb6b05975d0653079034f9792d5d8cf5743e1737e1b3860e431a1e159199efa5a55b2d3283f6d270c9ed3156a233e858c", - "0xa2ea8fc31294943a3a6d02509cf8b75a7b5d94de917ced468fa64a6c24ead4edef11c34782eed848792b0570219fb77b", - "0xad0b54dc5dceb242c05a7f7c529289c8caed93ebe743f6609df653aedffbd7eaffceb53a18dfd109f28d14c80e1f7935", - "0x81e4d4667900eb5a8434e2153503b2318f63708499534a8d58382931791eb0ad0522b41cecc7eb0e6ddf99002bd0127c", - "0xa4c5c329fe159bdeeaecbaf479c60c8f43a58ce613e135e9e9eed4af6bf5b6116bdbfea31c82bf0ba87c3f651e1464f8", - "0xb95eaf48a9128df7f970754af926f9865c2078cabb4da4918d8b45e95d72748750ffd12f1d8d3f76cac0936ad0097d16", - "0x8567385d52e6f6dceeee52f6b690781f7c05c26f0d20912bacc38c23afe8f64925ba18f8b6464d4a0557670ed0cea232", - "0x8f7483cacd15fb7e49b2f8deb7ab05e64bac18ac9dba475666649c2cdbc5d6df0d5e789fdaaaa997a3b524521f0470ae", - "0x9252efa0698c0cb30dd431a72a0f5f2f14429f6ba50bb60f7039df45777557afe3ae732b9283b4a814d2146a8cd8b7b9", - "0xa54da5287928a02cd5eedabe70cff80e56db252e2811842545beb14f25ab67788460a71ab8ee47cf0c1a5f8d01635256", - "0x991a80279c622565a03929c94590f33cf0621a79b70a2168a41a4376bb3f0dd12a9ed9b16c0b6a4a59c50b5802449874", - "0x924ff5d3a6f0ff4ee58c3674319971257543d2e19f0ce3fd0b0edb214faee920f8d6199ca794a173363a9fa06c96d7b4", - "0x96b136b8df76ba24e4dcd68065c650fdc224fdfc9c1ab6410e008fa5b9580680c3c85801fa217917c620c86dcb5ce3eb", - "0x95934e64af642e7d45ada1bbe8b9fe972877a674252005afc34ec2e857f755ea0d77e7759ddb24255f21252d6c739305", - "0xab14c6bdd6d1ccaf69e0dfc6c832751afb70f89e4800c6fafd22db2e7e5d6f2addab8b1267c8f3fb85cee51c761e69f0", - "0x87e2edb8dec1253547cece2a7e6934b0299715e634d599316af0f076c61726c7f2aec83eaddcc9add1c397cbc9fed0ca", - "0x91170baea88ba00fe00db375e8d948f58061f9e7b36a4573031b9996757afcc2c7e9c2d9642bc51402aa586569f0a398", - "0x89d99b120e4565b0538b2ef4f8d8c05997cdbdf61840e068054e7f21677cdc1dc2f63adab1b6814225d14275c737b0e0", - "0x880c2b79bff714665e9b3a0a647773a212ec5f0dea37ee6b29ed6850692055013e108a86affbe44d1abd0ae80a748950", - "0xb564181f9ea65ca25b1ae7f25eee84b73f9db109ad1939e6b9351663ac0b083fc13e6518ad8eaafa3caba9ab959bf7c5", - "0x93cd91391deaa726320574bb46706fd8e30ffc2308290c79abfe2d234d0f0f59ee4c38791e3bbd8c3f840a920489ebaf", - "0x8e846d48e7b120b59c6556a0394d25f744dfda0cd58d4e70029837753a82afb63a015e79157fe8c810cc68bb481d19d6", - "0xb36904e7dd71bada7c9b9172e4a6748287cfa0cb6960ccfb7202a36c57bc28d351e1f5371c2b449437cd266f2d22e7f7", - "0x8947c11af34a42f314983ba9c673e62fcf44c6c1f733a697351e1b8422a75338a85bb19149fc130d01492ee18b3c9492", - "0x905afc0103e34fa9787102fbb80967b8c917bd03abb02731fe49ba1acff1e96059227616cd21080563e92dd021117a84", - "0x88c7acdc65e6373e4c8ac6a13d1bce1d534aeef2965a4d9f887b2e823c7ee7921db1397df5cb5e7f12030e310172d6e7", - "0xb028c19082411efe8a46c8abfb9936c005e766e2ad3120be774172f16419e2b04ba3f31132ed2bc209e7214c2d7b2b61", - "0xb6b3a561d583870193226391ebf51ef47185ab6efb3556ae59106b6f266776064e5cdb66f0c93748e60d557db64e8f84", - "0x93732aa1473dc2e50610eab2c8152f2d96992fea840ac2d82c6e2c5760d8c1c05e8ecbd69e14d03713f43e77ced9d3bd", - "0x9734c433ad41a8fd91e161de033a2a55189ae31e2af406d1fae443a182bf1977dddff93f6fe2ac7d9c4fb955c26ed59e", - "0xa1f305d17c36c06c515d30fdfb560f899e80a2e2461d0bd947032e5ec764116c7ccbd528ea42a3b9351e3c9b45904431", - "0xb517f46b582655e551f766930637e8dc2a762dd7a2c54fce429fdc4cd503e9fe4bfbf323f50860be2c18b3a17d528654", - "0xb395b5c48b1cb0daa8c156188b390a78441c8f16ecc8650520f9f2914bd1d992b83849bb11ec17a47f9f2d40d138e3d1", - "0x9147b715b62fd50e59bc96d210e10f1062c87a90263b5586746325deeea89e759464be55a09b0548766e13bc910c4d3f", - "0xa7dfe5e7a39767d14d531d371b92fc4979d326ed0f966eeb7b4b4252d78117bf5295b3c17d1fd636dc2c0097cac901c2", - "0xaa3f9fb858b30675e9e57170a1835717521eafe4bd0a0690b10020c7a753951576b4c7dc80cf9f042894fd5741b41b1a", - "0xa1f11dec034733e862cdd4aefaf0252a9e8175b6d4c834b7a7d30ab884bb6ed6a1d92bb0e958f0082085cd80157a0e40", - "0xa1751d7452b7c5596fb801466d8d07a70831e568b8ca66fdd75e5898739709795a5768726ebe13c469b1d51092d86a60", - "0x80acf49051b7caa6efe78318792d05401f5246c5b3bef25170b2a49adfeec8048ad5a5e6d50cc498b23896176a9d9669", - "0x94156df9959c678578ec6e12ac068f3a28d69a981443fc35161d14b1f0327b8424746d62869ea9377a86ca6fd2c95b5e", - "0x95dd91b1e9b457de913a65f198dcdceb1fca75692853bd5ed44eda6343f32126e6aa2a309411e019dbdb9519c865b96d", - "0xb2516bc36a726cf2dd5553e319c64fc508682c7446a2a5ae696e43f1a8c129ca9602f5a41bfbb57865a9dad1d56728d3", - "0x90cd63b4f9216fb70635e4dcbc9a6c5874cabeabe4f9ea83bb923146d03366d9befa48b20a64f3a2cfdb0c3a84007ab2", - "0xa55bfe9b33781501f10d5632e8f5330841eba2d0a64b0aaaa92db56f014b5e44dbeda3b1f5b2e4c17eb6a243977b2a82", - "0xb9e84b3c617708971f5e174fb8718906f9bd353f8b0fec8fa03d1a6e4bec20430212396a5406595343cd15777c5a3f8b", - "0x97deb79dd82185555442f91fb9a70cbd30a564751528fa0df0a681315b8a71bab5073716908ee0546d70dc41efa3b53c", - "0xac77c2fe555584b9cba7438a4e3904958f671c49536f185cf1f3b25c5a57ea65e15554de22def94c5c623e8c99e47a9a", - "0xa27c62d39508552d79d2899bac6138783f308e3befab65a96a1ae4ab108b799628cf37db1ec72859a0ce1ac68f68b106", - "0xa2aa287741f03e31f2c87fc37e228279b1acb886f32c6438b3e9807b8126da875fca7f194295c45531e939a13048a882", - "0x84df8999c4c5ecc807819248957d68909d16ef64d94a820dd0d266cddb6775c9c7464f0b2385b7bdde8fc0f2169a4177", - "0x8388e1a1babb941e03806b392fdc1bbe1a01438292ea1db4087b010de0805be78cfa56d20e9ef7c8b6be5a04bab1b1e0", - "0x8cb6ec409cec27e7c4537ee2e5bcf82a33e7cd4761d19059e902b6068a9744e897a6010e2ab42ce72625cbc433892ec5", - "0xb6e71cf74455b0f506e03eecc0976831ec9a56eb8fd0e39e5e12ae199180a4c6e5123174ddea6ce6cfd7a414cf0afc5f", - "0x815dd267d9f67b4d229a798a499b70ea2a611f3bf2a9d3698d1105890a2b6462fcc7c6ebff0d5d709707ee4ffa981689", - "0xb4e5b7fbab4d8a66d1b167a5acaa4d53949e1fbdb00107e62b727b4b4b2cc70e2685cd4a16266e8d13ab176f9be09c10", - "0x8d1bae7566ff551f06baacd8c640d0d04accdd49fbfedda0841914aa1bceaf9f3f27344b80bdf5f9b93ada438a4e6d68", - "0xadb054123e27afd4a691d2cd808a3232ab58f56fbd514935caf47b8193b4c64aaafed4d08a7a10ec4deb66be8c292e64", - "0x8ab5255246e01478ba7dc6807c84850308a719f8f8433eb049d5b11cbc361c08930722e7e5878ad99fe1586b3d11cb1f", - "0x90e862be1e3d0824106da33aec437a87dbd2599aeb58d46b4a39a5f651097d49943c3248a154e09e309eaa7abff3d500", - "0xabf16f35e3b2b29a72cd96802c900fbc54100484299198b2d40cc6071945781cc9bb3eb43f6ebe433a14c1aeb172929c", - "0x867a0f396374cca7303845d8a1e4bcebaa53cc0fc2e790dd58cdd0b5ff2b9a99e18ad4e57aa2b061826545935a5607b5", - "0xa6b6a2e22932d7c9ba8f27b1e1de8559631a81effc77ed2cd7c45c48e49ea7d2f68c59d07a155757493ad82f733d93ee", - "0x885e4c3904c545c0eecc9cd02e16d359ce69a78e3a355e7fbe6485762d4523f2604f2f663a4521152a8bdb6fd4a9d4be", - "0xa668f417391b07a35c5d40ee5212cb7bdaffcf040a4f20a3d7e70e9d715bd908d4f8fca87a7dbf7b676e088ac8651ee8", - "0xa70d67f3379e1ee0708c34c4c7a7f552267ff679460b9d8891549077c724becb99ff79b35bd80420a4290f293ed4133f", - "0xa523cca782ced0d8a3f7e19707f9c64ff38495f739e035bcfb5483f202b209c07c50c764eb28d3bd8cf68ae093c46f19", - "0x8ce98e5f96889ebada090449ae198208cae5c247cc5f6fe7800b4c2254b0e7f2475b632cbd5021a0871b466c5b943dc8", - "0xa69cfdeb27ce1163ae6b6b4b5d46b49507c7e62789f2f90f7f5a0fdce79de988c755cc9afd8397b1c02976e03589f985", - "0xacbffc94dc0445f7797a0d83e5107ad3ec8bf61620fa83e73a999ce4f9b6bbabb00245a619aa6f9b082a2711bad5ce8a", - "0xb64162794503c86e478c23f060228105bab4f3f5d46582bd455a94526aa6d71f4c9630d8d63854c8c67aff3904681e0c", - "0xb1288073c012a0b2b7e31708e874106031a8cc98b2c94ad0ef1d7b9df42f429f58caef5494f6d581baf12970cded2a17", - "0x8d7ad217c3c1cb74cc301540a0e43be6d74d5a3c0383ab7c9dae57e25f8725781735b58301ebc014476171725299782a", - "0x924a33c759249af270617767101385910494724a51fc63600836ca00d06f0ca86a4a0a85e5e87cc29e404ff8e04d036c", - "0xa7b21ad39bcacc96cd857328a83e5d26cddd0a5bb2326da9a8f593927ae7b5927704acda9ee217176618c964d0452d54", - "0xa5c3616c308bef98807a852e16f146859b0b1f31ea8a721941d90abcbe37eeacb4403c6568480b6d6e773bbb94a89307", - "0xaefaa1033e47673ca2b68e4c945e6ed892e223146d4fd24219304c2667777c1b18a19488b73053cf7b0e6e09ba1278e3", - "0xb308c690176bc43051f51839d3ae1636f6de5a57c626e8def464820ce2f96ca09ff26294a3dbc9b4573cfc42dd03bbb0", - "0x8f7b1253ea9e257195ee92c54de41f2e7a310c90602a628ba3180e059e5bba79d6bb8110d1964c59daf4b65cd9735704", - "0xa387f003f7731b81bace54c8501a3a2a25d8a910cbb28dd603ed16ce61ef1df34e233dc8579071856d7198a071efedf6", - "0x955ad5523828c0fbe8ad6a77a191661ee9c8005b741b7b0439b76711b6992795758d76133399d266df5e494e4f86cd67", - "0xa44441964f5cad7b54d0105f162ed3ec40d12870fe8c5c30bf16238440143b330ba986d6adb00c9626747e99449f765c", - "0xa52df726de07cccbc77e81abf4f1712657c9261f65feee8815ef0e8a4ca61b8e470503801f1da8a23fe6d52f3306807c", - "0xb5d239222c3d852f2c49997e76d97b70bcfe73d85e81258d9781f5f7de87f9c81648bcf58cfffd554e4685b2f860e6d8", - "0x96f0193aecbeb1540678f1a3369401e916ee75d2a85f7124c555466a3def91a5d8b5f774e3156a163e1010690d457c5d", - "0x886b9f4965120d942b076d053571837780232e139c3efcc6bd6c64eabddbed2d55c3a9a06001bd7a2ccebb36135edf4b", - "0x897a1e4e9f4eaf755807bed984ef0bfea251740386a168061f4386819acaa337fa6d3f038b4cff9a11926e68f7888f90", - "0x989d9706f8396ba422a34b55897b9e261ac1ba0c7a7a11a30562ebfab92473b9e9b604ea8baa6067137a4ded070fda10", - "0x96376812651020f68c6a1f0aecd04591fdb628051f01daae179f7008ae33af5abb42e8f304662c9b6e2584e8b02ba6a6", - "0x9344e6f3ce42ada6281d0fff654f408e61f0acce81e23ce47466bf1145a99cf60dfba9a22304efbb1f428c92357d644e", - "0xb90c5463445156c8de69d8c35db656a76f3e195c325808396a829c11c06a7503f3c092816b3f23a263d56d3f2c075ff7", - "0xb4dc6d948f4b67b513ce27fd12bc8efe43813c119d01b2da391d01c1cb0abb7d51350a5446e0a72a6f8bbbde2ee4b0c4", - "0x84d208ab983941bde208fd71d58c7f9335e14db237cec42df453155a3a8dcb21dec8696a1334cfe5d035c192fc44e88f", - "0x9577996c78372d2d6c9de27d497afb29c918bd894bfefad9059bd85cf2ab520ce1d517994724e1567f12e385c126f26a", - "0xb778b9054776a2b8ee81be356050b977bc8aca0d0a202be91d56ba89d8a384bd29c5c652ea084709d4fb365b107962b9", - "0xb7ea99f8c841678dc854527ad0c8ffc700b43b5b36b3d18303e51175b3901b144c53e22eea6ce7cd500f6879a80a8c21", - "0xb466aa7d1a5ae3d9aea240c8114b3dc3af38f7d8f1e323800a6382de5766f19626d07cd6ca6eddfc4d71a43d2d49a07a", - "0x8a72b1ee7993f16400396982b6a5198f0de08821431bc66489189d5b364b0e36daff5077b48aff1d55c9a88580cd1dc2", - "0xa7c4dd6095f8cf61f42c5901ab67e9d1ad21a42d1eae9ca5e147a9396507c7a21747c2794f71ac66002840f4fa4e1dd0", - "0xabe40e33cca787e7c521e2e97fb5f95cd4ca7ad6148a505afdc94e0c003e4903b1524164a1df2b2a1330fd800ac33b7d", - "0xab8e1930b1e592aa2379cff636e7fda9fd7f05b358f47d9cbadcfe35fbdee5bf06469fefc052f62159c10942ea2bc5af", - "0xb28edfbfdcc27c3892d64e7e05a2aebb173808c020186c225590b03d91dacb866108370f2c14ac97a6d20d95a8e32f8a", - "0x97d4841704bacb06bce2778104e4437c930fdd9320d85cac383d11ce9246525ad5167cbd63ef04a8ea39c8fbe3d88169", - "0xb4b178a1c3ccd3344831936b784203919cffb611cd18def1a52ffa2a8e4286f9f9681bd48dff9b2abfe62da5fd619fa7", - "0xafb01a4777a128b02fc22e282e0c4ab1d86246d8e0813a7e85c51907bce079766ae40c31d3c440d5f99c92e89d3a683e", - "0x91cd070a607c20140c1f35b25057bfa20290b1435e99c5b33068c4e5755ff8f1aa2be61fba28dcfc131cf881aa1c39ec", - "0xaaac82ccda92c6090970f60a56668c011ac20dcab26347ad40585a60b5a9b5a9af883307c55526d4eca1b110a079fd3d", - "0xa7480de83b4cbb2eedece3d3b27b8d934e9183f448d56d5f49e0b63e24575014a94e09d406d7ca5afda08da9f4eafbc1", - "0x8e568ae356775b06d963b440f75bad9b5977b7bcfb8fbd1dbb4daad5192521bd189674654d4ab86ded4a6be5fee27ef7", - "0xa501a84cd0b4138383572fdd09242e3a748e593f55400fa7600698d4f916a2fc1feb96557a34f4ef0f13eee580fe9f54", - "0x8be0f6b98d52b83e9deccf09d05fc4a7b4ae1cb11972f01baee4fabdb804cee2b0e9b44a1385238f755d2c1ce395cfa5", - "0xafd01e3658ed9204d39fcdda2d79239b8c45dcf80fda8a680a8797b6be294e7e8bf56ce345896c3f08446e9a2a053a61", - "0x851f0def025a50910bfb6c2fbe5ca62a31426747d9cf4634c8faa714a014fa22586c3eabde84e23ca77371ae25d720d9", - "0x90a1aa7bbe7842cd361d0ab2e16203a56318885d2d32409370ffb64ef0ffd3c8c57658573a0714b04dd1595aabfc8f21", - "0xaf56f30bbd796de5cbf6f3d041c2f649d0f36e0a1335ba923eb1487e0b29d0ab307a1143e6cabb79674ddc01dd9a5cd9", - "0x8429afa5476d0f3a4eed4104fdeafb79f80e94e709b59aa44b4caf0a94bf75fb3efadf76e96389179eafc389fe896efa", - "0x91d8399bcc3b82f0044b9a697b2bc402285f6d2e7b76eec25ffecab769f3fbdd45d20897d28a8676f090edf847eb3c70", - "0xa06f8d37404ae58c35732db58c4c6270e4311c691ecaa7d89b3e9b2bb1421ee3c3cde555d066340c0f2479faea1ae162", - "0x8011fcbb711ba6511960245c69a73fa99167eeb4d238426bc31ce359a90a2339d5936042b281f3ff3eb976357db96885", - "0x8dff2bc19830b4a58d2cc3c1598d060da34c8fde11635062dd4222c96dcbf2bef79b339c63fefdb1653153ef9af79c48", - "0x84ae7869e2405e326bd167249f89c2e018817d3edf59f3db8adc25f64836ea4606c78158cb30020a54551283bcd9f39e", - "0xb7be6cfbb7cbb7788fd60fbfcae3504d441b0af3b46317944e00a23079c605c08fd116311432be5b378ed8a11da219e7", - "0xa3656ce4a79484e365b6b7f81a9dd72a09746da16175a61794bc5fcc0c3dd608751ea2cfcf7bb0c14421e0b05d94df75", - "0x929d5603a936bedc69ede2d1277692012d0c820a23915ac6e776b832b9f4e0e6276fb3b257c7abbca32ea166d4482149", - "0x82d47138de8b6ed4bdaf69526ace4f6fdc50fe5abee63f1c6d4447fe4948a84a63b7963c8a65858442856e282fabaf26", - "0x8f8b2d05e77e9e4e2cc5229ea98c5c02ef9d9b6939ce6663d98d8e2dbed73af3d41628662c354972c1b48157f8d3c459", - "0x9353ee31f477b51558f4ba5ca437d801f59d01ed995a8801552f8c578d1937997bd76c31aedab99fb5532789e72469b0", - "0x941f777fc9115fe948f3a979e1ab87f133238935acdc19d33e1d86a1a64924eb9008e91bdff8d650f5e3ad06db931234", - "0x8ee79ecb7d07e3a5fb80ec15c085898e97876448685891e09ebee9aacd5cd0147715dc60b6f75b601fbe83598f1a939b", - "0xa96a50de4fa25367706c99abe9dba95ce1717483f50692bde7c8c3a6b427d02d59ef6e8bee82762fe774f5afa528f0d0", - "0xa451ff58246340157fd94d028ce1ebe5ce49e5ed87d9b61884c8ad903ef9b697c4ab9e5acf66180a89a17749046c99fe", - "0xb12739d77fb96e9e93065fe279307eafb11c795da2b9efba4cb239425faf687b9762235700d9f2cd5df9cd9fb2b36e3f", - "0xa665e34895d02e68f1dee7ad8090558514720ff3e809cf46cc06d1e1274d219fd08781fd430367a3f09e2c591dfd7cf4", - "0xa262410cb60409720ce68935e91c743aed5eccb4a0427449f32a4acca103f9d65413290ffe2cbc895e2e1cef96ba7c6e", - "0x9597cf4d49db896796132aed4bdfbec71ebba08a617a611d1fece75bbfcce902e8ba228b662d0ec5fb927429f082eb38", - "0x80a045d2bd30aff2267a5385be12c646b65b24a586c4f5cb1bdb81387e3ff8edd2358cc8e3c4c5c60cab88d4dce61d90", - "0x80323f4a9fc78bc89aaa172f739bbd4f57f9321a7b8e9eddb74ee5c99d6c7b8dfe847f7103110f0a44d4e7c20ed29114", - "0x943b749ab77112be7593bb2ac11094c66c94bb89d5ee2cc05316ad623a3997a38aec741ec93c24770adc670b6ad12e91", - "0xa8e1b4935aad8a84112a99fd5a4d3786ccf1c985aca0b256c46d52a8801a132024391431cc2cfee760c25eb18289041e", - "0x8abbe403bf13bad914a4d5bb0c8709f5b264a7a81ba0542850cb89c3c45bc294f62b22a36d7f829ca80830a3be5832aa", - "0x9084defe85d170967c08d1f5e74ad1dd592c2b013b01b84b5fe3f2ceb260bde2e52ca22e4006b07f01e3dc7a36774e94", - "0xa34cf1cfca622dda528589b5e5e36a4a68cee7e18cc621736e3e165b06a5df9a8e9f3ddc406838c1fe94ebdc44bfaa18", - "0x8c5f5d7e98828d0a063d90d5f78bc493c46316fec3245d47ef8900241fffd5316e0d6d6f1653cb3b22bbf05286736c06", - "0xae7f2beef46b6645a6d0b3ca664c826be205ca6de35bd2809a4871f19368bd2c009ad7de0cb4c269c2934357e87c7f64", - "0xabae2cd1ff7320d0803b6b5345ef9dd364fcc001d91fa456199dde24f474ff3d0ce94d460be9659caffe7ae0a6814217", - "0xb86710fd69a6eeca8a813c7c1d643b487a32cadd70013a4aff4b7331ec08d1469fb17a06d06926e68f546e7f5238e1f5", - "0xb42e9dd8d0f12f95a16112ef7ea60e6f4c76a39cb64e7f6bb4fde6ed1fc54fe8317e93160d736d97d87ff7af69ac2a41", - "0x86e5561a7b621e68afda9d63945dc69bcd615cc099c84ac51ebf6350b25c9c07ab371ed5b160a86488e8213d143335fe", - "0x831c730524214b8368bdc619e5c7e55a0731b6c5ddd035e9d7cd90577a472a429e779efb0ce47320c8d3b62997eec0de", - "0xa3bcbb6c321b329ea2bb944f48ac984f8bb6cbcd38a5f80e8780257765756cd902d252a51804879104527bc7b251c1b5", - "0x8b1a0ee0219a010653f816de88b05116269325c42811d717544468b3bf477953900394a71d56b6dea13e4e6ef9c9c5cf", - "0xa5d06e2a43d965e47d400343c093d64bd5d4adcbe3928093c80439f815938b9e952bf59da7fb26f459a5439fe60fd49c", - "0xb92df54cd0515bb9868a8dadb2a808d3e62fec12be3c708fa6c845c833c3321017e2f8d71f10b064fdde02b098e22962", - "0xafd8fb1d8ced274650ecb6c370c5bbe8f09d263391af7c2f2290b5c99196ddeaeedc8b9b6173b6fa349872f58c83149e", - "0xb359418883d3425b1bb896a9a9e2a3068c19abbb18ebaccadb85dee713b14bca5b83992cf239cfbb73adbe2f07c63f04", - "0xb8cb045dcb0735b02d6e81d9aa9906ab2f19df2e2adb5bff0533022c03a9a530bb27fcd45173faac63a8d13bf0f41565", - "0xb8b8ed443891d3ecd807d3f92d8c2afe714a423b97019cec3690c24002cd0444548ba6b454e1f9934f01a419206896b8", - "0xa3c28de7e71c54dfba701b7e1053a1719032bf48d0e520bf8d513d155d588d08d14af3cf1e9ba3083f8e59dc947ef99b", - "0xa94d1569107e659db2ca58864eb8feb03c83ca083c75a6d949805faaf7095a4f981cbd1f89a464aa46107a891ba758f7", - "0xa9c98b0501a8c5102ec0daffddce83ab40bd097c4ccce66a8f8a61a3fc28564ce5dec37940092640b36a0ef6efbea4a2", - "0xa473b784935b52ce73755894177ead52cd9f2a10521e9c034298fc0461aa6cfb03d499812189eddbce4b3cfb58774a3f", - "0x8c7a7984739a3db7b28b7ef10f081e2cbec93e1da39738d082898fc73e83db206fb52cbec476c9809c7de61ff5871b71", - "0x88b87148a573e576d0a8fa912858b7a9876001d8364bdaa7dd2759dd908555119f6f580c0d3a663ff5c2a2bcb05fef99", - "0xb169b58fa10256b2753221aa33dc4f0f3308a294b98300528347ea4e89128a1a1da502990c9f2d266fcc10031b3c5a07", - "0x85b1f0e49528ec8e51112771686b5b9f18e4cab091f6f97dc9a327221fde48be87f59cb46e6caac6e1af1a8c70135e66", - "0x954021598c24f5889a642b9d20488a04e3c78c5b04bafcd0a01f377cf24b49f64b1d820ee8a73f8cc193e1de9a106a6f", - "0x8954b280ae11638d6e9c27f826fe66c0ec584fccefda8665f75e0699ed640e8e74fb1945753f84baf606d2fcc804b1a4", - "0x899303d3bfcf48d28aa49e915ddfe263117ab87384f611bf7d555ed141dd728a39b97eca74b6b623a20d44458f35a157", - "0x8d792116aaba18c94069cbaf0da373c4e61662189e8bd0f24dd675531ee6f99d574f91844ace86e3d018c4603ff0e2c6", - "0x876c457846f27161c796f2d063aac7f73c2197ce707872c828af81ffabe91a6f353c6e887464c921718425d046c0a722", - "0xa0382a36d4f8007d444643bd5d85d0b6c3c892c7ef8158f51c878b13af6a5b7c8b098ac7a6e3241a6e13b4ae987addc9", - "0x81d668e292ae20a5a39b55e6798255c39368d9b83ca46e986c343ff9cf4f3140e3f07115995b8fc2554dc0372e4acfdf", - "0x85e58c950d8089ebd5d0a9d852c9e78d1e066c4cf1f1e64b4e76356745d3eddc13f1abf177dd32f0aede2f190053f8c9", - "0x9420d1c470588085057549b7e6544aca2ca329ac9b232187d8ac894b7a878d6d3ea351357174089943b43a83f057ab8e", - "0xb6ea12644c6ae73b8b9752f8eb8bf06312ca14d93fddeb5f79b92167ed78338964d620379387ffc9e12ac0e323f3500e", - "0x82767d1ca19c7672d38216bf17a8ca0a52aed5dca77684da56419430f9129ed25b6c547fce50c834746cab666ddd43cc", - "0xb1864c611fdb1b641708a5be8140ca0ac52d59d7c3fa3eaa10bd815f7f5e34413751f829f5fc0faa259064b73d43f4c8", - "0x92f67f02d17a1ead3b01478687cf26b40fb32f055f3b34feff21de083852460e02afb834f61c37fb91100709891379ac", - "0xb640a52bf00e4b29623c9b150635391b4dd42f0016e827daaad7aeff6e6a64fae4d67193664bc5bb368c72b138c76efe", - "0x941c8aed9a85a005059b83d35f6a70dae2e2b5f645719b567de0da3bbf1d2b85704ac977970a7206bd98609182090160", - "0xaa976af6c9809550721103fc8bb8359cc4313f672421a4ddd760bc4ddd86a036c1b4145049d9c8165335309fb608d6e4", - "0xafb11dfe01bb6a9d2cc2c040e18152645b4aa972fa01b6cb4285312bcb11a317107e743efb53aeb4bb6f8a7fb7741f50", - "0x95f8f780fae2878792aa3f60eab8954ecb107942bf07f0e2854173595eb2d4b914f4aa208f98a63b0ebcfbca46840123", - "0xb1dbec7871209fea98676e68d7a02dd82179a74e389bb9dc0eaeb2ac2d446d26810146586b637869ddec4caac8281bcb", - "0x931c9d571e50dfd2e1bee0c36f42085e4aa4e7d80a1c3bf99573d9d09ff710f6fa27f30712daba107d43d263b226d130", - "0xb080bc730ed34724851d00be3bba84093a296d6320fe7671a83364ab1faf922189ffe997eca0e1ce4ac2c4435d7b7f10", - "0x8dbbdb4f82398c891d16dbd4169716e078de5d677d3d550fd3853ff6ac8d15d278f17a2950333545bab823fad09a4922", - "0xa71bb5b71699082cc45037805fcd95e410c29575d229a556a7c5f2969fb6f957f0c63861048c65d5b73fc4680a5c3c70", - "0xb5bc06a742016a20c60d61cf55006cd7c2c7b8f367968f279815087b2bda7009c1773b9c91b8a4b78717b2bdf6a5e96e", - "0x91aa31c68634a81c9784d0d6adf4dc85c981e122635d4e1f25604c8184da860b1292d65324d4bb9bd8a002572cc96bff", - "0x85484fa47e235410e2ebc49f4dbbea9847ea064f6f6776dceb0d868280fe88bf6c5bb72e24c0ed3cb0a3f1d96ef8c9ce", - "0x88ab35f32986f0bbd8502dc81506cb18638298e856934fa374480dc455463482ca780385537e7ea73c4c863107b74f7a", - "0xb3022847a668b6d5d52d0af14d594c3e842afaab5416e3ffef21224bede0e1bbecb0799ddb7e095623a3a6f28b6d5f43", - "0x8802d0e6e5203d0018d243301c57934ca85a002f91e5763b2f7372816c7b3ddf719c3b743f2530d9b7330f4f35d69d83", - "0x85709fddeaaddead7a27d3f75e5ac568b0c9691c797f1505f5b33678158f5dff96ab98b921bfbc83368c6763420bf949", - "0xa45ddf8ed1c273d61578bf6830fabd4927f82e3efe7732d64a1c82887b9693dcabdad1e7a65f09bde459fef89c0eef82", - "0x970fb837063e059b1c2b4ec88011131e8cdc459daa1e704095bd327b7c94115c57cc1d9e8b4a29d6cc4f20895e309c61", - "0xb789aabda019356bc5c5dcb015f8e7c5da25150988af0d44cfb11d8702da22fbb43f69c4af889dddc0647745d162d91e", - "0x8ccd44696d8c52454c833b0b256ed0073527616ce49ef25a113cb9f16d41f39d27e3bf169ef0d3b2fe78f2a5910ec83a", - "0x9846a3ae6a2c339b09f53b6cb1262114d1ce2fa3ea43d77b04816eea6451e8620f0030ba428eff80d72d8e907c8f9e3d", - "0x80c18de89a31e2c8309353be974e42ca97dcebefc1a914e76b57609b9cb7c1c6298e2ee1bb35ab9d587f195010d24337", - "0xa43ac7ac3798af0923ef5bcf2342550aef8551c198a31b0bc9015ecb24fd2633bdcffd84a2c84f9eb72b4e67084caed4", - "0x8cc1551213a33114c8e6b3e00c68dd26b9cb3728376b498c95aeec60e7506a3346641ed5297fd4ead33c0e42b85079be", - "0xafb54536b43e311eef9f584b8f1074866f6d33cfc75a3294aad5aea870cdbc3c97ab6e849ef719e2e1e4386a8a360fe2", - "0xa2c5a2240256c64673372b63359b646dcadb108d785b4fb16a165f4b3b0ab3dc3dd5058582b25ed7b728d56d5aa45649", - "0xb35e3e4407b63edf3eb567fdbe03eef00dadddcf41b799c80a9c9e26ddcf0c6b0b9dc4df0a0c5d54bf31ac8273740a32", - "0xa3ce737baa7e1c1c69931a5b2fe1493a06fa0dcfc0b819ef8242b4fdae8d63bec8d15561d4fa24ef6d6c3a326d0abafa", - "0x910a67b377fb17d3f9cd1f994db52eb5f35a4aa002bc1b7208b259b12c64c095e4dd65ffe54772f8e2773923a890bc97", - "0x908c5ee131dea3f444a9ee2052c93a657d28f2f58e604bf08e51500a306decb2db964f68e87d5ac2f8207cc4e92adb09", - "0x8f3de5e756409b575ac2786590fc85de506f0adb51450f5c24669bb3a688f080c1cc37cb8e7a3c8db8e25c49a4bd76cc", - "0xaa62ceaef91fdf09d2ac2edbc07fcc651584a7e7d7d93e7bd4bb4c42105144c2da32326b3ae320b36a2df8aed07e5610", - "0x959fc29ce63dcac2b4dbe8180577cecf9bfbb6db6505d76aada43ddfde5f48ec8f6fed14fac19389f6c9ed3555ef7882", - "0x984cbe54156763d6ae078d6a8205cb6f9d63eee390dc5990f5d8e85b9a19fef563653d3dcc190c9b18c2232a916b1409", - "0x923b448808d9ac04488e8345d3fbf9aa57cc3b3f375af138b674daa0e5a864faaeabed08f94010478543f3e1248c699c", - "0x8c0823bf2706d9aa4c074673e9d221eb021de2baffe8b703e64e676b6801da73440b7793254fe4c8c48d2ff395e44bfd", - "0x93c9cb050494824aba0d57320e2d1dfc95c988bec46dc8d73f7036be9ce0d7de02e56ad1ea3dd8fc129100800aa639bd", - "0x9339fa01caba0f4837efca7a3d983fda1f6a479f63890db7f7beb837e3f6535b1f1d0788884dbeb73fa657410a4ad308", - "0x953f213ec904d4540b356d53eb88f646a98581a6deeebdf99a6646cf612e5b07110839d46c57b76545f6879f12371b10", - "0x99a4576f12de20fbecd3906e48dcc784cdbdf7fa0843c570c6f59f13cf3a559cc1f4882fc1d31015304090f83306280b", - "0xb07fb8b73793a236e58b7181df5a0a2e8d50c1d3069c475c6e178e32d14b6e75c45af60a8b54823c23ffbb316bd4a98e", - "0x98781507866499ce396730ee91a08e91d3be337690f7195750bd43a601a8f78e9475d5ebb43e347934429a4ff3db58b3", - "0x972a5a21354beadf80d8a6e449cc4f072d6b747de293f075b8e0925c89660db9195a30188dfc8b73dba02467ae02913f", - "0x827dd2e21ca88891b9b37e10f0d6b6304438cd6aaf9cb125ea7ed822078a233f3e1b49a8bc65f843e9551691b46cf91f", - "0xad3a4ebaccc157a7b880db6990a937e2d230875f509ce864fb0b7ba5febc7f4668191bf3aa55b85f3c17ce8b7d75b349", - "0x976672c981d106fe1835188e202adf6ce77314f8d7c8f797aacf25f00d57f8cfea31b057f6afcb40b9f97df6ea387979", - "0x8c77ba87e3e8fd5de85802a764b719d67f4edbdace75433af7fe966d3d18a94778c4414572b84f98bc6b5993a3225212", - "0x84ca9b0435001c246143e50487a55349bf38300cde86219d29199e2e9137e262a1619ee7d6f6c44b9551319f1ea8526f", - "0xab06e3313800a7dbb970145c1e89b7849db8a1e3169560fe2c64956a1f9d1d3157d531713a8d7c2213356b22fd3014ed", - "0xa0d04163ae987227aaba1ae82f74fd1b44387501fa47fa61b66728a0697155f48bb44b9eb5e87050a5bdb7605038db12", - "0x8e76d3e50853ba5945610251dd18736b8541bf72bd643f6b561cab1c028dd044c820fcf79a5b45882e7dde0ba6db164d", - "0x967ec8fdee2e6d61f0ca2cc255f4e72c41a9c1a62150318be0fa508b424487d9254ad135fbe8dcda55caa51b9901eda1", - "0xae25c496f872f7380d9c64fc9bee0dfdc0f05cc1d2d6ea7747e166cae7e67c17a72a24a9e351de15f52baad355625d7c", - "0xb8a95f3bc67ad2a2d3cfbbf2de2003b7bc894b3f99f0364fd181eb11d154a7193b1df9b671a3a8eb8bbabafeee2d1a86", - "0xb79996f818d94842175b06650a1e7819cb12c96b6ba37e61fa14b67684c2879e7d3884fa6bae06faba009329db2b0d1c", - "0x856e1478ef99338f144757fe4be68d935f0069a05b0a6209be2fac9ebc5cc669c6a80825d3c74801a54ff8b1a3777da8", - "0x8024798b150aa722dc599f288cdf455479763a9bf776da74d5f9cf76026500e5a0282d840e5ae5451a0e28d507b397a5", - "0x97cb767ebfc0a6cfe25666089f85e5a3705c108096a38151baa22308276ebf7cb3c04079ecd130cb8cae1689508d4bcb", - "0x874ff07def0f8d32f2ffce7cf31a43e8bc5e633b279acd7138ae938e46889e486c092ac34924aed9a4e1f93a91994211", - "0xab5b6bec8c81133b6edddcd048fbd526d59fc8a1f5acd7aa72d07852696faf5e8d305e85077450188cddd43d6c1aad27", - "0x8402f5173327a95438797cee3b107407e8b277759c942bf1b1f975dc63ab89b8c43f0f4ce2a11de6e5727e9952b8923b", - "0xa5179a16297f7a0913ba61d69879014b9adb5e41813ac33acb8973e2b43cbc17a2f9a7d98210b39471a47b534f0eea23", - "0x8f7cf3928b51b0b1bce18a34da935e7e2558595e4ebc50cc1cb698f0bf3c1ea0050aadbcec33786118d791218e1734b1", - "0x81552a8927942591572429892e5a1572c8bc4fa7901395a5a2de3ce9f1ead122e4e5ffef6cc8434b3b18af1aa62e45b3", - "0x8999a1bf4f22fdc884f9310e7a3f5baa0d32c04e887c51a20736cff3216f3dac5bbede43632d29704536d7f275b0be9b", - "0x85d9952816412a890a3e479025d1c0c8858337498ae28731ae23332c16a788cfe51fa9836bee73d03b253803096100a9", - "0xb6a736447acaa6f4d83998973cd2bc3e12d34c6c076880e6765513c04087eeee5b5dfe9394c120a85bec8fbe127f1f54", - "0x89302db4ea39662024c748ff5b22108c0f34850f0fda476051a89a3eba0e02a2294a4511666987d89f3b3bbcc334fdf3", - "0x88ef018d32e6b379cea9ce35d1c12af698d8241c4c7468a5d558099f5101f04ac8e49d09b6bf031a811970faf02ed0ac", - "0xb33afb11f73195a7132430dc3961252251aef42150e9aa63a8c7cae724f467543a4afec289bf27e10ccabcad2f7f812a", - "0xb706315deef0529725fa6c4520e5d320a028b4607d47fa6c4c8ca3558afd68ed65dc072a354d263d20405bb13ca119f0", - "0x8ba035d75939c1a3cfc72a9ad3aa4ade105880345eaad9286a182950368e688a182f6175357a2e62d977ff7ae08959cf", - "0xb47ca04b80107eefd3a596be2e990f5f021cafc6b7fb64cbb78802f9bb7bd2cec4f37755c451bb1fc8786a076e90bad9", - "0xb6fb1676fbdf6cf95add7173c264b820042511e34dbcafa76273ef5e4500ad1208b274855985f0eff5196e7200e5a8b5", - "0x8c7493894853f4e9fef5a0143dc134f03eeeaa10c80c5a72afb12f10ca5207df1c7bcefba6728d61f3f89d3978902629", - "0x97d14d9debd4228be04f2352e57d9c8138d4e40228c269100930e2a3f6eb6e66f2f99807be0c9857082ff8b9a089049e", - "0x86e327360a19f6ddc8d0362cf92fa84677737064a94d9d0c1031bae92b85abed36193428199b0f66720be0d6edb0d28c", - "0xac79bf758fe91d47d1ddfba62bba87f5e64d93f82309d4d07b62d78ad6ae95908e1989299db99ec52c5ad8c8f3d7132f", - "0x804712afd93328864a52a9f9ca1ae148de26fdec7d9f51d1bf8c0385959ddfb639ae0904c855180dd418ac21f9a8a7d0", - "0xa789e15cf3c1e911fca7f6759a2c5d0a281c6ab744f29709b8d0623c1fc197ed9bf56b89fb0953baf261ffc4bd8d1552", - "0xb738474bd1788f326c5145ca2a468d914ead6dbc338680f62ee21b1e5fed49fa501236d70dce5363a72147b0a8974c8c", - "0xa34019db5e8d5cb680a78c1692978ce0f3f8b21c1615ff65f3d103ed5a1e32884680c90d1dc18f0edcd8a506b1003806", - "0xb1b1f26ed57a7bf77257e2ab1bf314b22e47f8a4f4c5cd154beaafdc34b257e9b976b26c8d9f1508498b6e8c9e9fd2ff", - "0xa5f645d7a75741f536e5218d4a38ac75f5f2d759e62bde2c9b628a4cac299b477a833bca98010b6c2a8f30b85f407250", - "0xb3947ca7df831d68107713bbd52fa300411bc14362c37c2617780f5d621e211b8bcf5fb7099d903748420818d840234a", - "0xad16157ac24899485e7eae71eabf9e4262d01b8f8bde30d7c79fd00ffb4c347d765bf2b4a30c260c9fe2e5153a3e1c69", - "0xb1bcde4588570576872430e704a64d7130f921667c13101c3fb771efc1e0bd72c4ad4b8e35cbb98d709062257f0ef59f", - "0xab45dce0e29201e79cb1da61cc4353840eb8b47db99319ff7802d2e18f00e3fa8d5e70aa6a797684b4a6741277ae033e", - "0xb6977424f2f5b968a4eaa9dc1ac1018ca51e6158a6a4c189f0adc94ea1c2f30bb33c57960a5c972a1800cca2650e2f6e", - "0x899f48fedeee4badd5b69632f78a244756458529f27c75d05e9c54cb29579abcbe4ff7567445ccef96274c8cf5b7d31e", - "0xa8225095071acb2610d28d9ce2645280a84c702f5f5040df7a4134de1144fe1a1b07d3e28d4ff5e2517b4b2bbae674f9", - "0xb48316873f8245854568a37ad9c5fe9d5e6d9ebd60c9cbbf9e6f63c512bd8568e3a736039422d21d383378c77d8f10b7", - "0x8b40afa65e47ba365e723b9e24bd4a13335455e059346187356ff9abe99cf71eae383ee33bc184a9ec17b32d0800f158", - "0x96c3b7ad1e31b8d4ac0e14358655e21e687beac6f6b7b48dd3750641315ac7088352976e9804b9c625a712f9d4fcfc4e", - "0x914dcb36d621753286340077d16b36bdaa1414eac7a8e7ee84404a37f8fadda837bf4c5a932e8b0f3e8e673832e9b3f6", - "0xb20a438985a4bdaea41b98e831537027f4bf19ea9c1ac5fd37546eef117cd3d41b9c1b125d2302ae3d41176ab5d9e9dd", - "0x94a4cf3cc42d7055b55cf58959a7715232a273e66ec6f83fbcdb79d01769f7e6b1e328f6b0a910d1f8cf7a5ba4934779", - "0xa62b07dc466c2f83dcac7fa98215ce5bece548164e32b4bb3aac055b3c0aa68ef5cad58bf7d392e3b1d54ea6f0d9f0d7", - "0x9870784890da6cb0223daa367163cdd41ead23c300d246d62debe980fc3e7de0b42576309ae35da914474b8ed2c5acdf", - "0xb0f28a74169391fbb179ffe8647f3e6228e75b409c49ba81d34ce780b12d408d2db5968e9664b9de6a7416d2f6d1c1cc", - "0x857697b0222cce1458ff591e1add39f5632cb3aa2e589a64166738d8c00855e354c2ed44c4cee8dd707188790fffe6b1", - "0xb3566bb224742d0871ec5d15ee890755d7e6727aa7e2f374abe965ef8380b49422897545e2cf8fd3f58bc81f05abf173", - "0x88271995f9c647da82820b042e59011121ac723b4d0a2e461cfc1351d89cc10eb7d18830adf1c7b9fca55ed3e910aedf", - "0x863a43548db29c9cf35f24c1d5f7aa984ba21bb924dd9e09210a1feadb1e0ddca98df47e970c230879faa5e7434b118b", - "0xaf5c71b27157a2391247622a5029ba11d17ab4329001b01b3236f38d67ddd6b8902aebb48ee9c963983c16f6d8c53d26", - "0x97abbcd4fff0d1ee2ea777788cc146c1b32601fd93a5ff9908fdc2de225b954d8fc0c9874c258dcb90ecc7fd169823c3", - "0x94129bc418ff5d00ba3a3389b62986fcda5714ad25d25091db12a66e138a35a9e38999c4cf38fe7cdb1340c099c467ab", - "0x8a785f303473e763578a5bff75a069764e346224fa2dd8ee3105ca779cccd5939ed8c21f7369bab9947a4ca92d3b605e", - "0xb37d1644a00401b213f29191a238f4c9c32ba550db2ab3b4c9d1f02021a8f6972ab0fc76d0bc5b9c6291d5edb9632658", - "0x8e42a2c87d7feadf1a2dad9dc822b40029eeb8afb785ce574a340929c4c6ddfe4d750bd3a482e62bfef1bdfdc36f5bd9", - "0x8837b0408f48c8b975ae777b0516c569dad0daf607da51f187bc2c67d3f67315340848fabf7ca78dfa46b05e3fe33005", - "0x96d53e8e9b14e602dec666fcbff8ac2a7ca0474605b294365bab5f5228d8cf0a17a772cf2f37f7de3607f7ea6127d0e0", - "0xb286888ab9afd161a714fcb1954f6046754c1e3e398cf639bc215327057ae68ed69113481da88627839b551cb9660be3", - "0xae5747c882d3ad685e481b0b42907f0934a717ef5b0bcf110fe3125d40628959b879186464f95bc4a69d90754034c141", - "0xb1ca38e7b1f87e4c878d4b878afbca750fdc2509f625a06871da830c1f68a6cb20dde7d51ec73a78454ffdf54451ed47", - "0x82225700e9b32f416618b074479302629114761fc721ff492d792d4d1a4d6fec887185aa81395703fc8d07a67fa4d87d", - "0xa132ead3cac6087bc7bf5153f29ea858a115249d779419e5c52f4d1f2180f4631caf45ab7cf90129af68bf67665b8dd6", - "0xafd4778ab2921b6c9c3d3e2d8ab7e33882f7fde3c8f66d6704f0859f3bec468178eb25a103b78ab092de5b694f2d2ff6", - "0xaa0123ab3e8af146e974c9fc72dce94554cbab212336c8aebe618ea082c15ef02c6b47b6c575d262d1cc6b0cf2d11f47", - "0xa5e35113393e82c0ff3a39afc99a60f90993d5e4995e5d21a9a395ae31074ed5e2244214d4dd877c3d89e52fac6c4252", - "0xb2f476cd5d9df15e624713f22716ff01790f2fe16957b526677bdd3d203fa8af98ae58daaffca10f84a7c61e29ba1d79", - "0x82d6d062828337677ae19ce13d27ef45ee55270a27e674946c7c1c762bf43af6391d77454dda4dc613b519f4cde43636", - "0x8e86b1803d4ee07791269ec9175dc3d3b595197c089551e5bec3edc55c77532235e64848aba62e43936d3e9438635f5a", - "0x845b7233e40eab725c241853013d1884d782457ec188ff7ea535926c36da0893882fea2c9609f96b6d85392471b71d2c", - "0xa2090ef73e125c0809f2bddcdd7b74b4f4eae452d76afebdf47691d2afacd1db7c6a3032e9a4c4ca744bb496258b8ead", - "0x98e759616bf468bb4feedbebaa8df381d01cb4b0009a5ca5fc980426e160698abd6fcd2095cf964eca6f8d92fe1bfc42", - "0x8a29df48ccec0ecb8b3d904078897d996ecea1d2db6b40b79fe51bc5dad04358d7f7edb6543d7d1cf0c1f54544c3d85e", - "0x9422e88414d88e5d84b17f9d2f1c50fb48e9c5b8de215dcd7c52bb26a6ea71cf92c90f3004c4fcb34040eacf5b60b06b", - "0xa643123915445bf0e528d36dd7f2da9a3b993f93a7fc9f6148049fe14eb5a0063575d971ec955aeffbdce069d0bc2937", - "0x81741f92a157bfe12aaabf0d81121e5a8c7df2dae86f5fdba826167c4558103363c653a928babf4ad7e3e80634d26375", - "0x904fe8e258be2500bc5566c3890a9372c9404935ba19396e8cd30289cf02bda13ff3d776bef56dd87ce57aba0a8539bf", - "0x811997c1d70feed33ae3684eee512a46ea91400b39638d405a8bd6f1d0169706f48d1c04beb1c5afc5b10879390a1a0f", - "0xa4fff30378dcf1f04eb97951b85abc0f5257b9e53b7bee814a5acf060919d73504db14d55edaf54e4030b4c1d7278c57", - "0xac84f2568084ee7a715b2387e3fa3b15e6940a27ea99b4fc9889c828179c55f29992b68d719323c2ede8ded3a4e16553", - "0x8fa542c15bd29bcf72a34b3c56eac1e7d4e4f3b15b82672cd956d23a9b9863233816ffbcc6738a225c86d9dd13d1c3d8", - "0x90d94517e7f1236e49ed6903db72c0de3098b42fbc76afae7abc1b09a903cf92cb1bb6a6ec8c29331e05b8946c2e9e2b", - "0x916c0d6b1fb7c74c0001599211ca37812f30c309cb6cae529c876221c5e7187876d37268776451df2aa44f91a3a17a11", - "0xb9ae0c4f0c00e8b07b489e015711346caedfc7cbbcb36acf3a2ffadf2a8f11118f93cb70181c27443d42634b2f2f6d90", - "0x97a51eb35da8b67e82d55fed968ac9aa42cf2d1348ac146d534344c4c7535c29ce25dacf9901abcd7e8c43a83e34e25f", - "0xb2f035822c552cfe3325da99f28aa31b847477a644402d43379947ee219fed751497cfffd3536c91f2474a94bf758203", - "0xaa2fc0777c208a2efb2884dff33c459f2f6c9dd4cba360a65918c8604cb02fd493c8e5d26069953bba56039f8bb694ea", - "0x84c63bbbea15e06775bd39f39995afc613586fcbaf32c9ada1410dfdeff09b8e7f3dd0c99b23c678ee72e44543ee6443", - "0x8259332662ff222d4d2f817bb94033d458e650e5f6e2c31ca84c6f3a4b3d2e8d1f40593083337a17193cddd960ea86c7", - "0x899fc292aafc17d01c06cac50a78edf1f11c8c3253f4b32116625537351a1e82ee3cac67725973e3563fdd73781985b1", - "0x92d3b9aab29854556666588d94c3b49d159c9ba9046487583469ace7a6b8ffa61164839dee24d28dc2fd36b9387b6225", - "0xb54f62077e1e42e18586a03b3d3fbe3fd680dda6988bee8aadc95dcde46c215167b261433d6cfaad8e2b3b6207662af8", - "0xa6c021aa10019319f944f8a77455ad5b153a502dc9eabd9d306be3830a4fa2539e3cb380355953c3581f12348b919214", - "0x8cdbc2c995699cc83768dd23383fe492a1bebcdfa55fc4b9d1113e510a6f4432ae55fd57db732eb56265dba6ad452c46", - "0xaa474f1710bf6556538fe389694b4fb737713dbbc9c93d8a879dd3aee8e004c2441dd14b5f4cdd4a98e804d031ce00ca", - "0x95448d62b1503e71d47ef4f5a01c60c938fc3cfd9280d7b6d3490ef331131130630425adcc53c9c96f262a80c3251e4e", - "0xa4535757aedbf6d7b9bbea99f4bb7bdfd1c99d5d976bd8d4f8c69ee09c9902ea81884d8b6f4fc084e12702fcbb9e4b3d", - "0x87796bbc38d5c2d9a56a65ca91a40530b57fc2a768e9e08a2081734bde163f39e069edc99e87a84b539606164993f30b", - "0x8cb7647e60f023066c4835c956186b9e997a7425cc38465e95be568ab448b7303977c7ddaca73b78f6bc137f25e5e020", - "0x90584dbd8f672a349682effe2f775f2bccb1911b06d20cd02f3a6e30311c6678e5082ab87ee47af72e0c064a43592bea", - "0x8886147e87a552c74767faa64516438d6473ae275e72b4cdc174825696a4d7878297b1ecd0fe1a62fa4559ed232e9e26", - "0xb739745959c324a62943a225140daa51faa8e41c8e20ebd68d6f000351101a89341641933dcb2ac5b3a45ebbbf7fb26c", - "0x814f858b4c709694472eae1c82cfb7370191ad6d0cc5aad69084fb8e9d81e90ac2fae52b4051af25f1b806c273f61e0c", - "0xa00426131acb84ee08684f2fc2a3ef01290e48e6b5f96bcb0459adb62f4190a4b2616eff2a2712991c48adc551ddaf64", - "0xb37a1e92b72e3ba42b79dd997bbeb031a392e42606254965597ea4b8a2ca51f8c324619fc2b9f886e17b632ea3bee629", - "0x90817db93eed264f49445d1d3a14ddc0d5ca93191b6baae278b4c48231a56b25725ba6f7ac0e9c7326755f0082b79587", - "0x95b7f470ef1630dee768698a31398e8cb407df3b651a15493c38f6be6c7eb387148124a2cb1fe1237117617017c12203", - "0xac49be639391aa5dc08e8678cc690ff617e9a0ab40166285f90c2d889c87ac70c455a972e61cfc339db59be4394a0ad1", - "0xa6f5a698508f8047edc45bd605ad4e88245de20013e7a4e51994e99fc60d81dc945504b24f23f7241f28059f4b5d6756", - "0xa4d30a6db06153074871c6adb0ef4e562c1491c1f9841c110359dc41a3bc0bfcba3b49fa53c29b8258a814b8ba1ba328", - "0xb25a500efa7d38f797395cbec660250f4a00d104459cdf7a15b541db3917e26bb7568526444d469d363040fd094680ab", - "0x8444d11f8a0c686e2b22642ba1b28cc556ab7311686028e3fb4040fcce22959b7b6cf244b77c711ba86e350e17411823", - "0x8ce90bfdfa93cbe58421be78e30e471b2c6e6beb1f9b3f85031cbe269305e18d25a2170819f2699346bdd735b6f5d664", - "0xb73970a3dc993e28b71bc236b3391acbd85a8cc622b79e669109f9d3ad7ce7a01a8686e75d85408c54bb70ff9771ca80", - "0xa64cebe05fd027069a18f152a18be155ed65b6b563696e395e045c8b2f0455fa75c2ff41c1247e596451b36ddf258460", - "0xafec84a7a480b09cecdeafd025ee3ee02e3b3338b02d26cb3b7575ecb895057650f0955978d1d732ca2e6b391ed97728", - "0x8caaf53038bfad6e0651e61e9a44a39027d456ff3ea46ee9d8e190698d5a66938d5c5723dd7bc75f0ddab660e178383f", - "0xa91607e39108d2540b4b5c9d33d96328f56ce9574ac9d1d4a81ab5c938443c3d7014e19f77cd55ef7be0a408e44efa43", - "0xa3f4c6629a3c0f34ea060a8b976096e6fd3a91c24d2b056e9b6b60088bb0c706e25dfb31079f42e0ec031aa840f46afa", - "0x96b9c7d3f47ec35ab0270cc57841e9f3b3f5bce3d26faf6abf6cf657b6e949ce0bd1ccdcf9d490beebce722aea48caef", - "0xabd2433b4003b7d861b35e99b51e2eedaea4831776e7c289beae2b561ad69a771233e3d6bc4a7f869d0744c5be61b5a9", - "0xa989e5080d39d4031aea86c03b77abe069ea9b7fbc515c6a79c825eedd6a9bf6a0ced1891eed20edc605f9e25a691f74", - "0x93ca5b311d28e4dfbf4de84a1e1530a9153599e0853c9abd3671a1ce04995e00f7d3092895461137fd78c72d24a99494", - "0x8acebb0309595f4eeb990b7a1543f0633690b7469ce89884d5654a7bd2d2543f09232693a04e1e1b445e6e0041c8b242", - "0xabe3858cea5a873a7576d641571965736d55d46f9040fec219803740dc2a5b43c72689e94c9b61d3c3c44dd3a821b694", - "0x947cd395aef4faeca9b78b6cfcc8b2f8f361de884b29181266fd95b21ca6176e7944058e20cc77c7757fbca4fe445394", - "0x8c2e50234c75d645f3c887693e2439ef42433eff328111b9c37aa3ad5a3b21678ee44ee2959a91610006b27a0f5363b2", - "0x967253e02e34069ac676063aae9a493bc6d52b8bcbf1da6243bfeaa9fe05f8c840ada0a282df9c0180d05eb866402441", - "0xa16a4c9a11686a5294d8329983c8a4aa0e6e5ad0003ab319b493842e8d072aaef45c3335d9a64bfde6bba120a48a72a3", - "0x85187b866fbc72e5b42b91d76e7ec2647b93bedecb060b7475236d7d152d17f901c778b704f7c2c1d3d269341890c233", - "0x83b192d925e3f4a1fafcf22cb48083b2f88632ba93c1d498539bbc4997f61f74a0a3b8d4947253a0daaca8999c407b87", - "0x8338eb3e7f549988435f4f618f4ae1942c6756bdc28a80dba7ccc530bef161c0bbd20f77c8c4d63c94e60bc12f7cd387", - "0xadc869c5acec5e58459eb804c2141e03e3582ce1fef7d40fc1dffa3ca0f923919e291a2ca4a60129e2a470cdb395be31", - "0x9279068c28840f2c34e48e9a7e7e9406907ac14bdf4eec7b8c28ebcfe16a18fcb047015e4309f19e6fd73d6e6c454157", - "0x98c4fb637a868f161f2f4222f93f3bdf13a61ec1f4e4c20435c150fca1bc0c01c790da91afb6227ed2a6aa540d70366c", - "0x9209fc7b307f40294bd9cce166277a7ade9c64277c600b3ff09579fbfffa471a887061e9cb5fac97c423eada30a8a52c", - "0xb1d63569d5d47d052f3a6e8b2c390bfac1e92098291a2adb209f20af875ebb2a8669533155b1d15b232690e26d399ab2", - "0xa2c975c004e69e7b0f22636141d34adfb2dd1396c7355e95fcd0493e931eb7eb99b4df0f0f202945d7bf837809a29ed2", - "0x818f48e65e337913c52e9256af134f4311be84dc332e3ac4cb5ef659b9c6e9cb34f04b0bcc0e2a3a574c9c3cc51d7368", - "0xb92b63d0b363a368a348a4abb10661c38ced99a3132afa6cf002b81e6cac26f862c9d0a6886aede555d7bc453753cd37", - "0xb4051275cef361cdebd254115275b0b86692d3802241cae5e2c75accee7df98d3165cd1de86226f382e736b12d9dbac3", - "0xad89d85749c23e045bcb95c3433eb8038139a51c8edaf06b5cb235549a2f9ad17589097ff8a350e934c8662a8879a3d4", - "0x802010e6dbf4265cdb5b5362c0b197317f2435253237561a3a7bc6766f98b129ee06d370382998ae70080624fd65831e", - "0x8ed6a5b601a5ee11e983035f3109075444b063aff693b3601f87c0d76d2ac253459de48d0fee32330c3785d38eab5cc2", - "0xa6c8bee787c4b87137f70c2c54ad3ad0955269c7ea57ddabb1a215e613e250944cada7f241430c0ef09f8eee29fadaa7", - "0xa3fc6a643e1ce110b08344f8913ea7f8c9e44bdf1a02978df8dcd3671d9b357397df9857fb11ba220521d1ce40064ee5", - "0x94089626bd9c81247f45e25e573bd6bf727a0e1a7dcd630dd5e661f65d4b6f35bdc16b64da648dfda404b5eab39d9152", - "0x88362a160a95f01026a2e52aee3521e8496340f96a35351892034198740d8b6159175c60b910a4ee05af488dfa578c8b", - "0xb55a5b875f5594bf41949c212543517bb1ce34db3a896f93d0216813261aa95f73663c789ea0ceb2bf8815255bd328ca", - "0x8f9acdca0158df5ecea4d574e0ef0c256ab271d9d3d3bb4100761f5062f0a1a5d2b8a23685097a1a2b2a08287a2e2c94", - "0xb6d4e3bd49a17fe7d929b41fb223eaf93141453f7dc233eaa74424290014a63ca6a099174b687048d59cefd41fc720db", - "0xac0fa8aeca20a0b4189e96c57c85a2174338550855f9d0ff0c553e773a1a1c32fe3f8db7c8362bddf601e41380c9177a", - "0x82f05710f08f12b206b2ad6a2d06161c884b2511ad90b43fbfcdf54933c2360b7c85dfa4f598b5bdce8809a803d483a0", - "0xa2ca711642fd498cfeb897e4072d13e43b5cdb2480449975188fdfbd4b471070cad941af03a2dd8938d3c376366fd199", - "0x90c27a1df934339bd0821cacaac41fa70496900044aadfccf6e5fe28ceaebae5cbc500fd6f2f88c5552b7fafea79d06e", - "0x818651b7c7a6f691fc47a61ae4960bba7239007e14930f3a8cc9c95dcc0b03643047671f819e30d89c2d1891640fc13e", - "0xa88f01062ded714e7f2f1523644222cd8e8cb8e535eda88738f4b4b19079f4f7be664abedcdb618ad1de3e74689042df", - "0x8174282a183f3f393667352fdd60460d2199de16752c372a44465f8b71ca134c410d1d81f15afac839748447875f8643", - "0xa358c3e53dd70e1a608f36a1fdbe225e28c13b5817dba890ed8e82adcb7ae86fa68ff6cbda7e02e8116c11587ae1ded1", - "0x8aa0bc208a84d5a58b0206a8fe5ee3c8d224ccb86b11b7c9d924e16b2853a6c3623502dd60b94f8d720810e0079078b8", - "0x8bca870eb6cc5f7b5f6b84f88b49d9a3994e61ca3f2ad963f28f925e58430887f5362ed4bdc2a2a38b5fb9e774a75cbb", - "0xab86840fe84b1eab81675eeee17f85a500dfcc95dc4872e57b39919ccc71b702585ab9ac66146d264d2bc8fa39338a72", - "0x87c46966a4bbf2523dde607852a40b26cf3431d0bde9b2c609997c0f29c5932d28014026862abb7d4107b28ab8e2ba70", - "0xa91666a8c846a9944ee7ab243ab535e4124ca8bbb756777609aad848527b354060c484acc19c333459c09012670f03f7", - "0xb7145784894c6df87d2ce6a06cbaa713e46097b5f83db60e5449e62ed5bf382a7fc3136e5599226a2fe7951847527c4c", - "0x951bdbaaa06ba8b427fc4ec6bb44e93e70692bcef6369fa06c7a6882227d27f09465f37f0a5868ce43ade188a5f37f8c", - "0xb69662dd5dcc9ce7bf24be8a0e85e80c8e5af9b030e740796f91de548569bafa2fbcb19d98e13900c76cae3fb601a8ca", - "0x9630a7eb15718a2324518f78f26a71e3c801a8e2eab3236be7623807321c128ccd79c74ab657ea8e115d6ff3078a6887", - "0xa2f98c2084f8cd556cc1bab19398e98921ef56f6445f63444384efe5d7c895690c57d0d94cfd24e99f63f5e31859e34c", - "0x8c3994d3cb76fc6ac22ba2049ea4547db92ef78f009d24f08695b282c95e395f2c1477bd52d3f569d64551aa5e259b5b", - "0xb58571076faaaa547df9522b48c684b310500850339d79d2349dd8211bc2c8307d13cd5bb7571e0b5baaa013b502e410", - "0x93e07feb14f691e66be756b37467f290da9a6677b8ff565964f010fc20ed9c58d8c712c4abaf012c787bbb22cd1473d9", - "0xb4bc6159db1578111190b19aa678281eb2fcf7a82c7f699da7473720493e66e0ab54429da7af24315ed9f7399863c954", - "0x93cfc98563f25b45c15a07780ae0a38c4ada52ffc1350233a3b45417c16cef92e7926354b761d0e0de55aea4c1314406", - "0x820c37c923807790d77d2cec39f0eca63fa3ac6eaf0a1978522f0b1d293a5c46af3a0b4ca542cf39e796afc1fb3d7195", - "0xb87fec722faec6a739355fd30a2757e5d184c07b5bbab8581b74eabc2da413faa6d11ccd65cc93f886c788239b1eefb7", - "0xa183bac7f647a0c15b14089879a8aadb712f079bcf2078d3c65851137a00dd3ed7e47263c064feb19362f98180aa425b", - "0x996233b2010c20e0246295735b6d5b3e932f2aeaf0b35aa3dee66b6296f39e2e7ee95a7e1a15838ff3389ecc8052e315", - "0x85c943e09a6c77e15d49ef4fe57d89744fcdb705ca370cdf70b3d84aeeccbf2155868f6790333f88fe36e08042ce195d", - "0xb88f82b35ae14a3e6fb972c47123236bb7db08b9f9f3828033fbf5a895b09b9b0de423f1caa04b3e8e754409b21f3a52", - "0xa12c957409b6dd335964532ce3c045aabd280188b4d6ee809cef479e51dba030cbecc86b0ea8777cc8828c087006c5ec", - "0x87227fb4299efa535240793cf0079e952e971a18ee62cd71a62d6a5db921da669c5d8eb1bbda318ed5f3b03b38798a73", - "0x84b5c7585fb1c98d031a0bf6fa8ad5484c7766025af552cdd72e7ae59247deb845f8678862c44ebe640a7333cef8391b", - "0xa94cdb0f42ae3afb4b1878f960669bd99008c7ddc24f2fed45ca521c60472e5587fa9bf97b315efee1f74619a4d9b762", - "0x969a9bd21a6a90aa30fea44e397cc88118fd5abeb68839556194f9ab0076806aa320928a8ec94a47c4eade15498f5175", - "0xb2fb215bbe7acc3baa04b0aa9be654afdc450faabe2702a0c9fa760c9e9389a58aa5e3a4c6af4f6f5c934640d90b59d0", - "0x8be6a43071464e6c7dfb0e9a791a658214c1a95adc88f144d8349ecaa0e76b8ea5f88cfe95e82694bc170e49a43ec4cd", - "0xb75d56cfa1f3b61428d24784d51dd65b78b977bbb52cd71401ac7d3c2643f3dc097d6e7668104af402cf7e7e6ddfbaaf", - "0x811005c49d1be377ebd2fd3bea9771089a0f5675c55e9da5f31fe13cfc9d1ff8520f085918279ccbdb0363eda16f8056", - "0xa487f7000c16429f2b7bd7e8bf4990bf26f666f8aeb11a99114d33e24f946cb0e3e025ec8c0b0721f9be101504c8a1ca", - "0x99b72e711ba7b97083976b2db7b97346000a78bff9b20ed910eaad02f6c03b45fb3f0f1217b328c3e2d87b481eaab52b", - "0x828429d387a0b83ac8e377b32db1c893a4555ca253b8e3159399cd053c5de726655a2ad39348c8e7ef11b37b0bca78e6", - "0x835de10c73da7f0c07295a3306ffb18991334c86e5fa4c6df2d8091e8989f6454c8b43721b13696e1f665480a64284de", - "0xa4ea48f0cc5915993c83309df99247dcd7df4c15c723d168759175010fbe8d84adab8393707cb338fb90a6a77b69745e", - "0x9976bc842b06ffbc5afb309eef8964908802e9a5c733de4a8292d5d5773ecafb6daeecc63a8dc8847d76b77d4c3915ef", - "0xaae89156b013e4adb4bd8e7b6007937f0ece09af077fd407798e4155dc09a129d44fe8f8b5f6cf6b3c84746181d7f4a3", - "0x81891cf2d70a8c326c6870a8158edb79babf302b4f9d51229bbafdf038cee39b97f01694eb719df99a62478bbf909a85", - "0x97bdcb526108ef3cc2205aac074ef3001d528f56b4889573e0f4a3a2504232adf23880f7fa2f57bb787ff30b89599da9", - "0x9778949a95fc663c742e70596faf91ccaf8b5b93b78bc2d4993480722ffe10bab3c8df3ae50535d258b6e749a0abb86e", - "0x88bffdb927dd88c1ba3eefe7da3fd6a42ae494bf282e094893e144330cf08e3f518f47aa1dd76d6f249cf83e6bb9d4a7", - "0xb53effa345fe59917f4f1ae754e9f6b8fec9bd34cee85872b3fc47e52fee29c418b9406aa50c8d5a2e853d6f2056a49c", - "0xa616775e7e77e846066fcea413f0023dd491d8176dc450b0941368680571cdd236f0f83883d268842fa61dcbf3e4864a", - "0x8b5ae13dbbd07ad19bd2c7bdb48eb3c760244fe0caa73d28df3f0c31f5418f8b06862526f5a84bb16b2a92eb2c7ebc28", - "0xa62294830750dbf43ea497155b185d611d75e767aafa8c2c30256f8a4875b6fdadaac429e8363848b39e964cab2aaabb", - "0x94b6973fb87c2efef5efc0e7dd7ecff5ffbe76320fed8a20d730f9e3751efe9e5db39981f45477ddfe037e18cb971930", - "0xb931b6f789947b5298c258c8f0b221ca927c447f190f0d8afe2f18ce9b90979eb3247e77e128a1d6c57d3bf5523e787c", - "0x968259d9d001a08c0329bc19b2438b48dceb5942bc6ff9892d78fc36014f1b60a5ce7deecc7a808e41aeb4e26143aa41", - "0xa52c1906f103e3fbee8c12fecd93f7b7d6f37eb733147bed841b32caabc880fd6e72884380a3cf93129d9800ee7877a7", - "0x969dd12f0f6ef0b868e21539dcba5dc7327033eb546570f5bbf91b13f9c2ba6070da430538c40bc53a2ace4794514038", - "0xa853a74380d78710c212bcfa92d3f9c433b8ccc6936356f3bdf923b8e420e1017bc530ce73bb8d04bf7a25b20594c753", - "0xa84dfbbd3d9f409badc8ac2da5a0db98124df9d01bd71b1cf5b2b9c32866309304848a4bc1fcad1130bddfb9636c1b56", - "0xa9599f55173e77dad54cfce6ddc77bc48588f36b79a98c156963a2f5397262ae07634a98ab9bfe1aa6357f78aaf89d89", - "0x91e429b5ad0bafc09b5eefe600e179ef56f1ee045765ab3d5ecbd73eb201305a6de4382038b1350abc70bd1435151a0d", - "0x8785056b83a726622c565985e815847b63745fb66b138d24c985d6f42d5762c61ccd5172d4a3237222c881e5f036b98d", - "0x85869796ef180f500dae84f669b76a9b245e2ff4614a58d74820c22e439837b7d9866f480b72d88f44682be54c6dafb8", - "0xa118baf9c17d85e22ac3315f5ba9aa4e230ca2a031906f99bc43fc750a0f96aaa5e6774d1cf16b492726a37db7b51327", - "0xac8e33f32c1cd14c6de14e75f83b8518bf1bf6f0a70e23ea0e5a29f096e2992f1259a121bbccc5252b9668c605240435", - "0x97babe93e2016d29af74f776e167d82f1cf2242202bdcbaac4a1eba2b3fbd9e7ce57cdfbfe799a0f6a06a0e6838c4e99", - "0xa70acd7e1f159adf7381d3f3ec2cc42b56232601f18ee62fb650e13a80954cd06d39a57217ebf4d8927e28c910671ae0", - "0xb33ef5c10d0588df0b9d2d963912b294a2375a26bd25225f002cdc206a1cc058465c64180d348cccc899baf3d677033f", - "0x93086926eb1be21ab929b0098767611bdf1a853b6b67045c14f00714f806f8655be37150be1da05c5d2e6d9c66057bf9", - "0x8890aad532a6c9b818ddb9f1ea12d627010b4120fd4969bd239a9654a05116272d4cf783ff8256de337bc01f9b4154d5", - "0xb878977630f647a5ed7c99f59ca5eb653cd647277b899b918e5f667eb17b6dc433b56c2f3a2a18a785a4b5a9ae95f372", - "0x975582fadbc276c9afc4d8ef767a66684df5f56e898d2a8749cbc2763982c013e5fd0ad0ca7ebc67758124a609b59663", - "0xac45e154a651857f0464db38afb2fb25853e8bb1eb476df31908b13b4fc86491d4f831c0a15ed6bed0c873b3dcff55e3", - "0xa778d373e361753964a7fe4e1d28702c62a919e5203b941b04b0e74cdd3b4e838cd9b6dac3c39dd523f3227f1b5e6766", - "0xb1bab7994941f8de4235e2e188b302bba847c1618ebdec7fb782241e9eca8d32dd506d254d865e0319c66396535cc484", - "0x8c4ae5b346325f1d1609328e41d20108c4388bbe021361a86a1f9681caf1e6fd64896d72641ba8c984e153537317420a", - "0x8cd312c6a23e56657624d21f230a2c22d102badbfb2e38a8c067186abc5a459d0c78433ae7b54334591862c18864d7fd", - "0x8739d71181c5a657c6fcfee1df71756c3b6b8c48e8d97460fb64eb891abfd23433ccd08574a677fff600ffa5519a2363", - "0xad3c8d1e9eaa6f9122fb14d323318bb0338c5f9f29c719715cbeb15a2738493930542769b596098a5f505359c0314381", - "0xa6d78b78227f8c1203e502caab1213092f320e77a6e9729e1659cf81e981cf905170e99b56c4eed1326320acc6aa60fe", - "0x8e5ba0e69e0f08a49ea4fa28ce0792f7ff6c032844ceef11be90b2215940d4b0f3e4acd5e4b189b189b0a0ef8045aa26", - "0xb7b31957e7a85a640b851d4241c7b2f6af64f59ac221783b88c1b51cc4185f5ae6446a3c7137ee072c2eeb97c929d7ce", - "0xb066bb41c5818d6008349dc6015ab780633cd961b5d947062e14618c1ee1abfe42139c86b77e7f5be0c275fc3f5b8909", - "0xa6597158957e1a0af153183151fbc4c73bbf8156c77f7b409d0f97470b5e127beee6d9246bde770127d3e3ad400cddd4", - "0x82a6de6344e5bd0c5ca95f3be1ccd42fc974403269874603944c08ae1cd3ca887e66fc51ed61da8b7af3cce02f152e6a", - "0x89fd363aea11ddb2dc71288bb534a4a6e32feb5b9e4b88d132f4181f2579f8f8f39d19fcdb3d3d7ea118b9f4431520ba", - "0xb70c945986c8227d239201857e12cc7cebc932c3bda8913c82b62c872b18f866313738788e11bddd630bb5953492fec4", - "0xb4e3a1e8f82d988c85cbb58d9cec69bc63fadb4c1c9a09f36b5a61f1ee96baac1a9458bfd0f3250e1734ab3fc6c0a0d6", - "0x8d01d1eff496e8bdad1e6fb4b60e4bef0ada39a378c8b57cce2c115e611e0c4fa54f9b599e4c34dac925bc06e940eceb", - "0x90857123505746f7bff08e03b1a90f67051a71ba47b49e7bc63f1a8ec30e02a98aecf56465d3b4746aae166081099da8", - "0x98b9d3b7fe1d5449bf6498c30036e3f60c8b90962fe04ede9ebf605d07497f617d99d51f0f0c0c14381377de765ecfd4", - "0x891e7867e70582ade85730a21c19f6fc239760f76f8bbd8c6dafeddfaabd6fa4845f62d649b454fd3af8ae7028ee5f9c", - "0x945136f71f290d8cc6bf282b554cdf8ff92200feb7901987a1348f2d2edd3bd7b7bff6f57ec25fa302a27121a1a959af", - "0xb574d8379842467c5f3cdabc2da9a45e5a6083efd7298b077ccef2c4c3bab03abf1dc11507f4c896d745ffd41e4dd319", - "0x946fea5c1b1d840c10a5a732c7dc23c2bc5eeeedba6956f85ad78fc1ee4a95b309c0d4a4919d1f7155814e3f36fe322e", - "0x98befb2f7d0a860de662f28968fb6200cee5a33cd7a6c376405a9cc6f0718b90fcc5cd5b9142e84e95223a3dfbd10f29", - "0x8c5f208ca23aeae44a056bc21e04b570c13bfd49b14681cc085d5b318e62e4c088f0bea9dde52376967649919b59829b", - "0xb14478442f8e985618272d4c56d43a28e10112ea200b52fbb32b19a4d1eae965fd5ee1e9772416d52dc0e2db57d3ecd6", - "0xaa308b19a57159ff702bceeb69a385f91339db7e683db991b1414bf3af9916d8e52dec4c492d7e8b5a5a011680defc1b", - "0xa8ac18a1adeeaadc192e15b7e92b611c936ba9cc9aee01f03c2a236827ba527039c719f16149d7aa5fb297cd63878766", - "0xaa09af54f9a5fab6a61552421c13ca62f99fae212a9606f8a5a050997271ab6dbc763390bb98d90b1af3bbe9e8d9513f", - "0x96b8ce26b346a0d3fc99b6e463f0c619196cd85340b795fe1c1c0cd4f1b3a9f2bef5454e0bc7d51d75ce26f217460602", - "0xa3efa46273c392704ba0718a44f306edfea03b1a6be0bc1e5c67c62c89671c650eb8ac9bacc35372ade6bed12321f1ff", - "0xb484881108a43a1dbc16a6e7369a04286f896aaa1dae847b4019fa287c18e9d82c8ba4ad22cea2837bc220524a9a7a17", - "0x827b63d83e15ef61d54dfc365ed8a4f9e200d526884ec4b1d35032227245410ad7e1b1dd3c1d0ad48ddc4720f0fb5e1c", - "0xb513c3ddafb01b6189590b74d20348db74e400c106202dacd9ea9552ee2c642909a7a73ed7ab45a986eda3a0701be79d", - "0x831f4030463c84cc6cced28dfce0b3e6b6ead01afa200ddffd807f31ddd4ab93a8699ccc9d26970407628d521118ba6c", - "0x86312e006a800720329b82f6feb2934e2cc156958ba394278caa6766ee10800d2fb8907aa92346dcf6d389c4f66f5e1f", - "0xab6841da372a286fde1dbbc57cfe967cb4bebd6fe2ab9e317cb9f9eda04a4db0d5844ffa8db72eb9cc6bf311667ff6e5", - "0xb8238dca3f2be29bfc4aa65a9f59bd4e2c17fae78114a69bba1170782b993afacee3755e768317a923fd32d860f6a74f", - "0x923c1b60c052a3ed4736da7e84e52b0e9e154627cd90cae335dbdf05af109ceeaa016954d6e47fbfc40d9d5649c198d9", - "0x96a69d18c838512d95d97735263a8cde752b2bc790b3827ce048e177a063dd11e2a69b86b3184873503a68170b2ec255", - "0xaed7c3af469a93c22afb47a904bc70b7d88262ecdad48ea6a6c07eba7398410bf5a97a786beb11843cf40ddea9a37098", - "0xa6b50f6369ae558dda3ceb8cc9d99382a1e62d0d9804b903086845479b9381fadf8d4595c2f342307c94d09e02e0ba2c", - "0x89fd703d457580a76544bbaecf65f93d3335d7a22e93d14afbaa61e5236d9c8d8b005e403e9f5e7a103b0386971a5e65", - "0x8e909a3638208c8f885820af8bca6ae839128ce0d902a2b7b6f9713d21da8c943a7984d9aeee7fb104df4cbd1092967d", - "0xb41e2d7a1a0082eef43e886eab5e781bd961a83155d6a14d62756ab7144a208f4e4574d47d2ea094a7fb50d0ddd7a241", - "0xacc6c63450d124014a1db846bf578c44e910436c83190fae428fc3125ff2065d4c6a1165aea799b46093a86126d4c483", - "0x8dc63127435cf2f269a83024b562b3f4da18aee3399ed5255c295e6b80c357cd8f1887de94bcea29f84db3193570c417", - "0x8c4cc72a98d42b9c5807322f596ac0b48b08b16ec956ea4a70c641a16a70895644e5b14aee85a4046673849249115abf", - "0x992afaccf05d79a3147d2985970c1793459130ddfb18a9d31f3036c260790109c9ee6a19a812f5d55779c2facf87785c", - "0x91394d3e84649cbfe018d9c709604f6aeed53e91cd57e2b29d0e84aca3c413f1e0135c6bcbc70784dc8985a30b9f3fb5", - "0xa33fc126a8f9652c6905b1f522bee45848ce42d7f4c3a4cb3f6ce0e6e64c82de74e175c2ab6b5a867a8d42322d257ea8", - "0x962d5fb816010a14140767c2486cd629f7793b304a96cb82ab85a867bd9a407bc8ed44131f87238c34a1e4ba41adb1f4", - "0xb09048879ce26a34f56e1d4b2cbd6eb2a55d4ddcf3738c462329ba6726fc0a21b8c1bb55169cb8338b2debf20dc0927f", - "0xa9f9ddcb86b7427e268973bc7f3239212716df394394fa535b9fa5225b6191d234a44a126766eb470ade18c469a2d166", - "0x87cba6afb115c0b3a515b08cc842e7cc2c705880c82184174837c0a06e1687ef668609c2ca080840fff80282caec7390", - "0xada109548c449990dd8f1bd42c9cbf194a62625d165416ca469c334222446fad7a743b1f584ec3f20526c3c44d870696", - "0xa69a0c58fdfac715311dbd37c4464f0475120362234f5366ffc65227e8178e037ae91efa5a52cda5fe28243f47390175", - "0x98e726cf884c6f706fa25fe25be154afaecc0c3bcfe682a85afed225bb44ea07cd1767b4d9f2201358ef19770330f0bb", - "0x988ad5bc57f0621e3ce1256720f1161e785371fd478c467c39e554e2de27df5ab8176508658aa90ed7179bc212ed0dac", - "0xad0ff6dbfb397da51fa4d9d959ba5819adbf1a1ee31f68fbd62ae07a9cbce0641588cb1008dcd0056c17d74e83c7114b", - "0x94c703cd32b9f52c41b07aee3e3c19b8c2b4182da80487ed7991d568ea8827f0cdbd1e977d482dbc102c4de2058903c9", - "0x906fc2a06cda5d82445e50bf475dc4ff2c17e64c982539c26545798f9e4dce0bd4daa8d55b027cc0a8e1b26c3e45cb08", - "0xb09a51f22a9a24cde25f805cb18754e27d3d168e64db4ff13a202839a81c69dee8372b5872faa0d23fea283609cf4391", - "0x93c908f514e97502d170310bc495d02948d99eca51e71f9a4779ebabae1431e1f7ba465af485546a9fc97c025877df50", - "0x8337859db710ed7e276a47e90cb3912c829151cc2bd3dbbd4dd04acc90c9cb4870c44f4233b407db8240a93aaaf7302a", - "0xb13b16ea0943e235f8cb064d6dfaba9bd8dac67e9b6275a4c3f54a1770b9c905d8855517415ef6489843848108dc85ff", - "0xb28489f0de1a76839a898b8d2f302c95610f17e893a6e7f2e824fec773cde6f2b219038a3f1fa212bed98c97daa66d5d", - "0xaf13afb48d56caffa32f941ac5542ec2b7fc0d6dbc3b16e51bd2a8b633f0a041ba1e098d9a68c470da00e630f08f24bc", - "0x81465afadc45ec24825cba7c9edbb36858bd2ca8f8b0b9d7240152b58a411b08503b530932e7b6ec3b0f8612869cb244", - "0xb2e6b7438fb6f70b52b8726aa870f18191920bcb213a375817d100297b507908d79567d0c1780b3f25be807a8ddcb853", - "0xaa7ed2b0b2bb2070b5f001578efb3d1085950c19f9d06584a2d31e1c235e6d6d1d7f081ca6fa2b0b0616b01b9a467005", - "0x91a245f1aa8c8ffe03f7db1412e5466f0345196727eb8e6f98b80c01672e4260e90724a538d26b094e678a3d85f2dda6", - "0xb9ecde963c8176d6a726b129f229d803d1a6259533e432eecd7404703c972ec7296ba35015acb1f4b5ab2653a3991902", - "0x8cf535bff6e98f75d98c5d2a691a5d1aa645c7ea18d677d07d3a11a9cfa222a7b8edd048529d163120a5aca411161314", - "0xad2e51afe96dd0e942a7da5a37587ca1359fc17cf66ab70cf37ab70ea34f90054fa52503d6527e89e464f8058f5cde79", - "0x97337d62f83ecbaa1f402c3964dabfaeb279b916ca08430a61fad6c31d60087c7e3a9decd541651a2b6e68fb2816bf45", - "0x898b7581288bc7f97987138b7481d29e2cfd5255ebef133177d9060790a0973ba07de62cdf38568c336c237cb084b7c5", - "0xab53c0759663bd976de62f9f98fc82fa4f58c146b8a6a3053d4dad836c762063ad69a54d51b5499e9def86f8d4bd7ce5", - "0xb35ba58109d44c14be159333b999c1e471fb61f5ed48f9d2a6bc689eb045864f3fe88a6ecae12315183703e2b1fc1ae3", - "0x858a20e233f2860c24c5a3f4a820cac7544eb3ce91a2d8284f12013b13120121fea3c4f25427c3524a1e883aead429e6", - "0x965be1a56adffa51f5d80761327cf69656e6c37577225b36a34afc2f8a959d8799ad0ecc3eff4470d49eb22ebf8f198b", - "0x8e575ee39077bd865d70fca2d93473f51dbc99ef4f715f4a3b1d9eb21deb2afcd0873b4dc53035b77e05f52029f069e0", - "0xa5c670a73da241f5888c5cb44c27eff2b8ad3f491e0b128e5f1d498aa6d76640c9e625f3c5399ad8e99b666e4b2a9759", - "0x920e1524255b03cbe500edb230696c55b7774963535c063121c9e9987ab74d504f2f1cfa14ba7ca82a6f66745fb0b392", - "0x8a0bb7cb267b8e1e0cddee208734632b28313b3ad89f9c2168f341be5390bea3f09eaa51f8923b87697799a53201dc26", - "0x859ab9b3cd602e78dbee8d8d5c3a9eb4270f130ea4a1b417ca5612be753d20106cb2724097840ca8919a9a96e73f96b9", - "0xa76126d9a997fb0e7e2b27ac682dda1c6b99067313371387084be1f6e7a9a59bfac395e91f089e14cecafd151674a446", - "0x8aeb466c58e2829790975fa08dd31f18a51a63777070d2e24605adb1a77b5e0e5c5e0bcb483076d23a6fddee5f402f8d", - "0xa8959f312f2ce0d7d974a4998bb709bb98ff6456413ef4ae9bcaa8d687b8b5ecad91414bce8f704aa44a83d8a0c86462", - "0xb9545c8567827fb28d070624579702ab96af4f06fce515ad880893b97ad9a374c4c91d6288e2a584ef14b1ce4930a6bc", - "0xace41f9c2756ced611da16e21704a367b122ee1c2feb83145103203ace1a5cce0ebd3bf295caaeff05281672c92574cf", - "0x93b90e75f56601191e3b568368bf1d24f97512cd42cac1da8b42f0019e07fa04cd5f835b7e9503fe4702632df27ddc19", - "0x973c8feba289eb473448c32b141ab4a6f861222626b3f2fa265a431a54675dfe5eb479100a33c172ff935464d6e85f90", - "0xa6b0798ce89512644490d65ce3d0441ad950f9a25f6fe2c9a766a58b9c8222fa6cba753f314cc7ad6b6e3da881c67abf", - "0xa79c560dfa179075e0d1506adf5082318915c15048328b15ddca5748ebc6ed8b10fc5d7a50bfaf8942cf9ddc6912be0b", - "0x8841b34df170519d07edffc4d33a0e70c518dcf53ea8d0a9f13563822312a65d16f99cf596bb95eb0daf85435d4bc0a9", - "0x88527539258323edc2c296dc742cc26b9a4a984ca299a81705c702a425ebc7345a79b4df84b2d440a1e73a09fa18b5d4", - "0x88026753926a068e1cbf49a9a0fa545846cc7ca9abc0778e44f5b7510c1b6b73e9a9b1aff754199379522b2a985c0881", - "0xaa2c306ccf91f967b5cdcb50808771ede36acb9a6cd84fa15c6de4c873cc2d0a64e79778a2822a109b41f5205fccc30f", - "0x9751fd8bc2a07ffe0566e5587e652d3d8d5250517278bcf8754e14717b8941c07c194f14fa757f9a2f3461ca6376bdee", - "0x919746e5eaa18b734ef59c0de09ee8ec65e348efa659219d9eb6a3a77b8edd9449a6dab727e9860ca0d3837b161c0352", - "0xa271c146794d6a65c6fb87797262c22b1d938ecb2624e03809233469296d33ac0de6909183c8efa438b5f3971c8c3eed", - "0x82fbadd9049248914a15755dff692bf689eb2650fdc6e44e8f3ae003b8f15a0f2858c7a2a8dd137b3448420c5f434229", - "0xb90992cad6df98d2fd1c75bf90978205280d993d439c44d6721cb95d52eb042e01b418e45c3c48ed667aad577f3fd1c1", - "0xa0c3d1e8b80ed4a979a22d6a9647bd57f22ac8d73c37ec3d56d06dc178a5c9d5ad3ffd6dba9eb7844c1f40b8c89d3d33", - "0xb513aaf2f0a07fff3543d8687426d07773677ca4d23554ca517e50bcb891164d1f5651944a2f6e0a634475f6d33bf0dc", - "0xa0b179aa2ecf572ac4a3ed898aa34679be3cf3d8d9bc74e33609345cf1743e915124a59ffcff41bec012ed2a99447e6a", - "0x8e991c5549126d64e0b515a03d266e19097eee38d08121d942973d568f8ae23a15b037337cead0686f7c944b9fda3e39", - "0x93cab29e1bb116a39ce1a82897776da1bcac06ea131a7dd141a688ecd4089c5a0b8616d6721b1c6d92309ae0820a367a", - "0x8d4e0159fd3534172b2481764cae7746b1a47e9b7b9465fcec0824ef234674fc567c16ca7116dc90ba9b2ac3eef28d60", - "0x88cbd9ff6ca551a7efca30b5f59fedaca8f9efaacd4e9bdd17ef0dcfe4f3449b52c7d3066716f3e7fd478f264d10714e", - "0x873c71b2feef5230c31f13d8425f8b0eb0171eacb9225f484a36da3cc796d62594fa4771af9ce1e7ba951f5377e5db76", - "0x939eb99d7fefc9fd5b3dabaaa5b331365121374a4ced862b8cbe0cb3c574fb1f0cf4932106088a1d87522acc31ba7f77", - "0xb283f1d54bcc72e31ef572498821ded40485f38d2ffc67f70bac68a100612b020a538b36001d94228a4dc97da8fdaf17", - "0xb2e4c2be605c8ab3038b4e91bca7e77e127c5c3106913ec1341102e138bc8aa1d218c3d3c2ec1d36fb8e044b4bc211a5", - "0x82e73cb5b2cfd78c17131e871e92026643bb56916ae64f009a009555903df878fa3a2019b82f7e71a3ef7eb503c792d1", - "0xa6d828a5b7de0e7818975b65244f6efeefc712c29f2f17b27f3264e19856d869c350ab39750ba63d6d46afa3aeb369fd", - "0x865b17027e9d5bdf2de0afa2f524f67b6defed87b14e0af5f4b6b1966c2de45550fd2b6b39b1be88ee9cb06e755f917d", - "0xac8b47f9b7e675b556445d103271e6bd3b39b94d15ee1f3108fd1b777d439c75437c78ec3b281f7104af6d0efbf0ecbd", - "0x85c2f71ae18105fe499aa4da0a835de3e92ce05d0f28ccbcffdd2860898ae9909e1c05831ca4fed96545133bb3170302", - "0x8bdb4a72b06562591ee44799bd7400ebe71f6737290420dd4ba2bffe0050d8ea4d586b7e329541a52611e945ff1b16b8", - "0xaee4843c9ab02026ae723531112170bc7464f51460bd4ba5166fed54ecda0f53342cdf94f4354a5bc1b941e8ab085a80", - "0x84de368006db07c89a7a43b7de54a63637ed72379a41d029430f6b4ebe404866896d2e84996998f7b2c20324143649f8", - "0xa8375f69c01289cebbc97843f417d0146f68c6416981032bc1f42d3e09845d5131eb9b4d68fd0ba7f5b1223b83e51bab", - "0xb1ae126dda1a88fee9265ed8e5bccb810014044d83c70e01e7f80059a685067f4204cd15809b230caf5dd77738a64e38", - "0x8177544c7b1f77181735c660102da20fbf9a2ca4efa79b21c92f1cd2b912630aa6c830b7513980656bd275097be59d1b", - "0x874fe8038905065ff3b77f1e53904854fa4fcbdc4c8959fd2df2e3967b3b84100c6f63fc44338c01fb26c042c454991a", - "0xb19324d737364cabef3d2ee4785e8f19cae399afc06fedff8fdc120e0ce525b3755161183a1f5ad11ee758104081a49b", - "0x8e7525bffe35c1f5c2db63ee911e7e0197951ebd25868660e6672a3e7d4fb309738268b36677921be3be2f323ca718cd", - "0x846c51c7d22e7d43f6e2addb7fb25113c25ddaa51252a898fc1d7d5b510f772534c4f5d37ed3074d124cb259e2bf8110", - "0xaafe2a16cbc901730178841c39803ed84d3a78f92f42f84a1c01be4aa3b63ed7ad9d054ceaa8a2d56eadddecb287e8b2", - "0x8781c9810ffe3d93fbee3b576a15b8786c3efd6b5a96b75819b1f93daf413d4fd0f56d1ec277e8f5adcb784b17f08371", - "0xad66011f0e2927ee1924725bcf8a296069f74df78ec66ef6aa8792f68425e48e9d7f717d022f68a079501770ce192fce", - "0xacd0ef46fafb06f336565d86e0b22f9e5500d2f73d047c827d6a207af40b706440afdaceb32e6571deaa1a79f5e6fe27", - "0x8f65bb98baaae22e84a3ff375e7598b5c61ebec676fbb5a4f79c8463c427eaa96ebc51b1fb504840b7b0206ca6c2c72c", - "0xa4078341325d7debf766e43679b8b68331dc13651455a73912afe062525d2ea909d8829ac08771d9b32f2eea28b64022", - "0x88eb29841b022f2ec9029ecd1a137173cfb79addde1c7cd4be425e5806ea6ee295b11a0459a940ba79f789689a8fdb81", - "0xb762b9923a40a1965847bc7d046723c3b8f0d63323303aa3b25e93b4af8e527f1afb3dafda831f50baaf79926d1b1e78", - "0xa21551dffcdb069cb8f30b625c8404dfe5efec83227e3a1a67ef0c9c9b49c658bbb31240c3ff6f8c68e02f67974c732c", - "0xb4735a6610c371754001425772aa5314b759c24da50b38a9390969c27e215ad9d463a76762323b7954756a8d5ee7936f", - "0x81bd78e545938f8a3e53ecc2e88dc26bfbc30941cbfd009572d9b38f8eee47a85209a318cafe8cbe055eccd5e62d50e4", - "0x82ea5495db9dd48da97723bcfce02788549c6006773eb9f4aa4f0f3ae13414430edfecb5cd095259179ec2014b6ee1d9", - "0x8493147b8f0818c2d5e75acda498139f95fa6f904b47f87a8c07e258c60f39bb1faa1d29cf0834c8a1ef1d6015d37b42", - "0xa491233ab353f9daad86e60fd48b6f70dce60dbe36775958d8e270725cbbda96578b17a0c4925ba1298e630c6b9ca9a3", - "0xa8c148b9e1373afa54778b6d4f76cb12f40eb6e07404a7f27b271fbce0d18d621675f1dfcb30e3908d7df1d962de2e5f", - "0x9826a45c29ee22cc26ae399be25cabd16180a291669fc822a44d14cfac81aa7ce85112d7f2c30effc7e83d131c9937cf", - "0xa793c75e716aed4048a2893f11eeba78ec565ac250bdae16594d056f06f4aa7d2a156e1617fc26def4e39984fb28936d", - "0xb6c454d822343cd1b1ef7161cd2ddc28145134d4e59e6d69634c010ad1bd44120aa8458eafc28f6103ece7e34b368e1f", - "0xa3340a0edc3fa82fe4f31ca2d19d295aa09c74cda3bfc3534c66eb71bbb7903843bafa92f7120de4505c7ec819a89664", - "0xa18e5218cd4349985f412ffc7741b5db21bb14c6e00431daba194771300e740f75fd46aef1876543967e8719bc6517de", - "0x885ce63a88617bee05144bc67d08f1c7072d8c4e09b23b7359f020995aa8cc9654378d382de6340ddf0803717687eddf", - "0x8d8a0b614be7df01a12e534bac102b1881905a9d084146b3d0cf2086dc7d400129e0de8e48fc966adf9d8ec8c1336429", - "0x8baad19f604bad656398a4010b20ffb6ec6131681d120220dbf2cc5779de1ee146d0b773bdbdf4e8e30aa0f464f2b18b", - "0xa39ae3d204491871c2e88d7772055b35af341ba66531ce3575f47c749eb8e380d63a7939d3408cd51356cca29c76d4b3", - "0x813afd593876667ebf0fff2b8a8a5bfd0f42a4fe2e4a0b7c78b6183605706c97dfc40b627340e1d9527f618719d60e88", - "0xa013e458d678fb302bcb6f002a52e3e0ace443009eecc9113ab5b78f4663acadb8ca9cd757a7cab1e850aa23f09ed698", - "0xb6e14f351fc47b9e46a83984756812cfac795cac5ebbc6f00d673ee23209d0d91a6bd7d576c7d35ec3c7e7cafb758a46", - "0xb94246a346966caf6fc1e0081a211f27b38f058dbb9dff915e3e65391dd36d66c51324667e3d7469a865c0cc064589ab", - "0xa1bf4bcc7420bd17acba90ee67af96e73502777e1723255a73b1ae3e92fc77e80a287ce7c3d4088040e0edd64577c8c7", - "0x8b6f5eb9b6bc7320349b19876864baa6cd8e07da4f70653d7369740184ad416c40b4395c04750f5d8b54b3b3ba68295f", - "0x83250b957d920b1b738f4d0f44f9eefc01b5b0582128f5ddb5a282a11ee207ba1ea7867f00588f8b891bbde2e56b4c43", - "0x8eab312cac9de78c9fece9d67a6b26d58c4e15d5e0668ca2cca2d9c51636eea5210a893f9321c2fb232e09f9d0b40fa6", - "0xb4d1e5f284d50360dffd2a0d21c4b31d81f521158aa21bf333a525cc68e690ce8ce4f0eff8e71a0c2d5853e2fed18231", - "0xb1f194c28bbe217a2c98ca8212fdca744f0806d60e51f9da81548155cfb97a39e2a98e753be8b2196c83f7db8caad2e9", - "0xa7905cbb59722d9463c6317ae59adc10d5bcd6e9788f2a54f4ff4a4de03df3f830d6b8faebcda541d429a7e42d158c9b", - "0x8a3b31d0d0b33e7011dafe25ba5c3b7629cdb5dd5b31385d786fd80888fb8409812b96d28fedf6a401a93310b045c620", - "0x86e4990bf50b27bac76926dbc65a1ca34a759d005e56eca56fd0e00ce82635dffed6f3052740cac2f1f37204699bba9d", - "0x8f0b6a0b66f1f5fa3d12af444963c6a45785a36dbd9e1a5f42830b5534ca8773a05fb618292e718cfe8a066b8fea7980", - "0xb7f206827d715b33989de5c02f0886d3a457d0ae34931ddfdfe2dbab121b69ccb560a71fdafcc3ff204217611674f5d3", - "0xa6e2ffb0c5f29043984c54f5fe6449ac4b7a86f2982c909606b88316ef1f0a54638d565f44d2fe8cf00279e0eee435a9", - "0x8cdde76212e73f9430cac210b52507583e81aae0bea491f6cbe27e5e2c8fdda373ce8c7a5f16bcf8c6238b282d06639d", - "0x8030955eecc9b8e2161d40f1f514d58024d25e07c6710916824ed8982b8bcf7ebebc5643f0260e1ef6150b6901dc2dbc", - "0x8938fc90e836d7bdf1cfefb2716cc25aff34e7c4dcf66b349d1fc6681580de7f58665faac29494b30bfa0c743d6f33e3", - "0xb182f9b4a5d838e9d534e41ecbf06330c56a8a64800eee561de1fc2dd9486565ae6099f40d0f1901066f45047641bd65", - "0x81f98b85de7b6c581613f4a42e0cb0dd9e6336399b39d38a4751c2a9f195b65c9e5592fa1728b91d83cac0ebfec7d675", - "0x94681572da95137ce41d913360cd567b570a87c9a439f2b67b90d623561b75bd3dd0486a90a63d49eaeb412cb97768da", - "0x8e64922606ce05375556901b8c042d4f41a18fafeca24c1d56998e0bc9054bcee7ab079c3729a67d908d0d7967a85edb", - "0x8e10e8952b24125321d0cd9ba922affc93908b3abdce47eed22fb2d44cd556842c31c36de6d4c28b4a1b5dd84e07df81", - "0xb6d464020a51bbb53670c81d5f1474ef439e006851d5d5a3fcf74219614a2a9c983737f20b254d38a2fc7333b35fb3a6", - "0x91801712ba264cc2eb65e8a3d5a032674a89f4c2dff95ef9d80d3a9285f3c2cc108e775dc326881484756814c2a03637", - "0x986e5a00f13326735bfc6b41b086623101f01dd55f2a88bf995a3b81054da86bb2f97fcf647d58e90428e8e9555eb020", - "0xb2875b4ebbab678fcafd270a0238a208b19803012fdb3c23f06c74bfd45929a9856b7a0f9881b75c7e97fa9d35e49d1a", - "0xb3d1acb9c844d8d2232834a81862c59548cfa849e8e5408ee66b4c8b86ddac0fc231b2538a752eb6c1ceee92ca443d1f", - "0xad0b1b5d6bb50c43f5f3b692c5d3569d2117b01caa7f0ffff502d5ab727f7702a2d458b89d77d218d3f92351b4c2b92c", - "0x95b1b99dc260ae6ac7c387bedd43fba793274b15768d93df13c88ff6cf637732cb6b1719467919b444c3b5166f4f0107", - "0xa0c3c8b59016056742145e7f4ca6568d4393124efac6540645152bf71173dea3d0058bb11b3093228ca4729cdd5b3033", - "0x9278afba60643257d9f21a4033df3b57743c3b43d66d96ccf413154a63db054fbc3a36f2ef378169f4f19987964c0bce", - "0xb558754c97f9824a72644de1725127dd36e284fc07ce89006b990f09db77c48ad6728e5c1891a691460bd5416ad65faa", - "0x833a02af76172f868a25e850d35f4d164889bab8381fa9c8d9880ab0564a3769ce3961cde72bc94ed73a1723daa35cef", - "0xaca496f3e528a2e3eceee138291107ddddd68bb088b2e49ea76d0c4136c6924b3251d7661ff467a36dff29f07ed09102", - "0xa9367961ae88a19038c446df3eadb280da005d120c16f48ffeabbe4cb8e5e2784902cfa1192876ab934bc90606baf2cf", - "0xb43feb49373dc36cb46e050e3cea43e636a64289efa3af790dd3fe960446492b858f51b3be62c6b75b510d8e2b985573", - "0x8cf24955965468125fba2c5a5799672845ea6ce97cd307b09236ef1a3cfe55c88958ffa311e8bc8335bf261a84275d93", - "0x88ceac98b512e5bb915554af92318a5d07a494e0b8734c4415e192e7405d6b06d170fbbe254e3bf387759f6d4961c34c", - "0x8a9044ddde945daf3e0cb3f897ca00d0d4e6a5f7c99aaa3929f0beb9a44d2ed23c191e37c57140ebf3ec759f50f84d57", - "0x8b2a2c0fb51e7c5fa51e8c593bcf118696b8411bc93e35cfe5de6c5465c6e80bba64398d7c6b71badda616b918bcc7d0", - "0xad8bba2b7d5577f971a1a561b17a9d8f6b7c35fba55e3e421a0d8d77b520eccd52122f02afaf3899218b652980986a92", - "0xa8d743b56896d44bec838e10ac1ba5a43f58c26655c71be0a5417d936260453a8e91752c87334676c5dd1dcdeef4fbd7", - "0xb0b0540f8d2d1ebdcd74d6e4007324de8f8bdea0531880520d79773c0b8eda65ed49e672c5a294fca6b4560442085829", - "0x96da830d1c1625d002008e9a364005b2ef16cf56f5aa4a758ee963388493cbf90aa75c25dd67d496af17212537ad44ab", - "0x89e819577a95195056b872f8f790d001fde3253a23120e2c41b3ced7fe8e9bae0df74907b7d65ddf9bbd6d2efa18eba3", - "0x90a139ffc7dc0992c023651517db4c195aa2f618dc3002f4a0a43013b6c6022d8d9844a49cfbaca543c9cf5d9b2210f3", - "0xa2061f543b216fc9c801d885ed681f9253f67cac40528b23aa8a709f24e0992fa42a10f1bddc7f10af2c22209343ca26", - "0xb5f53715b9146966f386f214477743e2fd2b771bcf90b192a5863c06d7225be34edb6bf71389085edf344e60afd88561", - "0x9634ce27272f3c687035789fa4eaea2aaa71db5b5531b21b8e029645827b40561a5901b33afd80a3aeb5777aa89850f8", - "0x9674736cdb4a823bf982d54876794e99c7672eaea7455be90e815abd03ac06ce1fd9e73bb987a515863c6cb4ae597835", - "0x90379303e285b19fd7816a6d02c0b8f94e6291b56c196d76aa389cbf813dee7ebf64e45555ebe8a281daeecfd7aa5b00", - "0x8a1f759f6cd6e5134f67b96e0edce7170e4be1b39afaa7af1c2de989116a6ec9d38a2c077c8e6e65ce0bdf729f20f1c7", - "0xb416f9937a51a298548e91cbe8fff71585335c00e69602423adc9cd72d18821987b8fb5ede32fd8bd2166e2ba9aaa792", - "0xa423073148046c81f840a481d57909f7ef621a51827e44706da9e1f0e27fccb8f88652097a9880ca64c41f6386aa9069", - "0xa173305a5aa2a17349eca704fee25593f5c2fdc5cd8cb932a1bbc0ef34bf54ec2f867ca93d8e6aa33679cbb71fe11083", - "0x87c6756d14d815ac8237ed4a75fb11206f615585ed527ad582841526371366ab19f602c7448a21722adbf2d987d89b81", - "0x8a1a6f06d6375d2bfbdc7531e9177a45330458da2581f65ad129367c400cd77f548aa748bb470bc560c0b02ee5b802ab", - "0xa24a05c30d0fcc8334f6974c30d13a5593bd3b388e2146ba006f232bcd6886edffaf7e48ed6126efd3e651997dcceb12", - "0xb35c5f8a5842d97cbe19105305cae1f971da5662c52eb979975efa0753bb60a050206fc0babac5b5083799e9ce8a68e0", - "0x939ca5532c922d00d08ec5914e6c58f8a1302a1214a1cbd5c844b334ddc84e694768edaf1a2af02289ad74970800198a", - "0x911d6104a240f84e0f6502597405b47a7faf5e68717f6d389baca62bf82fbb7207ce8d0c584fd9d57d3afe1f957b7cc6", - "0x88777ba7a4bdaecee78d42687cb4fd6dcf04402b43524e2ae67506d93acfdc29d9dae640c05d01c90caee1d04cf3d35a", - "0x9226e684606f8169e38dc21a14911d0306b1c9ce5244500e4b108eb1a0c0783486acaafd2e0b3b60c413bb003448ff21", - "0xb2f527adbb9feef9553bf508f192b5ca211d0e491925a2331bb294fcde7d8e0fd72b441e9f07c838640dd35fba03e1a7", - "0xb474e6d6ce22ea272a93a3c078197f40c01b9121c6f3083a8e587c367200b8c97ad94e156883475603f0a66d0340fa52", - "0x95c4d9896df11d2b5a8205a19d6331ea02a2de038aded8e6fea6d79bf5a6648d5d986bd29430e4cb5a6afde8b07a9a48", - "0xa12bc53ba6b6f8350b400fde04518a741a1d755123a6ad1d435c7642492c7df28f7091f17b254e793561923de781eae8", - "0x8a0578ac03070bc920a3b5a7a33d976b3133501309af5339b0cc70536965465b4f7288af70db3d5be16bc2a1e5c26a86", - "0xa66e27284ce6114e48ab56d7f623dc37a6e79cc5f487cb2bdf0acee099cae744cf3a9de53b111492b5ef99b0deaae0a7", - "0x832a338951022c80444ad8c6d0285e86db54254d2689defecac2ed87f5eb4d876373af6d76e3d613523e32c3966142f2", - "0x81e83f01bac3ac3fb67e780b28de30b32247a774aaaae118db3d45c8e74d1d4f1defbf9c2a7ffdf176f5c1cf4ae4167e", - "0xa1b214ba7265f692b4637352c6139bae8bcaf3e7db5806fad0014ded93048fa4a36ac9c9e0b7cca0a86cd45bbbba2fe1", - "0xa7ab6f470a421e72fb703a9d153362056ce80c40264a3ee5698168130cab8e827df5ce3e6321ce9a669c87a2e5c67499", - "0xaefafd219f2d062a378474c48d2650b51901b6bce00e4ba0b509395a6fb39699037577da353cbde187e65de87ad01575", - "0x93db16a0a77d1b181f33ef10300112fd8db5b2eea26732bffa3b1fbebb792c6ecdf2899cf6f26b505dfb46deb81b217d", - "0xa63b6d9d1cc2f31ac5f836133ae66bc9de3e07ced5026f5bc90116599461dbdc03cd7680c1bb43dade9218ebfe1bc1fc", - "0x984b49ca86d38a486f6315f4f9e6ad521901a06f8862ce1fc095d9c66bb2164e334718c71d7472ed765367db5fede105", - "0xab49ae93955a38f45f756afc4248a37773ba8d0a19779253fca3b744854715b9c9b10c09a37d3426614ffd3a8ced7bcb", - "0xb22166dd64c83fe16feecc09d4b1df2d967ce7f4ab526ae39799dd5a5a4a9ebb1d4a432c5efb90e0875a4eb6b079e2fd", - "0xaabad619d887b69b9562066fba2179c69c684b8cc9318c9e39646f4a5381535c024ab277a0f0be46abc95283b337212a", - "0x99f5d484db149e9f8dc9c6758647c4e3702d88986600a3226874d612bb4b5e92a76b1dfbdb0909b8f21afc773ec67c7b", - "0xadc8bb04eb8c56dc4ce97c3fc1670da10db134cff2edc214ee3221079251b968e2dbc087c56c01c9260b49506958a6ac", - "0xad625ddf5cd211102543e0943a7770a9b45cf3550d12dbb484cb5522b70cb626f9ac795b07a305be3e6949d7ad475f66", - "0x8f9f5b2b43624e89e8535dc73fc54b744f247572b2920679bdf6a3ef346e654ec40fe8f81a0f7c7ce7cd5b48f3975359", - "0xb70b1642f28bad56bb24b342eeddf5c3782e0cf6e0d5007c252413bb44b32586da1e3b4d8b45a72c91e44e07334da68b", - "0x81b0311e557c47ec22c5f5d1f757c6193cfffae357dd2460019247178b13733484dc8630fe2e13037a1a3d681c69066d", - "0x951c9f1504b19acdac1c04aaf535d3cd3e39c431b2b5d9def9b374468e93d378ecc3f5aa02c91ddb93eea431b327ca4b", - "0xa85e1f4c292723d18a49cc9323dc7af12bb5a8d0c95d71881ae235ce123c50018907f46bfc846dda1a01b14ec45dce14", - "0x8a46c8b86bf9890df60de4c210cd7865892d0c11fdf2747620289d73bad597e6b482c208dc310c25955dae8392d8f278", - "0xab65408622c63b67842a80c4ed665258ab617ccd07871fa3f0fde2e5ddfeec49f01d7501790a60b3a05d7579b087b787", - "0x8706913d42b557d9ea4d7b70697069281504b3c4e1172a2291e3b3e0a0305c8d0bff6b7721356d971d2fe58e32d4556f", - "0x8d9b8f3c113ca1215dcd15d4c37913d023c8c5d04f617319af76bb7bab72fb756c5bd992db6b9e765cd7695c316360f3", - "0x942d4d3351b2a9bfaab2500b27d24fc2d7237e791993a7d0335f36fe6456c5a1a8bd28dde9228fb139e95288d6de5bbb", - "0xab014e9cc7d3ca08f1d3d24473ddbd693331f4bf21ebdee0fc997aa2faadb43db6a1195644c459b52a969f3d98a85b8b", - "0x8b679da80561955990c91da9093837953f4ff7fdc658b51639c462b578a2b31443421712c6b7742fddbe0ced48c21cb9", - "0xa9132ac18b1bce93e815f6d2f8a0d2f433ae4d6fa04269eb0f5f25864a8009b01531c7c3ebe87f07454927a010ab6dbc", - "0x8ab02c113149efc877967c92621a8ef618bf423017e78b9cd30cbb13c51200c6ce27c46be75e19ba843d64a3050d4467", - "0xa881043298341efc28c60d850d90d90279fa6d8428953337ba57b74eefd858e362c6118a82ebb025c9c102c91b4aeafc", - "0x92e4a587479c64b8df955c6bf1abf1d7979a978e45d96f05bc1b9648f10428d77890be9ee03bc1b1982f5ae7b926f0a3", - "0x90c21a22826e2e9978dd7522f51353fb33224cb65603779de41db3ba41e01d664e131233bf873e28d6c71294b565c533", - "0x88e8ccbdc54ff06380c2243203d3f8c8a75fcfe638d6e6a010c0b3a39d5cda31f8d2cc416ee5264267aad2b457c94e50", - "0xa256198394b458f6468dc91c35f579da0ef02a55fd93e98b25e43b1bcb650ff889df4899236765c1a6b35cf49da940bb", - "0xb5c7d9c03c36cbca068abc6778053727e77d9b58c5dc33b11629f1ade1c228b1c964f5a7d8dea16057e76662c4d79f18", - "0x9588e133517f0d49622222b4de5c124b1aa4260971e43e4aa767fba8055540f2848954886b7f245583ea527fe2fd1de7", - "0xb66025d75169bfc7ea366cd32419e24fbff829709e3e9587d7d59620b3a7b72034d3303106f965f5f7a71d66b7f314f8", - "0x891357bbe44e60627b975c10c872a34b78d6b264380e351f3a86dbf99abf8e2dd8d20c52dd6073086e48e1ca782e2ac1", - "0x8a066a3482526a92476bb8c3e5caf07575c725d72203d67ce98f654f5ee8b7f979187416fe3d7ae0128800b253d7209d", - "0x80a9e3d8900046b71fcd5b7034d1e0f57d95d2756da8307a11aec0553e5715518a125a653d356f399409545256a1984c", - "0x924a13fb2da7a899cebf2ac09c8c0a183491777100de1aa056a6c2bceffd5a63e255f16a9066e4ed89ef28096a1230bd", - "0x866cfc8116d2e0216d8049d5ae2ef0e3fffd377028850716a4bc2cfe16c5a6be023334bd6ddafa0c77913dd4ff0a34ff", - "0x95eb74bebbbc59d793e3fbae8e98c258451bf9bc5097df4edd832e9f1c30a1446a59e1f75a44832d0658d5ecc13dfc86", - "0x972517b2d72ab53193db5d682db2de7790a418ce4952c29d64e1f9107d51a782f4084591b7c775648f103445b797e8e5", - "0xa14ad2cb69da568f2f958ef4253d7a6daf574c6976f4f5d771ae7673853ca22eca81e20400092bac84453b6eedf5aea2", - "0xad95bfcec6c06cdc11d316b7ad33fe65555e985bb33b15c9f481a09caba1e5990601ed6a88038c0ae2e04b1607e2da48", - "0xb7e3bf3a585af1029d83f12cf75acda894fc4441cd7b3d56efb6991ea91b07512bcd7d6d68738557a48f0446b2cb21af", - "0xa57efb1e2d2e10e41f356768385375a21d9f78bdb34d618117581bf7a15024eba43570c3956ddb85a025d39476f831d2", - "0xa66d3622b1cdd472a2a4491881de035c2eb4f1c94927902a3bb9f10739f900130907c6b002982e03785c43ac30b8109d", - "0xa79f2417d32fd772e46f3bca61ac788af8fab174e1e1e48a84ac557f7e80a9cb4e2d7b467365ad18f9777f4cb5bb2b8f", - "0xb952b976e3b6660326c0ed357ff25ee1291b74891f3eb7bcea39dec2ebb11e287d6e26ae0506425a20e5e445273cc63b", - "0x8c23929e9740ab51d9b82c6b7840067e7163e6c7b9b9441e1bf867ca2e532926981c98641e6c798ef12d35108abc1dd6", - "0xa519578772c9ed2d691a8c423d360e4bad76afa422f1a5218a7a08ed52c9a5935ce2ae4c0be182eac0712259a43f849d", - "0xb1529dd189cbf3bcca50e97199bfb85b42f2b26edd95b35758d988d1d3740f5d0d2e249763874fdfadcefad9ea1b3d02", - "0xaa3fed8d14a4f38df75b9eed7f187a31cbb7a748bd3225dacd8325a71dfb680729fcc91ad8cf0b67ce314e1fa8ba02c4", - "0xb77c28abce17732a08e682491182f63fb55640e60384932f6a9c6d3d7886508c9e67a841cb93e59448d2d59fceec4620", - "0xb7a24c58e3b85d60d654ed14d78993a9cc78c130442c8cca42921ade8ec94bbd0653c9fe5c69ad1fb2aa46ffba04da39", - "0xb7d08f3ce97901261514a5dbae582848e75515c5f9f41f5e70ec17a8d0db3067ddb19aa1c86803bdbb757230b148bb21", - "0xa5b8a6818be4d59079d88f72d7aa4957c48ff5898f3fd01def48ff6bc7aaf9840aa91f2f05617d340092dd9299115c2e", - "0x8e548db6b871fb23ca1cb8538d44b77ad02f4cae4d33c8c43228b820abee1aa913ff9acf2483725b195b4e65e2e92063", - "0x9509189e063812fa04f4e26f87b33a2289a05c229ed1038fde0dacecd87aa55ae0fdc678a1c86bf13b81f4b3a872426a", - "0xb355f24a5dfb7a8f3ea717111a038487632bf00d67cc2cfa2ab61e1cace7bc7f5bc9e04b190aa6be0652627ee219bf76", - "0xa9b335f235df51b92f40f44f19150e182a938b9abb3bdd8e8c447b2b128050d228e0115a268af4c1bc2ca49552b4e0a6", - "0xb306d3e6cd7ab56f5f7572fe51175ac6b29b189220fe2d380b959d131a35804da5ce95adcfa51d799f18e27d8d5eee0c", - "0xaa49cd2bd34c37ce1f05e192fa6837f964c068170ab97989e1cb22ea7e13c2400417a51282519e74d8fb6983ba89a549", - "0xb1d4fff41d95613e30427ae2ae1d3df8c9d06389e1e0f404f8cd40199d6c4277b7a898d06f1579be107fc5744247c36f", - "0x99d220454889f476931b0cba3570eb1a8eae30b4c3617513833a551aab0a2630125f72dafc64a766b1a322dd42dc385a", - "0x8267ae38c9c8532c7d4ec4455279a5ed4f2e48746cb0f2619937157534b0e5466c5f4b99b7c342c095f71f3b77fd5882", - "0x8bba0794cc4ca00eac50309a92878084a6a22e4c23206c68b8d7268f9e7f615da4b9d0f3e006d9dd84bc3dcf32261e27", - "0xadc965bd7c7bb2a52cd3f4d2cd3fbd72a196876a678863c6a67a25b4a2330d1d3be603220de22c8c3f60c1411df57b7d", - "0xa7d5f38a3c4ca0541d5ab101af9c27b04c5bfaa42a1715e882c5e7715e460c4666aac4b6272b9fc54514346fc49d0560", - "0xaf94b91ad9b0f01df1d41a459f16ffbe30710325617651cf1da000eec43876161957f079a27b70018ba34d1d5d68cf6f", - "0xa0e2a492da4614f41741157d3a1d19a2370ecc8e178d813e22b902cf7454b8237f1ce3c16270eb6f3ead1f92797e36f2", - "0x8dfcd39155d7b8073b0a1a9a617fa75218f569520d4817f3ead375850ea8a3e3dca64c44e83f54afc37173d927070601", - "0x98302358e5b740b73e1a6c568b99affc6de3c7245ae96d9c712d377fd363d8b8f49dbb714aa8d39b5b947b6de341ece7", - "0xa2fe0f9fad663cbbf4bb05f61edfc90716564d5ee5a9529ac3cb8f06f96329248cda85c43f24a2382a9056e9a53520ac", - "0xac50b0727ca2ba80692c0b7f564417916695ea3760ce9fd71593050912bb97366d29ae5ed05ce52984e52218854b8e3e", - "0x86f56bea946a4516336a90328fb4b24cc7f82d8710d0d1e34c2e27b6af73c4f4a9d6a848dcc56a87d6259a99ac444557", - "0xb33d0244948c430a58b210943e41aa3cfecc9a823dd3e160634ccc45ea2680987db2912ab2a173ab6cb9cc2b7e16f7d5", - "0x8808f8c2c2377cf52e7314820d88234d7819a6108fe9e1c6a675dc47cd59f81f95594ba2f5fa10e3719580f53edda641", - "0xad34a814be6019931972a76b3300a4fc9ce763d6f4fa1ea131a67d575c00c827b9ae7260d88577b4b3689e90a845137e", - "0x9370abc67ad0fedf30b929d1613e336c6e99e4bf83ce969e61e5d77061b48a1a493f28fe2eff436d4a979af700a83b5d", - "0xb0db136c8f4ba2fb7148b1451b18f694769f5e53650d68342f15817b04734ef8ae59681a5754df617d755a687b6ee45e", - "0x9149909d24382054a05fc0b057613d059721f132a19017a92198b30e48fbbc5f8f0b5f5db55347dbd9d190ca88f9a28e", - "0x883d1d170fb0fa95b55b10b32ebed24b1232dbfb5c783148a63a765fda200e796aaec52747441704967914433a01a323", - "0x8f55fd5ea11c4fac277112d72489ac1de28fe163a756b125f27acb78aa6651c70d1cd8c45e0daae417bf894149ed2d57", - "0x8d08685f99aa8525b008b868f5486e24a08568a5afba9b729f7d26370fb1b162937db28b935d67e4d22f7fda69a3a6a4", - "0xb1882e23d784ab48b2f9e58114c5920bc9d0c4c01d2d7fa5111561df0cf2d738e31a32963cfa58939af87e79428659da", - "0xa3eba902d376063e48634c9436802cdc6b89d3a7c7cd03b26a3fccc7218dca85a3ed939eb53956d2e001805aa5c2d63c", - "0xb97330c40d51a4b71f91f56292b628379ba735509a66c7df054112578b9df40d3aa32598bc64c03c78a3311a17997bd1", - "0xb84f3d2af2aae2aefdfec9a0693f6bd71eaf4d477cd72d80f4919235a471607c5483b354c9d46628a76d6b6fe7c586af", - "0x8a1c39bea7fa580de967d8ced7e3860a9031b07842d71f8c5941b8877cd55ba15ef7aec6116ba38ba290b887b4530685", - "0xb120fccf939e7d7959c2c1e70d7a7aa3b84684dd1ca8e5cfa9d281fd06d23eb67a629b1a27052614c3ba639ff9c90dde", - "0x827a8e0dc841af0e2c4a9ca36c84a0ea60099aecfa40294344f82878b6909f5581f7b34fa9510883113795bd09b5e4bb", - "0x88c24cc54dac5a2982be5ac49684d99f95574bb8cc44afae4f6e18231ebea0f2ab65b49870840bd3e8f2c9247f62c7c0", - "0xb91fc3f2cf743f4ed42e49007514d43dea1d7bab388a18de6f71367fb8f2e9b8e88ed9f7492b647e548396ef3e3d7765", - "0xa175000c4765a57c57b219b21f8302cfd85aedbc3340fa1690119bbe7cd93dac4fd0ba676b1784ebac83efe3e78d4bf6", - "0x881a373630ebc24dfe17e27b3f176de6651347ae741d55675675e9e6904ebf157e787d86eec42ecebfe4eb8f28de6fc7", - "0xa47c8b155c8ce8e16f38deb345a051fe0c9b217cb7a266fce78d7694134247887789645a82c0ac24341f51da8ee6ef00", - "0xadfa5bcc682d4449adcc436649b444dc61157154e24d68615b0ceab50eced1ab55e15b45562dd8e00785806e9ef2b7e7", - "0xb7d2ecddf47e9fd25dcb283eb80e323300bf5c3ee3344abbc3a1f2a3296c631577a1fadfbf685abb336d5d7059d17166", - "0x8833f6b388e46e1f8fef1086777466277cd418051ac0323e2cdac5902d7ae45eefef93ce90b088bbd618e0381c1ada78", - "0xb6abf44c5aee5d0fbfdbcbf1e77354d5a2ccc239b894e1e06d7ffa76584683f707384319ab0e0d17afd93a854d7d26b2", - "0xa8c61859a9553a83bac398c14c987b20c8dc27d63112115b8aad26bca275cf98913783c802ebe3b7c3d878c130407b34", - "0xa5de7a519f8de4daad9137f2c2838544219834cd70457ef09467d869f4dc32098b7a8d4fa85e1eb283632f6d09971318", - "0x98c33a315a66cd8ab9ca8a58c87e5ec588107a6416c4ea498d0b91bf7597f53a405e437ca0a9d9c6acea27ad0ddbf4cf", - "0xb2909b1f8752f4eec25180a17163ab215fc20c4a931d4471d3be0ab64207a65c7e462fc0707791286a92ff2f2b7dcb0f", - "0x8b96c2fec34cda02e98510a3ed80a980b0cbf4ec03e3c4260f84027cc7453acfedb5f708c401d26db137032c6cb4a31b", - "0xaff645dd6ffe8b5076c83a823daca4149f0769bea3293b61330ebd97a17fe16758e4fbbcb5bea7449595c6e261127b34", - "0xa45f8b3b7196449f9952cadc8d87a787a28b4ed89f8c7599e7db361cd0f0aac6bfa464024ded5c0ffc660e417594fd41", - "0x85016b5f7ea9863557eccb0e742cfbf0b09630f0bad3de55aec92b95d4645055cac60d03602586b34f774bd356dd5554", - "0x94fd89dff2fc6099e5ab90149458a4c794eb1857f1dd9a2c84b88099412477dccfc2996cca2abee68d23a05265dcf271", - "0x945a52621ec19d26f7c8abb5d01e4f5630924b75a349ce74219377a137f4a0d386172da523edaa522d27902444023cd9", - "0xafbd452dcc57f5db6b3fdd55368807320459c16559d944ee8ecd1af6acfe9d58c13f37961f78030883f8ad7dbfac66e7", - "0x8ce96b3be871a1f33d559a6e55e4d86a0b92ec3954417f8d98676264596c3296296532097b9b20c83c341527a0c929b6", - "0xac6a4dcd58486d25a4db1751a60ca4d02b80c939b39ca165a37d9a0a52d8675b3753719f136a59ac400bde3efd036c8c", - "0xac87a37a14a5d48842d30432935929a0e9dce5642142a8c5b95e377ad1bf52120dc64697f0508b7c258af24a0ef484ae", - "0x859f0ba02d496861455d9c39c269a1ae5bd224319918fdc3648311c93303c0e13301ae7f3f77eab4ae43f1184a912b64", - "0x96d9b1d2d2fe70b8fcac136a65b62a4ded85aad9d350c19bb955750a0b24f93174e9cd00c0e0a1987793e1180dfdf66c", - "0xa7f5135873a1c08c7c8d46adfed19d0ed0e33168d463ca74f75116168355318ad588ebcca1946d7669c5106bc9f5a8f1", - "0x830b0587587b80df078ecfe0857a4b4cfc05b722c0f4f3e1217048ee18749e9940cd0200c1f7a0f60de832a5a44e9f1a", - "0xb6625ed0199097acc9aae20611f02d2fb837e4695762cdeeb4dd722517ba5a344e5011f14d5076783f3c32bb5c4a027f", - "0xa17be2e528c463aa4ce4bba2df5b005f88e363b87be7324239413ecd5bd68e350d290370e1080ab9911a0d54856536da", - "0x834064460f0e5f38950cf5ec197818712f01950ee1f32b1987dcf7f4098d20e1d91fae6d48e8a054390693a2e572f888", - "0x86217b9bd269408ac92b5cffda5716bb3bf8674b7e222668d72939a626f4ab64f30efddf85108c0764127cdbcbad7d69", - "0x8d7cf47b0648be0bcbd3ad1062d90010e5ee84e397895ce98160d5a568d60a19582c985944ec27bb284459789ad8f6eb", - "0xac056e3ed3487427142b3a4e4f9db53f1a752e1994f178577c46dad71be5fad4d03d24ae7019804c41232705a4bffaa1", - "0x94b83d67af6735e81b2e392e6af8ee4dbafb0071d84486389f36f222dfd015da718c621acdc4360630403762dffcbe3f", - "0x8ad27bb51c6cb860c21954f5d09dfefcbe3a9a0bff3e24fd1f74850edcbcc76b5b389a616ea0c0796b239b0c22357a44", - "0xaf9990dc4c9f536385811528f207a8352b083a4abe6dc016eb5eece0ad74da65b2c6c475a78cd0ecce0b2b550e4412cc", - "0x816dcb8ff8556540b54dcc1efbd2242dada0acc1e3d3da13ae581d905a9106bdfb8c138eee93992a23e7740593e8ad80", - "0xb8fcf8e11e5924d3d38643b2a4bed4b54e69f816f40d4020e76655eba8ffee758c16cdc2d970d3c8c1163cf501044c03", - "0xa50e0ef4ddfba6d969e7dd864a20cafc7fa6aa232fa7a806c3d53c3e029cf110828c5a9c354ea42aca5688896f27e6fb", - "0xa560435900c48879ff3f89067daa8e512482f061c68474d951c608ebb5a69c7863a28fd1e216eb4b140e32124e50fc73", - "0xb9202d152b7b708ee61c4fde6cf423b481854538d2580bc43462610f12141b89ce779c7398a35c27ea6ed0afa5332bb2", - "0xa9b3f8be28f9546bc70f680dfb9b08c1eea6fc381cb6f3ebfbe33bcab48294347d4e64004c11dde5eb409ecb19941ad1", - "0x8cb3086d265060f8e52a96fcecddfd261886002c1821a8f59a1ddde19a6bb1354b17cd19a9cbec19149dc219a4c394c5", - "0x906e8dea406ba0f0ef43ff623f8521039a9455a2290cae4ca9bb6494ee0aa812528267d1349bd5d339113dc9d1616b28", - "0xb9b5212b76d5824d66b8df7cdd5effcb05ccab5df6ce67558872c99d1e484ab8d21037bc0e22f5c4082b192972b80acc", - "0xa1fe817596bbb5bed93a5dc4c03e14eab627484cdc7ab7e4fba569ad0aaa93b34c4fc8680c4f8180d8190113218d26fc", - "0x82fe7a20fe93564cfaf7eade8d4d1394d1b4e36048cb8632bf366d3d8084ee52c74d65c4c69d9d24208f7916278aa592", - "0x81f42f9a3b8007e5f02c26770947f884c715bce1e600f38f164a390f159e2e5b6f8522ef566bf36422b14340bb6d3556", - "0xb53d3c89bf2a4b29bdd8f1bfc001c2533f86d869fbdb383fe9cd93ef0c49da0692361baa9f537094e1af662a3461f8af", - "0x8fbeee613823ebfd514e991d81babc05176d5c115907ec36dbf83a69eaaacd622f1f36be2e47b984cd6ac66a6b35816d", - "0xa9068ba463ac13d4dba25f9bbe3c93baa35828563f357c53a7009cf0c809a23502e023a32f651e29f14424c5daab2884", - "0x87468aa4c942476b3ac3000e740c4dc72d320884357dd99eb25e81d7b52a859b9ebeb55f3070022bcea3855a9a198e9a", - "0xa5f1219eb902234ffe8ba809df590080ce8329ee574eb346f6b4372892d66b0725f048465221655b70b3d4c2deba9fa0", - "0x8d9663d4b48cb86201d343b20a8e7a6ec47a4bce0e85a905be31121a01fbef95d9f29d83530faf79dda163c6c76ec514", - "0x9921ea9176744e15f64b20ac6e95ec132052eb853ef47e9334108778fee60d9d9b53fa0b8011c6a4aaae472eb11cc61f", - "0xa04c2c5e2c5a7673652919aecbc5fe09a636fcae2d06003ca6775018112b606e50bd2d6ae6ec6131d2a9999837186bd0", - "0xa00ddb29776d2747e3a6e68eb51a7cb00ca0066a9aac5a2da632f355db515b32e2c441fde870c9731a9dcc8d9834557b", - "0x85afeeae8bfd92c51522320cded430c2fef57b1950f9f966f47ce6354e492e9c40f950a7ef6d5202fc79fc020f7a6260", - "0xb047d214201744cf7e675af5fbd29579c3b26020c5e0a53e2ce078778b3d3a673f0fd87eae8af8f0fba3bf0f8341b63c", - "0xb8aa5364d914020158d11fe82c2b77197ed2b1a12492435200204e20a9209d3c0b4fdb6fd3f0b1db71ee3b986400ff46", - "0xa59a903fcafaa8b5876a3eb1d79a7db17c37457dca018e393324d8db3be7c2aa3ed2303eb3530d6fe1612fd75dd92e08", - "0xb1929c1711ce44465daada15808099234c0c5c8f43b050b2792b6ef9b77825996a74abdcd84d6ef08d648e77cf804357", - "0x85bdc33f8dda0d853074e0657688899befb6356c38f0ec2ac27c46c39fff06617edbb1c5cd220314335bd1b792f1e240", - "0x862047e51f9119f5a0a607469496c0574b0087d566bc58cb5b61a9a841a3cb693b89837a7c927c542ca03d0106055438", - "0x84ba54c002150e5989f59064b68989413abb5f289f3ccba215b923f86f76c19718be51d503ce3bcec68322a7c7d5446d", - "0xadc9ea06c11bf3f0d704b321005020917e731e6706f18a5aeb1b56dab3de39a94fe8aca3c248a47565ca5ce82face9f8", - "0x868324c4ef80bae55510316f3a8b13aa40e60c8a3d55f4994439d1dca6f5928c4cb202769d78c21597d8737e391536d2", - "0xa6e3b57e9909b5fbea2114c352b34235a4d4147417e480580c291308b4b9cd696b36278480893667e8ba01fe3bce571f", - "0xb92e1d6ba0a2a244ac5ae2e7b20e152591c1c466c9b4c658c72cc5985ded0392b04ec00e32291f1652d21dcb633919a6", - "0xa3e2bb4dc07ffb1e8dc9055ab45bf22864980f64b612548ca7feac85ecdc426f773d6d48bb7e6c7a578025bfe99307e8", - "0xaf764cdb70d5afdbb49dddd519451218db4e97ef3ee622585880425c3d85a8df88613f4b51ad40a1f6635e45b2efa5f5", - "0xa426230b8ed77eca3d1ef7f4735fcfe0e51ae37efea5b96ea3bf313c241bd703b91a592f035e98056315c9822ffe8c26", - "0x96a3ae7f1b80690f97372d086d2d13ea2b40802bd053980f73cddfd37045364ebe38064a8cf3531e9bcbfed421040f20", - "0x8cdfbf0663bde624b703d7e6c36c5753282487147e10e5a24fdec75836f7034e4c38f3fa3df373476af969a4f835cec9", - "0xb7f7a549cdfcca30b78349b831ea5173bf5b91d56dbb999b2dbf6b85d8c22ca8a9a62b38e37dcad7ee5136a32edd5743", - "0x82ca90321c43d616670a7d85447afaa9034459b796b51792c970fd5b8f124e47a13ef661291a4ea58a495e68aa36dd87", - "0xa824a36e4e2db2bbc513d39e4e2a841fa81106437eeb4fca9ebd78050667d0b284b7217a35ee3eac67d8be58c0af317a", - "0x9370dd0c0f9c7585761eb5f06e7899d75eac07e13c140c64d4c506b90495fb8ea914f222608c478708d4b47163dc9907", - "0x88c07e19252e905faf129e3e877dff8dfe93e81b3903b150aa33a93a7eda2820a8471be935825d709dc662d06c9f99b7", - "0x81e936c00425f7db8f0dd88b16c3c5208e8d95a5072e69524f3b5de45f4e2dd25f0aba8ef17016bd914bc8f5a42fcb6b", - "0xb23227dceec18d6dda92a15b7dc8623d9928d545db93b3547fb068c99cacb3fcf3d7f88e4357801de8a652b919dd907a", - "0xb23f1627219587773c17070bbb190e1280ab27c5d7e98b43adea0e1f5017790149b71f90c3691301bd514d20238c5e6c", - "0x821b7bff6349c204ce50e00e296982536baff68031165ae4c639122195e7295ea0c82ce66fe32a1b762f6a311aec384c", - "0xa26c15bf1ef4d5543c4a006e4ad2a450d44c93c62c0f0b035698530cbbf925f6705d375e1dc8b2c6fd9a2c69f4126b77", - "0xb5c5bfff4697fe13a5177fd87a8e293fd1c6782cfb3d1f95c5ddcb13c309dd1ddbeb14cd359c9f3029b57ba52996c9a1", - "0x87a0d37f04155bc22ade44f567dd8a81445facff15d643886cbe6534aa44505e331bb75c9ea2f27624154a5890aaa2cf", - "0xad85c0e6345e2333a0ff76b769592f2b24fd0661984498dec6fbd2d9b0cec5f139bd71331a28b13aa490baa7fe27b635", - "0xa9e6298b90aa8d3f4385858e08f393b3bd61376ac3dc44a0907ccfb372813bbfab1388d544c1a4907aac38a87dab2abc", - "0xb5cfc8bbe4cd3ac1a66b1c8138c5c68e643f7f4c310cbf1483f6e48d4f7e2d1cf24b2704fc687032eb03978f18239072", - "0x9493895ce0c815b60b0ab3a989f63c6ba4c752976160f3e52290a724ddaac9075e07dfa913e113807e0e57725b1cd593", - "0xb1e800c2aa32d34d34b24dcf890f6ccde7da60b98c4646a5471fea7cc6df8862b7a9c4c40f38d0554e33e2984fd564ae", - "0x90a18f877f149a314767f5dc15c8726efe5d20a8e15ad4922c6042420a2cd82018be813debf02c6d69b96e8a27c0c5dc", - "0x8fe35142442c103e7bca602445b87cb017c76befc83d66894d4f810e343b3a571f3fba14d94521340ee7c5ccb13338dc", - "0xb43547cfaaae899fc6295f496f213916e5adf9b0d75805c32df0f969fbc1b4f8584759b2a06b81546b48004d72f2e8d9", - "0x9410d55865098325c7b559eb4e84fef8a3ae890e1d6053b3f173ce22e60ec6563041ad8cedaa2dedbb59f3dd645dd1b1", - "0xb127d9e4b8280e10434d53207a7191782464ae83b4463cd8a32026e5d8a7a8c5306ba43ed9b7ea637d65f64d6a08bcec", - "0x87de8fe67524c7d107d7033d4107659206c347c47cbbdf85e3441b53c933417feedcfb049465c67f4c4156219a4f63ac", - "0xa582f976e77b861731595ea8450c6b525e371c6548cbf7911f05560d4c7a4b62a425d5c785190628d1aa1f8f27c43b51", - "0xa195e358742d924fe2a7f955eb21ced7b211cfcd5dc3e598e0d2713c3639b72f986aa586b7a22a75f8547bfb46cd52a4", - "0x97c249b70ca2f9da728a256d18d600bb923005ebad3e1d541ebd580af3fe07123fdf87f4e8f98fdf9dc8ddd826ab7344", - "0x8fc7891e2f540d8f20464a36056f227ac2ef3bcf2b6edd4cd2d9024a48fce19480fba36afc7f5c4bd7234787b8d17f31", - "0x9047512fa27e2d8d901516b5714133c1845494b6f2aeb2a0570dd8533852f00a8d9a8ca64979310e83ac73fbeccc33ef", - "0xa1be9cba454617af0dd38865ec29e7d0777d7c68968c856f90b5bd63a7cc4274fd8b179be54143bed972b921864424df", - "0xb086ccc8a705999184f51e9b45c76975ca8b108b32a3955e122711fc1ee007d8417a85c9cef217f28d6c7799b60aae4a", - "0xab0938a72118ee2980b28dbea9f7100c6f54fa82d93fba8bfa81b6bc34f9d2033a987e5d6d3816fe0bad53cb88bb8c2b", - "0x90fca0bddc14f70282f11998fb4c289fad5c0e78c8e8f9e7a811f20413459a05c9d107ae771e9da501854022d827f2b8", - "0x84cc69b7200f63c2214375a7a0a5ccc14bc02ae45bb6f3b27f67ac538d01e628c66b40e5c40cee38bc8634f1a3c3cc6d", - "0x8a07a1cc0a96e6c6da0d27a540e235c2ab6a95d087e624c90cdccd781a9bea6abc0456d896574950a9e21e7d87fdc582", - "0x87f2084a2f2218d7f9eb7725617ea37db0a19fb0bcfd7c95501f62fec0bb6bde3950690420a40d93e26f426fc622c825", - "0x8c9fc9b470dcf8e576af943edaad28c29f53ac7e24657618c21d910eeba6d7b16f16c418bdd5cea3d639c3919e93b7dc", - "0x8f026883d9d8c7c2a5c04e4c7220ba7061a48392a8a7794a3e290a94967d14caf040a3da3513fd9b4e695376e706006b", - "0x83bef852b9f387a2aed0d3537e77c895799c639310cac98e7b699e9f5d74b2b21cbca58ef910c6583e0b849d665ad379", - "0xb08a03accdc64474490706edce9df7853b78b719ee731c195f70871b7586ed274778d99b84ec3cb8cc0b5e38c102bce0", - "0x99fada688669b2ea8d9b7cd730b057292ec3fabd30cb733ea3f7cb76f756b244cfb26df03b9c087b6d9c58f5233dd1b1", - "0x8eb0fc7ab6b4238f2317620191dbe835d4ebaad0882e22e8f0857053d25d6d9077754251202472d875303669dbb806ef", - "0x8fac2cb38c3a1e361aae5313ebdc1c7e0b7d1a440482fbbe24389a7fcd381169fb325c79e430be170452326cd4931732", - "0x92bacde1472436209032f0592973a5a40d505a9b2c9de648eed1ce392d0c18e23aed9114a9634ad3a7e6afc4ea80ff21", - "0xa28b394018434be07323a2356fcfd6c70b3a4b1c6b6ea44da1da66c389a659235e0dc941019bc5053ca41f10d9b6db2e", - "0xa6d23d7fe7ef475bfe6486ad4a99ea376c6a6db3e70a0a7af421ef6e6c4d6b9cff68d03a7239a56eac784769f63b2bf0", - "0xa1232e6747573e19df98a088fdba57116745612cfdd4ff21f8df82a66c7d5df7e0a6c0cd73117121a516dfaabd0f5016", - "0x8dc574376016b73f6730103cc45c952c5df5d047d0b4ab3da0303f66f43f7d147b5eba5300750e885c621e72b4a64b43", - "0xa66e9eaec79c958e624655fc2adb7b89ff3da0393898e028bb07cbd6511ca8d9318e1d60dc11cf0265a498290e756ecb", - "0x8e5299b661dc0e088527904d2c2fc0256613a1fc2b92bb92c633acf145edbeeb053e82b468a3877f6f14f0878fab57b6", - "0x969943ce7b54f6e728724b26cfdf4df90faf9f9796bafb910ba66d96cf34062fee6ed9121abd193c9e322950c8eadbcb", - "0xad29ce021d7fc875d1e61ad3a99e112ff092ffd7900a608bad30517e50e2270e0f8dc7fb5cd42f1bb995c17d86268f48", - "0xa55fd82520f4d35434066bf93a9601c96549cb4714d9ac05c32e16803daf8763e23c3125d2005eb229bf5d7e2a91ec3e", - "0xa95eccc21af531c5e1a36ce88eda6b87732f5fa680e851bdeaef73421c1c87c8e66bc314b07ab472ecb67a08ec53cd4c", - "0x8f48b5a0636bd89a1ee259223065449523984cf3bd9be78c9242276c848d2140bd94d1a6670e446b51b178ff694b5c7f", - "0x8a58b340e30f0cbabcba1c565b68eae66405fa2242b43a7f7d3bdce279af42fcb4ef58c85fe89cc2dc56a41a51f058b9", - "0x99103a659e26c9a4d19404db4220dcc5defbfacfdd969eb7d70b4fbf9b2c91c92112c0097e8f0c32ddcfc35741da21ee", - "0xa088cc15a45094cffac52c38df427b7144d621cd1d12ae87d74c00a039d757e36fe3cc2fb35fda9b33b375553585497c", - "0xa0f1d15bc388f6602c975b4b9cb23ab83fe31124acd946195b999620c3c20c6610157a891114a43e3af551d7b8c3e4be", - "0xa571057592f3a9008bdf726254c364975705a71bce4e084a37915c5317f635528088a2f50afdbe7240c14d813e8e979e", - "0xa31e425feee58f8372e2bd4c21c48c299850df34044f27db3e4df55fc5e7c042cd19be59500acb375fd3478379f06053", - "0x94645ca6400f80d9a90f5b1c5b515816d6049ab04a552109c9c16b41366a7f3931d49338d944ee8eaf2ef5c77062c362", - "0xa61fba865027b7ccb03a4ea966081325eb64db5a64c5d765d2893f6a19411d40dd957d8a0b34733aeb3f002a4e0279bf", - "0x8199b89ea13ef8eb2f54d27bdcc781a5a7fe5bfef4ba4444bd651ac6021f4d90250b2f2cd8c63fa3ef237ac6eb1bab36", - "0xb39e1e98d07c95a4fc19ab175147547e5a20e66c044f29e4855818db4a7d0e7e2c24192aa8c89fe378f8d8ab3e9f0e1b", - "0xb807bb0069474e190b40bb2b34165351e73a392ffb0de83879ddb181989a22bccaebfdc90748f54de81c41ea244e7ebd", - "0x8b058266df90032a1a9befc7abb759b64a23ab628edd051da8b81db4211c72fd63093dbd94796b0690ff2b0c0fe16cd9", - "0x865decd95200fe45947a4249d2d8551ca5d7b3d7955adf10f94ada3e69d684e5c5b8939fee9a4457f22d21bbd3ce9670", - "0x95fb5ce7af13976320b36422b5cd9dd46379d13110fce619969308ed6a250cf3eb84c73e8ba1d32edc01aa2f6e367707", - "0xa1a176350aed82d5ac01a072ac7f3cc1535e20fb597ebc7e417921537f9bfc4cfc0d490d4df831f0f8ecedb6be970a15", - "0x974ddd091c1aaab7ed356b65c244748a713e98b133c5606436e531c31b31f6ccdcad2037b12c68fb54af4b19bd1d82ab", - "0x8ae9b7a8cd856087300ca90799ec3265b92f84da8ee9e98c6ede1be378dc040d0fe68b8ffc94b146f2521b9fe3d19e54", - "0xae17df60b83e4530af584991b545bf4b3cc1045416dc15250a6b75a9a04defae4c0f60b8bfbeb54c8a21fa84fee58e69", - "0xaca1e75d4a05282b0cbe6256925c0f269a4a8323888bece4a48aa0b5e7bde7fbf1d3e4f5cc38fe6a38aaa091ccbba4f6", - "0xac19171d3ee2f2e5021418c37a0eb25c083de6a6396290ed35b4771abcd07fda745fd082e3c32c117bbab7d9fec2b67c", - "0xad8a35eebd3bb28e08b9ef64bf2d8b75ead69db29c96544d71686ccc0819ebc6823e49b3b879ce0e5ee3131153900632", - "0x9479f12dab191269b020b70132996cb32059ac087e2a3f3e559d297494189e1d6849c340ace032946e12bd4923a3908e", - "0x8885e680de6c158cd67d66c142b2b4ac42b96e97eab8e2dcb90c3b454dd214bc530fbab6b5d5746064b9813775b6d5a0", - "0xa16d8d27d9b2fa04c7eb8436062a53ee0a4d679bb205d7d7cfc24e5f28e3752a9959847e9e31496bb0cb1c11caadc30d", - "0x951b00c69dfd9fc80b17733b843c440c58095989bb8744fc9db63a4a6116f24d5f224a279478fba8cf57753261bde217", - "0x8a693564994a1dd298f0b279e618b46bed349c53236fed9d8e05ad9383ce55fed02b8a361fb8c09ec5ffa8a271cee015", - "0xa09fbd62995a33904b4a34ac55c80f6d4cbd39a902f5db1038d909f1a2d385c3f5eab04b157b5662558bf35ed29cabc4", - "0x8662373988373409a4b31d45c5110afc32aa008bccbeab39d5b09a0e10370dd879684e722a8856b0da278e2bb91d67a2", - "0x8980d3cb8a82b3a827ba65f44e50efed0a6f37d6c150d70e4dafb67b1db173b46ca29d487ef9db92d37ca8312d246008", - "0xa279558faa11850aa4f0dd9ca8bddf69cb98bcd4edfbb0c19f22d1bff85d808e8f2cc026d95afd09fec2d15c116bcf73", - "0xa3fadf9c3066c93aa6a31d2346ad0a1d012c12ca7a24630aee46a087eafe5fa518d20647856d44ac03576bb3a9f81a76", - "0x8a8a19b09417e1b1607aeb54841fa69f58e094b46971c6a5cd0fbeb2aaa98c56599ac242272e6973ca0a9d2c09ff8d77", - "0x858a636f510b494edc76e86b1718228f076b8a21306b02abd086dc2a96c7a034704d743ca5d89b17903fe7b2e43e6fe7", - "0xb031b789e4073b82bb8c78f9d3fc2b901d75278733a4fa0a5aaf985a298269a735217e85eacc0dd554375d610a425359", - "0xb8603ce7cff755f5e07eaeb4d74dff179cde405234bbd7b3f62fd903054aaa34a9b868b04617d7d407c2b8e377227f07", - "0xaa41829c941acb3f9f0e2008e852fe855e153960cd3c85c4b8ab9f97ca91b7a5aa18f997cd023ba9e03a653f238a4f46", - "0xa35639f920619dff592176aad2b4b071d5c960f149c3a75311b16841d1872f29aeeb7c155cc9bff41ea7ee56f799de78", - "0xb252195aaa52e9a34936ccd1aeb40d28fc262cc4570d4f9685da8c591080e97438edf64d4d4d074491344bb5e86b6b23", - "0xabe2e52d10620b503dd1aa584e005d857294565ad90dd89217a77fcce4bea7b0c72d54dca7a1c31b5a9042a9602557cb", - "0x818085f2f1b525d9b2322c8785bf27a6759af9aeb231b0977cdcc7d7e77cab5de056e522dc791e72b8d9b93a9c41e833", - "0x930f64d40ab26be006e91deb854c5b22bf6951308dc406b2c7c7791d5dcec470529957fbcfd6a3c9655d544d974de7ad", - "0x92b28bdbea8c7588ad3a27992c19d73bd3a478b276f0e11c4e82ee2482e4e167cbcfddd17a1ac6bebdd862be65f54098", - "0xafa6a85fb906f5ffe52b6e9715435dcdf9f7892a396d740d67560fc42248d23bef470989663a73190ac9da442cfe6a82", - "0x82d3338e58fb316d66694ff4674a5d99bf0b13204dd251fdec95d48382f2d2ac60176a19e5ecbaab5e00af2a39a338b9", - "0xb30cd35eb15b3910b8b8f91cf04c223d79d587a7ef713030f0ab93f446caeef52c60ada365f8d3d645b477e7fca61d94", - "0x89554d2a9a11dd7e56f0b568f2a50c72d86362d95eab5d94a2386397012e18bef7c9e01a2d71fd325c0692e4d316dd16", - "0xad58326fea1c00e0f8aa92923661be4b3ecc79164d68e91c4d1366c9894b6d049a4f31c9bef6e5f21466ec014ba6b94a", - "0x8915a16afb0e68a84fd12a9203f8f348954920126d88136ec027a81f541b67c421b84ebb3d6e8f085c38c2499c28ea61", - "0x8e246e1acf655572863481367da007e94bc1bdc1f28aeaa1fb163dc05a51c3526a2bb9bda0a14fc6d658d85a9322e44d", - "0xaf83f9ad3c7c1504fcf60084e0948624fccfe3a9892dbcba8f166d0d67b475ce57ba008f286069da20a0da0cffe3b4ae", - "0xaec86d2d803612e8d27a01e3382e0a876164baaf2f3b8c4e9455ea00bc2e525378018e6a41ed9686c6408148e852bec7", - "0x871bdd8c84abeb1456ef73595360de6cf9f92ca9e6a8b6b816ec7497be60a9f509ef2c91332d17cb5fbd347bb0113d2d", - "0x9503ce513df28b61d721fd5e8667272a28f210ef787bee58538f786acd16f04a412387c6f5e6313c43f426a70aab65b3", - "0xb2cb0526e7e524ca9fe918e951c19460aca911d2b5ebf97c2bc74aeb69778a28316dec8916a4e3628b46bc51586c1bd9", - "0x98f52ee1896b632dff5153e3d1fe389c6200b14cdda6b27e12d4a4182763b63e0f587386aed78c97a32114dc286b975b", - "0xabbea974929c9ba70551231e3833d5cecc71c60988826771f792f190ca77c80efee7607dc1d6bf01a53796d8d9b73017", - "0xa4cfea1d06cf840bd599b14c011b6b204b2cf6f57fc7d7f310052062a4fe8870f02504e6c837c2b556c925921e543556", - "0xb957529d7e5d1fc45c5a822a6e0e480e46af2f5cc3801c31996b9b1acacfdd8d142265148b3e1453a0df0c5e6cffc5e6", - "0xb7411aaebb1b6a6a75568f81d052e60fa7752a64c20dd7cd5457f999f0185807987de8fb72ed94ca9d1148c19ecbe1d6", - "0x84be67a5ca80a1fd0f43cce4c00a465f167445e42232c2d2cad5e1097a62d3ad564041a55f0c76a340387503f15e0ac4", - "0x98803688f8e7b445c7ad14277b9f5f12acfba4f9a4ba6df9e2b7dadb726f1bee5098fd15e0b5308b6686a38864f84912", - "0xb085eaa421e566276bcd45d8b9fb71475c4530d63e90914eb2a33c17333d5628c1ec8a45691cbae82ccad97d4addcc94", - "0xa08ff7dc59dadb183dd0b5d336b6174464604bb2b49315e0c42f34ea08a8bca9dc9207750638bb7ebb6387257411956a", - "0x94d72607cd8a75b2fe2e9333959bb9d5b54d74ec36fb8c123c157b19a17f01f310b3311116b34bcfac305e9deabc79db", - "0x85fa61a796226ce555f8195c792ff6f3d483f62dac41c17b7e8295bd49ae6039574896548728fad4ce966be84a62a6ca", - "0x829ab1087ebb61db05c59e3c9d03e7010f8c546db117a6409bb813f6fa04061833771c8aa4c5e2981bd1ee551df0ea59", - "0x97f5c5261db0b130bb8352fbcf65002327bd6d8a7d4fee2a9bc293173c8c54be37ae229c5488c1983bc1f7857c66188c", - "0x8756439e5978ba19e2cef95dc55f706d53a05d1fa964c64d89b0e95470b5344b2f8d44680080626c37c77a00ff0e6b00", - "0x915d33f90980089c33f403ba4fc5c689ea7f1656f5c4e1110db987c59eb981b6a46dd9fe82c8efe7d1e3504f1d2c4d2b", - "0xab5bbb84884ef036c9b00a84f7d5ffa2931854e2afa5a63121fe64d548531af4203495b977bfb9301bb1e4679d42665a", - "0x9830b846a9041e4539eb858a179b4db6da89b507424e6d858ca4334d973ddae255bbfb04ae25c3276ccbe97c46f5816d", - "0x8e35f4563b8a5c9a76cc1da87ab21cd894de393dd61bc977cf22d3de454de350836e032ccf7d6ea55e2e6b83c4424146", - "0xb6338ced0f05806c625905cc51b7e772c5db3bac144e897339f67b6949f4d648d41b7d23bd3f299f4879507951ec031a", - "0xb3afa470fc71b92f415b879a814feb0702b6adfa08e395cee4f7d8b0e3537288f16c83b28ad4e2db02c1fd6d39e6afad", - "0xb4fcf7af3196bec84fe1f6e3bbebb8abadbcd46de02a37966d0ebe20972fd890803d570e4a201f2a89f479e09f19191d", - "0xa21fe1f8f57691165d0c7d8436765562cc935288f24fe765351be335f906c6c4dd1d0714b134c51255b14511c957319e", - "0x880a3a8f6b4ba410be06628a011e6bfd38d86919cf8014b4b4e1c930f8e3035749579389690f21bddc4d4699de8a4b1c", - "0x907d93a7666d847a140367c0a0ff80a96d6a8295b07cc4ee52d3be987f431d8dcb95d3717dfd248a5643c5395ec2891a", - "0xb8f38c78b8a2c487874c1a6a92d15cf0dcfd26319d4cf65c2f4fa9432203ba5ffefb02b7324022c34bfe0da369d96d65", - "0x8bd4ebb6d720fe52d626a621670a06c8a304faefca3846df1f619f4d456e14f8bdc547fa7210b8639b89c6584ea5c5d3", - "0x8ebdaa288a71a2d3188d6294ad0948a4f72c1eb6a2e921ec82cecda4d315a86e3e6233b5ffdc7219f34a99e9b4555317", - "0x83320fb9dc62119655bb0055192471ae06b7641dd4af64670a4d9475155733555ad06a93ad2fae72e029049601780654", - "0x80b3d022738318238dd32f122bd88cf2f734a61e315ece521e9e038f4a9bd7b54b5e67784f5949fbcc5fa911dd4b048a", - "0x891a23b4bf5cb8558b4540b66fb6b9fa54e9d0b2c084f660c8bc77af5ddb97cb5d8042b538f61330d9fa8ccbee6c8a41", - "0x8e5651d9c95aee23835bb1a06eea76efc9d5c881cf87ee847ee5149fdbf3d67dcd8ad0675dec8fca6cef25368348abaa", - "0x86bf1d094bc4fc7c21b21cfc7adbc750db0b27c35bb160d306b26fefb2699cbbb1fe624df1b9e7f6f895f1b81a829361", - "0xaebc3cb2623344315875029378c71ab7ed3cdc9d3d42d4b835b373c8420adefd177a44e532f3f06f74f0a40d53713e5a", - "0x9527f659e93a740b4c50d0d3d9aaf1a85936f04866ffde1aed30ab2fa1c1d565b46bec5fdfa510fc3ea934137bbd46df", - "0x8488673a4bc29c3ce9133cbf41c546fab4ff28c5d46048a21e710a8df4f2bd1c77d0ee242dfd962a30d646e5ebee8c01", - "0x8cf29773c0e0fdb75bf6f52d7066e7d6e9a3ef056bbb70a98026464b32316189addb5766822f57df63bb68b78c85e1de", - "0x810c6c1aa53f9c3fd0018651b1bf25215fe24687b568f21a121e0bebee576a75e5f0d08ac9c6c21085e52228b314c6f8", - "0xb529a87fe47402aa9ddaceac63a060a6640418891f931036c6e4098a1b308821a6f1a244fd5c1c22a6ed5f72f6bcf825", - "0xace9284ce89b5c81049d329db2376a85feeacdd9f735cf00038adc51865bb78bd9bd5d060efd0b727c509ec19988f99b", - "0xa2e7a949c951bddc99e68d80b3f3fc4ab960b682229fdd794f9eadc80bee91dfd5eda0052149d05c74fa33bb40d75ecb", - "0x86bac22daefca9143e0b1d25534900b1f7711ade4437642043c4a8c65f0d963cd1f0f958c7391e5a663dd3c59ed9de60", - "0xb7d2a6e2d44edcaad19498ab3368bfb87f9ab039cf2249d6e76091dc3db0c3bf45012779c02811cc818e95796e6ad9c3", - "0xab474f74e1ebb3dc696e7a6bfd8845ef15fb6411fa28426c486f7b0f789a6af3016ed5f7da2a36215729f5cca0b36b4d", - "0x86616a1a9dcb50d1896f3eb937bde67f213558feb401aae9898e41cf1fe33b443170c7c2dbd1648c9e3cdd0c24289286", - "0xa466169a2d95a5fadb6a69c7061cd2911c3eabc0b1a2488e216f8cdbd2c5bd87e80908b002b9efa51a67f02d7af2155b", - "0x8368af8b7c0f55f3c4f7036fbefc9d6a0ee9ff61197cea8ce48546753bdbc0b61eab604b8fe2c1aa91ced7a879e5899c", - "0x996c91779ff9767232ae603c5b1da5bbe0e303c4c2c72ad2d5944ee1297af3535f6bb3548fd1fe8a92cf4b281e1d83ab", - "0xad4a93d1ceecedd27389c658b38dd71cb13c729b27e490381d8c3ed4815b11ecbc37b1f82c0656e0ebf77e5bc35196b1", - "0xa3538c7ea3dddfbae80d67caa9fc547938bf77114559f9fc5180d9d0ab837d7fb1b27bc37405686f212f2e98b0028e59", - "0x8abc9fe135fbd48414f2ba28344d9f49ec2d5ce94fcb314ab8dc31c754f3ab7e6ab268184a67dafe8b1fb811a762c112", - "0x99ace100d8db88a83f1727b7b48baa1cf45b971d08112e452f5003566815ccba0ac3f8b1df6504f55a392efac8e3e70a", - "0x91ff50978ce629651f1501708908d75b490c18615e933191cd37613a83d4b605b0b48d024d27807637e662056d76276e", - "0x8e4104331ff1a40cbee9f489a814cf5bbd6fe4eaa1cbe1e13625fc3e6697b27d933265e5ef8728cfa8fc4ba5b19a614d", - "0xa442360d49bc9ce3e75eb40bf2ba05e9437fa594e8b8de34bbc822cc7b706dfa0dd10bd6bccb702d8556cd1320924821", - "0xb6ed6cb0aa34d5793e929e0d9b9651e41da3457a0b20c1bfa93a8f65bbb65bc07c7507482d71c1c285f5f663ae60019e", - "0x86d64df2dcd36d0c7348c77480c8af33dfd889bae7bb045888eecbd317cf3e4731b96ac18f410a99ed33a3f22d448f77", - "0xb8dd25415275d5ef8260bf5195ddb9b15b78a8799e4d15cca7d6317a18eab7bcb8fc759be61360915a28a8fcb5d6ddfe", - "0xa325a334c84dc1d9acc0a9315b399d4be93351c7049f474702ab58b4cccfd69aa50b8731ffd598ef0144ca96477c273a", - "0x9012a2dfedda5147cb9ceac681fa9e75e2901eeb3c94d87465a44d11250de4bc66d1e00ff674f2da1d800b43f686df9e", - "0xa1049d59da2a942d4d2aabfc0d972ebf3babef9c5d8fc5598ea23a048c2e58f7f17b4d860e437276bdae221d9e41e3b5", - "0x8c9d9a8826c812943d74c4d4f0fd2f1c8087135c37bcd9647b722b59801b01775a644e38c24b43e8e70f83bccc4afa27", - "0xb9cebd7bc7b041c18bd35b970f87e9b5183e4ace034e21117001fff8a05b2a7f9ab65cf6ab8b818b8192d1db5649312c", - "0x826710d6432ef97625db25104fc8dc3225bea594a10cdd4473d5ab72be57b74928ff356d210032a61ca590bc68509880", - "0xa18422ceb8c61af305277628e154d3a9c49f47e230a44c6216128d73db7c3ca9eca9f87e29cb2126f1c312f423c61463", - "0x919d357886de9eaddcfc46cd43e2b3dda3f82e926a3aedf02ebda9159faa00736bd2cd7aa044c76ae238a3a95a5bef38", - "0xa822d5a726f5c38e9d4a750ddec80bb965a6e5374a3d87757e2e48a18421f3142c3985450d1833f3ff4ca36e3b838c89", - "0x86bfb86eece6f6ea8f51985e312171b9bc821e0c3ab4cace556da28dd7bf89cfd5be3fbdadcacc19f2371c6a11c564d5", - "0x91b42643b297d8eb2c1bb3f16b57ab2964de99dd22bcfa07db1d0010715ebde94d11851df575f4f1ae602644e454fe0b", - "0xa5e444ed3d5fb3c5afd2c9c24d676adbf396f5d1d47bd532edbc72c83845970582ec49ed026b3b982c9c1ea725192cfd", - "0x8448387a14d84aac6afef682a27be67e5b05d31b59346748d2940072eec771adb53339f335daf4463f555da2d8543f18", - "0xa5034b66a26bad0f753be56dec722fc98a072bcdaeab0bb9cf35a56a573d9424cfbadbbaa8ae30690f7c6c6495331fc8", - "0x9317ac32da1772099f41564ddd8247e3532528b240db753a1fa6fb35cc039c6a4ac4546597bb2fb28721684bdfebdb88", - "0x8b4b0001a6234335502c8b17c4de274b83b0610960b5c46b9075c6e41f357ef0d8c94e9b14bff8be7849435512626ce7", - "0xb1aa903511fe4219acabf8761a8e4316cc4f8955ac8640c68a7b547cfc65365a8fe0843a4098f9f17a4c9beb75624393", - "0x8384f4953395aba4939b24b0669853df78f2fcc01b2145c08d3fc333ee2a7d4adc12f2d81c37d0cc187ef45b5f69f59d", - "0x92beb5a3c14637f84ee7a3c9b4d9b305b10af8963c087b86047e9fa959f41ff362d56eaccfe887bad1ccbedc488abe2e", - "0x8c60e16dbdfed2d1c8cf3f1bb0b0f462489293892f9d2e0221b1691321a771b163fbb599daec4cbd917da75f5f662de7", - "0xa8a6e3041a0c2a12c76f51139b221b03ccd1afaea3b72ba2c3533b797d5f67d8b90d3474b4f6f8e19a77894fb90842e4", - "0x966aaf74560bd4d164ee46c7d393b2c628e307019ca4289dbfb6a9a991608ad80efc1ee6e9847a19382ff8f3004aac8e", - "0xadeaec475d4bfb6075be90cc37d61d45ce14da77f8a9a508b9f829ddf2abf91683aa2fd0372d3025a660c94b0f612685", - "0xb3392bd1ad0c202d4a95912e0e06d8c64d7e2a8818dba8f895abcd0f6932efa9a0bff8a2aac107046d3478782fe42d33", - "0xab38804443da16d32f11c0e364449ed351dd36b7c82b5c7ababcc33a930acefe09fdb5261da04f6dfab29421fb1cc017", - "0xa34e0df9e953841bc44c09e16d69235a26ff390a6d128339ac97aaae5616865f86153d8d7466519dec6c52ad592dd3ad", - "0x99581db106391e6816403b1e9d13747aa05bfbfa5b46696cdfdedd1627b60e1ddb92215d138e007770512e93bc6184f7", - "0xae60c3b1ae3594aa4e3f08eeba3951157921aa6511148c6d32003d42157654d4a3a39efb1bb317135620f09729d134d0", - "0xadab0bc35ca3fefb14729259b16907a34e10ddb6d78a23f28596d3d9b244709651be7719537df33bcf003c0e43bb1a66", - "0xa31b7b2f3411f986b3415870ae42f90bb678a9fc44c942f6613cc4f90f3dbffa4b5fb8bf3abfa4361dd8e396d9a3c5ab", - "0xa69b188a8662eee48fc98201fde6f0d14f6b54db83ab79c2ec2f4b6be809773231704fae2cb281fed8b05107c63f2fda", - "0xb79e1e7a9045af6537981f54dfeed0a1335606301b73eff001880798f01ae9c0fef6e427e171afbb1d0a78135ba912cc", - "0xb1b883fbe379995b3741836a849516a0f21b18f42a34db2c8cba01f86711a2baa5d14910a110f1058e36431dec163cbf", - "0x87bc463b90123cd9e177f2284d72a7f4a1d4151659e1e4e8900bc21986f641af2f9a3386aba56601e6fb64da630b84a1", - "0x97a51bb7d717495f943db162837d3bf700ee0653da9a94b36153443584602156e168fde97d77407d0861641d8d373094", - "0x8b02561709564d0721b5247775dc66c6c09cf68a8ea62fd7dd361905ebcd98bdbb2c554fa272de71c6d22b04d33e6902", - "0xa914b9406e71c09deae875bbd628af3f54de5ccf811365cf229dfc69541d996689d05679eb02d42a0adda02be6e32d2d", - "0x85dcc5f3f77f72cf0818bd04c037cef560f0b0eece3191e06fcbb54228d11f7afbb8d9f8675b404bb39ffd04a3b65bae", - "0xb15bcb96a98bc6cc7b802dc29b79a903223b1712a10a22e776f45c530a4f767665dab1a3c6d1b52157f4b79055d5ac81", - "0x965e353e665b3734042b61951e105c1800718eb3c46759952755321ff5c639327d045c58fe90befa896e96b826910298", - "0x96776a5cd26b69f08a68af0201b2f739cdfb9553b466271063a6c8b8307f2a3f51294ea12c7e8118c0e6b884886e1bd9", - "0xa369453bfbe7ef0b2445231704abba25527b073bf735a968758975fad789c74110a573bc7ec50001368209a0ff385500", - "0x8e54dc4f2a557703b2d8bdb74ff107bbb239034ed363818197b2569c03572c14cff21273e94802159563d50205edd651", - "0xa1c66a1a82c60dcbd139b8ef4de62be423e7641a6b94ce0d0468e60bb1b000d268755946a028d3961d8b4d3722016ad1", - "0xb14b3c26dd9d17d6fd8eeefc7f706c177ebbee9b8d05f9b01174deb37649f77f97ef1a1abc0cd4ca7a13618a4036067d", - "0x8fe8f9754c5ee102bf96ba6b6f29a14fbf83cfe3c5f81b5358ccd4db87fd8c5d88760172373bdfaba7eaac98ab1fa863", - "0xa8c308c15242bd9c7b28e110715315a1f9818ebe03662027a6f1feac13a5dc9bb111d29444d13546d8e441e49960b0a6", - "0x85d87035d74a1f4662f42a8c6d26782daceded0aecee9232b78139b1b50fb764e87cdc5d1ca9d6905735dd9c3dd00dbb", - "0x986c31370f594d4c8a9096c091cb1484c1c0755804819a9462ad1b67937c6b378d97f1e4c27900405b21de2646be70ca", - "0x832b4b427f3347b0073c24f54e17ac16d5a80d04261c1d464f89dce89f42750d69dc8a83ee0481e410f94cf1d108c0ab", - "0xb13e54d91d5d414223caf43a7fad36930300594b8cb3ba97c0c873cfefedc165d05f05deec8d74a0412d5f0932813088", - "0x89c931d595849c8e54f50d550ae4a5d71c4bc74af436965bc32adbfe829a48ab15c856a69070b4a4602e0113131ce4cf", - "0xb03731793db466b74112a1b9dec3344fa952e81bfcc7fb2bef3cb20f566c3b2bf60c16a93f84f77f4c83d8f2a535a2d2", - "0x92e8fc80d49001139363e3201c93db8326c41322db51937ab641ee7f1b2f7d03089e20eab19afd27abc23de039ab4b0f", - "0xb27d95c90dfa91886aa91c9c8c85ce09fc875279028bef49abfeaf44437a0528ade620c8c2b3d712ab594e73c5c032f5", - "0xa42e2598731a792975feb5b24bf00b1e7cba1620922f8c2319dd5878419ce6099663b448299c0623ce400875c48e12a1", - "0xb062840f63b555a254e3bc36e9075d57c816ed2e9cb0e262f9de0f3692456d94eef702489e5b11c9746b949b5e84c06b", - "0x886226745d906664c476615dd41deef6c338ee10380657fdb75cf9ef28b4d9f56e69c8d0ef01e9cb80eeb42f3e5773ba", - "0x854a3649dd5b22def4f246eb0d1f1a206d3dfe42b5e44b5fa963a7c5b8bdaaf7f35b542b3e9cc53187e66a2315ed9f9e", - "0xb5a48cef68a056955ef4c080c85e4044e9f8a562f2beac9fbb5e19f8d618718c86794338c6dae8f94b6f5e9f8e98404b", - "0x8f8bea7304cab80d0009b417c198bfffd166eed6f6db19f28b7616e8b0733cf0a4d54d204361d7f8f179985c8c3a16ad", - "0x8af81f10339e2f75f6b6fe22a641298bf90c8676260abeeef90bcd52f46ef013f5aa4bd9d0b5ec15be61b7c3e0f32350", - "0xb0397c64034598c825f9ef653ff16f680325546695ee6e9c2957d3c87593161a063c5219304ce6a16b7db75f1a2c5f7f", - "0x8d2e7677ab6fbe2b0f5ab6dc356190bb3ecd7fc468c698d512a6c69f22ea97b71fa961c88635897a5b539ea51b70b4a0", - "0xb4e91a693cca1007fdaeb7e679c6837bb8eae0bf61aae447560ca6eb5ba918cbd9952b41769657978413106b359e169d", - "0xa8331a907ba7d95a5e4090a7680d1bce3cd803db49fb84a48996e96514701de1602c4eeb4b5e0b1c2a106c4f678a72a7", - "0xb54dd28a97a5f934a34c2817af91a41e57f88d7eb5fb584d3b6692f2d1c4b2a4e273c4de5fa33a0fd1fa02c9d7cd1fb1", - "0xb8b780e0f6059ea27aec9f3693ac9adf0b93f75fe7fac5230deee1e7471df0bce9b5b2f260a6a0a346afa723860471b2", - "0x980e9847ec83d61442a86cf8c7464b357694dbe67aa5de3a8c88ccd1a038256453101366dcdfe11a565065d78ce89807", - "0x9350a17e397bdc3d2bfbb84ddc79a48bdc6ef5c3d8c1ea39251e762fddf9962b69cdd42c563d03f64615b72c9dab07bd", - "0xa34d24b69089cb5ffc5f06eb2acfeba13c96a1096d1af9620aea28753baf3d0aad0bcb30037ef3a5ac36b178816e878d", - "0xa7c8b9108fceb4e0377eed48af9846530114df986cbdd35f6d54026104fe6bfb3b58e57fa2b3a750225528f8dcb8bb9b", - "0xb0f71f6a04cc7119db96033f89530853d58a445565de2efd269b1e3956397c35a49c328102325b780fa5d0cf5adc2a4a", - "0x92be082f04722fdf3abca7ebfd162b7062939c3410ec204d5478dc8de2bae2b25e2654441d29fe2c350895512d333ab0", - "0x95e7afbcac22dc2d04c5635d7a8c6293f6ce29bc6c932850d24ab5216b449251bdf7aaea838ef40e0e4eee1de8f63bfe", - "0xae0a877b488865f21194470677e12ea7e357c5d63f6bc454f608e33df9a3b20e9aaea5b6aa42e8999779b8b445831c39", - "0x98df977479667e23b897b91f2db8f4cdee7ece7fc3ecf8a07d752efae090d8bd34d781353ec1394550d8a207bffe582c", - "0xaaa0f1bfece62a63f3bc76862b8789e2593b4bb40b3c413956e9e5c4eec604e39d722cbe1db210396eca7c2293489099", - "0xb362703d2b72909d06407d139531fc144e68ba94e55643cc3cbb9ed24145223aff460b1627b41eb9a3b709978aee5a58", - "0xb020025128804bd642fdb1d2b70b07d181e5ba30a5ee16f6bd00d7e2d0c6af782e454cec107304823be61647e65221fd", - "0xa409894c0030081a2c7f8fba27bd0ac53997a31b35a33498d78bbcfa0b7ec0a89b9efa99dc1b8770ba889060f39d56e2", - "0x862f9eace3f54288749ca8402c22ddd7829f0454d17ff4891727c86eace899cbf72d302065f5f581169f00186c23b4dc", - "0x91432f2a823c3ce95bdeb5854e8dc7faea5031fd65c82dc69e4adbc5ead2e5a5b58a9cd1428d3f526cf94a217f37d7de", - "0x9543a9038fdecaffecc4d3023fd67f7976dcdbc7014e82edb4573479b1789b4c610c3964643e031f69ac7a3e3dfbe241", - "0xb4f31d580987f47c550eabd2d276678a294a612ac26806a06634b8812a571391151d84c29b6b82218cd84dac85bdcc88", - "0x8d922ae4eecb45ebc23eb1a8404aa1524b281d0f0ceda58ea93a0cfd4184efb894c047f0a46e2d007704f5506544907e", - "0x98973979672d1d52e561cae7331b730a577c422258c22720edc344aae35ce071be1b017816d58bb29b9cf5c433fd64b4", - "0xa46be974ea72c5e5bd16de591bf27087d97b9030fb4a74743bde5e480052a0de58bd962dbbf0e0fbb0559566c3d9780b", - "0xb2b4464973322d865207949afa4dadbd888c9b0230737561c3b76a1953a85ea9439fbb1db9d0d42083c62419db512450", - "0xae811a9eac5f4ee6cb3a4dab456a3e5d95cb1ab303c19e76fc4b36ef6b4c83ec0b2803ab8680ad1663bdec0ea2f19aaf", - "0x95a426f3d2ae6c6069f888010bb20c392bcbb65d0986125e0f0066d4206f4f443f70dcba8a789da042b57a36980e75be", - "0xa9ec01a5777d10275153ba7441f2e27ba3d6f1a875f204469220ad999bb8a0372369278bf5a11640ac0709771b814a31", - "0xadf1091e24bdf10d848f1a0920eabca0a2087220fa0c3f8e5b4c72ca0424ff3e0c77ad4c259c12c3cd1c0eb0cf32c67f", - "0xb9a57eb8642729541088164b9974775934d7a4c56a3a3ff2a190d549b294fa87002810f31251170b0407c7e9695cfba2", - "0x8625501e5c56948812b72b3495747412e03ede90096be089cb8040069e49cddfe697766ee72505bf715802fc77c08fa3", - "0x8a745aeeddd1be100474d96aedc737208ef19a93a8ad72c10bdc0218073fde6209510048eb46e271553b04d8e6529f46", - "0x8b8d9ac3b91ac0333094c85e81fe2b8cd5c2207073a33f66bb1939e8f1c84ef064a8b2ee984a9f450f0a6e54bb56ccc4", - "0x8cace31444da99fa5dadc7c46f689fa427949d8c089af3d90c604fbdbe0dab052392fbad4b5aeab707e4caa9e739f366", - "0x8750c8bd1f1abe5acfb29ecab0923008cb4455ae8a1db01bf3317df05e1e02f9df3c74e879d9c57b8f59877591939ab4", - "0x8904a39ad86cb42c06692d4801b3718bb63a07a2dc5ef13de16f668b08968db34966457ff2e4cb770dc61a216f4abc5e", - "0x967d1750b0db53e977bb9ba65aa820d7970f8c75d5355cf12a3f4c509dee7e9b6c0f7a828474b167c25b15d74f0e9cb3", - "0xb37297bb6c2d9175e0a7654c5bc6d248f47f7183c3b10375f07e21e9f3e66f6581caebfcf468dc0f8c04132a2a0ede55", - "0x803862e6fbca945cb6c0ba202257df5c7e1e1fadd78b842970206575f70c9757d4a54e9b1a0a269dd37c4f830a40d2d6", - "0xa7a27f2fc7a1e6d276522177f0ae6630dcf5205d08c19319c315bacb165b687d125832d97ed689960885bb7cf42fdf36", - "0x87fbc08506fdf980cdd534d4ecc4dcfbd381f3937dafa09db402e07a67e1cde579e680d3f77865b5669f35fc00901530", - "0x8fab8bd57f76d187f1cc22e40b51736b1b0234e70813ca02559ded9c7835cb3dc71a24c8f679081510c32f330d6ca45b", - "0x8fb917b7dd71e1728bbf32fcb55343890aa6fc890740f41f42e9620b5bc3ef8b1ec67d9c047e4a9de0863a5eec18e5f9", - "0xb7429e758850bb7f69db000d49763df43d18af11460ee0f158b741dd6b7575527c5c8068cf54f7f78098f9ddb92a82db", - "0x8bd3c73c1b6f88ed2696d53d2a0617f74bfada964d2eef2afb5e1cf00bfb646f552643c55d5453cc622c9ecfb23ad492", - "0x8e025e91b30b0f328cd6b79df9603698f1715eb6209e64ef8705cdde5ee1c4ec737a61d9b8a4e72e83b2157c621e1590", - "0xac0b91bbb5ce5bbc8e8d6c0d9d4e51b3960169c608b6220a352aeb13072133aa9d934b4e69d7c5c39fde68d467fa8069", - "0x88255d08bde3b967dfb1dd338dfbdec12a2079851aa796be070a1d70204048c34f2739b7461840136b03429a8b83b1f8", - "0x97a83477e765f3f17eef0d3243ba9bbdcc50fc581f904e92a853a076adeba917279fc0e01aeca42de1aed8af9579bca1", - "0xb0d9f1afb807e0e6f839632393edef25731ab2141cfa1cd965e986940a4916c8788733a39def0cf67afedc516dcc6ce4", - "0xb563e9ed9ba2134011d7bea6314af5d71f63caa1bcbf878c26d7162dfc76343c38308955215252962fd0c9c87200f1f7", - "0x838d4e97bd67822c22cda558f0d35f971a0ab7debd3da3f15c27f7d4b3301b2c17b60cdbca0da8e067f23fc97d136ae7", - "0xa7bccea326cccbbc4773de402fdf2cbc21a028197be98cebf6e691a7679fc948e6bc4981a14fbf493a254eedc444dd7a", - "0x8b2687732f7aebb153bd6030dfca0b6d257b8f2945eb8221ffd36ede50d463172cfc4bb17dc30bd21d8572aae2540d6f", - "0xa4a3e87ec5984c3a755cb39fb04472583a0d2c771552b0701788f03b4618c87690a13a905420358701777e8b5ff2d190", - "0x904c4dee5dfff129de3fb8cd0a6e0576c18ed3d77f49b8f0601997013cdd4ecadb86690389555ebe8a436d8084024f2f", - "0xad1d9c7a6236b22474fe8d45fde2e1f072101b5cb7018ac73c0541c0f9ebec4d5c4469e0570cc188cb5f5ba1d8958be1", - "0x87fc8ca6f737cfdedee973f1586b94d971564a1fada0e4d65894222edcca6552764f1ca0b02782066f19f871ba5842d8", - "0x851829a8b39eb6b91523548ad600bb876408cabed98d30958056367c686bdedbc876e1903b8af8ffa3d3618e3580e3db", - "0xb99270859bfe7d8a4c1a22e2d88a644dfd2f100c54070ffd9c4e1044140fc0d21d62c61214a2b81a9cfadf944327ef8e", - "0xb89a2ddc91c59dc2ed9b8d11e4838002e731b0bcc576e0e18106238cd3282264b14cebebd8a64f004521cbdc690c4182", - "0x8be96bb11a025d265b7b3ff3151e9e227a8416e6118595ac29bf836ef751504eaa3a9cc05bbdcdeabde11f2dc3d20c3d", - "0x87185ed410be571fb532e32d0ff4ef68e98ba2d3d5fbe92180cf1fe8ddfbcc89fd1e03694a9fde1a12ab905db89323d6", - "0x97ef023f71850ddb282f244b3f39579eab69ce5bf3fe5dd2159329b7def4982cdbdb4e71476471acfea0f7ba5a7fd061", - "0x9944324d804fd3978e7e137e6e65348d7473ea23c591136d55779b5a31f45f9e4d866d8a18c76a3a0e8cf2ee61cdd041", - "0xb9930c9aff260105d4d15fb749aa33436f6fb52cf9d50e39dca19d9cc7938d752773f06756af86369e1f5fd5aa71d5ea", - "0xa85ac6bc027ade2a9bbbab2b231241cbbe56e562fe621ea19208a8ea36e1baced89ec9ab8e2f83b602539e5c053f5764", - "0x9917d40d37549caae646848e18ffcb49f5c6c4e396ebe7e74129a41b0cfe2726b4dad34d51f4bc706063e654da898824", - "0xa25f8a4d8ab34724a732dacd2b316c80a6544d4b8c1f45115c4f55c3efae6129b83623ffb31da80e2601f70ca51ead16", - "0x932b54b2bd26670936843a92346d231f2f3e3659542f4d4def73fb36ac0350733853130a5e5e9d8e386d34f817f5a91d", - "0x871bf29d7263bce62a02690681d4e1c3c2f9c2751de4e35810ece13c9480eab93b80a00230ef0ffb858a829ee6bd96e2", - "0xab9643bb1c32dc2e8c05ef49bbde9937072af214c19c3932be137b7b75268edbcdd81d1370089be44462b8032bba3c57", - "0xb67d510c460a2f14b7cebaf9a15642a14b2542c13ebb1d1690596447ddfce6a86327ffa377c28891f6bbd8febc2c17ca", - "0x93a5ad5019a8e680bd053a524e0ffaf8cb18adfcdb22ccb6cbed67012316bcebed65294bcc0cf4f4e2915dbf19ff0948", - "0xac7a7fc1140b1197f2aa424b053e8ceaf48abf41819efaff87a2e63cd6e962c278942c2b97742ffbbedc5cd426a8df50", - "0xaf0115d9c2f887ff97ee15a1116ab06af1920f2f42700b75cc010d4c8038eea941c9bcc8e7cf4a41036813143ab3e8eb", - "0x90c768d880b6ac17ed7ff9bcf76cbd5c1c4879247a757d8cc8b31c4c7bb0ec763d746e6e06e361afa8ee158e36ccaffc", - "0xb3f10561432a97c95d02c1a6f317bb1ab5b98cc88cf5d56e1492ca84eb2ae1db92e9e31fa454de562e719b71442e69f0", - "0x8d94729b5fb0afc196064991f9b3c8e04c0858199aa951f49421ab890079804179fe00707978f15637b8d16246794001", - "0x968515d07a0f0eb52adf439d8f70ecd1f6655072abbeea45431dad26c8937f4aaeda552a22a420598d2136f576a966d9", - "0x91f50e6f292e2bbbe226b221cedb9db36bcd459bfd74fd6356b0620766d96869266315e8503435af1719d8ff765467ea", - "0x968b328d79e183ec560e8f0de113298755cb23a893a631406defdd26ecd52e4b6f28934ad2e219382837fbb8f87f4360", - "0x94c126a9035059d2d817c4fb16cb13fe6047c356fc495aeb369acb1c45e89306554631f50d313707e82624b6107d2fa0", - "0x90ee85deb494043a1cb280d687e3f55345085e576484308755df1bdb6f734e7dd25fd2489cea746be5d2c6384e7986e0", - "0x92a4f64d98e7e633271bdafb1eb88474013b5ed2c65137c243729df0d79ccdc6b210118ed3587ad00d3f0f757400e47b", - "0xaf31031fcc867a53760216cc1f467901aeaa3b28438fb3ec90d6a1c8a46590062c40fac939bc3c7e7a7deff8f83c262f", - "0x94306afe09f20d5de9ea26f37f5fc8df1e29b3a6963caa94df031efd428545493d53e0d8d7af12ee525e2e21cba23324", - "0xab6285371b981d5443ecea699af9da916f9320b3ed0a11c15503f3b10eada3ff5dc95d24a54f5aaab97d3312de5b985b", - "0x8e9735364ae128f790dfcbedcfe0e11b991602dce0c90ab3cfd4aac74526c30a798fcb51a8ebcc5528d88c724e9c2b02", - "0x89a3c12bcc68129b14fdc1d9b5db8d26d11c6c9467f5bff7c40abb8ec23b193746697421ea4400d5ebe53bb3fbfe59d9", - "0x8368e44144947f9ecfa5d126f4a57bb1d3728fe3c5d3bf83310174d638a10cea02ae79fca91d5489ecc9fa679feab49c", - "0xa0474ff532e1a6a3dc8f16ae27e77d6ab72b62535ba0d3ed09da5c692c6fd34424141cd68470922e1e147fb7f7479d5e", - "0xb9ae0e47fa8d999135f78c733cdcad786b96087a340f86e4cc2bdf019b07fd4a76f9b4b041eb397f61bda20c31d27838", - "0xa7262ca18a7179924d28161d64e6b6cec5da35b7eaf695642dbc127a7bf4a52bffad82b8d3fcd010b308dd72eb567f26", - "0xa23ecfac8a3f978f9ca8810458973f528846de6bb92fa6422b9547d55d29d7db7d8bdc5181e9ee2257a458466f714449", - "0xb04c32403400f82677d831314956acd3cb507520ff14d856cf8ec4fab37a4428a5d24ecfabfd2c6086e4ea6d26b005e5", - "0x9669b2725cd5965305c6ea48331e693086f4c1c4ca7dec26bc6290e9a8e70f9f0bedca6e36985c39ea35b412abc7f4b5", - "0xa6f68cecace45317a758658463c5fc1f005283d8c3d3de9364e7dea453007d8d4bc849a21205d61ef81019e0d25858fa", - "0x8ee19ccc1c83b2c4d7c7b712bb370c129201bfb340c5b49d991207c995f870de2d0efaa88e05bc9eac567c4c36e20175", - "0x8a530ece1992d1de92c4e845e71a1ab24e53a8a0679aa5bdeefc60fd890ca3cee2121f66c6f4b29c437440e7644e65d0", - "0x924338d7f356da9b8477b3aeaad6f754a8d8f6a791d70c3ff23c2a6d4488efde9b9fc351319f3ea1f545dd11cd23ab76", - "0x8eb76f86e057cfe9f655ba29bac89cc97db07f0487c86e7b41555b5549897bd3d042cd2ede35e312cbea357df622c6c2", - "0xa2c0da965489d15ced574f5e27cd4781a1dce8fa4f17762a25fef1320096b9eddd92a007d58a194ef57def3aaf4e925b", - "0xa3fc89753e8896d796859c9e5a00d184be7d37c4d5741ae8a60cae9a7a30c5d840325d6479701e1f61e37065fce81870", - "0x8b2f90cdb3add567b94f4c7fc89a8a57a0f06877639c33df2697f7c39e52c1869aadc98a2f8b85a58fbb02bb1bc1a441", - "0xaeb2c22d9186725ea40d3a4bf551482bddeef42c0ad33801e35361d3695769429449c2a13955cccab55778d3ff29b664", - "0x80bce007abd8ebe2238d465a312c2d125d1a695184b93108d728075595c7716786f9188e90ae37fea89009d087e71b07", - "0x86f5df2b83383c737bb6db4e435f496ebfd56b51300612c402bea9ac2f439ee7e98cbc2655d31646472ef983aa6ccbbe", - "0x880e8a19af5ad76f14cdf94396b8dacf061e02eeaba02d4c29ddf0d07c6d2a737c987d69ea2eee52f0db5a4dec318932", - "0x8b82368968f9b5bb175c95862ad403beee68d199a20d5dd618395daf11ff0c2e1fbf3a31c23d3e582556276b44e70b99", - "0x94a062abbdc5ba740077fb9de722ad2ccf3f6ffc8b4a9dfbb0bf2ff789bd529e7b9d8da520d0342af91808fc00431638", - "0x890b4ee1e9837a4c215616819dadbd3c6ed7586ad391498012a54d735c6df0b72c2dc3969d1b24cf6fe822f37f9c10e7", - "0xa7dfcf43c9c22fd22f47db490e8f0b8f42597a5b4ae3e7bc4a9b12252b32f89412a2aed946eec19b133cee89b4a70322", - "0xacbd9e85b9d9c3b068220f893d7b6368984f6cdb1cd06a784cc9571f0c632775ef890dbd51371e8701894cbf667d04f2", - "0xa9b1f84f053ef6f41c59b1758836a82d53932cc4b8ee9c2cafe705150e1c717e3f5c15fc21a2532c071e9dd9bccb4dac", - "0xb2c63345748a28d04680e3e451d1f7d430bc8ff2031b6bd4237a1f55dfadaec20d1854ac158cd6a1466dae525c1b9b06", - "0xa23e7b2e5b8f3e3b0e350e1a461708be9c1434d49fe2e51473e2e360bb0be140a96f8ddac99e3b804557cc25d3e44776", - "0xa4c4729a38f5f32f155ca4d1994b61802ee418b276486e2dcd681fec13316f3b6d4a8e76eb9f48e2df0339543b11326c", - "0x93be67dbdec2655edfe40dcdcc0a7e761b7259a9d909ebb12fd7c9a5d4efa10de065d2eb049660ed01ede2f26388d43e", - "0x932480849f97e32fb14d4a69af4073c377e949af7293951b3ca371a306d9e2096157f51c8e5036a44eb73c7c842c5aa9", - "0x8b5e79ddafd675ff88d8f65176321a08183429d42d7fc1e7cc3cfccdef0dc5824ee40f279a05edbf4d50418a4cab2126", - "0x962bd6fcf7c7f2a9c569d584658a735bd16440de2ffae236c68ccbf2ddc5e13836efb163690062537d52f7d8bbb24222", - "0xaf80793655c0b3ec3029673c50a7f212d297f9f80d7d1c7cb1409d292f3bd7dbb8b24581017d9f3964e3432f46e79ca1", - "0x94c8cf3c737c102e9e91216752c82b17e4e42074e08ce44e701c2f8ac7c08902b911cabf38c4c5bd41400eeb1fc97acb", - "0x8708ea7af8c86b2a1964edf64a9e9c56c7febffa742c3ff2e3088a61d3ccd63e135811212878ba7ad8a819e1859f4e95", - "0xab8e726d468417c168c892c10c7e2297e50c67e4283e5b48c3f3b014981ec482e211374f779faa0c1ead906f5dd4114d", - "0xa93911e672aa3d8dd686280cf062f128bd8eefc058fbaea52cc0a9bb255fda84e65ea546f662fc75fee4c5b24bdc61fd", - "0x8aae6d9289d8adf0f81e7990cc79cb704d0a975f03b9ec02be66089d62954fd9a8b005c5ba8179cede366d25ccf40869", - "0x91e44ca55de8ad3ab42816504813cd9ed9c6d64abf6373e8891f909cb49c8a951ee823cd1f947058d542f0bf6290a11c", - "0xa377f97e075b66e740b8476f085d50ce8ac21f206802384e2e072f6b9700a5f9cf0e6f2236307775c0e0d6ae8459d864", - "0x953c08d9f2a9d6ccb22cab906efda69ec1c228aa5c2ab93822b6f71c007fa3bced68c6a70ac605c6145e4af770b60de0", - "0x86d8dcf5a9ba81cf6a3149b2fff96e36639767e9de461bbd3ccc870634e8db331b98a888d7e8d3d70b6ed241d8ce54da", - "0x88db73952866ec07c49b484c6b18de70d439e67d971c1b436684d489253cb96d793cc4d9a4362b51dffce837dbd03bf6", - "0x970b7aa9070334b0649bea1f0b4e53fded64665f87e055e3527e0e567cb57a0e97d369aa16a005155cb69000073d7695", - "0x928c8aaf72b3f51e38c866ab457f75cbd7131b676817a3c6d522fb8f876b01a9ab3a84238eaadaa0a095ccd6c1ac060b", - "0x9561e78d16061b5361ba0be11387c3f6029415f83bcc8477b8729e88c55f4bfe74b59c1b24bec0eebd9779cdfcfbc96c", - "0xaef133788d1e04ac64f573f3ffab473209dfdcaf2c675fddcff83724d17b91d6340830409b391a94405d6ade005cd01b", - "0xb8ad4ab0a1ad6383e4cb12d479cde732f202687ebf886184507371ac277446b3bd1648c49c89343920e5d57fa6b255c3", - "0xa8d00257e331f342b79b3d25b74d300b070326b358f690edbaad5e325293d8b55078605a43ecd9dfd27206013db4c286", - "0xaa71abee2720052cce7a5a0c3968e64c2c540cc852dfe08b742fefe005dbfd10397f66386744c9bfbbaa40779c2ae190", - "0x80c6680857b88afd3ae8801677720100f0fdcb9e49c82f74b8ca5a27aef34e6df03462cf9ef5f80169c72da15be567b2", - "0x8c2f2865e132869fca5232ba5e6844ac0540a53a66971ad54ff91f186e011e5412450df9099fbe0b987be443867dfdb6", - "0x89cf681e0506baaa528043a15ab3bae09b8817853e889f0b3de138992e52612fa3e50d54b3686cbca6428a7644f58208", - "0x89ddf69b72b9ddf7d535f742bd3c942000061a5a54987f2ccc7a09e035be978cb32f653df9568526c5857a5df4321f59", - "0x9908a3288b9a9972c3f3f0d5923d9840756b74f31ae0b24ef2188465efaa5247b1ed13b774991bbe4c065c7e71b487ea", - "0x9454ea9a664390fb1ba79fbb5c0cc765d8ccd32a02d946a14353290fa2a1ba911605ff2e302c340e9ed6fbe8543ee6a9", - "0xaa4f4d9ef843ca3ba334d73af634a0ee800b3393f8f7701cd152274f4296eb79d63869d452b5e83976eca246203d6f03", - "0x8fce1e2e59dfc4fb46f3741d27772579fbf2f48acf1a38c49b0e5dae7d35f2624af3a46a48b89bd835b7d452ab0cec80", - "0x810ec0e58504ed556e788e23067296a8e4b4ef31257d508f05e5245bfe6d2c2f658fca8c81538c6c9ea6ed05a8f249a9", - "0xb6667bad0a7d49cd2dc60af85e373fdaac2af0d34fdee51a9fbc1fe8b77470c162a04da38228fe68b7d5247d43026734", - "0x8982971d57bdf35e0f34e867fecbe0c140d94101484ef4ea01b796633beba184f980c3ced28b24ff42de1dc504dbc854", - "0x86d8d1f3edef9e61058a58d966169a05f07fed0d93bd4f4a7cfca5a872b2aad0d1a78f8ec7784828e5813c8da577469c", - "0xb491624c3d5e517c9019258db6284d7533778e44b1a0060dec5f655a7b79057141079115f5cb1d8d97a90af33cd7563e", - "0x856e1cd4f9ab7cf323f5988bb5d272857d2fa90527f800362569a39defd93e37be2a60c11f498c482654f55560356f7c", - "0xa08884d0e642c479fc8e5a9837d1babbe63f3165c02a57b19d0547fa1fdc18ee382ea82a86cfd3135dec8f2aff793f53", - "0xb1a4de5ea703fa5ac8a70ec515bc65203a9415f6da109b67fa32843a39d7fa6232c9c13920d78c0f16e99fa5f6a27e83", - "0x931a2ee3220ac7888157c426d1b33b8a56f8879fecf1461af4cd6c85f94e193bd6ae6f8dc3946fc689e42bee213f0027", - "0xa844a78e65ea6f75bb55a5db1e78b88896caa1d54b624f218eeb302397dc98a084a2ff4b964acd0650667160928ceea4", - "0xb9c214280a15b423654a36b11646c928fb42ed2a692aedc01441c67522760df29c6ae7bbcb9237938a823188ad4d83f4", - "0xa19575f9bbdfccf970bb3754818e49c709d1bf0af015541182fc7203f7aab51cad31544072d52c0234a3b649d03d9a52", - "0x8cd1127b7485ea7f349e2c89a4b78fab3e5fabe5a95ff0cee10a3f4fd48940e431ca5e526f6342f7da93e32e8eaa1448", - "0x9906abc725e445092dd7dd0aef90f989e3c78aee96f3c0a67ccb62fb2a51363c71d783490fa5fdda0ff9ea69f5b9233b", - "0x8996df92e014c226e5ac724075c19d19a9204b2e086ed5e75a9bfa1f8391c4c77fd5c0b85a29f28b302a4af29d38735e", - "0x90225c9490b39d151a80a9f4d9a7f2595961c44779a54d5e195ec95096f77e30157c6c629cb1c36d995f6c3ee129ad20", - "0x85925b1dfe3884ae3a0e993b67b6c49685deeab6cf0d9997309961b7f727cd6133797bf04d21ef7b620d1d6392450b64", - "0x88a6c518e577a820d83f87e9d5f236f43d262756b1bae0fde72af241fcc192417ca9724428d17a3f9dd813712a772cac", - "0x8f501fd5634fddd00a8411c0e9c6947bab0dded26339821bc3543a64c519d9575c3129f6619c6079d5e95237c11cfeac", - "0xaf2b42945d7c81bc422a1bcdeb80027a1a243f89c65c173294d6c92e4cb3cd650740cac17408e7ba1a34d37d865b9bc5", - "0xabfa5e76f1112602ddf152aceaa9f588beda3aba1115d0823d6a6b074d28389fd4c8178e8a845262200b9af024a33a88", - "0x9732a0e3efcef5ad4d43811bcaffaa1418c791d3fd6ca4489d6cbbb7c44b55156d218f0fe86f2ec96ac306fefab2e694", - "0x8837a6c4e60839ffb0b59e94b37d81bf1ea392d44cc81717c1c9104468d31fb5fc3c35b1efd9991b5e7e9819c66a7221", - "0xb6545fd0b455748ac3482e0ead3b0157563cea7bf6bdd5ae2af7afe1ade921e5ba80466885ba73a89657a735c92658a2", - "0xb72fc49fd3be541bc26cb968ba4eb3078ce7c71fe0ac06340f7ac25c0befb86af03c4cf8f31c857f9e5d946401e86bb0", - "0x929f424548e29c3b47fbbd59ec00d17b00ee1c4f6b966c1fa7e0f8528d52078278f2852da976b8931fe813b0c3b71ac9", - "0xb37861ba981001aa6192cff06c13f041410aa60f965ea03dd48068b4295d61d2fa276c3f477f985f50189e33308c1876", - "0xa73c7cdffd646cffb255d2519d8e08dd8d9a9eca0610211177e259230b8f8c7ec8727015853197a0f11eec8b59d4f2bc", - "0x8da1260ce51220ad107c3127e871715bd738639cd90824d1c9f5b6181304f363b8bdbdb42c21e4e360cbdee496b573a9", - "0xaac6bbc35bce8b54820ef8d7219a4092c49aa5d4fbb187968cb91ac04bc44fa119766f8c630a727ba184cad19278d9c8", - "0xb964de0bd31847ada13dc3f6e1bdc679f421e262c03353e39f0ef1df720ba05e6d806dba15b6e10df559519ca125fc39", - "0xa62e4336b61f85eaa415f57e21cebc7d54c68f6febab02de76bc04a69658ab1d2f7cf0104da79448e32e2b7c92b684c8", - "0x897c6ca595bb2884b643ce8e69078431979d7e6e1b2dcc6effaf5a62fc906db6466f85020bf5930597adbd99e2ff90d3", - "0x932956e0ba09f6499f1ed231732a444b0adf17080237d9345d06d4262fe8a5fb0f704c919513ed42473751069c57dafe", - "0xa24b9cb4ea9c2203a95b0056bb95342c4fa0d91bcc25595fea0161e7d6f45595f7ea171e0ac1bbde13a6d8ca6ad10bf5", - "0xa7714728bc3318f6ac005e350de94f59495ef3972b328c673c5e608fa9059be3277b48f03a5a9634c3d03397af7d089f", - "0xb98732aec7a0a9a7998ba51e2b76e5232379482d0047f4876cd39918119776ae2683590f7fe5e44d12b3b3efdd916e8a", - "0x87700c3fe20cad8fa3041976c87ee761941d323f2d64a9818f20fcdf0259f796a11e55cdee31446bd19307cbe8becf09", - "0xa37cd03fd348694b2ea5cf081696d12dc4ae108da8d48695bf74c921b90612d18c1aa71b1071bbcc02829e05ba1363ab", - "0x830e4e7ac24fb3f64294e5c64563ab5708ebf0e133540b35b985390d68c420a6d680d779fc87245bb1f5c58e59c5ff39", - "0xb5922242a82565753dd2c1438008462d531f820af1b565756d4d27a30e3406ecc503b1e5b628012ea0329fd75561dd7b", - "0x91068438d2bfbb0666824d3cc2be488f2eaf3a8a9f21805838f9f2d582ca6bcb103b2f0f313b57bc86f87704aad7c7d1", - "0xa9a2133fe55e99114e526904f5fb3e2e141f31963562887a9fe6a262496dc405c756bf6dfdd6acb8853ef5a0a5146037", - "0x8e48e79f9eb1f8757b4c4afc4e3d6da4d368bb25b4d54e3a1f34f3af13d8037b0d465b29894f68272b79cc60fa676071", - "0x9378b90495b0e6468dce3102a97e9965a5d21fa4a6494d401888b8777bd58616b99d49177f2eb2796476ae84d20b67b7", - "0xb0aea247d7d7c7767519b87dd66f56c306d9eec88b0db8060bb97370099892957e2c950fa2e05f24f8ad097889cab087", - "0x89d0d48769ad81699d5b83f26ac49a29c3e835caee03469e93c11e5f4b8470eb02b52290bb2c37f06afb0746630803fb", - "0x94de42d8554583b24317d9ea283dad5849e2f124f659d0afa11414898ffdc4347a9c4ebe783dded21679337b58b67f4d", - "0xb76c3047eaecaf4a4e6fb6176c7f4a1d393fec3a360f4c711d6293a993aee39d5aea654fc6429c2e4d4955b12fea5c8e", - "0xa307fcef0915e3e3a27b94ddb9561e5d210a091714b73afbc0b3fa5e8140e8c3818f4914903975e8f78d0492d7784c25", - "0x95079c4a5008fb6ae0d653c00ad901a108df0b8c442a68492740eacd15048106b7c4cb5ee88bc6b1dc089987935bdba1", - "0xb65a354aa8e92d6ca2e11f4ed3c1ed011852bab8f0e5b8157a10c26db2748be688512423c11d582b3dc1da57b9d6a826", - "0xa32c2fc62c38eb19dea24b545d2537dfe596423f8ae530e562ba7eaac34139fb443d88f18f39d65d36a65ed1277973ef", - "0x81b83b37927e9a6a7c34cfe587dc9cfbd560db3ac57a8a88161fe4ae9a7c66843d32f6f568c927e2ff8f21d8b4299475", - "0x8b6993ef73c2021842060ec0424464412242aeb711da2c43d3985f9d15e4d936eb7a1b5098bfe892fcd3b6ba8bf42369", - "0x965535b46a18f94a1203fafa4dee5963742511ab77e98e471e03376847850357d543dc6ef2dbb765cbc1f03f66ebbc14", - "0xa9386ef496b4f96bd591847baf6dcf8520f7cb5aaf1713025ee894b40b10f243aef06c553376663488377fb8b1b0a022", - "0xa6bae4486fc16ec1f12817f2d47871c8bb61f5f1a2db5f828c6e2c06bca64b1ff7cf4c059a10d6bc2f561fc3a12aa38d", - "0xa2b6cda6a75fac16f324935cc1820bfdf013ae02c209802befecac0288d90263a7f84762dfb7c9aa1351415c03288714", - "0xaac87216619a8c50b5d54432ed5681b1cbb2c7084f33e9a91889bfbb94fd18c8071b79ebdb403ad81fea495bc1e37dcc", - "0x8bb3b3a7ceca82e4268ab52c00322d5d0822427e43c1d8b88b2f43c3dfae7100f6a29832d16454e093579cbaa1074062", - "0xa2363b4506b1464391a194412a73d47d0cd4ea1ffa541cf8b936c97a46bfeaebd1fec409c6aa2109d277bfae0ea7f0fb", - "0xb56911be2bbf1e564715191a526c2ae73bb6e85c45e3dc22bd9dd87cde620de93875c48b11e02ea66eebb68f533f353e", - "0x81609eacf4b2e78a9d7f469e0882ad24c86ad98dd18f466d321aa32a762171cfc334dcc049962ef5e63248ef48244229", - "0x866b26d3dbab7837edec84217c85653c6abaa617e0ba2657d67757fd1c7dfc0c7f83f6198fb85a49b402108d6fedeea6", - "0x9771f5796d5d47d22100c7ff7d191795677d53796f4a1e1aada949b372ec12decb6c49e28f2662e729d44f0e09eac063", - "0xa9fdfbfbe114c7f93806b988c50f8ae4e13a4d433f2e40c72b81d0ed7fe879db5e89216a0b0c8392a6d9d54f57760ecc", - "0x965336222244229fac41336464c36dac8700d5289c0aba78016db76e436289a0797af8c96d52583618f8c6dbe7b3562d", - "0x99719ac482b72d54fa515395847e9a65b733da84f7d10a0be82f34afc20159d64411aacca15041726251fd90ae06a9f4", - "0xab96b7ac88842ad0ab61f7550b7b4697d6a3b651cfa3c10ad404e7505c742e2c1364bbfd08ad0039ca3b81ffa9d6a6e5", - "0xae96088cf12f76140888582f6f6404b6f2666c048950166e37bbe46c1398fec343fcacd3e8f332f7afa222ca13fbdb87", - "0xb5b5c1ad493b2e72ce8ba698351f596cb85841f7f7055e31325cadbb4fec3e8045b335643190d6b97c3049d10551764c", - "0x85f066c7ffd2bfc4519f42f0778ce0e46195466768322a22673a073ebb66cd77c7b8b3a14157845cdb369d3f40911421", - "0x99f4f10397cb7ff47a2d9d2f29021d1ca96f0da01f8afd76f72457cba6e6376f925fcee28ce77475b90c9466042ac414", - "0x85116606b18f6e5404e9176570bf6d7a9d86116e5a29721a1b20d6b28a733886e2085a7563cbff45d1f11bf3d552ea12", - "0xa17d81b236fb138ed820d335dde2640ac3a44cccb5f11fc6bea5fe3132c4a9247b874e75fba55bdf8093f0f56310a999", - "0x8a16a5cfe10c5dbecb4fd9f4b0c370162071f88198e016111937199b87d006d1b24f3f412d853d7c6541e1c68076b70a", - "0x8cb83fd2b1afbad7c454430fb9dbf6530230b782c7dfb01443c2c16563e833c5b230f4c4268dc37a55a681a5f0bef420", - "0xb8851a8dd6a3a17619e7c84b18f29ac9680b456c03e8c8489376e6de9a22ea75d1730787ca5d269af44eeae47f87bc24", - "0xa8f990c9290456e849ae4cc0c320580fcfd50263af8945d01b00baddf801aa0a7bef2ac119d4d1b4be6290615c781656", - "0xb0fa1c28c8c67ff87427691047c362aa35de0be9b0121d83b116b23170ad2b712a0b5bdf6a57a25c59201ba165d5f0d6", - "0xafcd2f5e66a277cef775b636abb598ee9d7e3bc1b48b521da787dc561cea8d7ad963f593c3ac6f23a66a27c15876b775", - "0x92888863568ef01b40d51f467e8364cb1b09808238644bbee5ed118b392475e90c6a1e03a0ef826dff5ada8d10be716c", - "0xa8ddad388f2dc94294371d0ebbce02016c463a65bcf3a5366419a7a910d3d24748fb5716ddd81cbab44a2362ee3c077e", - "0x8b8ef4f818ca3de1683064ea7e968edc8d9fe2675b8bb2ae637a5784a20cd909d18eed45140189eb9f080c53c06376fd", - "0xa52d9c49db4819cf6280c220a6cd306a5851b771de3032f28c7f8750c20e80cbfda57323a55a8c03085b41f4f236b5ba", - "0xb01fbfa0f80ef574a1d6733899002a8672cc309e1014fec8e81ea1e96a7be9c247a570f825b7862e814e1f006a8227ac", - "0xb07e163eb0f96a51d74aa8a7fab5d23e44e37b1b1027ae9c4155280d8d159f0cdeecd3258c098a7358c5bf2fcf1eb7e2", - "0x80c4512a5bb5e8255488fed7b7e297988732473f0ccc1192cab716a88d035e23cc374a937fca7da87e18048ab026d9f7", - "0xb3e343b13c1d4c98b7706edbf362eab12b1fa87510d5cf168e510844b24c8a9624f1e7e0babf455c6d425741c23e1ca6", - "0x83e4b53953ef683c512756b3fea37756b3c562c88a15cddd902eeecf0de82d0345fb05feeba511e8a6de91aa1f722ef7", - "0x922512dd5ce444df62fded2c53a73385570804e7305cde401116c06dff5ec7812b776b8cccdfdafe422f1ba53b2b56f5", - "0x8d1f7feee880abfe9f09708ccf72f376013b2910886edcceb76018759b88b95cab9c0e8f176faf042729b405a10242f5", - "0xabb7cd087d0cea2cdbb47cdf9be2c6a0c6ec113e1ad5fac083f66a027697d477ec69f46b9aff40c239ad9959b9854e11", - "0xb10592443daa8708f8c882da795da07465efb9829305170bc3bdd781cb6651b503d3b21eca027486d413f1748f40f068", - "0xb14dcb895ab22335373d2b736628c1ed0e815072fd3844867ae24638aec60f8591c6885869ad0bfe509fa3fa3101a5f0", - "0x89631708996651bba6b2113626a2fe1ef0f2ea2f21857b2a1e5544ad31e8a53e755b6d611546ebbba4b2213acde65e72", - "0x82e9436700fcc5b842ac2f0482de4248ec9d1f206db3dd36917c00c7749bda257fedaec513d8a9ef3765057bf5aff25e", - "0xb1c2b26d93658451fb4e9cfcd77209dbfea909b2212c000fcc576ef29b808061c9f58827682cfa09e357c1722c3215b1", - "0x8be32f59768777a785d8b257f941215f37db8912183aef4a39a856b88cc680ae7124789c58cb3c6c6f06a951dc96a1ce", - "0x8cb60a3d0c9a1efb89f89f78e6f0e4bcf5eabeae6cb215e98cd7f9eb58699ed70dabed73a8b95daf32a5e4bf0d411d3f", - "0x8ec7156d6b672e631ebd88467f40caa9ba5411ab727602f3146b468bc00ae54fe44b3228572670215a0dbd59feb66e2d", - "0x97b7162101d740aedc894bd5f74b8cfa7ca7e7fe8363b05491c15e8cd54f21b0b09eb41f756b9089c379ea0ab189c468", - "0x8524c9de6be47cb6808df761ed03c505932ba715e757dfb3c97b6deb462433d98953ee6cbc7a576b6837e68eb16d3188", - "0xb024c8fc3fa4f602ab73448418548d9896200065a95e8a001f6c8d4cc3f53f18ec8b85524377fd93e2d2a18eb4c48b57", - "0xb344dc93d3057465592460b7f35dc015f4f8025fbcb44a645dcc3dfb37044d5681d8abd81bd544272dc57cd50048f29a", - "0xa7b270b94d9870f8afec3bf2ed58afb76f4ea576a2175502630d0d3f92f9152c1ab0c019f175f566eed29713dd97712d", - "0xb86dd953c40d4f5574bc7489323d71e9798f7c6f2dff8d41f6295655c5a275179ffb4bb8d2408b88226c98583a7c26b1", - "0xb73074289a5b08aa695de03ce2f5b106107c6cf2bee8061e3195056e799b0bd8b4172deff7f413ce8e477391ee6294cd", - "0x98b801a58ac7e083da541ba058c64b00ba709d4d0ba1683e5d83dfb80a29272fc2a33a18f32351b103b227abd5123da1", - "0xa7cf232c6ec6b9dfb32d729b9d4216688f6d2b6e68053ddfb293ebd5774218c69133baaccec7ba3da9b221af619c2ed1", - "0x8cc1d33ffedcea05f3c593e5b63dbfebdf26d05a5719cbf642997be929336b92457fd9df0d6be6c063918ada8fa2d322", - "0x8d273497dd9f822984f1d8dffd471cc703d03c342f022b2bb24492209a3889f010c4f7ec124f9fb9f884a1a11f84a414", - "0xb62cd013944d8d9d72fbe54897a94e804c93eb84a24beb0880cd98fd5d48fccf5dedf5076abcb1b857adcc986b729cb1", - "0xa1bc703a67ee709f7776b2871f2a88d8574c9e2910690c9242c162ad926ef2263d5260f5c19015ddd5ee1c8ad1a444ae", - "0x87de434e8ab5b1d067188cb9c12ed936c26ddb0ee76c4c9cee9bd1ea916e411a354bfab2ce77ed8c8ab5d8c62038f933", - "0xab128e9de30bad31dc2eaad851da1e39741ea61bd203b48e5671e37f7b4e3db86687574d3cea1f561bbea84f68cd17c2", - "0xb54576c9c4bc3b43270b83b89eb75cb7e89057c99e14021ca42237dce393dc6a8614c5af5c2f69160001b2ecbb407c9f", - "0x93adf38f161ea886f41e4af8e42c69c53a51074db9ecd7b7e4e36c858426237167aa49b79737625c9f9826dfd22f39ed", - "0xa6907c8dc4073d3d4d40df8302c1637c15f9197aad8511dc95c210f6a60b06f3aab2622b826d16596af27e42f2c9d5b2", - "0xa8b0c4a3a5d3dd5b6a85802039f48fc80350f6f0be2e55bdf75e3197a22f6547ff4a7dce38ef3667006128141364625b", - "0x8a5f4c17c729509309b2ac7e0dbadfbf0baabbcfb1fab02f91d055238faa3b66aae850ac9b8d7b7245f0a26bc5253c99", - "0x8bfc5d594700287da2a85a78630c616af8e555cbd7864ea604ba38eb75742fabf6aca12ed99a2439e2e046d8f048a29d", - "0xb0f91b7546613341cd95ea112e04b0963fbf7795f118c393fbdc37e37dc25244d10d987c13d6fa6eff3c4721fd0a328c", - "0xa70b6fdc66ce4c2e7f1d830b7765b7f0640ceb7306cc85778488964cbcc336ac378b74b9c4ec27358f27378380b3dec1", - "0x87387cd6b643721aac8e1a8533c27602d9632168d3a3532059163dc5e4613838bb4f07803e6852b23c58f7103d134f92", - "0x888722a5a56f5b6b00daba53991ab6fccc32a029a7f1748f779b57732285e56e09ecdb7d4848abb3dbf3e167cf8033c7", - "0xb7f5b9ffa8ba66f54cac387c197058eb9025cb3761550c78429db95f9e1e3b49c208ce86b6126c162a62939e1080895a", - "0xa53f38c068233b584211157c66d9d2452c811bcd340d8cfafd32b070c326169306975e558745d63e1617f4b4528a2399", - "0xb1c3e9b0f19993f973f038bc45be6a834b1cd3d56f112c857711c8e6c30303eeb0b205bd5dfe75e46b1f4d4bbb68fabb", - "0xa81fc28620e640ccb57dedd40c79b73b0c51565dc61097527b2341bbaa3e1c9ccf20f9d8da1c16704e881b24df0b7335", - "0x910a7f4960a0ec2aae66cbe2ac98f43646b017de84ef3d486c19b7809aa16813400bc2dccfc80e09c63422a9d4d88f56", - "0xa463868e3a8c2d2a7c49850be2740e01c7892c83063d381f405282b4c521cb6e3317361abaa92042c38bb68695c10bb9", - "0x991957100ea0f66cd4ebd23d9f6bc7aa62220f6ecb71ac947cbffc6f36f7891173670977bc58a6f57b9a1e8766100c2c", - "0x961dcbd2e6cb94574a33fd48b5d87e0623411574666d5d37f4ff7dc587980e2644cf056e002746088c3c3b0ee7044510", - "0xa27cdb374cdbff217370015d78c7f5c4674ec9948339475cc80a5805979a4c6a2614b6904f943374e96bb838549ea517", - "0xa567bd4a59f9df9f5f30a70cd9f6cea7dc0e19b7fca37fef5846aeb1697dcf7925a4735367be0828f2ded5007f711f03", - "0x823937a900e3b8705b657d40470357d91eeb339324f0fed58695ad72dda7c64f2a6b7bb7ae4a20cd1b2016cb9edbdd1a", - "0xb07f2248802ba7dce15b2698a60a4896323d37ecae6666a71cdf85878094bbd4e9c0e4733bd8bc6e9c850e57727e1d86", - "0xadfcdea69c5608f02630db045e5679f9f0768fbfa9c8e97bc9cf9cafe1f747d3813e7bb1adc6085cd57102afd71db133", - "0x908153d3eb2eb2b93c15aa606531b08981bcfc8b76684c2483bf869f335f9d8773a9aa3986ee54d9392856daaf82b684", - "0x8fbb2acf533e7d6e96e9b68e77f7a1df2ea6c652cd8862b946c93c084436d7349ef4a0c453197a9769e986322e9174b5", - "0xb83cf4ddee6140c9df0a08a39bfda79c0d55516fd799c1c24b59397b87a33ea5a0885b2998dadc354cb6f65a4bd946a5", - "0x957a52cb24f19106d80d4115a8a0843d047d157c4a8535775593c1dba9be24318dd434bf43a82aa7755897f895d2ed15", - "0xad93dbc2c055f9d7e42717391cfae64962a78bddbb9fd102a05cea520654d4a9cb6634234d3a188693c87c5b4c78959e", - "0x8dc4b8e49de9b05c33d2a98973e223c01ed5745eeaada3a4c0e474cc22430644a53a60c3d6efb1212ca298c4331763f7", - "0x948b0172df27db83e70fbfdc896ed82696876ac4c51842d270d9ce1e7f1fcc9487d781eab97f40074861401b809dd7a0", - "0xace190f75cc102a79412fceebc013bda8cf329798db4b4dba658e63228ca7f141bf0849d30139ffdededf98986f3066e", - "0x8f968dd6d7e06008a1374743b965a6204c11a610ad92705e8dbe6df3c58baf36b47c5d9988e4a417c80ffd5e7725da7f", - "0xb8ba0d5b36cc41f6839543d43166a08bf512f7b56040891ab80efefc774db28c833ecd444a703c75547fa1404fa1ec22", - "0xa29944dd0e5c861eb37c744c429a0dce596cdb2e5b0b2d2438a259a0faaf3d261faee1434bd28ebb2e6adab59ff3951d", - "0x85c70422fde0ac6e7a0574512eff2a19df8049757bf78b5a7d7870848626850f17e8b0a5661e5292f3df0f613886306e", - "0xa5ff5c3ca2c70b88d15633f9c38d2e065bcfb0e447adca33301a0d4f05b761049c8f795444f11e39357fe6bc0d7f1880", - "0xa2167cdb114d7707f1010e0be9cad488fe56cef65941c35a5878a100adbe522a8abdf7eab7bc689b8727fafb174032c2", - "0xad3f526ef9ed367b2a25c95453135510472581a758760d47eb9f9b57b04f8c061152e5a792584d6ca7124dfeb7e21703", - "0x86443033ece13fd386485115765aa95673be72b0543fac2138e1488d22419591176423213ec06e5e4549a025eb6aafd8", - "0x887e4ccd58603e6c9cc99bd2740bb1db2fc4127e8d3ec9cf41bcfa3589b0fe1931ed2a6140ae1199d323d2870882ef6b", - "0xb701f7d7637662ea7024d31e94245a5f745c7ca889f4f7a8362537df82b0164eae83da5a107a21c0ca889926aa50de49", - "0xab6bc11d6049cc5945011d3973eb2dbd5a3d786b3824bc125786e734887254a6ed61fdc2a97ea34e6b37b13cd97eb781", - "0x9901a1f44122bf5aec7cea28e9405c33567eb118752edc60f3cf6c80642370f86557cbd76478d6b0ea33be92a22c746f", - "0xb9f453650996f14629642bef8fea66c90105c8523f3875d86694100f8022d4fff2915ac9f9b9efd6f425751b113d5623", - "0xa5bf9385a1c94c09ec326c49b6b603f2de987b2878faf0263ed109410536543095c07320f762fb6fe56ee72a082daed6", - "0xab003c86dd62c801cb16b727fbd1037aeacbec0f76e8abda4c6d87066cf0a33dc1c61721f2134c3b78975efe013cddb7", - "0x8dd8c580c288168f896fd7ffbcf5c8167a55d34f17b2c362d0ada0f33a08cc0568b37b01cf0cef1fd5e2c2e250fcdf7b", - "0xacfe675aca83a774d3f526ad461f0deeebfc73a15ab3d37b224f8740ac2d9df238373e6cd1f03ca78a9daa0a796c96f0", - "0xa45cf3242600fb9733dd5e0dda1994e8d37fc483885a34a76cc16bd245f6d9c8d15bef360ef59d0a2c3cd59114280b87", - "0xb64097145d97cdc8b7a84edd1da7e84f8aa44c3c2a4823e6e8485fc3a44d94cde7d7ce8bfb3da5d583386461ccb42afe", - "0xa10ec5859c274c0972ec39ac80e461c29995b35d03603dc97dc30ff826ef24c5e52d5dc9296319ffc672b9e1d57d7936", - "0x9702ee805b70a1bfac7318e8470667ee20739e3db3030bbcb9a08568e96c9c8d2af2cbeb26837c43e04997457c623486", - "0xacb3f5a663333d1b4d54dd76a23c6601fd4540e2b050ec2a7fbf0b850b6f592837155e6bee5ca942406643f98bb2ca83", - "0xa577b96723f64d2671f1253fca72df86ef3247006c92cedcfb26eba4b4f4ba71bfffe1d5eb87b0192378d0303177fdba", - "0x8c397ac56cb723df015d0ef202fe486d1acb42f8129d3e4b37125a7ff9e81aefb1e19f477d528be1e6b18e7bced27ba3", - "0xa7a6e6994324a80ee0a85e8e8cf818f5f8d16d299f21b2fca8a9f65231982584afe921e76723701abea873275ce0c15f", - "0x82c8ee7a39e49528efa60ce1cbcb3017904de6beaeb751c406b64a94aa73af72932e32b63c1d3fa22619498fc46de6bf", - "0xa1d0193ac8bdd44ffcd37092a5dcf6e775824e5dee4c0aea5bd46f2e25b312fe58e1e6b9dccf9dd14f240d7ced4fe071", - "0x82c48967f22b8aa1dc63dbda0f66ff2f40f3ca9a7b3e752e1a950dd7daadf9afd81ae1fe3d0e440259dccbc1520d4e22", - "0xa69d43e6f962b728d223f6d474a923dd13c04eb8858f7fdd2df2c25dd4d13a0a69e71132f450613e8e2d9a65938f31f5", - "0xa613b731fe0d23ebf376cb1f3707ab9b2d428d1ea3a95faca9988a1ff4fcbde0a077b38b5942590e594307acf88c9db8", - "0xa7d2f249ec666f59dc51f9c31db6168f33a94b17ab95123d4b19aa00dbe9e1cdf340dc6f64bffc6dabb11912e10edbba", - "0x8e64b8f99ada5f317c6e2fd64ac17c4d6e5314c82848efe1eb97a5a52e6bf08923360dcb44c05d3fa59a82119610a898", - "0x865d9512ec4a18ab31e4062b2ea6c43ef32c7c58d89bb0afdad9fe57dadaddd2150f78a0e85086454812855bf09f31ef", - "0xb2d23f01a0d182abcd6862ab6f4bf924ccaac399ec143fe2614908dddec102e2feb8555479bfb71ec3476cbdd71b1137", - "0xb50d176e628e06258b518be78c6dcbc3c9b2b4a1ed4ba10ee822b3ebfeaedc4fa69c61c1985e1bb20ea9f3d6df7a27e5", - "0x8174953f4023e31e39f1cc3bad674bf2f1605ec9fc053948bb60dbf2cabade885376f8c76f45b638c95fdb14f5bc562c", - "0x92b95a12d1fb1ec489943b3a2a1c8e3c8c6a30d0767125b87fb491f9d4f8de0629afa39fb5c8a84078b08bcc26e88c4c", - "0x93f4b80d76689d5936aff6cf195d579ff5328ccd0f04db42522a225f96b0bde2088706356675f185186548006940898e", - "0xa5f7f4577943741def19df611c2ad3d459c386a5e3c765eaa5a0cb6c192873675cccbe62845418dbe47d7a9822e4038b", - "0xb59bdb196d59928326572807b2ff2edfc93a28632542b270ed462567d32bc36cefc40300619aafe4cd1e91c38d6c9c30", - "0x90df4b921e13ca1e63e8a5c9968ff64bbcc5b829e3421d74bf7f678aa1dccc1db9ed9dfe5aff05539bcc5379dd59e575", - "0x837b0b6813249c456631b2f2fea9402a2303a454a114149bc35efb400813397366eabeb4477f2cfe037f722d78a5849a", - "0xab5b33ae561312d9791bcafc8faf6d65f2c4260f126f11ab5c20c7626d88f2c00177588ec62ca763a7ca44c6ed60eb0f", - "0xb0ed2e48cf650a4267c3da1378b8164cf6b774200a5898155716f85f7abda093a60b802ce549811644e5f075d2b26067", - "0x8d47a4e27f448773fa2d592f052bbdbdf30cbef152db6d8cbeb3d7b1a0dc0f2c820ed7572eacddcb51c19a8268426b33", - "0xa56ccd0961bf238ccd197e5bbf430d7c637ff6e01590febab3529776403682ee32d0a776c3dbc6581f60002dac86c38a", - "0x9163bbdbf468be88a391698ab1f40a919517beb6c780062d4bab3bf8fd42eed6546a8c743e249fd61c3c347ea60ee185", - "0x8d59f46606f063e68198457917004ae50ebb99cccb07755825782ddb25b96c3cf8973a6c5092c9db71a4b8ed075876af", - "0x8ebffeae4fef7a83d81f31a88589e05f885dd0c0b4360545b22a18369a3e6762f187ea6a173f25419e70031861936463", - "0x96013c6b47119e017c8bf473b3d643d0bea1cc12d84d412c2b9f6f55f22543a6e15ff7e818e9799af43984ca2ec3bfb3", - "0xaf46ef7696d9908fb343a6754de37e31cbb41dc1a4ab8203d2a2483d1cb0dd3183e5015d8048ff146ec34a6c3f2eae21", - "0xae047ec4584a962a7ae9359292c98f4d8e0916dd98a414e2e15429ff30ffadb3e0296282f0f7e257495e8ec4bc0e5328", - "0xa16de787896a056d31e3f174418aa3882c03c879a292246a43dafb81f8e0e05564f1cd3ecfa251cdb159f63777fc6346", - "0x97d1c4a94182ada88aa3cac95520711802cd3889e3e057e99a72a69589fd222b241d35a54b04f42503756ec3c1a3d701", - "0x86be4ebe8b92f5bfceba757e1e2eb649f9803c8cb10130b88b13caab6bc04dac4e26d538b7adef68413b047ab9031252", - "0x95d4c0b08caa283ffa9e307f78db15470fca5b577189a33bcdf14c36d4ae3f571d004c5aa1e69172a4068e72b7dc25d3", - "0x965b7053a1d14f9091de5df8bf033a07b9f8d39a6d66979ab5424bbfa32b803075afc2d74e71235a0f881bacb6704689", - "0xa93e72836e2efc704f87065dac0463ddd4b063eab125d834df583d8833873f575a0179781b72aeb2a35533a34a395481", - "0xa2997d7c377060d910654550316ea7374a0329fcf30e743d613e2ebaa15b1bc6c936c2529f5466ef0e60ff53aa2b709f", - "0xaf5259d4d08617d9be068d1b79a8209497972910938831a435487395512187691d0cb021bd57eff0f694f32efc1487ab", - "0xa78b8318838b1049f308200782c4409fc6c97ca5bb6af28996eb191027c8935b7a43a41961ec046e6c8539376c1aa293", - "0xa4a6a9ec652d1c95883d21d3767b13a7e1dee73be907dacad197cfee025755db3cc7a8fb9f40146912f8a3f4c2c49c14", - "0xa8a8ab62334a3c67793fa0691a0d2e80ac1681ce64a02df93b78e4a2f6fbf3af9b160d9ca6b4e835d58ed60d8ce627d1", - "0x980c32e492464a6f36ce12ed06541e3b2eb098934c0ebccdcc984cdbfee4a506d15afe1a30a84d642322c2987d9d51a6", - "0x8ea8c1adfd73747db98705e9fe1aec441484d2e4862b84817cdf1262fcce91f56cd26073d4dd23b04f836e0962428593", - "0xb0f20edb8552d2b08613cb574e9de1c4dce1eae55ba9ab05dd7f2ca3590a7496d63d55af88b3dff881e16d8bf9147037", - "0x915af4e9a28b12ea126668db7de6ff0c2cc9935b138022fadbb1f385f327fdc927388c945b93d252cb51803c242f7e1f", - "0xa553e08f67c61ecc5c8955f7251cfe18cde02e6170845e70db1761bc00f42a31cc10de26d4c904200166311f32a3e56a", - "0x99f4b066a805512e16addb0bcb08d76f017213ca6aa6afb5c2fc621805c4e123bbe0aa85eb5a0f89d3112635905093e0", - "0x9236c5b0f4d2e58033735d7bd5d53ccbe82c05aa290149286a16a318043ffedfdca9d2d07817601d4216fed50c1082f0", - "0x90a4c7898c58c9af83f94095f6afd5ca65664f16c0af4c8121407cf0864fdeb09958500b2bd0b78950aa9051e3480928", - "0xa589666688e6e7f8e4d99b84d21a1f9ebfe681fad346a237de20a11a2b210eb99c4d3e2f645b23a85c93bcccd51f63f8", - "0xa010849ed4df0e3a8eb61f7fd114d05a8669bfa36cb95d089bb1964ea8f5fa26be0cd10fcd9b38b259722c5a14ba3a1f", - "0xb21f974a10a2dfe9987370ef4b6af294cbe8f4bbe35ce9400d0538c5f71287498054d73606e26f93e2f19584aa18e285", - "0x81fea77bad05c3bfa8d5d8409b189fd5c759613cd69ddb19b2d46673d4df944b2c7293989f79580d229d20959c74b18f", - "0xac962b0819a03d2a2fa42c492f54c3d293c6d5ead403c50f7a1ccc2faad58beeb0dfe888a928e505fea9e09807e13a23", - "0xb78b913f2ad9622d20c175ed23f80f235b5336343b0353f82383fa6aab99aef77cb489df822bb168e56496c1854f623d", - "0x8c06abf72913ffcb6b59bb8201c00034b447011880733aa6b563acc423e90bdae19f2a7a286943b55488fc863d09269c", - "0xb34168972fcd90c78286bfc6078ce559e3c216d8d1885ecd5044bf9f23a4ad15bfc9830aabb4273472c43e2980048586", - "0x88350e0ffe9b5576dd0afabc6d8445d25b2b9a0945c71e6b9a5587649ac5d95cbd722db5ea1e65d3fb8230c794fda5fc", - "0xa3bec1fc362a33f38795158f1b869e9ee857a7f2e1acb92c6a7dcfffa64443a5b7f6dffb656452e7f855051ae849be3e", - "0xa21f64c49334720883e1243a27575648f53637a644c308ff24f5c26bfe65cc690a5e46b8e432171f31c4229aff4db416", - "0x85dcd8ebef8f7f44372912b4a3a0dfe66a56f16c3757a8ec01b71aa81eeda9f8e5082f92e3ae8cbf3c1eddf5e6ffed03", - "0xaf3c1a770f34f2acc504f38ffa7a18cc4b38f8f84f310cdf2d7346b18824ebc7c7663cc0e00b44cfb5494fe6081aff84", - "0xa5dc7c5989fb5cea87c2d878d8436d858458171b667ab5200dc2cafd8af2d9c2bfe2515b0c002cdc9c3e61e4cfe4b444", - "0xb136dcd4577ef3a3a8bc946cf2ec79d3fab301114ee2a692a6c219e252c9128db63fedebc6bd6695a8ae58e7d28501e8", - "0x91d3a1ba625632d59dc963ed54c0310d0447df3e1d9151299722d10f8b26161bb73e0020d408b748fa6fd1db51adabd3", - "0xb89f1a2497b04b3f1b018dc88b52133e1d7470f97f846965fbc934d34dbc8d38f2d8b07d218e62c609de33b61831cc9c", - "0x92fec43fc5af23fda5dfab78234f5ea7813d8de34f8ec269c5fa35dd916b9143ff0033d35e7a284c0ef4f41047e98fe4", - "0x8a0b89cd35ecf5b6db26c39705b416a4b950aafaf3b00a9d75f864955e9074aac03826ff9393456871107563eacc024a", - "0xb04db63ebce71161fd42bb878e89155bc9e887329e164481077c6a1db092477370a362810d291444f5463437e0ec5906", - "0x88ecd5275592f8b133928770e2273a0e0c23424d72b9e505130b0599ba28d1c11eceb2318a49dee054a8ba0971874357", - "0x8eb0271197fb9f1eeedaadd8eb603b8753ada11abf04ce90950034f51f756ed6ec6a6182a47e1f3ae51e3a1f3ecdf467", - "0x81cc996bc6b12ac56a1ae3add4483ae4f2e2284e9d304f5fa1723231d0e5b196813b6dbbc20b70f5d51fcbb65bf284bd", - "0x8e1d94ecca2928c4c68fbc13199b6281f8782c75c119b763e0eb122f81c35f8fd079d1bd76b498393371a08dac95dd1d", - "0xa92f98bc09f8a91fd165bb8d05e3b5ec50121d760b353d7e4ea23c0e04ff29614ad9028a4a16bdfe323f2af647e515ce", - "0x82e8dc99a14da065200699e458150dc6d49ec0e098bbd91ab8f1fc1767e8732f53855499c8f24da7b9dd681100633be0", - "0xa67b6cb4eeab4fe5f4ebdf5649b7d61bf5fbf7b6cd2d357fdf348ba32dbfa9d6830b1265ea76a1c666b266e30d119182", - "0xa64e3af1d0e600bde18d7f53a4e8d89d296eab4bcd9cc3a9f476c5b8425e6e8082066948fbf40689f626e27e4830edfd", - "0x8f66b59782cbccdb31cb1bb2d6385307633ba4db31c375c0a8424a497b2fdf309e7ec1c95490324b9a909bb43041998d", - "0xb93f4817eb1d91ac78eb650c110f7c29df40df47ed1d5d3209c3abe5cf59a5e7aee3d1cd232bcce77e157b1a9daa2557", - "0x864b6cd72029640fc041fd3efa71bb210edb40589a26981724b944192c3c2543352b4b757836a7b0b13bf830f22b8374", - "0x9064a0ac94f2f133e287b796363f6d27e9646a8b531cd9ac0eb45b99fa73f327238161a43f7c4fc914036d69abd1473f", - "0xa40e60d4aaf9f50f7bfebd0e714fcfeba64e0f7ccaa0f4829144a7efeaf15a7cda2d62d771a76f98a45cda9196b0522b" + "g1_lagrange": [ + "0xa0413c0dcafec6dbc9f47d66785cf1e8c981044f7d13cfe3e4fcbb71b5408dfde6312493cb3c1d30516cb3ca88c03654", + "0x8b997fb25730d661918371bb41f2a6e899cac23f04fc5365800b75433c0a953250e15e7a98fb5ca5cc56a8cd34c20c57", + "0x83302852db89424d5699f3f157e79e91dc1380f8d5895c5a772bb4ea3a5928e7c26c07db6775203ce33e62a114adaa99", + "0xa759c48b7e4a685e735c01e5aa6ef9c248705001f470f9ad856cd87806983e917a8742a3bd5ee27db8d76080269b7c83", + "0x967f8dc45ebc3be14c8705f43249a30ff48e96205fb02ae28daeab47b72eb3f45df0625928582aa1eb4368381c33e127", + "0xa418eb1e9fb84cb32b370610f56f3cb470706a40ac5a47c411c464299c45c91f25b63ae3fcd623172aa0f273c0526c13", + "0x8f44e3f0387293bc7931e978165abbaed08f53acd72a0a23ac85f6da0091196b886233bcee5b4a194db02f3d5a9b3f78", + "0x97173434b336be73c89412a6d70d416e170ea355bf1956c32d464090b107c090ef2d4e1a467a5632fbc332eeb679bf2d", + "0xa24052ad8d55ad04bc5d951f78e14213435681594110fd18173482609d5019105b8045182d53ffce4fc29fc8810516c1", + "0xb950768136b260277590b5bec3f56bbc2f7a8bc383d44ce8600e85bf8cf19f479898bcc999d96dfbd2001ede01d94949", + "0x92ab8077871037bd3b57b95cbb9fb10eb11efde9191690dcac655356986fd02841d8fdb25396faa0feadfe3f50baf56d", + "0xa79b096dff98038ac30f91112dd14b78f8ad428268af36d20c292e2b3b6d9ed4fb28480bb04e465071cc67d05786b6d1", + "0xb9ff71461328f370ce68bf591aa7fb13027044f42a575517f3319e2be4aa4843fa281e756d0aa5645428d6dfa857cef2", + "0x8d765808c00b3543ff182e2d159c38ae174b12d1314da88ea08e13bd9d1c37184cb515e6bf6420531b5d41767987d7ce", + "0xb8c9a837d20c3b53e6f578e4a257bb7ef8fc43178614ec2a154915b267ad2be135981d01ed2ee1b5fbd9d9bb27f0800a", + "0xa9773d92cf23f65f98ef68f6cf95c72b53d0683af2f9bf886bb9036e4a38184b1131b26fd24397910b494fbef856f3aa", + "0xb41ebe38962d112da4a01bf101cb248d808fbd50aaf749fc7c151cf332032eb3e3bdbd716db899724b734d392f26c412", + "0x90fbb030167fb47dcc13d604a726c0339418567c1d287d1d87423fa0cb92eec3455fbb46bcbe2e697144a2d3972142e4", + "0xb11d298bd167464b35fb923520d14832bd9ed50ed841bf6d7618424fd6f3699190af21759e351b89142d355952149da1", + "0x8bc36066f69dc89f7c4d1e58d67497675050c6aa002244cebd9fc957ec5e364c46bab4735ea3db02b73b3ca43c96e019", + "0xab7ab92c5d4d773068e485aa5831941ebd63db7118674ca38089635f3b4186833af2455a6fb9ed2b745df53b3ce96727", + "0xaf191ca3089892cb943cd97cf11a51f38e38bd9be50844a4e8da99f27e305e876f9ed4ab0628e8ae3939066b7d34a15f", + "0xa3204c1747feabc2c11339a542195e7cb6628fd3964f846e71e2e3f2d6bb379a5e51700682ea1844eba12756adb13216", + "0x903a29883846b7c50c15968b20e30c471aeac07b872c40a4d19eb1a42da18b649d5bbfde4b4cf6225d215a461b0deb6d", + "0x8e6e9c15ffbf1e16e5865a5fef7ed751dc81957a9757b535cb38b649e1098cda25d42381dc4f776778573cdf90c3e6e0", + "0xa8f6dd26100b512a8c96c52e00715c4b2cb9ac457f17aed8ffe1cf1ea524068fe5a1ddf218149845fc1417b789ecfc98", + "0xa5b0ffc819451ea639cfd1c18cbc9365cc79368d3b2e736c0ae54eba2f0801e6eb0ee14a5f373f4a70ca463bdb696c09", + "0x879f91ccd56a1b9736fbfd20d8747354da743fb121f0e308a0d298ff0d9344431890e41da66b5009af3f442c636b4f43", + "0x81bf3a2d9755e206b515a508ac4d1109bf933c282a46a4ae4a1b4cb4a94e1d23642fad6bd452428845afa155742ade7e", + "0x8de778d4742f945df40004964e165592f9c6b1946263adcdd5a88b00244bda46c7bb49098c8eb6b3d97a0dd46148a8ca", + "0xb7a57b21d13121907ee28c5c1f80ee2e3e83a3135a8101e933cf57171209a96173ff5037f5af606e9fd6d066de6ed693", + "0xb0877d1963fd9200414a38753dffd9f23a10eb3198912790d7eddbc9f6b477019d52ddd4ebdcb9f60818db076938a5a9", + "0x88da2d7a6611bc16adc55fc1c377480c828aba4496c645e3efe0e1a67f333c05a0307f7f1d2df8ac013602c655c6e209", + "0x95719eb02e8a9dede1a888c656a778b1c69b7716fbe3d1538fe8afd4a1bc972183c7d32aa7d6073376f7701df80116d8", + "0x8e8a1ca971f2444b35af3376e85dccda3abb8e8e11d095d0a4c37628dfe5d3e043a377c3de68289ef142e4308e9941a0", + "0xb720caaff02f6d798ac84c4f527203e823ff685869e3943c979e388e1c34c3f77f5c242c6daa7e3b30e511aab917b866", + "0x86040d55809afeec10e315d1ad950d269d37cfee8c144cd8dd4126459e3b15a53b3e68df5981df3c2346d23c7b4baaf4", + "0x82d8cabf13ab853db0377504f0aec00dba3a5cd3119787e8ad378ddf2c40b022ecfc67c642b7acc8c1e3dd03ab50993e", + "0xb8d873927936719d2484cd03a6687d65697e17dcf4f0d5aed6f5e4750f52ef2133d4645894e7ebfc4ef6ce6788d404c8", + "0xb1235594dbb15b674a419ff2b2deb644ad2a93791ca05af402823f87114483d6aa1689b7a9bea0f547ad12fe270e4344", + "0xa53fda86571b0651f5affb74312551a082fffc0385cfd24c1d779985b72a5b1cf7c78b42b4f7e51e77055f8e5e915b00", + "0xb579adcfd9c6ef916a5a999e77a0cb21d378c4ea67e13b7c58709d5da23a56c2e54218691fc4ac39a4a3d74f88cc31f7", + "0xab79e584011713e8a2f583e483a91a0c2a40771b77d91475825b5acbea82db4262132901cb3e4a108c46d7c9ee217a4e", + "0xa0fe58ea9eb982d7654c8aaf9366230578fc1362f6faae0594f8b9e659bcb405dff4aac0c7888bbe07f614ecf0d800a6", + "0x867e50e74281f28ecd4925560e2e7a6f8911b135557b688254623acce0dbc41e23ac3e706a184a45d54c586edc416eb0", + "0x89f81b61adda20ea9d0b387a36d0ab073dc7c7cbff518501962038be19867042f11fcc7ff78096e5d3b68c6d8dc04d9b", + "0xa58ee91bb556d43cf01f1398c5811f76dc0f11efdd569eed9ef178b3b0715e122060ec8f945b4dbf6eebfa2b90af6fa6", + "0xac460be540f4c840def2eef19fc754a9af34608d107cbadb53334cf194cc91138d53b9538fcd0ec970b5d4aa455b224a", + "0xb09b91f929de52c09d48ca0893be6eb44e2f5210a6c394689dc1f7729d4be4e11d0474b178e80cea8c2ac0d081f0e811", + "0x8d37a442a76b06a02a4e64c2504aea72c8b9b020ab7bcc94580fe2b9603c7c50d7b1e9d70d2a7daea19c68667e8f8c31", + "0xa9838d4c4e3f3a0075a952cf7dd623307ec633fcc81a7cf9e52e66c31780de33dbb3d74c320dc7f0a4b72f7a49949515", + "0xa44766b6251af458fe4f5f9ed1e02950f35703520b8656f09fc42d9a2d38a700c11a7c8a0436ac2e5e9f053d0bb8ff91", + "0xad78d9481c840f5202546bea0d13c776826feb8b1b7c72e83d99a947622f0bf38a4208551c4c41beb1270d7792075457", + "0xb619ffa8733b470039451e224b777845021e8dc1125f247a4ff2476cc774657d0ff9c5279da841fc1236047de9d81c60", + "0xaf760b0a30a1d6af3bc5cd6686f396bd41779aeeb6e0d70a09349bd5da17ca2e7965afc5c8ec22744198fbe3f02fb331", + "0xa0cc209abdb768b589fcb7b376b6e1cac07743288c95a1cf1a0354b47f0cf91fca78a75c1fcafa6f5926d6c379116608", + "0x864add673c89c41c754eeb3cd8dcff5cdde1d739fce65c30e474a082bb5d813cba6412e61154ce88fdb6c12c5d9be35b", + "0xb091443b0ce279327dc37cb484e9a5b69b257a714ce21895d67539172f95ffa326903747b64a3649e99aea7bb10d03f7", + "0xa8c452b8c4ca8e0a61942a8e08e28f17fb0ef4c5b018b4e6d1a64038280afa2bf1169202f05f14af24a06ca72f448ccd", + "0xa23c24721d18bc48d5dcf70effcbef89a7ae24e67158d70ae1d8169ee75d9a051d34b14e9cf06488bac324fe58549f26", + "0x92a730e30eb5f3231feb85f6720489dbb1afd42c43f05a1610c6b3c67bb949ec8fde507e924498f4ffc646f7b07d9123", + "0x8dbe5abf4031ec9ba6bb06d1a47dd1121fb9e03b652804069250967fd5e9577d0039e233441b7f837a7c9d67ba18c28e", + "0xaa456bcfef6a21bb88181482b279df260297b3778e84594ebddbdf337e85d9e3d46ca1d0b516622fb0b103df8ec519b7", + "0xa3b31ae621bd210a2b767e0e6f22eb28fe3c4943498a7e91753225426168b9a26da0e02f1dc5264da53a5ad240d9f51b", + "0xaa8d66857127e6e71874ce2202923385a7d2818b84cb73a6c42d71afe70972a70c6bdd2aad1a6e8c5e4ca728382a8ea8", + "0xac7e8e7a82f439127a5e40558d90d17990f8229852d21c13d753c2e97facf077cf59582b603984c3dd3faebd80aff4f5", + "0x93a8bcf4159f455d1baa73d2ef2450dcd4100420de84169bbe28b8b7a5d1746273f870091a87a057e834f754f34204b1", + "0x89d0ebb287c3613cdcae7f5acc43f17f09c0213fc40c074660120b755d664109ffb9902ed981ede79e018ddb0c845698", + "0xa87ccbfad431406aadbee878d9cf7d91b13649d5f7e19938b7dfd32645a43b114eef64ff3a13201398bd9b0337832e5a", + "0x833c51d0d0048f70c3eefb4e70e4ff66d0809c41838e8d2c21c288dd3ae9d9dfaf26d1742bf4976dab83a2b381677011", + "0x8bcd6b1c3b02fffead432e8b1680bad0a1ac5a712d4225e220690ee18df3e7406e2769e1f309e2e803b850bc96f0e768", + "0xb61e3dbd88aaf4ff1401521781e2eea9ef8b66d1fac5387c83b1da9e65c2aa2a56c262dea9eceeb4ad86c90211672db0", + "0x866d3090db944ecf190dd0651abf67659caafd31ae861bab9992c1e3915cb0952da7c561cc7e203560a610f48fae633b", + "0xa5e8971543c14274a8dc892b0be188c1b4fbc75c692ed29f166e0ea80874bc5520c2791342b7c1d2fb5dd454b03b8a5b", + "0x8f2f9fc50471bae9ea87487ebd1bc8576ef844cc42d606af5c4c0969670fdf2189afd643e4de3145864e7773d215f37f", + "0xb1bb0f2527db6d51f42b9224383c0f96048bbc03d469bf01fe1383173ef8b1cc9455d9dd8ba04d46057f46949bfc92b5", + "0xaa7c99d906b4d7922296cfe2520473fc50137c03d68b7865c5bfb8adbc316b1034310ec4b5670c47295f4a80fb8d61e9", + "0xa5d1da4d6aba555919df44cbaa8ff79378a1c9e2cfdfbf9d39c63a4a00f284c5a5724e28ecbc2d9dba27fe4ee5018bd5", + "0xa8db53224f70af4d991b9aae4ffe92d2aa5b618ad9137784b55843e9f16cefbfd25ada355d308e9bbf55f6d2f7976fb3", + "0xb6536c4232bb20e22af1a8bb12de76d5fec2ad9a3b48af1f38fa67e0f8504ef60f305a73d19385095bb6a9603fe29889", + "0x87f7e371a1817a63d6838a8cf4ab3a8473d19ce0d4f40fd013c03d5ddd5f4985df2956531cc9f187928ef54c68f4f9a9", + "0xae13530b1dbc5e4dced9d909ea61286ec09e25c12f37a1ed2f309b0eb99863d236c3b25ed3484acc8c076ad2fa8cd430", + "0x98928d850247c6f7606190e687d5c94a627550198dbdbea0161ef9515eacdb1a0f195cae3bb293112179082daccf8b35", + "0x918528bb8e6a055ad4db6230d3a405e9e55866da15c4721f5ddd1f1f37962d4904aad7a419218fe6d906fe191a991806", + "0xb71e31a06afe065773dd3f4a6e9ef81c3292e27a3b7fdfdd452d03e05af3b6dd654c355f7516b2a93553360c6681a73a", + "0x8870b83ab78a98820866f91ac643af9f3ff792a2b7fda34185a9456a63abdce42bfe8ad4dc67f08a6392f250d4062df4", + "0x91eea1b668e52f7a7a5087fabf1cab803b0316f78d9fff469fbfde2162f660c250e4336a9eea4cb0450bd30ac067bc8b", + "0x8b74990946de7b72a92147ceac1bd9d55999a8b576e8df68639e40ed5dc2062cfcd727903133de482b6dca19d0aaed82", + "0x8ebad537fece090ebbab662bdf2618e21ca30cf6329c50935e8346d1217dcbe3c1fe1ea28efca369c6003ce0a94703c1", + "0xa8640479556fb59ebd1c40c5f368fbd960932fdbb782665e4a0e24e2bdb598fc0164ce8c0726d7759cfc59e60a62e182", + "0xa9a52a6bf98ee4d749f6d38be2c60a6d54b64d5cbe4e67266633dc096cf28c97fe998596707d31968cbe2064b72256bf", + "0x847953c48a4ce6032780e9b39d0ed4384e0be202c2bbe2dfda3910f5d87aa5cd3c2ffbfcfae4dddce16d6ab657599b95", + "0xb6f6e1485d3ec2a06abaecd23028b200b2e4a0096c16144d07403e1720ff8f9ba9d919016b5eb8dc5103880a7a77a1d3", + "0x98dfc2065b1622f596dbe27131ea60bef7a193b12922cecb27f8c571404f483014f8014572e86ae2e341ab738e4887ef", + "0xacb0d205566bacc87bbe2e25d10793f63f7a1f27fd9e58f4f653ceae3ffeba511eaf658e068fad289eeb28f9edbeb35b", + "0xae4411ed5b263673cee894c11fe4abc72a4bf642d94022a5c0f3369380fcdfc1c21e277f2902972252503f91ada3029a", + "0xac4a7a27ba390a75d0a247d93d4a8ef1f0485f8d373a4af4e1139369ec274b91b3464d9738eeaceb19cd6f509e2f8262", + "0x87379c3bf231fdafcf6472a79e9e55a938d851d4dd662ab6e0d95fd47a478ed99e2ad1e6e39be3c0fc4f6d996a7dd833", + "0x81316904b035a8bcc2041199a789a2e6879486ba9fddcba0a82c745cc8dd8374a39e523b91792170cd30be7aa3005b85", + "0xb8206809c6cd027ed019f472581b45f7e12288f89047928ba32b4856b6560ad30395830d71e5e30c556f6f182b1fe690", + "0x88d76c028f534a62e019b4a52967bb8642ede6becfa3807be68fdd36d366fc84a4ac8dc176e80a68bc59eb62caf5dff9", + "0x8c3b8be685b0f8aad131ee7544d0e12f223f08a6f8edaf464b385ac644e0ddc9eff7cc7cb5c1b50ab5d71ea0f41d2213", + "0x8d91410e004f76c50fdc05784157b4d839cb5090022c629c7c97a5e0c3536eeafee17a527b54b1165c3cd81774bb54ce", + "0xb25c2863bc28ec5281ce800ddf91a7e1a53f4c6d5da1e6c86ef4616e93bcf55ed49e297216d01379f5c6e7b3c1e46728", + "0x865f7b09ac3ca03f20be90c48f6975dd2588838c2536c7a3532a6aa5187ed0b709cd03d91ff4048061c10d0aa72b69ce", + "0xb3f7477c90c11596eb4f8bbf34adbcb832638c4ff3cdd090d4d477ee50472ac9ddaf5be9ad7eca3f148960d362bbd098", + "0x8db35fd53fca04faecd1c76a8227160b3ab46ac1af070f2492445a19d8ff7c25bbaef6c9fa0c8c088444561e9f7e4eb2", + "0xa478b6e9d058a2e01d2fc053b739092e113c23a6a2770a16afbef044a3709a9e32f425ace9ba7981325f02667c3f9609", + "0x98caa6bd38916c08cf221722a675a4f7577f33452623de801d2b3429595f988090907a7e99960fff7c076d6d8e877b31", + "0xb79aaaacefc49c3038a14d2ac468cfec8c2161e88bdae91798d63552cdbe39e0e02f9225717436b9b8a40a022c633c6e", + "0x845a31006c680ee6a0cc41d3dc6c0c95d833fcf426f2e7c573fa15b2c4c641fbd6fe5ebb0e23720cc3467d6ee1d80dc4", + "0xa1bc287e272cf8b74dbf6405b3a5190883195806aa351f1dc8e525aa342283f0a35ff687e3b434324dedee74946dd185", + "0xa4fd2dc8db75d3783a020856e2b3aa266dc6926e84f5c491ef739a3bddd46dc8e9e0fc1177937839ef1b18d062ffbb9e", + "0xacbf0d3c697f57c202bb8c5dc4f3fc341b8fc509a455d44bd86acc67cad2a04495d5537bcd3e98680185e8aa286f2587", + "0xa5caf423a917352e1b8e844f5968a6da4fdeae467d10c6f4bbd82b5eea46a660b82d2f5440d3641c717b2c3c9ed0be52", + "0x8a39d763c08b926599ab1233219c49c825368fad14d9afc7c0c039224d37c00d8743293fd21645bf0b91eaf579a99867", + "0xb2b53a496def0ba06e80b28f36530fbe0fb5d70a601a2f10722e59abee529369c1ae8fd0f2db9184dd4a2519bb832d94", + "0xa73980fcef053f1b60ebbb5d78ba6332a475e0b96a0c724741a3abf3b59dd344772527f07203cf4c9cb5155ebed81fa0", + "0xa070d20acce42518ece322c9db096f16aed620303a39d8d5735a0df6e70fbeceb940e8d9f5cc38f3314b2240394ec47b", + "0xa50cf591f522f19ca337b73089557f75929d9f645f3e57d4f241e14cdd1ea3fb48d84bcf05e4f0377afbb789fbdb5d20", + "0x82a5ffce451096aca8eeb0cd2ae9d83db3ed76da3f531a80d9a70a346359bf05d74863ce6a7c848522b526156a5e20cd", + "0x88e0e84d358cbb93755a906f329db1537c3894845f32b9b0b691c29cbb455373d9452fadd1e77e20a623f6eaf624de6f", + "0xaa07ac7b84a6d6838826e0b9e350d8ec75e398a52e9824e6b0da6ae4010e5943fec4f00239e96433f291fef9d1d1e609", + "0xac8887bf39366034bc63f6cc5db0c26fd27307cbc3d6cce47894a8a019c22dd51322fb5096edc018227edfafc053a8f6", + "0xb7d26c26c5b33f77422191dca94977588ab1d4b9ce7d0e19c4a3b4cd1c25211b78c328dbf81e755e78cd7d1d622ad23e", + "0x99a676d5af49f0ba44047009298d8474cabf2d5bca1a76ba21eff7ee3c4691a102fdefea27bc948ccad8894a658abd02", + "0xb0d09a91909ab3620c183bdf1d53d43d39eb750dc7a722c661c3de3a1a5d383ad221f71bae374f8a71867505958a3f76", + "0x84681a883de8e4b93d68ac10e91899c2bbb815ce2de74bb48a11a6113b2a3f4df8aceabda1f5f67bc5aacac8c9da7221", + "0x9470259957780fa9b43521fab3644f555f5343281c72582b56d2efd11991d897b3b481cafa48681c5aeb80c9663b68f7", + "0xab1b29f7ece686e6fa968a4815da1d64f3579fed3bc92e1f3e51cd13a3c076b6cf695ed269d373300a62463dc98a4234", + "0x8ab415bfcd5f1061f7687597024c96dd9c7cb4942b5989379a7a3b5742f7d394337886317659cbeacaf030234a24f972", + "0xb9b524aad924f9acc63d002d617488f31b0016e0f0548f050cada285ce7491b74a125621638f19e9c96eabb091d945be", + "0x8c4c373e79415061837dd0def4f28a2d5d74d21cb13a76c9049ad678ca40228405ab0c3941df49249847ecdefc1a5b78", + "0xa8edf4710b5ab2929d3db6c1c0e3e242261bbaa8bcec56908ddadd7d2dad2dca9d6eb9de630b960b122ebeea41040421", + "0x8d66bb3b50b9df8f373163629f9221b3d4b6980a05ea81dc3741bfe9519cf3ebba7ab98e98390bae475e8ede5821bd5c", + "0x8d3c21bae7f0cfb97c56952bb22084b58e7bb718890935b73103f33adf5e4d99cd262f929c6eeab96209814f0dbae50a", + "0xa5c66cfab3d9ebf733c4af24bebc97070e7989fe3c73e79ac85fb0e4d40ae44fb571e0fad4ad72560e13ed453900d14f", + "0x9362e6b50b43dbefbc3254471372297b5dcce809cd3b60bf74a1268ab68bdb50e46e462cbd78f0d6c056330e982846af", + "0x854630d08e3f0243d570cc2e856234cb4c1a158d9c1883bf028a76525aaa34be897fe918d5f6da9764a3735fa9ebd24a", + "0x8c7d246985469ff252c3f4df6c7c9196fc79f05c1c66a609d84725c78001d0837c7a7049394ba5cf7e863e2d58af8417", + "0xae050271e01b528925302e71903f785b782f7bf4e4e7a7f537140219bc352dc7540c657ed03d3a297ad36798ecdb98cd", + "0x8d2ae9179fcf2b0c69850554580b52c1f4a5bd865af5f3028f222f4acad9c1ad69a8ef6c7dc7b03715ee5c506b74325e", + "0xb8ef8de6ce6369a8851cd36db0ccf00a85077e816c14c4e601f533330af9e3acf0743a95d28962ed8bfcfc2520ef3cfe", + "0xa6ecad6fdfb851b40356a8b1060f38235407a0f2706e7b8bb4a13465ca3f81d4f5b99466ac2565c60af15f022d26732e", + "0x819ff14cdea3ab89d98e133cd2d0379361e2e2c67ad94eeddcdb9232efd509f51d12f4f03ebd4dd953bd262a886281f7", + "0x8561cd0f7a6dbcddd83fcd7f472d7dbcba95b2d4fb98276f48fccf69f76d284e626d7e41314b633352df8e6333fd52a1", + "0xb42557ccce32d9a894d538c48712cb3e212d06ac05cd5e0527ccd2db1078ee6ae399bf6a601ffdab1f5913d35fc0b20c", + "0x89b4008d767aad3c6f93c349d3b956e28307311a5b1cec237e8d74bb0dee7e972c24f347fd56afd915a2342bd7bc32f0", + "0x877487384b207e53f5492f4e36c832c2227f92d1bb60542cfeb35e025a4a7afc2b885fae2528b33b40ab09510398f83e", + "0x8c411050b63c9053dd0cd81dacb48753c3d7f162028098e024d17cd6348482703a69df31ad6256e3d25a8bbf7783de39", + "0xa8506b54a88d17ac10fb1b0d1fe4aa40eae7553a064863d7f6b52ccc4236dd4b82d01dca6ba87da9a239e3069ba879fb", + "0xb1a24caef9df64750c1350789bb8d8a0db0f39474a1c74ea9ba064b1516db6923f00af8d57c632d58844fb8786c3d47a", + "0x959d6e255f212b0708c58a2f75cb1fe932248c9d93424612c1b8d1e640149656059737e4db2139afd5556bcdacf3eda2", + "0x84525af21a8d78748680b6535bbc9dc2f0cf9a1d1740d12f382f6ecb2e73811d6c1da2ad9956070b1a617c61fcff9fe5", + "0xb74417d84597a485d0a8e1be07bf78f17ebb2e7b3521b748f73935b9afbbd82f34b710fb7749e7d4ab55b0c7f9de127d", + "0xa4a9aecb19a6bab167af96d8b9d9aa5308eab19e6bfb78f5a580f9bf89bdf250a7b52a09b75f715d651cb73febd08e84", + "0x9777b30be2c5ffe7d29cc2803a562a32fb43b59d8c3f05a707ab60ec05b28293716230a7d264d7cd9dd358fc031cc13e", + "0x95dce7a3d4f23ac0050c510999f5fbf8042f771e8f8f94192e17bcbfa213470802ebdbe33a876cb621cf42e275cbfc8b", + "0xb0b963ebcbbee847ab8ae740478544350b3ac7e86887e4dfb2299ee5096247cd2b03c1de74c774d9bde94ae2ee2dcd59", + "0xa4ab20bafa316030264e13f7ef5891a2c3b29ab62e1668fcb5881f50a9acac6adbe3d706c07e62f2539715db768f6c43", + "0x901478a297669d608e406fe4989be75264b6c8be12169aa9e0ad5234f459ca377f78484ffd2099a2fe2db5e457826427", + "0x88c76e5c250810c057004a03408b85cd918e0c8903dc55a0dd8bb9b4fc2b25c87f9b8cf5943eb19fbbe99d36490050c5", + "0x91607322bbad4a4f03fc0012d0821eff5f8c516fda45d1ec1133bface6f858bf04b25547be24159cab931a7aa08344d4", + "0x843203e07fce3c6c81f84bc6dc5fb5e9d1c50c8811ace522dc66e8658433a0ef9784c947e6a62c11bf705307ef05212e", + "0x91dd8813a5d6dddcda7b0f87f672b83198cd0959d8311b2b26fb1fae745185c01f796fbd03aad9db9b58482483fdadd8", + "0x8d15911aacf76c8bcd7136e958febd6963104addcd751ce5c06b6c37213f9c4fb0ffd4e0d12c8e40c36d658999724bfd", + "0x8a36c5732d3f1b497ebe9250610605ee62a78eaa9e1a45f329d09aaa1061131cf1d9df00f3a7d0fe8ad614a1ff9caaae", + "0xa407d06affae03660881ce20dab5e2d2d6cddc23cd09b95502a9181c465e57597841144cb34d22889902aff23a76d049", + "0xb5fd856d0578620a7e25674d9503be7d97a2222900e1b4738c1d81ff6483b144e19e46802e91161e246271f90270e6cf", + "0x91b7708869cdb5a7317f88c0312d103f8ce90be14fb4f219c2e074045a2a83636fdc3e69e862049fc7c1ef000e832541", + "0xb64719cc5480709d1dae958f1d3082b32a43376da446c8f9f64cb02a301effc9c34d9102051733315a8179aed94d53cc", + "0x94347a9542ff9d18f7d9eaa2f4d9b832d0e535fe49d52aa2de08aa8192400eddabdb6444a2a78883e27c779eed7fdf5a", + "0x840ef44a733ff1376466698cd26f82cf56bb44811e196340467f932efa3ae1ef9958a0701b3b032f50fd9c1d2aed9ab5", + "0x90ab3f6f67688888a31ffc2a882bb37adab32d1a4b278951a21646f90d03385fc976715fc639a785d015751171016f10", + "0xb56f35d164c24b557dbcbc8a4bfa681ec916f8741ffcb27fb389c164f4e3ed2be325210ef5bdaeae7a172ca9599ab442", + "0xa7921a5a80d7cf6ae81ba9ee05e0579b18c20cd2852762c89d6496aa4c8ca9d1ca2434a67b2c16d333ea8e382cdab1e3", + "0xa506bcfbd7e7e5a92f68a1bd87d07ad5fe3b97aeee40af2bf2cae4efcd77fff03f872732c5b7883aa6584bee65d6f8cb", + "0xa8c46cff58931a1ce9cbe1501e1da90b174cddd6d50f3dfdfb759d1d4ad4673c0a8feed6c1f24c7af32865a7d6c984e5", + "0xb45686265a83bff69e312c5149db7bb70ac3ec790dc92e392b54d9c85a656e2bf58596ce269f014a906eafc97461aa5f", + "0x8d4009a75ccb2f29f54a5f16684b93202c570d7a56ec1a8b20173269c5f7115894f210c26b41e8d54d4072de2d1c75d0", + "0xaef8810af4fc676bf84a0d57b189760ddc3375c64e982539107422e3de2580b89bd27aa6da44e827b56db1b5555e4ee8", + "0x888f0e1e4a34f48eb9a18ef4de334c27564d72f2cf8073e3d46d881853ac1424d79e88d8ddb251914890588937c8f711", + "0xb64b0aa7b3a8f6e0d4b3499fe54e751b8c3e946377c0d5a6dbb677be23736b86a7e8a6be022411601dd75012012c3555", + "0x8d57776f519f0dd912ea14f79fbab53a30624e102f9575c0bad08d2dc754e6be54f39b11278c290977d9b9c7c0e1e0ad", + "0xa018fc00d532ceb2e4de908a15606db9b6e0665dd77190e2338da7c87a1713e6b9b61554e7c1462f0f6d4934b960b15c", + "0x8c932be83ace46f65c78e145b384f58e41546dc0395270c1397874d88626fdeda395c8a289d602b4c312fe98c1311856", + "0x89174838e21639d6bdd91a0621f04dc056907b88e305dd66e46a08f6d65f731dea72ae87ca5e3042d609e8de8de9aa26", + "0xb7b7f508bb74f7a827ac8189daa855598ff1d96fa3a02394891fd105d8f0816224cd50ac4bf2ed1cf469ace516c48184", + "0xb31877ad682583283baadd68dc1bebd83f5748b165aadd7fe9ef61a343773b88bcd3a022f36d6c92f339b7bfd72820a9", + "0xb79d77260b25daf9126dab7a193df2d7d30542786fa1733ffaf6261734770275d3ca8bae1d9915d1181a78510b3439db", + "0x91894fb94cd4c1dd2ceaf9c53a7020c5799ba1217cf2d251ea5bc91ed26e1159dd758e98282ebe35a0395ef9f1ed15a0", + "0xab59895cdafd33934ceedfc3f0d5d89880482cba6c99a6db93245f9e41987efd76e0640e80aef31782c9a8c7a83fccec", + "0xaa22ea63654315e033e09d4d4432331904a6fc5fb1732557987846e3c564668ca67c60a324b4af01663a23af11a9ce4b", + "0xb53ba3ef342601467e1f71aa280e100fbabbd38518fa0193e0099505036ee517c1ac78e96e9baeb549bb6879bb698fb0", + "0x943fd69fd656f37487cca3605dc7e5a215fddd811caf228595ec428751fc1de484a0cb84c667fe4d7c35599bfa0e5e34", + "0x9353128b5ebe0dddc555093cf3e5942754f938173541033e8788d7331fafc56f68d9f97b4131e37963ab7f1c8946f5f1", + "0xa76cd3c566691f65cfb86453b5b31dbaf3cab8f84fe1f795dd1e570784b9b01bdd5f0b3c1e233942b1b5838290e00598", + "0x983d84b2e53ffa4ae7f3ba29ef2345247ea2377686b74a10479a0ef105ecf90427bf53b74c96dfa346d0f842b6ffb25b", + "0x92e0fe9063306894a2c6970c001781cff416c87e87cb5fbac927a3192655c3da4063e6fa93539f6ff58efac6adcc5514", + "0xb00a81f03c2b8703acd4e2e4c21e06973aba696415d0ea1a648ace2b0ea19b242fede10e4f9d7dcd61c546ab878bc8f9", + "0xb0d08d880f3b456a10bf65cff983f754f545c840c413aea90ce7101a66eb0a0b9b1549d6c4d57725315828607963f15a", + "0x90cb64d03534f913b411375cce88a9e8b1329ce67a9f89ca5df8a22b8c1c97707fec727dbcbb9737f20c4cf751359277", + "0x8327c2d42590dfcdb78477fc18dcf71608686ad66c49bce64d7ee874668be7e1c17cc1042a754bbc77c9daf50b2dae07", + "0x8532171ea13aa7e37178e51a6c775da469d2e26ec854eb16e60f3307db4acec110d2155832c202e9ba525fc99174e3b0", + "0x83ca44b15393d021de2a511fa5511c5bd4e0ac7d67259dce5a5328f38a3cce9c3a269405959a2486016bc27bb140f9ff", + "0xb1d36e8ca812be545505c8214943b36cabee48112cf0de369957afa796d37f86bf7249d9f36e8e990f26f1076f292b13", + "0x9803abf45be5271e2f3164c328d449efc4b8fc92dfc1225d38e09630909fe92e90a5c77618daa5f592d23fc3ad667094", + "0xb268ad68c7bf432a01039cd889afae815c3e120f57930d463aece10af4fd330b5bd7d8869ef1bcf6b2e78e4229922edc", + "0xa4c91a0d6f16b1553264592b4cbbbf3ca5da32ab053ffbdd3dbb1aed1afb650fb6e0dc5274f71a51d7160856477228db", + "0xad89d043c2f0f17806277ffdf3ecf007448e93968663f8a0b674254f36170447b7527d5906035e5e56f4146b89b5af56", + "0x8b6964f757a72a22a642e4d69102951897e20c21449184e44717bd0681d75f7c5bfa5ee5397f6e53febf85a1810d6ed1", + "0xb08f5cdaabec910856920cd6e836c830b863eb578423edf0b32529488f71fe8257d90aed4a127448204df498b6815d79", + "0xaf26bb3358be9d280d39b21d831bb53145c4527a642446073fee5a86215c4c89ff49a3877a7a549486262f6f57a0f476", + "0xb4010b37ec4d7c2af20800e272539200a6b623ae4636ecbd0e619484f4ab9240d02bc5541ace3a3fb955dc0a3d774212", + "0x82752ab52bdcc3cc2fc405cb05a2e694d3df4a3a68f2179ec0652536d067b43660b96f85f573f26fbd664a9ef899f650", + "0x96d392dde067473a81faf2d1fea55b6429126b88b160e39b4210d31d0a82833ffd3a80e07d24d495aea2d96be7251547", + "0xa76d8236d6671204d440c33ac5b8deb71fa389f6563d80e73be8b043ec77d4c9b06f9a586117c7f957f4af0331cbc871", + "0xb6c90961f68b5e385d85c9830ec765d22a425f506904c4d506b87d8944c2b2c09615e740ed351df0f9321a7b93979cae", + "0xa6ec5ea80c7558403485b3b1869cdc63bde239bafdf936d9b62a37031628402a36a2cfa5cfbb8e26ac922cb0a209b3ba", + "0x8c3195bbdbf9bc0fc95fa7e3d7f739353c947f7767d1e3cb24d8c8602d8ea0a1790ac30b815be2a2ba26caa5227891e2", + "0xa7f8a63d809f1155722c57f375ea00412b00147776ae4444f342550279ef4415450d6f400000a326bf11fea6c77bf941", + "0x97fa404df48433a00c85793440e89bb1af44c7267588ae937a1f5d53e01e1c4d4fc8e4a6d517f3978bfdd6c2dfde012f", + "0xa984a0a3836de3d8d909c4629a2636aacb85393f6f214a2ef68860081e9db05ad608024762db0dc35e895dc00e2d4cdd", + "0x9526cf088ab90335add1db4d3a4ac631b58cbfbe88fa0845a877d33247d1cfeb85994522e1eb8f8874651bfb1df03e2a", + "0xac83443fd0afe99ad49de9bf8230158c118e2814c9c89db5ac951c240d6c2ce45e7677221279d9e97848ec466b99aafe", + "0xaeeefdbaba612e971697798ceaf63b247949dc823a0ad771ae5b988a5e882b338a98d3d0796230f49d533ec5ba411b39", + "0xae3f248b5a7b0f92b7820a6c5ae21e5bd8f4265d4f6e21a22512079b8ee9be06393fd3133ce8ebac0faf23f4f8517e36", + "0xa64a831b908eee784b8388b45447d2885ec0551b26b0c2b15e5f417d0a12c79e867fb7bd3d008d0af98b44336f8ec1ad", + "0xb242238cd8362b6e440ba21806905714dd55172db25ec7195f3fc4937b2aba146d5cbf3cf691a1384b4752dc3b54d627", + "0x819f97f337eea1ffb2a678cc25f556f1aab751c6b048993a1d430fe1a3ddd8bb411c152e12ca60ec6e057c190cd1db9a", + "0xb9d7d187407380df54ee9fef224c54eec1bfabf17dc8abf60765b7951f538f59aa26fffd5846cfe05546c35f59b573f4", + "0xaa6e3c14efa6a5962812e3f94f8ce673a433f4a82d07a67577285ea0eaa07f8be7115853122d12d6d4e1fdf64c504be1", + "0x82268bee9c1662d3ddb5fb785abfae6fb8b774190f30267f1d47091d2cd4b3874db4372625aa36c32f27b0eee986269b", + "0xb236459565b7b966166c4a35b2fa71030b40321821b8e96879d95f0e83a0baf33fa25721f30af4a631df209e25b96061", + "0x8708d752632d2435d2d5b1db4ad1fa2558d776a013655f88e9a3556d86b71976e7dfe5b8834fdec97682cd94560d0d0d", + "0xae1424a68ae2dbfb0f01211f11773732a50510b5585c1fb005cb892b2c6a58f4a55490b5c5b4483c6fce40e9d3236a52", + "0xb3f5f722af9dddb07293c871ce97abbccba0093ca98c8d74b1318fa21396fc1b45b69c15084f63d728f9908442024506", + "0x9606f3ce5e63886853ca476dc0949e7f1051889d529365c0cb0296fdc02abd088f0f0318ecd2cf36740a3634132d36f6", + "0xb11a833a49fa138db46b25ff8cdda665295226595bc212c0931b4931d0a55c99da972c12b4ef753f7e37c6332356e350", + "0xafede34e7dab0a9e074bc19a7daddb27df65735581ca24ad70c891c98b1349fcebbcf3ba6b32c2617fe06a5818dabc2d", + "0x97993d456e459e66322d01f8eb13918979761c3e8590910453944bdff90b24091bb018ac6499792515c9923be289f99f", + "0x977e3e967eff19290a192cd11df3667d511b398fb3ac9a5114a0f3707e25a0edcb56105648b1b85a8b7519fc529fc6f6", + "0xb873a7c88bf58731fe1bf61ff6828bf114cf5228f254083304a4570e854e83748fc98683ddba62d978fff7909f2c5c47", + "0xad4b2691f6f19da1d123aaa23cca3e876247ed9a4ab23c599afdbc0d3aa49776442a7ceaa996ac550d0313d9b9a36cee", + "0xb9210713c78e19685608c6475bfa974b57ac276808a443f8b280945c5d5f9c39da43effa294bfb1a6c6f7b6b9f85bf6c", + "0xa65152f376113e61a0e468759de38d742caa260291b4753391ee408dea55927af08a4d4a9918600a3bdf1df462dffe76", + "0x8bf8c27ad5140dde7f3d2280fd4cc6b29ab76537e8d7aa7011a9d2796ee3e56e9a60c27b5c2da6c5e14fc866301dc195", + "0x92fde8effc9f61393a2771155812b863cff2a0c5423d7d40aa04d621d396b44af94ddd376c28e7d2f53c930aea947484", + "0x97a01d1dd9ee30553ce676011aea97fa93d55038ada95f0057d2362ae9437f3ed13de8290e2ff21e3167dd7ba10b9c3f", + "0x89affffaa63cb2df3490f76f0d1e1d6ca35c221dd34057176ba739fa18d492355e6d2a5a5ad93a136d3b1fed0bb8aa19", + "0x928b8e255a77e1f0495c86d3c63b83677b4561a5fcbbe5d3210f1e0fc947496e426d6bf3b49394a5df796c9f25673fc4", + "0x842a0af91799c9b533e79ee081efe2a634cac6c584c2f054fb7d1db67dde90ae36de36cbf712ec9cd1a0c7ee79e151ea", + "0xa65b946cf637e090baf2107c9a42f354b390e7316beb8913638130dbc67c918926eb87bec3b1fe92ef72bc77a170fa3b", + "0xaafc0f19bfd71ab5ae4a8510c7861458b70ad062a44107b1b1dbacbfa44ba3217028c2824bd7058e2fa32455f624040b", + "0x95269dc787653814e0be899c95dba8cfa384f575a25e671c0806fd80816ad6797dc819d30ae06e1d0ed9cb01c3950d47", + "0xa1e760f7fa5775a1b2964b719ff961a92083c5c617f637fc46e0c9c20ab233f8686f7f38c3cb27d825c54dd95e93a59b", + "0xac3b8a7c2317ea967f229eddc3e23e279427f665c4705c7532ed33443f1243d33453c1088f57088d2ab1e3df690a9cc9", + "0xb787beeddfbfe36dd51ec4efd9cf83e59e84d354c3353cc9c447be53ae53d366ed1c59b686e52a92f002142c8652bfe0", + "0xb7a64198300cb6716aa7ac6b25621f8bdec46ad5c07a27e165b3f774cdf65bcfdbf31e9bae0c16b44de4b00ada7a4244", + "0xb8ae9f1452909e0c412c7a7fe075027691ea8df1347f65a5507bc8848f1d2c833d69748076db1129e5b4fb912f65c86c", + "0x9682e41872456b9fa67def89e71f06d362d6c8ca85c9c48536615bc401442711e1c9803f10ab7f8ab5feaec0f9df20a6", + "0x88889ff4e271dc1c7e21989cc39f73cde2f0475acd98078281591ff6c944fadeb9954e72334319050205d745d4df73df", + "0x8f79b5b8159e7fd0d93b0645f3c416464f39aec353b57d99ecf24f96272df8a068ad67a6c90c78d82c63b40bb73989bb", + "0x838c01a009a3d8558a3f0bdd5e22de21af71ca1aefc8423c91dc577d50920e9516880e87dce3e6d086e11cd45c9052d9", + "0xb97f1c6eee8a78f137c840667cc288256e39294268a3009419298a04a1d0087c9c9077b33c917c65caf76637702dda8a", + "0x972284ce72f96a61c899260203dfa06fc3268981732bef74060641c1a5068ead723e3399431c247ca034b0dae861e8df", + "0x945a8d52d6d3db6663dbd3110c6587f9e9c44132045eeffba15621576d178315cb52870fa5861669f84f0bee646183fe", + "0xa0a547b5f0967b1c3e5ec6c6a9a99f0578521489180dfdfbb5561f4d166baac43a2f06f950f645ce991664e167537eed", + "0xa0592cda5cdddf1340033a745fd13a6eff2021f2e26587116c61c60edead067e0f217bc2bef4172a3c9839b0b978ab35", + "0xb9c223b65a3281587fa44ec829e609154b32f801fd1de6950e01eafb07a8324243b960d5735288d0f89f0078b2c42b5b", + "0x99ebfc3b8f9f98249f4d37a0023149ed85edd7a5abe062c8fb30c8c84555258b998bdcdd1d400bc0fa2a4aaa8b224466", + "0x955b68526e6cb3937b26843270f4e60f9c6c8ece2fa9308fe3e23afa433309c068c66a4bc16ee2cf04220f095e9afce4", + "0xb766caeafcc00378135ae53397f8a67ed586f5e30795462c4a35853de6681b1f17401a1c40958de32b197c083b7279c1", + "0x921bf87cad947c2c33fa596d819423c10337a76fe5a63813c0a9dc78a728207ae7b339407a402fc4d0f7cba3af6da6fc", + "0xa74ba1f3bc3e6c025db411308f49b347ec91da1c916bda9da61e510ec8d71d25e0ac0f124811b7860e5204f93099af27", + "0xa29b4d144e0bf17a7e8353f2824cef0ce85621396babe8a0b873ca1e8a5f8d508b87866cf86da348470649fceefd735c", + "0xa8040e12ffc3480dd83a349d06741d1572ef91932c46f5cf03aee8454254156ee95786fd013d5654725e674c920cec32", + "0x8c4cf34ca60afd33923f219ffed054f90cd3f253ffeb2204a3b61b0183417e366c16c07fae860e362b0f2bfe3e1a1d35", + "0x8195eede4ddb1c950459df6c396b2e99d83059f282b420acc34220cadeed16ab65c856f2c52568d86d3c682818ed7b37", + "0x91fff19e54c15932260aa990c7fcb3c3c3da94845cc5aa8740ef56cf9f58d19b4c3c55596f8d6c877f9f4d22921d93aa", + "0xa3e0bf7e5d02a80b75cf75f2db7e66cb625250c45436e3c136d86297d652590ec97c2311bafe407ad357c79ab29d107b", + "0x81917ff87e5ed2ae4656b481a63ced9e6e5ff653b8aa6b7986911b8bc1ee5b8ef4f4d7882c3f250f2238e141b227e510", + "0x915fdbe5e7de09c66c0416ae14a8750db9412e11dc576cf6158755fdcaf67abdbf0fa79b554cac4fe91c4ec245be073f", + "0x8df27eafb5c3996ba4dc5773c1a45ca77e626b52e454dc1c4058aa94c2067c18332280630cc3d364821ee53bf2b8c130", + "0x934f8a17c5cbb827d7868f5c8ca00cb027728a841000a16a3428ab16aa28733f16b52f58c9c4fbf75ccc45df72d9c4df", + "0xb83f4da811f9183c25de8958bc73b504cf790e0f357cbe74ef696efa7aca97ad3b7ead1faf76e9f982c65b6a4d888fc2", + "0x87188213c8b5c268dc2b6da413f0501c95749e953791b727450af3e43714149c115b596b33b63a2f006a1a271b87efd0", + "0x83e9e888ab9c3e30761de635d9aabd31248cdd92f7675fc43e4b21fd96a03ec1dc4ad2ec94fec857ffb52683ac98e360", + "0xb4b9a1823fe2d983dc4ec4e3aaea297e581c3fc5ab4b4af5fa1370caa37af2d1cc7fc6bfc5e7da60ad8fdce27dfe4b24", + "0x856388bc78aef465dbcdd1f559252e028c9e9a2225c37d645c138e78f008f764124522705822a61326a6d1c79781e189", + "0xa6431b36db93c3b47353ba22e7c9592c9cdfb9cbdd052ecf2cc3793f5b60c1e89bc96e6bae117bfd047f2308da00dd2f", + "0xb619972d48e7e4291542dcde08f7a9cdc883c892986ded2f23ccb216e245cd8d9ad1d285347b0f9d7611d63bf4cee2bc", + "0x8845cca6ff8595955f37440232f8e61d5351500bd016dfadd182b9d39544db77a62f4e0102ff74dd4173ae2c181d24ef", + "0xb2f5f7fa26dcd3b6550879520172db2d64ee6aaa213cbef1a12befbce03f0973a22eb4e5d7b977f466ac2bf8323dcedd", + "0x858b7f7e2d44bdf5235841164aa8b4f3d33934e8cb122794d90e0c1cac726417b220529e4f896d7b77902ab0ccd35b3a", + "0x80b0408a092dae2b287a5e32ea1ad52b78b10e9c12f49282976cd738f5d834e03d1ad59b09c5ccaccc39818b87d06092", + "0xb996b0a9c6a2d14d984edcd6ab56bc941674102980d65b3ad9733455f49473d3f587c8cbf661228a7e125ddbe07e3198", + "0x90224fcebb36865293bd63af786e0c5ade6b67c4938d77eb0cbae730d514fdd0fe2d6632788e858afd29d46310cf86df", + "0xb71351fdfff7168b0a5ec48397ecc27ac36657a8033d9981e97002dcca0303e3715ce6dd3f39423bc8ef286fa2e9e669", + "0xae2a3f078b89fb753ce4ed87e0c1a58bb19b4f0cfb6586dedb9fcab99d097d659a489fb40e14651741e1375cfc4b6c5f", + "0x8ef476b118e0b868caed297c161f4231bbeb863cdfa5e2eaa0fc6b6669425ce7af50dc374abceac154c287de50c22307", + "0x92e46ab472c56cfc6458955270d3c72b7bde563bb32f7d4ab4d959db6f885764a3d864e1aa19802fefaa5e16b0cb0b54", + "0x96a3f68323d1c94e73d5938a18a377af31b782f56212de3f489d22bc289cf24793a95b37f1d6776edf88114b5c1fa695", + "0x962cc068cfce6faaa27213c4e43e44eeff0dfbb6d25b814e82c7da981fb81d7d91868fa2344f05fb552362f98cfd4a72", + "0x895d4e4c4ad670abf66d43d59675b1add7afad7438ada8f42a0360c704cee2060f9ac15b4d27e9b9d0996bb801276fe3", + "0xb3ad18d7ece71f89f2ef749b853c45dc56bf1c796250024b39a1e91ed11ca32713864049c9aaaea60cde309b47486bbf", + "0x8f05404e0c0258fdbae50e97ccb9b72ee17e0bd2400d9102c0dad981dac8c4c71585f03e9b5d50086d0a2d3334cb55d1", + "0x8bd877e9d4591d02c63c6f9fc9976c109de2d0d2df2bfa5f6a3232bab5b0b8b46e255679520480c2d7a318545efa1245", + "0x8d4c16b5d98957c9da13d3f36c46f176e64e5be879f22be3179a2c0e624fe4758a82bf8c8027410002f973a3b84cd55a", + "0x86e2a8dea86427b424fa8eada881bdff896907084a495546e66556cbdf070b78ba312bf441eb1be6a80006d25d5097a3", + "0x8608b0c117fd8652fdab0495b08fadbeba95d9c37068e570de6fddfef1ba4a1773b42ac2be212836141d1bdcdef11a17", + "0xa13d6febf5fb993ae76cae08423ca28da8b818d6ef0fde32976a4db57839cd45b085026b28ee5795f10a9a8e3098c683", + "0x8e261967fa6de96f00bc94a199d7f72896a6ad8a7bbb1d6187cca8fad824e522880e20f766620f4f7e191c53321d70f9", + "0x8b8e8972ac0218d7e3d922c734302803878ad508ca19f5f012bc047babd8a5c5a53deb5fe7c15a4c00fd6d1cb9b1dbd0", + "0xb5616b233fb3574a2717d125a434a2682ff68546dccf116dd8a3b750a096982f185614b9fb6c7678107ff40a451f56fa", + "0xaa6adf9b0c3334b0d0663f583a4914523b2ac2e7adffdb026ab9109295ff6af003ef8357026dbcf789896d2afded8d73", + "0xacb72df56a0b65496cd534448ed4f62950bb1e11e50873b6ed349c088ee364441821294ce0f7c61bd7d38105bea3b442", + "0xabae12df83e01ec947249fedd0115dc501d2b03ff7232092979eda531dbbca29ace1d46923427c7dde4c17bdf3fd7708", + "0x820b4fc2b63a9fda7964acf5caf19a2fc4965007cb6d6b511fcafcb1f71c3f673a1c0791d3f86e3a9a1eb6955b191cc0", + "0xaf277259d78c6b0f4f030a10c53577555df5e83319ddbad91afbd7c30bc58e7671c56d00d66ec3ab5ef56470cd910cee", + "0xad4a861c59f1f5ca1beedd488fb3d131dea924fffd8e038741a1a7371fad7370ca5cf80dc01f177fbb9576713bb9a5b3", + "0xb67a5162982ce6a55ccfb2f177b1ec26b110043cf18abd6a6c451cf140b5af2d634591eb4f28ad92177d8c7e5cd0a5e8", + "0x96176d0a83816330187798072d449cbfccff682561e668faf6b1220c9a6535b32a6e4f852e8abb00f79abb87493df16b", + "0xb0afe6e7cb672e18f0206e4423f51f8bd0017bf464c4b186d46332c5a5847647f89ff7fa4801a41c1b0b42f6135bcc92", + "0x8fc5e7a95ef20c1278c645892811f6fe3f15c431ebc998a32ec0da44e7213ea934ed2be65239f3f49b8ec471e9914160", + "0xb7793e41adda6c82ba1f2a31f656f6205f65bf8a3d50d836ee631bc7ce77c153345a2d0fc5c60edf8b37457c3729c4ec", + "0xa504dd7e4d6b2f4379f22cc867c65535079c75ccc575955f961677fa63ecb9f74026fa2f60c9fb6323c1699259e5e9c8", + "0xab899d00ae693649cc1afdf30fb80d728973d2177c006e428bf61c7be01e183866614e05410041bc82cb14a33330e69c", + "0x8a3bd8b0b1be570b65c4432a0f6dc42f48a2000e30ab089cf781d38f4090467b54f79c0d472fcbf18ef6a00df69cc6f3", + "0xb4d7028f7f76a96a3d7803fca7f507ae11a77c5346e9cdfccb120a833a59bda1f4264e425aa588e7a16f8e7638061d84", + "0xb9c7511a76ea5fb105de905d44b02edb17008335766ee357ed386b7b3cf19640a98b38785cb14603c1192bee5886c9b6", + "0x8563afb12e53aed71ac7103ab8602bfa8371ae095207cb0d59e8fd389b6ad1aff0641147e53cb6a7ca16c7f37c9c5e6b", + "0x8e108be614604e09974a9ed90960c28c4ea330a3d9a0cb4af6dd6f193f84ab282b243ecdf549b3131036bebc8905690c", + "0xb794d127fbedb9c5b58e31822361706ffac55ce023fbfe55716c3c48c2fd2f2c7660a67346864dfe588812d369cb50b6", + "0xb797a3442fc3b44f41baefd30346f9ac7f96e770d010d53c146ce74ce424c10fb62758b7e108b8abfdc5fafd89d745cb", + "0x993bb71e031e8096442e6205625e1bfddfe6dd6a83a81f3e2f84fafa9e5082ab4cad80a099f21eff2e81c83457c725c3", + "0x8711ab833fc03e37acf2e1e74cfd9133b101ff4144fe30260654398ae48912ab46549d552eb9d15d2ea57760d35ac62e", + "0xb21321fd2a12083863a1576c5930e1aecb330391ef83326d9d92e1f6f0d066d1394519284ddab55b2cb77417d4b0292f", + "0x877d98f731ffe3ee94b0b5b72d127630fa8a96f6ca4f913d2aa581f67732df6709493693053b3e22b0181632ac6c1e3b", + "0xae391c12e0eb8c145103c62ea64f41345973311c3bf7281fa6bf9b7faafac87bcf0998e5649b9ef81e288c369c827e07", + "0xb83a2842f36998890492ab1cd5a088d9423d192681b9a3a90ec518d4c541bce63e6c5f4df0f734f31fbfdd87785a2463", + "0xa21b6a790011396e1569ec5b2a423857b9bec16f543e63af28024e116c1ea24a3b96e8e4c75c6537c3e4611fd265e896", + "0xb4251a9c4aab3a495da7a42e684ba4860dbcf940ad1da4b6d5ec46050cbe8dab0ab9ae6b63b5879de97b905723a41576", + "0x8222f70aebfe6ac037f8543a08498f4cadb3edaac00336fc00437eb09f2cba758f6c38e887cc634b4d5b7112b6334836", + "0x86f05038e060594c46b5d94621a1d9620aa8ba59a6995baf448734e21f58e23c1ea2993d3002ad5250d6edd5ba59b34f", + "0xa7c0c749baef811ab31b973c39ceb1d94750e2bc559c90dc5eeb20d8bb6b78586a2b363c599ba2107d6be65cd435f24e", + "0x861d46a5d70b38d6c1cd72817a2813803d9f34c00320c8b62f8b9deb67f5b5687bc0b37c16d28fd017367b92e05da9ca", + "0xb3365d3dab639bffbe38e35383686a435c8c88b397b717cd4aeced2772ea1053ceb670f811f883f4e02975e5f1c4ac58", + "0xa5750285f61ab8f64cd771f6466e2c0395e01b692fd878f2ef2d5c78bdd8212a73a3b1dfa5e4c8d9e1afda7c84857d3b", + "0x835a10809ccf939bc46cf950a33b36d71be418774f51861f1cd98a016ade30f289114a88225a2c11e771b8b346cbe6ef", + "0xa4f59473a037077181a0a62f1856ec271028546ca9452b45cedfcb229d0f4d1aabfc13062b07e536cc8a0d4b113156a2", + "0x95cd14802180b224d44a73cc1ed599d6c4ca62ddcaa503513ccdc80aaa8be050cc98bd4b4f3b639549beb4587ac6caf9", + "0x973b731992a3e69996253d7f36dd7a0af1982b5ed21624b77a7965d69e9a377b010d6dabf88a8a97eec2a476259859cc", + "0xaf8a1655d6f9c78c8eb9a95051aa3baaf9c811adf0ae8c944a8d3fcba87b15f61021f3baf6996fa0aa51c81b3cb69de1", + "0x835aad5c56872d2a2d6c252507b85dd742bf9b8c211ccb6b25b52d15c07245b6d89b2a40f722aeb5083a47cca159c947", + "0xabf4e970b02bef8a102df983e22e97e2541dd3650b46e26be9ee394a3ea8b577019331857241d3d12b41d4eacd29a3ac", + "0xa13c32449dbedf158721c13db9539ae076a6ce5aeaf68491e90e6ad4e20e20d1cdcc4a89ed9fd49cb8c0dd50c17633c1", + "0x8c8f78f88b7e22dd7e9150ab1c000f10c28e696e21d85d6469a6fe315254740f32e73d81ab1f3c1cf8f544c86df506e8", + "0xb4b77f2acfe945abf81f2605f906c10b88fb4d28628487fb4feb3a09f17f28e9780445dfcee4878349d4c6387a9d17d4", + "0x8d255c235f3812c6ecc646f855fa3832be5cb4dbb9c9e544989fafdf3f69f05bfd370732eaf954012f0044aa013fc9c6", + "0xb982efd3f34b47df37c910148ac56a84e8116647bea24145a49e34e0a6c0176e3284d838dae6230cb40d0be91c078b85", + "0x983f365aa09bd85df2a6a2ad8e4318996b1e27d02090755391d4486144e40d80b1fbfe1c798d626db92f52e33aa634da", + "0x95fd1981271f3ea3a41d654cf497e6696730d9ff7369f26bc4d7d15c7adb4823dd0c42e4a005a810af12d234065e5390", + "0xa9f5219bd4b913c186ef30c02f995a08f0f6f1462614ea5f236964e02bdaa33db9d9b816c4aee5829947840a9a07ba60", + "0x9210e6ceb05c09b46fd09d036287ca33c45124ab86315e5d6911ff89054f1101faaa3e83d123b7805056d388bcec6664", + "0x8ed9cbf69c6ff3a5c62dd9fe0d7264578c0f826a29e614bc2fb4d621d90c8c9992438accdd7a614b1dca5d1bb73dc315", + "0x85cf2a8cca93e00da459e3cecd22c342d697eee13c74d5851634844fc215f60053cf84b0e03c327cb395f48d1c71a8a4", + "0x8818a18e9a2ec90a271b784400c1903089ffb0e0b40bc5abbbe12fbebe0f731f91959d98c5519ef1694543e31e2016d4", + "0x8dabc130f296fa7a82870bf9a8405aaf542b222ed9276bba9bd3c3555a0f473acb97d655ee7280baff766a827a8993f0", + "0xac7952b84b0dc60c4d858f034093b4d322c35959605a3dad2b806af9813a4680cb038c6d7f4485b4d6b2ff502aaeca25", + "0xad65cb6d57b48a2602568d2ec8010baed0eb440eec7638c5ec8f02687d764e9de5b5d42ad5582934e592b48471c22d26", + "0xa02ab8bd4c3d114ea23aebdd880952f9495912817da8c0c08eabc4e6755439899d635034413d51134c72a6320f807f1c", + "0x8319567764b8295402ec1ebef4c2930a138480b37e6d7d01c8b4c9cd1f2fc3f6e9a44ae6e380a0c469b25b06db23305f", + "0xafec53b2301dc0caa8034cd9daef78c48905e6068d692ca23d589b84a6fa9ddc2ed24a39480597e19cb3e83eec213b3f", + "0xac0b4ffdb5ae08e586a9cdb98f9fe56f4712af3a97065e89e274feacfb52b53c839565aee93c4cfaaccfe51432c4fab0", + "0x8972cbf07a738549205b1094c5987818124144bf187bc0a85287c94fdb22ce038c0f11df1aa16ec5992e91b44d1af793", + "0xb7267aa6f9e3de864179b7da30319f1d4cb2a3560f2ea980254775963f1523b44c680f917095879bebfa3dc2b603efcf", + "0x80f68f4bfc337952e29504ee5149f15093824ea7ab02507efd1317a670f6cbc3611201848560312e3e52e9d9af72eccf", + "0x8897fee93ce8fc1e1122e46b6d640bba309384dbd92e46e185e6364aa8210ebf5f9ee7e5e604b6ffba99aa80a10dd7d0", + "0xb58ea6c02f2360be60595223d692e82ee64874fda41a9f75930f7d28586f89be34b1083e03bbc1575bbfdda2d30db1ea", + "0x85a523a33d903280d70ac5938770453a58293480170c84926457ac2df45c10d5ff34322ab130ef4a38c916e70d81af53", + "0xa2cbf045e1bed38937492c1f2f93a5ba41875f1f262291914bc1fc40c60bd0740fb3fea428faf6da38b7c180fe8ac109", + "0x8c09328770ed8eb17afc6ac7ddd87bb476de18ed63cab80027234a605806895959990c47bd10d259d7f3e2ecb50074c9", + "0xb4b9e19edb4a33bde8b7289956568a5b6b6557404e0a34584b5721fe6f564821091013fbb158e2858c6d398293bb4b59", + "0x8a47377df61733a2aa5a0e945fce00267f8e950f37e109d4487d92d878fb8b573317bb382d902de515b544e9e233458d", + "0xb5804c9d97efeff5ca94f3689b8088c62422d92a1506fd1d8d3b1b30e8a866ad0d6dad4abfa051dfc4471250cac4c5d9", + "0x9084a6ee8ec22d4881e9dcc8a9eb3c2513523d8bc141942370fd191ad2601bf9537a0b1e84316f3209b3d8a54368051e", + "0x85447eea2fa26656a649f8519fa67279183044791d61cf8563d0783d46d747d96af31d0a93507bbb2242666aa87d3720", + "0x97566a84481027b60116c751aec552adfff2d9038e68d48c4db9811fb0cbfdb3f1d91fc176a0b0d988a765f8a020bce1", + "0xae87e5c1b9e86c49a23dceda4ecfd1dcf08567f1db8e5b6ec752ebd45433c11e7da4988573cdaebbb6f4135814fc059e", + "0xabee05cf9abdbc52897ac1ce9ed157f5466ed6c383d6497de28616238d60409e5e92619e528af8b62cc552bf09970dc2", + "0xae6d31cd7bf9599e5ee0828bab00ceb4856d829bba967278a73706b5f388465367aa8a6c7da24b5e5f1fdd3256ef8e63", + "0xac33e7b1ee47e1ee4af472e37ab9e9175260e506a4e5ce449788075da1b53c44cb035f3792d1eea2aa24b1f688cc6ed3", + "0x80f65b205666b0e089bb62152251c48c380a831e5f277f11f3ef4f0d52533f0851c1b612267042802f019ec900dc0e8f", + "0x858520ad7aa1c9fed738e3b583c84168f2927837ad0e1d326afe9935c26e9b473d7f8c382e82ef1fe37d2b39bb40a1ee", + "0xb842dd4af8befe00a97c2d0f0c33c93974761e2cb9e5ab8331b25170318ddd5e4bdbc02d8f90cbfdd5f348f4f371c1f7", + "0x8bf2cb79bc783cb57088aae7363320cbeaabd078ffdec9d41bc74ff49e0043d0dad0086a30e5112b689fd2f5a606365d", + "0x982eb03bbe563e8850847cd37e6a3306d298ab08c4d63ab6334e6b8c1fa13fce80cf2693b09714c7621d74261a0ff306", + "0xb143edb113dec9f1e5105d4a93fbe502b859e587640d3db2f628c09a17060e6aec9e900e2c8c411cda99bc301ff96625", + "0xaf472d9befa750dcebc5428fe1a024f18ec1c07bca0f95643ce6b5f4189892a910285afb03fd7ed7068fbe614e80d33c", + "0xa97e3bc57ede73ecd1bbf02de8f51b4e7c1a067da68a3cd719f4ba26a0156cbf1cef2169fd35a18c5a4cced50d475998", + "0xa862253c937cf3d75d7183e5f5be6a4385d526aeda5171c1c60a8381fea79f88f5f52a4fab244ecc70765d5765e6dfd5", + "0x90cb776f8e5a108f1719df4a355bebb04bf023349356382cae55991b31720f0fd03206b895fa10c56c98f52453be8778", + "0xa7614e8d0769dccd520ea4b46f7646e12489951efaef5176bc889e9eb65f6e31758df136b5bf1e9107e68472fa9b46ec", + "0xac3a9b80a3254c42e5ed3a090a0dd7aee2352f480de96ad187027a3bb6c791eddfc3074b6ffd74eea825188f107cda4d", + "0x82a01d0168238ef04180d4b6e0a0e39024c02c2d75b065017c2928039e154d093e1af4503f4d1f3d8a948917abb5d09f", + "0x8fab000a2b0eef851a483aec8d2dd85fe60504794411a2f73ed82e116960547ac58766cb73df71aea71079302630258d", + "0x872451a35c6db61c63e9b8bb9f16b217f985c20be4451c14282c814adb29d7fb13f201367c664435c7f1d4d9375d7a58", + "0x887d9ff54cc96b35d562df4a537ff972d7c4b3fd91ab06354969a4cfede0b9fc68bbffb61d0dbf1a58948dc701e54f5a", + "0x8cb5c2a6bd956875d88f41ae24574434f1308514d44057b55c9c70f13a3366ed054150eed0955a38fda3f757be73d55f", + "0x89ad0163cad93e24129d63f8e38422b7674632a8d0a9016ee8636184cab177659a676c4ee7efba3abe1a68807c656d60", + "0xb9ec01c7cab6d00359b5a0b4a1573467d09476e05ca51a9227cd16b589a9943d161eef62dcc73f0de2ec504d81f4d252", + "0x8031d17635d39dfe9705c485d2c94830b6fc9bc67b91300d9d2591b51e36a782e77ab5904662effa9382d9cca201f525", + "0x8be5a5f6bc8d680e5092d6f9a6585acbaaaa2ddc671da560dcf5cfa4472f4f184b9597b5b539438accd40dda885687cc", + "0xb1fc0f052fae038a2e3de3b3a96b0a1024b009de8457b8b3adb2d315ae68a89af905720108a30038e5ab8d0d97087785", + "0x8b8bdc77bd3a6bc7ca5492b6f8c614852c39a70d6c8a74916eaca0aeb4533b11898b8820a4c2620a97bf35e275480029", + "0xaf35f4dc538d4ad5cdf710caa38fd1eb496c3fa890a047b6a659619c5ad3054158371d1e88e0894428282eed9f47f76b", + "0x8166454a7089cc07758ad78724654f4e7a1a13e305bbf88ddb86f1a4b2904c4fc8ab872d7da364cdd6a6c0365239e2ad", + "0xab287c7d3addce74ce40491871c768abe01daaa0833481276ff2e56926b38a7c6d2681ffe837d2cc323045ad1a4414f9", + "0xb90317f4505793094d89365beb35537f55a6b5618904236258dd04ca61f21476837624a2f45fef8168acf732cab65579", + "0x98ae5ea27448e236b6657ab5ef7b1cccb5372f92ab25f5fa651fbac97d08353a1dae1b280b1cd42b17d2c6a70a63ab9d", + "0xadcf54e752d32cbaa6cb98fbca48d8cd087b1db1d131d465705a0d8042c8393c8f4d26b59006eb50129b21e6240f0c06", + "0xb591a3e4db18a7345fa935a8dd7994bbac5cc270b8ebd84c8304c44484c7a74afb45471fdbe4ab22156a30fae1149b40", + "0x806b53ac049a42f1dcc1d6335505371da0bf27c614f441b03bbf2e356be7b2fb4eed7117eabcce9e427a542eaa2bf7d8", + "0x800482e7a772d49210b81c4a907f5ce97f270b959e745621ee293cf8c71e8989363d61f66a98f2d16914439544ca84c7", + "0x99de9eafdad3617445312341644f2bb888680ff01ce95ca9276b1d2e5ef83fa02dab5e948ebf66c17df0752f1bd37b70", + "0x961ee30810aa4c93ae157fbe9009b8e443c082192bd36a73a6764ff9b2ad8b0948fe9a73344556e01399dd77badb4257", + "0xae0a361067c52efbe56c8adf982c00432cd478929459fc7f74052c8ee9531cd031fe1335418fde53f7c2ef34254eb7ac", + "0xa3503d16b6b27eb20c1b177bcf90d13706169220523a6271b85b2ce35a9a2b9c5bed088540031c0a4ebfdae3a4c6ab04", + "0x909420122c3e723289ca4e7b81c2df5aff312972a2203f4c45821b176e7c862bf9cac7f7df3adf1d59278f02694d06e7", + "0x989f42380ae904b982f85d0c6186c1aef5d6bcba29bcfbb658e811b587eb2749c65c6e4a8cc6409c229a107499a4f5d7", + "0x8037a6337195c8e26a27ea4ef218c6e7d79a9720aaab43932d343192abc2320fe72955f5e431c109093bda074103330a", + "0xb312e168663842099b88445e940249cc508f080ab0c94331f672e7760258dbd86be5267e4cf25ea25facb80bff82a7e9", + "0xaaa3ff8639496864fcdbfdda1ac97edc4f08e3c9288b768f6c8073038c9fbbf7e1c4bea169b4d45c31935cdf0680d45e", + "0x97dbd3df37f0b481a311dfc5f40e59227720f367912200d71908ef6650f32cc985cb05b981e3eea38958f7e48d10a15d", + "0xa89d49d1e267bb452d6cb621b9a90826fe55e9b489c0427b94442d02a16f390eed758e209991687f73f6b5a032321f42", + "0x9530dea4e0e19d6496f536f2e75cf7d814d65fde567055eb20db48fd8d20d501cd2a22fb506db566b94c9ee10f413d43", + "0x81a7009b9e67f1965fa7da6a57591c307de91bf0cd35ab4348dc4a98a4961e096d004d7e7ad318000011dc4342c1b809", + "0x83440a9402b766045d7aca61a58bba2aa29cac1cf718199e472ba086f5d48093d9dda4d135292ba51d049a23964eceae", + "0xa06c9ce5e802df14f6b064a3d1a0735d429b452f0e2e276042800b0a4f16df988fd94cf3945921d5dd3802ab2636f867", + "0xb1359e358b89936dee9e678a187aad3e9ab14ac40e96a0a68f70ee2583cdcf467ae03bef4215e92893f4e12f902adec8", + "0x835304f8619188b4d14674d803103d5a3fa594d48e96d9699e653115dd05fdc2dda6ba3641cf7ad53994d448da155f02", + "0x8327cba5a9ff0d3f5cd0ae55e77167448926d5fcf76550c0ad978092a14122723090c51c415e88e42a2b62eb07cc3981", + "0xb373dcdaea85f85ce9978b1426a7ef4945f65f2d3467a9f1cc551a99766aac95df4a09e2251d3f89ca8c9d1a7cfd7b0e", + "0xab1422dc41af2a227b973a6fd124dfcb2367e2a11a21faa1d381d404f51b7257e5bc82e9cf20cd7fe37d7ae761a2ab37", + "0xa93774a03519d2f20fdf2ef46547b0a5b77c137d6a3434b48d56a2cbef9e77120d1b85d0092cf8842909213826699477", + "0x8eb967a495a38130ea28711580b7e61bcd1d051cd9e4f2dbf62f1380bd86e0d60e978d72f6f31e909eb97b3b9a2b867c", + "0xae8213378da1287ba1fe4242e1acaec19b877b6fe872400013c6eac1084b8d03156792fa3020201725b08228a1e80f49", + "0xb143daf6893d674d607772b3b02d8ac48f294237e2f2c87963c0d4e26d9227d94a2a13512457c3d5883544bbc259f0ef", + "0xb343bd2aca8973888e42542218924e2dda2e938fd1150d06878af76f777546213912b7c7a34a0f94186817d80ffa185c", + "0xb188ebc6a8c3007001aa347ae72cc0b15d09bc6c19a80e386ee4b334734ec0cc2fe8b493c2422f38d1e6d133cc3db6fe", + "0xb795f6a8b9b826aaeee18ccd6baf6c5adeeec85f95eb5b6d19450085ec7217e95a2d9e221d77f583b297d0872073ba0e", + "0xb1c7dbd998ad32ae57bfa95deafa147024afd57389e98992c36b6e52df915d3d5a39db585141ec2423173e85d212fed8", + "0x812bcdeb9fe5f12d0e1df9964798056e1f1c3de3b17b6bd2919b6356c4b86d8e763c01933efbe0224c86a96d5198a4be", + "0xb19ebeda61c23d255cbf472ef0b8a441f4c55b70f0d8ed47078c248b1d3c7c62e076b43b95c00a958ec8b16d5a7cb0d7", + "0xb02adc9aaa20e0368a989c2af14ff48b67233d28ebee44ff3418bb0473592e6b681af1cc45450bd4b175df9051df63d9", + "0x8d87f0714acee522eb58cec00360e762adc411901dba46adc9227124fa70ee679f9a47e91a6306d6030dd4eb8de2f3c1", + "0x8be54cec21e74bcc71de29dc621444263737db15f16d0bb13670f64e42f818154e04b484593d19ef95f2ee17e4b3fe21", + "0xab8e20546c1db38d31493b5d5f535758afb17e459645c1b70813b1cf7d242fd5d1f4354a7c929e8f7259f6a25302e351", + "0x89f035a1ed8a1e302ac893349ba8ddf967580fcb6e73d44af09e3929cde445e97ff60c87dafe489e2c0ab9c9986cfa00", + "0x8b2b0851a795c19191a692af55f7e72ad2474efdc5401bc3733cfdd910e34c918aaebe69d5ea951bdddf3c01cabbfc67", + "0xa4edb52c2b51495ccd1ee6450fc14b7b3ede8b3d106808929d02fb31475bacb403e112ba9c818d2857651e508b3a7dd1", + "0x9569341fded45d19f00bcf3cbf3f20eb2b4d82ef92aba3c8abd95866398438a2387437e580d8b646f17cf6fde8c5af23", + "0xaa4b671c6d20f72f2f18a939a6ff21cc37e0084b44b4a717f1be859a80b39fb1be026b3205adec2a66a608ec2bcd578f", + "0x94902e980de23c4de394ad8aec91b46f888d18f045753541492bfbb92c59d3daa8de37ae755a6853744af8472ba7b72b", + "0xaf651ef1b2a0d30a7884557edfad95b6b5d445a7561caebdc46a485aedd25932c62c0798465c340a76f6feaa196dd712", + "0xb7b669b8e5a763452128846dd46b530dca4893ace5cc5881c7ddcd3d45969d7e73fbebdb0e78aa81686e5f7b22ec5759", + "0x82507fd4ebe9fa656a7f2e084d64a1fa6777a2b0bc106d686e2d9d2edafc58997e58cb6bfd0453b2bf415704aa82ae62", + "0xb40bce2b42b88678400ecd52955bbdadd15f8b9e1b3751a1a3375dc0efb5ca3ee258cf201e1140b3c09ad41217d1d49e", + "0xb0210d0cbb3fbf3b8cdb39e862f036b0ff941cd838e7aaf3a8354e24246e64778d22f3de34572e6b2a580614fb6425be", + "0x876693cba4301b251523c7d034108831df3ce133d8be5a514e7a2ca494c268ca0556fa2ad8310a1d92a16b55bcd99ea9", + "0x8660281406d22a4950f5ef050bf71dd3090edb16eff27fa29ef600cdea628315e2054211ed2cc6eaf8f2a1771ef689fd", + "0xa610e7e41e41ab66955b809ba4ade0330b8e9057d8efc9144753caed81995edeb1a42a53f93ce93540feca1fae708dac", + "0xa49e2c176a350251daef1218efaccc07a1e06203386ede59c136699d25ca5cb2ac1b800c25b28dd05678f14e78e51891", + "0x83e0915aa2b09359604566080d411874af8c993beba97d4547782fdbe1a68e59324b800ff1f07b8db30c71adcbd102a8", + "0xa19e84e3541fb6498e9bb8a099c495cbfcad113330e0262a7e4c6544495bb8a754b2208d0c2d895c93463558013a5a32", + "0x87f2bd49859a364912023aca7b19a592c60214b8d6239e2be887ae80b69ebdeb59742bdebcfa73a586ab23b2c945586c", + "0xb8e8fdddae934a14b57bc274b8dcd0d45ebb95ddbaabef4454e0f6ce7d3a5a61c86181929546b3d60c447a15134d08e1", + "0x87e0c31dcb736ea4604727e92dc1d9a3cf00adcff79df3546e02108355260f3dd171531c3c0f57be78d8b28058fcc8c0", + "0x9617d74e8f808a4165a8ac2e30878c349e1c3d40972006f0787b31ea62d248c2d9f3fc3da83181c6e57e95feedfd0e8c", + "0x8949e2cee582a2f8db86e89785a6e46bc1565c2d8627d5b6bf43ba71ffadfab7e3c5710f88dcb5fb2fc6edf6f4fae216", + "0xad3fa7b0edceb83118972a2935a09f409d09a8db3869f30be3a76f67aa9fb379cabb3a3aff805ba023a331cad7d7eb64", + "0x8c95718a4112512c4efbd496be38bf3ca6cdcaad8a0d128f32a3f9aae57f3a57bdf295a3b372a8c549fda8f4707cffed", + "0x88f3261d1e28a58b2dee3fcc799777ad1c0eb68b3560f9b4410d134672d9533532a91ea7be28a041784872632d3c9d80", + "0xb47472a41d72dd2e8b72f5c4f8ad626737dde3717f63d6bc776639ab299e564cbad0a2ad5452a07f02ff49a359c437e5", + "0x9896d21dc2e8aad87b76d6df1654f10cd7bceed4884159d50a818bea391f8e473e01e14684814c7780235f28e69dca6e", + "0x82d47c332bbd31bbe83b5eb44a23da76d4a7a06c45d7f80f395035822bc27f62f59281d5174e6f8e77cc9b5c3193d6f0", + "0x95c74cd46206e7f70c9766117c34c0ec45c2b0f927a15ea167901a160e1530d8522943c29b61e03568aa0f9c55926c53", + "0xa89d7757825ae73a6e81829ff788ea7b3d7409857b378ebccd7df73fdbe62c8d9073741cf038314971b39af6c29c9030", + "0x8c1cd212d0b010905d560688cfc036ae6535bc334fa8b812519d810b7e7dcf1bb7c5f43deaa40f097158358987324a7f", + "0xb86993c383c015ed8d847c6b795164114dd3e9efd25143f509da318bfba89389ea72a420699e339423afd68b6512fafb", + "0x8d06bd379c6d87c6ed841d8c6e9d2d0de21653a073725ff74be1934301cc3a79b81ef6dd0aad4e7a9dc6eac9b73019bc", + "0x81af4d2d87219985b9b1202d724fe39ef988f14fef07dfe3c3b11714e90ffba2a97250838e8535eb63f107abfe645e96", + "0x8c5e0af6330a8becb787e4b502f34f528ef5756e298a77dc0c7467433454347f3a2e0bd2641fbc2a45b95e231c6e1c02", + "0x8e2a8f0f04562820dc8e7da681d5cad9fe2e85dd11c785fb6fba6786c57a857e0b3bd838fb849b0376c34ce1665e4837", + "0xa39be8269449bfdfc61b1f62077033649f18dae9bef7c6163b9314ca8923691fb832f42776f0160b9e8abd4d143aa4e1", + "0x8c154e665706355e1cc98e0a4cabf294ab019545ba9c4c399d666e6ec5c869ca9e1faf8fb06cd9c0a5c2f51a7d51b70a", + "0xa046a7d4de879d3ebd4284f08f24398e9e3bf006cd4e25b5c67273ade248689c69affff92ae810c07941e4904296a563", + "0xafd94c1cb48758e5917804df03fb38a6da0e48cd9b6262413ea13b26973f9e266690a1b7d9d24bbaf7e82718e0e594b0", + "0x859e21080310c8d6a38e12e2ac9f90a156578cdeb4bb2e324700e97d9a5511cd6045dc39d1d0de3f94aeed043a24119d", + "0xa219fb0303c379d0ab50893264919f598e753aac9065e1f23ef2949abc992577ab43c636a1d2c089203ec9ddb941e27d", + "0xb0fdb639d449588a2ca730afcba59334e7c387342d56defdfb7ef79c493f7fd0e5277eff18e7203e756c7bdda5803047", + "0x87f9c3b7ed01f54368aca6dbcf2f6e06bff96e183c4b2c65f8baa23b377988863a0a125d5cdd41a072da8462ced4c070", + "0x99ef7a5d5ac2f1c567160e1f8c95f2f38d41881850f30c461a205f7b1b9fb181277311333839b13fb3ae203447e17727", + "0xaeaca9b1c2afd24e443326cc68de67b4d9cedb22ad7b501a799d30d39c85bb2ea910d4672673e39e154d699e12d9b3dc", + "0xa11675a1721a4ba24dd3d0e4c3c33a6edf4cd1b9f6b471070b4386c61f77452266eae6e3f566a40cfc885eada9a29f23", + "0xb228334445e37b9b49cb4f2cc56b454575e92173ddb01370a553bba665adadd52df353ad74470d512561c2c3473c7bb9", + "0xa18177087c996572d76f81178d18ed1ceebc8362a396348ce289f1d8bd708b9e99539be6fccd4acb1112381cfc5749b4", + "0x8e7b8bf460f0d3c99abb19803b9e43422e91507a1c0c22b29ee8b2c52d1a384da4b87c292e28eff040db5be7b1f8641f", + "0xb03d038d813e29688b6e6f444eb56fec3abba64c3d6f890a6bcf2e916507091cdb2b9d2c7484617be6b26552ed1c56cb", + "0xa1c88ccd30e934adfc5494b72655f8afe1865a84196abfb376968f22ddc07761210b6a9fb7638f1413d1b4073d430290", + "0x961b714faebf172ad2dbc11902461e286e4f24a99a939152a53406117767682a571057044decbeb3d3feef81f4488497", + "0xa03dc4059b46effdd786a0a03cc17cfee8585683faa35bb07936ded3fa3f3a097f518c0b8e2db92fd700149db1937789", + "0xadf60180c99ca574191cbcc23e8d025b2f931f98ca7dfcebfc380226239b6329347100fcb8b0fcb12db108c6ad101c07", + "0x805d4f5ef24d46911cbf942f62cb84b0346e5e712284f82b0db223db26d51aabf43204755eb19519b00e665c7719fcaa", + "0x8dea7243e9c139662a7fe3526c6c601eee72fd8847c54c8e1f2ad93ef7f9e1826b170afe58817dac212427164a88e87f", + "0xa2ba42356606d651b077983de1ad643650997bb2babb188c9a3b27245bb65d2036e46667c37d4ce02cb1be5ae8547abe", + "0xaf2ae50b392bdc013db2d12ce2544883472d72424fc767d3f5cb0ca2d973fc7d1f425880101e61970e1a988d0670c81b", + "0x98e6bec0568d3939b31d00eb1040e9b8b2a35db46ddf4369bdaee41bbb63cc84423d29ee510a170fb5b0e2df434ba589", + "0x822ff3cd12fbef4f508f3ca813c04a2e0b9b799c99848e5ad3563265979e753ee61a48f6adc2984a850f1b46c1a43d35", + "0x891e8b8b92a394f36653d55725ef514bd2e2a46840a0a2975c76c2a935577f85289026aaa74384da0afe26775cbddfb9", + "0xb2a3131a5d2fe7c8967047aa66e4524babae941d90552171cc109527f345f42aa0df06dcbb2fa01b33d0043917bbed69", + "0x80c869469900431f3eeefafdbe07b8afd8cee7739e659e6d0109b397cacff85a88247698f87dc4e2fe39a592f250ac64", + "0x9091594f488b38f9d2bb5df49fd8b4f8829d9c2f11a197dd1431ed5abbc5c954bbde3387088f9ee3a5a834beb7619bce", + "0xb472e241e6956146cca57b97a8a204668d050423b4e76f857bad5b47f43b203a04c8391ba9d9c3e95093c071f9d376a1", + "0xb7dd2de0284844392f7dfb56fe7ca3ede41e27519753ffc579a0a8d2d65ceb8108d06b6b0d4c3c1a2588951297bd1a1e", + "0x902116ce70d0a079ac190321c1f48701318c05f8e69ee09694754885d33a835a849cafe56f499a2f49f6cda413ddf9a7", + "0xb18105cc736787fafaf7c3c11c448bce9466e683159dff52723b7951dff429565e466e4841d982e3aaa9ee2066838666", + "0x97ab9911f3f659691762d568ae0b7faa1047b0aed1009c319fa79d15d0db8db9f808fc385dc9a68fa388c10224985379", + "0xb2a2cba65f5b927e64d2904ba412e2bac1cf18c9c3eda9c72fb70262497ecf505b640827e2afebecf10eebbcf48ccd3e", + "0xb36a3fd677baa0d3ef0dac4f1548ff50a1730286b8c99d276a0a45d576e17b39b3cbadd2fe55e003796d370d4be43ce3", + "0xa5dfec96ca3c272566e89dc453a458909247e3895d3e44831528130bc47cc9d0a0dac78dd3cad680a4351d399d241967", + "0x8029382113909af6340959c3e61db27392531d62d90f92370a432aec3eb1e4c36ae1d4ef2ba8ec6edb4d7320c7a453f6", + "0x971d85121ea108e6769d54f9c51299b0381ece8b51d46d49c89f65bedc123bab4d5a8bc14d6f67f4f680077529cbae4c", + "0x98ff6afc01d0bec80a278f25912e1b1ebff80117adae72e31d5b9fa4d9624db4ba2065b444df49b489b0607c45e26c4c", + "0x8fa29be10fb3ab30ce25920fec0187e6e91e458947009dabb869aade7136c8ba23602682b71e390c251f3743164cbdaa", + "0xb3345c89eb1653418fe3940cf3e56a9a9c66526389b98f45ca02dd62bfb37baa69a4baaa7132d7320695f8ea6ad1fd94", + "0xb72c7f5541c9ac6b60a7ec9f5415e7fb14da03f7164ea529952a29399f3a071576608dbbcc0d45994f21f92ddbeb1e19", + "0xaa3450bb155a5f9043d0ef95f546a2e6ade167280bfb75c9f09c6f9cdb1fffb7ce8181436161a538433afa3681c7a141", + "0x92a18fecaded7854b349f441e7102b638ababa75b1b0281dd0bded6541abe7aa37d96693595be0b01fe0a2e2133d50f9", + "0x980756ddf9d2253cfe6c94960b516c94889d09e612810935150892627d2ecee9a2517e04968eea295d0106850c04ca44", + "0xae68c6ccc454318cdd92f32b11d89116a3b8350207a36d22a0f626718cad671d960090e054c0c77ac3162ae180ecfd4b", + "0x99f31f66eaaa551749ad91d48a0d4e3ff4d82ef0e8b28f3184c54e852422ba1bdafd53b1e753f3a070f3b55f3c23b6a2", + "0xa44eaeaa6589206069e9c0a45ff9fc51c68da38d4edff1d15529b7932e6f403d12b9387019c44a1488a5d5f27782a51f", + "0xb80b5d54d4b344840e45b79e621bd77a3f83fb4ce6d8796b7d6915107b3f3c34d2e7d95bdafd120f285669e5acf2437a", + "0xb36c069ec085a612b5908314d6b84c00a83031780261d1c77a0384c406867c9847d5b0845deddfa512cc04a8df2046fb", + "0xb09dbe501583220f640d201acea7ee3e39bf9eda8b91aa07b5c50b7641d86d71acb619b38d27835ce97c3759787f08e9", + "0x87403d46a2bf63170fff0b857acacf42ee801afe9ccba8e5b4aea967b68eac73a499a65ca46906c2eb4c8f27bc739faa", + "0x82b93669f42a0a2aa5e250ffe6097269da06a9c02fcd1801abbad415a7729a64f830754bafc702e64600ba47671c2208", + "0x8e3a3029be7edb8dd3ab1f8216664c8dc50d395f603736061d802cef77627db7b859ef287ed850382c13b4d22d6a2d80", + "0x968e9ec7194ff424409d182ce0259acd950c384c163c04463bc8700a40b79beba6146d22b7fa7016875a249b7b31c602", + "0x8b42c984bbe4996e0c20862059167c6bdc5164b1ffcd928f29512664459212d263e89f0f0e30eed4e672ffa5ed0b01b5", + "0x96bac54062110dada905363211133f1f15dc7e4fd80a4c6e4a83bc9a0bcbbaba11cd2c7a13debcf0985e1a954c1da66b", + "0xa16dc8a653d67a7cd7ae90b2fffac0bf1ca587005430fe5ba9403edd70ca33e38ba5661d2ed6e9d2864400d997626a62", + "0xa68ab11a570a27853c8d67e491591dcba746bfbee08a2e75ae0790399130d027ed387f41ef1d7de8df38b472df309161", + "0x92532b74886874447c0300d07eda9bbe4b41ed25349a3da2e072a93fe32c89d280f740d8ff70d5816793d7f2b97373cc", + "0x88e35711b471e89218fd5f4d0eadea8a29405af1cd81974427bc4a5fb26ed60798daaf94f726c96e779b403a2cd82820", + "0xb5c72aa4147c19f8c4f3a0a62d32315b0f4606e0a7025edc5445571eaf4daff64f4b7a585464821574dd50dbe1b49d08", + "0x9305d9b4095258e79744338683fd93f9e657367b3ab32d78080e51d54eec331edbc224fad5093ebf8ee4bd4286757eb8", + "0xb2a17abb3f6a05bcb14dc7b98321fa8b46d299626c73d7c6eb12140bf4c3f8e1795250870947af817834f033c88a59d6", + "0xb3477004837dbd8ba594e4296f960fc91ab3f13551458445e6c232eb04b326da803c4d93e2e8dcd268b4413305ff84da", + "0x924b4b2ebaafdcfdfedb2829a8bf46cd32e1407d8d725a5bd28bdc821f1bafb3614f030ea4352c671076a63494275a3f", + "0x8b81b9ef6125c82a9bece6fdcb9888a767ac16e70527753428cc87c56a1236e437da8be4f7ecfe57b9296dc3ae7ba807", + "0x906e19ec8b8edd58bdf9ae05610a86e4ea2282b1bbc1e8b00b7021d093194e0837d74cf27ac9916bdb8ec308b00da3da", + "0xb41c5185869071760ac786078a57a2ab4e2af60a890037ac0c0c28d6826f15c2cf028fddd42a9b6de632c3d550bfbc14", + "0xa646e5dec1b713ae9dfdf7bdc6cd474d5731a320403c7dfcfd666ffc9ae0cff4b5a79530e8df3f4aa9cb80568cb138e9", + "0xb0efad22827e562bd3c3e925acbd0d9425d19057868608d78c2209a531cccd0f2c43dc5673acf9822247428ffa2bb821", + "0xa94c19468d14b6f99002fc52ac06bbe59e5c472e4a0cdb225144a62f8870b3f10593749df7a2de0bd3c9476ce682e148", + "0x803864a91162f0273d49271dafaab632d93d494d1af935aefa522768af058fce52165018512e8d6774976d52bd797e22", + "0xa08711c2f7d45c68fb340ac23597332e1bcaec9198f72967b9921204b9d48a7843561ff318f87908c05a44fc35e3cc9d", + "0x91c3cad94a11a3197ae4f9461faab91a669e0dddb0371d3cab3ed9aeb1267badc797d8375181130e461eadd05099b2a2", + "0x81bdaaf48aae4f7b480fc13f1e7f4dd3023a41439ba231760409ce9292c11128ab2b0bdbbf28b98af4f97b3551f363af", + "0x8d60f9df9fd303f625af90e8272c4ecb95bb94e6efc5da17b8ab663ee3b3f673e9f6420d890ccc94acf4d2cae7a860d8", + "0xa7b75901520c06e9495ab983f70b61483504c7ff2a0980c51115d11e0744683ce022d76e3e09f4e99e698cbd21432a0d", + "0x82956072df0586562fda7e7738226f694e1c73518dd86e0799d2e820d7f79233667192c9236dcb27637e4c65ef19d493", + "0xa586beb9b6ffd06ad200957490803a7cd8c9bf76e782734e0f55e04a3dc38949de75dc607822ec405736c576cf83bca3", + "0xa179a30d00def9b34a7e85607a447eea0401e32ab5abeee1a281f2acd1cf6ec81a178020666f641d9492b1bdf66f05a3", + "0x83e129705c538787ed8e0fdc1275e6466a3f4ee21a1e6abedd239393b1df72244723b92f9d9d9339a0cab6ebf28f5a16", + "0x811bd8d1e3722b64cd2f5b431167e7f91456e8bba2cc669d3fbbce7d553e29c3c19f629fcedd2498bc26d33a24891d17", + "0xa243c030c858f1f60cccd26b45b024698cc6d9d9e6198c1ed4964a235d9f8d0baf9cde10c8e63dfaa47f8e74e51a6e85", + "0xab839eb82e23ca52663281f863b55b0a3d6d4425c33ffb4eeb1d7979488ab068bf99e2a60e82cea4dc42c56c26cbfebe", + "0x8b896f9bb21d49343e67aec6ad175b58c0c81a3ca73d44d113ae4354a0065d98eb1a5cafedaf232a2bb9cdc62152f309", + "0xaf6230340cc0b66f5bf845540ed4fc3e7d6077f361d60762e488d57834c3e7eb7eacc1b0ed73a7d134f174a01410e50c", + "0x88975e1b1af678d1b5179f72300a30900736af580dd748fd9461ef7afccc91ccd9bed33f9da55c8711a7635b800e831f", + "0xa97486bb9047391661718a54b8dd5a5e363964e495eae6c692730264478c927cf3e66dd3602413189a3699fbeae26e15", + "0xa5973c161ab38732885d1d2785fd74bf156ba34881980cba27fe239caef06b24a533ffe6dbbbeca5e6566682cc00300a", + "0xa24776e9a840afda0003fa73b415d5bd6ecd9b5c2cc842b643ee51b8c6087f4eead4d0bfbd987eb174c489a7b952ff2a", + "0xa8a6ee06e3af053b705a12b59777267c546f33ba8a0f49493af8e6df4e15cf8dd2d4fb4daf7e84c6b5d3a7363118ff03", + "0xa28e59ce6ad02c2ce725067c0123117e12ac5a52c8f5af13eec75f4a9efc4f696777db18a374fa33bcae82e0734ebd16", + "0x86dfc3b78e841c708aff677baa8ee654c808e5d257158715097c1025d46ece94993efe12c9d188252ad98a1e0e331fec", + "0xa88d0275510f242eab11fdb0410ff6e1b9d7a3cbd3658333539815f1b450a84816e6613d15aa8a8eb15d87cdad4b27a2", + "0x8440acea2931118a5b481268ff9f180ee4ede85d14a52c026adc882410825b8275caa44aff0b50c2b88d39f21b1a0696", + "0xa7c3182eab25bd6785bacf12079d0afb0a9b165d6ed327814e2177148539f249eb9b5b2554538f54f3c882d37c0a8abe", + "0x85291fbe10538d7da38efdd55a7acebf03b1848428a2f664c3ce55367aece60039f4f320b1771c9c89a35941797f717c", + "0xa2c6414eeb1234728ab0de94aa98fc06433a58efa646ca3fcbd97dbfb8d98ae59f7ce6d528f669c8149e1e13266f69c9", + "0x840c8462785591ee93aee2538d9f1ec44ba2ca61a569ab51d335ac873f5d48099ae8d7a7efa0725d9ff8f9475bfa4f56", + "0xa7065a9d02fb3673acf7702a488fbc01aa69580964932f6f40b6c2d1c386b19e50b0e104fcac24ea26c4e723611d0238", + "0xb72db6d141267438279e032c95e6106c2ccb3164b842ba857a2018f3a35f4b040da92680881eb17cd61d0920d5b8f006", + "0xa8005d6c5960e090374747307ef0be2871a7a43fa4e76a16c35d2baab808e9777b496e9f57a4218b23390887c33a0b55", + "0x8e152cea1e00a451ca47c20a1e8875873419700af15a5f38ee2268d3fbc974d4bd5f4be38008fa6f404dbdedd6e6e710", + "0xa3391aed1fcd68761f06a7d1008ec62a09b1cb3d0203cd04e300a0c91adfed1812d8bc1e4a3fd7976dc0aae0e99f52f1", + "0x967eb57bf2aa503ee0c6e67438098149eac305089c155f1762cf5e84e31f0fbf27c34a9af05621e34645c1ec96afaec8", + "0x88af97ddc4937a95ec0dcd25e4173127260f91c8db2f6eac84afb789b363705fb3196235af631c70cafd09411d233589", + "0xa32df75b3f2c921b8767638fd289bcfc61e08597170186637a7128ffedd52c798c434485ac2c7de07014f9e895c2c3d8", + "0xb0a783832153650aa0d766a3a73ec208b6ce5caeb40b87177ffc035ab03c7705ecdd1090b6456a29f5fb7e90e2fa8930", + "0xb59c8e803b4c3486777d15fc2311b97f9ded1602fa570c7b0200bada36a49ee9ef4d4c1474265af8e1c38a93eb66b18b", + "0x982f2c85f83e852022998ff91bafbb6ff093ef22cf9d5063e083a48b29175ccbd51b9c6557151409e439096300981a6c", + "0x939e3b5989fefebb9d272a954659a4eb125b98c9da6953f5e628d26266bd0525ec38304b8d56f08d65abc4d6da4a8dbb", + "0x8898212fe05bc8de7d18503cb84a1c1337cc2c09d1eeef2b475aa79185b7322bf1f8e065f1bf871c0c927dd19faf1f6d", + "0x94b0393a41cd00f724aee2d4bc72103d626a5aecb4b5486dd1ef8ac27528398edf56df9db5c3d238d8579af368afeb09", + "0x96ac564450d998e7445dd2ea8e3fc7974d575508fa19e1c60c308d83b645864c029f2f6b7396d4ff4c1b24e92e3bac37", + "0x8adf6638e18aff3eb3b47617da696eb6c4bdfbecbbc3c45d3d0ab0b12cbad00e462fdfbe0c35780d21aa973fc150285e", + "0xb53f94612f818571b5565bbb295e74bada9b5f9794b3b91125915e44d6ddcc4da25510eab718e251a09c99534d6042d9", + "0x8b96462508d77ee083c376cd90807aebad8de96bca43983c84a4a6f196d5faf6619a2351f43bfeec101864c3bf255519", + "0xaeadf34657083fc71df33bd44af73bf5281c9ca6d906b9c745536e1819ea90b56107c55e2178ebad08f3ba75b3f81c86", + "0x9784ba29b2f0057b5af1d3ab2796d439b8753f1f749c73e791037461bdfc3f7097394283105b8ab01788ea5255a96710", + "0x8756241bda159d4a33bf74faba0d4594d963c370fb6a18431f279b4a865b070b0547a6d1613cf45b8cfb5f9236bbf831", + "0xb03ebfd6b71421dfd49a30460f9f57063eebfe31b9ceaa2a05c37c61522b35bdc09d7db3ad75c76c253c00ba282d3cd2", + "0xb34e7e6341fa9d854b2d3153bdda0c4ae2b2f442ab7af6f99a0975d45725aa48e36ae5f7011edd249862e91f499687d4", + "0xb462ee09dc3963a14354244313e3444de5cc37ea5ccfbf14cd9aca8027b59c4cb2a949bc30474497cab8123e768460e6", + "0xaea753290e51e2f6a21a9a0ee67d3a2713f95c2a5c17fe41116c87d3aa77b1683761264d704df1ac34f8b873bc88ef7b", + "0x98430592afd414394f98ddfff9f280fcb1c322dbe3510f45e1e9c4bb8ee306b3e0cf0282c0ee73ebb8ba087d4d9e0858", + "0xb95d3b5aaf54ffca11f4be8d57f76e14afdb20afc859dc7c7471e0b42031e8f3d461b726ecb979bdb2f353498dfe95ea", + "0x984d17f9b11a683132e0b5a9ee5945e3ff7054c2d5c716be73b29078db1d36f54c6e652fd2f52a19da313112e97ade07", + "0xab232f756b3fff3262be418a1af61a7e0c95ceebbc775389622a8e10610508cd6784ab7960441917a83cc191c58829ea", + "0xa28f41678d6e60de76b0e36ab10e4516e53e02e9c77d2b5af3cfeee3ce94cfa30c5797bd1daab20c98e1cad83ad0f633", + "0xb55395fca84dd3ccc05dd480cb9b430bf8631ff06e24cb51d54519703d667268c2f8afcde4ba4ed16bece8cc7bc8c6e0", + "0x8a8a5392a0e2ea3c7a8c51328fab11156004e84a9c63483b64e8f8ebf18a58b6ffa8fe8b9d95af0a2f655f601d096396", + "0xab480000fe194d23f08a7a9ec1c392334e9c687e06851f083845121ce502c06b54dda8c43092bcc1035df45cc752fe9b", + "0xb265644c29f628d1c7e8e25a5e845cabb21799371814730a41a363e1bda8a7be50fee7c3996a365b7fcba4642add10db", + "0xb8a915a3c685c2d4728f6931c4d29487cad764c5ce23c25e64b1a3259ac27235e41b23bfe7ae982921b4cb84463097df", + "0x8efa7338442a4b6318145a5440fc213b97869647eeae41b9aa3c0a27ee51285b73e3ae3b4a9423df255e6add58864aa9", + "0x9106d65444f74d217f4187dfc8fcf3810b916d1e4275f94f6a86d1c4f3565b131fd6cde1fa708bc05fe183c49f14941a", + "0x948252dac8026bbbdb0a06b3c9d66ec4cf9532163bab68076fda1bd2357b69e4b514729c15aaa83b5618b1977bbc60c4", + "0xae6596ccfdf5cbbc5782efe3bb0b101bb132dbe1d568854ca24cacc0b2e0e9fabcb2ca7ab42aecec412efd15cf8cb7a2", + "0x84a0b6c198ff64fd7958dfd1b40eac9638e8e0b2c4cd8cf5d8cdf80419baee76a05184bce6c5b635f6bf2d30055476a7", + "0x8893118be4a055c2b3da593dbca51b1ae2ea2469911acfb27ee42faf3e6c3ad0693d3914c508c0b05b36a88c8b312b76", + "0xb097479e967504deb6734785db7e60d1d8034d6ca5ba9552887e937f5e17bb413fccac2c1d1082154ed76609127860ad", + "0xa0294e6b9958f244d29943debf24b00b538b3da1116269b6e452bb12dc742226712fd1a15b9c88195afeb5d2415f505c", + "0xb3cc15f635080bc038f61b615f62b5b5c6f2870586191f59476e8368a73641d6ac2f7d0c1f54621982defdb318020230", + "0x99856f49b9fe1604d917c94d09cc0ed753d13d015d30587a94e6631ffd964b214e607deb8a69a8b5e349a7edf4309206", + "0xa8571e113ea22b4b4fce41a094da8c70de37830ae32e62c65c2fa5ad06a9bc29e884b945e73d448c72b176d6ecebfb58", + "0xa9e9c6e52beb0013273c29844956b3ce291023678107cdc785f7b44eff5003462841ad8780761b86aefc6b734adde7cf", + "0x80a784b0b27edb51ef2bad3aee80e51778dcaa0f3f5d3dcb5dc5d4f4b2cf7ae35b08de6680ea9dac53f8438b92eb09ef", + "0x827b543e609ea328e97e373f70ad72d4915a2d1daae0c60d44ac637231070e164c43a2a58db80a64df1c624a042b38f9", + "0xb449c65e8195202efdcb9bdb4e869a437313b118fef8b510cbbf8b79a4e99376adb749b37e9c20b51b31ed3310169e27", + "0x8ea3028f4548a79a94c717e1ed28ad4d8725b8d6ab18b021063ce46f665c79da3c49440c6577319dab2d036b7e08f387", + "0x897798431cfb17fe39f08f5f854005dc37b1c1ec1edba6c24bc8acb3b88838d0534a75475325a5ea98b326ad47dbad75", + "0x89cf232e6303b0751561960fd4dea5754a28c594daf930326b4541274ffb03c7dd75938e411eb9a375006a70ce38097f", + "0x9727c6ae7f0840f0b6c8bfb3a1a5582ceee705e0b5c59b97def7a7a2283edd4d3f47b7971e902a3a2079e40b53ff69b8", + "0xb76ed72b122c48679d221072efc0eeea063cb205cbf5f9ef0101fd10cb1075b8628166c83577cced654e1c001c7882f7", + "0xae908c42d208759da5ee9b405df85a6532ea35c6f0f6a1288d22870f59d98edc896841b8ac890a538e6c8d1e8b02d359", + "0x809d12fe4039a0ec80dc9be6a89acaab7797e5f7f9b163378f52f9a75a1d73b2e9ae6e3dd49e32ced439783c1cabbef5", + "0xa4149530b7f85d1098ba534d69548c6c612c416e8d35992fc1f64f4deeb41e09e49c6cf7aadbed7e846b91299358fe2d", + "0xa49342eacd1ec1148b8df1e253b1c015f603c39de11fa0a364ccb86ea32d69c34fd7aa6980a1fadcd8e785a57fa46f60", + "0x87d43eff5a006dc4dddcf76cc96c656a1f3a68f19f124181feab86c6cc9a52cb9189cdbb423414defdd9bb0ca8ff1ddc", + "0x861367e87a9aa2f0f68296ba50aa5dbc5713008d260cc2c7e62d407c2063064749324c4e8156dc21b749656cfebce26b", + "0xb5303c2f72e84e170e66ae1b0fbd51b8c7a6f27476eaf5694b64e8737d5c84b51fe90100b256465a4c4156dd873cddb0", + "0xb62849a4f891415d74f434cdc1d23c4a69074487659ca96e1762466b2b7a5d8525b056b891d0feea6fe6845cba8bc7fb", + "0x923dd9e0d6590a9307e8c4c23f13bae3306b580e297a937711a8b13e8de85e41a61462f25b7d352b682e8437bf2b4ab3", + "0x9147379860cd713cd46c94b8cdf75125d36c37517fbecf81ace9680b98ce6291cd1c3e472f84249cc3b2b445e314b1b6", + "0xa808a4f17ac21e3fb5cfef404e61fae3693ca3e688d375f99b6116779696059a146c27b06de3ac36da349b0649befd56", + "0x87787e9322e1b75e66c1f0d9ea0915722a232770930c2d2a95e9478c4b950d15ab767e30cea128f9ed65893bfc2d0743", + "0x9036a6ee2577223be105defe1081c48ea7319e112fff9110eb9f61110c319da25a6cea0464ce65e858635b079691ef1f", + "0xaf5548c7c24e1088c23b57ee14d26c12a83484c9fd9296edf1012d8dcf88243f20039b43c8c548c265ef9a1ffe9c1c88", + "0xa0fff520045e14065965fb8accd17e878d3fcaf9e0af2962c8954e50be6683d31fa0bf4816ab68f08630dbac6bfce52a", + "0xb4c1b249e079f6ae1781af1d97a60b15855f49864c50496c09c91fe1946266915b799f0406084d7783f5b1039116dd8b", + "0x8b0ffa5e7c498cb3879dddca34743b41eee8e2dea3d4317a6e961b58adb699ef0c92400c068d5228881a2b08121226bf", + "0x852ae8b19a1d80aa8ae5382e7ee5c8e7670ceb16640871c56b20b96b66b3b60e00015a3dde039446972e57b49a999ddd", + "0xa49942f04234a7d8492169da232cfff8051df86e8e1ba3db46aede02422c689c87dc1d99699c25f96cb763f5ca0983e5", + "0xb04b597b7760cf5dcf411ef896d1661e6d5b0db3257ac2cf64b20b60c6cc18fa10523bb958a48d010b55bac7b02ab3b1", + "0xa494591b51ea8285daecc194b5e5bd45ae35767d0246ac94fae204d674ee180c8e97ff15f71f28b7aeb175b8aea59710", + "0x97d2624919e78406e7460730680dea8e71c8571cf988e11441aeea54512b95bd820e78562c99372d535d96f7e200d20d", + "0xac693ddb00e48f76e667243b9b6a7008424043fb779e4f2252330285232c3fccac4da25cbd6d95fe9ad959ff305a91f6", + "0x8d20ca0a71a64a3f702a0825bb46bd810d03bebfb227683680d474a52f965716ff99e19a165ebaf6567987f4f9ee3c94", + "0xa5c516a438f916d1d68ca76996404792e0a66e97b7f18fc54c917bf10cf3211b62387932756e39e67e47b0bd6e88385a", + "0xb089614d830abc0afa435034cec7f851f2f095d479cacf1a3fb57272da826c499a52e7dcbc0eb85f4166fb94778e18e9", + "0xa8dacc943765d930848288192f4c69e2461c4b9bc6e79e30eeef9a543318cf9ae9569d6986c65c5668a89d49993f8e07", + "0xab5a9361fa339eec8c621bdad0a58078983abd8942d4282b22835d7a3a47e132d42414b7c359694986f7db39386c2e19", + "0x94230517fb57bd8eb26c6f64129b8b2abd0282323bf7b94b8bac7fab27b4ecc2c4290c294275e1a759de19f2216134f3", + "0xb8f158ea5006bc3b90b285246625faaa6ac9b5f5030dc69701b12f3b79a53ec7e92eeb5a63bbd1f9509a0a3469ff3ffc", + "0x8b6944fd8cb8540957a91a142fdcda827762aa777a31e8810ca6d026e50370ee1636fc351724767e817ca38804ebe005", + "0x82d1ee40fe1569c29644f79fa6c4033b7ed45cd2c3b343881f6eb0de2e79548fded4787fae19bed6ee76ed76ff9f2f11", + "0xa8924c7035e99eaed244ca165607e7e568b6c8085510dcdbaf6ebdbed405af2e6c14ee27d94ffef10d30aa52a60bf66d", + "0x956f82a6c2ae044635e85812581e4866c5fa2f427b01942047d81f6d79a14192f66fbbe77c9ffeaef4e6147097fdd2b5", + "0xb1100255a1bcf5e05b6aff1dfeb6e1d55b5d68d43a7457ba10cc76b61885f67f4d0d5179abda786e037ae95deb8eea45", + "0x99510799025e3e5e8fbf06dedb14c060c6548ba2bda824f687d3999dc395e794b1fb6514b9013f3892b6cf65cb0d65aa", + "0x8f9091cebf5e9c809aab415942172258f894e66e625d7388a05289183f01b8d994d52e05a8e69f784fba41db9ea357f0", + "0xa13d2eeb0776bdee9820ecb6693536720232848c51936bb4ef4fe65588d3f920d08a21907e1fdb881c1ad70b3725e726", + "0xa68b8f18922d550284c5e5dc2dda771f24c21965a6a4d5e7a71678178f46df4d8a421497aad8fcb4c7e241aba26378a0", + "0x8b7601f0a3c6ad27f03f2d23e785c81c1460d60100f91ea9d1cab978aa03b523150206c6d52ce7c7769c71d2c8228e9e", + "0xa8e02926430813caa851bb2b46de7f0420f0a64eb5f6b805401c11c9091d3b6d67d841b5674fa2b1dce0867714124cd8", + "0xb7968ecba568b8193b3058400af02c183f0a6df995a744450b3f7e0af7a772454677c3857f99c140bbdb2a09e832e8e0", + "0x8f20b1e9ba87d0a3f35309b985f3c18d2e8800f1ca7f0c52cadef773f1496b6070c936eea48c4a1cae83fd2524e9d233", + "0x88aef260042db0d641a51f40639dbeeefa9e9811df30bee695f3791f88a2f84d318f04e8926b7f47bf25956cb9e3754f", + "0x9725345893b647e9ba4e6a29e12f96751f1ae25fcaec2173e9a259921a1a7edb7a47159b3c8767e44d9e2689f5aa0f72", + "0x8c281e6f72752cb11e239e4df9341c45106eb7993c160e54423c2bffe10bc39d42624b45a1f673936ef2e1a02fc92f1a", + "0x90aba2f68bddb2fcce6c51430dacdfeec43ea8dc379660c99095df11017691ccf5faa27665cf4b9f0eea7728ae53c327", + "0xb7022695c16521c5704f49b7ddbdbec9b5f57ce0ceebe537bc0ebb0906d8196cc855a9afeb8950a1710f6a654464d93f", + "0x8fe1b9dd3c6a258116415d36e08374e094b22f0afb104385a5da48be17123e86fb8327baacc4f0d9ebae923d55d99bb5", + "0x817e85d8e3d19a4cbc1dec31597142c2daa4871bda89c2177fa719c00eda3344eb08b82eb92d4aa91a9eaacb3fc09783", + "0xb59053e1081d2603f1ca0ba553804d6fa696e1fd996631db8f62087b26a40dfef02098b0326bb75f99ec83b9267ca738", + "0x990a173d857d3ba81ff3789b931bfc9f5609cde0169b7f055fa3cb56451748d593d62d46ba33f80f9cafffe02b68dd14", + "0xb0c538dbba4954b809ab26f9f94a3cf1dcb77ce289eaec1d19f556c0ae4be1fa03af4a9b7057837541c3cc0a80538736", + "0xac3ba42f5f44f9e1fc453ce49c4ab79d0e1d5c42d3b30b1e098f3ab3f414c4c262fa12fb2be249f52d4aaf3c5224beb9", + "0xaf47467eb152e59870e21f0d4da2f43e093daf40180ab01438030684b114d025326928eaab12c41b81a066d94fce8436", + "0x98d1b58ba22e7289b1c45c79a24624f19b1d89e00f778eef327ec4856a9a897278e6f1a9a7e673844b31dde949153000", + "0x97ccb15dfadc7c59dca08cfe0d22df2e52c684cf97de1d94bc00d7ba24e020025130b0a39c0f4d46e4fc872771ee7875", + "0xb699e4ed9a000ff96ca296b2f09dce278832bc8ac96851ff3cff99ed3f6f752cfc0fea8571be28cd9b5a7ec36f1a08ee", + "0xb9f49f0edb7941cc296435ff0a912e3ad16848ee8765ab5f60a050b280d6ea585e5b34051b15f6b8934ef01ceb85f648", + "0xac3893df7b4ceab23c6b9054e48e8ba40d6e5beda8fbe90b814f992f52494186969b35d8c4cdc3c99890a222c9c09008", + "0xa41293ad22fae81dea94467bc1488c3707f3d4765059173980be93995fa4fcc3c9340796e3eed0beeb0ba0d9bb4fa3aa", + "0xa0543e77acd2aeecde13d18d258aeb2c7397b77f17c35a1992e8666ea7abcd8a38ec6c2741bd929abba2f766138618cc", + "0x92e79b22bc40e69f6527c969500ca543899105837b6b1075fa1796755c723462059b3d1b028e0b3df2559fa440e09175", + "0xa1fa1eac8f41a5197a6fb4aa1eae1a031c89f9c13ff9448338b222780cf9022e0b0925d930c37501a0ef7b2b00fdaf83", + "0xb3cb29ff73229f0637335f28a08ad8c5f166066f27c6c175164d0f26766a927f843b987ee9b309ed71cbf0a65d483831", + "0x84d4ab787f0ac00f104f4a734dc693d62d48c2aeb03913153da62c2ae2c27d11b1110dcef8980368dd84682ea2c1a308", + "0xab6a8e4bbc78d4a7b291ad3e9a8fe2d65f640524ba3181123b09d2d18a9e300e2509ccf7000fe47e75b65f3e992a2e7e", + "0xb7805ebe4f1a4df414003dc10bca805f2ab86ca75820012653e8f9b79c405196b0e2cab099f2ab953d67f0d60d31a0f9", + "0xb12c582454148338ea605d22bd00a754109063e22617f1f8ac8ddf5502c22a181c50c216c3617b9852aa5f26af56b323", + "0x86333ad9f898947e31ce747728dc8c887479e18d36ff3013f69ebef807d82c6981543b5c3788af93c4d912ba084d3cba", + "0xb514efa310dc4ad1258add138891e540d8c87142a881b5f46563cc58ecd1488e6d3a2fca54c0b72a929f3364ca8c333e", + "0xaa0a30f92843cf2f484066a783a1d75a7aa6f41f00b421d4baf20a6ac7886c468d0eea7ca8b17dd22f4f74631b62b640", + "0xb3b7dc63baec9a752e8433c0cdee4d0f9bc41f66f2b8d132faf925eef9cf89aae756fc132c45910f057122462605dc10", + "0xb9b8190dac5bfdeb59fd44f4da41a57e7f1e7d2c21faba9da91fa45cbeca06dcf299c9ae22f0c89ece11ac46352d619f", + "0x89f8cf36501ad8bdfeab863752a9090e3bfda57cf8fdeca2944864dc05925f501e252c048221bcc57136ab09a64b64b2", + "0xb0cbfaf317f05f97be47fc9d69eda2dd82500e00d42612f271a1fe24626408c28881f171e855bd5bd67409f9847502b4", + "0xa7c21a8fcede581bfd9847b6835eda62ba250bea81f1bb17372c800a19c732abe03064e64a2f865d974fb636cab4b859", + "0x95f9df524ba7a4667351696c4176b505d8ea3659f5ff2701173064acc624af69a0fad4970963736383b979830cb32260", + "0x856a74fe8b37a2e3afeac858c8632200485d438422a16ae3b29f359e470e8244995c63ad79c7e007ed063f178d0306fd", + "0xb37faa4d78fdc0bb9d403674dbea0176c2014a171c7be8527b54f7d1a32a76883d3422a3e7a5f5fcc5e9b31b57822eeb", + "0x8d37234d8594ec3fe75670b5c9cc1ec3537564d4739b2682a75b18b08401869a4264c0f264354219d8d896cded715db4", + "0xb5289ee5737f0e0bde485d32096d23387d68dab8f01f47821ab4f06cc79a967afe7355e72dc0c751d96b2747b26f6255", + "0x9085e1fdf9f813e9c3b8232d3c8863cd84ab30d45e8e0d3d6a0abd9ebc6fd70cdf749ff4d04390000e14c7d8c6655fc7", + "0x93a388c83630331eca4da37ea4a97b3b453238af474817cc0a0727fd3138dcb4a22de38c04783ec829c22cb459cb4e8e", + "0xa5377116027c5d061dbe24c240b891c08cdd8cd3f0899e848d682c873aff5b8132c1e7cfe76d2e5ed97ee0eb1d42cb68", + "0xa274c84b04338ed28d74683e2a7519c2591a3ce37c294d6f6e678f7d628be2db8eff253ede21823e2df7183e6552f622", + "0x8bc201147a842453a50bec3ac97671397bc086d6dfc9377fa38c2124cdc286abda69b7324f47d64da094ae011d98d9d9", + "0x9842d0c066c524592b76fbec5132bc628e5e1d21c424bec4555efca8619cc1fd8ea3161febcb8b9e8ab54702f4e815e2", + "0xa19191b713a07efe85c266f839d14e25660ee74452e6c691cd9997d85ae4f732052d802d3deb018bdd847caa298a894b", + "0xa24f71fc0db504da4e287dd118a4a74301cbcd16033937ba2abc8417956fcb4ae19b8e63b931795544a978137eff51cb", + "0xa90eec4a6a3a4b8f9a5b93d978b5026fcf812fe65585b008d7e08c4aaf21195a1d0699f12fc16f79b6a18a369af45771", + "0x8b551cf89737d7d06d9b3b9c4c1c73b41f2ea0af4540999c70b82dabff8580797cf0a3caf34c86c59a7069eb2e38f087", + "0xb8d312e6c635e7a216a1cda075ae77ba3e1d2fd501dc31e83496e6e81ed5d9c7799f8e578869c2e0e256fb29f5de10a7", + "0x8d144bdb8cae0b2cdb5b33d44bbc96984a5925202506a8cc65eb67ac904b466f5a7fe3e1cbf04aa785bbb7348c4bb73c", + "0xa101b3d58b7a98659244b88de0b478b3fb87dc5fc6031f6e689b99edf498abd43e151fd32bd4bbd240e0b3e59c440359", + "0x907453abca7d8e7151a05cc3d506c988007692fe7401395dc93177d0d07d114ab6cca0cc658eb94c0223fe8658295cad", + "0x825329ffbe2147ddb68f63a0a67f32d7f309657b8e5d9ab5bb34b3730bfa2c77a23eaaadb05def7d9f94a9e08fdc1e96", + "0x88ee923c95c1dac99ae7ed6067906d734d793c5dc5d26339c1bb3314abe201c5dccb33b9007351885eb2754e9a8ea06c", + "0x98bc9798543f5f1adc9f2cfcfa72331989420e9c3f6598c45269f0dc9b7c8607bbeaf03faa0aea2ddde2b8f17fdceff5", + "0x8ee87877702a79aef923ab970db6fa81561b3c07d5bf1a072af0a7bad765b4cbaec910afe1a91703feacc7822fa38a94", + "0x8060b9584aa294fe8adc2b22f67e988bc6da768eae91e429dcc43ddc53cfcc5d6753fdc1b420b268c7eb2fb50736a970", + "0xb344a5524d80a2f051870c7001f74fcf348a70fcf78dbd20c6ff9ca85d81567d2318c8b8089f2c4f195d6aec9fc15fa6", + "0x8f5a5d893e1936ed062149d20eb73d98b62b7f50ab5d93a6429c03656b36688d1c80cb5010e4977491e51fa0d7dd35d5", + "0x86fa32ebbf97328c5f5f15564e1238297e289ec3219b9a741724e9f3ae8d5c15277008f555863a478b247ba5dc601d44", + "0x9557e55377e279f4b6b5e0ffe01eca037cc13aac242d67dfcd0374a1e775c5ed5cb30c25fe21143fee54e3302d34a3ea", + "0x8cb6bcbc39372d23464a416ea7039f57ba8413cf3f00d9a7a5b356ab20dcb8ed11b3561f7bce372b8534d2870c7ee270", + "0xb5d59075cb5abde5391f64b6c3b8b50adc6e1f654e2a580b6d6d6eff3f4fbdd8fffc92e06809c393f5c8eab37f774c4b", + "0xafcfb6903ef13e493a1f7308675582f15af0403b6553e8c37afb8b2808ad21b88b347dc139464367dc260df075fea1ad", + "0x810fbbe808375735dd22d5bc7fc3828dc49fdd22cc2d7661604e7ac9c4535c1df578780affb3b895a0831640a945bcad", + "0x8056b0c678803b416f924e09a6299a33cf9ad7da6fe1ad7accefe95c179e0077da36815fde3716711c394e2c5ea7127f", + "0x8b67403702d06979be19f1d6dc3ec73cc2e81254d6b7d0cc49cd4fdda8cd51ab0835c1d2d26fc0ecab5df90585c2f351", + "0x87f97f9e6d4be07e8db250e5dd2bffdf1390665bc5709f2b631a6fa69a7fca958f19bd7cc617183da1f50ee63e9352b5", + "0xae151310985940471e6803fcf37600d7fa98830613e381e00dab943aec32c14162d51c4598e8847148148000d6e5af5c", + "0x81eb537b35b7602c45441cfc61b27fa9a30d3998fad35a064e05bc9479e9f10b62eba2b234b348219eea3cadcaac64bb", + "0x8a441434934180ab6f5bc541f86ebd06eadbee01f438836d797e930fa803a51510e005c9248cecc231a775b74d12b5e9", + "0x81f3c250a27ba14d8496a5092b145629eb2c2e6a5298438670375363f57e2798207832c8027c3e9238ad94ecdadfc4df", + "0xa6217c311f2f3db02ceaa5b6096849fe92b6f4b6f1491535ef8525f6ccee6130bed2809e625073ecbaddd4a3eb3df186", + "0x82d1c396f0388b942cf22b119d7ef1ad03d3dad49a74d9d01649ee284f377c8daddd095d596871669e16160299a210db", + "0xa40ddf7043c5d72a7246bd727b07f7fff1549f0e443d611de6f9976c37448b21664c5089c57f20105102d935ab82f27b", + "0xb6c03c1c97adf0c4bf4447ec71366c6c1bff401ba46236cd4a33d39291e7a1f0bb34bd078ba3a18d15c98993b153a279", + "0x8a94f5f632068399c359c4b3a3653cb6df2b207379b3d0cdace51afdf70d6d5cce6b89a2b0fee66744eba86c98fb21c2", + "0xb2f19e78ee85073f680c3bba1f07fd31b057c00b97040357d97855b54a0b5accb0d3b05b2a294568fcd6a4be6f266950", + "0xa74632d13bbe2d64b51d7a9c3ae0a5a971c19f51cf7596a807cea053e6a0f3719700976d4e394b356c0329a2dced9aa2", + "0xafef616d341a9bc94393b8dfba68ff0581436aa3a3adb7c26a1bbf2cf19fa877066191681f71f17f3cd6f9cf6bf70b5a", + "0x8ce96d93ae217408acf7eb0f9cbb9563363e5c7002e19bbe1e80760bc9d449daee2118f3878b955163ed664516b97294", + "0x8414f79b496176bc8b8e25f8e4cfee28f4f1c2ddab099d63d2aca1b6403d26a571152fc3edb97794767a7c4686ad557c", + "0xb6c61d01fd8ce087ef9f079bf25bf10090db483dd4f88c4a786d31c1bdf52065651c1f5523f20c21e75cea17df69ab73", + "0xa5790fd629be70545093631efadddc136661f63b65ec682609c38ef7d3d7fa4e56bdf94f06e263bc055b90cb1c6bcefe", + "0xb515a767e95704fb7597bca9e46f1753abacdc0e56e867ee3c6f4cd382643c2a28e65312c05ad040eaa3a8cbe7217a65", + "0x8135806a02ead6aa92e9adb6fefb91349837ab73105aaa7be488ef966aa8dfaafdfa64bbae30fcbfa55dd135a036a863", + "0x8f22435702716d76b1369750694540742d909d5e72b54d0878245fab7c269953b1c6f2b29c66f08d5e0263ca3a731771", + "0x8e0f8a8e8753e077dac95848212aeffd51c23d9b6d611df8b102f654089401954413ecbedc6367561ca599512ae5dda7", + "0x815a9084e3e2345f24c5fa559deec21ee1352fb60f4025c0779be65057f2d528a3d91593bd30d3a185f5ec53a9950676", + "0x967e6555ccba395b2cc1605f8484c5112c7b263f41ce8439a99fd1c71c5ed14ad02684d6f636364199ca48afbbde13be", + "0x8cd0ccf17682950b34c796a41e2ea7dd5367aba5e80a907e01f4cdc611e4a411918215e5aebf4292f8b24765d73314a6", + "0xa58bf1bbb377e4b3915df6f058a0f53b8fb8130fdec8c391f6bc82065694d0be59bb67ffb540e6c42cc8b380c6e36359", + "0x92af3151d9e6bfb3383d85433e953c0160859f759b0988431ec5893542ba40288f65db43c78a904325ef8d324988f09d", + "0x8011bbb05705167afb47d4425065630f54cb86cd462095e83b81dfebf348f846e4d8fbcf1c13208f5de1931f81da40b9", + "0x81c743c104fc3cb047885c9fa0fb9705c3a83ee24f690f539f4985509c3dafd507af3f6a2128276f45d5939ef70c167f", + "0xa2c9679b151c041aaf5efeac5a737a8f70d1631d931609fca16be1905682f35e291292874cb3b03f14994f98573c6f44", + "0xa4949b86c4e5b1d5c82a337e5ce6b2718b1f7c215148c8bfb7e7c44ec86c5c9476048fc5c01f57cb0920876478c41ad6", + "0x86c2495088bd1772152e527a1da0ef473f924ea9ab0e5b8077df859c28078f73c4e22e3a906b507fdf217c3c80808b5c", + "0x892e0a910dcf162bcea379763c3e2349349e4cda9402949255ac4a78dd5a47e0bf42f5bd0913951576b1d206dc1e536a", + "0xa7009b2c6b396138afe4754b7cc10dee557c51c7f1a357a11486b3253818531f781ea8107360c8d4c3b1cd96282353c0", + "0x911763ef439c086065cc7b4e57484ed6d693ea44acee4b18c9fd998116da55fbe7dcb8d2a0f0f9b32132fca82d73dff6", + "0xa722000b95a4a2d40bed81870793f15ba2af633f9892df507f2842e52452e02b5ea8dea6a043c2b2611d82376e33742a", + "0x9387ac49477bd719c2f92240d0bdfcf9767aad247ca93dc51e56106463206bc343a8ec855eb803471629a66fffb565d6", + "0x92819a1fa48ab4902939bb72a0a4e6143c058ea42b42f9bc6cea5df45f49724e2530daf3fc4f097cceefa2a8b9db0076", + "0x98eac7b04537653bc0f4941aae732e4b1f84bd276c992c64a219b8715eb1fb829b5cbd997d57feb15c7694c468f95f70", + "0xb275e7ba848ce21bf7996e12dbeb8dadb5d0e4f1cb5a0248a4f8f9c9fe6c74e3c93f4b61edbcb0a51af5a141e1c14bc7", + "0x97243189285aba4d49c53770c242f2faf5fd3914451da4931472e3290164f7663c726cf86020f8f181e568c72fd172d1", + "0x839b0b3c25dd412bee3dc24653b873cc65454f8f16186bb707bcd58259c0b6765fa4c195403209179192a4455c95f3b8", + "0x8689d1a870514568a074a38232e2ceb4d7df30fabeb76cff0aed5b42bf7f02baea12c5fadf69f4713464dbd52aafa55f", + "0x8958ae7b290f0b00d17c3e9fdb4dbf168432b457c7676829299dd428984aba892de1966fc106cfc58a772862ecce3976", + "0xa422bc6bd68b8870cfa5bc4ce71781fd7f4368b564d7f1e0917f6013c8bbb5b240a257f89ecfdbecb40fe0f3aa31d310", + "0xaa61f78130cebe09bc9a2c0a37f0dd57ed2d702962e37d38b1df7f17dc554b1d4b7a39a44182a452ce4c5eb31fa4cfcc", + "0xb7918bd114f37869bf1a459023386825821bfadce545201929d13ac3256d92a431e34f690a55d944f77d0b652cefeffc", + "0x819bba35fb6ace1510920d4dcff30aa682a3c9af9022e287751a6a6649b00c5402f14b6309f0aeef8fce312a0402915e", + "0x8b7c9ad446c6f63c11e1c24e24014bd570862b65d53684e107ba9ad381e81a2eaa96731b4b33536efd55e0f055071274", + "0x8fe79b53f06d33386c0ec7d6d521183c13199498594a46d44a8a716932c3ec480c60be398650bbfa044fa791c4e99b65", + "0x9558e10fb81250b9844c99648cf38fa05ec1e65d0ccbb18aa17f2d1f503144baf59d802c25be8cc0879fff82ed5034ad", + "0xb538a7b97fbd702ba84645ca0a63725be1e2891c784b1d599e54e3480e4670d0025526674ef5cf2f87dddf2290ba09f0", + "0x92eafe2e869a3dd8519bbbceb630585c6eb21712b2f31e1b63067c0acb5f9bdbbcbdb612db4ea7f9cc4e7be83d31973f", + "0xb40d21390bb813ab7b70a010dff64c57178418c62685761784e37d327ba3cb9ef62df87ecb84277c325a637fe3709732", + "0xb349e6fbf778c4af35fbed33130bd8a7216ed3ba0a79163ebb556e8eb8e1a7dad3456ddd700dad9d08d202491c51b939", + "0xa8fdaedecb251f892b66c669e34137f2650509ade5d38fbe8a05d9b9184bb3b2d416186a3640429bd1f3e4b903c159dd", + "0xac6167ebfee1dbab338eff7642f5e785fc21ef0b4ddd6660333fe398068cbd6c42585f62e81e4edbb72161ce852a1a4f", + "0x874b1fbf2ebe140c683bd7e4e0ab017afa5d4ad38055aaa83ee6bbef77dbc88a6ce8eb0dcc48f0155244af6f86f34c2d", + "0x903c58e57ddd9c446afab8256a6bb6c911121e6ccfb4f9b4ed3e2ed922a0e500a5cb7fa379d5285bc16e11dac90d1fda", + "0x8dae7a0cffa2fd166859cd1bf10ff82dd1932e488af377366b7efc0d5dec85f85fe5e8150ff86a79a39cefc29631733a", + "0xaa047857a47cc4dfc08585f28640420fcf105b881fd59a6cf7890a36516af0644d143b73f3515ab48faaa621168f8c31", + "0x864508f7077c266cc0cb3f7f001cb6e27125ebfe79ab57a123a8195f2e27d3799ff98413e8483c533b46a816a3557f1f", + "0x8bcd45ab1f9cbab36937a27e724af819838f66dfeb15923f8113654ff877bd8667c54f6307aaf0c35027ca11b6229bfd", + "0xb21aa34da9ab0a48fcfdd291df224697ce0c1ebc0e9b022fdee8750a1a4b5ba421c419541ed5c98b461eecf363047471", + "0xa9a18a2ab2fae14542dc336269fe612e9c1af6cf0c9ac933679a2f2cb77d3c304114f4d219ca66fe288adde30716775b", + "0xb5205989b92c58bdda71817f9a897e84100b5c4e708de1fced5c286f7a6f01ae96b1c8d845f3a320d77c8e2703c0e8b1", + "0xa364059412bbcc17b8907d43ac8e5df90bc87fd1724b5f99832d0d24559fae6fa76a74cff1d1eac8cbac6ec80b44af20", + "0xae709f2c339886b31450834cf29a38b26eb3b0779bd77c9ac269a8a925d1d78ea3837876c654b61a8fe834b3b6940808", + "0x8802581bba66e1952ac4dab36af371f66778958f4612901d95e5cac17f59165e6064371d02de8fb6fccf89c6dc8bd118", + "0xa313252df653e29c672cbcfd2d4f775089cb77be1077381cf4dc9533790e88af6cedc8a119158e7da5bf6806ad9b91a1", + "0x992a065b4152c7ef11515cd54ba9d191fda44032a01aed954acff3443377ee16680c7248d530b746b8c6dee2d634e68c", + "0xb627b683ee2b32c1ab4ccd27b9f6cce2fe097d96386fa0e5c182ad997c4c422ab8dfc03870cd830b8c774feb66537282", + "0xb823cf8a9aee03dadd013eb9efe40a201b4b57ef67efaae9f99683005f5d1bf55e950bf4af0774f50859d743642d3fea", + "0xb8a7449ffac0a3f206677097baf7ce00ca07a4d2bd9b5356fbcb83f3649b0fda07cfebad220c1066afba89e5a52abf4b", + "0xb2dd1a2f986395bb4e3e960fbbe823dbb154f823284ebc9068502c19a7609790ec0073d08bfa63f71e30c7161b6ef966", + "0x98e5236de4281245234f5d40a25b503505af140b503a035fc25a26159a9074ec81512b28f324c56ea2c9a5aa7ce90805", + "0x89070847dc8bbf5bc4ed073aa2e2a1f699cf0c2ca226f185a0671cecc54e7d3e14cd475c7752314a7a8e7476829da4bc", + "0xa9402dc9117fdb39c4734c0688254f23aed3dce94f5f53f5b7ef2b4bf1b71a67f85ab1a38ec224a59691f3bee050aeb3", + "0x957288f9866a4bf56a4204218ccc583f717d7ce45c01ea27142a7e245ad04a07f289cc044f8cf1f21d35e67e39299e9c", + "0xb2fb31ccb4e69113763d7247d0fc8edaae69b550c5c56aecacfd780c7217dc672f9fb7496edf4aba65dacf3361268e5b", + "0xb44a4526b2f1d6eb2aa8dba23bfa385ff7634572ab2afddd0546c3beb630fbfe85a32f42dd287a7fec069041411537f7", + "0x8db5a6660c3ac7fd7a093573940f068ee79a82bc17312af900b51c8c439336bc86ca646c6b7ab13aaaa008a24ca508ab", + "0x8f9899a6d7e8eb4367beb5c060a1f8e94d8a21099033ae582118477265155ba9e72176a67f7f25d7bad75a152b56e21a", + "0xa67de0e91ade8d69a0e00c9ff33ee2909b8a609357095fa12319e6158570c232e5b6f4647522efb7345ce0052aa9d489", + "0x82eb2414898e9c3023d57907a2b17de8e7eea5269029d05a94bfd7bf5685ac4a799110fbb375eb5e0e2bd16acf6458ae", + "0x94451fc7fea3c5a89ba701004a9693bab555cb622caf0896b678faba040409fdfd14a978979038b2a81e8f0abc4994d2", + "0xac879a5bb433998e289809a4a966bd02b4bf6a9c1cc276454e39c886efcf4fc68baebed575826bde577ab5aa71d735a9", + "0x880c0f8f49c875dfd62b4ddedde0f5c8b19f5687e693717f7e5c031bc580e58e13ab497d48b4874130a18743c59fdce3", + "0xb582af8d8ff0bf76f0a3934775e0b54c0e8fed893245d7d89cae65b03c8125b7237edc29dc45b4fe1a3fe6db45d280ee", + "0x89f337882ed3ae060aaee98efa20d79b6822bde9708c1c5fcee365d0ec9297f694cae37d38fd8e3d49717c1e86f078e7", + "0x826d2c1faea54061848b484e288a5f4de0d221258178cf87f72e14baaa4acc21322f8c9eab5dde612ef497f2d2e1d60b", + "0xa5333d4f227543e9cd741ccf3b81db79f2f03ca9e649e40d6a6e8ff9073e06da83683566d3b3c8d7b258c62970fb24d1", + "0xa28f08c473db06aaf4c043a2fae82b3c8cfaa160bce793a4c208e4e168fb1c65115ff8139dea06453c5963d95e922b94", + "0x8162546135cc5e124e9683bdfaa45833c18553ff06a0861c887dc84a5b12ae8cd4697f6794c7ef6230492c32faba7014", + "0xb23f0d05b74c08d6a7df1760792be83a761b36e3f8ae360f3c363fb196e2a9dd2de2e492e49d36561366e14daa77155c", + "0xb6f70d6c546722d3907c708d630dbe289771d2c8bf059c2e32b77f224696d750b4dda9b3a014debda38e7d02c9a77585", + "0x83bf4c4a9f3ca022c631017e7a30ea205ba97f7f5927cba8fc8489a4646eac6712cb821c5668c9ffe94d69d524374a27", + "0xb0371475425a8076d0dd5f733f55aabbe42d20a7c8ea7da352e736d4d35a327b2beb370dfcb05284e22cfd69c5f6c4cc", + "0xa0031ba7522c79211416c2cca3aa5450f96f8fee711552a30889910970ba13608646538781a2c08b834b140aadd7166f", + "0x99d273c80c7f2dc6045d4ed355d9fc6f74e93549d961f4a3b73cd38683f905934d359058cd1fc4da8083c7d75070487f", + "0xb0e4b0efa3237793e9dcce86d75aafe9879c5fa23f0d628649aef2130454dcf72578f9bf227b9d2b9e05617468e82588", + "0xa5ab076fa2e1c5c51f3ae101afdd596ad9d106bba7882b359c43d8548b64f528af19afa76cd6f40da1e6c5fca4def3fa", + "0x8ce2299e570331d60f6a6eff1b271097cd5f1c0e1113fc69b89c6a0f685dabea3e5bc2ac6bd789aa492ab189f89be494", + "0x91b829068874d911a310a5f9dee001021f97471307b5a3de9ec336870ec597413e1d92010ce320b619f38bed7c4f7910", + "0xb14fe91f4b07bf33b046e9285b66cb07927f3a8da0af548ac2569b4c4fb1309d3ced76d733051a20814e90dd5b75ffd1", + "0xabaab92ea6152d40f82940277c725aa768a631ee0b37f5961667f82fb990fc11e6d3a6a2752b0c6f94563ed9bb28265c", + "0xb7fe28543eca2a716859a76ab9092f135337e28109544f6bd2727728d0a7650428af5713171ea60bfc273d1c821d992c", + "0x8a4917b2ab749fc7343fc64bdf51b6c0698ff15d740cc7baf248c030475c097097d5a473bcc00d8c25817563fe0447b4", + "0xaa96156d1379553256350a0a3250166add75948fb9cde62aa555a0a9dc0a9cb7f2f7b8428aff66097bf6bfedaf14bbe2", + "0xae4ffeb9bdc76830d3eca2b705f30c1bdede6412fa064260a21562c8850c7fb611ec62bc68479fe48f692833e6f66d8d", + "0xb96543caaba9d051600a14997765d49e4ab10b07c7a92cccf0c90b309e6da334fdd6d18c96806cbb67a7801024fbd3c7", + "0x97b2b9ad76f19f500fcc94ca8e434176249f542ac66e5881a3dccd07354bdab6a2157018b19f8459437a68d8b86ba8e0", + "0xa8d206f6c5a14c80005849474fde44b1e7bcf0b2d52068f5f97504c3c035b09e65e56d1cf4b5322791ae2c2fdbd61859", + "0x936bad397ad577a70cf99bf9056584a61bd7f02d2d5a6cf219c05d770ae30a5cd902ba38366ce636067fc1dd10108d31", + "0xa77e30195ee402b84f3882e2286bf5380c0ed374a112dbd11e16cef6b6b61ab209d4635e6f35cdaaa72c1a1981d5dabe", + "0xa46ba4d3947188590a43c180757886a453a0503f79cc435322d92490446f37419c7b999fdf868a023601078070e03346", + "0x80d8d4c5542f223d48240b445d4d8cf6a75d120b060bc08c45e99a13028b809d910b534d2ac47fb7068930c54efd8da9", + "0x803be9c68c91b42b68e1f55e58917a477a9a6265e679ca44ee30d3eb92453f8c89c64eafc04c970d6831edd33d066902", + "0xb14b2b3d0dfe2bb57cee4cd72765b60ac33c1056580950be005790176543826c1d4fbd737f6cfeada6c735543244ab57", + "0xa9e480188bba1b8fb7105ff12215706665fd35bf1117bacfb6ab6985f4dbc181229873b82e5e18323c2b8f5de03258e0", + "0xa66a0f0779436a9a3999996d1e6d3000f22c2cac8e0b29cddef9636393c7f1457fb188a293b6c875b05d68d138a7cc4a", + "0x848397366300ab40c52d0dbbdafbafef6cd3dadf1503bb14b430f52bb9724188928ac26f6292a2412bc7d7aa620763c8", + "0x95466cc1a78c9f33a9aaa3829a4c8a690af074916b56f43ae46a67a12bb537a5ac6dbe61590344a25b44e8512355a4a7", + "0x8b5f7a959f818e3baf0887f140f4575cac093d0aece27e23b823cf421f34d6e4ff4bb8384426e33e8ec7b5eed51f6b5c", + "0x8d5e1368ec7e3c65640d216bcc5d076f3d9845924c734a34f3558ac0f16e40597c1a775a25bf38b187213fbdba17c93b", + "0xb4647c1b823516880f60d20c5cc38c7f80b363c19d191e8992226799718ee26b522a12ecb66556ed3d483aa4824f3326", + "0xac3abaea9cd283eb347efda4ed9086ea3acf495043e08d0d19945876329e8675224b685612a6badf8fd72fb6274902b1", + "0x8eae1ce292d317aaa71bcf6e77e654914edd5090e2e1ebab78b18bb41b9b1bc2e697439f54a44c0c8aa0d436ebe6e1a9", + "0x94dc7d1aec2c28eb43d93b111fa59aaa0d77d5a09501220bd411768c3e52208806abf973c6a452fd8292ff6490e0c9e2", + "0x8fd8967f8e506fef27d17b435d6b86b232ec71c1036351f12e6fb8a2e12daf01d0ee04451fb944d0f1bf7fd20e714d02", + "0x824e6865be55d43032f0fec65b3480ea89b0a2bf860872237a19a54bc186a85d2f8f9989cc837fbb325b7c72d9babe2c", + "0x8bd361f5adb27fd6f4e3f5de866e2befda6a8454efeb704aacc606f528c03f0faae888f60310e49440496abd84083ce2", + "0xb098a3c49f2aaa28b6b3e85bc40ce6a9cdd02134ee522ae73771e667ad7629c8d82c393fba9f27f5416986af4c261438", + "0xb385f5ca285ff2cfe64dcaa32dcde869c28996ed091542600a0b46f65f3f5a38428cca46029ede72b6cf43e12279e3d3", + "0x8196b03d011e5be5288196ef7d47137d6f9237a635ab913acdf9c595fa521d9e2df722090ec7eb0203544ee88178fc5f", + "0x8ed1270211ef928db18e502271b7edf24d0bbd11d97f2786aee772d70c2029e28095cf8f650b0328cc8a4c38d045316d", + "0xa52ab60e28d69b333d597a445884d44fd2a7e1923dd60f763951e1e45f83e27a4dac745f3b9eff75977b3280e132c15d", + "0x91e9fe78cdac578f4a4687f71b800b35da54b824b1886dafec073a3c977ce7a25038a2f3a5b1e35c2c8c9d1a7312417c", + "0xa42832173f9d9491c7bd93b21497fbfa4121687cd4d2ab572e80753d7edcbb42cfa49f460026fbde52f420786751a138", + "0x97b947126d84dcc70c97be3c04b3de3f239b1c4914342fa643b1a4bb8c4fe45c0fcb585700d13a7ed50784790c54bef9", + "0x860e407d353eac070e2418ef6cb80b96fc5f6661d6333e634f6f306779651588037be4c2419562c89c61f9aa2c4947f5", + "0xb2c9d93c3ba4e511b0560b55d3501bf28a510745fd666b3cb532db051e6a8617841ea2f071dda6c9f15619c7bfd2737f", + "0x8596f4d239aeeac78311207904d1bd863ef68e769629cc379db60e019aaf05a9d5cd31dc8e630b31e106a3a93e47cbc5", + "0x8b26e14e2e136b65c5e9e5c2022cee8c255834ea427552f780a6ca130a6446102f2a6f334c3f9a0308c53df09e3dba7e", + "0xb54724354eb515a3c8bed0d0677ff1db94ac0a07043459b4358cb90e3e1aa38ac23f2caa3072cf9647275d7cd61d0e80", + "0xb7ce9fe0e515e7a6b2d7ddcb92bc0196416ff04199326aea57996eef8c5b1548bd8569012210da317f7c0074691d01b7", + "0xa1a13549c82c877253ddefa36a29ea6a23695ee401fdd48e65f6f61e5ebd956d5e0edeff99484e9075cb35071fec41e2", + "0x838ba0c1e5bd1a6da05611ff1822b8622457ebd019cb065ece36a2d176bd2d889511328120b8a357e44569e7f640c1e6", + "0xb916eccff2a95519400bbf76b5f576cbe53cf200410370a19d77734dc04c05b585cfe382e8864e67142d548cd3c4c2f4", + "0xa610447cb7ca6eea53a6ff1f5fe562377dcb7f4aaa7300f755a4f5e8eba61e863c51dc2aa9a29b35525b550fbc32a0fe", + "0x9620e8f0f0ee9a4719aa9685eeb1049c5c77659ba6149ec4c158f999cfd09514794b23388879931fe26fea03fa471fd3", + "0xa9dcf8b679e276583cf5b9360702a185470d09aea463dc474ee9c8aee91ef089dacb073e334e47fbc78ec5417c90465c", + "0x8c9adee8410bdd99e5b285744cee61e2593b6300ff31a8a83b0ec28da59475a5c6fb9346fe43aadea2e6c3dad2a8e30a", + "0x97d5afe9b3897d7b8bb628b7220cf02d8ee4e9d0b78f5000d500aaf4c1df9251aaaabfd1601626519f9d66f00a821d4e", + "0x8a382418157b601ce4c3501d3b8409ca98136a4ef6abcbf62885e16e215b76b035c94d149cc41ff92e42ccd7c43b9b3d", + "0xb64b8d11fb3b01abb2646ac99fdb9c02b804ce15d98f9fe0fbf1c9df8440c71417487feb6cdf51e3e81d37104b19e012", + "0x849d7d044f9d8f0aab346a9374f0b3a5d14a9d1faa83dbacccbdc629ad1ef903a990940255564770537f8567521d17f0", + "0x829dbb0c76b996c2a91b4cbbe93ba455ca0d5729755e5f0c92aaee37dff7f36fcdc06f33aca41f1b609c784127b67d88", + "0x85a7c0069047b978422d264d831ab816435f63938015d2e977222b6b5746066c0071b7f89267027f8a975206ed25c1b0", + "0x84b9fbc1cfb302df1acdcf3dc5d66fd1edfe7839f7a3b2fb3a0d5548656249dd556104d7c32b73967bccf0f5bdcf9e3b", + "0x972220ac5b807f53eac37dccfc2ad355d8b21ea6a9c9b011c09fe440ddcdf7513e0b43d7692c09ded80d7040e26aa28f", + "0x855885ed0b21350baeca890811f344c553cf9c21024649c722453138ba29193c6b02c4b4994cd414035486f923472e28", + "0x841874783ae6d9d0e59daea03e96a01cbbe4ecaced91ae4f2c8386e0d87b3128e6d893c98d17c59e4de1098e1ad519dd", + "0x827e50fc9ce56f97a4c3f2f4cbaf0b22f1c3ce6f844ff0ef93a9c57a09b8bf91ebfbd2ba9c7f83c442920bffdaf288cc", + "0xa441f9136c7aa4c08d5b3534921b730e41ee91ab506313e1ba5f7c6f19fd2d2e1594e88c219834e92e6fb95356385aa7", + "0x97d75b144471bf580099dd6842b823ec0e6c1fb86dd0da0db195e65524129ea8b6fd4a7a9bbf37146269e938a6956596", + "0xa4b6fa87f09d5a29252efb2b3aaab6b3b6ea9fab343132a651630206254a25378e3e9d6c96c3d14c150d01817d375a8e", + "0xa31a671876d5d1e95fe2b8858dc69967231190880529d57d3cab7f9f4a2b9b458ac9ee5bdaa3289158141bf18f559efb", + "0x90bee6fff4338ba825974021b3b2a84e36d617e53857321f13d2b3d4a28954e6de3b3c0e629d61823d18a9763313b3bf", + "0x96b622a63153f393bb419bfcf88272ea8b3560dbd46b0aa07ada3a6223990d0abdd6c2adb356ef4be5641688c8d83941", + "0x84c202adeaff9293698022bc0381adba2cd959f9a35a4e8472288fd68f96f6de8be9da314c526d88e291c96b1f3d6db9", + "0x8ca01a143b8d13809e5a8024d03e6bc9492e22226073ef6e327edf1328ef4aff82d0bcccee92cb8e212831fa35fe1204", + "0xb2f970dbad15bfbefb38903c9bcc043d1367055c55dc1100a850f5eb816a4252c8c194b3132c929105511e14ea10a67d", + "0xa5e36556472a95ad57eb90c3b6623671b03eafd842238f01a081997ffc6e2401f76e781d049bb4aa94d899313577a9cf", + "0x8d1057071051772f7c8bedce53a862af6fd530dd56ae6321eaf2b9fc6a68beff5ed745e1c429ad09d5a118650bfd420a", + "0x8aadc4f70ace4fcb8d93a78610779748dcffc36182d45b932c226dc90e48238ea5daa91f137c65ed532352c4c4d57416", + "0xa2ea05ae37e673b4343232ae685ee14e6b88b867aef6dfac35db3589cbcd76f99540fed5c2641d5bb5a4a9f808e9bf0d", + "0x947f1abad982d65648ae4978e094332b4ecb90f482c9be5741d5d1cf5a28acf4680f1977bf6e49dd2174c37f11e01296", + "0xa27b144f1565e4047ba0e3f4840ef19b5095d1e281eaa463c5358f932114cbd018aa6dcf97546465cf2946d014d8e6d6", + "0x8574e1fc3acade47cd4539df578ce9205e745e161b91e59e4d088711a7ab5aa3b410d517d7304b92109924d9e2af8895", + "0xa48ee6b86b88015d6f0d282c1ae01d2a5b9e8c7aa3d0c18b35943dceb1af580d08a65f54dc6903cde82fd0d73ce94722", + "0x8875650cec543a7bf02ea4f2848a61d167a66c91ffaefe31a9e38dc8511c6a25bde431007eefe27a62af3655aca208dc", + "0x999b0a6e040372e61937bf0d68374e230346b654b5a0f591a59d33a4f95bdb2f3581db7c7ccb420cd7699ed709c50713", + "0x878c9e56c7100c5e47bbe77dc8da5c5fe706cec94d37fa729633bca63cace7c40102eee780fcdabb655f5fa47a99600e", + "0x865006fb5b475ada5e935f27b96f9425fc2d5449a3c106aa366e55ebed3b4ee42adc3c3f0ac19fd129b40bc7d6bc4f63", + "0xb7a7da847f1202e7bc1672553e68904715e84fd897d529243e3ecda59faa4e17ba99c649a802d53f6b8dfdd51f01fb74", + "0x8b2fb4432c05653303d8c8436473682933a5cb604da10c118ecfcd2c8a0e3132e125afef562bdbcc3df936164e5ce4f2", + "0x808d95762d33ddfa5d0ee3d7d9f327de21a994d681a5f372e2e3632963ea974da7f1f9e5bac8ccce24293509d1f54d27", + "0x932946532e3c397990a1df0e94c90e1e45133e347a39b6714c695be21aeb2d309504cb6b1dde7228ff6f6353f73e1ca2", + "0x9705e7c93f0cdfaa3fa96821f830fe53402ad0806036cd1b48adc2f022d8e781c1fbdab60215ce85c653203d98426da3", + "0xaa180819531c3ec1feb829d789cb2092964c069974ae4faad60e04a6afcce5c3a59aec9f11291e6d110a788d22532bc6", + "0x88f755097f7e25cb7dd3c449520c89b83ae9e119778efabb54fbd5c5714b6f37c5f9e0346c58c6ab09c1aef2483f895d", + "0x99fc03ab7810e94104c494f7e40b900f475fde65bdec853e60807ffd3f531d74de43335c3b2646b5b8c26804a7448898", + "0xaf2dea9683086bed1a179110efb227c9c00e76cd00a2015b089ccbcee46d1134aa18bda5d6cab6f82ae4c5cd2461ac21", + "0xa500f87ba9744787fdbb8e750702a3fd229de6b8817594348dec9a723b3c4240ddfa066262d002844b9e38240ce55658", + "0x924d0e45c780f5bc1c1f35d15dfc3da28036bdb59e4c5440606750ecc991b85be18bc9a240b6c983bc5430baa4c68287", + "0x865b11e0157b8bf4c5f336024b016a0162fc093069d44ac494723f56648bc4ded13dfb3896e924959ea11c96321afefc", + "0x93672d8607d4143a8f7894f1dcca83fb84906dc8d6dd7dd063bb0049cfc20c1efd933e06ca7bd03ea4cb5a5037990bfe", + "0x826891efbdff0360446825a61cd1fa04326dd90dae8c33dfb1ed97b045e165766dd070bd7105560994d0b2044bdea418", + "0x93c4a4a8bcbc8b190485cc3bc04175b7c0ed002c28c98a540919effd6ed908e540e6594f6db95cd65823017258fb3b1c", + "0xaeb2a0af2d2239fda9aa6b8234b019708e8f792834ff0dd9c487fa09d29800ddceddd6d7929faa9a3edcb9e1b3aa0d6b", + "0x87f11de7236d387863ec660d2b04db9ac08143a9a2c4dfff87727c95b4b1477e3bc473a91e5797313c58754905079643", + "0x80dc1db20067a844fe8baceca77f80db171a5ca967acb24e2d480eae9ceb91a3343c31ad1c95b721f390829084f0eae6", + "0x9825c31f1c18da0de3fa84399c8b40f8002c3cae211fb6a0623c76b097b4d39f5c50058f57a16362f7a575909d0a44a2", + "0xa99fc8de0c38dbf7b9e946de83943a6b46a762167bafe2a603fb9b86f094da30d6de7ed55d639aafc91936923ee414b3", + "0xad594678b407db5d6ea2e90528121f84f2b96a4113a252a30d359a721429857c204c1c1c4ff71d8bb5768c833f82e80e", + "0xb33d985e847b54510b9b007e31053732c8a495e43be158bd2ffcea25c6765bcbc7ca815f7c60b36ad088b955dd6e9350", + "0x815f8dfc6f90b3342ca3fbd968c67f324dae8f74245cbf8bc3bef10e9440c65d3a2151f951e8d18959ba01c1b50b0ec1", + "0x94c608a362dd732a1abc56e338637c900d59013db8668e49398b3c7a0cae3f7e2f1d1bf94c0299eeafe6af7f76c88618", + "0x8ebd8446b23e5adfcc393adc5c52fe172f030a73e63cd2d515245ca0dd02782ceed5bcdd9ccd9c1b4c5953dfac9c340c", + "0x820437f3f6f9ad0f5d7502815b221b83755eb8dc56cd92c29e9535eb0b48fb8d08c9e4fcc26945f9c8cca60d89c44710", + "0x8910e4e8a56bf4be9cc3bbf0bf6b1182a2f48837a2ed3c2aaec7099bfd7f0c83e14e608876b17893a98021ff4ab2f20d", + "0x9633918fde348573eec15ce0ad53ac7e1823aac86429710a376ad661002ae6d049ded879383faaa139435122f64047c6", + "0xa1f5e3fa558a9e89318ca87978492f0fb4f6e54a9735c1b8d2ecfb1d1c57194ded6e0dd82d077b2d54251f3bee1279e1", + "0xb208e22d04896abfd515a95c429ff318e87ff81a5d534c8ac2c33c052d6ffb73ef1dccd39c0bbe0734b596c384014766", + "0x986d5d7d2b5bde6d16336f378bd13d0e671ad23a8ec8a10b3fc09036faeeb069f60662138d7a6df3dfb8e0d36180f770", + "0xa2d4e6c5f5569e9cef1cddb569515d4b6ace38c8aed594f06da7434ba6b24477392cc67ba867c2b079545ca0c625c457", + "0xb5ac32b1d231957d91c8b7fc43115ce3c5c0d8c13ca633374402fa8000b6d9fb19499f9181844f0c10b47357f3f757ce", + "0x96b8bf2504b4d28fa34a4ec378e0e0b684890c5f44b7a6bb6e19d7b3db2ab27b1e2686389d1de9fbd981962833a313ea", + "0x953bfd7f6c3a0469ad432072b9679a25486f5f4828092401eff494cfb46656c958641a4e6d0d97d400bc59d92dba0030", + "0x876ab3cea7484bbfd0db621ec085b9ac885d94ab55c4bb671168d82b92e609754b86aaf472c55df3d81421d768fd108a", + "0x885ff4e67d9ece646d02dd425aa5a087e485c3f280c3471b77532b0db6145b69b0fbefb18aa2e3fa5b64928b43a94e57", + "0xb91931d93f806d0b0e6cc62a53c718c099526140f50f45d94b8bbb57d71e78647e06ee7b42aa5714aed9a5c05ac8533f", + "0xa0313eeadd39c720c9c27b3d671215331ab8d0a794e71e7e690f06bcd87722b531d6525060c358f35f5705dbb7109ccb", + "0x874c0944b7fedc6701e53344100612ddcb495351e29305c00ec40a7276ea5455465ffb7bded898886c1853139dfb1fc7", + "0x8dc31701a01ee8137059ca1874a015130d3024823c0576aa9243e6942ec99d377e7715ed1444cd9b750a64b85dcaa3e5", + "0x836d2a757405e922ec9a2dfdcf489a58bd48b5f9683dd46bf6047688f778c8dee9bc456de806f70464df0b25f3f3d238", + "0xb30b0a1e454a503ea3e2efdec7483eaf20b0a5c3cefc42069e891952b35d4b2c955cf615f3066285ed8fafd9fcfbb8f6", + "0x8e6d4044b55ab747e83ec8762ea86845f1785cc7be0279c075dadf08aca3ccc5a096c015bb3c3f738f647a4eadea3ba5", + "0xad7735d16ab03cbe09c029610aa625133a6daecfc990b297205b6da98eda8c136a7c50db90f426d35069708510d5ae9c", + "0x8d62d858bbb59ec3c8cc9acda002e08addab4d3ad143b3812098f3d9087a1b4a1bb255dcb1635da2402487d8d0249161", + "0x805beec33238b832e8530645a3254aeef957e8f7ea24bcfc1054f8b9c69421145ebb8f9d893237e8a001c857fedfc77e", + "0xb1005644be4b085e3f5775aa9bd3e09a283e87ddada3082c04e7a62d303dcef3b8cf8f92944c200c7ae6bb6bdf63f832", + "0xb4ba0e0790dc29063e577474ffe3b61f5ea2508169f5adc1e394934ebb473e356239413a17962bc3e5d3762d72cce8c2", + "0xa157ba9169c9e3e6748d9f1dd67fbe08b9114ade4c5d8fc475f87a764fb7e6f1d21f66d7905cd730f28a1c2d8378682a", + "0x913e52b5c93989b5d15e0d91aa0f19f78d592bc28bcfdfddc885a9980c732b1f4debb8166a7c4083c42aeda93a702898", + "0x90fbfc1567e7cd4e096a38433704d3f96a2de2f6ed3371515ccc30bc4dd0721a704487d25a97f3c3d7e4344472702d8d", + "0x89646043028ffee4b69d346907586fd12c2c0730f024acb1481abea478e61031966e72072ff1d5e65cb8c64a69ad4eb1", + "0xb125a45e86117ee11d2fb42f680ab4a7894edd67ff927ae2c808920c66c3e55f6a9d4588eee906f33a05d592e5ec3c04", + "0xaad47f5b41eae9be55fb4f67674ff1e4ae2482897676f964a4d2dcb6982252ee4ff56aac49578b23f72d1fced707525e", + "0xb9ddff8986145e33851b4de54d3e81faa3352e8385895f357734085a1616ef61c692d925fe62a5ed3be8ca49f5d66306", + "0xb3cb0963387ed28c0c0adf7fe645f02606e6e1780a24d6cecef5b7c642499109974c81a7c2a198b19862eedcea2c2d8c", + "0xac9c53c885457aaf5cb36c717a6f4077af701e0098eebd7aa600f5e4b14e6c1067255b3a0bc40e4a552025231be7de60", + "0x8e1a8d823c4603f6648ec21d064101094f2a762a4ed37dd2f0a2d9aa97b2d850ce1e76f4a4b8cae58819b058180f7031", + "0xb268b73bf7a179b6d22bd37e5e8cb514e9f5f8968c78e14e4f6d5700ca0d0ca5081d0344bb73b028970eebde3cb4124e", + "0xa7f57d71940f0edbd29ed8473d0149cae71d921dd15d1ff589774003e816b54b24de2620871108cec1ab9fa956ad6ce6", + "0x8053e6416c8b120e2b999cc2fc420a6a55094c61ac7f2a6c6f0a2c108a320890e389af96cbe378936132363c0d551277", + "0xb3823f4511125e5aa0f4269e991b435a0d6ceb523ebd91c04d7add5534e3df5fc951c504b4fd412a309fd3726b7f940b", + "0xae6eb04674d04e982ca9a6add30370ab90e303c71486f43ed3efbe431af1b0e43e9d06c11c3412651f304c473e7dbf39", + "0x96ab55e641ed2e677591f7379a3cd126449614181fce403e93e89b1645d82c4af524381ff986cae7f9cebe676878646d", + "0xb52423b4a8c37d3c3e2eca8f0ddbf7abe0938855f33a0af50f117fab26415fb0a3da5405908ec5fdc22a2c1f2ca64892", + "0x82a69ce1ee92a09cc709d0e3cd22116c9f69d28ea507fe5901f5676000b5179b9abe4c1875d052b0dd42d39925e186bb", + "0xa84c8cb84b9d5cfb69a5414f0a5283a5f2e90739e9362a1e8c784b96381b59ac6c18723a4aa45988ee8ef5c1f45cc97d", + "0xafd7efce6b36813082eb98257aae22a4c1ae97d51cac7ea9c852d4a66d05ef2732116137d8432e3f117119725a817d24", + "0xa0f5fe25af3ce021b706fcff05f3d825384a272284d04735574ce5fb256bf27100fad0b1f1ba0e54ae9dcbb9570ecad3", + "0x8751786cb80e2e1ff819fc7fa31c2833d25086534eb12b373d31f826382430acfd87023d2a688c65b5e983927e146336", + "0x8cf5c4b17fa4f3d35c78ce41e1dc86988fd1135cd5e6b2bb0c108ee13538d0d09ae7102609c6070f39f937b439b31e33", + "0xa9108967a2fedd7c322711eca8159c533dd561bedcb181b646de98bf5c3079449478eab579731bee8d215ae8852c7e21", + "0xb54c5171704f42a6f0f4e70767cdb3d96ffc4888c842eece343a01557da405961d53ffdc34d2f902ea25d3e1ed867cad", + "0xae8d4b764a7a25330ba205bf77e9f46182cd60f94a336bbd96773cf8064e3d39caf04c310680943dc89ed1fbad2c6e0d", + "0xaa5150e911a8e1346868e1b71c5a01e2a4bb8632c195861fb6c3038a0e9b85f0e09b3822e9283654a4d7bb17db2fc5f4", + "0x9685d3756ce9069bf8bb716cf7d5063ebfafe37e15b137fc8c3159633c4e006ff4887ddd0ae90360767a25c3f90cba7f", + "0x82155fd70f107ab3c8e414eadf226c797e07b65911508c76c554445422325e71af8c9a8e77fd52d94412a6fc29417cd3", + "0xabfae52f53a4b6e00760468d973a267f29321997c3dbb5aee36dc1f20619551229c0c45b9d9749f410e7f531b73378e8", + "0x81a76d921f8ef88e774fd985e786a4a330d779b93fad7def718c014685ca0247379e2e2a007ad63ee7f729cd9ed6ce1b", + "0x81947c84bc5e28e26e2e533af5ae8fe10407a7b77436dbf8f1d5b0bbe86fc659eae10f974659dc7c826c6dabd03e3a4b", + "0x92b8c07050d635b8dd4fd09df9054efe4edae6b86a63c292e73cc819a12a21dd7d104ce51fa56af6539dedf6dbe6f7b6", + "0xb44c579e3881f32b32d20c82c207307eca08e44995dd2aac3b2692d2c8eb2a325626c80ac81c26eeb38c4137ff95add5", + "0x97efab8941c90c30860926dea69a841f2dcd02980bf5413b9fd78d85904588bf0c1021798dbc16c8bbb32cce66c82621", + "0x913363012528b50698e904de0588bf55c8ec5cf6f0367cfd42095c4468fcc64954fbf784508073e542fee242d0743867", + "0x8ed203cf215148296454012bd10fddaf119203db1919a7b3d2cdc9f80e66729464fdfae42f1f2fc5af1ed53a42b40024", + "0xab84312db7b87d711e9a60824f4fe50e7a6190bf92e1628688dfcb38930fe87b2d53f9e14dd4de509b2216856d8d9188", + "0x880726def069c160278b12d2258eac8fa63f729cd351a710d28b7e601c6712903c3ac1e7bbd0d21e4a15f13ca49db5aa", + "0x980699cd51bac6283959765f5174e543ed1e5f5584b5127980cbc2ef18d984ecabba45042c6773b447b8e694db066028", + "0xaeb019cb80dc4cb4207430d0f2cd24c9888998b6f21d9bf286cc638449668d2eec0018a4cf3fe6448673cd6729335e2b", + "0xb29852f6aa6c60effdffe96ae88590c88abae732561d35cc19e82d3a51e26cb35ea00986193e07f90060756240f5346e", + "0xa0fa855adc5ba469f35800c48414b8921455950a5c0a49945d1ef6e8f2a1881f2e2dfae47de6417270a6bf49deeb091d", + "0xb6c7332e3b14813641e7272d4f69ecc7e09081df0037d6dab97ce13a9e58510f5c930d300633f208181d9205c5534001", + "0x85a6c050f42fce560b5a8d54a11c3bbb8407abbadd859647a7b0c21c4b579ec65671098b74f10a16245dc779dff7838e", + "0x8f3eb34bb68759d53c6677de4de78a6c24dd32c8962a7fb355ed362572ef8253733e6b52bc21c9f92ecd875020a9b8de", + "0xa17dd44181e5dab4dbc128e1af93ec22624b57a448ca65d2d9e246797e4af7d079e09c6e0dfb62db3a9957ce92f098d5", + "0xa56a1b854c3183082543a8685bb34cae1289f86cfa8123a579049dbd059e77982886bfeb61bf6e05b4b1fe4e620932e7", + "0xaedae3033cb2fb7628cb4803435bdd7757370a86f808ae4cecb9a268ad0e875f308c048c80cbcac523de16b609683887", + "0x9344905376aa3982b1179497fac5a1d74b14b7038fd15e3b002db4c11c8bfc7c39430db492cdaf58b9c47996c9901f28", + "0xa3bfafdae011a19f030c749c3b071f83580dee97dd6f949e790366f95618ca9f828f1daaeabad6dcd664fcef81b6556d", + "0x81c03d8429129e7e04434dee2c529194ddb01b414feda3adee2271eb680f6c85ec872a55c9fa9d2096f517e13ed5abcc", + "0x98205ef3a72dff54c5a9c82d293c3e45d908946fa74bb749c3aabe1ab994ea93c269bcce1a266d2fe67a8f02133c5985", + "0x85a70aeed09fda24412fadbafbbbf5ba1e00ac92885df329e147bfafa97b57629a3582115b780d8549d07d19b7867715", + "0xb0fbe81c719f89a57d9ea3397705f898175808c5f75f8eb81c2193a0b555869ba7bd2e6bc54ee8a60cea11735e21c68c", + "0xb03a0bd160495ee626ff3a5c7d95bc79d7da7e5a96f6d10116600c8fa20bedd1132f5170f25a22371a34a2d763f2d6d0", + "0xa90ab04091fbca9f433b885e6c1d60ab45f6f1daf4b35ec22b09909d493a6aab65ce41a6f30c98239cbca27022f61a8b", + "0xb66f92aa3bf2549f9b60b86f99a0bd19cbdd97036d4ae71ca4b83d669607f275260a497208f6476cde1931d9712c2402", + "0xb08e1fdf20e6a9b0b4942f14fa339551c3175c1ffc5d0ab5b226b6e6a322e9eb0ba96adc5c8d59ca4259e2bdd04a7eb0", + "0xa2812231e92c1ce74d4f5ac3ab6698520288db6a38398bb38a914ac9326519580af17ae3e27cde26607e698294022c81", + "0xabfcbbcf1d3b9e84c02499003e490a1d5d9a2841a9e50c7babbef0b2dd20d7483371d4dc629ba07faf46db659459d296", + "0xb0fe9f98c3da70927c23f2975a9dc4789194d81932d2ad0f3b00843dd9cbd7fb60747a1da8fe5a79f136a601becf279d", + "0xb130a6dba7645165348cb90f023713bed0eefbd90a976b313521c60a36d34f02032e69a2bdcf5361e343ed46911297ec", + "0x862f0cffe3020cea7a5fd4703353aa1eb1be335e3b712b29d079ff9f7090d1d8b12013011e1bdcbaa80c44641fd37c9f", + "0x8c6f11123b26633e1abb9ed857e0bce845b2b3df91cc7b013b2fc77b477eee445da0285fc6fc793e29d5912977f40916", + "0x91381846126ea819d40f84d3005e9fb233dc80071d1f9bb07f102bf015f813f61e5884ffffb4f5cd333c1b1e38a05a58", + "0x8add7d908de6e1775adbd39c29a391f06692b936518db1f8fde74eb4f533fc510673a59afb86e3a9b52ade96e3004c57", + "0x8780e086a244a092206edcde625cafb87c9ab1f89cc3e0d378bc9ee776313836160960a82ec397bc3800c0a0ec3da283", + "0xa6cb4cd9481e22870fdd757fae0785edf4635e7aacb18072fe8dc5876d0bab53fb99ce40964a7d3e8bcfff6f0ab1332f", + "0xaf30ff47ecc5b543efba1ba4706921066ca8bb625f40e530fb668aea0551c7647a9d126e8aba282fbcce168c3e7e0130", + "0x91b0bcf408ce3c11555dcb80c4410b5bc2386d3c05caec0b653352377efdcb6bab4827f2018671fc8e4a0e90d772acc1", + "0xa9430b975ef138b6b2944c7baded8fe102d31da4cfe3bd3d8778bda79189c99d38176a19c848a19e2d1ee0bddd9a13c1", + "0xaa5a4eef849d7c9d2f4b018bd01271c1dd83f771de860c4261f385d3bdcc130218495860a1de298f14b703ec32fa235f", + "0xb0ce79e7f9ae57abe4ff366146c3b9bfb38b0dee09c28c28f5981a5d234c6810ad4d582751948affb480d6ae1c8c31c4", + "0xb75122748560f73d15c01a8907d36d06dc068e82ce22b84b322ac1f727034493572f7907dec34ebc3ddcc976f2f89ed7", + "0xb0fc7836369a3e4411d34792d6bd5617c14f61d9bba023dda64e89dc5fb0f423244e9b48ee64869258931daa9753a56f", + "0x8956d7455ae9009d70c6e4a0bcd7610e55f37494cf9897a8f9e1b904cc8febc3fd2d642ebd09025cfff4609ad7e3bc52", + "0xad741efe9e472026aa49ae3d9914cb9c1a6f37a54f1a6fe6419bebd8c7d68dca105a751c7859f4389505ede40a0de786", + "0xb52f418797d719f0d0d0ffb0846788b5cba5d0454a69a2925de4b0b80fa4dd7e8c445e5eac40afd92897ed28ca650566", + "0xa0ab65fb9d42dd966cd93b1de01d7c822694669dd2b7a0c04d99cd0f3c3de795f387b9c92da11353412f33af5c950e9a", + "0xa0052f44a31e5741a331f7cac515a08b3325666d388880162d9a7b97598fde8b61f9ff35ff220df224eb5c4e40ef0567", + "0xa0101cfdc94e42b2b976c0d89612a720e55d145a5ef6ef6f1f78cf6de084a49973d9b5d45915349c34ce712512191e3c", + "0xa0dd99fcf3f5cead5aaf08e82212df3a8bb543c407a4d6fab88dc5130c1769df3f147e934a46f291d6c1a55d92b86917", + "0xa5939153f0d1931bbda5cf6bdf20562519ea55fbfa978d6dbc6828d298260c0da7a50c37c34f386e59431301a96c2232", + "0x9568269f3f5257200f9ca44afe1174a5d3cf92950a7f553e50e279c239e156a9faaa2a67f288e3d5100b4142efe64856", + "0xb746b0832866c23288e07f24991bbf687cad794e7b794d3d3b79367566ca617d38af586cdc8d6f4a85a34835be41d54f", + "0xa871ce28e39ab467706e32fec1669fda5a4abba2f8c209c6745df9f7a0fa36bbf1919cf14cb89ea26fa214c4c907ae03", + "0xa08dacdd758e523cb8484f6bd070642c0c20e184abdf8e2a601f61507e93952d5b8b0c723c34fcbdd70a8485eec29db2", + "0x85bdb78d501382bb95f1166b8d032941005661aefd17a5ac32df9a3a18e9df2fc5dc2c1f07075f9641af10353cecc0c9", + "0x98d730c28f6fa692a389e97e368b58f4d95382fad8f0baa58e71a3d7baaea1988ead47b13742ce587456f083636fa98e", + "0xa557198c6f3d5382be9fb363feb02e2e243b0c3c61337b3f1801c4a0943f18e38ce1a1c36b5c289c8fa2aa9d58742bab", + "0x89174f79201742220ac689c403fc7b243eed4f8e3f2f8aba0bf183e6f5d4907cb55ade3e238e3623d9885f03155c4d2b", + "0xb891d600132a86709e06f3381158db300975f73ea4c1f7c100358e14e98c5fbe792a9af666b85c4e402707c3f2db321e", + "0xb9e5b2529ef1043278c939373fc0dbafe446def52ddd0a8edecd3e4b736de87e63e187df853c54c28d865de18a358bb6", + "0x8589b2e9770340c64679062c5badb7bbef68f55476289b19511a158a9a721f197da03ece3309e059fc4468b15ac33aa3", + "0xaad8c6cd01d785a881b446f06f1e9cd71bca74ba98674c2dcddc8af01c40aa7a6d469037498b5602e76e9c91a58d3dbd", + "0xabaccb1bd918a8465f1bf8dbe2c9ad4775c620b055550b949a399f30cf0d9eb909f3851f5b55e38f9e461e762f88f499", + "0xae62339d26db46e85f157c0151bd29916d5cc619bd4b832814b3fd2f00af8f38e7f0f09932ffe5bba692005dab2d9a74", + "0x93a6ff30a5c0edf8058c89aba8c3259e0f1b1be1b80e67682de651e5346f7e1b4b4ac3d87cbaebf198cf779524aff6bf", + "0x8980a2b1d8f574af45b459193c952400b10a86122b71fca2acb75ee0dbd492e7e1ef5b959baf609a5172115e371f3177", + "0x8c2f49f3666faee6940c75e8c7f6f8edc3f704cca7a858bbb7ee5e96bba3b0cf0993996f781ba6be3b0821ef4cb75039", + "0xb14b9e348215b278696018330f63c38db100b0542cfc5be11dc33046e3bca6a13034c4ae40d9cef9ea8b34fef0910c4e", + "0xb59bc3d0a30d66c16e6a411cb641f348cb1135186d5f69fda8b0a0934a5a2e7f6199095ba319ec87d3fe8f1ec4a06368", + "0x8874aca2a3767aa198e4c3fec2d9c62d496bc41ff71ce242e9e082b7f38cdf356089295f80a301a3cf1182bde5308c97", + "0xb1820ebd61376d91232423fc20bf008b2ba37e761199f4ef0648ea2bd70282766799b4de814846d2f4d516d525c8daa7", + "0xa6b202e5dedc16a4073e04a11af3a8509b23dfe5a1952f899adeb240e75c3f5bde0c424f811a81ea48d343591faffe46", + "0xa69becee9c93734805523b92150a59a62eed4934f66056b645728740d42223f2925a1ad38359ba644da24d9414f4cdda", + "0xad72f0f1305e37c7e6b48c272323ee883320994cb2e0d850905d6655fafc9f361389bcb9c66b3ff8d2051dbb58c8aa96", + "0xb563600bd56fad7c8853af21c6a02a16ed9d8a8bbeea2c31731d63b976d83cb05b9779372d898233e8fd597a75424797", + "0xb0abb78ce465bf7051f563c62e8be9c57a2cc997f47c82819300f36e301fefd908894bb2053a9d27ce2d0f8c46d88b5b", + "0xa071a85fb8274bac2202e0cb8e0e2028a5e138a82d6e0374d39ca1884a549c7c401312f00071b91f455c3a2afcfe0cda", + "0xb931c271513a0f267b9f41444a5650b1918100b8f1a64959c552aff4e2193cc1b9927906c6fa7b8a8c68ef13d79aaa52", + "0xa6a1bb9c7d32cb0ca44d8b75af7e40479fbce67d216b48a2bb680d3f3a772003a49d3cd675fc64e9e0f8fabeb86d6d61", + "0xb98d609858671543e1c3b8564162ad828808bb50ded261a9f8690ded5b665ed8368c58f947365ed6e84e5a12e27b423d", + "0xb3dca58cd69ec855e2701a1d66cad86717ff103ef862c490399c771ad28f675680f9500cb97be48de34bcdc1e4503ffd", + "0xb34867c6735d3c49865e246ddf6c3b33baf8e6f164db3406a64ebce4768cb46b0309635e11be985fee09ab7a31d81402", + "0xacb966c554188c5b266624208f31fab250b3aa197adbdd14aee5ab27d7fb886eb4350985c553b20fdf66d5d332bfd3fe", + "0x943c36a18223d6c870d54c3b051ef08d802b85e9dd6de37a51c932f90191890656c06adfa883c87b906557ae32d09da0", + "0x81bca7954d0b9b6c3d4528aadf83e4bc2ef9ea143d6209bc45ae9e7ae9787dbcd8333c41f12c0b6deee8dcb6805e826a", + "0xaba176b92256efb68f574e543479e5cf0376889fb48e3db4ebfb7cba91e4d9bcf19dcfec444c6622d9398f06de29e2b9", + "0xb9f743691448053216f6ece7cd699871fff4217a1409ceb8ab7bdf3312d11696d62c74b0664ba0a631b1e0237a8a0361", + "0xa383c2b6276fa9af346b21609326b53fb14fdf6f61676683076e80f375b603645f2051985706d0401e6fbed7eb0666b6", + "0xa9ef2f63ec6d9beb8f3d04e36807d84bda87bdd6b351a3e4a9bf7edcb5618c46c1f58cfbf89e64b40f550915c6988447", + "0xa141b2d7a82f5005eaea7ae7d112c6788b9b95121e5b70b7168d971812f3381de8b0082ac1f0a82c7d365922ebd2d26a", + "0xb1b76ef8120e66e1535c17038b75255a07849935d3128e3e99e56567b842fb1e8d56ef932d508d2fb18b82f7868fe1a9", + "0x8e2e234684c81f21099f5c54f6bbe2dd01e3b172623836c77668a0c49ce1fe218786c3827e4d9ae2ea25c50a8924fb3c", + "0xa5caf5ff948bfd3c4ca3ffbdfcd91eec83214a6c6017235f309a0bbf7061d3b0b466307c00b44a1009cf575163898b43", + "0x986415a82ca16ebb107b4c50b0c023c28714281db0bcdab589f6cb13d80e473a3034b7081b3c358e725833f6d845cb14", + "0xb94836bf406ac2cbacb10e6df5bcdfcc9d9124ae1062767ca4e322d287fd5e353fdcebd0e52407cb3cd68571258a8900", + "0x83c6d70a640b33087454a4788dfd9ef3ed00272da084a8d36be817296f71c086b23b576f98178ab8ca6a74f04524b46b", + "0xad4115182ad784cfe11bcfc5ce21fd56229cc2ce77ac82746e91a2f0aa53ca6593a22efd2dc4ed8d00f84542643d9c58", + "0xab1434c5e5065da826d10c2a2dba0facccab0e52b506ce0ce42fbe47ced5a741797151d9ecc99dc7d6373cfa1779bbf6", + "0x8a8b591d82358d55e6938f67ea87a89097ab5f5496f7260adb9f649abb289da12b498c5b2539c2f9614fb4e21b1f66b0", + "0x964f355d603264bc1f44c64d6d64debca66f37dff39c971d9fc924f2bc68e6c187b48564a6dc82660a98b035f8addb5d", + "0xb66235eaaf47456bc1dc4bde454a028e2ce494ece6b713a94cd6bf27cf18c717fd0c57a5681caaa2ad73a473593cdd7a", + "0x9103e3bb74304186fa4e3e355a02da77da4aca9b7e702982fc2082af67127ebb23a455098313c88465bc9b7d26820dd5", + "0xb6a42ff407c9dd132670cdb83cbad4b20871716e44133b59a932cd1c3f97c7ac8ff7f61acfaf8628372508d8dc8cad7c", + "0x883a9c21c16a167a4171b0f084565c13b6f28ba7c4977a0de69f0a25911f64099e7bbb4da8858f2e93068f4155d04e18", + "0x8dbb3220abc6a43220adf0331e3903d3bfd1d5213aadfbd8dfcdf4b2864ce2e96a71f35ecfb7a07c3bbabf0372b50271", + "0xb4ad08aee48e176bda390b7d9acf2f8d5eb008f30d20994707b757dc6a3974b2902d29cd9b4d85e032810ad25ac49e97", + "0x865bb0f33f7636ec501bb634e5b65751c8a230ae1fa807a961a8289bbf9c7fe8c59e01fbc4c04f8d59b7f539cf79ddd5", + "0x86a54d4c12ad1e3605b9f93d4a37082fd26e888d2329847d89afa7802e815f33f38185c5b7292293d788ad7d7da1df97", + "0xb26c8615c5e47691c9ff3deca3021714662d236c4d8401c5d27b50152ce7e566266b9d512d14eb63e65bc1d38a16f914", + "0x827639d5ce7db43ba40152c8a0eaad443af21dc92636cc8cc2b35f10647da7d475a1e408901cd220552fddad79db74df", + "0xa2b79a582191a85dbe22dc384c9ca3de345e69f6aa370aa6d3ff1e1c3de513e30b72df9555b15a46586bd27ea2854d9d", + "0xae0d74644aba9a49521d3e9553813bcb9e18f0b43515e4c74366e503c52f47236be92dfbd99c7285b3248c267b1de5a0", + "0x80fb0c116e0fd6822a04b9c25f456bdca704e2be7bdc5d141dbf5d1c5eeb0a2c4f5d80db583b03ef3e47517e4f9a1b10", + "0xac3a1fa3b4a2f30ea7e0a114cdc479eb51773573804c2a158d603ad9902ae8e39ffe95df09c0d871725a5d7f9ba71a57", + "0xb56b2b0d601cba7f817fa76102c68c2e518c6f20ff693aad3ff2e07d6c4c76203753f7f91686b1801e8c4659e4d45c48", + "0x89d50c1fc56e656fb9d3915964ebce703cb723fe411ab3c9eaa88ccc5d2b155a9b2e515363d9c600d3c0cee782c43f41", + "0xb24207e61462f6230f3cd8ccf6828357d03e725769f7d1de35099ef9ee4dca57dbce699bb49ed994462bee17059d25ce", + "0xb886f17fcbcbfcd08ac07f04bb9543ef58510189decaccea4b4158c9174a067cb67d14b6be3c934e6e2a18c77efa9c9c", + "0xb9c050ad9cafd41c6e2e192b70d080076eed59ed38ea19a12bd92fa17b5d8947d58d5546aaf5e8e27e1d3b5481a6ce51", + "0xaaf7a34d3267e3b1ddbc54c641e3922e89303f7c86ebebc7347ebca4cffad5b76117dac0cbae1a133053492799cd936f", + "0xa9ee604ada50adef82e29e893070649d2d4b7136cc24fa20e281ce1a07bd736bf0de7c420369676bcbcecff26fb6e900", + "0x9855315a12a4b4cf80ab90b8bd13003223ba25206e52fd4fe6a409232fbed938f30120a3db23eab9c53f308bd8b9db81", + "0x8cd488dd7a24f548a3cf03c54dec7ff61d0685cb0f6e5c46c2d728e3500d8c7bd6bba0156f4bf600466fda53e5b20444", + "0x890ad4942ebac8f5b16c777701ab80c68f56fa542002b0786f8fea0fb073154369920ac3dbfc07ea598b82f4985b8ced", + "0x8de0cf9ddc84c9b92c59b9b044387597799246b30b9f4d7626fc12c51f6e423e08ee4cbfe9289984983c1f9521c3e19d", + "0xb474dfb5b5f4231d7775b3c3a8744956b3f0c7a871d835d7e4fd9cc895222c7b868d6c6ce250de568a65851151fac860", + "0x86433b6135d9ed9b5ee8cb7a6c40e5c9d30a68774cec04988117302b8a02a11a71a1e03fd8e0264ef6611d219f103007", + "0x80b9ed4adbe9538fb1ef69dd44ec0ec5b57cbfea820054d8d445b4261962624b4c70ac330480594bc5168184378379c3", + "0x8b2e83562ccd23b7ad2d17f55b1ab7ef5fbef64b3a284e6725b800f3222b8bdf49937f4a873917ada9c4ddfb090938c2", + "0xabe78cebc0f5a45d754140d1f685e387489acbfa46d297a8592aaa0d676a470654f417a4f7d666fc0b2508fab37d908e", + "0xa9c5f8ff1f8568e252b06d10e1558326db9901840e6b3c26bbd0cd5e850cb5fb3af3f117dbb0f282740276f6fd84126f", + "0x975f8dc4fb55032a5df3b42b96c8c0ffecb75456f01d4aef66f973cb7270d4eff32c71520ceefc1adcf38d77b6b80c67", + "0xb043306ed2c3d8a5b9a056565afd8b5e354c8c4569fda66b0d797a50a3ce2c08cffbae9bbe292da69f39e89d5dc7911e", + "0x8d2afc36b1e44386ba350c14a6c1bb31ff6ea77128a0c5287584ac3584282d18516901ce402b4644a53db1ed8e7fa581", + "0x8c294058bed53d7290325c363fe243f6ec4f4ea2343692f4bac8f0cb86f115c069ccb8334b53d2e42c067691ad110dba", + "0xb92157b926751aaf7ef82c1aa8c654907dccab6376187ee8b3e8c0c82811eae01242832de953faa13ebaff7da8698b3e", + "0xa780c4bdd9e4ba57254b09d745075cecab87feda78c88ffee489625c5a3cf96aa6b3c9503a374a37927d9b78de9bd22b", + "0x811f548ef3a2e6a654f7dcb28ac9378de9515ed61e5a428515d9594a83e80b35c60f96a5cf743e6fab0d3cb526149f49", + "0x85a4dccf6d90ee8e094731eec53bd00b3887aec6bd81a0740efddf812fd35e3e4fe4f983afb49a8588691c202dabf942", + "0xb152c2da6f2e01c8913079ae2b40a09b1f361a80f5408a0237a8131b429677c3157295e11b365b1b1841924b9efb922e", + "0x849b9efee8742502ffd981c4517c88ed33e4dd518a330802caff168abae3cd09956a5ee5eda15900243bc2e829016b74", + "0x955a933f3c18ec0f1c0e38fa931e4427a5372c46a3906ebe95082bcf878c35246523c23f0266644ace1fa590ffa6d119", + "0x911989e9f43e580c886656377c6f856cdd4ff1bd001b6db3bbd86e590a821d34a5c6688a29b8d90f28680e9fdf03ba69", + "0xb73b8b4f1fd6049fb68d47cd96a18fcba3f716e0a1061aa5a2596302795354e0c39dea04d91d232aec86b0bf2ba10522", + "0x90f87456d9156e6a1f029a833bf3c7dbed98ca2f2f147a8564922c25ae197a55f7ea9b2ee1f81bf7383197c4bad2e20c", + "0x903cba8b1e088574cb04a05ca1899ab00d8960580c884bd3c8a4c98d680c2ad11410f2b75739d6050f91d7208cac33a5", + "0x9329987d42529c261bd15ecedd360be0ea8966e7838f32896522c965adfc4febf187db392bd441fb43bbd10c38fdf68b", + "0x8178ee93acf5353baa349285067b20e9bb41aa32d77b5aeb7384fe5220c1fe64a2461bd7a83142694fe673e8bbf61b7c", + "0xa06a8e53abcff271b1394bcc647440f81fb1c1a5f29c27a226e08f961c3353f4891620f2d59b9d1902bf2f5cc07a4553", + "0xaaf5fe493b337810889e777980e6bbea6cac39ac66bc0875c680c4208807ac866e9fda9b5952aa1d04539b9f4a4bec57", + "0xaa058abb1953eceac14ccfa7c0cc482a146e1232905dcecc86dd27f75575285f06bbae16a8c9fe8e35d8713717f5f19f", + "0x8f15dd732799c879ca46d2763453b359ff483ca33adb1d0e0a57262352e0476c235987dc3a8a243c74bc768f93d3014c", + "0xa61cc8263e9bc03cce985f1663b8a72928a607121005a301b28a278e9654727fd1b22bc8a949af73929c56d9d3d4a273", + "0x98d6dc78502d19eb9f921225475a6ebcc7b44f01a2df6f55ccf6908d65b27af1891be2a37735f0315b6e0f1576c1f8d8", + "0x8bd258b883f3b3793ec5be9472ad1ff3dc4b51bc5a58e9f944acfb927349ead8231a523cc2175c1f98e7e1e2b9f363b8", + "0xaeacc2ecb6e807ad09bedd99654b097a6f39840e932873ace02eabd64ccfbb475abdcb62939a698abf17572d2034c51e", + "0xb8ccf78c08ccd8df59fd6eda2e01de328bc6d8a65824d6f1fc0537654e9bc6bf6f89c422dd3a295cce628749da85c864", + "0x8f91fd8cb253ba2e71cc6f13da5e05f62c2c3b485c24f5d68397d04665673167fce1fc1aec6085c69e87e66ec555d3fd", + "0xa254baa10cb26d04136886073bb4c159af8a8532e3fd36b1e9c3a2e41b5b2b6a86c4ebc14dbe624ee07b7ccdaf59f9ab", + "0x94e3286fe5cd68c4c7b9a7d33ae3d714a7f265cf77cd0e9bc19fc51015b1d1c34ad7e3a5221c459e89f5a043ee84e3a9", + "0xa279da8878af8d449a9539bec4b17cea94f0242911f66fab275b5143ab040825f78c89cb32a793930609415cfa3a1078", + "0xac846ceb89c9e5d43a2991c8443079dc32298cd63e370e64149cec98cf48a6351c09c856f2632fd2f2b3d685a18bbf8b", + "0xa847b27995c8a2e2454aaeb983879fb5d3a23105c33175839f7300b7e1e8ec3efd6450e9fa3f10323609dee7b98c6fd5", + "0xa2f432d147d904d185ff4b2de8c6b82fbea278a2956bc406855b44c18041854c4f0ecccd472d1d0dff1d8aa8e281cb1d", + "0x94a48ad40326f95bd63dff4755f863a1b79e1df771a1173b17937f9baba57b39e651e7695be9f66a472f098b339364fc", + "0xa12a0ccd8f96e96e1bc6494341f7ebce959899341b3a084aa1aa87d1c0d489ac908552b7770b887bb47e7b8cbc3d8e66", + "0x81a1f1681bda923bd274bfe0fbb9181d6d164fe738e54e25e8d4849193d311e2c4253614ed673c98af2c798f19a93468", + "0xabf71106a05d501e84cc54610d349d7d5eae21a70bd0250f1bebbf412a130414d1c8dbe673ffdb80208fd72f1defa4d4", + "0x96266dc2e0df18d8136d79f5b59e489978eee0e6b04926687fe389d4293c14f36f055c550657a8e27be4118b64254901", + "0x8df5dcbefbfb4810ae3a413ca6b4bf08619ca53cd50eb1dde2a1c035efffc7b7ac7dff18d403253fd80104bd83dc029e", + "0x9610b87ff02e391a43324a7122736876d5b3af2a137d749c52f75d07b17f19900b151b7f439d564f4529e77aa057ad12", + "0xa90a5572198b40fe2fcf47c422274ff36c9624df7db7a89c0eb47eb48a73a03c985f4ac5016161c76ca317f64339bce1", + "0x98e5e61a6ab6462ba692124dba7794b6c6bde4249ab4fcc98c9edd631592d5bc2fb5e38466691a0970a38e48d87c2e43", + "0x918cefb8f292f78d4db81462c633daf73b395e772f47b3a7d2cea598025b1d8c3ec0cbff46cdb23597e74929981cde40", + "0xa98918a5dc7cf610fe55f725e4fd24ce581d594cb957bb9b4e888672e9c0137003e1041f83e3f1d7b9caab06462c87d4", + "0xb92b74ac015262ca66c33f2d950221e19d940ba3bf4cf17845f961dc1729ae227aa9e1f2017829f2135b489064565c29", + "0xa053ee339f359665feb178b4e7ee30a85df37debd17cacc5a27d6b3369d170b0114e67ad1712ed26d828f1df641bcd99", + "0x8c3c8bad510b35da5ce5bd84b35c958797fbea024ad1c97091d2ff71d9b962e9222f65a9b776e5b3cc29c36e1063d2ee", + "0xaf99dc7330fe7c37e850283eb47cc3257888e7c197cb0d102edf94439e1e02267b6a56306d246c326c4c79f9dc8c6986", + "0xafecb2dc34d57a725efbd7eb93d61eb29dbe8409b668ab9ea040791f5b796d9be6d4fc10d7f627bf693452f330cf0435", + "0x93334fedf19a3727a81a6b6f2459db859186227b96fe7a391263f69f1a0884e4235de64d29edebc7b99c44d19e7c7d7a", + "0x89579c51ac405ad7e9df13c904061670ce4b38372492764170e4d3d667ed52e5d15c7cd5c5991bbfa3a5e4e3fa16363e", + "0x9778f3e8639030f7ef1c344014f124e375acb8045bd13d8e97a92c5265c52de9d1ffebaa5bc3e1ad2719da0083222991", + "0x88f77f34ee92b3d36791bdf3326532524a67d544297dcf1a47ff00b47c1b8219ff11e34034eab7d23b507caa2fd3c6b9", + "0xa699c1e654e7c484431d81d90657892efeb4adcf72c43618e71ca7bd7c7a7ebbb1db7e06e75b75dc4c74efd306b5df3f", + "0x81d13153baebb2ef672b5bdb069d3cd669ce0be96b742c94e04038f689ff92a61376341366b286eee6bf3ae85156f694", + "0x81efb17de94400fdacc1deec2550cbe3eecb27c7af99d8207e2f9be397e26be24a40446d2a09536bb5172c28959318d9", + "0x989b21ebe9ceab02488992673dc071d4d5edec24bff0e17a4306c8cb4b3c83df53a2063d1827edd8ed16d6e837f0d222", + "0x8d6005d6536825661b13c5fdce177cb37c04e8b109b7eb2b6d82ea1cb70efecf6a0022b64f84d753d165edc2bba784a3", + "0xa32607360a71d5e34af2271211652d73d7756d393161f4cf0da000c2d66a84c6826e09e759bd787d4fd0305e2439d342", + "0xaaad8d6f6e260db45d51b2da723be6fa832e76f5fbcb77a9a31e7f090dd38446d3b631b96230d78208cae408c288ac4e", + "0xabcfe425255fd3c5cffd3a818af7650190c957b6b07b632443f9e33e970a8a4c3bf79ac9b71f4d45f238a04d1c049857", + "0xaeabf026d4c783adc4414b5923dbd0be4b039cc7201219f7260d321f55e9a5b166d7b5875af6129c034d0108fdc5d666", + "0xaf49e740c752d7b6f17048014851f437ffd17413c59797e5078eaaa36f73f0017c3e7da020310cfe7d3c85f94a99f203", + "0x8854ca600d842566e3090040cd66bb0b3c46dae6962a13946f0024c4a8aca447e2ccf6f240045f1ceee799a88cb9210c", + "0xb6c03b93b1ab1b88ded8edfa1b487a1ed8bdce8535244dddb558ffb78f89b1c74058f80f4db2320ad060d0c2a9c351cc", + "0xb5bd7d17372faff4898a7517009b61a7c8f6f0e7ed4192c555db264618e3f6e57fb30a472d169fea01bf2bf0362a19a8", + "0x96eb1d38319dc74afe7e7eb076fcd230d19983f645abd14a71e6103545c01301b31c47ae931e025f3ecc01fb3d2f31fa", + "0xb55a8d30d4403067def9b65e16f867299f8f64c9b391d0846d4780bc196569622e7e5b64ce799b5aefac8f965b2a7a7b", + "0x8356d199a991e5cbbff608752b6291731b6b6771aed292f8948b1f41c6543e4ab1bedc82dd26d10206c907c03508df06", + "0x97f4137445c2d98b0d1d478049de952610ad698c91c9d0f0e7227d2aae690e9935e914ec4a2ea1fbf3fc1dddfeeacebb", + "0xaf5621707e0938320b15ddfc87584ab325fbdfd85c30efea36f8f9bd0707d7ec12c344eff3ec21761189518d192df035", + "0x8ac7817e71ea0825b292687928e349da7140285d035e1e1abff0c3704fa8453faaae343a441b7143a74ec56539687cc4", + "0x8a5e0a9e4758449489df10f3386029ada828d1762e4fb0a8ffe6b79e5b6d5d713cb64ed95960e126398b0cdb89002bc9", + "0x81324be4a71208bbb9bca74b77177f8f1abb9d3d5d9db195d1854651f2cf333cd618d35400da0f060f3e1b025124e4b2", + "0x849971d9d095ae067525b3cbc4a7dfae81f739537ade6d6cec1b42fb692d923176197a8770907c58069754b8882822d6", + "0x89f830825416802477cc81fdf11084885865ee6607aa15aa4eb28e351c569c49b8a1b9b5e95ddc04fa0ebafe20071313", + "0x9240aeeaff37a91af55f860b9badd466e8243af9e8c96a7aa8cf348cd270685ab6301bc135b246dca9eda696f8b0e350", + "0xacf74db78cc33138273127599eba35b0fb4e7b9a69fe02dae18fc6692d748ca332bd00b22afa8e654ed587aab11833f3", + "0xb091e6d37b157b50d76bd297ad752220cd5c9390fac16dc838f8557aed6d9833fc920b61519df21265406216315e883f", + "0xa6446c429ebf1c7793c622250e23594c836b2fbcaf6c5b3d0995e1595a37f50ea643f3e549b0be8bbdadd69044d72ab9", + "0x93e675353bd60e996bf1c914d5267eeaa8a52fc3077987ccc796710ef9becc6b7a00e3d82671a6bdfb8145ee3c80245a", + "0xa2f731e43251d04ed3364aa2f072d05355f299626f2d71a8a38b6f76cf08c544133f7d72dd0ab4162814b674b9fc7fa6", + "0x97a8b791a5a8f6e1d0de192d78615d73d0c38f1e557e4e15d15adc663d649e655bc8da3bcc499ef70112eafe7fb45c7a", + "0x98cd624cbbd6c53a94469be4643c13130916b91143425bcb7d7028adbbfede38eff7a21092af43b12d4fab703c116359", + "0x995783ce38fd5f6f9433027f122d4cf1e1ff3caf2d196ce591877f4a544ce9113ead60de2de1827eaff4dd31a20d79a8", + "0x8cf251d6f5229183b7f3fe2f607a90b4e4b6f020fb4ba2459d28eb8872426e7be8761a93d5413640a661d73e34a5b81f", + "0xb9232d99620652a3aa7880cad0876f153ff881c4ed4c0c2e7b4ea81d5d42b70daf1a56b869d752c3743c6d4c947e6641", + "0x849716f938f9d37250cccb1bf77f5f9fde53096cdfc6f2a25536a6187029a8f1331cdbed08909184b201f8d9f04b792f", + "0x80c7c4de098cbf9c6d17b14eba1805e433b5bc905f6096f8f63d34b94734f2e4ebf4bce8a177efd1186842a61204a062", + "0xb790f410cf06b9b8daadceeb4fd5ff40a2deda820c8df2537e0a7554613ae3948e149504e3e79aa84889df50c8678eeb", + "0x813aab8bd000299cd37485b73cd7cba06e205f8efb87f1efc0bae8b70f6db2bc7702eb39510ad734854fb65515fe9d0f", + "0x94f0ab7388ac71cdb67f6b85dfd5945748afb2e5abb622f0b5ad104be1d4d0062b651f134ba22385c9e32c2dfdcccce1", + "0xab6223dca8bd6a4f969e21ccd9f8106fc5251d321f9e90cc42cea2424b3a9c4e5060a47eeef6b23c7976109b548498e8", + "0x859c56b71343fce4d5c5b87814c47bf55d581c50fd1871a17e77b5e1742f5af639d0e94d19d909ec7dfe27919e954e0c", + "0xaae0d632b6191b8ad71b027791735f1578e1b89890b6c22e37de0e4a6074886126988fe8319ae228ac9ef3b3bcccb730", + "0x8ca9f32a27a024c3d595ecfaf96b0461de57befa3b331ab71dc110ec3be5824fed783d9516597537683e77a11d334338", + "0xa061df379fb3f4b24816c9f6cd8a94ecb89b4c6dc6cd81e4b8096fa9784b7f97ab3540259d1de9c02eb91d9945af4823", + "0x998603102ac63001d63eb7347a4bb2bf4cf33b28079bb48a169076a65c20d511ccd3ef696d159e54cc8e772fb5d65d50", + "0x94444d96d39450872ac69e44088c252c71f46be8333a608a475147752dbb99db0e36acfc5198f158509401959c12b709", + "0xac1b51b6c09fe055c1d7c9176eea9adc33f710818c83a1fbfa073c8dc3a7eb3513cbdd3f5960b7845e31e3e83181e6ba", + "0x803d530523fc9e1e0f11040d2412d02baef3f07eeb9b177fa9bfa396af42eea898a4276d56e1db998dc96ae47b644cb2", + "0x85a3c9fc7638f5bf2c3e15ba8c2fa1ae87eb1ceb44c6598c67a2948667a9dfa41e61f66d535b4e7fda62f013a5a8b885", + "0xa961cf5654c46a1a22c29baf7a4e77837a26b7f138f410e9d1883480ed5fa42411d522aba32040b577046c11f007388e", + "0xad1154142344f494e3061ef45a34fab1aaacf5fdf7d1b26adbb5fbc3d795655fa743444e39d9a4119b4a4f82a6f30441", + "0xb1d6c30771130c77806e7ab893b73d4deb590b2ff8f2f8b5e54c2040c1f3e060e2bd99afc668cf706a2df666a508bbf6", + "0xa00361fd440f9decabd98d96c575cd251dc94c60611025095d1201ef2dedde51cb4de7c2ece47732e5ed9b3526c2012c", + "0xa85c5ab4d17d328bda5e6d839a9a6adcc92ff844ec25f84981e4f44a0e8419247c081530f8d9aa629c7eb4ca21affba6", + "0xa4ddd3eab4527a2672cf9463db38bc29f61460e2a162f426b7852b7a7645fbd62084fd39a8e4d60e1958cce436dd8f57", + "0x811648140080fe55b8618f4cf17f3c5a250adb0cd53d885f2ddba835d2b4433188e41fc0661faac88e4ff910b16278c0", + "0xb85c7f1cfb0ed29addccf7546023a79249e8f15ac2d14a20accbfef4dd9dc11355d599815fa09d2b6b4e966e6ea8cff1", + "0xa10b5d8c260b159043b020d5dd62b3467df2671afea6d480ca9087b7e60ed170c82b121819d088315902842d66c8fb45", + "0x917e191df1bcf3f5715419c1e2191da6b8680543b1ba41fe84ed07ef570376e072c081beb67b375fca3565a2565bcabb", + "0x881fd967407390bfd7badc9ab494e8a287559a01eb07861f527207c127eadea626e9bcc5aa9cca2c5112fbac3b3f0e9c", + "0x959fd71149af82cc733619e0e5bf71760ca2650448c82984b3db74030d0e10f8ab1ce1609a6de6f470fe8b5bd90df5b3", + "0xa3370898a1c5f33d15adb4238df9a6c945f18b9ada4ce2624fc32a844f9ece4c916a64e9442225b6592afa06d2e015f2", + "0x817efb8a791435e4236f7d7b278181a5fa34587578c629dbc14fbf9a5c26772290611395eecd20222a4c58649fc256d8", + "0xa04c9876acf2cfdc8ef96de4879742709270fa1d03fe4c8511fbef2d59eb0aaf0336fa2c7dfe41a651157377fa217813", + "0x81e15875d7ea7f123e418edf14099f2e109d4f3a6ce0eb65f67fe9fb10d2f809a864a29f60ad3fc949f89e2596b21783", + "0xb49f529975c09e436e6bc202fdc16e3fdcbe056db45178016ad6fdece9faad4446343e83aed096209690b21a6910724f", + "0x879e8eda589e1a279f7f49f6dd0580788c040d973748ec4942dbe51ea8fbd05983cc919b78f0c6b92ef3292ae29db875", + "0x81a2b74b2118923f34139a102f3d95e7eee11c4c2929c2576dee200a5abfd364606158535a6c9e4178a6a83dbb65f3c4", + "0x8913f281d8927f2b45fc815d0f7104631cb7f5f7278a316f1327d670d15868daadd2a64e3eb98e1f53fe7e300338cc80", + "0xa6f815fba7ef9af7fbf45f93bc952e8b351f5de6568a27c7c47a00cb39a254c6b31753794f67940fc7d2e9cc581529f4", + "0xb3722a15c66a0014ce4d082de118def8d39190c15678a472b846225585f3a83756ae1b255b2e3f86a26168878e4773b2", + "0x817ae61ab3d0dd5b6e24846b5a5364b1a7dc2e77432d9fed587727520ae2f307264ea0948c91ad29f0aea3a11ff38624", + "0xb3db467464415fcad36dc1de2d6ba7686772a577cc2619242ac040d6734881a45d3b40ed4588db124e4289cfeec4bbf6", + "0xad66a14f5a54ac69603b16e5f1529851183da77d3cc60867f10aea41339dd5e06a5257982e9e90a352cdd32750f42ee4", + "0xadafa3681ef45d685555601a25a55cf23358319a17f61e2179e704f63df83a73bdd298d12cf6cef86db89bd17119e11d", + "0xa379dc44cb6dd3b9d378c07b2ec654fec7ca2f272de6ba895e3d00d20c9e4c5550498a843c8ac67e4221db2115bedc1c", + "0xb7bf81c267a78efc6b9e5a904574445a6487678d7ef70054e3e93ea6a23f966c2b68787f9164918e3b16d2175459ed92", + "0xb41d66a13a4afafd5760062b77f79de7e6ab8ccacde9c6c5116a6d886912fb491dc027af435b1b44aacc6af7b3c887f2", + "0x9904d23a7c1c1d2e4bab85d69f283eb0a8e26d46e8b7b30224438015c936729b2f0af7c7c54c03509bb0500acb42d8a4", + "0xae30d65e9e20c3bfd603994ae2b175ff691d51f3e24b2d058b3b8556d12ca4c75087809062dddd4aaac81c94d15d8a17", + "0x9245162fab42ac01527424f6013310c3eb462982518debef6c127f46ba8a06c705d7dc9f0a41e796ba8d35d60ae6cc64", + "0x87fab853638d7a29a20f3ba2b1a7919d023e9415bfa78ebb27973d8cbc7626f584dc5665d2e7ad71f1d760eba9700d88", + "0x85aac46ecd330608e5272430970e6081ff02a571e8ea444f1e11785ea798769634a22a142d0237f67b75369d3c484a8a", + "0x938c85ab14894cc5dfce3d80456f189a2e98eddbc8828f4ff6b1df1dcb7b42b17ca2ff40226a8a1390a95d63dca698dd", + "0xa18ce1f846e3e3c4d846822f60271eecf0f5d7d9f986385ac53c5ace9589dc7c0188910448c19b91341a1ef556652fa9", + "0x8611608a9d844f0e9d7584ad6ccf62a5087a64f764caf108db648a776b5390feb51e5120f0ef0e9e11301af3987dd7dc", + "0x8106333ba4b4de8d1ae43bc9735d3fea047392e88efd6a2fa6f7b924a18a7a265ca6123c3edc0f36307dd7fb7fe89257", + "0xa91426fa500951ff1b051a248c050b7139ca30dde8768690432d597d2b3c4357b11a577be6b455a1c5d145264dcf81fc", + "0xb7f9f90e0e450f37b081297f7f651bad0496a8b9afd2a4cf4120a2671aaaa8536dce1af301258bfbfdb122afa44c5048", + "0x84126da6435699b0c09fa4032dec73d1fca21d2d19f5214e8b0bea43267e9a8dd1fc44f8132d8315e734c8e2e04d7291", + "0xaff064708103884cb4f1a3c1718b3fc40a238d35cf0a7dc24bdf9823693b407c70da50df585bf5bc4e9c07d1c2d203e8", + "0xa8b40fc6533752983a5329c31d376c7a5c13ce6879cc7faee648200075d9cd273537001fb4c86e8576350eaac6ba60c2", + "0xa02db682bdc117a84dcb9312eb28fcbde12d49f4ce915cc92c610bb6965ec3cc38290f8c5b5ec70afe153956692cda95", + "0x86decd22b25d300508472c9ce75d3e465b737e7ce13bc0fcce32835e54646fe12322ba5bc457be18bfd926a1a6ca4a38", + "0xa18666ef65b8c2904fd598791f5627207165315a85ee01d5fb0e6b2e10bdd9b00babc447da5bd63445e3337de33b9b89", + "0x89bb0c06effadefdaf34ffe4b123e1678a90d4451ee856c863df1e752eef41fd984689ded8f0f878bf8916d5dd8e8024", + "0x97cfcba08ebec05d0073992a66b1d7d6fb9d95871f2cdc36db301f78bf8069294d1c259efef5c93d20dc937eedae3a1a", + "0xac2643b14ece79dcb2e289c96776a47e2bebd40dd6dc74fd035df5bb727b5596f40e3dd2d2202141e69b0993717ede09", + "0xa5e6fd88a2f9174d9bd4c6a55d9c30974be414992f22aa852f552c7648f722ed8077acf5aba030abd47939bb451b2c60", + "0x8ad40a612824a7994487731a40b311b7349038c841145865539c6ada75c56de6ac547a1c23df190e0caaafecddd80ccc", + "0x953a7cea1d857e09202c438c6108060961f195f88c32f0e012236d7a4b39d840c61b162ec86436e8c38567328bea0246", + "0x80d8b47a46dae1868a7b8ccfe7029445bbe1009dad4a6c31f9ef081be32e8e1ac1178c3c8fb68d3e536c84990cc035b1", + "0x81ecd99f22b3766ce0aca08a0a9191793f68c754fdec78b82a4c3bdc2db122bbb9ebfd02fc2dcc6e1567a7d42d0cc16a", + "0xb1dd0446bccc25846fb95d08c1c9cc52fb51c72c4c5d169ffde56ecfe800f108dc1106d65d5c5bd1087c656de3940b63", + "0xb87547f0931e164e96de5c550ca5aa81273648fe34f6e193cd9d69cf729cb432e17aa02e25b1c27a8a0d20a3b795e94e", + "0x820a94e69a927e077082aae66f6b292cfbe4589d932edf9e68e268c9bd3d71ef76cf7d169dd445b93967c25db11f58f1", + "0xb0d07ddf2595270c39adfa0c8cf2ab1322979b0546aa4d918f641be53cd97f36c879bb75d205e457c011aca3bbd9f731", + "0x8700b876b35b4b10a8a9372c5230acecd39539c1bb87515640293ad4464a9e02929d7d6a6a11112e8a29564815ac0de4", + "0xa61a601c5bb27dcb97e37c8e2b9ce479c6b192a5e04d9ed5e065833c5a1017ee5f237b77d1a17be5d48f8e7cc0bcacf6", + "0x92fb88fe774c1ba1d4a08cae3c0e05467ad610e7a3f1d2423fd47751759235fe0a3036db4095bd6404716aa03820f484", + "0xb274f140d77a3ce0796f5e09094b516537ccaf27ae1907099bff172e6368ba85e7c3ef8ea2a07457cac48ae334da95b3", + "0xb2292d9181f16581a9a9142490b2bdcdfb218ca6315d1effc8592100d792eb89d5356996c890441f04f2b4a95763503e", + "0x8897e73f576d86bc354baa3bd96e553107c48cf5889dcc23c5ba68ab8bcd4e81f27767be2233fdfa13d39f885087e668", + "0xa29eac6f0829791c728d71abc49569df95a4446ecbfc534b39f24f56c88fe70301838dfc1c19751e7f3c5c1b8c6af6a0", + "0x9346dc3720adc5df500a8df27fd9c75ef38dc5c8f4e8ed66983304750e66d502c3c59b8e955be781b670a0afc70a2167", + "0x9566d534e0e30a5c5f1428665590617e95fd05d45f573715f58157854ad596ece3a3cfec61356aee342308d623e029d5", + "0xa464fb8bffe6bd65f71938c1715c6e296cc6d0311a83858e4e7eb5873b7f2cf0c584d2101e3407b85b64ca78b2ac93ce", + "0xb54088f7217987c87e9498a747569ac5b2f8afd5348f9c45bf3fd9fbf713a20f495f49c8572d087efe778ac7313ad6d3", + "0x91fa9f5f8000fe050f5b224d90b59fcce13c77e903cbf98ded752e5b3db16adb2bc1f8c94be48b69f65f1f1ad81d6264", + "0x92d04a5b0ac5d8c8e313709b432c9434ecd3e73231f01e9b4e7952b87df60cbfa97b5dedd2200bd033b4b9ea8ba45cc1", + "0xa94b90ad3c3d6c4bbe169f8661a790c40645b40f0a9d1c7220f01cf7fc176e04d80bab0ced9323fcafb93643f12b2760", + "0x94d86149b9c8443b46196f7e5a3738206dd6f3be7762df488bcbb9f9ee285a64c997ed875b7b16b26604fa59020a8199", + "0x82efe4ae2c50a2d7645240c173a047f238536598c04a2c0b69c96e96bd18e075a99110f1206bc213f39edca42ba00cc1", + "0xab8667685f831bc14d4610f84a5da27b4ea5b133b4d991741a9e64dceb22cb64a3ce8f1b6e101d52af6296df7127c9ad", + "0x83ba433661c05dcc5d562f4a9a261c8110dac44b8d833ae1514b1fc60d8b4ee395b18804baea04cb10adb428faf713c3", + "0xb5748f6f660cc5277f1211d2b8649493ed8a11085b871cd33a5aea630abd960a740f08c08be5f9c21574600ac9bf5737", + "0xa5c8dd12af48fb710642ad65ebb97ca489e8206741807f7acfc334f8035d3c80593b1ff2090c9bb7bd138f0c48714ca8", + "0xa2b382fd5744e3babf454b1d806cc8783efeb4761bc42b6914ea48a46a2eae835efbe0a18262b6bc034379e03cf1262b", + "0xb3145ffaf603f69f15a64936d32e3219eea5ed49fdfd2f5bf40ea0dfd974b36fb6ff12164d4c2282d892db4cf3ff3ce1", + "0x87a316fb213f4c5e30c5e3face049db66be4f28821bd96034714ec23d3e97849d7b301930f90a4323c7ccf53de23050c", + "0xb9de09a919455070fed6220fc179c8b7a4c753062bcd27acf28f5b9947a659c0b364298daf7c85c4ca6fca7f945add1f", + "0x806fbd98d411b76979464c40ad88bc07a151628a27fcc1012ba1dfbaf5b5cc9d962fb9b3386008978a12515edce934bc", + "0xa15268877fae0d21610ae6a31061ed7c20814723385955fac09fdc9693a94c33dea11db98bb89fdfe68f933490f5c381", + "0x8d633fb0c4da86b2e0b37d8fad5972d62bff2ac663c5ec815d095cd4b7e1fe66ebef2a2590995b57eaf941983c7ad7a4", + "0x8139e5dd9cf405e8ef65f11164f0440827d98389ce1b418b0c9628be983a9ddd6cf4863036ccb1483b40b8a527acd9ed", + "0x88b15fa94a08eac291d2b94a2b30eb851ff24addf2cc30b678e72e32cfcb3424cf4b33aa395d741803f3e578ddf524de", + "0xb5eaf0c8506e101f1646bcf049ee38d99ea1c60169730da893fd6020fd00a289eb2f415947e44677af49e43454a7b1be", + "0x8489822ad0647a7e06aa2aa5595960811858ddd4542acca419dd2308a8c5477648f4dd969a6740bb78aa26db9bfcc555", + "0xb1e9a7b9f3423c220330d45f69e45fa03d7671897cf077f913c252e3e99c7b1b1cf6d30caad65e4228d5d7b80eb86e5e", + "0xb28fe9629592b9e6a55a1406903be76250b1c50c65296c10c5e48c64b539fb08fe11f68cf462a6edcbba71b0cee3feb2", + "0xa41acf96a02c96cd8744ff6577c244fc923810d17ade133587e4c223beb7b4d99fa56eae311a500d7151979267d0895c", + "0x880798938fe4ba70721be90e666dfb62fcab4f3556fdb7b0dc8ec5bc34f6b4513df965eae78527136eb391889fe2caf9", + "0x98d4d89d358e0fb7e212498c73447d94a83c1b66e98fc81427ab13acddb17a20f52308983f3a5a8e0aaacec432359604", + "0x81430b6d2998fc78ba937a1639c6020199c52da499f68109da227882dc26d005b73d54c5bdcac1a04e8356a8ca0f7017", + "0xa8d906a4786455eb74613aba4ce1c963c60095ffb8658d368df9266fdd01e30269ce10bf984e7465f34b4fd83beba26a", + "0xaf54167ac1f954d10131d44a8e0045df00d581dd9e93596a28d157543fbe5fb25d213806ed7fb3cba6b8f5b5423562db", + "0x8511e373a978a12d81266b9afbd55035d7bc736835cfa921903a92969eeba3624437d1346b55382e61415726ab84a448", + "0x8cf43eea93508ae586fa9a0f1354a1e16af659782479c2040874a46317f9e8d572a23238efa318fdfb87cc63932602b7", + "0xb0bdd3bacff077173d302e3a9678d1d37936188c7ecc34950185af6b462b7c679815176f3cce5db19aac8b282f2d60ad", + "0xa355e9b87f2f2672052f5d4d65b8c1c827d24d89b0d8594641fccfb69aef1b94009105f3242058bb31c8bf51caae5a41", + "0xb8baa9e4b950b72ff6b88a6509e8ed1304bc6fd955748b2e59a523a1e0c5e99f52aec3da7fa9ff407a7adf259652466c", + "0x840bc3dbb300ea6f27d1d6dd861f15680bd098be5174f45d6b75b094d0635aced539fa03ddbccb453879de77fb5d1fe9", + "0xb4bc7e7e30686303856472bae07e581a0c0bfc815657c479f9f5931cff208d5c12930d2fd1ff413ebd8424bcd7a9b571", + "0x89b5d514155d7999408334a50822508b9d689add55d44a240ff2bdde2eee419d117031f85e924e2a2c1ca77db9b91eea", + "0xa8604b6196f87a04e1350302e8aa745bba8dc162115d22657b37a1d1a98cb14876ddf7f65840b5dbd77e80cd22b4256c", + "0x83cb7acdb9e03247515bb2ce0227486ccf803426717a14510f0d59d45e998b245797d356f10abca94f7a14e1a2f0d552", + "0xaeb3266a9f16649210ab2df0e1908ac259f34ce1f01162c22b56cf1019096ee4ea5854c36e30bb2feb06c21a71e8a45c", + "0x89e72e86edf2aa032a0fc9acf4d876a40865fbb2c8f87cb7e4d88856295c4ac14583e874142fd0c314a49aba68c0aa3c", + "0x8c3576eba0583c2a7884976b4ed11fe1fda4f6c32f6385d96c47b0e776afa287503b397fa516a455b4b8c3afeedc76db", + "0xa31e5b633bda9ffa174654fee98b5d5930a691c3c42fcf55673d927dbc8d91c58c4e42e615353145431baa646e8bbb30", + "0x89f2f3f7a8da1544f24682f41c68114a8f78c86bd36b066e27da13acb70f18d9f548773a16bd8e24789420e17183f137", + "0xada27fa4e90a086240c9164544d2528621a415a5497badb79f8019dc3dce4d12eb6b599597e47ec6ac39c81efda43520", + "0x90dc1eb21bf21c0187f359566fc4bf5386abea52799306a0e5a1151c0817c5f5bc60c86e76b1929c092c0f3ff48cedd2", + "0xb702a53ebcc17ae35d2e735a347d2c700e9cbef8eadbece33cac83df483b2054c126593e1f462cfc00a3ce9d737e2af5", + "0x9891b06455ec925a6f8eafffba05af6a38cc5e193acaaf74ffbf199df912c5197106c5e06d72942bbb032ce277b6417f", + "0x8c0ee71eb01197b019275bcf96cae94e81d2cdc3115dbf2d8e3080074260318bc9303597e8f72b18f965ad601d31ec43", + "0x8aaf580aaf75c1b7a5f99ccf60503506e62058ef43b28b02f79b8536a96be3f019c9f71caf327b4e6730134730d1bef5", + "0xae6f9fc21dd7dfa672b25a87eb0a41644f7609fab5026d5cedb6e43a06dbbfd6d6e30322a2598c8dedde88c52eaed626", + "0x8159b953ffece5693edadb2e906ebf76ff080ee1ad22698950d2d3bfc36ac5ea78f58284b2ca180664452d55bd54716c", + "0xab7647c32ca5e9856ac283a2f86768d68de75ceeba9e58b74c5324f8298319e52183739aba4340be901699d66ac9eb3f", + "0xa4d85a5701d89bcfaf1572db83258d86a1a0717603d6f24ac2963ffcf80f1265e5ab376a4529ca504f4396498791253c", + "0x816080c0cdbfe61b4d726c305747a9eb58ac26d9a35f501dd32ba43c098082d20faf3ccd41aad24600aa73bfa453dfac", + "0x84f3afac024f576b0fd9acc6f2349c2fcefc3f77dbe5a2d4964d14b861b88e9b1810334b908cf3427d9b67a8aee74b18", + "0x94b390655557b1a09110018e9b5a14490681ade275bdc83510b6465a1218465260d9a7e2a6e4ec700f58c31dc3659962", + "0xa8c66826b1c04a2dd4c682543242e7a57acae37278bd09888a3d17747c5b5fec43548101e6f46d703638337e2fd3277b", + "0x86e6f4608a00007fa533c36a5b054c5768ccafe41ad52521d772dcae4c8a4bcaff8f7609be30d8fab62c5988cbbb6830", + "0x837da4cf09ae8aa0bceb16f8b3bfcc3b3367aecac9eed6b4b56d7b65f55981ef066490764fb4c108792623ecf8cad383", + "0x941ff3011462f9b5bf97d8cbdb0b6f5d37a1b1295b622f5485b7d69f2cb2bcabc83630dae427f0259d0d9539a77d8424", + "0xb99e5d6d82aa9cf7d5970e7f710f4039ac32c2077530e4c2779250c6b9b373bc380adb0a03b892b652f649720672fc8c", + "0xa791c78464b2d65a15440b699e1e30ebd08501d6f2720adbc8255d989a82fcded2f79819b5f8f201bed84a255211b141", + "0x84af7ad4a0e31fcbb3276ab1ad6171429cf39adcf78dc03750dc5deaa46536d15591e26d53e953dfb31e1622bc0743ab", + "0xa833e62fe97e1086fae1d4917fbaf09c345feb6bf1975b5cb863d8b66e8d621c7989ab3dbecda36bc9eaffc5eaa6fa66", + "0xb4ef79a46a2126f53e2ebe62770feb57fd94600be29459d70a77c5e9cc260fa892be06cd60f886bf48459e48eb50d063", + "0xb43b8f61919ea380bf151c294e54d3a3ff98e20d1ee5efbfe38aa2b66fafbc6a49739793bd5cb1c809f8b30466277c3a", + "0xab37735af2412d2550e62df9d8b3b5e6f467f20de3890bf56faf1abf2bf3bd1d98dc3fa0ad5e7ab3fce0fa20409eb392", + "0x82416b74b1551d484250d85bb151fabb67e29cce93d516125533df585bc80779ab057ea6992801a3d7d5c6dcff87a018", + "0x8145d0787f0e3b5325190ae10c1d6bee713e6765fb6a0e9214132c6f78f4582bb2771aaeae40d3dad4bafb56bf7e36d8", + "0xb6935886349ecbdd5774e12196f4275c97ec8279fdf28ccf940f6a022ebb6de8e97d6d2173c3fe402cbe9643bed3883b", + "0x87ef9b4d3dc71ac86369f8ed17e0dd3b91d16d14ae694bc21a35b5ae37211b043d0e36d8ff07dcc513fb9e6481a1f37f", + "0xae1d0ded32f7e6f1dc8fef495879c1d9e01826f449f903c1e5034aeeabc5479a9e323b162b688317d46d35a42d570d86", + "0xa40d16497004db4104c6794e2f4428d75bdf70352685944f3fbe17526df333e46a4ca6de55a4a48c02ecf0bde8ba03c0", + "0x8d45121efba8cc308a498e8ee39ea6fa5cae9fb2e4aab1c2ff9d448aa8494ccbec9a078f978a86fcd97b5d5e7be7522a", + "0xa8173865c64634ba4ac2fa432740f5c05056a9deaf6427cb9b4b8da94ca5ddbc8c0c5d3185a89b8b28878194de9cdfcd", + "0xb6ec06a74d690f6545f0f0efba236e63d1fdfba54639ca2617408e185177ece28901c457d02b849fd00f1a53ae319d0a", + "0xb69a12df293c014a40070e3e760169b6f3c627caf9e50b35a93f11ecf8df98b2bc481b410eecb7ab210bf213bbe944de", + "0x97e7dc121795a533d4224803e591eef3e9008bab16f12472210b73aaf77890cf6e3877e0139403a0d3003c12c8f45636", + "0xacdfa6fdd4a5acb7738cc8768f7cba84dbb95c639399b291ae8e4e63df37d2d4096900a84d2f0606bf534a9ccaa4993f", + "0x86ee253f3a9446a33e4d1169719b7d513c6b50730988415382faaf751988c10a421020609f7bcdef91be136704b906e2", + "0xaac9438382a856caf84c5a8a234282f71b5fc5f65219103b147e7e6cf565522285fbfd7417b513bdad8277a00f652ca1", + "0x83f3799d8e5772527930f5dc071a2e0a65471618993ec8990a96ccdeee65270e490bda9d26bb877612475268711ffd80", + "0x93f28a81ac8c0ec9450b9d762fae9c7f8feaace87a6ee6bd141ef1d2d0697ef1bbd159fe6e1de640dbdab2b0361fca8a", + "0xa0825c95ba69999b90eac3a31a3fd830ea4f4b2b7409bde5f202b61d741d6326852ce790f41de5cb0eccec7af4db30c1", + "0x83924b0e66233edd603c3b813d698daa05751fc34367120e3cf384ea7432e256ccee4d4daf13858950549d75a377107d", + "0x956fd9fa58345277e06ba2ec72f49ed230b8d3d4ff658555c52d6cddeb84dd4e36f1a614f5242d5ca0192e8daf0543c2", + "0x944869912476baae0b114cced4ff65c0e4c90136f73ece5656460626599051b78802df67d7201c55d52725a97f5f29fe", + "0x865cb25b64b4531fb6fe4814d7c8cd26b017a6c6b72232ff53defc18a80fe3b39511b23f9e4c6c7249d06e03b2282ed2", + "0x81e09ff55214960775e1e7f2758b9a6c4e4cd39edf7ec1adfaad51c52141182b79fe2176b23ddc7df9fd153e5f82d668", + "0xb31006896f02bc90641121083f43c3172b1039334501fbaf1672f7bf5d174ddd185f945adf1a9c6cf77be34c5501483d", + "0x88b92f6f42ae45e9f05b16e52852826e933efd0c68b0f2418ac90957fd018df661bc47c8d43c2a7d7bfcf669dab98c3c", + "0x92fc68f595853ee8683930751789b799f397135d002eda244fe63ecef2754e15849edde3ba2f0cc8b865c9777230b712", + "0x99ca06a49c5cd0bb097c447793fcdd809869b216a34c66c78c7e41e8c22f05d09168d46b8b1f3390db9452d91bc96dea", + "0xb48b9490a5d65296802431852d548d81047bbefc74fa7dc1d4e2a2878faacdfcb365ae59209cb0ade01901a283cbd15d", + "0xaff0fdbef7c188b120a02bc9085d7b808e88f73973773fef54707bf2cd772cd066740b1b6f4127b5c349f657bd97e738", + "0x966fd4463b4f43dd8ccba7ad50baa42292f9f8b2e70da23bb6780e14155d9346e275ef03ddaf79e47020dcf43f3738bd", + "0x9330c3e1fadd9e08ac85f4839121ae20bbeb0a5103d84fa5aadbd1213805bdcda67bf2fb75fc301349cbc851b5559d20", + "0x993bb99867bd9041a71a55ad5d397755cfa7ab6a4618fc526179bfc10b7dc8b26e4372fe9a9b4a15d64f2b63c1052dda", + "0xa29b59bcfab51f9b3c490a3b96f0bf1934265c315349b236012adbd64a56d7f6941b2c8cc272b412044bc7731f71e1dc", + "0xa65c9cefe1fc35d089fe8580c2e7671ebefdb43014ac291528ff4deefd4883fd4df274af83711dad610dad0d615f9d65", + "0x944c78c56fb227ae632805d448ca3884cd3d2a89181cead3d2b7835e63297e6d740aa79a112edb1d4727824991636df5", + "0xa73d782da1db7e4e65d7b26717a76e16dd9fab4df65063310b8e917dc0bc24e0d6755df5546c58504d04d9e68c3b474a", + "0xaf80f0b87811ae3124f68108b4ca1937009403f87928bbc53480e7c5408d072053ace5eeaf5a5aba814dab8a45502085", + "0x88aaf1acfc6e2e19b8387c97da707cb171c69812fefdd4650468e9b2c627bd5ccfb459f4d8e56bdfd84b09ddf87e128f", + "0x92c97276ff6f72bab6e9423d02ad6dc127962dbce15a0dd1e4a393b4510c555df6aa27be0f697c0d847033a9ca8b8dfd", + "0xa0e07d43d96e2d85b6276b3c60aadb48f0aedf2de8c415756dc597249ea64d2093731d8735231dadc961e5682ac59479", + "0xadc9e6718a8f9298957d1da3842a7751c5399bbdf56f8de6c1c4bc39428f4aee6f1ba6613d37bf46b9403345e9d6fc81", + "0x951da434da4b20d949b509ceeba02e24da7ed2da964c2fcdf426ec787779c696b385822c7dbea4df3e4a35921f1e912c", + "0xa04cbce0d2b2e87bbf038c798a12ec828423ca6aca08dc8d481cf6466e3c9c73d4d4a7fa47df9a7e2e15aae9e9f67208", + "0x8f855cca2e440d248121c0469de1f94c2a71b8ee2682bbad3a78243a9e03da31d1925e6760dbc48a1957e040fae9abe8", + "0xb642e5b17c1df4a4e101772d73851180b3a92e9e8b26c918050f51e6dd3592f102d20b0a1e96f0e25752c292f4c903ff", + "0xa92454c300781f8ae1766dbbb50a96192da7d48ef4cbdd72dd8cbb44c6eb5913c112cc38e9144615fdc03684deb99420", + "0x8b74f7e6c2304f8e780df4649ef8221795dfe85fdbdaa477a1542d135b75c8be45bf89adbbb6f3ddf54ca40f02e733e9", + "0x85cf66292cbb30cec5fd835ab10c9fcb3aea95e093aebf123e9a83c26f322d76ebc89c4e914524f6c5f6ee7d74fc917d", + "0xae0bfe0cdc97c09542a7431820015f2d16067b30dca56288013876025e81daa8c519e5e347268e19aa1a85fa1dc28793", + "0x921322fc6a47dc091afa0ad6df18ed14cde38e48c6e71550aa513918b056044983aee402de21051235eecf4ce8040fbe", + "0x96c030381e97050a45a318d307dcb3c8377b79b4dd5daf6337cded114de26eb725c14171b9b8e1b3c08fe1f5ea6b49e0", + "0x90c23b86b6111818c8baaf53a13eaee1c89203b50e7f9a994bf0edf851919b48edbac7ceef14ac9414cf70c486174a77", + "0x8bf6c301240d2d1c8d84c71d33a6dfc6d9e8f1cfae66d4d0f7a256d98ae12b0bcebfa94a667735ee89f810bcd7170cff", + "0xa41a4ffbbea0e36874d65c009ee4c3feffff322f6fc0e30d26ee4dbc1f46040d05e25d9d0ecb378cef0d24a7c2c4b850", + "0xa8d4cdd423986bb392a0a92c12a8bd4da3437eec6ef6af34cf5310944899287452a2eb92eb5386086d5063381189d10e", + "0xa81dd26ec057c4032a4ed7ad54d926165273ed51d09a1267b2e477535cf6966835a257c209e4e92d165d74fa75695fa3", + "0x8d7f708c3ee8449515d94fc26b547303b53d8dd55f177bc3b25d3da2768accd9bc8e9f09546090ebb7f15c66e6c9c723", + "0x839ba65cffcd24cfffa7ab3b21faabe3c66d4c06324f07b2729c92f15cad34e474b0f0ddb16cd652870b26a756b731d3", + "0x87f1a3968afec354d92d77e2726b702847c6afcabb8438634f9c6f7766de4c1504317dc4fa9a4a735acdbf985e119564", + "0x91a8a7fd6542f3e0673f07f510d850864b34ac087eb7eef8845a1d14b2b1b651cbdc27fa4049bdbf3fea54221c5c8549", + "0xaef3cf5f5e3a2385ead115728d7059e622146c3457d266c612e778324b6e06fbfb8f98e076624d2f3ce1035d65389a07", + "0x819915d6232e95ccd7693fdd78d00492299b1983bc8f96a08dcb50f9c0a813ed93ae53c0238345d5bea0beda2855a913", + "0x8e9ba68ded0e94935131b392b28218315a185f63bf5e3c1a9a9dd470944509ca0ba8f6122265f8da851b5cc2abce68f1", + "0xb28468e9b04ee9d69003399a3cf4457c9bf9d59f36ab6ceeb8e964672433d06b58beeea198fedc7edbaa1948577e9fa2", + "0xa633005e2c9f2fd94c8bce2dd5bb708fe946b25f1ec561ae65e54e15cdd88dc339f1a083e01f0d39610c8fe24151aaf0", + "0x841d0031e22723f9328dd993805abd13e0c99b0f59435d2426246996b08d00ce73ab906f66c4eab423473b409e972ce0", + "0x85758d1b084263992070ec8943f33073a2d9b86a8606672550c17545507a5b3c88d87382b41916a87ee96ff55a7aa535", + "0x8581b06b0fc41466ef94a76a1d9fb8ae0edca6d018063acf6a8ca5f4b02d76021902feba58972415691b4bdbc33ae3b4", + "0x83539597ff5e327357ee62bc6bf8c0bcaec2f227c55c7c385a4806f0d37fb461f1690bad5066b8a5370950af32fafbef", + "0xaee3557290d2dc10827e4791d00e0259006911f3f3fce4179ed3c514b779160613eca70f720bff7804752715a1266ffa", + "0xb48d2f0c4e90fc307d5995464e3f611a9b0ef5fe426a289071f4168ed5cc4f8770c9332960c2ca5c8c427f40e6bb389f", + "0x847af8973b4e300bb06be69b71b96183fd1a0b9d51b91701bef6fcfde465068f1eb2b1503b07afda380f18d69de5c9e1", + "0xa70a6a80ce407f07804c0051ac21dc24d794b387be94eb24e1db94b58a78e1bcfb48cd0006db8fc1f9bedaece7a44fbe", + "0xb40e942b8fa5336910ff0098347df716bff9d1fa236a1950c16eeb966b3bc1a50b8f7b0980469d42e75ae13ced53cead", + "0xb208fabaa742d7db3148515330eb7a3577487845abdb7bd9ed169d0e081db0a5816595c33d375e56aeac5b51e60e49d3", + "0xb7c8194b30d3d6ef5ab66ec88ad7ebbc732a3b8a41731b153e6f63759a93f3f4a537eab9ad369705bd730184bdbbdc34", + "0x9280096445fe7394d04aa1bc4620c8f9296e991cc4d6c131bd703cb1cc317510e6e5855ac763f4d958c5edfe7eebeed7", + "0xabc2aa4616a521400af1a12440dc544e3c821313d0ab936c86af28468ef8bbe534837e364598396a81cf8d06274ed5a6", + "0xb18ca8a3325adb0c8c18a666d4859535397a1c3fe08f95eebfac916a7a99bbd40b3c37b919e8a8ae91da38bc00fa56c0", + "0x8a40c33109ecea2a8b3558565877082f79121a432c45ec2c5a5e0ec4d1c203a6788e6b69cb37f1fd5b8c9a661bc5476d", + "0x88c47301dd30998e903c84e0b0f2c9af2e1ce6b9f187dab03528d44f834dc991e4c86d0c474a2c63468cf4020a1e24a0", + "0x920c832853e6ab4c851eecfa9c11d3acc7da37c823be7aa1ab15e14dfd8beb5d0b91d62a30cec94763bd8e4594b66600", + "0x98e1addbe2a6b8edc7f12ecb9be81c3250aeeca54a1c6a7225772ca66549827c15f3950d01b8eb44aecb56fe0fff901a", + "0x8cfb0fa1068be0ec088402f5950c4679a2eb9218c729da67050b0d1b2d7079f3ddf4bf0f57d95fe2a8db04bc6bcdb20c", + "0xb70f381aafe336b024120453813aeab70baac85b9c4c0f86918797b6aee206e6ed93244a49950f3d8ec9f81f4ac15808", + "0xa4c8edf4aa33b709a91e1062939512419711c1757084e46f8f4b7ed64f8e682f4e78b7135920c12f0eb0422fe9f87a6a", + "0xb4817e85fd0752d7ebb662d3a51a03367a84bac74ebddfba0e5af5e636a979500f72b148052d333b3dedf9edd2b4031b", + "0xa87430169c6195f5d3e314ff2d1c2f050e766fd5d2de88f5207d72dba4a7745bb86d0baca6e9ae156582d0d89e5838c7", + "0x991b00f8b104566b63a12af4826b61ce7aa40f4e5b8fff3085e7a99815bdb4471b6214da1e480214fac83f86a0b93cc5", + "0xb39966e3076482079de0678477df98578377a094054960ee518ef99504d6851f8bcd3203e8da5e1d4f6f96776e1fe6eb", + "0xa448846d9dc2ab7a0995fa44b8527e27f6b3b74c6e03e95edb64e6baa4f1b866103f0addb97c84bef1d72487b2e21796", + "0x894bec21a453ae84b592286e696c35bc30e820e9c2fd3e63dd4fbe629e07df16439c891056070faa490155f255bf7187", + "0xa9ec652a491b11f6a692064e955f3f3287e7d2764527e58938571469a1e29b5225b9415bd602a45074dfbfe9c131d6ca", + "0xb39d37822e6cbe28244b5f42ce467c65a23765bd16eb6447c5b3e942278069793763483dafd8c4dd864f8917aad357fe", + "0x88dba51133f2019cb266641c56101e3e5987d3b77647a2e608b5ff9113dfc5f85e2b7c365118723131fbc0c9ca833c9c", + "0xb566579d904b54ecf798018efcb824dccbebfc6753a0fd2128ac3b4bd3b038c2284a7c782b5ca6f310eb7ea4d26a3f0a", + "0xa97a55c0a492e53c047e7d6f9d5f3e86fb96f3dddc68389c0561515343b66b4bc02a9c0d5722dff1e3445308240b27f7", + "0xa044028ab4bcb9e1a2b9b4ca4efbf04c5da9e4bf2fff0e8bd57aa1fc12a71e897999c25d9117413faf2f45395dee0f13", + "0xa78dc461decbeaeed8ebd0909369b491a5e764d6a5645a7dac61d3140d7dc0062526f777b0eb866bff27608429ebbdde", + "0xb2c2a8991f94c39ca35fea59f01a92cb3393e0eccb2476dfbf57261d406a68bd34a6cff33ed80209991688c183609ef4", + "0x84189eefb521aff730a4fd3fd5b10ddfd29f0d365664caef63bb015d07e689989e54c33c2141dd64427805d37a7e546e", + "0x85ac80bd734a52235da288ff042dea9a62e085928954e8eacd2c751013f61904ed110e5b3afe1ab770a7e6485efb7b5e", + "0x9183a560393dcb22d0d5063e71182020d0fbabb39e32493eeffeb808df084aa243eb397027f150b55a247d1ed0c8513e", + "0x81c940944df7ecc58d3c43c34996852c3c7915ed185d7654627f7af62abae7e0048dd444a6c09961756455000bd96d09", + "0xaa8c34e164019743fd8284b84f06c3b449aae7996e892f419ee55d82ad548cb300fd651de329da0384243954c0ef6a60", + "0x89a7b7bdfc7e300d06a14d463e573d6296d8e66197491900cc9ae49504c4809ff6e61b758579e9091c61085ba1237b83", + "0x878d21809ba540f50bd11f4c4d9590fb6f3ab9de5692606e6e2ef4ed9d18520119e385be5e1f4b3f2e2b09c319f0e8fc", + "0x8eb248390193189cf0355365e630b782cd15751e672dc478b39d75dc681234dcd9309df0d11f4610dbb249c1e6be7ef9", + "0xa1d7fb3aecb896df3a52d6bd0943838b13f1bd039c936d76d03de2044c371d48865694b6f532393b27fd10a4cf642061", + "0xa34bca58a24979be442238cbb5ece5bee51ae8c0794dd3efb3983d4db713bc6f28a96e976ac3bd9a551d3ed9ba6b3e22", + "0x817c608fc8cacdd178665320b5a7587ca21df8bdd761833c3018b967575d25e3951cf3d498a63619a3cd2ad4406f5f28", + "0x86c95707db0495689afd0c2e39e97f445f7ca0edffad5c8b4cacd1421f2f3cc55049dfd504f728f91534e20383955582", + "0x99c3b0bb15942c301137765d4e19502f65806f3b126dc01a5b7820c87e8979bce6a37289a8f6a4c1e4637227ad5bf3bf", + "0x8aa1518a80ea8b074505a9b3f96829f5d4afa55a30efe7b4de4e5dbf666897fdd2cf31728ca45921e21a78a80f0e0f10", + "0x8d74f46361c79e15128ac399e958a91067ef4cec8983408775a87eca1eed5b7dcbf0ddf30e66f51780457413496c7f07", + "0xa41cde4a786b55387458a1db95171aca4fd146507b81c4da1e6d6e495527c3ec83fc42fad1dfe3d92744084a664fd431", + "0x8c352852c906fae99413a84ad11701f93f292fbf7bd14738814f4c4ceab32db02feb5eb70bc73898b0bc724a39d5d017", + "0xa5993046e8f23b71ba87b7caa7ace2d9023fb48ce4c51838813174880d918e9b4d2b0dc21a2b9c6f612338c31a289df8", + "0x83576d3324bf2d8afbfb6eaecdc5d767c8e22e7d25160414924f0645491df60541948a05e1f4202e612368e78675de8a", + "0xb43749b8df4b15bc9a3697e0f1c518e6b04114171739ef1a0c9c65185d8ec18e40e6954d125cbc14ebc652cf41ad3109", + "0xb4eebd5d80a7327a040cafb9ccdb12b2dfe1aa86e6bc6d3ac8a57fadfb95a5b1a7332c66318ff72ba459f525668af056", + "0x9198be7f1d413c5029b0e1c617bcbc082d21abe2c60ec8ce9b54ca1a85d3dba637b72fda39dae0c0ae40d047eab9f55a", + "0x8d96a0232832e24d45092653e781e7a9c9520766c3989e67bbe86b3a820c4bf621ea911e7cd5270a4bfea78b618411f6", + "0x8d7160d0ea98161a2d14d46ef01dff72d566c330cd4fabd27654d300e1bc7644c68dc8eabf2a20a59bfe7ba276545f9b", + "0xabb60fce29dec7ba37e3056e412e0ec3e05538a1fc0e2c68877378c867605966108bc5742585ab6a405ce0c962b285b6", + "0x8fabffa3ed792f05e414f5839386f6449fd9f7b41a47595c5d71074bd1bb3784cc7a1a7e1ad6b041b455035957e5b2dc", + "0x90ff017b4804c2d0533b72461436b10603ab13a55f86fd4ec11b06a70ef8166f958c110519ca1b4cc7beba440729fe2d", + "0xb340cfd120f6a4623e3a74cf8c32bfd7cd61a280b59dfd17b15ca8fae4d82f64a6f15fbde4c02f424debc72b7db5fe67", + "0x871311c9c7220c932e738d59f0ecc67a34356d1429fe570ca503d340c9996cb5ee2cd188fad0e3bd16e4c468ec1dbebd", + "0xa772470262186e7b94239ba921b29f2412c148d6f97c4412e96d21e55f3be73f992f1ad53c71008f0558ec3f84e2b5a7", + "0xb2a897dcb7ffd6257f3f2947ec966f2077d57d5191a88840b1d4f67effebe8c436641be85524d0a21be734c63ab5965d", + "0xa044f6eacc48a4a061fa149500d96b48cbf14853469aa4d045faf3dca973be1bd4b4ce01646d83e2f24f7c486d03205d", + "0x981af5dc2daa73f7fa9eae35a93d81eb6edba4a7f673b55d41f6ecd87a37685d31bb40ef4f1c469b3d72f2f18b925a17", + "0x912d2597a07864de9020ac77083eff2f15ceb07600f15755aba61251e8ce3c905a758453b417f04d9c38db040954eb65", + "0x9642b7f6f09394ba5e0805734ef6702c3eddf9eea187ba98c676d5bbaec0e360e3e51dc58433aaa1e2da6060c8659cb7", + "0x8ab3836e0a8ac492d5e707d056310c4c8e0489ca85eb771bff35ba1d658360084e836a6f51bb990f9e3d2d9aeb18fbb5", + "0x879e058e72b73bb1f4642c21ffdb90544b846868139c6511f299aafe59c2d0f0b944dffc7990491b7c4edcd6a9889250", + "0xb9e60b737023f61479a4a8fd253ed0d2a944ea6ba0439bbc0a0d3abf09b0ad1f18d75555e4a50405470ae4990626f390", + "0xb9c2535d362796dcd673640a9fa2ebdaec274e6f8b850b023153b0a7a30fffc87f96e0b72696f647ebe7ab63099a6963", + "0x94aeff145386a087b0e91e68a84a5ede01f978f9dd9fe7bebca78941938469495dc30a96bba9508c0d017873aeea9610", + "0x98b179f8a3d9f0d0a983c30682dd425a2ddc7803be59bd626c623c8951a5179117d1d2a68254c95c9952989877d0ee55", + "0x889ecf5f0ee56938273f74eb3e9ecfb5617f04fb58e83fe4c0e4aef51615cf345bc56f3f61b17f6eed3249d4afd54451", + "0xa0f2b2c39bcea4b50883e2587d16559e246248a66ecb4a4b7d9ab3b51fb39fe98d83765e087eee37a0f86b0ba4144c02", + "0xb2a61e247ed595e8a3830f7973b07079cbda510f28ad8c78c220b26cb6acde4fbb5ee90c14a665f329168ee951b08cf0", + "0x95bd0fcfb42f0d6d8a8e73d7458498a85bcddd2fb132fd7989265648d82ac2707d6d203fac045504977af4f0a2aca4b7", + "0x843e5a537c298666e6cf50fcc044f13506499ef83c802e719ff2c90e85003c132024e04711be7234c04d4b0125512d5d", + "0xa46d1797c5959dcd3a5cfc857488f4d96f74277c3d13b98b133620192f79944abcb3a361d939a100187f1b0856eae875", + "0xa1c7786736d6707a48515c38660615fcec67eb8a2598f46657855215f804fd72ab122d17f94fcffad8893f3be658dca7", + "0xb23dc9e610abc7d8bd21d147e22509a0fa49db5be6ea7057b51aae38e31654b3aa044df05b94b718153361371ba2f622", + "0xb00cc8f257d659c22d30e6d641f79166b1e752ea8606f558e4cad6fc01532e8319ea4ee12265ba4140ac45aa4613c004", + "0xac7019af65221b0cc736287b32d7f1a3561405715ba9a6a122342e04e51637ba911c41573de53e4781f2230fdcb2475f", + "0x81a630bc41b3da8b3eb4bf56cba10cd9f93153c3667f009dc332287baeb707d505fb537e6233c8e53d299ec0f013290c", + "0xa6b7aea5c545bb76df0f230548539db92bc26642572cb7dd3d5a30edca2b4c386f44fc8466f056b42de2a452b81aff5b", + "0x8271624ff736b7b238e43943c81de80a1612207d32036d820c11fc830c737972ccc9c60d3c2359922b06652311e3c994", + "0x8a684106458cb6f4db478170b9ad595d4b54c18bf63b9058f095a2fa1b928c15101472c70c648873d5887880059ed402", + "0xa5cc3c35228122f410184e4326cf61a37637206e589fcd245cb5d0cec91031f8f7586b80503070840fdfd8ce75d3c88b", + "0x9443fc631aed8866a7ed220890911057a1f56b0afe0ba15f0a0e295ab97f604b134b1ed9a4245e46ee5f9a93aa74f731", + "0x984b6f7d79835dffde9558c6bb912d992ca1180a2361757bdba4a7b69dc74b056e303adc69fe67414495dd9c2dd91e64", + "0xb15a5c8cba5de080224c274d31c68ed72d2a7126d347796569aef0c4e97ed084afe3da4d4b590b9dda1a07f0c2ff3dfb", + "0x991708fe9650a1f9a4e43938b91d45dc68c230e05ee999c95dbff3bf79b1c1b2bb0e7977de454237c355a73b8438b1d9", + "0xb4f7edc7468b176a4a7c0273700c444fa95c726af6697028bed4f77eee887e3400f9c42ee15b782c0ca861c4c3b8c98a", + "0x8c60dcc16c51087eb477c13e837031d6c6a3dc2b8bf8cb43c23f48006bc7173151807e866ead2234b460c2de93b31956", + "0x83ad63e9c910d1fc44bc114accfb0d4d333b7ebe032f73f62d25d3e172c029d5e34a1c9d547273bf6c0fead5c8801007", + "0x85de73213cc236f00777560756bdbf2b16841ba4b55902cf2cad9742ecaf5d28209b012ceb41f337456dfeca93010cd7", + "0xa7561f8827ccd75b6686ba5398bb8fc3083351c55a589b18984e186820af7e275af04bcd4c28e1dc11be1e8617a0610b", + "0x88c0a4febd4068850557f497ea888035c7fc9f404f6cc7794e7cc8722f048ad2f249e7dc62743e7a339eb7473ad3b0cd", + "0x932b22b1d3e6d5a6409c34980d176feb85ada1bf94332ef5c9fc4d42b907dabea608ceef9b5595ef3feee195151f18d8", + "0xa2867bb3f5ab88fbdae3a16c9143ab8a8f4f476a2643c505bb9f37e5b1fd34d216cab2204c9a017a5a67b7ad2dda10e8", + "0xb573d5f38e4e9e8a3a6fd82f0880dc049efa492a946d00283019bf1d5e5516464cf87039e80aef667cb86fdea5075904", + "0xb948f1b5ab755f3f5f36af27d94f503b070696d793b1240c1bdfd2e8e56890d69e6904688b5f8ff5a4bdf5a6abfe195f", + "0x917eae95ebc4109a2e99ddd8fec7881d2f7aaa0e25fda44dec7ce37458c2ee832f1829db7d2dcfa4ca0f06381c7fe91d", + "0x95751d17ed00a3030bce909333799bb7f4ab641acf585807f355b51d6976dceee410798026a1a004ef4dcdff7ec0f5b8", + "0xb9b7bd266f449a79bbfe075e429613e76c5a42ac61f01c8f0bbbd34669650682efe01ff9dbbc400a1e995616af6aa278", + "0xac1722d097ce9cd7617161f8ec8c23d68f1fb1c9ca533e2a8b4f78516c2fd8fb38f23f834e2b9a03bb06a9d655693ca9", + "0xa7ad9e96ffd98db2ecdb6340c5d592614f3c159abfd832fe27ee9293519d213a578e6246aae51672ee353e3296858873", + "0x989b8814d5de7937c4acafd000eec2b4cd58ba395d7b25f98cafd021e8efa37029b29ad8303a1f6867923f5852a220eb", + "0xa5bfe6282c771bc9e453e964042d44eff4098decacb89aecd3be662ea5b74506e1357ab26f3527110ba377711f3c9f41", + "0x8900a7470b656639721d2abbb7b06af0ac4222ab85a1976386e2a62eb4b88bfb5b72cf7921ddb3cf3a395d7eeb192a2e", + "0x95a71b55cd1f35a438cf5e75f8ff11c5ec6a2ebf2e4dba172f50bfad7d6d5dca5de1b1afc541662c81c858f7604c1163", + "0x82b5d62fea8db8d85c5bc3a76d68dedd25794cf14d4a7bc368938ffca9e09f7e598fdad2a5aac614e0e52f8112ae62b9", + "0x997173f07c729202afcde3028fa7f52cefc90fda2d0c8ac2b58154a5073140683e54c49ed1f254481070d119ce0ce02a", + "0xaeffb91ccc7a72bbd6ffe0f9b99c9e66e67d59cec2e02440465e9636a613ab3017278cfa72ea8bc4aba9a8dc728cb367", + "0x952743b06e8645894aeb6440fc7a5f62dd3acf96dab70a51e20176762c9751ea5f2ba0b9497ccf0114dc4892dc606031", + "0x874c63baeddc56fbbca2ff6031f8634b745f6e34ea6791d7c439201aee8f08ef5ee75f7778700a647f3b21068513fce6", + "0x85128fec9c750c1071edfb15586435cc2f317e3e9a175bb8a9697bcda1eb9375478cf25d01e7fed113483b28f625122d", + "0x85522c9576fd9763e32af8495ae3928ed7116fb70d4378448926bc9790e8a8d08f98cf47648d7da1b6e40d6a210c7924", + "0x97d0f37a13cfb723b848099ca1c14d83e9aaf2f7aeb71829180e664b7968632a08f6a85f557d74b55afe6242f2a36e7c", + "0xabaa472d6ad61a5fccd1a57c01aa1bc081253f95abbcba7f73923f1f11c4e79b904263890eeb66926de3e2652f5d1c70", + "0xb3c04945ba727a141e5e8aec2bf9aa3772b64d8fd0e2a2b07f3a91106a95cbcb249adcd074cbe498caf76fffac20d4ef", + "0x82c46781a3d730d9931bcabd7434a9171372dde57171b6180e5516d4e68db8b23495c8ac3ab96994c17ddb1cf249b9fb", + "0xa202d8b65613c42d01738ccd68ed8c2dbc021631f602d53f751966e04182743ebc8e0747d600b8a8676b1da9ae7f11ab", + "0xae73e7256e9459db04667a899e0d3ea5255211fb486d084e6550b6dd64ca44af6c6b2d59d7aa152de9f96ce9b58d940d", + "0xb67d87b176a9722945ec7593777ee461809861c6cfd1b945dde9ee4ff009ca4f19cf88f4bbb5c80c9cbab2fe25b23ac8", + "0x8f0b7a317a076758b0dac79959ee4a06c08b07d0f10538a4b53d3da2eda16e2af26922feb32c090330dc4d969cf69bd3", + "0x90b36bf56adbd8c4b6cb32febc3a8d5f714370c2ac3305c10fa6d168dffb2a026804517215f9a2d4ec8310cdb6bb459b", + "0xaa80c19b0682ead69934bf18cf476291a0beddd8ef4ed75975d0a472e2ab5c70f119722a8574ae4973aceb733d312e57", + "0xa3fc9abb12574e5c28dcb51750b4339b794b8e558675eef7d26126edf1de920c35e992333bcbffcbf6a5f5c0d383ce62", + "0xa1573ff23ab972acdcd08818853b111fc757fdd35aa070186d3e11e56b172fb49d840bf297ac0dd222e072fc09f26a81", + "0x98306f2be4caa92c2b4392212d0cbf430b409b19ff7d5b899986613bd0e762c909fc01999aa94be3bd529d67f0113d7f", + "0x8c1fc42482a0819074241746d17dc89c0304a2acdae8ed91b5009e9e3e70ff725ba063b4a3e68fdce05b74f5180c545e", + "0xa6c6113ebf72d8cf3163b2b8d7f3fa24303b13f55752522c660a98cd834d85d8c79214d900fa649499365e2e7641f77a", + "0xab95eea424f8a2cfd9fb1c78bb724e5b1d71a0d0d1e4217c5d0f98b0d8bbd3f8400a2002abc0a0e4576d1f93f46fefad", + "0x823c5a4fd8cf4a75fdc71d5f2dd511b6c0f189b82affeacd2b7cfcad8ad1a5551227dcc9bfdb2e34b2097eaa00efbb51", + "0xb97314dfff36d80c46b53d87a61b0e124dc94018a0bb680c32765b9a2d457f833a7c42bbc90b3b1520c33a182580398d", + "0xb17566ee3dcc6bb3b004afe4c0136dfe7dd27df9045ae896dca49fb36987501ae069eb745af81ba3fc19ff037e7b1406", + "0xb0bdc0f55cfd98d331e3a0c4fbb776a131936c3c47c6bffdc3aaf7d8c9fa6803fbc122c2fefbb532e634228687d52174", + "0xaa5d9e60cc9f0598559c28bb9bdd52aa46605ab4ffe3d192ba982398e72cec9a2a44c0d0d938ce69935693cabc0887ea", + "0x802b6459d2354fa1d56c592ac1346c428dadea6b6c0a87bf7d309bab55c94e1cf31dd98a7a86bd92a840dd51f218b91b", + "0xa526914efdc190381bf1a73dd33f392ecf01350b9d3f4ae96b1b1c3d1d064721c7d6eec5788162c933245a3943f5ee51", + "0xb3b8fcf637d8d6628620a1a99dbe619eabb3e5c7ce930d6efd2197e261bf394b74d4e5c26b96c4b8009c7e523ccfd082", + "0x8f7510c732502a93e095aba744535f3928f893f188adc5b16008385fb9e80f695d0435bfc5b91cdad4537e87e9d2551c", + "0x97b90beaa56aa936c3ca45698f79273a68dd3ccd0076eab48d2a4db01782665e63f33c25751c1f2e070f4d1a8525bf96", + "0xb9fb798324b1d1283fdc3e48288e3861a5449b2ab5e884b34ebb8f740225324af86e4711da6b5cc8361c1db15466602f", + "0xb6d52b53cea98f1d1d4c9a759c25bf9d8a50b604b144e4912acbdbdc32aab8b9dbb10d64a29aa33a4f502121a6fb481c", + "0x9174ffff0f2930fc228f0e539f5cfd82c9368d26b074467f39c07a774367ff6cccb5039ac63f107677d77706cd431680", + "0xa33b6250d4ac9e66ec51c063d1a6a31f253eb29bbaed12a0d67e2eccfffb0f3a52750fbf52a1c2aaba8c7692346426e7", + "0xa97025fd5cbcebe8ef865afc39cd3ea707b89d4e765ec817fd021d6438e02fa51e3544b1fd45470c58007a08efac6edd", + "0xb32a78480edd9ff6ba2f1eec4088db5d6ceb2d62d7e59e904ecaef7bb4a2e983a4588e51692b3be76e6ffbc0b5f911a5", + "0xb5ab590ef0bb77191f00495b33d11c53c65a819f7d0c1f9dc4a2caa147a69c77a4fff7366a602d743ee1f395ce934c1e", + "0xb3fb0842f9441fb1d0ee0293b6efbc70a8f58d12d6f769b12872db726b19e16f0f65efbc891cf27a28a248b0ef9c7e75", + "0x9372ad12856fefb928ccb0d34e198df99e2f8973b07e9d417a3134d5f69e12e79ff572c4e03ccd65415d70639bc7c73e", + "0xaa8d6e83d09ce216bfe2009a6b07d0110d98cf305364d5529c170a23e693aabb768b2016befb5ada8dabdd92b4d012bb", + "0xa954a75791eeb0ce41c85200c3763a508ed8214b5945a42c79bfdcfb1ec4f86ad1dd7b2862474a368d4ac31911a2b718", + "0x8e2081cfd1d062fe3ab4dab01f68062bac802795545fede9a188f6c9f802cb5f884e60dbe866710baadbf55dc77c11a4", + "0xa2f06003b9713e7dd5929501ed485436b49d43de80ea5b15170763fd6346badf8da6de8261828913ee0dacd8ff23c0e1", + "0x98eecc34b838e6ffd1931ca65eec27bcdb2fdcb61f33e7e5673a93028c5865e0d1bf6d3bec040c5e96f9bd08089a53a4", + "0x88cc16019741b341060b95498747db4377100d2a5bf0a5f516f7dec71b62bcb6e779de2c269c946d39040e03b3ae12b7", + "0xad1135ccbc3019d5b2faf59a688eef2500697642be8cfbdf211a1ab59abcc1f24483e50d653b55ff1834675ac7b4978f", + "0xa946f05ed9972f71dfde0020bbb086020fa35b482cce8a4cc36dd94355b2d10497d7f2580541bb3e81b71ac8bba3c49f", + "0xa83aeed488f9a19d8cfd743aa9aa1982ab3723560b1cd337fc2f91ad82f07afa412b3993afb845f68d47e91ba4869840", + "0x95eebe006bfc316810cb71da919e5d62c2cebb4ac99d8e8ef67be420302320465f8b69873470982de13a7c2e23516be9", + "0xa55f8961295a11e91d1e5deadc0c06c15dacbfc67f04ccba1d069cba89d72aa3b3d64045579c3ea8991b150ac29366ae", + "0xb321991d12f6ac07a5de3c492841d1a27b0d3446082fbce93e7e1f9e8d8fe3b45d41253556261c21b70f5e189e1a7a6f", + "0xa0b0822f15f652ce7962a4f130104b97bf9529797c13d6bd8e24701c213cc37f18157bd07f3d0f3eae6b7cd1cb40401f", + "0x96e2fa4da378aa782cc2d5e6e465fc9e49b5c805ed01d560e9b98abb5c0de8b74a2e7bec3aa5e2887d25cccb12c66f0c", + "0x97e4ab610d414f9210ed6f35300285eb3ccff5b0b6a95ed33425100d7725e159708ea78704497624ca0a2dcabce3a2f9", + "0x960a375b17bdb325761e01e88a3ea57026b2393e1d887b34b8fa5d2532928079ce88dc9fd06a728b26d2bb41b12b9032", + "0x8328a1647398e832aadc05bd717487a2b6fcdaa0d4850d2c4da230c6a2ed44c3e78ec4837b6094f3813f1ee99414713f", + "0xaa283834ebd18e6c99229ce4b401eda83f01d904f250fedd4e24f1006f8fa0712a6a89a7296a9bf2ce8de30e28d1408e", + "0xb29e097f2caadae3e0f0ae3473c072b0cd0206cf6d2e9b22c1a5ad3e07d433e32bd09ed1f4e4276a2da4268633357b7f", + "0x9539c5cbba14538b2fe077ecf67694ef240da5249950baaabea0340718b882a966f66d97f08556b08a4320ceb2cc2629", + "0xb4529f25e9b42ae8cf8338d2eface6ba5cd4b4d8da73af502d081388135c654c0b3afb3aa779ffc80b8c4c8f4425dd2b", + "0x95be0739c4330619fbe7ee2249c133c91d6c07eab846c18c5d6c85fc21ac5528c5d56dcb0145af68ed0c6a79f68f2ccd", + "0xac0c83ea802227bfc23814a24655c9ff13f729619bcffdb487ccbbf029b8eaee709f8bddb98232ef33cd70e30e45ca47", + "0xb503becb90acc93b1901e939059f93e671900ca52c6f64ae701d11ac891d3a050b505d89324ce267bc43ab8275da6ffe", + "0x98e3811b55b1bacb70aa409100abb1b870f67e6d059475d9f278c751b6e1e2e2d6f2e586c81a9fb6597fda06e7923274", + "0xb0b0f61a44053fa6c715dbb0731e35d48dba257d134f851ee1b81fd49a5c51a90ebf5459ec6e489fce25da4f184fbdb1", + "0xb1d2117fe811720bb997c7c93fe9e4260dc50fca8881b245b5e34f724aaf37ed970cdad4e8fcb68e05ac8cf55a274a53", + "0xa10f502051968f14b02895393271776dee7a06db9de14effa0b3471825ba94c3f805302bdddac4d397d08456f620999d", + "0xa3dbad2ef060ae0bb7b02eaa4a13594f3f900450faa1854fc09620b01ac94ab896321dfb1157cf2374c27e5718e8026a", + "0xb550fdec503195ecb9e079dcdf0cad559d64d3c30818ef369b4907e813e689da316a74ad2422e391b4a8c2a2bef25fc0", + "0xa25ba865e2ac8f28186cea497294c8649a201732ecb4620c4e77b8e887403119910423df061117e5f03fc5ba39042db1", + "0xb3f88174e03fdb443dd6addd01303cf88a4369352520187c739fc5ae6b22fa99629c63c985b4383219dab6acc5f6f532", + "0x97a7503248e31e81b10eb621ba8f5210c537ad11b539c96dfb7cf72b846c7fe81bd7532c5136095652a9618000b7f8d3", + "0xa8bcdc1ce5aa8bfa683a2fc65c1e79de8ff5446695dcb8620f7350c26d2972a23da22889f9e2b1cacb3f688c6a2953dc", + "0x8458c111df2a37f5dd91a9bee6c6f4b79f4f161c93fe78075b24a35f9817da8dde71763218d627917a9f1f0c4709c1ed", + "0xac5f061a0541152b876cbc10640f26f1cc923c9d4ae1b6621e4bb3bf2cec59bbf87363a4eb72fb0e5b6d4e1c269b52d5", + "0xa9a25ca87006e8a9203cbb78a93f50a36694aa4aad468b8d80d3feff9194455ca559fcc63838128a0ab75ad78c07c13a", + "0xa450b85f5dfffa8b34dfd8bc985f921318efacf8857cf7948f93884ba09fb831482ee90a44224b1a41e859e19b74962f", + "0x8ed91e7f92f5c6d7a71708b6132f157ac226ecaf8662af7d7468a4fa25627302efe31e4620ad28719318923e3a59bf82", + "0xab524165fd4c71b1fd395467a14272bd2b568592deafa039d8492e9ef36c6d3f96927c95c72d410a768dc0b6d1fbbc9b", + "0xb662144505aa8432c75ffb8d10318526b6d5777ac7af9ebfad87d9b0866c364f7905a6352743bd8fd79ffd9d5dd4f3e6", + "0xa48f1677550a5cd40663bb3ba8f84caaf8454f332d0ceb1d94dbea52d0412fe69c94997f7749929712fd3995298572f7", + "0x8391cd6e2f6b0c242de1117a612be99776c3dc95cb800b187685ea5bf7e2722275eddb79fd7dfc8be8e389c4524cdf70", + "0x875d3acb9af47833b72900bc0a2448999d638f153c5e97e8a14ec02d0c76f6264353a7e275e1f1a5855daced523d243b", + "0x91f1823657d30b59b2f627880a9a9cb530f5aca28a9fd217fe6f2f5133690dfe7ad5a897872e400512db2e788b3f7628", + "0xad3564332aa56cea84123fc7ca79ea70bb4fef2009fa131cb44e4b15e8613bd11ca1d83b9d9bf456e4b7fee9f2e8b017", + "0x8c530b84001936d5ab366c84c0b105241a26d1fb163669f17c8f2e94776895c2870edf3e1bc8ccd04d5e65531471f695", + "0x932d01fa174fdb0c366f1230cffde2571cc47485f37f23ba5a1825532190cc3b722aeb1f15aed62cf83ccae9403ba713", + "0x88b28c20585aca50d10752e84b901b5c2d58efef5131479fbbe53de7bce2029e1423a494c0298e1497669bd55be97a5d", + "0xb914148ca717721144ebb3d3bf3fcea2cd44c30c5f7051b89d8001502f3856fef30ec167174d5b76265b55d70f8716b5", + "0x81d0173821c6ddd2a068d70766d9103d1ee961c475156e0cbd67d54e668a796310474ef698c7ab55abe6f2cf76c14679", + "0x8f28e8d78e2fe7fa66340c53718e0db4b84823c8cfb159c76eac032a62fb53da0a5d7e24ca656cf9d2a890cb2a216542", + "0x8a26360335c73d1ab51cec3166c3cf23b9ea51e44a0ad631b0b0329ef55aaae555420348a544e18d5760969281759b61", + "0x94f326a32ed287545b0515be9e08149eb0a565025074796d72387cc3a237e87979776410d78339e23ef3172ca43b2544", + "0xa785d2961a2fa5e70bffa137858a92c48fe749fee91b02599a252b0cd50d311991a08efd7fa5e96b78d07e6e66ffe746", + "0x94af9030b5ac792dd1ce517eaadcec1482206848bea4e09e55cc7f40fd64d4c2b3e9197027c5636b70d6122c51d2235d", + "0x9722869f7d1a3992850fe7be405ec93aa17dc4d35e9e257d2e469f46d2c5a59dbd504056c85ab83d541ad8c13e8bcd54", + "0xb13c4088b61a06e2c03ac9813a75ff1f68ffdfee9df6a8f65095179a475e29cc49119cad2ce05862c3b1ac217f3aace9", + "0x8c64d51774753623666b10ca1b0fe63ae42f82ed6aa26b81dc1d48c86937c5772eb1402624c52a154b86031854e1fb9f", + "0xb47e4df18002b7dac3fee945bf9c0503159e1b8aafcce2138818e140753011b6d09ef1b20894e08ba3006b093559061b", + "0x93cb5970076522c5a0483693f6a35ffd4ea2aa7aaf3730c4eccd6af6d1bebfc1122fc4c67d53898ae13eb6db647be7e2", + "0xa68873ef80986795ea5ed1a597d1cd99ed978ec25e0abb57fdcc96e89ef0f50aeb779ff46e3dce21dc83ada3157a8498", + "0x8cab67f50949cc8eee6710e27358aea373aae3c92849f8f0b5531c080a6300cdf2c2094fe6fecfef6148de0d28446919", + "0x993e932bcb616dbaa7ad18a4439e0565211d31071ef1b85a0627db74a05d978c60d507695eaeea5c7bd9868a21d06923", + "0xacdadff26e3132d9478a818ef770e9fa0d2b56c6f5f48bd3bd674436ccce9bdfc34db884a73a30c04c5f5e9764cb2218", + "0xa0d3e64c9c71f84c0eef9d7a9cb4fa184224b969db5514d678e93e00f98b41595588ca802643ea225512a4a272f5f534", + "0x91c9140c9e1ba6e330cb08f6b2ce4809cd0d5a0f0516f70032bf30e912b0ed684d07b413b326ab531ee7e5b4668c799b", + "0x87bc2ee7a0c21ba8334cd098e35cb703f9af57f35e091b8151b9b63c3a5b0f89bd7701dbd44f644ea475901fa6d9ef08", + "0x9325ccbf64bf5d71b303e31ee85d486298f9802c5e55b2c3d75427097bf8f60fa2ab4fcaffa9b60bf922c3e24fbd4b19", + "0x95d0506e898318f3dc8d28d16dfd9f0038b54798838b3c9be2a2ae3c2bf204eb496166353fc042220b0bd4f6673b9285", + "0x811de529416331fe9c416726d45df9434c29dcd7e949045eb15740f47e97dde8f31489242200e19922cac2a8b7c6fd1f", + "0xade632d04a4c8bbab6ca7df370b2213cb9225023e7973f0e29f4f5e52e8aeaabc65171306bbdd12a67b195dfbb96d48f", + "0x88b7f029e079b6ae956042c0ea75d53088c5d0efd750dd018adaeacf46be21bf990897c58578c491f41afd3978d08073", + "0x91f477802de507ffd2be3f4319903119225b277ad24f74eb50f28b66c14d32fae53c7edb8c7590704741af7f7f3e3654", + "0x809838b32bb4f4d0237e98108320d4b079ee16ed80c567e7548bd37e4d7915b1192880f4812ac0e00476d246aec1dbc8", + "0x84183b5fc4a7997a8ae5afedb4d21dce69c480d5966b5cbdafd6dd10d29a9a6377f3b90ce44da0eb8b176ac3af0253bb", + "0x8508abbf6d3739a16b9165caf0f95afb3b3ac1b8c38d6d374cf0c91296e2c1809a99772492b539cda184510bce8a0271", + "0x8722054e59bab2062e6419a6e45fc803af77fde912ef2cd23055ad0484963de65a816a2debe1693d93c18218d2b8e81a", + "0x8e895f80e485a7c4f56827bf53d34b956281cdc74856c21eb3b51f6288c01cc3d08565a11cc6f3e2604775885490e8c5", + "0xafc92714771b7aa6e60f3aee12efd9c2595e9659797452f0c1e99519f67c8bc3ac567119c1ddfe82a3e961ee9defea9a", + "0x818ff0fd9cefd32db87b259e5fa32967201016fc02ef44116cdca3c63ce5e637756f60477a408709928444a8ad69c471", + "0x8251e29af4c61ae806fc5d032347fb332a94d472038149225298389495139ce5678fae739d02dfe53a231598a992e728", + "0xa0ea39574b26643f6f1f48f99f276a8a64b5481989cfb2936f9432a3f8ef5075abfe5c067dc5512143ce8bf933984097", + "0xaf67a73911b372bf04e57e21f289fc6c3dfac366c6a01409b6e76fea4769bdb07a6940e52e8d7d3078f235c6d2f632c6", + "0xb5291484ef336024dd2b9b4cf4d3a6b751133a40656d0a0825bcc6d41c21b1c79cb50b0e8f4693f90c29c8f4358641f9", + "0x8bc0d9754d70f2cb9c63f991902165a87c6535a763d5eece43143b5064ae0bcdce7c7a8f398f2c1c29167b2d5a3e6867", + "0x8d7faff53579ec8f6c92f661c399614cc35276971752ce0623270f88be937c414eddcb0997e14724a783905a026c8883", + "0x9310b5f6e675fdf60796f814dbaa5a6e7e9029a61c395761e330d9348a7efab992e4e115c8be3a43d08e90d21290c892", + "0xb5eb4f3eb646038ad2a020f0a42202532d4932e766da82b2c1002bf9c9c2e5336b54c8c0ffcc0e02d19dde2e6a35b6cc", + "0x91dabfd30a66710f1f37a891136c9be1e23af4abf8cb751f512a40c022a35f8e0a4fb05b17ec36d4208de02d56f0d53a", + "0xb3ded14e82d62ac7a5a036122a62f00ff8308498f3feae57d861babaff5a6628d43f0a0c5fc903f10936bcf4e2758ceb", + "0xa88e8348fed2b26acca6784d19ef27c75963450d99651d11a950ea81d4b93acd2c43e0ecce100eaf7e78508263d5baf3", + "0xb1f5bbf7c4756877b87bb42163ac570e08c6667c4528bf68b5976680e19beeff7c5effd17009b0718797077e2955457a", + "0xad2e7b516243f915d4d1415326e98b1a7390ae88897d0b03b66c2d9bd8c3fba283d7e8fe44ed3333296a736454cef6d8", + "0x8f82eae096d5b11f995de6724a9af895f5e1c58d593845ad16ce8fcae8507e0d8e2b2348a0f50a1f66a17fd6fac51a5c", + "0x890e4404d0657c6c1ee14e1aac132ecf7a568bb3e04137b85ac0f84f1d333bd94993e8750f88eee033a33fb00f85dcc7", + "0x82ac7d3385e035115f1d39a99fc73e5919de44f5e6424579776d118d711c8120b8e5916372c6f27bed4cc64cac170b6c", + "0x85ee16d8901c272cfbbe966e724b7a891c1bd5e68efd5d863043ad8520fc409080af61fd726adc680b3f1186fe0ac8b8", + "0x86dc564c9b545567483b43a38f24c41c6551a49cabeebb58ce86404662a12dbfafd0778d30d26e1c93ce222e547e3898", + "0xa29f5b4522db26d88f5f95f18d459f8feefab02e380c2edb65aa0617a82a3c1a89474727a951cef5f15050bcf7b380fb", + "0xa1ce039c8f6cac53352899edb0e3a72c76da143564ad1a44858bd7ee88552e2fe6858d1593bbd74aeee5a6f8034b9b9d", + "0x97f10d77983f088286bd7ef3e7fdd8fa275a56bec19919adf33cf939a90c8f2967d2b1b6fc51195cb45ad561202a3ed7", + "0xa25e2772e8c911aaf8712bdac1dd40ee061c84d3d224c466cfaae8e5c99604053f940cde259bd1c3b8b69595781dbfec", + "0xb31bb95a0388595149409c48781174c340960d59032ab2b47689911d03c68f77a2273576fbe0c2bf4553e330656058c7", + "0xb8b2e9287ad803fb185a13f0d7456b397d4e3c8ad5078f57f49e8beb2e85f661356a3392dbd7bcf6a900baa5582b86a1", + "0xa3d0893923455eb6e96cc414341cac33d2dbc88fba821ac672708cce131761d85a0e08286663a32828244febfcae6451", + "0x82310cb42f647d99a136014a9f881eb0b9791efd2e01fc1841907ad3fc8a9654d3d1dab6689c3607214b4dc2aca01cee", + "0x874022d99c16f60c22de1b094532a0bc6d4de700ad01a31798fac1d5088b9a42ad02bef8a7339af7ed9c0d4f16b186ee", + "0x94981369e120265aed40910eebc37eded481e90f4596b8d57c3bec790ab7f929784bd33ddd05b7870aad6c02e869603b", + "0xa4f1f50e1e2a73f07095e0dd31cb45154f24968dae967e38962341c1241bcd473102fff1ff668b20c6547e9732d11701", + "0xae2328f3b0ad79fcda807e69a1b5278145225083f150f67511dafc97e079f860c3392675f1752ae7e864c056e592205b", + "0x875d8c971e593ca79552c43d55c8c73b17cd20c81ff2c2fed1eb19b1b91e4a3a83d32df150dbfd5db1092d0aebde1e1f", + "0xadd2e80aa46aae95da73a11f130f4bda339db028e24c9b11e5316e75ba5e63bc991d2a1da172c7c8e8fee038baae3433", + "0xb46dbe1cb3424002aa7de51e82f600852248e251465c440695d52538d3f36828ff46c90ed77fc1d11534fe3c487df8ef", + "0xa5e5045d28b4e83d0055863c30c056628c58d4657e6176fd0536f5933f723d60e851bb726d5bf3c546b8ce4ac4a57ef8", + "0x91fec01e86dd1537e498fff7536ea3ca012058b145f29d9ada49370cd7b7193ac380e116989515df1b94b74a55c45df3", + "0xa7428176d6918cd916a310bdc75483c72de660df48cac4e6e7478eef03205f1827ea55afc0df5d5fa7567d14bbea7fc9", + "0x851d89bef45d9761fe5fdb62972209335193610015e16a675149519f9911373bac0919add226ef118d9f3669cfdf4734", + "0xb74acf5c149d0042021cb2422ea022be4c4f72a77855f42393e71ffd12ebb3eec16bdf16f812159b67b79a9706e7156d", + "0x99f35dce64ec99aa595e7894b55ce7b5a435851b396e79036ffb249c28206087db4c85379df666c4d95857db02e21ff9", + "0xb6b9a384f70db9e298415b8ab394ee625dafff04be2886476e59df8d052ca832d11ac68a9b93fba7ab055b7bc36948a4", + "0x898ee4aefa923ffec9e79f2219c7389663eb11eb5b49014e04ed4a336399f6ea1691051d86991f4c46ca65bcd4fdf359", + "0xb0f948217b0d65df7599a0ba4654a5e43c84db477936276e6f11c8981efc6eaf14c90d3650107ed4c09af4cc8ec11137", + "0xaa6286e27ac54f73e63dbf6f41865dd94d24bc0cf732262fcaff67319d162bb43af909f6f8ee27b1971939cfbba08141", + "0x8bca7cdf730cf56c7b2c8a2c4879d61361a6e1dba5a3681a1a16c17a56e168ace0e99cf0d15826a1f5e67e6b8a8a049a", + "0xa746d876e8b1ce225fcafca603b099b36504846961526589af977a88c60d31ba2cc56e66a3dec8a77b3f3531bf7524c9", + "0xa11e2e1927e6704cdb8874c75e4f1842cef84d7d43d7a38e339e61dc8ba90e61bbb20dd3c12e0b11d2471d58eed245be", + "0xa36395e22bc1d1ba8b0459a235203177737397da5643ce54ded3459d0869ff6d8d89f50c73cb62394bf66a959cde9b90", + "0x8b49f12ba2fdf9aca7e5f81d45c07d47f9302a2655610e7634d1e4bd16048381a45ef2c95a8dd5b0715e4b7cf42273af", + "0x91cffa2a17e64eb7f76bccbe4e87280ee1dd244e04a3c9eac12e15d2d04845d876eb24fe2ec6d6d266cce9efb281077f", + "0xa6b8afabf65f2dee01788114e33a2f3ce25376fb47a50b74da7c3c25ff1fdc8aa9f41307534abbf48acb6f7466068f69", + "0x8d13db896ccfea403bd6441191995c1a65365cab7d0b97fbe9526da3f45a877bd1f4ef2edef160e8a56838cd1586330e", + "0x98c717de9e01bef8842c162a5e757fe8552d53269c84862f4d451e7c656ae6f2ae473767b04290b134773f63be6fdb9d", + "0x8c2036ace1920bd13cf018e82848c49eb511fad65fd0ff51f4e4b50cf3bfc294afb63cba682c16f52fb595a98fa84970", + "0xa3520fdff05dbad9e12551b0896922e375f9e5589368bcb2cc303bde252743b74460cb5caf99629325d3620f13adc796", + "0x8d4f83a5bfec05caf5910e0ce538ee9816ee18d0bd44c1d0da2a87715a23cd2733ad4d47552c6dc0eb397687d611dd19", + "0xa7b39a0a6a02823452d376533f39d35029867b3c9a6ad6bca181f18c54132d675613a700f9db2440fb1b4fa13c8bf18a", + "0x80bcb114b2544b80f404a200fc36860ed5e1ad31fe551acd4661d09730c452831751baa9b19d7d311600d267086a70bc", + "0x90dcce03c6f88fc2b08f2b42771eedde90cc5330fe0336e46c1a7d1b5a6c1641e5fcc4e7b3d5db00bd8afca9ec66ed81", + "0xaec15f40805065c98e2965b1ae12a6c9020cfdb094c2d0549acfc7ea2401a5fb48d3ea7d41133cf37c4e096e7ff53eb9", + "0x80e129b735dba49fa627a615d6c273119acec8e219b2f2c4373a332b5f98d66cbbdd688dfbe72a8f8bfefaccc02c50c1", + "0xa9b596da3bdfe23e6799ece5f7975bf7a1979a75f4f546deeaf8b34dfe3e0d623217cb4cf4ccd504cfa3625b88cd53f1", + "0xabcbbb70b16f6e517c0ab4363ab76b46e4ff58576b5f8340e5c0e8cc0e02621b6e23d742d73b015822a238b17cfd7665", + "0xa046937cc6ea6a2e1adae543353a9fe929c1ae4ad655be1cc051378482cf88b041e28b1e9a577e6ccff2d3570f55e200", + "0x831279437282f315e65a60184ef158f0a3dddc15a648dc552bdc88b3e6fe8288d3cfe9f0031846d81350f5e7874b4b33", + "0x993d7916fa213c6d66e7c4cafafc1eaec9a2a86981f91c31eb8a69c5df076c789cbf498a24c84e0ee77af95b42145026", + "0x823907a3b6719f8d49b3a4b7c181bd9bb29fcf842d7c70660c4f351852a1e197ca46cf5e879b47fa55f616fa2b87ce5e", + "0x8d228244e26132b234930ee14c75d88df0943cdb9c276a8faf167d259b7efc1beec2a87c112a6c608ad1600a239e9aae", + "0xab6e55766e5bfb0cf0764ed909a8473ab5047d3388b4f46faeba2d1425c4754c55c6daf6ad4751e634c618b53e549529", + "0xab0cab6860e55a84c5ad2948a7e0989e2b4b1fd637605634b118361497332df32d9549cb854b2327ca54f2bcb85eed8f", + "0xb086b349ae03ef34f4b25a57bcaa5d1b29bd94f9ebf87e22be475adfe475c51a1230c1ebe13506cb72c4186192451658", + "0x8a0b49d8a254ca6d91500f449cbbfbb69bb516c6948ac06808c65595e46773e346f97a5ce0ef7e5a5e0de278af22709c", + "0xac49de11edaaf04302c73c578cc0824bdd165c0d6321be1c421c1950e68e4f3589aa3995448c9699e93c6ebae8803e27", + "0x884f02d841cb5d8f4c60d1402469216b114ab4e93550b5bc1431756e365c4f870a9853449285384a6fa49e12ce6dc654", + "0xb75f3a28fa2cc8d36b49130cb7448a23d73a7311d0185ba803ad55c8219741d451c110f48b786e96c728bc525903a54f", + "0x80ae04dbd41f4a35e33f9de413b6ad518af0919e5a30cb0fa1b061b260420780bb674f828d37fd3b52b5a31673cbd803", + "0xb9a8011eb5fcea766907029bf743b45262db3e49d24f84503687e838651ed11cb64c66281e20a0ae9f6aa51acc552263", + "0x90bfdd75e2dc9cf013e22a5d55d2d2b8a754c96103a17524488e01206e67f8b6d52b1be8c4e3d5307d4fe06d0e51f54c", + "0xb4af353a19b06203a815ec43e79a88578cc678c46f5a954b85bc5c53b84059dddba731f3d463c23bfd5273885c7c56a4", + "0xaa125e96d4553b64f7140e5453ff5d2330318b69d74d37d283e84c26ad672fa00e3f71e530eb7e28be1e94afb9c4612e", + "0xa18e060aee3d49cde2389b10888696436bb7949a79ca7d728be6456a356ea5541b55492b2138da90108bd1ce0e6f5524", + "0x93e55f92bdbccc2de655d14b1526836ea2e52dba65eb3f87823dd458a4cb5079bf22ce6ef625cb6d6bfdd0995ab9a874", + "0x89f5a683526b90c1c3ceebbb8dc824b21cff851ce3531b164f6626e326d98b27d3e1d50982e507d84a99b1e04e86a915", + "0x83d1c38800361633a3f742b1cb2bfc528129496e80232611682ddbe403e92c2ac5373aea0bca93ecb5128b0b2b7a719e", + "0x8ecba560ac94905e19ce8d9c7af217bf0a145d8c8bd38e2db82f5e94cc3f2f26f55819176376b51f154b4aab22056059", + "0xa7e2a4a002b60291924850642e703232994acb4cfb90f07c94d1e0ecd2257bb583443283c20fc6017c37e6bfe85b7366", + "0x93ed7316fa50b528f1636fc6507683a672f4f4403e55e94663f91221cc198199595bd02eef43d609f451acc9d9b36a24", + "0xa1220a8ebc5c50ceed76a74bc3b7e0aa77f6884c71b64b67c4310ac29ce5526cb8992d6abc13ef6c8413ce62486a6795", + "0xb2f6eac5c869ad7f4a25161d3347093e2f70e66cd925032747e901189355022fab3038bca4d610d2f68feb7e719c110b", + "0xb703fa11a4d511ca01c7462979a94acb40b5d933759199af42670eb48f83df202fa0c943f6ab3b4e1cc54673ea3aab1e", + "0xb5422912afbfcb901f84791b04f1ddb3c3fbdc76d961ee2a00c5c320e06d3cc5b5909c3bb805df66c5f10c47a292b13d", + "0xad0934368da823302e1ac08e3ede74b05dfdbfffca203e97ffb0282c226814b65c142e6e15ec1e754518f221f01b30f7", + "0xa1dd302a02e37df15bf2f1147efe0e3c06933a5a767d2d030e1132f5c3ce6b98e216b6145eb39e1e2f74e76a83165b8d", + "0xa346aab07564432f802ae44738049a36f7ca4056df2d8f110dbe7fef4a3e047684dea609b2d03dc6bf917c9c2a47608f", + "0xb96c5f682a5f5d02123568e50f5d0d186e4b2c4c9b956ec7aabac1b3e4a766d78d19bd111adb5176b898e916e49be2aa", + "0x8a96676d56876fc85538db2e806e1cba20fd01aeb9fa3cb43ca6ca94a2c102639f65660db330e5d74a029bb72d6a0b39", + "0xab0048336bd5c3def1a4064eadd49e66480c1f2abb4df46e03afbd8a3342c2c9d74ee35d79f08f4768c1646681440984", + "0x888427bdf76caec90814c57ee1c3210a97d107dd88f7256f14f883ad0f392334b82be11e36dd8bfec2b37935177c7831", + "0xb622b282becf0094a1916fa658429a5292ba30fb48a4c8066ce1ddcefb71037948262a01c95bab6929ed3a76ba5db9fe", + "0xb5b9e005c1f456b6a368a3097634fb455723abe95433a186e8278dceb79d4ca2fbe21f8002e80027b3c531e5bf494629", + "0xa3c6707117a1e48697ed41062897f55d8119403eea6c2ee88f60180f6526f45172664bfee96bf61d6ec0b7fbae6aa058", + "0xb02a9567386a4fbbdb772d8a27057b0be210447348efe6feb935ceec81f361ed2c0c211e54787dc617cdffed6b4a6652", + "0xa9b8364e40ef15c3b5902e5534998997b8493064fa2bea99600def58279bb0f64574c09ba11e9f6f669a8354dd79dc85", + "0x9998a2e553a9aa9a206518fae2bc8b90329ee59ab23005b10972712389f2ec0ee746033c733092ffe43d73d33abbb8ef", + "0x843a4b34d9039bf79df96d79f2d15e8d755affb4d83d61872daf540b68c0a3888cf8fc00d5b8b247b38524bcb3b5a856", + "0x84f7128920c1b0bb40eee95701d30e6fc3a83b7bb3709f16d97e72acbb6057004ee7ac8e8f575936ca9dcb7866ab45f7", + "0x918d3e2222e10e05edb34728162a899ad5ada0aaa491aeb7c81572a9c0d506e31d5390e1803a91ff3bd8e2bb15d47f31", + "0x9442d18e2489613a7d47bb1cb803c8d6f3259d088cd079460976d87f7905ee07dea8f371b2537f6e1d792d36d7e42723", + "0xb491976970fe091995b2ed86d629126523ccf3e9daf8145302faca71b5a71a5da92e0e05b62d7139d3efac5c4e367584", + "0xaa628006235dc77c14cef4c04a308d66b07ac92d377df3de1a2e6ecfe3144f2219ad6d7795e671e1cb37a3641910b940", + "0x99d386adaea5d4981d7306feecac9a555b74ffdc218c907c5aa7ac04abaead0ec2a8237300d42a3fbc464673e417ceed", + "0x8f78e8b1556f9d739648ea3cab9606f8328b52877fe72f9305545a73b74d49884044ba9c1f1c6db7d9b7c7b7c661caba", + "0x8fb357ae49932d0babdf74fc7aa7464a65d3b6a2b3acf4f550b99601d3c0215900cfd67f2b6651ef94cfc323bac79fae", + "0x9906f2fa25c0290775aa001fb6198113d53804262454ae8b83ef371b5271bde189c0460a645829cb6c59f9ee3a55ce4d", + "0x8f4379b3ebb50e052325b27655ca6a82e6f00b87bf0d2b680d205dd2c7afdc9ff32a9047ae71a1cdf0d0ce6b9474d878", + "0xa85534e88c2bd43c043792eaa75e50914b21741a566635e0e107ae857aed0412035f7576cf04488ade16fd3f35fdbb87", + "0xb4ce93199966d3c23251ca7f28ec5af7efea1763d376b0385352ffb2e0a462ef95c69940950278cf0e3dafd638b7bd36", + "0xb10cb3d0317dd570aa73129f4acf63c256816f007607c19b423fb42f65133ce21f2f517e0afb41a5378cccf893ae14d0", + "0xa9b231c9f739f7f914e5d943ed9bff7eba9e2c333fbd7c34eb1648a362ee01a01af6e2f7c35c9fe962b11152cddf35de", + "0x99ff6a899e156732937fb81c0cced80ae13d2d44c40ba99ac183aa246103b31ec084594b1b7feb96da58f4be2dd5c0ed", + "0x8748d15d18b75ff2596f50d6a9c4ce82f61ecbcee123a6ceae0e43cab3012a29b6f83cf67b48c22f6f9d757c6caf76b2", + "0xb88ab05e4248b7fb634cf640a4e6a945d13e331237410f7217d3d17e3e384ddd48897e7a91e4516f1b9cbd30f35f238b", + "0x8d826deaeeb84a3b2d2c04c2300ca592501f992810582d6ae993e0d52f6283a839dba66c6c72278cff5871802b71173b", + "0xb36fed027c2f05a5ef625ca00b0364b930901e9e4420975b111858d0941f60e205546474bb25d6bfa6928d37305ae95f", + "0xaf2fcfc6b87967567e8b8a13a4ed914478185705724e56ce68fb2df6d1576a0cf34a61e880997a0d35dc2c3276ff7501", + "0xac351b919cd1fbf106feb8af2c67692bfcddc84762d18cea681cfa7470a5644839caace27efee5f38c87d3df306f4211", + "0x8d6665fb1d4d8d1fa23bd9b8a86e043b8555663519caac214d1e3e3effbc6bee7f2bcf21e645f77de0ced279d69a8a8b", + "0xa9fc1c2061756b2a1a169c1b149f212ff7f0d2488acd1c5a0197eba793cffa593fc6d1d1b40718aa75ca3ec77eff10e1", + "0xaff64f0fa009c7a6cf0b8d7a22ddb2c8170c3cb3eec082e60d5aadb00b0040443be8936d728d99581e33c22178c41c87", + "0x82e0b181adc5e3b1c87ff8598447260e839d53debfae941ebea38265575546c3a74a14b4325a030833a62ff6c52d9365", + "0xb7ad43cbb22f6f892c2a1548a41dc120ab1f4e1b8dea0cb6272dd9cb02054c542ecabc582f7e16de709d48f5166cae86", + "0x985e0c61094281532c4afb788ecb2dfcba998e974b5d4257a22040a161883908cdd068fe80f8eb49b8953cfd11acf43a", + "0xae46895c6d67ea6d469b6c9c07b9e5d295d9ae73b22e30da4ba2c973ba83a130d7eef39717ec9d0f36e81d56bf742671", + "0x8600177ea1f7e7ef90514b38b219a37dedfc39cb83297e4c7a5b479817ef56479d48cf6314820960c751183f6edf8b0e", + "0xb9208ec1c1d7a1e99b59c62d3e4e61dfb706b0e940d09d3abfc3454c19749083260614d89cfd7e822596c3cdbcc6bb95", + "0xa1e94042c796c2b48bc724352d2e9f3a22291d9a34705993357ddb6adabd76da6fc25dac200a8cb0b5bbd99ecddb7af6", + "0xb29c3adedd0bcad8a930625bc4dfdc3552a9afd5ca6dd9c0d758f978068c7982b50b711aa0eb5b97f2b84ee784637835", + "0xaf0632a238bb1f413c7ea8e9b4c3d68f2827bd2e38cd56024391fba6446ac5d19a780d0cfd4a78fe497d537b766a591a", + "0xaaf6e7f7d54f8ef5e2e45dd59774ecbeecf8683aa70483b2a75be6a6071b5981bbaf1627512a65d212817acdfab2e428", + "0x8c751496065da2e927cf492aa5ca9013b24f861d5e6c24b30bbf52ec5aaf1905f40f9a28175faef283dd4ed4f2182a09", + "0x8952377d8e80a85cf67d6b45499f3bad5fd452ea7bcd99efc1b066c4720d8e5bff1214cea90fd1f972a7f0baac3d29be", + "0xa1946ee543d1a6e21f380453be4d446e4130950c5fc3d075794eb8260f6f52d0a795c1ff91d028a648dc1ce7d9ab6b47", + "0x89f3fefe37af31e0c17533d2ca1ce0884cc1dc97c15cbfab9c331b8debd94781c9396abef4bb2f163d09277a08d6adf0", + "0xa2753f1e6e1a154fb117100a5bd9052137add85961f8158830ac20541ab12227d83887d10acf7fd36dcaf7c2596d8d23", + "0x814955b4198933ee11c3883863b06ff98c7eceb21fc3e09df5f916107827ccf3323141983e74b025f46ae00284c9513b", + "0x8cc5c6bb429073bfef47cae7b3bfccb0ffa076514d91a1862c6bda4d581e0df87db53cc6c130bf8a7826304960f5a34e", + "0x909f22c1f1cdc87f7be7439c831a73484a49acbf8f23d47087d7cf867c64ef61da3bde85dc57d705682b4c3fc710d36e", + "0x8048fee7f276fcd504aed91284f28e73693615e0eb3858fa44bcf79d7285a9001c373b3ef71d9a3054817ba293ebe28c", + "0x94400e5cf5d2700ca608c5fe35ce14623f71cc24959f2bc27ca3684092850f76b67fb1f07ca9e5b2ca3062cf8ad17bd4", + "0x81c2ae7d4d1b17f8b6de6a0430acc0d58260993980fe48dc2129c4948269cdc74f9dbfbf9c26b19360823fd913083d48", + "0x8c41fe765128e63f6889d6a979f6a4342300327c8b245a8cfe3ecfbcac1e09c3da30e2a1045b24b78efc6d6d50c8c6ac", + "0xa5dd4ae51ae48c8be4b218c312ade226cffce671cf121cb77810f6c0990768d6dd767badecb5c69921d5574d5e8433d3", + "0xb7642e325f4ba97ae2a39c1c9d97b35aafd49d53dba36aed3f3cb0ca816480b3394079f46a48252d46596559c90f4d58", + "0xae87375b40f35519e7bd4b1b2f73cd0b329b0c2cb9d616629342a71c6c304338445eda069b78ea0fbe44087f3de91e09", + "0xb08918cb6f736855e11d3daca1ddfbdd61c9589b203b5493143227bf48e2c77c2e8c94b0d1aa2fab2226e0eae83f2681", + "0xac36b84a4ac2ebd4d6591923a449c564e3be8a664c46092c09e875c2998eba16b5d32bfd0882fd3851762868e669f0b1", + "0xa44800a3bb192066fa17a3f29029a23697240467053b5aa49b9839fb9b9b8b12bcdcbfc557f024b61f4f51a9aacdefcb", + "0x9064c688fec23441a274cdf2075e5a449caf5c7363cc5e8a5dc9747183d2e00a0c69f2e6b3f6a7057079c46014c93b3b", + "0xaa367b021469af9f5b764a79bb3afbe2d87fe1e51862221672d1a66f954b165778b7c27a705e0f93841fab4c8468344d", + "0xa1a8bfc593d4ab71f91640bc824de5c1380ab2591cfdafcbc78a14b32de3c0e15f9d1b461d85c504baa3d4232c16bb53", + "0x97df48da1799430f528184d30b6baa90c2a2f88f34cdfb342d715339c5ebd6d019aa693cea7c4993daafc9849063a3aa", + "0xabd923831fbb427e06e0dd335253178a9e5791395c84d0ab1433c07c53c1209161097e9582fb8736f8a60bde62d8693e", + "0x84cd1a43f1a438b43dc60ffc775f646937c4f6871438163905a3cebf1115f814ccd38a6ccb134130bff226306e412f32", + "0x91426065996b0743c5f689eb3ca68a9f7b9e4d01f6c5a2652b57fa9a03d8dc7cd4bdbdab0ca5a891fee1e97a7f00cf02", + "0xa4bee50249db3df7fd75162b28f04e57c678ba142ce4d3def2bc17bcb29e4670284a45f218dad3969af466c62a903757", + "0x83141ebcc94d4681404e8b67a12a46374fded6df92b506aff3490d875919631408b369823a08b271d006d5b93136f317", + "0xa0ea1c8883d58d5a784da3d8c8a880061adea796d7505c1f903d07c287c5467f71e4563fc0faafbc15b5a5538b0a7559", + "0x89d9d480574f201a87269d26fb114278ed2c446328df431dc3556e3500e80e4cd01fcac196a2459d8646361ebda840df", + "0x8bf302978973632dd464bec819bdb91304712a3ec859be071e662040620422c6e75eba6f864f764cffa2799272efec39", + "0x922f666bc0fd58b6d7d815c0ae4f66d193d32fc8382c631037f59eeaeae9a8ca6c72d08e72944cf9e800b8d639094e77", + "0x81ad8714f491cdff7fe4399f2eb20e32650cff2999dd45b9b3d996d54a4aba24cc6c451212e78c9e5550368a1a38fb3f", + "0xb58fcf4659d73edb73175bd9139d18254e94c3e32031b5d4b026f2ed37aa19dca17ec2eb54c14340231615277a9d347e", + "0xb365ac9c2bfe409b710928c646ea2fb15b28557e0f089d39878e365589b9d1c34baf5566d20bb28b33bb60fa133f6eff", + "0x8fcae1d75b53ab470be805f39630d204853ca1629a14158bac2f52632277d77458dec204ff84b7b2d77e641c2045be65", + "0xa03efa6bebe84f4f958a56e2d76b5ba4f95dd9ed7eb479edc7cc5e646c8d4792e5b0dfc66cc86aa4b4afe2f7a4850760", + "0xaf1c823930a3638975fb0cc5c59651771b2719119c3cd08404fbd4ce77a74d708cefbe3c56ea08c48f5f10e6907f338f", + "0x8260c8299b17898032c761c325ac9cabb4c5b7e735de81eacf244f647a45fb385012f4f8df743128888c29aefcaaad16", + "0xab2f37a573c82e96a8d46198691cd694dfa860615625f477e41f91b879bc58a745784fccd8ffa13065834ffd150d881d", + "0x986c746c9b4249352d8e5c629e8d7d05e716b3c7aab5e529ca969dd1e984a14b5be41528baef4c85d2369a42d7209216", + "0xb25e32da1a8adddf2a6080725818b75bc67240728ad1853d90738485d8924ea1e202df0a3034a60ffae6f965ec55cf63", + "0xa266e627afcebcefea6b6b44cbc50f5c508f7187e87d047b0450871c2a030042c9e376f3ede0afcf9d1952f089582f71", + "0x86c3bbca4c0300606071c0a80dbdec21ce1dd4d8d4309648151c420854032dff1241a1677d1cd5de4e4de4385efda986", + "0xb9a21a1fe2d1f3273a8e4a9185abf2ff86448cc98bfa435e3d68306a2b8b4a6a3ea33a155be3cb62a2170a86f77679a5", + "0xb117b1ea381adce87d8b342cba3a15d492ff2d644afa28f22424cb9cbc820d4f7693dfc1a4d1b3697046c300e1c9b4c8", + "0x9004c425a2e68870d6c69b658c344e3aa3a86a8914ee08d72b2f95c2e2d8a4c7bb0c6e7e271460c0e637cec11117bf8e", + "0x86a18aa4783b9ebd9131580c8b17994825f27f4ac427b0929a1e0236907732a1c8139e98112c605488ee95f48bbefbfc", + "0x84042243b955286482ab6f0b5df4c2d73571ada00716d2f737ca05a0d2e88c6349e8ee9e67934cfee4a1775dbf7f4800", + "0x92c2153a4733a62e4e1d5b60369f3c26777c7d01cd3c8679212660d572bd3bac9b8a8a64e1f10f7dbf5eaa7579c4e423", + "0x918454b6bb8e44a2afa144695ba8d48ae08d0cdfef4ad078f67709eddf3bb31191e8b006f04e82ea45a54715ef4d5817", + "0xacf0b54f6bf34cf6ed6c2b39cf43194a40d68de6bcf1e4b82c34c15a1343e9ac3737885e1a30b78d01fa3a5125463db8", + "0xa7d60dbe4b6a7b054f7afe9ee5cbbfeca0d05dc619e6041fa2296b549322529faddb8a11e949562309aecefb842ac380", + "0x91ffb53e6d7e5f11159eaf13e783d6dbdfdb1698ed1e6dbf3413c6ea23492bbb9e0932230a9e2caac8fe899a17682795", + "0xb6e8d7be5076ee3565d5765a710c5ecf17921dd3cf555c375d01e958a365ae087d4a88da492a5fb81838b7b92bf01143", + "0xa8c6b763de2d4b2ed42102ef64eccfef31e2fb2a8a2776241c82912fa50fc9f77f175b6d109a97ede331307c016a4b1a", + "0x99839f86cb700c297c58bc33e28d46b92931961548deac29ba8df91d3e11721b10ea956c8e16984f9e4acf1298a79b37", + "0x8c2e2c338f25ea5c25756b7131cde0d9a2b35abf5d90781180a00fe4b8e64e62590dc63fe10a57fba3a31c76d784eb01", + "0x9687d7df2f41319ca5469d91978fed0565a5f11f829ebadaa83db92b221755f76c6eacd7700735e75c91e257087512e3", + "0x8795fdfb7ff8439c58b9bf58ed53873d2780d3939b902b9ddaaa4c99447224ced9206c3039a23c2c44bcc461e2bb637f", + "0xa803697b744d2d087f4e2307218d48fa88620cf25529db9ce71e2e3bbcc65bac5e8bb9be04777ef7bfb5ed1a5b8e6170", + "0x80f3d3efbbb9346ddd413f0a8e36b269eb5d7ff6809d5525ff9a47c4bcab2c01b70018b117f6fe05253775612ff70c6b", + "0x9050e0e45bcc83930d4c505af35e5e4d7ca01cd8681cba92eb55821aececcebe32bb692ebe1a4daac4e7472975671067", + "0x8d206812aac42742dbaf233e0c080b3d1b30943b54b60283515da005de05ea5caa90f91fedcfcba72e922f64d7040189", + "0xa2d44faaeb2eff7915c83f32b13ca6f31a6847b1c1ce114ea240bac3595eded89f09b2313b7915ad882292e2b586d5b4", + "0x961776c8576030c39f214ea6e0a3e8b3d32f023d2600958c098c95c8a4e374deeb2b9dc522adfbd6bda5949bdc09e2a2", + "0x993fa7d8447407af0fbcd9e6d77f815fa5233ab00674efbcf74a1f51c37481445ae291cc7b76db7c178f9cb0e570e0fc", + "0xabd5b1c78e05f9d7c8cc99bdaef8b0b6a57f2daf0f02bf492bec48ea4a27a8f1e38b5854da96efff11973326ff980f92", + "0x8f15af4764bc275e6ccb892b3a4362cacb4e175b1526a9a99944e692fe6ccb1b4fc19abf312bb2a089cb1f344d91a779", + "0xa09b27ccd71855512aba1d0c30a79ffbe7f6707a55978f3ced50e674b511a79a446dbc6d7946add421ce111135a460af", + "0x94b2f98ce86a9271fbd4153e1fc37de48421fe3490fb3840c00f2d5a4d0ba8810c6a32880b002f6374b59e0a7952518b", + "0x8650ac644f93bbcb88a6a0f49fee2663297fd4bc6fd47b6a89b9d8038d32370438ab3a4775ec9b58cb10aea8a95ef7b6", + "0x95e5c2f2e84eed88c6980bbba5a1c0bb375d5a628bff006f7516d45bb7d723da676add4fdd45956f312e7bab0f052644", + "0xb3278a3fa377ac93af7cfc9453f8cb594aae04269bbc99d2e0e45472ff4b6a2f97a26c4c57bf675b9d86f5e77a5d55d1", + "0xb4bcbe6eb666a206e2ea2f877912c1d3b5bdbd08a989fc4490eb06013e1a69ad1ba08bcdac048bf29192312be399077b", + "0xa76d70b78c99fffcbf9bb9886eab40f1ea4f99a309710b660b64cbf86057cbcb644d243f6e341711bb7ef0fedf0435a7", + "0xb2093c1ee945dca7ac76ad5aed08eae23af31dd5a77c903fd7b6f051f4ab84425d33a03c3d45bf2907bc93c02d1f3ad8", + "0x904b1f7534e053a265b22d20be859912b9c9ccb303af9a8d6f1d8f6ccdc5c53eb4a45a1762b880d8444d9be0cd55e7f9", + "0x8f664a965d65bc730c9ef1ec7467be984d4b8eb46bd9b0d64e38e48f94e6e55dda19aeac82cbcf4e1473440e64c4ca18", + "0x8bcee65c4cc7a7799353d07b114c718a2aae0cd10a3f22b7eead5185d159dafd64852cb63924bf87627d176228878bce", + "0x8c78f2e3675096fef7ebaa898d2615cd50d39ca3d8f02b9bdfb07e67da648ae4be3da64838dffc5935fd72962c4b96c7", + "0x8c40afd3701629421fec1df1aac4e849384ef2e80472c0e28d36cb1327acdf2826f99b357f3d7afdbc58a6347fc40b3c", + "0xa197813b1c65a8ea5754ef782522a57d63433ef752215ecda1e7da76b0412ee619f58d904abd2e07e0c097048b6ae1dd", + "0xa670542629e4333884ad7410f9ea3bd6f988df4a8f8a424ca74b9add2312586900cf9ae8bd50411f9146e82626b4af56", + "0xa19875cc07ab84e569d98b8b67fb1dbbdfb59093c7b748fae008c8904a6fd931a63ca8d03ab5fea9bc8d263568125a9b", + "0xb57e7f68e4eb1bd04aafa917b1db1bdab759a02aa8a9cdb1cba34ba8852b5890f655645c9b4e15d5f19bf37e9f2ffe9f", + "0x8abe4e2a4f6462b6c64b3f10e45db2a53c2b0d3c5d5443d3f00a453e193df771eda635b098b6c8604ace3557514027af", + "0x8459e4fb378189b22b870a6ef20183deb816cefbf66eca1dc7e86d36a2e011537db893729f500dc154f14ce24633ba47", + "0x930851df4bc7913c0d8c0f7bd3b071a83668987ed7c397d3d042fdc0d9765945a39a3bae83da9c88cb6b686ed8aeeb26", + "0x8078c9e5cd05e1a8c932f8a1d835f61a248b6e7133fcbb3de406bf4ffc0e584f6f9f95062740ba6008d98348886cf76b", + "0xaddff62bb29430983fe578e3709b0949cdc0d47a13a29bc3f50371a2cb5c822ce53e2448cfaa01bcb6e0aa850d5a380e", + "0x9433add687b5a1e12066721789b1db2edf9b6558c3bdc0f452ba33b1da67426abe326e9a34d207bfb1c491c18811bde1", + "0x822beda3389963428cccc4a2918fa9a8a51cf0919640350293af70821967108cded5997adae86b33cb917780b097f1ca", + "0xa7a9f52bda45e4148ed56dd176df7bd672e9b5ed18888ccdb405f47920fdb0844355f8565cefb17010b38324edd8315f", + "0xb35c3a872e18e607b2555c51f9696a17fa18da1f924d503b163b4ec9fe22ed0c110925275cb6c93ce2d013e88f173d6a", + "0xadf34b002b2b26ab84fc1bf94e05bd8616a1d06664799ab149363c56a6e0c807fdc473327d25632416e952ea327fcd95", + "0xae4a6b9d22a4a3183fac29e2551e1124a8ce4a561a9a2afa9b23032b58d444e6155bb2b48f85c7b6d70393274e230db7", + "0xa2ea3be4fc17e9b7ce3110284038d46a09e88a247b6971167a7878d9dcf36925d613c382b400cfa4f37a3ebea3699897", + "0x8e5863786b641ce3140fbfe37124d7ad3925472e924f814ebfc45959aaf3f61dc554a597610b5defaecc85b59a99b50f", + "0xaefde3193d0f700d0f515ab2aaa43e2ef1d7831c4f7859f48e52693d57f97fa9e520090f3ed700e1c966f4b76048e57f", + "0x841a50f772956622798e5cd208dc7534d4e39eddee30d8ce133383d66e5f267e389254a0cdae01b770ecd0a9ca421929", + "0x8fbc2bfd28238c7d47d4c03b1b910946c0d94274a199575e5b23242619b1de3497784e646a92aa03e3e24123ae4fcaba", + "0x926999579c8eec1cc47d7330112586bdca20b4149c8b2d066f527c8b9f609e61ce27feb69db67eea382649c6905efcf9", + "0xb09f31f305efcc65589adf5d3690a76cf339efd67cd43a4e3ced7b839507466e4be72dd91f04e89e4bbef629d46e68c0", + "0xb917361f6b95f759642638e0b1d2b3a29c3bdef0b94faa30de562e6078c7e2d25976159df3edbacbf43614635c2640b4", + "0x8e7e8a1253bbda0e134d62bfe003a2669d471b47bd2b5cde0ff60d385d8e62279d54022f5ac12053b1e2d3aaa6910b4c", + "0xb69671a3c64e0a99d90b0ed108ce1912ff8ed983e4bddd75a370e9babde25ee1f5efb59ec707edddd46793207a8b1fe7", + "0x910b2f4ebd37b7ae94108922b233d0920b4aba0bd94202c70f1314418b548d11d8e9caa91f2cd95aff51b9432d122b7f", + "0x82f645c90dfb52d195c1020346287c43a80233d3538954548604d09fbab7421241cde8593dbc4acc4986e0ea39a27dd9", + "0x8fee895f0a140d88104ce442fed3966f58ff9d275e7373483f6b4249d64a25fb5374bbdc6bce6b5ab0270c2847066f83", + "0x84f5bd7aab27b2509397aeb86510dd5ac0a53f2c8f73799bf720f2f87a52277f8d6b0f77f17bc80739c6a7119b7eb062", + "0x9903ceced81099d7e146e661bcf01cbaccab5ba54366b85e2177f07e2d8621e19d9c9c3eee14b9266de6b3f9b6ea75ae", + "0xb9c16ea2a07afa32dd6c7c06df0dec39bca2067a9339e45475c98917f47e2320f6f235da353fd5e15b477de97ddc68dd", + "0x9820a9bbf8b826bec61ebf886de2c4f404c1ebdc8bab82ee1fea816d9de29127ce1852448ff717a3fe8bbfe9e92012e5", + "0x817224d9359f5da6f2158c2c7bf9165501424f063e67ba9859a07ab72ee2ee62eb00ca6da821cfa19065c3282ca72c74", + "0x94b95c465e6cb00da400558a3c60cfec4b79b27e602ca67cbc91aead08de4b6872d8ea096b0dc06dca4525c8992b8547", + "0xa2b539a5bccd43fa347ba9c15f249b417997c6a38c63517ca38394976baa08e20be384a360969ff54e7e721db536b3e5", + "0x96caf707e34f62811ee8d32ccf28d8d6ec579bc33e424d0473529af5315c456fd026aa910c1fed70c91982d51df7d3ca", + "0x8a77b73e890b644c6a142bdbac59b22d6a676f3b63ddafb52d914bb9d395b8bf5aedcbcc90429337df431ebd758a07a6", + "0x8857830a7351025617a08bc44caec28d2fae07ebf5ffc9f01d979ce2a53839a670e61ae2783e138313929129790a51a1", + "0xaa3e420321ed6f0aa326d28d1a10f13facec6f605b6218a6eb9cbc074801f3467bf013a456d1415a5536f12599efa3d3", + "0x824aed0951957b00ea2f3d423e30328a3527bf6714cf9abbae84cf27e58e5c35452ba89ccc011de7c68c75d6e021d8f1", + "0xa2e87cc06bf202e953fb1081933d8b4445527dde20e38ed1a4f440144fd8fa464a2b73e068b140562e9045e0f4bd3144", + "0xae3b8f06ad97d7ae3a5e5ca839efff3e4824dc238c0c03fc1a8d2fc8aa546cdfd165b784a31bb4dec7c77e9305b99a4b", + "0xb30c3e12395b1fb8b776f3ec9f87c70e35763a7b2ddc68f0f60a4982a84017f27c891a98561c830038deb033698ed7fc", + "0x874e507757cd1177d0dff0b0c62ce90130324442a33da3b2c8ee09dbca5d543e3ecfe707e9f1361e7c7db641c72794bb", + "0xb53012dd10b5e7460b57c092eaa06d6502720df9edbbe3e3f61a9998a272bf5baaac4a5a732ad4efe35d6fac6feca744", + "0x85e6509d711515534d394e6cacbed6c81da710074d16ef3f4950bf2f578d662a494d835674f79c4d6315bced4defc5f0", + "0xb6132b2a34b0905dcadc6119fd215419a7971fe545e52f48b768006944b4a9d7db1a74b149e2951ea48c083b752d0804", + "0x989867da6415036d19b4bacc926ce6f4df7a556f50a1ba5f3c48eea9cefbb1c09da81481c8009331ee83f0859185e164", + "0x960a6c36542876174d3fbc1505413e29f053ed87b8d38fef3af180491c7eff25200b45dd5fe5d4d8e63c7e8c9c00f4c8", + "0x9040b59bd739d9cc2e8f6e894683429e4e876a8106238689ff4c22770ae5fdae1f32d962b30301fa0634ee163b524f35", + "0xaf3fcd0a45fe9e8fe256dc7eab242ef7f582dd832d147444483c62787ac820fafc6ca55d639a73f76bfa5e7f5462ab8f", + "0xb934c799d0736953a73d91e761767fdb78454355c4b15c680ce08accb57ccf941b13a1236980001f9e6195801cffd692", + "0x8871e8e741157c2c326b22cf09551e78da3c1ec0fc0543136f581f1550f8bab03b0a7b80525c1e99812cdbf3a9698f96", + "0xa8a977f51473a91d178ee8cfa45ffef8d6fd93ab1d6e428f96a3c79816d9c6a93cd70f94d4deda0125fd6816e30f3bea", + "0xa7688b3b0a4fc1dd16e8ba6dc758d3cfe1b7cf401c31739484c7fa253cce0967df1b290769bcefc9d23d3e0cb19e6218", + "0x8ae84322662a57c6d729e6ff9d2737698cc2da2daeb1f39e506618750ed23442a6740955f299e4a15dda6db3e534d2c6", + "0xa04a961cdccfa4b7ef83ced17ab221d6a043b2c718a0d6cc8e6f798507a31f10bf70361f70a049bc8058303fa7f96864", + "0xb463e39732a7d9daec8a456fb58e54b30a6e160aa522a18b9a9e836488cce3342bcbb2e1deab0f5e6ec0a8796d77197d", + "0xb1434a11c6750f14018a2d3bcf94390e2948f4f187e93bb22070ca3e5393d339dc328cbfc3e48815f51929465ffe7d81", + "0x84ff81d73f3828340623d7e3345553610aa22a5432217ef0ebd193cbf4a24234b190c65ca0873c22d10ea7b63bd1fbed", + "0xb6fe2723f0c47757932c2ddde7a4f8434f665612f7b87b4009c2635d56b6e16b200859a8ade49276de0ef27a2b6c970a", + "0x9742884ed7cd52b4a4a068a43d3faa02551a424136c85a9313f7cb58ea54c04aa83b0728fd741d1fe39621e931e88f8f", + "0xb7d2d65ea4d1ad07a5dee39e40d6c03a61264a56b1585b4d76fc5b2a68d80a93a42a0181d432528582bf08d144c2d6a9", + "0x88c0f66bada89f8a43e5a6ead2915088173d106c76f724f4a97b0f6758aed6ae5c37c373c6b92cdd4aea8f6261f3a374", + "0x81f9c43582cb42db3900747eb49ec94edb2284999a499d1527f03315fd330e5a509afa3bff659853570e9886aab5b28b", + "0x821f9d27d6beb416abf9aa5c79afb65a50ed276dbda6060103bc808bcd34426b82da5f23e38e88a55e172f5c294b4d40", + "0x8ba307b9e7cb63a6c4f3851b321aebfdb6af34a5a4c3bd949ff7d96603e59b27ff4dc4970715d35f7758260ff942c9e9", + "0xb142eb6c5f846de33227d0bda61d445a7c33c98f0a8365fe6ab4c1fabdc130849be597ef734305894a424ea715372d08", + "0xa732730ae4512e86a741c8e4c87fee8a05ee840fec0e23b2e037d58dba8dde8d10a9bc5191d34d00598941becbbe467f", + "0xadce6f7c30fd221f6b10a0413cc76435c4bb36c2d60bca821e5c67409fe9dbb2f4c36ef85eb3d734695e4be4827e9fd3", + "0xa74f00e0f9b23aff7b2527ce69852f8906dab9d6abe62ecd497498ab21e57542e12af9918d4fd610bb09e10b0929c510", + "0xa593b6b0ef26448ce4eb3ab07e84238fc020b3cb10d542ff4b16d4e2be1bcde3797e45c9cf753b8dc3b0ffdb63984232", + "0xaed3913afccf1aa1ac0eb4980eb8426d0baccebd836d44651fd72af00d09fac488a870223c42aca3ceb39752070405ae", + "0xb2c44c66a5ea7fde626548ba4cef8c8710191343d3dadfd3bb653ce715c0e03056a5303a581d47dde66e70ea5a2d2779", + "0x8e5029b2ccf5128a12327b5103f7532db599846e422531869560ceaff392236434d87159f597937dbf4054f810c114f4", + "0x82beed1a2c4477e5eb39fc5b0e773b30cfec77ef2b1bf17eadaf60eb35b6d0dd9d8cf06315c48d3546badb3f21cd0cca", + "0x90077bd6cc0e4be5fff08e5d07a5a158d36cebd1d1363125bc4fae0866ffe825b26f933d4ee5427ba5cd0c33c19a7b06", + "0xa7ec0d8f079970e8e34f0ef3a53d3e0e45428ddcef9cc776ead5e542ef06f3c86981644f61c5a637e4faf001fb8c6b3e", + "0xae6d4add6d1a6f90b22792bc9d40723ee6850c27d0b97eefafd5b7fd98e424aa97868b5287cc41b4fbd7023bca6a322c", + "0x831aa917533d077da07c01417feaa1408846363ba2b8d22c6116bb858a95801547dd88b7d7fa1d2e3f0a02bdeb2e103d", + "0x96511b860b07c8a5ed773f36d4aa9d02fb5e7882753bf56303595bcb57e37ccc60288887eb83bef08c657ec261a021a2", + "0x921d2a3e7e9790f74068623de327443666b634c8443aba80120a45bba450df920b2374d96df1ce3fb1b06dd06f8cf6e3", + "0xaa74451d51fe82b4581ead8e506ec6cd881010f7e7dd51fc388eb9a557db5d3c6721f81c151d08ebd9c2591689fbc13e", + "0xa972bfbcf4033d5742d08716c927c442119bdae336bf5dff914523b285ccf31953da2733759aacaa246a9af9f698342c", + "0xad1fcd0cae0e76840194ce4150cb8a56ebed728ec9272035f52a799d480dfc85840a4d52d994a18b6edb31e79be6e8ad", + "0xa2c69fe1d36f235215432dad48d75887a44c99dfa0d78149acc74087da215a44bdb5f04e6eef88ff7eff80a5a7decc77", + "0xa94ab2af2b6ee1bc6e0d4e689ca45380d9fbd3c5a65b9bd249d266a4d4c07bf5d5f7ef2ae6000623aee64027892bf8fe", + "0x881ec1fc514e926cdc66480ac59e139148ff8a2a7895a49f0dff45910c90cdda97b66441a25f357d6dd2471cddd99bb3", + "0x884e6d3b894a914c8cef946a76d5a0c8351843b2bffa2d1e56c6b5b99c84104381dd1320c451d551c0b966f4086e60f9", + "0x817c6c10ce2677b9fc5223500322e2b880583254d0bb0d247d728f8716f5e05c9ff39f135854342a1afecd9fbdcf7c46", + "0xaaf4a9cb686a14619aa1fc1ac285dd3843ac3dd99f2b2331c711ec87b03491c02f49101046f3c5c538dc9f8dba2a0ac2", + "0x97ecea5ce53ca720b5d845227ae61d70269a2f53540089305c86af35f0898bfd57356e74a8a5e083fa6e1ea70080bd31", + "0xa22d811e1a20a75feac0157c418a4bfe745ccb5d29466ffa854dca03e395b6c3504a734341746b2846d76583a780b32e", + "0x940cbaa0d2b2db94ae96b6b9cf2deefbfd059e3e5745de9aec4a25f0991b9721e5cd37ef71c631575d1a0c280b01cd5b", + "0xae33cb4951191258a11044682de861bf8d92d90ce751b354932dd9f3913f542b6a0f8a4dc228b3cd9244ac32c4582832", + "0xa580df5e58c4274fe0f52ac2da1837e32f5c9db92be16c170187db4c358f43e5cfdda7c5911dcc79d77a5764e32325f5", + "0x81798178cb9d8affa424f8d3be67576ba94d108a28ccc01d330c51d5a63ca45bb8ca63a2f569b5c5fe1303cecd2d777f", + "0x89975b91b94c25c9c3660e4af4047a8bacf964783010820dbc91ff8281509379cb3b24c25080d5a01174dd9a049118d5", + "0xa7327fcb3710ed3273b048650bde40a32732ef40a7e58cf7f2f400979c177944c8bc54117ba6c80d5d4260801dddab79", + "0x92b475dc8cb5be4b90c482f122a51bcb3b6c70593817e7e2459c28ea54a7845c50272af38119406eaadb9bcb993368d0", + "0x9645173e9ecefc4f2eae8363504f7c0b81d85f8949a9f8a6c01f2d49e0a0764f4eacecf3e94016dd407fc14494fce9f9", + "0x9215fd8983d7de6ae94d35e6698226fc1454977ae58d42d294be9aad13ac821562ad37d5e7ee5cdfe6e87031d45cd197", + "0x810360a1c9b88a9e36f520ab5a1eb8bed93f52deefbe1312a69225c0a08edb10f87cc43b794aced9c74220cefcc57e7d", + "0xad7e810efd61ed4684aeda9ed8bb02fb9ae4b4b63fda8217d37012b94ff1b91c0087043bfa4e376f961fff030c729f3b", + "0x8b07c95c6a06db8738d10bb03ec11b89375c08e77f0cab7e672ce70b2685667ca19c7e1c8b092821d31108ea18dfd4c7", + "0x968825d025ded899ff7c57245250535c732836f7565eab1ae23ee7e513201d413c16e1ba3f5166e7ac6cf74de8ceef4f", + "0x908243370c5788200703ade8164943ad5f8c458219186432e74dbc9904a701ea307fd9b94976c866e6c58595fd891c4b", + "0x959969d16680bc535cdc6339e6186355d0d6c0d53d7bbfb411641b9bf4b770fd5f575beef5deec5c4fa4d192d455c350", + "0xad177f4f826a961adeac76da40e2d930748effff731756c797eddc4e5aa23c91f070fb69b19221748130b0961e68a6bb", + "0x82f8462bcc25448ef7e0739425378e9bb8a05e283ce54aae9dbebaf7a3469f57833c9171672ad43a79778366c72a5e37", + "0xa28fb275b1845706c2814d9638573e9bc32ff552ebaed761fe96fdbce70395891ca41c400ae438369264e31a2713b15f", + "0x8a9c613996b5e51dadb587a787253d6081ea446bf5c71096980bf6bd3c4b69905062a8e8a3792de2d2ece3b177a71089", + "0x8d5aefef9f60cb27c1db2c649221204dda48bb9bf8bf48f965741da051340e8e4cab88b9d15c69f3f84f4c854709f48a", + "0x93ebf2ca6ad85ab6deace6de1a458706285b31877b1b4d7dcb9d126b63047efaf8c06d580115ec9acee30c8a7212fa55", + "0xb3ee46ce189956ca298057fa8223b7fd1128cf52f39159a58bca03c71dd25161ac13f1472301f72aef3e1993fe1ab269", + "0xa24d7a8d066504fc3f5027ccb13120e2f22896860e02c45b5eba1dbd512d6a17c28f39155ea581619f9d33db43a96f92", + "0xae9ceacbfe12137db2c1a271e1b34b8f92e4816bad1b3b9b6feecc34df0f8b3b0f7ed0133acdf59c537d43d33fc8d429", + "0x83967e69bf2b361f86361bd705dce0e1ad26df06da6c52b48176fe8dfcbeb03c462c1a4c9e649eff8c654b18c876fdef", + "0x9148e6b814a7d779c19c31e33a068e97b597de1f8100513db3c581190513edc4d544801ce3dd2cf6b19e0cd6daedd28a", + "0x94ccdafc84920d320ed22de1e754adea072935d3c5f8c2d1378ebe53d140ea29853f056fb3fb1e375846061a038cc9bc", + "0xafb43348498c38b0fa5f971b8cdd3a62c844f0eb52bc33daf2f67850af0880fce84ecfb96201b308d9e6168a0d443ae3", + "0x86d5736520a83538d4cd058cc4b4e84213ed00ebd6e7af79ae787adc17a92ba5359e28ba6c91936d967b4b28d24c3070", + "0xb5210c1ff212c5b1e9ef9126e08fe120a41e386bb12c22266f7538c6d69c7fd8774f11c02b81fd4e88f9137b020801fe", + "0xb78cfd19f94d24e529d0f52e18ce6185cb238edc6bd43086270fd51dd99f664f43dd4c7d2fe506762fbd859028e13fcf", + "0xa6e7220598c554abdcc3fdc587b988617b32c7bb0f82c06205467dbedb58276cc07cae317a190f19d19078773f4c2bbb", + "0xb88862809487ee430368dccd85a5d72fa4d163ca4aad15c78800e19c1a95be2192719801e315d86cff7795e0544a77e4", + "0x87ecb13a03921296f8c42ceb252d04716f10e09c93962239fcaa0a7fef93f19ab3f2680bc406170108bc583e9ff2e721", + "0xa810cd473832b6581c36ec4cb403f2849357ba2d0b54df98ef3004b8a530c078032922a81d40158f5fb0043d56477f6e", + "0xa247b45dd85ca7fbb718b328f30a03f03c84aef2c583fbdc9fcc9eb8b52b34529e8c8f535505c10598b1b4dac3d7c647", + "0x96ee0b91313c68bac4aa9e065ce9e1d77e51ca4cff31d6a438718c58264dee87674bd97fc5c6b8008be709521e4fd008", + "0x837567ad073e42266951a9a54750919280a2ac835a73c158407c3a2b1904cf0d17b7195a393c71a18ad029cbd9cf79ee", + "0xa6a469c44b67ebf02196213e7a63ad0423aab9a6e54acc6fcbdbb915bc043586993454dc3cd9e4be8f27d67c1050879b", + "0x8712d380a843b08b7b294f1f06e2f11f4ad6bcc655fdde86a4d8bc739c23916f6fad2b902fe47d6212f03607907e9f0e", + "0x920adfb644b534789943cdae1bdd6e42828dda1696a440af2f54e6b97f4f97470a1c6ea9fa6a2705d8f04911d055acd1", + "0xa161c73adf584a0061e963b062f59d90faac65c9b3a936b837a10d817f02fcabfa748824607be45a183dd40f991fe83f", + "0x874f4ecd408c76e625ea50bc59c53c2d930ee25baf4b4eca2440bfbffb3b8bc294db579caa7c68629f4d9ec24187c1ba", + "0x8bff18087f112be7f4aa654e85c71fef70eee8ae480f61d0383ff6f5ab1a0508f966183bb3fc4d6f29cb7ca234aa50d3", + "0xb03b46a3ca3bc743a173cbc008f92ab1aedd7466b35a6d1ca11e894b9482ea9dc75f8d6db2ddd1add99bfbe7657518b7", + "0x8b4f3691403c3a8ad9e097f02d130769628feddfa8c2b3dfe8cff64e2bed7d6e5d192c1e2ba0ac348b8585e94acd5fa1", + "0xa0d9ca4a212301f97591bf65d5ef2b2664766b427c9dd342e23cb468426e6a56be66b1cb41fea1889ac5d11a8e3c50a5", + "0x8c93ed74188ca23b3df29e5396974b9cc135c91fdefdea6c0df694c8116410e93509559af55533a3776ac11b228d69b1", + "0x82dd331fb3f9e344ebdeeb557769b86a2cc8cc38f6c298d7572a33aea87c261afa9dbd898989139b9fc16bc1e880a099", + "0xa65faedf326bcfd8ef98a51410c78b021d39206704e8291cd1f09e096a66b9b0486be65ff185ca224c45918ac337ddeb", + "0xa188b37d363ac072a766fd5d6fa27df07363feff1342217b19e3c37385e42ffde55e4be8355aceaa2f267b6d66b4ac41", + "0x810fa3ba3e96d843e3bafd3f2995727f223d3567c8ba77d684c993ba1773c66551eb5009897c51b3fe9b37196984f5ec", + "0x87631537541852da323b4353af45a164f68b304d24c01183bf271782e11687f3fcf528394e1566c2a26cb527b3148e64", + "0xb721cb2b37b3c477a48e3cc0044167d51ff568a5fd2fb606e5aec7a267000f1ddc07d3db919926ae12761a8e017c767c", + "0x904dfad4ba2cc1f6e60d1b708438a70b1743b400164cd981f13c064b8328d5973987d4fb9cf894068f29d3deaf624dfb", + "0xa70491538893552c20939fae6be2f07bfa84d97e2534a6bbcc0f1729246b831103505e9f60e97a8fa7d2e6c1c2384579", + "0x8726cf1b26b41f443ff7485adcfddc39ace2e62f4d65dd0bb927d933e262b66f1a9b367ded5fbdd6f3b0932553ac1735", + "0xae8a11cfdf7aa54c08f80cb645e3339187ab3886babe9fae5239ba507bb3dd1c0d161ca474a2df081dcd3d63e8fe445e", + "0x92328719e97ce60e56110f30a00ac5d9c7a2baaf5f8d22355d53c1c77941e3a1fec7d1405e6fbf8959665fe2ba7a8cad", + "0x8d9d6255b65798d0018a8cccb0b6343efd41dc14ff2058d3eed9451ceaad681e4a0fa6af67b0a04318aa628024e5553d", + "0xb70209090055459296006742d946a513f0cba6d83a05249ee8e7a51052b29c0ca9722dc4af5f9816a1b7938a5dac7f79", + "0xaab7b766b9bf91786dfa801fcef6d575dc6f12b77ecc662eb4498f0312e54d0de9ea820e61508fc8aeee5ab5db529349", + "0xa8104b462337748b7f086a135d0c3f87f8e51b7165ca6611264b8fb639d9a2f519926cb311fa2055b5fadf03da70c678", + "0xb0d2460747d5d8b30fc6c6bd0a87cb343ddb05d90a51b465e8f67d499cfc5e3a9e365da05ae233bbee792cdf90ec67d5", + "0xaa55f5bf3815266b4a149f85ed18e451c93de9163575e3ec75dd610381cc0805bb0a4d7c4af5b1f94d10231255436d2c", + "0x8d4c6a1944ff94426151909eb5b99cfd92167b967dabe2bf3aa66bb3c26c449c13097de881b2cfc1bf052862c1ef7b03", + "0x8862296162451b9b6b77f03bf32e6df71325e8d7485cf3335d66fd48b74c2a8334c241db8263033724f26269ad95b395", + "0x901aa96deb26cda5d9321190ae6624d357a41729d72ef1abfd71bebf6139af6d690798daba53b7bc5923462115ff748a", + "0x96c195ec4992728a1eb38cdde42d89a7bce150db43adbc9e61e279ea839e538deec71326b618dd39c50d589f78fc0614", + "0xb6ff8b8aa0837b99a1a8b46fb37f20ad4aecc6a98381b1308697829a59b8442ffc748637a88cb30c9b1f0f28a926c4f6", + "0x8d807e3dca9e7bef277db1d2cfb372408dd587364e8048b304eff00eacde2c723bfc84be9b98553f83cba5c7b3cba248", + "0x8800c96adb0195c4fc5b24511450dee503c32bf47044f5e2e25bd6651f514d79a2dd9b01cd8c09f3c9d3859338490f57", + "0x89fe366096097e38ec28dd1148887112efa5306cc0c3da09562aafa56f4eb000bf46ff79bf0bdd270cbde6bf0e1c8957", + "0xaf409a90c2776e1e7e3760b2042507b8709e943424606e31e791d42f17873a2710797f5baaab4cc4a19998ef648556b0", + "0x8d761863c9b6edbd232d35ab853d944f5c950c2b643f84a1a1327ebb947290800710ff01dcfa26dc8e9828481240e8b1", + "0x90b95e9be1e55c463ed857c4e0617d6dc3674e99b6aa62ed33c8e79d6dfcf7d122f4f4cc2ee3e7c5a49170cb617d2e2e", + "0xb3ff381efefabc4db38cc4727432e0301949ae4f16f8d1dea9b4f4de611cf5a36d84290a0bef160dac4e1955e516b3b0", + "0xa8a84564b56a9003adcadb3565dc512239fc79572762cda7b5901a255bc82656bb9c01212ad33d6bef4fbbce18dacc87", + "0x90a081890364b222eef54bf0075417f85e340d2fec8b7375995f598aeb33f26b44143ebf56fca7d8b4ebb36b5747b0eb", + "0xade6ee49e1293224ddf2d8ab7f14bb5be6bc6284f60fd5b3a1e0cf147b73cff57cf19763b8a36c5083badc79c606b103", + "0xb2fa99806dd2fa3de09320b615a2570c416c9bcdb052e592b0aead748bbe407ec9475a3d932ae48b71c2627eb81986a6", + "0x91f3b7b73c8ccc9392542711c45fe6f236057e6efad587d661ad5cb4d6e88265f86b807bb1151736b1009ab74fd7acb4", + "0x8800e2a46af96696dfbdcbf2ca2918b3dcf28ad970170d2d1783b52b8d945a9167d052beeb55f56c126da7ffa7059baa", + "0x9862267a1311c385956b977c9aa08548c28d758d7ba82d43dbc3d0a0fd1b7a221d39e8399997fea9014ac509ff510ac4", + "0xb7d24f78886fd3e2d283e18d9ad5a25c1a904e7d9b9104bf47da469d74f34162e27e531380dbbe0a9d051e6ffd51d6e7", + "0xb0f445f9d143e28b9df36b0f2c052da87ee2ca374d9d0fbe2eff66ca6fe5fe0d2c1951b428d58f7314b7e74e45d445ea", + "0xb63fc4083eabb8437dafeb6a904120691dcb53ce2938b820bb553da0e1eecd476f72495aacb72600cf9cad18698fd3db", + "0xb9ffd8108eaebd582d665f8690fe8bb207fd85185e6dd9f0b355a09bac1bbff26e0fdb172bc0498df025414e88fe2eda", + "0x967ed453e1f1a4c5b7b6834cc9f75c13f6889edc0cc91dc445727e9f408487bbf05c337103f61397a10011dfbe25d61d", + "0x98ceb673aff36e1987d5521a3984a07079c3c6155974bb8b413e8ae1ce84095fe4f7862fba7aefa14753eb26f2a5805f", + "0x85f01d28603a8fdf6ce6a50cb5c44f8a36b95b91302e3f4cd95c108ce8f4d212e73aec1b8d936520d9226802a2bd9136", + "0x88118e9703200ca07910345fbb789e7a8f92bd80bbc79f0a9e040e8767d33df39f6eded403a9b636eabf9101e588482a", + "0x90833a51eef1b10ed74e8f9bbd6197e29c5292e469c854eed10b0da663e2bceb92539710b1858bbb21887bd538d28d89", + "0xb513b905ec19191167c6193067b5cfdf5a3d3828375360df1c7e2ced5815437dfd37f0c4c8f009d7fb29ff3c8793f560", + "0xb1b6d405d2d18f9554b8a358cc7e2d78a3b34269737d561992c8de83392ac9a2857be4bf15de5a6c74e0c9d0f31f393c", + "0xb828bd3e452b797323b798186607849f85d1fb20c616833c0619360dfd6b3e3aa000fd09dafe4b62d74abc41072ff1a9", + "0x8efde67d0cca56bb2c464731879c9ac46a52e75bac702a63200a5e192b4f81c641f855ca6747752b84fe469cb7113b6c", + "0xb2762ba1c89ac3c9a983c242e4d1c2610ff0528585ed5c0dfc8a2c0253551142af9b59f43158e8915a1da7cc26b9df67", + "0x8a3f1157fb820d1497ef6b25cd70b7e16bb8b961b0063ad340d82a79ee76eb2359ca9e15e6d42987ed7f154f5eeaa2da", + "0xa75e29f29d38f09c879f971c11beb5368affa084313474a5ecafa2896180b9e47ea1995c2733ec46f421e395a1d9cffe", + "0x8e8c3dd3e7196ef0b4996b531ec79e4a1f211db5d5635e48ceb80ff7568b2ff587e845f97ee703bb23a60945ad64314a", + "0x8e7f32f4a3e3c584af5e3d406924a0aa34024c42eca74ef6cc2a358fd3c9efaf25f1c03aa1e66bb94b023a2ee2a1cace", + "0xab7dce05d59c10a84feb524fcb62478906b3fa045135b23afbede3bb32e0c678d8ebe59feabccb5c8f3550ea76cae44b", + "0xb38bb4b44d827f6fd3bd34e31f9186c59e312dbfadd4a7a88e588da10146a78b1f8716c91ad8b806beb8da65cab80c4c", + "0x9490ce9442bbbd05438c7f5c4dea789f74a7e92b1886a730544b55ba377840740a3ae4f2f146ee73f47c9278b0e233bc", + "0x83c003fab22a7178eed1a668e0f65d4fe38ef3900044e9ec63070c23f2827d36a1e73e5c2b883ec6a2afe2450171b3b3", + "0x9982f02405978ddc4fca9063ebbdb152f524c84e79398955e66fe51bc7c1660ec1afc3a86ec49f58d7b7dde03505731c", + "0xab337bd83ccdd2322088ffa8d005f450ced6b35790f37ab4534313315ee84312adc25e99cce052863a8bedee991729ed", + "0x8312ce4bec94366d88f16127a17419ef64285cd5bf9e5eda010319b48085966ed1252ed2f5a9fd3e0259b91bb65f1827", + "0xa60d5a6327c4041b0c00a1aa2f0af056520f83c9ce9d9ccd03a0bd4d9e6a1511f26a422ea86bd858a1f77438adf07e6c", + "0xb84a0a0b030bdad83cf5202aa9afe58c9820e52483ab41f835f8c582c129ee3f34aa096d11c1cd922eda02ea1196a882", + "0x8077d105317f4a8a8f1aadeb05e0722bb55f11abcb490c36c0904401107eb3372875b0ac233144829e734f0c538d8c1d", + "0x9202503bd29a6ec198823a1e4e098f9cfe359ed51eb5174d1ca41368821bfeebcbd49debfd02952c41359d1c7c06d2b1", + "0xabc28c155e09365cb77ffead8dc8f602335ef93b2f44e4ef767ce8fc8ef9dd707400f3a722e92776c2e0b40192c06354", + "0xb0f6d1442533ca45c9399e0a63a11f85ff288d242cea6cb3b68c02e77bd7d158047cae2d25b3bcd9606f8f66d9b32855", + "0xb01c3d56a0db84dc94575f4b6ee2de4beca3230e86bed63e2066beb22768b0a8efb08ebaf8ac3dedb5fe46708b084807", + "0x8c8634b0432159f66feaabb165842d1c8ac378f79565b1b90c381aa8450eb4231c3dad11ec9317b9fc2b155c3a771e32", + "0x8e67f623d69ecd430c9ee0888520b6038f13a2b6140525b056dc0951f0cfed2822e62cf11d952a483107c5c5acac4826", + "0x9590bb1cba816dd6acd5ac5fba5142c0a19d53573e422c74005e0bcf34993a8138c83124cad35a3df65879dba6134edd", + "0x801cd96cde0749021a253027118d3ea135f3fcdbe895db08a6c145641f95ebd368dd6a1568d995e1d0084146aebe224a", + "0x848b5d196427f6fc1f762ee3d36e832b64a76ec1033cfedc8b985dea93932a7892b8ef1035c653fb9dcd9ab2d9a44ac8", + "0xa1017eb83d5c4e2477e7bd2241b2b98c4951a3b391081cae7d75965cadc1acaec755cf350f1f3d29741b0828e36fedea", + "0x8d6d2785e30f3c29aad17bd677914a752f831e96d46caf54446d967cb2432be2c849e26f0d193a60bee161ea5c6fe90a", + "0x935c0ba4290d4595428e034b5c8001cbd400040d89ab00861108e8f8f4af4258e41f34a7e6b93b04bc253d3b9ffc13bf", + "0xaac02257146246998477921cef2e9892228590d323b839f3e64ea893b991b463bc2f47e1e5092ddb47e70b2f5bce7622", + "0xb921fde9412970a5d4c9a908ae8ce65861d06c7679af577cf0ad0d5344c421166986bee471fd6a6cecb7d591f06ec985", + "0x8ef4c37487b139d6756003060600bb6ebac7ea810b9c4364fc978e842f13ac196d1264fbe5af60d76ff6d9203d8e7d3f", + "0x94b65e14022b5cf6a9b95f94be5ace2711957c96f4211c3f7bb36206bd39cfbd0ea82186cab5ad0577a23214a5c86e9e", + "0xa31c166d2a2ca1d5a75a5920fef7532681f62191a50d8555fdaa63ba4581c3391cc94a536fc09aac89f64eafceec3f90", + "0x919a8cc128de01e9e10f5d83b08b52293fdd41bde2b5ae070f3d95842d4a16e5331cf2f3d61c765570c8022403610fa4", + "0xb23d6f8331eef100152d60483cfa14232a85ee712c8538c9b6417a5a7c5b353c2ac401390c6c215cb101f5cee6b5f43e", + "0xab357160c08a18319510a571eafff154298ce1020de8e1dc6138a09fcb0fcbcdd8359f7e9386bda00b7b9cdea745ffdc", + "0xab55079aea34afa5c0bd1124b9cdfe01f325b402fdfa017301bf87812eaa811ea5798c3aaf818074d420d1c782b10ada", + "0xade616010dc5009e7fc4f8d8b00dc716686a5fa0a7816ad9e503e15839d3b909b69d9dd929b7575376434ffec0d2bea8", + "0x863997b97ed46898a8a014599508fa3079f414b1f4a0c4fdc6d74ae8b444afa350f327f8bfc2a85d27f9e2d049c50135", + "0x8d602ff596334efd4925549ed95f2aa762b0629189f0df6dbb162581657cf3ea6863cd2287b4d9c8ad52813d87fcd235", + "0xb70f68c596dcdeed92ad5c6c348578b26862a51eb5364237b1221e840c47a8702f0fbc56eb520a22c0eed99795d3903e", + "0x9628088f8e0853cefadee305a8bf47fa990c50fa96a82511bbe6e5dc81ef4b794e7918a109070f92fc8384d77ace226f", + "0x97e26a46e068b605ce96007197ecd943c9a23881862f4797a12a3e96ba2b8d07806ad9e2a0646796b1889c6b7d75188c", + "0xb1edf467c068cc163e2d6413cc22b16751e78b3312fe47b7ea82b08a1206d64415b2c8f2a677fa89171e82cc49797150", + "0xa44d15ef18745b251429703e3cab188420e2d974de07251501799b016617f9630643fcd06f895634d8ecdd579e1bf000", + "0xabd126df3917ba48c618ee4dbdf87df506193462f792874439043fa1b844466f6f4e0ff2e42516e63b5b23c0892b2695", + "0xa2a67f57c4aa3c2aa1eeddbfd5009a89c26c2ce8fa3c96a64626aba19514beb125f27df8559506f737de3eae0f1fc18f", + "0xa633e0132197e6038197304b296ab171f1d8e0d0f34dcf66fe9146ac385b0239232a8470b9205a4802ab432389f4836d", + "0xa914b3a28509a906c3821463b936455d58ff45dcbe158922f9efb2037f2eb0ce8e92532d29b5d5a3fcd0d23fa773f272", + "0xa0e1412ce4505daf1a2e59ce4f0fc0e0023e335b50d2b204422f57cd65744cc7a8ed35d5ef131a42c70b27111d3115b7", + "0xa2339e2f2b6072e88816224fdd612c04d64e7967a492b9f8829db15367f565745325d361fd0607b0def1be384d010d9e", + "0xa7309fc41203cb99382e8193a1dcf03ac190a7ce04835304eb7e341d78634e83ea47cb15b885601956736d04cdfcaa01", + "0x81f3ccd6c7f5b39e4e873365f8c37b214e8ab122d04a606fbb7339dc3298c427e922ec7418002561d4106505b5c399ee", + "0x92c121cf914ca549130e352eb297872a63200e99b148d88fbc9506ad882bec9d0203d65f280fb5b0ba92e336b7f932e8", + "0xa4b330cf3f064f5b131578626ad7043ce2a433b6f175feb0b52d36134a454ca219373fd30d5e5796410e005b69082e47", + "0x86fe5774112403ad83f9c55d58317eeb17ad8e1176d9f2f69c2afb7ed83bc718ed4e0245ceab4b377f5f062dcd4c00e7", + "0x809d152a7e2654c7fd175b57f7928365a521be92e1ed06c05188a95864ddb25f7cab4c71db7d61bbf4cae46f3a1d96ce", + "0xb82d663e55c2a5ada7e169e9b1a87bc1c0177baf1ec1c96559b4cb1c5214ce1ddf2ab8d345014cab6402f3774235cf5a", + "0x86580af86df1bd2c385adb8f9a079e925981b7184db66fc5fe5b14cddb82e7d836b06eaeef14924ac529487b23dae111", + "0xb5f5f4c5c94944ecc804df6ab8687d64e27d988cbfeae1ba7394e0f6adbf778c5881ead7cd8082dd7d68542b9bb4ecd5", + "0xa6016916146c2685c46e8fdd24186394e2d5496e77e08c0c6a709d4cd7dfa97f1efcef94922b89196819076a91ad37b5", + "0xb778e7367ded3b6eab53d5fc257f7a87e8faf74a593900f2f517220add2125be3f6142022660d8181df8d164ad9441ce", + "0x8581b2d36abe6f553add4d24be761bec1b8efaa2929519114346615380b3c55b59e6ad86990e312f7e234d0203bdf59b", + "0x9917e74fd45c3f71a829ff5498a7f6b5599b48c098dda2339bf04352bfc7f368ccf1a407f5835901240e76452ae807d7", + "0xafd196ce6f9335069138fd2e3d133134da253978b4ce373152c0f26affe77a336505787594022e610f8feb722f7cc1fb", + "0xa477491a1562e329764645e8f24d8e228e5ef28c9f74c6b5b3abc4b6a562c15ffb0f680d372aed04d9e1bf944dece7be", + "0x9767440d58c57d3077319d3a330e5322b9ba16981ec74a5a14d53462eab59ae7fd2b14025bfc63b268862094acb444e6", + "0x80986d921be3513ef69264423f351a61cb48390c1be8673aee0f089076086aaebea7ebe268fd0aa7182695606116f679", + "0xa9554c5c921c07b450ee04e34ec58e054ac1541b26ce2ce5a393367a97348ba0089f53db6660ad76b60278b66fd12e3e", + "0x95097e7d2999b3e84bf052c775581cf361325325f4a50192521d8f4693c830bed667d88f482dc1e3f833aa2bd22d2cbf", + "0x9014c91d0f85aefd28436b5228c12f6353c055a9326c7efbf5e071e089e2ee7c070fcbc84c5fafc336cbb8fa6fec1ca1", + "0x90f57ba36ee1066b55d37384942d8b57ae00f3cf9a3c1d6a3dfee1d1af42d4b5fa9baeb0cd7e46687d1d6d090ddb931d", + "0x8e4b1db12fd760a17214c9e47f1fce6e43c0dbb4589a827a13ac61aaae93759345697bb438a00edab92e0b7b62414683", + "0x8022a959a513cdc0e9c705e0fc04eafd05ff37c867ae0f31f6d01cddd5df86138a426cab2ff0ac8ff03a62e20f7e8f51", + "0x914e9a38829834c7360443b8ed86137e6f936389488eccf05b4b4db7c9425611705076ecb3f27105d24b85c852be7511", + "0x957fb10783e2bd0db1ba66b18e794df710bc3b2b05776be146fa5863c15b1ebdd39747b1a95d9564e1772cdfc4f37b8a", + "0xb6307028444daed8ed785ac9d0de76bc3fe23ff2cc7e48102553613bbfb5afe0ebe45e4212a27021c8eb870721e62a1f", + "0x8f76143597777d940b15a01b39c5e1b045464d146d9a30a6abe8b5d3907250e6c7f858ff2308f8591e8b0a7b3f3c568a", + "0x96163138ac0ce5fd00ae9a289648fd9300a0ca0f63a88481d703ecd281c06a52a3b5178e849e331f9c85ca4ba398f4cc", + "0xa63ef47c3e18245b0482596a09f488a716df3cbd0f9e5cfabed0d742843e65db8961c556f45f49762f3a6ac8b627b3ef", + "0x8cb595466552e7c4d42909f232d4063e0a663a8ef6f6c9b7ce3a0542b2459cde04e0e54c7623d404acb5b82775ac04f6", + "0xb47fe69960eb45f399368807cff16d941a5a4ebad1f5ec46e3dc8a2e4d598a7e6114d8f0ca791e9720fd786070524e2b", + "0x89eb5ff83eea9df490e5beca1a1fbbbbcf7184a37e2c8c91ede7a1e654c81e8cd41eceece4042ea7918a4f4646b67fd6", + "0xa84f5d155ed08b9054eecb15f689ba81e44589e6e7207a99790c598962837ca99ec12344105b16641ca91165672f7153", + "0xa6cc8f25c2d5b2d2f220ec359e6a37a52b95fa6af6e173c65e7cd55299eff4aa9e6d9e6f2769e6459313f1f2aecb0fab", + "0xafcde944411f017a9f7979755294981e941cc41f03df5e10522ef7c7505e5f1babdd67b3bf5258e8623150062eb41d9b", + "0x8fab39f39c0f40182fcd996ade2012643fe7731808afbc53f9b26900b4d4d1f0f5312d9d40b3df8baa4739970a49c732", + "0xae193af9726da0ebe7df1f9ee1c4846a5b2a7621403baf8e66c66b60f523e719c30c6b4f897bb14b27d3ff3da8392eeb", + "0x8ac5adb82d852eba255764029f42e6da92dcdd0e224d387d1ef94174038db9709ac558d90d7e7c57ad4ce7f89bbfc38c", + "0xa2066b3458fdf678ee487a55dd5bfb74fde03b54620cb0e25412a89ee28ad0d685e309a51e3e4694be2fa6f1593a344c", + "0x88d031745dd0ae07d61a15b594be5d4b2e2a29e715d081649ad63605e3404b0c3a5353f0fd9fad9c05c18e93ce674fa1", + "0x8283cfb0ef743a043f2b77ecaeba3005e2ca50435585b5dd24777ee6bce12332f85e21b446b536da38508807f0f07563", + "0xb376de22d5f6b0af0b59f7d9764561f4244cf8ffe22890ecd3dcf2ff1832130c9b821e068c9d8773136f4796721e5963", + "0xae3afc50c764f406353965363840bf28ee85e7064eb9d5f0bb3c31c64ab10f48c853e942ee2c9b51bae59651eaa08c2f", + "0x948b204d103917461a01a6c57a88f2d66b476eae5b00be20ec8c747650e864bc8a83aee0aff59cb7584b7a3387e0ee48", + "0x81ab098a082b07f896c5ffd1e4446cb7fb44804cbbf38d125208b233fc82f8ec9a6a8d8dd1c9a1162dc28ffeec0dde50", + "0xa149c6f1312821ced2969268789a3151bdda213451760b397139a028da609c4134ac083169feb0ee423a0acafd10eceb", + "0xb0ac9e27a5dadaf523010f730b28f0ebac01f460d3bbbe277dc9d44218abb5686f4fac89ae462682fef9edbba663520a", + "0x8d0e0073cca273daaaa61b6fc54bfe5a009bc3e20ae820f6c93ba77b19eca517d457e948a2de5e77678e4241807157cb", + "0xad61d3a2edf7c7533a04964b97499503fd8374ca64286dba80465e68fe932e96749b476f458c6fc57cb1a7ca85764d11", + "0x90eb5e121ae46bc01a30881eaa556f46bd8457a4e80787cf634aab355082de34ac57d7f497446468225f7721e68e2a47", + "0x8cdac557de7c42d1f3780e33dec1b81889f6352279be81c65566cdd4952d4c15d79e656cbd46035ab090b385e90245ef", + "0x82b67e61b88b84f4f4d4f65df37b3e3dcf8ec91ea1b5c008fdccd52da643adbe6468a1cfdb999e87d195afe2883a3b46", + "0x8503b467e8f5d6048a4a9b78496c58493a462852cab54a70594ae3fd064cfd0deb4b8f336a262155d9fedcaa67d2f6fd", + "0x8db56c5ac763a57b6ce6832930c57117058e3e5a81532b7d19346346205e2ec614eb1a2ee836ef621de50a7bc9b7f040", + "0xad344699198f3c6e8c0a3470f92aaffc805b76266734414c298e10b5b3797ca53578de7ccb2f458f5e0448203f55282b", + "0x80602032c43c9e2a09154cc88b83238343b7a139f566d64cb482d87436b288a98f1ea244fd3bff8da3c398686a900c14", + "0xa6385bd50ecd548cfb37174cdbb89e10025b5cadaf3cff164c95d7aef5a33e3d6a9bf0c681b9e11db9ef54ebeee2a0c1", + "0xabf2d95f4aa34b0581eb9257a0cc8462b2213941a5deb8ba014283293e8b36613951b61261cc67bbd09526a54cbbff76", + "0xa3d5de52f48df72c289ff713e445991f142390798cd42bd9d9dbefaee4af4f5faf09042d126b975cf6b98711c3072553", + "0x8e627302ff3d686cff8872a1b7c2a57b35f45bf2fc9aa42b049d8b4d6996a662b8e7cbac6597f0cb79b0cc4e29fbf133", + "0x8510702e101b39a1efbf4e504e6123540c34b5689645e70d0bac1ecc1baf47d86c05cef6c4317a4e99b4edaeb53f2d00", + "0xaa173f0ecbcc6088f878f8726d317748c81ebf501bba461f163b55d66099b191ec7c55f7702f351a9c8eb42cfa3280e2", + "0xb560a697eafab695bcef1416648a0a664a71e311ecbe5823ae903bd0ed2057b9d7574b9a86d3fe22aa3e6ddce38ea513", + "0x8df6304a3d9cf40100f3f687575419c998cd77e5cc27d579cf4f8e98642de3609af384a0337d145dd7c5635172d26a71", + "0x8105c7f3e4d30a29151849673853b457c1885c186c132d0a98e63096c3774bc9deb956cf957367e633d0913680bda307", + "0x95373fc22c0917c3c2044ac688c4f29a63ed858a45c0d6d2d0fe97afd6f532dcb648670594290c1c89010ecc69259bef", + "0x8c2fae9bcadab341f49b55230310df93cac46be42d4caa0d42e45104148a91e527af1b4209c0d972448162aed28fab64", + "0xb05a77baab70683f76209626eaefdda2d36a0b66c780a20142d23c55bd479ddd4ad95b24579384b6cf62c8eb4c92d021", + "0x8e6bc6a7ea2755b4aaa19c1c1dee93811fcde514f03485fdc3252f0ab7f032c315614f6336e57cea25dcfb8fb6084eeb", + "0xb656a27d06aade55eadae2ad2a1059198918ea6cc3fd22c0ed881294d34d5ac7b5e4700cc24350e27d76646263b223aa", + "0xa296469f24f6f56da92d713afcd4dd606e7da1f79dc4e434593c53695847eefc81c7c446486c4b3b8c8d00c90c166f14", + "0x87a326f57713ac2c9dffeb3af44b9f3c613a8f952676fc46343299122b47ee0f8d792abaa4b5db6451ced5dd153aabd0", + "0xb689e554ba9293b9c1f6344a3c8fcb6951d9f9eac4a2e2df13de021aade7c186be27500e81388e5b8bcab4c80f220a31", + "0x87ae0aa0aa48eac53d1ca5a7b93917de12db9e40ceabf8fdb40884ae771cfdf095411deef7c9f821af0b7070454a2608", + "0xa71ffa7eae8ace94e6c3581d4cb2ad25d48cbd27edc9ec45baa2c8eb932a4773c3272b2ffaf077b40f76942a1f3af7f2", + "0x94c218c91a9b73da6b7a495b3728f3028df8ad9133312fc0c03e8c5253b7ccb83ed14688fd4602e2fd41f29a0bc698bd", + "0xae1e77b90ca33728af07a4c03fb2ef71cd92e2618e7bf8ed4d785ce90097fc4866c29999eb84a6cf1819d75285a03af2", + "0xb7a5945b277dab9993cf761e838b0ac6eaa903d7111fca79f9fde3d4285af7a89bf6634a71909d095d7619d913972c9c", + "0x8c43b37be02f39b22029b20aca31bff661abce4471dca88aa3bddefd9c92304a088b2dfc8c4795acc301ca3160656af2", + "0xb32e5d0fba024554bd5fe8a793ebe8003335ddd7f585876df2048dcf759a01285fecb53daae4950ba57f3a282a4d8495", + "0x85ea7fd5e10c7b659df5289b2978b2c89e244f269e061b9a15fcab7983fc1962b63546e82d5731c97ec74b6804be63ef", + "0x96b89f39181141a7e32986ac02d7586088c5a9662cec39843f397f3178714d02f929af70630c12cbaba0268f8ba2d4fa", + "0x929ab1a2a009b1eb37a2817c89696a06426529ebe3f306c586ab717bd34c35a53eca2d7ddcdef36117872db660024af9", + "0xa696dccf439e9ca41511e16bf3042d7ec0e2f86c099e4fc8879d778a5ea79e33aa7ce96b23dc4332b7ba26859d8e674d", + "0xa8fe69a678f9a194b8670a41e941f0460f6e2dbc60470ab4d6ae2679cc9c6ce2c3a39df2303bee486dbfde6844e6b31a", + "0x95f58f5c82de2f2a927ca99bf63c9fc02e9030c7e46d0bf6b67fe83a448d0ae1c99541b59caf0e1ccab8326231af09a5", + "0xa57badb2c56ca2c45953bd569caf22968f76ed46b9bac389163d6fe22a715c83d5e94ae8759b0e6e8c2f27bff7748f3f", + "0x868726fd49963b24acb5333364dffea147e98f33aa19c7919dc9aca0fd26661cfaded74ede7418a5fadbe7f5ae67b67b", + "0xa8d8550dcc64d9f1dd7bcdab236c4122f2b65ea404bb483256d712c7518f08bb028ff8801f1da6aed6cbfc5c7062e33b", + "0x97e25a87dae23155809476232178538d4bc05d4ff0882916eb29ae515f2a62bfce73083466cc0010ca956aca200aeacc", + "0xb4ea26be3f4bd04aa82d7c4b0913b97bcdf5e88b76c57eb1a336cbd0a3eb29de751e1bc47c0e8258adec3f17426d0c71", + "0x99ee555a4d9b3cf2eb420b2af8e3bc99046880536116d0ce7193464ac40685ef14e0e3c442f604e32f8338cb0ef92558", + "0x8c64efa1da63cd08f319103c5c7a761221080e74227bbc58b8fb35d08aa42078810d7af3e60446cbaff160c319535648", + "0x8d9fd88040076c28420e3395cbdfea402e4077a3808a97b7939d49ecbcf1418fe50a0460e1c1b22ac3f6e7771d65169a", + "0xae3c19882d7a9875d439265a0c7003c8d410367627d21575a864b9cb4918de7dbdb58a364af40c5e045f3df40f95d337", + "0xb4f7bfacab7b2cafe393f1322d6dcc6f21ffe69cd31edc8db18c06f1a2b512c27bd0618091fd207ba8df1808e9d45914", + "0x94f134acd0007c623fb7934bcb65ef853313eb283a889a3ffa79a37a5c8f3665f3d5b4876bc66223610c21dc9b919d37", + "0xaa15f74051171daacdc1f1093d3f8e2d13da2833624b80a934afec86fc02208b8f55d24b7d66076444e7633f46375c6a", + "0xa32d6bb47ef9c836d9d2371807bafbbbbb1ae719530c19d6013f1d1f813c49a60e4fa51d83693586cba3a840b23c0404", + "0xb61b3599145ea8680011aa2366dc511a358b7d67672d5b0c5be6db03b0efb8ca5a8294cf220ea7409621f1664e00e631", + "0x859cafc3ee90b7ececa1ed8ef2b2fc17567126ff10ca712d5ffdd16aa411a5a7d8d32c9cab1fbf63e87dce1c6e2f5f53", + "0xa2fef1b0b2874387010e9ae425f3a9676d01a095d017493648bcdf3b31304b087ccddb5cf76abc4e1548b88919663b6b", + "0x939e18c73befc1ba2932a65ede34c70e4b91e74cc2129d57ace43ed2b3af2a9cc22a40fbf50d79a63681b6d98852866d", + "0xb3b4259d37b1b14aee5b676c9a0dd2d7f679ab95c120cb5f09f9fbf10b0a920cb613655ddb7b9e2ba5af4a221f31303c", + "0x997255fe51aaca6e5a9cb3359bcbf25b2bb9e30649bbd53a8a7c556df07e441c4e27328b38934f09c09d9500b5fabf66", + "0xabb91be2a2d860fd662ed4f1c6edeefd4da8dc10e79251cf87f06029906e7f0be9b486462718f0525d5e049472692cb7", + "0xb2398e593bf340a15f7801e1d1fbda69d93f2a32a889ec7c6ae5e8a37567ac3e5227213c1392ee86cfb3b56ec2787839", + "0x8ddf10ccdd72922bed36829a36073a460c2118fc7a56ff9c1ac72581c799b15c762cb56cb78e3d118bb9f6a7e56cb25e", + "0x93e6bc0a4708d16387cacd44cf59363b994dc67d7ada7b6d6dbd831c606d975247541b42b2a309f814c1bfe205681fc6", + "0xb93fc35c05998cffda2978e12e75812122831523041f10d52f810d34ff71944979054b04de0117e81ddf5b0b4b3e13c0", + "0x92221631c44d60d68c6bc7b287509f37ee44cbe5fdb6935cee36b58b17c7325098f98f7910d2c3ca5dc885ad1d6dabc7", + "0xa230124424a57fad3b1671f404a94d7c05f4c67b7a8fbacfccea28887b78d7c1ed40b92a58348e4d61328891cd2f6cee", + "0xa6a230edb8518a0f49d7231bc3e0bceb5c2ac427f045819f8584ba6f3ae3d63ed107a9a62aad543d7e1fcf1f20605706", + "0x845be1fe94223c7f1f97d74c49d682472585d8f772762baad8a9d341d9c3015534cc83d102113c51a9dea2ab10d8d27b", + "0xb44262515e34f2db597c8128c7614d33858740310a49cdbdf9c8677c5343884b42c1292759f55b8b4abc4c86e4728033", + "0x805592e4a3cd07c1844bc23783408310accfdb769cca882ad4d07d608e590a288b7370c2cb327f5336e72b7083a0e30f", + "0x95153e8b1140df34ee864f4ca601cb873cdd3efa634af0c4093fbaede36f51b55571ab271e6a133020cd34db8411241f", + "0x82878c1285cfa5ea1d32175c9401f3cc99f6bb224d622d3fd98cc7b0a27372f13f7ab463ce3a33ec96f9be38dbe2dfe3", + "0xb7588748f55783077c27fc47d33e20c5c0f5a53fc0ac10194c003aa09b9f055d08ec971effa4b7f760553997a56967b3", + "0xb36b4de6d1883b6951f59cfae381581f9c6352fcfcf1524fccdab1571a20f80441d9152dc6b48bcbbf00371337ca0bd5", + "0x89c5523f2574e1c340a955cbed9c2f7b5fbceb260cb1133160dabb7d41c2f613ec3f6e74bbfab3c4a0a6f0626dbe068f", + "0xa52f58cc39f968a9813b1a8ddc4e83f4219e4dd82c7aa1dd083bea7edf967151d635aa9597457f879771759b876774e4", + "0x8300a67c2e2e123f89704abfde095463045dbd97e20d4c1157bab35e9e1d3d18f1f4aaba9cbe6aa2d544e92578eaa1b6", + "0xac6a7f2918768eb6a43df9d3a8a04f8f72ee52f2e91c064c1c7d75cad1a3e83e5aba9fe55bb94f818099ac91ccf2e961", + "0x8d64a2b0991cf164e29835c8ddef6069993a71ec2a7de8157bbfa2e00f6367be646ed74cbaf524f0e9fe13fb09fa15fd", + "0x8b2ffe5a545f9f680b49d0a9797a4a11700a2e2e348c34a7a985fc278f0f12def6e06710f40f9d48e4b7fbb71e072229", + "0x8ab8f71cd337fa19178924e961958653abf7a598e3f022138b55c228440a2bac4176cea3aea393549c03cd38a13eb3fc", + "0x8419d28318c19ea4a179b7abb43669fe96347426ef3ac06b158d79c0acf777a09e8e770c2fb10e14b3a0421705990b23", + "0x8bacdac310e1e49660359d0a7a17fe3d334eb820e61ae25e84cb52f863a2f74cbe89c2e9fc3283745d93a99b79132354", + "0xb57ace3fa2b9f6b2db60c0d861ace7d7e657c5d35d992588aeed588c6ce3a80b6f0d49f8a26607f0b17167ab21b675e4", + "0x83e265cde477f2ecc164f49ddc7fb255bb05ff6adc347408353b7336dc3a14fdedc86d5a7fb23f36b8423248a7a67ed1", + "0xa60ada971f9f2d79d436de5d3d045f5ab05308cae3098acaf5521115134b2a40d664828bb89895840db7f7fb499edbc5", + "0xa63eea12efd89b62d3952bf0542a73890b104dd1d7ff360d4755ebfa148fd62de668edac9eeb20507967ea37fb220202", + "0xa0275767a270289adc991cc4571eff205b58ad6d3e93778ddbf95b75146d82517e8921bd0d0564e5b75fa0ccdab8e624", + "0xb9b03fd3bf07201ba3a039176a965d736b4ef7912dd9e9bf69fe1b57c330a6aa170e5521fe8be62505f3af81b41d7806", + "0xa95f640e26fb1106ced1729d6053e41a16e4896acac54992279ff873e5a969aad1dcfa10311e28b8f409ac1dab7f03bb", + "0xb144778921742418053cb3c70516c63162c187f00db2062193bb2c14031075dbe055d020cde761b26e8c58d0ea6df2c1", + "0x8432fbb799e0435ef428d4fefc309a05dd589bce74d7a87faf659823e8c9ed51d3e42603d878e80f439a38be4321c2fa", + "0xb08ddef14e42d4fd5d8bf39feb7485848f0060d43b51ed5bdda39c05fe154fb111d29719ee61a23c392141358c0cfcff", + "0x8ae3c5329a5e025b86b5370e06f5e61177df4bda075856fade20a17bfef79c92f54ed495f310130021ba94fb7c33632b", + "0x92b6d3c9444100b4d7391febfc1dddaa224651677c3695c47a289a40d7a96d200b83b64e6d9df51f534564f272a2c6c6", + "0xb432bc2a3f93d28b5e506d68527f1efeb2e2570f6be0794576e2a6ef9138926fdad8dd2eabfa979b79ab7266370e86bc", + "0x8bc315eacedbcfc462ece66a29662ca3dcd451f83de5c7626ef8712c196208fb3d8a0faf80b2e80384f0dd9772f61a23", + "0xa72375b797283f0f4266dec188678e2b2c060dfed5880fc6bb0c996b06e91a5343ea2b695adaab0a6fd183b040b46b56", + "0xa43445036fbaa414621918d6a897d3692fdae7b2961d87e2a03741360e45ebb19fcb1703d23f1e15bb1e2babcafc56ac", + "0xb9636b2ffe305e63a1a84bd44fb402442b1799bd5272638287aa87ca548649b23ce8ce7f67be077caed6aa2dbc454b78", + "0x99a30bf0921d854c282b83d438a79f615424f28c2f99d26a05201c93d10378ab2cd94a792b571ddae5d4e0c0013f4006", + "0x8648e3c2f93d70b392443be116b48a863e4b75991bab5db656a4ef3c1e7f645e8d536771dfe4e8d1ceda3be8d32978b0", + "0xab50dc9e6924c1d2e9d2e335b2d679fc7d1a7632e84964d3bac0c9fe57e85aa5906ec2e7b0399d98ddd022e9b19b5904", + "0xab729328d98d295f8f3272afaf5d8345ff54d58ff9884da14f17ecbdb7371857fdf2f3ef58080054e9874cc919b46224", + "0x83fa5da7592bd451cad3ad7702b4006332b3aae23beab4c4cb887fa6348317d234bf62a359e665b28818e5410c278a09", + "0x8bdbff566ae9d368f114858ef1f009439b3e9f4649f73efa946e678d6c781d52c69af195df0a68170f5f191b2eac286b", + "0x91245e59b4425fd4edb2a61d0d47c1ccc83d3ced8180de34887b9655b5dcda033d48cde0bdc3b7de846d246c053a02e8", + "0xa2cb00721e68f1cad8933947456f07144dc69653f96ceed845bd577d599521ba99cdc02421118971d56d7603ed118cbf", + "0xaf8cd66d303e808b22ec57860dd909ca64c27ec2c60e26ffecfdc1179d8762ffd2739d87b43959496e9fee4108df71df", + "0x9954136812dffcd5d3f167a500e7ab339c15cfc9b3398d83f64b0daa3dd5b9a851204f424a3493b4e326d3de81e50a62", + "0x93252254d12511955f1aa464883ad0da793f84d900fea83e1df8bca0f2f4cf5b5f9acbaec06a24160d33f908ab5fea38", + "0x997cb55c26996586ba436a95566bd535e9c22452ca5d2a0ded2bd175376557fa895f9f4def4519241ff386a063f2e526", + "0xa12c78ad451e0ac911260ade2927a768b50cb4125343025d43474e7f465cdc446e9f52a84609c5e7e87ae6c9b3f56cda", + "0xa789d4ca55cbba327086563831b34487d63d0980ba8cf55197c016702ed6da9b102b1f0709ce3da3c53ff925793a3d73", + "0xa5d76acbb76741ce85be0e655b99baa04f7f587347947c0a30d27f8a49ae78cce06e1cde770a8b618d3db402be1c0c4b", + "0x873c0366668c8faddb0eb7c86f485718d65f8c4734020f1a18efd5fa123d3ea8a990977fe13592cd01d17e60809cb5ff", + "0xb659b71fe70f37573ff7c5970cc095a1dc0da3973979778f80a71a347ef25ad5746b2b9608bad4ab9a4a53a4d7df42d7", + "0xa34cbe05888e5e5f024a2db14cb6dcdc401a9cbd13d73d3c37b348f68688f87c24ca790030b8f84fef9e74b4eab5e412", + "0x94ce8010f85875c045b0f014db93ef5ab9f1f6842e9a5743dce9e4cb872c94affd9e77c1f1d1ab8b8660b52345d9acb9", + "0xadefa9b27a62edc0c5b019ddd3ebf45e4de846165256cf6329331def2e088c5232456d3de470fdce3fa758bfdd387512", + "0xa6b83821ba7c1f83cc9e4529cf4903adb93b26108e3d1f20a753070db072ad5a3689643144bdd9c5ea06bb9a7a515cd0", + "0xa3a9ddedc2a1b183eb1d52de26718151744db6050f86f3580790c51d09226bf05f15111691926151ecdbef683baa992c", + "0xa64bac89e7686932cdc5670d07f0b50830e69bfb8c93791c87c7ffa4913f8da881a9d8a8ce8c1a9ce5b6079358c54136", + "0xa77b5a63452cb1320b61ab6c7c2ef9cfbcade5fd4727583751fb2bf3ea330b5ca67757ec1f517bf4d503ec924fe32fbd", + "0x8746fd8d8eb99639d8cd0ca34c0d9c3230ed5a312aab1d3d925953a17973ee5aeb66e68667e93caf9cb817c868ea8f3d", + "0x88a2462a26558fc1fbd6e31aa8abdc706190a17c27fdc4217ffd2297d1b1f3321016e5c4b2384c5454d5717dc732ed03", + "0xb78893a97e93d730c8201af2e0d3b31cb923d38dc594ffa98a714e627c473d42ea82e0c4d2eeb06862ee22a9b2c54588", + "0x920cc8b5f1297cf215a43f6fc843e379146b4229411c44c0231f6749793d40f07b9af7699fd5d21fd69400b97febe027", + "0xa0f0eafce1e098a6b58c7ad8945e297cd93aaf10bc55e32e2e32503f02e59fc1d5776936577d77c0b1162cb93b88518b", + "0x98480ba0064e97a2e7a6c4769b4d8c2a322cfc9a3b2ca2e67e9317e2ce04c6e1108169a20bd97692e1cb1f1423b14908", + "0x83dbbb2fda7e287288011764a00b8357753a6a44794cc8245a2275237f11affdc38977214e463ad67aec032f3dfa37e9", + "0x86442fff37598ce2b12015ff19b01bb8a780b40ad353d143a0f30a06f6d23afd5c2b0a1253716c855dbf445cc5dd6865", + "0xb8a4c60c5171189414887847b9ed9501bff4e4c107240f063e2d254820d2906b69ef70406c585918c4d24f1dd052142b", + "0x919f33a98e84015b2034b57b5ffe9340220926b2c6e45f86fd79ec879dbe06a148ae68b77b73bf7d01bd638a81165617", + "0x95c13e78d89474a47fbc0664f6f806744b75dede95a479bbf844db4a7f4c3ae410ec721cb6ffcd9fa9c323da5740d5ae", + "0xab7151acc41fffd8ec6e90387700bcd7e1cde291ea669567295bea1b9dd3f1df2e0f31f3588cd1a1c08af8120aca4921", + "0x80e74c5c47414bd6eeef24b6793fb1fa2d8fb397467045fcff887c52476741d5bc4ff8b6d3387cb53ad285485630537f", + "0xa296ad23995268276aa351a7764d36df3a5a3cffd7dbeddbcea6b1f77adc112629fdeffa0918b3242b3ccd5e7587e946", + "0x813d2506a28a2b01cb60f49d6bd5e63c9b056aa56946faf2f33bd4f28a8d947569cfead3ae53166fc65285740b210f86", + "0x924b265385e1646287d8c09f6c855b094daaee74b9e64a0dddcf9ad88c6979f8280ba30c8597b911ef58ddb6c67e9fe3", + "0x8d531513c70c2d3566039f7ca47cd2352fd2d55b25675a65250bdb8b06c3843db7b2d29c626eed6391c238fc651cf350", + "0x82b338181b62fdc81ceb558a6843df767b6a6e3ceedc5485664b4ea2f555904b1a45fbb35f6cf5d96f27da10df82a325", + "0x92e62faaedea83a37f314e1d3cb4faaa200178371d917938e59ac35090be1db4b4f4e0edb78b9c991de202efe4f313d8", + "0x99d645e1b642c2dc065bac9aaa0621bc648c9a8351efb6891559c3a41ba737bd155fb32d7731950514e3ecf4d75980e4", + "0xb34a13968b9e414172fb5d5ece9a39cf2eb656128c3f2f6cc7a9f0c69c6bae34f555ecc8f8837dc34b5e470e29055c78", + "0xa2a0bb7f3a0b23a2cbc6585d59f87cd7e56b2bbcb0ae48f828685edd9f7af0f5edb4c8e9718a0aaf6ef04553ba71f3b7", + "0x8e1a94bec053ed378e524b6685152d2b52d428266f2b6eadd4bcb7c4e162ed21ab3e1364879673442ee2162635b7a4d8", + "0x9944adaff14a85eab81c73f38f386701713b52513c4d4b838d58d4ffa1d17260a6d056b02334850ea9a31677c4b078bd", + "0xa450067c7eceb0854b3eca3db6cf38669d72cb7143c3a68787833cbca44f02c0be9bfbe082896f8a57debb13deb2afb1", + "0x8be4ad3ac9ef02f7df09254d569939757101ee2eda8586fefcd8c847adc1efe5bdcb963a0cafa17651befaafb376a531", + "0x90f6de91ea50255f148ac435e08cf2ac00c772a466e38155bd7e8acf9197af55662c7b5227f88589b71abe9dcf7ba343", + "0x86e5a24f0748b106dee2d4d54e14a3b0af45a96cbee69cac811a4196403ebbee17fd24946d7e7e1b962ac7f66dbaf610", + "0xafdd96fbcda7aa73bf9eeb2292e036c25753d249caee3b9c013009cc22e10d3ec29e2aa6ddbb21c4e949b0c0bccaa7f4", + "0xb5a4e7436d5473647c002120a2cb436b9b28e27ad4ebdd7c5f122b91597c507d256d0cbd889d65b3a908531936e53053", + "0xb632414c3da704d80ac2f3e5e0e9f18a3637cdc2ebeb613c29300745582427138819c4e7b0bec3099c1b8739dac1807b", + "0xa28df1464d3372ce9f37ef1db33cc010f752156afae6f76949d98cd799c0cf225c20228ae86a4da592d65f0cffe3951b", + "0x898b93d0a31f7d3f11f253cb7a102db54b669fd150da302d8354d8e02b1739a47cb9bd88015f3baf12b00b879442464e", + "0x96fb88d89a12049091070cb0048a381902965e67a8493e3991eaabe5d3b7ff7eecd5c94493a93b174df3d9b2c9511755", + "0xb899cb2176f59a5cfba3e3d346813da7a82b03417cad6342f19cc8f12f28985b03bf031e856a4743fd7ebe16324805b0", + "0xa60e2d31bc48e0c0579db15516718a03b73f5138f15037491f4dae336c904e312eda82d50862f4debd1622bb0e56d866", + "0x979fc8b987b5cef7d4f4b58b53a2c278bd25a5c0ea6f41c715142ea5ff224c707de38451b0ad3aa5e749aa219256650a", + "0xb2a75bff18e1a6b9cf2a4079572e41205741979f57e7631654a3c0fcec57c876c6df44733c9da3d863db8dff392b44a3", + "0xb7a0f0e811222c91e3df98ff7f286b750bc3b20d2083966d713a84a2281744199e664879401e77470d44e5a90f3e5181", + "0x82b74ba21c9d147fbc338730e8f1f8a6e7fc847c3110944eb17a48bea5e06eecded84595d485506d15a3e675fd0e5e62", + "0xa7f44eef817d5556f0d1abcf420301217d23c69dd2988f44d91ea1f1a16c322263cbacd0f190b9ba22b0f141b9267b4f", + "0xaadb68164ede84fc1cb3334b3194d84ba868d5a88e4c9a27519eef4923bc4abf81aab8114449496c073c2a6a0eb24114", + "0xb5378605fabe9a8c12a5dc55ef2b1de7f51aedb61960735c08767a565793cea1922a603a6983dc25f7cea738d0f7c40d", + "0xa97a4a5cd8d51302e5e670aee78fe6b5723f6cc892902bbb4f131e82ca1dfd5de820731e7e3367fb0c4c1922a02196e3", + "0x8bdfeb15c29244d4a28896f2b2cb211243cd6a1984a3f5e3b0ebe5341c419beeab3304b390a009ffb47588018034b0ea", + "0xa9af3022727f2aa2fca3b096968e97edad3f08edcbd0dbca107b892ae8f746a9c0485e0d6eb5f267999b23a845923ed0", + "0x8e7594034feef412f055590fbb15b6322dc4c6ab7a4baef4685bd13d71a83f7d682b5781bdfa0d1c659489ce9c2b8000", + "0x84977ca6c865ebee021c58106c1a4ad0c745949ecc5332948002fd09bd9b890524878d0c29da96fd11207621136421fe", + "0x8687551a79158e56b2375a271136756313122132a6670fa51f99a1b5c229ed8eea1655a734abae13228b3ebfd2a825dd", + "0xa0227d6708979d99edfc10f7d9d3719fd3fc68b0d815a7185b60307e4c9146ad2f9be2b8b4f242e320d4288ceeb9504c", + "0x89f75583a16735f9dd8b7782a130437805b34280ccea8dac6ecaee4b83fe96947e7b53598b06fecfffdf57ffc12cc445", + "0xa0056c3353227f6dd9cfc8e3399aa5a8f1d71edf25d3d64c982910f50786b1e395c508d3e3727ac360e3e040c64b5298", + "0xb070e61a6d813626144b312ded1788a6d0c7cec650a762b2f8df6e4743941dd82a2511cd956a3f141fc81e15f4e092da", + "0xb4e6db232e028a1f989bb5fc13416711f42d389f63564d60851f009dcffac01acfd54efa307aa6d4c0f932892d4e62b0", + "0x89b5991a67db90024ddd844e5e1a03ef9b943ad54194ae0a97df775dde1addf31561874f4e40fbc37a896630f3bbda58", + "0xad0e8442cb8c77d891df49cdb9efcf2b0d15ac93ec9be1ad5c3b3cca1f4647b675e79c075335c1f681d56f14dc250d76", + "0xb5d55a6ae65bb34dd8306806cb49b5ccb1c83a282ee47085cf26c4e648e19a52d9c422f65c1cd7e03ca63e926c5e92ea", + "0xb749501347e5ec07e13a79f0cb112f1b6534393458b3678a77f02ca89dca973fa7b30e55f0b25d8b92b97f6cb0120056", + "0x94144b4a3ffc5eec6ba35ce9c245c148b39372d19a928e236a60e27d7bc227d18a8cac9983851071935d8ffb64b3a34f", + "0x92bb4f9f85bc8c028a3391306603151c6896673135f8a7aefedd27acb322c04ef5dac982fc47b455d6740023e0dd3ea3", + "0xb9633a4a101461a782fc2aa092e9dbe4e2ad00987578f18cd7cf0021a909951d60fe79654eb7897806795f93c8ff4d1c", + "0x809f0196753024821b48a016eca5dbb449a7c55750f25981bb7a4b4c0e0846c09b8f6128137905055fc43a3f0deb4a74", + "0xa27dc9cdd1e78737a443570194a03d89285576d3d7f3a3cf15cc55b3013e42635d4723e2e8fe1d0b274428604b630db9", + "0x861f60f0462e04cd84924c36a28163def63e777318d00884ab8cb64c8df1df0bce5900342163edb60449296484a6c5bf", + "0xb7bc23fb4e14af4c4704a944253e760adefeca8caee0882b6bbd572c84434042236f39ae07a8f21a560f486b15d82819", + "0xb9a6eb492d6dd448654214bd01d6dc5ff12067a11537ab82023fc16167507ee25eed2c91693912f4155d1c07ed9650b3", + "0x97678af29c68f9a5e213bf0fb85c265303714482cfc4c2c00b4a1e8a76ed08834ee6af52357b143a1ca590fb0265ea5a", + "0x8a15b499e9eca5b6cac3070b5409e8296778222018ad8b53a5d1f6b70ad9bb10c68a015d105c941ed657bf3499299e33", + "0xb487fefede2e8091f2c7bfe85770db2edff1db83d4effe7f7d87bff5ab1ace35e9b823a71adfec6737fede8d67b3c467", + "0x8b51b916402aa2c437fce3bcad6dad3be8301a1a7eab9d163085b322ffb6c62abf28637636fe6114573950117fc92898", + "0xb06a2106d031a45a494adec0881cb2f82275dff9dcdd2bc16807e76f3bec28a6734edd3d54f0be8199799a78cd6228ad", + "0xaf0a185391bbe2315eb97feac98ad6dd2e5d931d012c621abd6e404a31cc188b286fef14871762190acf086482b2b5e2", + "0x8e78ee8206506dd06eb7729e32fceda3bebd8924a64e4d8621c72e36758fda3d0001af42443851d6c0aea58562870b43", + "0xa1ba52a569f0461aaf90b49b92be976c0e73ec4a2c884752ee52ffb62dd137770c985123d405dfb5de70692db454b54a", + "0x8d51b692fa1543c51f6b62b9acb8625ed94b746ef96c944ca02859a4133a5629da2e2ce84e111a7af8d9a5b836401c64", + "0xa7a20d45044cf6492e0531d0b8b26ffbae6232fa05a96ed7f06bdb64c2b0f5ca7ec59d5477038096a02579e633c7a3ff", + "0x84df867b98c53c1fcd4620fef133ee18849c78d3809d6aca0fb6f50ff993a053a455993f216c42ab6090fa5356b8d564", + "0xa7227c439f14c48e2577d5713c97a5205feb69acb0b449152842e278fa71e8046adfab468089c8b2288af1fc51fa945b", + "0x855189b3a105670779997690876dfaa512b4a25a24931a912c2f0f1936971d2882fb4d9f0b3d9daba77eaf660e9d05d5", + "0xb5696bd6706de51c502f40385f87f43040a5abf99df705d6aac74d88c913b8ecf7a99a63d7a37d9bdf3a941b9e432ff5", + "0xab997beb0d6df9c98d5b49864ef0b41a2a2f407e1687dfd6089959757ba30ed02228940b0e841afe6911990c74d536c4", + "0xb36b65f85546ebfdbe98823d5555144f96b4ab39279facd19c0de3b8919f105ba0315a0784dce4344b1bc62d8bb4a5a3", + "0xb8371f0e4450788720ac5e0f6cd3ecc5413d33895083b2c168d961ec2b5c3de411a4cc0712481cbe8df8c2fa1a7af006", + "0x98325d8026b810a8b7a114171ae59a57e8bbc9848e7c3df992efc523621729fd8c9f52114ce01d7730541a1ada6f1df1", + "0x8d0e76dbd37806259486cd9a31bc8b2306c2b95452dc395546a1042d1d17863ef7a74c636b782e214d3aa0e8d717f94a", + "0xa4e15ead76da0214d702c859fb4a8accdcdad75ed08b865842bd203391ec4cba2dcc916455e685f662923b96ee0c023f", + "0x8618190972086ebb0c4c1b4a6c94421a13f378bc961cc8267a301de7390c5e73c3333864b3b7696d81148f9d4843fd02", + "0x85369d6cc7342e1aa15b59141517d8db8baaaeb7ab9670f3ba3905353948d575923d283b7e5a05b13a30e7baf1208a86", + "0x87c51ef42233c24a6da901f28c9a075d9ba3c625687c387ad6757b72ca6b5a8885e6902a3082da7281611728b1e45f26", + "0xaa6348a4f71927a3106ad0ea8b02fc8d8c65531e4ab0bd0a17243e66f35afe252e40ab8eef9f13ae55a72566ffdaff5c", + "0x96a3bc976e9d03765cc3fee275fa05b4a84c94fed6b767e23ca689394501e96f56f7a97cffddc579a6abff632bf153be", + "0x97dbf96c6176379fdb2b888be4e757b2bca54e74124bd068d3fa1dbd82a011bbeb75079da38e0cd22a761fe208ecad9b", + "0xb70cf0a1d14089a4129ec4e295313863a59da8c7e26bf74cc0e704ed7f0ee4d7760090d0ddf7728180f1bf2c5ac64955", + "0x882d664714cc0ffe53cbc9bef21f23f3649824f423c4dbad1f893d22c4687ab29583688699efc4d5101aa08b0c3e267a", + "0x80ecb7cc963e677ccaddbe3320831dd6ee41209acf4ed41b16dc4817121a3d86a1aac9c4db3d8c08a55d28257088af32", + "0xa25ba667d832b145f9ce18c3f9b1bd00737aa36db020e1b99752c8ef7d27c6c448982bd8d352e1b6df266b8d8358a8d5", + "0x83734841c13dee12759d40bdd209b277e743b0d08cc0dd1e0b7afd2d65bfa640400eefcf6be4a52e463e5b3d885eeac6", + "0x848d16505b04804afc773aebabb51b36fd8aacfbb0e09b36c0d5d57df3c0a3b92f33e7d5ad0a7006ec46ebb91df42b8c", + "0x909a8d793f599e33bb9f1dc4792a507a97169c87cd5c087310bc05f30afcd247470b4b56dec59894c0fb1d48d39bb54e", + "0x8e558a8559df84a1ba8b244ece667f858095c50bb33a5381e60fcc6ba586b69693566d8819b4246a27287f16846c1dfa", + "0x84d6b69729f5aaa000cd710c2352087592cfbdf20d5e1166977e195818e593fa1a50d1e04566be23163a2523dc1612f1", + "0x9536d262b7a42125d89f4f32b407d737ba8d9242acfc99d965913ab3e043dcac9f7072a43708553562cac4cba841df30", + "0x9598548923ca119d6a15fd10861596601dd1dedbcccca97bb208cdc1153cf82991ea8cc17686fbaa867921065265970c", + "0xb87f2d4af6d026e4d2836bc3d390a4a18e98a6e386282ce96744603bab74974272e97ac2da281afa21885e2cbb3a8001", + "0x991ece62bf07d1a348dd22191868372904b9f8cf065ae7aa4e44fd24a53faf6d851842e35fb472895963aa1992894918", + "0xa8c53dea4c665b30e51d22ca6bc1bc78aaf172b0a48e64a1d4b93439b053877ec26cb5221c55efd64fa841bbf7d5aff4", + "0x93487ec939ed8e740f15335b58617c3f917f72d07b7a369befd479ae2554d04deb240d4a14394b26192efae4d2f4f35d", + "0xa44793ab4035443f8f2968a40e043b4555960193ffa3358d22112093aadfe2c136587e4139ffd46d91ed4107f61ea5e0", + "0xb13fe033da5f0d227c75927d3dacb06dbaf3e1322f9d5c7c009de75cdcba5e308232838785ab69a70f0bedea755e003f", + "0x970a29b075faccd0700fe60d1f726bdebf82d2cc8252f4a84543ebd3b16f91be42a75c9719a39c4096139f0f31393d58", + "0xa4c3eb1f7160f8216fc176fb244df53008ff32f2892363d85254002e66e2de21ccfe1f3b1047589abee50f29b9d507e3", + "0x8c552885eab04ba40922a8f0c3c38c96089c95ff1405258d3f1efe8d179e39e1295cbf67677894c607ae986e4e6b1fb0", + "0xb3671746fa7f848c4e2ae6946894defadd815230b906b419143523cc0597bc1d6c0a4c1e09d49b66b4a2c11cde3a4de3", + "0x937a249a95813a5e2ef428e355efd202e15a37d73e56cfb7e57ea9f943f2ce5ca8026f2f1fd25bf164ba89d07077d858", + "0x83646bdf6053a04aa9e2f112499769e5bd5d0d10f2e13db3ca89bd45c0b3b7a2d752b7d137fb3909f9c62b78166c9339", + "0xb4eac4b91e763666696811b7ed45e97fd78310377ebea1674b58a2250973f80492ac35110ed1240cd9bb2d17493d708c", + "0x82db43a99bc6573e9d92a3fd6635dbbb249ac66ba53099c3c0c8c8080b121dd8243cd5c6e36ba0a4d2525bae57f5c89c", + "0xa64d6a264a681b49d134c655d5fc7756127f1ee7c93d328820f32bca68869f53115c0d27fef35fe71f7bc4fdaed97348", + "0x8739b7a9e2b4bc1831e7f04517771bc7cde683a5e74e052542517f8375a2f64e53e0d5ac925ef722327e7bb195b4d1d9", + "0x8f337cdd29918a2493515ebb5cf702bbe8ecb23b53c6d18920cc22f519e276ca9b991d3313e2d38ae17ae8bdfa4f8b7e", + "0xb0edeab9850e193a61f138ef2739fc42ceec98f25e7e8403bfd5fa34a7bc956b9d0898250d18a69fa4625a9b3d6129da", + "0xa9920f26fe0a6d51044e623665d998745c9eca5bce12051198b88a77d728c8238f97d4196f26e43b24f8841500b998d0", + "0x86e655d61502b979eeeeb6f9a7e1d0074f936451d0a1b0d2fa4fb3225b439a3770767b649256fe481361f481a8dbc276", + "0x84d3b32fa62096831cc3bf013488a9f3f481dfe293ae209ed19585a03f7db8d961a7a9dd0db82bd7f62d612707575d9c", + "0x81c827826ec9346995ffccf62a241e3b2d32f7357acd1b1f8f7a7dbc97022d3eb51b8a1230e23ce0b401d2e535e8cd78", + "0x94a1e40c151191c5b055b21e86f32e69cbc751dcbdf759a48580951834b96a1eed75914c0d19a38aefd21fb6c8d43d0c", + "0xab890222b44bc21b71f7c75e15b6c6e16bb03371acce4f8d4353ff3b8fcd42a14026589c5ed19555a3e15e4d18bfc3a3", + "0xaccb0be851e93c6c8cc64724cdb86887eea284194b10e7a43c90528ed97e9ec71ca69c6fac13899530593756dd49eab2", + "0xb630220aa9e1829c233331413ee28c5efe94ea8ea08d0c6bfd781955078b43a4f92915257187d8526873e6c919c6a1de", + "0xadd389a4d358c585f1274b73f6c3c45b58ef8df11f9d11221f620e241bf3579fba07427b288c0c682885a700cc1fa28d", + "0xa9fe6ca8bf2961a3386e8b8dcecc29c0567b5c0b3bcf3b0f9169f88e372b80151af883871fc5229815f94f43a6f5b2b0", + "0xad839ae003b92b37ea431fa35998b46a0afc3f9c0dd54c3b3bf7a262467b13ff3c323ada1c1ae02ac7716528bdf39e3e", + "0x9356d3fd0edcbbb65713c0f2a214394f831b26f792124b08c5f26e7f734b8711a87b7c4623408da6a091c9aef1f6af3c", + "0x896b25b083c35ac67f0af3784a6a82435b0e27433d4d74cd6d1eafe11e6827827799490fb1c77c11de25f0d75f14e047", + "0x8bfa019391c9627e8e5f05c213db625f0f1e51ec68816455f876c7e55b8f17a4f13e5aae9e3fb9e1cf920b1402ee2b40", + "0x8ba3a6faa6a860a8f3ce1e884aa8769ceded86380a86520ab177ab83043d380a4f535fe13884346c5e51bee68da6ab41", + "0xa8292d0844084e4e3bb7af92b1989f841a46640288c5b220fecfad063ee94e86e13d3d08038ec2ac82f41c96a3bfe14d", + "0x8229bb030b2fc566e11fd33c7eab7a1bb7b49fed872ea1f815004f7398cb03b85ea14e310ec19e1f23e0bdaf60f8f76c", + "0x8cfbf869ade3ec551562ff7f63c2745cc3a1f4d4dc853a0cd42dd5f6fe54228f86195ea8fe217643b32e9f513f34a545", + "0xac52a3c8d3270ddfe1b5630159da9290a5ccf9ccbdef43b58fc0a191a6c03b8a5974cf6e2bbc7bd98d4a40a3581482d7", + "0xab13decb9e2669e33a7049b8eca3ca327c40dea15ad6e0e7fa63ed506db1d258bc36ac88b35f65cae0984e937eb6575d", + "0xb5e748eb1a7a1e274ff0cc56311c198f2c076fe4b7e73e5f80396fe85358549df906584e6bb2c8195b3e2be7736850a5", + "0xb5cb911325d8f963c41f691a60c37831c7d3bbd92736efa33d1f77a22b3fde7f283127256c2f47e197571e6fe0b46149", + "0x8a01dc6ed1b55f26427a014faa347130738b191a06b800e32042a46c13f60b49534520214359d68eb2e170c31e2b8672", + "0xa72fa874866e19b2efb8e069328362bf7921ec375e3bcd6b1619384c3f7ee980f6cf686f3544e9374ff54b4d17a1629c", + "0x8db21092f7c5f110fba63650b119e82f4b42a997095d65f08f8237b02dd66fdf959f788df2c35124db1dbd330a235671", + "0x8c65d50433d9954fe28a09fa7ba91a70a590fe7ba6b3060f5e4be0f6cef860b9897fa935fb4ebc42133524eb071dd169", + "0xb4614058e8fa21138fc5e4592623e78b8982ed72aa35ee4391b164f00c68d277fa9f9eba2eeefc890b4e86eba5124591", + "0xab2ad3a1bce2fbd55ca6b7c23786171fe1440a97d99d6df4d80d07dd56ac2d7203c294b32fc9e10a6c259381a73f24a1", + "0x812ae3315fdc18774a8da3713a4679e8ed10b9405edc548c00cacbe25a587d32040566676f135e4723c5dc25df5a22e9", + "0xa464b75f95d01e5655b54730334f443c8ff27c3cb79ec7af4b2f9da3c2039c609908cd128572e1fd0552eb597e8cef8d", + "0xa0db3172e93ca5138fe419e1c49a1925140999f6eff7c593e5681951ee0ec1c7e454c851782cbd2b8c9bc90d466e90e0", + "0x806db23ba7d00b87d544eed926b3443f5f9c60da6b41b1c489fba8f73593b6e3b46ebfcab671ee009396cd77d5e68aa1", + "0x8bfdf2c0044cc80260994e1c0374588b6653947b178e8b312be5c2a05e05767e98ea15077278506aee7df4fee1aaf89e", + "0x827f6558c16841b5592ff089c9c31e31eb03097623524394813a2e4093ad2d3f8f845504e2af92195aaa8a1679d8d692", + "0x925c4f8eab2531135cd71a4ec88e7035b5eea34ba9d799c5898856080256b4a15ed1a746e002552e2a86c9c157e22e83", + "0xa9f9a368f0e0b24d00a35b325964c85b69533013f9c2cfad9708be5fb87ff455210f8cb8d2ce3ba58ca3f27495552899", + "0x8ac0d3bebc1cae534024187e7c71f8927ba8fcc6a1926cb61c2b6c8f26bb7831019e635a376146c29872a506784a4aaa", + "0x97c577be2cbbfdb37ad754fae9df2ada5fc5889869efc7e18a13f8e502fbf3f4067a509efbd46fd990ab47ce9a70f5a8", + "0x935e7d82bca19f16614aa43b4a3474e4d20d064e4bfdf1cea2909e5c9ab72cfe3e54dc50030e41ee84f3588cebc524e9", + "0x941aafc08f7c0d94cebfbb1f0aad5202c02e6e37f2c12614f57e727efa275f3926348f567107ee6d8914dd71e6060271", + "0xaf0fbc1ba05b4b5b63399686df3619968be5d40073de0313cbf5f913d3d4b518d4c249cdd2176468ccaa36040a484f58", + "0xa0c414f23f46ca6d69ce74c6f8a00c036cb0edd098af0c1a7d39c802b52cfb2d5dbdf93fb0295453d4646e2af7954d45", + "0x909cf39e11b3875bb63b39687ae1b5d1f5a15445e39bf164a0b14691b4ddb39a8e4363f584ef42213616abc4785b5d66", + "0xa92bac085d1194fbd1c88299f07a061d0bdd3f980b663e81e6254dbb288bf11478c0ee880e28e01560f12c5ccb3c0103", + "0x841705cd5cd76b943e2b7c5e845b9dd3c8defe8ef67e93078d6d5e67ade33ad4b0fd413bc196f93b0a4073c855cd97d4", + "0x8e7eb8364f384a9161e81d3f1d52ceca9b65536ae49cc35b48c3e2236322ba4ae9973e0840802d9fa4f4d82ea833544f", + "0xaed3ab927548bc8bec31467ba80689c71a168e34f50dcb6892f19a33a099f5aa6b3f9cb79f5c0699e837b9a8c7f27efe", + "0xb8fbf7696210a36e20edabd77839f4dfdf50d6d015cdf81d587f90284a9bcef7d2a1ff520728d7cc69a4843d6c20dedd", + "0xa9d533769ce6830211c884ae50a82a7bf259b44ac71f9fb11f0296fdb3981e6b4c1753fe744647b247ebc433a5a61436", + "0x8b4bdf90d33360b7f428c71cde0a49fb733badba8c726876945f58c620ce7768ae0e98fc8c31fa59d8955a4823336bb1", + "0x808d42238e440e6571c59e52a35ae32547d502dc24fd1759d8ea70a7231a95859baf30b490a4ba55fa2f3aaa11204597", + "0x85594701f1d2fee6dc1956bc44c7b31db93bdeec2f3a7d622c1a08b26994760773e3d57521a44cfd7e407ac3fd430429", + "0xa66de045ce7173043a6825e9dc440ac957e2efb6df0a337f4f8003eb0c719d873a52e6eba3cb0d69d977ca37d9187674", + "0x87a1c6a1fdff993fa51efa5c3ba034c079c0928a7d599b906336af7c2dcab9721ceaf3108c646490af9dff9a754f54b3", + "0x926424223e462ceb75aed7c22ade8a7911a903b7e5dd4bc49746ddce8657f4616325cd12667d4393ac52cdd866396d0e", + "0xb5dc96106593b42b30f06f0b0a1e0c1aafc70432e31807252d3674f0b1ea5e58eac8424879d655c9488d85a879a3e572", + "0x997ca0987735cc716507cb0124b1d266d218b40c9d8e0ecbf26a1d65719c82a637ce7e8be4b4815d307df717bde7c72a", + "0x92994d3f57a569b7760324bb5ae4e8e14e1633d175dab06aa57b8e391540e05f662fdc08b8830f489a063f59b689a688", + "0xa8087fcc6aa4642cb998bea11facfe87eb33b90a9aa428ab86a4124ad032fc7d2e57795311a54ec9f55cc120ebe42df1", + "0xa9bd7d1de6c0706052ca0b362e2e70e8c8f70f1f026ea189b4f87a08ce810297ebfe781cc8004430776c54c1a05ae90c", + "0x856d33282e8a8e33a3d237fb0a0cbabaf77ba9edf2fa35a831fdafcadf620561846aa6cbb6bdc5e681118e1245834165", + "0x9524a7aa8e97a31a6958439c5f3339b19370f03e86b89b1d02d87e4887309dbbe9a3a8d2befd3b7ed5143c8da7e0a8ad", + "0x824fdf433e090f8acbd258ac7429b21f36f9f3b337c6d0b71d1416a5c88a767883e255b2888b7c906dd2e9560c4af24c", + "0x88c7fee662ca7844f42ed5527996b35723abffd0d22d4ca203b9452c639a5066031207a5ae763dbc0865b3299d19b1ec", + "0x919dca5c5595082c221d5ab3a5bc230f45da7f6dec4eb389371e142c1b9c6a2c919074842479c2844b72c0d806170c0c", + "0xb939be8175715e55a684578d8be3ceff3087f60fa875fff48e52a6e6e9979c955efef8ff67cfa2b79499ea23778e33b0", + "0x873b6db725e7397d11bc9bed9ac4468e36619135be686790a79bc6ed4249058f1387c9a802ea86499f692cf635851066", + "0xaeae06db3ec47e9e5647323fa02fac44e06e59b885ad8506bf71b184ab3895510c82f78b6b22a5d978e8218e7f761e9f", + "0xb99c0a8359c72ab88448bae45d4bf98797a26bca48b0d4460cd6cf65a4e8c3dd823970ac3eb774ae5d0cea4e7fadf33e", + "0x8f10c8ec41cdfb986a1647463076a533e6b0eec08520c1562401b36bb063ac972aa6b28a0b6ce717254e35940b900e3c", + "0xa106d9be199636d7add43b942290269351578500d8245d4aae4c083954e4f27f64740a3138a66230391f2d0e6043a8de", + "0xa469997908244578e8909ff57cffc070f1dbd86f0098df3cfeb46b7a085cfecc93dc69ee7cad90ff1dc5a34d50fe580c", + "0xa4ef087bea9c20eb0afc0ee4caba7a9d29dfa872137828c721391273e402fb6714afc80c40e98bbd8276d3836bffa080", + "0xb07a013f73cd5b98dae0d0f9c1c0f35bff8a9f019975c4e1499e9bee736ca6fcd504f9bc32df1655ff333062382cff04", + "0xb0a77188673e87cc83348c4cc5db1eecf6b5184e236220c8eeed7585e4b928db849944a76ec60ef7708ef6dac02d5592", + "0xb1284b37e59b529f0084c0dacf0af6c0b91fc0f387bf649a8c74819debf606f7b07fc3e572500016fb145ec2b24e9f17", + "0x97b20b5b4d6b9129da185adfbf0d3d0b0faeba5b9715f10299e48ea0521709a8296a9264ce77c275a59c012b50b6519a", + "0xb9d37e946fae5e4d65c1fbfacc8a62e445a1c9d0f882e60cca649125af303b3b23af53c81d7bac544fb7fcfc7a314665", + "0x8e5acaac379f4bb0127efbef26180f91ff60e4c525bc9b798fc50dfaf4fe8a5aa84f18f3d3cfb8baead7d1e0499af753", + "0xb0c0b8ab1235bf1cda43d4152e71efc1a06c548edb964eb4afceb201c8af24240bf8ab5cae30a08604e77432b0a5faf0", + "0x8cc28d75d5c8d062d649cbc218e31c4d327e067e6dbd737ec0a35c91db44fbbd0d40ec424f5ed79814add16947417572", + "0x95ae6219e9fd47efaa9cb088753df06bc101405ba50a179d7c9f7c85679e182d3033f35b00dbba71fdcd186cd775c52e", + "0xb5d28fa09f186ebc5aa37453c9b4d9474a7997b8ae92748ecb940c14868792292ac7d10ade01e2f8069242b308cf97e5", + "0x8c922a0faa14cc6b7221f302df3342f38fc8521ec6c653f2587890192732c6da289777a6cd310747ea7b7d104af95995", + "0xb9ad5f660b65230de54de535d4c0fcae5bc6b59db21dea5500fdc12eea4470fb8ea003690fdd16d052523418d5e01e8c", + "0xa39a9dd41a0ff78c82979483731f1cd68d3921c3e9965869662c22e02dde3877802e180ba93f06e7346f96d9fa9261d2", + "0x8b32875977ec372c583b24234c27ed73aef00cdff61eb3c3776e073afbdeade548de9497c32ec6d703ff8ad0a5cb7fe4", + "0x9644cbe755a5642fe9d26cfecf170d3164f1848c2c2e271d5b6574a01755f3980b3fc870b98cf8528fef6ecef4210c16", + "0x81ea9d1fdd9dd66d60f40ce0712764b99da9448ae0b300f8324e1c52f154e472a086dda840cb2e0b9813dc8ce8afd4b5", + "0x906aaa4a7a7cdf01909c5cfbc7ded2abc4b869213cbf7c922d4171a4f2e637e56f17020b852ad339d83b8ac92f111666", + "0x939b5f11acbdeff998f2a080393033c9b9d8d5c70912ea651c53815c572d36ee822a98d6dfffb2e339f29201264f2cf4", + "0xaba4898bf1ccea9b9e2df1ff19001e05891581659c1cbbde7ee76c349c7fc7857261d9785823c9463a8aea3f40e86b38", + "0x83ca1a56b8a0be4820bdb5a9346357c68f9772e43f0b887729a50d2eb2a326bbcede676c8bf2e51d7c89bbd8fdb778a6", + "0x94e86e9fe6addfe2c3ee3a547267ed921f4230d877a85bb4442c2d9350c2fa9a9c54e6fe662de82d1a2407e4ab1691c2", + "0xa0cc3bdef671a59d77c6984338b023fa2b431b32e9ed2abe80484d73edc6540979d6f10812ecc06d4d0c5d4eaca7183c", + "0xb5343413c1b5776b55ea3c7cdd1f3af1f6bd802ea95effe3f2b91a523817719d2ecc3f8d5f3cc2623ace7e35f99ca967", + "0x92085d1ed0ed28d8cabe3e7ff1905ed52c7ceb1eac5503760c52fb5ee3a726aba7c90b483c032acc3f166b083d7ec370", + "0x8ec679520455275cd957fca8122724d287db5df7d29f1702a322879b127bff215e5b71d9c191901465d19c86c8d8d404", + "0xb65eb2c63d8a30332eb24ee8a0c70156fc89325ebbb38bacac7cf3f8636ad8a472d81ccca80423772abc00192d886d8a", + "0xa9fe1c060b974bee4d590f2873b28635b61bfcf614e61ff88b1be3eee4320f4874e21e8d666d8ac8c9aba672efc6ecae", + "0xb3fe2a9a389c006a831dea7e777062df84b5c2803f9574d7fbe10b7e1c125817986af8b6454d6be9d931a5ac94cfe963", + "0x95418ad13b734b6f0d33822d9912c4c49b558f68d08c1b34a0127fcfa666bcae8e6fda8832d2c75bb9170794a20e4d7c", + "0xa9a7df761e7f18b79494bf429572140c8c6e9d456c4d4e336184f3f51525a65eb9582bea1e601bdb6ef8150b7ca736a5", + "0xa0de03b1e75edf7998c8c1ac69b4a1544a6fa675a1941950297917366682e5644a4bda9cdeedfaf9473d7fccd9080b0c", + "0xa61838af8d95c95edf32663a68f007d95167bf6e41b0c784a30b22d8300cfdd5703bd6d16e86396638f6db6ae7e42a85", + "0x8866d62084d905c145ff2d41025299d8b702ac1814a7dec4e277412c161bc9a62fed735536789cb43c88693c6b423882", + "0x91da22c378c81497fe363e7f695c0268443abee50f8a6625b8a41e865638a643f07b157ee566de09ba09846934b4e2d7", + "0x941d21dd57c9496aa68f0c0c05507405fdd413acb59bc668ce7e92e1936c68ec4b065c3c30123319884149e88228f0b2", + "0xa77af9b094bc26966ddf2bf9e1520c898194a5ccb694915950dadc204facbe3066d3d89f50972642d76b14884cfbaa21", + "0x8e76162932346869f4618bde744647f7ab52ab498ad654bdf2a4feeb986ac6e51370841e5acbb589e38b6e7142bb3049", + "0xb60979ace17d6937ece72e4f015da4657a443dd01cebc7143ef11c09e42d4aa8855999a65a79e2ea0067f31c9fc2ab0f", + "0xb3e2ffdd5ee6fd110b982fd4fad4b93d0fca65478f986d086eeccb0804960bfaa1919afa743c2239973ea65091fe57d2", + "0x8ce0ce05e7d7160d44574011da687454dbd3c8b8290aa671731b066e2c82f8cf2d63cb8e932d78c6122ec610e44660e6", + "0xab005dd8d297045c39e2f72fb1c48edb501ccf3575d3d04b9817b3afee3f0bb0f3f53f64bda37d1d9cde545aae999bae", + "0x95bd7edb4c4cd60e3cb8a72558845a3cce6bb7032ccdf33d5a49ebb6ddf203bc3c79e7b7e550735d2d75b04c8b2441e8", + "0x889953ee256206284094e4735dbbb17975bafc7c3cb94c9fbfee4c3e653857bfd49e818f64a47567f721b98411a3b454", + "0xb188423e707640ab0e75a061e0b62830cde8afab8e1ad3dae30db69ffae4e2fc005bababbdcbd7213b918ed4f70e0c14", + "0xa97e0fafe011abd70d4f99a0b36638b3d6e7354284588f17a88970ed48f348f88392779e9a038c6cbc9208d998485072", + "0x87db11014a91cb9b63e8dfaa82cdebca98272d89eb445ee1e3ff9dbaf2b3fad1a03b888cffc128e4fe208ed0dddece0f", + "0xaad2e40364edd905d66ea4ac9d51f9640d6fda9a54957d26ba233809851529b32c85660fa401dbee3679ec54fa6dd966", + "0x863e99336ca6edf03a5a259e59a2d0f308206e8a2fb320cfc0be06057366df8e0f94b33a28f574092736b3c5ada84270", + "0xb34bcc56a057589f34939a1adc51de4ff6a9f4fee9c7fa9aa131e28d0cf0759a0c871b640162acdfbf91f3f1b59a3703", + "0x935dd28f2896092995c5eff1618e5b6efe7a40178888d7826da9b0503c2d6e68a28e7fac1a334e166d0205f0695ef614", + "0xb842cd5f8f5de5ca6c68cb4a5c1d7b451984930eb4cc18fd0934d52fdc9c3d2d451b1c395594d73bc3451432bfba653f", + "0x9014537885ce2debad736bc1926b25fdab9f69b216bf024f589c49dc7e6478c71d595c3647c9f65ff980b14f4bb2283b", + "0x8e827ccca1dd4cd21707140d10703177d722be0bbe5cac578db26f1ef8ad2909103af3c601a53795435b27bf95d0c9ed", + "0x8a0b8ad4d466c09d4f1e9167410dbe2edc6e0e6229d4b3036d30f85eb6a333a18b1c968f6ca6d6889bb08fecde017ef4", + "0x9241ee66c0191b06266332dc9161dede384c4bb4e116dbd0890f3c3790ec5566da4568243665c4725b718ac0f6b5c179", + "0xaeb4d5fad81d2b505d47958a08262b6f1b1de9373c2c9ba6362594194dea3e002ab03b8cbb43f867be83065d3d370f19", + "0x8781bc83bb73f7760628629fe19e4714b494dbed444c4e4e4729b7f6a8d12ee347841a199888794c2234f51fa26fc2b9", + "0xb58864f0acd1c2afa29367e637cbde1968d18589245d9936c9a489c6c495f54f0113ecdcbe4680ac085dd3c397c4d0c3", + "0x94a24284afaeead61e70f3e30f87248d76e9726759445ca18cdb9360586c60cc9f0ec1c397f9675083e0b56459784e2e", + "0xaed358853f2b54dcbddf865e1816c2e89be12e940e1abfa661e2ee63ffc24a8c8096be2072fa83556482c0d89e975124", + "0xb95374e6b4fc0765708e370bc881e271abf2e35c08b056a03b847e089831ef4fe3124b9c5849d9c276eb2e35b3daf264", + "0xb834cdbcfb24c8f84bfa4c552e7fadc0028a140952fd69ed13a516e1314a4cd35d4b954a77d51a1b93e1f5d657d0315d", + "0x8fb6d09d23bfa90e7443753d45a918d91d75d8e12ec7d016c0dfe94e5c592ba6aaf483d2f16108d190822d955ad9cdc3", + "0xaa315cd3c60247a6ad4b04f26c5404c2713b95972843e4b87b5a36a89f201667d70f0adf20757ebe1de1b29ae27dda50", + "0xa116862dca409db8beff5b1ccd6301cdd0c92ca29a3d6d20eb8b87f25965f42699ca66974dd1a355200157476b998f3b", + "0xb4c2f5fe173c4dc8311b60d04a65ce1be87f070ac42e13cd19c6559a2931c6ee104859cc2520edebbc66a13dc7d30693", + "0x8d4a02bf99b2260c334e7d81775c5cf582b00b0c982ce7745e5a90624919028278f5e9b098573bad5515ce7fa92a80c8", + "0x8543493bf564ce6d97bd23be9bff1aba08bd5821ca834f311a26c9139c92a48f0c2d9dfe645afa95fec07d675d1fd53b", + "0x9344239d13fde08f98cb48f1f87d34cf6abe8faecd0b682955382a975e6eed64e863fa19043290c0736261622e00045c", + "0xaa49d0518f343005ca72b9e6c7dcaa97225ce6bb8b908ebbe7b1a22884ff8bfb090890364e325a0d414ad180b8f161d1", + "0x907d7fd3e009355ab326847c4a2431f688627faa698c13c03ffdd476ecf988678407f029b8543a475dcb3dafdf2e7a9c", + "0x845f1f10c6c5dad2adc7935f5cd2e2b32f169a99091d4f1b05babe7317b9b1cdce29b5e62f947dc621b9acbfe517a258", + "0x8f3be8e3b380ea6cdf9e9c237f5e88fd5a357e5ded80ea1fc2019810814de82501273b4da38916881125b6fa0cfd4459", + "0xb9c7f487c089bf1d20c822e579628db91ed9c82d6ca652983aa16d98b4270c4da19757f216a71b9c13ddee3e6e43705f", + "0x8ba2d8c88ad2b872db104ea8ddbb006ec2f3749fd0e19298a804bb3a5d94de19285cc7fb19fee58a66f7851d1a66c39f", + "0x9375ecd3ed16786fe161af5d5c908f56eeb467a144d3bbddfc767e90065b7c94fc53431adebecba2b6c9b5821184d36e", + "0xa49e069bfadb1e2e8bff6a4286872e2a9765d62f0eaa4fcb0e5af4bbbed8be3510fb19849125a40a8a81d1e33e81c3eb", + "0x9522cc66757b386aa6b88619525c8ce47a5c346d590bb3647d12f991e6c65c3ab3c0cfc28f0726b6756c892eae1672be", + "0xa9a0f1f51ff877406fa83a807aeb17b92a283879f447b8a2159653db577848cc451cbadd01f70441e351e9ed433c18bc", + "0x8ff7533dcff6be8714df573e33f82cf8e9f2bcaaa43e939c4759d52b754e502717950de4b4252fb904560fc31dce94a4", + "0x959724671e265a28d67c29d95210e97b894b360da55e4cf16e6682e7912491ed8ca14bfaa4dce9c25a25b16af580494f", + "0x92566730c3002f4046c737032487d0833c971e775de59fe02d9835c9858e2e3bc37f157424a69764596c625c482a2219", + "0xa84b47ceff13ed9c3e5e9cdf6739a66d3e7c2bd8a6ba318fefb1a9aecf653bb2981da6733ddb33c4b0a4523acc429d23", + "0xb4ddf571317e44f859386d6140828a42cf94994e2f1dcbcc9777f4eebbfc64fc1e160b49379acc27c4672b8e41835c5d", + "0x8ab95c94072b853d1603fdd0a43b30db617d13c1d1255b99075198e1947bfa5f59aed2b1147548a1b5e986cd9173d15c", + "0x89511f2eab33894fd4b3753d24249f410ff7263052c1fef6166fc63a79816656b0d24c529e45ccce6be28de6e375d916", + "0xa0866160ca63d4f2be1b4ea050dac6b59db554e2ebb4e5b592859d8df339b46fd7cb89aaed0951c3ee540aee982c238a", + "0x8fcc5cbba1b94970f5ff2eb1922322f5b0aa7d918d4b380c9e7abfd57afd8b247c346bff7b87af82efbce3052511cd1b", + "0x99aeb2a5e846b0a2874cca02c66ed40d5569eb65ab2495bc3f964a092e91e1517941f2688e79f8cca49cd3674c4e06dc", + "0xb7a096dc3bad5ca49bee94efd884aa3ff5615cf3825cf95fbe0ce132e35f46581d6482fa82666c7ef5f1643eaee8f1ca", + "0x94393b1da6eaac2ffd186b7725eca582f1ddc8cdd916004657f8a564a7c588175cb443fc6943b39029f5bbe0add3fad8", + "0x884b85fe012ccbcd849cb68c3ad832d83b3ef1c40c3954ffdc97f103b1ed582c801e1a41d9950f6bddc1d11f19d5ec76", + "0xb00061c00131eded8305a7ce76362163deb33596569afb46fe499a7c9d7a0734c084d336b38d168024c2bb42b58e7660", + "0xa439153ac8e6ca037381e3240e7ba08d056c83d7090f16ed538df25901835e09e27de2073646e7d7f3c65056af6e4ce7", + "0x830fc9ca099097d1f38b90e6843dc86f702be9d20bdacc3e52cae659dc41df5b8d2c970effa6f83a5229b0244a86fe22", + "0xb81ea2ffaaff2bb00dd59a9ab825ba5eed4db0d8ac9c8ed1a632ce8f086328a1cddd045fbe1ace289083c1325881b7e7", + "0xb51ea03c58daf2db32c99b9c4789b183365168cb5019c72c4cc91ac30b5fb7311d3db76e6fa41b7cd4a8c81e2f6cdc94", + "0xa4170b2c6d09ca5beb08318730419b6f19215ce6c631c854116f904be3bc30dd85a80c946a8ab054d3e307afaa3f8fbc", + "0x897cc42ff28971ff54d2a55dd6b35cfb8610ac902f3c06e3a5cea0e0a257e870c471236a8e84709211c742a09c5601a6", + "0xa18f2e98d389dace36641621488664ecbb422088ab03b74e67009b8b8acacaaa24fdcf42093935f355207d934adc52a8", + "0x92adcfb678cc2ba19c866f3f2b988fdcb4610567f3ab436cc0cb9acaf5a88414848d71133ebdbec1983e38e6190f1b5f", + "0xa86d43c2ce01b366330d3b36b3ca85f000c3548b8297e48478da1ee7d70d8576d4650cba7852ed125c0d7cb6109aa7f3", + "0x8ed31ceed9445437d7732dce78a762d72ff32a7636bfb3fd7974b7ae15db414d8184a1766915244355deb354fbc5803b", + "0x9268f70032584f416e92225d65af9ea18c466ebc7ae30952d56a4e36fd9ea811dde0a126da9220ba3c596ec54d8a335e", + "0x9433b99ee94f2d3fbdd63b163a2bdf440379334c52308bd24537f7defd807145a062ff255a50d119a7f29f4b85d250e3", + "0x90ce664f5e4628a02278f5cf5060d1a34f123854634b1870906e5723ac9afd044d48289be283b267d45fcbf3f4656aaf", + "0xaaf21c4d59378bb835d42ae5c5e5ab7a3c8c36a59e75997989313197752b79a472d866a23683b329ea69b048b87fa13e", + "0xb83c0589b304cec9ede549fde54f8a7c2a468c6657da8c02169a6351605261202610b2055c639b9ed2d5b8c401fb8f56", + "0x9370f326ea0f170c2c05fe2c5a49189f20aec93b6b18a5572a818cd4c2a6adb359e68975557b349fb54f065d572f4c92", + "0xac3232fa5ce6f03fca238bef1ce902432a90b8afce1c85457a6bee5571c033d4bceefafc863af04d4e85ac72a4d94d51", + "0x80d9ea168ff821b22c30e93e4c7960ce3ad3c1e6deeebedd342a36d01bd942419b187e2f382dbfd8caa34cca08d06a48", + "0xa387a3c61676fb3381eefa2a45d82625635a666e999aba30e3b037ec9e040f414f9e1ad9652abd3bcad63f95d85038db", + "0xa1b229fe32121e0b391b0f6e0180670b9dc89d79f7337de4c77ea7ad0073e9593846f06797c20e923092a08263204416", + "0x92164a9d841a2b828cedf2511213268b698520f8d1285852186644e9a0c97512cafa4bfbe29af892c929ebccd102e998", + "0x82ee2fa56308a67c7db4fd7ef539b5a9f26a1c2cc36da8c3206ba4b08258fbb3cec6fe5cdbd111433fb1ba2a1e275927", + "0x8c77bfe9e191f190a49d46f05600603fa42345592539b82923388d72392404e0b29a493a15e75e8b068dddcd444c2928", + "0x80b927f93ccf79dcf5c5b20bcf5a7d91d7a17bc0401bb7cc9b53a6797feac31026eb114257621f5a64a52876e4474cc1", + "0xb6b68b6501c37804d4833d5a063dd108a46310b1400549074e3cac84acc6d88f73948b7ad48d686de89c1ec043ae8c1a", + "0xab3da00f9bdc13e3f77624f58a3a18fc3728956f84b5b549d62f1033ae4b300538e53896e2d943f160618e05af265117", + "0xb6830e87233b8eace65327fdc764159645b75d2fd4024bf8f313b2dd5f45617d7ecfb4a0b53ccafb5429815a9a1adde6", + "0xb9251cfe32a6dc0440615aadcd98b6b1b46e3f4e44324e8f5142912b597ee3526bea2431e2b0282bb58f71be5b63f65e", + "0xaf8d70711e81cdddfb39e67a1b76643292652584c1ce7ce4feb1641431ad596e75c9120e85f1a341e7a4da920a9cdd94", + "0x98cd4e996594e89495c078bfd52a4586b932c50a449a7c8dfdd16043ca4cda94dafbaa8ad1b44249c99bbcc52152506e", + "0xb9fc6d1c24f48404a4a64fbe3e43342738797905db46e4132aee5f086aaa4c704918ad508aaefa455cfe1b36572e6242", + "0xa365e871d30ba9291cedaba1be7b04e968905d003e9e1af7e3b55c5eb048818ae5b913514fb08b24fb4fbdccbb35d0b8", + "0x93bf99510971ea9af9f1e364f1234c898380677c8e8de9b0dd24432760164e46c787bc9ec42a7ad450500706cf247b2d", + "0xb872f825a5b6e7b9c7a9ddfeded3516f0b1449acc9b4fd29fc6eba162051c17416a31e5be6d3563f424d28e65bab8b8f", + "0xb06b780e5a5e8eb4f4c9dc040f749cf9709c8a4c9ef15e925f442b696e41e5095db0778a6c73bcd329b265f2c6955c8b", + "0x848f1a981f5fc6cd9180cdddb8d032ad32cdfa614fc750d690dbae36cc0cd355cbf1574af9b3ffc8b878f1b2fafb9544", + "0xa03f48cbff3e9e8a3a655578051a5ae37567433093ac500ed0021c6250a51b767afac9bdb194ee1e3eac38a08c0eaf45", + "0xb5be78ce638ff8c4aa84352b536628231d3f7558c5be3bf010b28feac3022e64691fa672f358c8b663904aebe24a54ed", + "0xa9d4da70ff676fa55d1728ba6ab03b471fa38b08854d99e985d88c2d050102d8ccffbe1c90249a5607fa7520b15fe791", + "0x8fe9f7092ffb0b69862c8e972fb1ecf54308c96d41354ed0569638bb0364f1749838d6d32051fff1599112978c6e229c", + "0xae6083e95f37770ecae0df1e010456f165d96cfe9a7278c85c15cffd61034081ce5723e25e2bede719dc9341ec8ed481", + "0xa260891891103089a7afbd9081ea116cfd596fd1015f5b65e10b0961eb37fab7d09c69b7ce4be8bf35e4131848fb3fe4", + "0x8d729fa32f6eb9fd2f6a140bef34e8299a2f3111bffd0fe463aa8622c9d98bfd31a1df3f3e87cd5abc52a595f96b970e", + "0xa30ec6047ae4bc7da4daa7f4c28c93aedb1112cfe240e681d07e1a183782c9ff6783ac077c155af23c69643b712a533f", + "0xac830726544bfe7b5467339e5114c1a75f2a2a8d89453ce86115e6a789387e23551cd64620ead6283dfa4538eb313d86", + "0x8445c135b7a48068d8ed3e011c6d818cfe462b445095e2fbf940301e50ded23f272d799eea47683fc027430ce14613ef", + "0x95785411715c9ae9d8293ce16a693a2aa83e3cb1b4aa9f76333d0da2bf00c55f65e21e42e50e6c5772ce213dd7b4f7a0", + "0xb273b024fa18b7568c0d1c4d2f0c4e79ec509dafac8c5951f14192d63ddbcf2d8a7512c1c1b615cc38fa3e336618e0c5", + "0xa78b9d3ea4b6a90572eb27956f411f1d105fdb577ee2ffeec9f221da9b45db84bfe866af1f29597220c75e0c37a628d8", + "0xa4be2bf058c36699c41513c4d667681ce161a437c09d81383244fc55e1c44e8b1363439d0cce90a3e44581fb31d49493", + "0xb6eef13040f17dd4eba22aaf284d2f988a4a0c4605db44b8d2f4bf9567ac794550b543cc513c5f3e2820242dd704152e", + "0x87eb00489071fa95d008c5244b88e317a3454652dcb1c441213aa16b28cd3ecaa9b22fec0bdd483c1df71c37119100b1", + "0x92d388acdcb49793afca329cd06e645544d2269234e8b0b27d2818c809c21726bc9cf725651b951e358a63c83dedee24", + "0xae27e219277a73030da27ab5603c72c8bd81b6224b7e488d7193806a41343dff2456132274991a4722fdb0ef265d04cd", + "0x97583e08ecb82bbc27c0c8476d710389fa9ffbead5c43001bd36c1b018f29faa98de778644883e51870b69c5ffb558b5", + "0x90a799a8ce73387599babf6b7da12767c0591cadd36c20a7990e7c05ea1aa2b9645654ec65308ee008816623a2757a6a", + "0xa1b47841a0a2b06efd9ab8c111309cc5fc9e1d5896b3e42ed531f6057e5ade8977c29831ce08dbda40348386b1dcc06d", + "0xb92b8ef59bbddb50c9457691bc023d63dfcc54e0fd88bd5d27a09e0d98ac290fc90e6a8f6b88492043bf7c87fac8f3e4", + "0xa9d6240b07d62e22ec8ab9b1f6007c975a77b7320f02504fc7c468b4ee9cfcfd945456ff0128bc0ef2174d9e09333f8d", + "0x8e96534c94693226dc32bca79a595ca6de503af635f802e86442c67e77564829756961d9b701187fe91318da515bf0e6", + "0xb6ba290623cd8dd5c2f50931c0045d1cfb0c30877bc8fe58cbc3ff61ee8da100045a39153916efa1936f4aee0892b473", + "0xb43baa7717fac02d4294f5b3bb5e58a65b3557747e3188b482410388daac7a9c177f762d943fd5dcf871273921213da8", + "0xb9cf00f8fb5e2ef2b836659fece15e735060b2ea39b8e901d3dcbdcf612be8bf82d013833718c04cd46ffaa70b85f42e", + "0x8017d0c57419e414cbba504368723e751ef990cc6f05dad7b3c2de6360adc774ad95512875ab8337d110bf39a42026fa", + "0xae7401048b838c0dcd4b26bb6c56d79d51964a0daba780970b6c97daee4ea45854ea0ac0e4139b3fe60dac189f84df65", + "0x887b237b0cd0f816b749b21db0b40072f9145f7896c36916296973f9e6990ede110f14e5976c906d08987c9836cca57f", + "0xa88c3d5770148aee59930561ca1223aceb2c832fb5417e188dca935905301fc4c6c2c9270bc1dff7add490a125eb81c6", + "0xb6cf9b02c0cd91895ad209e38c54039523f137b5848b9d3ad33ae43af6c20c98434952db375fe378de7866f2d0e8b18a", + "0x84ef3d322ff580c8ad584b1fe4fe346c60866eb6a56e982ba2cf3b021ecb1fdb75ecc6c29747adda86d9264430b3f816", + "0xa0561c27224baf0927ad144cb71e31e54a064c598373fcf0d66aebf98ab7af1d8e2f343f77baefff69a6da750a219e11", + "0xaa5cc43f5b8162b016f5e1b61214c0c9d15b1078911c650b75e6cdfb49b85ee04c6739f5b1687d15908444f691f732de", + "0xad4ac099b935589c7b8fdfdf3db332b7b82bb948e13a5beb121ebd7db81a87d278024a1434bcf0115c54ca5109585c3d", + "0x8a00466abf3f109a1dcd19e643b603d3af23d42794ef8ca2514dd507ecea44a031ac6dbc18bd02f99701168b25c1791e", + "0xb00b5900dfad79645f8bee4e5adc7b84eb22e5b1e67df77ccb505b7fc044a6c08a8ea5faca662414eb945f874f884cea", + "0x950e204e5f17112250b22ea6bb8423baf522fc0af494366f18fe0f949f51d6e6812074a80875cf1ed9c8e7420058d541", + "0x91e5cbf8bb1a1d50c81608c9727b414d0dd2fb467ebc92f100882a3772e54f94979cfdf8e373fdef7c7fcdd60fec9e00", + "0xa093f6a857b8caaff80599c2e89c962b415ecbaa70d8fd973155fa976a284c6b29a855f5f7a3521134d00d2972755188", + "0xb4d55a3551b00da54cc010f80d99ddd2544bde9219a3173dfaadf3848edc7e4056ab532fb75ac26f5f7141e724267663", + "0xa03ea050fc9b011d1b04041b5765d6f6453a93a1819cd9bd6328637d0b428f08526466912895dcc2e3008ee58822e9a7", + "0x99b12b3665e473d01bc6985844f8994fb65cb15745024fb7af518398c4a37ff215da8f054e8fdf3286984ae36a73ca5e", + "0x9972c7e7a7fb12e15f78d55abcaf322c11249cd44a08f62c95288f34f66b51f146302bce750ff4d591707075d9123bd2", + "0xa64b4a6d72354e596d87cda213c4fc2814009461570ccb27d455bbe131f8d948421a71925425b546d8cf63d5458cd64b", + "0x91c215c73b195795ede2228b7ed1f6e37892e0c6b0f4a0b5a16c57aa1100c84df9239054a173b6110d6c2b7f4bf1ce52", + "0x88807198910ec1303480f76a3683870246a995e36adaeadc29c22f0bdba8152fe705bd070b75de657b04934f7d0ccf80", + "0xb37c0026c7b32eb02cacac5b55cb5fe784b8e48b2945c64d3037af83ece556a117f0ff053a5968c2f5fa230e291c1238", + "0x94c768384ce212bc2387e91ce8b45e4ff120987e42472888a317abc9dcdf3563b62e7a61c8e98d7cdcbe272167d91fc6", + "0xa10c2564936e967a390cb14ef6e8f8b04ea9ece5214a38837eda09e79e0c7970b1f83adf017c10efd6faa8b7ffa2c567", + "0xa5085eed3a95f9d4b1269182ea1e0d719b7809bf5009096557a0674bde4201b0ddc1f0f16a908fc468846b3721748ce3", + "0x87468eb620b79a0a455a259a6b4dfbc297d0d53336537b771254dd956b145dc816b195b7002647ea218552e345818a3f", + "0xace2b77ffb87366af0a9cb5d27d6fc4a14323dbbf1643f5f3c4559306330d86461bb008894054394cbfaefeaa0bc2745", + "0xb27f56e840a54fbd793f0b7a7631aa4cee64b5947e4382b2dfb5eb1790270288884c2a19afebe5dc0c6ef335d4531c1c", + "0x876e438633931f7f895062ee16c4b9d10428875f7bc79a8e156a64d379a77a2c45bf5430c5ab94330f03da352f1e9006", + "0xa2512a252587d200d2092b44c914df54e04ff8bcef36bf631f84bde0cf5a732e3dc7f00f662842cfd74b0b0f7f24180e", + "0x827f1bc8f54a35b7a4bd8154f79bcc055e45faed2e74adf7cf21cca95df44d96899e847bd70ead6bb27b9c0ed97bbd8b", + "0xa0c92cf5a9ed843714f3aea9fe7b880f622d0b4a3bf66de291d1b745279accf6ba35097849691370f41732ba64b5966b", + "0xa63f5c1e222775658421c487b1256b52626c6f79cb55a9b7deb2352622cedffb08502042d622eb3b02c97f9c09f9c957", + "0x8cc093d52651e65fb390e186db6cc4de559176af4624d1c44cb9b0e836832419dacac7b8db0627b96288977b738d785d", + "0xaa7b6a17dfcec146134562d32a12f7bd7fe9522e300859202a02939e69dbd345ed7ff164a184296268f9984f9312e8fc", + "0x8ac76721f0d2b679f023d06cbd28c85ae5f4b43c614867ccee88651d4101d4fd352dbdb65bf36bfc3ebc0109e4b0c6f9", + "0x8d350f7c05fc0dcd9a1170748846fb1f5d39453e4cb31e6d1457bed287d96fc393b2ecc53793ca729906a33e59c6834a", + "0xb9913510dfc5056d7ec5309f0b631d1ec53e3a776412ada9aefdaf033c90da9a49fdde6719e7c76340e86599b1f0eec2", + "0x94955626bf4ce87612c5cfffcf73bf1c46a4c11a736602b9ba066328dc52ad6d51e6d4f53453d4ed55a51e0aad810271", + "0xb0fcab384fd4016b2f1e53f1aafd160ae3b1a8865cd6c155d7073ecc1664e05b1d8bca1def39c158c7086c4e1103345e", + "0x827de3f03edfbde08570b72de6662c8bfa499b066a0a27ebad9b481c273097d17a5a0a67f01553da5392ec3f149b2a78", + "0xab7940384c25e9027c55c40df20bd2a0d479a165ced9b1046958353cd69015eeb1e44ed2fd64e407805ba42df10fc7bf", + "0x8ad456f6ff8cd58bd57567d931f923d0c99141978511b17e03cab7390a72b9f62498b2893e1b05c7c22dd274e9a31919", + "0xac75399e999effe564672db426faa17a839e57c5ef735985c70cd559a377adec23928382767b55ed5a52f7b11b54b756", + "0xb17f975a00b817299ac7af5f2024ea820351805df58b43724393bfb3920a8cd747a3bbd4b8286e795521489db3657168", + "0xa2bed800a6d95501674d9ee866e7314063407231491d794f8cf57d5be020452729c1c7cefd8c50dc1540181f5caab248", + "0x9743f5473171271ffdd3cc59a3ae50545901a7b45cd4bc3570db487865f3b73c0595bebabbfe79268809ee1862e86e4a", + "0xb7eab77c2d4687b60d9d7b04e842b3880c7940140012583898d39fcc22d9b9b0a9be2c2e3788b3e6f30319b39c338f09", + "0x8e2b8f797a436a1b661140e9569dcf3e1eea0a77c7ff2bc4ff0f3e49af04ed2de95e255df8765f1d0927fb456a9926b1", + "0x8aefea201d4a1f4ff98ffce94e540bb313f2d4dfe7e9db484a41f13fc316ed02b282e1acc9bc6f56cad2dc2e393a44c9", + "0xb950c17c0e5ca6607d182144aa7556bb0efe24c68f06d79d6413a973b493bfdf04fd147a4f1ab03033a32004cc3ea66f", + "0xb7b8dcbb179a07165f2dc6aa829fad09f582a71b05c3e3ea0396bf9e6fe73076f47035c031c2101e8e38e0d597eadd30", + "0xa9d77ed89c77ec1bf8335d08d41c3c94dcca9fd1c54f22837b4e54506b212aa38d7440126c80648ab7723ff18e65ed72", + "0xa819d6dfd4aef70e52b8402fe5d135f8082d40eb7d3bb5c4d7997395b621e2bb10682a1bad2c9caa33dd818550fc3ec6", + "0x8f6ee34128fac8bbf13ce2d68b2bb363eb4fd65b297075f88e1446ddeac242500eeb4ef0735e105882ff5ba8c44c139b", + "0xb4440e48255c1644bcecf3a1e9958f1ec4901cb5b1122ee5b56ffd02cad1c29c4266999dbb85aa2605c1b125490074d4", + "0xa43304a067bede5f347775d5811cf65a6380a8d552a652a0063580b5c5ef12a0867a39c7912fa219e184f4538eba1251", + "0xa891ad67a790089ffc9f6d53e6a3d63d3556f5f693e0cd8a7d0131db06fd4520e719cfcc3934f0a8f62a95f90840f1d4", + "0xaea6df8e9bb871081aa0fc5a9bafb00be7d54012c5baf653791907d5042a326aeee966fd9012a582cc16695f5baf7042", + "0x8ffa2660dc52ed1cd4eff67d6a84a8404f358a5f713d04328922269bee1e75e9d49afeec0c8ad751620f22352a438e25", + "0x87ec6108e2d63b06abed350f8b363b7489d642486f879a6c3aa90e5b0f335efc2ff2834eef9353951a42136f8e6a1b32", + "0x865619436076c2760d9e87ddc905023c6de0a8d56eef12c98a98c87837f2ca3f27fd26a2ad752252dbcbe2b9f1d5a032", + "0x980437dce55964293cb315c650c5586ffd97e7a944a83f6618af31c9d92c37b53ca7a21bb5bc557c151b9a9e217e7098", + "0x95d128fc369df4ad8316b72aea0ca363cbc7b0620d6d7bb18f7076a8717a6a46956ff140948b0cc4f6d2ce33b5c10054", + "0x8c7212d4a67b9ec70ebbca04358ad2d36494618d2859609163526d7b3acc2fc935ca98519380f55e6550f70a9bc76862", + "0x893a2968819401bf355e85eee0f0ed0406a6d4a7d7f172d0017420f71e00bb0ba984f6020999a3cdf874d3cd8ebcd371", + "0x9103c1af82dece25d87274e89ea0acd7e68c2921c4af3d8d7c82ab0ed9990a5811231b5b06113e7fa43a6bd492b4564f", + "0x99cfd87a94eab7d35466caa4ed7d7bb45e5c932b2ec094258fb14bf205659f83c209b83b2f2c9ccb175974b2a33e7746", + "0x874b6b93e4ee61be3f00c32dd84c897ccd6855c4b6251eb0953b4023634490ed17753cd3223472873cbc6095b2945075", + "0x84a32c0dc4ea60d33aac3e03e70d6d639cc9c4cc435c539eff915017be3b7bdaba33349562a87746291ebe9bc5671f24", + "0xa7057b24208928ad67914e653f5ac1792c417f413d9176ba635502c3f9c688f7e2ee81800d7e3dc0a340c464da2fd9c5", + "0xa03fb9ed8286aacfa69fbd5d953bec591c2ae4153400983d5dbb6cd9ea37fff46ca9e5cceb9d117f73e9992a6c055ad2", + "0x863b2de04e89936c9a4a2b40380f42f20aefbae18d03750fd816c658aee9c4a03df7b12121f795c85d01f415baaeaa59", + "0x8526eb9bd31790fe8292360d7a4c3eed23be23dd6b8b8f01d2309dbfdc0cfd33ad1568ddd7f8a610f3f85a9dfafc6a92", + "0xb46ab8c5091a493d6d4d60490c40aa27950574a338ea5bbc045be3a114af87bdcb160a8c80435a9b7ad815f3cb56a3f3", + "0xaeadc47b41a8d8b4176629557646202f868b1d728b2dda58a347d937e7ffc8303f20d26d6c00b34c851b8aeec547885d", + "0xaebb19fc424d72c1f1822aa7adc744cd0ef7e55727186f8df8771c784925058c248406ebeeaf3c1a9ee005a26e9a10c6", + "0x8ff96e81c1a4a2ab1b4476c21018fae0a67e92129ee36120cae8699f2d7e57e891f5c624902cb1b845b944926a605cc3", + "0x8251b8d2c43fadcaa049a9e7aff838dae4fb32884018d58d46403ac5f3beb5c518bfd45f03b8abb710369186075eb71c", + "0xa8b2a64f865f51a5e5e86a66455c093407933d9d255d6b61e1fd81ffafc9538d73caaf342338a66ba8ee166372a3d105", + "0xaad915f31c6ba7fdc04e2aaac62e84ef434b7ee76a325f07dc430d12c84081999720181067b87d792efd0117d7ee1eab", + "0xa13db3bb60389883fd41d565c54fb5180d9c47ce2fe7a169ae96e01d17495f7f4fa928d7e556e7c74319c4c25d653eb2", + "0xa4491b0198459b3f552855d680a59214eb74e6a4d6c5fa3b309887dc50ebea2ecf6d26c040550f7dc478b452481466fb", + "0x8f017f13d4b1e3f0c087843582b52d5f8d13240912254d826dd11f8703a99a2f3166dfbdfdffd9a3492979d77524276b", + "0x96c3d5dcd032660d50d7cd9db2914f117240a63439966162b10c8f1f3cf74bc83b0f15451a43b31dbd85e4a7ce0e4bb1", + "0xb479ec4bb79573d32e0ec93b92bdd7ec8c26ddb5a2d3865e7d4209d119fd3499eaac527615ffac78c440e60ef3867ae0", + "0xb2c49c4a33aa94b52b6410b599e81ff15490aafa7e43c8031c865a84e4676354a9c81eb4e7b8be6825fdcefd1e317d44", + "0x906dc51d6a90c089b6704b47592805578a6eed106608eeb276832f127e1b8e858b72e448edcbefb497d152447e0e68ff", + "0xb0e81c63b764d7dfbe3f3fddc9905aef50f3633e5d6a4af6b340495124abedcff5700dfd1577bbbed7b6bf97d02719cb", + "0x9304c64701e3b4ed6d146e48a881f7d83a17f58357cca0c073b2bb593afd2d94f6e2a7a1ec511d0a67ad6ff4c3be5937", + "0xb6fdbd12ba05aa598d80b83f70a15ef90e5cba7e6e75fa038540ee741b644cd1f408a6cecfd2a891ef8d902de586c6b5", + "0xb80557871a6521b1b3c74a1ba083ae055b575df607f1f7b04c867ba8c8c181ea68f8d90be6031f4d25002cca27c44da2", + "0xaa7285b8e9712e06b091f64163f1266926a36607f9d624af9996856ed2aaf03a580cb22ce407d1ade436c28b44ca173f", + "0x8148d72b975238b51e6ea389e5486940d22641b48637d7dfadfa603a605bfc6d74a016480023945d0b85935e396aea5d", + "0x8a014933a6aea2684b5762af43dcf4bdbb633cd0428d42d71167a2b6fc563ece5e618bff22f1db2ddb69b845b9a2db19", + "0x990d91740041db770d0e0eb9d9d97d826f09fd354b91c41e0716c29f8420e0e8aac0d575231efba12fe831091ec38d5a", + "0x9454d0d32e7e308ddec57cf2522fb1b67a2706e33fb3895e9e1f18284129ab4f4c0b7e51af25681d248d7832c05eb698", + "0xa5bd434e75bac105cb3e329665a35bce6a12f71dd90c15165777d64d4c13a82bceedb9b48e762bd24034e0fc9fbe45f4", + "0xb09e3b95e41800d4dc29c6ffdaab2cd611a0050347f6414f154a47ee20ee59bf8cf7181454169d479ebce1eb5c777c46", + "0xb193e341d6a047d15eea33766d656d807b89393665a783a316e9ba10518e5515c8e0ade3d6e15641d917a8a172a5a635", + "0xade435ec0671b3621dde69e07ead596014f6e1daa1152707a8c18877a8b067bde2895dd47444ffa69db2bbef1f1d8816", + "0xa7fd3d6d87522dfc56fb47aef9ce781a1597c56a8bbfd796baba907afdc872f753d732bfda1d3402aee6c4e0c189f52d", + "0xa298cb4f4218d0464b2fab393e512bbc477c3225aa449743299b2c3572f065bc3a42d07e29546167ed9e1b6b3b3a3af3", + "0xa9ee57540e1fd9c27f4f0430d194b91401d0c642456c18527127d1f95e2dba41c2c86d1990432eb38a692fda058fafde", + "0x81d6c1a5f93c04e6d8e5a7e0678c1fc89a1c47a5c920bcd36180125c49fcf7c114866b90e90a165823560b19898a7c16", + "0xa4b7a1ec9e93c899b9fd9aaf264c50e42c36c0788d68296a471f7a3447af4dbc81e4fa96070139941564083ec5b5b5a1", + "0xb3364e327d381f46940c0e11e29f9d994efc6978bf37a32586636c0070b03e4e23d00650c1440f448809e1018ef9f6d8", + "0x8056e0913a60155348300e3a62e28b5e30629a90f7dd4fe11289097076708110a1d70f7855601782a3cdc5bdb1ca9626", + "0xb4980fd3ea17bac0ba9ee1c470b17e575bb52e83ebdd7d40c93f4f87bebeaff1c8a679f9d3d09d635f068d37d5bd28bd", + "0x905a9299e7e1853648e398901dfcd437aa575c826551f83520df62984f5679cb5f0ea86aa45ed3e18b67ddc0dfafe809", + "0xab99553bf31a84f2e0264eb34a08e13d8d15e2484aa9352354becf9a15999c76cc568d68274b70a65e49703fc23540d0", + "0xa43681597bc574d2dae8964c9a8dc1a07613d7a1272bdcb818d98c85d44e16d744250c33f3b5e4d552d97396b55e601f", + "0xa54e5a31716fccb50245898c99865644405b8dc920ded7a11f3d19bdc255996054b268e16f2e40273f11480e7145f41e", + "0x8134f3ad5ef2ad4ba12a8a4e4d8508d91394d2bcdc38b7c8c8c0b0a820357ac9f79d286c65220f471eb1adca1d98fc68", + "0x94e2f755e60471578ab2c1adb9e9cea28d4eec9b0e92e0140770bca7002c365fcabfe1e5fb4fe6cfe79a0413712aa3ef", + "0xad48f8d0ce7eb3cc6e2a3086ad96f562e5bed98a360721492ae2e74dc158586e77ec8c35d5fd5927376301b7741bad2b", + "0x8614f0630bdd7fbad3a31f55afd9789f1c605dc85e7dc67e2edfd77f5105f878bb79beded6e9f0b109e38ea7da67e8d5", + "0x9804c284c4c5e77dabb73f655b12181534ca877c3e1e134aa3f47c23b7ec92277db34d2b0a5d38d2b69e5d1c3008a3e3", + "0xa51b99c3088e473afdaa9e0a9f7e75a373530d3b04e44e1148da0726b95e9f5f0c7e571b2da000310817c36f84b19f7f", + "0xac4ff909933b3b76c726b0a382157cdc74ab851a1ac6cef76953c6444441804cc43abb883363f416592e8f6cfbc4550b", + "0xae7d915eb9fc928b65a29d6edbc75682d08584d0014f7bcf17d59118421ae07d26a02137d1e4de6938bcd1ab8ef48fad", + "0x852f7e453b1af89b754df6d11a40d5d41ea057376e8ecacd705aacd2f917457f4a093d6b9a8801837fa0f62986ad7149", + "0x92c6bf5ada5d0c3d4dd8058483de36c215fa98edab9d75242f3eff9db07c734ad67337da6f0eefe23a487bf75a600dee", + "0xa2b42c09d0db615853763552a48d2e704542bbd786aae016eb58acbf6c0226c844f5fb31e428cb6450b9db855f8f2a6f", + "0x880cc07968266dbfdcfbc21815cd69e0eddfee239167ac693fb0413912d816f2578a74f7716eecd6deefa68c6eccd394", + "0xb885b3ace736cd373e8098bf75ba66fa1c6943ca1bc4408cd98ac7074775c4478594f91154b8a743d9c697e1b29f5840", + "0xa51ce78de512bd87bfa0835de819941dffbf18bec23221b61d8096fc9436af64e0693c335b54e7bfc763f287bdca2db6", + "0xa3c76166a3bdb9b06ef696e57603b58871bc72883ee9d45171a30fe6e1d50e30bc9c51b4a0f5a7270e19a77b89733850", + "0xacefc5c6f8a1e7c24d7b41e0fc7f6f3dc0ede6cf3115ffb9a6e54b1d954cbca9bda8ad7a084be9be245a1b8e9770d141", + "0xb420ed079941842510e31cfad117fa11fb6b4f97dfbc6298cb840f27ebaceba23eeaf3f513bcffbf5e4aae946310182d", + "0x95c3bb5ef26c5ed2f035aa5d389c6b3c15a6705b9818a3fefaed28922158b35642b2e8e5a1a620fdad07e75ad4b43af4", + "0x825149f9081ecf07a2a4e3e8b5d21bade86c1a882475d51c55ee909330b70c5a2ac63771c8600c6f38df716af61a3ea1", + "0x873b935aae16d9f08adbc25353cee18af2f1b8d5f26dec6538d6bbddc515f2217ed7d235dcfea59ae61b428798b28637", + "0x9294150843a2bedcedb3bb74c43eb28e759cf9499582c5430bccefb574a8ddd4f11f9929257ff4c153990f9970a2558f", + "0xb619563a811cc531da07f4f04e5c4c6423010ff9f8ed7e6ec9449162e3d501b269fb1c564c09c0429431879b0f45df02", + "0x91b509b87eb09f007d839627514658c7341bc76d468920fe8a740a8cb96a7e7e631e0ea584a7e3dc1172266f641d0f5c", + "0x8b8aceace9a7b9b4317f1f01308c3904d7663856946afbcea141a1c615e21ccad06b71217413e832166e9dd915fbe098", + "0x87b3b36e725833ea0b0f54753c3728c0dbc87c52d44d705ffc709f2d2394414c652d3283bab28dcce09799504996cee0", + "0xb2670aad5691cbf308e4a6a77a075c4422e6cbe86fdba24e9f84a313e90b0696afb6a067eebb42ba2d10340d6a2f6e51", + "0x876784a9aff3d54faa89b2bacd3ff5862f70195d0b2edc58e8d1068b3c9074c0da1cfa23671fe12f35e33b8a329c0ccd", + "0x8b48b9e758e8a8eae182f5cbec96f67d20cca6d3eee80a2d09208eb1d5d872e09ef23d0df8ebbb9b01c7449d0e3e3650", + "0xb79303453100654c04a487bdcadc9e3578bc80930c489a7069a52e8ca1dba36c492c8c899ce025f8364599899baa287d", + "0x961b35a6111da54ece6494f24dacd5ea46181f55775b5f03df0e370c34a5046ac2b4082925855325bb42bc2a2c98381d", + "0xa31feb1be3f5a0247a1f7d487987eb622e34fca817832904c6ee3ee60277e5847945a6f6ea1ac24542c72e47bdf647df", + "0xa12a2aa3e7327e457e1aae30e9612715dd2cfed32892c1cd6dcda4e9a18203af8a44afb46d03b2eed89f6b9c5a2c0c23", + "0xa08265a838e69a2ca2f80fead6ccf16f6366415b920c0b22ee359bcd8d4464ecf156f400a16a7918d52e6d733dd64211", + "0xb723d6344e938d801cca1a00032af200e541d4471fd6cbd38fb9130daa83f6a1dffbbe7e67fc20f9577f884acd7594b2", + "0xa6733d83ec78ba98e72ddd1e7ff79b7adb0e559e256760d0c590a986e742445e8cdf560d44b29439c26d87edd0b07c8c", + "0xa61c2c27d3f7b9ff4695a17afedf63818d4bfba390507e1f4d0d806ce8778d9418784430ce3d4199fd3bdbc2504d2af3", + "0x8332f3b63a6dc985376e8b1b25eeae68be6160fbe40053ba7bcf6f073204f682da72321786e422d3482fd60c9e5aa034", + "0xa280f44877583fbb6b860d500b1a3f572e3ee833ec8f06476b3d8002058e25964062feaa1e5bec1536d734a5cfa09145", + "0xa4026a52d277fcea512440d2204f53047718ebfcae7b48ac57ea7f6bfbc5de9d7304db9a9a6cbb273612281049ddaec5", + "0x95cdf69c831ab2fad6c2535ede9c07e663d2ddccc936b64e0843d2df2a7b1c31f1759c3c20f1e7a57b1c8f0dbb21b540", + "0x95c96cec88806469c277ab567863c5209027cecc06c7012358e5f555689c0d9a5ffb219a464f086b45817e8536b86d2f", + "0xafe38d4684132a0f03d806a4c8df556bf589b25271fbc6fe2e1ed16de7962b341c5003755da758d0959d2e6499b06c68", + "0xa9b77784fda64987f97c3a23c5e8f61b918be0f7c59ba285084116d60465c4a2aaafc8857eb16823282cc83143eb9126", + "0xa830f05881ad3ce532a55685877f529d32a5dbe56cea57ffad52c4128ee0fad0eeaf0da4362b55075e77eda7babe70e5", + "0x992b3ad190d6578033c13ed5abfee4ef49cbc492babb90061e3c51ee4b5790cdd4c8fc1abff1fa2c00183b6b64f0bbbe", + "0xb1015424d9364aeff75de191652dc66484fdbec3e98199a9eb9671ec57bec6a13ff4b38446e28e4d8aedb58dd619cd90", + "0xa745304604075d60c9db36cada4063ac7558e7ec2835d7da8485e58d8422e817457b8da069f56511b02601289fbb8981", + "0xa5ba4330bc5cb3dbe0486ddf995632a7260a46180a08f42ae51a2e47778142132463cc9f10021a9ad36986108fefa1a9", + "0xb419e9fd4babcaf8180d5479db188bb3da232ae77a1c4ed65687c306e6262f8083070a9ac32220cddb3af2ec73114092", + "0xa49e23dc5f3468f3bf3a0bb7e4a114a788b951ff6f23a3396ae9e12cbff0abd1240878a3d1892105413dbc38818e807c", + "0xb7ecc7b4831f650202987e85b86bc0053f40d983f252e9832ef503aea81c51221ce93279da4aa7466c026b2d2070e55d", + "0x96a8c35cb87f84fa84dcd6399cc2a0fd79cc9158ef4bdde4bae31a129616c8a9f2576cd19baa3f497ca34060979aed7d", + "0x8681b2c00aa62c2b519f664a95dcb8faef601a3b961bb4ce5d85a75030f40965e2983871d41ea394aee934e859581548", + "0x85c229a07efa54a713d0790963a392400f55fbb1a43995a535dc6c929f20d6a65cf4efb434e0ad1cb61f689b8011a3bc", + "0x90856f7f3444e5ad44651c28e24cc085a5db4d2ffe79aa53228c26718cf53a6e44615f3c5cda5aa752d5f762c4623c66", + "0x978999b7d8aa3f28a04076f74d11c41ef9c89fdfe514936c4238e0f13c38ec97e51a5c078ebc6409e517bfe7ccb42630", + "0xa099914dd7ed934d8e0d363a648e9038eb7c1ec03fa04dbcaa40f7721c618c3ef947afef7a16b4d7ac8c12aa46637f03", + "0xab2a104fed3c83d16f2cda06878fa5f30c8c9411de71bfb67fd2fc9aa454dcbcf3d299d72f8cc12e919466a50fcf7426", + "0xa4471d111db4418f56915689482f6144efc4664cfb0311727f36c864648d35734351becc48875df96f4abd3cfcf820f9", + "0x83be11727cd30ea94ccc8fa31b09b81c9d6a9a5d3a4686af9da99587332fe78c1f94282f9755854bafd6033549afec91", + "0x88020ff971dc1a01a9e993cd50a5d2131ffdcbb990c1a6aaa54b20d8f23f9546a70918ea57a21530dcc440c1509c24ad", + "0xae24547623465e87905eaffa1fa5d52bb7c453a8dbd89614fa8819a2abcedaf455c2345099b7324ae36eb0ad7c8ef977", + "0xb59b0c60997de1ee00b7c388bc7101d136c9803bf5437b1d589ba57c213f4f835a3e4125b54738e78abbc21b000f2016", + "0xa584c434dfe194546526691b68fa968c831c31da42303a1d735d960901c74011d522246f37f299555416b8cf25c5a548", + "0x80408ce3724f4837d4d52376d255e10f69eb8558399ae5ca6c11b78b98fe67d4b93157d2b9b639f1b5b64198bfe87713", + "0xabb941e8d406c2606e0ddc35c113604fdd9d249eacc51cb64e2991e551b8639ce44d288cc92afa7a1e7fc599cfc84b22", + "0xb223173f560cacb1c21dba0f1713839e348ad02cbfdef0626748604c86f89e0f4c919ed40b583343795bdd519ba952c8", + "0xaf1c70512ec3a19d98b8a1fc3ff7f7f5048a27d17d438d43f561974bbdd116fcd5d5c21040f3447af3f0266848d47a15", + "0x8a44809568ebe50405bede19b4d2607199159b26a1b33e03d180e6840c5cf59d991a4fb150d111443235d75ecad085b7", + "0xb06207cdca46b125a27b3221b5b50cf27af4c527dd7c80e2dbcebbb09778a96df3af67e50f07725239ce3583dad60660", + "0x993352d9278814ec89b26a11c4a7c4941bf8f0e6781ae79559d14749ee5def672259792db4587f85f0100c7bb812f933", + "0x9180b8a718b971fd27bc82c8582d19c4b4f012453e8c0ffeeeffe745581fc6c07875ab28be3af3fa3896d19f0c89ac5b", + "0x8b8e1263eb48d0fe304032dd5ea1f30e73f0121265f7458ba9054d3626894e8a5fef665340abd2ede9653045c2665938", + "0x99a2beee4a10b7941c24b2092192faf52b819afd033e4a2de050fd6c7f56d364d0cf5f99764c3357cf32399e60fc5d74", + "0x946a4aad7f8647ea60bee2c5fcdeb6f9a58fb2cfca70c4d10e458027a04846e13798c66506151be3df9454b1e417893f", + "0xa672a88847652d260b5472d6908d1d57e200f1e492d30dd1cecc441cdfc9b76e016d9bab560efd4d7f3c30801de884a9", + "0x9414e1959c156cde1eb24e628395744db75fc24b9df4595350aaad0bc38e0246c9b4148f6443ef68b8e253a4a6bcf11c", + "0x9316e9e4ec5fab4f80d6540df0e3a4774db52f1d759d2e5b5bcd3d7b53597bb007eb1887cb7dc61f62497d51ffc8d996", + "0x902d6d77bb49492c7a00bc4b70277bc28c8bf9888f4307bb017ac75a962decdedf3a4e2cf6c1ea9f9ba551f4610cbbd7", + "0xb07025a18b0e32dd5e12ec6a85781aa3554329ea12c4cd0d3b2c22e43d777ef6f89876dd90a9c8fb097ddf61cf18adc5", + "0xb355a849ad3227caa4476759137e813505ec523cbc2d4105bc7148a4630f9e81918d110479a2d5f5e4cd9ccec9d9d3e3", + "0xb49532cfdf02ee760109881ad030b89c48ee3bb7f219ccafc13c93aead754d29bdafe345be54c482e9d5672bd4505080", + "0x9477802410e263e4f938d57fa8f2a6cac7754c5d38505b73ee35ea3f057aad958cb9722ba6b7b3cfc4524e9ca93f9cdc", + "0x9148ea83b4436339580f3dbc9ba51509e9ab13c03063587a57e125432dd0915f5d2a8f456a68f8fff57d5f08c8f34d6e", + "0xb00b6b5392b1930b54352c02b1b3b4f6186d20bf21698689bbfc7d13e86538a4397b90e9d5c93fd2054640c4dbe52a4f", + "0x926a9702500441243cd446e7cbf15dde16400259726794694b1d9a40263a9fc9e12f7bcbf12a27cb9aaba9e2d5848ddc", + "0xa0c6155f42686cbe7684a1dc327100962e13bafcf3db97971fc116d9f5c0c8355377e3d70979cdbd58fd3ea52440901c", + "0xa277f899f99edb8791889d0817ea6a96c24a61acfda3ad8c3379e7c62b9d4facc4b965020b588651672fd261a77f1bfc", + "0x8f528cebb866b501f91afa50e995234bef5bf20bff13005de99cb51eaac7b4f0bf38580cfd0470de40f577ead5d9ba0f", + "0x963fc03a44e9d502cc1d23250efef44d299befd03b898d07ce63ca607bb474b5cf7c965a7b9b0f32198b04a8393821f7", + "0xab087438d0a51078c378bf4a93bd48ef933ff0f1fa68d02d4460820df564e6642a663b5e50a5fe509527d55cb510ae04", + "0xb0592e1f2c54746bb076be0fa480e1c4bebc4225e1236bcda3b299aa3853e3afb401233bdbcfc4a007b0523a720fbf62", + "0x851613517966de76c1c55a94dc4595f299398a9808f2d2f0a84330ba657ab1f357701d0895f658c18a44cb00547f6f57", + "0xa2fe9a1dd251e72b0fe4db27be508bb55208f8f1616b13d8be288363ec722826b1a1fd729fc561c3369bf13950bf1fd6", + "0xb896cb2bc2d0c77739853bc59b0f89b2e008ba1f701c9cbe3bef035f499e1baee8f0ff1e794854a48c320586a2dfc81a", + "0xa1b60f98e5e5106785a9b81a85423452ee9ef980fa7fa8464f4366e73f89c50435a0c37b2906052b8e58e212ebd366cf", + "0xa853b0ebd9609656636df2e6acd5d8839c0fda56f7bf9288a943b06f0b67901a32b95e016ca8bc99bd7b5eab31347e72", + "0xb290fa4c1346963bd5225235e6bdf7c542174dab4c908ab483d1745b9b3a6015525e398e1761c90e4b49968d05e30eea", + "0xb0f65a33ad18f154f1351f07879a183ad62e5144ad9f3241c2d06533dad09cbb2253949daff1bb02d24d16a3569f7ef0", + "0xa00db59b8d4218faf5aeafcd39231027324408f208ec1f54d55a1c41228b463b88304d909d16b718cfc784213917b71e", + "0xb8d695dd33dc2c3bc73d98248c535b2770ad7fa31aa726f0aa4b3299efb0295ba9b4a51c71d314a4a1bd5872307534d1", + "0xb848057cca2ca837ee49c42b88422303e58ea7d2fc76535260eb5bd609255e430514e927cc188324faa8e657396d63ec", + "0x92677836061364685c2aaf0313fa32322746074ed5666fd5f142a7e8f87135f45cd10e78a17557a4067a51dfde890371", + "0xa854b22c9056a3a24ab164a53e5c5cf388616c33e67d8ebb4590cb16b2e7d88b54b1393c93760d154208b5ca822dc68f", + "0x86fff174920388bfab841118fb076b2b0cdec3fdb6c3d9a476262f82689fb0ed3f1897f7be9dbf0932bb14d346815c63", + "0x99661cf4c94a74e182752bcc4b98a8c2218a8f2765642025048e12e88ba776f14f7be73a2d79bd21a61def757f47f904", + "0x8a8893144d771dca28760cba0f950a5d634195fd401ec8cf1145146286caffb0b1a6ba0c4c1828d0a5480ce49073c64c", + "0x938a59ae761359ee2688571e7b7d54692848eb5dde57ffc572b473001ea199786886f8c6346a226209484afb61d2e526", + "0x923f68a6aa6616714cf077cf548aeb845bfdd78f2f6851d8148cba9e33a374017f2f3da186c39b82d14785a093313222", + "0xac923a93d7da7013e73ce8b4a2b14b8fd0cc93dc29d5de941a70285bdd19be4740fedfe0c56b046689252a3696e9c5bc", + "0xb49b32c76d4ec1a2c68d4989285a920a805993bc6fcce6dacd3d2ddae73373050a5c44ba8422a3781050682fa0ef6ba2", + "0x8a367941c07c3bdca5712524a1411bad7945c7c48ffc7103b1d4dff2c25751b0624219d1ccde8c3f70c465f954be5445", + "0xb838f029df455efb6c530d0e370bbbf7d87d61a9aea3d2fe5474c5fe0a39cf235ceecf9693c5c6c5820b1ba8f820bd31", + "0xa8983b7c715eaac7f13a001d2abc462dfc1559dab4a6b554119c271aa8fe00ffcf6b6949a1121f324d6d26cb877bcbae", + "0xa2afb24ad95a6f14a6796315fbe0d8d7700d08f0cfaf7a2abe841f5f18d4fecf094406cbd54da7232a159f9c5b6e805e", + "0x87e8e95ad2d62f947b2766ff405a23f7a8afba14e7f718a691d95369c79955cdebe24c54662553c60a3f55e6322c0f6f", + "0x87c2cbcecb754e0cc96128e707e5c5005c9de07ffd899efa3437cadc23362f5a1d3fcdd30a1f5bdc72af3fb594398c2a", + "0x91afd6ee04f0496dc633db88b9370d41c428b04fd991002502da2e9a0ef051bcd7b760e860829a44fbe5539fa65f8525", + "0x8c50e5d1a24515a9dd624fe08b12223a75ca55196f769f24748686315329b337efadca1c63f88bee0ac292dd0a587440", + "0x8a07e8f912a38d94309f317c32068e87f68f51bdfa082d96026f5f5f8a2211621f8a3856dda8069386bf15fb2d28c18f", + "0x94ad1dbe341c44eeaf4dc133eed47d8dbfe752575e836c075745770a6679ff1f0e7883b6aa917462993a7f469d74cab5", + "0x8745f8bd86c2bb30efa7efb7725489f2654f3e1ac4ea95bd7ad0f3cfa223055d06c187a16192d9d7bdaea7b050c6a324", + "0x900d149c8d79418cda5955974c450a70845e02e5a4ecbcc584a3ca64d237df73987c303e3eeb79da1af83bf62d9e579f", + "0x8f652ab565f677fb1a7ba03b08004e3cda06b86c6f1b0b9ab932e0834acf1370abb2914c15b0d08327b5504e5990681c", + "0x9103097d088be1f75ab9d3da879106c2f597e2cc91ec31e73430647bdd5c33bcfd771530d5521e7e14df6acda44f38a6", + "0xb0fec7791cfb0f96e60601e1aeced9a92446b61fedab832539d1d1037558612d78419efa87ff5f6b7aab8fd697d4d9de", + "0xb9d2945bdb188b98958854ba287eb0480ef614199c4235ce5f15fc670b8c5ffe8eeb120c09c53ea8a543a022e6a321ac", + "0xa9461bb7d5490973ebaa51afc0bb4a5e42acdccb80e2f939e88b77ac28a98870e103e1042899750f8667a8cc9123bae9", + "0xa37fdf11d4bcb2aed74b9f460a30aa34afea93386fa4cdb690f0a71bc58f0b8df60bec56e7a24f225978b862626fa00e", + "0xa214420e183e03d531cf91661466ea2187d84b6e814b8b20b3730a9400a7d25cf23181bb85589ebc982cec414f5c2923", + "0xad09a45a698a6beb3e0915f540ef16e9af7087f53328972532d6b5dfe98ce4020555ece65c6cbad8bd6be8a4dfefe6fd", + "0xab6742800b02728c92d806976764cb027413d6f86edd08ad8bb5922a2969ee9836878cd39db70db0bd9a2646862acc4f", + "0x974ca9305bd5ea1dc1755dff3b63e8bfe9f744321046c1395659bcea2a987b528e64d5aa96ac7b015650b2253b37888d", + "0x84eee9d6bce039c52c2ebc4fccc0ad70e20c82f47c558098da4be2f386a493cbc76adc795b5488c8d11b6518c2c4fab8", + "0x875d7bda46efcb63944e1ccf760a20144df3b00d53282b781e95f12bfc8f8316dfe6492c2efbf796f1150e36e436e9df", + "0xb68a2208e0c587b5c31b5f6cb32d3e6058a9642e2d9855da4f85566e1412db528475892060bb932c55b3a80877ad7b4a", + "0xba006368ecab5febb6ab348644d9b63de202293085ed468df8bc24d992ae8ce468470aa37f36a73630c789fb9c819b30", + "0x90a196035150846cd2b482c7b17027471372a8ce7d914c4d82b6ea7fa705d8ed5817bd42d63886242585baf7d1397a1c", + "0xa223b4c85e0daa8434b015fd9170b5561fe676664b67064974a1e9325066ecf88fc81f97ab5011c59fad28cedd04b240", + "0x82e8ec43139cf15c6bbeed484b62e06cded8a39b5ce0389e4cbe9c9e9c02f2f0275d8d8d4e8dfec8f69a191bef220408", + "0x81a3fc07a7b68d92c6ee4b6d28f5653ee9ec85f7e2ee1c51c075c1b130a8c5097dc661cf10c5aff1c7114b1a6a19f11a", + "0x8ed2ef8331546d98819a5dd0e6c9f8cb2630d0847671314a28f277faf68da080b53891dd75c82cbcf7788b255490785d", + "0xacecabf84a6f9bbed6b2fc2e7e4b48f02ef2f15e597538a73aea8f98addc6badda15e4695a67ecdb505c1554e8f345ec", + "0xb8f51019b2aa575f8476e03dcadf86cc8391f007e5f922c2a36b2daa63f5a503646a468990cd5c65148d323942193051", + "0xaaa595a84b403ec65729bc1c8055a94f874bf9adddc6c507b3e1f24f79d3ad359595a672b93aab3394db4e2d4a7d8970", + "0x895144c55fcbd0f64d7dd69e6855cfb956e02b5658eadf0f026a70703f3643037268fdd673b0d21b288578a83c6338dd", + "0xa2e92ae6d0d237d1274259a8f99d4ea4912a299816350b876fba5ebc60b714490e198a916e1c38c6e020a792496fa23c", + "0xa45795fda3b5bb0ad1d3c628f6add5b2a4473a1414c1a232e80e70d1cfffd7f8a8d9861f8df2946999d7dbb56bf60113", + "0xb6659bf7f6f2fef61c39923e8c23b8c70e9c903028d8f62516d16755cd3fba2fe41c285aa9432dc75ab08f8a1d8a81fc", + "0xa735609a6bc5bfd85e58234fc439ff1f58f1ff1dd966c5921d8b649e21f006bf2b8642ad8a75063c159aaf6935789293", + "0xa3c622eb387c9d15e7bda2e3e84d007cb13a6d50d655c3f2f289758e49d3b37b9a35e4535d3cc53d8efd51f407281f19", + "0x8afe147b53ad99220f5ef9d763bfc91f9c20caecbcf823564236fb0e6ede49414c57d71eec4772c8715cc65a81af0047", + "0xb5f0203233cf71913951e9c9c4e10d9243e3e4a1f2cb235bf3f42009120ba96e04aa414c9938ea8873b63148478927e8", + "0x93c52493361b458d196172d7ba982a90a4f79f03aa8008edc322950de3ce6acf4c3977807a2ffa9e924047e02072b229", + "0xb9e72b805c8ac56503f4a86c82720afbd5c73654408a22a2ac0b2e5caccdfb0e20b59807433a6233bc97ae58cf14c70a", + "0xaf0475779b5cee278cca14c82da2a9f9c8ef222eb885e8c50cca2315fea420de6e04146590ed0dd5a29c0e0812964df5", + "0xb430ccab85690db02c2d0eb610f3197884ca12bc5f23c51e282bf3a6aa7e4a79222c3d8761454caf55d6c01a327595f9", + "0x830032937418b26ee6da9b5206f3e24dc76acd98589e37937e963a8333e5430abd6ce3dd93ef4b8997bd41440eed75d6", + "0x8820a6d73180f3fe255199f3f175c5eb770461ad5cfdde2fb11508041ed19b8c4ce66ad6ecebf7d7e836cc2318df47ca", + "0xaef1393e7d97278e77bbf52ef6e1c1d5db721ccf75fe753cf47a881fa034ca61eaa5098ee5a344c156d2b14ff9e284ad", + "0x8a4a26c07218948c1196c45d927ef4d2c42ade5e29fe7a91eaebe34a29900072ce5194cf28d51f746f4c4c649daf4396", + "0x84011dc150b7177abdcb715efbd8c201f9cb39c36e6069af5c50a096021768ba40cef45b659c70915af209f904ede3b6", + "0xb1bd90675411389bb66910b21a4bbb50edce5330850c5ab0b682393950124252766fc81f5ecfc72fb7184387238c402e", + "0x8dfdcd30583b696d2c7744655f79809f451a60c9ad5bf1226dc078b19f4585d7b3ef7fa9d54e1ac09520d95cbfd20928", + "0xb351b4dc6d98f75b8e5a48eb7c6f6e4b78451991c9ba630e5a1b9874c15ac450cd409c1a024713bf2cf82dc400e025ef", + "0xa462b8bc97ac668b97b28b3ae24b9f5de60e098d7b23ecb600d2194cd35827fb79f77c3e50d358f5bd72ee83fef18fa0", + "0xa183753265c5f7890270821880cce5f9b2965b115ba783c6dba9769536f57a04465d7da5049c7cf8b3fcf48146173c18", + "0xa8a771b81ed0d09e0da4d79f990e58eabcd2be3a2680419502dd592783fe52f657fe55125b385c41d0ba3b9b9cf54a83", + "0xa71ec577db46011689d073245e3b1c3222a9b1fe6aa5b83629adec5733dd48617ebea91346f0dd0e6cdaa86e4931b168", + "0xa334b8b244f0d598a02da6ae0f918a7857a54dce928376c4c85df15f3b0f2ba3ac321296b8b7c9dd47d770daf16c8f8c", + "0xa29037f8ef925c417c90c4df4f9fb27fb977d04e2b3dd5e8547d33e92ab72e7a00f5461de21e28835319eae5db145eb7", + "0xb91054108ae78b00e3298d667b913ebc44d8f26e531eae78a8fe26fdfb60271c97efb2dee5f47ef5a3c15c8228138927", + "0x926c13efbe90604f6244be9315a34f72a1f8d1aab7572df431998949c378cddbf2fe393502c930fff614ff06ae98a0ce", + "0x995c758fd5600e6537089b1baa4fbe0376ab274ff3e82a17768b40df6f91c2e443411de9cafa1e65ea88fb8b87d504f4", + "0x9245ba307a7a90847da75fca8d77ec03fdfc812c871e7a2529c56a0a79a6de16084258e7a9ac4ae8a3756f394336e21c", + "0x99e0cfa2bb57a7e624231317044c15e52196ecce020db567c8e8cb960354a0be9862ee0c128c60b44777e65ac315e59f", + "0xad4f6b3d27bbbb744126601053c3dc98c07ff0eb0b38a898bd80dce778372846d67e5ab8fb34fb3ad0ef3f235d77ba7f", + "0xa0f12cae3722bbbca2e539eb9cc7614632a2aefe51410430070a12b5bc5314ecec5857b7ff8f41e9980cac23064f7c56", + "0xb487f1bc59485848c98222fd3bc36c8c9bb3d2912e2911f4ceca32c840a7921477f9b1fe00877e05c96c75d3eecae061", + "0xa6033db53925654e18ecb3ce715715c36165d7035db9397087ac3a0585e587998a53973d011ac6d48af439493029cee6", + "0xa6b4d09cd01c70a3311fd131d3710ccf97bde3e7b80efd5a8c0eaeffeb48cca0f951ced905290267b115b06d46f2693b", + "0xa9dff1df0a8f4f218a98b6f818a693fb0d611fed0fc3143537cbd6578d479af13a653a8155e535548a2a0628ae24fa58", + "0xa58e469f65d366b519f9a394cacb7edaddac214463b7b6d62c2dbc1316e11c6c5184ce45c16de2d77f990dcdd8b55430", + "0x989e71734f8119103586dc9a3c5f5033ddc815a21018b34c1f876cdfc112efa868d5751bf6419323e4e59fa6a03ece1c", + "0xa2da00e05036c884369e04cf55f3de7d659cd5fa3f849092b2519dd263694efe0f051953d9d94b7e121f0aee8b6174d7", + "0x968f3c029f57ee31c4e1adea89a7f92e28483af9a74f30fbdb995dc2d40e8e657dff8f8d340d4a92bf65f54440f2859f", + "0x932778df6f60ac1639c1453ef0cbd2bf67592759dcccb3e96dcc743ff01679e4c7dd0ef2b0833dda548d32cb4eba49e2", + "0xa805a31139f8e0d6dae1ac87d454b23a3dc9fc653d4ca18d4f8ebab30fc189c16e73981c2cb7dd6f8c30454a5208109d", + "0xa9ba0991296caa2aaa4a1ceacfb205544c2a2ec97088eace1d84ee5e2767656a172f75d2f0c4e16a3640a0e0dec316e0", + "0xb1e49055c968dced47ec95ae934cf45023836d180702e20e2df57e0f62fb85d7ac60d657ba3ae13b8560b67210449459", + "0xa94e1da570a38809c71e37571066acabff7bf5632737c9ab6e4a32856924bf6211139ab3cedbf083850ff2d0e0c0fcfc", + "0x88ef1bb322000c5a5515b310c838c9af4c1cdbb32eab1c83ac3b2283191cd40e9573747d663763a28dad0d64adc13840", + "0xa987ce205f923100df0fbd5a85f22c9b99b9b9cbe6ddfa8dfda1b8fe95b4f71ff01d6c5b64ca02eb24edb2b255a14ef0", + "0x84fe8221a9e95d9178359918a108de4763ebfa7a6487facb9c963406882a08a9a93f492f8e77cf9e7ea41ae079c45993", + "0xaa1cf3dc7c5dcfa15bbbc811a4bb6dbac4fba4f97fb1ed344ab60264d7051f6eef19ea9773441d89929ee942ed089319", + "0x8f6a7d610d59d9f54689bbe6a41f92d9f6096cde919c1ab94c3c7fcecf0851423bc191e5612349e10f855121c0570f56", + "0xb5af1fa7894428a53ea520f260f3dc3726da245026b6d5d240625380bfb9c7c186df0204bb604efac5e613a70af5106e", + "0xa5bce6055ff812e72ce105f147147c7d48d7a2313884dd1f488b1240ee320f13e8a33f5441953a8e7a3209f65b673ce1", + "0xb9b55b4a1422677d95821e1d042ab81bbf0bf087496504021ec2e17e238c2ca6b44fb3b635a5c9eac0871a724b8d47c3", + "0x941c38e533ce4a673a3830845b56786585e5fe49c427f2e5c279fc6db08530c8f91db3e6c7822ec6bb4f956940052d18", + "0xa38e191d66c625f975313c7007bbe7431b5a06ed2da1290a7d5d0f2ec73770d476efd07b8e632de64597d47df175cbb0", + "0x94ba76b667abf055621db4c4145d18743a368d951565632ed4e743dd50dd3333507c0c34f286a5c5fdbf38191a2255cd", + "0xa5ca38c60be5602f2bfa6e00c687ac96ac36d517145018ddbee6f12eb0faa63dd57909b9eeed26085fe5ac44e55d10ab", + "0xb00fea3b825e60c1ed1c5deb4b551aa65a340e5af36b17d5262c9cd2c508711e4dc50dc2521a2c16c7c901902266e64a", + "0x971b86fc4033485e235ccb0997a236206ba25c6859075edbcdf3c943116a5030b7f75ebca9753d863a522ba21a215a90", + "0xb3b31f52370de246ee215400975b674f6da39b2f32514fe6bd54e747752eedca22bb840493b44a67df42a3639c5f901f", + "0xaffbbfac9c1ba7cbfa1839d2ae271dd6149869b75790bf103230637da41857fc326ef3552ff31c15bda0694080198143", + "0xa95d42aa7ef1962520845aa3688f2752d291926f7b0d73ea2ee24f0612c03b43f2b0fe3c9a9a99620ffc8d487b981bc2", + "0x914a266065caf64985e8c5b1cb2e3f4e3fe94d7d085a1881b1fefa435afef4e1b39a98551d096a62e4f5cc1a7f0fdc2e", + "0x81a0b4a96e2b75bc1bf2dbd165d58d55cfd259000a35504d1ffb18bc346a3e6f07602c683723864ffb980f840836fd8d", + "0x91c1556631cddd4c00b65b67962b39e4a33429029d311c8acf73a18600e362304fb68bccb56fde40f49e95b7829e0b87", + "0x8befbacc19e57f7c885d1b7a6028359eb3d80792fe13b92a8400df21ce48deb0bb60f2ddb50e3d74f39f85d7eab23adc", + "0x92f9458d674df6e990789690ec9ca73dacb67fc9255b58c417c555a8cc1208ace56e8e538f86ba0f3615573a0fbac00d", + "0xb4b1b3062512d6ae7417850c08c13f707d5838e43d48eb98dd4621baf62eee9e82348f80fe9b888a12874bfa538771f8", + "0xa13c4a3ac642ede37d9c883f5319e748d2b938f708c9d779714108a449b343f7b71a6e3ef4080fee125b416762920273", + "0xaf44983d5fc8cceee0551ef934e6e653f2d3efa385e5c8a27a272463a6f333e290378cc307c2b664eb923c78994e706e", + "0xa389fd6c59fe2b4031cc244e22d3991e541bd203dd5b5e73a6159e72df1ab41d49994961500dcde7989e945213184778", + "0x8d2141e4a17836c548de9598d7b298b03f0e6c73b7364979a411c464e0628e21cff6ac3d6decdba5d1c4909eff479761", + "0x980b22ef53b7bdf188a3f14bc51b0dbfdf9c758826daa3cbc1e3986022406a8aa9a6a79e400567120b88c67faa35ce5f", + "0xa28882f0a055f96df3711de5d0aa69473e71245f4f3e9aa944e9d1fb166e02caa50832e46da6d3a03b4801735fd01b29", + "0x8db106a37d7b88f5d995c126abb563934dd8de516af48e85695d02b1aea07f79217e3cdd03c6f5ca57421830186c772b", + "0xb5a7e50da0559a675c472f7dfaee456caab6695ab7870541b2be8c2b118c63752427184aad81f0e1afc61aef1f28c46f", + "0x9962118780e20fe291d10b64f28d09442a8e1b5cffd0f3dd68d980d0614050a626c616b44e9807fbee7accecae00686a", + "0xb38ddf33745e8d2ad6a991aefaf656a33c5f8cbe5d5b6b6fd03bd962153d8fd0e01b5f8f96d80ae53ab28d593ab1d4e7", + "0x857dc12c0544ff2c0c703761d901aba636415dee45618aba2e3454ff9cbc634a85c8b05565e88520ff9be2d097c8b2b1", + "0xa80d465c3f8cc63af6d74a6a5086b626c1cb4a8c0fee425964c3bd203d9d7094e299f81ce96d58afc20c8c9a029d9dae", + "0x89e1c8fbde8563763be483123a3ed702efac189c6d8ab4d16c85e74bbaf856048cc42d5d6e138633a38572ba5ec3f594", + "0x893a594cf495535f6d216508f8d03c317dcf03446668cba688da90f52d0111ac83d76ad09bf5ea47056846585ee5c791", + "0xaadbd8be0ae452f7f9450c7d2957598a20cbf10139a4023a78b4438172d62b18b0de39754dd2f8862dbd50a3a0815e53", + "0xae7d39670ecca3eb6db2095da2517a581b0e8853bdfef619b1fad9aacd443e7e6a40f18209fadd44038a55085c5fe8b2", + "0x866ef241520eacb6331593cfcb206f7409d2f33d04542e6e52cba5447934e02d44c471f6c9a45963f9307e9809ab91d9", + "0xb1a09911ad3864678f7be79a9c3c3eb5c84a0a45f8dcb52c67148f43439aeaaa9fd3ed3471276b7e588b49d6ebe3033a", + "0xadd07b7f0dbb34049cd8feeb3c18da5944bf706871cfd9f14ff72f6c59ad217ebb1f0258b13b167851929387e4e34cfe", + "0xae048892d5c328eefbdd4fba67d95901e3c14d974bfc0a1fc68155ca9f0d59e61d7ba17c6c9948b120cf35fd26e6fee9", + "0x9185b4f3b7da0ddb4e0d0f09b8a9e0d6943a4611e43f13c3e2a767ed8592d31e0ba3ebe1914026a3627680274291f6e5", + "0xa9c022d4e37b0802284ce3b7ee9258628ab4044f0db4de53d1c3efba9de19d15d65cc5e608dbe149c21c2af47d0b07b5", + "0xb24dbd5852f8f24921a4e27013b6c3fa8885b973266cb839b9c388efad95821d5d746348179dcc07542bd0d0aefad1ce", + "0xb5fb4f279300876a539a27a441348764908bc0051ebd66dc51739807305e73db3d2f6f0f294ffb91b508ab150eaf8527", + "0xace50841e718265b290c3483ed4b0fdd1175338c5f1f7530ae9a0e75d5f80216f4de37536adcbc8d8c95982e88808cd0", + "0xb19cadcde0f63bd1a9c24bd9c2806f53c14c0b9735bf351601498408ba503ddbd2037c891041cbba47f58b8c483f3b21", + "0xb6061e63558d312eb891b97b39aa552fa218568d79ee26fe6dd5b864aea9e3216d8f2e2f3b093503be274766dac41426", + "0x89730fdb2876ab6f0fe780d695f6e12090259027e789b819956d786e977518057e5d1d7f5ab24a3ae3d5d4c97773bd2b", + "0xb6fa841e81f9f2cad0163a02a63ae96dc341f7ae803b616efc6e1da2fbea551c1b96b11ad02c4afbdf6d0cc9f23da172", + "0x8fb66187182629c861ddb6896d7ed3caf2ad050c3dba8ab8eb0d7a2c924c3d44c48d1a148f9e33fb1f061b86972f8d21", + "0x86022ac339c1f84a7fa9e05358c1a5b316b4fc0b83dbe9c8c7225dc514f709d66490b539359b084ce776e301024345fa", + "0xb50b9c321468da950f01480bb62b6edafd42f83c0001d6e97f2bd523a1c49a0e8574fb66380ea28d23a7c4d54784f9f0", + "0xa31c05f7032f30d1dac06678be64d0250a071fd655e557400e4a7f4c152be4d5c7aa32529baf3e5be7c4bd49820054f6", + "0xb95ac0848cd322684772119f5b682d90a66bbf9dac411d9d86d2c34844bbd944dbaf8e47aa41380455abd51687931a78", + "0xae4a6a5ce9553b65a05f7935e61e496a4a0f6fd8203367a2c627394c9ce1e280750297b74cdc48fd1d9a31e93f97bef4", + "0xa22daf35f6e9b05e52e0b07f7bd1dbbebd2c263033fb0e1b2c804e2d964e2f11bc0ece6aca6af079dd3a9939c9c80674", + "0x902150e0cb1f16b9b59690db35281e28998ce275acb313900da8b2d8dfd29fa1795f8ca3ff820c31d0697de29df347c1", + "0xb17b5104a5dc665cdd7d47e476153d715eb78c6e5199303e4b5445c21a7fa7cf85fe7cfd08d7570f4e84e579b005428c", + "0xa03f49b81c15433f121680aa02d734bb9e363af2156654a62bcb5b2ba2218398ccb0ff61104ea5d7df5b16ea18623b1e", + "0x802101abd5d3c88876e75a27ffc2f9ddcce75e6b24f23dba03e5201281a7bd5cc7530b6a003be92d225093ca17d3c3bb", + "0xa4d183f63c1b4521a6b52226fc19106158fc8ea402461a5cccdaa35fee93669df6a8661f45c1750cd01308149b7bf08e", + "0x8d17c22e0c8403b69736364d460b3014775c591032604413d20a5096a94d4030d7c50b9fe3240e31d0311efcf9816a47", + "0x947225acfcce5992eab96276f668c3cbe5f298b90a59f2bb213be9997d8850919e8f496f182689b5cbd54084a7332482", + "0x8df6f4ed216fc8d1905e06163ba1c90d336ab991a18564b0169623eb39b84e627fa267397da15d3ed754d1f3423bff07", + "0x83480007a88f1a36dea464c32b849a3a999316044f12281e2e1c25f07d495f9b1710b4ba0d88e9560e72433addd50bc2", + "0xb3019d6e591cf5b33eb972e49e06c6d0a82a73a75d78d383dd6f6a4269838289e6e07c245f54fed67f5c9bb0fd5e1c5f", + "0x92e8ce05e94927a9fb02debadb99cf30a26172b2705003a2c0c47b3d8002bf1060edb0f6a5750aad827c98a656b19199", + "0xac2aff801448dbbfc13cca7d603fd9c69e82100d997faf11f465323b97255504f10c0c77401e4d1890339d8b224f5803", + "0xb0453d9903d08f508ee27e577445dc098baed6cde0ac984b42e0f0efed62760bd58d5816cf1e109d204607b7b175e30c", + "0xae68dc4ba5067e825d46d2c7c67f1009ceb49d68e8d3e4c57f4bcd299eb2de3575d42ea45e8722f8f28497a6e14a1cfe", + "0xb22486c2f5b51d72335ce819bbafb7fa25eb1c28a378a658f13f9fc79cd20083a7e573248d911231b45a5cf23b561ca7", + "0x89d1201d1dbd6921867341471488b4d2fd0fc773ae1d4d074c78ae2eb779a59b64c00452c2a0255826fca6b3d03be2b1", + "0xa2998977c91c7a53dc6104f5bc0a5b675e5350f835e2f0af69825db8af4aeb68435bdbcc795f3dd1f55e1dd50bc0507f", + "0xb0be4937a925b3c05056ed621910d535ccabf5ab99fd3b9335080b0e51d9607d0fd36cb5781ff340018f6acfca4a9736", + "0xaea145a0f6e0ba9df8e52e84bb9c9de2c2dc822f70d2724029b153eb68ee9c17de7d35063dcd6a39c37c59fdd12138f7", + "0x91cb4545d7165ee8ffbc74c874baceca11fdebbc7387908d1a25877ca3c57f2c5def424dab24148826832f1e880bede0", + "0xb3b579cb77573f19c571ad5eeeb21f65548d7dff9d298b8d7418c11f3e8cd3727c5b467f013cb87d6861cfaceee0d2e3", + "0xb98a1eeec2b19fecc8378c876d73645aa52fb99e4819903735b2c7a885b242787a30d1269a04bfb8573d72d9bbc5f0f0", + "0x940c1f01ed362bd588b950c27f8cc1d52276c71bb153d47f07ec85b038c11d9a8424b7904f424423e714454d5e80d1cd", + "0xaa343a8ecf09ce11599b8cf22f7279cf80f06dbf9f6d62cb05308dbbb39c46fd0a4a1240b032665fbb488a767379b91b", + "0x87c3ac72084aca5974599d3232e11d416348719e08443acaba2b328923af945031f86432e170dcdd103774ec92e988c9", + "0x91d6486eb5e61d2b9a9e742c20ec974a47627c6096b3da56209c2b4e4757f007e793ebb63b2b246857c9839b64dc0233", + "0xaebcd3257d295747dd6fc4ff910d839dd80c51c173ae59b8b2ec937747c2072fa85e3017f9060aa509af88dfc7529481", + "0xb3075ba6668ca04eff19efbfa3356b92f0ab12632dcda99cf8c655f35b7928c304218e0f9799d68ef9f809a1492ff7db", + "0x93ba7468bb325639ec2abd4d55179c69fd04eaaf39fc5340709227bbaa4ad0a54ea8b480a1a3c8d44684e3be0f8d1980", + "0xa6aef86c8c0d92839f38544d91b767c582568b391071228ff5a5a6b859c87bf4f81a7d926094a4ada1993ddbd677a920", + "0x91dcd6d14207aa569194aa224d1e5037b999b69ade52843315ca61ba26abe9a76412c9e88259bc5cf5d7b95b97d9c3bc", + "0xb3b483d31c88f78d49bd065893bc1e3d2aa637e27dedb46d9a7d60be7660ce7a10aaaa7deead362284a52e6d14021178", + "0x8e5730070acf8371461ef301cc4523e8e672aa0e3d945d438a0e0aa6bdf8cb9c685dcf38df429037b0c8aff3955c6f5b", + "0xb8c6d769890a8ee18dc4f9e917993315877c97549549b34785a92543cbeec96a08ae3a28d6e809c4aacd69de356c0012", + "0x95ca86cd384eaceaa7c077c5615736ca31f36824bd6451a16142a1edc129fa42b50724aeed7c738f08d7b157f78b569e", + "0x94df609c6d71e8eee7ab74226e371ccc77e01738fe0ef1a6424435b4570fe1e5d15797b66ed0f64eb88d4a3a37631f0e", + "0x89057b9783212add6a0690d6bb99097b182738deff2bd9e147d7fd7d6c8eacb4c219923633e6309ad993c24572289901", + "0x83a0f9f5f265c5a0e54defa87128240235e24498f20965009fef664f505a360b6fb4020f2742565dfc7746eb185bcec0", + "0x91170da5306128931349bc3ed50d7df0e48a68b8cc8420975170723ac79d8773e4fa13c5f14dc6e3fafcad78379050b1", + "0xb7178484d1b55f7e56a4cc250b6b2ec6040437d96bdfddfa7b35ed27435860f3855c2eb86c636f2911b012eb83b00db8", + "0xac0b00c4322d1e4208e09cd977b4e54d221133ff09551f75b32b0b55d0e2be80941dda26257b0e288c162e63c7e9cf68", + "0x9690ed9e7e53ed37ff362930e4096b878b12234c332fd19d5d064824084245952eda9f979e0098110d6963e468cf513e", + "0xb6fa547bb0bb83e5c5be0ed462a8783fba119041c136a250045c09d0d2af330c604331e7de960df976ff76d67f8000cd", + "0x814603907c21463bcf4e59cfb43066dfe1a50344ae04ef03c87c0f61b30836c3f4dea0851d6fa358c620045b7f9214c8", + "0x9495639e3939fad2a3df00a88603a5a180f3c3a0fe4d424c35060e2043e0921788003689887b1ed5be424d9a89bb18bb", + "0xaba4c02d8d57f2c92d5bc765885849e9ff8393d6554f5e5f3e907e5bfac041193a0d8716d7861104a4295d5a03c36b03", + "0x8ead0b56c1ca49723f94a998ba113b9058059321da72d9e395a667e6a63d5a9dac0f5717cec343f021695e8ced1f72af", + "0xb43037f7e3852c34ed918c5854cd74e9d5799eeddfe457d4f93bb494801a064735e326a76e1f5e50a339844a2f4a8ec9", + "0x99db8422bb7302199eb0ff3c3d08821f8c32f53a600c5b6fb43e41205d96adae72be5b460773d1280ad1acb806af9be8", + "0x8a9be08eae0086c0f020838925984df345c5512ff32e37120b644512b1d9d4fecf0fd30639ca90fc6cf334a86770d536", + "0x81b43614f1c28aa3713a309a88a782fb2bdfc4261dd52ddc204687791a40cf5fd6a263a8179388596582cccf0162efc2", + "0xa9f3a8b76912deb61d966c75daf5ddb868702ebec91bd4033471c8e533183df548742a81a2671de5be63a502d827437d", + "0x902e2415077f063e638207dc7e14109652e42ab47caccd6204e2870115791c9defac5425fd360b37ac0f7bd8fe7011f8", + "0xaa18e4fdc1381b59c18503ae6f6f2d6943445bd00dd7d4a2ad7e5adad7027f2263832690be30d456e6d772ad76f22350", + "0xa348b40ba3ba7d81c5d4631f038186ebd5e5f314f1ea737259151b07c3cc8cf0c6ed4201e71bcc1c22fefda81a20cde6", + "0xaa1306f7ac1acbfc47dc6f7a0cb6d03786cec8c8dc8060388ccda777bca24bdc634d03e53512c23dba79709ff64f8620", + "0x818ccfe46e700567b7f3eb400e5a35f6a5e39b3db3aa8bc07f58ace35d9ae5a242faf8dbccd08d9a9175bbce15612155", + "0xb7e3da2282b65dc8333592bb345a473f03bd6df69170055fec60222de9897184536bf22b9388b08160321144d0940279", + "0xa4d976be0f0568f4e57de1460a1729129252b44c552a69fceec44e5b97c96c711763360d11f9e5bf6d86b4976bf40d69", + "0x85d185f0397c24c2b875b09b6328a23b87982b84ee880f2677a22ff4c9a1ba9f0fea000bb3f7f66375a00d98ebafce17", + "0xb4ccbb8c3a2606bd9b87ce022704663af71d418351575f3b350d294f4efc68c26f9a2ce49ff81e6ff29c3b63d746294e", + "0x93ffd3265fddb63724dfde261d1f9e22f15ecf39df28e4d89e9fea03221e8e88b5dd9b77628bacaa783c6f91802d47cc", + "0xb1fd0f8d7a01378e693da98d03a2d2fda6b099d03454b6f2b1fa6472ff6bb092751ce6290059826b74ac0361eab00e1e", + "0xa89f440c71c561641589796994dd2769616b9088766e983c873fae0716b95c386c8483ab8a4f367b6a68b72b7456dd32", + "0xaf4fe92b01d42d03dd5d1e7fa55e96d4bbcb7bf7d4c8c197acd16b3e0f3455807199f683dcd263d74547ef9c244b35cc", + "0xa8227f6e0a344dfe76bfbe7a1861be32c4f4bed587ccce09f9ce2cf481b2dda8ae4f566154bc663d15f962f2d41761bd", + "0xa7b361663f7495939ed7f518ba45ea9ff576c4e628995b7aea026480c17a71d63fc2c922319f0502eb7ef8f14a406882", + "0x8ddcf382a9f39f75777160967c07012cfa89e67b19714a7191f0c68eaf263935e5504e1104aaabd0899348c972a8d3c6", + "0x98c95b9f6f5c91f805fb185eedd06c6fc4457d37dd248d0be45a6a168a70031715165ea20606245cbdf8815dc0ac697f", + "0x805b44f96e001e5909834f70c09be3efcd3b43632bcac5b6b66b6d227a03a758e4b1768ce2a723045681a1d34562aaeb", + "0xb0e81b07cdc45b3dca60882676d9badb99f25c461b7efe56e3043b80100bb62d29e1873ae25eb83087273160ece72a55", + "0xb0c53f0abe78ee86c7b78c82ae1f7c070bb0b9c45c563a8b3baa2c515d482d7507bb80771e60b38ac13f78b8af92b4a9", + "0xa7838ef6696a9e4d2e5dfd581f6c8d6a700467e8fd4e85adabb5f7a56f514785dd4ab64f6f1b48366f7d94728359441b", + "0x88c76f7700a1d23c30366a1d8612a796da57b2500f97f88fdf2d76b045a9d24e7426a8ffa2f4e86d3046937a841dad58", + "0xad8964baf98c1f02e088d1d9fcb3af6b1dfa44cdfe0ed2eae684e7187c33d3a3c28c38e8f4e015f9c04d451ed6f85ff6", + "0x90e9d00a098317ececaa9574da91fc149eda5b772dedb3e5a39636da6603aa007804fa86358550cfeff9be5a2cb7845e", + "0xa56ff4ddd73d9a6f5ab23bb77efa25977917df63571b269f6a999e1ad6681a88387fcc4ca3b26d57badf91b236503a29", + "0x97ad839a6302c410a47e245df84c01fb9c4dfef86751af3f9340e86ff8fc3cd52fa5ff0b9a0bd1d9f453e02ca80658a6", + "0xa4c8c44cbffa804129e123474854645107d1f0f463c45c30fd168848ebea94880f7c0c5a45183e9eb837f346270bdb35", + "0xa72e53d0a1586d736e86427a93569f52edd2f42b01e78aee7e1961c2b63522423877ae3ac1227a2cf1e69f8e1ff15bc3", + "0x8559f88a7ef13b4f09ac82ae458bbae6ab25671cfbf52dae7eac7280d6565dd3f0c3286aec1a56a8a16dc3b61d78ce47", + "0x8221503f4cdbed550876c5dc118a3f2f17800c04e8be000266633c83777b039a432d576f3a36c8a01e8fd18289ebc10b", + "0x99bfbe5f3e46d4d898a578ba86ed26de7ed23914bd3bcdf3c791c0bcd49398a52419077354a5ab75cea63b6c871c6e96", + "0xaa134416d8ff46f2acd866c1074af67566cfcf4e8be8d97329dfa0f603e1ff208488831ce5948ac8d75bfcba058ddcaa", + "0xb02609d65ebfe1fe8e52f21224a022ea4b5ea8c1bd6e7b9792eed8975fc387cdf9e3b419b8dd5bcce80703ab3a12a45f", + "0xa4f14798508698fa3852e5cac42a9db9797ecee7672a54988aa74037d334819aa7b2ac7b14efea6b81c509134a6b7ad2", + "0x884f01afecbcb987cb3e7c489c43155c416ed41340f61ecb651d8cba884fb9274f6d9e7e4a46dd220253ae561614e44c", + "0xa05523c9e71dce1fe5307cc71bd721feb3e1a0f57a7d17c7d1c9fb080d44527b7dbaa1f817b1af1c0b4322e37bc4bb1e", + "0x8560aec176a4242b39f39433dd5a02d554248c9e49d3179530815f5031fee78ba9c71a35ceeb2b9d1f04c3617c13d8f0", + "0x996aefd402748d8472477cae76d5a2b92e3f092fc834d5222ae50194dd884c9fb8b6ed8e5ccf8f6ed483ddbb4e80c747", + "0x8fd09900320000cbabc40e16893e2fcf08815d288ec19345ad7b6bb22f7d78a52b6575a3ca1ca2f8bc252d2eafc928ec", + "0x939e51f73022bc5dc6862a0adf8fb8a3246b7bfb9943cbb4b27c73743926cc20f615a036c7e5b90c80840e7f1bfee0e7", + "0xa0a6258700cadbb9e241f50766573bf9bdb7ad380b1079dc3afb4054363d838e177b869cad000314186936e40359b1f2", + "0x972699a4131c8ed27a2d0e2104d54a65a7ff1c450ad9da3a325c662ab26869c21b0a84d0700b98c8b5f6ce3b746873d7", + "0xa454c7fe870cb8aa6491eafbfb5f7872d6e696033f92e4991d057b59d70671f2acdabef533e229878b60c7fff8f748b1", + "0xa167969477214201f09c79027b10221e4707662e0c0fde81a0f628249f2f8a859ce3d30a7dcc03b8ecca8f7828ad85c7", + "0x8ff6b7265175beb8a63e1dbf18c9153fb2578c207c781282374f51b40d57a84fd2ef2ea2b9c6df4a54646788a62fd17f", + "0xa3d7ebeccde69d73d8b3e76af0da1a30884bb59729503ff0fb0c3bccf9221651b974a6e72ea33b7956fc3ae758226495", + "0xb71ef144c9a98ce5935620cb86c1590bd4f48e5a2815d25c0cdb008fde628cf628c31450d3d4f67abbfeb16178a74cfd", + "0xb5e0a16d115134f4e2503990e3f2035ed66b9ccf767063fe6747870d97d73b10bc76ed668550cb82eedc9a2ca6f75524", + "0xb30ffaaf94ee8cbc42aa2c413175b68afdb207dbf351fb20be3852cb7961b635c22838da97eaf43b103aff37e9e725cc", + "0x98aa7d52284f6c1f22e272fbddd8c8698cf8f5fbb702d5de96452141fafb559622815981e50b87a72c2b1190f59a7deb", + "0x81fbacda3905cfaf7780bb4850730c44166ed26a7c8d07197a5d4dcd969c09e94a0461638431476c16397dd7bdc449f9", + "0x95e47021c1726eac2e5853f570d6225332c6e48e04c9738690d53e07c6b979283ebae31e2af1fc9c9b3e59f87e5195b1", + "0xac024a661ba568426bb8fce21780406537f518075c066276197300841e811860696f7588188bc01d90bace7bc73d56e3", + "0xa4ebcaf668a888dd404988ab978594dee193dad2d0aec5cdc0ccaf4ec9a7a8228aa663db1da8ddc52ec8472178e40c32", + "0xa20421b8eaf2199d93b083f2aff37fb662670bd18689d046ae976d1db1fedd2c2ff897985ecc6277b396db7da68bcb27", + "0x8bc33d4b40197fd4d49d1de47489d10b90d9b346828f53a82256f3e9212b0cbc6930b895e879da9cec9fedf026aadb3e", + "0xaaafdd1bec8b757f55a0433eddc0a39f818591954fd4e982003437fcceb317423ad7ee74dbf17a2960380e7067a6b4e2", + "0xaad34277ebaed81a6ec154d16736866f95832803af28aa5625bf0461a71d02b1faba02d9d9e002be51c8356425a56867", + "0x976e9c8b150d08706079945bd0e84ab09a648ecc6f64ded9eb5329e57213149ae409ae93e8fbd8eda5b5c69f5212b883", + "0x8097fae1653247d2aed4111533bc378171d6b2c6d09cbc7baa9b52f188d150d645941f46d19f7f5e27b7f073c1ebd079", + "0x83905f93b250d3184eaba8ea7d727c4464b6bdb027e5cbe4f597d8b9dc741dcbea709630bd4fd59ce24023bec32fc0f3", + "0x8095030b7045cff28f34271386e4752f9a9a0312f8df75de4f424366d78534be2b8e1720a19cb1f9a2d21105d790a225", + "0xa7b7b73a6ae2ed1009c49960374b0790f93c74ee03b917642f33420498c188a169724945a975e5adec0a1e83e07fb1b2", + "0x856a41c54df393b6660b7f6354572a4e71c8bfca9cabaffb3d4ef2632c015e7ee2bc10056f3eccb3dbed1ad17d939178", + "0xa8f7a55cf04b38cd4e330394ee6589da3a07dc9673f74804fdf67b364e0b233f14aec42e783200a2e4666f7c5ff62490", + "0x82c529f4e543c6bca60016dc93232c115b359eaee2798a9cf669a654b800aafe6ab4ba58ea8b9cdda2b371c8d62fa845", + "0x8caab020c1baddce77a6794113ef1dfeafc5f5000f48e97f4351b588bf02f1f208101745463c480d37f588d5887e6d8c", + "0x8fa91b3cc400f48b77b6fd77f3b3fbfb3f10cdff408e1fd22d38f77e087b7683adad258804409ba099f1235b4b4d6fea", + "0x8aa02787663d6be9a35677d9d8188b725d5fcd770e61b11b64e3def8808ea5c71c0a9afd7f6630c48634546088fcd8e2", + "0xb5635b7b972e195cab878b97dea62237c7f77eb57298538582a330b1082f6207a359f2923864630136d8b1f27c41b9aa", + "0x8257bb14583551a65975946980c714ecd6e5b629672bb950b9caacd886fbd22704bc9e3ba7d30778adab65dc74f0203a", + "0xab5fe1cd12634bfa4e5c60d946e2005cbd38f1063ec9a5668994a2463c02449a0a185ef331bd86b68b6e23a8780cb3ba", + "0xa7d3487da56cda93570cc70215d438204f6a2709bfb5fda6c5df1e77e2efc80f4235c787e57fbf2c74aaff8cbb510a14", + "0xb61cff7b4c49d010e133319fb828eb900f8a7e55114fc86b39c261a339c74f630e1a7d7e1350244ada566a0ff3d46c4b", + "0x8d4d1d55d321d278db7a85522ccceca09510374ca81d4d73e3bb5249ace7674b73900c35a531ec4fa6448fabf7ad00dc", + "0x966492248aee24f0f56c8cfca3c8ec6ba3b19abb69ae642041d4c3be8523d22c65c4dafcab4c58989ccc4e0bd2f77919", + "0xb20c320a90cb220b86e1af651cdc1e21315cd215da69f6787e28157172f93fc8285dcd59b039c626ed8ca4633cba1a47", + "0xaae9e6b22f018ceb5c0950210bb8182cb8cb61014b7e14581a09d36ebd1bbfebdb2b82afb7fdb0cf75e58a293d9c456d", + "0x875547fb67951ad37b02466b79f0c9b985ccbc500cfb431b17823457dc79fb9597ec42cd9f198e15523fcd88652e63a4", + "0x92afce49773cb2e20fb21e4f86f18e0959ebb9c33361547ddb30454ee8e36b1e234019cbdca0e964cb292f7f77df6b90", + "0x8af85343dfe1821464c76ba11c216cbef697b5afc69c4d821342e55afdac047081ec2e3f7b09fc14b518d9a23b78c003", + "0xb7de4a1648fd63f3a918096ea669502af5357438e69dac77cb8102b6e6c15c76e033cfaa80dafc806e535ede5c1a20aa", + "0xac80e9b545e8bd762951d96c9ce87f629d01ffcde07efc2ef7879ca011f1d0d8a745abf26c9d452541008871304fac00", + "0xa4cf0f7ed724e481368016c38ea5816698a5f68eb21af4d3c422d2ba55f96a33e427c2aa40de1b56a7cfac7f7cf43ab0", + "0x899b0a678bb2db2cae1b44e75a661284844ebcdd87abf308fedeb2e4dbe5c5920c07db4db7284a7af806a2382e8b111a", + "0xaf0588a2a4afce2b1b13c1230816f59e8264177e774e4a341b289a101dcf6af813638fed14fb4d09cb45f35d5d032609", + "0xa4b8df79e2be76e9f5fc5845f06fe745a724cf37c82fcdb72719b77bdebea3c0e763f37909373e3a94480cc5e875cba0", + "0x83e42c46d88930c8f386b19fd999288f142d325e2ebc86a74907d6d77112cb0d449bc511c95422cc810574031a8cbba9", + "0xb5e39534070de1e5f6e27efbdd3dc917d966c2a9b8cf2d893f964256e95e954330f2442027dc148c776d63a95bcde955", + "0x958607569dc28c075e658cd4ae3927055c6bc456eef6212a6fea8205e48ed8777a8064f584cda38fe5639c371e2e7fba", + "0x812adf409fa63575113662966f5078a903212ffb65c9b0bbe62da0f13a133443a7062cb8fd70f5e5dd5559a32c26d2c8", + "0xa679f673e5ce6a3cce7fa31f22ee3785e96bcb55e5a776e2dd3467bef7440e3555d1a9b87cb215e86ee9ed13a090344b", + "0xafedbb34508b159eb25eb2248d7fe328f86ef8c7d84c62d5b5607d74aae27cc2cc45ee148eb22153b09898a835c58df4", + "0xb75505d4f6b67d31e665cfaf5e4acdb5838ae069166b7fbcd48937c0608a59e40a25302fcc1873d2e81c1782808c70f0", + "0xb62515d539ec21a155d94fc00ea3c6b7e5f6636937bce18ed5b618c12257fb82571886287fd5d1da495296c663ebc512", + "0xab8e1a9446bbdd588d1690243b1549d230e6149c28f59662b66a8391a138d37ab594df38e7720fae53217e5c3573b5be", + "0xb31e8abf4212e03c3287bb2c0a153065a7290a16764a0bac8f112a72e632185a654bb4e88fdd6053e6c7515d9719fadb", + "0xb55165477fe15b6abd2d0f4fddaa9c411710dcc4dd712daba3d30e303c9a3ee5415c256f9dc917ecf18c725b4dbab059", + "0xa0939d4f57cacaae549b78e87cc234de4ff6a35dc0d9cd5d7410abc30ebcd34c135e008651c756e5a9d2ca79c40ef42b", + "0x8cf10e50769f3443340844aad4d56ec790850fed5a41fcbd739abac4c3015f0a085a038fbe7fae9f5ad899cce5069f6b", + "0x924055e804d82a99ea4bb160041ea4dc14b568abf379010bc1922fde5d664718c31d103b8b807e3a1ae809390e708c73", + "0x8ec0f9d26f71b0f2e60a179e4fd1778452e2ffb129d50815e5d7c7cb9415fa69ae5890578086e8ef6bfde35ad2a74661", + "0x98c7f12b15ec4426b59f737f73bf5faea4572340f4550b7590dfb7f7ffedb2372e3e555977c63946d579544c53210ad0", + "0x8a935f7a955c78f69d66f18eee0092e5e833fa621781c9581058e219af4d7ceee48b84e472e159dda6199715fb2f9acf", + "0xb78d4219f95a2dbfaa7d0c8a610c57c358754f4f43c2af312ab0fe8f10a5f0177e475332fb8fd23604e474fc2abeb051", + "0x8d086a14803392b7318c28f1039a17e3cfdcece8abcaca3657ec3d0ac330842098a85c0212f889fabb296dfb133ce9aa", + "0xa53249f417aac82f2c2a50c244ce21d3e08a5e5a8bd33bec2a5ab0d6cd17793e34a17edfa3690899244ce201e2fb9986", + "0x8619b0264f9182867a1425be514dc4f1ababc1093138a728a28bd7e4ecc99b9faaff68c23792264bc6e4dce5f52a5c52", + "0x8c171edbbbde551ec19e31b2091eb6956107dd9b1f853e1df23bff3c10a3469ac77a58335eee2b79112502e8e163f3de", + "0xa9d19ec40f0ca07c238e9337c6d6a319190bdba2db76fb63902f3fb459aeeb50a1ac30db5b25ee1b4201f3ca7164a7f4", + "0xb9c6ec14b1581a03520b8d2c1fbbc31fb8ceaef2c0f1a0d0080b6b96e18442f1734bea7ef7b635d787c691de4765d469", + "0x8cb437beb4cfa013096f40ccc169a713dc17afee6daa229a398e45fd5c0645a9ad2795c3f0cd439531a7151945d7064d", + "0xa6e8740cc509126e146775157c2eb278003e5bb6c48465c160ed27888ca803fa12eee1f6a8dd7f444f571664ed87fdc1", + "0xb75c1fecc85b2732e96b3f23aefb491dbd0206a21d682aee0225838dc057d7ed3b576176353e8e90ae55663f79e986e4", + "0xad8d249b0aea9597b08358bce6c77c1fd552ef3fbc197d6a1cfe44e5e6f89b628b12a6fb04d5dcfcbacc51f46e4ae7bb", + "0xb998b2269932cbd58d04b8e898d373ac4bb1a62e8567484f4f83e224061bc0f212459f1daae95abdbc63816ae6486a55", + "0x827988ef6c1101cddc96b98f4a30365ff08eea2471dd949d2c0a9b35c3bbfa8c07054ad1f4c88c8fbf829b20bb5a9a4f", + "0x8692e638dd60babf7d9f2f2d2ce58e0ac689e1326d88311416357298c6a2bffbfebf55d5253563e7b3fbbf5072264146", + "0xa685d75b91aea04dbc14ab3c1b1588e6de96dae414c8e37b8388766029631b28dd860688079b12d09cd27f2c5af11adf", + "0xb57eced93eec3371c56679c259b34ac0992286be4f4ff9489d81cf9712403509932e47404ddd86f89d7c1c3b6391b28c", + "0xa1c8b4e42ebcbd8927669a97f1b72e236fb19249325659e72be7ddaaa1d9e81ca2abb643295d41a8c04a2c01f9c0efd7", + "0x877c33de20d4ed31674a671ba3e8f01a316581e32503136a70c9c15bf0b7cb7b1cba6cd4eb641fad165fb3c3c6c235fd", + "0xa2a469d84ec478da40838f775d11ad38f6596eb41caa139cc190d6a10b5108c09febae34ffdafac92271d2e73c143693", + "0x972f817caedb254055d52e963ed28c206848b6c4cfdb69dbc961c891f8458eaf582a6d4403ce1177d87bc2ea410ef60a", + "0xaccbd739e138007422f28536381decc54bb6bd71d93edf3890e54f9ef339f83d2821697d1a4ac1f5a98175f9a9ecb9b5", + "0x8940f8772e05389f823b62b3adc3ed541f91647f0318d7a0d3f293aeeb421013de0d0a3664ea53dd24e5fbe02d7efef6", + "0x8ecce20f3ef6212edef07ec4d6183fda8e0e8cad2c6ccd0b325e75c425ee1faba00b5c26b4d95204238931598d78f49d", + "0x97cc72c36335bd008afbed34a3b0c7225933faba87f7916d0a6d2161e6f82e0cdcda7959573a366f638ca75d30e9dab1", + "0x9105f5de8699b5bdb6bd3bb6cc1992d1eac23929c29837985f83b22efdda92af64d9c574aa9640475087201bbbe5fd73", + "0x8ffb33c4f6d05c413b9647eb6933526a350ed2e4278ca2ecc06b0e8026d8dbe829c476a40e45a6df63a633090a3f82ef", + "0x8bfc6421fdc9c2d2aaa68d2a69b1a2728c25b84944cc3e6a57ff0c94bfd210d1cbf4ff3f06702d2a8257024d8be7de63", + "0xa80e1dc1dddfb41a70220939b96dc6935e00b32fb8be5dff4eed1f1c650002ff95e4af481c43292e3827363b7ec4768a", + "0x96f714ebd54617198bd636ba7f7a7f8995a61db20962f2165078d9ed8ee764d5946ef3cbdc7ebf8435bb8d5dd4c1deac", + "0x8cdb0890e33144d66391d2ae73f5c71f5a861f72bc93bff6cc399fc25dd1f9e17d8772592b44593429718784802ac377", + "0x8ccf9a7f80800ee770b92add734ed45a73ecc31e2af0e04364eefc6056a8223834c7c0dc9dfc52495bdec6e74ce69994", + "0xaa0875f423bd68b5f10ba978ddb79d3b96ec093bfbac9ff366323193e339ed7c4578760fb60f60e93598bdf1e5cc4995", + "0xa9214f523957b59c7a4cb61a40251ad72aba0b57573163b0dc0f33e41d2df483fb9a1b85a5e7c080e9376c866790f8cb", + "0xb6224b605028c6673a536cc8ff9aeb94e7a22e686fda82cf16068d326469172f511219b68b2b3affb7933af0c1f80d07", + "0xb6d58968d8a017c6a34e24c2c09852f736515a2c50f37232ac6b43a38f8faa7572cc31dade543b594b61b5761c4781d0", + "0x8a97cefe5120020c38deeb861d394404e6c993c6cbd5989b6c9ebffe24f46ad11b4ba6348e2991cbf3949c28cfc3c99d", + "0x95bf046f8c3a9c0ce2634be4de3713024daec3fc4083e808903b25ce3ac971145af90686b451efcc72f6b22df0216667", + "0xa6a4e2f71b8fa28801f553231eff2794c0f10d12e7e414276995e21195abc9c2983a8997e41af41e78d19ff6fbb2680b", + "0x8e5e62a7ca9c2f58ebaab63db2ff1fb1ff0877ae94b7f5e2897f273f684ae639dff44cc65718f78a9c894787602ab26a", + "0x8542784383eec4f565fcb8b9fc2ad8d7a644267d8d7612a0f476fc8df3aff458897a38003d506d24142ad18f93554f2b", + "0xb7db68ba4616ea072b37925ec4fb39096358c2832cc6d35169e032326b2d6614479f765ae98913c267105b84afcb9bf2", + "0x8b31dbb9457d23d416c47542c786e07a489af35c4a87dadb8ee91bea5ac4a5315e65625d78dad2cf8f9561af31b45390", + "0xa8545a1d91ac17257732033d89e6b7111db8242e9c6ebb0213a88906d5ef407a2c6fdb444e29504b06368b6efb4f4839", + "0xb1bd85d29ebb28ccfb05779aad8674906b267c2bf8cdb1f9a0591dd621b53a4ee9f2942687ee3476740c0b4a7621a3ae", + "0xa2b54534e152e46c50d91fff03ae9cd019ff7cd9f4168b2fe7ac08ef8c3bbc134cadd3f9d6bd33d20ae476c2a8596c8a", + "0xb19b571ff4ae3e9f5d95acda133c455e72c9ea9973cae360732859836c0341c4c29ab039224dc5bc3deb824e031675d8", + "0x940b5f80478648bac025a30f3efeb47023ce20ee98be833948a248bca6979f206bb28fc0f17b90acf3bb4abd3d14d731", + "0x8f106b40588586ac11629b96d57808ad2808915d89539409c97414aded90b4ff23286a692608230a52bff696055ba5d6", + "0xae6bda03aa10da3d2abbc66d764ca6c8d0993e7304a1bdd413eb9622f3ca1913baa6da1e9f4f9e6cf847f14f44d6924d", + "0xa18e7796054a340ef826c4d6b5a117b80927afaf2ebd547794c400204ae2caf277692e2eabb55bc2f620763c9e9da66d", + "0x8d2d25180dc2c65a4844d3e66819ccfcf48858f0cc89e1c77553b463ec0f7feb9a4002ce26bc618d1142549b9850f232", + "0x863f413a394de42cc8166c1c75d513b91d545fff1de6b359037a742c70b008d34bf8e587afa2d62c844d0c6f0ea753e7", + "0x83cd0cf62d63475e7fcad18a2e74108499cdbf28af2113cfe005e3b5887794422da450b1944d0a986eb7e1f4c3b18f25", + "0xb4f8b350a6d88fea5ab2e44715a292efb12eb52df738c9b2393da3f1ddee68d0a75b476733ccf93642154bceb208f2b8", + "0xb3f52aaa4cd4221cb9fc45936cc67fd3864bf6d26bf3dd86aa85aa55ecfc05f5e392ecce5e7cf9406b4b1c4fce0398c8", + "0xb33137084422fb643123f40a6df2b498065e65230fc65dc31791c330e898c51c3a65ff738930f32c63d78f3c9315f85b", + "0x91452bfa75019363976bb7337fe3a73f1c10f01637428c135536b0cdc7da5ce558dae3dfc792aa55022292600814a8ef", + "0xad6ba94c787cd4361ca642c20793ea44f1f127d4de0bb4a77c7fbfebae0fcadbf28e2cb6f0c12c12a07324ec8c19761d", + "0x890aa6248b17f1501b0f869c556be7bf2b1d31a176f9978bb97ab7a6bd4138eed32467951c5ef1871944b7f620542f43", + "0x82111db2052194ee7dd22ff1eafffac0443cf969d3762cceae046c9a11561c0fdce9c0711f88ac01d1bed165f8a7cee3", + "0xb1527b71df2b42b55832f72e772a466e0fa05743aacc7814f4414e4bcc8d42a4010c9e0fd940e6f254cafedff3cd6543", + "0x922370fa49903679fc565f09c16a5917f8125e72acfeb060fcdbadbd1644eb9f4016229756019c93c6d609cda5d5d174", + "0xaa4c7d98a96cab138d2a53d4aee8ebff6ef903e3b629a92519608d88b3bbd94de5522291a1097e6acf830270e64c8ee1", + "0xb3dc21608a389a72d3a752883a382baaafc61ecc44083b832610a237f6a2363f24195acce529eb4aed4ef0e27a12b66e", + "0x94619f5de05e07b32291e1d7ab1d8b7337a2235e49d4fb5f3055f090a65e932e829efa95db886b32b153bdd05a53ec8c", + "0xade1e92722c2ffa85865d2426fb3d1654a16477d3abf580cfc45ea4b92d5668afc9d09275d3b79283e13e6b39e47424d", + "0xb7201589de7bed094911dd62fcd25c459a8e327ac447b69f541cdba30233063e5ddffad0b67e9c3e34adcffedfd0e13d", + "0x809d325310f862d6549e7cb40f7e5fc9b7544bd751dd28c4f363c724a0378c0e2adcb5e42ec8f912f5f49f18f3365c07", + "0xa79c20aa533de7a5d671c99eb9eb454803ba54dd4f2efa3c8fec1a38f8308e9905c71e9282955225f686146388506ff6", + "0xa85eeacb5e8fc9f3ed06a3fe2dc3108ab9f8c5877b148c73cf26e4e979bf5795edbe2e63a8d452565fd1176ed40402b2", + "0x97ef55662f8a1ec0842b22ee21391227540adf7708f491436044f3a2eb18c471525e78e1e14fa292507c99d74d7437c6", + "0x93110d64ed5886f3d16ce83b11425576a3a7a9bb831cd0de3f9a0b0f2270a730d68136b4ef7ff035ede004358f419b5c", + "0xac9ed0a071517f0ae4f61ce95916a90ba9a77a3f84b0ec50ef7298acdcd44d1b94525d191c39d6bd1bb68f4471428760", + "0x98abd6a02c7690f5a339adf292b8c9368dfc12e0f8069cf26a5e0ce54b4441638f5c66ea735142f3c28e00a0024267e6", + "0xb51efb73ba6d44146f047d69b19c0722227a7748b0e8f644d0fc9551324cf034c041a2378c56ce8b58d06038fb8a78de", + "0x8f115af274ef75c1662b588b0896b97d71f8d67986ae846792702c4742ab855952865ce236b27e2321967ce36ff93357", + "0xb3c4548f14d58b3ab03c222da09e4381a0afe47a72d18d50a94e0008797f78e39e99990e5b4757be62310d400746e35a", + "0xa9b1883bd5f31f909b8b1b6dcb48c1c60ed20aa7374b3ffa7f5b2ed036599b5bef33289d23c80a5e6420d191723b92f7", + "0x85d38dffd99487ae5bb41ab4a44d80a46157bbbe8ef9497e68f061721f74e4da513ccc3422936b059575975f6787c936", + "0xadf870fcb96e972c033ab7a35d28ae79ee795f82bc49c3bd69138f0e338103118d5529c53f2d72a9c0d947bf7d312af2", + "0xab4c7a44e2d9446c6ff303eb49aef0e367a58b22cc3bb27b4e69b55d1d9ee639c9234148d2ee95f9ca8079b1457d5a75", + "0xa386420b738aba2d7145eb4cba6d643d96bda3f2ca55bb11980b318d43b289d55a108f4bc23a9606fb0bccdeb3b3bb30", + "0x847020e0a440d9c4109773ecca5d8268b44d523389993b1f5e60e541187f7c597d79ebd6e318871815e26c96b4a4dbb1", + "0xa530aa7e5ca86fcd1bec4b072b55cc793781f38a666c2033b510a69e110eeabb54c7d8cbcb9c61fee531a6f635ffa972", + "0x87364a5ea1d270632a44269d686b2402da737948dac27f51b7a97af80b66728b0256547a5103d2227005541ca4b7ed04", + "0x8816fc6e16ea277de93a6d793d0eb5c15e9e93eb958c5ef30adaf8241805adeb4da8ce19c3c2167f971f61e0b361077d", + "0x8836a72d301c42510367181bb091e4be377777aed57b73c29ef2ce1d475feedd7e0f31676284d9a94f6db01cc4de81a2", + "0xb0d9d8b7116156d9dde138d28aa05a33e61f8a85839c1e9071ccd517b46a5b4b53acb32c2edd7150c15bc1b4bd8db9e3", + "0xae931b6eaeda790ba7f1cd674e53dc87f6306ff44951fa0df88d506316a5da240df9794ccbd7215a6470e6b31c5ea193", + "0x8c6d5bdf87bd7f645419d7c6444e244fe054d437ed1ba0c122fde7800603a5fadc061e5b836cb22a6cfb2b466f20f013", + "0x90d530c6d0cb654999fa771b8d11d723f54b8a8233d1052dc1e839ea6e314fbed3697084601f3e9bbb71d2b4eaa596df", + "0xb0d341a1422588c983f767b1ed36c18b141774f67ef6a43cff8e18b73a009da10fc12120938b8bba27f225bdfd3138f9", + "0xa131b56f9537f460d304e9a1dd75702ace8abd68cb45419695cb8dee76998139058336c87b7afd6239dc20d7f8f940cc", + "0xaa6c51fa28975f709329adee1bbd35d49c6b878041841a94465e8218338e4371f5cb6c17f44a63ac93644bf28f15d20f", + "0x88440fb584a99ebd7f9ea04aaf622f6e44e2b43bbb49fb5de548d24a238dc8f26c8da2ccf03dd43102bda9f16623f609", + "0x9777b8695b790e702159a4a750d5e7ff865425b95fa0a3c15495af385b91c90c00a6bd01d1b77bffe8c47d01baae846f", + "0x8b9d764ece7799079e63c7f01690c8eff00896a26a0d095773dea7a35967a8c40db7a6a74692f0118bf0460c26739af4", + "0x85808c65c485520609c9e61fa1bb67b28f4611d3608a9f7a5030ee61c3aa3c7e7dc17fff48af76b4aecee2cb0dbd22ac", + "0xad2783a76f5b3db008ef5f7e67391fda4e7e36abde6b3b089fc4835b5c339370287935af6bd53998bed4e399eda1136d", + "0x96f18ec03ae47c205cc4242ca58e2eff185c9dca86d5158817e2e5dc2207ab84aadda78725f8dc080a231efdc093b940", + "0x97de1ab6c6cc646ae60cf7b86df73b9cf56cc0cd1f31b966951ebf79fc153531af55ca643b20b773daa7cab784b832f7", + "0x870ba266a9bfa86ef644b1ef025a0f1b7609a60de170fe9508de8fd53170c0b48adb37f19397ee8019b041ce29a16576", + "0xad990e888d279ac4e8db90619d663d5ae027f994a3992c2fbc7d262b5990ae8a243e19157f3565671d1cb0de17fe6e55", + "0x8d9d5adcdd94c5ba3be4d9a7428133b42e485f040a28d16ee2384758e87d35528f7f9868de9bd23d1a42a594ce50a567", + "0x85a33ed75d514ece6ad78440e42f7fcdb59b6f4cff821188236d20edae9050b3a042ce9bc7d2054296e133d033e45022", + "0x92afd2f49a124aaba90de59be85ff269457f982b54c91b06650c1b8055f9b4b0640fd378df02a00e4fc91f7d226ab980", + "0x8c0ee09ec64bd831e544785e3d65418fe83ed9c920d9bb4d0bf6dd162c1264eb9d6652d2def0722e223915615931581c", + "0x8369bedfa17b24e9ad48ebd9c5afea4b66b3296d5770e09b00446c5b0a8a373d39d300780c01dcc1c6752792bccf5fd0", + "0x8b9e960782576a59b2eb2250d346030daa50bbbec114e95cdb9e4b1ba18c3d34525ae388f859708131984976ca439d94", + "0xb682bface862008fea2b5a07812ca6a28a58fd151a1d54c708fc2f8572916e0d678a9cb8dc1c10c0470025c8a605249e", + "0xa38d5e189bea540a824b36815fc41e3750760a52be0862c4cac68214febdc1a754fb194a7415a8fb7f96f6836196d82a", + "0xb9e7fbda650f18c7eb8b40e42cc42273a7298e65e8be524292369581861075c55299ce69309710e5b843cb884de171bd", + "0xb6657e5e31b3193874a1bace08f42faccbd3c502fb73ad87d15d18a1b6c2a146f1baa929e6f517db390a5a47b66c0acf", + "0xae15487312f84ed6265e4c28327d24a8a0f4d2d17d4a5b7c29b974139cf93223435aaebe3af918f5b4bb20911799715f", + "0x8bb4608beb06bc394e1a70739b872ce5a2a3ffc98c7547bf2698c893ca399d6c13686f6663f483894bccaabc3b9c56ad", + "0xb58ac36bc6847077584308d952c5f3663e3001af5ecf2e19cb162e1c58bd6c49510205d453cffc876ca1dc6b8e04a578", + "0x924f65ced61266a79a671ffb49b300f0ea44c50a0b4e3b02064faa99fcc3e4f6061ea8f38168ab118c5d47bd7804590e", + "0x8d67d43b8a06b0ff4fafd7f0483fa9ed1a9e3e658a03fb49d9d9b74e2e24858dc1bed065c12392037b467f255d4e5643", + "0xb4d4f87813125a6b355e4519a81657fa97c43a6115817b819a6caf4823f1d6a1169683fd68f8d025cdfa40ebf3069acb", + "0xa7fd4d2c8e7b59b8eed3d4332ae94b77a89a2616347402f880bc81bde072220131e6dbec8a605be3a1c760b775375879", + "0x8d4a7d8fa6f55a30df37bcf74952e2fa4fd6676a2e4606185cf154bdd84643fd01619f8fb8813a564f72e3f574f8ce30", + "0x8086fb88e6260e9a9c42e9560fde76315ff5e5680ec7140f2a18438f15bc2cc7d7d43bfb5880b180b738c20a834e6134", + "0x916c4c54721de03934fee6f43de50bb04c81f6f8dd4f6781e159e71c40c60408aa54251d457369d133d4ba3ed7c12cb4", + "0x902e5bf468f11ed9954e2a4a595c27e34abe512f1d6dc08bbca1c2441063f9af3dc5a8075ab910a10ff6c05c1c644a35", + "0xa1302953015e164bf4c15f7d4d35e3633425a78294406b861675667eec77765ff88472306531e5d3a4ec0a2ff0dd6a9e", + "0x87874461df3c9aa6c0fa91325576c0590f367075f2f0ecfeb34afe162c04c14f8ce9d608c37ac1adc8b9985bc036e366", + "0x84b50a8a61d3cc609bfb0417348133e698fe09a6d37357ce3358de189efcf35773d78c57635c2d26c3542b13cc371752", + "0xacaed2cff8633d12c1d12bb7270c54d65b0b0733ab084fd47f81d0a6e1e9b6f300e615e79538239e6160c566d8bb8d29", + "0x889e6a0e136372ca4bac90d1ab220d4e1cad425a710e8cdd48b400b73bb8137291ceb36a39440fa84305783b1d42c72f", + "0x90952e5becec45b2b73719c228429a2c364991cf1d5a9d6845ae5b38018c2626f4308daa322cab1c72e0f6c621bb2b35", + "0x8f5a97a801b6e9dcd66ccb80d337562c96f7914e7169e8ff0fda71534054c64bf2a9493bb830623d612cfe998789be65", + "0x84f3df8b9847dcf1d63ca470dc623154898f83c25a6983e9b78c6d2d90a97bf5e622445be835f32c1e55e6a0a562ea78", + "0x91d12095cd7a88e7f57f254f02fdb1a1ab18984871dead2f107404bcf8069fe68258c4e6f6ebd2477bddf738135400bb", + "0xb771a28bc04baef68604d4723791d3712f82b5e4fe316d7adc2fc01b935d8e644c06d59b83bcb542afc40ebafbee0683", + "0x872f6341476e387604a7e93ae6d6117e72d164e38ebc2b825bc6df4fcce815004d7516423c190c1575946b5de438c08d", + "0x90d6b4aa7d40a020cdcd04e8b016d041795961a8e532a0e1f4041252131089114a251791bf57794cadb7d636342f5d1c", + "0x899023ba6096a181448d927fed7a0fe858be4eac4082a42e30b3050ee065278d72fa9b9d5ce3bc1372d4cbd30a2f2976", + "0xa28f176571e1a9124f95973f414d5bdbf5794d41c3839d8b917100902ac4e2171eb940431236cec93928a60a77ede793", + "0x838dbe5bcd29c4e465d02350270fa0036cd46f8730b13d91e77afb7f5ed16525d0021d3b2ae173a76c378516a903e0cb", + "0x8e105d012dd3f5d20f0f1c4a7e7f09f0fdd74ce554c3032e48da8cce0a77260d7d47a454851387770f5c256fa29bcb88", + "0x8f4df0f9feeb7a487e1d138d13ea961459a6402fd8f8cabb226a92249a0d04ded5971f3242b9f90d08da5ff66da28af6", + "0xad1cfda4f2122a20935aa32fb17c536a3653a18617a65c6836700b5537122af5a8206befe9eaea781c1244c43778e7f1", + "0x832c6f01d6571964ea383292efc8c8fa11e61c0634a25fa180737cc7ab57bc77f25e614aac9a2a03d98f27b3c1c29de2", + "0x903f89cc13ec6685ac7728521898781fecb300e9094ef913d530bf875c18bcc3ceed7ed51e7b482d45619ab4b025c2e9", + "0xa03c474bb915aad94f171e8d96f46abb2a19c9470601f4c915512ec8b9e743c3938450a2a5b077b4618b9df8809e1dc1", + "0x83536c8456f306045a5f38ae4be2e350878fa7e164ea408d467f8c3bc4c2ee396bd5868008c089183868e4dfad7aa50b", + "0x88f26b4ea1b236cb326cd7ad7e2517ec8c4919598691474fe15d09cabcfc37a8d8b1b818f4d112432ee3a716b0f37871", + "0xa44324e3fe96e9c12b40ded4f0f3397c8c7ee8ff5e96441118d8a6bfad712d3ac990b2a6a23231a8f691491ac1fd480f", + "0xb0de4693b4b9f932191a21ee88629964878680152a82996c0019ffc39f8d9369bbe2fe5844b68d6d9589ace54af947e4", + "0x8e5d8ba948aea5fd26035351a960e87f0d23efddd8e13236cc8e4545a3dda2e9a85e6521efb8577e03772d3637d213d9", + "0x93efc82d2017e9c57834a1246463e64774e56183bb247c8fc9dd98c56817e878d97b05f5c8d900acf1fbbbca6f146556", + "0x8731176363ad7658a2862426ee47a5dce9434216cef60e6045fa57c40bb3ce1e78dac4510ae40f1f31db5967022ced32", + "0xb10c9a96745722c85bdb1a693100104d560433d45b9ac4add54c7646a7310d8e9b3ca9abd1039d473ae768a18e489845", + "0xa2ac374dfbb464bf850b4a2caf15b112634a6428e8395f9c9243baefd2452b4b4c61b0cb2836d8eae2d57d4900bf407e", + "0xb69fe3ded0c4f5d44a09a0e0f398221b6d1bf5dbb8bc4e338b93c64f1a3cac1e4b5f73c2b8117158030ec03787f4b452", + "0x8852cdbaf7d0447a8c6f211b4830711b3b5c105c0f316e3a6a18dcfbb9be08bd6f4e5c8ae0c3692da08a2dfa532f9d5c", + "0x93bbf6d7432a7d98ade3f94b57bf9f4da9bc221a180a370b113066dd42601bb9e09edd79e2e6e04e00423399339eebda", + "0xa80941c391f1eeafc1451c59e4775d6a383946ff22997aeaadf806542ba451d3b0f0c6864eeba954174a296efe2c1550", + "0xa045fe2bb011c2a2f71a0181a8f457a3078470fb74c628eab8b59aef69ffd0d649723bf74d6885af3f028bc5a104fb39", + "0xb9d8c35911009c4c8cad64692139bf3fc16b78f5a19980790cb6a7aea650a25df4231a4437ae0c351676a7e42c16134f", + "0x94c79501ded0cfcbab99e1841abe4a00a0252b3870e20774c3da16c982d74c501916ec28304e71194845be6e3113c7ab", + "0x900a66418b082a24c6348d8644ddb1817df5b25cb33044a519ef47cc8e1f7f1e38d2465b7b96d32ed472d2d17f8414c6", + "0xb26f45d393b8b2fcb29bdbb16323dc7f4b81c09618519ab3a39f8ee5bd148d0d9f3c0b5dfab55b5ce14a1cb9206d777b", + "0xaa1a87735fc493a80a96a9a57ca40a6d9c32702bfcaa9869ce1a116ae65d69cefe2f3e79a12454b4590353e96f8912b4", + "0xa922b188d3d0b69b4e4ea2a2aa076566962844637da12c0832105d7b31dea4a309eee15d12b7a336be3ea36fcbd3e3b7", + "0x8f3841fcf4105131d8c4d9885e6e11a46c448226401cf99356c291fadb864da9fa9d30f3a73c327f23f9fd99a11d633e", + "0x9791d1183fae270e226379af6c497e7da803ea854bb20afa74b253239b744c15f670ee808f708ede873e78d79a626c9a", + "0xa4cad52e3369491ada61bf28ada9e85de4516d21c882e5f1cd845bea9c06e0b2887b0c5527fcff6fc28acd3c04f0a796", + "0xb9ac86a900899603452bd11a7892a9bfed8054970bfcbeaa8c9d1930db891169e38d6977f5258c25734f96c8462eee3b", + "0xa3a154c28e5580656a859f4efc2f5ebfa7eaa84ca40e3f134fa7865e8581586db74992dbfa4036aa252fba103773ddde", + "0x95cc2a0c1885a029e094f5d737e3ecf4d26b99036453a8773c77e360101f9f98676ee246f6f732a377a996702d55691f", + "0x842651bbe99720438d8d4b0218feb60481280c05beb17750e9ca0d8c0599a60f873b7fbdcc7d8835ba9a6d57b16eec03", + "0x81ee54699da98f5620307893dcea8f64670609fa20e5622265d66283adeac122d458b3308c5898e6c57c298db2c8b24f", + "0xb97868b0b2bc98032d68352a535a1b341b9ff3c7af4e3a7f3ebc82d3419daa1b5859d6aedc39994939623c7cd878bd9b", + "0xb60325cd5d36461d07ef253d826f37f9ee6474a760f2fff80f9873d01fd2b57711543cdc8d7afa1c350aa753c2e33dea", + "0x8c205326c11d25a46717b780c639d89714c7736c974ae71287e3f4b02e6605ac2d9b4928967b1684f12be040b7bf2dd3", + "0x95a392d82db51e26ade6c2ccd3396d7e40aff68fa570b5951466580d6e56dda51775dce5cf3a74a7f28c3cb2eb551c4d", + "0x8f2cc8071eb56dffb70bda6dd433b556221dc8bba21c53353c865f00e7d4d86c9e39f119ea9a8a12ef583e9a55d9a6b6", + "0x9449a71af9672aaf8856896d7e3d788b22991a7103f75b08c0abbcc2bfe60fda4ed8ce502cea4511ff0ea52a93e81222", + "0x857090ab9fdb7d59632d068f3cc8cf27e61f0d8322d30e6b38e780a1f05227199b4cd746aac1311c36c659ef20931f28", + "0x98a891f4973e7d9aaf9ac70854608d4f7493dffc7e0987d7be9dd6029f6ea5636d24ef3a83205615ca1ff403750058e1", + "0xa486e1365bbc278dd66a2a25d258dc82f46b911103cb16aab3945b9c95ae87b386313a12b566df5b22322ede0afe25ad", + "0xa9a1eb399ed95d396dccd8d1ac718043446f8b979ec62bdce51c617c97a312f01376ab7fb87d27034e5f5570797b3c33", + "0xb7abc3858d7a74bb446218d2f5a037e0fae11871ed9caf44b29b69c500c1fa1dcfad64c9cdccc9d80d5e584f06213deb", + "0x8cfb09fe2e202faa4cebad932b1d35f5ca204e1c2a0c740a57812ac9a6792130d1312aabd9e9d4c58ca168bfebd4c177", + "0xa90a305c2cd0f184787c6be596fa67f436afd1f9b93f30e875f817ac2aae8bdd2e6e656f6be809467e6b3ad84adb86b1", + "0x80a9ef993c2b009ae172cc8f7ec036f5734cf4f4dfa06a7db4d54725e7fbfae5e3bc6f22687bdbb6961939d6f0c87537", + "0x848ade1901931e72b955d7db1893f07003e1708ff5d93174bac5930b9a732640f0578839203e9b77eb27965c700032d3", + "0x93fdf4697609c5ae9c33b9ca2f5f1af44abeb2b98dc4fdf732cf7388de086f410730dc384d9b7a7f447bb009653c8381", + "0x89ce3fb805aea618b5715c0d22a9f46da696b6fa86794f56fdf1d44155a33d42daf1920bcbe36cbacf3cf4c92df9cbc7", + "0x829ce2c342cf82aa469c65f724f308f7a750bd1494adc264609cd790c8718b8b25b5cab5858cf4ee2f8f651d569eea67", + "0xaf2f0cee7bf413204be8b9df59b9e4991bc9009e0d6dbe6815181df0ec2ca93ab8f4f3135b1c14d8f53d74bff0bd6f27", + "0xb87998cecf7b88cde93d1779f10a521edd5574a2fbd240102978639ec57433ba08cdb53849038a329cebbe74657268d2", + "0xa64542a1261a6ed3d720c2c3a802303aad8c4c110c95d0f12e05c1065e66f42da494792b6bfc5b9272363f3b1d457f58", + "0x86a6fd042e4f282fadf07a4bfee03fc96a3aea49f7a00f52bf249a20f1ec892326855410e61f37fbb27d9305eb2fc713", + "0x967ea5bc403b6db269682f7fd0df90659350d7e1aa66bc4fab4c9dfcd75ed0bba4b52f1cebc5f34dc8ba810793727629", + "0xa52990f9f3b8616ce3cdc2c74cd195029e6a969753dcf2d1630438700e7d6ebde36538532b3525ac516f5f2ce9dd27a3", + "0xa64f7ff870bab4a8bf0d4ef6f5c744e9bf1021ed08b4c80903c7ad318e80ba1817c3180cc45cb5a1cae1170f0241655f", + "0xb00f706fa4de1f663f021e8ad3d155e84ce6084a409374b6e6cd0f924a0a0b51bebaaaf1d228c77233a73b0a5a0df0e9", + "0x8b882cc3bff3e42babdb96df95fb780faded84887a0a9bab896bef371cdcf169d909f5658649e93006aa3c6e1146d62e", + "0x9332663ef1d1dcf805c3d0e4ce7a07d9863fb1731172e766b3cde030bf81682cc011e26b773fb9c68e0477b4ae2cfb79", + "0xa8aa8151348dbd4ef40aaeb699b71b4c4bfd3218560c120d85036d14f678f6736f0ec68e80ce1459d3d35feccc575164", + "0xa16cd8b729768f51881c213434aa28301fa78fcb554ddd5f9012ee1e4eae7b5cb3dd88d269d53146dea92d10790faf0b", + "0x86844f0ef9d37142faf3b1e196e44fbe280a3ba4189aa05c356778cb9e3b388a2bff95eed305ada8769935c9974e4c57", + "0xae2eec6b328fccf3b47bcdac32901ac2744a51beb410b04c81dea34dee4912b619466a4f5e2780d87ecefaebbe77b46d", + "0x915df4c38d301c8a4eb2dc5b1ba0ffaad67cbb177e0a80095614e9c711f4ef24a4cef133f9d982a63d2a943ba6c8669d", + "0xae6a2a4dedfc2d1811711a8946991fede972fdf2a389b282471280737536ffc0ac3a6d885b1f8bda0366eb0b229b9979", + "0xa9b628c63d08b8aba6b1317f6e91c34b2382a6c85376e8ef2410a463c6796740ae936fc4e9e0737cb9455d1daa287bd8", + "0x848e30bf7edf2546670b390d5cf9ab71f98fcb6add3c0b582cb34996c26a446dee5d1bde4fdcde4fc80c10936e117b29", + "0x907d6096c7c8c087d1808dd995d5d2b9169b3768c3f433475b50c2e2bd4b082f4d543afd8b0b0ddffa9c66222a72d51d", + "0xa59970a2493b07339124d763ac9d793c60a03354539ecbcf6035bc43d1ea6e35718202ae6d7060b7d388f483d971573c", + "0xb9cfef2af9681b2318f119d8611ff6d9485a68d8044581b1959ab1840cbca576dbb53eec17863d2149966e9feb21122f", + "0xad47271806161f61d3afa45cdfe2babceef5e90031a21779f83dc8562e6076680525b4970b2f11fe9b2b23c382768323", + "0x8e425a99b71677b04fe044625d338811fbb8ee32368a424f6ab2381c52e86ee7a6cecedf777dc97181519d41c351bc22", + "0x86b55b54d7adefc12954a9252ee23ae83efe8b5b4b9a7dc307904413e5d69868c7087a818b2833f9b004213d629be8ad", + "0xa14fda6b93923dd11e564ae4457a66f397741527166e0b16a8eb91c6701c244fd1c4b63f9dd3515193ec88fa6c266b35", + "0xa9b17c36ae6cd85a0ed7f6cabc5b47dc8f80ced605db327c47826476dc1fb8f8669aa7a7dc679fbd4ee3d8e8b4bd6a6f", + "0x82a0829469c1458d959c821148f15dacae9ea94bf56c59a6ab2d4dd8b3d16d73e313b5a3912a6c1f131d73a8f06730c4", + "0xb22d56d549a53eaef549595924bdb621ff807aa4513feedf3fdcbf7ba8b6b9cfa4481c2f67fc642db397a6b794a8b63a", + "0x974c59c24392e2cb9294006cbe3c52163e255f3bd0c2b457bdc68a6338e6d5b6f87f716854492f8d880a6b896ccf757c", + "0xb70d247ba7cad97c50b57f526c2ba915786e926a94e8f8c3eebc2e1be6f4255411b9670e382060049c8f4184302c40b2", + "0xad80201fe75ef21c3ddbd98cf23591e0d7a3ba1036dfe77785c32f44755a212c31f0ceb0a0b6f5ee9b6dc81f358d30c3", + "0x8c656e841f9bb90b9a42d425251f3fdbc022a604d75f5845f479ed4be23e02aaf9e6e56cde351dd7449c50574818a199", + "0x8b88dd3fa209d3063b7c5b058f7249ee9900fbc2287d16da61a0704a0a1d71e45d9c96e1cda7fdf9654534ec44558b22", + "0x961da00cc8750bd84d253c08f011970ae1b1158ad6778e8ed943d547bceaf52d6d5a212a7de3bf2706688c4389b827d2", + "0xa5dd379922549a956033e3d51a986a4b1508e575042b8eaa1df007aa77cf0b8c2ab23212f9c075702788fa9c53696133", + "0xac8fcfde3a349d1e93fc8cf450814e842005c545c4844c0401bc80e6b96cdb77f29285a14455e167c191d4f312e866cd", + "0xac63d79c799783a8466617030c59dd5a8f92ee6c5204676fd8d881ce5f7f8663bdbeb0379e480ea9b6340ab0dc88e574", + "0x805874fde19ce359041ae2bd52a39e2841acabfd31f965792f2737d7137f36d4e4722ede8340d8c95afa6af278af8acb", + "0x8d2f323a228aa8ba7b7dc1399138f9e6b41df1a16a7069003ab8104b8b68506a45141bc5fe66acf430e23e13a545190b", + "0xa1610c721a2d9af882bb6b39bea97cff1527a3aea041d25934de080214ae77c959e79957164440686d15ab301e897d4d", + "0xaba16d29a47fc36f12b654fde513896723e2c700c4190f11b26aa4011da57737ad717daa02794aa3246e4ae5f0b0cc3a", + "0xa406db2f15fdd135f346cc4846623c47edd195e80ba8c7cb447332095314d565e4040694ca924696bb5ee7f8996ea0ba", + "0x8b30e2cd9b47d75ba57b83630e40f832249af6c058d4f490416562af451993eec46f3e1f90bc4d389e4c06abd1b32a46", + "0xaacf9eb7036e248e209adbfc3dd7ce386569ea9b312caa4b240726549db3c68c4f1c8cbf8ed5ea9ea60c7e57c9df3b8e", + "0xb20fcac63bf6f5ee638a42d7f89be847f348c085ddcbec3fa318f4323592d136c230495f188ef2022aa355cc2b0da6f9", + "0x811eff750456a79ec1b1249d76d7c1547065b839d8d4aaad860f6d4528eb5b669473dcceeeea676cddbc3980b68461b7", + "0xb52d14ae33f4ab422f953392ae76a19c618cc31afc96290bd3fe2fb44c954b5c92c4789f3f16e8793f2c0c1691ade444", + "0xa7826dafeeba0db5b66c4dfcf2b17fd7b40507a5a53ac2e42942633a2cb30b95ba1739a6e9f3b7a0e0f1ec729bf274e2", + "0x8acfd83ddf7c60dd7c8b20c706a3b972c65d336b8f9b3d907bdd8926ced271430479448100050b1ef17578a49c8fa616", + "0xaf0c69f65184bb06868029ad46f8465d75c36814c621ac20a5c0b06a900d59305584f5a6709683d9c0e4b6cd08d650a6", + "0xb6cc8588191e00680ee6c3339bd0f0a17ad8fd7f4be57d5d7075bede0ea593a19e67f3d7c1a20114894ee5bfcab71063", + "0xa82fd4f58635129dbb6cc3eb9391cf2d28400018b105fc41500fbbd12bd890b918f97d3d359c29dd3b4c4e34391dfab0", + "0x92fc544ed65b4a3625cf03c41ddff7c039bc22d22c0d59dcc00efd5438401f2606adb125a1d5de294cca216ec8ac35a3", + "0x906f67e4a32582b71f15940523c0c7ce370336935e2646bdaea16a06995256d25e99df57297e39d6c39535e180456407", + "0x97510337ea5bbd5977287339197db55c60533b2ec35c94d0a460a416ae9f60e85cee39be82abeeacd5813cf54df05862", + "0x87e6894643815c0ea48cb96c607266c5ee4f1f82ba5fe352fb77f9b6ed14bfc2b8e09e80a99ac9047dfcf62b2ae26795", + "0xb6fd55dd156622ad7d5d51b7dde75e47bd052d4e542dd6449e72411f68275775c846dde301e84613312be8c7bce58b07", + "0xb98461ac71f554b2f03a94e429b255af89eec917e208a8e60edf5fc43b65f1d17a20de3f31d2ce9f0cb573c25f2f4d98", + "0x96f0dea40ca61cefbee41c4e1fe9a7d81fbe1f49bb153d083ab70f5d0488a1f717fd28cedcf6aa18d07cce2c62801898", + "0x8d7c3ab310184f7dc34b6ce4684e4d29a31e77b09940448ea4daac730b7eb308063125d4dd229046cf11bfd521b771e0", + "0x96f0564898fe96687918bbf0a6adead99cf72e3a35ea3347e124af9d006221f8e82e5a9d2fe80094d5e8d48e610f415e", + "0xad50fcb92c2675a398cf07d4c40a579e44bf8d35f27cc330b57e54d5ea59f7d898af0f75dccfe3726e5471133d70f92b", + "0x828beed62020361689ae7481dd8f116902b522fb0c6c122678e7f949fdef70ead011e0e6bffd25678e388744e17cdb69", + "0x8349decac1ca16599eee2efc95bcaabf67631107da1d34a2f917884bd70dfec9b4b08ab7bc4379d6c73b19c0b6e54fb8", + "0xb2a6a2e50230c05613ace9e58bb2e98d94127f196f02d9dddc53c43fc68c184549ca12d713cb1b025d8260a41e947155", + "0x94ff52181aadae832aed52fc3b7794536e2a31a21fc8be3ea312ca5c695750d37f08002f286b33f4023dba1e3253ecfa", + "0xa21d56153c7e5972ee9a319501be4faff199fdf09bb821ea9ce64aa815289676c00f105e6f00311b3a5b627091b0d0fc", + "0xa27a60d219f1f0c971db73a7f563b371b5c9fc3ed1f72883b2eac8a0df6698400c9954f4ca17d7e94e44bd4f95532afb", + "0xa2fc56fae99b1f18ba5e4fe838402164ce82f8a7f3193d0bbd360c2bac07c46f9330c4c7681ffb47074c6f81ee6e7ac6", + "0xb748e530cd3afb96d879b83e89c9f1a444f54e55372ab1dcd46a0872f95ce8f49cf2363fc61be82259e04f555937ed16", + "0x8bf8993e81080c7cbba1e14a798504af1e4950b2f186ab3335b771d6acaee4ffe92131ae9c53d74379d957cb6344d9cd", + "0x96774d0ef730d22d7ab6d9fb7f90b9ead44285219d076584a901960542756700a2a1603cdf72be4708b267200f6c36a9", + "0xb47703c2ab17be1e823cc7bf3460db1d6760c0e33862c90ca058845b2ff234b0f9834ddba2efb2ee1770eb261e7d8ffd", + "0x84319e67c37a9581f8b09b5e4d4ae88d0a7fb4cbb6908971ab5be28070c3830f040b1de83ee663c573e0f2f6198640e4", + "0x96811875fa83133e0b3c0e0290f9e0e28bca6178b77fdf5350eb19344d453dbd0d71e55a0ef749025a5a2ca0ad251e81", + "0x81a423423e9438343879f2bfd7ee9f1c74ebebe7ce3cfffc8a11da6f040cc4145c3b527bd3cf63f9137e714dbcb474ef", + "0xb8c3535701ddbeec2db08e17a4fa99ba6752d32ece5331a0b8743676f421fcb14798afc7c783815484f14693d2f70db8", + "0x81aee980c876949bf40782835eec8817d535f6f3f7e00bf402ddd61101fdcd60173961ae90a1cf7c5d060339a18c959d", + "0x87e67b928d97b62c49dac321ce6cb680233f3a394d4c9a899ac2e8db8ccd8e00418e66cdfd68691aa3cb8559723b580c", + "0x8eac204208d99a2b738648df96353bbb1b1065e33ee4f6bba174b540bbbd37d205855e1f1e69a6b7ff043ca377651126", + "0x848e6e7a54ad64d18009300b93ea6f459ce855971dddb419b101f5ac4c159215626fadc20cc3b9ab1701d8f6dfaddd8b", + "0x88aa123d9e0cf309d46dddb6acf634b1ade3b090a2826d6e5e78669fa1220d6df9a6697d7778cd9b627db17eea846126", + "0x9200c2a629b9144d88a61151b661b6c4256cc5dadfd1e59a8ce17a013c2d8f7e754aabe61663c3b30f1bc47784c1f8cf", + "0xb6e1a2827c3bdda91715b0e1b1f10dd363cef337e7c80cac1f34165fc0dea7c8b69747e310563db5818390146ce3e231", + "0x92c333e694f89f0d306d54105b2a5dcc912dbe7654d9e733edab12e8537350815be472b063e56cfde5286df8922fdecb", + "0xa6fac04b6d86091158ebb286586ccfec2a95c9786e14d91a9c743f5f05546073e5e3cc717635a0c602cad8334e922346", + "0xa581b4af77feebc1fb897d49b5b507c6ad513d8f09b273328efbb24ef0d91eb740d01b4d398f2738125dacfe550330cd", + "0x81c4860cccf76a34f8a2bc3f464b7bfd3e909e975cce0d28979f457738a56e60a4af8e68a3992cf273b5946e8d7f76e2", + "0x8d1eaa09a3180d8af1cbaee673db5223363cc7229a69565f592fa38ba0f9d582cedf91e15dabd06ebbf2862fc0feba54", + "0x9832f49b0147f4552402e54593cfa51f99540bffada12759b71fcb86734be8e500eea2d8b3d036710bdf04c901432de9", + "0x8bdb0e8ec93b11e5718e8c13cb4f5de545d24829fd76161216340108098dfe5148ed25e3b57a89a516f09fa79043734d", + "0xab96f06c4b9b0b2c0571740b24fca758e6976315053a7ecb20119150a9fa416db2d3a2e0f8168b390bb063f0c1caf785", + "0xab777f5c52acd62ecf4d1f168b9cc8e1a9b45d4ec6a8ff52c583e867c2239aba98d7d3af977289b367edce03d9c2dfb1", + "0xa09d3ce5e748da84802436951acc3d3ea5d8ec1d6933505ed724d6b4b0d69973ab0930daec9c6606960f6e541e4a3ce2", + "0x8ef94f7be4d85d5ad3d779a5cf4d7b2fc3e65c52fb8e1c3c112509a4af77a0b5be994f251e5e40fabeeb1f7d5615c22b", + "0xa7406a5bf5708d9e10922d3c5c45c03ef891b8d0d74ec9f28328a72be4cdc05b4f2703fa99366426659dfca25d007535", + "0xb7f52709669bf92a2e070bfe740f422f0b7127392c5589c7f0af71bb5a8428697c762d3c0d74532899da24ea7d8695c2", + "0xb9dfb0c8df84104dbf9239ccefa4672ef95ddabb8801b74997935d1b81a78a6a5669a3c553767ec19a1281f6e570f4ff", + "0xae4d5c872156061ce9195ac640190d8d71dd406055ee43ffa6f9893eb24b870075b74c94d65bc1d5a07a6573282b5520", + "0xafe6bd3eb72266d333f1807164900dcfa02a7eb5b1744bb3c86b34b3ee91e3f05e38fa52a50dc64eeb4bdb1dd62874b8", + "0x948043cf1bc2ef3c01105f6a78dc06487f57548a3e6ef30e6ebc51c94b71e4bf3ff6d0058c72b6f3ecc37efd7c7fa8c0", + "0xa22fd17c2f7ffe552bb0f23fa135584e8d2d8d75e3f742d94d04aded2a79e22a00dfe7acbb57d44e1cdb962fb22ae170", + "0x8cd0f4e9e4fb4a37c02c1bde0f69359c43ab012eb662d346487be0c3758293f1ca560122b059b091fddce626383c3a8f", + "0x90499e45f5b9c81426f3d735a52a564cafbed72711d9279fdd88de8038e953bc48c57b58cba85c3b2e4ce56f1ddb0e11", + "0x8c30e4c034c02958384564cac4f85022ef36ab5697a3d2feaf6bf105049675bbf23d01b4b6814711d3d9271abff04cac", + "0x81f7999e7eeea30f3e1075e6780bbf054f2fb6f27628a2afa4d41872a385b4216dd5f549da7ce6cf39049b2251f27fb7", + "0xb36a7191f82fc39c283ffe53fc1f5a9a00b4c64eee7792a8443475da9a4d226cf257f226ea9d66e329af15d8f04984ec", + "0xaad4da528fdbb4db504f3041c747455baff5fcd459a2efd78f15bdf3aea0bdb808343e49df88fe7a7c8620009b7964a3", + "0x99ebd8c6dd5dd299517fb6381cfc2a7f443e6e04a351440260dd7c2aee3f1d8ef06eb6c18820b394366ecdfd2a3ce264", + "0x8873725b81871db72e4ec3643084b1cdce3cbf80b40b834b092767728605825c19b6847ad3dcf328438607e8f88b4410", + "0xb008ee2f895daa6abd35bd39b6f7901ae4611a11a3271194e19da1cdcc7f1e1ea008fe5c5440e50d2c273784541ad9c5", + "0x9036feafb4218d1f576ef89d0e99124e45dacaa6d816988e34d80f454d10e96809791d5b78f7fd65f569e90d4d7238c5", + "0x92073c1d11b168e4fa50988b0288638b4868e48bbc668c5a6dddf5499875d53be23a285acb5e4bad60114f6cf6c556e9", + "0x88c87dfcb8ba6cbfe7e1be081ccfadbd589301db2cb7c99f9ee5d7db90aa297ed1538d5a867678a763f2deede5fd219a", + "0xb42a562805c661a50f5dea63108002c0f27c0da113da6a9864c9feb5552225417c0356c4209e8e012d9bcc9d182c7611", + "0x8e6317d00a504e3b79cd47feb4c60f9df186467fe9ca0f35b55c0364db30528f5ff071109dabb2fc80bb9cd4949f0c24", + "0xb7b1ea6a88694f8d2f539e52a47466695e39e43a5eb9c6f23bca15305fe52939d8755cc3ac9d6725e60f82f994a3772f", + "0xa3cd55161befe795af93a38d33290fb642b8d80da8b786c6e6fb02d393ea308fbe87f486994039cbd7c7b390414594b6", + "0xb416d2d45b44ead3b1424e92c73c2cf510801897b05d1724ff31cbd741920cd858282fb5d6040fe1f0aa97a65bc49424", + "0x950ee01291754feace97c2e933e4681e7ddfbc4fcd079eb6ff830b0e481d929c93d0c7fb479c9939c28ca1945c40da09", + "0x869bd916aee8d86efe362a49010382674825d49195b413b4b4018e88ce43fe091b475d0b863ff0ba2259400f280c2b23", + "0x9782f38cd9c9d3385ec286ebbc7cba5b718d2e65a5890b0a5906b10a89dc8ed80d417d71d7c213bf52f2af1a1f513ea7", + "0x91cd33bc2628d096269b23faf47ee15e14cb7fdc6a8e3a98b55e1031ea0b68d10ba30d97e660f7e967d24436d40fad73", + "0x8becc978129cc96737034c577ae7225372dd855da8811ae4e46328e020c803833b5bdbc4a20a93270e2b8bd1a2feae52", + "0xa36b1d8076783a9522476ce17f799d78008967728ce920531fdaf88303321bcaf97ecaa08e0c01f77bc32e53c5f09525", + "0xb4720e744943f70467983aa34499e76de6d59aa6fadf86f6b787fdce32a2f5b535b55db38fe2da95825c51002cfe142d", + "0x91ad21fc502eda3945f6de874d1b6bf9a9a7711f4d61354f9e5634fc73f9c06ada848de15ab0a75811d3250be862827d", + "0x84f78e2ebf5fc077d78635f981712daf17e2475e14c2a96d187913006ad69e234746184a51a06ef510c9455b38acb0d7", + "0x960aa7906e9a2f11db64a26b5892ac45f20d2ccb5480f4888d89973beb6fa0dfdc06d68d241ff5ffc7f1b82b1aac242d", + "0xa99365dcd1a00c66c9db6924b97c920f5c723380e823b250db85c07631b320ec4e92e586f7319e67a522a0578f7b6d6c", + "0xa25d92d7f70cf6a88ff317cfec071e13774516da664f5fac0d4ecaa65b8bf4eb87a64a4d5ef2bd97dfae98d388dbf5cc", + "0xa7af47cd0041295798f9779020a44653007444e8b4ef0712982b06d0dcdd434ec4e1f7c5f7a049326602cb605c9105b7", + "0xaefe172eac5568369a05980931cc476bebd9dea573ba276d59b9d8c4420784299df5a910033b7e324a6c2dfc62e3ef05", + "0xb69bc9d22ffa645baa55e3e02522e9892bb2daa7fff7c15846f13517d0799766883ee09ae0869df4139150c5b843ca8a", + "0x95a10856140e493354fdd12722c7fdded21b6a2ffbc78aa2697104af8ad0c8e2206f44b0bfee077ef3949d46bbf7c16b", + "0x891f2fcd2c47cbea36b7fa715968540c233313f05333f09d29aba23c193f462ed490dd4d00969656e89c53155fdfe710", + "0xa6c33e18115e64e385c843dde34e8a228222795c7ca90bc2cc085705d609025f3351d9be61822c69035a49fb3e48f2d5", + "0xb87fb12f12c0533b005adad0487f03393ff682e13575e3cb57280c3873b2c38ba96a63c49eef7a442753d26b7005230b", + "0xb905c02ba451bfd411c135036d92c27af3b0b1c9c2f1309d6948544a264b125f39dd41afeff4666b12146c545adc168a", + "0x8b29c513f43a78951cf742231cf5457a6d9d55edf45df5481a0f299a418d94effef561b15d2c1a01d1b8067e7153fda9", + "0xb9941cccd51dc645920d2781c81a317e5a33cb7cf76427b60396735912cb6d2ca9292bb4d36b6392467d390d2c58d9f3", + "0xa8546b627c76b6ef5c93c6a98538d8593dbe21cb7673fd383d5401b0c935eea0bdeeefeb1af6ad41bad8464fb87bbc48", + "0xaa286b27de2812de63108a1aec29d171775b69538dc6198640ac1e96767c2b83a50391f49259195957d457b493b667c9", + "0xa932fb229f641e9abbd8eb2bd874015d97b6658ab6d29769fc23b7db9e41dd4f850382d4c1f08af8f156c5937d524473", + "0xa1412840fcc86e2aeec175526f2fb36e8b3b8d21a78412b7266daf81e51b3f68584ed8bd42a66a43afdd8c297b320520", + "0x89c78be9efb624c97ebca4fe04c7704fa52311d183ffd87737f76b7dadc187c12c982bd8e9ed7cd8beb48cdaafd2fd01", + "0xa3f5ddec412a5bec0ce15e3bcb41c6214c2b05d4e9135a0d33c8e50a78eaba71e0a5a6ea8b45854dec5c2ed300971fc2", + "0x9721f9cec7a68b7758e3887548790de49fa6a442d0396739efa20c2f50352a7f91d300867556d11a703866def2d5f7b5", + "0xa23764e140a87e5991573521af039630dd28128bf56eed2edbed130fd4278e090b60cf5a1dca9de2910603d44b9f6d45", + "0xa1a6494a994215e48ab55c70efa8ffdddce6e92403c38ae7e8dd2f8288cad460c6c7db526bbdf578e96ca04d9fe12797", + "0xb1705ea4cb7e074efe0405fc7b8ee2ec789af0426142f3ec81241cacd4f7edcd88e39435e4e4d8e7b1df64f3880d6613", + "0x85595d061d677116089a6064418b93eb44ff79e68d12bd9625078d3bbc440a60d0b02944eff6054433ee34710ae6fbb4", + "0x9978d5e30bedb7526734f9a1febd973a70bfa20890490e7cc6f2f9328feab1e24f991285dbc3711d892514e2d7d005ad", + "0xaf30243c66ea43b9f87a061f947f7bce745f09194f6e95f379c7582b9fead920e5d6957eaf05c12ae1282ada4670652f", + "0xa1930efb473f88001e47aa0b2b2a7566848cccf295792e4544096ecd14ee5d7927c173a8576b405bfa2eec551cd67eb5", + "0xb0446d1c590ee5a45f7e22d269c044f3848c97aec1d226b44bfd0e94d9729c28a38bccddc3a1006cc5fe4e3c24f001f2", + "0xb8a8380172df3d84b06176df916cf557966d4f2f716d3e9437e415d75b646810f79f2b2b71d857181b7fc944018883a3", + "0xa563afec25b7817bfa26e19dc9908bc00aa8fc3d19be7d6de23648701659009d10e3e4486c28e9c6b13d48231ae29ac5", + "0xa5a8e80579de886fb7d6408f542791876885947b27ad6fa99a8a26e381f052598d7b4e647b0115d4b5c64297e00ce28e", + "0x8f87afcc7ad33c51ac719bade3cd92da671a37a82c14446b0a2073f4a0a23085e2c8d31913ed2d0be928f053297de8f6", + "0xa43c455ce377e0bc434386c53c752880687e017b2f5ae7f8a15c044895b242dffde4c92fb8f8bb50b18470b17351b156", + "0x8368f8b12a5bceb1dba25adb3a2e9c7dc9b1a77a1f328e5a693f5aec195cd1e06b0fe9476b554c1c25dac6c4a5b640a3", + "0x919878b27f3671fc78396f11531c032f3e2bd132d04cc234fa4858676b15fb1db3051c0b1db9b4fc49038216f11321ce", + "0xb48cd67fb7f1242696c1f877da4bdf188eac676cd0e561fbac1a537f7b8229aff5a043922441d603a26aae56a15faee4", + "0xa3e0fdfd4d29ea996517a16f0370b54787fefe543c2fe73bfc6f9e560c1fd30dad8409859e2d7fa2d44316f24746c712", + "0x8bb156ade8faf149df7bea02c140c7e392a4742ae6d0394d880a849127943e6f26312033336d3b9fdc0092d71b5efe87", + "0x8845e5d5cc555ca3e0523244300f2c8d7e4d02aaebcb5bd749d791208856c209a6f84dd99fd55968c9f0ab5f82916707", + "0xa3e90bb5c97b07789c2f32dff1aec61d0a2220928202f5ad5355ae71f8249237799d6c8a22602e32e572cb12eabe0c17", + "0xb150bcc391884c996149dc3779ce71f15dda63a759ee9cc05871f5a8379dcb62b047098922c0f26c7bd04deb394c33f9", + "0x95cd4ad88d51f0f2efcfd0c2df802fe252bb9704d1afbf9c26a248df22d55da87bdfaf41d7bc6e5df38bd848f0b13f42", + "0xa05a49a31e91dff6a52ac8b9c2cfdd646a43f0d488253f9e3cfbce52f26667166bbb9b608fc358763a65cbf066cd6d05", + "0xa59c3c1227fdd7c2e81f5e11ef5c406da44662987bac33caed72314081e2eed66055d38137e01b2268e58ec85dd986c0", + "0xb7020ec3bd73a99861f0f1d88cf5a19abab1cbe14b7de77c9868398c84bb8e18dbbe9831838a96b6d6ca06e82451c67b", + "0x98d1ff2525e9718ee59a21d8900621636fcd873d9a564b8dceb4be80a194a0148daf1232742730b3341514b2e5a5436c", + "0x886d97b635975fc638c1b6afc493e5998ca139edba131b75b65cfe5a8e814f11bb678e0eeee5e6e5cd913ad3f2fefdfc", + "0x8fb9fd928d38d5d813b671c924edd56601dd7163b686c13f158645c2f869d9250f3859aa5463a39258c90fef0f41190a", + "0xaac35e1cd655c94dec3580bb3800bd9c2946c4a9856f7d725af15fbea6a2d8ca51c8ad2772abed60ee0e3fb9cb24046b", + "0xb8d71fa0fa05ac9e443c9b4929df9e7f09a919be679692682e614d24227e04894bfc14a5c73a62fb927fedff4a0e4aa7", + "0xa45a19f11fbbb531a704badbb813ed8088ab827c884ee4e4ebf363fa1132ff7cfa9d28be9c85b143e4f7cdbc94e7cf1a", + "0x82b54703a4f295f5471b255ab59dce00f0fe90c9fb6e06b9ee48b15c91d43f4e2ef4a96c3118aeb03b08767be58181bb", + "0x8283264c8e6d2a36558f0d145c18576b6600ff45ff99cc93eca54b6c6422993cf392668633e5df396b9331e873d457e5", + "0x8c549c03131ead601bc30eb6b9537b5d3beb7472f5bb1bcbbfd1e9f3704477f7840ab3ab7f7dc13bbbbcdff886a462d4", + "0xafbb0c520ac1b5486513587700ad53e314cb74bfbc12e0b5fbdcfdaac36d342e8b59856196a0d84a25cff6e6e1d17e76", + "0x89e4c22ffb51f2829061b3c7c1983c5c750cad158e3a825d46f7cf875677da5d63f653d8a297022b5db5845c9271b32b", + "0xafb27a86c4c2373088c96b9adf4433f2ebfc78ac5c526e9f0510670b6e4e5e0057c0a4f75b185e1a30331b9e805c1c15", + "0xa18e16b57445f88730fc5d3567bf5a176861dc14c7a08ed2996fe80eed27a0e7628501bcb78a1727c5e9ac55f29c12c4", + "0x93d61bf88b192d6825cf4e1120af1c17aa0f994d158b405e25437eaeefae049f7b721a206e7cc8a04fdc29d3c42580a1", + "0xa99f2995a2e3ed2fd1228d64166112038de2f516410aa439f4c507044e2017ea388604e2d0f7121256fadf7fbe7023d1", + "0x914fd91cffc23c32f1c6d0e98bf660925090d873367d543034654389916f65f552e445b0300b71b61b721a72e9a5983c", + "0xb42a578a7787b71f924e7def425d849c1c777156b1d4170a8ee7709a4a914e816935131afd9a0412c4cb952957b20828", + "0x82fb30590e84b9e45db1ec475a39971cf554dc01bcc7050bc89265740725c02e2be5a972168c5170c86ae83e5b0ad2c0", + "0xb14f8d8e1e93a84976289e0cf0dfa6f3a1809e98da16ee5c4932d0e1ed6bf8a07697fdd4dd86a3df84fb0003353cdcc0", + "0x85d7a2f4bda31aa2cb208b771fe03291a4ebdaf6f1dc944c27775af5caec412584c1f45bc741fca2a6a85acb3f26ad7d", + "0xaf02e56ce886ff2253bc0a68faad76f25ead84b2144e5364f3fb9b648f03a50ee9dc0b2c33ebacf7c61e9e43201ef9ef", + "0x87e025558c8a0b0abd06dfc350016847ea5ced7af2d135a5c9eec9324a4858c4b21510fb0992ec52a73447f24945058e", + "0x80fff0bafcd058118f5e7a4d4f1ae0912efeb281d2cbe4d34ba8945cc3dbe5d8baf47fb077343b90b8d895c90b297aca", + "0xb6edcf3a40e7b1c3c0148f47a263cd819e585a51ef31c2e35a29ce6f04c53e413f743034c0d998d9c00a08ba00166f31", + "0xabb87ed86098c0c70a76e557262a494ff51a30fb193f1c1a32f8e35eafa34a43fcc07aa93a3b7a077d9e35afa07b1a3d", + "0xa280214cd3bb0fb7ecd2d8bcf518cbd9078417f2b91d2533ec2717563f090fb84f2a5fcfdbbeb2a2a1f8a71cc5aa5941", + "0xa63083ca7238ea2b57d15a475963cf1d4f550d8cd76db290014a0461b90351f1f26a67d674c837b0b773b330c7c3d534", + "0xa8fa39064cb585ece5263e2f42f430206476bf261bd50f18d2b694889bd79d04d56410664cecad62690e5c5a20b3f6ff", + "0x85ba52ce9d700a5dcf6c5b00559acbe599d671ce5512467ff4b6179d7fad550567ce2a9c126a50964e3096458ea87920", + "0xb913501e1008f076e5eac6d883105174f88b248e1c9801e568fefaffa1558e4909364fc6d9512aa4d125cbd7cc895f05", + "0x8eb33b5266c8f2ed4725a6ad147a322e44c9264cf261c933cbbe230a43d47fca0f29ec39756b20561dabafadd5796494", + "0x850ebc8b661a04318c9db5a0515066e6454fa73865aa4908767a837857ecd717387f614acb614a88e075d4edc53a2f5a", + "0xa08d6b92d866270f29f4ce23a3f5d99b36b1e241a01271ede02817c8ec3f552a5c562db400766c07b104a331835c0c64", + "0x8131804c89bb3e74e9718bfc4afa547c1005ff676bd4db9604335032b203390cfa54478d45c6c78d1fe31a436ed4be9f", + "0x9106d94f23cc1eacec8316f16d6f0a1cc160967c886f51981fdb9f3f12ee1182407d2bb24e5b873de58cb1a3ee915a6b", + "0xa13806bfc3eae7a7000c9d9f1bd25e10218d4e67f59ae798b145b098bca3edad2b1040e3fc1e6310e612fb8818f459ac", + "0x8c69fbca502046cb5f6db99900a47b34117aef3f4b241690cdb3b84ca2a2fc7833e149361995dc41fa78892525bce746", + "0x852c473150c91912d58ecb05769222fa18312800c3f56605ad29eec9e2d8667b0b81c379048d3d29100ed2773bb1f3c5", + "0xb1767f6074426a00e01095dbb1795beb4e4050c6411792cbad6537bc444c3165d1058bafd1487451f9c5ddd209e0ae7e", + "0x80c600a5fe99354ce59ff0f84c760923dc8ff66a30bf47dc0a086181785ceb01f9b951c4e66df800ea6d705e8bc47055", + "0xb5cf19002fbc88a0764865b82afcb4d64a50196ea361e5c71dff7de084f4dcbbc34ec94a45cc9e0247bd51da565981aa", + "0x93e67a254ea8ce25e112d93cc927fadaa814152a2c4ec7d9a56eaa1ed47aec99b7e9916b02e64452cc724a6641729bbb", + "0xace70b32491bda18eee4a4d041c3bc9effae9340fe7e6c2f5ad975ee0874c17f1a7da7c96bd85fccff9312c518fac6e9", + "0xab4cfa02065017dd7f1aadc66f2c92f78f0f11b8597c03a5d69d82cb2eaf95a4476a836ac102908f137662472c8d914b", + "0xa40b8cd8deb8ae503d20364d64cab7c2801b7728a9646ed19c65edea6a842756a2f636283494299584ad57f4bb12cd0b", + "0x8594e11d5fc2396bcd9dbf5509ce4816dbb2b7305168021c426171fb444d111da5a152d6835ad8034542277011c26c0e", + "0x8024de98c26b4c994a66628dc304bb737f4b6859c86ded552c5abb81fd4c6c2e19d5a30beed398a694b9b2fdea1dd06a", + "0x8843f5872f33f54df8d0e06166c1857d733995f67bc54abb8dfa94ad92407cf0179bc91b0a50bbb56cdc2b350d950329", + "0xb8bab44c7dd53ef9edf497dcb228e2a41282c90f00ba052fc52d57e87b5c8ab132d227af1fcdff9a12713d1f980bcaae", + "0x982b4d7b29aff22d527fd82d2a52601d95549bfb000429bb20789ed45e5abf1f4b7416c7b7c4b79431eb3574b29be658", + "0x8eb1f571b6a1878e11e8c1c757e0bc084bab5e82e897ca9be9b7f4b47b91679a8190bf0fc8f799d9b487da5442415857", + "0xa6e74b588e5af935c8b243e888582ef7718f8714569dd4992920740227518305eb35fab674d21a5551cca44b3e511ef2", + "0xa30fc2f3a4cb4f50566e82307de73cd7bd8fe2c1184e9293c136a9b9e926a018d57c6e4f308c95b9eb8299e94d90a2a1", + "0xa50c5869ca5d2b40722c056a32f918d47e0b65ca9d7863ca7d2fb4a7b64fe523fe9365cf0573733ceaadebf20b48fff8", + "0x83bbdd32c04d17581418cf360749c7a169b55d54f2427390defd9f751f100897b2d800ce6636c5bbc046c47508d60c8c", + "0xa82904bdf614de5d8deaff688c8a5e7ac5b3431687acbcda8fa53960b7c417a39c8b2e462d7af91ce6d79260f412db8e", + "0xa4362e31ff4b05d278b033cf5eebea20de01714ae16d4115d04c1da4754269873afc8171a6f56c5104bfd7b0db93c3e7", + "0xb5b8daa63a3735581e74a021b684a1038cea77168fdb7fdf83c670c2cfabcfc3ab2fc7359069b5f9048188351aef26b5", + "0xb48d723894b7782d96ac8433c48faca1bdfa5238019c451a7f47d958097cce3ae599b876cf274269236b9d6ff8b6d7ca", + "0x98ffff6a61a3a6205c7820a91ca2e7176fab5dba02bc194c4d14942ac421cb254183c705506ab279e4f8db066f941c6c", + "0xae7db24731da2eaa6efc4f7fcba2ecc26940ddd68038dce43acf2cee15b72dc4ef42a7bfdd32946d1ed78786dd7696b3", + "0xa656db14f1de9a7eb84f6301b4acb2fbf78bfe867f48a270e416c974ab92821eb4df1cb881b2d600cfed0034ac784641", + "0xaa315f8ecba85a5535e9a49e558b15f39520fce5d4bf43131bfbf2e2c9dfccc829074f9083e8d49f405fb221d0bc4c3c", + "0x90bffba5d9ff40a62f6c8e9fc402d5b95f6077ed58d030c93e321b8081b77d6b8dac3f63a92a7ddc01585cf2c127d66c", + "0xabdd733a36e0e0f05a570d0504e73801bf9b5a25ff2c78786f8b805704997acb2e6069af342538c581144d53149fa6d3", + "0xb4a723bb19e8c18a01bd449b1bb3440ddb2017f10bb153da27deb7a6a60e9bb37619d6d5435fbb1ba617687838e01dd0", + "0x870016b4678bab3375516db0187a2108b2e840bae4d264b9f4f27dbbc7cc9cac1d7dc582d7a04d6fd1ed588238e5e513", + "0x80d33d2e20e8fc170aa3cb4f69fffb72aeafb3b5bb4ea0bc79ab55da14142ca19b2d8b617a6b24d537366e3b49cb67c3", + "0xa7ee76aec273aaae03b3b87015789289551969fb175c11557da3ab77e39ab49d24634726f92affae9f4d24003050d974", + "0x8415ea4ab69d779ebd42d0fe0c6aef531d6a465a5739e429b1fcf433ec45aa8296c527e965a20f0ec9f340c9273ea3cf", + "0x8c7662520794e8b4405d0b33b5cac839784bc86a5868766c06cbc1fa306dbe334978177417b31baf90ce7b0052a29c56", + "0x902b2abecc053a3dbdea9897ee21e74821f3a1b98b2d560a514a35799f4680322550fd3a728d4f6d64e1de98033c32b8", + "0xa05e84ed9ecab8d508d670c39f2db61ad6e08d2795ec32a3c9d0d3737ef3801618f4fc2a95f90ec2f068606131e076c5", + "0x8b9208ff4d5af0c2e3f53c9375da666773ac57197dfabb0d25b1c8d0588ba7f3c15ee9661bb001297f322ea2fbf6928b", + "0xa3c827741b34a03254d4451b5ab74a96f2b9f7fb069e2f5adaf54fd97cc7a4d516d378db5ca07da87d8566d6eef13726", + "0x8509d8a3f4a0ed378e0a1e28ea02f6bf1d7f6c819c6c2f5297c7df54c895b848f841653e32ba2a2c22c2ff739571acb8", + "0xa0ce988b7d3c40b4e496aa83a09e4b5472a2d98679622f32bea23e6d607bc7de1a5374fb162bce0549a67dad948519be", + "0xaa8a3dd12bd60e3d2e05f9c683cdcb8eab17fc59134815f8d197681b1bcf65108cba63ac5c58ee632b1e5ed6bba5d474", + "0x8b955f1d894b3aefd883fb4b65f14cd37fc2b9db77db79273f1700bef9973bf3fd123897ea2b7989f50003733f8f7f21", + "0xac79c00ddac47f5daf8d9418d798d8af89fc6f1682e7e451f71ea3a405b0d36af35388dd2a332af790bc83ca7b819328", + "0xa0d44dd2a4438b809522b130d0938c3fe7c5c46379365dbd1810a170a9aa5818e1c783470dd5d0b6d4ac7edbb7330910", + "0xa30b69e39ad43dd540a43c521f05b51b5f1b9c4eed54b8162374ae11eac25da4f5756e7b70ce9f3c92c2eeceee7431ed", + "0xac43220b762c299c7951222ea19761ab938bf38e4972deef58ed84f4f9c68c230647cf7506d7cbfc08562fcca55f0485", + "0xb28233b46a8fb424cfa386a845a3b5399d8489ceb83c8f3e05c22c934798d639c93718b7b68ab3ce24c5358339e41cbb", + "0xac30d50ee8ce59a10d4b37a3a35e62cdb2273e5e52232e202ca7d7b8d09d28958ee667fae41a7bb6cdc6fe8f6e6c9c85", + "0xb199842d9141ad169f35cc7ff782b274cbaa645fdb727761e0a89edbf0d781a15f8218b4bf4eead326f2903dd88a9cc1", + "0x85e018c7ddcad34bb8285a737c578bf741ccd547e68c734bdb3808380e12c5d4ef60fc896b497a87d443ff9abd063b38", + "0x8c856e6ba4a815bdb891e1276f93545b7072f6cb1a9aa6aa5cf240976f29f4dee01878638500a6bf1daf677b96b54343", + "0xb8a47555fa8710534150e1a3f13eab33666017be6b41005397afa647ea49708565f2b86b77ad4964d140d9ced6b4d585", + "0x8cd1f1db1b2f4c85a3f46211599caf512d5439e2d8e184663d7d50166fd3008f0e9253272f898d81007988435f715881", + "0xb1f34b14612c973a3eceb716dc102b82ab18afef9de7630172c2780776679a7706a4874e1df3eaadf541fb009731807f", + "0xb25464af9cff883b55be2ff8daf610052c02df9a5e147a2cf4df6ce63edcdee6dc535c533590084cc177da85c5dc0baa", + "0x91c3c4b658b42d8d3448ae1415d4541d02379a40dc51e36a59bd6e7b9ba3ea51533f480c7c6e8405250ee9b96a466c29", + "0x86dc027b95deb74c36a58a1333a03e63cb5ae22d3b29d114cfd2271badb05268c9d0c819a977f5e0c6014b00c1512e3a", + "0xae0e6ff58eb5fa35da5107ebeacf222ab8f52a22bb1e13504247c1dfa65320f40d97b0e6b201cb6613476687cb2f0681", + "0x8f13415d960b9d7a1d93ef28afc2223e926639b63bdefce0f85e945dfc81670a55df288893a0d8b3abe13c5708f82f91", + "0x956f67ca49ad27c1e3a68c1faad5e7baf0160c459094bf6b7baf36b112de935fdfd79fa4a9ea87ea8de0ac07272969f4", + "0x835e45e4a67df9fb51b645d37840b3a15c171d571a10b03a406dd69d3c2f22df3aa9c5cbe1e73f8d767ce01c4914ea9a", + "0x919b938e56d4b32e2667469d0bdccb95d9dda3341aa907683ee70a14bbbe623035014511c261f4f59b318b610ac90aa3", + "0x96b48182121ccd9d689bf1dfdc228175564cd68dc904a99c808a7f0053a6f636c9d953e12198bdf2ea49ea92772f2e18", + "0xac5e5a941d567fa38fdbcfa8cf7f85bb304e3401c52d88752bcd516d1fa9bac4572534ea2205e38423c1df065990790f", + "0xac0bd594fb85a8d4fc26d6df0fa81f11919401f1ecf9168b891ec7f061a2d9368af99f7fd8d9b43b2ce361e7b8482159", + "0x83d92c69ca540d298fe80d8162a1c7af3fa9b49dfb69e85c1d136a3ec39fe419c9fa78e0bb6d96878771fbd37fe92e40", + "0xb35443ae8aa66c763c2db9273f908552fe458e96696b90e41dd509c17a5c04ee178e3490d9c6ba2dc0b8f793c433c134", + "0x923b2d25aa45b2e580ffd94cbb37dc8110f340f0f011217ee1bd81afb0714c0b1d5fb4db86006cdd2457563276f59c59", + "0x96c9125d38fca1a61ac21257b696f8ac3dae78def50285e44d90ea293d591d1c58f703540a7e4e99e070afe4646bbe15", + "0xb57946b2332077fbcdcb406b811779aefd54473b5559a163cd65cb8310679b7e2028aa55c12a1401fdcfcac0e6fae29a", + "0x845daedc5cf972883835d7e13c937b63753c2200324a3b8082a6c4abb4be06c5f7c629d4abe4bfaf1d80a1f073eb6ce6", + "0x91a55dfd0efefcd03dc6dacc64ec93b8d296cb83c0ee72400a36f27246e7f2a60e73b7b70ba65819e9cfb73edb7bd297", + "0x8874606b93266455fe8fdd25df9f8d2994e927460af06f2e97dd4d2d90db1e6b06d441b72c2e76504d753badca87fb37", + "0x8ee99e6d231274ff9252c0f4e84549da173041299ad1230929c3e3d32399731c4f20a502b4a307642cac9306ccd49d3c", + "0x8836497714a525118e20849d6933bb8535fb6f72b96337d49e3133d936999c90a398a740f42e772353b5f1c63581df6d", + "0xa6916945e10628f7497a6cdc5e2de113d25f7ade3e41e74d3de48ccd4fce9f2fa9ab69645275002e6f49399b798c40af", + "0x9597706983107eb23883e0812e1a2c58af7f3499d50c6e29b455946cb9812fde1aa323d9ed30d1c0ffd455abe32303cd", + "0xa24ee89f7f515cc33bdbdb822e7d5c1877d337f3b2162303cfc2dae028011c3a267c5cb4194afa63a4856a6e1c213448", + "0x8cd25315e4318801c2776824ae6e7d543cb85ed3bc2498ba5752df2e8142b37653cf9e60104d674be3aeb0a66912e97a", + "0xb5085ecbe793180b40dbeb879f4c976eaaccaca3a5246807dced5890e0ed24d35f3f86955e2460e14fb44ff5081c07ba", + "0x960188cc0b4f908633a6840963a6fa2205fc42c511c6c309685234911c5304ef4c304e3ae9c9c69daa2fb6a73560c256", + "0xa32d0a70bf15d569b4cda5aebe3e41e03c28bf99cdd34ffa6c5d58a097f322772acca904b3a47addb6c7492a7126ebac", + "0x977f72d06ad72d4aa4765e0f1f9f4a3231d9f030501f320fe7714cc5d329d08112789fa918c60dd7fdb5837d56bb7fc6", + "0x99fa038bb0470d45852bb871620d8d88520adb701712fcb1f278fed2882722b9e729e6cdce44c82caafad95e37d0e6f7", + "0xb855e8f4fc7634ada07e83b6c719a1e37acb06394bc8c7dcab7747a8c54e5df3943915f021364bd019fdea103864e55f", + "0x88bc2cd7458532e98c596ef59ea2cf640d7cc31b4c33cef9ed065c078d1d4eb49677a67de8e6229cc17ea48bace8ee5a", + "0xaaa78a3feaa836d944d987d813f9b9741afb076e6aca1ffa42682ab06d46d66e0c07b8f40b9dbd63e75e81efa1ef7b08", + "0xb7b080420cc4d808723b98b2a5b7b59c81e624ab568ecdfdeb8bf3aa151a581b6f56e983ef1b6f909661e25db40b0c69", + "0xabee85c462ac9a2c58e54f06c91b3e5cd8c5f9ab5b5deb602b53763c54826ed6deb0d6db315a8d7ad88733407e8d35e2", + "0x994d075c1527407547590df53e9d72dd31f037c763848d1662eebd4cefec93a24328c986802efa80e038cb760a5300f5", + "0xab8777640116dfb6678e8c7d5b36d01265dfb16321abbfc277da71556a34bb3be04bc4ae90124ed9c55386d2bfb3bda0", + "0x967e3a828bc59409144463bcf883a3a276b5f24bf3cbfdd7a42343348cba91e00b46ac285835a9b91eef171202974204", + "0x875a9f0c4ffe5bb1d8da5e3c8e41d0397aa6248422a628bd60bfae536a651417d4e8a7d2fb98e13f2dad3680f7bd86d3", + "0xacaa330c3e8f95d46b1880126572b238dbb6d04484d2cd4f257ab9642d8c9fc7b212188b9c7ac9e0fd135c520d46b1bf", + "0xaceb762edbb0f0c43dfcdb01ea7a1ac5918ca3882b1e7ebc4373521742f1ed5250d8966b498c00b2b0f4d13212e6dd0b", + "0x81d072b4ad258b3646f52f399bced97c613b22e7ad76373453d80b1650c0ca87edb291a041f8253b649b6e5429bb4cff", + "0x980a47d27416ac39c7c3a0ebe50c492f8c776ea1de44d5159ac7d889b6d554357f0a77f0e5d9d0ff41aae4369eba1fc2", + "0x8b4dfd5ef5573db1476d5e43aacfb5941e45d6297794508f29c454fe50ea622e6f068b28b3debe8635cf6036007de2e3", + "0xa60831559d6305839515b68f8c3bc7abbd8212cc4083502e19dd682d56ca37c9780fc3ce4ec2eae81ab23b221452dc57", + "0x951f6b2c1848ced9e8a2339c65918e00d3d22d3e59a0a660b1eca667d18f8430d737884e9805865ef3ed0fe1638a22d9", + "0xb02e38fe790b492aa5e89257c4986c9033a8b67010fa2add9787de857d53759170fdd67715ca658220b4e14b0ca48124", + "0xa51007e4346060746e6b0e4797fc08ef17f04a34fe24f307f6b6817edbb8ce2b176f40771d4ae8a60d6152cbebe62653", + "0xa510005b05c0b305075b27b243c9d64bcdce85146b6ed0e75a3178b5ff9608213f08c8c9246f2ca6035a0c3e31619860", + "0xaaff4ef27a7a23be3419d22197e13676d6e3810ceb06a9e920d38125745dc68a930f1741c9c2d9d5c875968e30f34ab5", + "0x864522a9af9857de9814e61383bebad1ba9a881696925a0ea6bfc6eff520d42c506bbe5685a9946ed710e889765be4a0", + "0xb63258c080d13f3b7d5b9f3ca9929f8982a6960bdb1b0f8676f4dca823971601672f15e653917bf5d3746bb220504913", + "0xb51ce0cb10869121ae310c7159ee1f3e3a9f8ad498827f72c3d56864808c1f21fa2881788f19ece884d3f705cd7bd0c5", + "0x95d9cecfc018c6ed510e441cf84c712d9909c778c16734706c93222257f64dcd2a9f1bd0b400ca271e22c9c487014274", + "0x8beff4d7d0140b86380ff4842a9bda94c2d2be638e20ac68a4912cb47dbe01a261857536375208040c0554929ced1ddc", + "0x891ff49258749e2b57c1e9b8e04b12c77d79c3308b1fb615a081f2aacdfb4b39e32d53e069ed136fdbd43c53b87418fa", + "0x9625cad224e163d387738825982d1e40eeff35fe816d10d7541d15fdc4d3eee48009090f3faef4024b249205b0b28f72", + "0x8f3947433d9bd01aa335895484b540a9025a19481a1c40b4f72dd676bfcf332713714fd4010bde936eaf9470fd239ed0", + "0xa00ec2d67789a7054b53f0e858a8a232706ccc29a9f3e389df7455f1a51a2e75801fd78469a13dbc25d28399ae4c6182", + "0xa3f65884506d4a62b8775a0ea0e3d78f5f46bc07910a93cd604022154eabdf1d73591e304d61edc869e91462951975e1", + "0xa14eef4fd5dfac311713f0faa9a60415e3d30b95a4590cbf95f2033dffb4d16c02e7ceff3dcd42148a4e3bc49cce2dd4", + "0x8afa11c0eef3c540e1e3460bc759bb2b6ea90743623f88e62950c94e370fe4fd01c22b6729beba4dcd4d581198d9358f", + "0xafb05548a69f0845ffcc5f5dc63e3cdb93cd270f5655173b9a950394b0583663f2b7164ba6df8d60c2e775c1d9f120af", + "0x97f179e01a947a906e1cbeafa083960bc9f1bade45742a3afee488dfb6011c1c6e2db09a355d77f5228a42ccaa7bdf8e", + "0x8447fca4d35f74b3efcbd96774f41874ca376bf85b79b6e66c92fa3f14bdd6e743a051f12a7fbfd87f319d1c6a5ce217", + "0xa57ca39c23617cd2cf32ff93b02161bd7baf52c4effb4679d9d5166406e103bc8f3c6b5209e17c37dbb02deb8bc72ddd", + "0x9667c7300ff80f0140be002b0e36caab07aaee7cce72679197c64d355e20d96196acaf54e06e1382167d081fe6f739c1", + "0x828126bb0559ce748809b622677267ca896fa2ee76360fd2c02990e6477e06a667241379ca7e65d61a5b64b96d7867de", + "0x8b8835dea6ba8cf61c91f01a4b3d2f8150b687a4ee09b45f2e5fc8f80f208ae5d142d8e3a18153f0722b90214e60c5a7", + "0xa98e8ff02049b4da386e3ee93db23bbb13dfeb72f1cfde72587c7e6d962780b7671c63e8ac3fbaeb1a6605e8d79e2f29", + "0x87a4892a0026d7e39ef3af632172b88337cb03669dea564bcdb70653b52d744730ebb5d642e20cb627acc9dbb547a26b", + "0x877352a22fc8052878a57effc159dac4d75fe08c84d3d5324c0bab6d564cdf868f33ceee515eee747e5856b62cfa0cc7", + "0x8b801ba8e2ff019ee62f64b8cb8a5f601fc35423eb0f9494b401050103e1307dc584e4e4b21249cd2c686e32475e96c3", + "0xa9e7338d6d4d9bfec91b2af28a8ed13b09415f57a3a00e5e777c93d768fdb3f8e4456ae48a2c6626b264226e911a0e28", + "0x99c05fedf40ac4726ed585d7c1544c6e79619a0d3fb6bda75a08c7f3c0008e8d5e19ed4da48de3216135f34a15eba17c", + "0xa61cce8a1a8b13a4a650fdbec0eeea8297c352a8238fb7cac95a0df18ed16ee02a3daa2de108fa122aca733bd8ad7855", + "0xb97f37da9005b440b4cb05870dd881bf8491fe735844f2d5c8281818583b38e02286e653d9f2e7fa5e74c3c3eb616540", + "0xa72164a8554da8e103f692ac5ebb4aece55d5194302b9f74b6f2a05335b6e39beede0bf7bf8c5bfd4d324a784c5fb08c", + "0xb87e8221c5341cd9cc8bb99c10fe730bc105550f25ed4b96c0d45e6142193a1b2e72f1b3857373a659b8c09be17b3d91", + "0xa41fb1f327ef91dcb7ac0787918376584890dd9a9675c297c45796e32d6e5985b12f9b80be47fc3a8596c245f419d395", + "0x90dafa3592bdbb3465c92e2a54c2531822ba0459d45d3e7a7092fa6b823f55af28357cb51896d4ec2d66029c82f08e26", + "0xa0a9adc872ebc396557f484f1dd21954d4f4a21c4aa5eec543f5fa386fe590839735c01f236574f7ff95407cd12de103", + "0xb8c5c940d58be7538acf8672852b5da3af34f82405ef2ce8e4c923f1362f97fc50921568d0fd2fe846edfb0823e62979", + "0x85aaf06a8b2d0dac89dafd00c28533f35dbd074978c2aaa5bef75db44a7b12aeb222e724f395513b9a535809a275e30b", + "0x81f3cbe82fbc7028c26a6c1808c604c63ba023a30c9f78a4c581340008dbda5ec07497ee849a2183fcd9124f7936af32", + "0xa11ac738de75fd60f15a34209d3825d5e23385796a4c7fc5931822f3f380af977dd0f7b59fbd58eed7777a071e21b680", + "0x85a279c493de03db6fa6c3e3c1b1b29adc9a8c4effc12400ae1128da8421954fa8b75ad19e5388fe4543b76fb0812813", + "0x83a217b395d59ab20db6c4adb1e9713fc9267f5f31a6c936042fe051ce8b541f579442f3dcf0fa16b9e6de9fd3518191", + "0x83a0b86e7d4ed8f9ccdc6dfc8ff1484509a6378fa6f09ed908e6ab9d1073f03011dc497e14304e4e3d181b57de06a5ab", + "0xa63ad69c9d25704ce1cc8e74f67818e5ed985f8f851afa8412248b2df5f833f83b95b27180e9e7273833ed0d07113d3b", + "0x99b1bc2021e63b561fe44ddd0af81fcc8627a91bfeecbbc989b642bc859abc0c8d636399701aad7bbaf6a385d5f27d61", + "0xb53434adb66f4a807a6ad917c6e856321753e559b1add70824e5c1e88191bf6993fccb9b8b911fc0f473fb11743acacd", + "0x97ed3b9e6fb99bf5f945d4a41f198161294866aa23f2327818cdd55cb5dc4c1a8eff29dd8b8d04902d6cd43a71835c82", + "0xb1e808260e368a18d9d10bdea5d60223ba1713b948c782285a27a99ae50cc5fc2c53d407de07155ecc16fb8a36d744a0", + "0xa3eb4665f18f71833fec43802730e56b3ee5a357ea30a888ad482725b169d6f1f6ade6e208ee081b2e2633079b82ba7d", + "0xab8beb2c8353fc9f571c18fdd02bdb977fc883313469e1277b0372fbbb33b80dcff354ca41de436d98d2ed710faa467e", + "0xaa9071cfa971e4a335a91ad634c98f2be51544cb21f040f2471d01bb97e1df2277ae1646e1ea8f55b7ba9f5c8c599b39", + "0x80b7dbfdcaf40f0678012acc634eba44ea51181475180d9deb2050dc4f2de395289edd0223018c81057ec79b04b04c49", + "0x89623d7f6cb17aa877af14de842c2d4ab7fd576d61ddd7518b5878620a01ded40b6010de0da3cdf31d837eecf30e9847", + "0xa773bb024ae74dd24761f266d4fb27d6fd366a8634febe8235376b1ae9065c2fe12c769f1d0407867dfbe9f5272c352f", + "0x8455a561c3aaa6ba64c881a5e13921c592b3a02e968f4fb24a2243c36202795d0366d9cc1a24e916f84d6e158b7aeac7", + "0x81d8bfc4b283cf702a40b87a2b96b275bdbf0def17e67d04842598610b67ea08c804d400c3e69fa09ea001eaf345b276", + "0xb8f8f82cb11fea1c99467013d7e167ff03deb0c65a677fab76ded58826d1ba29aa7cf9fcd7763615735ea3ad38e28719", + "0x89a6a04baf9cccc1db55179e1650b1a195dd91fb0aebc197a25143f0f393524d2589975e3fbfc2547126f0bced7fd6f2", + "0xb81b2162df045390f04df07cbd0962e6b6ca94275a63edded58001a2f28b2ae2af2c7a6cba4ecd753869684e77e7e799", + "0xa3757f722776e50de45c62d9c4a2ee0f5655a512344c4cbec542d8045332806568dd626a719ef21a4eb06792ca70f204", + "0x8c5590df96ec22179a4e8786de41beb44f987a1dcc508eb341eecbc0b39236fdfad47f108f852e87179ccf4e10091e59", + "0x87502f026ed4e10167419130b88c3737635c5b9074c364e1dd247cef5ef0fc064b4ae99b187e33301e438bbd2fe7d032", + "0xaf925a2165e980ced620ff12289129fe17670a90ae0f4db9d4b39bd887ccb1f5d2514ac9ecf910f6390a8fc66bd5be17", + "0x857fca899828cf5c65d26e3e8a6e658542782fc72762b3b9c73514919f83259e0f849a9d4838b40dc905fe43024d0d23", + "0x87ffebdbfb69a9e1007ebac4ffcb4090ff13705967b73937063719aa97908986effcb7262fdadc1ae0f95c3690e3245d", + "0xa9ff6c347ac6f4c6ab993b748802e96982eaf489dc69032269568412fc9a79e7c2850dfc991b28211b3522ee4454344b", + "0xa65b3159df4ec48bebb67cb3663cd744027ad98d970d620e05bf6c48f230fa45bf17527fe726fdf705419bb7a1bb913e", + "0x84b97b1e6408b6791831997b03cd91f027e7660fd492a93d95daafe61f02427371c0e237c75706412f442991dfdff989", + "0xab761c26527439b209af0ae6afccd9340bbed5fbe098734c3145b76c5d2cd7115d9227b2eb523882b7317fbb09180498", + "0xa0479a8da06d7a69c0b0fee60df4e691c19c551f5e7da286dab430bfbcabf31726508e20d26ea48c53365a7f00a3ad34", + "0xa732dfc9baa0f4f40b5756d2e8d8937742999623477458e0bc81431a7b633eefc6f53b3b7939fe0a020018549c954054", + "0x901502436a1169ba51dc479a5abe7c8d84e0943b16bc3c6a627b49b92cd46263c0005bc324c67509edd693f28e612af1", + "0xb627aee83474e7f84d1bab9b7f6b605e33b26297ac6bbf52d110d38ba10749032bd551641e73a383a303882367af429b", + "0x95108866745760baef4a46ef56f82da6de7e81c58b10126ebd2ba2cd13d339f91303bf2fb4dd104a6956aa3b13739503", + "0x899ed2ade37236cec90056f3569bc50f984f2247792defafcceb49ad0ca5f6f8a2f06573705300e07f0de0c759289ff5", + "0xa9f5eee196d608efe4bcef9bf71c646d27feb615e21252cf839a44a49fd89da8d26a758419e0085a05b1d59600e2dc42", + "0xb36c6f68fed6e6c85f1f4a162485f24817f2843ec5cbee45a1ebfa367d44892e464949c6669f7972dc7167af08d55d25", + "0xaaaede243a9a1b6162afbc8f571a52671a5a4519b4062e3f26777664e245ba873ed13b0492c5dbf0258c788c397a0e9e", + "0x972b4fb39c31cbe127bf9a32a5cc10d621ebdd9411df5e5da3d457f03b2ab2cd1f6372d8284a4a9400f0b06ecdbfd38e", + "0x8f6ca1e110e959a4b1d9a5ce5f212893cec21db40d64d5ac4d524f352d72198f923416a850bf845bc5a22a79c0ea2619", + "0xa0f3c93b22134f66f04b2553a53b738644d1665ceb196b8494b315a4c28236fb492017e4a0de4224827c78e42f9908b7", + "0x807fb5ee74f6c8735b0b5ca07e28506214fe4047dbeb00045d7c24f7849e98706aea79771241224939cb749cf1366c7d", + "0x915eb1ff034224c0b645442cdb7d669303fdc00ca464f91aaf0b6fde0b220a3a74ff0cb043c26c9f3a5667b3fdaa9420", + "0x8fda6cef56ed33fefffa9e6ac8e6f76b1af379f89761945c63dd448801f7bb8ca970504a7105fac2f74f652ccff32327", + "0x87380cffdcffb1d0820fa36b63cc081e72187f86d487315177d4d04da4533eb19a0e2ff6115ceab528887819c44a5164", + "0x8cd89e03411a18e7f16f968b89fb500c36d47d229f6487b99e62403a980058db5925ce249206743333538adfad168330", + "0x974451b1df33522ce7056de9f03e10c70bf302c44b0741a59df3d6877d53d61a7394dcee1dd46e013d7cb9d73419c092", + "0x98c35ddf645940260c490f384a49496a7352bb8e3f686feed815b1d38f59ded17b1ad6e84a209e773ed08f7b8ff1e4c2", + "0x963f386cf944bb9b2ddebb97171b64253ea0a2894ac40049bdd86cda392292315f3a3d490ca5d9628c890cfb669f0acb", + "0x8d507712152babd6d142ee682638da8495a6f3838136088df9424ef50d5ec28d815a198c9a4963610b22e49b4cdf95e9", + "0x83d4bc6b0be87c8a4f1e9c53f257719de0c73d85b490a41f7420e777311640937320557ff2f1d9bafd1daaa54f932356", + "0x82f5381c965b7a0718441131c4d13999f4cdce637698989a17ed97c8ea2e5bdb5d07719c5f7be8688edb081b23ede0f4", + "0xa6ebecab0b72a49dfd01d69fa37a7f74d34fb1d4fef0aa10e3d6fceb9eccd671225c230af89f6eb514250e41a5f91f52", + "0x846d185bdad6e11e604df7f753b7a08a28b643674221f0e750ebdb6b86ec584a29c869e131bca868972a507e61403f6a", + "0x85a98332292acb744bd1c0fd6fdcf1f889a78a2c9624d79413ffa194cc8dfa7821a4b60cde8081d4b5f71f51168dd67f", + "0x8f7d97c3b4597880d73200d074eb813d95432306e82dafc70b580b8e08cb8098b70f2d07b4b3ac6a4d77e92d57035031", + "0x8185439c8751e595825d7053518cbe121f191846a38d4dbcb558c3f9d7a3104f3153401adaaaf27843bbe2edb504bfe3", + "0xb3c00d8ece1518fca6b1215a139b0a0e26d9cba1b3a424f7ee59f30ce800a5db967279ed60958dd1f3ee69cf4dd1b204", + "0xa2e6cb6978e883f9719c3c0d44cfe8de0cc6f644b98f98858433bea8bbe7b612c8aca5952fccce4f195f9d54f9722dc2", + "0x99663087e3d5000abbec0fbda4e7342ec38846cc6a1505191fb3f1a337cb369455b7f8531a6eb8b0f7b2c4baf83cbe2b", + "0xab0836c6377a4dbc7ca6a4d6cf021d4cd60013877314dd05f351706b128d4af6337711ed3443cb6ca976f40d74070a9a", + "0x87abfd5126152fd3bac3c56230579b489436755ea89e0566aa349490b36a5d7b85028e9fb0710907042bcde6a6f5d7e3", + "0x974ba1033f75f60e0cf7c718a57ae1da3721cf9d0fb925714c46f027632bdd84cd9e6de4cf4d00bc55465b1c5ebb7384", + "0xa607b49d73689ac64f25cec71221d30d53e781e1100d19a2114a21da6507a60166166369d860bd314acb226596525670", + "0xa7c2b0b915d7beba94954f2aa7dd08ec075813661e2a3ecca5d28a0733e59583247fed9528eb28aba55b972cdbaf06eb", + "0xb8b3123e44128cc8efbe3270f2f94e50ca214a4294c71c3b851f8cbb70cb67fe9536cf07d04bf7fe380e5e3a29dd3c15", + "0xa59a07e343b62ad6445a0859a32b58c21a593f9ddbfe52049650f59628c93715aa1f4e1f45b109321756d0eeec8a5429", + "0x94f51f8a4ed18a6030d0aaa8899056744bd0e9dc9ac68f62b00355cddab11da5da16798db75f0bfbce0e5bdfe750c0b6", + "0x97460a97ca1e1fa5ce243b81425edc0ec19b7448e93f0b55bc9785eedeeafe194a3c8b33a61a5c72990edf375f122777", + "0x8fa859a089bc17d698a7ee381f37ce9beadf4e5b44fce5f6f29762bc04f96faff5d58c48c73631290325f05e9a1ecf49", + "0xabdf38f3b20fc95eff31de5aa9ef1031abfa48f1305ee57e4d507594570401503476d3bcc493838fc24d6967a3082c7f", + "0xb8914bfb82815abb86da35c64d39ab838581bc0bf08967192697d9663877825f2b9d6fbdcf9b410463482b3731361aef", + "0xa8187f9d22b193a5f578999954d6ec9aa9b32338ccadb8a3e1ce5bad5ea361d69016e1cdfac44e9d6c54e49dd88561b9", + "0xaac262cb7cba7fd62c14daa7b39677cabc1ef0947dd06dd89cac8570006a200f90d5f0353e84f5ff03179e3bebe14231", + "0xa630ef5ece9733b8c46c0a2df14a0f37647a85e69c63148e79ffdcc145707053f9f9d305c3f1cf3c7915cb46d33abd07", + "0xb102c237cb2e254588b6d53350dfda6901bd99493a3fbddb4121d45e0b475cf2663a40d7b9a75325eda83e4ba1e68cb3", + "0x86a930dd1ddcc16d1dfa00aa292cb6c2607d42c367e470aa920964b7c17ab6232a7108d1c2c11fc40fb7496547d0bbf8", + "0xa832fdc4500683e72a96cce61e62ac9ee812c37fe03527ad4cf893915ca1962cee80e72d4f82b20c8fc0b764376635a1", + "0x88ad985f448dabb04f8808efd90f273f11f5e6d0468b5489a1a6a3d77de342992a73eb842d419034968d733f101ff683", + "0x98a8538145f0d86f7fbf9a81c9140f6095c5bdd8960b1c6f3a1716428cd9cca1bf8322e6d0af24e6169abcf7df2b0ff6", + "0x9048c6eba5e062519011e177e955a200b2c00b3a0b8615bdecdebc217559d41058d3315f6d05617be531ef0f6aef0e51", + "0x833bf225ab6fc68cdcacf1ec1b50f9d05f5410e6cdcd8d56a3081dc2be8a8d07b81534d1ec93a25c2e270313dfb99e3b", + "0xa84bcd24c3da5e537e64a811b93c91bfc84d7729b9ead7f79078989a6eb76717d620c1fad17466a0519208651e92f5ff", + "0xb7cdd0a3fbd79aed93e1b5a44ca44a94e7af5ed911e4492f332e3a5ed146c7286bde01b52276a2fcc02780d2109874dd", + "0x8a19a09854e627cb95750d83c20c67442b66b35896a476358f993ba9ac114d32c59c1b3d0b8787ee3224cf3888b56c64", + "0xa9abd5afb8659ee52ada8fa5d57e7dd355f0a7350276f6160bec5fbf70d5f99234dd179eb221c913e22a49ec6d267846", + "0x8c13c4274c0d30d184e73eaf812200094bbbd57293780bdadbceb262e34dee5b453991e7f37c7333a654fc71c69d6445", + "0xa4320d73296ff8176ce0127ca1921c450e2a9c06eff936681ebaffb5a0b05b17fded24e548454de89aca2dcf6d7a9de4", + "0xb2b8b3e15c1f645f07783e5628aba614e60157889db41d8161d977606788842b67f83f361eae91815dc0abd84e09abd5", + "0xad26c3aa35ddfddc15719b8bb6c264aaec7065e88ac29ba820eb61f220fef451609a7bb037f3722d022e6c86e4f1dc88", + "0xb8615bf43e13ae5d7b8dd903ce37190800cd490f441c09b22aa29d7a29ed2c0417b7a08ead417868f1de2589deaadd80", + "0x8d3425e1482cd1e76750a76239d33c06b3554c3c3c87c15cb7ab58b1cee86a4c5c4178b44e23f36928365a1b484bde02", + "0x806893a62e38c941a7dd6f249c83af16596f69877cc737d8f73f6b8cd93cbc01177a7a276b2b8c6b0e5f2ad864db5994", + "0x86618f17fa4b0d65496b661bbb5ba3bc3a87129d30a4b7d4f515b904f4206ca5253a41f49fd52095861e5e065ec54f21", + "0x9551915da1304051e55717f4c31db761dcdcf3a1366c89a4af800a9e99aca93a357bf928307f098e62b44a02cb689a46", + "0x8f79c4ec0ec1146cb2a523b52fe33def90d7b5652a0cb9c2d1c8808a32293e00aec6969f5b1538e3a94cd1efa3937f86", + "0xa0c03e329a707300081780f1e310671315b4c6a4cedcb29697aedfabb07a9d5df83f27b20e9c44cf6b16e39d9ded5b98", + "0x86a7cfa7c8e7ce2c01dd0baec2139e97e8e090ad4e7b5f51518f83d564765003c65968f85481bbb97cb18f005ccc7d9f", + "0xa33811770c6dfda3f7f74e6ad0107a187fe622d61b444bbd84fd7ef6e03302e693b093df76f6ab39bb4e02afd84a575a", + "0x85480f5c10d4162a8e6702b5e04f801874d572a62a130be94b0c02b58c3c59bdcd48cd05f0a1c2839f88f06b6e3cd337", + "0x8e181011564b17f7d787fe0e7f3c87f6b62da9083c54c74fd6c357a1f464c123c1d3d8ade3cf72475000b464b14e2be3", + "0x8ee178937294b8c991337e0621ab37e9ffa4ca2bdb3284065c5e9c08aad6785d50cf156270ff9daf9a9127289710f55b", + "0x8bd1e8e2d37379d4b172f1aec96f2e41a6e1393158d7a3dbd9a95c8dd4f8e0b05336a42efc11a732e5f22b47fc5c271d", + "0x8f3da353cd487c13136a85677de8cedf306faae0edec733cf4f0046f82fa4639db4745b0095ff33a9766aba50de0cbcf", + "0x8d187c1e97638df0e4792b78e8c23967dac43d98ea268ca4aabea4e0fa06cb93183fd92d4c9df74118d7cc27bf54415e", + "0xa4c992f08c2f8bac0b74b3702fb0c75c9838d2ce90b28812019553d47613c14d8ce514d15443159d700b218c5a312c49", + "0xa6fd1874034a34c3ea962a316c018d9493d2b3719bb0ec4edbc7c56b240802b2228ab49bee6f04c8a3e9f6f24a48c1c2", + "0xb2efed8e799f8a15999020900dc2c58ece5a3641c90811b86a5198e593d7318b9d53b167818ccdfbe7df2414c9c34011", + "0x995ff7de6181ddf95e3ead746089c6148da3508e4e7a2323c81785718b754d356789b902e7e78e2edc6b0cbd4ff22c78", + "0x944073d24750a9068cbd020b834afc72d2dde87efac04482b3287b40678ad07588519a4176b10f2172a2c463d063a5cd", + "0x99db4b1bb76475a6fd75289986ef40367960279524378cc917525fb6ba02a145a218c1e9caeb99332332ab486a125ac0", + "0x89fce4ecd420f8e477af4353b16faabb39e063f3f3c98fde2858b1f2d1ef6eed46f0975a7c08f233b97899bf60ccd60a", + "0x8c09a4f07a02b80654798bc63aada39fd638d3e3c4236ccd8a5ca280350c31e4a89e5f4c9aafb34116e71da18c1226b8", + "0x85325cfa7ded346cc51a2894257eab56e7488dbff504f10f99f4cd2b630d913003761a50f175ed167e8073f1b6b63fb0", + "0xb678b4fbec09a8cc794dcbca185f133578f29e354e99c05f6d07ac323be20aecb11f781d12898168e86f2e0f09aca15e", + "0xa249cfcbca4d9ba0a13b5f6aac72bf9b899adf582f9746bb2ad043742b28915607467eb794fca3704278f9136f7642be", + "0x9438e036c836a990c5e17af3d78367a75b23c37f807228362b4d13e3ddcb9e431348a7b552d09d11a2e9680704a4514f", + "0x925ab70450af28c21a488bfb5d38ac994f784cf249d7fd9ad251bb7fd897a23e23d2528308c03415074d43330dc37ef4", + "0xa290563904d5a8c0058fc8330120365bdd2ba1fdbaef7a14bc65d4961bb4217acfaed11ab82669e359531f8bf589b8db", + "0xa7e07a7801b871fc9b981a71e195a3b4ba6b6313bc132b04796a125157e78fe5c11a3a46cf731a255ac2d78a4ae78cd0", + "0xb26cd2501ee72718b0eebab6fb24d955a71f363f36e0f6dff0ab1d2d7836dab88474c0cef43a2cc32701fca7e82f7df3", + "0xa1dc3b6c968f3de00f11275092290afab65b2200afbcfa8ddc70e751fa19dbbc300445d6d479a81bda3880729007e496", + "0xa9bc213e28b630889476a095947d323b9ac6461dea726f2dc9084473ae8e196d66fb792a21905ad4ec52a6d757863e7d", + "0xb25d178df8c2df8051e7c888e9fa677fde5922e602a95e966db9e4a3d6b23ce043d7dc48a5b375c6b7c78e966893e8c3", + "0xa1c8d88d72303692eaa7adf68ea41de4febec40cc14ae551bb4012afd786d7b6444a3196b5d9d5040655a3366d96b7cd", + "0xb22bd44f9235a47118a9bbe2ba5a2ba9ec62476061be2e8e57806c1a17a02f9a51403e849e2e589520b759abd0117683", + "0xb8add766050c0d69fe81d8d9ea73e1ed05f0135d093ff01debd7247e42dbb86ad950aceb3b50b9af6cdc14ab443b238f", + "0xaf2cf95f30ef478f018cf81d70d47d742120b09193d8bb77f0d41a5d2e1a80bfb467793d9e2471b4e0ad0cb2c3b42271", + "0x8af5ef2107ad284e246bb56e20fef2a255954f72de791cbdfd3be09f825298d8466064f3c98a50496c7277af32b5c0bc", + "0x85dc19558572844c2849e729395a0c125096476388bd1b14fa7f54a7c38008fc93e578da3aac6a52ff1504d6ca82db05", + "0xae8c9b43c49572e2e166d704caf5b4b621a3b47827bb2a3bcd71cdc599bba90396fd9a405261b13e831bb5d44c0827d7", + "0xa7ba7efede25f02e88f6f4cbf70643e76784a03d97e0fbd5d9437c2485283ad7ca3abb638a5f826cd9f6193e5dec0b6c", + "0x94a9d122f2f06ef709fd8016fd4b712d88052245a65a301f5f177ce22992f74ad05552b1f1af4e70d1eac62cef309752", + "0x82d999b3e7cf563833b8bc028ff63a6b26eb357dfdb3fd5f10e33a1f80a9b2cfa7814d871b32a7ebfbaa09e753e37c02", + "0xaec6edcde234df502a3268dd2c26f4a36a2e0db730afa83173f9c78fcb2b2f75510a02b80194327b792811caefda2725", + "0x94c0bfa66c9f91d462e9194144fdd12d96f9bbe745737e73bab8130607ee6ea9d740e2cfcbbd00a195746edb6369ee61", + "0xab7573dab8c9d46d339e3f491cb2826cabe8b49f85f1ede78d845fc3995537d1b4ab85140b7d0238d9c24daf0e5e2a7e", + "0x87e8b16832843251fe952dadfd01d41890ed4bb4b8fa0254550d92c8cced44368225eca83a6c3ad47a7f81ff8a80c984", + "0x9189d2d9a7c64791b19c0773ad4f0564ce6bea94aa275a917f78ad987f150fdb3e5e26e7fef9982ac184897ecc04683f", + "0xb3661bf19e2da41415396ae4dd051a9272e8a2580b06f1a1118f57b901fa237616a9f8075af1129af4eabfefedbe2f1c", + "0xaf43c86661fb15daf5d910a4e06837225e100fb5680bd3e4b10f79a2144c6ec48b1f8d6e6b98e067d36609a5d038889a", + "0x82ac0c7acaa83ddc86c5b4249aae12f28155989c7c6b91e5137a4ce05113c6cbc16f6c44948b0efd8665362d3162f16a", + "0x8f268d1195ab465beeeb112cd7ffd5d5548559a8bc01261106d3555533fc1971081b25558d884d552df0db1cddda89d8", + "0x8ef7caa5521f3e037586ce8ac872a4182ee20c7921c0065ed9986c047e3dda08294da1165f385d008b40d500f07d895f", + "0x8c2f98f6880550573fad46075d3eba26634b5b025ce25a0b4d6e0193352c8a1f0661064027a70fe8190b522405f9f4e3", + "0xb7653f353564feb164f0f89ec7949da475b8dad4a4d396d252fc2a884f6932d027b7eb2dc4d280702c74569319ed701a", + "0xa026904f4066333befd9b87a8fad791d014096af60cdd668ef919c24dbe295ff31f7a790e1e721ba40cf5105abca67f4", + "0x988f982004ada07a22dd345f2412a228d7a96b9cae2c487de42e392afe1e35c2655f829ce07a14629148ce7079a1f142", + "0x9616add009067ed135295fb74d5b223b006b312bf14663e547a0d306694ff3a8a7bb9cfc466986707192a26c0bce599f", + "0xad4c425de9855f6968a17ee9ae5b15e0a5b596411388cf976df62ecc6c847a6e2ddb2cea792a5f6e9113c2445dba3e5c", + "0xb698ac9d86afa3dc69ff8375061f88e3b0cff92ff6dfe747cebaf142e813c011851e7a2830c10993b715e7fd594604a9", + "0xa386fa189847bb3b798efca917461e38ead61a08b101948def0f82cd258b945ed4d45b53774b400af500670149e601b7", + "0x905c95abda2c68a6559d8a39b6db081c68cef1e1b4be63498004e1b2f408409be9350b5b5d86a30fd443e2b3e445640a", + "0x9116dade969e7ce8954afcdd43e5cab64dc15f6c1b8da9d2d69de3f02ba79e6c4f6c7f54d6bf586d30256ae405cd1e41", + "0xa3084d173eacd08c9b5084a196719b57e47a0179826fda73466758235d7ecdb87cbcf097bd6b510517d163a85a7c7edd", + "0x85bb00415ad3c9be99ff9ba83672cc59fdd24356b661ab93713a3c8eab34e125d8867f628a3c3891b8dc056e69cd0e83", + "0x8d58541f9f39ed2ee4478acce5d58d124031338ec11b0d55551f00a5a9a6351faa903a5d7c132dc5e4bb026e9cbd18e4", + "0xa622adf72dc250e54f672e14e128c700166168dbe0474cecb340da175346e89917c400677b1bc1c11fcc4cc26591d9db", + "0xb3f865014754b688ca8372e8448114fff87bf3ca99856ab9168894d0c4679782c1ced703f5b74e851b370630f5e6ee86", + "0xa7e490b2c40c2446fcd91861c020da9742c326a81180e38110558bb5d9f2341f1c1885e79b364e6419023d1cbdc47380", + "0xb3748d472b1062e54572badbb8e87ac36534407f74932e7fc5b8392d008e8e89758f1671d1e4d30ab0fa40551b13bb5e", + "0x89898a5c5ec4313aabc607b0049fd1ebad0e0c074920cf503c9275b564d91916c2c446d3096491c950b7af3ac5e4b0ed", + "0x8eb8c83fef2c9dd30ea44e286e9599ec5c20aba983f702e5438afe2e5b921884327ad8d1566c72395587efac79ca7d56", + "0xb92479599e806516ce21fb0bd422a1d1d925335ebe2b4a0a7e044dd275f30985a72b97292477053ac5f00e081430da80", + "0xa34ae450a324fe8a3c25a4d653a654f9580ed56bbea213b8096987bbad0f5701d809a17076435e18017fea4d69f414bc", + "0x81381afe6433d62faf62ea488f39675e0091835892ecc238e02acf1662669c6d3962a71a3db652f6fe3bc5f42a0e5dc5", + "0xa430d475bf8580c59111103316fe1aa79c523ea12f1d47a976bbfae76894717c20220e31cf259f08e84a693da6688d70", + "0xb842814c359754ece614deb7d184d679d05d16f18a14b288a401cef5dad2cf0d5ee90bad487b80923fc5573779d4e4e8", + "0x971d9a2627ff2a6d0dcf2af3d895dfbafca28b1c09610c466e4e2bff2746f8369de7f40d65b70aed135fe1d72564aa88", + "0x8f4ce1c59e22b1ce7a0664caaa7e53735b154cfba8d2c5cc4159f2385843de82ab58ed901be876c6f7fce69cb4130950", + "0x86cc9dc321b6264297987000d344fa297ef45bcc2a4df04e458fe2d907ad304c0ea2318e32c3179af639a9a56f3263cf", + "0x8229e0876dfe8f665c3fb19b250bd89d40f039bbf1b331468b403655be7be2e104c2fd07b9983580c742d5462ca39a43", + "0x99299d73066e8eb128f698e56a9f8506dfe4bd014931e86b6b487d6195d2198c6c5bf15cccb40ccf1f8ddb57e9da44a2", + "0xa3a3be37ac554c574b393b2f33d0a32a116c1a7cfeaf88c54299a4da2267149a5ecca71f94e6c0ef6e2f472b802f5189", + "0xa91700d1a00387502cdba98c90f75fbc4066fefe7cc221c8f0e660994c936badd7d2695893fde2260c8c11d5bdcdd951", + "0x8e03cae725b7f9562c5c5ab6361644b976a68bada3d7ca508abca8dfc80a469975689af1fba1abcf21bc2a190dab397d", + "0xb01461ad23b2a8fa8a6d241e1675855d23bc977dbf4714add8c4b4b7469ccf2375cec20e80cedfe49361d1a30414ac5b", + "0xa2673bf9bc621e3892c3d7dd4f1a9497f369add8cbaa3472409f4f86bd21ac67cfac357604828adfee6ada1835365029", + "0xa042dff4bf0dfc33c178ba1b335e798e6308915128de91b12e5dbbab7c4ac8d60a01f6aea028c3a6d87b9b01e4e74c01", + "0x86339e8a75293e4b3ae66b5630d375736b6e6b6b05c5cda5e73fbf7b2f2bd34c18a1d6cefede08625ce3046e77905cb8", + "0xaf2ebe1b7d073d03e3d98bc61af83bf26f7a8c130fd607aa92b75db22d14d016481b8aa231e2c9757695f55b7224a27f", + "0xa00ee882c9685e978041fd74a2c465f06e2a42ffd3db659053519925be5b454d6f401e3c12c746e49d910e4c5c9c5e8c", + "0x978a781c0e4e264e0dad57e438f1097d447d891a1e2aa0d5928f79a9d5c3faae6f258bc94fdc530b7b2fa6a9932bb193", + "0xaa4b7ce2e0c2c9e9655bf21e3e5651c8503bce27483017b0bf476be743ba06db10228b3a4c721219c0779747f11ca282", + "0xb003d1c459dacbcf1a715551311e45d7dbca83a185a65748ac74d1800bbeaba37765d9f5a1a221805c571910b34ebca8", + "0x95b6e531b38648049f0d19de09b881baa1f7ea3b2130816b006ad5703901a05da57467d1a3d9d2e7c73fb3f2e409363c", + "0xa6cf9c06593432d8eba23a4f131bb7f72b9bd51ab6b4b772a749fe03ed72b5ced835a349c6d9920dba2a39669cb7c684", + "0xaa3d59f6e2e96fbb66195bc58c8704e139fa76cd15e4d61035470bd6e305db9f98bcbf61ac1b95e95b69ba330454c1b3", + "0xb57f97959c208361de6d7e86dff2b873068adb0f158066e646f42ae90e650079798f165b5cd713141cd3a2a90a961d9a", + "0xa76ee8ed9052f6a7a8c69774bb2597be182942f08115baba03bf8faaeaee526feba86120039fe8ca7b9354c3b6e0a8e6", + "0x95689d78c867724823f564627d22d25010f278674c6d2d0cdb10329169a47580818995d1d727ce46c38a1e47943ebb89", + "0xab676d2256c6288a88e044b3d9ffd43eb9d5aaee00e8fc60ac921395fb835044c71a26ca948e557fed770f52d711e057", + "0x96351c72785c32e5d004b6f4a1259fb8153d631f0c93fed172f18e8ba438fbc5585c1618deeabd0d6d0b82173c2e6170", + "0x93dd8d3db576418e22536eba45ab7f56967c6c97c64260d6cddf38fb19c88f2ec5cd0e0156f50e70855eee8a2b879ffd", + "0xad6ff16f40f6de3d7a737f8e6cebd8416920c4ff89dbdcd75eabab414af9a6087f83ceb9aff7680aa86bff98bd09c8cc", + "0x84de53b11671abc9c38710e19540c5c403817562aeb22a88404cdaff792c1180f717dbdfe8f54940c062c4d032897429", + "0x872231b9efa1cdd447b312099a5c164c560440a9441d904e70f5abfc3b2a0d16be9a01aca5e0a2599a61e19407587e3d", + "0x88f44ac27094a2aa14e9dc40b099ee6d68f97385950f303969d889ee93d4635e34dff9239103bdf66a4b7cbba3e7eb7a", + "0xa59afebadf0260e832f6f44468443562f53fbaf7bcb5e46e1462d3f328ac437ce56edbca617659ac9883f9e13261fad7", + "0xb1990e42743a88de4deeacfd55fafeab3bc380cb95de43ed623d021a4f2353530bcab9594389c1844b1c5ea6634c4555", + "0x85051e841149a10e83f56764e042182208591396d0ce78c762c4a413e6836906df67f38c69793e158d64fef111407ba3", + "0x9778172bbd9b1f2ec6bbdd61829d7b39a7df494a818e31c654bf7f6a30139899c4822c1bf418dd4f923243067759ce63", + "0x9355005b4878c87804fc966e7d24f3e4b02bed35b4a77369d01f25a3dcbff7621b08306b1ac85b76fe7b4a3eb5f839b1", + "0x8f9dc6a54fac052e236f8f0e1f571ac4b5308a43acbe4cc8183bce26262ddaf7994e41cf3034a4cbeca2c505a151e3b1", + "0x8cc59c17307111723fe313046a09e0e32ea0cce62c13814ab7c6408c142d6a0311d801be4af53fc9240523f12045f9ef", + "0x8e6057975ed40a1932e47dd3ac778f72ee2a868d8540271301b1aa6858de1a5450f596466494a3e0488be4fbeb41c840", + "0x812145efbd6559ae13325d56a15940ca4253b17e72a9728986b563bb5acc13ec86453796506ac1a8f12bd6f9e4a288c3", + "0x911da0a6d6489eb3dab2ec4a16e36127e8a291ae68a6c2c9de33e97f3a9b1f00da57a94e270a0de79ecc5ecb45d19e83", + "0xb72ea85973f4b2a7e6e71962b0502024e979a73c18a9111130e158541fa47bbaaf53940c8f846913a517dc69982ba9e1", + "0xa7a56ad1dbdc55f177a7ad1d0af78447dc2673291e34e8ab74b26e2e2e7d8c5fe5dc89e7ef60f04a9508847b5b3a8188", + "0xb52503f6e5411db5d1e70f5fb72ccd6463fa0f197b3e51ca79c7b5a8ab2e894f0030476ada72534fa4eb4e06c3880f90", + "0xb51c7957a3d18c4e38f6358f2237b3904618d58b1de5dec53387d25a63772e675a5b714ad35a38185409931157d4b529", + "0xb86b4266e719d29c043d7ec091547aa6f65bbf2d8d831d1515957c5c06513b72aa82113e9645ad38a7bc3f5383504fa6", + "0xb95b547357e6601667b0f5f61f261800a44c2879cf94e879def6a105b1ad2bbf1795c3b98a90d588388e81789bd02681", + "0xa58fd4c5ae4673fa350da6777e13313d5d37ed1dafeeb8f4f171549765b84c895875d9d3ae6a9741f3d51006ef81d962", + "0x9398dc348d078a604aadc154e6eef2c0be1a93bb93ba7fe8976edc2840a3a318941338cc4d5f743310e539d9b46613d2", + "0x902c9f0095014c4a2f0dccaaab543debba6f4cc82c345a10aaf4e72511725dbed7a34cd393a5f4e48a3e5142b7be84ed", + "0xa7c0447849bb44d04a0393a680f6cd390093484a79a147dd238f5d878030d1c26646d88211108e59fe08b58ad20c6fbd", + "0x80db045535d6e67a422519f5c89699e37098449d249698a7cc173a26ccd06f60238ae6cc7242eb780a340705c906790c", + "0x8e52b451a299f30124505de2e74d5341e1b5597bdd13301cc39b05536c96e4380e7f1b5c7ef076f5b3005a868657f17c", + "0x824499e89701036037571761e977654d2760b8ce21f184f2879fda55d3cda1e7a95306b8abacf1caa79d3cc075b9d27f", + "0x9049b956b77f8453d2070607610b79db795588c0cec12943a0f5fe76f358dea81e4f57a4692112afda0e2c05c142b26f", + "0x81911647d818a4b5f4990bfd4bc13bf7be7b0059afcf1b6839333e8569cdb0172fd2945410d88879349f677abaed5eb3", + "0xad4048f19b8194ed45b6317d9492b71a89a66928353072659f5ce6c816d8f21e69b9d1817d793effe49ca1874daa1096", + "0x8d22f7b2ddb31458661abd34b65819a374a1f68c01fc6c9887edeba8b80c65bceadb8f57a3eb686374004b836261ef67", + "0x92637280c259bc6842884db3d6e32602a62252811ae9b019b3c1df664e8809ffe86db88cfdeb8af9f46435c9ee790267", + "0xa2f416379e52e3f5edc21641ea73dc76c99f7e29ea75b487e18bd233856f4c0183429f378d2bfc6cd736d29d6cadfa49", + "0x882cb6b76dbdc188615dcf1a8439eba05ffca637dd25197508156e03c930b17b9fed2938506fdd7b77567cb488f96222", + "0xb68b621bb198a763fb0634eddb93ed4b5156e59b96c88ca2246fd1aea3e6b77ed651e112ac41b30cd361fadc011d385e", + "0xa3cb22f6b675a29b2d1f827cacd30df14d463c93c3502ef965166f20d046af7f9ab7b2586a9c64f4eae4fad2d808a164", + "0x8302d9ce4403f48ca217079762ce42cee8bc30168686bb8d3a945fbd5acd53b39f028dce757b825eb63af2d5ae41169d", + "0xb2eef1fbd1a176f1f4cd10f2988c7329abe4eb16c7405099fb92baa724ab397bc98734ef7d4b24c0f53dd90f57520d04", + "0xa1bbef0bd684a3f0364a66bde9b29326bac7aa3dde4caed67f14fb84fed3de45c55e406702f1495a3e2864d4ee975030", + "0x976acdb0efb73e3a3b65633197692dedc2adaed674291ae3df76b827fc866d214e9cac9ca46baefc4405ff13f953d936", + "0xb9fbf71cc7b6690f601f0b1c74a19b7d14254183a2daaafec7dc3830cba5ae173d854bbfebeca985d1d908abe5ef0cda", + "0x90591d7b483598c94e38969c4dbb92710a1a894bcf147807f1bcbd8aa3ac210b9f2be65519aa829f8e1ccdc83ad9b8cf", + "0xa30568577c91866b9c40f0719d46b7b3b2e0b4a95e56196ac80898a2d89cc67880e1229933f2cd28ee3286f8d03414d7", + "0x97589a88c3850556b359ec5e891f0937f922a751ac7c95949d3bbc7058c172c387611c0f4cb06351ef02e5178b3dd9e4", + "0x98e7bbe27a1711f4545df742f17e3233fbcc63659d7419e1ca633f104cb02a32c84f2fac23ca2b84145c2672f68077ab", + "0xa7ddb91636e4506d8b7e92aa9f4720491bb71a72dadc47c7f4410e15f93e43d07d2b371951a0e6a18d1bd087aa96a5c4", + "0xa7c006692227a06db40bceac3d5b1daae60b5692dd9b54772bedb5fea0bcc91cbcdb530cac31900ffc70c5b3ffadc969", + "0x8d3ec6032778420dfa8be52066ba0e623467df33e4e1901dbadd586c5d750f4ccde499b5197e26b9ea43931214060f69", + "0x8d9a8410518ea64f89df319bfd1fc97a0971cdb9ad9b11d1f8fe834042ea7f8dce4db56eeaf179ff8dda93b6db93e5ce", + "0xa3c533e9b3aa04df20b9ff635cb1154ce303e045278fcf3f10f609064a5445552a1f93989c52ce852fd0bbd6e2b6c22e", + "0x81934f3a7f8c1ae60ec6e4f212986bcc316118c760a74155d06ce0a8c00a9b9669ec4e143ca214e1b995e41271774fd9", + "0xab8e2d01a71192093ef8fafa7485e795567cc9db95a93fb7cc4cf63a391ef89af5e2bfad4b827fffe02b89271300407f", + "0x83064a1eaa937a84e392226f1a60b7cfad4efaa802f66de5df7498962f7b2649924f63cd9962d47906380b97b9fe80e1", + "0xb4f5e64a15c6672e4b55417ee5dc292dcf93d7ea99965a888b1cc4f5474a11e5b6520eacbcf066840b343f4ceeb6bf33", + "0xa63d278b842456ef15c278b37a6ea0f27c7b3ffffefca77c7a66d2ea06c33c4631eb242bbb064d730e70a8262a7b848a", + "0x83a41a83dbcdf0d22dc049de082296204e848c453c5ab1ba75aa4067984e053acf6f8b6909a2e1f0009ed051a828a73b", + "0x819485b036b7958508f15f3c19436da069cbe635b0318ebe8c014cf1ef9ab2df038c81161b7027475bcfa6fff8dd9faf", + "0xaa40e38172806e1e045e167f3d1677ef12d5dcdc89b43639a170f68054bd196c4fae34c675c1644d198907a03f76ba57", + "0x969bae484883a9ed1fbed53b26b3d4ee4b0e39a6c93ece5b3a49daa01444a1c25727dabe62518546f36b047b311b177c", + "0x80a9e73a65da99664988b238096a090d313a0ee8e4235bc102fa79bb337b51bb08c4507814eb5baec22103ec512eaab0", + "0x86604379aec5bddda6cbe3ef99c0ac3a3c285b0b1a15b50451c7242cd42ae6b6c8acb717dcca7917838432df93a28502", + "0xa23407ee02a495bed06aa7e15f94cfb05c83e6d6fba64456a9bbabfa76b2b68c5c47de00ba169e710681f6a29bb41a22", + "0x98cff5ecc73b366c6a01b34ac9066cb34f7eeaf4f38a5429bad2d07e84a237047e2a065c7e8a0a6581017dadb4695deb", + "0x8de9f68a938f441f3b7ab84bb1f473c5f9e5c9e139e42b7ccee1d254bd57d0e99c2ccda0f3198f1fc5737f6023dd204e", + "0xb0ce48d815c2768fb472a315cad86aa033d0e9ca506f146656e2941829e0acb735590b4fbc713c2d18d3676db0a954ac", + "0x82f485cdefd5642a6af58ac6817991c49fac9c10ace60f90b27f1788cc026c2fe8afc83cf499b3444118f9f0103598a8", + "0x82c24550ed512a0d53fc56f64cc36b553823ae8766d75d772dacf038c460f16f108f87a39ceef7c66389790f799dbab3", + "0x859ffcf1fe9166388316149b9acc35694c0ea534d43f09dae9b86f4aa00a23b27144dda6a352e74b9516e8c8d6fc809c", + "0xb8f7f353eec45da77fb27742405e5ad08d95ec0f5b6842025be9def3d9892f85eb5dd0921b41e6eff373618dba215bca", + "0x8ccca4436f9017e426229290f5cd05eac3f16571a4713141a7461acfe8ae99cd5a95bf5b6df129148693c533966145da", + "0xa2c67ecc19c0178b2994846fea4c34c327a5d786ac4b09d1d13549d5be5996d8a89021d63d65cb814923388f47cc3a03", + "0xaa0ff87d676b418ec08f5cbf577ac7e744d1d0e9ebd14615b550eb86931eafd2a36d4732cc5d6fab1713fd7ab2f6f7c0", + "0x8aef4730bb65e44efd6bb9441c0ae897363a2f3054867590a2c2ecf4f0224e578c7a67f10b40f8453d9f492ac15a9b2d", + "0x86a187e13d8fba5addcfdd5b0410cedd352016c930f913addd769ee09faa6be5ca3e4b1bdb417a965c643a99bd92be42", + "0xa0a4e9632a7a094b14b29b78cd9c894218cdf6783e61671e0203865dc2a835350f465fbaf86168f28af7c478ca17bc89", + "0xa8c7b02d8deff2cd657d8447689a9c5e2cd74ef57c1314ac4d69084ac24a7471954d9ff43fe0907d875dcb65fd0d3ce5", + "0x97ded38760aa7be6b6960b5b50e83b618fe413cbf2bcc1da64c05140bcc32f5e0e709cd05bf8007949953fac5716bad9", + "0xb0d293835a24d64c2ae48ce26e550b71a8c94a0883103757fb6b07e30747f1a871707d23389ba2b2065fa6bafe220095", + "0x8f9e291bf849feaa575592e28e3c8d4b7283f733d41827262367ea1c40f298c7bcc16505255a906b62bf15d9f1ba85fb", + "0x998f4e2d12708b4fd85a61597ca2eddd750f73c9e0c9b3cf0825d8f8e01f1628fd19797dcaed3b16dc50331fc6b8b821", + "0xb30d1f8c115d0e63bf48f595dd10908416774c78b3bbb3194192995154d80ea042d2e94d858de5f8aa0261b093c401fd", + "0xb5d9c75bb41f964cbff3f00e96d9f1480c91df8913f139f0d385d27a19f57a820f838eb728e46823cbff00e21c660996", + "0xa6edec90b5d25350e2f5f0518777634f9e661ec9d30674cf5b156c4801746d62517751d90074830ac0f4b09911c262f1", + "0x82f98da1264b6b75b8fbeb6a4d96d6a05b25c24db0d57ba3a38efe3a82d0d4e331b9fc4237d6494ccfe4727206457519", + "0xb89511843453cf4ecd24669572d6371b1e529c8e284300c43e0d5bb6b3aaf35aeb634b3cb5c0a2868f0d5e959c1d0772", + "0xa82bf065676583e5c1d3b81987aaae5542f522ba39538263a944bb33ea5b514c649344a96c0205a3b197a3f930fcda6c", + "0xa37b47ea527b7e06c460776aa662d9a49ff4149d3993f1a974b0dd165f7171770d189b0e2ea54fd5fccb6a14b116e68a", + "0xa1017677f97dda818274d47556d09d0e4ccacb23a252f82a6cfe78c630ad46fb9806307445a59fb61262182de3a2b29c", + "0xb01e9fcac239ba270e6877b79273ddd768bf8a51d2ed8a051b1c11e18eff3de5920e2fcbfbd26f06d381eddd3b1f1e1b", + "0x82fcd53d803b1c8e4ed76adc339b7f3a5962d37042b9683aabac7513ac68775d4a566a9460183926a6a95dbe7d551a1f", + "0xa763e78995d55cd21cdb7ef75d9642d6e1c72453945e346ab6690c20a4e1eeec61bb848ef830ae4b56182535e3c71d8f", + "0xb769f4db602251d4b0a1186782799bdcef66de33c110999a5775c50b349666ffd83d4c89714c4e376f2efe021a5cfdb2", + "0xa59cbd1b785efcfa6e83fc3b1d8cf638820bc0c119726b5368f3fba9dce8e3414204fb1f1a88f6c1ff52e87961252f97", + "0x95c8c458fd01aa23ecf120481a9c6332ebec2e8bb70a308d0576926a858457021c277958cf79017ddd86a56cacc2d7db", + "0x82eb41390800287ae56e77f2e87709de5b871c8bdb67c10a80fc65f3acb9f7c29e8fa43047436e8933f27449ea61d94d", + "0xb3ec25e3545eb83aed2a1f3558d1a31c7edde4be145ecc13b33802654b77dc049b4f0065069dd9047b051e52ab11dcdd", + "0xb78a0c715738f56f0dc459ab99e252e3b579b208142836b3c416b704ca1de640ca082f29ebbcee648c8c127df06f6b1e", + "0xa4083149432eaaf9520188ebf4607d09cf664acd1f471d4fb654476e77a9eaae2251424ffda78d09b6cb880df35c1219", + "0x8c52857d68d6e9672df3db2df2dbf46b516a21a0e8a18eec09a6ae13c1ef8f369d03233320dd1c2c0bbe00abfc1ea18b", + "0x8c856089488803066bff3f8d8e09afb9baf20cecc33c8823c1c0836c3d45498c3de37e87c016b705207f60d2b00f8609", + "0x831a3df39be959047b2aead06b4dcd3012d7b29417f642b83c9e8ce8de24a3dbbd29c6fdf55e2db3f7ea04636c94e403", + "0xaed84d009f66544addabe404bf6d65af7779ce140dc561ff0c86a4078557b96b2053b7b8a43432ffb18cd814f143b9da", + "0x93282e4d72b0aa85212a77b336007d8ba071eea17492da19860f1ad16c1ea8867ccc27ef5c37c74b052465cc11ea4f52", + "0xa7b78b8c8d057194e8d68767f1488363f77c77bddd56c3da2bc70b6354c7aa76247c86d51f7371aa38a4aa7f7e3c0bb7", + "0xb1c77283d01dcd1bde649b5b044eac26befc98ff57cbee379fb5b8e420134a88f2fc7f0bf04d15e1fbd45d29e7590fe6", + "0xa4aa8de70330a73b2c6458f20a1067eed4b3474829b36970a8df125d53bbdda4f4a2c60063b7cccb0c80fc155527652f", + "0x948a6c79ba1b8ad7e0bed2fae2f0481c4e41b4d9bbdd9b58164e28e9065700e83f210c8d5351d0212e0b0b68b345b3a5", + "0x86a48c31dcbbf7b082c92d28e1f613a2378a910677d7db3a349dc089e4a1e24b12eee8e8206777a3a8c64748840b7387", + "0x976adb1af21e0fc34148917cf43d933d7bfd3fd12ed6c37039dcd5a4520e3c6cf5868539ba5bf082326430deb8a4458d", + "0xb93e1a4476f2c51864bb4037e7145f0635eb2827ab91732b98d49b6c07f6ac443111aa1f1da76d1888665cb897c3834e", + "0x8afd46fb23bf869999fa19784b18a432a1f252d09506b8dbb756af900518d3f5f244989b3d7c823d9029218c655d3dc6", + "0x83f1e59e3abeed18cdc632921672673f1cb6e330326e11c4e600e13e0d5bc11bdc970ae12952e15103a706fe720bf4d6", + "0x90ce4cc660714b0b673d48010641c09c00fc92a2c596208f65c46073d7f349dd8e6e077ba7dcef9403084971c3295b76", + "0x8b09b0f431a7c796561ecf1549b85048564de428dac0474522e9558b6065fede231886bc108539c104ce88ebd9b5d1b0", + "0x85d6e742e2fb16a7b0ba0df64bc2c0dbff9549be691f46a6669bca05e89c884af16822b85faefefb604ec48c8705a309", + "0xa87989ee231e468a712c66513746fcf03c14f103aadca0eac28e9732487deb56d7532e407953ab87a4bf8961588ef7b0", + "0xb00da10efe1c29ee03c9d37d5918e391ae30e48304e294696b81b434f65cf8c8b95b9d1758c64c25e534d045ba28696f", + "0x91c0e1fb49afe46c7056400baa06dbb5f6e479db78ee37e2d76c1f4e88994357e257b83b78624c4ef6091a6c0eb8254d", + "0x883fb797c498297ccbf9411a3e727c3614af4eccde41619b773dc7f3259950835ee79453debf178e11dec4d3ada687a0", + "0xa14703347e44eb5059070b2759297fcfcfc60e6893c0373eea069388eba3950aa06f1c57cd2c30984a2d6f9e9c92c79e", + "0xafebc7585b304ceba9a769634adff35940e89cd32682c78002822aab25eec3edc29342b7f5a42a56a1fec67821172ad5", + "0xaea3ff3822d09dba1425084ca95fd359718d856f6c133c5fabe2b2eed8303b6e0ba0d8698b48b93136a673baac174fd9", + "0xaf2456a09aa777d9e67aa6c7c49a1845ea5cdda2e39f4c935c34a5f8280d69d4eec570446998cbbe31ede69a91e90b06", + "0x82cada19fed16b891ef3442bafd49e1f07c00c2f57b2492dd4ee36af2bd6fd877d6cb41188a4d6ce9ec8d48e8133d697", + "0x82a21034c832287f616619a37c122cee265cc34ae75e881fcaea4ea7f689f3c2bc8150bbf7dbcfd123522bfb7f7b1d68", + "0x86877217105f5d0ec3eeff0289fc2a70d505c9fdf7862e8159553ef60908fb1a27bdaf899381356a4ef4649072a9796c", + "0x82b196e49c6e861089a427c0b4671d464e9d15555ffb90954cd0d630d7ae02eb3d98ceb529d00719c2526cd96481355a", + "0xa29b41d0d43d26ce76d4358e0db2b77df11f56e389f3b084d8af70a636218bd3ac86b36a9fe46ec9058c26a490f887f7", + "0xa4311c4c20c4d7dd943765099c50f2fd423e203ccfe98ff00087d205467a7873762510cac5fdce7a308913ed07991ed7", + "0xb1f040fc5cc51550cb2c25cf1fd418ecdd961635a11f365515f0cb4ffb31da71f48128c233e9cc7c0cf3978d757ec84e", + "0xa9ebae46f86d3bd543c5f207ed0d1aed94b8375dc991161d7a271f01592912072e083e2daf30c146430894e37325a1b9", + "0x826418c8e17ad902b5fe88736323a47e0ca7a44bce4cbe27846ec8fe81de1e8942455dda6d30e192cdcc73e11df31256", + "0x85199db563427c5edcbac21f3d39fec2357be91fb571982ddcdc4646b446ad5ced84410de008cb47b3477ee0d532daf8", + "0xb7eed9cd400b2ca12bf1d9ae008214b8561fb09c8ad9ff959e626ffde00fee5ff2f5b6612e231f2a1a9b1646fcc575e3", + "0x8b40bf12501dcbac78f5a314941326bfcddf7907c83d8d887d0bb149207f85d80cd4dfbd7935439ea7b14ea39a3fded7", + "0x83e3041af302485399ba6cd5120e17af61043977083887e8d26b15feec4a6b11171ac5c06e6ad0971d4b58a81ff12af3", + "0x8f5b9a0eecc589dbf8c35a65d5e996a659277ef6ea509739c0cb7b3e2da9895e8c8012de662e5b23c5fa85d4a8f48904", + "0x835d71ed5e919d89d8e6455f234f3ff215462c4e3720c371ac8c75e83b19dfe3ae15a81547e4dc1138e5f5997f413cc9", + "0x8b7d2e4614716b1db18e9370176ea483e6abe8acdcc3dcdf5fb1f4d22ca55d652feebdccc171c6de38398d9f7bfdec7a", + "0x93eace72036fe57d019676a02acf3d224cf376f166658c1bf705db4f24295881d477d6fdd7916efcfceff8c7a063deda", + "0xb1ac460b3d516879a84bc886c54f020a9d799e7c49af3e4d7de5bf0d2793c852254c5d8fe5616147e6659512e5ccb012", + "0xacd0947a35cb167a48bcd9667620464b54ac0e78f9316b4aa92dcaab5422d7a732087e52e1c827faa847c6b2fe6e7766", + "0x94ac33d21c3d12ff762d32557860e911cd94d666609ddcc42161b9c16f28d24a526e8b10bb03137257a92cec25ae637d", + "0x832e02058b6b994eadd8702921486241f9a19e68ed1406dad545e000a491ae510f525ccf9d10a4bba91c68f2c53a0f58", + "0x9471035d14f78ff8f463b9901dd476b587bb07225c351161915c2e9c6114c3c78a501379ab6fb4eb03194c457cbd22bf", + "0xab64593e034c6241d357fcbc32d8ea5593445a5e7c24cac81ad12bd2ef01843d477a36dc1ba21dbe63b440750d72096a", + "0x9850f3b30045e927ad3ec4123a32ed2eb4c911f572b6abb79121873f91016f0d80268de8b12e2093a4904f6e6cab7642", + "0x987212c36b4722fe2e54fa30c52b1e54474439f9f35ca6ad33c5130cd305b8b54b532dd80ffd2c274105f20ce6d79f6e", + "0x8b4d0c6abcb239b5ed47bef63bc17efe558a27462c8208fa652b056e9eae9665787cd1aee34fbb55beb045c8bfdb882b", + "0xa9f3483c6fee2fe41312d89dd4355d5b2193ac413258993805c5cbbf0a59221f879386d3e7a28e73014f10e65dd503d9", + "0xa2225da3119b9b7c83d514b9f3aeb9a6d9e32d9cbf9309cbb971fd53c4b2c001d10d880a8ad8a7c281b21d85ceca0b7c", + "0xa050be52e54e676c151f7a54453bbb707232f849beab4f3bf504b4d620f59ed214409d7c2bd3000f3ff13184ccda1c35", + "0xadbccf681e15b3edb6455a68d292b0a1d0f5a4cb135613f5e6db9943f02181341d5755875db6ee474e19ace1c0634a28", + "0x8b6eff675632a6fad0111ec72aacc61c7387380eb87933fd1d098856387d418bd38e77d897e65d6fe35951d0627c550b", + "0xaabe2328ddf90989b15e409b91ef055cb02757d34987849ae6d60bef2c902bf8251ed21ab30acf39e500d1d511e90845", + "0x92ba4eb1f796bc3d8b03515f65c045b66e2734c2da3fc507fdd9d6b5d1e19ab3893726816a32141db7a31099ca817d96", + "0x8a98b3cf353138a1810beb60e946183803ef1d39ac4ea92f5a1e03060d35a4774a6e52b14ead54f6794d5f4022b8685c", + "0x909f8a5c13ec4a59b649ed3bee9f5d13b21d7f3e2636fd2bb3413c0646573fdf9243d63083356f12f5147545339fcd55", + "0x9359d914d1267633141328ed0790d81c695fea3ddd2d406c0df3d81d0c64931cf316fe4d92f4353c99ff63e2aefc4e34", + "0xb88302031681b54415fe8fbfa161c032ea345c6af63d2fb8ad97615103fd4d4281c5a9cae5b0794c4657b97571a81d3b", + "0x992c80192a519038082446b1fb947323005b275e25f2c14c33cc7269e0ec038581cc43705894f94bad62ae33a8b7f965", + "0xa78253e3e3eece124bef84a0a8807ce76573509f6861d0b6f70d0aa35a30a123a9da5e01e84969708c40b0669eb70aa6", + "0x8d5724de45270ca91c94792e8584e676547d7ac1ac816a6bb9982ee854eb5df071d20545cdfd3771cd40f90e5ba04c8e", + "0x825a6f586726c68d45f00ad0f5a4436523317939a47713f78fd4fe81cd74236fdac1b04ecd97c2d0267d6f4981d7beb1" ], - "setup_G2": [ + "g2_monomial": [ "0x93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8", - "0x99aca9fb2f7760cecb892bf7262c176b334824f5727f680bba701a33e322cb6667531410dfc7c8e4321a3f0ea8af48cb1436638a2093123f046f0f504cc2a864825542873edbbc5d7ed17af125a4f2cf6433c6f4f61b81173726981dd989761d", - "0x88e2e982982bf8231e747e9dfcd14c05bd02623d1332734d2af26246c6869fb56ee6c994843f593178a040495ba61f4a083b0e18110b1d9f5224783d8f9a895e8ee744e87929430e9ba96bd29251cbf61240b256d1525600f3d562894d93d659", - "0xa2d33775e3d9e6af0d1b27d389e6c021a578e617a3d6627686db6288d4b3dffd7a847a00f7ef01828b7f42885b660e4204923402aca18fbae74ccd4e9c50dd8c2281b38dc09c022342ed1ac695d53f7081cb21f05fdfc0a3508c04759196fcd3", - "0xaf565445d2ad54c83a75c40e8895f5ad7219a8c728bce9d58d7a83716e095432993ebbd3f6911c66415a6f920d1a4d171478509b54a114308a020b33bf4487a7a8d0aa76ae4676a9b54e765a680f562d3a4fcb2e92c58b14b49b5b2917cc258f", - "0x8aa99cfaf514cef4801599cadd780d222194ca1ad69a34779c2bcfda93e5dbeb931e13914421b5809a6c81f12cf7038b04a35257cc9e94c33761e68565b1274aa6a6f9d66477229747a66b308b138f92aa4326a3bf23df65a1fe33b3b289bfe1", - "0x99ba36d8b4f56bde026099278548b1afc0a987cbd7c9baa51fc8e6cbb8237a17636f1a44a385cec69b05a5802059956a11fe793cabb939c38800f9c239ca2518e898ade1ec2513c9ee492071a35aabd78182392a09123d28dbc233313c9120c4", - "0xa7dc40c36afccb30a2eaff250860b28b227c195cf05674704c567d77d6655c446ae835f8fc8667e71147ab02afcb2dad0babe60cbfa37d7c2cddc68d2dec54f28a4142f8353590a3902d5ddaa22066ab563dd1435dda83f276387b9767d69120", - "0x939e6cc97a8b88572852a5b7f25e4838556307f60aeafb5d2b6961edbcafd4b48cb6ac980ffbacf4be963f324ba81e3d12de4f1459d8c746d0762c66ae1b166027f7fbe641d9c48f3c7d97b06d956b0be51dcc9aab65f3e99e1388e63bdd79f9", - "0xb391e156541dfd4003d1697cdb7ec815b309807320574906b2e652ef0175828b356d215cd374b1b34d9f470b3fa0e643113e67b2273268f922f04f072cfb89008358185b25cd631f82911a3f20f90f75758ffb99bebb8076458ae1e9d1ae898c", - "0xb9ac9c84934cc2a85c876eff65577e1dfce1935cd6392c877dd881a7d2f5c3e9344f28c04f90c62a6db4237ca00f9e0d00cb5f63e3f060fc7303916e19273b6fe455f331cabbe2fe5a22d584484f0d4176120fec9819fbb0a01e6d38695acfcd", - "0x88209eb030c5d78734bf2c2a5c539653fd3c24b4c08e624f9ddc4a6550efbdc1054a56eb0c807595aad6de56fda326aa196d032a8b4b48d40140a2d77df3c7243eda6507936389a321a5811eb38e32ee433c788deeae1eb928b00940e2944bcc", - "0xa8632ddc9cf7cbc1e8b74a05b7d4a89618c64afe30367ca0c9550ae7d320bf4e51c5a69e1501a1d8bee4240d13d7835501aa39fdc401a74f4d5734e268a7ce29a1fcfdb0a8bc64e0dd4a9e8578d6985bc2bc6a3764ce7a3703f6fb2e52557a2b", - "0xa037ac67e8bb6f4193ac967e05d080a489f58ef8d3d30a89798246f3e4936121ee445b03e410a09e8ebc0db2e2477d110aad0ade99b0887f1eb016e750f42135866907f150bd6f4f99a8cb94281474166874808ebe03b118c5daab16dafdc38b", - "0xa50d9143116bffa3b237da8e1805327e81e9cd25e658289bd727d5f9e0020172cc8690dcfe31a240e5cbc48353b88c4908baa1dd7320165556e0aa633f62fcbe7870222d345a3bbcdb7ab6c07f0fd86be559964afabf56f0a8cbc0b4b91d477e", - "0xafa988ea6fa4f40c5ad07d2d580d29025ddf56d6ef1171a8b8de3464203f70b97d6f5ace72747345204b35150e06154d1477516a989ce8eea7871cc0d0de00a077c0fb23ad4837e409d0b885bf3f2dde11a30fa6273d662e68e09f461e52932f", - "0x97fa1a943ed8b81574304a3d03f4f15907f6e6e0cd36a66bd2ad2c75afafc70a61d3ff69b77ebe4dae9ca0fcedef80081062705e60bbb6ea0f1f398c84d2f8e4a3ac142ac66426c21ad5e9994ebbcc406af474c4aec5e32fadcb21875af7c9f1", - "0xb30a564614493886f14a5dd71c89457504f8c59a7ac01b665ed167e9a8f9ee5832198fd319ecd234196ee57031bdf3840bd5a923e203a1938bc795c704b5285389750e1fd10d7050061ba19db00a60a2c0384a7d661d7d48ebe6962272230859", - "0x84c8dea942cfae71cb02e705ec496d967425793ce8812e7ee53c2f23713abeaff566a658cd1c73dfd18187d16253a6ee0a623e82cd18e31cd1a1875d19c078835dc9292e141686150a88065226ada264740143e87c03a0f6c4da8c187438ebf4", - "0x8c3abae8aed60338f8c4ff80aab22f8a2ae56756a93566c906f490a97151d34a1c3318054e1c494c60cc53327ad86a2d02c6c76a406726ce4f88635bc32eff0db0b61762dc518b95fa8da82e87e4bf3de54f1d72180ef53ed7bc5413e6a9a510", - "0xa328230c92a6b1cef6a444bcb64edb992f71e3d7b93f0b6b8b408ba7c908db746d92ddb2c7588bab438ef3bc61be1c2f0dfc86ba2ff514b42b35c80f89b2e780f813ea1dfb977fbded2cd9b553b747fa952e227ebd8f071163d421fc337f04c9", - "0xb482cab423cd5f1c5df036070aade7aa016283d69619d664025c3feab866a0a5691d344b2ee2bedc5dedd1f9a73eae16003a3827c9e5bbe22ded32d848fba840ffad1141ad158f5c40bc8ae0d03781b9705d851a7f1391b096c576c0f4f2a6b0", - "0x919ee1df27fabcb21237a1b7b98f53d41d849e1b6a8f9e28c3fae2841c6b5a250e4041c737e6725476e5cd715e34d3880f58d80f61efaabc261bdc703e8750f48a923e9bf8980931b9fd9e40014c66c54b3e7c98241d76d1aa47af43313a65a1", - "0xac94830145dbe9a8f7e6e0fc1f5fb454502d22abcafdc2dd96c6933c604461fa83b2b37385f4bc454875a02a6d4157841250956783515d11c7456e7f11b745f12856d89f5feedaf6a61a483a6c33a21cd2ba0c18eb41a1a2e7fc33bb53e4c570", - "0xb209c699f1233735c5bb4bce848e4365fd76651ae2184d2279a90df0c2f69ffa2a24d84a9b9f274021072953c0d65e1a0202d490d6c37186af240114e445d87bff754b4824937e4f2c90a574061b1c4910fed88d90f698025a2a264e656cb8a4", - "0x93320dc0576b0d069de63c40e5582b4486d9adf5e69e77e3ebaf3da26976fe42147a65051501bc8383f99e7ba75479c70a6726c2cd08bf98c7481f1f819712292d833a879f21a1221a9610bc748fb5e911055122fdb4055cdc84e8bfe0f4df9b", - "0xa4380b240e998cdf668591f71a0c88ed143b0185a920787627ce65095f8223dc606fa5bce93377af100de92d663e675c0736d7f1973603a84a5c4162fb5e01c88c7493503ae1d7e9fbe8ece9b418397d68c21eeb88dae226e09875d372c646dd", - "0xaab48517d69135a16b36b685adfe9b2544a709135a21ba3e75981a2cba4ec81d1fe28ac0f72fde0c0001c15300ed6a810f58d3117bdd58d0149751d6508cf8a1a1ff7b63dd02d2730a9d6fe96c77c502fe8ed46d50a181ec4bb35e37dfbd6af4", - "0x8277265fe75ab89ce4ec65b33fb4084bec0a56d81faf2f7a9070d2ca3065678e03a790350eba56323a54e0285bc32fe8007d5259740fde226e16cbde8354eacd562294eb9b7f727ed72ffbdad86f467cf057c737b34b80a41deb92634ed866f5", - "0xaa40a24cb2ebe606d969392c03020070f044c95088d80f57f771b837c048342d2cd3474600d7660441090ffb8d2ffb7f0eddd67eb378e3e1477a6ba0bc38096d5d2d3355bc8b60f605f57f0c1899da591457440352381d2b38c0aa9acc7fe419", - "0x80815d10685808cb630820629bcd2fa9041c9b74433630c0b9c1b7f7e8edf1440b520217f76ec9a50c125cf4438aa66006a1928a9ed2321da7ea325c3d56b65462b72118ca2c99a0ea733aa11da9abbeda6cc71ffeed301ae70213a29e697dcd", - "0xac235d079f91b00b1fead7523da8f73a5409fa8970907af0c5d5e4c6a0996dccfcdb0d822d08c7fbc0c24799457d011d04312d20831825f23cf988141056a6814c8a1cac9efe37bdcbfa272aed24cd92810fea7c49b0d07683a5c53643872179", - "0xb8aa59534d75fa5ac1c2c3f963bf73899aff5210059dbde8a8635561c6249e5143affee3bd2fd57575213b52d9a73d5702525867a7dcbb1d0a49b98c2925556fc5463ff0209742046a24ab29e74257d6419401093cc4371944d811cc300b6a67", - "0x80bbfc5b816eea29a6d84e2217dee4d547306994d39e5592515e1b0807b67fe960d1d5addb0ff1a20c158bdb294c04bf093d28996121845a2c9268e2c9ac0f4067e889c6aaca62f8535d35b45036954bd069e3afa84f04721538c26003304c20", - "0xa535c17d0e151d0e03d42dd58ba8c715bee3fabca2890e0e016071d34184b6b34e770d2be29c8ec76b69bcc471d50f4d043c2c240e9b93a81cff7ee2724e02018dfd9b534e40be641fdb4884abcd83b76f517557ffba508f1ba2f56313f4de94", - "0xb237eb7465df0d325a3aa58269be2627e4978f9863f4f100ed4c303cb1f6549e606f2e3c9180824d8049191965c8dacd0a0c76cc56cb22cf1bcfdb39372c8aa29b4f7b34582b1719e6bd59c930d87d5ccd838743b585d6e229d5ed42337315c0", - "0x805c335a2a9d2de30809cf30808ef836d88e9453c510716f01696f14c72dd60505eca8f128970edc8e63a9aa1f8792ac0dd50dcc84fbf4cc8b32349c682a6a27bc7551c7aa273a94c1606d07710188d93579afe3be1781bded15a34ed6047922", - "0xb25dadf385ddd3c39bcb0a014d3d4f66127946b1aceae8809e3a03d66cc25e27142ca108316391f857fe82fdea4db2520cc73793b695eafbf3ade00ef7ec747b0457e49303f5e1a370f5263b436566fe24a0876e5fe088238c7be37a0718d65f", - "0xb0f753081cabe2c8fce73aba82ff67dbc9842598b3e7fa3ce2a1f534536f8ac63c532fe66552ac6b7adb28c73ed4c8a4184849be7c1756a4681ce29ebf5e1c3aa806b667ee6bd68f6397aba3215dc1caec6742f21d681e32cd1160d6a3b1d7ee", - "0xb798771eeb3d7a17c62ba5916cc034bba870da6b1ac14c2e1cae71af3ad4e0c0d1ff983f691e0e55289d5a33b131f2ec12430c9566dd71f4d8be9c79155357a5c30c5efcfd75bbe1bb6d5ada4d50604ea49ed838d3641f268ca6e25c9c4b6b72", - "0xb52554c017388b099804abbe565346591a086d9979e10140ddaccc0a3680e506db775d7cbeafde67563adf0f09f5c2420caf19629f4e8f03e6fe02e9416ecd5269989e482b90004a083967d1141387eb74865bac6bd17e7a6d5f58225e52d4b7", - "0xb520ff694520919023d44d53f98a7de2f78ff37b2d9193dcaa35556a6a0febf767781a4c961dce7c804bfdf81935f8f0082865253da52e79dfa1c5ff74d61495b2da76e167d46114709e877a7791a3a95e33a42f56b83f5f5afe271c67ae997c", - "0xb721401983440797a03d5b99f2088a0b249aa911969c34dd6c615b0060325da555d2ad99d931170c0868b0488a2234a4114cc0013d5163b833f5c45c5eb536421c016cf85788390176bb2dc4c196d6be26bbbfceae048b82f0d8039222e71c94", - "0xacd9d833ba0a8cbd8d1ba939a11ea0fa5607e1bc6e693ec318bdb097aedd042d76e695dcebebd142e2e4ac30b1905dff03ec36d9cc70577e4dbe5e9ed7c20c7afb13a7f0155f203c6b83b9f1ad3d20a0d4aef0fbbbcf466ffc1bcd482bc2f5e0", - "0x8cc1795de015f2b0e72116f169f3b4624b7738ceebea354e0bd9051c27b86f647ea36cad57ea6884c1a8adf9b45cd83514fa687e68878bbd613d793aa10986d5a0411f081689229e0d72133b3667b9f3f1a02211d0e680564eb1ea43393e1f36", - "0xaa9281c61113c343a108de1036570feefc72fb7a96ff11f73024de12b83f29631f5a8a5900e6f10b15227c6f7462881511271bf785ebdf95ce288100e5dab391f664f6ff76c72b65b34479a4f43e5e8eba292209d6654157286ad3242ac342db", - "0xaaf16866275082e59d415db317aa874267d048ee405a553e852e6d175711d31a1fee99912345915bce121f43bc3e00d81338e5fcd3c8a1012fb4f172a9fe15622dd368b4d9d5cb60d189f423b071791fe26cea7676aca8df07965cacf80b0cd0", - "0xaccc80b3d8a6ffa648487a3d3c0ce1aeeb5401edf3cf2e385ea4a6d5fc110054fcce38f01f1da7141bbed30eb7a0a6810c82212bbb9da75d6033082dbcf6bc6a5791f85aa0f045a10da5de015edbf369b4d23b32b0c058962d2ee88e6911f994", - "0x83f1089395a16077738cc7c9a6d6a3dc9033aac4abc508af5a1f007ca92e1a80b2e6f2dbda7fdcf0d5646de790a6201d0a9cfbcb6620a1426600e3a6a425ec004384f49fb9dcd166691a47177d45dcbcb761a11d46220b0aa09fc946131f7aa5", - "0x9246bb586d43cb817c2e15ed609156e9f1cd284ba2f4797bbfa51c0341e1ba382eaac059aa9f63fb88d228a1a932839a171e7c7d00199dc7c4d6c5ea038a02cbc3cc5297c70401520e70ebbcffacd6a703f62896f3c788f94dde3c33ab0ecbdb", - "0xa316cb7c74feb0563c56cc79015e2774fbeca458bf8e9fb07894f9d6bcd73f7fb9428e87c816e5629e4bf7f3ec567fbc091549471b75492dde08217cb334b716b4582b24384586e53388873a78a90ec01bd7c3bace9cfc52161467df16e27c33", - "0xade18c74bbe60d1d69f4a570f8e5fd8696c26cc9e02829040b6b14cb9c49a4b3263b5bd5e16ec0b29010b4be054c16ab09304e23442af7d7f5fcc60bc6c5634ab6e4aed7ef334b2785e4c7672d59a687278e42d310342db5e5975d716e6d1595", - "0xb7728800bb2039acf228fa3d8028569c426cb85d28b2b5820bbef938d5ca8c4df981d3e01a309e26ca101e8295d0f6990c03b8c239798323575874a4ee5bfe46cfe99b9657189142aacd8f8d1f26cf4c0e73c6397c31ba8f18102b9ea315b638", - "0x8fb14f2a9be193f54977ecd3021663108ea143627b9a9d9faff85d1a86b855f6c437eab435fad3304f245bd7732af07f1173494cdb802fb96e85d2db89e1643206e183f3b228ca8d3f586e71aa9308eaf0223100bf07942fc39e465016d1f775", - "0xac1e025e53d98fdb3380489dce82d9d4bd3a6c98f0a523b841cb09a6f26ddd4d22efd98776e78d10fd996995fd00e81e08d3c25dd14a54b25a9d483677a24bbb8d1cb41a443b2c71038e6893b1b30f70758424e0f2039a48060191389033ef55", - "0xa4c017311b9e930868132527a9849072b91db04fd36c619ae39c98da9e2174e6201d3c2ff1246c06b1b6815bbf3ea4a1116564f55ee2fe4c4d655e2294c0ded842cba209c255ca3d7b7f82d162f97890dfdeed087aa2f87cbfc61d61815da39d", - "0x89516315a3956b455843c2555248bd94dcb19993060fe75fdd51f7aa9c9147ab13997d8a98036a8f04bee5c91d78d2990907e35a52537a8ab3ed15f1a71afdcd38044a5b6e93f662b9d36c16933a881927cacae668c4c06ee6f004c9e3989bad", - "0xa1e78a011e210400c68ca76045f7da74119bff3cbe382efd2bd2ac76567c52d68d75536a91999d084043e1ce2d07d02e0b69fb99924101d2543521747536fbc51b0454aa9a4cbbec101121f597863a5c0fee2ca5eab35dff9b9085bef8b2b0d0", - "0x830fd8d083e39153ecab43cabb22e29d7b44a55fba467af4ddd3f069439d2972ef53c3518de788f96b3f4f64963987d0155ba27afc28643af3de8e476ff515a68285728167408f45d99e574680bda6bacdd4322e587e4aa99386e035c0e931ad", - "0xb89584da22237e3061d991b1a55a5e55dc637b8b671130d304587729348138ef87885180310efe9f9f6d3580b9d7fdcf0649e8a79d2dec8c25a9f53df0fac5d517db999029cbfdd7c2cbd3e9a5503e5d267d3d8ad752335915c92b850b14bafb", - "0x959b8030733799882c5e3735479924b013756e57b893f9792bab4043e2d362d77cf308166d782e3989caa771b8a0c0a01302cb7b5e8ca12e2d6cebd59d4cd173c9dc25f438bac597fab17b4ff44997a489c168e7204b7d7c21d0938f0a2e3b51", - "0xa0a9e5503d9afe0027891dab890c687fd5f5fac5741418490c64d7c15f59533dd603a50163c79402afa61bd02de486761983c94501da17e6bbe78c497f2122210071602f578adc0ebe7a4679f87fe77e09c8c122de69105f13455fea25f08e6f", - "0x9811487283ad620cd7c9b303ae2f348d0e6f5ee17b504baaa817ae207adb912a00d3cc36dbf48745eb899e6b6e22f09f0f9ba29d949ecd7350fbbfe87a8c7cdd5d0e687fc807751d07634aaf7c38baf3b24a0670c38fa6ccd7431436fc95525f", - "0x8a13aa5071c526e560def7d8583393942f07d88c9d8d26c98738fd65f57af2e3326dbb1edff0f39fe98eda4a13ed4fd71844254b954690154c4804e1c4a53df9dc4643f4b7b09d0860070f6b2318d0d63d28fb56bf5b6ff456a18dfc72fdfbbe", - "0xb9c90ff6bff5dd97d90aee27ea1c61c1afe64b054c258b097709561fe00710e9e616773fc4bdedcbf91fbd1a6cf139bf14d20db07297418694c12c6c9b801638eeb537cb3741584a686d69532e3b6c12d8a376837f712032421987f1e770c258" - ], - "setup_G1_lagrange": [ - "0x8d0c6eeadd3f8529d67246f77404a4ac2d9d7fd7d50cf103d3e6abb9003e5e36d8f322663ebced6707a7f46d97b7566d", - "0xa0d2392f030681c61c2a867862917e10f7678d882034bb89af3db87e6ab3883a304034643dc9688a04e41a5b831582bc", - "0x94298073048d70c74f36685e547d04b7311479daa05912e18ead64b2099a194bf48ec344273d58daf0b86b1d8f1d318d", - "0x85c4063d13499013dc2ccaa98c1606763e6b1e8cca20922d4cec12ecbaf006ea81ffabe6596d1ac7ba1daf7e63e30898", - "0x84c64bce36c6b5145c6880113366025ab9a8f88e3948d374e27be8b8f9f87402c70fec9b3c621a2d1d26764a84370d0c", - "0x8b206c823acf5294552ee54579fac0f45ea15bd273dbacd63b88cd7cddbcce23b56e52f8ea352e1e1d7dcd9b3991b413", - "0xb70aaa4038ba3f5ff306c647b4392d004950c53ad8f6713b5c9c21ac99f5c56cf57323dac500a1f4e9507c4746b07a2f", - "0x895f6d1fc70b52f838d81b24f4840729cd5988b649e9d6e6f6dbac4281d8818f39ebdae7e6ea139d7f98a832bd6f29f1", - "0xa71a2832bbaade974c9ef7505dfa24e1ba466a9951b7c2db56886be31c9c7b871f3ee76cb1fcc1aab4b906d6502bc9b5", - "0x9530ba64a21e27834609c00616bc63e8fc2dc7800e478ad728ec39c624f65bbc62cb48f59decb7fbf605ce1920d02622", - "0x8d0609affaf8619bb2f6c80699e5bc7783becbd5973630cdd227ae52d6d701c45f4270becca97701b40279fab588cf64", - "0x8f5d5b4c3bb8dc9a19e5a0f84df6322a79a00c7783c86254197d313a5b35d3965a1f7c0b9c4e39ec1e8f5d02d3aa0862", - "0x96aa47a3ba20b1cfe81eb26bef503225037fdf4c9df53bea1b520841875cd1db6aa8e0f34685da08b55a3ce7289e6de0", - "0xb4c27ee3f4b8c0031837160f0a75632f5b51b5850d52b530096443f54c2b264aeccc5c61b4fcc8de7074475f354fa0d8", - "0xacfd735cda20be1d6f425a7886629c91732fbb5a4e0350ca740a8fb5b39f2001071cec0b2a0f6ca35e1f35a5ea18d00f", - "0xae44d87b1d16d59504c602cbacde2c2791f1520391ca50154e6036d3953ca466cf93d6537da2adb729e6f9f4ffa87853", - "0x97b492872ce44941ea4668ffca83b82fac0f4021bd47e0a5ffeaaacb1b3fc924ee4d53b99f7bcafe0985caf0fbe5d1d3", - "0xb3fbe2f9103d293f49c6c6016d5913f041c9113295397388111a0fdf4245d8edd6e63b9a1a1c9c8f868d6e1988116880", - "0x805efa08fd2046c44c427b225c17bed8a1eb3320cdf94026fdc24c6d345a6cfebfd7475f85d2d1bf22018ca72d2761d3", - "0x9888bae0d83077d1dfde82fdffb1195565c31c519b80cba1e21aba58ee9ccb5677f74bfde13fa5723026514a7d839661", - "0x922e19d2646ba90c9f56278bddf74621cc4518ae2f042fb8245843e87cd82724c6d7c9a99907ac6de5f2187fd2e77cbe", - "0xa38f0e1faf97dd1e0804b44e4d150dbfa48318442d1c5255eb0c14ea56b50502f3c7cb216a0336e7c140398088dc01cf", - "0x93598ea391c8735799a1d4cd0456f34994ccdf4883fad57419f634f30fee595938bc66b066dade9ae52578818c00d899", - "0xa528dc920734cfaee9feacbc0baa5b73befb1ec6fbd422fcad09a9c1f8f8c40b5ea332b2cf04dc1d6d921e9da9ddfeb4", - "0xb38d45316bf78d11e796a34ee535814e6cde0e642f14108329c5b21f4fec18cd61f84a3025824bb8dc4cbd26b2ecc9bf", - "0x8eec35a7404c9a35dc6ad0260b7f0f7fd1bfe92a2e08bc72548b99ed9acdc378728a8ea9c6879a6e47e37edb0d28c193", - "0xa68a4446274ccd947c61bf736c5219dad680b99c6085a26719793e0d9dab26d5f8a0b28e71be6e1b9ea4ae39139f7f57", - "0xa0acb543f41ad12e3b2e096629ccdd719a001d0ff53bb151e9a37aa57852f7275a7bbd06dc2a06af9144524548164af5", - "0xb271e74cdbcf8b9143f8472174bdb068c23308ea807c60a554c185f7be6f231aac13347139837514171a876dfac5baa5", - "0x8195a460719000cd1df379ebbf7918f71301a50a2fa587505cc5b8c4534c3d2343f63d28e7ee991d7a1cebb15d380696", - "0x96202b60426773e8731dcbedbf613477f65940a19fb4be0f4f742b0c76ae9d88ecdb6d36cd4f12bb404dd5d360c819e2", - "0xb0a80fe60b71ca9e80157138de8787b8a786326179604b8a15a744e52662645987e5f859ef5c76492d560daf4624b9a7", - "0xa331ea8adf87daa5e2d458d0113c307edae1a84927bca7d484aca5f8c1b6378ab42981c44b0d916d7249f4b475f926f1", - "0xaa1a8f59ae0912abf191ea7e209ff401628278dfb2269db6d87cf33bd52af3dbffbe96513a8b210e965c853a554b787a", - "0xac4f4a0e1b1a155e1f22a9085b0b047fe54c8437dbbb8e9720fd6b0cdd76557d19ca2e885a48890f0247b1a72be0e287", - "0xa428465505eac7b9660eb0d495a7a00c8cc238de3a02ebbd2eb07e502e9868086e9584b59953cf1480c0b781295db339", - "0xb7b77e21e08f6357cbd3dcd3035c3e8ec84cdfa13c7baef6c67e0ef43095e61fd549694263d7def8b8adc3a0fdcc7987", - "0xabb991d17c5bdd264c592c55101e265cb3210c4157aee4079173fd51da1e0199eed1d6c890aab95817ec078561d771af", - "0x846a8e4f801faf5fbec078b09c362ee30a00b2b58a4871744d03cd118b913464233ff926e52b0c75fbfcf098ad25a1e6", - "0x947e91ffa32f38c1ccb72cca4bfabaee9e63ab74a16f034cabba25e462f7331ebe5a7ba393f69e91830415fa75b1b52e", - "0x8dc5e26adc693f4e300cab7385edca1a2fe14c8ee6dc0cd6d013cb5aa154dc380e9e81e259cbc59c1f38f7c4a57f1c7d", - "0x9818ef6605d6ea3b7bf4da5c6d6d8ed540bb94df4d14c974e1b79ed2fd1a0b897b8cf1ff671a181a697effd66b1644a5", - "0xb5eab6baf03af994fc32cc9dce388394c18c01cdafe7909fde948f3e00a72dc8f30d15977d0f114bd7c140f5f94cf005", - "0x83b2e9858d3b929f9a2ad66a91a2c0c44d15d288c17c12a1614301a6f2d61d31eaa540ca7781520fe4420afae0ec0208", - "0xab338fbd38bce4d1b7a759f71e5e5673746c52846eff3d0b6825e390aeeca8f9f123ee88c78fe4d520cc415cbae32bf1", - "0x81adb6322b8db95d1711304e5b59f37640ca88c03e6c7e15de932be5267dff7351fa17664113ecc528e8920f5bfdc0d1", - "0x89e2e0c0d769e4107232df741678a6bacb041d0154385450aaca8be9c3c18c42f817373962e7569d33935c35666a8a6a", - "0x8f0756fea8b34a2b471ec39e4448a6a6935e5432ec2859d222964a4c82777a340e1d702777aeb946fa405afc0438221a", - "0xa2bf90c505a6f03b3dd09d04e1e7cf301fe3415b273e263f15fdfe5d0e40f619b95e8bf00916d3eaa7d7f8c0bae41c8e", - "0x91d5c76b5542637588cd47279d0bd74a25dbda0d8ec0ff68b62d7e01e34a63fc3e06d116ee75c803864b1cf330f6c360", - "0xa9958c388d25315a979566174b0622446335cb559aff1992bd71910c47497536019c6854d31c0e22df07505963fc44ff", - "0x91d82b09d5726077eed6c19bcb398abe79d87ce16c413df6bf5932b8fd64b4c0fd19c9bf0fa8db657a4a4d4c0d8f5a2d", - "0xac6e0a86e0ee416855c3e9eef2526c43835f5245527ed0038bc83b4fcadb4ea5beb91143cc674486681a9f0e63f856b1", - "0xaaf00d6efd0c6efb9f7d6a42555abec05c5af8f324e2e579fc2ac83bdc937cc682d9bc2ffd250619c8bb098b8c84db80", - "0x963f5fcd8476d0dbeb03a62cde40e3deee25f55e7ded7572d8884975f38eddc5406fc4b0adff602a1cca90f7205a7fdc", - "0xa3805ee01512f644d2679511bd8607890ee9721e75ac9a85ab9fd6fceb1308d5b9b0e9907686b4e683b34aed0f34cd81", - "0xa483d7708465cd4e33b4407fe82c84ef6bc7fa21475d961fe2e99802d0c999b6474ef7a46dd615b219c9c7e9faec45ee", - "0xb6b5f9456f12d6781c41f17cdc9d259f9515994d5dee49bb701a33fa2e8dcbb2c8c13f822b51ad232fc5e05bff2f68ef", - "0x8766b721b0cf9b1a42614c7d29aad2d89da4996dc9e2a3baeba4b33ca74100ab0b83f55c546c963e3b6af1dcf9ca067c", - "0xac5e8da1154cf4be8df2bbd2e212b7f8077099b2010c99e739441198f65337c6f7ef0d9136453a7668fde6e1389c32c7", - "0xa9d6d2c8845e5f1fec183c5153f1f6e23421e28ce0c86b0ce993b30b87869065acad9e6d9927d9f03c590852821b2f9c", - "0xa320ca07c44f7ea3ff858fe18395a86f59559617f13ec96d1e8b4a3f01d9c066a45c8d8cf8f1f14a360bb774d55f5f18", - "0xb3adb00e1312dce73b74fbd2ea16f0fb0085bd0db10772e9c260e9ed9f8829ff690e3dfffacaddc8233d484bb69778b3", - "0x87b0c8d8a167d5199d0b0743c20fb83ec8a1c442f0204bcc53bf292ba382bef58a58a6d1e2467920e32c290fdc6dae7c", - "0xa74fa436a5adc280a68e0c56b28ac33647bdfc8c5326f4c99db6dbd1b98d91afb1f41f5fffd6bcc31c1f8789c148e2db", - "0x8a37349e4ba7558965077f7f9d839c61b7dcb857fcc7965c76a64a75e377bfea8cd09b7a269ce602cc4472affc483b69", - "0x8af813f62c5962ff96bf73e33f47fd5a8e3e55651d429e77d2ce64a63c535ecc5cfc749bb120c489b7ea1d9b2a5d233c", - "0x833021445b7d9817caa33d6853fa25efc38e9d62494d209627d26799432ea7b87a96de4694967151abc1252dd2d04dfc", - "0x8f78a715107e0ace3a41bff0385fd75c13bf1250f9e5ddecf39e81bacc1244b978e3464892f7fb2596957855b8bf9fc7", - "0xaed144134dc1cc6c671f70ebe71a3aadf7511eea382969bc5d499a678d2d8ce249ebf1a06b51183f61413eba0517012b", - "0xb39a53e82c5553943a5e45bc5116d8672ec44bed96b3541dead40344b287a7b02dbf7107372effb067edd946f47de500", - "0xb383844c3b20a8bc06098046ec6b406df9419ad86fac4a000905c01325426903a5e369af856d71ccd52fea362ed29db5", - "0x83815a7098283723eec6aa6451b5d99578bf28a02971375a1fe90c15a20963e129372ac4af7b306ee2e7316472c5d66d", - "0xb426b4e185806a31febd745fa8d26b6397832a04e33c9a7eb460cbf302b4c134a8a01d4e5e40bc9b73296c539e60b3ca", - "0xa6cabf8205711457e6363ef4379ebc1226001e1aaea3002b25bfd9e173f4368002f4461e79eeb9f4aa46f1b56c739ab9", - "0xa6e88ab01282313269cd2d8c0df1a79dada5b565d6623900af9e7e15351de2b0105cc55d3e9080e1e41efe48be32a622", - "0xb2b106db3d56d189ea57afa133ae4941b4eb1dc168357af488e46811c687713fc66bbd6f8500bbd13cdb45cb82c14d1d", - "0xb3a74780ff949d19e6438db280e53632c60dc544f41320d40297fe5bb7fcee7e7931111053c30fb1ed9019ab28965b44", - "0x8c67f32b9fdc04ec291cc0d928841ab09b08e87356e43fbbf7ac3ff0f955642628f661b6f0c8e2192a887489fddf07bb", - "0xb3be58bd628383352e6473fe9a1a27cf17242df0b1273f5867e9119e908969b9e9e7e294a83b9ea14825003cb652d80c", - "0xa867acf6ab03e50936c19a21d4040bfd97eb5a89852bd9967da0e326d67ce839937cab4e910d1149ecef9d5f1b2d8f08", - "0x8006b19126bd49cbb40d73a99a37c2e02d6d37065bbe0cfcee888280176184964bd8f222f85960667c5b36dfaee0ee35", - "0xac50967b8b7840bf9d51216d68a274f1d3431c7d4031fbac75a754befbbb707c2bb184867db6b9d957f3ba0fd0a26231", - "0xb5a794c928aff0c4271674eb0a02143ed9b4d3bc950584c7cd97b7d3c3f2e323798fd5ccc6fcc0eb2e417d87f4c542a2", - "0xa2ca3d6509f04b37091ce6697672ee6495b42d986d75bd2d2058faa100d09fd0a145350f2d280d2cb36516171bd97dbf", - "0x92cfa293469967a9207b37cd70392312faf81b52963bfbad5f9f3da00817d26e10faf469e0e720c3bb195f23dda8c696", - "0xa0dd5135da0a0e33fa922c623263b29518d7fa000e5beefc66faa4d6201516d058f155475c4806917a3259db4377c38a", - "0x8fc3ae8ea6231aa9afb245a0af437e88ebca2c9ab76850c731981afba90d5add0ea254053449355eccf39df55bd912ed", - "0x9727afe1f0804297717cec9dc96d2d27024a6ae6d352fee5d25377ee858ee801593df6124b79cb62ddc9235ec1ade4ac", - "0x8bcb2c53fcaa38e8e2e0fd0929bc4d9ddce73c0282c8675676950ff806cb9f56ebd398b269f9a8c2a6265b15faf25fca", - "0xa8bd9007fbbdd4b8c049d0eb7d3649bd6a3e5097372fa8ea4b8821ba955c9ef3f39ac8b19f39d3af98640c74b9595005", - "0x92c7e851c8bd6b09dfcbfdb644725c4f65e1c3dbd111df9d85d14a0bb2d7b657eb0c7db796b42bf447b3912ef1d3b8c3", - "0x98c499b494d5b2b8bea97d00ac3a6d826ab3045bb35424575c87117fc2a1958f3829813e266630749caf0fa6eeb76819", - "0x8df190d71e432fe8691d843f6eb563445805c372eb5b6b064ec4e939be3e07526b5b7f5a289ede44ae6116a91357b8b1", - "0xb5010243f7c760fb52a935f6d8ed8fc12c0c2f57db3de8bb01fdeedf7e1c87b08f3dd3c649b65751f9fd27afa6be34c7", - "0x889c8057402cc18649f5f943aed38d6ef609b66c583f75584f3b876c1f50c5dc7d738dc7642135742e1f13fa87be46c1", - "0x996087337f69a19a4ebe8e764acf7af8170a7ad733cd201b0e4efde6ea11039a1853e115ad11387e0fb30ab655a666d8", - "0x902732c429e767ab895f47b2e72f7facad5ef05a72c36a5f9762c2194eb559f22845bbb87c1acc985306ecb4b4fbbf79", - "0x8519b62a150ea805cdfc05788b8d4e797d8396a7306b41777c438c2e8b5c38839cfec5e7dc5d546b42b7b76e062982a7", - "0x862a53ba169e6842a72763f9082ff48fbfbb63129d5a26513917c2bca9ad6362c624ce6fc973cf464f2eb4892131eb04", - "0xb86cd67c809d75fdb9f1c9453a39870f448b138f2b4058d07a707b88bb37f29d42e33ce444f4fbe50d6be13339cae8a6", - "0x8cf5d8365dbbafc0af192feb4fc00c181e2c3babc5d253268ef5564934555fb1e9b1d85ec46f0ca4709b7d5b27169b89", - "0xb48f11a1809ec780bf6181fae3b8d14f8d4dc7d1721128854354be691c7fc7695d60624f84016c1cea29a02aaf28bfbc", - "0x8b46b695a08cb9a2f29ab9dd79ab8a39ec7f0086995b8685568e007cd73aa2cd650d4fae6c3fb109c35612f751ba225e", - "0x8d2f9f0a5a7de894d6c50baceb8d75c96082df1dcf893ac95f420a93acbbf910204903d2eb6012b1b0495f08aaf9992f", - "0xb334db00a770394a84ec55c1bd5440b7d9f2521029030ef3411b0c2e0a34c75c827fd629c561ea76bd21cd6cf47027f4", - "0x96e9ff76c42bcb36f2fb7819e9123420ed5608132f7c791f95cb657a61b13041e9ba2b36f798a0fdb484878cbe015905", - "0x99f8d701e889abd7815d43ba99e0a85776ec48311fa7cb719d049f73b5d530fa950746ffbbb7beb9e30c39d864891dc2", - "0x98169c20df7c15d7543991f9c68e40ac66607cbd43fc6195416e40009917039357e932d6e807f3a40bc4503ad01ae80a", - "0x84bd97dd9e4e2ba75d0dee7d4418c720d4746203d847ce2bdd6ed17d492023df48d7b1de27e3f5cb8660c4bb9519ae1b", - "0xa54319e06db7f5f826277a54734a875c5b3fd2fa09d36d8b73594137aa62774b7356560157bc9e3fdf1046dc57b6006a", - "0x90cfff7cd4e7c73b84f63455d31b0d428cb5eee53e378028591478511985bcc95eb94f79ad28af5b3bed864e422d7b06", - "0xa11c23cc8dce26ac35aea9abe911905a32616a259fa7da3a20f42dc853ad31b2634007aa110c360d3771ff19851f4fb4", - "0x9856fbee9095074ad0568498ff45f13fe81e84ea5edaf04127d9ee7e35e730c6d23fa7f8f49d092cf06b222f94ab7f36", - "0x818862dec89f0dc314629fffbca9b96f24dfde2d835fa8bde21b30dc99fe46d837d8f745e41b39b8cf26bfe7f338f582", - "0x831819d41524c50d19f7720bf48f65346b42fb7955ee6ecc192f7e9fed2e7010abccdfdeac2b0c7c599bc83ac70be371", - "0xb367e588eb96aa8a908d8cc354706fee97e092d1bc7a836dbcc97c6ed4de349643a783fb4ddf0dec85a32060318efa85", - "0xb7aaef729befd4ab2be5ec957d7d1dbe6178de1d05c2b230d8c4b0574a3363e2d51bc54ea0279a49cc7adffa15a5a43a", - "0xae2891d848822794ecb641e12e30701f571431821d281ceecbccaaa69b8cd8242495dc5dbf38f7d8ed98f6c6919038aa", - "0x872cf2f230d3fffce17bf6f70739084876dc13596415644d151e477ce04170d6ab5a40773557eeb3600c1ad953a0bfce", - "0xb853d0a14cef7893ba1efb8f4c0fdb61342d30fa66f8e3d2ca5208826ce1db5c8a99aa5b64c97e9d90857d53beb93d67", - "0x910b434536cec39a2c47ca396e279afdbc997a1c0192a7d8be2ba24126b4d762b4525a94cea593a7c1f707ba39f17c0c", - "0xb6511e9dea1fbccedd7b8bb0a790a71db3999bd4e3db91be2f1e25062fae9bb4e94e50d8ec0dcc67b7a0abce985200b2", - "0x936885c90ebe5a231d9c2eb0dfd8d08a55ecaa8e0db31c28b7416869b3cc0371448168cbec968d4d26d1cb5a16ebe541", - "0xb71c2ac873b27fe3da67036ca546d31ca7f7a3dc13070f1530fce566e7a707daeb22b80423d505f1835fe557173754f8", - "0x85acb64140915c940b078478b7d4dadd4d8504cde595e64f60bd6c21e426b4e422608df1ed2dd94709c190e8592c22d7", - "0xb5831c7d7c413278070a4ef1653cec9c4c029ee27a209a6ea0ad09b299309dea70a7aef4ff9c6bdeda87dcda8fa0c318", - "0xaa0e56e3205751b4b8f8fa2b6d68b25121f2b2468df9f1bd4ef55f236b031805a7d9fd6f3bba876c69cdba8c5ea5e05f", - "0xb021f5ae4ed50f9b53f66dd326e3f49a96f4314fc7986ace23c1f4be9955ec61d8f7c74961b5fdeabcd0b9bccbf92ce8", - "0x88df439f485c297469e04a1d407e738e4e6ac09a7a0e14e2df66681e562fdb637a996df4b9df4e185faab8914a5cef76", - "0x8e7ae06baa69cb23ca3575205920cb74ac3cda9eb316f4eef7b46e2bff549175a751226d5b5c65fe631a35c3f8e34d61", - "0x99b26ff174418d1efc07dfbed70be8e0cb86ac0cec84e7524677161f519977d9ca3e2bbe76face8fe9016f994dafc0ff", - "0xa5f17fe28992be57abd2d2dcaa6f7c085522795bfdf87ba9d762a0070ad4630a42aa1e809801bc9f2a5daf46a03e0c22", - "0x8d673c7934d0e072b9d844994f30c384e55cec8d37ce88d3ad21f8bb1c90ecc770a0eaf2945851e5dab697c3fc2814a9", - "0xa003ed4eb401cfe08d56405442ca572f29728cfff8f682ef4d0e56dd06557750f6a9f28a20c033bc6bbb792cc76cc1a8", - "0x8010408f845cf1185b381fed0e03c53b33b86ea4912426819d431477bd61c534df25b6d3cf40042583543093e5f4bb44", - "0x9021a1ae2eb501134e0f51093c9f9ac7d276d10b14471b14f4a9e386256e8c155bef59973a3d81c38bdab683cd5c10e0", - "0xa5abf269ceabbb1cf0b75d5b9c720a3d230d38f284ed787b6a05145d697a01909662a5b095269996e6fa021849d0f41f", - "0xb4b260af0a005220deb2266518d11dbc36d17e59fc7b4780ab20a813f2412ebd568b1f8adc45bf045fcbe0e60c65fd24", - "0xb8c4cb93bedbb75d058269dfccda44ae92fe37b3ab2ef3d95c4a907e1fadf77c3db0fa5869c19843e14b122e01e5c1f4", - "0xac818f7cdecc7b495779d8d0ff487f23ab36a61d0cf073e11000349747537b5b77044203585a55214bb34f67ef76f2d2", - "0x86215799c25356904611e71271327ca4882f19a889938839c80a30d319ddbe6c0f1dfa9d5523813a096048c4aef338cd", - "0xa9204889b9388bf713ca59ea35d288cd692285a34e4aa47f3751453589eb3b03a9cc49a40d82ec2c913c736752d8674d", - "0x893aecf973c862c71602ffb9f5ac7bf9c256db36e909c95fe093d871aab2499e7a248f924f72dea604de14abfc00e21c", - "0xb8882ee51cfe4acba958fa6f19102aa5471b1fbaf3c00292e474e3e2ec0d5b79af3748b7eea7489b17920ce29efc4139", - "0x8350813d2ec66ef35f1efa6c129e2ebaedc082c5160507bcf04018e170fc0731858ad417a017dadbd9ade78015312e7f", - "0x83f6829532be8cd92f3bf1fef264ee5b7466b96e2821d097f56cbb292d605a6fb26cd3a01d4037a3b1681d8143ae54d7", - "0x87d6258777347e4c1428ba3dcbf87fdd5113d5c30cf329e89fa3c9c1d954d031e8acacb4eed9dca8d44507c65e47e7cd", - "0xa05669a1e561b1c131b0f70e3d9fc846dc320dc0872334d07347e260d40b2e51fdbabeb0d1ae1fb89fba70af51f25a1a", - "0x819925c23fd4d851ea0eecc8c581f4a0047f5449c821d34eccc59a2911f1bd4c319dab6ece19411d028b7fdedece366b", - "0xb831b762254afd35364a04966d07b3c97e0b883c27444ff939c2ab1b649dc21ac8915b99dc6903623ed7adaae44870ac", - "0x93ec0190f47deffe74179879d3df8113a720423f5ca211d56db9654db20afe10371f3f8ec491d4e166609b9b9a82d0d4", - "0x8f4aa6313719bcfad7ca1ed0af2d2ee10424ea303177466915839f17d2c5df84cc28fcef192cbb91bb696dd383efd3b2", - "0x8d9c9fdf4b8b6a0a702959cf784ad43d550834e5ab2cd3bebede7773c0c755417ad2de7d25b7ff579f377f0800234b44", - "0x99d9427c20752f89049195a91cf85e7082f9150c3b5cb66b267be44c89d41e7cc269a66dacabacadab62f2fa00cc03be", - "0xb37709d1aca976cbbf3dc4f08d9c35924d1b8b0f1c465bd92e4c8ff9708e7d045c423183b04a0e0ab4c29efd99ef6f0e", - "0xa163f42fb371b138d59c683c2a4db4ca8cbc971ae13f9a9cc39d7f253b7ee46a207b804360e05e8938c73bf3193bab55", - "0x87a037aa558508773fc9a0b9ba18e3d368ffe47dfaf1afacee4748f72e9d3decc2f7c44b7bf0b0268873a9c2ef5fe916", - "0xa1f20cb535cc3aebd6e738491fe3446478f7609d210af56a4004d72500b3ec2236e93446783fe628c9337bcd89c1e8e1", - "0x9757aa358dfbba4f7116da00fe9af97f7ac6d390792ea07682b984aa853379ac525222ac8a83de802859c6dec9182ef7", - "0x815daca1eded189ec7cb7cbc8ad443f38e6ddb3fb1301d1e5a1b02586f1329035209b7c9232dc4dff3fc546cb5ac7835", - "0xaed86dfaf9c4f0a4b2a183f70f9041172002a773482a8ebf3d9d5f97d37ee7c6767badfda15476b3b243931235c7831c", - "0x8d032e681e89e41b29f26be02f80030fa888f6967061d2204c1ebb2279a3211d759d187bce6408c6830affa1337fb4e0", - "0x877bff5c2db06116f918a722b26422c920aeade1efa02fa61773fca77f0ea4a7e4ee0ecaaa5cfe98044c0ff91b627588", - "0xb9ee5310d0996a10a242738d846565bdb343a4049a24cd4868db318ea6168a32548efaf4ab84edfbf27ce8aec1be2d1c", - "0xb59f6928167323037c6296dd7697846e80a7a4b81320cfae9073ebd2002a03bdf6933e887f33ad83eda8468876c2c4fb", - "0x8167686245149dc116a175331c25301e18bb48a6627e2835ae3dd80dd373d029129c50ab2aebeaf2c2ccddc58dcc72ec", - "0x82b7dcc29803f916effb67c5ba96a1c067ed8ca43ad0e8d61a510ab067baefd4d6b49e3886b863da2de1d8f2979a4baa", - "0xb43824cd6f6872a576d64372dde466fef6decdbb5ad5db55791249fde0a483e4e40c6e1c221e923e096a038fe47dab5e", - "0xab1e9884cf5a8444140cf4a22b9a4311a266db11b392e06c89843ac9d027729fee410560bcd35626fd8de3aad19afc4a", - "0xa0dbd92a8d955eb1d24887ca739c639bdee8493506d7344aadb28c929f9eb3b4ebaae6bd7fd9ffe8abb83d0d29091e43", - "0x8352a47a70e343f21b55da541b8c0e35cd88731276a1550d45792c738c4d4d7dc664f447c3933daabd4dbb29bb83be4a", - "0x8ce4a1e3c4370346d6f58528a5ef1a85360d964f89e54867ba09c985c1e6c07e710a32cdda8da9fa0e3b26622d866874", - "0xb5e356d67dd70b6f01dd6181611d89f30ea00b179ae1fa42c7eadb0b077fb52b19212b0b9a075ebd6dc62c74050b2d2f", - "0xb68f2cd1db8e4ad5efdba3c6eaa60bfcc7b51c2b0ce8bb943a4bc6968995abe8a45fe7f12434e5b0076f148d942786be", - "0xb5c7b07f80cd05c0b0840a9f634845928210433b549fb0f84a36c87bf5f7d7eb854736c4083445c952348482a300226a", - "0x8cfd9ea5185ff9779dee35efe0252957d6a74693104fb7c2ea989252a1aa99d19abaab76b2d7416eb99145c6fdb89506", - "0x8cc8e2c5c6ddee7ef720052a39cab1ecc5e1d4c5f00fb6989731a23f6d87ac4b055abb47da7202a98c674684d103152a", - "0x8c95394c9ed45e1bf1b7cfe93b2694f6a01ff5fed8f6064e673ba3e67551829949f6885963d11860d005e6fabd5ac32c", - "0xadf00b86f4a295b607df157f14195d6b51e18e2757778fde0006289fabba8c0a4ab8fad5e3e68ddbb16ccb196cc5973f", - "0xb1714b95c4885aac0ee978e6bbabbc9596f92b8858cb953df077511d178527c462cbe1d97fdc898938bae2cd560f7b66", - "0xadf103f4344feb6b9c8104105d64475abc697e5f805e9b08aa874e4953d56605677ef7ff4b0b97987dc47257168ae94d", - "0xb0ce6ede9edb272d8769aed7c9c7a7c9df2fb83d31cc16771f13173bcdc209daf2f35887dcca85522d5fdae39f7b8e36", - "0xad698d1154f7eda04e2e65f66f7fcdb7b0391f248ba37d210a18db75dafd10aedc8a4d6f9299d5b6a77964c58b380126", - "0x904856cd3ecdbb1742239441f92d579beb5616a6e46a953cf2f1dd4a83a147679fc45270dcac3e9e3d346b46ab061757", - "0xb600b5b521af51cdfcb75581e1eccc666a7078d6a7f49f4fdb0d73c9b2dab4ce0ecafcbd71f6dd22636e135c634ee055", - "0xa170c5d31f6657f85078c48c7bbf11687ce032ab2ff4b9b3aee5af742baecf41ea1c2db83bcba00bccc977af7d0c5c8e", - "0xa9ef1cbb6a7acb54faf1bcbd4676cdeba36013ca5d1ac1914c3ff353954f42e152b16da2bdf4a7d423b986d62b831974", - "0xaa706d88d3bd2ce9e992547e285788295fd3e2bbf88e329fae91e772248aa68fdfdb52f0b766746a3d7991308c725f47", - "0x911a837dfff2062bae6bcd1fe41032e889eb397e8206cedadf888c9a427a0afe8c88dcb24579be7bfa502a40f6a8c1cc", - "0xae80382929b7a9b6f51fe0439528a7b1a78f97a8565ba8cddb9ee4ba488f2ab710e7923443f8759a10f670087e1292c4", - "0xb8962de382aaa844d45a882ffb7cd0cd1ab2ef073bce510a0d18a119f7a3f9088a7e06d8864a69b13dc2f66840af35ae", - "0x954538ffff65191538dca17ec1df5876cb2cd63023ff2665cc3954143e318ece7d14d64548929e939b86038f6c323fc1", - "0x89efa770de15201a41f298020d1d6880c032e3fb8de3690d482843eb859e286acabb1a6dc001c94185494759f47a0c83", - "0xa7a22d95b97c7c07b555764069adaa31b00b6738d853a5da0fe7dc47297d4912a0add87b14fa7db0a087a9de402ea281", - "0x9190d60740c0813ba2ae1a7a1400fa75d6db4d5ce88b4db0626922647f0c50796a4e724e9cc67d635b8a03c5f41978f7", - "0xab07c30b95477c65f35dc4c56d164e9346d393ad1c2f989326763a4cc04b2cb0386e263007cc5d0125631a09ad3b874c", - "0x9398d8e243147de3f70ce60f162c56c6c75f29feb7bc913512420ee3f992e3c3fb964d84ef8de70ef2c118db7d6d7fd5", - "0xb161b15b38cbd581f51ca991d1d897e0710cd6fdf672b9467af612cd26ec30e770c2553469de587af44b17e3d7fea9f7", - "0x8c5d0260b6eb71375c7ad2e243257065e4ea15501190371e9c33721a121c8111e68387db278e8f1a206c0cce478aaa2b", - "0xb54ac06a0fb7711d701c0cd25c01ef640e60e3cb669f76e530a97615680905b5c5eac3c653ce6f97ceca2b04f6248e46", - "0xb5c7f76e3ed6dc6c5d45494f851fa1b5eaf3b89adac7c34ad66c730e10488928f6ef0c399c4c26cbeb231e6e0d3d5022", - "0xb6cd90bdd011ac1370a7bbc9c111489da2968d7b50bf1c40330375d1a405c62a31e338e89842fe67982f8165b03480c7", - "0xb0afcaf8d01f5b57cdeb54393f27b27dc81922aa9eaccc411de3b03d920ae7b45295b090ef65685457b1f8045c435587", - "0xb2786c0460e5057f94d346c8ebe194f994f6556ab2904a1d1afd66c0ff36391b56f72ed769dcc58558ee5efaa2ed6785", - "0x965dbb0cb671be339afcb2d6f56e3c386fb5d28536d61d6073b420ee15dee79c205af2f089fbb07514a03c71bf54b4e2", - "0x90f2003e2286bba9cebff3a6791637ca83b6509201c6aed1d47f27097d383d5c2d8532bff9e3541d2c34259841cf26ab", - "0x902142d1224e1888ebbfef66aaf8d5b98c27927a00b950753a41d1d28a687a8286b51655da9a60db285b20dc81d5ea89", - "0xa5d364448bf0d0849e5104bdaef9cb2cc8c555f5d6d34239c68671fbe1252f7c8c75b83cea10159dee4da73298f39a12", - "0xb013a54c5b99e296d9419ad5c2aaf4545acd34405e57d13cb764e92132cc20d1a14b33e10caf22d898b608670c04f273", - "0xb92976dceda373331804d48a7847f508cafde8d15949df53dbda09d03908678db1e61ee637baad5f05b2b03ea6f5a870", - "0x968bcb308c7ad0813dc9b3170f23f419aecd7b42176f27fac698811795bf42659fea6b04dab4ef43595dcc990622041b", - "0xa9d0a20e9367ea831dccd37f4d97ea75e9aeec952947a7946d95e0d249c94024183ef79a624bdea782469824df0ee4e4", - "0x8521b9667453c3658703e5db365b13f0e0d2331ce611ff1e708f8124d8a81bb5e82871de4a66d45c1a6b0a3901bd901e", - "0xb9c88e76e69b0722c0a2f97e57dbc4a6f7456434cd694e2ff67f4e24740cffa4db03e2b18f07f22954ae7db2286e1fa2", - "0x8400e55aa9ab01d4cc0affd611127b5d8d9a9dbd897f3cb8e2050379983aa54249be17d7b7891977b2515bb44a483f65", - "0x8cbb967b4ed31dc40ea06822a94d54cbfc8845c66fbafa3474c8f5fe1ada97299ed4ca955d9d7a39af8821eabf711854", - "0xb4d266ee3fea264a6c563fd6bed46f958c2d7bd328225f6e47faf41a0916aef3b697574322f8b814dfb2f5c242022bf6", - "0x8f7c72d69a919450215ead660ffa9637642c5306354888d549fd4a42e11c649b389f67cc802a0184d10fdb261351140c", - "0xa5f9e494ea9b2393ec32c48aac76c04158ccef436d4e70ad930cba20c55fbf61e8f239f70b9d75462405c4b6317c71a1", - "0xb3befb259b52a44a6f44345859e315c20efa48c0c992b0b1621d903164a77667a93f13859790a5e4acb9f3ec6c5a3c6e", - "0xb9e4ca259b4ee490d0824207d4d05baf0910d3fe5561ff8b514d8aa5c646417ca76f36ab7c6a9d0fb04c279742f6167a", - "0x98fa8c32a39092edb3c2c65c811d2a553931010ccb18d2124d5b96debd8b637d42b8a80111289f2079d9ebca2131a6dc", - "0xa65e5aa4631ab168b0954e404006ce05ac088fd3d8692d48af2de5fd47edbf306c80e1c7529697754dbbba1b54164ba0", - "0xb94b7d37e4d970b4bb67bf324ebf80961a1b5a1fa7d9531286ab81a71d6c5f79886f8ef59d38ae35b518a10ed8176dcc", - "0xb5ed2f4b0a9ae9ace2e8f6a7fd6560d17c90ae11a74fa8bef2c6c0e38bfd2b9dd2984480633bca276cb73137467e2ce3", - "0xa18556fe291d87a2358e804ee62ddff2c1d53569858b8ae9b4949d117e3bfb4aefce1950be8b6545277f112bebeeb93d", - "0xa0d60b9def5d3c05856dff874b4b66ec6e6f0a55c7b33060cc26206c266017cdcf79b1d6f6be93ed7005a932f9c6a0b9", - "0x801fced58a3537c69c232ce846b7517efd958e57c4d7cd262dbec9038d71246dafad124aa48e47fe84ecc786433747c7", - "0xa5e9a8ea302524323aa64a7c26274f08d497df3d570676ecc86bd753c96a487a650389a85f0bc8f5ea94fe6819dc14e5", - "0xa8a2963dc9238a268045d103db101adc3b2f3ab4651b7703b2fe40ece06f66bf60af91369c712aa176df6ed3d64a82fa", - "0xa4a8ff0a9a98442357bcdd9a44665919c5d9da6a7d7d21ccdbbd8f3079b1e01125af054b43b37fc303941d0a2e7baee0", - "0x90ef893350f50d6f61ee13dfab6e3121f4a06a1908a707b5f0036cdc2fe483614de3b1445df663934036784342b0106f", - "0x84e74d5bc40aaab2cc1d52946b7e06781fbef9d8de6f8b50cd74955d6bdb724864c0e31d5ac57bf271a521db6a352bd6", - "0x832cdf653bbbd128e2e36e7360354a9e82813737c8ab194303d76667a27aa95252756c1514b9e4257db1875f70f73eb4", - "0xa0af8660ed32e6dbcc4d5d21b0a79a25ff49394224f14e6e47604cf3b00136de8f9ab92e82814a595bf65340271c16c3", - "0x9040b5caf5e4dc4118572a2df6176716b5b79d510877bbb4a1211b046596899ea193be4d889e11e464ffb445ab71907b", - "0xb9bf8354c70238ab084b028f59e379b8a65c21604034d1b8c9b975f35a476e3c0ba09dd25bf95c5d8ffb25832537319b", - "0xa7b492cc1df2a8f62c935d49770d5078586bd0fefda262eb5622033e867e0b9dc0ffc2ce61cd678136a3878d4cbb2b56", - "0x95a5ef06f38743bba187a7a977023b1d9d5ec9ef95ba4343ad149a7b8b0db0e8e528bfb268dc7e5c708bc614dc3d02c8", - "0x99dcf7f123df6c55aeff0a20885a73e84d861ec95cf9208ba90494f37a2dcaacebc8344f392547d3046616d9753c7217", - "0xb3e14f309281a3685ceb14f8921c1e021b7e93c9e9595596b9fb627e60d09ed9e5534733fcbdf2fbc8c981698f5e62ac", - "0x816a5e0463074f8c7fb2998e0f0cf89b55790bdbbb573715f6268afb0492453bd640dd07a9953d0400169d555fdf4ac8", - "0x8356d68f3fe7e02a751f579813bd888c9f4edcc568142307d1c9259caef692800e1581d14225e3a3585dac667928fa94", - "0x8d70ea3314c91bfc3f7c1dcf08328ae96f857d98c6aac12ad9eebc2f77e514afdbaf728dfcb192ed29e7ce9a0623ecbb", - "0xb68280e7f62ced834b55bc2fcc38d9ea0b1fbcd67cc1682622231894d707c51478ed5edf657d68e0b1b734d9f814b731", - "0xb712dd539e1d79a6222328615d548612eab564ace9737d0249aa2eefed556bbcf3101eba35a8d429d4a5f9828c2ac1fe", - "0x8da42ca096419f267f0680fd3067a5dbb790bc815606800ae87fe0263cae47c29a9a1d8233b19fe89f8cc8df6f64697e", - "0x8cb2ffd647e07a6754b606bde29582c0665ac4dde30ebdda0144d3479998948dae9eb0f65f82a6c5630210449fbd59f7", - "0x8064c3ef96c8e04398d49e665d6de714de6ee0fced836695baa2aa31139373fad63a7fc3d40600d69799c9df1374a791", - "0xaec99bea8ab4e6d4b246c364b5edc27631c0acc619687941d83fa5ba087dd41f8eaec024c7e5c97cf83b141b6fb135da", - "0x8db6051f48901308b08bb1feb8fd2bceaedde560548e79223bd87e485ea45d28c6dcec58030537406ed2b7a9e94e60cc", - "0xa5b812c92d0081833dcf9e54f2e1979a919b01302535d10b03b779330c6d25d2de1f374b77fe357db65d24f9cbcd5572", - "0x967d442485c44cf94971d035040e090c98264e3348f55deabd9b48366ec8fe0d5a52e4b2c9a96780a94fc1340338484e", - "0xa4b4110bef27f55d70f2765fc3f83c5ddcdfe7f8c341ea9d7c5bcee2f6341bcfbf7b170b52e51480e9b5509f3b52048f", - "0xa0d39e4eb013da967a6ac808625122a1c69bf589e3855482dedb6847bb78adc0c8366612c1886d485b31cda7304ec987", - "0xa92f756b44d44b4e22ad265b688b13c9358114557489b8fb0d9720a35e1773b3f0fa7805ac59b35d119a57fe0f596692", - "0xaa27e4b979af6742b49db8bf73c064afd83a9cfe9016131a10381f35a46169e8cfd1a466f295fcc432c217c7c9fa44a5", - "0x845961319cc10bcfbb1f3cb414a5c6a6d008fb3aac42c7d5d74e892cc998af97bc9a9120c3f794e4078135e16a416e38", - "0xa18dbe3015c26ae3e95034c01d7898e3c884d49cc82e71ddb2cf89d11cec34cc2a3dff0fafb464e8e59b82ce1a0a7a11", - "0xa954aed6d7124fa5bd5074bd65be4d28547a665fb4fe5a31c75a5313b77d1c6fc3c978e24c9591a2774f97f76632bdde", - "0x8f983b2da584bdff598fcb83c4caa367b4542f4417cc9fa05265ff11d6e12143c384b4398d3745a2d826235c72186a79", - "0xb2caa17d434982d8dd59a9427307dfe4416b0efc8df627dd5fc20d2c11046c93461d669cab2862c094eec6a9845990c6", - "0x8c2baa5a97ee3154cce9fa24f6b54b23e9d073e222220fdd0e83e210c0058fb45ce844382828b0cb21438cf4cad76ee6", - "0xb93437406e4755ccf1de89f5cbe89e939490a2a5cf1585d4363c21ae35b986cb0b981dec02be2940b4ec429cc7a64d4c", - "0xa90ac36c97b7ea2eddb65e98e0d08a61e5253019eeb138b9f68f82bb61cdbadf06245b9dfffe851dfa3aa0667c6ac4b8", - "0x8bcdd7b92f43b721ddbfd7596e104bc30b8b43bdaee098aac11222903c37f860df29d888a44aa19f6041da8400ddd062", - "0x98f62d96bdf4e93ed25b2184598081f77732795b06b3041515aa95ffda18eb2af5da1db0e7cfed3899143e4a5d5e7d6c", - "0xad541e3d7f24e4546b4ae1160c1c359f531099dab4be3c077e446c82cb41b9e20b35fa7569798a9f72c1fae312b140b4", - "0x8844a1471ff3f868c6465459a5e0f2fb4d93c65021641760f1bb84f792b151bc04b5a0421bbc72cf978e038edc046b8f", - "0xaf895aebe27f8357ae6d991c2841572c2063b8d0b05a2a35e51d9b58944c425c764f45a3f3b13f50b1b1f3d9025e52ad", - "0xadf85265bb8ee7fead68d676a8301129a6b4984149f0eb4701eae82ec50120ddad657d8798af533e2295877309366e9c", - "0x962e157fe343d7296b45f88d9495d2e5481e05ea44ca7661c1fdf8cc0ac87c403753ca81101c1294f248e09089c090eb", - "0xa7c8959548c7ae2338b083172fee07543dc14b25860538b48c76ef98ab8f2f126ecb53f8576b8a2b5813ecb152867f18", - "0xae71680366e11471e1c9a0bc7ea3095bc4d6ceb6cf15b51f1b6061b043f6d5941c9f869be7cb5513e8450dca16df2547", - "0x831290201f42ebf21f611ca769477b767cf0ee58d549fcd9e993fae39d07745813c5ce66afa61b55bb5b4664f400ece7", - "0xaf5879e992f86de4787f1bc6decbc4de7d340367b420a99a6c34ac4650d2a40cbe1cef5c6470fc6c72de8ee1fe6bcce4", - "0x8d3c27e1b2ef88d76ac0b1441d327567c761962779c8b1f746e3c976acb63b21d03e5e76589ce9bb0d9ba6e849ed3d53", - "0xab23b09c9f4151e22654d43c1523f009623b01fe1953d343107cef38b95bd10afd898964946d3cb8521bcbe893e1c84d", - "0x8a6acade9520e7a8c07f33d60a87fd53faa6fbf7f018735bffcbbb757c3bafb26f547ceb68e7b8b6bca74819bfcd521a", - "0x94db50080d557440a46b6b45ee8083bc90e9267d40489040cbed6234bebf350c788ec51557b969f95194102fde8e9713", - "0x8be8031f32504e0c44958d893649f76cec17af79efcd22bbedb78378f0a150845467e59f79a3f2a3b6a66bdf0d71d13c", - "0xa69a4ac47fd92e1926b5e14adcbebbef049848e8a00d4bb387340892e5a9333cae512f447201728d3b53c6cf980a5fdc", - "0x8fc713825277c5a8d9ef0a1f6219d141def6d8b30aff0d901026280a17d1265d563ff5192a0817e0e1a04ff447fb6643", - "0x8bf0a85569c4f0770ff09db30b8b2ea6c687630c7801302c17986c69a57c30f0781d14b3f98a10b50c4ecebc16a5b5ec", - "0x896baa4135d5621fd6b6a19c6d20b47415923c6e10f76c03a8879fd8354e853b0b98993aa44e334623d60166ba3e3ca9", - "0xb82cde1c2e75a519ef727b17f1e76f4a858857261be9d866a4429d9facf9ea71d16b8af53c26bde34739fe6ea99edc73", - "0xb1a9e1f2e34895a7c5711b983220580589713306837c14073d952fe2aef0297135de0be4b25cbfaed5e2566727fb32ef", - "0xb42ed0e9eaf02312d1dba19a044702038cf72d02944d3018960077effc6da86c5753036a85d93cd7233671f03d78d49a", - "0xa402e34849e911dbf0981328b9fe6fff834c1b8683591efd3b85aa7d249811d6b460a534d95e7a96fdd7f821a201c2c4", - "0xa774417470c1532f39923d499566af762fa176c9d533767efd457cc5e4a27f60e9217f4b84a9343ecb133d9a9aab96b7", - "0x83dc340541b9ef2eb8394d957cd07b996d2b52ac6eb5562cbba8f1a3312f941c424c12d1341a6dc19d18d289c681ef40", - "0xb2906c32d5756b5712e45dec53782494a81e80f887c6e1ef76e79c737625eccecb8fd17b20e6f84890d322b6ffde6eab", - "0xb89705c30cec4d50691bc9f4d461c902d6a4d147cf75ee2f1c542ad73e5f0dabe3d04cd41c6c04ab1422be4134cf1ad7", - "0x8c3293651f4c4fac688bf5837c208b15e5a19ce51b20dd80ffc7fca12d3e615b2773cfc3ed62a1b39c66808a116bde06", - "0x8fceb8ef481163527d1fc3abc7e1a5b3b6de2f654c3fe116d1367b177dcba2e0d2124a7216803513a3d53fc1e30435b9", - "0xb2a42c827da630aaa3eb20ed07d136aa11ba01b4c8efc0a57ebab7d5b851a15daa6ba118bcffbc20703916e430e30a87", - "0xa86340153abb3fe97414e2fde857e15aac27c9bb9b61258eea6766024f426ed0753f08f07f6b02b5375e1587ea3afcab", - "0xb006465e258e646f91ba889765113d3dc9bd657246c533cab6516d55ba054baa9d7276a3b0fa31730c3bd824845bf107", - "0xa08aadc09428719cde0050d064c0f42c5b7c4f6c158227d7636f870957d6cfe821b4c62d39279a7c98f5a75fcb7bbfba", - "0x885e7d47ce9b50d21b95116be195be25f15223a6a189387575cc76740174c3e9044f1196986d82856b3fb25cdd562049", - "0xb18c3780362d822cc06910743c4cbcef044823a22d12987fe2e56f3801e417f2e9cd31574ea1c5c6ee7673a14aa56e3e", - "0xa625570ef7d31c042d968018865aeeba34ee65a059ab1ec079c7a8ba1be9e24bce6afb7036c07d9d6c96ab014f95d661", - "0x8fc9bd4764adc4c300b5bd49a06dce885d1d8aff9bae68a47976d0cd42110aa6afa2d7b90b64e81c0f14de729f2fb851", - "0x91d88714cb669f5f00241aa5ab80dffb04109492ea9c72b59645eb1f85f3539c61db2ab418af986f42241df8b35445e9", - "0xb98f14e664df2590dd2d00b5b5c817e388e5d9fb074f718637c33b3d4969c89e82fdd12db8997f5ff3bf5bb5ca5dd839", - "0x86cb3d9f148cb2170317a4c22af7092155aa66ecff7ab1299b102fbbaa33ed2a284b97b08f529d2da9faea63fb98972c", - "0x92449f6b8a7c737ecef291c947cbd602c47d7fe47dc3426c2b413f3019169aa56e14c2a7216adce713e1c7bd5c08a83f", - "0xb08c1b9080bba88b44a65070948142d73c00730715fbdd01e13fc3415c5b4f3248ef514fa3ade4a918c9a820cccae97c", - "0xb0a05297da76e37c22be7383e60bba1cbc4f98ba650e12d4afcfcea569842003644a10ad73c9148958f7bf1ffa0a27d0", - "0x839092c1f4e9fb1ec0dde8176f013b0d706ab275079f00f8e774287dd658d1b5638d5fe206f5f2a141911a74bb120f75", - "0xa36bd669bdc055ece4b17ff6eac4c60a2f23324a5eb6d0d6c16a2fce44c39cfd52d1fa2b67f3f5e83504e36426fbfc40", - "0x8aa428323512cf769645e2913a72976d32da4c0062ffe468a6062fd009340f0f23c6b63285848a0e7631a907adb032a0", - "0x944800f7d43f41283eb56115ac39ccc5bf107ae5db6abcaba6936b896260cd09428a6b828c0bccebeb00541073dbf38e", - "0x8e700ca7c9e1538cf64e161dd8d16af56fc29d53c79648150d6d8c268b0c95c76acded723e29918690d66252bd75f5b3", - "0xb9c4ce35b5b16b4c39b6e85800c76b26e8d0999500fabc1e5b6234a7f8da18c621266ac0d5ebc085354297ff21ac89a5", - "0xa0c706d32063f1877f7e903048ce885f5d012008d4a8019dd00261a8bbc30834bffeba56cdeddc59167d54cc9e65f8fa", - "0x839813b736225087cbbcf24506ea7bf69138605036b764ec0514055ac174bbc67c786a405708eb39a6c14c8d7e0ec6ee", - "0xb1a5fef055a7e921c664f1a6d3cb8b21943c89b7e61524a307d8e45aa432e5765a27c32efdb32d88062cd80800a260de", - "0xb17f8202d9ed42f0f5cb1b1dbda60711de3b917a77f6069546fa3f86d21f372b8dd5cb86f1994b873ba9982404e08daf", - "0xb5211d54bd02d44d4d808ad57067606f3e9fa2cad244a5f2acef0edf82de3c496d2b800f7c05f175d01fa6ace28b44d1", - "0xaa9c6f8f489b35fdb7544116fe5102a34ff542de29262f156df4db4ea6e064f5ea20c4bd877d40377ed5d58114b68f19", - "0x826668b1f32e85844ff85dd7e2a8e7f4e0fd349162428bc9d91626b5ab21bdbacd1c9e30cf16f5809b8bf5da4f4fe364", - "0xb30d14917b49437f9fdbae13d50aee3d8a18da3a7f247b39e5d3e975c60bd269da32da4e4cc8844666fca0d65f4e3640", - "0x8c6918d8d94b36c6b9e772e9a432e66df16724e3b0660bde5ea397e6ef88028bb7d26184fbe266a1e86aef4a0dfe5faa", - "0x906d80ffd692c1dd03ab89be52e0a5a9e90a9cdbfc523d2b99c138ae81f45d24c34703f9cb5a666b67416e3bb6272bc4", - "0x8b07e8ba22b436e64f011cacf5e89c55cd3bfb72ae8b32a3a8922c4fccb29de6f73662d6e330da6aa6e732a2187ef3c9", - "0x9547466b4553a49adf59cc65d4c3c9401b2178947ebe3bd33c6e63cfb67d6be8729033158594f6f244b272c4487d6958", - "0xaafcccea41e05cb47223fa8dfec0dd55964268bd4d05e24469614077668655ac8a51d2ac2bfb22862f8f4fa817048c2f", - "0x870f8c1173e8fd365b0a2e55c66eea3ab55355990c311f3042377803d37e68d712edcc5a0a2e2f5a46df0c1c8e6310c2", - "0xb4288f792008f342935f18d8d9447fe4ddcfea350566e13dba451f58c68e27241af1367f2603a9dff6748e7fe0c53de4", - "0x91c58c0e537d3afdcf7783601dd9cda2aa9956e11f711b15403760cf15fc6dffb40ed643886854571da8c0f84e17adfe", - "0xa43fec8ee92febed32e7cdd4e6314a62d9d3052c7a9504057dfba6c71fdfbeff1cef945d8f087bd106b5bec7478ad51f", - "0x99cf5e0e3593a92f2ec12eb71d00eccec3eec8662333471b2cb3a7826b7daca2c4d57ffba18299189cf7364e2af5df6d", - "0xaf50f9ab890b7517ff1f1194c5b3b6f7f82eabc607687a8380be371a6a67b117aeb9b6f725556551b81f8117971706a2", - "0xaa352430887053602a54403bd0d24d6b5181b44aa976dfa190e21851699a88127dcc904c90a48ec44610056b5dcd36c4", - "0x964c821ea1902354736fa382a929c156bd67b9468d6920d47c27b9d0d304b6144118888d124c1f6785da596435ed2410", - "0xb2284a67af26b5f5aff87b4d8e12c78ab37c5eb6e92718fca8549f86f4f001b660fc4520456aff72c9bcddd686603942", - "0x83c54cbb997ea493dc75df4023071dce6da94268feaa2352373789616f012098270ba4fd60c791796a6f5062fb2cd35e", - "0x9143e8fee0b8f0f34c65c7750858093dcf165c6a83c026bfac2d5ffa746361eb4b6a14fdb43e403add901ac3735735a3", - "0x97d7748a5b278ee47b18c9e60689b12a0a05be47e58e78bf8c04b9e8b34e2e2f2d3ac3c25c76ab2e0a75e8a54777b7c8", - "0xb4e68f6f2d978a5411414c164c81ddb2a141b01ebe18c65a8626ca75d6432e5988310b50a888a78c3a0a242353525af5", - "0x8976f4cc3eaf2684718cf584712c4adaf00a4d9c521f395f937e13233b30329658b3deacfe7e29fac84c496047f2d36b", - "0xa40bcdf4b6e95f1535c88dddcbf2074ef2e746b7fd232bdfd2b88f2f6d4bbf21c6b263cf5fd3e12a03476f2f5ffe00d2", - "0x88c7b6337ee705acd8358ef6d2242d36b140afff0579a7784b3928a0c49698bd39c1f400e8a2e3eda5fbfb2e8f28fe51", - "0xa98612ba8b450a71d2075d51617ebeb7ca401ad3cbd9b8554850c65ef4f093ba78defb00638428c9f1f6f850d619287f", - "0xb7e71d3ffa18b185c1a6bd75668ff65d985efc0a0c19f3812cafde9adbfb59ffd108abeb376e6a8877fdf5061562f82b", - "0x8a3e5fd776cc26908a108a22b1b122d60cb8c4f483cbedcd8af78a85217bb5a887df3efed2b8b4ec66e68eb02a56ca93", - "0xb0d92b28b169d9422c75f9d5cb0a701e2e47b051e4eacd2fd1aa46e25581a711c16caf32f40de7c7721f5bf19f48b3f5", - "0x88895739d5152282f23e5909cf4beebda0425116eb45fc5a6a162e19207686d164506c53b745fb2e051bb493f6dbad74", - "0xadbccfed12085cd3930bd97534980888ee564dda49e510c4e3ca0c088894855ef6178d5b060bca8a8a1a427afdbec8a8", - "0x87d00674abd3d2e7047a07ed82d887e1d8b8155635887f232dd50d6a0de3fb8e45b80b5a05bc2ec0dea9497b4aa783ac", - "0x806e1d3dfadd91cbf10e0d6a5e61738d0dbff83407b523720dce8f21f8468b8a3fc8102acf6ba3cf632ca1cb2af54675", - "0x95a9dff67cf30e993071edede12623d60031fa684dfbe1654f278a1eb1eb7e1be47886d3f8a46c29b032da3176c0d857", - "0x9721973288384c70a9b191436029e85be57970ad001717edc76d44cbfa0dff74f8af61d5279c5cd5c92c9d0f6c793f63", - "0x95c22d1d9b51ef36ba30ee059dcd61d22be3c65f245d0a5179186874219c08e1a4266f687fc973e71f3e33df2b0f7fd3", - "0xb53ec083dd12cc42ae2bae46883a71f2a35443c9ce4ed43aa341eb5f616a53b64211ed5aac717fe09ef1d50f551ed9f0", - "0xa103dab6695c682400f60be8d5851ce07f12e4bd9f454d83b39c41ddcf1443bb14c719b00b4da477a03f341aa1e920cb", - "0xb522236988518e5363b1c4bb3f641ff91d3d4c4d64c5f065415b738160b4ce4b0c22e1e054a876aa6c6a52fa4a21dfa2", - "0xa6a00562f0879702cdba5befd256a09f44bf48e61780e0677ff8c3fda81d8e6dc76ba1b05e3494ca9a4cef057eba6610", - "0xb974a2ae631e0b348421f0cda5bd4ce7d73c22dd0fc30404c28852c33499818cab89fbf5c95436d56a0aab3bf2bbab51", - "0x9148cf2a7b7e773245d4df5a9d34cf6d9d42b1a26a4ca6bc3013feca6f3941d6c44f29ba9328b7fe6ce6d7f6565f8e4a", - "0xa34035c4a63e98528a135cc53bbbcfcda75572bc4c765f212507f33ac1a4f55563c1a2991624f7133c77b748bbe1a6da", - "0xa0c45923cfb7bd272ee113aecb21ae8c94dda7ad1fe051ddb37ab13d3bb7da5d52d86fff9f807273476c24f606a21521", - "0x81ec2ca57f4e7d47897d0c5b232c59d7b56fe9ce0a204be28256a7472808de93d99b43c824a0cd26391e6cac59171daa", - "0x8373852f14a3366d46c7a4fc470199f4eebe8ee40379bd5aae36e9dd3336decaead2a284975ba8c84d08236e6b87c369", - "0xb47e878a93779f71773af471ba372cb998f43baca1ae85ea7ff1b93a4dee9327e2fb79691c468ec6e61ab0eae7ceb9f1", - "0x8fc8f260f74303f26360464cfef5ee7eebcbb06073cef3b1b71dab806d7c22f6b3244ce21d0945b35c41f032f7929683", - "0x87e3c4e1dab00596e051ce780b9a8dba02ecdc358f6ddaeb4ec03c326e4b7da248404745392658eb1defff75b1ba25c8", - "0xaac95d8e3b7fe236a7ca347d12a13ec33073f2b2b5a220ecfd1986ca5c3889f0e6a9d9c377a721949aa8991c1821953a", - "0x91a483679437ae126a16f5dc3bba6e9bb199dfbba417f0dc479f22819b018c420edc79b602db6183c6591b1909df4488", - "0x94a4b2c663aa87a2417cad4daf21a88b84983a7b212ffcd18048a297b98e07dd4c059617136976fac1d9e94c8c25b8d2", - "0x83e2a690bfa93c79f878a63c0f69f57aabdd8bede16b5966ffba7903dc6ad76775df1fd5347e6f2825f6cd7640f45a45", - "0xa316af7ac11b7780d15312dc729499a1a63b61c4283e103ecce43c3b0cbb0f4bce6ff04e403f5c7cb670dee80c75ab99", - "0x8d0a911c54ee1f9f7e7794732ad87b434c3f356294d196a5e35eac871727fd32a49c27c2dfa10833f9e6f9c7ccbe0064", - "0x8b8db09028298a1f6362b346c8bfeced7cb5d13165a67c0559a9798a95b7a4a9810c02bb852289d47c59f507bd24ce77", - "0x962d57305c518f175ed5d0847fb52ddc4258ca0e4c9ddfc8c333a2ee9f8b4e48d25a3d7e644b785a5953e2e4063da224", - "0x92e0799491898271769250fe88b0cb9dadec98ac92f79de58c418d23ef8c47fcf21ddc90e0cd68bb8f1deb5da82da183", - "0x99855067125f6a6c3a3e58d3bd2700a73ef558926bd8320d2c805a68e94207b63eda6bdc5a925ec36556045900802d51", - "0xa724ae105ab4364a17ddb43d93da1e3fc6b50213f99b7be60954b24dc375c4f93a0737f4a10b4499b6f52667d5f3a64e", - "0x82070fb43a63fb50869b118f8940108f0a3e4cc5e4618948417e5cc3801996f2c869d22f90ca4ca1fdbef83c4778421a", - "0xb25c04365d6f24d5d3296c10d85a5de87d52a139ddbcbf9e0142074bc18b63a8bc5f5d135bd1e06c111702a4db4cee28", - "0x851093282dcda93e5c98d687a17a7ee828cf868f6c85d372d9ae87f55d0593d8f9f0c273d31f7afa031cf6aea6a7ef93", - "0x93f04f086fa48578210ed207065d80a40abcc82d8bfc99386a4044561d35748ff6c3da6489933c23644ad4b60726da8a", - "0x84b1b50d1e876ca5fc341bbedab5b3cc0f6a3f43ea7dd72605f74d0d9c781297b2f12b7872dd600924f1659a4cdf8089", - "0x81b0ba88c582d3956f6b49ca3e031c6400f2ec7e1cd73684f380f608101e9807f54866be0bb9a09c03953c4c74fbb3c8", - "0xa641af6ac644c41a55dee2ef55d3c37abdb19d52bc1835d88e7adda6b6ccd13987c5fd9cba9d318cabb541aa6a0c652e", - "0xa7b75b0624d04ad0901070e691eb2d2645b60f87e9d6b26e77a5fb843f846c32fc26e76ae93fd33fe3b857f87bc25162", - "0xa81ba3e2ed0f94c67cd02ba7360e134f8becf7ed2ed2db09b9f5ef0942f7073bfee74ca446067db6092f7b38f74ccc11", - "0xab80edcabab5830a24210420f880ebac4e41bf7650c11ba230f4889634dbf8e8e2309f36be892b071c67a3bab8fc7ed6", - "0x94d69b64675076fecad40fae4887fb13a8b991b325fa84e9d2d66e3b57646de71a58ad8fd8700fefb46975b18289250b", - "0xb44fc0df480cd753a041620fa655be9df74963ae03d4625847d5bb025ceb37f48d19c8c9c444546fba5fe5abb2868506", - "0xb56e2c51324d6200b3d9781b68b5b5e1617a68afccd28b3a12a4be498d2e3aafcd86514c373a9f3a001db733010c29cf", - "0xa359a0c172e5cd7ce25080dd2652d863d7c95a4a502ae277ac47f613be5991300f05978404a0acb3bcda93524dcf36e4", - "0xb01427a3dfdf8888727c0c9b01590b8ae372b7b4080d61e17ccb581bac21e61c4a58c75db7a410d1b2a367304e1e4943", - "0x95cb08be4a96c18fbf9d32a4bbf632242029d039a5fdea811488d3634cd86520d4f9806250a8c01855ee2481210f542a", - "0xb8594fe6c0717164058f08aedeed1853523f56cec5edbf0d2be271fa5e8bfd61f2974b0f3988d70f5baa2e7888c7ec1f", - "0x8f64ee89f59daf74fa1056803247c9d678783ee3917b12a201f30f7523957763e979ceaddb38bae20de40b9885728049", - "0xb6093ee4bdb837bcc59172e236f4bdbd439c0a5a50e2aa16636cbff81b51e92989eb5f80a3f75c37ae7b5b942e55b3d2", - "0x913b6fbb7b43e3e5c49e96cd8e82ed25c655e51c7b8ca82e8fbf92b01ac83c39d52f6f4efab5d39b0591a0538601a86f", - "0x81f42668479ca0bec589678dc0973bf716b632578690efe1a0f13de630f306fb4a189a98c2302572fd85d3877ee030b5", - "0x90ff89c38a9a7189f28d35a088657f52283670e7fec842fa91c265660ea2e73b0ad6c46703d649f406f787490b7a7e4b", - "0x9077b8b5f1e083183f3152ceb9c5491b5d4b86525a08879f7fb6d5e27f9f1a6867cf0d81b669a4a2d1f1654b67fa8d9c", - "0xa7a0275cf5b894adbf2e54a972310cfe113e811872111d6ee497d03750d9f6ffa5517b6c13a99b111a4a91e8e4dfeeee", - "0xa08976bf8125b7538313a584bbe710741d630cab067a204ad4501cc4938874ce7aa6a1a826259c2e82ef10a66f1f36fa", - "0x8aa45385b5b97f1f3e45f2bbf7a4f3e8ef068e628608484971c97adeb610ebd5deec31317e03eb6536808921062c04db", - "0x945b106b8f3ae85e60dfd34ef3dcc079bc6f0aab6df279ed000856efd51321462038ac0a1ca5db3ebf6379bc341e7c55", - "0xa4199c87a96f98cc9d8776fe6de131d2c706b481eb9e9a3bbc50a93d492d7fd724ea469f723fbcfb94920cb5b32c1d76", - "0xa5347b1b2f6149805de67546c5ed72253311099bf1473dbc63edcf14a0a5e68d401f5341338623fbe2e2715b8257e386", - "0xaf5dcd03ddc3769e83351d6b958d47a06d4e5224bd5b0ec40ffe6b319763fab8572002f4da294a9673d47762fd0e6e1d", - "0x82ec1031b7430419d83b3eea10a4af4c7027f32b91c3ae723de043233b4a2e0c022c9e0f5a1ac49753800f119159112d", - "0x8a744d911b67d03b69811f72e9b40d77084547e4da5c05ff33893468b029a08266fc07303f7005fd6099683ca42b3db4", - "0x93ab566bd62d3439b8fc620f3313ef0d4cb369f0f0c352cdaf8e5c9e50b9950ac3540b72f4bf5adcb9635f9f7ce74219", - "0xb2a211d72e314799bc2ac7030b8bbb8ef4c38ebd0ebb09d6cbd43bd40c6c61d80a3aad02cc73f5775a08b9657da20a48", - "0x98d60f0a98d28718e0c6dcccc35a53521ea7f2d8fe08ea474374a336b44cea4cd1c63b31f2ad10186822bfb54aca53e6", - "0x831f89cb94627cfe554d46ae1aad8c1cde7ebe86c4bd8fac4ef73ac2d5b491f5efa5dc4198cb8ffbec563e0606b91d89", - "0x8f8552583bc6cb3fb176b7202236ee4128faf0c8ec608f9150f8e011d8c80b42aab5242c434d622b6d43510eaef752c0", - "0x897bf27baaee0f9a8445200c3d688ae04789c380d1b795557841606a2031092328eb4c47fef31c27fdd64ba841d9d691", - "0xb57589a4af8184b4a8ceb6d8657a35522672229b91692c1cec3ac632951e707922a00086d55d7550d699c4828bcfaab1", - "0x98c2fe98095e026aa34074bcff1215e5a8595076167b6023311176e1c314b92b5a6d5faa9599d28fca286fadd4e3b26c", - "0xa034992e563bd31ede3360efd9987ecddc289bc31046aa8680903bb82345724805e6f6cf30f7889b6b95cf7319c3aea1", - "0x85c33d9f10cc7185f54d53c24095e621966065e0ff2689a9aa6bb3d63706796c37a95021738df990c2c19493c0d44b64", - "0xa8c1247d6de2215f45b50dd2dc24945ff9b93184bcc2159b69703b0bba246adcd1a70a12659f34c4ca4ba27dea6e3df5", - "0x83ebdad2834c97bf92aac8717bab2f5cb1f01026b964d78e2f3b44e99d7908e419165b345d2b2f125b903096584e6683", - "0xb0af6f7f81780ceb6e70adfd98e7702ec930c8ca854b50704c4a0fc8b887b9df60a6fe9038b487f3ed0eb8eb457307ea", - "0x933ec7e53882453898617f842ab2efae4756eb6f6ea0161cced5b62a0cdde4c08c7700d52f7546d4dd11a4c9e25d624e", - "0xadf6e6d4706025f85eb734f506dde66459c9537a1abf6189199cf219ae583b461e11c6242fce5f0795e4d9025270fabf", - "0x89e4316319483098761b0b065df4cfb542963b7a2556ba5425b6442fb0e596eb2a4f03e2dc8c617eebe8f243a12e7d10", - "0x90c5a147555759ebc4d0e15e957a548315f9994ef0c7a3f53f2d18da44fb93bf051d96ba8551597a6f3e701b926fd791", - "0xa151a9a5199c72c697b771cd81e550fc6f9596c752ae686ad988b316a7548360cf9785ab4645164d96cfdf9069a94020", - "0x80cba11a3977729d7948db5bcc186159f4cae7c0a835bb38bb781e287dd6c238508e748f23454405c9d5eed28e77df02", - "0xae4b92ea03cb8ad12ad3ec76869ad05acb09f9d07a3c9a87dec0e50d9a276fe5d3d515a8c446f3aa35cd7d340a22c369", - "0x8630062709a1f180f952de9f1ca3f41acce5420677f43d9619097e905a6237f1908d66db7a4dfdf1b2b92fb087e9944f", - "0x81defc33dd383d984c902c014424bddd5e53b013f67f791a919446daa103b09b972fa5242aba1b1dbe4a93149373f6c3", - "0x963891ecaea97e661bac2594642327a54f5a0beb38fcb1c642c44b0b61faab9c87b0c9f544a3369171b533d3ab22f8f1", - "0x932fadbff5f922ddcd4da942d57fe3e6da45c3d230808d800a3ca55f39b0b62f159be31a5924b395d577a259f48c6400", - "0x992ce13bd037723447f88aeb6c7722fd9510c7474192b174ea914ed57c195c44c298aec9a8cabac103f0a5b50051c70b", - "0xb032157b3e4fe69db6ce6bb10bdf706a853fbd0bee08c2ab89da51ad827425df5df498b90e7a30247a7f9e954ca986e5", - "0xb2478d4874578da3d5000893736bb65712e6aafe96e6fa5cf5878ae59ba0ce640dbe5d76ec2b5baca75af57def471719", - "0xa387c17b14dd54910fecf472f760e67cf71a95e9e965cc09484e19581ada65e79938b86136a93e287e615fbd4908e080", - "0x98f02be271d0f8841d8d561163f9e55e99b57aff121a93fba7a4654bcf15a0899811f00f5bcbfbebd98e365a0e332e97", - "0xa3c34f01d54cab52a8890391b8cf152cc9cdc16e7e53794ed11aa7b1a21e9a84d39ddcfbcb36c5df6891c12307efc2e0", - "0xa940331f491ec7ad4a9236ca581b280688d7015eb839ee6a64415827693d82d01710dc4bbd5352396be22781fea7a900", - "0xb10874ed88423731535094031c40c4b82af407160dfade4229ac8f4ef09d57b3db95c4a9d73c1a35704f6bd0d5f6c561", - "0xa9c5a4a7680261c1b0596f8ab631d73d4a7881b01e6559c628b5cdafa6dd2b6db2db64f3f2ab5841413a8a52b966a0da", - "0x8fc154564a61d5e799badc98b43a3587f804385a850adce9a115cbd2ad911f3fd4072b8e6b22fc6c025a6b7e7ea5a49f", - "0xb9caf7c6dcce3d378aa62c182b50bc9c6f651eb791d20fffa37ef4c9925962335fe0b3bc90190539312aa9ccf596b3b9", - "0x90c5b7acf5cb37596d1f64fc91dee90f625f4219fa05e03e29aebea416c8e13384f2996f8d56791bcf44ae67dc808945", - "0xab8d311fc78f8a1b98830555a447c230c03981f59089e3d8a73069d402a3c7485abe3db82faf6304aaca488a12dbe921", - "0x8a74fda6100c1f8810a8cacc41b62875dd46d5c4a869e3db46202d45a8d9c733b9299dda17ce2ad3e159122412a29372", - "0x8769dcacba90e6fc8cab8592f996c95a9991a3efecfb8646555f93c8e208af9b57cf15569e1d6e603edac0148a94eb87", - "0x854fd65eea71247df6963499bafc7d0e4e9649f970716d5c02fbd8708346dcde878253febb5797a0690bd45a2779fa04", - "0x83e12dc75ef79fd4cc0c89c99d2dace612956723fb2e888432ec15b858545f94c16fae6230561458ceee658738db55ba", - "0x8416ef9ac4e93deff8a571f10ed05588bef96a379a4bdcc1d4b31891a922951fa9580e032610ac1bb694f01cb78e099b", - "0x93aea6e5561c9470b69d6a3a1801c7eef59d792d2795a428970185c0d59b883ab12e5e30612d5b6cde60323d8b6a4619", - "0x91d383035aa4ec3d71e84675be54f763f03427d26c83afb229f9a59e748fb1919a81aca9c049f2f2b69c17207b0fb410", - "0xb1c438956f015aef0d89304beb1477a82aed7b01703c89372b0e6f114c1d6e02a1b90d961b4acbb411cd730e8cacc022", - "0xa1ee864a62ca6007681d1f859d868e0bcd9e0d27d1da220a983106dc695cb440980cfdb286e31768b0324b39ae797f18", - "0xb57881eba0712599d588258ceada1f9e59c246cc38959747d86e5a286d5780d72d09e77fd1284614122e73da30d5cf5c", - "0xa48f9ae05ba0e3a506ba2e8bbce0d04e10c9238fa3dffa273ef3ffe9ec2ed929198a46507c0c9d9b54653427f12160f9", - "0x8db18da7426c7779756790c62daf32ae40d4b797073cd07d74e5a7a3858c73850a3060f5a3506aae904c3219a149e35d", - "0xa2bf815f1a18d7be8ce0c452dfc421da00dcd17e794300cdd536e4c195b8c5b7ccc9729f78936940a527672ac538c470", - "0xa34c6f1f2398c5712acc84e2314f16d656055adcafad765575ae909f80ab706cf526d59e5a43074d671c55b3a4c3c718", - "0xb19357c82069a51a856f74cbb848d99166ce37bd9aca993467d5c480a1b54e6122ebddb6aa86d798188ea9f3087f7534", - "0xb440eac6f24d12c293d21f88e7c57c17be2bdb2a0569a593766ae90d43eccf813a884f09d45a0fb044ee0b74ff54146a", - "0xb585d42ef5c7f8d5a1f47aa1329f3b1a566c38bf812af522aa26553010a02bfd6e9cc78fdb940ef413e163c836396a5f", - "0xaca213b27f3718348e5496342c89fffc7335f6792283084458c4a1aa5fe0a1e534fcec8e7c002f36141308faae73ef2a", - "0xb24c07359769f8ffc33bb60c1f463ea2baad440687ef83d8b7c77931592d534b2c44953c405914ace5b90b65646c1913", - "0xb53dfaf381205a87ca4347328ff14a27541fa6436538f697824071d02d4a737ceb76a38dcc6e8dadef3b5bc6442f5109", - "0xb55972d8ed5197215c0a9144fc76f2cd562ca5f4e28c33a4df913363fd1388978b224c44814adb4c065c588a4ac1fe10", - "0xa3303bc650e120c2e9b8e964ad550eb6ac65ffe6b520768b3e8735565ae37eafdc00e3c15fae766d812f66956a460733", - "0xb11e53912ea0e40c3636d81d7637e10c94cc7ed9330a7e78171a66d02b7603f4cb9b3f6968104b158de254e65b81640f", - "0xb076bb9f6d396aa09c2f4706ea553b426fdfd87d7d69e438285b74d334e82f73973cb4dbd6cb1647493433dad65dbc41", - "0x9415828b1632175f0b733541e32c26a9c88fe12c721c23e595f2efceaa7f867f359e32564b7c032185686587ac935cf4", - "0x89579a112c306181c79aabdbf683e7806357febcb73bf5e8883862ae29618ef89498b62634404bb612d618fcd16da415", - "0x8761bcd55d04297c4f24899e8fb9f7c1fcd7449ae86371ee985b6a262e228f561c2584980694d9bf354bdf01543edb6a", - "0x9100c88bf5f6f00305de0c9cf73555f16a2016d71c50cb77438e8062bd549fa5407793a8a6a7e06398756777680a2069", - "0x9235dfef45aeff9c174898b0755881b7171ed86362854f0eabc3bc9256176c05a5dc27ca527c91c3fa70c0ec5fd5e160", - "0xac53b1d677cebab6a99381dd9072b8ac1abae9870ec04a1f8d2a59b6f1de797c1492b59af6948f5cf2b20599170f5bba", - "0x946542936b0c59156e8fd5c1623b41369bc2cbcc46ece80360dcb5e7cce718a3dd8a021f0b9c223062a4e43d910b634f", - "0xb1e9939b34e1fcc026e820fcfa9ce748b79499f8e81d24a3ef0457b3f507fe5fa37b975a47c143e92eb695623b4e253b", - "0x9382d9b5766f6ae960d8a8435e8b5666e57ef8e5f56219e7bfd02857afe5cb16f44d70a9e444cfb1008649ae9b863857", - "0x91770ed1215ed97dca1282b60b960be69c78e1473edb17cd833e712632f4338ff74bf435c3b257439497c72d535ae31f", - "0x8eb2cbe8681bb289781bf5250e8fa332141548234c5c428ff648700103a7cd31fdc2f17230992516c674aa0ab211af02", - "0xa823b71c82481bc6ac4f157d5c7f84b893a326bbb498c74222427ded463d231bc6e0240d572ab96266e60eb7c8486aea", - "0xa13ce4f482089d867e5babcd11c39fa9a9facd41a2c34ee2577de9ce9c249187e16f2b3a984cc55f9e45b9343462d6d2", - "0x8d80e7bc706059cf5151f9f90e761b033db35d16b80b34dc8b538adc8709d305a0c06933dcd391e96629cf3888c8bf87", - "0xabcd36cdd86c0fb57fb7c0d7a3b9af5fd9aed14e9f4e7e84b0796c5c0ad18c41585e8c46e511cef73dc486fe43f6a014", - "0xa947a5b6916f416fa5a69c31aba94add48584791148b27d0b3ed32c02a05dfc06f7fdc5006e3b2503bdf6e410e30f2fb", - "0xb158e621580659f1fa061d976b8591ac03b53ecd23d9eb2b08c1a20353d78438287749664d196020d469ef44b3b8752e", - "0x90a5a9540281e481ac4b8d29968f477cb006b56bd145529da855d65d7db0cf610062418c41a1d80c4a5a880c0abe62a0", - "0xb2c91808b6289d08a395204a5c416d4e50a8bb1a8d04a4117c596c4ad8f4dd9e3fb9ce5336d745fc6566086ae2b8e94f", - "0xaf6767c9b4a444b90aeb69dfddae5ee05d73b5d96e307ce0f3c12bccca7bc16475b237ba3bc401d8dafb413865edf71e", - "0x8dcecf624419f6517ef038748ac50797623b771d6111aa29194f7d44cfb30097ced26879e24f1b12a1f6b4591af4639b", - "0x954437559d082a718b0d6d7cec090532104ab4e85088e1fc8ee781d42e1a7f4cdb99960429707d72f195ff5d00928793", - "0x80f0b7d190baa6e6ab859dc5baab355e277b00ddcca32e5cebe192877ad1b90ead9e4e846ca0c94c26315465aeb21108", - "0xb8c29f181ed0bb6ac5f6a8d9016980303bb9a6e3bd63ce7a1a03b73829ac306d4fab306ac21c4d285e0d9acb289c8f2a", - "0xa7685079fe73ecaeabf2a0ef56bad8b8afb6aeca50f550c97bf27e6b4a8b6866601427fcd741dc9cb4ce67a223d52990", - "0xada2ebf6f2a05708d3757fbf91365ec4d8747eb4c9d7a8728de3198ceac5694516ab6fd6235568aecd8d6d21fef5ef48", - "0x846bc5da33d969c53ab98765396cab8dcdbb73b9836c9bda176470582a3427cb6de26d9732fab5395d042a66bdba704c", - "0x800a3a7ea83ce858b5ebc80820f4117efa5e3927a7350d9771cad9cb38b8299a5ad6d1593682bba281c23a48d8b2aa71", - "0xa002b18595dec90b5b7103a5e3ec55bdd7a5602ee2d3e5bd4d635730483d42745d339521c824128423dfe7571e66cbaf", - "0xb6b4e2067ac00a32f74b71007d8ab058c2ef6b7f57249cb02301085e1a1e71d5de8f24f79b463376fd5c848f2ab1c5bc", - "0xa3e03036db1b6117efe995bf238b0353ad6f12809630dca51f7daaaf69f7db18702e6b265208944bfb1e8d3897878a51", - "0xadd16712f66d48aab0885bd8f0f1fb8230227b8e0ffca751951c97077888e496d6bfab678cb8f9ffba34cee7a8027634", - "0xad211af2dd0748f85a9701b68c19edd4a7c420e497cb2e20afdc9df0e79663841e03b3c52b66d4474736f50d66c713ce", - "0x8c8a899ce0f16d797b342dc03c2212dda9ee02244c73c7511626dba845d11a0feb138441da5459c42f97209bf758cd9b", - "0xa17efc75c7d34326564ec2fdc3b7450e08ad5d1de4eb353de9d1cd919d90f4be99f7d8e236908b1f29cf07ae1ffe0f84", - "0x862d4a8b844e1b0dd9f4deff180456ebed5333b54290b84f23c0ddb2725ac20307e21cbb7343feac598756fe36d39053", - "0x9187fbb19e728a95629deda66a59e178f3fcd6e9d7877465aa5a02cea3baba2b684bd247b4afbf4aa466b64cb6460485", - "0x85ae5636688d06eab3be16e44fe148515d9448c6123af2365d2c997f511764f16830610a58d747adab6db5031bea3981", - "0x8aa8a82891f4e041ce6df3d6d5d7e5c9aaaffe08e0a345ac0a34df218272664c1b7be2450abb9bc428bd4077e6e5dcc4", - "0x8c3bcc85ea574dfe1b9ca8748565c88024e94374434612925b4e9a09fa9d49c0a56b8d0e44de7bd49a587ef71c4bff5f", - "0x9524f9dd866fe62faf8049a0a3f1572b024120d2e27d1be90ad8b8805b4e2c14a58614516281cc646c19460a6b75587c", - "0x84580d9c72cfa6726ff07e8d9628f0382dc84ce586d616c0c1bd1fd193d0a49305893eae97388de45ba79afe88052ee9", - "0xb5573e7b9e5f0e423548f0583423a5db453790ab4869bd83d4d860167e13fd78f49f9a1ffe93ddddf5d7cd6ec1402bc4", - "0xaff658033db3dad70170decb471aee2cf477cf4d7e03267a45f1af5fd18200f5505c7ce75516d70af0b0804ec5868a05", - "0x84a0eab4e732a0484c6c9ed51431e80cea807702fa99c8209f4371e55551088a12e33a11a7ef69012202b0bc2b063159", - "0xa68f8e730f8eb49420fe9d7d39bb986f0584c1775817e35bb3f7dae02fd860cddf44f1788dc9e10d5bf837886b51947f", - "0x946002dd6cf7a4fd3be4bf451440e3f3fd7e9b09f609fa4e64767180b43146095dfc4b6994287f8cfa6d1390d144be71", - "0xb7f19777d0da06f2ab53d6382751dc5e415249d2c96fce94ef971401935c1d1f7d3b678501e785cf04b237efe2fe736e", - "0x81e5c66dd404fc8ffd3ac5fe5e69ead7b32a5a7bc8605a2c19185efcc65c5073e7817be41e1c49143e191c63f35239c1", - "0xb5f49c523532dfa897034977b9151d753e8a0fc834fa326d0f3d6dacc7c7370a53fc6e80f6d5a90a3fbec9bbb61b4b7c", - "0x8fc8e78c07319877adfaa154a339e408a4ae7572c4fb33c8c5950376060667fbfc8ede31e1b067933d47e3fdbf8564d7", - "0x859cfef032a1a044532e2346975679545fbb3993a34497ce81bdcc312e8d51b021f153090724e4b08214f38276ee1e0d", - "0xae476722f456c79a9c9dfdc1c501efa37f2bff19ab33a049908409c7309d8dd2c2912aa138a57a8d5cb3790ca3c0ba2f", - "0x89acbbeffb37a19d89cfe8ed9aa8b6acf332767a4c54900428dd9ab3bf223b97315aca399c6971fe3b73a10a5e95a325", - "0x90a4a00418fdf4420a4f48e920622aae6feb5bf41fd21a54e44039378e24f0d93ccc858d2d8a302200c199987d7cb5e4", - "0xa3f316b0bd603143eba4c3d2f8efe51173c48afe3c25b4ca69d862c44922c441bd50d9a5040b7b42ba5685b44071c272", - "0xa22f4dc96fedd62b9a9f51812349e04d42d81d0103465c09295a26544e394a34abdc6ded37902d913d7f99752dbfb627", - "0xa49f51baf32d0b228f76796a0fef0fe48a0c43ec5d6af1aa437603d7332505be8b57b1c5e133bc5d413739f5ae2ce9d0", - "0xa9e4fe133057a0cd991898e119b735b31a79811307625277c97491ff5d864c428cfa42ae843601d7bb05c0313472d086", - "0xb987edfe0add1463a797ff3de10492b2b6b7ef0da67c221ab6f0f2b259445768a73fbe495de238c4abbe4d328e817c49", - "0xb7f0e4532a379a4c306bbef98b45af3b82b17175dfe0f884222ed954c12f27d8a5bdd0cdeb1df27ff5832ba42a6dd521", - "0x9471bc5ad5ec554acfd61b2eb97b752cb754536f95ae54ca2cbd1dc2b32eb618881f6d8a8b2802c1a4e58c927067d6cf", - "0xb4c84f09225cf963c7cc9d082efe51afbbbe33469dd90b072807438e6bde71db8352a31bb0efde6cd3529619812ef067", - "0x8f08005a83e716062d6659c7e86c7d3b51e27b22be70371c125046de08f10ea51db12d616fbf43e47a52e546e7acaac7", - "0xa8937e66a23f9d9b353224491f06e98750b04eca14a88021ee72caf41bdce17d128957c78127fba8ef3dc47598d768a7", - "0x80ad991de9bd3ad543cddeaa1d69ca4e749aaefb461644de9fc4bd18c3b4376c6555fc73517a8b1268d0e1e1628d3c1f", - "0xb22f98bca8fe5a048ba0e155c03e7df3e3cee2bfe8d50e110159abdb16b316d6948f983c056991a737b646b4d1807866", - "0xb0bb925c19ca875cf8cdbefa8879b950016cc98b1deb59df8b819018e8c0ad71ea7413733286f9a1db457066965ce452", - "0x95a991e66d00dd99a1f4753f6171046a5ab4f4d5d4fe0adfe9842795348a772d5a4a714dba06b4264b30f22dafa1322f", - "0xad91e781fa68527a37c7d43dd242455752da9c3f6065cd954c46ae23ce2db08f9df9fec3917e80912f391c7a7f2f7ffa", - "0xa202d3becbf28d899fe28f09a58a0a742617c1b9b03209eca1be7f072a8ada1f7eac2cc47e08788d85e1908eb9d3d8ee", - "0xa360ccb27e40d774d5a07b4ebed713e59a0d71b3ee3f02374e7582b59ec4a5ce22cc69c55e89742ba036dd9b4edd8f34", - "0xa10b897a946882b7c9e28abbb512a603ffa18f9274369843eb3491524a321df1f572eea349099ac6e749ea253c901ea0", - "0xb782a672cd344da368732ecd7e0a1476c2af04613d3eb6da0e322f80438af932bd6d49be7a6f69f7c877512731723d89", - "0xaeccee8dfd764e1adcfc4bf669e0fa87a94e7c79324333e958df47888bff5cec358b8b5bbb48db54822b54d11bbb4bc6", - "0xad4953913662a9ee8753a354864339f43916f2c2390d0a3f847c712b42718ee00ee14158d730709971941e8680d54560", - "0x92ccb31d6c9e8940c7e8a4873e7eb9de9fb2fa2bac344fa367062ea451fd49a6920a45218dca3ee968711397d2a01536", - "0x9448d9b2b3d12dde9b702f53373db8b8595f9d1f9de2ebee76de292f966f375316953aadf6bfc0e4e853e1fa12d8f02c", - "0x8919230878a7219da8c80a4b7d00b9169fb503e72d79789dd53863c243b8d0fb0a819d46fa636d805d0b9b1d15d1f2d9", - "0xb6581ab01215aac023f5e6f57419b6aa63c0743c07caf57d4e146b56b02d90ce1423f70489ac3a11e5c968cb924f937c", - "0xa793ec1b1fe56a76920296af06073caadfd6f1d7e30950f8ca13de3de45fe275ca4b361f5249d9405264c3a06ebb5502", - "0x86385b4a4e1bfb5efe7bfef8fd0dfeba7f4400852237cab60febb1dfa409e497a649e81284b5a15fe680b78927256756", - "0x85d10600de96103daa7c90657174b6cb4a1286df5379f1eda9f11c97f9df57043c290eb1ae83658530fe0fd264867b86", - "0xae01b2396d0f598c21659cd854c15edd4904a34d22278aef97c9260a14a8b250b52d972d304ac4b187c24d08795d5355", - "0xb91b3e4b6fc06e88081fe023ef1b773d82c628eb0f73a2731a9aa05b0dc89b7aeef2eea60125d302e696f45c407aeac2", - "0x986d0f478e33af7568eab6bb26a55c13ffd7cae27525b4abe2f3a994bdb11bbc73d59bdb9a2f6b6ba420a26f8f620ba6", - "0x9746f4fdeef35feaff1def0ea5366b64f21ed29749ae6349f9cb75987e7f931952f913f446100f2a6b182561f382e8eb", - "0xa34a116cfde1acbce0d7de037f72a7ca30ab126d8f4815b2b8bcb88e0e6c89015a4daaf4d4ce8eae23eb5d059cf9a5cf", - "0x80c3ea37f6a44f07cc9c9c881990f2a5deb9f9489a382718b18a287aa3c50ee6ebe8fd1b3afb84a3cf87f06556f4ca15", - "0x97cff3bc88cfc72ce5e561f7eeb95d4ffb32697e290190c7902e9570c56b3854753777fc417fd27536fc398c8fefb63b", - "0xb8807232455833e4072df9bffa388ae6e8099758c2a739194719af7d9ed4041974a6cd9605f089de8b43f0e12f181358", - "0x96f79fca72f75dc182c71f2343f0c43b06d98563fd02d2e1fbc031b96601608d8a726c811a74bb51ab8b0a3ce3632dc4", - "0xb5262761680a4235a8c1257de4735cdcadf08d5d12c6e9d4f628464d5c05dfff3884a9ef2af3b7724b5a8c97e6be74eb", - "0xb6ce0eada73433d98f8fae7d55e4ea2b9d9d7a0ae850d328dd06991f27b1f03e470868fb102800ff3efe4ee1698531b9", - "0xa37b7d9fe9d3fdfbc72c59cf6cacc7e7a89d534dea3d73121f7483331aec8ab3fbff58ffabb943b75d6f86df0ba43262", - "0x93fce9be8a27fcaa1283d90d3e87265a6221ee302ec708161a42bd00ffe8e726743d9e187e1bf4307c0e3f25afbb1d44", - "0xa4ea919021346ae7ea69d5e8f46d860b24c35c676b62f4e577c90e0c05c5646fe73721b143b7c38835dd4b443e6c3676", - "0xb79983a5948453f70dfa4c396ce1945204498fe79f40c0667291bd0fdd96ed0b9ea424571f7ade342275c854c9f03d9e", - "0x866f8e395ed730b614b70bf999cad6e87e9086c1f5aea8d69020b562ee285dd0fb93afaca0dd13a0713f74a3f9340f01", - "0xa3fef158782292c6139f9a0d01711aa4ed6f5cac11d4c499e9e65c60469ae3afbde44fb059845973a4b3bbca627b7eb7", - "0xb4a2c0321b68f056e7d8051beede396fa2f0704d8aa34224f79f7b7a62eb485fc81889cb617019622fd5b5fa604516f5", - "0x8f0e3edddbaead9059df94de4139e3a70693c9ea9bc6baaa5695dddfd67263b33926670159846292801941b9a0c6545b", - "0x9804e850f961e091dadd985d43d526ba8054d1bf9c573ed38f24bbd87aeaad4dcba4c321480abc515a16b3b28f27bb2a", - "0x95f330da28af29e362da3776f153f391703a0595323585220712dae2b54362cc6222070edd2f0dd970acfbe2e3147d5c", - "0x82d03b771231179cc31b29fe1e53379d77b5273b5c0a68d973accd7a757c7584dbb37f0507cdfde8807313ec733a6393", - "0x81b3c39a9f632086e97b7c1f0ec7e2eaf9dc3cb0d84dec18a4441dbdc9fe9878fde4bcfa686bca1a9522632a353a5566", - "0xa2db124ab2b493d5f9a1e4ca6b3144593c2fc8bfac129fd79da11dfbb7ef410a234fda9273a50a5ca05d7b37cc2088a2", - "0xaa8550633c9449228702690cc505c0fc4837ea40862058e8f9713622b34d49fdc3a979b9317993c5da53b5bb5b7f4974", - "0xae783bcf7a736fdc815d0205b4c2c2b2fee0a854765228f76c39638ba503e2d37f1e28f6bdf263923f96fead76b4187b", - "0xb5ec86092c1d250251e93bab2f24e321afd2cd24cf49adfcbed9e8bc5142343ae750206c556320551e50fc972142f0da", - "0xb3b5791b590a6e9b3f473d5148624014aa244495249322a5d75cde2c64117ff9d32f4b0698b0e4382e5e7f72933061f8", - "0x876c6a9162c17b16d6b35e6ce1ba32e26aec7dd1368bceab261ab880ad845c91e54b96a52c7d3aafbfbafc0e37139dca", - "0x902ddb5774d20b0707a704486457c29048776a5b88c377b14af6616c8ddf6cd34f49807df9c9d8866d6b39685cfb0f19", - "0x8b87f71f94bc96de927d77a5d7123fa9cdda8c76aff64a5e6112cbc2eca43b07f8376db3e330f8af6a1db9b948908a6a", - "0xa69a5922e572b13d6778218e3657f1e1eea9a9682f6eb1b731d676d03563e14a37ff69bc5e673c74090ecb0969a593f7", - "0xaff3510d78ba72f3cf5e3101847b7c4a956815aa77148689c07864e8a12dd0ef33d5f6c8cb486e0ea55850161f6afed0", - "0xaa9c459cb2a008d94cbee2c6b561d18b0d7c6ffa8a65cbf86ae2c14eec070ee9d5324f5d38f25a945ddcd70307e964c4", - "0x8310e15b050b1e40ece7530b22964bde0fd04f48dfffdec5a0d1fb8af0799a7fdc1d878139fb7cb8d043d3a52c2d1605", - "0xb8f0856ce2c4034ee4041d0383f25fb0eeefc00b82443311a466fc18608313683af2e70e333eb87e7c687e8498e8a1ce", - "0xa8200a75c158fbb78474cab8a543caecd430b5d8b9964fc45d2d494dd938021cd00c7c33413ad53aa437d508f460a42a", - "0xa310091472b5b42b02176b72d5f8120bdb173025de24b420e3ca3fb9a386c39092a1d1bb591c6f68ee97a268a7ff9e95", - "0xb23f1bf8bcec9cb5232b407115eead855fd06f5bf86ba322ad61d45460c84f0f36911aba303de788c9a0878207eac288", - "0xae4c129ad6d08be44690bb84370e48bfd92c5d87940750ee2c98c9a2604456f7f42727ab211989657bb202f6d907df04", - "0x95992057d654f3e189a859346aa9aa009f074cb193b7f5720fa70c2b7c9ce887d886f6cff93fa57c1f7c8eaa187603f6", - "0xad12d560273963da94151dd6be49c665d7624011c67d54ab41447452a866bc997e92a80bdd9ca56a03528e72c456dc76", - "0x8e4eda72e9cfcaa07265bb6a66d88e9ce3390ae1a6b8831045b36ea4156b53d23724824d0f0bca250ce850c5926fa38f", - "0x980fe29c1a267c556532c46130fb54a811944bdfea263f1afcdab248fa85591c22ac26167f4133372b18d9f5cce83707", - "0xa7da9f99ddde16c0eac63d534a6b6776ad89b48a5b9718a2f2331dce903a100a2b7855cf7b257565a326ddc76adc71a5", - "0x8ca854c55e256efd790940cb01125f293e60a390b5bd3e7a60e13ac11a24f350a7eb5ebddfa0a2890905ca0f1980b315", - "0x9440335818859b5e8f180893a8acedceabaaa44e320286506721c639a489b5bfb80b42b28902ee87237b0bd3dd49552a", - "0xb9da545a20a5e7d60fd0c376dcaf4b144f5c5a62c8ffa7b250c53ce44be69c4e0d5e4e11422ef90593ae58ae1df0e5d3", - "0xb75852a850687f477849fc51e0479703cd44428671c71bfdd27fe3e7930b97d2fc55f20348ca4e5bc08db2fc16a4f23c", - "0xb515081d8d099e4b6253c991ca2d3e42633f5832c64aa8f9cde23cb42c097c2c3717c46c5f178f16c58295f97b2b3fe7", - "0x9506c9902419243e73d3197e407985dd5113f16c6be492651bbbf9576621942710aea74522d6fb56d5b52c6ccdaa4307", - "0x952673ae27462a0f6c9545eede245c2f8e2fd6077b72a71f5672f1a5a02c263bc2a66f24f0e30376feb7a8187b715f08", - "0xa8f1e2085ed666a8f86b474d9589dc309d5c83bd53e745f8e09abe0dfbaf53e5384c68580672990344d4aa739438b4d8", - "0xad6e04d4a67a5a5529ceaf7de6e19416be5b4c436610aa576ac04aee3b73317da88f891121f966393a37f52b775a2dd8", - "0xa35a884736f08c7f76923ae7adb17fdac04e6c505178bca9502eaa2ed16d4d93fa953fb6dcf99e9e9962a6eb3eeead00", - "0xb8af72273360bab4b3ca302cf0659717cbfb335fbc9ad4ffdd3340113ece9e63b2bdbd611e5f6b740a4689286f9a452d", - "0xb1a1f4ba2640800c3ed3892e049f6e10f8a571efa3bbe21fe2d6cee8fded171c675a3bb8aa121e2d1d715de84bad2e2b", - "0x8102a6c3598b40da4d6e8eccfdd5dadc8d6262e38b69c5b211b0732f4c6e3045d79fba12770a0b2b66f1e9f4664b1510", - "0x90979587d75bf12819f63832beea7dcbef101f6814bf88db4575bfcd9cf0ea8eceba76d4d6db17630b73b46c1acfe011", - "0x8dd98f14d2beb5b5b79cc30f6825ec11ed76bd5a8864593ffc0c2baffab6872bad182e1c64b93aab8dd5adb465fa5cec", - "0x8083334dadc49c84f936c603a2857f174eda5659ab2b7214572f318aba3ebd7b1c50e7cbea57272b9edf106bd016df3b", - "0xa634d08d2e8641b852e89d7ccab1bab700c32fb143bcbea132f2a5fb2968d74ded2af4107f69818798f0128cc245a8cb", - "0x94fc2dccf746d5b3027f7cf4547edf97097cd11db8d6a304c1c2ca6b3aba28c1af17c08d2bbb66f88c14472e0196a45e", - "0xb257a6fb01424b35e414c1c002e60487abb3b889d74c60cbdbf591e222739c6f97b95f6962842401f5e2009e91b28c55", - "0x81955bdbf25741f3b85d5044898dc76ae51b1b805a51f7c72a389d3b4d94b2e3e0aa1ec271685bbcf192ed80db7367ab", - "0x86eb229b66c542514e42b113b9de7d4f146861a60f2a253264873e7de7da2ac206e156ff11f2de88491b9897174fe2f4", - "0x8b8db00533afbb56b3d7d7a9a4a6af3cebb523699ffcb974603e54f268b3ef739c41cd11850b9651d9640d72217c3402", - "0x8b7cbb72a6c4408d5f1b61001e65de459790444530245d47d4ee8e2d17716695283f21540bd7ac4f5a793a0d00bdf1d4", - "0x875920b9bab4bc1712e6af89ae2e58e9928c22095026070b07e338421b554d9f96e549ac3706c6c8d73f502913a27553", - "0x9455d192db7b039b3e8f0bc186c25ff07dfbe90dab911e3c62e3bd636db8019ed712cbb0ecd5cbb9a36c11034e102aba", - "0x8cb0b28e5d3838d69f6c12274d6b1250f8843938065d0665b347977fa3c1c685caef6930bae9483ed0d0a67005baad76", - "0x94df2e14aae1ae2882ab22a7baf3dc768c4a72b346c2d46bfd93d394458398f91315e85dc68be371f35d5720d6ca8e11", - "0xaacd94b416bfbeb5334032701214dd453ad6be312f303b7bec16a9b7d46ab95432a14c0fbf21a90f26aafb50ec7bb887", - "0xb43d26963665244633cbb9b3c000cacce068c688119e94cc0dac7df0e6ee30188e53befff255977788be888a74c60fc2", - "0xb40d67c9ad0078f61e8744be175e19c659a12065fe4363b0e88482b098b2431612e7c2fa7e519a092965de09ceafe25c", - "0x82cd4a4e547c798f89ce8b59687614aa128877e6d38b761646d03dc78f6cdd28054649fb3441bcd95c59b65a6d0dd158", - "0xa058e9700f05cef6e40c88b154d66a818298e71ae9c2cf23e2af99a0a7dc8f57fbe529d566cb4247432e3c1dee839b08", - "0x95c6f84406466346c0b4a2a7331ac266177fb08c493d9febb284c5ca0b141ccc17aa32407f579666b208fb187c0227dd", - "0x905d1d47a26b154f44d7531c53efbc3743ff70bd7dba50c9b9d26636767b0ae80de3963c56d4604399126f4ad41a0574", - "0x83dfa11c520b4abaefe1b2bc1ce117806e222f373cd4fb724f3c037c228e3379d27a364e68faa73984ba73a0845f1b9a", - "0xa16e54786ba308a9c0241aff8f1bf785dece387d93bd74aa31de0969e3431479e2c0abebff9939a6644d2b0af44f80bb", - "0x81ac565212365176f5be1c0217f4e7c9fdbc9fe90f16161367635d52edcf57af79290531d2e8b585e1223d33febd957d", - "0xa296f4b09915e5d80ff7274dc3ffc9b04f0427e049ea4ef83dca91095275e8a260ef0335c7b6585953b62682da8c8e99", - "0xa9150626208168a21ae871192ca9f11c1f7f6e41e8e02de00732de2324d0d69fe52f8762155c9913ee408a034552e49a", - "0xa42a56008ca340c6e9ff5a68c8778bb899ba5de9e7508c0cac355c157979a7ff6a6bd64f98b182114d3831cfa97ee72b", - "0xa4f05adf22c051812279258eea9eb00956b04ef095f2ca175f775ff53c710fb0020266adabd1dacaee814c4f1d965299", - "0x967492e78ac0bceb8ad726ea0d2292b760043d16d64a6b1bb896e32630a7bf405c2b20e4e00842ae519a21697ff8db2d", - "0xadbf05e9b5931ae3dd24d105b5c523c221a486a4123c727069b9e295a5bc94f3e647a3c2cde1f9f45dbd89df411453c9", - "0xa1759c0ebebd146ee3be0e5461a642938a8e6d0cdd2253ebd61645b227624c10c711e12615cd1e7ea9de9b83d63d1a25", - "0xa4c5945d635b9efc89ad51f5428862aefe3d868d8fb8661911338a6d9e12b6c4e5c15a25e8cb4a7edc889b9fa2b57592", - "0xaff127675ea6ad99cb51c6e17c055c9f8fd6c40130c195a78afdf4f9f7bc9c21eed56230adb316d681fc5cacc97187da", - "0x9071294e8ff05b246ff4526105742c8bf2d97a7e7913f4541080838ecfd2dbc67c7be664a8521af48dbc417c1b466a85", - "0x990880b0dd576b04f4b4ce6f0c5d9ff4606ec9d3f56743ac2f469ac6a78c33d25c3105cf54f675e300ac68073b61b97a", - "0xa8d1a62ce47a4648988633ed1f22b6dea50a31d11fdddf490c81de08599f6b665e785d9d2a56be05844bd27e6d2e0933", - "0x8ea5a6c06f2096ded450c9538da7d9e402a27d070f43646533c69de8ea7993545673a469c0e59c31520e973de71db1b4", - "0x99d3a098782520612b98a5b1862ae91bcb338ab97d1a75536e44b36a22885f1450a50af05c76da3dd5ca3c718e69fdd4", - "0xb987451526e0389b5fe94c8be92f4e792405745b0a76acd6f777053d0809868657ba630aa5945f4bd7ce51319f8996f7", - "0xafffccc5ddd41313888a4f9fee189f3d20d8b2918aa5ad0617009ea6d608e7968063c71bd5e6a1d7557880d9a639328d", - "0x8ac51a02505d5cadfd158dde44932ab33984c420aeceb032ed1ee3a72770d268f9e60ccf80ce8494dfc7434b440daafd", - "0xb6543e50bd9c6f8e0862850c3d89835ddd96231527681d4ab7ae039c4a3a5a0b133a6d40cdb35c8a6c8dbb8d421d3e2b", - "0xa2ba901f4fde2b62274d0c5b4dbbea8f89518571d8f95ec0705b303b91832f7027704790a30f7d9d2cdafde92f241b3e", - "0xa6974b09280591c86998a6854a7d790f2a6fbe544770e062845cfc8f25eb48c58f5dfb1b325b21f049d81998029ad221", - "0x890baeb336bbf6c16a65c839ffaab7b13dd3e55a3e7189f7732dbcb281b2901b6d8ba896650a55caa71f0c2219d9b70e", - "0xb694211e0556aebbe4baf9940326e648c34fda17a34e16aa4cefd0133558c8513ffb3b35e4ee436d9d879e11a44ec193", - "0x97cf9eb2611d467421a3e0bfe5c75382696b15346f781311e4c9192b7bca5eb8eaf24fa16156f91248053d44de8c7c6f", - "0x8247f88605bd576e97128d4115a53ab1f33a730dc646c40d76c172ca2aa8641c511dddad60ee3a6fbe1bb15cac94a36c", - "0xae7ecd1c4a5e9e6b46b67366bc85b540915623a63ab67e401d42ca1d34ae210a0d5487f2eef96d0021ebecfd8d4cd9a8", - "0xaec5123fff0e5d395babe3cb7c3813e2888eb8d9056ad4777097e4309fb9d0928f5c224c00260a006f0e881be6a3bf8f", - "0x8101724fa0ce7c40ea165e81f3c8d52aa55951cc49b4da0696d98c9fafd933e7b6c28119aa33f12928d9f2339a1075d1", - "0xa8360843bab19590e6f20694cdd8c15717a8539616f2c41a3e1690f904b5575adb0849226502a305baefb2ead2024974", - "0xade5cad933e6ed26bba796c9997b057c68821e87645c4079e38e3048ea75d8372758f8819cde85a3ab3ab8e44a7d9742", - "0xab1fe373fb2454174bd2bd1fe15251c6140b4ac07bda1a15e5eabf74b6f9a5b47581ef5f0dbd99fdf4d1c8c56a072af7", - "0xb425e1af8651e2be3891213ff47a4d92df7432b8d8ea045bb6670caf37800a4cd563931a4eb13bff77575cbcae8bc14f", - "0xb274799fe9dd410e7aed7436f0c562010b3da9106dc867405822b1e593f56478645492dbc101a871f1d20acf554c3be6", - "0xb01a62a9d529cc3156bc3e07f70e7a5614b8d005646c0d193c4feb68be0b449d02b8f0000da3404e75dbdfa9ca655186", - "0x878b95e692d938573cdb8c3a5841de0b05e5484a61e36ea14042f4eadb8b54a24038d2f09745455715d7562b38a8e0df", - "0xa89e998e979dba65c5b1a9000ad0fd9bb1b2e1c168970f2744982781306bbe338857e2fac49c8cafda23f7cc7c22f945", - "0x85880fdf30faed6acce9973225e8fe160e680a55fc77a31daacf9df185453ad0c0552eb3fd874698ad8e33c224f7f615", - "0xac28d20d4bbb35ba77366272474f90f0ed1519a0e4d5de737adee2de774ccd5f115949e309e85c5883dbc63daaa6e27b", - "0xa1758ac86db859e323f5231ad82d78acbe11d53d3ebf7e644e581b646eede079d86f90dc23b54e5de55f5b75f7ea7758", - "0xae4c0b84903f89353bf9a462370f0bf22c04628c38bb0caae23d6e2d91699a58bd064e3c2b1cbda7f0a675d129f67930", - "0x95f21a099ffc21a0f9064d9b94ce227b3ff0a8c5a2af06ff5ee6b7f3248a17a8ca2f78cd7929ef1d0784f81eddefcd48", - "0x8d06fbc1b468f12b381fd1e6108c63c0d898ddf123ea4e2e1247af115043c4f90b52796076277b722dd2b92708f80c21", - "0xa300f39039d8b2452e63b272c6d1f6d14a808b2cd646e04476545da65b71a6e29060f879409f6941c84bde9abe3c7d01", - "0xadecce1ccc5373072ba73930e47b17298e16d19dbb512eed88ad58d3046bb7eec9d90b3e6c9ba6b51e9119cf27ce53f2", - "0x941a7e03a64a2885d9e7bee604ddc186f93ff792877a04209bbee2361ab4cb2aed3291f51a39be10900a1a11479282ca", - "0xacbcb1ab19f3add61d4544c5e3c1f6022e5cc20672b5dc28586e0e653819bdae18cda221bb9017dfaa89c217f9394f63", - "0xb8d92cea7766d3562772b0f287df4d2e486657b7ab743ed31ec48fdc15b271c2b41d6264697282b359f5cb4d91200195", - "0x957360ecb5d242f06d13c1b6d4fcd19897fb50a9a27eb1bd4882b400dc3851d0871c0c52716c05c6c6cf3dee3d389002", - "0xabd2a23abbc903fbb00454c44b9fb4a03554a5ef04101b2f66b259101125058346d44d315b903c6d8d678132f30b1393", - "0xae9572beff080dd51d3c132006107a99c4271210af8fbe78beb98d24a40b782537c89308c5a2bddfdfe770f01f482550", - "0x82c7e5a5e723938eb698602dc84d629042c1999938ebd0a55411be894bccfb2c0206ac1644e11fddd7f7ab5ee3de9fdc", - "0xaba22f23c458757dc71adb1ce7ef158f50fdd1917b24d09cfc2fbbcbe430b2d60785ab141cf35ad9f3d0a2b3e2c7f058", - "0x8eff41278e6c512c7552469b74abedf29efa4632f800f1a1058a0b7a9d23da55d21d07fdbb954acb99de3a3e56f12df6", - "0x8abd591e99b7e0169459861a3c2429d1087b4f5c7b3814e8cee12ecc527a14a3bdda3472409f62f49a1eb4b473f92dbf", - "0x82dcbff4c49a9970893afc965f1264fcab9bae65e8fb057f883d4417b09e547924123493501c3d6c23a5160277d22a8e", - "0xb5a919fcb448a8203ad3a271c618e7824a33fd523ed638c9af7cfe2c23e3290e904d2cd217a7f1f7170a5545f7e49264", - "0x96d6834b592ddb9cf999ad314c89c09bedc34545eeda4698507676674b62c06cc9b5256483f4f114cd1ed9aaec2fba5e", - "0xa4e878cf4976eb5ff3b0c8f19b87de0ef10cd8ec06fe3cd0677bd6be80ba052ff721a4b836841bdffb1df79639d0446c", - "0x8e15787a8075fd45ab92503120de67beb6d37c1cc0843c4d3774e1f939ac5ed0a85dad7090d92fa217bd9d831319021b", - "0x8506c7fea5a90cd12b68fdbbae4486a630372e6fd97a96eea83a31863905def661c5cdead3cf8819515afe258dbcd4d9", - "0x952ef3bc16a93714d611072a6d54008b5e1bf138fd92e57f40a6efb1290d6a1ffcc0e55ff7e1a6f5d106702bd06807cd", - "0xa5f7761fa0be1e160470e3e9e6ab4715992587c0a81b028c9e2cf89d6f9531c2f83c31d42b71fca4cc873d85eba74f33", - "0xb4811f0df11ff05bf4c2c108a48eece601109304f48cde358400d4d2fa5c1fdaaf3627f31cb3a1bdd3c98862b221720d", - "0x9207ad280b0832f8687def16ad8686f6ce19beb1ca20c01b40dd49b1313f486f2cb837cfbbf243be64d1c2ab9d497c3f", - "0xb18a8c1e6363fadd881efb638013e980e4edb68c1313f3744e781ce38730e7777f0cba70ea97440318d93a77059d4a2b", - "0x901faf777867995aac092f23c99c61f97eeadf4ac6bcb7791c67fa3c495947baef494b2aace77077c966c5d427abbf92", - "0xa123281aca1c4f98f56cff7ff2ae36862449f234d1723b2f54ebfccd2740d83bd768f9f4008b4771e56c302d7bfc764f", - "0x8cffe1266468cad1075652d0765ff9b89f19b3d385e29b40f5395b5a3ad4b157eed62e94279ac3ec5090a6bad089d8b3", - "0x8d39870719bc4ebbcecba2c54322111b949a6ed22bda28a6cea4b150272e98c9ded48cc58fc5c6e3a6002327856726ec", - "0xb3d482c00301f6e7667aaeaf261150b322164a5a19a2fa3d7e7c7bf77dc12fa74f5b5685228ab8bf0daf4b87d9092447", - "0x801acb8e2204afb513187936d30eb7cab61f3fbb87bfd4cd69d7f3b3ddba8e232b93050616c5a2e6daa0e64cef6d106f", - "0xac11e18adda82d2a65e1363eb21bda612414b20202ecc0e2e80cc95679a9efa73029034b38fd8745ce7f85172a9ab639", - "0xb631d6990d0f975a3394f800f3df1174a850b60111567784f1c4d5bba709739d8af934acfa4efc784b8fc151e3e4e423", - "0xaeda6279b136b043415479a18b3bbff83f50e4207b113e30a9ccfd16bd1756065fc3b97553a97998a66013c6ac28f3d8", - "0x8840b305dc893f1cb7ad9dd288f40774ec29ea7545477573a6f1b23eaee11b20304939797fd4bcab8703567929ce93ad", - "0x963cc84505a28571b705166592bffa4ea5c4eeafe86be90b3e4ae7b699aaaca968a151fe3d1e89709fe0a3f0edf5d61a", - "0x8e1ec0d0e51f89afea325051fc2fa69ab77d6c7363cc762e470a9dfa28d4827de5e50f0b474c407b8c8713bad85c4acd", - "0x909f313420403cb36c11d392cf929a4c20514aa2cb2d9c80565f79029121efd5410ef74e51faba4e9ba6d06fcf9f1bd1", - "0xb2992b45da467e9c327ac4d8815467cf4d47518fc2094870d4355eb941534d102354fbda5ab7f53fbf9defa7e767ca13", - "0x9563b50feb99df160946da0b435ac26f9c8b26f4470c88a62755cdf57faebeefffff41c7bdc6711511b1f33e025f6870", - "0xa2a364d9536cd5537a4add24867deec61e38d3f5eb3490b649f61c72b20205a17545e61403d1fb0d3a6f382c75da1eb3", - "0x89b6d7c56251304b57b1d1a4255cb588bd7a851e33bf9070ee0b1d841d5c35870f359bc0fdc0c69afe4e0a99f3b16ec2", - "0xa8ae1ee0484fe46b13a627741ddcdae6a71c863b78aafe3852b49775a0e44732eaf54d81715b1dca06bb0f51a604b7e2", - "0xb814ecbfbc9645c46fc3d81c7917268e86314162d270aed649171db8c8603f2bd01370f181f77dbcbcc5caf263bedc6c", - "0x8e5d7cc8aad908f3b4e96af00e108754915fecebdb54f0d78d03153d63267b67682e72cd9b427839dca94902d2f3cda7", - "0x8fc5ff6d61dd5b1de8c94053aef5861009cb6781efcca5050172ef9502e727d648838f43df567f2e777b7d3a47c235dd", - "0x8788eea19d09e42b0e3e35eb9bcd14f643751c80c6e69a6ff3a9f1711e8031bbe82ccd854a74a5cfcf25dda663a49a62", - "0x95d441d8cd715596343182ddcecb8566d47eaa2d957d8aea1313bbed9d643a52b954443deb90a8037a7fa51c88eec942", - "0xa15efd36ef72783ccdc6336ef22a68cc46b1ecec0f660cfe8a055952a974342bf30f08cb808214bce69e516ff94c14c5", - "0xacc084d36907a16de09a5299f183391e597beaf9fa27d905f74dc227701a7678a0f5a5d1be83657de45c9270a287ec69", - "0xb3fd385764356346061570beb760ccf3808619618fd7521eb0feadc55b8153ef4986ff0cbfcbd4153ad4ea566989d72a", - "0x91ec6b26725532e8edfda109daa7ce578235f33bd858238dfa2eb6f3cd214115b44cce262a0f2f46727a96b7311d32e1", - "0x96b867ccddb73afe1049bda018c96cfe4083fff5bb499e6a4d9fd1a88a325144f9a08cb0aee310e1bb4f6a5793777e80", - "0xad10c18465910152676f1bc6a40986119607b5c272488e6422cfda2eb31da741af13a50f5de84037348014a869c8e686", - "0x86ade2dbc4cceb52b84afe1c874d1e3644691284c189761febc4804b520adf60b25817e46f3f3c08d2ab227d00b93076", - "0x998b949af82065c709fc8f63113a9fecdd1367fc84fc3b88857d92321ba795e630ce1396a39c2e056b5acd206ee011d8", - "0x8dec440bbd17b47dfd04e566c2d1b46f9133023b982fdc5eaeae51404bc83a593f8d10c30b24e13aec709549137cae47", - "0x89436ff47431b99f037cddaee08bb199be836587a7db6ed740317888638e5f4bebbb86b80549edff89678fc137dfb40a", - "0xa8e9960746769b3f76246c82cd722d46d66625e124d99a1f71a790c01cec842bcf6c23c19cc7011ec972cedf54dc8a4c", - "0x980979dafedfd75ff235b37e09e17361cfdda14a5ac3db0b90ed491abfd551916016b2254538da7f4b86ece3038b1b1c", - "0x8ec340ca7654720bb9d2f209985439ebbc3f9990ef27e7d7ae366e0c45b4ed973316943122119604ea9a87fc41ebd29f", - "0xab24440a40ab238d8cd811edb3ef99948ae0f33bf3d257b22c445204016cce22b6f06a1ca979fa72a36c4ddedc2b3195", - "0xa1bcd2473ac7cfebfa61c10e56cae5422c6b261a4a1be60b763fcbcdf2eae4ccf80695f09b062b6cf5654dfab0ee62a5", - "0x9027a613ce7bd827110a3a0e63e83f652e9bc7f4ce8da26c38b28ee893fd0c38bdb20f63a33470a73cb77f776244ab4a", - "0x86911cc8aeb628197a22bf44d95a0b49afb8332c38857fba8e390c27c527b8b45335e22b0f2e0a3395c16ced3c1ed2e8", - "0x8f0529a330a3e9967dce09357d774715fd305bd9e47b53b8b71a2a1303d390942a835aa02fb865a14cfed4f6f2f33fe6", - "0xb71ec81a64c834e7e6ef75b7f321a308943b4bad55b92f4dbaf46658613cebf7e4b5b1bc7f1cdc5d50d1a2a0690e2766", - "0x98d66aaed9fb92f4c7bb1b488ccbca5e570aa14433028867562a561d84f673ac72e971cbe2cb3cbbb0a702797dc45a7e", - "0x8380aa94d96c6b3efd178de39f92f12ca4edd49fe3fe098b2b7781e7f3e5f81ee71d196fb8e260d1d52f2e300e72e7bc", - "0x8c36296ff907893ac58cecadd957b29f5508ae75c6cc61b15ae147b789e38c0eace67963ae62eff556221b3d64a257a2", - "0x97e17676cbc0f62a93555375e82422ee49bc7cf56ad6c3d69bb1989d1dc043f9f7113d0ed84616dde310441b795db843", - "0xa952229615534c7e9a715409d68e33086cdaddf0aec51f4369c4017a94ec3d7113a045054d695fb9d7fd335527259012", - "0x817b90958246f15cbd73a9679e10192ca7f5325b41af6388b666d8436706dea94eafffbc3b8d53057f67ad726dbcd528", - "0x95776e378c8abd9223c55cd6a2608e42e851c827b6f71ad3d4dc255c400f9eccf4847c43155f2d56af0c881abef4acfa", - "0x8476c254f4b82858ecbe128ed7d4d69a6563fd9c5f7d4defc3c67e0bfa44e41cfd78b8e2a63b0773ce3076e01d3f6a7d", - "0xa64b0b189063d31bcae1d13931e92d5ab0cfc23bf40566ac34b5b8b711d0e7d941102e6beb140547512e1fe2d9342e6c", - "0x9678460acff1f6eae81a14d5c8049cdcd50779a8719b5c5861762a035b07f7fa1b1ada8b6173f9decf051fd5a55bebd8", - "0x88398758ce86ed0388b13413a73062adb8a026d6b044cd1e7f52142758bed397befee46f161f8a99900ae6a2b8f6b89f", - "0xa7dfaf40637c81d8b28358b6135bd7ad9cc59177bd9bc8e42ba54d687d974cdf56be0457638c46b6a18ceaa02d3c53f3", - "0xb0e885e5d48aa8d7af498c5e00b7862ed4be1dad52002f2135d98e8f2e89ca0b36cf95b3218aad71d5b4ada403b7045b", - "0x803b0e69a89e8de138123f8da76f6c3e433402d80d2baba98cde3b775a8eda4168530a49345962c4b25a57257ba9f0a7", - "0x8ce6ef80dadb4b1790167fbc48be10ef24248536834ff2b74887b1716c75cb5480c30aa8439c20474477f1ac69734e61", - "0x824764396e2b1e8dcc9f83827a665ef493faec007276f118b5a1f32526340b117c0df12bea630030a131bf389ec78fc3", - "0x874edb379ce4cc8247d071ef86e6efbd8890ba6fcb41ea7427942c140347ebf93e8cf369d1c91bd5f486eb69b45bce70", - "0xadadcb6eb4cafa1e2a9aef3efb5b09ffa2a5cf3ce21f886d96a136336be680dabc0a7c96ec327d172072f66d6dcdbb39", - "0xb993591b280e1f3527f083d238a8f7cf516d3cf00c3690d384881911c1495192a419b8e37872a565ce8007eb04ebe1b6", - "0xb125faaeca3f0b9af7cb51bb30a7c446adbb9a993b11600c8b533bff43c1278de5cdda8cb46a4df46f2e42adb995bce8", - "0xa7efe1b57326b57c2c01720d4fdf348d6a84d35f229d32a8f2eb5d2be4e561ef8aea4d4d0bcfcbf17da10a8e49835031", - "0xa6bd4f5a87574b90a37b44f778d5c7117d78eb38f3d7874bad15ae141b60eed4ab0a7281ed747297f92e0b3fe5f9cafa", - "0x94b5e3067ca1db3c4e82daf6189d7d00246b0360cb863940840358daa36cb33857fde4c01acd0457a90e15accee7d764", - "0xa5ff3ab12197b8a07dd80222a709271ab3b07beba453aacbaf225cfb055d729e5a17a20f0ff9e08febf307823cba4383", - "0xa76dd8aa2b6a957ed82ecec49b72085394af22843272f19360a5b5f700910c6ec65bf2a832e1d70aa53fd6baa43c24f6", - "0x8dfcbe4143ae63c6515f151e78e6690078a349a69bb1602b79f59dc51dea7d00d808cf3e9a88b3f390f29aaae6e69834", - "0x8c6134b95946a1dd54126952e805aeb682bc634c17fe642d5d3d8deffffd7693c90c4cd7d112890abfd874aa26736a93", - "0x933531875561d327c181a2e89aaaac0b53e7f506d59ef2dfc930c166446565bd3df03bab8f7d0da7c65624949cfbae2f", - "0xac6937c5e2193395e5bb69fd45aa6a9ae76b336ea7b6fd3e6aeac124365edcba7e918ec2c663fb5142df2f3ad03411a6", - "0xa8f0f968f2a61d61d2cf01625e6ac423b447d3e48378ea70d6ff38bc98c42e222fe3cbcb04662b19973a160dc9f868a2", - "0x94100a36f63d5c3a6cfb903c25a228389921684cc84f123390f38f90859f37ec9714942ffe6766f9b615101a3c009e43", - "0xb5321b07f5b1eb2c1c20b0c8ab407f72f9705b55a761ec5176c5bcc6e585a01cae78546c54117ca3428b2b63793f2e65", - "0x9922f61ed6763d1c4d12485c142b8ff02119066b5011c43e78da1ee51f10a1cf514329874061e67b55597ca01a7b92ab", - "0xa212eb2d72af0c45c9ef547d7c34ac5c4f81a4f5ec41459c4abd83d06ec6b09fdab52f801a2209b79612ae797fa4507b", - "0x8577d2d8f17c7d90a90bab477a432602d6918ca3d2af082fbb9e83644b93e21ca0bced7f90f6e9279eaa590f4e41dc4d", - "0x9002d424e3bebd908b95c5e6a47180b7e1d83e507bfb81d6ad7903aa106df4808c55f10aa34d1dccad3fab4d3f7a453e", - "0xb9050299bf9163f6ebeff57c748cb86f587aea153c2e06e334b709a7c48c4cbfba427babf6188786a0387b0c4f50b5ce", - "0x852ae1195cc657c4d4690d4b9a5dea8e0baaa59c8de363ba5fccd9e39ec50c6aa8d2087c8b7589b19248c84608f5d0a8", - "0xa02ff5781417ca0c476d82cf55b35615f9995dc7a482124bc486e29b0b06a215fbe3e79228c04547c143d32cd3bac645", - "0x8d7bc95e34bc914642e514a401448b23cf58bce767bab1277697327eb47c4a99214a78b04c92d2e3f99a654308b96e34", - "0xadb28445d3b1cc7d4e4dd1f8b992a668f6b6f777810465fdab231fd42f06b5bada290ba9ae0472110366fad033da514e", - "0xa0c72b15a609f56ff71da17b5b744d8701af24b99fbc24a88588213864f511bfa592775e9ab4d11959f4c8538dc015b8", - "0x933205a40379d5f5a7fb62cda17873fbbd99a0aaa8773ddf4cd2707966d8f3b93a107ebfe98b2bb222fe0de33ef68d03", - "0x90690c1a4635e2e165773249477fc07bf48b1fd4d27c1b41a8f83a898c8d3763efb289867f8d6b0d354d7f4c3f5c7320", - "0x99858d8c4f1be5a462e17a349b60991cb8ce9990895d6e42ae762ce144abc65b5a6f6e14df6592a4a07a680e0f103b2a", - "0xb354a7da06bd93fb5269e44925295b7c5049467b5cacce68cbb3cab60135b15e2010037a889cb927e6065053af9ccb77", - "0xaf01fc4ac396d9b15a4bbd8cc4fe7b30c32a9f544d39e88cdcb9b20c1c3056f56d92583a9781ddb039ec2eeda31fb653", - "0xa8d889fb7155f7900982cf2a65eb2121eb1cc8525bbee48fae70e5f6275c5b554e923d29ebbd9772b62109ff48fb7c99", - "0xb80edae6e26364c28749fd17c7c10eb96787053c7744a5cc6c44082ae96c5d3a4008c899a284f2747d25b72ecb9cb3d0", - "0xb495b37503d77e7aafc226fca575e974b7bb6af2b7488372b32055feecc465a9f2909729e6114b52a69d8726e08739cb", - "0xa877f18b1144ff22e10a4879539968a01321cecde898894cbe0c34348b5e6faa85e1597105c49653faed631b1e913ec7", - "0x8c235c558a065f64e06b4bb4f876fe549aab73302a25d8c06a60df9fad05843915ac91b507febca6fe78c69b51b597de", - "0xb4c31398b854ccc3847065e79329a3fdae960f200c1cce020234778d9c519a244ff1988c1fbc12eb3da2540a5fa33327", - "0xb7bd134b3460cb05abf5aed0bc3f9d0ccbfac4647324bedbdf5011da18d8b85dc4178dd128f6ddbe9d56ea58f59d0b5d", - "0x92594c786c810cf3b5d24c433c8a947f9277fe6c669e51ceb359f0ae8a2c4e513a6dad1ae71b7ded3cdca823a51e849b", - "0xb178535e043f1efcce10fbec720c05458e459fdda727753e0e412ef0114db957dc9793e58ec2c031008e8fb994145d59", - "0xb31da7189abf3e66042053f0261c248d4da142861bfd76a9aced19559be5284523d3e309ef69843772b05e03741a13fe", - "0xb190a8c1a477e4187fecff2a93033e77e02de20aae93dda1e154598814b78fdf8b9ff574c5f63047d97e736e69621462", - "0x98234bd1d079c52f404bf5e7f68b349a948ec1f770c999c3c98888a55d370982bfa976e7e32848a1ebb4c7694acc1740", - "0x99b9eeb33a6fb104bba5571a3822ebe612bf4b07d720d46bde17f0db0b8e8b52165f9b569be9356a302614e43df3e087", - "0xa1e3915b0dd90625b424303860d78e243dda73eecd01cba7c33100b30471d0a1ec378c29da0f5a297008b115be366160", - "0x975118bf6ca718671335a427b6f2946ee7ece2d09ccfb1df08aa1e98ff8863b6c8b174c608b6b2f4b1176fb3cbc1e30d", - "0x903cb1e469694b99360a5850e2ca4201cad23cfccce15de9441e9065eb3e6e87f51cba774ab9015852abd51194c25e57", - "0x821f7ff4d0b133e3be4e91d7ff241fa46c649ff61fc25a9fdcf23d685fe74cf6fade5729763f206876764a3d1a8e9b24", - "0xa1ee8db859439c17e737b4b789023d8b3ce15f3294ec39684f019e1ea94b234ec8a5402bc6e910c2ed1cd22ff3add4de", - "0xaf27383148757bdf6631c0ea8a5c382f65fc6ab09f3d342a808ca7e18401e437cd1df3b4383190fdf437a3b35cbcc069", - "0x8310551d240750cef8232cd935869bad092b81add09e2e638e41aa8a50042ce25742120b25fb54ebece0b9f9bdb3f255", - "0x8b1954e0761a6397e8da47dc07133434ebe2f32c1c80cd1f7f941f9965acdf3d0c0b1eb57f7ff45a55697d8b804e1d03", - "0x8c11612381c6be93df17851d9f516395a14a13c7816c8556d9510472b858184bf3cc5b9d14ded8d72e8fb4729f0b23ba", - "0xb413ac49121c7e8731e536b59d5f40d73a200c4e8300f8b9f2b01df95a3dc5fe85404027fc79b0e52946e8679b3a8e43", - "0x8451e5c1c83df9b590ec53d1f1717d44229ed0f0b6e7011d01ea355d8b351f572866b88032030af372bd9104124df55a", - "0x8d0a5c848ec43299bc3ea106847ed418876bc3cd09b2280c2a9b798c469661505ed147a8f4ffba33af0e1167fdb17508", - "0xa6aa97a1f10709582471000b54ec046925a6ad72f2b37c4435621c9f48026d3e332b8e205b6518f11b90b476405960a9", - "0x97696635b5a2a6c51de823eea97d529f6c94846abb0bd4c322b108825589eba9af97762484efaac04ee4847fb2fb7439", - "0x92fd142181fe6ca8d648736866fed8bc3a158af2a305084442155ba8ce85fa1dfb31af7610c1c52a1d38686ac1306b70", - "0xae3da824ecc863b5229a1a683145be51dd5b81c042b3910a5409ca5009ba63330e4983020271aa4a1304b63b2a2df69e", - "0xaecc0fe31432c577c3592110c2f4058c7681c1d15cd8ed8ffb137da4de53188a5f34ca3593160936119bdcf3502bff7c", - "0x821eac5545e7f345a865a65e54807e66de3b114a31ddeb716f38fe76fdd9d117bee0d870dd37f34b91d4c070a60d81f4", - "0x91a02abb7923f37d9d8aa9e22ded576c558188c5f6093c891c04d98ab9886893f82b25b962e9b87f3bf93d2c37a53cb9", - "0x99a96f5d6c612ee68e840d5f052bf6a90fecfd61891d8a973e64be2e2bdd5de555b1d8bffbd2d3c66621f6e8a5072106", - "0xb1d5ec8f833d8fbb0e320ff03141868d4a8fff09d6a401c22dbefadbb64323e6d65932879291090daf25658844c91f2e", - "0xa06afd66ebc68af507c7cf5ab514947ca7d6ccc89fb2e2e8cb6e5ae0f471473e5fba40bb84d05f2c0f97c87f9a50cb73", - "0x83de3ca182bcf1eac0cc1db6ad9b1c2a1ecd5e394e78add7faa36e039a1b13cb0d1d2639892489df080fbf43e5cef8d5", - "0xadf77fc7b342ff67a2eddaa4be2f04b4e6ceaca8ea89a9fc45cc892fcce8ac3cf8646cfa5aab10ac9d9706ce4c48a636", - "0x8509a430ef8dc9a0abc30ef8f8ccdb349d66d40390fb39f0d3281f3f44acb034625361270162822ef0743d458a82b836", - "0x8350fc09e8617826f708e8154a3280d8753e7dbbcf87e852f9b789fdbeb10bf3fed84fb76edd7b8239a920c449e2f4b7", - "0xa2e7a29da8391a5b2d762bf86cb6ae855cdfad49821175f83f4713dd0c342a0784beba98d4948356985a44d9b8b9d0f7", - "0xa99c50a1a88b8efe540e0f246439db73263648546d199ef0d5bc941524a07d7e02b3ef6e5b08dc9e316b0b4c6966823e", - "0xb34ba55136c341f4ca2927080a07476915b86aa820066230903f1f503afebd79f2acf52a0bc8589b148d3a9a4a99f536", - "0xaf637be5a3e71c172af1f2644d3674e022bc49c393df565ea5b05ce6401a27718c38a9232049dd18cbd5bf4f2ce65b32", - "0xa2972ba7bfa7f40c2e175bb35048a8ef9bc296d5e5a6c4ca7ab3728f4264d64f2d81d29dce518dc86849485ff9703d7d", - "0x8c9db203e8726299adeb331d6f4c235dc3873a8022138d35796fb7098887e95e06dcfad5d766ceaa2c4fb0f8857f37fa", - "0xa82bfbaa9a6379442109e89aad0c0cfc6a27d4a5db5480741a509d549c229cb847b46a974dde9f1398c6b3010530f612", - "0xb2d8ef6e091a76dfc04ab85a24dbe8b5a611c85f0ed529a752c2e4c04500de5b305c539d807184e05f120be2c4a05fc3", - "0x8c6ffc66a87d38cea485d16ee6c63ce79c56b64ae413b7593f99cc9c6d3cd78ef3fa2ab8a7943d2f0e182176642adadb", - "0xacbc92de68b2b04e3dc128109511a1cbe07518042f365d5634e8b651cb1ac435ea48eeeb2b921876239183096ef6edee", - "0x979c4e1165e0ecfa17ed59fb33f70797e000ddbb64acf5fc478cccde940451df051e51b6449c5b11a36afa7868af82e3", - "0xa5a017c5a94952aeae473976027124231abe50460cec4db3ebeb8b1290525776be7c15d108b749c2a1e4b018de827915", - "0x8b6922ab1db925eed24b2586e95f5c709b79d2408a8fa2a71057045ead3ebdd0cc72bee23d9064cd824166eda1e29318", - "0x89a991087a0b5805fcc5c6c5f6ac27e100da0d3713645aa9c90114e68ca9f185f21155eb7645a2c6c0616a47291fe129", - "0xae6ef954c942cbfd37f8f2dc58a649e2584d6777e7eb09ae6992ccde283ac4f4ec39e3a5cda7f7c60f467fb308d37f08", - "0x9335ca5ccac59b39eb2bcef09c54b778ebb690415ba13fe5c8e4b6091d9343a01cc9baa6228cefd8dba98f0710f714da", - "0xa0211c9328be2b46f90ff13614eeffb4c1285e55580db3874610653219926af1d83bda5b089fd37a7c7440a0f1d94984", - "0xa82e097dfa782c40808fac5d8ed1c4fccf6b95ef92e22276fd8d285303fcf18c46d8f752595a658ee5294088b9dc6fc0", - "0xad108fcd0ead65f7f839a1337d520f5bd0cb665ee7100fc3f0563ff1d2959eb01617de8eb7a67c9b98b7b4892082acdb", - "0xb89e6aeabcb3ee3cbf12e3c836bab29e59d49676bcf17a922f861d63141076833f4149fe9e9c3beed24edfacdf1e248b", - "0x8477501bd91211e3b1f66c3bfd399ef785271511bc9366366ce95ec5ea95d9288ab0928a6b7887aba62de4da754d3eaf", - "0xaeec40c04b279096946b743ad8171bf27988405e1321c04894d9a34e2cbd71f444ff0d14da6cda47e93aa6fe9c780d50", - "0xa703bd2d8a5c3521a8aad92afef5162aed64e9e6343d5b0096ca87b5b5d05e28ed31ba235ab1a626943533a57872dd01", - "0xb52d9dfc12c359efb548d7e2b36ddedaefdec0ef78eda8ac49a990b3eb0ed7668690a98d4d3c7bec4748a43df73f0271", - "0xaf887c008bad761ee267b9c1600054c9f17f9fc71acfe0d26d3b9b55536bca5c8aebe403a80aa66a1e3748bb150b20ef", - "0xad2f7a545ef2c2a2978f25cf2402813665c156bab52c9e436d962e54913c85d815f0ba1ce57f61e944f84d9835ce05ea", - "0x91a0a9b3cfd05baf9b7df8e1fb42577ec873f8a46bb69a777a6ac9f702735d6e75e66c9257822c781c47b9f78993a46b", - "0x939fdc380fb527f9a1ddecf9c9460f37e406cd06c59ce988e361404acbfcb6379f2664a078531705dbc0c375d724137b", - "0x8bbbe5d5a0d102b8e0c8a62e7542e13c8c8a6acb88859e78d8e1d01ec0ddff71d429fcb98099e09ff0aa673c8b399dc4", - "0xb67a70e4ef138f48258f7d905af753c962c3cc21b7b8ae8b311a2356c4753f8cd42fdee09ac5ed6de31296ead88c351a", - "0x8d21539e7dca02a271ce7d16431773bbe30e6a03f5aff517132d34cdd215ad0da2f06aa4a2a595be489234b233e0852e", - "0x892ae11513f572cc5dc8b734b716bb38c0876e50e5e942631bb380b754e9114c34b0606740301e29b27d88439fb32071", - "0xa8780dc9faa485f51b6f93a986bc4e15b166986b13d22ec2fefc6b25403b8b81c15cc9ac0025acc09d84932b15afa09b", - "0xb01af013360cd9f2bb9789a2b909c5e010fe6ff179f15997dee1a2ba9ef1ccec19545afdecfcb476f92fcdd482bb2b5a", - "0xb5202e5d5053d3af21375d50ad1ccd92538ef9916d17c60eb55c164767c3c74681886297b6f52e258c98d0304d195d3d", - "0x8f6adbcfbb0734bf3a4609d75cf2e10f74ed855a8b07cf04ac89a73d23b2e3e5cf270a1f2547b3d73e9da033a3c514b0", - "0x8abe529cd31e4cb2bd75fa2a5e45bd92cbe3b281e90ffc7dea01ba0df17c9a3df97a3fde373cce5d25b5814cf1128fed", - "0xb8bbf51187bb3bb124da3870e2dfecb326f25a9383e5cc3323813487457010b9055811669c3da87105050825dc98a743", - "0xa5c83875fe61ebbdd3fd478540d7e5a1ad0f8c790bad0b7dd3a44831e2c376c4fffbc6b988667afa1b67bfaa2dbbb256", - "0xa0606b3062e4beba9031ba2a8e6e90aa5a43ba7321003976e721fd4eedb56486f2c5b10ba7a7f5383272f4022092eacb", - "0xb485cc5e001de6bd1bbc9cd8d777098e426d88275aaa659232f317352e1ddff3478262d06b46a573c45409bc461883e1", - "0x916449580b64a9d8510e2f8c7aee0b467a0e93b11edc3d50725bcbc3ca53c2b8bb231fdc0fc0ed5270bf2df3f64750d9", - "0xb2e687caa9f148c2b20a27a91bada01a88bff47faaf6ed87815db26bb6cdd93672199661654763a6b8b4b2012f59dcca", - "0xb6933f7f9dabc8fb69197571366ac61295160d25881adf2fcc8aaabc9c5ed7cf229a493fd9e2f1c2f84facd1f55fee84", - "0xb01eb8b2cf88c75c3e31807cfc7a4d5cafded88b1974ba0a9d5aaeda95a788030898239e12843eda02873b0cabe30e2b", - "0xa3ca290fa6ce064514a3431b44ecdb390ef500629270202041f23bc2f74038147f338189c497949fb3126bae3a6e3524", - "0x93b0f8d02bd08af74918b1c22131865aa82aba9429dc47f6b51354ba72e33a8b56684b335a44661aa87774931eb85974", - "0x81eebeb9bd92546c37c98e0a5deba012c159f69331a89615cf40c5b95c73dcdbf3ceb46b8620d94ff44fcdad88020c1e", - "0xb350e497932382c453a27bb33d2a9e0dbadf4cd8a858b6b72d1f3a0921afc571371e22b051b97da3bb08694c4ca3a4e8", - "0x8c7052f63ba16f14fa85d885aa857d52f04b3a899a4108493799c90c0410de7549be85bec1f539f1608924668df48e5a", - "0xb397574d1fb43de0faaea67d1d9348d67b712b1adce300d6dc497bca94e0994eef8707c285c5c9ac0a66022655a8420b", - "0xa934661d2168ae1bd95b1143c2e5c19261708aeb795abad8ec87f23dc1b352fa436de997ebb4903d97cb875adb40dc2b", - "0xacf535fa1b77255210e1b8975e0e195624c9e9ffd150286ccd531a276cadc12047a4ded6362977891e145a2bd765e6b9", - "0x8cc32356015d7fd29738dcc13c8008cdbe487755dd87d449ab569c85d0556a1ec520dbce6c3698fc413d470c93cb0c92", - "0x8787c7b3b890e0d3734ac1c196588cacf0a3bde65e2cf42e961e23dbf784eef14c07337d3300ed430f518b03037bd558", - "0x99da90994030cbc2fb8a057350765acac66129a62514bbd3f4ec29d5aab8acdd5f4d69ca83efe7f62b96b36116181e79", - "0xa306424f71e8b58dfa0a0564b2b249f0d02c795c30eee5b0ad276db60423210bba33380fb45dbe2c7fedd6ee83794819", - "0xb207a35d31ce966282348792d53d354bbd29ac1f496f16f3d916e9adbf321dc8a14112ca44965eb67370a42f64ca1850", - "0x89e62e208147a7f57e72290eefccb9d681baa505d615ca33325dfa7b91919214646ca9bdc7749d89c9a2ce78c1b55936", - "0xac2d0ec2b26552335c6c30f56925baa7f68886a0917e41cfbc6358a7c82c1cb1b536246f59638fb2de84b9e66d2e57eb", - "0x8f1487659ecc3b383cebc23a1dc417e5e1808e5c8ae77c7c9d86d5ab705e8041ce5a906a700d1e06921f899f9f0ee615", - "0xa58f1d414f662f4b78b86cae7b0e85dfddae33c15431af47352b6e7168a96c1d307d8b93f9888871fc859f3ed61c6efc", - "0x94f3626a225ac8e38a592b9c894e3b9168f9cf7116d5e43e570368ee6ee4ab76e725a59029006a9b12d5c19ddce8f811", - "0xb5986e2601ad9b3260e691c34f78e1a015c3286fdd55101dcef7921f6cbcc910c79025d5b2b336d2b2f6fd86ee4e041e", - "0xb6e6798ddd0255fbe5cb04a551a32d4c5d21bdfd8444ff2c879afe722af8878d0a3a2fe92d63936f1f63fea2d213febf", - "0x86bea9bfffef8bc11758f93928c9fdfae916703b110c61fa7d8fe65653f8c62c6fecd4ff66a1f1a7f3c5e349492e334c", - "0x9595a4606284569f4b41d88111320840159fd3b446e00ec8afd7ddaa53dd5268db523f011074a092f8e931fc301a8081", - "0x83b540a6bc119bf604a7db5f6c0665c33b41c365c12c72ca4fa7b0724115bbb0ff1ae38532c3356e8bb3ac551285929f", - "0x92c6daf961ca4eb25293e1794cf85cda4333cf1c128207af8a434e7e0b45d365f0f5baaefc4ebd5cd9720c245139c6e2", - "0xb71465f3d7dba67990afc321384a8bb17f6d59243098dbed5abd9a6ffc7a3133b301dd0c6ca3843abbaa51d0953abbed", - "0xb15d93482d2ee5b1fec7921fcc5e218c1f4a9105a554220a4fb1895c7b1d7a41f90bbf8463d195eecf919fcbe8738c51", - "0xa79c98e70931ffd64f4dcf7157fbae601a358261e280fe607eb70cef7d87f03efa44cf6ba0f17fbb283a9c8a437d2fdb", - "0x9019d51a6873331f8fe04cb45e728a0c8724a93d904522a9915c748360ddf5cdbf426a47b24abf2005295ed2a676cbf0", - "0xb34cc339fec9a903a0c92ce265e64626029497762ff4dcaaf9bb3994298400ce80f4fb7dbe9ec55fe0c4a522c495cb69", - "0x8fda9be7abfe3b2033cad31661432300e2905aef45a6f9a884e97729224887a6ec13368075df88bd75c11d05247bef15", - "0x9417d120e70d6d5ca4b9369cba255805b5083c84d62dc8afec1a716ead1f874c71a98ad102dac4224467178fe3228f62", - "0xa0a06b64867eebb70d3ce8aaa62908a767fb55438a0af3edf9a8249cd115879cde9f7425778b66bb6778cb0afeb44512", - "0xa44309d3e1624b62754a3a4de28b4421f1969870f005ac5dc7e15183fa5b3ad182bcd09cca44924e03fbdb22f92f8cf8", - "0xaea80f1c3a8fc36cfb5c9357d59470915370b2bec05f51f1d0e1d4437657e2303ba2d1ac3f64cf88f2df412dff158160", - "0xb3f1557883d91b24485123d2f3ae0fce65caa533c09345ae6b30d2ac49953acee61c880c57975be7b4f5558d3a081305", - "0xb52cb1e56f0d147cfb58528b29c7a40bab7cfc9365f2409df7299bfc92614269ff9de3cb2500bbc4909f6a56cf4b9984", - "0xaa4f8fd0f5f87c177ee7242f7da76d352db161846cd31523a2100c069d9e4464170eec0bffc6d4da4f9e87017b415dbd", - "0xb5b61f52242985c718461a34504f82495d73cbb4bc51f9554b7fe9799491f26826d773656225f52a1531cd5bd6103cde", - "0xad12ba9697804ede96001181c048f95b24ba60761c93fb41f4b4a27e0f361e6b1434e9b61391bacaf0705fdaa4a3a90e", - "0x9319286cbda236f19192ae9eb8177e5a57a195c261082ba1385b20328fb83ed438f29d263dddae2f5278c09548830c4a", - "0x88b01ee88c3a7ae2c9f80317dddbaa2b7b0c3a3c23828f03ff196e244500410c9ac81c2e2d3e1f609d4b36ee1732738c", - "0x8e31f30600a9d629488d44a008c821c3c57f13734eaee5a19f0182a2de9e538fff7d982980d7fcc725c969f29f7c2572", - "0xb215740eea98b4bb14197a803a8975700ad2f25a25ef3628eae10166d56c823301f6dd62ce3f9ebf2d42d1f33d535004", - "0x8fb0fdb253d4bcc6693642779be13a5b816189532763dfd7da868cfacfdb87cb5ebe53b18b69dfd721f8d4baf3c1d22d", - "0x8cdd050a447f431ff792156d10381aaf83c6634a94b614dd5b428274538a9cc1f830073533b4fd0a734d6dd4f8d9c4ce", - "0x81b01ee8c72ac668ad9dd19ead2d69cac28c3525e613e036e87aa455c2da9651cc8fcc97c451a8c8a071a4eb69623cd1", - "0x8d9e02dc9ac83f861b3745bd69216232144c47cb468a7dbc49083ed961f978e34265b3f42c400339120bdc4644fe5711", - "0x89e9410455b34cba9db0a5ea738e150fae54dd000d61e614f3274a6c8102ba7cd05b0936f484a85711ad9da7946f51ea", - "0x91f9d4949678f8e6f4b8499899818bdd0f510da552b5d79d8e09bf3b69d706ab36524b5e86d3251318899b9223debf6b", - "0x8b3c38eec7e1926a4be5e6863038c2d38ab41057bcfa20f2b494e9a0c13bc74c3a44c653402eb62a98e934928d0ebccb", - "0xa5cfe465bfbf6e8bfbd19d5e2da2fc434bd71acd651371087450c041aa55e3c4f822361e113c6c3d58646ed3ba89d6da", - "0x918665b8810bcb8d573ca88b02a02c62eaa5a4a689efb5c564b0c9183f78144e75d91fd1603e17d2c77586cbe5932954", - "0x997dace0b739aeb52ba786faae5bdf1d48630a90321f9ceebfa9e86d189a3d79d7b04e459ac8e4adcfe83a5ce964eb1c", - "0xa5a1ca9f0ccc88017a616d481d912aab3f0e154b673f1131c5d9c9c3f5f147d25b6392b2c31e49f7bb7eb2697d05dbec", - "0xa76e99bec509eff01bf6767a06ac97ebc6671cb58bc3d4acc2803580a874885453dbba2e1bba26e45f8d2bda5f688860", - "0x956c1362c8123c5d9ebff7049e851235d69fa645f211ef98e2b6564f2871114a12224e0ec676738d77d23c709dd28a6c", - "0x885efede83b1a3e96417e9f2858ab0c7a576fc420e8f1f26cabf3b1abeec36bcaa63e535da177847f5e0afdb211bf347", - "0xaffca2257f292a2db52f8b1bab350093f16f27ef17e724728eeaec324e2513cd576f6d2e003cc1c6e881334cb2e8bf22", - "0x8dac963d34dcc9d479207a586715e938c232612107bb2d0af534d8da57ad678555d7c1887fadca6551c4f736ffa61739", - "0xb55e600a6bbde81f5a0384f17679d3facb93a7c62ca50c81a1d520cf6e8008ac0160e9763cb2ca6f2e65d93ca458783b", - "0x9485e6c5ab2ebfb51498017e3823547b6ab297d818521ceac85cd6c3aa2d85ae075a0a264ae748fc76ce96a601462ffa", - "0xb4d8abca786c0db304a6634fba9b2a40d055c737ed0f933e1739354befdae138dae3c8620a44138f50ebeaf13b91929f", - "0x8bde7ca39c7bda95b1677a206b16c3a752db76869ea23c4b445c2ff320f2ee01f7358d67a514982ee3d1fb92b7bd7229", - "0x8f8cd0acc689b6403ee401383e36cae5db2ff36fc2311bbadf8ebb6c31cbcc2ca4ffac4c049da5ba387761ef5ec93b02", - "0xa06f42d5f69a566ff959139c707355bbf7aa033c08d853dce43f74a9933e6d7b90e72010ef3fcb3d12e25852343d1d31", - "0xb10ece7cf6b69a76dba453b41049db0cdf13d116cf09c625312b150ee7437abd71d921eda872403d7d7ce7af1e6dccb7", - "0xa3d820318e0f3b54fba7a4567912a82d6e6adf22b67cfc39784683a8e75f77538e793d9708aae228fa48a71abb596195", - "0x8758fad55b68a260bea3bd113e078fd58d64a92f7935ff877f9f77d8adc0994b27040cfc850126c7777cfdfb2428a3e5", - "0xb504913ee96c10f00b848cd417c555a24bc549bf5c7306140eff0af2ada8cb5e76bed1adb188e494332b210fbf24e781", - "0xa00e019a40acc7aab84c1cc27c69920ad7205c2a3dc9e908a7ef59383695c9cb7093c4bcbc2945aab2655119552e3810", - "0xb1000b4c4f306672e39d634e5e2026886a99930d81b8670a5d4046db9621e44997c4b78f583374a09c60995f18a6fd4f", - "0xa6c5053c4e748540ad2b622c28896c9d4ca3978ca4784ac8f09da5314a245f5cdc5d6203c84e6e0bcb3081829720a56d", - "0x8e37e67a70205a5c7da95de94ac4d0ebd287c1c9922d60c18eec1705030dfcbf74ae179e377c008bf5a8bc29c7c07cce", - "0xa66bd7c0243319b553d5cb7013f17e3504216e8b51ba4f0947b008c53bcb6b4979286b614a4a828ee40d58b5ef83e527", - "0x97e2110b0fb485508a2d82ecc2ce1fbe9e12e188f06c7ef2ac81caeeb3aca2c00e5e6c031243b5ca870a9692e1c4e69b", - "0x8734ce8bbc862e12bea5f18d8a8d941d7b16a56ef714792fed912ca9c087497e69b6481fdf14efe1f9d1af0a77dac9b1", - "0xb441dddac94a6a6ae967e0e8d7ab9a52eb9525fb7039e42665e33d697e9a39c7dcef19c28932fb3736e5651d56944756", - "0x918b8997f2d99a3a6150d738daed2ff9eb1f5ed4a1c432d18eab4a898297f7ffbffd1e4ae9037acf589b1cd9e1185ef6", - "0xa0247b8ac4d708cf6b398dc2d5c127a291d98e8bef5f195f820c4fddb490574ba4f62647c2d725237a3e4856eec73af0", - "0xb45636e7e0a823c2a32e8529bb06fcccfd88e9964f61201ee116279223ed77458811d1b23bcb6b70508d16d4570a7afb", - "0xa99c1188fa22b30b04fda180d2733586ea6ef414618f1f766d240c71f66b453900d3645541c019361027aebe0a0f305f", - "0xb4c2f758e27fe233f7e590e8e0c6de88441164da3fcd5211a228318d3066dfdafc1d40246dd194f2b597f6fe9600b3d7", - "0x972530819445b11374c3043d7855d5f1d3c4922b3b205d0bf40162c51605375dd0b61f49cd7f3d39a533a86a13005989", - "0x992b533a13e5d790259bfdfdf1074f84a5e5a0a0d7be9cd6568cdc1662524f1a6666a46da36cea3792ba6707850f4d86", - "0x9875d130457e04dc6ea2607309bfbb900ad3cb5f3e0574f808d27b20cbf6f88389d87dca19998680c5bc30d1df30a41b", - "0xadea8494a69e83221edf360ab847272b5c47eba5404665fb743d98c0682732c30085ae3ec82bc1e8e4aba8454c9b1849", - "0x887d4c624ce05e224216c5f6fa13c5741012ac33330bc291754782f0bfe668decdc98c0e43a1ce28323effe6b639f477", - "0xab6b167aeb5e93ab155990b94895e7e7ff6dea91384854a42cc8a3b9983495b4b3c33ab1b60b2b6450ccf0418fada158", - "0xa7588d0b7c6a6bc32fc474aa0f4e51dfb8e6e010346ad32c59d6f99e6f0522424111a03a4f56ba4075da8009ee7a63e9", - "0x94d645cc3936db1563568193639badfc064dd5bda8d0631804ee00b09e141b200619e07506b5a8225130541436327194", - "0x8d695c03cc51530bdc01ee8afcd424e1460d2c009e1d7765c335368e5c563cf01a2373c32a36400c10e2bf23c185ed19", - "0xad824a0a7ed5528e1f9992cbb2050785e092b1ea73edd7fb92b174849794a5b04059e276f2941e945bc0f3e46172f2af", - "0xad6ed2af077a495d84f8eeed7d340b75c0d1c8b7c5a854dfc63ab40a3d0c2b0d45016d30b3373a13f0caae549f657976", - "0x82454126c666023c5028599a24be76d8776d49951dfe403ebf9a5739b8eb2480c6934a34010d32cd384c91c62a9aa251", - "0xb57070006793eca9fa2f5237453ed853994ad22c21deb9b835e1fb3fbc5ac73aec265a4a08de7afae1610dc8c42b7745", - "0xad94667c791cf58875eb77eb17b6ad02de44e4ba2ddc2efe4d0ff22a5e1a090c670354437847349fd61edc4ba5606f07", - "0xb2aac0c345ffc00badaab331c12a22019617b004d32c099c78fa406d683744d96d51d1237ad0842f9f54655186f8f95b", - "0x8fed51076cc939b354e3b69034a594e6c9c98425ccf546154ab087a195375128444732388d2eb28f82877de971ec2f58", - "0x8e521c0093deb9dff37888893db8ffebc139984e7701e68b94d053c544c1be0d85f0f98d84b2657933647b17e10a474c", - "0xa2c6c9a307aff9b1dea85f90fa9e3b8057fd854835055edeb73842a7ef7c5ae63d97c51fec19dd8f15d696a18a0424a6", - "0xa3390b25a9c11344ed1e8a0de44c848313026067a0f289481673c2c0e7883a8fc9f6cab6ccd9129729a6d8d0a2498dc2", - "0x82770c42b1c67bbd8698c7fe84dd38cc5f2ad69a898097a33b5d7c5638928eb1520df2cb29853d1fa86a0f1bcc1187e8", - "0xa6fdf7a4af67bc4708b1d589135df81607332a410741f6e1cc87b92362a4d7a1a791b191e145be915aa2d8531ee7a150", - "0xaecac69574188afc5b6394f48ba39607fe5bb2aa1bd606bc0848128a3630d7d27101eb2cea1fb3e6f9380353a1bb2acc", - "0xa23fd0c52c95d0dffb7c17ec45b79bf48ed3f760a3a035626f00b6fe151af2e8b83561d0b9f042eaae99fde4cbd0788d", - "0xa5f98068525cdd9b9af60e0353beb3ac5ac61e6d3bac1322e55c94b3d29909d414f7f3a3f897d5ae61f86226219215c6", - "0xb2a4d724faac0adf0637c303ff493a1d269b2cdbec5f514c027d2d81af0d740de04fb40c07344e224908f81f5e303c61", - "0xadeadb3521e1f32ef7def50512854b5d99552e540ec0a58ea8e601008de377538c44e593e99060af76f6126d40477641", - "0xa18b7fc2fcd78404fed664272e0fef08766a3e2bc2a46301451df158bd6c1c8aa8cf674dd4d5b3dedfaceb9dd8a68ae3", - "0x83bcfb49313d6db08b58c6827486224115ceef01ca96c620e105f06954298e301399cdd657a5ff6df0b0c696feec1a08", - "0x8c94391eba496e53428ec76dfe5fa38f773c55c0f34a567823316522a0664a3d92bff38ec21cf62ac061d7d1030650c5", - "0xb1fa196ccfd7d5f1535b2e1c002b5cde01165c444757c606b9848bc5f11b7960973038fb7cc3da24300fc1848e34c9af", - "0xb139f6c6449449638de220c9d294e53fc09865a171756d63bbf28ec7916bf554f587c24bddf51dd44372d15260d8fe25", - "0xb716242299d4ee72b5b218781b38ca5e005dcf52333364f85130615d1dbf56216af8ee2c9c652d82f7aab5345356538c", - "0x9909f24e4ad561aa31afd3a3b9456b2bd13a1d2e21e809a66af62fec5f95b504507ac50e81d2233da2b223f5443e7585", - "0xae863530a02cf3a757f72b945c8c0725d9f634d2ff26233478d1883595ff9a1eef69e8babffdbfa161452fc204f5b5a1", - "0x8eb82bde283b6a6e692b30236cbf41433b03eda8dad121282772edd56f144b1ebf5fb489d18c6ce8776135771cbb91e2", - "0x9296141fadf8dadc885fff4999c36efa25ec76c5637a8300a1a7dc9cf55bcedfe159e0ef33f90eee9be8c4f085734e10", - "0xb6c07f2e6fcbd6c42a8b51e52fbcd5df3aa9f7c3f0b3c31021de1aec2111d0a1c36b5ab489ba126af44fd43cf31c2594", - "0xa70ca669c357535b363d16b240fd9cb9c5ba1b648510afc21218ea034e9bf5f22717ae31ff43ef89dded95b7132fa58f", - "0xb350721f8f6b4d164fd08aca30cd4dece9b4a81aed0ac12119c9399bab691d5945814306f9a61f0106b76d4d96f7b9d6", - "0xb6886076c9d8c344bf3fb6975173d00fa82866012894f31c17e6fc784fbc0dd2d24d6a1cddd17f7379c74566a23219aa", - "0x87636e4a83ceadc170a4b2517b19525c98e2163900401996b7a995b2f3da8d6ba2ab92f909eade65074fac07cf42f6fa", - "0x8ff61d87c4699a067a54b8540e8642f4c7be09d3783ec18318bcba903c6714fcd61be69165e07e1ca561fe98e07507de", - "0x85485d6b569ac20e6b81a9e97ef724e038f4fee482f0c294c755c7b6dad91293814f143bfcfc157f6cfa50b77b677f37", - "0xa49256cb1970cc1011a7aed489128f9b6981f228c68d53b1214d28fbcfb921386cc7cf5059027e667a18073efa525a74", - "0x87bc710444b0c3e6682d19307bedc99c22952af76e2d851465ee4f60e5e1146a69f9e0f0314f38a18342e04ece8e3ed3", - "0xa671a6cabfd19121a421fdfe7732eccbb5105dfb68e8cbcf2b44ae8465c99e78c31b99730beca5bc47db6fc2f167203a", - "0xa2f3270c184629f6dfc5bf4bdd6e1b8a41e8840a1e4b152253c35c3d9e7ab4b8e3516dc999c31f567e246243e4a92141", - "0xb9795a5a44f3f68a2460be69ecacdbb4664991ebbedffed5c95952147ad739e2874c099029412b9653d980a2d4307462", - "0x959053faec9a966dd5a4a767a3154e4b8e4f56ca540ae53e373c565dda99fb626f725e5a5e3721c82918f8c5f2e9e0a3", - "0xb3ef9d6a1b3cd44a3e5112819fa91cb8a7becc3f5b164c6f759f93171d568497b01c8e743f4727b341a1296a0dbadf4f", - "0xb852dfdfbe2b8c77d938fad45f00737e14eacf71d5fecbb3e4f60052ec9efb502c38c1fcecaf71da69eabe8b33852a67", - "0x921c7007f26bdd4139e919dfe27d87b489a0bc5bd6fb341e949e4451f14c74add0489b108c9c9666a54c5455ac914a9f", - "0x86b63d73ba31c02e5337f4138e1684eccdc45ab5e4f30e952fb37d638b54ecec11010414d7a4b7aa91f7cc658f638845", - "0x853c55e0720b66708a648933407795571fc11ad5c234e97f92faabce9e592983dfb97a1705047ee803648ecf9fbb2e5c", - "0x995fe7d1dc09bb0c3c3f9557c4146534778f5ea9c1d731c57440fdcf8094f82debf19090b5d23298da1ed71c283b3ae5", - "0xb9c49c911a0c4d716b7baec130f9e615bfa7d504aa8766ed38878a93c22b1f6353503d4f7f425d4902239fb4689429df", - "0x80504d964246789a09dcd5c0298680afb6fe50bca3bb9c43d088f044df2424a1828de10e0dbdc5c0aac114fa6d9cf5d1", - "0x90249351f109f6b23a49a610aaa3b2032189fd50e5e87cdc3b20f23ed4998af3a8b292bf9fbab9bd1cbe0a1371081878", - "0xabb5f0148850f0d80b429c2b9e0038772432340ef0862ccb5dcb7347026ca95bf9a5857f538e295aebd3a6a5027adb4c", - "0xb92ac9c0f7e73150798348265e5f01f3c752480c72613c6894a95e9330bba1c642b21b9cbd8988442b5975476634b4fa", - "0xaf3fbcc825abd92c6d7ea259467f27045e288f27a505e6a3c9ec864aa08fcaca0d4123034513dbd4c82d4814075708ab", - "0xa738232a66030e0e9c78e093a92fcc545b10e62fb0ecb832bbbc71471b28eb6ec422a498c2402e2c6d74983df801e947", - "0xae60194ce2035edd1af253b9eefbb4b1b7609c9678256c89c3cb076c332a9f4442c3441ad2ecc9d73265359bdadc926c", - "0x8b2fd55e686f16725fc0addb4065f696275852320b03221fd22889825d66fae5bb986b03c47452e32b3a32c1fdfc8dfd", - "0x8e2e1a36673b7729b07e7bc5014584e1c03e9552f7440fbfda0a6a7f41953947fcdf8d666f843bfc03dcca5b06a14318", - "0x95a3df04368c069f3fd32a20b627c5f043e952167c9e80bf5914bbf2086879909c60e089bbd488725ab977c0e6051728", - "0x9856403b2211d0152d4eec10db7ec34c16ac35170714b75af3ebc398a676c171b24b6f370361de0f9057ba444293db14", - "0xa2cb484b758af5fd8e2baca7f0406f849c71255e58ef110d685cd0c1137893a25d85a4d8582e3ced7dd14287faa95476", - "0xb0f697b6a42f37916b90ab91994ae4a92c96dc71e4da527af41b9d510bc2db5a9b4f29183a758074b6437a1e62b2d1d7", - "0xb39c49266aae46f257b7ae57322972fb1483125298f9f04c30910a70fe5629dba0ec86b94cc6ba16df3537a55e06f189", - "0x86cd5595b5b769dfd9ceb68b11b451f6c5b2e7a9f6f6958eac8037db1c616e8a9defb68a0d6c2287494d1f18076072c1", - "0xb462e8fa9a372d4c1888fd20708c3bed1cb00c17f7d91a0481238d6584fbbf2d238e25931154f78a17296a12825d7053", - "0xa5ef28286628ba509bac34c9f13158d0013239fdca96b5165161f90b89d6e46295822ebdf63f22d7739911363a0e0e86", - "0xa629a95a24e2545862b41a97ecba61b1efa792fd5555dc0599c175947e9501bffc82b05a605fd5aabc06969ccf14fff4", - "0xaf83467e4b1f23a641630cc00c38d4225ff2b4277612b204d88de12a07d9de52fb4d54a2375a7fd91eb768623c255376", - "0xa630f29fb2e9a9e2096d7f3b2f6814ee046ebc515f6911d4bc54ad8a5a821a41511ff9dcfbe3176f35c444338ecd0288", - "0x950dedc11bd29e01ba9744bec681ad9462127c35e9fcadfacc9405ec86b985a1b1c4f9ac374c0f1fa248212e5e170503", - "0x82e8e7be8011ee0fd9c682d26a0ef992d0191e621d07fd46a3a5640ef93a42e1b98a33cad1f8017341a671d28caebb03", - "0xa075860554e712398dac2fb0375067a48d0e4ca655195cefc5ccb1feb8900d77124aa52a12e4f54f7dab2a8f1c905b5b", - "0x81d2183d868f08714046128df0525653a2dc2ff9e2c3b17900139c9e315b9f4f796e0fb9d1d8cbadbaa439931c0e0879", - "0x81fb1456969579515a75fb66560f873302088cde2edc67659b99a29172165482ca1f563758c750f00086b362ae405322", - "0xa13c15ab19203c89208c6af48d2734bb0873b70edb660d1d5953141f44db9012528d48fb05aa91d16638cbda2ca8f0cc", - "0x8ba46eef93e4ec8d7818124a0b9fcfe2bcf84a98db3545d2b3d0192cfadc81fc667dcc22ab833c3e71508d0f3c621fe4", - "0xb9bd60d2266a7d01e1665631a6ed6d80ffc0cd7f088f115a5d4ea785c518a8f97d955e2115b13c4960302b9825526c92", - "0xb26fa4e87142150250876083a70c229249099331410f0e09096077fdf97b31b88dc57a3e3568d2a66a39af161cf5dfec", - "0xb9d147564124728b813d8660ba15fa030c924f0e381ad51d4e0cf11cc92537c512499d3c2983dd15f2e24ca166070d70", - "0xb6fb44e1a111efb3890306fa911fafda88324335da07f7de729b2239921ef15b481630a89c80e228bec7ab6444a0b719", - "0xa6cd9c7acac052909ef0cf848b6012375486b59b7bac55b42c41f0255b332c1d45a801f6212d735be8341053bd5070b9", - "0x864258d69234786af5de874c02856fc64df51eff16d43bfb351b410402ab28f66895aec4025e370a4864f19ff30fd683", - "0x84370fa1243b64b3669dd62e1e041ff9bd62810752603486aac3cba69978bd5f525c93cbc5f120d6f2af24db31ec3638", - "0xb983c2cdc1a310446de71a7380b916f9866d16837855b7d4a3a6c56c54dab3e373a6fc6563b8309dc3b984d4e09275d6", - "0x914f8587f876470f7812fa11c6f67e2dd38bf3090e8928e91fe2fe5595bee96cbe5f93d26fdced6b4e7b94f75662b35d", - "0x8b47bcb111d91aa3d80e4ceef283824aa00d1faeb6fe4111aecd9819869c0e1f6f4b6fb2018aebb07a0f997412cda031", - "0x95b2befa98f9992450ca7ff715ae4da8c36dd8adcfef3f0097de6e3a0b68674b05cbf98734f9665051bb4562692641e0", - "0x8bcd1651a2bfce390873a958e5ff9ca62aac5edd1b2fd0f414d6bcf2f4cf5fa828e9004a9d0629621b5e80fbbd5edb90", - "0xaf79bed3c4d63239ac050e4fa1516c8ad990e2f3d5cb0930fc9d3ce36c81c1426e6b9fe26ac6a416d148bf5025d29f8b", - "0x881257e86b7ab5af385c567fde5badf67a8e7fff9b7521931b3ce3bac60485c0fe7497339194fb7d40e1fad727c5c558", - "0xa1b40b63482cd5109990dfb5a1f1084b114696cbbf444bf3b4200ab78c51dad62c84731879ea9d5d8d1220e297d6e78a", - "0xb472212baa2a31480791828ca5538c3dcc92e23f561b0412f8cc9e58839d1625ddcaf09c8078d31ac93470436843cd74", - "0x8f516d252b1863cd3608d852a2857052bb2a3570066d4332fa61cb684b10ac8d1a31c8d32f2a0d1c77eee2ad7a49643d", - "0x8d20b75c51daa56117eda2fd5d7a80a62226074b6a3ff201519f2054eecfeff0aa2b2f34b63bea3f53d7d0ce5c036db9", - "0x8282f433229e7948a286ba7f4a25deb0e0a3c5da8870562c3646757bef90ca1e8d3390b0a25b3f2bf45bf259a4569b77", - "0x8a2dbf4b55cc74f0a085d143a88ebc8c2a75a08eab2703d13a00b747eaddc259a3dd57f7330be938131835a6da9a6a68", - "0xaa0bc51617a938ea6a7b0570e98b8a80862dd9e1cf87e572b51b2a973e027bcd444ef08e0d7b5dee642e0da894435e91", - "0xaa7319ca1ac4fe3cc7835e255419eeb7d5b2d9680769cc0ca11283e6147295db75713b71a9312418a8f5505cd45b783d", - "0xab3f9c465663dc90fae327a2ee9cb7b55361a9b6fbe713540a7edd3cff1c716802fb8ad4dd8fb0c945d96b3b44c5795b", - "0x913a2ae88acffab12541fc08920ee13ab949f985a117efe9a5b2c76f69f327f60c5b5ad3fa5afa748034ac14298fc45a", - "0x9008f044183d2237b723b235953e4d8b47bec6a7b300d98075555478da173b599ba9c7c547c2f111ce1fae5ac646e7a3", - "0xa26b4cc42b353e1c18222d2e088d7f705c36be12e01179db440f10fcfa9691d31fc4fc7e7ee47876f1624e6d44be1021", - "0x995e75824f322294336bfa2c5d1a319f0d77f6a0709beabaf1b43015d8a78d62447eab907349524734170f0294d1ca7a", - "0x8b96f04a19dbe4edc71d1f2c6d3475ae77962e070ec5797752453283c027c6b29b6e58e8b7eb5c3f9770557be7e80b67", - "0x8621459865234734bcfaa492ca1b89899525198a7916ccc6f078fb24c8bf01154815bb5b12e1c3d0a10bd4f1e2ea2338", - "0xab52174541185b72650212e10a0fe2e18ccfd4b266a81233706e6988c4af751b89af87de0989875f7b5107d8d34c6108", - "0x966819d637bdd36db686be5a85065071cf17e1b2c53b0e59594897afc29354ecba73bf5fc6fa8d332959607f8c0a9c27", - "0xb7411209b5ab50b3292c3a30e16f50d46351b67b716b0efb7853f75dc4e59ec530a48c121b0b5410854cd830f6c4b3ea", - "0xa5dc04adbadce0af5dc1d6096bad47081110d4233c1bf59a5c48a8e8422858620f4be89bf1f770681be2f4684ee4cce7", - "0xaf77a8f83cffb5f8d17be0ab628dedcad63226c9b13ce4975fb047f44bfef7d85e7179aa485abb581624913eddbb27ec", - "0x82bf28dc58c893c93712ce297cc0d64f70acb73a641cb4954ccf9bf17597f6d85eecf5a77c8984ab9afbe588562a0ee9", - "0x988a7cef9a178e8edb91f3ec12f878fd68af2ac0762fa0a48a2423e24f765ed8f7837429fd8bc0e547e82e6894e63008", - "0xa5d5969311056d84b3ee87f49286fac0bd9a7220c196cea4f9dced3b858dcdba74718eab95b38bd5d38d2d1184679c98", - "0xaf4d51b3ded0aaad8f12bef66c0616e9398fc42618852ac958e6ab2984a720a6111ac55b249d7e4523051740e12b346f", - "0xac635b4a49f6fbb94a5f663660f28431ba9f7c5c18c36ebc84fd51e16077de7753595f64619b10c16510ecbc94c2052d", - "0xae25eb349735ced1fe8952c023a9b186a1f628a7ddf1a4b6f682354a88f98987ac35b80b33189b016182f3428a276936", - "0xae3ab269690fdd94134403691ba4f5ed291c837c1f5fdc56b63b44e716526e18abb54f68ca5d880e2fb7bea38e74c287", - "0xa748b03b2bd3fbc862572bc4ddc0579fa268ee7089bcfd0d07d0c5776afcd721302dbb67cb94128e0b1b25c75f28e09a", - "0x8f09a2aaa9ba3dfe7271f06648aba9cc1ea149e500a7902d94bb9c941a4b01d1bb80226fd0fd2a59ad72c4f85a2a95d0", - "0x853d55ad8446fd7034e67d79e55d73a0afcb5e473ed290e1c3c7aa5497e7f6e9bbf12d513fc29e394a3dc84158a6d630", - "0xb1610417fb404336354f384d0bf9e0eb085073005d236a0b25c515d28235cea5733d6fbd0ac0483d23d4960064306745", - "0x86de805b3d4f6fbb75233b2cf4d22fcc589faa2ac9688b26730cb5f487a3c6800c09bb041b2c6ab0807bfd61b255d4c9", - "0x893b38c72cf2566282ee558d8928588dca01def9ba665fcb9a8d0164ee00dedafbf9d7c6c13bcc6b823294b2e8a6a32c", - "0x8e50de7a70ac9a25b0b5cf4abc188d88141605e60ce16d74a17913a2aff3862dec8fbbf7c242cf956f0caae5bcc4c6bf", - "0xb5cf09886a4fb4ce9ea07d1601d648f9f9d1a435b5e1e216826c75197cd6dafd6b2b07d0425a4397a38d859a13fdb6dc", - "0x859dc05daf98e7f778a7e96591cc344159c1cbe1a7d017d77111db95b491da0a9272866d2638a731923ca559b2345ebe", - "0x8ff1792f77ecdfbd9962f791a89521561c7b82031a4e53725f32fe7d99634a97b43af04cbf3e0b0fdff4afa84c49eb99", - "0x81e2cd8a221b68ae46dd7ce97563bd58767dc4ce1192b50ff385423de92206ff585107865c693c707e9d4ed05f3149fb", - "0x8fce7da7574e915def0d1a3780aa47ef79b6d13c474192bd1f510539359494ddc07e5412f9aac4fc6c8725ade4529173", - "0xac02f5df60242734f5ead3b8a62f712fefdb33f434f019868a0b8ddf286770244e2ddfb35e04e5243ba1e42bcd98a6a5", - "0xa8d69783349a442c4a21ecb3abd478a63e2c24312cb2d2b3e10ea37829eb2226a9b8d05a8c9b56db79ffaa10d1f582d1", - "0xb25b5cca48bf01535aba6d435f0d999282845d07ac168f2ca7d5dba56ee556b37eab9221abdb1809767b2de7c01866c1", - "0x8af7e1d1f4df21857d84e5767c3abe9a04de3256652b882672b056a3ab9528e404a8597b1ad87b6644243f8c4cd3799f", - "0xa6718308dfa6992ae84fcb5361e172dbbb24a1258a6bd108fd7fc78f44cc1d91be36e423204a219a259be4ab030f27ff", - "0xb99cbe3552c1a5259e354c008b58767c53451932162e92231b1bebfc6a962eb97535966a9bd1dfd39010dfcda622d62a", - "0xa8458f6b8b259581f894e4b5ce04d865f80c5a900736ca5b7c303c64eaf11fe9cb75e094eece0424ba871b2aee9f7a46", - "0x914f763e646107b513c88f899335d0c93688ffa6e56c3d76bff6c7d35cb35a09f70dc9f2fe31673a364119c67cd21939", - "0x9210f2d39e04374f39b7650debe4aceeb21508f6110ab6fc0ab105ec7b99b825e65753d4d40f35fad283eeff22a63db0", - "0x98729cf927a4222c643b2aa45b3957b418bce3f20715dd9d07997a3c66daa48dd62355dbd95a73be9f1d1516d1910964", - "0xa602c399f1217264325b82e5467a67afed333651c9f97230baf86aec0dd4edeae1e973cafef2ea2236d6d5b26719954d", - "0xac9632921d45900bf3be122c229ba20b105b84d0f0cba208ccdce867d3e9addfb3ef6ece9955950d41e1b98e9191ef42", - "0xa76ce1f53e1dc82245679077cb3bca622558f2269f2d1a1d76b053896eba1c3fc29d6c94d67523beb38a47998b8c0aa7", - "0xb22b51fcc1b328caa67cc97fb4966cb27d0916488a43248309c745cd6e2225f55ad8736d049250fa0d647e5f8daa713c", - "0xb7645c1923a6243fa652494fe9033fa0da2d32a0fb3ab7fcb40a97d784282a1ffad3646c499961d4b16dadbc3cbb6fd6", - "0xacab12b490da690db77c4efdc8b2fe6c97ac4ba5afb5165d6647fdd743b4edbad4e78d939fc512bebcf73019c73bae40", - "0xad7a0fcd4e4ccb937a20e46232a6938fccf66c48a858cf14c8e3035d63db9d1486e68a6bf113227406087b94a0ece6a0", - "0xa78605beaa50c7db7f81ab5d77a8e64180feea00347c059b15dc44c7274f542dc4c6c3a9c3760240df5f196d40f3e78b", - "0x8763315981c8efa9b8ae531b5b21cfc1bbc3da3d6de8628a11dcc79dee8706bd8309f9524ec84915f234e685dd744b69", - "0xb4a6c48531190219bf11be8336ec32593b58ff8c789ee0b1024414179814df20402c94f5bfd3157f40eb50e4ef30c520", - "0x8dac8a3f152f608ce07b44aee9f0ed6030fa993fd902e3d12f5ac70bf19f9cde2168777d2683952a00b4b3027d7b45ea", - "0x8baf7dfae8a5840c5d94eabfe8960265f6287bb8bc9d0794a6d142266667a48bec99b11d91120907592950a0dddc97d9", - "0xb8595e6ea6b8734d8ae02118da161d3d8d47298d43128a47e13557976032dad8c2ccbfff7080261c741d84d973f65961", - "0x8b93979c51c8d49f4f3825826a5b9121c4351e0241b60434a3c94f2c84f0b46bbf8245f4d03068676166d0280cf4f90c", - "0xaceb0fdaf20bf3be6daebf53719604d3ab865807cc2523285f8fef6f3fc4f86f92a83ad65da39de5bd3d73718a9c4bd2", - "0x814dd41764a7d0f1a14a9c92e585f154a26c8dbf2f9bff7c63ae47f1ac588cec94f601ccc12e8a63a7a7fce75a4287f2", - "0xb47b711848e54fa5c73efc079d0a51a095fa6f176e1e4047e4dac4a1c609e72099df905958421aee0460a645cba14006", - "0xaaf7bd7e1282e9449c0bc3a61a4fca3e8e1f55b1c65b29e9c642bb30a8381ce6451f60c5e0403abc8cee91c121fa001f", - "0xb8b0e16e93b47f7828826e550f68e71a578a567055c83e031033c1b7f854e7fc8359662a32cc5f857b6de4aff49e8828", - "0xb3eb70b8c8743a64e1657be22a0d5aeb093070f85a5795f0c4cb35dc555958b857c6c6b7727f45bf5bedf6e6dc079f40", - "0xae68987acd1666f9d5fa8b51a6d760a7fb9f85bf9413a6c80e5a4837eb8e3651a12e4d1c5105bfb5cfa0d134d0d9cfc2", - "0xacd8fa5742b0bac8bd2e68c037b9a940f62284ff74c717f0db0c033bf8637e4f50774a25eb57f17b2db46e5a05e1d13d", - "0xa98dac386e7b00397f623f5f4b6c742c48ab3c75d619f3eaf87b1a0692baf7cb7deac13f61e7035423e339c5f9ae8abf", - "0x99169bd4d1b4c72852245ebfbc08f18a68fb5bcce6208dd6d78b512b0bc7461f5caf70472b8babf3e6be2b0276e12296", - "0x937d908967f12bf7f728fe7287988c9b3f06c1006d7cd082e079d9820d67080736910bc7e0e458df5bae77adb9a7cbc1", - "0x8c50e90ce67c6b297fd9406c8f9174058c29e861597a0f4ed2126d854a5632fa408dfa62ad9bb8b6b9b6b67b895d5a4d", - "0x8f4840a91b0a198226631a28e7a2e893fc6fed4d5eb3cb87b585aac7f4e780855a353631ad56731803296f931e68a8d0", - "0x96a4b8c64d3d29765e877345383bf0e59f4ac08798ac79dd530acd7f3e693256f85823ad3130fb373d21a546fe3ca883", - "0xb0dce7a6ab5e6e98b362442d6e365f8063ba9fef4b2461809b756b5da6f310839ac19b01d3fd96e6d6b178db4ff90ee1", - "0x8f012cb2be5f7cb842b1ffc5b9137cafef4bd807188c1791936248570138f59f646230a1876f45b38a396cbdd3d02e08", - "0x94a87b5ce36253491739ca5325e84d84aaff9556d83dcb718e93f3ff5d1eecf9ae09d0800a20b9e5c54a95dfebfcecd3", - "0xb993ec5f9e82cc9ceeb7c5755d768bc68af92cc84f109dfaf9cf5feb3aa54881e43c3f598ba74ed98e8d6163377440ca", - "0x92f845d4d06a5b27d16aef942f1e3bcbe479b10fef313f9ca995315983090511701b39ccbb86b62d0c7c90a2d1f0c071", - "0xb6ec6da0f9e7881e57fa3385f712e77f798abc523609a5b23e017bb05acc6898825541aed7fe2416c4873de129feceea", - "0x86b181183655badfe222161d4adf92a59371624a358d0ec10e72ee9fa439d8418f03d635435ec431161b79fd3fa0d611", - "0xb5e28eeed55fb5318b06a0f63dbf23e00128d3b70358f1c6549fd21c08ef34cb1372bc0d4b0906cc18005a2f4cd349bf", - "0x85c4d3fddda61dbfb802214aa0f7fc68e81230fb6a99f312848df76cddc7b6dfd02860e8a4feb085dad1c92d9c6c65e0", - "0x80f7fdec119309b2ac575562854f6c2918f80fc51346de4523ec32154d278f95364fdef6f93c7d3537a298dd88df7be6", - "0x9192c1949d058614c25f99d4db48f97d64e265a15254aa6ed429e1ef61d46aa12355769f1909a5545cd925d455a57dbe", - "0xa0b1e7d928efc4dcbd79db45df026ae59c20c1a4538d650c0415ab7cb0657bc1e9daeacc3053ee547e8f9c01bdbd59c4", - "0x893e84c41d3a56bca35652983c53c906143b9ad8d37b7c57f9dacbeb7b8dd34defc6a841f5b9857ffb90062bbd8e9dee", - "0xa7f89a448349dbc79854cf888980327f92aedc383c7fadd34fdc0eaa4f63d751315b4f979e14f188854ef4d16c9e8107", - "0x833f2774a96187805f8d6b139c22e7476bce93bc5507344d345008080fb01b36d702b96e4c045617a23a8ca1770b4901", - "0x80e46e86d68bd0a48ac6fa0b376d5bb93a5d6b14f08b3a47efa02bb604c8828c2047695f1f88fc5080e5548e1a37130f", - "0x943f42b7b4ad930059a26ad06b62e639f06c1c425d66066c55134e97c49abe412358c7cb994fcc1cf517ea296bca1f68", - "0x8b9d4fe835dc6a2cbf85738937bbfb03f0119ab8df04a7d68860716ce6ee757dbe388a1e8854ddb69fe0c9fa7ed51822", - "0x909030c7fde2591f9ea41ae6b8fa6095e6e1a14180dda478e23f9c1a87b42c082a1ea5489c98702f6ccd2ba5812d1133", - "0xa715ec1beb421b41c5155c7ef065bbb50b691d0fa76d7df7ee47683d9e4eb69b9ea3e62fc65196a405d6e5e29e6c2c60", - "0x8c9e801cb7ef780a535be5c2a59b03e56912acbfdb00447bfa22e8fc4b11dceecc528f848d5fba0eec4237d6f81f4c79", - "0xb96b6af857c3bc0344082bd08ec49a9bed478d4d35b85a2099b1849cd6997521c42225305f414cdd82aef94b9e1007d3", - "0x8764db720b4e44a4d2527f7f9b535a494a46c60e28eac06bf1569d0703c4284aefa6cb81fbba9d967286f9202d4b59ea", - "0xa66fd2f9158e1ffcdd576cba1413081f43eed00c7eb8f5919226f7b423f34ac783c1c06247819b238de150eb5a48d977", - "0x82c52e817ac3bb0833ea055dec58c276c86ca5181811cf7a483b3703a06ea1bee90ae3aeaa2cffeaeba0b15fe5bf99be", - "0x987d07cb276f7f03a492cfb82bba6d841981518286402d3e69e730a9a0e29689a3619298124030da494e2a91974e0258", - "0xb34f2c5740236bc6d4ae940920c5bc2d89ff62a3dd3a3ec9a0d904d812b16f483073db1e53b07f2b62e23f381d7bdbe5", - "0xa1c0679331ab779501516681b3db9eefb7e3c0affb689e33326306ada6d7115fafd2cc8c1c57b2fa6c2072552f90a86e", - "0x94805e30d7852fc746e0c105f36961cc62648e438e8b9182fc0140dbf566ec14a37ad6e7f61cacb82596fc82aed321e5", - "0xa42fb00b29a760141ff0faaeb7aca50b44e7bbc0a3f00e9fb8842da7abfcaae6fae9450abe6ba11e8ecf11d449cbe792", - "0x8fb36ce4cfa6187bfe8080ac86b0fa4994f20575fb853bd8ffa57c696179cc39f58ff3b4bd5a2542ff1c8b09015539df", - "0xa1c54e7aa64df7fb85ce26521ecfc319563b687ffecd7ca9b9da594bbef03f2d39f51f6aaff9a3b5872d59388c0511c6", - "0x855e48fdb8f771d4e824dbedff79f372fd2d9b71aa3c3ecf39e25bf935e2d6e0429934817d7356429d26bf5fd9f3dd79", - "0x8ae6157a8026352a564de5ee76b9abb292ae598694d0ea16c60f9379e3bb9838ce7fd21def755f331482dc1c880f2306", - "0xa78de754e826989de56fe4f52047b3ffd683c6ceaf3e569a7926f51f0a4c4203354f7b5cfa10c4880ba2a034d55a9b0d", - "0x97609477d0a1af746455bbd8cb2216adacc42f22bfd21f0d6124588cd4fec0c74d5bde2cdba04cdbfbff4ac6041b61b1", - "0xa03dc3173417381eb427a4949c2dbfa0835ef6032e038bf4f99297acf4f0ba34a5fc8ccf7e11f95d701f24ee45b70e27", - "0xaad6283e85cd1b873aeb8b5a3759b43343fdadc9c814a5bf2e8cf3137d686b3270f1ec2fb20d155bbfd38c7091f82c44", - "0x92ab94ed989203a283d9c190f84479c2b683615438d37018e9c8de29c2610bb8fccd97bb935dca000d97d91f11a98d65", - "0x8c0444a0b9feb3acb65a53014742e764fa07105e1c1db016aec84f7a3011d9adc168dbba8034da8d0d5db177a244d655", - "0x95a33d25e682f6c542d4e81716cc1c57ef19938409df38bf8f434bc03193b07cedd4e0563414ce00ab1eebbd3256f3e7", - "0x8716c30e3e4b3778f25c021946c6fb5813db765fde55e7e9083a8985c7c815e1b3d3b74925ba108d9a733ddf93b056af", - "0xa186aabc10f1fff820376fa4cc254211c850c23a224f967d602324daec041bbe0996bf359ed26806a8c18e13633a18a8", - "0xa1e8489f3db6487c81be0c93bade31e4d56ec89d1a1b00c7df847f5cd7b878671015f5eaa42ee02165087991936660b9", - "0x8f688c969c1304dfa6c1a370119d1988604026a2ab8e059016c5d33393d149aae6e56f3ee2b5d25edc20d4c6c9666ad9", - "0x91950b651fefd13d2fa383fd0fdc022138ce064ee3b0911157768ad67ed1fb862257c06211cf429fba0865e0b1d06fc8", - "0x86cff4080870d3e94ed5c51226a64d0e30266641272666c2348671a02049ae2e8530f5fb1c866c89b28740a9110e8478", - "0x88732c4d9e165d4bb40fb5f98c6d17744a91ff72ca344bc0623d4b215849a420f23338d571a03dd3e973877228334111", - "0xafcc476ad92f09cf2ac7297c5f2eb24d27896d7648ba3e78e1f538c353ceeb1e569917a2447f03f3d4d7735b92687ba5", - "0xb622aa475e70d9b47b56f8f5026e2304d207684726fb470a0f36da7cb17c30dd952813fab6c7eb9c14579aacca76f391", - "0x802cf5630c0407ae0d3c5cf3bef84e223e9eb81e7c697ea10ec12e029fc4697ce7385b5efab7014976dacc4eb834a841", - "0xa08596493f4cd1b8ac2ec8604496ee66aa77f79454bb8ab6fdf84208dc7607b81406c31845d386f6ac8326a9a90e7fc5", - "0xa54652ca9e6b7515cb16e5e60e9eabbccbc40bb52423d56f0532d0bac068aec659a16103342971f2cc68178f29f695db", - "0xa3ab54875cb4914c3a75b35d47855df50694310c49eb567f12bbc5fa56296e11f4930162700e85ba2dbfdd94c7339f91", - "0x94183a040285259c8f56bef0f03975a75d4def33222cc7f615f0463798f01b1c25756502385020750ac20ae247f649a1", - "0xb0004261cc47b0dc0b554b7c6ebf7adf3a5ece004f06e6db3bbac880634cdf100523b952256a796998a5c25359f12665", - "0xa25dfeb0e18ebe0eb47339190f6a16f8e116509ab2eef4920f0d3ff354e3ead5abe7f5050b2f74f00b0885ea75b4b590", - "0xab10ef2f5dc0ede54e20fa8b0bce4439543db8d8b31e7f8600f926b87ec5b8eea0ac2153685c7585e062ffac9e8633c3", - "0x8386eac1d34d033df85690807251e47d0eaacb5fe219df410ab492e9004e8adabb91de7c3e162de5388f30e03336d922", - "0xb6f44245a7d0cb6b1e1a68f5003a9461c3d950c60b2c802e904bc4bc976d79e051900168b17c5ac70a0aed531e442964", - "0xad12f06af4aa5030b506e6c6f3244f79f139f48aec9fc9e89bbfbd839674cfd5b74cea5b118fb8434ba035bda20180af", - "0x88511306dfe1e480a17dba764de9b11b9126b99f340ceb17598b1c1f1e5acbdd1932301806fe7e7e5e9aa487a35e85de", - "0xa17cdf656e1492e73321134a7678296a144c9c88c9a413932d1e4ca0983e63afc9cdc20fd34b5c6a545436b4db50f699", - "0xb555b11598a76de00df0f83f0a6b8c866c5b07f7ac2325f64fb4a0c2db5b84e0e094d747186c3c698ee4d0af259dc4c7", - "0x88014560587365e1138d5b95c2a69bdae5d64eb475838fee387b7eb4c41d8c11925c4402b33d6360f0da257907aa2650", - "0xb220634e6adee56e250e211e0339701b09bf1ea21cd68a6bd6ee79b37750da4efe9402001ba0b5f5cbbfcb6a29b20b0c", - "0xac5970adc08bc9acec46121b168af1b3f4697fb38a2f90a0fbc53416a2030da4c7e5864321225526662d26f162559230", - "0x97667115b459b270e6e0f42475f5bce4f143188efc886e0e0977fb6a31aba831a8e8149f39bc8f61848e19bcd60ceb52", - "0xb6c456b36c40a0914417dd7395da9ed608b1d09e228c4f0880719549367f6398116bf215db67efe2813aa2d8122048f2", - "0xab7aef0d6cda6b4e5b82d554bd8416a566d38ded953ffd61ef1fcca92df96cdcc75b99a266205ff84180ab1c3de852a4", - "0x81d354c70ce31174888c94e6cf28b426e7d5c4f324dc005cd3b13e22d3080f3881d883ca009800f21b0bb32fa323a0cf", - "0x94f3440965f12bee4916fcc46723135b56773adba612f5ce5400f58e4d4c21435e70518bdef4f81e595fa89e76d08fc6", - "0xa6683e7a1147f87cbeeb5601184cc10f81bca4c3c257fd7b796a2786c83381e7698fb5d1898eb5b5457571619e89e7d6", - "0x8ca29539600f8040793b3e25d28808127f7dc20c191827a26b830fff284739fb3fc111453ff7333d63bce334653a0875", - "0x98a69644048b63e92670e3e460f9587cf545a05882eb5cba0bcbd2d080636a0a48147048a26743509ab3729484b3cc12", - "0x84d40302889c03c3578c93aca9d09a1b072aadd51873a19ef4a371ca4427267615050c320165abece7f37c13a73d4857", - "0x87954271e3de3f0b061c6469d038108aac36f148c3c97aefb24bf1d3563f342ea6c1c1c44c703e1587a801708a5e03f8", - "0x86b6f5367e04c5caa3ec95fd5678c0df650371edac68f8719910adf1c3b9df902cc709a2bddc4b6dde334568ca8f98ac", - "0xa95fed2895a035811a5fee66ca796fdecce1157481dd422f8427033ed50c559692908d05f39cb6bea5b17f78a924633c", - "0x8ba05bdadde08a6592a506ea438dbdc3211b97ea853d1ad995681a1065ececce80f954552b1685ef8def4d2d6a72e279", - "0x90b6b7494687923e9c5eb350e4b4b2e2fa362764d9a9d2ebb60ee2ad15b761e0850c9a293123cf2ef74d087693e41015", - "0x8819ea00c5ea7b960eb96ab56a18c10a41fd77e150ab6c409258bc7f88a8d718d053e8f6cb5879825b86563e8740808d", - "0x91e42031d866a6c7b4fd336a2ae25da28f8bde7ace6ff15dc509708b693327884e270d889fff725e6403914546055c28", - "0x85763642052f21cf1d8bd15fd2dc0c2b91bba076751e4c4f7a31fbdb28787b4c6a74d434d6ef58b10f3ad5cde53ef56d", - "0x8b61c36c7342a1967a1e7b4c01cddf4dce0e2025bc4a4a827c64994825f53e45277550ceb73c34bb277323fb784aa3c6", - "0x80b9634a45c8b3770e993257bd14df6a17709243d5429969ab8b9a4645bf2a94f9b3cd3d759169887b4aa0eb50f4f78c", - "0xb5c44db9439dd8aa4edd151d95e48a25c1154e1525c337f97109f40131db81a4898344c8c3144c270bdc835c269b3477", - "0x863080fcbc227eea32d0dc844f42dc642fbda7efc398ab698be3a3c6f3bf8803dea6ba2b51fed6151f9522b4ab2a8722", - "0x8481e871129e9cb9d2d109c513cbba264053e75192e967f89659dcfcc1499de9ae7a1ac4f88f02289150231c70b4da01", - "0x834d8183698d3d2d1352c22c373222cb78d0f4c8cb15e0ad82073dde273b613515ebcd184aa020f48f8e6fc18f3e223c", - "0xa227e300f0c5bc1b8d9138411413d56c274cc014ae8747ec9713f3314d5fae48bb6f8cc896f232fd066182af12c924e4", - "0xab7242835e91ba273de1c21eb4fca8312bdda5b63b080888b96a67a819b50294a7f17a7dc0cd87fae5e7f34bc24c209a", - "0x86eb27c898a5d6c3618c3b8927acee195d45fe3f27b0991903520a26fb8021b279e2a8015fbbba5352223ae906c7c5d6", - "0xa61b1c200b0af25da8ad8e29f78d000a98683d1508ae92ee7f4326a7c88e0edb645b6cb5dde393ac74d322895e77ba24", - "0x887739318c710aae457b9fe709debff63bfbb3ffbbb48a582c758b45d6bf47a7d563f954b1f085c3bc633ffd68c93902", - "0xaacfcb0e2b0a868b1c41680487dc6600577ce00aa2edeee8c6141f4dc407217ddb4d37b79e7c9182258c750d12a91508", - "0xad8cd2cf5ccd350cd675a17f31b86a0e47499c6c4c11df640a5391bb10989c9c70df0a3ddeba9c89c51e15fedaf67644", - "0x8aba897d32c7ef615c4dfa9498436529c91c488a83efc07ba9600875c90c08b00f66a51469eb901451b6e18e7f38ffd7", - "0xaab8a600609b80e36b4a6772308bac77929a0c5d8d92bbc38e9999186a1c2bfdbef4f7a2b1efba9c17a68dc15a9373ab", - "0xb95811d1454307a30c2ac8588c8104804b06c1aec783fed75a6f12c9df626be57865850100f1ad28073e3867aca941cf", - "0x8b119d3bd4ee644469457df5d8a0020fd99b8b20bd65ab121cf95a7f55e50dd8945fcf1dff9d269d9d0b74b4edbc7726", - "0xa980b912df832ea09353fd755aa3eec9eb4cfd07ca04387f02a27feab26efa036fca54cc290bb0c04a8a42fdfd94ce2f", - "0x91288e84da1d4ee2a4dad2df712544da3a098fdb06a5470c981fb6d6f3dcc1c141b6f426d6196ff3df6f551287179820", - "0x98b0473bcffcbd478fd1b49895c61dd2311dab3cdec84f8e3402f8add126c439ffcb09cae3b7f8523754090d8487b5a9", - "0xabe76988cf3065801f62a1eb3cfe9f8185bd6ab6f126c1b4b4fde497ca9118d02a0db3fadccd4ca98826b30475fa67ef", - "0x94a316a0faa177273574e9e31989576a43e9feb4cc0f67aa14d5c1967c4e10fc99db3ef4fdca2e63800a0b75f4b84256", - "0x975ad39adadc7e69e34981be2e5dc379b325dc24dddacc0bb22311ff4a551a0020a8bdecf8ab8ac5830ca651b7b630ce", - "0x8b3bc73b640dc80ac828541b723a968fb1b51a70fa05872b5db2c2f9b16242c5fe2e8d1d01a1dbeaac67262e0088b7b0", - "0xaa8d892a6c23dbc028aae82c1534acb430a1e7891b2a9337cedb913ff286da5715209cffb4a11008eae2578f072836cb", - "0x8dee9747a3ae8ed43ce47d3b4db24905c651663e0f70e2d6d2ddb84841272848a1106c1aa6ba7800c5a9693c8ac2804e", - "0x81e2c651b8b448f7b2319173ecdc35005c2180a1263e685a7e3a8af05e27d57ec96d1b2af2cae4e16f6382b9f6ec917c", - "0x98a9a47635de61462943f4a9098747a9cf6a9072a6d71903d2173d17c073eff3fc59b2db4168515be31e6867210ecbcd", - "0x912b2398505c45b0bb4a749c3f690b1553b76f580b57007f82f7f6cce4fadd290d6df9048258978c8a95ef9c751a59a2", - "0x8ac8f0893fe642111ef98ae4e7b6378313a12041bbca52141e94d23152f78c2e4747ae50521fc9c5874f5eb06976e5cf", - "0x946b4a8eb05b529aaed56ac05e7abeb307b186a7835623fa4e85ed9eb41a4910663c09ea1bd932a2c467d28776b67811", - "0xa4be51abeddd40e1da6fdb395d1c741244988ff30e10705417b508574b32dce14d08b464486114709339549794df9254", - "0xb33b6b7d66cb013e7afeabbd7ed1e0734eb0364afe4f0f4c3093938eec15f808985fb7f3976969bf059fa95f4d8e335b", - "0xa808adbcf0049f394914482483ee0f711d9a865615ff39b5313ed997f7a0d202ad9ed6e6de5be8a5c1aaafe61df84bca", - "0x8856268be15a78465ad00b495162dc14f28d4ef4dcf2b5cba4f383472363716f66dabc961a6dbdda396e900551411e41", - "0xb16ba931e570e1bf124ea3bd3bdf79aed8aa556697ea333e6a7d3f11d41538f98dcde893d0d9ba7050442f1515fb83b1", - "0x91ecde1864c1a9c950fd28fa4c160958246b6f0aa9dda2a442f7222641433f1592d38763c77d3f036a3dbb535b8c6d8f", - "0x92cda991f69fbf8e55c6bf281b07fff5dbbb79d1222b8c55686480669247b60212aac27aa7cccd12fcee94e7a759b8af", - "0xb1d9b5b4e996b375d505d7250a54c12d32372c004a9cabf1497899054cb8b5584b1cef1105f87b6e97603ccbf2035260", - "0x86e98bde8b484fb809b100f150199f13a70c80813ad8b673bf38e291595e2e362ad1fa6470d07d6fbe2cf7aeac08effc", - "0xaa12f7c39ba0597a8b15405057822e083aca3cee6ed30c4e0861eeb22620823588d96c97bb1c3776b711041c4dc3d85d", - "0xb477b34f29334f3bae69c7781d574342b7c27326088f9a622559ab93075c7357953ae84eb40e3421f453e04e9b4d5877", - "0x9625067cb2120ce8220a469900aa1d1bb10db8fe1609988786b07eb2b88e0ddb35a3eccd4b6741e1fa2365c0db6b1134", - "0x997b92af7765f587d70ea9718e78a05498cd523fc675ad7b0e54a4aae75fbeac55d0c8d72471471439dacd5bfcfae78d", - "0x88b59eaea802e6a2cf0c0075bf3fd6891515adcb9adf480b793f87f1e38d2188c5ed61ac83d16268182771237047ec8a", - "0xa57d078b230b1532c706a81eaabfef190fb3eb2932f4764631e916a0f0d837d6014da84ede100abaf610519b01054976", - "0x94ed5c5b96f6afa9f2d5e57e1c847ae711839126ab6efb4b0cf10c4564ff63c819d206fdc706178eb6a0301df2434c01", - "0x980296511019c86cc32212bad6e4a77bc5668b82a2321a1ecabc759a8bbc516183a4787c7f75f9ee7f1338691dc426cc", - "0xb10ef97db257343474601fd95f9016c205e28bd22bf7b8f9e30c3b14aca1cc9a11e6404ff412ef269c55fb101fee5a37", - "0xb670d5d9c77fc6aa14212dd3eae100620f3510031b11a9625aa40bf31835c9fd717753b555bd159b1aa64a2104929340", - "0x862054fabf6d6d529a7584d1a48f72d2eb216caf959c782ec36c69c26aef4595415d19a28b041946811b34a629105241", - "0xae4bf2ccd7b0f3774653848b5b4d39e5517dcbcff30d8441d78bc387ff42b573f16b7b0a7366e6ca5cef1dd9f0816df9", - "0x8f810527badcb49f1542a0ccd12e3541efa084243f7106eae003458c176f4c1f01daae9d4a073c2cb2aced747e8a4576", - "0x8a32c2067aaf6baf32db67acd4974a22a6da33db5444028a7c8c4135f9c84e102dc3b2c635b15afa6dc907d0270daffb", - "0xb15fc057f306a60b20c8487125b6b334ab749cf70eb8a30c962f625bb203ebd0d2a315949ee3b7a99e3d91acec384806", - "0xa37f145d321359b21cba7be8b64dfae7c67a20b7b324f27c9db172d58e77a49fa02ed3d06d09d7644bf1fd81f4aab44b", - "0xb338d2e39a485ee4297adcf5e58e16c3cc331c5dffeade0be190907c1c5bdfed38537a6d81dc39a2cdfc1bc45e677886", - "0xb69d84d8511b3aedfdc7c7e66f68b24e12e5a2365dbbe014bddd2e99e54143428cf8b74cf12c0e71316038aa5300e87e", - "0xab210cc38661667450561a1857337879633f5d5bf2c434a3df74ff67f5c3ba69a7880872f19ae4dcbbb426462cd7d0fb", - "0x94538ef487a58a5ff93a5e9616494c5f066715d02be5b249d881a00bd0edfe2fe19dd7a5daa27f043d1dbb5ac69cf58d", - "0xafb47a899c1b25fe800241635fa05de9687a69722802ad45434f551971df91d8ca9edda0d835d82eb8f36ff9378ed7e8", - "0x827a10d7536230887283a9b1dedccc6b95ef89cb883c4ee7b9821058b0f559704d1636670c0ada2b253bf60b7cb8a820", - "0x97cc07965065d64409f19fb2c833b89ca3a249694b16b58818a6f49d3800926627ce0f87e5c0853ae868b4699cfdee5e", - "0xae0c93d44780ef48ea537cf4cb8713fd49227f4b233bc074e339d754b5953e637a7289c6f965162701e4b64e4eaec26d", - "0x80953053397c4c0ba9b8e434707f183f9ced2a4c00d5c83b7dc204e247ad7febc1855daeb906c53abfdf3fe3caca30c4", - "0x80f017e87b471b5216ebe25d807be6c027614572337f59f0b19d2d1f3125537478cb58e148f3f29b94985eac526cd92f", - "0x8a8e1c0d49801a8dd97e9e7c6955fc8b2c163a63bd6a4be90bb13e7809bb0dddc7a5025cc7d289a165d24048eac4e496", - "0x8530e5b5c551a2e513d04e046672902c29e3bb3436b54869c6dea21bab872d84c4b90465de25dff58669c87c4c7d2292", - "0xae3589d389766b94428e9bde35e937ed11aac7ead3ce1b8efe4916c9bfff231d83b7e904fe203884825b41022988897a", - "0xac02e629a900438350dd0df7134dfa33e3624169a5386ea7411177b40aa7a638e8d8aef8a528535efdbe1ca549911c0b", - "0xb1ac60b7270e789422c3871db0fa6c52946d709087b3b82e6eba0d54f478520b1dc366bb8b7f00ff4cf76e065c4146eb", - "0xa7465e1f8e57de1a087144d3c735fee2b8213fcbf2b9e987bb33c2d4f811de237bf007402e8d7f895563e88b864f7933", - "0x8ab0007ba8984dee8695ec831d3c07524c5d253e04ec074f4d9f8bd36e076b7160eb150d33d15de5dd6e6fb94f709006", - "0x9605bbe98dadd29504ce13078c1891eca955f08f366e681d8b5c691eadb74d6b1f2620220b823f90ef72eb4ab7098e16", - "0x942a083d07c9cb7f415fedef01e86af4019b14ef72d8ab39fe6bd474f61ba444b9aac7776bea7e975724adb737e6337a", - "0xb9a49a8c4e210022d013b42363ac3609f90ea94b111af014f2c5754fbc2270f6846fa6a8deb81b1513bb8a5d442ea8dc", - "0x99cd62b177d5d7ce922e980cc891b4f0a5a8fa5b96dfc3204673fbef2e7fb2d7553bbacd7b2e6eca4efb5e9a86096e2e", - "0x94e30b65b3edd7472111566dde7fab0e39a17e1f462686050f7134c7d3897e977550faf00174748cbeaec6c9c928baa8", - "0xa32fbcb29f3391d62092f2720e92b6ef4d687d8a3eae39395e0464669a64a38fe21a887f60bc9519d831b9efde27f0f4", - "0x8f1492c4890d8f9deecb4adada35656e078754dcf40b81291e7ef9666d11ba3747a478f9420a17409d7d242cecd2808f", - "0x8942960b319ef65812d74cb1d08a492334db58d41e8437e83ddf32e387d9f3ad36834f59e6a71d1afb31263773c3ec49", - "0x88d692f4976c99e763b027df9c2d95744d224724041dfbe35afc78b1f12626db60b9d0056b3673af3a1741eaf5f61b43", - "0x9920cd37eab256108249a34d3f1cc487829cc5f16d1bce3a2328fe48b4de735ebde56c8b5cf4e532a4d68792387257c5", - "0x87d34c9f5a913b806504a458c843eda9f00ff02ad982142543aa85551208cab36ebf8b3409f1c566a09a60001891a921", - "0xa2ee8339c96f790b3cf86435860219322428b03ea7909784f750fe222bc99128d1da2670ad0b1f45e71a6856c7744e09", - "0x84bd257f755de6e729cc3798777c8e688da0251a2c66d7ba2e0ce5470414db607f94572f5559f55648373ce70e0b560e", - "0x8d0e170714ddf5dde98b670846307ab7346d623f7e504874bfd19fbf2a96c85e91351ba198d09caa63489552b708fbc8", - "0x9484cc95d64f5a913ed15d380c2301a74da3d489b8689f92c03c6109a99f7431feb8a07d9f39905dcef25a8e04bcec9b", - "0xb14685f67dd781f8ef3f20b4370e8a77fef558aa212982f1014f14b1bdd8b375c8a782d1b8c79efc31b41eec5aa10731", - "0xb22fb1541aa7d2b792aa25d335d66e364193fdbf51b24a90677191cae443f0ce40a52faf5983d2cb5f91f0b62a5f20e1", - "0xb06fa9489123ab7209d85e8c34d7122eb0c35c88ee6c4c5e8ae03a5f1ae7c497c859b0d62e0e91f5e549952330aa95a4", - "0xb5cd71617ff848178650e6f54836d83947714d2e074d8954cfb361d9a01e578e8537d4a42eb345031e3566c294813f73", - "0x848d39ea2975d5de89125a5cbe421496d32414032c1e2fbc96af33501d3062745b94e27dfe1798acaf9626eabff66c79", - "0xad35955efd5a7b6d06b15d8738c32067ffa7dd21cf24afc8ea4772e11b79b657af706ce58a7adcc3947e026768d9cdaf", - "0xaff6d7c4861ff06da7cb9252e3bd447309ad553b2f529200df304953f76b712ac8b24925cf4d80a80b1adaa2396f259a", - "0xb4b88d35e03b7404fc14880b029c188feecb4d712057f7ba9dedb77a25d4023e5a2eb29c408fde2c0329718bdaf1ff63", - "0x88e96720e2f7c63236cca923e017ca665b867ba363bc72e653830caf585d802fad485199055b5dba94a4af2c3130a6f6", - "0x982675dc0299aeedba4b122b9b5f523ca06d54dc35da0f21b24f7c56c07f4280265fb64cec2f130993521272c3470504", - "0x95c77d418490e7e28293169cf7a491a7dcc138362f444c65b75d245c1b986d67c9e979a43c6bd8634dae3052df975124", - "0x8fd6c4dff54fb2edc0bdd44ccd1f18238c145859ccd40fbfbc1cf485264445b9d55ffd4089c31a9c7a0543cc411a0398", - "0xb153eb30af9807b5fe05d99735c97471d369c8a1af06b2e2f0b903b991eb787ab5a88c6e406e86225582acf8186ad5ef", - "0x826b55de54496751b0134583b35c0c2049b38de82821177e893feeeeb76ceeb747c7a18312cb79a6fc52f2c18f62f33e", - "0x91650d7205b232c495f1386bea0c36e136a22b645ffd4f5207f5870b9ce329c44524781c983adf2769f4c05b28a8f385", - "0xb8d51a39162ebb38625e341caacc030913f7971f178b3eee62dc96f979495a94763ea52152198919c6dd4733bc234f64", - "0xa1fbd3673f2ae18a61e402fe3129b7506d9142f2baca78f461579a99183c596b17d65821f00d797519e9d3c44884d8a6", - "0xb7c5f5407263398cf0ed3f0cf3e6fcebdd05c4b8fd4656a152cedcdbf9204315f265fd8a34a2206131585fad978a0d6c", - "0x94fa71804e90f0e530a3f2853164bc90929af242e8703671aa33d2baad57928f5336e67c9efdcbd92c5e32a220b4df07", - "0xb75dcea5ad5e3ed9d49062713c158ebc244c2e4455e7a930239998b16836b737dd632a00664fded275abe4f40a286952", - "0xa02f7b37fc30874898618bfcc5b8ff8d85ef19f455f2120c36f4014549d68a60a0473ddfd294530dfd47f87fbd5e992d", - "0x8b48e1626917b8ba70c945fe0d92d65cab0609f0a1371fd6614d262d49fe037f96991c697904d02031ec47aab4b32f48", - "0xb368f02c21d4af59c4d11027e583ca03ef727f2b2b7918ef623f529ceac76753a05a4ce724ce2e018da6ecc5c1c1261b", - "0xa95cba06eeae3b846fc19a36d840cbcf8036c6b0dc8c2a090afcf3434aaf5f51ef5d14b1e9189b1d8f6e4961bf39bbf8", - "0xb32ca4dfbeb1d3114163152361754e97d3300e0647d255c34ec3025d867ed99e36d67ebafe8255b8c29be41864c08edc", - "0x8e4eddefa27d4fe581f331314d203a6a0417c481085134d8376898f9260f133e2bf48576528d62adf29953ad303e63a7", - "0x92b7d5505833f00d5901ae16c87af028de6921c2d1752a4d08a594eb15446756ea905b0036ae6ffe6b8374e85eb49348", - "0xb50e9018d3c4e05ba9b28b74b6634043f622d06aa8123da7cd0bc482b3131912149214d51bdfd887484422e143c3c1c0", - "0xab980a2f5317dfcb92baa4e2b3eb64a9ac2a755da6c11094d57e781ae5cf43e351824f1dd3abb4c6df75065b3784210b", - "0xaaabb009dfcb0bae65a0aee26ed74872c226965c52a6ed0998209e020a9ee806297dba4b15845cf61e1a514de5d125db", - "0xa1fe78f67000ebb6e90fe33e1a9dd5489be6e15fedb93b2a37a961932b77137fe85d46e89a132ecf7bcfb7aa95e16757", - "0x85bc6e7d660180de2803d87b19ed719d3f195ea0a92baf9bfff6113c743f4237f51355b048549913e95be8ddf237864d", - "0x87a167968c4973105710e6d24ad550302ee47fe1f5079d0f9f9d49f829b9f5c1cd65d832d10fe63533e9ad1fa0ad20f5", - "0xb2ad1a7b95b8a89d58e0b05c8b04ae6b21b571d035ae56dc935f673d2813418e21a271cccaf9d03f0d6fa311f512d28c", - "0x8268e555319992d5ac50cb457516bd80c69888d4afa5795fcc693d48a297034f51e79f877487b6f7219cfdd34f373e14", - "0xb235411f1f6d89de3898642f9f110811e82b04ad7e960d1dd66ec7a9bf21de60e00cfabcd3004f3b5c4f89f5d9c7422a", - "0xb6963effcfe883f7ed782a3df3c40edd70f54ceca551859bcccb5d3e28fd2c1fcbdd7acc7af24a104687fd02b53c704d", - "0x862645c944e1e2909b941578cc5071afd7353fed1c2c99517e2de7573037704ef5d35accf6ec79b8269da27564209d50", - "0x90f585eeb1a053e2f18c1280c9d6a561c0bc510b5f43cd68370ed6daac4b3749852b66c371397b6a7c1ece05ee5906c9", - "0x876d9a3686feb79ce781e87ac3e3fbeef747b6ab031285e808c8a73f73f55b44507850dcaa745c0791d2cae8ad61d74e", - "0xa7ecc3b8c10de41a7bd9527228a0d3b695a651a5b5cb552a3664a887077d39ee60e649aecd68ed630da6288d9c3074ad", - "0x83529f1f2b4dc731ea05c1ee602fa2e4c3eebe2f963f3625959ba47657be30716d64e05e8b7e645a98bf71c237d9c189", - "0x834ca6b14428c30a4bc8d5a795596820af6f3606f85bee9f3008f3fb94b3adffa968d21a29e2588d7a473d8b5d3a8b42", - "0xb8d08cd8b73430984fd16e8db0525ae2b76253c92cccd7b3470add4d12d082eafb55a72bde04870924d0bdaf61f76c5d", - "0x96ef32df669690c2391f82136fc720231e4a185c90ba79eef7beaadedf7fbeb56ed264825564bdc7da01829b47f4aa88", - "0x93d637b2f04d71891a80a1ee93fd9c9046d671bc4c15c4e597cfcc36f4ae85a7efc111359628965fd10d36c39129b160", - "0x89f28dd3f7bc43749d0e3750c136385d4ffaf2c40354d3be38341416d755de7886d8108d83721b36f99feb3bccd73c88", - "0xac6392e274659f4c293e5cb19859828f101959c4c0939920a8dfed0e2df24a0cbf89a7aa983e947318c58791c893928e", - "0x83b2d4ce42c2fa0f672cd911365d1f1a3e19f1c38f32bedc82820ad665d83ae5fac4068e4eca6907bd116898966fed92", - "0xb5e0144d6e59a9d178d4ee9f8c5dba18d22747fcdf8dc4d96d4596a6e048e384cd1e211065f34109c9ed6b96010d37e5", - "0xb1a65e6b38c9e84b3937404d5b86c803c2dac2b369a97cbf532cfdd9478ee7972cf42677296ad23a094da748d910bc48", - "0x849d7f012df85c4c881b4d5c5859ab3fb12407b3258799cfc2cb0a48ae07305923d7c984ff168b3e7166698368a0653d", - "0x84d9b7ee22bf4e779c5b1dd5f2d378ef74878899e9dbb475dfdcd30c2d13460f97f71c2e142c4442160b467a84f1c57d", - "0x964e497ef289fac7e67673a6cb0e6f0462cd27fc417479ecb5eb882e83be594977fb0c15a360418886aece1aaf9f4828", - "0xae1226222098a38ce71f88ab72de6ededb2497e30580e7ae63d4829dcc9c093bdd486102b7a7441cb06253cf0df93772", - "0xa72865b66d79009b759022e53b9eedbd647ff4b1aab5d98b188100d01fc6b5d8c02b80eb6f53dc686f1fdda47d4722b8", - "0x93aa8d7d8400bdfa736521133c8485c973d6d989ec0a81db503074fe46957a3999880fd9e4e7f44de92adf6ac0abe99b", - "0xa75e5ab84399962ada1f9ebcfc29f64405a1b17cd0a983950d0595b17f66386393d95a5aa4c6c878408984141625141c", - "0x91b1e5e75f4b55ec2e8f922897537082a1414eedc2bc92608376a626d8752d5d94f22f0e78ea1970eb0e7969874ad203", - "0x83bf9c308424ef4711bfa2324d722f550d95f37d7f7b4de0487ccf952b89d7219ca94e7fa25bee60309efefd9a0e4716", - "0xa42060476c425ff7979456d3c5484bc205fb1ef2d7149554a4d483d48e2a19119f708c263e902943bcf20a47e6c7d605", - "0x8170c45ea126e6367aa5f4a44b27f7489a5dd50202cb7c69f27a2bdf86d22cf6b00613b0080d75fca22439eeaaaa9707", - "0x8e5a82da70617697e42c6b829e1889b550c9d481408fe4cf8dc9d01daccabdec01f9e1b8c27dc84902a615d539bf9bc6", - "0x80606c51401d0bf5f2700ebce694c807ab1f7d668920bdcccef2775e0939472419a8f404567bd4f9355095517eb4d628", - "0xa40314565d60d0ddf8995673e8c643b1baa77a143b3d29433263730a6871032260abc1320e95af8287b90aa316133da0", - "0xa87e07e84435f9e8a51ce155cd3096aa4b20d18e493c9dcbc0ac997ac180f3a255bf68ccd8195f2564d35ec60551a628", - "0x84d2ab98416643c457bf7ddd9f1aa82967ecea189db08f3558f56803fe7001693ed67ec6ca8574c81ec1293b84a7c542", - "0x937c3b955889ceae77f28054ce53d75f33cfe3a04f28e049cea8b8ade2a0440d5e2e8c4f377e6c1ae2115d68cc95fc16", - "0x885a911f16845fe587b15ce7cd18cc2a84295bf609732340f74e0f5275b698cffed3e9aa1440e19e6940a7fa8f24c89c", - "0xad90059a50c399996aaa0a10a8f637b7bab0dd5d9100301f0159a2c816596da55c30b2568d1717705fd2826b117a42d6", - "0x828de9ff1e095c189da1f1ee18009afe14613ac696025add6f4e330488e02d5f1a90be69edd9a17bfb3355a0ca77b525", - "0xb7aedb8394064a58dd802be6457555c0cf7b94805ed00cc66f38449773f4b1865feaee3a6f166eb51b2123b89d853a4d", - "0xb09c564ff37ccea34e90f2d50a40919a94c2e10d4fa58ffeaed656f88f9f4ae712d51c751b1b8f443dc6c9506d442301", - "0xb24882d66b2ebb0271ebb939c72308d81f653940e70d6f1bcaae352f829134aff7f37522cc42de9e7fe6243db2c4806f", - "0x8e6f8dd906e0d4eb8d883f527e926ad1d8156b500c4cfa27214450c8112267c319900de2443c87bed1e4bb4466297dd5", - "0xae42f4578e8d79b6aa2dca422ada767e63553a5ee913ff09cb18918116905b68f365720a1a8c54c62cce4475ba5cdd47", - "0xade639bcd5017ea83ec84689874175ed9835c91f4ec858039948010a50c2b62abc46b9aee66a26bf9387ab78f968b73e", - "0x8d310a57aeb123cc895ee2fd37edc3e36ce12743f1a794ad0e1a46d0f5e4c9a68b3f128719ed003e010f717ec8949f43", - "0x8606c086fcf3e2f92c1b483f7e2a4d034f08aef1a9d5db9e8a598718e544b82544268a0a54dfed65b4d0e6027a901d47", - "0x8ccd95dd673d8cfdfa5554c61bcdbe6bb5b026403a320856fe51571e7c59504fe1c035f2ad87d67827339d84c0e1a0c6", - "0x955a7cb4afcf70f2eb78756fc3a82e85ab4330eb89a87117294809beb197d1d474001e25306e8ad71daab6928abf6d64", - "0xae6b44ec6294736ea853ddeb18fc00cce0ac63b38170ff0416a7825cd9a0450e2f2b340d27a7f2e9c5ac479b4cb8a5fe", - "0xa88ec3f12b7020dd593c54376597b056e70c772c0ec62c24c5bfd258b02f772161b66e5dcd95c0c0fceb23433df9ff23", - "0xb4a83933b4de552dba45eedf3711f32714e58ae41d4dab8a6114daeb06e90a5a5732c70384150d04124ac6936ca9804b", - "0xb8b7c4fa549b0fa1dc9c1f0af0750d6573f1648767751882d41f0dd7e430e3934590757e1c8b436ac35381bdde808117", - "0xab598b911234a98cfde07234cfc0d2fddfc5cb9ea760212aa3e175a787ce012965c8fcfdf52d30347f5f1b79cf4a0f54", - "0xa9d354f9dfbd1976e5921dd80cbb56b2e15df53ce099ecb4368eff416998130d7830209282aaf1d4354129845f47eb80", - "0x8c889afff546c721969e4d8aae6e6716ad7c2e9c1914dd650e30419ee77d630efb54dfffb4ec4ff487687b1864bf5667", - "0x94ed2fa79116c7c8c554dc306b1617834dd3eab58baf8f0d085132c4688ca4a6bd38420281283678b38970a3f02b9a94", - "0x944fdc8f0516d22f1672193d183833d3e3b043e26807fb2123729a0216c299785b1c4e24b5aa56e9bbe74fa54d43e22a", - "0xa48521454a3e0c10a13d8e810fad9d0522c68eea841821a8e0e57811362f7064a8f9c50f79c780a02df7df8c277feaef", - "0x8f3d26670ab55e1bd63144e785203373b2b13b76cac305f0363e48a6339fac3364caa3fceb245527161fc2fac9890912", - "0xb4d6fe71001cb4141f6d8174dd7586d617cfccb54471e1fbce30debc2b1dead62cab29565abb140b682811c6231acb03", - "0x91dc8afc4934fcc53ef851462a055cc1c3c87d7d767e128806891738427606d2fbfa832664d2a7f95f8ffe2cf0c44dc6", - "0xb297eb432c74071764272c1b1663547ba753e66bf026643bfc0e42a9c5cdfb05a88083ad67d6ddfe6ab290678c607b29", - "0xb343d1df85be154faeb5b21741a5ac454ca93f70a0b83a98f5901d1be173a1b2969d43e646363c5d4975924e1912599e", - "0xb2d74a66e4dfc41128aee6a3f0ff1e5137a953ed7a2a0ab5a08d7ea75642f12bd150b965c8f786ad0caf55ef7c26be4f", - "0xa54141faa8dd9a567c3cd507e4fc9057535ffe352fa1e8a311538fe17e4a72df073fbf9371523e5390303db02321650e", - "0x8e229a58f1acc641202d2a7c7e120210b9924e048603b9f785a9787ad4688294140ef3f4508c8c332d2dedafff2485be", - "0x9523554c11d39b56e6a38b3b0fadb7a9a32a73c55e455efdcfda923aff1e9f457d1b7cbc859b5ecbb03094eae8b87d38", - "0xa199ffdff1812aaea10cd21a02b3e7bf3d8e80e501aa20bb2105b5f4cb3d37265abcda4fd4c298d6c555e43fa34517f8", - "0x97f1285229b07f6f9acd84559afef5daad4320de633c9898b8068c6cb3b19b4468b4445607559ddf719f97d2410e2872", - "0xa1dfff82908c90fc38ec7108c484735f104e6ce7f06097e1e80f6545702b6a0bc2a2706203cd85162edb7e9294fdedba", - "0xb12a706311c617d6c19e964e296072afce520c2711086b827cff43a18e26577e103434c0086d9d880c709df53947b48c", - "0x88503a6f48cef2f5cd3efa96a5aacc85dc3712a3b9abbb720a2cff582a6ea3c2afc49288b6832c8599f894950843ac11", - "0x83ed63e38dfbe062fe8c7e6bc2eeb5a116f1cc505c6b038990038de6051281f9062e761ea882906ccff69c9c5b8a4a25", - "0x911090d5d0231dde1189408dca939daddcb69a812ac408d1326060f0220781bcc131c9229e6015540f529d9fb33d9b0a", - "0x8a8352f1d9e5c7e80276e4448f997d420d5a7e0e2d5be58ae4106f47f867d1caa478b2e714d9c3263e93e5cc4c7be08b", - "0x9362f1ea9995f9b3850ebb7c8d5bf95927ab5ea25ee00e85d7456b3bf54459798b1fffde049d445c0d0587b0ab0a1694", - "0x8859502b391273f4a00b6c0e87e5cdae676b7baf6c402f12b3360db6a5dfb4931ece4da0e1e4d98c7a71c3d01a183a9b", - "0xa9a5edf474120f9bbec9485d8b1e6f83be68b10de3d765219b0bf3e5d2840e478f1fb2bf806d78a8b8ad22ec50cf7555", - "0x82c75daf983b06e49f0d75a042dfaae8cc92af050293d9059d6e8b01ca3ab2597e7adfc1159ed805513488944e739fa5", - "0xa5cf240f04a9bfa65b811702c923d209e01f9535e217fa55ae3e0d1eb3257d6749e5587e727091e860609d1df29a1305", - "0x95608ab8ade1c9fb814bad78d9cc99a36ad3e9562d5319830e4611ceea508ef76be04639294be9062f938667e33bce6e", - "0x8e44181f35c38b02133473de15560ae6588ac744cfdaf5cdfc34f30ca8e5ff6c85eb67dddc1c7d764f96ed7717c89f06", - "0x8007b6ddece0646b7e9b694931a6a59e65a5660c723ebdffb036cf3eb4564177725b1e858ed8bc8561220e9352f23166", - "0xa2d9d10fa3879de69c2a5325f31d36e26a7fb789dc3058ee12e6ccdda3394b8b33f6287ba1699fce7989d81f51390465", - "0x81993d0806f877ca59d7ffa97bd9b90c4ebf16455ea44b9fe894323c8de036c5cc64eacf3f53b51461f18fa701a5860d", - "0xa20030f457874d903b2940ec32fa482410efecb8a20e93f7406fc55ab444e6c93fa46561786e40e9bf1e3c7d5d130bc8", - "0x80c72d4985346ac71a231e7bbbb3e4a91bf50142af0927e8eb86069303eb4ce7fca1aa5b919d5efc82f2f09b41949acb", - "0x91b857d2f47f1408494940281127ba4b9ac93525788e700889aa43402eedea002e70eded017f5f5263741ed3ee53a36c", - "0x97445d007f08e285ea7f4d25e34890e955dac97448f87d8baa408e826763c06cbd58dd26416ba038d6c28f55bcea2d3a", - "0xa409c89526c2886f6a6439e2cd477351fc7f886d1a48acc221d628e11895a4eedd426112a368a0dbd02440cd577880a8", - "0xa2c6adc7866535f6ffc29e00be4a20fa301357e1b86dff6df5f8b395ad9fb1cdc981ff3f101a1d66672b9b22bd94ec0f", - "0x8887fc53ffc45e4335778325463b3242190f65ae5d086c294a1dd587f62dd0d6dc57ca0c784bf1acaa5bbba996af201c", - "0x9731d3261a7a0e8c7d2b11886cd7c0b6bb1f5c57816944cc146caa518565034cea250eeee44ddffaeb6e818c6b519f4d", - "0xafe91c706efb9ee9e9c871e46abde63573baa8b2ea2b61e426cd70d25de3cc8b46d94c142749094287a71f4dfadd3507", - "0xae7bdf6ecc4fc0d8d8a7fa7159aae063d035f96ca5a06b6438b6562a4eee2b48d9024dbe0a54cfd075eac39b7a517f2b", - "0xa382e5205bfa21a6259f42e9ebc11406b5da2aad47f7a722212fdd6fef39117dd158a9991ff95e82efa0826625168a1c", - "0x862760c80bf44c2d41c2a9a15c887889eaeea32acc894f92167fb6f72593377c228499f445ccb59794415597f038ac9e", - "0xb4e96595a91a611c4563d09f29a136a4c04f07be74dd71a6bbabc836617ecb95494e48971a8229f980b2189fd108d2e5", - "0xb5e7200357317c36244c2e902de660d3c86774f7da348aca126e2fc2e2ba765fa0facd29eebcb3db3d306260e91a6739", - "0xa64c7133156afee0613701189c37c1362e2b4414f7e99408e66370680c554de67832c30c211c2c678dab5cfcdcecb3f7", - "0x88f4cb67b1db497a91a0823ee3541378133eb98777842d73e43ab99efe8aa52fa02dfb611c1691be23684618394988d6", - "0x89a9382a147d7387d0ff9516ee0c75cd1f8ee23333f4a2c9693d1a8cbe03680bc5b10c43c238c2190db746cac409bf39", - "0xad510bcc067373d40b05a830bf96fac5487de1ad5b708a13f62484c09b00fba6c5b00b981004e5ab3f28e55c9a5bce26", - "0x8384156d7117675547279ad40dc6bf81e8f9a57b2d8cfebeea6b9cd1d8534dc0cf704068bc3ba0815010cd8731d93932", - "0xa818fb76e53165b2f86c7f2317d64cf5e45f48405a34560983cd88bfbd48369e258ce2952233a8ce09c464e07afcade6", - "0xab19a4ed90527e30796064634b66cdc023bc5966e2c282468f5abef7879fc52986d5bb873a796b077d10e7b374b60309", - "0xa17dafe2484d633fe295f8533662631b0bb93cdb4e7cd6115271f20336f602f7f8b073983cd23115093c7f9891c4eef5", - "0x804acbc149d0334c0b505a8b04f99c455a01592a12f64d1ec3b82b2f053ccc4107e47f418f813d6f400940c7c8700a4a", - "0x965e097a825d8511d095b247554ec736bcb3701ead3ba785bd425cbabd56f4b989764e0965a437fa63e7e16efd991fc0", - "0xb6701675ca27d7a4084f06f89bd61a250b4a292ee0521b2a857c88c32b75f2a70b97f98abce563a25d57555b631844e0", - "0xabbdf65fcbdf7d6551ccd8d6e5edc556f1ecd275ccd87ee2bda8ea577c74615f725aa66e0911e76661a77f5278e0c2b9", - "0xab715ae372c900239a0758a3524e42063afc605b8fb72f884dc82ab9b0ff16715f3fb2fd06f20f15f9e454f73a34e668", - "0xb45f41ea1d25a90af80a8a67c45dea881775fed000538a15edc72e64c7aa435a5e4375dcdedc5c652397c02b0bc61b16", - "0x86f7be9252f8ed9078e642c31a70a09639899f7ffcd7faaf1a039fec8f37e1fa318fba0ed1097f54fc55d79900265478", - "0xa30e5ed4277dd94007d58d5a3dc2f8d3e729d14d33a83d23c44ddfc31c6eac3c6fe5eb13b5b4be81b6230cfd13517163", - "0x87e723d916f5fcda13fab337af80354e8efe6b1c09ae5a8ceeb52df45bfca618eb4bec95fefef3404671fb21e80bf9db", - "0xa521b8a04dc3abd3e9e0454b9a395b3638e5394dc2d60e97fda61b0a1880d1d73a64a4633f3d7acbd379bde113240d03", - "0x851686c79c5403d5f05fbaac4959fcbfdfb51151bec55e10481b3c16e3be019e449907ae782ca154f76a805543d5755d", - "0x8ec1929e746b6c62b0c3fdd8f4e255e5c707e6e0d8d57ff9e409ae2dd6e76fdb50af923749992cf92d1b5f2f770bafbc", - "0x9175f7b6820d47205c9e44f8c684833e1e81da46c1fdf918a4dcafbc3231173f68370d442a20e45f8902bcab76a4e259", - "0xb4f66c698115333b5ac00c9fe09aa9e1e9c943fbb4cce09c7d8a6ed4f030e5d97b48e944fd6d3e69ac70f1ae49d35332", - "0xb958878b875eead61a4416a4597b1c567ddbb1eaaa971033f4a656f01a277822c1f4ea3972045156c2a5a28d159f5ddf", - "0x8188de8ad5258024d0280137a40909d24748137ac7c045dddd2bc794eac8edd5850b9d38f568fa8174b2c0593bb57e96", - "0x91152c7bafce7a0358152221081bc065796fa4736bfc7d78076a0a6845287cde2ee2a2c9b96f500297c0a00410634888", - "0xa5328ab939a2d3bd4c21e5f3894c02986b6590ad551c7734be3f4e70380eb7bc19629e9031b886ce3b4074ee4edee63a", - "0x97c4d49db40e266bcedaacb55edca4e1ebf50294679b271f3a2332c841705089b5ba96ef2064040fa56c36bb1375a8d9", - "0x85cf0514f340f9d865b32415710d7451b9d50342dbf2c99a91a502a9691c24cd3403cb20d84809101cd534408ddf74e8", - "0x950c3d167f59f03f803dcba3f34fe841d40adc31e5be7eefff2103d84e77a7cbe4f14bd9c3dfa51cde71feb3468a9c00", - "0x96a69624e29c0fde3b92caf75a63ac0f3921e483f52e398652f27a1ec4e3cc3202f17af1f66224731bc736a25638d3e4", - "0xaeac4170cf4b967227f66212f25edc76157eb4fb44c84190b520ecc2946470c37da505790e225fd1b0682bef7fc12657", - "0xa94146a04e3662c50c2580ae1dba969cbb3fb0f43a038729c9e8be6ed45860b2c7de74f248dfa50ccdbe2ecaf3f2b201", - "0x917b8e2880e85b8db723631c539992ec42536146e7091d4a3f87d37f051b5da934d84393523814f19962c78e6cb12ef8", - "0x931f140ff8f7de79e399f5cd8503558d566b5c2ab41671724dd38aed08dd378210f01ac8fa9911f3047993dbc10cf8c4", - "0x859eb9b560bc36273694f8ae1a70d25e7f206013597c4855a11328162ba1254bb736f1ae41240c8ec8dea8db035e08f2", - "0xb4ad2cb2c3a3e6ab1e174f2dbfb1787a8544f3c9109215aa6d33265ef269455e3cde9858738b4fe04711a9cf9050e7d4", - "0x8a3b342b87b19c0cdb866afff60317e722013c02dee458ba71e7123edc8b5a9f308c533b9074c7dd0d684948467502d1", - "0x89185ac5cc5ea8f10a1f2a3eb968bb5867376d3cff98ef7560b9a0060206c4046ff7001be10b9e4d7ad0836178eba7e4", - "0x845f48301f25868f6d0f55b678eab1f8458e3321137dba02b4cfbb782cbc09f736a7585bf62f485e06a4e205b54a10b7", - "0x931a6c523d4a66b51efadb7eefadba15bf639d52d1df5026d81fd1734e7f8d5b51b3f815f4370b618747e3e8eb19699c", - "0x8eb3a64fa83dcd8dd2258942aea3f11e9cf8207f2fdd7617507c6dae5ea603f9c89f19d1a75d56eaa74305a1284ce047", - "0x912a5050ed6058221d780235fb0233207c546236316176a104a9761bc332323cf03786dbac196d80a9084790506e0a88", - "0x945fe10ec8dc5e51aa6f8ba7dace6f489449810f664484e572bfe30c2fe6b64229f3c8801e2eb1a9cb92ff3c4428cdf7", - "0xb62383bf99c7822efd659e3ef667efee67956c5150aea57e412cbd6cd470807dfaad65c857fada374c82fcfca2516ad1", - "0xa727a31c45b2970d08a37e169ea578c21484dde15cb11f9c94eaaf3736652619ce9d3a44e7431d50b0e75b658ebbc1da", - "0x97bf54ea9b84b82e4616027bd903ef6152439f1c6a8e1bae6db1d10fdf016af2cac10ff539845833dfd1ddad1403aa8c", - "0xa08cf36437e010e59b2057aedb7192e04b16f1cc66382cdef3490b7ad1544ae51f03e87cba0fe43a275841c247a2a0cf", - "0xacafab9fa28c1a607df2246490b630ddda1ecf0885ad24c2ecb2c2c1b7b9c7de8066714bf5b9b25f61981d08576789ec", - "0x851f0375128d2782586223467d0a595f4c5baa79616622a32f7d6ce1f08af06f8a109bd6527f88d93367dba17be661e8", - "0xa2f1187c2a7cbf776653ff834ed703dd32e68eaf36f0700709be929f4c0ce5fa1d9930d1e3ea2aa01c7a16239e66cb33", - "0xb3721f4a5d24ca112f020cb3f849543bf0e7f84b470fb00126ae80aaaa6f2c208d8359cd82ad9fbafd3ef2ac70656fb2", - "0x98773ac3ce9528c73cfd8e7b95976ce597f67e146357642ac4fb6cb35046f3f39cf6c4a7b5af5c7740dda358aa0d2d08", - "0x92c883a5d820541692af75be1b25dd4a50a4b91f39f367a551a7d5ad6065a26b60d68221a01e4950559717b559c2626a", - "0xb82e46dd25fd1234dad26fbcd8bb5177d7b87d79d362ffb9c2f6a5c16eb2ff324d135996fcd6274d919634597869d772", - "0x82a53ed356ced5e94d77ee2a7f6e63f2ad8240aff2d17c5012cf5d1f18512c88c24793339b565dfbb659bd7c48dcbcd2", - "0x84d20c7859b35a1cae1ff2b486d50822f9e6858b6a1f089ce4c598970e63e7c0f7dfbcb3337845e897a9dedf9d449dd3", - "0x974892e5cf5ee809e9353d00e9cd5253d04826a8989d30cf488528c5dcdcad7650e23b4d228c3eb81f6647d2035a9e02", - "0xb2327854910dbf3d97fe668da5fc507e179c4bc941f39bdd62e8b6035f004449c467240f656417e501f32dee109f0365", - "0x88888f73475613d45d0b441276b1dd55835b69adfb27e26c4186936dae047b85478cca56be8dc06107b89a28f3bbb707", - "0x836ba22e40511feff81a5dace3df54e2c822b55e66874dd1a73929994ec29909ffc2a8e39bfc2d16e316b621eb4a5ec6", - "0xa754cedcccf4165a8d998f326f3f37d2989f92ca36d9da066a153c4aab5a62bb0011896bcbf90f14c18e00488d4123bd", - "0x86c26fa9584314292c4b7d6fe315f65dadd0f811c699e6e45c95a7a4ea4886c57dc5417b67edd78e597d037c7689568e", - "0xb205589648aa49ef56637712490e6867aa3b85b2b31e91437a249fb51bdb31401bff57b865c9e27293b30014b4604246", - "0xafab0843ede582e5a1898ee266235066b94ea378884eaf34919ceaacc0e2738e1074b6ed41e0a1dd9711563e24f0215d", - "0x996ed65fbcab7611eada5bd0fd592d3e44705098b8b1dfba6dcdbdcfa1382fe893fa55270a0df0be0e1938bd71ab997c", - "0x881bc448a5ef8c3756b67ecb1a378a5792525d0a5adb26cc22a36c5df69e14925f67c9cb747a2f7e5f86ba1435509d7c", - "0xb219303c02c9015c6a9a737b35fb38578ab6b85194950a0695f7d521206e1e12956cd010d4d6c3bc3fafd6415845d5d1", - "0x91748829bbd005d2ec37fc36fee97adaccb015208b74d2f89faa2e4295679f7685298f6a94b42d93c75ca9d256487427", - "0xa41d6fd33b9864ebc404d10a07b82ba9d733e904875f75526d9a1f1c1c08b27160dcdb9023c5d99b8ff8a3461d57281f", - "0xb68978d39c97d34f2b2fea61174e05e05e6e49cde587e818b584201cf59b7096cf1807b68f315119c6db8d6110b28a9f", - "0xb64e66cec798022d64ce52477475d27ea7340817fe7f570617f58c3a9c74071d7ea6b54743d4f520b62aecad9a3a6620", - "0x87b2b9e1c1786b7824f239a857024780a1457e51c64599b858118885833fb87a17d408bc09dcc0607d15ec1e53683a74", - "0x9814799bac07dab4f0c934cc3c051676ca13abd49cf8d4739864e5bb9f2a8474897695113f49239f28832a8658332846", - "0x806931a1526a843a9c2045943d616a8102b02b1f219535a1f1fbda659a1244f1bfead52ca7f1851ff8a97169b91c9ec0", - "0xb8678249595a9641c6404c35f89745b93d8e7b34d9d44da933a1b2f1606972624c5108f1c04eb42e454d0509f441ed9e", - "0x81426714851741045a4332eb32b6dfe6422a4a2e75b094fb7c3f37da85648c47ee8af1e54ba26f4e1b57ebe32d0e8392", - "0xb7a1875ea3f119fe0429fd9068548f65cf2869f8519dbbce0b143e66127cb618c81d7578e8391d676b2f3963e9d87f43", - "0x872220a803ea0c6294cdc55aceea42cfacfd7a482982bcb90c0361c351a900c46736a890609cd78f02fb5c8cc21fa04b", - "0x974f0380197b68205ff4bb2c9efe5626add52c0ad9441d7b83e6e59ddb2ed93ad4e9bbdbf33b3e0a206ed97e114ea0f2", - "0xa840f2d9a74fca343aedb32ac970a30cbb38991f010d015dc76eb38c5bb0bfe97dd8951de925a692057262e28f2b4e9d", - "0xb0913c3ce61f12f9fdc4be3366ed514c3efc438f82fc58c4de60fe76098fbc033a580ec6e4531b9799611c89a8063a66", - "0xa0180d533eee93b070dac618be1496f653a9a0e4e3455b58752bf1703ec68d0be33ec0b786f9431ef4208574b0ad316e", - "0xa4a6b871bc95d3aa57bed90e14a0a1dda6e7b92b7ae50e364593ce6773fbf736672b1f4c44e383af4c3cc33e017a545a", - "0xa3f44cf19fe52bacc4f911cab435a9accbe137bdbe05d34bdd8951531eb20b41d17e3540e8d81e6b3eea92c744562ee5", - "0xae6b6d0ff3b30ff0b7f9984ef741cba27ffb70d558de78b897199d586cf60622ec2d8a9d841712fe719cf0f97628842c", - "0x87abf72f98c81d6d3a57ab1e224fe4b502ab0d8090d8abc71791271550b721c220d4e2e7da3be94a20c0e63d98e39a50", - "0xb2f73ebdfe7133af57353052f4599776e16862905e64d97e1020c4bb84132e476d1ab79a9fb71611410f3f9d56c95433", - "0xae1a928253af2b210d31e1b64c765fcbd20a96b8d53823a6b9b6e7fc62249abf4a66c6a6aedb0b687e7384af9a845e0d", - "0x99c54398627833ca1435718154de171a47c709e4d5c58589fdabe62e72f2a7a11ae561bc31d7cbe92df4aff23e08cd0e", - "0x8a1310bbf1a31fae18189479f470977d324dec6518a5d374ab2ffcc8f64412fb765df57d2ddf69b9a6efaeb2b4c723b8", - "0x898312c6c0d3d3438229b19a8a233eca8f62f680c2897f4dd9bbcacde32c5996d56ac0e63e3e9360158761185491ce93", - "0x81b3f965815b97bc6988d945496a51e4a4d8582679c22d138f3d3bd467ed1f59545da2d66e7b4c2e0373628ae2682686", - "0xb9aca91c6e6f4199beb6976b28e0e35e36e8752618468d436b1cf00d8d23538d0747920e5b2c31f71e34dfe4d5c86a0d", - "0xb908f4aa18293295b8cacfda8f3ea731bc791074902c554764c603ab9a1de1bbc72654fd826bffc632d95ce9f79c27d9", - "0xa7316ae1baf4b1196961d53be7fe36535499287aba9bc5f3bed4323039b4121b65bb0bd15a14c1b9cd8b65ede3566da2", - "0x815e39208f205c5fac25ac9988c14a62ab01657c7737a24472d17b0e765644bc2cbb7ff1e8ea169b8b0b17b6996c4704", - "0x89a451d2b740cdaa83ccaa9efb4d0ff5822140783979a4fee89eda68329a08c018a75d58bd9325bdc648b0d08340b944", - "0x8cd08f768438c76bae6bee1809dd7be38ec42e49eb6a4d6862db7698f338bf6b4b409088e4f3d1c5bee430295b12a71f", - "0xa4bd8c312103a4bfeb25b0cfffec7a1c15e6e6513b35af685286333c1dce818ffeb52826f2f5bada6b67d109c4ab709e", - "0x93afbef5382d89fa539ca527f3e9b4a8e27ab69fd5d5023962cc6d8932b33cb4dfc5f14343e1a3749bfd5e100c9924e5", - "0x8d8e69d046992ec9ff14f21840809166cae8e0e9e7c8f14fb29daf163b05abe6611daa4010960e1141c5ab24373fb58e", - "0x96f8e72e96ba673c9265e9cc312f6b9c3b931745fc62d2444d59404bb08e5fb02ddb60715181feb9971cbd954526a616", - "0x8d444c2b8e4d0baadb79e3147a2ee20f1bfe30d72eb9a02f15d632185fb8f4e8c3116066f7de1ebfe38577aaccacb927", - "0x971410c0b10e3698f4f64148b3d2148fc6a4a22217fcf4253583530a9d6fbec77e2cf6f7bb5e819120a29c44653de3fc", - "0x99e7e1857bd5ee57007b7b99494b1f1c6bf1b0abd70c054770427d59a3c48eda71b7de7a0d7fcf6084a454469a439b41", - "0x8c8a4cd864894f7a870f35b242b01d17133cb5dfdf2e8007cd5f1753decc0d1fd41be04e1e724df89f1d727e760fdb15", - "0x890a24328bdeaaadf901b120497d1efa17d798f6f4406661e46ecdc64951f9d123d724ab1b2b49e0e9a10d532dd6f06c", - "0xa7cbe1f42981c9518608569a133b0b449e9d67c742d62f0d3358112c97e65ee3f08ec0ff4894ce538b64e134d168e5c8", - "0x87c976dea77b3b750c3a50847f25b851af95afbaad635f9bb9f7a6ba8f0c4faeb099dd777cf7eac41072a526474cb594", - "0x9882aa5e9bcc4ea2dd3de4bb5a0878a672bea924b50c58ae077563b6df0268910a60e969d3da1694ae7394ad0d9acd3d", - "0x90d35ce677327c461fb5dcb032202e851af1d205e9d21a34ed2b95635f13f8fb8dfa470ea202ccfa4b08140d0cf1d636", - "0xb3b4cbb521cce2b681e45e30a4d22078267e97ccdbdc611b2c9719705650dd87e0ca6e80cf2e174f8f8160be94232c36", - "0x95892b00478e6b27ed09efe23a2092c08e691b4120336109d51e24efbf8aba31d59abf3cf55c0cdab1c210670b9743ba", - "0x8643018957fb8ef752673ad73102d0b928796c6496e22f47b6454c9ed5df784306f4908641ae23695db46ebfcfb0b62b", - "0xb166ce57669bf0543019ecf832d85164c551c3a3a66c05b17874bccd5d0ae87245925d6f8edc62ac13dbd5db265823a2", - "0x89fb4800ce4b6c5900d58f1a216ad77a170ea186f3aa0e355840aeedcf374e92a15ae442800c9d60334544be020b17a4", - "0x8c65e586215a97bf11ffc591bce5147b4e20750e82486cc868070c7736c3de697debc1f335674aef24b7afdd41922d93", - "0x90f68ce0c97d2661d3df1040ce9c4fa106661a719e97c7b2d7c96f0a958930c57d6b78d823a2d41910261ae1f10e7b0e", - "0xadda85e1287371ccbe752aa2a3c1d5285595027ba4a47b67baf7b105a22fb8548fa2b5b3eb93ca6850ecc3995f76d3dd", - "0xb26535d218f48d6c846828f028c5b733594ce01186e22e412dd4f4a45b3d87d2ac1bfe5d54c987e4e8aaddeb86366d7d", - "0xa081bd86962ea3d4fd13df6481f3aeaabdd7ceae66f7bbb913e601131f95d016cf147d045253d28457a28b56f15643c8", - "0xb3d852cef4c8b4c7a694edbf6f0e103f3ae7f046a45945c77a1a85ec8dad3423636a89058fafc6628aabff4dbb95c2ba", - "0xb424ffc94e06e6addc90a6324e0482814229b5902e2a266d0c2d716e40651b952bc9f00d7dad9b6050377a70a72c7f24", - "0xb2cafd908cae0ca22eaa2d9a96175744897a20eb7b0a6d43b0098cb1c69e3cb55373888201e4ed32816655eb7d8a3dd7", - "0xb61177ecf1ae9d7e7852d98cbf6080d9f1e33c90f2436720b4ea4690437e8c7850c3754768fc1312cb4e838d855c5ccc", - "0x81b486644e1ae22cf0ba3a37e1df34dc186c82a99ab35ad6f475c37babdea574ddfbe5811d4aa020581292a793d66bd2", - "0x97ae848a823ea7a99f91834e537fb47208f616c08fe32c8f8fe06bd35c9b638698c513265d0b4de9e572a2f9692b98e2", - "0x81b8fef4ea5d399c65e78f40e47c559ada86d890777c549ce362e7ab81b3bfb00d5ff4ae4ee30fd7bda7ee90d28f85d8", - "0xaada6912cc748923ea40bf01922c06c84bc81b2ab0bb3664a0579b646f03d47ce88de733ac7f2cb9be4a8200584cdb71", - "0x89b48b9c79332f8f58eac9100ada5bb7decdc4b1555c5d383e2c1ce447efb0ebdff9c50bb52bc3042107f33a61ab2520", - "0xa32ecca8b870b2b6e9d10b5c1d8f925b3d629d271febad65abed316262bb283c60cade0e91047fbd0fac53ac6db372b9", - "0xb829cd1f13409e3573a8e109c9541b0a9546e98b6c879a11152b5564477ada4d8cb4b3079040e05a5cb63d75ef11eaab", - "0x91f3b100baa19e960b170fe9e03b799faac5b9c6f305c56115940bf81f6e64dcb9cda77e8de70ed73a21c0e8a74acc58", - "0xb25b5e872c84065aee04822bbcb4f3bdff57fbd7cea314c383765cc387786c17de3d5bb3de3ae3314bdede14542bfac6", - "0xa89bea9eca1f5a17a3efccfa4987d8e5366b0dba70ef1fef43aaea83c528428d1498c8b056ac27f16e8946ee93f7028e", - "0x818a1f7b0b8b06ea0514d6b4a0296da4f69cb18ac8e48c5579e6ba2880b06215fcbe81672566b8b94fcc3c0cadecb191", - "0x98dd6e6b4b4d63d9aa7464a2be08ae8babac4da7716a3f109340bc9187d59c6ca0c88e6877a67c65096f64a3ced22a4b", - "0xa2069c5bac4f6590042aefb37570cc20908b0df9d0130180f565ed8a53b4ea476a274de993561fb4d009f529fe7aa1cd", - "0x860b7ec2410f033a7b0c5ca08f88a0ad29f951a5ebd5383408a84367e92f1bd33bee3b87adef2466b7e33b47daabf30e", - "0xa408855a8414102c3cb49f47dda104edf0887e414723da59b6b6537ada7433529f6a4d1a4ad4fe311c279213cdd59356", - "0x8ca0d81dcb43b89a4c6742747d29598ede83a185a8301d78c6e7f1c02c938441360a1ab62a5e571e3eb16fe17131cbc0", - "0xaf7875a495cb4201cdb26e23b7c76492f47f8dd4c81251de2397d73d4c8d5f419cdbad69ba88ef0dc3552e460dbcd22e", - "0x80e901e433dca34f3d386f39b975e97f7fc16c7f692808221fb2ee60c1aaa8db079cc48c7d72fd548aaf8dde8d0b8f05", - "0xb6062319e13926416e57a0ffc65668bfa667e708a4e3f5cb26d8a6a32072f5b790d628052d5946c5068dd17cf4a81df8", - "0x90094b569e8975f8799863798912dbf89b12d2c2d62b3e5fac7efc245436fcd33af23b8c509ae28c6591d3f020966e06", - "0xa504f72d3d06a0c9b188a1035c7c6d80047451c378b6c5b2ffa1f8cecdb64871cb6440afb296974c0a528e5e563061a1", - "0x959061c4924e133a419e76e000e7c62204093576ff733ce0b8ae656ec6045ef94c5a1f3c934fb76fa9188c5eb397a548", - "0xa8b9d0b58de38cb86cb88fb039a7c4c0c79e9f07f03954af29013baa18fc2633883f8f9ca847209c61a8da378f9075d3", - "0xb16d8341da4ff003ed6d1bbdb3be4e35654a77277341fe604b4c4e4a1cb95e61362094fb3d20ab8482ea14661c8b9852", - "0x8ea4ca202e3aed58081a208a74b912d1a17f7b99a9aa836cfaa689a4a6aa9d9fbfe48425cad53b972000f23940db4c5c", - "0x96a372f55e9a25652db144ec077f17acc1be6aa8b4891e408f1909100cd62644a1c0296a3ddc38cd63ef46bef4e08462", - "0x87df40018ab3a47c3782e053dbd020f199fda791f3109253334a71be4159f893a197a494de8f94d6f09efa5811a99977", - "0xaff82d2ea6b3ad28d0ca1999a4b390641d727689dc2df6829a53e57d4f6418196f63a18495caf19d31fc23fdff26d5e2", - "0x9091053c4a18a22d13ad309313b6d2133a96df10fe167f96ec367f9b8c789ecca7667f47d486fc5ba8531323b9f035ac", - "0xa4842090515a1faccc3d8cadbb234b7024254eba5fdfcef0d15265c7cec9dc8727c496ad4e46565d1f08504c77e511d2", - "0xb1d8a37b1a97883d5804d0d2adaa8dbf0c2d334ef4b5095170b19613fb05e9c648484093d0c70d545cf9b043b449c707", - "0xb1ea40f3dd1c3d437072f8adf02c32024f32488dd59389d1c3dfe78aca3df0bab7767f6ded5943cc10f50555da6092f5", - "0xad219c6a8149f10391452892b65a3268743baa7402736f810a35d56cdfed83d2172b03f15c205f0dc5446baf855907a5", - "0xafe44c3e1373df9fc53a440807fa6af8ebc53f705e8ee44a162891684970b04fb55d60bc2595626b020532cb455ee868", - "0x859ae154b017eae9be9da5c02d151de747cc23094d8f96d5db7d397e529b12fb55666f55e846e2bbe5e6f5b59c9d8b05", - "0x8aa01354697de23e890fe54869cd3ec371f1be32064616ca3a556d3019541ba8e00d683f1396ca08e48988f7f7df5de4", - "0xb8f682487460b9d825302c40a7d6dd0353ff43bf24cd8807cdfa46c043e3f5a7db182b27a8350b28e91888802a015af4", - "0xb6d4d6c3ac40f8976b50be271cf64539eb66dc5d5b7cec06804dfe486d1e386037b01271cf81ef96dba5ea98a35a4b43", - "0x9385a2fd1cd3549b0056af53f9e4a6c2dfcd229801ffda266610118ade9a568b33e75b6964e52fcc49c8e3b900e1e380", - "0x98f4aa0e4ef039786cbd569536204e02b0b1338568d1d22bb5bc47b5e0633fb7ffe1da93eb9d825b40b9b7f291f84d51", - "0xb7b3460cf706dc270a773c66d50b949dabad07075021d373c41fbb56228355324d120703e523ea3f345ef7249bfff99d", - "0x81b826255f95201987513d7987cdc0ca0529524d0e043b315a47583136dbada23a114d50d885bb3f855fa8313eff801a", - "0xafdc6c35161645a14b54f7b7a799910e2e07c8a5efe1827031a2eecd5d9263b3baa367fdd867360fabc41e85ab687e74", - "0x817b361ce582153f2952f3042e235ee2d229e5a6b51c3d3da7bbe840b5c6ec2f01446125045848d15fd77dc46c8a8fe2", - "0xaeb599265398af6e5613297d97d2b70222534590fcbd534d68b24a0289b6366ac8188b753f6fd1000ee73ef44f8fb7af", - "0xa5a9e528b606557be64460c1ad302a43e741357827b92ddc50766a7e6287740fc23bd528d9faf23345ce8bff527d5bc7", - "0xa8d3b3b438d5f75efaae6ce7b67c2212899ece5b5bdc9bac655e271fd1846ea8560e646fdbded3d9363eefe29473d80d", - "0x984c7976d557e2f591e779c2885f5033da6f90d63a898d515b5da3adbffa526764cd8eb679b771573fdf7eed82c594ec", - "0x8ac748689cc3280e064807e68e27e234609e3cc87cb011f172204e1865ad7fdc78bec1672bd6e6fddcf4e7902b0f38bf", - "0x877bb392059540b1c8f45917254b8cc34fb7e423952bdc927e0a1622efec4113fa88988686b48134eb67ddebcb7c3ef4", - "0xac04b154ccd307ca20428091585e00121b61bae37b22d5d2a1565bc1134be3c81ccf3715fffebe90744164e5091b3d9a", - "0x90745c04278c3a47ceea491d9dc70a21a99d52648149b1ab623b5396b7d968fd3c4d1a2d08fc5638e8790463e0cf934e", - "0x80bf26ca7301e370f101cc69e7921e187cf5315b484fc80a872dec28bb65886569611a939958f4a3d2d3da4350011298", - "0x87cbf4d6f0c06cc5f24e0f173a5f2f9bf2083a619dcce69a8347c1a6cd1d03325544610f2984eb87a13241e6ab9a22b7", - "0x8909368817a515789ff4d19ed26afafa5729a24b303a368ea945a9287bc9facec9e1c8af19cbec8dab4acbb6a6ddf6c7", - "0xad8d2f82b08e0990dfd6b09fd54db3a30fd70aad218275550f173fd862347e1258a4716ca2bf4c40e4963850b2277eab", - "0xa9467ceacf9337cae4f2c7eeb3e03752ac7d77692b07d5e5d75c438fbe7dc2029ff84f7759372a0ddfa953b4ec7e9e38", - "0xa5feb7669e84b977cb1a50ff3a39c28f7ad1ecc33a893fdf1ddae7a0d8a4c5f6fbaff25cc56631b708af038a961f3b55", - "0x8f2e1fa07963ba18db890b44c3b9ae7f8992b702a5148679df69e4d9d4b1c082b2bd2ae53f96a4fe24b54f3dc1588f17", - "0x896778f35cbecb43f001277c306e38a9c637275101f1a09546f87378b10ccc025644bc650b3b6c36e4fd0c09fbb3df35", - "0x91dc702778176a4d089dc65502d703752dd9a766f125ffef26bdc38fe4abcae07cdea14102c3448d10f8dd6c852ee720", - "0xa5df3004cec6b68b937cadded0dd2f48bd3203a903a3e1c22498c1193f4567659ecaaf3deb7ed7cf43796da9188f5dc6", - "0xb18b4c8ffcb8599c24d9851abf8ee43047cbd4c9074c9cfbf88376a170da4554978988f550afde8a45306ca32713c204", - "0x8370bc38c84da04d236e3c5a6c063e1db6613dcc4b47239d23efdcb0cf86846955b60da3e50f17b17cd3f7e0c29302d9", - "0xab7d6bb6be10aa52ef43abbe90945e78e488561afb959dc2fe768f8fd660d267c7203a2b7bdfa1b44cd07898f4849e06", - "0x965c96047d82d76ec2cfe5035fd58d483cd2cb7f65c728ab3049562c5d1943096d6a5014c05babc697d79c07907cf284", - "0x9614f7006aef6f0478ebd37fbf17276fe48db877394590e348c724059f07c3d1da80d357120d3063cd2b2bc56c58d9d6", - "0x819c7b2a1a4bb4915b434b40a4e86dd7863ea85177b47a759bc8ecd8017f78d643982e8a091ee9a9e582f2b0208725a5", - "0x8e159a185b5790a3ed444b6daab45f430f72f4ac4026750cbd5c7cd7947b5e00f2b10eaaf5aadf8d23054c5b29245546", - "0xb48cb6f6c0aaea04833e10d735b67607846158b6663da380ef01c5bca3c9d537611716867dc2259883e5bc9daed57473", - "0x8b48ce8b5ab76b7d662c29d0f874f5eec178baf3f14221bffd5d20e952f54f3ed053182a486da1d1f400e0acef58f673", - "0xb6fd3cba177bfbcb5e7ebb1e3c1967cad5848c09c615ba2a6c277908f8b1f4f1ac5f184c33f2a401e8bdafcaed48bb88", - "0xabd8f44c4a447de8fde1c119f4fd43c75b4cc99de9c817a019d219d4b2ad2a73b60606c27e36e9856a86bf03e7fc861f", - "0xaf9f7e8b3e9e8599c7e355433c503a05171900a5754200520fd2afed072305be0e4aebb9764525d2c37a5a7eede72025", - "0xa0960a58bd2681804edd7684793e3cbb0e20d1d4bd8721b192baf9aee97266be14c4ee8b3a3715845dca157ba2fb2c1d", - "0x949a37213209adfbfa4e67c7bad591c128352efd9b881c1202cf526bf4f657140ef213acf0efeb827a0c51a1f18809c4", - "0x9192fae84a2a256f69a5e4a968d673bebf14ea9a2c3953f69fe0416f7b0fafa5166f3e4588d281f00d6deac1b6ec08bc", - "0xb1a249662f34a88d2798eae20c096268d19f1769d94879b8f1aa40a37b3764349b8e6ab970558436a88a5aa5c37e150d", - "0xaea87086dcd6de0b92886b3da0813ff271a7107ab1a3cb7021b85172c1e816a84dbb1a8fdb47e8a8eb5e6fcddd5b919a", - "0xa586b5078b3f113eec9f074430bcf9aabe4e82752e5b421c6e31d1c2a911512e34154bf8143b5197e820c5af42aa8ac7", - "0xa6eda122e400a6600f025daa383685a10f72f62317a621698bd0106b331077b05ac1afc68ece7a2e285c54a366921a3c", - "0x8875e9ba654ad7b1d57ede84e2b702600416d40f7475fe2df25dd1b95c0178a227ee187547898e5b9d1ce8ce9ebd15c9", - "0xaf2cb289f8c75f4ddae9e3ef9c1977fe4d4d513e411777b03b996f5baa372eb995b5ca96255fad9ace776168806ecc42", - "0x8d24c465d26bd93290f45ef035bb6dde4530d9d7d051baf583b1f8b98e9886de262c88b5709084710cffa7c767b4c27d", - "0x8cf35b1b28a7726645971805170392d522f5e7e6cb94157fe9c122a987051c1c90abe3c5bdb957ef97b1c45dd9bba05c", - "0x93e2bbd82a3cb872cea663f9248b21d4541d981f3f8d5af80a43920db5194857f69e2884753f6ed03b6d748dbfb33620", - "0x8b774b97657db654ebdafce3654d645f849203452e876e49dad7af562491cb6531bd056f51cb5b2e8f0a99e69bd8566b", - "0xb5333c49d3e1c4c52f70f3a52f0ad77165bed6ad9dcbfaf1364e7a8a0f24570e85a218e4c2193f63d58a7dd975ceb7a5", - "0xb4a34c443e4fdaab8e69fcda1fce5e72eaa50cf968f5d3d19084d049c5e005d63ab6e1d63dee038317da36f50ffb6b74", - "0x824a224009c6848b92d6e1c96e77cb913fee098aaac810e2c39a0e64d5adb058e626d6a99be58593d921198edd48b19c", - "0xa86f1fdd2e1ba11ebda82411b75536fc0c7d2cdb99424e0896d7db6cae0743ee9349ffa5bff8a8995e011337fa735a9d", - "0xb406b5b89b8bed7221628b0b24eb23b91f548e9079a3abd18be2ed49baf38536a2c1ec61ab1ddc17928f14b006623e7b", - "0x8a7ea88d1f7420e2aaf06ee90efa4af798e2ec7cd297aacd44141471ed500107fdd93bd43b6de540314ef576646a7535", - "0xa7a8c071e68bbae9aca110394cf56daad89404dff3e91ea3440670cd3d0423b67905e32b1ba7218fd4f24d2f8bd86ce7", - "0xb959830f152e4d31c357be1ded5782aed5d6970e823cf8809434cf4fddd364963bc7cfda15c8f6b53eda16ab20ca3451", - "0xb59232c8396c418238807ce07e0d248ad2045289e032678b811cc52730f99b480eb76f6adf985e6d5e38331d4bb2b9d5", - "0xa14092fddecc1df18847ab659f6cf7c8603769a4e96fbe386d8303b225cebbbe8f61d6ab3dca08e3ed027e7e39f2641f", - "0x941cb0632acd395439f615c6b4b7da9ed5abf39700a8f6e6f3d3b87a58a1a7dbb2478a6c9ff1990637ada7f7d883f103", - "0x951b8805ecb46c68101078847737e579206f2029e24b071bae6013e9dde8efa22bce28aa72c71708caf4e37f9789a803", - "0xb2cbf22e53f6535fa950dd8de4aa6a85e72784dd1b800c7f31ec5030709d93595768748785ff2dd196fbedf3b53cd9d7", - "0x8d84ea3a7eafb014b6bd6d57b02cab5ac3533aa7be4b86d2c5d53ce2d281304409071100d508ed276f09df81db9080ea", - "0xa2204b60836cba8bf29acd33709e6424226ae4d789ef6b280df8a62e30d940bc9f958ff44b5590d12fa99fcde2a4a7a9", - "0x86692c58214f326c70eb2aaf2d8b26eae66fb624f143a3c144fd00f0249e30e0c832733a7822fac05c8fe74293768ace", - "0xb1cb3d64eb5b9ca0e01211128f990506fba602cd1417da02237205aa42879ae2a6457386da5f06434bcb757f745f701d", - "0xb3eb4290a53d5ff9b4596e4854516f05283f2c9f616ec928a0934b81c61afc351835f7eca66704a18a8b6695571adb30", - "0xb0bfb1d44b039d067d7e0e2621e7c4444a648bce4231a6245179a58cd99758ec8c9e3f261d0adb22f9f1551fceb13e4a", - "0xa29320f71a9e23115672ea2b611764fe60df0374e0d3ff83237d78032e69c591a4bdec514e8b34f4b3aeb98181153081", - "0x8a6abe9c8a048002b2ff34154a02c2f13fc6dbae928da47c77f3e5b553ea93d8f763821a6ead3c6069677870fdff7ff3", - "0xb73ab66a62f427e1a5e315239a2e823e2a43550d245cff243c2799eb2e4701fabb7d5f9ce74a601b5ee65f6555dacf64", - "0xb64858e98b9c10de8c9264b841b87e7396ba1da52f0f25029339ca1d13f7f9d97f4de008cfe12a1e27b0a6b0f2c9e1ab", - "0x807d2440d1f79a03f7163f5669021f3518094881f190cb02922eb4e9b17312da5e729316fe7ba9bfffc21ed247b033cb", - "0xa7f06458d47ebe932c2af053823433a8a06061c48f44314fad8c34846261c8c3f7f63d585a7930937327ad7d7ca31a6f", - "0x82ac2215eba9352b37eb8980f03374f5e0a2f439c0508daa7a32cdce398dde2a600e65a36795a4f5cc95bbcf49b01936", - "0xa1882c83a2f946d54d74a008eac4aed70664db969e6799b142e0d0465e5662ba0d224a1cc33be339438d69bdad446ff6", - "0x8009776f7a34a3c8779e21511fa409b0c5a38e172d1331acc29a16114e002f5f2f001381adb5fb3427a100752d775114", - "0xb24441019af4a0df2dc68e3a736f358da0fd930c288398a18bb5a8d9a1e98ea376395f19d8e03a5f020b83fcb709f1af", - "0xac72b4de3920c4f3c9b8ea90035cd7ed74d34b79e79aab392f057c3e992ebe79050cc1c6ccf87120e4162b29419147de", - "0x973e75577cd2a131a0bd568fd44e43554ac5a9ea3bf10f02d1ad3ac6ce9dc7a8a7ea93aacf3325f7d252d094a0de1376", - "0x98a114de2a86f62c86862de37c328bf6a7fccff4d45a124addbe0eb64debe365409fcb72ce763f2a75030e1ff4060c64", - "0xaff753e1dd4707f1a359eaec06ebef1903242889a2cb705d59dd78a79eb5b894731f5a91547479506145ca5768877dec", - "0xb856e4234858b5aa515de843e8bd4141c15a4cc02c51640e98a8aaa1e40344f1ff8ef7c3b913ea2ae7411713daa558d2", - "0x863525eb2f8147a6d1d0d4304881795bfed348913cd7f38d815d929a426788b69e41f022dba5fdcaf56c85720e37fefe", - "0xa14ad76b145a6de2e0f8d4f615288c1512701a7b3010eb8a95941a2171bc23561e9c643764a08c4599040a3b4f5e936a", - "0xa18bfc66f6139dcb0485a193104fec2e7d52043837a4c0cadb95743e229712a05cf9ce4ccb482f36ff1ce021e04b574a", - "0x991c8e6678077d6e5f5733267c1819d8f7594e3b2c468b86a5c6346495a50701b1b05967e9590c15cef2f72bc10a38f9", - "0xa034e7f9b547b047c99b99a0dd45509b0ac520d09130519174611de5bcdb9998259e1543470b74dcd112d0305c058bad", - "0x95ffe0d02317b5c6d5bfddbcec7f3fdfb257b26ad1783bb5634d983012e2ea1c6b9778009e1b6d10564198562f849ac0", - "0xb3db442aa4adb33577583b2a4ad743f41efe0e1f87bfc66091d1d975333ffc00b4afc43057bcb88a7d68b0c9695d38dd", - "0xad2e97d10d7c53d231619e3f2e8155a27ea4f2fb3c0cecf5c7f14f4cfcdd21f62ea46d843b21df748b2892131633fed2", - "0x905d7aad6d3b56bad48694b6b20b27e370ebca8b91d0821e48e2f9cad39910c26cc11c77c266894db3d470485a63ed11", - "0x99bfadefca796ce6af04ede65ba5ef5bf683ff7e2852bb9c406fda77b95ef382289853dfe4d933525071e4cab8ce3936", - "0x94d9905ed4ef92107d0adb9ea38f085a2a24b8f792108bec702d747c215b1f14aafd486ea0c07ed42602b12d8f602b93", - "0xa78dce23ca09dda2d5e7fe923290062546825286d624de35ac5756b6c8ae030e211f4f9c9c8d18a924f5880e3b383d1f", - "0xabce9e2128ff51fa17e73d93e63d7134859b2f328eedbcefb337c39e752d6750d9cffe6abfcd359c135dc5a12018827b", - "0xa9ea7d91e8a3524acb3182bedd7e1614d37b48f8eb2d8f677eb682d38408b8d512786d8bb65811f4d96788b9378e59b3", - "0x912c9f804fb57dd1928f8274be58b42618f589fc72a7e5b6cb4d4b5d78c547f80737cdd77ebe5d2b71eaf60b8fd2b663", - "0xb7227ec9a62d5538974547f717fdd554ab522d8782667fc3e9962e9c79a21134ef168371bf3b67e28d0964e92cf44028", - "0x89440a781c812a19c758172bf722139598023ed0425374fbb0d91f33be7b7f62a36d7aa34696c4fb0da533bd5dd41532", - "0xb31e4a9792d6e9c625c95aa3c0cd3519410dec07940afab820ef9f63017415d237a47f957d0b591b6de399ffc2a8a893", - "0xa66ec47393df2693be161daaa88be0cf07b430c709ca97246d10a6080ae79db55c9e206b69a61f52512b868ba543e96b", - "0x90ca425dee74cc6a7e8eb1755cf9b7b76ba2a36ab851333b0fb7b35e8e6e189702456f2781ad87b4215993d62230ff4f", - "0x88b64741f93a2ae5d7b90b22a5e83c9d56bcee5c6bfcedb86f212acc776cc3ebd0b62cc025f596cd8db4f4b6a7aeebab", - "0xa1b6c7d2358bb201b42264f8fbebaa242ef105450bab21b4a2f16f368048c16ad1f3695841787eb33a0192f1f6b595eb", - "0x8a932f1cd227ceb18389791ed9ea1ff26571715ed1ab56601a994795713a8f7f031d1e8472ec3eb665b7bfbbca8ca623", - "0x8bb2e34a2bf77f9f657dfc51ff296a6279a4d7d15860924f72b184fb7d5680320c7769954b9dac73c4bfe9c698e65e58", - "0xaf54e7367891c09f2cea44cc7d908d37d058162ec40059d32ded3983a4cabfe5057953878cf23bfad5292dbd0e03c0e1", - "0x8a202532b9205385cf79f0299ddcb3156fd9fab09f9197bce762b5623f75c72ab1d74334ee6f0d289007befe222bf588", - "0x83bd0f5896eaad58cfa7c88fc5ed505cd223f815dcfe93881b7b696cdd08b8b5ede03ea5b98e195c1a99c74ac5394c1b", - "0xb4a84d9940e58e3b4f804e4dd506f8c242579cfa19323c6e59047e5a1e35150699a2fab2f4862dba2f0ee4ed1d8970f8", - "0x8c9ec477d057abebc2e2f6df5c4356a4f565bde09f499a131967d803d4bf36940ca2ed9d4a72adbe0a4a8b83fc686176", - "0x8598f43c32623fd5b563d1ec8048ffc36db3d7f9b3a784299811687976f64b60585b2a2707050a3c36523b75d1e26716", - "0xb55eb07014fe5ad3e5c9359259733945799e7429435d9bf5c72b2e0418776e329379433e17206f9f0a892d702a342917", - "0xa5ed942eda7b36a3b0f516fafd43d9133986e4c623b14c0f6405db04e29c2d0f22f1c588150f670dbb501edda6e6dd4b", - "0x92b6abb28cefab2e332c41c98bfa53d065b7d262638389603a43f4431e6caf837b986254c71f7cdacf4d6cc4064b0195", - "0xb01806178a28cc00d1561db03721eef6f6539676d93dd1fa76a13b42a31d38797e99b1848de92fd11821a342b04f3f72", - "0xa2f10303437acfbb5912e186bbff1c15b27ed194c02cbc1c5b482b0b732c41fa809136e8e314e26b5bfe57690fe3b250", - "0x9990207fcc711102e7e941b3ac105547a3e7301390e84f03086c99c6d3e14efff3a2e2b06e26227f496d88d5cdaa3af1", - "0xb903cdb0c2fd578612398c30fe76d435cd1c2bab755478761244abb1e18ba8506fd9c95b326422affbcaf237309959d7", - "0x99e0c12cae23f244f551d649302aac29bfdeb2c7b95578c591f512ad7ac562bd47e7c7317ac9bac52c9ea246617bdb48", - "0xb996d267ab5149c1c06168ee41e403be83f99c385be118928d6e2c042a782de0659d4d837f0c58b26df0ce22049a5836", - "0x989001b8414743765282f7e9517e4b8983a929341b8971d7dd8a87d246f6c8ba5e550c983566ddd932c22948f4fa5402", - "0xa0b006a2c9124375364b8fc5ddb543a7468fa6d321ea046d0fd2bfdaef79e5e3600b3d56190733491ca499add1298c7f", - "0x80881d6f3ee507089b7dfb847fc53dd443d4384ef6fce878d07d9b4a1171eefea98242580e8a6a69664699f31e675cfb", - "0xadc48ef53d88b9d70409ed89cc3be592c4bd5eb65d9b1b28f2167dc4b12406889c00f2465c554f3aff673debc2997ccf", - "0xa62f5d9f167b9f4a4aab40d9cd8c8a48c519f64a1985823e20e233191b037c02e511b0280487112a9f8b1f1503b02db7", - "0xb89aa2d4fb345a1d21133b0bd87f2326eb3285bd4da78b62174bf43d30a36340e4217dbe233afb925ab59e74c90fccf0", - "0x932ba22acdd2f9d9494da90958bf39d8793af22417647d2082d2c3e6a5e17a2d14b0c096139fa8fa3f03967ca2f84963", - "0xb67b107e71d96de1488b4154da83919d990502601c719e89feabe779049ddf7e4fb7e146eb05e754b70bbead4449efb1", - "0x84509de1b8dc35aa2966d8a48501f725d59b4c65f3abf314b2009b9a573365ae3163c1f276708c66af17de180aae0868", - "0x849153fe837a33fcb32c5fa6722c2db9753e984867c112a364eb880d87467782142d1c53a74b41df1dec7e900c877e1f", - "0x903d05c73ae043b69b18e980a058ce2254d008647a8d951175b9c47984164b34fc857108dcc29ad9df0806d7e90405f4", - "0xa6b05917ac32c0b0eeea18f1ef3af5343778c543592078fdf6a1b47165013e2676bfe6a592a24efab9d49c4bd92b8fc0", - "0x8648482f6947a5a8d892a39f098160aae1a648cb93e7724ea9e91b0d1a4f4150b91481f6e67d3bf29ff9d65ba4fa61a8", - "0xa6ecaabc38895013297ae020686f04ea739c4512d2e3d6f2d9caf3f54000fb031f202e804ee615eb3357714a18657bcf", - "0x912f5935acc2dd20d5ef42b2ad5b307c925324a84a3c78ff66bc5885751934bd92f244e9636b60a744d750a2a7621198", - "0xa0d6f261a776c5b114298f5de08d6e3372649b562051ea2470d3edfc376048793e18fc57ec84809b463dc72496d94329", - "0x940744cd3118d1598c248b38503f6f1fbdbe7a147e683e5b3635140aa91679f8d6c1472600f8e9c36117a60203be6b4e", - "0xab81737c839fe340f6f1fb7275811cb0c0d5fe8bbc265f6a56c6c68d0291bc7234eaa581ff26f8929d9a5bed4aac7002", - "0x8df47341160f1c728c3e31be17a32e42b54faaa1286ef2c7946882ca4dd46443b8428f3654616c6e4053f1cda2e11994", - "0xa721067e75c3c791f4d9f58d4810ac9621606e29c6badb593d6bb78c39968b45be1777ddb9bf03696d4d4be95b2dc1bf", - "0xa4e399213d3c4350c2d0cbe30757ba7e1f9680f58e214ff65433b36232323744c866a87d717851ba1dbd6769599f69a6", - "0xb0be851d1e43dee27abe68f85e2330d94521b5f1c1a356ad83fcd09162c0ca9c2e88bccbcc5bacfa59661764361867a3", - "0x86111bdd3dbfca232aa5802a6db41d639502e43a2e24cb06bb5d05c7f9b5ccac334d16b61d1c5eaac4fa0cab91113b46", - "0xa4f805b11c174c34250748b9beebfb7c8c243198fb13463911906ee4effe7d331258a077e374b639a0c5cdcdff166b7f", - "0x87e4cf2c6f46d2dbac726a121127502921decf0195d7165e7bbeec6f976adb2d1c375eaa57f419895a2c70193215dc4c", - "0x8ff06de2c1c4d0744483bb4f7c5c80bf9c97b4df23e86c0bb17f1498ea70e0ee3af20827da5e8cb9d7f279dc50d7bd85", - "0xab112c0116471b4dc3fd1e6d918f99158eb7a08153e891ddbba2fe5bf0eeb188209e3019176e758231c3df937438136c", - "0xa67f89194e99e028a5da57747268e5ef66fefb881144043429920d222d37aaf268ebf73ca1da659fcdac3b4e7a65092a", - "0xb4da1dcc791566140d6abeaa2923cb6b21a6e6aaa30bb4cc70011e931eefa71f96b7e05358c0654bad7ce45191ab9fa8", - "0x8283933231bca359db588c80e043ad6ea765fb0cba5ef233c5d514ba01ddd1b409efbadb368f26763402e4576dc4655f", - "0x97f568ce3edacd06f3e31a15462f5f9818a8c3fdbcf92b1ac5840b0b6e73166a154013dd52e85a18e8ead3fc9e54aca0", - "0xa9cd1601c41e5ab2018f986443914fb703ddb6b06a36c06fb58065f2fee8e1751071ef924ea3ad76f0c19baccb1b5f8b", - "0x92aad71bb7e929cc35a48020d16a5822f4f106a7f59985005a5ae5ba8e8016ec33727610393498f56b4f353b3d5161b8", - "0x89427780aa4e7ac894c681fbe2889153b94db883f17f109bc9caa93f0c259dda42aab502bbefaf572c56f70abbc42db8", - "0xaa8cf76ff847dfe59534432ed8520bb48bf412c28497747dce04d2b2a54ba843c3be1564630cb49ec0217167847ba590", - "0xa1570a6748a2303e74a31c2131d05ab372ec006ee92ef74c42f2e9a250663bebdfb3777e7ad91f50c954889a59c2d434", - "0xa4c2b1bbc48199c31ea8d8196729eab00ce0200350d4aa9f23347a3289355e5828cb2f93036a14d2d9ec575fb3835239", - "0x84819d0bedbaab5bf8afdf23f59a7ec5f50da3063cfdd1ef5fc4ca4c1fe68980b5c80e30a49f38e5816765e81dfc5a57", - "0xa57cfb5e877b88202f589be777605deafbfc85ed1357af03a18709cfb4b668a271199899243cd3750f1cb77ebc40bba7", - "0x8d95934bbb0efaf3339f27cb96de46e4486aa58a2c40dbc77c1c3ac7c27a228062824b9045c046631b2e286e8549603a", - "0xb99a8356abeee69f40cb3bd8c87e8039a1e076897dde430bfbf989dc495c48609a7122bc6c1d1c32ccac687b47d5558a", - "0xaac2edcf2fe5d3f1a84e8f1f27ece920eabe7793bf0ed5290cda380752e55d57a55a362c5253bebb71e4a55f2c437ff6", - "0xaf7c76876072c3b0091e22b9c5b27ce99bf1f0079ea1a7816ad9c06e9e5fc407595c7f4f9953e67d86fb2da656443dc3", - "0x9175b64d104f78d3310c9c02f82e04c8e9878d2044ea5ee9c799846a3d23afa5fa2aa4af7350956136c69a0eed03cb2e", - "0xb3328e953317494a3d976e7f7c3d264258a5d4b2c88e12d06786a9e7b2affd41086762ef6124c6a6e5b6b028db933c14", - "0xa49d166065e19d39299ee870229e4a04be81acd6af3a2201f3a291a025dd5f8bc3e676ee123cd4b9d8455f6a330b395b", - "0x85fa15bc8947ba03681d87b50bd2f8238b1c07849a7ed4e065053fad46aac9dd428186a6dd69dc61b5eba6ffec470831", - "0xb6fcb2f694a47d3879b374b8b2967dcd59bd82a5d67ae6289a7326c18791b1b374e12571e8c8ea16a4bfc5525ced3ec4", - "0xb6115f52566aa90ccac2aab6d2dbf46eca296d047db1eb29a1b8a2bc2eef7a24e90407f8dae528806aceb2a1e684d49e", - "0x9707e66220233f6a48a93e8dec7b253d19075eaa79238e519b82ce1ac5562cca184f8a1c14f708a96c34ad234673d646", - "0xa0822903fb3825eae07ee9d3482277c0b8fc811856dfe4a51cf24b373f603924166fc5485185f99c4547cd6476b62270", - "0x88dac6366c439daaeee2532b2ddbe206132cf6e12befbb8e99870ac684e04e62de150cba0e22e395a0b858948f40808b", - "0xa72dfba9caad3179f43fead0f75e33ba5342470d8c9cb7c86d30d2c7ce7244a8aafd1d558b0ec8e2a9436de2c2e95ccc", - "0x8d696046defcc32cc19954c559213100f0ba273ea12abb55ca7c42818071d853846bd4213af2c41ecd4442f6b4b511b1", - "0x89d6f2d52cf65414da15a2fb1911c53afbfb50bb5f2638844abfc325ff2651cd9130be4beff05dc4046adfc44394a182", - "0xafb91abd7c2a9cfe62855ede3c6960ad037fe8778364a2746ff7c214c55f84e19a474a9a0062b52a380d3170456ee9c6", - "0x87f724a16ec8fdae8c05788fa3f823ecc3613df46581a63fc79b58f7c0dc2519b6b23e3dd441a0ca6946dfe4bc6cd0ce", - "0x86760f90f6bedfba404b234e90fbf981d26c29b87f2fa272c09540afa0f22e6682d08c21627b8a153c0feb27150458e2", - "0xad4d0342f255a232252450ce4209507ba619abfd1ffcb9c5707cfa45f89be41d88f1837acea993a1c47211b110250b4d", - "0xace54b5889bccdf1d46c4ca21ed97cca57f7d12648381411d1b64afdfc64532a12d49655776ea24cf5eabe34145705ad", - "0x936dac693d0c1b1e5de1701f0bc46aef6e439e84bc368a23c0abe942eb539a2950e8929265786fcdb18d40a44bda14b9", - "0x94fafbc544decec1d489b9ad6b23410b9de4779f9f44aabd093d7fab08340a4646a8cba31633e49c04d2690b8369a1d7", - "0x98157e757f1a677c5d9d65c47759727a4dbc49fec2da4d9889c4ea90573fb42e2a8d72eaef92b782ac6f320970f09363", - "0x8eaa0498c191c810c7e1ca7398f7c80dd0a7e7d7829ed07039490f60e7c2ae108843c06fe38fa36d45d63da46cba887c", - "0xa0ae116e5b0d2dccf83f056ad876037225687904e0290fe513fdc6b2dbe4cbf5fac1d828352e64734895895840b3c57c", - "0xb592b318dbbd7ec4872aae5e64bdf2305db2e5e8cfe0ad77b691f542ba5e066dd20b09b0b08ff0d798bd79ad946ddf7f", - "0x879e50c8c3e7f414ad2b38632bc482b71759cd561aeb2215550186ebb4559e4cf744cdf980512d8321954b3458d21e11", - "0xaed5c6c7ce0407d7b2c04785fcb9deadb9b9413e37cef5b1d918f474cccc7de012fe1fa6f5fa93cb7ef9ac974d9fbc20", - "0x892274a9f0afc68fa74be276c2a16de5cec674193f96b27a80bbb9f3add163f85716b531f3c920b98577a0225f84e8ca", - "0x938fb7a53266b997a7669596577af82f5289b160b7fcf06d76eee2a094696f6f12b28c2c65b833a52529a116c42e6c7e", - "0x892083929b6067f5045b1208f3dc8f0ee25bd0533a8831f5c23bb4ff46a82d48f0a34523359df5061d84a86b718d5060", - "0x99159ae9574df6c16273eda66b6d8b79a327940e335b28c75d647f4744a009f4b5f0f385e2017bd3e7fbf59e629cd215", - "0xa03e5757ef7738eba32d396923ff7ef82db2c15bb6adc8770fcb37260b7bda3be62473bc352a9a2ef7ec8ebe0d7688bc", - "0xae3c24a85c9b1fa55158b2acd56d2016f70dca45a23f3ef7e0c6b096f4a7c54c14020d61bec7c7f87be4a595bf254209", - "0xa920a6f9cc803fe31352fca39c13f8ac1e8d494fcf11b206092227c2af38469b1fbc068b8fe014800b70f137107aafc4", - "0xb893853be57519ffa6410da605e7d3a746ebadec4788c7907f6e0dde9f20f5a6a01181148b874b3decf9b4814846a11a", - "0xb46f43918c5195729f6532439f815d1eb519e91005bc641a4a30ae88700982bf4ed07a342e77945780317c297c903755", - "0x8e431bf4497d0ef6538c93c4bdda520179301a0104eebcfd104efa1edea876818d7d31079656f01a5ff76c4f5fcd71df", - "0x92e3dbcb580dfb9cc998f878052b0c3be1c5119e5249ae9bad3538ebb0f0c4ab5a959b04033b96d61836ef07784e6b64", - "0xb712d9d63aa888156f4ec83e939c6bad53de18045f115f54fbf4261fb02f10a8a46a8d716ab43d4acbad3b02283c32fc", - "0xb2334e776988b4f772446a47c87416b4f19f9b44164a5f828424d3f35ef10baa56afe810d49b0b86b786b9c0227681a6", - "0xa3f25ad18e435ef585fa90e6cef65a8ba327e5e33701979e27e64ef7d8e09e2591e52bff9c5749d35643456d18625685", - "0xadcfa48ae43cac6fa9866b4cce10a243969965942c891d5e6c0e5b03bd4763f9b63779fbf40d26ac674534fe7cc478d7", - "0xa0eb3448e045038740e2ee666e88aa0f8b8e24b1b55d7d4964f01bfc0c581f7e9d4c0e79f8cfbfecfa8b024b216c8ea6", - "0x8110aa1d82f11965af4f4eedb4de09ee9c353481b2d7ee7a2bc2f302d2a5ae6c31ebc6451309ba7c305da41070b0f666", - "0xb074fdad419d42783ebda17f19863aa499eec71fda5aab6cdcc389276b7bf08053795d15890175ca3dc89f6d8d17758c", - "0xa14665846d95d7d5f0b5381502080c822776ec0994ccb1ae1ffbb3f19205ce9c7c9bf9c2d2ca098807ce99f29e4f07a0", - "0xb4884842670a333cb5548a842fa2971881e26b442dfab0b91d6bf3b4cbdf99adbbc9d14fe2bb46872cfcabedae85db30", - "0x94549b01cb47ba16c0cf6f7522c833545397de0b3388c25d03e60132eddada6401682f9ffd8c50d1a61b4d2dde37461f", - "0xa790c9b4cec96e4c54777f3e03cea5769b20382cdcaf1de494bac2b9425eaf453eff643c62ab284cc1af33bbd36013be", - "0xb1b45fd298ed11609aa1ae6c5ac655e365bb451de1b9fc92aad40422ba85c6a454f33b8142acabe55171328c13d92edf", - "0xa74cea9e7096e38327064f058a3cdaa34e6eafaa9c7d58f753c40be67998152380fbd612b9dc0751bda7befcdffcc749", - "0xb18978dfc5efb07b7ef992c7b0cf5d1b4ca551578b1dd13057b7aced8b1deb9f2036e1e3116248a803e922659d206545", - "0x8153c07603cdff6622835a9853b795274390abf7197d7a192193bec44acb43e8cd50b56c11a03f4a2a27124c36974f3d", - "0x86b987f30bb9a37cc91d22dffffcd346ec5773e846a6c2b8f9e03b25ffcae859c470c901c4e29695d325dfe4eee927bd", - "0xaf5e980b9507d10d5269c1a5d02bc16f4f009b663e413ea6a7c655250f3a21c608c12f4002269a05d3779907e7be7d69", - "0xa6f737fab2af9f27bfb8ca87f5fdab6ad51e73ccf074e90576db57b309dfa0a95f9624526dfa4feaef39c388802f2ae9", - "0xb7ed51f699f615f58a7ff4f99d52c4ce7a8d662843c1f4d91f1620fa119b80a0f6848f9fb6c4b9822dc019830e7dfd11", - "0xb71f27f291aa6ef0723ed79c13a1c7a1c40198ffb780a129d9d20e250406bc91f459705b2b6674c9bb412a7b5dd9ff07", - "0x9698cf8f638c3d2916fefa5f28c6050784479f84c2ee76a8aeda7e562630a6ae135b445ec4e29af8588ca5ad94a67f49", - "0x9270aa5030966a9990d8bc71b00b9a7a1d7c1ad8f4c7f78a31b3d7f86467332f21407c74a89ba4f574d723acaf0d2042", - "0xb1b82faceed8e2297cd49cc355471d15ff8dc2ccc78f6944c8f7a75d3ad1629a2e2f1d0a2ff7fa2b3c38cd19839aa5e9", - "0x8a8c4ed49dc9bd961773edf8d41d04385b11bbd3577024639a39319cc7068380236bf73fce0b83e6535bd3f95cef0e65", - "0x8d04ec1e7d148b7e66910ab45a0e6bf409612a3b560bfa784e26f2963152821c646a655cf17a0ce3d4ba4c4ebeeb4a1e", - "0x8e9d707f6186d93accb60813715ed1f6b3001ff6d2f87daf8b906bd0b988c1833b2ccd80dee9bdefb45901e81bb82971", - "0x9762317ca6a5e6fe0b2991e0fa54b5fbf419dd0550d70074957d65cd7ebf79ceba607dd40d709ed635c822b3b4da2cac", - "0x82b53cd9a1eca2f5d3256723dc4b6531ca422bd87bab36243c727d1952db58d7288ab11467305d875d172ce165b1e4a5", - "0xb4dbeafa05c87029ae257bee1ed7603645fab41f6ba7ac8b57ced5b4774a72ba3e671c2433a93acc3c498795b5cccc42", - "0xa916d3ab7f0e7cef294e11c97c910a19c338ad8e615406e6d1c8995b4a19c3b2527100cc6b97a950ec5a4f3f6db7d01a", - "0xb9a785c7123609bdc96f8dd74500c6c77831d9d246f73244de964910b4045ce3242c881271bb1a4bc207d67de7b62e97", - "0xb5f94084f695d0821c472e59c0b761e625b537c8ae3a09f11d9a57259e148cfadba1e43bf22c681b6b32390121cec208", - "0x8f91b36d8570f19a90cf3ed6d5bb25f49a3315ddb566280c091fe2795c4e25ed2c6a1ef8d2669b83f2d7bb78fc8c40f5", - "0x80f27359a73ed8fdd52762f0c7b9f676be2398b1f33c67877261480bf375f975f626c2ca3e7a9f59634db176ed672c98", - "0xb96b91e3d5148ca793edefe4ca776b949c9305acb6f3a3cf87767a684014d2c8f2937c2c672eef8510f17d2da5d51385", - "0x99c4e1ca2cabd4388ea2437dbdf809013d19be9bd09ff6088c8c0cfdb9ecf8fd514391a07b4288dd362434638b8834d9", - "0xb6fdfb812e145f74853892c14f77c29b0c877d8b00055fd084b81360425b3660cd42236ecc853eadb25253e1cd8445c4", - "0xa714af044ef500104576898b9409a9a326ef4286a45c3dae440bd9003fdf689c5f498f24a6f6d18502ce705c60a1cf14", - "0xa9444e201be4a4d8c72119b3d3b13098afee6e5d13c5448fa2e9845cc9188239778f29b208749c960571dfa02b484f05", - "0x91c826a6b8425f93ff395d9fdfa60dbfa655534c36c40a295906578540b9a0e6b94fd8d025b8b8611433022fbbc4fb0b", - "0xa355d76bc3cc48ba07026197130f25a593ec730d2ef0d5d2642bfcad745ecbe5c391324bc2485944060ff3100c952557", - "0xb5f9b5a289a6f9a7252cc1f381c892bdb6836a5998f323ee21ae387936148ad1ad7cc6eca37ecece36404b958ae01e8e", - "0xa3c7ae04a6208851f6cc40ff270047283b95218905396c5dedc490e405061cbefd1251ecf77837d08c5ec1c77d2776ce", - "0xaa02ee387dd2cc7a23cf5cd582da0bc84bb33a7158d76545cbd6e06b26a6f30565dc712d7a8594c29f0529a892138802", - "0x8aff025c841f167fadaf77a68284c355ace41d6df3a9f1e41a6e91454b336f0b69ea34cce495839b642a7c43997a8fd9", - "0x82eccf0b6b4b6460f676d677266451d50f775446df313fc89bdf4c96e082340f6811939d215a54ba0fe30c69b3e43e25", - "0xaf324d871b038ff45a04366817c31d2c1e810359776fb57ac44907c6157004e3705476574e676b405d48a48bfb596f59", - "0x9411dcca93ef5620ce375f379fea5c1017a2dd299e288e77b1ab126273631a299d7436f3bf3c860bf795e5faaaefa804", - "0x934fca809e66f582c690c3778ea49de2e7940c0aeb8d7edad68f2edccdfda853d2c4844abd366fbc2215348935e4b2e2", - "0xa1b1fa4c088418f2609d4dea0656b02a8ee664db25f40d53d8f4b1be89a55e5abecbf2c44c0499874abeb3d3a80acf71", - "0xae6ed7a0ba6280c679b0bf86111afad76fc5d930e9fb199df08134ba807f781d7e0b8b9b2c8c03b02d8cc20dbe949a28", - "0x937d200a72fe4ab8d52f6cb849e322bc5959632b85a93c89744b33e832e8dcf1dddd6ffac0c049b03c105afb8930f7f5", - "0xb4b4a46ebe0c5db16004933c08ad039d365db600a13d68be5346b1c840cce154f56c858874e866de8c3711e755c6e5dd", - "0xafcbcb7170c8caa2b77d2b3388dc2f640aeb9eff55798aeceb6eb6494438be05a2ae82f7034b2d439a45ad31d8c64b07", - "0xa2c676273081b8761f58e0b11306ddb6a4cde3d90e7c47b434468700c5b749932819b01efd7637ca820e10fc28dfb427", - "0xb445715162d834c9ee75ac2ff8932ace91c8242d67926b2a650217e4765e0531c2393c9438a52852d63dbbe2cceaafc5", - "0xa0c0ebdc1480fb238a25fbfc77fae0db6e5e74b91809f0ff20a819e56b8c3141549615d1bd7b99829898f6028e8c86be", - "0xb3d11933e9d1db8ca617934261ed26c6f5ca06ba16369e7541482bf99c4f86520d43fbb10f4effb2fdf3cc70a189fdb5", - "0x888ac610f8fd87a36b5646e1016eaf6dbca04aa0cc43f53a1046d74a658c4d2794606e79fb07fae57cf9d71ed339f4b6", - "0x979818dab00c58435dc0d0d21185943f95819d2a13531abd2d798e1773c4bbd90047f4eebe117868743db75604a50227", - "0xa6fbcd2656e475065fe44e995e8e2b5309b286b787a7597117e7acc3bb159e591a3e7304ef26f567b5720799d8ae1836", - "0xa03f0ac08d2101ec4d99ca1443eea0efa767a65448a8ecd73a7818a99e863a04392bec8c5b8e5192834e8f98d4683f13", - "0xb3c4ea8c6c3ee8aab2873d446ad702000b0e927e0991c9e30d83c6fe62a604efdc3ac92453313ff0d5e0ac6952922366", - "0xab25c857f26830631113d50145e961441b5e35d47b9e57f92466654dffebde43e4f78b0867d20929f97c2888c2f06509", - "0x98950aa5a70ef41f274775f021a284d4d801a2efe2dea38460db8a3a8c08c243836d176e69127c2cd17497b0ca393e9e", - "0xa9698113febfb6d87fcb84bad82ce52d85a279d3a2933bdd179d53cfe8d6c6c68770e549a1e2947e7528a0e82c95d582", - "0x832b504513266259db78478bd1b5a3b0f3bf2c6d25f1013e64bf0cfae9dc23da8ecd25f7f1047d2efb90e5f1d9b4b3cc", - "0xb588bba7bcc0d268ab260d5c1db2122cee7fd01583c7cc27a8ae6b48b29f34c6ea8a6acbb71b9b09c6156ec0a0766142", - "0xa73d2223c7afadc381951a2e9e7bcb7b5c232369f27108c9f3c2ced2dc173e0f49531d0ca527eb142fbb70285307433f", - "0x9152cd6b97bd3278465348dde2095892f46342aed0e3d48675848c05b9aee6ef5ad7fe26e0dcd4ab176532289d40eedd", - "0xa7812a95a43b020721f688dd726356dda8ebe4de79b4f0fdef78615795e29681bff7c6ff710ff5b2d6ae3fd81bdb8507", - "0x83724c16049e9eaae3269ea8e65caa212f0592e0190b47159bb3346208ccb9af3cfe8f6c3176fa566377da1046044ab8", - "0x877634ec37c7dcd3b83705b103c31013697012795f11e8abf88d54bc84f2c060f665f0c3b14ef8087d3c6a8a7982d64f", - "0xb3e53aaacef7a20327bdbba8cd84513534d2e12fd5e1dcf2849f43146e098143b539ebd555623d0ecc46f5ebb4051fca", - "0x952d58ecafca9b7ffc25768ee4f05ce138f0289d72978eb5e5d3b23a0daedcb17478890afdce42e30d924d680e13c561", - "0xa10dcc725f9a261de53dd3133858c126f6aa684cf26d92bce63a70e0ff5fff9610ad00d2b87e598b0a7548cfd1ffe713", - "0xb7bc5d0c6b665d5e6f4d0af1c539d8a636550a327e50a0915c898ac494c42b3100e5fae0074c282d1c5073bf4a5456fb", - "0x8adc330d3b49ddf3ed210166afc944491aaedb28cb4e67472aeb496f66ce59184c842aa583bfb1a26d67d03b85065134", - "0xb2df992a1310936394a1ebca94a7885b4c0a785638f92a7b567cfb4e68504ac5966a9e2b14891d0aa67d035a99e6583a", - "0x96f5da525d140739d19cebb706e2e1e0211edea1f518e040d361d5aca4c80f15be797f58cb4cd3908e4c360c18821243", - "0xb2c0d9173a3d4867c8842e9b58feb1fb47f139f25d1e2332d6b70a85a58811ef99324bf8e52e144e839a4fe2d484e37b", - "0xad95a7631ddb4846d9343d16533493524dfd22e8cbfc280a202343fccee86ab14446f6e7dad9bad9b4185c43fd5f862e", - "0x97f38ab82a51a7a792d459a90e7ea71c5a2f02d58e7d542eb3776d82413932737d9431bd6b74ec2a6a8b980d22d55887", - "0xad4e4c57ec3def5350c37659e8c15bd76d4c13d6de5453493123198dda2c2f40df349f20190e84d740a6b05e0b8f3deb", - "0xa691bc10810d11172a6662e46b6bbc48c351df32f325b319553377f525af44a50aaa02790c915b3a49824aa43f17fff0", - "0xa80ccac79bb4014ee366dbf6e380beb61552bd30ef649d4ec39ab307e4139b7775e776fab30831517674ff3d673566f6", - "0xb11e010b855d80e171705ab9e94364c45998e69d9120e4ca4127049b7a620c2eec1377356e7b877874e767f7c44afef4", - "0x96bfab7777769a1e00ce16ada6667a0d21d709e71bd0371c03002427d138d9172640cdd5c529c710fea74bb9d19270c7", - "0xa5bffd2c30e29633b4ecf637c1e792c0378252e2a99b385a093675940b48de2f262c275332ed4765f4a02467f98e3ddd", - "0x8d11929d67a6bd8a835b80660a89496250c766e713bddb2cd7052d67b92c39a38ce49005d38b4877856c4bef30fb9af4", - "0x8e704597a0dba1dbd1ff8c9755ddac3f334eeeb513fd1c6b78366603ebc1778231deb8e18f2889421f0091e2c24d3668", - "0x904fbb3f78a49e391a0544cf1faa96ba9402cba818359582258d00aff5319e3c214156cff8c603fbc53a45ede22443e9", - "0xaf12ac61eaa9c636481a46fd91903c8a16e7647534fc6fd9baa58ae2998c38ffbd9f03182062311c8adfef0a338aa075", - "0x87f2e544b2993349ab305ab8c3bf050e7764f47d3f3031e26e084e907523d49e1d46c63d0c97b790394f25868e12b932", - "0xa279a7bef6de9d4e183e2bedaf8c553fadfc623a9af8785fe7577cadced02b86e3dab1e97b492d4680c060ea0126abeb", - "0x8ece08667ed826f0a239cea72e11359f7e85d891826292b61d4edbdc672f8342e32c66bec3e6498016b8194168ba0e0d", - "0x90a15162586e991b302427bc0307790a957b53ab0e83c8b2216f6e6302bc496cb256f0f054ff2cccdfe042763de00976", - "0x9966c0413b086a983f031a39080efde41a9fedcaf8e92897ce92e0c573b37981f5ea266b39dc4f4fb926a1bce5e95ad7", - "0x9515be2f65a57e6960d71bfb1917d33f3f6d8b06f8f31df30fc76622949770fea90ff20be525ae3294c56bc91efb7654", - "0x86e71c9b4059dc4fd1ce7e28883e4f579a51449cab5899e371118cdb6afe2758b1485961ca637c299896dea7c732151b", - "0x8695b4ff746d573f8d150f564e69fe51c0726c5d14aa1d72d944f4195e96165eca7eba8cac583fd19d26718b0ce3eb61", - "0x813eecf402151c99c1a55b4c931716e95810fc4e6d117dfc44abbf5ef8dcdf3f971d90d7fa5e5def393681b9584637e0", - "0xa9caf7219eed1db14b7b8f626f20294a3305ed1f6c22f6a26962772c2fa3e50b5234f6d9ba7fa5c3448824c2a15271b3", - "0xb2b2ee20de9b334f2d82cbe0d2e426ca1f35f76218737d0069af9b727a1bfc12d40cf8b88d4afcbeaadf317b7f7ad418", - "0xb853960749521a17ff45f16ac46813d249c4e26e3c08fd33d31ef1ed2b2e157c9cb18bd2454fb5c62690bdd090a48f60", - "0x88772297d2972471b3db71f3ddbf5945a90154768ca49fa6729a5e2299f1795445fb3d4d969d1620e87dca618fbc8a6c", - "0xa2bb783fd13aee993e3efd3a963ebc8a8eacfc8450042f018f2040353de88c71ac784b0898bdff27f606c60a3d5ef2c6", - "0x9210903ac619edca0cb8c288ed6dcc93c472f45182cd6614a8e2390801ddea41d48a4ac04a40e2f0adfd48f91aabe2ea", - "0xa621d00f83260c22db9fa28757ea81dabcc78b10eeaaf58b06b401db6cc7a7d9a6831a16f171ead4e8506d0c46a752ca", - "0xb25c525bf6761a18bbd156ac141df2595940c7b011ed849dbb8ac3a2cd2da6b63ba4755324d70dc14c959deb29fb9ad3", - "0xa35111d0db3e862e1b06249d289e0fc6b110877d254f2ae1604fb21292c227a8b6d87dd17a7b31166038d6860b1bd249", - "0x90bf057309867d95f27637bd10ef15ceb788f07d38aca7ad7920042293d7c4a1a13d4ca1d6db202864d86d20a93e16cf", - "0xa88510e110b268d15dcd163ba1e403e44b656771399ac3a049dcb672a1201e88bf60bdd1d303158888a3d30d616cc0bd", - "0xb33b7e1f765e9cbd5eeb925e69c39b0a9ea3348ab17f1dbb84b66f4a4b3233e28cbdeb0903d6cfe49ec4fc2f27378ff9", - "0xb777da64fa64d9bc3d2d81b088933fce0e5fcc29c15536159c82af3622a2604c2b968991edea7b6882c9e6f76b544203", - "0x8ea598e402a056fd8031fbf3b9e392347999adc1bd5b68c5797a791a787d006e96918c799467af9ac7f5f57eb30b4f94", - "0xb6901a389bf3b3045e679d015c714d24f8bbe6183349b7f6b42f43409a09f0d5bd4b794012257d735c5fdf6d1812554b", - "0xb5866426336d1805447e6efc3f3deb629b945b2781f618df9a2cc48c96020846e9108f9d8507a42ba58d7617cb796c31", - "0xa18ccc6ad1caa8462fa9bec79510689dd2a68d2e8b8e0ddbeb50be4d77728e1d6a18748a11e27edd8d3336c212689a4d", - "0xabbd48c48a271b6b7c95518a9352d01a84fb165f7963b87cdc95d5891119a219571a920f0d9ceedc8f9f0de4ab9deb65", - "0x94a4e5f4d7e49229e435530b12a1ff0e9259a44a4f183fb1fe5b7b59970436e19cf932625f83f7b75702fd2456c3b801", - "0xaf0a6f2a0d0af7fc72e8cb690f0c4b4b57b82e1034cca3d627e8ef85415adec8eb5df359932c570b1ee077c1d7a5a335", - "0x9728025e03114b9e37ed43e9dcba54a2d67f1c99c34c6139e03d4f9c57c9e28b6b27941d9fca4051d32f9b89bec6537b", - "0x941601742d1e1ec8426591733a4f1c13785b0a9b0a6b2275909301a6a3c6c1e2fb1ffa5fdcc08d7fb69f836ae641ced5", - "0xb84b90480defd22f309e294379d1ca324a76b8f0ba13b8496b75a6657494e97d48b0ea5cfdb8e8ac7f2065360e4b1048", - "0x95cc438ee8e370fc857fd36c3679c5660cf6a6c870f56ef8adf671e6bf4b25d1dbad78872cc3989fdfe39b29fc30486d", - "0x8aafba32e4a30cad79c5800c8709241b4041b0c13185ea1aa9bc510858709870b931d70b5d9a629f47579b161f1d8af7", - "0x865b0155d9013e80cba57f204c21910edbd4d15e53ae4fee79992cb854dc8b8a73f0a9be92f74893e30eb70f270511bc", - "0xb9a49ce58d40b429ac7192cdbf76da31300efc88c827b1e441dd5bdb2f1c180d57808c48992492a2dc5231008629159f", - "0x8d1438b10f6cd996494d4c7b5a0841617ec7cf237c9e0956eac04fda3f9ded5110ec99776b816e3c78abd24eb4a9c635", - "0xaf2dd18211bb8a3e77c0a49d5773da6e29e4e6fa6632a6eeb56c4be233f6afe81655d977932548de2be16567c54ffbd7", - "0x92b92443f44464f2b48002a966664a4267eae559fa24051983bcf09d81bed5bcc15cb6ff95139d991707697a5d0cc1ab", - "0xa1864a2bac0c0dd5b2fb1a79913dd675fe0a5ae08603a9f69d8ca33268239ac7f2fed4f6bf6182a4775683cb9ccd92a8", - "0x948e8f1cf5bd594c5372845b940db4cb2cb5694f62f687952c73eb77532993de2e2d7d974a2ced58730d12c8255c30a2", - "0xaa825c08284fa74a99fcfc473576e8a9788277f72f8c87f29be1dd41229c286c2753ff7444c753767bd8180226763dfc", - "0x8384d8d51415e1a4d6fe4324504e958c1b86374cc0513ddf5bcbffabb3edcf4b7d401421e5d1aa9da9010f07ef502677", - "0x8b8223a42585409041d8a6e3326342df02b2fe0bcc1758ff950288e8e4677e3dc17b0641286eaf759a68e005791c249c", - "0xa98a98cc2fb14e71928da7f8ce53ab1fb339851c9f1f4bceb5f1d896c46906bd027ef5950ca53b3c8850407439efedd4", - "0x866f44d2e35a4dbffe6cd539b6ef5901924061e37f9a0e7007696fb23526379c9b8d095b417effe1eecda698de744dcb", - "0x91774f44bf15edafdf43957fdf254682a97e493eb49d0779c745cb5dbe5d313bf30b372edd343f6d2220475084430a2e", - "0xab52fc3766c499a5f5c838210aada2c3bcc1a2ec1a82f5227d4243df60809ee7be10026642010869cfbf53b335834608", - "0xa0e613af98f92467339c1f3dc4450b7af396d30cefd35713388ccd600a3d7436620e433bf294285876a92f2e845b90d0", - "0x8a1b5ca60a9ae7adc6999c2143c07a855042013d93b733595d7a78b2dc94a9daa8787e2e41b89197a0043343dbd7610f", - "0xae7e4557bc47b1a9af81667583d30d0da0d4a9bb0c922450c04ec2a4ae796c3f6b0ede7596a7a3d4e8a64c1f9ee8ff36", - "0x8d4e7368b542f9f028309c296b4f84d4bde4837350cf71cfe2fa9d4a71bce7b860f48e556db5e72bc21cf994ffdf8e13", - "0xaf6ed1fbff52dd7d67d6a0edfa193aa0aab1536979d27dba36e348759d3649779f74b559194b56e9378b41e896c4886f", - "0xa069ba90a349ac462cac0b44d02c52a4adf06f40428aef5a2ddff713de31f991f2247fc63426193a3ea1b1e50aa69ded", - "0x8750f5f4baf49a5987470f5022921108abe0ead3829ddef00e61aedd71f11b1cdd4be8c958e169440b6a8f8140f4fbf9", - "0xa0c53cefc08a8d125abd6e9731bd351d3d05f078117ff9c47ae6b71c8b8d8257f0d830481f941f0c349fc469f01c9368", - "0x94eea18c5ed056900c8285b05ba47c940dff0a4593b627fdd8f952c7d0122b2c26200861ef3e5c9688511857535be823", - "0x8e1b7bd80d13460787e5060064c65fbcdac000c989886d43c7244ccb5f62dcc771defc6eb9e00bae91b47e23aeb9a21f", - "0xb4b23f9dd17d12e145e7c9d3c6c0b0665d1b180a7cfdf7f8d1ab40b501c4b103566570dca2d2f837431b4bf698984cad", - "0x847a47c6b225a8eb5325af43026fb9ef737eede996257e63601f80302092516013fde27b93b40ff8a631887e654f7a54", - "0x9582d7afb77429461bd8ebb5781e6390a4dde12a9e710e183581031ccfacd9067686cfaf47584efaafeb1936eae495cc", - "0x8e4fd5dbd9002720202151608f49ef260b2af647bd618eb48ebeceeb903b5d855aa3e3f233632587a88dc4d12a482df9", - "0x87b99fe6a9c1d8413a06a60d110d9e56bb06d9f0268dc12e4ab0f17dd6ca088a16ade8f4fb7f15d3322cbe7bfd319ae1", - "0xb562d23002ed00386db1187f519018edd963a72fca7d2b9fcaab9a2213ac862803101b879d1d8ac28d1ccae3b4868a05", - "0xb4cc8b2acacf2ce7219a17af5d42ce50530300029bc7e8e6e2a3c14ff02a5b33f0a7fecb0bb4a7900ea63befa854a840", - "0x9789f0fe18d832ff72df45befa7cabf0a326b42ada3657d164c821c35ac7ed7b2e0eba3d67856e8c387626770059b0c3", - "0x986c6fe6771418549fa3263fa8203e48552d5ecb4e619d35483cb4e348d849851f09692821c9233ae9f16f36979c30c2", - "0xa9160182a9550c5756f35cea1fe752c647d1b64a12426a0b5b8d48af06a12896833ec5f5d9b90185764db0160905ca01", - "0x82614dbd89d54c1e0af4f6ffe8710e6e871f57ef833cbcb3d3d7c617a75ec31e2a459a89ebb716b18fc77867ff8d5d47", - "0x8fc298ffba280d903a7873d1b5232ce0d302201957226cddff120ffe8df9fee34e08420302c6b301d90e3d58f10beeb9", - "0x898da9ac8494e31705bdf684545eee1c99b564b9601877d226d0def9ec67a20e06f8c8ba2a5202cc57a643487b94af19", - "0x88218478d51c3ed2de35b310beedf2715e30208c18f046ee65e824f5e6fd9def921f6d5f75fd6dde47fa670c9520f91a", - "0x89703ae7dff9b3bc2a93b44cdbab12c3d8496063a3c658e21a7c2078e4c00be0eecae6379ee8c400c67c879748f1d909", - "0xa44d463477dece0d45abb0ebb5f130bfb9c0a3bbcd3be62adf84a47bbd6938568a89bc92a53ca638ff1a2118c1744738", - "0x95df2b4d392143ee4c39ad72f636d0ed72922de492769c6264015776a652f394a688f1d2b5cf46077d01fda8319ba265", - "0xaa989867375710ed07ad6789bfb32f85bdc71d207f6f838bd3bde9da5a169325481ac326076b72358808bd5c763ba5bb", - "0xb859d97d0173920d16bc01eb7d3ddd47273daac72f86c4c30392f8de05fee643e8d6aa8bebdbc5c2d89037bc68a8a105", - "0xb0249ec97411fa39aa06b3d9a6e04bbbcd5e99a7bc527273b6aa95e7ae5f437b495385adaefa4327231562d232c9f822", - "0x8209e156fe525d67e1c83ec2340d50d45eba5363f617f2e5738117cdcc4a829c4cc37639afd7745cbe929c66754fd486", - "0x99fd2728ceb4c62e5f0763337e6d28bf11fbe5df114217f002bc5cd3543c9f62a05a8a41b2e02295360d007eaab796a6", - "0x902ebc68b8372feeaf2e0b40bd6998a0e17981db9cc9d23f932c34fbcc680292a0d8adcea2ad3fb2c9ed89e7019445c2", - "0x8b5653f4770df67f87cb68970555b9131c3d01e597f514e0a399eec8056e4c5a7deed0371a27b3b2be426d8e860bf9f2", - "0x8f5af27fdc98a29c647de60d01b9e9fd0039013003b44ba7aa75a4b9c42c91feb41c8ae06f39e22d3aed0932a137affa", - "0x81babb9c1f5bcc0fd3b97d11dd871b1bbd9a56947794ff70ab4758ae9850122c2e78d53cb30db69ece23538dc4ee033e", - "0xb8b65d972734f8ecae10dd4e072fa73c9a1bf37484abcfa87e0d2fcecac57294695765f63be87e1ba4ec0eb95688403a", - "0xb0fe17d0e53060aef1947d776b06ab5b461a8ef41235b619ca477e3182fadaf9574f12ffc76420f074f82ac4a9aa7071", - "0xae265c0b90bf064d7a938e224cb1cd3b7eca3e348fbc4f50a29ac0930a803b96e0640992354aa14b303ea313cb523697", - "0x8bc10ffde3224e8668700a3450463ab460ec6f198e1deb016e2c9d1643cc2fe1b377319223f41ffeb0b85afd35400d40", - "0x8d5113b43aea2e0cc6f8ec740d6254698aff7881d72a6d77affd6e6b182909b4de8eb5f524714b5971b418627f15d218", - "0xae2ef0a401278b7b5d333f0588773ec62ead58807cdee679f72b1af343c1689c5f314989d9e6c9369f8da9ce76979db6", - "0xb9c1cb996a78d4f7793956daaa8d8825dd43c4c37877bc04026db4866144b1bf37aa804d2fe0a63c374cf89e55e9069f", - "0xa35f73851081f6540e536a24a28808d478a2bb1fd15ee7ff61b1562e44fbafc0004b9c92c9f96328d546b1287e523e48", - "0x82007f34e3383c628c8f490654369744592aa95a63a72be6e90848ad54f8bc2d0434b62f92a7c802c93017214ecf326e", - "0x9127db515b1ed3644c64eaf17a6656e6663838fed4c6612a444a6761636eaaeb6a27b72d0e6d438c863f67b0d3ec25c5", - "0x984c9fcc3deccf83df3bbbb9844204c68f6331f0f8742119ba30634c8c5d786cd708aa99555196cf6563c953816aec44", - "0xa0f9daf900112029474c56ddd9eb3b84af3ed2f52cd83b4eb34531cf5218e7c58b3cab4027b9fc17831e1b6078f3bf4a", - "0x90adbcc921369023866a23f5cea7b0e587d129ad71cab0449e2e2137838cea759dec27b0b922c59ac4870ef6146ea283", - "0x8c5650b6b9293c168af98cf60ad35c945a30f5545992a5a8c05d42e09f43b04d370c4d800f474b2323b4269281ca50f8", - "0x868d95be8b34a337b5da5d886651e843c073f324f9f1b4fbd1db14f74aba6559449f94c599f387856c5f8a7bc83b52a1", - "0x812df0401d299c9e95a8296f9c520ef12d9a3dd88749b51eab8c1b7cc97961608ab9fc241a7e2888a693141962c8fd6d", - "0xabda319119d8a4d089393846830eee19d5d6e65059bf78713b307d0b4aad245673608b0880aa31c27e96c8d02eff39c0", - "0x887f11ae9e488b99cb647506dcaa5e2518b169ee70a55cd49e45882fe5bfb35ffaf11feb2bf460c17d5e0490b7c1c14d", - "0xb36b6e9f95ffff917ca472a38fa7028c38dc650e1e906e384c10fe38a6f55e9b84b56ffa3a429d3b0c3e2cf8169e66a9", - "0xa0450514d20622b7c534f54be3260bab8309632ca21c6093aa0ccc975b8eed33a922cbcc30a730ccc506edf9b188a879", - "0x87cfaf7bcd5d26875ca665ac45f9decd3854701b0443332da0f9b213e69d6f5521ae0217ec375489cd4fad7b4babf724", - "0x842ad67c1baf7a9d4504c10c5c979ce0a4d1b86a263899e2b5757407c2adcdcf7ed58173ad9d156d84075ef8798cb1c4", - "0xac1a05755fe4d3fb2ab5b951bafe65cca7c7842022ca567b32cddf7741782cbf8c4990c1dd4ea05dc087a4712844aebb", - "0xa000c8cecc4fddeb926dc8dd619952bc51d00d7c662e025f973387a3fc8b1ef5c7c10b6a62e963eb785e0ec04cb1ffbe", - "0x8a573c9986dbeb469547dfd09f60078eab252d8ec17351fe373a38068af046b0037967f2b3722fa73ed73512afd038d2", - "0xb8dff15dff931f58ba05b6010716c613631d7dd9562ae5138dbec966630bcdb0e72552e4eefc0351a6a6b7912d785094", - "0x990e81fd459433522e8b475e67e847cb342c4742f0dbf71acc5754244ccd1d9ff75919168588d8f18b8aea17092dd2a4", - "0xb012f8644da2113bef7dd6cdc622a55cfa0734bd267b847d11bba2e257a97a2a465c2bb616c240e197ff7b23e2ce8d8e", - "0xa659bd590fde467766e2091c34a0b070772f79380be069eef1afecc470368a95afd9eed6520d542c09c0d1a9dca23bd0", - "0xb9239f318b849079477d1cf0a60a3d530391adacd95c449373da1c9f83f03c496c42097c3f9aca10c1b9b3dbe5d98923", - "0x851e9a6add6e4a0ee9994962178d06f6d4fbc0def97feef1ba4c86d3bcf027a59bafa0cf25876ca33e515a1e1696e5cc", - "0x803b9c5276eed78092de2f340b2f0d0165349a24d546e495bd275fe16f89a291e4c74c22fdee5185f8fce0c7fbced201", - "0x95915654ca4656d07575168fb7290f50dc5dcbbcdf55a44df9ec25a9754a6571ab8ca8a159bc27d9fa47c35ffd8f7ffd", - "0x88f865919764e8e765948780c4fdd76f79af556cd95e56105d603c257d3bfb28f11efca1dfb2ce77162f9a5b1700bac8", - "0xb1233131f666579b4cc8b37cfa160fc10551b1ec33b784b82685251464d3c095cdde53d0407c73f862520aa8667b1981", - "0xa91115a15cf4a83bda1b46f9b9719cfba14ffb8b6e77add8d5a0b61bea2e4ea8ce208e3d4ed8ca1aab50802b800e763a", - "0x93553b6c92b14546ae6011a34600a46021ce7d5b6fbfcda2a70335c232612205dbe6bfb1cc42db6d49bd4042c8919525", - "0x8c2a498e5d102e80c93786f13ccf3c9cab7f4c538ccf0aee8d8191da0dbca5d07dff4448383e0cf5146f6d7e629d64f8", - "0xa66ab92c0d2c07ea0c36787a86b63ee200499527c93b9048b4180fc77e0bb0aa919f4222c4bec46eeb3f93845ab2f657", - "0x917e4fc34081a400fc413335fdf5a076495ae19705f8542c09db2f55fa913d6958fa6d711f49ad191aec107befc2f967", - "0x940631a5118587291c48ac8576cdc7e4a904dd9272acb79407a7d3549c3742d9b3669338adbc1386724cc17ee0cc1ca3", - "0xae23ae3a531900550671fd10447a35d3653c5f03f65b0fdffe092844c1c95d0e67cab814d36e6388db5f8bd0667cd232", - "0xae545727fca94fd02f43e848f0fbbb1381fd0e568a1a082bf3929434cc73065bfbc9f2c840b270dda8cc2e08cd4d44b0", - "0x8a9bc9b90e98f55007c3a830233c7e5dc3c4760e4e09091ff30ee484b54c5c269e1292ce4e05c303f6462a2a1bd5de33", - "0xa5a2e7515ce5e5c1a05e5f4c42f99835f6fde14d47ecb4a4877b924246038f5bc1b91622e2ff97ed58737ed58319acfa", - "0x8fa9f5edf9153618b72b413586e10aaa6c4b6e5d2d9c3e8693ca6b87804c58dc4bf23a480c0f80cb821ebc3cf20ea4fc", - "0x925134501859a181913aadac9f07f73d82555058d55a7d5aaa305067fbd0c43017178702facc404e952ea5cfd39db59b", - "0x8b5ab1d9b5127cb590d6bddbf698ffe08770b6fc6527023d6c381f39754aecc43f985c47a46be23fe29f6ca170249b44", - "0xaa39c6b9626354c967d93943f4ef09d637e13c505e36352c385b66e996c19c5603b9f0488ad4014bb5fc2e051b2876cc", - "0x8e77399c6e9cb8345002195feb7408eb571e6a81c0418590d2d775af7414fc17e61fe0cd37af8e737b59b89c849d3a28", - "0xa0150aeca2ddc9627c7ea0af0dd4426726583389169bc8174fc1597cc8048299cc594b22d234a4e013dff7232b2d946c", - "0x98659422ef91f193e6104b09ff607d1ed856bb6baed2a6386c9457efbc748bd1bf436573d80465ebc54f8c340b697ea5", - "0x8d6fb015898d3672eb580e1ffdf623fc4b23076664623b66bfb18f450d29522e8cb9c90f00d28ccf00af34f730bff7ac", - "0x996a8538efa9e2937c1caad58dc6564e5c185ada6cdcef07d5ec0056eb1259b0e4cef410252a1b5dbaee0da0b98dac91", - "0xaa0ae2548149d462362a33f96c3ce9b5010ebf202602e81e0ef77e22cfc57ecf03946a3076b6171bea3d3dc9681187d7", - "0xa5ce876b29f6b89050700df46d679bed85690daf7bad5c0df65e6f3bde5673e6055e6c29a4f4dcb82b93ccecf3bad9cc", - "0x81d824bb283c2f55554340c3514e15f7f1db8e9e95dd60a912826b1cccb1096f993a6440834dad3f2a5de70071b4b4b5", - "0x914e7291da286a89dfc923749da8f0bf61a04faa3803d6d10633261a717184065dcc4980114ad852e359f79794877dd9", - "0xae49dc760db497c8e834510fe89419cc81f33fd2a2d33de3e5e680d9a95a0e6a3ccbdf7c0953beeb3d1caf0a08b3e131", - "0xb24f527d83e624d71700a4b238016835a2d06f905f3740f0005105f4b2e49fc62f7e800e33cdc900d805429267e42fc0", - "0xb03471ecaa7a3bf54503347f470a6c611e44a3cee8218ad3fcad61d286cfb7bb6a1113dad18475ec3354a71fcc4ec1e2", - "0x881289b82b30aff4c8f467c2a25fced6064e1eece97c0de083e224b21735da61c51592a60f2913e8c8ba4437801f1a83", - "0xb4ce59c0fc1e0ecad88e79b056c2fd09542d53c40f41dea0f094b7f354ad88db92c560b9aeb3c0ef48137b1a0b1c3f95", - "0xa1ffb30eb8ef0e3ea749b5f300241ebe748ed7cf480e283dfcda7380aa1c15347491be97e65bc96bdf3fe62d8b74b3ae", - "0xb8954a826c59d18c6bfab24719f8730cc901868a95438838cd61dac468a2d79b1d42f77284e86e3382bf4f2a22044927", - "0x818e7e7c59b6b5e22b3c2c19c163f2e787f2ff3758d395a4da02766948935eb44413c3ddd2bf45804a3c19744aa332f3", - "0xa29556e49866e4e6f01d4f042eed803beeda781462884a603927791bd3750331a11bc013138f3270c216ab3aa5d39221", - "0xb40885fa0287dc92859b8b030c7cca4497e96c387dcfe6ed13eb7f596b1eb18fb813e4ae139475d692f196431acb58fe", - "0x89cd634682fd99ee74843ae619832780cf7cd717f230ea30f0b1821caf2f312b41c91f459bdba723f780c7e3eed15676", - "0xb48c550db835750d45a7f3f06c58f8f3bf8766a441265ca80089ead0346f2e17cbb1a5e843557216f5611978235e0f83", - "0x90936ee810039783c09392857164ab732334be3a3b9c6776b8b19f5685379c623b1997fb0cdd43af5061d042247bc72f", - "0xa6258a6bae36525794432f058d4b3b7772ba6a37f74ef1c1106c80a380fc894cbeac4f340674b4e2f7a0f9213b001afd", - "0x8f26943a32cf239c4e2976314e97f2309a1c775777710393c672a4aab042a8c6ee8aa9ac168aed7c408a436965a47aeb", - "0x820f793573ca5cc3084fe5cef86894c5351b6078df9807d4e1b9341f9d5422dd29d19a73b0843a14ad63e8827a75d2da", - "0xa3c4fca786603cd28f2282ba02afe7cf9287529e0e924ca90d6cdfd1a3912478ebb3076b370ee72e00df5517134fe17f", - "0x8f3cdabd0b64a35b9ee9c6384d3a8426cc49ae6063632fb1a56a0ae94affa833955f458976ff309dafd0b2dd540786ae", - "0x945a0630cd8fa111cfd776471075e5d2bbe8eb7512408b5c79c8999bfaeca6c097f988fb1c38fa9c1048bac2bca19f2e", - "0x8a7f6c4e0ba1920c98d0b0235b4dda73b631f511e209b10c05c550f51e91b4ba3893996d1562f04ac7105a141464e0e9", - "0xab3c13d8b78203b4980412edc8a8f579e999bf79569e028993da9138058711d19417cf20b477ef7ed627fa4a234c727a", - "0x82b00d9a3e29ed8d14c366f7bb25b8cfe953b7be275db9590373a7d8a86ea927d56dc3070a09ef7f265f6dd99a7c896e", - "0xb6e48a282de57949821e0c06bc9ba686f79e76fb7cbf50ea8b4651ccd29bc4b6da67efea4662536ba9912d197b78d915", - "0xa749e9edcba6b4f72880d3f84a493f4e8146c845637009f6ff227ff98521dbbe556a3446340483c705a87e40d07364bc", - "0xb9b93c94bd0603ce5922e9c4c29a60066b64a767b3aed81d8f046f48539469f5886f14c09d83b5c4742f1b03f84bb619", - "0xafa70b349988f85ed438faafa982df35f242dd7869bda95ae630b7fd48b5674ef0f2b4d7a1ca8d3a2041eff9523e9333", - "0xa8e7e09b93010982f50bd0930842898c0dcd30cdb9b123923e9d5ef662b31468222fc50f559edc57fcfdc597151ebb6e", - "0x8ce73be5ac29b0c2f5ab17cae32c715a91380288137d7f8474610d2f28d06d458495d42b9cb156fb1b2a7dfdcc437e1c", - "0x85596c1d81f722826d778e62b604eb0867337b0204c9fae636399fa25bb81204b501e5a5912654d215ec28ff48b2cb07", - "0x96ff380229393ea94d9d07e96d15233f76467b43a3e245ca100cbecbdbb6ad8852046ea91b95bb03d8c91750b1dfe6e1", - "0xb7417d9860b09f788eb95ef89deb8e528befcfa24efddbc18deaf0b8b9867b92361662db49db8121aeea85a9396f64fd", - "0x97b07705332a59cdba830cc8490da53624ab938e76869b2ce56452e696dcc18eb63c95da6dffa933fb5ffb7585070e2d", - "0x971f757d08504b154f9fc1c5fd88e01396175b36acf7f7abcfed4fff0e421b859879ed268e2ac13424c043b96fbe99fc", - "0xb9adb5d3605954943a7185bddf847d4dbe7bafe970e55dc0ec84d484967124c26dd60f57800d0a8d38833b91e4da476a", - "0xb4856741667bb45cae466379d9d6e1e4191f319b5001b4f963128b0c4f01819785732d990b2f5db7a3452722a61cd8cc", - "0xa81ec9f2ab890d099fb078a0c430d64e1d06cbbe00b1f140d75fc24c99fe35c13020af22de25bbe3acf6195869429ba5", - "0x99dcea976c093a73c08e574d930d7b2ae49d7fe43064c3c52199307e54db9e048abe3a370b615798b05fe8425a260ba0", - "0xa1f7437c0588f8958b06beb07498e55cd6553429a68cd807082aa4cc031ab2d998d16305a618b3d92221f446e6cd766d", - "0x806e4e0958e0b5217996d6763293f39c4f4f77016b3373b9a88f7b1221728d14227fce01b885a43b916ff6c7a8bc2e06", - "0x8e210b7d1aff606a6fc9e02898168d48ec39bc687086a7fe4be79622dd12284a5991eb53c4adfe848251f20d5bfe9de0", - "0x82810111e10c654a6c07cbfd1aff66727039ebc3226eef8883d570f25117acf259b1683742f916ac287097223afc6343", - "0x92f0e28cca06fd543f2f620cc975303b6e9a3d7c96a760e1d65b740514ccd713dc7a27a356a4be733570ca199edd17ba", - "0x900810aa4f98a0d6e13baf5403761a0aeb6422249361380c52f98b2c79c651e3c72f7807b5b5e3a30d65d6ff7a2a9203", - "0xb0740bfefea7470c4c94e85185dbe6e20685523d870ff3ef4eb2c97735cef41a6ab9d8f074a37a81c35f3f8a7d259f0e", - "0xaf022e98f2f418efbbe2de6fefb2aa133c726174f0f36925a4eafd2c6fd6c744edb91386bafb205ce13561de4294f3a6", - "0x95e4592e21ba97e950abb463e1bc7b0d65f726e84c06a98eb200b1d8bfc75d4b8cff3f55924837009e88272542fd25ec", - "0xb13bd6b18cd8a63f76c9831d547c39bbd553bda66562c3085999c4da5e95b26b74803d7847af86b613a2e80e2f08caae", - "0xa5625658b474a95aba3e4888c57d82fb61c356859a170bc5022077aa6c1245022e94d3a800bf7bd5f2b9ab1348a8834e", - "0xa097ee9e6f1d43e686df800c6ce8cfc1962e5a39bb6de3cf5222b220a41b3d608922dae499bce5c89675c286a98fdabd", - "0x94230ba8e9a5e9749cd476257b3f14a6bf9683e534fb5c33ca21330617533c773cb80e508e96150763699ad6ecd5aee7", - "0xb5fea7e1f4448449c4bc5f9cc01ac32333d05f464d0ed222bf20e113bab0ee7b1b778cd083ceae03fdfd43d73f690728", - "0xa18a41a78a80a7db8860a6352642cdeef8a305714543b857ca53a0ee6bed70a69eeba8cfcf617b11586a5cc66af4fc4f", - "0x85d7f4b3ff9054944ac80a51ef43c04189d491e61a58abed3f0283d041f0855612b714a8a0736d3d25c27239ab08f2ec", - "0xb1da94f1e2aedd357cb35d152e265ccfc43120825d86733fa007fc1e291192e8ff8342306bef0c28183d1df0ccec99d0", - "0x852893687532527d0fbeea7543ac89a37195eadab2f8f0312a77c73bdeed4ad09d0520f008d7611539425f3e1b542cfd", - "0x99e3bd4d26df088fc9019a8c0b82611fd4769003b2a262be6b880651d687257ded4b4d18ccb102cba48c5e53891535e4", - "0x98c407bc3bbc0e8f24bedf7a24510a5d16bce1df22940515a4fbdacd20d06d522ef9405f5f9b9b55964915dd474e2b5c", - "0x80de0a12f917717c6fc9dc3ccc9732c28bae36cff4a9f229d5eaf0d3e43f0581a635ba2e38386442c973f7cb3f0fdfa7", - "0x94f9615f51466ae4bb9c8478200634b9a3d762d63f2a16366849096f9fc57f56b2e68fe0ca5d4d1327a4f737b3c30154", - "0xa3dcbe16499be5ccb822dfcd7c2c8848ba574f73f9912e9aa93d08d7f030b5076ca412ad4bf6225b6c67235e0ab6a748", - "0x98f137bf2e1aea18289750978feb2e379054021e5d574f66ca7b062410dcfe7abb521fab428f5b293bbe2268a9af3aa4", - "0x8f5021c8254ba426f646e2a15b6d96b337a588f4dfb8cbae2d593a4d49652ca2ada438878de5e7c2dbbd69b299506070", - "0x8cc3f67dd0edcdb51dfd0c390586622e4538c7a179512f3a4f84dd7368153a28b1cf343afd848ac167cb3fcaa6aee811", - "0x863690f09ac98484d6189c95bc0d9e8f3b01c489cb3f9f25bf7a13a9b6c1deaf8275ad74a95f519932149d9c2a41db42", - "0x8494e70d629543de6f937b62beca44d10a04875bd782c9a457d510f82c85c52e6d34b9c3d4415dd7a461abbcc916c3c4", - "0x925b5e1e38fbc7f20371b126d76522c0ea1649eb6f8af8efb389764ddcf2653775ef99a58a2dcf1812ce882964909798", - "0x94d0494dcc44893c65152e7d42f4fb0dc46af5dc5674d3c607227160447939a56d9f9ea2b3d3736074eef255f7ec7566", - "0xb0484d33f0ef80ff9b9d693c0721c77e518d0238918498ddf71f14133eb484defb9f9f7b9083d52bc6d6ba2012c7b036", - "0x8979e41e0bb3b501a7ebbd024567ce7f0171acfea8403a530fe9e791e6e859dfbd60b742b3186d7cf5ab264b14d34d04", - "0xaf93185677d39e94a2b5d08867b44be2ba0bb50642edca906066d80facde22df4e6a7a2bd8b2460a22bdf6a6e59c5fdd", - "0x90f0ef0d7e7ab878170a196da1b8523488d33e0fde7481f6351558b312d00fa2b6b725b38539063f035d2a56a0f5e8f1", - "0xa9ca028ccb373f9886574c2d0ea5184bc5b94d519aa07978a4814d649e1b6c93168f77ae9c6aa3872dd0eea17968ec22", - "0x82e7aa6e2b322f9f9c180af585b9213fb9d3ad153281f456a02056f2d31b20d0f1e8807ff0c85e71e7baca8283695403", - "0xaffce186f842c547e9db2dffc0f3567b175be754891f616214e8c341213cbf7345c9ecd2f704bb0f4b6eba8845c8d8a7", - "0xab119eb621fade27536e98c6d1bc596388bb8f5cad65194ea75c893edbe6b4d860006160f1a9053aea2946bd663e5653", - "0x99cd2c1c38ead1676657059dc9b43d104e8bd00ae548600d5fc5094a4d875d5b2c529fac4af601a262045e1af3892b5e", - "0xb531a43b0714cc638123487ef2f03dfb5272ff399ff1aa67e8bc6a307130d996910fb27075cbe53050c0f2902fc32ffe", - "0x923b59ac752c77d16b64a2d0a5f824e718460ef78d732b70c4c776fecc43718ecfaf35f11afbb544016232f445ecab66", - "0xa53439cd05e6e1633cdce4a14f01221efcd3f496ac1a38331365c3cadc30013e5a71600c097965927ee824b9983a79cb", - "0x8af976ffab688d2d3f9e537e2829323dda9abf7f805f973b7e0a01e25c88425b881466dee37b25fda4ea683a0e7b2c03", - "0x92e5f40230a9bfbb078fa965f58912abb753b236f6a5c28676fb35be9b7f525e25428160caeaf0e3645f2be01f1a6599", - "0x8c4e7b04e2f968be527feba16f98428508a157b7b4687399df87666a86583b4446a9f4b86358b153e1660bb80bd92e8b", - "0x97cd622d4d8e94dceb753c7a4d49ea7914f2eb7d70c9f56d1d9a6e5e5cc198a3e3e29809a1d07d563c67c1f8b8a5665a", - "0x967bfa8f411e98bec142c7e379c21f5561f6fd503aaf3af1a0699db04c716c2795d1cb909cccbcb917794916fdb849f1", - "0xb3c18a6caa5ca2be52dd500f083b02a4745e3bcaed47b6a000ce7149cee4ed7a78d2d7012bf3731b1c15c6f04cbd0bd1", - "0xb3f651f1f84026f1936872956a88f39fcfe3e5a767233349123f52af160f6c59f2c908c2b5691255561f0e70620c8998", - "0xae23b59dc2d81cec2aebcaaf607d7d29cf588f0cbf7fa768c422be911985ca1f532bb39405f3653cc5bf0dcba4194298", - "0xa1f4da396f2eec8a9b3252ea0e2d4ca205f7e003695621ae5571f62f5708d51ca3494ac09c824fca4f4d287a18beea9a", - "0xa036fa15e929abed7aac95aa2718e9f912f31e3defd224e5ed379bf6e1b43a3ad75b4b41208c43d7b2c55e8a6fedca72", - "0x80e8372d8a2979ee90afbdb842624ace72ab3803542365a9d1a778219d47f6b01531185f5a573db72213ab69e3ffa318", - "0xaf68b5cdc39e5c4587e491b2e858a728d79ae7e5817a93b1ea39d34aec23dea452687046c8feae4714def4d0ed71da16", - "0xb36658dfb756e7e9eec175918d3fe1f45b398679f296119cd53be6c6792d765ef5c7d5afadc5f3886e3f165042f4667f", - "0xad831da03b759716f51099d7c046c1a8e7bf8bb45a52d2f2bfd769e171c8c6871741ef8474f06e2aca6d2b141cf2971f", - "0x8bae1202dde053c2f59efc1b05cb8268ba9876e4bd3ff1140fa0cc5fa290b13529aede965f5efdff3f72e1a579efc9cc", - "0x86344afbc9fe077021558e43d2a032fcc83b328f72948dba1a074bb1058e8a8faec85b1c019fc9836f0d11d2585d69c8", - "0x831d1fc7aa28f069585d84c46bdc030d6cb12440cfaae28098365577fc911c4b8f566d88f80f3a3381be2ec8088bf119", - "0x899de139797ac1c8f0135f0656f04ad4f9b0fa2c83a264d320eb855a3c0b9a4907fc3dc01521d33c07b5531e6a997064", - "0x855bc752146d3e5b8ba7f382b198d7dc65321b93cdfc76250eabc28dba5bbf0ad1be8ccda1adf2024125107cb52c6a6e", - "0xaf0aeccab48eb35f8986cabf07253c5b876dd103933e1eee0d99dc0105936236b2a6c413228490ed3db4fa69aab51a80", - "0xae62e9d706fbf535319c909855909b3deba3e06eaf560803fa37bce3b5aab5ea6329f7609fea84298b9da48977c00c3b", - "0x823a8d222e8282d653082d55a9508d9eaf9703ce54d0ab7e2b3c661af745a8b6571647ec5bd3809ae6dddae96a220ea7", - "0xa4c87e0ea142fc287092bc994e013c85e884bc7c2dde771df30ca887a07f955325c387b548de3caa9efa97106da8176a", - "0xb55d925e2f614f2495651502cf4c3f17f055041fa305bb20195146d896b7b542b1e45d37fa709ca4bfc6b0d49756af92", - "0xb0ebe8947f8c68dc381d7bd460995340efcbb4a2b89f17077f5fde3a9e76aef4a9a430d1f85b2274993afc0f17fdbead", - "0x8baaa640d654e2652808afd68772f6489df7cad37b7455b9cd9456bdddae80555a3f84b68906cc04185b8462273dcfc9", - "0xadd9aa08f827e7dc292ac80e374c593cd40ac5e34ad4391708b3db2fe89550f293181ea11b5c0a341b5e3f7813512739", - "0x909e31846576c6bdd2c162f0f29eea819b6125098452caad42451491a7cde9fd257689858f815131194200bca54511f4", - "0xabc4b34098db10d71ce7297658ef03edfa7377bd7ed36b2ffbab437f8fd47a60e2bcfbc93ff74c85cfce74ca9f93106c", - "0x857dbecc5879c1b952f847139484ef207cecf80a3d879849080758ef7ac96acfe16a11afffb42daf160dc4b324279d9b", - "0xaab0b49beecbcf3af7c08fbf38a6601c21061bed7c8875d6e3c2b557ecb47fd93e2114a3b09b522a114562467fcd2f7d", - "0x94306dec35e7b93d43ed7f89468b15d3ce7d7723f5179cacc8781f0cf500f66f8c9f4e196607fd14d56257d7df7bf332", - "0x9201784d571da4a96ef5b8764f776a0b86615500d74ec72bc89e49d1e63a3763b867deca07964e2f3914e576e2ca0ded", - "0xaabe1260a638112f4280d3bdea3c84ce3c158b81266d5df480be02942cecf3de1ac1284b9964c93d2db33f3555373dcc", - "0x8ef28607ca2e0075aa07de9af5a0f2d0a97f554897cab8827dfe3623a5e9d007d92755d114b7c390d29e988b40466db9", - "0x87a9b1b097c3a7b5055cd9cb0c35ba6251c50e21c74f6a0bca1e87e6463efc38385d3acc9d839b4698dfa2eb4cb7a2ef", - "0xaee277e90d2ffce9c090295c575e7cd3bafc214d1b5794dd145e6d02d987a015cb807bd89fd6268cd4c59350e7907ee2", - "0x836ad3c9324eaa5e022e9835ff1418c8644a8f4cd8e4378bd4b7be5632b616bb6f6c53399752b96d77472f99ece123cd", - "0x8ffffdb67faa5f56887c834f9d489bb5b4dab613b72eac8abf7e4bcb799ccd0dbd88a2e73077cadf7e761cb159fb5ec5", - "0x9158f6cd4f5e88e6cdb700fddcbc5a99b2d31a7a1b37dce704bd9dd3385cca69607a615483350a2b1153345526c8e05d", - "0xa7ff0958e9f0ccff76742fc6b60d2dd91c552e408c84172c3a736f64acb133633540b2b7f33bc7970220b35ce787cd4e", - "0x8f196938892e2a79f23403e1b1fb4687a62e3a951f69a7874ec0081909eb4627973a7a983f741c65438aff004f03ba6f", - "0x97e3c1981c5cdb0a388f1e4d50b9b5b5f3b86d83417831c27b143698b432bb5dba3f2e590d6d211931ed0f3d80780e77", - "0x903a53430b87a7280d37816946245db03a49e38a789f866fe00469b7613ee7a22d455fb271d42825957282c8a4e159d9", - "0xb78955f686254c3994f610e49f1c089717f5fb030da4f9b66e9a7f82d72381ba77e230764ab593335ff29a1874848a09", - "0x938b6d04356b9d7c8c56be93b0049d0d0c61745af7790edf4ef04e64de2b4740b038069c95be5c91a0ba6a1bb38512a9", - "0xa769073b9648fe21bc66893a9ef3b8848d06f4068805a43f1c180fdd0d37c176b4546f8e5e450f7b09223c2f735b006f", - "0x863c30ebe92427cdd7e72d758f2c645ab422e51ecef6c402eb1a073fd7f715017cd58a2ad1afe7edccdf4ff01309e306", - "0xa617b0213d161964eccfc68a7ad00a3ee4365223b479576e887c41ef658f846f69edf928bd8da8785b6e9887031f6a57", - "0xa699834bf3b20d345082f13f360c5f8a86499e498e459b9e65b5a56ae8a65a9fcb5c1f93c949391b4795ef214c952e08", - "0x9921f1da00130f22e38908dd2e44c5f662ead6c4526ebb50011bc2f2819e8e3fca64c9428b5106fa8924db76b7651f35", - "0x98da928be52eb5b0287912fd1c648f8bbda00f5fd0289baf161b5a7dbda685db6ad6bdc121bc9ffa7ed6ae03a13dbee3", - "0x927b91d95676ff3c99de1312c20f19251e21878bfb47ad9f19c9791bc7fb9d6f5c03e3e61575c0760180d3445be86125", - "0xb8e4977a892100635310dfcb46d8b74931ac59ae687b06469b3cee060888a3b6b52d89de54e173d9e1641234754b32b1", - "0x98f6fd5f81ca6e2184abd7a3a59b764d4953d408cec155b4e5cf87cd1f6245d8bdd58b52e1e024e22903e85ae15273f1", - "0x909aaacbbfe30950cf7587faa190dc36c05e3c8131749cc21a0c92dc4afc4002275762ca7f66f91aa751b630ad3e324d", - "0x91712141592758f0e43398c075aaa7180f245189e5308e6605a6305d01886d2b22d144976b30460d8ce17312bb819e8f", - "0x947d85cb299b189f9116431f1c5449f0f8c3f1a70061aa9ebf962aa159ab76ee2e39b4706365d44a5dbf43120a0ac255", - "0xb39eced3e9a2e293e04d236976e7ee11e2471fe59b43e7b6dd32ab74f51a3d372afee70be1d90af017452ec635574e0e", - "0x8a4ba456491911fc17e1cadcbb3020500587c5b42cf6b538d1cb907f04c65c168add71275fbf21d3875e731404f3f529", - "0x8f6858752363e2a94c295e0448078e9144bf033ccd4d74f4f6b95d582f3a7638b6d3f921e2d89fcd6afd878b12977a9d", - "0xb7f349aa3e8feb844a56a42f82b6b00f2bfe42cab19f5a68579a6e8a57f5cf93e3cdb56cbbb9163ab4d6b599d6c0f6aa", - "0xa4a24dc618a6b4a0857fb96338ac3e10b19336efc26986e801434c8fdde42ca8777420722f45dfe7b67b9ed9d7ce8fb1", - "0xaafe4d415f939e0730512fc2e61e37d65c32e435991fb95fb73017493014e3f8278cd0d213379d2330b06902f21fe4e1", - "0x845cc6f0f0a41cc6a010d5cb938c0ef8183ff5ed623b70f7ea65a8bdbc7b512ea33c0ee8b8f31fdf5f39ec88953f0c1e", - "0x811173b4dd89d761c0bdffe224cd664ef303c4647e6cf5ef0ed665d843ed556b04882c2a4adfc77709e40af1cfdea40b", - "0x93ba1db7c20bfba22da123b6813cb38c12933b680902cef3037f01f03ab003f76260acc12e01e364c0d0cf8d45fca694", - "0xb41694db978b2cf0f4d2aa06fcfc4182d65fb7c9b5e909650705f779b28e47672c47707d0e5308cd680c5746c37e1bc7", - "0xa0e92c4c5be56a4ccf1f94d289e453a5f80e172fc90786e5b03c1c14ce2f3c392c349f76e48a7df02c8ae535326ea8fe", - "0x96cbeb1d0693f4f0b0b71ad30def5ccc7ad9ebe58dbe9d3b077f2ac16256cde10468875e4866d63e88ce82751aaf8ef6", - "0x935b87fd336f0bf366046e10f7c2f7c2a2148fa6f53af5607ad66f91f850894527ecec7d23d81118d3b2ee23351ed6ed", - "0xb7c2c1fa6295735f6b31510777b597bc8a7bfb014e71b4d1b5859be0d8d64f62a1587caafc669dfe865b365eb27bd94f", - "0xb25d93af43d8704ffd53b1e5c16953fd45e57a9a4b7acfcfa6dd4bf30ee2a8e98d2a76f3c8eba8dc7d08d9012b9694c6", - "0xb5a005cd9f891e33882f5884f6662479d5190b7e2aec1aa5a6d15a8cb60c9c983d1e7928e25e4cf43ec804eaea1d97b0", - "0x93f9f0725a06e4a0fb83892102b7375cf5438b5ebc9e7be5a655f3478d18706cf7dbb1cd1adcee7444c575516378aa1b", - "0x900d7cbf43fd6ac64961287fe593c08446874bfc1eb09231fc93de858ac7a8bca496c9c457bced5881f7bf245b6789e0", - "0x90c198526b8b265d75160ef3ed787988e7632d5f3330e8c322b8faf2ac51eef6f0ce5a45f3b3a890b90aecf1244a3436", - "0xb499707399009f9fe7617d8e73939cb1560037ad59ac9f343041201d7cc25379df250219fd73fa012b9ade0b04e92efa", - "0x94415f6c3a0705a9be6a414be19d478181d82752b9af760dda0dbd24a8ff0f873c4d89e61ad2c13ebf01de55892d07fa", - "0x90a9f0b9f1edb87751c696d390e5f253586aae6ebfc31eb3b2125d23877a497b4aa778de8b11ec85efe49969021eaa5a", - "0xa9942c56506e5cd8f9289be8205823b403a2ea233ba211cf72c2b3827064fd34cd9b61ff698a4158e7379891ca4120d8", - "0x83bb2ee8c07be1ab3a488ec06b0c85e10b83a531758a2a6741c17a3ccfa6774b34336926a50e11c8543d30b56a6ac570", - "0x8a08a3e5ebe10353e0b7fff5f887e7e25d09bb65becf7c74a03c60c166132efaada27e5aea242c8b9f43b472561ae3ed", - "0x957c7a24cefaa631fe8a28446bc44b09a3d8274591ade53ba489757b854db54820d98df47c8a0fbee0e094f8ad7a5dc4", - "0xb63556e1f47ed3ee283777ed46b69be8585d5930960d973f8a5a43508fc56000009605662224daec2de54ea52a8dcd82", - "0xabed2b3d16641f0f459113b105f884886d171519b1229758f846a488c7a474a718857323c3e239faa222c1ab24513766", - "0x882d36eed6756d86335de2f7b13d753f91c0a4d42ef50e30195cc3e5e4f1441afa5ff863022434acb66854eda5de8715", - "0xa65ea7f8745bb8a623b44e43f19158fd96e7d6b0a5406290f2c1348fc8674fbfc27beb4f724cc2b217c6042cb82bc178", - "0xa038116a0c76af090a069ca289eb2c3a615b96093efacfe68ea1610890b291a274e26b445d34f414cfec00c333906148", - "0x90294f452f8b80b0a47c3bcb6e30bdd6854e3b01deaf93f5e82a1889a4a1036d17ecb59b48efa7dc41412168d7a523dd", - "0x88faf969c8978a756f48c6114f7f33a1ca3fd7b5865c688aa9cd32578b1f7ba7c06120502f8dc9aee174ecd41597f055", - "0x8883763b2762dfff0d9be9ac19428d9fd00357ac8b805efda213993152b9b7eb7ba3b1b2623015d60778bffda07a724d", - "0xa30a1a5a9213636aa9b0f8623345dc7cf5c563b906e11cc4feb97d530a1480f23211073dcb81105b55193dcde5a381d2", - "0xb45ee93c58139a5f6be82572d6e14e937ef9fcbb6154a2d77cb4bf2e4b63c5aabc3277527ecf4e531fe3c58f521cc5e3", - "0xac5a73e4f686978e06131a333f089932adda6c7614217fcaf0e9423b96e16fd73e913e5e40bf8d7800bed4318b48d4b1", - "0xb6c1e6cdd14a48a7fe27cd370d2e3f7a52a91f3e8d80fb405f142391479f6c6f31aa5c59a4a0fdc9e88247c42688e0cf", - "0xab1760530312380152d05c650826a16c26223960fc8e3bf813161d129c01bac77583eff04ce8678ff52987a69886526b", - "0xa4252dffae7429d4f81dfaeeecc48ab922e60d6a50986cf063964f282e47407b7e9c64cf819da6f93735de000a70f0b2", - "0x94c19f96d5ecf4a15c9c5a24598802d2d21acbbd9ee8780b1bc234b794b8442437c36badc0a24e8d2cff410e892bb1d2", - "0x89fafe1799cf7b48a9ea24f707d912fccb99a8700d7287c6438a8879f3a3ca3e60a0f66640e31744722624139ba30396", - "0xb0108405df25cf421c2f1873b20b28552f4d5d1b4a0bf1c202307673927931cbd59f5781e6b8748ddb1206a5ec332c0b", - "0xaa0f0e7d09f12b48f1e44d55ec3904aa5707e263774126e0b30f912e2f83df9eb933ca073752e6b86876adaf822d14ba", - "0xb0cbe8abb58876d055c8150d9fdbde4fea881a517a2499e7c2ea4d55c518a3c2d00b3494f6a8fd1a660bfca102f86d2a", - "0xb1ef80ec903bac55f58b75933dc00f1751060690fd9dfb54cf448a7a4b779c2a80391f5fda65609274bd9e0d83f36141", - "0x8b52e05b1845498c4879bb12816097be7fc268ce1cf747f83a479c8e08a44159fc7b244cf24d55aca06dccf0b97d11e1", - "0xb632a2fc4fdb178687e983a2876ae23587fd5b7b5e0bb8c0eb4cfe6d921a2c99894762e2aaccdc5da6c48da3c3c72f6c", - "0x953ef80ab5f74274ae70667e41363ae6e2e98ccbd6b7d21f7283f0c1cafb120338b7a8b64e7c189d935a4e5b87651587", - "0xb929cfd311017c9731eed9d08d073f6cf7e9d4cd560cddd3fdcb1149ab20c6610a7674a66a3616785b13500f8f43ee86", - "0x870fb0d02704b6a328e68721fb6a4b0f8647681bfcb0d92ec3e241e94b7a53aecc365ed384e721c747b13fbf251002f1", - "0x979501159833a8ba5422ed9b86f87b5961711f5b474d8b0e891373fe2d0b98ff41a3a7a74a8b154615bb412b662a48be", - "0xb20f9c13cdeceef67f877b3878839ef425f645b16a69c785fe38f687c87a03b9de9ae31ac2edb1e1dd3a9f2c0f09d35d", - "0x8c7705ed93290731b1cf6f3bf87fc4d7159bb2c039d1a9f2246cda462d9cdf2beef62d9f658cfeea2e6aef7869a6fc00", - "0xaa439eb15705ad729b9163daee2598d98a32a8a412777c0d12fd48dc7796d422227a014705e445cc9d66f115c96bbc24", - "0xa32307e16f89749fe98b5df1effef0429801c067e0d8067794e56b01c4fef742ad5e7ab42a1a4cc4741808f47a0b7cb8", - "0xb31e65c549003c1207258a2912a72f5bad9844e18f16b0773ea7af8ff124390eb33b2f715910fc156c104572d4866b91", - "0x85608d918ed7b08a0dc03aee60ea5589713304d85eee7b4c8c762b6b34c9355d9d2e192575af0fd523318ae36e19ae1c", - "0xa6497dbaf0e7035160b7a787150971b19cf5ba272c235b0113542288611ebecefa2b22f08008d3f17db6a70a542c258d", - "0x87862adb1ac0510614ab909457c49f9ec86dc8bdf0e4682f76d2739df11f6ffcfb59975527f279e890d22964a1fba9b6", - "0x8717ac3b483b3094c3b642f3fafe4fbafc52a5d4f2f5d43c29d9cfe02a569daee34c178ee081144494f3a2ca6e67d7b1", - "0x855100ac1ec85c8b437fdd844abaa0ca4ac9830a5bdd065b68dafb37046fcf8625dd482dc0253476926e80a4c438c9ec", - "0xae74821bf265ca3c8702c557cf9ef0732ede7ef6ed658283af669d19c6f6b6055aca807cf2fa1a64785ec91c42b18ae5", - "0x812a745b1419a306f7f20429103d6813cbdea68f82ff635ac59da08630cd61bda6e0fa9a3735bfd4378f58ad179c1332", - "0x867dbbfe0d698f89451c37ca6d0585fd71ee07c3817e362ef6779b7b1d70b27c989cdd5f85ac33a0498db1c4d14521fe", - "0x84db735d3eb4ff7f16502dccc3b604338c3a4a301220ad495991d6f507659db4b9f81bba9c528c5a6114bcdba0160252", - "0xaadc83d1c4e5e32bf786cfb26f2f12a78c8024f1f5271427b086370cdef7a71d8a5bf7cd7690bae40df56c38b1ad2411", - "0xa27860eb0caaea37298095507f54f7729d8930ac1929de3b7a968df9737f4c6da3173bda9d64ff797ed4c6f3a1718092", - "0xa3cdcaa74235c0440a34171506ed03d1f72b150d55904ce60ec7b90fcd9a6f46f0e45feab0f9166708b533836686d909", - "0xb209a30bdac5c62e95924928f9d0d0b4113ebb8b346d7f3a572c024821af7f036222a3bd38bd8efd2ee1dbf9ac9556cd", - "0x83c93987eff8bc56506e7275b6bef0946672621ded641d09b28266657db08f75846dcbde80d8abc9470e1b24db4ca65b", - "0x800c09b3ee5d0251bdaef4a82a7fe8173de997cc1603a2e8df020dd688a0c368ad1ebef016b35136db63e774b266c74c", - "0x93fb52de00d9f799a9bce3e3e31aaf49e0a4fc865473feb728217bd70f1bc8a732ec37ac3582bf30ab60e8c7fdf3cb8d", - "0xa1aff6b4a50d02f079a8895c74443539231bfdf474600910febf52c9151da7b31127242334ac63f3093e83a047769146", - "0x8c4532d8e3abb5f0da851138bfa97599039bcd240d87bbdf4fd6553b2329abb4781074b63caf09bc724ceb4d36cb3952", - "0x8bd9b0ae3da5acda9eb3881172d308b03beec55014cd73b15026299541c42fd38bab4983a85c06894ebb7a2af2a23d4c", - "0x979441e7f5a0e6006812f21b0d236c5f505bb30f7d023cb4eb84ec2aa54a33ac91d87ece704b8069259d237f40901356", - "0xa1c6d2d82e89957d6a3e9fef48deb112eb00519732d66d55aa0f8161e19a01e83b9f7c42ac2b94f337dcc9865f0da837", - "0x97a0b8e04e889d18947d5bf77d06c25bbd62b19ce4be36aaa90ddbeafd93a07353308194199ba138efaadf1b928cd8d2", - "0x822f7fbe9d966b8ec3db0fc8169ab39334e91bf027e35b8cc7e1fe3ead894d8982505c092f15ddfe5d8f726b360ac058", - "0xa6e517eedd216949e3a10bf12c8c8ddbfde43cddcd2c0950565360a38444459191bdbc6c0af0e2e6e98bc6a813601c6d", - "0x858b5f15c46c074adb879b6ba5520966549420cb58721273119f1f8bc335605aeb4aa6dbe64aae9e573ca7cc1c705cdc", - "0xb5191bb105b60deb10466d8114d48fb95c4d72036164dd35939976e41406dff3ee3974c49f00391abfad51b695b3258c", - "0xb1b375353ed33c734f4a366d4afad77168c4809aff1b972a078fd2257036fd6b7a7edad569533abf71bc141144a14d62", - "0xa94c502a9cdd38c0a0e0187de1637178ad4fa0763887f97cc5bdd55cb6a840cb68a60d7dbb7e4e0e51231f7d92addcff", - "0x8fe2082c1b410486a3e24481ae0630f28eb5b488e0bb2546af3492a3d9318c0d4c52db1407e8b9b1d1f23a7ffbaf260a", - "0xb73fe7aa2b73f9cae6001af589bf8a9e73ea2bb3bb01b46743e39390c08d8e1be5e85a3d562857a9c9b802b780c78e6d", - "0x8e347f51330ae62275441ccd60f5ac14e1a925a54ced8a51893d956acc26914df1bb8595385d240aa9b0e5ada7b520ea", - "0x8dc573d6357c0113b026a0191a5807dbe42dcd2e19772d14b2ca735e1e67c70e319ef571db1f2a20e62254ed7fb5bcd6", - "0xa5dacbe51549fe412e64af100b8b5eba5ec2258cc2a7c27a34bc10177d1894baf8707886d2f2ef438f077596a07681e9", - "0x8349153c64961d637a5ff56f49003cb24106de19a5bbcf674016a466bfbe0877f5d1e74ccb7c2920665ef90a437b1b7e", - "0x96ad35429d40a262fdc8f34b379f2e05a411057d7852c3d77b9c6c01359421c71ef8620f23854e0f5d231a1d037e3a0d", - "0xb52385e40af0ed16e31c2154d73d1517e10a01435489fc801fbea65b92b3866ab46dab38d2c25e5fb603b029ae727317", - "0x8e801c7a3e8fa91d9c22ebd3e14a999023a7b5beea13ec0456f7845425d28c92452922ca35ec64012276acb3bbc93515", - "0xa8630870297d415e9b709c7f42aa4a32210b602f03a3015410123f0988aea2688d8bcfc6d07dc3602884abbf6199b23f", - "0x8cd518392e09df2a3771a736f72c05af60efc030d62dbbb9cd68dc6cbbe1fb0854eb78b6ed38337010eb1bb44a5d5d30", - "0x921aa4c66590f6c54bf2fa2b324f08cbe866329cc31f6e3477f97f73e1a1721d5eb50ed4eacc38051fe9eda76ba17632", - "0xa37e595cb63524cb033c5540b6343c3a292569fc115e813979f63fe1a3c384b554cecc2cae76b510b640fe3a18800c81", - "0xb0bb57e4e31ae3ce9f28cef158ed52dabfad5aa612f5fcc75b3f7f344b7cec56b989b5690dacd294e49c922d550ee36b", - "0xa3c618ce4d091e768c7295d37e3f9b11c44c37507ae1f89867441f564bf0108f67bf64b4cf45d73c2afc17a4dc8b2c68", - "0x999e6650eda5455e474c22a8c7a3fd5b547ec2875dc3043077ad70c332f1ccd02135e7b524fcbf3621d386dec9e614fa", - "0xb018f080888dec3c2ca7fcfeb0d3d9984699b8435d8823079fc9e1af4ca44e257fbe8da2f6f641ee6152b5c7110e3e3c", - "0xa2bcd4bcd9b40c341e9bba76b86481842f408166c9a7159205726f0776dcb7f15a033079e7589699e9e94ce24b2a77fd", - "0xb03de48f024a520bb9c54985ca356fd087ca35ac1dd6e95168694d9dae653138c9755e18d5981946a080e32004e238fe", - "0xa6c1a54973c0c32a410092441e20594aa9aa3700513ed90c8854956e98894552944b0b7ee9edf6e62e487dc4565baa2f", - "0x845d7abf577c27c4c1fafc955dcad99a1f2b84b2c978cfe4bd3cd2a6185979491f3f3b0ec693818739ed9184aba52654", - "0x9531bcfc0d3fcd4d7459484d15607d6e6181cee440ba6344b12a21daa62ff1153a4e9a0b5c3c33d373a0a56a7ad18025", - "0xa0bbf49b2dd581be423a23e8939528ceaae7fb8c04b362066fe7d754ca2546304a2a90e6ac25cdf6396bf0096fae9781", - "0xa1ec264c352e34ed2bf49681b4e294ffea7d763846be62b96b234d9a28905cdece4be310a56ec6a00fc0361d615b547c", - "0x87c575e85b5dfbfd215432cb355a86f69256fff5318e8fda457763ac513b53baa90499dc37574bdfad96b117f71cb45e", - "0x9972edfdeec56897bef4123385ee643a1b9dc24e522752b5a197ce6bd2e53d4b6b782b9d529ca50592ee65b60e4c9c3c", - "0xb8bcf8d4ab6ad37bdd6ad9913a1ba0aba160cb83d1d6f33a8524064a27ba74a33984cc64beeee9d834393c2636ff831a", - "0x83082b7ec5b224422d0ff036fbb89dc68918e6fde4077dfc0b8e2ee02595195ecadb60c9ab0ad69deb1bac9be75024fa", - "0x8b061fce6df6a0e5c486fd8d8809f6f3c93bd3378a537ff844970492384fb769d3845d0805edd7f0fcd19efabf32f197", - "0xb9597e717bb53e6afae2278dbc45d98959c7a10c87c1001ed317414803b5f707f3c559be6784119d08f0c06547ec60b1", - "0xb9d990fd7677dd80300714cfd09336e7748bbf26f4bb0597406fcb756d8828c33695743d7a3e3bd6ddf4f508149610ef", - "0xb45f7d2b00ceea3bf6131b230b5b401e13a6c63ba8d583a4795701226bf9eb5c88506f4a93219ac90ccbceef0bfd9d49", - "0xa8ccaa13ca7986bc34e4a4f5e477b11ae91abb45c8f8bf44a1f5e839289681495aba3daa8fb987e321d439bbf00be789", - "0xae0f59f7a94288a0ead9a398fdd088c2f16cccb68624de4e77b70616a17ddf7406ca9dc88769dadeb5673ff9346d6006", - "0xb28e965dcc08c07112ae3817e98f8d8b103a279ad7e1b7c3de59d9dbd14ab5a3e3266775a5b8bbf0868a14ae4ab110f1", - "0x84751c1a945a6db3df997fcbde9d4fe824bc7ba51aa6cb572bb5a8f9561bef144c952198a783b0b5e06f9dd8aa421be8", - "0xa83586db6d90ef7b4fa1cbda1de1df68ee0019f9328aded59b884329b616d888f300abb90e4964021334d6afdea058fd", - "0x8fcea1ce0abf212a56c145f0b8d47376730611e012b443b3d1563498299f55cbcbe8cbd02f10b78224818bb8cbbd9aaa", - "0x8d66c30a40c34f23bae0ea0999754d19c0eb84c6c0aa1b2cf7b0740a96f55dd44b8fee82b625e2dd6c3182c021340ac6", - "0x92c9b35076e2998f1a0f720d5a507a602bd6bd9d44ffc29ede964044b17c710d24ce3c0b4a53c12195de93278f9ec83b", - "0xa37d213913aff0b792ee93da5d7e876f211e10a027883326d582ad7c41deebdfce52f86b57d07868918585908ebd070a", - "0xa03995b4c6863f80dd02ed0169b4f1609dc48174ec736de78be1cdff386648426d031f6d81d1d2a7f2c683b31e7628c0", - "0xb08b628d481302aa68daf0fa31fd909064380d62d8ed23a49037cb38569058e4c16c80e600e84828d37a89a33c323d1f", - "0xa0ee2e2dd8e27661d7b607c61ac36f590909aa97f80bdfd5b42463ca147b610ac31a9f173cbecdd2260f0f9ea9e56033", - "0x967162fba8b69ffce9679aac49214debb691c6d9f604effd6493ce551abacbe4c8cc2b0ccee6c9927c3d3cfbdcb0be11", - "0x8deab0c5ed531ce99dadb98b8d37b3ff017f07438bc6d50840577f0f3b56be3e801181333b4e8a070135f9d82872b7f2", - "0xb1bfa00ec8c9365b3d5b4d77a718cb3a66ed6b6cf1f5cf5c5565d3aa20f63d3c06bb13d47d2524e159debf81325ba623", - "0x90109780e53aeacd540b9fe9fc9b88e83c73eaf3507e2b76edc67f97a656c06a8a9e1ec5bce58bfd98b59a6b9f81b89d", - "0x88a1009a39a40421fdcc0ffc3c78a4fbace96a4e53420b111218091223494e780a998ebecf5a0abd0243e1523df90b28", - "0x90b77146711ee8d91b0346de40eca2823f4e4671a12dad486a8ec104c01ef5ee7ab9bd0398f35b02b8cb62917455f8b3", - "0xb262c5e25f24ae7e0e321b66fdb73b3bf562ded566a2d6a0152cf8bafb56138d87b6a917a82f5ace65efc73cfc177d81", - "0xae65a438c7ea46c82925b5ec5f71314558ca5146f5d90311431d363cfeac0537223c02cbb50fa6535d72fc2d949f4482", - "0x8984208bfc193a6ef4720cc9d40c17f4be2f14595ef887980f2e61fa6927f9d73c00220937013b46290963116cbe66ac", - "0xa8f33a580508f667fac866456dce5d9246562188ad0f568eb1a2f28cf9fd3452dd20dc613adb1d07a5542319a37ecf1a", - "0xaedadd705fc086d8d2b647c62e209e2d499624ab37c8b19af80229f85e64a6e608d9cd414cb95ae38cf147d80ec3f894", - "0xae28077a235cd959f37dc3daedc3706f7a7c2ffe324e695f2e65f454bf5a9fc27b10149a6268ebfaa961ad67bb9b75d7", - "0xa234c7f5a5e0e30f2026d62657bd92d91a9907ec6a2177f91383f86abb919778121ff78afb8f52c473fe6fb731018b52", - "0x816a2ea7826b778f559a815267b6c6eb588558391c0a675d61bb19470d87489ba6c1e2486ea81dd5420a42ee7c35a8de", - "0x9218b61948c14234f549c438105ae98367ef6b727ad185f17ad69a6965c044bb857c585b84d72ef4c5fb46962974eed7", - "0xa628031217a0b1330b497351758cf72d90fb87d8bdf542ea32092e14ff32d5ef4ca700653794bb78514d4b0edfd7a8d7", - "0xab4e977141be639a78eb9ed17366f9642f9335873aca87cce2bae0dddc161621d0e23264a54a7395ae706d748c690ee9", - "0xb1538c4edff59bcf5668557d994bac77d508c757e382512c4368c1ded4242a41f6200b73fe8809fb528a7a0c1fc96feb", - "0x965caabe5590e2ff8c9f1048bbdda2817e7a2847e287944bfab40d94cb48389441ac42ff3a7b559760bfab42ff82e1e0", - "0xa64b7484d22c4b8047c7a8ef54dc88cb8d110c61ef28ba853821b61e87d318b2b4226f7f0d1f3cdf086a0e1666d0212c", - "0x8915ab7e41d974eef9a651b01c2521392e8899e6ab91c22aeee61605c78fb2b052399ba1d03473aa9cfb52d1a8ba4257", - "0x8dd26875d4a1716db2f75a621d01e971983267770e2da92399aecf08f74af1f7e73643ac6f0a9b610eda54e5460f70ed", - "0x83dabcb84c9cbce67e1a24ecbfa4473766b9519588b22288edbaa29aca34cefd9884f7310e7771f8f7a7cbced2e7eea0", - "0x956be00c67987fb4971afca261065a7f6fcef9fb6b1fcb1939f664bbc5b704223253ebfda48565624a68fb249742c2cf", - "0xa374824a24db1ab298bee759cee8d8260e0ac92cd1c196f896600fd57484a9f9be1912ded01203976ac4fab66c0e5091", - "0xa225f2ed0de4e06c500876e68e0c58be49535885378584a1442aae2140c38d3ca35c1bc41936a3baf8a78e7ab516f790", - "0x8e79c8de591a6c70e2ef2de35971888ab0ca6fd926fdb6e845fb4b63eb3831c5839f084201b951984f6d66a214b946b8", - "0x91babc849a9e67ab40192342c3d0d6ce58798101cb85c9bd7fc0ac4509ffc17b5ea19e58045cf1ca09ec0dee0e18c8f9", - "0x8b4897fc2aef5bbe0fa3c3015ca09fc9414fdb2315f54dbecc03b9ae3099be6c0767b636b007a804d8b248c56e670713", - "0x8f63ba42e7459ea191a8ad18de0b90b151d5acbf4751e2c790e7d8328e82c20de518132d6290ff3c23d2601f21c1558e", - "0xa1a035dc9b936587a16665ea25646d0bb2322f81960d9b6468c3234c9137f7c2b1e4f0b9dbe59e290a418007b0e7a138", - "0x81c4904c08f7bb2ac7b6d4ac4577f10dd98c318f35aac92fc31bab05eceb80a0556a7fc82614b8d95357af8a9c85a829", - "0x8c40e44e5e8e65f61e0a01f79057e1cb29966cc5074de790ea9c60454b25d7ea2b04c3e5decb9f27f02a7f3d3cb7014f", - "0xad8709e357094076eb1eb601539b7bcc37247a25fbc6ada5f74bb88b1b371917c2a733522190f076c44e9b8e2ae127fb", - "0x92d43cd82c943fd71b8700977244436c696df808c34d4633f0624700a3445f3ecc15b426c850f9fb60b9aa4708f2c7c0", - "0xb2cb8080697d1524a6dcb640b25e7255ae2e560613dbd27beaa8c5fc5c8d2524b7e6edd6db7ad0bb8a4e2e2735d4a6f7", - "0x971ca6393d9e312bfb5c33955f0325f34946d341ff7077151f0bcafd2e6cbd23e2ad62979454f107edc6a756a443e888", - "0xb6a563f42866afcee0df6c6c2961c800c851aa962d04543541a3cedeb3a6a2a608c1d8391cf405428cd40254e59138f3", - "0x986bd17bad9a8596f372a0185f7f9e0fb8de587cd078ae40f3cd1048305ba00954aff886b18d0d04640b718ea1f0d5a3", - "0xae32dbccfb7be8e9165f4e663b26f57c407f96750e0f3a5e8e27a7c0ca36bc89e925f64ddd116263be90ace4a27872c4", - "0x83725445ec8916c7c2dd46899241a03cf23568ac63ae2d34de3bce6d2db0bc1cfd00055d850b644a059fb26c62ed3585", - "0xa83f7e61c05b1c6797a36ad5ded01bf857a838147f088d33eb19a5f7652b88e55734e8e884d1d1103a50d4393dfcd7a8", - "0xaa010b4ec76260d88855347df9eaf036911d5d178302063d6fd7ecad009e353162177f92240fe5a239acd1704d188a9d", - "0xa88f4ba3cf4aff68ec1e3ded24622d4f1b9812350f6670d2909ea59928eb1d2e8d66935634d218aeac6d1a0fc6cae893", - "0xb819112b310b8372be40b2752c6f08426ef154b53ef2814ae7d67d58586d7023ffa29d6427a044a3b288e0c779866791", - "0xb5d1e728de5daf68e63b0bb1dee5275edae203e53614edeeeefff0f2f7ac4281191a33b7811de83b7f68111361ef42e1", - "0x953fb3ddc6f78045e53eaacfd83c5c769d32608b29391e05612e4e75725e54e82ad4960fbef96da8b2f35ba862968a3e", - "0x936471136fb2c1b3bb986a5207a225a8bf3b206a1a9db54dc3029e408e78c95bfb7539b67006d269c09df6354d7254ac", - "0xac353364b413cae799b13d7dc6fa09c322b47e60b9333e06499155e22d913929b92a45a0ad04ba90b29358f7b792d864", - "0xa0177419ead02ba3f0755a32eee3fd23ec81a13c01eab462f3b0af1e2dba42f81b47b2c8b1a90d8cec5a0afa371b7f11", - "0xb009eeb5db80d4244c130e6e3280af120917bb6fcebac73255c09f3f0c9da3b2aa718cd92d3d40e6b50737dbd23461aa", - "0xb8a43426c3746c1a5445535338c6a10b65474b684a2c81cd2f4b8ebecc91a57e2e0687df4a40add015cd12e351bbb3eb", - "0x94ff3698a6ac6e7df222675a00279c0ea42925dc6b748e3e74a62ea5d1e3fd70d5ab2d0c20b83704d389dd3a6063cf1a", - "0x90e4142e7ce15266144153e21b9893d3e14b3b4d980e5c87ce615ecd27efac87d86fa90354307857f75d7ebaeffe79ef", - "0xa5fd82c3f509ec9a36d72ba204a16f905e1e329f75cfd18aaa14fb00a212d21f3fac17e1a8e3bc5691ab0d07f8ec3cd0", - "0x962e6bfd75ea554f304a5fee1123e5bf2e048ccd3b401716b34c52740384579188ac98bc0d91269fc814de23f4b2dd34", - "0xb50b4e45c180badf9cd842cd769f78f963e077a9a4c016098dc19b18210580ad271ae1ba86de7760dd2e1f299c13f6a0", - "0x84cf08858d08eca6acc86158ffda3fbe920d1d5c04ac6f1fc677760e46e66599df697397373959acf319c31e47db115c", - "0xa697a38ba21caa66b7739ed0e74fe762a3da02144b67971fcad28c1132d7b83e0ac062cc71479f99e2219086d7d23374", - "0xad1f6d01dd7f0de814fe5fbb6f08c1190ff37f4a50754d7b6291fc547c0820506ea629aabacf749fec9c1bbfda22d2d0", - "0xb11fd7f8c120d8a370a223a1adc053a31bef7454b5522b848dec82de5482308fc68fdaf479875b7a4bc3fc94e1ea30eb", - "0x93ecf90ebfc190f30086bcaeee18cda972073a8469cf42a3b19f8c1ec5419dff2d6a5cc8ef412ccd9725b0f0a5f38f88", - "0x911f25aaa5260b56b3009fa5e1346a29f13a085cf8a61b36b2d851791f7bcf8456840eccbfc23797b63ecd312e2d5e12", - "0xa52f17a8b2db66c98291020b1db44ab23827e1790e418e078d1316185df6aa9f78292f43a12cd47131bd4b521d134060", - "0x9646fca10bf7401e91d9a49753c72f3ecb142f5ed13aba2c510a6c5ccb8d07b8e8d1581fc81321ad5e3996b6d81b5538", - "0xaa1da4a5665b91b62dda7f71bb19c8e3f6f49cc079d94fcd07b3604a74547e8334efa5a202822d0078158056bbda2822", - "0xa2432ae5feeaf38252c28aa491e92a68b47d5b4c6f44c1b3d7f3abc2f10b588f64a23c3357e742a0f5e4f216e7ca5827", - "0x83c7b47735cd0ef80658a387f34f259940096ebb9464c67919b278db4109fea294d09ea01a371b79b332cff6777c116d", - "0xa740a2959e86e413c62d6bdd1bc27efe9596ee363c2460535eab89ba1715e808b658bd9581b894b5d5997132b0c9c85c", - "0xb76947237fa9d71c3bece0b4f7119d7f94d2162d0ced52f2eac4de92b41da5b72ad332db9f31ebb2df1c02f400a76481", - "0xa20e1f2b7e9cc1443226d2b1a29696f627c83836116d64d2a5559d08b67e7e4efa9a849f5bb93a0dadb62450f5a9eaab", - "0xb44bff680fba52443a5b3bd25f69c5640006d544fca1d3dc11482ee8e03b4463aae59d1ec9d200aa6711ce72350580fb", - "0xa9490f5643bacec7e5adcda849ab3e7ff1f89026bf7597980b13a09887376f243158d0035e9d24fdee7cb6500e53ef29", - "0x96081010b82c04ad0bfc3605df622db27c10a91494685ef2e6e1839c218b91cbb56e043e9a25c7b18c5ddee7c6769517", - "0xa9522d59bcf887cbbbc130d8de3ff29a86df5d9343a918f5e52c65a28e4c33f6106ac4b48ecd849a33d39eeb2319d85b", - "0xaa5e0cea1a1db2283783788b4d77c09829563b75c503c154fdaa2247c9149918edac7737ef58c079e02dca7d8397b0eb", - "0x8c03f064e777d0c07c4f04c713a86bf581cc85155afe40e9065ead15139b47a50ead5c87ac032f01b142d63ff849758a", - "0xa34d672bf33def02ee7a63e6d6519676c052fa65ca91ed0fe5fdd785c231ba7af19f1e990fc33f5d1d17e75f6af270be", - "0x8680443393e8ac45a0b07c30a82ac18e67dcc8f20254bd5ede7bf99fc03e6123f2fcd64c0ca62f69d240f23acd777482", - "0xa4e00ab43d8ae5b13a6190f8ef5395ec17fbac4aa7dfa25b33e81b7e7bf63a4c28910b3a7dc9204dbc4168b08575a75e", - "0x8249259066ee5672b422c1889ab5ed620bddd1297f70b4197c40bb736afba05d513b91d3a82ee030336c311d952cd60c", - "0xa0651d8cf34fa971bde1ec037158a229e8e9ad4b5ca6c4a41adedb6d306a7772634f703dcfac36f9daf17289f33c23fb", - "0xb02ff6e8abff19969e265395ceaf465f43e7f1c3c9cfc91f1748042d9c352b284e49515a58078c877a37ff6915ee8bf4", - "0x927fb7351ac28254458a1a2ea7388e1fbd831fbc2feedb230818f73cc8c505b7ff61e150898ce1567fcb0d2c40881c7b", - "0xa9d3861f72090bc61382a81286bb71af93cdeefab9a83b3c59537ad21810104e0e054859eeafa13be10f8027b6fc33b8", - "0xa523306656730b1a31b9a370c45224b08baf45773d62952a0bf7d6c4684898ae78914cfafbd3e21406407cc39e12afdc", - "0x947a090e7703a3ea303a4a09b3ab6b6d3fda72912c9f42cc37627557028b4667f5398a6d64b9281fa2efbe16f6c61ed6", - "0xb41d24d40c10239c85d5b9bf1a3886d514a7a06b31ca982ea983e37162293350b12428eabc9f6a460473ad811e61ba40", - "0xb0bb9805724f4ca860e687985c0dc6b8f9017fe71147e5383cfbbbdcb2a42c93c7062ba42acdead9d992b6f48fc1d5ac", - "0xaec775aa97a78851893d3c5c209a91267f1daf4205bfb719c44a9ed2614d71854b95bb523cd04a7f818a4a70aa27d8fc", - "0xb53e52e32ca90b38987610585ad5b77ecd584bd22c55af7d7c9edf5fbcae9c9241b55200b51eaed0fbdb6f7be356368f", - "0xa2c5ac7822c2529f0201717b4922fb30fb037540ab222c97f0cdac341d09ccb1415e7908288fabef60177c0643ed21bf", - "0x92162fda0cbd1dafbed9419ae0837e470451403231ee086b49a21d20de2e3eed7ce64382153272b02cf099106688af70", - "0x8452d5df66682396718a76f219a9333a3559231e5f7f109a1f25c1970eb7c3408a5e32a479357f148af63b7a1d352451", - "0x831ea95d4feb520994bc4904017a557797e7ad455a431d94de03b873a57b24b127fcc9ff5b97c255c6c8d8e18c5c7e12", - "0x93d451d5e0885ccdbb113a267c31701e7c3d9e823d735dc9dfd6cfdcd82767012dc71396af53d3bedd2e0d9210acf57f", - "0xa2126f75a768dcc7ebddf2452aebf20ad790c844442b78e4027c0b511a054c27efb987550fcab877c46f2c7be4883ae0", - "0xaa4d2dcba2ccfc11a002639c30af6beb35e33745ecbab0627cf0f200fdae580e42d5a8569a9c971044405dfdafed4887", - "0xab13616069ef71d308e8bf6724e13737dc98b06a8f2d2631284429787d25d43c04b584793256ed358234e7cd9ad37d1f", - "0x9115ee0edc9f96a10edcafeb9771c74321106e7f74e48652df96e7ca5592a2f448659939291ff613dd41f42170b600ad", - "0x97b10a37243dc897ccc143da8c27e53ccc31f68220bffd344835729942bb5905ae16f71ccaed29ca189432d1c2cc09b1", - "0x875cf9c71ae29c3bde8cdcb9af5c7aca468fbb9243718f2b946e49314221a664959140c1ebc8622e4ed0ba81526302fd", - "0x86b193afbb7ff135ce5fc7eb0ee838a22e04806ceec7e02b3fb010e938fff733fc8e3a1d4b6cba970852d6307018b738", - "0xb3403a94f1483edce5d688e5ed4ab67933430ede39cd57e2cddb4b469479018757d37dd2687f7182b202967da12a6c16", - "0x83edfa0a6f77974c4047b03d7930e10251e939624afa2dcafbd35a9523c6bf684e1bb7915fc2e5b3ded3e6dc78daacf2", - "0x88ff3375fe33942e6d534f76ed0f1dfa35ae1d62c97c84e85f884da76092a83ecd08454096c83c3c67fac4cd966673d7", - "0xaf0726a2a92ee12a9411db66333c347e1a634c0ab8709cc0eab5043a2f4afac08a7ae3a15ce37f5042548c6764ae4cf6", - "0x81cfa33bb702e2f26169a006af0af0dcaa849cec2faf0f4784a06aa3c232d85a85b8123d49a1555cca7498d65e0317e4", - "0x910a16526176b6e01eb8fb2033ffbb8c9b48be6e65f4c52c582909681805b3d9e1c28e3b421be9b9829b32175b8d4d80", - "0x93d23befa411ca1adbdba726f762f2403e1cc740e44c9af3e895962e4047c2782ca7f2f9878512c37afd5a5a0abbd259", - "0x82fcf316027fedfe235905588b7651b41e703836f96cb7ac313b23b4e6c134bff39cd10b3bddb7458d418d2b9b3c471b", - "0x8febc47c5752c513c4e5573428ad0bb40e15a5e12dbfa4c1ef29453f0588f0b75c3591075fef698e5abcf4d50c818a27", - "0x83dab521d58b976dcea1576a8e2808dfaea9fa3e545902d0e0ce184d02dca8245d549134a238ab757950ad8bc11f56eb", - "0x898cfb9bf83c1c424eca817e8d0b99f5e482865070167adab0ecf04f3deeb3c71363b9f155c67b84d5e286c28238bef8", - "0xb845e388cc1a8e8b72a24d48219ac4fd7868ee5e30960f7074b27dada842aa206889122acfce9e28512038547b428225", - "0xb1ce4720e07e6eecc2a652f9edbad6bd5d787fbaff2a72a5ca33fa5a054dd3b4d5952563bc6db6d1ce1757a578bba480", - "0x8db6990dd10741cf5de36e47726d76a12ebe2235fdcb8957ab26dba9466e6707d4a795d4e12ec7400d961bd564bdee7e", - "0xa3ca7afd20e16c2a45f73fc36357763847ed0be11cb05bfd9722f92c7ba3fa708cf10d4e0ae726c3eccae23cc55fd2be", - "0x8701b085c45b36f3afb589207bbf245ef4c5c82aa967ecd0c334daa1f5a54093c5e0fcacd09be540801920f49766aa0f", - "0x84e3736727ba76191d9a6a2a3796f55bb3c3a8bbb6e41f58e892ea282c90530b53ab5490bbf1a066723399bb132160fb", - "0x87c02a01917333c7b8866f6b717b1e727b279894108f70574d1b6e9e8dc978eda8778342baf3c6464d6e0dd507163e76", - "0xb8da532dac81fafaed759e99c3ae011d75f3fda67a8c420c3b9747281fe32e31ac3c81e539940286440704c2c3e3b53e", - "0xa0cc63c3bef75a5c02942977a68a88cd3d103d829b6c0f070f64206da7e3638f10f42452788092de8fbbc626ce17b0d4", - "0xb5c9317b3f6b1d7ee6871506c0430cdf73e28b02c001ba6ca11061c7e121c91152d2b80c4f80e1d8f51ff5653bc0db5b", - "0xb798fb572da977dd3ef2dce64042b012a470d6bd2cb61a16267abe2b8399f74540d7c70462a6b2278d73567447e31994", - "0xb868eda58739effda68c834745cd2cf66a09f0f215607b65685bb5ca3eba71150f43a6e47b81a0c19fb58eeae3da56e8", - "0x9041c93a7e8f2c34812fd6e9744b154e898e1ef69db72bf36242c71e2c251f3db7e86cbd802da603a92cd0b06b62ea63", - "0xa834d648e974230582fc17b3a449f4f65b3297038a3a5401e975b9b60ff79b2006a33e1486d3428106580276993311e1", - "0xa3ce874da6ade9f0f854d7ae7651fc3ff63cec748a847527539fe0d67e6c99eaa3011065a4627c2192af7f9569f7ab57", - "0xae78ad16de150cc0400d3b6b424c608cd2b2d01a7a38ea9c4e504d8463c0af09613774dbefdd5198415b29904e0fbb63", - "0xb966db5a961067e743212d564595ef534e71dcd79b690a5a2c642d787059fc7959b9039b650372461a1f52910f7e857b", - "0x8069904f360af3edfd6cabd9b7f2adf5b61bd7feb0e9a040dc15c2a9d20052c3e5e0158f3065ec3200d19b91db603b71", - "0x9600917dbcd80a47f81c02c3aafecfcef77f031bf612a0f1a8bdef09de9656f4bb0f8e3e95f72ece1c22bd2824f145b6", - "0x834a0767b7b6199496c1faee0e3580c233cc0763e71eebc5d7c112a5a5e5bd95c0cf76a32ea5bb1b74f3cf00fbd2cfb4", - "0x99469a893579ed5da7d34ec228854c4666c58115d3cae86d4fc2d03d38f10d8c5dc8fb693763a96ab6be2045cc8d518b", - "0xa52cc0aecda6594de57d8ca13b146e77212cc55854929c03f2a8a6cdfa46296791c336aebcc2610d98612d5b4c0452df", - "0x97864434d55aa8a7aad0415d36f9558ce6e6c00452923db68a1e738232d0cb2d47e3b0b8f340c709112838adeaee4695", - "0xa4a7f2c45db3661b6af7ec759f9455ba043b0de6fd4787e3372cba215b9f7c641d5d817a0576e7aa28a46349d2fe0ae6", - "0x864e857652d95e1d168c1b9c294777fc9251a4d5b4b00a346b1f1c9c898af9a9b5ec0ac1f3a66f18a370b721dbd77b23", - "0xab8eac458fa8e7eb5539da3964ccd297a216448c3af4e4af0dcfed0ce29e877a85e29b9601dc7508a060b97a05f37e15", - "0xa6fd0782c5629c824fcd89ac80e81d95b97d8374c82010a1c69f30cef16ffc0f19e5da2d0648d2a36a636071cb4b69a7", - "0xad35a75fd8832643989d51d94ee6462d729e15f6444ffdf340dfb222af5d2b6b52e5df86082dbc7728fde7c1f28ac6b4", - "0x8e06831cc8a0c34245732ea610ea6aae6d02950299aa071a1b3df43b474e5baee815648784718b63acfd02a6655e8ea7", - "0x994ac097f913a4ce2a65236339fe523888ee43494499c5abf4ac3bce3e4b090f45d9abd750f4142a9f8f800a0115488c", - "0xa3e6a8e5e924f3a4f93e43f3f5aafb8b5831ce8169cddde7296c319d8964a0b6322a0aa69e1da1778fcc24b7de9d8b93", - "0x81a9bd04f4c6e75517de4b5e2713f746bd7f3f78a81a2d95adc87ba0e266d1f5e89c9cfb04b5159c1ff813f7968a27a4", - "0xb24de8f3a5b480981c6f29607b257ded665ecd8db73e2a69a32fcf44e926fdc7e6610598e10081cf270d2f879414b1ab", - "0xadc1b3f8ed1e7d5a26b0959ffe5afc19e235028b94cb7f364f6e57b6bf7f04742986f923fae9bf3802d163d4d0ebc519", - "0xa9fa5092b6dd0b4e1a338a06900b790abbc25e2f867b9fb319fdcdfb58600315a45a49584c614f0f9f8b844aa59dd785", - "0xb29c06b92b14215e7ef4120562893351ae8bf97cc5c3d64f4ecd0eb365b0e464cf27beec3f3ddac17ed5e725706b6343", - "0xadc0d532ba4c1c033da92ba31aa83c64054de79508d06ee335dcab5cabae204a05e427f6f8c2a556870a8230b4115fd0", - "0x9737150d439e6db2471d51e006891d9687593af4e38ee8e38bfa626abcefa768ca22d39133f865d0a25b8bbf7443d7db", - "0xa10d1e6a760f54d26c923c773b963534e5c2c0826c0a7462db2ea2c34d82890f9c58f0150db00aa2679aa0fdb1afcb08", - "0x816947dc6c08ee779e9c2229d73dbfd42c2b3b6749b98ec76dbad017f4b4d4f77b5916600b576691978287208c025d6f", - "0xa2dc52b6056219d999f07b11869c254e8b3977113fd9ba1a7f322377a5d20e16c2adf46efb7d8149e94989b3f063334a", - "0x8153900aae9cf48ebc7438b75c16f5478960ef9170e251708f0c2457967b7b31521c889b5fe843d2694a07c0e804fa48", - "0xa9e9d8d66c8774972cc1686809ce1fa5f0e16997ef2178b49bcd8654541b5b6e234cb55188f071477ba1cebcf770da45", - "0xb1fa775f9b2a9b05b4b1f0d6ad5635c7d7f4d3af8abaa01e28d32b62684f9921197ba040777711836bc78429bf339977", - "0xb1afbbd522b30e1ae2adf9a22993ab28b72a86a3d68d67b1833115e513632db075d047e21dfe442d6facc7b0a1b856bf", - "0x8779b7d22f42845a06ae31ac434e0044f5f9b4e704847fb93943e118e642a8b21265505ad9d6e418405d0cb529e00691", - "0xab2c6cef1c4e7c410e9e8deb74c84bedeb3c454ae98e3bc228eb13f6b7081b57977b3e849ba66346250e37c86842c10c", - "0x908d6c781d7d96aa2048c83e865896c720a66fdec7b06ab4b172192fe82f9ff6167815ffb66549d72bfb540bb35c36c6", - "0xb790440f205ece489e2703d5d1d01ba8921dd237c8814afb5cb521515ed4c3b0a6df45fd4bd65ba63592c2fe1d008df3", - "0xaec346251f9c78336b388c4e9069a1c6c3afbbb6bfaffdad050a9e70e92fb3cae3609067b4903552936f904c804b0ea6", - "0xa0e528cc2cb84b04cc91b4084e53ead4188682a6050b3857c34280899c8233aa8c1a9c6fa4fd6a7087acf1b36d67734a", - "0xaa8d7632be3e4340712a1461a0ad0ae90ba6d76e2916511c263f484c6c426939fa93ffbb702cd0341eea404d6ddffebb", - "0xa4ea871d8a1d4b925d890aefb9897847599b92e15ce14886b27ce5c879daa9edead26e02ccc33fcf37f40ff0783d4d9e", - "0xab63e4dc0dbdaf2ada03b3733aafe17e719d028b30dc9a7e5783c80933a39935dbe1ef0320bb03f9564cafdf7a4b029b", - "0x8219761bbaa39b96b835f9c2b4cec0bf53801f8e4f4a4498d19638be2fa0a193b2c1fbf94e26c1058d90a9ac145a7a12", - "0xa609ee5561828b0f634640c68a98da47cb872b714df7302ef6b24d253211e770acd0aa888802cd378e7fa036d829cd36", - "0x90793ff0736f3c80b5e0c5098b56cda8b0b2bca5032bb153d7b3aa3def277f2fc6cea60ac03edc82e3a9d06aff7d1c56", - "0x8760085283a479d15a72429971a0a5b885609fd61787a40adb3d3d7c139b97497aa6bcb11b08979e2354f1bc4dbf7a0d", - "0xb168ede8b9a528c60666057f746530fc52327546872dd03c8903f827d02c8313e58c38791fb46e154d4247ea4b859473", - "0x842c1149ca212736ebe7b6b2cb9a7c3b81ae893393c20a2f1a8c8bfef16d0a473ff865a1c130d90cc3626045f9088100", - "0xb41d0e2c7d55108a8526aa0b951a5c8d7e3734e22fe0a6a2dd25361a5d6dea45c4ab4a71440b582a2f9337940238fe20", - "0x8380bd49677e61123506dd482cdf76a8f1877ea54ed023d1deabfc05846103cfd213de2aef331cdf1baf69cfc6767be9", - "0xa026f92030666b723d937f507e5a40e3f3cfd414ad4b2712db0a7a245a31a46002504974ed8ba9d8e714f37353926a4e", - "0xb492e9e9917b29eb04cde0b012df15cbd04f3963d120b63c55dc4369e04f5ac7682b2c7dff8c03410936c26ca73ad34c", - "0x81fd9271b4ee36be0ba8f560d191e1b6616dd53c56d1d8deac8c1be7bc67bbc53d434cf70d04e7fa9de3e63415389693", - "0x835c3711abe85683d2344a3ee5f70e68342fd1aec025ad248efe66aab3e3d5790fad2f45bae0d7a53a80998fde45f0aa", - "0xb46599be80b8f7dbad0b17808dd5ca91d787929c0bef96fbbcf6c767727d07ed6785bad164d733ecb015eb6c8469a16d", - "0xb36bf5c17271d39f5ccb3d82a5e002957207a0cdf9ae7108a4946e6f3ed21a5d353fa940b6fe949c39422b452339bae9", - "0xa12f5444e602d6fb8be51a08b8bc4ec105dfd759d2afe98d51ff4edd673c92e4fc91ff32417ae8070e12169004f8aad3", - "0x892ce3ca0a2961a01f7f0149b8a98fdc0f8871c2d85e76daf7c8aed2a18624b978a4d0a84213f81f9d2a81f7ca4826d0", - "0xb1e6229ebd5b3d85e62d0474d1fed34564f1b5b9c5856fae36164dd0eff378d67d6717dda77536379006fb462bced9da", - "0xac852921dcb81e54e1e315fd6226219932f7b785c2ceb2035710e814899784d7001101f1515d68e3fb74cdbb4baf9e26", - "0x989a42d851123d708a213f3a02cfc926df15af058ec9b5a9df968fe16decbd781b5e65a4c17fbfedd2ac17126084700f", - "0xb1d0fc2f7c948e466445f307da7b64b3070057c79c07c7ebbbe6f8ed300a642b3567aed2e5f28988ac566ba62e0d2a79", - "0x83057263b41775bc29f1d59868a05b0f76d3bdf8a13c1014496feb4c0ee379bfd0d4079785252f51fbeb641e47a89b69", - "0xac9e6a208aa9c557155cf82b389bb4227db5ac4b22a0c7c8d1c3d98946df8b82b0c49d093ba55c8255e024a6d67c14b4", - "0x8294a11cd3f5111b1f8bd135be23b4de337ac45711db9566ebf6e162cd58e7859b1309eba8149b0f0a43e07f62a92411", - "0x8c15f3388b196603c05adec195c1d2cc589e3466da3169e9afd37157fa55cd34bfafbfc5ff10ac0e04aa6a0d0b2ce3db", - "0xb8faf8ba89c3115576ab6b340f6cc09edfea8f7331f5a5e8003960c584e839fcecf401113dfbb9a5c11a13721b35c263", - "0x955c63b1166514c02847402d0e92dccfe3c0dee3bc70d2375669afb061594c85651e6569f471a6969759e5f373277da4", - "0x963bd4f9ae7361d6936d209592a07d9a22cc9ef330cf0c5cb845cb4085d76e114aee66d7599bf5b9f11c6b1c05dade8d", - "0x85509b3c97e06e0db113b8b40022c8989a305cec39acab36ba3a73a4b4719573e5bdb82dc4795699c26d983465cd61b0", - "0xb870cfd7f691f88db8d1dfbe809b7b402eabd3b3299606b7dfdb7ef49415411f01d2a7e4f7ebd919ac82c7094f628166", - "0xa5533e7b58a6a9e5c25589134f501584163551247d36f50666eeb0a0745cf33e65bb8f7a9c2dc7fe7cb392414f1ece4a", - "0xb93d1ade01ff5678fcd5b5b4f06a32b706213748076cae3a375e20a97231133ec37c1c3202cbc4896b66c3410210f446", - "0x86ed3a58000a46fe2c37d4de515430a57d8f54ab4300294685534372fed1d68e192dd43d43ea190accf3dc9b22e1548b", - "0xa8c7d8dc30057bb8ad66b9cfda5e223334407730aeb0f51705922c18e7a07d960c470d463d1781899203e1b1ed1df484", - "0x8d86821d006e957e8544f95a98b110c89941bcc6985562e7a97285f5826b35b690963b2c141ff3f389d92ee18ec76d24", - "0xa4e1108cd3cf01810e74dbbf94340487011b80013b9bfdc04f019188c0d4d077a54b71a3f97a036601aad42a268531e8", - "0xa822cd61db07f64bea00de226102f5fc0adf8fa9f05a6c7478b0ff93e48f6cc3191302d22e1f369b571877d5eb96139c", - "0xb1ad4094d0bb4c325dfe072b17711962247dd7ff7e4bce4612e80a6f3c1bde04880ba1682f60d5f1451318afd4d3ba60", - "0x88e7beb0cfd7361288ea27f6b2cb18870e621152ff47994440c18d45284d21bad80d9806ed7d9d392a5cd791d5150ce2", - "0xaad3724a176cf4476595cdfb9e2c3261c37052324c0b5373a30b6cbeb481bccd303720840c49a84ddca916d470eb6929", - "0xa57983370d159e7078a273746fb22468000a6448b1a31d277272e35c6f548f97928e9015f1daf577511bd9cfee165237", - "0xa54136e9db381cdd6dfb3fff8bdec427d4dc1072f914f6fecfec13d7b8f95bb3b5f30ad7677288c008ce134edfb039a7", - "0xa25dfc4019f165db552f769f9c8e94fa7dbbf5c54a9b7cde76629cc08808c1039ecbd916560c2b6307696dd9db87d030", - "0xa917d25328b0754d70f36e795fe928e71ae77e93166c5e4788716c1ef431115c966f2aad0ce016f4bacc2649f7466647", - "0x842ce5e4ad7d8d4b8c58430e97ff40a9fce1f1c65ecba75fed2e215e101d1b2d7ab32c18df38dab722c329ab724e8866", - "0xa8eb2ed2986ff937a26a72699eb3b87ef88119179719ff1335f53094c690020123f27e44fc6b09f7a3874bf739b97629", - "0x96753c1f9c226f626122dad6981e9810a3cf3bbee15cfc88e617cfd42753e34593610861be147a7b8966bcdec55bba8d", - "0x94119d31606098f5b129931b51b4b42c4e3513a128b9bfb03cfeee78b77b9909b1c2fcf0a292e49d63bc4e5fe823dfef", - "0xa869654f5880d9c21a0af1ff4cfa926e03ec1f2d80fe5524605e04f484e09dc80d6769249f31fd378ff3926ab4cebc69", - "0xb2a539bdd8de4499c5f35cd8824974c2abb1933b3f50d0175dd044563ca829eaa0fc47bdac97eafa98434d1cd05d7c5d", - "0x85f53b2bfcde1986ce7279f3a2f5f841f87d75af5d197c897f261d4874bc6868c575ecf7556a32b7b33f7b2795454591", - "0x964f087ed02228b30f401d8aea35c1a7f76698e4075e1bb343398be74c716884e9ca1a31b81566e1ff7513cf76a2f0cd", - "0xa1c9d9c9bfbc9c4e281a2953d5991e7b22ff1a32ddaace9e8d9a42e080efb802b853d3276973b5189a5745943c9b4389", - "0xb0c45a9852663a427d7f50c608a6419fbd00f90e8452757a45269d25c0386ec29942f48a34aafc0187ef6020e581d290", - "0xaa3ca7b01862d5d2aea714fa06724b7dda7062b6608605cb712588b2c49fc3c7d89a8799e6e7c31e7a9ef28b1ad4d1f7", - "0x88f5e98ae8c5ae7add42f6d358a35667e590aa80e1869593cbf597d7ee466efa35b429f1836ba2199d8280fe7f60ce3a", - "0x8a3bff472e8008f7e50362acc1a0b53c09ac60430942544532722e938470376f0672662261992146765b7c75a380c318", - "0xb9847be7f7aee7532282c279dde928698a892a183ca3047ceda521e9e0a50d96fd3ce59f8e58f31af49508ade6d4ba51", - "0x98065dc23ea3df6d9f8459e81887d88d5752b7e7ba6050ec5c3f0dce93e463e0bf12be3c94ec74c16e2f7ba62e447845", - "0x994aff677b97ee790894dbdb21b1f9210734e008cee2aa2200c8f2579ea650b872f39776a13a8c31e95cc817091bae1c", - "0xb292811674e18912ebe79df1af4a132b04ab702c125c039e0213f735f658fafd36c38e5bbd7cad35842576431f5f3630", - "0x96520d750ec10bb10f75019f8f0e4a93ecbc6b678a710d76cd10aa27a6642ad1461bd58fc2aab8e0391b3f788339ed29", - "0x80d478da7fe246ad0e81a00141229e9d91ffb7fd1b29975c8ec358ed5e864e481bf01b927a9ba002c5ec4aa226d0cb57", - "0xae58049d93a11ae845dc5be2505e95657f83b95d83ff3591a3c565d587157be795ff4481f42d59eda95e6d523444e199", - "0x85f1f5ad988b9f8a7e24b6d6a22b9de9fb3fe408f95711389c444d7ba2243987225b04318aa97a4cde2cb4c30c05508f", - "0x922092d0cb828e764ce62f86cbc55c04dce07233cff041888fae48cfe93818780b4aec9b4ff4718275bb2bfa6bd9e9ba", - "0xa85ba97125feff0590a05fb78f19a7338639ad1748802918af4d59307bc994536c0ad638b97b9acd26a08b6b4370dfbf", - "0x8c46fcaa8d13266d650bd9366180e5ebbfa002c339e4424a030de19ed922e2daa9a353ae54921a42299607ae53feb075", - "0xb8549832230eb1ec6ee3c33c078deb47f556a0907d2a85fde7720391c82d2ed63dd753cf544a6a0a46eed4b8d1ecd9b8", - "0xb7b96f24504c7f8fbed9c1c654a2550feeee068407b809c43f1082c9558c8665806d911d5d244308169d8a531373bf56", - "0x81c483fd9d9ad7af7869d617ac592e7e951e39738da041d8c4110637689108eb29c8acadfc85366c70885cdf77b353c3", - "0xacf33bcfd9080dfdba828727fe36803327a94e8a3ee5b6e445274f0e8267ad3c943994a8dd6d09b8072912b57e1e25b8", - "0xb3475e7456ff96861bc11068198d51b69b899f5ff13022694b501d3adc8bac58a16204b12011d61e880c8459f4badbbb", - "0x8ceb9562026aa96d6e786ec2e5cd49200b5b424349a2214cd3ff5c8f1c2bf1b9872480428f5428e45cc61106cbfbd953", - "0xaf56f7e482c24a1367fd798201a20c464848ece431f2d8a31a6ef4f9bdbaa50991e748dcb4ef0c08fdac0ef8ddda3b80", - "0x896dae8b12549909d512fd5c02a2f72dde4086aef6c8007ddb26bb04dff51a707ae94ff87e45191fc10339967fa28958", - "0x8ed1c606840e07a2ac6ff16ac6e81ed3e1c90872ababfe68d56ed2dc50d9294579b9c3546dc63292874299a3162d59f9", - "0xb4d7a5c0836e419a46942281ce77d0aade8e39eb1bf1190dd274ca5070898a1c02ad9d165855629d6e1c96df1a6bd5f3", - "0xaebad8939ac117deb28b789d9846c2c80359dc260920ac8408dbae0b6228dbf496dac0023a3b4302bb9a53e8ada18e61", - "0x812d07c74a8650dc3f318c9b2dbf265f181041fb432fee989cedabd44b933dc6590e36c71dcf9dbe7b4bbf74ea0d7c50", - "0x87b131dd3489889e090839c392231e0ee198acac65bb2e9e63e7d6da322391d1685cfc8ba60699308054c4b0fd89c90c", - "0x8b12110ece0b99b2e653b4bc840a12bce5b85abf6fb953a2b23483b15b732a0068824f25fcaa100900e742886c7b4a0d", - "0x8765fc9b526a98512e5264c877bdca567e32fdcde95cdbcf4f4c88ce8501e1c7fab755f80b87b9b32d86d18856f1d005", - "0xac806a32a14019337dfdb5f781ecba5cdea8fb69b23e0e57a0f885e0082a9c330ba808621a48e24316604f6c6c550991", - "0xa711970fa40cf067c73e3edee9a111bf00cd927112205e9d36a21897529be9a051be45c336d6b56725dca3aeea0aed15", - "0x908adbc17fc18821f217d46c25656de811d4473779a41eacd70d2a0d7dd3010de4268a562378814e619e13ac594bb0c3", - "0x894251b79be5ae763f44853f6999289b3a9abda64d52797c6c7d6d31ff2a79e9b3906da72f9ebb95b61d6b29479e076f", - "0xaadcf11ea15bcb6d979c3ea320cff8dfcc23c5118ed075f35e77f71459b2141253060e3a90839adbcd3d040ad3bdc5e2", - "0xb4e55d7d2eeaaffb0267448ecce0b75166e4805dc0e261eb5634d4a3f3c08964a597302fd8f6b45ec48178619291dadc", - "0xa8e2a02c93d6bec7f42f9265269660b4b404940c3e3de9515b4d826ea7e71f18c6f90a71ce3fbe452d0713de73cb391e", - "0x8e2467accfe207cb1ba37d60662920f95338ee212927edb706228c25345734217740159310edf17687f58b333754cb65", - "0x90376b88f653381b3bab673c48c2b84fa82a091e18f710a732fef836e0d39043fcd5527aa97a3a385c0a77cf53746993", - "0xb16530e289198c235ab680f86851bcc177f0c16a58483d83a89213077b06d6840600b03834b6b7af0e22b1914f72de43", - "0x8c4fc3854f938ef1c2b5df065e4e75e9f299798afae8205706439491bdf9784c756134922e77af007e349a790afa52b7", - "0xa68aaec4341d29b92b35322f89b1ae3612e7b440c89a86135a07c261dc5799217a651460c92113d099b486817226d8cd", - "0xa653f965feefd2df24156478f0cf3755274ca395afb79e8c72d3b6e1d1f5ba7f3e4f9a4c5ee85355de6f3c81935ff579", - "0xaaf6c8d2717b57f6b14e06c742a11a3bc736bfc0327ca4b8a005b6e924f06871141d231737698a9a59286e44f244a168", - "0x8de32e3c104b4278e27aac695d224f134001c3619f15186466c57c0c46f67e2efe537501d0d9f52f4cdbc724a170b92d", - "0x8e9b5858b6d4ffe811f6498bd80e454f0d6b345d4729c946626c7cdc196c803a349a14515296aadb7258bb7a5b37e930", - "0x82fc711043aaf1d7a9c712d00eafd816a710f82eb10818ba6af09f591447f36814dbff6e6a1cb2b5c7f16c73930dbbca", - "0xb2f0205327fc8ff687f751e7b97788732afaef4fcf51bb17fd7579ed07501915790b70fc36624371fe4fb87a0179d850", - "0xadd87d5b1288d30f3449d3ccfa11cba4dc7756d85cee1cb6171b493680a625a01f273d0bb8e6332d0410250036b3acdd", - "0xa411f75ef7dd8de8062331ea40929db989e4d65ae8f33d3fa6cc19c98fa8a8ec2b7c7534a5c5eee9e5051626a6a2e47c", - "0x89d40a647781e7f2e8ab3a0f7dc7133669944c0cf627376433687a2ea15c137be26f582a6b07ff94b266ac0910009f7c", - "0xb2b5f808c26b40ed507922ed119b0fb95e0d6d8b084bbbba58ca456b4354d03110c99989b93207998334ea5d1b70fe49", - "0x8c8db028671969a1e80e595283ce5e678ee955d785043bb5fd39fdb68a00e4c15b462600a7ab1f41486b6883e725894e", - "0x958087ce0c75fe77b71770c2f645ef3360c1a9c98637693b988c5f6ce731f72b24ab8b734e8eb6258ee8b23914451f0d", - "0xaad6c00df131c1eec6c556bae642e6dcc031e70f63eee18682f711c7b2fcd9afbf1f18cf8a4af562759130add67bd4a3", - "0xb6d23c567291f019cd9008e727704e7e6679b274feb29abba0d92e036f349b1f0fa8c5271ec7384e8d70a2c3977b1f8a", - "0xa942c770e903d4150b5684e4b94bb72d0e171df2c7cae6f46e002c41c6b04d774ac6e2753ba8dccdbba3ad1e297a9ae5", - "0xaa542d1849390f86d797408ed7f6a31504aa65d583481a00e475028af20f8b69248a87a8ffab1dace0377db77fe5f9b2", - "0xa1ed3f9564a97f7cabe7c67e018eaeaa42db73a2f3d2332041ca9a7bea57436d848784d6dc402862c22a47f0692b1286", - "0x925c757750c91db8b1b3c220fcbdd80742b4a060abfb0a402071d215c780ef6b420132ec5a43043b9fd7a06bf1b323db", - "0x94e575daa7fa0bbb35b4386f510fc3877c9df57bcf15349c5923f30ad6a8df95372835cc078216b41a7192921c1e8973", - "0x9346a41174865d9ab31c7fb9a5329f322bfce06002386d3f5a2e2193de9bfff12bd0bd93307928f7b85e1097b2aaddff", - "0xa6e54c9324baa1bff7e9bf39c94fdd308ec6f210aad937112ec727565f8a6141375c04196831873bf506294854f6a20e", - "0x98d47b662504f400f1a0e14e24b43829490d022ade02a56288aaf148d466b45d89b5fc146cef67c9ba548cd37ad5e354", - "0xab690dd59a69904b6b3a4d5a42d17ea4898d9b00c6753aec216d5d4ea564f9a1642697df44d5a62f2c2ab19aaabf1532", - "0x8d0aa8d3c5ec944af49beb99e403cc0d6d1adc6003b960075358a4ff1cbfa02a83d6cb4d848d9e83b34882446a330883", - "0xaf9334b7300780c752f32eaa68f3dcecd07dc50d265083f37f9800b02c2595ba24dab89f5fc27c1ecfdbf5291b4d77bc", - "0x81c4a6aaf7d4ccee9925c512dae5da6d916a6dd59f7a4cc79d216a91201b4d300114a309e3ddb3291bb95f85bec2a8ea", - "0x8c804e810c0785789de26e12b1beff56a163769733be7a31f34f81093782d6410293768a166c9191ef8636fc8724a31e", - "0xa91222b48de238f6dfe79c84080cee618611bd0bdca15cfe44474829e42481f8511a82589e69964e19f8cba04e3f5f3f", - "0xb26a8885aa594b0c8ad4a1711d80bcf687df996442075dd1497db1b446d16c74e28bc6f0e92b2ecea9c3e15c9c7e828a", - "0x85940f45d324ad1d335bd1d7d6f81758f52213e63d5770d9fe0c0c9507d5550795e538b6a2dd463f73d789b5ce377aed", - "0x931a277c78082f416880620df3aeb6d0bff2103d19679dd092ea981f5323e438c50a0d094908034ff8a2cb47b1a44108", - "0x88dd85e4e2aa349a757b98661fc00d4538ec1d3f53daf44b16ffcf7f943dd4f2bba5b8ba3b05c529251dfeed73f6f1e9", - "0xb7fd7182cd33639710b8216c54a11bb02e199bbc54fe33492a809dbe17771a685d6238ea3ebcfc75e3b0d4ea5369bc9f", - "0x85d77194d910f8cdad7330e1bca9087529a40fece17492f1d17cc4790833891b6d01d24f036b6422175c732b438faeb5", - "0x9845265892d672d9517fbd22f88be4f225711b4abafa8327cc059f000656e4737188506051565d97912a0c19c3d063c0", - "0x90a81987aa841c7f640c298b816643a0ae00cd3609c3a31d0b01245283cc785d9bb27763131b31a4f21aeda4e50073e8", - "0x8b1256eb41a600bda8a06ac08b98a220ebfd52f89a0e4fdce32425db7a0481e9b7873ba3b7a24ad9fb782ee217dfdbf6", - "0x870548998deed85c59507cec7e69cc001c279bb2a99c45a4d030a35c107e69feb76afecb9e435e67965051d6d7a88220", - "0xb1504d194a0dd8df48d431ce991f89d7a0f72f573d21bd5bb46474c5005e43820877a44e62db555f194427ac8a4b9168", - "0xa00d7423ec2cf0c9e9da07f3dae092d09e1ff4be852e07e531aa54d62ad937bfb52c8bf44683ac3a70f6dfc125575da1", - "0x8019625ad3d218018803aacc2efcedba3a41c24aca8c5aab2005556e58fdf2ed614831277df7937aa594e97a2fc65e7d", - "0x8595596284f3add0155ecfee3fc0b66a6b6fc7923d82ca8302952e2ed906d119a1c053aed1123b51f73e1d30d93aba57", - "0xa8ba033f5e7d06177e9ae2d99c40ed4e99e14e1c1b61795997f62e21ed8af1531c4720f23d6a39b0f75c6cd91c58c700", - "0xa94f4167c0f6ae214bae75dd92c63299dd954b00b0d8b0416b8af929fe5aec6a259e44f83a183412d7ba4eb3a49728c0", - "0xa73ee3c3a0fd2a369e0a279c3e214fb662d0378eea3c95cfb91412d7213a1f05958bd0de8f2a4f80f9f80d7eef943b41", - "0x8ef6f3e241f6a761c9ab412629a49648c08b70b837c2cd8bea620bc93056ec73754e3e11f0df50f8e9fa67a9867501a9", - "0x80b473ac4ba8cb82b4ae684206cde124d10fcf619f55a6c90d035981e1b08b9e141b4e5fa9a9af0b7f0c281b355dd593", - "0xa566e2be0b41f01978dfffbb32f442b5e6706f5b9901110e645cf390f6a82869e3ca16887ffa35782a004d251d29c26e", - "0xa74e01eefa03546d00afdd24bf17015eee95d36de28c03c9b055e062cd5e8d8f20473c6d7ad21c94f9058fc5e84f9628", - "0xacefc74de146911275dfd19bbe43d72729e89e96da04aff58e5fcb90962856c0b24eb13f43e30329f5477a1b65ae9400", - "0xb5f113ef36e75de6d6d44130f38e460ad3ffc65cb9a5606828c4f7617981fecf76f5e862d7626ccb117aa757cc3c3e52", - "0x96d3aeb1d3a66b136244062b891fc7f93ce745b776478d361a375ae57bdba9b4fcb257becbae228c1a3aff4a1c4fb5e2", - "0xab26c4a110877e5495b674569a32025dad599637b5dafedcfe32f205dfa68cd46f3ddf4f132a8e5765883b5c83214a07", - "0x922a7a738066692193af32ccbab74edef067668ce3253e18a3275afcd5a6df7168deb2f5175c5fb413dc08fdaef63b17", - "0xa47542f8e4a3a35ef6049280d1a9442c920887d5f1a1483149e143ca412318495a36decb804f81c9f5a7672a14965a4c", - "0x8fde57991e72a2aebd3376b4d9fdd795943ba3833431e52b136683567e6ee2cc1c1847dc49dc9534983060c54bf22f7e", - "0xaddb041f01a99e7238ab2f9f2f94579861d0470b93b91cfb29f3a2e4c82386c868b2cfb6f3778b8a9cf908788acafe58", - "0xa8c4e1df726431c43703739776e2cc51f5ebac57051244991baf53582538120133a44ca603d0722a4b5193e1be3c5ec0", - "0x846379125968d1154376c5dc63100bdcd99b9403d182e3566fe48d79099099f51523cd81d21f0d1dcd622b715bdd851a", - "0xb828bf0d936d275abb40e3d73ef57fcd7ce97e9af35e194ae61463317bac6c1b0c3e4b40afe08a1061037bb7149108fc", - "0xabd07c71754973e698fa26c5019afd9551548f8369e2249b9902513f19a097057ee7065a1d88912e8f52e6e0fbfa6d82", - "0xa9e36b6fcc9a3cc98e76d5751c76c50e1f92b7670f8076ab6ca8a30de4ec14c34669e049fd39bd293cde8789b1ca67f0", - "0x8c060835496a04c7b51790790035862b20547e62fa8bb4e8857fb36891ec6309520af5c0f45d5ea46e3d228747d710a4", - "0x8cc472ec62b8dce244373f40a821db585628989b6a7c4d394edffbc6346c8be455f4528d528fff41f91f2c875bd9fc0f", - "0xb4a75571f84f93451f15b3a86479063d7324d2789b6d2f2f4f8af68c66fac32743dc09b51df29608d62aaba78f6904af", - "0x916484984743b5ac16d40d0544faf9184819d92f779254b7fb892eb68cefbe59e75be8a6336a585e120f6ccae0a1eeac", - "0xb906ae585a73119764024e9eb87d92e53ee0c673474fec43fec4d344a3bbf471ce3976d25e37d197604689bbc944f1ab", - "0x8552708487305f16f95db3e01fbbfb969398f5b6d116844cbb000c9befd03f15c767584bf9541a42141949a4dc787a3a", - "0xa6025a2773f78c247f78c0d895ade8a6baa76e5499085f6175935d98a05fc41c1359f7843e0c6c323f1be256c45f45e6", - "0x96dac695dd9288aeb6e32dce50e51ddf1fbd41de6146e3605c7a81f2253b17babf2bfda4f5a9d0c28352b9746c0dfa2c", - "0xa215b21f8eb2290f9d308278f2859a999eb3a31f4888f84a65f9ed05e1151c17777f91054d4d0de759ac5c3547d91929", - "0x8fd7c9a279e9b619acf927d501b35dc551979731a89eab91d38b2356c0d73569baddacb9d1096d20a75c917ecaedadd6", - "0xb985e8baa5195e2f1ea1091122d55aa321178d597f87b732b23eccb12b891638be1a992305a1ffcf5233af34339fa02c", - "0xae1a9604b7f569aa48d2daa1889e76d3d103065fc8c3deb9ae127a6d94145695cab3bef640fa781612e8082c6d616c47", - "0xa8fc67f9069f753360349eb874fa4dcadb2ec48d97c61abe568faee5f370ec3c87786c7faf0f73fc0ae7181a36eb89ca", - "0xa506d13acc3a9f80509fac936aef848cd30698631fff6130ed6217512ed9527d075f653cf6ef91f68e48a24c903eeb3a", - "0xa415093755cc012863043bf586b970bafdd87653ad14d1929672e04949bae4a753d16aa3eb5bd1afe3df3691b80f240f", - "0xace3b792a1960580348b6fae8513149242378a18382741bbc2fb2f785cb8bf87550da4b5e0df2955970ab3a31f99f5d7", - "0xa47d7fa7522664c8f9c404c18102f6f13a1db33ba8b0a56faa31a78a3decba3168c68f410115c5d9f240b3dc046dc9b4", - "0xa9c930db3ea948cd2dd6ea9d0f9a465a5018bbaf6e9958013f151f89a3040cc03ae0b8eaf74b0ff96b4e7a6cd8aa5b4f", - "0x88abd235e3e760166cdedff4be82cf6ba02d68f51c6d53b1de326769f1f635215890f9a4c35b06dd16a9b93f30f3a471", - "0x8f8d7b2fcdb70bfedde1ffd7f0b94108f0fa432f6ae81097988521dd2c4da928c10c5da3c7f33f11bd5331f2da8ec219", - "0xb7abdbd48cece30d8f795a58a94913d76842cb006892485a9382a0502826538ca4ff951cc1ef4493e45de8571360d20d", - "0xb3e7b125f350c52695f7c5ec4a30916ea6c11744f1151a18ea0510e6cf6ed6f6dba4beaa4ca56988d306bd80ec360056", - "0x9a004423c95e1f1714f98fb97ab798d6ab16cb5f6d6cad860635585d4d4b43ffcda63d8e931351189275e5a2cef28c2f", - "0xa8eab6ef917cacdc9b1932eb312309e1f85298d63e55ed9c89ab79da99d3eb60f1643d16be920e82d9285f60c7f7cab3", - "0x934df955485113d10c4dde476ec14a98771145aadf3c8b61af26b09b9948757fa1abcc945ac91466a18c18c2fdce40d0", - "0x99ed9146561597cff8add2196ff3a0f161dd5302685ceb846afca6efb5225f642e8f4a0970eecb01cdf18694fa697095", - "0xb37062dd12a81267bbbf89bc9d6e30784c0e11e713cc49c6c96440f800f2a6a2a7e7f6c7f6c9eed4bc3c8890f2787342", - "0x83a3d70055b6044e0207b3ece4da849755ab5798317b36b20c3555a392c27982f811e1c5007697554eeedc737b37f3ef", - "0xa85392c07ff8658935fbc52acec7221cd916c5fde8537a8444eefd507220e76f600350ae8f5dc3353911087b88b91045", - "0xb1ea23558ad805dde9cc1eade995cd8e7f46d9afa230908b5fbaaa09f48547f49c2bd277bff8ab176f1c240beedd2b09", - "0x8a16a48b9105d94700e8e5706b8d8a1ed14cffda5558a596974ea3191c5c3449da6e7efe2059e7baf4530a15f175ce16", - "0xac5fa54381fc565842417558e131df26e9505027759416165035357816a7e1859a7c14c228c79b4e5ba2ef6758e12ad8", - "0x8475e290c399cc9322c05264a516cf766bf5fdb6b9dec7283961da0b99012d499b244b33fc0eaf94b461ab777f2a9537", - "0xa7922f3c70e6857652805af7d435646c66d94eec174be997c4fe973d8f019990c4f757eeb730b2cfdf8154e6e97f7d5b", - "0xb90deb797fba3150cf265a23ea6bd49a382855cd4efe171cbcb1664683a9f1687cfcadfdca4e39cd971ec13aa5cdc296", - "0x91ca761dd9659007d2fe8970bbd336c19ed0d2845d0d8aaab397116affcc793de2da73d89e6625cf4dae5983cceffa56", - "0x9121ae9b60323ab1301e97555bcc74ddba0f5b1e62bfe9eaa2c239e1d685c4a614d397b32a59febed4db9968db44f38a", - "0x8477b07da4bbfe9087975f30d2c2333fccfcd7149f90e0e6fabecee627eee3ea324df31cf6a680393f5dedf68a35c9de", - "0x946a9c0f02fa6bf9f9d4933e7fc691749f4ac2f82a9b880666b5185189d4f3432da9096d0ea4d6baacbc079e19c887ce", - "0xb24663332914ea519435874d4c42d11842ea84dd3dc55292d5b0f27f64587848d095bacaec235a37003bdb5185daa6f2", - "0xb980f46f84ac21dea75b4650f9412f6123325842758589a9b47caa68545905061f03fcad23cc102e2ce8ffeb1ae634a8", - "0x90e9ebb060182d3043ea4210a2d934858559522a19eab9f0ff81a367484a05ec7cce78ee6a91dfff96145869db6a4e80", - "0xb04228a009c91847693eab29c9ea71d1d6ba07060bc2b0b3bb81c46a125baecb3e1412f6ce4305076a97d316d14e4665", - "0x8d3268370dbf38d378c7228c7b54e91f90f43cbfddc0d8468de11a4312616ca6372619209b89114152b16f334f4d2780", - "0x964a63ffae653e0249685e227d937937b079ec3da9c977dad2b2e052af5eb560ce7d175941f2ae0df90e3d0a20b77e75", - "0x855604c2910be885b14b27896e16d8dc339236b975398c771d29ac74e4278a2305fcf85203050a8faffddf64ea19cf78", - "0x8e0b1d61a4349411eec77cf3490555843187a25a93e1f45bf66ad3982b9cc141b07805f8cb252b0fcc125e0052a7c450", - "0xa03bc9588f971a1257cd0cfd2ca406c76aaeb634001864b0e4dda91e009d3361b33fc39f34922835031a423a13619a82", - "0xb703fa855c2c4e1641d2687717fe8c5061acab71cd2dab55cdb069a6865464c3080f7936ddfd320516b6791b36c64b8c", - "0xaad1cfa7295e463fc3d5374ea4b952020010d67a77c7a86fe2c351a5959cd50df6a0045ad588257567a99bfd0e9400b3", - "0x97906fb82abf5c1d9be8f72add8e6f175a6a5a4300b40295cb5ec8527cc7ec700fa03a7a494122d9605d212457452e41", - "0xa83366cf93ad9a07f617e4002a10b624270f60083559b045ab5a805aaa592ac37b90c1e8b5437158f3bd942cf33bb633", - "0xa585168e157e111bfa329d0ed6651a96509b20b30f6bb0691c6a5875d134d4a284867ab52511cdc19e360d10638e58a1", - "0xb17d480a0b39f2487b7f3878714658fda82f2147c5ecbccd4004eb92d267c4663b42c93bafb95ce24e2f2f0a9ea14b8f", - "0x9362297a1a3951d92db4fd8ea6b48c403d6d8d2f7e7b6310b9cf9b4e4ba9e84cfe1ae025830aab9466c32fd659144474", - "0xb1a62fbadfd4ea4909d8d0714c1e3ee9f95237fde20720f88d5ad25c274a6792158b99966d7b93151f769c832b6a132b", - "0x8d9af736949a33fe929548abe72384281365385862821a584f5198eed63bc5388f89fc574cda35a9eaabed0d336b86b6", - "0x90ee2235f4ec2c6089b5cb7b8a41c9bc39e4a57935022ef28bed490e2ab12680922af7395bda4f708809e2bfc62192c9", - "0x91f3a123d420bca34d3d751119bbebc435630c6605fb59a8d80d16a4895972e56cfe4cf1998e0a527c18ee38c2796617", - "0xa2c4fbb20e7fbaae103b86ca9d8dbc2828e6bf33d1d7ce153bd98e8880fe7ac62abbf7059194b1eee64f4526a36c63a9", - "0x91a7f93310ac74f385f11509f4bea9a4d74f2ce91cf2024fee32a4a44d5e636a73339c6b4027ee4d014a24b90de41ecb", - "0x914a6d405fee0a15e99704efb93fd240105572335f418d95e1f2de9afeb97f5f4b80aaf20bd5bf150b9da9abc2b6d6a5", - "0x9462cf2c7e57e224389269b9fdddc593b31e1b72ab5389346aa9759fad5d218039a4a5bc496f4bf7982481bc0086292a", - "0xb7596132d972e15dc24f2cd0cf55ee4a6cc3f5a0e66dff33021a95e5a742889e811afd1dc0cd465cee6336ad96f25162", - "0x99409bba2548f4ece04751308f815ecee71222869d8548fa142788fb19df5366d093a5131e57560237471bbd5279bbe5", - "0x8e7560988a844b5b844ad460b19c452a5a04346d8c51ca20d3b144a3670ecc60c064b2415c2eeebf140d6ae4ba5c5360", - "0x8cd9e18d311e178e00eb81ca839cfaa8e64e50a197de8461f07135fca28c1d895dd9c2401b923a4175ff711853497317", - "0x91ebf99c95e8f653402b3079ecbd533ed7cd3b6c857a710142354ce8330cebdee7cf0fd0400417883b66055bec9d0552", - "0xa9d0cf8cc6bbdc44426dcb716df667826426b4559056d73738bf3eaa6df373403861b6bbd6fa0454b1d2730e3b0015c4", - "0x928320b452ef21d2443dee360110550f531d7a4275b2cb227814150f3e9e360e05a884d6e3bc4415f202120ea5ac333e", - "0xb9551f2b2e7bb984618f2e7467e33b5b5303b8707f503f2e696e49c2990ea760c31e0944d52257c7a38b553a67cf621c", - "0xb2ec34126fe61345e5c6361fe55b8fb3218cdcc9103bba5b200252d50b758153cd549226b7aabedd265906401e755190", - "0xa8cf814926082a96a921d471036a9919a58e68d02ee671c215ea304759cd92a7c2c9ccebdd5e9ec5572164ad2abb22ad", - "0x8c0563c28c261bbe9a1ec4986f8b277324bf05b4fe5e2b79a862168e646bbea50ce7c4622b2aa7ca899c1a728c226d24", - "0xb558cdc334ea894d3a13347ea9e30f78a0a20621903d6c009c54feceba3ba81d2445a43572e088ae691f65489702e963", - "0xa62ba0b20f46c367cfd409beb300e39f1a6cd5be95e63457b6ad3cb66374aed754fd037b8e4215d651a7d8e1a442f762", - "0x8543e2c6135df471bd7a5c09f1313674c7f6847cb88f15eabf40b2bc9535d0ec606725b97103334a0c162a20d9f5bb53", - "0x8c0367d7058d63b425450f8ee9252e64234c0c2e61878c7c2d4b17bab22a72f40c75ac3bf8b64f264c00d9c5963af041", - "0xacb7207445993d563f1b6e7b179bbd6e87044399f80e6d15980acf7aaccb9d85071fecb22250afb3aba850712fbda240", - "0xb93725e66184bb03f0ab4078c737a7fb2b10294a3a09995958de3dcf5316b476ce9b5cd8d180017196d9482abdfcab88", - "0xafcb52bb7b8f45a945299da6fc6a877ba9f69f7f23d5f94b5f5d9a04c3cf3089333bbd50fc305e3907825003da73b9f6", - "0x961de781cb238cef52d43bc0dc7d8e3a75bca4c27ab37a2e9353137a9aa9403444a5841b595adeca75a3de5485ab97f6", - "0x9408c828d3ed6df40cc167d72ca9882a9c9cf8e765d6f9125e02e0d66ee0ac94f449803afb50bf1b92176feae92473d6", - "0xa85480591e7e033b9087fd0efe5cf3c88c10a75de4a5d7da4443df1cc1fa1aa59b6cde3ce7453fcabe555495e49ef6f7", - "0xa2611bd82344bc5d70d7e6cf3f0d25866b9f709ac4bf6f75d1006da2a11e2cd07a4c0ac71505e5062a04f71db7a3063b", - "0xac466aaa96febb5b810ba350c7a874797ce4bd6c9585f6b9d114d646894a67c9af9526ade4f7ec834d3a69e18ab643af", - "0xb73fc98a79fe77cdbc524c76a09cb9f2d5f8b0a5508846bed1ba5ea9ae3bb62120e01d3b8fb544d90ac9ae0c3d4ccefe", - "0xaed333c3403adc899a870082f70aadc770c9f880dc057f05a46d7400be9d893354121a0a31e5475898f437bf722eefcf", - "0x97f02133c72187178a8c48db26031f0b2c0317a6648d2be5f7450f00c37391cec935bea46b8144ec9fea5327ee959f27", - "0x940b582b41f1d0f09f0c5f51bab471e4eb143e91b1e96dde83e94650421d51f9c9baec10cc802fb83cd63b56d0b907c0", - "0xb1286a55a74a88a75da47671994916be428be1ca3f42783e497d6478eaa6aca69d50a421b210e9ed3283d578b651b8cf", - "0x97cd4e87e21c71d11f1df1c0b6518c00e1610661be4b13cdbdbb026d60fc3f4a2b8549326a648b3fdecb7de8f6aa9fb7", - "0x8f36bbcccee986c35328633bf6ee8f70b5dbf42d0f677c0f4e009d2289976e512af6af91a6ddcd87dc0df93bc4ecd02d", - "0x9253ad44ad182e67ab574d718733a69c05cd5bcc43e6292ef0519a9430460aa6a233fe26269da7298ea88cf406e733c0", - "0xb616b5ea74db0dcf8f10a2db79df6ec3566c06410f68a933eff150194608c591b2b175908d4b4ccaef1018b0fefc5693", - "0x80a712ba89394381cbb83fedcaae914cc4f21ab024b8da8a7bbad7762a22f82940451427b1a3f5d84c246d5ba0c7ccc7", - "0xa806909a5517a970879143ad789c6cb6256b82553b649f6865cdafbbc050b1f86528241b3cb600e784186e1a672b588f", - "0xb6ae801d1f0e4adf3ce57659d7c61f94abd3c8d1635ad28133a79eff0586fc48bdc195615335449e9bfee39e8a955eb2", - "0xb8a000561211844bef72adf3413f3b438a8789fcddf6676402ca6a1c2c63b9deed322030de2ae3a0aeb3cedbb89406c3", - "0x8bc3615b28e33fc24a7c989f8b4f719c914c4c65b35ad3d4cf15e2196e37c62e42ca34e8b1275e0f32589b969bdfc21b", - "0xb2f9637f370a79e7591e5056dac004f56b375f33645ae9f5a192cc6b7b6b3d8a1105cc00f10d8bc8ef250ecc2ac63c39", - "0xb51899978b9c5b737999fee1935a5b0944261e7005bea411b5903d2c16ea045a3b0bcd69395b6733752caed43bc4e343", - "0x873c71a01009dddb9885c48658f83aa6320e74bc152e09de8b631c763c2b4e2e8cbac921418a0d9085ff5c53a2b52d39", - "0x96470f48efd7d2ac2daea8753ef097c09c6fc128a54cc7ef758ff07e32c0b0ac7d122f97b53e88a29cc26874dfee5e0d", - "0x8dd2decbd3504b7961d65edb8d51b96377f4edd2e0d2cd8a4d98333f373c79a8d7ca8f8408718d0e7b5e48255857c339", - "0xb536ae387bdd0f6e40850c71fcaecb1051b2c8f7bf5cf92c6bda030de72a03e9212d00390c53a72a08e9fb2bff1249c0", - "0xb1566076f59064e3545adef74fd1acadc1bee0ae23543c30caf9e1ad1fc20ebe84ee25004c612525b26857253f5345b7", - "0xafd180e25444cb720342923b8897d38a6537bc33a0ca1fc9c6e4d524b280193618f19e2bcfbd07606b78b734fe6114ed", - "0x89b2a6c8811e5a6d07aa74c79dd854bdfc292cc104b525bc37e4c7c1f9485e19d759c8e27cd7cd73c46346f56ce3b189", - "0x8234196e196898b2501b79d0dc016f6df3d5878952cdb8a93735e4ce2ecf77d07924c701e084533a20f0c50a7d1ee376", - "0xadea7ce2efc77711f50138691ef1a2b946aaba08e7e3b21378708dd5a10bae933ed121e71834b43b14e2ea30a7b306e8", - "0xa566d406a35fae703b3d1ea1791d9207116002e5ee008d01e053a1ea4fe5af2feb63605b011ae6a14414028aa054b861", - "0xb83bbb063682386456719179b6f6bbc8cf6f791229600b7d402167737492f99437b45886695b26a28731e952e56f1ee1", - "0xa8f5fffc2c335d3ad5c7593e81f0862351413cc348392afa86d50921dabb929a5a1de20d604666af9e17a13bbc30bc3b", - "0x8d5dcdc1335f01847f6ef650ff64b26e7c4cecb934a7bbce11254e8ced9fa9e4fc87eec55248f69bf499180101c63f5a", - "0x83fec30b8bc62f9fc28301a03ef18158d6364738f1c42de311bbfba2e62b25d4c9ea9d6097698b24c84fff956a6748b9", - "0x96394fbe0c2d03cdaa56e13326aeb62344238ad3043ee2fb4f18ebf0a6f7f090f410032a2d15bfbeca9449202d59f2a0", - "0x94880f5928fe71a797362a37d05849d23e118742697f75bc87173a777e7b9d4383b8796a8a2bbee27fb781f363301dfe", - "0xaf229535896ab86fdf6d2ae676a0dbf44f868f6c7f17bd9a65567631c7aa2e29758f41de050ca5311bd1528bcc811532", - "0x8d4fa4968575b483b3ac16345e7f1ea3f81e8dad72c945a48b7b982054fe1030584be2f89b2f53af84d2490cda551b84", - "0x8052aeb115e4d242078c8726d376a13156cc832705243f14adaa3ef3889e1f2fcdfd46e087acab6fa85a74afde5f5eef", - "0xa1349c8a22788a1937a837fceecfaada9e93a63e582a09c56b53da52c9db1600254dc85f63f5eadfa30b89b31dcbdb30", - "0xa10178cdb263ff1a5e0cc034b6deaa160d00c3c3fe1fd1ff0c55fdf1ecb83d771070c10930f88832b75fef39a10024ea", - "0x938b17e4405934ea5ef29c2187d6787c5ff5d8c9a02665efb453117d462dbc50ef2c202cbc884305cd807a70b5cc177b", - "0x84f01f0da6b58c71788616be71fb3c259ceea7f8bd131a5661c5c03d0205feaff6dac2915919347b0559c381477b3d89", - "0x98787f0a2fac2b04bb7aa247ac77236bbe690aae64203e553be328a2c3bffb772e7a0244e585d27558cc64b089a5ee11", - "0xa14501d8b6b3a84b13b9006d521667e8d168f642ebf154c4e90ec8c75d11985fd0c9d86fc2efa6c7077dafecfdf0ab13", - "0x8215dee75eed04de83a3e910129bee8c48ce01cf1317ea477ff35c09a6f9e9771a8b05aa79e6b0f3e71b9874695e7a2a", - "0x85763c3072c7400a2c5668ef5cc53e6f4b8dff474146028a8be370ca9d8af9bf9ee10cd7d23d33eb6d6e257dd3af38d6", - "0x91bf62245c5a59d514d39bfb74db7f72ca7160c1c5d5be3844fff37e53e99d451e18a6747c65e33f98f48a55f38962c6", - "0x8c68817c6a6ea348d9aedce99929371c440fbad72718c2d239ffcaebb26ecc8a4e8c38c2819d945fdb7f02ffda70a5e0", - "0xa96ce2745866a22267a49faa7ea00ebf009ea8d0b0ca2c233c62759b9d5514306b5822dd2eee0124c9e28380e2f97aa4", - "0x8b18d5757c73843dcd55f0f0dc894bcd17e0ecf4c9fd901eacd38480844a15b4ce5e9598ccee039f9d93185137630cdb", - "0xa5b45c403b6735aaae14389bcee23ca10571f5437f1f5ab0c2b4e573dfd3341c638fff2cc780166af96b118d47ff2299", - "0xac849a0ccd354dd46bf55ea837d509b4ae3eefcbd5b8eb2582d301fd56c27b89950c6eefdd4e98e608ef4a6b75251311", - "0x89f13ac14bb064e9c6b49a482831ecea6344faec490bd18bb44028b83a0f22e21145861558029bd172ba7c5247c2cba7", - "0xaa57b057a2ac32c101e442c33831630c81b2e061a542e3e1d6897b2b7ca8a7241ef717a548b3f751d60d89be384ba5da", - "0x8a43db4e12682b98230364f25c75b49002f5002bd72a1674cf2a9d53197b5ef1b95e48429af98af503b0d5c3e0e017b2", - "0xa10cd7b8e1574d78c4e917cf833d3d845b878e8e8b60312e6a994bd4f391a5e8c38dcd774087b93c9241238f43f80937", - "0x8b61ccb949088286216cd628811df1a362a7f5c333654ce823e63ebd04b069d5b0f627fb6c96d54c7b853de8aab05472", - "0x887b902020ad45f70f2d5bcfa7324fcbe7be09fd2b1bd40f9ae43a89d487986e89867aee0945ea6a0fe8dfd051ffec56", - "0x822fcd260a7876cad31f54987053aab06108de336878b91b7a15d35013d6d4d6de2d4b30397bb6f1d5c1a7b48e9d1ced", - "0x80b89ff95d725858b50e84d825ea99fb6a8866f10b91a5d364671ccbb89cb292bada9537c30dbde56b989c8bdc355baa", - "0xb53cab156006c3a1766a57dd8013f4563a2e8250995dbeda99c5286a447618e8ac33ebf25704b9245266e009a0712dc5", - "0xb6e2da9c1156e68c15861a05cd572976b21773e60fc5f2f58c93f3e19c73ad6c2ee3239e6cb4654040c8e15df75a505d", - "0x8b7e187d473a0bd0b493adcdb91ca07c9310fd915dec46c2c9f36a5144eb7425dd35dfa50feb0e9ef747caed9f199944", - "0x9743ec3917e953e0a420406b53f4daa433adf4ad686207e9f296e7c83d1ffdbf81191b920ba635c85416e580178c16ff", - "0x98d1476fd4504a347c5261012298ca69c8593fec91919d37ddfdf84155b6f1c600cd8dbb92b93f3262da16cf40a0b3c6", - "0x94f50d52982a3c81ac47a7b3032dad505b4e556804f8606d63d821f2c1a4830917614630d943642ba375b30409546385", - "0xb5c0eb5f4cf3f719be1a9ad0103349269e8b798dbffe1b5b132370b9de1188a6d71dcbc3635dfdb4b888400f790b6ea4", - "0xb47fb45ec73392598866d27994c2feb0b0f3d7fc54303a2090757a64b6426d183ae41af16794ced349ede98b9b3fd48c", - "0xb5f45fd0aee6194dd207e11881694191e7538b830bfe10a9666493ae8b971d65bc72214a4d483de17c2530d24687d666", - "0xa50c149ea189387740d717290064a776e2af277deafcf5f0115bbbdc73c0840d630965a4e0214b738d1cb0d75737e822", - "0xb941afc772043928c62e5dbe5aa563fa29882bff9b5811673f72286ac04fddf9a9ed0f9faf348268fa593a57bc00ba6b", - "0x839051a7838937270bdf2f8990fd9aa7d72bfc86cffe0b057aa8eca7393abf16b70d71a6470d877f8ec6771efa5a8f26", - "0x835bc9d049418ab24dd1cbf76ed5811381e2f0b04035f15943327771f574f723b07c2b61a67a6f9ddc1a6a20b01f990d", - "0x8935cf5634d6ae7b21c797a7d56675e50f9d50240cb2461056632420f7f466fdcd944a777437dcb3342841ad4c3834bf", - "0xb5698fe3da1f9d1e176c9919fddd0d4d7376106774aa23a7a699f631566318d59b74ae8c033eba04d06f8cdcb4edbbed", - "0xad11421ba75d74c600e220f4bce2ca7eacb28e082b993b4368d91218e7b96029acfbdf15a2ab0b8133b7c8027b3c785b", - "0x886ef813644599051dafdaa65363795cf34a3009933c469bd66a676fdd47fc0d590c401cc2686d1ba61fce0f693426d4", - "0x8858fdf3e98e36d644257ab6076f7956f2e7eacc8530ec1da7f3e9001036cba7a0855fb5011925cdc95a69600de58b2d", - "0xb59eca7085a2f6dfeaa6a414b5216ff0160fbea28c0e2ad4f4ffd3d388e1cc2c23a32dbe517648221b75a92500af85e3", - "0xabec62d259bcd65b31892badad4ac8d2088366d9591cd0dab408a9b70ad517db39c2ef5df52348ba4334dce06a4e3ba5", - "0xa9acfe8f5a310779509621ed2946166ffb6168e68ecf6d5a3b2f6008df1728c8fceb811636c50d2e419b642a848a9ca9", - "0x9929bb1a3537362848fac3f1bcb7cfb503dac0a0b1bebbfd6ddf14c9a73731e2248cbaf0fbb16c7d9c40cc6737c3a555", - "0x981d06c7431e6f4654e32f1c5b27e7be89e7c38d59c4e2a872a0f0934cb852c6aeff2d2eaee8302131795590b8913f5e", - "0xa6ba9dd43354320f65fd5cdd5446cfa40080bcf3ef4a083a76ad4e6a609b0b088bcf26c4957bfab829dca6064410ca5f", - "0x9367ef28def311c79adfd87e617651fcc41ad8caf047d73ce9a1f327e8871e9b35d5b203fd0c0138e32e2ef91e20ba62", - "0x855d1bb508a9036f42116c8bbb830c576189798baee27c7c3477ef1b1fc5d7b0c2c7203457f1eb48d4b029dd6f646be2", - "0x8539a5d0528d3d601083e162b34cb33b5bf6736b4feeeab4941f10eea127c56b7e0b8d57f34b72f8f674d89c10bf302c", - "0xa3b71a9a9ac2dfcd681bfd8f6a5d9abf5df6950821705bdfb19db25f80d9b8a89fac7a922541cc681325679c629743d2", - "0x8e95929dfd4e5b56e5a8882aad6b7e783337e39055a228b36022646a13a853d574603de5fed12b6c1f2585621ead7afd", - "0x8b05c885575d6894cb67ba737db5915639a6f281bf249480df444ff9f02724e28ed7371ee7ec26d50d25f3966010f763", - "0x90f1a45de0cc0641181d54ee86630b5d182d24e7c30c2615803f16de90ec7c982a00b21f250ccebc2e94ef53a13e77e6", - "0x90f0e97a132092e51a4521c2ecaaa47e4e4f319e67a3cdbd00ed85c2f10dfb69c339bc9498e2abbffcd54b1fdc509a20", - "0xa9995234520cab9d1bdec1897b0b67571b718d5021c0fcf913140206b50ab515273b5f8a77e88fe96f718c80dd9be048", - "0xaebc6495d54d0e45a3c74388891dbcfab767f574fed0581566415af872dc5b3bd5d808c44f6e1fbdde7aa9ffd260b035", - "0xae757f8f4b1000a623a7d8e337a50c3681544520683207e09d05e08a6f39384b7aaadf72018e88b401e4a7bb636f6483", - "0xa626a28d5ce144cc0c6a30b90ec2c1412cbbc464ee96ac49035e5b3a37bb3e4ed74e8934c489b4563f2f7db1caf8b2ad", - "0x8c994e81dfd7a5c2f9d4425636611d5dd72d0b091a5862f8bec609d0cdd3c423eb95b0c999c48faa5dbb31e510c22b61", - "0xa1c0e59e076b908de760d9becff24883c6eb9f968eac356e719c75cce481f2f7bcb1a41ed983a00c1a3b9369a7ff18f9", - "0x8d7e199044fe2e552bc514668fe8171c3416515f7a5019f239c0384f0ade349e88df26cd30f6b67d02b83bf005d85de8", - "0x80190f2255199be690fb502d02ed159aa568c390a684f7840512efc3d2a62f28a49d5d1928ad99a5f975ad81a245acd5", - "0x889d84cefef33f5714e14d558f41d406072ba66b427bf27918b669c5be46261c3de0139610a2c2eadef8e6508e937bcb", - "0xa480a686d5085b854ccf9e261e7f1f2d40d978fc30b62b1a8fa9561127745529405820df21a680ee2258b8cefa5f0201", - "0xae6243400d416a8c13b80b6637726959ef07b8d9b6aff2bd3bb23aaaf97337c7a6b466c5db617bf2798e01d4ccc68e4d", - "0x85e0ff143657e465f3d934ee781de5cbd2bfd24f2fbbe6d65c698cdd93204a845f6ef1fa8941c2578463a06a8a418481", - "0x8f4f8b45f1a9f6c2a711776db70f20149dd6d0e28d125906ba9893c5e74e31c195b0906f04c922c8b556ced7cd3d611d", - "0x877b852c33483b25c4cd8da74b6b589d8aa96e217c3c4d813466c77ef83af95a94a47364aa8421f0396ce631ad87d543", - "0x852cb06bc4222ce125287a7a55a79ad0bf55596f26830dd6d79da3c60f80e3ba7b9a9b42b126dcb99d2cb9ce142783ef", - "0x810cd64c1dfce85d509eeb57a5c84efafe1d671454ef601a040de8d46fb33bc419577f6a6c404e28ffdfe315ffec558a", - "0xb60ff8bc804d101a32079b8ed52285fdbb47fd60c3c15cef17cfe7f6b0567de6b50128b9dbc49a1d9811b62b22c99143", - "0xa9df7068b26a6a58f7a499e67b17d34f2a2e8e5029c6e51e2b4c0d19324fb5cd9734c4c4d5034e1bfc274cd0c74a82d0", - "0xad93c50802ded1e21217a58b874c074ea52322492d589820691572084d8edaede8c2ce8021c6df8c0060f395f3c25ee8", - "0xa17b98e090f7ef5800477132b436c1fccc1802f34956711bfc176e36890c7df95a108e03f34659142434cbd8aee9dccd", - "0xacb14aea5575c293dc0a2b58c5350390801d57e9bcda876d87c56565043ddde1a544a88b48ad0d8ec3d41f690aef801e", - "0x88b8e26cbc83faa053fa247e26c95d1bbb77955b336e1b0e41d080633248238de8adc9b98688c98fdfc67e7286bc5be4", - "0x899f69823cf1b2204c8da91bb4f943c04d943137b08b1c46e160919e3378bd22a666a079a66e63d81c05336c742efdd2", - "0x8d7ffbc0b47a32408c9e88676ac4f87683cf37c37d214163ca630aec2d3cc014d88caff35022ff3b6d036eb8343d52a3", - "0xb7760f27db0704a6742855998a0c31333bb34d60ddebc95588e25b72445ae2030427aab088ec023f94563118980f3b74", - "0xad06ecc0f3745861c266bf93f00b30d41ed89d41e99ab63fedd795c970d3ad40560e57ab7333883a72e5575a059df39c", - "0x8687d28b1cbc8aa34a0e5dbdb540a517da9bda36160daaa7801fce99754f5d16eda3bc8e1df6b0722cfb49e177e9bcb6", - "0xa38332c3ebbd7f734c8e6ab23ae9756f47afbf7d1786fe45daebc8d7d005d6d8fd22f5dbd0fa8741e1bfb2014d3f9df7", - "0xb86f84426dee88188be9c5cc10a41599e53b7733ba6f2402392b0ea985effc7525756ca1b7b92041ae323337618b238f", - "0x958731a6f1881f652d340832728bc7fadd1acebd8daebd772b5acea634e9f7b7254b76d38a7065ea1b2cdea83b18a54f", - "0xadb90bff1f0d7d45b8ba28b536c0e0f7f4dc4b9a0354692ecf29539631d7a57d308db3e438e0f907810234c490b42153", - "0xa5188c775ad76617d3bb6e7f1f3b2449f48b7bb7a84035c316284396529564a227e3b9762a89c7114fa47b3ca7ba418a", - "0xa3826ef63c98793a5c8c5d5159e2e00cc85fb5e5124f06421b165de68c9495e93c2f23cd446adf6e6528967aa3ed3909", - "0x80eab97de89f3824ace5565b540b229adcc6ef9d2940e90de185af309234cd8aa4ae9c7ce1b409b3898c8fd10c8c2896", - "0x8824f5acd4c2330c459fdb9ece9313263a8b20419f50f8d49958dc21754c21a77bcf7fbf3e0041f78d8fb667a3342188", - "0x95091cf06911a997a09b643326c2fadbbe302555ab2521db806a762a5f4492636507ca71d7a093840236ac3c096614f7", - "0xa392c81a546196d7e78b61f3ceaadfb2771d09fe43f862c0af65f5e55ce490a0293b9ab754cb5ab03ff642a9a8213a23", - "0xafd76cce1dfa2c9e4af4f840376674f090af37d8c6541824963373f97b9dd1f405c50b2ff56165e1d4dde760e590738a", - "0x8fc4f513d3b40c10872603e1c29a4b2cf4c99320962644ce89f69ffb57f844344e1d472b2d43559119bdfb5a2c21749a", - "0x9951ca8e13b9a2b4a789e851c04c4f030470772da62f101074ef304612e9653b43b37d2c081b5d0a09196b3a167f5871", - "0xb4f16fc2a113403ab5fc1b6a9afddec77be7406413b70ee126f0e84796168a572940550d61e443e5635591d4b6c46ca9", - "0x8d71452cf39e7345c7298d514b9638a5cbe78af7652f0286d42632c5c6d7953ed284551fb40c77569a7721413cdbf79c", - "0x953625b58d52a308cb00ad87c44a3fd936786ada44000d45bb609ea9db6b156a0d0f9475e13ee5e053eaded19a09990a", - "0xa0983a3baa278ad5f5de734eb1b65a04f668408994e396fb0b054991ad2e56e27ac522b04fe37c9583b754e344f795b3", - "0x8eaa454257f77a6754b2c1c5ff0036fa5b03e214576fabc657902c737fcbf298b1795b43c5006e18894f951f5f7cd203", - "0x90183fdeae2ce2a295a567fa61b997b1f975d1be7b03d0101728cd707bb2a7111c222588ab22e573518fa1ef03719f54", - "0x8abec7f31f6b897a1d497368a42733a6bd14ffbb8b21d3e49fc4cd3c802da70e8886827c1aea0b18d1b44635f81ec461", - "0xa6d1e6fd24b0878ff264b725662e489451c590b2aadaf357d64210a3701fe763f529826fa6e0555267c1f5ecc2c52c05", - "0x8fe6d2a4ea0d91702cb2a8a1d802f5598f26d892f1a929ff056d2b928821e4b172c1c1c0505aa245813fe67074cf9834", - "0x82a026a408003583036f16268113ca6067ce13e89c6e9af0a760f4b2481851c62fadeeef0d361f51dcd9fa5674ec5750", - "0xa489a574b862d4056091ef630e089c163c16c2f104d95eb79a27ae1e898b26d6c1adc23edc1490f73bb545d3a6e3b348", - "0x939d85148547fc7b9894497841bd4430bc670bb670f0efeac424b529a9aebf2c02ac18a9d1402a12e4e590d623de09f0", - "0xa3ab52cf911a2ba7fb0cd242d7778ec0d4fa382960c9bd5b476bb1cd44ff1430a3871bbbcea0a0db2630c39ee639fd1e", - "0xb7629509d8c3a3b88b31f1af137a25c38f536284f11a5bbbe0d05b86a86bc92ebbf70f17c256dc8b0d48374e1985e6f3", - "0x8a8647ff33e0747dd6c6ceddcf7938a542656174a08a31b08337ea49b08d814e75f8363fb51676a2cd2746569e3bc14e", - "0xa7a7f8d94d32b7cee00b3ff272d644b8dca86b8da38c726f632c2bcdfa0afb13fd0a9a5685ddaeb6073df4d9cfa3d878", - "0xb7136eea8d05bfee2265b0e9addb4bdf060270894de30d593627891584b9446b363973de334b6105e0495cf8cb98e8f7", - "0xa9fcd33ea59315ad7611a3e87e8d1fd6730c8cbeeaebd254e4d59ed7d92c97670303a2d22e881ab16c58779331837529", - "0x965fd41741a0d898c2f2048945b2aefc49c735228c25deaf17fed82c4d52cf3f8e93b3fb8825ade632dc4940311b1542", - "0xb9f400a2c7ca7da8b36470ee5d26c672b529b98e6582012cbfc2a3c24b72e73f5633de4265c417c0d47c474155a603c6", - "0x85f333b0b1630a688a385f48bf0175cd13ecdd92fa5499494f4ad5aea0ef1b9d180fad8f936018538d842630ff72884c", - "0x8da95a735a1a98ed8e563099bd87d13a237dd7ec6880cfac56c6416b001e983a56f3d72dda7f68684bb33e4f64cadd30", - "0xa29b66a2095e1acce751f6aec8dfeae1e5b24187dfedb5d1635ca8deae19b580ef09329a18b3385ebb117cd71671f4dd", - "0xb001deeeaf5eaf99ac558c60677b667b9f3d57cf43a2c4d57fd74b125a6da72ea6c9dc81b110655e0df01ca7b8a7a7ed", - "0x912e11dfff77c778969836d5029747b494dd81d9f965f8be2c9db9e8b08f53858eface81862c3ee6a9aa10993d0d23f3", - "0xac166a00e9793cf86753aa002ca274cb6f62328869fe920f5632a69a5d30d8d3ce3f0c5487cb354165763ca41d83495a", - "0xb74df519ae1a8faeff2ccd29892886b327c7434360ab5c5355752667069a77d466a48cb57b1950d10b6c47c88b2a8538", - "0x8751679aeffa39da55f2c2a668f7b26fb8258f70c5454b13e2483e3ad452f3ac7cc4fa075783e72b4a121cd69936c176", - "0xae0cc16848b8bf8fffbb44047d6f1d32b52b19d3551d443a39fb25976a89d1a5d2909a4fc42ee81a98ad09d896bd90a9", - "0xa0c8acd6a2f0d4ab0e0a680fa4a67b076bbbf42b9ec512eb04be05fb2625f6d2ed7b4349eebe61eb9f7bd4f85e9de7fa", - "0x85c629ce0deeb75c18a3b1b4e14577b5666cf25453a89d27f1029a2984133a2b8e7766597e2ff9ee26a65649b816b650", - "0x938dbb477840d3ed27f903d09fd9959f6fec443fbc93324bc28300dd29e602bd3861fd29508da0dfdbb0fff7f09c5a6c", - "0xa7c76cd4a42ab7904d036fe6637471d9836ad15d0d26a07b1803b7fb8988b8c9edf522e0d337a1852131d0f658565ae7", - "0x838a30260cf341ae0cd7a9df84cbc36354c6bc7b8f50c95d154453c9e8ec5435d5f9b23de2a5d91b55adde3dbdb755b9", - "0x8f870b1f798c0516b679273c583c266c2020b8dea7e68be4b0628b85059d49e5a680709c3d6caabe767a0f03975c4626", - "0x89bad0b6499d671b362ae898fee34ad285aa8c77d33ca1d66e8f85b5d637bbd7ae2145caae7d9f47e94c25e9d16b8c4f", - "0xaf963d3dd3d983864c54b0ed1429c52b466383f07a1504215bbf998c071a099a3a1deb08d94b54630ac76d1d40cfc3da", - "0xb5686de207c3d60d4dcfe6a109c0b2f343ed1eb785941301b827b8c07a8f1311e481a56a4baab88edb3ddc4dace6a66a", - "0x95e5978739a3e875e76d927f7c68bdf7ab20966db9fa8859f46a837760dfe529afa9a371a184dfb89d2962c95d5fcf3b", - "0x96d2855e20c37ed7bd7f736e11cfba5f61bb78a68303a7ced418c4c29a889a4798c5680be721a46d548d63525637e6b0", - "0xb134bceb776cd5866e911f8e96016704c9a3caeadcabd7c0f37204497d789bc949e41b93e4c2d597e4c924853f1b21e3", - "0xa1949ff397013acde0303e5d64432bf6dd7f01caa03c5fc38e7c8ae705b9d5c2646b4b02d013004e5eb58e344703260c", - "0x8036a5f79d8aeb6df4810974cf8dbd0ac778906d2f82b969ac9dcfbe7ece832a7e8aad08a4dc520f7abeb24b1610ae84", - "0x982b6b0af8602a992c389232b525d4239edc3ae6ceea77d7729d1fffc829664dd647ff91c4cb9c7f7c25cea507f03167", - "0xb34c7d24fa56ab6acdb8af5b4fa694a1985a1741cc53a2b0c5833611e8ed6fb3b663a4d9a126bb4a1a469f2072199d66", - "0x8166366fec4ee2b3eda097dc200cdfa0533a742dfbe7082dfa14c1c1ecafc9d9fa71f518476634f29d06430869bd5e02", - "0x86c0251ac00b8200618c8b7ce696d1e88c587f91e38580b2d6ae48a3ef904e0ba1b20b7f432719ca40e7995f2281a696", - "0xafd89f3bc7843a1e45ac961e49c1971114c5238d9e21647804b1852b8f476a89c12d1edfb97fff71445e879d6bfd3b70", - "0x911d8bec4d4c3e73a2c35469b2167569f59705404425bd95440408fb788e122f96e9b1bd695f35c6b090f10135b20cd3", - "0xb3f6350ff7afaa0660f9dddd9559db7f164e89351a743fc695d987c88f89fc29136e3c5eb81963edabf2b6f2057120be", - "0xa371229680d1468777862e9c0e864156f9cd7c12ce7313a8de67b7bd34e3d1b6fa45ce891a81f8316f4afcbdecf3b6ca", - "0xa6a9a875ef9efe8ba72523e645b5773aa62c4fb41efd23da3fa38105472308b8d293be766342ee0a2f00758825bd3b6a", - "0xa840d495a184f4499b944ee08f07193a1e1bb8ab21f8ce7aa51d03bd8643f2bc2616c17b68d3fe7c0fb364136926a166", - "0xb55200ae7d6ebb0b04b748051c5907293184b126cf8a1c2f357e024f1a63220b573e2875df83d9b5e0c6e2ace9300c40", - "0xb1e0870f2e3719f42a48256ee58cc27f613308680f2d3645c0f6db0187042dddcfed0cb545423a1a0b851b3a16146d70", - "0xb43a22ff3f838ad43786dc120b7f89a399ed432c7d3aa4e2062ad4152021b6fa01d41b7698da596d6452570c49a62062", - "0x88b1dc50873564560affaa277b1c9d955aebdcdd4117dab1973306893b0e3f090899210102e7e1eef6f7cdf2f4e0e5db", - "0x9223c6246aa320b1b36eb1e28b5f9ccc2977e847850964f9762c7559da9546e508503050e5566ccb67262d570162b7a3", - "0xaeeed21b932752709f43dc0c2c7d27d20263b96a54175dd675677a40a093f02bba80e2e65afe3eb22732a7617bf4ff9d", - "0xb47cae580ae84f4e4303db8f684f559382f075ef6e95698b9a629e92b67bf004f64e7cf47e401768fa170c4259efbda1", - "0x849821e1ead81fe2dc49cd59f2bba305578c4ea0e8f4b8ae8fc275a1c4a6192f8819d5b6d7da786c94dfc16aacf3e236", - "0x8c60d9a8baefc72a3d3f9dd2e24cca40fb5ce36b19d075122391d9b371c904a0a15d2196c0f2ac9da3acf188d15b0fe8", - "0x946edfe168bbe5ddb0fa6c2890bb227d8418bfbebe2bafab84909825484f799407b610d8aab6a900c5ff9eb796cdc4bf", - "0xae7bf8ae71de5d7ea644d9541e49da1ec31eca6ff4c3fbec5480d30e07ef2c2046cc0a486af7b3615a6a908846341e99", - "0xb4d31a6f578463c9a5ccde0ea526c95b1981eb79468665395c0e550829abfdfa86689699d57830856e324092a423f231", - "0x93415ad3a732417cca9771b056ed42db7ce50879aca7c6f71883ad297eaf5a37fd4641d44a0b7e28b90c168834141340", - "0x98960617a413a3ba86d8257a7386355a69258943aa71834166bd624ea93b0af06178e86538e237f88fd039eacf7cb04a", - "0x881335200a487545e38d5b1ffda3080caf5729e1b980603bcdf9ea652cea7848335b83aeeaa321d3476ae4a8d9073582", - "0xb39e84c14666d51895b7a8341fd8319f9e0a58b2a50fc3d7925cce3037f7c75367b5fb5bf25ff4720c9992cab7b8b9f4", - "0x8ea4bab42ee3f0772d6bd24dff3643d8b61147b46ada374414d8d35c0c340e458e449d31023d96e66decf9c58e30cc34", - "0xa5198f6759a045b6a4ba28e4bc3bb638fad44c5a139064327580e285adf38ea82a7570acebf925e81a39d9025f3a6f2e", - "0x80267097e2d27c1b19ecf95d184dcff822d34e03326b9fc139a4f8b75b3f80777bb97a9dd284d9b755f14dd401d63c0e", - "0x946f346220bd3b6f733e94b61a1ad0b44e45c356fa6036dde5882d93b5613c98e23b20e91eddc6b3c5acea38085705af", - "0xa5f559e110cad99bbcae2d9362434aee7db0f3b6d72311291649dbda3f84c10e9760b66b988db3d30067bf18ae2e5238", - "0x8433b38e5c7b293ef532f8c70cef1ed9be7f31f60d5b532e65df7d2885203be78b7ad78ab3011bc54cd9f64c789bf837", - "0xa5a4c0a9b0e0b6bb912cf6ecd30738b0acc0146d77442449b486c3f32d7e60244f643a5cf9cc6da2de5408d0c5f17691", - "0xa81feb329fb51b72464bddcfcf4e02149d995b548d88c64ba143144ce16b652c9913c8ee948ee837596ec97cc43d8cc9", - "0x88e5a7e93a738d61330425bc21ade88d33d7160d124bf174eb3e12a00283654431036977c4f1a47a1bbbf2ef8449ac89", - "0xac75ad7c099383069e662bfd3624b92b64b5838246902e167fc31b9411efda89b2c6bbd1d61b9eb7d304faacf438d70b", - "0x8583bcd1c7cb9bb4bb6bcff803b0a991912b8403a63c0d997761ff77295ccc357d0292318601a8c61329ab28fed7bb83", - "0xa1f9aa0523f1dff00023a44a6c3a9e4e123be0f6722a1c6682ac3c6047efe9e62f4773daf4767e854e1fcbf8ee7339e2", - "0x85f65ebcf5c7e574174b7c4c4166a9a5368e7986b8c0ef846c2e13b75dea7311a87483503149ebfb3cb839b3ef35c82d", - "0xabc55eeb72699031a367b9675a2b91a8434e1f01467660903ced43a0b2a11a85ebdf48f95c13ff67e4e2958065a50ff3", - "0xa4ff77c9b86939a15647499b9412417b984bfb051e5bf27b35392a258a5dac297bbdbcf753a4be6729ffb16be924a2ff", - "0xaf0d41c15b5172efa801cc85ed101b76844dcd06712d0d21160893235a2dbedd15d187a9b31cf0d0ca6c14de6ab2b707", - "0x92661339199f18e5dd9a210783c1d173a26dfa315bd99a33d6f04bf506c871a2b47745c1909faa209d5e6c5c645124a4", - "0xb35813dafb52df709dfa47982bfb44e1bf704f9f46085b2a0e92511dff90e5597110f614f8915830821fc5ed69ae0083", - "0x934a05aa713fa276a4d47f1a28ef06591e5a9a69293c1651c223174df0af4927fc9cd43d374d89c1b4f7c8dc91abe44b", - "0x8f83a0ef05202c0b7170ac96f880135e2256fdf8964dae5aed5dd0f6452a6d8e123321e8c182b3aa6f1f8ab767caa735", - "0xb92db10c21c321cf1349fd34129d7180e5088daf2bbe570de6427299aab68992c011c2e2939a44247396f5427c1d914a", - "0x95ce1892d1ce25ef2bc88a23880055a4d829a3b31f3806635fd49bec32cca4e965b129b6dd3e90f7e3a2eb293ffc548d", - "0x970cf816ee7501ade36b0b59f87c7e352957f67f1f75bbacd8ed52893f9fc40572c76f49c23db44866af7e34a63cd3f9", - "0xa2fcd08581d3569fff699fd7ed1ede5f98f2b95956ecdf975a29af053d9f4f42600b3616ad6161e958c3ce60139c20a4", - "0xb032688b6cc8a7e63dcb82694f71f087b1ee74c4d5fa27323b1ead3ba21722d7fc49eda765725b5553db5260005049c3", - "0xb0b79e4329f1ad25ef6a603390baf889757cab5af10bfa6953a61f89aaace0442b9ef08e57ba778f1e97bf22f16f0ace", - "0xa2e6ac06f8973266cd0df447f82cec16614df65174c756e07f513e2c19aa82c10d8670047860960cfba3c5e4c42768c8", - "0x811e66df0f3721a1ae0293549a0e3cd789f93fb6be2cab8e16015a6d52482af9057b1b75e9456322a5a9e87235e024cd", - "0x8744a80b3d9e37da4c50c536007981a4958d7e531cb93916dbf985cdc22f4ff482a5cc4fe50915c049d2de66530f1881", - "0xb20b6e8c7be654c23c8ca440be2c37cf9cc9f4e81feedfd0cd7c56f37eda8f295fe5d415e9bac93d5f0a237edd8bc465", - "0xb33fd84377f31f7819150d464b5eb3ef66e06cb8712665cf0587d61e1b1c121d11cc647f3753bbc18604941c77edbc1f", - "0x83acb8a3ec5f477b6d44cd49f9e091bc2bf7c9dfee876cde12075a7db9262314cb66ad2e7557114e0c19373e31c6eff1", - "0xacfe4172327832ee207eb07da9cd37da3b009c776f7a8290529f0249f58da213254baddc7c3074fbaa1d226ba1e52b7c", - "0x81911b4dea863424b9d77a981987732382702e0294d8c8e1ec48e89678ecb0e64836b45205a120885fa8f8a3a4b9d4b0", - "0xb11f61b1302579a11077bb2f1f0db371ab943573b261be288dc76172eee8a5102b992a5b526092d160ffd20aac2d4856", - "0xab491f7f1e002a44944c02537f365e525ebb6d5614bba8e5e8e8bd12064c702a1759571ddbeee592a0ba8b73cfce8810", - "0x89211da3d92aed6b111de001b8b5a9231a1c2d09fb1cd2618ec457b635a6c8590fe119acca42fce76dce791c35b889c7", - "0xa5f076c8f7164bcab8af59021ef97a0afa93d0877e52241c3ff5a9a9f81227a55c119ed6a84d34b196e94ec851ca5ca0", - "0x80d91417d0d6c1adb5a3708165da1d54a83caaff482a4f65abf3fb335cbbc738c74ed19a8c451ca98befdf9b2d8b5f90", - "0xaecba33a67f66401614eec5fa945e763da284edb9dc713bad4ac03972630781a09a3e2a291aac0605a9560c5f3444de5", - "0x8a0aa1320bf5217a049b02ad02a4f892bfd6a3f5b48f472041d12f3aaab8dd197307f144f9de5f9e762c6b4971a121b4", - "0xa4120a569e446fe4129f998e51f09c1cc7b29dc2b353d6f6f05daad1a4ef99acfcbaa4950a58aacf7ee1b3fde0af33d0", - "0xaff71370d58b145758a5f24cf3c0c6667d22a1f950b8137c369fa845a5265cd645b422f24fa95e1cd7db1d68686120b6", - "0xa839f075a8a702809a51fbc94595eab4f269a2e7a027aa1f4fc472e77f586138bf5aa4e5570a560e139eb6cda4cca161", - "0x9484f1caa3e35cda0e3d36e43aff3dd8cf45a5a51fc34aafa3a63ed3543047ba9d6af2a9bc7c201c028499e6b4c41b28", - "0x84ddb374c5c9170903bb3e1054fad071b0a147a9ca2ebe2fdb491ebb2431d53b398872a39cc385f973e38579d8e60158", - "0xacaad8babaeaeb52c5b5a16ae689fa5ae15846f2d1f3596a52371bd8681819603822ee8d32ab8cda1bd5290d601e483f", - "0x946b69ca5361b60c3dc31db13669b05e5c0452f3c80e7e185f9667a36f351e9ed83bcb5c6dd2439ecd4490e3a87d260a", - "0x99f457221ac40df86f9b4bef0bf8812720b2f7218273a0aab08c4d4d4fb18a0fb0ef6ba9bf7fa53c116cc6f16742e44f", - "0x8bc0e812d8b718dbe48ead74a6bc7bac68897d01d097422be04110a25589bacd50d336d2c8b70d0dfde6c1b8bc372dc3", - "0x895d118dae2fb35a4b0de22be0d000ec0f0f317b9494db7c12f10d7db81b6f3eaf6d6f3fdfe952f86ec4143d7469368d", - "0x893bf3d7e579e800526bc317438a69590d33759931830daf965cec721baa793ea335e9624a86b84b8fed5effc3e2bbac", - "0xa112d30dda88c749ca15d6dc65bcbc7fe838b2d25329d44410a9a96db195c7ce6a6921196a61ba7c9d40efdb101a164d", - "0xb88b5340af052fc3b8e1a8cf7532206801e79d878f1fb02b32ac4f8e91b64e0ec9252d808b87c4579de15886a20aaef1", - "0x865f76475bb5da18c6a078c720c7b718e55d310876c98017c30ac31882ae347258b508ec34001918324250241d2df5b7", - "0xb6d8a15913eb1714061d5cacbd0bb05edd83ecdb848a89b864e7411598e9f7814d0c039ebe4735437c8370d2ff183751", - "0xa95fedce8351ae9c24d7fa06ebc5cd4e3aef87afaf04a7150e561a6a7f2347bdcec1e56b82d6e5f597fe7124f6cc503b", - "0x8526004ca0c802b073d50b0902ea69975949e7567b2e59ca2cf420bc53d91951d26096f2abb07a2955a51506e86488dd", - "0x99ccecaab68b6e5adadb9c848cb577de7e7ff4afc48d3b6b73bc0872730245b8a1c68cebf467074af6756d6226f4f4a7", - "0xb5497d5c0cd79b7e6022e295642e1f2161254379eb78ef45e47f02c84ef5a3f6b6297718e4fac8093bf017287e456917", - "0xb6943f30012b2093c351413c2b1b648afc14a5c4c0c338179d497e908451d2779919fe806181452ed386c1e8f8e8c25c", - "0xafdb56ce89bcd3247876c918cad68aad8da65d03c7c73ccbee0c4c39f3ad615aab87ffa0db5b3b63b4cc915d0b66deb7", - "0xa44659d7be2f11d4d4949571d7bf84a6f27f874d3281edc34ef1098d321a4dcad9a42632b39633f8f9d20a39f54a2464", - "0xa3e489b4db5832280dd58c62120262471b6fb4355c2ad307bd17c5c246b3f1e1b00f925930f5f5f6987de234fcbb7d16", - "0x87a4e3a190340ed4949597703083d338e9c17263ba8a39b67100589f0dddbc420d9557f9522c17c71ae04b76876f8db0", - "0xa35a3978e928eaac8c182a0a613c611ae7b4827c5e999f938eed06921c0294befdc21d02e68d035a2fc8d03c82641126", - "0xa6898d90265dcf0fb215629f04b07c7918e022667583efe0bfe02f258b446954876c6ca9e369ffe1bb079e2314ebda32", - "0x922fc52e648b6b2b6768c079c67ab425da72907a46add801715f8a2537280869d7071d527b833aa63ef562ce059a392b", - "0x8acbb7c4297196d8d1c131040c34cc7064656a148c2110b19c672abb094b1d084fafe967f7122ba9dd1523a4eaec3b42", - "0x82dbf2cdd581fe3b81b156792228eae2485710e6c21dd5fd14614dc341bb0afbebbc0f32340eda9f094b630afcfc17e8", - "0x907a095dca885da219e4558e9251ec765cf616e995c61546bc010963bf26f2d8adbd9b2ef61f2036e1740a627c20fbed", - "0xa7a83f849691d04640137989a2d0c90a7ed42a42b0ad328435d7e1fba557a27a58eec9170ab3d0099ec97da0c950765a", - "0xb7d435a801c2a5652cb479027f2c172eafa3df8ca0d896bbb9d49a42c42660fb382a8439bfed09ddf7e0214cb6066761", - "0x8bc6b5e79af5512589f90de8e69bc858277055cf7243f592cc4edd193f03f71d16c9300097ddafb79752c63f135c884c", - "0x913264fca800467bee58a429e1f245ef303f5dbeea90f0ce6bb3c7ae6d1bd0f99ea75d3d309634684d2178642c81b5d8", - "0x83ba558f9c23b785a123027c52924a1d7334c853a6165d4f5afd093b0b41951a36860ba0a20fa68f73d7db9df0e3ef38", - "0x875b2df7cb54ecdf7ba31181b9dc7dbe02761ab8ffb61757d42a735c8e20d44bad5b904e76dcec6bb44883fdb9f4ad84", - "0xaf3dc5d2dd29565de8f4c700d5f1ab71dadb4351f06e9ee2eb5ee7a9b5da827d0c6726c6dc780748a26aa3b4d10e6c2d", - "0xa113ff09296b25f550f6d0d3f37dd4517b14cf6d5517293bd3068aa3aea765a8640fcd4bf0ba96db5c00167267fbd574", - "0xa138c5cca485b9180ef091c9e327982bea203c165cb83564f416c36e813bea1ef1f6345f57c8a591df360541b7b758f5", - "0x85793441e917ed520d41dda6e762269fb9f9702e5ef83cee3e90652d324536bf4233425cd05b54a383609076ab84ea13", - "0xb422ac9de53d329e6321a8544c264d63cffc37965d627d7e180a999c3332644e21fedf10cd2f43cf6ba4fc542db91155", - "0xa85d31d4bfa583a493681e57bfccca677ec5b85870a53de37f7be7833b573f8c8dcf029cea4ae548d83048030d77d56d", - "0xab8a0702a371db496715a4ee8fcb6d430641b0f666d7fe3ef80c09df0bf570293cec1aa1675381c6bbd9ecc1f7cdccf9", - "0xb308ef2b87438d35957191294782e9f5014a3394fadad3e2ccaf6ebf20fd889a36dbb8ddb3634baa8e2e131618aa4e70", - "0x919e972e5b67cd65f377e937d67c27b4dd6fd42cfe394a34a70e8c253a1922f62ff36b9dcc7fbbc29b0960ad6a7fde88", - "0xa0e4d4be28301af38a910971c8391ef3ec822ce35757226a7fd96955cd79afa14accba484ef4e7073e46b4b240a5863f", - "0x9422f6d424c1736b4b9bb9762aa62944085e8662c4460319dac4877b1e705aa5cd8b6b3a91268363ec3857c185685f4b", - "0xb7cf9f2053119d284a37df4e4489b632594df64e5dc846652ee26b4715e352e6333118b125021481138e4ec3e9f9987b", - "0xaea983e81c823472df8652654be8a60a8bf40147d599f87e323397f06bf88c98e9c6db0f28414f6ea4091f3eb0f6a96d", - "0xaa20bf03cd8b6ffda09fe0ef693fc0aaa3bb372603e786700e52063a4f7ee742771c41cf5e67e6248f99b7fc73f68dbf", - "0x8748a4978198071d7d5ddc08f8c8f0675d895dc19df0889e70bd86d44c469c719b93f6526c7e7e916c7bfeb9a1379aaf", - "0xb8fcd863d55dab2f7b1c93844306e00056ba17338ddfa3f02689a0b58b30239beb687b64c79b8420ecea8d0d082d9ffa", - "0xabb1a35952dc8a74dd1cdbc8ae7294c6bfd1910edab6f05c879e9ed06c636a949fe0017ec67f8f6f73effcb5817cccae", - "0x8bef43422b1c59e354b7f46c08a8eb78e26c4d01c236a4fe781cefb7465293a4444f2bdc68c6a221cd585a2494d9a1d7", - "0x93527258940feff61befa18fcd6626fcff019d34a3ac8c6886599cbef75b15c15d689e8c1bd2177cc93c4c1792dee8d7", - "0xb7f114eea99c8278841180ec8886ad2bab1826554a1657b9eeb17aa815f31b59c3931913ddec40aa9923bc92f8975635", - "0x91a96446158b194a0a6ada2e37c8a45f3017c34034f757245f6f3b98c65d39d084e74d2a9dc271e5918faa53990ec63f", - "0xaea4ada0a853753db03f9790e20bab80d106f9b09e950f09aeaba5d869f0173bed673b866a96d6b0dd8123a539caac9a", - "0xb8e3e98ff0d3e512441e008a4a6783233045a4639e0c215c81984846b43ff98de99d7925cf717b1ca644f6229b6d16a2", - "0x8987ef81a75213894e11e0310e8ba60fe06e2b264cc61655e5b51bf41cc8c3d6c10696642ea3517770f93be360207621", - "0x8d4eff7335252f74af4a619c78625fd245df640f2086338dbb6c26b059f83fe70f3e81f5b6c12d62c0f784e572d56865", - "0xa56f6389b0bac338f20c615d7d11e16045a76cbea23ced0a9d9067f538421c378200bfd4523b7c96094ab67f47f98d42", - "0x83f5ab0727fd6ce8b3370ce3fac1f3a9c1930ea7ebbd16be61cc26f34aa1291ba4b5f16729d7d4f5924eaa4a1e31a04e", - "0x8cc62366874bf8751067a526ea32927584cef41174e2ec5a53079ee557067bc282f372b831cb2547c5e21a2f178c91b4", - "0xb609e141006dc8d8649457efc03f8710d49abb34bc26a33ed4e173e51b85d7acdf18d74aed161b074f679d88f5aa2bf3", - "0x873c7aa784c17b678443320950e494250baff8766db42619b9fc7ec4c3afa4eee290cd1f822b925d5b9e55c9cdd1af2f", - "0x859ba787f052d3665481c3dd58159ec8c238d918fb6d2787ebe275ef9acd377cb7aaa03a69820c78247bf51afee3d5bf", - "0x8eb1e6d2b0f51a3275b4a8be96957cb2d518b32c815dc0dfd5f75340c7dee73e5edc45db7c7d375c4ffaf8c59767d0c1", - "0x85f3876ff5edbb826a9592e68db3dcc975725bfdda4fcac197758a8b27e4f493e6c531b1342ba0f5a75f965273720345", - "0x8a1272f2678d4ba57e76c8758818965e6849971e8296b60ff85a522feeaaa3d23d3696c040d8bdaf1b380db392e988aa", - "0x85002b31ce31be7cc8757141a59a7cf9228b83144993d325b2241f5bfac09a02aca0c336307257f1a978c0bbf79fa4fe", - "0xb96bd26a6bbbc705c640285fd561943ef659fca73f25e8bf28cfcd21195752b40359d0edca0adc252d6e1784da267197", - "0x936cfe367b83a798ab495b220f19cfe2e5bde1b879c8a130f84516ac07e3e3addcc791dc0e83a69c3afc225bed008542", - "0xb1302f36190e204efd9b1d720bfaec162fcbba1b30400669dbcdd6e302c8c28f8b58b8bbde10f4512467dd78ed70d5e0", - "0x8291b49f56259c8d6b4fd71525725dd1f35b87858606fc3fe7e048ac48b8a23ba3f0b1907b7c0d0c5ef6fa76cddc23f0", - "0x97aca69d8e88ed8d468d538f863e624f6aed86424c6b7a861e3f45c8bf47c03e7b15d35e01f7add0a4157af171d9360c", - "0xb590d896e6b6f2e4dcffebfa67fc087fa518a9c8cb0834a5668cabe44e5c2b6f248f309b9cd74779030e172dba5d9e29", - "0x97e7099bff654bcb37b051a3e8a5a7672d6ab7e93747a97b062fc7ae00c95deef51f5ced2966499217147058e00da4be", - "0x83435b739426f1b57f54ebad423939a68ad3d520db8ca5b7e28d1142ebfb4df93f418b180a6c226c0ca28fa0651163a0", - "0x946c9144d982837c4dbc0b59544bdbc9f57e7c9ef0c82a7ad8cfddea78dedc379dbc97af54ba3ac751d844842a2990a4", - "0x90ba1eff9c25adba8c3e6ef5b0d46c13de304632fec0646ee3a7bee69da2bc29e162dd3fb98a37ed1184ae5da359cf0a", - "0xb17b7a5c0a48eb9784efb5ff8499230b45efeb801cf68e13fe16d0d308511af5aa60e3b9a5610f96d7c2242ae57d455b", - "0x9991245e5617c4ea71575e5b2efe444f09cbbed13b130da08f8e9809d62512e8298a88d41f6aa3dbf3bcbc90654ceb18", - "0xa1190c4cbccf2898a7fe025afd03f8652973a11cef59775fb47d69a6b4dcb9a5a0c554070421a5e10a75e43b63d37b79", - "0x857c0a5f291eb35a76be11543a8c3d798187bd0717e2cdee50d390b66322d0d9529520fd3377136cdc93cfee99b6403f", - "0x944d11e5f9a3493c67786df94f129352d892fbdc43e98206b8dbf83cce240f65305e1768b38e5576048a31dca5c18f31", - "0x818f361c5dae709e067a82b81beffbd9674de8df2bc1bfc3a27ddf326260e124e46b1e36697fb8de539b7736db093e9e", - "0xb07f5b737735a0d628e7ac2d335080b769bdb3acea38ad121e247a6e4307916ba1d029da5d341f079ea61eeaf7d8554e", - "0xa69e338803f3ee0fbbddc7ee481a13f6b64d25d71bae0d76f4b5145b54923cf1616c77ba0fd9ca37a3ae47208f490423", - "0xacaee66b94e226622e28a144f93f6b1b442b9c79d7a8a1740c4d53044d0675a661e7453509b9e716e469fe11ce45ee31", - "0x9402ca799d2e1cce0317ed49453ee0b2669b05e68ff101b89306db215c3941b3786ad3402d00369cb1dee020b56d3142", - "0x849440c539fc0df3c8d06e23e271e6faa50234d5c057b8561e9376415f4396e548351cc677b0abeafe4f51b855a3dc83", - "0x865b99587eb3dbc17e412647673f22b2e89185d1df1ec8ea04515585ad2edfb731be458123118dcd7b41b475026477b9", - "0x9390618833b5adbaf24bd38cf9fc6f25104717f314259bb4da5c7a1f6963ecdc04d07bed391d8cd765c3d53567b2b6b1", - "0x95383e8b1d0a629cec238b5ae2bda236a027f4e3b5f99ceace05f1d5a781ec1e7a43058f44ef0a5aee6b0db5697a0d89", - "0x91739b8946d90db3a5244f7485295cc58143ba0449c9e539df1ba3c166ecf85ff914c9941192963c32d35033ae2f0980", - "0xb5d88848d856d882db5947b9182025f0abf2bc4335b650fa0a48a578e2c87f32cc86d42d3b665ee2eab46d072bf1eccd", - "0x91f4c754549f5a53b1902ef84274ce9acf0bfd2e824e62eb127d67e3214ce05fc2430c05ea51e94dc6e8978f5d076bab", - "0x91fff8c75f8ad86afe78ec301de05e4ca71421d731419a17c747a9a0bf81129422c9499e4749107b168d1695dc90292f", - "0x99fbd7bede9cc1e2974c2a21c70788960c2dbf45a89552da8d73bb1d398b8399590707f2f4ba4b43cb356e703eb01b5e", - "0x80a51cd83e3d748c07b9ac82de1a697b09031e3edc7bf585f06cd0ffa8ea319517fcc2b735614b656677b54b4910814e", - "0x886b27de1f93311d1a31b6d698aa28b54fbd800decd8e25243d89e352ee38cb252d5648b5134a3e1ed021bae46e9da48", - "0x976e70c94db905f83b4ef72188d840874bf005814c0c772f3832aa65b1f21927403125eea7a07b6d3305b1a781b36ab7", - "0xb4adb9d1c49eb31462583580e3ffa625bea4f8b2a7d4927e4ff925c1759d4b3c1e43283d635b54fb0eabfbe1f4c12992", - "0xb66b466bd48485ebeedd47e749d86cbaa3deffbbee2e69cfaa5e9f3bd28b143d7c1c0255a7a1393a2cc1490b2c485571", - "0x8bded5bc0794513947ddb00ff6b780c5cc63a74e2a0b0284153c346a31c82e1eff07c073939da39e6f87a06c14ff1a80", - "0xaceea8c6f799589f6b7070abf69fec724e6679514e60f1eaf9a52c37e9cebb72abcc833a81d8da1a4f5194c1a7eeff63", - "0x89a9f76d053379687fd221ebcaf02c15c2c241bb673ef5298e32640a115d9e0f2331c3e185572cd65946dd6c5bd42412", - "0xa57b6f1e3fdd92eadc6220760f22d0685a82cada1c7a1bda96d36e48e2852f74f3a83c757dd8857e0aee59e978da4919", - "0x9106cf0891bb39ce87433c5f06a5c97a071d08ad44a7cbcd6918c0729c66bb317fbbee8aa45591cee332ad1234c7257d", - "0x96c18cca4a0f0299e0027ff697798085f9f698a7237052c5f191b1dba914e5a015ae356b80c17f0fdd31d08c5a939ebb", - "0xa892103c93df126c024825c07d8769bdac5f1d26ea9509ee26530dc594384b2a5095cc34e0b41ab3db0392a29792c9e8", - "0xb7c2dbc95edb6fc25802ea051803b7bea682f87a99f8a9fdcc3091c81d914b9493dfb18a8894c964805298a6c22b07f2", - "0x8e40948927d560a6840d7fb99802989ce72b43693e9dc7ed9dcda4bca7daedf75271cf656bcc22b3f999a550faad8648", - "0xb354de1c6f0603df3ed9036c610281e55b51a48950ee3ce57a00b4692232de7ca57d19722700e15cbe67a91fcec2f786", - "0xadf987b90737b933436d8036c1d3f0c9104f26c540052e22e703964f72739ac1261e4289b8f27dec47281a0f3f51378a", - "0x8ed5248e9c836fffa7c924178db593e1aaeb54bcf2e93c1983c1f3899cad538deeb2b836430fddc9b2f283e0797ea11e", - "0x907e5410e3bd5d7f55340e2f497bd1ca10bfcb4abed2c66a3cdf94dc40bbd7c43ac98754e0b4b223ea4c61eebf2f27f5", - "0x8e81b441ea0397db28840fb4b3c3bfe6d8e31418816f7bda36f9c1cfe4556daee30c43639d90a2dc9b02a3d65e5f4ab2", - "0x897085c477f5030f9fed06e181b05953a8cd2001d959dd6139738d40f1d673b2c7120b5348f678547acfdc90ffc9fcc6", - "0xb0bf2784c4b3808a04be5a00a0593035ce162b3886e1500247b48365eac8ec3d27c7e5e6372e030c779c75fb79772d0d", - "0xaf3fe6c75f2a1241ac885d5091ff3882cf01695d957d882e940f0c31f7a5b5e269c1a2bae7336e9a7cda2b1d23c03bd1", - "0xa6d94e065f85736d77080a4f775885ccb0dd5efdbe747e4595280bca0ebe12450257c1beadcbec77566ef57508c5d4df", - "0xa5c50fe56b5532bf391da639a2f2b6cbb2634fc6637416fea7c29a522dea024d4adaaa29b6d472b4d2cc3e3b85c72e2a", - "0xafc35f5a03b245a6286318ef489db05d397bbd16c17b4e92eeb56509f875246c0176c01804139eb67dc4247c2a36ff9e", - "0x99ba14ab5a9612c078f9bbaa0e68fd1d52ecceb2ed19bd9abf8f98dd4ed1f9c4fa6e4d41bcef69be2ff020b291749ca8", - "0x8018cdd3d96f331b4c470a4c3904bed44cadecbeec2544ca10e4352cf4ae1a856cf55f6383d666bf997ad3e16816006e", - "0xa9964790c318bb07b8fe61d230dd2161dd3160e186004647a925cfec4c583b4e33530bf5d93d8a14338b090055085b05", - "0xab89d8401df722101c2785cb3ef833017f58376ee82cedd3e9405b2534f259bb76063434a247652c7615a6de5194de65", - "0xa72c3d320a0d40936dee8edfb36703be633aefbb8f89530df04eb6aebe0305ef4f4b6709436f8036d417272a7e47e22a", - "0xb3457661ad62634cc25e2918921a97b0bf5c59ccc7063bc8eb53194783f07659f42f8978c589228af5b12696588d8b2f", - "0x926fa35cd3ed4c8ad78af6284b87ae53b2e25a1ff50398034142a2bbed5b989ba3181ff116838931742c0fbcd8b8a56c", - "0xae57fe506626432f27ae4f8791421c2df9efd9aaabe4b840ccf65fc3d0dd2f83e19eb63ae87bfa6898d37b5da869ddb2", - "0x99c0a26ac74211db77918156d7ae9bea6ecf48da3ce9e53829a9ad5ed41321227c94fbd7449ae2e44aae801811552b1b", - "0xabdd2635b61cb948e51b762a256cf9d159b9fcb39b2fb11ba2fed1cb53475a03fc6e024a6a824a67a689396119a36a7b", - "0xa5ca98b98da8bb8eb07b1e5e3c85a854db42addefacd141771a0c63a8e198421dccc55ef1d94662ca99a7d83b9173fc3", - "0xa821bb5cf1eb3aeae6318c8d554e2ea3137d73bb29d2e4450c9a33f441355ea77bb0e0e0ce7c819abc3ed119110a3a92", - "0x95cdfb19b3f7196c26d60586e2c1efaa93352a712f8c8ef6209f6f318cecd52d7bebdfbfee4be1f5903a1595f73bc985", - "0xaef6e6a400106e217f9888afcef0a1e1299b59017e77dc5453317dec0c32ae96873608bef3f1b504a7e4f45b06edc9c6", - "0x96399ad093299ba26dc09ae85dbec9a1801dea4a338dd5d578bcdcb91246db0059e54098ba8a56cbb24600a40095cf79", - "0xad8b018ac99857ad4b38bdf6d110bbef64029a4d9f08df85a278c6ddc362a5f64e1f3a919f798ccb2f85a7f4ca1260b4", - "0xb211f3b5dd91941d119c4fe05e2b4c7bb0ce0a8d7ef05932a96e850f549a78cd20cded0b3adb3f9f8b7058889ae2cb4e", - "0xab780dd363671765c9c9ab0f4e7096aacf5894e042b75f40a92df8eb272a6229078cd6eadcc500eead3650860aa82177", - "0xa4d96b16ab3abe77ead9b4477c81957e66a028f95557e390352743da53d1a7ba0c81d928a7ea8bc03b9900135ac36a6a", - "0xb4d4e028099bf0f28ac32141cd8de4ee7c3d62d4f519fad6abbb4ba39592750812220a4167d1da4c4f46df965f7cf43d", - "0xaa929c5f0bd8cb44a861bfb3d18340a58c61d82afa642447b71b1470a7b99fe3d5796bdd016b121838cb3594f5a92967", - "0xa038e66f0a28aba19d7079643788db3eed8e412fb9ab4c0f6cacf438af4657cc386a7c22ae97ccc8c33f19a572d6431c", - "0x89c1ff879faa80428910e00b632d31c0cebb0c67e8f5ded333d41f918032282fb59fbcbe26d3156592f9692213667560", - "0x8d899072c9d30e27065d73c79ce3130a09b6a4a4c7d9c4e4488fda4d52ad72bd5f1fd80f3a8936ef79cf362a60817453", - "0x8ffb84a897df9031f9a8e7af06855180562f7ca796489b51bb7cca8d0ca1d9766a4de197a3eb7e298b1dfb39bc6e9778", - "0x836ebd0b37e7ef4ff7b4fc5af157b75fa07a2244045c3852702eaafa119ca1260c654a872f1b3708b65671a2ece66ad2", - "0x9292dfd6d5bfc95f043f4eb9855c10cbcf90fbd03e7a256c163749b23a307b46a331bdbd202236dca0e8ea29e24906de", - "0x8bc37eaa720e293e32b7986061d2ffcbd654d8143e661aabe5602adc832ab535cffbe12a7b571d423675636a74b956e4", - "0x887455f368515340eb6f9b535f16a1cf3e22f0ceda2ead08c5caefccef4087e9f4b5d61c5b110ff3e28e4ab2ad9e97c5", - "0xa6e5ec36e7712056fec00de15b8696952b17891e48ebe2fa90c6f782c7d927b430917b36b4a25b3d8466da3ca2a4985d", - "0x895cae36ba786104ec45740c5dc4f2416b2adce6e806815e3994e98d9e1be372eaec50094fbb7089015684874631ab7e", - "0x9687444fe6250c246b1711a8f73992f15c3cac801e79c54ffd5e243ad539fdd98727043e4f62d36daf866750de1ba926", - "0xb17f75044c8e9ce311bb421a5427006b6fa1428706d04613bd31328f4549decd133e62f4b1917016e36eb02ea316a0ca", - "0x8538a84d2f9079dd272a7383ff03b7674f50b9c220e0399c794a2bcb825d643d0fc8095d972d5186b6f0fe9db0f7084f", - "0xaf07b37644cc216e7083bac1c4e6095fa898f3417699df172c1f6e55d6c13c11f5279edd4c7714d65360b5e4c3c6731e", - "0x87eed8fe7486c0794884c344c07d3964f8fc065aebb0bb3426506ab879b2e0dfaefa5cece213ec16c7b20e6f946c0bd2", - "0x8a4bf42f141d8bc47c9702779d692a72752510ef38e290d36f529f545a2295082a936c8420f59d74b200a8fff55167c4", - "0xa7170e5e00a504a3b37cb19facf399c227497a0b1e9c8a161d541cb553eb8211449c6ac26fe79a7ff7b1c17f33591d74", - "0xa9a2cc7232f07ef9f6d451680648f6b4985ecab5db0125787ac37280e4c07c8210bab254d0b758fd5e8c6bcf2ee2b9ff", - "0x8908d82ebfa78a3de5c56e052d9b5d442af67a510e88a76ba89e4919ae1620c5d15655f663810cfc0ee56c256a420737", - "0xa9d47f3d14047ca86c5db9b71f99568768eaa8a6eb327981203fdb594bdb0a8df2a4a307f22dcea19d74801f4648ea89", - "0xa7c287e0e202ebfc5be261c1279af71f7a2096614ee6526cd8b70e38bb5b0b7aca21a17140d0eddea2f2b849c251656a", - "0x97807451e61557d122f638c3f736ab4dab603538396dca0fcdf99f434a6e1f9def0521816b819b1c57ecdfa93bd077eb", - "0xa8486d60742446396c9d8bc0d4bed868171de4127e9a5a227f24cbf4efbbe5689bbd38f2105498706a6179340b00aed5", - "0xa03b97c2a543dfefa1deb316db9316191ab14e3dd58255ce1027b4e65060d02fb5cb0d6ac1a2bf45bfeac72537b26429", - "0xa7d25060f6861873410c296a4959a058174e9a1681ac41770788191df67fc1391545dab09de06b56cd73a811b676aa1b", - "0x96bb9c9aa85d205e085434d44f5021d8bbafc52cd2727b44e2a66094a4e5467b6294d24146b54c0d964c711e74a258d4", - "0xb07b17f11267e577191e920fa5966880f85ff7089ac59d5d550e46f3a5cdadd94f438a547cd1ec66f20a447e421f96c6", - "0x964e33e1571c97088fe7c8ca3430db60a8119f743a47aa0827e6e2fb9bae5ff3bf6cecd17b11dd34628546b6eb938372", - "0x82a0513a05870b96509a559164e6ff26988ea8a2227ac6da9adc96fe793485a9eb6bdcab09afac7be4aef9a5ae358199", - "0xb1185bc679623e7a37a873d90a2a6393fb5ccc86e74ba4ba6f71277df3623cde632feae4414d6429db6b4babde16dee0", - "0xb3d77504b7032b5593a674d3c0cd2efbf56b2b44ed7fe8669f752828045e4e68202a37bf441f674b9c134886d4cee1df", - "0x95ab31749ff1f7b3f165ce45af943c6ed1f1071448c37009643a5f0281875695c16c28fc8d8011a71a108a2d8758e57d", - "0xb234dee9c56c582084af6546d1853f58e158549b28670b6783b4b5d7d52f00e805e73044a8b8bd44f3d5e10816c57ecc", - "0x86da5d2343f652715c1df58a4581e4010cf4cbe27a8c72bb92e322152000d14e44cc36e37ff6a55db890b29096c599b9", - "0x8b7be904c50f36453eff8c6267edcb4086a2f4803777d4414c5c70c45b97541753def16833e691d6b68d9ef19a15cb23", - "0xb1f4e81b2cdb08bd73404a4095255fa5d28bcd1992a5fd7e5d929cfd5f35645793462805a092ec621946aaf5607ef471", - "0xa7f2ca8dacb03825ef537669baff512baf1ea39a1a0333f6af93505f37ed2e4bbd56cb9c3b246810feee7bacdf4c2759", - "0x996d0c6c0530c44c1599ffdf7042c42698e5e9efee4feb92f2674431bbddf8cf26d109f5d54208071079dfa801e01052", - "0xb99647e7d428f3baa450841f10e2dc704ce8125634cc5e7e72a8aa149bf1b6035adce8979a116a97c58c93e5774f72b7", - "0x95960a7f95ad47b4a917920f1a82fbbecd17a4050e443f7f85b325929c1e1f803cf3d812d2cedeab724d11b135dde7a3", - "0x8f9cd1efdf176b80e961c54090e114324616b2764a147a0d7538efe6b0c406ec09fd6f04a011ff40e0fa0b774dd98888", - "0xb99431d2e946ac4be383b38a49b26e92139b17e6e0f0b0dc0481b59f1ff029fb73a0fc7e6fff3e28d7c3678d6479f5a3", - "0xa888887a4241ce156bedf74f5e72bfa2c6d580a438e206932aefc020678d3d0eb7df4c9fe8142a7c27191837f46a6af6", - "0xab62224ea33b9a66722eb73cfd1434b85b63c121d92e3eebb1dff8b80dd861238acf2003f80f9341bfea6bde0bfcd38c", - "0x9115df3026971dd3efe7e33618449ff94e8fd8c165de0b08d4a9593a906bbed67ec3ed925b921752700f9e54cd00b983", - "0x95de78c37e354decd2b80f8f5a817d153309a6a8e2f0c82a9586a32051a9af03e437a1fb03d1b147f0be489ef76b578b", - "0xa7b8a6e383de7739063f24772460e36209be9e1d367fe42153ffe1bccb788a699e1c8b27336435cd7bf85d51ba6bfdd6", - "0x937a8af7ed18d1a55bf3bbe21e24363ae2cb4c8f000418047bf696501aaeec41f2ddf952fd80ef3373f61566faa276a9", - "0xab5e4931771aeb41c10fa1796d6002b06e512620e9d1c1649c282f296853c913f44e06e377a02f57192b8f09937282eb", - "0x893d88009754c84ec1c523a381d2a443cb6d3879e98a1965e41759420a088a7582e4d0456067b2f90d9d56af4ea94bba", - "0x91b2388a4146ebaaa977fec28ffbfb88ac2a1089a8a258f0451c4152877065f50402a9397ba045b896997208b46f3ebf", - "0x8ce0523192e4cc8348cd0c79354a4930137f6f08063de4a940ea66c0b31d5ea315ce9d9c5c2ec4fa6ee79d4df83840dd", - "0xb72f75c4ab77aca8df1a1b691b6ef1a3ff1c343dd9ed48212542e447d2ed3af3017c9ad6826991e9ef472348c21b72a4", - "0xaf0fa5a960f185326877daf735ad96c6bd8f8f99ab0ab22e0119c22a0939976ece5c6a878c40380497570dc397844dba", - "0xadf9f41393e1196e59b39499623da81be9f76df047ae2472ce5a45f83871bb2a0233e00233b52c5c2fa97a6870fbab0a", - "0x8d9fc3aecd8b9a9fca8951753eea8b3e6b9eb8819a31cca8c85a9606ce1bd3885edb4d8cdbc6f0c54449c12927285996", - "0x901969c1d6cac2adcdc83818d91b41dc29ef39c3d84a6f68740b262657ec9bd7871e09b0a9b156b39fa62065c61dacb1", - "0x9536a48ccd2c98f2dcbff3d81578bbb8f828bf94d8d846d985f575059cd7fb28dfa138b481d305a07b42fcb92bacfa11", - "0x8d336654833833558e01b7213dc0217d7943544d36d25b46ecc1e31a2992439679205b5b3ab36a8410311109daa5aa00", - "0x95113547163e969240701e7414bf38212140db073f90a65708c5970a6aaf3aba029590a94839618fc3f7dd4f23306734", - "0xa959d77a159b07b0d3d41a107c24a39f7514f8ce24efa046cfcf6ace852a1d948747f59c80eb06277dce1a2ba2ec8ea9", - "0x8d2cb52dd7f5c56ef479c0937b83b8519fa49eb19b13ea2ec67266a7b3d227fb8d0c2454c4618d63da1c8e5d4171ac7b", - "0x9941698c5078936d2c402d7db6756cc60c542682977f7e0497906a45df6b8d0ffe540f09a023c9593188ba1b8ce6dfcb", - "0x9631d9b7ec0fc2de8051c0a7b68c831ba5271c17644b815e8428e81bad056abb51b9ca2424d41819e09125baf7aaf2d4", - "0xa0f3d27b29a63f9626e1925eec38047c92c9ab3f72504bf1d45700a612682ad4bf4a4de41d2432e27b745b1613ff22f9", - "0x80e3701acfd01fc5b16ecfa0c6c6fd4c50fe60643c77de513f0ad7a1a2201e49479aa59056fd6c331e44292f820a6a2c", - "0xa758c81743ab68b8895db3d75030c5dd4b2ccc9f4a26e69eb54635378a2abfc21cba6ca431afb3f00be66cffba6ab616", - "0xa397acb2e119d667f1ab5f13796fd611e1813f98f554112c4c478956c6a0ebaceef3afae7ee71f279277df19e8e4543a", - "0xa95df7d52b535044a7c3cf3b95a03bafd4466bdb905f9b5f5290a6e5c2ac0f0e295136da2625df6161ab49abcdacb40f", - "0x8639fc0c48211135909d9e999459568dbdbbc7439933bab43d503e07e796a1f008930e8a8450e8346ab110ec558bcbb9", - "0xa837bcc0524614af9e7b677532fabfb48a50d8bec662578ba22f72462caabda93c35750eed6d77b936636bf165c6f14e", - "0x97d51535c469c867666e0e0d9ed8c2472aa27916370e6c3de7d6b2351a022e2a5330de6d23c112880b0dc5a4e90f2438", - "0xaadb093c06bd86bd450e3eb5aa20f542d450f9f62b4510e196f2659f2e3667b0fe026517c33e268af75a9c1b2bc45619", - "0x860cef2e0310d1a49a9dd6bc18d1ca3841ed1121d96a4f51008799b6e99eb65f48838cd1e0c134f7358a3346332f3c73", - "0xb11c4f9e7ef56db46636474a91d6416bcb4954e34b93abf509f8c3f790b98f04bd0853104ec4a1ff5401a66f27475fce", - "0x87cb52e90a96c5ee581dc8ab241e2fd5df976fe57cc08d9ffda3925a04398e7cffaf5a74c90a7319927f27c8a1f3cef5", - "0xb03831449f658a418a27fd91da32024fdf2b904baf1ba3b17bbf9400eaddc16c3d09ad62cc18a92b780c10b0543c9013", - "0x94e228af11cb38532e7256fa4a293a39ffa8f3920ed1c5ad6f39ce532e789bb262b354273af062add4ca04841f99d3aa", - "0x99eb3aeb61ec15f3719145cf80501f1336f357cc79fca6981ea14320faed1d04ebe0dbce91d710d25c4e4dc5b6461ebf", - "0x920a3c4b0d0fbe379a675e8938047ea3ec8d47b94430399b69dd4f46315ee44bd62089c9a25e7fa5a13a989612fe3d09", - "0xb6414a9a9650100a4c0960c129fa67e765fe42489e50868dd94e315e68d5471e11bfbc86faffb90670e0bec6f4542869", - "0x94b85e0b06580a85d45e57dae1cfd9d967d35bdfcd84169ef48b333c9321f2902278c2594c2e51fecd8dbcd221951e29", - "0xb2c0a0dd75e04a85def2a886ee1fda51f530e33b56f3c2cf61d1605d40217aa549eef3361d05975d565519c6079cc2ac", - "0xabb0ea261116c3f395360d5ac731a7514a3c290f29346dc82bacb024d5455d61c442fefe99cc94dddcae47e30c0e031f", - "0xa32d95ae590baa7956497eddf4c56bff5dfdc08c5817168196c794516610fcc4dbcd82cf9061716d880e151b455b01e0", - "0x8bd589fb6e3041f3ef9b8c50d29aed1a39e90719681f61b75a27489256a73c78c50c09dd9d994c83f0e75dfe40b4de84", - "0x82d01cdaf949d2c7f4db7bfadbf47e80ff9d9374c91512b5a77762488308e013689416c684528a1b16423c6b48406baf", - "0xb23e20deb7e1bbbc328cbe6e11874d6bdbb675704a55af1039b630a2866b53d4b48419db834a89b31ebed2cfc41278dd", - "0xa371559d29262abd4b13df5a6a5c23adab5a483f9a33a8d043163fcb659263322ee94f872f55b67447b0a488f88672d6", - "0x85b33ddf4a6472cacc0ed9b5ec75ed54b3157e73a2d88986c9afa8cb542e662a74797a9a4fec9111c67e5a81c54c82b3", - "0xaf1248bc47a6426c69011694f369dc0ec445f1810b3914a2ff7b830b69c7e4eaa4bafec8b10ed00b5372b0c78655a59b", - "0x94b261ed52d5637fd4c81187000bd0e5c5398ce25797b91c61b30d7b18d614ab9a2ca83d66a51faf4c3f98714e5b0ea5", - "0x953d4571c1b83279f6c5958727aaf9285d8b8cbdbfbaff51527b4a8cfdd73d3439ba862cdb0e2356e74987ff66d2c4d9", - "0xb765dae55d0651aca3b3eaef4ca477f0b0fda8d25c89dccd53a5573dd0c4be7faaadaa4e90029cdd7c09a76d4ce51b91", - "0xb6d7b7c41556c85c3894d0d350510b512a0e22089d3d1dd240ad14c2c2b0ce1f003388100f3154ad80ec50892a033294", - "0xa64561dc4b42289c2edf121f934bc6a6e283d7dce128a703f9a9555e0df7dda2825525dbd3679cd6ba7716de230a3142", - "0xa46c574721e8be4a3b10d41c71057270cca42eec94ca2268ee4ab5426c7ce894efa9fa525623252a6a1b97bcf855a0a5", - "0xa66d37f1999c9c6e071d2a961074c3d9fdcf9c94bf3e6c6ed82693095538dd445f45496e4c83b5333b9c8e0e64233adc", - "0xab13814b227a0043e7d1ff6365360e292aca65d39602d8e0a574d22d25d99ccb94417c9b73095632ff302e3d9a09d067", - "0xb2c445b69cff70d913143b722440d2564a05558d418c8ef847483b5196d7e581c094bae1dbb91c4499501cfa2c027759", - "0x87cbde089962d5f093324b71e2976edbe6ad54fb8834dd6e73da9585b8935fca1c597b4d525949699fdfa79686721616", - "0xa2c7e60966acb09c56cf9ad5bdcc820dcabf21ef7784970d10353048cf3b7df7790a40395561d1064e03109eaac0df98", - "0x8ea7b8af208678178553946b2ee9e68c0e751b34f3652409a5e66c40d3aee3a40ba6ffe2175ce16c6a81b78ecc597d02", - "0x960234239e1e3ea262e53d256ad41b2fe73f506b3d130732d0ee48819eb8a9c85bb5106a304874d8625afae682c34015", - "0x858459694c4e8fdafa6cdaee1184e1305ca6e102222b99b8e283dd9bb3ebf80e55d6c4d8831a072b813c8eceb8124d95", - "0xa30a8ce0f44aeb5590dc618c81c7cac441470ce79fd7881a8f2ea4ca5f9d848ebde762fcaee985cbd3d5990367403351", - "0xa83867643672248b07d3705813b56489453e7bc546cdba570468152d9a1bd04f0656034e7d03736ea156fc97c88dc37f", - "0xa7bb52e0fc58b940dc47ea4d0a583012ee41fad285aba1a60a6c54fa32cfe819146888c5d63222c93f90de15745efb2b", - "0x8627bcc853bdeaad37f1d0f7d6b30ada9b481ccdf79b618803673de8a142e8a4ce3e7e16caed1170a7332119bcdc10a9", - "0x8903d9dc3716b59e8e99e469bd9fde6f4bca857ce24f3a23db817012f1ea415c2b4656c7aeca31d810582bb3e1c08cc6", - "0x875169863a325b16f892ad8a7385be94d35e398408138bd0a8468923c05123d53dba4ce0e572ea48fcdadd9bd9faa47a", - "0xb255b98d46d6cc44235e6ce794cc0c1d3bd074c51d58436a7796ce6dc0ae69f4edaa3771b35d3b8a2a9acd2f6736fab3", - "0x9740c4d0ee40e79715a70890efda3455633ce3a715cbfc26a53e314ebbe61937b0346b4859df5b72eb20bcba96983870", - "0xa44ce22ab5ddc23953b02ec187a0f419db134522306a9078e1e13d5bf45d536450d48016a5e1885a346997003d024db0", - "0x90af81c08afdccd83a33f21d0dc0305898347f8bd77cc29385b9de9d2408434857044aec3b74cb72585338c122e83bb4", - "0x80e162a7656c9ae38efa91ae93e5bd6cb903f921f9f50874694b9a9e0e2d2595411963d0e3f0c2d536b86f83b6e4d6ef", - "0x8b49fa6babe47291f9d290df35e94e83be1946784b9c7867efd8bc97a12be453013939667164b24aeb53d8950288a442", - "0xa1df6435d718915df3da6dda61da1532a86e196dc7632703508679630f5f14d4cb44ce89eff489d7ff3fe599cc193940", - "0xafd44c143dbb94c71acc2a309c9c88b8847ef45d98479fccce9920db9b268e8e36f8db9f02ff4ee3cff01e548f719627", - "0xb2cf33d65d205e944b691292c2d9b0b124c9de546076dd80630742989f1ffd07102813c64d69ba2a902a928a08bce801", - "0xb9f295e9f9eca432b2d5c77d6316186027caca40a6d6713f41356497a507b6e8716fb471faf973aaa4e856983183c269", - "0xb3bd50c4b034473edce4b9be1171376a522899cb0c1a1ae7dc22dd2b52d20537cf4129797235084648ac4a3afc1fa854", - "0x8ef37683d7ca37c950ba4df72564888bedaf681931d942d0ea88ead5cc90f4cbef07985a3c55686a225f76f7d90e137d", - "0x82107855b330bc9d644129cebecf2efbfab90f81792c3928279f110250e727ce12790fd5117501c895057fa76a484fc0", - "0x816a5474c3b545fb0b58d3118cc3088a6d83aad790dbf93025ad8b94a2659cceba4fa6a6b994cb66603cc9aef683a5e3", - "0x8f633f9b31f3bb9b0b01ea1a8830f897ecd79c28f257a6417af6a5f64e6c78b66c586cf8d26586830bd007fb6279cd35", - "0xacb69d55a732b51693d4b11f7d14d21258d3a3af0936385a7ce61e9d7028a8fe0dd902bda09b33fb728bc8a1bc542035", - "0x8d099582ac1f46768c17bf5a39c13015cfe145958d7fc6ddfd2876ad3b1a55a383fbe940e797db2b2b3dc8a232f545dc", - "0x97a4dd488b70bf772348ececaca4cf87bc2875d3846f29fe6ef01190c5b030219b9e4f8137d49ea0cc50ca418024c488", - "0xb4d81148f93fa8ec0656bbfb5f9d96bbf5879fa533004a960faac9fd9f0fe541481935fdf1f9b5dd08dff38469ef81c5", - "0x8e9b2ae4fc57b817f9465610a77966caaff013229018f6c90fa695bd734cb713b78a345b2e9254b1aff87df58c1cd512", - "0x99eb7126e347c636e9a906e6bfdc7c8ca0c1d08580c08e6609889a5d515848c7ca0f32ab3a90c0e346f976a7883611f7", - "0x8ca87944aa3e398492b268bda0d97917f598bc0b28584aa629dfec1c3f5729d2874db422727d82219880577267641baa", - "0x88ab0e290dc9a6878d6b4e98891ff6bfc090e8f621d966493fcbe1336cc6848fcbb958d15abcfa77091d337da4e70e74", - "0x8956a2e1dc3ec5eb21f4f93a5e8f0600a06e409bb5ec54e062a1290dff9ce339b53fbbfc4d42b4eed21accea07b724d6", - "0x8d22220da9dc477af2bddb85c7073c742c4d43b7afee4761eba9346cadbcd522106ed8294281a7ef2e69883c28da0685", - "0x90dafd9a96db7e1d6bde424245305c94251d5d07e682198ae129cd77bd2907a86d34722cbde06683cc2ca67cebe54033", - "0xb5202e62cf8ea8e145b12394bd52fd09bda9145a5f78285b52fda4628c4e2ccfc2c208ecde4951bd0a59ac03fa8bc202", - "0x8959856793ba4acf680fb36438c9722da74d835a9fe25a08cf9e32d7800c890a8299c7d350141d2e6b9feceb2ebb636f", - "0xab0aa23c1cd2d095825a3456861871d298043b615ae03fcd9283f388f0deef3cc76899e7fde15899e3edf362b4b4657f", - "0x9603b333cc48fe39bea8d9824cfee6ac6c4e21668c162c196ecd1ff08ef4052ace96a785c36b8f7906fdcb6bc8802ddd", - "0x93bfecbc3c7cc03c563240e109850a74948f9fa078eb903b322368cda0b50888663a17953579578ba060b14dbf053024", - "0xb01f843b808cf7939a474de155a45462e159eb5044f00c6d77e0f7ec812720a3153209e971a971ccbf5ebee76ec4074f", - "0xb009e0567c3c75ed767247d06fa39049a4d95df3392d35a9808cb114accf934e78f765cd18a2290efef016f1918c7aeb", - "0xad35631df8331da3a12f059813dfa343d831225a392f9c7e641c7d23a6c1ad8df8e021201c9f6afb27c1575948d6bf68", - "0xa89c2a631d84128471c8ef3d24b6c35c97b4b9b5dad905c1a092fb9396ae0370e215a82308e13e90e7bb6ebcc455eb2a", - "0xb59c7f5fbfeb02f8f69e6cedef7ff104982551f842c890a14834f5e834b32de1148cf4b414a11809d53dd3f002b15d6a", - "0xaa6f267305b55fede2f3547bc751ba844ce189d0b4852022712b0aee474de54a257d4abcd95efe7854e33a912c774eba", - "0xafddd668f30cce70904577f49071432c49386ec27389f30a8223b5273b37e6de9db243aceb461a7dc8f1f231517463a9", - "0xb902a09da9157b3efa1d98f644371904397019d0c84915880628a646a3ad464a9d130fdc651315098179e11da643ad2e", - "0xb05f31957364b016c6f299ae4c62eede54cab8ea3871d49534828c8bdc6adbc6a04a708df268f50107d81d1384d983ae", - "0xb4c3f7284802e614ddf1f51640f29e7139aae891467d5f62778310372071793e56fbd770837b97d501191edd0da06572", - "0xb4eddb7c3775fb14fac7f63bb73b3cde0efa2f9a3b70e6a65d200765f6c4b466d3d76fcd4d329baee88e2aba183b8e69", - "0xa83e7dbae5a279f0cfd1c94e9849c58a3d4cecc6d6d44bb9b17508576ca347fca52c2c81371d946b11a09d4ed76ec846", - "0x8018ea17e2381c0233867670f9e04c8a47ace1207fdcf72dce61b6c280ba42d0a65f4b4e0b1070cc19c7bb00734974d9", - "0xaf90b541dfed22e181ff3ef4cf11f5e385fd215c1e99d988e4d247bc9dcee9f04f2182b961797c0bcc5f2aaa05c901a9", - "0xa37046e44cf35944e8b66df80c985b8a1aa7004a2fd0b81ac251638977d2ff1465f23f93ac0ce56296f88fdc591bbdd7", - "0xa735bd94d3be9d41fcd764ec0d8d7e732c9fc5038463f7728fd9d59321277e2c73a45990223bd571dab831545d46e7aa", - "0x94b32dcb86f5d7e83d70a5b48fe42c50f419be2f848f2d3d32ee78bf4181ab18077a7666eedb08607eece4de90f51a46", - "0xa7f0804cafbf513293485afc1b53117f0cbfaea10919e96d9e4eb06f0c96535e87065d93f3def1bbc42044dbb00eb523", - "0xaaaad1166d7f19f08583dd713275a71a856ab89312f84ca8078957664924bb31994b5c9a1210d0c41b085be4058ed52e", - "0xa1757aac9f64f953e68e680985a8d97c5aac8688b7d90f4db860166dd3d6119e8fca7d700a9530a2b9ba3932c5e74e33", - "0x98cada5db4a1430c272bfc1065fb685872e664ed200d84060ee9f797d0a00864f23943e0fb84ba122a961996a73dfb14", - "0xa5e609f716dc7729d1247f40f9368a2e4a15067e1dd6a231fece85eeefb7e7d4a5ac8918fb376debd79d95088750b2ca", - "0xb5365eb8caab8b1118619a626ff18ce6b2e717763f04f6fa8158cdca530c5779204efa440d088083f1a3685454aa0555", - "0xa6e01b8da5f008b3d09e51a5375d3c87c1da82dff337a212223e4d0cdb2d02576d59f4eef0652d6b5f2fc806d8c8149c", - "0xae310f613d81477d413d19084f117248ad756572c22a85b9e4c86b432e6c602c4a6db5edf2976e11f7353743d679e82a", - "0xa1f219c0b8e8bb8a9df2c6c030acbb9bbfa17ba3db0366f547da925a6abb74e1d7eb852bd5a34bae6ac61d033c37e9dc", - "0xa2087fa121c0cdd5ea495e911b4bc0e29f1d5c725aadfb497d84434d2291c350cdaa3dc8c85285f65a7d91b163789b7a", - "0x929c63c266da73d726435fa89d47041cfe39d4efa0edce7fc6eca43638740fbc82532fd44d24c7e7dd3a208536025027", - "0x91c1051dcc5f52ad89720a368dddd2621f470e184e746f5985908ba34e1d3e8078a32e47ab7132be780bea5277afecb0", - "0xae089b90ba99894d5a21016b1ea0b72a6e303d87e59fb0223f12e4bb92262e4d7e64bfdbdb71055d23344bc76e7794b2", - "0x8b69aa29a6970f9e66243494223bad07ac8f7a12845f60c19b1963e55a337171a67bdc27622153016fce9828473a3056", - "0x95ca6b08680f951f6f05fd0d180d5805d25caf7e5bda21c218c1344e661d0c723a4dfc2493642be153793c1b3b2caaa4", - "0xa4789dc0f2a07c794dab7708510d3c893d82ddbd1d7e7e4bbbeca7684d9e6f4520fb019b923a06c7efab0735f94aa471", - "0x93c4f57a3cf75085f5656b08040f4cd49c40f1aab6384a1def4c5c48a9fe4c03514f8e61aabe2cfa399ff1ccac06f869", - "0xb6c37f92c76a96b852cd41445aa46a9c371836dd40176cc92d06666f767695d2284a2780fdfd5efc34cf6b18bcfb5430", - "0x9113e4575e4b363479daa7203be662c13d7de2debcda1c142137228aeead2c1c9bc2d06d93a226302fa63cc75b7353ec", - "0xb70addeb5b842ac78c70272137f6a1cef6b1d3a551d3dd906d9a0e023c8f49f9b6a13029010f3309d0b4c8623a329faf", - "0xb976a5132b7eb42d5b759c2d06f87927ef66ecd6c94b1a08e4c9e02a4ce7feca3ac91f9479daa1f18da3d4a168c2ba77", - "0x8fdab795af64b16a7ddf3fad11ab7a85d10f4057cf7716784184960013baa54e7ba2050b0e036dc978ff8c9a25dc5832", - "0xb2c982ad13be67d5cdc1b8fac555d4d1ec5d25f84e58b0553a9836f8f9e1c37582d69ad52c086a880a08b4efcccd552e", - "0x810661d9075ae6942735215f2ab46d60763412e1f6334e4e00564b6e5f479fc48cf37225512abbccf249c0ca225fc935", - "0xa0c4bf00a20f19feff4004004f08231b4c6c86ac4ed57921eea28d7dea32034f3f4ab5b7ded7184f6c7ffbf5847232ad", - "0xb2bb5a9eea80bf067f3686a488529d9c2abd63fc9e1d4d921b1247ef86d40cd99e0a8b74f750e85c962af84e84e163a6", - "0x887ee493c96d50f619ba190ce23acddc5f31913e7a8f1895e6339d03794ecefd29da5f177d1d25bc8df8337ae963fc7b", - "0xb7966fb07029d040f2228efa2cfcd04341e4666c4cf0b653e6e5708631aa2dd0e8c2ac1a62b50c5a1219a2737b82f4f7", - "0x92234cfd6b07f210b82db868f585953aafbcbc9b07b02ded73ff57295104c6f44a16e2775ca7d7d8ee79babb20160626", - "0x8d3cd7f09c6fd1072bc326ff329e19d856e552ac2a9f20274bc9752527cd3274142aa2e32b65f285fb84bc3adaaea3cc", - "0x8caed1cb90d8cd61e7f66edc132672172f4fa315e594273bb0a7f58a75c30647ec7d52eda0394c86e6477fbc352f4fe8", - "0xae192194b09e9e17f35d8537f947b56f905766c31224e41c632c11cd73764d22496827859c72f4c1ab5fd73e26175a5d", - "0x8b7be56aac76d053969e46882d80a254e89f55c5ab434883cbafc634a2c882375898074a57bc24be3c7b2c56401a7842", - "0x98bc4a7a9b05ba19f6b85f3ee82b08bed0640fd7d24d4542eb7a7f7fde443e880bdb6f5499bd8cb64e1ddd7c5f529b19", - "0xa5a41eaa5e9c1d52b00d64ab72bc9def6b9d41972d80703e9bfe080199d4e476e8833a51079c6b0155b78c3ab195a2a7", - "0xa0823f6f66465fd9be3769c164183f8470c74e56af617f8afd99b742909d1a51f2e0f96a84397597afbd8eeaabb51996", - "0x801da41d47207bdd280cc4c4c9753a0f0e9d655e09e0be5f89aeed4ce875a904f3da952464399bf8efc2398940d5fba2", - "0xa719314085fd8c9beac4706c24875833d59a9a59b55bca5da339037c0a5fc03df46dbecb2b4efcfed67830942e3c4ea1", - "0xa75dde0a56070bb7e9237b144ea79f578d413a1cbbd1821cee04f14f533638b24f46d88a7001e92831843b37ed7a709f", - "0xa6b4ef8847a4b980146e1849e1d8ab38695635e0394ca074589f900ce41fa1bb255938dc5f37027523bac6a291779bef", - "0xb26d84dfd0b7bd60bcfdbea667350462a93dca8ff5a53d6fc226214dcb765fada0f39e446a1a87f18e4e4f4a7133155f", - "0xae7bd66cc0b72f14ac631ff329a5ca4958a80ba7597d6da049b4eb16ac3decde919ca5f6f9083e6e541b303fb336dc2f", - "0xa69306e6bfbbc10de0621cffb13c586e2fcfd1a80935e07c746c95651289aec99066126a6c33cb8eb93e87d843fc631f", - "0xa47e4815585865218d73c68ba47139568ea7ae23bfa863cb914a68454242dd79beaec760616b48eea74ceab6df2298dd", - "0xb2da3cfb07d0721cd226c9513e5f3ace98ed2bc0b198f6626b8d8582268e441fa839f5834f650e2db797655ca2afa013", - "0xb615d0819554f1a301a704d3fc4742bd259d04ad75d50bccee3a949b6226655f7d623301703506253cca464208a56232", - "0x85e06ed5797207f0e7ae85909e31776eb9dae8af2ec39cc7f6a42843d94ea1de8be2a3cdadfcbe779da59394d4ffeb45", - "0x8c3529475b5fdbc636ee21d763f5ec11b8cb040a592116fb609f8e89ca9f032b4fa158dd6e9ceab9aceb28e067419544", - "0xaccddb9c341f32be82b6fa2ef258802c9ae77cd8085c16ec6a5a83db4ab88255231b73a0e100c75b7369a330bfc82e78", - "0x93b8e4c6e7480948fa17444b59545a5b28538b8484a75ad6bc6044a1d2dbd76e7c44970757ca53188d951dc7347d6a37", - "0x90111721d68b29209f4dc4cfb2f75ab31d15c55701922e50a5d786fb01707ab53fcec08567cd366362c898df2d6e0e93", - "0xb60a349767df04bd15881c60be2e5cc5864d00075150d0be3ef8f6b778715bebca8be3be2aa9dbdc49f1a485aeb76cda", - "0xb8d5a967fdd3a9bcf89a774077db39ef72ca9316242f3e5f2a350202102d494b2952e4c22badecd56b72ba1eea25e64b", - "0x8499ebd860f31f44167183b29574447b37a7ee11efcc9e086d56e107b826b64646b1454f40f748ccac93883918c89a91", - "0x99c35e529782db30f7ccab7f31c225858cf2393571690b229ece838ec421a628f678854a1ddbd83fa57103ccebd92c7f", - "0x99817660d8b00cbe03ec363bcdc5a77885586c9e8da9e01a862aca0fc69bf900c09b4e929171bc6681681eae10450541", - "0x8055e130964c3c2ebd980d3dc327a40a416bcdbf29f480480a89a087677a1fb51c823b57392c1db72f4093597100b8d3", - "0x877eaddef845215f8e6f9ed24060c87e3ab6b1b8fbb8037d1a57e6a1e8ed34d00e64abb98d4bf75edb5c9788cbdccbef", - "0xb5432bbff60aeae47f2438b68b123196dfb4a65cc875b8e080501a4a44f834b739e121bec58d39ac36f908881e4aa8ab", - "0xb3c3f859b7d03ff269228c0f9a023b12e1231c73aba71ad1e6d86700b92adc28dfa3757c052bbc0ba2a1d11b7fda4643", - "0xab8a29f7519a465f394ef4a5b3d4924d5419ca1489e4c89455b66a63ac430c8c9d121d9d2e2ed8aa1964e02cd4ebac8c", - "0x866ae1f5c2a6e159f2e9106221402d84c059f40d166fab355d970773189241cd5ee996540d7c6fc4faf6f7bcff967dce", - "0x973a63939e8f1142a82b95e699853c1e78d6e05536782b9bb178c799b884f1bc60177163a79a9d200b5ff4628beeb9e7", - "0xa5fc84798d3e2d7632e91673e89e968f5a67b7c8bb557ea467650d6e05e7fe370e18d9f2bdd44c244978295cf312dc27", - "0xb328fe036bcd0645b0e6a15e79d1dd8a4e2eda128401a4e0a213d9f92d07c88201416fc76193bb5b1fe4cb4203bab194", - "0x99239606b3725695a570ae9b6fb0fb0a34ad2f468460031cfa87aa09a0d555ff606ff204be42c1596c4b3b9e124b8bd6", - "0xaf3432337ca9d6cce3574e23e5b7e4aa8eda11d306dc612918e970cc7e5c756836605a3391f090a630bac0e2c6c42e61", - "0x8a545b3cb962ce5f494f2de3301de99286c4d551eaa93a9a1d6fef86647321834c95bf754c62ec6c77116a21494f380d", - "0x8f9b8ea4c25469c93556f1d91be583a5f0531ac828449b793ba03c0a841c9c73f251f49dd05cbb415f5d26e6f6802c99", - "0xa87199e33628eeffd3aff114e81f53dd54fba61ba9a9a4d7efdbff64503f25bc418969ab76ef1cf9016dd344d556bb29", - "0xa2fda05a566480602274d7ffcaefdd9e94171286e307581142974f57e1db1fa21c30be9e3c1ac4c9f2b167f92e7c7768", - "0xa6235d6a23304b5c797efb2b476ed02cb0f93b6021a719ae5389eb1e1d032944ae4d69aec2f29fcd6cbc71a6d789a3ba", - "0xa7f4a73215f7e99e2182c6157dd0f22e71b288e696a8cff2450689a3998f540cfb82f16b143e90add01b386cb60d8a33", - "0x922d8f9cd55423f5f6a60d26de2f8a396ac4070a6e2dc956e50c2a911906aa364d4718aea29c5b61c12603534e331e7e", - "0x96d7fdf5465f028fc28f21fbfe14c2db2061197baf26849e6a0989a4ea7d5e09ab49a15ba43a5377b9354d01e30ce860", - "0x8f94c4255a0fc1bd0fa60e8178c17f2a8e927cac7941c5547d2f8f539e7c6ed0653cab07e9fb1f2c56cdd03bb876512a", - "0x95984c10a2917bfa6647ebce69bf5252d9e72d9d15921f79b2c6d7c15ee61342b4fb8a6d34838e07132b904f024ded04", - "0x93e65e765a574277d3a4d1d08ca2f2ff46e9921a7806ca8ca3d8055f22d6507744a649db7c78117d9168a1cbdb3bbc61", - "0x8d453b7364662dc6f36faf099aa7cbbe61151d79da7e432deba7c3ed8775cfe51eaf1ba7789779713829dde6828e189a", - "0xacffa3ee6c75160286090162df0a32a123afb1f9b21e17fd8b808c2c4d51a4270cab18fba06c91ef9d22e98a8dc26cdd", - "0xa5597cc458186efa1b3545a3926f6ecaaa6664784190e50eed1feac8de56631bee645c3bac1589fa9d0e85feb2be79d4", - "0x87ba9a898df9dfa7dabc4ab7b28450e4daf6013340e329408d1a305de959415ab7315251bad40511f917dfc43974e5f0", - "0xa598778cf01d6eef2c6aabc2678e1b5194ee8a284ebd18a2a51a3c28a64110d5117bcbf68869147934e600572a9e4c8a", - "0x84c69a4ad95861d48709f93ade5ac3800f811b177feb852ebcd056e35f5af5201f1d8a34ab318da8fe214812d0a7d964", - "0x9638a237e4aed623d80980d91eda45e24ebf48c57a25e389c57bd5f62fa6ffa7ca3fb7ae9887faf46d3e1288af2c153b", - "0x800f975721a942a4b259d913f25404d5b7b4c5bf14d1d7e30eee106a49cb833b92058dab851a32ee41faf4ef9cb0dea4", - "0xb9127a34a59fed9b5b56b6d912a29b0c7d3cb9581afc9bd174fc308b86fdb076f7d436f2abc8f61cef04c4e80cd47f59", - "0x8004eda83f3263a1ccfc8617bc4f76305325c405160fb4f8efeff0662d605e98ba2510155c74840b6fe4323704e903c4", - "0xaa857b771660d6799ff03ccad1ab8479e7f585a1624260418fc66dc3e2b8730cfa491d9e249505141103f9c52f935463", - "0x98b21083942400f34cde9adbe1977dee45ba52743dc54d99404ad9da5d48691ddea4946f08470a2faad347e9535690c7", - "0xa4b766b2faec600a6305d9b2f7317b46f425442da0dc407321fc5a63d4571c26336d2bccedf61097f0172ec90fb01f5f", - "0xb9736619578276f43583de1e4ed8632322ea8a351f3e1506c5977b5031d1c8ad0646fb464010e97c4ddb30499ddc3fb0", - "0x973444ffaff75f84c17f9a4f294a13affd10e2bceed6b4b327e4a32c07595ff891b887a9f1af34d19766d8e6cb42bfd1", - "0xb09ce4964278eff81a976fbc552488cb84fc4a102f004c87179cb912f49904d1e785ecaf5d184522a58e9035875440ef", - "0xb80c2aa3d0e52b4d8b02c0b706e54b70c3dbca80e5e5c6a354976721166ea0ca9f59c490b3e74272ef669179f53cb50d", - "0x8e52fa5096ff960c0d7da1aa4bce80e89527cdc3883eba0c21cb9a531088b9d027aa22e210d58cf7cbc82f1ec71eb44f", - "0x969f85db95f455b03114e4d3dc1f62a58996d19036513e56bee795d57bf4ed18da555722cd77a4f6e6c1a8e5efe2f5d7", - "0xab84b29b04a117e53caea394a9b452338364c45a0c4444e72c44132a71820b96a6754828e7c8b52282ad8dca612d7b6a", - "0x83e97e9ab3d9e453a139c9e856392f4cef3ec1c43bce0a879b49b27a0ce16f9c69063fd8e0debbe8fabafc0621bc200c", - "0x8c138ebdf3914a50be41be8aa8e2530088fb38af087fa5e873b58b4df8e8fd560e8090c7a337a5e36ef65566409ad8f3", - "0xa56da9db2f053516a2141c1a8ed368ae278ab33a572122450249056857376d1dffc76d1b34daf89c86b6fe1ead812a0c", - "0xa3233ea249f07531f5bc6e94e08cea085fd2b2765636d75ff5851f224f41a63085510db26f3419b031eb6b5143735914", - "0xb034bb6767ce818371c719b84066d3583087979ba405d8fbb2090b824633241e1c001b0cb0a7856b1af7a70e9a7b397e", - "0x8722803fe88877d14a4716e59b070dd2c5956bb66b7038f6b331b650e0c31230c8639c0d87ddc3c21efc005d74a4b5cc", - "0x8afe664cb202aacf3bd4810ebf820c2179c11c997f8c396692a93656aa249a0df01207c680157e851a30330a73e386b9", - "0xa999e86319395351d2b73ff3820f49c6516285e459224f82174df57deb3c4d11822fd92cbbed4fc5a0a977d01d241b19", - "0x9619408e1b58b6610d746b058d7b336d178e850065ba73906e08e748651e852f5e3aab17dcadcb47cc21ff61d1f02fcf", - "0x947cf9c2ed3417cd53ea498d3f8ae891efe1f1b5cd777e64cec05aba3d97526b8322b4558749f2d8a8f17836fb6e07aa", - "0xaec2fdae2009fda6852decb6f2ff24e4f8d8ca67c59f92f4b0cf7184be72602f23753ed781cf04495c3c72c5d1056ffe", - "0x8dba3d8c09df49fbfc9506f7a71579348c51c6024430121d1c181cad7c9f7e5e9313c1d151d46d4aa85fb0f68dd45573", - "0xb6334cb2580ae33720ebf91bb616294532a1d1640568745dcda756a3a096786e004c6375728a9c2c0fb320441e7d297a", - "0x9429224c1205d5ecd115c052b701c84c390f4e3915275bb8ce6504e08c2e9b4dd67b764dd2ea99f317b4c714f345b6ff", - "0xabe421db293f0e425cfd1b806686bdfd8fdbac67a33f4490a2dc601e0ddbf69899aa9a119360dad75de78c8c688ca08b", - "0x95c78bffed9ae3fff0f12754e2bd66eb6a9b6d66a9b7faaeb7a1c112015347374c9fe6ce14bf588f8b06a78e9a98f44c", - "0xac08f8b96b52c77d6b48999a32b337c5ad377adf197cda18dbdf6e2a50260b4ee23ca6b983f95e33f639363e11229ee4", - "0x911a0e85815b3b9f3ba417da064f760e84af94712184faeb9957ddd2991dee71c3f17e82a1a8fbeec192b0d73f0ebce7", - "0xaa640bd5cb9f050568a0ad37168f53b2f2b13a91e12b6980ca47ae40289cf14b5b89ddd0b4ca452ce9b1629da0ce4b5d", - "0x907486f31b4ecea0125c1827007ea0ecb1c55cadb638e65adc9810ca331e82bb2fd87e3064045f8d2c5d93dc6c2f5368", - "0x8cbfaf4ce0bbbf89208c980ff8b7bc8f3cfef90f0fe910f463cb1c0f8e17cce18db120142d267045a00ba6b5368f0dd3", - "0x9286f08f4e315df470d4759dec6c9f8eacef345fc0c0b533ad487bb6cfefa8c6c3821a22265c9e77d34170e0bc0d078b", - "0x94a3c088bc1a7301579a092b8ece2cefc9633671bc941904488115cd5cb01bd0e1d2deef7bdccb44553fd123201a7a53", - "0x8f3d0114fbf85e4828f34abb6d6fddfa12789d7029d9f1bb5e28bc161c37509afdab16c32c90ec346bc6a64a0b75726f", - "0xa8ed2d774414e590ec49cb9a3a726fafd674e9595dd8a1678484f2897d6ea0eea1a2ee8525afac097b1f35e5f8b16077", - "0x9878789ff33b11527355a317343f34f70c7c1aa9dc1eca16ca4a21e2e15960be8a050ec616ffb97c76d756ce4bce2e90", - "0x854e47719dae1fe5673cacf583935122139cf71a1e7936cf23e4384fbf546d48e9a7f6b65c3b7bf60028e5aa1234ba85", - "0xaf74bdda2c6772fe9a02d1b95e437787effad834c91c8174720cc6e2ea1f1f6c32a9d73094fc494c0d03eef60b1a0f05", - "0x80a3e22139029b8be32cb167d3bc9e62d16ca446a588b644e53b5846d9d8b7ab1ad921057d99179e41515df22470fb26", - "0x86c393afd9bd3c7f42008bba5fe433ec66c790ebd7aa15d4aeaf9bb39a42af3cfaf8c677f3580932bbd7ada47f406c8c", - "0x90433c95c9bb86a2c2ddcf10adccb521532ebd93db9e072671a4220f00df014e20cd9ce70c4397567a439b24893808dc", - "0x95b2c170f08c51d187270ddc4f619300b5f079bbc89dbca0656eae23eecc6339bf27fa5bf5fd0f5565d4021105e967d2", - "0x8e5eced897e2535199951d4cff8383be81703bca3818837333dd41a130aa8760156af60426ceadb436f5dea32af2814c", - "0xa254a460ebefbe91d6e32394e1c8f9075f3e7a2bb078430ac6922ab14d795b7f2df1397cb8062e667d809b506b0e28d4", - "0xac2062e8ca7b1c6afb68af0ebab31aebd56fc0a0f949ef4ea3e36baf148681619b7a908facf962441905782d26ecbdb5", - "0x8b96af45b283b3d7ffeec0a7585fc6b077ea5fd9e208e18e9f8997221b303ab0ce3b5bafa516666591f412109ce71aa5", - "0xafd73baada5a27e4fa3659f70083bf728d4dc5c882540638f85ea53bf2b1a45ddf50abc2458c79f91fb36d13998c7604", - "0xa5d2fff226e80cb2e9f456099812293333d6be31dd1899546e3ad0cd72b2a8bcb45ec5986e20faa77c2564b93983210c", - "0xa8c9b8de303328fbdaccf60f4de439cf28f5360cf4104581dc2d126bc2e706f49b7281723487ff0eaf92b4cc684bc167", - "0xa5d0d5849102bf1451f40e8261cb71fc57a49e032773cb6cd7b137f71ee32438d9e958077ffafce080a116ccc788a2d4", - "0x80716596f502d1c727d5d2f1469ce35f15e2dbd048d2713aa4975ee757d09c38d20665326bd63303cfe7e820b6de393d", - "0x97baf29b20f3719323cc1d5de23eaa4899dc4f4e58f6c356ec4c3ad3896a89317c612d74e0d3ab623fe73370c5972e2f", - "0xb58bdc9aa5061bf6e5add99a7443d7a8c7ba8f6875b8667d1acbe96fc3ecafbdcc2b4010cb6970a3b849fff84660e588", - "0xb6be68728776d30c8541d743b05a9affc191ad64918fdbd991d2ddd4b32b975c4d3377f9242defef3805c0bfb80fbac7", - "0xb0cddace33333b8a358acad84b9c83382f0569d3854b4b34450fd6f757d63c5bdab090e330b0f86e578f22c934d09c36", - "0x854bd205d6051b87f9914c8c2494075d7620e3d61421cc80f06b13cea64fd1e16c62c01f107a5987d10b8a95a8416ad9", - "0x80351254a353132300ba73a3d23a966f4d10ce9bf6eae82aedb6cdc30d71f9d08a9dd73cb6441e02a7b2ad93ad43159c", - "0x937aae24fb1b636929453fc308f23326b74c810f5755d9a0290652c9c2932ad52cc272b1c83bd3d758ef7da257897eae", - "0xb84d51ef758058d5694ffeac6d8ce70cef8d680a7902f867269c33717f55dd2e57b25347841d3c0872ae5f0d64f64281", - "0xa4b31bb7c878d5585193535b51f04135108134eff860f4eac941053155f053d8f85ff47f16268a986b2853480a6e75e6", - "0x93543f0828835186a4af1c27bdf97b5dd72b6dfa91b4bf5e759ff5327eaf93b0cb55d9797149e465a6b842c02635ffe5", - "0xafdac9e07652bf1668183664f1dd6818ef5109ee9b91827b3d7d5970f6a03e716adcc191e3e78b0c474442a18ad3fc65", - "0x9314077b965aa2977636ae914d4a2d3ce192641a976ffa1624c116828668edbfbe5a09e3a81cb3eed0694566c62a9757", - "0xb395ddcf5082de6e3536825a1c352802c557b3a5118b25c29f4c4e3565ecaaf4bdd543a3794d05156f91fc4ceadc0a11", - "0xb71f774aad394c36609b8730e5be244aaebfff22e0e849acc7ee9d33bedc3ec2e787e0b8b2ffe535560fcd9e15a0897e", - "0x92e9409fa430f943a49bce3371b35ac2efb5bc09c88f70ff7120f5e7da3258a4387dfc45c8b127f2ef2668679aeb314e", - "0x8ef55bef7b71952f05e20864b10f62be45c46e2dca0ef880a092d11069b8a4aa05f2e0251726aca1d5933d7dea98f3f8", - "0xaad3fba9e09fae885cdeef45dfafa901419f5156fb673818f92a4acc59d0e2e9870b025e711de590a63fd481164f3aa8", - "0xb444d52af545dd3a2d3dd94e6613816b154afea0c42b96468aceb0c721395de89e53e81a25db857ca2e692dcb24ba971", - "0x88b279fe173007e64fe58f2c4adba68a1f538dbd3d32d175aa0d026bbb05b72a0c9f5d02b8201a94adb75fe01f6aa8b2", - "0x88494cea4260741c198640a079e584cabfea9fcfb8bcf2520c9becd2419cde469b79021e5578a00d0f7dbc25844d2683", - "0x94f3cce58837c76584b26426b9abdb45f05fee34dd9e5914b6eae08e78b7262ed51c4317031dab1ad716f28b287f9fc2", - "0xb8c7ed564f54df01c0fbd5a0c741beed8183ce0d7842dc3a862a1b335de518810077314aa9d6054bb939663362f496da", - "0x81c153320d85210394d48340619d5eb41304daea65e927266f0262c8a7598321aba82ad6c3f78e5104db2afd2823baca", - "0xab6695a8d48a179e9cd32f205608359cf8f6a9aead016252a35b74287836aa395e76572f21a3839bec6a244aa49573e5", - "0x920ed571539b3002a9cd358095b8360400e7304e9a0717cc8c85ab4a0514a8ad3b9bf5c30cb997647066f93a7e683da9", - "0xa7ec7c194d1e5103bc976e072bf1732d9cb995984d9a8c70a8ee55ce23007f21b8549ad693f118aa974f693ed6da0291", - "0x87a042d6e40c2951a68afc3ccf9646baf031286377f37f6ac47e37a0ec04d5ac69043757d7dff7959e7cd57742017a8d", - "0xb9f054dd8117dd41b6e5b9d3af32ee4a9eebef8e4a5c6daa9b99c30a9024eabeae850ab90dbdb188ca32fd31fd071445", - "0xa8386da875799a84dc519af010eaf47cdbc4a511fe7e0808da844a95a3569ce94054efd32a4d3a371f6aba72c5993902", - "0x8b3343a7cf4ffb261d5f2dbd217fb43590e00feac82510bdf73b34595b10ee51acae878a09efebc5a597465777ef4c05", - "0x8312a5f1ea4f9e93578e0f50169286e97884a5ed17f1780275ab2b36f0a8aa1ab2e45c1de4c8bce87e99e3896af1fa45", - "0xb461198cb7572ac04c484a9454954e157bdd4db457816698b7290f93a10268d75a7e1211e757c6190df6144bbb605d91", - "0x9139764a099580d6f1d462c8bf7d339c537167be92c780e76acb6e638f94d3c54b40ed0892843f6532366861e85a515a", - "0x8bb70acb3c9e041b4fc20e92ba0f3f28f0d5c677bcb017af26f9171e07d28c3c0729bef72457231e3512f909455a13a2", - "0x93301a18e5064c55fcfe8e860fab72da1b89a824ca77c8932023b7c79e4a51df93a89665d308a8d3aa145e46ebe6a0ad", - "0xae3bca496fbd70ce44f916e2db875b2ce2e1ded84edd2cebc0503bdfdec40ec30e1d9afb4eb58c8fa23f7b44e71d88f8", - "0x93cb3a918c95c5d973c0cb7621b66081ed81fba109b09a5e71e81ca01ec6a8bb5657410fdec453585309ef5bf10d6263", - "0x95a50b9b85bb0fc8ff6d5f800d683f0f645e7c2404f7f63228a15b95ce85a1f8100e2e56c0acee19c36ed3346f190e87", - "0x816cc4d9337461caca888809b746ab3713054f5b0eac823b795a1a9de9417c58e32a9f020fef807908fa530cbf35dee8", - "0xa9c2890c2dd0d5d7aedc4cca7f92764086c50f92f0efd2642c59920d807086031bfe2d3ba574318db236c61a8f5f69c2", - "0xad0d5c8c80bddfe14bdaf507da96dc01dc9941aecc8ad3b64513d0a00d67c3f4b4659defb6839b8b18d8775e5344c107", - "0x9047c9fad6ef452e0219e58e52c686b620e2eb769571021e3524bd7eac504f03b84834b16b849d42b3d75c601fd36bb7", - "0xa04dd988fed91fb09cb747a3ac84efe639d7d355524cd7dee5477ecbcdec44d8ac1cec2c181755dcfdb77e9594fb3c5b", - "0xb0ea0c725debd1cec496ced9ce48f456f19af36e8b027094bf38fa37de9b9b2d10282363ea211a93a34a0a5387cace5d", - "0xb5fc46e2bb3e4653ea5e6884dcb3c14e401a6005685ee5a3983644b5b92300b7066289159923118df4332aac52045b8c", - "0x841fc5b26b23226e725e29802da86b35e4f5e3babc8b394f74e30fd5dec6d3840b19a9a096625ce79a4f1edae6369700", - "0x8fd2bbbeea452451def3659bbe0ceb396120ebe8f81eee1ea848691614422c81d7c3e6a7a38032b4120b25c5ffa8f0c2", - "0x9131ce3d25c3d418f50c0ab99e229d4190027ee162b8ba7c6670420ea821831dec1294ac00d66c50fac61c275a9e2c71", - "0x99ec6eafe0eb869d128158cee97b984fb589e1af07699247946e4a85db772289dff3084d224a6f208005c342f32bbd73", - "0xac100fbbe7c2bf00cc56fcd5aa1f27181f82c150c53bbb1e15d2c18a51ed13dcfa7bccab85821b8ddddf493603e38809", - "0xaffd73a458d70c0d9d221e0c2da4348fed731f6b34c0b3e2d5711ba432e85a1ec92e40b83b246a9031b61f5bc824be47", - "0x8ed30ed817816a817e9e07374ef1f94405a7e22dd0096aeaae54504382fc50e7d07b4f1186c1792fc25ea442cd7edc6b", - "0xa52370cfe99a35fa1405aeca9f922ad8d31905e41f390e514ea8d22ee66469637d6c2d4d3a7ee350d59af019ae5a10a4", - "0x8d0b439741c57b82c8e4b994cf3956b5aeaee048b17e0a1edb98253a8d7256f436d8b2f36b7e12504132dbf91f3376b1", - "0x8caac7e1a4486c35109cff63557a0f77d0e4ca94de0817e100678098a72b3787a1c5afc7244991cebcd1f468e18d91d4", - "0xa729a8e64b7405db5ebfb478bb83b51741569331b88de80680e9e283cc8299ba0de07fcf252127750f507e273dc4c576", - "0xa30545a050dad030db5583c768a6e593a7d832145b669ad6c01235813da749d38094a46ac3b965700230b8deacd91f82", - "0x9207e059a9d696c46fa95bd0925983cd8e42aefd6b3fb9d5f05420a413cbc9e7c91213648554228f76f2dd757bde0492", - "0xa83fa862ae3a8d98c1e854a8b17181c1025f4f445fbc3af265dc99e44bbd74cfa5cc25497fb63ee9a7e1f4a624c3202c", - "0x84cdfc490343b3f26b5ad9e1d4dcf2a2d373e05eb9e9c36b6b7b5de1ce29fda51383761a47dbd96deca593a441ccb28e", - "0x881a1aa0c60bb0284a58b0a44d3f9ca914d6d8fa1437315b9ad2a4351c4da3ee3e01068aa128284a8926787ea2a618d1", - "0xaace78e497b32fbff4df81b1b2de69dbc650645e790953d543282cb8d004a59caf17d9d385673a146a9be70bf08a2279", - "0xaa2da4760f1261615bffd1c3771c506965c17e6c8270c0f7c636d90428c0054e092247c3373eca2fb858211fdb17f143", - "0xacb79f291b19e0aa8edb4c4476a172834009c57e0dcc544c7ce95084488c3ad0c63ffd51c2b48855e429b6e1a9555433", - "0x814b58773a18d50a716c40317f8b80362b6c746a531776a9251c831d34fb63e9473197c899c0277838668babc4aa0ecb", - "0xb1f69522b0f7657d78bd1ee3020bcce3447116bf62c146d20684537d36cafb5a7a1531b86932b51a70e6d3ce0808a17e", - "0x8549712c251ef382f7abe5798534f8c8394aa8bcecdca9e7aa1a688dc19dc689dcd017a78b118f3bd585673514832fe4", - "0x912a04463e3240e0293cfc5234842a88513ff930c47bd6b60f22d6bc2d8404e10270d46bf6900fee338d8ac873ebb771", - "0xa327cb7c3fada842e5dd05c2eeedd6fcd8cf2bfb2f90c71c6a8819fb5783c97dd01bd2169018312d33078b2bc57e19f7", - "0xb4794f71d3eceed331024a4cee246cc427a31859c257e0287f5a3507bfbd4d3486cb7781c5c9c5537af3488d389fe03e", - "0x82ffcb418d354ed01688e2e8373a8db07197a2de702272a9f589aed08468eab0c8f14e6d0b3146e2eb8908e40e8389c5", - "0x910b73421298f1315257f19d0dfd47e79d7d2a98310fb293f704e387a4dc84909657f0f236b70b309910271b2f2b5d46", - "0xa15466397302ea22f240eb7316e14d88376677b060c0b0ae9a1c936eb8c62af8530732fc2359cfd64a339a1c564f749b", - "0xa8091975a0d94cdc82fbaff8091d5230a70d6ea461532050abbdfee324c0743d14445cfe6efe6959c89a7c844feaa435", - "0xa677d1af454c7b7731840326589a22c9e81efbbf2baf3fdeaf8ea3f263a522584fbca4405032c4cdf4a2a6109344dfc8", - "0x894e6ffa897b6e0b37237e6587a42bbc7f2dd34fb09c2e8ac79e2b25b18180e158c6dc2dd26761dba0cfed1fb4eb4080", - "0x928d31b87f4fe8fe599d2c9889b0ff837910427ba9132d2fba311685635458041321ae178a6331ed0c398efe9d7912f0", - "0xafc1c4a31f0db24b53ee71946c3c1e1a0884bd46f66b063a238e6b65f4e8a675faa844e4270892035ef0dae1b1442aa0", - "0xa294fcb23d87cf5b1e4237d478cac82ba570649d425b43b1e4feead6da1f031e3af0e4df115ca46689b9315268c92336", - "0x85d12fd4a8fcfd0d61cbf09b22a9325f0b3f41fb5eb4285b327384c9056b05422d535f74d7dc804fb4bab8fb53d556bd", - "0x91b107d9b0ea65c48128e09072acd7c5949a02dd2a68a42ff1d63cf528666966f221005c2e5ca0a4f85df28459cdede6", - "0x89aa5dc255c910f439732fcd4e21341707e8dd6689c67c60551a8b6685bd3547e3f47db4df9dfadd212405f644c4440b", - "0x8c307d6b827fa1adcf0843537f12121d68087d686e9cc283a3907b9f9f36b7b4d05625c33dab2b8e206c7f5aabd0c1e5", - "0x843f48dadf8523d2b4b0db4e01f3c0ea721a54d821098b578fcaa6433e8557cadfea50d16e85133fa78f044a3e8c1e5b", - "0x9942eb8bd88a8afa9c0e3154b3c16554428309624169f66606bfb2814e8bac1c93825780cf68607f3e7cffe7bf9be737", - "0xb7edb0c7637a5beb2332f2ae242ba4732837f9da0a83f00f9e9a77cf35516e6236eb013133ddc2f958ea09218fe260d3", - "0x9655fe4910bc1e0208afbcf0ff977a2e23faded393671218fba0d9927a70d76514a0c45d473a97ecb00cf9031b9d527c", - "0x8434bc8b4c5839d9e4404ff17865ded8dd76af56ef2a24ea194c579d41b40ed3450c4e7d52219807db93e8e6f001f8da", - "0xb6c6d844860353dab49818bed2c80536dbc932425fdaa29915405324a6368277cf94d5f4ab45ea074072fc593318edff", - "0xb2887e04047660aa5c83aad3fa29b79c5555dd4d0628832c84ba7bf1f8619df4c9591fcde122c174de16ca7e5a95d5e3", - "0x953ba5221360444b32911c8b24689078df3fbf58b53f3eec90923f53a22c0fc934db04dd9294e9ec724056076229cf42", - "0x926917529157063e4aade647990577394c34075d1cb682da1acf600639d53a350b33df6a569d5ebb753687374b86b227", - "0xb37894a918d6354dd28f850d723c1c5b839f2456e2a220f64ecadac88ae5c9e9cf9ab64b53aac7d77bf3c6dfa09632dc", - "0xb9d28148c2c15d50d1d13153071d1f6e83c7bb5cb5614adf3eb9edede6f707a36c0fa0eadb6a6135ead3c605dfb75bd1", - "0x9738d73ea0b9154ed38da9e6bd3a741be789ea882d909af93e58aa097edf0df534849f3b1ba03099a61ceb6a11f34c4d", - "0xafabbecbbf73705851382902ec5f1da88b84a06b3abfb4df8d33df6a60993867f853d0d9bd324d49a808503615c7858a", - "0xa9e395ddd855b12c87ba8fdb0ea93c5bd045e4f6f57611b27a2ee1b8129efe111e484abc27cb256ed9dcace58975d311", - "0xb501c2f3d8898934e45e456d36a8a5b0258aeea6ff7ac46f951f36da1ec01bd6d0914c4d83305eb517545f1f35e033cc", - "0x86f79688315241fe619b727b7f426dbd27bcc8f33aef043438c95c0751ada6f4cd0831b25ae3d53bcf61324d69ea01eb", - "0x83237e42fa773a4ccaa811489964f3fab100b9eea48c98bdef05fa119a61bde9efe7d0399369f87c775f4488120b4f2e", - "0xb89f437552cab77d0cd5f87aca52dd827fb6648c033351c00ab6d40ac0b1829b4fcdf8a7dad467d4408c691223987fbe", - "0x8e21061698cb1a233792976c2d8ab2eeb6e84925d59bb34434fff688be2b5b2973d737d9dda164bd407be852d48ef43f", - "0xb17a9e43aa4580f542e00c3212fbf974f1363f433c5502f034dfd5ed8c05ac88b901729d3b822bec391cca24cc9f5348", - "0xaac6d6cda3e207006c042a4d0823770632fc677e312255b4aff5ad1598dc1022cab871234ad3aa40b61dc033a5b0930b", - "0xb25e69f17b36a30dada96a39bc75c0d5b79d63e5088da62be9fcbddfd1230d11654890caa8206711d59836d6abbc3e03", - "0xaf59fe667dd9e7e4a9863c994fc4212de4714d01149a2072e97197f311be1f39e7ad3d472e446dcc439786bf21359ede", - "0x957952988f8c777516527b63e0c717fc637d89b0fd590bcb8c72d0e8a40901598930c5b2506ff7fea371c73a1b12a9be", - "0xa46becd9b541fc37d0857811062ca1c42c96181c7d285291aa48dc2f6d115fcff5f3dfdf4490d8c619da9b5ce7878440", - "0x87168fbd32c01a4e0be2b46fe58b74d6e6586e66bbb4a74ad94d5975ac09aa6fa48fd9d87f1919bd0d37b8ebe02c180c", - "0x895c4aa29de9601fc01298d54cfb62dd7b137e6f4f6c69b15dc3769778bfba5fc9cbd2fc57fd3fad78d6c5a3087f6576", - "0xb9cf19416228230319265557285f8da5b3ca503de586180f68cf055407d1588ecec2e13fc38817064425134f1c92b4d5", - "0x9302aaef005b22f7b41a0527b36d60801ff6e8aa26fe8be74685b5f3545f902012fcade71edca7aaa0560296dac5fca5", - "0xa0ccda9883027f6b29da1aaa359d8f2890ce1063492c875d34ff6bf2e7efea917e7369d0a2b35716e5afd68278e1a93a", - "0xa086ac36beeba9c0e5921f5a8afea87167f59670e72f98e788f72f4546af1e1b581b29fbdd9a83f24f44bd3ec14aee91", - "0x8be471bf799cab98edf179d0718c66bbc2507d3a4dac4b271c2799113ce65645082dc49b3a02a8c490e0ef69d7edbcb1", - "0x8a7f5b50a18baf9e9121e952b65979bda5f1c32e779117e21238fb9e7f49e15008d5c878581ac9660f6f79c73358934a", - "0xb3520a194d42b45cbab66388bee79aad895a7c2503b8d65e6483867036497d3e2e905d4d51f76871d0114ec13280d82f", - "0x8e6ca8342ec64f6dbe6523dc6d87c48065cd044ea45fa74b05fff548539fd2868eb6dd038d38d19c09d81d5a96364053", - "0xb126a0e8263a948ba8813bf5fb95d786ae7d1aa0069a63f3e847957822b5fe79a3a1afa0ce2318b9ba1025f229a92eb7", - "0x8e4461d6708cac53441a3d23ac4b5ff2b9a835b05008c26d7d9c0562a29403847cf760b7e9d0bcb24a6f498d2a8a9dd2", - "0xb280a761bab256dfe7a8d617863999e3b4255ddbdc11fe7fe5b3bb9633fc8f0cb4f28e594d3b5b0b649c8e7082c4666a", - "0xa3e3043bfd7461e38088ee6a165d2ca015de98350f1cb0efc8e39ed4fcdb12a717f0ede7fbf9dadb90496c47652cc0ce", - "0xa4c1f5b1b88ae3c397d171e64395afe0cd13c717677775a01dd0461d44a04ee30ec3da58a54c89a3ca77b19b5e51062c", - "0xa268638e0655b6d5a037061808619b9ae276bb883999d60c33a9f7f872c46d83d795d1f302b4820030c57604fa3686e7", - "0xac20176111c5c6db065668987227658c00a1572ce21fe15f25e62d816b56472c5d847dd9c781fb293c6d49cc33b1f98f", - "0xacc0e22d9b6b45c968c22fd16b4ece85e82a1b0ab72369bdd467857fee1a12b9635f5b339a9236cbd1acc791811d0e29", - "0xb56066e522bee1f31480ff8450f4d469ace8eb32730c55b7c9e8fa160070bdec618454e665b8cbc5483bc30b6cebbfb9", - "0x8c1772bdfacff85f174d35c36f2d2182ae7897ad5e06097511968bbb136b626c0c7e462b08a21aca70f8e456b0204bf8", - "0xb4de3cf4a064bf589be92513b8727df58f2da4cd891580ef79635ac8c195f15a6199327bb41864e2f614c8589b24f67e", - "0x8f3c534125613f2d17bf3e5b667c203cb3eab0dbca0638e222fe552fddf24783965aa111de844e8c3595304bfc41c33b", - "0x8e445b2711987fe0bf260521cb21a5b71db41f19396822059912743bf6ca146100c755c8b6e0e74f1bf2e34c03b19db9", - "0x87ff9adf319adb78c9393003b5bdda08421f95551d81b37520b413fe439e42acf82d47fa3b61476b53166bf4f8544f0e", - "0x83f3c00c55632e1937dcdc1857de4eccd072efa319b3953d737e1d37382b3cf8343d54a435588eb75aa05bf413b4caa0", - "0xb4d8ee1004bac0307030b8605a2e949ca2f8d237e9c1dcf1553bd1eb9b4156e2deb8c79331e84d2936ec5f1224b8b655", - "0x93b2812b6377622e67bf9a624898227b56ebe3c7a1d917487fc9e4941f735f83679f7ac137065eb4098ad1a4cfbc3892", - "0x81943d9eab6dcea8a120dde5356a0a665b1466709ebb18d1cbfa5f213a31819cb3cf2634e6d293b5b13caa158a9bb30b", - "0xa9042aae02efd4535681119e67a60211fc46851319eb389b42ebadcab1229c94199091fb1652beba3434f7b98c90785f", - "0x91db52b27fd9b1715df202106b373c4e63ce8ec7db8c818c9016ace5b08ef5f8c27e67f093395937ba4ce2f16edf9aef", - "0x83cb9b7b94bd6ead3ff2a7d40394f54612c9cb80c4e0adadffea39e301d1052305eb1fe0f7467268b5aba3b423a87246", - "0x8720fd6712a99d92dd3fdaae922743ab53fad50d183e119a59dae47cdac6fbea6064c732d02cb341eaea10723db048fa", - "0x8d40022c1254462a2ac2380a85381c370b1221e5a202d95c75bccba6d1e52972dd5585a1294a1e487bf6ae6651867167", - "0xb7bc06e08d8c72daba143627582f4b4f34cc2234b5cb5cd83536f2ef2e058631a3920468ea4d550aea01cad221d6a8a6", - "0xa6e1a6f70fba42d3b9ce5f04ffdcfca46fc94041840c0066a204030cf75ea9f9856113fea3a9f69ea0037d9a68e3a9d4", - "0x8b064c350083fce9a52da2e2e17bf44c4c9643d2d83667cbd9ad650bbeba55e2c408e746ccf693e56d08826e8a6d57fc", - "0x8d304a5405a0c0696917fcddc6795dd654567ca427f007d9b16be5de98febbf8692374e93f40822f63cf6f143c4d9499", - "0xb968db239efec353a44f20a7cf4c0d0fca4c4c2dc21e6cbb5d669e4fe624356a8341e1eec0955b70afb893f55e9a9e32", - "0x98971f745ce4ce5f1f398b1cd25d1697ada0cc7b329cee11d34b2d171e384b07aeb06ac7896c8283664a06d6dd82ec6b", - "0x881f5a20a80f728354fad9d0a32a79ffe0ba9bed644ed9d6a2d85444cda9821018159a3fa3d3d6b4fadbf6ea97e6aff6", - "0xb7c76cbb82919ec08cf0bd7430b868a74cb4021e43b5e291caa0495ca579798fab1b64855e2d301f3461dd4d153adeb6", - "0xb44c8c69b3df9b4e933fe6550982a6f76e18046e050229bd2456337e02efb75efa0dfe1b297ed9f5d7fa37fec69c8374", - "0xa5bd7781820ba857aee07e38406538b07ab5180317689a58676f77514747672dd525ea64512a0e4958896f8df85e9d4d", - "0xa8443d1dc91b4faa20a2626505b5b4ad49cc5c1fd7a240a0e65d12f52d31df1585ba52c21e604dcec65ec00b81ae21fe", - "0xa157ae42fc6302c54bcdd774e8b8bafc4f5d221717f7bf49668c620e47051b930dce262d55668e546272dd07ca7c8d3f", - "0x8732c10448b63e907ff95f53cd746f970c946fd84fcbfe4cf9ede63afbbfc66b293bbc7c470d691bbd149bb3c78bb351", - "0xa82192f4fd9a0c33489a0486b79d0f6c797c7eccb45f91f7f1e8e1dd1924ca9944b983951025b99ab5861d31841451fe", - "0x839efc6d199ddd43f34f6729b6b63f9ee05f18859bf8fd3f181fa71f4399a48bff7dde89b36e9dc1c572f1b9b6127cca", - "0x992ef084abe57adfd5eb65f880b411d5f4ed34c1aeb0d2cfac84fff4f92a9a855c521a965ba81b5eef2268e9a9e73048", - "0xa2518ab712fa652e6e0bd0840307ef3831094e9a18723fb8ec052adacbb87f488d33778c6ec3fd845003af62e75125d1", - "0xb630ac3c9e71b85dd9e9f2984bb5b762e8491d8edb99cad82c541faf5a22dd96f0fddb49d9a837b1955dea2d91284f28", - "0x8d886d1b7f818391b473deba4a9a01acce1fe2abe9152955e17ba39adc55400590c61582c4fef37a286e2151566576ed", - "0x884f100dc437639247f85e5d638fcc7583d21bf37a66ce11e05bfc12f5dbe78685b0e51b4594e10549c92bb980512e12", - "0x806d7bac2d24cfff6090ba9513698292d411cdea02976daa3c91c352b09f5a80a092cfa31304dcfcd9356eaf5164c81b", - "0x934ed65f8579ee458b9959295f69e4c7333775eb77084db69ad7096f07ad50ad88f65e31818b1942380f5b89e8d12f1b", - "0xaaf50ca5df249f0a7caf493334b6dca1700f34bd0c33fe8844fadd4afedbb87a09673426741ac7cbbb3bf4ab73f2d0f3", - "0xb2868642cfa0a4a8a2553691c2bef41dab9dff87a94d100eaa41645614ab4d0e839ec2f465cc998c50cd203f0c65df22", - "0xa326513112e0b46600d52be9aa04d8e47fe84e57b3b7263e2f3cf1a2c0e73269acb9636a99eb84417f3ae374c56e99b0", - "0x97b93efc047896ddf381e8a3003b9e1229c438cc93a6dbef174bb74be30fac47c2d7e7dc250830459bed61d950e9c924", - "0xb45e4f0a9806e44db75dbb80edc369be45f6e305352293bcae086f2193e3f55e6a75068de08d751151fdf9ebc6094fa1", - "0x87f2161c130e57e8b4bb15616e63fa1f20a1b44d3e1683967a285f0d4f0b810f9202e75af2efa9fc472687c007a163f7", - "0x8f6400a45666142752580a2dce55ef974f59235a209d32d2036c229c33a6189d51435b7ea184db36f765b0db574a9c52", - "0xa0ee079462805f91b2200417da4900227acde0d48c98e92c8011a05b01c9db78fc5c0157d15cb084b947a68588f146f4", - "0xab0612d9bb228b30366b48e8d6ae11026230695f6f0607c7fa7a6e427e520121ff0edea55d1f0880a7478c4a8060872d", - "0xad65dfde48f914de69f255bb58fa095a75afe9624fc8b7b586d23eb6cf34a4905e61186bc978e71ccb2b26b0381778a6", - "0x8c8a4847d138d221c0b6d3194879fd462fb42ed5bd99f34ebe5f5b1e1d7902903ec55e4b52c90217b8b6e65379f005a4", - "0xa41dca4449584353337aef1496b70e751502aeed9d51202de6d9723e155ca13be2d0db059748704653685a98eaa72a07", - "0xae40e5450fd994d1be245a7cd176a98dd26332b78da080159295f38802a7e7c9c17cc95da78d56558d84948cf48242cd", - "0x863878fda80ad64244b7493e3578908d4a804887ad1ad2c26f84404dcad69ea2851846ad2c6f2080e1ed64fe93bbec31", - "0xb262fb990535f162dc2b039057a1d744409a3f41dd4b70f93ff29ba41c264c11cb78a3579aad82f3fa2163b33a8ce0e1", - "0xa7f6eb552b9a1bb7c9cb50bc93d0dda4c7ecf2d4805535f10de0b6f2b3316688c5e19199d5c9ec2968e2d9e2bd0c6205", - "0xa50aa5869412dc7081c8d827299237910ecec3154587692548da73e71fa398ff035656972777950ba84e472f267ba475", - "0x924c3af750afc5dfad99d5f3ed3d6bdd359492cff81abcb6505696bb4c2b4664926cb1078a55851809f630e199955eb3", - "0xa1acffa31323ce6b9c2135fb9b5705664de8949f8235b4889803fbd1b27eb80eb3f6a81e5b7cc44e3a67b288b747cf2f", - "0x8dec9fd48db028c33c03d4d96c5eecea2b27201f2b33d22e08529e1ae06da89449fe260703ac7bb6d794be4c0c6ea432", - "0xaa6642922ccf912d60d678612fffe22ef4f77368a3c53a206c072ed07c024aa9dcde2df068c9821b4c12e5606cfe9be2", - "0xa16ddf02609038fcb9655031b1cb94afe30b801739e02a5743c6cd2f79b04b2524c2085ca32ec3a39df53de0280f555d", - "0xb067d48589e9d3428c6d6129e104c681e4af376a351f502840bbea6c3e11fcbfdf54dadf6f1729621720a75ff89786c3", - "0xb14a24079de311c729750bb4dd318590df1cd7ffc544a0a4b79432c9a2903d36a0d50ecd452b923730ade6d76a75c02c", - "0x97437bac649f70464ace93e9bec49659a7f01651bba762c4e626b5b6aa5746a3f0a8c55b555b1d0dc356d1e81f84c503", - "0xa6f4cb2ffc83564b1170e7a9a34460a58a4d6129bd514ff23371a9e38b7da6a214ac47f23181df104c1619c57dff8fe2", - "0x896d0f31dfc440cc6c8fde8831a2181f7257ffb73e1057fd39f1b7583ea35edf942ad67502cd895a1ad6091991eabc5e", - "0x9838007f920559af0de9c07e348939dfd9afe661b3c42053b4d9f11d79768cba268a2ee83bb07a655f8c970c0ee6844b", - "0xb41b8a47e3a19cadec18bff250068e1b543434ce94a414750852709cd603fc2e57cd9e840609890c8ff69217ea1f7593", - "0xa0fb4396646c0a2272059b5aeb95b513e84265b89e58c87d6103229f489e2e900f4414133ed2458ddf9528461cfa8342", - "0xae026cfa49babc1006a3e8905d6f237a56a3db9ddf7559b0e4de8d47d08c3f172bde117cdf28dfdfd7627bd47d6a3c85", - "0xa6a3f3e7006bc67290c0c40c1680bf9367982eb8aaf17ecb484a58c8e9c2a7c24932e2caa9aacc9b4fbf4c0abd087a46", - "0x9093e05bd814177a01a3b8d7b733db66294e1c688c56def6e1827c0f2d9a97cf202721641bf81fb837f8581ae68cb5ce", - "0x87feef4de24942044f47d193d4efc44e39a8c0f4042fba582f2491a063e3a4640cb81f69579b6f353b9208884a4f7ce6", - "0x975f9b94e78aac55bd4755f475e171e04f6fbddb6fd3d20a89a64a6346754a3ff64ecff8c04b612a1250e1d8d8a9e048", - "0x87cde4d0164922d654cf2dc08df009e923c62f1a2e3b905dfde30f958e9e4dd6070d9f889712acd6c658804f48f3edb1", - "0xae8e22e158dda90a185eec92602831b5d826e5a19aab8c6400dba38b024c7d31c4cf265eb7b206dd45834f020b3f53cd", - "0xa4475807adc28aa086e977b65bbd7c8512119318c89d2619ea03a6739a72c3fb90c9622451896c7113ad4d12a3004de6", - "0x97f1ae1e0d258a94532c7b73fa8ebdbbd53349a4d2d0a217fe56dfdd084dd879960bc6ff45ebb61b5dbf2054642800a4", - "0xb3c832bd3691332a658b0caaa7717db13f5b5df2b5776b38131ac334b5fd80d0b90b6993701e5d74d2b7f6b2fd1f6b9d", - "0xa4b6af590187eb1b2cb5ae2b8cffa45c5e76abdb37cec56fc9b07a457730f5af0706d9ce0a17da792bbece5056d05670", - "0x97b99a73a0e3145bf91f9dd611a67f894d608c954e9b8f5a4c77e07574064b3db47353eba8038062cebaad06a2500bab", - "0x8e5ca5a675de6e6d3916bd9ce5898bb379372afe3f310e70ff031bc8cc8fabfb7f3bfb784f409bb7eb06fdb4511ee477", - "0xaabbbee4da1f16b5bbe001c19debe04745932d36dfbbf023fbf1010a2b1d54eb92fa5e266ac1e9337e26e2ddba752f40", - "0xb13447c77496825f48e35c14f9b501c5056e6d5519f397a2580cea9a383a56a96994d88926aa681142fe2f1589c03185", - "0xb89c55db39ff0e73dde7435b61e8a4d3e10f51dd8096cbc7f678661962e6de3d16f2f17a0e729cc699234cb847f55378", - "0x82c36b7de53698a1bafbb311fefc6007fcefa47a806ebe33a4e7e0fc1c7b6b92a40a1860702cf9295a16c6b1433e3323", - "0x8daeec8c88543d09c494a15cc9a83c0b918d544311fd2a7d09e06cf39cdebfa0cfc0e8fc0e3b5954960b92332f98697c", - "0xb18e55a1a7ae16be3a453d2bfa7659a7ec2d283dd46bdc82decef6d3751eeafc4f86f2416a22955c7e750c0582d4f3eb", - "0xb50c743462e2915bf773848669e50a3bcdb5a9ac5f664e97eaccf568c7d64a6493d321be0225de16142ce82ce1e24f66", - "0xaf69c9643805fb860434424b1608aababc593aaebc6a75fc017f7f62bb2b1da932b0b9bd5e6dcbba328422dafc06efd8", - "0xb5947db4f809fd0d27af838b82eef8ab4fe78687a23ebc61c09c67eb7e8d0e6a310ecb907fd257859d5a2759a07c21cc", - "0x92c7960e163ca5bdf9196c7215102f8e9d88efc718843321c6e2a6170137b8ecec4ea5d5a5ce4c28012b6cdbd777dd01", - "0xb63f9509ed5e798add4db43b562e8f57df50d5844af6e5c7acf6c3b71637c0a2d2433f4a0627b944f0af584892208bb8", - "0x8ef28304a9bfe5220af6a9a6a942d2589606f5dc970d708ef18bc7ed08e433161020d36fb327c525398cd8ecb57002f9", - "0xb722e0410f896c4462d630a84a5a14e94289fc38ed6d513ca88a09005935cec334c480028efa1943c7a5e202ae8c8379", - "0xb56b6672b488e64d4dde43571f9ceaa7e61e336b0fd55bb769a57cd894a6300e724e5f88bad39a68bc307eb7406cb832", - "0x8bf493da411fd41502b61a47827731193652e6ce3810709e70869d9aae49e4b17a40437a7a0dcc0547dbac21f355c0da", - "0x9613b60a144c01f6a0e7d46ddde07402e2133a1fe005c049a56415ff90401765040b2fc55971d24b94c5fd69fec58941", - "0x85e2f02b291563d8eea3768cf6a4602c0ca36568ffcf3d93795d642044196ca6b0b28991ea5898e7974ee02831a0ec70", - "0xb08ef66703dd9ac46e0208487566fbf8d8654d08c00f03e46f112c204782ccc02a880a3f9dffd849088693cee33b7b6d", - "0xa0b19eeda6c71b0e83b1f95dffef4d370318bdea6ea31d0845695e6b48d5c428c3dbba1a0ded80964992c4a0695f12ee", - "0xb052642e5772d2ef6f49dd35c5e765c5f305006b2add3b4bee5909ca572161edf0e9c2bc3bc3bc7f56fd596360ef2201", - "0x8261af164c768fec80d63fca6cd07d1c0449e9ca665fe60c29babdbd8a2b20cf1f556a4b24cd7341712468a731c21b32", - "0x8a17016a1b2fc0fa0d9e3610ea80548fcf514e0a35e327f6b5f8069b425c0f0829af7e206013eab552be92b241be5ac5", - "0x8eea25c680172696f5600271761d27ef4c8cec9ab22f01f72b2c7c313a142fafaec39e6920b96fcace858883e02eff7a", - "0xb8e0c590106e125c5bca7e7a071cc408b93629da0d8d6381f1b73fbdf17024a0cf13f679f5203a99bbbcb664b4a94e88", - "0xb9943b29395258b7afdf1781cfaf131297a4f325540755df73401b2ec4a549f962952e9907413c39a95585c4aff38157", - "0x8286eab4a04f8113fb3f738a9bc9c2deaf3a22bf247151515568703da4efe6450ab3970f5c74e978a2db7e8d795331b7", - "0xa10cf383c8a7e3f0a0a5556b57532170ff46dabdcbb6a31c4617271634b99540aa575786c636d3809207cbf1d2f364d3", - "0xa5af7eb998140d01ba24baa0e8c71625aee6bd37db4c5ff607518f907892219ba8c9a03c326b273bfd7068232809b73c", - "0xaed5f461e38fccc8b3936f1328a9747efcbceb66312f6d6eddce57c59570852767159f1a7d9998f63342515fef4ba9bf", - "0xaec3e94b029aa692bfe2b8dbc6c3b0d132b504242e5ebe0cad79c065085e2fc05550e5cdaa2353892a40ff1a062dd9eb", - "0x87c23703960129396018d0347f5dd034abdbd57232b74195b6a29af34b6197b3cd63c60ac774d525add96ae54d5c0fb4", - "0x97964a7768216e1c84dece71ce9202cc64b6d483650aa6f6d67215f655f66cda14df0a0f251db55832c77bfd9b6316e2", - "0x8167aaf24c8a023d0aea16b8c24d993618b9d0c63619e11a28feab8f14952bafcb0918ed322cbc0ae1b2e1786071819b", - "0xb58318bd62852ffb712fc58f368c21b641dde7b3fa7d7269974c7a7b5b3e1641569fc7b5f32ca49de22f4f993506d92d", - "0xb172e7911d5cd3f53af388af847b928947c711185aebd3328f8e6ed1106c161ae0c1b67d3d9eb237e9e66eb0672edec0", - "0xa6834cf69b2c4433cf6e779bfbb736b12e73e71e149c38101d13dbacf6c5048db53994a6a039381df40bbd67de40fcd0", - "0x882604aa3bb19fffd6db744b5cf4a2431b157dac06d0617e0703684a118ca90b2d22a7758a1de7732a7144e68b11b7f7", - "0xaddc128ba52bf7553b9ba49eff42004d388a02c6b6e9809abe1c0d88f467e5ff6cb0c82a8fd901b80dfc9a001f7b9997", - "0xabf19604a3f0cffefa7a9ced81627f6aacb8d7267b52b825f25d813d9afa24af6d70da21450ed93eaff8b4d2a9b905a9", - "0xa3c67e7bf02dbca183d86924611a7149556ee17cb3469793624da496b6c25617a9071925dd02aab9cb028739cb79043d", - "0xb1cea4284a3ac4d5b1c6f0947c6ec8365b3281ed15495bf328a907a9a02cdd186e7cb1ef080385b3399df786855985a9", - "0xa6edb126314559e6129caf1111dc3c82ff914efce658b11f2c9b48081be1cf3f46bde482469d493379025a158d95ab1b", - "0x9843fd7dd424da1acc6f92f87fac364a8b0d4097d74b6b451386384966c85145d43fc6ecedf04271b0f963ac731fd93f", - "0x83852bedca03a97a2e63053cb102387866cbefe6707ebb6dae2d32a59c343079f1a863f299fd64d0ecbe024d0a1247d5", - "0xa570e645a0679ebc6f0ca03cc8f7367b03c3886f3d9c787992de7f3e93360a170d3ac9ae7720999c727a887b1dc762bb", - "0xad644c40555238f28844eed632c8972b63d2602098031d53b5599d1a874903e0d0c428e0ab12a209ea3fb31225578f1c", - "0xb64e9f92a14812ed31075f9fdd3324659a036ef2f293ef9ca6f6feb87d0c138e1ba74bc36a910afd22ff9b3c8ec7cfa5", - "0x8f2d75a86d517dafac09b65596f4b89c4a9c0a7003632407504153fa297c9e3228e236948a5d5224b8df49a087c8e0e3", - "0xb02d6ab9292ae336c8a74115f33765af2c9f62c331d70c087cf4c2979792bb3c2666f6699c017f8d4c6b378fd4bda86a", - "0xa923d660d2e55228b8bc74f87d966069bd77c34a776fa96f37b48539c85634482e514e2cb76cb8eb20efd85eb9c83fae", - "0x81d7ffb53090a6d512055ecfd582ca92805525a05654e39bb12653a6a8902a16e651ba7b687b36b8bea7186632c7e9e3", - "0x83e9b33e29b57ae53d9f72bd4622ff388252333b4fa32ad360a5b00f3ffc8813b9cb8a1361454d3bb7156c01b94b6a08", - "0xad7d6bffe4d67eb53b58daa3fc8a5a60790c54fa42226ae12847e94c6de3b4365b3be39855a4f6a5f12e4803cdaed96b", - "0xa7709fed85abbee5a2fa49c5238582ec565da08c132d4912821491985bf83b681eb4823634bfe826abd63a6c41a64ea7", - "0xb8fb6ed55741132a1053b6ca77bdf892e96b048488373ba4aa2f2225fae6d578724124eb6975e7518e2bf3d25d215763", - "0x85e0c53089529a09b5bce50f5760af6aeafef9395388aa4b6144ca59953169101783347ee46264ec0163713a25fe7c63", - "0x8f9e47a9c37b678e56c92b38d5b4dab05defc6b9c35b05e28431d54b1d69ac31878c82c1357d016f3e57ca07d82d9c16", - "0xa81f508136ee6ec9122c48584df51637f768ccfe8a0b812af02b122a0fafa9abcc24778bf54143abb79eccebbdde2aac", - "0x931a96d2257a4714d1ef20ac0704438481632647b993467e806b1acc4a381cc5a9dec257e63239ba285deb79f92122dd", - "0x99fb0ff747bcd44b512bf8a963b3183ce3f0e825a7b92ddd179253e65942a79494a515c0c0bc9345db136b774b0a76b0", - "0xa9dbb940b5f8ab92f2d85fc5999e982e3d990fe9df247cfc6f3a3f8934fb7b70e2d0362ba3a71edc5d0b039db2a5f705", - "0x99011a1e2670b1b142ec68b276ff6b38c1687eed310a79e2b902065bc798618c0cdee7b2009ad49623ed7ae0aa2b5219", - "0x9361e9f3aa859c07924c49f3d6e9b5d39a3df2fc1c10769202ec812955d7d3814c9e6982f4df3a8f3bdbfb4550cd1819", - "0xa8aa23f177ddc1e7a7856da3eac559791d8b3f188c0b3ae7021bcb35dfb72b0f043c3699597a9188200408bc3daf6ab7", - "0xa5a502ff673f6dab7ae4591a7b550c04ede22a45a960c6b5499644f721c62b12b9e08248e7f8b8a59a740b058d2a67e6", - "0xad374f80f0b52bc5a9491f79a547ce5e4a3ce4468a35d7dbca8a64083af35ab38eff9aa774ccba2e2e1e006e45cb0b85", - "0xab6851827125e3f869e2b7671a80e2dff3d2d01ce5bfbeb36cbaf30c3d974a2d36fd9f7c3d331bb96d24b33dbd21f307", - "0x96658f6a2d225a82f7ccee7f7a7e476967e31a0cd6c62859d3b13ee89702bb821547f70ffd31cb46a6a0d26a93158883", - "0x878f59ff2590bc3d44fdc674717589800748b78d543d3c0dbb50125b1d6011d6a083f10ab396e36b79f2d89b7cf51cdd", - "0xb8bdfb97829c5d973a15172bfe4cb39620af148d496900969bd7ca35de9b0e98eec87af4e20bef1022e5fb6c73952aa0", - "0xa292a78b452743998aee099f5a0b075e88762222da7a10398761030ffcc01128138d0f32fccf3296fcbea4f07b398b5f", - "0x85da44fdd7b852a766f66ba8804ed53e1fc54d282f9a6410106c45626df5a4380cbea2b76677fdfde32446a4d313742a", - "0x84bebf036073d121e11abc6180cba440465c6eaadc9a0c0853a5f1418f534d21cccf0cfc62533eaeae4653c7b4988046", - "0x923dec006a6af04ef675f5351afffffd2c62a17a98f4144221927c69f4553dd105e4fcc2227b5f493653d758cd7d0352", - "0xa51eda64f4a4410a1cfa080d1f8598e23b59856436eb20a241e11106989fbbb48f14c2251f608cbf9531c7c442b30bf7", - "0xac6d26ae7bab22d49b7fba7fe4b8cf6d70617977008c8290787c9da1a4759c17c5e441efb3dee706d5d64d9d2ace1de5", - "0xab5138b94d23c1bf920b2fb54039e8a3c41960a0fe6173261a5503da11ff7b3afdb43204f84a99e99888618a017aac1b", - "0x8c85647a91e652190eee4e98a1eec13a09a33f6532926427bf09e038f487e483f7930fbe6ff7a2126ccde989690dc668", - "0xa6026ab87cffec3e47b4c9673957d670cb48c9b968d2ad0e3d624d81c1082dcebbc70d0815cbd0325e0a900d703a6909", - "0xac4f6ff6baf8374a3c62bdd5a8d207d184ff993f6055bcee1e6dcc54173d756c37c24570d6462395add6f7871d60b1ae", - "0xa0dd6bc93930d0016557588f2598b7462ca48cbed637c8190be0fb4811e4576217ca9fc3c669c2a4db82e3f8bb24acaf", - "0xa67c1d79f7e7193a23e42928a5cc6a6e8e0c48b6b286607dbcfaaa0f10a7ba29ad62d1d57ca28c486794f0908bece29c", - "0x822f411bab4882202ed24e67c84e0c9a8da5b3389804ed9dfba0f672e3e1457ea76cad0cb935dbb3d7a39500fba5fe12", - "0x8a1198572323689300a9d7db2e2bcb7c519392e5d3d33e83cd64bcf1517a7dde52318a98203727b186597702c0eed258", - "0x8a84141b02f1d037c68d92567d71cda3a0b805d1e200b1d3fff3caf9902457cbfbaac33157b87ab0bb9e4fe3bac882c3", - "0x8070ace16d9eef8658fdcf21bed0d6938f948f31ca9d40b8bdb97fc20432cd2a7ef78eeefc991a87eae7f8c81adf9b19", - "0x9522e7123b733ce9ca58ab364509f308a1ead0915421ccede48071a983fd102e81e1634ffa07a9e03766f167f5c7cb5e", - "0x82cbdf97a755e952304f5a933fd4d74a3038009f242dac149595439130a815e9cc0065597c0b362130183a4c4a444173", - "0x81e904f9b65cd7049c75f64c7261e0cbb0cc15961ffcac063d09399d0d2b0553b19e7c233aca0f209f90cf50c7f5e0b2", - "0x8f5f6ea87429542ea04ad3eb5fc7eeb28fcf69c01c1a5d29b0de219524f6fba90c26069bfc9092379fe18cb46274393a", - "0xa4e5815481eb33b7990d2de1a3a591c1ab545b64fbeb4cff8c71b6bcb04d28940097899062bf43b27c5a8f899616703e", - "0xa7afe6066681e312882b3b181f462a1af2139d9bd2aefffae7976f3fc357bfd8fbd6ddd4e5e321412f107736e77f0cb6", - "0xb8ab102d7ff8d46b055095d8fb0ec2f658c9e18eee523c295b148b37f8342c120798113553b8bfebf2a11f27bc704cc4", - "0x862175ecc7e0e294c304a0352cd0f1d11b2603d326bb0e54e02b6cc8d04d01ac31c8864e9395aa1f3b90b76bc4397f5b", - "0xa4ea51ef3d82509f0e4eb6af705fa7530921cf9512cb5bf030571e69f4504a299297219a0a7e40db1b45165a5ea3a3f2", - "0xa6fb8b573e2ba6db0e8aba53a489e99bebe533c0fcd947dbfa732e00594f03f4e8609ccc44d8215986d38bc3d4e55d48", - "0x93fe8e0bdd5d66df2bd18be5963e864bddfcdcd3298590e7c3b11d99a070a4948fecef46453f19960bbfeada37979613", - "0xacbc45bc55c7080b45c69a3db80cbfc0267006dcf49c47330975aeff2a8ac07b206e1b1c3a515e50866ff510739b92c0", - "0x94a577df0983e4ee3d6b80c73d7e8e3bb78bd8390ff56fea350e51bdf5e0176b8494e7e81dc7b1d842ada961089cd1eb", - "0x81eb1fbe9e9c89f5818d0ef98e694da86e88625f0a37cfe88e6de69f90e58297e67f1d5c9d71263b523b63e42685975a", - "0xa81a2391ea4d0f65ab4325196559d67e2648b3f1e464509430b40d9948d5b0fc01c337d9b51048a93c4d62e6b73e1e8c", - "0x849a026e55ed77135138836c9df67883763e4602357d8566da2ee2505d135d44061de0c070cf333ffb9ac2e55a0894b2", - "0x8e272cc5734374c003c7b2e6ba833eb99b6be608da04e576df471c24705b6b2a790549c53e7971df2d9f0b88d0f570c6", - "0xb0f9e6d985064aa311d4a147f41007fdc576b7b9194aa4b8712bf59a76a71543fec2ee3db21bd3d30d4096f25babc543", - "0x96331837f0d74e2ba6cb1bfaddf4b1fb359bf46cb6c3c664938eb030e56bc85a5ce17bcd60b7fa7b72cb0ba1f3af0b5b", - "0xa0eaab6de4b5a551896e7d26153fb5df4bc22a37833ec864090b57b5115b0f8f1279e855cea456bb844802b294b0dbb7", - "0x955e87d3b966edff34f28137f871881c59bbbc6d69986b739867807680ca22b5e3272ced1d25854ed9700d87f133848b", - "0x9270a6db157a8ce78a1af6bfe2b5bbe7b621d56cc8f9940a03b5a5f600848b87b05d83595b2a3a315d4b7f4687c46085", - "0x9043328f2dd4dd85e14c91237a3478dc1eed239164924b53d1de9364d76c81315afa9639b58eedb1ab2122e2ae2e7cfb", - "0x857fe9f7d00b03bce367de7f789d755911a5f85d78044f18311ecd9b955e821b4a50228347260ba1205aef61219001fe", - "0xa0f878050367a7103fddf380908da66058ef4430eae1758335c46c24f5c22fefb0753991b3a47dba5c7eaafa4d598178", - "0xab5959296b1af14d2878816c7da9926484cbf8896b7eeac8a99dc255013319a67a0209025e1f8266ffd8cd7d960bdc87", - "0xabe53abc57ea46419dbe0ac1f39eee39a4feae265e58b50928eb0695e25938a16a8b00e65c1313837dc3367297e2c258", - "0x93e3e42ed6ba9c45d4e7a4bf21c1e469efafded1f3be9931a683dbb780db2494742fd76c9ad29fd7d12da2b778ede543", - "0xab3e64035c488a6e63496ddb2de9648cc63a670c5d4b610c187d8ceb144fcc50b016046f50b10e93b82937ebe932ac08", - "0xa3a8fa898f489b313d31838ad9f0c7ffe62ef7155de5da9ffe6ecd49a984fac3c6763e8cb64e675e1c4a0e45e7daf078", - "0x8356b26aa7c9fc9734b511480dad07b164cfec1324ad98eec9839a7943f2889d37c188d465515ad4e47c23df641c18c3", - "0x83c4476f829e0fe91da2353d5b58091e9335157941e89ca60ccab1d7fdd014bcf21bd55249805780ddc655c5c8c2536e", - "0x814f6e66505b2cb36de92c0de8004d6d094476522e66b9537787beff8f71a1381ed9f2b7d86778979ad016a7dae6cbac", - "0xb1cd7f6da4a625b82bea475442f65d1caa881b0f7ce0d37d4b12134d3f1beb3ad4c2f25f352811e618c446185486adb6", - "0xa71b918481b9bda667de0533292d81396853a3b7e2504edd63904400511f1a29891564d0091409f1de61276d2aebc12a", - "0xa2cd3d4104ec5fb6d75f5f34762d5e7d2ff0b261bea5f40a00deec08fbdab730721231a214e4df9b47685d5bacfe37c6", - "0x807f2d9de1399093bf284814bc4093f448f56a9bde0169407cdc0e7d2a34ff45052aef18bcb92f0ac7a0a5e54bd843e9", - "0xabeb03010c3ac38587be2547890a8476dd166ac7b2a92c50d442f031eaf273ad97114c38e57fe76d662c3e615334ac0b", - "0xb90a688da4b0bf65ff01bcf8699f0cba995b3397fcbe472e876ae1091a294463e4b94350ae8bd5c63b8441089e0884fd", - "0xad88db4afb177931788fb08eff187e15ad739edc7e1a14c8b777b6bf668aec69ca4749773f94250c1fdda3b59f705f7c", - "0x9886809f9ae952797c6527c6db297d2aa3d5209b360efe6a19970575a9f78aee3c21daadb8e8dfcbeeea5290238d16d9", - "0x930f486e95d7c053c9742e6f0b31e6d4fa2187e41229e46a074b469aafb87880aa8e972719b363049fc9fe2db8f03ce2", - "0x8d229af4fa08bd8aeb5fd9acfee47571eb03fcd2f19073b94cd27e2a6735029d31f123249d557f8d20c32ac881eae3aa", - "0x84576ed5aebe3a9c3449243a25247628993fdb2cc327072418ea2f1d11342756e56e9a82449bc3ea6e8eaecabc62e9b5", - "0xb775cb86cbec9c46a4a93d426379c62872c85dd08bccda39b21cb471222b85b93afd34a53337b6d258f4891c6458e502", - "0x8be1540e6b535b416b8d21e3ecf67dfb27a10fd4010f9f19426422edaeb0a4961d43ff3afd1db0994170056ce4d77aec", - "0xb9c7438e90a5501a4d05bbb8ab68d6db7e9baa8927231a5c58715ee2ab76ca1da0e94910a076958654869148d813d0e9", - "0xaa9bed1c4d2e7cbc2e1a884c8998773f7cc6fa9d6493c8abe8b425114a48305c3a43a1abda2292177ffd39ef02db4163", - "0x897b395356047cd86f576cfc050f7e4546ecd4df30b2c31ed8945797b81dd4ed9b9106cfbe6d7dd8bf91882e3cf1f42e", - "0x949a37e1037d9464b2ccd3ad23eda7089570d6b5ffa18025d2548a9df8829de8d62960f04a603f21eecbca5893d45284", - "0xb8a0642f68ff169ffbcd8cd684fae75d96f9bd76949472775bf155edc55a3d9c3e6f0299ee73a6cfb96289361fdbe9ee", - "0xa1273141510fcddd89b9b92c19a268dadd1528ad85744b8174684c9b56668e6b35dabb05f2b4cc6ef5611eaea6052f27", - "0x97c7415c82de83ecc066eb922268b8205ad7266c65b2b8f7e0aadac87f076c738cea72f9b0f069b8d28cf9d5438b8287", - "0xb32c7005380c848f71092a74297555dc6022369fc2a4f285e586ac8f53f6bd354fbe4b1f8a4cfb406a101103bf87bb64", - "0x91b48eeba52f02d04f536d32112038f8ba70bb34284fbb39e0f7bae2e08b3f45ad32e2f55d1beae94b949c15652d06a1", - "0x99e24f5ea378cb816a4436af2ee7891ac78a2e37c72590be0abd619244a190fee51fc701b6c1c073611b412cb76332c9", - "0x9465d1e73a1a0a5f7b1cd85f4fa4f5dee008b622b14d228d5cd5baeec174451e7ae93c5de688393d37cc24ce15df4139", - "0xa6ac3986ee01debdacb5ddc1e2550cb4f039156df15c7d5752b79f333175b840bdca89c4959a523e58cf97bbd6b2039e", - "0xb7f7a5cc1b1b6145988170d619c170c130231abbe0b5143a9bccaaebeef9ceb1c16e26749bc9dc5650fe91f92fa1b79b", - "0x854cb04f1557457383a401d79a655adfd0a4b706ea2bbc6262949c8d657efcfdc9c7960cbe1a50b5eebb361c5e378f80", - "0x8dd199dccbdc85aeca9ddcb5a78dd741a452f7a0d3ceb6546d76624bad2fce0e7e6c47ee30d60bf773f18d98503e7f9c", - "0x889e1ca9f0582be9bf5f1aede6a7312b30ea9bed45ab02d87182a013430f16007ae477ee6a823ae86c7fef7da016a0ec", - "0x892a60e63edfb3e7a6cf2d0be184413d214401fc1e6c004ca2902c3f1423728bf759a136e6e715d26d5bb229c75cc20a", - "0xa2287cd092261b39d22dcb1fa19512590b244771bb69fb62eda72f12be37d48e408b3e37a47608f68d743834edee7f15", - "0xb3b6afb950bbec0ff631bdf18af433e68adc63d02cb479704f24329ca6b6edd9a3d1d606563dbdce6038b676b85130b9", - "0x847da90f37b294509de51ab6521fdff12d5a1ec3cccaf730aa744da7e54b85fd9c70618787e87c0ba9947ce6c81387fb", - "0xad872153c00bccac75bdb30d1ab7044d814f4f8655ff26421d48fea04fb21d4dc82c1900620a57d13adc45c1062a1817", - "0x90fa5ee98fd7ec719f2a8543bbd0ff45ac69296c2416fc8666d05de3deea1017079a68aba55540a19585925803c8335d", - "0x962ba6d029e9176d0e8c80a21f2413f7322f22a9e9a32c933697a8b0e995ce25bea5264736a75718b3d330e215a58a05", - "0xa446f9530db30c5e9c1b3844d635e5c2cd311cc4537ff277fe83dd1a0382bcfa73beb07aaa0cf5a97d24c67e688086a4", - "0x8766b2053f16c72db387abe18b43d7b357a542916c9b8d530ee264e921c999494d6eb1e491352ecdf53758640c7a246d", - "0x83f32f511f7b0233662acfc14f30df345af99d2d6c777ce0b4bcdc4dd110533f30b45071df17230aaec392cc482355e1", - "0x82e3521bc9519b36f0cc020225586b263e4feb57b533b38d8e89ccf8d03f301d94da90efb4902002732fbf3876697f38", - "0xb5d1ea69c97ceaa34a720bb67af3fcf0c24293df37a5f6d06268b1eabe441531606954ac2598a1513f64231af722b3a3", - "0x956842696b411e6221c5064e6f16739e731497e074326ef9517b095671f52a19e792d93fe1b99b5a99a5dc29782a5deb", - "0xb19b5658e55c279eb4b0c19a0807865858cbec1255acd621f6d60c7e9c50e5d3ee57da76b133580899a97c09f1dd8dac", - "0x89e6a8b916d3fcc8607790e5da7e391f6bc9eae44cc7665eb326a230b02bc4eb4ef66e608ccc6031048fc682529833d0", - "0xb1a210bc8070ed68b79debd0ec8f24ec5241457b2d79fd651e5d12ceb7920e0136c3e0260bc75c7ff23a470da90d8de9", - "0x85b1954278e2c69007ad3ab9be663ad23ae37c8e7fa9bc8bd64143184d51aea913a25b954471b8badc9e49078146f5ac", - "0x98bf63c7a4b200f3ce6bf99e98543925bc02659dc76dfedebe91ec5c8877d1271973a6e75dad1d56c54d5844617313e1", - "0xb7404b6e0f320889e2a0a9c3c8238b918b5eb37bcdab6925c9c8865e22192ba9be2b7d408e1ea921a71af3f4d46806d0", - "0xb73cbbebf1d89801aa838475be27c15b901f27d1052072d8317dcae630ab2af0986e56e755431f1c93f96cd249f2c564", - "0x95b2027302f7f536e009f8a63018da6c91ec2b2733c07f526cc34cbcfa2f895ccfd3cc70be89f4e92c63c7ddc2a93370", - "0x9201d9ff5d0b1222bfa2345394f88ddf4fe9282acf51bee9b18b96bb724fdf8e736d7101acc2795a34e72f9e0545c9a8", - "0xacbff7eb160f427d8de6f29feeddfa8994674e033a0ccdc8e8c73f9243968f1a6379da670a7340f422892d50c97113c7", - "0x97ae8d03352c3729e1623e680dd9664f303b3bcfb844ef80d21e9c773a247967d27b86c9326af29db5eefd0bd3d4fac8", - "0x8e53ae5c22f5bfa5fe4c414dad6a10b28a3e5b82a22e24a94e50ce3b2bf41af31e7ba017d2968811c179017b78741ef0", - "0xb5ac7dd150247eb63dfb7dd28f64b1bf14426dc3c95c941e8e92750c206c4c7f4ad1a6b89e777cfe26ecb680dbf0acb6", - "0x99ae2e4652ea1c1c695e7ea2022fd35bd72b1a0d145c0b050da1be48ad781a413dc20fbda1b0b538881d4421e7609286", - "0xb8abe1fb3a7443f19cd8b687a45e68364842fc8c23d5af5ec85da41d73afb6840ef4b160d022b2dad1a75456d809e80b", - "0x842619c3547e44db805127c462f5964551f296a270ed2b922e271f9dc1074fdf1c5e45bb31686cec55cb816d77853c01", - "0x902dff769391de4e241a98c3ed759436e018e82b2c50b57147552bb94baddd1f66530915555e45404df9e7101b20e607", - "0x82e4f2ee7c7ca1ee8f38afa295d884e0629a509c909a5464eb9ea6b2d089205478120eed7b6049b077b2df685ec8ba48", - "0xaa21a68b0888e4a98b919002a7e71e6876b4eb42227858bf48c82daf664c3870df49e4d5f6363c05878a9a00a0bcf178", - "0xa8420cd71b1d8edd11ebc6a52ba7fc82da87dd0a1af386d5471b8b5362c4f42718338bcbc302d53794204a0a06b0671d", - "0x98c686bd3a994668fbbd80c472eed8aedd3ab5aa730c8d3ce72e63fb70742e58525437be1f260b7ecc6d9d18a43356a0", - "0xaca0b2df9ec8ede0b72f03b121cded5387d9f472b8c1f3a5f1badd5879fb2d5d0bbb6af1a2dd6bdebf758cfceadbe61d", - "0x93b1abd9cb41da1422d171b4dbf6fbcb5421189c48e85c9b8492d0597838f5845198494c13032e631c32456054598e1d", - "0xa246ab3a47f7dc5caedc26c6c2f0f3f303ed24188844ab67a3da1e793d64c7c7fe3e5cc46efafbd791b751e71de0614c", - "0xb9b52095ca98f1f07f3b0f568dd8462b4056c7350c449aa6ce10e5e8e313c2516ac4b303a4fc521fe51faf9bf7766ce9", - "0x8e2e9d26036e847c2a2e4ba25706a465ac9fbb27804a243e3f1da15dd4084f184e37808661ec929479d3c735555085ee", - "0x8b8c4f4ad5c8e57e6a7c55d70ef643083d4b8dac02716ea476d02dbbb16c702a2f2d5dd5efe3aec7704d2b8cdafe3959", - "0xa800afea30d0df333805d295bac25419b7049d70044be00c7c85a92a0503ca471001bc1e6552323f1a719eb96616fc20", - "0x868bced4560e1495b8527058ebc82a538b7cf806f8d8fe8eeed6981aba771de4d5e9f03cbfc7157d38b9f99cdea87b96", - "0x86b86258b0c1feb988cc79f6c4d4b458ff39428eda292f9608a5fc4c3765782c8c23c66f82d7538e78e092cd81d69a56", - "0x9370eac15de2555824c7d48520a678316a7bb672e66f8115ad7dbc7c7b1f35a7718e8fa0c35f37e3ef2df32dfa7ca8d1", - "0xae200bc5be0c1c8c6ec8e9fd28b4d256c6f806c0f270766099e191e256d67b9cceda2cc2fed46dfa2d410971a7408993", - "0xaf2428c77b2b9887ecde1ea835ed53c04891547fb79fe92e92f9c6009cdfffa0cb14de390532ad0ef81348b91798bd47", - "0xa9069eef0316a5d13d1aa4cef0cf9431518f99b916c8d734bd27b789828ae03e5870837163ea6ad0be67c69184b31e8d", - "0xb1b1ce6d529f5a8f80728173b2f873c8357f29644b00f619c15111224377ae31a2efb98f7e0c06f5f868030aab78ed52", - "0xb89c98beef19ee7f300e1c332a91569618ef8bf2c1d3de284fc393d45f036e2335d54917c762f7c2874a03fe4f0f6926", - "0x8264f993dceb202f8426339183157e9e0e026d4e935efe4cf957eb14cd53edcdc866305fb1334cdf0e819b69eafbaccf", - "0xaebd113f73210b11f5ac75b474f70a2005e5c349345003989175dffa19f168abd7f0e28125b18907502fff6fcc6f769b", - "0x9993ad061066ca6c2bb29fe258a645089184c5a5a2ef22c811352749a199be3a3af3a0d5ce963febf20b7d9e63508139", - "0x97952105000c6fc6c2dcae1ebdb2feae64f578d26a5523807d88e6caf1fe944b8185e49222d06a4553b3bdb48c3267a2", - "0x82dd955f208957d74693bed78d479c9663f7d911f68ff033929418eb4a5c5dc467589ca210c1ba3c2e37d18f04afe887", - "0xb816fc4763d4c8a1d64a549c4ef22918e045ea25fa394272c7e8a46dcb0c84d843d323a68cc3b2ef47a5bbb11b3913bc", - "0xa7a87ba4d12a60ee459aad306309b66b935d0c6115a5d62a8738482f89e4f80d533c7bba8503e0d53e9e11a7fd5fe72b", - "0x92b36d8fa2fdee71b7eea62a5cc739be518d0ecf5056f93e30b8169c3729a6a7ed3aa44c329aa1990809142e0e5e2b15", - "0x8835b6cf207b4499529a9034997d2d3bc2054e35937038deb9c3e2f729ebd97125f111c12816d30b716b397016133c52", - "0xacf14cd6d978ba905cf33b9839b386958b7a262b41cbd15e0d3a9d4ef191fcc598c5ab5681cf63bc722fe8acfda25ce6", - "0xb31302881969c5b283c6df90971f4fb2cc8b9a5da8073662da4029f7977fbb4aaa57dd95b003a9e509c817b739f964e7", - "0xb74669e1c3fa7f435e15b5e81f40de6cfb4ad252fcdfb29862724b0a540f373d6e26c3d600471c7421b60a1d43dbeb0f", - "0x861d01615cba6ca4e4ef86b8b90f37fa9a4cc65cef25d12370f7e3313b33bb75de0953c8e69972b3c2a54fe110f2a520", - "0xa58a56820efaf9572fd0f487542aaff37171d5db4a5d25bfb1a5c36ca975eb5df3cb3f427589e1101494abb96b5e4031", - "0xaf13d0a6869ef95cb8025367c0a12350800c6bc4ae5b5856dcb0a3ca495211d4139f30a8682d848cb7c05c14ae9f48cb", - "0x8c385767d49ba85b25a3a00026dd6a3052e09cd28809d5a1374edf4f02dc1beed367055b0dee09102c85985492b90333", - "0xb5129fc2fec76711449f0fcb057f9cf65add01b254900c425e89b593b8d395fc53bb0a83ddbd3166acc6d2c17f7fc2a4", - "0x86bd01b3417d192341518ad4abf1b59190d9c1829041e6f621068bce0bef77ec3b86875b7803cf84ff93c053c2e9aad1", - "0xa74fc276f6af05348b5fabccb03179540858e55594eb8d42417788438c574784919fb6297460f698bd0da31ce84cebfc", - "0x967ed3ec9f1fc51f76f07b956e1568d597f59840ef899472a3138f8af4b4c90861e23690c56b7db536f4dd477f23add6", - "0xb9e678206de4fc1437c62d63814d65f3496be25a7a452e53d719981d09c7e3cae75e6475f00474e7c8a589e2e0c6bfa3", - "0xb028eaffaa4ff2b1b508886ff13c522d0b6881998e60e06b83abe2ac1b69f036eece3ded0f95e9ae721aea02efff17b6", - "0x935f82de9be578c12de99707af6905c04c30a993a70e20c7e9dd2088c05660e361942fa3099db14f55a73097bfd32a44", - "0x96a1cc133997d6420a45555611af8bcd09a4c7dbddf11dbe65aab7688cc5a397485596c21d67d1c60aae9d840f2d8e48", - "0x80d117b25aa1a78e5d92ea50e8f1e932d632d8b37bebf444dcc76cc409322fb8eface74a5dddab101e793ff0a31f0a53", - "0x893229136d5ab555dc3217fb4e8c6d785b5e97a306cdaa62f98c95bad7b5558ed43e9a62a87af39630a1563abd56ec54", - "0xb7ec1973ec60bd61d34201a7f8f7d89d2bc468c8edc772a0ba4b886785f4dadc979e23d37b9f7ef3ff7d2101d3aa8947", - "0xb6080ca201d99205a90953b50fc0d1bd5efd5eadbfe5014db2aeb2e1874d645ab152fb4b0ff836f691b013b98ce7c010", - "0xb546e66ec0c39037bbaa66b2b3f4704a6a72cf1924a561550564b6fcf41fbc2930e708cd5cac1d05e12a4b8ec93ff7eb", - "0x8abeed90a01477260f4b09fff8fa00e93afe727e8eed6f111d225c872a67e6ab61d0472ab6add3fe987744e16f7c5268", - "0x8e02342d5cc1836ed21834b9ef81686172cc730f0412479db5f590b0ff7a729a0e986ffed16d6ecafd6b83d65922ca5e", - "0xb05660605cf8e8a10c8d3c77cccbe4e7179fa27cc829571f6b722a58e65e4e44d7fe977446118e9da2d2f40af146cc2d", - "0x942a00e006baba6d025cbd99297bdb0cbf3d84cddf849b1b5a9fe9ef1745352fad81313cce5d7622d6652096a8fa065c", - "0xaace8212b3d8dbe44ac97460a5938a3b803aca9bd00d8a643a859351daf391b22d1fd2a6b3e0ff83cc9ee272a1ad7686", - "0x965a9885a5259197a75a19707a2f040e0fd62505e00e35ebe5041d8467596752aedf0b7ec12111689eceb3e2e01ecfc8", - "0x81d58270a4e7ee0137cb2bf559c78c4fd5b3a613468a8157b6a9c5c0b6ca20a071b87c127d59cecc3d0359237a66d890", - "0xaf92b6354fbf35674abf005cb109edc5d95845e3d84b968e6001c4b83d548715dffc6723ac754c45a5ace8cd7dd30a24", - "0xb112caa707f9be48fdde27f1649149d9456857f928ea73e05b64bb62d597801daac0b89165fea76074f8b5770043f673", - "0xb6e7380746da358fc429f676b3d800341e7ab3f9072c271310626ae7f67b62562ff76c63bc9f5a1dbc0e0af87752408a", - "0xa45e9e8d0931207ebc75199aa0c983134aa97f771ff546a94a3367bcedf14486f761e7f572cf112e8c412018995fdaf4", - "0x854381128de5bfb79c67b3820f3005555f3ee6f1200046ebbfaee4b61b3b80a9cebf059c363a76b601ff574b8dbf0e6b", - "0xaa1b828a8b015d7c879669d5b729709f20a2614be6af6ff43b9c09b031f725f15b30cde63521edda6cd4cf9e4ab4b840", - "0x8f28f6b62c744084eeddcb756eced786c33725f0f255e5999af32b81d6c6506a3f83b99a46c68fc822643339fe1b91c5", - "0xac584e76a74cafe4298ca4954c5189ccc0cc92840c42f557c40e65a173ea2a5cd4ae9d9f9b4211c9e3dfd6471fc03a1b", - "0xa413365df01db91e6a9933d52ab3e5ed22d7f36a5585ad6054e96753b832e363484fb388c82d808d1e4dfb77f836eab9", - "0x8a68c51006d45bf1454a6c48a2923a6dbeb04bd78b720bb6921a3ca64c007043937498557f0a157262aac906f84f9bf8", - "0xb93ff8b6c8c569cc90ee00cfe2fc3c23cccea2d69cbca98a4007554878311635cb3b6582f91636006c47b97e989fe53d", - "0xb9a8a44d54592511d74c92f6a64d4a8c539a1d8949916ef3773e544f6f72c19a79577de9878433bd35bb5f14d92f411d", - "0x94f066a7e49ae88d497893e4ce6d34edc2dc0b42fe03934da5d4ed264d1620d506fcc0661faa90a6cf5083e1720beaaf", - "0xb42b102adef8f42c1059b5ca90fe3524dcd633cf49893b04b4a97a1b932ca4c7f305cebd89f466d5c79e246bad9c5ced", - "0x86b560d78d3c5fb24a81317c32912b92f6ea644e9bedfdea224a2f0e069f87d59e6680b36c18b3b955c43c52f0a9d040", - "0xa3829fa7e017c934fa999779c50618c6fb5eafb5e6dec0183f7254708a275c94ba6d2226c5ca0c0c357b2f2b053eea93", - "0x9337dda730076da88798fd50faed1efa062f7936a8879ea4658c41d4fcf18cee7120366100d574536e71f2f11271b574", - "0x853d09a30f4342f5a84c4758e4f55517a9c878b9b3f8f19e1362be9ae85ca0d79c2d4a1c0c14f5eff86010ad21476a7a", - "0xb0bc74cb69bdd8fdffca647979e693ad5cbf12a9f4ead139162fa3263bfebef3d085aab424ed8c6220b655228c63c6b1", - "0x88d8dc8faf3aab12ba7180550e6a047f00d63798775b038e4a43a3b40a421a3f5f152a7e09f28ccd7198bb8cefc40c07", - "0x88db2e3b8746415d0c3e9f5706eda69a29d0b9ee5135ad006060be7787f4f1f7069e2e2e693c5e10b7c3d5a949085ae0", - "0xb5bd830d2f1c722188dba2690d21b7b84b92cbdd873a55aaa966f1d08d217bfc8cffe8caea68868f3850b90b4ab68439", - "0xb5ad4be0c9626a33fce6c8501297bdde21b07b88531451912ed41971a4c48fdd1036d8a4994a99a7fbba4a5901a7095e", - "0xb0e1337a2a1772191faa91302f1e562e7cdc69ba5b25139e7728ce778a68a7fa9817f852ec8e04a159122cff62992ec6", - "0xb4fd4a4c1be8bc7e4e2bfd45404c35d65b75f45fb19ce55c213a8035b41f1ccbce9766f3df687c0d7cd6cdfc1abb00a5", - "0x814bf565ece6e9e2a094ffbd101f0b9fea7f315a2f4917abe2bf7d070ed8c64a2987bd288385a42fd336ed0a70a9d132", - "0xaf860af861dc80894ed69f29c8601d986917ec4add3d3f7c933a5e9d540bc8ff8e4e79d0bb01bbc08fa19ef062f2890c", - "0xb66d33fcf3cd28f15111960ffc6ed032c3b33d4bb53d035ab460cc5fa7ce78872f0476d0bb13f1d38f2672347d2d6c4d", - "0x89603ae1a5dd7c526936b86a3b69b7b1d0bdf79ba3cc9cc2e542ec801a6126d1514c075d6ad119fe6b6e95544ffe7fbe", - "0x8a1b097f46a62d85cff354d1e38df19a9619875aad055cc6313fdb17e2866d8f837a369a9ee56d4f57995e2b0a94310e", - "0x8dc165d86c7f80b0fcd4b6f90d96cd11dc62e61d4aae27594e661d5b08ef6c91156c749de8948adfaf3265b1d13e21cf", - "0x98e3173772c3b083b728040b8e0ee01dc717b74c48b79669dd9d2f7da207af64ccd7e9244bc21438a5d4ac79b88e9822", - "0x924d168099b6952d6fe615355851f2b474f6edfcd6a4bd3ad2972e6e45c31bf0a7fb6f7fca5879a0de3ea99830cfb5bc", - "0x95452f0b7efda93c9e7a99348e13f356bad4350f60fcd246a8f2aa5f595a9505d05ec9f88b1fe01b90ecd781027b9856", - "0xb95e8af516bb0941fc0767ecd651ada2bc64cc3e5c67a1f70048c634260c0f2c0e55ed22948e1870c54590b36683a977", - "0x82f7feb71e746d5ca24455e3f3e57e4eade92669ab043e877b836612efd3de82009f0555e5d8811bff9f2b75fc57a01d", - "0x87623c02caf590ea84cf4a84d1be501f89262e26eb463f2f94a2d3042889c051b058823c3367a989498e46ff25edab16", - "0xb88da847b1ef74c66f923773ce8c920ca89751335fde17b3a98c0603862069a2afbf35b1552b43ad64dccea69f040ff8", - "0x96b734758c823e5ce5b44625c252957e16fa09f87f869baac195956052dc92f933f377b288c7f63b8028751cbbdca609", - "0xa23cc5fbbe5cb7c1d33d433cec4e502f6548412e2374e285d307f75e98280b0c0af4f46bba18015be88cdf7db8b1239c", - "0x8bd5bbe04bc929ca8f546e673803ec79602f66ec24298d3e3b6bf6f2c25180fc0032ea6f86c38a6e0ec20ff4eaafc7a1", - "0xb95768ca113e5d57ad887a1cb5ef84ce89007ce34c3156cd80b9aa891f3ebaa52b74c0cb42919cfbcf0cb8bafa8085f9", - "0xa117f99045f65e88acc5a14fc944f8363f466e4a64057eb8fc64569da5dd022a01f2860c8e21b16aff98aebdf89461b7", - "0x895cda6503907c98c43477eaf71dfd26759032523691659f13662ca3a967d93bbc5be342d168223cef7e8a333987d6a0", - "0xa084d77d913d3ec0586ad5df2647610c7ed1f592e06a4993a5914f41994a29c4a8492d9dce2e14d8130c872d20722920", - "0x84a328b73c64137bb97a0a289b56b12060fa186ce178f46fe96648402f1b6a97d1c6c7b75321e4b546046c726add5a08", - "0xb7c35087b2c95127ce1470d97bceb8d873a7ad11a8034cc1cba7b60d56f7e882fc06796048435a9586eab25880787804", - "0xab05e3394375ee617c39c25c0ec76e8a7f2381954650c94fbcd11063ea6772c1823c693d2d9dd18bd540a130d7b92855", - "0x82ba5907051d84b37fd9d28f8b9abebc41fc4aaa334570516ca2e848846644016356d40fa9314543017d4f710d193901", - "0x9170517b6e23ee2b87ff7c930cb02b3e6bd8e2ae446107b5b19e269bf88f08de5ded3d81a2ff71b632ca8b8f933253a0", - "0x93dc0e3f6234b756cdbb3fe473b9214e970972e6bf70803f4e2bf25b195b60075177a1a16382f1dee612a4758aa076ee", - "0xb4b49fac49cdfccda33db991994a8e26ab97366545166cc7140aef3d965529f96a5dac14d038191af4fb9beb020ff6d5", - "0xb826537670acdf7a8a45ef4a422d5ae5a1b5416ad0b938307518d103cc7ba78e495ea200adc5941414a70158a366e8a2", - "0x8ae3588b1fbecbc769c761f0390d888e34773cf521d976ee335f6c813bf06dad38850871ac8a8e16528684f1e093d0c1", - "0xad9c00b8dccdb545315fbf26849135699c6aa3735f89581244281154c906aba80d20c1e7f18f41acc61e0565f8015a33", - "0x954ce68146c05fc1c9e536add3d4f702335d93c1650b8c1fad893722a81f915eee2d38275dad00ce87f3f5bc90ef7341", - "0x8243feaeff9a12f5aeb782e3dd68609ce04ecde897c90fd8a19c9c5dace3cf43bd5bc0f1624bf7fd2607ca0d71adbba8", - "0xa8a1be55259cd27898d9d60a61998d8da2bf2d439ba6eedb61d6d16dacc4a81ec706b9196dfa080ba20701d2cd9fa1f4", - "0xb0eac6212c7a62ef6062c30875fbe24b8e1a9d88854c035686f849a9eed4d17fbc9af27429eb7c3fd60b47a5e29f6783", - "0x878561a88412e95f19f1cb8894be9d0ea4a2cdd44f343387f87dd37445e5777bceb643cebc68c910acb5e588c509cd2e", - "0xa57b6c347955d8b0057a87494223148ff9ff12b88e79dbd9d0aae352fe55e15ea57fcfb9add3d5d269ee0001d8660f20", - "0xa07fa66340d4082585e4d72c77510c59b272e7a3345f4b1de6be7ff4a11ea95d712d035a7355fc8d2e571fa65fe8236f", - "0xb9d84a627462438e8ede6c453e3367bfaf81cff199d3e5157ef2bc582d358b28b5ccc3bc27bb73af98ef45179ea79caf", - "0xb14f26ea7ca558761cb19508e5940fbf5dcf2ad8555c5a03e8ff92481994072f523b1ab6b7176f698e2cfd83d4f8caad", - "0x800cca1cbb14e1fc230c7b420ff06864a934b082321bbf5b71f37340383923f23183d4fdc8fa2913928722b8892db28e", - "0x94790c950b92e971ec39e9396c3f32dee32a8275d78e6ea28a47130651bddc86a189ef404c5e8c210bd291186dee0df4", - "0xad7b3b3e377df64023b8726d43a7b6ec81e5a5e8c0943c5bebe5ab5ddd6597255f434a205c14ba90e9e5e3c462a1fe0c", - "0x86ff8156cc857a416e735009cf656b89da59b766b4c4e5a0c0165282b530c10657cc28cf5cb847696725c37ac48b69d7", - "0x89cb64cf9294f68f01533660a2af2aec0ec34cc0b4a0cc36a128f2e0efb3da244981f69aede962f50590faeeb9a5da01", - "0xa2ea5a94a524bb8e6f767017246cd1af9d87c9abb9894e91c4e90c34c5161be6179b49dafcab9cff877a522c76beb145", - "0xb5d9abf29ed6030a1e0f9dc19be416c45ba8cb5ed21aff5492233e114035715d77405d574cd62f2716285e49f79b9c99", - "0xac441cf6104473420babdfb74c76459cbea901f56938723de7ad3c2d3fadb0c47f19c8d9cb15a3ff374e01480b78a813", - "0xabea34bd2d36c5c15f6f1cdd906eb887f0dd89726279925dbe20546609178afd7c37676c1db9687bc7c7ea794516af03", - "0x8140abfd0ec5ca60ef21ad1f9aabbb41c4198bac0198cb4d220e8d26864eedb77af438349a89ca4c3ff0f732709d41a9", - "0xa5a25abf69f3acd7745facb275d85df23e0f1f4104e7a3d2d533c0b98af80477a26ac3cf5a73117db8954d08f9c67222", - "0xb45ac8d221a7e726ad2233ba66f46e83ed7d84dbe68182a00a0cf10020b6d4872f3707d90a6da85f6440c093914c4efa", - "0x80f586dfd0ceaa8844441c3337195ba5392c1c655403a1d6375f441e89d86ce678b207be5698c120166999576611b157", - "0xb8ce52089e687d77408d69f2d1e4f160a640778466489d93b0ec4281db68564b544ec1228b5ab03e518a12a365915e49", - "0x8990f80bae5f61542cc07cb625d988800954aa6d3b2af1997415f35bd12d3602071503b9483c27db4197f0f1f84a97ac", - "0x8329858a37285249d37225b44b68e4e70efeef45f889d2d62de4e60bd89dde32e98e40e2422f7908e244f5bd4ffc9fe2", - "0x8d70c66ea780c68735283ed8832dc10b99d3daeb18329c8a44a99611a3f49542e215bf4066ff4232d36ad72f1a17ccc3", - "0xa3b2676cc8cdf4cc9e38c6cb8482c088e5e422163357da3b7586a3768030f851ad2a138eeb31584845be9ffb8067fc00", - "0x95b1fa74e9f429c26d84a8e3c500c943c585ad8df3ce3aea1f6ab3d6c5d0ed8bb8fa5c2e50dd395fa8d4d40e30f26947", - "0xb1185f2ac7ada67b63a06d2aa42c4970ca8ef4233d4f87c8ffa14a712a211b1ffde0752916bfafdfa739be30e39af15d", - "0x8705a8f86db7c4ecd3fd8cc42dd8c9844eab06b27d66809dc1e893ece07186c57b615eab957a623a7cf3283ddc880107", - "0xaf6356b372f0280658744c355051f38ff086f5563491fc1b3b1c22cfec41d5c42b47762baeb9ee6c2d9be59efd21d2b7", - "0x86bdd4527b6fe79872740d399bc2ebf6c92c423f629cdfcd5ece58e8ed86e797378a2485ead87cbb5e2f91ba7b3fbda1", - "0xa900f0be1785b7f1fda90b8aedd17172d389c55907f01c2dfb9da07c4dc4743cb385e94f1b0fc907dd0fedb6c52e0979", - "0xa9f59f79829a9e3d9a591e4408eaec68782c30bc148d16eb6ae2efccb0e5478830bbdaa4ae6eac1f1088e7de2a60f542", - "0x99cf54a69ad5e8c8ec2c67880900e0202bcc90c9815531d66de8866c0a06489ea750745cc3e3aa1c4d5cb55dcd1e88f7", - "0x8676246a4710d6d73066f23078e09b3fa19411af067258e0b8790456525c02081727b585d6f428c8be285da4aa775a4b", - "0xb596c7014fe9214529c8e6b7602f501f796b545b8c70dbf3d47acc88e2f5afd65dccef2ef01010df31f03653566b16df", - "0xa12205c6c1780fc8aebdd98611e12180005b57750d40210b9eff0396d06023bd4ff7e45f36777123ff8bed7c5f52e7a3", - "0xae7dbd435bba81685d5eab9abc806e620253da83e56b4170952852d442648a5d8743f494a4b0fc9d606574f87895b0d6", - "0x9786257b1726b7cdc85219ca9eec415f98f5a11e78027c67c7b38f36f29fe7a56443570fdfedc1d9293a50e4c89d89f6", - "0xaaf0515070d1ca92aacdf5fac84193d98473d8eb2592381f391b8599bcd7503dbf23055324399d84f75b4278a601c8b2", - "0xb31654dbf62fbbe24db4055f750f43b47f199a2f03c4d5b7155645276b2e456a218ca133743fb29d6f1a711977323f6e", - "0x8f4d39106ecdca55c1122346bdaaac7f3589d0cf0897a6b4b69e14b4d60550fd017876399401ce7c5d35f27da95f50be", - "0x8a7bfdb48cd47afe94aff705fac65f260b3a3359223cff159b4135565c04b544dd889f6c9a6686f417e6081ad01e0685", - "0x967ba91111e5e08f9befcbaad031c4fb193776320989f8ede4018254be0e94586254432d3dbae1455014f3a2f2549d01", - "0xa9db52352feeb76715a35c8bed49fb3a8774c9c8e58838febf800285fd6c4938ec162eb8457029e6984d8397dc79ea19", - "0x811794e6bfe2539e8f6d5397c6058876e9e30763ad20dad942bb5dbcab2f16d51718ce52bfb4de17889ba91da1b85bcd", - "0xa6db0f65a6dc8b8cc2312a3e0146d8daf520255bb12f74874c05693914e64e92be0cd53d479c72cb2591e7725dfaf8b0", - "0x918d21bfa06d166e9eb5b7875c600663a0f19cc88c8e14412319d7aa982e3365f2dff79c09c915fc45013f6b3a21200d", - "0x9894852b7d5d7f8d335dd5f0f3d455b98f1525ad896fdd54c020eeaf52824cc0277ecbfa242001070dc83368e219b76d", - "0xad00acc47080c31fcc17566b29b9f1f19ccaae9e85a312a8dcc0340965c4db17e6c8bd085b327eaf867f72966bf61452", - "0x965e74649e35696744ecc8bed1589700bae9ca83978966f602cf4d9518074a9aa7c29bc81d36e868a0161293f5a96e95", - "0x961e29a239c2e0e0999b834e430b8edfe481eb024cc54ffaffd14edaf4b8522e6350dc32039465badfff90dcb2ba31cc", - "0x943dda8fa8237418a07e311efde8353c56dd8ec0bfa04889ccdd7faa3dee527e316fdc60d433a3b75a3e36ca2aa9d441", - "0xa0ed4c102e3f1d6ebf52e85a2bc863c1af2f55dc48eb94e40066f96964e4d37fff86db2cff55a8d43d517e47d49b5bd7", - "0x9045770ad4e81345bc6d9a10853ee131232bf5634ef4931b0e4ba56161585b4286876bc8a49b7b1f458d768718cb8ebf", - "0xb0dd430295ff28f81895fde7e96809630d1360009bbe555e3ac10962de217d93ead55a99fd4f84d8cadd1e8d86d7b7ef", - "0x95ced48419b870ea4d478a2c8db699b94292f03303f1bf4560b5b1e49ca9b47e7008514fe0a9cf785717f3824567e1b2", - "0xa7986e0e389e8aef6aac4a7a95e2440a9af877ae2bc5ad4c5f29d198ec66aa0db1d58c451e76ae70275a2e44c3d3fa68", - "0x85a8490faf32d15de12d6794c47cc48e02428af1e32205e0742f8299ea96b64bcd6d3b4655272afa595eec74ecbb047c", - "0xb790d7fb1307aacc2d303d9b6753a9773252b66c6b67763cf8841c690cbccc4866ffb5fec1c068b97601a7953fe0f7e8", - "0xafcc4011f8c53f10d63c29b74d9779cd75c861e01974c28a4ec2cbb909b67a1b2287ead175231343c936ad75dfa416ff", - "0x918058bffdecc1ae8779dccf1d874bb9e28edbe34c8b5954a8da64a848858d2f0776437b423baf4e731f3f5fa05a2841", - "0xab554db549aa36dfa9f966a5ed6be8267e3aa9ced348695f3dafc96333c6dbb48ef031693aafd59d1b746ecd11a89c51", - "0xac4ecf746b46b26a7af49cc9cc1d381e1e49b538dbd7fb773ce6b1df63ae31c916693cca8a90fb89f1e7ec5e0e8dd467", - "0xa8de66d48f16b016f780a25ba25bd6338fd8895a1909aabcfb6e70f04ff66f9866e6e2a339bcbfa4bfba4070a6a8db26", - "0xb4b49374eff6dac622e49b0e9c0e334ecbec513a96297f6369696ad39e5ec0de81a1417f6544be866c9f60957a9ba09a", - "0xb8023968549ebab6c1e7a8e82954a5b213bec50bbf35b36697a8d4fd75f9e12d510b365962aace4c9978c5b04da974a7", - "0x8d4bc016026dd19e4059d1c5784897cefa47f7ae2ed6dfa2b3c14a852fff2b64abc09549d106584e0daed861a2d6d6c2", - "0x85e26f433d0b657a53da4c1353485e0c2efa092484c5b8adb3f63dc72ee00be79197ebef7937b37a6a006571641cd6af", - "0xabb37a917301e68328032ff4715abc0fee32e5f5be68232ca8bf7ffb8732bc47504e75b40bcc0a7c7720b71496fa80af", - "0x9837c8d2660522c0357f5222777559d40321a1377f89ca1717215195bad4a348a14764bd87fa75f08e1f6263e9d08982", - "0x97e06f971b4c56408ed5f1de621d233e6a91c797f96ec912737be29352760a58831aaf1f64e377c3ed9f2f4dc8ad1adb", - "0xa12d211304da7b91101513d57a557b2504069b4383db8ecb88aa91e9e66e46e8139dadc1270620c0982103bc89666215", - "0xaab74ba48991c728ba65213e8c769e6824c594a31a9b73804e53d0fda9429403ff3d9f6ea5ef60884585d46356c87390", - "0x92f19be2b7adf031f73611282ad33e462852f778c5e072f689dd0e9458fa6ebccfae02f2b2dc021802c9225035862468", - "0x953bb843c48d722604576cef297123755cef8daa648c30c3a678eada8718dfdb16e71cc3e042a51fedc80577235c2563", - "0x86f509e3c1b9ee9a3b95e6da8516b47feb8c8a83403984228f4903c7ee1ee4f03addcb8fe86283af1196a54b36b9470c", - "0x903d793a377e98e2562c49de33e3fbf84bf99211925e7002a4f688470db655884e1efe92782bf970ffa55d9c418ef3b5", - "0xa41b65681ed7f10987a7bfdf9e56b010d53683819d845d880fc21b2d525540605c5823e75c434f17b5a0d08a091c1564", - "0x971be802de51cfc0d10a96be7977c037873f19334ed4ed4904b7675aec8bfa1f8956cd0150b07064caf18229ffd1ccd9", - "0xb253ebe4f82cdbefbc3ef816d40c497fe426a9f0f0f170e783fa4a05ae6dabdfa8c448817a24e723a314b43e76a7c422", - "0x86f397c95025489929ce9230b1466b5c330ec7c58a3c7e3153d6d05bcb8348a13398908e192590b8812f5c5ff09c133a", - "0xa0713983a3dc9f10b3833687cd2575de2fc63c4ad8d2f54ff85c6db23dd308daefef1bd1e51eec26732f77c1f37ba793", - "0x8249a1d53ec92f311f4fa77e777800d777f3e9d4d452df740fc767fa7b0f36c8dce603d6e6e25f464c0399b8d0b93c30", - "0xa73d0a206a62922f07b928501940d415e5a95716ee23bf6625b01ff2cd303f777adfa373d70279ba8a30fbb4c99a6f1f", - "0xb1106b407ecf234e73b95ff58ac9fdf6709ad2e763b58f0aacc5d41790226d441b5d41405ac03a0641f577848a4f5e8e", - "0xb009963ccc7b2d42792f09ab7cb0e929503dd1438f33b953104b4de43274ca3ce051554d10d7b37041b6f47d7a2dab6f", - "0xb744512a1b3c7ef9180b095c6a0c5bc16086a50020cf20dc2216bbff24d91ca99b95cb73070444dafc3ab45c3598960d", - "0xa0209669ffeddc074d35cc6aa2dac53acac8e870f8a8a5118e734482245b70c3175f760652e792118fdddac028642259", - "0x8ddd3e0d313da17292fdcc1bbc6e9d81189bb1d768411c6fe99801975eddb48dbf76699dcf785cac20ab2d48e392c8fd", - "0x8392aa285b8b734aa7a6e0f5a1850b631ddf6315922e39314916e627e7078065d705ff63adbc85e281d214ec7567863e", - "0xb655a1fff4dba544a068bf944e9de35eaaa6c9a0672d193c23926776c82bebed8aa6c07c074b352882136b17abdab04b", - "0xaf5095f40d1e345b3d37bebee3eb48c5d7b0547f12c030d5bfe8c0285943e0a7a53a186f33f791decba6a416cba0c5c9", - "0x8223527f9eb3c8ff52708613cd2ee47e64c0da039cea3a0189b211dc25e9bfa3d5367a137f024abe94f98722e5c14b67", - "0xafdb106d279273edc1ee43b4eead697f73cb0d291388f7e3fc70f0dd06513e20cc88b32056567dcc9d05364cb9ca8c58", - "0x9319eac79ff22a2d538dcd451d69bca8aa8e639979b0d1b60d494809dbd184a60e92ad03b889037a1ac29a5547423070", - "0xb79191ce22dbd356044e1777b6373b2d9d55d02b2cc23167642bc26d5f29fd9e2fb67dce5bd5cf81a602c3243bedd55c", - "0x988e0da1e96188ffd7c5460ecdf2321f07bc539d61c74a3292c34cb8c56dbafbca23eb4471a61e8e64e9a771a49fd967", - "0xb0792b6cf4b10f8af89d3401c91c9833736616bb9fe1367b5f561c09d8911fb5a43b7a4fd808927b33ab06e82dd37a28", - "0x862f68ea55206023ca470dbd08b69f0f785fcbabb575a1306ff3453c98ffcad5fd6ead42e8a1f9edf14c6fd165ffd63a", - "0x815ff0898b1330ac70610180c0f909561877888ff10def749a1e65edf9f4f7cea710a757c85241dfb13d0031efb5e54b", - "0xaa6e6ce21776ea4507d452ccdaf43a161a63687aae1cb009d340c9200e5646e9c2de4104dfd66b8e55dfa6de6ee83e4a", - "0x8e8f3d3403e0256ecc254b9b1464edca199cad3f3348002d744721c345a1a3c7f257c3587d2229774cd395e26693d1ba", - "0x90483e28985e4a0f7a3cb4bc5e865b9d408b94cd2146c04aed00b48a7ab80a28deb05efec320817d63578d4f953bd137", - "0x84fb2a762ba29193b07f1dd84b3f69153cedb679b66ad04f8a4adf01c14f115163a107e6db23aaf0f0c9687824ded197", - "0xb4a23922bf4302cc9a6583f252a1afa026c87c132b9ae44cc1f75a972cb6ae473447c500827906f9b677617ddd6fb473", - "0x809bb9edbbe3a2769165f029f2a48b6e10e833eb55d8f9107c4a09ca71f0986dc28f3bf4ead9cab498086eb54c626bbf", - "0xa0459dbb08db4155d16301933ec03df77c4f835db2aa3f9697eeb2bb6fcd03337fab45fa43372a469fecc9a8be2e3119", - "0xa638eaace7f21854de49f4db6e4ea83d2983751645e0fb200c5e56561f599fd37dac70bdbd36566fdd10d4114fbb9c2f", - "0xa3a27bc2728390643a524521bf8ef3b6437cfba6febfd8bb54f2b6ecbafafb96196d3dea279ce782efd97b212f364ef5", - "0xb86693b3ea23ea6b2c4d52554f61ef39c0ef57e514ff6da80c6e54395df8376e2e96b9d50e4ec301c59e022c5c5610db", - "0xaf4d7cd678d79e67ae19789d43331dff99346cd18efff7bab68f6170c111598d32837372e3afe3e881fd1e984648483e", - "0xb8735a555ba7fe294e7adc471145276b6525de31cda8c75aae39182915129025fb572ed10c51392e93c114f3a71bd0be", - "0xb1dfb6dbda4e0faaa90fe0154f4ddaf68ee7da19b03daad1356a8550fca78a7354a58e00adeecb364e2fd475f8242c24", - "0x9044b73c1bd19cd8bb46d778214d047f5dd89b99b42466431b661279220af5c50c0cffecebd2b64c3d0847a9c7a8b1ec", - "0x891f0d162651a0aa0d68fb1cc39fa8d77fd9f41ff98b5d6c056c969c4bac05ba8c52cbfa7fbb6ef9adfe44543a6ec416", - "0x8920ae1d5ac05bf4be6aba843e9fc1bc5b109817381cdd9aa13df53cabea319a34ee122dcb32086d880b20900ff28239", - "0xabb14023142876cbc9301336dced18c7878daa830070b5515ff4ac87b7bf358aa7ff129ebbf6fb78e827570a4142661f", - "0xa74b15e178cf91cde56eab0332e62d5ff84c05fcc849b86f45f94d7978bf9c0fc72a04f24d092a9d795ca3d976467f46", - "0x806829621a908ca9b6433f04557a305814a95d91c13152dca221e4c56bfaa3473d8bb1bacd66e5095a53070f85954278", - "0xb09a3c185e93869aa266a0593456a5d70587712bca81983dbc9eebbb0bd4b9108a38ae1643020ecf60c39c55bb3ac062", - "0xb2bbe8f5361a3ecdb19598dd02e85a4c4c87e009f66fee980b4819a75d61f0a5c5e0bdc882830606cb89554ef1f90ead", - "0x825e16cb54fc2e378187aedae84a037e32903467ac022deb302cf4142da3eda3ead5b9f3e188d44f004824a3b5d94fbe", - "0x8b39d4a11d9b8ba885d36bcdb6446b41da12cfd66cb22705be05ab86936464716954360cc403f8a0fd3db6d8b301cb59", - "0xac19d453106c9121b856c4b327ddb3e3112b3af04793df13f02d760842b93d1b1fbdff5734edc38e53103a6e429a1d1f", - "0xb1cacbb965ec563f9e07d669ffc5e84d4149f1fb9fcfbc505788c073578c8f67956fb8f603e0b9a9d65e2d41803038ce", - "0xb7612d9e7dc930bff29191d1503feb2d6451b368b69fa8ecb06353c959967daccdc262a963f01c7fb95496f1bd50d92e", - "0x93f8fceb65ea9ef2052fa8113fb6720c94f0fed3432d89014ee5ad16260aeb428aadea0d1f1e002d2f670612ba565da3", - "0xb3eb9213752156ed1fced3bca151fd0c630554215c808b9a0938b55fed42b6b89f9b76bc698f3e37c3c348d2395dbed1", - "0xb46ab3553ef172ae40fc21c51d1d7eab8599a67f2f89a32a971aa52c2f031664e268b976dd2f7dc2195458fcf4bf3860", - "0x8fb66f2c67ca5b6fb371c7d04592385a15df0c343857ba8037fe2aa9f2a5d4abc1058323ff9652653261b1c7db0edc24", - "0xa7dfdbbf0b14e4af70fdb017875cdc36ad2108f90deb30bfca49301c92cbf821645a00ade1d1ee59a1a55a346675c904", - "0x856199cad25ec80ee0327869077f272e33d59bf2af66c972e4a5839ec3b2a689e16f7fd0a03a3138bec458fcff8edbea", - "0xa2842ac5a715c2f48394988c6f84a6644c567673806feaa575838e906138c1b25d699e1b6ffdfc9be850b15da34077e4", - "0x814b448ada88f769de33054c3c19f988226317797acacdbe55ed2485b52cd259ac5bcbee13f9de057eee33930a7fa0c0", - "0xb49de8dd90da916ed374ca42665464b6abe89ff4453168921f5a7e5ddd3dcfa69422782e389e586e531fd78a1f236a8b", - "0x851f9d942b4c8ffc020c02c7fbee0f65ef42b1ab210ab4668a3db6aa0f8ab9eedb16f6fd739a542cc7e3cc03172b565b", - "0xa5128c155b8062d7fa0117412f43a6fdc2de98fa5628e1f5fc1175de0fa49fc52d015ec0aff228f060628268359e299c", - "0xb0765849127cc4ce1a1668011556367d22ce46027aa3056f741c7869287abcaccf0da726a5781a03964a9ded1febf67d", - "0x984562c64f3338ffe82f840c6a98a3dc958113f7ed28ee085af6890bbc0cd025723543a126df86f379e9c4771bb69c17", - "0x8087fe60a9a22a4333f6fbe7d070b372c428d8c5df3804bb874b6035e5602c0693757fb30a9cd5a86684b5bca6737106", - "0xa15e195b5850f7d45674cdc3bd74f972768b46fe9473182498263edc401745a8716fc532df8fc8c1375e39e391019226", - "0x858ec10208c14a67c4156ea9c147f36d36c4fa0a232195b647e976ba82c8e16262b2b68d31e3b4702070c3dc701bccb5", - "0x84bf3fb83c003380ee1158e2d6b1dca75cd14c7b2a32aec89d901f0d79e1475aa0827cb07cba1784a6bb0d37f6ca5cd4", - "0x91e69f5392648e7f7c698059a0fc4b8478ab8af166d3842fb382ec5c396daa082ee3b2cb0192da3c9d90f6523c4c039d", - "0x8f7299f451c5e641d6fd961946b7a6ba4755685b2a40164e6276c25aefc66715b92492097a191813d39bb4405dc5da36", - "0xade2cf04ff6c94c1019bfa1e0e8f580696230fa6ee9695c4772e5a44501b2fffdd765ec7cc71ba14b83559ad62cc0fc5", - "0x85fc98ecf469d6f98c8b3e441680816f764de39001a249bc7162f990c5a5354683e849164d4fc9287ee516780cdcd436", - "0x928d118188120d038c37abdbe66c05adaa87f1cf9957dee2783b09fa91c4c43a7b0d0b2b6c5f4dea57e3ec8af230e84f", - "0x8025f71cf8d3085d6ea5104dddea8fa66cdb8527e40db01472469be021632daf22721f4acf1a8698a53439fe2f82596c", - "0x83266fffb12b3c795a6b551ac2aa7d9a29c183f861e78768c11286a04e22bd423bba05a68775bd77273e3ca316a4318e", - "0x95fd0c69c2d9df4e795c7ba71ed71a9d9f2878cd7e3a64be7b671d9611649fd41d29f8bdab642ba19cbd3db660d6a7e7", - "0x92a912cb4d5ef4b639876daf4289500c4ebdbd80aff07fd93dc3eea645f084f910e5c02c10492a37f16acaa7e646d073", - "0xb3d2622c987189a0873932aaea8b92ebb6e9e67ff46e91a96bf733c3b84175fffe950f8f4622cc4fa50f116321c5537f", - "0xa98f9a40054b31023a8f7549a44cae853b379bbfe673c815b8726e43ecd11a96db40b20369d712cbf72ffab064ecfac5", - "0xb4e9a38e371fc21f4b8a3d7ad173c9ffad0554530dc053365d9555ddb60f5c9063c72ff4c65d78b091af631a9e1ee430", - "0x875a31aee4ba19e09f8c2754fab0b5530ec283c7861a4858b239a12432f09ef155a35fedb0bc33eac2117c7e62f1c7ee", - "0x95edd0d1a6e94af718590756b5c5f5492f1c3441ecc7fa22f4e37f4ec256b9fffd2fda4c11fc1a7c220daee096eb1ff8", - "0xb35fdc435adc73e15c5aaf4e2eea795f9e590d3e3ee4066cafa9c489ee5917466c2a4c897a186b2d27b848c8a65fa8a8", - "0x94a5ce56f8d72ec4d0f480cb8f03e52b22f7d43f949a4b50d4a688a928ffd2c9074ecbab37733c0c30759204a54f9a6a", - "0x987562d78ef42228c56074193f80de1b5a9ed625dd7c4c7df3bf5096e7d7b08e2ee864bd12d2ea563e24fa20ad4d30ef", - "0x95a8218405038c991ace2f45980dbb1efa9e4ad0d8153486b0213a89e4d7e3cac6d607100660784627c74f90a8e55482", - "0xb6a29d566f5a924355b7f7912f55140e1b5f99f983c614b8a92814ce261f2750e8db178866651ea3b461fb8f92890b14", - "0xafdacc0a13da0446a92455f57a42b3ba27ba707f24171727aa974d05143fae219de9e2eb7c857235dd9c7568f43be5a8", - "0x862a7dc25f7cfa4a09aeca0ed2c9c5ee66189e119e226720b19344e231981504e37bca179aa7cad238ee3ab1386aa722", - "0xa336364e76635f188e544613a47a85978073f1686e4ee7a8987f54da91c4193540ac448b91d07d1fc5c7a8538b1f1688", - "0x8f1ddca9638decd8247c1ce49c1e6cf494d03d91c4f33e48a84452d12b6736e8bd18c157068dfeff3a90977af19e5b1e", - "0x96ae91b9aaf00e437c18ddfc1aef2113ee278153ba090aedeb3f48f1e66feb8897bb1ac7f5ffeffc3be29376dd51e498", - "0x8230b5bd9067efb6089e50213f1cc84da892e6faf0b79d5e4768c29303a80b1b754cb09d17a21933aba4c5f32070878a", - "0xa79dfe217faec7b4d3cf97d8363949efbc6f3d2c6bbc25df2c7bb8b7fd2521e6d3fa76672bfc06de6f426290d0b3cc45", - "0x8290bd36552609d6b3ac9ccb57ff8668fc8290548eecdcee9a231f1125298c20bd8e60f033214dfbd42cd3c8642c699b", - "0x8945db9e8ec437c5145add028d25936ee8823ceb300a959402d262232ae0cbd9a64c1f0a1be6aed15ff152202ea9a70c", - "0x949e232b48adeaf57bd38eacb035267d3e78333c6b4524cab86651a428a730baf9c27ff42cb172526d925de863132e82", - "0x98917e7a5073a9c93a526399bb74af71c76958a74619caccf47949f8fd25962810a19e399b4efcba0c550c371bea3676", - "0xb5b144e0707aefc853ea5570bd78dedc4e690cf29edc9413080f28335ac78022139bfe7f7d6986eb1f76872bb91e82ad", - "0x949945072a08de6fd5838e9d2c3dc3200d048b5d21183020240fa13e71a1a8d30e6bfee4e6895e91d87b92f1444d0589", - "0xb351a03c7c98506ee92d7fb9476065839baa8ed8ac1dc250f5a095c0d4c8abcfab62690d29d001f0862672da29721f16", - "0xa82d81c136bc5e418d1fba614cb40a11f39dc526e66a8b1d7609f42fea4c02b63196315014400084f31f62c24b177cbd", - "0x87d51c907fdcdf528d01291b28adfee1e5b6221c6da68fd92ab66126247cd8086a6bcffff0ea17e7b57b0ba8d01bb95d", - "0xa2a9a1a91dfd918f36c1bfeeca705ea8e926ee012f8c18d633e50ec6e50f68f3380ef2ee839e5a43cf80fbb75bfb5304", - "0x86f22616caed13c9e9cd5568175b6b0a6a463f9a15c301b8766feca593efa6e5ee4c7066e1cd61b407c0be12b3d8236a", - "0xb57e0a2c42790d2fd0207ef6476a433fca0cf213d70840c4af1ad45833f23fca082d21a484f78af447a19a0b068ea55c", - "0x8ae9bda5d85e6e3600dde26379b7270abd088678098506b72196ac8f9ce5b0173bc9c7ff245c95cbab5b5b967bcb043b", - "0x95c7d11f6c874f59ba632b63ce07a7a9d917a74d0b89cefa043f52aa1a7fe2e81c38dea0b20378264b5b4f64039932bc", - "0xac7dee7479f50722526ea1c9d4e2f1a4578d1b5cce2092a07722069c96bb4da295de1c4f16e21005276e3b3f1624ac5a", - "0x89b8aaa49bd18b09f78fc5a1f3dd85d69b5dfcff28fc6d5a92b1520bc54107b8b71bb71fd6e0bde10e0a5809c633e5d2", - "0x8982cb43fe4d3488c55e8c08b935e6c8d31bb57e4f2aeb76d6319470cce99ebf7dc2f116ac15b9d845ab1bc16aa6a583", - "0xa12c63f48e27b1a1c83a32992642f37fb5b89851a35e80f6d1f9bc483cb25acd0e12b1dcf68781ae0cc861f002368bcb", - "0xaa6da92a4b4fa229afc8007abca257ce0ff5fad3b1ccfe5d836b9b52ff6b72575a0b915a759403b993733b16a47fdb15", - "0x8bf706a92fe54f15d633b9463926b874dd43e28aaeca3fe2353fb58ad7753c8a293c56b0e94176070e8a9ec7401073a1", - "0xb81e86de4bb5c1046e40cca79585c5b98c8673626fd3a28e563c5a3296256c2f7086522ae95cbabfaa8f1a8f7eae6272", - "0xad10f895b05d35cb251f78cc042d3f0969a8b6b3f289ddb4b016e0b8e06bfffc3a3e1afa9b0cc548f8c092832bb766bc", - "0xad993aceb68d5217cfb07f862956cde83d05dec5060fc7a8fbfd37c6bfd5429ba69bdaf478b6cd01c323a06793dcd9fa", - "0x83da9c9a8fcb2775df0777aceabe90642a2df1c6abc646566e954f42d6e43455b00b101ec5ef58850c8d4b3100222ca1", - "0xb55484f339fe7c7d107e70432601f4a34e1cc02ae4de5d18b99e5aa995f7b3710fc745769b85c1af803d457491dd8ce3", - "0x8009d80593e82f3e751cec9e7e495fd29ad6f45f8d3ae513bec998b43c49fed74c44229c6f27c421e80c65413b897644", - "0x9868081bbcc71192f7ff8dcf99a91dcd40f96556fbd6f285bdbfdfc785f604d8bf75c368c59db5ac8cdcc663087db53a", - "0xa04b1e91af025b4387ee0a2d790a1afb842e46f4c3717e355578efd1f84fea78782c6f7944b4961268de7f1ac71fb92b", - "0xa7b6301ddb9738b89b28a36d29d5323264a78d93d369f57ddab4cea399c36018a1fcc2cc1bfadf956a775124ae2925bd", - "0xa6cdb469014b33c590a07a728ce48f15f17c027eb92055e1858a1f9805c8deb58491a471aaa765de86a6bda62a18aef4", - "0x828a23280ec67384a8846376378896037bd0cb5a6927ff9422fca266ee10a6fde5b95d963a4acfa92efbb0309cdb17b4", - "0xb498ec16bcdb50091647ae02d199d70c783d7c91348a1354661b1c42bc1266e5a5309b542ef5fdf5281d426819a671cb", - "0x806533fb603e78b63598ff390375eebe5b68380640f5e020e89a5430037db2e519ab8ae5d0d0ad3fa041921c098448e1", - "0x9104ad119681c54cdee19f0db92ebfe1da2fa6bef4177f5a383df84512d1b0af5cbe7baf6a93ad4b89138cd51c7c5838", - "0xac695cde30d021d9f4f295109890c4013f7e213d2150c9d5c85a36d4abfdca4cdc88faee9891e927a82fc204b988dcd9", - "0xa311c244df546d5dc76eccb91fe4c47055fc9d222d310b974d4c067923a29e7a7f6d5a88bfef72fd6d317471f80d5c82", - "0x89e4518335240479ad041a0915fc4f1afaab660bd4033c5d09c6707f0cc963eb2e6872cabc4a02169893943be7f847d4", - "0xa8ad395b784c83aacf133de50d6b23bd63b4f245bb9e180c11f568faca4c897f8dbda73335ef0f80a8cb548a0c3c48fc" + "0xb5bfd7dd8cdeb128843bc287230af38926187075cbfbefa81009a2ce615ac53d2914e5870cb452d2afaaab24f3499f72185cbfee53492714734429b7b38608e23926c911cceceac9a36851477ba4c60b087041de621000edc98edada20c1def2", + "0xb5337ba0ce5d37224290916e268e2060e5c14f3f9fc9e1ec3af5a958e7a0303122500ce18f1a4640bf66525bd10e763501fe986d86649d8d45143c08c3209db3411802c226e9fe9a55716ac4a0c14f9dcef9e70b2bb309553880dc5025eab3cc", + "0xb3c1dcdc1f62046c786f0b82242ef283e7ed8f5626f72542aa2c7a40f14d9094dd1ebdbd7457ffdcdac45fd7da7e16c51200b06d791e5e43e257e45efdf0bd5b06cd2333beca2a3a84354eb48662d83aef5ecf4e67658c851c10b13d8d87c874", + "0x954d91c7688983382609fca9e211e461f488a5971fd4e40d7e2892037268eacdfd495cfa0a7ed6eb0eb11ac3ae6f651716757e7526abe1e06c64649d80996fd3105c20c4c94bc2b22d97045356fe9d791f21ea6428ac48db6f9e68e30d875280", + "0x88a6b6bb26c51cf9812260795523973bb90ce80f6820b6c9048ab366f0fb96e48437a7f7cb62aedf64b11eb4dfefebb0147608793133d32003cb1f2dc47b13b5ff45f1bb1b2408ea45770a08dbfaec60961acb8119c47b139a13b8641e2c9487", + "0x85cd7be9728bd925d12f47fb04b32d9fad7cab88788b559f053e69ca18e463113ecc8bbb6dbfb024835f901b3a957d3108d6770fb26d4c8be0a9a619f6e3a4bf15cbfd48e61593490885f6cee30e4300c5f9cf5e1c08e60a2d5b023ee94fcad0", + "0x80477dba360f04399821a48ca388c0fa81102dd15687fea792ee8c1114e00d1bc4839ad37ac58900a118d863723acfbe08126ea883be87f50e4eabe3b5e72f5d9e041db8d9b186409fd4df4a7dde38c0e0a3b1ae29b098e5697e7f110b6b27e4", + "0xb7a6aec08715a9f8672a2b8c367e407be37e59514ac19dd4f0942a68007bba3923df22da48702c63c0d6b3efd3c2d04e0fe042d8b5a54d562f9f33afc4865dcbcc16e99029e25925580e87920c399e710d438ac1ce3a6dc9b0d76c064a01f6f7", + "0xac1b001edcea02c8258aeffbf9203114c1c874ad88dae1184fadd7d94cd09053649efd0ca413400e6e9b5fa4eac33261000af88b6bd0d2abf877a4f0355d2fb4d6007adb181695201c5432e50b850b51b3969f893bddf82126c5a71b042b7686", + "0x90043fda4de53fb364fab2c04be5296c215599105ecff0c12e4917c549257125775c29f2507124d15f56e30447f367db0596c33237242c02d83dfd058735f1e3c1ff99069af55773b6d51d32a68bf75763f59ec4ee7267932ae426522b8aaab6", + "0xa8660ce853e9dc08271bf882e29cd53397d63b739584dda5263da4c7cc1878d0cf6f3e403557885f557e184700575fee016ee8542dec22c97befe1d10f414d22e84560741cdb3e74c30dda9b42eeaaf53e27822de2ee06e24e912bf764a9a533", + "0x8fe3921a96d0d065e8aa8fce9aa42c8e1461ca0470688c137be89396dd05103606dab6cdd2a4591efd6addf72026c12e065da7be276dee27a7e30afa2bd81c18f1516e7f068f324d0bad9570b95f6bd02c727cd2343e26db0887c3e4e26dceda", + "0x8ae1ad97dcb9c192c9a3933541b40447d1dc4eebf380151440bbaae1e120cc5cdf1bcea55180b128d8e180e3af623815191d063cc0d7a47d55fb7687b9d87040bf7bc1a7546b07c61db5ccf1841372d7c2fe4a5431ffff829f3c2eb590b0b710", + "0x8c2fa96870a88150f7876c931e2d3cc2adeaaaf5c73ef5fa1cf9dfa0991ae4819f9321af7e916e5057d87338e630a2f21242c29d76963cf26035b548d2a63d8ad7bd6efefa01c1df502cbdfdfe0334fb21ceb9f686887440f713bf17a89b8081", + "0xb9aa98e2f02bb616e22ee5dd74c7d1049321ac9214d093a738159850a1dbcc7138cb8d26ce09d8296368fd5b291d74fa17ac7cc1b80840fdd4ee35e111501e3fa8485b508baecda7c1ab7bd703872b7d64a2a40b3210b6a70e8a6ffe0e5127e3", + "0x9292db67f8771cdc86854a3f614a73805bf3012b48f1541e704ea4015d2b6b9c9aaed36419769c87c49f9e3165f03edb159c23b3a49c4390951f78e1d9b0ad997129b17cdb57ea1a6638794c0cca7d239f229e589c5ae4f9fe6979f7f8cba1d7", + "0x91cd9e86550f230d128664f7312591fee6a84c34f5fc7aed557bcf986a409a6de722c4330453a305f06911d2728626e611acfdf81284f77f60a3a1595053a9479964fd713117e27c0222cc679674b03bc8001501aaf9b506196c56de29429b46", + "0xa9516b73f605cc31b89c68b7675dc451e6364595243d235339437f556cf22d745d4250c1376182273be2d99e02c10eee047410a43eff634d051aeb784e76cb3605d8e079b9eb6ad1957dfdf77e1cd32ce4a573c9dfcc207ca65af6eb187f6c3d", + "0xa9667271f7d191935cc8ad59ef3ec50229945faea85bfdfb0d582090f524436b348aaa0183b16a6231c00332fdac2826125b8c857a2ed9ec66821cfe02b3a2279be2412441bc2e369b255eb98614e4be8490799c4df22f18d47d24ec70bba5f7", + "0xa4371144d2aa44d70d3cb9789096d3aa411149a6f800cb46f506461ee8363c8724667974252f28aea61b6030c05930ac039c1ee64bb4bd56532a685cae182bf2ab935eee34718cffcb46cae214c77aaca11dbb1320faf23c47247db1da04d8dc", + "0x89a7eb441892260b7e81168c386899cd84ffc4a2c5cad2eae0d1ab9e8b5524662e6f660fe3f8bfe4c92f60b060811bc605b14c5631d16709266886d7885a5eb5930097127ec6fb2ebbaf2df65909cf48f253b3d5e22ae48d3e9a2fd2b01f447e", + "0x9648c42ca97665b5eccb49580d8532df05eb5a68db07f391a2340769b55119eaf4c52fe4f650c09250fa78a76c3a1e271799b8333cc2628e3d4b4a6a3e03da1f771ecf6516dd63236574a7864ff07e319a6f11f153406280d63af9e2b5713283", + "0x9663bf6dd446ea7a90658ee458578d4196dc0b175ef7fcfa75f44d41670850774c2e46c5a6be132a2c072a3c0180a24f0305d1acac49d2d79878e5cda80c57feda3d01a6af12e78b5874e2a4b3717f11c97503b41a4474e2e95b179113726199", + "0xb212aeb4814e0915b432711b317923ed2b09e076aaf558c3ae8ef83f9e15a83f9ea3f47805b2750ab9e8106cb4dc6ad003522c84b03dc02829978a097899c773f6fb31f7fe6b8f2d836d96580f216fec20158f1590c3e0d7850622e15194db05", + "0x925f005059bf07e9ceccbe66c711b048e236ade775720d0fe479aebe6e23e8af281225ad18e62458dc1b03b42ad4ca290d4aa176260604a7aad0d9791337006fbdebe23746f8060d42876f45e4c83c3643931392fde1cd13ff8bddf8111ef974", + "0x9553edb22b4330c568e156a59ef03b26f5c326424f830fe3e8c0b602f08c124730ffc40bc745bec1a22417adb22a1a960243a10565c2be3066bfdb841d1cd14c624cd06e0008f4beb83f972ce6182a303bee3fcbcabc6cfe48ec5ae4b7941bfc", + "0x935f5a404f0a78bdcce709899eda0631169b366a669e9b58eacbbd86d7b5016d044b8dfc59ce7ed8de743ae16c2343b50e2f925e88ba6319e33c3fc76b314043abad7813677b4615c8a97eb83cc79de4fedf6ccbcfa4d4cbf759a5a84e4d9742", + "0xa5b014ab936eb4be113204490e8b61cd38d71da0dec7215125bcd131bf3ab22d0a32ce645bca93e7b3637cf0c2db3d6601a0ddd330dc46f9fae82abe864ffc12d656c88eb50c20782e5bb6f75d18760666f43943abb644b881639083e122f557", + "0x935b7298ae52862fa22bf03bfc1795b34c70b181679ae27de08a9f5b4b884f824ef1b276b7600efa0d2f1d79e4a470d51692fd565c5cf8343dd80e5d3336968fc21c09ba9348590f6206d4424eb229e767547daefa98bc3aa9f421158dee3f2a", + "0x9830f92446e708a8f6b091cc3c38b653505414f8b6507504010a96ffda3bcf763d5331eb749301e2a1437f00e2415efb01b799ad4c03f4b02de077569626255ac1165f96ea408915d4cf7955047620da573e5c439671d1fa5c833fb11de7afe6", + "0x840dcc44f673fff3e387af2bb41e89640f2a70bcd2b92544876daa92143f67c7512faf5f90a04b7191de01f3e2b1bde00622a20dc62ca23bbbfaa6ad220613deff43908382642d4d6a86999f662efd64b1df448b68c847cfa87630a3ffd2ec76", + "0x92950c895ed54f7f876b2fda17ecc9c41b7accfbdd42c210cc5b475e0737a7279f558148531b5c916e310604a1de25a80940c94fe5389ae5d6a5e9c371be67bceea1877f5401725a6595bcf77ece60905151b6dfcb68b75ed2e708c73632f4fd", + "0x8010246bf8e94c25fd029b346b5fbadb404ef6f44a58fd9dd75acf62433d8cc6db66974f139a76e0c26dddc1f329a88214dbb63276516cf325c7869e855d07e0852d622c332ac55609ba1ec9258c45746a2aeb1af0800141ee011da80af175d4", + "0xb0f1bad257ebd187bdc3f37b23f33c6a5d6a8e1f2de586080d6ada19087b0e2bf23b79c1b6da1ee82271323f5bdf3e1b018586b54a5b92ab6a1a16bb3315190a3584a05e6c37d5ca1e05d702b9869e27f513472bcdd00f4d0502a107773097da", + "0x9636d24f1ede773ce919f309448dd7ce023f424afd6b4b69cb98c2a988d849a283646dc3e469879daa1b1edae91ae41f009887518e7eb5578f88469321117303cd3ac2d7aee4d9cb5f82ab9ae3458e796dfe7c24284b05815acfcaa270ff22e2", + "0xb373feb5d7012fd60578d7d00834c5c81df2a23d42794fed91aa9535a4771fde0341c4da882261785e0caca40bf83405143085e7f17e55b64f6c5c809680c20b050409bf3702c574769127c854d27388b144b05624a0e24a1cbcc4d08467005b", + "0xb15680648949ce69f82526e9b67d9b55ce5c537dc6ab7f3089091a9a19a6b90df7656794f6edc87fb387d21573ffc847062623685931c2790a508cbc8c6b231dd2c34f4d37d4706237b1407673605a604bcf6a50cc0b1a2db20485e22b02c17e", + "0x8817e46672d40c8f748081567b038a3165f87994788ec77ee8daea8587f5540df3422f9e120e94339be67f186f50952504cb44f61e30a5241f1827e501b2de53c4c64473bcc79ab887dd277f282fbfe47997a930dd140ac08b03efac88d81075", + "0xa6e4ef6c1d1098f95aae119905f87eb49b909d17f9c41bcfe51127aa25fee20782ea884a7fdf7d5e9c245b5a5b32230b07e0dbf7c6743bf52ee20e2acc0b269422bd6cf3c07115df4aa85b11b2c16630a07c974492d9cdd0ec325a3fabd95044", + "0x8634aa7c3d00e7f17150009698ce440d8e1b0f13042b624a722ace68ead870c3d2212fbee549a2c190e384d7d6ac37ce14ab962c299ea1218ef1b1489c98906c91323b94c587f1d205a6edd5e9d05b42d591c26494a6f6a029a2aadb5f8b6f67", + "0x821a58092900bdb73decf48e13e7a5012a3f88b06288a97b855ef51306406e7d867d613d9ec738ebacfa6db344b677d21509d93f3b55c2ebf3a2f2a6356f875150554c6fff52e62e3e46f7859be971bf7dd9d5b3e1d799749c8a97c2e04325df", + "0x8dba356577a3a388f782e90edb1a7f3619759f4de314ad5d95c7cc6e197211446819c4955f99c5fc67f79450d2934e3c09adefc91b724887e005c5190362245eec48ce117d0a94d6fa6db12eda4ba8dde608fbbd0051f54dcf3bb057adfb2493", + "0xa32a690dc95c23ed9fb46443d9b7d4c2e27053a7fcc216d2b0020a8cf279729c46114d2cda5772fd60a97016a07d6c5a0a7eb085a18307d34194596f5b541cdf01b2ceb31d62d6b55515acfd2b9eec92b27d082fbc4dc59fc63b551eccdb8468", + "0xa040f7f4be67eaf0a1d658a3175d65df21a7dbde99bfa893469b9b43b9d150fc2e333148b1cb88cfd0447d88fa1a501d126987e9fdccb2852ecf1ba907c2ca3d6f97b055e354a9789854a64ecc8c2e928382cf09dda9abde42bbdf92280cdd96", + "0x864baff97fa60164f91f334e0c9be00a152a416556b462f96d7c43b59fe1ebaff42f0471d0bf264976f8aa6431176eb905bd875024cf4f76c13a70bede51dc3e47e10b9d5652d30d2663b3af3f08d5d11b9709a0321aba371d2ef13174dcfcaf", + "0x95a46f32c994133ecc22db49bad2c36a281d6b574c83cfee6680b8c8100466ca034b815cfaedfbf54f4e75188e661df901abd089524e1e0eb0bf48d48caa9dd97482d2e8c1253e7e8ac250a32fd066d5b5cb08a8641bdd64ecfa48289dca83a3", + "0xa2cce2be4d12144138cb91066e0cd0542c80b478bf467867ebef9ddaf3bd64e918294043500bf5a9f45ee089a8d6ace917108d9ce9e4f41e7e860cbce19ac52e791db3b6dde1c4b0367377b581f999f340e1d6814d724edc94cb07f9c4730774", + "0xb145f203eee1ac0a1a1731113ffa7a8b0b694ef2312dabc4d431660f5e0645ef5838e3e624cfe1228cfa248d48b5760501f93e6ab13d3159fc241427116c4b90359599a4cb0a86d0bb9190aa7fabff482c812db966fd2ce0a1b48cb8ac8b3bca", + "0xadabe5d215c608696e03861cbd5f7401869c756b3a5aadc55f41745ad9478145d44393fec8bb6dfc4ad9236dc62b9ada0f7ca57fe2bae1b71565dbf9536d33a68b8e2090b233422313cc96afc7f1f7e0907dc7787806671541d6de8ce47c4cd0", + "0xae7845fa6b06db53201c1080e01e629781817f421f28956589c6df3091ec33754f8a4bd4647a6bb1c141ac22731e3c1014865d13f3ed538dcb0f7b7576435133d9d03be655f8fbb4c9f7d83e06d1210aedd45128c2b0c9bab45a9ddde1c862a5", + "0x9159eaa826a24adfa7adf6e8d2832120ebb6eccbeb3d0459ffdc338548813a2d239d22b26451fda98cc0c204d8e1ac69150b5498e0be3045300e789bcb4e210d5cd431da4bdd915a21f407ea296c20c96608ded0b70d07188e96e6c1a7b9b86b", + "0xa9fc6281e2d54b46458ef564ffaed6944bff71e389d0acc11fa35d3fcd8e10c1066e0dde5b9b6516f691bb478e81c6b20865281104dcb640e29dc116daae2e884f1fe6730d639dbe0e19a532be4fb337bf52ae8408446deb393d224eee7cfa50", + "0x84291a42f991bfb36358eedead3699d9176a38f6f63757742fdbb7f631f2c70178b1aedef4912fed7b6cf27e88ddc7eb0e2a6aa4b999f3eb4b662b93f386c8d78e9ac9929e21f4c5e63b12991fcde93aa64a735b75b535e730ff8dd2abb16e04", + "0xa1b7fcacae181495d91765dfddf26581e8e39421579c9cbd0dd27a40ea4c54af3444a36bf85a11dda2114246eaddbdd619397424bb1eb41b5a15004b902a590ede5742cd850cf312555be24d2df8becf48f5afba5a8cd087cb7be0a521728386", + "0x92feaaf540dbd84719a4889a87cdd125b7e995a6782911931fef26da9afcfbe6f86aaf5328fe1f77631491ce6239c5470f44c7791506c6ef1626803a5794e76d2be0af92f7052c29ac6264b7b9b51f267ad820afc6f881460521428496c6a5f1", + "0xa525c925bfae1b89320a5054acc1fa11820f73d0cf28d273092b305467b2831fab53b6daf75fb926f332782d50e2522a19edcd85be5eb72f1497193c952d8cd0bcc5d43b39363b206eae4cb1e61668bde28a3fb2fc1e0d3d113f6dfadb799717", + "0x98752bb6f5a44213f40eda6aa4ff124057c1b13b6529ab42fe575b9afa66e59b9c0ed563fb20dff62130c436c3e905ee17dd8433ba02c445b1d67182ab6504a90bbe12c26a754bbf734665c622f76c62fe2e11dd43ce04fd2b91a8463679058b", + "0xa9aa9a84729f7c44219ff9e00e651e50ddea3735ef2a73fdf8ed8cd271961d8ed7af5cd724b713a89a097a3fe65a3c0202f69458a8b4c157c62a85668b12fc0d3957774bc9b35f86c184dd03bfefd5c325da717d74192cc9751c2073fe9d170e", + "0xb221c1fd335a4362eff504cd95145f122bf93ea02ae162a3fb39c75583fc13a932d26050e164da97cff3e91f9a7f6ff80302c19dd1916f24acf6b93b62f36e9665a8785413b0c7d930c7f1668549910f849bca319b00e59dd01e5dec8d2edacc", + "0xa71e2b1e0b16d754b848f05eda90f67bedab37709550171551050c94efba0bfc282f72aeaaa1f0330041461f5e6aa4d11537237e955e1609a469d38ed17f5c2a35a1752f546db89bfeff9eab78ec944266f1cb94c1db3334ab48df716ce408ef", + "0xb990ae72768779ba0b2e66df4dd29b3dbd00f901c23b2b4a53419226ef9232acedeb498b0d0687c463e3f1eead58b20b09efcefa566fbfdfe1c6e48d32367936142d0a734143e5e63cdf86be7457723535b787a9cfcfa32fe1d61ad5a2617220", + "0x8d27e7fbff77d5b9b9bbc864d5231fecf817238a6433db668d5a62a2c1ee1e5694fdd90c3293c06cc0cb15f7cbeab44d0d42be632cb9ff41fc3f6628b4b62897797d7b56126d65b694dcf3e298e3561ac8813fbd7296593ced33850426df42db", + "0xa92039a08b5502d5b211a7744099c9f93fa8c90cedcb1d05e92f01886219dd464eb5fb0337496ad96ed09c987da4e5f019035c5b01cc09b2a18b8a8dd419bc5895388a07e26958f6bd26751929c25f89b8eb4a299d822e2d26fec9ef350e0d3c", + "0x92dcc5a1c8c3e1b28b1524e3dd6dbecd63017c9201da9dbe077f1b82adc08c50169f56fc7b5a3b28ec6b89254de3e2fd12838a761053437883c3e01ba616670cea843754548ef84bcc397de2369adcca2ab54cd73c55dc68d87aec3fc2fe4f10" ] } \ No newline at end of file diff --git a/go.mod b/go.mod index 30ae287982cf..d849d578c82b 100644 --- a/go.mod +++ b/go.mod @@ -46,10 +46,10 @@ require ( require ( github.com/btcsuite/btcd/btcec/v2 v2.3.4 github.com/consensys/gnark-crypto v0.10.0 - github.com/crate-crypto/go-kzg-4844 v0.3.0 + github.com/crate-crypto/go-kzg-4844 v0.7.0 github.com/deckarep/golang-set v1.8.0 github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 - github.com/ethereum/c-kzg-4844 v0.3.1 + github.com/ethereum/c-kzg-4844 v0.4.0 github.com/kylelemons/godebug v1.1.0 github.com/mattn/go-isatty v0.0.17 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible diff --git a/go.sum b/go.sum index d7fc489ab42e..55a922e8cc24 100644 --- a/go.sum +++ b/go.sum @@ -23,8 +23,8 @@ github.com/consensys/gnark-crypto v0.10.0 h1:zRh22SR7o4K35SoNqouS9J/TKHTyU2QWaj5 github.com/consensys/gnark-crypto v0.10.0/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU= github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/crate-crypto/go-kzg-4844 v0.3.0 h1:UBlWE0CgyFqqzTI+IFyCzA7A3Zw4iip6uzRv5NIXG0A= -github.com/crate-crypto/go-kzg-4844 v0.3.0/go.mod h1:SBP7ikXEgDnUPONgm33HtuDZEDtWa3L4QtN1ocJSEQ4= +github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= +github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -43,8 +43,8 @@ github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 h1:Y9vTBSsV4hSwPSj4bac github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/ethereum/c-kzg-4844 v0.3.1 h1:sR65+68+WdnMKxseNWxSJuAv2tsUrihTpVBTfM/U5Zg= -github.com/ethereum/c-kzg-4844 v0.3.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= +github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= From cae53aa7fdb279e50c154a97a7861ea7f48387dc Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:32 +0800 Subject: [PATCH 301/479] crypto/kzg4844: add helpers for versioned blob hashes (#28827) --- crypto/kzg4844/kzg4844.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/crypto/kzg4844/kzg4844.go b/crypto/kzg4844/kzg4844.go index 5969d1c2cee1..4561ef9de95b 100644 --- a/crypto/kzg4844/kzg4844.go +++ b/crypto/kzg4844/kzg4844.go @@ -20,6 +20,7 @@ package kzg4844 import ( "embed" "errors" + "hash" "sync/atomic" ) @@ -108,3 +109,21 @@ func VerifyBlobProof(blob Blob, commitment Commitment, proof Proof) error { } return gokzgVerifyBlobProof(blob, commitment, proof) } + +// CalcBlobHashV1 calculates the 'versioned blob hash' of a commitment. +// The given hasher must be a sha256 hash instance, otherwise the result will be invalid! +func CalcBlobHashV1(hasher hash.Hash, commit *Commitment) (vh [32]byte) { + if hasher.Size() != 32 { + panic("wrong hash size") + } + hasher.Reset() + hasher.Write(commit[:]) + hasher.Sum(vh[:0]) + vh[0] = 0x01 // version + return vh +} + +// IsValidVersionedHash checks that h is a structurally-valid versioned blob hash. +func IsValidVersionedHash(h []byte) bool { + return len(h) == 32 && h[0] == 0x01 +} From fabfcc7f6c73c9913ba414bea73d4bebca54d8cf Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:32 +0800 Subject: [PATCH 302/479] crypto: fix docstring names (#28923) --- crypto/bn256/google/bn256.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/bn256/google/bn256.go b/crypto/bn256/google/bn256.go index 0a9d5cd35dce..93953e23a95f 100644 --- a/crypto/bn256/google/bn256.go +++ b/crypto/bn256/google/bn256.go @@ -166,7 +166,7 @@ type G2 struct { p *twistPoint } -// RandomG1 returns x and g₂ˣ where x is a random, non-zero number read from r. +// RandomG2 returns x and g₂ˣ where x is a random, non-zero number read from r. func RandomG2(r io.Reader) (*big.Int, *G2, error) { var k *big.Int var err error From 727855752c533b1233d580399552bb3044a9f98e Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:32 +0800 Subject: [PATCH 303/479] crypto: add support for blobs in eth_fillTransaction (#28839) --- crypto/kzg4844/kzg4844.go | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/crypto/kzg4844/kzg4844.go b/crypto/kzg4844/kzg4844.go index 4561ef9de95b..36ac9e2b0d84 100644 --- a/crypto/kzg4844/kzg4844.go +++ b/crypto/kzg4844/kzg4844.go @@ -21,21 +21,60 @@ import ( "embed" "errors" "hash" + "reflect" "sync/atomic" + + "github.com/XinFinOrg/XDPoSChain/common/hexutil" ) //go:embed trusted_setup.json var content embed.FS +var ( + blobT = reflect.TypeOf(Blob{}) + commitmentT = reflect.TypeOf(Commitment{}) + proofT = reflect.TypeOf(Proof{}) +) + // Blob represents a 4844 data blob. type Blob [131072]byte +// UnmarshalJSON parses a blob in hex syntax. +func (b *Blob) UnmarshalJSON(input []byte) error { + return hexutil.UnmarshalFixedJSON(blobT, input, b[:]) +} + +// MarshalText returns the hex representation of b. +func (b Blob) MarshalText() ([]byte, error) { + return hexutil.Bytes(b[:]).MarshalText() +} + // Commitment is a serialized commitment to a polynomial. type Commitment [48]byte +// UnmarshalJSON parses a commitment in hex syntax. +func (c *Commitment) UnmarshalJSON(input []byte) error { + return hexutil.UnmarshalFixedJSON(commitmentT, input, c[:]) +} + +// MarshalText returns the hex representation of c. +func (c Commitment) MarshalText() ([]byte, error) { + return hexutil.Bytes(c[:]).MarshalText() +} + // Proof is a serialized commitment to the quotient polynomial. type Proof [48]byte +// UnmarshalJSON parses a proof in hex syntax. +func (p *Proof) UnmarshalJSON(input []byte) error { + return hexutil.UnmarshalFixedJSON(proofT, input, p[:]) +} + +// MarshalText returns the hex representation of p. +func (p Proof) MarshalText() ([]byte, error) { + return hexutil.Bytes(p[:]).MarshalText() +} + // Point is a BLS field element. type Point [32]byte From 3fbbc9da9f4b51c702427d1ac3e7f90651c39965 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 29 Nov 2024 16:08:32 +0800 Subject: [PATCH 304/479] crypto: fix typos in comments (#29186) --- crypto/kzg4844/kzg4844.go | 2 +- crypto/secp256k1/libsecp256k1/include/secp256k1.h | 2 +- crypto/secp256k1/libsecp256k1/sage/group_prover.sage | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crypto/kzg4844/kzg4844.go b/crypto/kzg4844/kzg4844.go index 36ac9e2b0d84..8424557b19a6 100644 --- a/crypto/kzg4844/kzg4844.go +++ b/crypto/kzg4844/kzg4844.go @@ -85,7 +85,7 @@ type Claim [32]byte var useCKZG atomic.Bool // UseCKZG can be called to switch the default Go implementation of KZG to the C -// library if fo some reason the user wishes to do so (e.g. consensus bug in one +// library if for some reason the user wishes to do so (e.g. consensus bug in one // or the other). func UseCKZG(use bool) error { if use && !ckzgAvailable { diff --git a/crypto/secp256k1/libsecp256k1/include/secp256k1.h b/crypto/secp256k1/libsecp256k1/include/secp256k1.h index f268e309d0bf..76af8396918e 100644 --- a/crypto/secp256k1/libsecp256k1/include/secp256k1.h +++ b/crypto/secp256k1/libsecp256k1/include/secp256k1.h @@ -357,7 +357,7 @@ SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact( /** Verify an ECDSA signature. * * Returns: 1: correct signature - * 0: incorrect or unparseable signature + * 0: incorrect or unparsable signature * Args: ctx: a secp256k1 context object, initialized for verification. * In: sig: the signature being verified (cannot be NULL) * msg32: the 32-byte message hash being verified (cannot be NULL) diff --git a/crypto/secp256k1/libsecp256k1/sage/group_prover.sage b/crypto/secp256k1/libsecp256k1/sage/group_prover.sage index ab580c5b23bb..68882e93659a 100644 --- a/crypto/secp256k1/libsecp256k1/sage/group_prover.sage +++ b/crypto/secp256k1/libsecp256k1/sage/group_prover.sage @@ -17,7 +17,7 @@ # - A constraint describing the requirements of the law, called "require" # * Implementations are transliterated into functions that operate as well on # algebraic input points, and are called once per combination of branches -# exectured. Each execution returns: +# executed. Each execution returns: # - A constraint describing the assumptions this implementation requires # (such as Z1=1), called "assumeFormula" # - A constraint describing the assumptions this specific branch requires, From b794f6a849d5bb45ebafba8630a458639b1553f3 Mon Sep 17 00:00:00 2001 From: Wanwiset Peerapatanapokin Date: Fri, 13 Dec 2024 08:28:46 +0400 Subject: [PATCH 305/479] recover devnet by increasing timeout (#760) --- params/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/params/config.go b/params/config.go index 2b28c9159149..a97f07bfb336 100644 --- a/params/config.go +++ b/params/config.go @@ -131,7 +131,7 @@ var ( SwitchRound: 13625855, CertThreshold: 0.4, TimeoutSyncThreshold: 3, - TimeoutPeriod: 30, + TimeoutPeriod: 60, MinePeriod: 2, }, } From 11b74151bd2c3c16818a92b215f0383942b684cc Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 13:56:09 +0800 Subject: [PATCH 306/479] cmd/XDC: fix double metrics collect process issue --- cmd/XDC/main.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go index 00f87d32f69b..5ececb431df7 100644 --- a/cmd/XDC/main.go +++ b/cmd/XDC/main.go @@ -203,8 +203,6 @@ func init() { return err } flags.CheckEnvVars(ctx, app.Flags, "XDC") - // Start system runtime metrics collection - go metrics.CollectProcessMetrics(3 * time.Second) utils.SetupNetwork(ctx) return nil @@ -307,6 +305,7 @@ func startNode(ctx *cli.Context, stack *node.Node, cfg XDCConfig) { } // Start metrics export if enabled utils.SetupMetrics(ctx) + // Start system runtime metrics collection go metrics.CollectProcessMetrics(3 * time.Second) From 23bc924585b60dffb0ff65916935235f7576df54 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:11 +0800 Subject: [PATCH 307/479] internal/debug: start pprof server by new function StartPProf (#16532) --- internal/debug/flags.go | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/internal/debug/flags.go b/internal/debug/flags.go index de8d587f0852..1e9970b73ce5 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -312,17 +312,8 @@ func Setup(ctx *cli.Context) error { // pprof server if ctx.Bool(pprofFlag.Name) { - // Hook go-metrics into expvar on any /debug/metrics request, load all vars - // from the registry into expvar, and execute regular expvar handler. - exp.Exp(metrics.DefaultRegistry) - address := fmt.Sprintf("%s:%d", ctx.String(pprofAddrFlag.Name), ctx.Int(pprofPortFlag.Name)) - go func() { - log.Info("Starting pprof server", "addr", fmt.Sprintf("http://%s/debug/pprof", address)) - if err := http.ListenAndServe(address, nil); err != nil { - log.Error("Failure in running pprof server", "err", err) - } - }() + StartPProf(address) } if len(logFile) > 0 || rotation { @@ -332,6 +323,18 @@ func Setup(ctx *cli.Context) error { return nil } +func StartPProf(address string) { + // Hook go-metrics into expvar on any /debug/metrics request, load all vars + // from the registry into expvar, and execute regular expvar handler. + exp.Exp(metrics.DefaultRegistry) + log.Info("Starting pprof server", "addr", fmt.Sprintf("http://%s/debug/pprof", address)) + go func() { + if err := http.ListenAndServe(address, nil); err != nil { + log.Error("Failure in running pprof server", "err", err) + } + }() +} + // Exit stops all running profiles, flushing their output to the // respective file. func Exit() { From 92a7b64961940fb2a1090b65bdd7cabc60348f5f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:11 +0800 Subject: [PATCH 308/479] metrics: export to InfluxDB (#16979) --- cmd/XDC/main.go | 19 +++++++-- cmd/utils/flags.go | 82 ++++++++++++++++++++++++++++++------ metrics/config.go | 12 ++++++ metrics/influxdb/influxdb.go | 12 +++--- 4 files changed, 104 insertions(+), 21 deletions(-) diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go index 5ececb431df7..f5bdd24709d2 100644 --- a/cmd/XDC/main.go +++ b/cmd/XDC/main.go @@ -126,9 +126,6 @@ var ( utils.HTTPCORSDomainFlag, utils.HTTPVirtualHostsFlag, utils.EthStatsURLFlag, - utils.MetricsEnabledFlag, - utils.MetricsHTTPFlag, - utils.MetricsPortFlag, //utils.FakePoWFlag, //utils.NoCompactionFlag, //utils.GpoBlocksFlag, @@ -163,6 +160,18 @@ var ( utils.IPCPathFlag, utils.RPCGlobalTxFeeCap, } + + metricsFlags = []cli.Flag{ + utils.MetricsEnabledFlag, + utils.MetricsHTTPFlag, + utils.MetricsPortFlag, + utils.MetricsEnableInfluxDBFlag, + utils.MetricsInfluxDBEndpointFlag, + utils.MetricsInfluxDBDatabaseFlag, + utils.MetricsInfluxDBUsernameFlag, + utils.MetricsInfluxDBPasswordFlag, + utils.MetricsInfluxDBHostTagFlag, + } ) func init() { @@ -194,6 +203,7 @@ func init() { app.Flags = append(app.Flags, rpcFlags...) app.Flags = append(app.Flags, consoleFlags...) app.Flags = append(app.Flags, debug.Flags...) + app.Flags = append(app.Flags, metricsFlags...) flags.AutoEnvVars(app.Flags, "XDC") app.Before = func(ctx *cli.Context) error { @@ -204,6 +214,9 @@ func init() { } flags.CheckEnvVars(ctx, app.Flags, "XDC") + // Start system runtime metrics collection + go metrics.CollectProcessMetrics(3 * time.Second) + utils.SetupNetwork(ctx) return nil } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 0efc867a273e..39a06e0e3b09 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -28,6 +28,7 @@ import ( godebug "runtime/debug" "strconv" "strings" + "time" "github.com/XinFinOrg/XDPoSChain/XDCx" "github.com/XinFinOrg/XDPoSChain/accounts" @@ -52,6 +53,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/metrics" "github.com/XinFinOrg/XDPoSChain/metrics/exp" + "github.com/XinFinOrg/XDPoSChain/metrics/influxdb" "github.com/XinFinOrg/XDPoSChain/node" "github.com/XinFinOrg/XDPoSChain/p2p" "github.com/XinFinOrg/XDPoSChain/p2p/discover" @@ -661,6 +663,45 @@ var ( Value: metrics.DefaultConfig.Port, Category: flags.MetricsCategory, } + MetricsEnableInfluxDBFlag = &cli.BoolFlag{ + Name: "metrics-influxdb", + Usage: "Enable metrics export/push to an external InfluxDB database", + Category: flags.MetricsCategory, + } + MetricsInfluxDBEndpointFlag = &cli.StringFlag{ + Name: "metrics-influxdb.endpoint", + Usage: "InfluxDB API endpoint to report metrics to", + Value: metrics.DefaultConfig.InfluxDBEndpoint, + Category: flags.MetricsCategory, + } + MetricsInfluxDBDatabaseFlag = &cli.StringFlag{ + Name: "metrics-influxdb.database", + Usage: "InfluxDB database name to push reported metrics to", + Value: metrics.DefaultConfig.InfluxDBDatabase, + Category: flags.MetricsCategory, + } + MetricsInfluxDBUsernameFlag = &cli.StringFlag{ + Name: "metrics-influxdb.username", + Usage: "Username to authorize access to the database", + Value: metrics.DefaultConfig.InfluxDBUsername, + Category: flags.MetricsCategory, + } + MetricsInfluxDBPasswordFlag = &cli.StringFlag{ + Name: "metrics-influxdb.password", + Usage: "Password to authorize access to the database", + Value: metrics.DefaultConfig.InfluxDBPassword, + Category: flags.MetricsCategory, + } + // The `host` tag is part of every measurement sent to InfluxDB. Queries on tags are faster in InfluxDB. + // It is used so that we can group all nodes and average a measurement across all of them, but also so + // that we can select a specific node and inspect its measurements. + // https://docs.influxdata.com/influxdb/v1.4/concepts/key_concepts/#tag-key + MetricsInfluxDBHostTagFlag = &cli.StringFlag{ + Name: "metrics-influxdb.host.tag", + Usage: "InfluxDB `host` tag attached to all measurements", + Value: metrics.DefaultConfig.InfluxDBTags, + Category: flags.MetricsCategory, + } // MISC settings RollbackFlag = &cli.StringFlag{ @@ -1468,6 +1509,35 @@ func SetupNetwork(ctx *cli.Context) { params.TargetGasLimit = ctx.Uint64(MinerGasLimitFlag.Name) } +func SetupMetrics(ctx *cli.Context) { + if metrics.Enabled { + log.Info("Enabling metrics collection") + var ( + enableExport = ctx.Bool(MetricsEnableInfluxDBFlag.Name) + endpoint = ctx.String(MetricsInfluxDBEndpointFlag.Name) + database = ctx.String(MetricsInfluxDBDatabaseFlag.Name) + username = ctx.String(MetricsInfluxDBUsernameFlag.Name) + password = ctx.String(MetricsInfluxDBPasswordFlag.Name) + hosttag = ctx.String(MetricsInfluxDBHostTagFlag.Name) + ) + + if enableExport { + log.Info("Enabling metrics export to InfluxDB") + go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "xdc.", map[string]string{ + "host": hosttag, + }) + } + + if ctx.IsSet(MetricsHTTPFlag.Name) { + address := fmt.Sprintf("%s:%d", ctx.String(MetricsHTTPFlag.Name), ctx.Int(MetricsPortFlag.Name)) + log.Info("Enabling stand-alone metrics HTTP endpoint", "address", address) + exp.Setup(address) + } else if ctx.IsSet(MetricsPortFlag.Name) { + log.Warn(fmt.Sprintf("--%s specified without --%s, metrics server will not start.", MetricsPortFlag.Name, MetricsHTTPFlag.Name)) + } + } +} + // MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails. func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database { var ( @@ -1595,15 +1665,3 @@ func RegisterFilterAPI(stack *node.Node, backend ethapi.Backend, ethcfg *ethconf }}) return filterSystem } - -func SetupMetrics(ctx *cli.Context) { - if metrics.Enabled { - log.Info("Enabling metrics collection") - - if ctx.IsSet(MetricsHTTPFlag.Name) { - address := fmt.Sprintf("%s:%d", ctx.String(MetricsHTTPFlag.Name), ctx.Int(MetricsPortFlag.Name)) - log.Info("Enabling stand-alone metrics HTTP endpoint", "address", address) - exp.Setup(address) - } - } -} diff --git a/metrics/config.go b/metrics/config.go index 169c683a97b5..4570628c243b 100644 --- a/metrics/config.go +++ b/metrics/config.go @@ -22,6 +22,12 @@ type Config struct { EnabledExpensive bool `toml:",omitempty"` HTTP string `toml:",omitempty"` Port int `toml:",omitempty"` + EnableInfluxDB bool `toml:",omitempty"` + InfluxDBEndpoint string `toml:",omitempty"` + InfluxDBDatabase string `toml:",omitempty"` + InfluxDBUsername string `toml:",omitempty"` + InfluxDBPassword string `toml:",omitempty"` + InfluxDBTags string `toml:",omitempty"` } // DefaultConfig is the default config for metrics used in go-ethereum. @@ -30,4 +36,10 @@ var DefaultConfig = Config{ EnabledExpensive: false, HTTP: "127.0.0.1", Port: 6060, + EnableInfluxDB: false, + InfluxDBEndpoint: "http://localhost:8086", + InfluxDBDatabase: "xdc", + InfluxDBUsername: "test", + InfluxDBPassword: "test", + InfluxDBTags: "localhost", } diff --git a/metrics/influxdb/influxdb.go b/metrics/influxdb/influxdb.go index 5c00c1a4c320..0567705a9b99 100644 --- a/metrics/influxdb/influxdb.go +++ b/metrics/influxdb/influxdb.go @@ -2,10 +2,10 @@ package influxdb import ( "fmt" - "log" uurl "net/url" "time" + "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/metrics" "github.com/influxdata/influxdb/client" ) @@ -35,7 +35,7 @@ func InfluxDB(r metrics.Registry, d time.Duration, url, database, username, pass func InfluxDBWithTags(r metrics.Registry, d time.Duration, url, database, username, password, namespace string, tags map[string]string) { u, err := uurl.Parse(url) if err != nil { - log.Printf("unable to parse InfluxDB url %s. err=%v", url, err) + log.Warn("unable to parse InfluxDB url %s. err=%v", url, err) return } @@ -51,7 +51,7 @@ func InfluxDBWithTags(r metrics.Registry, d time.Duration, url, database, userna cache: make(map[string]int64), } if err := rep.makeClient(); err != nil { - log.Printf("unable to make InfluxDB client. err=%v", err) + log.Warn("unable to make InfluxDB client. err=%v", err) return } @@ -79,15 +79,15 @@ func (r *reporter) run() { select { case <-intervalTicker.C: if err := r.send(); err != nil { - log.Printf("unable to send to InfluxDB. err=%v", err) + log.Warn("unable to send to InfluxDB. err=%v", err) } case <-pingTicker.C: _, _, err := r.client.Ping() if err != nil { - log.Printf("got error while sending a ping to InfluxDB, trying to recreate client. err=%v", err) + log.Warn("got error while sending a ping to InfluxDB, trying to recreate client. err=%v", err) if err = r.makeClient(); err != nil { - log.Printf("unable to make InfluxDB client. err=%v", err) + log.Warn("unable to make InfluxDB client. err=%v", err) } } } From 61f5a888c37c24e812db298cbfde1ffb244466ec Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:11 +0800 Subject: [PATCH 309/479] metrics: add force option for metric system (#17667) --- metrics/ewma.go | 3 --- metrics/meter.go | 43 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/metrics/ewma.go b/metrics/ewma.go index 3aecd4fa35a1..57c949e7d421 100644 --- a/metrics/ewma.go +++ b/metrics/ewma.go @@ -17,9 +17,6 @@ type EWMA interface { // NewEWMA constructs a new EWMA with the given alpha. func NewEWMA(alpha float64) EWMA { - if !Enabled { - return NilEWMA{} - } return &StandardEWMA{alpha: alpha} } diff --git a/metrics/meter.go b/metrics/meter.go index 82b2141a624b..58d170fae027 100644 --- a/metrics/meter.go +++ b/metrics/meter.go @@ -29,6 +29,17 @@ func GetOrRegisterMeter(name string, r Registry) Meter { return r.GetOrRegister(name, NewMeter).(Meter) } +// GetOrRegisterMeterForced returns an existing Meter or constructs and registers a +// new StandardMeter no matter the global switch is enabled or not. +// Be sure to unregister the meter from the registry once it is of no use to +// allow for garbage collection. +func GetOrRegisterMeterForced(name string, r Registry) Meter { + if nil == r { + r = DefaultRegistry + } + return r.GetOrRegister(name, NewMeterForced).(Meter) +} + // NewMeter constructs a new StandardMeter and launches a goroutine. // Be sure to call Stop() once the meter is of no use to allow for garbage collection. func NewMeter() Meter { @@ -46,8 +57,23 @@ func NewMeter() Meter { return m } -// NewMeter constructs and registers a new StandardMeter and launches a -// goroutine. +// NewMeterForced constructs a new StandardMeter and launches a goroutine no matter +// the global switch is enabled or not. +// Be sure to call Stop() once the meter is of no use to allow for garbage collection. +func NewMeterForced() Meter { + m := newStandardMeter() + arbiter.Lock() + defer arbiter.Unlock() + arbiter.meters[m] = struct{}{} + if !arbiter.started { + arbiter.started = true + go arbiter.tick() + } + return m +} + +// NewRegisteredMeter constructs and registers a new StandardMeter +// and launches a goroutine. // Be sure to unregister the meter from the registry once it is of no use to // allow for garbage collection. func NewRegisteredMeter(name string, r Registry) Meter { @@ -59,6 +85,19 @@ func NewRegisteredMeter(name string, r Registry) Meter { return c } +// NewRegisteredMeterForced constructs and registers a new StandardMeter +// and launches a goroutine no matter the global switch is enabled or not. +// Be sure to unregister the meter from the registry once it is of no use to +// allow for garbage collection. +func NewRegisteredMeterForced(name string, r Registry) Meter { + c := NewMeterForced() + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + // MeterSnapshot is a read-only copy of another Meter. type MeterSnapshot struct { count int64 From 01a52982732bf7bef2770859b63c669d5b6127e2 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:11 +0800 Subject: [PATCH 310/479] metrics: added NewCounterForced (#17919) --- metrics/counter.go | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/metrics/counter.go b/metrics/counter.go index c7f2b4bd3aa3..2f78c90d5c64 100644 --- a/metrics/counter.go +++ b/metrics/counter.go @@ -1,6 +1,8 @@ package metrics -import "sync/atomic" +import ( + "sync/atomic" +) // Counters hold an int64 value that can be incremented and decremented. type Counter interface { @@ -20,6 +22,17 @@ func GetOrRegisterCounter(name string, r Registry) Counter { return r.GetOrRegister(name, NewCounter).(Counter) } +// GetOrRegisterCounterForced returns an existing Counter or constructs and registers a +// new Counter no matter the global switch is enabled or not. +// Be sure to unregister the counter from the registry once it is of no use to +// allow for garbage collection. +func GetOrRegisterCounterForced(name string, r Registry) Counter { + if nil == r { + r = DefaultRegistry + } + return r.GetOrRegister(name, NewCounterForced).(Counter) +} + // NewCounter constructs a new StandardCounter. func NewCounter() Counter { if !Enabled { @@ -28,6 +41,12 @@ func NewCounter() Counter { return &StandardCounter{0} } +// NewCounterForced constructs a new StandardCounter and returns it no matter if +// the global switch is enabled or not. +func NewCounterForced() Counter { + return &StandardCounter{0} +} + // NewRegisteredCounter constructs and registers a new StandardCounter. func NewRegisteredCounter(name string, r Registry) Counter { c := NewCounter() @@ -38,6 +57,19 @@ func NewRegisteredCounter(name string, r Registry) Counter { return c } +// NewRegisteredCounterForced constructs and registers a new StandardCounter +// and launches a goroutine no matter the global switch is enabled or not. +// Be sure to unregister the counter from the registry once it is of no use to +// allow for garbage collection. +func NewRegisteredCounterForced(name string, r Registry) Counter { + c := NewCounterForced() + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + // CounterSnapshot is a read-only copy of another Counter. type CounterSnapshot int64 From 4202f2389775c38c3b9c64409a1e0ab9727800f3 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:11 +0800 Subject: [PATCH 311/479] core: more detailed metrics for block processing (#18119) --- core/blockchain.go | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 2f731331decc..ebd17ccbc56f 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -56,13 +56,18 @@ import ( ) var ( - blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil) - CheckpointCh = make(chan int) - ErrNoGenesis = errors.New("Genesis not found in chain") + blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil) + blockValidationTimer = metrics.NewRegisteredTimer("chain/validation", nil) + blockExecutionTimer = metrics.NewRegisteredTimer("chain/execution", nil) + blockWriteTimer = metrics.NewRegisteredTimer("chain/write", nil) blockReorgMeter = metrics.NewRegisteredMeter("chain/reorg/executes", nil) blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil) blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil) + + CheckpointCh = make(chan int) + + ErrNoGenesis = errors.New("Genesis not found in chain") ) const ( @@ -1658,7 +1663,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, [] } feeCapacity := state.GetTRC21FeeCapacityFromStateWithCache(parent.Root(), statedb) // Process block using the parent state as reference point. + t0 := time.Now() receipts, logs, usedGas, err := bc.processor.Process(block, statedb, tradingState, bc.vmConfig, feeCapacity) + t1 := time.Now() if err != nil { bc.reportBlock(block, receipts, err) return i, events, coalescedLogs, err @@ -1669,19 +1676,27 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, [] bc.reportBlock(block, receipts, err) return i, events, coalescedLogs, err } + t2 := time.Now() proctime := time.Since(bstart) + // Write the block to the chain and get the status. status, err := bc.WriteBlockWithState(block, receipts, statedb, tradingState, lendingState) + t3 := time.Now() if err != nil { return i, events, coalescedLogs, err } + + blockInsertTimer.UpdateSince(bstart) + blockExecutionTimer.Update(t1.Sub(t0)) + blockValidationTimer.Update(t2.Sub(t1)) + blockWriteTimer.Update(t3.Sub(t2)) + switch status { case CanonStatTy: log.Debug("Inserted new block from downloader", "number", block.Number(), "hash", block.Hash(), "uncles", len(block.Uncles()), "txs", len(block.Transactions()), "gas", block.GasUsed(), "elapsed", common.PrettyDuration(time.Since(bstart))) coalescedLogs = append(coalescedLogs, logs...) - blockInsertTimer.UpdateSince(bstart) events = append(events, ChainEvent{block, block.Hash(), logs}) lastCanon = block @@ -1695,8 +1710,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, [] case SideStatTy: log.Debug("Inserted forked block from downloader", "number", block.Number(), "hash", block.Hash(), "diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(bstart)), "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles())) - - blockInsertTimer.UpdateSince(bstart) events = append(events, ChainSideEvent{block}) bc.UpdateBlocksHashCache(block) } @@ -2015,7 +2028,6 @@ func (bc *BlockChain) insertBlock(block *types.Block) ([]interface{}, []*types.L "txs", len(block.Transactions()), "gas", block.GasUsed(), "elapsed", common.PrettyDuration(time.Since(block.ReceivedAt))) coalescedLogs = append(coalescedLogs, result.logs...) events = append(events, ChainEvent{block, block.Hash(), result.logs}) - // Only count canonical blocks for GC processing time bc.gcproc += result.proctime bc.UpdateBlocksHashCache(block) @@ -2026,10 +2038,8 @@ func (bc *BlockChain) insertBlock(block *types.Block) ([]interface{}, []*types.L case SideStatTy: log.Debug("Inserted forked block from fetcher", "number", block.Number(), "hash", block.Hash(), "diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(block.ReceivedAt)), "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles())) - blockInsertTimer.Update(result.proctime) events = append(events, ChainSideEvent{block}) - bc.UpdateBlocksHashCache(block) } stats.processed++ From 3e4583e7c44f85181f1ec870c7b267e2068dc67b Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:11 +0800 Subject: [PATCH 312/479] cmd/utils: allow for multiple influxdb flags (#18520) --- cmd/XDC/main.go | 2 +- cmd/utils/flags.go | 37 ++++++++++++++++++------- cmd/utils/flags_test.go | 60 +++++++++++++++++++++++++++++++++++++++++ metrics/config.go | 2 +- 4 files changed, 89 insertions(+), 12 deletions(-) diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go index f5bdd24709d2..37b5dde48ab8 100644 --- a/cmd/XDC/main.go +++ b/cmd/XDC/main.go @@ -170,7 +170,7 @@ var ( utils.MetricsInfluxDBDatabaseFlag, utils.MetricsInfluxDBUsernameFlag, utils.MetricsInfluxDBPasswordFlag, - utils.MetricsInfluxDBHostTagFlag, + utils.MetricsInfluxDBTagsFlag, } ) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 39a06e0e3b09..04ddbca93622 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -692,13 +692,13 @@ var ( Value: metrics.DefaultConfig.InfluxDBPassword, Category: flags.MetricsCategory, } - // The `host` tag is part of every measurement sent to InfluxDB. Queries on tags are faster in InfluxDB. - // It is used so that we can group all nodes and average a measurement across all of them, but also so - // that we can select a specific node and inspect its measurements. + // Tags are part of every measurement sent to InfluxDB. Queries on tags are faster in InfluxDB. + // For example `host` tag could be used so that we can group all nodes and average a measurement + // across all of them, but also so that we can select a specific node and inspect its measurements. // https://docs.influxdata.com/influxdb/v1.4/concepts/key_concepts/#tag-key - MetricsInfluxDBHostTagFlag = &cli.StringFlag{ - Name: "metrics-influxdb.host.tag", - Usage: "InfluxDB `host` tag attached to all measurements", + MetricsInfluxDBTagsFlag = &cli.StringFlag{ + Name: "metrics-influxdb.tags", + Usage: "Comma-separated InfluxDB tags (key/values) attached to all measurements", Value: metrics.DefaultConfig.InfluxDBTags, Category: flags.MetricsCategory, } @@ -1518,14 +1518,14 @@ func SetupMetrics(ctx *cli.Context) { database = ctx.String(MetricsInfluxDBDatabaseFlag.Name) username = ctx.String(MetricsInfluxDBUsernameFlag.Name) password = ctx.String(MetricsInfluxDBPasswordFlag.Name) - hosttag = ctx.String(MetricsInfluxDBHostTagFlag.Name) ) if enableExport { log.Info("Enabling metrics export to InfluxDB") - go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "xdc.", map[string]string{ - "host": hosttag, - }) + + tagsMap := SplitTagsFlag(ctx.String(MetricsInfluxDBTagsFlag.Name)) + + go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "xdc.", tagsMap) } if ctx.IsSet(MetricsHTTPFlag.Name) { @@ -1538,6 +1538,23 @@ func SetupMetrics(ctx *cli.Context) { } } +func SplitTagsFlag(tagsFlag string) map[string]string { + tags := strings.Split(tagsFlag, ",") + tagsMap := map[string]string{} + + for _, t := range tags { + if t != "" { + kv := strings.Split(t, "=") + + if len(kv) == 2 { + tagsMap[kv[0]] = kv[1] + } + } + } + + return tagsMap +} + // MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails. func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database { var ( diff --git a/cmd/utils/flags_test.go b/cmd/utils/flags_test.go index adbf25b45bf6..728cbb707014 100644 --- a/cmd/utils/flags_test.go +++ b/cmd/utils/flags_test.go @@ -1,3 +1,20 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// Package utils contains internal helper functions for go-ethereum commands. package utils import ( @@ -8,6 +25,49 @@ import ( "testing" ) +func Test_SplitTagsFlag(t *testing.T) { + t.Parallel() + tests := []struct { + name string + args string + want map[string]string + }{ + { + "2 tags case", + "host=localhost,bzzkey=123", + map[string]string{ + "host": "localhost", + "bzzkey": "123", + }, + }, + { + "1 tag case", + "host=localhost123", + map[string]string{ + "host": "localhost123", + }, + }, + { + "empty case", + "", + map[string]string{}, + }, + { + "garbage", + "smth=smthelse=123", + map[string]string{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + if got := SplitTagsFlag(tt.args); !reflect.DeepEqual(got, tt.want) { + t.Errorf("splitTagsFlag() = %v, want %v", got, tt.want) + } + }) + } +} + func TestWalkMatch(t *testing.T) { type args struct { root string diff --git a/metrics/config.go b/metrics/config.go index 4570628c243b..1150a007aaf7 100644 --- a/metrics/config.go +++ b/metrics/config.go @@ -41,5 +41,5 @@ var DefaultConfig = Config{ InfluxDBDatabase: "xdc", InfluxDBUsername: "test", InfluxDBPassword: "test", - InfluxDBTags: "localhost", + InfluxDBTags: "host=localhost", } From 7608787b3c1c40ae6570cb7ae9ce4e83bc826ce9 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:11 +0800 Subject: [PATCH 313/479] metrics: remove redundant type specifiers (#19090) --- metrics/metrics.go | 2 +- metrics/opentsdb.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/metrics/metrics.go b/metrics/metrics.go index 2d9d652a0201..ebfa602d18d9 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -19,7 +19,7 @@ import ( // // This global kill-switch helps quantify the observer effect and makes // for less cluttered pprof profiles. -var Enabled bool = false +var Enabled = false // MetricsEnabledFlag is the CLI flag name to use to enable metrics collections. const MetricsEnabledFlag = "metrics" diff --git a/metrics/opentsdb.go b/metrics/opentsdb.go index df7f152ed2eb..3fde55454ba9 100644 --- a/metrics/opentsdb.go +++ b/metrics/opentsdb.go @@ -10,7 +10,7 @@ import ( "time" ) -var shortHostName string = "" +var shortHostName = "" // OpenTSDBConfig provides a container with configuration parameters for // the OpenTSDB exporter From 1557746bcd94546a9b46fa7c56a97482f04e0311 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:11 +0800 Subject: [PATCH 314/479] metrics/influxdb: add a timeout to the InfluxDB HTTP client (#19250) --- metrics/influxdb/influxdb.go | 1 + 1 file changed, 1 insertion(+) diff --git a/metrics/influxdb/influxdb.go b/metrics/influxdb/influxdb.go index 0567705a9b99..ac5e0fce3c4d 100644 --- a/metrics/influxdb/influxdb.go +++ b/metrics/influxdb/influxdb.go @@ -63,6 +63,7 @@ func (r *reporter) makeClient() (err error) { URL: r.url, Username: r.username, Password: r.password, + Timeout: 10 * time.Second, }) return From db9487f1e8f924d118cf54040e4cff370fd60b3b Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:11 +0800 Subject: [PATCH 315/479] core: split out detailed trie access metrics from insertion time (#19316) --- cmd/XDC/main.go | 1 + cmd/utils/flags.go | 6 +++- cmd/utils/flags_legacy.go | 2 +- core/blockchain.go | 26 ++++++++++++++++- core/state/state_object.go | 19 ++++++++++++ core/state/statedb.go | 47 ++++++++++++++++++++++++++---- metrics/metrics.go | 59 +++++++++++++++++++++++++++----------- 7 files changed, 135 insertions(+), 25 deletions(-) diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go index 37b5dde48ab8..fb01ba2f5f77 100644 --- a/cmd/XDC/main.go +++ b/cmd/XDC/main.go @@ -163,6 +163,7 @@ var ( metricsFlags = []cli.Flag{ utils.MetricsEnabledFlag, + utils.MetricsEnabledExpensiveFlag, utils.MetricsHTTPFlag, utils.MetricsPortFlag, utils.MetricsEnableInfluxDBFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 04ddbca93622..b42914b2a866 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -644,7 +644,11 @@ var ( Usage: "Enable metrics collection and reporting", Category: flags.MetricsCategory, } - + MetricsEnabledExpensiveFlag = &cli.BoolFlag{ + Name: "metrics-expensive", + Usage: "Enable expensive metrics collection and reporting", + Category: flags.MetricsCategory, + } // MetricsHTTPFlag defines the endpoint for a stand-alone metrics HTTP endpoint. // Since the pprof service enables sensitive/vulnerable behavior, this allows a user // to enable a public-OK metrics endpoint without having to worry about ALSO exposing diff --git a/cmd/utils/flags_legacy.go b/cmd/utils/flags_legacy.go index b47a74595613..3bd314292d52 100644 --- a/cmd/utils/flags_legacy.go +++ b/cmd/utils/flags_legacy.go @@ -50,7 +50,7 @@ var ( Usage: "Enable light client mode", Category: flags.DeprecatedCategory, } - // (Deprecated May 2020, shown in aliased flags section) + // Deprecated May 2020, shown in aliased flags section NoUSBFlag = &cli.BoolFlag{ Name: "nousb", Usage: "Disables monitoring for and managing USB hardware wallets (deprecated)", diff --git a/core/blockchain.go b/core/blockchain.go index ebd17ccbc56f..b90e461cc6ae 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -56,6 +56,16 @@ import ( ) var ( + accountReadTimer = metrics.NewRegisteredTimer("chain/account/reads", nil) + accountHashTimer = metrics.NewRegisteredTimer("chain/account/hashes", nil) + accountUpdateTimer = metrics.NewRegisteredTimer("chain/account/updates", nil) + accountCommitTimer = metrics.NewRegisteredTimer("chain/account/commits", nil) + + storageReadTimer = metrics.NewRegisteredTimer("chain/storage/reads", nil) + storageHashTimer = metrics.NewRegisteredTimer("chain/storage/hashes", nil) + storageUpdateTimer = metrics.NewRegisteredTimer("chain/storage/updates", nil) + storageCommitTimer = metrics.NewRegisteredTimer("chain/storage/commits", nil) + blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil) blockValidationTimer = metrics.NewRegisteredTimer("chain/validation", nil) blockExecutionTimer = metrics.NewRegisteredTimer("chain/execution", nil) @@ -1686,8 +1696,22 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, [] return i, events, coalescedLogs, err } + // Update the metrics subsystem with all the measurements + accountReadTimer.Update(statedb.AccountReads) + accountHashTimer.Update(statedb.AccountHashes) + accountUpdateTimer.Update(statedb.AccountUpdates) + accountCommitTimer.Update(statedb.AccountCommits) + + storageReadTimer.Update(statedb.StorageReads) + storageHashTimer.Update(statedb.StorageHashes) + storageUpdateTimer.Update(statedb.StorageUpdates) + storageCommitTimer.Update(statedb.StorageCommits) + + trieAccess := statedb.AccountReads + statedb.AccountHashes + statedb.AccountUpdates + statedb.AccountCommits + trieAccess += statedb.StorageReads + statedb.StorageHashes + statedb.StorageUpdates + statedb.StorageCommits + blockInsertTimer.UpdateSince(bstart) - blockExecutionTimer.Update(t1.Sub(t0)) + blockExecutionTimer.Update(t1.Sub(t0) - trieAccess) blockValidationTimer.Update(t2.Sub(t1)) blockWriteTimer.Update(t3.Sub(t2)) diff --git a/core/state/state_object.go b/core/state/state_object.go index ca695efe3b85..10fcfc8b3cb2 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -21,10 +21,12 @@ import ( "fmt" "io" "math/big" + "time" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/metrics" "github.com/XinFinOrg/XDPoSChain/rlp" ) @@ -173,6 +175,10 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has if s.fakeStorage != nil { return s.fakeStorage[key] } + // Track the amount of time wasted on reading the storge trie + if metrics.EnabledExpensive { + defer func(start time.Time) { s.db.StorageReads += time.Since(start) }(time.Now()) + } value := common.Hash{} // Load from DB in case it is missing. enc, err := s.getTrie(db).TryGet(key[:]) @@ -269,6 +275,10 @@ func (s *stateObject) setState(key, value common.Hash) { // updateTrie writes cached storage modifications into the object's storage trie. func (s *stateObject) updateTrie(db Database) Trie { + // Track the amount of time wasted on updating the storge trie + if metrics.EnabledExpensive { + defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now()) + } tr := s.getTrie(db) for key, value := range s.dirtyStorage { delete(s.dirtyStorage, key) @@ -286,6 +296,11 @@ func (s *stateObject) updateTrie(db Database) Trie { // UpdateRoot sets the trie root to the current root hash of func (s *stateObject) updateRoot(db Database) { s.updateTrie(db) + + // Track the amount of time wasted on hashing the storge trie + if metrics.EnabledExpensive { + defer func(start time.Time) { s.db.StorageHashes += time.Since(start) }(time.Now()) + } s.data.Root = s.trie.Hash() } @@ -296,6 +311,10 @@ func (s *stateObject) CommitTrie(db Database) error { if s.dbErr != nil { return s.dbErr } + // Track the amount of time wasted on committing the storge trie + if metrics.EnabledExpensive { + defer func(start time.Time) { s.db.StorageCommits += time.Since(start) }(time.Now()) + } root, err := s.trie.Commit(nil) if err == nil { s.data.Root = root diff --git a/core/state/statedb.go b/core/state/statedb.go index 7782643f8a0e..0e0dc9e7e5b5 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -22,11 +22,13 @@ import ( "math/big" "sort" "sync" + "time" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/log" + "github.com/XinFinOrg/XDPoSChain/metrics" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rlp" "github.com/XinFinOrg/XDPoSChain/trie" @@ -80,6 +82,16 @@ type StateDB struct { nextRevisionId int lock sync.Mutex + + // Measurements gathered during execution for debugging purposes + AccountReads time.Duration + AccountHashes time.Duration + AccountUpdates time.Duration + AccountCommits time.Duration + StorageReads time.Duration + StorageHashes time.Duration + StorageUpdates time.Duration + StorageCommits time.Duration } type AccountInfo struct { @@ -446,7 +458,13 @@ func (s *StateDB) GetTransientState(addr common.Address, key common.Hash) common // updateStateObject writes the given object to the trie. func (s *StateDB) updateStateObject(stateObject *stateObject) { + // Track the amount of time wasted on updating the account from the trie + if metrics.EnabledExpensive { + defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now()) + } + // Encode the account and update the account trie addr := stateObject.Address() + data, err := rlp.EncodeToBytes(stateObject) if err != nil { panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err)) @@ -456,7 +474,13 @@ func (s *StateDB) updateStateObject(stateObject *stateObject) { // deleteStateObject removes the given object from the state trie. func (s *StateDB) deleteStateObject(stateObject *stateObject) { + // Track the amount of time wasted on deleting the account from the trie + if metrics.EnabledExpensive { + defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now()) + } + // Delete the account from the trie stateObject.deleted = true + addr := stateObject.Address() s.setError(s.trie.TryDelete(addr[:])) } @@ -471,15 +495,18 @@ func (s *StateDB) DeleteAddress(addr common.Address) { // Retrieve a state object given my the address. Returns nil if not found. func (s *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) { - // Prefer 'live' objects. + // Prefer live objects if any is available if obj := s.stateObjects[addr]; obj != nil { if obj.deleted { return nil } return obj } - - // Load the object from the database. + // Track the amount of time wasted on loading the object from the database + if metrics.EnabledExpensive { + defer func(start time.Time) { s.AccountReads += time.Since(start) }(time.Now()) + } + // Load the object from the database enc, err := s.trie.TryGet(addr[:]) if len(enc) == 0 { s.setError(err) @@ -490,7 +517,7 @@ func (s *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) log.Error("Failed to decode state object", "addr", addr, "err", err) return nil } - // Insert into the live set. + // Insert into the live set obj := newObject(s, addr, data, s.MarkStateObjectDirty) s.setStateObject(obj) return obj @@ -672,6 +699,11 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { // goes into transaction receipts. func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { s.Finalise(deleteEmptyObjects) + + // Track the amount of time wasted on hashing the account trie + if metrics.EnabledExpensive { + defer func(start time.Time) { s.AccountHashes += time.Since(start) }(time.Now()) + } return s.trie.Hash() } @@ -714,7 +746,7 @@ func (s *StateDB) clearJournalAndRefund() { func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) { defer s.clearJournalAndRefund() - // Commit objects to the trie. + // Commit objects to the trie, measuring the elapsed time for addr, stateObject := range s.stateObjects { _, isDirty := s.stateObjectsDirty[addr] switch { @@ -737,7 +769,10 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) } delete(s.stateObjectsDirty, addr) } - // Write trie changes. + // Write the account trie changes, measuing the amount of wasted time + if metrics.EnabledExpensive { + defer func(start time.Time) { s.AccountCommits += time.Since(start) }(time.Now()) + } root, err = s.trie.Commit(func(leaf []byte, parent common.Hash) error { var account Account if err := rlp.DecodeBytes(leaf, &account); err != nil { diff --git a/metrics/metrics.go b/metrics/metrics.go index ebfa602d18d9..53c8e81fbed7 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -15,23 +15,41 @@ import ( ) // Enabled is checked by the constructor functions for all of the -// standard metrics. If it is true, the metric returned is a stub. +// standard metrics. If it is true, the metric returned is a stub. // // This global kill-switch helps quantify the observer effect and makes // for less cluttered pprof profiles. var Enabled = false -// MetricsEnabledFlag is the CLI flag name to use to enable metrics collections. -const MetricsEnabledFlag = "metrics" +// EnabledExpensive is a soft-flag meant for external packages to check if costly +// metrics gathering is allowed or not. The goal is to separate standard metrics +// for health monitoring and debug metrics that might impact runtime performance. +var EnabledExpensive = false + +// enablerFlags is the CLI flag names to use to enable metrics collections. +var enablerFlags = []string{"metrics"} + +// expensiveEnablerFlags is the CLI flag names to use to enable metrics collections. +var expensiveEnablerFlags = []string{"metrics-expensive"} // Init enables or disables the metrics system. Since we need this to run before // any other code gets to create meters and timers, we'll actually do an ugly hack // and peek into the command line args for the metrics flag. func init() { for _, arg := range os.Args { - if flag := strings.TrimLeft(arg, "-"); flag == MetricsEnabledFlag { - log.Info("Enabling metrics collection") - Enabled = true + flag := strings.TrimLeft(arg, "-") + + for _, enabler := range enablerFlags { + if !Enabled && flag == enabler { + log.Info("Enabling metrics collection") + Enabled = true + } + } + for _, enabler := range expensiveEnablerFlags { + if !Enabled && flag == enabler { + log.Info("Enabling expensive metrics collection") + EnabledExpensive = true + } } } } @@ -57,27 +75,36 @@ func CollectProcessMetrics(refresh time.Duration) { memPauses := GetOrRegisterMeter("system/memory/pauses", DefaultRegistry) var diskReads, diskReadBytes, diskWrites, diskWriteBytes Meter + var diskReadBytesCounter, diskWriteBytesCounter Counter if err := ReadDiskStats(diskstats[0]); err == nil { diskReads = GetOrRegisterMeter("system/disk/readcount", DefaultRegistry) diskReadBytes = GetOrRegisterMeter("system/disk/readdata", DefaultRegistry) + diskReadBytesCounter = GetOrRegisterCounter("system/disk/readbytes", DefaultRegistry) diskWrites = GetOrRegisterMeter("system/disk/writecount", DefaultRegistry) diskWriteBytes = GetOrRegisterMeter("system/disk/writedata", DefaultRegistry) + diskWriteBytesCounter = GetOrRegisterCounter("system/disk/writebytes", DefaultRegistry) } else { log.Debug("Failed to read disk metrics", "err", err) } // Iterate loading the different stats and updating the meters for i := 1; ; i++ { - runtime.ReadMemStats(memstats[i%2]) - memAllocs.Mark(int64(memstats[i%2].Mallocs - memstats[(i-1)%2].Mallocs)) - memFrees.Mark(int64(memstats[i%2].Frees - memstats[(i-1)%2].Frees)) - memInuse.Mark(int64(memstats[i%2].Alloc - memstats[(i-1)%2].Alloc)) - memPauses.Mark(int64(memstats[i%2].PauseTotalNs - memstats[(i-1)%2].PauseTotalNs)) + location1 := i % 2 + location2 := (i - 1) % 2 + + runtime.ReadMemStats(memstats[location1]) + memAllocs.Mark(int64(memstats[location1].Mallocs - memstats[location2].Mallocs)) + memFrees.Mark(int64(memstats[location1].Frees - memstats[location2].Frees)) + memInuse.Mark(int64(memstats[location1].Alloc - memstats[location2].Alloc)) + memPauses.Mark(int64(memstats[location1].PauseTotalNs - memstats[location2].PauseTotalNs)) + + if ReadDiskStats(diskstats[location1]) == nil { + diskReads.Mark(diskstats[location1].ReadCount - diskstats[location2].ReadCount) + diskReadBytes.Mark(diskstats[location1].ReadBytes - diskstats[location2].ReadBytes) + diskWrites.Mark(diskstats[location1].WriteCount - diskstats[location2].WriteCount) + diskWriteBytes.Mark(diskstats[location1].WriteBytes - diskstats[location2].WriteBytes) - if ReadDiskStats(diskstats[i%2]) == nil { - diskReads.Mark(diskstats[i%2].ReadCount - diskstats[(i-1)%2].ReadCount) - diskReadBytes.Mark(diskstats[i%2].ReadBytes - diskstats[(i-1)%2].ReadBytes) - diskWrites.Mark(diskstats[i%2].WriteCount - diskstats[(i-1)%2].WriteCount) - diskWriteBytes.Mark(diskstats[i%2].WriteBytes - diskstats[(i-1)%2].WriteBytes) + diskReadBytesCounter.Inc(diskstats[location1].ReadBytes - diskstats[location2].ReadBytes) + diskWriteBytesCounter.Inc(diskstats[location1].WriteBytes - diskstats[location2].WriteBytes) } time.Sleep(refresh) } From ed427a9426f3151ab33f50ffc0c6d5ec6ede5106 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:11 +0800 Subject: [PATCH 316/479] metrics: fix expensive metrics flag processing (#19327) --- metrics/metrics.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/metrics.go b/metrics/metrics.go index 53c8e81fbed7..bd23e4b24747 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -46,7 +46,7 @@ func init() { } } for _, enabler := range expensiveEnablerFlags { - if !Enabled && flag == enabler { + if !EnabledExpensive && flag == enabler { log.Info("Enabling expensive metrics collection") EnabledExpensive = true } From a577c719445a85cb93b57882d71b5c11e619e462 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:11 +0800 Subject: [PATCH 317/479] metrics/prometheus: added prometheus metrics (#17077) --- metrics/exp/exp.go | 2 + metrics/prometheus/collector.go | 115 +++++++++++++++++++++++++++++++ metrics/prometheus/prometheus.go | 68 ++++++++++++++++++ 3 files changed, 185 insertions(+) create mode 100644 metrics/prometheus/collector.go create mode 100644 metrics/prometheus/prometheus.go diff --git a/metrics/exp/exp.go b/metrics/exp/exp.go index 253b4e94903a..28435beceac5 100644 --- a/metrics/exp/exp.go +++ b/metrics/exp/exp.go @@ -10,6 +10,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/metrics" + "github.com/XinFinOrg/XDPoSChain/metrics/prometheus" ) type exp struct { @@ -43,6 +44,7 @@ func Exp(r metrics.Registry) { // http.HandleFunc("/debug/vars", e.expHandler) // haven't found an elegant way, so just use a different endpoint http.Handle("/debug/metrics", h) + http.Handle("/debug/metrics/prometheus", prometheus.Handler(r)) } // ExpHandler will return an expvar powered metrics handler. diff --git a/metrics/prometheus/collector.go b/metrics/prometheus/collector.go new file mode 100644 index 000000000000..d0e731e6c2dc --- /dev/null +++ b/metrics/prometheus/collector.go @@ -0,0 +1,115 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package prometheus + +import ( + "bytes" + "fmt" + "strconv" + "strings" + + "github.com/XinFinOrg/XDPoSChain/metrics" +) + +var ( + typeGaugeTpl = "# TYPE %s gauge\n" + typeCounterTpl = "# TYPE %s counter\n" + typeSummaryTpl = "# TYPE %s summary\n" + keyValueTpl = "%s %v\n\n" + keyQuantileTagValueTpl = "%s {quantile=\"%s\"} %v\n\n" +) + +// collector is a collection of byte buffers that aggregate Prometheus reports +// for different metric types. +type collector struct { + buff *bytes.Buffer +} + +// newCollector createa a new Prometheus metric aggregator. +func newCollector() *collector { + return &collector{ + buff: &bytes.Buffer{}, + } +} + +func (c *collector) addCounter(name string, m metrics.Counter) { + c.writeGaugeCounter(name, m.Count()) +} + +func (c *collector) addGauge(name string, m metrics.Gauge) { + c.writeGaugeCounter(name, m.Value()) +} + +func (c *collector) addGaugeFloat64(name string, m metrics.GaugeFloat64) { + c.writeGaugeCounter(name, m.Value()) +} + +func (c *collector) addHistogram(name string, m metrics.Histogram) { + pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999} + ps := m.Percentiles(pv) + c.writeSummaryCounter(name, m.Count()) + for i := range pv { + c.writeSummaryPercentile(name, strconv.FormatFloat(pv[i], 'f', -1, 64), ps[i]) + } +} + +func (c *collector) addMeter(name string, m metrics.Meter) { + c.writeGaugeCounter(name, m.Count()) +} + +func (c *collector) addTimer(name string, m metrics.Timer) { + pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999} + ps := m.Percentiles(pv) + c.writeSummaryCounter(name, m.Count()) + for i := range pv { + c.writeSummaryPercentile(name, strconv.FormatFloat(pv[i], 'f', -1, 64), ps[i]) + } +} + +func (c *collector) addResettingTimer(name string, m metrics.ResettingTimer) { + if len(m.Values()) <= 0 { + return + } + ps := m.Percentiles([]float64{50, 95, 99}) + val := m.Values() + c.writeSummaryCounter(name, len(val)) + c.writeSummaryPercentile(name, "0.50", ps[0]) + c.writeSummaryPercentile(name, "0.95", ps[1]) + c.writeSummaryPercentile(name, "0.99", ps[2]) +} + +func (c *collector) writeGaugeCounter(name string, value interface{}) { + name = mutateKey(name) + c.buff.WriteString(fmt.Sprintf(typeGaugeTpl, name)) + c.buff.WriteString(fmt.Sprintf(keyValueTpl, name, value)) +} + +func (c *collector) writeSummaryCounter(name string, value interface{}) { + name = mutateKey(name + "_count") + c.buff.WriteString(fmt.Sprintf(typeCounterTpl, name)) + c.buff.WriteString(fmt.Sprintf(keyValueTpl, name, value)) +} + +func (c *collector) writeSummaryPercentile(name, p string, value interface{}) { + name = mutateKey(name) + c.buff.WriteString(fmt.Sprintf(typeSummaryTpl, name)) + c.buff.WriteString(fmt.Sprintf(keyQuantileTagValueTpl, name, p, value)) +} + +func mutateKey(key string) string { + return strings.Replace(key, "/", "_", -1) +} diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go new file mode 100644 index 000000000000..d34c2206f951 --- /dev/null +++ b/metrics/prometheus/prometheus.go @@ -0,0 +1,68 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package prometheus exposes go-metrics into a Prometheus format. +package prometheus + +import ( + "fmt" + "net/http" + "sort" + + "github.com/XinFinOrg/XDPoSChain/log" + "github.com/XinFinOrg/XDPoSChain/metrics" +) + +// Handler returns an HTTP handler which dump metrics in Prometheus format. +func Handler(reg metrics.Registry) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Gather and pre-sort the metrics to avoid random listings + var names []string + reg.Each(func(name string, i interface{}) { + names = append(names, name) + }) + sort.Strings(names) + + // Aggregate all the metris into a Prometheus collector + c := newCollector() + + for _, name := range names { + i := reg.Get(name) + + switch m := i.(type) { + case metrics.Counter: + c.addCounter(name, m.Snapshot()) + case metrics.Gauge: + c.addGauge(name, m.Snapshot()) + case metrics.GaugeFloat64: + c.addGaugeFloat64(name, m.Snapshot()) + case metrics.Histogram: + c.addHistogram(name, m.Snapshot()) + case metrics.Meter: + c.addMeter(name, m.Snapshot()) + case metrics.Timer: + c.addTimer(name, m.Snapshot()) + case metrics.ResettingTimer: + c.addResettingTimer(name, m.Snapshot()) + default: + log.Warn("Unknown Prometheus metric type", "type", fmt.Sprintf("%T", i)) + } + } + w.Header().Add("Content-Type", "text/plain") + w.Header().Add("Content-Length", fmt.Sprint(c.buff.Len())) + w.Write(c.buff.Bytes()) + }) +} From 1eb2ed82932da77b3d9b8c21596e43cbb37692db Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:11 +0800 Subject: [PATCH 318/479] core, metrics, p2p: expose various counter metrics for grafana (#19692) --- core/blockchain.go | 23 +++++++++++++++++++++++ core/headerchain.go | 6 ++++++ go.mod | 1 + go.sum | 5 +++++ metrics/cpu.go | 36 ++++++++++++++++++++++++++++++++++++ metrics/cpu_syscall.go | 35 +++++++++++++++++++++++++++++++++++ metrics/cpu_windows.go | 23 +++++++++++++++++++++++ metrics/metrics.go | 23 +++++++++++++++++++---- p2p/server.go | 4 +--- 9 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 metrics/cpu.go create mode 100644 metrics/cpu_syscall.go create mode 100644 metrics/cpu_windows.go diff --git a/core/blockchain.go b/core/blockchain.go index b90e461cc6ae..d11a421978ca 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -56,6 +56,10 @@ import ( ) var ( + headBlockGauge = metrics.NewRegisteredGauge("chain/head/block", nil) + headHeaderGauge = metrics.NewRegisteredGauge("chain/head/header", nil) + headFastBlockGauge = metrics.NewRegisteredGauge("chain/head/receipt", nil) + accountReadTimer = metrics.NewRegisteredTimer("chain/account/reads", nil) accountHashTimer = metrics.NewRegisteredTimer("chain/account/hashes", nil) accountUpdateTimer = metrics.NewRegisteredTimer("chain/account/updates", nil) @@ -355,6 +359,7 @@ func (bc *BlockChain) loadLastState() error { } // Everything seems to be fine, set as the head block bc.currentBlock.Store(currentBlock) + headBlockGauge.Update(int64(currentBlock.NumberU64())) // Restore the last known head header currentHeader := currentBlock.Header() @@ -374,9 +379,12 @@ func (bc *BlockChain) loadLastState() error { // Restore the last known head fast block bc.currentFastBlock.Store(currentBlock) + headFastBlockGauge.Update(int64(currentBlock.NumberU64())) + if head := GetHeadFastBlockHash(bc.db); head != (common.Hash{}) { if block := bc.GetBlockByHash(head); block != nil { bc.currentFastBlock.Store(block) + headFastBlockGauge.Update(int64(block.NumberU64())) } } @@ -422,23 +430,28 @@ func (bc *BlockChain) SetHead(head uint64) error { // Rewind the block chain, ensuring we don't end up with a stateless head block if currentBlock := bc.CurrentBlock(); currentBlock != nil && currentHeader.Number.Uint64() < currentBlock.NumberU64() { bc.currentBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())) + headBlockGauge.Update(int64(currentHeader.Number.Uint64())) } if currentBlock := bc.CurrentBlock(); currentBlock != nil { if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil { // Rewound state missing, rolled back to before pivot, reset to genesis bc.currentBlock.Store(bc.genesisBlock) + headBlockGauge.Update(int64(bc.genesisBlock.NumberU64())) } } // Rewind the fast block in a simpleton way to the target head if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock != nil && currentHeader.Number.Uint64() < currentFastBlock.NumberU64() { bc.currentFastBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())) + headFastBlockGauge.Update(int64(currentHeader.Number.Uint64())) } // If either blocks reached nil, reset to the genesis state if currentBlock := bc.CurrentBlock(); currentBlock == nil { bc.currentBlock.Store(bc.genesisBlock) + headBlockGauge.Update(int64(bc.genesisBlock.NumberU64())) } if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock == nil { bc.currentFastBlock.Store(bc.genesisBlock) + headFastBlockGauge.Update(int64(bc.genesisBlock.NumberU64())) } currentBlock := bc.CurrentBlock() currentFastBlock := bc.CurrentFastBlock() @@ -463,6 +476,7 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error { // If all checks out, manually set the head block bc.mu.Lock() bc.currentBlock.Store(block) + headBlockGauge.Update(int64(block.NumberU64())) bc.mu.Unlock() log.Info("Committed new head block", "number", block.Number(), "hash", hash) @@ -593,9 +607,12 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error { bc.genesisBlock = genesis bc.insert(bc.genesisBlock, false) bc.currentBlock.Store(bc.genesisBlock) + headBlockGauge.Update(int64(bc.genesisBlock.NumberU64())) + bc.hc.SetGenesis(bc.genesisBlock.Header()) bc.hc.SetCurrentHeader(bc.genesisBlock.Header()) bc.currentFastBlock.Store(bc.genesisBlock) + headFastBlockGauge.Update(int64(bc.genesisBlock.NumberU64())) return nil } @@ -695,7 +712,9 @@ func (bc *BlockChain) insert(block *types.Block, writeBlock bool) { if writeBlock { rawdb.WriteBlock(bc.db, block) } + bc.currentBlock.Store(block) + headBlockGauge.Update(int64(block.NumberU64())) // save cache BlockSigners if bc.chainConfig.XDPoS != nil && !bc.chainConfig.IsTIPSigning(block.Number()) { @@ -713,6 +732,7 @@ func (bc *BlockChain) insert(block *types.Block, writeBlock bool) { log.Crit("Failed to insert head fast block hash", "err", err) } bc.currentFastBlock.Store(block) + headFastBlockGauge.Update(int64(block.NumberU64())) } } @@ -1050,10 +1070,12 @@ func (bc *BlockChain) Rollback(chain []common.Hash) { newFastBlock := bc.GetBlock(currentFastBlock.ParentHash(), currentFastBlock.NumberU64()-1) bc.currentFastBlock.Store(newFastBlock) WriteHeadFastBlockHash(bc.db, newFastBlock.Hash()) + headFastBlockGauge.Update(int64(newFastBlock.NumberU64())) } if currentBlock := bc.CurrentBlock(); currentBlock.Hash() == hash { newBlock := bc.GetBlock(currentBlock.ParentHash(), currentBlock.NumberU64()-1) bc.currentBlock.Store(newBlock) + headBlockGauge.Update(int64(newBlock.NumberU64())) rawdb.WriteHeadBlockHash(bc.db, newBlock.Hash()) } } @@ -1136,6 +1158,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ log.Crit("Failed to update head fast block hash", "err", err) } bc.currentFastBlock.Store(head) + headFastBlockGauge.Update(int64(head.NumberU64())) } } bc.mu.Unlock() diff --git a/core/headerchain.go b/core/headerchain.go index b3cdc10f68d5..51a876d2ecac 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -101,6 +101,7 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c } } hc.currentHeaderHash = hc.CurrentHeader().Hash() + headHeaderGauge.Update(hc.CurrentHeader().Number.Int64()) return hc, nil } @@ -176,8 +177,10 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er if err := WriteHeadHeaderHash(hc.chainDb, hash); err != nil { log.Crit("Failed to insert head header hash", "err", err) } + hc.currentHeaderHash = hash hc.currentHeader.Store(types.CopyHeader(header)) + headHeaderGauge.Update(header.Number.Int64()) status = CanonStatTy } else { @@ -392,8 +395,10 @@ func (hc *HeaderChain) SetCurrentHeader(head *types.Header) { if err := WriteHeadHeaderHash(hc.chainDb, head.Hash()); err != nil { log.Crit("Failed to insert head header hash", "err", err) } + hc.currentHeader.Store(head) hc.currentHeaderHash = head.Hash() + headHeaderGauge.Update(head.Number.Int64()) } // DeleteCallback is a callback function that is called by SetHead before @@ -432,6 +437,7 @@ func (hc *HeaderChain) SetHead(head uint64, delFn DeleteCallback) { hc.currentHeader.Store(hc.genesisHeader) } hc.currentHeaderHash = hc.CurrentHeader().Hash() + headHeaderGauge.Update(hc.CurrentHeader().Number.Int64()) if err := WriteHeadHeaderHash(hc.chainDb, hc.currentHeaderHash); err != nil { log.Crit("Failed to reset head header hash", "err", err) diff --git a/go.mod b/go.mod index d849d578c82b..109e053747f8 100644 --- a/go.mod +++ b/go.mod @@ -49,6 +49,7 @@ require ( github.com/crate-crypto/go-kzg-4844 v0.7.0 github.com/deckarep/golang-set v1.8.0 github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 + github.com/elastic/gosigar v0.14.3 github.com/ethereum/c-kzg-4844 v0.4.0 github.com/kylelemons/godebug v1.1.0 github.com/mattn/go-isatty v0.0.17 diff --git a/go.sum b/go.sum index 55a922e8cc24..b826a51c6e9e 100644 --- a/go.sum +++ b/go.sum @@ -43,6 +43,8 @@ github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 h1:Y9vTBSsV4hSwPSj4bac github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/elastic/gosigar v0.14.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/uo= +github.com/elastic/gosigar v0.14.3/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= @@ -166,6 +168,7 @@ github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639 github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= @@ -192,6 +195,7 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -245,6 +249,7 @@ gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 h1:a6cXbcDDUk gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/metrics/cpu.go b/metrics/cpu.go new file mode 100644 index 000000000000..3278d81616a3 --- /dev/null +++ b/metrics/cpu.go @@ -0,0 +1,36 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package metrics + +import "github.com/elastic/gosigar" + +// CPUStats is the system and process CPU stats. +type CPUStats struct { + GlobalTime int64 // Time spent by the CPU working on all processes + GlobalWait int64 // Time spent by waiting on disk for all processes + LocalTime int64 // Time spent by the CPU working on this process +} + +// ReadCPUStats retrieves the current CPU stats. +func ReadCPUStats(stats *CPUStats) { + global := gosigar.Cpu{} + global.Get() + + stats.GlobalTime = int64(global.User + global.Nice + global.Sys) + stats.GlobalWait = int64(global.Wait) + stats.LocalTime = getProcessCPUTime() +} diff --git a/metrics/cpu_syscall.go b/metrics/cpu_syscall.go new file mode 100644 index 000000000000..c111d89e7fcf --- /dev/null +++ b/metrics/cpu_syscall.go @@ -0,0 +1,35 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build !windows + +package metrics + +import ( + "syscall" + + "github.com/XinFinOrg/XDPoSChain/log" +) + +// getProcessCPUTime retrieves the process' CPU time since program startup. +func getProcessCPUTime() int64 { + var usage syscall.Rusage + if err := syscall.Getrusage(syscall.RUSAGE_SELF, &usage); err != nil { + log.Warn("Failed to retrieve CPU time", "err", err) + return 0 + } + return int64(usage.Utime.Sec+usage.Stime.Sec)*100 + int64(usage.Utime.Usec+usage.Stime.Usec)/10000 //nolint:unconvert +} diff --git a/metrics/cpu_windows.go b/metrics/cpu_windows.go new file mode 100644 index 000000000000..fb29a52a82c1 --- /dev/null +++ b/metrics/cpu_windows.go @@ -0,0 +1,23 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package metrics + +// getProcessCPUTime returns 0 on Windows as there is no system call to resolve +// the actual process' CPU time. +func getProcessCPUTime() int64 { + return 0 +} diff --git a/metrics/metrics.go b/metrics/metrics.go index bd23e4b24747..74de03a9dcc7 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -61,18 +61,27 @@ func CollectProcessMetrics(refresh time.Duration) { if !Enabled { return } + refreshFreq := int64(refresh / time.Second) + // Create the various data collectors + cpuStats := make([]*CPUStats, 2) memstats := make([]*runtime.MemStats, 2) diskstats := make([]*DiskStats, 2) for i := 0; i < len(memstats); i++ { + cpuStats[i] = new(CPUStats) memstats[i] = new(runtime.MemStats) diskstats[i] = new(DiskStats) } // Define the various metrics to collect + cpuSysLoad := GetOrRegisterGauge("system/cpu/sysload", DefaultRegistry) + cpuSysWait := GetOrRegisterGauge("system/cpu/syswait", DefaultRegistry) + cpuProcLoad := GetOrRegisterGauge("system/cpu/procload", DefaultRegistry) + + memPauses := GetOrRegisterMeter("system/memory/pauses", DefaultRegistry) memAllocs := GetOrRegisterMeter("system/memory/allocs", DefaultRegistry) memFrees := GetOrRegisterMeter("system/memory/frees", DefaultRegistry) - memInuse := GetOrRegisterMeter("system/memory/inuse", DefaultRegistry) - memPauses := GetOrRegisterMeter("system/memory/pauses", DefaultRegistry) + memHeld := GetOrRegisterGauge("system/memory/held", DefaultRegistry) + memUsed := GetOrRegisterGauge("system/memory/used", DefaultRegistry) var diskReads, diskReadBytes, diskWrites, diskWriteBytes Meter var diskReadBytesCounter, diskWriteBytesCounter Counter @@ -91,11 +100,17 @@ func CollectProcessMetrics(refresh time.Duration) { location1 := i % 2 location2 := (i - 1) % 2 + ReadCPUStats(cpuStats[location1]) + cpuSysLoad.Update((cpuStats[location1].GlobalTime - cpuStats[location2].GlobalTime) / refreshFreq) + cpuSysWait.Update((cpuStats[location1].GlobalWait - cpuStats[location2].GlobalWait) / refreshFreq) + cpuProcLoad.Update((cpuStats[location1].LocalTime - cpuStats[location2].LocalTime) / refreshFreq) + runtime.ReadMemStats(memstats[location1]) + memPauses.Mark(int64(memstats[location1].PauseTotalNs - memstats[location2].PauseTotalNs)) memAllocs.Mark(int64(memstats[location1].Mallocs - memstats[location2].Mallocs)) memFrees.Mark(int64(memstats[location1].Frees - memstats[location2].Frees)) - memInuse.Mark(int64(memstats[location1].Alloc - memstats[location2].Alloc)) - memPauses.Mark(int64(memstats[location1].PauseTotalNs - memstats[location2].PauseTotalNs)) + memHeld.Update(int64(memstats[location1].HeapSys - memstats[location1].HeapReleased)) + memUsed.Update(int64(memstats[location1].Alloc)) if ReadDiskStats(diskstats[location1]) == nil { diskReads.Mark(diskstats[location1].ReadCount - diskstats[location2].ReadCount) diff --git a/p2p/server.go b/p2p/server.go index fabbb9cdbfcd..51579ff7c30c 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -650,9 +650,7 @@ running: // This channel is used by RemoveTrustedPeer to remove an enode // from the trusted node set. srv.log.Trace("Removing trusted node", "node", n) - if _, ok := trusted[n.ID]; ok { - delete(trusted, n.ID) - } + delete(trusted, n.ID) // Unmark any already-connected peer as trusted if p, ok := peers[n.ID]; ok { p.rw.set(trustedConn, false) From a4e113ca110dc04d31f2e5da5aacb13958ba21aa Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:11 +0800 Subject: [PATCH 319/479] metrics: gather and export threads and goroutines (#19725) --- metrics/metrics.go | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/metrics/metrics.go b/metrics/metrics.go index 74de03a9dcc7..2df2404b5f60 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -73,28 +73,26 @@ func CollectProcessMetrics(refresh time.Duration) { diskstats[i] = new(DiskStats) } // Define the various metrics to collect - cpuSysLoad := GetOrRegisterGauge("system/cpu/sysload", DefaultRegistry) - cpuSysWait := GetOrRegisterGauge("system/cpu/syswait", DefaultRegistry) - cpuProcLoad := GetOrRegisterGauge("system/cpu/procload", DefaultRegistry) - - memPauses := GetOrRegisterMeter("system/memory/pauses", DefaultRegistry) - memAllocs := GetOrRegisterMeter("system/memory/allocs", DefaultRegistry) - memFrees := GetOrRegisterMeter("system/memory/frees", DefaultRegistry) - memHeld := GetOrRegisterGauge("system/memory/held", DefaultRegistry) - memUsed := GetOrRegisterGauge("system/memory/used", DefaultRegistry) - - var diskReads, diskReadBytes, diskWrites, diskWriteBytes Meter - var diskReadBytesCounter, diskWriteBytesCounter Counter - if err := ReadDiskStats(diskstats[0]); err == nil { - diskReads = GetOrRegisterMeter("system/disk/readcount", DefaultRegistry) - diskReadBytes = GetOrRegisterMeter("system/disk/readdata", DefaultRegistry) - diskReadBytesCounter = GetOrRegisterCounter("system/disk/readbytes", DefaultRegistry) - diskWrites = GetOrRegisterMeter("system/disk/writecount", DefaultRegistry) - diskWriteBytes = GetOrRegisterMeter("system/disk/writedata", DefaultRegistry) + var ( + cpuSysLoad = GetOrRegisterGauge("system/cpu/sysload", DefaultRegistry) + cpuSysWait = GetOrRegisterGauge("system/cpu/syswait", DefaultRegistry) + cpuProcLoad = GetOrRegisterGauge("system/cpu/procload", DefaultRegistry) + cpuThreads = GetOrRegisterGauge("system/cpu/threads", DefaultRegistry) + cpuGoroutines = GetOrRegisterGauge("system/cpu/goroutines", DefaultRegistry) + + memPauses = GetOrRegisterMeter("system/memory/pauses", DefaultRegistry) + memAllocs = GetOrRegisterMeter("system/memory/allocs", DefaultRegistry) + memFrees = GetOrRegisterMeter("system/memory/frees", DefaultRegistry) + memHeld = GetOrRegisterGauge("system/memory/held", DefaultRegistry) + memUsed = GetOrRegisterGauge("system/memory/used", DefaultRegistry) + + diskReads = GetOrRegisterMeter("system/disk/readcount", DefaultRegistry) + diskReadBytes = GetOrRegisterMeter("system/disk/readdata", DefaultRegistry) + diskReadBytesCounter = GetOrRegisterCounter("system/disk/readbytes", DefaultRegistry) + diskWrites = GetOrRegisterMeter("system/disk/writecount", DefaultRegistry) + diskWriteBytes = GetOrRegisterMeter("system/disk/writedata", DefaultRegistry) diskWriteBytesCounter = GetOrRegisterCounter("system/disk/writebytes", DefaultRegistry) - } else { - log.Debug("Failed to read disk metrics", "err", err) - } + ) // Iterate loading the different stats and updating the meters for i := 1; ; i++ { location1 := i % 2 @@ -104,6 +102,8 @@ func CollectProcessMetrics(refresh time.Duration) { cpuSysLoad.Update((cpuStats[location1].GlobalTime - cpuStats[location2].GlobalTime) / refreshFreq) cpuSysWait.Update((cpuStats[location1].GlobalWait - cpuStats[location2].GlobalWait) / refreshFreq) cpuProcLoad.Update((cpuStats[location1].LocalTime - cpuStats[location2].LocalTime) / refreshFreq) + cpuThreads.Update(int64(threadCreateProfile.Count())) + cpuGoroutines.Update(int64(runtime.NumGoroutine())) runtime.ReadMemStats(memstats[location1]) memPauses.Mark(int64(memstats[location1].PauseTotalNs - memstats[location2].PauseTotalNs)) From 745640795a13579a071c8cf47d79d64900b9b5d2 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:11 +0800 Subject: [PATCH 320/479] metrics: change links in README.md to https (#20182) --- metrics/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metrics/README.md b/metrics/README.md index bc2a45a8382d..e2d7945008e6 100644 --- a/metrics/README.md +++ b/metrics/README.md @@ -5,7 +5,7 @@ go-metrics Go port of Coda Hale's Metrics library: . -Documentation: . +Documentation: . Usage ----- @@ -128,7 +128,7 @@ go stathat.Stathat(metrics.DefaultRegistry, 10e9, "example@example.com") Maintain all metrics along with expvars at `/debug/metrics`: -This uses the same mechanism as [the official expvar](http://golang.org/pkg/expvar/) +This uses the same mechanism as [the official expvar](https://golang.org/pkg/expvar/) but exposed under `/debug/metrics`, which shows a json representation of all your usual expvars as well as all your go-metrics. From 332ac32bc5275302a342b362bba1d5cacfc78d12 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 321/479] metrics: not compare float numbers directly (#20219) --- metrics/doc.go | 4 ++ metrics/ewma_test.go | 101 +++++++++++++++++++++-------------------- metrics/sample_test.go | 3 +- 3 files changed, 58 insertions(+), 50 deletions(-) create mode 100644 metrics/doc.go diff --git a/metrics/doc.go b/metrics/doc.go new file mode 100644 index 000000000000..13f429c1689d --- /dev/null +++ b/metrics/doc.go @@ -0,0 +1,4 @@ +package metrics + +const epsilon = 0.0000000000000001 +const epsilonPercentile = .00000000001 diff --git a/metrics/ewma_test.go b/metrics/ewma_test.go index 39e67c605b89..5b244191616e 100644 --- a/metrics/ewma_test.go +++ b/metrics/ewma_test.go @@ -1,6 +1,9 @@ package metrics -import "testing" +import ( + "math" + "testing" +) func BenchmarkEWMA(b *testing.B) { a := NewEWMA1() @@ -15,67 +18,67 @@ func TestEWMA1(t *testing.T) { a := NewEWMA1() a.Update(3) a.Tick() - if rate := a.Rate(); rate != 0.6 { + if rate := a.Rate(); math.Abs(0.6-rate) > epsilon { t.Errorf("initial a.Rate(): 0.6 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.22072766470286553 { + if rate := a.Rate(); math.Abs(0.22072766470286553-rate) > epsilon { t.Errorf("1 minute a.Rate(): 0.22072766470286553 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.08120116994196772 { + if rate := a.Rate(); math.Abs(0.08120116994196772-rate) > epsilon { t.Errorf("2 minute a.Rate(): 0.08120116994196772 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.029872241020718428 { + if rate := a.Rate(); math.Abs(0.029872241020718428-rate) > epsilon { t.Errorf("3 minute a.Rate(): 0.029872241020718428 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.01098938333324054 { + if rate := a.Rate(); math.Abs(0.01098938333324054-rate) > epsilon { t.Errorf("4 minute a.Rate(): 0.01098938333324054 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.004042768199451294 { + if rate := a.Rate(); math.Abs(0.004042768199451294-rate) > epsilon { t.Errorf("5 minute a.Rate(): 0.004042768199451294 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.0014872513059998212 { + if rate := a.Rate(); math.Abs(0.0014872513059998212-rate) > epsilon { t.Errorf("6 minute a.Rate(): 0.0014872513059998212 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.0005471291793327122 { + if rate := a.Rate(); math.Abs(0.0005471291793327122-rate) > epsilon { t.Errorf("7 minute a.Rate(): 0.0005471291793327122 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.00020127757674150815 { + if rate := a.Rate(); math.Abs(0.00020127757674150815-rate) > epsilon { t.Errorf("8 minute a.Rate(): 0.00020127757674150815 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 7.404588245200814e-05 { + if rate := a.Rate(); math.Abs(7.404588245200814e-05-rate) > epsilon { t.Errorf("9 minute a.Rate(): 7.404588245200814e-05 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 2.7239957857491083e-05 { + if rate := a.Rate(); math.Abs(2.7239957857491083e-05-rate) > epsilon { t.Errorf("10 minute a.Rate(): 2.7239957857491083e-05 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 1.0021020474147462e-05 { + if rate := a.Rate(); math.Abs(1.0021020474147462e-05-rate) > epsilon { t.Errorf("11 minute a.Rate(): 1.0021020474147462e-05 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 3.6865274119969525e-06 { + if rate := a.Rate(); math.Abs(3.6865274119969525e-06-rate) > epsilon { t.Errorf("12 minute a.Rate(): 3.6865274119969525e-06 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 1.3561976441886433e-06 { + if rate := a.Rate(); math.Abs(1.3561976441886433e-06-rate) > epsilon { t.Errorf("13 minute a.Rate(): 1.3561976441886433e-06 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 4.989172314621449e-07 { + if rate := a.Rate(); math.Abs(4.989172314621449e-07-rate) > epsilon { t.Errorf("14 minute a.Rate(): 4.989172314621449e-07 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 1.8354139230109722e-07 { + if rate := a.Rate(); math.Abs(1.8354139230109722e-07-rate) > epsilon { t.Errorf("15 minute a.Rate(): 1.8354139230109722e-07 != %v\n", rate) } } @@ -84,67 +87,67 @@ func TestEWMA5(t *testing.T) { a := NewEWMA5() a.Update(3) a.Tick() - if rate := a.Rate(); rate != 0.6 { + if rate := a.Rate(); math.Abs(0.6-rate) > epsilon { t.Errorf("initial a.Rate(): 0.6 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.49123845184678905 { + if rate := a.Rate(); math.Abs(0.49123845184678905-rate) > epsilon { t.Errorf("1 minute a.Rate(): 0.49123845184678905 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.4021920276213837 { + if rate := a.Rate(); math.Abs(0.4021920276213837-rate) > epsilon { t.Errorf("2 minute a.Rate(): 0.4021920276213837 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.32928698165641596 { + if rate := a.Rate(); math.Abs(0.32928698165641596-rate) > epsilon { t.Errorf("3 minute a.Rate(): 0.32928698165641596 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.269597378470333 { + if rate := a.Rate(); math.Abs(0.269597378470333-rate) > epsilon { t.Errorf("4 minute a.Rate(): 0.269597378470333 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.2207276647028654 { + if rate := a.Rate(); math.Abs(0.2207276647028654-rate) > epsilon { t.Errorf("5 minute a.Rate(): 0.2207276647028654 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.18071652714732128 { + if rate := a.Rate(); math.Abs(0.18071652714732128-rate) > epsilon { t.Errorf("6 minute a.Rate(): 0.18071652714732128 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.14795817836496392 { + if rate := a.Rate(); math.Abs(0.14795817836496392-rate) > epsilon { t.Errorf("7 minute a.Rate(): 0.14795817836496392 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.12113791079679326 { + if rate := a.Rate(); math.Abs(0.12113791079679326-rate) > epsilon { t.Errorf("8 minute a.Rate(): 0.12113791079679326 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.09917933293295193 { + if rate := a.Rate(); math.Abs(0.09917933293295193-rate) > epsilon { t.Errorf("9 minute a.Rate(): 0.09917933293295193 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.08120116994196763 { + if rate := a.Rate(); math.Abs(0.08120116994196763-rate) > epsilon { t.Errorf("10 minute a.Rate(): 0.08120116994196763 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.06648189501740036 { + if rate := a.Rate(); math.Abs(0.06648189501740036-rate) > epsilon { t.Errorf("11 minute a.Rate(): 0.06648189501740036 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.05443077197364752 { + if rate := a.Rate(); math.Abs(0.05443077197364752-rate) > epsilon { t.Errorf("12 minute a.Rate(): 0.05443077197364752 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.04456414692860035 { + if rate := a.Rate(); math.Abs(0.04456414692860035-rate) > epsilon { t.Errorf("13 minute a.Rate(): 0.04456414692860035 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.03648603757513079 { + if rate := a.Rate(); math.Abs(0.03648603757513079-rate) > epsilon { t.Errorf("14 minute a.Rate(): 0.03648603757513079 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.0298722410207183831020718428 { + if rate := a.Rate(); math.Abs(0.0298722410207183831020718428-rate) > epsilon { t.Errorf("15 minute a.Rate(): 0.0298722410207183831020718428 != %v\n", rate) } } @@ -153,67 +156,67 @@ func TestEWMA15(t *testing.T) { a := NewEWMA15() a.Update(3) a.Tick() - if rate := a.Rate(); rate != 0.6 { + if rate := a.Rate(); math.Abs(0.6-rate) > epsilon { t.Errorf("initial a.Rate(): 0.6 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.5613041910189706 { + if rate := a.Rate(); math.Abs(0.5613041910189706-rate) > epsilon { t.Errorf("1 minute a.Rate(): 0.5613041910189706 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.5251039914257684 { + if rate := a.Rate(); math.Abs(0.5251039914257684-rate) > epsilon { t.Errorf("2 minute a.Rate(): 0.5251039914257684 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.4912384518467888184678905 { + if rate := a.Rate(); math.Abs(0.4912384518467888184678905-rate) > epsilon { t.Errorf("3 minute a.Rate(): 0.4912384518467888184678905 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.459557003018789 { + if rate := a.Rate(); math.Abs(0.459557003018789-rate) > epsilon { t.Errorf("4 minute a.Rate(): 0.459557003018789 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.4299187863442732 { + if rate := a.Rate(); math.Abs(0.4299187863442732-rate) > epsilon { t.Errorf("5 minute a.Rate(): 0.4299187863442732 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.4021920276213831 { + if rate := a.Rate(); math.Abs(0.4021920276213831-rate) > epsilon { t.Errorf("6 minute a.Rate(): 0.4021920276213831 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.37625345116383313 { + if rate := a.Rate(); math.Abs(0.37625345116383313-rate) > epsilon { t.Errorf("7 minute a.Rate(): 0.37625345116383313 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.3519877317060185 { + if rate := a.Rate(); math.Abs(0.3519877317060185-rate) > epsilon { t.Errorf("8 minute a.Rate(): 0.3519877317060185 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.3292869816564153165641596 { + if rate := a.Rate(); math.Abs(0.3292869816564153165641596-rate) > epsilon { t.Errorf("9 minute a.Rate(): 0.3292869816564153165641596 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.3080502714195546 { + if rate := a.Rate(); math.Abs(0.3080502714195546-rate) > epsilon { t.Errorf("10 minute a.Rate(): 0.3080502714195546 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.2881831806538789 { + if rate := a.Rate(); math.Abs(0.2881831806538789-rate) > epsilon { t.Errorf("11 minute a.Rate(): 0.2881831806538789 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.26959737847033216 { + if rate := a.Rate(); math.Abs(0.26959737847033216-rate) > epsilon { t.Errorf("12 minute a.Rate(): 0.26959737847033216 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.2522102307052083 { + if rate := a.Rate(); math.Abs(0.2522102307052083-rate) > epsilon { t.Errorf("13 minute a.Rate(): 0.2522102307052083 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.23594443252115815 { + if rate := a.Rate(); math.Abs(0.23594443252115815-rate) > epsilon { t.Errorf("14 minute a.Rate(): 0.23594443252115815 != %v\n", rate) } elapseMinute(a) - if rate := a.Rate(); rate != 0.2207276647028646247028654470286553 { + if rate := a.Rate(); math.Abs(0.2207276647028646247028654470286553-rate) > epsilon { t.Errorf("15 minute a.Rate(): 0.2207276647028646247028654470286553 != %v\n", rate) } } diff --git a/metrics/sample_test.go b/metrics/sample_test.go index d6e966400b24..be7bf3d2dffb 100644 --- a/metrics/sample_test.go +++ b/metrics/sample_test.go @@ -1,6 +1,7 @@ package metrics import ( + "math" "math/rand" "runtime" "testing" @@ -326,7 +327,7 @@ func testUniformSampleStatistics(t *testing.T, s Sample) { if ps[1] != 7380.5 { t.Errorf("75th percentile: 7380.5 != %v\n", ps[1]) } - if ps[2] != 9986.429999999998 { + if math.Abs(ps[2]-9986.429999999998) > epsilonPercentile { t.Errorf("99th percentile: 9986.429999999998 != %v\n", ps[2]) } } From 462999b38124b95704eea83c1c5168e1f9b265ef Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 322/479] metrics: fix issues reported by staticcheck (#20365) --- metrics/librato/librato.go | 5 +++-- metrics/registry_test.go | 13 +++++++++---- metrics/timer.go | 5 +---- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/metrics/librato/librato.go b/metrics/librato/librato.go index 45bf6cf297a7..9323290bc51b 100644 --- a/metrics/librato/librato.go +++ b/metrics/librato/librato.go @@ -42,9 +42,10 @@ func Librato(r metrics.Registry, d time.Duration, e string, t string, s string, func (re *Reporter) Run() { log.Printf("WARNING: This client has been DEPRECATED! It has been moved to https://github.com/mihasya/go-metrics-librato and will be removed from rcrowley/go-metrics on August 5th 2015") - ticker := time.Tick(re.Interval) + ticker := time.NewTicker(re.Interval) + defer ticker.Stop() metricsApi := &LibratoClient{re.Email, re.Token} - for now := range ticker { + for now := range ticker.C { var metrics Batch var err error if metrics, err = re.BuildRequest(now, re.Registry); err != nil { diff --git a/metrics/registry_test.go b/metrics/registry_test.go index 416c82d0b207..d277ae5c3e47 100644 --- a/metrics/registry_test.go +++ b/metrics/registry_test.go @@ -270,7 +270,10 @@ func TestChildPrefixedRegistryOfChildRegister(t *testing.T) { if err != nil { t.Fatal(err.Error()) } - r2.Register("baz", NewCounter()) + err = r2.Register("baz", NewCounter()) + if err != nil { + t.Fatal(err.Error()) + } c := NewCounter() Register("bars", c) @@ -278,7 +281,7 @@ func TestChildPrefixedRegistryOfChildRegister(t *testing.T) { r2.Each(func(name string, m interface{}) { i++ if name != "prefix.prefix2.baz" { - //t.Fatal(name) + t.Fatal(name) } }) if i != 1 { @@ -293,7 +296,10 @@ func TestWalkRegistries(t *testing.T) { if err != nil { t.Fatal(err.Error()) } - r2.Register("baz", NewCounter()) + err = r2.Register("baz", NewCounter()) + if err != nil { + t.Fatal(err.Error()) + } c := NewCounter() Register("bars", c) @@ -301,5 +307,4 @@ func TestWalkRegistries(t *testing.T) { if prefix != "prefix.prefix2." { t.Fatal(prefix) } - } diff --git a/metrics/timer.go b/metrics/timer.go index 89e22208fde0..a63c9dfb6c2d 100644 --- a/metrics/timer.go +++ b/metrics/timer.go @@ -76,10 +76,7 @@ func NewTimer() Timer { } // NilTimer is a no-op Timer. -type NilTimer struct { - h Histogram - m Meter -} +type NilTimer struct{} // Count is a no-op. func (NilTimer) Count() int64 { return 0 } From 9fee8a72ebd0ddd485af610b6478e26c2ea61960 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 323/479] metrics: disable CPU stats (gosigar) on iOS (#20816) --- metrics/cpu.go | 12 ------------ metrics/cpu_disabled.go | 23 +++++++++++++++++++++++ metrics/cpu_enabled.go | 31 +++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 12 deletions(-) create mode 100644 metrics/cpu_disabled.go create mode 100644 metrics/cpu_enabled.go diff --git a/metrics/cpu.go b/metrics/cpu.go index 3278d81616a3..72ece16e0768 100644 --- a/metrics/cpu.go +++ b/metrics/cpu.go @@ -16,21 +16,9 @@ package metrics -import "github.com/elastic/gosigar" - // CPUStats is the system and process CPU stats. type CPUStats struct { GlobalTime int64 // Time spent by the CPU working on all processes GlobalWait int64 // Time spent by waiting on disk for all processes LocalTime int64 // Time spent by the CPU working on this process } - -// ReadCPUStats retrieves the current CPU stats. -func ReadCPUStats(stats *CPUStats) { - global := gosigar.Cpu{} - global.Get() - - stats.GlobalTime = int64(global.User + global.Nice + global.Sys) - stats.GlobalWait = int64(global.Wait) - stats.LocalTime = getProcessCPUTime() -} diff --git a/metrics/cpu_disabled.go b/metrics/cpu_disabled.go new file mode 100644 index 000000000000..6c3428993fb1 --- /dev/null +++ b/metrics/cpu_disabled.go @@ -0,0 +1,23 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build ios + +package metrics + +// ReadCPUStats retrieves the current CPU stats. Internally this uses `gosigar`, +// which is not supported on the platforms in this file. +func ReadCPUStats(stats *CPUStats) {} diff --git a/metrics/cpu_enabled.go b/metrics/cpu_enabled.go new file mode 100644 index 000000000000..99d44e400229 --- /dev/null +++ b/metrics/cpu_enabled.go @@ -0,0 +1,31 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build !ios + +package metrics + +import "github.com/elastic/gosigar" + +// ReadCPUStats retrieves the current CPU stats. +func ReadCPUStats(stats *CPUStats) { + global := gosigar.Cpu{} + global.Get() + + stats.GlobalTime = int64(global.User + global.Nice + global.Sys) + stats.GlobalWait = int64(global.Wait) + stats.LocalTime = getProcessCPUTime() +} From 47ce406a4a2a73121581ed4f3e01c067cefb58d5 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 324/479] metrics: make flawed test less flawed (#20818) --- metrics/timer_test.go | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/metrics/timer_test.go b/metrics/timer_test.go index d3750e4f9fa3..390a0e6c23a8 100644 --- a/metrics/timer_test.go +++ b/metrics/timer_test.go @@ -45,10 +45,23 @@ func TestTimerStop(t *testing.T) { } func TestTimerFunc(t *testing.T) { - tm := NewTimer() - tm.Time(func() { time.Sleep(50e6) }) - if max := tm.Max(); 35e6 > max || max > 95e6 { - t.Errorf("tm.Max(): 35e6 > %v || %v > 95e6\n", max, max) + var ( + tm = NewTimer() + testStart = time.Now() + actualTime time.Duration + ) + tm.Time(func() { + time.Sleep(50 * time.Millisecond) + actualTime = time.Since(testStart) + }) + var ( + drift = time.Millisecond * 2 + measured = time.Duration(tm.Max()) + ceil = actualTime + drift + floor = actualTime - drift + ) + if measured > ceil || measured < floor { + t.Errorf("tm.Max(): %v > %v || %v > %v\n", measured, ceil, measured, floor) } } From 1415bb63695780d26924f1a696c3a4f42a41a80f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 325/479] metrics: add missing calls to Ticker.Stop in tests (#20866) --- metrics/meter_test.go | 1 + metrics/sample_test.go | 1 + 2 files changed, 2 insertions(+) diff --git a/metrics/meter_test.go b/metrics/meter_test.go index 0dfce76b7af0..37e45bded251 100644 --- a/metrics/meter_test.go +++ b/metrics/meter_test.go @@ -26,6 +26,7 @@ func TestMeterDecay(t *testing.T) { ticker: time.NewTicker(time.Millisecond), meters: make(map[*StandardMeter]struct{}), } + defer ma.ticker.Stop() m := newStandardMeter() ma.meters[m] = struct{}{} go ma.tick() diff --git a/metrics/sample_test.go b/metrics/sample_test.go index be7bf3d2dffb..5b2ee96e3679 100644 --- a/metrics/sample_test.go +++ b/metrics/sample_test.go @@ -346,6 +346,7 @@ func TestUniformSampleConcurrentUpdateCount(t *testing.T) { quit := make(chan struct{}) go func() { t := time.NewTicker(10 * time.Millisecond) + defer t.Stop() for { select { case <-t.C: From c65d0cd947ced255c0d2f9fdcc7116b87f972626 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 326/479] cmd/XDC: enable metrics for geth import command (#20738) --- cmd/XDC/chaincmd.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cmd/XDC/chaincmd.go b/cmd/XDC/chaincmd.go index 5f12048f2cac..78a249ea1c4f 100644 --- a/cmd/XDC/chaincmd.go +++ b/cmd/XDC/chaincmd.go @@ -71,6 +71,14 @@ It expects the genesis file as argument.`, utils.GCModeFlag, utils.CacheDatabaseFlag, utils.CacheGCFlag, + utils.MetricsEnabledFlag, + utils.MetricsEnabledExpensiveFlag, + utils.MetricsEnableInfluxDBFlag, + utils.MetricsInfluxDBEndpointFlag, + utils.MetricsInfluxDBDatabaseFlag, + utils.MetricsInfluxDBUsernameFlag, + utils.MetricsInfluxDBPasswordFlag, + utils.MetricsInfluxDBTagsFlag, }, Description: ` The import command imports blocks from an RLP-encoded form. The form can be one file From 32f974cc7b28bc3225d116b069253a693f3d3f74 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 327/479] metrics/prometheus: define TYPE once, add tests (#21068) * metrics/prometheus: define type once for histograms * metrics/prometheus: test collector --- metrics/prometheus/collector.go | 11 ++- metrics/prometheus/collector_test.go | 110 +++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 metrics/prometheus/collector_test.go diff --git a/metrics/prometheus/collector.go b/metrics/prometheus/collector.go index d0e731e6c2dc..22f2d2f02a70 100644 --- a/metrics/prometheus/collector.go +++ b/metrics/prometheus/collector.go @@ -30,7 +30,7 @@ var ( typeCounterTpl = "# TYPE %s counter\n" typeSummaryTpl = "# TYPE %s summary\n" keyValueTpl = "%s %v\n\n" - keyQuantileTagValueTpl = "%s {quantile=\"%s\"} %v\n\n" + keyQuantileTagValueTpl = "%s {quantile=\"%s\"} %v\n" ) // collector is a collection of byte buffers that aggregate Prometheus reports @@ -39,7 +39,7 @@ type collector struct { buff *bytes.Buffer } -// newCollector createa a new Prometheus metric aggregator. +// newCollector creates a new Prometheus metric aggregator. func newCollector() *collector { return &collector{ buff: &bytes.Buffer{}, @@ -62,9 +62,11 @@ func (c *collector) addHistogram(name string, m metrics.Histogram) { pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999} ps := m.Percentiles(pv) c.writeSummaryCounter(name, m.Count()) + c.buff.WriteString(fmt.Sprintf(typeSummaryTpl, mutateKey(name))) for i := range pv { c.writeSummaryPercentile(name, strconv.FormatFloat(pv[i], 'f', -1, 64), ps[i]) } + c.buff.WriteRune('\n') } func (c *collector) addMeter(name string, m metrics.Meter) { @@ -75,9 +77,11 @@ func (c *collector) addTimer(name string, m metrics.Timer) { pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999} ps := m.Percentiles(pv) c.writeSummaryCounter(name, m.Count()) + c.buff.WriteString(fmt.Sprintf(typeSummaryTpl, mutateKey(name))) for i := range pv { c.writeSummaryPercentile(name, strconv.FormatFloat(pv[i], 'f', -1, 64), ps[i]) } + c.buff.WriteRune('\n') } func (c *collector) addResettingTimer(name string, m metrics.ResettingTimer) { @@ -87,9 +91,11 @@ func (c *collector) addResettingTimer(name string, m metrics.ResettingTimer) { ps := m.Percentiles([]float64{50, 95, 99}) val := m.Values() c.writeSummaryCounter(name, len(val)) + c.buff.WriteString(fmt.Sprintf(typeSummaryTpl, mutateKey(name))) c.writeSummaryPercentile(name, "0.50", ps[0]) c.writeSummaryPercentile(name, "0.95", ps[1]) c.writeSummaryPercentile(name, "0.99", ps[2]) + c.buff.WriteRune('\n') } func (c *collector) writeGaugeCounter(name string, value interface{}) { @@ -106,7 +112,6 @@ func (c *collector) writeSummaryCounter(name string, value interface{}) { func (c *collector) writeSummaryPercentile(name, p string, value interface{}) { name = mutateKey(name) - c.buff.WriteString(fmt.Sprintf(typeSummaryTpl, name)) c.buff.WriteString(fmt.Sprintf(keyQuantileTagValueTpl, name, p, value)) } diff --git a/metrics/prometheus/collector_test.go b/metrics/prometheus/collector_test.go new file mode 100644 index 000000000000..4c2ffcb1fed6 --- /dev/null +++ b/metrics/prometheus/collector_test.go @@ -0,0 +1,110 @@ +package prometheus + +import ( + "os" + "testing" + "time" + + "github.com/XinFinOrg/XDPoSChain/metrics" +) + +func TestMain(m *testing.M) { + metrics.Enabled = true + os.Exit(m.Run()) +} + +func TestCollector(t *testing.T) { + c := newCollector() + + counter := metrics.NewCounter() + counter.Inc(12345) + c.addCounter("test/counter", counter) + + gauge := metrics.NewGauge() + gauge.Update(23456) + c.addGauge("test/gauge", gauge) + + gaugeFloat64 := metrics.NewGaugeFloat64() + gaugeFloat64.Update(34567.89) + c.addGaugeFloat64("test/gauge_float64", gaugeFloat64) + + histogram := metrics.NewHistogram(&metrics.NilSample{}) + c.addHistogram("test/histogram", histogram) + + meter := metrics.NewMeter() + defer meter.Stop() + meter.Mark(9999999) + c.addMeter("test/meter", meter) + + timer := metrics.NewTimer() + defer timer.Stop() + timer.Update(20 * time.Millisecond) + timer.Update(21 * time.Millisecond) + timer.Update(22 * time.Millisecond) + timer.Update(120 * time.Millisecond) + timer.Update(23 * time.Millisecond) + timer.Update(24 * time.Millisecond) + c.addTimer("test/timer", timer) + + resettingTimer := metrics.NewResettingTimer() + resettingTimer.Update(10 * time.Millisecond) + resettingTimer.Update(11 * time.Millisecond) + resettingTimer.Update(12 * time.Millisecond) + resettingTimer.Update(120 * time.Millisecond) + resettingTimer.Update(13 * time.Millisecond) + resettingTimer.Update(14 * time.Millisecond) + c.addResettingTimer("test/resetting_timer", resettingTimer.Snapshot()) + + emptyResettingTimer := metrics.NewResettingTimer().Snapshot() + c.addResettingTimer("test/empty_resetting_timer", emptyResettingTimer) + + const expectedOutput = `# TYPE test_counter gauge +test_counter 12345 + +# TYPE test_gauge gauge +test_gauge 23456 + +# TYPE test_gauge_float64 gauge +test_gauge_float64 34567.89 + +# TYPE test_histogram_count counter +test_histogram_count 0 + +# TYPE test_histogram summary +test_histogram {quantile="0.5"} 0 +test_histogram {quantile="0.75"} 0 +test_histogram {quantile="0.95"} 0 +test_histogram {quantile="0.99"} 0 +test_histogram {quantile="0.999"} 0 +test_histogram {quantile="0.9999"} 0 + +# TYPE test_meter gauge +test_meter 9999999 + +# TYPE test_timer_count counter +test_timer_count 6 + +# TYPE test_timer summary +test_timer {quantile="0.5"} 2.25e+07 +test_timer {quantile="0.75"} 4.8e+07 +test_timer {quantile="0.95"} 1.2e+08 +test_timer {quantile="0.99"} 1.2e+08 +test_timer {quantile="0.999"} 1.2e+08 +test_timer {quantile="0.9999"} 1.2e+08 + +# TYPE test_resetting_timer_count counter +test_resetting_timer_count 6 + +# TYPE test_resetting_timer summary +test_resetting_timer {quantile="0.50"} 12000000 +test_resetting_timer {quantile="0.95"} 120000000 +test_resetting_timer {quantile="0.99"} 120000000 + +` + exp := c.buff.String() + if exp != expectedOutput { + t.Log("Expected Output:\n", expectedOutput) + t.Log("Actual Output:\n", exp) + t.Fatal("unexpected collector output") + } +} From d7d54b00f74f83aa12627bd9eb10611444d92015 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 328/479] metrics: replace gosigar with gopsutil (#21041) --- go.mod | 3 ++- go.sum | 9 ++++----- metrics/cpu_enabled.go | 21 +++++++++++++++------ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 109e053747f8..60f4d9bc50a2 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,6 @@ require ( github.com/crate-crypto/go-kzg-4844 v0.7.0 github.com/deckarep/golang-set v1.8.0 github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 - github.com/elastic/gosigar v0.14.3 github.com/ethereum/c-kzg-4844 v0.4.0 github.com/kylelemons/godebug v1.1.0 github.com/mattn/go-isatty v0.0.17 @@ -80,6 +79,8 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect github.com/supranational/blst v0.3.11 // indirect + github.com/tklauser/go-sysconf v0.3.14 // indirect + github.com/tklauser/numcpus v0.8.0 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/term v0.26.0 // indirect diff --git a/go.sum b/go.sum index b826a51c6e9e..4b74e864cb21 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,6 @@ github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 h1:Y9vTBSsV4hSwPSj4bac github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/elastic/gosigar v0.14.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/uo= -github.com/elastic/gosigar v0.14.3/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= @@ -168,13 +166,16 @@ github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639 github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU= +github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY= +github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY= +github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE= github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= @@ -195,7 +196,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -249,7 +249,6 @@ gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 h1:a6cXbcDDUk gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/metrics/cpu_enabled.go b/metrics/cpu_enabled.go index 99d44e400229..15476aa902fb 100644 --- a/metrics/cpu_enabled.go +++ b/metrics/cpu_enabled.go @@ -14,18 +14,27 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . +//go:build !ios // +build !ios package metrics -import "github.com/elastic/gosigar" +import ( + "github.com/XinFinOrg/XDPoSChain/log" + "github.com/shirou/gopsutil/cpu" +) // ReadCPUStats retrieves the current CPU stats. func ReadCPUStats(stats *CPUStats) { - global := gosigar.Cpu{} - global.Get() - - stats.GlobalTime = int64(global.User + global.Nice + global.Sys) - stats.GlobalWait = int64(global.Wait) + // passing false to request all cpu times + timeStats, err := cpu.Times(false) + if err != nil { + log.Error("Could not read cpu stats", "err", err) + return + } + // requesting all cpu times will always return an array with only one time stats entry + timeStat := timeStats[0] + stats.GlobalTime = int64((timeStat.User + timeStat.Nice + timeStat.System) * cpu.ClocksPerSec) + stats.GlobalWait = int64((timeStat.Iowait) * cpu.ClocksPerSec) stats.LocalTime = getProcessCPUTime() } From 3dee6675d21f56e6042e4537f0eab3e5c29ef249 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 329/479] metrics/exp: allow configuring metrics HTTP server on separate endpoint (#21290) --- internal/debug/flags.go | 10 +++++++--- metrics/exp/exp.go | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/internal/debug/flags.go b/internal/debug/flags.go index 1e9970b73ce5..cf5b49e4754e 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -313,7 +313,9 @@ func Setup(ctx *cli.Context) error { // pprof server if ctx.Bool(pprofFlag.Name) { address := fmt.Sprintf("%s:%d", ctx.String(pprofAddrFlag.Name), ctx.Int(pprofPortFlag.Name)) - StartPProf(address) + // This context value ("metrics-addr") represents the utils.MetricsHTTPFlag.Name. + // It cannot be imported because it will cause a cyclical dependency. + StartPProf(address, !ctx.IsSet("metrics-addr") && !ctx.IsSet("metrics.addr")) } if len(logFile) > 0 || rotation { @@ -323,10 +325,12 @@ func Setup(ctx *cli.Context) error { return nil } -func StartPProf(address string) { +func StartPProf(address string, withMetrics bool) { // Hook go-metrics into expvar on any /debug/metrics request, load all vars // from the registry into expvar, and execute regular expvar handler. - exp.Exp(metrics.DefaultRegistry) + if withMetrics { + exp.Exp(metrics.DefaultRegistry) + } log.Info("Starting pprof server", "addr", fmt.Sprintf("http://%s/debug/pprof", address)) go func() { if err := http.ListenAndServe(address, nil); err != nil { diff --git a/metrics/exp/exp.go b/metrics/exp/exp.go index 28435beceac5..622c2b5289af 100644 --- a/metrics/exp/exp.go +++ b/metrics/exp/exp.go @@ -58,6 +58,7 @@ func ExpHandler(r metrics.Registry) http.Handler { func Setup(address string) { m := http.NewServeMux() m.Handle("/debug/metrics", ExpHandler(metrics.DefaultRegistry)) + m.Handle("/debug/metrics/prometheus", prometheus.Handler(metrics.DefaultRegistry)) log.Info("Starting metrics server", "addr", fmt.Sprintf("http://%s/debug/metrics", address)) go func() { if err := http.ListenAndServe(address, m); err != nil { From 730960ff0669e3a5c94af15f8cb6eb60849610f9 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 330/479] metrics: make meter updates lock-free (#21446) --- metrics/ewma.go | 5 ++-- metrics/meter.go | 65 +++++++++++++++++++++---------------------- metrics/meter_test.go | 5 ++-- 3 files changed, 37 insertions(+), 38 deletions(-) diff --git a/metrics/ewma.go b/metrics/ewma.go index 57c949e7d421..039286493ebc 100644 --- a/metrics/ewma.go +++ b/metrics/ewma.go @@ -4,6 +4,7 @@ import ( "math" "sync" "sync/atomic" + "time" ) // EWMAs continuously calculate an exponentially-weighted moving average @@ -85,7 +86,7 @@ type StandardEWMA struct { func (a *StandardEWMA) Rate() float64 { a.mutex.Lock() defer a.mutex.Unlock() - return a.rate * float64(1e9) + return a.rate * float64(time.Second) } // Snapshot returns a read-only copy of the EWMA. @@ -98,7 +99,7 @@ func (a *StandardEWMA) Snapshot() EWMA { func (a *StandardEWMA) Tick() { count := atomic.LoadInt64(&a.uncounted) atomic.AddInt64(&a.uncounted, -count) - instantRate := float64(count) / float64(5e9) + instantRate := float64(count) / float64(5*time.Second) a.mutex.Lock() defer a.mutex.Unlock() if a.init { diff --git a/metrics/meter.go b/metrics/meter.go index 58d170fae027..7d2a2f530788 100644 --- a/metrics/meter.go +++ b/metrics/meter.go @@ -2,6 +2,7 @@ package metrics import ( "sync" + "sync/atomic" "time" ) @@ -101,6 +102,7 @@ func NewRegisteredMeterForced(name string, r Registry) Meter { // MeterSnapshot is a read-only copy of another Meter. type MeterSnapshot struct { count int64 + temp int64 rate1, rate5, rate15, rateMean float64 } @@ -149,7 +151,7 @@ func (NilMeter) Rate1() float64 { return 0.0 } // Rate5 is a no-op. func (NilMeter) Rate5() float64 { return 0.0 } -// Rate15is a no-op. +// Rate15 is a no-op. func (NilMeter) Rate15() float64 { return 0.0 } // RateMean is a no-op. @@ -167,7 +169,7 @@ type StandardMeter struct { snapshot *MeterSnapshot a1, a5, a15 EWMA startTime time.Time - stopped bool + stopped uint32 } func newStandardMeter() *StandardMeter { @@ -182,11 +184,8 @@ func newStandardMeter() *StandardMeter { // Stop stops the meter, Mark() will be a no-op if you use it after being stopped. func (m *StandardMeter) Stop() { - m.lock.Lock() - stopped := m.stopped - m.stopped = true - m.lock.Unlock() - if !stopped { + stopped := atomic.SwapUint32(&m.stopped, 1) + if stopped != 1 { arbiter.Lock() delete(arbiter.meters, m) arbiter.Unlock() @@ -194,57 +193,45 @@ func (m *StandardMeter) Stop() { } // Count returns the number of events recorded. +// It updates the meter to be as accurate as possible func (m *StandardMeter) Count() int64 { - m.lock.RLock() - count := m.snapshot.count - m.lock.RUnlock() - return count + m.lock.Lock() + defer m.lock.Unlock() + m.updateMeter() + return m.snapshot.count } // Mark records the occurrence of n events. func (m *StandardMeter) Mark(n int64) { - m.lock.Lock() - defer m.lock.Unlock() - if m.stopped { - return - } - m.snapshot.count += n - m.a1.Update(n) - m.a5.Update(n) - m.a15.Update(n) - m.updateSnapshot() + atomic.AddInt64(&m.snapshot.temp, n) } // Rate1 returns the one-minute moving average rate of events per second. func (m *StandardMeter) Rate1() float64 { m.lock.RLock() - rate1 := m.snapshot.rate1 - m.lock.RUnlock() - return rate1 + defer m.lock.RUnlock() + return m.snapshot.rate1 } // Rate5 returns the five-minute moving average rate of events per second. func (m *StandardMeter) Rate5() float64 { m.lock.RLock() - rate5 := m.snapshot.rate5 - m.lock.RUnlock() - return rate5 + defer m.lock.RUnlock() + return m.snapshot.rate5 } // Rate15 returns the fifteen-minute moving average rate of events per second. func (m *StandardMeter) Rate15() float64 { m.lock.RLock() - rate15 := m.snapshot.rate15 - m.lock.RUnlock() - return rate15 + defer m.lock.RUnlock() + return m.snapshot.rate15 } // RateMean returns the meter's mean rate of events per second. func (m *StandardMeter) RateMean() float64 { m.lock.RLock() - rateMean := m.snapshot.rateMean - m.lock.RUnlock() - return rateMean + defer m.lock.RUnlock() + return m.snapshot.rateMean } // Snapshot returns a read-only copy of the meter. @@ -264,9 +251,19 @@ func (m *StandardMeter) updateSnapshot() { snapshot.rateMean = float64(snapshot.count) / time.Since(m.startTime).Seconds() } +func (m *StandardMeter) updateMeter() { + // should only run with write lock held on m.lock + n := atomic.LoadInt64(&m.snapshot.temp) + m.snapshot.count += n + m.a1.Update(n) + m.a5.Update(n) + m.a15.Update(n) +} + func (m *StandardMeter) tick() { m.lock.Lock() defer m.lock.Unlock() + m.updateMeter() m.a1.Tick() m.a5.Tick() m.a15.Tick() @@ -282,7 +279,7 @@ type meterArbiter struct { ticker *time.Ticker } -var arbiter = meterArbiter{ticker: time.NewTicker(5e9), meters: make(map[*StandardMeter]struct{})} +var arbiter = meterArbiter{ticker: time.NewTicker(5 * time.Second), meters: make(map[*StandardMeter]struct{})} // Ticks meters on the scheduled interval func (ma *meterArbiter) tick() { diff --git a/metrics/meter_test.go b/metrics/meter_test.go index 37e45bded251..c44286a0356c 100644 --- a/metrics/meter_test.go +++ b/metrics/meter_test.go @@ -17,7 +17,7 @@ func TestGetOrRegisterMeter(t *testing.T) { r := NewRegistry() NewRegisteredMeter("foo", r).Mark(47) if m := GetOrRegisterMeter("foo", r); m.Count() != 47 { - t.Fatal(m) + t.Fatal(m.Count()) } } @@ -29,10 +29,11 @@ func TestMeterDecay(t *testing.T) { defer ma.ticker.Stop() m := newStandardMeter() ma.meters[m] = struct{}{} - go ma.tick() m.Mark(1) + ma.tickMeters() rateMean := m.RateMean() time.Sleep(100 * time.Millisecond) + ma.tickMeters() if m.RateMean() >= rateMean { t.Error("m.RateMean() didn't decrease") } From bf4b42a551b486524cc1c6a014f258c1773ee363 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 331/479] metrics: zero temp variable in updateMeter (#21470) * metrics: zero temp variable in updateMeter Previously the temp variable was not updated properly after summing it to count. This meant we had astronomically high metrics, now we zero out the temp whenever we sum it onto the snapshot count * metrics: move temp variable to be aligned, unit tests Moves the temp variable in MeterSnapshot to be 64-bit aligned because of the atomic bug. Adds a unit test, that catches the previous bug. --- metrics/meter.go | 8 ++++++-- metrics/meter_test.go | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/metrics/meter.go b/metrics/meter.go index 7d2a2f530788..60ae919d04db 100644 --- a/metrics/meter.go +++ b/metrics/meter.go @@ -101,8 +101,12 @@ func NewRegisteredMeterForced(name string, r Registry) Meter { // MeterSnapshot is a read-only copy of another Meter. type MeterSnapshot struct { - count int64 + // WARNING: The `temp` field is accessed atomically. + // On 32 bit platforms, only 64-bit aligned fields can be atomic. The struct is + // guaranteed to be so aligned, so take advantage of that. For more information, + // see https://golang.org/pkg/sync/atomic/#pkg-note-BUG. temp int64 + count int64 rate1, rate5, rate15, rateMean float64 } @@ -253,7 +257,7 @@ func (m *StandardMeter) updateSnapshot() { func (m *StandardMeter) updateMeter() { // should only run with write lock held on m.lock - n := atomic.LoadInt64(&m.snapshot.temp) + n := atomic.SwapInt64(&m.snapshot.temp, 0) m.snapshot.count += n m.a1.Update(n) m.a5.Update(n) diff --git a/metrics/meter_test.go b/metrics/meter_test.go index c44286a0356c..99bbd6d98937 100644 --- a/metrics/meter_test.go +++ b/metrics/meter_test.go @@ -73,3 +73,19 @@ func TestMeterZero(t *testing.T) { t.Errorf("m.Count(): 0 != %v\n", count) } } + +func TestMeterRepeat(t *testing.T) { + m := NewMeter() + for i := 0; i < 101; i++ { + m.Mark(int64(i)) + } + if count := m.Count(); count != 5050 { + t.Errorf("m.Count(): 5050 != %v\n", count) + } + for i := 0; i < 101; i++ { + m.Mark(int64(i)) + } + if count := m.Count(); count != 10100 { + t.Errorf("m.Count(): 10100 != %v\n", count) + } +} From d4f1b8a6dd1c0886e5dc94abdcebe2bb28e3f6bb Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 332/479] metrics: fix the panic for reading empty cpu stats (#21864) --- metrics/cpu_enabled.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/metrics/cpu_enabled.go b/metrics/cpu_enabled.go index 15476aa902fb..87a2620a5256 100644 --- a/metrics/cpu_enabled.go +++ b/metrics/cpu_enabled.go @@ -32,6 +32,10 @@ func ReadCPUStats(stats *CPUStats) { log.Error("Could not read cpu stats", "err", err) return } + if len(timeStats) == 0 { + log.Error("Empty cpu stats") + return + } // requesting all cpu times will always return an array with only one time stats entry timeStat := timeStats[0] stats.GlobalTime = int64((timeStat.User + timeStat.Nice + timeStat.System) * cpu.ClocksPerSec) From 9d082aa38cbf474bb0e75226ea05f8879c7b5dba Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 333/479] cmd/XDC: dump config for metrics (#22083) --- cmd/XDC/config.go | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/cmd/XDC/config.go b/cmd/XDC/config.go index d5e60c932180..f2ec5d3b83c2 100644 --- a/cmd/XDC/config.go +++ b/cmd/XDC/config.go @@ -32,6 +32,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/eth/ethconfig" "github.com/XinFinOrg/XDPoSChain/internal/flags" + "github.com/XinFinOrg/XDPoSChain/metrics" "github.com/XinFinOrg/XDPoSChain/node" "github.com/XinFinOrg/XDPoSChain/params" "github.com/naoina/toml" @@ -90,6 +91,7 @@ type XDCConfig struct { Eth ethconfig.Config Node node.Config Ethstats ethstatsConfig + Metrics metrics.Config XDCX XDCx.Config Account account StakeEnable bool @@ -122,12 +124,14 @@ func defaultNodeConfig() node.Config { return cfg } +// makeConfigNode loads geth configuration and creates a blank node instance. func makeConfigNode(ctx *cli.Context) (*node.Node, XDCConfig) { // Load defaults. cfg := XDCConfig{ Eth: ethconfig.Defaults, XDCX: XDCx.DefaultConfig, Node: defaultNodeConfig(), + Metrics: metrics.DefaultConfig, StakeEnable: true, Verbosity: 3, NAT: "", @@ -208,6 +212,9 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, XDCConfig) { } utils.SetXDCXConfig(ctx, &cfg.XDCX, cfg.Node.DataDir) + + applyMetricConfig(ctx, &cfg) + return stack, cfg } @@ -258,3 +265,36 @@ func dumpConfig(ctx *cli.Context) error { os.Stdout.Write(out) return nil } + +func applyMetricConfig(ctx *cli.Context, cfg *XDCConfig) { + if ctx.IsSet(utils.MetricsEnabledFlag.Name) { + cfg.Metrics.Enabled = ctx.Bool(utils.MetricsEnabledFlag.Name) + } + if ctx.IsSet(utils.MetricsEnabledExpensiveFlag.Name) { + cfg.Metrics.EnabledExpensive = ctx.Bool(utils.MetricsEnabledExpensiveFlag.Name) + } + if ctx.IsSet(utils.MetricsHTTPFlag.Name) { + cfg.Metrics.HTTP = ctx.String(utils.MetricsHTTPFlag.Name) + } + if ctx.IsSet(utils.MetricsPortFlag.Name) { + cfg.Metrics.Port = ctx.Int(utils.MetricsPortFlag.Name) + } + if ctx.IsSet(utils.MetricsEnableInfluxDBFlag.Name) { + cfg.Metrics.EnableInfluxDB = ctx.Bool(utils.MetricsEnableInfluxDBFlag.Name) + } + if ctx.IsSet(utils.MetricsInfluxDBEndpointFlag.Name) { + cfg.Metrics.InfluxDBEndpoint = ctx.String(utils.MetricsInfluxDBEndpointFlag.Name) + } + if ctx.IsSet(utils.MetricsInfluxDBDatabaseFlag.Name) { + cfg.Metrics.InfluxDBDatabase = ctx.String(utils.MetricsInfluxDBDatabaseFlag.Name) + } + if ctx.IsSet(utils.MetricsInfluxDBUsernameFlag.Name) { + cfg.Metrics.InfluxDBUsername = ctx.String(utils.MetricsInfluxDBUsernameFlag.Name) + } + if ctx.IsSet(utils.MetricsInfluxDBPasswordFlag.Name) { + cfg.Metrics.InfluxDBPassword = ctx.String(utils.MetricsInfluxDBPasswordFlag.Name) + } + if ctx.IsSet(utils.MetricsInfluxDBTagsFlag.Name) { + cfg.Metrics.InfluxDBTags = ctx.String(utils.MetricsInfluxDBTagsFlag.Name) + } +} From 1a844e457867103e7e47adbb3476dfd6d5aae75d Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 334/479] metrics: remove uneeded syntax (#21921) --- metrics/cpu_syscall.go | 3 ++- metrics/gauge_float64_test.go | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/metrics/cpu_syscall.go b/metrics/cpu_syscall.go index c111d89e7fcf..0a3897ea3de5 100644 --- a/metrics/cpu_syscall.go +++ b/metrics/cpu_syscall.go @@ -14,6 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . +//go:build !windows // +build !windows package metrics @@ -31,5 +32,5 @@ func getProcessCPUTime() int64 { log.Warn("Failed to retrieve CPU time", "err", err) return 0 } - return int64(usage.Utime.Sec+usage.Stime.Sec)*100 + int64(usage.Utime.Usec+usage.Stime.Usec)/10000 //nolint:unconvert + return (usage.Utime.Sec+usage.Stime.Sec)*100 + int64(usage.Utime.Usec+usage.Stime.Usec)/10000 //nolint:unconvert } diff --git a/metrics/gauge_float64_test.go b/metrics/gauge_float64_test.go index 3ee568e7ba09..ae088d4abf3b 100644 --- a/metrics/gauge_float64_test.go +++ b/metrics/gauge_float64_test.go @@ -12,27 +12,27 @@ func BenchmarkGuageFloat64(b *testing.B) { func TestGaugeFloat64(t *testing.T) { g := NewGaugeFloat64() - g.Update(float64(47.0)) - if v := g.Value(); float64(47.0) != v { + g.Update(47.0) + if v := g.Value(); v != 47.0 { t.Errorf("g.Value(): 47.0 != %v\n", v) } } func TestGaugeFloat64Snapshot(t *testing.T) { g := NewGaugeFloat64() - g.Update(float64(47.0)) + g.Update(47.0) snapshot := g.Snapshot() g.Update(float64(0)) - if v := snapshot.Value(); float64(47.0) != v { + if v := snapshot.Value(); v != 47.0 { t.Errorf("g.Value(): 47.0 != %v\n", v) } } func TestGetOrRegisterGaugeFloat64(t *testing.T) { r := NewRegistry() - NewRegisteredGaugeFloat64("foo", r).Update(float64(47.0)) + NewRegisteredGaugeFloat64("foo", r).Update(47.0) t.Logf("registry: %v", r) - if g := GetOrRegisterGaugeFloat64("foo", r); float64(47.0) != g.Value() { + if g := GetOrRegisterGaugeFloat64("foo", r); g.Value() != 47.0 { t.Fatal(g) } } From ec0ae4965dfd33a9c73798e2e2907c0a9eac3005 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 335/479] metrics: fix cast omission in cpu_syscall.go (#22262) fixes an regression which caused build failure on certain platforms --- metrics/cpu_syscall.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/cpu_syscall.go b/metrics/cpu_syscall.go index 0a3897ea3de5..89b38c9687d5 100644 --- a/metrics/cpu_syscall.go +++ b/metrics/cpu_syscall.go @@ -32,5 +32,5 @@ func getProcessCPUTime() int64 { log.Warn("Failed to retrieve CPU time", "err", err) return 0 } - return (usage.Utime.Sec+usage.Stime.Sec)*100 + int64(usage.Utime.Usec+usage.Stime.Usec)/10000 //nolint:unconvert + return int64(usage.Utime.Sec+usage.Stime.Sec)*100 + int64(usage.Utime.Usec+usage.Stime.Usec)/10000 //nolint:unconvert } From 5e9cb5d758a707a6157588a1d16218607346a9ae Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 336/479] metrics: add handler performance metrics (#22581) --- metrics/histogram.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/metrics/histogram.go b/metrics/histogram.go index 46f3bbd2f138..2c54ce8b4063 100644 --- a/metrics/histogram.go +++ b/metrics/histogram.go @@ -26,6 +26,15 @@ func GetOrRegisterHistogram(name string, r Registry, s Sample) Histogram { return r.GetOrRegister(name, func() Histogram { return NewHistogram(s) }).(Histogram) } +// GetOrRegisterHistogramLazy returns an existing Histogram or constructs and +// registers a new StandardHistogram. +func GetOrRegisterHistogramLazy(name string, r Registry, s func() Sample) Histogram { + if nil == r { + r = DefaultRegistry + } + return r.GetOrRegister(name, func() Histogram { return NewHistogram(s()) }).(Histogram) +} + // NewHistogram constructs a new StandardHistogram from a Sample. func NewHistogram(s Sample) Histogram { if !Enabled { From ebbcd608cc10144a34e1a6e29ec481b1a967c145 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 337/479] metrics: use resetting histograms for rare packets (#22586) --- metrics/resetting_sample.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 metrics/resetting_sample.go diff --git a/metrics/resetting_sample.go b/metrics/resetting_sample.go new file mode 100644 index 000000000000..43c1129cd0bc --- /dev/null +++ b/metrics/resetting_sample.go @@ -0,0 +1,24 @@ +package metrics + +// ResettingSample converts an ordinary sample into one that resets whenever its +// snapshot is retrieved. This will break for multi-monitor systems, but when only +// a single metric is being pushed out, this ensure that low-frequency events don't +// skew th charts indefinitely. +func ResettingSample(sample Sample) Sample { + return &resettingSample{ + Sample: sample, + } +} + +// resettingSample is a simple wrapper around a sample that resets it upon the +// snapshot retrieval. +type resettingSample struct { + Sample +} + +// Snapshot returns a read-only copy of the sample with the original reset. +func (rs *resettingSample) Snapshot() Sample { + s := rs.Sample.Snapshot() + rs.Sample.Clear() + return s +} From d16c72edbe618d452c8ee20532bbea7c289dfca1 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 338/479] metrics/influxdb: don't push empty histograms, no measurement != 0 (#22590) --- metrics/influxdb/influxdb.go | 43 +++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/metrics/influxdb/influxdb.go b/metrics/influxdb/influxdb.go index ac5e0fce3c4d..51cf088d6a48 100644 --- a/metrics/influxdb/influxdb.go +++ b/metrics/influxdb/influxdb.go @@ -137,26 +137,29 @@ func (r *reporter) send() error { }) case metrics.Histogram: ms := metric.Snapshot() - ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) - pts = append(pts, client.Point{ - Measurement: fmt.Sprintf("%s%s.histogram", namespace, name), - Tags: r.tags, - Fields: map[string]interface{}{ - "count": ms.Count(), - "max": ms.Max(), - "mean": ms.Mean(), - "min": ms.Min(), - "stddev": ms.StdDev(), - "variance": ms.Variance(), - "p50": ps[0], - "p75": ps[1], - "p95": ps[2], - "p99": ps[3], - "p999": ps[4], - "p9999": ps[5], - }, - Time: now, - }) + + if ms.Count() > 0 { + ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) + pts = append(pts, client.Point{ + Measurement: fmt.Sprintf("%s%s.histogram", namespace, name), + Tags: r.tags, + Fields: map[string]interface{}{ + "count": ms.Count(), + "max": ms.Max(), + "mean": ms.Mean(), + "min": ms.Min(), + "stddev": ms.StdDev(), + "variance": ms.Variance(), + "p50": ps[0], + "p75": ps[1], + "p95": ps[2], + "p99": ps[3], + "p999": ps[4], + "p9999": ps[5], + }, + Time: now, + }) + } case metrics.Meter: ms := metric.Snapshot() pts = append(pts, client.Point{ From 73b81dde780b06011bdbab823916778ee99fd25f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 339/479] metrics: use golang.org/x/sys/unix to support Solaris (#22584) Fixes #11113 Co-authored-by: rene <41963722+renaynay@users.noreply.github.com> --- metrics/cpu_syscall.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/cpu_syscall.go b/metrics/cpu_syscall.go index 89b38c9687d5..b3394464e5f5 100644 --- a/metrics/cpu_syscall.go +++ b/metrics/cpu_syscall.go @@ -20,7 +20,7 @@ package metrics import ( - "syscall" + syscall "golang.org/x/sys/unix" "github.com/XinFinOrg/XDPoSChain/log" ) From 29b72dbba62aa5305cf852f40b948b46cd3453d7 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 340/479] metrics/influxdb: support V2 (#23194) --- cmd/XDC/chaincmd.go | 4 + cmd/XDC/config.go | 12 ++ cmd/XDC/main.go | 4 + cmd/utils/flags.go | 65 +++++++++- go.mod | 5 + go.sum | 53 ++++++++ metrics/config.go | 11 ++ metrics/influxdb/influxdbv2.go | 214 +++++++++++++++++++++++++++++++++ 8 files changed, 363 insertions(+), 5 deletions(-) create mode 100644 metrics/influxdb/influxdbv2.go diff --git a/cmd/XDC/chaincmd.go b/cmd/XDC/chaincmd.go index 78a249ea1c4f..3ddcae6f145f 100644 --- a/cmd/XDC/chaincmd.go +++ b/cmd/XDC/chaincmd.go @@ -74,11 +74,15 @@ It expects the genesis file as argument.`, utils.MetricsEnabledFlag, utils.MetricsEnabledExpensiveFlag, utils.MetricsEnableInfluxDBFlag, + utils.MetricsEnableInfluxDBV2Flag, utils.MetricsInfluxDBEndpointFlag, utils.MetricsInfluxDBDatabaseFlag, utils.MetricsInfluxDBUsernameFlag, utils.MetricsInfluxDBPasswordFlag, utils.MetricsInfluxDBTagsFlag, + utils.MetricsInfluxDBTokenFlag, + utils.MetricsInfluxDBBucketFlag, + utils.MetricsInfluxDBOrganizationFlag, }, Description: ` The import command imports blocks from an RLP-encoded form. The form can be one file diff --git a/cmd/XDC/config.go b/cmd/XDC/config.go index f2ec5d3b83c2..511ba21715a6 100644 --- a/cmd/XDC/config.go +++ b/cmd/XDC/config.go @@ -297,4 +297,16 @@ func applyMetricConfig(ctx *cli.Context, cfg *XDCConfig) { if ctx.IsSet(utils.MetricsInfluxDBTagsFlag.Name) { cfg.Metrics.InfluxDBTags = ctx.String(utils.MetricsInfluxDBTagsFlag.Name) } + if ctx.IsSet(utils.MetricsEnableInfluxDBV2Flag.Name) { + cfg.Metrics.EnableInfluxDBV2 = ctx.Bool(utils.MetricsEnableInfluxDBV2Flag.Name) + } + if ctx.IsSet(utils.MetricsInfluxDBTokenFlag.Name) { + cfg.Metrics.InfluxDBToken = ctx.String(utils.MetricsInfluxDBTokenFlag.Name) + } + if ctx.IsSet(utils.MetricsInfluxDBBucketFlag.Name) { + cfg.Metrics.InfluxDBBucket = ctx.String(utils.MetricsInfluxDBBucketFlag.Name) + } + if ctx.IsSet(utils.MetricsInfluxDBOrganizationFlag.Name) { + cfg.Metrics.InfluxDBOrganization = ctx.String(utils.MetricsInfluxDBOrganizationFlag.Name) + } } diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go index fb01ba2f5f77..b11f3b976c63 100644 --- a/cmd/XDC/main.go +++ b/cmd/XDC/main.go @@ -172,6 +172,10 @@ var ( utils.MetricsInfluxDBUsernameFlag, utils.MetricsInfluxDBPasswordFlag, utils.MetricsInfluxDBTagsFlag, + utils.MetricsEnableInfluxDBV2Flag, + utils.MetricsInfluxDBTokenFlag, + utils.MetricsInfluxDBBucketFlag, + utils.MetricsInfluxDBOrganizationFlag, } ) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index b42914b2a866..797f578243bb 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -707,6 +707,30 @@ var ( Category: flags.MetricsCategory, } + MetricsEnableInfluxDBV2Flag = &cli.BoolFlag{ + Name: "metrics-influxdbv2", + Usage: "Enable metrics export/push to an external InfluxDB v2 database", + Category: flags.MetricsCategory, + } + MetricsInfluxDBTokenFlag = &cli.StringFlag{ + Name: "metrics-influxdb.token", + Usage: "Token to authorize access to the database (v2 only)", + Value: metrics.DefaultConfig.InfluxDBToken, + Category: flags.MetricsCategory, + } + MetricsInfluxDBBucketFlag = &cli.StringFlag{ + Name: "metrics-influxdb.bucket", + Usage: "InfluxDB bucket name to push reported metrics to (v2 only)", + Value: metrics.DefaultConfig.InfluxDBBucket, + Category: flags.MetricsCategory, + } + MetricsInfluxDBOrganizationFlag = &cli.StringFlag{ + Name: "metrics-influxdb.organization", + Usage: "InfluxDB organization name (v2 only)", + Value: metrics.DefaultConfig.InfluxDBOrganization, + Category: flags.MetricsCategory, + } + // MISC settings RollbackFlag = &cli.StringFlag{ Name: "rollback", @@ -1517,11 +1541,36 @@ func SetupMetrics(ctx *cli.Context) { if metrics.Enabled { log.Info("Enabling metrics collection") var ( - enableExport = ctx.Bool(MetricsEnableInfluxDBFlag.Name) - endpoint = ctx.String(MetricsInfluxDBEndpointFlag.Name) - database = ctx.String(MetricsInfluxDBDatabaseFlag.Name) - username = ctx.String(MetricsInfluxDBUsernameFlag.Name) - password = ctx.String(MetricsInfluxDBPasswordFlag.Name) + enableExport = ctx.Bool(MetricsEnableInfluxDBFlag.Name) + enableExportV2 = ctx.Bool(MetricsEnableInfluxDBV2Flag.Name) + ) + + if enableExport || enableExportV2 { + checkExclusive(ctx, MetricsEnableInfluxDBFlag, MetricsEnableInfluxDBV2Flag) + + v1FlagIsSet := ctx.IsSet(MetricsInfluxDBUsernameFlag.Name) || + ctx.IsSet(MetricsInfluxDBPasswordFlag.Name) + + v2FlagIsSet := ctx.IsSet(MetricsInfluxDBTokenFlag.Name) || + ctx.IsSet(MetricsInfluxDBOrganizationFlag.Name) || + ctx.IsSet(MetricsInfluxDBBucketFlag.Name) + + if enableExport && v2FlagIsSet { + Fatalf("Flags --influxdb.metrics.organization, --influxdb.metrics.token, --influxdb.metrics.bucket are only available for influxdb-v2") + } else if enableExportV2 && v1FlagIsSet { + Fatalf("Flags --influxdb.metrics.username, --influxdb.metrics.password are only available for influxdb-v1") + } + } + + var ( + endpoint = ctx.String(MetricsInfluxDBEndpointFlag.Name) + database = ctx.String(MetricsInfluxDBDatabaseFlag.Name) + username = ctx.String(MetricsInfluxDBUsernameFlag.Name) + password = ctx.String(MetricsInfluxDBPasswordFlag.Name) + + token = ctx.String(MetricsInfluxDBTokenFlag.Name) + bucket = ctx.String(MetricsInfluxDBBucketFlag.Name) + organization = ctx.String(MetricsInfluxDBOrganizationFlag.Name) ) if enableExport { @@ -1530,6 +1579,12 @@ func SetupMetrics(ctx *cli.Context) { tagsMap := SplitTagsFlag(ctx.String(MetricsInfluxDBTagsFlag.Name)) go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "xdc.", tagsMap) + } else if enableExportV2 { + log.Info("Enabling metrics export to InfluxDB (v2)") + + tagsMap := SplitTagsFlag(ctx.String(MetricsInfluxDBTagsFlag.Name)) + + go influxdb.InfluxDBV2WithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, token, bucket, organization, "xdc.", tagsMap) } if ctx.IsSet(MetricsHTTPFlag.Name) { diff --git a/go.mod b/go.mod index 60f4d9bc50a2..3160395d2a06 100644 --- a/go.mod +++ b/go.mod @@ -50,6 +50,7 @@ require ( github.com/deckarep/golang-set v1.8.0 github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 github.com/ethereum/c-kzg-4844 v0.4.0 + github.com/influxdata/influxdb-client-go/v2 v2.4.0 github.com/kylelemons/godebug v1.1.0 github.com/mattn/go-isatty v0.0.17 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible @@ -64,10 +65,12 @@ require ( github.com/consensys/bavard v0.1.13 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/deepmap/oapi-codegen v1.6.0 // indirect github.com/dlclark/regexp2 v1.10.0 // indirect github.com/go-ole/go-ole v1.2.5 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/google/uuid v1.3.0 // indirect + github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect @@ -83,9 +86,11 @@ require ( github.com/tklauser/numcpus v0.8.0 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.25.0 // indirect golang.org/x/term v0.26.0 // indirect golang.org/x/text v0.20.0 // indirect google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools v2.2.0+incompatible // indirect rsc.io/tmplfunc v0.0.3 // indirect diff --git a/go.sum b/go.sum index 4b74e864cb21..84d68e6c41ca 100644 --- a/go.sum +++ b/go.sum @@ -26,6 +26,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -35,6 +36,9 @@ github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/deepmap/oapi-codegen v1.6.0 h1:w/d1ntwh91XI0b/8ja7+u5SvA4IFfM0UNNLmiDR1gg0= +github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf h1:sh8rkQZavChcmakYiSlqu2425CHyFXLZZnvm7PDpU8M= @@ -50,10 +54,15 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -69,6 +78,7 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -79,6 +89,7 @@ github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= @@ -90,12 +101,17 @@ github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/influxdata/influxdb v1.7.9 h1:uSeBTNO4rBkbp1Be5FKRsAmglM9nlx25TzVQRQt1An4= github.com/influxdata/influxdb v1.7.9/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= +github.com/influxdata/influxdb-client-go/v2 v2.4.0 h1:HGBfZYStlx3Kqvsv1h2pJixbCl/jhnFtxpKFAv9Tu5k= +github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= +github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU= +github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/karalabe/hid v1.0.0 h1:+/CIMNXhSU/zIJgnIvBD2nKHxS/bnRHhhs9xBryLpPo= github.com/karalabe/hid v1.0.0/go.mod h1:Vr51f8rUOLYrfrWDFlV12GGQgM5AT8sVh+2fY4MPeu8= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -105,11 +121,21 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -142,6 +168,7 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -166,6 +193,8 @@ github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639 github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= @@ -178,52 +207,74 @@ github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYg github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE= github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -238,6 +289,7 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -249,6 +301,7 @@ gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 h1:a6cXbcDDUk gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/metrics/config.go b/metrics/config.go index 1150a007aaf7..cac1fc9c184a 100644 --- a/metrics/config.go +++ b/metrics/config.go @@ -28,6 +28,11 @@ type Config struct { InfluxDBUsername string `toml:",omitempty"` InfluxDBPassword string `toml:",omitempty"` InfluxDBTags string `toml:",omitempty"` + + EnableInfluxDBV2 bool `toml:",omitempty"` + InfluxDBToken string `toml:",omitempty"` + InfluxDBBucket string `toml:",omitempty"` + InfluxDBOrganization string `toml:",omitempty"` } // DefaultConfig is the default config for metrics used in go-ethereum. @@ -42,4 +47,10 @@ var DefaultConfig = Config{ InfluxDBUsername: "test", InfluxDBPassword: "test", InfluxDBTags: "host=localhost", + + // influxdbv2-specific flags + EnableInfluxDBV2: false, + InfluxDBToken: "test", + InfluxDBBucket: "xdc", + InfluxDBOrganization: "xdc", } diff --git a/metrics/influxdb/influxdbv2.go b/metrics/influxdb/influxdbv2.go new file mode 100644 index 000000000000..5dc2bff4d7b0 --- /dev/null +++ b/metrics/influxdb/influxdbv2.go @@ -0,0 +1,214 @@ +package influxdb + +import ( + "context" + "fmt" + "time" + + "github.com/XinFinOrg/XDPoSChain/log" + "github.com/XinFinOrg/XDPoSChain/metrics" + influxdb2 "github.com/influxdata/influxdb-client-go/v2" + "github.com/influxdata/influxdb-client-go/v2/api" +) + +type v2Reporter struct { + reg metrics.Registry + interval time.Duration + + endpoint string + token string + bucket string + organization string + namespace string + tags map[string]string + + client influxdb2.Client + write api.WriteAPI + + cache map[string]int64 +} + +// InfluxDBWithTags starts a InfluxDB reporter which will post the from the given metrics.Registry at each d interval with the specified tags +func InfluxDBV2WithTags(r metrics.Registry, d time.Duration, endpoint string, token string, bucket string, organization string, namespace string, tags map[string]string) { + rep := &v2Reporter{ + reg: r, + interval: d, + endpoint: endpoint, + token: token, + bucket: bucket, + organization: organization, + namespace: namespace, + tags: tags, + cache: make(map[string]int64), + } + + rep.client = influxdb2.NewClient(rep.endpoint, rep.token) + defer rep.client.Close() + + // async write client + rep.write = rep.client.WriteAPI(rep.organization, rep.bucket) + errorsCh := rep.write.Errors() + + // have to handle write errors in a separate goroutine like this b/c the channel is unbuffered and will block writes if not read + go func() { + for err := range errorsCh { + log.Warn("write error", "err", err.Error()) + } + }() + rep.run() +} + +func (r *v2Reporter) run() { + intervalTicker := time.Tick(r.interval) + pingTicker := time.Tick(time.Second * 5) + + for { + select { + case <-intervalTicker: + r.send() + case <-pingTicker: + _, err := r.client.Health(context.Background()) + if err != nil { + log.Warn("Got error from influxdb client health check", "err", err.Error()) + } + } + } +} + +func (r *v2Reporter) send() { + r.reg.Each(func(name string, i interface{}) { + now := time.Now() + namespace := r.namespace + + switch metric := i.(type) { + + case metrics.Counter: + v := metric.Count() + l := r.cache[name] + + measurement := fmt.Sprintf("%s%s.count", namespace, name) + fields := map[string]interface{}{ + "value": v - l, + } + + pt := influxdb2.NewPoint(measurement, r.tags, fields, now) + r.write.WritePoint(pt) + + r.cache[name] = v + + case metrics.Gauge: + ms := metric.Snapshot() + + measurement := fmt.Sprintf("%s%s.gauge", namespace, name) + fields := map[string]interface{}{ + "value": ms.Value(), + } + + pt := influxdb2.NewPoint(measurement, r.tags, fields, now) + r.write.WritePoint(pt) + + case metrics.GaugeFloat64: + ms := metric.Snapshot() + + measurement := fmt.Sprintf("%s%s.gauge", namespace, name) + fields := map[string]interface{}{ + "value": ms.Value(), + } + + pt := influxdb2.NewPoint(measurement, r.tags, fields, now) + r.write.WritePoint(pt) + + case metrics.Histogram: + ms := metric.Snapshot() + + if ms.Count() > 0 { + ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) + measurement := fmt.Sprintf("%s%s.histogram", namespace, name) + fields := map[string]interface{}{ + "count": ms.Count(), + "max": ms.Max(), + "mean": ms.Mean(), + "min": ms.Min(), + "stddev": ms.StdDev(), + "variance": ms.Variance(), + "p50": ps[0], + "p75": ps[1], + "p95": ps[2], + "p99": ps[3], + "p999": ps[4], + "p9999": ps[5], + } + + pt := influxdb2.NewPoint(measurement, r.tags, fields, now) + r.write.WritePoint(pt) + } + + case metrics.Meter: + ms := metric.Snapshot() + + measurement := fmt.Sprintf("%s%s.meter", namespace, name) + fields := map[string]interface{}{ + "count": ms.Count(), + "m1": ms.Rate1(), + "m5": ms.Rate5(), + "m15": ms.Rate15(), + "mean": ms.RateMean(), + } + + pt := influxdb2.NewPoint(measurement, r.tags, fields, now) + r.write.WritePoint(pt) + + case metrics.Timer: + ms := metric.Snapshot() + ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) + + measurement := fmt.Sprintf("%s%s.timer", namespace, name) + fields := map[string]interface{}{ + "count": ms.Count(), + "max": ms.Max(), + "mean": ms.Mean(), + "min": ms.Min(), + "stddev": ms.StdDev(), + "variance": ms.Variance(), + "p50": ps[0], + "p75": ps[1], + "p95": ps[2], + "p99": ps[3], + "p999": ps[4], + "p9999": ps[5], + "m1": ms.Rate1(), + "m5": ms.Rate5(), + "m15": ms.Rate15(), + "meanrate": ms.RateMean(), + } + + pt := influxdb2.NewPoint(measurement, r.tags, fields, now) + r.write.WritePoint(pt) + + case metrics.ResettingTimer: + t := metric.Snapshot() + + if len(t.Values()) > 0 { + ps := t.Percentiles([]float64{50, 95, 99}) + val := t.Values() + + measurement := fmt.Sprintf("%s%s.span", namespace, name) + fields := map[string]interface{}{ + "count": len(val), + "max": val[len(val)-1], + "mean": t.Mean(), + "min": val[0], + "p50": ps[0], + "p95": ps[1], + "p99": ps[2], + } + + pt := influxdb2.NewPoint(measurement, r.tags, fields, now) + r.write.WritePoint(pt) + } + } + }) + + // Force all unwritten data to be sent + r.write.Flush() +} From 98079104e4937bcb8db5bfe696437cef36061d39 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 341/479] metrics: fix compilation for GOOS=js (#23449) --- metrics/cpu_disabled.go | 2 +- metrics/cpu_enabled.go | 4 ++-- metrics/{cpu_windows.go => cputime_nop.go} | 2 ++ metrics/{cpu_syscall.go => cputime_unix.go} | 4 ++-- metrics/runtime_cgo.go | 3 +-- metrics/runtime_no_cgo.go | 2 +- 6 files changed, 9 insertions(+), 8 deletions(-) rename metrics/{cpu_windows.go => cputime_nop.go} (97%) rename metrics/{cpu_syscall.go => cputime_unix.go} (96%) diff --git a/metrics/cpu_disabled.go b/metrics/cpu_disabled.go index 6c3428993fb1..b0b483ee4bce 100644 --- a/metrics/cpu_disabled.go +++ b/metrics/cpu_disabled.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// +build ios +// +build ios js package metrics diff --git a/metrics/cpu_enabled.go b/metrics/cpu_enabled.go index 87a2620a5256..5d0e8485d7fa 100644 --- a/metrics/cpu_enabled.go +++ b/metrics/cpu_enabled.go @@ -14,8 +14,8 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -//go:build !ios -// +build !ios +//go:build !ios && !js +// +build !ios,!js package metrics diff --git a/metrics/cpu_windows.go b/metrics/cputime_nop.go similarity index 97% rename from metrics/cpu_windows.go rename to metrics/cputime_nop.go index fb29a52a82c1..1bf5537277fe 100644 --- a/metrics/cpu_windows.go +++ b/metrics/cputime_nop.go @@ -14,6 +14,8 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . +// +build windows js + package metrics // getProcessCPUTime returns 0 on Windows as there is no system call to resolve diff --git a/metrics/cpu_syscall.go b/metrics/cputime_unix.go similarity index 96% rename from metrics/cpu_syscall.go rename to metrics/cputime_unix.go index b3394464e5f5..50bb95b31e93 100644 --- a/metrics/cpu_syscall.go +++ b/metrics/cputime_unix.go @@ -14,8 +14,8 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -//go:build !windows -// +build !windows +//go:build !windows && !js +// +build !windows,!js package metrics diff --git a/metrics/runtime_cgo.go b/metrics/runtime_cgo.go index e3391f4e89fa..11722f76ccd5 100644 --- a/metrics/runtime_cgo.go +++ b/metrics/runtime_cgo.go @@ -1,5 +1,4 @@ -// +build cgo -// +build !appengine +// +build cgo,!appengine,!js package metrics diff --git a/metrics/runtime_no_cgo.go b/metrics/runtime_no_cgo.go index 616a3b4751be..e760af554f1a 100644 --- a/metrics/runtime_no_cgo.go +++ b/metrics/runtime_no_cgo.go @@ -1,4 +1,4 @@ -// +build !cgo appengine +// +build !cgo appengine js package metrics From 2e5b342826a51f0af3685567e914a9d881868b6d Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 342/479] metrics: add go:build lines (#23468) --- metrics/cpu_disabled.go | 1 + metrics/cputime_nop.go | 1 + metrics/runtime_cgo.go | 1 + metrics/runtime_gccpufraction.go | 1 + metrics/runtime_no_cgo.go | 1 + metrics/runtime_no_gccpufraction.go | 1 + metrics/syslog.go | 1 + 7 files changed, 7 insertions(+) diff --git a/metrics/cpu_disabled.go b/metrics/cpu_disabled.go index b0b483ee4bce..025d97aeb32a 100644 --- a/metrics/cpu_disabled.go +++ b/metrics/cpu_disabled.go @@ -14,6 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . +//go:build ios || js // +build ios js package metrics diff --git a/metrics/cputime_nop.go b/metrics/cputime_nop.go index 1bf5537277fe..0188735a7833 100644 --- a/metrics/cputime_nop.go +++ b/metrics/cputime_nop.go @@ -14,6 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . +//go:build windows || js // +build windows js package metrics diff --git a/metrics/runtime_cgo.go b/metrics/runtime_cgo.go index 11722f76ccd5..4307ebdba689 100644 --- a/metrics/runtime_cgo.go +++ b/metrics/runtime_cgo.go @@ -1,3 +1,4 @@ +//go:build cgo && !appengine && !js // +build cgo,!appengine,!js package metrics diff --git a/metrics/runtime_gccpufraction.go b/metrics/runtime_gccpufraction.go index ca12c05bac74..28cd44752b45 100644 --- a/metrics/runtime_gccpufraction.go +++ b/metrics/runtime_gccpufraction.go @@ -1,3 +1,4 @@ +//go:build go1.5 // +build go1.5 package metrics diff --git a/metrics/runtime_no_cgo.go b/metrics/runtime_no_cgo.go index e760af554f1a..1799bef63bfb 100644 --- a/metrics/runtime_no_cgo.go +++ b/metrics/runtime_no_cgo.go @@ -1,3 +1,4 @@ +//go:build !cgo || appengine || js // +build !cgo appengine js package metrics diff --git a/metrics/runtime_no_gccpufraction.go b/metrics/runtime_no_gccpufraction.go index be96aa6f1be9..af1a4b63c809 100644 --- a/metrics/runtime_no_gccpufraction.go +++ b/metrics/runtime_no_gccpufraction.go @@ -1,3 +1,4 @@ +//go:build !go1.5 // +build !go1.5 package metrics diff --git a/metrics/syslog.go b/metrics/syslog.go index a0ed4b1b2364..551a2bd0f072 100644 --- a/metrics/syslog.go +++ b/metrics/syslog.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package metrics From d77c1e5ea3c3c90fb6d5e89fd0de8fe6843037a4 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 343/479] metrics: replace strings.Replace with string.ReplaceAll (#24835) --- metrics/prometheus/collector.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/prometheus/collector.go b/metrics/prometheus/collector.go index 22f2d2f02a70..23934b212c23 100644 --- a/metrics/prometheus/collector.go +++ b/metrics/prometheus/collector.go @@ -116,5 +116,5 @@ func (c *collector) writeSummaryPercentile(name, p string, value interface{}) { } func mutateKey(key string) string { - return strings.Replace(key, "/", "_", -1) + return strings.ReplaceAll(key, "/", "_") } From 7b0a7e459331066af0618fc949086a6849eb70cc Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 344/479] metrics/influxdb: temp solution to present counter meaningfully (#24811) --- metrics/influxdb/influxdb.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/metrics/influxdb/influxdb.go b/metrics/influxdb/influxdb.go index 51cf088d6a48..d4ebea74fbe7 100644 --- a/metrics/influxdb/influxdb.go +++ b/metrics/influxdb/influxdb.go @@ -104,17 +104,15 @@ func (r *reporter) send() error { switch metric := i.(type) { case metrics.Counter: - v := metric.Count() - l := r.cache[name] + count := metric.Count() pts = append(pts, client.Point{ Measurement: fmt.Sprintf("%s%s.count", namespace, name), Tags: r.tags, Fields: map[string]interface{}{ - "value": v - l, + "value": count, }, Time: now, }) - r.cache[name] = v case metrics.Gauge: ms := metric.Snapshot() pts = append(pts, client.Point{ From 2e34afe4000e9c46f5620bbe039b2e084940a765 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 345/479] rpc: swap out timer metrics to histograms (#25044) --- rpc/handler.go | 4 ++-- rpc/metrics.go | 27 +++++++++++++++++++-------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/rpc/handler.go b/rpc/handler.go index 0e407abec900..777e47e7c88f 100644 --- a/rpc/handler.go +++ b/rpc/handler.go @@ -340,12 +340,12 @@ func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage if callb != h.unsubscribeCb { rpcRequestGauge.Inc(1) if answer.Error != nil { - failedReqeustGauge.Inc(1) + failedRequestGauge.Inc(1) } else { successfulRequestGauge.Inc(1) } rpcServingTimer.UpdateSince(start) - newRPCServingTimer(msg.Method, answer.Error == nil).UpdateSince(start) + updateServeTimeHistogram(msg.Method, answer.Error == nil, time.Since(start)) } return answer } diff --git a/rpc/metrics.go b/rpc/metrics.go index ebb407fa3dad..11f853dd2401 100644 --- a/rpc/metrics.go +++ b/rpc/metrics.go @@ -18,6 +18,7 @@ package rpc import ( "fmt" + "time" "github.com/XinFinOrg/XDPoSChain/metrics" ) @@ -25,15 +26,25 @@ import ( var ( rpcRequestGauge = metrics.NewRegisteredGauge("rpc/requests", nil) successfulRequestGauge = metrics.NewRegisteredGauge("rpc/success", nil) - failedReqeustGauge = metrics.NewRegisteredGauge("rpc/failure", nil) - rpcServingTimer = metrics.NewRegisteredTimer("rpc/duration/all", nil) + failedRequestGauge = metrics.NewRegisteredGauge("rpc/failure", nil) + + // serveTimeHistName is the prefix of the per-request serving time histograms. + serveTimeHistName = "rpc/duration" + + rpcServingTimer = metrics.NewRegisteredTimer("rpc/duration/all", nil) ) -func newRPCServingTimer(method string, valid bool) metrics.Timer { - flag := "success" - if !valid { - flag = "failure" +// updateServeTimeHistogram tracks the serving time of a remote RPC call. +func updateServeTimeHistogram(method string, success bool, elapsed time.Duration) { + note := "success" + if !success { + note = "failure" + } + h := fmt.Sprintf("%s/%s/%s", serveTimeHistName, method, note) + sampler := func() metrics.Sample { + return metrics.ResettingSample( + metrics.NewExpDecaySample(1028, 0.015), + ) } - m := fmt.Sprintf("rpc/duration/%s/%s", method, flag) - return metrics.GetOrRegisterTimer(m, nil) + metrics.GetOrRegisterHistogramLazy(h, nil, sampler).Update(elapsed.Microseconds()) } From 392b44c948ad4e3dd23274fe2d3627ab841f8b1e Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 346/479] metrics/influxdb: replace time.Tick with time.NewTicker (#24783) --- metrics/influxdb/influxdbv2.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/metrics/influxdb/influxdbv2.go b/metrics/influxdb/influxdbv2.go index 5dc2bff4d7b0..583a1ebc78e0 100644 --- a/metrics/influxdb/influxdbv2.go +++ b/metrics/influxdb/influxdbv2.go @@ -59,14 +59,14 @@ func InfluxDBV2WithTags(r metrics.Registry, d time.Duration, endpoint string, to } func (r *v2Reporter) run() { - intervalTicker := time.Tick(r.interval) - pingTicker := time.Tick(time.Second * 5) + intervalTicker := time.NewTicker(r.interval) + pingTicker := time.NewTicker(time.Second * 5) for { select { - case <-intervalTicker: + case <-intervalTicker.C: r.send() - case <-pingTicker: + case <-pingTicker.C: _, err := r.client.Health(context.Background()) if err != nil { log.Warn("Got error from influxdb client health check", "err", err.Error()) From e8452c94a7f73538b5e7ee7ec4b65255b613cae3 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:12 +0800 Subject: [PATCH 347/479] metrics: fix some typos (#25551) --- metrics/gauge_float64_test.go | 2 +- metrics/gauge_test.go | 2 +- metrics/prometheus/prometheus.go | 2 +- miner/unconfirmed_test.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/metrics/gauge_float64_test.go b/metrics/gauge_float64_test.go index ae088d4abf3b..1ae598049229 100644 --- a/metrics/gauge_float64_test.go +++ b/metrics/gauge_float64_test.go @@ -2,7 +2,7 @@ package metrics import "testing" -func BenchmarkGuageFloat64(b *testing.B) { +func BenchmarkGaugeFloat64(b *testing.B) { g := NewGaugeFloat64() b.ResetTimer() for i := 0; i < b.N; i++ { diff --git a/metrics/gauge_test.go b/metrics/gauge_test.go index 3aee143455c3..a98fe985d8c2 100644 --- a/metrics/gauge_test.go +++ b/metrics/gauge_test.go @@ -5,7 +5,7 @@ import ( "testing" ) -func BenchmarkGuage(b *testing.B) { +func BenchmarkGauge(b *testing.B) { g := NewGauge() b.ResetTimer() for i := 0; i < b.N; i++ { diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go index d34c2206f951..b367e6c3aa59 100644 --- a/metrics/prometheus/prometheus.go +++ b/metrics/prometheus/prometheus.go @@ -36,7 +36,7 @@ func Handler(reg metrics.Registry) http.Handler { }) sort.Strings(names) - // Aggregate all the metris into a Prometheus collector + // Aggregate all the metrics into a Prometheus collector c := newCollector() for _, name := range names { diff --git a/miner/unconfirmed_test.go b/miner/unconfirmed_test.go index 71a0dde931a5..781ba0636651 100644 --- a/miner/unconfirmed_test.go +++ b/miner/unconfirmed_test.go @@ -72,7 +72,7 @@ func TestUnconfirmedShifts(t *testing.T) { if n := pool.blocks.Len(); n != int(limit)/2 { t.Errorf("unconfirmed count mismatch: have %d, want %d", n, limit/2) } - // Try to shift all the remaining blocks out and verify emptyness + // Try to shift all the remaining blocks out and verify emptiness pool.Shift(start + 2*uint64(limit)) if n := pool.blocks.Len(); n != 0 { t.Errorf("unconfirmed count mismatch: have %d, want %d", n, 0) From dddd6c57cdc32a6474c819506d62054d13cc7cfa Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 348/479] metrics: improve reading Go runtime metrics (#25886) This changes how we read performance metrics from the Go runtime. Instead of using runtime.ReadMemStats, we now rely on the API provided by package runtime/metrics. runtime/metrics provides more accurate information. For example, the new interface has better reporting of memory use. In my testing, the reported value of held memory more accurately reflects the usage reported by the OS. The semantics of metrics system/memory/allocs and system/memory/frees have changed to report amounts in bytes. ReadMemStats only reported the count of allocations in number-of-objects. This is imprecise: 'tiny objects' are not counted because the runtime allocates them in batches; and certain improvements in allocation behavior, such as struct size optimizations, will be less visible when the number of allocs doesn't change. Changing allocation reports to be in bytes makes it appear in graphs that lots more is being allocated. I don't think that's a problem because this metric is primarily interesting for geth developers. The metric system/memory/pauses has been changed to report statistical values from the histogram provided by the runtime. Its name in influxdb has changed from geth.system/memory/pauses.meter to geth.system/memory/pauses.histogram. We also have a new histogram metric, system/cpu/schedlatency, reporting the Go scheduler latency. --- metrics/influxdb/influxdb.go | 35 +-- metrics/metrics.go | 176 ++++++++++----- metrics/metrics_test.go | 32 +-- metrics/runtime.go | 212 ------------------ metrics/runtime_cgo.go | 10 - metrics/runtime_gccpufraction.go | 10 - metrics/runtime_no_cgo.go | 8 - metrics/runtime_no_gccpufraction.go | 10 - metrics/runtime_test.go | 88 -------- metrics/runtimehistogram.go | 319 ++++++++++++++++++++++++++++ metrics/runtimehistogram_test.go | 133 ++++++++++++ 11 files changed, 602 insertions(+), 431 deletions(-) delete mode 100644 metrics/runtime.go delete mode 100644 metrics/runtime_cgo.go delete mode 100644 metrics/runtime_gccpufraction.go delete mode 100644 metrics/runtime_no_cgo.go delete mode 100644 metrics/runtime_no_gccpufraction.go delete mode 100644 metrics/runtime_test.go create mode 100644 metrics/runtimehistogram.go create mode 100644 metrics/runtimehistogram_test.go diff --git a/metrics/influxdb/influxdb.go b/metrics/influxdb/influxdb.go index d4ebea74fbe7..56f19e944abf 100644 --- a/metrics/influxdb/influxdb.go +++ b/metrics/influxdb/influxdb.go @@ -135,27 +135,28 @@ func (r *reporter) send() error { }) case metrics.Histogram: ms := metric.Snapshot() - if ms.Count() > 0 { - ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) + ps := ms.Percentiles([]float64{0.25, 0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) + fields := map[string]interface{}{ + "count": ms.Count(), + "max": ms.Max(), + "mean": ms.Mean(), + "min": ms.Min(), + "stddev": ms.StdDev(), + "variance": ms.Variance(), + "p25": ps[0], + "p50": ps[1], + "p75": ps[2], + "p95": ps[3], + "p99": ps[4], + "p999": ps[5], + "p9999": ps[6], + } pts = append(pts, client.Point{ Measurement: fmt.Sprintf("%s%s.histogram", namespace, name), Tags: r.tags, - Fields: map[string]interface{}{ - "count": ms.Count(), - "max": ms.Max(), - "mean": ms.Mean(), - "min": ms.Min(), - "stddev": ms.StdDev(), - "variance": ms.Variance(), - "p50": ps[0], - "p75": ps[1], - "p95": ps[2], - "p99": ps[3], - "p999": ps[4], - "p9999": ps[5], - }, - Time: now, + Fields: fields, + Time: now, }) } case metrics.Meter: diff --git a/metrics/metrics.go b/metrics/metrics.go index 2df2404b5f60..2b8bad8bee36 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -7,7 +7,8 @@ package metrics import ( "os" - "runtime" + "runtime/metrics" + "runtime/pprof" "strings" "time" @@ -54,38 +55,106 @@ func init() { } } -// CollectProcessMetrics periodically collects various metrics about the running -// process. +var threadCreateProfile = pprof.Lookup("threadcreate") + +type runtimeStats struct { + GCPauses *metrics.Float64Histogram + GCAllocBytes uint64 + GCFreedBytes uint64 + + MemTotal uint64 + HeapObjects uint64 + HeapFree uint64 + HeapReleased uint64 + HeapUnused uint64 + + Goroutines uint64 + SchedLatency *metrics.Float64Histogram +} + +var runtimeSamples = []metrics.Sample{ + {Name: "/gc/pauses:seconds"}, // histogram + {Name: "/gc/heap/allocs:bytes"}, + {Name: "/gc/heap/frees:bytes"}, + {Name: "/memory/classes/total:bytes"}, + {Name: "/memory/classes/heap/objects:bytes"}, + {Name: "/memory/classes/heap/free:bytes"}, + {Name: "/memory/classes/heap/released:bytes"}, + {Name: "/memory/classes/heap/unused:bytes"}, + {Name: "/sched/goroutines:goroutines"}, + {Name: "/sched/latencies:seconds"}, // histogram +} + +func readRuntimeStats(v *runtimeStats) { + metrics.Read(runtimeSamples) + for _, s := range runtimeSamples { + // Skip invalid/unknown metrics. This is needed because some metrics + // are unavailable in older Go versions, and attempting to read a 'bad' + // metric panics. + if s.Value.Kind() == metrics.KindBad { + continue + } + + switch s.Name { + case "/gc/pauses:seconds": + v.GCPauses = s.Value.Float64Histogram() + case "/gc/heap/allocs:bytes": + v.GCAllocBytes = s.Value.Uint64() + case "/gc/heap/frees:bytes": + v.GCFreedBytes = s.Value.Uint64() + case "/memory/classes/total:bytes": + v.MemTotal = s.Value.Uint64() + case "/memory/classes/heap/objects:bytes": + v.HeapObjects = s.Value.Uint64() + case "/memory/classes/heap/free:bytes": + v.HeapFree = s.Value.Uint64() + case "/memory/classes/heap/released:bytes": + v.HeapReleased = s.Value.Uint64() + case "/memory/classes/heap/unused:bytes": + v.HeapUnused = s.Value.Uint64() + case "/sched/goroutines:goroutines": + v.Goroutines = s.Value.Uint64() + case "/sched/latencies:seconds": + v.SchedLatency = s.Value.Float64Histogram() + } + } +} + +// CollectProcessMetrics periodically collects various metrics about the running process. func CollectProcessMetrics(refresh time.Duration) { // Short circuit if the metrics system is disabled if !Enabled { return } + refreshFreq := int64(refresh / time.Second) // Create the various data collectors - cpuStats := make([]*CPUStats, 2) - memstats := make([]*runtime.MemStats, 2) - diskstats := make([]*DiskStats, 2) - for i := 0; i < len(memstats); i++ { - cpuStats[i] = new(CPUStats) - memstats[i] = new(runtime.MemStats) - diskstats[i] = new(DiskStats) - } - // Define the various metrics to collect var ( - cpuSysLoad = GetOrRegisterGauge("system/cpu/sysload", DefaultRegistry) - cpuSysWait = GetOrRegisterGauge("system/cpu/syswait", DefaultRegistry) - cpuProcLoad = GetOrRegisterGauge("system/cpu/procload", DefaultRegistry) - cpuThreads = GetOrRegisterGauge("system/cpu/threads", DefaultRegistry) - cpuGoroutines = GetOrRegisterGauge("system/cpu/goroutines", DefaultRegistry) - - memPauses = GetOrRegisterMeter("system/memory/pauses", DefaultRegistry) - memAllocs = GetOrRegisterMeter("system/memory/allocs", DefaultRegistry) - memFrees = GetOrRegisterMeter("system/memory/frees", DefaultRegistry) - memHeld = GetOrRegisterGauge("system/memory/held", DefaultRegistry) - memUsed = GetOrRegisterGauge("system/memory/used", DefaultRegistry) + cpustats = make([]CPUStats, 2) + diskstats = make([]DiskStats, 2) + rstats = make([]runtimeStats, 2) + ) + + // This scale factor is used for the runtime's time metrics. It's useful to convert to + // ns here because the runtime gives times in float seconds, but runtimeHistogram can + // only provide integers for the minimum and maximum values. + const secondsToNs = float64(time.Second) + // Define the various metrics to collect + var ( + cpuSysLoad = GetOrRegisterGauge("system/cpu/sysload", DefaultRegistry) + cpuSysWait = GetOrRegisterGauge("system/cpu/syswait", DefaultRegistry) + cpuProcLoad = GetOrRegisterGauge("system/cpu/procload", DefaultRegistry) + cpuThreads = GetOrRegisterGauge("system/cpu/threads", DefaultRegistry) + cpuGoroutines = GetOrRegisterGauge("system/cpu/goroutines", DefaultRegistry) + cpuSchedLatency = getOrRegisterRuntimeHistogram("system/cpu/schedlatency", secondsToNs, nil) + memPauses = getOrRegisterRuntimeHistogram("system/memory/pauses", secondsToNs, nil) + memAllocs = GetOrRegisterMeter("system/memory/allocs", DefaultRegistry) + memFrees = GetOrRegisterMeter("system/memory/frees", DefaultRegistry) + memTotal = GetOrRegisterGauge("system/memory/held", DefaultRegistry) + heapUsed = GetOrRegisterGauge("system/memory/used", DefaultRegistry) + heapObjects = GetOrRegisterGauge("system/memory/objects", DefaultRegistry) diskReads = GetOrRegisterMeter("system/disk/readcount", DefaultRegistry) diskReadBytes = GetOrRegisterMeter("system/disk/readdata", DefaultRegistry) diskReadBytesCounter = GetOrRegisterCounter("system/disk/readbytes", DefaultRegistry) @@ -93,34 +162,43 @@ func CollectProcessMetrics(refresh time.Duration) { diskWriteBytes = GetOrRegisterMeter("system/disk/writedata", DefaultRegistry) diskWriteBytesCounter = GetOrRegisterCounter("system/disk/writebytes", DefaultRegistry) ) - // Iterate loading the different stats and updating the meters - for i := 1; ; i++ { - location1 := i % 2 - location2 := (i - 1) % 2 - - ReadCPUStats(cpuStats[location1]) - cpuSysLoad.Update((cpuStats[location1].GlobalTime - cpuStats[location2].GlobalTime) / refreshFreq) - cpuSysWait.Update((cpuStats[location1].GlobalWait - cpuStats[location2].GlobalWait) / refreshFreq) - cpuProcLoad.Update((cpuStats[location1].LocalTime - cpuStats[location2].LocalTime) / refreshFreq) + + // Iterate loading the different stats and updating the meters. + now, prev := 0, 1 + for ; ; now, prev = prev, now { + // CPU + ReadCPUStats(&cpustats[now]) + cpuSysLoad.Update((cpustats[now].GlobalTime - cpustats[prev].GlobalTime) / refreshFreq) + cpuSysWait.Update((cpustats[now].GlobalWait - cpustats[prev].GlobalWait) / refreshFreq) + cpuProcLoad.Update((cpustats[now].LocalTime - cpustats[prev].LocalTime) / refreshFreq) + + // Threads cpuThreads.Update(int64(threadCreateProfile.Count())) - cpuGoroutines.Update(int64(runtime.NumGoroutine())) - - runtime.ReadMemStats(memstats[location1]) - memPauses.Mark(int64(memstats[location1].PauseTotalNs - memstats[location2].PauseTotalNs)) - memAllocs.Mark(int64(memstats[location1].Mallocs - memstats[location2].Mallocs)) - memFrees.Mark(int64(memstats[location1].Frees - memstats[location2].Frees)) - memHeld.Update(int64(memstats[location1].HeapSys - memstats[location1].HeapReleased)) - memUsed.Update(int64(memstats[location1].Alloc)) - - if ReadDiskStats(diskstats[location1]) == nil { - diskReads.Mark(diskstats[location1].ReadCount - diskstats[location2].ReadCount) - diskReadBytes.Mark(diskstats[location1].ReadBytes - diskstats[location2].ReadBytes) - diskWrites.Mark(diskstats[location1].WriteCount - diskstats[location2].WriteCount) - diskWriteBytes.Mark(diskstats[location1].WriteBytes - diskstats[location2].WriteBytes) - - diskReadBytesCounter.Inc(diskstats[location1].ReadBytes - diskstats[location2].ReadBytes) - diskWriteBytesCounter.Inc(diskstats[location1].WriteBytes - diskstats[location2].WriteBytes) + + // Go runtime metrics + readRuntimeStats(&rstats[now]) + + cpuGoroutines.Update(int64(rstats[now].Goroutines)) + cpuSchedLatency.update(rstats[now].SchedLatency) + memPauses.update(rstats[now].GCPauses) + + memAllocs.Mark(int64(rstats[now].GCAllocBytes - rstats[prev].GCAllocBytes)) + memFrees.Mark(int64(rstats[now].GCFreedBytes - rstats[prev].GCFreedBytes)) + + memTotal.Update(int64(rstats[now].MemTotal)) + heapUsed.Update(int64(rstats[now].MemTotal - rstats[now].HeapUnused - rstats[now].HeapFree - rstats[now].HeapReleased)) + heapObjects.Update(int64(rstats[now].HeapObjects)) + + // Disk + if ReadDiskStats(&diskstats[now]) == nil { + diskReads.Mark(diskstats[now].ReadCount - diskstats[prev].ReadCount) + diskReadBytes.Mark(diskstats[now].ReadBytes - diskstats[prev].ReadBytes) + diskWrites.Mark(diskstats[now].WriteCount - diskstats[prev].WriteCount) + diskWriteBytes.Mark(diskstats[now].WriteBytes - diskstats[prev].WriteBytes) + diskReadBytesCounter.Inc(diskstats[now].ReadBytes - diskstats[prev].ReadBytes) + diskWriteBytesCounter.Inc(diskstats[now].WriteBytes - diskstats[prev].WriteBytes) } + time.Sleep(refresh) } } diff --git a/metrics/metrics_test.go b/metrics/metrics_test.go index 029c99870eba..e3fde1ea62ce 100644 --- a/metrics/metrics_test.go +++ b/metrics/metrics_test.go @@ -2,8 +2,6 @@ package metrics import ( "fmt" - "io" - "log" "sync" "testing" "time" @@ -11,11 +9,11 @@ import ( const FANOUT = 128 -// Stop the compiler from complaining during debugging. -var ( - _ = io.Discard - _ = log.LstdFlags -) +func TestReadRuntimeValues(t *testing.T) { + var v runtimeStats + readRuntimeStats(&v) + t.Logf("%+v", v) +} func BenchmarkMetrics(b *testing.B) { r := NewRegistry() @@ -26,7 +24,6 @@ func BenchmarkMetrics(b *testing.B) { m := NewRegisteredMeter("meter", r) t := NewRegisteredTimer("timer", r) RegisterDebugGCStats(r) - RegisterRuntimeMemStats(r) b.ResetTimer() ch := make(chan bool) @@ -48,24 +45,6 @@ func BenchmarkMetrics(b *testing.B) { }() //*/ - wgR := &sync.WaitGroup{} - //* - wgR.Add(1) - go func() { - defer wgR.Done() - //log.Println("go CaptureRuntimeMemStats") - for { - select { - case <-ch: - //log.Println("done CaptureRuntimeMemStats") - return - default: - CaptureRuntimeMemStatsOnce(r) - } - } - }() - //*/ - wgW := &sync.WaitGroup{} /* wgW.Add(1) @@ -104,7 +83,6 @@ func BenchmarkMetrics(b *testing.B) { wg.Wait() close(ch) wgD.Wait() - wgR.Wait() wgW.Wait() } diff --git a/metrics/runtime.go b/metrics/runtime.go deleted file mode 100644 index 9450c479bad7..000000000000 --- a/metrics/runtime.go +++ /dev/null @@ -1,212 +0,0 @@ -package metrics - -import ( - "runtime" - "runtime/pprof" - "time" -) - -var ( - memStats runtime.MemStats - runtimeMetrics struct { - MemStats struct { - Alloc Gauge - BuckHashSys Gauge - DebugGC Gauge - EnableGC Gauge - Frees Gauge - HeapAlloc Gauge - HeapIdle Gauge - HeapInuse Gauge - HeapObjects Gauge - HeapReleased Gauge - HeapSys Gauge - LastGC Gauge - Lookups Gauge - Mallocs Gauge - MCacheInuse Gauge - MCacheSys Gauge - MSpanInuse Gauge - MSpanSys Gauge - NextGC Gauge - NumGC Gauge - GCCPUFraction GaugeFloat64 - PauseNs Histogram - PauseTotalNs Gauge - StackInuse Gauge - StackSys Gauge - Sys Gauge - TotalAlloc Gauge - } - NumCgoCall Gauge - NumGoroutine Gauge - NumThread Gauge - ReadMemStats Timer - } - frees uint64 - lookups uint64 - mallocs uint64 - numGC uint32 - numCgoCalls int64 - - threadCreateProfile = pprof.Lookup("threadcreate") -) - -// Capture new values for the Go runtime statistics exported in -// runtime.MemStats. This is designed to be called as a goroutine. -func CaptureRuntimeMemStats(r Registry, d time.Duration) { - for range time.Tick(d) { - CaptureRuntimeMemStatsOnce(r) - } -} - -// Capture new values for the Go runtime statistics exported in -// runtime.MemStats. This is designed to be called in a background -// goroutine. Giving a registry which has not been given to -// RegisterRuntimeMemStats will panic. -// -// Be very careful with this because runtime.ReadMemStats calls the C -// functions runtime·semacquire(&runtime·worldsema) and runtime·stoptheworld() -// and that last one does what it says on the tin. -func CaptureRuntimeMemStatsOnce(r Registry) { - t := time.Now() - runtime.ReadMemStats(&memStats) // This takes 50-200us. - runtimeMetrics.ReadMemStats.UpdateSince(t) - - runtimeMetrics.MemStats.Alloc.Update(int64(memStats.Alloc)) - runtimeMetrics.MemStats.BuckHashSys.Update(int64(memStats.BuckHashSys)) - if memStats.DebugGC { - runtimeMetrics.MemStats.DebugGC.Update(1) - } else { - runtimeMetrics.MemStats.DebugGC.Update(0) - } - if memStats.EnableGC { - runtimeMetrics.MemStats.EnableGC.Update(1) - } else { - runtimeMetrics.MemStats.EnableGC.Update(0) - } - - runtimeMetrics.MemStats.Frees.Update(int64(memStats.Frees - frees)) - runtimeMetrics.MemStats.HeapAlloc.Update(int64(memStats.HeapAlloc)) - runtimeMetrics.MemStats.HeapIdle.Update(int64(memStats.HeapIdle)) - runtimeMetrics.MemStats.HeapInuse.Update(int64(memStats.HeapInuse)) - runtimeMetrics.MemStats.HeapObjects.Update(int64(memStats.HeapObjects)) - runtimeMetrics.MemStats.HeapReleased.Update(int64(memStats.HeapReleased)) - runtimeMetrics.MemStats.HeapSys.Update(int64(memStats.HeapSys)) - runtimeMetrics.MemStats.LastGC.Update(int64(memStats.LastGC)) - runtimeMetrics.MemStats.Lookups.Update(int64(memStats.Lookups - lookups)) - runtimeMetrics.MemStats.Mallocs.Update(int64(memStats.Mallocs - mallocs)) - runtimeMetrics.MemStats.MCacheInuse.Update(int64(memStats.MCacheInuse)) - runtimeMetrics.MemStats.MCacheSys.Update(int64(memStats.MCacheSys)) - runtimeMetrics.MemStats.MSpanInuse.Update(int64(memStats.MSpanInuse)) - runtimeMetrics.MemStats.MSpanSys.Update(int64(memStats.MSpanSys)) - runtimeMetrics.MemStats.NextGC.Update(int64(memStats.NextGC)) - runtimeMetrics.MemStats.NumGC.Update(int64(memStats.NumGC - numGC)) - runtimeMetrics.MemStats.GCCPUFraction.Update(gcCPUFraction(&memStats)) - - // - i := numGC % uint32(len(memStats.PauseNs)) - ii := memStats.NumGC % uint32(len(memStats.PauseNs)) - if memStats.NumGC-numGC >= uint32(len(memStats.PauseNs)) { - for i = 0; i < uint32(len(memStats.PauseNs)); i++ { - runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i])) - } - } else { - if i > ii { - for ; i < uint32(len(memStats.PauseNs)); i++ { - runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i])) - } - i = 0 - } - for ; i < ii; i++ { - runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i])) - } - } - frees = memStats.Frees - lookups = memStats.Lookups - mallocs = memStats.Mallocs - numGC = memStats.NumGC - - runtimeMetrics.MemStats.PauseTotalNs.Update(int64(memStats.PauseTotalNs)) - runtimeMetrics.MemStats.StackInuse.Update(int64(memStats.StackInuse)) - runtimeMetrics.MemStats.StackSys.Update(int64(memStats.StackSys)) - runtimeMetrics.MemStats.Sys.Update(int64(memStats.Sys)) - runtimeMetrics.MemStats.TotalAlloc.Update(int64(memStats.TotalAlloc)) - - currentNumCgoCalls := numCgoCall() - runtimeMetrics.NumCgoCall.Update(currentNumCgoCalls - numCgoCalls) - numCgoCalls = currentNumCgoCalls - - runtimeMetrics.NumGoroutine.Update(int64(runtime.NumGoroutine())) - - runtimeMetrics.NumThread.Update(int64(threadCreateProfile.Count())) -} - -// Register runtimeMetrics for the Go runtime statistics exported in runtime and -// specifically runtime.MemStats. The runtimeMetrics are named by their -// fully-qualified Go symbols, i.e. runtime.MemStats.Alloc. -func RegisterRuntimeMemStats(r Registry) { - runtimeMetrics.MemStats.Alloc = NewGauge() - runtimeMetrics.MemStats.BuckHashSys = NewGauge() - runtimeMetrics.MemStats.DebugGC = NewGauge() - runtimeMetrics.MemStats.EnableGC = NewGauge() - runtimeMetrics.MemStats.Frees = NewGauge() - runtimeMetrics.MemStats.HeapAlloc = NewGauge() - runtimeMetrics.MemStats.HeapIdle = NewGauge() - runtimeMetrics.MemStats.HeapInuse = NewGauge() - runtimeMetrics.MemStats.HeapObjects = NewGauge() - runtimeMetrics.MemStats.HeapReleased = NewGauge() - runtimeMetrics.MemStats.HeapSys = NewGauge() - runtimeMetrics.MemStats.LastGC = NewGauge() - runtimeMetrics.MemStats.Lookups = NewGauge() - runtimeMetrics.MemStats.Mallocs = NewGauge() - runtimeMetrics.MemStats.MCacheInuse = NewGauge() - runtimeMetrics.MemStats.MCacheSys = NewGauge() - runtimeMetrics.MemStats.MSpanInuse = NewGauge() - runtimeMetrics.MemStats.MSpanSys = NewGauge() - runtimeMetrics.MemStats.NextGC = NewGauge() - runtimeMetrics.MemStats.NumGC = NewGauge() - runtimeMetrics.MemStats.GCCPUFraction = NewGaugeFloat64() - runtimeMetrics.MemStats.PauseNs = NewHistogram(NewExpDecaySample(1028, 0.015)) - runtimeMetrics.MemStats.PauseTotalNs = NewGauge() - runtimeMetrics.MemStats.StackInuse = NewGauge() - runtimeMetrics.MemStats.StackSys = NewGauge() - runtimeMetrics.MemStats.Sys = NewGauge() - runtimeMetrics.MemStats.TotalAlloc = NewGauge() - runtimeMetrics.NumCgoCall = NewGauge() - runtimeMetrics.NumGoroutine = NewGauge() - runtimeMetrics.NumThread = NewGauge() - runtimeMetrics.ReadMemStats = NewTimer() - - r.Register("runtime.MemStats.Alloc", runtimeMetrics.MemStats.Alloc) - r.Register("runtime.MemStats.BuckHashSys", runtimeMetrics.MemStats.BuckHashSys) - r.Register("runtime.MemStats.DebugGC", runtimeMetrics.MemStats.DebugGC) - r.Register("runtime.MemStats.EnableGC", runtimeMetrics.MemStats.EnableGC) - r.Register("runtime.MemStats.Frees", runtimeMetrics.MemStats.Frees) - r.Register("runtime.MemStats.HeapAlloc", runtimeMetrics.MemStats.HeapAlloc) - r.Register("runtime.MemStats.HeapIdle", runtimeMetrics.MemStats.HeapIdle) - r.Register("runtime.MemStats.HeapInuse", runtimeMetrics.MemStats.HeapInuse) - r.Register("runtime.MemStats.HeapObjects", runtimeMetrics.MemStats.HeapObjects) - r.Register("runtime.MemStats.HeapReleased", runtimeMetrics.MemStats.HeapReleased) - r.Register("runtime.MemStats.HeapSys", runtimeMetrics.MemStats.HeapSys) - r.Register("runtime.MemStats.LastGC", runtimeMetrics.MemStats.LastGC) - r.Register("runtime.MemStats.Lookups", runtimeMetrics.MemStats.Lookups) - r.Register("runtime.MemStats.Mallocs", runtimeMetrics.MemStats.Mallocs) - r.Register("runtime.MemStats.MCacheInuse", runtimeMetrics.MemStats.MCacheInuse) - r.Register("runtime.MemStats.MCacheSys", runtimeMetrics.MemStats.MCacheSys) - r.Register("runtime.MemStats.MSpanInuse", runtimeMetrics.MemStats.MSpanInuse) - r.Register("runtime.MemStats.MSpanSys", runtimeMetrics.MemStats.MSpanSys) - r.Register("runtime.MemStats.NextGC", runtimeMetrics.MemStats.NextGC) - r.Register("runtime.MemStats.NumGC", runtimeMetrics.MemStats.NumGC) - r.Register("runtime.MemStats.GCCPUFraction", runtimeMetrics.MemStats.GCCPUFraction) - r.Register("runtime.MemStats.PauseNs", runtimeMetrics.MemStats.PauseNs) - r.Register("runtime.MemStats.PauseTotalNs", runtimeMetrics.MemStats.PauseTotalNs) - r.Register("runtime.MemStats.StackInuse", runtimeMetrics.MemStats.StackInuse) - r.Register("runtime.MemStats.StackSys", runtimeMetrics.MemStats.StackSys) - r.Register("runtime.MemStats.Sys", runtimeMetrics.MemStats.Sys) - r.Register("runtime.MemStats.TotalAlloc", runtimeMetrics.MemStats.TotalAlloc) - r.Register("runtime.NumCgoCall", runtimeMetrics.NumCgoCall) - r.Register("runtime.NumGoroutine", runtimeMetrics.NumGoroutine) - r.Register("runtime.NumThread", runtimeMetrics.NumThread) - r.Register("runtime.ReadMemStats", runtimeMetrics.ReadMemStats) -} diff --git a/metrics/runtime_cgo.go b/metrics/runtime_cgo.go deleted file mode 100644 index 4307ebdba689..000000000000 --- a/metrics/runtime_cgo.go +++ /dev/null @@ -1,10 +0,0 @@ -//go:build cgo && !appengine && !js -// +build cgo,!appengine,!js - -package metrics - -import "runtime" - -func numCgoCall() int64 { - return runtime.NumCgoCall() -} diff --git a/metrics/runtime_gccpufraction.go b/metrics/runtime_gccpufraction.go deleted file mode 100644 index 28cd44752b45..000000000000 --- a/metrics/runtime_gccpufraction.go +++ /dev/null @@ -1,10 +0,0 @@ -//go:build go1.5 -// +build go1.5 - -package metrics - -import "runtime" - -func gcCPUFraction(memStats *runtime.MemStats) float64 { - return memStats.GCCPUFraction -} diff --git a/metrics/runtime_no_cgo.go b/metrics/runtime_no_cgo.go deleted file mode 100644 index 1799bef63bfb..000000000000 --- a/metrics/runtime_no_cgo.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build !cgo || appengine || js -// +build !cgo appengine js - -package metrics - -func numCgoCall() int64 { - return 0 -} diff --git a/metrics/runtime_no_gccpufraction.go b/metrics/runtime_no_gccpufraction.go deleted file mode 100644 index af1a4b63c809..000000000000 --- a/metrics/runtime_no_gccpufraction.go +++ /dev/null @@ -1,10 +0,0 @@ -//go:build !go1.5 -// +build !go1.5 - -package metrics - -import "runtime" - -func gcCPUFraction(memStats *runtime.MemStats) float64 { - return 0 -} diff --git a/metrics/runtime_test.go b/metrics/runtime_test.go deleted file mode 100644 index f85f7868f71a..000000000000 --- a/metrics/runtime_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package metrics - -import ( - "runtime" - "testing" - "time" -) - -func BenchmarkRuntimeMemStats(b *testing.B) { - r := NewRegistry() - RegisterRuntimeMemStats(r) - b.ResetTimer() - for i := 0; i < b.N; i++ { - CaptureRuntimeMemStatsOnce(r) - } -} - -func TestRuntimeMemStats(t *testing.T) { - r := NewRegistry() - RegisterRuntimeMemStats(r) - CaptureRuntimeMemStatsOnce(r) - zero := runtimeMetrics.MemStats.PauseNs.Count() // Get a "zero" since GC may have run before these tests. - runtime.GC() - CaptureRuntimeMemStatsOnce(r) - if count := runtimeMetrics.MemStats.PauseNs.Count(); count-zero != 1 { - t.Fatal(count - zero) - } - runtime.GC() - runtime.GC() - CaptureRuntimeMemStatsOnce(r) - if count := runtimeMetrics.MemStats.PauseNs.Count(); count-zero != 3 { - t.Fatal(count - zero) - } - for i := 0; i < 256; i++ { - runtime.GC() - } - CaptureRuntimeMemStatsOnce(r) - if count := runtimeMetrics.MemStats.PauseNs.Count(); count-zero != 259 { - t.Fatal(count - zero) - } - for i := 0; i < 257; i++ { - runtime.GC() - } - CaptureRuntimeMemStatsOnce(r) - if count := runtimeMetrics.MemStats.PauseNs.Count(); count-zero != 515 { // We lost one because there were too many GCs between captures. - t.Fatal(count - zero) - } -} - -func TestRuntimeMemStatsNumThread(t *testing.T) { - r := NewRegistry() - RegisterRuntimeMemStats(r) - CaptureRuntimeMemStatsOnce(r) - - if value := runtimeMetrics.NumThread.Value(); value < 1 { - t.Fatalf("got NumThread: %d, wanted at least 1", value) - } -} - -func TestRuntimeMemStatsBlocking(t *testing.T) { - if g := runtime.GOMAXPROCS(0); g < 2 { - t.Skipf("skipping TestRuntimeMemStatsBlocking with GOMAXPROCS=%d\n", g) - } - ch := make(chan int) - go testRuntimeMemStatsBlocking(ch) - var memStats runtime.MemStats - t0 := time.Now() - runtime.ReadMemStats(&memStats) - t1 := time.Now() - t.Log("i++ during runtime.ReadMemStats:", <-ch) - go testRuntimeMemStatsBlocking(ch) - d := t1.Sub(t0) - t.Log(d) - time.Sleep(d) - t.Log("i++ during time.Sleep:", <-ch) -} - -func testRuntimeMemStatsBlocking(ch chan int) { - i := 0 - for { - select { - case ch <- i: - return - default: - i++ - } - } -} diff --git a/metrics/runtimehistogram.go b/metrics/runtimehistogram.go new file mode 100644 index 000000000000..c68939af1ef7 --- /dev/null +++ b/metrics/runtimehistogram.go @@ -0,0 +1,319 @@ +package metrics + +import ( + "math" + "runtime/metrics" + "sort" + "sync/atomic" +) + +func getOrRegisterRuntimeHistogram(name string, scale float64, r Registry) *runtimeHistogram { + if r == nil { + r = DefaultRegistry + } + constructor := func() Histogram { return newRuntimeHistogram(scale) } + return r.GetOrRegister(name, constructor).(*runtimeHistogram) +} + +// runtimeHistogram wraps a runtime/metrics histogram. +type runtimeHistogram struct { + v atomic.Value + scaleFactor float64 +} + +func newRuntimeHistogram(scale float64) *runtimeHistogram { + h := &runtimeHistogram{scaleFactor: scale} + h.update(&metrics.Float64Histogram{}) + return h +} + +func (h *runtimeHistogram) update(mh *metrics.Float64Histogram) { + if mh == nil { + // The update value can be nil if the current Go version doesn't support a + // requested metric. It's just easier to handle nil here than putting + // conditionals everywhere. + return + } + + s := runtimeHistogramSnapshot{ + Counts: make([]uint64, len(mh.Counts)), + Buckets: make([]float64, len(mh.Buckets)), + } + copy(s.Counts, mh.Counts) + copy(s.Buckets, mh.Buckets) + for i, b := range s.Buckets { + s.Buckets[i] = b * h.scaleFactor + } + h.v.Store(&s) +} + +func (h *runtimeHistogram) load() *runtimeHistogramSnapshot { + return h.v.Load().(*runtimeHistogramSnapshot) +} + +func (h *runtimeHistogram) Clear() { + panic("runtimeHistogram does not support Clear") +} +func (h *runtimeHistogram) Update(int64) { + panic("runtimeHistogram does not support Update") +} +func (h *runtimeHistogram) Sample() Sample { + return NilSample{} +} + +// Snapshot returns a non-changing cop of the histogram. +func (h *runtimeHistogram) Snapshot() Histogram { + return h.load() +} + +// Count returns the sample count. +func (h *runtimeHistogram) Count() int64 { + return h.load().Count() +} + +// Mean returns an approximation of the mean. +func (h *runtimeHistogram) Mean() float64 { + return h.load().Mean() +} + +// StdDev approximates the standard deviation of the histogram. +func (h *runtimeHistogram) StdDev() float64 { + return h.load().StdDev() +} + +// Variance approximates the variance of the histogram. +func (h *runtimeHistogram) Variance() float64 { + return h.load().Variance() +} + +// Percentile computes the p'th percentile value. +func (h *runtimeHistogram) Percentile(p float64) float64 { + return h.load().Percentile(p) +} + +// Percentiles computes all requested percentile values. +func (h *runtimeHistogram) Percentiles(ps []float64) []float64 { + return h.load().Percentiles(ps) +} + +// Max returns the highest sample value. +func (h *runtimeHistogram) Max() int64 { + return h.load().Max() +} + +// Min returns the lowest sample value. +func (h *runtimeHistogram) Min() int64 { + return h.load().Min() +} + +// Sum returns the sum of all sample values. +func (h *runtimeHistogram) Sum() int64 { + return h.load().Sum() +} + +type runtimeHistogramSnapshot metrics.Float64Histogram + +func (h *runtimeHistogramSnapshot) Clear() { + panic("runtimeHistogram does not support Clear") +} +func (h *runtimeHistogramSnapshot) Update(int64) { + panic("runtimeHistogram does not support Update") +} +func (h *runtimeHistogramSnapshot) Sample() Sample { + return NilSample{} +} + +func (h *runtimeHistogramSnapshot) Snapshot() Histogram { + return h +} + +// Count returns the sample count. +func (h *runtimeHistogramSnapshot) Count() int64 { + var count int64 + for _, c := range h.Counts { + count += int64(c) + } + return count +} + +// Mean returns an approximation of the mean. +func (h *runtimeHistogramSnapshot) Mean() float64 { + if len(h.Counts) == 0 { + return 0 + } + mean, _ := h.mean() + return mean +} + +// mean computes the mean and also the total sample count. +func (h *runtimeHistogramSnapshot) mean() (mean, totalCount float64) { + var sum float64 + for i, c := range h.Counts { + midpoint := h.midpoint(i) + sum += midpoint * float64(c) + totalCount += float64(c) + } + return sum / totalCount, totalCount +} + +func (h *runtimeHistogramSnapshot) midpoint(bucket int) float64 { + high := h.Buckets[bucket+1] + low := h.Buckets[bucket] + if math.IsInf(high, 1) { + // The edge of the highest bucket can be +Inf, and it's supposed to mean that this + // bucket contains all remaining samples > low. We can't get the middle of an + // infinite range, so just return the lower bound of this bucket instead. + return low + } + if math.IsInf(low, -1) { + // Similarly, we can get -Inf in the left edge of the lowest bucket, + // and it means the bucket contains all remaining values < high. + return high + } + return (low + high) / 2 +} + +// StdDev approximates the standard deviation of the histogram. +func (h *runtimeHistogramSnapshot) StdDev() float64 { + return math.Sqrt(h.Variance()) +} + +// Variance approximates the variance of the histogram. +func (h *runtimeHistogramSnapshot) Variance() float64 { + if len(h.Counts) == 0 { + return 0 + } + + mean, totalCount := h.mean() + if totalCount <= 1 { + // There is no variance when there are zero or one items. + return 0 + } + + var sum float64 + for i, c := range h.Counts { + midpoint := h.midpoint(i) + d := midpoint - mean + sum += float64(c) * (d * d) + } + return sum / (totalCount - 1) +} + +// Percentile computes the p'th percentile value. +func (h *runtimeHistogramSnapshot) Percentile(p float64) float64 { + threshold := float64(h.Count()) * p + values := [1]float64{threshold} + h.computePercentiles(values[:]) + return values[0] +} + +// Percentiles computes all requested percentile values. +func (h *runtimeHistogramSnapshot) Percentiles(ps []float64) []float64 { + // Compute threshold values. We need these to be sorted + // for the percentile computation, but restore the original + // order later, so keep the indexes as well. + count := float64(h.Count()) + thresholds := make([]float64, len(ps)) + indexes := make([]int, len(ps)) + for i, percentile := range ps { + thresholds[i] = count * math.Max(0, math.Min(1.0, percentile)) + indexes[i] = i + } + sort.Sort(floatsAscendingKeepingIndex{thresholds, indexes}) + + // Now compute. The result is stored back into the thresholds slice. + h.computePercentiles(thresholds) + + // Put the result back into the requested order. + sort.Sort(floatsByIndex{thresholds, indexes}) + return thresholds +} + +func (h *runtimeHistogramSnapshot) computePercentiles(thresh []float64) { + var totalCount float64 + for i, count := range h.Counts { + totalCount += float64(count) + + for len(thresh) > 0 && thresh[0] < totalCount { + thresh[0] = h.Buckets[i] + thresh = thresh[1:] + } + if len(thresh) == 0 { + return + } + } +} + +// Note: runtime/metrics.Float64Histogram is a collection of float64s, but the methods +// below need to return int64 to satisfy the interface. The histogram provided by runtime +// also doesn't keep track of individual samples, so results are approximated. + +// Max returns the highest sample value. +func (h *runtimeHistogramSnapshot) Max() int64 { + for i := len(h.Counts) - 1; i >= 0; i-- { + count := h.Counts[i] + if count > 0 { + edge := h.Buckets[i+1] + if math.IsInf(edge, 1) { + edge = h.Buckets[i] + } + return int64(math.Ceil(edge)) + } + } + return 0 +} + +// Min returns the lowest sample value. +func (h *runtimeHistogramSnapshot) Min() int64 { + for i, count := range h.Counts { + if count > 0 { + return int64(math.Floor(h.Buckets[i])) + } + } + return 0 +} + +// Sum returns the sum of all sample values. +func (h *runtimeHistogramSnapshot) Sum() int64 { + var sum float64 + for i := range h.Counts { + sum += h.Buckets[i] * float64(h.Counts[i]) + } + return int64(math.Ceil(sum)) +} + +type floatsAscendingKeepingIndex struct { + values []float64 + indexes []int +} + +func (s floatsAscendingKeepingIndex) Len() int { + return len(s.values) +} + +func (s floatsAscendingKeepingIndex) Less(i, j int) bool { + return s.values[i] < s.values[j] +} + +func (s floatsAscendingKeepingIndex) Swap(i, j int) { + s.values[i], s.values[j] = s.values[j], s.values[i] + s.indexes[i], s.indexes[j] = s.indexes[j], s.indexes[i] +} + +type floatsByIndex struct { + values []float64 + indexes []int +} + +func (s floatsByIndex) Len() int { + return len(s.values) +} + +func (s floatsByIndex) Less(i, j int) bool { + return s.indexes[i] < s.indexes[j] +} + +func (s floatsByIndex) Swap(i, j int) { + s.values[i], s.values[j] = s.values[j], s.values[i] + s.indexes[i], s.indexes[j] = s.indexes[j], s.indexes[i] +} diff --git a/metrics/runtimehistogram_test.go b/metrics/runtimehistogram_test.go new file mode 100644 index 000000000000..d53a01438311 --- /dev/null +++ b/metrics/runtimehistogram_test.go @@ -0,0 +1,133 @@ +package metrics + +import ( + "fmt" + "math" + "reflect" + "runtime/metrics" + "testing" +) + +var _ Histogram = (*runtimeHistogram)(nil) + +type runtimeHistogramTest struct { + h metrics.Float64Histogram + + Count int64 + Min int64 + Max int64 + Sum int64 + Mean float64 + Variance float64 + StdDev float64 + Percentiles []float64 // .5 .8 .9 .99 .995 +} + +// This test checks the results of statistical functions implemented +// by runtimeHistogramSnapshot. +func TestRuntimeHistogramStats(t *testing.T) { + tests := []runtimeHistogramTest{ + 0: { + h: metrics.Float64Histogram{ + Counts: []uint64{}, + Buckets: []float64{}, + }, + Count: 0, + Max: 0, + Min: 0, + Sum: 0, + Mean: 0, + Variance: 0, + StdDev: 0, + Percentiles: []float64{0, 0, 0, 0, 0}, + }, + 1: { + // This checks the case where the highest bucket is +Inf. + h: metrics.Float64Histogram{ + Counts: []uint64{0, 1, 2}, + Buckets: []float64{0, 0.5, 1, math.Inf(1)}, + }, + Count: 3, + Max: 1, + Min: 0, + Sum: 3, + Mean: 0.9166666, + Percentiles: []float64{1, 1, 1, 1, 1}, + Variance: 0.020833, + StdDev: 0.144433, + }, + 2: { + h: metrics.Float64Histogram{ + Counts: []uint64{8, 6, 3, 1}, + Buckets: []float64{12, 16, 18, 24, 25}, + }, + Count: 18, + Max: 25, + Min: 12, + Sum: 270, + Mean: 16.75, + Variance: 10.3015, + StdDev: 3.2096, + Percentiles: []float64{16, 18, 18, 24, 24}, + }, + } + + for i, test := range tests { + t.Run(fmt.Sprint(i), func(t *testing.T) { + s := runtimeHistogramSnapshot(test.h) + + if v := s.Count(); v != test.Count { + t.Errorf("Count() = %v, want %v", v, test.Count) + } + if v := s.Min(); v != test.Min { + t.Errorf("Min() = %v, want %v", v, test.Min) + } + if v := s.Max(); v != test.Max { + t.Errorf("Max() = %v, want %v", v, test.Max) + } + if v := s.Sum(); v != test.Sum { + t.Errorf("Sum() = %v, want %v", v, test.Sum) + } + if v := s.Mean(); !approxEqual(v, test.Mean, 0.0001) { + t.Errorf("Mean() = %v, want %v", v, test.Mean) + } + if v := s.Variance(); !approxEqual(v, test.Variance, 0.0001) { + t.Errorf("Variance() = %v, want %v", v, test.Variance) + } + if v := s.StdDev(); !approxEqual(v, test.StdDev, 0.0001) { + t.Errorf("StdDev() = %v, want %v", v, test.StdDev) + } + ps := []float64{.5, .8, .9, .99, .995} + if v := s.Percentiles(ps); !reflect.DeepEqual(v, test.Percentiles) { + t.Errorf("Percentiles(%v) = %v, want %v", ps, v, test.Percentiles) + } + }) + } +} + +func approxEqual(x, y, ε float64) bool { + if math.IsInf(x, -1) && math.IsInf(y, -1) { + return true + } + if math.IsInf(x, 1) && math.IsInf(y, 1) { + return true + } + if math.IsNaN(x) && math.IsNaN(y) { + return true + } + return math.Abs(x-y) < ε +} + +// This test verifies that requesting Percentiles in unsorted order +// returns them in the requested order. +func TestRuntimeHistogramStatsPercentileOrder(t *testing.T) { + p := runtimeHistogramSnapshot{ + Counts: []uint64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + Buckets: []float64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + } + result := p.Percentiles([]float64{1, 0.2, 0.5, 0.1, 0.2}) + expected := []float64{10, 2, 5, 1, 2} + if !reflect.DeepEqual(result, expected) { + t.Fatal("wrong result:", result) + } +} From 40f47a641ba6cf02933f78c9e5a20356a5191748 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 349/479] metrics/influxdb: fix time ticker leaks (#26507) --- metrics/influxdb/influxdbv2.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/metrics/influxdb/influxdbv2.go b/metrics/influxdb/influxdbv2.go index 583a1ebc78e0..4fa0d02bcdca 100644 --- a/metrics/influxdb/influxdbv2.go +++ b/metrics/influxdb/influxdbv2.go @@ -62,6 +62,9 @@ func (r *v2Reporter) run() { intervalTicker := time.NewTicker(r.interval) pingTicker := time.NewTicker(time.Second * 5) + defer intervalTicker.Stop() + defer pingTicker.Stop() + for { select { case <-intervalTicker.C: From 76320b4b9807d1e33f20e20e58bbedc982ab3602 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 350/479] metrics/librato: use http package to replace http method names (#26535) --- metrics/librato/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/librato/client.go b/metrics/librato/client.go index a807c392af01..89ecb9ec64da 100644 --- a/metrics/librato/client.go +++ b/metrics/librato/client.go @@ -80,7 +80,7 @@ func (lc *LibratoClient) PostMetrics(batch Batch) (err error) { return } - if req, err = http.NewRequest("POST", MetricsPostUrl, bytes.NewBuffer(js)); err != nil { + if req, err = http.NewRequest(http.MethodPost, MetricsPostUrl, bytes.NewBuffer(js)); err != nil { return } From 6199c84050dff2ca21a070a2dba92d5635138fd0 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 351/479] metrics: remove deprecated uses of math.rand (#26710) --- metrics/sample.go | 29 +++++++++++++++++++++++++++-- metrics/sample_test.go | 19 +++++-------------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/metrics/sample.go b/metrics/sample.go index fa2bfb274e39..afcaa2118426 100644 --- a/metrics/sample.go +++ b/metrics/sample.go @@ -41,6 +41,7 @@ type ExpDecaySample struct { reservoirSize int t0, t1 time.Time values *expDecaySampleHeap + rand *rand.Rand } // NewExpDecaySample constructs a new exponentially-decaying sample with the @@ -59,6 +60,12 @@ func NewExpDecaySample(reservoirSize int, alpha float64) Sample { return s } +// SetRand sets the random source (useful in tests) +func (s *ExpDecaySample) SetRand(prng *rand.Rand) Sample { + s.rand = prng + return s +} + // Clear clears all samples. func (s *ExpDecaySample) Clear() { s.mutex.Lock() @@ -168,8 +175,14 @@ func (s *ExpDecaySample) update(t time.Time, v int64) { if s.values.Size() == s.reservoirSize { s.values.Pop() } + var f64 float64 + if s.rand != nil { + f64 = s.rand.Float64() + } else { + f64 = rand.Float64() + } s.values.Push(expDecaySample{ - k: math.Exp(t.Sub(s.t0).Seconds()*s.alpha) / rand.Float64(), + k: math.Exp(t.Sub(s.t0).Seconds()*s.alpha) / f64, v: v, }) if t.After(s.t1) { @@ -402,6 +415,7 @@ type UniformSample struct { mutex sync.Mutex reservoirSize int values []int64 + rand *rand.Rand } // NewUniformSample constructs a new uniform sample with the given reservoir @@ -416,6 +430,12 @@ func NewUniformSample(reservoirSize int) Sample { } } +// SetRand sets the random source (useful in tests) +func (s *UniformSample) SetRand(prng *rand.Rand) Sample { + s.rand = prng + return s +} + // Clear clears all samples. func (s *UniformSample) Clear() { s.mutex.Lock() @@ -511,7 +531,12 @@ func (s *UniformSample) Update(v int64) { if len(s.values) < s.reservoirSize { s.values = append(s.values, v) } else { - r := rand.Int63n(s.count) + var r int64 + if s.rand != nil { + r = s.rand.Int63n(s.count) + } else { + r = rand.Int63n(s.count) + } if r < int64(len(s.values)) { s.values[int(r)] = v } diff --git a/metrics/sample_test.go b/metrics/sample_test.go index 5b2ee96e3679..3ae128d56f67 100644 --- a/metrics/sample_test.go +++ b/metrics/sample_test.go @@ -80,7 +80,6 @@ func BenchmarkUniformSample1028(b *testing.B) { } func TestExpDecaySample10(t *testing.T) { - rand.Seed(1) s := NewExpDecaySample(100, 0.99) for i := 0; i < 10; i++ { s.Update(int64(i)) @@ -102,7 +101,6 @@ func TestExpDecaySample10(t *testing.T) { } func TestExpDecaySample100(t *testing.T) { - rand.Seed(1) s := NewExpDecaySample(1000, 0.01) for i := 0; i < 100; i++ { s.Update(int64(i)) @@ -124,7 +122,6 @@ func TestExpDecaySample100(t *testing.T) { } func TestExpDecaySample1000(t *testing.T) { - rand.Seed(1) s := NewExpDecaySample(100, 0.99) for i := 0; i < 1000; i++ { s.Update(int64(i)) @@ -150,7 +147,6 @@ func TestExpDecaySample1000(t *testing.T) { // The priority becomes +Inf quickly after starting if this is done, // effectively freezing the set of samples until a rescale step happens. func TestExpDecaySampleNanosecondRegression(t *testing.T) { - rand.Seed(1) s := NewExpDecaySample(100, 0.99) for i := 0; i < 100; i++ { s.Update(10) @@ -183,8 +179,7 @@ func TestExpDecaySampleRescale(t *testing.T) { func TestExpDecaySampleSnapshot(t *testing.T) { now := time.Now() - rand.Seed(1) - s := NewExpDecaySample(100, 0.99) + s := NewExpDecaySample(100, 0.99).(*ExpDecaySample).SetRand(rand.New(rand.NewSource(1))) for i := 1; i <= 10000; i++ { s.(*ExpDecaySample).update(now.Add(time.Duration(i)), int64(i)) } @@ -195,8 +190,7 @@ func TestExpDecaySampleSnapshot(t *testing.T) { func TestExpDecaySampleStatistics(t *testing.T) { now := time.Now() - rand.Seed(1) - s := NewExpDecaySample(100, 0.99) + s := NewExpDecaySample(100, 0.99).(*ExpDecaySample).SetRand(rand.New(rand.NewSource(1))) for i := 1; i <= 10000; i++ { s.(*ExpDecaySample).update(now.Add(time.Duration(i)), int64(i)) } @@ -204,7 +198,6 @@ func TestExpDecaySampleStatistics(t *testing.T) { } func TestUniformSample(t *testing.T) { - rand.Seed(1) s := NewUniformSample(100) for i := 0; i < 1000; i++ { s.Update(int64(i)) @@ -226,7 +219,6 @@ func TestUniformSample(t *testing.T) { } func TestUniformSampleIncludesTail(t *testing.T) { - rand.Seed(1) s := NewUniformSample(100) max := 100 for i := 0; i < max; i++ { @@ -244,7 +236,7 @@ func TestUniformSampleIncludesTail(t *testing.T) { } func TestUniformSampleSnapshot(t *testing.T) { - s := NewUniformSample(100) + s := NewUniformSample(100).(*UniformSample).SetRand(rand.New(rand.NewSource(1))) for i := 1; i <= 10000; i++ { s.Update(int64(i)) } @@ -254,8 +246,7 @@ func TestUniformSampleSnapshot(t *testing.T) { } func TestUniformSampleStatistics(t *testing.T) { - rand.Seed(1) - s := NewUniformSample(100) + s := NewUniformSample(100).(*UniformSample).SetRand(rand.New(rand.NewSource(1))) for i := 1; i <= 10000; i++ { s.Update(int64(i)) } @@ -327,7 +318,7 @@ func testUniformSampleStatistics(t *testing.T, s Sample) { if ps[1] != 7380.5 { t.Errorf("75th percentile: 7380.5 != %v\n", ps[1]) } - if math.Abs(ps[2]-9986.429999999998) > epsilonPercentile { + if math.Abs(9986.429999999998-ps[2]) > epsilonPercentile { t.Errorf("99th percentile: 9986.429999999998 != %v\n", ps[2]) } } From c616077fb54efd9f8522024b58a59db6989f2d0f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 352/479] metrics: improve accuracy of CPU gauges (#26793) This PR changes metrics collection to actually measure the time interval between collections, rather than assume 3 seconds. I did some ad hoc profiling, and on slower hardware (eg, my Raspberry Pi 4) I routinely saw intervals between 3.3 - 3.5 seconds, with some being as high as 4.5 seconds. This will generally cause the CPU gauge readings to be too high, and in some cases can cause impossibly large values for the CPU load metrics (eg. greater than 400 for a 4 core CPU). --------- Co-authored-by: Felix Lange --- metrics/cpu.go | 7 ++++--- metrics/cpu_enabled.go | 4 ++-- metrics/cputime_nop.go | 2 +- metrics/cputime_unix.go | 4 ++-- metrics/metrics.go | 21 +++++++++++++++------ 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/metrics/cpu.go b/metrics/cpu.go index 72ece16e0768..3a49cd42493a 100644 --- a/metrics/cpu.go +++ b/metrics/cpu.go @@ -17,8 +17,9 @@ package metrics // CPUStats is the system and process CPU stats. +// All values are in seconds. type CPUStats struct { - GlobalTime int64 // Time spent by the CPU working on all processes - GlobalWait int64 // Time spent by waiting on disk for all processes - LocalTime int64 // Time spent by the CPU working on this process + GlobalTime float64 // Time spent by the CPU working on all processes + GlobalWait float64 // Time spent by waiting on disk for all processes + LocalTime float64 // Time spent by the CPU working on this process } diff --git a/metrics/cpu_enabled.go b/metrics/cpu_enabled.go index 5d0e8485d7fa..efb2234c9990 100644 --- a/metrics/cpu_enabled.go +++ b/metrics/cpu_enabled.go @@ -38,7 +38,7 @@ func ReadCPUStats(stats *CPUStats) { } // requesting all cpu times will always return an array with only one time stats entry timeStat := timeStats[0] - stats.GlobalTime = int64((timeStat.User + timeStat.Nice + timeStat.System) * cpu.ClocksPerSec) - stats.GlobalWait = int64((timeStat.Iowait) * cpu.ClocksPerSec) + stats.GlobalTime = timeStat.User + timeStat.Nice + timeStat.System + stats.GlobalWait = timeStat.Iowait stats.LocalTime = getProcessCPUTime() } diff --git a/metrics/cputime_nop.go b/metrics/cputime_nop.go index 0188735a7833..465d88c4d232 100644 --- a/metrics/cputime_nop.go +++ b/metrics/cputime_nop.go @@ -21,6 +21,6 @@ package metrics // getProcessCPUTime returns 0 on Windows as there is no system call to resolve // the actual process' CPU time. -func getProcessCPUTime() int64 { +func getProcessCPUTime() float64 { return 0 } diff --git a/metrics/cputime_unix.go b/metrics/cputime_unix.go index 50bb95b31e93..a5b50ebaa27f 100644 --- a/metrics/cputime_unix.go +++ b/metrics/cputime_unix.go @@ -26,11 +26,11 @@ import ( ) // getProcessCPUTime retrieves the process' CPU time since program startup. -func getProcessCPUTime() int64 { +func getProcessCPUTime() float64 { var usage syscall.Rusage if err := syscall.Getrusage(syscall.RUSAGE_SELF, &usage); err != nil { log.Warn("Failed to retrieve CPU time", "err", err) return 0 } - return int64(usage.Utime.Sec+usage.Stime.Sec)*100 + int64(usage.Utime.Usec+usage.Stime.Usec)/10000 //nolint:unconvert + return float64(usage.Utime.Sec+usage.Stime.Sec) + float64(usage.Utime.Usec+usage.Stime.Usec)/1000000 //nolint:unconvert } diff --git a/metrics/metrics.go b/metrics/metrics.go index 2b8bad8bee36..00f46e2a7c0a 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -127,8 +127,6 @@ func CollectProcessMetrics(refresh time.Duration) { return } - refreshFreq := int64(refresh / time.Second) - // Create the various data collectors var ( cpustats = make([]CPUStats, 2) @@ -163,14 +161,25 @@ func CollectProcessMetrics(refresh time.Duration) { diskWriteBytesCounter = GetOrRegisterCounter("system/disk/writebytes", DefaultRegistry) ) + var lastCollectTime time.Time + // Iterate loading the different stats and updating the meters. now, prev := 0, 1 for ; ; now, prev = prev, now { - // CPU + // Gather CPU times. ReadCPUStats(&cpustats[now]) - cpuSysLoad.Update((cpustats[now].GlobalTime - cpustats[prev].GlobalTime) / refreshFreq) - cpuSysWait.Update((cpustats[now].GlobalWait - cpustats[prev].GlobalWait) / refreshFreq) - cpuProcLoad.Update((cpustats[now].LocalTime - cpustats[prev].LocalTime) / refreshFreq) + collectTime := time.Now() + secondsSinceLastCollect := collectTime.Sub(lastCollectTime).Seconds() + lastCollectTime = collectTime + if secondsSinceLastCollect > 0 { + sysLoad := (cpustats[now].GlobalTime - cpustats[prev].GlobalTime) / secondsSinceLastCollect + sysWait := (cpustats[now].GlobalWait - cpustats[prev].GlobalWait) / secondsSinceLastCollect + procLoad := (cpustats[now].LocalTime - cpustats[prev].LocalTime) / secondsSinceLastCollect + // Convert to integer percentage. + cpuSysLoad.Update(int64(sysLoad * 100)) + cpuSysWait.Update(int64(sysWait * 100)) + cpuProcLoad.Update(int64(procLoad * 100)) + } // Threads cpuThreads.Update(int64(threadCreateProfile.Count())) From 9eae1243cd3d215a71b1e1e58ba26719b8100972 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 353/479] metrics: add cpu counters (#26796) This PR adds counter metrics for the CPU system and the Geth process. Currently the only metrics available for these items are gauges. Gauges are fine when the consumer scrapes metrics data at the same interval as Geth produces new values (every 3 seconds), but it is likely that most consumers will not scrape that often. Intervals of 10, 15, or maybe even 30 seconds are probably more common. So the problem is, how does the consumer estimate what the CPU was doing in between scrapes. With a counter, it's easy ... you just subtract two successive values and divide by the time to get a nice, accurate average. But with a gauge, you can't do that. A gauge reading is an instantaneous picture of what was happening at that moment, but it gives you no idea about what was going on between scrapes. Taking an average of values is meaningless. --- metrics/counter_float64.go | 153 +++++++++++++++++++++++++++ metrics/counter_float_64_test.go | 77 ++++++++++++++ metrics/exp/exp.go | 7 ++ metrics/graphite.go | 2 + metrics/influxdb/influxdb.go | 10 ++ metrics/influxdb/influxdbv2.go | 17 +-- metrics/librato/librato.go | 11 ++ metrics/log.go | 3 + metrics/metrics.go | 19 ++-- metrics/metrics_test.go | 2 + metrics/opentsdb.go | 2 + metrics/prometheus/collector.go | 4 + metrics/prometheus/collector_test.go | 7 ++ metrics/prometheus/prometheus.go | 2 + metrics/registry.go | 4 +- metrics/syslog.go | 2 + metrics/writer.go | 3 + 17 files changed, 312 insertions(+), 13 deletions(-) create mode 100644 metrics/counter_float64.go create mode 100644 metrics/counter_float_64_test.go diff --git a/metrics/counter_float64.go b/metrics/counter_float64.go new file mode 100644 index 000000000000..f05f62fed632 --- /dev/null +++ b/metrics/counter_float64.go @@ -0,0 +1,153 @@ +package metrics + +import ( + "sync" +) + +// CounterFloat64 holds a float64 value that can be incremented and decremented. +type CounterFloat64 interface { + Clear() + Count() float64 + Dec(float64) + Inc(float64) + Snapshot() CounterFloat64 +} + +// GetOrRegisterCounterFloat64 returns an existing CounterFloat64 or constructs and registers +// a new StandardCounterFloat64. +func GetOrRegisterCounterFloat64(name string, r Registry) CounterFloat64 { + if nil == r { + r = DefaultRegistry + } + return r.GetOrRegister(name, NewCounterFloat64).(CounterFloat64) +} + +// GetOrRegisterCounterFloat64Forced returns an existing CounterFloat64 or constructs and registers a +// new CounterFloat64 no matter the global switch is enabled or not. +// Be sure to unregister the counter from the registry once it is of no use to +// allow for garbage collection. +func GetOrRegisterCounterFloat64Forced(name string, r Registry) CounterFloat64 { + if nil == r { + r = DefaultRegistry + } + return r.GetOrRegister(name, NewCounterFloat64Forced).(CounterFloat64) +} + +// NewCounterFloat64 constructs a new StandardCounterFloat64. +func NewCounterFloat64() CounterFloat64 { + if !Enabled { + return NilCounterFloat64{} + } + return &StandardCounterFloat64{count: 0.0} +} + +// NewCounterFloat64Forced constructs a new StandardCounterFloat64 and returns it no matter if +// the global switch is enabled or not. +func NewCounterFloat64Forced() CounterFloat64 { + return &StandardCounterFloat64{count: 0.0} +} + +// NewRegisteredCounterFloat64 constructs and registers a new StandardCounterFloat64. +func NewRegisteredCounterFloat64(name string, r Registry) CounterFloat64 { + c := NewCounterFloat64() + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// NewRegisteredCounterFloat64Forced constructs and registers a new StandardCounterFloat64 +// and launches a goroutine no matter the global switch is enabled or not. +// Be sure to unregister the counter from the registry once it is of no use to +// allow for garbage collection. +func NewRegisteredCounterFloat64Forced(name string, r Registry) CounterFloat64 { + c := NewCounterFloat64Forced() + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// CounterFloat64Snapshot is a read-only copy of another CounterFloat64. +type CounterFloat64Snapshot float64 + +// Clear panics. +func (CounterFloat64Snapshot) Clear() { + panic("Clear called on a CounterFloat64Snapshot") +} + +// Count returns the value at the time the snapshot was taken. +func (c CounterFloat64Snapshot) Count() float64 { return float64(c) } + +// Dec panics. +func (CounterFloat64Snapshot) Dec(float64) { + panic("Dec called on a CounterFloat64Snapshot") +} + +// Inc panics. +func (CounterFloat64Snapshot) Inc(float64) { + panic("Inc called on a CounterFloat64Snapshot") +} + +// Snapshot returns the snapshot. +func (c CounterFloat64Snapshot) Snapshot() CounterFloat64 { return c } + +// NilCounterFloat64 is a no-op CounterFloat64. +type NilCounterFloat64 struct{} + +// Clear is a no-op. +func (NilCounterFloat64) Clear() {} + +// Count is a no-op. +func (NilCounterFloat64) Count() float64 { return 0.0 } + +// Dec is a no-op. +func (NilCounterFloat64) Dec(i float64) {} + +// Inc is a no-op. +func (NilCounterFloat64) Inc(i float64) {} + +// Snapshot is a no-op. +func (NilCounterFloat64) Snapshot() CounterFloat64 { return NilCounterFloat64{} } + +// StandardCounterFloat64 is the standard implementation of a CounterFloat64 and uses the +// sync.Mutex package to manage a single float64 value. +type StandardCounterFloat64 struct { + mutex sync.Mutex + count float64 +} + +// Clear sets the counter to zero. +func (c *StandardCounterFloat64) Clear() { + c.mutex.Lock() + defer c.mutex.Unlock() + c.count = 0.0 +} + +// Count returns the current value. +func (c *StandardCounterFloat64) Count() float64 { + c.mutex.Lock() + defer c.mutex.Unlock() + return c.count +} + +// Dec decrements the counter by the given amount. +func (c *StandardCounterFloat64) Dec(v float64) { + c.mutex.Lock() + defer c.mutex.Unlock() + c.count -= v +} + +// Inc increments the counter by the given amount. +func (c *StandardCounterFloat64) Inc(v float64) { + c.mutex.Lock() + defer c.mutex.Unlock() + c.count += v +} + +// Snapshot returns a read-only copy of the counter. +func (c *StandardCounterFloat64) Snapshot() CounterFloat64 { + return CounterFloat64Snapshot(c.Count()) +} diff --git a/metrics/counter_float_64_test.go b/metrics/counter_float_64_test.go new file mode 100644 index 000000000000..44d9b4c20c85 --- /dev/null +++ b/metrics/counter_float_64_test.go @@ -0,0 +1,77 @@ +package metrics + +import "testing" + +func BenchmarkCounterFloat64(b *testing.B) { + c := NewCounterFloat64() + b.ResetTimer() + for i := 0; i < b.N; i++ { + c.Inc(1.0) + } +} + +func TestCounterFloat64Clear(t *testing.T) { + c := NewCounterFloat64() + c.Inc(1.0) + c.Clear() + if count := c.Count(); count != 0 { + t.Errorf("c.Count(): 0 != %v\n", count) + } +} + +func TestCounterFloat64Dec1(t *testing.T) { + c := NewCounterFloat64() + c.Dec(1.0) + if count := c.Count(); count != -1.0 { + t.Errorf("c.Count(): -1.0 != %v\n", count) + } +} + +func TestCounterFloat64Dec2(t *testing.T) { + c := NewCounterFloat64() + c.Dec(2.0) + if count := c.Count(); count != -2.0 { + t.Errorf("c.Count(): -2.0 != %v\n", count) + } +} + +func TestCounterFloat64Inc1(t *testing.T) { + c := NewCounterFloat64() + c.Inc(1.0) + if count := c.Count(); count != 1.0 { + t.Errorf("c.Count(): 1.0 != %v\n", count) + } +} + +func TestCounterFloat64Inc2(t *testing.T) { + c := NewCounterFloat64() + c.Inc(2.0) + if count := c.Count(); count != 2.0 { + t.Errorf("c.Count(): 2.0 != %v\n", count) + } +} + +func TestCounterFloat64Snapshot(t *testing.T) { + c := NewCounterFloat64() + c.Inc(1.0) + snapshot := c.Snapshot() + c.Inc(1.0) + if count := snapshot.Count(); count != 1.0 { + t.Errorf("c.Count(): 1.0 != %v\n", count) + } +} + +func TestCounterFloat64Zero(t *testing.T) { + c := NewCounterFloat64() + if count := c.Count(); count != 0 { + t.Errorf("c.Count(): 0 != %v\n", count) + } +} + +func TestGetOrRegisterCounterFloat64(t *testing.T) { + r := NewRegistry() + NewRegisteredCounterFloat64("foo", r).Inc(47.0) + if c := GetOrRegisterCounterFloat64("foo", r); c.Count() != 47.0 { + t.Fatal(c) + } +} diff --git a/metrics/exp/exp.go b/metrics/exp/exp.go index 622c2b5289af..77b198e610fd 100644 --- a/metrics/exp/exp.go +++ b/metrics/exp/exp.go @@ -100,6 +100,11 @@ func (exp *exp) publishCounter(name string, metric metrics.Counter) { v.Set(metric.Count()) } +func (exp *exp) publishCounterFloat64(name string, metric metrics.CounterFloat64) { + v := exp.getFloat(name) + v.Set(metric.Count()) +} + func (exp *exp) publishGauge(name string, metric metrics.Gauge) { v := exp.getInt(name) v.Set(metric.Value()) @@ -167,6 +172,8 @@ func (exp *exp) syncToExpvar() { switch i := i.(type) { case metrics.Counter: exp.publishCounter(name, i) + case metrics.CounterFloat64: + exp.publishCounterFloat64(name, i) case metrics.Gauge: exp.publishGauge(name, i) case metrics.GaugeFloat64: diff --git a/metrics/graphite.go b/metrics/graphite.go index 142eec86beb4..29f72b0c4181 100644 --- a/metrics/graphite.go +++ b/metrics/graphite.go @@ -67,6 +67,8 @@ func graphite(c *GraphiteConfig) error { switch metric := i.(type) { case Counter: fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, metric.Count(), now) + case CounterFloat64: + fmt.Fprintf(w, "%s.%s.count %f %d\n", c.Prefix, name, metric.Count(), now) case Gauge: fmt.Fprintf(w, "%s.%s.value %d %d\n", c.Prefix, name, metric.Value(), now) case GaugeFloat64: diff --git a/metrics/influxdb/influxdb.go b/metrics/influxdb/influxdb.go index 56f19e944abf..9f331183f413 100644 --- a/metrics/influxdb/influxdb.go +++ b/metrics/influxdb/influxdb.go @@ -113,6 +113,16 @@ func (r *reporter) send() error { }, Time: now, }) + case metrics.CounterFloat64: + count := metric.Count() + pts = append(pts, client.Point{ + Measurement: fmt.Sprintf("%s%s.count", namespace, name), + Tags: r.tags, + Fields: map[string]interface{}{ + "value": count, + }, + Time: now, + }) case metrics.Gauge: ms := metric.Snapshot() pts = append(pts, client.Point{ diff --git a/metrics/influxdb/influxdbv2.go b/metrics/influxdb/influxdbv2.go index 4fa0d02bcdca..8a4dbab3c3ca 100644 --- a/metrics/influxdb/influxdbv2.go +++ b/metrics/influxdb/influxdbv2.go @@ -24,8 +24,6 @@ type v2Reporter struct { client influxdb2.Client write api.WriteAPI - - cache map[string]int64 } // InfluxDBWithTags starts a InfluxDB reporter which will post the from the given metrics.Registry at each d interval with the specified tags @@ -39,7 +37,6 @@ func InfluxDBV2WithTags(r metrics.Registry, d time.Duration, endpoint string, to organization: organization, namespace: namespace, tags: tags, - cache: make(map[string]int64), } rep.client = influxdb2.NewClient(rep.endpoint, rep.token) @@ -87,17 +84,25 @@ func (r *v2Reporter) send() { case metrics.Counter: v := metric.Count() - l := r.cache[name] measurement := fmt.Sprintf("%s%s.count", namespace, name) fields := map[string]interface{}{ - "value": v - l, + "value": v, } pt := influxdb2.NewPoint(measurement, r.tags, fields, now) r.write.WritePoint(pt) - r.cache[name] = v + case metrics.CounterFloat64: + v := metric.Count() + + measurement := fmt.Sprintf("%s%s.count", namespace, name) + fields := map[string]interface{}{ + "value": v, + } + + pt := influxdb2.NewPoint(measurement, r.tags, fields, now) + r.write.WritePoint(pt) case metrics.Gauge: ms := metric.Snapshot() diff --git a/metrics/librato/librato.go b/metrics/librato/librato.go index 9323290bc51b..7fa4c56b5db3 100644 --- a/metrics/librato/librato.go +++ b/metrics/librato/librato.go @@ -107,6 +107,17 @@ func (re *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Ba } snapshot.Counters = append(snapshot.Counters, measurement) } + case metrics.CounterFloat64: + if m.Count() > 0 { + measurement[Name] = fmt.Sprintf("%s.%s", name, "count") + measurement[Value] = m.Count() + measurement[Attributes] = map[string]interface{}{ + DisplayUnitsLong: Operations, + DisplayUnitsShort: OperationsShort, + DisplayMin: "0", + } + snapshot.Counters = append(snapshot.Counters, measurement) + } case metrics.Gauge: measurement[Name] = name measurement[Value] = float64(m.Value()) diff --git a/metrics/log.go b/metrics/log.go index 0c8ea7c97123..d1ce627a8378 100644 --- a/metrics/log.go +++ b/metrics/log.go @@ -24,6 +24,9 @@ func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) { case Counter: l.Printf("counter %s\n", name) l.Printf(" count: %9d\n", metric.Count()) + case CounterFloat64: + l.Printf("counter %s\n", name) + l.Printf(" count: %f\n", metric.Count()) case Gauge: l.Printf("gauge %s\n", name) l.Printf(" value: %9d\n", metric.Value()) diff --git a/metrics/metrics.go b/metrics/metrics.go index 00f46e2a7c0a..3468cfe8e407 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -144,6 +144,9 @@ func CollectProcessMetrics(refresh time.Duration) { cpuSysLoad = GetOrRegisterGauge("system/cpu/sysload", DefaultRegistry) cpuSysWait = GetOrRegisterGauge("system/cpu/syswait", DefaultRegistry) cpuProcLoad = GetOrRegisterGauge("system/cpu/procload", DefaultRegistry) + cpuSysLoadTotal = GetOrRegisterCounterFloat64("system/cpu/sysload/total", DefaultRegistry) + cpuSysWaitTotal = GetOrRegisterCounterFloat64("system/cpu/syswait/total", DefaultRegistry) + cpuProcLoadTotal = GetOrRegisterCounterFloat64("system/cpu/procload/total", DefaultRegistry) cpuThreads = GetOrRegisterGauge("system/cpu/threads", DefaultRegistry) cpuGoroutines = GetOrRegisterGauge("system/cpu/goroutines", DefaultRegistry) cpuSchedLatency = getOrRegisterRuntimeHistogram("system/cpu/schedlatency", secondsToNs, nil) @@ -172,13 +175,17 @@ func CollectProcessMetrics(refresh time.Duration) { secondsSinceLastCollect := collectTime.Sub(lastCollectTime).Seconds() lastCollectTime = collectTime if secondsSinceLastCollect > 0 { - sysLoad := (cpustats[now].GlobalTime - cpustats[prev].GlobalTime) / secondsSinceLastCollect - sysWait := (cpustats[now].GlobalWait - cpustats[prev].GlobalWait) / secondsSinceLastCollect - procLoad := (cpustats[now].LocalTime - cpustats[prev].LocalTime) / secondsSinceLastCollect + sysLoad := cpustats[now].GlobalTime - cpustats[prev].GlobalTime + sysWait := cpustats[now].GlobalWait - cpustats[prev].GlobalWait + procLoad := cpustats[now].LocalTime - cpustats[prev].LocalTime // Convert to integer percentage. - cpuSysLoad.Update(int64(sysLoad * 100)) - cpuSysWait.Update(int64(sysWait * 100)) - cpuProcLoad.Update(int64(procLoad * 100)) + cpuSysLoad.Update(int64(sysLoad / secondsSinceLastCollect * 100)) + cpuSysWait.Update(int64(sysWait / secondsSinceLastCollect * 100)) + cpuProcLoad.Update(int64(procLoad / secondsSinceLastCollect * 100)) + // increment counters (ms) + cpuSysLoadTotal.Inc(sysLoad) + cpuSysWaitTotal.Inc(sysWait) + cpuProcLoadTotal.Inc(procLoad) } // Threads diff --git a/metrics/metrics_test.go b/metrics/metrics_test.go index e3fde1ea62ce..534c44139b36 100644 --- a/metrics/metrics_test.go +++ b/metrics/metrics_test.go @@ -18,6 +18,7 @@ func TestReadRuntimeValues(t *testing.T) { func BenchmarkMetrics(b *testing.B) { r := NewRegistry() c := NewRegisteredCounter("counter", r) + cf := NewRegisteredCounterFloat64("counterfloat64", r) g := NewRegisteredGauge("gauge", r) gf := NewRegisteredGaugeFloat64("gaugefloat64", r) h := NewRegisteredHistogram("histogram", r, NewUniformSample(100)) @@ -71,6 +72,7 @@ func BenchmarkMetrics(b *testing.B) { //log.Println("go", i) for i := 0; i < b.N; i++ { c.Inc(1) + cf.Inc(1.0) g.Update(int64(i)) gf.Update(float64(i)) h.Update(int64(i)) diff --git a/metrics/opentsdb.go b/metrics/opentsdb.go index 3fde55454ba9..c9fd2e75d5e5 100644 --- a/metrics/opentsdb.go +++ b/metrics/opentsdb.go @@ -71,6 +71,8 @@ func openTSDB(c *OpenTSDBConfig) error { switch metric := i.(type) { case Counter: fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, metric.Count(), shortHostname) + case CounterFloat64: + fmt.Fprintf(w, "put %s.%s.count %d %f host=%s\n", c.Prefix, name, now, metric.Count(), shortHostname) case Gauge: fmt.Fprintf(w, "put %s.%s.value %d %d host=%s\n", c.Prefix, name, now, metric.Value(), shortHostname) case GaugeFloat64: diff --git a/metrics/prometheus/collector.go b/metrics/prometheus/collector.go index 23934b212c23..b7c238f601d7 100644 --- a/metrics/prometheus/collector.go +++ b/metrics/prometheus/collector.go @@ -50,6 +50,10 @@ func (c *collector) addCounter(name string, m metrics.Counter) { c.writeGaugeCounter(name, m.Count()) } +func (c *collector) addCounterFloat64(name string, m metrics.CounterFloat64) { + c.writeGaugeCounter(name, m.Count()) +} + func (c *collector) addGauge(name string, m metrics.Gauge) { c.writeGaugeCounter(name, m.Value()) } diff --git a/metrics/prometheus/collector_test.go b/metrics/prometheus/collector_test.go index 4c2ffcb1fed6..eb6dbe603f99 100644 --- a/metrics/prometheus/collector_test.go +++ b/metrics/prometheus/collector_test.go @@ -20,6 +20,10 @@ func TestCollector(t *testing.T) { counter.Inc(12345) c.addCounter("test/counter", counter) + counterfloat64 := metrics.NewCounterFloat64() + counterfloat64.Inc(54321.98) + c.addCounterFloat64("test/counter_float64", counterfloat64) + gauge := metrics.NewGauge() gauge.Update(23456) c.addGauge("test/gauge", gauge) @@ -61,6 +65,9 @@ func TestCollector(t *testing.T) { const expectedOutput = `# TYPE test_counter gauge test_counter 12345 +# TYPE test_counter_float64 gauge +test_counter_float64 54321.98 + # TYPE test_gauge gauge test_gauge 23456 diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go index b367e6c3aa59..2d999e493dc8 100644 --- a/metrics/prometheus/prometheus.go +++ b/metrics/prometheus/prometheus.go @@ -45,6 +45,8 @@ func Handler(reg metrics.Registry) http.Handler { switch m := i.(type) { case metrics.Counter: c.addCounter(name, m.Snapshot()) + case metrics.CounterFloat64: + c.addCounterFloat64(name, m.Snapshot()) case metrics.Gauge: c.addGauge(name, m.Snapshot()) case metrics.GaugeFloat64: diff --git a/metrics/registry.go b/metrics/registry.go index cc34c9dfd2c8..c550b363d0a1 100644 --- a/metrics/registry.go +++ b/metrics/registry.go @@ -120,6 +120,8 @@ func (r *StandardRegistry) GetAll() map[string]map[string]interface{} { switch metric := i.(type) { case Counter: values["count"] = metric.Count() + case CounterFloat64: + values["count"] = metric.Count() case Gauge: values["value"] = metric.Value() case GaugeFloat64: @@ -196,7 +198,7 @@ func (r *StandardRegistry) register(name string, i interface{}) error { return DuplicateMetric(name) } switch i.(type) { - case Counter, Gauge, GaugeFloat64, Healthcheck, Histogram, Meter, Timer, ResettingTimer: + case Counter, CounterFloat64, Gauge, GaugeFloat64, Healthcheck, Histogram, Meter, Timer, ResettingTimer: r.metrics[name] = i } return nil diff --git a/metrics/syslog.go b/metrics/syslog.go index 551a2bd0f072..f23b07e199f3 100644 --- a/metrics/syslog.go +++ b/metrics/syslog.go @@ -17,6 +17,8 @@ func Syslog(r Registry, d time.Duration, w *syslog.Writer) { switch metric := i.(type) { case Counter: w.Info(fmt.Sprintf("counter %s: count: %d", name, metric.Count())) + case CounterFloat64: + w.Info(fmt.Sprintf("counter %s: count: %f", name, metric.Count())) case Gauge: w.Info(fmt.Sprintf("gauge %s: value: %d", name, metric.Value())) case GaugeFloat64: diff --git a/metrics/writer.go b/metrics/writer.go index 88521a80d9d7..256fbd14c9b9 100644 --- a/metrics/writer.go +++ b/metrics/writer.go @@ -29,6 +29,9 @@ func WriteOnce(r Registry, w io.Writer) { case Counter: fmt.Fprintf(w, "counter %s\n", namedMetric.name) fmt.Fprintf(w, " count: %9d\n", metric.Count()) + case CounterFloat64: + fmt.Fprintf(w, "counter %s\n", namedMetric.name) + fmt.Fprintf(w, " count: %f\n", metric.Count()) case Gauge: fmt.Fprintf(w, "gauge %s\n", namedMetric.name) fmt.Fprintf(w, " value: %9d\n", metric.Value()) From 6e055a601d38f5788c43f04921786d7059c106fb Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 354/479] metrics/influxdb: reuse code between v1 and v2 reporters (#26963) --- go.mod | 2 +- go.sum | 4 +- metrics/influxdb/influxdb.go | 332 ++++++++++----------------------- metrics/influxdb/influxdbv1.go | 145 ++++++++++++++ metrics/influxdb/influxdbv2.go | 143 +------------- 5 files changed, 254 insertions(+), 372 deletions(-) create mode 100644 metrics/influxdb/influxdbv1.go diff --git a/go.mod b/go.mod index 3160395d2a06..28a24f8b6b38 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,6 @@ require ( github.com/hashicorp/golang-lru v0.5.4 github.com/holiman/uint256 v1.2.4 github.com/huin/goupnp v1.3.0 - github.com/influxdata/influxdb v1.7.9 github.com/jackpal/go-nat-pmp v1.0.2 github.com/julienschmidt/httprouter v1.3.0 github.com/karalabe/hid v1.0.0 @@ -51,6 +50,7 @@ require ( github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 github.com/ethereum/c-kzg-4844 v0.4.0 github.com/influxdata/influxdb-client-go/v2 v2.4.0 + github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c github.com/kylelemons/godebug v1.1.0 github.com/mattn/go-isatty v0.0.17 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible diff --git a/go.sum b/go.sum index 84d68e6c41ca..68019c92aba9 100644 --- a/go.sum +++ b/go.sum @@ -99,10 +99,10 @@ github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXei github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= -github.com/influxdata/influxdb v1.7.9 h1:uSeBTNO4rBkbp1Be5FKRsAmglM9nlx25TzVQRQt1An4= -github.com/influxdata/influxdb v1.7.9/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= github.com/influxdata/influxdb-client-go/v2 v2.4.0 h1:HGBfZYStlx3Kqvsv1h2pJixbCl/jhnFtxpKFAv9Tu5k= github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= +github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs= +github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU= github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= diff --git a/metrics/influxdb/influxdb.go b/metrics/influxdb/influxdb.go index 9f331183f413..1e7fb9e11dd4 100644 --- a/metrics/influxdb/influxdb.go +++ b/metrics/influxdb/influxdb.go @@ -2,242 +2,112 @@ package influxdb import ( "fmt" - uurl "net/url" - "time" - "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/metrics" - "github.com/influxdata/influxdb/client" ) -type reporter struct { - reg metrics.Registry - interval time.Duration - - url uurl.URL - database string - username string - password string - namespace string - tags map[string]string - - client *client.Client - - cache map[string]int64 -} - -// InfluxDB starts a InfluxDB reporter which will post the from the given metrics.Registry at each d interval. -func InfluxDB(r metrics.Registry, d time.Duration, url, database, username, password, namespace string) { - InfluxDBWithTags(r, d, url, database, username, password, namespace, nil) -} - -// InfluxDBWithTags starts a InfluxDB reporter which will post the from the given metrics.Registry at each d interval with the specified tags -func InfluxDBWithTags(r metrics.Registry, d time.Duration, url, database, username, password, namespace string, tags map[string]string) { - u, err := uurl.Parse(url) - if err != nil { - log.Warn("unable to parse InfluxDB url %s. err=%v", url, err) - return - } - - rep := &reporter{ - reg: r, - interval: d, - url: *u, - database: database, - username: username, - password: password, - namespace: namespace, - tags: tags, - cache: make(map[string]int64), - } - if err := rep.makeClient(); err != nil { - log.Warn("unable to make InfluxDB client. err=%v", err) - return - } - - rep.run() -} - -func (r *reporter) makeClient() (err error) { - r.client, err = client.NewClient(client.Config{ - URL: r.url, - Username: r.username, - Password: r.password, - Timeout: 10 * time.Second, - }) - - return -} - -func (r *reporter) run() { - intervalTicker := time.NewTicker(r.interval) - pingTicker := time.NewTicker(time.Second * 5) - - defer intervalTicker.Stop() - defer pingTicker.Stop() - - for { - select { - case <-intervalTicker.C: - if err := r.send(); err != nil { - log.Warn("unable to send to InfluxDB. err=%v", err) - } - case <-pingTicker.C: - _, _, err := r.client.Ping() - if err != nil { - log.Warn("got error while sending a ping to InfluxDB, trying to recreate client. err=%v", err) - - if err = r.makeClient(); err != nil { - log.Warn("unable to make InfluxDB client. err=%v", err) - } - } +func readMeter(namespace, name string, i interface{}) (string, map[string]interface{}) { + switch metric := i.(type) { + case metrics.Counter: + measurement := fmt.Sprintf("%s%s.count", namespace, name) + fields := map[string]interface{}{ + "value": metric.Count(), } - } -} - -func (r *reporter) send() error { - var pts []client.Point - - r.reg.Each(func(name string, i interface{}) { - now := time.Now() - namespace := r.namespace - - switch metric := i.(type) { - case metrics.Counter: - count := metric.Count() - pts = append(pts, client.Point{ - Measurement: fmt.Sprintf("%s%s.count", namespace, name), - Tags: r.tags, - Fields: map[string]interface{}{ - "value": count, - }, - Time: now, - }) - case metrics.CounterFloat64: - count := metric.Count() - pts = append(pts, client.Point{ - Measurement: fmt.Sprintf("%s%s.count", namespace, name), - Tags: r.tags, - Fields: map[string]interface{}{ - "value": count, - }, - Time: now, - }) - case metrics.Gauge: - ms := metric.Snapshot() - pts = append(pts, client.Point{ - Measurement: fmt.Sprintf("%s%s.gauge", namespace, name), - Tags: r.tags, - Fields: map[string]interface{}{ - "value": ms.Value(), - }, - Time: now, - }) - case metrics.GaugeFloat64: - ms := metric.Snapshot() - pts = append(pts, client.Point{ - Measurement: fmt.Sprintf("%s%s.gauge", namespace, name), - Tags: r.tags, - Fields: map[string]interface{}{ - "value": ms.Value(), - }, - Time: now, - }) - case metrics.Histogram: - ms := metric.Snapshot() - if ms.Count() > 0 { - ps := ms.Percentiles([]float64{0.25, 0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) - fields := map[string]interface{}{ - "count": ms.Count(), - "max": ms.Max(), - "mean": ms.Mean(), - "min": ms.Min(), - "stddev": ms.StdDev(), - "variance": ms.Variance(), - "p25": ps[0], - "p50": ps[1], - "p75": ps[2], - "p95": ps[3], - "p99": ps[4], - "p999": ps[5], - "p9999": ps[6], - } - pts = append(pts, client.Point{ - Measurement: fmt.Sprintf("%s%s.histogram", namespace, name), - Tags: r.tags, - Fields: fields, - Time: now, - }) - } - case metrics.Meter: - ms := metric.Snapshot() - pts = append(pts, client.Point{ - Measurement: fmt.Sprintf("%s%s.meter", namespace, name), - Tags: r.tags, - Fields: map[string]interface{}{ - "count": ms.Count(), - "m1": ms.Rate1(), - "m5": ms.Rate5(), - "m15": ms.Rate15(), - "mean": ms.RateMean(), - }, - Time: now, - }) - case metrics.Timer: - ms := metric.Snapshot() - ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) - pts = append(pts, client.Point{ - Measurement: fmt.Sprintf("%s%s.timer", namespace, name), - Tags: r.tags, - Fields: map[string]interface{}{ - "count": ms.Count(), - "max": ms.Max(), - "mean": ms.Mean(), - "min": ms.Min(), - "stddev": ms.StdDev(), - "variance": ms.Variance(), - "p50": ps[0], - "p75": ps[1], - "p95": ps[2], - "p99": ps[3], - "p999": ps[4], - "p9999": ps[5], - "m1": ms.Rate1(), - "m5": ms.Rate5(), - "m15": ms.Rate15(), - "meanrate": ms.RateMean(), - }, - Time: now, - }) - case metrics.ResettingTimer: - t := metric.Snapshot() - - if len(t.Values()) > 0 { - ps := t.Percentiles([]float64{50, 95, 99}) - val := t.Values() - pts = append(pts, client.Point{ - Measurement: fmt.Sprintf("%s%s.span", namespace, name), - Tags: r.tags, - Fields: map[string]interface{}{ - "count": len(val), - "max": val[len(val)-1], - "mean": t.Mean(), - "min": val[0], - "p50": ps[0], - "p95": ps[1], - "p99": ps[2], - }, - Time: now, - }) - } + return measurement, fields + case metrics.CounterFloat64: + measurement := fmt.Sprintf("%s%s.count", namespace, name) + fields := map[string]interface{}{ + "value": metric.Count(), } - }) - - bps := client.BatchPoints{ - Points: pts, - Database: r.database, + return measurement, fields + case metrics.Gauge: + measurement := fmt.Sprintf("%s%s.gauge", namespace, name) + fields := map[string]interface{}{ + "value": metric.Snapshot().Value(), + } + return measurement, fields + case metrics.GaugeFloat64: + measurement := fmt.Sprintf("%s%s.gauge", namespace, name) + fields := map[string]interface{}{ + "value": metric.Snapshot().Value(), + } + return measurement, fields + case metrics.Histogram: + ms := metric.Snapshot() + if ms.Count() <= 0 { + break + } + ps := ms.Percentiles([]float64{0.25, 0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) + measurement := fmt.Sprintf("%s%s.histogram", namespace, name) + fields := map[string]interface{}{ + "count": ms.Count(), + "max": ms.Max(), + "mean": ms.Mean(), + "min": ms.Min(), + "stddev": ms.StdDev(), + "variance": ms.Variance(), + "p25": ps[0], + "p50": ps[1], + "p75": ps[2], + "p95": ps[3], + "p99": ps[4], + "p999": ps[5], + "p9999": ps[6], + } + return measurement, fields + case metrics.Meter: + ms := metric.Snapshot() + measurement := fmt.Sprintf("%s%s.meter", namespace, name) + fields := map[string]interface{}{ + "count": ms.Count(), + "m1": ms.Rate1(), + "m5": ms.Rate5(), + "m15": ms.Rate15(), + "mean": ms.RateMean(), + } + return measurement, fields + case metrics.Timer: + ms := metric.Snapshot() + ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) + + measurement := fmt.Sprintf("%s%s.timer", namespace, name) + fields := map[string]interface{}{ + "count": ms.Count(), + "max": ms.Max(), + "mean": ms.Mean(), + "min": ms.Min(), + "stddev": ms.StdDev(), + "variance": ms.Variance(), + "p50": ps[0], + "p75": ps[1], + "p95": ps[2], + "p99": ps[3], + "p999": ps[4], + "p9999": ps[5], + "m1": ms.Rate1(), + "m5": ms.Rate5(), + "m15": ms.Rate15(), + "meanrate": ms.RateMean(), + } + return measurement, fields + case metrics.ResettingTimer: + t := metric.Snapshot() + if len(t.Values()) == 0 { + break + } + ps := t.Percentiles([]float64{50, 95, 99}) + val := t.Values() + measurement := fmt.Sprintf("%s%s.span", namespace, name) + fields := map[string]interface{}{ + "count": len(val), + "max": val[len(val)-1], + "mean": t.Mean(), + "min": val[0], + "p50": ps[0], + "p95": ps[1], + "p99": ps[2], + } + return measurement, fields } - - _, err := r.client.Write(bps) - return err + return "", nil } diff --git a/metrics/influxdb/influxdbv1.go b/metrics/influxdb/influxdbv1.go new file mode 100644 index 000000000000..40ae9a3c10b7 --- /dev/null +++ b/metrics/influxdb/influxdbv1.go @@ -0,0 +1,145 @@ +package influxdb + +import ( + "fmt" + uurl "net/url" + "time" + + "github.com/XinFinOrg/XDPoSChain/log" + "github.com/XinFinOrg/XDPoSChain/metrics" + client "github.com/influxdata/influxdb1-client/v2" +) + +type reporter struct { + reg metrics.Registry + interval time.Duration + + url uurl.URL + database string + username string + password string + namespace string + tags map[string]string + + client client.Client + + cache map[string]int64 +} + +// InfluxDB starts a InfluxDB reporter which will post the from the given metrics.Registry at each d interval. +func InfluxDB(r metrics.Registry, d time.Duration, url, database, username, password, namespace string) { + InfluxDBWithTags(r, d, url, database, username, password, namespace, nil) +} + +// InfluxDBWithTags starts a InfluxDB reporter which will post the from the given metrics.Registry at each d interval with the specified tags +func InfluxDBWithTags(r metrics.Registry, d time.Duration, url, database, username, password, namespace string, tags map[string]string) { + u, err := uurl.Parse(url) + if err != nil { + log.Warn("Unable to parse InfluxDB", "url", url, "err", err) + return + } + + rep := &reporter{ + reg: r, + interval: d, + url: *u, + database: database, + username: username, + password: password, + namespace: namespace, + tags: tags, + cache: make(map[string]int64), + } + if err := rep.makeClient(); err != nil { + log.Warn("Unable to make InfluxDB client", "err", err) + return + } + + rep.run() +} + +// InfluxDBWithTagsOnce runs once an InfluxDB reporter and post the given metrics.Registry with the specified tags +func InfluxDBWithTagsOnce(r metrics.Registry, url, database, username, password, namespace string, tags map[string]string) error { + u, err := uurl.Parse(url) + if err != nil { + return fmt.Errorf("unable to parse InfluxDB. url: %s, err: %v", url, err) + } + + rep := &reporter{ + reg: r, + url: *u, + database: database, + username: username, + password: password, + namespace: namespace, + tags: tags, + cache: make(map[string]int64), + } + if err := rep.makeClient(); err != nil { + return fmt.Errorf("unable to make InfluxDB client. err: %v", err) + } + + if err := rep.send(); err != nil { + return fmt.Errorf("unable to send to InfluxDB. err: %v", err) + } + + return nil +} + +func (r *reporter) makeClient() (err error) { + r.client, err = client.NewHTTPClient(client.HTTPConfig{ + Addr: r.url.String(), + Username: r.username, + Password: r.password, + Timeout: 10 * time.Second, + }) + + return +} + +func (r *reporter) run() { + intervalTicker := time.NewTicker(r.interval) + pingTicker := time.NewTicker(time.Second * 5) + + defer intervalTicker.Stop() + defer pingTicker.Stop() + + for { + select { + case <-intervalTicker.C: + if err := r.send(); err != nil { + log.Warn("Unable to send to InfluxDB", "err", err) + } + case <-pingTicker.C: + _, _, err := r.client.Ping(0) + if err != nil { + log.Warn("Got error while sending a ping to InfluxDB, trying to recreate client", "err", err) + + if err = r.makeClient(); err != nil { + log.Warn("Unable to make InfluxDB client", "err", err) + } + } + } + } +} + +func (r *reporter) send() error { + bps, err := client.NewBatchPoints( + client.BatchPointsConfig{ + Database: r.database, + }) + if err != nil { + return err + } + r.reg.Each(func(name string, i interface{}) { + now := time.Now() + measurement, fields := readMeter(r.namespace, name, i) + if fields == nil { + return + } + if p, err := client.NewPoint(measurement, r.tags, fields, now); err == nil { + bps.AddPoint(p) + } + }) + return r.client.Write(bps) +} diff --git a/metrics/influxdb/influxdbv2.go b/metrics/influxdb/influxdbv2.go index 8a4dbab3c3ca..15ea660d865c 100644 --- a/metrics/influxdb/influxdbv2.go +++ b/metrics/influxdb/influxdbv2.go @@ -2,7 +2,6 @@ package influxdb import ( "context" - "fmt" "time" "github.com/XinFinOrg/XDPoSChain/log" @@ -78,145 +77,13 @@ func (r *v2Reporter) run() { func (r *v2Reporter) send() { r.reg.Each(func(name string, i interface{}) { now := time.Now() - namespace := r.namespace - - switch metric := i.(type) { - - case metrics.Counter: - v := metric.Count() - - measurement := fmt.Sprintf("%s%s.count", namespace, name) - fields := map[string]interface{}{ - "value": v, - } - - pt := influxdb2.NewPoint(measurement, r.tags, fields, now) - r.write.WritePoint(pt) - - case metrics.CounterFloat64: - v := metric.Count() - - measurement := fmt.Sprintf("%s%s.count", namespace, name) - fields := map[string]interface{}{ - "value": v, - } - - pt := influxdb2.NewPoint(measurement, r.tags, fields, now) - r.write.WritePoint(pt) - - case metrics.Gauge: - ms := metric.Snapshot() - - measurement := fmt.Sprintf("%s%s.gauge", namespace, name) - fields := map[string]interface{}{ - "value": ms.Value(), - } - - pt := influxdb2.NewPoint(measurement, r.tags, fields, now) - r.write.WritePoint(pt) - - case metrics.GaugeFloat64: - ms := metric.Snapshot() - - measurement := fmt.Sprintf("%s%s.gauge", namespace, name) - fields := map[string]interface{}{ - "value": ms.Value(), - } - - pt := influxdb2.NewPoint(measurement, r.tags, fields, now) - r.write.WritePoint(pt) - - case metrics.Histogram: - ms := metric.Snapshot() - - if ms.Count() > 0 { - ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) - measurement := fmt.Sprintf("%s%s.histogram", namespace, name) - fields := map[string]interface{}{ - "count": ms.Count(), - "max": ms.Max(), - "mean": ms.Mean(), - "min": ms.Min(), - "stddev": ms.StdDev(), - "variance": ms.Variance(), - "p50": ps[0], - "p75": ps[1], - "p95": ps[2], - "p99": ps[3], - "p999": ps[4], - "p9999": ps[5], - } - - pt := influxdb2.NewPoint(measurement, r.tags, fields, now) - r.write.WritePoint(pt) - } - - case metrics.Meter: - ms := metric.Snapshot() - - measurement := fmt.Sprintf("%s%s.meter", namespace, name) - fields := map[string]interface{}{ - "count": ms.Count(), - "m1": ms.Rate1(), - "m5": ms.Rate5(), - "m15": ms.Rate15(), - "mean": ms.RateMean(), - } - - pt := influxdb2.NewPoint(measurement, r.tags, fields, now) - r.write.WritePoint(pt) - - case metrics.Timer: - ms := metric.Snapshot() - ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) - - measurement := fmt.Sprintf("%s%s.timer", namespace, name) - fields := map[string]interface{}{ - "count": ms.Count(), - "max": ms.Max(), - "mean": ms.Mean(), - "min": ms.Min(), - "stddev": ms.StdDev(), - "variance": ms.Variance(), - "p50": ps[0], - "p75": ps[1], - "p95": ps[2], - "p99": ps[3], - "p999": ps[4], - "p9999": ps[5], - "m1": ms.Rate1(), - "m5": ms.Rate5(), - "m15": ms.Rate15(), - "meanrate": ms.RateMean(), - } - - pt := influxdb2.NewPoint(measurement, r.tags, fields, now) - r.write.WritePoint(pt) - - case metrics.ResettingTimer: - t := metric.Snapshot() - - if len(t.Values()) > 0 { - ps := t.Percentiles([]float64{50, 95, 99}) - val := t.Values() - - measurement := fmt.Sprintf("%s%s.span", namespace, name) - fields := map[string]interface{}{ - "count": len(val), - "max": val[len(val)-1], - "mean": t.Mean(), - "min": val[0], - "p50": ps[0], - "p95": ps[1], - "p99": ps[2], - } - - pt := influxdb2.NewPoint(measurement, r.tags, fields, now) - r.write.WritePoint(pt) - } + measurement, fields := readMeter(r.namespace, name, i) + if fields == nil { + return } + pt := influxdb2.NewPoint(measurement, r.tags, fields, now) + r.write.WritePoint(pt) }) - // Force all unwritten data to be sent r.write.Flush() } From 54570300cc7734f12d0d6bf96d366bd9de9e618a Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 355/479] all: ensure resp.body closed (#26969) --- metrics/librato/client.go | 4 +++- node/node_test.go | 1 + p2p/simulations/mocker_test.go | 5 ++++- rpc/http_test.go | 1 + 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/metrics/librato/client.go b/metrics/librato/client.go index 89ecb9ec64da..053e1811d4ed 100644 --- a/metrics/librato/client.go +++ b/metrics/librato/client.go @@ -87,9 +87,11 @@ func (lc *LibratoClient) PostMetrics(batch Batch) (err error) { req.Header.Set("Content-Type", "application/json") req.SetBasicAuth(lc.Email, lc.Token) - if resp, err = http.DefaultClient.Do(req); err != nil { + resp, err = http.DefaultClient.Do(req) + if err != nil { return } + defer resp.Body.Close() if resp.StatusCode != http.StatusOK { var body []byte diff --git a/node/node_test.go b/node/node_test.go index df97c66c71ca..b733ea517492 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -651,5 +651,6 @@ func doHTTPRequest(t *testing.T, req *http.Request) *http.Response { if err != nil { t.Error("could not issue a GET request to the given endpoint", err) } + t.Cleanup(func() { resp.Body.Close() }) return resp } diff --git a/p2p/simulations/mocker_test.go b/p2p/simulations/mocker_test.go index 32b566ecee7e..8cc84f8e83b8 100644 --- a/p2p/simulations/mocker_test.go +++ b/p2p/simulations/mocker_test.go @@ -127,6 +127,7 @@ func TestMocker(t *testing.T) { if err != nil { t.Fatalf("Could not start mocker: %s", err) } + resp.Body.Close() if resp.StatusCode != 200 { t.Fatalf("Invalid Status Code received for starting mocker, expected 200, got %d", resp.StatusCode) } @@ -148,15 +149,17 @@ func TestMocker(t *testing.T) { if err != nil { t.Fatalf("Could not stop mocker: %s", err) } + resp.Body.Close() if resp.StatusCode != 200 { t.Fatalf("Invalid Status Code received for stopping mocker, expected 200, got %d", resp.StatusCode) } //reset the network - _, err = http.Post(s.URL+"/reset", "", nil) + resp, err = http.Post(s.URL+"/reset", "", nil) if err != nil { t.Fatalf("Could not reset network: %s", err) } + resp.Body.Close() //now the number of nodes in the network should be zero nodes_info, err = client.GetNodes() diff --git a/rpc/http_test.go b/rpc/http_test.go index 73920b44f09a..73124fb3508a 100644 --- a/rpc/http_test.go +++ b/rpc/http_test.go @@ -92,6 +92,7 @@ func confirmHTTPRequestYieldsStatusCode(t *testing.T, method, contentType, body if err != nil { t.Fatalf("request failed: %v", err) } + resp.Body.Close() confirmStatusCode(t, resp.StatusCode, expectedStatusCode) } From aa5e3e2c0886e52112ee6f18c8bfe106ca0a0cd9 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 356/479] metrics: make gauge_float64 and counter_float64 lock free (#27025) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Makes the float-gauges lock-free name old time/op new time/op delta CounterFloat64Parallel-8 1.45µs ±10% 0.85µs ± 6% -41.65% (p=0.008 n=5+5) --------- Co-authored-by: Exca-DK Co-authored-by: Martin Holst Swende --- metrics/counter_float64.go | 38 +++++++++++++++++--------------- metrics/counter_float_64_test.go | 24 +++++++++++++++++++- metrics/gauge_float64.go | 22 ++++++++---------- metrics/gauge_float64_test.go | 23 ++++++++++++++++++- 4 files changed, 74 insertions(+), 33 deletions(-) diff --git a/metrics/counter_float64.go b/metrics/counter_float64.go index f05f62fed632..d1197bb8e0ae 100644 --- a/metrics/counter_float64.go +++ b/metrics/counter_float64.go @@ -1,7 +1,8 @@ package metrics import ( - "sync" + "math" + "sync/atomic" ) // CounterFloat64 holds a float64 value that can be incremented and decremented. @@ -38,13 +39,13 @@ func NewCounterFloat64() CounterFloat64 { if !Enabled { return NilCounterFloat64{} } - return &StandardCounterFloat64{count: 0.0} + return &StandardCounterFloat64{} } // NewCounterFloat64Forced constructs a new StandardCounterFloat64 and returns it no matter if // the global switch is enabled or not. func NewCounterFloat64Forced() CounterFloat64 { - return &StandardCounterFloat64{count: 0.0} + return &StandardCounterFloat64{} } // NewRegisteredCounterFloat64 constructs and registers a new StandardCounterFloat64. @@ -113,41 +114,42 @@ func (NilCounterFloat64) Inc(i float64) {} func (NilCounterFloat64) Snapshot() CounterFloat64 { return NilCounterFloat64{} } // StandardCounterFloat64 is the standard implementation of a CounterFloat64 and uses the -// sync.Mutex package to manage a single float64 value. +// atomic to manage a single float64 value. type StandardCounterFloat64 struct { - mutex sync.Mutex - count float64 + floatBits atomic.Uint64 } // Clear sets the counter to zero. func (c *StandardCounterFloat64) Clear() { - c.mutex.Lock() - defer c.mutex.Unlock() - c.count = 0.0 + c.floatBits.Store(0) } // Count returns the current value. func (c *StandardCounterFloat64) Count() float64 { - c.mutex.Lock() - defer c.mutex.Unlock() - return c.count + return math.Float64frombits(c.floatBits.Load()) } // Dec decrements the counter by the given amount. func (c *StandardCounterFloat64) Dec(v float64) { - c.mutex.Lock() - defer c.mutex.Unlock() - c.count -= v + atomicAddFloat(&c.floatBits, -v) } // Inc increments the counter by the given amount. func (c *StandardCounterFloat64) Inc(v float64) { - c.mutex.Lock() - defer c.mutex.Unlock() - c.count += v + atomicAddFloat(&c.floatBits, v) } // Snapshot returns a read-only copy of the counter. func (c *StandardCounterFloat64) Snapshot() CounterFloat64 { return CounterFloat64Snapshot(c.Count()) } + +func atomicAddFloat(fbits *atomic.Uint64, v float64) { + for { + loadedBits := fbits.Load() + newBits := math.Float64bits(math.Float64frombits(loadedBits) + v) + if fbits.CompareAndSwap(loadedBits, newBits) { + break + } + } +} diff --git a/metrics/counter_float_64_test.go b/metrics/counter_float_64_test.go index 44d9b4c20c85..f17aca330cbe 100644 --- a/metrics/counter_float_64_test.go +++ b/metrics/counter_float_64_test.go @@ -1,6 +1,9 @@ package metrics -import "testing" +import ( + "sync" + "testing" +) func BenchmarkCounterFloat64(b *testing.B) { c := NewCounterFloat64() @@ -10,6 +13,25 @@ func BenchmarkCounterFloat64(b *testing.B) { } } +func BenchmarkCounterFloat64Parallel(b *testing.B) { + c := NewCounterFloat64() + b.ResetTimer() + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + for i := 0; i < b.N; i++ { + c.Inc(1.0) + } + wg.Done() + }() + } + wg.Wait() + if have, want := c.Count(), 10.0*float64(b.N); have != want { + b.Fatalf("have %f want %f", have, want) + } +} + func TestCounterFloat64Clear(t *testing.T) { c := NewCounterFloat64() c.Inc(1.0) diff --git a/metrics/gauge_float64.go b/metrics/gauge_float64.go index 66819c957774..237ff8036e01 100644 --- a/metrics/gauge_float64.go +++ b/metrics/gauge_float64.go @@ -1,6 +1,9 @@ package metrics -import "sync" +import ( + "math" + "sync/atomic" +) // GaugeFloat64s hold a float64 value that can be set arbitrarily. type GaugeFloat64 interface { @@ -23,9 +26,7 @@ func NewGaugeFloat64() GaugeFloat64 { if !Enabled { return NilGaugeFloat64{} } - return &StandardGaugeFloat64{ - value: 0.0, - } + return &StandardGaugeFloat64{} } // NewRegisteredGaugeFloat64 constructs and registers a new StandardGaugeFloat64. @@ -83,10 +84,9 @@ func (NilGaugeFloat64) Update(v float64) {} func (NilGaugeFloat64) Value() float64 { return 0.0 } // StandardGaugeFloat64 is the standard implementation of a GaugeFloat64 and uses -// sync.Mutex to manage a single float64 value. +// atomic to manage a single float64 value. type StandardGaugeFloat64 struct { - mutex sync.Mutex - value float64 + floatBits atomic.Uint64 } // Snapshot returns a read-only copy of the gauge. @@ -96,16 +96,12 @@ func (g *StandardGaugeFloat64) Snapshot() GaugeFloat64 { // Update updates the gauge's value. func (g *StandardGaugeFloat64) Update(v float64) { - g.mutex.Lock() - defer g.mutex.Unlock() - g.value = v + g.floatBits.Store(math.Float64bits(v)) } // Value returns the gauge's current value. func (g *StandardGaugeFloat64) Value() float64 { - g.mutex.Lock() - defer g.mutex.Unlock() - return g.value + return math.Float64frombits(g.floatBits.Load()) } // FunctionalGaugeFloat64 returns value from given function diff --git a/metrics/gauge_float64_test.go b/metrics/gauge_float64_test.go index 1ae598049229..0e11d803c4f6 100644 --- a/metrics/gauge_float64_test.go +++ b/metrics/gauge_float64_test.go @@ -1,6 +1,9 @@ package metrics -import "testing" +import ( + "sync" + "testing" +) func BenchmarkGaugeFloat64(b *testing.B) { g := NewGaugeFloat64() @@ -10,6 +13,24 @@ func BenchmarkGaugeFloat64(b *testing.B) { } } +func BenchmarkGaugeFloat64Parallel(b *testing.B) { + c := NewGaugeFloat64() + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + for i := 0; i < b.N; i++ { + c.Update(float64(i)) + } + wg.Done() + }() + } + wg.Wait() + if have, want := c.Value(), float64(b.N-1); have != want { + b.Fatalf("have %f want %f", have, want) + } +} + func TestGaugeFloat64(t *testing.T) { g := NewGaugeFloat64() g.Update(47.0) From 7015a9e1ccec0cb23b6eeb7cffb5a8e5f31e2ca0 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 357/479] metrics: use atomic type (#27121) --- metrics/counter.go | 14 +++++++------- metrics/ewma.go | 8 ++++---- metrics/gauge.go | 12 ++++++------ metrics/meter.go | 25 ++++++++++++++----------- 4 files changed, 31 insertions(+), 28 deletions(-) diff --git a/metrics/counter.go b/metrics/counter.go index 2f78c90d5c64..55e1c59540f6 100644 --- a/metrics/counter.go +++ b/metrics/counter.go @@ -38,13 +38,13 @@ func NewCounter() Counter { if !Enabled { return NilCounter{} } - return &StandardCounter{0} + return &StandardCounter{} } // NewCounterForced constructs a new StandardCounter and returns it no matter if // the global switch is enabled or not. func NewCounterForced() Counter { - return &StandardCounter{0} + return &StandardCounter{} } // NewRegisteredCounter constructs and registers a new StandardCounter. @@ -115,27 +115,27 @@ func (NilCounter) Snapshot() Counter { return NilCounter{} } // StandardCounter is the standard implementation of a Counter and uses the // sync/atomic package to manage a single int64 value. type StandardCounter struct { - count int64 + count atomic.Int64 } // Clear sets the counter to zero. func (c *StandardCounter) Clear() { - atomic.StoreInt64(&c.count, 0) + c.count.Store(0) } // Count returns the current count. func (c *StandardCounter) Count() int64 { - return atomic.LoadInt64(&c.count) + return c.count.Load() } // Dec decrements the counter by the given amount. func (c *StandardCounter) Dec(i int64) { - atomic.AddInt64(&c.count, -i) + c.count.Add(-i) } // Inc increments the counter by the given amount. func (c *StandardCounter) Inc(i int64) { - atomic.AddInt64(&c.count, i) + c.count.Add(i) } // Snapshot returns a read-only copy of the counter. diff --git a/metrics/ewma.go b/metrics/ewma.go index 039286493ebc..ed95cba19b4f 100644 --- a/metrics/ewma.go +++ b/metrics/ewma.go @@ -75,7 +75,7 @@ func (NilEWMA) Update(n int64) {} // of uncounted events and processes them on each tick. It uses the // sync/atomic package to manage uncounted events. type StandardEWMA struct { - uncounted int64 // /!\ this should be the first member to ensure 64-bit alignment + uncounted atomic.Int64 alpha float64 rate float64 init bool @@ -97,8 +97,8 @@ func (a *StandardEWMA) Snapshot() EWMA { // Tick ticks the clock to update the moving average. It assumes it is called // every five seconds. func (a *StandardEWMA) Tick() { - count := atomic.LoadInt64(&a.uncounted) - atomic.AddInt64(&a.uncounted, -count) + count := a.uncounted.Load() + a.uncounted.Add(-count) instantRate := float64(count) / float64(5*time.Second) a.mutex.Lock() defer a.mutex.Unlock() @@ -112,5 +112,5 @@ func (a *StandardEWMA) Tick() { // Update adds n uncounted events. func (a *StandardEWMA) Update(n int64) { - atomic.AddInt64(&a.uncounted, n) + a.uncounted.Add(n) } diff --git a/metrics/gauge.go b/metrics/gauge.go index b6b2758b0d13..81137d7f7c5e 100644 --- a/metrics/gauge.go +++ b/metrics/gauge.go @@ -25,7 +25,7 @@ func NewGauge() Gauge { if !Enabled { return NilGauge{} } - return &StandardGauge{0} + return &StandardGauge{} } // NewRegisteredGauge constructs and registers a new StandardGauge. @@ -101,7 +101,7 @@ func (NilGauge) Value() int64 { return 0 } // StandardGauge is the standard implementation of a Gauge and uses the // sync/atomic package to manage a single int64 value. type StandardGauge struct { - value int64 + value atomic.Int64 } // Snapshot returns a read-only copy of the gauge. @@ -111,22 +111,22 @@ func (g *StandardGauge) Snapshot() Gauge { // Update updates the gauge's value. func (g *StandardGauge) Update(v int64) { - atomic.StoreInt64(&g.value, v) + g.value.Store(v) } // Value returns the gauge's current value. func (g *StandardGauge) Value() int64 { - return atomic.LoadInt64(&g.value) + return g.value.Load() } // Dec decrements the gauge's current value by the given amount. func (g *StandardGauge) Dec(i int64) { - atomic.AddInt64(&g.value, -i) + g.value.Add(-i) } // Inc increments the gauge's current value by the given amount. func (g *StandardGauge) Inc(i int64) { - atomic.AddInt64(&g.value, i) + g.value.Add(i) } // FunctionalGauge returns value from given function diff --git a/metrics/meter.go b/metrics/meter.go index 60ae919d04db..e8564d6a5e76 100644 --- a/metrics/meter.go +++ b/metrics/meter.go @@ -101,11 +101,7 @@ func NewRegisteredMeterForced(name string, r Registry) Meter { // MeterSnapshot is a read-only copy of another Meter. type MeterSnapshot struct { - // WARNING: The `temp` field is accessed atomically. - // On 32 bit platforms, only 64-bit aligned fields can be atomic. The struct is - // guaranteed to be so aligned, so take advantage of that. For more information, - // see https://golang.org/pkg/sync/atomic/#pkg-note-BUG. - temp int64 + temp atomic.Int64 count int64 rate1, rate5, rate15, rateMean float64 } @@ -173,7 +169,7 @@ type StandardMeter struct { snapshot *MeterSnapshot a1, a5, a15 EWMA startTime time.Time - stopped uint32 + stopped atomic.Bool } func newStandardMeter() *StandardMeter { @@ -188,8 +184,8 @@ func newStandardMeter() *StandardMeter { // Stop stops the meter, Mark() will be a no-op if you use it after being stopped. func (m *StandardMeter) Stop() { - stopped := atomic.SwapUint32(&m.stopped, 1) - if stopped != 1 { + stopped := m.stopped.Swap(true) + if !stopped { arbiter.Lock() delete(arbiter.meters, m) arbiter.Unlock() @@ -207,7 +203,7 @@ func (m *StandardMeter) Count() int64 { // Mark records the occurrence of n events. func (m *StandardMeter) Mark(n int64) { - atomic.AddInt64(&m.snapshot.temp, n) + m.snapshot.temp.Add(n) } // Rate1 returns the one-minute moving average rate of events per second. @@ -241,7 +237,14 @@ func (m *StandardMeter) RateMean() float64 { // Snapshot returns a read-only copy of the meter. func (m *StandardMeter) Snapshot() Meter { m.lock.RLock() - snapshot := *m.snapshot + snapshot := MeterSnapshot{ + count: m.snapshot.count, + rate1: m.snapshot.rate1, + rate5: m.snapshot.rate5, + rate15: m.snapshot.rate15, + rateMean: m.snapshot.rateMean, + } + snapshot.temp.Store(m.snapshot.temp.Load()) m.lock.RUnlock() return &snapshot } @@ -257,7 +260,7 @@ func (m *StandardMeter) updateSnapshot() { func (m *StandardMeter) updateMeter() { // should only run with write lock held on m.lock - n := atomic.SwapInt64(&m.snapshot.temp, 0) + n := m.snapshot.temp.Swap(0) m.snapshot.count += n m.a1.Update(n) m.a5.Update(n) From 7d40ca6f3526fda11c39d4bdb067a3699b75cf28 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 358/479] metrics: use sync.map in registry (#27159) --- metrics/registry.go | 103 ++++++++++++++++++--------------------- metrics/registry_test.go | 25 ++++++++++ 2 files changed, 72 insertions(+), 56 deletions(-) diff --git a/metrics/registry.go b/metrics/registry.go index c550b363d0a1..88e192c0d196 100644 --- a/metrics/registry.go +++ b/metrics/registry.go @@ -45,21 +45,17 @@ type Registry interface { // Unregister the metric with the given name. Unregister(string) - - // Unregister all metrics. (Mostly for testing.) - UnregisterAll() } -// The standard implementation of a Registry is a mutex-protected map +// The standard implementation of a Registry uses sync.map // of names to metrics. type StandardRegistry struct { - metrics map[string]interface{} - mutex sync.Mutex + metrics sync.Map } // Create a new registry. func NewRegistry() Registry { - return &StandardRegistry{metrics: make(map[string]interface{})} + return &StandardRegistry{} } // Call the given function for each registered metric. @@ -71,9 +67,8 @@ func (r *StandardRegistry) Each(f func(string, interface{})) { // Get the metric by the given name or nil if none is registered. func (r *StandardRegistry) Get(name string) interface{} { - r.mutex.Lock() - defer r.mutex.Unlock() - return r.metrics[name] + item, _ := r.metrics.Load(name) + return item } // Gets an existing metric or creates and registers a new one. Threadsafe @@ -81,35 +76,48 @@ func (r *StandardRegistry) Get(name string) interface{} { // The interface can be the metric to register if not found in registry, // or a function returning the metric for lazy instantiation. func (r *StandardRegistry) GetOrRegister(name string, i interface{}) interface{} { - r.mutex.Lock() - defer r.mutex.Unlock() - if metric, ok := r.metrics[name]; ok { - return metric + // fast path + cached, ok := r.metrics.Load(name) + if ok { + return cached } if v := reflect.ValueOf(i); v.Kind() == reflect.Func { i = v.Call(nil)[0].Interface() } - r.register(name, i) - return i + item, _, ok := r.loadOrRegister(name, i) + if !ok { + return i + } + return item } // Register the given metric under the given name. Returns a DuplicateMetric // if a metric by the given name is already registered. func (r *StandardRegistry) Register(name string, i interface{}) error { - r.mutex.Lock() - defer r.mutex.Unlock() - return r.register(name, i) + // fast path + _, ok := r.metrics.Load(name) + if ok { + return DuplicateMetric(name) + } + + if v := reflect.ValueOf(i); v.Kind() == reflect.Func { + i = v.Call(nil)[0].Interface() + } + _, loaded, _ := r.loadOrRegister(name, i) + if loaded { + return DuplicateMetric(name) + } + return nil } // Run all registered healthchecks. func (r *StandardRegistry) RunHealthchecks() { - r.mutex.Lock() - defer r.mutex.Unlock() - for _, i := range r.metrics { - if h, ok := i.(Healthcheck); ok { + r.metrics.Range(func(key, value any) bool { + if h, ok := value.(Healthcheck); ok { h.Check() } - } + return true + }) } // GetAll metrics in the Registry @@ -177,45 +185,31 @@ func (r *StandardRegistry) GetAll() map[string]map[string]interface{} { // Unregister the metric with the given name. func (r *StandardRegistry) Unregister(name string) { - r.mutex.Lock() - defer r.mutex.Unlock() r.stop(name) - delete(r.metrics, name) -} - -// Unregister all metrics. (Mostly for testing.) -func (r *StandardRegistry) UnregisterAll() { - r.mutex.Lock() - defer r.mutex.Unlock() - for name := range r.metrics { - r.stop(name) - delete(r.metrics, name) - } + r.metrics.LoadAndDelete(name) } -func (r *StandardRegistry) register(name string, i interface{}) error { - if _, ok := r.metrics[name]; ok { - return DuplicateMetric(name) - } +func (r *StandardRegistry) loadOrRegister(name string, i interface{}) (interface{}, bool, bool) { switch i.(type) { case Counter, CounterFloat64, Gauge, GaugeFloat64, Healthcheck, Histogram, Meter, Timer, ResettingTimer: - r.metrics[name] = i + default: + return nil, false, false } - return nil + item, loaded := r.metrics.LoadOrStore(name, i) + return item, loaded, true } func (r *StandardRegistry) registered() map[string]interface{} { - r.mutex.Lock() - defer r.mutex.Unlock() - metrics := make(map[string]interface{}, len(r.metrics)) - for name, i := range r.metrics { - metrics[name] = i - } + metrics := make(map[string]interface{}) + r.metrics.Range(func(key, value any) bool { + metrics[key.(string)] = value + return true + }) return metrics } func (r *StandardRegistry) stop(name string) { - if i, ok := r.metrics[name]; ok { + if i, ok := r.metrics.Load(name); ok { if s, ok := i.(Stoppable); ok { s.Stop() } @@ -308,12 +302,9 @@ func (r *PrefixedRegistry) Unregister(name string) { r.underlying.Unregister(realName) } -// Unregister all metrics. (Mostly for testing.) -func (r *PrefixedRegistry) UnregisterAll() { - r.underlying.UnregisterAll() -} - -var DefaultRegistry Registry = NewRegistry() +var ( + DefaultRegistry = NewRegistry() +) // Call the given function for each registered metric. func Each(f func(string, interface{})) { diff --git a/metrics/registry_test.go b/metrics/registry_test.go index d277ae5c3e47..7cc5cf14fe55 100644 --- a/metrics/registry_test.go +++ b/metrics/registry_test.go @@ -1,6 +1,7 @@ package metrics import ( + "sync" "testing" ) @@ -13,6 +14,30 @@ func BenchmarkRegistry(b *testing.B) { } } +func BenchmarkRegistryGetOrRegisterParallel_8(b *testing.B) { + benchmarkRegistryGetOrRegisterParallel(b, 8) +} + +func BenchmarkRegistryGetOrRegisterParallel_32(b *testing.B) { + benchmarkRegistryGetOrRegisterParallel(b, 32) +} + +func benchmarkRegistryGetOrRegisterParallel(b *testing.B, amount int) { + r := NewRegistry() + b.ResetTimer() + var wg sync.WaitGroup + for i := 0; i < amount; i++ { + wg.Add(1) + go func() { + for i := 0; i < b.N; i++ { + r.GetOrRegister("foo", NewMeter) + } + wg.Done() + }() + } + wg.Wait() +} + func TestRegistry(t *testing.T) { r := NewRegistry() r.Register("foo", NewCounter()) From e524043a3b5897ef51292d3e0775e8a6113d67d0 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 359/479] metrics: use slices package for sorting (#27493 #27909) Co-authored-by: Felix Lange --- go.mod | 1 + go.sum | 2 ++ metrics/resetting_timer.go | 12 +++--------- metrics/sample.go | 15 +++++---------- metrics/writer.go | 20 +++++++------------- metrics/writer_test.go | 7 ++++--- 6 files changed, 22 insertions(+), 35 deletions(-) diff --git a/go.mod b/go.mod index 28a24f8b6b38..de84356c85ad 100644 --- a/go.mod +++ b/go.mod @@ -55,6 +55,7 @@ require ( github.com/mattn/go-isatty v0.0.17 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible github.com/urfave/cli/v2 v2.27.5 + golang.org/x/exp v0.0.0-20231006140011-7918f672742d gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) diff --git a/go.sum b/go.sum index 68019c92aba9..f1e8dd27aa3f 100644 --- a/go.sum +++ b/go.sum @@ -218,6 +218,8 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/metrics/resetting_timer.go b/metrics/resetting_timer.go index 57bcb31343fd..228963dc9416 100644 --- a/metrics/resetting_timer.go +++ b/metrics/resetting_timer.go @@ -2,9 +2,10 @@ package metrics import ( "math" - "sort" "sync" "time" + + "golang.org/x/exp/slices" ) // Initial slice capacity for the values stored in a ResettingTimer @@ -182,7 +183,7 @@ func (t *ResettingTimerSnapshot) Mean() float64 { } func (t *ResettingTimerSnapshot) calc(percentiles []float64) { - sort.Sort(Int64Slice(t.values)) + slices.Sort(t.values) count := len(t.values) if count > 0 { @@ -228,10 +229,3 @@ func (t *ResettingTimerSnapshot) calc(percentiles []float64) { t.calculated = true } - -// Int64Slice attaches the methods of sort.Interface to []int64, sorting in increasing order. -type Int64Slice []int64 - -func (s Int64Slice) Len() int { return len(s) } -func (s Int64Slice) Less(i, j int) bool { return s[i] < s[j] } -func (s Int64Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } diff --git a/metrics/sample.go b/metrics/sample.go index afcaa2118426..252a878f581b 100644 --- a/metrics/sample.go +++ b/metrics/sample.go @@ -3,9 +3,10 @@ package metrics import ( "math" "math/rand" - "sort" "sync" "time" + + "golang.org/x/exp/slices" ) const rescaleThreshold = time.Hour @@ -282,17 +283,17 @@ func SampleMin(values []int64) int64 { } // SamplePercentiles returns an arbitrary percentile of the slice of int64. -func SamplePercentile(values int64Slice, p float64) float64 { +func SamplePercentile(values []int64, p float64) float64 { return SamplePercentiles(values, []float64{p})[0] } // SamplePercentiles returns a slice of arbitrary percentiles of the slice of // int64. -func SamplePercentiles(values int64Slice, ps []float64) []float64 { +func SamplePercentiles(values []int64, ps []float64) []float64 { scores := make([]float64, len(ps)) size := len(values) if size > 0 { - sort.Sort(values) + slices.Sort(values) for i, p := range ps { pos := p * float64(size+1) if pos < 1.0 { @@ -633,9 +634,3 @@ func (h *expDecaySampleHeap) down(i, n int) { i = j } } - -type int64Slice []int64 - -func (p int64Slice) Len() int { return len(p) } -func (p int64Slice) Less(i, j int) bool { return p[i] < p[j] } -func (p int64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } diff --git a/metrics/writer.go b/metrics/writer.go index 256fbd14c9b9..82434e9d1d62 100644 --- a/metrics/writer.go +++ b/metrics/writer.go @@ -3,8 +3,10 @@ package metrics import ( "fmt" "io" - "sort" + "strings" "time" + + "golang.org/x/exp/slices" ) // Write sorts writes each metric in the given registry periodically to the @@ -18,12 +20,11 @@ func Write(r Registry, d time.Duration, w io.Writer) { // WriteOnce sorts and writes metrics in the given registry to the given // io.Writer. func WriteOnce(r Registry, w io.Writer) { - var namedMetrics namedMetricSlice + var namedMetrics []namedMetric r.Each(func(name string, i interface{}) { namedMetrics = append(namedMetrics, namedMetric{name, i}) }) - - sort.Sort(namedMetrics) + slices.SortFunc(namedMetrics, namedMetric.cmp) for _, namedMetric := range namedMetrics { switch metric := namedMetric.m.(type) { case Counter: @@ -91,13 +92,6 @@ type namedMetric struct { m interface{} } -// namedMetricSlice is a slice of namedMetrics that implements sort.Interface. -type namedMetricSlice []namedMetric - -func (nms namedMetricSlice) Len() int { return len(nms) } - -func (nms namedMetricSlice) Swap(i, j int) { nms[i], nms[j] = nms[j], nms[i] } - -func (nms namedMetricSlice) Less(i, j int) bool { - return nms[i].name < nms[j].name +func (m namedMetric) cmp(other namedMetric) int { + return strings.Compare(m.name, other.name) } diff --git a/metrics/writer_test.go b/metrics/writer_test.go index 1aacc287121b..8376bf8975c8 100644 --- a/metrics/writer_test.go +++ b/metrics/writer_test.go @@ -1,19 +1,20 @@ package metrics import ( - "sort" "testing" + + "golang.org/x/exp/slices" ) func TestMetricsSorting(t *testing.T) { - var namedMetrics = namedMetricSlice{ + var namedMetrics = []namedMetric{ {name: "zzz"}, {name: "bbb"}, {name: "fff"}, {name: "ggg"}, } - sort.Sort(namedMetrics) + slices.SortFunc(namedMetrics, namedMetric.cmp) for i, name := range []string{"bbb", "fff", "ggg", "zzz"} { if namedMetrics[i].name != name { t.Fail() From 51e49a869dee6cd408e13b0fa6d95c98b32da3f9 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 360/479] metrics: NilTimer should still run the function to be timed (#27723) --- metrics/timer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/timer.go b/metrics/timer.go index a63c9dfb6c2d..2e1a9be47295 100644 --- a/metrics/timer.go +++ b/metrics/timer.go @@ -123,7 +123,7 @@ func (NilTimer) Stop() {} func (NilTimer) Sum() int64 { return 0 } // Time is a no-op. -func (NilTimer) Time(func()) {} +func (NilTimer) Time(f func()) { f() } // Update is a no-op. func (NilTimer) Update(time.Duration) {} From d43f6c0cb0dc82a5f62ea3eab8e83f45cc178051 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 361/479] metrics: NilResettingTimer.Time should execute the timed function (#27724) --- metrics/resetting_timer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/resetting_timer.go b/metrics/resetting_timer.go index 228963dc9416..858984a32cc4 100644 --- a/metrics/resetting_timer.go +++ b/metrics/resetting_timer.go @@ -62,7 +62,7 @@ func (NilResettingTimer) Values() []int64 { return nil } func (NilResettingTimer) Snapshot() ResettingTimer { return NilResettingTimer{} } // Time is a no-op. -func (NilResettingTimer) Time(func()) {} +func (NilResettingTimer) Time(f func()) { f() } // Update is a no-op. func (NilResettingTimer) Update(time.Duration) {} From 35380508fce2f04c87202f103b4cb865a3f30715 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 362/479] metrics, cmd/geth: informational metrics (prometheus, influxdb, opentsb) (#24877) This chang creates a GaugeInfo metrics type for registering informational (textual) metrics, e.g. geth version number. It also improves the testing for backend-exporters, and uses a shared subpackage in 'internal' to provide sample datasets and ordered registry. Implements #21783 --------- Co-authored-by: Martin Holst Swende --- cmd/XDC/config.go | 2 +- cmd/utils/utils.go | 22 ++- core/blockchain.go | 6 + metrics/exp/exp.go | 20 +++ metrics/gauge_info.go | 144 +++++++++++++++++++ metrics/gauge_info_test.go | 75 ++++++++++ metrics/graphite.go | 2 + metrics/influxdb/influxdb.go | 7 + metrics/influxdb/influxdb_test.go | 114 +++++++++++++++ metrics/influxdb/influxdbv1.go | 15 +- metrics/influxdb/influxdbv2.go | 13 +- metrics/influxdb/testdata/influxdbv1.want | 9 ++ metrics/influxdb/testdata/influxdbv2.want | 9 ++ metrics/internal/sampledata.go | 64 +++++++++ metrics/librato/librato.go | 4 + metrics/log.go | 3 + metrics/meter.go | 10 ++ metrics/opentsdb.go | 27 ++-- metrics/opentsdb_test.go | 30 ++++ metrics/prometheus/collector.go | 46 ++++++ metrics/prometheus/collector_test.go | 146 +++++++------------- metrics/prometheus/prometheus.go | 20 +-- metrics/prometheus/testdata/prometheus.want | 48 +++++++ metrics/registry.go | 35 ++++- metrics/syslog.go | 2 + metrics/testdata/opentsb.want | 23 +++ metrics/writer.go | 3 + 27 files changed, 756 insertions(+), 143 deletions(-) create mode 100644 metrics/gauge_info.go create mode 100644 metrics/gauge_info_test.go create mode 100644 metrics/influxdb/influxdb_test.go create mode 100644 metrics/influxdb/testdata/influxdbv1.want create mode 100644 metrics/influxdb/testdata/influxdbv2.want create mode 100644 metrics/internal/sampledata.go create mode 100644 metrics/prometheus/testdata/prometheus.want create mode 100644 metrics/testdata/opentsb.want diff --git a/cmd/XDC/config.go b/cmd/XDC/config.go index 511ba21715a6..7dbc8774b895 100644 --- a/cmd/XDC/config.go +++ b/cmd/XDC/config.go @@ -237,7 +237,7 @@ func makeFullNode(ctx *cli.Context) (*node.Node, XDCConfig) { // Register XDCX's OrderBook service if requested. // enable in default utils.RegisterXDCXService(stack, &cfg.XDCX) - utils.RegisterEthService(stack, &cfg.Eth) + utils.RegisterEthService(stack, &cfg.Eth, cfg.Node.Version) // Add the Ethereum Stats daemon if requested. if cfg.Ethstats.URL != "" { diff --git a/cmd/utils/utils.go b/cmd/utils/utils.go index bc676c7b08ff..5afa50c32bbe 100644 --- a/cmd/utils/utils.go +++ b/cmd/utils/utils.go @@ -1,6 +1,10 @@ package utils import ( + "fmt" + "runtime" + "strings" + "github.com/XinFinOrg/XDPoSChain/XDCx" "github.com/XinFinOrg/XDPoSChain/XDCxlending" "github.com/XinFinOrg/XDPoSChain/eth" @@ -8,11 +12,12 @@ import ( "github.com/XinFinOrg/XDPoSChain/eth/ethconfig" "github.com/XinFinOrg/XDPoSChain/ethstats" "github.com/XinFinOrg/XDPoSChain/les" + "github.com/XinFinOrg/XDPoSChain/metrics" "github.com/XinFinOrg/XDPoSChain/node" ) // RegisterEthService adds an Ethereum client to the stack. -func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) { +func RegisterEthService(stack *node.Node, cfg *ethconfig.Config, version string) { var err error if cfg.SyncMode == downloader.LightSync { err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { @@ -29,6 +34,21 @@ func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) { ls, _ := les.NewLesServer(fullNode, cfg) fullNode.AddLesServer(ls) } + + // TODO: move the following code to function makeFullNode + // Ref: #21105, #22641, #23761, #24877 + // Create gauge with geth system and build information + var protos []string + for _, p := range fullNode.Protocols() { + protos = append(protos, fmt.Sprintf("%v/%d", p.Name, p.Version)) + } + metrics.NewRegisteredGaugeInfo("xdc/info", nil).Update(metrics.GaugeInfoValue{ + "arch": runtime.GOARCH, + "os": runtime.GOOS, + "version": version, // cfg.Node.Version + "eth_protocols": strings.Join(protos, ","), + }) + return fullNode, err }) } diff --git a/core/blockchain.go b/core/blockchain.go index d11a421978ca..f09fb31d670e 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -60,6 +60,8 @@ var ( headHeaderGauge = metrics.NewRegisteredGauge("chain/head/header", nil) headFastBlockGauge = metrics.NewRegisteredGauge("chain/head/receipt", nil) + chainInfoGauge = metrics.NewRegisteredGaugeInfo("chain/info", nil) + accountReadTimer = metrics.NewRegisteredTimer("chain/account/reads", nil) accountHashTimer = metrics.NewRegisteredTimer("chain/account/hashes", nil) accountUpdateTimer = metrics.NewRegisteredTimer("chain/account/updates", nil) @@ -244,6 +246,10 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par if bc.genesisBlock == nil { return nil, ErrNoGenesis } + + // Update chain info data metrics + chainInfoGauge.Update(metrics.GaugeInfoValue{"chain_id": bc.chainConfig.ChainId.String()}) + if err := bc.loadLastState(); err != nil { return nil, err } diff --git a/metrics/exp/exp.go b/metrics/exp/exp.go index 77b198e610fd..6091f2f848b5 100644 --- a/metrics/exp/exp.go +++ b/metrics/exp/exp.go @@ -95,6 +95,20 @@ func (exp *exp) getFloat(name string) *expvar.Float { return v } +func (exp *exp) getInfo(name string) *expvar.String { + var v *expvar.String + exp.expvarLock.Lock() + p := expvar.Get(name) + if p != nil { + v = p.(*expvar.String) + } else { + v = new(expvar.String) + expvar.Publish(name, v) + } + exp.expvarLock.Unlock() + return v +} + func (exp *exp) publishCounter(name string, metric metrics.Counter) { v := exp.getInt(name) v.Set(metric.Count()) @@ -113,6 +127,10 @@ func (exp *exp) publishGaugeFloat64(name string, metric metrics.GaugeFloat64) { exp.getFloat(name).Set(metric.Value()) } +func (exp *exp) publishGaugeInfo(name string, metric metrics.GaugeInfo) { + exp.getInfo(name).Set(metric.Value().String()) +} + func (exp *exp) publishHistogram(name string, metric metrics.Histogram) { h := metric.Snapshot() ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) @@ -178,6 +196,8 @@ func (exp *exp) syncToExpvar() { exp.publishGauge(name, i) case metrics.GaugeFloat64: exp.publishGaugeFloat64(name, i) + case metrics.GaugeInfo: + exp.publishGaugeInfo(name, i) case metrics.Histogram: exp.publishHistogram(name, i) case metrics.Meter: diff --git a/metrics/gauge_info.go b/metrics/gauge_info.go new file mode 100644 index 000000000000..f1b2075939e7 --- /dev/null +++ b/metrics/gauge_info.go @@ -0,0 +1,144 @@ +package metrics + +import ( + "encoding/json" + "sync" +) + +// GaugeInfos hold a GaugeInfoValue value that can be set arbitrarily. +type GaugeInfo interface { + Snapshot() GaugeInfo + Update(GaugeInfoValue) + Value() GaugeInfoValue +} + +// GaugeInfoValue is a mappng of (string) keys to (string) values +type GaugeInfoValue map[string]string + +func (val GaugeInfoValue) String() string { + data, _ := json.Marshal(val) + return string(data) +} + +// GetOrRegisterGaugeInfo returns an existing GaugeInfo or constructs and registers a +// new StandardGaugeInfo. +func GetOrRegisterGaugeInfo(name string, r Registry) GaugeInfo { + if nil == r { + r = DefaultRegistry + } + return r.GetOrRegister(name, NewGaugeInfo()).(GaugeInfo) +} + +// NewGaugeInfo constructs a new StandardGaugeInfo. +func NewGaugeInfo() GaugeInfo { + if !Enabled { + return NilGaugeInfo{} + } + return &StandardGaugeInfo{ + value: GaugeInfoValue{}, + } +} + +// NewRegisteredGaugeInfo constructs and registers a new StandardGaugeInfo. +func NewRegisteredGaugeInfo(name string, r Registry) GaugeInfo { + c := NewGaugeInfo() + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// NewFunctionalGauge constructs a new FunctionalGauge. +func NewFunctionalGaugeInfo(f func() GaugeInfoValue) GaugeInfo { + if !Enabled { + return NilGaugeInfo{} + } + return &FunctionalGaugeInfo{value: f} +} + +// NewRegisteredFunctionalGauge constructs and registers a new StandardGauge. +func NewRegisteredFunctionalGaugeInfo(name string, r Registry, f func() GaugeInfoValue) GaugeInfo { + c := NewFunctionalGaugeInfo(f) + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// GaugeInfoSnapshot is a read-only copy of another GaugeInfo. +type GaugeInfoSnapshot GaugeInfoValue + +// Snapshot returns the snapshot. +func (g GaugeInfoSnapshot) Snapshot() GaugeInfo { return g } + +// Update panics. +func (GaugeInfoSnapshot) Update(GaugeInfoValue) { + panic("Update called on a GaugeInfoSnapshot") +} + +// Value returns the value at the time the snapshot was taken. +func (g GaugeInfoSnapshot) Value() GaugeInfoValue { return GaugeInfoValue(g) } + +// NilGauge is a no-op Gauge. +type NilGaugeInfo struct{} + +// Snapshot is a no-op. +func (NilGaugeInfo) Snapshot() GaugeInfo { return NilGaugeInfo{} } + +// Update is a no-op. +func (NilGaugeInfo) Update(v GaugeInfoValue) {} + +// Value is a no-op. +func (NilGaugeInfo) Value() GaugeInfoValue { return GaugeInfoValue{} } + +// StandardGaugeInfo is the standard implementation of a GaugeInfo and uses +// sync.Mutex to manage a single string value. +type StandardGaugeInfo struct { + mutex sync.Mutex + value GaugeInfoValue +} + +// Snapshot returns a read-only copy of the gauge. +func (g *StandardGaugeInfo) Snapshot() GaugeInfo { + return GaugeInfoSnapshot(g.Value()) +} + +// Update updates the gauge's value. +func (g *StandardGaugeInfo) Update(v GaugeInfoValue) { + g.mutex.Lock() + defer g.mutex.Unlock() + g.value = v +} + +// Value returns the gauge's current value. +func (g *StandardGaugeInfo) Value() GaugeInfoValue { + g.mutex.Lock() + defer g.mutex.Unlock() + return g.value +} + +// FunctionalGaugeInfo returns value from given function +type FunctionalGaugeInfo struct { + value func() GaugeInfoValue +} + +// Value returns the gauge's current value. +func (g FunctionalGaugeInfo) Value() GaugeInfoValue { + return g.value() +} + +// Value returns the gauge's current value in JSON string format +func (g FunctionalGaugeInfo) ValueJsonString() string { + data, _ := json.Marshal(g.value()) + return string(data) +} + +// Snapshot returns the snapshot. +func (g FunctionalGaugeInfo) Snapshot() GaugeInfo { return GaugeInfoSnapshot(g.Value()) } + +// Update panics. +func (FunctionalGaugeInfo) Update(GaugeInfoValue) { + panic("Update called on a FunctionalGaugeInfo") +} diff --git a/metrics/gauge_info_test.go b/metrics/gauge_info_test.go new file mode 100644 index 000000000000..4227a6a85fab --- /dev/null +++ b/metrics/gauge_info_test.go @@ -0,0 +1,75 @@ +package metrics + +import ( + "strconv" + "testing" +) + +func TestGaugeInfoJsonString(t *testing.T) { + g := NewGaugeInfo() + g.Update(GaugeInfoValue{ + "chain_id": "5", + "anotherKey": "any_string_value", + "third_key": "anything", + }, + ) + want := `{"anotherKey":"any_string_value","chain_id":"5","third_key":"anything"}` + if have := g.Value().String(); have != want { + t.Errorf("\nhave: %v\nwant: %v\n", have, want) + } +} + +func TestGaugeInfoSnapshot(t *testing.T) { + g := NewGaugeInfo() + g.Update(GaugeInfoValue{"value": "original"}) + snapshot := g.Snapshot() // Snapshot @chainid 5 + g.Update(GaugeInfoValue{"value": "updated"}) + // The 'g' should be updated + if have, want := g.Value().String(), `{"value":"updated"}`; have != want { + t.Errorf("\nhave: %v\nwant: %v\n", have, want) + } + // Snapshot should be unupdated + if have, want := snapshot.Value().String(), `{"value":"original"}`; have != want { + t.Errorf("\nhave: %v\nwant: %v\n", have, want) + } +} + +func TestGetOrRegisterGaugeInfo(t *testing.T) { + r := NewRegistry() + NewRegisteredGaugeInfo("foo", r).Update( + GaugeInfoValue{"chain_id": "5"}) + g := GetOrRegisterGaugeInfo("foo", r) + if have, want := g.Value().String(), `{"chain_id":"5"}`; have != want { + t.Errorf("have\n%v\nwant\n%v\n", have, want) + } +} + +func TestFunctionalGaugeInfo(t *testing.T) { + info := GaugeInfoValue{"chain_id": "0"} + counter := 1 + // A "functional" gauge invokes the method to obtain the value + fg := NewFunctionalGaugeInfo(func() GaugeInfoValue { + info["chain_id"] = strconv.Itoa(counter) + counter++ + return info + }) + fg.Value() + fg.Value() + if have, want := info["chain_id"], "2"; have != want { + t.Errorf("have %v want %v", have, want) + } +} + +func TestGetOrRegisterFunctionalGaugeInfo(t *testing.T) { + r := NewRegistry() + NewRegisteredFunctionalGaugeInfo("foo", r, func() GaugeInfoValue { + return GaugeInfoValue{ + "chain_id": "5", + } + }) + want := `{"chain_id":"5"}` + have := GetOrRegisterGaugeInfo("foo", r).Value().String() + if have != want { + t.Errorf("have\n%v\nwant\n%v\n", have, want) + } +} diff --git a/metrics/graphite.go b/metrics/graphite.go index 29f72b0c4181..4e3dd3b3b894 100644 --- a/metrics/graphite.go +++ b/metrics/graphite.go @@ -73,6 +73,8 @@ func graphite(c *GraphiteConfig) error { fmt.Fprintf(w, "%s.%s.value %d %d\n", c.Prefix, name, metric.Value(), now) case GaugeFloat64: fmt.Fprintf(w, "%s.%s.value %f %d\n", c.Prefix, name, metric.Value(), now) + case GaugeInfo: + fmt.Fprintf(w, "%s.%s.value %s %d\n", c.Prefix, name, metric.Value().String(), now) case Histogram: h := metric.Snapshot() ps := h.Percentiles(c.Percentiles) diff --git a/metrics/influxdb/influxdb.go b/metrics/influxdb/influxdb.go index 1e7fb9e11dd4..a1a210033ff4 100644 --- a/metrics/influxdb/influxdb.go +++ b/metrics/influxdb/influxdb.go @@ -32,6 +32,13 @@ func readMeter(namespace, name string, i interface{}) (string, map[string]interf "value": metric.Snapshot().Value(), } return measurement, fields + case metrics.GaugeInfo: + ms := metric.Snapshot() + measurement := fmt.Sprintf("%s%s.gauge", namespace, name) + fields := map[string]interface{}{ + "value": ms.Value().String(), + } + return measurement, fields case metrics.Histogram: ms := metric.Snapshot() if ms.Count() <= 0 { diff --git a/metrics/influxdb/influxdb_test.go b/metrics/influxdb/influxdb_test.go new file mode 100644 index 000000000000..dca3d9ab3264 --- /dev/null +++ b/metrics/influxdb/influxdb_test.go @@ -0,0 +1,114 @@ +// Copyright 2023 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package influxdb + +import ( + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" + "os" + "strings" + "testing" + + "github.com/XinFinOrg/XDPoSChain/metrics" + "github.com/XinFinOrg/XDPoSChain/metrics/internal" + influxdb2 "github.com/influxdata/influxdb-client-go/v2" +) + +func TestMain(m *testing.M) { + metrics.Enabled = true + os.Exit(m.Run()) +} + +func TestExampleV1(t *testing.T) { + r := internal.ExampleMetrics() + var have, want string + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + haveB, _ := io.ReadAll(r.Body) + have = string(haveB) + r.Body.Close() + })) + defer ts.Close() + u, _ := url.Parse(ts.URL) + rep := &reporter{ + reg: r, + url: *u, + namespace: "goth.", + } + if err := rep.makeClient(); err != nil { + t.Fatal(err) + } + if err := rep.send(978307200); err != nil { + t.Fatal(err) + } + if wantB, err := os.ReadFile("./testdata/influxdbv1.want"); err != nil { + t.Fatal(err) + } else { + want = string(wantB) + } + if have != want { + t.Errorf("\nhave:\n%v\nwant:\n%v\n", have, want) + t.Logf("have vs want:\n%v", findFirstDiffPos(have, want)) + } +} + +func TestExampleV2(t *testing.T) { + r := internal.ExampleMetrics() + var have, want string + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + haveB, _ := io.ReadAll(r.Body) + have = string(haveB) + r.Body.Close() + })) + defer ts.Close() + + rep := &v2Reporter{ + reg: r, + endpoint: ts.URL, + namespace: "goth.", + } + rep.client = influxdb2.NewClient(rep.endpoint, rep.token) + defer rep.client.Close() + rep.write = rep.client.WriteAPI(rep.organization, rep.bucket) + + rep.send(978307200) + + if wantB, err := os.ReadFile("./testdata/influxdbv2.want"); err != nil { + t.Fatal(err) + } else { + want = string(wantB) + } + if have != want { + t.Errorf("\nhave:\n%v\nwant:\n%v\n", have, want) + t.Logf("have vs want:\n %v", findFirstDiffPos(have, want)) + } +} + +func findFirstDiffPos(a, b string) string { + yy := strings.Split(b, "\n") + for i, x := range strings.Split(a, "\n") { + if i >= len(yy) { + return fmt.Sprintf("have:%d: %s\nwant:%d: ", i, x, i) + } + if y := yy[i]; x != y { + return fmt.Sprintf("have:%d: %s\nwant:%d: %s", i, x, i, y) + } + } + return "" +} diff --git a/metrics/influxdb/influxdbv1.go b/metrics/influxdb/influxdbv1.go index 40ae9a3c10b7..7b55872fbad3 100644 --- a/metrics/influxdb/influxdbv1.go +++ b/metrics/influxdb/influxdbv1.go @@ -79,7 +79,7 @@ func InfluxDBWithTagsOnce(r metrics.Registry, url, database, username, password, return fmt.Errorf("unable to make InfluxDB client. err: %v", err) } - if err := rep.send(); err != nil { + if err := rep.send(0); err != nil { return fmt.Errorf("unable to send to InfluxDB. err: %v", err) } @@ -107,7 +107,7 @@ func (r *reporter) run() { for { select { case <-intervalTicker.C: - if err := r.send(); err != nil { + if err := r.send(0); err != nil { log.Warn("Unable to send to InfluxDB", "err", err) } case <-pingTicker.C: @@ -123,7 +123,9 @@ func (r *reporter) run() { } } -func (r *reporter) send() error { +// send sends the measurements. If provided tstamp is >0, it is used. Otherwise, +// a 'fresh' timestamp is used. +func (r *reporter) send(tstamp int64) error { bps, err := client.NewBatchPoints( client.BatchPointsConfig{ Database: r.database, @@ -132,7 +134,12 @@ func (r *reporter) send() error { return err } r.reg.Each(func(name string, i interface{}) { - now := time.Now() + var now time.Time + if tstamp <= 0 { + now = time.Now() + } else { + now = time.Unix(tstamp, 0) + } measurement, fields := readMeter(r.namespace, name, i) if fields == nil { return diff --git a/metrics/influxdb/influxdbv2.go b/metrics/influxdb/influxdbv2.go index 15ea660d865c..d45e9c4d45be 100644 --- a/metrics/influxdb/influxdbv2.go +++ b/metrics/influxdb/influxdbv2.go @@ -64,7 +64,7 @@ func (r *v2Reporter) run() { for { select { case <-intervalTicker.C: - r.send() + r.send(0) case <-pingTicker.C: _, err := r.client.Health(context.Background()) if err != nil { @@ -74,9 +74,16 @@ func (r *v2Reporter) run() { } } -func (r *v2Reporter) send() { +// send sends the measurements. If provided tstamp is >0, it is used. Otherwise, +// a 'fresh' timestamp is used. +func (r *v2Reporter) send(tstamp int64) { r.reg.Each(func(name string, i interface{}) { - now := time.Now() + var now time.Time + if tstamp <= 0 { + now = time.Now() + } else { + now = time.Unix(tstamp, 0) + } measurement, fields := readMeter(r.namespace, name, i) if fields == nil { return diff --git a/metrics/influxdb/testdata/influxdbv1.want b/metrics/influxdb/testdata/influxdbv1.want new file mode 100644 index 000000000000..5efffb959504 --- /dev/null +++ b/metrics/influxdb/testdata/influxdbv1.want @@ -0,0 +1,9 @@ +goth.test/counter.count value=12345 978307200000000000 +goth.test/counter_float64.count value=54321.98 978307200000000000 +goth.test/gauge.gauge value=23456i 978307200000000000 +goth.test/gauge_float64.gauge value=34567.89 978307200000000000 +goth.test/gauge_info.gauge value="{\"arch\":\"amd64\",\"commit\":\"7caa2d8163ae3132c1c2d6978c76610caee2d949\",\"os\":\"linux\",\"protocol_versions\":\"64 65 66\",\"version\":\"1.10.18-unstable\"}" 978307200000000000 +goth.test/histogram.histogram count=3i,max=3i,mean=2,min=1i,p25=1,p50=2,p75=3,p95=3,p99=3,p999=3,p9999=3,stddev=0.816496580927726,variance=0.6666666666666666 978307200000000000 +goth.test/meter.meter count=0i,m1=0,m15=0,m5=0,mean=0 978307200000000000 +goth.test/resetting_timer.span count=6i,max=120000000i,mean=30000000,min=10000000i,p50=12000000i,p95=120000000i,p99=120000000i 978307200000000000 +goth.test/timer.timer count=6i,m1=0,m15=0,m5=0,max=120000000i,mean=38333333.333333336,meanrate=0,min=20000000i,p50=22500000,p75=48000000,p95=120000000,p99=120000000,p999=120000000,p9999=120000000,stddev=36545253.529775314,variance=1335555555555555.2 978307200000000000 diff --git a/metrics/influxdb/testdata/influxdbv2.want b/metrics/influxdb/testdata/influxdbv2.want new file mode 100644 index 000000000000..5efffb959504 --- /dev/null +++ b/metrics/influxdb/testdata/influxdbv2.want @@ -0,0 +1,9 @@ +goth.test/counter.count value=12345 978307200000000000 +goth.test/counter_float64.count value=54321.98 978307200000000000 +goth.test/gauge.gauge value=23456i 978307200000000000 +goth.test/gauge_float64.gauge value=34567.89 978307200000000000 +goth.test/gauge_info.gauge value="{\"arch\":\"amd64\",\"commit\":\"7caa2d8163ae3132c1c2d6978c76610caee2d949\",\"os\":\"linux\",\"protocol_versions\":\"64 65 66\",\"version\":\"1.10.18-unstable\"}" 978307200000000000 +goth.test/histogram.histogram count=3i,max=3i,mean=2,min=1i,p25=1,p50=2,p75=3,p95=3,p99=3,p999=3,p9999=3,stddev=0.816496580927726,variance=0.6666666666666666 978307200000000000 +goth.test/meter.meter count=0i,m1=0,m15=0,m5=0,mean=0 978307200000000000 +goth.test/resetting_timer.span count=6i,max=120000000i,mean=30000000,min=10000000i,p50=12000000i,p95=120000000i,p99=120000000i 978307200000000000 +goth.test/timer.timer count=6i,m1=0,m15=0,m5=0,max=120000000i,mean=38333333.333333336,meanrate=0,min=20000000i,p50=22500000,p75=48000000,p95=120000000,p99=120000000,p999=120000000,p9999=120000000,stddev=36545253.529775314,variance=1335555555555555.2 978307200000000000 diff --git a/metrics/internal/sampledata.go b/metrics/internal/sampledata.go new file mode 100644 index 000000000000..6b03af394f70 --- /dev/null +++ b/metrics/internal/sampledata.go @@ -0,0 +1,64 @@ +// Copyright 2023 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package internal + +import ( + "time" + + "github.com/XinFinOrg/XDPoSChain/metrics" +) + +// ExampleMetrics returns an ordered registry populated with a sample of metrics. +func ExampleMetrics() metrics.Registry { + var registry = metrics.NewOrderedRegistry() + + metrics.NewRegisteredCounterFloat64("test/counter", registry).Inc(12345) + metrics.NewRegisteredCounterFloat64("test/counter_float64", registry).Inc(54321.98) + metrics.NewRegisteredGauge("test/gauge", registry).Update(23456) + metrics.NewRegisteredGaugeFloat64("test/gauge_float64", registry).Update(34567.89) + metrics.NewRegisteredGaugeInfo("test/gauge_info", registry).Update( + metrics.GaugeInfoValue{ + "version": "1.10.18-unstable", + "arch": "amd64", + "os": "linux", + "commit": "7caa2d8163ae3132c1c2d6978c76610caee2d949", + "protocol_versions": "64 65 66", + }) + metrics.NewRegisteredHistogram("test/histogram", registry, metrics.NewSampleSnapshot(3, []int64{1, 2, 3})) + registry.Register("test/meter", metrics.NewInactiveMeter()) + { + timer := metrics.NewRegisteredResettingTimer("test/resetting_timer", registry) + timer.Update(10 * time.Millisecond) + timer.Update(11 * time.Millisecond) + timer.Update(12 * time.Millisecond) + timer.Update(120 * time.Millisecond) + timer.Update(13 * time.Millisecond) + timer.Update(14 * time.Millisecond) + } + { + timer := metrics.NewRegisteredTimer("test/timer", registry) + timer.Update(20 * time.Millisecond) + timer.Update(21 * time.Millisecond) + timer.Update(22 * time.Millisecond) + timer.Update(120 * time.Millisecond) + timer.Update(23 * time.Millisecond) + timer.Update(24 * time.Millisecond) + timer.Stop() + } + registry.Register("test/empty_resetting_timer", metrics.NewResettingTimer().Snapshot()) + return registry +} diff --git a/metrics/librato/librato.go b/metrics/librato/librato.go index 7fa4c56b5db3..2df2085c6f95 100644 --- a/metrics/librato/librato.go +++ b/metrics/librato/librato.go @@ -126,6 +126,10 @@ func (re *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Ba measurement[Name] = name measurement[Value] = m.Value() snapshot.Gauges = append(snapshot.Gauges, measurement) + case metrics.GaugeInfo: + measurement[Name] = name + measurement[Value] = m.Value() + snapshot.Gauges = append(snapshot.Gauges, measurement) case metrics.Histogram: if m.Count() > 0 { gauges := make([]Measurement, histogramGaugeCount) diff --git a/metrics/log.go b/metrics/log.go index d1ce627a8378..d71a1c3d9c66 100644 --- a/metrics/log.go +++ b/metrics/log.go @@ -33,6 +33,9 @@ func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) { case GaugeFloat64: l.Printf("gauge %s\n", name) l.Printf(" value: %f\n", metric.Value()) + case GaugeInfo: + l.Printf("gauge %s\n", name) + l.Printf(" value: %s\n", metric.Value()) case Healthcheck: metric.Check() l.Printf("healthcheck %s\n", name) diff --git a/metrics/meter.go b/metrics/meter.go index e8564d6a5e76..8a89dc4275f1 100644 --- a/metrics/meter.go +++ b/metrics/meter.go @@ -58,6 +58,16 @@ func NewMeter() Meter { return m } +// NewInactiveMeter returns a meter but does not start any goroutines. This +// method is mainly intended for testing. +func NewInactiveMeter() Meter { + if !Enabled { + return NilMeter{} + } + m := newStandardMeter() + return m +} + // NewMeterForced constructs a new StandardMeter and launches a goroutine no matter // the global switch is enabled or not. // Be sure to call Stop() once the meter is of no use to allow for garbage collection. diff --git a/metrics/opentsdb.go b/metrics/opentsdb.go index c9fd2e75d5e5..4d2e209238fa 100644 --- a/metrics/opentsdb.go +++ b/metrics/opentsdb.go @@ -3,6 +3,7 @@ package metrics import ( "bufio" "fmt" + "io" "log" "net" "os" @@ -57,16 +58,10 @@ func getShortHostname() string { return shortHostName } -func openTSDB(c *OpenTSDBConfig) error { - shortHostname := getShortHostname() - now := time.Now().Unix() +// writeRegistry writes the registry-metrics on the opentsb format. +func (c *OpenTSDBConfig) writeRegistry(w io.Writer, now int64, shortHostname string) { du := float64(c.DurationUnit) - conn, err := net.DialTCP("tcp", nil, c.Addr) - if nil != err { - return err - } - defer conn.Close() - w := bufio.NewWriter(conn) + c.Registry.Each(func(name string, i interface{}) { switch metric := i.(type) { case Counter: @@ -77,6 +72,8 @@ func openTSDB(c *OpenTSDBConfig) error { fmt.Fprintf(w, "put %s.%s.value %d %d host=%s\n", c.Prefix, name, now, metric.Value(), shortHostname) case GaugeFloat64: fmt.Fprintf(w, "put %s.%s.value %d %f host=%s\n", c.Prefix, name, now, metric.Value(), shortHostname) + case GaugeInfo: + fmt.Fprintf(w, "put %s.%s.value %d %s host=%s\n", c.Prefix, name, now, metric.Value().String(), shortHostname) case Histogram: h := metric.Snapshot() ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) @@ -115,7 +112,17 @@ func openTSDB(c *OpenTSDBConfig) error { fmt.Fprintf(w, "put %s.%s.fifteen-minute %d %.2f host=%s\n", c.Prefix, name, now, t.Rate15(), shortHostname) fmt.Fprintf(w, "put %s.%s.mean-rate %d %.2f host=%s\n", c.Prefix, name, now, t.RateMean(), shortHostname) } - w.Flush() }) +} + +func openTSDB(c *OpenTSDBConfig) error { + conn, err := net.DialTCP("tcp", nil, c.Addr) + if nil != err { + return err + } + defer conn.Close() + w := bufio.NewWriter(conn) + c.writeRegistry(w, time.Now().Unix(), getShortHostname()) + w.Flush() return nil } diff --git a/metrics/opentsdb_test.go b/metrics/opentsdb_test.go index c43728960ed5..c02b98af061e 100644 --- a/metrics/opentsdb_test.go +++ b/metrics/opentsdb_test.go @@ -2,6 +2,9 @@ package metrics import ( "net" + "os" + "strings" + "testing" "time" ) @@ -19,3 +22,30 @@ func ExampleOpenTSDBWithConfig() { DurationUnit: time.Millisecond, }) } + +func TestExampleOpenTSB(t *testing.T) { + r := NewOrderedRegistry() + NewRegisteredGaugeInfo("foo", r).Update(GaugeInfoValue{"chain_id": "5"}) + NewRegisteredGaugeFloat64("pi", r).Update(3.14) + NewRegisteredCounter("months", r).Inc(12) + NewRegisteredCounterFloat64("tau", r).Inc(1.57) + NewRegisteredMeter("elite", r).Mark(1337) + NewRegisteredTimer("second", r).Update(time.Second) + NewRegisteredCounterFloat64("tau", r).Inc(1.57) + NewRegisteredCounterFloat64("tau", r).Inc(1.57) + + w := new(strings.Builder) + (&OpenTSDBConfig{ + Registry: r, + DurationUnit: time.Millisecond, + Prefix: "pre", + }).writeRegistry(w, 978307200, "hal9000") + + wantB, err := os.ReadFile("./testdata/opentsb.want") + if err != nil { + t.Fatal(err) + } + if have, want := w.String(), string(wantB); have != want { + t.Errorf("\nhave:\n%v\nwant:\n%v\n", have, want) + } +} diff --git a/metrics/prometheus/collector.go b/metrics/prometheus/collector.go index b7c238f601d7..2187438df8ee 100644 --- a/metrics/prometheus/collector.go +++ b/metrics/prometheus/collector.go @@ -19,6 +19,7 @@ package prometheus import ( "bytes" "fmt" + "sort" "strconv" "strings" @@ -46,6 +47,34 @@ func newCollector() *collector { } } +// Add adds the metric i to the collector. This method returns an error if the +// metric type is not supported/known. +func (c *collector) Add(name string, i any) error { + switch m := i.(type) { + case metrics.Counter: + c.addCounter(name, m.Snapshot()) + case metrics.CounterFloat64: + c.addCounterFloat64(name, m.Snapshot()) + case metrics.Gauge: + c.addGauge(name, m.Snapshot()) + case metrics.GaugeFloat64: + c.addGaugeFloat64(name, m.Snapshot()) + case metrics.GaugeInfo: + c.addGaugeInfo(name, m.Snapshot()) + case metrics.Histogram: + c.addHistogram(name, m.Snapshot()) + case metrics.Meter: + c.addMeter(name, m.Snapshot()) + case metrics.Timer: + c.addTimer(name, m.Snapshot()) + case metrics.ResettingTimer: + c.addResettingTimer(name, m.Snapshot()) + default: + return fmt.Errorf("unknown prometheus metric type %T", i) + } + return nil +} + func (c *collector) addCounter(name string, m metrics.Counter) { c.writeGaugeCounter(name, m.Count()) } @@ -62,6 +91,10 @@ func (c *collector) addGaugeFloat64(name string, m metrics.GaugeFloat64) { c.writeGaugeCounter(name, m.Value()) } +func (c *collector) addGaugeInfo(name string, m metrics.GaugeInfo) { + c.writeGaugeInfo(name, m.Value()) +} + func (c *collector) addHistogram(name string, m metrics.Histogram) { pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999} ps := m.Percentiles(pv) @@ -102,6 +135,19 @@ func (c *collector) addResettingTimer(name string, m metrics.ResettingTimer) { c.buff.WriteRune('\n') } +func (c *collector) writeGaugeInfo(name string, value metrics.GaugeInfoValue) { + name = mutateKey(name) + c.buff.WriteString(fmt.Sprintf(typeGaugeTpl, name)) + c.buff.WriteString(name) + c.buff.WriteString(" ") + var kvs []string + for k, v := range value { + kvs = append(kvs, fmt.Sprintf("%v=%q", k, v)) + } + sort.Strings(kvs) + c.buff.WriteString(fmt.Sprintf("{%v} 1\n\n", strings.Join(kvs, ", "))) +} + func (c *collector) writeGaugeCounter(name string, value interface{}) { name = mutateKey(name) c.buff.WriteString(fmt.Sprintf(typeGaugeTpl, name)) diff --git a/metrics/prometheus/collector_test.go b/metrics/prometheus/collector_test.go index eb6dbe603f99..b7a17ff1011e 100644 --- a/metrics/prometheus/collector_test.go +++ b/metrics/prometheus/collector_test.go @@ -1,11 +1,29 @@ +// Copyright 2023 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + package prometheus import ( + "fmt" "os" + "strings" "testing" - "time" "github.com/XinFinOrg/XDPoSChain/metrics" + "github.com/XinFinOrg/XDPoSChain/metrics/internal" ) func TestMain(m *testing.M) { @@ -14,104 +32,34 @@ func TestMain(m *testing.M) { } func TestCollector(t *testing.T) { - c := newCollector() - - counter := metrics.NewCounter() - counter.Inc(12345) - c.addCounter("test/counter", counter) - - counterfloat64 := metrics.NewCounterFloat64() - counterfloat64.Inc(54321.98) - c.addCounterFloat64("test/counter_float64", counterfloat64) - - gauge := metrics.NewGauge() - gauge.Update(23456) - c.addGauge("test/gauge", gauge) - - gaugeFloat64 := metrics.NewGaugeFloat64() - gaugeFloat64.Update(34567.89) - c.addGaugeFloat64("test/gauge_float64", gaugeFloat64) - - histogram := metrics.NewHistogram(&metrics.NilSample{}) - c.addHistogram("test/histogram", histogram) - - meter := metrics.NewMeter() - defer meter.Stop() - meter.Mark(9999999) - c.addMeter("test/meter", meter) - - timer := metrics.NewTimer() - defer timer.Stop() - timer.Update(20 * time.Millisecond) - timer.Update(21 * time.Millisecond) - timer.Update(22 * time.Millisecond) - timer.Update(120 * time.Millisecond) - timer.Update(23 * time.Millisecond) - timer.Update(24 * time.Millisecond) - c.addTimer("test/timer", timer) - - resettingTimer := metrics.NewResettingTimer() - resettingTimer.Update(10 * time.Millisecond) - resettingTimer.Update(11 * time.Millisecond) - resettingTimer.Update(12 * time.Millisecond) - resettingTimer.Update(120 * time.Millisecond) - resettingTimer.Update(13 * time.Millisecond) - resettingTimer.Update(14 * time.Millisecond) - c.addResettingTimer("test/resetting_timer", resettingTimer.Snapshot()) - - emptyResettingTimer := metrics.NewResettingTimer().Snapshot() - c.addResettingTimer("test/empty_resetting_timer", emptyResettingTimer) - - const expectedOutput = `# TYPE test_counter gauge -test_counter 12345 - -# TYPE test_counter_float64 gauge -test_counter_float64 54321.98 - -# TYPE test_gauge gauge -test_gauge 23456 - -# TYPE test_gauge_float64 gauge -test_gauge_float64 34567.89 - -# TYPE test_histogram_count counter -test_histogram_count 0 - -# TYPE test_histogram summary -test_histogram {quantile="0.5"} 0 -test_histogram {quantile="0.75"} 0 -test_histogram {quantile="0.95"} 0 -test_histogram {quantile="0.99"} 0 -test_histogram {quantile="0.999"} 0 -test_histogram {quantile="0.9999"} 0 - -# TYPE test_meter gauge -test_meter 9999999 - -# TYPE test_timer_count counter -test_timer_count 6 - -# TYPE test_timer summary -test_timer {quantile="0.5"} 2.25e+07 -test_timer {quantile="0.75"} 4.8e+07 -test_timer {quantile="0.95"} 1.2e+08 -test_timer {quantile="0.99"} 1.2e+08 -test_timer {quantile="0.999"} 1.2e+08 -test_timer {quantile="0.9999"} 1.2e+08 - -# TYPE test_resetting_timer_count counter -test_resetting_timer_count 6 - -# TYPE test_resetting_timer summary -test_resetting_timer {quantile="0.50"} 12000000 -test_resetting_timer {quantile="0.95"} 120000000 -test_resetting_timer {quantile="0.99"} 120000000 + var ( + c = newCollector() + want string + ) + internal.ExampleMetrics().Each(func(name string, i interface{}) { + c.Add(name, i) + }) + if wantB, err := os.ReadFile("./testdata/prometheus.want"); err != nil { + t.Fatal(err) + } else { + want = string(wantB) + } + if have := c.buff.String(); have != want { + t.Logf("have\n%v", have) + t.Logf("have vs want:\n%v", findFirstDiffPos(have, want)) + t.Fatalf("unexpected collector output") + } +} -` - exp := c.buff.String() - if exp != expectedOutput { - t.Log("Expected Output:\n", expectedOutput) - t.Log("Actual Output:\n", exp) - t.Fatal("unexpected collector output") +func findFirstDiffPos(a, b string) string { + yy := strings.Split(b, "\n") + for i, x := range strings.Split(a, "\n") { + if i >= len(yy) { + return fmt.Sprintf("a:%d: %s\nb:%d: ", i, x, i) + } + if y := yy[i]; x != y { + return fmt.Sprintf("a:%d: %s\nb:%d: %s", i, x, i, y) + } } + return "" } diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go index 2d999e493dc8..d11db3bae3eb 100644 --- a/metrics/prometheus/prometheus.go +++ b/metrics/prometheus/prometheus.go @@ -41,25 +41,7 @@ func Handler(reg metrics.Registry) http.Handler { for _, name := range names { i := reg.Get(name) - - switch m := i.(type) { - case metrics.Counter: - c.addCounter(name, m.Snapshot()) - case metrics.CounterFloat64: - c.addCounterFloat64(name, m.Snapshot()) - case metrics.Gauge: - c.addGauge(name, m.Snapshot()) - case metrics.GaugeFloat64: - c.addGaugeFloat64(name, m.Snapshot()) - case metrics.Histogram: - c.addHistogram(name, m.Snapshot()) - case metrics.Meter: - c.addMeter(name, m.Snapshot()) - case metrics.Timer: - c.addTimer(name, m.Snapshot()) - case metrics.ResettingTimer: - c.addResettingTimer(name, m.Snapshot()) - default: + if err := c.Add(name, i); err != nil { log.Warn("Unknown Prometheus metric type", "type", fmt.Sprintf("%T", i)) } } diff --git a/metrics/prometheus/testdata/prometheus.want b/metrics/prometheus/testdata/prometheus.want new file mode 100644 index 000000000000..f35496e61d31 --- /dev/null +++ b/metrics/prometheus/testdata/prometheus.want @@ -0,0 +1,48 @@ +# TYPE test_counter gauge +test_counter 12345 + +# TYPE test_counter_float64 gauge +test_counter_float64 54321.98 + +# TYPE test_gauge gauge +test_gauge 23456 + +# TYPE test_gauge_float64 gauge +test_gauge_float64 34567.89 + +# TYPE test_gauge_info gauge +test_gauge_info {arch="amd64", commit="7caa2d8163ae3132c1c2d6978c76610caee2d949", os="linux", protocol_versions="64 65 66", version="1.10.18-unstable"} 1 + +# TYPE test_histogram_count counter +test_histogram_count 3 + +# TYPE test_histogram summary +test_histogram {quantile="0.5"} 2 +test_histogram {quantile="0.75"} 3 +test_histogram {quantile="0.95"} 3 +test_histogram {quantile="0.99"} 3 +test_histogram {quantile="0.999"} 3 +test_histogram {quantile="0.9999"} 3 + +# TYPE test_meter gauge +test_meter 0 + +# TYPE test_resetting_timer_count counter +test_resetting_timer_count 6 + +# TYPE test_resetting_timer summary +test_resetting_timer {quantile="0.50"} 12000000 +test_resetting_timer {quantile="0.95"} 120000000 +test_resetting_timer {quantile="0.99"} 120000000 + +# TYPE test_timer_count counter +test_timer_count 6 + +# TYPE test_timer summary +test_timer {quantile="0.5"} 2.25e+07 +test_timer {quantile="0.75"} 4.8e+07 +test_timer {quantile="0.95"} 1.2e+08 +test_timer {quantile="0.99"} 1.2e+08 +test_timer {quantile="0.999"} 1.2e+08 +test_timer {quantile="0.9999"} 1.2e+08 + diff --git a/metrics/registry.go b/metrics/registry.go index 88e192c0d196..0b54f2dc92c8 100644 --- a/metrics/registry.go +++ b/metrics/registry.go @@ -3,6 +3,7 @@ package metrics import ( "fmt" "reflect" + "sort" "strings" "sync" ) @@ -47,17 +48,39 @@ type Registry interface { Unregister(string) } +type orderedRegistry struct { + StandardRegistry +} + +// Call the given function for each registered metric. +func (r *orderedRegistry) Each(f func(string, interface{})) { + var names []string + reg := r.registered() + for name := range reg { + names = append(names, name) + } + sort.Strings(names) + for _, name := range names { + f(name, reg[name]) + } +} + +// NewRegistry creates a new registry. +func NewRegistry() Registry { + return new(StandardRegistry) +} + +// NewOrderedRegistry creates a new ordered registry (for testing). +func NewOrderedRegistry() Registry { + return new(orderedRegistry) +} + // The standard implementation of a Registry uses sync.map // of names to metrics. type StandardRegistry struct { metrics sync.Map } -// Create a new registry. -func NewRegistry() Registry { - return &StandardRegistry{} -} - // Call the given function for each registered metric. func (r *StandardRegistry) Each(f func(string, interface{})) { for name, i := range r.registered() { @@ -191,7 +214,7 @@ func (r *StandardRegistry) Unregister(name string) { func (r *StandardRegistry) loadOrRegister(name string, i interface{}) (interface{}, bool, bool) { switch i.(type) { - case Counter, CounterFloat64, Gauge, GaugeFloat64, Healthcheck, Histogram, Meter, Timer, ResettingTimer: + case Counter, CounterFloat64, Gauge, GaugeFloat64, GaugeInfo, Healthcheck, Histogram, Meter, Timer, ResettingTimer: default: return nil, false, false } diff --git a/metrics/syslog.go b/metrics/syslog.go index f23b07e199f3..76c849056757 100644 --- a/metrics/syslog.go +++ b/metrics/syslog.go @@ -23,6 +23,8 @@ func Syslog(r Registry, d time.Duration, w *syslog.Writer) { w.Info(fmt.Sprintf("gauge %s: value: %d", name, metric.Value())) case GaugeFloat64: w.Info(fmt.Sprintf("gauge %s: value: %f", name, metric.Value())) + case GaugeInfo: + w.Info(fmt.Sprintf("gauge %s: value: %s", name, metric.Value())) case Healthcheck: metric.Check() w.Info(fmt.Sprintf("healthcheck %s: error: %v", name, metric.Error())) diff --git a/metrics/testdata/opentsb.want b/metrics/testdata/opentsb.want new file mode 100644 index 000000000000..c8e40a525042 --- /dev/null +++ b/metrics/testdata/opentsb.want @@ -0,0 +1,23 @@ +put pre.elite.count 978307200 0 host=hal9000 +put pre.elite.one-minute 978307200 0.00 host=hal9000 +put pre.elite.five-minute 978307200 0.00 host=hal9000 +put pre.elite.fifteen-minute 978307200 0.00 host=hal9000 +put pre.elite.mean 978307200 0.00 host=hal9000 +put pre.foo.value 978307200 {"chain_id":"5"} host=hal9000 +put pre.months.count 978307200 12 host=hal9000 +put pre.pi.value 978307200 3.140000 host=hal9000 +put pre.second.count 978307200 1 host=hal9000 +put pre.second.min 978307200 1000 host=hal9000 +put pre.second.max 978307200 1000 host=hal9000 +put pre.second.mean 978307200 1000.00 host=hal9000 +put pre.second.std-dev 978307200 0.00 host=hal9000 +put pre.second.50-percentile 978307200 1000.00 host=hal9000 +put pre.second.75-percentile 978307200 1000.00 host=hal9000 +put pre.second.95-percentile 978307200 1000.00 host=hal9000 +put pre.second.99-percentile 978307200 1000.00 host=hal9000 +put pre.second.999-percentile 978307200 1000.00 host=hal9000 +put pre.second.one-minute 978307200 0.00 host=hal9000 +put pre.second.five-minute 978307200 0.00 host=hal9000 +put pre.second.fifteen-minute 978307200 0.00 host=hal9000 +put pre.second.mean-rate 978307200 0.00 host=hal9000 +put pre.tau.count 978307200 1.570000 host=hal9000 diff --git a/metrics/writer.go b/metrics/writer.go index 82434e9d1d62..ec2e4f8c6a60 100644 --- a/metrics/writer.go +++ b/metrics/writer.go @@ -39,6 +39,9 @@ func WriteOnce(r Registry, w io.Writer) { case GaugeFloat64: fmt.Fprintf(w, "gauge %s\n", namedMetric.name) fmt.Fprintf(w, " value: %f\n", metric.Value()) + case GaugeInfo: + fmt.Fprintf(w, "gauge %s\n", namedMetric.name) + fmt.Fprintf(w, " value: %s\n", metric.Value().String()) case Healthcheck: metric.Check() fmt.Fprintf(w, "healthcheck %s\n", namedMetric.name) From 1fe46b8d3f3bd212d3446aa90dae32d5067649ed Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 363/479] metrics/librato: rename receiver re to rep --- metrics/librato/librato.go | 46 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/metrics/librato/librato.go b/metrics/librato/librato.go index 2df2085c6f95..87c3ab6a9088 100644 --- a/metrics/librato/librato.go +++ b/metrics/librato/librato.go @@ -40,15 +40,15 @@ func Librato(r metrics.Registry, d time.Duration, e string, t string, s string, NewReporter(r, d, e, t, s, p, u).Run() } -func (re *Reporter) Run() { +func (rep *Reporter) Run() { log.Printf("WARNING: This client has been DEPRECATED! It has been moved to https://github.com/mihasya/go-metrics-librato and will be removed from rcrowley/go-metrics on August 5th 2015") - ticker := time.NewTicker(re.Interval) + ticker := time.NewTicker(rep.Interval) defer ticker.Stop() - metricsApi := &LibratoClient{re.Email, re.Token} + metricsApi := &LibratoClient{rep.Email, rep.Token} for now := range ticker.C { var metrics Batch var err error - if metrics, err = re.BuildRequest(now, re.Registry); err != nil { + if metrics, err = rep.BuildRequest(now, rep.Registry); err != nil { log.Printf("ERROR constructing librato request body %s", err) continue } @@ -80,21 +80,21 @@ func sumSquaresTimer(t metrics.Timer) float64 { return sumSquares } -func (re *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Batch, err error) { +func (rep *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Batch, err error) { snapshot = Batch{ // coerce timestamps to a stepping fn so that they line up in Librato graphs - MeasureTime: (now.Unix() / re.intervalSec) * re.intervalSec, - Source: re.Source, + MeasureTime: (now.Unix() / rep.intervalSec) * rep.intervalSec, + Source: rep.Source, } snapshot.Gauges = make([]Measurement, 0) snapshot.Counters = make([]Measurement, 0) - histogramGaugeCount := 1 + len(re.Percentiles) + histogramGaugeCount := 1 + len(rep.Percentiles) r.Each(func(name string, metric interface{}) { - if re.Namespace != "" { - name = fmt.Sprintf("%s.%s", re.Namespace, name) + if rep.Namespace != "" { + name = fmt.Sprintf("%s.%s", rep.Namespace, name) } measurement := Measurement{} - measurement[Period] = re.Interval.Seconds() + measurement[Period] = rep.Interval.Seconds() switch m := metric.(type) { case metrics.Counter: if m.Count() > 0 { @@ -141,7 +141,7 @@ func (re *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Ba measurement[Sum] = float64(s.Sum()) measurement[SumSquares] = sumSquares(s) gauges[0] = measurement - for i, p := range re.Percentiles { + for i, p := range rep.Percentiles { gauges[i+1] = Measurement{ Name: fmt.Sprintf("%s.%.2f", measurement[Name], p), Value: s.Percentile(p), @@ -158,7 +158,7 @@ func (re *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Ba Measurement{ Name: fmt.Sprintf("%s.%s", name, "1min"), Value: m.Rate1(), - Period: int64(re.Interval.Seconds()), + Period: int64(rep.Interval.Seconds()), Attributes: map[string]interface{}{ DisplayUnitsLong: Operations, DisplayUnitsShort: OperationsShort, @@ -168,7 +168,7 @@ func (re *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Ba Measurement{ Name: fmt.Sprintf("%s.%s", name, "5min"), Value: m.Rate5(), - Period: int64(re.Interval.Seconds()), + Period: int64(rep.Interval.Seconds()), Attributes: map[string]interface{}{ DisplayUnitsLong: Operations, DisplayUnitsShort: OperationsShort, @@ -178,7 +178,7 @@ func (re *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Ba Measurement{ Name: fmt.Sprintf("%s.%s", name, "15min"), Value: m.Rate15(), - Period: int64(re.Interval.Seconds()), + Period: int64(rep.Interval.Seconds()), Attributes: map[string]interface{}{ DisplayUnitsLong: Operations, DisplayUnitsShort: OperationsShort, @@ -200,15 +200,15 @@ func (re *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Ba Max: float64(m.Max()), Min: float64(m.Min()), SumSquares: sumSquaresTimer(m), - Period: int64(re.Interval.Seconds()), - Attributes: re.TimerAttributes, + Period: int64(rep.Interval.Seconds()), + Attributes: rep.TimerAttributes, } - for i, p := range re.Percentiles { + for i, p := range rep.Percentiles { gauges[i+1] = Measurement{ Name: fmt.Sprintf("%s.timer.%2.0f", name, p*100), Value: m.Percentile(p), - Period: int64(re.Interval.Seconds()), - Attributes: re.TimerAttributes, + Period: int64(rep.Interval.Seconds()), + Attributes: rep.TimerAttributes, } } snapshot.Gauges = append(snapshot.Gauges, gauges...) @@ -216,7 +216,7 @@ func (re *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Ba Measurement{ Name: fmt.Sprintf("%s.%s", name, "rate.1min"), Value: m.Rate1(), - Period: int64(re.Interval.Seconds()), + Period: int64(rep.Interval.Seconds()), Attributes: map[string]interface{}{ DisplayUnitsLong: Operations, DisplayUnitsShort: OperationsShort, @@ -226,7 +226,7 @@ func (re *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Ba Measurement{ Name: fmt.Sprintf("%s.%s", name, "rate.5min"), Value: m.Rate5(), - Period: int64(re.Interval.Seconds()), + Period: int64(rep.Interval.Seconds()), Attributes: map[string]interface{}{ DisplayUnitsLong: Operations, DisplayUnitsShort: OperationsShort, @@ -236,7 +236,7 @@ func (re *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Ba Measurement{ Name: fmt.Sprintf("%s.%s", name, "rate.15min"), Value: m.Rate15(), - Period: int64(re.Interval.Seconds()), + Period: int64(rep.Interval.Seconds()), Attributes: map[string]interface{}{ DisplayUnitsLong: Operations, DisplayUnitsShort: OperationsShort, From cc4ea7a68594c9292a05437895e774a15780931a Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 364/479] metrics: refactor metrics (#28035) This change includes a lot of things, listed below. The interfaces have been split up into one write-interface and one read-interface, with `Snapshot` being the gateway from write to read. This simplifies the semantics _a lot_. Example of splitting up an interface into one readonly 'snapshot' part, and one updatable writeonly part: ```golang type MeterSnapshot interface { Count() int64 Rate1() float64 Rate5() float64 Rate15() float64 RateMean() float64 } // Meters count events to produce exponentially-weighted moving average rates // at one-, five-, and fifteen-minutes and a mean rate. type Meter interface { Mark(int64) Snapshot() MeterSnapshot Stop() } ``` This PR makes the concurrency model clearer. We have actual meters and snapshot of meters. The `meter` is the thing which can be accessed from the registry, and updates can be made to it. - For all `meters`, (`Gauge`, `Timer` etc), it is assumed that they are accessed by different threads, making updates. Therefore, all `meters` update-methods (`Inc`, `Add`, `Update`, `Clear` etc) need to be concurrency-safe. - All `meters` have a `Snapshot()` method. This method is _usually_ called from one thread, a backend-exporter. But it's fully possible to have several exporters simultaneously: therefore this method should also be concurrency-safe. TLDR: `meter`s are accessible via registry, all their methods must be concurrency-safe. For all `Snapshot`s, it is assumed that an individual exporter-thread has obtained a `meter` from the registry, and called the `Snapshot` method to obtain a readonly snapshot. This snapshot is _not_ guaranteed to be concurrency-safe. There's no need for a snapshot to be concurrency-safe, since exporters should not share snapshots. Note, though: that by happenstance a lot of the snapshots _are_ concurrency-safe, being unmutable minimal representations of a value. Only the more complex ones are _not_ threadsafe, those that lazily calculate things like `Variance()`, `Mean()`. Example of how a background exporter typically works, obtaining the snapshot and sequentially accessing the non-threadsafe methods in it: ```golang ms := metric.Snapshot() ... fields := map[string]interface{}{ "count": ms.Count(), "max": ms.Max(), "mean": ms.Mean(), "min": ms.Min(), "stddev": ms.StdDev(), "variance": ms.Variance(), ``` TLDR: `snapshots` are not guaranteed to be concurrency-safe (but often are). I also changed the `Sample` type: previously, it iterated the samples fully every time `Mean()`,`Sum()`, `Min()` or `Max()` was invoked. Since we now have readonly base data, we can just iterate it once, in the constructor, and set all four values at once. The same thing has been done for runtimehistogram. Back when ResettingTImer was implemented, as part of https://github.com/ethereum/go-ethereum/pull/15910, Anton implemented a `Percentiles` on the new type. However, the method did not conform to the other existing types which also had a `Percentiles`. 1. The existing ones, on input, took `0.5` to mean `50%`. Anton used `50` to mean `50%`. 2. The existing ones returned `float64` outputs, thus interpolating between values. A value-set of `0, 10`, at `50%` would return `5`, whereas Anton's would return either `0` or `10`. This PR removes the 'new' version, and uses only the 'legacy' percentiles, also for the ResettingTimer type. The resetting timer snapshot was also defined so that it would expose the internal values. This has been removed, and getters for `Max, Min, Mean` have been added instead. A lot of types were exported, but do not need to be. This PR unexports quite a lot of them. metrics: refactor metrics (28035) --- consensus/ethash/ethash.go | 2 +- metrics/counter.go | 72 +--- metrics/counter_float64.go | 61 +-- metrics/counter_float_64_test.go | 16 +- metrics/counter_test.go | 14 +- metrics/doc.go | 4 - metrics/ewma.go | 91 ++--- metrics/ewma_test.go | 233 +++-------- metrics/exp/exp.go | 32 +- metrics/gauge.go | 116 ++---- metrics/gauge_float64.go | 80 +--- metrics/gauge_float64_test.go | 33 +- metrics/gauge_info.go | 88 +--- metrics/gauge_info_test.go | 49 +-- metrics/gauge_test.go | 39 +- metrics/graphite.go | 10 +- metrics/histogram.go | 158 +------ metrics/histogram_test.go | 8 +- metrics/inactive.go | 48 +++ metrics/influxdb/influxdb.go | 21 +- metrics/influxdb/influxdb_test.go | 2 +- metrics/influxdb/testdata/influxdbv1.want | 4 +- metrics/influxdb/testdata/influxdbv2.want | 4 +- metrics/internal/sampledata.go | 33 +- metrics/internal/sampledata_test.go | 27 ++ metrics/librato/librato.go | 73 ++-- metrics/log.go | 10 +- metrics/meter.go | 207 ++-------- metrics/meter_test.go | 30 +- metrics/metrics.go | 6 + metrics/metrics_test.go | 4 +- metrics/opentsdb.go | 10 +- metrics/opentsdb_test.go | 15 + metrics/prometheus/collector.go | 25 +- metrics/prometheus/collector_test.go | 4 +- metrics/prometheus/testdata/prometheus.want | 28 +- metrics/registry.go | 8 +- metrics/registry_test.go | 4 +- metrics/resetting_sample.go | 2 +- metrics/resetting_timer.go | 200 ++++----- metrics/resetting_timer_test.go | 141 +++++-- metrics/runtimehistogram.go | 218 +++++----- metrics/runtimehistogram_test.go | 37 +- metrics/sample.go | 432 ++++++-------------- metrics/sample_test.go | 150 +++---- metrics/syslog.go | 10 +- metrics/testdata/opentsb.want | 2 +- metrics/timer.go | 214 ++-------- metrics/timer_test.go | 10 +- metrics/writer.go | 10 +- 50 files changed, 1107 insertions(+), 1988 deletions(-) delete mode 100644 metrics/doc.go create mode 100644 metrics/inactive.go create mode 100644 metrics/internal/sampledata_test.go diff --git a/consensus/ethash/ethash.go b/consensus/ethash/ethash.go index a65468f57103..98141b29393c 100644 --- a/consensus/ethash/ethash.go +++ b/consensus/ethash/ethash.go @@ -562,7 +562,7 @@ func (ethash *Ethash) SetThreads(threads int) { // Hashrate implements PoW, returning the measured rate of the search invocations // per second over the last minute. func (ethash *Ethash) Hashrate() float64 { - return ethash.hashrate.Rate1() + return ethash.hashrate.Snapshot().Rate1() } // APIs implements consensus.Engine, returning the user facing RPC APIs. Currently diff --git a/metrics/counter.go b/metrics/counter.go index 55e1c59540f6..cb81599c215a 100644 --- a/metrics/counter.go +++ b/metrics/counter.go @@ -4,13 +4,16 @@ import ( "sync/atomic" ) +type CounterSnapshot interface { + Count() int64 +} + // Counters hold an int64 value that can be incremented and decremented. type Counter interface { Clear() - Count() int64 Dec(int64) Inc(int64) - Snapshot() Counter + Snapshot() CounterSnapshot } // GetOrRegisterCounter returns an existing Counter or constructs and registers @@ -38,13 +41,13 @@ func NewCounter() Counter { if !Enabled { return NilCounter{} } - return &StandardCounter{} + return new(StandardCounter) } // NewCounterForced constructs a new StandardCounter and returns it no matter if // the global switch is enabled or not. func NewCounterForced() Counter { - return &StandardCounter{} + return new(StandardCounter) } // NewRegisteredCounter constructs and registers a new StandardCounter. @@ -70,75 +73,40 @@ func NewRegisteredCounterForced(name string, r Registry) Counter { return c } -// CounterSnapshot is a read-only copy of another Counter. -type CounterSnapshot int64 - -// Clear panics. -func (CounterSnapshot) Clear() { - panic("Clear called on a CounterSnapshot") -} +// counterSnapshot is a read-only copy of another Counter. +type counterSnapshot int64 // Count returns the count at the time the snapshot was taken. -func (c CounterSnapshot) Count() int64 { return int64(c) } - -// Dec panics. -func (CounterSnapshot) Dec(int64) { - panic("Dec called on a CounterSnapshot") -} - -// Inc panics. -func (CounterSnapshot) Inc(int64) { - panic("Inc called on a CounterSnapshot") -} - -// Snapshot returns the snapshot. -func (c CounterSnapshot) Snapshot() Counter { return c } +func (c counterSnapshot) Count() int64 { return int64(c) } // NilCounter is a no-op Counter. type NilCounter struct{} -// Clear is a no-op. -func (NilCounter) Clear() {} - -// Count is a no-op. -func (NilCounter) Count() int64 { return 0 } - -// Dec is a no-op. -func (NilCounter) Dec(i int64) {} - -// Inc is a no-op. -func (NilCounter) Inc(i int64) {} - -// Snapshot is a no-op. -func (NilCounter) Snapshot() Counter { return NilCounter{} } +func (NilCounter) Clear() {} +func (NilCounter) Dec(i int64) {} +func (NilCounter) Inc(i int64) {} +func (NilCounter) Snapshot() CounterSnapshot { return (*emptySnapshot)(nil) } // StandardCounter is the standard implementation of a Counter and uses the // sync/atomic package to manage a single int64 value. -type StandardCounter struct { - count atomic.Int64 -} +type StandardCounter atomic.Int64 // Clear sets the counter to zero. func (c *StandardCounter) Clear() { - c.count.Store(0) -} - -// Count returns the current count. -func (c *StandardCounter) Count() int64 { - return c.count.Load() + (*atomic.Int64)(c).Store(0) } // Dec decrements the counter by the given amount. func (c *StandardCounter) Dec(i int64) { - c.count.Add(-i) + (*atomic.Int64)(c).Add(-i) } // Inc increments the counter by the given amount. func (c *StandardCounter) Inc(i int64) { - c.count.Add(i) + (*atomic.Int64)(c).Add(i) } // Snapshot returns a read-only copy of the counter. -func (c *StandardCounter) Snapshot() Counter { - return CounterSnapshot(c.Count()) +func (c *StandardCounter) Snapshot() CounterSnapshot { + return counterSnapshot((*atomic.Int64)(c).Load()) } diff --git a/metrics/counter_float64.go b/metrics/counter_float64.go index d1197bb8e0ae..15c81494efb8 100644 --- a/metrics/counter_float64.go +++ b/metrics/counter_float64.go @@ -5,13 +5,16 @@ import ( "sync/atomic" ) +type CounterFloat64Snapshot interface { + Count() float64 +} + // CounterFloat64 holds a float64 value that can be incremented and decremented. type CounterFloat64 interface { Clear() - Count() float64 Dec(float64) Inc(float64) - Snapshot() CounterFloat64 + Snapshot() CounterFloat64Snapshot } // GetOrRegisterCounterFloat64 returns an existing CounterFloat64 or constructs and registers @@ -71,47 +74,19 @@ func NewRegisteredCounterFloat64Forced(name string, r Registry) CounterFloat64 { return c } -// CounterFloat64Snapshot is a read-only copy of another CounterFloat64. -type CounterFloat64Snapshot float64 - -// Clear panics. -func (CounterFloat64Snapshot) Clear() { - panic("Clear called on a CounterFloat64Snapshot") -} +// counterFloat64Snapshot is a read-only copy of another CounterFloat64. +type counterFloat64Snapshot float64 // Count returns the value at the time the snapshot was taken. -func (c CounterFloat64Snapshot) Count() float64 { return float64(c) } - -// Dec panics. -func (CounterFloat64Snapshot) Dec(float64) { - panic("Dec called on a CounterFloat64Snapshot") -} +func (c counterFloat64Snapshot) Count() float64 { return float64(c) } -// Inc panics. -func (CounterFloat64Snapshot) Inc(float64) { - panic("Inc called on a CounterFloat64Snapshot") -} - -// Snapshot returns the snapshot. -func (c CounterFloat64Snapshot) Snapshot() CounterFloat64 { return c } - -// NilCounterFloat64 is a no-op CounterFloat64. type NilCounterFloat64 struct{} -// Clear is a no-op. -func (NilCounterFloat64) Clear() {} - -// Count is a no-op. -func (NilCounterFloat64) Count() float64 { return 0.0 } - -// Dec is a no-op. -func (NilCounterFloat64) Dec(i float64) {} - -// Inc is a no-op. -func (NilCounterFloat64) Inc(i float64) {} - -// Snapshot is a no-op. -func (NilCounterFloat64) Snapshot() CounterFloat64 { return NilCounterFloat64{} } +func (NilCounterFloat64) Clear() {} +func (NilCounterFloat64) Count() float64 { return 0.0 } +func (NilCounterFloat64) Dec(i float64) {} +func (NilCounterFloat64) Inc(i float64) {} +func (NilCounterFloat64) Snapshot() CounterFloat64Snapshot { return NilCounterFloat64{} } // StandardCounterFloat64 is the standard implementation of a CounterFloat64 and uses the // atomic to manage a single float64 value. @@ -124,11 +99,6 @@ func (c *StandardCounterFloat64) Clear() { c.floatBits.Store(0) } -// Count returns the current value. -func (c *StandardCounterFloat64) Count() float64 { - return math.Float64frombits(c.floatBits.Load()) -} - // Dec decrements the counter by the given amount. func (c *StandardCounterFloat64) Dec(v float64) { atomicAddFloat(&c.floatBits, -v) @@ -140,8 +110,9 @@ func (c *StandardCounterFloat64) Inc(v float64) { } // Snapshot returns a read-only copy of the counter. -func (c *StandardCounterFloat64) Snapshot() CounterFloat64 { - return CounterFloat64Snapshot(c.Count()) +func (c *StandardCounterFloat64) Snapshot() CounterFloat64Snapshot { + v := math.Float64frombits(c.floatBits.Load()) + return counterFloat64Snapshot(v) } func atomicAddFloat(fbits *atomic.Uint64, v float64) { diff --git a/metrics/counter_float_64_test.go b/metrics/counter_float_64_test.go index f17aca330cbe..c21bd3307fa1 100644 --- a/metrics/counter_float_64_test.go +++ b/metrics/counter_float_64_test.go @@ -27,7 +27,7 @@ func BenchmarkCounterFloat64Parallel(b *testing.B) { }() } wg.Wait() - if have, want := c.Count(), 10.0*float64(b.N); have != want { + if have, want := c.Snapshot().Count(), 10.0*float64(b.N); have != want { b.Fatalf("have %f want %f", have, want) } } @@ -36,7 +36,7 @@ func TestCounterFloat64Clear(t *testing.T) { c := NewCounterFloat64() c.Inc(1.0) c.Clear() - if count := c.Count(); count != 0 { + if count := c.Snapshot().Count(); count != 0 { t.Errorf("c.Count(): 0 != %v\n", count) } } @@ -44,7 +44,7 @@ func TestCounterFloat64Clear(t *testing.T) { func TestCounterFloat64Dec1(t *testing.T) { c := NewCounterFloat64() c.Dec(1.0) - if count := c.Count(); count != -1.0 { + if count := c.Snapshot().Count(); count != -1.0 { t.Errorf("c.Count(): -1.0 != %v\n", count) } } @@ -52,7 +52,7 @@ func TestCounterFloat64Dec1(t *testing.T) { func TestCounterFloat64Dec2(t *testing.T) { c := NewCounterFloat64() c.Dec(2.0) - if count := c.Count(); count != -2.0 { + if count := c.Snapshot().Count(); count != -2.0 { t.Errorf("c.Count(): -2.0 != %v\n", count) } } @@ -60,7 +60,7 @@ func TestCounterFloat64Dec2(t *testing.T) { func TestCounterFloat64Inc1(t *testing.T) { c := NewCounterFloat64() c.Inc(1.0) - if count := c.Count(); count != 1.0 { + if count := c.Snapshot().Count(); count != 1.0 { t.Errorf("c.Count(): 1.0 != %v\n", count) } } @@ -68,7 +68,7 @@ func TestCounterFloat64Inc1(t *testing.T) { func TestCounterFloat64Inc2(t *testing.T) { c := NewCounterFloat64() c.Inc(2.0) - if count := c.Count(); count != 2.0 { + if count := c.Snapshot().Count(); count != 2.0 { t.Errorf("c.Count(): 2.0 != %v\n", count) } } @@ -85,7 +85,7 @@ func TestCounterFloat64Snapshot(t *testing.T) { func TestCounterFloat64Zero(t *testing.T) { c := NewCounterFloat64() - if count := c.Count(); count != 0 { + if count := c.Snapshot().Count(); count != 0 { t.Errorf("c.Count(): 0 != %v\n", count) } } @@ -93,7 +93,7 @@ func TestCounterFloat64Zero(t *testing.T) { func TestGetOrRegisterCounterFloat64(t *testing.T) { r := NewRegistry() NewRegisteredCounterFloat64("foo", r).Inc(47.0) - if c := GetOrRegisterCounterFloat64("foo", r); c.Count() != 47.0 { + if c := GetOrRegisterCounterFloat64("foo", r).Snapshot(); c.Count() != 47.0 { t.Fatal(c) } } diff --git a/metrics/counter_test.go b/metrics/counter_test.go index af26ef1548fe..1b15b23f215f 100644 --- a/metrics/counter_test.go +++ b/metrics/counter_test.go @@ -14,7 +14,7 @@ func TestCounterClear(t *testing.T) { c := NewCounter() c.Inc(1) c.Clear() - if count := c.Count(); count != 0 { + if count := c.Snapshot().Count(); count != 0 { t.Errorf("c.Count(): 0 != %v\n", count) } } @@ -22,7 +22,7 @@ func TestCounterClear(t *testing.T) { func TestCounterDec1(t *testing.T) { c := NewCounter() c.Dec(1) - if count := c.Count(); count != -1 { + if count := c.Snapshot().Count(); count != -1 { t.Errorf("c.Count(): -1 != %v\n", count) } } @@ -30,7 +30,7 @@ func TestCounterDec1(t *testing.T) { func TestCounterDec2(t *testing.T) { c := NewCounter() c.Dec(2) - if count := c.Count(); count != -2 { + if count := c.Snapshot().Count(); count != -2 { t.Errorf("c.Count(): -2 != %v\n", count) } } @@ -38,7 +38,7 @@ func TestCounterDec2(t *testing.T) { func TestCounterInc1(t *testing.T) { c := NewCounter() c.Inc(1) - if count := c.Count(); count != 1 { + if count := c.Snapshot().Count(); count != 1 { t.Errorf("c.Count(): 1 != %v\n", count) } } @@ -46,7 +46,7 @@ func TestCounterInc1(t *testing.T) { func TestCounterInc2(t *testing.T) { c := NewCounter() c.Inc(2) - if count := c.Count(); count != 2 { + if count := c.Snapshot().Count(); count != 2 { t.Errorf("c.Count(): 2 != %v\n", count) } } @@ -63,7 +63,7 @@ func TestCounterSnapshot(t *testing.T) { func TestCounterZero(t *testing.T) { c := NewCounter() - if count := c.Count(); count != 0 { + if count := c.Snapshot().Count(); count != 0 { t.Errorf("c.Count(): 0 != %v\n", count) } } @@ -71,7 +71,7 @@ func TestCounterZero(t *testing.T) { func TestGetOrRegisterCounter(t *testing.T) { r := NewRegistry() NewRegisteredCounter("foo", r).Inc(47) - if c := GetOrRegisterCounter("foo", r); c.Count() != 47 { + if c := GetOrRegisterCounter("foo", r).Snapshot(); c.Count() != 47 { t.Fatal(c) } } diff --git a/metrics/doc.go b/metrics/doc.go deleted file mode 100644 index 13f429c1689d..000000000000 --- a/metrics/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -package metrics - -const epsilon = 0.0000000000000001 -const epsilonPercentile = .00000000001 diff --git a/metrics/ewma.go b/metrics/ewma.go index ed95cba19b4f..1d7a4f00cf45 100644 --- a/metrics/ewma.go +++ b/metrics/ewma.go @@ -7,11 +7,14 @@ import ( "time" ) +type EWMASnapshot interface { + Rate() float64 +} + // EWMAs continuously calculate an exponentially-weighted moving average // based on an outside source of clock ticks. type EWMA interface { - Rate() float64 - Snapshot() EWMA + Snapshot() EWMASnapshot Tick() Update(int64) } @@ -36,40 +39,19 @@ func NewEWMA15() EWMA { return NewEWMA(1 - math.Exp(-5.0/60.0/15)) } -// EWMASnapshot is a read-only copy of another EWMA. -type EWMASnapshot float64 +// ewmaSnapshot is a read-only copy of another EWMA. +type ewmaSnapshot float64 // Rate returns the rate of events per second at the time the snapshot was // taken. -func (a EWMASnapshot) Rate() float64 { return float64(a) } - -// Snapshot returns the snapshot. -func (a EWMASnapshot) Snapshot() EWMA { return a } - -// Tick panics. -func (EWMASnapshot) Tick() { - panic("Tick called on an EWMASnapshot") -} - -// Update panics. -func (EWMASnapshot) Update(int64) { - panic("Update called on an EWMASnapshot") -} +func (a ewmaSnapshot) Rate() float64 { return float64(a) } // NilEWMA is a no-op EWMA. type NilEWMA struct{} -// Rate is a no-op. -func (NilEWMA) Rate() float64 { return 0.0 } - -// Snapshot is a no-op. -func (NilEWMA) Snapshot() EWMA { return NilEWMA{} } - -// Tick is a no-op. -func (NilEWMA) Tick() {} - -// Update is a no-op. -func (NilEWMA) Update(n int64) {} +func (NilEWMA) Snapshot() EWMASnapshot { return (*emptySnapshot)(nil) } +func (NilEWMA) Tick() {} +func (NilEWMA) Update(n int64) {} // StandardEWMA is the standard implementation of an EWMA and tracks the number // of uncounted events and processes them on each tick. It uses the @@ -77,37 +59,50 @@ func (NilEWMA) Update(n int64) {} type StandardEWMA struct { uncounted atomic.Int64 alpha float64 - rate float64 - init bool + rate atomic.Uint64 + init atomic.Bool mutex sync.Mutex } -// Rate returns the moving average rate of events per second. -func (a *StandardEWMA) Rate() float64 { - a.mutex.Lock() - defer a.mutex.Unlock() - return a.rate * float64(time.Second) -} - // Snapshot returns a read-only copy of the EWMA. -func (a *StandardEWMA) Snapshot() EWMA { - return EWMASnapshot(a.Rate()) +func (a *StandardEWMA) Snapshot() EWMASnapshot { + r := math.Float64frombits(a.rate.Load()) * float64(time.Second) + return ewmaSnapshot(r) } // Tick ticks the clock to update the moving average. It assumes it is called // every five seconds. func (a *StandardEWMA) Tick() { - count := a.uncounted.Load() - a.uncounted.Add(-count) - instantRate := float64(count) / float64(5*time.Second) + // Optimization to avoid mutex locking in the hot-path. + if a.init.Load() { + a.updateRate(a.fetchInstantRate()) + return + } + // Slow-path: this is only needed on the first Tick() and preserves transactional updating + // of init and rate in the else block. The first conditional is needed below because + // a different thread could have set a.init = 1 between the time of the first atomic load and when + // the lock was acquired. a.mutex.Lock() - defer a.mutex.Unlock() - if a.init { - a.rate += a.alpha * (instantRate - a.rate) + if a.init.Load() { + // The fetchInstantRate() uses atomic loading, which is unnecessary in this critical section + // but again, this section is only invoked on the first successful Tick() operation. + a.updateRate(a.fetchInstantRate()) } else { - a.init = true - a.rate = instantRate + a.init.Store(true) + a.rate.Store(math.Float64bits(a.fetchInstantRate())) } + a.mutex.Unlock() +} + +func (a *StandardEWMA) fetchInstantRate() float64 { + count := a.uncounted.Swap(0) + return float64(count) / float64(5*time.Second) +} + +func (a *StandardEWMA) updateRate(instantRate float64) { + currentRate := math.Float64frombits(a.rate.Load()) + currentRate += a.alpha * (instantRate - currentRate) + a.rate.Store(math.Float64bits(currentRate)) } // Update adds n uncounted events. diff --git a/metrics/ewma_test.go b/metrics/ewma_test.go index 5b244191616e..9a91b43db81a 100644 --- a/metrics/ewma_test.go +++ b/metrics/ewma_test.go @@ -5,6 +5,8 @@ import ( "testing" ) +const epsilon = 0.0000000000000001 + func BenchmarkEWMA(b *testing.B) { a := NewEWMA1() b.ResetTimer() @@ -14,72 +16,33 @@ func BenchmarkEWMA(b *testing.B) { } } +func BenchmarkEWMAParallel(b *testing.B) { + a := NewEWMA1() + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + a.Update(1) + a.Tick() + } + }) +} + func TestEWMA1(t *testing.T) { a := NewEWMA1() a.Update(3) a.Tick() - if rate := a.Rate(); math.Abs(0.6-rate) > epsilon { - t.Errorf("initial a.Rate(): 0.6 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.22072766470286553-rate) > epsilon { - t.Errorf("1 minute a.Rate(): 0.22072766470286553 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.08120116994196772-rate) > epsilon { - t.Errorf("2 minute a.Rate(): 0.08120116994196772 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.029872241020718428-rate) > epsilon { - t.Errorf("3 minute a.Rate(): 0.029872241020718428 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.01098938333324054-rate) > epsilon { - t.Errorf("4 minute a.Rate(): 0.01098938333324054 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.004042768199451294-rate) > epsilon { - t.Errorf("5 minute a.Rate(): 0.004042768199451294 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.0014872513059998212-rate) > epsilon { - t.Errorf("6 minute a.Rate(): 0.0014872513059998212 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.0005471291793327122-rate) > epsilon { - t.Errorf("7 minute a.Rate(): 0.0005471291793327122 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.00020127757674150815-rate) > epsilon { - t.Errorf("8 minute a.Rate(): 0.00020127757674150815 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(7.404588245200814e-05-rate) > epsilon { - t.Errorf("9 minute a.Rate(): 7.404588245200814e-05 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(2.7239957857491083e-05-rate) > epsilon { - t.Errorf("10 minute a.Rate(): 2.7239957857491083e-05 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(1.0021020474147462e-05-rate) > epsilon { - t.Errorf("11 minute a.Rate(): 1.0021020474147462e-05 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(3.6865274119969525e-06-rate) > epsilon { - t.Errorf("12 minute a.Rate(): 3.6865274119969525e-06 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(1.3561976441886433e-06-rate) > epsilon { - t.Errorf("13 minute a.Rate(): 1.3561976441886433e-06 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(4.989172314621449e-07-rate) > epsilon { - t.Errorf("14 minute a.Rate(): 4.989172314621449e-07 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(1.8354139230109722e-07-rate) > epsilon { - t.Errorf("15 minute a.Rate(): 1.8354139230109722e-07 != %v\n", rate) + for i, want := range []float64{0.6, + 0.22072766470286553, 0.08120116994196772, 0.029872241020718428, + 0.01098938333324054, 0.004042768199451294, 0.0014872513059998212, + 0.0005471291793327122, 0.00020127757674150815, 7.404588245200814e-05, + 2.7239957857491083e-05, 1.0021020474147462e-05, 3.6865274119969525e-06, + 1.3561976441886433e-06, 4.989172314621449e-07, 1.8354139230109722e-07, + } { + if rate := a.Snapshot().Rate(); math.Abs(want-rate) > epsilon { + t.Errorf("%d minute a.Snapshot().Rate(): %f != %v\n", i, want, rate) + } + elapseMinute(a) } } @@ -87,68 +50,17 @@ func TestEWMA5(t *testing.T) { a := NewEWMA5() a.Update(3) a.Tick() - if rate := a.Rate(); math.Abs(0.6-rate) > epsilon { - t.Errorf("initial a.Rate(): 0.6 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.49123845184678905-rate) > epsilon { - t.Errorf("1 minute a.Rate(): 0.49123845184678905 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.4021920276213837-rate) > epsilon { - t.Errorf("2 minute a.Rate(): 0.4021920276213837 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.32928698165641596-rate) > epsilon { - t.Errorf("3 minute a.Rate(): 0.32928698165641596 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.269597378470333-rate) > epsilon { - t.Errorf("4 minute a.Rate(): 0.269597378470333 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.2207276647028654-rate) > epsilon { - t.Errorf("5 minute a.Rate(): 0.2207276647028654 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.18071652714732128-rate) > epsilon { - t.Errorf("6 minute a.Rate(): 0.18071652714732128 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.14795817836496392-rate) > epsilon { - t.Errorf("7 minute a.Rate(): 0.14795817836496392 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.12113791079679326-rate) > epsilon { - t.Errorf("8 minute a.Rate(): 0.12113791079679326 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.09917933293295193-rate) > epsilon { - t.Errorf("9 minute a.Rate(): 0.09917933293295193 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.08120116994196763-rate) > epsilon { - t.Errorf("10 minute a.Rate(): 0.08120116994196763 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.06648189501740036-rate) > epsilon { - t.Errorf("11 minute a.Rate(): 0.06648189501740036 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.05443077197364752-rate) > epsilon { - t.Errorf("12 minute a.Rate(): 0.05443077197364752 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.04456414692860035-rate) > epsilon { - t.Errorf("13 minute a.Rate(): 0.04456414692860035 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.03648603757513079-rate) > epsilon { - t.Errorf("14 minute a.Rate(): 0.03648603757513079 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.0298722410207183831020718428-rate) > epsilon { - t.Errorf("15 minute a.Rate(): 0.0298722410207183831020718428 != %v\n", rate) + for i, want := range []float64{ + 0.6, 0.49123845184678905, 0.4021920276213837, 0.32928698165641596, + 0.269597378470333, 0.2207276647028654, 0.18071652714732128, + 0.14795817836496392, 0.12113791079679326, 0.09917933293295193, + 0.08120116994196763, 0.06648189501740036, 0.05443077197364752, + 0.04456414692860035, 0.03648603757513079, 0.0298722410207183831020718428, + } { + if rate := a.Snapshot().Rate(); math.Abs(want-rate) > epsilon { + t.Errorf("%d minute a.Snapshot().Rate(): %f != %v\n", i, want, rate) + } + elapseMinute(a) } } @@ -156,68 +68,17 @@ func TestEWMA15(t *testing.T) { a := NewEWMA15() a.Update(3) a.Tick() - if rate := a.Rate(); math.Abs(0.6-rate) > epsilon { - t.Errorf("initial a.Rate(): 0.6 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.5613041910189706-rate) > epsilon { - t.Errorf("1 minute a.Rate(): 0.5613041910189706 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.5251039914257684-rate) > epsilon { - t.Errorf("2 minute a.Rate(): 0.5251039914257684 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.4912384518467888184678905-rate) > epsilon { - t.Errorf("3 minute a.Rate(): 0.4912384518467888184678905 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.459557003018789-rate) > epsilon { - t.Errorf("4 minute a.Rate(): 0.459557003018789 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.4299187863442732-rate) > epsilon { - t.Errorf("5 minute a.Rate(): 0.4299187863442732 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.4021920276213831-rate) > epsilon { - t.Errorf("6 minute a.Rate(): 0.4021920276213831 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.37625345116383313-rate) > epsilon { - t.Errorf("7 minute a.Rate(): 0.37625345116383313 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.3519877317060185-rate) > epsilon { - t.Errorf("8 minute a.Rate(): 0.3519877317060185 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.3292869816564153165641596-rate) > epsilon { - t.Errorf("9 minute a.Rate(): 0.3292869816564153165641596 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.3080502714195546-rate) > epsilon { - t.Errorf("10 minute a.Rate(): 0.3080502714195546 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.2881831806538789-rate) > epsilon { - t.Errorf("11 minute a.Rate(): 0.2881831806538789 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.26959737847033216-rate) > epsilon { - t.Errorf("12 minute a.Rate(): 0.26959737847033216 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.2522102307052083-rate) > epsilon { - t.Errorf("13 minute a.Rate(): 0.2522102307052083 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.23594443252115815-rate) > epsilon { - t.Errorf("14 minute a.Rate(): 0.23594443252115815 != %v\n", rate) - } - elapseMinute(a) - if rate := a.Rate(); math.Abs(0.2207276647028646247028654470286553-rate) > epsilon { - t.Errorf("15 minute a.Rate(): 0.2207276647028646247028654470286553 != %v\n", rate) + for i, want := range []float64{ + 0.6, 0.5613041910189706, 0.5251039914257684, 0.4912384518467888184678905, + 0.459557003018789, 0.4299187863442732, 0.4021920276213831, + 0.37625345116383313, 0.3519877317060185, 0.3292869816564153165641596, + 0.3080502714195546, 0.2881831806538789, 0.26959737847033216, + 0.2522102307052083, 0.23594443252115815, 0.2207276647028646247028654470286553, + } { + if rate := a.Snapshot().Rate(); math.Abs(want-rate) > epsilon { + t.Errorf("%d minute a.Snapshot().Rate(): %f != %v\n", i, want, rate) + } + elapseMinute(a) } } diff --git a/metrics/exp/exp.go b/metrics/exp/exp.go index 6091f2f848b5..e7f54d1320fa 100644 --- a/metrics/exp/exp.go +++ b/metrics/exp/exp.go @@ -109,25 +109,25 @@ func (exp *exp) getInfo(name string) *expvar.String { return v } -func (exp *exp) publishCounter(name string, metric metrics.Counter) { +func (exp *exp) publishCounter(name string, metric metrics.CounterSnapshot) { v := exp.getInt(name) v.Set(metric.Count()) } -func (exp *exp) publishCounterFloat64(name string, metric metrics.CounterFloat64) { +func (exp *exp) publishCounterFloat64(name string, metric metrics.CounterFloat64Snapshot) { v := exp.getFloat(name) v.Set(metric.Count()) } -func (exp *exp) publishGauge(name string, metric metrics.Gauge) { +func (exp *exp) publishGauge(name string, metric metrics.GaugeSnapshot) { v := exp.getInt(name) v.Set(metric.Value()) } -func (exp *exp) publishGaugeFloat64(name string, metric metrics.GaugeFloat64) { +func (exp *exp) publishGaugeFloat64(name string, metric metrics.GaugeFloat64Snapshot) { exp.getFloat(name).Set(metric.Value()) } -func (exp *exp) publishGaugeInfo(name string, metric metrics.GaugeInfo) { +func (exp *exp) publishGaugeInfo(name string, metric metrics.GaugeInfoSnapshot) { exp.getInfo(name).Set(metric.Value().String()) } @@ -176,28 +176,28 @@ func (exp *exp) publishTimer(name string, metric metrics.Timer) { func (exp *exp) publishResettingTimer(name string, metric metrics.ResettingTimer) { t := metric.Snapshot() - ps := t.Percentiles([]float64{50, 75, 95, 99}) - exp.getInt(name + ".count").Set(int64(len(t.Values()))) + ps := t.Percentiles([]float64{0.50, 0.75, 0.95, 0.99}) + exp.getInt(name + ".count").Set(int64(t.Count())) exp.getFloat(name + ".mean").Set(t.Mean()) - exp.getInt(name + ".50-percentile").Set(ps[0]) - exp.getInt(name + ".75-percentile").Set(ps[1]) - exp.getInt(name + ".95-percentile").Set(ps[2]) - exp.getInt(name + ".99-percentile").Set(ps[3]) + exp.getFloat(name + ".50-percentile").Set(ps[0]) + exp.getFloat(name + ".75-percentile").Set(ps[1]) + exp.getFloat(name + ".95-percentile").Set(ps[2]) + exp.getFloat(name + ".99-percentile").Set(ps[3]) } func (exp *exp) syncToExpvar() { exp.registry.Each(func(name string, i interface{}) { switch i := i.(type) { case metrics.Counter: - exp.publishCounter(name, i) + exp.publishCounter(name, i.Snapshot()) case metrics.CounterFloat64: - exp.publishCounterFloat64(name, i) + exp.publishCounterFloat64(name, i.Snapshot()) case metrics.Gauge: - exp.publishGauge(name, i) + exp.publishGauge(name, i.Snapshot()) case metrics.GaugeFloat64: - exp.publishGaugeFloat64(name, i) + exp.publishGaugeFloat64(name, i.Snapshot()) case metrics.GaugeInfo: - exp.publishGaugeInfo(name, i) + exp.publishGaugeInfo(name, i.Snapshot()) case metrics.Histogram: exp.publishHistogram(name, i) case metrics.Meter: diff --git a/metrics/gauge.go b/metrics/gauge.go index 81137d7f7c5e..00b59873848c 100644 --- a/metrics/gauge.go +++ b/metrics/gauge.go @@ -2,13 +2,18 @@ package metrics import "sync/atomic" +// gaugeSnapshot contains a readonly int64. +type GaugeSnapshot interface { + Value() int64 +} + // Gauges hold an int64 value that can be set arbitrarily. type Gauge interface { - Snapshot() Gauge + Snapshot() GaugeSnapshot Update(int64) + UpdateIfGt(int64) Dec(int64) Inc(int64) - Value() int64 } // GetOrRegisterGauge returns an existing Gauge or constructs and registers a @@ -38,65 +43,20 @@ func NewRegisteredGauge(name string, r Registry) Gauge { return c } -// NewFunctionalGauge constructs a new FunctionalGauge. -func NewFunctionalGauge(f func() int64) Gauge { - if !Enabled { - return NilGauge{} - } - return &FunctionalGauge{value: f} -} - -// NewRegisteredFunctionalGauge constructs and registers a new StandardGauge. -func NewRegisteredFunctionalGauge(name string, r Registry, f func() int64) Gauge { - c := NewFunctionalGauge(f) - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// GaugeSnapshot is a read-only copy of another Gauge. -type GaugeSnapshot int64 - -// Snapshot returns the snapshot. -func (g GaugeSnapshot) Snapshot() Gauge { return g } - -// Update panics. -func (GaugeSnapshot) Update(int64) { - panic("Update called on a GaugeSnapshot") -} - -// Dec panics. -func (GaugeSnapshot) Dec(int64) { - panic("Dec called on a GaugeSnapshot") -} - -// Inc panics. -func (GaugeSnapshot) Inc(int64) { - panic("Inc called on a GaugeSnapshot") -} +// gaugeSnapshot is a read-only copy of another Gauge. +type gaugeSnapshot int64 // Value returns the value at the time the snapshot was taken. -func (g GaugeSnapshot) Value() int64 { return int64(g) } +func (g gaugeSnapshot) Value() int64 { return int64(g) } // NilGauge is a no-op Gauge. type NilGauge struct{} -// Snapshot is a no-op. -func (NilGauge) Snapshot() Gauge { return NilGauge{} } - -// Update is a no-op. -func (NilGauge) Update(v int64) {} - -// Dec is a no-op. -func (NilGauge) Dec(i int64) {} - -// Inc is a no-op. -func (NilGauge) Inc(i int64) {} - -// Value is a no-op. -func (NilGauge) Value() int64 { return 0 } +func (NilGauge) Snapshot() GaugeSnapshot { return (*emptySnapshot)(nil) } +func (NilGauge) Update(v int64) {} +func (NilGauge) UpdateIfGt(v int64) {} +func (NilGauge) Dec(i int64) {} +func (NilGauge) Inc(i int64) {} // StandardGauge is the standard implementation of a Gauge and uses the // sync/atomic package to manage a single int64 value. @@ -105,8 +65,8 @@ type StandardGauge struct { } // Snapshot returns a read-only copy of the gauge. -func (g *StandardGauge) Snapshot() Gauge { - return GaugeSnapshot(g.Value()) +func (g *StandardGauge) Snapshot() GaugeSnapshot { + return gaugeSnapshot(g.value.Load()) } // Update updates the gauge's value. @@ -114,9 +74,17 @@ func (g *StandardGauge) Update(v int64) { g.value.Store(v) } -// Value returns the gauge's current value. -func (g *StandardGauge) Value() int64 { - return g.value.Load() +// Update updates the gauge's value if v is larger then the current value. +func (g *StandardGauge) UpdateIfGt(v int64) { + for { + exist := g.value.Load() + if exist >= v { + break + } + if g.value.CompareAndSwap(exist, v) { + break + } + } } // Dec decrements the gauge's current value by the given amount. @@ -128,31 +96,3 @@ func (g *StandardGauge) Dec(i int64) { func (g *StandardGauge) Inc(i int64) { g.value.Add(i) } - -// FunctionalGauge returns value from given function -type FunctionalGauge struct { - value func() int64 -} - -// Value returns the gauge's current value. -func (g FunctionalGauge) Value() int64 { - return g.value() -} - -// Snapshot returns the snapshot. -func (g FunctionalGauge) Snapshot() Gauge { return GaugeSnapshot(g.Value()) } - -// Update panics. -func (FunctionalGauge) Update(int64) { - panic("Update called on a FunctionalGauge") -} - -// Dec panics. -func (FunctionalGauge) Dec(int64) { - panic("Dec called on a FunctionalGauge") -} - -// Inc panics. -func (FunctionalGauge) Inc(int64) { - panic("Inc called on a FunctionalGauge") -} diff --git a/metrics/gauge_float64.go b/metrics/gauge_float64.go index 237ff8036e01..967f2bc60e5c 100644 --- a/metrics/gauge_float64.go +++ b/metrics/gauge_float64.go @@ -5,11 +5,14 @@ import ( "sync/atomic" ) -// GaugeFloat64s hold a float64 value that can be set arbitrarily. +type GaugeFloat64Snapshot interface { + Value() float64 +} + +// GaugeFloat64 hold a float64 value that can be set arbitrarily. type GaugeFloat64 interface { - Snapshot() GaugeFloat64 + Snapshot() GaugeFloat64Snapshot Update(float64) - Value() float64 } // GetOrRegisterGaugeFloat64 returns an existing GaugeFloat64 or constructs and registers a @@ -39,49 +42,18 @@ func NewRegisteredGaugeFloat64(name string, r Registry) GaugeFloat64 { return c } -// NewFunctionalGauge constructs a new FunctionalGauge. -func NewFunctionalGaugeFloat64(f func() float64) GaugeFloat64 { - if !Enabled { - return NilGaugeFloat64{} - } - return &FunctionalGaugeFloat64{value: f} -} - -// NewRegisteredFunctionalGauge constructs and registers a new StandardGauge. -func NewRegisteredFunctionalGaugeFloat64(name string, r Registry, f func() float64) GaugeFloat64 { - c := NewFunctionalGaugeFloat64(f) - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// GaugeFloat64Snapshot is a read-only copy of another GaugeFloat64. -type GaugeFloat64Snapshot float64 - -// Snapshot returns the snapshot. -func (g GaugeFloat64Snapshot) Snapshot() GaugeFloat64 { return g } - -// Update panics. -func (GaugeFloat64Snapshot) Update(float64) { - panic("Update called on a GaugeFloat64Snapshot") -} +// gaugeFloat64Snapshot is a read-only copy of another GaugeFloat64. +type gaugeFloat64Snapshot float64 // Value returns the value at the time the snapshot was taken. -func (g GaugeFloat64Snapshot) Value() float64 { return float64(g) } +func (g gaugeFloat64Snapshot) Value() float64 { return float64(g) } // NilGauge is a no-op Gauge. type NilGaugeFloat64 struct{} -// Snapshot is a no-op. -func (NilGaugeFloat64) Snapshot() GaugeFloat64 { return NilGaugeFloat64{} } - -// Update is a no-op. -func (NilGaugeFloat64) Update(v float64) {} - -// Value is a no-op. -func (NilGaugeFloat64) Value() float64 { return 0.0 } +func (NilGaugeFloat64) Snapshot() GaugeFloat64Snapshot { return NilGaugeFloat64{} } +func (NilGaugeFloat64) Update(v float64) {} +func (NilGaugeFloat64) Value() float64 { return 0.0 } // StandardGaugeFloat64 is the standard implementation of a GaugeFloat64 and uses // atomic to manage a single float64 value. @@ -90,34 +62,12 @@ type StandardGaugeFloat64 struct { } // Snapshot returns a read-only copy of the gauge. -func (g *StandardGaugeFloat64) Snapshot() GaugeFloat64 { - return GaugeFloat64Snapshot(g.Value()) +func (g *StandardGaugeFloat64) Snapshot() GaugeFloat64Snapshot { + v := math.Float64frombits(g.floatBits.Load()) + return gaugeFloat64Snapshot(v) } // Update updates the gauge's value. func (g *StandardGaugeFloat64) Update(v float64) { g.floatBits.Store(math.Float64bits(v)) } - -// Value returns the gauge's current value. -func (g *StandardGaugeFloat64) Value() float64 { - return math.Float64frombits(g.floatBits.Load()) -} - -// FunctionalGaugeFloat64 returns value from given function -type FunctionalGaugeFloat64 struct { - value func() float64 -} - -// Value returns the gauge's current value. -func (g FunctionalGaugeFloat64) Value() float64 { - return g.value() -} - -// Snapshot returns the snapshot. -func (g FunctionalGaugeFloat64) Snapshot() GaugeFloat64 { return GaugeFloat64Snapshot(g.Value()) } - -// Update panics. -func (FunctionalGaugeFloat64) Update(float64) { - panic("Update called on a FunctionalGaugeFloat64") -} diff --git a/metrics/gauge_float64_test.go b/metrics/gauge_float64_test.go index 0e11d803c4f6..194a18821f83 100644 --- a/metrics/gauge_float64_test.go +++ b/metrics/gauge_float64_test.go @@ -26,19 +26,11 @@ func BenchmarkGaugeFloat64Parallel(b *testing.B) { }() } wg.Wait() - if have, want := c.Value(), float64(b.N-1); have != want { + if have, want := c.Snapshot().Value(), float64(b.N-1); have != want { b.Fatalf("have %f want %f", have, want) } } -func TestGaugeFloat64(t *testing.T) { - g := NewGaugeFloat64() - g.Update(47.0) - if v := g.Value(); v != 47.0 { - t.Errorf("g.Value(): 47.0 != %v\n", v) - } -} - func TestGaugeFloat64Snapshot(t *testing.T) { g := NewGaugeFloat64() g.Update(47.0) @@ -53,28 +45,7 @@ func TestGetOrRegisterGaugeFloat64(t *testing.T) { r := NewRegistry() NewRegisteredGaugeFloat64("foo", r).Update(47.0) t.Logf("registry: %v", r) - if g := GetOrRegisterGaugeFloat64("foo", r); g.Value() != 47.0 { - t.Fatal(g) - } -} - -func TestFunctionalGaugeFloat64(t *testing.T) { - var counter float64 - fg := NewFunctionalGaugeFloat64(func() float64 { - counter++ - return counter - }) - fg.Value() - fg.Value() - if counter != 2 { - t.Error("counter != 2") - } -} - -func TestGetOrRegisterFunctionalGaugeFloat64(t *testing.T) { - r := NewRegistry() - NewRegisteredFunctionalGaugeFloat64("foo", r, func() float64 { return 47 }) - if g := GetOrRegisterGaugeFloat64("foo", r); g.Value() != 47 { + if g := GetOrRegisterGaugeFloat64("foo", r).Snapshot(); g.Value() != 47.0 { t.Fatal(g) } } diff --git a/metrics/gauge_info.go b/metrics/gauge_info.go index f1b2075939e7..c44b2d85f3ad 100644 --- a/metrics/gauge_info.go +++ b/metrics/gauge_info.go @@ -5,14 +5,17 @@ import ( "sync" ) +type GaugeInfoSnapshot interface { + Value() GaugeInfoValue +} + // GaugeInfos hold a GaugeInfoValue value that can be set arbitrarily. type GaugeInfo interface { - Snapshot() GaugeInfo Update(GaugeInfoValue) - Value() GaugeInfoValue + Snapshot() GaugeInfoSnapshot } -// GaugeInfoValue is a mappng of (string) keys to (string) values +// GaugeInfoValue is a mapping of keys to values type GaugeInfoValue map[string]string func (val GaugeInfoValue) String() string { @@ -49,49 +52,17 @@ func NewRegisteredGaugeInfo(name string, r Registry) GaugeInfo { return c } -// NewFunctionalGauge constructs a new FunctionalGauge. -func NewFunctionalGaugeInfo(f func() GaugeInfoValue) GaugeInfo { - if !Enabled { - return NilGaugeInfo{} - } - return &FunctionalGaugeInfo{value: f} -} - -// NewRegisteredFunctionalGauge constructs and registers a new StandardGauge. -func NewRegisteredFunctionalGaugeInfo(name string, r Registry, f func() GaugeInfoValue) GaugeInfo { - c := NewFunctionalGaugeInfo(f) - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// GaugeInfoSnapshot is a read-only copy of another GaugeInfo. -type GaugeInfoSnapshot GaugeInfoValue - -// Snapshot returns the snapshot. -func (g GaugeInfoSnapshot) Snapshot() GaugeInfo { return g } - -// Update panics. -func (GaugeInfoSnapshot) Update(GaugeInfoValue) { - panic("Update called on a GaugeInfoSnapshot") -} +// gaugeInfoSnapshot is a read-only copy of another GaugeInfo. +type gaugeInfoSnapshot GaugeInfoValue // Value returns the value at the time the snapshot was taken. -func (g GaugeInfoSnapshot) Value() GaugeInfoValue { return GaugeInfoValue(g) } +func (g gaugeInfoSnapshot) Value() GaugeInfoValue { return GaugeInfoValue(g) } -// NilGauge is a no-op Gauge. type NilGaugeInfo struct{} -// Snapshot is a no-op. -func (NilGaugeInfo) Snapshot() GaugeInfo { return NilGaugeInfo{} } - -// Update is a no-op. -func (NilGaugeInfo) Update(v GaugeInfoValue) {} - -// Value is a no-op. -func (NilGaugeInfo) Value() GaugeInfoValue { return GaugeInfoValue{} } +func (NilGaugeInfo) Snapshot() GaugeInfoSnapshot { return NilGaugeInfo{} } +func (NilGaugeInfo) Update(v GaugeInfoValue) {} +func (NilGaugeInfo) Value() GaugeInfoValue { return GaugeInfoValue{} } // StandardGaugeInfo is the standard implementation of a GaugeInfo and uses // sync.Mutex to manage a single string value. @@ -101,8 +72,8 @@ type StandardGaugeInfo struct { } // Snapshot returns a read-only copy of the gauge. -func (g *StandardGaugeInfo) Snapshot() GaugeInfo { - return GaugeInfoSnapshot(g.Value()) +func (g *StandardGaugeInfo) Snapshot() GaugeInfoSnapshot { + return gaugeInfoSnapshot(g.value) } // Update updates the gauge's value. @@ -111,34 +82,3 @@ func (g *StandardGaugeInfo) Update(v GaugeInfoValue) { defer g.mutex.Unlock() g.value = v } - -// Value returns the gauge's current value. -func (g *StandardGaugeInfo) Value() GaugeInfoValue { - g.mutex.Lock() - defer g.mutex.Unlock() - return g.value -} - -// FunctionalGaugeInfo returns value from given function -type FunctionalGaugeInfo struct { - value func() GaugeInfoValue -} - -// Value returns the gauge's current value. -func (g FunctionalGaugeInfo) Value() GaugeInfoValue { - return g.value() -} - -// Value returns the gauge's current value in JSON string format -func (g FunctionalGaugeInfo) ValueJsonString() string { - data, _ := json.Marshal(g.value()) - return string(data) -} - -// Snapshot returns the snapshot. -func (g FunctionalGaugeInfo) Snapshot() GaugeInfo { return GaugeInfoSnapshot(g.Value()) } - -// Update panics. -func (FunctionalGaugeInfo) Update(GaugeInfoValue) { - panic("Update called on a FunctionalGaugeInfo") -} diff --git a/metrics/gauge_info_test.go b/metrics/gauge_info_test.go index 4227a6a85fab..319afbf92e8f 100644 --- a/metrics/gauge_info_test.go +++ b/metrics/gauge_info_test.go @@ -1,7 +1,6 @@ package metrics import ( - "strconv" "testing" ) @@ -14,22 +13,14 @@ func TestGaugeInfoJsonString(t *testing.T) { }, ) want := `{"anotherKey":"any_string_value","chain_id":"5","third_key":"anything"}` - if have := g.Value().String(); have != want { - t.Errorf("\nhave: %v\nwant: %v\n", have, want) - } -} -func TestGaugeInfoSnapshot(t *testing.T) { - g := NewGaugeInfo() - g.Update(GaugeInfoValue{"value": "original"}) - snapshot := g.Snapshot() // Snapshot @chainid 5 + original := g.Snapshot() g.Update(GaugeInfoValue{"value": "updated"}) - // The 'g' should be updated - if have, want := g.Value().String(), `{"value":"updated"}`; have != want { + + if have := original.Value().String(); have != want { t.Errorf("\nhave: %v\nwant: %v\n", have, want) } - // Snapshot should be unupdated - if have, want := snapshot.Value().String(), `{"value":"original"}`; have != want { + if have, want := g.Snapshot().Value().String(), `{"value":"updated"}`; have != want { t.Errorf("\nhave: %v\nwant: %v\n", have, want) } } @@ -38,38 +29,8 @@ func TestGetOrRegisterGaugeInfo(t *testing.T) { r := NewRegistry() NewRegisteredGaugeInfo("foo", r).Update( GaugeInfoValue{"chain_id": "5"}) - g := GetOrRegisterGaugeInfo("foo", r) + g := GetOrRegisterGaugeInfo("foo", r).Snapshot() if have, want := g.Value().String(), `{"chain_id":"5"}`; have != want { t.Errorf("have\n%v\nwant\n%v\n", have, want) } } - -func TestFunctionalGaugeInfo(t *testing.T) { - info := GaugeInfoValue{"chain_id": "0"} - counter := 1 - // A "functional" gauge invokes the method to obtain the value - fg := NewFunctionalGaugeInfo(func() GaugeInfoValue { - info["chain_id"] = strconv.Itoa(counter) - counter++ - return info - }) - fg.Value() - fg.Value() - if have, want := info["chain_id"], "2"; have != want { - t.Errorf("have %v want %v", have, want) - } -} - -func TestGetOrRegisterFunctionalGaugeInfo(t *testing.T) { - r := NewRegistry() - NewRegisteredFunctionalGaugeInfo("foo", r, func() GaugeInfoValue { - return GaugeInfoValue{ - "chain_id": "5", - } - }) - want := `{"chain_id":"5"}` - have := GetOrRegisterGaugeInfo("foo", r).Value().String() - if have != want { - t.Errorf("have\n%v\nwant\n%v\n", have, want) - } -} diff --git a/metrics/gauge_test.go b/metrics/gauge_test.go index a98fe985d8c2..f2ba930bc465 100644 --- a/metrics/gauge_test.go +++ b/metrics/gauge_test.go @@ -1,7 +1,6 @@ package metrics import ( - "fmt" "testing" ) @@ -13,14 +12,6 @@ func BenchmarkGauge(b *testing.B) { } } -func TestGauge(t *testing.T) { - g := NewGauge() - g.Update(int64(47)) - if v := g.Value(); v != 47 { - t.Errorf("g.Value(): 47 != %v\n", v) - } -} - func TestGaugeSnapshot(t *testing.T) { g := NewGauge() g.Update(int64(47)) @@ -34,35 +25,7 @@ func TestGaugeSnapshot(t *testing.T) { func TestGetOrRegisterGauge(t *testing.T) { r := NewRegistry() NewRegisteredGauge("foo", r).Update(47) - if g := GetOrRegisterGauge("foo", r); g.Value() != 47 { - t.Fatal(g) - } -} - -func TestFunctionalGauge(t *testing.T) { - var counter int64 - fg := NewFunctionalGauge(func() int64 { - counter++ - return counter - }) - fg.Value() - fg.Value() - if counter != 2 { - t.Error("counter != 2") - } -} - -func TestGetOrRegisterFunctionalGauge(t *testing.T) { - r := NewRegistry() - NewRegisteredFunctionalGauge("foo", r, func() int64 { return 47 }) - if g := GetOrRegisterGauge("foo", r); g.Value() != 47 { + if g := GetOrRegisterGauge("foo", r); g.Snapshot().Value() != 47 { t.Fatal(g) } } - -func ExampleGetOrRegisterGauge() { - m := "server.bytes_sent" - g := GetOrRegisterGauge(m, nil) - g.Update(47) - fmt.Println(g.Value()) // Output: 47 -} diff --git a/metrics/graphite.go b/metrics/graphite.go index 4e3dd3b3b894..aba752e0ed5e 100644 --- a/metrics/graphite.go +++ b/metrics/graphite.go @@ -66,15 +66,15 @@ func graphite(c *GraphiteConfig) error { c.Registry.Each(func(name string, i interface{}) { switch metric := i.(type) { case Counter: - fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, metric.Count(), now) + fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, metric.Snapshot().Count(), now) case CounterFloat64: - fmt.Fprintf(w, "%s.%s.count %f %d\n", c.Prefix, name, metric.Count(), now) + fmt.Fprintf(w, "%s.%s.count %f %d\n", c.Prefix, name, metric.Snapshot().Count(), now) case Gauge: - fmt.Fprintf(w, "%s.%s.value %d %d\n", c.Prefix, name, metric.Value(), now) + fmt.Fprintf(w, "%s.%s.value %d %d\n", c.Prefix, name, metric.Snapshot().Value(), now) case GaugeFloat64: - fmt.Fprintf(w, "%s.%s.value %f %d\n", c.Prefix, name, metric.Value(), now) + fmt.Fprintf(w, "%s.%s.value %f %d\n", c.Prefix, name, metric.Snapshot().Value(), now) case GaugeInfo: - fmt.Fprintf(w, "%s.%s.value %s %d\n", c.Prefix, name, metric.Value().String(), now) + fmt.Fprintf(w, "%s.%s.value %s %d\n", c.Prefix, name, metric.Snapshot().Value().String(), now) case Histogram: h := metric.Snapshot() ps := h.Percentiles(c.Percentiles) diff --git a/metrics/histogram.go b/metrics/histogram.go index 2c54ce8b4063..44de588bc1dc 100644 --- a/metrics/histogram.go +++ b/metrics/histogram.go @@ -1,20 +1,14 @@ package metrics +type HistogramSnapshot interface { + SampleSnapshot +} + // Histograms calculate distribution statistics from a series of int64 values. type Histogram interface { Clear() - Count() int64 - Max() int64 - Mean() float64 - Min() int64 - Percentile(float64) float64 - Percentiles([]float64) []float64 - Sample() Sample - Snapshot() Histogram - StdDev() float64 - Sum() int64 Update(int64) - Variance() float64 + Snapshot() HistogramSnapshot } // GetOrRegisterHistogram returns an existing Histogram or constructs and @@ -54,108 +48,12 @@ func NewRegisteredHistogram(name string, r Registry, s Sample) Histogram { return c } -// HistogramSnapshot is a read-only copy of another Histogram. -type HistogramSnapshot struct { - sample *SampleSnapshot -} - -// Clear panics. -func (*HistogramSnapshot) Clear() { - panic("Clear called on a HistogramSnapshot") -} - -// Count returns the number of samples recorded at the time the snapshot was -// taken. -func (h *HistogramSnapshot) Count() int64 { return h.sample.Count() } - -// Max returns the maximum value in the sample at the time the snapshot was -// taken. -func (h *HistogramSnapshot) Max() int64 { return h.sample.Max() } - -// Mean returns the mean of the values in the sample at the time the snapshot -// was taken. -func (h *HistogramSnapshot) Mean() float64 { return h.sample.Mean() } - -// Min returns the minimum value in the sample at the time the snapshot was -// taken. -func (h *HistogramSnapshot) Min() int64 { return h.sample.Min() } - -// Percentile returns an arbitrary percentile of values in the sample at the -// time the snapshot was taken. -func (h *HistogramSnapshot) Percentile(p float64) float64 { - return h.sample.Percentile(p) -} - -// Percentiles returns a slice of arbitrary percentiles of values in the sample -// at the time the snapshot was taken. -func (h *HistogramSnapshot) Percentiles(ps []float64) []float64 { - return h.sample.Percentiles(ps) -} - -// Sample returns the Sample underlying the histogram. -func (h *HistogramSnapshot) Sample() Sample { return h.sample } - -// Snapshot returns the snapshot. -func (h *HistogramSnapshot) Snapshot() Histogram { return h } - -// StdDev returns the standard deviation of the values in the sample at the -// time the snapshot was taken. -func (h *HistogramSnapshot) StdDev() float64 { return h.sample.StdDev() } - -// Sum returns the sum in the sample at the time the snapshot was taken. -func (h *HistogramSnapshot) Sum() int64 { return h.sample.Sum() } - -// Update panics. -func (*HistogramSnapshot) Update(int64) { - panic("Update called on a HistogramSnapshot") -} - -// Variance returns the variance of inputs at the time the snapshot was taken. -func (h *HistogramSnapshot) Variance() float64 { return h.sample.Variance() } - // NilHistogram is a no-op Histogram. type NilHistogram struct{} -// Clear is a no-op. -func (NilHistogram) Clear() {} - -// Count is a no-op. -func (NilHistogram) Count() int64 { return 0 } - -// Max is a no-op. -func (NilHistogram) Max() int64 { return 0 } - -// Mean is a no-op. -func (NilHistogram) Mean() float64 { return 0.0 } - -// Min is a no-op. -func (NilHistogram) Min() int64 { return 0 } - -// Percentile is a no-op. -func (NilHistogram) Percentile(p float64) float64 { return 0.0 } - -// Percentiles is a no-op. -func (NilHistogram) Percentiles(ps []float64) []float64 { - return make([]float64, len(ps)) -} - -// Sample is a no-op. -func (NilHistogram) Sample() Sample { return NilSample{} } - -// Snapshot is a no-op. -func (NilHistogram) Snapshot() Histogram { return NilHistogram{} } - -// StdDev is a no-op. -func (NilHistogram) StdDev() float64 { return 0.0 } - -// Sum is a no-op. -func (NilHistogram) Sum() int64 { return 0 } - -// Update is a no-op. -func (NilHistogram) Update(v int64) {} - -// Variance is a no-op. -func (NilHistogram) Variance() float64 { return 0.0 } +func (NilHistogram) Clear() {} +func (NilHistogram) Snapshot() HistogramSnapshot { return (*emptySnapshot)(nil) } +func (NilHistogram) Update(v int64) {} // StandardHistogram is the standard implementation of a Histogram and uses a // Sample to bound its memory use. @@ -166,46 +64,10 @@ type StandardHistogram struct { // Clear clears the histogram and its sample. func (h *StandardHistogram) Clear() { h.sample.Clear() } -// Count returns the number of samples recorded since the histogram was last -// cleared. -func (h *StandardHistogram) Count() int64 { return h.sample.Count() } - -// Max returns the maximum value in the sample. -func (h *StandardHistogram) Max() int64 { return h.sample.Max() } - -// Mean returns the mean of the values in the sample. -func (h *StandardHistogram) Mean() float64 { return h.sample.Mean() } - -// Min returns the minimum value in the sample. -func (h *StandardHistogram) Min() int64 { return h.sample.Min() } - -// Percentile returns an arbitrary percentile of the values in the sample. -func (h *StandardHistogram) Percentile(p float64) float64 { - return h.sample.Percentile(p) -} - -// Percentiles returns a slice of arbitrary percentiles of the values in the -// sample. -func (h *StandardHistogram) Percentiles(ps []float64) []float64 { - return h.sample.Percentiles(ps) -} - -// Sample returns the Sample underlying the histogram. -func (h *StandardHistogram) Sample() Sample { return h.sample } - // Snapshot returns a read-only copy of the histogram. -func (h *StandardHistogram) Snapshot() Histogram { - return &HistogramSnapshot{sample: h.sample.Snapshot().(*SampleSnapshot)} +func (h *StandardHistogram) Snapshot() HistogramSnapshot { + return h.sample.Snapshot() } -// StdDev returns the standard deviation of the values in the sample. -func (h *StandardHistogram) StdDev() float64 { return h.sample.StdDev() } - -// Sum returns the sum in the sample. -func (h *StandardHistogram) Sum() int64 { return h.sample.Sum() } - // Update samples a new value. func (h *StandardHistogram) Update(v int64) { h.sample.Update(v) } - -// Variance returns the variance of the values in the sample. -func (h *StandardHistogram) Variance() float64 { return h.sample.Variance() } diff --git a/metrics/histogram_test.go b/metrics/histogram_test.go index 7c9f42fcec96..22fc5468b0b5 100644 --- a/metrics/histogram_test.go +++ b/metrics/histogram_test.go @@ -14,7 +14,7 @@ func TestGetOrRegisterHistogram(t *testing.T) { r := NewRegistry() s := NewUniformSample(100) NewRegisteredHistogram("foo", r, s).Update(47) - if h := GetOrRegisterHistogram("foo", r, s); h.Count() != 1 { + if h := GetOrRegisterHistogram("foo", r, s).Snapshot(); h.Count() != 1 { t.Fatal(h) } } @@ -24,11 +24,11 @@ func TestHistogram10000(t *testing.T) { for i := 1; i <= 10000; i++ { h.Update(int64(i)) } - testHistogram10000(t, h) + testHistogram10000(t, h.Snapshot()) } func TestHistogramEmpty(t *testing.T) { - h := NewHistogram(NewUniformSample(100)) + h := NewHistogram(NewUniformSample(100)).Snapshot() if count := h.Count(); count != 0 { t.Errorf("h.Count(): 0 != %v\n", count) } @@ -66,7 +66,7 @@ func TestHistogramSnapshot(t *testing.T) { testHistogram10000(t, snapshot) } -func testHistogram10000(t *testing.T, h Histogram) { +func testHistogram10000(t *testing.T, h HistogramSnapshot) { if count := h.Count(); count != 10000 { t.Errorf("h.Count(): 10000 != %v\n", count) } diff --git a/metrics/inactive.go b/metrics/inactive.go new file mode 100644 index 000000000000..1f47f0210af3 --- /dev/null +++ b/metrics/inactive.go @@ -0,0 +1,48 @@ +// Copyright 2023 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package metrics + +// compile-time checks that interfaces are implemented. +var ( + _ SampleSnapshot = (*emptySnapshot)(nil) + _ HistogramSnapshot = (*emptySnapshot)(nil) + _ CounterSnapshot = (*emptySnapshot)(nil) + _ GaugeSnapshot = (*emptySnapshot)(nil) + _ MeterSnapshot = (*emptySnapshot)(nil) + _ EWMASnapshot = (*emptySnapshot)(nil) + _ TimerSnapshot = (*emptySnapshot)(nil) +) + +type emptySnapshot struct{} + +func (*emptySnapshot) Count() int64 { return 0 } +func (*emptySnapshot) Max() int64 { return 0 } +func (*emptySnapshot) Mean() float64 { return 0.0 } +func (*emptySnapshot) Min() int64 { return 0 } +func (*emptySnapshot) Percentile(p float64) float64 { return 0.0 } +func (*emptySnapshot) Percentiles(ps []float64) []float64 { return make([]float64, len(ps)) } +func (*emptySnapshot) Size() int { return 0 } +func (*emptySnapshot) StdDev() float64 { return 0.0 } +func (*emptySnapshot) Sum() int64 { return 0 } +func (*emptySnapshot) Values() []int64 { return []int64{} } +func (*emptySnapshot) Variance() float64 { return 0.0 } +func (*emptySnapshot) Value() int64 { return 0 } +func (*emptySnapshot) Rate() float64 { return 0.0 } +func (*emptySnapshot) Rate1() float64 { return 0.0 } +func (*emptySnapshot) Rate5() float64 { return 0.0 } +func (*emptySnapshot) Rate15() float64 { return 0.0 } +func (*emptySnapshot) RateMean() float64 { return 0.0 } diff --git a/metrics/influxdb/influxdb.go b/metrics/influxdb/influxdb.go index a1a210033ff4..d7c35b671821 100644 --- a/metrics/influxdb/influxdb.go +++ b/metrics/influxdb/influxdb.go @@ -11,13 +11,13 @@ func readMeter(namespace, name string, i interface{}) (string, map[string]interf case metrics.Counter: measurement := fmt.Sprintf("%s%s.count", namespace, name) fields := map[string]interface{}{ - "value": metric.Count(), + "value": metric.Snapshot().Count(), } return measurement, fields case metrics.CounterFloat64: measurement := fmt.Sprintf("%s%s.count", namespace, name) fields := map[string]interface{}{ - "value": metric.Count(), + "value": metric.Snapshot().Count(), } return measurement, fields case metrics.Gauge: @@ -99,20 +99,19 @@ func readMeter(namespace, name string, i interface{}) (string, map[string]interf return measurement, fields case metrics.ResettingTimer: t := metric.Snapshot() - if len(t.Values()) == 0 { + if t.Count() == 0 { break } - ps := t.Percentiles([]float64{50, 95, 99}) - val := t.Values() + ps := t.Percentiles([]float64{0.50, 0.95, 0.99}) measurement := fmt.Sprintf("%s%s.span", namespace, name) fields := map[string]interface{}{ - "count": len(val), - "max": val[len(val)-1], + "count": t.Count(), + "max": t.Max(), "mean": t.Mean(), - "min": val[0], - "p50": ps[0], - "p95": ps[1], - "p99": ps[2], + "min": t.Min(), + "p50": int(ps[0]), + "p95": int(ps[1]), + "p99": int(ps[2]), } return measurement, fields } diff --git a/metrics/influxdb/influxdb_test.go b/metrics/influxdb/influxdb_test.go index dca3d9ab3264..07af3f90ce21 100644 --- a/metrics/influxdb/influxdb_test.go +++ b/metrics/influxdb/influxdb_test.go @@ -96,7 +96,7 @@ func TestExampleV2(t *testing.T) { } if have != want { t.Errorf("\nhave:\n%v\nwant:\n%v\n", have, want) - t.Logf("have vs want:\n %v", findFirstDiffPos(have, want)) + t.Logf("have vs want:\n%v", findFirstDiffPos(have, want)) } } diff --git a/metrics/influxdb/testdata/influxdbv1.want b/metrics/influxdb/testdata/influxdbv1.want index 5efffb959504..9443faedc5a2 100644 --- a/metrics/influxdb/testdata/influxdbv1.want +++ b/metrics/influxdb/testdata/influxdbv1.want @@ -1,3 +1,5 @@ +goth.system/cpu/schedlatency.histogram count=5645i,max=41943040i,mean=1819544.0410983171,min=0i,p25=0,p50=0,p75=7168,p95=16777216,p99=29360128,p999=33554432,p9999=33554432,stddev=6393570.217198883,variance=40877740122252.57 978307200000000000 +goth.system/memory/pauses.histogram count=14i,max=229376i,mean=50066.28571428572,min=5120i,p25=10240,p50=32768,p75=57344,p95=196608,p99=196608,p999=196608,p9999=196608,stddev=54726.062410783874,variance=2994941906.9890113 978307200000000000 goth.test/counter.count value=12345 978307200000000000 goth.test/counter_float64.count value=54321.98 978307200000000000 goth.test/gauge.gauge value=23456i 978307200000000000 @@ -5,5 +7,5 @@ goth.test/gauge_float64.gauge value=34567.89 978307200000000000 goth.test/gauge_info.gauge value="{\"arch\":\"amd64\",\"commit\":\"7caa2d8163ae3132c1c2d6978c76610caee2d949\",\"os\":\"linux\",\"protocol_versions\":\"64 65 66\",\"version\":\"1.10.18-unstable\"}" 978307200000000000 goth.test/histogram.histogram count=3i,max=3i,mean=2,min=1i,p25=1,p50=2,p75=3,p95=3,p99=3,p999=3,p9999=3,stddev=0.816496580927726,variance=0.6666666666666666 978307200000000000 goth.test/meter.meter count=0i,m1=0,m15=0,m5=0,mean=0 978307200000000000 -goth.test/resetting_timer.span count=6i,max=120000000i,mean=30000000,min=10000000i,p50=12000000i,p95=120000000i,p99=120000000i 978307200000000000 +goth.test/resetting_timer.span count=6i,max=120000000i,mean=30000000,min=10000000i,p50=12500000i,p95=120000000i,p99=120000000i 978307200000000000 goth.test/timer.timer count=6i,m1=0,m15=0,m5=0,max=120000000i,mean=38333333.333333336,meanrate=0,min=20000000i,p50=22500000,p75=48000000,p95=120000000,p99=120000000,p999=120000000,p9999=120000000,stddev=36545253.529775314,variance=1335555555555555.2 978307200000000000 diff --git a/metrics/influxdb/testdata/influxdbv2.want b/metrics/influxdb/testdata/influxdbv2.want index 5efffb959504..9443faedc5a2 100644 --- a/metrics/influxdb/testdata/influxdbv2.want +++ b/metrics/influxdb/testdata/influxdbv2.want @@ -1,3 +1,5 @@ +goth.system/cpu/schedlatency.histogram count=5645i,max=41943040i,mean=1819544.0410983171,min=0i,p25=0,p50=0,p75=7168,p95=16777216,p99=29360128,p999=33554432,p9999=33554432,stddev=6393570.217198883,variance=40877740122252.57 978307200000000000 +goth.system/memory/pauses.histogram count=14i,max=229376i,mean=50066.28571428572,min=5120i,p25=10240,p50=32768,p75=57344,p95=196608,p99=196608,p999=196608,p9999=196608,stddev=54726.062410783874,variance=2994941906.9890113 978307200000000000 goth.test/counter.count value=12345 978307200000000000 goth.test/counter_float64.count value=54321.98 978307200000000000 goth.test/gauge.gauge value=23456i 978307200000000000 @@ -5,5 +7,5 @@ goth.test/gauge_float64.gauge value=34567.89 978307200000000000 goth.test/gauge_info.gauge value="{\"arch\":\"amd64\",\"commit\":\"7caa2d8163ae3132c1c2d6978c76610caee2d949\",\"os\":\"linux\",\"protocol_versions\":\"64 65 66\",\"version\":\"1.10.18-unstable\"}" 978307200000000000 goth.test/histogram.histogram count=3i,max=3i,mean=2,min=1i,p25=1,p50=2,p75=3,p95=3,p99=3,p999=3,p9999=3,stddev=0.816496580927726,variance=0.6666666666666666 978307200000000000 goth.test/meter.meter count=0i,m1=0,m15=0,m5=0,mean=0 978307200000000000 -goth.test/resetting_timer.span count=6i,max=120000000i,mean=30000000,min=10000000i,p50=12000000i,p95=120000000i,p99=120000000i 978307200000000000 +goth.test/resetting_timer.span count=6i,max=120000000i,mean=30000000,min=10000000i,p50=12500000i,p95=120000000i,p99=120000000i 978307200000000000 goth.test/timer.timer count=6i,m1=0,m15=0,m5=0,max=120000000i,mean=38333333.333333336,meanrate=0,min=20000000i,p50=22500000,p75=48000000,p95=120000000,p99=120000000,p999=120000000,p9999=120000000,stddev=36545253.529775314,variance=1335555555555555.2 978307200000000000 diff --git a/metrics/internal/sampledata.go b/metrics/internal/sampledata.go index 6b03af394f70..15b52066f16a 100644 --- a/metrics/internal/sampledata.go +++ b/metrics/internal/sampledata.go @@ -17,6 +17,9 @@ package internal import ( + "bytes" + "encoding/gob" + metrics2 "runtime/metrics" "time" "github.com/XinFinOrg/XDPoSChain/metrics" @@ -38,7 +41,15 @@ func ExampleMetrics() metrics.Registry { "commit": "7caa2d8163ae3132c1c2d6978c76610caee2d949", "protocol_versions": "64 65 66", }) - metrics.NewRegisteredHistogram("test/histogram", registry, metrics.NewSampleSnapshot(3, []int64{1, 2, 3})) + + { + s := metrics.NewUniformSample(3) + s.Update(1) + s.Update(2) + s.Update(3) + //metrics.NewRegisteredHistogram("test/histogram", registry, metrics.NewSampleSnapshot(3, []int64{1, 2, 3})) + metrics.NewRegisteredHistogram("test/histogram", registry, s) + } registry.Register("test/meter", metrics.NewInactiveMeter()) { timer := metrics.NewRegisteredResettingTimer("test/resetting_timer", registry) @@ -60,5 +71,25 @@ func ExampleMetrics() metrics.Registry { timer.Stop() } registry.Register("test/empty_resetting_timer", metrics.NewResettingTimer().Snapshot()) + + { // go runtime metrics + var sLatency = "7\xff\x81\x03\x01\x01\x10Float64Histogram\x01\xff\x82\x00\x01\x02\x01\x06Counts\x01\xff\x84\x00\x01\aBuckets\x01\xff\x86\x00\x00\x00\x16\xff\x83\x02\x01\x01\b[]uint64\x01\xff\x84\x00\x01\x06\x00\x00\x17\xff\x85\x02\x01\x01\t[]float64\x01\xff\x86\x00\x01\b\x00\x00\xfe\x06T\xff\x82\x01\xff\xa2\x00\xfe\r\xef\x00\x01\x02\x02\x04\x05\x04\b\x15\x17 B?6.L;$!2) \x1a? \x190aH7FY6#\x190\x1d\x14\x10\x1b\r\t\x04\x03\x01\x01\x00\x03\x02\x00\x03\x05\x05\x02\x02\x06\x04\v\x06\n\x15\x18\x13'&.\x12=H/L&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\xa3\xfe\xf0\xff\x00\xf8\x95\xd6&\xe8\v.q>\xf8\x95\xd6&\xe8\v.\x81>\xf8\xdfA:\xdc\x11ʼn>\xf8\x95\xd6&\xe8\v.\x91>\xf8:\x8c0\xe2\x8ey\x95>\xf8\xdfA:\xdc\x11ř>\xf8\x84\xf7C֔\x10\x9e>\xf8\x95\xd6&\xe8\v.\xa1>\xf8:\x8c0\xe2\x8ey\xa5>\xf8\xdfA:\xdc\x11ũ>\xf8\x84\xf7C֔\x10\xae>\xf8\x95\xd6&\xe8\v.\xb1>\xf8:\x8c0\xe2\x8ey\xb5>\xf8\xdfA:\xdc\x11Ź>\xf8\x84\xf7C֔\x10\xbe>\xf8\x95\xd6&\xe8\v.\xc1>\xf8:\x8c0\xe2\x8ey\xc5>\xf8\xdfA:\xdc\x11\xc5\xc9>\xf8\x84\xf7C֔\x10\xce>\xf8\x95\xd6&\xe8\v.\xd1>\xf8:\x8c0\xe2\x8ey\xd5>\xf8\xdfA:\xdc\x11\xc5\xd9>\xf8\x84\xf7C֔\x10\xde>\xf8\x95\xd6&\xe8\v.\xe1>\xf8:\x8c0\xe2\x8ey\xe5>\xf8\xdfA:\xdc\x11\xc5\xe9>\xf8\x84\xf7C֔\x10\xee>\xf8\x95\xd6&\xe8\v.\xf1>\xf8:\x8c0\xe2\x8ey\xf5>\xf8\xdfA:\xdc\x11\xc5\xf9>\xf8\x84\xf7C֔\x10\xfe>\xf8\x95\xd6&\xe8\v.\x01?\xf8:\x8c0\xe2\x8ey\x05?\xf8\xdfA:\xdc\x11\xc5\t?\xf8\x84\xf7C֔\x10\x0e?\xf8\x95\xd6&\xe8\v.\x11?\xf8:\x8c0\xe2\x8ey\x15?\xf8\xdfA:\xdc\x11\xc5\x19?\xf8\x84\xf7C֔\x10\x1e?\xf8\x95\xd6&\xe8\v.!?\xf8:\x8c0\xe2\x8ey%?\xf8\xdfA:\xdc\x11\xc5)?\xf8\x84\xf7C֔\x10.?\xf8\x95\xd6&\xe8\v.1?\xf8:\x8c0\xe2\x8ey5?\xf8\xdfA:\xdc\x11\xc59?\xf8\x84\xf7C֔\x10>?\xf8\x95\xd6&\xe8\v.A?\xf8:\x8c0\xe2\x8eyE?\xf8\xdfA:\xdc\x11\xc5I?\xf8\x84\xf7C֔\x10N?\xf8\x95\xd6&\xe8\v.Q?\xf8:\x8c0\xe2\x8eyU?\xf8\xdfA:\xdc\x11\xc5Y?\xf8\x84\xf7C֔\x10^?\xf8\x95\xd6&\xe8\v.a?\xf8:\x8c0\xe2\x8eye?\xf8\xdfA:\xdc\x11\xc5i?\xf8\x84\xf7C֔\x10n?\xf8\x95\xd6&\xe8\v.q?\xf8:\x8c0\xe2\x8eyu?\xf8\xdfA:\xdc\x11\xc5y?\xf8\x84\xf7C֔\x10~?\xf8\x95\xd6&\xe8\v.\x81?\xf8:\x8c0\xe2\x8ey\x85?\xf8\xdfA:\xdc\x11ʼn?\xf8\x84\xf7C֔\x10\x8e?\xf8\x95\xd6&\xe8\v.\x91?\xf8:\x8c0\xe2\x8ey\x95?\xf8\xdfA:\xdc\x11ř?\xf8\x84\xf7C֔\x10\x9e?\xf8\x95\xd6&\xe8\v.\xa1?\xf8:\x8c0\xe2\x8ey\xa5?\xf8\xdfA:\xdc\x11ũ?\xf8\x84\xf7C֔\x10\xae?\xf8\x95\xd6&\xe8\v.\xb1?\xf8:\x8c0\xe2\x8ey\xb5?\xf8\xdfA:\xdc\x11Ź?\xf8\x84\xf7C֔\x10\xbe?\xf8\x95\xd6&\xe8\v.\xc1?\xf8:\x8c0\xe2\x8ey\xc5?\xf8\xdfA:\xdc\x11\xc5\xc9?\xf8\x84\xf7C֔\x10\xce?\xf8\x95\xd6&\xe8\v.\xd1?\xf8:\x8c0\xe2\x8ey\xd5?\xf8\xdfA:\xdc\x11\xc5\xd9?\xf8\x84\xf7C֔\x10\xde?\xf8\x95\xd6&\xe8\v.\xe1?\xf8:\x8c0\xe2\x8ey\xe5?\xf8\xdfA:\xdc\x11\xc5\xe9?\xf8\x84\xf7C֔\x10\xee?\xf8\x95\xd6&\xe8\v.\xf1?\xf8:\x8c0\xe2\x8ey\xf5?\xf8\xdfA:\xdc\x11\xc5\xf9?\xf8\x84\xf7C֔\x10\xfe?\xf8\x95\xd6&\xe8\v.\x01@\xf8:\x8c0\xe2\x8ey\x05@\xf8\xdfA:\xdc\x11\xc5\t@\xf8\x84\xf7C֔\x10\x0e@\xf8\x95\xd6&\xe8\v.\x11@\xf8:\x8c0\xe2\x8ey\x15@\xf8\xdfA:\xdc\x11\xc5\x19@\xf8\x84\xf7C֔\x10\x1e@\xf8\x95\xd6&\xe8\v.!@\xf8:\x8c0\xe2\x8ey%@\xf8\xdfA:\xdc\x11\xc5)@\xf8\x84\xf7C֔\x10.@\xf8\x95\xd6&\xe8\v.1@\xf8:\x8c0\xe2\x8ey5@\xf8\xdfA:\xdc\x11\xc59@\xf8\x84\xf7C֔\x10>@\xf8\x95\xd6&\xe8\v.A@\xf8:\x8c0\xe2\x8eyE@\xf8\xdfA:\xdc\x11\xc5I@\xf8\x84\xf7C֔\x10N@\xf8\x95\xd6&\xe8\v.Q@\xf8:\x8c0\xe2\x8eyU@\xf8\xdfA:\xdc\x11\xc5Y@\xf8\x84\xf7C֔\x10^@\xf8\x95\xd6&\xe8\v.a@\xf8:\x8c0\xe2\x8eye@\xf8\xdfA:\xdc\x11\xc5i@\xf8\x84\xf7C֔\x10n@\xf8\x95\xd6&\xe8\v.q@\xf8:\x8c0\xe2\x8eyu@\xf8\xdfA:\xdc\x11\xc5y@\xf8\x84\xf7C֔\x10~@\xf8\x95\xd6&\xe8\v.\x81@\xf8:\x8c0\xe2\x8ey\x85@\xf8\xdfA:\xdc\x11ʼn@\xf8\x84\xf7C֔\x10\x8e@\xf8\x95\xd6&\xe8\v.\x91@\xf8:\x8c0\xe2\x8ey\x95@\xf8\xdfA:\xdc\x11ř@\xf8\x84\xf7C֔\x10\x9e@\xf8\x95\xd6&\xe8\v.\xa1@\xf8:\x8c0\xe2\x8ey\xa5@\xf8\xdfA:\xdc\x11ũ@\xf8\x84\xf7C֔\x10\xae@\xf8\x95\xd6&\xe8\v.\xb1@\xf8:\x8c0\xe2\x8ey\xb5@\xf8\xdfA:\xdc\x11Ź@\xf8\x84\xf7C֔\x10\xbe@\xf8\x95\xd6&\xe8\v.\xc1@\xf8:\x8c0\xe2\x8ey\xc5@\xf8\xdfA:\xdc\x11\xc5\xc9@\xf8\x84\xf7C֔\x10\xce@\xf8\x95\xd6&\xe8\v.\xd1@\xf8:\x8c0\xe2\x8ey\xd5@\xf8\xdfA:\xdc\x11\xc5\xd9@\xf8\x84\xf7C֔\x10\xde@\xf8\x95\xd6&\xe8\v.\xe1@\xf8:\x8c0\xe2\x8ey\xe5@\xf8\xdfA:\xdc\x11\xc5\xe9@\xf8\x84\xf7C֔\x10\xee@\xf8\x95\xd6&\xe8\v.\xf1@\xf8:\x8c0\xe2\x8ey\xf5@\xf8\xdfA:\xdc\x11\xc5\xf9@\xf8\x84\xf7C֔\x10\xfe@\xf8\x95\xd6&\xe8\v.\x01A\xfe\xf0\x7f\x00" + var gcPauses = "7\xff\x81\x03\x01\x01\x10Float64Histogram\x01\xff\x82\x00\x01\x02\x01\x06Counts\x01\xff\x84\x00\x01\aBuckets\x01\xff\x86\x00\x00\x00\x16\xff\x83\x02\x01\x01\b[]uint64\x01\xff\x84\x00\x01\x06\x00\x00\x17\xff\x85\x02\x01\x01\t[]float64\x01\xff\x86\x00\x01\b\x00\x00\xfe\x06R\xff\x82\x01\xff\xa2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x00\x01\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x00\x02\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\xa3\xfe\xf0\xff\x00\xf8\x95\xd6&\xe8\v.q>\xf8\x95\xd6&\xe8\v.\x81>\xf8\xdfA:\xdc\x11ʼn>\xf8\x95\xd6&\xe8\v.\x91>\xf8:\x8c0\xe2\x8ey\x95>\xf8\xdfA:\xdc\x11ř>\xf8\x84\xf7C֔\x10\x9e>\xf8\x95\xd6&\xe8\v.\xa1>\xf8:\x8c0\xe2\x8ey\xa5>\xf8\xdfA:\xdc\x11ũ>\xf8\x84\xf7C֔\x10\xae>\xf8\x95\xd6&\xe8\v.\xb1>\xf8:\x8c0\xe2\x8ey\xb5>\xf8\xdfA:\xdc\x11Ź>\xf8\x84\xf7C֔\x10\xbe>\xf8\x95\xd6&\xe8\v.\xc1>\xf8:\x8c0\xe2\x8ey\xc5>\xf8\xdfA:\xdc\x11\xc5\xc9>\xf8\x84\xf7C֔\x10\xce>\xf8\x95\xd6&\xe8\v.\xd1>\xf8:\x8c0\xe2\x8ey\xd5>\xf8\xdfA:\xdc\x11\xc5\xd9>\xf8\x84\xf7C֔\x10\xde>\xf8\x95\xd6&\xe8\v.\xe1>\xf8:\x8c0\xe2\x8ey\xe5>\xf8\xdfA:\xdc\x11\xc5\xe9>\xf8\x84\xf7C֔\x10\xee>\xf8\x95\xd6&\xe8\v.\xf1>\xf8:\x8c0\xe2\x8ey\xf5>\xf8\xdfA:\xdc\x11\xc5\xf9>\xf8\x84\xf7C֔\x10\xfe>\xf8\x95\xd6&\xe8\v.\x01?\xf8:\x8c0\xe2\x8ey\x05?\xf8\xdfA:\xdc\x11\xc5\t?\xf8\x84\xf7C֔\x10\x0e?\xf8\x95\xd6&\xe8\v.\x11?\xf8:\x8c0\xe2\x8ey\x15?\xf8\xdfA:\xdc\x11\xc5\x19?\xf8\x84\xf7C֔\x10\x1e?\xf8\x95\xd6&\xe8\v.!?\xf8:\x8c0\xe2\x8ey%?\xf8\xdfA:\xdc\x11\xc5)?\xf8\x84\xf7C֔\x10.?\xf8\x95\xd6&\xe8\v.1?\xf8:\x8c0\xe2\x8ey5?\xf8\xdfA:\xdc\x11\xc59?\xf8\x84\xf7C֔\x10>?\xf8\x95\xd6&\xe8\v.A?\xf8:\x8c0\xe2\x8eyE?\xf8\xdfA:\xdc\x11\xc5I?\xf8\x84\xf7C֔\x10N?\xf8\x95\xd6&\xe8\v.Q?\xf8:\x8c0\xe2\x8eyU?\xf8\xdfA:\xdc\x11\xc5Y?\xf8\x84\xf7C֔\x10^?\xf8\x95\xd6&\xe8\v.a?\xf8:\x8c0\xe2\x8eye?\xf8\xdfA:\xdc\x11\xc5i?\xf8\x84\xf7C֔\x10n?\xf8\x95\xd6&\xe8\v.q?\xf8:\x8c0\xe2\x8eyu?\xf8\xdfA:\xdc\x11\xc5y?\xf8\x84\xf7C֔\x10~?\xf8\x95\xd6&\xe8\v.\x81?\xf8:\x8c0\xe2\x8ey\x85?\xf8\xdfA:\xdc\x11ʼn?\xf8\x84\xf7C֔\x10\x8e?\xf8\x95\xd6&\xe8\v.\x91?\xf8:\x8c0\xe2\x8ey\x95?\xf8\xdfA:\xdc\x11ř?\xf8\x84\xf7C֔\x10\x9e?\xf8\x95\xd6&\xe8\v.\xa1?\xf8:\x8c0\xe2\x8ey\xa5?\xf8\xdfA:\xdc\x11ũ?\xf8\x84\xf7C֔\x10\xae?\xf8\x95\xd6&\xe8\v.\xb1?\xf8:\x8c0\xe2\x8ey\xb5?\xf8\xdfA:\xdc\x11Ź?\xf8\x84\xf7C֔\x10\xbe?\xf8\x95\xd6&\xe8\v.\xc1?\xf8:\x8c0\xe2\x8ey\xc5?\xf8\xdfA:\xdc\x11\xc5\xc9?\xf8\x84\xf7C֔\x10\xce?\xf8\x95\xd6&\xe8\v.\xd1?\xf8:\x8c0\xe2\x8ey\xd5?\xf8\xdfA:\xdc\x11\xc5\xd9?\xf8\x84\xf7C֔\x10\xde?\xf8\x95\xd6&\xe8\v.\xe1?\xf8:\x8c0\xe2\x8ey\xe5?\xf8\xdfA:\xdc\x11\xc5\xe9?\xf8\x84\xf7C֔\x10\xee?\xf8\x95\xd6&\xe8\v.\xf1?\xf8:\x8c0\xe2\x8ey\xf5?\xf8\xdfA:\xdc\x11\xc5\xf9?\xf8\x84\xf7C֔\x10\xfe?\xf8\x95\xd6&\xe8\v.\x01@\xf8:\x8c0\xe2\x8ey\x05@\xf8\xdfA:\xdc\x11\xc5\t@\xf8\x84\xf7C֔\x10\x0e@\xf8\x95\xd6&\xe8\v.\x11@\xf8:\x8c0\xe2\x8ey\x15@\xf8\xdfA:\xdc\x11\xc5\x19@\xf8\x84\xf7C֔\x10\x1e@\xf8\x95\xd6&\xe8\v.!@\xf8:\x8c0\xe2\x8ey%@\xf8\xdfA:\xdc\x11\xc5)@\xf8\x84\xf7C֔\x10.@\xf8\x95\xd6&\xe8\v.1@\xf8:\x8c0\xe2\x8ey5@\xf8\xdfA:\xdc\x11\xc59@\xf8\x84\xf7C֔\x10>@\xf8\x95\xd6&\xe8\v.A@\xf8:\x8c0\xe2\x8eyE@\xf8\xdfA:\xdc\x11\xc5I@\xf8\x84\xf7C֔\x10N@\xf8\x95\xd6&\xe8\v.Q@\xf8:\x8c0\xe2\x8eyU@\xf8\xdfA:\xdc\x11\xc5Y@\xf8\x84\xf7C֔\x10^@\xf8\x95\xd6&\xe8\v.a@\xf8:\x8c0\xe2\x8eye@\xf8\xdfA:\xdc\x11\xc5i@\xf8\x84\xf7C֔\x10n@\xf8\x95\xd6&\xe8\v.q@\xf8:\x8c0\xe2\x8eyu@\xf8\xdfA:\xdc\x11\xc5y@\xf8\x84\xf7C֔\x10~@\xf8\x95\xd6&\xe8\v.\x81@\xf8:\x8c0\xe2\x8ey\x85@\xf8\xdfA:\xdc\x11ʼn@\xf8\x84\xf7C֔\x10\x8e@\xf8\x95\xd6&\xe8\v.\x91@\xf8:\x8c0\xe2\x8ey\x95@\xf8\xdfA:\xdc\x11ř@\xf8\x84\xf7C֔\x10\x9e@\xf8\x95\xd6&\xe8\v.\xa1@\xf8:\x8c0\xe2\x8ey\xa5@\xf8\xdfA:\xdc\x11ũ@\xf8\x84\xf7C֔\x10\xae@\xf8\x95\xd6&\xe8\v.\xb1@\xf8:\x8c0\xe2\x8ey\xb5@\xf8\xdfA:\xdc\x11Ź@\xf8\x84\xf7C֔\x10\xbe@\xf8\x95\xd6&\xe8\v.\xc1@\xf8:\x8c0\xe2\x8ey\xc5@\xf8\xdfA:\xdc\x11\xc5\xc9@\xf8\x84\xf7C֔\x10\xce@\xf8\x95\xd6&\xe8\v.\xd1@\xf8:\x8c0\xe2\x8ey\xd5@\xf8\xdfA:\xdc\x11\xc5\xd9@\xf8\x84\xf7C֔\x10\xde@\xf8\x95\xd6&\xe8\v.\xe1@\xf8:\x8c0\xe2\x8ey\xe5@\xf8\xdfA:\xdc\x11\xc5\xe9@\xf8\x84\xf7C֔\x10\xee@\xf8\x95\xd6&\xe8\v.\xf1@\xf8:\x8c0\xe2\x8ey\xf5@\xf8\xdfA:\xdc\x11\xc5\xf9@\xf8\x84\xf7C֔\x10\xfe@\xf8\x95\xd6&\xe8\v.\x01A\xfe\xf0\x7f\x00" + + var secondsToNs = float64(time.Second) + + dserialize := func(data string) *metrics2.Float64Histogram { + var res metrics2.Float64Histogram + if err := gob.NewDecoder(bytes.NewReader([]byte(data))).Decode(&res); err != nil { + panic(err) + } + return &res + } + cpuSchedLatency := metrics.RuntimeHistogramFromData(secondsToNs, dserialize(sLatency)) + registry.Register("system/cpu/schedlatency", cpuSchedLatency) + + memPauses := metrics.RuntimeHistogramFromData(secondsToNs, dserialize(gcPauses)) + registry.Register("system/memory/pauses", memPauses) + } return registry } diff --git a/metrics/internal/sampledata_test.go b/metrics/internal/sampledata_test.go new file mode 100644 index 000000000000..f0c0ce39b286 --- /dev/null +++ b/metrics/internal/sampledata_test.go @@ -0,0 +1,27 @@ +package internal + +import ( + "bytes" + "encoding/gob" + "fmt" + metrics2 "runtime/metrics" + "testing" + "time" + + "github.com/XinFinOrg/XDPoSChain/metrics" +) + +func TestCollectRuntimeMetrics(t *testing.T) { + t.Skip("Only used for generating testdata") + serialize := func(path string, histogram *metrics2.Float64Histogram) { + var f = new(bytes.Buffer) + if err := gob.NewEncoder(f).Encode(histogram); err != nil { + panic(err) + } + fmt.Printf("var %v = %q\n", path, f.Bytes()) + } + time.Sleep(2 * time.Second) + stats := metrics.ReadRuntimeStats() + serialize("schedlatency", stats.SchedLatency) + serialize("gcpauses", stats.GCPauses) +} diff --git a/metrics/librato/librato.go b/metrics/librato/librato.go index 87c3ab6a9088..fa0123e05abf 100644 --- a/metrics/librato/librato.go +++ b/metrics/librato/librato.go @@ -61,16 +61,16 @@ func (rep *Reporter) Run() { // calculate sum of squares from data provided by metrics.Histogram // see http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods -func sumSquares(s metrics.Sample) float64 { - count := float64(s.Count()) - sumSquared := math.Pow(count*s.Mean(), 2) - sumSquares := math.Pow(count*s.StdDev(), 2) + sumSquared/count +func sumSquares(icount int64, mean, stDev float64) float64 { + count := float64(icount) + sumSquared := math.Pow(count*mean, 2) + sumSquares := math.Pow(count*stDev, 2) + sumSquared/count if math.IsNaN(sumSquares) { return 0.0 } return sumSquares } -func sumSquaresTimer(t metrics.Timer) float64 { +func sumSquaresTimer(t metrics.TimerSnapshot) float64 { count := float64(t.Count()) sumSquared := math.Pow(count*t.Mean(), 2) sumSquares := math.Pow(count*t.StdDev(), 2) + sumSquared/count @@ -97,9 +97,10 @@ func (rep *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot B measurement[Period] = rep.Interval.Seconds() switch m := metric.(type) { case metrics.Counter: - if m.Count() > 0 { + ms := m.Snapshot() + if ms.Count() > 0 { measurement[Name] = fmt.Sprintf("%s.%s", name, "count") - measurement[Value] = float64(m.Count()) + measurement[Value] = float64(ms.Count()) measurement[Attributes] = map[string]interface{}{ DisplayUnitsLong: Operations, DisplayUnitsShort: OperationsShort, @@ -108,9 +109,9 @@ func (rep *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot B snapshot.Counters = append(snapshot.Counters, measurement) } case metrics.CounterFloat64: - if m.Count() > 0 { + if count := m.Snapshot().Count(); count > 0 { measurement[Name] = fmt.Sprintf("%s.%s", name, "count") - measurement[Value] = m.Count() + measurement[Value] = count measurement[Attributes] = map[string]interface{}{ DisplayUnitsLong: Operations, DisplayUnitsShort: OperationsShort, @@ -120,44 +121,45 @@ func (rep *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot B } case metrics.Gauge: measurement[Name] = name - measurement[Value] = float64(m.Value()) + measurement[Value] = float64(m.Snapshot().Value()) snapshot.Gauges = append(snapshot.Gauges, measurement) case metrics.GaugeFloat64: measurement[Name] = name - measurement[Value] = m.Value() + measurement[Value] = m.Snapshot().Value() snapshot.Gauges = append(snapshot.Gauges, measurement) case metrics.GaugeInfo: measurement[Name] = name - measurement[Value] = m.Value() + measurement[Value] = m.Snapshot().Value() snapshot.Gauges = append(snapshot.Gauges, measurement) case metrics.Histogram: - if m.Count() > 0 { + ms := m.Snapshot() + if ms.Count() > 0 { gauges := make([]Measurement, histogramGaugeCount) - s := m.Sample() measurement[Name] = fmt.Sprintf("%s.%s", name, "hist") - measurement[Count] = uint64(s.Count()) - measurement[Max] = float64(s.Max()) - measurement[Min] = float64(s.Min()) - measurement[Sum] = float64(s.Sum()) - measurement[SumSquares] = sumSquares(s) + measurement[Count] = uint64(ms.Count()) + measurement[Max] = float64(ms.Max()) + measurement[Min] = float64(ms.Min()) + measurement[Sum] = float64(ms.Sum()) + measurement[SumSquares] = sumSquares(ms.Count(), ms.Mean(), ms.StdDev()) gauges[0] = measurement for i, p := range rep.Percentiles { gauges[i+1] = Measurement{ Name: fmt.Sprintf("%s.%.2f", measurement[Name], p), - Value: s.Percentile(p), + Value: ms.Percentile(p), Period: measurement[Period], } } snapshot.Gauges = append(snapshot.Gauges, gauges...) } case metrics.Meter: + ms := m.Snapshot() measurement[Name] = name - measurement[Value] = float64(m.Count()) + measurement[Value] = float64(ms.Count()) snapshot.Counters = append(snapshot.Counters, measurement) snapshot.Gauges = append(snapshot.Gauges, Measurement{ Name: fmt.Sprintf("%s.%s", name, "1min"), - Value: m.Rate1(), + Value: ms.Rate1(), Period: int64(rep.Interval.Seconds()), Attributes: map[string]interface{}{ DisplayUnitsLong: Operations, @@ -167,7 +169,7 @@ func (rep *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot B }, Measurement{ Name: fmt.Sprintf("%s.%s", name, "5min"), - Value: m.Rate5(), + Value: ms.Rate5(), Period: int64(rep.Interval.Seconds()), Attributes: map[string]interface{}{ DisplayUnitsLong: Operations, @@ -177,7 +179,7 @@ func (rep *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot B }, Measurement{ Name: fmt.Sprintf("%s.%s", name, "15min"), - Value: m.Rate15(), + Value: ms.Rate15(), Period: int64(rep.Interval.Seconds()), Attributes: map[string]interface{}{ DisplayUnitsLong: Operations, @@ -187,26 +189,27 @@ func (rep *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot B }, ) case metrics.Timer: + ms := m.Snapshot() measurement[Name] = name - measurement[Value] = float64(m.Count()) + measurement[Value] = float64(ms.Count()) snapshot.Counters = append(snapshot.Counters, measurement) - if m.Count() > 0 { + if ms.Count() > 0 { libratoName := fmt.Sprintf("%s.%s", name, "timer.mean") gauges := make([]Measurement, histogramGaugeCount) gauges[0] = Measurement{ Name: libratoName, - Count: uint64(m.Count()), - Sum: m.Mean() * float64(m.Count()), - Max: float64(m.Max()), - Min: float64(m.Min()), - SumSquares: sumSquaresTimer(m), + Count: uint64(ms.Count()), + Sum: ms.Mean() * float64(ms.Count()), + Max: float64(ms.Max()), + Min: float64(ms.Min()), + SumSquares: sumSquaresTimer(ms), Period: int64(rep.Interval.Seconds()), Attributes: rep.TimerAttributes, } for i, p := range rep.Percentiles { gauges[i+1] = Measurement{ Name: fmt.Sprintf("%s.timer.%2.0f", name, p*100), - Value: m.Percentile(p), + Value: ms.Percentile(p), Period: int64(rep.Interval.Seconds()), Attributes: rep.TimerAttributes, } @@ -215,7 +218,7 @@ func (rep *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot B snapshot.Gauges = append(snapshot.Gauges, Measurement{ Name: fmt.Sprintf("%s.%s", name, "rate.1min"), - Value: m.Rate1(), + Value: ms.Rate1(), Period: int64(rep.Interval.Seconds()), Attributes: map[string]interface{}{ DisplayUnitsLong: Operations, @@ -225,7 +228,7 @@ func (rep *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot B }, Measurement{ Name: fmt.Sprintf("%s.%s", name, "rate.5min"), - Value: m.Rate5(), + Value: ms.Rate5(), Period: int64(rep.Interval.Seconds()), Attributes: map[string]interface{}{ DisplayUnitsLong: Operations, @@ -235,7 +238,7 @@ func (rep *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot B }, Measurement{ Name: fmt.Sprintf("%s.%s", name, "rate.15min"), - Value: m.Rate15(), + Value: ms.Rate15(), Period: int64(rep.Interval.Seconds()), Attributes: map[string]interface{}{ DisplayUnitsLong: Operations, diff --git a/metrics/log.go b/metrics/log.go index d71a1c3d9c66..3b9773faa728 100644 --- a/metrics/log.go +++ b/metrics/log.go @@ -23,19 +23,19 @@ func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) { switch metric := i.(type) { case Counter: l.Printf("counter %s\n", name) - l.Printf(" count: %9d\n", metric.Count()) + l.Printf(" count: %9d\n", metric.Snapshot().Count()) case CounterFloat64: l.Printf("counter %s\n", name) - l.Printf(" count: %f\n", metric.Count()) + l.Printf(" count: %f\n", metric.Snapshot().Count()) case Gauge: l.Printf("gauge %s\n", name) - l.Printf(" value: %9d\n", metric.Value()) + l.Printf(" value: %9d\n", metric.Snapshot().Value()) case GaugeFloat64: l.Printf("gauge %s\n", name) - l.Printf(" value: %f\n", metric.Value()) + l.Printf(" value: %f\n", metric.Snapshot().Value()) case GaugeInfo: l.Printf("gauge %s\n", name) - l.Printf(" value: %s\n", metric.Value()) + l.Printf(" value: %s\n", metric.Snapshot().Value()) case Healthcheck: metric.Check() l.Printf("healthcheck %s\n", name) diff --git a/metrics/meter.go b/metrics/meter.go index 8a89dc4275f1..22475ef6ebee 100644 --- a/metrics/meter.go +++ b/metrics/meter.go @@ -1,21 +1,25 @@ package metrics import ( + "math" "sync" "sync/atomic" "time" ) -// Meters count events to produce exponentially-weighted moving average rates -// at one-, five-, and fifteen-minutes and a mean rate. -type Meter interface { +type MeterSnapshot interface { Count() int64 - Mark(int64) Rate1() float64 Rate5() float64 Rate15() float64 RateMean() float64 - Snapshot() Meter +} + +// Meters count events to produce exponentially-weighted moving average rates +// at one-, five-, and fifteen-minutes and a mean rate. +type Meter interface { + Mark(int64) + Snapshot() MeterSnapshot Stop() } @@ -30,17 +34,6 @@ func GetOrRegisterMeter(name string, r Registry) Meter { return r.GetOrRegister(name, NewMeter).(Meter) } -// GetOrRegisterMeterForced returns an existing Meter or constructs and registers a -// new StandardMeter no matter the global switch is enabled or not. -// Be sure to unregister the meter from the registry once it is of no use to -// allow for garbage collection. -func GetOrRegisterMeterForced(name string, r Registry) Meter { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, NewMeterForced).(Meter) -} - // NewMeter constructs a new StandardMeter and launches a goroutine. // Be sure to call Stop() once the meter is of no use to allow for garbage collection. func NewMeter() Meter { @@ -68,115 +61,53 @@ func NewInactiveMeter() Meter { return m } -// NewMeterForced constructs a new StandardMeter and launches a goroutine no matter -// the global switch is enabled or not. -// Be sure to call Stop() once the meter is of no use to allow for garbage collection. -func NewMeterForced() Meter { - m := newStandardMeter() - arbiter.Lock() - defer arbiter.Unlock() - arbiter.meters[m] = struct{}{} - if !arbiter.started { - arbiter.started = true - go arbiter.tick() - } - return m -} - // NewRegisteredMeter constructs and registers a new StandardMeter // and launches a goroutine. // Be sure to unregister the meter from the registry once it is of no use to // allow for garbage collection. func NewRegisteredMeter(name string, r Registry) Meter { - c := NewMeter() - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c + return GetOrRegisterMeter(name, r) } -// NewRegisteredMeterForced constructs and registers a new StandardMeter -// and launches a goroutine no matter the global switch is enabled or not. -// Be sure to unregister the meter from the registry once it is of no use to -// allow for garbage collection. -func NewRegisteredMeterForced(name string, r Registry) Meter { - c := NewMeterForced() - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// MeterSnapshot is a read-only copy of another Meter. -type MeterSnapshot struct { - temp atomic.Int64 +// meterSnapshot is a read-only copy of the meter's internal values. +type meterSnapshot struct { count int64 rate1, rate5, rate15, rateMean float64 } // Count returns the count of events at the time the snapshot was taken. -func (m *MeterSnapshot) Count() int64 { return m.count } - -// Mark panics. -func (*MeterSnapshot) Mark(n int64) { - panic("Mark called on a MeterSnapshot") -} +func (m *meterSnapshot) Count() int64 { return m.count } // Rate1 returns the one-minute moving average rate of events per second at the // time the snapshot was taken. -func (m *MeterSnapshot) Rate1() float64 { return m.rate1 } +func (m *meterSnapshot) Rate1() float64 { return m.rate1 } // Rate5 returns the five-minute moving average rate of events per second at // the time the snapshot was taken. -func (m *MeterSnapshot) Rate5() float64 { return m.rate5 } +func (m *meterSnapshot) Rate5() float64 { return m.rate5 } // Rate15 returns the fifteen-minute moving average rate of events per second // at the time the snapshot was taken. -func (m *MeterSnapshot) Rate15() float64 { return m.rate15 } +func (m *meterSnapshot) Rate15() float64 { return m.rate15 } // RateMean returns the meter's mean rate of events per second at the time the // snapshot was taken. -func (m *MeterSnapshot) RateMean() float64 { return m.rateMean } - -// Snapshot returns the snapshot. -func (m *MeterSnapshot) Snapshot() Meter { return m } - -// Stop is a no-op. -func (m *MeterSnapshot) Stop() {} +func (m *meterSnapshot) RateMean() float64 { return m.rateMean } // NilMeter is a no-op Meter. type NilMeter struct{} -// Count is a no-op. -func (NilMeter) Count() int64 { return 0 } - -// Mark is a no-op. -func (NilMeter) Mark(n int64) {} - -// Rate1 is a no-op. -func (NilMeter) Rate1() float64 { return 0.0 } - -// Rate5 is a no-op. -func (NilMeter) Rate5() float64 { return 0.0 } - -// Rate15 is a no-op. -func (NilMeter) Rate15() float64 { return 0.0 } - -// RateMean is a no-op. -func (NilMeter) RateMean() float64 { return 0.0 } - -// Snapshot is a no-op. -func (NilMeter) Snapshot() Meter { return NilMeter{} } - -// Stop is a no-op. -func (NilMeter) Stop() {} +func (NilMeter) Count() int64 { return 0 } +func (NilMeter) Mark(n int64) {} +func (NilMeter) Snapshot() MeterSnapshot { return (*emptySnapshot)(nil) } +func (NilMeter) Stop() {} // StandardMeter is the standard implementation of a Meter. type StandardMeter struct { - lock sync.RWMutex - snapshot *MeterSnapshot + count atomic.Int64 + uncounted atomic.Int64 // not yet added to the EWMAs + rateMean atomic.Uint64 + a1, a5, a15 EWMA startTime time.Time stopped atomic.Bool @@ -184,7 +115,6 @@ type StandardMeter struct { func newStandardMeter() *StandardMeter { return &StandardMeter{ - snapshot: &MeterSnapshot{}, a1: NewEWMA1(), a5: NewEWMA5(), a15: NewEWMA15(), @@ -194,97 +124,42 @@ func newStandardMeter() *StandardMeter { // Stop stops the meter, Mark() will be a no-op if you use it after being stopped. func (m *StandardMeter) Stop() { - stopped := m.stopped.Swap(true) - if !stopped { + if stopped := m.stopped.Swap(true); !stopped { arbiter.Lock() delete(arbiter.meters, m) arbiter.Unlock() } } -// Count returns the number of events recorded. -// It updates the meter to be as accurate as possible -func (m *StandardMeter) Count() int64 { - m.lock.Lock() - defer m.lock.Unlock() - m.updateMeter() - return m.snapshot.count -} - // Mark records the occurrence of n events. func (m *StandardMeter) Mark(n int64) { - m.snapshot.temp.Add(n) -} - -// Rate1 returns the one-minute moving average rate of events per second. -func (m *StandardMeter) Rate1() float64 { - m.lock.RLock() - defer m.lock.RUnlock() - return m.snapshot.rate1 -} - -// Rate5 returns the five-minute moving average rate of events per second. -func (m *StandardMeter) Rate5() float64 { - m.lock.RLock() - defer m.lock.RUnlock() - return m.snapshot.rate5 -} - -// Rate15 returns the fifteen-minute moving average rate of events per second. -func (m *StandardMeter) Rate15() float64 { - m.lock.RLock() - defer m.lock.RUnlock() - return m.snapshot.rate15 -} - -// RateMean returns the meter's mean rate of events per second. -func (m *StandardMeter) RateMean() float64 { - m.lock.RLock() - defer m.lock.RUnlock() - return m.snapshot.rateMean + m.uncounted.Add(n) } // Snapshot returns a read-only copy of the meter. -func (m *StandardMeter) Snapshot() Meter { - m.lock.RLock() - snapshot := MeterSnapshot{ - count: m.snapshot.count, - rate1: m.snapshot.rate1, - rate5: m.snapshot.rate5, - rate15: m.snapshot.rate15, - rateMean: m.snapshot.rateMean, +func (m *StandardMeter) Snapshot() MeterSnapshot { + return &meterSnapshot{ + count: m.count.Load() + m.uncounted.Load(), + rate1: m.a1.Snapshot().Rate(), + rate5: m.a5.Snapshot().Rate(), + rate15: m.a15.Snapshot().Rate(), + rateMean: math.Float64frombits(m.rateMean.Load()), } - snapshot.temp.Store(m.snapshot.temp.Load()) - m.lock.RUnlock() - return &snapshot -} - -func (m *StandardMeter) updateSnapshot() { - // should run with write lock held on m.lock - snapshot := m.snapshot - snapshot.rate1 = m.a1.Rate() - snapshot.rate5 = m.a5.Rate() - snapshot.rate15 = m.a15.Rate() - snapshot.rateMean = float64(snapshot.count) / time.Since(m.startTime).Seconds() } -func (m *StandardMeter) updateMeter() { - // should only run with write lock held on m.lock - n := m.snapshot.temp.Swap(0) - m.snapshot.count += n +func (m *StandardMeter) tick() { + // Take the uncounted values, add to count + n := m.uncounted.Swap(0) + count := m.count.Add(n) + m.rateMean.Store(math.Float64bits(float64(count) / time.Since(m.startTime).Seconds())) + // Update the EWMA's internal state m.a1.Update(n) m.a5.Update(n) m.a15.Update(n) -} - -func (m *StandardMeter) tick() { - m.lock.Lock() - defer m.lock.Unlock() - m.updateMeter() + // And trigger them to calculate the rates m.a1.Tick() m.a5.Tick() m.a15.Tick() - m.updateSnapshot() } // meterArbiter ticks meters every 5s from a single goroutine. diff --git a/metrics/meter_test.go b/metrics/meter_test.go index 99bbd6d98937..8330ea9647f5 100644 --- a/metrics/meter_test.go +++ b/metrics/meter_test.go @@ -12,11 +12,17 @@ func BenchmarkMeter(b *testing.B) { m.Mark(1) } } - +func TestMeter(t *testing.T) { + m := NewMeter() + m.Mark(47) + if v := m.Snapshot().Count(); v != 47 { + t.Fatalf("have %d want %d", v, 47) + } +} func TestGetOrRegisterMeter(t *testing.T) { r := NewRegistry() NewRegisteredMeter("foo", r).Mark(47) - if m := GetOrRegisterMeter("foo", r); m.Count() != 47 { + if m := GetOrRegisterMeter("foo", r).Snapshot(); m.Count() != 47 { t.Fatal(m.Count()) } } @@ -31,10 +37,10 @@ func TestMeterDecay(t *testing.T) { ma.meters[m] = struct{}{} m.Mark(1) ma.tickMeters() - rateMean := m.RateMean() + rateMean := m.Snapshot().RateMean() time.Sleep(100 * time.Millisecond) ma.tickMeters() - if m.RateMean() >= rateMean { + if m.Snapshot().RateMean() >= rateMean { t.Error("m.RateMean() didn't decrease") } } @@ -42,7 +48,7 @@ func TestMeterDecay(t *testing.T) { func TestMeterNonzero(t *testing.T) { m := NewMeter() m.Mark(3) - if count := m.Count(); count != 3 { + if count := m.Snapshot().Count(); count != 3 { t.Errorf("m.Count(): 3 != %v\n", count) } } @@ -59,16 +65,8 @@ func TestMeterStop(t *testing.T) { } } -func TestMeterSnapshot(t *testing.T) { - m := NewMeter() - m.Mark(1) - if snapshot := m.Snapshot(); m.RateMean() != snapshot.RateMean() { - t.Fatal(snapshot) - } -} - func TestMeterZero(t *testing.T) { - m := NewMeter() + m := NewMeter().Snapshot() if count := m.Count(); count != 0 { t.Errorf("m.Count(): 0 != %v\n", count) } @@ -79,13 +77,13 @@ func TestMeterRepeat(t *testing.T) { for i := 0; i < 101; i++ { m.Mark(int64(i)) } - if count := m.Count(); count != 5050 { + if count := m.Snapshot().Count(); count != 5050 { t.Errorf("m.Count(): 5050 != %v\n", count) } for i := 0; i < 101; i++ { m.Mark(int64(i)) } - if count := m.Count(); count != 10100 { + if count := m.Snapshot().Count(); count != 10100 { t.Errorf("m.Count(): 10100 != %v\n", count) } } diff --git a/metrics/metrics.go b/metrics/metrics.go index 3468cfe8e407..d5c1c61cbd06 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -85,6 +85,12 @@ var runtimeSamples = []metrics.Sample{ {Name: "/sched/latencies:seconds"}, // histogram } +func ReadRuntimeStats() *runtimeStats { + r := new(runtimeStats) + readRuntimeStats(r) + return r +} + func readRuntimeStats(v *runtimeStats) { metrics.Read(runtimeSamples) for _, s := range runtimeSamples { diff --git a/metrics/metrics_test.go b/metrics/metrics_test.go index 534c44139b36..2861d5f2caf6 100644 --- a/metrics/metrics_test.go +++ b/metrics/metrics_test.go @@ -98,8 +98,8 @@ func Example() { t.Time(func() { time.Sleep(10 * time.Millisecond) }) t.Update(1) - fmt.Println(c.Count()) - fmt.Println(t.Min()) + fmt.Println(c.Snapshot().Count()) + fmt.Println(t.Snapshot().Min()) // Output: 17 // 1 } diff --git a/metrics/opentsdb.go b/metrics/opentsdb.go index 4d2e209238fa..e81690f94340 100644 --- a/metrics/opentsdb.go +++ b/metrics/opentsdb.go @@ -65,15 +65,15 @@ func (c *OpenTSDBConfig) writeRegistry(w io.Writer, now int64, shortHostname str c.Registry.Each(func(name string, i interface{}) { switch metric := i.(type) { case Counter: - fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, metric.Count(), shortHostname) + fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, metric.Snapshot().Count(), shortHostname) case CounterFloat64: - fmt.Fprintf(w, "put %s.%s.count %d %f host=%s\n", c.Prefix, name, now, metric.Count(), shortHostname) + fmt.Fprintf(w, "put %s.%s.count %d %f host=%s\n", c.Prefix, name, now, metric.Snapshot().Count(), shortHostname) case Gauge: - fmt.Fprintf(w, "put %s.%s.value %d %d host=%s\n", c.Prefix, name, now, metric.Value(), shortHostname) + fmt.Fprintf(w, "put %s.%s.value %d %d host=%s\n", c.Prefix, name, now, metric.Snapshot().Value(), shortHostname) case GaugeFloat64: - fmt.Fprintf(w, "put %s.%s.value %d %f host=%s\n", c.Prefix, name, now, metric.Value(), shortHostname) + fmt.Fprintf(w, "put %s.%s.value %d %f host=%s\n", c.Prefix, name, now, metric.Snapshot().Value(), shortHostname) case GaugeInfo: - fmt.Fprintf(w, "put %s.%s.value %d %s host=%s\n", c.Prefix, name, now, metric.Value().String(), shortHostname) + fmt.Fprintf(w, "put %s.%s.value %d %s host=%s\n", c.Prefix, name, now, metric.Snapshot().Value().String(), shortHostname) case Histogram: h := metric.Snapshot() ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) diff --git a/metrics/opentsdb_test.go b/metrics/opentsdb_test.go index c02b98af061e..4548309f9c23 100644 --- a/metrics/opentsdb_test.go +++ b/metrics/opentsdb_test.go @@ -1,6 +1,7 @@ package metrics import ( + "fmt" "net" "os" "strings" @@ -47,5 +48,19 @@ func TestExampleOpenTSB(t *testing.T) { } if have, want := w.String(), string(wantB); have != want { t.Errorf("\nhave:\n%v\nwant:\n%v\n", have, want) + t.Logf("have vs want:\n%v", findFirstDiffPos(have, want)) } } + +func findFirstDiffPos(a, b string) string { + yy := strings.Split(b, "\n") + for i, x := range strings.Split(a, "\n") { + if i >= len(yy) { + return fmt.Sprintf("have:%d: %s\nwant:%d: ", i, x, i) + } + if y := yy[i]; x != y { + return fmt.Sprintf("have:%d: %s\nwant:%d: %s", i, x, i, y) + } + } + return "" +} diff --git a/metrics/prometheus/collector.go b/metrics/prometheus/collector.go index 2187438df8ee..0b81c63ba5b7 100644 --- a/metrics/prometheus/collector.go +++ b/metrics/prometheus/collector.go @@ -75,27 +75,27 @@ func (c *collector) Add(name string, i any) error { return nil } -func (c *collector) addCounter(name string, m metrics.Counter) { +func (c *collector) addCounter(name string, m metrics.CounterSnapshot) { c.writeGaugeCounter(name, m.Count()) } -func (c *collector) addCounterFloat64(name string, m metrics.CounterFloat64) { +func (c *collector) addCounterFloat64(name string, m metrics.CounterFloat64Snapshot) { c.writeGaugeCounter(name, m.Count()) } -func (c *collector) addGauge(name string, m metrics.Gauge) { +func (c *collector) addGauge(name string, m metrics.GaugeSnapshot) { c.writeGaugeCounter(name, m.Value()) } -func (c *collector) addGaugeFloat64(name string, m metrics.GaugeFloat64) { +func (c *collector) addGaugeFloat64(name string, m metrics.GaugeFloat64Snapshot) { c.writeGaugeCounter(name, m.Value()) } -func (c *collector) addGaugeInfo(name string, m metrics.GaugeInfo) { +func (c *collector) addGaugeInfo(name string, m metrics.GaugeInfoSnapshot) { c.writeGaugeInfo(name, m.Value()) } -func (c *collector) addHistogram(name string, m metrics.Histogram) { +func (c *collector) addHistogram(name string, m metrics.HistogramSnapshot) { pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999} ps := m.Percentiles(pv) c.writeSummaryCounter(name, m.Count()) @@ -106,11 +106,11 @@ func (c *collector) addHistogram(name string, m metrics.Histogram) { c.buff.WriteRune('\n') } -func (c *collector) addMeter(name string, m metrics.Meter) { +func (c *collector) addMeter(name string, m metrics.MeterSnapshot) { c.writeGaugeCounter(name, m.Count()) } -func (c *collector) addTimer(name string, m metrics.Timer) { +func (c *collector) addTimer(name string, m metrics.TimerSnapshot) { pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999} ps := m.Percentiles(pv) c.writeSummaryCounter(name, m.Count()) @@ -121,13 +121,12 @@ func (c *collector) addTimer(name string, m metrics.Timer) { c.buff.WriteRune('\n') } -func (c *collector) addResettingTimer(name string, m metrics.ResettingTimer) { - if len(m.Values()) <= 0 { +func (c *collector) addResettingTimer(name string, m metrics.ResettingTimerSnapshot) { + if m.Count() <= 0 { return } - ps := m.Percentiles([]float64{50, 95, 99}) - val := m.Values() - c.writeSummaryCounter(name, len(val)) + ps := m.Percentiles([]float64{0.50, 0.95, 0.99}) + c.writeSummaryCounter(name, m.Count()) c.buff.WriteString(fmt.Sprintf(typeSummaryTpl, mutateKey(name))) c.writeSummaryPercentile(name, "0.50", ps[0]) c.writeSummaryPercentile(name, "0.95", ps[1]) diff --git a/metrics/prometheus/collector_test.go b/metrics/prometheus/collector_test.go index b7a17ff1011e..948c539ad666 100644 --- a/metrics/prometheus/collector_test.go +++ b/metrics/prometheus/collector_test.go @@ -55,10 +55,10 @@ func findFirstDiffPos(a, b string) string { yy := strings.Split(b, "\n") for i, x := range strings.Split(a, "\n") { if i >= len(yy) { - return fmt.Sprintf("a:%d: %s\nb:%d: ", i, x, i) + return fmt.Sprintf("have:%d: %s\nwant:%d: ", i, x, i) } if y := yy[i]; x != y { - return fmt.Sprintf("a:%d: %s\nb:%d: %s", i, x, i, y) + return fmt.Sprintf("have:%d: %s\nwant:%d: %s", i, x, i, y) } } return "" diff --git a/metrics/prometheus/testdata/prometheus.want b/metrics/prometheus/testdata/prometheus.want index f35496e61d31..861c5f5cf087 100644 --- a/metrics/prometheus/testdata/prometheus.want +++ b/metrics/prometheus/testdata/prometheus.want @@ -1,3 +1,25 @@ +# TYPE system_cpu_schedlatency_count counter +system_cpu_schedlatency_count 5645 + +# TYPE system_cpu_schedlatency summary +system_cpu_schedlatency {quantile="0.5"} 0 +system_cpu_schedlatency {quantile="0.75"} 7168 +system_cpu_schedlatency {quantile="0.95"} 1.6777216e+07 +system_cpu_schedlatency {quantile="0.99"} 2.9360128e+07 +system_cpu_schedlatency {quantile="0.999"} 3.3554432e+07 +system_cpu_schedlatency {quantile="0.9999"} 3.3554432e+07 + +# TYPE system_memory_pauses_count counter +system_memory_pauses_count 14 + +# TYPE system_memory_pauses summary +system_memory_pauses {quantile="0.5"} 32768 +system_memory_pauses {quantile="0.75"} 57344 +system_memory_pauses {quantile="0.95"} 196608 +system_memory_pauses {quantile="0.99"} 196608 +system_memory_pauses {quantile="0.999"} 196608 +system_memory_pauses {quantile="0.9999"} 196608 + # TYPE test_counter gauge test_counter 12345 @@ -31,9 +53,9 @@ test_meter 0 test_resetting_timer_count 6 # TYPE test_resetting_timer summary -test_resetting_timer {quantile="0.50"} 12000000 -test_resetting_timer {quantile="0.95"} 120000000 -test_resetting_timer {quantile="0.99"} 120000000 +test_resetting_timer {quantile="0.50"} 1.25e+07 +test_resetting_timer {quantile="0.95"} 1.2e+08 +test_resetting_timer {quantile="0.99"} 1.2e+08 # TYPE test_timer_count counter test_timer_count 6 diff --git a/metrics/registry.go b/metrics/registry.go index 0b54f2dc92c8..a6cae1fff0a9 100644 --- a/metrics/registry.go +++ b/metrics/registry.go @@ -150,13 +150,13 @@ func (r *StandardRegistry) GetAll() map[string]map[string]interface{} { values := make(map[string]interface{}) switch metric := i.(type) { case Counter: - values["count"] = metric.Count() + values["count"] = metric.Snapshot().Count() case CounterFloat64: - values["count"] = metric.Count() + values["count"] = metric.Snapshot().Count() case Gauge: - values["value"] = metric.Value() + values["value"] = metric.Snapshot().Value() case GaugeFloat64: - values["value"] = metric.Value() + values["value"] = metric.Snapshot().Value() case Healthcheck: values["error"] = nil metric.Check() diff --git a/metrics/registry_test.go b/metrics/registry_test.go index 7cc5cf14fe55..75012dd4ac00 100644 --- a/metrics/registry_test.go +++ b/metrics/registry_test.go @@ -85,11 +85,11 @@ func TestRegistryDuplicate(t *testing.T) { func TestRegistryGet(t *testing.T) { r := NewRegistry() r.Register("foo", NewCounter()) - if count := r.Get("foo").(Counter).Count(); count != 0 { + if count := r.Get("foo").(Counter).Snapshot().Count(); count != 0 { t.Fatal(count) } r.Get("foo").(Counter).Inc(1) - if count := r.Get("foo").(Counter).Count(); count != 1 { + if count := r.Get("foo").(Counter).Snapshot().Count(); count != 1 { t.Fatal(count) } } diff --git a/metrics/resetting_sample.go b/metrics/resetting_sample.go index 43c1129cd0bc..c38ffcd3ec32 100644 --- a/metrics/resetting_sample.go +++ b/metrics/resetting_sample.go @@ -17,7 +17,7 @@ type resettingSample struct { } // Snapshot returns a read-only copy of the sample with the original reset. -func (rs *resettingSample) Snapshot() Sample { +func (rs *resettingSample) Snapshot() SampleSnapshot { s := rs.Sample.Snapshot() rs.Sample.Clear() return s diff --git a/metrics/resetting_timer.go b/metrics/resetting_timer.go index 858984a32cc4..6802e3fcea98 100644 --- a/metrics/resetting_timer.go +++ b/metrics/resetting_timer.go @@ -1,22 +1,24 @@ package metrics import ( - "math" "sync" "time" - - "golang.org/x/exp/slices" ) // Initial slice capacity for the values stored in a ResettingTimer const InitialResettingTimerSliceCap = 10 +type ResettingTimerSnapshot interface { + Count() int + Mean() float64 + Max() int64 + Min() int64 + Percentiles([]float64) []float64 +} + // ResettingTimer is used for storing aggregated values for timers, which are reset on every flush interval. type ResettingTimer interface { - Values() []int64 - Snapshot() ResettingTimer - Percentiles([]float64) []int64 - Mean() float64 + Snapshot() ResettingTimerSnapshot Time(func()) Update(time.Duration) UpdateSince(time.Time) @@ -52,66 +54,40 @@ func NewResettingTimer() ResettingTimer { } // NilResettingTimer is a no-op ResettingTimer. -type NilResettingTimer struct { -} - -// Values is a no-op. -func (NilResettingTimer) Values() []int64 { return nil } - -// Snapshot is a no-op. -func (NilResettingTimer) Snapshot() ResettingTimer { return NilResettingTimer{} } - -// Time is a no-op. -func (NilResettingTimer) Time(f func()) { f() } - -// Update is a no-op. -func (NilResettingTimer) Update(time.Duration) {} - -// Percentiles panics. -func (NilResettingTimer) Percentiles([]float64) []int64 { - panic("Percentiles called on a NilResettingTimer") -} - -// Mean panics. -func (NilResettingTimer) Mean() float64 { - panic("Mean called on a NilResettingTimer") -} - -// UpdateSince is a no-op. -func (NilResettingTimer) UpdateSince(time.Time) {} +type NilResettingTimer struct{} + +func (NilResettingTimer) Values() []int64 { return nil } +func (n NilResettingTimer) Snapshot() ResettingTimerSnapshot { return n } +func (NilResettingTimer) Time(f func()) { f() } +func (NilResettingTimer) Update(time.Duration) {} +func (NilResettingTimer) Percentiles([]float64) []float64 { return nil } +func (NilResettingTimer) Mean() float64 { return 0.0 } +func (NilResettingTimer) Max() int64 { return 0 } +func (NilResettingTimer) Min() int64 { return 0 } +func (NilResettingTimer) UpdateSince(time.Time) {} +func (NilResettingTimer) Count() int { return 0 } // StandardResettingTimer is the standard implementation of a ResettingTimer. // and Meter. type StandardResettingTimer struct { values []int64 - mutex sync.Mutex -} + sum int64 // sum is a running count of the total sum, used later to calculate mean -// Values returns a slice with all measurements. -func (t *StandardResettingTimer) Values() []int64 { - return t.values + mutex sync.Mutex } // Snapshot resets the timer and returns a read-only copy of its contents. -func (t *StandardResettingTimer) Snapshot() ResettingTimer { +func (t *StandardResettingTimer) Snapshot() ResettingTimerSnapshot { t.mutex.Lock() defer t.mutex.Unlock() - currentValues := t.values - t.values = make([]int64, 0, InitialResettingTimerSliceCap) - - return &ResettingTimerSnapshot{ - values: currentValues, + snapshot := &resettingTimerSnapshot{} + if len(t.values) > 0 { + snapshot.mean = float64(t.sum) / float64(len(t.values)) + snapshot.values = t.values + t.values = make([]int64, 0, InitialResettingTimerSliceCap) } -} - -// Percentiles panics. -func (t *StandardResettingTimer) Percentiles([]float64) []int64 { - panic("Percentiles called on a StandardResettingTimer") -} - -// Mean panics. -func (t *StandardResettingTimer) Mean() float64 { - panic("Mean called on a StandardResettingTimer") + t.sum = 0 + return snapshot } // Record the duration of the execution of the given function. @@ -126,106 +102,70 @@ func (t *StandardResettingTimer) Update(d time.Duration) { t.mutex.Lock() defer t.mutex.Unlock() t.values = append(t.values, int64(d)) + t.sum += int64(d) } // Record the duration of an event that started at a time and ends now. func (t *StandardResettingTimer) UpdateSince(ts time.Time) { - t.mutex.Lock() - defer t.mutex.Unlock() - t.values = append(t.values, int64(time.Since(ts))) + t.Update(time.Since(ts)) } -// ResettingTimerSnapshot is a point-in-time copy of another ResettingTimer. -type ResettingTimerSnapshot struct { +// resettingTimerSnapshot is a point-in-time copy of another ResettingTimer. +type resettingTimerSnapshot struct { values []int64 mean float64 - thresholdBoundaries []int64 + max int64 + min int64 + thresholdBoundaries []float64 calculated bool } -// Snapshot returns the snapshot. -func (t *ResettingTimerSnapshot) Snapshot() ResettingTimer { return t } - -// Time panics. -func (*ResettingTimerSnapshot) Time(func()) { - panic("Time called on a ResettingTimerSnapshot") -} - -// Update panics. -func (*ResettingTimerSnapshot) Update(time.Duration) { - panic("Update called on a ResettingTimerSnapshot") -} - -// UpdateSince panics. -func (*ResettingTimerSnapshot) UpdateSince(time.Time) { - panic("UpdateSince called on a ResettingTimerSnapshot") -} - -// Values returns all values from snapshot. -func (t *ResettingTimerSnapshot) Values() []int64 { - return t.values +// Count return the length of the values from snapshot. +func (t *resettingTimerSnapshot) Count() int { + return len(t.values) } // Percentiles returns the boundaries for the input percentiles. -func (t *ResettingTimerSnapshot) Percentiles(percentiles []float64) []int64 { +// note: this method is not thread safe +func (t *resettingTimerSnapshot) Percentiles(percentiles []float64) []float64 { t.calc(percentiles) - return t.thresholdBoundaries } // Mean returns the mean of the snapshotted values -func (t *ResettingTimerSnapshot) Mean() float64 { +// note: this method is not thread safe +func (t *resettingTimerSnapshot) Mean() float64 { if !t.calculated { - t.calc([]float64{}) + t.calc(nil) } return t.mean } -func (t *ResettingTimerSnapshot) calc(percentiles []float64) { - slices.Sort(t.values) - - count := len(t.values) - if count > 0 { - min := t.values[0] - max := t.values[count-1] - - cumulativeValues := make([]int64, count) - cumulativeValues[0] = min - for i := 1; i < count; i++ { - cumulativeValues[i] = t.values[i] + cumulativeValues[i-1] - } - - t.thresholdBoundaries = make([]int64, len(percentiles)) - - thresholdBoundary := max - - for i, pct := range percentiles { - if count > 1 { - var abs float64 - if pct >= 0 { - abs = pct - } else { - abs = 100 + pct - } - // poor man's math.Round(x): - // math.Floor(x + 0.5) - indexOfPerc := int(math.Floor(((abs / 100.0) * float64(count)) + 0.5)) - if pct >= 0 { - indexOfPerc -= 1 // index offset=0 - } - thresholdBoundary = t.values[indexOfPerc] - } - - t.thresholdBoundaries[i] = thresholdBoundary - } - - sum := cumulativeValues[count-1] - t.mean = float64(sum) / float64(count) - } else { - t.thresholdBoundaries = make([]int64, len(percentiles)) - t.mean = 0 +// Max returns the max of the snapshotted values +// note: this method is not thread safe +func (t *resettingTimerSnapshot) Max() int64 { + if !t.calculated { + t.calc(nil) } + return t.max +} - t.calculated = true +// Min returns the min of the snapshotted values +// note: this method is not thread safe +func (t *resettingTimerSnapshot) Min() int64 { + if !t.calculated { + t.calc(nil) + } + return t.min +} + +func (t *resettingTimerSnapshot) calc(percentiles []float64) { + scores := CalculatePercentiles(t.values, percentiles) + t.thresholdBoundaries = scores + if len(t.values) == 0 { + return + } + t.min = t.values[0] + t.max = t.values[len(t.values)-1] } diff --git a/metrics/resetting_timer_test.go b/metrics/resetting_timer_test.go index 58fd47f35245..4571fc8eb052 100644 --- a/metrics/resetting_timer_test.go +++ b/metrics/resetting_timer_test.go @@ -10,9 +10,9 @@ func TestResettingTimer(t *testing.T) { values []int64 start int end int - wantP50 int64 - wantP95 int64 - wantP99 int64 + wantP50 float64 + wantP95 float64 + wantP99 float64 wantMean float64 wantMin int64 wantMax int64 @@ -21,14 +21,14 @@ func TestResettingTimer(t *testing.T) { values: []int64{}, start: 1, end: 11, - wantP50: 5, wantP95: 10, wantP99: 10, + wantP50: 5.5, wantP95: 10, wantP99: 10, wantMin: 1, wantMax: 10, wantMean: 5.5, }, { values: []int64{}, start: 1, end: 101, - wantP50: 50, wantP95: 95, wantP99: 99, + wantP50: 50.5, wantP95: 95.94999999999999, wantP99: 99.99, wantMin: 1, wantMax: 100, wantMean: 50.5, }, { @@ -56,11 +56,11 @@ func TestResettingTimer(t *testing.T) { values: []int64{1, 10}, start: 0, end: 0, - wantP50: 1, wantP95: 10, wantP99: 10, + wantP50: 5.5, wantP95: 10, wantP99: 10, wantMin: 1, wantMax: 10, wantMean: 5.5, }, } - for ind, tt := range tests { + for i, tt := range tests { timer := NewResettingTimer() for i := tt.start; i < tt.end; i++ { @@ -70,37 +70,128 @@ func TestResettingTimer(t *testing.T) { for _, v := range tt.values { timer.Update(time.Duration(v)) } - snap := timer.Snapshot() - ps := snap.Percentiles([]float64{50, 95, 99}) + ps := snap.Percentiles([]float64{0.50, 0.95, 0.99}) - val := snap.Values() + if have, want := snap.Min(), tt.wantMin; have != want { + t.Fatalf("%d: min: have %d, want %d", i, have, want) + } + if have, want := snap.Max(), tt.wantMax; have != want { + t.Fatalf("%d: max: have %d, want %d", i, have, want) + } + if have, want := snap.Mean(), tt.wantMean; have != want { + t.Fatalf("%d: mean: have %v, want %v", i, have, want) + } + if have, want := ps[0], tt.wantP50; have != want { + t.Errorf("%d: p50: have %v, want %v", i, have, want) + } + if have, want := ps[1], tt.wantP95; have != want { + t.Errorf("%d: p95: have %v, want %v", i, have, want) + } + if have, want := ps[2], tt.wantP99; have != want { + t.Errorf("%d: p99: have %v, want %v", i, have, want) + } + } +} - if len(val) > 0 { - if tt.wantMin != val[0] { - t.Fatalf("%d: min: got %d, want %d", ind, val[0], tt.wantMin) - } +func TestResettingTimerWithFivePercentiles(t *testing.T) { + tests := []struct { + values []int64 + start int + end int + wantP05 float64 + wantP20 float64 + wantP50 float64 + wantP95 float64 + wantP99 float64 + wantMean float64 + wantMin int64 + wantMax int64 + }{ + { + values: []int64{}, + start: 1, + end: 11, + wantP05: 1, wantP20: 2.2, wantP50: 5.5, wantP95: 10, wantP99: 10, + wantMin: 1, wantMax: 10, wantMean: 5.5, + }, + { + values: []int64{}, + start: 1, + end: 101, + wantP05: 5.050000000000001, wantP20: 20.200000000000003, wantP50: 50.5, wantP95: 95.94999999999999, wantP99: 99.99, + wantMin: 1, wantMax: 100, wantMean: 50.5, + }, + { + values: []int64{1}, + start: 0, + end: 0, + wantP05: 1, wantP20: 1, wantP50: 1, wantP95: 1, wantP99: 1, + wantMin: 1, wantMax: 1, wantMean: 1, + }, + { + values: []int64{0}, + start: 0, + end: 0, + wantP05: 0, wantP20: 0, wantP50: 0, wantP95: 0, wantP99: 0, + wantMin: 0, wantMax: 0, wantMean: 0, + }, + { + values: []int64{}, + start: 0, + end: 0, + wantP05: 0, wantP20: 0, wantP50: 0, wantP95: 0, wantP99: 0, + wantMin: 0, wantMax: 0, wantMean: 0, + }, + { + values: []int64{1, 10}, + start: 0, + end: 0, + wantP05: 1, wantP20: 1, wantP50: 5.5, wantP95: 10, wantP99: 10, + wantMin: 1, wantMax: 10, wantMean: 5.5, + }, + } + for ind, tt := range tests { + timer := NewResettingTimer() - if tt.wantMax != val[len(val)-1] { - t.Fatalf("%d: max: got %d, want %d", ind, val[len(val)-1], tt.wantMax) - } + for i := tt.start; i < tt.end; i++ { + tt.values = append(tt.values, int64(i)) } - if tt.wantMean != snap.Mean() { - t.Fatalf("%d: mean: got %.2f, want %.2f", ind, snap.Mean(), tt.wantMean) + for _, v := range tt.values { + timer.Update(time.Duration(v)) } - if tt.wantP50 != ps[0] { - t.Fatalf("%d: p50: got %d, want %d", ind, ps[0], tt.wantP50) + snap := timer.Snapshot() + + ps := snap.Percentiles([]float64{0.05, 0.20, 0.50, 0.95, 0.99}) + + if tt.wantMin != snap.Min() { + t.Errorf("%d: min: got %d, want %d", ind, snap.Min(), tt.wantMin) } - if tt.wantP95 != ps[1] { - t.Fatalf("%d: p95: got %d, want %d", ind, ps[1], tt.wantP95) + if tt.wantMax != snap.Max() { + t.Errorf("%d: max: got %d, want %d", ind, snap.Max(), tt.wantMax) } - if tt.wantP99 != ps[2] { - t.Fatalf("%d: p99: got %d, want %d", ind, ps[2], tt.wantP99) + if tt.wantMean != snap.Mean() { + t.Errorf("%d: mean: got %.2f, want %.2f", ind, snap.Mean(), tt.wantMean) + } + if tt.wantP05 != ps[0] { + t.Errorf("%d: p05: got %v, want %v", ind, ps[0], tt.wantP05) + } + if tt.wantP20 != ps[1] { + t.Errorf("%d: p20: got %v, want %v", ind, ps[1], tt.wantP20) + } + if tt.wantP50 != ps[2] { + t.Errorf("%d: p50: got %v, want %v", ind, ps[2], tt.wantP50) + } + if tt.wantP95 != ps[3] { + t.Errorf("%d: p95: got %v, want %v", ind, ps[3], tt.wantP95) + } + if tt.wantP99 != ps[4] { + t.Errorf("%d: p99: got %v, want %v", ind, ps[4], tt.wantP99) } } } diff --git a/metrics/runtimehistogram.go b/metrics/runtimehistogram.go index c68939af1ef7..92fcbcc2814c 100644 --- a/metrics/runtimehistogram.go +++ b/metrics/runtimehistogram.go @@ -17,13 +17,19 @@ func getOrRegisterRuntimeHistogram(name string, scale float64, r Registry) *runt // runtimeHistogram wraps a runtime/metrics histogram. type runtimeHistogram struct { - v atomic.Value + v atomic.Value // v is a pointer to a metrics.Float64Histogram scaleFactor float64 } func newRuntimeHistogram(scale float64) *runtimeHistogram { h := &runtimeHistogram{scaleFactor: scale} - h.update(&metrics.Float64Histogram{}) + h.update(new(metrics.Float64Histogram)) + return h +} + +func RuntimeHistogramFromData(scale float64, hist *metrics.Float64Histogram) *runtimeHistogram { + h := &runtimeHistogram{scaleFactor: scale} + h.update(hist) return h } @@ -35,130 +41,107 @@ func (h *runtimeHistogram) update(mh *metrics.Float64Histogram) { return } - s := runtimeHistogramSnapshot{ + s := metrics.Float64Histogram{ Counts: make([]uint64, len(mh.Counts)), Buckets: make([]float64, len(mh.Buckets)), } copy(s.Counts, mh.Counts) - copy(s.Buckets, mh.Buckets) - for i, b := range s.Buckets { + for i, b := range mh.Buckets { s.Buckets[i] = b * h.scaleFactor } h.v.Store(&s) } -func (h *runtimeHistogram) load() *runtimeHistogramSnapshot { - return h.v.Load().(*runtimeHistogramSnapshot) -} - func (h *runtimeHistogram) Clear() { panic("runtimeHistogram does not support Clear") } func (h *runtimeHistogram) Update(int64) { panic("runtimeHistogram does not support Update") } -func (h *runtimeHistogram) Sample() Sample { - return NilSample{} -} - -// Snapshot returns a non-changing cop of the histogram. -func (h *runtimeHistogram) Snapshot() Histogram { - return h.load() -} - -// Count returns the sample count. -func (h *runtimeHistogram) Count() int64 { - return h.load().Count() -} - -// Mean returns an approximation of the mean. -func (h *runtimeHistogram) Mean() float64 { - return h.load().Mean() -} - -// StdDev approximates the standard deviation of the histogram. -func (h *runtimeHistogram) StdDev() float64 { - return h.load().StdDev() -} - -// Variance approximates the variance of the histogram. -func (h *runtimeHistogram) Variance() float64 { - return h.load().Variance() -} - -// Percentile computes the p'th percentile value. -func (h *runtimeHistogram) Percentile(p float64) float64 { - return h.load().Percentile(p) -} -// Percentiles computes all requested percentile values. -func (h *runtimeHistogram) Percentiles(ps []float64) []float64 { - return h.load().Percentiles(ps) -} - -// Max returns the highest sample value. -func (h *runtimeHistogram) Max() int64 { - return h.load().Max() +// Snapshot returns a non-changing copy of the histogram. +func (h *runtimeHistogram) Snapshot() HistogramSnapshot { + hist := h.v.Load().(*metrics.Float64Histogram) + return newRuntimeHistogramSnapshot(hist) } -// Min returns the lowest sample value. -func (h *runtimeHistogram) Min() int64 { - return h.load().Min() -} - -// Sum returns the sum of all sample values. -func (h *runtimeHistogram) Sum() int64 { - return h.load().Sum() +type runtimeHistogramSnapshot struct { + internal *metrics.Float64Histogram + calculated bool + // The following fields are (lazily) calculated based on 'internal' + mean float64 + count int64 + min int64 // min is the lowest sample value. + max int64 // max is the highest sample value. + variance float64 } -type runtimeHistogramSnapshot metrics.Float64Histogram - -func (h *runtimeHistogramSnapshot) Clear() { - panic("runtimeHistogram does not support Clear") -} -func (h *runtimeHistogramSnapshot) Update(int64) { - panic("runtimeHistogram does not support Update") -} -func (h *runtimeHistogramSnapshot) Sample() Sample { - return NilSample{} +func newRuntimeHistogramSnapshot(h *metrics.Float64Histogram) *runtimeHistogramSnapshot { + return &runtimeHistogramSnapshot{ + internal: h, + } } -func (h *runtimeHistogramSnapshot) Snapshot() Histogram { - return h +// calc calculates the values for the snapshot. This method is not threadsafe. +func (h *runtimeHistogramSnapshot) calc() { + h.calculated = true + var ( + count int64 // number of samples + sum float64 // approx sum of all sample values + min int64 + max float64 + ) + if len(h.internal.Counts) == 0 { + return + } + for i, c := range h.internal.Counts { + if c == 0 { + continue + } + if count == 0 { // Set min only first loop iteration + min = int64(math.Floor(h.internal.Buckets[i])) + } + count += int64(c) + sum += h.midpoint(i) * float64(c) + // Set max on every iteration + edge := h.internal.Buckets[i+1] + if math.IsInf(edge, 1) { + edge = h.internal.Buckets[i] + } + if edge > max { + max = edge + } + } + h.min = min + h.max = int64(max) + h.mean = sum / float64(count) + h.count = count } // Count returns the sample count. func (h *runtimeHistogramSnapshot) Count() int64 { - var count int64 - for _, c := range h.Counts { - count += int64(c) + if !h.calculated { + h.calc() } - return count + return h.count } -// Mean returns an approximation of the mean. -func (h *runtimeHistogramSnapshot) Mean() float64 { - if len(h.Counts) == 0 { - return 0 - } - mean, _ := h.mean() - return mean +// Size returns the size of the sample at the time the snapshot was taken. +func (h *runtimeHistogramSnapshot) Size() int { + return len(h.internal.Counts) } -// mean computes the mean and also the total sample count. -func (h *runtimeHistogramSnapshot) mean() (mean, totalCount float64) { - var sum float64 - for i, c := range h.Counts { - midpoint := h.midpoint(i) - sum += midpoint * float64(c) - totalCount += float64(c) +// Mean returns an approximation of the mean. +func (h *runtimeHistogramSnapshot) Mean() float64 { + if !h.calculated { + h.calc() } - return sum / totalCount, totalCount + return h.mean } func (h *runtimeHistogramSnapshot) midpoint(bucket int) float64 { - high := h.Buckets[bucket+1] - low := h.Buckets[bucket] + high := h.internal.Buckets[bucket+1] + low := h.internal.Buckets[bucket] if math.IsInf(high, 1) { // The edge of the highest bucket can be +Inf, and it's supposed to mean that this // bucket contains all remaining samples > low. We can't get the middle of an @@ -180,23 +163,31 @@ func (h *runtimeHistogramSnapshot) StdDev() float64 { // Variance approximates the variance of the histogram. func (h *runtimeHistogramSnapshot) Variance() float64 { - if len(h.Counts) == 0 { + if len(h.internal.Counts) == 0 { return 0 } - - mean, totalCount := h.mean() - if totalCount <= 1 { + if !h.calculated { + h.calc() + } + if h.count <= 1 { // There is no variance when there are zero or one items. return 0 } - + // Variance is not calculated in 'calc', because it requires a second iteration. + // Therefore we calculate it lazily in this method, triggered either by + // a direct call to Variance or via StdDev. + if h.variance != 0.0 { + return h.variance + } var sum float64 - for i, c := range h.Counts { + + for i, c := range h.internal.Counts { midpoint := h.midpoint(i) - d := midpoint - mean + d := midpoint - h.mean sum += float64(c) * (d * d) } - return sum / (totalCount - 1) + h.variance = sum / float64(h.count-1) + return h.variance } // Percentile computes the p'th percentile value. @@ -231,11 +222,11 @@ func (h *runtimeHistogramSnapshot) Percentiles(ps []float64) []float64 { func (h *runtimeHistogramSnapshot) computePercentiles(thresh []float64) { var totalCount float64 - for i, count := range h.Counts { + for i, count := range h.internal.Counts { totalCount += float64(count) for len(thresh) > 0 && thresh[0] < totalCount { - thresh[0] = h.Buckets[i] + thresh[0] = h.internal.Buckets[i] thresh = thresh[1:] } if len(thresh) == 0 { @@ -250,34 +241,25 @@ func (h *runtimeHistogramSnapshot) computePercentiles(thresh []float64) { // Max returns the highest sample value. func (h *runtimeHistogramSnapshot) Max() int64 { - for i := len(h.Counts) - 1; i >= 0; i-- { - count := h.Counts[i] - if count > 0 { - edge := h.Buckets[i+1] - if math.IsInf(edge, 1) { - edge = h.Buckets[i] - } - return int64(math.Ceil(edge)) - } + if !h.calculated { + h.calc() } - return 0 + return h.max } // Min returns the lowest sample value. func (h *runtimeHistogramSnapshot) Min() int64 { - for i, count := range h.Counts { - if count > 0 { - return int64(math.Floor(h.Buckets[i])) - } + if !h.calculated { + h.calc() } - return 0 + return h.min } // Sum returns the sum of all sample values. func (h *runtimeHistogramSnapshot) Sum() int64 { var sum float64 - for i := range h.Counts { - sum += h.Buckets[i] * float64(h.Counts[i]) + for i := range h.internal.Counts { + sum += h.internal.Buckets[i] * float64(h.internal.Counts[i]) } return int64(math.Ceil(sum)) } diff --git a/metrics/runtimehistogram_test.go b/metrics/runtimehistogram_test.go index d53a01438311..cf7e36420ae9 100644 --- a/metrics/runtimehistogram_test.go +++ b/metrics/runtimehistogram_test.go @@ -1,11 +1,14 @@ package metrics import ( + "bytes" + "encoding/gob" "fmt" "math" "reflect" "runtime/metrics" "testing" + "time" ) var _ Histogram = (*runtimeHistogram)(nil) @@ -74,7 +77,7 @@ func TestRuntimeHistogramStats(t *testing.T) { for i, test := range tests { t.Run(fmt.Sprint(i), func(t *testing.T) { - s := runtimeHistogramSnapshot(test.h) + s := RuntimeHistogramFromData(1.0, &test.h).Snapshot() if v := s.Count(); v != test.Count { t.Errorf("Count() = %v, want %v", v, test.Count) @@ -121,13 +124,39 @@ func approxEqual(x, y, ε float64) bool { // This test verifies that requesting Percentiles in unsorted order // returns them in the requested order. func TestRuntimeHistogramStatsPercentileOrder(t *testing.T) { - p := runtimeHistogramSnapshot{ + s := RuntimeHistogramFromData(1.0, &metrics.Float64Histogram{ Counts: []uint64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, Buckets: []float64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, - } - result := p.Percentiles([]float64{1, 0.2, 0.5, 0.1, 0.2}) + }).Snapshot() + result := s.Percentiles([]float64{1, 0.2, 0.5, 0.1, 0.2}) expected := []float64{10, 2, 5, 1, 2} if !reflect.DeepEqual(result, expected) { t.Fatal("wrong result:", result) } } + +func BenchmarkRuntimeHistogramSnapshotRead(b *testing.B) { + var sLatency = "7\xff\x81\x03\x01\x01\x10Float64Histogram\x01\xff\x82\x00\x01\x02\x01\x06Counts\x01\xff\x84\x00\x01\aBuckets\x01\xff\x86\x00\x00\x00\x16\xff\x83\x02\x01\x01\b[]uint64\x01\xff\x84\x00\x01\x06\x00\x00\x17\xff\x85\x02\x01\x01\t[]float64\x01\xff\x86\x00\x01\b\x00\x00\xfe\x06T\xff\x82\x01\xff\xa2\x00\xfe\r\xef\x00\x01\x02\x02\x04\x05\x04\b\x15\x17 B?6.L;$!2) \x1a? \x190aH7FY6#\x190\x1d\x14\x10\x1b\r\t\x04\x03\x01\x01\x00\x03\x02\x00\x03\x05\x05\x02\x02\x06\x04\v\x06\n\x15\x18\x13'&.\x12=H/L&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\xa3\xfe\xf0\xff\x00\xf8\x95\xd6&\xe8\v.q>\xf8\x95\xd6&\xe8\v.\x81>\xf8\xdfA:\xdc\x11ʼn>\xf8\x95\xd6&\xe8\v.\x91>\xf8:\x8c0\xe2\x8ey\x95>\xf8\xdfA:\xdc\x11ř>\xf8\x84\xf7C֔\x10\x9e>\xf8\x95\xd6&\xe8\v.\xa1>\xf8:\x8c0\xe2\x8ey\xa5>\xf8\xdfA:\xdc\x11ũ>\xf8\x84\xf7C֔\x10\xae>\xf8\x95\xd6&\xe8\v.\xb1>\xf8:\x8c0\xe2\x8ey\xb5>\xf8\xdfA:\xdc\x11Ź>\xf8\x84\xf7C֔\x10\xbe>\xf8\x95\xd6&\xe8\v.\xc1>\xf8:\x8c0\xe2\x8ey\xc5>\xf8\xdfA:\xdc\x11\xc5\xc9>\xf8\x84\xf7C֔\x10\xce>\xf8\x95\xd6&\xe8\v.\xd1>\xf8:\x8c0\xe2\x8ey\xd5>\xf8\xdfA:\xdc\x11\xc5\xd9>\xf8\x84\xf7C֔\x10\xde>\xf8\x95\xd6&\xe8\v.\xe1>\xf8:\x8c0\xe2\x8ey\xe5>\xf8\xdfA:\xdc\x11\xc5\xe9>\xf8\x84\xf7C֔\x10\xee>\xf8\x95\xd6&\xe8\v.\xf1>\xf8:\x8c0\xe2\x8ey\xf5>\xf8\xdfA:\xdc\x11\xc5\xf9>\xf8\x84\xf7C֔\x10\xfe>\xf8\x95\xd6&\xe8\v.\x01?\xf8:\x8c0\xe2\x8ey\x05?\xf8\xdfA:\xdc\x11\xc5\t?\xf8\x84\xf7C֔\x10\x0e?\xf8\x95\xd6&\xe8\v.\x11?\xf8:\x8c0\xe2\x8ey\x15?\xf8\xdfA:\xdc\x11\xc5\x19?\xf8\x84\xf7C֔\x10\x1e?\xf8\x95\xd6&\xe8\v.!?\xf8:\x8c0\xe2\x8ey%?\xf8\xdfA:\xdc\x11\xc5)?\xf8\x84\xf7C֔\x10.?\xf8\x95\xd6&\xe8\v.1?\xf8:\x8c0\xe2\x8ey5?\xf8\xdfA:\xdc\x11\xc59?\xf8\x84\xf7C֔\x10>?\xf8\x95\xd6&\xe8\v.A?\xf8:\x8c0\xe2\x8eyE?\xf8\xdfA:\xdc\x11\xc5I?\xf8\x84\xf7C֔\x10N?\xf8\x95\xd6&\xe8\v.Q?\xf8:\x8c0\xe2\x8eyU?\xf8\xdfA:\xdc\x11\xc5Y?\xf8\x84\xf7C֔\x10^?\xf8\x95\xd6&\xe8\v.a?\xf8:\x8c0\xe2\x8eye?\xf8\xdfA:\xdc\x11\xc5i?\xf8\x84\xf7C֔\x10n?\xf8\x95\xd6&\xe8\v.q?\xf8:\x8c0\xe2\x8eyu?\xf8\xdfA:\xdc\x11\xc5y?\xf8\x84\xf7C֔\x10~?\xf8\x95\xd6&\xe8\v.\x81?\xf8:\x8c0\xe2\x8ey\x85?\xf8\xdfA:\xdc\x11ʼn?\xf8\x84\xf7C֔\x10\x8e?\xf8\x95\xd6&\xe8\v.\x91?\xf8:\x8c0\xe2\x8ey\x95?\xf8\xdfA:\xdc\x11ř?\xf8\x84\xf7C֔\x10\x9e?\xf8\x95\xd6&\xe8\v.\xa1?\xf8:\x8c0\xe2\x8ey\xa5?\xf8\xdfA:\xdc\x11ũ?\xf8\x84\xf7C֔\x10\xae?\xf8\x95\xd6&\xe8\v.\xb1?\xf8:\x8c0\xe2\x8ey\xb5?\xf8\xdfA:\xdc\x11Ź?\xf8\x84\xf7C֔\x10\xbe?\xf8\x95\xd6&\xe8\v.\xc1?\xf8:\x8c0\xe2\x8ey\xc5?\xf8\xdfA:\xdc\x11\xc5\xc9?\xf8\x84\xf7C֔\x10\xce?\xf8\x95\xd6&\xe8\v.\xd1?\xf8:\x8c0\xe2\x8ey\xd5?\xf8\xdfA:\xdc\x11\xc5\xd9?\xf8\x84\xf7C֔\x10\xde?\xf8\x95\xd6&\xe8\v.\xe1?\xf8:\x8c0\xe2\x8ey\xe5?\xf8\xdfA:\xdc\x11\xc5\xe9?\xf8\x84\xf7C֔\x10\xee?\xf8\x95\xd6&\xe8\v.\xf1?\xf8:\x8c0\xe2\x8ey\xf5?\xf8\xdfA:\xdc\x11\xc5\xf9?\xf8\x84\xf7C֔\x10\xfe?\xf8\x95\xd6&\xe8\v.\x01@\xf8:\x8c0\xe2\x8ey\x05@\xf8\xdfA:\xdc\x11\xc5\t@\xf8\x84\xf7C֔\x10\x0e@\xf8\x95\xd6&\xe8\v.\x11@\xf8:\x8c0\xe2\x8ey\x15@\xf8\xdfA:\xdc\x11\xc5\x19@\xf8\x84\xf7C֔\x10\x1e@\xf8\x95\xd6&\xe8\v.!@\xf8:\x8c0\xe2\x8ey%@\xf8\xdfA:\xdc\x11\xc5)@\xf8\x84\xf7C֔\x10.@\xf8\x95\xd6&\xe8\v.1@\xf8:\x8c0\xe2\x8ey5@\xf8\xdfA:\xdc\x11\xc59@\xf8\x84\xf7C֔\x10>@\xf8\x95\xd6&\xe8\v.A@\xf8:\x8c0\xe2\x8eyE@\xf8\xdfA:\xdc\x11\xc5I@\xf8\x84\xf7C֔\x10N@\xf8\x95\xd6&\xe8\v.Q@\xf8:\x8c0\xe2\x8eyU@\xf8\xdfA:\xdc\x11\xc5Y@\xf8\x84\xf7C֔\x10^@\xf8\x95\xd6&\xe8\v.a@\xf8:\x8c0\xe2\x8eye@\xf8\xdfA:\xdc\x11\xc5i@\xf8\x84\xf7C֔\x10n@\xf8\x95\xd6&\xe8\v.q@\xf8:\x8c0\xe2\x8eyu@\xf8\xdfA:\xdc\x11\xc5y@\xf8\x84\xf7C֔\x10~@\xf8\x95\xd6&\xe8\v.\x81@\xf8:\x8c0\xe2\x8ey\x85@\xf8\xdfA:\xdc\x11ʼn@\xf8\x84\xf7C֔\x10\x8e@\xf8\x95\xd6&\xe8\v.\x91@\xf8:\x8c0\xe2\x8ey\x95@\xf8\xdfA:\xdc\x11ř@\xf8\x84\xf7C֔\x10\x9e@\xf8\x95\xd6&\xe8\v.\xa1@\xf8:\x8c0\xe2\x8ey\xa5@\xf8\xdfA:\xdc\x11ũ@\xf8\x84\xf7C֔\x10\xae@\xf8\x95\xd6&\xe8\v.\xb1@\xf8:\x8c0\xe2\x8ey\xb5@\xf8\xdfA:\xdc\x11Ź@\xf8\x84\xf7C֔\x10\xbe@\xf8\x95\xd6&\xe8\v.\xc1@\xf8:\x8c0\xe2\x8ey\xc5@\xf8\xdfA:\xdc\x11\xc5\xc9@\xf8\x84\xf7C֔\x10\xce@\xf8\x95\xd6&\xe8\v.\xd1@\xf8:\x8c0\xe2\x8ey\xd5@\xf8\xdfA:\xdc\x11\xc5\xd9@\xf8\x84\xf7C֔\x10\xde@\xf8\x95\xd6&\xe8\v.\xe1@\xf8:\x8c0\xe2\x8ey\xe5@\xf8\xdfA:\xdc\x11\xc5\xe9@\xf8\x84\xf7C֔\x10\xee@\xf8\x95\xd6&\xe8\v.\xf1@\xf8:\x8c0\xe2\x8ey\xf5@\xf8\xdfA:\xdc\x11\xc5\xf9@\xf8\x84\xf7C֔\x10\xfe@\xf8\x95\xd6&\xe8\v.\x01A\xfe\xf0\x7f\x00" + + dserialize := func(data string) *metrics.Float64Histogram { + var res metrics.Float64Histogram + if err := gob.NewDecoder(bytes.NewReader([]byte(data))).Decode(&res); err != nil { + panic(err) + } + return &res + } + latency := RuntimeHistogramFromData(float64(time.Second), dserialize(sLatency)) + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + snap := latency.Snapshot() + // These are the fields that influxdb accesses + _ = snap.Count() + _ = snap.Max() + _ = snap.Mean() + _ = snap.Min() + _ = snap.StdDev() + _ = snap.Variance() + _ = snap.Percentiles([]float64{0.25, 0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) + } +} diff --git a/metrics/sample.go b/metrics/sample.go index 252a878f581b..5398dd42d5de 100644 --- a/metrics/sample.go +++ b/metrics/sample.go @@ -11,10 +11,7 @@ import ( const rescaleThreshold = time.Hour -// Samples maintain a statistically-significant selection of values from -// a stream. -type Sample interface { - Clear() +type SampleSnapshot interface { Count() int64 Max() int64 Mean() float64 @@ -22,14 +19,19 @@ type Sample interface { Percentile(float64) float64 Percentiles([]float64) []float64 Size() int - Snapshot() Sample StdDev() float64 Sum() int64 - Update(int64) - Values() []int64 Variance() float64 } +// Samples maintain a statistically-significant selection of values from +// a stream. +type Sample interface { + Snapshot() SampleSnapshot + Clear() + Update(int64) +} + // ExpDecaySample is an exponentially-decaying sample using a forward-decaying // priority reservoir. See Cormode et al's "Forward Decay: A Practical Time // Decay Model for Streaming Systems". @@ -77,72 +79,29 @@ func (s *ExpDecaySample) Clear() { s.values.Clear() } -// Count returns the number of samples recorded, which may exceed the -// reservoir size. -func (s *ExpDecaySample) Count() int64 { - s.mutex.Lock() - defer s.mutex.Unlock() - return s.count -} - -// Max returns the maximum value in the sample, which may not be the maximum -// value ever to be part of the sample. -func (s *ExpDecaySample) Max() int64 { - return SampleMax(s.Values()) -} - -// Mean returns the mean of the values in the sample. -func (s *ExpDecaySample) Mean() float64 { - return SampleMean(s.Values()) -} - -// Min returns the minimum value in the sample, which may not be the minimum -// value ever to be part of the sample. -func (s *ExpDecaySample) Min() int64 { - return SampleMin(s.Values()) -} - -// Percentile returns an arbitrary percentile of values in the sample. -func (s *ExpDecaySample) Percentile(p float64) float64 { - return SamplePercentile(s.Values(), p) -} - -// Percentiles returns a slice of arbitrary percentiles of values in the -// sample. -func (s *ExpDecaySample) Percentiles(ps []float64) []float64 { - return SamplePercentiles(s.Values(), ps) -} - -// Size returns the size of the sample, which is at most the reservoir size. -func (s *ExpDecaySample) Size() int { - s.mutex.Lock() - defer s.mutex.Unlock() - return s.values.Size() -} - // Snapshot returns a read-only copy of the sample. -func (s *ExpDecaySample) Snapshot() Sample { +func (s *ExpDecaySample) Snapshot() SampleSnapshot { s.mutex.Lock() defer s.mutex.Unlock() - vals := s.values.Values() - values := make([]int64, len(vals)) - for i, v := range vals { - values[i] = v.v - } - return &SampleSnapshot{ - count: s.count, - values: values, + var ( + samples = s.values.Values() + values = make([]int64, len(samples)) + max int64 = math.MinInt64 + min int64 = math.MaxInt64 + sum int64 + ) + for i, item := range samples { + v := item.v + values[i] = v + sum += v + if v > max { + max = v + } + if v < min { + min = v + } } -} - -// StdDev returns the standard deviation of the values in the sample. -func (s *ExpDecaySample) StdDev() float64 { - return SampleStdDev(s.Values()) -} - -// Sum returns the sum of the values in the sample. -func (s *ExpDecaySample) Sum() int64 { - return SampleSum(s.Values()) + return newSampleSnapshotPrecalculated(s.count, values, min, max, sum) } // Update samples a new value. @@ -150,23 +109,6 @@ func (s *ExpDecaySample) Update(v int64) { s.update(time.Now(), v) } -// Values returns a copy of the values in the sample. -func (s *ExpDecaySample) Values() []int64 { - s.mutex.Lock() - defer s.mutex.Unlock() - vals := s.values.Values() - values := make([]int64, len(vals)) - for i, v := range vals { - values[i] = v.v - } - return values -} - -// Variance returns the variance of the values in the sample. -func (s *ExpDecaySample) Variance() float64 { - return SampleVariance(s.Values()) -} - // update samples a new value at a particular timestamp. This is a method all // its own to facilitate testing. func (s *ExpDecaySample) update(t time.Time, v int64) { @@ -202,207 +144,160 @@ func (s *ExpDecaySample) update(t time.Time, v int64) { // NilSample is a no-op Sample. type NilSample struct{} -// Clear is a no-op. -func (NilSample) Clear() {} - -// Count is a no-op. -func (NilSample) Count() int64 { return 0 } - -// Max is a no-op. -func (NilSample) Max() int64 { return 0 } - -// Mean is a no-op. -func (NilSample) Mean() float64 { return 0.0 } - -// Min is a no-op. -func (NilSample) Min() int64 { return 0 } - -// Percentile is a no-op. -func (NilSample) Percentile(p float64) float64 { return 0.0 } - -// Percentiles is a no-op. -func (NilSample) Percentiles(ps []float64) []float64 { - return make([]float64, len(ps)) -} - -// Size is a no-op. -func (NilSample) Size() int { return 0 } - -// Sample is a no-op. -func (NilSample) Snapshot() Sample { return NilSample{} } - -// StdDev is a no-op. -func (NilSample) StdDev() float64 { return 0.0 } - -// Sum is a no-op. -func (NilSample) Sum() int64 { return 0 } - -// Update is a no-op. -func (NilSample) Update(v int64) {} - -// Values is a no-op. -func (NilSample) Values() []int64 { return []int64{} } - -// Variance is a no-op. -func (NilSample) Variance() float64 { return 0.0 } - -// SampleMax returns the maximum value of the slice of int64. -func SampleMax(values []int64) int64 { - if len(values) == 0 { - return 0 - } - var max int64 = math.MinInt64 - for _, v := range values { - if max < v { - max = v - } - } - return max -} - -// SampleMean returns the mean value of the slice of int64. -func SampleMean(values []int64) float64 { - if len(values) == 0 { - return 0.0 - } - return float64(SampleSum(values)) / float64(len(values)) -} - -// SampleMin returns the minimum value of the slice of int64. -func SampleMin(values []int64) int64 { - if len(values) == 0 { - return 0 - } - var min int64 = math.MaxInt64 - for _, v := range values { - if min > v { - min = v - } - } - return min -} +func (NilSample) Clear() {} +func (NilSample) Snapshot() SampleSnapshot { return (*emptySnapshot)(nil) } +func (NilSample) Update(v int64) {} // SamplePercentiles returns an arbitrary percentile of the slice of int64. func SamplePercentile(values []int64, p float64) float64 { - return SamplePercentiles(values, []float64{p})[0] + return CalculatePercentiles(values, []float64{p})[0] } -// SamplePercentiles returns a slice of arbitrary percentiles of the slice of -// int64. -func SamplePercentiles(values []int64, ps []float64) []float64 { +// CalculatePercentiles returns a slice of arbitrary percentiles of the slice of +// int64. This method returns interpolated results, so e.g if there are only two +// values, [0, 10], a 50% percentile will land between them. +// +// Note: As a side-effect, this method will also sort the slice of values. +// Note2: The input format for percentiles is NOT percent! To express 50%, use 0.5, not 50. +func CalculatePercentiles(values []int64, ps []float64) []float64 { scores := make([]float64, len(ps)) size := len(values) - if size > 0 { - slices.Sort(values) - for i, p := range ps { - pos := p * float64(size+1) - if pos < 1.0 { - scores[i] = float64(values[0]) - } else if pos >= float64(size) { - scores[i] = float64(values[size-1]) - } else { - lower := float64(values[int(pos)-1]) - upper := float64(values[int(pos)]) - scores[i] = lower + (pos-math.Floor(pos))*(upper-lower) - } + if size == 0 { + return scores + } + slices.Sort(values) + for i, p := range ps { + pos := p * float64(size+1) + + if pos < 1.0 { + scores[i] = float64(values[0]) + } else if pos >= float64(size) { + scores[i] = float64(values[size-1]) + } else { + lower := float64(values[int(pos)-1]) + upper := float64(values[int(pos)]) + scores[i] = lower + (pos-math.Floor(pos))*(upper-lower) } } return scores } -// SampleSnapshot is a read-only copy of another Sample. -type SampleSnapshot struct { +// sampleSnapshot is a read-only copy of another Sample. +type sampleSnapshot struct { count int64 values []int64 + + max int64 + min int64 + mean float64 + sum int64 + variance float64 } -func NewSampleSnapshot(count int64, values []int64) *SampleSnapshot { - return &SampleSnapshot{ +// newSampleSnapshotPrecalculated creates a read-only sampleSnapShot, using +// precalculated sums to avoid iterating the values +func newSampleSnapshotPrecalculated(count int64, values []int64, min, max, sum int64) *sampleSnapshot { + if len(values) == 0 { + return &sampleSnapshot{ + count: count, + values: values, + } + } + return &sampleSnapshot{ count: count, values: values, + max: max, + min: min, + mean: float64(sum) / float64(len(values)), + sum: sum, } } -// Clear panics. -func (*SampleSnapshot) Clear() { - panic("Clear called on a SampleSnapshot") +// newSampleSnapshot creates a read-only sampleSnapShot, and calculates some +// numbers. +func newSampleSnapshot(count int64, values []int64) *sampleSnapshot { + var ( + max int64 = math.MinInt64 + min int64 = math.MaxInt64 + sum int64 + ) + for _, v := range values { + sum += v + if v > max { + max = v + } + if v < min { + min = v + } + } + return newSampleSnapshotPrecalculated(count, values, min, max, sum) } // Count returns the count of inputs at the time the snapshot was taken. -func (s *SampleSnapshot) Count() int64 { return s.count } +func (s *sampleSnapshot) Count() int64 { return s.count } // Max returns the maximal value at the time the snapshot was taken. -func (s *SampleSnapshot) Max() int64 { return SampleMax(s.values) } +func (s *sampleSnapshot) Max() int64 { return s.max } // Mean returns the mean value at the time the snapshot was taken. -func (s *SampleSnapshot) Mean() float64 { return SampleMean(s.values) } +func (s *sampleSnapshot) Mean() float64 { return s.mean } // Min returns the minimal value at the time the snapshot was taken. -func (s *SampleSnapshot) Min() int64 { return SampleMin(s.values) } +func (s *sampleSnapshot) Min() int64 { return s.min } // Percentile returns an arbitrary percentile of values at the time the // snapshot was taken. -func (s *SampleSnapshot) Percentile(p float64) float64 { +func (s *sampleSnapshot) Percentile(p float64) float64 { return SamplePercentile(s.values, p) } // Percentiles returns a slice of arbitrary percentiles of values at the time // the snapshot was taken. -func (s *SampleSnapshot) Percentiles(ps []float64) []float64 { - return SamplePercentiles(s.values, ps) +func (s *sampleSnapshot) Percentiles(ps []float64) []float64 { + return CalculatePercentiles(s.values, ps) } // Size returns the size of the sample at the time the snapshot was taken. -func (s *SampleSnapshot) Size() int { return len(s.values) } +func (s *sampleSnapshot) Size() int { return len(s.values) } // Snapshot returns the snapshot. -func (s *SampleSnapshot) Snapshot() Sample { return s } +func (s *sampleSnapshot) Snapshot() SampleSnapshot { return s } // StdDev returns the standard deviation of values at the time the snapshot was // taken. -func (s *SampleSnapshot) StdDev() float64 { return SampleStdDev(s.values) } +func (s *sampleSnapshot) StdDev() float64 { + if s.variance == 0.0 { + s.variance = SampleVariance(s.mean, s.values) + } + return math.Sqrt(s.variance) +} // Sum returns the sum of values at the time the snapshot was taken. -func (s *SampleSnapshot) Sum() int64 { return SampleSum(s.values) } - -// Update panics. -func (*SampleSnapshot) Update(int64) { - panic("Update called on a SampleSnapshot") -} +func (s *sampleSnapshot) Sum() int64 { return s.sum } // Values returns a copy of the values in the sample. -func (s *SampleSnapshot) Values() []int64 { +func (s *sampleSnapshot) Values() []int64 { values := make([]int64, len(s.values)) copy(values, s.values) return values } // Variance returns the variance of values at the time the snapshot was taken. -func (s *SampleSnapshot) Variance() float64 { return SampleVariance(s.values) } - -// SampleStdDev returns the standard deviation of the slice of int64. -func SampleStdDev(values []int64) float64 { - return math.Sqrt(SampleVariance(values)) -} - -// SampleSum returns the sum of the slice of int64. -func SampleSum(values []int64) int64 { - var sum int64 - for _, v := range values { - sum += v +func (s *sampleSnapshot) Variance() float64 { + if s.variance == 0.0 { + s.variance = SampleVariance(s.mean, s.values) } - return sum + return s.variance } // SampleVariance returns the variance of the slice of int64. -func SampleVariance(values []int64) float64 { +func SampleVariance(mean float64, values []int64) float64 { if len(values) == 0 { return 0.0 } - m := SampleMean(values) var sum float64 for _, v := range values { - d := float64(v) - m + d := float64(v) - mean sum += d * d } return sum / float64(len(values)) @@ -445,83 +340,14 @@ func (s *UniformSample) Clear() { s.values = make([]int64, 0, s.reservoirSize) } -// Count returns the number of samples recorded, which may exceed the -// reservoir size. -func (s *UniformSample) Count() int64 { - s.mutex.Lock() - defer s.mutex.Unlock() - return s.count -} - -// Max returns the maximum value in the sample, which may not be the maximum -// value ever to be part of the sample. -func (s *UniformSample) Max() int64 { - s.mutex.Lock() - defer s.mutex.Unlock() - return SampleMax(s.values) -} - -// Mean returns the mean of the values in the sample. -func (s *UniformSample) Mean() float64 { - s.mutex.Lock() - defer s.mutex.Unlock() - return SampleMean(s.values) -} - -// Min returns the minimum value in the sample, which may not be the minimum -// value ever to be part of the sample. -func (s *UniformSample) Min() int64 { - s.mutex.Lock() - defer s.mutex.Unlock() - return SampleMin(s.values) -} - -// Percentile returns an arbitrary percentile of values in the sample. -func (s *UniformSample) Percentile(p float64) float64 { - s.mutex.Lock() - defer s.mutex.Unlock() - return SamplePercentile(s.values, p) -} - -// Percentiles returns a slice of arbitrary percentiles of values in the -// sample. -func (s *UniformSample) Percentiles(ps []float64) []float64 { - s.mutex.Lock() - defer s.mutex.Unlock() - return SamplePercentiles(s.values, ps) -} - -// Size returns the size of the sample, which is at most the reservoir size. -func (s *UniformSample) Size() int { - s.mutex.Lock() - defer s.mutex.Unlock() - return len(s.values) -} - // Snapshot returns a read-only copy of the sample. -func (s *UniformSample) Snapshot() Sample { +func (s *UniformSample) Snapshot() SampleSnapshot { s.mutex.Lock() - defer s.mutex.Unlock() values := make([]int64, len(s.values)) copy(values, s.values) - return &SampleSnapshot{ - count: s.count, - values: values, - } -} - -// StdDev returns the standard deviation of the values in the sample. -func (s *UniformSample) StdDev() float64 { - s.mutex.Lock() - defer s.mutex.Unlock() - return SampleStdDev(s.values) -} - -// Sum returns the sum of the values in the sample. -func (s *UniformSample) Sum() int64 { - s.mutex.Lock() - defer s.mutex.Unlock() - return SampleSum(s.values) + count := s.count + s.mutex.Unlock() + return newSampleSnapshot(count, values) } // Update samples a new value. @@ -544,22 +370,6 @@ func (s *UniformSample) Update(v int64) { } } -// Values returns a copy of the values in the sample. -func (s *UniformSample) Values() []int64 { - s.mutex.Lock() - defer s.mutex.Unlock() - values := make([]int64, len(s.values)) - copy(values, s.values) - return values -} - -// Variance returns the variance of the values in the sample. -func (s *UniformSample) Variance() float64 { - s.mutex.Lock() - defer s.mutex.Unlock() - return SampleVariance(s.values) -} - // expDecaySample represents an individual sample in a heap. type expDecaySample struct { k float64 diff --git a/metrics/sample_test.go b/metrics/sample_test.go index 3ae128d56f67..79673570554c 100644 --- a/metrics/sample_test.go +++ b/metrics/sample_test.go @@ -8,28 +8,36 @@ import ( "time" ) +const epsilonPercentile = .00000000001 + // Benchmark{Compute,Copy}{1000,1000000} demonstrate that, even for relatively // expensive computations like Variance, the cost of copying the Sample, as // approximated by a make and copy, is much greater than the cost of the // computation for small samples and only slightly less for large samples. func BenchmarkCompute1000(b *testing.B) { s := make([]int64, 1000) + var sum int64 for i := 0; i < len(s); i++ { s[i] = int64(i) + sum += int64(i) } + mean := float64(sum) / float64(len(s)) b.ResetTimer() for i := 0; i < b.N; i++ { - SampleVariance(s) + SampleVariance(mean, s) } } func BenchmarkCompute1000000(b *testing.B) { s := make([]int64, 1000000) + var sum int64 for i := 0; i < len(s); i++ { s[i] = int64(i) + sum += int64(i) } + mean := float64(sum) / float64(len(s)) b.ResetTimer() for i := 0; i < b.N; i++ { - SampleVariance(s) + SampleVariance(mean, s) } } func BenchmarkCopy1000(b *testing.B) { @@ -79,65 +87,42 @@ func BenchmarkUniformSample1028(b *testing.B) { benchmarkSample(b, NewUniformSample(1028)) } -func TestExpDecaySample10(t *testing.T) { - s := NewExpDecaySample(100, 0.99) - for i := 0; i < 10; i++ { - s.Update(int64(i)) - } - if size := s.Count(); size != 10 { - t.Errorf("s.Count(): 10 != %v\n", size) - } - if size := s.Size(); size != 10 { - t.Errorf("s.Size(): 10 != %v\n", size) - } - if l := len(s.Values()); l != 10 { - t.Errorf("len(s.Values()): 10 != %v\n", l) - } - for _, v := range s.Values() { - if v > 10 || v < 0 { - t.Errorf("out of range [0, 10): %v\n", v) - } +func min(a, b int) int { + if a < b { + return a } + return b } -func TestExpDecaySample100(t *testing.T) { - s := NewExpDecaySample(1000, 0.01) - for i := 0; i < 100; i++ { - s.Update(int64(i)) - } - if size := s.Count(); size != 100 { - t.Errorf("s.Count(): 100 != %v\n", size) - } - if size := s.Size(); size != 100 { - t.Errorf("s.Size(): 100 != %v\n", size) - } - if l := len(s.Values()); l != 100 { - t.Errorf("len(s.Values()): 100 != %v\n", l) - } - for _, v := range s.Values() { - if v > 100 || v < 0 { - t.Errorf("out of range [0, 100): %v\n", v) +func TestExpDecaySample(t *testing.T) { + for _, tc := range []struct { + reservoirSize int + alpha float64 + updates int + }{ + {100, 0.99, 10}, + {1000, 0.01, 100}, + {100, 0.99, 1000}, + } { + sample := NewExpDecaySample(tc.reservoirSize, tc.alpha) + for i := 0; i < tc.updates; i++ { + sample.Update(int64(i)) } - } -} - -func TestExpDecaySample1000(t *testing.T) { - s := NewExpDecaySample(100, 0.99) - for i := 0; i < 1000; i++ { - s.Update(int64(i)) - } - if size := s.Count(); size != 1000 { - t.Errorf("s.Count(): 1000 != %v\n", size) - } - if size := s.Size(); size != 100 { - t.Errorf("s.Size(): 100 != %v\n", size) - } - if l := len(s.Values()); l != 100 { - t.Errorf("len(s.Values()): 100 != %v\n", l) - } - for _, v := range s.Values() { - if v > 1000 || v < 0 { - t.Errorf("out of range [0, 1000): %v\n", v) + snap := sample.Snapshot() + if have, want := int(snap.Count()), tc.updates; have != want { + t.Errorf("have %d want %d", have, want) + } + if have, want := snap.Size(), min(tc.updates, tc.reservoirSize); have != want { + t.Errorf("have %d want %d", have, want) + } + values := snap.(*sampleSnapshot).values + if have, want := len(values), min(tc.updates, tc.reservoirSize); have != want { + t.Errorf("have %d want %d", have, want) + } + for _, v := range values { + if v > int64(tc.updates) || v < 0 { + t.Errorf("out of range [0, %d): %v", tc.updates, v) + } } } } @@ -147,15 +132,16 @@ func TestExpDecaySample1000(t *testing.T) { // The priority becomes +Inf quickly after starting if this is done, // effectively freezing the set of samples until a rescale step happens. func TestExpDecaySampleNanosecondRegression(t *testing.T) { - s := NewExpDecaySample(100, 0.99) + sw := NewExpDecaySample(100, 0.99) for i := 0; i < 100; i++ { - s.Update(10) + sw.Update(10) } time.Sleep(1 * time.Millisecond) for i := 0; i < 100; i++ { - s.Update(20) + sw.Update(20) } - v := s.Values() + s := sw.Snapshot() + v := s.(*sampleSnapshot).values avg := float64(0) for i := 0; i < len(v); i++ { avg += float64(v[i]) @@ -194,24 +180,27 @@ func TestExpDecaySampleStatistics(t *testing.T) { for i := 1; i <= 10000; i++ { s.(*ExpDecaySample).update(now.Add(time.Duration(i)), int64(i)) } - testExpDecaySampleStatistics(t, s) + testExpDecaySampleStatistics(t, s.Snapshot()) } func TestUniformSample(t *testing.T) { - s := NewUniformSample(100) + sw := NewUniformSample(100) for i := 0; i < 1000; i++ { - s.Update(int64(i)) + sw.Update(int64(i)) } + s := sw.Snapshot() if size := s.Count(); size != 1000 { t.Errorf("s.Count(): 1000 != %v\n", size) } if size := s.Size(); size != 100 { t.Errorf("s.Size(): 100 != %v\n", size) } - if l := len(s.Values()); l != 100 { + values := s.(*sampleSnapshot).values + + if l := len(values); l != 100 { t.Errorf("len(s.Values()): 100 != %v\n", l) } - for _, v := range s.Values() { + for _, v := range values { if v > 1000 || v < 0 { t.Errorf("out of range [0, 100): %v\n", v) } @@ -219,12 +208,13 @@ func TestUniformSample(t *testing.T) { } func TestUniformSampleIncludesTail(t *testing.T) { - s := NewUniformSample(100) + sw := NewUniformSample(100) max := 100 for i := 0; i < max; i++ { - s.Update(int64(i)) + sw.Update(int64(i)) } - v := s.Values() + s := sw.Snapshot() + v := s.(*sampleSnapshot).values sum := 0 exp := (max - 1) * max / 2 for i := 0; i < len(v); i++ { @@ -250,7 +240,7 @@ func TestUniformSampleStatistics(t *testing.T) { for i := 1; i <= 10000; i++ { s.Update(int64(i)) } - testUniformSampleStatistics(t, s) + testUniformSampleStatistics(t, s.Snapshot()) } func benchmarkSample(b *testing.B, s Sample) { @@ -267,7 +257,7 @@ func benchmarkSample(b *testing.B, s Sample) { b.Logf("GC cost: %d ns/op", int(memStats.PauseTotalNs-pauseTotalNs)/b.N) } -func testExpDecaySampleStatistics(t *testing.T, s Sample) { +func testExpDecaySampleStatistics(t *testing.T, s SampleSnapshot) { if count := s.Count(); count != 10000 { t.Errorf("s.Count(): 10000 != %v\n", count) } @@ -295,7 +285,7 @@ func testExpDecaySampleStatistics(t *testing.T, s Sample) { } } -func testUniformSampleStatistics(t *testing.T, s Sample) { +func testUniformSampleStatistics(t *testing.T, s SampleSnapshot) { if count := s.Count(); count != 10000 { t.Errorf("s.Count(): 10000 != %v\n", count) } @@ -349,8 +339,22 @@ func TestUniformSampleConcurrentUpdateCount(t *testing.T) { } }() for i := 0; i < 1000; i++ { - s.Count() + s.Snapshot().Count() time.Sleep(5 * time.Millisecond) } quit <- struct{}{} } + +func BenchmarkCalculatePercentiles(b *testing.B) { + pss := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999} + var vals []int64 + for i := 0; i < 1000; i++ { + vals = append(vals, int64(rand.Int31())) + } + v := make([]int64, len(vals)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + copy(v, vals) + _ = CalculatePercentiles(v, pss) + } +} diff --git a/metrics/syslog.go b/metrics/syslog.go index 76c849056757..fd856d697316 100644 --- a/metrics/syslog.go +++ b/metrics/syslog.go @@ -16,15 +16,15 @@ func Syslog(r Registry, d time.Duration, w *syslog.Writer) { r.Each(func(name string, i interface{}) { switch metric := i.(type) { case Counter: - w.Info(fmt.Sprintf("counter %s: count: %d", name, metric.Count())) + w.Info(fmt.Sprintf("counter %s: count: %d", name, metric.Snapshot().Count())) case CounterFloat64: - w.Info(fmt.Sprintf("counter %s: count: %f", name, metric.Count())) + w.Info(fmt.Sprintf("counter %s: count: %f", name, metric.Snapshot().Count())) case Gauge: - w.Info(fmt.Sprintf("gauge %s: value: %d", name, metric.Value())) + w.Info(fmt.Sprintf("gauge %s: value: %d", name, metric.Snapshot().Value())) case GaugeFloat64: - w.Info(fmt.Sprintf("gauge %s: value: %f", name, metric.Value())) + w.Info(fmt.Sprintf("gauge %s: value: %f", name, metric.Snapshot().Value())) case GaugeInfo: - w.Info(fmt.Sprintf("gauge %s: value: %s", name, metric.Value())) + w.Info(fmt.Sprintf("gauge %s: value: %s", name, metric.Snapshot().Value())) case Healthcheck: metric.Check() w.Info(fmt.Sprintf("healthcheck %s: error: %v", name, metric.Error())) diff --git a/metrics/testdata/opentsb.want b/metrics/testdata/opentsb.want index c8e40a525042..43fe1b2ac27a 100644 --- a/metrics/testdata/opentsb.want +++ b/metrics/testdata/opentsb.want @@ -1,4 +1,4 @@ -put pre.elite.count 978307200 0 host=hal9000 +put pre.elite.count 978307200 1337 host=hal9000 put pre.elite.one-minute 978307200 0.00 host=hal9000 put pre.elite.five-minute 978307200 0.00 host=hal9000 put pre.elite.fifteen-minute 978307200 0.00 host=hal9000 diff --git a/metrics/timer.go b/metrics/timer.go index 2e1a9be47295..576ad8aa3e63 100644 --- a/metrics/timer.go +++ b/metrics/timer.go @@ -5,26 +5,18 @@ import ( "time" ) +type TimerSnapshot interface { + HistogramSnapshot + MeterSnapshot +} + // Timers capture the duration and rate of events. type Timer interface { - Count() int64 - Max() int64 - Mean() float64 - Min() int64 - Percentile(float64) float64 - Percentiles([]float64) []float64 - Rate1() float64 - Rate5() float64 - Rate15() float64 - RateMean() float64 - Snapshot() Timer - StdDev() float64 + Snapshot() TimerSnapshot Stop() - Sum() int64 Time(func()) - Update(time.Duration) UpdateSince(time.Time) - Variance() float64 + Update(time.Duration) } // GetOrRegisterTimer returns an existing Timer or constructs and registers a @@ -78,61 +70,11 @@ func NewTimer() Timer { // NilTimer is a no-op Timer. type NilTimer struct{} -// Count is a no-op. -func (NilTimer) Count() int64 { return 0 } - -// Max is a no-op. -func (NilTimer) Max() int64 { return 0 } - -// Mean is a no-op. -func (NilTimer) Mean() float64 { return 0.0 } - -// Min is a no-op. -func (NilTimer) Min() int64 { return 0 } - -// Percentile is a no-op. -func (NilTimer) Percentile(p float64) float64 { return 0.0 } - -// Percentiles is a no-op. -func (NilTimer) Percentiles(ps []float64) []float64 { - return make([]float64, len(ps)) -} - -// Rate1 is a no-op. -func (NilTimer) Rate1() float64 { return 0.0 } - -// Rate5 is a no-op. -func (NilTimer) Rate5() float64 { return 0.0 } - -// Rate15 is a no-op. -func (NilTimer) Rate15() float64 { return 0.0 } - -// RateMean is a no-op. -func (NilTimer) RateMean() float64 { return 0.0 } - -// Snapshot is a no-op. -func (NilTimer) Snapshot() Timer { return NilTimer{} } - -// StdDev is a no-op. -func (NilTimer) StdDev() float64 { return 0.0 } - -// Stop is a no-op. -func (NilTimer) Stop() {} - -// Sum is a no-op. -func (NilTimer) Sum() int64 { return 0 } - -// Time is a no-op. -func (NilTimer) Time(f func()) { f() } - -// Update is a no-op. -func (NilTimer) Update(time.Duration) {} - -// UpdateSince is a no-op. -func (NilTimer) UpdateSince(time.Time) {} - -// Variance is a no-op. -func (NilTimer) Variance() float64 { return 0.0 } +func (NilTimer) Snapshot() TimerSnapshot { return (*emptySnapshot)(nil) } +func (NilTimer) Stop() {} +func (NilTimer) Time(f func()) { f() } +func (NilTimer) Update(time.Duration) {} +func (NilTimer) UpdateSince(time.Time) {} // StandardTimer is the standard implementation of a Timer and uses a Histogram // and Meter. @@ -142,82 +84,21 @@ type StandardTimer struct { mutex sync.Mutex } -// Count returns the number of events recorded. -func (t *StandardTimer) Count() int64 { - return t.histogram.Count() -} - -// Max returns the maximum value in the sample. -func (t *StandardTimer) Max() int64 { - return t.histogram.Max() -} - -// Mean returns the mean of the values in the sample. -func (t *StandardTimer) Mean() float64 { - return t.histogram.Mean() -} - -// Min returns the minimum value in the sample. -func (t *StandardTimer) Min() int64 { - return t.histogram.Min() -} - -// Percentile returns an arbitrary percentile of the values in the sample. -func (t *StandardTimer) Percentile(p float64) float64 { - return t.histogram.Percentile(p) -} - -// Percentiles returns a slice of arbitrary percentiles of the values in the -// sample. -func (t *StandardTimer) Percentiles(ps []float64) []float64 { - return t.histogram.Percentiles(ps) -} - -// Rate1 returns the one-minute moving average rate of events per second. -func (t *StandardTimer) Rate1() float64 { - return t.meter.Rate1() -} - -// Rate5 returns the five-minute moving average rate of events per second. -func (t *StandardTimer) Rate5() float64 { - return t.meter.Rate5() -} - -// Rate15 returns the fifteen-minute moving average rate of events per second. -func (t *StandardTimer) Rate15() float64 { - return t.meter.Rate15() -} - -// RateMean returns the meter's mean rate of events per second. -func (t *StandardTimer) RateMean() float64 { - return t.meter.RateMean() -} - // Snapshot returns a read-only copy of the timer. -func (t *StandardTimer) Snapshot() Timer { +func (t *StandardTimer) Snapshot() TimerSnapshot { t.mutex.Lock() defer t.mutex.Unlock() - return &TimerSnapshot{ - histogram: t.histogram.Snapshot().(*HistogramSnapshot), - meter: t.meter.Snapshot().(*MeterSnapshot), + return &timerSnapshot{ + histogram: t.histogram.Snapshot(), + meter: t.meter.Snapshot(), } } -// StdDev returns the standard deviation of the values in the sample. -func (t *StandardTimer) StdDev() float64 { - return t.histogram.StdDev() -} - // Stop stops the meter. func (t *StandardTimer) Stop() { t.meter.Stop() } -// Sum returns the sum in the sample. -func (t *StandardTimer) Sum() int64 { - return t.histogram.Sum() -} - // Record the duration of the execution of the given function. func (t *StandardTimer) Time(f func()) { ts := time.Now() @@ -241,86 +122,63 @@ func (t *StandardTimer) UpdateSince(ts time.Time) { t.meter.Mark(1) } -// Variance returns the variance of the values in the sample. -func (t *StandardTimer) Variance() float64 { - return t.histogram.Variance() -} - -// TimerSnapshot is a read-only copy of another Timer. -type TimerSnapshot struct { - histogram *HistogramSnapshot - meter *MeterSnapshot +// timerSnapshot is a read-only copy of another Timer. +type timerSnapshot struct { + histogram HistogramSnapshot + meter MeterSnapshot } // Count returns the number of events recorded at the time the snapshot was // taken. -func (t *TimerSnapshot) Count() int64 { return t.histogram.Count() } +func (t *timerSnapshot) Count() int64 { return t.histogram.Count() } // Max returns the maximum value at the time the snapshot was taken. -func (t *TimerSnapshot) Max() int64 { return t.histogram.Max() } +func (t *timerSnapshot) Max() int64 { return t.histogram.Max() } + +// Size returns the size of the sample at the time the snapshot was taken. +func (t *timerSnapshot) Size() int { return t.histogram.Size() } // Mean returns the mean value at the time the snapshot was taken. -func (t *TimerSnapshot) Mean() float64 { return t.histogram.Mean() } +func (t *timerSnapshot) Mean() float64 { return t.histogram.Mean() } // Min returns the minimum value at the time the snapshot was taken. -func (t *TimerSnapshot) Min() int64 { return t.histogram.Min() } +func (t *timerSnapshot) Min() int64 { return t.histogram.Min() } // Percentile returns an arbitrary percentile of sampled values at the time the // snapshot was taken. -func (t *TimerSnapshot) Percentile(p float64) float64 { +func (t *timerSnapshot) Percentile(p float64) float64 { return t.histogram.Percentile(p) } // Percentiles returns a slice of arbitrary percentiles of sampled values at // the time the snapshot was taken. -func (t *TimerSnapshot) Percentiles(ps []float64) []float64 { +func (t *timerSnapshot) Percentiles(ps []float64) []float64 { return t.histogram.Percentiles(ps) } // Rate1 returns the one-minute moving average rate of events per second at the // time the snapshot was taken. -func (t *TimerSnapshot) Rate1() float64 { return t.meter.Rate1() } +func (t *timerSnapshot) Rate1() float64 { return t.meter.Rate1() } // Rate5 returns the five-minute moving average rate of events per second at // the time the snapshot was taken. -func (t *TimerSnapshot) Rate5() float64 { return t.meter.Rate5() } +func (t *timerSnapshot) Rate5() float64 { return t.meter.Rate5() } // Rate15 returns the fifteen-minute moving average rate of events per second // at the time the snapshot was taken. -func (t *TimerSnapshot) Rate15() float64 { return t.meter.Rate15() } +func (t *timerSnapshot) Rate15() float64 { return t.meter.Rate15() } // RateMean returns the meter's mean rate of events per second at the time the // snapshot was taken. -func (t *TimerSnapshot) RateMean() float64 { return t.meter.RateMean() } - -// Snapshot returns the snapshot. -func (t *TimerSnapshot) Snapshot() Timer { return t } +func (t *timerSnapshot) RateMean() float64 { return t.meter.RateMean() } // StdDev returns the standard deviation of the values at the time the snapshot // was taken. -func (t *TimerSnapshot) StdDev() float64 { return t.histogram.StdDev() } - -// Stop is a no-op. -func (t *TimerSnapshot) Stop() {} +func (t *timerSnapshot) StdDev() float64 { return t.histogram.StdDev() } // Sum returns the sum at the time the snapshot was taken. -func (t *TimerSnapshot) Sum() int64 { return t.histogram.Sum() } - -// Time panics. -func (*TimerSnapshot) Time(func()) { - panic("Time called on a TimerSnapshot") -} - -// Update panics. -func (*TimerSnapshot) Update(time.Duration) { - panic("Update called on a TimerSnapshot") -} - -// UpdateSince panics. -func (*TimerSnapshot) UpdateSince(time.Time) { - panic("UpdateSince called on a TimerSnapshot") -} +func (t *timerSnapshot) Sum() int64 { return t.histogram.Sum() } // Variance returns the variance of the values at the time the snapshot was // taken. -func (t *TimerSnapshot) Variance() float64 { return t.histogram.Variance() } +func (t *timerSnapshot) Variance() float64 { return t.histogram.Variance() } diff --git a/metrics/timer_test.go b/metrics/timer_test.go index 390a0e6c23a8..0afa63a23072 100644 --- a/metrics/timer_test.go +++ b/metrics/timer_test.go @@ -18,7 +18,7 @@ func BenchmarkTimer(b *testing.B) { func TestGetOrRegisterTimer(t *testing.T) { r := NewRegistry() NewRegisteredTimer("foo", r).Update(47) - if tm := GetOrRegisterTimer("foo", r); tm.Count() != 1 { + if tm := GetOrRegisterTimer("foo", r).Snapshot(); tm.Count() != 1 { t.Fatal(tm) } } @@ -27,7 +27,7 @@ func TestTimerExtremes(t *testing.T) { tm := NewTimer() tm.Update(math.MaxInt64) tm.Update(0) - if stdDev := tm.StdDev(); stdDev != 4.611686018427388e+18 { + if stdDev := tm.Snapshot().StdDev(); stdDev != 4.611686018427388e+18 { t.Errorf("tm.StdDev(): 4.611686018427388e+18 != %v\n", stdDev) } } @@ -56,7 +56,7 @@ func TestTimerFunc(t *testing.T) { }) var ( drift = time.Millisecond * 2 - measured = time.Duration(tm.Max()) + measured = time.Duration(tm.Snapshot().Max()) ceil = actualTime + drift floor = actualTime - drift ) @@ -66,7 +66,7 @@ func TestTimerFunc(t *testing.T) { } func TestTimerZero(t *testing.T) { - tm := NewTimer() + tm := NewTimer().Snapshot() if count := tm.Count(); count != 0 { t.Errorf("tm.Count(): 0 != %v\n", count) } @@ -110,5 +110,5 @@ func ExampleGetOrRegisterTimer() { m := "account.create.latency" t := GetOrRegisterTimer(m, nil) t.Update(47) - fmt.Println(t.Max()) // Output: 47 + fmt.Println(t.Snapshot().Max()) // Output: 47 } diff --git a/metrics/writer.go b/metrics/writer.go index ec2e4f8c6a60..098da45c27b2 100644 --- a/metrics/writer.go +++ b/metrics/writer.go @@ -29,19 +29,19 @@ func WriteOnce(r Registry, w io.Writer) { switch metric := namedMetric.m.(type) { case Counter: fmt.Fprintf(w, "counter %s\n", namedMetric.name) - fmt.Fprintf(w, " count: %9d\n", metric.Count()) + fmt.Fprintf(w, " count: %9d\n", metric.Snapshot().Count()) case CounterFloat64: fmt.Fprintf(w, "counter %s\n", namedMetric.name) - fmt.Fprintf(w, " count: %f\n", metric.Count()) + fmt.Fprintf(w, " count: %f\n", metric.Snapshot().Count()) case Gauge: fmt.Fprintf(w, "gauge %s\n", namedMetric.name) - fmt.Fprintf(w, " value: %9d\n", metric.Value()) + fmt.Fprintf(w, " value: %9d\n", metric.Snapshot().Value()) case GaugeFloat64: fmt.Fprintf(w, "gauge %s\n", namedMetric.name) - fmt.Fprintf(w, " value: %f\n", metric.Value()) + fmt.Fprintf(w, " value: %f\n", metric.Snapshot().Value()) case GaugeInfo: fmt.Fprintf(w, "gauge %s\n", namedMetric.name) - fmt.Fprintf(w, " value: %s\n", metric.Value().String()) + fmt.Fprintf(w, " value: %s\n", metric.Snapshot().Value().String()) case Healthcheck: metric.Check() fmt.Fprintf(w, "healthcheck %s\n", namedMetric.name) From 2f36a79f670a2516fb401369d8538677a8fe5b6e Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 365/479] metrics: add support for enabling metrics from env vars (#28118) --- metrics/metrics.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/metrics/metrics.go b/metrics/metrics.go index d5c1c61cbd06..3ff72bd73fd2 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -9,7 +9,9 @@ import ( "os" "runtime/metrics" "runtime/pprof" + "strconv" "strings" + "syscall" "time" "github.com/XinFinOrg/XDPoSChain/log" @@ -30,13 +32,35 @@ var EnabledExpensive = false // enablerFlags is the CLI flag names to use to enable metrics collections. var enablerFlags = []string{"metrics"} +// enablerEnvVars is the env var names to use to enable metrics collections. +var enablerEnvVars = []string{"XDC_METRICS"} + // expensiveEnablerFlags is the CLI flag names to use to enable metrics collections. var expensiveEnablerFlags = []string{"metrics-expensive"} +// expensiveEnablerEnvVars is the env var names to use to enable metrics collections. +var expensiveEnablerEnvVars = []string{"XDC_METRICS_EXPENSIVE"} + // Init enables or disables the metrics system. Since we need this to run before // any other code gets to create meters and timers, we'll actually do an ugly hack // and peek into the command line args for the metrics flag. func init() { + for _, enabler := range enablerEnvVars { + if val, found := syscall.Getenv(enabler); found && !Enabled { + if enable, _ := strconv.ParseBool(val); enable { // ignore error, flag parser will choke on it later + log.Info("Enabling metrics collection") + Enabled = true + } + } + } + for _, enabler := range expensiveEnablerEnvVars { + if val, found := syscall.Getenv(enabler); found && !EnabledExpensive { + if enable, _ := strconv.ParseBool(val); enable { // ignore error, flag parser will choke on it later + log.Info("Enabling expensive metrics collection") + EnabledExpensive = true + } + } + } for _, arg := range os.Args { flag := strings.TrimLeft(arg, "-") From 49b5886150457806fc8f7158570f9e92afd11a1b Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 366/479] =?UTF-8?q?rpc:=20fix=20ns/=C2=B5s=20mismatch=20in?= =?UTF-8?q?=20metrics=20(#28649)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rpc/duration/all meter was in nanoseconds, the individual meter in microseconds. This PR changes it so both of them use nanoseconds. --- metrics/timer.go | 10 ++++------ rpc/metrics.go | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/metrics/timer.go b/metrics/timer.go index 576ad8aa3e63..bb8def82fb29 100644 --- a/metrics/timer.go +++ b/metrics/timer.go @@ -106,20 +106,18 @@ func (t *StandardTimer) Time(f func()) { t.Update(time.Since(ts)) } -// Record the duration of an event. +// Record the duration of an event, in nanoseconds. func (t *StandardTimer) Update(d time.Duration) { t.mutex.Lock() defer t.mutex.Unlock() - t.histogram.Update(int64(d)) + t.histogram.Update(d.Nanoseconds()) t.meter.Mark(1) } // Record the duration of an event that started at a time and ends now. +// The record uses nanoseconds. func (t *StandardTimer) UpdateSince(ts time.Time) { - t.mutex.Lock() - defer t.mutex.Unlock() - t.histogram.Update(int64(time.Since(ts))) - t.meter.Mark(1) + t.Update(time.Since(ts)) } // timerSnapshot is a read-only copy of another Timer. diff --git a/rpc/metrics.go b/rpc/metrics.go index 11f853dd2401..ea8837f6664b 100644 --- a/rpc/metrics.go +++ b/rpc/metrics.go @@ -46,5 +46,5 @@ func updateServeTimeHistogram(method string, success bool, elapsed time.Duration metrics.NewExpDecaySample(1028, 0.015), ) } - metrics.GetOrRegisterHistogramLazy(h, nil, sampler).Update(elapsed.Microseconds()) + metrics.GetOrRegisterHistogramLazy(h, nil, sampler).Update(elapsed.Nanoseconds()) } From 6f67b357deb04736a6972b525aa04eeb895f7f87 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 367/479] metrics: fix docstring names (#28923) --- metrics/counter.go | 2 +- metrics/gauge.go | 4 ++-- metrics/gauge_float64.go | 2 +- metrics/gauge_info.go | 2 +- metrics/healthcheck.go | 2 +- metrics/histogram.go | 2 +- metrics/influxdb/influxdbv2.go | 2 +- metrics/sample.go | 4 ++-- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/metrics/counter.go b/metrics/counter.go index cb81599c215a..dbe8e16a90d2 100644 --- a/metrics/counter.go +++ b/metrics/counter.go @@ -8,7 +8,7 @@ type CounterSnapshot interface { Count() int64 } -// Counters hold an int64 value that can be incremented and decremented. +// Counter hold an int64 value that can be incremented and decremented. type Counter interface { Clear() Dec(int64) diff --git a/metrics/gauge.go b/metrics/gauge.go index 00b59873848c..5933df310786 100644 --- a/metrics/gauge.go +++ b/metrics/gauge.go @@ -2,12 +2,12 @@ package metrics import "sync/atomic" -// gaugeSnapshot contains a readonly int64. +// GaugeSnapshot contains a readonly int64. type GaugeSnapshot interface { Value() int64 } -// Gauges hold an int64 value that can be set arbitrarily. +// Gauge holds an int64 value that can be set arbitrarily. type Gauge interface { Snapshot() GaugeSnapshot Update(int64) diff --git a/metrics/gauge_float64.go b/metrics/gauge_float64.go index 967f2bc60e5c..c1c3c6b6e6f0 100644 --- a/metrics/gauge_float64.go +++ b/metrics/gauge_float64.go @@ -48,7 +48,7 @@ type gaugeFloat64Snapshot float64 // Value returns the value at the time the snapshot was taken. func (g gaugeFloat64Snapshot) Value() float64 { return float64(g) } -// NilGauge is a no-op Gauge. +// NilGaugeFloat64 is a no-op Gauge. type NilGaugeFloat64 struct{} func (NilGaugeFloat64) Snapshot() GaugeFloat64Snapshot { return NilGaugeFloat64{} } diff --git a/metrics/gauge_info.go b/metrics/gauge_info.go index c44b2d85f3ad..0010edc3249d 100644 --- a/metrics/gauge_info.go +++ b/metrics/gauge_info.go @@ -9,7 +9,7 @@ type GaugeInfoSnapshot interface { Value() GaugeInfoValue } -// GaugeInfos hold a GaugeInfoValue value that can be set arbitrarily. +// GaugeInfo holds a GaugeInfoValue value that can be set arbitrarily. type GaugeInfo interface { Update(GaugeInfoValue) Snapshot() GaugeInfoSnapshot diff --git a/metrics/healthcheck.go b/metrics/healthcheck.go index f1ae31e34aee..adcd15ab581a 100644 --- a/metrics/healthcheck.go +++ b/metrics/healthcheck.go @@ -1,6 +1,6 @@ package metrics -// Healthchecks hold an error value describing an arbitrary up/down status. +// Healthcheck holds an error value describing an arbitrary up/down status. type Healthcheck interface { Check() Error() error diff --git a/metrics/histogram.go b/metrics/histogram.go index 44de588bc1dc..10259a246377 100644 --- a/metrics/histogram.go +++ b/metrics/histogram.go @@ -4,7 +4,7 @@ type HistogramSnapshot interface { SampleSnapshot } -// Histograms calculate distribution statistics from a series of int64 values. +// Histogram calculates distribution statistics from a series of int64 values. type Histogram interface { Clear() Update(int64) diff --git a/metrics/influxdb/influxdbv2.go b/metrics/influxdb/influxdbv2.go index d45e9c4d45be..5d8ba1b012ac 100644 --- a/metrics/influxdb/influxdbv2.go +++ b/metrics/influxdb/influxdbv2.go @@ -25,7 +25,7 @@ type v2Reporter struct { write api.WriteAPI } -// InfluxDBWithTags starts a InfluxDB reporter which will post the from the given metrics.Registry at each d interval with the specified tags +// InfluxDBV2WithTags starts a InfluxDB reporter which will post the from the given metrics.Registry at each d interval with the specified tags func InfluxDBV2WithTags(r metrics.Registry, d time.Duration, endpoint string, token string, bucket string, organization string, namespace string, tags map[string]string) { rep := &v2Reporter{ reg: r, diff --git a/metrics/sample.go b/metrics/sample.go index 5398dd42d5de..dbd5a02f5452 100644 --- a/metrics/sample.go +++ b/metrics/sample.go @@ -148,13 +148,13 @@ func (NilSample) Clear() {} func (NilSample) Snapshot() SampleSnapshot { return (*emptySnapshot)(nil) } func (NilSample) Update(v int64) {} -// SamplePercentiles returns an arbitrary percentile of the slice of int64. +// SamplePercentile returns an arbitrary percentile of the slice of int64. func SamplePercentile(values []int64, p float64) float64 { return CalculatePercentiles(values, []float64{p})[0] } // CalculatePercentiles returns a slice of arbitrary percentiles of the slice of -// int64. This method returns interpolated results, so e.g if there are only two +// int64. This method returns interpolated results, so e.g. if there are only two // values, [0, 10], a 50% percentile will land between them. // // Note: As a side-effect, this method will also sort the slice of values. From 2220156b9a2dbf14b291cb6493c1e18cb9dadffd Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:13 +0800 Subject: [PATCH 368/479] cmd, core, metrics: always report expensive metrics (#29191) * cmd, core, metrics: always report expensive metrics * core, metrics: report block processing metrics as resetting timer * metrics: update reporter tests --- cmd/XDC/config.go | 3 ++- cmd/utils/flags.go | 5 ---- cmd/utils/flags_legacy.go | 6 +++++ core/blockchain.go | 28 ++++++++++----------- core/state/state_object.go | 19 +++++--------- core/state/statedb.go | 26 ++++++++----------- metrics/config.go | 2 +- metrics/influxdb/influxdb.go | 25 ++++++++++-------- metrics/influxdb/testdata/influxdbv1.want | 2 +- metrics/influxdb/testdata/influxdbv2.want | 2 +- metrics/metrics.go | 25 ------------------ metrics/prometheus/collector.go | 9 ++++--- metrics/prometheus/testdata/prometheus.want | 5 +++- 13 files changed, 64 insertions(+), 93 deletions(-) diff --git a/cmd/XDC/config.go b/cmd/XDC/config.go index 7dbc8774b895..360fb768d142 100644 --- a/cmd/XDC/config.go +++ b/cmd/XDC/config.go @@ -32,6 +32,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/eth/ethconfig" "github.com/XinFinOrg/XDPoSChain/internal/flags" + "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/metrics" "github.com/XinFinOrg/XDPoSChain/node" "github.com/XinFinOrg/XDPoSChain/params" @@ -271,7 +272,7 @@ func applyMetricConfig(ctx *cli.Context, cfg *XDCConfig) { cfg.Metrics.Enabled = ctx.Bool(utils.MetricsEnabledFlag.Name) } if ctx.IsSet(utils.MetricsEnabledExpensiveFlag.Name) { - cfg.Metrics.EnabledExpensive = ctx.Bool(utils.MetricsEnabledExpensiveFlag.Name) + log.Warn("Expensive metrics are collected by default, please remove this flag", "flag", utils.MetricsEnabledExpensiveFlag.Name) } if ctx.IsSet(utils.MetricsHTTPFlag.Name) { cfg.Metrics.HTTP = ctx.String(utils.MetricsHTTPFlag.Name) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 797f578243bb..eb8d75f297ce 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -644,11 +644,6 @@ var ( Usage: "Enable metrics collection and reporting", Category: flags.MetricsCategory, } - MetricsEnabledExpensiveFlag = &cli.BoolFlag{ - Name: "metrics-expensive", - Usage: "Enable expensive metrics collection and reporting", - Category: flags.MetricsCategory, - } // MetricsHTTPFlag defines the endpoint for a stand-alone metrics HTTP endpoint. // Since the pprof service enables sensitive/vulnerable behavior, this allows a user // to enable a public-OK metrics endpoint without having to worry about ALSO exposing diff --git a/cmd/utils/flags_legacy.go b/cmd/utils/flags_legacy.go index 3bd314292d52..afd1e0e8bba8 100644 --- a/cmd/utils/flags_legacy.go +++ b/cmd/utils/flags_legacy.go @@ -68,6 +68,12 @@ var ( Usage: "Prepends log messages with call-site location (deprecated)", Category: flags.DeprecatedCategory, } + // Deprecated February 2024 + MetricsEnabledExpensiveFlag = &cli.BoolFlag{ + Name: "metrics-expensive", + Usage: "Enable expensive metrics collection and reporting (deprecated)", + Category: flags.DeprecatedCategory, + } ) // showDeprecated displays deprecated flags that will be soon removed from the codebase. diff --git a/core/blockchain.go b/core/blockchain.go index f09fb31d670e..7aebc61155bd 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -62,20 +62,20 @@ var ( chainInfoGauge = metrics.NewRegisteredGaugeInfo("chain/info", nil) - accountReadTimer = metrics.NewRegisteredTimer("chain/account/reads", nil) - accountHashTimer = metrics.NewRegisteredTimer("chain/account/hashes", nil) - accountUpdateTimer = metrics.NewRegisteredTimer("chain/account/updates", nil) - accountCommitTimer = metrics.NewRegisteredTimer("chain/account/commits", nil) - - storageReadTimer = metrics.NewRegisteredTimer("chain/storage/reads", nil) - storageHashTimer = metrics.NewRegisteredTimer("chain/storage/hashes", nil) - storageUpdateTimer = metrics.NewRegisteredTimer("chain/storage/updates", nil) - storageCommitTimer = metrics.NewRegisteredTimer("chain/storage/commits", nil) - - blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil) - blockValidationTimer = metrics.NewRegisteredTimer("chain/validation", nil) - blockExecutionTimer = metrics.NewRegisteredTimer("chain/execution", nil) - blockWriteTimer = metrics.NewRegisteredTimer("chain/write", nil) + accountReadTimer = metrics.NewRegisteredResettingTimer("chain/account/reads", nil) + accountHashTimer = metrics.NewRegisteredResettingTimer("chain/account/hashes", nil) + accountUpdateTimer = metrics.NewRegisteredResettingTimer("chain/account/updates", nil) + accountCommitTimer = metrics.NewRegisteredResettingTimer("chain/account/commits", nil) + + storageReadTimer = metrics.NewRegisteredResettingTimer("chain/storage/reads", nil) + storageHashTimer = metrics.NewRegisteredResettingTimer("chain/storage/hashes", nil) + storageUpdateTimer = metrics.NewRegisteredResettingTimer("chain/storage/updates", nil) + storageCommitTimer = metrics.NewRegisteredResettingTimer("chain/storage/commits", nil) + + blockInsertTimer = metrics.NewRegisteredResettingTimer("chain/inserts", nil) + blockValidationTimer = metrics.NewRegisteredResettingTimer("chain/validation", nil) + blockExecutionTimer = metrics.NewRegisteredResettingTimer("chain/execution", nil) + blockWriteTimer = metrics.NewRegisteredResettingTimer("chain/write", nil) blockReorgMeter = metrics.NewRegisteredMeter("chain/reorg/executes", nil) blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil) diff --git a/core/state/state_object.go b/core/state/state_object.go index 10fcfc8b3cb2..bb19597cc6f9 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -26,7 +26,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/metrics" "github.com/XinFinOrg/XDPoSChain/rlp" ) @@ -176,9 +175,7 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has return s.fakeStorage[key] } // Track the amount of time wasted on reading the storge trie - if metrics.EnabledExpensive { - defer func(start time.Time) { s.db.StorageReads += time.Since(start) }(time.Now()) - } + defer func(start time.Time) { s.db.StorageReads += time.Since(start) }(time.Now()) value := common.Hash{} // Load from DB in case it is missing. enc, err := s.getTrie(db).TryGet(key[:]) @@ -276,9 +273,7 @@ func (s *stateObject) setState(key, value common.Hash) { // updateTrie writes cached storage modifications into the object's storage trie. func (s *stateObject) updateTrie(db Database) Trie { // Track the amount of time wasted on updating the storge trie - if metrics.EnabledExpensive { - defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now()) - } + defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now()) tr := s.getTrie(db) for key, value := range s.dirtyStorage { delete(s.dirtyStorage, key) @@ -298,9 +293,8 @@ func (s *stateObject) updateRoot(db Database) { s.updateTrie(db) // Track the amount of time wasted on hashing the storge trie - if metrics.EnabledExpensive { - defer func(start time.Time) { s.db.StorageHashes += time.Since(start) }(time.Now()) - } + defer func(start time.Time) { s.db.StorageHashes += time.Since(start) }(time.Now()) + s.data.Root = s.trie.Hash() } @@ -312,9 +306,8 @@ func (s *stateObject) CommitTrie(db Database) error { return s.dbErr } // Track the amount of time wasted on committing the storge trie - if metrics.EnabledExpensive { - defer func(start time.Time) { s.db.StorageCommits += time.Since(start) }(time.Now()) - } + defer func(start time.Time) { s.db.StorageCommits += time.Since(start) }(time.Now()) + root, err := s.trie.Commit(nil) if err == nil { s.data.Root = root diff --git a/core/state/statedb.go b/core/state/statedb.go index 0e0dc9e7e5b5..d357e6e1bf58 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -28,7 +28,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/log" - "github.com/XinFinOrg/XDPoSChain/metrics" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rlp" "github.com/XinFinOrg/XDPoSChain/trie" @@ -459,9 +458,8 @@ func (s *StateDB) GetTransientState(addr common.Address, key common.Hash) common // updateStateObject writes the given object to the trie. func (s *StateDB) updateStateObject(stateObject *stateObject) { // Track the amount of time wasted on updating the account from the trie - if metrics.EnabledExpensive { - defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now()) - } + defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now()) + // Encode the account and update the account trie addr := stateObject.Address() @@ -475,9 +473,8 @@ func (s *StateDB) updateStateObject(stateObject *stateObject) { // deleteStateObject removes the given object from the state trie. func (s *StateDB) deleteStateObject(stateObject *stateObject) { // Track the amount of time wasted on deleting the account from the trie - if metrics.EnabledExpensive { - defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now()) - } + defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now()) + // Delete the account from the trie stateObject.deleted = true @@ -503,9 +500,8 @@ func (s *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) return obj } // Track the amount of time wasted on loading the object from the database - if metrics.EnabledExpensive { - defer func(start time.Time) { s.AccountReads += time.Since(start) }(time.Now()) - } + defer func(start time.Time) { s.AccountReads += time.Since(start) }(time.Now()) + // Load the object from the database enc, err := s.trie.TryGet(addr[:]) if len(enc) == 0 { @@ -701,9 +697,8 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { s.Finalise(deleteEmptyObjects) // Track the amount of time wasted on hashing the account trie - if metrics.EnabledExpensive { - defer func(start time.Time) { s.AccountHashes += time.Since(start) }(time.Now()) - } + defer func(start time.Time) { s.AccountHashes += time.Since(start) }(time.Now()) + return s.trie.Hash() } @@ -770,9 +765,8 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) delete(s.stateObjectsDirty, addr) } // Write the account trie changes, measuing the amount of wasted time - if metrics.EnabledExpensive { - defer func(start time.Time) { s.AccountCommits += time.Since(start) }(time.Now()) - } + defer func(start time.Time) { s.AccountCommits += time.Since(start) }(time.Now()) + root, err = s.trie.Commit(func(leaf []byte, parent common.Hash) error { var account Account if err := rlp.DecodeBytes(leaf, &account); err != nil { diff --git a/metrics/config.go b/metrics/config.go index cac1fc9c184a..6acb985c16e5 100644 --- a/metrics/config.go +++ b/metrics/config.go @@ -19,7 +19,7 @@ package metrics // Config contains the configuration for the metric collection. type Config struct { Enabled bool `toml:",omitempty"` - EnabledExpensive bool `toml:",omitempty"` + EnabledExpensive bool `toml:"-"` HTTP string `toml:",omitempty"` Port int `toml:",omitempty"` EnableInfluxDB bool `toml:",omitempty"` diff --git a/metrics/influxdb/influxdb.go b/metrics/influxdb/influxdb.go index d7c35b671821..8cfa081c1421 100644 --- a/metrics/influxdb/influxdb.go +++ b/metrics/influxdb/influxdb.go @@ -98,20 +98,23 @@ func readMeter(namespace, name string, i interface{}) (string, map[string]interf } return measurement, fields case metrics.ResettingTimer: - t := metric.Snapshot() - if t.Count() == 0 { + ms := metric.Snapshot() + if ms.Count() == 0 { break } - ps := t.Percentiles([]float64{0.50, 0.95, 0.99}) - measurement := fmt.Sprintf("%s%s.span", namespace, name) + ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) + measurement := fmt.Sprintf("%s%s.timer", namespace, name) fields := map[string]interface{}{ - "count": t.Count(), - "max": t.Max(), - "mean": t.Mean(), - "min": t.Min(), - "p50": int(ps[0]), - "p95": int(ps[1]), - "p99": int(ps[2]), + "count": ms.Count(), + "max": ms.Max(), + "mean": ms.Mean(), + "min": ms.Min(), + "p50": ps[0], + "p75": ps[1], + "p95": ps[2], + "p99": ps[3], + "p999": ps[4], + "p9999": ps[5], } return measurement, fields } diff --git a/metrics/influxdb/testdata/influxdbv1.want b/metrics/influxdb/testdata/influxdbv1.want index 9443faedc5a2..ded9434c7314 100644 --- a/metrics/influxdb/testdata/influxdbv1.want +++ b/metrics/influxdb/testdata/influxdbv1.want @@ -7,5 +7,5 @@ goth.test/gauge_float64.gauge value=34567.89 978307200000000000 goth.test/gauge_info.gauge value="{\"arch\":\"amd64\",\"commit\":\"7caa2d8163ae3132c1c2d6978c76610caee2d949\",\"os\":\"linux\",\"protocol_versions\":\"64 65 66\",\"version\":\"1.10.18-unstable\"}" 978307200000000000 goth.test/histogram.histogram count=3i,max=3i,mean=2,min=1i,p25=1,p50=2,p75=3,p95=3,p99=3,p999=3,p9999=3,stddev=0.816496580927726,variance=0.6666666666666666 978307200000000000 goth.test/meter.meter count=0i,m1=0,m15=0,m5=0,mean=0 978307200000000000 -goth.test/resetting_timer.span count=6i,max=120000000i,mean=30000000,min=10000000i,p50=12500000i,p95=120000000i,p99=120000000i 978307200000000000 +goth.test/resetting_timer.timer count=6i,max=120000000i,mean=30000000,min=10000000i,p50=12500000,p75=40500000,p95=120000000,p99=120000000,p999=120000000,p9999=120000000 978307200000000000 goth.test/timer.timer count=6i,m1=0,m15=0,m5=0,max=120000000i,mean=38333333.333333336,meanrate=0,min=20000000i,p50=22500000,p75=48000000,p95=120000000,p99=120000000,p999=120000000,p9999=120000000,stddev=36545253.529775314,variance=1335555555555555.2 978307200000000000 diff --git a/metrics/influxdb/testdata/influxdbv2.want b/metrics/influxdb/testdata/influxdbv2.want index 9443faedc5a2..ded9434c7314 100644 --- a/metrics/influxdb/testdata/influxdbv2.want +++ b/metrics/influxdb/testdata/influxdbv2.want @@ -7,5 +7,5 @@ goth.test/gauge_float64.gauge value=34567.89 978307200000000000 goth.test/gauge_info.gauge value="{\"arch\":\"amd64\",\"commit\":\"7caa2d8163ae3132c1c2d6978c76610caee2d949\",\"os\":\"linux\",\"protocol_versions\":\"64 65 66\",\"version\":\"1.10.18-unstable\"}" 978307200000000000 goth.test/histogram.histogram count=3i,max=3i,mean=2,min=1i,p25=1,p50=2,p75=3,p95=3,p99=3,p999=3,p9999=3,stddev=0.816496580927726,variance=0.6666666666666666 978307200000000000 goth.test/meter.meter count=0i,m1=0,m15=0,m5=0,mean=0 978307200000000000 -goth.test/resetting_timer.span count=6i,max=120000000i,mean=30000000,min=10000000i,p50=12500000i,p95=120000000i,p99=120000000i 978307200000000000 +goth.test/resetting_timer.timer count=6i,max=120000000i,mean=30000000,min=10000000i,p50=12500000,p75=40500000,p95=120000000,p99=120000000,p999=120000000,p9999=120000000 978307200000000000 goth.test/timer.timer count=6i,m1=0,m15=0,m5=0,max=120000000i,mean=38333333.333333336,meanrate=0,min=20000000i,p50=22500000,p75=48000000,p95=120000000,p99=120000000,p999=120000000,p9999=120000000,stddev=36545253.529775314,variance=1335555555555555.2 978307200000000000 diff --git a/metrics/metrics.go b/metrics/metrics.go index 3ff72bd73fd2..42623114d021 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -24,23 +24,12 @@ import ( // for less cluttered pprof profiles. var Enabled = false -// EnabledExpensive is a soft-flag meant for external packages to check if costly -// metrics gathering is allowed or not. The goal is to separate standard metrics -// for health monitoring and debug metrics that might impact runtime performance. -var EnabledExpensive = false - // enablerFlags is the CLI flag names to use to enable metrics collections. var enablerFlags = []string{"metrics"} // enablerEnvVars is the env var names to use to enable metrics collections. var enablerEnvVars = []string{"XDC_METRICS"} -// expensiveEnablerFlags is the CLI flag names to use to enable metrics collections. -var expensiveEnablerFlags = []string{"metrics-expensive"} - -// expensiveEnablerEnvVars is the env var names to use to enable metrics collections. -var expensiveEnablerEnvVars = []string{"XDC_METRICS_EXPENSIVE"} - // Init enables or disables the metrics system. Since we need this to run before // any other code gets to create meters and timers, we'll actually do an ugly hack // and peek into the command line args for the metrics flag. @@ -53,14 +42,6 @@ func init() { } } } - for _, enabler := range expensiveEnablerEnvVars { - if val, found := syscall.Getenv(enabler); found && !EnabledExpensive { - if enable, _ := strconv.ParseBool(val); enable { // ignore error, flag parser will choke on it later - log.Info("Enabling expensive metrics collection") - EnabledExpensive = true - } - } - } for _, arg := range os.Args { flag := strings.TrimLeft(arg, "-") @@ -70,12 +51,6 @@ func init() { Enabled = true } } - for _, enabler := range expensiveEnablerFlags { - if !EnabledExpensive && flag == enabler { - log.Info("Enabling expensive metrics collection") - EnabledExpensive = true - } - } } } diff --git a/metrics/prometheus/collector.go b/metrics/prometheus/collector.go index 0b81c63ba5b7..ac2c960ac775 100644 --- a/metrics/prometheus/collector.go +++ b/metrics/prometheus/collector.go @@ -125,12 +125,13 @@ func (c *collector) addResettingTimer(name string, m metrics.ResettingTimerSnaps if m.Count() <= 0 { return } - ps := m.Percentiles([]float64{0.50, 0.95, 0.99}) + pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999} + ps := m.Percentiles(pv) c.writeSummaryCounter(name, m.Count()) c.buff.WriteString(fmt.Sprintf(typeSummaryTpl, mutateKey(name))) - c.writeSummaryPercentile(name, "0.50", ps[0]) - c.writeSummaryPercentile(name, "0.95", ps[1]) - c.writeSummaryPercentile(name, "0.99", ps[2]) + for i := range pv { + c.writeSummaryPercentile(name, strconv.FormatFloat(pv[i], 'f', -1, 64), ps[i]) + } c.buff.WriteRune('\n') } diff --git a/metrics/prometheus/testdata/prometheus.want b/metrics/prometheus/testdata/prometheus.want index 861c5f5cf087..a999d83801c6 100644 --- a/metrics/prometheus/testdata/prometheus.want +++ b/metrics/prometheus/testdata/prometheus.want @@ -53,9 +53,12 @@ test_meter 0 test_resetting_timer_count 6 # TYPE test_resetting_timer summary -test_resetting_timer {quantile="0.50"} 1.25e+07 +test_resetting_timer {quantile="0.5"} 1.25e+07 +test_resetting_timer {quantile="0.75"} 4.05e+07 test_resetting_timer {quantile="0.95"} 1.2e+08 test_resetting_timer {quantile="0.99"} 1.2e+08 +test_resetting_timer {quantile="0.999"} 1.2e+08 +test_resetting_timer {quantile="0.9999"} 1.2e+08 # TYPE test_timer_count counter test_timer_count 6 From 7f37ee2e635179c5f0b2456d9a5af4a9ca9b09ee Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:14 +0800 Subject: [PATCH 369/479] metrics: fix docstrings (#29279) --- metrics/json.go | 4 ++-- metrics/registry.go | 38 +++++++++++++++++++------------------- metrics/timer.go | 8 ++++---- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/metrics/json.go b/metrics/json.go index 2087d8211eb1..6b134d477b60 100644 --- a/metrics/json.go +++ b/metrics/json.go @@ -26,6 +26,6 @@ func WriteJSONOnce(r Registry, w io.Writer) { json.NewEncoder(w).Encode(r) } -func (p *PrefixedRegistry) MarshalJSON() ([]byte, error) { - return json.Marshal(p.GetAll()) +func (r *PrefixedRegistry) MarshalJSON() ([]byte, error) { + return json.Marshal(r.GetAll()) } diff --git a/metrics/registry.go b/metrics/registry.go index a6cae1fff0a9..8325f4e153e6 100644 --- a/metrics/registry.go +++ b/metrics/registry.go @@ -8,8 +8,8 @@ import ( "sync" ) -// DuplicateMetric is the error returned by Registry.Register when a metric -// already exists. If you mean to Register that metric you must first +// DuplicateMetric is the error returned by Registry. Register when a metric +// already exists. If you mean to Register that metric you must first // Unregister the existing metric. type DuplicateMetric string @@ -20,11 +20,11 @@ func (err DuplicateMetric) Error() string { // A Registry holds references to a set of metrics by name and can iterate // over them, calling callback functions provided by the user. // -// This is an interface so as to encourage other structs to implement +// This is an interface to encourage other structs to implement // the Registry API as appropriate. type Registry interface { - // Call the given function for each registered metric. + // Each call the given function for each registered metric. Each(func(string, interface{})) // Get the metric by the given name or nil if none is registered. @@ -33,7 +33,7 @@ type Registry interface { // GetAll metrics in the Registry. GetAll() map[string]map[string]interface{} - // Gets an existing metric or registers the given one. + // GetOrRegister gets an existing metric or registers the given one. // The interface can be the metric to register if not found in registry, // or a function returning the metric for lazy instantiation. GetOrRegister(string, interface{}) interface{} @@ -41,7 +41,7 @@ type Registry interface { // Register the given metric under the given name. Register(string, interface{}) error - // Run all registered healthchecks. + // RunHealthchecks run all registered healthchecks. RunHealthchecks() // Unregister the metric with the given name. @@ -52,7 +52,7 @@ type orderedRegistry struct { StandardRegistry } -// Call the given function for each registered metric. +// Each call the given function for each registered metric. func (r *orderedRegistry) Each(f func(string, interface{})) { var names []string reg := r.registered() @@ -75,13 +75,13 @@ func NewOrderedRegistry() Registry { return new(orderedRegistry) } -// The standard implementation of a Registry uses sync.map +// StandardRegistry the standard implementation of a Registry uses sync.map // of names to metrics. type StandardRegistry struct { metrics sync.Map } -// Call the given function for each registered metric. +// Each call the given function for each registered metric. func (r *StandardRegistry) Each(f func(string, interface{})) { for name, i := range r.registered() { f(name, i) @@ -94,7 +94,7 @@ func (r *StandardRegistry) Get(name string) interface{} { return item } -// Gets an existing metric or creates and registers a new one. Threadsafe +// GetOrRegister gets an existing metric or creates and registers a new one. Threadsafe // alternative to calling Get and Register on failure. // The interface can be the metric to register if not found in registry, // or a function returning the metric for lazy instantiation. @@ -114,7 +114,7 @@ func (r *StandardRegistry) GetOrRegister(name string, i interface{}) interface{} return item } -// Register the given metric under the given name. Returns a DuplicateMetric +// Register the given metric under the given name. Returns a DuplicateMetric // if a metric by the given name is already registered. func (r *StandardRegistry) Register(name string, i interface{}) error { // fast path @@ -133,7 +133,7 @@ func (r *StandardRegistry) Register(name string, i interface{}) error { return nil } -// Run all registered healthchecks. +// RunHealthchecks run all registered healthchecks. func (r *StandardRegistry) RunHealthchecks() { r.metrics.Range(func(key, value any) bool { if h, ok := value.(Healthcheck); ok { @@ -263,7 +263,7 @@ func NewPrefixedChildRegistry(parent Registry, prefix string) Registry { } } -// Call the given function for each registered metric. +// Each call the given function for each registered metric. func (r *PrefixedRegistry) Each(fn func(string, interface{})) { wrappedFn := func(prefix string) func(string, interface{}) { return func(name string, iface interface{}) { @@ -295,7 +295,7 @@ func (r *PrefixedRegistry) Get(name string) interface{} { return r.underlying.Get(realName) } -// Gets an existing metric or registers the given one. +// GetOrRegister gets an existing metric or registers the given one. // The interface can be the metric to register if not found in registry, // or a function returning the metric for lazy instantiation. func (r *PrefixedRegistry) GetOrRegister(name string, metric interface{}) interface{} { @@ -309,7 +309,7 @@ func (r *PrefixedRegistry) Register(name string, metric interface{}) error { return r.underlying.Register(realName, metric) } -// Run all registered healthchecks. +// RunHealthchecks run all registered healthchecks. func (r *PrefixedRegistry) RunHealthchecks() { r.underlying.RunHealthchecks() } @@ -329,7 +329,7 @@ var ( DefaultRegistry = NewRegistry() ) -// Call the given function for each registered metric. +// Each call the given function for each registered metric. func Each(f func(string, interface{})) { DefaultRegistry.Each(f) } @@ -339,7 +339,7 @@ func Get(name string) interface{} { return DefaultRegistry.Get(name) } -// Gets an existing metric or creates and registers a new one. Threadsafe +// GetOrRegister gets an existing metric or creates and registers a new one. Threadsafe // alternative to calling Get and Register on failure. func GetOrRegister(name string, i interface{}) interface{} { return DefaultRegistry.GetOrRegister(name, i) @@ -351,7 +351,7 @@ func Register(name string, i interface{}) error { return DefaultRegistry.Register(name, i) } -// Register the given metric under the given name. Panics if a metric by the +// MustRegister register the given metric under the given name. Panics if a metric by the // given name is already registered. func MustRegister(name string, i interface{}) { if err := Register(name, i); err != nil { @@ -359,7 +359,7 @@ func MustRegister(name string, i interface{}) { } } -// Run all registered healthchecks. +// RunHealthchecks run all registered healthchecks. func RunHealthchecks() { DefaultRegistry.RunHealthchecks() } diff --git a/metrics/timer.go b/metrics/timer.go index bb8def82fb29..fc2a88f508bc 100644 --- a/metrics/timer.go +++ b/metrics/timer.go @@ -10,7 +10,7 @@ type TimerSnapshot interface { MeterSnapshot } -// Timers capture the duration and rate of events. +// Timer capture the duration and rate of events. type Timer interface { Snapshot() TimerSnapshot Stop() @@ -99,14 +99,14 @@ func (t *StandardTimer) Stop() { t.meter.Stop() } -// Record the duration of the execution of the given function. +// Time record the duration of the execution of the given function. func (t *StandardTimer) Time(f func()) { ts := time.Now() f() t.Update(time.Since(ts)) } -// Record the duration of an event, in nanoseconds. +// Update the duration of an event, in nanoseconds. func (t *StandardTimer) Update(d time.Duration) { t.mutex.Lock() defer t.mutex.Unlock() @@ -114,7 +114,7 @@ func (t *StandardTimer) Update(d time.Duration) { t.meter.Mark(1) } -// Record the duration of an event that started at a time and ends now. +// UpdateSince update the duration of an event that started at a time and ends now. // The record uses nanoseconds. func (t *StandardTimer) UpdateSince(ts time.Time) { t.Update(time.Since(ts)) From fd8782b2d08810a7964d4bbdafd9c8a133c3d53f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:14 +0800 Subject: [PATCH 370/479] metrics: use min from go1.21 (#29307) --- metrics/sample_test.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/metrics/sample_test.go b/metrics/sample_test.go index 79673570554c..9835ec1c3003 100644 --- a/metrics/sample_test.go +++ b/metrics/sample_test.go @@ -87,13 +87,6 @@ func BenchmarkUniformSample1028(b *testing.B) { benchmarkSample(b, NewUniformSample(1028)) } -func min(a, b int) int { - if a < b { - return a - } - return b -} - func TestExpDecaySample(t *testing.T) { for _, tc := range []struct { reservoirSize int From 99f605ffabd7d3d1c70bdee393896af68e5aefca Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:14 +0800 Subject: [PATCH 371/479] metrics/influxdb: skip float64-precision-dependent tests on arm64 (#29047) metrics/influxdb: fix failed cases caused by float64 precision on arm64 --- metrics/influxdb/influxdb_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/metrics/influxdb/influxdb_test.go b/metrics/influxdb/influxdb_test.go index 07af3f90ce21..aa47f15e1825 100644 --- a/metrics/influxdb/influxdb_test.go +++ b/metrics/influxdb/influxdb_test.go @@ -23,6 +23,7 @@ import ( "net/http/httptest" "net/url" "os" + "runtime" "strings" "testing" @@ -37,6 +38,10 @@ func TestMain(m *testing.M) { } func TestExampleV1(t *testing.T) { + if runtime.GOARCH == "arm64" { + t.Skip("test skipped on ARM64 due to floating point precision differences") + } + r := internal.ExampleMetrics() var have, want string ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -69,6 +74,10 @@ func TestExampleV1(t *testing.T) { } func TestExampleV2(t *testing.T) { + if runtime.GOARCH == "arm64" { + t.Skip("test skipped on ARM64 due to floating point precision differences") + } + r := internal.ExampleMetrics() var have, want string ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { From c7565af9b8dca054d0b77e77d67bcca3734c0a02 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:14 +0800 Subject: [PATCH 372/479] metrics: remove dependency on golang.org/exp (#29314) --- go.mod | 1 - go.sum | 2 -- metrics/sample.go | 3 +-- metrics/writer.go | 3 +-- metrics/writer_test.go | 3 +-- 5 files changed, 3 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index de84356c85ad..28a24f8b6b38 100644 --- a/go.mod +++ b/go.mod @@ -55,7 +55,6 @@ require ( github.com/mattn/go-isatty v0.0.17 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible github.com/urfave/cli/v2 v2.27.5 - golang.org/x/exp v0.0.0-20231006140011-7918f672742d gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) diff --git a/go.sum b/go.sum index f1e8dd27aa3f..68019c92aba9 100644 --- a/go.sum +++ b/go.sum @@ -218,8 +218,6 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/metrics/sample.go b/metrics/sample.go index dbd5a02f5452..17b2bee28f10 100644 --- a/metrics/sample.go +++ b/metrics/sample.go @@ -3,10 +3,9 @@ package metrics import ( "math" "math/rand" + "slices" "sync" "time" - - "golang.org/x/exp/slices" ) const rescaleThreshold = time.Hour diff --git a/metrics/writer.go b/metrics/writer.go index 098da45c27b2..c211c5046b7d 100644 --- a/metrics/writer.go +++ b/metrics/writer.go @@ -3,10 +3,9 @@ package metrics import ( "fmt" "io" + "slices" "strings" "time" - - "golang.org/x/exp/slices" ) // Write sorts writes each metric in the given registry periodically to the diff --git a/metrics/writer_test.go b/metrics/writer_test.go index 8376bf8975c8..edcfe955abcf 100644 --- a/metrics/writer_test.go +++ b/metrics/writer_test.go @@ -1,9 +1,8 @@ package metrics import ( + "slices" "testing" - - "golang.org/x/exp/slices" ) func TestMetricsSorting(t *testing.T) { From 988465cafa5304e6de97fd259db74f29210e4bb7 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:14 +0800 Subject: [PATCH 373/479] metrics: fix mismatched names in comments (#29348) --- metrics/gauge.go | 2 +- metrics/meter.go | 2 +- metrics/metrics.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/metrics/gauge.go b/metrics/gauge.go index 5933df310786..6d10bbf85660 100644 --- a/metrics/gauge.go +++ b/metrics/gauge.go @@ -74,7 +74,7 @@ func (g *StandardGauge) Update(v int64) { g.value.Store(v) } -// Update updates the gauge's value if v is larger then the current value. +// UpdateIfGt updates the gauge's value if v is larger then the current value. func (g *StandardGauge) UpdateIfGt(v int64) { for { exist := g.value.Load() diff --git a/metrics/meter.go b/metrics/meter.go index 22475ef6ebee..432838f4ef7b 100644 --- a/metrics/meter.go +++ b/metrics/meter.go @@ -173,7 +173,7 @@ type meterArbiter struct { var arbiter = meterArbiter{ticker: time.NewTicker(5 * time.Second), meters: make(map[*StandardMeter]struct{})} -// Ticks meters on the scheduled interval +// tick meters on the scheduled interval func (ma *meterArbiter) tick() { for range ma.ticker.C { ma.tickMeters() diff --git a/metrics/metrics.go b/metrics/metrics.go index 42623114d021..797001ab656e 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -30,7 +30,7 @@ var enablerFlags = []string{"metrics"} // enablerEnvVars is the env var names to use to enable metrics collections. var enablerEnvVars = []string{"XDC_METRICS"} -// Init enables or disables the metrics system. Since we need this to run before +// init enables or disables the metrics system. Since we need this to run before // any other code gets to create meters and timers, we'll actually do an ugly hack // and peek into the command line args for the metrics flag. func init() { From cf90a7aa76aba706beef6515968687c08ae48f6f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:14 +0800 Subject: [PATCH 374/479] metrics: remove librato (#29624) --- metrics/README.md | 19 --- metrics/librato/client.go | 104 --------------- metrics/librato/librato.go | 254 ------------------------------------- 3 files changed, 377 deletions(-) delete mode 100644 metrics/librato/client.go delete mode 100644 metrics/librato/librato.go diff --git a/metrics/README.md b/metrics/README.md index e2d7945008e6..85b119470a92 100644 --- a/metrics/README.md +++ b/metrics/README.md @@ -100,24 +100,6 @@ go influxdb.InfluxDB(metrics.DefaultRegistry, ) ``` -Periodically upload every metric to Librato using the [Librato client](https://github.com/mihasya/go-metrics-librato): - -**Note**: the client included with this repository under the `librato` package -has been deprecated and moved to the repository linked above. - -```go -import "github.com/mihasya/go-metrics-librato" - -go librato.Librato(metrics.DefaultRegistry, - 10e9, // interval - "example@example.com", // account owner email address - "token", // Librato API token - "hostname", // source - []float64{0.95}, // percentiles to send - time.Millisecond, // time unit -) -``` - Periodically emit every metric to StatHat: ```go @@ -157,7 +139,6 @@ Publishing Metrics Clients are available for the following destinations: -* Librato - https://github.com/mihasya/go-metrics-librato * Graphite - https://github.com/cyberdelia/go-metrics-graphite * InfluxDB - https://github.com/vrischmann/go-metrics-influxdb * Ganglia - https://github.com/appscode/metlia diff --git a/metrics/librato/client.go b/metrics/librato/client.go deleted file mode 100644 index 053e1811d4ed..000000000000 --- a/metrics/librato/client.go +++ /dev/null @@ -1,104 +0,0 @@ -package librato - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "net/http" -) - -const Operations = "operations" -const OperationsShort = "ops" - -type LibratoClient struct { - Email, Token string -} - -// property strings -const ( - // display attributes - Color = "color" - DisplayMax = "display_max" - DisplayMin = "display_min" - DisplayUnitsLong = "display_units_long" - DisplayUnitsShort = "display_units_short" - DisplayStacked = "display_stacked" - DisplayTransform = "display_transform" - // special gauge display attributes - SummarizeFunction = "summarize_function" - Aggregate = "aggregate" - - // metric keys - Name = "name" - Period = "period" - Description = "description" - DisplayName = "display_name" - Attributes = "attributes" - - // measurement keys - MeasureTime = "measure_time" - Source = "source" - Value = "value" - - // special gauge keys - Count = "count" - Sum = "sum" - Max = "max" - Min = "min" - SumSquares = "sum_squares" - - // batch keys - Counters = "counters" - Gauges = "gauges" - - MetricsPostUrl = "https://metrics-api.librato.com/v1/metrics" -) - -type Measurement map[string]interface{} -type Metric map[string]interface{} - -type Batch struct { - Gauges []Measurement `json:"gauges,omitempty"` - Counters []Measurement `json:"counters,omitempty"` - MeasureTime int64 `json:"measure_time"` - Source string `json:"source"` -} - -func (lc *LibratoClient) PostMetrics(batch Batch) (err error) { - var ( - js []byte - req *http.Request - resp *http.Response - ) - - if len(batch.Counters) == 0 && len(batch.Gauges) == 0 { - return nil - } - - if js, err = json.Marshal(batch); err != nil { - return - } - - if req, err = http.NewRequest(http.MethodPost, MetricsPostUrl, bytes.NewBuffer(js)); err != nil { - return - } - - req.Header.Set("Content-Type", "application/json") - req.SetBasicAuth(lc.Email, lc.Token) - - resp, err = http.DefaultClient.Do(req) - if err != nil { - return - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var body []byte - if body, err = io.ReadAll(resp.Body); err != nil { - body = []byte(fmt.Sprintf("(could not fetch response body for error: %s)", err)) - } - err = fmt.Errorf("unable to post to Librato: %d %s %s", resp.StatusCode, resp.Status, string(body)) - } - return -} diff --git a/metrics/librato/librato.go b/metrics/librato/librato.go deleted file mode 100644 index fa0123e05abf..000000000000 --- a/metrics/librato/librato.go +++ /dev/null @@ -1,254 +0,0 @@ -package librato - -import ( - "fmt" - "log" - "math" - "regexp" - "time" - - "github.com/XinFinOrg/XDPoSChain/metrics" -) - -// a regexp for extracting the unit from time.Duration.String -var unitRegexp = regexp.MustCompile(`[^\\d]+$`) - -// a helper that turns a time.Duration into librato display attributes for timer metrics -func translateTimerAttributes(d time.Duration) (attrs map[string]interface{}) { - attrs = make(map[string]interface{}) - attrs[DisplayTransform] = fmt.Sprintf("x/%d", int64(d)) - attrs[DisplayUnitsShort] = string(unitRegexp.Find([]byte(d.String()))) - return -} - -type Reporter struct { - Email, Token string - Namespace string - Source string - Interval time.Duration - Registry metrics.Registry - Percentiles []float64 // percentiles to report on histogram metrics - TimerAttributes map[string]interface{} // units in which timers will be displayed - intervalSec int64 -} - -func NewReporter(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) *Reporter { - return &Reporter{e, t, "", s, d, r, p, translateTimerAttributes(u), int64(d / time.Second)} -} - -func Librato(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) { - NewReporter(r, d, e, t, s, p, u).Run() -} - -func (rep *Reporter) Run() { - log.Printf("WARNING: This client has been DEPRECATED! It has been moved to https://github.com/mihasya/go-metrics-librato and will be removed from rcrowley/go-metrics on August 5th 2015") - ticker := time.NewTicker(rep.Interval) - defer ticker.Stop() - metricsApi := &LibratoClient{rep.Email, rep.Token} - for now := range ticker.C { - var metrics Batch - var err error - if metrics, err = rep.BuildRequest(now, rep.Registry); err != nil { - log.Printf("ERROR constructing librato request body %s", err) - continue - } - if err := metricsApi.PostMetrics(metrics); err != nil { - log.Printf("ERROR sending metrics to librato %s", err) - continue - } - } -} - -// calculate sum of squares from data provided by metrics.Histogram -// see http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods -func sumSquares(icount int64, mean, stDev float64) float64 { - count := float64(icount) - sumSquared := math.Pow(count*mean, 2) - sumSquares := math.Pow(count*stDev, 2) + sumSquared/count - if math.IsNaN(sumSquares) { - return 0.0 - } - return sumSquares -} -func sumSquaresTimer(t metrics.TimerSnapshot) float64 { - count := float64(t.Count()) - sumSquared := math.Pow(count*t.Mean(), 2) - sumSquares := math.Pow(count*t.StdDev(), 2) + sumSquared/count - if math.IsNaN(sumSquares) { - return 0.0 - } - return sumSquares -} - -func (rep *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Batch, err error) { - snapshot = Batch{ - // coerce timestamps to a stepping fn so that they line up in Librato graphs - MeasureTime: (now.Unix() / rep.intervalSec) * rep.intervalSec, - Source: rep.Source, - } - snapshot.Gauges = make([]Measurement, 0) - snapshot.Counters = make([]Measurement, 0) - histogramGaugeCount := 1 + len(rep.Percentiles) - r.Each(func(name string, metric interface{}) { - if rep.Namespace != "" { - name = fmt.Sprintf("%s.%s", rep.Namespace, name) - } - measurement := Measurement{} - measurement[Period] = rep.Interval.Seconds() - switch m := metric.(type) { - case metrics.Counter: - ms := m.Snapshot() - if ms.Count() > 0 { - measurement[Name] = fmt.Sprintf("%s.%s", name, "count") - measurement[Value] = float64(ms.Count()) - measurement[Attributes] = map[string]interface{}{ - DisplayUnitsLong: Operations, - DisplayUnitsShort: OperationsShort, - DisplayMin: "0", - } - snapshot.Counters = append(snapshot.Counters, measurement) - } - case metrics.CounterFloat64: - if count := m.Snapshot().Count(); count > 0 { - measurement[Name] = fmt.Sprintf("%s.%s", name, "count") - measurement[Value] = count - measurement[Attributes] = map[string]interface{}{ - DisplayUnitsLong: Operations, - DisplayUnitsShort: OperationsShort, - DisplayMin: "0", - } - snapshot.Counters = append(snapshot.Counters, measurement) - } - case metrics.Gauge: - measurement[Name] = name - measurement[Value] = float64(m.Snapshot().Value()) - snapshot.Gauges = append(snapshot.Gauges, measurement) - case metrics.GaugeFloat64: - measurement[Name] = name - measurement[Value] = m.Snapshot().Value() - snapshot.Gauges = append(snapshot.Gauges, measurement) - case metrics.GaugeInfo: - measurement[Name] = name - measurement[Value] = m.Snapshot().Value() - snapshot.Gauges = append(snapshot.Gauges, measurement) - case metrics.Histogram: - ms := m.Snapshot() - if ms.Count() > 0 { - gauges := make([]Measurement, histogramGaugeCount) - measurement[Name] = fmt.Sprintf("%s.%s", name, "hist") - measurement[Count] = uint64(ms.Count()) - measurement[Max] = float64(ms.Max()) - measurement[Min] = float64(ms.Min()) - measurement[Sum] = float64(ms.Sum()) - measurement[SumSquares] = sumSquares(ms.Count(), ms.Mean(), ms.StdDev()) - gauges[0] = measurement - for i, p := range rep.Percentiles { - gauges[i+1] = Measurement{ - Name: fmt.Sprintf("%s.%.2f", measurement[Name], p), - Value: ms.Percentile(p), - Period: measurement[Period], - } - } - snapshot.Gauges = append(snapshot.Gauges, gauges...) - } - case metrics.Meter: - ms := m.Snapshot() - measurement[Name] = name - measurement[Value] = float64(ms.Count()) - snapshot.Counters = append(snapshot.Counters, measurement) - snapshot.Gauges = append(snapshot.Gauges, - Measurement{ - Name: fmt.Sprintf("%s.%s", name, "1min"), - Value: ms.Rate1(), - Period: int64(rep.Interval.Seconds()), - Attributes: map[string]interface{}{ - DisplayUnitsLong: Operations, - DisplayUnitsShort: OperationsShort, - DisplayMin: "0", - }, - }, - Measurement{ - Name: fmt.Sprintf("%s.%s", name, "5min"), - Value: ms.Rate5(), - Period: int64(rep.Interval.Seconds()), - Attributes: map[string]interface{}{ - DisplayUnitsLong: Operations, - DisplayUnitsShort: OperationsShort, - DisplayMin: "0", - }, - }, - Measurement{ - Name: fmt.Sprintf("%s.%s", name, "15min"), - Value: ms.Rate15(), - Period: int64(rep.Interval.Seconds()), - Attributes: map[string]interface{}{ - DisplayUnitsLong: Operations, - DisplayUnitsShort: OperationsShort, - DisplayMin: "0", - }, - }, - ) - case metrics.Timer: - ms := m.Snapshot() - measurement[Name] = name - measurement[Value] = float64(ms.Count()) - snapshot.Counters = append(snapshot.Counters, measurement) - if ms.Count() > 0 { - libratoName := fmt.Sprintf("%s.%s", name, "timer.mean") - gauges := make([]Measurement, histogramGaugeCount) - gauges[0] = Measurement{ - Name: libratoName, - Count: uint64(ms.Count()), - Sum: ms.Mean() * float64(ms.Count()), - Max: float64(ms.Max()), - Min: float64(ms.Min()), - SumSquares: sumSquaresTimer(ms), - Period: int64(rep.Interval.Seconds()), - Attributes: rep.TimerAttributes, - } - for i, p := range rep.Percentiles { - gauges[i+1] = Measurement{ - Name: fmt.Sprintf("%s.timer.%2.0f", name, p*100), - Value: ms.Percentile(p), - Period: int64(rep.Interval.Seconds()), - Attributes: rep.TimerAttributes, - } - } - snapshot.Gauges = append(snapshot.Gauges, gauges...) - snapshot.Gauges = append(snapshot.Gauges, - Measurement{ - Name: fmt.Sprintf("%s.%s", name, "rate.1min"), - Value: ms.Rate1(), - Period: int64(rep.Interval.Seconds()), - Attributes: map[string]interface{}{ - DisplayUnitsLong: Operations, - DisplayUnitsShort: OperationsShort, - DisplayMin: "0", - }, - }, - Measurement{ - Name: fmt.Sprintf("%s.%s", name, "rate.5min"), - Value: ms.Rate5(), - Period: int64(rep.Interval.Seconds()), - Attributes: map[string]interface{}{ - DisplayUnitsLong: Operations, - DisplayUnitsShort: OperationsShort, - DisplayMin: "0", - }, - }, - Measurement{ - Name: fmt.Sprintf("%s.%s", name, "rate.15min"), - Value: ms.Rate15(), - Period: int64(rep.Interval.Seconds()), - Attributes: map[string]interface{}{ - DisplayUnitsLong: Operations, - DisplayUnitsShort: OperationsShort, - DisplayMin: "0", - }, - }, - ) - } - } - }) - return -} From a9cbcde7780b59344dd5392d5df76494d2fc880a Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:14 +0800 Subject: [PATCH 375/479] metrics: fix out of range error message (#29821) --- metrics/sample_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metrics/sample_test.go b/metrics/sample_test.go index 9835ec1c3003..92806c65b2c7 100644 --- a/metrics/sample_test.go +++ b/metrics/sample_test.go @@ -114,7 +114,7 @@ func TestExpDecaySample(t *testing.T) { } for _, v := range values { if v > int64(tc.updates) || v < 0 { - t.Errorf("out of range [0, %d): %v", tc.updates, v) + t.Errorf("out of range [0, %d]: %v", tc.updates, v) } } } @@ -195,7 +195,7 @@ func TestUniformSample(t *testing.T) { } for _, v := range values { if v > 1000 || v < 0 { - t.Errorf("out of range [0, 100): %v\n", v) + t.Errorf("out of range [0, 1000]: %v\n", v) } } } From fb6a26876310df920d4212e787bf520338d9bb96 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:14 +0800 Subject: [PATCH 376/479] metrics: fix flaky test `TestExpDecaySampleNanosecondRegression` (#29832) --- metrics/sample_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/metrics/sample_test.go b/metrics/sample_test.go index 92806c65b2c7..87f085c70454 100644 --- a/metrics/sample_test.go +++ b/metrics/sample_test.go @@ -125,12 +125,12 @@ func TestExpDecaySample(t *testing.T) { // The priority becomes +Inf quickly after starting if this is done, // effectively freezing the set of samples until a rescale step happens. func TestExpDecaySampleNanosecondRegression(t *testing.T) { - sw := NewExpDecaySample(100, 0.99) - for i := 0; i < 100; i++ { + sw := NewExpDecaySample(1000, 0.99) + for i := 0; i < 1000; i++ { sw.Update(10) } time.Sleep(1 * time.Millisecond) - for i := 0; i < 100; i++ { + for i := 0; i < 1000; i++ { sw.Update(20) } s := sw.Snapshot() From 7e784f1d701f9d550bd7b68475a78939992f499c Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:14 +0800 Subject: [PATCH 377/479] metrics: add test for `SampleSnapshot.Sum` (#29831) --- metrics/sample_test.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/metrics/sample_test.go b/metrics/sample_test.go index 87f085c70454..4227b43ef775 100644 --- a/metrics/sample_test.go +++ b/metrics/sample_test.go @@ -103,14 +103,14 @@ func TestExpDecaySample(t *testing.T) { } snap := sample.Snapshot() if have, want := int(snap.Count()), tc.updates; have != want { - t.Errorf("have %d want %d", have, want) + t.Errorf("unexpected count: have %d want %d", have, want) } if have, want := snap.Size(), min(tc.updates, tc.reservoirSize); have != want { - t.Errorf("have %d want %d", have, want) + t.Errorf("unexpected size: have %d want %d", have, want) } values := snap.(*sampleSnapshot).values if have, want := len(values), min(tc.updates, tc.reservoirSize); have != want { - t.Errorf("have %d want %d", have, want) + t.Errorf("unexpected values length: have %d want %d", have, want) } for _, v := range values { if v > int64(tc.updates) || v < 0 { @@ -251,6 +251,9 @@ func benchmarkSample(b *testing.B, s Sample) { } func testExpDecaySampleStatistics(t *testing.T, s SampleSnapshot) { + if sum := s.Sum(); sum != 496598 { + t.Errorf("s.Sum(): 496598 != %v\n", sum) + } if count := s.Count(); count != 10000 { t.Errorf("s.Count(): 10000 != %v\n", count) } From ea355e824bcfd8cddeda60b4d4104be2fe3579f6 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:14 +0800 Subject: [PATCH 378/479] metrics: fix function comment (#29843) --- metrics/debug.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/metrics/debug.go b/metrics/debug.go index de4a2739fe08..9dfee1a86698 100644 --- a/metrics/debug.go +++ b/metrics/debug.go @@ -19,18 +19,18 @@ var ( gcStats debug.GCStats ) -// Capture new values for the Go garbage collector statistics exported in -// debug.GCStats. This is designed to be called as a goroutine. +// CaptureDebugGCStats captures new values for the Go garbage collector statistics +// exported in debug.GCStats. This is designed to be called as a goroutine. func CaptureDebugGCStats(r Registry, d time.Duration) { for range time.Tick(d) { CaptureDebugGCStatsOnce(r) } } -// Capture new values for the Go garbage collector statistics exported in -// debug.GCStats. This is designed to be called in a background goroutine. -// Giving a registry which has not been given to RegisterDebugGCStats will -// panic. +// CaptureDebugGCStatsOnce captures new values for the Go garbage collector +// statistics exported in debug.GCStats. This is designed to be called in +// a background goroutine. Giving a registry which has not been given to +// RegisterDebugGCStats will panic. // // Be careful (but much less so) with this because debug.ReadGCStats calls // the C function runtime·lock(runtime·mheap) which, while not a stop-the-world @@ -50,9 +50,9 @@ func CaptureDebugGCStatsOnce(r Registry) { debugMetrics.GCStats.PauseTotal.Update(int64(gcStats.PauseTotal)) } -// Register metrics for the Go garbage collector statistics exported in -// debug.GCStats. The metrics are named by their fully-qualified Go symbols, -// i.e. debug.GCStats.PauseTotal. +// RegisterDebugGCStats registers metrics for the Go garbage collector statistics +// exported in debug.GCStats. The metrics are named by their fully-qualified Go +// symbols, i.e. debug.GCStats.PauseTotal. func RegisterDebugGCStats(r Registry) { debugMetrics.GCStats.LastGC = NewGauge() debugMetrics.GCStats.NumGC = NewGauge() From 7d82f278941634f99a933ee1431143b72a3e7b61 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:14 +0800 Subject: [PATCH 379/479] metrics: fix issues with benchmarks (#30667) --- metrics/sample_test.go | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/metrics/sample_test.go b/metrics/sample_test.go index 4227b43ef775..c855671ae23c 100644 --- a/metrics/sample_test.go +++ b/metrics/sample_test.go @@ -3,7 +3,6 @@ package metrics import ( "math" "math/rand" - "runtime" "testing" "time" ) @@ -27,6 +26,7 @@ func BenchmarkCompute1000(b *testing.B) { SampleVariance(mean, s) } } + func BenchmarkCompute1000000(b *testing.B) { s := make([]int64, 1000000) var sum int64 @@ -40,28 +40,6 @@ func BenchmarkCompute1000000(b *testing.B) { SampleVariance(mean, s) } } -func BenchmarkCopy1000(b *testing.B) { - s := make([]int64, 1000) - for i := 0; i < len(s); i++ { - s[i] = int64(i) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - sCopy := make([]int64, len(s)) - copy(sCopy, s) - } -} -func BenchmarkCopy1000000(b *testing.B) { - s := make([]int64, 1000000) - for i := 0; i < len(s); i++ { - s[i] = int64(i) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - sCopy := make([]int64, len(s)) - copy(sCopy, s) - } -} func BenchmarkExpDecaySample257(b *testing.B) { benchmarkSample(b, NewExpDecaySample(257, 0.015)) @@ -237,17 +215,9 @@ func TestUniformSampleStatistics(t *testing.T) { } func benchmarkSample(b *testing.B, s Sample) { - var memStats runtime.MemStats - runtime.ReadMemStats(&memStats) - pauseTotalNs := memStats.PauseTotalNs - b.ResetTimer() for i := 0; i < b.N; i++ { s.Update(1) } - b.StopTimer() - runtime.GC() - runtime.ReadMemStats(&memStats) - b.Logf("GC cost: %d ns/op", int(memStats.PauseTotalNs-pauseTotalNs)/b.N) } func testExpDecaySampleStatistics(t *testing.T, s SampleSnapshot) { From 6beee27886d6ccd67d75f1e08896694dcf6af85f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 14:00:14 +0800 Subject: [PATCH 380/479] metrics, cmd/XDC: change init-process of metrics (#30814) This PR modifies how the metrics library handles `Enabled`: previously, the package `init` decided whether to serve real metrics or just dummy-types. This has several drawbacks: - During pkg init, we need to determine whether metrics are enabled or not. So we first hacked in a check if certain geth-specific commandline-flags were enabled. Then we added a similar check for geth-env-vars. Then we almost added a very elaborate check for toml-config-file, plus toml parsing. - Using "real" types and dummy types interchangeably means that everything is hidden behind interfaces. This has a performance penalty, and also it just adds a lot of code. This PR removes the interface stuff, uses concrete types, and allows for the setting of Enabled to happen later. It is still assumed that `metrics.Enable()` is invoked early on. The somewhat 'heavy' operations, such as ticking meters and exp-decay, now checks the enable-flag to prevent resource leak. The change may be large, but it's mostly pretty trivial, and from the last time I gutted the metrics, I ensured that we have fairly good test coverage. --------- Co-authored-by: Felix Lange --- cmd/XDC/chaincmd.go | 11 +- cmd/XDC/config.go | 24 +++ cmd/XDC/main.go | 5 - cmd/utils/flags.go | 103 +++++----- consensus/ethash/ethash.go | 2 +- eth/downloader/downloader.go | 2 +- eth/downloader/queue.go | 4 +- eth/metrics.go | 2 +- ethdb/leveldb/leveldb.go | 24 +-- les/metrics.go | 2 +- metrics/counter.go | 94 ++------- metrics/counter_float64.go | 103 +++------- metrics/counter_float_64_test.go | 58 ++---- metrics/counter_test.go | 38 ++-- metrics/debug.go | 8 +- metrics/ewma.go | 64 +++--- metrics/ewma_test.go | 14 +- metrics/exp/exp.go | 22 +-- metrics/gauge.go | 84 +++----- metrics/gauge_float64.go | 65 ++---- metrics/gauge_info.go | 50 ++--- metrics/graphite.go | 117 ----------- metrics/graphite_test.go | 22 --- metrics/healthcheck.go | 44 +---- metrics/histogram.go | 23 ++- metrics/inactive.go | 48 ----- metrics/influxdb/influxdb.go | 16 +- metrics/influxdb/influxdb_test.go | 2 +- metrics/init_test.go | 2 +- metrics/log.go | 18 +- metrics/meter.go | 156 +++++++-------- metrics/meter_test.go | 12 +- metrics/metrics.go | 57 ++---- metrics/metrics_test.go | 71 ++----- metrics/opentsdb.go | 14 +- metrics/prometheus/collector.go | 22 +-- metrics/prometheus/collector_test.go | 2 +- metrics/registry.go | 35 ++-- metrics/registry_test.go | 18 +- metrics/resetting_sample.go | 2 +- metrics/resetting_timer.go | 90 +++------ metrics/sample.go | 282 +++++++++++++-------------- metrics/sample_test.go | 14 +- metrics/syslog.go | 16 +- metrics/timer.go | 104 ++++------ metrics/writer.go | 16 +- p2p/metrics.go | 2 +- 47 files changed, 674 insertions(+), 1310 deletions(-) delete mode 100644 metrics/graphite.go delete mode 100644 metrics/graphite_test.go delete mode 100644 metrics/inactive.go diff --git a/cmd/XDC/chaincmd.go b/cmd/XDC/chaincmd.go index 3ddcae6f145f..aa050dfd3c29 100644 --- a/cmd/XDC/chaincmd.go +++ b/cmd/XDC/chaincmd.go @@ -35,7 +35,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/eth/downloader" "github.com/XinFinOrg/XDPoSChain/event" "github.com/XinFinOrg/XDPoSChain/log" - "github.com/XinFinOrg/XDPoSChain/metrics" "github.com/urfave/cli/v2" ) @@ -239,14 +238,12 @@ func importChain(ctx *cli.Context) error { if ctx.Args().Len() < 1 { utils.Fatalf("This command requires an argument.") } - // Start metrics export if enabled - utils.SetupMetrics(ctx) - // Start system runtime metrics collection - go metrics.CollectProcessMetrics(3 * time.Second) - - stack, _ := makeFullNode(ctx) + stack, cfg := makeFullNode(ctx) defer stack.Close() + // Start metrics export if enabled + utils.SetupMetrics(&cfg.Metrics) + chain, chainDb := utils.MakeChain(ctx, stack) defer chainDb.Close() diff --git a/cmd/XDC/config.go b/cmd/XDC/config.go index 360fb768d142..ef0dbbb00df1 100644 --- a/cmd/XDC/config.go +++ b/cmd/XDC/config.go @@ -235,6 +235,9 @@ func applyValues(values []string, params *[]string) { func makeFullNode(ctx *cli.Context) (*node.Node, XDCConfig) { stack, cfg := makeConfigNode(ctx) + // Start metrics export if enabled + utils.SetupMetrics(&cfg.Metrics) + // Register XDCX's OrderBook service if requested. // enable in default utils.RegisterXDCXService(stack, &cfg.XDCX) @@ -310,4 +313,25 @@ func applyMetricConfig(ctx *cli.Context, cfg *XDCConfig) { if ctx.IsSet(utils.MetricsInfluxDBOrganizationFlag.Name) { cfg.Metrics.InfluxDBOrganization = ctx.String(utils.MetricsInfluxDBOrganizationFlag.Name) } + // Sanity-check the commandline flags. It is fine if some unused fields is part + // of the toml-config, but we expect the commandline to only contain relevant + // arguments, otherwise it indicates an error. + var ( + enableExport = ctx.Bool(utils.MetricsEnableInfluxDBFlag.Name) + enableExportV2 = ctx.Bool(utils.MetricsEnableInfluxDBV2Flag.Name) + ) + if enableExport || enableExportV2 { + v1FlagIsSet := ctx.IsSet(utils.MetricsInfluxDBUsernameFlag.Name) || + ctx.IsSet(utils.MetricsInfluxDBPasswordFlag.Name) + + v2FlagIsSet := ctx.IsSet(utils.MetricsInfluxDBTokenFlag.Name) || + ctx.IsSet(utils.MetricsInfluxDBOrganizationFlag.Name) || + ctx.IsSet(utils.MetricsInfluxDBBucketFlag.Name) + + if enableExport && v2FlagIsSet { + utils.Fatalf("Flags --influxdb-metrics.organization, --influxdb-metrics.token, --influxdb-metrics.bucket are only available for influxdb-v2") + } else if enableExportV2 && v1FlagIsSet { + utils.Fatalf("Flags --influxdb-metrics.username, --influxdb-metrics.password are only available for influxdb-v1") + } + } } diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go index b11f3b976c63..f3b9bdba139e 100644 --- a/cmd/XDC/main.go +++ b/cmd/XDC/main.go @@ -321,11 +321,6 @@ func startNode(ctx *cli.Context, stack *node.Node, cfg XDCConfig) { if ctx.Bool(utils.LightModeFlag.Name) || ctx.String(utils.SyncModeFlag.Name) == "light" { utils.Fatalf("Light clients do not support staking") } - // Start metrics export if enabled - utils.SetupMetrics(ctx) - - // Start system runtime metrics collection - go metrics.CollectProcessMetrics(3 * time.Second) var ethereum *eth.Ethereum if err := stack.Service(ðereum); err != nil { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index eb8d75f297ce..18ce92a23fc8 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -22,6 +22,7 @@ import ( "fmt" "math" "math/big" + "net" "os" "path/filepath" "runtime" @@ -1532,66 +1533,56 @@ func SetupNetwork(ctx *cli.Context) { params.TargetGasLimit = ctx.Uint64(MinerGasLimitFlag.Name) } -func SetupMetrics(ctx *cli.Context) { - if metrics.Enabled { - log.Info("Enabling metrics collection") - var ( - enableExport = ctx.Bool(MetricsEnableInfluxDBFlag.Name) - enableExportV2 = ctx.Bool(MetricsEnableInfluxDBV2Flag.Name) - ) - - if enableExport || enableExportV2 { - checkExclusive(ctx, MetricsEnableInfluxDBFlag, MetricsEnableInfluxDBV2Flag) - - v1FlagIsSet := ctx.IsSet(MetricsInfluxDBUsernameFlag.Name) || - ctx.IsSet(MetricsInfluxDBPasswordFlag.Name) - - v2FlagIsSet := ctx.IsSet(MetricsInfluxDBTokenFlag.Name) || - ctx.IsSet(MetricsInfluxDBOrganizationFlag.Name) || - ctx.IsSet(MetricsInfluxDBBucketFlag.Name) - - if enableExport && v2FlagIsSet { - Fatalf("Flags --influxdb.metrics.organization, --influxdb.metrics.token, --influxdb.metrics.bucket are only available for influxdb-v2") - } else if enableExportV2 && v1FlagIsSet { - Fatalf("Flags --influxdb.metrics.username, --influxdb.metrics.password are only available for influxdb-v1") - } - } - - var ( - endpoint = ctx.String(MetricsInfluxDBEndpointFlag.Name) - database = ctx.String(MetricsInfluxDBDatabaseFlag.Name) - username = ctx.String(MetricsInfluxDBUsernameFlag.Name) - password = ctx.String(MetricsInfluxDBPasswordFlag.Name) - - token = ctx.String(MetricsInfluxDBTokenFlag.Name) - bucket = ctx.String(MetricsInfluxDBBucketFlag.Name) - organization = ctx.String(MetricsInfluxDBOrganizationFlag.Name) - ) - - if enableExport { - log.Info("Enabling metrics export to InfluxDB") - - tagsMap := SplitTagsFlag(ctx.String(MetricsInfluxDBTagsFlag.Name)) - - go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "xdc.", tagsMap) - } else if enableExportV2 { - log.Info("Enabling metrics export to InfluxDB (v2)") - - tagsMap := SplitTagsFlag(ctx.String(MetricsInfluxDBTagsFlag.Name)) - - go influxdb.InfluxDBV2WithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, token, bucket, organization, "xdc.", tagsMap) - } +// SetupMetrics configures the metrics system. +func SetupMetrics(cfg *metrics.Config) { + if !cfg.Enabled { + return + } + log.Info("Enabling metrics collection") + metrics.Enable() - if ctx.IsSet(MetricsHTTPFlag.Name) { - address := fmt.Sprintf("%s:%d", ctx.String(MetricsHTTPFlag.Name), ctx.Int(MetricsPortFlag.Name)) - log.Info("Enabling stand-alone metrics HTTP endpoint", "address", address) - exp.Setup(address) - } else if ctx.IsSet(MetricsPortFlag.Name) { - log.Warn(fmt.Sprintf("--%s specified without --%s, metrics server will not start.", MetricsPortFlag.Name, MetricsHTTPFlag.Name)) - } + // InfluxDB exporter. + var ( + enableExport = cfg.EnableInfluxDB + enableExportV2 = cfg.EnableInfluxDBV2 + ) + if cfg.EnableInfluxDB && cfg.EnableInfluxDBV2 { + Fatalf("Flags %v can't be used at the same time", strings.Join([]string{MetricsEnableInfluxDBFlag.Name, MetricsEnableInfluxDBV2Flag.Name}, ", ")) } + var ( + endpoint = cfg.InfluxDBEndpoint + database = cfg.InfluxDBDatabase + username = cfg.InfluxDBUsername + password = cfg.InfluxDBPassword + + token = cfg.InfluxDBToken + bucket = cfg.InfluxDBBucket + organization = cfg.InfluxDBOrganization + tagsMap = SplitTagsFlag(cfg.InfluxDBTags) + ) + if enableExport { + log.Info("Enabling metrics export to InfluxDB") + go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "geth.", tagsMap) + } else if enableExportV2 { + tagsMap := SplitTagsFlag(cfg.InfluxDBTags) + log.Info("Enabling metrics export to InfluxDB (v2)") + go influxdb.InfluxDBV2WithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, token, bucket, organization, "geth.", tagsMap) + } + + // Expvar exporter. + if cfg.HTTP != "" { + address := net.JoinHostPort(cfg.HTTP, fmt.Sprintf("%d", cfg.Port)) + log.Info("Enabling stand-alone metrics HTTP endpoint", "address", address) + exp.Setup(address) + } else if cfg.HTTP == "" && cfg.Port != 0 { + log.Warn(fmt.Sprintf("--%s specified without --%s, metrics server will not start.", MetricsPortFlag.Name, MetricsHTTPFlag.Name)) + } + + // Enable system metrics collection. + go metrics.CollectProcessMetrics(3 * time.Second) } +// SplitTagsFlag parses a comma-separated list of k=v metrics tags. func SplitTagsFlag(tagsFlag string) map[string]string { tags := strings.Split(tagsFlag, ",") tagsMap := map[string]string{} diff --git a/consensus/ethash/ethash.go b/consensus/ethash/ethash.go index 98141b29393c..394d495a07e8 100644 --- a/consensus/ethash/ethash.go +++ b/consensus/ethash/ethash.go @@ -401,7 +401,7 @@ type Ethash struct { rand *rand.Rand // Properly seeded random source for nonces threads int // Number of threads to mine on if mining update chan struct{} // Notification channel to update mining parameters - hashrate metrics.Meter // Meter tracking the average hashrate + hashrate *metrics.Meter // Meter tracking the average hashrate // The fields below are hooks for testing shared *Ethash // Shared PoW verifier to avoid cache regeneration diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 1a500142c839..f5fb6d1c5851 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -1586,7 +1586,7 @@ func (d *Downloader) DeliverNodeData(id string, data [][]byte) (err error) { } // deliver injects a new batch of data received from a remote node. -func (d *Downloader) deliver(id string, destCh chan dataPack, packet dataPack, inMeter, dropMeter metrics.Meter) (err error) { +func (d *Downloader) deliver(id string, destCh chan dataPack, packet dataPack, inMeter, dropMeter *metrics.Meter) (err error) { // Update the delivery metrics for both good and failed deliveries inMeter.Mark(int64(packet.Items())) defer func() { diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index 7f55ce733574..b21460c2efd6 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -650,7 +650,7 @@ func (q *queue) ExpireReceipts(timeout time.Duration) map[string]int { // Note, this method expects the queue lock to be already held. The // reason the lock is not obtained in here is because the parameters already need // to access the queue, so they already need a lock anyway. -func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest, taskQueue *prque.Prque, timeoutMeter metrics.Meter) map[string]int { +func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest, taskQueue *prque.Prque, timeoutMeter *metrics.Meter) map[string]int { // Iterate over the expired requests and return each to the queue expiries := make(map[string]int) for id, request := range pendPool { @@ -805,7 +805,7 @@ func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt) (int, // reason the lock is not obtained in here is because the parameters already need // to access the queue, so they already need a lock anyway. func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQueue *prque.Prque, - pendPool map[string]*fetchRequest, donePool map[common.Hash]struct{}, reqTimer metrics.Timer, + pendPool map[string]*fetchRequest, donePool map[common.Hash]struct{}, reqTimer *metrics.Timer, results int, reconstruct func(header *types.Header, index int, result *fetchResult) error) (int, error) { // Short circuit if the data was never requested diff --git a/eth/metrics.go b/eth/metrics.go index 2f3bd6bfb839..59d9258a0625 100644 --- a/eth/metrics.go +++ b/eth/metrics.go @@ -66,7 +66,7 @@ type meteredMsgReadWriter struct { // newMeteredMsgWriter wraps a p2p MsgReadWriter with metering support. If the // metrics system is disabled, this function returns the original object. func newMeteredMsgWriter(rw p2p.MsgReadWriter) p2p.MsgReadWriter { - if !metrics.Enabled { + if !metrics.Enabled() { return rw } return &meteredMsgReadWriter{MsgReadWriter: rw} diff --git a/ethdb/leveldb/leveldb.go b/ethdb/leveldb/leveldb.go index 601ffc94a1df..cd4603982695 100644 --- a/ethdb/leveldb/leveldb.go +++ b/ethdb/leveldb/leveldb.go @@ -62,18 +62,18 @@ type Database struct { fn string // filename for reporting db *leveldb.DB // LevelDB instance - compTimeMeter metrics.Meter // Meter for measuring the total time spent in database compaction - compReadMeter metrics.Meter // Meter for measuring the data read during compaction - compWriteMeter metrics.Meter // Meter for measuring the data written during compaction - writeDelayNMeter metrics.Meter // Meter for measuring the write delay number due to database compaction - writeDelayMeter metrics.Meter // Meter for measuring the write delay duration due to database compaction - diskSizeGauge metrics.Gauge // Gauge for tracking the size of all the levels in the database - diskReadMeter metrics.Meter // Meter for measuring the effective amount of data read - diskWriteMeter metrics.Meter // Meter for measuring the effective amount of data written - memCompGauge metrics.Gauge // Gauge for tracking the number of memory compaction - level0CompGauge metrics.Gauge // Gauge for tracking the number of table compaction in level0 - nonlevel0CompGauge metrics.Gauge // Gauge for tracking the number of table compaction in non0 level - seekCompGauge metrics.Gauge // Gauge for tracking the number of table compaction caused by read opt + compTimeMeter *metrics.Meter // Meter for measuring the total time spent in database compaction + compReadMeter *metrics.Meter // Meter for measuring the data read during compaction + compWriteMeter *metrics.Meter // Meter for measuring the data written during compaction + writeDelayNMeter *metrics.Meter // Meter for measuring the write delay number due to database compaction + writeDelayMeter *metrics.Meter // Meter for measuring the write delay duration due to database compaction + diskSizeGauge *metrics.Gauge // Gauge for tracking the size of all the levels in the database + diskReadMeter *metrics.Meter // Meter for measuring the effective amount of data read + diskWriteMeter *metrics.Meter // Meter for measuring the effective amount of data written + memCompGauge *metrics.Gauge // Gauge for tracking the number of memory compaction + level0CompGauge *metrics.Gauge // Gauge for tracking the number of table compaction in level0 + nonlevel0CompGauge *metrics.Gauge // Gauge for tracking the number of table compaction in non0 level + seekCompGauge *metrics.Gauge // Gauge for tracking the number of table compaction caused by read opt quitLock sync.Mutex // Mutex protecting the quit channel access quitChan chan chan error // Quit channel to stop the metrics collection before closing the database diff --git a/les/metrics.go b/les/metrics.go index 4ba1af2475c5..9215c712eb71 100644 --- a/les/metrics.go +++ b/les/metrics.go @@ -74,7 +74,7 @@ type meteredMsgReadWriter struct { // newMeteredMsgWriter wraps a p2p MsgReadWriter with metering support. If the // metrics system is disabled, this function returns the original object. func newMeteredMsgWriter(rw p2p.MsgReadWriter) p2p.MsgReadWriter { - if !metrics.Enabled { + if !metrics.Enabled() { return rw } return &meteredMsgReadWriter{MsgReadWriter: rw} diff --git a/metrics/counter.go b/metrics/counter.go index dbe8e16a90d2..0f373b0d9289 100644 --- a/metrics/counter.go +++ b/metrics/counter.go @@ -4,109 +4,55 @@ import ( "sync/atomic" ) -type CounterSnapshot interface { - Count() int64 -} - -// Counter hold an int64 value that can be incremented and decremented. -type Counter interface { - Clear() - Dec(int64) - Inc(int64) - Snapshot() CounterSnapshot -} - // GetOrRegisterCounter returns an existing Counter or constructs and registers -// a new StandardCounter. -func GetOrRegisterCounter(name string, r Registry) Counter { - if nil == r { +// a new Counter. +func GetOrRegisterCounter(name string, r Registry) *Counter { + if r == nil { r = DefaultRegistry } - return r.GetOrRegister(name, NewCounter).(Counter) + return r.GetOrRegister(name, NewCounter).(*Counter) } -// GetOrRegisterCounterForced returns an existing Counter or constructs and registers a -// new Counter no matter the global switch is enabled or not. -// Be sure to unregister the counter from the registry once it is of no use to -// allow for garbage collection. -func GetOrRegisterCounterForced(name string, r Registry) Counter { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, NewCounterForced).(Counter) +// NewCounter constructs a new Counter. +func NewCounter() *Counter { + return new(Counter) } -// NewCounter constructs a new StandardCounter. -func NewCounter() Counter { - if !Enabled { - return NilCounter{} - } - return new(StandardCounter) -} - -// NewCounterForced constructs a new StandardCounter and returns it no matter if -// the global switch is enabled or not. -func NewCounterForced() Counter { - return new(StandardCounter) -} - -// NewRegisteredCounter constructs and registers a new StandardCounter. -func NewRegisteredCounter(name string, r Registry) Counter { +// NewRegisteredCounter constructs and registers a new Counter. +func NewRegisteredCounter(name string, r Registry) *Counter { c := NewCounter() - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// NewRegisteredCounterForced constructs and registers a new StandardCounter -// and launches a goroutine no matter the global switch is enabled or not. -// Be sure to unregister the counter from the registry once it is of no use to -// allow for garbage collection. -func NewRegisteredCounterForced(name string, r Registry) Counter { - c := NewCounterForced() - if nil == r { + if r == nil { r = DefaultRegistry } r.Register(name, c) return c } -// counterSnapshot is a read-only copy of another Counter. -type counterSnapshot int64 +// CounterSnapshot is a read-only copy of a Counter. +type CounterSnapshot int64 // Count returns the count at the time the snapshot was taken. -func (c counterSnapshot) Count() int64 { return int64(c) } - -// NilCounter is a no-op Counter. -type NilCounter struct{} +func (c CounterSnapshot) Count() int64 { return int64(c) } -func (NilCounter) Clear() {} -func (NilCounter) Dec(i int64) {} -func (NilCounter) Inc(i int64) {} -func (NilCounter) Snapshot() CounterSnapshot { return (*emptySnapshot)(nil) } - -// StandardCounter is the standard implementation of a Counter and uses the -// sync/atomic package to manage a single int64 value. -type StandardCounter atomic.Int64 +// Counter hold an int64 value that can be incremented and decremented. +type Counter atomic.Int64 // Clear sets the counter to zero. -func (c *StandardCounter) Clear() { +func (c *Counter) Clear() { (*atomic.Int64)(c).Store(0) } // Dec decrements the counter by the given amount. -func (c *StandardCounter) Dec(i int64) { +func (c *Counter) Dec(i int64) { (*atomic.Int64)(c).Add(-i) } // Inc increments the counter by the given amount. -func (c *StandardCounter) Inc(i int64) { +func (c *Counter) Inc(i int64) { (*atomic.Int64)(c).Add(i) } // Snapshot returns a read-only copy of the counter. -func (c *StandardCounter) Snapshot() CounterSnapshot { - return counterSnapshot((*atomic.Int64)(c).Load()) +func (c *Counter) Snapshot() CounterSnapshot { + return CounterSnapshot((*atomic.Int64)(c).Load()) } diff --git a/metrics/counter_float64.go b/metrics/counter_float64.go index 15c81494efb8..91c4215c4df6 100644 --- a/metrics/counter_float64.go +++ b/metrics/counter_float64.go @@ -5,114 +5,57 @@ import ( "sync/atomic" ) -type CounterFloat64Snapshot interface { - Count() float64 -} - -// CounterFloat64 holds a float64 value that can be incremented and decremented. -type CounterFloat64 interface { - Clear() - Dec(float64) - Inc(float64) - Snapshot() CounterFloat64Snapshot -} - -// GetOrRegisterCounterFloat64 returns an existing CounterFloat64 or constructs and registers -// a new StandardCounterFloat64. -func GetOrRegisterCounterFloat64(name string, r Registry) CounterFloat64 { +// GetOrRegisterCounterFloat64 returns an existing *CounterFloat64 or constructs and registers +// a new CounterFloat64. +func GetOrRegisterCounterFloat64(name string, r Registry) *CounterFloat64 { if nil == r { r = DefaultRegistry } - return r.GetOrRegister(name, NewCounterFloat64).(CounterFloat64) -} - -// GetOrRegisterCounterFloat64Forced returns an existing CounterFloat64 or constructs and registers a -// new CounterFloat64 no matter the global switch is enabled or not. -// Be sure to unregister the counter from the registry once it is of no use to -// allow for garbage collection. -func GetOrRegisterCounterFloat64Forced(name string, r Registry) CounterFloat64 { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, NewCounterFloat64Forced).(CounterFloat64) -} - -// NewCounterFloat64 constructs a new StandardCounterFloat64. -func NewCounterFloat64() CounterFloat64 { - if !Enabled { - return NilCounterFloat64{} - } - return &StandardCounterFloat64{} + return r.GetOrRegister(name, NewCounterFloat64).(*CounterFloat64) } -// NewCounterFloat64Forced constructs a new StandardCounterFloat64 and returns it no matter if -// the global switch is enabled or not. -func NewCounterFloat64Forced() CounterFloat64 { - return &StandardCounterFloat64{} +// NewCounterFloat64 constructs a new CounterFloat64. +func NewCounterFloat64() *CounterFloat64 { + return new(CounterFloat64) } -// NewRegisteredCounterFloat64 constructs and registers a new StandardCounterFloat64. -func NewRegisteredCounterFloat64(name string, r Registry) CounterFloat64 { +// NewRegisteredCounterFloat64 constructs and registers a new CounterFloat64. +func NewRegisteredCounterFloat64(name string, r Registry) *CounterFloat64 { c := NewCounterFloat64() - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// NewRegisteredCounterFloat64Forced constructs and registers a new StandardCounterFloat64 -// and launches a goroutine no matter the global switch is enabled or not. -// Be sure to unregister the counter from the registry once it is of no use to -// allow for garbage collection. -func NewRegisteredCounterFloat64Forced(name string, r Registry) CounterFloat64 { - c := NewCounterFloat64Forced() - if nil == r { + if r == nil { r = DefaultRegistry } r.Register(name, c) return c } -// counterFloat64Snapshot is a read-only copy of another CounterFloat64. -type counterFloat64Snapshot float64 +// CounterFloat64Snapshot is a read-only copy of a float64 counter. +type CounterFloat64Snapshot float64 // Count returns the value at the time the snapshot was taken. -func (c counterFloat64Snapshot) Count() float64 { return float64(c) } - -type NilCounterFloat64 struct{} +func (c CounterFloat64Snapshot) Count() float64 { return float64(c) } -func (NilCounterFloat64) Clear() {} -func (NilCounterFloat64) Count() float64 { return 0.0 } -func (NilCounterFloat64) Dec(i float64) {} -func (NilCounterFloat64) Inc(i float64) {} -func (NilCounterFloat64) Snapshot() CounterFloat64Snapshot { return NilCounterFloat64{} } - -// StandardCounterFloat64 is the standard implementation of a CounterFloat64 and uses the -// atomic to manage a single float64 value. -type StandardCounterFloat64 struct { - floatBits atomic.Uint64 -} +// CounterFloat64 holds a float64 value that can be incremented and decremented. +type CounterFloat64 atomic.Uint64 // Clear sets the counter to zero. -func (c *StandardCounterFloat64) Clear() { - c.floatBits.Store(0) +func (c *CounterFloat64) Clear() { + (*atomic.Uint64)(c).Store(0) } // Dec decrements the counter by the given amount. -func (c *StandardCounterFloat64) Dec(v float64) { - atomicAddFloat(&c.floatBits, -v) +func (c *CounterFloat64) Dec(v float64) { + atomicAddFloat((*atomic.Uint64)(c), -v) } // Inc increments the counter by the given amount. -func (c *StandardCounterFloat64) Inc(v float64) { - atomicAddFloat(&c.floatBits, v) +func (c *CounterFloat64) Inc(v float64) { + atomicAddFloat((*atomic.Uint64)(c), v) } // Snapshot returns a read-only copy of the counter. -func (c *StandardCounterFloat64) Snapshot() CounterFloat64Snapshot { - v := math.Float64frombits(c.floatBits.Load()) - return counterFloat64Snapshot(v) +func (c *CounterFloat64) Snapshot() CounterFloat64Snapshot { + return CounterFloat64Snapshot(math.Float64frombits((*atomic.Uint64)(c).Load())) } func atomicAddFloat(fbits *atomic.Uint64, v float64) { diff --git a/metrics/counter_float_64_test.go b/metrics/counter_float_64_test.go index c21bd3307fa1..618cbbbc2b08 100644 --- a/metrics/counter_float_64_test.go +++ b/metrics/counter_float_64_test.go @@ -32,61 +32,35 @@ func BenchmarkCounterFloat64Parallel(b *testing.B) { } } -func TestCounterFloat64Clear(t *testing.T) { +func TestCounterFloat64(t *testing.T) { c := NewCounterFloat64() - c.Inc(1.0) - c.Clear() if count := c.Snapshot().Count(); count != 0 { - t.Errorf("c.Count(): 0 != %v\n", count) + t.Errorf("wrong count: %v", count) } -} - -func TestCounterFloat64Dec1(t *testing.T) { - c := NewCounterFloat64() c.Dec(1.0) if count := c.Snapshot().Count(); count != -1.0 { - t.Errorf("c.Count(): -1.0 != %v\n", count) + t.Errorf("wrong count: %v", count) } -} - -func TestCounterFloat64Dec2(t *testing.T) { - c := NewCounterFloat64() + snapshot := c.Snapshot() c.Dec(2.0) - if count := c.Snapshot().Count(); count != -2.0 { - t.Errorf("c.Count(): -2.0 != %v\n", count) + if count := c.Snapshot().Count(); count != -3.0 { + t.Errorf("wrong count: %v", count) } -} - -func TestCounterFloat64Inc1(t *testing.T) { - c := NewCounterFloat64() c.Inc(1.0) - if count := c.Snapshot().Count(); count != 1.0 { - t.Errorf("c.Count(): 1.0 != %v\n", count) + if count := c.Snapshot().Count(); count != -2.0 { + t.Errorf("wrong count: %v", count) } -} - -func TestCounterFloat64Inc2(t *testing.T) { - c := NewCounterFloat64() c.Inc(2.0) - if count := c.Snapshot().Count(); count != 2.0 { - t.Errorf("c.Count(): 2.0 != %v\n", count) + if count := c.Snapshot().Count(); count != 0.0 { + t.Errorf("wrong count: %v", count) } -} - -func TestCounterFloat64Snapshot(t *testing.T) { - c := NewCounterFloat64() - c.Inc(1.0) - snapshot := c.Snapshot() - c.Inc(1.0) - if count := snapshot.Count(); count != 1.0 { - t.Errorf("c.Count(): 1.0 != %v\n", count) + if count := snapshot.Count(); count != -1.0 { + t.Errorf("snapshot count wrong: %v", count) } -} - -func TestCounterFloat64Zero(t *testing.T) { - c := NewCounterFloat64() - if count := c.Snapshot().Count(); count != 0 { - t.Errorf("c.Count(): 0 != %v\n", count) + c.Inc(1.0) + c.Clear() + if count := c.Snapshot().Count(); count != 0.0 { + t.Errorf("wrong count: %v", count) } } diff --git a/metrics/counter_test.go b/metrics/counter_test.go index 1b15b23f215f..bf0ca6bae44f 100644 --- a/metrics/counter_test.go +++ b/metrics/counter_test.go @@ -19,35 +19,26 @@ func TestCounterClear(t *testing.T) { } } -func TestCounterDec1(t *testing.T) { +func TestCounter(t *testing.T) { c := NewCounter() + if count := c.Snapshot().Count(); count != 0 { + t.Errorf("wrong count: %v", count) + } c.Dec(1) if count := c.Snapshot().Count(); count != -1 { - t.Errorf("c.Count(): -1 != %v\n", count) + t.Errorf("wrong count: %v", count) } -} - -func TestCounterDec2(t *testing.T) { - c := NewCounter() c.Dec(2) - if count := c.Snapshot().Count(); count != -2 { - t.Errorf("c.Count(): -2 != %v\n", count) + if count := c.Snapshot().Count(); count != -3 { + t.Errorf("wrong count: %v", count) } -} - -func TestCounterInc1(t *testing.T) { - c := NewCounter() c.Inc(1) - if count := c.Snapshot().Count(); count != 1 { - t.Errorf("c.Count(): 1 != %v\n", count) + if count := c.Snapshot().Count(); count != -2 { + t.Errorf("wrong count: %v", count) } -} - -func TestCounterInc2(t *testing.T) { - c := NewCounter() c.Inc(2) - if count := c.Snapshot().Count(); count != 2 { - t.Errorf("c.Count(): 2 != %v\n", count) + if count := c.Snapshot().Count(); count != 0 { + t.Errorf("wrong count: %v", count) } } @@ -61,13 +52,6 @@ func TestCounterSnapshot(t *testing.T) { } } -func TestCounterZero(t *testing.T) { - c := NewCounter() - if count := c.Snapshot().Count(); count != 0 { - t.Errorf("c.Count(): 0 != %v\n", count) - } -} - func TestGetOrRegisterCounter(t *testing.T) { r := NewRegistry() NewRegisteredCounter("foo", r).Inc(47) diff --git a/metrics/debug.go b/metrics/debug.go index 9dfee1a86698..5d0d3992f10b 100644 --- a/metrics/debug.go +++ b/metrics/debug.go @@ -8,13 +8,13 @@ import ( var ( debugMetrics struct { GCStats struct { - LastGC Gauge - NumGC Gauge + LastGC *Gauge + NumGC *Gauge Pause Histogram //PauseQuantiles Histogram - PauseTotal Gauge + PauseTotal *Gauge } - ReadGCStats Timer + ReadGCStats *Timer } gcStats debug.GCStats ) diff --git a/metrics/ewma.go b/metrics/ewma.go index 1d7a4f00cf45..194527a79892 100644 --- a/metrics/ewma.go +++ b/metrics/ewma.go @@ -7,56 +7,36 @@ import ( "time" ) -type EWMASnapshot interface { - Rate() float64 -} +// EWMASnapshot is a read-only copy of an EWMA. +type EWMASnapshot float64 -// EWMAs continuously calculate an exponentially-weighted moving average -// based on an outside source of clock ticks. -type EWMA interface { - Snapshot() EWMASnapshot - Tick() - Update(int64) -} +// Rate returns the rate of events per second at the time the snapshot was +// taken. +func (a EWMASnapshot) Rate() float64 { return float64(a) } // NewEWMA constructs a new EWMA with the given alpha. -func NewEWMA(alpha float64) EWMA { - return &StandardEWMA{alpha: alpha} +func NewEWMA(alpha float64) *EWMA { + return &EWMA{alpha: alpha} } // NewEWMA1 constructs a new EWMA for a one-minute moving average. -func NewEWMA1() EWMA { +func NewEWMA1() *EWMA { return NewEWMA(1 - math.Exp(-5.0/60.0/1)) } // NewEWMA5 constructs a new EWMA for a five-minute moving average. -func NewEWMA5() EWMA { +func NewEWMA5() *EWMA { return NewEWMA(1 - math.Exp(-5.0/60.0/5)) } // NewEWMA15 constructs a new EWMA for a fifteen-minute moving average. -func NewEWMA15() EWMA { +func NewEWMA15() *EWMA { return NewEWMA(1 - math.Exp(-5.0/60.0/15)) } -// ewmaSnapshot is a read-only copy of another EWMA. -type ewmaSnapshot float64 - -// Rate returns the rate of events per second at the time the snapshot was -// taken. -func (a ewmaSnapshot) Rate() float64 { return float64(a) } - -// NilEWMA is a no-op EWMA. -type NilEWMA struct{} - -func (NilEWMA) Snapshot() EWMASnapshot { return (*emptySnapshot)(nil) } -func (NilEWMA) Tick() {} -func (NilEWMA) Update(n int64) {} - -// StandardEWMA is the standard implementation of an EWMA and tracks the number -// of uncounted events and processes them on each tick. It uses the -// sync/atomic package to manage uncounted events. -type StandardEWMA struct { +// EWMA continuously calculate an exponentially-weighted moving average +// based on an outside source of clock ticks. +type EWMA struct { uncounted atomic.Int64 alpha float64 rate atomic.Uint64 @@ -65,27 +45,27 @@ type StandardEWMA struct { } // Snapshot returns a read-only copy of the EWMA. -func (a *StandardEWMA) Snapshot() EWMASnapshot { +func (a *EWMA) Snapshot() EWMASnapshot { r := math.Float64frombits(a.rate.Load()) * float64(time.Second) - return ewmaSnapshot(r) + return EWMASnapshot(r) } -// Tick ticks the clock to update the moving average. It assumes it is called +// tick ticks the clock to update the moving average. It assumes it is called // every five seconds. -func (a *StandardEWMA) Tick() { +func (a *EWMA) tick() { // Optimization to avoid mutex locking in the hot-path. if a.init.Load() { a.updateRate(a.fetchInstantRate()) return } - // Slow-path: this is only needed on the first Tick() and preserves transactional updating + // Slow-path: this is only needed on the first tick() and preserves transactional updating // of init and rate in the else block. The first conditional is needed below because // a different thread could have set a.init = 1 between the time of the first atomic load and when // the lock was acquired. a.mutex.Lock() if a.init.Load() { // The fetchInstantRate() uses atomic loading, which is unnecessary in this critical section - // but again, this section is only invoked on the first successful Tick() operation. + // but again, this section is only invoked on the first successful tick() operation. a.updateRate(a.fetchInstantRate()) } else { a.init.Store(true) @@ -94,18 +74,18 @@ func (a *StandardEWMA) Tick() { a.mutex.Unlock() } -func (a *StandardEWMA) fetchInstantRate() float64 { +func (a *EWMA) fetchInstantRate() float64 { count := a.uncounted.Swap(0) return float64(count) / float64(5*time.Second) } -func (a *StandardEWMA) updateRate(instantRate float64) { +func (a *EWMA) updateRate(instantRate float64) { currentRate := math.Float64frombits(a.rate.Load()) currentRate += a.alpha * (instantRate - currentRate) a.rate.Store(math.Float64bits(currentRate)) } // Update adds n uncounted events. -func (a *StandardEWMA) Update(n int64) { +func (a *EWMA) Update(n int64) { a.uncounted.Add(n) } diff --git a/metrics/ewma_test.go b/metrics/ewma_test.go index 9a91b43db81a..4b9bde3a4b37 100644 --- a/metrics/ewma_test.go +++ b/metrics/ewma_test.go @@ -12,7 +12,7 @@ func BenchmarkEWMA(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { a.Update(1) - a.Tick() + a.tick() } } @@ -23,7 +23,7 @@ func BenchmarkEWMAParallel(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { a.Update(1) - a.Tick() + a.tick() } }) } @@ -31,7 +31,7 @@ func BenchmarkEWMAParallel(b *testing.B) { func TestEWMA1(t *testing.T) { a := NewEWMA1() a.Update(3) - a.Tick() + a.tick() for i, want := range []float64{0.6, 0.22072766470286553, 0.08120116994196772, 0.029872241020718428, 0.01098938333324054, 0.004042768199451294, 0.0014872513059998212, @@ -49,7 +49,7 @@ func TestEWMA1(t *testing.T) { func TestEWMA5(t *testing.T) { a := NewEWMA5() a.Update(3) - a.Tick() + a.tick() for i, want := range []float64{ 0.6, 0.49123845184678905, 0.4021920276213837, 0.32928698165641596, 0.269597378470333, 0.2207276647028654, 0.18071652714732128, @@ -67,7 +67,7 @@ func TestEWMA5(t *testing.T) { func TestEWMA15(t *testing.T) { a := NewEWMA15() a.Update(3) - a.Tick() + a.tick() for i, want := range []float64{ 0.6, 0.5613041910189706, 0.5251039914257684, 0.4912384518467888184678905, 0.459557003018789, 0.4299187863442732, 0.4021920276213831, @@ -82,8 +82,8 @@ func TestEWMA15(t *testing.T) { } } -func elapseMinute(a EWMA) { +func elapseMinute(a *EWMA) { for i := 0; i < 12; i++ { - a.Tick() + a.tick() } } diff --git a/metrics/exp/exp.go b/metrics/exp/exp.go index e7f54d1320fa..149176989d9f 100644 --- a/metrics/exp/exp.go +++ b/metrics/exp/exp.go @@ -146,7 +146,7 @@ func (exp *exp) publishHistogram(name string, metric metrics.Histogram) { exp.getFloat(name + ".999-percentile").Set(ps[4]) } -func (exp *exp) publishMeter(name string, metric metrics.Meter) { +func (exp *exp) publishMeter(name string, metric *metrics.Meter) { m := metric.Snapshot() exp.getInt(name + ".count").Set(m.Count()) exp.getFloat(name + ".one-minute").Set(m.Rate1()) @@ -155,7 +155,7 @@ func (exp *exp) publishMeter(name string, metric metrics.Meter) { exp.getFloat(name + ".mean").Set(m.RateMean()) } -func (exp *exp) publishTimer(name string, metric metrics.Timer) { +func (exp *exp) publishTimer(name string, metric *metrics.Timer) { t := metric.Snapshot() ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) exp.getInt(name + ".count").Set(t.Count()) @@ -174,7 +174,7 @@ func (exp *exp) publishTimer(name string, metric metrics.Timer) { exp.getFloat(name + ".mean-rate").Set(t.RateMean()) } -func (exp *exp) publishResettingTimer(name string, metric metrics.ResettingTimer) { +func (exp *exp) publishResettingTimer(name string, metric *metrics.ResettingTimer) { t := metric.Snapshot() ps := t.Percentiles([]float64{0.50, 0.75, 0.95, 0.99}) exp.getInt(name + ".count").Set(int64(t.Count())) @@ -188,23 +188,23 @@ func (exp *exp) publishResettingTimer(name string, metric metrics.ResettingTimer func (exp *exp) syncToExpvar() { exp.registry.Each(func(name string, i interface{}) { switch i := i.(type) { - case metrics.Counter: + case *metrics.Counter: exp.publishCounter(name, i.Snapshot()) - case metrics.CounterFloat64: + case *metrics.CounterFloat64: exp.publishCounterFloat64(name, i.Snapshot()) - case metrics.Gauge: + case *metrics.Gauge: exp.publishGauge(name, i.Snapshot()) - case metrics.GaugeFloat64: + case *metrics.GaugeFloat64: exp.publishGaugeFloat64(name, i.Snapshot()) - case metrics.GaugeInfo: + case *metrics.GaugeInfo: exp.publishGaugeInfo(name, i.Snapshot()) case metrics.Histogram: exp.publishHistogram(name, i) - case metrics.Meter: + case *metrics.Meter: exp.publishMeter(name, i) - case metrics.Timer: + case *metrics.Timer: exp.publishTimer(name, i) - case metrics.ResettingTimer: + case *metrics.ResettingTimer: exp.publishResettingTimer(name, i) default: panic(fmt.Sprintf("unsupported type for '%s': %T", name, i)) diff --git a/metrics/gauge.go b/metrics/gauge.go index 6d10bbf85660..ba7843e03b27 100644 --- a/metrics/gauge.go +++ b/metrics/gauge.go @@ -2,97 +2,69 @@ package metrics import "sync/atomic" -// GaugeSnapshot contains a readonly int64. -type GaugeSnapshot interface { - Value() int64 -} +// GaugeSnapshot is a read-only copy of a Gauge. +type GaugeSnapshot int64 -// Gauge holds an int64 value that can be set arbitrarily. -type Gauge interface { - Snapshot() GaugeSnapshot - Update(int64) - UpdateIfGt(int64) - Dec(int64) - Inc(int64) -} +// Value returns the value at the time the snapshot was taken. +func (g GaugeSnapshot) Value() int64 { return int64(g) } // GetOrRegisterGauge returns an existing Gauge or constructs and registers a -// new StandardGauge. -func GetOrRegisterGauge(name string, r Registry) Gauge { - if nil == r { +// new Gauge. +func GetOrRegisterGauge(name string, r Registry) *Gauge { + if r == nil { r = DefaultRegistry } - return r.GetOrRegister(name, NewGauge).(Gauge) + return r.GetOrRegister(name, NewGauge).(*Gauge) } -// NewGauge constructs a new StandardGauge. -func NewGauge() Gauge { - if !Enabled { - return NilGauge{} - } - return &StandardGauge{} +// NewGauge constructs a new Gauge. +func NewGauge() *Gauge { + return &Gauge{} } -// NewRegisteredGauge constructs and registers a new StandardGauge. -func NewRegisteredGauge(name string, r Registry) Gauge { +// NewRegisteredGauge constructs and registers a new Gauge. +func NewRegisteredGauge(name string, r Registry) *Gauge { c := NewGauge() - if nil == r { + if r == nil { r = DefaultRegistry } r.Register(name, c) return c } -// gaugeSnapshot is a read-only copy of another Gauge. -type gaugeSnapshot int64 - -// Value returns the value at the time the snapshot was taken. -func (g gaugeSnapshot) Value() int64 { return int64(g) } - -// NilGauge is a no-op Gauge. -type NilGauge struct{} - -func (NilGauge) Snapshot() GaugeSnapshot { return (*emptySnapshot)(nil) } -func (NilGauge) Update(v int64) {} -func (NilGauge) UpdateIfGt(v int64) {} -func (NilGauge) Dec(i int64) {} -func (NilGauge) Inc(i int64) {} - -// StandardGauge is the standard implementation of a Gauge and uses the -// sync/atomic package to manage a single int64 value. -type StandardGauge struct { - value atomic.Int64 -} +// Gauge holds an int64 value that can be set arbitrarily. +type Gauge atomic.Int64 // Snapshot returns a read-only copy of the gauge. -func (g *StandardGauge) Snapshot() GaugeSnapshot { - return gaugeSnapshot(g.value.Load()) +func (g *Gauge) Snapshot() GaugeSnapshot { + return GaugeSnapshot((*atomic.Int64)(g).Load()) } // Update updates the gauge's value. -func (g *StandardGauge) Update(v int64) { - g.value.Store(v) +func (g *Gauge) Update(v int64) { + (*atomic.Int64)(g).Store(v) } // UpdateIfGt updates the gauge's value if v is larger then the current value. -func (g *StandardGauge) UpdateIfGt(v int64) { +func (g *Gauge) UpdateIfGt(v int64) { + value := (*atomic.Int64)(g) for { - exist := g.value.Load() + exist := value.Load() if exist >= v { break } - if g.value.CompareAndSwap(exist, v) { + if value.CompareAndSwap(exist, v) { break } } } // Dec decrements the gauge's current value by the given amount. -func (g *StandardGauge) Dec(i int64) { - g.value.Add(-i) +func (g *Gauge) Dec(i int64) { + (*atomic.Int64)(g).Add(-i) } // Inc increments the gauge's current value by the given amount. -func (g *StandardGauge) Inc(i int64) { - g.value.Add(i) +func (g *Gauge) Inc(i int64) { + (*atomic.Int64)(g).Add(i) } diff --git a/metrics/gauge_float64.go b/metrics/gauge_float64.go index c1c3c6b6e6f0..05b401ef9cb7 100644 --- a/metrics/gauge_float64.go +++ b/metrics/gauge_float64.go @@ -5,35 +5,28 @@ import ( "sync/atomic" ) -type GaugeFloat64Snapshot interface { - Value() float64 -} - -// GaugeFloat64 hold a float64 value that can be set arbitrarily. -type GaugeFloat64 interface { - Snapshot() GaugeFloat64Snapshot - Update(float64) -} - // GetOrRegisterGaugeFloat64 returns an existing GaugeFloat64 or constructs and registers a -// new StandardGaugeFloat64. -func GetOrRegisterGaugeFloat64(name string, r Registry) GaugeFloat64 { +// new GaugeFloat64. +func GetOrRegisterGaugeFloat64(name string, r Registry) *GaugeFloat64 { if nil == r { r = DefaultRegistry } - return r.GetOrRegister(name, NewGaugeFloat64()).(GaugeFloat64) + return r.GetOrRegister(name, NewGaugeFloat64()).(*GaugeFloat64) } -// NewGaugeFloat64 constructs a new StandardGaugeFloat64. -func NewGaugeFloat64() GaugeFloat64 { - if !Enabled { - return NilGaugeFloat64{} - } - return &StandardGaugeFloat64{} +// GaugeFloat64Snapshot is a read-only copy of a GaugeFloat64. +type GaugeFloat64Snapshot float64 + +// Value returns the value at the time the snapshot was taken. +func (g GaugeFloat64Snapshot) Value() float64 { return float64(g) } + +// NewGaugeFloat64 constructs a new GaugeFloat64. +func NewGaugeFloat64() *GaugeFloat64 { + return new(GaugeFloat64) } -// NewRegisteredGaugeFloat64 constructs and registers a new StandardGaugeFloat64. -func NewRegisteredGaugeFloat64(name string, r Registry) GaugeFloat64 { +// NewRegisteredGaugeFloat64 constructs and registers a new GaugeFloat64. +func NewRegisteredGaugeFloat64(name string, r Registry) *GaugeFloat64 { c := NewGaugeFloat64() if nil == r { r = DefaultRegistry @@ -42,32 +35,16 @@ func NewRegisteredGaugeFloat64(name string, r Registry) GaugeFloat64 { return c } -// gaugeFloat64Snapshot is a read-only copy of another GaugeFloat64. -type gaugeFloat64Snapshot float64 - -// Value returns the value at the time the snapshot was taken. -func (g gaugeFloat64Snapshot) Value() float64 { return float64(g) } - -// NilGaugeFloat64 is a no-op Gauge. -type NilGaugeFloat64 struct{} - -func (NilGaugeFloat64) Snapshot() GaugeFloat64Snapshot { return NilGaugeFloat64{} } -func (NilGaugeFloat64) Update(v float64) {} -func (NilGaugeFloat64) Value() float64 { return 0.0 } - -// StandardGaugeFloat64 is the standard implementation of a GaugeFloat64 and uses -// atomic to manage a single float64 value. -type StandardGaugeFloat64 struct { - floatBits atomic.Uint64 -} +// GaugeFloat64 hold a float64 value that can be set arbitrarily. +type GaugeFloat64 atomic.Uint64 // Snapshot returns a read-only copy of the gauge. -func (g *StandardGaugeFloat64) Snapshot() GaugeFloat64Snapshot { - v := math.Float64frombits(g.floatBits.Load()) - return gaugeFloat64Snapshot(v) +func (g *GaugeFloat64) Snapshot() GaugeFloat64Snapshot { + v := math.Float64frombits((*atomic.Uint64)(g).Load()) + return GaugeFloat64Snapshot(v) } // Update updates the gauge's value. -func (g *StandardGaugeFloat64) Update(v float64) { - g.floatBits.Store(math.Float64bits(v)) +func (g *GaugeFloat64) Update(v float64) { + (*atomic.Uint64)(g).Store(math.Float64bits(v)) } diff --git a/metrics/gauge_info.go b/metrics/gauge_info.go index 0010edc3249d..2f78455649e4 100644 --- a/metrics/gauge_info.go +++ b/metrics/gauge_info.go @@ -5,16 +5,6 @@ import ( "sync" ) -type GaugeInfoSnapshot interface { - Value() GaugeInfoValue -} - -// GaugeInfo holds a GaugeInfoValue value that can be set arbitrarily. -type GaugeInfo interface { - Update(GaugeInfoValue) - Snapshot() GaugeInfoSnapshot -} - // GaugeInfoValue is a mapping of keys to values type GaugeInfoValue map[string]string @@ -24,26 +14,23 @@ func (val GaugeInfoValue) String() string { } // GetOrRegisterGaugeInfo returns an existing GaugeInfo or constructs and registers a -// new StandardGaugeInfo. -func GetOrRegisterGaugeInfo(name string, r Registry) GaugeInfo { +// new GaugeInfo. +func GetOrRegisterGaugeInfo(name string, r Registry) *GaugeInfo { if nil == r { r = DefaultRegistry } - return r.GetOrRegister(name, NewGaugeInfo()).(GaugeInfo) + return r.GetOrRegister(name, NewGaugeInfo()).(*GaugeInfo) } -// NewGaugeInfo constructs a new StandardGaugeInfo. -func NewGaugeInfo() GaugeInfo { - if !Enabled { - return NilGaugeInfo{} - } - return &StandardGaugeInfo{ +// NewGaugeInfo constructs a new GaugeInfo. +func NewGaugeInfo() *GaugeInfo { + return &GaugeInfo{ value: GaugeInfoValue{}, } } -// NewRegisteredGaugeInfo constructs and registers a new StandardGaugeInfo. -func NewRegisteredGaugeInfo(name string, r Registry) GaugeInfo { +// NewRegisteredGaugeInfo constructs and registers a new GaugeInfo. +func NewRegisteredGaugeInfo(name string, r Registry) *GaugeInfo { c := NewGaugeInfo() if nil == r { r = DefaultRegistry @@ -53,31 +40,24 @@ func NewRegisteredGaugeInfo(name string, r Registry) GaugeInfo { } // gaugeInfoSnapshot is a read-only copy of another GaugeInfo. -type gaugeInfoSnapshot GaugeInfoValue +type GaugeInfoSnapshot GaugeInfoValue // Value returns the value at the time the snapshot was taken. -func (g gaugeInfoSnapshot) Value() GaugeInfoValue { return GaugeInfoValue(g) } - -type NilGaugeInfo struct{} - -func (NilGaugeInfo) Snapshot() GaugeInfoSnapshot { return NilGaugeInfo{} } -func (NilGaugeInfo) Update(v GaugeInfoValue) {} -func (NilGaugeInfo) Value() GaugeInfoValue { return GaugeInfoValue{} } +func (g GaugeInfoSnapshot) Value() GaugeInfoValue { return GaugeInfoValue(g) } -// StandardGaugeInfo is the standard implementation of a GaugeInfo and uses -// sync.Mutex to manage a single string value. -type StandardGaugeInfo struct { +// GaugeInfo maintains a set of key/value mappings. +type GaugeInfo struct { mutex sync.Mutex value GaugeInfoValue } // Snapshot returns a read-only copy of the gauge. -func (g *StandardGaugeInfo) Snapshot() GaugeInfoSnapshot { - return gaugeInfoSnapshot(g.value) +func (g *GaugeInfo) Snapshot() GaugeInfoSnapshot { + return GaugeInfoSnapshot(g.value) } // Update updates the gauge's value. -func (g *StandardGaugeInfo) Update(v GaugeInfoValue) { +func (g *GaugeInfo) Update(v GaugeInfoValue) { g.mutex.Lock() defer g.mutex.Unlock() g.value = v diff --git a/metrics/graphite.go b/metrics/graphite.go deleted file mode 100644 index aba752e0ed5e..000000000000 --- a/metrics/graphite.go +++ /dev/null @@ -1,117 +0,0 @@ -package metrics - -import ( - "bufio" - "fmt" - "log" - "net" - "strconv" - "strings" - "time" -) - -// GraphiteConfig provides a container with configuration parameters for -// the Graphite exporter -type GraphiteConfig struct { - Addr *net.TCPAddr // Network address to connect to - Registry Registry // Registry to be exported - FlushInterval time.Duration // Flush interval - DurationUnit time.Duration // Time conversion unit for durations - Prefix string // Prefix to be prepended to metric names - Percentiles []float64 // Percentiles to export from timers and histograms -} - -// Graphite is a blocking exporter function which reports metrics in r -// to a graphite server located at addr, flushing them every d duration -// and prepending metric names with prefix. -func Graphite(r Registry, d time.Duration, prefix string, addr *net.TCPAddr) { - GraphiteWithConfig(GraphiteConfig{ - Addr: addr, - Registry: r, - FlushInterval: d, - DurationUnit: time.Nanosecond, - Prefix: prefix, - Percentiles: []float64{0.5, 0.75, 0.95, 0.99, 0.999}, - }) -} - -// GraphiteWithConfig is a blocking exporter function just like Graphite, -// but it takes a GraphiteConfig instead. -func GraphiteWithConfig(c GraphiteConfig) { - log.Printf("WARNING: This go-metrics client has been DEPRECATED! It has been moved to https://github.com/cyberdelia/go-metrics-graphite and will be removed from rcrowley/go-metrics on August 12th 2015") - for range time.Tick(c.FlushInterval) { - if err := graphite(&c); nil != err { - log.Println(err) - } - } -} - -// GraphiteOnce performs a single submission to Graphite, returning a -// non-nil error on failed connections. This can be used in a loop -// similar to GraphiteWithConfig for custom error handling. -func GraphiteOnce(c GraphiteConfig) error { - log.Printf("WARNING: This go-metrics client has been DEPRECATED! It has been moved to https://github.com/cyberdelia/go-metrics-graphite and will be removed from rcrowley/go-metrics on August 12th 2015") - return graphite(&c) -} - -func graphite(c *GraphiteConfig) error { - now := time.Now().Unix() - du := float64(c.DurationUnit) - conn, err := net.DialTCP("tcp", nil, c.Addr) - if nil != err { - return err - } - defer conn.Close() - w := bufio.NewWriter(conn) - c.Registry.Each(func(name string, i interface{}) { - switch metric := i.(type) { - case Counter: - fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, metric.Snapshot().Count(), now) - case CounterFloat64: - fmt.Fprintf(w, "%s.%s.count %f %d\n", c.Prefix, name, metric.Snapshot().Count(), now) - case Gauge: - fmt.Fprintf(w, "%s.%s.value %d %d\n", c.Prefix, name, metric.Snapshot().Value(), now) - case GaugeFloat64: - fmt.Fprintf(w, "%s.%s.value %f %d\n", c.Prefix, name, metric.Snapshot().Value(), now) - case GaugeInfo: - fmt.Fprintf(w, "%s.%s.value %s %d\n", c.Prefix, name, metric.Snapshot().Value().String(), now) - case Histogram: - h := metric.Snapshot() - ps := h.Percentiles(c.Percentiles) - fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, h.Count(), now) - fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, h.Min(), now) - fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, h.Max(), now) - fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, h.Mean(), now) - fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, h.StdDev(), now) - for psIdx, psKey := range c.Percentiles { - key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1) - fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now) - } - case Meter: - m := metric.Snapshot() - fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, m.Count(), now) - fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, m.Rate1(), now) - fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, m.Rate5(), now) - fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, m.Rate15(), now) - fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, m.RateMean(), now) - case Timer: - t := metric.Snapshot() - ps := t.Percentiles(c.Percentiles) - fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, t.Count(), now) - fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, t.Min()/int64(du), now) - fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, t.Max()/int64(du), now) - fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, t.Mean()/du, now) - fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, t.StdDev()/du, now) - for psIdx, psKey := range c.Percentiles { - key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1) - fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now) - } - fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, t.Rate1(), now) - fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, t.Rate5(), now) - fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, t.Rate15(), now) - fmt.Fprintf(w, "%s.%s.mean-rate %.2f %d\n", c.Prefix, name, t.RateMean(), now) - } - w.Flush() - }) - return nil -} diff --git a/metrics/graphite_test.go b/metrics/graphite_test.go deleted file mode 100644 index c797c781df6f..000000000000 --- a/metrics/graphite_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package metrics - -import ( - "net" - "time" -) - -func ExampleGraphite() { - addr, _ := net.ResolveTCPAddr("net", ":2003") - go Graphite(DefaultRegistry, 1*time.Second, "some.prefix", addr) -} - -func ExampleGraphiteWithConfig() { - addr, _ := net.ResolveTCPAddr("net", ":2003") - go GraphiteWithConfig(GraphiteConfig{ - Addr: addr, - Registry: DefaultRegistry, - FlushInterval: 1 * time.Second, - DurationUnit: time.Millisecond, - Percentiles: []float64{0.5, 0.75, 0.99, 0.999}, - }) -} diff --git a/metrics/healthcheck.go b/metrics/healthcheck.go index adcd15ab581a..435e5e0bf93d 100644 --- a/metrics/healthcheck.go +++ b/metrics/healthcheck.go @@ -1,61 +1,35 @@ package metrics -// Healthcheck holds an error value describing an arbitrary up/down status. -type Healthcheck interface { - Check() - Error() error - Healthy() - Unhealthy(error) -} - // NewHealthcheck constructs a new Healthcheck which will use the given // function to update its status. -func NewHealthcheck(f func(Healthcheck)) Healthcheck { - if !Enabled { - return NilHealthcheck{} - } - return &StandardHealthcheck{nil, f} +func NewHealthcheck(f func(*Healthcheck)) *Healthcheck { + return &Healthcheck{nil, f} } -// NilHealthcheck is a no-op. -type NilHealthcheck struct{} - -// Check is a no-op. -func (NilHealthcheck) Check() {} - -// Error is a no-op. -func (NilHealthcheck) Error() error { return nil } - -// Healthy is a no-op. -func (NilHealthcheck) Healthy() {} - -// Unhealthy is a no-op. -func (NilHealthcheck) Unhealthy(error) {} - -// StandardHealthcheck is the standard implementation of a Healthcheck and +// Healthcheck is the standard implementation of a Healthcheck and // stores the status and a function to call to update the status. -type StandardHealthcheck struct { +type Healthcheck struct { err error - f func(Healthcheck) + f func(*Healthcheck) } // Check runs the healthcheck function to update the healthcheck's status. -func (h *StandardHealthcheck) Check() { +func (h *Healthcheck) Check() { h.f(h) } // Error returns the healthcheck's status, which will be nil if it is healthy. -func (h *StandardHealthcheck) Error() error { +func (h *Healthcheck) Error() error { return h.err } // Healthy marks the healthcheck as healthy. -func (h *StandardHealthcheck) Healthy() { +func (h *Healthcheck) Healthy() { h.err = nil } // Unhealthy marks the healthcheck as unhealthy. The error is stored and // may be retrieved by the Error method. -func (h *StandardHealthcheck) Unhealthy(err error) { +func (h *Healthcheck) Unhealthy(err error) { h.err = err } diff --git a/metrics/histogram.go b/metrics/histogram.go index 10259a246377..7c27bcc92880 100644 --- a/metrics/histogram.go +++ b/metrics/histogram.go @@ -1,7 +1,16 @@ package metrics type HistogramSnapshot interface { - SampleSnapshot + Count() int64 + Max() int64 + Mean() float64 + Min() int64 + Percentile(float64) float64 + Percentiles([]float64) []float64 + Size() int + StdDev() float64 + Sum() int64 + Variance() float64 } // Histogram calculates distribution statistics from a series of int64 values. @@ -31,10 +40,7 @@ func GetOrRegisterHistogramLazy(name string, r Registry, s func() Sample) Histog // NewHistogram constructs a new StandardHistogram from a Sample. func NewHistogram(s Sample) Histogram { - if !Enabled { - return NilHistogram{} - } - return &StandardHistogram{sample: s} + return &StandardHistogram{s} } // NewRegisteredHistogram constructs and registers a new StandardHistogram from @@ -48,13 +54,6 @@ func NewRegisteredHistogram(name string, r Registry, s Sample) Histogram { return c } -// NilHistogram is a no-op Histogram. -type NilHistogram struct{} - -func (NilHistogram) Clear() {} -func (NilHistogram) Snapshot() HistogramSnapshot { return (*emptySnapshot)(nil) } -func (NilHistogram) Update(v int64) {} - // StandardHistogram is the standard implementation of a Histogram and uses a // Sample to bound its memory use. type StandardHistogram struct { diff --git a/metrics/inactive.go b/metrics/inactive.go deleted file mode 100644 index 1f47f0210af3..000000000000 --- a/metrics/inactive.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2023 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package metrics - -// compile-time checks that interfaces are implemented. -var ( - _ SampleSnapshot = (*emptySnapshot)(nil) - _ HistogramSnapshot = (*emptySnapshot)(nil) - _ CounterSnapshot = (*emptySnapshot)(nil) - _ GaugeSnapshot = (*emptySnapshot)(nil) - _ MeterSnapshot = (*emptySnapshot)(nil) - _ EWMASnapshot = (*emptySnapshot)(nil) - _ TimerSnapshot = (*emptySnapshot)(nil) -) - -type emptySnapshot struct{} - -func (*emptySnapshot) Count() int64 { return 0 } -func (*emptySnapshot) Max() int64 { return 0 } -func (*emptySnapshot) Mean() float64 { return 0.0 } -func (*emptySnapshot) Min() int64 { return 0 } -func (*emptySnapshot) Percentile(p float64) float64 { return 0.0 } -func (*emptySnapshot) Percentiles(ps []float64) []float64 { return make([]float64, len(ps)) } -func (*emptySnapshot) Size() int { return 0 } -func (*emptySnapshot) StdDev() float64 { return 0.0 } -func (*emptySnapshot) Sum() int64 { return 0 } -func (*emptySnapshot) Values() []int64 { return []int64{} } -func (*emptySnapshot) Variance() float64 { return 0.0 } -func (*emptySnapshot) Value() int64 { return 0 } -func (*emptySnapshot) Rate() float64 { return 0.0 } -func (*emptySnapshot) Rate1() float64 { return 0.0 } -func (*emptySnapshot) Rate5() float64 { return 0.0 } -func (*emptySnapshot) Rate15() float64 { return 0.0 } -func (*emptySnapshot) RateMean() float64 { return 0.0 } diff --git a/metrics/influxdb/influxdb.go b/metrics/influxdb/influxdb.go index 8cfa081c1421..37a6a3808e3b 100644 --- a/metrics/influxdb/influxdb.go +++ b/metrics/influxdb/influxdb.go @@ -8,31 +8,31 @@ import ( func readMeter(namespace, name string, i interface{}) (string, map[string]interface{}) { switch metric := i.(type) { - case metrics.Counter: + case *metrics.Counter: measurement := fmt.Sprintf("%s%s.count", namespace, name) fields := map[string]interface{}{ "value": metric.Snapshot().Count(), } return measurement, fields - case metrics.CounterFloat64: + case *metrics.CounterFloat64: measurement := fmt.Sprintf("%s%s.count", namespace, name) fields := map[string]interface{}{ "value": metric.Snapshot().Count(), } return measurement, fields - case metrics.Gauge: + case *metrics.Gauge: measurement := fmt.Sprintf("%s%s.gauge", namespace, name) fields := map[string]interface{}{ "value": metric.Snapshot().Value(), } return measurement, fields - case metrics.GaugeFloat64: + case *metrics.GaugeFloat64: measurement := fmt.Sprintf("%s%s.gauge", namespace, name) fields := map[string]interface{}{ "value": metric.Snapshot().Value(), } return measurement, fields - case metrics.GaugeInfo: + case *metrics.GaugeInfo: ms := metric.Snapshot() measurement := fmt.Sprintf("%s%s.gauge", namespace, name) fields := map[string]interface{}{ @@ -62,7 +62,7 @@ func readMeter(namespace, name string, i interface{}) (string, map[string]interf "p9999": ps[6], } return measurement, fields - case metrics.Meter: + case *metrics.Meter: ms := metric.Snapshot() measurement := fmt.Sprintf("%s%s.meter", namespace, name) fields := map[string]interface{}{ @@ -73,7 +73,7 @@ func readMeter(namespace, name string, i interface{}) (string, map[string]interf "mean": ms.RateMean(), } return measurement, fields - case metrics.Timer: + case *metrics.Timer: ms := metric.Snapshot() ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) @@ -97,7 +97,7 @@ func readMeter(namespace, name string, i interface{}) (string, map[string]interf "meanrate": ms.RateMean(), } return measurement, fields - case metrics.ResettingTimer: + case *metrics.ResettingTimer: ms := metric.Snapshot() if ms.Count() == 0 { break diff --git a/metrics/influxdb/influxdb_test.go b/metrics/influxdb/influxdb_test.go index aa47f15e1825..d7e47fda0e08 100644 --- a/metrics/influxdb/influxdb_test.go +++ b/metrics/influxdb/influxdb_test.go @@ -33,7 +33,7 @@ import ( ) func TestMain(m *testing.M) { - metrics.Enabled = true + metrics.Enable() os.Exit(m.Run()) } diff --git a/metrics/init_test.go b/metrics/init_test.go index 43401e833c34..af75bee425b1 100644 --- a/metrics/init_test.go +++ b/metrics/init_test.go @@ -1,5 +1,5 @@ package metrics func init() { - Enabled = true + metricsEnabled = true } diff --git a/metrics/log.go b/metrics/log.go index 3b9773faa728..3380bbf9c4d0 100644 --- a/metrics/log.go +++ b/metrics/log.go @@ -21,25 +21,21 @@ func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) { for range time.Tick(freq) { r.Each(func(name string, i interface{}) { switch metric := i.(type) { - case Counter: + case *Counter: l.Printf("counter %s\n", name) l.Printf(" count: %9d\n", metric.Snapshot().Count()) - case CounterFloat64: + case *CounterFloat64: l.Printf("counter %s\n", name) l.Printf(" count: %f\n", metric.Snapshot().Count()) - case Gauge: + case *Gauge: l.Printf("gauge %s\n", name) l.Printf(" value: %9d\n", metric.Snapshot().Value()) - case GaugeFloat64: + case *GaugeFloat64: l.Printf("gauge %s\n", name) l.Printf(" value: %f\n", metric.Snapshot().Value()) - case GaugeInfo: + case *GaugeInfo: l.Printf("gauge %s\n", name) l.Printf(" value: %s\n", metric.Snapshot().Value()) - case Healthcheck: - metric.Check() - l.Printf("healthcheck %s\n", name) - l.Printf(" error: %v\n", metric.Error()) case Histogram: h := metric.Snapshot() ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) @@ -54,7 +50,7 @@ func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) { l.Printf(" 95%%: %12.2f\n", ps[2]) l.Printf(" 99%%: %12.2f\n", ps[3]) l.Printf(" 99.9%%: %12.2f\n", ps[4]) - case Meter: + case *Meter: m := metric.Snapshot() l.Printf("meter %s\n", name) l.Printf(" count: %9d\n", m.Count()) @@ -62,7 +58,7 @@ func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) { l.Printf(" 5-min rate: %12.2f\n", m.Rate5()) l.Printf(" 15-min rate: %12.2f\n", m.Rate15()) l.Printf(" mean rate: %12.2f\n", m.RateMean()) - case Timer: + case *Timer: t := metric.Snapshot() ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) l.Printf("timer %s\n", name) diff --git a/metrics/meter.go b/metrics/meter.go index 432838f4ef7b..194bd1f30411 100644 --- a/metrics/meter.go +++ b/metrics/meter.go @@ -7,114 +7,78 @@ import ( "time" ) -type MeterSnapshot interface { - Count() int64 - Rate1() float64 - Rate5() float64 - Rate15() float64 - RateMean() float64 -} - -// Meters count events to produce exponentially-weighted moving average rates -// at one-, five-, and fifteen-minutes and a mean rate. -type Meter interface { - Mark(int64) - Snapshot() MeterSnapshot - Stop() -} - // GetOrRegisterMeter returns an existing Meter or constructs and registers a -// new StandardMeter. +// new Meter. // Be sure to unregister the meter from the registry once it is of no use to // allow for garbage collection. -func GetOrRegisterMeter(name string, r Registry) Meter { - if nil == r { +func GetOrRegisterMeter(name string, r Registry) *Meter { + if r == nil { r = DefaultRegistry } - return r.GetOrRegister(name, NewMeter).(Meter) + return r.GetOrRegister(name, NewMeter).(*Meter) } -// NewMeter constructs a new StandardMeter and launches a goroutine. +// NewMeter constructs a new Meter and launches a goroutine. // Be sure to call Stop() once the meter is of no use to allow for garbage collection. -func NewMeter() Meter { - if !Enabled { - return NilMeter{} - } - m := newStandardMeter() - arbiter.Lock() - defer arbiter.Unlock() - arbiter.meters[m] = struct{}{} - if !arbiter.started { - arbiter.started = true - go arbiter.tick() - } +func NewMeter() *Meter { + m := newMeter() + arbiter.add(m) return m } // NewInactiveMeter returns a meter but does not start any goroutines. This // method is mainly intended for testing. -func NewInactiveMeter() Meter { - if !Enabled { - return NilMeter{} - } - m := newStandardMeter() - return m +func NewInactiveMeter() *Meter { + return newMeter() } -// NewRegisteredMeter constructs and registers a new StandardMeter +// NewRegisteredMeter constructs and registers a new Meter // and launches a goroutine. // Be sure to unregister the meter from the registry once it is of no use to // allow for garbage collection. -func NewRegisteredMeter(name string, r Registry) Meter { +func NewRegisteredMeter(name string, r Registry) *Meter { return GetOrRegisterMeter(name, r) } -// meterSnapshot is a read-only copy of the meter's internal values. -type meterSnapshot struct { +// MeterSnapshot is a read-only copy of the meter's internal values. +type MeterSnapshot struct { count int64 rate1, rate5, rate15, rateMean float64 } // Count returns the count of events at the time the snapshot was taken. -func (m *meterSnapshot) Count() int64 { return m.count } +func (m *MeterSnapshot) Count() int64 { return m.count } // Rate1 returns the one-minute moving average rate of events per second at the // time the snapshot was taken. -func (m *meterSnapshot) Rate1() float64 { return m.rate1 } +func (m *MeterSnapshot) Rate1() float64 { return m.rate1 } // Rate5 returns the five-minute moving average rate of events per second at // the time the snapshot was taken. -func (m *meterSnapshot) Rate5() float64 { return m.rate5 } +func (m *MeterSnapshot) Rate5() float64 { return m.rate5 } // Rate15 returns the fifteen-minute moving average rate of events per second // at the time the snapshot was taken. -func (m *meterSnapshot) Rate15() float64 { return m.rate15 } +func (m *MeterSnapshot) Rate15() float64 { return m.rate15 } // RateMean returns the meter's mean rate of events per second at the time the // snapshot was taken. -func (m *meterSnapshot) RateMean() float64 { return m.rateMean } - -// NilMeter is a no-op Meter. -type NilMeter struct{} +func (m *MeterSnapshot) RateMean() float64 { return m.rateMean } -func (NilMeter) Count() int64 { return 0 } -func (NilMeter) Mark(n int64) {} -func (NilMeter) Snapshot() MeterSnapshot { return (*emptySnapshot)(nil) } -func (NilMeter) Stop() {} - -// StandardMeter is the standard implementation of a Meter. -type StandardMeter struct { +// Meter count events to produce exponentially-weighted moving average rates +// at one-, five-, and fifteen-minutes and a mean rate. +type Meter struct { count atomic.Int64 uncounted atomic.Int64 // not yet added to the EWMAs rateMean atomic.Uint64 - a1, a5, a15 EWMA + a1, a5, a15 *EWMA startTime time.Time stopped atomic.Bool } -func newStandardMeter() *StandardMeter { - return &StandardMeter{ +func newMeter() *Meter { + return &Meter{ a1: NewEWMA1(), a5: NewEWMA5(), a15: NewEWMA15(), @@ -123,22 +87,20 @@ func newStandardMeter() *StandardMeter { } // Stop stops the meter, Mark() will be a no-op if you use it after being stopped. -func (m *StandardMeter) Stop() { +func (m *Meter) Stop() { if stopped := m.stopped.Swap(true); !stopped { - arbiter.Lock() - delete(arbiter.meters, m) - arbiter.Unlock() + arbiter.remove(m) } } // Mark records the occurrence of n events. -func (m *StandardMeter) Mark(n int64) { +func (m *Meter) Mark(n int64) { m.uncounted.Add(n) } // Snapshot returns a read-only copy of the meter. -func (m *StandardMeter) Snapshot() MeterSnapshot { - return &meterSnapshot{ +func (m *Meter) Snapshot() *MeterSnapshot { + return &MeterSnapshot{ count: m.count.Load() + m.uncounted.Load(), rate1: m.a1.Snapshot().Rate(), rate5: m.a5.Snapshot().Rate(), @@ -147,7 +109,7 @@ func (m *StandardMeter) Snapshot() MeterSnapshot { } } -func (m *StandardMeter) tick() { +func (m *Meter) tick() { // Take the uncounted values, add to count n := m.uncounted.Swap(0) count := m.count.Add(n) @@ -157,33 +119,51 @@ func (m *StandardMeter) tick() { m.a5.Update(n) m.a15.Update(n) // And trigger them to calculate the rates - m.a1.Tick() - m.a5.Tick() - m.a15.Tick() + m.a1.tick() + m.a5.tick() + m.a15.tick() } -// meterArbiter ticks meters every 5s from a single goroutine. +var arbiter = meterTicker{meters: make(map[*Meter]struct{})} + +// meterTicker ticks meters every 5s from a single goroutine. // meters are references in a set for future stopping. -type meterArbiter struct { - sync.RWMutex +type meterTicker struct { + mu sync.RWMutex + started bool - meters map[*StandardMeter]struct{} - ticker *time.Ticker + meters map[*Meter]struct{} } -var arbiter = meterArbiter{ticker: time.NewTicker(5 * time.Second), meters: make(map[*StandardMeter]struct{})} - -// tick meters on the scheduled interval -func (ma *meterArbiter) tick() { - for range ma.ticker.C { - ma.tickMeters() +// add adds another *Meter ot the arbiter, and starts the arbiter ticker. +func (ma *meterTicker) add(m *Meter) { + ma.mu.Lock() + defer ma.mu.Unlock() + ma.meters[m] = struct{}{} + if !ma.started { + ma.started = true + go ma.loop() } } -func (ma *meterArbiter) tickMeters() { - ma.RLock() - defer ma.RUnlock() - for meter := range ma.meters { - meter.tick() +// remove removes a meter from the set of ticked meters. +func (ma *meterTicker) remove(m *Meter) { + ma.mu.Lock() + delete(ma.meters, m) + ma.mu.Unlock() +} + +// loop ticks meters on a 5 second interval. +func (ma *meterTicker) loop() { + ticker := time.NewTicker(5 * time.Second) + for range ticker.C { + if !metricsEnabled { + continue + } + ma.mu.RLock() + for meter := range ma.meters { + meter.tick() + } + ma.mu.RUnlock() } } diff --git a/metrics/meter_test.go b/metrics/meter_test.go index 8330ea9647f5..d62cf386402c 100644 --- a/metrics/meter_test.go +++ b/metrics/meter_test.go @@ -28,18 +28,12 @@ func TestGetOrRegisterMeter(t *testing.T) { } func TestMeterDecay(t *testing.T) { - ma := meterArbiter{ - ticker: time.NewTicker(time.Millisecond), - meters: make(map[*StandardMeter]struct{}), - } - defer ma.ticker.Stop() - m := newStandardMeter() - ma.meters[m] = struct{}{} + m := newMeter() m.Mark(1) - ma.tickMeters() + m.tick() rateMean := m.Snapshot().RateMean() time.Sleep(100 * time.Millisecond) - ma.tickMeters() + m.tick() if m.Snapshot().RateMean() >= rateMean { t.Error("m.RateMean() didn't decrease") } diff --git a/metrics/metrics.go b/metrics/metrics.go index 797001ab656e..a9d6623173ed 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -6,52 +6,29 @@ package metrics import ( - "os" "runtime/metrics" "runtime/pprof" - "strconv" - "strings" - "syscall" "time" +) - "github.com/XinFinOrg/XDPoSChain/log" +var ( + metricsEnabled = false ) -// Enabled is checked by the constructor functions for all of the -// standard metrics. If it is true, the metric returned is a stub. +// Enabled is checked by functions that are deemed 'expensive', e.g. if a +// meter-type does locking and/or non-trivial math operations during update. +func Enabled() bool { + return metricsEnabled +} + +// Enable enables the metrics system. +// The Enabled-flag is expected to be set, once, during startup, but toggling off and on +// is not supported. // -// This global kill-switch helps quantify the observer effect and makes -// for less cluttered pprof profiles. -var Enabled = false - -// enablerFlags is the CLI flag names to use to enable metrics collections. -var enablerFlags = []string{"metrics"} - -// enablerEnvVars is the env var names to use to enable metrics collections. -var enablerEnvVars = []string{"XDC_METRICS"} - -// init enables or disables the metrics system. Since we need this to run before -// any other code gets to create meters and timers, we'll actually do an ugly hack -// and peek into the command line args for the metrics flag. -func init() { - for _, enabler := range enablerEnvVars { - if val, found := syscall.Getenv(enabler); found && !Enabled { - if enable, _ := strconv.ParseBool(val); enable { // ignore error, flag parser will choke on it later - log.Info("Enabling metrics collection") - Enabled = true - } - } - } - for _, arg := range os.Args { - flag := strings.TrimLeft(arg, "-") - - for _, enabler := range enablerFlags { - if !Enabled && flag == enabler { - log.Info("Enabling metrics collection") - Enabled = true - } - } - } +// Enable is not safe to call concurrently. You need to call this as early as possible in +// the program, before any metrics collection will happen. +func Enable() { + metricsEnabled = true } var threadCreateProfile = pprof.Lookup("threadcreate") @@ -128,7 +105,7 @@ func readRuntimeStats(v *runtimeStats) { // CollectProcessMetrics periodically collects various metrics about the running process. func CollectProcessMetrics(refresh time.Duration) { // Short circuit if the metrics system is disabled - if !Enabled { + if !metricsEnabled { return } diff --git a/metrics/metrics_test.go b/metrics/metrics_test.go index 2861d5f2caf6..dc144f2425a6 100644 --- a/metrics/metrics_test.go +++ b/metrics/metrics_test.go @@ -7,8 +7,6 @@ import ( "time" ) -const FANOUT = 128 - func TestReadRuntimeValues(t *testing.T) { var v runtimeStats readRuntimeStats(&v) @@ -16,60 +14,23 @@ func TestReadRuntimeValues(t *testing.T) { } func BenchmarkMetrics(b *testing.B) { - r := NewRegistry() - c := NewRegisteredCounter("counter", r) - cf := NewRegisteredCounterFloat64("counterfloat64", r) - g := NewRegisteredGauge("gauge", r) - gf := NewRegisteredGaugeFloat64("gaugefloat64", r) - h := NewRegisteredHistogram("histogram", r, NewUniformSample(100)) - m := NewRegisteredMeter("meter", r) - t := NewRegisteredTimer("timer", r) + var ( + r = NewRegistry() + c = NewRegisteredCounter("counter", r) + cf = NewRegisteredCounterFloat64("counterfloat64", r) + g = NewRegisteredGauge("gauge", r) + gf = NewRegisteredGaugeFloat64("gaugefloat64", r) + h = NewRegisteredHistogram("histogram", r, NewUniformSample(100)) + m = NewRegisteredMeter("meter", r) + t = NewRegisteredTimer("timer", r) + ) RegisterDebugGCStats(r) b.ResetTimer() - ch := make(chan bool) - - wgD := &sync.WaitGroup{} - /* - wgD.Add(1) - go func() { - defer wgD.Done() - //log.Println("go CaptureDebugGCStats") - for { - select { - case <-ch: - //log.Println("done CaptureDebugGCStats") - return - default: - CaptureDebugGCStatsOnce(r) - } - } - }() - //*/ - - wgW := &sync.WaitGroup{} - /* - wgW.Add(1) + var wg sync.WaitGroup + wg.Add(128) + for i := 0; i < 128; i++ { go func() { - defer wgW.Done() - //log.Println("go Write") - for { - select { - case <-ch: - //log.Println("done Write") - return - default: - WriteOnce(r, io.Discard) - } - } - }() - //*/ - - wg := &sync.WaitGroup{} - wg.Add(FANOUT) - for i := 0; i < FANOUT; i++ { - go func(i int) { defer wg.Done() - //log.Println("go", i) for i := 0; i < b.N; i++ { c.Inc(1) cf.Inc(1.0) @@ -79,13 +40,9 @@ func BenchmarkMetrics(b *testing.B) { m.Mark(1) t.Update(1) } - //log.Println("done", i) - }(i) + }() } wg.Wait() - close(ch) - wgD.Wait() - wgW.Wait() } func Example() { diff --git a/metrics/opentsdb.go b/metrics/opentsdb.go index e81690f94340..57af3d025e67 100644 --- a/metrics/opentsdb.go +++ b/metrics/opentsdb.go @@ -64,15 +64,15 @@ func (c *OpenTSDBConfig) writeRegistry(w io.Writer, now int64, shortHostname str c.Registry.Each(func(name string, i interface{}) { switch metric := i.(type) { - case Counter: + case *Counter: fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, metric.Snapshot().Count(), shortHostname) - case CounterFloat64: + case *CounterFloat64: fmt.Fprintf(w, "put %s.%s.count %d %f host=%s\n", c.Prefix, name, now, metric.Snapshot().Count(), shortHostname) - case Gauge: + case *Gauge: fmt.Fprintf(w, "put %s.%s.value %d %d host=%s\n", c.Prefix, name, now, metric.Snapshot().Value(), shortHostname) - case GaugeFloat64: + case *GaugeFloat64: fmt.Fprintf(w, "put %s.%s.value %d %f host=%s\n", c.Prefix, name, now, metric.Snapshot().Value(), shortHostname) - case GaugeInfo: + case *GaugeInfo: fmt.Fprintf(w, "put %s.%s.value %d %s host=%s\n", c.Prefix, name, now, metric.Snapshot().Value().String(), shortHostname) case Histogram: h := metric.Snapshot() @@ -87,14 +87,14 @@ func (c *OpenTSDBConfig) writeRegistry(w io.Writer, now int64, shortHostname str fmt.Fprintf(w, "put %s.%s.95-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[2], shortHostname) fmt.Fprintf(w, "put %s.%s.99-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[3], shortHostname) fmt.Fprintf(w, "put %s.%s.999-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[4], shortHostname) - case Meter: + case *Meter: m := metric.Snapshot() fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, m.Count(), shortHostname) fmt.Fprintf(w, "put %s.%s.one-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate1(), shortHostname) fmt.Fprintf(w, "put %s.%s.five-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate5(), shortHostname) fmt.Fprintf(w, "put %s.%s.fifteen-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate15(), shortHostname) fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, m.RateMean(), shortHostname) - case Timer: + case *Timer: t := metric.Snapshot() ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, t.Count(), shortHostname) diff --git a/metrics/prometheus/collector.go b/metrics/prometheus/collector.go index ac2c960ac775..1d49f51dc295 100644 --- a/metrics/prometheus/collector.go +++ b/metrics/prometheus/collector.go @@ -51,23 +51,23 @@ func newCollector() *collector { // metric type is not supported/known. func (c *collector) Add(name string, i any) error { switch m := i.(type) { - case metrics.Counter: + case *metrics.Counter: c.addCounter(name, m.Snapshot()) - case metrics.CounterFloat64: + case *metrics.CounterFloat64: c.addCounterFloat64(name, m.Snapshot()) - case metrics.Gauge: + case *metrics.Gauge: c.addGauge(name, m.Snapshot()) - case metrics.GaugeFloat64: + case *metrics.GaugeFloat64: c.addGaugeFloat64(name, m.Snapshot()) - case metrics.GaugeInfo: + case *metrics.GaugeInfo: c.addGaugeInfo(name, m.Snapshot()) case metrics.Histogram: c.addHistogram(name, m.Snapshot()) - case metrics.Meter: + case *metrics.Meter: c.addMeter(name, m.Snapshot()) - case metrics.Timer: + case *metrics.Timer: c.addTimer(name, m.Snapshot()) - case metrics.ResettingTimer: + case *metrics.ResettingTimer: c.addResettingTimer(name, m.Snapshot()) default: return fmt.Errorf("unknown prometheus metric type %T", i) @@ -106,11 +106,11 @@ func (c *collector) addHistogram(name string, m metrics.HistogramSnapshot) { c.buff.WriteRune('\n') } -func (c *collector) addMeter(name string, m metrics.MeterSnapshot) { +func (c *collector) addMeter(name string, m *metrics.MeterSnapshot) { c.writeGaugeCounter(name, m.Count()) } -func (c *collector) addTimer(name string, m metrics.TimerSnapshot) { +func (c *collector) addTimer(name string, m *metrics.TimerSnapshot) { pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999} ps := m.Percentiles(pv) c.writeSummaryCounter(name, m.Count()) @@ -121,7 +121,7 @@ func (c *collector) addTimer(name string, m metrics.TimerSnapshot) { c.buff.WriteRune('\n') } -func (c *collector) addResettingTimer(name string, m metrics.ResettingTimerSnapshot) { +func (c *collector) addResettingTimer(name string, m *metrics.ResettingTimerSnapshot) { if m.Count() <= 0 { return } diff --git a/metrics/prometheus/collector_test.go b/metrics/prometheus/collector_test.go index 948c539ad666..49b979432fbd 100644 --- a/metrics/prometheus/collector_test.go +++ b/metrics/prometheus/collector_test.go @@ -27,7 +27,7 @@ import ( ) func TestMain(m *testing.M) { - metrics.Enabled = true + metrics.Enable() os.Exit(m.Run()) } diff --git a/metrics/registry.go b/metrics/registry.go index 8325f4e153e6..527da6238de7 100644 --- a/metrics/registry.go +++ b/metrics/registry.go @@ -1,6 +1,7 @@ package metrics import ( + "errors" "fmt" "reflect" "sort" @@ -8,14 +9,10 @@ import ( "sync" ) -// DuplicateMetric is the error returned by Registry. Register when a metric +// ErrDuplicateMetric is the error returned by Registry.Register when a metric // already exists. If you mean to Register that metric you must first // Unregister the existing metric. -type DuplicateMetric string - -func (err DuplicateMetric) Error() string { - return fmt.Sprintf("duplicate metric: %s", string(err)) -} +var ErrDuplicateMetric = errors.New("duplicate metric") // A Registry holds references to a set of metrics by name and can iterate // over them, calling callback functions provided by the user. @@ -114,13 +111,13 @@ func (r *StandardRegistry) GetOrRegister(name string, i interface{}) interface{} return item } -// Register the given metric under the given name. Returns a DuplicateMetric +// Register the given metric under the given name. Returns a ErrDuplicateMetric // if a metric by the given name is already registered. func (r *StandardRegistry) Register(name string, i interface{}) error { // fast path _, ok := r.metrics.Load(name) if ok { - return DuplicateMetric(name) + return fmt.Errorf("%w: %v", ErrDuplicateMetric, name) } if v := reflect.ValueOf(i); v.Kind() == reflect.Func { @@ -128,7 +125,7 @@ func (r *StandardRegistry) Register(name string, i interface{}) error { } _, loaded, _ := r.loadOrRegister(name, i) if loaded { - return DuplicateMetric(name) + return fmt.Errorf("%w: %v", ErrDuplicateMetric, name) } return nil } @@ -136,7 +133,7 @@ func (r *StandardRegistry) Register(name string, i interface{}) error { // RunHealthchecks run all registered healthchecks. func (r *StandardRegistry) RunHealthchecks() { r.metrics.Range(func(key, value any) bool { - if h, ok := value.(Healthcheck); ok { + if h, ok := value.(*Healthcheck); ok { h.Check() } return true @@ -149,15 +146,15 @@ func (r *StandardRegistry) GetAll() map[string]map[string]interface{} { r.Each(func(name string, i interface{}) { values := make(map[string]interface{}) switch metric := i.(type) { - case Counter: + case *Counter: values["count"] = metric.Snapshot().Count() - case CounterFloat64: + case *CounterFloat64: values["count"] = metric.Snapshot().Count() - case Gauge: + case *Gauge: values["value"] = metric.Snapshot().Value() - case GaugeFloat64: + case *GaugeFloat64: values["value"] = metric.Snapshot().Value() - case Healthcheck: + case *Healthcheck: values["error"] = nil metric.Check() if err := metric.Error(); nil != err { @@ -176,14 +173,14 @@ func (r *StandardRegistry) GetAll() map[string]map[string]interface{} { values["95%"] = ps[2] values["99%"] = ps[3] values["99.9%"] = ps[4] - case Meter: + case *Meter: m := metric.Snapshot() values["count"] = m.Count() values["1m.rate"] = m.Rate1() values["5m.rate"] = m.Rate5() values["15m.rate"] = m.Rate15() values["mean.rate"] = m.RateMean() - case Timer: + case *Timer: t := metric.Snapshot() ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) values["count"] = t.Count() @@ -214,7 +211,7 @@ func (r *StandardRegistry) Unregister(name string) { func (r *StandardRegistry) loadOrRegister(name string, i interface{}) (interface{}, bool, bool) { switch i.(type) { - case Counter, CounterFloat64, Gauge, GaugeFloat64, GaugeInfo, Healthcheck, Histogram, Meter, Timer, ResettingTimer: + case *Counter, *CounterFloat64, *Gauge, *GaugeFloat64, *GaugeInfo, *Healthcheck, Histogram, *Meter, *Timer, *ResettingTimer: default: return nil, false, false } @@ -345,7 +342,7 @@ func GetOrRegister(name string, i interface{}) interface{} { return DefaultRegistry.GetOrRegister(name, i) } -// Register the given metric under the given name. Returns a DuplicateMetric +// Register the given metric under the given name. Returns a ErrDuplicateMetric // if a metric by the given name is already registered. func Register(name string, i interface{}) error { return DefaultRegistry.Register(name, i) diff --git a/metrics/registry_test.go b/metrics/registry_test.go index 75012dd4ac00..bdc58fee6c74 100644 --- a/metrics/registry_test.go +++ b/metrics/registry_test.go @@ -47,7 +47,7 @@ func TestRegistry(t *testing.T) { if name != "foo" { t.Fatal(name) } - if _, ok := iface.(Counter); !ok { + if _, ok := iface.(*Counter); !ok { t.Fatal(iface) } }) @@ -73,7 +73,7 @@ func TestRegistryDuplicate(t *testing.T) { i := 0 r.Each(func(name string, iface interface{}) { i++ - if _, ok := iface.(Counter); !ok { + if _, ok := iface.(*Counter); !ok { t.Fatal(iface) } }) @@ -85,11 +85,11 @@ func TestRegistryDuplicate(t *testing.T) { func TestRegistryGet(t *testing.T) { r := NewRegistry() r.Register("foo", NewCounter()) - if count := r.Get("foo").(Counter).Snapshot().Count(); count != 0 { + if count := r.Get("foo").(*Counter).Snapshot().Count(); count != 0 { t.Fatal(count) } - r.Get("foo").(Counter).Inc(1) - if count := r.Get("foo").(Counter).Snapshot().Count(); count != 1 { + r.Get("foo").(*Counter).Inc(1) + if count := r.Get("foo").(*Counter).Snapshot().Count(); count != 1 { t.Fatal(count) } } @@ -100,7 +100,7 @@ func TestRegistryGetOrRegister(t *testing.T) { // First metric wins with GetOrRegister _ = r.GetOrRegister("foo", NewCounter()) m := r.GetOrRegister("foo", NewGauge()) - if _, ok := m.(Counter); !ok { + if _, ok := m.(*Counter); !ok { t.Fatal(m) } @@ -110,7 +110,7 @@ func TestRegistryGetOrRegister(t *testing.T) { if name != "foo" { t.Fatal(name) } - if _, ok := iface.(Counter); !ok { + if _, ok := iface.(*Counter); !ok { t.Fatal(iface) } }) @@ -125,7 +125,7 @@ func TestRegistryGetOrRegisterWithLazyInstantiation(t *testing.T) { // First metric wins with GetOrRegister _ = r.GetOrRegister("foo", NewCounter) m := r.GetOrRegister("foo", NewGauge) - if _, ok := m.(Counter); !ok { + if _, ok := m.(*Counter); !ok { t.Fatal(m) } @@ -135,7 +135,7 @@ func TestRegistryGetOrRegisterWithLazyInstantiation(t *testing.T) { if name != "foo" { t.Fatal(name) } - if _, ok := iface.(Counter); !ok { + if _, ok := iface.(*Counter); !ok { t.Fatal(iface) } }) diff --git a/metrics/resetting_sample.go b/metrics/resetting_sample.go index c38ffcd3ec32..730ef93416d5 100644 --- a/metrics/resetting_sample.go +++ b/metrics/resetting_sample.go @@ -17,7 +17,7 @@ type resettingSample struct { } // Snapshot returns a read-only copy of the sample with the original reset. -func (rs *resettingSample) Snapshot() SampleSnapshot { +func (rs *resettingSample) Snapshot() *sampleSnapshot { s := rs.Sample.Snapshot() rs.Sample.Clear() return s diff --git a/metrics/resetting_timer.go b/metrics/resetting_timer.go index 6802e3fcea98..1b3e87bc3d12 100644 --- a/metrics/resetting_timer.go +++ b/metrics/resetting_timer.go @@ -5,36 +5,17 @@ import ( "time" ) -// Initial slice capacity for the values stored in a ResettingTimer -const InitialResettingTimerSliceCap = 10 - -type ResettingTimerSnapshot interface { - Count() int - Mean() float64 - Max() int64 - Min() int64 - Percentiles([]float64) []float64 -} - -// ResettingTimer is used for storing aggregated values for timers, which are reset on every flush interval. -type ResettingTimer interface { - Snapshot() ResettingTimerSnapshot - Time(func()) - Update(time.Duration) - UpdateSince(time.Time) -} - // GetOrRegisterResettingTimer returns an existing ResettingTimer or constructs and registers a -// new StandardResettingTimer. -func GetOrRegisterResettingTimer(name string, r Registry) ResettingTimer { +// new ResettingTimer. +func GetOrRegisterResettingTimer(name string, r Registry) *ResettingTimer { if nil == r { r = DefaultRegistry } - return r.GetOrRegister(name, NewResettingTimer).(ResettingTimer) + return r.GetOrRegister(name, NewResettingTimer).(*ResettingTimer) } -// NewRegisteredResettingTimer constructs and registers a new StandardResettingTimer. -func NewRegisteredResettingTimer(name string, r Registry) ResettingTimer { +// NewRegisteredResettingTimer constructs and registers a new ResettingTimer. +func NewRegisteredResettingTimer(name string, r Registry) *ResettingTimer { c := NewResettingTimer() if nil == r { r = DefaultRegistry @@ -43,33 +24,15 @@ func NewRegisteredResettingTimer(name string, r Registry) ResettingTimer { return c } -// NewResettingTimer constructs a new StandardResettingTimer -func NewResettingTimer() ResettingTimer { - if !Enabled { - return NilResettingTimer{} - } - return &StandardResettingTimer{ - values: make([]int64, 0, InitialResettingTimerSliceCap), +// NewResettingTimer constructs a new ResettingTimer +func NewResettingTimer() *ResettingTimer { + return &ResettingTimer{ + values: make([]int64, 0, 10), } } -// NilResettingTimer is a no-op ResettingTimer. -type NilResettingTimer struct{} - -func (NilResettingTimer) Values() []int64 { return nil } -func (n NilResettingTimer) Snapshot() ResettingTimerSnapshot { return n } -func (NilResettingTimer) Time(f func()) { f() } -func (NilResettingTimer) Update(time.Duration) {} -func (NilResettingTimer) Percentiles([]float64) []float64 { return nil } -func (NilResettingTimer) Mean() float64 { return 0.0 } -func (NilResettingTimer) Max() int64 { return 0 } -func (NilResettingTimer) Min() int64 { return 0 } -func (NilResettingTimer) UpdateSince(time.Time) {} -func (NilResettingTimer) Count() int { return 0 } - -// StandardResettingTimer is the standard implementation of a ResettingTimer. -// and Meter. -type StandardResettingTimer struct { +// ResettingTimer is used for storing aggregated values for timers, which are reset on every flush interval. +type ResettingTimer struct { values []int64 sum int64 // sum is a running count of the total sum, used later to calculate mean @@ -77,28 +40,31 @@ type StandardResettingTimer struct { } // Snapshot resets the timer and returns a read-only copy of its contents. -func (t *StandardResettingTimer) Snapshot() ResettingTimerSnapshot { +func (t *ResettingTimer) Snapshot() *ResettingTimerSnapshot { t.mutex.Lock() defer t.mutex.Unlock() - snapshot := &resettingTimerSnapshot{} + snapshot := &ResettingTimerSnapshot{} if len(t.values) > 0 { snapshot.mean = float64(t.sum) / float64(len(t.values)) snapshot.values = t.values - t.values = make([]int64, 0, InitialResettingTimerSliceCap) + t.values = make([]int64, 0, 10) } t.sum = 0 return snapshot } // Record the duration of the execution of the given function. -func (t *StandardResettingTimer) Time(f func()) { +func (t *ResettingTimer) Time(f func()) { ts := time.Now() f() t.Update(time.Since(ts)) } // Record the duration of an event. -func (t *StandardResettingTimer) Update(d time.Duration) { +func (t *ResettingTimer) Update(d time.Duration) { + if !metricsEnabled { + return + } t.mutex.Lock() defer t.mutex.Unlock() t.values = append(t.values, int64(d)) @@ -106,12 +72,12 @@ func (t *StandardResettingTimer) Update(d time.Duration) { } // Record the duration of an event that started at a time and ends now. -func (t *StandardResettingTimer) UpdateSince(ts time.Time) { +func (t *ResettingTimer) UpdateSince(ts time.Time) { t.Update(time.Since(ts)) } -// resettingTimerSnapshot is a point-in-time copy of another ResettingTimer. -type resettingTimerSnapshot struct { +// ResettingTimerSnapshot is a point-in-time copy of another ResettingTimer. +type ResettingTimerSnapshot struct { values []int64 mean float64 max int64 @@ -121,20 +87,20 @@ type resettingTimerSnapshot struct { } // Count return the length of the values from snapshot. -func (t *resettingTimerSnapshot) Count() int { +func (t *ResettingTimerSnapshot) Count() int { return len(t.values) } // Percentiles returns the boundaries for the input percentiles. // note: this method is not thread safe -func (t *resettingTimerSnapshot) Percentiles(percentiles []float64) []float64 { +func (t *ResettingTimerSnapshot) Percentiles(percentiles []float64) []float64 { t.calc(percentiles) return t.thresholdBoundaries } // Mean returns the mean of the snapshotted values // note: this method is not thread safe -func (t *resettingTimerSnapshot) Mean() float64 { +func (t *ResettingTimerSnapshot) Mean() float64 { if !t.calculated { t.calc(nil) } @@ -144,7 +110,7 @@ func (t *resettingTimerSnapshot) Mean() float64 { // Max returns the max of the snapshotted values // note: this method is not thread safe -func (t *resettingTimerSnapshot) Max() int64 { +func (t *ResettingTimerSnapshot) Max() int64 { if !t.calculated { t.calc(nil) } @@ -153,14 +119,14 @@ func (t *resettingTimerSnapshot) Max() int64 { // Min returns the min of the snapshotted values // note: this method is not thread safe -func (t *resettingTimerSnapshot) Min() int64 { +func (t *ResettingTimerSnapshot) Min() int64 { if !t.calculated { t.calc(nil) } return t.min } -func (t *resettingTimerSnapshot) calc(percentiles []float64) { +func (t *ResettingTimerSnapshot) calc(percentiles []float64) { scores := CalculatePercentiles(t.values, percentiles) t.thresholdBoundaries = scores if len(t.values) == 0 { diff --git a/metrics/sample.go b/metrics/sample.go index 17b2bee28f10..dc8167809fdd 100644 --- a/metrics/sample.go +++ b/metrics/sample.go @@ -10,27 +10,123 @@ import ( const rescaleThreshold = time.Hour -type SampleSnapshot interface { - Count() int64 - Max() int64 - Mean() float64 - Min() int64 - Percentile(float64) float64 - Percentiles([]float64) []float64 - Size() int - StdDev() float64 - Sum() int64 - Variance() float64 -} - -// Samples maintain a statistically-significant selection of values from +// Sample maintains a statistically-significant selection of values from // a stream. type Sample interface { - Snapshot() SampleSnapshot + Snapshot() *sampleSnapshot Clear() Update(int64) } +var ( + _ Sample = (*ExpDecaySample)(nil) + _ Sample = (*UniformSample)(nil) + _ Sample = (*resettingSample)(nil) +) + +// sampleSnapshot is a read-only copy of a Sample. +type sampleSnapshot struct { + count int64 + values []int64 + + max int64 + min int64 + mean float64 + sum int64 + variance float64 +} + +// newSampleSnapshotPrecalculated creates a read-only sampleSnapShot, using +// precalculated sums to avoid iterating the values +func newSampleSnapshotPrecalculated(count int64, values []int64, min, max, sum int64) *sampleSnapshot { + if len(values) == 0 { + return &sampleSnapshot{ + count: count, + values: values, + } + } + return &sampleSnapshot{ + count: count, + values: values, + max: max, + min: min, + mean: float64(sum) / float64(len(values)), + sum: sum, + } +} + +// newSampleSnapshot creates a read-only sampleSnapShot, and calculates some +// numbers. +func newSampleSnapshot(count int64, values []int64) *sampleSnapshot { + var ( + max int64 = math.MinInt64 + min int64 = math.MaxInt64 + sum int64 + ) + for _, v := range values { + sum += v + if v > max { + max = v + } + if v < min { + min = v + } + } + return newSampleSnapshotPrecalculated(count, values, min, max, sum) +} + +// Count returns the count of inputs at the time the snapshot was taken. +func (s *sampleSnapshot) Count() int64 { return s.count } + +// Max returns the maximal value at the time the snapshot was taken. +func (s *sampleSnapshot) Max() int64 { return s.max } + +// Mean returns the mean value at the time the snapshot was taken. +func (s *sampleSnapshot) Mean() float64 { return s.mean } + +// Min returns the minimal value at the time the snapshot was taken. +func (s *sampleSnapshot) Min() int64 { return s.min } + +// Percentile returns an arbitrary percentile of values at the time the +// snapshot was taken. +func (s *sampleSnapshot) Percentile(p float64) float64 { + return SamplePercentile(s.values, p) +} + +// Percentiles returns a slice of arbitrary percentiles of values at the time +// the snapshot was taken. +func (s *sampleSnapshot) Percentiles(ps []float64) []float64 { + return CalculatePercentiles(s.values, ps) +} + +// Size returns the size of the sample at the time the snapshot was taken. +func (s *sampleSnapshot) Size() int { return len(s.values) } + +// StdDev returns the standard deviation of values at the time the snapshot was +// taken. +func (s *sampleSnapshot) StdDev() float64 { + if s.variance == 0.0 { + s.variance = SampleVariance(s.mean, s.values) + } + return math.Sqrt(s.variance) +} + +// Sum returns the sum of values at the time the snapshot was taken. +func (s *sampleSnapshot) Sum() int64 { return s.sum } + +// Values returns a copy of the values in the sample. +func (s *sampleSnapshot) Values() []int64 { + return slices.Clone(s.values) +} + +// Variance returns the variance of values at the time the snapshot was taken. +func (s *sampleSnapshot) Variance() float64 { + if s.variance == 0.0 { + s.variance = SampleVariance(s.mean, s.values) + } + return s.variance +} + // ExpDecaySample is an exponentially-decaying sample using a forward-decaying // priority reservoir. See Cormode et al's "Forward Decay: A Practical Time // Decay Model for Streaming Systems". @@ -49,9 +145,6 @@ type ExpDecaySample struct { // NewExpDecaySample constructs a new exponentially-decaying sample with the // given reservoir size and alpha. func NewExpDecaySample(reservoirSize int, alpha float64) Sample { - if !Enabled { - return NilSample{} - } s := &ExpDecaySample{ alpha: alpha, reservoirSize: reservoirSize, @@ -79,7 +172,7 @@ func (s *ExpDecaySample) Clear() { } // Snapshot returns a read-only copy of the sample. -func (s *ExpDecaySample) Snapshot() SampleSnapshot { +func (s *ExpDecaySample) Snapshot() *sampleSnapshot { s.mutex.Lock() defer s.mutex.Unlock() var ( @@ -105,6 +198,9 @@ func (s *ExpDecaySample) Snapshot() SampleSnapshot { // Update samples a new value. func (s *ExpDecaySample) Update(v int64) { + if !metricsEnabled { + return + } s.update(time.Now(), v) } @@ -140,13 +236,6 @@ func (s *ExpDecaySample) update(t time.Time, v int64) { } } -// NilSample is a no-op Sample. -type NilSample struct{} - -func (NilSample) Clear() {} -func (NilSample) Snapshot() SampleSnapshot { return (*emptySnapshot)(nil) } -func (NilSample) Update(v int64) {} - // SamplePercentile returns an arbitrary percentile of the slice of int64. func SamplePercentile(values []int64, p float64) float64 { return CalculatePercentiles(values, []float64{p})[0] @@ -181,114 +270,6 @@ func CalculatePercentiles(values []int64, ps []float64) []float64 { return scores } -// sampleSnapshot is a read-only copy of another Sample. -type sampleSnapshot struct { - count int64 - values []int64 - - max int64 - min int64 - mean float64 - sum int64 - variance float64 -} - -// newSampleSnapshotPrecalculated creates a read-only sampleSnapShot, using -// precalculated sums to avoid iterating the values -func newSampleSnapshotPrecalculated(count int64, values []int64, min, max, sum int64) *sampleSnapshot { - if len(values) == 0 { - return &sampleSnapshot{ - count: count, - values: values, - } - } - return &sampleSnapshot{ - count: count, - values: values, - max: max, - min: min, - mean: float64(sum) / float64(len(values)), - sum: sum, - } -} - -// newSampleSnapshot creates a read-only sampleSnapShot, and calculates some -// numbers. -func newSampleSnapshot(count int64, values []int64) *sampleSnapshot { - var ( - max int64 = math.MinInt64 - min int64 = math.MaxInt64 - sum int64 - ) - for _, v := range values { - sum += v - if v > max { - max = v - } - if v < min { - min = v - } - } - return newSampleSnapshotPrecalculated(count, values, min, max, sum) -} - -// Count returns the count of inputs at the time the snapshot was taken. -func (s *sampleSnapshot) Count() int64 { return s.count } - -// Max returns the maximal value at the time the snapshot was taken. -func (s *sampleSnapshot) Max() int64 { return s.max } - -// Mean returns the mean value at the time the snapshot was taken. -func (s *sampleSnapshot) Mean() float64 { return s.mean } - -// Min returns the minimal value at the time the snapshot was taken. -func (s *sampleSnapshot) Min() int64 { return s.min } - -// Percentile returns an arbitrary percentile of values at the time the -// snapshot was taken. -func (s *sampleSnapshot) Percentile(p float64) float64 { - return SamplePercentile(s.values, p) -} - -// Percentiles returns a slice of arbitrary percentiles of values at the time -// the snapshot was taken. -func (s *sampleSnapshot) Percentiles(ps []float64) []float64 { - return CalculatePercentiles(s.values, ps) -} - -// Size returns the size of the sample at the time the snapshot was taken. -func (s *sampleSnapshot) Size() int { return len(s.values) } - -// Snapshot returns the snapshot. -func (s *sampleSnapshot) Snapshot() SampleSnapshot { return s } - -// StdDev returns the standard deviation of values at the time the snapshot was -// taken. -func (s *sampleSnapshot) StdDev() float64 { - if s.variance == 0.0 { - s.variance = SampleVariance(s.mean, s.values) - } - return math.Sqrt(s.variance) -} - -// Sum returns the sum of values at the time the snapshot was taken. -func (s *sampleSnapshot) Sum() int64 { return s.sum } - -// Values returns a copy of the values in the sample. -func (s *sampleSnapshot) Values() []int64 { - values := make([]int64, len(s.values)) - copy(values, s.values) - return values -} - -// Variance returns the variance of values at the time the snapshot was taken. -func (s *sampleSnapshot) Variance() float64 { - if s.variance == 0.0 { - s.variance = SampleVariance(s.mean, s.values) - } - return s.variance -} - // SampleVariance returns the variance of the slice of int64. func SampleVariance(mean float64, values []int64) float64 { if len(values) == 0 { @@ -302,7 +283,7 @@ func SampleVariance(mean float64, values []int64) float64 { return sum / float64(len(values)) } -// A uniform sample using Vitter's Algorithm R. +// UniformSample implements a uniform sample using Vitter's Algorithm R. // // type UniformSample struct { @@ -316,9 +297,6 @@ type UniformSample struct { // NewUniformSample constructs a new uniform sample with the given reservoir // size. func NewUniformSample(reservoirSize int) Sample { - if !Enabled { - return NilSample{} - } return &UniformSample{ reservoirSize: reservoirSize, values: make([]int64, 0, reservoirSize), @@ -336,14 +314,13 @@ func (s *UniformSample) Clear() { s.mutex.Lock() defer s.mutex.Unlock() s.count = 0 - s.values = make([]int64, 0, s.reservoirSize) + clear(s.values) } // Snapshot returns a read-only copy of the sample. -func (s *UniformSample) Snapshot() SampleSnapshot { +func (s *UniformSample) Snapshot() *sampleSnapshot { s.mutex.Lock() - values := make([]int64, len(s.values)) - copy(values, s.values) + values := slices.Clone(s.values) count := s.count s.mutex.Unlock() return newSampleSnapshot(count, values) @@ -351,21 +328,24 @@ func (s *UniformSample) Snapshot() SampleSnapshot { // Update samples a new value. func (s *UniformSample) Update(v int64) { + if !metricsEnabled { + return + } s.mutex.Lock() defer s.mutex.Unlock() s.count++ if len(s.values) < s.reservoirSize { s.values = append(s.values, v) + return + } + var r int64 + if s.rand != nil { + r = s.rand.Int63n(s.count) } else { - var r int64 - if s.rand != nil { - r = s.rand.Int63n(s.count) - } else { - r = rand.Int63n(s.count) - } - if r < int64(len(s.values)) { - s.values[int(r)] = v - } + r = rand.Int63n(s.count) + } + if r < int64(len(s.values)) { + s.values[int(r)] = v } } diff --git a/metrics/sample_test.go b/metrics/sample_test.go index c855671ae23c..6619eb1e9eb2 100644 --- a/metrics/sample_test.go +++ b/metrics/sample_test.go @@ -86,7 +86,7 @@ func TestExpDecaySample(t *testing.T) { if have, want := snap.Size(), min(tc.updates, tc.reservoirSize); have != want { t.Errorf("unexpected size: have %d want %d", have, want) } - values := snap.(*sampleSnapshot).values + values := snap.values if have, want := len(values), min(tc.updates, tc.reservoirSize); have != want { t.Errorf("unexpected values length: have %d want %d", have, want) } @@ -111,8 +111,7 @@ func TestExpDecaySampleNanosecondRegression(t *testing.T) { for i := 0; i < 1000; i++ { sw.Update(20) } - s := sw.Snapshot() - v := s.(*sampleSnapshot).values + v := sw.Snapshot().values avg := float64(0) for i := 0; i < len(v); i++ { avg += float64(v[i]) @@ -166,7 +165,7 @@ func TestUniformSample(t *testing.T) { if size := s.Size(); size != 100 { t.Errorf("s.Size(): 100 != %v\n", size) } - values := s.(*sampleSnapshot).values + values := s.values if l := len(values); l != 100 { t.Errorf("len(s.Values()): 100 != %v\n", l) @@ -184,8 +183,7 @@ func TestUniformSampleIncludesTail(t *testing.T) { for i := 0; i < max; i++ { sw.Update(int64(i)) } - s := sw.Snapshot() - v := s.(*sampleSnapshot).values + v := sw.Snapshot().values sum := 0 exp := (max - 1) * max / 2 for i := 0; i < len(v); i++ { @@ -220,7 +218,7 @@ func benchmarkSample(b *testing.B, s Sample) { } } -func testExpDecaySampleStatistics(t *testing.T, s SampleSnapshot) { +func testExpDecaySampleStatistics(t *testing.T, s *sampleSnapshot) { if sum := s.Sum(); sum != 496598 { t.Errorf("s.Sum(): 496598 != %v\n", sum) } @@ -251,7 +249,7 @@ func testExpDecaySampleStatistics(t *testing.T, s SampleSnapshot) { } } -func testUniformSampleStatistics(t *testing.T, s SampleSnapshot) { +func testUniformSampleStatistics(t *testing.T, s *sampleSnapshot) { if count := s.Count(); count != 10000 { t.Errorf("s.Count(): 10000 != %v\n", count) } diff --git a/metrics/syslog.go b/metrics/syslog.go index fd856d697316..0bc4ed0da59f 100644 --- a/metrics/syslog.go +++ b/metrics/syslog.go @@ -15,17 +15,17 @@ func Syslog(r Registry, d time.Duration, w *syslog.Writer) { for range time.Tick(d) { r.Each(func(name string, i interface{}) { switch metric := i.(type) { - case Counter: + case *Counter: w.Info(fmt.Sprintf("counter %s: count: %d", name, metric.Snapshot().Count())) - case CounterFloat64: + case *CounterFloat64: w.Info(fmt.Sprintf("counter %s: count: %f", name, metric.Snapshot().Count())) - case Gauge: + case *Gauge: w.Info(fmt.Sprintf("gauge %s: value: %d", name, metric.Snapshot().Value())) - case GaugeFloat64: + case *GaugeFloat64: w.Info(fmt.Sprintf("gauge %s: value: %f", name, metric.Snapshot().Value())) - case GaugeInfo: + case *GaugeInfo: w.Info(fmt.Sprintf("gauge %s: value: %s", name, metric.Snapshot().Value())) - case Healthcheck: + case *Healthcheck: metric.Check() w.Info(fmt.Sprintf("healthcheck %s: error: %v", name, metric.Error())) case Histogram: @@ -45,7 +45,7 @@ func Syslog(r Registry, d time.Duration, w *syslog.Writer) { ps[3], ps[4], )) - case Meter: + case *Meter: m := metric.Snapshot() w.Info(fmt.Sprintf( "meter %s: count: %d 1-min: %.2f 5-min: %.2f 15-min: %.2f mean: %.2f", @@ -56,7 +56,7 @@ func Syslog(r Registry, d time.Duration, w *syslog.Writer) { m.Rate15(), m.RateMean(), )) - case Timer: + case *Timer: t := metric.Snapshot() ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) w.Info(fmt.Sprintf( diff --git a/metrics/timer.go b/metrics/timer.go index fc2a88f508bc..9df15c967aba 100644 --- a/metrics/timer.go +++ b/metrics/timer.go @@ -5,47 +5,30 @@ import ( "time" ) -type TimerSnapshot interface { - HistogramSnapshot - MeterSnapshot -} - -// Timer capture the duration and rate of events. -type Timer interface { - Snapshot() TimerSnapshot - Stop() - Time(func()) - UpdateSince(time.Time) - Update(time.Duration) -} - // GetOrRegisterTimer returns an existing Timer or constructs and registers a -// new StandardTimer. +// new Timer. // Be sure to unregister the meter from the registry once it is of no use to // allow for garbage collection. -func GetOrRegisterTimer(name string, r Registry) Timer { +func GetOrRegisterTimer(name string, r Registry) *Timer { if nil == r { r = DefaultRegistry } - return r.GetOrRegister(name, NewTimer).(Timer) + return r.GetOrRegister(name, NewTimer).(*Timer) } -// NewCustomTimer constructs a new StandardTimer from a Histogram and a Meter. +// NewCustomTimer constructs a new Timer from a Histogram and a Meter. // Be sure to call Stop() once the timer is of no use to allow for garbage collection. -func NewCustomTimer(h Histogram, m Meter) Timer { - if !Enabled { - return NilTimer{} - } - return &StandardTimer{ +func NewCustomTimer(h Histogram, m *Meter) *Timer { + return &Timer{ histogram: h, meter: m, } } -// NewRegisteredTimer constructs and registers a new StandardTimer. +// NewRegisteredTimer constructs and registers a new Timer. // Be sure to unregister the meter from the registry once it is of no use to // allow for garbage collection. -func NewRegisteredTimer(name string, r Registry) Timer { +func NewRegisteredTimer(name string, r Registry) *Timer { c := NewTimer() if nil == r { r = DefaultRegistry @@ -54,60 +37,47 @@ func NewRegisteredTimer(name string, r Registry) Timer { return c } -// NewTimer constructs a new StandardTimer using an exponentially-decaying +// NewTimer constructs a new Timer using an exponentially-decaying // sample with the same reservoir size and alpha as UNIX load averages. // Be sure to call Stop() once the timer is of no use to allow for garbage collection. -func NewTimer() Timer { - if !Enabled { - return NilTimer{} - } - return &StandardTimer{ +func NewTimer() *Timer { + return &Timer{ histogram: NewHistogram(NewExpDecaySample(1028, 0.015)), meter: NewMeter(), } } -// NilTimer is a no-op Timer. -type NilTimer struct{} - -func (NilTimer) Snapshot() TimerSnapshot { return (*emptySnapshot)(nil) } -func (NilTimer) Stop() {} -func (NilTimer) Time(f func()) { f() } -func (NilTimer) Update(time.Duration) {} -func (NilTimer) UpdateSince(time.Time) {} - -// StandardTimer is the standard implementation of a Timer and uses a Histogram -// and Meter. -type StandardTimer struct { +// Timer captures the duration and rate of events, using a Histogram and a Meter. +type Timer struct { histogram Histogram - meter Meter + meter *Meter mutex sync.Mutex } // Snapshot returns a read-only copy of the timer. -func (t *StandardTimer) Snapshot() TimerSnapshot { +func (t *Timer) Snapshot() *TimerSnapshot { t.mutex.Lock() defer t.mutex.Unlock() - return &timerSnapshot{ + return &TimerSnapshot{ histogram: t.histogram.Snapshot(), meter: t.meter.Snapshot(), } } // Stop stops the meter. -func (t *StandardTimer) Stop() { +func (t *Timer) Stop() { t.meter.Stop() } // Time record the duration of the execution of the given function. -func (t *StandardTimer) Time(f func()) { +func (t *Timer) Time(f func()) { ts := time.Now() f() t.Update(time.Since(ts)) } // Update the duration of an event, in nanoseconds. -func (t *StandardTimer) Update(d time.Duration) { +func (t *Timer) Update(d time.Duration) { t.mutex.Lock() defer t.mutex.Unlock() t.histogram.Update(d.Nanoseconds()) @@ -116,67 +86,67 @@ func (t *StandardTimer) Update(d time.Duration) { // UpdateSince update the duration of an event that started at a time and ends now. // The record uses nanoseconds. -func (t *StandardTimer) UpdateSince(ts time.Time) { +func (t *Timer) UpdateSince(ts time.Time) { t.Update(time.Since(ts)) } -// timerSnapshot is a read-only copy of another Timer. -type timerSnapshot struct { +// TimerSnapshot is a read-only copy of another Timer. +type TimerSnapshot struct { histogram HistogramSnapshot - meter MeterSnapshot + meter *MeterSnapshot } // Count returns the number of events recorded at the time the snapshot was // taken. -func (t *timerSnapshot) Count() int64 { return t.histogram.Count() } +func (t *TimerSnapshot) Count() int64 { return t.histogram.Count() } // Max returns the maximum value at the time the snapshot was taken. -func (t *timerSnapshot) Max() int64 { return t.histogram.Max() } +func (t *TimerSnapshot) Max() int64 { return t.histogram.Max() } // Size returns the size of the sample at the time the snapshot was taken. -func (t *timerSnapshot) Size() int { return t.histogram.Size() } +func (t *TimerSnapshot) Size() int { return t.histogram.Size() } // Mean returns the mean value at the time the snapshot was taken. -func (t *timerSnapshot) Mean() float64 { return t.histogram.Mean() } +func (t *TimerSnapshot) Mean() float64 { return t.histogram.Mean() } // Min returns the minimum value at the time the snapshot was taken. -func (t *timerSnapshot) Min() int64 { return t.histogram.Min() } +func (t *TimerSnapshot) Min() int64 { return t.histogram.Min() } // Percentile returns an arbitrary percentile of sampled values at the time the // snapshot was taken. -func (t *timerSnapshot) Percentile(p float64) float64 { +func (t *TimerSnapshot) Percentile(p float64) float64 { return t.histogram.Percentile(p) } // Percentiles returns a slice of arbitrary percentiles of sampled values at // the time the snapshot was taken. -func (t *timerSnapshot) Percentiles(ps []float64) []float64 { +func (t *TimerSnapshot) Percentiles(ps []float64) []float64 { return t.histogram.Percentiles(ps) } // Rate1 returns the one-minute moving average rate of events per second at the // time the snapshot was taken. -func (t *timerSnapshot) Rate1() float64 { return t.meter.Rate1() } +func (t *TimerSnapshot) Rate1() float64 { return t.meter.Rate1() } // Rate5 returns the five-minute moving average rate of events per second at // the time the snapshot was taken. -func (t *timerSnapshot) Rate5() float64 { return t.meter.Rate5() } +func (t *TimerSnapshot) Rate5() float64 { return t.meter.Rate5() } // Rate15 returns the fifteen-minute moving average rate of events per second // at the time the snapshot was taken. -func (t *timerSnapshot) Rate15() float64 { return t.meter.Rate15() } +func (t *TimerSnapshot) Rate15() float64 { return t.meter.Rate15() } // RateMean returns the meter's mean rate of events per second at the time the // snapshot was taken. -func (t *timerSnapshot) RateMean() float64 { return t.meter.RateMean() } +func (t *TimerSnapshot) RateMean() float64 { return t.meter.RateMean() } // StdDev returns the standard deviation of the values at the time the snapshot // was taken. -func (t *timerSnapshot) StdDev() float64 { return t.histogram.StdDev() } +func (t *TimerSnapshot) StdDev() float64 { return t.histogram.StdDev() } // Sum returns the sum at the time the snapshot was taken. -func (t *timerSnapshot) Sum() int64 { return t.histogram.Sum() } +func (t *TimerSnapshot) Sum() int64 { return t.histogram.Sum() } // Variance returns the variance of the values at the time the snapshot was // taken. -func (t *timerSnapshot) Variance() float64 { return t.histogram.Variance() } +func (t *TimerSnapshot) Variance() float64 { return t.histogram.Variance() } diff --git a/metrics/writer.go b/metrics/writer.go index c211c5046b7d..2a41f8e1fe36 100644 --- a/metrics/writer.go +++ b/metrics/writer.go @@ -26,22 +26,22 @@ func WriteOnce(r Registry, w io.Writer) { slices.SortFunc(namedMetrics, namedMetric.cmp) for _, namedMetric := range namedMetrics { switch metric := namedMetric.m.(type) { - case Counter: + case *Counter: fmt.Fprintf(w, "counter %s\n", namedMetric.name) fmt.Fprintf(w, " count: %9d\n", metric.Snapshot().Count()) - case CounterFloat64: + case *CounterFloat64: fmt.Fprintf(w, "counter %s\n", namedMetric.name) fmt.Fprintf(w, " count: %f\n", metric.Snapshot().Count()) - case Gauge: + case *Gauge: fmt.Fprintf(w, "gauge %s\n", namedMetric.name) fmt.Fprintf(w, " value: %9d\n", metric.Snapshot().Value()) - case GaugeFloat64: + case *GaugeFloat64: fmt.Fprintf(w, "gauge %s\n", namedMetric.name) fmt.Fprintf(w, " value: %f\n", metric.Snapshot().Value()) - case GaugeInfo: + case *GaugeInfo: fmt.Fprintf(w, "gauge %s\n", namedMetric.name) fmt.Fprintf(w, " value: %s\n", metric.Snapshot().Value().String()) - case Healthcheck: + case *Healthcheck: metric.Check() fmt.Fprintf(w, "healthcheck %s\n", namedMetric.name) fmt.Fprintf(w, " error: %v\n", metric.Error()) @@ -59,7 +59,7 @@ func WriteOnce(r Registry, w io.Writer) { fmt.Fprintf(w, " 95%%: %12.2f\n", ps[2]) fmt.Fprintf(w, " 99%%: %12.2f\n", ps[3]) fmt.Fprintf(w, " 99.9%%: %12.2f\n", ps[4]) - case Meter: + case *Meter: m := metric.Snapshot() fmt.Fprintf(w, "meter %s\n", namedMetric.name) fmt.Fprintf(w, " count: %9d\n", m.Count()) @@ -67,7 +67,7 @@ func WriteOnce(r Registry, w io.Writer) { fmt.Fprintf(w, " 5-min rate: %12.2f\n", m.Rate5()) fmt.Fprintf(w, " 15-min rate: %12.2f\n", m.Rate15()) fmt.Fprintf(w, " mean rate: %12.2f\n", m.RateMean()) - case Timer: + case *Timer: t := metric.Snapshot() ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) fmt.Fprintf(w, "timer %s\n", namedMetric.name) diff --git a/p2p/metrics.go b/p2p/metrics.go index 0f1f42b08a48..febff6c4ece9 100644 --- a/p2p/metrics.go +++ b/p2p/metrics.go @@ -42,7 +42,7 @@ type meteredConn struct { // returns the original object. func newMeteredConn(conn net.Conn, ingress bool) net.Conn { // Short circuit if metrics are disabled - if !metrics.Enabled { + if !metrics.Enabled() { return conn } // Otherwise bump the connection counters and wrap the connection From 21b05243b61acb92ee53a807fe4f68c34a50a3cf Mon Sep 17 00:00:00 2001 From: benjamin202410 Date: Thu, 19 Dec 2024 01:17:29 -0800 Subject: [PATCH 381/479] Merge from master mining time patch (#767) * merge from master * close channel * close channel --------- Co-authored-by: liam.lai --- consensus/XDPoS/XDPoS.go | 11 ++++- consensus/XDPoS/engines/engine_v2/engine.go | 11 ++++- miner/worker.go | 51 ++++++++++++++++++--- 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/consensus/XDPoS/XDPoS.go b/consensus/XDPoS/XDPoS.go index d6e532fc129e..5001abe4e8f1 100644 --- a/consensus/XDPoS/XDPoS.go +++ b/consensus/XDPoS/XDPoS.go @@ -41,6 +41,7 @@ import ( const ( ExtraFieldCheck = true SkipExtraFieldCheck = false + newRoundChanSize = 1 ) func (x *XDPoS) SigHash(header *types.Header) (hash common.Hash) { @@ -64,6 +65,8 @@ type XDPoS struct { // Share Channel MinePeriodCh chan int // Miner wait Period Channel + NewRoundCh chan types.Round // Miner use this channel to trigger worker to commitNewWork + // Trading and lending service GetXDCXService func() utils.TradingService GetLendingService func() utils.LendingService @@ -104,6 +107,7 @@ func New(chainConfig *params.ChainConfig, db ethdb.Database) *XDPoS { log.Info("xdc config loading", "v2 config", config.V2) minePeriodCh := make(chan int) + newRoundCh := make(chan types.Round, newRoundChanSize) // Allocate the snapshot caches and create the engine signingTxsCache, _ := lru.New(utils.BlockSignersCacheLimit) @@ -113,10 +117,11 @@ func New(chainConfig *params.ChainConfig, db ethdb.Database) *XDPoS { db: db, MinePeriodCh: minePeriodCh, + NewRoundCh: newRoundCh, signingTxsCache: signingTxsCache, EngineV1: engine_v1.New(chainConfig, db), - EngineV2: engine_v2.New(chainConfig, db, minePeriodCh), + EngineV2: engine_v2.New(chainConfig, db, minePeriodCh, newRoundCh), } } @@ -131,6 +136,7 @@ func NewFaker(db ethdb.Database, chainConfig *params.ChainConfig) *XDPoS { } minePeriodCh := make(chan int) + newRoundCh := make(chan types.Round, newRoundChanSize) // Allocate the snapshot caches and create the engine signingTxsCache, _ := lru.New(utils.BlockSignersCacheLimit) @@ -140,13 +146,14 @@ func NewFaker(db ethdb.Database, chainConfig *params.ChainConfig) *XDPoS { db: db, MinePeriodCh: minePeriodCh, + NewRoundCh: newRoundCh, GetXDCXService: func() utils.TradingService { return nil }, GetLendingService: func() utils.LendingService { return nil }, signingTxsCache: signingTxsCache, EngineV1: engine_v1.NewFaker(db, chainConfig), - EngineV2: engine_v2.New(chainConfig, db, minePeriodCh), + EngineV2: engine_v2.New(chainConfig, db, minePeriodCh, newRoundCh), } return fakeEngine } diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index b808a56236f1..25515c3f7acb 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -48,6 +48,7 @@ type XDPoS_v2 struct { BroadcastCh chan interface{} minePeriodCh chan int + newRoundCh chan types.Round timeoutWorker *countdown.CountdownTimer // Timer to generate broadcast timeout msg if threashold reached timeoutCount int // number of timeout being sent @@ -71,7 +72,7 @@ type XDPoS_v2 struct { votePoolCollectionTime time.Time } -func New(chainConfig *params.ChainConfig, db ethdb.Database, minePeriodCh chan int) *XDPoS_v2 { +func New(chainConfig *params.ChainConfig, db ethdb.Database, minePeriodCh chan int, newRoundCh chan types.Round) *XDPoS_v2 { config := chainConfig.XDPoS // Setup timeoutTimer duration := time.Duration(config.V2.CurrentConfig.TimeoutPeriod) * time.Second @@ -100,6 +101,7 @@ func New(chainConfig *params.ChainConfig, db ethdb.Database, minePeriodCh chan i timeoutWorker: timeoutTimer, BroadcastCh: make(chan interface{}), minePeriodCh: minePeriodCh, + newRoundCh: newRoundCh, round2epochBlockInfo: round2epochBlockInfo, @@ -902,6 +904,7 @@ func (x *XDPoS_v2) processQC(blockChainReader consensus.ChainReader, incomingQuo 1. Set currentRound = QC round + 1 (or TC round +1) 2. Reset timer 3. Reset vote and timeout Pools +4. Send signal to miner */ func (x *XDPoS_v2) setNewRound(blockChainReader consensus.ChainReader, round types.Round) { log.Info("[setNewRound] new round and reset pools and workers", "round", round) @@ -911,6 +914,12 @@ func (x *XDPoS_v2) setNewRound(blockChainReader consensus.ChainReader, round typ x.timeoutPool.Clear() // don't need to clean vote pool, we have other process to clean and it's not good to clean here, some edge case may break // for example round gets bump during collecting vote, so we have to keep vote. + + // send signal to newRoundCh, but if full don't send + select { + case x.newRoundCh <- round: + default: + } } func (x *XDPoS_v2) broadcastToBftChannel(msg interface{}) { diff --git a/miner/worker.go b/miner/worker.go index 9aa9d4589eea..6514b48811fc 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/binary" "errors" + "fmt" "math/big" "sync" "sync/atomic" @@ -116,7 +117,9 @@ type worker struct { chainHeadSub event.Subscription chainSideCh chan core.ChainSideEvent chainSideSub event.Subscription - wg sync.WaitGroup + resetCh chan time.Duration // Channel to request timer resets + + wg sync.WaitGroup agents map[Agent]struct{} recv chan *Result @@ -158,6 +161,7 @@ func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase com txsCh: make(chan core.NewTxsEvent, txChanSize), chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize), + resetCh: make(chan time.Duration, 1), chainDb: eth.ChainDb(), recv: make(chan *Result, resultQueueSize), chain: eth.BlockChain(), @@ -273,16 +277,30 @@ func (w *worker) update() { minePeriod := 2 MinePeriodCh := w.engine.(*XDPoS.XDPoS).MinePeriodCh defer close(MinePeriodCh) + NewRoundCh := w.engine.(*XDPoS.XDPoS).NewRoundCh + defer close(NewRoundCh) timeout := time.NewTimer(time.Duration(minePeriod) * time.Second) - c := make(chan struct{}) + defer timeout.Stop() + c := make(chan struct{}, 1) + defer close(c) finish := make(chan struct{}) defer close(finish) - defer timeout.Stop() + go func() { for { // A real event arrived, process interesting content select { + case d := <-w.resetCh: + // Reset the timer to the new duration. + if !timeout.Stop() { + // Drain the timer channel if it had already expired. + select { + case <-timeout.C: + default: + } + } + timeout.Reset(d) case <-timeout.C: c <- struct{}{} case <-finish: @@ -296,18 +314,26 @@ func (w *worker) update() { case v := <-MinePeriodCh: log.Info("[worker] update wait period", "period", v) minePeriod = v - timeout.Reset(time.Duration(minePeriod) * time.Second) + w.resetCh <- time.Duration(minePeriod) * time.Second case <-c: if atomic.LoadInt32(&w.mining) == 1 { w.commitNewWork() } - timeout.Reset(time.Duration(minePeriod) * time.Second) + resetTime := getResetTime(w.chain, minePeriod) + w.resetCh <- resetTime // Handle ChainHeadEvent case <-w.chainHeadCh: w.commitNewWork() - timeout.Reset(time.Duration(minePeriod) * time.Second) + resetTime := getResetTime(w.chain, minePeriod) + w.resetCh <- resetTime + + // Handle new round + case <-NewRoundCh: + w.commitNewWork() + resetTime := getResetTime(w.chain, minePeriod) + w.resetCh <- resetTime // Handle ChainSideEvent case <-w.chainSideCh: @@ -354,6 +380,19 @@ func (w *worker) update() { } } +func getResetTime(chain *core.BlockChain, minePeriod int) time.Duration { + minePeriodDuration := time.Duration(minePeriod) * time.Second + currentBlockTime := chain.CurrentBlock().Time().Int64() + nowTime := time.Now().UnixMilli() + resetTime := time.Duration(currentBlockTime)*time.Second + minePeriodDuration - time.Duration(nowTime)*time.Millisecond + // in case the current block time is not very accurate + if resetTime > minePeriodDuration || resetTime <= 0 { + resetTime = minePeriodDuration + } + log.Debug("[update] Miner worker timer reset", "resetMilliseconds", resetTime.Milliseconds(), "minePeriodSec", minePeriod, "currentBlockTimeSec", fmt.Sprintf("%d", currentBlockTime), "currentSystemTimeSec", fmt.Sprintf("%d.%03d", nowTime/1000, nowTime%1000)) + return resetTime +} + func (w *worker) wait() { for { mustCommitNewWork := true From 7491a7ba7479de1db0358369abc4aea8e68733ff Mon Sep 17 00:00:00 2001 From: JukLee0ira Date: Fri, 20 Dec 2024 17:30:15 +0800 Subject: [PATCH 382/479] all: improve EstimateGas API (#20830) --- accounts/abi/abi_test.go | 32 ++++ accounts/abi/bind/backends/simulated.go | 74 +++++++-- accounts/abi/bind/backends/simulated_test.go | 154 +++++++++++++++++++ core/blockchain.go | 2 - core/error.go | 16 ++ core/state_processor.go | 12 +- core/state_transition.go | 92 ++++++----- core/token_validator.go | 4 +- core/vm/instructions.go | 2 +- eth/api_tracer.go | 10 +- eth/tracers/testing/calltrace_test.go | 4 +- eth/tracers/tracers_test.go | 6 +- internal/ethapi/api.go | 103 ++++++++----- les/odr_test.go | 8 +- light/odr_test.go | 4 +- tests/state_test_util.go | 2 +- 16 files changed, 404 insertions(+), 121 deletions(-) create mode 100644 accounts/abi/bind/backends/simulated_test.go diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index 4cf5668ff641..790505b8c043 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -19,6 +19,7 @@ package abi import ( "bytes" "encoding/hex" + "errors" "fmt" "log" "math/big" @@ -713,3 +714,34 @@ func TestABI_MethodById(t *testing.T) { } } + +func TestUnpackRevert(t *testing.T) { + t.Parallel() + + var cases = []struct { + input string + expect string + expectErr error + }{ + {"", "", errors.New("invalid data for unpacking")}, + {"08c379a1", "", errors.New("invalid data for unpacking")}, + {"08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d72657665727420726561736f6e00000000000000000000000000000000000000", "revert reason", nil}, + } + for index, c := range cases { + t.Run(fmt.Sprintf("case %d", index), func(t *testing.T) { + got, err := UnpackRevert(common.Hex2Bytes(c.input)) + if c.expectErr != nil { + if err == nil { + t.Fatalf("Expected non-nil error") + } + if err.Error() != c.expectErr.Error() { + t.Fatalf("Expected error mismatch, want %v, got %v", c.expectErr, err) + } + return + } + if c.expect != got { + t.Fatalf("Output mismatch, want %v, got %v", c.expect, got) + } + }) + } +} diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index f6b4a13da6fc..12005705c710 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -29,6 +29,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/XDCx" "github.com/XinFinOrg/XDPoSChain/XDCxlending" "github.com/XinFinOrg/XDPoSChain/accounts" + "github.com/XinFinOrg/XDPoSChain/accounts/abi" "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" "github.com/XinFinOrg/XDPoSChain/accounts/keystore" "github.com/XinFinOrg/XDPoSChain/common" @@ -54,7 +55,6 @@ import ( var _ bind.ContractBackend = (*SimulatedBackend)(nil) var errBlockNumberUnsupported = errors.New("SimulatedBackend cannot access blocks other than the latest block") -var errGasEstimationFailed = errors.New("gas required exceeds allowance or always failing transaction") // SimulatedBackend implements bind.ContractBackend, simulating a blockchain in // the background. Its main purpose is to allow easily testing contract bindings. @@ -161,6 +161,12 @@ func NewSimulatedBackend(alloc core.GenesisAlloc) *SimulatedBackend { return backend } +// Close terminates the underlying blockchain's update loop. +func (b *SimulatedBackend) Close() error { + b.blockchain.Stop() + return nil +} + // Commit imports all the pending transactions as a single block and starts a // fresh new state. func (b *SimulatedBackend) Commit() { @@ -290,8 +296,11 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call XDPoSChain.Cal if err != nil { return nil, err } - rval, _, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state) - return rval, err + res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state) + if err != nil { + return nil, err + } + return res.Return(), nil } // PendingCallContract executes a contract call on the pending state. @@ -300,8 +309,11 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call XDPoSCh defer b.mu.Unlock() defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot()) - rval, _, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) - return rval, err + res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) + if err != nil { + return nil, err + } + return res.Return(), nil } // PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving @@ -348,22 +360,33 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call XDPoSChain.Call cap = hi // Create a helper to check if a gas allowance results in an executable transaction - executable := func(gas uint64) bool { + executable := func(gas uint64) (bool, *core.ExecutionResult, error) { call.Gas = gas snapshot := b.pendingState.Snapshot() - _, _, failed, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) + res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) b.pendingState.RevertToSnapshot(snapshot) - if err != nil || failed { - return false + if err != nil { + if err == core.ErrIntrinsicGas { + return true, nil, nil // Special case, raise gas limit + } + return true, nil, err // Bail out } - return true + return res.Failed(), res, nil } // Execute the binary search and hone in on an executable gas limit for lo+1 < hi { mid := (hi + lo) / 2 - if !executable(mid) { + failed, _, err := executable(mid) + + // If the error is not nil(consensus error), it means the provided message + // call or transaction will never be accepted no matter how much gas it is + // assigned. Return the error directly, don't struggle any more + if err != nil { + return 0, err + } + if failed { lo = mid } else { hi = mid @@ -371,8 +394,25 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call XDPoSChain.Call } // Reject the transaction as invalid if it still fails at the highest allowance if hi == cap { - if !executable(hi) { - return 0, errGasEstimationFailed + failed, result, err := executable(hi) + if err != nil { + return 0, err + } + if failed { + if result != nil && result.Err != vm.ErrOutOfGas { + errMsg := fmt.Sprintf("always failing transaction (%v)", result.Err) + if len(result.Revert()) > 0 { + ret, err := abi.UnpackRevert(result.Revert()) + if err != nil { + errMsg += fmt.Sprintf(" (%#x)", result.Revert()) + } else { + errMsg += fmt.Sprintf(" (%s)", ret) + } + } + return 0, errors.New(errMsg) + } + // Otherwise, the specified gas cap is too low + return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap) } } return hi, nil @@ -380,10 +420,10 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call XDPoSChain.Call // callContract implements common code between normal and pending contract calls. // state is modified during execution, make sure to copy it if necessary. -func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.CallMsg, block *types.Block, statedb *state.StateDB) (ret []byte, usedGas uint64, failed bool, err error) { +func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.CallMsg, block *types.Block, statedb *state.StateDB) (*core.ExecutionResult, error) { // Gas prices post 1559 need to be initialized if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) { - return nil, 0, false, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") + return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") } head := b.blockchain.CurrentHeader() if !b.blockchain.Config().IsEIP1559(head.Number) { @@ -438,8 +478,8 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.Cal vmenv := vm.NewEVM(evmContext, txContext, statedb, nil, b.config, vm.Config{NoBaseFee: true}) gaspool := new(core.GasPool).AddGas(math.MaxUint64) owner := common.Address{} - ret, usedGas, failed, err, _ = core.NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner) - return + res, err, _ := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner) + return res, err } // SendTransaction updates the pending block to include the given transaction. diff --git a/accounts/abi/bind/backends/simulated_test.go b/accounts/abi/bind/backends/simulated_test.go new file mode 100644 index 000000000000..568189852310 --- /dev/null +++ b/accounts/abi/bind/backends/simulated_test.go @@ -0,0 +1,154 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package backends + +import ( + "context" + "errors" + "math/big" + "strings" + "testing" + + "github.com/XinFinOrg/XDPoSChain" + "github.com/XinFinOrg/XDPoSChain/accounts/abi" + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/params" +) + +func TestSimulatedBackend_EstimateGas(t *testing.T) { + /* + pragma solidity ^0.6.4; + contract GasEstimation { + function PureRevert() public { revert(); } + function Revert() public { revert("revert reason");} + function OOG() public { for (uint i = 0; ; i++) {}} + function Assert() public { assert(false);} + function Valid() public {} + }*/ + const contractAbi = "[{\"inputs\":[],\"name\":\"Assert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"OOG\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PureRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Revert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Valid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" + const contractBin = "0x60806040523480156100115760006000fd5b50610017565b61016e806100266000396000f3fe60806040523480156100115760006000fd5b506004361061005c5760003560e01c806350f6fe3414610062578063aa8b1d301461006c578063b9b046f914610076578063d8b9839114610080578063e09fface1461008a5761005c565b60006000fd5b61006a610094565b005b6100746100ad565b005b61007e6100b5565b005b6100886100c2565b005b610092610135565b005b6000600090505b5b808060010191505061009b565b505b565b60006000fd5b565b600015156100bf57fe5b5b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f72657665727420726561736f6e0000000000000000000000000000000000000081526020015060200191505060405180910390fd5b565b5b56fea2646970667358221220345bbcbb1a5ecf22b53a78eaebf95f8ee0eceff6d10d4b9643495084d2ec934a64736f6c63430006040033" + + key, _ := crypto.GenerateKey() + addr := crypto.PubkeyToAddress(key.PublicKey) + opts := bind.NewKeyedTransactor(key) + + sim := NewXDCSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(params.Ether)}}, 10000000, ¶ms.ChainConfig{ + ConstantinopleBlock: big.NewInt(0), + XDPoS: ¶ms.XDPoSConfig{ + Epoch: 900, + SkipV1Validation: true, + V2: ¶ms.V2{ + SwitchBlock: big.NewInt(900), + CurrentConfig: params.UnitTestV2Configs[0], + }, + }, + }) + + defer sim.Close() + + parsed, _ := abi.JSON(strings.NewReader(contractAbi)) + contractAddr, _, _, _ := bind.DeployContract(opts, parsed, common.FromHex(contractBin), sim) + sim.Commit() + + var cases = []struct { + name string + message XDPoSChain.CallMsg + expect uint64 + expectError error + }{ + {"plain transfer(valid)", XDPoSChain.CallMsg{ + From: addr, + To: &addr, + Gas: 0, + GasPrice: big.NewInt(0), + Value: big.NewInt(1), + Data: nil, + }, params.TxGas, nil}, + + {"plain transfer(invalid)", XDPoSChain.CallMsg{ + From: addr, + To: &contractAddr, + Gas: 0, + GasPrice: big.NewInt(0), + Value: big.NewInt(1), + Data: nil, + }, 0, errors.New("always failing transaction (execution reverted)")}, + + {"Revert", XDPoSChain.CallMsg{ + From: addr, + To: &contractAddr, + Gas: 0, + GasPrice: big.NewInt(0), + Value: nil, + Data: common.Hex2Bytes("d8b98391"), + }, 0, errors.New("always failing transaction (execution reverted) (revert reason)")}, + + {"PureRevert", XDPoSChain.CallMsg{ + From: addr, + To: &contractAddr, + Gas: 0, + GasPrice: big.NewInt(0), + Value: nil, + Data: common.Hex2Bytes("aa8b1d30"), + }, 0, errors.New("always failing transaction (execution reverted)")}, + + {"OOG", XDPoSChain.CallMsg{ + From: addr, + To: &contractAddr, + Gas: 100000, + GasPrice: big.NewInt(0), + Value: nil, + Data: common.Hex2Bytes("50f6fe34"), + }, 0, errors.New("gas required exceeds allowance (100000)")}, + + {"Assert", XDPoSChain.CallMsg{ + From: addr, + To: &contractAddr, + Gas: 100000, + GasPrice: big.NewInt(0), + Value: nil, + Data: common.Hex2Bytes("b9b046f9"), + }, 0, errors.New("always failing transaction (invalid opcode: INVALID)")}, + + {"Valid", XDPoSChain.CallMsg{ + From: addr, + To: &contractAddr, + Gas: 100000, + GasPrice: big.NewInt(0), + Value: nil, + Data: common.Hex2Bytes("e09fface"), + }, 21483, nil}, + } + for _, c := range cases { + got, err := sim.EstimateGas(context.Background(), c.message) + if c.expectError != nil { + if err == nil { + t.Fatalf("Expect error, got nil") + } + if c.expectError.Error() != err.Error() { + t.Fatalf("Expect error, want %v, got %v", c.expectError, err) + } + continue + } + if got != c.expect { + t.Fatalf("Gas estimation mismatch, want %d, got %d", c.expect, got) + } + } +} diff --git a/core/blockchain.go b/core/blockchain.go index 7aebc61155bd..25ec81a7aa75 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -82,8 +82,6 @@ var ( blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil) CheckpointCh = make(chan int) - - ErrNoGenesis = errors.New("Genesis not found in chain") ) const ( diff --git a/core/error.go b/core/error.go index 4eb7eebf0ec9..67bb75730e84 100644 --- a/core/error.go +++ b/core/error.go @@ -29,6 +29,18 @@ var ( // ErrBlacklistedHash is returned if a block to import is on the blacklist. ErrBlacklistedHash = errors.New("blacklisted hash") + // ErrNoGenesis is returned when there is no Genesis Block. + ErrNoGenesis = errors.New("genesis not found in chain") +) + +// List of evm-call-message pre-checking errors. All state transtion messages will +// be pre-checked before execution. If any invalidation detected, the corresponding +// error should be returned which is defined here. +// +// - If the pre-checking happens in the miner, then the transaction won't be packed. +// - If the pre-checking happens in the block processing procedure, then a "BAD BLOCk" +// error should be emitted. +var ( // ErrNonceTooLow is returned if the nonce of a transaction is lower than the // one present in the local chain. ErrNonceTooLow = errors.New("nonce too low") @@ -45,6 +57,10 @@ var ( // by a transaction is higher than what's left in the block. ErrGasLimitReached = errors.New("gas limit reached") + // ErrInsufficientFundsForTransfer is returned if the transaction sender doesn't + // have enough funds for transfer(topmost call only). + ErrInsufficientFundsForTransfer = errors.New("insufficient funds for transfer") + // ErrMaxInitCodeSizeExceeded is returned if creation transaction provides the init code bigger // than init code size limit. ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded") diff --git a/core/state_processor.go b/core/state_processor.go index 8234e36c8f7d..5fc975c7c72d 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -403,7 +403,7 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* // End Bypass blacklist address // Apply the transaction to the current state (included in the env) - _, gas, failed, err, _ := ApplyMessage(evm, msg, gp, coinbaseOwner) + result, err, _ := ApplyMessage(evm, msg, gp, coinbaseOwner) if err != nil { return nil, 0, err, false @@ -416,18 +416,18 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* } else { root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes() } - *usedGas += gas + *usedGas += result.UsedGas // Create a new receipt for the transaction, storing the intermediate root and gas used // by the tx. receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas} - if failed { + if result.Failed() { receipt.Status = types.ReceiptStatusFailed } else { receipt.Status = types.ReceiptStatusSuccessful } receipt.TxHash = tx.Hash() - receipt.GasUsed = gas + receipt.GasUsed = result.UsedGas // If the transaction created a contract, store the creation address in the receipt. if msg.To() == nil { @@ -440,10 +440,10 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* receipt.BlockHash = blockHash receipt.BlockNumber = blockNumber receipt.TransactionIndex = uint(statedb.TxIndex()) - if balanceFee != nil && failed { + if balanceFee != nil && result.Failed() { state.PayFeeWithTRC21TxFail(statedb, msg.From(), *to) } - return receipt, gas, err, balanceFee != nil + return receipt, result.UsedGas, err, balanceFee != nil } func getCoinbaseOwner(bc *BlockChain, statedb *state.StateDB, header *types.Header, author *common.Address) common.Address { diff --git a/core/state_transition.go b/core/state_transition.go index 05db0e7d9501..42e044a8de73 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -27,8 +27,8 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" + "github.com/holiman/uint256" ) var emptyCodeHash = crypto.Keccak256Hash(nil) @@ -73,7 +73,6 @@ type StateTransition struct { // Message represents a message sent to a contract. type Message interface { From() common.Address - //FromFrontier() (common.Address, error) To() *common.Address GasPrice() *big.Int @@ -89,6 +88,35 @@ type Message interface { AccessList() types.AccessList } +// ExecutionResult includes all output after executing given evm +// message no matter the execution itself is successful or not. +type ExecutionResult struct { + UsedGas uint64 // Total used gas but include the refunded gas + Err error // Any error encountered during the execution(listed in core/vm/errors.go) + ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode) +} + +// Failed returns the indicator whether the execution is successful or not +func (result *ExecutionResult) Failed() bool { return result.Err != nil } + +// Return is a helper function to help caller distinguish between revert reason +// and function return. Return returns the data after execution if no error occurs. +func (result *ExecutionResult) Return() []byte { + if result.Err != nil { + return nil + } + return common.CopyBytes(result.ReturnData) +} + +// Revert returns the concrete revert reason if the execution is aborted by `REVERT` +// opcode. Note the reason can be nil if no data supplied with revert opcode. +func (result *ExecutionResult) Revert() []byte { + if result.Err != vm.ErrExecutionReverted { + return nil + } + return common.CopyBytes(result.ReturnData) +} + // IntrinsicGas computes the 'intrinsic gas' for a message with the given data. func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, isHomestead bool, isEIP3860 bool) (uint64, error) { // Set the starting gas for the raw transaction @@ -166,7 +194,7 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition // the gas used (which includes gas refunds) and an error if it failed. An error always // indicates a core error meaning that the message would always fail for that particular // state and would never be accepted within a block. -func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool, owner common.Address) ([]byte, uint64, bool, error, error) { +func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool, owner common.Address) (*ExecutionResult, error, error) { return NewStateTransition(evm, msg, gp).TransitionDb(owner) } @@ -213,7 +241,7 @@ func (st *StateTransition) buyGas() error { return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), have, want) } } else if balanceTokenFee.Cmp(mgval) < 0 { - return errInsufficientBalanceForGas + return ErrInsufficientFunds } if err := st.gp.SubGas(st.msg.Gas()); err != nil { return err @@ -289,7 +317,7 @@ func (st *StateTransition) preCheck() error { // // However if any consensus issue encountered, return the error directly with // nil evm execution result. -func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedGas uint64, failed bool, err error, vmErr error) { +func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult, error, error) { // First check this message satisfies all consensus rules before // applying the message. The rules include these clauses // @@ -301,8 +329,8 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG // 6. caller has enough balance to cover asset transfer for **topmost** call // Check clauses 1-3, buy gas if everything is correct - if err = st.preCheck(); err != nil { - return nil, 0, false, err, nil + if err := st.preCheck(); err != nil { + return nil, err, nil } var ( @@ -317,16 +345,16 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG // Check clauses 4-5, subtract intrinsic gas if everything is correct gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead, rules.IsEIP1559) if err != nil { - return nil, 0, false, err, nil + return nil, err, nil } if st.gas < gas { - return nil, 0, false, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas), nil + return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas), nil } st.gas -= gas // Check whether the init code size has been exceeded. if rules.IsEIP1559 && contractCreation && len(st.data) > params.MaxInitCodeSize { - return nil, 0, false, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(st.data), params.MaxInitCodeSize), nil + return nil, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(st.data), params.MaxInitCodeSize), nil } // Execute the preparatory steps for state transition which includes: @@ -334,35 +362,25 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG // - reset transient storage(eip 1153) st.state.Prepare(rules, msg.From(), st.evm.Context.Coinbase, msg.To(), vm.ActivePrecompiles(rules), msg.AccessList()) + // Check clause 6 + value, overflow := uint256.FromBig(msg.Value()) + if overflow { + return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From().Hex()), nil + } + if !value.IsZero() && !st.evm.Context.CanTransfer(st.state, msg.From(), value.ToBig()) { + return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From().Hex()), nil + } + var ( - evm = st.evm - // vm errors do not effect consensus and are therefor - // not assigned to err, except for insufficient balance - // error. - vmerr error + ret []byte + vmerr error // vm errors do not effect consensus and are therefore not assigned to err ) - // for debugging purpose - // TODO: clean it after fixing the issue https://github.com/XinFinOrg/XDPoSChain/issues/401 - var contractAction string - nonce := uint64(1) if contractCreation { - ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value) - contractAction = "contract creation" + ret, _, st.gas, vmerr = st.evm.Create(sender, st.data, st.gas, st.value) } else { // Increment the nonce for the next transaction - nonce = st.state.GetNonce(sender.Address()) + 1 - st.state.SetNonce(sender.Address(), nonce) - ret, st.gas, vmerr = evm.Call(sender, st.to().Address(), st.data, st.gas, st.value) - contractAction = "contract call" - } - if vmerr != nil { - log.Debug("VM returned with error", "action", contractAction, "contract address", st.to().Address(), "gas", st.gas, "gasPrice", st.gasPrice, "nonce", nonce, "err", vmerr) - // The only possible consensus-error would be if there wasn't - // sufficient balance to make the transfer happen. The first - // balance transfer may never fail. - if vmerr == vm.ErrInsufficientBalance { - return nil, 0, false, vmerr, nil - } + st.state.SetNonce(sender.Address(), st.state.GetNonce(sender.Address())+1) + ret, st.gas, vmerr = st.evm.Call(sender, st.to().Address(), st.data, st.gas, st.value) } if !eip3529 { // Before EIP-3529: refunds were capped to gasUsed / 2 @@ -384,7 +402,11 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip)) } - return ret, st.gasUsed(), vmerr != nil, nil, vmerr + return &ExecutionResult{ + UsedGas: st.gasUsed(), + Err: vmerr, + ReturnData: ret, + }, nil, vmerr } func (st *StateTransition) refundGas(refundQuotient uint64) { diff --git a/core/token_validator.go b/core/token_validator.go index 628db3c98a98..e888f37b102e 100644 --- a/core/token_validator.go +++ b/core/token_validator.go @@ -117,11 +117,11 @@ func CallContractWithState(call ethereum.CallMsg, chain consensus.ChainContext, vmenv := vm.NewEVM(evmContext, txContext, statedb, nil, chain.Config(), vm.Config{}) gaspool := new(GasPool).AddGas(1000000) owner := common.Address{} - rval, _, _, err, _ := NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner) + result, err, _ := NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner) if err != nil { return nil, err } - return rval, err + return result.Return(), err } // make sure that balance of token is at slot 0 diff --git a/core/vm/instructions.go b/core/vm/instructions.go index b51bb870d79d..6a0ddea1b190 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -21,8 +21,8 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/types" - "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/params" "github.com/holiman/uint256" ) diff --git a/eth/api_tracer.go b/eth/api_tracer.go index 88d2cf4881b4..35297da7f880 100644 --- a/eth/api_tracer.go +++ b/eth/api_tracer.go @@ -526,7 +526,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, vmenv := vm.NewEVM(blockCtx, txContext, statedb, XDCxState, api.config, vm.Config{}) owner := common.Address{} - if _, _, _, err, _ := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), owner); err != nil { + if _, err, _ := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), owner); err != nil { failed = err break } @@ -750,7 +750,7 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, t statedb.SetTxContext(txctx.TxHash, txctx.TxIndex) owner := common.Address{} - ret, gas, failed, err, _ := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), owner) + result, err, _ := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), owner) if err != nil { return nil, fmt.Errorf("tracing failed: %v", err) } @@ -758,9 +758,9 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, t switch tracer := tracer.(type) { case *vm.StructLogger: return ðapi.ExecutionResult{ - Gas: gas, - Failed: failed, - ReturnValue: fmt.Sprintf("%x", ret), + Gas: result.UsedGas, + Failed: result.Failed(), + ReturnValue: fmt.Sprintf("%x", result.Return()), StructLogs: ethapi.FormatLogs(tracer.StructLogs()), }, nil diff --git a/eth/tracers/testing/calltrace_test.go b/eth/tracers/testing/calltrace_test.go index aaea4f375d41..35d9407cd93a 100644 --- a/eth/tracers/testing/calltrace_test.go +++ b/eth/tracers/testing/calltrace_test.go @@ -120,7 +120,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { t.Fatalf("failed to prepare transaction for tracing: %v", err) } st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - if _, _, _, err, _ = st.TransitionDb(common.Address{}); err != nil { + if _, err, _ = st.TransitionDb(common.Address{}); err != nil { t.Fatalf("failed to execute transaction: %v", err) } // Retrieve the trace result and compare against the etalon @@ -231,7 +231,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { evm := vm.NewEVM(context, txContext, statedb, nil, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) snap := statedb.Snapshot() st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - if _, _, _, err, _ = st.TransitionDb(common.Address{}); err != nil { + if _, err, _ = st.TransitionDb(common.Address{}); err != nil { b.Fatalf("failed to execute transaction: %v", err) } if _, err = tracer.GetResult(); err != nil { diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 9505387fce35..4b3198c95e0f 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -158,7 +158,7 @@ func TestZeroValueToNotExitCall(t *testing.T) { t.Fatalf("failed to prepare transaction for tracing: %v", err) } st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - if _, _, _, err, _ = st.TransitionDb(common.Address{}); err != nil { + if _, err, _ = st.TransitionDb(common.Address{}); err != nil { t.Fatalf("failed to execute transaction: %v", err) } // Retrieve the trace result and compare against the etalon @@ -244,7 +244,7 @@ func TestPrestateTracerCreate2(t *testing.T) { t.Fatalf("failed to prepare transaction for tracing: %v", err) } st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - if _, _, _, err, _ = st.TransitionDb(common.Address{}); err != nil { + if _, err, _ = st.TransitionDb(common.Address{}); err != nil { t.Fatalf("failed to execute transaction: %v", err) } // Retrieve the trace result and compare against the etalon @@ -346,7 +346,7 @@ func BenchmarkTransactionTrace(b *testing.B) { for i := 0; i < b.N; i++ { snap := statedb.Snapshot() st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - _, _, _, err, _ = st.TransitionDb(common.Address{}) + _, err, _ = st.TransitionDb(common.Address{}) if err != nil { b.Fatal(err) } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 273021e4f0a6..f8687bf626a3 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1322,18 +1322,18 @@ func (s *PublicBlockChainAPI) getCandidatesFromSmartContract() ([]utils.Masterno return candidatesWithStakeInfo, nil } -func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) ([]byte, uint64, bool, error, error) { +func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error, error) { defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now()) statedb, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if statedb == nil || err != nil { - return nil, 0, false, err, nil + return nil, err, nil } if header == nil { - return nil, 0, false, errors.New("nil header in DoCall"), nil + return nil, errors.New("nil header in DoCall"), nil } if err := overrides.Apply(statedb); err != nil { - return nil, 0, false, err, nil + return nil, err, nil } // Setup context so it may be cancelled the call has completed @@ -1350,32 +1350,32 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash block, err := b.BlockByNumberOrHash(ctx, blockNrOrHash) if err != nil { - return nil, 0, false, err, nil + return nil, err, nil } if block == nil { - return nil, 0, false, fmt.Errorf("nil block in DoCall: number=%d, hash=%s", header.Number.Uint64(), header.Hash().Hex()), nil + return nil, fmt.Errorf("nil block in DoCall: number=%d, hash=%s", header.Number.Uint64(), header.Hash().Hex()), nil } author, err := b.GetEngine().Author(block.Header()) if err != nil { - return nil, 0, false, err, nil + return nil, err, nil } XDCxState, err := b.XDCxService().GetTradingState(block, author) if err != nil { - return nil, 0, false, err, nil + return nil, err, nil } // TODO: replace header.BaseFee with blockCtx.BaseFee // reference: https://github.com/ethereum/go-ethereum/pull/29051 msg, err := args.ToMessage(b, header.Number, globalGasCap, header.BaseFee) if err != nil { - return nil, 0, false, err, nil + return nil, err, nil } msg.SetBalanceTokenFeeForCall() // Get a new instance of the EVM. evm, vmError, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &vm.Config{NoBaseFee: true}) if err != nil { - return nil, 0, false, err, nil + return nil, err, nil } // Wait for the context to be done and cancel the evm. Even if the // EVM has finished, cancelling may be done (repeatedly) @@ -1387,19 +1387,19 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash // Execute the message. gp := new(core.GasPool).AddGas(math.MaxUint64) owner := common.Address{} - res, gas, failed, err, vmErr := core.ApplyMessage(evm, msg, gp, owner) + result, err, vmErr := core.ApplyMessage(evm, msg, gp, owner) if err := vmError(); err != nil { - return nil, 0, false, err, nil + return nil, err, nil } // If the timer caused an abort, return an appropriate error message if evm.Cancelled() { - return nil, 0, false, fmt.Errorf("execution aborted (timeout = %v)", timeout), nil + return nil, fmt.Errorf("execution aborted (timeout = %v)", timeout), nil } if err != nil { - return res, 0, false, fmt.Errorf("err: %w (supplied gas %d)", err, msg.Gas()), nil + return result, fmt.Errorf("err: %w (supplied gas %d)", err, msg.Gas()), nil } - return res, gas, failed, err, vmErr + return result, err, vmErr } func newRevertError(res []byte) *revertError { @@ -1443,16 +1443,32 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, bl if args.To != nil && *args.To == common.MasternodeVotingSMCBinary { timeout = 0 } - result, _, failed, err, vmErr := DoCall(ctx, s.b, args, *blockNrOrHash, overrides, timeout, s.b.RPCGasCap()) + result, err, vmErr := DoCall(ctx, s.b, args, *blockNrOrHash, overrides, timeout, s.b.RPCGasCap()) if err != nil { return nil, err } // If the result contains a revert reason, try to unpack and return it. - if failed && len(result) > 0 { - return nil, newRevertError(result) + if result.Failed() && len(result.Return()) > 0 { + return nil, newRevertError(result.Return()) } + return result.Return(), vmErr +} + +type estimateGasError struct { + error string // Concrete error type if it's failed to estimate gas usage + vmerr error // Additional field, it's non-nil if the given transaction is invalid + revert string // Additional field, it's non-empty if the transaction is reverted and reason is provided +} - return (hexutil.Bytes)(result), vmErr +func (e estimateGasError) Error() string { + errMsg := e.error + if e.vmerr != nil { + errMsg += fmt.Sprintf(" (%v)", e.vmerr) + } + if e.revert != "" { + errMsg += fmt.Sprintf(" (%s)", e.revert) + } + return errMsg } func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, gasCap uint64) (hexutil.Uint64, error) { @@ -1496,21 +1512,17 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr cap = hi // Create a helper to check if a gas allowance results in an executable transaction - executable := func(gas uint64) (bool, []byte, error, error) { + executable := func(gas uint64) (bool, *core.ExecutionResult, error) { args.Gas = (*hexutil.Uint64)(&gas) - res, _, failed, err, vmErr := DoCall(ctx, b, args, blockNrOrHash, nil, 0, gasCap) + result, err, _ := DoCall(ctx, b, args, blockNrOrHash, nil, 0, gasCap) if err != nil { if errors.Is(err, vm.ErrOutOfGas) || errors.Is(err, core.ErrIntrinsicGas) { - return false, nil, nil, nil // Special case, raise gas limit + return true, nil, nil // Special case, raise gas limit } - return false, nil, err, nil // Bail out - } - if failed { - return false, res, nil, vmErr + return true, nil, err // Bail out } - - return true, nil, nil, nil + return result.Failed(), result, nil } // If the transaction is a plain value transfer, short circuit estimation and @@ -1519,7 +1531,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr // unused access list items). Ever so slightly wasteful, but safer overall. if args.Data == nil || len(*args.Data) == 0 { if args.To != nil && state.GetCodeSize(*args.To) == 0 { - ok, _, err, _ := executable(params.TxGas) + ok, _, err := executable(params.TxGas) if ok && err == nil { return hexutil.Uint64(params.TxGas), nil } @@ -1529,7 +1541,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr // Execute the binary search and hone in on an executable gas limit for lo+1 < hi { mid := (hi + lo) / 2 - ok, _, err, _ := executable(mid) + failed, _, err := executable(mid) // If the error is not nil(consensus error), it means the provided message // call or transaction will never be accepted no matter how much gas it is @@ -1538,7 +1550,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr return 0, err } - if !ok { + if failed { lo = mid } else { hi = mid @@ -1547,21 +1559,30 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr // Reject the transaction as invalid if it still fails at the highest allowance if hi == cap { - ok, res, err, vmErr := executable(hi) + failed, result, err := executable(hi) if err != nil { return 0, err } - if !ok { - if vmErr != vm.ErrOutOfGas { - if len(res) > 0 { - return 0, newRevertError(res) + if failed { + if result != nil && result.Err != vm.ErrOutOfGas { + var revert string + if len(result.Revert()) > 0 { + ret, err := abi.UnpackRevert(result.Revert()) + if err != nil { + revert = hexutil.Encode(result.Revert()) + } else { + revert = ret + } + } + return 0, estimateGasError{ + error: "always failing transaction", + vmerr: result.Err, + revert: revert, } - return 0, vmErr } - // Otherwise, the specified gas cap is too low - return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap) + return 0, estimateGasError{error: fmt.Sprintf("gas required exceeds allowance (%d)", cap)} } } return hexutil.Uint64(hi), nil @@ -2075,12 +2096,12 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH return nil, 0, nil, err } // TODO: determine the value of owner - _, UsedGas, _, err, vmErr := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), owner) + res, err, _ := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), owner) if err != nil { return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err) } if tracer.Equal(prevTracer) { - return accessList, UsedGas, vmErr, nil + return accessList, res.UsedGas, res.Err, nil } prevTracer = tracer } diff --git a/les/odr_test.go b/les/odr_test.go index b4aa8cf21d0f..031a1df5dc1e 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -142,8 +142,8 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxUint64) owner := common.Address{} - ret, _, _, _, _ := core.ApplyMessage(vmenv, msg, gp, owner) - res = append(res, ret...) + result, _, _ := core.ApplyMessage(vmenv, msg, gp, owner) + res = append(res, result.Return()...) } } else { header := lc.GetHeaderByHash(bhash) @@ -160,9 +160,9 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai vmenv := vm.NewEVM(context, txContext, statedb, nil, config, vm.Config{NoBaseFee: true}) gp := new(core.GasPool).AddGas(math.MaxUint64) owner := common.Address{} - ret, _, _, _, _ := core.ApplyMessage(vmenv, msg, gp, owner) + result, _, _ := core.ApplyMessage(vmenv, msg, gp, owner) if statedb.Error() == nil { - res = append(res, ret...) + res = append(res, result.Return()...) } } } diff --git a/light/odr_test.go b/light/odr_test.go index 94c1246065ad..1562f955d9fe 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -191,8 +191,8 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain vmenv := vm.NewEVM(context, txContext, st, nil, config, vm.Config{NoBaseFee: true}) gp := new(core.GasPool).AddGas(math.MaxUint64) owner := common.Address{} - ret, _, _, _, _ := core.ApplyMessage(vmenv, msg, gp, owner) - res = append(res, ret...) + result, _, _ := core.ApplyMessage(vmenv, msg, gp, owner) + res = append(res, result.Return()...) if st.Error() != nil { return res, st.Error() } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 756eda3bf38f..1452ecd5d6a0 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -166,7 +166,7 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD gaspool.AddGas(block.GasLimit()) coinbase := &t.json.Env.Coinbase - if _, _, _, err, _ := core.ApplyMessage(evm, msg, gaspool, *coinbase); err != nil { + if _, err, _ := core.ApplyMessage(evm, msg, gaspool, *coinbase); err != nil { statedb.RevertToSnapshot(snapshot) } if logs := rlpHash(statedb.Logs()); logs != common.Hash(post.Logs) { From 82ff8c19a05d71d22b7db46259dba4843c9d730f Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 17 Dec 2024 17:35:07 +0800 Subject: [PATCH 383/479] all: remove uses of untyped golang-lru --- XDCx/XDCx.go | 32 +++----- XDCx/token.go | 11 ++- XDCx/tradingstate/database.go | 15 +--- XDCxDAO/leveldb.go | 6 -- XDCxDAO/mongodb.go | 7 +- XDCxlending/XDCxlending.go | 39 ++++------ XDCxlending/lendingstate/database.go | 16 ++-- consensus/XDPoS/XDPoS.go | 44 ++++------- consensus/XDPoS/engines/engine_v1/engine.go | 44 ++++------- consensus/XDPoS/engines/engine_v1/snapshot.go | 7 +- consensus/XDPoS/engines/engine_v1/utils.go | 5 +- consensus/XDPoS/engines/engine_v2/engine.go | 28 +++---- .../XDPoS/engines/engine_v2/epochSwitch.go | 7 +- consensus/XDPoS/engines/engine_v2/snapshot.go | 3 +- consensus/XDPoS/engines/engine_v2/utils.go | 10 +-- .../XDPoS/engines/engine_v2/verifyHeader.go | 2 +- consensus/XDPoS/utils/types.go | 3 + consensus/clique/clique.go | 21 +++-- consensus/clique/snapshot.go | 10 ++- consensus/ethash/ethash.go | 72 +++++++++-------- contracts/utils.go | 9 +-- core/state/trc21_reader.go | 14 ++-- eth/fetcher/fetcher.go | 11 ++- eth/handler.go | 77 ++++++++----------- eth/hooks/engine_v1_hooks.go | 7 +- eth/hooks/engine_v2_hooks.go | 14 ++-- go.mod | 1 - go.sum | 2 - light/lightchain.go | 29 +++---- 29 files changed, 226 insertions(+), 320 deletions(-) diff --git a/XDCx/XDCx.go b/XDCx/XDCx.go index ac3f83d582f4..3fbaf0c783e2 100644 --- a/XDCx/XDCx.go +++ b/XDCx/XDCx.go @@ -10,6 +10,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate" "github.com/XinFinOrg/XDPoSChain/XDCxDAO" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/lru" "github.com/XinFinOrg/XDPoSChain/common/prque" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/core/state" @@ -17,7 +18,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/p2p" "github.com/XinFinOrg/XDPoSChain/rpc" - lru "github.com/hashicorp/golang-lru" "golang.org/x/sync/syncmap" ) @@ -59,8 +59,8 @@ type XDCX struct { sdkNode bool settings syncmap.Map // holds configuration settings that can be dynamically changed - tokenDecimalCache *lru.Cache - orderCache *lru.Cache + tokenDecimalCache *lru.Cache[common.Address, *big.Int] + orderCache *lru.Cache[common.Hash, map[common.Hash]tradingstate.OrderHistoryItem] } func (XDCx *XDCX) Protocols() []p2p.Protocol { @@ -94,19 +94,11 @@ func NewMongoDBEngine(cfg *Config) *XDCxDAO.MongoDatabase { } func New(cfg *Config) *XDCX { - tokenDecimalCache, err := lru.New(defaultCacheLimit) - if err != nil { - log.Warn("[XDCx-New] fail to create new lru for token decimal", "error", err) - } - orderCache, err := lru.New(tradingstate.OrderCacheLimit) - if err != nil { - log.Warn("[XDCx-New] fail to create new lru for order", "error", err) - } XDCX := &XDCX{ orderNonce: make(map[common.Address]*big.Int), Triegc: prque.New(nil), - tokenDecimalCache: tokenDecimalCache, - orderCache: orderCache, + tokenDecimalCache: lru.NewCache[common.Address, *big.Int](defaultCacheLimit), + orderCache: lru.NewCache[common.Hash, map[common.Hash]tradingstate.OrderHistoryItem](tradingstate.OrderCacheLimit), } // default DBEngine: levelDB @@ -607,12 +599,9 @@ func (XDCx *XDCX) GetTradingStateRoot(block *types.Block, author common.Address) } func (XDCx *XDCX) UpdateOrderCache(baseToken, quoteToken common.Address, orderHash common.Hash, txhash common.Hash, lastState tradingstate.OrderHistoryItem) { - var orderCacheAtTxHash map[common.Hash]tradingstate.OrderHistoryItem - c, ok := XDCx.orderCache.Get(txhash) - if !ok || c == nil { + orderCacheAtTxHash, ok := XDCx.orderCache.Get(txhash) + if !ok || orderCacheAtTxHash == nil { orderCacheAtTxHash = make(map[common.Hash]tradingstate.OrderHistoryItem) - } else { - orderCacheAtTxHash = c.(map[common.Hash]tradingstate.OrderHistoryItem) } orderKey := tradingstate.GetOrderHistoryKey(baseToken, quoteToken, orderHash) _, ok = orderCacheAtTxHash[orderKey] @@ -629,16 +618,15 @@ func (XDCx *XDCX) RollbackReorgTxMatch(txhash common.Hash) error { items := db.GetListItemByTxHash(txhash, &tradingstate.OrderItem{}) if items != nil { for _, order := range items.([]*tradingstate.OrderItem) { - c, ok := XDCx.orderCache.Get(txhash) - log.Debug("XDCx reorg: rollback order", "txhash", txhash.Hex(), "order", tradingstate.ToJSON(order), "orderHistoryItem", c) - if !ok { + orderCacheAtTxHash, ok := XDCx.orderCache.Get(txhash) + log.Debug("XDCx reorg: rollback order", "txhash", txhash.Hex(), "order", tradingstate.ToJSON(order), "orderHistoryItem", orderCacheAtTxHash) + if !ok || orderCacheAtTxHash == nil { log.Debug("XDCx reorg: remove order due to no orderCache", "order", tradingstate.ToJSON(order)) if err := db.DeleteObject(order.Hash, &tradingstate.OrderItem{}); err != nil { log.Crit("SDKNode: failed to remove reorg order", "err", err.Error(), "order", tradingstate.ToJSON(order)) } continue } - orderCacheAtTxHash := c.(map[common.Hash]tradingstate.OrderHistoryItem) orderHistoryItem := orderCacheAtTxHash[tradingstate.GetOrderHistoryKey(order.BaseToken, order.QuoteToken, order.Hash)] if (orderHistoryItem == tradingstate.OrderHistoryItem{}) { log.Debug("XDCx reorg: remove order due to empty orderHistory", "order", tradingstate.ToJSON(order)) diff --git a/XDCx/token.go b/XDCx/token.go index 5aa8554cf9f5..c44dbe7e297f 100644 --- a/XDCx/token.go +++ b/XDCx/token.go @@ -4,15 +4,14 @@ import ( "math/big" "strings" - "github.com/XinFinOrg/XDPoSChain/contracts/XDCx/contract" - "github.com/XinFinOrg/XDPoSChain/core" - "github.com/XinFinOrg/XDPoSChain/log" - "github.com/XinFinOrg/XDPoSChain" "github.com/XinFinOrg/XDPoSChain/accounts/abi" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus" + "github.com/XinFinOrg/XDPoSChain/contracts/XDCx/contract" + "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/state" + "github.com/XinFinOrg/XDPoSChain/log" ) // GetTokenAbi return token abi @@ -45,8 +44,8 @@ func RunContract(chain consensus.ChainContext, statedb *state.StateDB, contractA } func (XDCx *XDCX) GetTokenDecimal(chain consensus.ChainContext, statedb *state.StateDB, tokenAddr common.Address) (*big.Int, error) { - if tokenDecimal, ok := XDCx.tokenDecimalCache.Get(tokenAddr); ok { - return tokenDecimal.(*big.Int), nil + if tokenDecimal, ok := XDCx.tokenDecimalCache.Get(tokenAddr); ok && tokenDecimal != nil { + return tokenDecimal, nil } if tokenAddr == common.XDCNativeAddressBinary { XDCx.tokenDecimalCache.Add(tokenAddr, common.BasePrice) diff --git a/XDCx/tradingstate/database.go b/XDCx/tradingstate/database.go index b40f916a0f7f..2fe21932438e 100644 --- a/XDCx/tradingstate/database.go +++ b/XDCx/tradingstate/database.go @@ -23,7 +23,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/trie" - lru "github.com/hashicorp/golang-lru" ) // Trie cache generation limit after which to evic trie nodes from memory. @@ -33,9 +32,6 @@ const ( // Number of past tries to keep. This value is chosen such that // reasonable chain reorg depths will hit an existing trie. maxPastTries = 12 - - // Number of codehash->size associations to keep. - codeSizeCacheSize = 100000 ) // Database wraps access to tries and contract code. @@ -79,18 +75,15 @@ type Trie interface { // intermediate trie-node memory pool between the low level storage layer and the // high level trie abstraction. func NewDatabase(db ethdb.Database) Database { - csc, _ := lru.New(codeSizeCacheSize) return &cachingDB{ - db: trie.NewDatabase(db), - codeSizeCache: csc, + db: trie.NewDatabase(db), } } type cachingDB struct { - db *trie.Database - mu sync.Mutex - pastTries []*XDCXTrie - codeSizeCache *lru.Cache + db *trie.Database + mu sync.Mutex + pastTries []*XDCXTrie } // OpenTrie opens the main account trie. diff --git a/XDCxDAO/leveldb.go b/XDCxDAO/leveldb.go index 2a76162d4ca4..a6d90dd57a9f 100644 --- a/XDCxDAO/leveldb.go +++ b/XDCxDAO/leveldb.go @@ -8,10 +8,8 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/rawdb" - "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/log" - lru "github.com/hashicorp/golang-lru" ) type BatchItem struct { @@ -21,7 +19,6 @@ type BatchItem struct { type BatchDatabase struct { db ethdb.Database emptyKey []byte - cacheItems *lru.Cache // Cache for reading lock sync.RWMutex cacheLimit int Debug bool @@ -44,11 +41,8 @@ func NewBatchDatabaseWithEncode(datadir string, cacheLimit int) *BatchDatabase { itemCacheLimit = cacheLimit } - cacheItems, _ := lru.New(itemCacheLimit) - batchDB := &BatchDatabase{ db: db, - cacheItems: cacheItems, emptyKey: EmptyKey(), // pre alloc for comparison cacheLimit: itemCacheLimit, } diff --git a/XDCxDAO/mongodb.go b/XDCxDAO/mongodb.go index 5c0352db7a59..8c8e45052ca7 100644 --- a/XDCxDAO/mongodb.go +++ b/XDCxDAO/mongodb.go @@ -10,11 +10,11 @@ import ( "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate" "github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/lru" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/log" "github.com/globalsign/mgo" "github.com/globalsign/mgo/bson" - lru "github.com/hashicorp/golang-lru" ) const ( @@ -32,7 +32,7 @@ type MongoDatabase struct { Session *mgo.Session dbName string emptyKey []byte - cacheItems *lru.Cache // Cache for reading + cacheItems *lru.Cache[string, interface{}] // Cache for reading orderBulk *mgo.Bulk tradeBulk *mgo.Bulk epochPriceBulk *mgo.Bulk @@ -64,12 +64,11 @@ func NewMongoDatabase(session *mgo.Session, dbName string, mongoURL string, repl if cacheLimit > 0 { itemCacheLimit = cacheLimit } - cacheItems, _ := lru.New(itemCacheLimit) db := &MongoDatabase{ Session: session, dbName: dbName, - cacheItems: cacheItems, + cacheItems: lru.NewCache[string, interface{}](itemCacheLimit), } if err := db.EnsureIndexes(); err != nil { return nil, err diff --git a/XDCxlending/XDCxlending.go b/XDCxlending/XDCxlending.go index 28e9321aff24..1bf2496ff237 100644 --- a/XDCxlending/XDCxlending.go +++ b/XDCxlending/XDCxlending.go @@ -13,6 +13,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/XDCxDAO" "github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/lru" "github.com/XinFinOrg/XDPoSChain/common/prque" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/core/state" @@ -20,7 +21,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/p2p" "github.com/XinFinOrg/XDPoSChain/rpc" - lru "github.com/hashicorp/golang-lru" ) const ( @@ -42,8 +42,8 @@ type Lending struct { orderNonce map[common.Address]*big.Int XDCx *XDCx.XDCX - lendingItemHistory *lru.Cache - lendingTradeHistory *lru.Cache + lendingItemHistory *lru.Cache[common.Hash, map[common.Hash]lendingstate.LendingItemHistoryItem] + lendingTradeHistory *lru.Cache[common.Hash, map[common.Hash]lendingstate.LendingTradeHistoryItem] } func (l *Lending) Protocols() []p2p.Protocol { @@ -62,13 +62,11 @@ func (l *Lending) Stop() error { } func New(XDCx *XDCx.XDCX) *Lending { - itemCache, _ := lru.New(defaultCacheLimit) - lendingTradeCache, _ := lru.New(defaultCacheLimit) lending := &Lending{ orderNonce: make(map[common.Address]*big.Int), Triegc: prque.New(nil), - lendingItemHistory: itemCache, - lendingTradeHistory: lendingTradeCache, + lendingItemHistory: lru.NewCache[common.Hash, map[common.Hash]lendingstate.LendingItemHistoryItem](defaultCacheLimit), + lendingTradeHistory: lru.NewCache[common.Hash, map[common.Hash]lendingstate.LendingTradeHistoryItem](defaultCacheLimit), } lending.StateCache = lendingstate.NewDatabase(XDCx.GetLevelDB()) lending.XDCx = XDCx @@ -705,12 +703,9 @@ func (l *Lending) GetLendingStateRoot(block *types.Block, author common.Address) } func (l *Lending) UpdateLendingItemCache(LendingToken, CollateralToken common.Address, hash common.Hash, txhash common.Hash, lastState lendingstate.LendingItemHistoryItem) { - var lendingCacheAtTxHash map[common.Hash]lendingstate.LendingItemHistoryItem - c, ok := l.lendingItemHistory.Get(txhash) - if !ok || c == nil { + lendingCacheAtTxHash, ok := l.lendingItemHistory.Get(txhash) + if !ok || lendingCacheAtTxHash == nil { lendingCacheAtTxHash = make(map[common.Hash]lendingstate.LendingItemHistoryItem) - } else { - lendingCacheAtTxHash = c.(map[common.Hash]lendingstate.LendingItemHistoryItem) } orderKey := lendingstate.GetLendingItemHistoryKey(LendingToken, CollateralToken, hash) _, ok = lendingCacheAtTxHash[orderKey] @@ -722,11 +717,9 @@ func (l *Lending) UpdateLendingItemCache(LendingToken, CollateralToken common.Ad func (l *Lending) UpdateLendingTradeCache(hash common.Hash, txhash common.Hash, lastState lendingstate.LendingTradeHistoryItem) { var lendingCacheAtTxHash map[common.Hash]lendingstate.LendingTradeHistoryItem - c, ok := l.lendingTradeHistory.Get(txhash) - if !ok || c == nil { + lendingCacheAtTxHash, ok := l.lendingTradeHistory.Get(txhash) + if !ok || lendingCacheAtTxHash == nil { lendingCacheAtTxHash = make(map[common.Hash]lendingstate.LendingTradeHistoryItem) - } else { - lendingCacheAtTxHash = c.(map[common.Hash]lendingstate.LendingTradeHistoryItem) } _, ok = lendingCacheAtTxHash[hash] if !ok { @@ -743,16 +736,15 @@ func (l *Lending) RollbackLendingData(txhash common.Hash) error { items := db.GetListItemByTxHash(txhash, &lendingstate.LendingItem{}) if items != nil { for _, item := range items.([]*lendingstate.LendingItem) { - c, ok := l.lendingItemHistory.Get(txhash) - log.Debug("XDCxlending reorg: rollback lendingItem", "txhash", txhash.Hex(), "item", lendingstate.ToJSON(item), "lendingItemHistory", c) - if !ok { + cacheAtTxHash, ok := l.lendingItemHistory.Get(txhash) + log.Debug("XDCxlending reorg: rollback lendingItem", "txhash", txhash.Hex(), "item", lendingstate.ToJSON(item), "lendingItemHistory", cacheAtTxHash) + if !ok || cacheAtTxHash == nil { log.Debug("XDCxlending reorg: remove item due to no lendingItemHistory", "item", lendingstate.ToJSON(item)) if err := db.DeleteObject(item.Hash, &lendingstate.LendingItem{}); err != nil { return fmt.Errorf("failed to remove reorg LendingItem. Err: %v . Item: %s", err.Error(), lendingstate.ToJSON(item)) } continue } - cacheAtTxHash := c.(map[common.Hash]lendingstate.LendingItemHistoryItem) lendingItemHistory := cacheAtTxHash[lendingstate.GetLendingItemHistoryKey(item.LendingToken, item.CollateralToken, item.Hash)] if (lendingItemHistory == lendingstate.LendingItemHistoryItem{}) { log.Debug("XDCxlending reorg: remove item due to empty lendingItemHistory", "item", lendingstate.ToJSON(item)) @@ -776,16 +768,15 @@ func (l *Lending) RollbackLendingData(txhash common.Hash) error { items = db.GetListItemByTxHash(txhash, &lendingstate.LendingTrade{}) if items != nil { for _, trade := range items.([]*lendingstate.LendingTrade) { - c, ok := l.lendingTradeHistory.Get(txhash) - log.Debug("XDCxlending reorg: rollback LendingTrade", "txhash", txhash.Hex(), "trade", lendingstate.ToJSON(trade), "LendingTradeHistory", c) - if !ok { + cacheAtTxHash, ok := l.lendingTradeHistory.Get(txhash) + log.Debug("XDCxlending reorg: rollback LendingTrade", "txhash", txhash.Hex(), "trade", lendingstate.ToJSON(trade), "LendingTradeHistory", cacheAtTxHash) + if !ok || cacheAtTxHash == nil { log.Debug("XDCxlending reorg: remove trade due to no LendingTradeHistory", "trade", lendingstate.ToJSON(trade)) if err := db.DeleteObject(trade.Hash, &lendingstate.LendingTrade{}); err != nil { return fmt.Errorf("failed to remove reorg LendingTrade. Err: %v . Trade: %s", err.Error(), lendingstate.ToJSON(trade)) } continue } - cacheAtTxHash := c.(map[common.Hash]lendingstate.LendingTradeHistoryItem) lendingTradeHistoryItem := cacheAtTxHash[trade.Hash] if (lendingTradeHistoryItem == lendingstate.LendingTradeHistoryItem{}) { log.Debug("XDCxlending reorg: remove trade due to empty LendingTradeHistory", "trade", lendingstate.ToJSON(trade)) diff --git a/XDCxlending/lendingstate/database.go b/XDCxlending/lendingstate/database.go index d765a1eade76..7c4934bc7fec 100644 --- a/XDCxlending/lendingstate/database.go +++ b/XDCxlending/lendingstate/database.go @@ -23,7 +23,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/trie" - lru "github.com/hashicorp/golang-lru" ) // Trie cache generation limit after which to evic trie nodes from memory. @@ -33,9 +32,6 @@ const ( // Number of past tries to keep. This value is chosen such that // reasonable chain reorg depths will hit an existing trie. maxPastTries = 12 - - // Number of codehash->size associations to keep. - codeSizeCacheSize = 100000 ) // Database wraps access to tries and contract code. @@ -78,18 +74,16 @@ type Trie interface { // intermediate trie-node memory pool between the low level storage layer and the // high level trie abstraction. func NewDatabase(db ethdb.Database) Database { - csc, _ := lru.New(codeSizeCacheSize) return &cachingDB{ - db: trie.NewDatabase(db), - codeSizeCache: csc, + db: trie.NewDatabase(db), + // codeSizeCache: csc, } } type cachingDB struct { - db *trie.Database - mu sync.Mutex - pastTries []*XDCXTrie - codeSizeCache *lru.Cache + db *trie.Database + mu sync.Mutex + pastTries []*XDCXTrie } // OpenTrie opens the main account trie. diff --git a/consensus/XDPoS/XDPoS.go b/consensus/XDPoS/XDPoS.go index 5001abe4e8f1..411411d51857 100644 --- a/consensus/XDPoS/XDPoS.go +++ b/consensus/XDPoS/XDPoS.go @@ -22,20 +22,19 @@ import ( "math/big" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/lru" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/engines/engine_v1" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/engines/engine_v2" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" - "github.com/XinFinOrg/XDPoSChain/event" - "github.com/XinFinOrg/XDPoSChain/consensus/clique" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/ethdb" + "github.com/XinFinOrg/XDPoSChain/event" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rpc" - lru "github.com/hashicorp/golang-lru" ) const ( @@ -60,7 +59,7 @@ type XDPoS struct { db ethdb.Database // Database to store and retrieve snapshot checkpoints // Transaction cache, only make sense for adaptor level - signingTxsCache *lru.Cache + signingTxsCache *lru.Cache[common.Hash, []*types.Transaction] // Share Channel MinePeriodCh chan int // Miner wait Period Channel @@ -109,17 +108,12 @@ func New(chainConfig *params.ChainConfig, db ethdb.Database) *XDPoS { minePeriodCh := make(chan int) newRoundCh := make(chan types.Round, newRoundChanSize) - // Allocate the snapshot caches and create the engine - signingTxsCache, _ := lru.New(utils.BlockSignersCacheLimit) - return &XDPoS{ - config: config, - db: db, - - MinePeriodCh: minePeriodCh, - NewRoundCh: newRoundCh, - - signingTxsCache: signingTxsCache, + config: config, + db: db, + MinePeriodCh: minePeriodCh, + NewRoundCh: newRoundCh, + signingTxsCache: lru.NewCache[common.Hash, []*types.Transaction](utils.BlockSignersCacheLimit), EngineV1: engine_v1.New(chainConfig, db), EngineV2: engine_v2.New(chainConfig, db, minePeriodCh, newRoundCh), } @@ -138,22 +132,16 @@ func NewFaker(db ethdb.Database, chainConfig *params.ChainConfig) *XDPoS { minePeriodCh := make(chan int) newRoundCh := make(chan types.Round, newRoundChanSize) - // Allocate the snapshot caches and create the engine - signingTxsCache, _ := lru.New(utils.BlockSignersCacheLimit) - fakeEngine = &XDPoS{ - config: conf, - db: db, - - MinePeriodCh: minePeriodCh, - NewRoundCh: newRoundCh, - + config: conf, + db: db, + MinePeriodCh: minePeriodCh, + NewRoundCh: newRoundCh, GetXDCXService: func() utils.TradingService { return nil }, GetLendingService: func() utils.LendingService { return nil }, - - signingTxsCache: signingTxsCache, - EngineV1: engine_v1.NewFaker(db, chainConfig), - EngineV2: engine_v2.New(chainConfig, db, minePeriodCh, newRoundCh), + signingTxsCache: lru.NewCache[common.Hash, []*types.Transaction](utils.BlockSignersCacheLimit), + EngineV1: engine_v1.NewFaker(db, chainConfig), + EngineV2: engine_v2.New(chainConfig, db, minePeriodCh, newRoundCh), } return fakeEngine } @@ -554,7 +542,7 @@ func (x *XDPoS) CacheSigningTxs(hash common.Hash, txs []*types.Transaction) []*t return signTxs } -func (x *XDPoS) GetCachedSigningTxs(hash common.Hash) (interface{}, bool) { +func (x *XDPoS) GetCachedSigningTxs(hash common.Hash) ([]*types.Transaction, bool) { return x.signingTxsCache.Get(hash) } diff --git a/consensus/XDPoS/engines/engine_v1/engine.go b/consensus/XDPoS/engines/engine_v1/engine.go index b93a8729d6ef..54d4d48b876e 100644 --- a/consensus/XDPoS/engines/engine_v1/engine.go +++ b/consensus/XDPoS/engines/engine_v1/engine.go @@ -14,8 +14,8 @@ import ( "github.com/XinFinOrg/XDPoSChain/accounts" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/lru" "github.com/XinFinOrg/XDPoSChain/consensus" - "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" "github.com/XinFinOrg/XDPoSChain/consensus/clique" "github.com/XinFinOrg/XDPoSChain/consensus/misc" @@ -26,7 +26,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" - lru "github.com/hashicorp/golang-lru" ) const ( @@ -44,10 +43,10 @@ type XDPoS_v1 struct { config *params.XDPoSConfig // Consensus engine configuration parameters db ethdb.Database // Database to store and retrieve snapshot checkpoints - recents *lru.ARCCache // Snapshots for recent block to speed up reorgs - signatures *lru.ARCCache // Signatures of recent blocks to speed up mining - validatorSignatures *lru.ARCCache // Signatures of recent blocks to speed up mining - verifiedHeaders *lru.ARCCache + recents *lru.Cache[common.Hash, *SnapshotV1] // Snapshots for recent block to speed up reorgs + signatures *utils.SigLRU // Signatures of recent blocks to speed up mining + validatorSignatures *utils.SigLRU // Signatures of recent blocks to speed up mining + verifiedHeaders *lru.Cache[common.Hash, struct{}] proposals map[common.Address]bool // Current list of proposals we are pushing signer common.Address // Ethereum address of the signing key @@ -92,20 +91,16 @@ func New(chainConfig *params.ChainConfig, db ethdb.Database) *XDPoS_v1 { conf.Epoch = utils.EpochLength } - recents, _ := lru.NewARC(utils.InmemorySnapshots) - signatures, _ := lru.NewARC(utils.InmemorySnapshots) - validatorSignatures, _ := lru.NewARC(utils.InmemorySnapshots) - verifiedHeaders, _ := lru.NewARC(utils.InmemorySnapshots) return &XDPoS_v1{ chainConfig: chainConfig, config: &conf, db: db, - recents: recents, - signatures: signatures, - verifiedHeaders: verifiedHeaders, - validatorSignatures: validatorSignatures, + recents: lru.NewCache[common.Hash, *SnapshotV1](utils.InmemorySnapshots), + signatures: lru.NewCache[common.Hash, common.Address](utils.InmemorySnapshots), + verifiedHeaders: lru.NewCache[common.Hash, struct{}](utils.InmemorySnapshots), + validatorSignatures: lru.NewCache[common.Hash, common.Address](utils.InmemorySnapshots), proposals: make(map[common.Address]bool), } } @@ -145,7 +140,7 @@ func (x *XDPoS_v1) verifyHeaderWithCache(chain consensus.ChainReader, header *ty } err := x.verifyHeader(chain, header, parents, fullVerify) if err == nil { - x.verifiedHeaders.Add(header.Hash(), true) + x.verifiedHeaders.Add(header.Hash(), struct{}{}) } return err } @@ -483,8 +478,8 @@ func (x *XDPoS_v1) snapshot(chain consensus.ChainReader, number uint64, hash com ) for { // If an in-memory SnapshotV1 was found, use that - if s, ok := x.recents.Get(hash); ok { - snap = s.(*SnapshotV1) + if s, ok := x.recents.Get(hash); ok && s != nil { + snap = s break } // If an on-disk checkpoint snapshot can be found, use that @@ -979,7 +974,7 @@ func (x *XDPoS_v1) RecoverValidator(header *types.Header) (common.Address, error // If the signature's already cached, return that hash := header.Hash() if address, known := x.validatorSignatures.Get(hash); known { - return address.(common.Address), nil + return address, nil } // Retrieve the signature from the header.Validator // len equals 65 bytes @@ -1044,20 +1039,15 @@ func NewFaker(db ethdb.Database, chainConfig *params.ChainConfig) *XDPoS_v1 { // Set any missing consensus parameters to their defaults conf := chainConfig.XDPoS - // Allocate the snapshot caches and create the engine - recents, _ := lru.NewARC(utils.InmemorySnapshots) - signatures, _ := lru.NewARC(utils.InmemorySnapshots) - validatorSignatures, _ := lru.NewARC(utils.InmemorySnapshots) - verifiedHeaders, _ := lru.NewARC(utils.InmemorySnapshots) fakeEngine = &XDPoS_v1{ chainConfig: chainConfig, config: conf, db: db, - recents: recents, - signatures: signatures, - verifiedHeaders: verifiedHeaders, - validatorSignatures: validatorSignatures, + recents: lru.NewCache[common.Hash, *SnapshotV1](utils.InmemorySnapshots), + signatures: lru.NewCache[common.Hash, common.Address](utils.InmemorySnapshots), + verifiedHeaders: lru.NewCache[common.Hash, struct{}](utils.InmemorySnapshots), + validatorSignatures: lru.NewCache[common.Hash, common.Address](utils.InmemorySnapshots), proposals: make(map[common.Address]bool), } return fakeEngine diff --git a/consensus/XDPoS/engines/engine_v1/snapshot.go b/consensus/XDPoS/engines/engine_v1/snapshot.go index 3ad8b221894a..012b29aabea0 100644 --- a/consensus/XDPoS/engines/engine_v1/snapshot.go +++ b/consensus/XDPoS/engines/engine_v1/snapshot.go @@ -10,7 +10,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/params" - lru "github.com/hashicorp/golang-lru" ) // Vote represents a single vote that an authorized signer made to modify the @@ -32,7 +31,7 @@ import ( // Snapshot is the state of the authorization voting at a given point in time. type SnapshotV1 struct { config *params.XDPoSConfig // Consensus engine parameters to fine tune behavior - sigcache *lru.ARCCache // Cache of recent block signatures to speed up ecrecover + sigcache *utils.SigLRU // Cache of recent block signatures to speed up ecrecover Number uint64 `json:"number"` // Block number where the snapshot was created Hash common.Hash `json:"hash"` // Block hash where the snapshot was created @@ -45,7 +44,7 @@ type SnapshotV1 struct { // newSnapshot creates a new snapshot with the specified startup parameters. This // method does not initialize the set of recent signers, so only ever use if for // the genesis block. -func newSnapshot(config *params.XDPoSConfig, sigcache *lru.ARCCache, number uint64, hash common.Hash, signers []common.Address) *SnapshotV1 { +func newSnapshot(config *params.XDPoSConfig, sigcache *utils.SigLRU, number uint64, hash common.Hash, signers []common.Address) *SnapshotV1 { snap := &SnapshotV1{ config: config, sigcache: sigcache, @@ -62,7 +61,7 @@ func newSnapshot(config *params.XDPoSConfig, sigcache *lru.ARCCache, number uint } // loadSnapshot loads an existing snapshot from the database. -func loadSnapshot(config *params.XDPoSConfig, sigcache *lru.ARCCache, db ethdb.Database, hash common.Hash) (*SnapshotV1, error) { +func loadSnapshot(config *params.XDPoSConfig, sigcache *utils.SigLRU, db ethdb.Database, hash common.Hash) (*SnapshotV1, error) { blob, err := db.Get(append([]byte("XDPoS-"), hash[:]...)) if err != nil { return nil, err diff --git a/consensus/XDPoS/engines/engine_v1/utils.go b/consensus/XDPoS/engines/engine_v1/utils.go index a0128331220c..fbd7814935d5 100644 --- a/consensus/XDPoS/engines/engine_v1/utils.go +++ b/consensus/XDPoS/engines/engine_v1/utils.go @@ -9,7 +9,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rlp" - lru "github.com/hashicorp/golang-lru" "golang.org/x/crypto/sha3" ) @@ -87,11 +86,11 @@ func sigHash(header *types.Header) (hash common.Hash) { } // ecrecover extracts the Ethereum account address from a signed header. -func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) { +func ecrecover(header *types.Header, sigcache *utils.SigLRU) (common.Address, error) { // If the signature's already cached, return that hash := header.Hash() if address, known := sigcache.Get(hash); known { - return address.(common.Address), nil + return address, nil } // Retrieve the signature from the header extra-data if len(header.Extra) < utils.ExtraSeal { diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index 25515c3f7acb..bcbf4651cb9f 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -13,6 +13,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/accounts" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/countdown" + "github.com/XinFinOrg/XDPoSChain/common/lru" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" "github.com/XinFinOrg/XDPoSChain/consensus/clique" @@ -21,7 +22,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" - lru "github.com/hashicorp/golang-lru" ) type XDPoS_v2 struct { @@ -32,14 +32,14 @@ type XDPoS_v2 struct { isInitilised bool // status of v2 variables whosTurn common.Address // Record waiting for who to mine - snapshots *lru.ARCCache // Snapshots for gap block - signatures *lru.ARCCache // Signatures of recent blocks to speed up mining - epochSwitches *lru.ARCCache // infos of epoch: master nodes, epoch switch block info, parent of that info - verifiedHeaders *lru.ARCCache + snapshots *lru.Cache[common.Hash, *SnapshotV2] // Snapshots for gap block + signatures *utils.SigLRU // Signatures of recent blocks to speed up mining + epochSwitches *lru.Cache[common.Hash, *types.EpochSwitchInfo] // infos of epoch: master nodes, epoch switch block info, parent of that info + verifiedHeaders *lru.Cache[common.Hash, struct{}] // only contains epoch switch block info // input: round, output: infos of epoch switch block and next epoch switch block info - round2epochBlockInfo *lru.ARCCache + round2epochBlockInfo *lru.Cache[types.Round, *types.BlockInfo] signer common.Address // Ethereum address of the signing key signFn clique.SignerFn // Signer function to authorize hashes with @@ -78,12 +78,6 @@ func New(chainConfig *params.ChainConfig, db ethdb.Database, minePeriodCh chan i duration := time.Duration(config.V2.CurrentConfig.TimeoutPeriod) * time.Second timeoutTimer := countdown.NewCountDown(duration) - snapshots, _ := lru.NewARC(utils.InmemorySnapshots) - signatures, _ := lru.NewARC(utils.InmemorySnapshots) - epochSwitches, _ := lru.NewARC(int(utils.InmemoryEpochs)) - verifiedHeaders, _ := lru.NewARC(utils.InmemorySnapshots) - round2epochBlockInfo, _ := lru.NewARC(utils.InmemoryRound2Epochs) - timeoutPool := utils.NewPool() votePool := utils.NewPool() engine := &XDPoS_v2{ @@ -93,17 +87,17 @@ func New(chainConfig *params.ChainConfig, db ethdb.Database, minePeriodCh chan i db: db, isInitilised: false, - signatures: signatures, + signatures: lru.NewCache[common.Hash, common.Address](utils.InmemorySnapshots), - verifiedHeaders: verifiedHeaders, - snapshots: snapshots, - epochSwitches: epochSwitches, + verifiedHeaders: lru.NewCache[common.Hash, struct{}](utils.InmemorySnapshots), + snapshots: lru.NewCache[common.Hash, *SnapshotV2](utils.InmemorySnapshots), + epochSwitches: lru.NewCache[common.Hash, *types.EpochSwitchInfo](int(utils.InmemoryEpochs)), timeoutWorker: timeoutTimer, BroadcastCh: make(chan interface{}), minePeriodCh: minePeriodCh, newRoundCh: newRoundCh, - round2epochBlockInfo: round2epochBlockInfo, + round2epochBlockInfo: lru.NewCache[types.Round, *types.BlockInfo](utils.InmemoryRound2Epochs), timeoutPool: timeoutPool, votePool: votePool, diff --git a/consensus/XDPoS/engines/engine_v2/epochSwitch.go b/consensus/XDPoS/engines/engine_v2/epochSwitch.go index e9d76daa620a..b6c4a0c16808 100644 --- a/consensus/XDPoS/engines/engine_v2/epochSwitch.go +++ b/consensus/XDPoS/engines/engine_v2/epochSwitch.go @@ -30,9 +30,8 @@ func (x *XDPoS_v2) getPreviousEpochSwitchInfoByHash(chain consensus.ChainReader, // Given header and its hash, get epoch switch info from the epoch switch block of that epoch, // header is allow to be nil. func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types.Header, hash common.Hash) (*types.EpochSwitchInfo, error) { - e, ok := x.epochSwitches.Get(hash) - if ok { - epochSwitchInfo := e.(*types.EpochSwitchInfo) + epochSwitchInfo, ok := x.epochSwitches.Get(hash) + if ok && epochSwitchInfo != nil { log.Debug("[getEpochSwitchInfo] cache hit", "number", epochSwitchInfo.EpochSwitchBlockInfo.Number, "hash", hash.Hex()) return epochSwitchInfo, nil } @@ -88,7 +87,7 @@ func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types x.epochSwitches.Add(hash, epochSwitchInfo) return epochSwitchInfo, nil } - epochSwitchInfo, err := x.getEpochSwitchInfo(chain, nil, h.ParentHash) + epochSwitchInfo, err = x.getEpochSwitchInfo(chain, nil, h.ParentHash) if err != nil { log.Error("[getEpochSwitchInfo] recursive error", "err", err, "hash", hash.Hex(), "number", h.Number.Uint64()) return nil, err diff --git a/consensus/XDPoS/engines/engine_v2/snapshot.go b/consensus/XDPoS/engines/engine_v2/snapshot.go index ccae59841278..de4fb76d82bf 100644 --- a/consensus/XDPoS/engines/engine_v2/snapshot.go +++ b/consensus/XDPoS/engines/engine_v2/snapshot.go @@ -84,8 +84,7 @@ func (x *XDPoS_v2) getSnapshot(chain consensus.ChainReader, number uint64, isGap log.Debug("get snapshot from gap block", "number", gapBlockNum, "hash", gapBlockHash.Hex()) // If an in-memory SnapshotV2 was found, use that - if s, ok := x.snapshots.Get(gapBlockHash); ok { - snap := s.(*SnapshotV2) + if snap, ok := x.snapshots.Get(gapBlockHash); ok && snap != nil { log.Trace("Loaded snapshot from memory", "number", gapBlockNum, "hash", gapBlockHash) return snap, nil } diff --git a/consensus/XDPoS/engines/engine_v2/utils.go b/consensus/XDPoS/engines/engine_v2/utils.go index b95d12c5f222..49d79177e8c1 100644 --- a/consensus/XDPoS/engines/engine_v2/utils.go +++ b/consensus/XDPoS/engines/engine_v2/utils.go @@ -13,7 +13,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/rlp" - lru "github.com/hashicorp/golang-lru" "golang.org/x/crypto/sha3" ) @@ -47,11 +46,11 @@ func sigHash(header *types.Header) (hash common.Hash) { return hash } -func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) { +func ecrecover(header *types.Header, sigcache *utils.SigLRU) (common.Address, error) { // If the signature's already cached, return that hash := header.Hash() if address, known := sigcache.Get(hash); known { - return address.(common.Address), nil + return address, nil } // Recover the public key and the Ethereum address @@ -224,9 +223,8 @@ func (x *XDPoS_v2) CalculateMissingRounds(chain consensus.ChainReader, header *t func (x *XDPoS_v2) getBlockByEpochNumberInCache(chain consensus.ChainReader, estRound types.Round) *types.BlockInfo { epochSwitchInCache := make([]*types.BlockInfo, 0) for r := estRound; r < estRound+types.Round(x.config.Epoch); r++ { - info, ok := x.round2epochBlockInfo.Get(r) - if ok { - blockInfo := info.(*types.BlockInfo) + blockInfo, ok := x.round2epochBlockInfo.Get(r) + if ok && blockInfo != nil { epochSwitchInCache = append(epochSwitchInCache, blockInfo) } } diff --git a/consensus/XDPoS/engines/engine_v2/verifyHeader.go b/consensus/XDPoS/engines/engine_v2/verifyHeader.go index 4e18e2031e68..8341ae1c6be8 100644 --- a/consensus/XDPoS/engines/engine_v2/verifyHeader.go +++ b/consensus/XDPoS/engines/engine_v2/verifyHeader.go @@ -190,6 +190,6 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade return utils.ErrNotItsTurn } - x.verifiedHeaders.Add(header.Hash(), true) + x.verifiedHeaders.Add(header.Hash(), struct{}{}) return nil } diff --git a/consensus/XDPoS/utils/types.go b/consensus/XDPoS/utils/types.go index b5d116834110..fc4aaf0463aa 100644 --- a/consensus/XDPoS/utils/types.go +++ b/consensus/XDPoS/utils/types.go @@ -7,6 +7,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate" "github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/lru" "github.com/XinFinOrg/XDPoSChain/common/prque" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/clique" @@ -79,3 +80,5 @@ type EpochNumInfo struct { EpochFirstBlockNumber *big.Int `json:"firstBlock"` EpochLastBlockNumber *big.Int `json:"lastBlock"` } + +type SigLRU = lru.Cache[common.Hash, common.Address] diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index bb1e8ef1189c..d6c7f42bde2a 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -29,6 +29,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/accounts" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/hexutil" + "github.com/XinFinOrg/XDPoSChain/common/lru" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/misc" "github.com/XinFinOrg/XDPoSChain/core/state" @@ -39,7 +40,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rlp" "github.com/XinFinOrg/XDPoSChain/rpc" - lru "github.com/hashicorp/golang-lru" "golang.org/x/crypto/sha3" ) @@ -173,11 +173,11 @@ func sigHash(header *types.Header) (hash common.Hash) { } // ecrecover extracts the Ethereum account address from a signed header. -func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) { +func ecrecover(header *types.Header, sigcache *sigLRU) (common.Address, error) { // If the signature's already cached, return that hash := header.Hash() if address, known := sigcache.Get(hash); known { - return address.(common.Address), nil + return address, nil } // Retrieve the signature from the header extra-data if len(header.Extra) < extraSeal { @@ -203,8 +203,8 @@ type Clique struct { config *params.CliqueConfig // Consensus engine configuration parameters db ethdb.Database // Database to store and retrieve snapshot checkpoints - recents *lru.ARCCache // Snapshots for recent block to speed up reorgs - signatures *lru.ARCCache // Signatures of recent blocks to speed up mining + recents *lru.Cache[common.Hash, *Snapshot] // Snapshots for recent block to speed up reorgs + signatures *sigLRU // Signatures of recent blocks to speed up mining proposals map[common.Address]bool // Current list of proposals we are pushing @@ -221,15 +221,12 @@ func New(config *params.CliqueConfig, db ethdb.Database) *Clique { if conf.Epoch == 0 { conf.Epoch = epochLength } - // Allocate the snapshot caches and create the engine - recents, _ := lru.NewARC(inmemorySnapshots) - signatures, _ := lru.NewARC(inmemorySignatures) return &Clique{ config: &conf, db: db, - recents: recents, - signatures: signatures, + recents: lru.NewCache[common.Hash, *Snapshot](inmemorySnapshots), + signatures: lru.NewCache[common.Hash, common.Address](inmemorySignatures), proposals: make(map[common.Address]bool), } } @@ -396,8 +393,8 @@ func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash commo ) for { // If an in-memory snapshot was found, use that - if s, ok := c.recents.Get(hash); ok { - snap = s.(*Snapshot) + if s, ok := c.recents.Get(hash); ok && s != nil { + snap = s break } // If an on-disk checkpoint snapshot can be found, use that diff --git a/consensus/clique/snapshot.go b/consensus/clique/snapshot.go index bc89168aff0f..ff7c5a4c8e9a 100644 --- a/consensus/clique/snapshot.go +++ b/consensus/clique/snapshot.go @@ -21,10 +21,10 @@ import ( "encoding/json" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/lru" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/params" - lru "github.com/hashicorp/golang-lru" ) // Vote represents a single vote that an authorized signer made to modify the @@ -43,10 +43,12 @@ type Tally struct { Votes int `json:"votes"` // Number of votes until now wanting to pass the proposal } +type sigLRU = lru.Cache[common.Hash, common.Address] + // Snapshot is the state of the authorization voting at a given point in time. type Snapshot struct { config *params.CliqueConfig // Consensus engine parameters to fine tune behavior - sigcache *lru.ARCCache // Cache of recent block signatures to speed up ecrecover + sigcache *sigLRU // Cache of recent block signatures to speed up ecrecover Number uint64 `json:"number"` // Block number where the snapshot was created Hash common.Hash `json:"hash"` // Block hash where the snapshot was created @@ -59,7 +61,7 @@ type Snapshot struct { // newSnapshot creates a new snapshot with the specified startup parameters. This // method does not initialize the set of recent signers, so only ever use if for // the genesis block. -func newSnapshot(config *params.CliqueConfig, sigcache *lru.ARCCache, number uint64, hash common.Hash, signers []common.Address) *Snapshot { +func newSnapshot(config *params.CliqueConfig, sigcache *sigLRU, number uint64, hash common.Hash, signers []common.Address) *Snapshot { snap := &Snapshot{ config: config, sigcache: sigcache, @@ -76,7 +78,7 @@ func newSnapshot(config *params.CliqueConfig, sigcache *lru.ARCCache, number uin } // loadSnapshot loads an existing snapshot from the database. -func loadSnapshot(config *params.CliqueConfig, sigcache *lru.ARCCache, db ethdb.Database, hash common.Hash) (*Snapshot, error) { +func loadSnapshot(config *params.CliqueConfig, sigcache *sigLRU, db ethdb.Database, hash common.Hash) (*Snapshot, error) { blob, err := db.Get(append([]byte("clique-"), hash[:]...)) if err != nil { return nil, err diff --git a/consensus/ethash/ethash.go b/consensus/ethash/ethash.go index 394d495a07e8..96dd5bb68d5d 100644 --- a/consensus/ethash/ethash.go +++ b/consensus/ethash/ethash.go @@ -32,12 +32,12 @@ import ( "time" "unsafe" + lrupkg "github.com/XinFinOrg/XDPoSChain/common/lru" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/metrics" "github.com/XinFinOrg/XDPoSChain/rpc" mmap "github.com/edsrzf/mmap-go" - "github.com/hashicorp/golang-lru/simplelru" ) var ErrInvalidDumpMagic = errors.New("invalid dump magic") @@ -144,34 +144,45 @@ func memoryMapAndGenerate(path string, size uint64, generator func(buffer []uint return memoryMap(path) } +type cacheOrDataset interface { + *cache | *dataset +} + // lru tracks caches or datasets by their last use time, keeping at most N of them. -type lru struct { +type lru[T cacheOrDataset] struct { what string - new func(epoch uint64) interface{} + new func(epoch uint64) T mu sync.Mutex // Items are kept in a LRU cache, but there is a special case: // We always keep an item for (highest seen epoch) + 1 as the 'future item'. - cache *simplelru.LRU + cache lrupkg.BasicLRU[uint64, T] future uint64 - futureItem interface{} + futureItem T } -// newlru create a new least-recently-used cache for ither the verification caches +// newlru create a new least-recently-used cache for either the verification caches // or the mining datasets. -func newlru(what string, maxItems int, new func(epoch uint64) interface{}) *lru { - if maxItems <= 0 { - maxItems = 1 +func newlru[T cacheOrDataset](maxItems int, new func(epoch uint64) T) *lru[T] { + var what string + switch any(T(nil)).(type) { + case *cache: + what = "cache" + case *dataset: + what = "dataset" + default: + panic("unknown type") + } + return &lru[T]{ + what: what, + new: new, + cache: lrupkg.NewBasicLRU[uint64, T](maxItems), } - cache, _ := simplelru.NewLRU(maxItems, func(key, value interface{}) { - log.Trace("Evicted ethash "+what, "epoch", key) - }) - return &lru{what: what, new: new, cache: cache} } // get retrieves or creates an item for the given epoch. The first return value is always // non-nil. The second return value is non-nil if lru thinks that an item will be useful in // the near future. -func (lru *lru) get(epoch uint64) (item, future interface{}) { +func (lru *lru[T]) get(epoch uint64) (item, future T) { lru.mu.Lock() defer lru.mu.Unlock() @@ -205,9 +216,8 @@ type cache struct { once sync.Once // Ensures the cache is generated only once } -// newCache creates a new ethash verification cache and returns it as a plain Go -// interface to be usable in an LRU cache. -func newCache(epoch uint64) interface{} { +// newCache creates a new ethash verification cache. +func newCache(epoch uint64) *cache { return &cache{epoch: epoch} } @@ -283,7 +293,7 @@ type dataset struct { // newDataset creates a new ethash mining dataset and returns it as a plain Go // interface to be usable in an LRU cache. -func newDataset(epoch uint64) interface{} { +func newDataset(epoch uint64) *dataset { return &dataset{epoch: epoch} } @@ -394,13 +404,13 @@ type Config struct { type Ethash struct { config Config - caches *lru // In memory caches to avoid regenerating too often - datasets *lru // In memory datasets to avoid regenerating too often + caches *lru[*cache] // In memory caches to avoid regenerating too often + datasets *lru[*dataset] // In memory datasets to avoid regenerating too often // Mining related fields - rand *rand.Rand // Properly seeded random source for nonces - threads int // Number of threads to mine on if mining - update chan struct{} // Notification channel to update mining parameters + rand *rand.Rand // Properly seeded random source for nonces + threads int // Number of threads to mine on if mining + update chan struct{} // Notification channel to update mining parameters hashrate *metrics.Meter // Meter tracking the average hashrate // The fields below are hooks for testing @@ -425,8 +435,8 @@ func New(config Config) *Ethash { } return &Ethash{ config: config, - caches: newlru("cache", config.CachesInMem, newCache), - datasets: newlru("dataset", config.DatasetsInMem, newDataset), + caches: newlru(config.CachesInMem, newCache), + datasets: newlru(config.DatasetsInMem, newDataset), update: make(chan struct{}), hashrate: metrics.NewMeter(), } @@ -494,15 +504,13 @@ func NewShared() *Ethash { // stored on disk, and finally generating one if none can be found. func (ethash *Ethash) cache(block uint64) *cache { epoch := block / epochLength - currentI, futureI := ethash.caches.get(epoch) - current := currentI.(*cache) + current, future := ethash.caches.get(epoch) // Wait for generation finish. current.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest) // If we need a new future cache, now's a good time to regenerate it. - if futureI != nil { - future := futureI.(*cache) + if future != nil { go future.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest) } return current @@ -513,15 +521,13 @@ func (ethash *Ethash) cache(block uint64) *cache { // stored on disk, and finally generating one if none can be found. func (ethash *Ethash) dataset(block uint64) *dataset { epoch := block / epochLength - currentI, futureI := ethash.datasets.get(epoch) - current := currentI.(*dataset) + current, future := ethash.datasets.get(epoch) // Wait for generation finish. current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest) // If we need a new future dataset, now's a good time to regenerate it. - if futureI != nil { - future := futureI.(*dataset) + if future != nil { go future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest) } diff --git a/contracts/utils.go b/contracts/utils.go index 5e236743ea27..b309e5bdd24e 100644 --- a/contracts/utils.go +++ b/contracts/utils.go @@ -331,20 +331,19 @@ func GetRewardForCheckpoint(c *XDPoS.XDPoS, chain consensus.ChainReader, header for i := prevCheckpoint + (rCheckpoint * 2) - 1; i >= startBlockNumber; i-- { header = chain.GetHeader(header.ParentHash, i) mapBlkHash[i] = header.Hash() - signData, ok := c.GetCachedSigningTxs(header.Hash()) + signingTxs, ok := c.GetCachedSigningTxs(header.Hash()) if !ok { log.Debug("Failed get from cached", "hash", header.Hash().String(), "number", i) block := chain.GetBlock(header.Hash(), i) txs := block.Transactions() if !chain.Config().IsTIPSigning(header.Number) { receipts := core.GetBlockReceipts(c.GetDb(), header.Hash(), i) - signData = c.CacheNoneTIPSigningTxs(header, txs, receipts) + signingTxs = c.CacheNoneTIPSigningTxs(header, txs, receipts) } else { - signData = c.CacheSigningTxs(header.Hash(), txs) + signingTxs = c.CacheSigningTxs(header.Hash(), txs) } } - txs := signData.([]*types.Transaction) - for _, tx := range txs { + for _, tx := range signingTxs { blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:]) from := *tx.From() data[blkHash] = append(data[blkHash], from) diff --git a/core/state/trc21_reader.go b/core/state/trc21_reader.go index af5fd25b1390..561cebb5c766 100644 --- a/core/state/trc21_reader.go +++ b/core/state/trc21_reader.go @@ -5,8 +5,7 @@ import ( "math/big" "github.com/XinFinOrg/XDPoSChain/common" - lru "github.com/hashicorp/golang-lru" - + "github.com/XinFinOrg/XDPoSChain/common/lru" ) var ( @@ -22,21 +21,18 @@ var ( } transferFuncHex = common.Hex2Bytes("0xa9059cbb") transferFromFuncHex = common.Hex2Bytes("0x23b872dd") - cache, _ = lru.NewARC(128) + cache = lru.NewCache[common.Hash, map[common.Address]*big.Int](128) ) func GetTRC21FeeCapacityFromStateWithCache(trieRoot common.Hash, statedb *StateDB) map[common.Address]*big.Int { if statedb == nil { return map[common.Address]*big.Int{} } - data, _ := cache.Get(trieRoot) - var info map[common.Address]*big.Int - if data != nil { - info = data.(map[common.Address]*big.Int) - } else { + info, ok := cache.Get(trieRoot) + if !ok || info == nil { info = GetTRC21FeeCapacityFromState(statedb) + cache.Add(trieRoot, info) } - cache.Add(trieRoot, info) tokensFee := map[common.Address]*big.Int{} for key, value := range info { tokensFee[key] = big.NewInt(0).SetBytes(value.Bytes()) diff --git a/eth/fetcher/fetcher.go b/eth/fetcher/fetcher.go index ed362f027635..259781a6bb11 100644 --- a/eth/fetcher/fetcher.go +++ b/eth/fetcher/fetcher.go @@ -22,9 +22,8 @@ import ( "math/rand" "time" - lru "github.com/hashicorp/golang-lru" - "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/lru" "github.com/XinFinOrg/XDPoSChain/common/prque" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/core/types" @@ -135,7 +134,8 @@ type Fetcher struct { queue *prque.Prque // Queue containing the import operations (block number sorted) queues map[string]int // Per peer block counts to prevent memory exhaustion queued map[common.Hash]*inject // Set of already queued blocks (to dedup imports) - knowns *lru.ARCCache + knowns *lru.Cache[common.Hash, struct{}] + // Callbacks getBlock blockRetrievalFn // Retrieves a block from the local chain verifyHeader headerVerifierFn // Checks if a block's headers have a valid proof of work @@ -157,7 +157,6 @@ type Fetcher struct { // New creates a block fetcher to retrieve blocks based on hash announcements. func New(getBlock blockRetrievalFn, verifyHeader headerVerifierFn, handleProposedBlock proposeBlockHandlerFn, broadcastBlock blockBroadcasterFn, chainHeight chainHeightFn, insertBlock blockInsertFn, prepareBlock blockPrepareFn, dropPeer peerDropFn) *Fetcher { - knownBlocks, _ := lru.NewARC(blockLimit) return &Fetcher{ notify: make(chan *announce), inject: make(chan *inject), @@ -174,7 +173,7 @@ func New(getBlock blockRetrievalFn, verifyHeader headerVerifierFn, handlePropose queue: prque.New(nil), queues: make(map[string]int), queued: make(map[common.Hash]*inject), - knowns: knownBlocks, + knowns: lru.NewCache[common.Hash, struct{}](blockLimit), getBlock: getBlock, verifyHeader: verifyHeader, handleProposedBlock: handleProposedBlock, @@ -641,7 +640,7 @@ func (f *Fetcher) enqueue(peer string, block *types.Block) { } f.queues[peer] = count f.queued[hash] = op - f.knowns.Add(hash, true) + f.knowns.Add(hash, struct{}{}) f.queue.Push(op, -int64(block.NumberU64())) if f.queueChangeHook != nil { f.queueChangeHook(op.block.Hash(), true) diff --git a/eth/handler.go b/eth/handler.go index 503671eb5280..7ac5c633f720 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -25,9 +25,8 @@ import ( "sync/atomic" "time" - lru "github.com/hashicorp/golang-lru" - "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/lru" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" "github.com/XinFinOrg/XDPoSChain/consensus/misc" @@ -104,14 +103,14 @@ type ProtocolManager struct { // wait group is used for graceful shutdowns during downloading // and processing wg sync.WaitGroup - knownTxs *lru.Cache - knowOrderTxs *lru.Cache - knowLendingTxs *lru.Cache + knownTxs *lru.Cache[common.Hash, struct{}] + knowOrderTxs *lru.Cache[common.Hash, struct{}] + knowLendingTxs *lru.Cache[common.Hash, struct{}] // V2 messages - knownVotes *lru.Cache - knownSyncInfos *lru.Cache - knownTimeouts *lru.Cache + knownVotes *lru.Cache[common.Hash, struct{}] + knownSyncInfos *lru.Cache[common.Hash, struct{}] + knownTimeouts *lru.Cache[common.Hash, struct{}] } // NewProtocolManagerEx add order pool to protocol @@ -128,14 +127,6 @@ func NewProtocolManagerEx(config *params.ChainConfig, mode downloader.SyncMode, // NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable // with the ethereum network. func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, networkID uint64, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) { - knownTxs, _ := lru.New(maxKnownTxs) - knowOrderTxs, _ := lru.New(maxKnownOrderTxs) - knowLendingTxs, _ := lru.New(maxKnownLendingTxs) - - knownVotes, _ := lru.New(maxKnownVote) - knownSyncInfos, _ := lru.New(maxKnownSyncInfo) - knownTimeouts, _ := lru.New(maxKnownTimeout) - // Create the protocol manager with the base fields manager := &ProtocolManager{ networkId: networkID, @@ -148,12 +139,12 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne noMorePeers: make(chan struct{}), txsyncCh: make(chan *txsync), quitSync: make(chan struct{}), - knownTxs: knownTxs, - knowOrderTxs: knowOrderTxs, - knowLendingTxs: knowLendingTxs, - knownVotes: knownVotes, - knownSyncInfos: knownSyncInfos, - knownTimeouts: knownTimeouts, + knownTxs: lru.NewCache[common.Hash, struct{}](maxKnownTxs), + knowOrderTxs: lru.NewCache[common.Hash, struct{}](maxKnownOrderTxs), + knowLendingTxs: lru.NewCache[common.Hash, struct{}](maxKnownLendingTxs), + knownVotes: lru.NewCache[common.Hash, struct{}](maxKnownVote), + knownSyncInfos: lru.NewCache[common.Hash, struct{}](maxKnownSyncInfo), + knownTimeouts: lru.NewCache[common.Hash, struct{}](maxKnownTimeout), orderpool: nil, lendingpool: nil, orderTxSub: nil, @@ -776,9 +767,10 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { return errResp(ErrDecode, "transaction %d is nil", i) } p.MarkTransaction(tx.Hash()) - exist, _ := pm.knownTxs.ContainsOrAdd(tx.Hash(), true) - if exist { + if pm.knownTxs.Contains(tx.Hash()) { log.Trace("Discard known tx", "hash", tx.Hash(), "nonce", tx.Nonce(), "to", tx.To()) + } else { + pm.knownTxs.Add(tx.Hash(), struct{}{}) } } @@ -801,11 +793,11 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { return errResp(ErrDecode, "transaction %d is nil", i) } p.MarkOrderTransaction(tx.Hash()) - exist, _ := pm.knowOrderTxs.ContainsOrAdd(tx.Hash(), true) - if exist { + if pm.knowOrderTxs.Contains(tx.Hash()) { log.Trace("Discard known tx", "hash", tx.Hash(), "nonce", tx.Nonce()) + } else { + pm.knowOrderTxs.Add(tx.Hash(), struct{}{}) } - } if pm.orderpool != nil { @@ -829,11 +821,11 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { return errResp(ErrDecode, "transaction %d is nil", i) } p.MarkLendingTransaction(tx.Hash()) - exist, _ := pm.knowLendingTxs.ContainsOrAdd(tx.Hash(), true) - if exist { + if pm.knowLendingTxs.Contains(tx.Hash()) { log.Trace("Discard known tx", "hash", tx.Hash(), "nonce", tx.Nonce()) + } else { + pm.knowLendingTxs.Add(tx.Hash(), struct{}{}) } - } if pm.lendingpool != nil { @@ -850,11 +842,11 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } p.MarkVote(vote.Hash()) - exist, _ := pm.knownVotes.ContainsOrAdd(vote.Hash(), true) - if !exist { - go pm.bft.Vote(p.id, &vote) - } else { + if pm.knownVotes.Contains(vote.Hash()) { log.Trace("Discarded vote, known vote", "vote hash", vote.Hash(), "voted block hash", vote.ProposedBlockInfo.Hash.Hex(), "number", vote.ProposedBlockInfo.Number, "round", vote.ProposedBlockInfo.Round) + } else { + pm.knownVotes.Add(vote.Hash(), struct{}{}) + go pm.bft.Vote(p.id, &vote) } case msg.Code == TimeoutMsg: @@ -868,12 +860,11 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } p.MarkTimeout(timeout.Hash()) - exist, _ := pm.knownTimeouts.ContainsOrAdd(timeout.Hash(), true) - - if !exist { - go pm.bft.Timeout(p.id, &timeout) - } else { + if pm.knownTimeouts.Contains(timeout.Hash()) { log.Trace("Discarded Timeout, known Timeout", "Signature", timeout.Signature, "hash", timeout.Hash(), "round", timeout.Round) + } else { + pm.knownTimeouts.Add(timeout.Hash(), struct{}{}) + go pm.bft.Timeout(p.id, &timeout) } case msg.Code == SyncInfoMsg: @@ -887,11 +878,11 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } p.MarkSyncInfo(syncInfo.Hash()) - exist, _ := pm.knownSyncInfos.ContainsOrAdd(syncInfo.Hash(), true) - if !exist { - go pm.bft.SyncInfo(p.id, &syncInfo) - } else { + if pm.knownSyncInfos.Contains(syncInfo.Hash()) { log.Trace("Discarded SyncInfo, known SyncInfo", "hash", syncInfo.Hash()) + } else { + pm.knownSyncInfos.Add(syncInfo.Hash(), struct{}{}) + go pm.bft.SyncInfo(p.id, &syncInfo) } default: diff --git a/eth/hooks/engine_v1_hooks.go b/eth/hooks/engine_v1_hooks.go index 553ffc591671..74f55ed278a9 100644 --- a/eth/hooks/engine_v1_hooks.go +++ b/eth/hooks/engine_v1_hooks.go @@ -141,15 +141,14 @@ func AttachConsensusV1Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf if blockNumber%common.MergeSignRange == 0 { mapBlockHash[bhash] = true } - signData, ok := adaptor.GetCachedSigningTxs(bhash) + signingTxs, ok := adaptor.GetCachedSigningTxs(bhash) if !ok { block := chain.GetBlock(bhash, blockNumber) txs := block.Transactions() - signData = adaptor.CacheSigningTxs(bhash, txs) + signingTxs = adaptor.CacheSigningTxs(bhash, txs) } - txs := signData.([]*types.Transaction) // Check signer signed? - for _, tx := range txs { + for _, tx := range signingTxs { blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:]) from := *tx.From() if mapBlockHash[blkHash] { diff --git a/eth/hooks/engine_v2_hooks.go b/eth/hooks/engine_v2_hooks.go index fde8e924879f..bd5b0ffdd1df 100644 --- a/eth/hooks/engine_v2_hooks.go +++ b/eth/hooks/engine_v2_hooks.go @@ -115,15 +115,14 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf if blockNumber%common.MergeSignRange == 0 { mapBlockHash[bhash] = true } - signData, ok := adaptor.GetCachedSigningTxs(bhash) + signingTxs, ok := adaptor.GetCachedSigningTxs(bhash) if !ok { block := chain.GetBlock(bhash, blockNumber) txs := block.Transactions() - signData = adaptor.CacheSigningTxs(bhash, txs) + signingTxs = adaptor.CacheSigningTxs(bhash, txs) } - txs := signData.([]*types.Transaction) // Check signer signed? - for _, tx := range txs { + for _, tx := range signingTxs { blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:]) from := *tx.From() if mapBlockHash[blkHash] { @@ -245,15 +244,14 @@ func GetSigningTxCount(c *XDPoS.XDPoS, chain consensus.ChainReader, header *type } } mapBlkHash[i] = header.Hash() - signData, ok := c.GetCachedSigningTxs(header.Hash()) + signingTxs, ok := c.GetCachedSigningTxs(header.Hash()) if !ok { log.Debug("Failed get from cached", "hash", header.Hash().String(), "number", i) block := chain.GetBlock(header.Hash(), i) txs := block.Transactions() - signData = c.CacheSigningTxs(header.Hash(), txs) + signingTxs = c.CacheSigningTxs(header.Hash(), txs) } - txs := signData.([]*types.Transaction) - for _, tx := range txs { + for _, tx := range signingTxs { blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:]) from := *tx.From() data[blkHash] = append(data[blkHash], from) diff --git a/go.mod b/go.mod index 28a24f8b6b38..d948f4cea3b8 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,6 @@ require ( github.com/golang/protobuf v1.5.3 github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb github.com/gorilla/websocket v1.4.2 - github.com/hashicorp/golang-lru v0.5.4 github.com/holiman/uint256 v1.2.4 github.com/huin/goupnp v1.3.0 github.com/jackpal/go-nat-pmp v1.0.2 diff --git a/go.sum b/go.sum index 68019c92aba9..bb2ab2b777cb 100644 --- a/go.sum +++ b/go.sum @@ -92,8 +92,6 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= diff --git a/light/lightchain.go b/light/lightchain.go index 236d1ed85bf9..26b5df09172b 100644 --- a/light/lightchain.go +++ b/light/lightchain.go @@ -25,6 +25,7 @@ import ( "time" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/lru" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/rawdb" @@ -35,7 +36,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rlp" - lru "github.com/hashicorp/golang-lru" ) var ( @@ -59,9 +59,9 @@ type LightChain struct { mu sync.RWMutex chainmu sync.RWMutex - bodyCache *lru.Cache // Cache for the most recent block bodies - bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format - blockCache *lru.Cache // Cache for the most recent entire blocks + bodyCache *lru.Cache[common.Hash, *types.Body] + bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue] + blockCache *lru.Cache[common.Hash, *types.Block] quit chan struct{} running int32 // running must be called automically @@ -76,17 +76,13 @@ type LightChain struct { // available in the database. It initialises the default Ethereum header // validator. func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.Engine) (*LightChain, error) { - bodyCache, _ := lru.New(bodyCacheLimit) - bodyRLPCache, _ := lru.New(bodyCacheLimit) - blockCache, _ := lru.New(blockCacheLimit) - bc := &LightChain{ chainDb: odr.Database(), odr: odr, quit: make(chan struct{}), - bodyCache: bodyCache, - bodyRLPCache: bodyRLPCache, - blockCache: blockCache, + bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit), + bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit), + blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit), engine: engine, } var err error @@ -218,9 +214,8 @@ func (lc *LightChain) State() (*state.StateDB, error) { // or ODR service by hash, caching it if found. func (lc *LightChain) GetBody(ctx context.Context, hash common.Hash) (*types.Body, error) { // Short circuit if the body's already in the cache, retrieve otherwise - if cached, ok := lc.bodyCache.Get(hash); ok { - body := cached.(*types.Body) - return body, nil + if cached, ok := lc.bodyCache.Get(hash); ok && cached != nil { + return cached, nil } body, err := GetBody(ctx, lc.odr, hash, lc.hc.GetBlockNumber(hash)) if err != nil { @@ -236,7 +231,7 @@ func (lc *LightChain) GetBody(ctx context.Context, hash common.Hash) (*types.Bod func (lc *LightChain) GetBodyRLP(ctx context.Context, hash common.Hash) (rlp.RawValue, error) { // Short circuit if the body's already in the cache, retrieve otherwise if cached, ok := lc.bodyRLPCache.Get(hash); ok { - return cached.(rlp.RawValue), nil + return cached, nil } body, err := GetBodyRLP(ctx, lc.odr, hash, lc.hc.GetBlockNumber(hash)) if err != nil { @@ -258,8 +253,8 @@ func (lc *LightChain) HasBlock(hash common.Hash, number uint64) bool { // caching it if found. func (lc *LightChain) GetBlock(ctx context.Context, hash common.Hash, number uint64) (*types.Block, error) { // Short circuit if the block's already in the cache, retrieve otherwise - if block, ok := lc.blockCache.Get(hash); ok { - return block.(*types.Block), nil + if block, ok := lc.blockCache.Get(hash); ok && block != nil { + return block, nil } block, err := GetBlock(ctx, lc.odr, hash, number) if err != nil { From 6114a3a13b6899389608ec6102a845ddcfabe7b1 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 25 Dec 2024 17:17:25 +0800 Subject: [PATCH 384/479] core: remove unused function reorgTxMatches --- core/blockchain.go | 41 ----------------------------------------- 1 file changed, 41 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 25ec81a7aa75..96dd45474573 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2315,9 +2315,6 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { } }() } - if bc.chainConfig.IsTIPXDCXReceiver(commonBlock.Number()) && bc.chainConfig.XDPoS != nil && commonBlock.NumberU64() > bc.chainConfig.XDPoS.Epoch { - bc.reorgTxMatches(deletedTxs, newChain) - } return nil } @@ -2702,44 +2699,6 @@ func (bc *BlockChain) logExchangeData(block *types.Block) { } } -func (bc *BlockChain) reorgTxMatches(deletedTxs types.Transactions, newChain types.Blocks) { - engine, ok := bc.Engine().(*XDPoS.XDPoS) - if !ok || engine == nil { - return - } - XDCXService := engine.GetXDCXService() - lendingService := engine.GetLendingService() - if XDCXService == nil || !XDCXService.IsSDKNode() { - return - } - start := time.Now() - defer func() { - //The deferred call's arguments are evaluated immediately, but the function call is not executed until the surrounding function returns - // That's why we should put this log statement in an anonymous function - log.Debug("reorgTxMatches takes", "time", common.PrettyDuration(time.Since(start))) - }() - for _, deletedTx := range deletedTxs { - if deletedTx.IsTradingTransaction() { - log.Debug("Rollback reorg txMatch", "txhash", deletedTx.Hash()) - if err := XDCXService.RollbackReorgTxMatch(deletedTx.Hash()); err != nil { - log.Crit("Reorg trading failed", "err", err, "hash", deletedTx.Hash()) - } - } - if lendingService != nil && (deletedTx.IsLendingTransaction() || deletedTx.IsLendingFinalizedTradeTransaction()) { - log.Debug("Rollback reorg lendingItem", "txhash", deletedTx.Hash()) - if err := lendingService.RollbackLendingData(deletedTx.Hash()); err != nil { - log.Crit("Reorg lending failed", "err", err, "hash", deletedTx.Hash()) - } - } - } - - // apply new chain - for i := len(newChain) - 1; i >= 0; i-- { - bc.logExchangeData(newChain[i]) - bc.logLendingData(newChain[i]) - } -} - func (bc *BlockChain) logLendingData(block *types.Block) { engine, ok := bc.Engine().(*XDPoS.XDPoS) if !ok || engine == nil { From a5c48e77bdb89909ab4e395fcc7aebb7aace4f3b Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 25 Dec 2024 17:22:28 +0800 Subject: [PATCH 385/479] core: remove unused function writeHeader --- core/blockchain.go | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 96dd45474573..5fe2d5390ddd 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2442,26 +2442,6 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i return bc.hc.InsertHeaderChain(chain, whFunc, start) } -// writeHeader writes a header into the local chain, given that its parent is -// already known. If the total difficulty of the newly inserted header becomes -// greater than the current known TD, the canonical chain is re-routed. -// -// Note: This method is not concurrent-safe with inserting blocks simultaneously -// into the chain, as side effects caused by reorganisations cannot be emulated -// without the real blocks. Hence, writing headers directly should only be done -// in two scenarios: pure-header mode of operation (light clients), or properly -// separated header/block phases (non-archive clients). -func (bc *BlockChain) writeHeader(header *types.Header) error { - bc.wg.Add(1) - defer bc.wg.Done() - - bc.mu.Lock() - defer bc.mu.Unlock() - - _, err := bc.hc.WriteHeader(header) - return err -} - // CurrentHeader retrieves the current head header of the canonical chain. The // header is retrieved from the HeaderChain's internal cache. func (bc *BlockChain) CurrentHeader() *types.Header { From e2afaec8b0268786007ba84075075be8d3073f89 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 25 Dec 2024 17:25:51 +0800 Subject: [PATCH 386/479] core: remove unused variables in BlockChain struct --- core/blockchain.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 5fe2d5390ddd..217e3be9e00a 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -153,7 +153,6 @@ type BlockChain struct { chainmu sync.RWMutex // blockchain insertion lock procmu sync.RWMutex // block processor lock - checkpoint int // checkpoint counts towards the new checkpoint currentBlock atomic.Value // Current head of the block chain currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!) @@ -181,9 +180,8 @@ type BlockChain struct { validator Validator // block and state validator interface vmConfig vm.Config - shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block. - IPCEndpoint string - Client bind.ContractBackend // Global ipc client instance. + IPCEndpoint string + Client bind.ContractBackend // Global ipc client instance. // Blocks hash array by block number // cache field for tracking finality purpose, can't use for tracking block vs block relationship From 0686b4428fcf8bb35b1bb80dc25259f8de72caf8 Mon Sep 17 00:00:00 2001 From: benjamin202410 Date: Wed, 25 Dec 2024 22:59:24 -0800 Subject: [PATCH 387/479] upgrade to golang 1.22 (#763) Co-authored-by: liam.lai --- .github/workflows/ci.yml | 2 +- .travis.yml.bak | 180 --------------------------------------- Dockerfile | 2 +- Dockerfile.bootnode | 2 +- Dockerfile.node | 2 +- Makefile | 2 +- build/ci.go | 2 +- cicd/Dockerfile | 2 +- go.mod | 2 +- 9 files changed, 8 insertions(+), 188 deletions(-) delete mode 100644 .travis.yml.bak diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2c41c2b2a65f..9d568dc50938 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,7 +57,7 @@ jobs: uses: actions/setup-go@v5 with: cache: false - go-version: '1.21.x' + go-version: '1.22.x' - name: Run tests run: ${{ matrix.script }} env: diff --git a/.travis.yml.bak b/.travis.yml.bak deleted file mode 100644 index b4e3db53b3b4..000000000000 --- a/.travis.yml.bak +++ /dev/null @@ -1,180 +0,0 @@ -sudo: required -language: go -go_import_path: github.com/XinFinOrg/XDPoSChain - -on: - branches: - - master - - dev-upgrade - tags: true - -env: - global: - - GOPROXY=https://proxy.golang.org - - GO111MODULE=on - # Terraform env - - tf_version=1.3.0 - # Setting terraform init CLI options - https://www.terraform.io/docs/commands/init.html - - tf_init_cli_options=" -input=false" - # Set terraform validation CLI options - https://www.terraform.io/docs/commands/validate.html - - tf_validation_cli_options="" - # Set terraform plan CLI options - https://www.terraform.io/docs/commands/plan.html - - tf_plan_cli_options=" -lock=false -input=false" - # Set terraform apply CLI options - https://www.terraform.io/docs/commands/apply.html - - tf_apply_cli_options=" -auto-approve -input=false" - - -jobs: - include: - - stage: Tests - os: linux - dist: bionic - go: 1.21.x - env: - - GO111MODULE=auto - name: A-B tests - script: travis_retry go run build/ci.go test -coverage $(go list ./... | grep "github.com\/XinFinOrg\/XDPoSChain\/[a-b].*") - - script: travis_retry go run build/ci.go test -coverage $(go list ./... | grep "github.com\/XinFinOrg\/XDPoSChain\/c[a-m].*") - os: linux - dist: bionic - go: 1.21.x - env: - - GO111MODULE=auto - name: C-[a-m] tests - - script: travis_retry go run build/ci.go test -coverage $(go list ./... | grep "github.com\/XinFinOrg\/XDPoSChain\/c[n-o].*") - os: linux - dist: bionic - go: 1.21.x - env: - - GO111MODULE=auto - name: C-[n-o] tests - - script: travis_retry go run build/ci.go test -coverage $(go list ./... | grep "github.com\/XinFinOrg\/XDPoSChain\/c[p-z].*") - os: linux - dist: bionic - go: 1.21.x - env: - - GO111MODULE=auto - name: C-[p-z] tests - - script: travis_retry go run build/ci.go test -coverage $(go list ./... | grep "github.com\/XinFinOrg\/XDPoSChain\/[d-i].*") - os: linux - dist: bionic - go: 1.21.x - env: - - GO111MODULE=auto - name: D-I tests - - script: travis_retry go run build/ci.go test -coverage $(go list ./... | grep "github.com\/XinFinOrg\/XDPoSChain\/[j-n].*") - os: linux - dist: bionic - go: 1.21.x - env: - - GO111MODULE=auto - name: J-N tests - - script: travis_retry go run build/ci.go test -coverage $(go list ./... | grep "github.com\/XinFinOrg\/XDPoSChain\/[o-r].*") - os: linux - dist: bionic - go: 1.21.x - env: - - GO111MODULE=auto - name: O-R tests - - script: travis_retry go run build/ci.go test -coverage $(go list ./... | grep "github.com\/XinFinOrg\/XDPoSChain\/s.*") - os: linux - dist: bionic - go: 1.21.x - env: - - GO111MODULE=auto - name: S tests - - script: travis_retry go run build/ci.go test -coverage $(go list ./... | grep "github.com\/XinFinOrg\/XDPoSChain\/[t-z].*") - os: linux - dist: bionic - go: 1.21.x - env: - - GO111MODULE=auto - name: T-Z tests - - - stage: TAG Build - if: tag IS present - services: - - docker - install: skip - before_script: - - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin - - docker --version # document the version travis is using - - docker build -t xinfinorg/xdposchain:$TRAVIS_TAG -f cicd/Dockerfile . - script: - - docker push xinfinorg/xdposchain:$TRAVIS_TAG - - - stage: (Devnet) Build, and push images - if: branch = dev-upgrade AND type = push AND tag IS blank - services: - - docker - install: skip - before_script: - - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin - - docker --version # document the version travis is using - - docker pull xinfinorg/devnet:latest # build the "previous" tag so that our devnet environment can run with both old and new code at the same time. - - docker tag xinfinorg/devnet:latest xinfinorg/devnet:previous - - docker rmi xinfinorg/devnet:latest - - docker build -t xinfinorg/devnet:latest -f cicd/Dockerfile . - script: - - docker push xinfinorg/devnet:latest - - docker push xinfinorg/devnet:previous - - - stage: (Devnet)Terraform plan - if: branch = dev-upgrade AND type = push AND tag IS blank - dist: xenial - language: bash - install: - - wget https://releases.hashicorp.com/terraform/"$tf_version"/terraform_"$tf_version"_linux_amd64.zip - - unzip terraform_"$tf_version"_linux_amd64.zip - - sudo mv terraform /usr/local/bin/ - - rm terraform_"$tf_version"_linux_amd64.zip - script: - - echo "Pull request detected, creating change plan(Devnet)" - - cd cicd/devnet/terraform - # Terraform init, validate, then create change plan. If any fail, fail validation - - terraform init $tf_init_cli_options - - terraform validate $tf_validation_cli_options - - terraform plan $tf_plan_cli_options - - - stage: (Devnet) Terraform apply - if: branch = dev-upgrade AND type = push AND tag IS blank - dist: xenial - language: bash - install: - # Download and install terraform before each run - - wget https://releases.hashicorp.com/terraform/"$tf_version"/terraform_"$tf_version"_linux_amd64.zip - - unzip terraform_"$tf_version"_linux_amd64.zip - - sudo mv terraform /usr/local/bin/ - - rm terraform_"$tf_version"_linux_amd64.zip - - curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" - - unzip awscliv2.zip - - sudo ./aws/install - - export PATH=$PATH:$HOME/.local/bin # put aws in the path - script: - - echo "Merge detected, executing changes(Devnet)" - - cd cicd/devnet/terraform - # Terraform init and then apply changes to environment - - terraform init $tf_init_cli_options - - terraform apply $tf_apply_cli_options - - sleep 5 - - | - source .env - for ((i=$us_east_2_start;i<$us_east_2_end;i++)); do - echo "Force deploy xdc-$i" - aws ecs update-service --region us-east-2 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager; - done - for ((i=$eu_west_1_start;i<$eu_west_1_end;i++)); do - echo "Force deploy xdc-$i" - aws ecs update-service --region eu-west-1 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager; - done - for ((i=$ap_southeast_2_start;i<$ap_southeast_2_end;i++)); do - echo "Force deploy xdc-$i" - aws ecs update-service --region ap-southeast-2 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager; - done - aws ecs update-service --region ap-southeast-1 --cluster devnet-xdcnode-cluster --service ecs-service-rpc1 --force-new-deployment --no-cli-pager; - - - stage: (Devnet) Send Deployment Notification - if: branch = dev-upgrade AND type = push AND tag IS blank - language: bash - script: - - curl --location --request POST "66.94.98.186:8080/deploy?environment=devnet&service=xdc&version=$TRAVIS_COMMIT" \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 1207545aa792..5aba541a4915 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.21-alpine as builder +FROM golang:1.22-alpine as builder RUN apk add --no-cache make gcc musl-dev linux-headers git diff --git a/Dockerfile.bootnode b/Dockerfile.bootnode index bb906b581f30..374bea3d3cd9 100644 --- a/Dockerfile.bootnode +++ b/Dockerfile.bootnode @@ -1,4 +1,4 @@ -FROM golang:1.21-alpine as builder +FROM golang:1.22-alpine as builder RUN apk add --no-cache make gcc musl-dev linux-headers diff --git a/Dockerfile.node b/Dockerfile.node index 3d6f58495bc0..b3e0f299d928 100644 --- a/Dockerfile.node +++ b/Dockerfile.node @@ -1,4 +1,4 @@ -FROM golang:1.21-alpine as builder +FROM golang:1.22-alpine as builder RUN apk add --no-cache make gcc musl-dev linux-headers git diff --git a/Makefile b/Makefile index 1ab433b070ef..3bd726906ce2 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ GOBIN = $(shell pwd)/build/bin GOFMT = gofmt -GO ?= 1.21.3 +GO ?= 1.22.10 GO_PACKAGES = . GO_FILES := $(shell find $(shell go list -f '{{.Dir}}' $(GO_PACKAGES)) -name \*.go) diff --git a/build/ci.go b/build/ci.go index 7d09ec7c9db6..ff3ea89d10f6 100644 --- a/build/ci.go +++ b/build/ci.go @@ -115,7 +115,7 @@ func doInstall(cmdline []string) { if minor < 21 { log.Println("You have Go version", runtime.Version()) - log.Println("XDC requires at least Go version 1.21 and cannot") + log.Println("XDC requires at least Go version 1.22 and cannot") log.Println("be compiled with an earlier version. Please upgrade your Go installation.") os.Exit(1) } diff --git a/cicd/Dockerfile b/cicd/Dockerfile index bf5b4b5c7988..055492e548bd 100644 --- a/cicd/Dockerfile +++ b/cicd/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.21-alpine as builder +FROM golang:1.22-alpine as builder RUN apk add make build-base linux-headers diff --git a/go.mod b/go.mod index d948f4cea3b8..23a0995a49e8 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/XinFinOrg/XDPoSChain -go 1.21 +go 1.22 require ( github.com/VictoriaMetrics/fastcache v1.12.2 From 48d1f22fdeb11b72813bc4432bef6592021d46bf Mon Sep 17 00:00:00 2001 From: JukLee0ira Date: Mon, 23 Dec 2024 17:53:28 +0800 Subject: [PATCH 388/479] all: simplify function TransitionDb (#20830) --- accounts/abi/bind/backends/simulated.go | 3 +- core/state_processor.go | 2 +- core/state_transition.go | 18 ++++++------ core/token_validator.go | 2 +- eth/api_tracer.go | 4 +-- eth/tracers/testing/calltrace_test.go | 4 +-- eth/tracers/tracers_test.go | 6 ++-- internal/ethapi/api.go | 38 ++++++++++++------------- les/odr_test.go | 4 +-- light/odr_test.go | 2 +- tests/state_test_util.go | 2 +- 11 files changed, 42 insertions(+), 43 deletions(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 12005705c710..bbc8f4373ffd 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -478,8 +478,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.Cal vmenv := vm.NewEVM(evmContext, txContext, statedb, nil, b.config, vm.Config{NoBaseFee: true}) gaspool := new(core.GasPool).AddGas(math.MaxUint64) owner := common.Address{} - res, err, _ := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner) - return res, err + return core.NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner) } // SendTransaction updates the pending block to include the given transaction. diff --git a/core/state_processor.go b/core/state_processor.go index 5fc975c7c72d..2f7980db6326 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -403,7 +403,7 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]* // End Bypass blacklist address // Apply the transaction to the current state (included in the env) - result, err, _ := ApplyMessage(evm, msg, gp, coinbaseOwner) + result, err := ApplyMessage(evm, msg, gp, coinbaseOwner) if err != nil { return nil, 0, err, false diff --git a/core/state_transition.go b/core/state_transition.go index 42e044a8de73..e18064501041 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -194,7 +194,7 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition // the gas used (which includes gas refunds) and an error if it failed. An error always // indicates a core error meaning that the message would always fail for that particular // state and would never be accepted within a block. -func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool, owner common.Address) (*ExecutionResult, error, error) { +func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool, owner common.Address) (*ExecutionResult, error) { return NewStateTransition(evm, msg, gp).TransitionDb(owner) } @@ -317,7 +317,7 @@ func (st *StateTransition) preCheck() error { // // However if any consensus issue encountered, return the error directly with // nil evm execution result. -func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult, error, error) { +func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult, error) { // First check this message satisfies all consensus rules before // applying the message. The rules include these clauses // @@ -330,7 +330,7 @@ func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult, // Check clauses 1-3, buy gas if everything is correct if err := st.preCheck(); err != nil { - return nil, err, nil + return nil, err } var ( @@ -345,16 +345,16 @@ func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult, // Check clauses 4-5, subtract intrinsic gas if everything is correct gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead, rules.IsEIP1559) if err != nil { - return nil, err, nil + return nil, err } if st.gas < gas { - return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas), nil + return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas) } st.gas -= gas // Check whether the init code size has been exceeded. if rules.IsEIP1559 && contractCreation && len(st.data) > params.MaxInitCodeSize { - return nil, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(st.data), params.MaxInitCodeSize), nil + return nil, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(st.data), params.MaxInitCodeSize) } // Execute the preparatory steps for state transition which includes: @@ -365,10 +365,10 @@ func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult, // Check clause 6 value, overflow := uint256.FromBig(msg.Value()) if overflow { - return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From().Hex()), nil + return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From().Hex()) } if !value.IsZero() && !st.evm.Context.CanTransfer(st.state, msg.From(), value.ToBig()) { - return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From().Hex()), nil + return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From().Hex()) } var ( @@ -406,7 +406,7 @@ func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult, UsedGas: st.gasUsed(), Err: vmerr, ReturnData: ret, - }, nil, vmerr + }, nil } func (st *StateTransition) refundGas(refundQuotient uint64) { diff --git a/core/token_validator.go b/core/token_validator.go index e888f37b102e..070a3342ce8e 100644 --- a/core/token_validator.go +++ b/core/token_validator.go @@ -117,7 +117,7 @@ func CallContractWithState(call ethereum.CallMsg, chain consensus.ChainContext, vmenv := vm.NewEVM(evmContext, txContext, statedb, nil, chain.Config(), vm.Config{}) gaspool := new(GasPool).AddGas(1000000) owner := common.Address{} - result, err, _ := NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner) + result, err := NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner) if err != nil { return nil, err } diff --git a/eth/api_tracer.go b/eth/api_tracer.go index 35297da7f880..18c0fc010945 100644 --- a/eth/api_tracer.go +++ b/eth/api_tracer.go @@ -526,7 +526,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, vmenv := vm.NewEVM(blockCtx, txContext, statedb, XDCxState, api.config, vm.Config{}) owner := common.Address{} - if _, err, _ := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), owner); err != nil { + if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), owner); err != nil { failed = err break } @@ -750,7 +750,7 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, t statedb.SetTxContext(txctx.TxHash, txctx.TxIndex) owner := common.Address{} - result, err, _ := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), owner) + result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), owner) if err != nil { return nil, fmt.Errorf("tracing failed: %v", err) } diff --git a/eth/tracers/testing/calltrace_test.go b/eth/tracers/testing/calltrace_test.go index 35d9407cd93a..102e6f7416cc 100644 --- a/eth/tracers/testing/calltrace_test.go +++ b/eth/tracers/testing/calltrace_test.go @@ -120,7 +120,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { t.Fatalf("failed to prepare transaction for tracing: %v", err) } st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - if _, err, _ = st.TransitionDb(common.Address{}); err != nil { + if _, err = st.TransitionDb(common.Address{}); err != nil { t.Fatalf("failed to execute transaction: %v", err) } // Retrieve the trace result and compare against the etalon @@ -231,7 +231,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { evm := vm.NewEVM(context, txContext, statedb, nil, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) snap := statedb.Snapshot() st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - if _, err, _ = st.TransitionDb(common.Address{}); err != nil { + if _, err = st.TransitionDb(common.Address{}); err != nil { b.Fatalf("failed to execute transaction: %v", err) } if _, err = tracer.GetResult(); err != nil { diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 4b3198c95e0f..af01acda6741 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -158,7 +158,7 @@ func TestZeroValueToNotExitCall(t *testing.T) { t.Fatalf("failed to prepare transaction for tracing: %v", err) } st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - if _, err, _ = st.TransitionDb(common.Address{}); err != nil { + if _, err = st.TransitionDb(common.Address{}); err != nil { t.Fatalf("failed to execute transaction: %v", err) } // Retrieve the trace result and compare against the etalon @@ -244,7 +244,7 @@ func TestPrestateTracerCreate2(t *testing.T) { t.Fatalf("failed to prepare transaction for tracing: %v", err) } st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - if _, err, _ = st.TransitionDb(common.Address{}); err != nil { + if _, err = st.TransitionDb(common.Address{}); err != nil { t.Fatalf("failed to execute transaction: %v", err) } // Retrieve the trace result and compare against the etalon @@ -346,7 +346,7 @@ func BenchmarkTransactionTrace(b *testing.B) { for i := 0; i < b.N; i++ { snap := statedb.Snapshot() st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - _, err, _ = st.TransitionDb(common.Address{}) + _, err = st.TransitionDb(common.Address{}) if err != nil { b.Fatal(err) } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index f8687bf626a3..44c39bdc6495 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1322,18 +1322,18 @@ func (s *PublicBlockChainAPI) getCandidatesFromSmartContract() ([]utils.Masterno return candidatesWithStakeInfo, nil } -func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error, error) { +func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) { defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now()) statedb, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if statedb == nil || err != nil { - return nil, err, nil + return nil, err } if header == nil { - return nil, errors.New("nil header in DoCall"), nil + return nil, errors.New("nil header in DoCall") } if err := overrides.Apply(statedb); err != nil { - return nil, err, nil + return nil, err } // Setup context so it may be cancelled the call has completed @@ -1350,32 +1350,32 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash block, err := b.BlockByNumberOrHash(ctx, blockNrOrHash) if err != nil { - return nil, err, nil + return nil, err } if block == nil { - return nil, fmt.Errorf("nil block in DoCall: number=%d, hash=%s", header.Number.Uint64(), header.Hash().Hex()), nil + return nil, fmt.Errorf("nil block in DoCall: number=%d, hash=%s", header.Number.Uint64(), header.Hash().Hex()) } author, err := b.GetEngine().Author(block.Header()) if err != nil { - return nil, err, nil + return nil, err } XDCxState, err := b.XDCxService().GetTradingState(block, author) if err != nil { - return nil, err, nil + return nil, err } // TODO: replace header.BaseFee with blockCtx.BaseFee // reference: https://github.com/ethereum/go-ethereum/pull/29051 msg, err := args.ToMessage(b, header.Number, globalGasCap, header.BaseFee) if err != nil { - return nil, err, nil + return nil, err } msg.SetBalanceTokenFeeForCall() // Get a new instance of the EVM. evm, vmError, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &vm.Config{NoBaseFee: true}) if err != nil { - return nil, err, nil + return nil, err } // Wait for the context to be done and cancel the evm. Even if the // EVM has finished, cancelling may be done (repeatedly) @@ -1387,19 +1387,19 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash // Execute the message. gp := new(core.GasPool).AddGas(math.MaxUint64) owner := common.Address{} - result, err, vmErr := core.ApplyMessage(evm, msg, gp, owner) + result, err := core.ApplyMessage(evm, msg, gp, owner) if err := vmError(); err != nil { - return nil, err, nil + return nil, err } // If the timer caused an abort, return an appropriate error message if evm.Cancelled() { - return nil, fmt.Errorf("execution aborted (timeout = %v)", timeout), nil + return nil, fmt.Errorf("execution aborted (timeout = %v)", timeout) } if err != nil { - return result, fmt.Errorf("err: %w (supplied gas %d)", err, msg.Gas()), nil + return result, fmt.Errorf("err: %w (supplied gas %d)", err, msg.Gas()) } - return result, err, vmErr + return result, err } func newRevertError(res []byte) *revertError { @@ -1443,7 +1443,7 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, bl if args.To != nil && *args.To == common.MasternodeVotingSMCBinary { timeout = 0 } - result, err, vmErr := DoCall(ctx, s.b, args, *blockNrOrHash, overrides, timeout, s.b.RPCGasCap()) + result, err := DoCall(ctx, s.b, args, *blockNrOrHash, overrides, timeout, s.b.RPCGasCap()) if err != nil { return nil, err } @@ -1451,7 +1451,7 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, bl if result.Failed() && len(result.Return()) > 0 { return nil, newRevertError(result.Return()) } - return result.Return(), vmErr + return result.Return(), nil } type estimateGasError struct { @@ -1515,7 +1515,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr executable := func(gas uint64) (bool, *core.ExecutionResult, error) { args.Gas = (*hexutil.Uint64)(&gas) - result, err, _ := DoCall(ctx, b, args, blockNrOrHash, nil, 0, gasCap) + result, err := DoCall(ctx, b, args, blockNrOrHash, nil, 0, gasCap) if err != nil { if errors.Is(err, vm.ErrOutOfGas) || errors.Is(err, core.ErrIntrinsicGas) { return true, nil, nil // Special case, raise gas limit @@ -2096,7 +2096,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH return nil, 0, nil, err } // TODO: determine the value of owner - res, err, _ := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), owner) + res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), owner) if err != nil { return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err) } diff --git a/les/odr_test.go b/les/odr_test.go index 031a1df5dc1e..706853d504b2 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -142,7 +142,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxUint64) owner := common.Address{} - result, _, _ := core.ApplyMessage(vmenv, msg, gp, owner) + result, _ := core.ApplyMessage(vmenv, msg, gp, owner) res = append(res, result.Return()...) } } else { @@ -160,7 +160,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai vmenv := vm.NewEVM(context, txContext, statedb, nil, config, vm.Config{NoBaseFee: true}) gp := new(core.GasPool).AddGas(math.MaxUint64) owner := common.Address{} - result, _, _ := core.ApplyMessage(vmenv, msg, gp, owner) + result, _ := core.ApplyMessage(vmenv, msg, gp, owner) if statedb.Error() == nil { res = append(res, result.Return()...) } diff --git a/light/odr_test.go b/light/odr_test.go index 1562f955d9fe..79d3241c2048 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -191,7 +191,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain vmenv := vm.NewEVM(context, txContext, st, nil, config, vm.Config{NoBaseFee: true}) gp := new(core.GasPool).AddGas(math.MaxUint64) owner := common.Address{} - result, _, _ := core.ApplyMessage(vmenv, msg, gp, owner) + result, _ := core.ApplyMessage(vmenv, msg, gp, owner) res = append(res, result.Return()...) if st.Error() != nil { return res, st.Error() diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 1452ecd5d6a0..6f9ace8ec3ef 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -166,7 +166,7 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD gaspool.AddGas(block.GasLimit()) coinbase := &t.json.Env.Coinbase - if _, err, _ := core.ApplyMessage(evm, msg, gaspool, *coinbase); err != nil { + if _, err := core.ApplyMessage(evm, msg, gaspool, *coinbase); err != nil { statedb.RevertToSnapshot(snapshot) } if logs := rlpHash(statedb.Logs()); logs != common.Hash(post.Logs) { From 517f4a16a6faf3aa4d199b64cabcdeb37aaa1d85 Mon Sep 17 00:00:00 2001 From: JukLee0ira Date: Mon, 23 Dec 2024 17:55:41 +0800 Subject: [PATCH 389/479] internal/ethapi: fix Call handle revert error --- internal/ethapi/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 44c39bdc6495..47d50b864f3d 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1448,7 +1448,7 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, bl return nil, err } // If the result contains a revert reason, try to unpack and return it. - if result.Failed() && len(result.Return()) > 0 { + if result.Failed() { return nil, newRevertError(result.Return()) } return result.Return(), nil From efa2605f26efe84bd307be0704e6b9484af1ffc4 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Sat, 21 Dec 2024 15:56:24 +0800 Subject: [PATCH 390/479] accounts/abi/bind/backends: fix wrong gas returned by EstimateGas --- accounts/abi/bind/backends/simulated.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index bbc8f4373ffd..d0c7562e155c 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -368,7 +368,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call XDPoSChain.Call b.pendingState.RevertToSnapshot(snapshot) if err != nil { - if err == core.ErrIntrinsicGas { + if errors.Is(err, core.ErrIntrinsicGas) { return true, nil, nil // Special case, raise gas limit } return true, nil, err // Bail out From 240757a1cea7dc70d0eeb4ed8f8e28c13fc373be Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 25 Dec 2024 09:16:01 +0800 Subject: [PATCH 391/479] all: remove empty function SaveData() --- XDCx/XDCx.go | 2 -- XDCxlending/XDCxlending.go | 3 --- core/blockchain.go | 9 ++------- eth/backend.go | 4 ---- ethstats/ethstats.go | 2 -- les/backend.go | 4 ---- light/lightchain.go | 3 --- node/node.go | 3 --- node/node_example_test.go | 1 - node/service.go | 3 +-- node/utils_test.go | 4 +--- p2p/simulations/adapters/exec.go | 2 -- p2p/simulations/examples/ping-pong.go | 3 --- p2p/simulations/http_test.go | 2 -- p2p/testing/protocoltester.go | 6 ------ 15 files changed, 4 insertions(+), 47 deletions(-) diff --git a/XDCx/XDCx.go b/XDCx/XDCx.go index 3fbaf0c783e2..fbb737240289 100644 --- a/XDCx/XDCx.go +++ b/XDCx/XDCx.go @@ -71,8 +71,6 @@ func (XDCx *XDCX) Start(server *p2p.Server) error { return nil } -func (XDCx *XDCX) SaveData() { -} func (XDCx *XDCX) Stop() error { return nil } diff --git a/XDCxlending/XDCxlending.go b/XDCxlending/XDCxlending.go index 1bf2496ff237..a1f8e9749fd1 100644 --- a/XDCxlending/XDCxlending.go +++ b/XDCxlending/XDCxlending.go @@ -54,9 +54,6 @@ func (l *Lending) Start(server *p2p.Server) error { return nil } -func (l *Lending) SaveData() { -} - func (l *Lending) Stop() error { return nil } diff --git a/core/blockchain.go b/core/blockchain.go index 217e3be9e00a..48509e652057 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -922,12 +922,7 @@ func (bc *BlockChain) TrieNode(hash common.Hash) ([]byte, error) { return bc.stateCache.TrieDB().Node(hash) } -func (bc *BlockChain) SaveData() { - bc.wg.Add(1) - defer bc.wg.Done() - // Make sure no inconsistent state is leaked during insertion - bc.mu.Lock() - defer bc.mu.Unlock() +func (bc *BlockChain) saveData() { // Ensure the state of a recent block is also stored to disk before exiting. // We're writing three different states to catch different restart scenarios: // - HEAD: So we don't need to reprocess any blocks in the general case @@ -1011,7 +1006,7 @@ func (bc *BlockChain) Stop() { close(bc.quit) atomic.StoreInt32(&bc.procInterrupt, 1) bc.wg.Wait() - bc.SaveData() + bc.saveData() log.Info("Blockchain manager stopped") } diff --git a/eth/backend.go b/eth/backend.go index b9cef04b11c8..6a3920dcc277 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -557,10 +557,6 @@ func (e *Ethereum) Start(srvr *p2p.Server) error { return nil } -func (e *Ethereum) SaveData() { - e.blockchain.SaveData() -} - // Stop implements node.Service, terminating all internal goroutines used by the // Ethereum protocol. func (e *Ethereum) Stop() error { diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go index 4510478bb0e5..edcb255fbc03 100644 --- a/ethstats/ethstats.go +++ b/ethstats/ethstats.go @@ -179,8 +179,6 @@ func (s *Service) Start(server *p2p.Server) error { log.Info("Stats daemon started") return nil } -func (s *Service) SaveData() { -} // Stop implements node.Service, terminating the monitoring and reporting daemon. func (s *Service) Stop() error { diff --git a/les/backend.go b/les/backend.go index 08933f93a0a4..e982603d2fdb 100644 --- a/les/backend.go +++ b/les/backend.go @@ -228,10 +228,6 @@ func (s *LightEthereum) Start(srvr *p2p.Server) error { return nil } -func (s *LightEthereum) SaveData() { - s.blockchain.SaveData() -} - // Stop implements node.Service, terminating all internal goroutines used by the // Ethereum protocol. func (s *LightEthereum) Stop() error { diff --git a/light/lightchain.go b/light/lightchain.go index 26b5df09172b..9cbf95ddf9c6 100644 --- a/light/lightchain.go +++ b/light/lightchain.go @@ -281,9 +281,6 @@ func (lc *LightChain) GetBlockByNumber(ctx context.Context, number uint64) (*typ return lc.GetBlock(ctx, hash, number) } -func (bc *LightChain) SaveData() { -} - // Stop stops the blockchain service. If any imports are currently in progress // it will abort them using the procInterrupt. func (lc *LightChain) Stop() { diff --git a/node/node.go b/node/node.go index 40cef4bab342..b8deaa1e857c 100644 --- a/node/node.go +++ b/node/node.go @@ -478,9 +478,6 @@ func (n *Node) Stop() error { return ErrNodeStopped } - for _, service := range n.services { - service.SaveData() - } // Terminate the API, services and the p2p server. n.stopWS() n.stopHTTP() diff --git a/node/node_example_test.go b/node/node_example_test.go index b2ad1a8ed3e0..c16aad73ab27 100644 --- a/node/node_example_test.go +++ b/node/node_example_test.go @@ -38,7 +38,6 @@ type SampleService struct{} func (s *SampleService) Protocols() []p2p.Protocol { return nil } func (s *SampleService) APIs() []rpc.API { return nil } func (s *SampleService) Start(*p2p.Server) error { fmt.Println("Service starting..."); return nil } -func (s *SampleService) SaveData() {} func (s *SampleService) Stop() error { fmt.Println("Service stopping..."); return nil } func ExampleService() { diff --git a/node/service.go b/node/service.go index 5d0358b93123..8bfc71884f50 100644 --- a/node/service.go +++ b/node/service.go @@ -97,8 +97,7 @@ type Service interface { // Start is called after all services have been constructed and the networking // layer was also initialized to spawn any goroutines required by the service. Start(server *p2p.Server) error - //Save data before stop - SaveData() + // Stop terminates all goroutines belonging to the service, blocking until they // are all terminated. Stop() error diff --git a/node/utils_test.go b/node/utils_test.go index d6aff608e05d..4f6034f47eca 100644 --- a/node/utils_test.go +++ b/node/utils_test.go @@ -32,7 +32,6 @@ type NoopService struct{} func (s *NoopService) Protocols() []p2p.Protocol { return nil } func (s *NoopService) APIs() []rpc.API { return nil } func (s *NoopService) Start(*p2p.Server) error { return nil } -func (s *NoopService) SaveData() {} func (s *NoopService) Stop() error { return nil } func NewNoopService(*ServiceContext) (Service, error) { return new(NoopService), nil } @@ -79,8 +78,7 @@ func (s *InstrumentedService) Start(server *p2p.Server) error { } return s.start } -func (s *InstrumentedService) SaveData() { -} + func (s *InstrumentedService) Stop() error { if s.stopHook != nil { s.stopHook() diff --git a/p2p/simulations/adapters/exec.go b/p2p/simulations/adapters/exec.go index 1ab534f8d07f..153aa32d6491 100644 --- a/p2p/simulations/adapters/exec.go +++ b/p2p/simulations/adapters/exec.go @@ -504,8 +504,6 @@ func (s *snapshotService) Start(*p2p.Server) error { return nil } -func (s *snapshotService) SaveData() { -} func (s *snapshotService) Stop() error { return nil } diff --git a/p2p/simulations/examples/ping-pong.go b/p2p/simulations/examples/ping-pong.go index 450ab047906e..144035348b69 100644 --- a/p2p/simulations/examples/ping-pong.go +++ b/p2p/simulations/examples/ping-pong.go @@ -127,9 +127,6 @@ func (p *pingPongService) Start(server *p2p.Server) error { return nil } -func (p *pingPongService) SaveData() { -} - func (p *pingPongService) Stop() error { p.log.Info("ping-pong service stopping") return nil diff --git a/p2p/simulations/http_test.go b/p2p/simulations/http_test.go index 2bc2e0fecc78..7121cc4c0d8c 100644 --- a/p2p/simulations/http_test.go +++ b/p2p/simulations/http_test.go @@ -117,8 +117,6 @@ func (t *testService) Start(server *p2p.Server) error { return nil } -func (t *testService) SaveData() { -} func (t *testService) Stop() error { return nil } diff --git a/p2p/testing/protocoltester.go b/p2p/testing/protocoltester.go index 622d63bfe324..df291202a0ff 100644 --- a/p2p/testing/protocoltester.go +++ b/p2p/testing/protocoltester.go @@ -145,9 +145,6 @@ func (tn *testNode) Start(server *p2p.Server) error { return nil } -func (tn *testNode) SaveData() { -} - func (tn *testNode) Stop() error { return nil } @@ -201,9 +198,6 @@ func (mn *mockNode) Expect(exp ...Expect) error { return <-mn.err } -func (mn *mockNode) SaveData() { -} - func (mn *mockNode) Stop() error { mn.stopOnce.Do(func() { close(mn.stop) }) return nil From 89c51c5e6911d814b7370b10a2c6a1cb7d79e560 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 18 Dec 2024 17:50:26 +0800 Subject: [PATCH 392/479] common: improve documentation comments (#16701) --- XDCx/order_processor.go | 2 +- XDCx/tradingstate/dump.go | 22 +++---- XDCx/tradingstate/state_lendingbook.go | 2 +- XDCx/tradingstate/state_orderbook.go | 8 +-- XDCx/tradingstate/statedb.go | 4 +- XDCxlending/lendingstate/dump.go | 22 +++---- XDCxlending/lendingstate/state_lendingbook.go | 10 +-- .../lendingstate/state_liquidationtime.go | 2 +- XDCxlending/lendingstate/statedb.go | 4 +- XDCxlending/order_processor.go | 2 +- common/bytes.go | 23 +++++-- common/math/big.go | 13 ++-- common/number/int.go | 47 +++++++------- common/path.go | 6 +- common/types.go | 47 ++++++-------- common/types_template.go | 64 ------------------- core/blockchain.go | 4 +- core/state/state_test.go | 2 +- core/state/trc21_reader.go | 10 +-- core/txpool/order_pool.go | 2 +- eth/downloader/downloader.go | 2 +- 21 files changed, 118 insertions(+), 180 deletions(-) delete mode 100644 common/types_template.go diff --git a/XDCx/order_processor.go b/XDCx/order_processor.go index a950ca43b788..441bb2ec8b60 100644 --- a/XDCx/order_processor.go +++ b/XDCx/order_processor.go @@ -525,7 +525,7 @@ func DoSettleBalance(coinbase common.Address, takerOrder, makerOrder *tradingsta matchingFee = new(big.Int).Add(matchingFee, common.RelayerFee) matchingFee = new(big.Int).Add(matchingFee, common.RelayerFee) - if common.EmptyHash(takerExOwner.Hash()) || common.EmptyHash(makerExOwner.Hash()) { + if takerExOwner.Hash().IsZero() || makerExOwner.Hash().IsZero() { return fmt.Errorf("empty echange owner: taker: %v , maker : %v", takerExOwner, makerExOwner) } mapBalances := map[common.Address]map[common.Address]*big.Int{} diff --git a/XDCx/tradingstate/dump.go b/XDCx/tradingstate/dump.go index 58026ed2a158..f813743870d1 100644 --- a/XDCx/tradingstate/dump.go +++ b/XDCx/tradingstate/dump.go @@ -56,7 +56,7 @@ func (t *TradingStateDB) DumpAskTrie(orderBook common.Hash) (map[*big.Int]DumpOr it := trie.NewIterator(exhangeObject.getAsksTrie(t.db).NodeIterator(nil)) for it.Next() { priceHash := common.BytesToHash(it.Key) - if common.EmptyHash(priceHash) { + if priceHash.IsZero() { continue } price := new(big.Int).SetBytes(priceHash.Bytes()) @@ -99,7 +99,7 @@ func (t *TradingStateDB) DumpBidTrie(orderBook common.Hash) (map[*big.Int]DumpOr it := trie.NewIterator(exhangeObject.getBidsTrie(t.db).NodeIterator(nil)) for it.Next() { priceHash := common.BytesToHash(it.Key) - if common.EmptyHash(priceHash) { + if priceHash.IsZero() { continue } price := new(big.Int).SetBytes(priceHash.Bytes()) @@ -142,7 +142,7 @@ func (t *TradingStateDB) GetBids(orderBook common.Hash) (map[*big.Int]*big.Int, it := trie.NewIterator(exhangeObject.getBidsTrie(t.db).NodeIterator(nil)) for it.Next() { priceHash := common.BytesToHash(it.Key) - if common.EmptyHash(priceHash) { + if priceHash.IsZero() { continue } price := new(big.Int).SetBytes(priceHash.Bytes()) @@ -185,7 +185,7 @@ func (t *TradingStateDB) GetAsks(orderBook common.Hash) (map[*big.Int]*big.Int, it := trie.NewIterator(exhangeObject.getAsksTrie(t.db).NodeIterator(nil)) for it.Next() { priceHash := common.BytesToHash(it.Key) - if common.EmptyHash(priceHash) { + if priceHash.IsZero() { continue } price := new(big.Int).SetBytes(priceHash.Bytes()) @@ -224,7 +224,7 @@ func (s *stateOrderList) DumpOrderList(db Database) DumpOrderList { orderListIt := trie.NewIterator(s.getTrie(db).NodeIterator(nil)) for orderListIt.Next() { keyHash := common.BytesToHash(orderListIt.Key) - if common.EmptyHash(keyHash) { + if keyHash.IsZero() { continue } if _, exist := s.cachedStorage[keyHash]; exist { @@ -235,7 +235,7 @@ func (s *stateOrderList) DumpOrderList(db Database) DumpOrderList { } } for key, value := range s.cachedStorage { - if !common.EmptyHash(value) { + if !value.IsZero() { mapResult.Orders[new(big.Int).SetBytes(key.Bytes())] = new(big.Int).SetBytes(value.Bytes()) } } @@ -277,7 +277,7 @@ func (s *stateLendingBook) DumpOrderList(db Database) DumpOrderList { orderListIt := trie.NewIterator(s.getTrie(db).NodeIterator(nil)) for orderListIt.Next() { keyHash := common.BytesToHash(orderListIt.Key) - if common.EmptyHash(keyHash) { + if keyHash.IsZero() { continue } if _, exist := s.cachedStorage[keyHash]; exist { @@ -288,7 +288,7 @@ func (s *stateLendingBook) DumpOrderList(db Database) DumpOrderList { } } for key, value := range s.cachedStorage { - if !common.EmptyHash(value) { + if !value.IsZero() { mapResult.Orders[new(big.Int).SetBytes(key.Bytes())] = new(big.Int).SetBytes(value.Bytes()) } } @@ -311,7 +311,7 @@ func (l *liquidationPriceState) DumpLendingBook(db Database) (DumpLendingBook, e it := trie.NewIterator(l.getTrie(db).NodeIterator(nil)) for it.Next() { lendingBook := common.BytesToHash(it.Key) - if common.EmptyHash(lendingBook) { + if lendingBook.IsZero() { continue } if _, exist := l.stateLendingBooks[lendingBook]; exist { @@ -326,7 +326,7 @@ func (l *liquidationPriceState) DumpLendingBook(db Database) (DumpLendingBook, e } } for lendingBook, stateLendingBook := range l.stateLendingBooks { - if !common.EmptyHash(lendingBook) { + if !lendingBook.IsZero() { result.LendingBooks[lendingBook] = stateLendingBook.DumpOrderList(db) } } @@ -342,7 +342,7 @@ func (t *TradingStateDB) DumpLiquidationPriceTrie(orderBook common.Hash) (map[*b it := trie.NewIterator(exhangeObject.getLiquidationPriceTrie(t.db).NodeIterator(nil)) for it.Next() { priceHash := common.BytesToHash(it.Key) - if common.EmptyHash(priceHash) { + if priceHash.IsZero() { continue } price := new(big.Int).SetBytes(priceHash.Bytes()) diff --git a/XDCx/tradingstate/state_lendingbook.go b/XDCx/tradingstate/state_lendingbook.go index 6f8b77695bec..f30faf60a951 100644 --- a/XDCx/tradingstate/state_lendingbook.go +++ b/XDCx/tradingstate/state_lendingbook.go @@ -118,7 +118,7 @@ func (s *stateLendingBook) getAllTradeIds(db Database) []common.Hash { return tradeIds } for id, value := range s.cachedStorage { - if !common.EmptyHash(value) { + if !value.IsZero() { tradeIds = append(tradeIds, id) } } diff --git a/XDCx/tradingstate/state_orderbook.go b/XDCx/tradingstate/state_orderbook.go index 8d74d748e404..39a4db730e5e 100644 --- a/XDCx/tradingstate/state_orderbook.go +++ b/XDCx/tradingstate/state_orderbook.go @@ -85,16 +85,16 @@ func (te *tradingExchanges) empty() bool { if te.data.TotalQuantity != nil && te.data.TotalQuantity.Sign() > 0 { return false } - if !common.EmptyHash(te.data.AskRoot) { + if !te.data.AskRoot.IsZero() { return false } - if !common.EmptyHash(te.data.BidRoot) { + if !te.data.BidRoot.IsZero() { return false } - if !common.EmptyHash(te.data.OrderRoot) { + if !te.data.OrderRoot.IsZero() { return false } - if !common.EmptyHash(te.data.LiquidationPriceRoot) { + if !te.data.LiquidationPriceRoot.IsZero() { return false } return true diff --git a/XDCx/tradingstate/statedb.go b/XDCx/tradingstate/statedb.go index 036b5cac4cbf..7520947db15c 100644 --- a/XDCx/tradingstate/statedb.go +++ b/XDCx/tradingstate/statedb.go @@ -358,7 +358,7 @@ func (t *TradingStateDB) GetBestAskPrice(orderBook common.Hash) (*big.Int, *big. stateObject := t.getStateExchangeObject(orderBook) if stateObject != nil { priceHash := stateObject.getBestPriceAsksTrie(t.db) - if common.EmptyHash(priceHash) { + if priceHash.IsZero() { return Zero, Zero } orderList := stateObject.getStateOrderListAskObject(t.db, priceHash) @@ -375,7 +375,7 @@ func (t *TradingStateDB) GetBestBidPrice(orderBook common.Hash) (*big.Int, *big. stateObject := t.getStateExchangeObject(orderBook) if stateObject != nil { priceHash := stateObject.getBestBidsTrie(t.db) - if common.EmptyHash(priceHash) { + if priceHash.IsZero() { return Zero, Zero } orderList := stateObject.getStateBidOrderListObject(t.db, priceHash) diff --git a/XDCxlending/lendingstate/dump.go b/XDCxlending/lendingstate/dump.go index f3fe43742e48..978f67fc8e71 100644 --- a/XDCxlending/lendingstate/dump.go +++ b/XDCxlending/lendingstate/dump.go @@ -48,7 +48,7 @@ func (ls *LendingStateDB) DumpInvestingTrie(orderBook common.Hash) (map[*big.Int it := trie.NewIterator(exhangeObject.getInvestingTrie(ls.db).NodeIterator(nil)) for it.Next() { interestHash := common.BytesToHash(it.Key) - if common.EmptyHash(interestHash) { + if interestHash.IsZero() { continue } interest := new(big.Int).SetBytes(interestHash.Bytes()) @@ -91,7 +91,7 @@ func (ls *LendingStateDB) DumpBorrowingTrie(orderBook common.Hash) (map[*big.Int it := trie.NewIterator(exhangeObject.getBorrowingTrie(ls.db).NodeIterator(nil)) for it.Next() { interestHash := common.BytesToHash(it.Key) - if common.EmptyHash(interestHash) { + if interestHash.IsZero() { continue } interest := new(big.Int).SetBytes(interestHash.Bytes()) @@ -134,7 +134,7 @@ func (ls *LendingStateDB) GetInvestings(orderBook common.Hash) (map[*big.Int]*bi it := trie.NewIterator(exhangeObject.getInvestingTrie(ls.db).NodeIterator(nil)) for it.Next() { interestHash := common.BytesToHash(it.Key) - if common.EmptyHash(interestHash) { + if interestHash.IsZero() { continue } interest := new(big.Int).SetBytes(interestHash.Bytes()) @@ -177,7 +177,7 @@ func (ls *LendingStateDB) GetBorrowings(orderBook common.Hash) (map[*big.Int]*bi it := trie.NewIterator(exhangeObject.getBorrowingTrie(ls.db).NodeIterator(nil)) for it.Next() { interestHash := common.BytesToHash(it.Key) - if common.EmptyHash(interestHash) { + if interestHash.IsZero() { continue } interest := new(big.Int).SetBytes(interestHash.Bytes()) @@ -216,7 +216,7 @@ func (il *itemListState) DumpItemList(db Database) DumpOrderList { orderListIt := trie.NewIterator(il.getTrie(db).NodeIterator(nil)) for orderListIt.Next() { keyHash := common.BytesToHash(orderListIt.Key) - if common.EmptyHash(keyHash) { + if keyHash.IsZero() { continue } if _, exist := il.cachedStorage[keyHash]; exist { @@ -227,7 +227,7 @@ func (il *itemListState) DumpItemList(db Database) DumpOrderList { } } for key, value := range il.cachedStorage { - if !common.EmptyHash(value) { + if !value.IsZero() { mapResult.Orders[new(big.Int).SetBytes(key.Bytes())] = new(big.Int).SetBytes(value.Bytes()) } } @@ -265,7 +265,7 @@ func (lts *liquidationTimeState) DumpItemList(db Database) DumpOrderList { orderListIt := trie.NewIterator(lts.getTrie(db).NodeIterator(nil)) for orderListIt.Next() { keyHash := common.BytesToHash(orderListIt.Key) - if common.EmptyHash(keyHash) { + if keyHash.IsZero() { continue } if _, exist := lts.cachedStorage[keyHash]; exist { @@ -276,7 +276,7 @@ func (lts *liquidationTimeState) DumpItemList(db Database) DumpOrderList { } } for key, value := range lts.cachedStorage { - if !common.EmptyHash(value) { + if !value.IsZero() { mapResult.Orders[new(big.Int).SetBytes(key.Bytes())] = new(big.Int).SetBytes(value.Bytes()) } } @@ -303,7 +303,7 @@ func (ls *LendingStateDB) DumpLiquidationTimeTrie(orderBook common.Hash) (map[*b it := trie.NewIterator(exhangeObject.getLiquidationTimeTrie(ls.db).NodeIterator(nil)) for it.Next() { unixTimeHash := common.BytesToHash(it.Key) - if common.EmptyHash(unixTimeHash) { + if unixTimeHash.IsZero() { continue } unixTime := new(big.Int).SetBytes(unixTimeHash.Bytes()) @@ -346,7 +346,7 @@ func (ls *LendingStateDB) DumpLendingOrderTrie(orderBook common.Hash) (map[*big. it := trie.NewIterator(exhangeObject.getLendingItemTrie(ls.db).NodeIterator(nil)) for it.Next() { orderIdHash := common.BytesToHash(it.Key) - if common.EmptyHash(orderIdHash) { + if orderIdHash.IsZero() { continue } orderId := new(big.Int).SetBytes(orderIdHash.Bytes()) @@ -386,7 +386,7 @@ func (ls *LendingStateDB) DumpLendingTradeTrie(orderBook common.Hash) (map[*big. it := trie.NewIterator(exhangeObject.getLendingTradeTrie(ls.db).NodeIterator(nil)) for it.Next() { tradeIdHash := common.BytesToHash(it.Key) - if common.EmptyHash(tradeIdHash) { + if tradeIdHash.IsZero() { continue } tradeId := new(big.Int).SetBytes(tradeIdHash.Bytes()) diff --git a/XDCxlending/lendingstate/state_lendingbook.go b/XDCxlending/lendingstate/state_lendingbook.go index 1cc3fda0778a..c6103790e274 100644 --- a/XDCxlending/lendingstate/state_lendingbook.go +++ b/XDCxlending/lendingstate/state_lendingbook.go @@ -70,19 +70,19 @@ func (s *lendingExchangeState) empty() bool { if s.data.TradeNonce != 0 { return false } - if !common.EmptyHash(s.data.InvestingRoot) { + if !s.data.InvestingRoot.IsZero() { return false } - if !common.EmptyHash(s.data.BorrowingRoot) { + if !s.data.BorrowingRoot.IsZero() { return false } - if !common.EmptyHash(s.data.LendingItemRoot) { + if !s.data.LendingItemRoot.IsZero() { return false } - if !common.EmptyHash(s.data.LendingTradeRoot) { + if !s.data.LendingTradeRoot.IsZero() { return false } - if !common.EmptyHash(s.data.LiquidationTimeRoot) { + if !s.data.LiquidationTimeRoot.IsZero() { return false } return true diff --git a/XDCxlending/lendingstate/state_liquidationtime.go b/XDCxlending/lendingstate/state_liquidationtime.go index 558650989a97..77ab934abfbb 100644 --- a/XDCxlending/lendingstate/state_liquidationtime.go +++ b/XDCxlending/lendingstate/state_liquidationtime.go @@ -116,7 +116,7 @@ func (lt *liquidationTimeState) getAllTradeIds(db Database) []common.Hash { return tradeIds } for id, value := range lt.cachedStorage { - if !common.EmptyHash(value) { + if !value.IsZero() { tradeIds = append(tradeIds, id) } } diff --git a/XDCxlending/lendingstate/statedb.go b/XDCxlending/lendingstate/statedb.go index e5a5cbf6ede6..a1b3899803d3 100644 --- a/XDCxlending/lendingstate/statedb.go +++ b/XDCxlending/lendingstate/statedb.go @@ -343,7 +343,7 @@ func (ls *LendingStateDB) GetBestInvestingRate(orderBook common.Hash) (*big.Int, stateObject := ls.getLendingExchange(orderBook) if stateObject != nil { investingHash := stateObject.getBestInvestingInterest(ls.db) - if common.EmptyHash(investingHash) { + if investingHash.IsZero() { return Zero, Zero } orderList := stateObject.getInvestingOrderList(ls.db, investingHash) @@ -360,7 +360,7 @@ func (ls *LendingStateDB) GetBestBorrowRate(orderBook common.Hash) (*big.Int, *b stateObject := ls.getLendingExchange(orderBook) if stateObject != nil { priceHash := stateObject.getBestBorrowingInterest(ls.db) - if common.EmptyHash(priceHash) { + if priceHash.IsZero() { return Zero, Zero } orderList := stateObject.getBorrowingOrderList(ls.db, priceHash) diff --git a/XDCxlending/order_processor.go b/XDCxlending/order_processor.go index 0a41347acdb8..a46b9dafedca 100644 --- a/XDCxlending/order_processor.go +++ b/XDCxlending/order_processor.go @@ -581,7 +581,7 @@ func DoSettleBalance(coinbase common.Address, takerOrder, makerOrder *lendingsta // masternodes only charge borrower relayer fee matchingFee = new(big.Int).Add(matchingFee, common.RelayerLendingFee) - if common.EmptyHash(takerExOwner.Hash()) || common.EmptyHash(makerExOwner.Hash()) { + if takerExOwner.Hash().IsZero() || makerExOwner.Hash().IsZero() { return fmt.Errorf("empty echange owner: taker: %v , maker : %v", takerExOwner, makerExOwner) } mapBalances := map[common.Address]map[common.Address]*big.Int{} diff --git a/common/bytes.go b/common/bytes.go index 79caa1f49ce7..6be6116bb979 100644 --- a/common/bytes.go +++ b/common/bytes.go @@ -19,15 +19,20 @@ package common import "encoding/hex" +// ToHex returns the hex representation of b, prefixed with '0x'. +// For empty slices, the return value is "0x0". +// +// Deprecated: use hexutil.Encode instead. func ToHex(b []byte) string { hex := Bytes2Hex(b) - // Prefer output of "0x0" instead of "0x" if len(hex) == 0 { hex = "0" } return "0x" + hex } +// FromHex returns the bytes represented by the hexadecimal string s. +// s may be prefixed with "0x". func FromHex(s string) []byte { if len(s) > 1 { if s[0:2] == "0x" || s[0:2] == "0X" { @@ -43,9 +48,7 @@ func FromHex(s string) []byte { return Hex2Bytes(s) } -// Copy bytes -// -// Returns an exact copy of the provided bytes +// CopyBytes returns an exact copy of the provided bytes. func CopyBytes(b []byte) (copiedBytes []byte) { if b == nil { return nil @@ -55,17 +58,23 @@ func CopyBytes(b []byte) (copiedBytes []byte) { return } + +// hasXDCPrefix validates str begins with 'xdc' or 'XDC'. func hasXDCPrefix(str string) bool { return len(str) >= 3 && (str[0] == 'x' || str[0] == 'X') && (str[1] == 'd' || str[1] == 'D') && (str[2] == 'c' || str[2] == 'C') } + +// hasHexPrefix validates str begins with '0x' or '0X'. func hasHexPrefix(str string) bool { return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X') } +// isHexCharacter returns bool of c being a valid hexadecimal. func isHexCharacter(c byte) bool { return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F') } +// isHex validates whether each byte is valid hexadecimal string. func isHex(str string) bool { if len(str)%2 != 0 { return false @@ -78,16 +87,18 @@ func isHex(str string) bool { return true } +// Bytes2Hex returns the hexadecimal encoding of d. func Bytes2Hex(d []byte) string { return hex.EncodeToString(d) } +// Hex2Bytes returns the bytes represented by the hexadecimal string str. func Hex2Bytes(str string) []byte { h, _ := hex.DecodeString(str) - return h } +// Hex2BytesFixed returns bytes of a specified fixed length flen. func Hex2BytesFixed(str string, flen int) []byte { h, _ := hex.DecodeString(str) if len(h) == flen { @@ -103,6 +114,7 @@ func Hex2BytesFixed(str string, flen int) []byte { } } +// RightPadBytes zero-pads slice to the right up to length l. func RightPadBytes(slice []byte, l int) []byte { if l <= len(slice) { return slice @@ -114,6 +126,7 @@ func RightPadBytes(slice []byte, l int) []byte { return padded } +// LeftPadBytes zero-pads slice to the left up to length l. func LeftPadBytes(slice []byte, l int) []byte { if l <= len(slice) { return slice diff --git a/common/math/big.go b/common/math/big.go index 78727865032a..d2cfb6ed5a9b 100644 --- a/common/math/big.go +++ b/common/math/big.go @@ -78,7 +78,7 @@ func ParseBig256(s string) (*big.Int, bool) { return bigint, ok } -// MustParseBig parses s as a 256 bit big integer and panics if the string is invalid. +// MustParseBig256 parses s as a 256 bit big integer and panics if the string is invalid. func MustParseBig256(s string) *big.Int { v, ok := ParseBig256(s) if !ok { @@ -179,16 +179,15 @@ func U256(x *big.Int) *big.Int { // S256 interprets x as a two's complement number. // x must not exceed 256 bits (the result is undefined if it does) and is not modified. // -// S256(0) = 0 -// S256(1) = 1 -// S256(2**255) = -2**255 -// S256(2**256-1) = -1 +// S256(0) = 0 +// S256(1) = 1 +// S256(2**255) = -2**255 +// S256(2**256-1) = -1 func S256(x *big.Int) *big.Int { if x.Cmp(tt255) < 0 { return x - } else { - return new(big.Int).Sub(x, tt256) } + return new(big.Int).Sub(x, tt256) } // Exp implements exponentiation by squaring. diff --git a/common/number/int.go b/common/number/int.go index 7a4f9f8ca14e..de55c6be878f 100644 --- a/common/number/int.go +++ b/common/number/int.go @@ -34,13 +34,12 @@ func limitUnsigned256(x *Number) *Number { func limitSigned256(x *Number) *Number { if x.num.Cmp(tt255) < 0 { return x - } else { - x.num.Sub(x.num, tt256) - return x } + x.num.Sub(x.num, tt256) + return x } -// Number function +// Initialiser is a Number function type Initialiser func(n int64) *Number // A Number represents a generic integer with a bounding function limiter. Limit is called after each operations @@ -58,58 +57,58 @@ func NewInitialiser(limiter func(*Number) *Number) Initialiser { } } -// Return a Number with a UNSIGNED limiter up to 256 bits +// Uint256 returns a Number with a UNSIGNED limiter up to 256 bits func Uint256(n int64) *Number { return &Number{big.NewInt(n), limitUnsigned256} } -// Return a Number with a SIGNED limiter up to 256 bits +// Int256 returns Number with a SIGNED limiter up to 256 bits func Int256(n int64) *Number { return &Number{big.NewInt(n), limitSigned256} } -// Returns a Number with a SIGNED unlimited size +// Big returns a Number with a SIGNED unlimited size func Big(n int64) *Number { return &Number{big.NewInt(n), func(x *Number) *Number { return x }} } -// Sets i to sum of x+y +// Add sets i to sum of x+y func (i *Number) Add(x, y *Number) *Number { i.num.Add(x.num, y.num) return i.limit(i) } -// Sets i to difference of x-y +// Sub sets i to difference of x-y func (i *Number) Sub(x, y *Number) *Number { i.num.Sub(x.num, y.num) return i.limit(i) } -// Sets i to product of x*y +// Mul sets i to product of x*y func (i *Number) Mul(x, y *Number) *Number { i.num.Mul(x.num, y.num) return i.limit(i) } -// Sets i to the quotient prodject of x/y +// Div sets i to the quotient prodject of x/y func (i *Number) Div(x, y *Number) *Number { i.num.Div(x.num, y.num) return i.limit(i) } -// Sets i to x % y +// Mod sets i to x % y func (i *Number) Mod(x, y *Number) *Number { i.num.Mod(x.num, y.num) return i.limit(i) } -// Sets i to x << s +// Lsh sets i to x << s func (i *Number) Lsh(x *Number, s uint) *Number { i.num.Lsh(x.num, s) return i.limit(i) } -// Sets i to x^y +// Pow sets i to x^y func (i *Number) Pow(x, y *Number) *Number { i.num.Exp(x.num, y.num, big.NewInt(0)) return i.limit(i) @@ -117,13 +116,13 @@ func (i *Number) Pow(x, y *Number) *Number { // Setters -// Set x to i +// Set sets x to i func (i *Number) Set(x *Number) *Number { i.num.Set(x.num) return i.limit(i) } -// Set x bytes to i +// SetBytes sets x bytes to i func (i *Number) SetBytes(x []byte) *Number { i.num.SetBytes(x) return i.limit(i) @@ -131,21 +130,21 @@ func (i *Number) SetBytes(x []byte) *Number { // Cmp compares x and y and returns: // -// -1 if x < y -// 0 if x == y -// +1 if x > y +// -1 if x < y +// 0 if x == y +// +1 if x > y func (i *Number) Cmp(x *Number) int { return i.num.Cmp(x.num) } // Getters -// Returns the string representation of i +// String returns the string representation of i func (i *Number) String() string { return i.num.String() } -// Returns the byte representation of i +// Bytes returns the byte representation of i func (i *Number) Bytes() []byte { return i.num.Bytes() } @@ -160,17 +159,17 @@ func (i *Number) Int64() int64 { return i.num.Int64() } -// Returns the signed version of i +// Int256 returns the signed version of i func (i *Number) Int256() *Number { return Int(0).Set(i) } -// Returns the unsigned version of i +// Uint256 returns the unsigned version of i func (i *Number) Uint256() *Number { return Uint(0).Set(i) } -// Returns the index of the first bit that's set to 1 +// FirstBitSet returns the index of the first bit that's set to 1 func (i *Number) FirstBitSet() int { for j := 0; j < i.num.BitLen(); j++ { if i.num.Bit(j) > 0 { diff --git a/common/path.go b/common/path.go index bd8da86e749e..69820cfe5dec 100644 --- a/common/path.go +++ b/common/path.go @@ -30,6 +30,7 @@ func MakeName(name, version string) string { return fmt.Sprintf("%s/v%s/%s/%s", name, version, runtime.GOOS, runtime.Version()) } +// FileExist checks if a file exists at filePath. func FileExist(filePath string) bool { _, err := os.Stat(filePath) if err != nil && os.IsNotExist(err) { @@ -39,9 +40,10 @@ func FileExist(filePath string) bool { return true } -func AbsolutePath(Datadir string, filename string) string { +// AbsolutePath returns datadir + filename, or filename if it is absolute. +func AbsolutePath(datadir string, filename string) string { if filepath.IsAbs(filename) { return filename } - return filepath.Join(Datadir, filename) + return filepath.Join(datadir, filename) } diff --git a/common/types.go b/common/types.go index 533aa562d915..a33fe35bcc34 100644 --- a/common/types.go +++ b/common/types.go @@ -106,7 +106,7 @@ func (h Hash) Cmp(other Hash) int { // IsZero returns if a Hash is empty func (h Hash) IsZero() bool { return h == Hash{} } -// Get the string representation of the underlying hash +// Str get the string representation of the underlying hash func (h Hash) Str() string { return string(h[:]) } // Bytes gets the byte representation of the underlying hash. @@ -161,14 +161,6 @@ func (h *Hash) SetBytes(b []byte) { copy(h[HashLength-len(b):], b) } -// Set string `s` to h. If s is larger than len(h) s will be cropped (from left) to fit. -func (h *Hash) SetString(s string) { h.SetBytes([]byte(s)) } - -// Sets h to other -func (h *Hash) Set(other Hash) { - copy(h[:], other[:]) -} - // Generate implements testing/quick.Generator. func (h Hash) Generate(rand *rand.Rand, size int) reflect.Value { m := rand.Intn(len(h)) @@ -178,10 +170,6 @@ func (h Hash) Generate(rand *rand.Rand, size int) reflect.Value { return reflect.ValueOf(h) } -func EmptyHash(h Hash) bool { - return h == Hash{} -} - // UnprefixedHash allows marshaling a Hash without 0x prefix. type UnprefixedHash Hash @@ -200,6 +188,8 @@ func (h UnprefixedHash) MarshalText() ([]byte, error) { // Address represents the 20 byte address of an Ethereum account. type Address [AddressLength]byte +// BytesToAddress returns Address with value b. +// If b is larger than len(h), b will be cropped from the left. func BytesToAddress(b []byte) Address { var a Address a.SetBytes(b) @@ -231,11 +221,17 @@ func IsHexAddress(s string) bool { // IsZero returns if a address is empty func (a Address) IsZero() bool { return a == Address{} } -// Get the string representation of the underlying address -func (a Address) Str() string { return string(a[:]) } +// Str gets the string representation of the underlying address +func (a Address) Str() string { return string(a[:]) } + +// Bytes gets the string representation of the underlying address. func (a Address) Bytes() []byte { return a[:] } + +// Big converts an address to a big integer. func (a Address) Big() *big.Int { return new(big.Int).SetBytes(a[:]) } -func (a Address) Hash() Hash { return BytesToHash(a[:]) } + +// Hash converts an address to a hash by left-padding it with zeros. +func (a Address) Hash() Hash { return BytesToHash(a[:]) } // Hex returns an EIP55-compliant hex string representation of the address. func (a Address) Hex() string { @@ -259,7 +255,7 @@ func (a Address) Hex() string { return "xdc" + string(result) } -// String implements the stringer interface and is used also by the logger. +// String implements fmt.Stringer. func (a Address) String() string { return a.Hex() } @@ -270,7 +266,8 @@ func (a Address) Format(s fmt.State, c rune) { fmt.Fprintf(s, "%"+string(c), a[:]) } -// Sets the address to the value of b. If b is larger than len(a) it will panic +// SetBytes sets the address to the value of b. +// If b is larger than len(a) it will panic. func (a *Address) SetBytes(b []byte) { if len(b) > len(a) { b = b[len(b)-AddressLength:] @@ -278,14 +275,6 @@ func (a *Address) SetBytes(b []byte) { copy(a[AddressLength-len(b):], b) } -// Set string `s` to a. If s is larger than len(a) it will panic -func (a *Address) SetString(s string) { a.SetBytes([]byte(s)) } - -// Sets a to other -func (a *Address) Set(other Address) { - copy(a[:], other[:]) -} - // MarshalText returns the hex representation of a. func (a Address) MarshalText() ([]byte, error) { // Handle '0x' or 'xdc' prefix here. @@ -306,7 +295,7 @@ func (a *Address) UnmarshalJSON(input []byte) error { return hexutil.UnmarshalFixedJSON(addressT, input, a[:]) } -// UnprefixedHash allows marshaling an Address without 0x prefix. +// UnprefixedAddress allows marshaling an Address without 0x prefix. type UnprefixedAddress Address // UnmarshalText decodes the address from hex. The 0x prefix is optional. @@ -319,7 +308,7 @@ func (a UnprefixedAddress) MarshalText() ([]byte, error) { return []byte(hex.EncodeToString(a[:])), nil } -// Extract validators from byte array. +// RemoveItemFromArray extracts validators from byte array. func RemoveItemFromArray(array []Address, items []Address) []Address { // Create newArray to stop append change array value newArray := make([]Address, len(array)) @@ -340,7 +329,7 @@ func RemoveItemFromArray(array []Address, items []Address) []Address { return newArray } -// Extract validators from byte array. +// ExtractAddressToBytes extracts validators from byte array. func ExtractAddressToBytes(penalties []Address) []byte { data := []byte{} for _, signer := range penalties { diff --git a/common/types_template.go b/common/types_template.go deleted file mode 100644 index 9a8f29977b6a..000000000000 --- a/common/types_template.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// +build none -//sed -e 's/_N_/Hash/g' -e 's/_S_/32/g' -e '1d' types_template.go | gofmt -w hash.go - -package common - -import "math/big" - -type _N_ [_S_]byte - -func BytesTo_N_(b []byte) _N_ { - var h _N_ - h.SetBytes(b) - return h -} -func StringTo_N_(s string) _N_ { return BytesTo_N_([]byte(s)) } -func BigTo_N_(b *big.Int) _N_ { return BytesTo_N_(b.Bytes()) } -func HexTo_N_(s string) _N_ { return BytesTo_N_(FromHex(s)) } - -// Don't use the default 'String' method in case we want to overwrite - -// Get the string representation of the underlying hash -func (h _N_) Str() string { return string(h[:]) } -func (h _N_) Bytes() []byte { return h[:] } -func (h _N_) Big() *big.Int { return new(big.Int).SetBytes(h[:]) } -func (h _N_) Hex() string { return "0x" + Bytes2Hex(h[:]) } - -// Sets the hash to the value of b. If b is larger than len(h) it will panic -func (h *_N_) SetBytes(b []byte) { - // Use the right most bytes - if len(b) > len(h) { - b = b[len(b)-_S_:] - } - - // Reverse the loop - for i := len(b) - 1; i >= 0; i-- { - h[_S_-len(b)+i] = b[i] - } -} - -// Set string `s` to h. If s is larger than len(h) it will panic -func (h *_N_) SetString(s string) { h.SetBytes([]byte(s)) } - -// Sets h to other -func (h *_N_) Set(other _N_) { - for i, v := range other { - h[i] = v - } -} diff --git a/core/blockchain.go b/core/blockchain.go index 48509e652057..6c75b759aa9a 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -957,7 +957,7 @@ func (bc *BlockChain) saveData() { author, _ := bc.Engine().Author(recent.Header()) if tradingService != nil { tradingRoot, _ := tradingService.GetTradingStateRoot(recent, author) - if !common.EmptyHash(tradingRoot) && tradingTriedb != nil { + if !tradingRoot.IsZero() && tradingTriedb != nil { if err := tradingTriedb.Commit(tradingRoot, true); err != nil { log.Error("Failed to commit trading state recent state trie", "err", err) } @@ -965,7 +965,7 @@ func (bc *BlockChain) saveData() { } if lendingService != nil { lendingRoot, _ := lendingService.GetLendingStateRoot(recent, author) - if !common.EmptyHash(lendingRoot) && lendingTriedb != nil { + if !lendingRoot.IsZero() && lendingTriedb != nil { if err := lendingTriedb.Commit(lendingRoot, true); err != nil { log.Error("Failed to commit lending state recent state trie", "err", err) } diff --git a/core/state/state_test.go b/core/state/state_test.go index ddc7df61ce2e..c4b49628d26b 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -100,7 +100,7 @@ func (s *StateSuite) TestNull(c *checker.C) { s.state.SetState(address, common.Hash{}, value) s.state.Commit(false) value = s.state.GetState(address, common.Hash{}) - if !common.EmptyHash(value) { + if !value.IsZero() { c.Errorf("expected empty hash. got %x", value) } } diff --git a/core/state/trc21_reader.go b/core/state/trc21_reader.go index 561cebb5c766..994575e6c068 100644 --- a/core/state/trc21_reader.go +++ b/core/state/trc21_reader.go @@ -51,7 +51,7 @@ func GetTRC21FeeCapacityFromState(statedb *StateDB) map[common.Address]*big.Int for i := uint64(0); i < tokenCount; i++ { key := GetLocDynamicArrAtElement(slotTokensHash, i, 1) value := statedb.GetState(common.TRC21IssuerSMC, key) - if !common.EmptyHash(value) { + if !value.IsZero() { token := common.BytesToAddress(value.Bytes()) balanceKey := GetLocMappingAtKey(token.Hash(), slotTokensState) balanceHash := statedb.GetState(common.TRC21IssuerSMC, common.BigToHash(balanceKey)) @@ -68,14 +68,14 @@ func PayFeeWithTRC21TxFail(statedb *StateDB, from common.Address, token common.A slotBalanceTrc21 := SlotTRC21Token["balances"] balanceKey := GetLocMappingAtKey(from.Hash(), slotBalanceTrc21) balanceHash := statedb.GetState(token, common.BigToHash(balanceKey)) - if !common.EmptyHash(balanceHash) { + if !balanceHash.IsZero() { balance := balanceHash.Big() feeUsed := big.NewInt(0) if balance.Cmp(feeUsed) <= 0 { return } issuerTokenKey := GetLocSimpleVariable(SlotTRC21Token["issuer"]) - if common.EmptyHash(issuerTokenKey) { + if issuerTokenKey.IsZero() { return } issuerAddr := common.BytesToAddress(statedb.GetState(token, issuerTokenKey).Bytes()) @@ -106,7 +106,7 @@ func ValidateTRC21Tx(statedb *StateDB, from common.Address, token common.Address balanceKey := GetLocMappingAtKey(from.Hash(), slotBalanceTrc21) balanceHash := statedb.GetState(token, common.BigToHash(balanceKey)) - if !common.EmptyHash(balanceHash) { + if !balanceHash.IsZero() { balance := balanceHash.Big() minFeeTokenKey := GetLocSimpleVariable(SlotTRC21Token["minFee"]) minFeeHash := statedb.GetState(token, minFeeTokenKey) @@ -129,7 +129,7 @@ func ValidateTRC21Tx(statedb *StateDB, from common.Address, token common.Address } else { // we both accept tx with balance = 0 and fee = 0 minFeeTokenKey := GetLocSimpleVariable(SlotTRC21Token["minFee"]) - if !common.EmptyHash(minFeeTokenKey) { + if !minFeeTokenKey.IsZero() { return true } } diff --git a/core/txpool/order_pool.go b/core/txpool/order_pool.go index 87ef9e80dc65..4857d993886e 100644 --- a/core/txpool/order_pool.go +++ b/core/txpool/order_pool.go @@ -492,7 +492,7 @@ func (pool *OrderPool) validateOrder(tx *types.OrderTransaction) error { var signer = types.OrderTxSigner{} if !tx.IsCancelledOrder() { - if !common.EmptyHash(tx.OrderHash()) { + if !tx.OrderHash().IsZero() { if signer.Hash(tx) != tx.OrderHash() { return ErrInvalidOrderHash } diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index f5fb6d1c5851..ec779eb07732 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -682,7 +682,7 @@ func (d *Downloader) findAncestor(p *peerConnection, height uint64) (uint64, err } } // If the head fetch already found an ancestor, return - if !common.EmptyHash(hash) { + if !hash.IsZero() { if int64(number) <= floor { p.log.Warn("Ancestor below allowance", "number", number, "hash", hash, "allowance", floor) return 0, errInvalidAncestor From 7cd3e56d18dcffe708fef3507fd9fb199f1cbccd Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 12 Dec 2024 17:56:39 +0800 Subject: [PATCH 393/479] accounts/abi/bind: stop using goimports in the binding generator (#17768 #20311) --- accounts/abi/bind/bind.go | 6 +- accounts/abi/bind/bind_test.go | 170 +++++++++++++++++++++++++++++---- accounts/abi/bind/template.go | 24 +++++ internal/build/util.go | 23 ----- 4 files changed, 181 insertions(+), 42 deletions(-) diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go index 79d05afb445d..0c4ba0a81e44 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/bind/bind.go @@ -23,13 +23,13 @@ package bind import ( "bytes" "fmt" + "go/format" "regexp" "strings" "text/template" "unicode" "github.com/XinFinOrg/XDPoSChain/accounts/abi" - "golang.org/x/tools/imports" ) // Lang is a target programming language selector to generate bindings for. @@ -143,9 +143,9 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La if err := tmpl.Execute(buffer, data); err != nil { return "", err } - // For Go bindings pass the code through goimports to clean it up and double check + // For Go bindings pass the code through gofmt to clean it up if lang == LangGo { - code, err := imports.Process(".", buffer.Bytes(), nil) + code, err := format.Source(buffer.Bytes()) if err != nil { return "", fmt.Errorf("%v\n%s", err, buffer) } diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index 2c477882ebf3..448ffe2b6acc 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -27,7 +27,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/params" - "golang.org/x/tools/imports" ) var bindTests = []struct { @@ -35,6 +34,7 @@ var bindTests = []struct { contract string bytecode string abi string + imports string tester string }{ // Test that the binding is available in combined and separate forms too @@ -43,6 +43,7 @@ var bindTests = []struct { `contract NilContract {}`, `606060405260068060106000396000f3606060405200`, `[]`, + `"github.com/XinFinOrg/XDPoSChain/common"`, ` if b, err := NewEmpty(common.Address{}, nil); b == nil || err != nil { t.Fatalf("combined binding (%v) nil or error (%v) not nil", b, nil) @@ -61,6 +62,7 @@ var bindTests = []struct { `https://ethereum.org/token`, `60606040526040516107fd3803806107fd83398101604052805160805160a05160c051929391820192909101600160a060020a0333166000908152600360209081526040822086905581548551838052601f6002600019610100600186161502019093169290920482018390047f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56390810193919290918801908390106100e857805160ff19168380011785555b506101189291505b8082111561017157600081556001016100b4565b50506002805460ff19168317905550505050610658806101a56000396000f35b828001600101855582156100ac579182015b828111156100ac5782518260005055916020019190600101906100fa565b50508060016000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017557805160ff19168380011785555b506100c89291506100b4565b5090565b82800160010185558215610165579182015b8281111561016557825182600050559160200191906001019061018756606060405236156100775760e060020a600035046306fdde03811461007f57806323b872dd146100dc578063313ce5671461010e57806370a082311461011a57806395d89b4114610132578063a9059cbb1461018e578063cae9ca51146101bd578063dc3080f21461031c578063dd62ed3e14610341575b610365610002565b61036760008054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156104eb5780601f106104c0576101008083540402835291602001916104eb565b6103d5600435602435604435600160a060020a038316600090815260036020526040812054829010156104f357610002565b6103e760025460ff1681565b6103d560043560036020526000908152604090205481565b610367600180546020600282841615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156104eb5780601f106104c0576101008083540402835291602001916104eb565b610365600435602435600160a060020a033316600090815260036020526040902054819010156103f157610002565b60806020604435600481810135601f8101849004909302840160405260608381526103d5948235946024803595606494939101919081908382808284375094965050505050505060006000836004600050600033600160a060020a03168152602001908152602001600020600050600087600160a060020a031681526020019081526020016000206000508190555084905080600160a060020a0316638f4ffcb1338630876040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a03168152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156102f25780820380516001836020036101000a031916815260200191505b50955050505050506000604051808303816000876161da5a03f11561000257505050509392505050565b6005602090815260043560009081526040808220909252602435815220546103d59081565b60046020818152903560009081526040808220909252602435815220546103d59081565b005b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156103c75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60408051918252519081900360200190f35b6060908152602090f35b600160a060020a03821660009081526040902054808201101561041357610002565b806003600050600033600160a060020a03168152602001908152602001600020600082828250540392505081905550806003600050600084600160a060020a0316815260200190815260200160002060008282825054019250508190555081600160a060020a031633600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a35050565b820191906000526020600020905b8154815290600101906020018083116104ce57829003601f168201915b505050505081565b600160a060020a03831681526040812054808301101561051257610002565b600160a060020a0380851680835260046020908152604080852033949094168086529382528085205492855260058252808520938552929052908220548301111561055c57610002565b816003600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816003600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816005600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054019250508190555082600160a060020a031633600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3939250505056`, `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"},{"name":"_extraData","type":"bytes"}],"name":"approveAndCall","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"spentAllowance","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"inputs":[{"name":"initialSupply","type":"uint256"},{"name":"tokenName","type":"string"},{"name":"decimalUnits","type":"uint8"},{"name":"tokenSymbol","type":"string"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]`, + `"github.com/XinFinOrg/XDPoSChain/common"`, ` if b, err := NewToken(common.Address{}, nil); b == nil || err != nil { t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil) @@ -72,6 +74,7 @@ var bindTests = []struct { `https://ethereum.org/crowdsale`, `606060408190526007805460ff1916905560a0806105a883396101006040529051608051915160c05160e05160008054600160a060020a03199081169095178155670de0b6b3a7640000958602600155603c9093024201600355930260045560058054909216909217905561052f90819061007990396000f36060604052361561006c5760e060020a600035046301cb3b20811461008257806329dcb0cf1461014457806338af3eed1461014d5780636e66f6e91461015f5780637a3a0e84146101715780637b3e5e7b1461017a578063a035b1fe14610183578063dc0d3dff1461018c575b61020060075460009060ff161561032357610002565b61020060035460009042106103205760025460015490106103cb576002548154600160a060020a0316908290606082818181858883f150915460025460408051600160a060020a039390931683526020830191909152818101869052517fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf6945090819003909201919050a15b60405160008054600160a060020a039081169230909116319082818181858883f150506007805460ff1916600117905550505050565b6103a160035481565b6103ab600054600160a060020a031681565b6103ab600554600160a060020a031681565b6103a160015481565b6103a160025481565b6103a160045481565b6103be60043560068054829081101561000257506000526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f8101547ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d409190910154600160a060020a03919091169082565b005b505050815481101561000257906000526020600020906002020160005060008201518160000160006101000a815481600160a060020a030219169083021790555060208201518160010160005055905050806002600082828250540192505081905550600560009054906101000a9004600160a060020a0316600160a060020a031663a9059cbb3360046000505484046040518360e060020a0281526004018083600160a060020a03168152602001828152602001925050506000604051808303816000876161da5a03f11561000257505060408051600160a060020a03331681526020810184905260018183015290517fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf692509081900360600190a15b50565b5060a0604052336060908152346080819052600680546001810180835592939282908280158290116102025760020281600202836000526020600020918201910161020291905b8082111561039d57805473ffffffffffffffffffffffffffffffffffffffff19168155600060019190910190815561036a565b5090565b6060908152602090f35b600160a060020a03166060908152602090f35b6060918252608052604090f35b5b60065481101561010e576006805482908110156100025760009182526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0190600680549254600160a060020a0316928490811015610002576002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d40015460405190915082818181858883f19350505050507fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf660066000508281548110156100025760008290526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01548154600160a060020a039190911691908490811015610002576002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d40015460408051600160a060020a0394909416845260208401919091526000838201525191829003606001919050a16001016103cc56`, `[{"constant":false,"inputs":[],"name":"checkGoalReached","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"deadline","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"beneficiary","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"tokenReward","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"fundingGoal","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"amountRaised","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"price","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"funders","outputs":[{"name":"addr","type":"address"},{"name":"amount","type":"uint256"}],"type":"function"},{"inputs":[{"name":"ifSuccessfulSendTo","type":"address"},{"name":"fundingGoalInEthers","type":"uint256"},{"name":"durationInMinutes","type":"uint256"},{"name":"etherCostOfEachToken","type":"uint256"},{"name":"addressOfTokenUsedAsReward","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"backer","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"isContribution","type":"bool"}],"name":"FundTransfer","type":"event"}]`, + `"github.com/XinFinOrg/XDPoSChain/common"`, ` if b, err := NewCrowdsale(common.Address{}, nil); b == nil || err != nil { t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil) @@ -83,6 +86,7 @@ var bindTests = []struct { `https://ethereum.org/dao`, `606060405260405160808061145f833960e06040529051905160a05160c05160008054600160a060020a03191633179055600184815560028490556003839055600780549182018082558280158290116100b8576003028160030283600052602060002091820191016100b891906101c8565b50506060919091015160029190910155600160a060020a0381166000146100a65760008054600160a060020a031916821790555b505050506111f18061026e6000396000f35b505060408051608081018252600080825260208281018290528351908101845281815292820192909252426060820152600780549194509250811015610002579081527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6889050815181546020848101517401000000000000000000000000000000000000000002600160a060020a03199290921690921760a060020a60ff021916178255604083015180516001848101805460008281528690209195600293821615610100026000190190911692909204601f9081018390048201949192919091019083901061023e57805160ff19168380011785555b50610072929150610226565b5050600060028201556001015b8082111561023a578054600160a860020a031916815560018181018054600080835592600290821615610100026000190190911604601f81901061020c57506101bb565b601f0160209004906000526020600020908101906101bb91905b8082111561023a5760008155600101610226565b5090565b828001600101855582156101af579182015b828111156101af57825182600050559160200191906001019061025056606060405236156100b95760e060020a6000350463013cf08b81146100bb578063237e9492146101285780633910682114610281578063400e3949146102995780635daf08ca146102a257806369bd34361461032f5780638160f0b5146103385780638da5cb5b146103415780639644fcbd14610353578063aa02a90f146103be578063b1050da5146103c7578063bcca1fd3146104b5578063d3c0715b146104dc578063eceb29451461058d578063f2fde38b1461067b575b005b61069c6004356004805482908110156100025790600052602060002090600a02016000506005810154815460018301546003840154600485015460068601546007870154600160a060020a03959095169750929560020194919360ff828116946101009093041692919089565b60408051602060248035600481810135601f81018590048502860185019096528585526107759581359591946044949293909201918190840183828082843750949650505050505050600060006004600050848154811015610002575090527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19e600a8402908101547f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b909101904210806101e65750600481015460ff165b8061026757508060000160009054906101000a9004600160a060020a03168160010160005054846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f15090500193505050506040518091039020816007016000505414155b8061027757506001546005820154105b1561109257610002565b61077560043560066020526000908152604090205481565b61077560055481565b61078760043560078054829081101561000257506000526003026000805160206111d18339815191528101547fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68a820154600160a060020a0382169260a060020a90920460ff16917fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c689019084565b61077560025481565b61077560015481565b610830600054600160a060020a031681565b604080516020604435600481810135601f81018490048402850184019095528484526100b9948135946024803595939460649492939101918190840183828082843750949650505050505050600080548190600160a060020a03908116339091161461084d57610002565b61077560035481565b604080516020604435600481810135601f8101849004840285018401909552848452610775948135946024803595939460649492939101918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976084979196506024909101945090925082915084018382808284375094965050505050505033600160a060020a031660009081526006602052604081205481908114806104ab5750604081205460078054909190811015610002579082526003026000805160206111d1833981519152015460a060020a900460ff16155b15610ce557610002565b6100b960043560243560443560005433600160a060020a03908116911614610b1857610002565b604080516020604435600481810135601f810184900484028501840190955284845261077594813594602480359593946064949293910191819084018382808284375094965050505050505033600160a060020a031660009081526006602052604081205481908114806105835750604081205460078054909190811015610002579082526003026000805160206111d18339815191520181505460a060020a900460ff16155b15610f1d57610002565b604080516020606435600481810135601f81018490048402850184019095528484526107759481359460248035956044359560849492019190819084018382808284375094965050505050505060006000600460005086815481101561000257908252600a027f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b01815090508484846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f150905001935050505060405180910390208160070160005054149150610cdc565b6100b960043560005433600160a060020a03908116911614610f0857610002565b604051808a600160a060020a031681526020018981526020018060200188815260200187815260200186815260200185815260200184815260200183815260200182810382528981815460018160011615610100020316600290048152602001915080546001816001161561010002031660029004801561075e5780601f106107335761010080835404028352916020019161075e565b820191906000526020600020905b81548152906001019060200180831161074157829003601f168201915b50509a505050505050505050505060405180910390f35b60408051918252519081900360200190f35b60408051600160a060020a038616815260208101859052606081018390526080918101828152845460026001821615610100026000190190911604928201839052909160a08301908590801561081e5780601f106107f35761010080835404028352916020019161081e565b820191906000526020600020905b81548152906001019060200180831161080157829003601f168201915b50509550505050505060405180910390f35b60408051600160a060020a03929092168252519081900360200190f35b600160a060020a03851660009081526006602052604081205414156108a957604060002060078054918290556001820180825582801582901161095c5760030281600302836000526020600020918201910161095c9190610a4f565b600160a060020a03851660009081526006602052604090205460078054919350908390811015610002575060005250600381026000805160206111d183398151915201805474ff0000000000000000000000000000000000000000191660a060020a85021781555b60408051600160a060020a03871681526020810186905281517f27b022af4a8347100c7a041ce5ccf8e14d644ff05de696315196faae8cd50c9b929181900390910190a15050505050565b505050915081506080604051908101604052808681526020018581526020018481526020014281526020015060076000508381548110156100025790600052602060002090600302016000508151815460208481015160a060020a02600160a060020a03199290921690921774ff00000000000000000000000000000000000000001916178255604083015180516001848101805460008281528690209195600293821615610100026000190190911692909204601f90810183900482019491929190910190839010610ad357805160ff19168380011785555b50610b03929150610abb565b5050600060028201556001015b80821115610acf57805474ffffffffffffffffffffffffffffffffffffffffff1916815560018181018054600080835592600290821615610100026000190190911604601f819010610aa15750610a42565b601f016020900490600052602060002090810190610a4291905b80821115610acf5760008155600101610abb565b5090565b82800160010185558215610a36579182015b82811115610a36578251826000505591602001919060010190610ae5565b50506060919091015160029190910155610911565b600183905560028290556003819055604080518481526020810184905280820183905290517fa439d3fa452be5e0e1e24a8145e715f4fd8b9c08c96a42fd82a855a85e5d57de9181900360600190a1505050565b50508585846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f150905001935050505060405180910390208160070160005081905550600260005054603c024201816003016000508190555060008160040160006101000a81548160ff0219169083021790555060008160040160016101000a81548160ff02191690830217905550600081600501600050819055507f646fec02522b41e7125cfc859a64fd4f4cefd5dc3b6237ca0abe251ded1fa881828787876040518085815260200184600160a060020a03168152602001838152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015610cc45780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a1600182016005555b50949350505050565b6004805460018101808355909190828015829011610d1c57600a0281600a028360005260206000209182019101610d1c9190610db8565b505060048054929450918491508110156100025790600052602060002090600a02016000508054600160a060020a031916871781556001818101879055855160028381018054600082815260209081902096975091959481161561010002600019011691909104601f90810182900484019391890190839010610ed857805160ff19168380011785555b50610b6c929150610abb565b50506001015b80821115610acf578054600160a060020a03191681556000600182810182905560028381018054848255909281161561010002600019011604601f819010610e9c57505b5060006003830181905560048301805461ffff191690556005830181905560068301819055600783018190556008830180548282559082526020909120610db2916002028101905b80821115610acf57805474ffffffffffffffffffffffffffffffffffffffffff1916815560018181018054600080835592600290821615610100026000190190911604601f819010610eba57505b5050600101610e44565b601f016020900490600052602060002090810190610dfc9190610abb565b601f016020900490600052602060002090810190610e929190610abb565b82800160010185558215610da6579182015b82811115610da6578251826000505591602001919060010190610eea565b60008054600160a060020a0319168217905550565b600480548690811015610002576000918252600a027f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b01905033600160a060020a0316600090815260098201602052604090205490915060ff1660011415610f8457610002565b33600160a060020a031660009081526009820160205260409020805460ff1916600190811790915560058201805490910190558315610fcd576006810180546001019055610fda565b6006810180546000190190555b7fc34f869b7ff431b034b7b9aea9822dac189a685e0b015c7d1be3add3f89128e8858533866040518085815260200184815260200183600160a060020a03168152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f16801561107a5780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a1509392505050565b6006810154600354901315611158578060000160009054906101000a9004600160a060020a0316600160a060020a03168160010160005054670de0b6b3a76400000284604051808280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156111225780820380516001836020036101000a031916815260200191505b5091505060006040518083038185876185025a03f15050505060048101805460ff191660011761ff00191661010017905561116d565b60048101805460ff191660011761ff00191690555b60068101546005820154600483015460408051888152602081019490945283810192909252610100900460ff166060830152517fd220b7272a8b6d0d7d6bcdace67b936a8f175e6d5c1b3ee438b72256b32ab3af9181900360800190a1509291505056a66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688`, `[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"proposals","outputs":[{"name":"recipient","type":"address"},{"name":"amount","type":"uint256"},{"name":"description","type":"string"},{"name":"votingDeadline","type":"uint256"},{"name":"executed","type":"bool"},{"name":"proposalPassed","type":"bool"},{"name":"numberOfVotes","type":"uint256"},{"name":"currentResult","type":"int256"},{"name":"proposalHash","type":"bytes32"}],"type":"function"},{"constant":false,"inputs":[{"name":"proposalNumber","type":"uint256"},{"name":"transactionBytecode","type":"bytes"}],"name":"executeProposal","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"memberId","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"numProposals","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"members","outputs":[{"name":"member","type":"address"},{"name":"canVote","type":"bool"},{"name":"name","type":"string"},{"name":"memberSince","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"debatingPeriodInMinutes","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"minimumQuorum","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"targetMember","type":"address"},{"name":"canVote","type":"bool"},{"name":"memberName","type":"string"}],"name":"changeMembership","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"majorityMargin","outputs":[{"name":"","type":"int256"}],"type":"function"},{"constant":false,"inputs":[{"name":"beneficiary","type":"address"},{"name":"etherAmount","type":"uint256"},{"name":"JobDescription","type":"string"},{"name":"transactionBytecode","type":"bytes"}],"name":"newProposal","outputs":[{"name":"proposalID","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"minimumQuorumForProposals","type":"uint256"},{"name":"minutesForDebate","type":"uint256"},{"name":"marginOfVotesForMajority","type":"int256"}],"name":"changeVotingRules","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"proposalNumber","type":"uint256"},{"name":"supportsProposal","type":"bool"},{"name":"justificationText","type":"string"}],"name":"vote","outputs":[{"name":"voteID","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"proposalNumber","type":"uint256"},{"name":"beneficiary","type":"address"},{"name":"etherAmount","type":"uint256"},{"name":"transactionBytecode","type":"bytes"}],"name":"checkProposalCode","outputs":[{"name":"codeChecksOut","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"type":"function"},{"inputs":[{"name":"minimumQuorumForProposals","type":"uint256"},{"name":"minutesForDebate","type":"uint256"},{"name":"marginOfVotesForMajority","type":"int256"},{"name":"congressLeader","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"recipient","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"description","type":"string"}],"name":"ProposalAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"position","type":"bool"},{"indexed":false,"name":"voter","type":"address"},{"indexed":false,"name":"justification","type":"string"}],"name":"Voted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"result","type":"int256"},{"indexed":false,"name":"quorum","type":"uint256"},{"indexed":false,"name":"active","type":"bool"}],"name":"ProposalTallied","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"member","type":"address"},{"indexed":false,"name":"isMember","type":"bool"}],"name":"MembershipChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"minimumQuorum","type":"uint256"},{"indexed":false,"name":"debatingPeriodInMinutes","type":"uint256"},{"indexed":false,"name":"majorityMargin","type":"int256"}],"name":"ChangeOfRules","type":"event"}]`, + `"github.com/XinFinOrg/XDPoSChain/common"`, ` if b, err := NewDAO(common.Address{}, nil); b == nil || err != nil { t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil) @@ -102,6 +106,11 @@ var bindTests = []struct { {"type":"function","name":"mixedInputs","constant":true,"inputs":[{"name":"","type":"string"},{"name":"str","type":"string"}],"outputs":[]} ] `, + ` + "fmt" + + "github.com/XinFinOrg/XDPoSChain/common" + `, `if b, err := NewInputChecker(common.Address{}, nil); b == nil || err != nil { t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil) } else if false { // Don't run, just compile and test types @@ -131,6 +140,11 @@ var bindTests = []struct { {"type":"function","name":"mixedOutputs","constant":true,"inputs":[],"outputs":[{"name":"","type":"string"},{"name":"str","type":"string"}]} ] `, + ` + "fmt" + + "github.com/XinFinOrg/XDPoSChain/common" + `, `if b, err := NewOutputChecker(common.Address{}, nil); b == nil || err != nil { t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil) } else if false { // Don't run, just compile and test types @@ -160,6 +174,13 @@ var bindTests = []struct { {"type":"event","name":"dynamic","inputs":[{"name":"idxStr","type":"string","indexed":true},{"name":"idxDat","type":"bytes","indexed":true},{"name":"str","type":"string"},{"name":"dat","type":"bytes"}]} ] `, + ` + "fmt" + "math/big" + "reflect" + + "github.com/XinFinOrg/XDPoSChain/common" + `, `if e, err := NewEventChecker(common.Address{}, nil); e == nil || err != nil { t.Fatalf("binding (%v) nil or error (%v) not nil", e, nil) } else if false { // Don't run, just compile and test types @@ -225,6 +246,15 @@ var bindTests = []struct { `, `6060604052604051610328380380610328833981016040528051018060006000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10608d57805160ff19168380011785555b50607c9291505b8082111560ba57838155600101606b565b50505061026a806100be6000396000f35b828001600101855582156064579182015b828111156064578251826000505591602001919060010190609e565b509056606060405260e060020a60003504630d86a0e181146100315780636874e8091461008d578063d736c513146100ea575b005b610190600180546020600282841615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156102295780601f106101fe57610100808354040283529160200191610229565b61019060008054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156102295780601f106101fe57610100808354040283529160200191610229565b60206004803580820135601f81018490049093026080908101604052606084815261002f946024939192918401918190838280828437509496505050505050508060016000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061023157805160ff19168380011785555b506102619291505b808211156102665760008155830161017d565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156101f05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b820191906000526020600020905b81548152906001019060200180831161020c57829003601f168201915b505050505081565b82800160010185558215610175579182015b82811115610175578251826000505591602001919060010190610243565b505050565b509056`, `[{"constant":true,"inputs":[],"name":"transactString","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":true,"inputs":[],"name":"deployString","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"str","type":"string"}],"name":"transact","outputs":[],"type":"function"},{"inputs":[{"name":"str","type":"string"}],"type":"constructor"}]`, + ` + "math/big" + + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" + "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/params" + `, ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() @@ -266,6 +296,15 @@ var bindTests = []struct { `, `606060405260dc8060106000396000f3606060405260e060020a6000350463993a04b78114601a575b005b600060605260c0604052600260809081527f486900000000000000000000000000000000000000000000000000000000000060a05260017fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060e0829052610100819052606060c0908152600261012081905281906101409060a09080838184600060046012f1505081517fffff000000000000000000000000000000000000000000000000000000000000169091525050604051610160819003945092505050f3`, `[{"constant":true,"inputs":[],"name":"getter","outputs":[{"name":"","type":"string"},{"name":"","type":"int256"},{"name":"","type":"bytes32"}],"type":"function"}]`, + ` + "math/big" + + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" + "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/params" + `, ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() @@ -298,6 +337,15 @@ var bindTests = []struct { `, `606060405260dc8060106000396000f3606060405260e060020a60003504633175aae28114601a575b005b600060605260c0604052600260809081527f486900000000000000000000000000000000000000000000000000000000000060a05260017fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060e0829052610100819052606060c0908152600261012081905281906101409060a09080838184600060046012f1505081517fffff000000000000000000000000000000000000000000000000000000000000169091525050604051610160819003945092505050f3`, `[{"constant":true,"inputs":[],"name":"tuple","outputs":[{"name":"a","type":"string"},{"name":"b","type":"int256"},{"name":"c","type":"bytes32"}],"type":"function"}]`, + ` + "math/big" + + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" + "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/params" + `, ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() @@ -340,6 +388,17 @@ var bindTests = []struct { `, `606060405261015c806100126000396000f3606060405260e060020a6000350463be1127a3811461003c578063d88becc014610092578063e15a3db71461003c578063f637e5891461003c575b005b604080516020600480358082013583810285810185019096528085526100ee959294602494909392850192829185019084908082843750949650505050505050604080516020810190915260009052805b919050565b604080516102e0818101909252610138916004916102e491839060179083908390808284375090955050505050506102e0604051908101604052806017905b60008152602001906001900390816100d15790505081905061008d565b60405180806020018281038252838181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050019250505060405180910390f35b60405180826102e0808381846000600461015cf15090500191505060405180910390f3`, `[{"constant":true,"inputs":[{"name":"input","type":"address[]"}],"name":"echoAddresses","outputs":[{"name":"output","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"uint24[23]"}],"name":"echoFancyInts","outputs":[{"name":"output","type":"uint24[23]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"int256[]"}],"name":"echoInts","outputs":[{"name":"output","type":"int256[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"bool[]"}],"name":"echoBools","outputs":[{"name":"output","type":"bool[]"}],"type":"function"}]`, + ` + "math/big" + "reflect" + + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/params" + `, ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() @@ -374,6 +433,15 @@ var bindTests = []struct { `, `6060604052606a8060106000396000f360606040523615601d5760e060020a6000350463fc9c8d3981146040575b605e6000805473ffffffffffffffffffffffffffffffffffffffff191633179055565b606060005473ffffffffffffffffffffffffffffffffffffffff1681565b005b6060908152602090f3`, `[{"constant":true,"inputs":[],"name":"caller","outputs":[{"name":"","type":"address"}],"type":"function"}]`, + ` + "math/big" + + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" + "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/params" + `, ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() @@ -409,6 +477,12 @@ var bindTests = []struct { `, `6060604052609f8060106000396000f3606060405260e060020a6000350463f97a60058114601a575b005b600060605260c0604052600d60809081527f4920646f6e27742065786973740000000000000000000000000000000000000060a052602060c0908152600d60e081905281906101009060a09080838184600060046012f15050815172ffffffffffffffffffffffffffffffffffffff1916909152505060405161012081900392509050f3`, `[{"constant":true,"inputs":[],"name":"String","outputs":[{"name":"","type":"string"}],"type":"function"}]`, + ` + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/params" + `, ` // Create a simulator and wrap a non-deployed contract sim := backends.NewXDCSimulatedBackend(nil, uint64(10000000000), params.TestXDPoSMockChainConfig) @@ -443,6 +517,15 @@ var bindTests = []struct { `, `606060405261021c806100126000396000f3606060405260e060020a600035046323fcf32a81146100265780634f28bf0e1461007b575b005b6040805160206004803580820135601f8101849004840285018401909552848452610024949193602493909291840191908190840183828082843750949650505050505050620186a05a101561014e57610002565b6100db60008054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281529291908301828280156102145780601f106101e957610100808354040283529160200191610214565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561013b5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b505050565b8060006000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106101b557805160ff19168380011785555b506101499291505b808211156101e557600081556001016101a1565b82800160010185558215610199579182015b828111156101995782518260005055916020019190600101906101c7565b5090565b820191906000526020600020905b8154815290600101906020018083116101f757829003601f168201915b50505050508156`, `[{"constant":false,"inputs":[{"name":"value","type":"string"}],"name":"SetField","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"field","outputs":[{"name":"","type":"string"}],"type":"function"}]`, + ` + "math/big" + + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" + "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/params" + `, ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() @@ -478,6 +561,16 @@ var bindTests = []struct { } `, `6060604052346000575b6086806100176000396000f300606060405263ffffffff60e060020a60003504166349f8e98281146022575b6000565b34600057602c6055565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b335b905600a165627a7a72305820aef6b7685c0fa24ba6027e4870404a57df701473fe4107741805c19f5138417c0029`, `[{"constant":true,"inputs":[],"name":"callFrom","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}]`, + ` + "math/big" + + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/params" + `, ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() @@ -538,6 +631,16 @@ var bindTests = []struct { } `, `6060604052341561000f57600080fd5b6103858061001e6000396000f30060606040526004361061008e576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806303a592131461009357806346546dbe146100c357806367e6633d146100ec5780639df4848514610181578063af7486ab146101b1578063b564b34d146101e1578063e02ab24d14610211578063e409ca4514610241575b600080fd5b341561009e57600080fd5b6100a6610271565b604051808381526020018281526020019250505060405180910390f35b34156100ce57600080fd5b6100d6610286565b6040518082815260200191505060405180910390f35b34156100f757600080fd5b6100ff61028e565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561014557808201518184015260208101905061012a565b50505050905090810190601f1680156101725780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b341561018c57600080fd5b6101946102dc565b604051808381526020018281526020019250505060405180910390f35b34156101bc57600080fd5b6101c46102f1565b604051808381526020018281526020019250505060405180910390f35b34156101ec57600080fd5b6101f4610306565b604051808381526020018281526020019250505060405180910390f35b341561021c57600080fd5b61022461031b565b604051808381526020018281526020019250505060405180910390f35b341561024c57600080fd5b610254610330565b604051808381526020018281526020019250505060405180910390f35b60008060016002819150809050915091509091565b600080905090565b6000610298610345565b61013a8090506040805190810160405280600281526020017f7069000000000000000000000000000000000000000000000000000000000000815250915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b6020604051908101604052806000815250905600a165627a7a72305820d1a53d9de9d1e3d55cb3dc591900b63c4f1ded79114f7b79b332684840e186a40029`, `[{"constant":true,"inputs":[],"name":"LowerUpperCollision","outputs":[{"name":"_res","type":"int256"},{"name":"Res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_under_scored_func","outputs":[{"name":"_int","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UnderscoredOutput","outputs":[{"name":"_int","type":"int256"},{"name":"_string","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"PurelyUnderscoredOutput","outputs":[{"name":"_","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UpperLowerCollision","outputs":[{"name":"_Res","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"AllPurelyUnderscoredOutput","outputs":[{"name":"_","type":"int256"},{"name":"__","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UpperUpperCollision","outputs":[{"name":"_Res","type":"int256"},{"name":"Res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"LowerLowerCollision","outputs":[{"name":"_res","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"}]`, + ` + "fmt" + "math/big" + + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" + "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/params" + `, ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() @@ -608,6 +711,17 @@ var bindTests = []struct { `, `6060604052341561000f57600080fd5b61042c8061001e6000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063528300ff1461005c578063630c31e2146100fc578063c7d116dd14610156575b600080fd5b341561006757600080fd5b6100fa600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610194565b005b341561010757600080fd5b610154600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035600019169060200190919080351515906020019091908035906020019091905050610367565b005b341561016157600080fd5b610192600480803590602001909190803560010b90602001909190803563ffffffff169060200190919050506103c3565b005b806040518082805190602001908083835b6020831015156101ca57805182526020820191506020810190506020830392506101a5565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020826040518082805190602001908083835b60208310151561022d5780518252602082019150602081019050602083039250610208565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f3281fd4f5e152dd3385df49104a3f633706e21c9e80672e88d3bcddf33101f008484604051808060200180602001838103835285818151815260200191508051906020019080838360005b838110156102c15780820151818401526020810190506102a6565b50505050905090810190601f1680156102ee5780820380516001836020036101000a031916815260200191505b50838103825284818151815260200191508051906020019080838360005b8381101561032757808201518184015260208101905061030c565b50505050905090810190601f1680156103545780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a35050565b81151583600019168573ffffffffffffffffffffffffffffffffffffffff167f1f097de4289df643bd9c11011cc61367aa12983405c021056e706eb5ba1250c8846040518082815260200191505060405180910390a450505050565b8063ffffffff168260010b847f3ca7f3a77e5e6e15e781850bc82e32adfa378a2a609370db24b4d0fae10da2c960405160405180910390a45050505600a165627a7a72305820d1f8a8bbddbc5bb29f285891d6ae1eef8420c52afdc05e1573f6114d8e1714710029`, `[{"constant":false,"inputs":[{"name":"str","type":"string"},{"name":"blob","type":"bytes"}],"name":"raiseDynamicEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"id","type":"bytes32"},{"name":"flag","type":"bool"},{"name":"value","type":"uint256"}],"name":"raiseSimpleEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"number","type":"uint256"},{"name":"short","type":"int16"},{"name":"long","type":"uint32"}],"name":"raiseNodataEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"Addr","type":"address"},{"indexed":true,"name":"Id","type":"bytes32"},{"indexed":true,"name":"Flag","type":"bool"},{"indexed":false,"name":"Value","type":"uint256"}],"name":"SimpleEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"Number","type":"uint256"},{"indexed":true,"name":"Short","type":"int16"},{"indexed":true,"name":"Long","type":"uint32"}],"name":"NodataEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"IndexedString","type":"string"},{"indexed":true,"name":"IndexedBytes","type":"bytes"},{"indexed":false,"name":"NonIndexedString","type":"string"},{"indexed":false,"name":"NonIndexedBytes","type":"bytes"}],"name":"DynamicEvent","type":"event"}]`, + ` + "math/big" + "time" + + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/params" + `, ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() @@ -757,6 +871,15 @@ var bindTests = []struct { `, `6060604052341561000f57600080fd5b6106438061001e6000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063344248551461005c5780638ed4573a1461011457806398ed1856146101ab575b600080fd5b341561006757600080fd5b610112600480806107800190600580602002604051908101604052809291906000905b828210156101055783826101800201600480602002604051908101604052809291906000905b828210156100f25783826060020160038060200260405190810160405280929190826003602002808284378201915050505050815260200190600101906100b0565b505050508152602001906001019061008a565b5050505091905050610208565b005b341561011f57600080fd5b61012761021d565b604051808260056000925b8184101561019b578284602002015160046000925b8184101561018d5782846020020151600360200280838360005b8381101561017c578082015181840152602081019050610161565b505050509050019260010192610147565b925050509260010192610132565b9250505091505060405180910390f35b34156101b657600080fd5b6101de6004808035906020019091908035906020019091908035906020019091905050610309565b604051808267ffffffffffffffff1667ffffffffffffffff16815260200191505060405180910390f35b80600090600561021992919061035f565b5050565b6102256103b0565b6000600580602002604051908101604052809291906000905b8282101561030057838260040201600480602002604051908101604052809291906000905b828210156102ed578382016003806020026040519081016040528092919082600380156102d9576020028201916000905b82829054906101000a900467ffffffffffffffff1667ffffffffffffffff16815260200190600801906020826007010492830192600103820291508084116102945790505b505050505081526020019060010190610263565b505050508152602001906001019061023e565b50505050905090565b60008360058110151561031857fe5b600402018260048110151561032957fe5b018160038110151561033757fe5b6004918282040191900660080292509250509054906101000a900467ffffffffffffffff1681565b826005600402810192821561039f579160200282015b8281111561039e5782518290600461038e9291906103df565b5091602001919060040190610375565b5b5090506103ac919061042d565b5090565b610780604051908101604052806005905b6103c9610459565b8152602001906001900390816103c15790505090565b826004810192821561041c579160200282015b8281111561041b5782518290600361040b929190610488565b50916020019190600101906103f2565b5b5090506104299190610536565b5090565b61045691905b8082111561045257600081816104499190610562565b50600401610433565b5090565b90565b610180604051908101604052806004905b6104726105a7565b81526020019060019003908161046a5790505090565b82600380016004900481019282156105255791602002820160005b838211156104ef57835183826101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555092602001926008016020816007010492830192600103026104a3565b80156105235782816101000a81549067ffffffffffffffff02191690556008016020816007010492830192600103026104ef565b505b50905061053291906105d9565b5090565b61055f91905b8082111561055b57600081816105529190610610565b5060010161053c565b5090565b90565b50600081816105719190610610565b50600101600081816105839190610610565b50600101600081816105959190610610565b5060010160006105a59190610610565b565b6060604051908101604052806003905b600067ffffffffffffffff168152602001906001900390816105b75790505090565b61060d91905b8082111561060957600081816101000a81549067ffffffffffffffff0219169055506001016105df565b5090565b90565b50600090555600a165627a7a7230582087e5a43f6965ab6ef7a4ff056ab80ed78fd8c15cff57715a1bf34ec76a93661c0029`, `[{"constant":false,"inputs":[{"name":"arr","type":"uint64[3][4][5]"}],"name":"storeDeepUintArray","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"retrieveDeepArray","outputs":[{"name":"","type":"uint64[3][4][5]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"name":"deepUint64Array","outputs":[{"name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"}]`, + ` + "math/big" + + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" + "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/params" + `, ` // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() @@ -820,15 +943,6 @@ func TestBindings(t *testing.T) { t.Skip("go sdk not found for testing") } t.Log("Using config", params.TestXDPoSMockChainConfig) - // Skip the test if the go-ethereum sources are symlinked (https://github.com/golang/go/issues/14845) - linkTestCode := "package linktest\nfunc CheckSymlinks(){\nfmt.Println(backends.NewSimulatedBackend(nil))\n}" - linkTestDeps, err := imports.Process(os.TempDir(), []byte(linkTestCode), nil) - if err != nil { - t.Fatalf("failed check for goimports symlink bug: %v", err) - } - if !strings.Contains(string(linkTestDeps), "go-ethereum") { - t.Skip("symlinked environment doesn't support bind (https://github.com/golang/go/issues/14845)") - } // Create a temporary workspace for the test suite ws, err := os.MkdirTemp("", "") if err != nil { @@ -851,15 +965,39 @@ func TestBindings(t *testing.T) { t.Fatalf("test %d: failed to write binding: %v", i, err) } // Generate the test file with the injected test code - code := fmt.Sprintf("package bindtest\nimport \"testing\"\nfunc Test%s(t *testing.T){\n%s\n}", tt.name, tt.tester) - blob, err := imports.Process("", []byte(code), nil) - if err != nil { - t.Fatalf("test %d: failed to generate tests: %v", i, err) - } - if err := os.WriteFile(filepath.Join(pkg, strings.ToLower(tt.name)+"_test.go"), blob, 0600); err != nil { + code := fmt.Sprintf(` + package bindtest + + import ( + "testing" + %s + ) + + func Test%s(t *testing.T) { + %s + } + `, tt.imports, tt.name, tt.tester) + if err := os.WriteFile(filepath.Join(pkg, strings.ToLower(tt.name)+"_test.go"), []byte(code), 0600); err != nil { t.Fatalf("test %d: failed to write tests: %v", i, err) } } + // Convert the package to go modules and use the current source for go-ethereum + moder := exec.Command(gocmd, "mod", "init", "bindtest") + moder.Dir = pkg + if out, err := moder.CombinedOutput(); err != nil { + t.Fatalf("failed to convert binding test to modules: %v\n%s", err, out) + } + pwd, _ := os.Getwd() + replacer := exec.Command(gocmd, "mod", "edit", "-x", "-require", "github.com/XinFinOrg/XDPoSChain@v0.0.0", "-replace", "github.com/XinFinOrg/XDPoSChain="+filepath.Join(pwd, "..", "..", "..")) // Repo root + replacer.Dir = pkg + if out, err := replacer.CombinedOutput(); err != nil { + t.Fatalf("failed to replace binding test dependency to current source tree: %v\n%s", err, out) + } + tidier := exec.Command(gocmd, "mod", "tidy") + tidier.Dir = pkg + if out, err := tidier.CombinedOutput(); err != nil { + t.Fatalf("failed to tidy Go module file: %v\n%s", err, out) + } // Test the entire package and report any failures cmd := exec.Command(gocmd, "test", "-v", "-count", "1") cmd.Dir = pkg diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go index 61951dcd7c00..ad3cb74f9529 100644 --- a/accounts/abi/bind/template.go +++ b/accounts/abi/bind/template.go @@ -63,6 +63,30 @@ const tmplSourceGo = ` package {{.Package}} +import ( + "math/big" + "strings" + + ethereum "github.com/XinFinOrg/XDPoSChain" + "github.com/XinFinOrg/XDPoSChain/accounts/abi" + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.ErrNotFound + _ = abi.U256 + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription +) + {{range $contract := .Contracts}} // {{.Type}}ABI is the input ABI used to generate the binding from. const {{.Type}}ABI = "{{.InputABI}}" diff --git a/internal/build/util.go b/internal/build/util.go index 387c2a7d6f2a..3fcb411c70de 100644 --- a/internal/build/util.go +++ b/internal/build/util.go @@ -20,7 +20,6 @@ import ( "bytes" "flag" "fmt" - "io" "log" "os" "os/exec" @@ -87,28 +86,6 @@ func readGitFile(file string) string { return strings.TrimSpace(string(content)) } -// CopyFile copies a file. -func CopyFile(dst, src string, mode os.FileMode) { - if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { - log.Fatal(err) - } - destFile, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode) - if err != nil { - log.Fatal(err) - } - defer destFile.Close() - - srcFile, err := os.Open(src) - if err != nil { - log.Fatal(err) - } - defer srcFile.Close() - - if _, err := io.Copy(destFile, srcFile); err != nil { - log.Fatal(err) - } -} - // GoTool returns the command that runs a go tool. This uses go from GOROOT instead of PATH // so that go commands executed by build use the same version of Go as the 'host' that runs // build code. e.g. From c0f547ca78cbef8b025ad67596373d0974617baf Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 10:27:25 +0800 Subject: [PATCH 394/479] cmd/utils: fix bug when checking for flag value conflicts (#17803) --- cmd/utils/flags.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 18ce92a23fc8..d244803b84d3 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1318,11 +1318,14 @@ func checkExclusive(ctx *cli.Context, args ...interface{}) { if i+1 < len(args) { switch option := args[i+1].(type) { case string: - // Extended flag, expand the name and shift the arguments + // Extended flag check, make sure value set doesn't conflict with passed in option if ctx.String(flag.Names()[0]) == option { name += "=" + option + set = append(set, "--"+name) } + // shift arguments and continue i++ + continue case cli.Flag: default: From e58441b317e67547f49bfdcf578cf786aec14fd7 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 12 Dec 2024 16:00:43 +0800 Subject: [PATCH 395/479] accounts/abi: change unpacking of abi fields w/ underscores (#16513) --- accounts/abi/argument.go | 19 +++++++-------- accounts/abi/bind/bind.go | 42 ++++----------------------------- accounts/abi/reflect.go | 2 +- accounts/abi/unpack_test.go | 47 +++++++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 49 deletions(-) diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go index 5ff473f62493..2b3a8bb00cf5 100644 --- a/accounts/abi/argument.go +++ b/accounts/abi/argument.go @@ -278,21 +278,20 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) { return ret, nil } -// capitalise makes the first character of a string upper case, also removing any -// prefixing underscores from the variable names. -func capitalise(input string) string { - for len(input) > 0 && input[0] == '_' { - input = input[1:] - } - if len(input) == 0 { - return "" +// ToCamelCase converts an under-score string to a camel-case string +func ToCamelCase(input string) string { + parts := strings.Split(input, "_") + for i, s := range parts { + if len(s) > 0 { + parts[i] = strings.ToUpper(s[:1]) + s[1:] + } } - return strings.ToUpper(input[:1]) + input[1:] + return strings.Join(parts, "") } // unpackStruct extracts each argument into its corresponding struct field func unpackStruct(value, reflectValue reflect.Value, arg Argument) error { - name := capitalise(arg.Name) + name := ToCamelCase(arg.Name) typ := value.Type() for j := 0; j < typ.NumField(); j++ { // TODO read tags: `abi:"fieldName"` diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go index 0c4ba0a81e44..e2a5110af5a0 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/bind/bind.go @@ -327,48 +327,14 @@ var methodNormalizer = map[Lang]func(string) string{ // capitalise makes a camel-case string which starts with an upper case character. func capitalise(input string) string { - for len(input) > 0 && input[0] == '_' { - input = input[1:] - } - if len(input) == 0 { - return "" - } - return toCamelCase(strings.ToUpper(input[:1]) + input[1:]) + return abi.ToCamelCase(input) } // decapitalise makes a camel-case string which starts with a lower case character. func decapitalise(input string) string { - for len(input) > 0 && input[0] == '_' { - input = input[1:] - } - if len(input) == 0 { - return "" - } - return toCamelCase(strings.ToLower(input[:1]) + input[1:]) -} - -// toCamelCase converts an under-score string to a camel-case string -func toCamelCase(input string) string { - toupper := false - - result := "" - for k, v := range input { - switch { - case k == 0: - result = strings.ToUpper(string(input[0])) - - case toupper: - result += strings.ToUpper(string(v)) - toupper = false - - case v == '_': - toupper = true - - default: - result += string(v) - } - } - return result + // NOTE: This is the current behavior, it doesn't match the comment + // above and needs to be investigated. + return abi.ToCamelCase(input) } // structured checks whether a list of ABI data types has enough information to diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go index c1d411ac9560..b2755adf65fb 100644 --- a/accounts/abi/reflect.go +++ b/accounts/abi/reflect.go @@ -116,7 +116,7 @@ func requireUnpackKind(v reflect.Value, t reflect.Type, k reflect.Kind, func requireUniqueStructFieldNames(args Arguments) error { exists := make(map[string]bool) for _, arg := range args { - field := capitalise(arg.Name) + field := ToCamelCase(arg.Name) if field == "" { return errors.New("abi: purely underscored output cannot unpack to struct") } diff --git a/accounts/abi/unpack_test.go b/accounts/abi/unpack_test.go index a6a16d3f700c..5e39d0d0fbcd 100644 --- a/accounts/abi/unpack_test.go +++ b/accounts/abi/unpack_test.go @@ -273,6 +273,53 @@ var unpackTests = []unpackTest{ Int2 *big.Int }{big.NewInt(1), big.NewInt(2)}, }, + { + def: `[{"name":"int_one","type":"int256"}]`, + enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + want: struct { + IntOne *big.Int + }{big.NewInt(1)}, + }, + { + def: `[{"name":"int__one","type":"int256"}]`, + enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + want: struct { + IntOne *big.Int + }{big.NewInt(1)}, + }, + { + def: `[{"name":"int_one_","type":"int256"}]`, + enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + want: struct { + IntOne *big.Int + }{big.NewInt(1)}, + }, + { + def: `[{"name":"int_one","type":"int256"}, {"name":"intone","type":"int256"}]`, + enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + want: struct { + IntOne *big.Int + Intone *big.Int + }{big.NewInt(1), big.NewInt(2)}, + }, + { + def: `[{"name":"___","type":"int256"}]`, + enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + want: struct { + IntOne *big.Int + Intone *big.Int + }{}, + err: "abi: purely underscored output cannot unpack to struct", + }, + { + def: `[{"name":"int_one","type":"int256"},{"name":"IntOne","type":"int256"}]`, + enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + want: struct { + Int1 *big.Int + Int2 *big.Int + }{}, + err: "abi: multiple outputs mapping to the same struct field 'IntOne'", + }, { def: `[{"name":"int","type":"int256"},{"name":"Int","type":"int256"}]`, enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", From 75c14d2cdb7cbb56e5a8eb6b9ea5665fadca6416 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 12 Dec 2024 16:14:00 +0800 Subject: [PATCH 396/479] accounts/abi: fix case of generated java functions (#18372) --- accounts/abi/bind/bind.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go index e2a5110af5a0..b0fd35373fcc 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/bind/bind.go @@ -322,7 +322,7 @@ var namedType = map[Lang]func(string, abi.Type) string{ // methodNormalizer is a name transformer that modifies Solidity method names to // conform to target language naming concentions. var methodNormalizer = map[Lang]func(string) string{ - LangGo: capitalise, + LangGo: abi.ToCamelCase, } // capitalise makes a camel-case string which starts with an upper case character. @@ -332,9 +332,12 @@ func capitalise(input string) string { // decapitalise makes a camel-case string which starts with a lower case character. func decapitalise(input string) string { - // NOTE: This is the current behavior, it doesn't match the comment - // above and needs to be investigated. - return abi.ToCamelCase(input) + if len(input) == 0 { + return input + } + + goForm := abi.ToCamelCase(input) + return strings.ToLower(goForm[:1]) + goForm[1:] } // structured checks whether a list of ABI data types has enough information to From 2772e096b48d18fcdd47b0bd16f9d692b06c291b Mon Sep 17 00:00:00 2001 From: Javier Sagredo Date: Sun, 3 Feb 2019 12:41:38 +0100 Subject: [PATCH 397/479] common/compiler: fixed testSource (#18978) --- common/compiler/solidity_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/compiler/solidity_test.go b/common/compiler/solidity_test.go index 20573d73ca34..0f6f0f7ce70f 100644 --- a/common/compiler/solidity_test.go +++ b/common/compiler/solidity_test.go @@ -23,9 +23,10 @@ import ( const ( testSource = ` +pragma solidity >0.0.0; contract test { /// @notice Will multiply ` + "`a`" + ` by 7. - function multiply(uint a) returns(uint d) { + function multiply(uint a) public returns(uint d) { return a * 7; } } From f19422e1c7a7f5b74e60f9211c70cb70a69d2076 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 11 Dec 2024 17:53:29 +0800 Subject: [PATCH 398/479] cmd/utils: relinquish GC cache to read cache in archive mode (#18991) --- cmd/utils/flags.go | 2 +- common/size.go | 16 ++++++++-------- common/size_test.go | 4 ++-- core/blockchain.go | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index d244803b84d3..9c25f50df4c0 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -298,7 +298,7 @@ var ( CacheGCFlag = &cli.IntFlag{ Name: "cache-gc", Aliases: []string{"cache.gc"}, - Usage: "Percentage of cache memory allowance to use for trie pruning", + Usage: "Percentage of cache memory allowance to use for trie pruning (default = 25% full mode, 0% archive mode)", Value: 25, Category: flags.PerfCategory, } diff --git a/common/size.go b/common/size.go index bd0fc85c7dcc..6381499a4871 100644 --- a/common/size.go +++ b/common/size.go @@ -26,10 +26,10 @@ type StorageSize float64 // String implements the stringer interface. func (s StorageSize) String() string { - if s > 1000000 { - return fmt.Sprintf("%.2f mB", s/1000000) - } else if s > 1000 { - return fmt.Sprintf("%.2f kB", s/1000) + if s > 1048576 { + return fmt.Sprintf("%.2f MiB", s/1048576) + } else if s > 1024 { + return fmt.Sprintf("%.2f KiB", s/1024) } else { return fmt.Sprintf("%.2f B", s) } @@ -38,10 +38,10 @@ func (s StorageSize) String() string { // TerminalString implements log.TerminalStringer, formatting a string for console // output during logging. func (s StorageSize) TerminalString() string { - if s > 1000000 { - return fmt.Sprintf("%.2fmB", s/1000000) - } else if s > 1000 { - return fmt.Sprintf("%.2fkB", s/1000) + if s > 1048576 { + return fmt.Sprintf("%.2fMiB", s/1048576) + } else if s > 1024 { + return fmt.Sprintf("%.2fKiB", s/1024) } else { return fmt.Sprintf("%.2fB", s) } diff --git a/common/size_test.go b/common/size_test.go index f5b6c725e237..0938d483c4bb 100644 --- a/common/size_test.go +++ b/common/size_test.go @@ -25,8 +25,8 @@ func TestStorageSizeString(t *testing.T) { size StorageSize str string }{ - {2381273, "2.38 mB"}, - {2192, "2.19 kB"}, + {2381273, "2.27 MiB"}, + {2192, "2.14 KiB"}, {12, "12.00 B"}, } diff --git a/core/blockchain.go b/core/blockchain.go index 6c75b759aa9a..3c74fc6f8e84 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2123,7 +2123,7 @@ const statsReportLimit = 8 * time.Second // report prints statistics if some number of blocks have been processed // or more than a few seconds have passed since the last message. -func (st *insertStats) report(chain []*types.Block, index int, cache common.StorageSize) { +func (st *insertStats) report(chain []*types.Block, index int, dirty common.StorageSize) { // Fetch the timings for the batch var ( now = mclock.Now() @@ -2138,7 +2138,7 @@ func (st *insertStats) report(chain []*types.Block, index int, cache common.Stor context := []interface{}{ "blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000, "elapsed", common.PrettyDuration(elapsed), "mgasps", float64(st.usedGas) * 1000 / float64(elapsed), - "number", end.Number(), "hash", end.Hash(), "cache", cache, + "number", end.Number(), "hash", end.Hash(), "dirty", dirty, } if st.queued > 0 { context = append(context, []interface{}{"queued", st.queued}...) From cd1ff5d3227fea17c492d267c921e530a43c3076 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 11 Dec 2024 18:46:00 +0800 Subject: [PATCH 399/479] common: remove function Big() for type Address (#19210) --- common/types.go | 3 --- common/types_test.go | 4 ++-- core/mkalloc.go | 12 ++++++------ 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/common/types.go b/common/types.go index a33fe35bcc34..e2dd3b4b2db6 100644 --- a/common/types.go +++ b/common/types.go @@ -227,9 +227,6 @@ func (a Address) Str() string { return string(a[:]) } // Bytes gets the string representation of the underlying address. func (a Address) Bytes() []byte { return a[:] } -// Big converts an address to a big integer. -func (a Address) Big() *big.Int { return new(big.Int).SetBytes(a[:]) } - // Hash converts an address to a hash by left-padding it with zeros. func (a Address) Hash() Hash { return BytesToHash(a[:]) } diff --git a/common/types_test.go b/common/types_test.go index 7c2822854ab8..ea0fb22d2480 100644 --- a/common/types_test.go +++ b/common/types_test.go @@ -118,8 +118,8 @@ func TestAddressUnmarshalJSON(t *testing.T) { if test.ShouldErr { t.Errorf("test #%d: expected error, got none", i) } - if v.Big().Cmp(test.Output) != 0 { - t.Errorf("test #%d: address mismatch: have %v, want %v", i, v.Big(), test.Output) + if got := new(big.Int).SetBytes(v.Bytes()); got.Cmp(test.Output) != 0 { + t.Errorf("test #%d: address mismatch: have %v, want %v", i, got, test.Output) } } } diff --git a/core/mkalloc.go b/core/mkalloc.go index 6bc4e55d890a..6c8a5d0ee5e4 100644 --- a/core/mkalloc.go +++ b/core/mkalloc.go @@ -14,15 +14,14 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . +//go:build none // +build none /* +The mkalloc tool creates the genesis allocation constants in genesis_alloc.go +It outputs a const declaration that contains an RLP-encoded list of (address, balance) tuples. - The mkalloc tool creates the genesis allocation constants in genesis_alloc.go - It outputs a const declaration that contains an RLP-encoded list of (address, balance) tuples. - - go run mkalloc.go genesis.json - + go run mkalloc.go genesis.json */ package main @@ -52,7 +51,8 @@ func makelist(g *core.Genesis) allocList { if len(account.Storage) > 0 || len(account.Code) > 0 || account.Nonce != 0 { panic(fmt.Sprintf("can't encode account %x", addr)) } - a = append(a, allocItem{addr.Big(), account.Balance}) + bigAddr := new(big.Int).SetBytes(addr.Bytes()) + a = append(a, allocItem{bigAddr, account.Balance}) } sort.Sort(a) return a From 66921899e9ed2a32bd018d3fc225fb51b249e690 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 12 Dec 2024 11:36:58 +0800 Subject: [PATCH 400/479] cmd/abigen: support Vyper (#19120) --- cmd/abigen/main.go | 77 ++++++++++++++--- common/compiler/helpers.go | 64 ++++++++++++++ common/compiler/solidity.go | 70 ++++++--------- common/compiler/solidity_test.go | 6 +- common/compiler/test.v.py | 3 + common/compiler/test_bad.v.py | 3 + common/compiler/vyper.go | 144 +++++++++++++++++++++++++++++++ common/compiler/vyper_test.go | 71 +++++++++++++++ 8 files changed, 381 insertions(+), 57 deletions(-) create mode 100644 common/compiler/helpers.go create mode 100644 common/compiler/test.v.py create mode 100644 common/compiler/test_bad.v.py create mode 100644 common/compiler/vyper.go create mode 100644 common/compiler/vyper_test.go diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index 9a19efe96fca..46a9f987194b 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -19,6 +19,7 @@ package main import ( "encoding/json" "fmt" + "io" "os" "strings" @@ -59,6 +60,15 @@ var ( Usage: "Solidity compiler to use if source builds are requested", Value: "solc", } + vyFlag = &cli.StringFlag{ + Name: "vy", + Usage: "Path to the Ethereum contract Vyper source to build and bind", + } + vyperFlag = &cli.StringFlag{ + Name: "vyper", + Usage: "Vyper compiler to use if source builds are requested", + Value: "vyper", + } excFlag = &cli.StringFlag{ Name: "exc", Usage: "Comma separated types to exclude from binding", @@ -87,6 +97,8 @@ func init() { typeFlag, solFlag, solcFlag, + vyFlag, + vyperFlag, excFlag, pkgFlag, outFlag, @@ -96,11 +108,14 @@ func init() { } func abigen(c *cli.Context) error { - if c.String(abiFlag.Name) == "" && c.String(solFlag.Name) == "" { - fmt.Printf("No contract ABI (--abi) or Solidity source (--sol) specified\n") + if c.String(abiFlag.Name) == "" && c.String(solFlag.Name) == "" && c.String(vyFlag.Name) == "" { + fmt.Printf("No contract ABI (--abi), Solidity source (--sol), or Vyper source (--vy) specified\n") + os.Exit(-1) + } else if (c.String(abiFlag.Name) != "" || c.String(binFlag.Name) != "" || c.String(typeFlag.Name) != "") && (c.String(solFlag.Name) != "" || c.String(vyFlag.Name) != "") { + fmt.Printf("Contract ABI (--abi), bytecode (--bin) and type (--type) flags are mutually exclusive with the Solidity (--sol) and Vyper (--vy) flags\n") os.Exit(-1) - } else if (c.String(abiFlag.Name) != "" || c.String(binFlag.Name) != "" || c.String(typeFlag.Name) != "") && c.String(solFlag.Name) != "" { - fmt.Printf("Contract ABI (--abi), bytecode (--bin) and type (--type) flags are mutually exclusive with the Solidity source (--sol) flag\n") + } else if c.String(solFlag.Name) != "" && c.String(vyFlag.Name) == "" { + fmt.Printf("Solidity (--sol) and Vyper (--vy) flags are mutually exclusive\n") os.Exit(-1) } if c.String(pkgFlag.Name) == "" { @@ -121,23 +136,47 @@ func abigen(c *cli.Context) error { bins []string types []string ) - if c.String(solFlag.Name) != "" { + if c.String(solFlag.Name) != "" || c.String(vyFlag.Name) != "" || (c.String(abiFlag.Name) == "-" && c.String(pkgFlag.Name) == "") { // Generate the list of types to exclude from binding exclude := make(map[string]bool) for _, kind := range strings.Split(c.String(excFlag.Name), ",") { exclude[strings.ToLower(kind)] = true } - contracts, err := compiler.CompileSolidity(c.String(solcFlag.Name), c.String(solFlag.Name)) - if err != nil { - fmt.Printf("Failed to build Solidity contract: %v\n", err) - os.Exit(-1) + + var contracts map[string]*compiler.Contract + var err error + + switch { + case c.String(solFlag.Name) != "": + contracts, err = compiler.CompileSolidity(c.String(solcFlag.Name), c.String(solFlag.Name)) + if err != nil { + fmt.Printf("Failed to build Solidity contract: %v\n", err) + os.Exit(-1) + } + case c.String(vyFlag.Name) != "": + contracts, err = compiler.CompileVyper(c.String(vyperFlag.Name), c.String(vyFlag.Name)) + if err != nil { + fmt.Printf("Failed to build Vyper contract: %v\n", err) + os.Exit(-1) + } + default: + contracts, err = contractsFromStdin() + if err != nil { + fmt.Printf("Failed to read input ABIs from STDIN: %v\n", err) + os.Exit(-1) + } } + // Gather all non-excluded contract for binding for name, contract := range contracts { if exclude[strings.ToLower(name)] { continue } - abi, _ := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse + abi, err := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse + if err != nil { + fmt.Printf("Failed to parse ABIs from compiler output: %v\n", err) + os.Exit(-1) + } abis = append(abis, string(abi)) bins = append(bins, contract.Code) @@ -146,14 +185,20 @@ func abigen(c *cli.Context) error { } } else { // Otherwise load up the ABI, optional bytecode and type name from the parameters - abi, err := os.ReadFile(c.String(abiFlag.Name)) + var abi []byte + var err error + if c.String(abiFlag.Name) == "-" { + abi, err = io.ReadAll(os.Stdin) + } else { + abi, err = os.ReadFile(c.String(abiFlag.Name)) + } if err != nil { fmt.Printf("Failed to read input ABI: %v\n", err) os.Exit(-1) } abis = append(abis, string(abi)) - bin := []byte{} + var bin []byte if c.String(binFlag.Name) != "" { if bin, err = os.ReadFile(c.String(binFlag.Name)); err != nil { fmt.Printf("Failed to read input bytecode: %v\n", err) @@ -194,3 +239,11 @@ func main() { os.Exit(1) } } + +func contractsFromStdin() (map[string]*compiler.Contract, error) { + bytes, err := io.ReadAll(os.Stdin) + if err != nil { + return nil, err + } + return compiler.ParseCombinedJSON(bytes, "", "", "", "") +} diff --git a/common/compiler/helpers.go b/common/compiler/helpers.go new file mode 100644 index 000000000000..c2d4a20e3649 --- /dev/null +++ b/common/compiler/helpers.go @@ -0,0 +1,64 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package compiler wraps the Solidity and Vyper compiler executables (solc; vyper). +package compiler + +import ( + "bytes" + "os" + "regexp" +) + +var versionRegexp = regexp.MustCompile(`([0-9]+)\.([0-9]+)\.([0-9]+)`) + +// Contract contains information about a compiled contract, alongside its code and runtime code. +type Contract struct { + Code string `json:"code"` + RuntimeCode string `json:"runtime-code"` + Info ContractInfo `json:"info"` +} + +// ContractInfo contains information about a compiled contract, including access +// to the ABI definition, source mapping, user and developer docs, and metadata. +// +// Depending on the source, language version, compiler version, and compiler +// options will provide information about how the contract was compiled. +type ContractInfo struct { + Source string `json:"source"` + Language string `json:"language"` + LanguageVersion string `json:"languageVersion"` + CompilerVersion string `json:"compilerVersion"` + CompilerOptions string `json:"compilerOptions"` + SrcMap interface{} `json:"srcMap"` + SrcMapRuntime string `json:"srcMapRuntime"` + AbiDefinition interface{} `json:"abiDefinition"` + UserDoc interface{} `json:"userDoc"` + DeveloperDoc interface{} `json:"developerDoc"` + Metadata string `json:"metadata"` +} + +func slurpFiles(files []string) (string, error) { + var concat bytes.Buffer + for _, file := range files { + content, err := os.ReadFile(file) + if err != nil { + return "", err + } + concat.Write(content) + } + return concat.String(), nil +} diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go index f34020123982..7ed9c2633e53 100644 --- a/common/compiler/solidity.go +++ b/common/compiler/solidity.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// Copyright 2019 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// Package compiler wraps the Solidity compiler executable (solc). +// Package compiler wraps the Solidity and Vyper compiler executables (solc; vyper). package compiler import ( @@ -22,32 +22,11 @@ import ( "encoding/json" "errors" "fmt" - "os" "os/exec" - "regexp" "strconv" "strings" ) -var versionRegexp = regexp.MustCompile(`([0-9]+)\.([0-9]+)\.([0-9]+)`) - -type Contract struct { - Code string `json:"code"` - Info ContractInfo `json:"info"` -} - -type ContractInfo struct { - Source string `json:"source"` - Language string `json:"language"` - LanguageVersion string `json:"languageVersion"` - CompilerVersion string `json:"compilerVersion"` - CompilerOptions string `json:"compilerOptions"` - AbiDefinition interface{} `json:"abiDefinition"` - UserDoc interface{} `json:"userDoc"` - DeveloperDoc interface{} `json:"developerDoc"` - Metadata string `json:"metadata"` -} - // Solidity contains information about the solidity compiler. type Solidity struct { Path, Version, FullVersion string @@ -57,14 +36,16 @@ type Solidity struct { // --combined-output format type solcOutput struct { Contracts map[string]struct { - Bin, Abi, Devdoc, Userdoc, Metadata string + BinRuntime string `json:"bin-runtime"` + SrcMapRuntime string `json:"srcmap-runtime"` + Bin, SrcMap, Abi, Devdoc, Userdoc, Metadata string } Version string } func (s *Solidity) makeArgs() []string { p := []string{ - "--combined-json", "bin,abi,userdoc,devdoc", + "--combined-json", "bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc", "--optimize", // code optimizer switched on } if s.Major > 0 || s.Minor > 4 || s.Patch > 6 { @@ -142,8 +123,22 @@ func (s *Solidity) run(cmd *exec.Cmd, source string) (map[string]*Contract, erro if err := cmd.Run(); err != nil { return nil, fmt.Errorf("solc: %v\n%s", err, stderr.Bytes()) } + + return ParseCombinedJSON(stdout.Bytes(), source, s.Version, s.Version, strings.Join(s.makeArgs(), " ")) +} + +// ParseCombinedJSON takes the direct output of a solc --combined-output run and +// parses it into a map of string contract name to Contract structs. The +// provided source, language and compiler version, and compiler options are all +// passed through into the Contract structs. +// +// The solc output is expected to contain ABI, source mapping, user docs, and dev docs. +// +// Returns an error if the JSON is malformed or missing data, or if the JSON +// embedded within the JSON is malformed. +func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion string, compilerVersion string, compilerOptions string) (map[string]*Contract, error) { var output solcOutput - if err := json.Unmarshal(stdout.Bytes(), &output); err != nil { + if err := json.Unmarshal(combinedJSON, &output); err != nil { return nil, err } @@ -164,13 +159,16 @@ func (s *Solidity) run(cmd *exec.Cmd, source string) (map[string]*Contract, erro return nil, fmt.Errorf("solc: error reading dev doc: %v", err) } contracts[name] = &Contract{ - Code: "0x" + info.Bin, + Code: "0x" + info.Bin, + RuntimeCode: "0x" + info.BinRuntime, Info: ContractInfo{ Source: source, Language: "Solidity", - LanguageVersion: s.Version, - CompilerVersion: s.Version, - CompilerOptions: strings.Join(s.makeArgs(), " "), + LanguageVersion: languageVersion, + CompilerVersion: compilerVersion, + CompilerOptions: compilerOptions, + SrcMap: info.SrcMap, + SrcMapRuntime: info.SrcMapRuntime, AbiDefinition: abi, UserDoc: userdoc, DeveloperDoc: devdoc, @@ -180,15 +178,3 @@ func (s *Solidity) run(cmd *exec.Cmd, source string) (map[string]*Contract, erro } return contracts, nil } - -func slurpFiles(files []string) (string, error) { - var concat bytes.Buffer - for _, file := range files { - content, err := os.ReadFile(file) - if err != nil { - return "", err - } - concat.Write(content) - } - return concat.String(), nil -} diff --git a/common/compiler/solidity_test.go b/common/compiler/solidity_test.go index 0f6f0f7ce70f..bd68eaee31a2 100644 --- a/common/compiler/solidity_test.go +++ b/common/compiler/solidity_test.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// Copyright 2019 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify @@ -39,7 +39,7 @@ func skipWithoutSolc(t *testing.T) { } } -func TestCompiler(t *testing.T) { +func TestSolidityCompiler(t *testing.T) { t.SkipNow() skipWithoutSolc(t) @@ -68,7 +68,7 @@ func TestCompiler(t *testing.T) { } } -func TestCompileError(t *testing.T) { +func TestSolidityCompileError(t *testing.T) { skipWithoutSolc(t) contracts, err := CompileSolidityString("", testSource[4:]) diff --git a/common/compiler/test.v.py b/common/compiler/test.v.py new file mode 100644 index 000000000000..35af56c8f6ef --- /dev/null +++ b/common/compiler/test.v.py @@ -0,0 +1,3 @@ +@public +def test(): + hello: int128 diff --git a/common/compiler/test_bad.v.py b/common/compiler/test_bad.v.py new file mode 100644 index 000000000000..443ef7826325 --- /dev/null +++ b/common/compiler/test_bad.v.py @@ -0,0 +1,3 @@ +lic +def test(): + hello: int128 diff --git a/common/compiler/vyper.go b/common/compiler/vyper.go new file mode 100644 index 000000000000..a9bca95e5901 --- /dev/null +++ b/common/compiler/vyper.go @@ -0,0 +1,144 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package compiler wraps the Solidity and Vyper compiler executables (solc; vyper). +package compiler + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "os/exec" + "strconv" + "strings" +) + +// Vyper contains information about the vyper compiler. +type Vyper struct { + Path, Version, FullVersion string + Major, Minor, Patch int +} + +func (s *Vyper) makeArgs() []string { + p := []string{ + "-f", "combined_json", + } + return p +} + +// VyperVersion runs vyper and parses its version output. +func VyperVersion(vyper string) (*Vyper, error) { + if vyper == "" { + vyper = "vyper" + } + var out bytes.Buffer + cmd := exec.Command(vyper, "--version") + cmd.Stdout = &out + err := cmd.Run() + if err != nil { + return nil, err + } + matches := versionRegexp.FindStringSubmatch(out.String()) + if len(matches) != 4 { + return nil, fmt.Errorf("can't parse vyper version %q", out.String()) + } + s := &Vyper{Path: cmd.Path, FullVersion: out.String(), Version: matches[0]} + if s.Major, err = strconv.Atoi(matches[1]); err != nil { + return nil, err + } + if s.Minor, err = strconv.Atoi(matches[2]); err != nil { + return nil, err + } + if s.Patch, err = strconv.Atoi(matches[3]); err != nil { + return nil, err + } + return s, nil +} + +// CompileVyper compiles all given Vyper source files. +func CompileVyper(vyper string, sourcefiles ...string) (map[string]*Contract, error) { + if len(sourcefiles) == 0 { + return nil, errors.New("vyper: no source files") + } + source, err := slurpFiles(sourcefiles) + if err != nil { + return nil, err + } + s, err := VyperVersion(vyper) + if err != nil { + return nil, err + } + args := s.makeArgs() + cmd := exec.Command(s.Path, append(args, sourcefiles...)...) + return s.run(cmd, source) +} + +func (s *Vyper) run(cmd *exec.Cmd, source string) (map[string]*Contract, error) { + var stderr, stdout bytes.Buffer + cmd.Stderr = &stderr + cmd.Stdout = &stdout + if err := cmd.Run(); err != nil { + return nil, fmt.Errorf("vyper: %v\n%s", err, stderr.Bytes()) + } + + return ParseVyperJSON(stdout.Bytes(), source, s.Version, s.Version, strings.Join(s.makeArgs(), " ")) +} + +// ParseVyperJSON takes the direct output of a vyper --f combined_json run and +// parses it into a map of string contract name to Contract structs. The +// provided source, language and compiler version, and compiler options are all +// passed through into the Contract structs. +// +// The vyper output is expected to contain ABI and source mapping. +// +// Returns an error if the JSON is malformed or missing data, or if the JSON +// embedded within the JSON is malformed. +func ParseVyperJSON(combinedJSON []byte, source string, languageVersion string, compilerVersion string, compilerOptions string) (map[string]*Contract, error) { + var output map[string]interface{} + if err := json.Unmarshal(combinedJSON, &output); err != nil { + return nil, err + } + + // Compilation succeeded, assemble and return the contracts. + contracts := make(map[string]*Contract) + for name, info := range output { + // Parse the individual compilation results. + if name == "version" { + continue + } + c := info.(map[string]interface{}) + + contracts[name] = &Contract{ + Code: c["bytecode"].(string), + RuntimeCode: c["bytecode_runtime"].(string), + Info: ContractInfo{ + Source: source, + Language: "Vyper", + LanguageVersion: languageVersion, + CompilerVersion: compilerVersion, + CompilerOptions: compilerOptions, + SrcMap: c["source_map"], + SrcMapRuntime: "", + AbiDefinition: c["abi"], + UserDoc: "", + DeveloperDoc: "", + Metadata: "", + }, + } + } + return contracts, nil +} diff --git a/common/compiler/vyper_test.go b/common/compiler/vyper_test.go new file mode 100644 index 000000000000..7761c92affc5 --- /dev/null +++ b/common/compiler/vyper_test.go @@ -0,0 +1,71 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package compiler + +import ( + "os/exec" + "testing" +) + +func skipWithoutVyper(t *testing.T) { + if _, err := exec.LookPath("vyper"); err != nil { + t.Skip(err) + } +} + +func TestVyperCompiler(t *testing.T) { + skipWithoutVyper(t) + + testSource := []string{"test.v.py"} + source, err := slurpFiles(testSource) + if err != nil { + t.Error("couldn't read test files") + } + contracts, err := CompileVyper("", testSource...) + if err != nil { + t.Fatalf("error compiling test.v.py. result %v: %v", contracts, err) + } + if len(contracts) != 1 { + t.Errorf("one contract expected, got %d", len(contracts)) + } + c, ok := contracts["test.v.py"] + if !ok { + c, ok = contracts[":test"] + if !ok { + t.Fatal("info for contract 'test.v.py' not present in result") + } + } + if c.Code == "" { + t.Error("empty code") + } + if c.Info.Source != source { + t.Error("wrong source") + } + if c.Info.CompilerVersion == "" { + t.Error("empty version") + } +} + +func TestVyperCompileError(t *testing.T) { + skipWithoutVyper(t) + + contracts, err := CompileVyper("", "test_bad.v.py") + if err == nil { + t.Errorf("error expected compiling test_bad.v.py. got none. result %v", contracts) + } + t.Logf("error: %v", err) +} From a747a9861d8c16db7cfb002924d795b1a11e078a Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 12 Dec 2024 12:25:28 +0800 Subject: [PATCH 401/479] common: improve functions of StorageSize (#19244) --- common/size.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/common/size.go b/common/size.go index 6381499a4871..097b6304a8d0 100644 --- a/common/size.go +++ b/common/size.go @@ -26,7 +26,11 @@ type StorageSize float64 // String implements the stringer interface. func (s StorageSize) String() string { - if s > 1048576 { + if s > 1099511627776 { + return fmt.Sprintf("%.2f TiB", s/1099511627776) + } else if s > 1073741824 { + return fmt.Sprintf("%.2f GiB", s/1073741824) + } else if s > 1048576 { return fmt.Sprintf("%.2f MiB", s/1048576) } else if s > 1024 { return fmt.Sprintf("%.2f KiB", s/1024) @@ -38,7 +42,11 @@ func (s StorageSize) String() string { // TerminalString implements log.TerminalStringer, formatting a string for console // output during logging. func (s StorageSize) TerminalString() string { - if s > 1048576 { + if s > 1099511627776 { + return fmt.Sprintf("%.2fTiB", s/1099511627776) + } else if s > 1073741824 { + return fmt.Sprintf("%.2fGiB", s/1073741824) + } else if s > 1048576 { return fmt.Sprintf("%.2fMiB", s/1048576) } else if s > 1024 { return fmt.Sprintf("%.2fKiB", s/1024) From b711dc811da60c01d234e05141ca0ec3ec462a12 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 16:31:41 +0800 Subject: [PATCH 402/479] cmd/abigen: allow using abigen --pkg flag with standard input (#19207) --- cmd/abigen/main.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index 46a9f987194b..7a696fce418b 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -136,7 +136,7 @@ func abigen(c *cli.Context) error { bins []string types []string ) - if c.String(solFlag.Name) != "" || c.String(vyFlag.Name) != "" || (c.String(abiFlag.Name) == "-" && c.String(pkgFlag.Name) == "") { + if c.String(solFlag.Name) != "" || c.String(vyFlag.Name) != "" || c.String(abiFlag.Name) == "-" { // Generate the list of types to exclude from binding exclude := make(map[string]bool) for _, kind := range strings.Split(c.String(excFlag.Name), ",") { @@ -185,13 +185,8 @@ func abigen(c *cli.Context) error { } } else { // Otherwise load up the ABI, optional bytecode and type name from the parameters - var abi []byte - var err error - if c.String(abiFlag.Name) == "-" { - abi, err = io.ReadAll(os.Stdin) - } else { - abi, err = os.ReadFile(c.String(abiFlag.Name)) - } + abi, err := os.ReadFile(c.String(abiFlag.Name)) + if err != nil { fmt.Printf("Failed to read input ABI: %v\n", err) os.Exit(-1) From 91570dce8abe5ee960c8169494de5fe07f177366 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 12 Dec 2024 16:28:30 +0800 Subject: [PATCH 403/479] accounts/abi: fix mobile interface (#19180) --- accounts/abi/bind/bind.go | 154 +++++++-------------------------- accounts/abi/bind/bind_test.go | 2 +- 2 files changed, 30 insertions(+), 126 deletions(-) diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go index b0fd35373fcc..ca9e3e8b52b7 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/bind/bind.go @@ -161,139 +161,43 @@ var bindType = map[Lang]func(kind abi.Type) string{ LangGo: bindTypeGo, } -// Helper function for the binding generators. -// It reads the unmatched characters after the inner type-match, -// -// (since the inner type is a prefix of the total type declaration), -// looks for valid arrays (possibly a dynamic one) wrapping the inner type, -// and returns the sizes of these arrays. -// -// Returned array sizes are in the same order as solidity signatures; inner array size first. -// Array sizes may also be "", indicating a dynamic array. -func wrapArray(stringKind string, innerLen int, innerMapping string) (string, []string) { - remainder := stringKind[innerLen:] - //find all the sizes - matches := regexp.MustCompile(`\[(\d*)\]`).FindAllStringSubmatch(remainder, -1) - parts := make([]string, 0, len(matches)) - for _, match := range matches { - //get group 1 from the regex match - parts = append(parts, match[1]) - } - return innerMapping, parts -} - -// Translates the array sizes to a Go-lang declaration of a (nested) array of the inner type. -// Simply returns the inner type if arraySizes is empty. -func arrayBindingGo(inner string, arraySizes []string) string { - out := "" - //prepend all array sizes, from outer (end arraySizes) to inner (start arraySizes) - for i := len(arraySizes) - 1; i >= 0; i-- { - out += "[" + arraySizes[i] + "]" - } - out += inner - return out -} - -// bindTypeGo converts a Solidity type to a Go one. Since there is no clear mapping -// from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly -// mapped will use an upscaled type (e.g. *big.Int). -func bindTypeGo(kind abi.Type) string { - stringKind := kind.String() - innerLen, innerMapping := bindUnnestedTypeGo(stringKind) - return arrayBindingGo(wrapArray(stringKind, innerLen, innerMapping)) -} - -// The inner function of bindTypeGo, this finds the inner type of stringKind. -// (Or just the type itself if it is not an array or slice) -// The length of the matched part is returned, with the the translated type. -func bindUnnestedTypeGo(stringKind string) (int, string) { - - switch { - case strings.HasPrefix(stringKind, "address"): - return len("address"), "common.Address" - - case strings.HasPrefix(stringKind, "bytes"): - parts := regexp.MustCompile(`bytes([0-9]*)`).FindStringSubmatch(stringKind) - return len(parts[0]), fmt.Sprintf("[%s]byte", parts[1]) - - case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"): - parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(stringKind) +// bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go one. +func bindBasicTypeGo(kind abi.Type) string { + switch kind.T { + case abi.AddressTy: + return "common.Address" + case abi.IntTy, abi.UintTy: + parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String()) switch parts[2] { case "8", "16", "32", "64": - return len(parts[0]), fmt.Sprintf("%sint%s", parts[1], parts[2]) + return fmt.Sprintf("%sint%s", parts[1], parts[2]) } - return len(parts[0]), "*big.Int" - - case strings.HasPrefix(stringKind, "bool"): - return len("bool"), "bool" - - case strings.HasPrefix(stringKind, "string"): - return len("string"), "string" - + return "*big.Int" + case abi.FixedBytesTy: + return fmt.Sprintf("[%d]byte", kind.Size) + case abi.BytesTy: + return "[]byte" + case abi.FunctionTy: + // todo(rjl493456442) + return "" default: - return len(stringKind), stringKind + // string, bool types + return kind.String() } } -// Translates the array sizes to a Java declaration of a (nested) array of the inner type. -// Simply returns the inner type if arraySizes is empty. -func arrayBindingJava(inner string, arraySizes []string) string { - // Java array type declarations do not include the length. - return inner + strings.Repeat("[]", len(arraySizes)) -} - -// The inner function of bindTypeJava, this finds the inner type of stringKind. -// (Or just the type itself if it is not an array or slice) -// The length of the matched part is returned, with the the translated type. -func bindUnnestedTypeJava(stringKind string) (int, string) { - - switch { - case strings.HasPrefix(stringKind, "address"): - parts := regexp.MustCompile(`address(\[[0-9]*\])?`).FindStringSubmatch(stringKind) - if len(parts) != 2 { - return len(stringKind), stringKind - } - if parts[1] == "" { - return len("address"), "Address" - } - return len(parts[0]), "Addresses" - - case strings.HasPrefix(stringKind, "bytes"): - parts := regexp.MustCompile(`bytes([0-9]*)`).FindStringSubmatch(stringKind) - if len(parts) != 2 { - return len(stringKind), stringKind - } - return len(parts[0]), "byte[]" - - case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"): - //Note that uint and int (without digits) are also matched, - // these are size 256, and will translate to BigInt (the default). - parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(stringKind) - if len(parts) != 3 { - return len(stringKind), stringKind - } - - namedSize := map[string]string{ - "8": "byte", - "16": "short", - "32": "int", - "64": "long", - }[parts[2]] - - //default to BigInt - if namedSize == "" { - namedSize = "BigInt" - } - return len(parts[0]), namedSize - - case strings.HasPrefix(stringKind, "bool"): - return len("bool"), "boolean" - - case strings.HasPrefix(stringKind, "string"): - return len("string"), "String" - +// bindTypeGo converts solidity types to Go ones. Since there is no clear mapping +// from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly +// mapped will use an upscaled type (e.g. BigDecimal). +func bindTypeGo(kind abi.Type) string { + // todo(rjl493456442) tuple + switch kind.T { + case abi.ArrayTy: + return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem) + case abi.SliceTy: + return "[]" + bindTypeGo(*kind.Elem) default: - return len(stringKind), stringKind + return bindBasicTypeGo(kind) } } diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index 448ffe2b6acc..55116c5a1072 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -936,7 +936,7 @@ var bindTests = []struct { // Tests that packages generated by the binder can be successfully compiled and // the requested tester run against it. -func TestBindings(t *testing.T) { +func TestGolangBindings(t *testing.T) { // Skip the test if no Go command can be found gocmd := runtime.GOROOT() + "/bin/go" if !common.FileExist(gocmd) { From 7455b91800d4f1d209f0690ce7b544a219f3501e Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 12 Dec 2024 17:11:08 +0800 Subject: [PATCH 404/479] accounts/abi/bind: accept function ptr parameter (#19755) --- accounts/abi/bind/bind.go | 8 +++-- accounts/abi/bind/bind_test.go | 61 +++++++++++++++++++++++++++++++++- accounts/abi/bind/template.go | 10 ++++++ cmd/abigen/main.go | 4 ++- common/compiler/helpers.go | 7 ++-- common/compiler/solidity.go | 4 ++- 6 files changed, 85 insertions(+), 9 deletions(-) diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go index ca9e3e8b52b7..e6312657bd12 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/bind/bind.go @@ -43,7 +43,7 @@ const ( // to be used as is in client code, but rather as an intermediate struct which // enforces compile time type safety and naming convention opposed to having to // manually maintain hard coded strings that break on runtime. -func Bind(types []string, abis []string, bytecodes []string, pkg string, lang Lang) (string, error) { +func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang) (string, error) { // Process each individual contract requested binding contracts := make(map[string]*tmplContract) @@ -124,6 +124,9 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La Transacts: transacts, Events: events, } + if len(fsigs) > i { + contracts[types[i]].FuncSigs = fsigs[i] + } } // Generate the contract template data content and render it data := &tmplData{ @@ -178,8 +181,7 @@ func bindBasicTypeGo(kind abi.Type) string { case abi.BytesTy: return "[]byte" case abi.FunctionTy: - // todo(rjl493456442) - return "" + return "[24]byte" default: // string, bool types return kind.String() diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index 55116c5a1072..8a73fc8b6415 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -36,6 +36,7 @@ var bindTests = []struct { abi string imports string tester string + fsigs []map[string]string }{ // Test that the binding is available in combined and separate forms too { @@ -55,6 +56,7 @@ var bindTests = []struct { t.Fatalf("transactor binding (%v) nil or error (%v) not nil", b, nil) } `, + nil, }, // Test that all the official sample contracts bind correctly { @@ -68,6 +70,7 @@ var bindTests = []struct { t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil) } `, + nil, }, { `Crowdsale`, @@ -80,6 +83,7 @@ var bindTests = []struct { t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil) } `, + nil, }, { `DAO`, @@ -92,6 +96,7 @@ var bindTests = []struct { t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil) } `, + nil, }, // Test that named and anonymous inputs are handled correctly { @@ -125,6 +130,7 @@ var bindTests = []struct { fmt.Println(err) }`, + nil, }, // Test that named and anonymous outputs are handled correctly { @@ -161,6 +167,7 @@ var bindTests = []struct { fmt.Println(str1, str2, res.Str1, res.Str2, err) }`, + nil, }, // Tests that named, anonymous and indexed events are handled correctly { @@ -226,6 +233,7 @@ var bindTests = []struct { if _, ok := reflect.TypeOf(&EventChecker{}).MethodByName("FilterAnonymous"); ok { t.Errorf("binding has disallowed method (FilterAnonymous)") }`, + nil, }, // Test that contract interactions (deploy, transact and call) generate working code { @@ -283,6 +291,7 @@ var bindTests = []struct { t.Fatalf("Transact string mismatch: have '%s', want 'Transact string'", str) } `, + nil, }, // Tests that plain values can be properly returned and deserialized { @@ -324,6 +333,7 @@ var bindTests = []struct { t.Fatalf("Retrieved value mismatch: have %v/%v, want %v/%v", str, num, "Hi", 1) } `, + nil, }, // Tests that tuples can be properly returned and deserialized { @@ -365,6 +375,7 @@ var bindTests = []struct { t.Fatalf("Retrieved value mismatch: have %v/%v, want %v/%v", res.A, res.B, "Hi", 1) } `, + nil, }, // Tests that arrays/slices can be properly returned and deserialized. // Only addresses are tested, remainder just compiled to keep the test small. @@ -418,6 +429,7 @@ var bindTests = []struct { t.Fatalf("Slice return mismatch: have %v, want %v", out, []common.Address{auth.From, common.Address{}}) } `, + nil, }, // Tests that anonymous default methods can be correctly invoked { @@ -464,6 +476,7 @@ var bindTests = []struct { t.Fatalf("Address mismatch: have %v, want %v", caller, auth.From) } `, + nil, }, // Tests that non-existent contracts are reported as such (though only simulator test) { @@ -498,6 +511,7 @@ var bindTests = []struct { t.Fatalf("Error mismatch: have %v, want %v", err, bind.ErrNoCode) } `, + nil, }, // Tests that gas estimation works for contracts with weird gas mechanics too. { @@ -549,6 +563,7 @@ var bindTests = []struct { t.Fatalf("Field mismatch: have %v, want %v", field, "automatic") } `, + nil, }, // Test that constant functions can be called from an (optional) specified address { @@ -598,6 +613,7 @@ var bindTests = []struct { } } `, + nil, }, // Tests that methods and returns with underscores inside work correctly. { @@ -673,6 +689,7 @@ var bindTests = []struct { fmt.Println(a, b, err) `, + nil, }, // Tests that logs can be successfully filtered and decoded. { @@ -855,6 +872,7 @@ var bindTests = []struct { case <-time.After(250 * time.Millisecond): } `, + nil, }, { `DeeplyNestedArray`, @@ -931,6 +949,47 @@ var bindTests = []struct { t.Fatalf("Retrieved value does not match expected value! got: %d, expected: %d. %v", retrievedArr[4][3][2], testArr[4][3][2], err) } `, + nil, + }, + { + `CallbackParam`, + ` + contract FunctionPointerTest { + function test(function(uint256) external callback) external { + callback(1); + } + } + `, + `608060405234801561001057600080fd5b5061015e806100206000396000f3fe60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063d7a5aba214610040575b600080fd5b34801561004c57600080fd5b506100be6004803603602081101561006357600080fd5b810190808035806c0100000000000000000000000090049068010000000000000000900463ffffffff1677ffffffffffffffffffffffffffffffffffffffffffffffff169091602001919093929190939291905050506100c0565b005b818160016040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561011657600080fd5b505af115801561012a573d6000803e3d6000fd5b50505050505056fea165627a7a7230582062f87455ff84be90896dbb0c4e4ddb505c600d23089f8e80a512548440d7e2580029`, + `[ + { + "constant": false, + "inputs": [ + { + "name": "callback", + "type": "function" + } + ], + "name": "test", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ]`, + ` + "strings" + `, + ` + if strings.Compare("test(function)", CallbackParamFuncSigs["d7a5aba2"]) != 0 { + t.Fatalf("") + } + `, + []map[string]string{ + { + "test(function)": "d7a5aba2", + }, + }, }, } @@ -957,7 +1016,7 @@ func TestGolangBindings(t *testing.T) { // Generate the test suite for all the contracts for i, tt := range bindTests { // Generate the binding and create a Go source file in the workspace - bind, err := Bind([]string{tt.name}, []string{tt.abi}, []string{tt.bytecode}, "bindtest", LangGo) + bind, err := Bind([]string{tt.name}, []string{tt.abi}, []string{tt.bytecode}, tt.fsigs, "bindtest", LangGo) if err != nil { t.Fatalf("test %d: failed to generate binding: %v", i, err) } diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go index ad3cb74f9529..f65c16bdcd9c 100644 --- a/accounts/abi/bind/template.go +++ b/accounts/abi/bind/template.go @@ -29,6 +29,7 @@ type tmplContract struct { Type string // Type name of the main contract binding InputABI string // JSON ABI used as the input to generate the binding from InputBin string // Optional EVM bytecode used to denetare deploy code from + FuncSigs map[string]string // Optional map: string signature -> 4-byte signature Constructor abi.Method // Contract constructor for deploy parametrization Calls map[string]*tmplMethod // Contract calls that only read state data Transacts map[string]*tmplMethod // Contract calls that write state data @@ -91,6 +92,15 @@ var ( // {{.Type}}ABI is the input ABI used to generate the binding from. const {{.Type}}ABI = "{{.InputABI}}" + {{if $contract.FuncSigs}} + // {{.Type}}FuncSigs maps the 4-byte function signature to its string representation. + var {{.Type}}FuncSigs = map[string]string{ + {{range $strsig, $binsig := .FuncSigs}} + "{{$binsig}}": "{{$strsig}}", + {{end}} + } + {{end}} + {{if .InputBin}} // {{.Type}}Bin is the compiled bytecode used for deploying new contracts. const {{.Type}}Bin = ` + "`" + `{{.InputBin}}` + "`" + ` diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index 7a696fce418b..b6b15335540e 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -135,6 +135,7 @@ func abigen(c *cli.Context) error { abis []string bins []string types []string + sigs []map[string]string ) if c.String(solFlag.Name) != "" || c.String(vyFlag.Name) != "" || c.String(abiFlag.Name) == "-" { // Generate the list of types to exclude from binding @@ -179,6 +180,7 @@ func abigen(c *cli.Context) error { } abis = append(abis, string(abi)) bins = append(bins, contract.Code) + sigs = append(sigs, contract.Hashes) nameParts := strings.Split(name, ":") types = append(types, nameParts[len(nameParts)-1]) @@ -209,7 +211,7 @@ func abigen(c *cli.Context) error { types = append(types, kind) } // Generate the contract binding - code, err := bind.Bind(types, abis, bins, c.String(pkgFlag.Name), lang) + code, err := bind.Bind(types, abis, bins, sigs, c.String(pkgFlag.Name), lang) if err != nil { fmt.Printf("Failed to generate ABI binding: %v\n", err) os.Exit(-1) diff --git a/common/compiler/helpers.go b/common/compiler/helpers.go index c2d4a20e3649..59d242af3df6 100644 --- a/common/compiler/helpers.go +++ b/common/compiler/helpers.go @@ -27,9 +27,10 @@ var versionRegexp = regexp.MustCompile(`([0-9]+)\.([0-9]+)\.([0-9]+)`) // Contract contains information about a compiled contract, alongside its code and runtime code. type Contract struct { - Code string `json:"code"` - RuntimeCode string `json:"runtime-code"` - Info ContractInfo `json:"info"` + Code string `json:"code"` + RuntimeCode string `json:"runtime-code"` + Info ContractInfo `json:"info"` + Hashes map[string]string `json:"hashes"` } // ContractInfo contains information about a compiled contract, including access diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go index 7ed9c2633e53..56e01ee334ca 100644 --- a/common/compiler/solidity.go +++ b/common/compiler/solidity.go @@ -39,6 +39,7 @@ type solcOutput struct { BinRuntime string `json:"bin-runtime"` SrcMapRuntime string `json:"srcmap-runtime"` Bin, SrcMap, Abi, Devdoc, Userdoc, Metadata string + Hashes map[string]string } Version string } @@ -49,7 +50,7 @@ func (s *Solidity) makeArgs() []string { "--optimize", // code optimizer switched on } if s.Major > 0 || s.Minor > 4 || s.Patch > 6 { - p[1] += ",metadata" + p[1] += ",metadata,hashes" } return p } @@ -161,6 +162,7 @@ func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion strin contracts[name] = &Contract{ Code: "0x" + info.Bin, RuntimeCode: "0x" + info.BinRuntime, + Hashes: info.Hashes, Info: ContractInfo{ Source: source, Language: "Solidity", From 993bc6963eb1ff410bfe78b92153fbd8b0a0e777 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 12:03:12 +0800 Subject: [PATCH 405/479] accounts/abi/bind: link dependent libs in deploy (#19718) --- accounts/abi/bind/bind.go | 32 ++++- accounts/abi/bind/bind_test.go | 206 ++++++++++++++++++++++++++------- accounts/abi/bind/template.go | 12 +- cmd/abigen/main.go | 7 +- 4 files changed, 206 insertions(+), 51 deletions(-) diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go index e6312657bd12..92dd5ba374a5 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/bind/bind.go @@ -30,6 +30,7 @@ import ( "unicode" "github.com/XinFinOrg/XDPoSChain/accounts/abi" + "github.com/XinFinOrg/XDPoSChain/log" ) // Lang is a target programming language selector to generate bindings for. @@ -43,10 +44,13 @@ const ( // to be used as is in client code, but rather as an intermediate struct which // enforces compile time type safety and naming convention opposed to having to // manually maintain hard coded strings that break on runtime. -func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang) (string, error) { +func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string) (string, error) { // Process each individual contract requested binding contracts := make(map[string]*tmplContract) + // Map used to flag each encountered library as such + isLib := make(map[string]struct{}) + for i := 0; i < len(types); i++ { // Parse the actual ABI to generate the binding for evmABI, err := abi.JSON(strings.NewReader(abis[i])) @@ -115,23 +119,47 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string] // Append the event to the accumulator list events[original.Name] = &tmplEvent{Original: original, Normalized: normalized} } + contracts[types[i]] = &tmplContract{ Type: capitalise(types[i]), InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""), - InputBin: strings.TrimSpace(bytecodes[i]), + InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"), Constructor: evmABI.Constructor, Calls: calls, Transacts: transacts, Events: events, + Libraries: make(map[string]string), } + // Function 4-byte signatures are stored in the same sequence + // as types, if available. if len(fsigs) > i { contracts[types[i]].FuncSigs = fsigs[i] } + // Parse library references. + for pattern, name := range libs { + matched, err := regexp.Match("__\\$"+pattern+"\\$__", []byte(contracts[types[i]].InputBin)) + if err != nil { + log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err) + } + if matched { + contracts[types[i]].Libraries[pattern] = name + // keep track that this type is a library + if _, ok := isLib[name]; !ok { + isLib[name] = struct{}{} + } + } + } + } + // Check if that type has already been identified as a library + for i := 0; i < len(types); i++ { + _, ok := isLib[types[i]] + contracts[types[i]].Library = ok } // Generate the contract template data content and render it data := &tmplData{ Package: pkg, Contracts: contracts, + Libraries: libs, } buffer := new(bytes.Buffer) diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index 8a73fc8b6415..5382861f1392 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -32,18 +32,20 @@ import ( var bindTests = []struct { name string contract string - bytecode string - abi string + bytecode []string + abi []string imports string tester string fsigs []map[string]string + libs map[string]string + types []string }{ // Test that the binding is available in combined and separate forms too { `Empty`, `contract NilContract {}`, - `606060405260068060106000396000f3606060405200`, - `[]`, + []string{`606060405260068060106000396000f3606060405200`}, + []string{`[]`}, `"github.com/XinFinOrg/XDPoSChain/common"`, ` if b, err := NewEmpty(common.Address{}, nil); b == nil || err != nil { @@ -57,13 +59,15 @@ var bindTests = []struct { } `, nil, + nil, + nil, }, // Test that all the official sample contracts bind correctly { `Token`, `https://ethereum.org/token`, - `60606040526040516107fd3803806107fd83398101604052805160805160a05160c051929391820192909101600160a060020a0333166000908152600360209081526040822086905581548551838052601f6002600019610100600186161502019093169290920482018390047f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56390810193919290918801908390106100e857805160ff19168380011785555b506101189291505b8082111561017157600081556001016100b4565b50506002805460ff19168317905550505050610658806101a56000396000f35b828001600101855582156100ac579182015b828111156100ac5782518260005055916020019190600101906100fa565b50508060016000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017557805160ff19168380011785555b506100c89291506100b4565b5090565b82800160010185558215610165579182015b8281111561016557825182600050559160200191906001019061018756606060405236156100775760e060020a600035046306fdde03811461007f57806323b872dd146100dc578063313ce5671461010e57806370a082311461011a57806395d89b4114610132578063a9059cbb1461018e578063cae9ca51146101bd578063dc3080f21461031c578063dd62ed3e14610341575b610365610002565b61036760008054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156104eb5780601f106104c0576101008083540402835291602001916104eb565b6103d5600435602435604435600160a060020a038316600090815260036020526040812054829010156104f357610002565b6103e760025460ff1681565b6103d560043560036020526000908152604090205481565b610367600180546020600282841615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156104eb5780601f106104c0576101008083540402835291602001916104eb565b610365600435602435600160a060020a033316600090815260036020526040902054819010156103f157610002565b60806020604435600481810135601f8101849004909302840160405260608381526103d5948235946024803595606494939101919081908382808284375094965050505050505060006000836004600050600033600160a060020a03168152602001908152602001600020600050600087600160a060020a031681526020019081526020016000206000508190555084905080600160a060020a0316638f4ffcb1338630876040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a03168152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156102f25780820380516001836020036101000a031916815260200191505b50955050505050506000604051808303816000876161da5a03f11561000257505050509392505050565b6005602090815260043560009081526040808220909252602435815220546103d59081565b60046020818152903560009081526040808220909252602435815220546103d59081565b005b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156103c75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60408051918252519081900360200190f35b6060908152602090f35b600160a060020a03821660009081526040902054808201101561041357610002565b806003600050600033600160a060020a03168152602001908152602001600020600082828250540392505081905550806003600050600084600160a060020a0316815260200190815260200160002060008282825054019250508190555081600160a060020a031633600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a35050565b820191906000526020600020905b8154815290600101906020018083116104ce57829003601f168201915b505050505081565b600160a060020a03831681526040812054808301101561051257610002565b600160a060020a0380851680835260046020908152604080852033949094168086529382528085205492855260058252808520938552929052908220548301111561055c57610002565b816003600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816003600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816005600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054019250508190555082600160a060020a031633600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3939250505056`, - `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"},{"name":"_extraData","type":"bytes"}],"name":"approveAndCall","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"spentAllowance","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"inputs":[{"name":"initialSupply","type":"uint256"},{"name":"tokenName","type":"string"},{"name":"decimalUnits","type":"uint8"},{"name":"tokenSymbol","type":"string"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]`, + []string{`60606040526040516107fd3803806107fd83398101604052805160805160a05160c051929391820192909101600160a060020a0333166000908152600360209081526040822086905581548551838052601f6002600019610100600186161502019093169290920482018390047f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56390810193919290918801908390106100e857805160ff19168380011785555b506101189291505b8082111561017157600081556001016100b4565b50506002805460ff19168317905550505050610658806101a56000396000f35b828001600101855582156100ac579182015b828111156100ac5782518260005055916020019190600101906100fa565b50508060016000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017557805160ff19168380011785555b506100c89291506100b4565b5090565b82800160010185558215610165579182015b8281111561016557825182600050559160200191906001019061018756606060405236156100775760e060020a600035046306fdde03811461007f57806323b872dd146100dc578063313ce5671461010e57806370a082311461011a57806395d89b4114610132578063a9059cbb1461018e578063cae9ca51146101bd578063dc3080f21461031c578063dd62ed3e14610341575b610365610002565b61036760008054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156104eb5780601f106104c0576101008083540402835291602001916104eb565b6103d5600435602435604435600160a060020a038316600090815260036020526040812054829010156104f357610002565b6103e760025460ff1681565b6103d560043560036020526000908152604090205481565b610367600180546020600282841615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156104eb5780601f106104c0576101008083540402835291602001916104eb565b610365600435602435600160a060020a033316600090815260036020526040902054819010156103f157610002565b60806020604435600481810135601f8101849004909302840160405260608381526103d5948235946024803595606494939101919081908382808284375094965050505050505060006000836004600050600033600160a060020a03168152602001908152602001600020600050600087600160a060020a031681526020019081526020016000206000508190555084905080600160a060020a0316638f4ffcb1338630876040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a03168152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156102f25780820380516001836020036101000a031916815260200191505b50955050505050506000604051808303816000876161da5a03f11561000257505050509392505050565b6005602090815260043560009081526040808220909252602435815220546103d59081565b60046020818152903560009081526040808220909252602435815220546103d59081565b005b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156103c75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60408051918252519081900360200190f35b6060908152602090f35b600160a060020a03821660009081526040902054808201101561041357610002565b806003600050600033600160a060020a03168152602001908152602001600020600082828250540392505081905550806003600050600084600160a060020a0316815260200190815260200160002060008282825054019250508190555081600160a060020a031633600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a35050565b820191906000526020600020905b8154815290600101906020018083116104ce57829003601f168201915b505050505081565b600160a060020a03831681526040812054808301101561051257610002565b600160a060020a0380851680835260046020908152604080852033949094168086529382528085205492855260058252808520938552929052908220548301111561055c57610002565b816003600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816003600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816005600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054019250508190555082600160a060020a031633600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3939250505056`}, + []string{`[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"},{"name":"_extraData","type":"bytes"}],"name":"approveAndCall","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"spentAllowance","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"inputs":[{"name":"initialSupply","type":"uint256"},{"name":"tokenName","type":"string"},{"name":"decimalUnits","type":"uint8"},{"name":"tokenSymbol","type":"string"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]`}, `"github.com/XinFinOrg/XDPoSChain/common"`, ` if b, err := NewToken(common.Address{}, nil); b == nil || err != nil { @@ -71,12 +75,14 @@ var bindTests = []struct { } `, nil, + nil, + nil, }, { `Crowdsale`, `https://ethereum.org/crowdsale`, - `606060408190526007805460ff1916905560a0806105a883396101006040529051608051915160c05160e05160008054600160a060020a03199081169095178155670de0b6b3a7640000958602600155603c9093024201600355930260045560058054909216909217905561052f90819061007990396000f36060604052361561006c5760e060020a600035046301cb3b20811461008257806329dcb0cf1461014457806338af3eed1461014d5780636e66f6e91461015f5780637a3a0e84146101715780637b3e5e7b1461017a578063a035b1fe14610183578063dc0d3dff1461018c575b61020060075460009060ff161561032357610002565b61020060035460009042106103205760025460015490106103cb576002548154600160a060020a0316908290606082818181858883f150915460025460408051600160a060020a039390931683526020830191909152818101869052517fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf6945090819003909201919050a15b60405160008054600160a060020a039081169230909116319082818181858883f150506007805460ff1916600117905550505050565b6103a160035481565b6103ab600054600160a060020a031681565b6103ab600554600160a060020a031681565b6103a160015481565b6103a160025481565b6103a160045481565b6103be60043560068054829081101561000257506000526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f8101547ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d409190910154600160a060020a03919091169082565b005b505050815481101561000257906000526020600020906002020160005060008201518160000160006101000a815481600160a060020a030219169083021790555060208201518160010160005055905050806002600082828250540192505081905550600560009054906101000a9004600160a060020a0316600160a060020a031663a9059cbb3360046000505484046040518360e060020a0281526004018083600160a060020a03168152602001828152602001925050506000604051808303816000876161da5a03f11561000257505060408051600160a060020a03331681526020810184905260018183015290517fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf692509081900360600190a15b50565b5060a0604052336060908152346080819052600680546001810180835592939282908280158290116102025760020281600202836000526020600020918201910161020291905b8082111561039d57805473ffffffffffffffffffffffffffffffffffffffff19168155600060019190910190815561036a565b5090565b6060908152602090f35b600160a060020a03166060908152602090f35b6060918252608052604090f35b5b60065481101561010e576006805482908110156100025760009182526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0190600680549254600160a060020a0316928490811015610002576002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d40015460405190915082818181858883f19350505050507fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf660066000508281548110156100025760008290526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01548154600160a060020a039190911691908490811015610002576002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d40015460408051600160a060020a0394909416845260208401919091526000838201525191829003606001919050a16001016103cc56`, - `[{"constant":false,"inputs":[],"name":"checkGoalReached","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"deadline","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"beneficiary","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"tokenReward","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"fundingGoal","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"amountRaised","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"price","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"funders","outputs":[{"name":"addr","type":"address"},{"name":"amount","type":"uint256"}],"type":"function"},{"inputs":[{"name":"ifSuccessfulSendTo","type":"address"},{"name":"fundingGoalInEthers","type":"uint256"},{"name":"durationInMinutes","type":"uint256"},{"name":"etherCostOfEachToken","type":"uint256"},{"name":"addressOfTokenUsedAsReward","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"backer","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"isContribution","type":"bool"}],"name":"FundTransfer","type":"event"}]`, + []string{`606060408190526007805460ff1916905560a0806105a883396101006040529051608051915160c05160e05160008054600160a060020a03199081169095178155670de0b6b3a7640000958602600155603c9093024201600355930260045560058054909216909217905561052f90819061007990396000f36060604052361561006c5760e060020a600035046301cb3b20811461008257806329dcb0cf1461014457806338af3eed1461014d5780636e66f6e91461015f5780637a3a0e84146101715780637b3e5e7b1461017a578063a035b1fe14610183578063dc0d3dff1461018c575b61020060075460009060ff161561032357610002565b61020060035460009042106103205760025460015490106103cb576002548154600160a060020a0316908290606082818181858883f150915460025460408051600160a060020a039390931683526020830191909152818101869052517fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf6945090819003909201919050a15b60405160008054600160a060020a039081169230909116319082818181858883f150506007805460ff1916600117905550505050565b6103a160035481565b6103ab600054600160a060020a031681565b6103ab600554600160a060020a031681565b6103a160015481565b6103a160025481565b6103a160045481565b6103be60043560068054829081101561000257506000526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f8101547ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d409190910154600160a060020a03919091169082565b005b505050815481101561000257906000526020600020906002020160005060008201518160000160006101000a815481600160a060020a030219169083021790555060208201518160010160005055905050806002600082828250540192505081905550600560009054906101000a9004600160a060020a0316600160a060020a031663a9059cbb3360046000505484046040518360e060020a0281526004018083600160a060020a03168152602001828152602001925050506000604051808303816000876161da5a03f11561000257505060408051600160a060020a03331681526020810184905260018183015290517fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf692509081900360600190a15b50565b5060a0604052336060908152346080819052600680546001810180835592939282908280158290116102025760020281600202836000526020600020918201910161020291905b8082111561039d57805473ffffffffffffffffffffffffffffffffffffffff19168155600060019190910190815561036a565b5090565b6060908152602090f35b600160a060020a03166060908152602090f35b6060918252608052604090f35b5b60065481101561010e576006805482908110156100025760009182526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0190600680549254600160a060020a0316928490811015610002576002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d40015460405190915082818181858883f19350505050507fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf660066000508281548110156100025760008290526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01548154600160a060020a039190911691908490811015610002576002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d40015460408051600160a060020a0394909416845260208401919091526000838201525191829003606001919050a16001016103cc56`}, + []string{`[{"constant":false,"inputs":[],"name":"checkGoalReached","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"deadline","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"beneficiary","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"tokenReward","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"fundingGoal","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"amountRaised","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"price","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"funders","outputs":[{"name":"addr","type":"address"},{"name":"amount","type":"uint256"}],"type":"function"},{"inputs":[{"name":"ifSuccessfulSendTo","type":"address"},{"name":"fundingGoalInEthers","type":"uint256"},{"name":"durationInMinutes","type":"uint256"},{"name":"etherCostOfEachToken","type":"uint256"},{"name":"addressOfTokenUsedAsReward","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"backer","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"isContribution","type":"bool"}],"name":"FundTransfer","type":"event"}]`}, `"github.com/XinFinOrg/XDPoSChain/common"`, ` if b, err := NewCrowdsale(common.Address{}, nil); b == nil || err != nil { @@ -84,12 +90,14 @@ var bindTests = []struct { } `, nil, + nil, + nil, }, { `DAO`, `https://ethereum.org/dao`, - `606060405260405160808061145f833960e06040529051905160a05160c05160008054600160a060020a03191633179055600184815560028490556003839055600780549182018082558280158290116100b8576003028160030283600052602060002091820191016100b891906101c8565b50506060919091015160029190910155600160a060020a0381166000146100a65760008054600160a060020a031916821790555b505050506111f18061026e6000396000f35b505060408051608081018252600080825260208281018290528351908101845281815292820192909252426060820152600780549194509250811015610002579081527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6889050815181546020848101517401000000000000000000000000000000000000000002600160a060020a03199290921690921760a060020a60ff021916178255604083015180516001848101805460008281528690209195600293821615610100026000190190911692909204601f9081018390048201949192919091019083901061023e57805160ff19168380011785555b50610072929150610226565b5050600060028201556001015b8082111561023a578054600160a860020a031916815560018181018054600080835592600290821615610100026000190190911604601f81901061020c57506101bb565b601f0160209004906000526020600020908101906101bb91905b8082111561023a5760008155600101610226565b5090565b828001600101855582156101af579182015b828111156101af57825182600050559160200191906001019061025056606060405236156100b95760e060020a6000350463013cf08b81146100bb578063237e9492146101285780633910682114610281578063400e3949146102995780635daf08ca146102a257806369bd34361461032f5780638160f0b5146103385780638da5cb5b146103415780639644fcbd14610353578063aa02a90f146103be578063b1050da5146103c7578063bcca1fd3146104b5578063d3c0715b146104dc578063eceb29451461058d578063f2fde38b1461067b575b005b61069c6004356004805482908110156100025790600052602060002090600a02016000506005810154815460018301546003840154600485015460068601546007870154600160a060020a03959095169750929560020194919360ff828116946101009093041692919089565b60408051602060248035600481810135601f81018590048502860185019096528585526107759581359591946044949293909201918190840183828082843750949650505050505050600060006004600050848154811015610002575090527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19e600a8402908101547f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b909101904210806101e65750600481015460ff165b8061026757508060000160009054906101000a9004600160a060020a03168160010160005054846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f15090500193505050506040518091039020816007016000505414155b8061027757506001546005820154105b1561109257610002565b61077560043560066020526000908152604090205481565b61077560055481565b61078760043560078054829081101561000257506000526003026000805160206111d18339815191528101547fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68a820154600160a060020a0382169260a060020a90920460ff16917fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c689019084565b61077560025481565b61077560015481565b610830600054600160a060020a031681565b604080516020604435600481810135601f81018490048402850184019095528484526100b9948135946024803595939460649492939101918190840183828082843750949650505050505050600080548190600160a060020a03908116339091161461084d57610002565b61077560035481565b604080516020604435600481810135601f8101849004840285018401909552848452610775948135946024803595939460649492939101918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976084979196506024909101945090925082915084018382808284375094965050505050505033600160a060020a031660009081526006602052604081205481908114806104ab5750604081205460078054909190811015610002579082526003026000805160206111d1833981519152015460a060020a900460ff16155b15610ce557610002565b6100b960043560243560443560005433600160a060020a03908116911614610b1857610002565b604080516020604435600481810135601f810184900484028501840190955284845261077594813594602480359593946064949293910191819084018382808284375094965050505050505033600160a060020a031660009081526006602052604081205481908114806105835750604081205460078054909190811015610002579082526003026000805160206111d18339815191520181505460a060020a900460ff16155b15610f1d57610002565b604080516020606435600481810135601f81018490048402850184019095528484526107759481359460248035956044359560849492019190819084018382808284375094965050505050505060006000600460005086815481101561000257908252600a027f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b01815090508484846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f150905001935050505060405180910390208160070160005054149150610cdc565b6100b960043560005433600160a060020a03908116911614610f0857610002565b604051808a600160a060020a031681526020018981526020018060200188815260200187815260200186815260200185815260200184815260200183815260200182810382528981815460018160011615610100020316600290048152602001915080546001816001161561010002031660029004801561075e5780601f106107335761010080835404028352916020019161075e565b820191906000526020600020905b81548152906001019060200180831161074157829003601f168201915b50509a505050505050505050505060405180910390f35b60408051918252519081900360200190f35b60408051600160a060020a038616815260208101859052606081018390526080918101828152845460026001821615610100026000190190911604928201839052909160a08301908590801561081e5780601f106107f35761010080835404028352916020019161081e565b820191906000526020600020905b81548152906001019060200180831161080157829003601f168201915b50509550505050505060405180910390f35b60408051600160a060020a03929092168252519081900360200190f35b600160a060020a03851660009081526006602052604081205414156108a957604060002060078054918290556001820180825582801582901161095c5760030281600302836000526020600020918201910161095c9190610a4f565b600160a060020a03851660009081526006602052604090205460078054919350908390811015610002575060005250600381026000805160206111d183398151915201805474ff0000000000000000000000000000000000000000191660a060020a85021781555b60408051600160a060020a03871681526020810186905281517f27b022af4a8347100c7a041ce5ccf8e14d644ff05de696315196faae8cd50c9b929181900390910190a15050505050565b505050915081506080604051908101604052808681526020018581526020018481526020014281526020015060076000508381548110156100025790600052602060002090600302016000508151815460208481015160a060020a02600160a060020a03199290921690921774ff00000000000000000000000000000000000000001916178255604083015180516001848101805460008281528690209195600293821615610100026000190190911692909204601f90810183900482019491929190910190839010610ad357805160ff19168380011785555b50610b03929150610abb565b5050600060028201556001015b80821115610acf57805474ffffffffffffffffffffffffffffffffffffffffff1916815560018181018054600080835592600290821615610100026000190190911604601f819010610aa15750610a42565b601f016020900490600052602060002090810190610a4291905b80821115610acf5760008155600101610abb565b5090565b82800160010185558215610a36579182015b82811115610a36578251826000505591602001919060010190610ae5565b50506060919091015160029190910155610911565b600183905560028290556003819055604080518481526020810184905280820183905290517fa439d3fa452be5e0e1e24a8145e715f4fd8b9c08c96a42fd82a855a85e5d57de9181900360600190a1505050565b50508585846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f150905001935050505060405180910390208160070160005081905550600260005054603c024201816003016000508190555060008160040160006101000a81548160ff0219169083021790555060008160040160016101000a81548160ff02191690830217905550600081600501600050819055507f646fec02522b41e7125cfc859a64fd4f4cefd5dc3b6237ca0abe251ded1fa881828787876040518085815260200184600160a060020a03168152602001838152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015610cc45780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a1600182016005555b50949350505050565b6004805460018101808355909190828015829011610d1c57600a0281600a028360005260206000209182019101610d1c9190610db8565b505060048054929450918491508110156100025790600052602060002090600a02016000508054600160a060020a031916871781556001818101879055855160028381018054600082815260209081902096975091959481161561010002600019011691909104601f90810182900484019391890190839010610ed857805160ff19168380011785555b50610b6c929150610abb565b50506001015b80821115610acf578054600160a060020a03191681556000600182810182905560028381018054848255909281161561010002600019011604601f819010610e9c57505b5060006003830181905560048301805461ffff191690556005830181905560068301819055600783018190556008830180548282559082526020909120610db2916002028101905b80821115610acf57805474ffffffffffffffffffffffffffffffffffffffffff1916815560018181018054600080835592600290821615610100026000190190911604601f819010610eba57505b5050600101610e44565b601f016020900490600052602060002090810190610dfc9190610abb565b601f016020900490600052602060002090810190610e929190610abb565b82800160010185558215610da6579182015b82811115610da6578251826000505591602001919060010190610eea565b60008054600160a060020a0319168217905550565b600480548690811015610002576000918252600a027f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b01905033600160a060020a0316600090815260098201602052604090205490915060ff1660011415610f8457610002565b33600160a060020a031660009081526009820160205260409020805460ff1916600190811790915560058201805490910190558315610fcd576006810180546001019055610fda565b6006810180546000190190555b7fc34f869b7ff431b034b7b9aea9822dac189a685e0b015c7d1be3add3f89128e8858533866040518085815260200184815260200183600160a060020a03168152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f16801561107a5780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a1509392505050565b6006810154600354901315611158578060000160009054906101000a9004600160a060020a0316600160a060020a03168160010160005054670de0b6b3a76400000284604051808280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156111225780820380516001836020036101000a031916815260200191505b5091505060006040518083038185876185025a03f15050505060048101805460ff191660011761ff00191661010017905561116d565b60048101805460ff191660011761ff00191690555b60068101546005820154600483015460408051888152602081019490945283810192909252610100900460ff166060830152517fd220b7272a8b6d0d7d6bcdace67b936a8f175e6d5c1b3ee438b72256b32ab3af9181900360800190a1509291505056a66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688`, - `[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"proposals","outputs":[{"name":"recipient","type":"address"},{"name":"amount","type":"uint256"},{"name":"description","type":"string"},{"name":"votingDeadline","type":"uint256"},{"name":"executed","type":"bool"},{"name":"proposalPassed","type":"bool"},{"name":"numberOfVotes","type":"uint256"},{"name":"currentResult","type":"int256"},{"name":"proposalHash","type":"bytes32"}],"type":"function"},{"constant":false,"inputs":[{"name":"proposalNumber","type":"uint256"},{"name":"transactionBytecode","type":"bytes"}],"name":"executeProposal","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"memberId","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"numProposals","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"members","outputs":[{"name":"member","type":"address"},{"name":"canVote","type":"bool"},{"name":"name","type":"string"},{"name":"memberSince","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"debatingPeriodInMinutes","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"minimumQuorum","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"targetMember","type":"address"},{"name":"canVote","type":"bool"},{"name":"memberName","type":"string"}],"name":"changeMembership","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"majorityMargin","outputs":[{"name":"","type":"int256"}],"type":"function"},{"constant":false,"inputs":[{"name":"beneficiary","type":"address"},{"name":"etherAmount","type":"uint256"},{"name":"JobDescription","type":"string"},{"name":"transactionBytecode","type":"bytes"}],"name":"newProposal","outputs":[{"name":"proposalID","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"minimumQuorumForProposals","type":"uint256"},{"name":"minutesForDebate","type":"uint256"},{"name":"marginOfVotesForMajority","type":"int256"}],"name":"changeVotingRules","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"proposalNumber","type":"uint256"},{"name":"supportsProposal","type":"bool"},{"name":"justificationText","type":"string"}],"name":"vote","outputs":[{"name":"voteID","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"proposalNumber","type":"uint256"},{"name":"beneficiary","type":"address"},{"name":"etherAmount","type":"uint256"},{"name":"transactionBytecode","type":"bytes"}],"name":"checkProposalCode","outputs":[{"name":"codeChecksOut","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"type":"function"},{"inputs":[{"name":"minimumQuorumForProposals","type":"uint256"},{"name":"minutesForDebate","type":"uint256"},{"name":"marginOfVotesForMajority","type":"int256"},{"name":"congressLeader","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"recipient","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"description","type":"string"}],"name":"ProposalAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"position","type":"bool"},{"indexed":false,"name":"voter","type":"address"},{"indexed":false,"name":"justification","type":"string"}],"name":"Voted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"result","type":"int256"},{"indexed":false,"name":"quorum","type":"uint256"},{"indexed":false,"name":"active","type":"bool"}],"name":"ProposalTallied","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"member","type":"address"},{"indexed":false,"name":"isMember","type":"bool"}],"name":"MembershipChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"minimumQuorum","type":"uint256"},{"indexed":false,"name":"debatingPeriodInMinutes","type":"uint256"},{"indexed":false,"name":"majorityMargin","type":"int256"}],"name":"ChangeOfRules","type":"event"}]`, + []string{`606060405260405160808061145f833960e06040529051905160a05160c05160008054600160a060020a03191633179055600184815560028490556003839055600780549182018082558280158290116100b8576003028160030283600052602060002091820191016100b891906101c8565b50506060919091015160029190910155600160a060020a0381166000146100a65760008054600160a060020a031916821790555b505050506111f18061026e6000396000f35b505060408051608081018252600080825260208281018290528351908101845281815292820192909252426060820152600780549194509250811015610002579081527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6889050815181546020848101517401000000000000000000000000000000000000000002600160a060020a03199290921690921760a060020a60ff021916178255604083015180516001848101805460008281528690209195600293821615610100026000190190911692909204601f9081018390048201949192919091019083901061023e57805160ff19168380011785555b50610072929150610226565b5050600060028201556001015b8082111561023a578054600160a860020a031916815560018181018054600080835592600290821615610100026000190190911604601f81901061020c57506101bb565b601f0160209004906000526020600020908101906101bb91905b8082111561023a5760008155600101610226565b5090565b828001600101855582156101af579182015b828111156101af57825182600050559160200191906001019061025056606060405236156100b95760e060020a6000350463013cf08b81146100bb578063237e9492146101285780633910682114610281578063400e3949146102995780635daf08ca146102a257806369bd34361461032f5780638160f0b5146103385780638da5cb5b146103415780639644fcbd14610353578063aa02a90f146103be578063b1050da5146103c7578063bcca1fd3146104b5578063d3c0715b146104dc578063eceb29451461058d578063f2fde38b1461067b575b005b61069c6004356004805482908110156100025790600052602060002090600a02016000506005810154815460018301546003840154600485015460068601546007870154600160a060020a03959095169750929560020194919360ff828116946101009093041692919089565b60408051602060248035600481810135601f81018590048502860185019096528585526107759581359591946044949293909201918190840183828082843750949650505050505050600060006004600050848154811015610002575090527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19e600a8402908101547f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b909101904210806101e65750600481015460ff165b8061026757508060000160009054906101000a9004600160a060020a03168160010160005054846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f15090500193505050506040518091039020816007016000505414155b8061027757506001546005820154105b1561109257610002565b61077560043560066020526000908152604090205481565b61077560055481565b61078760043560078054829081101561000257506000526003026000805160206111d18339815191528101547fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68a820154600160a060020a0382169260a060020a90920460ff16917fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c689019084565b61077560025481565b61077560015481565b610830600054600160a060020a031681565b604080516020604435600481810135601f81018490048402850184019095528484526100b9948135946024803595939460649492939101918190840183828082843750949650505050505050600080548190600160a060020a03908116339091161461084d57610002565b61077560035481565b604080516020604435600481810135601f8101849004840285018401909552848452610775948135946024803595939460649492939101918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976084979196506024909101945090925082915084018382808284375094965050505050505033600160a060020a031660009081526006602052604081205481908114806104ab5750604081205460078054909190811015610002579082526003026000805160206111d1833981519152015460a060020a900460ff16155b15610ce557610002565b6100b960043560243560443560005433600160a060020a03908116911614610b1857610002565b604080516020604435600481810135601f810184900484028501840190955284845261077594813594602480359593946064949293910191819084018382808284375094965050505050505033600160a060020a031660009081526006602052604081205481908114806105835750604081205460078054909190811015610002579082526003026000805160206111d18339815191520181505460a060020a900460ff16155b15610f1d57610002565b604080516020606435600481810135601f81018490048402850184019095528484526107759481359460248035956044359560849492019190819084018382808284375094965050505050505060006000600460005086815481101561000257908252600a027f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b01815090508484846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f150905001935050505060405180910390208160070160005054149150610cdc565b6100b960043560005433600160a060020a03908116911614610f0857610002565b604051808a600160a060020a031681526020018981526020018060200188815260200187815260200186815260200185815260200184815260200183815260200182810382528981815460018160011615610100020316600290048152602001915080546001816001161561010002031660029004801561075e5780601f106107335761010080835404028352916020019161075e565b820191906000526020600020905b81548152906001019060200180831161074157829003601f168201915b50509a505050505050505050505060405180910390f35b60408051918252519081900360200190f35b60408051600160a060020a038616815260208101859052606081018390526080918101828152845460026001821615610100026000190190911604928201839052909160a08301908590801561081e5780601f106107f35761010080835404028352916020019161081e565b820191906000526020600020905b81548152906001019060200180831161080157829003601f168201915b50509550505050505060405180910390f35b60408051600160a060020a03929092168252519081900360200190f35b600160a060020a03851660009081526006602052604081205414156108a957604060002060078054918290556001820180825582801582901161095c5760030281600302836000526020600020918201910161095c9190610a4f565b600160a060020a03851660009081526006602052604090205460078054919350908390811015610002575060005250600381026000805160206111d183398151915201805474ff0000000000000000000000000000000000000000191660a060020a85021781555b60408051600160a060020a03871681526020810186905281517f27b022af4a8347100c7a041ce5ccf8e14d644ff05de696315196faae8cd50c9b929181900390910190a15050505050565b505050915081506080604051908101604052808681526020018581526020018481526020014281526020015060076000508381548110156100025790600052602060002090600302016000508151815460208481015160a060020a02600160a060020a03199290921690921774ff00000000000000000000000000000000000000001916178255604083015180516001848101805460008281528690209195600293821615610100026000190190911692909204601f90810183900482019491929190910190839010610ad357805160ff19168380011785555b50610b03929150610abb565b5050600060028201556001015b80821115610acf57805474ffffffffffffffffffffffffffffffffffffffffff1916815560018181018054600080835592600290821615610100026000190190911604601f819010610aa15750610a42565b601f016020900490600052602060002090810190610a4291905b80821115610acf5760008155600101610abb565b5090565b82800160010185558215610a36579182015b82811115610a36578251826000505591602001919060010190610ae5565b50506060919091015160029190910155610911565b600183905560028290556003819055604080518481526020810184905280820183905290517fa439d3fa452be5e0e1e24a8145e715f4fd8b9c08c96a42fd82a855a85e5d57de9181900360600190a1505050565b50508585846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f150905001935050505060405180910390208160070160005081905550600260005054603c024201816003016000508190555060008160040160006101000a81548160ff0219169083021790555060008160040160016101000a81548160ff02191690830217905550600081600501600050819055507f646fec02522b41e7125cfc859a64fd4f4cefd5dc3b6237ca0abe251ded1fa881828787876040518085815260200184600160a060020a03168152602001838152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015610cc45780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a1600182016005555b50949350505050565b6004805460018101808355909190828015829011610d1c57600a0281600a028360005260206000209182019101610d1c9190610db8565b505060048054929450918491508110156100025790600052602060002090600a02016000508054600160a060020a031916871781556001818101879055855160028381018054600082815260209081902096975091959481161561010002600019011691909104601f90810182900484019391890190839010610ed857805160ff19168380011785555b50610b6c929150610abb565b50506001015b80821115610acf578054600160a060020a03191681556000600182810182905560028381018054848255909281161561010002600019011604601f819010610e9c57505b5060006003830181905560048301805461ffff191690556005830181905560068301819055600783018190556008830180548282559082526020909120610db2916002028101905b80821115610acf57805474ffffffffffffffffffffffffffffffffffffffffff1916815560018181018054600080835592600290821615610100026000190190911604601f819010610eba57505b5050600101610e44565b601f016020900490600052602060002090810190610dfc9190610abb565b601f016020900490600052602060002090810190610e929190610abb565b82800160010185558215610da6579182015b82811115610da6578251826000505591602001919060010190610eea565b60008054600160a060020a0319168217905550565b600480548690811015610002576000918252600a027f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b01905033600160a060020a0316600090815260098201602052604090205490915060ff1660011415610f8457610002565b33600160a060020a031660009081526009820160205260409020805460ff1916600190811790915560058201805490910190558315610fcd576006810180546001019055610fda565b6006810180546000190190555b7fc34f869b7ff431b034b7b9aea9822dac189a685e0b015c7d1be3add3f89128e8858533866040518085815260200184815260200183600160a060020a03168152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f16801561107a5780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a1509392505050565b6006810154600354901315611158578060000160009054906101000a9004600160a060020a0316600160a060020a03168160010160005054670de0b6b3a76400000284604051808280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156111225780820380516001836020036101000a031916815260200191505b5091505060006040518083038185876185025a03f15050505060048101805460ff191660011761ff00191661010017905561116d565b60048101805460ff191660011761ff00191690555b60068101546005820154600483015460408051888152602081019490945283810192909252610100900460ff166060830152517fd220b7272a8b6d0d7d6bcdace67b936a8f175e6d5c1b3ee438b72256b32ab3af9181900360800190a1509291505056a66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688`}, + []string{`[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"proposals","outputs":[{"name":"recipient","type":"address"},{"name":"amount","type":"uint256"},{"name":"description","type":"string"},{"name":"votingDeadline","type":"uint256"},{"name":"executed","type":"bool"},{"name":"proposalPassed","type":"bool"},{"name":"numberOfVotes","type":"uint256"},{"name":"currentResult","type":"int256"},{"name":"proposalHash","type":"bytes32"}],"type":"function"},{"constant":false,"inputs":[{"name":"proposalNumber","type":"uint256"},{"name":"transactionBytecode","type":"bytes"}],"name":"executeProposal","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"memberId","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"numProposals","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"members","outputs":[{"name":"member","type":"address"},{"name":"canVote","type":"bool"},{"name":"name","type":"string"},{"name":"memberSince","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"debatingPeriodInMinutes","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"minimumQuorum","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"targetMember","type":"address"},{"name":"canVote","type":"bool"},{"name":"memberName","type":"string"}],"name":"changeMembership","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"majorityMargin","outputs":[{"name":"","type":"int256"}],"type":"function"},{"constant":false,"inputs":[{"name":"beneficiary","type":"address"},{"name":"etherAmount","type":"uint256"},{"name":"JobDescription","type":"string"},{"name":"transactionBytecode","type":"bytes"}],"name":"newProposal","outputs":[{"name":"proposalID","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"minimumQuorumForProposals","type":"uint256"},{"name":"minutesForDebate","type":"uint256"},{"name":"marginOfVotesForMajority","type":"int256"}],"name":"changeVotingRules","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"proposalNumber","type":"uint256"},{"name":"supportsProposal","type":"bool"},{"name":"justificationText","type":"string"}],"name":"vote","outputs":[{"name":"voteID","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"proposalNumber","type":"uint256"},{"name":"beneficiary","type":"address"},{"name":"etherAmount","type":"uint256"},{"name":"transactionBytecode","type":"bytes"}],"name":"checkProposalCode","outputs":[{"name":"codeChecksOut","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"type":"function"},{"inputs":[{"name":"minimumQuorumForProposals","type":"uint256"},{"name":"minutesForDebate","type":"uint256"},{"name":"marginOfVotesForMajority","type":"int256"},{"name":"congressLeader","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"recipient","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"description","type":"string"}],"name":"ProposalAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"position","type":"bool"},{"indexed":false,"name":"voter","type":"address"},{"indexed":false,"name":"justification","type":"string"}],"name":"Voted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"result","type":"int256"},{"indexed":false,"name":"quorum","type":"uint256"},{"indexed":false,"name":"active","type":"bool"}],"name":"ProposalTallied","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"member","type":"address"},{"indexed":false,"name":"isMember","type":"bool"}],"name":"MembershipChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"minimumQuorum","type":"uint256"},{"indexed":false,"name":"debatingPeriodInMinutes","type":"uint256"},{"indexed":false,"name":"majorityMargin","type":"int256"}],"name":"ChangeOfRules","type":"event"}]`}, `"github.com/XinFinOrg/XDPoSChain/common"`, ` if b, err := NewDAO(common.Address{}, nil); b == nil || err != nil { @@ -97,11 +105,13 @@ var bindTests = []struct { } `, nil, + nil, + nil, }, // Test that named and anonymous inputs are handled correctly { - `InputChecker`, ``, ``, - ` + `InputChecker`, ``, []string{``}, + []string{` [ {"type":"function","name":"noInput","constant":true,"inputs":[],"outputs":[]}, {"type":"function","name":"namedInput","constant":true,"inputs":[{"name":"str","type":"string"}],"outputs":[]}, @@ -110,7 +120,7 @@ var bindTests = []struct { {"type":"function","name":"anonInputs","constant":true,"inputs":[{"name":"","type":"string"},{"name":"","type":"string"}],"outputs":[]}, {"type":"function","name":"mixedInputs","constant":true,"inputs":[{"name":"","type":"string"},{"name":"str","type":"string"}],"outputs":[]} ] - `, + `}, ` "fmt" @@ -131,11 +141,13 @@ var bindTests = []struct { fmt.Println(err) }`, nil, + nil, + nil, }, // Test that named and anonymous outputs are handled correctly { - `OutputChecker`, ``, ``, - ` + `OutputChecker`, ``, []string{``}, + []string{` [ {"type":"function","name":"noOutput","constant":true,"inputs":[],"outputs":[]}, {"type":"function","name":"namedOutput","constant":true,"inputs":[],"outputs":[{"name":"str","type":"string"}]}, @@ -145,7 +157,7 @@ var bindTests = []struct { {"type":"function","name":"anonOutputs","constant":true,"inputs":[],"outputs":[{"name":"","type":"string"},{"name":"","type":"string"}]}, {"type":"function","name":"mixedOutputs","constant":true,"inputs":[],"outputs":[{"name":"","type":"string"},{"name":"str","type":"string"}]} ] - `, + `}, ` "fmt" @@ -168,11 +180,13 @@ var bindTests = []struct { fmt.Println(str1, str2, res.Str1, res.Str2, err) }`, nil, + nil, + nil, }, // Tests that named, anonymous and indexed events are handled correctly { - `EventChecker`, ``, ``, - ` + `EventChecker`, ``, []string{``}, + []string{` [ {"type":"event","name":"empty","inputs":[]}, {"type":"event","name":"indexed","inputs":[{"name":"addr","type":"address","indexed":true},{"name":"num","type":"int256","indexed":true}]}, @@ -180,7 +194,7 @@ var bindTests = []struct { {"type":"event","name":"anonymous","anonymous":true,"inputs":[]}, {"type":"event","name":"dynamic","inputs":[{"name":"idxStr","type":"string","indexed":true},{"name":"idxDat","type":"bytes","indexed":true},{"name":"str","type":"string"},{"name":"dat","type":"bytes"}]} ] - `, + `}, ` "fmt" "math/big" @@ -234,6 +248,8 @@ var bindTests = []struct { t.Errorf("binding has disallowed method (FilterAnonymous)") }`, nil, + nil, + nil, }, // Test that contract interactions (deploy, transact and call) generate working code { @@ -252,8 +268,8 @@ var bindTests = []struct { } } `, - `6060604052604051610328380380610328833981016040528051018060006000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10608d57805160ff19168380011785555b50607c9291505b8082111560ba57838155600101606b565b50505061026a806100be6000396000f35b828001600101855582156064579182015b828111156064578251826000505591602001919060010190609e565b509056606060405260e060020a60003504630d86a0e181146100315780636874e8091461008d578063d736c513146100ea575b005b610190600180546020600282841615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156102295780601f106101fe57610100808354040283529160200191610229565b61019060008054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156102295780601f106101fe57610100808354040283529160200191610229565b60206004803580820135601f81018490049093026080908101604052606084815261002f946024939192918401918190838280828437509496505050505050508060016000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061023157805160ff19168380011785555b506102619291505b808211156102665760008155830161017d565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156101f05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b820191906000526020600020905b81548152906001019060200180831161020c57829003601f168201915b505050505081565b82800160010185558215610175579182015b82811115610175578251826000505591602001919060010190610243565b505050565b509056`, - `[{"constant":true,"inputs":[],"name":"transactString","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":true,"inputs":[],"name":"deployString","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"str","type":"string"}],"name":"transact","outputs":[],"type":"function"},{"inputs":[{"name":"str","type":"string"}],"type":"constructor"}]`, + []string{`6060604052604051610328380380610328833981016040528051018060006000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10608d57805160ff19168380011785555b50607c9291505b8082111560ba57838155600101606b565b50505061026a806100be6000396000f35b828001600101855582156064579182015b828111156064578251826000505591602001919060010190609e565b509056606060405260e060020a60003504630d86a0e181146100315780636874e8091461008d578063d736c513146100ea575b005b610190600180546020600282841615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156102295780601f106101fe57610100808354040283529160200191610229565b61019060008054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156102295780601f106101fe57610100808354040283529160200191610229565b60206004803580820135601f81018490049093026080908101604052606084815261002f946024939192918401918190838280828437509496505050505050508060016000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061023157805160ff19168380011785555b506102619291505b808211156102665760008155830161017d565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156101f05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b820191906000526020600020905b81548152906001019060200180831161020c57829003601f168201915b505050505081565b82800160010185558215610175579182015b82811115610175578251826000505591602001919060010190610243565b505050565b509056`}, + []string{`[{"constant":true,"inputs":[],"name":"transactString","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":true,"inputs":[],"name":"deployString","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"str","type":"string"}],"name":"transact","outputs":[],"type":"function"},{"inputs":[{"name":"str","type":"string"}],"type":"constructor"}]`}, ` "math/big" @@ -292,6 +308,8 @@ var bindTests = []struct { } `, nil, + nil, + nil, }, // Tests that plain values can be properly returned and deserialized { @@ -303,8 +321,8 @@ var bindTests = []struct { } } `, - `606060405260dc8060106000396000f3606060405260e060020a6000350463993a04b78114601a575b005b600060605260c0604052600260809081527f486900000000000000000000000000000000000000000000000000000000000060a05260017fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060e0829052610100819052606060c0908152600261012081905281906101409060a09080838184600060046012f1505081517fffff000000000000000000000000000000000000000000000000000000000000169091525050604051610160819003945092505050f3`, - `[{"constant":true,"inputs":[],"name":"getter","outputs":[{"name":"","type":"string"},{"name":"","type":"int256"},{"name":"","type":"bytes32"}],"type":"function"}]`, + []string{`606060405260dc8060106000396000f3606060405260e060020a6000350463993a04b78114601a575b005b600060605260c0604052600260809081527f486900000000000000000000000000000000000000000000000000000000000060a05260017fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060e0829052610100819052606060c0908152600261012081905281906101409060a09080838184600060046012f1505081517fffff000000000000000000000000000000000000000000000000000000000000169091525050604051610160819003945092505050f3`}, + []string{`[{"constant":true,"inputs":[],"name":"getter","outputs":[{"name":"","type":"string"},{"name":"","type":"int256"},{"name":"","type":"bytes32"}],"type":"function"}]`}, ` "math/big" @@ -334,6 +352,8 @@ var bindTests = []struct { } `, nil, + nil, + nil, }, // Tests that tuples can be properly returned and deserialized { @@ -345,8 +365,8 @@ var bindTests = []struct { } } `, - `606060405260dc8060106000396000f3606060405260e060020a60003504633175aae28114601a575b005b600060605260c0604052600260809081527f486900000000000000000000000000000000000000000000000000000000000060a05260017fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060e0829052610100819052606060c0908152600261012081905281906101409060a09080838184600060046012f1505081517fffff000000000000000000000000000000000000000000000000000000000000169091525050604051610160819003945092505050f3`, - `[{"constant":true,"inputs":[],"name":"tuple","outputs":[{"name":"a","type":"string"},{"name":"b","type":"int256"},{"name":"c","type":"bytes32"}],"type":"function"}]`, + []string{`606060405260dc8060106000396000f3606060405260e060020a60003504633175aae28114601a575b005b600060605260c0604052600260809081527f486900000000000000000000000000000000000000000000000000000000000060a05260017fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060e0829052610100819052606060c0908152600261012081905281906101409060a09080838184600060046012f1505081517fffff000000000000000000000000000000000000000000000000000000000000169091525050604051610160819003945092505050f3`}, + []string{`[{"constant":true,"inputs":[],"name":"tuple","outputs":[{"name":"a","type":"string"},{"name":"b","type":"int256"},{"name":"c","type":"bytes32"}],"type":"function"}]`}, ` "math/big" @@ -376,6 +396,8 @@ var bindTests = []struct { } `, nil, + nil, + nil, }, // Tests that arrays/slices can be properly returned and deserialized. // Only addresses are tested, remainder just compiled to keep the test small. @@ -397,8 +419,8 @@ var bindTests = []struct { } } `, - `606060405261015c806100126000396000f3606060405260e060020a6000350463be1127a3811461003c578063d88becc014610092578063e15a3db71461003c578063f637e5891461003c575b005b604080516020600480358082013583810285810185019096528085526100ee959294602494909392850192829185019084908082843750949650505050505050604080516020810190915260009052805b919050565b604080516102e0818101909252610138916004916102e491839060179083908390808284375090955050505050506102e0604051908101604052806017905b60008152602001906001900390816100d15790505081905061008d565b60405180806020018281038252838181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050019250505060405180910390f35b60405180826102e0808381846000600461015cf15090500191505060405180910390f3`, - `[{"constant":true,"inputs":[{"name":"input","type":"address[]"}],"name":"echoAddresses","outputs":[{"name":"output","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"uint24[23]"}],"name":"echoFancyInts","outputs":[{"name":"output","type":"uint24[23]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"int256[]"}],"name":"echoInts","outputs":[{"name":"output","type":"int256[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"bool[]"}],"name":"echoBools","outputs":[{"name":"output","type":"bool[]"}],"type":"function"}]`, + []string{`606060405261015c806100126000396000f3606060405260e060020a6000350463be1127a3811461003c578063d88becc014610092578063e15a3db71461003c578063f637e5891461003c575b005b604080516020600480358082013583810285810185019096528085526100ee959294602494909392850192829185019084908082843750949650505050505050604080516020810190915260009052805b919050565b604080516102e0818101909252610138916004916102e491839060179083908390808284375090955050505050506102e0604051908101604052806017905b60008152602001906001900390816100d15790505081905061008d565b60405180806020018281038252838181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050019250505060405180910390f35b60405180826102e0808381846000600461015cf15090500191505060405180910390f3`}, + []string{`[{"constant":true,"inputs":[{"name":"input","type":"address[]"}],"name":"echoAddresses","outputs":[{"name":"output","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"uint24[23]"}],"name":"echoFancyInts","outputs":[{"name":"output","type":"uint24[23]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"int256[]"}],"name":"echoInts","outputs":[{"name":"output","type":"int256[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"bool[]"}],"name":"echoBools","outputs":[{"name":"output","type":"bool[]"}],"type":"function"}]`}, ` "math/big" "reflect" @@ -430,6 +452,8 @@ var bindTests = []struct { } `, nil, + nil, + nil, }, // Tests that anonymous default methods can be correctly invoked { @@ -443,8 +467,8 @@ var bindTests = []struct { } } `, - `6060604052606a8060106000396000f360606040523615601d5760e060020a6000350463fc9c8d3981146040575b605e6000805473ffffffffffffffffffffffffffffffffffffffff191633179055565b606060005473ffffffffffffffffffffffffffffffffffffffff1681565b005b6060908152602090f3`, - `[{"constant":true,"inputs":[],"name":"caller","outputs":[{"name":"","type":"address"}],"type":"function"}]`, + []string{`6060604052606a8060106000396000f360606040523615601d5760e060020a6000350463fc9c8d3981146040575b605e6000805473ffffffffffffffffffffffffffffffffffffffff191633179055565b606060005473ffffffffffffffffffffffffffffffffffffffff1681565b005b6060908152602090f3`}, + []string{`[{"constant":true,"inputs":[],"name":"caller","outputs":[{"name":"","type":"address"}],"type":"function"}]`}, ` "math/big" @@ -477,6 +501,8 @@ var bindTests = []struct { } `, nil, + nil, + nil, }, // Tests that non-existent contracts are reported as such (though only simulator test) { @@ -488,8 +514,8 @@ var bindTests = []struct { } } `, - `6060604052609f8060106000396000f3606060405260e060020a6000350463f97a60058114601a575b005b600060605260c0604052600d60809081527f4920646f6e27742065786973740000000000000000000000000000000000000060a052602060c0908152600d60e081905281906101009060a09080838184600060046012f15050815172ffffffffffffffffffffffffffffffffffffff1916909152505060405161012081900392509050f3`, - `[{"constant":true,"inputs":[],"name":"String","outputs":[{"name":"","type":"string"}],"type":"function"}]`, + []string{`6060604052609f8060106000396000f3606060405260e060020a6000350463f97a60058114601a575b005b600060605260c0604052600d60809081527f4920646f6e27742065786973740000000000000000000000000000000000000060a052602060c0908152600d60e081905281906101009060a09080838184600060046012f15050815172ffffffffffffffffffffffffffffffffffffff1916909152505060405161012081900392509050f3`}, + []string{`[{"constant":true,"inputs":[],"name":"String","outputs":[{"name":"","type":"string"}],"type":"function"}]`}, ` "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" @@ -512,6 +538,8 @@ var bindTests = []struct { } `, nil, + nil, + nil, }, // Tests that gas estimation works for contracts with weird gas mechanics too. { @@ -529,8 +557,8 @@ var bindTests = []struct { } } `, - `606060405261021c806100126000396000f3606060405260e060020a600035046323fcf32a81146100265780634f28bf0e1461007b575b005b6040805160206004803580820135601f8101849004840285018401909552848452610024949193602493909291840191908190840183828082843750949650505050505050620186a05a101561014e57610002565b6100db60008054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281529291908301828280156102145780601f106101e957610100808354040283529160200191610214565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561013b5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b505050565b8060006000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106101b557805160ff19168380011785555b506101499291505b808211156101e557600081556001016101a1565b82800160010185558215610199579182015b828111156101995782518260005055916020019190600101906101c7565b5090565b820191906000526020600020905b8154815290600101906020018083116101f757829003601f168201915b50505050508156`, - `[{"constant":false,"inputs":[{"name":"value","type":"string"}],"name":"SetField","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"field","outputs":[{"name":"","type":"string"}],"type":"function"}]`, + []string{`606060405261021c806100126000396000f3606060405260e060020a600035046323fcf32a81146100265780634f28bf0e1461007b575b005b6040805160206004803580820135601f8101849004840285018401909552848452610024949193602493909291840191908190840183828082843750949650505050505050620186a05a101561014e57610002565b6100db60008054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281529291908301828280156102145780601f106101e957610100808354040283529160200191610214565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561013b5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b505050565b8060006000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106101b557805160ff19168380011785555b506101499291505b808211156101e557600081556001016101a1565b82800160010185558215610199579182015b828111156101995782518260005055916020019190600101906101c7565b5090565b820191906000526020600020905b8154815290600101906020018083116101f757829003601f168201915b50505050508156`}, + []string{`[{"constant":false,"inputs":[{"name":"value","type":"string"}],"name":"SetField","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"field","outputs":[{"name":"","type":"string"}],"type":"function"}]`}, ` "math/big" @@ -564,6 +592,8 @@ var bindTests = []struct { } `, nil, + nil, + nil, }, // Test that constant functions can be called from an (optional) specified address { @@ -574,8 +604,9 @@ var bindTests = []struct { return msg.sender; } } - `, `6060604052346000575b6086806100176000396000f300606060405263ffffffff60e060020a60003504166349f8e98281146022575b6000565b34600057602c6055565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b335b905600a165627a7a72305820aef6b7685c0fa24ba6027e4870404a57df701473fe4107741805c19f5138417c0029`, - `[{"constant":true,"inputs":[],"name":"callFrom","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}]`, + `, + []string{`6060604052346000575b6086806100176000396000f300606060405263ffffffff60e060020a60003504166349f8e98281146022575b6000565b34600057602c6055565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b335b905600a165627a7a72305820aef6b7685c0fa24ba6027e4870404a57df701473fe4107741805c19f5138417c0029`}, + []string{`[{"constant":true,"inputs":[],"name":"callFrom","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}]`}, ` "math/big" @@ -614,6 +645,8 @@ var bindTests = []struct { } `, nil, + nil, + nil, }, // Tests that methods and returns with underscores inside work correctly. { @@ -645,8 +678,9 @@ var bindTests = []struct { return 0; } } - `, `6060604052341561000f57600080fd5b6103858061001e6000396000f30060606040526004361061008e576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806303a592131461009357806346546dbe146100c357806367e6633d146100ec5780639df4848514610181578063af7486ab146101b1578063b564b34d146101e1578063e02ab24d14610211578063e409ca4514610241575b600080fd5b341561009e57600080fd5b6100a6610271565b604051808381526020018281526020019250505060405180910390f35b34156100ce57600080fd5b6100d6610286565b6040518082815260200191505060405180910390f35b34156100f757600080fd5b6100ff61028e565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561014557808201518184015260208101905061012a565b50505050905090810190601f1680156101725780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b341561018c57600080fd5b6101946102dc565b604051808381526020018281526020019250505060405180910390f35b34156101bc57600080fd5b6101c46102f1565b604051808381526020018281526020019250505060405180910390f35b34156101ec57600080fd5b6101f4610306565b604051808381526020018281526020019250505060405180910390f35b341561021c57600080fd5b61022461031b565b604051808381526020018281526020019250505060405180910390f35b341561024c57600080fd5b610254610330565b604051808381526020018281526020019250505060405180910390f35b60008060016002819150809050915091509091565b600080905090565b6000610298610345565b61013a8090506040805190810160405280600281526020017f7069000000000000000000000000000000000000000000000000000000000000815250915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b6020604051908101604052806000815250905600a165627a7a72305820d1a53d9de9d1e3d55cb3dc591900b63c4f1ded79114f7b79b332684840e186a40029`, - `[{"constant":true,"inputs":[],"name":"LowerUpperCollision","outputs":[{"name":"_res","type":"int256"},{"name":"Res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_under_scored_func","outputs":[{"name":"_int","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UnderscoredOutput","outputs":[{"name":"_int","type":"int256"},{"name":"_string","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"PurelyUnderscoredOutput","outputs":[{"name":"_","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UpperLowerCollision","outputs":[{"name":"_Res","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"AllPurelyUnderscoredOutput","outputs":[{"name":"_","type":"int256"},{"name":"__","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UpperUpperCollision","outputs":[{"name":"_Res","type":"int256"},{"name":"Res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"LowerLowerCollision","outputs":[{"name":"_res","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"}]`, + `, + []string{`6060604052341561000f57600080fd5b6103858061001e6000396000f30060606040526004361061008e576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806303a592131461009357806346546dbe146100c357806367e6633d146100ec5780639df4848514610181578063af7486ab146101b1578063b564b34d146101e1578063e02ab24d14610211578063e409ca4514610241575b600080fd5b341561009e57600080fd5b6100a6610271565b604051808381526020018281526020019250505060405180910390f35b34156100ce57600080fd5b6100d6610286565b6040518082815260200191505060405180910390f35b34156100f757600080fd5b6100ff61028e565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561014557808201518184015260208101905061012a565b50505050905090810190601f1680156101725780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b341561018c57600080fd5b6101946102dc565b604051808381526020018281526020019250505060405180910390f35b34156101bc57600080fd5b6101c46102f1565b604051808381526020018281526020019250505060405180910390f35b34156101ec57600080fd5b6101f4610306565b604051808381526020018281526020019250505060405180910390f35b341561021c57600080fd5b61022461031b565b604051808381526020018281526020019250505060405180910390f35b341561024c57600080fd5b610254610330565b604051808381526020018281526020019250505060405180910390f35b60008060016002819150809050915091509091565b600080905090565b6000610298610345565b61013a8090506040805190810160405280600281526020017f7069000000000000000000000000000000000000000000000000000000000000815250915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b6020604051908101604052806000815250905600a165627a7a72305820d1a53d9de9d1e3d55cb3dc591900b63c4f1ded79114f7b79b332684840e186a40029`}, + []string{`[{"constant":true,"inputs":[],"name":"LowerUpperCollision","outputs":[{"name":"_res","type":"int256"},{"name":"Res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_under_scored_func","outputs":[{"name":"_int","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UnderscoredOutput","outputs":[{"name":"_int","type":"int256"},{"name":"_string","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"PurelyUnderscoredOutput","outputs":[{"name":"_","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UpperLowerCollision","outputs":[{"name":"_Res","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"AllPurelyUnderscoredOutput","outputs":[{"name":"_","type":"int256"},{"name":"__","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UpperUpperCollision","outputs":[{"name":"_Res","type":"int256"},{"name":"Res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"LowerLowerCollision","outputs":[{"name":"_res","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"}]`}, ` "fmt" "math/big" @@ -690,6 +724,8 @@ var bindTests = []struct { fmt.Println(a, b, err) `, nil, + nil, + nil, }, // Tests that logs can be successfully filtered and decoded. { @@ -726,8 +762,8 @@ var bindTests = []struct { } } `, - `6060604052341561000f57600080fd5b61042c8061001e6000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063528300ff1461005c578063630c31e2146100fc578063c7d116dd14610156575b600080fd5b341561006757600080fd5b6100fa600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610194565b005b341561010757600080fd5b610154600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035600019169060200190919080351515906020019091908035906020019091905050610367565b005b341561016157600080fd5b610192600480803590602001909190803560010b90602001909190803563ffffffff169060200190919050506103c3565b005b806040518082805190602001908083835b6020831015156101ca57805182526020820191506020810190506020830392506101a5565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020826040518082805190602001908083835b60208310151561022d5780518252602082019150602081019050602083039250610208565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f3281fd4f5e152dd3385df49104a3f633706e21c9e80672e88d3bcddf33101f008484604051808060200180602001838103835285818151815260200191508051906020019080838360005b838110156102c15780820151818401526020810190506102a6565b50505050905090810190601f1680156102ee5780820380516001836020036101000a031916815260200191505b50838103825284818151815260200191508051906020019080838360005b8381101561032757808201518184015260208101905061030c565b50505050905090810190601f1680156103545780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a35050565b81151583600019168573ffffffffffffffffffffffffffffffffffffffff167f1f097de4289df643bd9c11011cc61367aa12983405c021056e706eb5ba1250c8846040518082815260200191505060405180910390a450505050565b8063ffffffff168260010b847f3ca7f3a77e5e6e15e781850bc82e32adfa378a2a609370db24b4d0fae10da2c960405160405180910390a45050505600a165627a7a72305820d1f8a8bbddbc5bb29f285891d6ae1eef8420c52afdc05e1573f6114d8e1714710029`, - `[{"constant":false,"inputs":[{"name":"str","type":"string"},{"name":"blob","type":"bytes"}],"name":"raiseDynamicEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"id","type":"bytes32"},{"name":"flag","type":"bool"},{"name":"value","type":"uint256"}],"name":"raiseSimpleEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"number","type":"uint256"},{"name":"short","type":"int16"},{"name":"long","type":"uint32"}],"name":"raiseNodataEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"Addr","type":"address"},{"indexed":true,"name":"Id","type":"bytes32"},{"indexed":true,"name":"Flag","type":"bool"},{"indexed":false,"name":"Value","type":"uint256"}],"name":"SimpleEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"Number","type":"uint256"},{"indexed":true,"name":"Short","type":"int16"},{"indexed":true,"name":"Long","type":"uint32"}],"name":"NodataEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"IndexedString","type":"string"},{"indexed":true,"name":"IndexedBytes","type":"bytes"},{"indexed":false,"name":"NonIndexedString","type":"string"},{"indexed":false,"name":"NonIndexedBytes","type":"bytes"}],"name":"DynamicEvent","type":"event"}]`, + []string{`6060604052341561000f57600080fd5b61042c8061001e6000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063528300ff1461005c578063630c31e2146100fc578063c7d116dd14610156575b600080fd5b341561006757600080fd5b6100fa600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610194565b005b341561010757600080fd5b610154600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035600019169060200190919080351515906020019091908035906020019091905050610367565b005b341561016157600080fd5b610192600480803590602001909190803560010b90602001909190803563ffffffff169060200190919050506103c3565b005b806040518082805190602001908083835b6020831015156101ca57805182526020820191506020810190506020830392506101a5565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020826040518082805190602001908083835b60208310151561022d5780518252602082019150602081019050602083039250610208565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f3281fd4f5e152dd3385df49104a3f633706e21c9e80672e88d3bcddf33101f008484604051808060200180602001838103835285818151815260200191508051906020019080838360005b838110156102c15780820151818401526020810190506102a6565b50505050905090810190601f1680156102ee5780820380516001836020036101000a031916815260200191505b50838103825284818151815260200191508051906020019080838360005b8381101561032757808201518184015260208101905061030c565b50505050905090810190601f1680156103545780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a35050565b81151583600019168573ffffffffffffffffffffffffffffffffffffffff167f1f097de4289df643bd9c11011cc61367aa12983405c021056e706eb5ba1250c8846040518082815260200191505060405180910390a450505050565b8063ffffffff168260010b847f3ca7f3a77e5e6e15e781850bc82e32adfa378a2a609370db24b4d0fae10da2c960405160405180910390a45050505600a165627a7a72305820d1f8a8bbddbc5bb29f285891d6ae1eef8420c52afdc05e1573f6114d8e1714710029`}, + []string{`[{"constant":false,"inputs":[{"name":"str","type":"string"},{"name":"blob","type":"bytes"}],"name":"raiseDynamicEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"id","type":"bytes32"},{"name":"flag","type":"bool"},{"name":"value","type":"uint256"}],"name":"raiseSimpleEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"number","type":"uint256"},{"name":"short","type":"int16"},{"name":"long","type":"uint32"}],"name":"raiseNodataEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"Addr","type":"address"},{"indexed":true,"name":"Id","type":"bytes32"},{"indexed":true,"name":"Flag","type":"bool"},{"indexed":false,"name":"Value","type":"uint256"}],"name":"SimpleEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"Number","type":"uint256"},{"indexed":true,"name":"Short","type":"int16"},{"indexed":true,"name":"Long","type":"uint32"}],"name":"NodataEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"IndexedString","type":"string"},{"indexed":true,"name":"IndexedBytes","type":"bytes"},{"indexed":false,"name":"NonIndexedString","type":"string"},{"indexed":false,"name":"NonIndexedBytes","type":"bytes"}],"name":"DynamicEvent","type":"event"}]`}, ` "math/big" "time" @@ -873,6 +909,8 @@ var bindTests = []struct { } `, nil, + nil, + nil, }, { `DeeplyNestedArray`, @@ -887,8 +925,8 @@ var bindTests = []struct { } } `, - `6060604052341561000f57600080fd5b6106438061001e6000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063344248551461005c5780638ed4573a1461011457806398ed1856146101ab575b600080fd5b341561006757600080fd5b610112600480806107800190600580602002604051908101604052809291906000905b828210156101055783826101800201600480602002604051908101604052809291906000905b828210156100f25783826060020160038060200260405190810160405280929190826003602002808284378201915050505050815260200190600101906100b0565b505050508152602001906001019061008a565b5050505091905050610208565b005b341561011f57600080fd5b61012761021d565b604051808260056000925b8184101561019b578284602002015160046000925b8184101561018d5782846020020151600360200280838360005b8381101561017c578082015181840152602081019050610161565b505050509050019260010192610147565b925050509260010192610132565b9250505091505060405180910390f35b34156101b657600080fd5b6101de6004808035906020019091908035906020019091908035906020019091905050610309565b604051808267ffffffffffffffff1667ffffffffffffffff16815260200191505060405180910390f35b80600090600561021992919061035f565b5050565b6102256103b0565b6000600580602002604051908101604052809291906000905b8282101561030057838260040201600480602002604051908101604052809291906000905b828210156102ed578382016003806020026040519081016040528092919082600380156102d9576020028201916000905b82829054906101000a900467ffffffffffffffff1667ffffffffffffffff16815260200190600801906020826007010492830192600103820291508084116102945790505b505050505081526020019060010190610263565b505050508152602001906001019061023e565b50505050905090565b60008360058110151561031857fe5b600402018260048110151561032957fe5b018160038110151561033757fe5b6004918282040191900660080292509250509054906101000a900467ffffffffffffffff1681565b826005600402810192821561039f579160200282015b8281111561039e5782518290600461038e9291906103df565b5091602001919060040190610375565b5b5090506103ac919061042d565b5090565b610780604051908101604052806005905b6103c9610459565b8152602001906001900390816103c15790505090565b826004810192821561041c579160200282015b8281111561041b5782518290600361040b929190610488565b50916020019190600101906103f2565b5b5090506104299190610536565b5090565b61045691905b8082111561045257600081816104499190610562565b50600401610433565b5090565b90565b610180604051908101604052806004905b6104726105a7565b81526020019060019003908161046a5790505090565b82600380016004900481019282156105255791602002820160005b838211156104ef57835183826101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555092602001926008016020816007010492830192600103026104a3565b80156105235782816101000a81549067ffffffffffffffff02191690556008016020816007010492830192600103026104ef565b505b50905061053291906105d9565b5090565b61055f91905b8082111561055b57600081816105529190610610565b5060010161053c565b5090565b90565b50600081816105719190610610565b50600101600081816105839190610610565b50600101600081816105959190610610565b5060010160006105a59190610610565b565b6060604051908101604052806003905b600067ffffffffffffffff168152602001906001900390816105b75790505090565b61060d91905b8082111561060957600081816101000a81549067ffffffffffffffff0219169055506001016105df565b5090565b90565b50600090555600a165627a7a7230582087e5a43f6965ab6ef7a4ff056ab80ed78fd8c15cff57715a1bf34ec76a93661c0029`, - `[{"constant":false,"inputs":[{"name":"arr","type":"uint64[3][4][5]"}],"name":"storeDeepUintArray","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"retrieveDeepArray","outputs":[{"name":"","type":"uint64[3][4][5]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"name":"deepUint64Array","outputs":[{"name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"}]`, + []string{`6060604052341561000f57600080fd5b6106438061001e6000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063344248551461005c5780638ed4573a1461011457806398ed1856146101ab575b600080fd5b341561006757600080fd5b610112600480806107800190600580602002604051908101604052809291906000905b828210156101055783826101800201600480602002604051908101604052809291906000905b828210156100f25783826060020160038060200260405190810160405280929190826003602002808284378201915050505050815260200190600101906100b0565b505050508152602001906001019061008a565b5050505091905050610208565b005b341561011f57600080fd5b61012761021d565b604051808260056000925b8184101561019b578284602002015160046000925b8184101561018d5782846020020151600360200280838360005b8381101561017c578082015181840152602081019050610161565b505050509050019260010192610147565b925050509260010192610132565b9250505091505060405180910390f35b34156101b657600080fd5b6101de6004808035906020019091908035906020019091908035906020019091905050610309565b604051808267ffffffffffffffff1667ffffffffffffffff16815260200191505060405180910390f35b80600090600561021992919061035f565b5050565b6102256103b0565b6000600580602002604051908101604052809291906000905b8282101561030057838260040201600480602002604051908101604052809291906000905b828210156102ed578382016003806020026040519081016040528092919082600380156102d9576020028201916000905b82829054906101000a900467ffffffffffffffff1667ffffffffffffffff16815260200190600801906020826007010492830192600103820291508084116102945790505b505050505081526020019060010190610263565b505050508152602001906001019061023e565b50505050905090565b60008360058110151561031857fe5b600402018260048110151561032957fe5b018160038110151561033757fe5b6004918282040191900660080292509250509054906101000a900467ffffffffffffffff1681565b826005600402810192821561039f579160200282015b8281111561039e5782518290600461038e9291906103df565b5091602001919060040190610375565b5b5090506103ac919061042d565b5090565b610780604051908101604052806005905b6103c9610459565b8152602001906001900390816103c15790505090565b826004810192821561041c579160200282015b8281111561041b5782518290600361040b929190610488565b50916020019190600101906103f2565b5b5090506104299190610536565b5090565b61045691905b8082111561045257600081816104499190610562565b50600401610433565b5090565b90565b610180604051908101604052806004905b6104726105a7565b81526020019060019003908161046a5790505090565b82600380016004900481019282156105255791602002820160005b838211156104ef57835183826101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555092602001926008016020816007010492830192600103026104a3565b80156105235782816101000a81549067ffffffffffffffff02191690556008016020816007010492830192600103026104ef565b505b50905061053291906105d9565b5090565b61055f91905b8082111561055b57600081816105529190610610565b5060010161053c565b5090565b90565b50600081816105719190610610565b50600101600081816105839190610610565b50600101600081816105959190610610565b5060010160006105a59190610610565b565b6060604051908101604052806003905b600067ffffffffffffffff168152602001906001900390816105b75790505090565b61060d91905b8082111561060957600081816101000a81549067ffffffffffffffff0219169055506001016105df565b5090565b90565b50600090555600a165627a7a7230582087e5a43f6965ab6ef7a4ff056ab80ed78fd8c15cff57715a1bf34ec76a93661c0029`}, + []string{`[{"constant":false,"inputs":[{"name":"arr","type":"uint64[3][4][5]"}],"name":"storeDeepUintArray","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"retrieveDeepArray","outputs":[{"name":"","type":"uint64[3][4][5]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"name":"deepUint64Array","outputs":[{"name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"}]`}, ` "math/big" @@ -950,6 +988,8 @@ var bindTests = []struct { } `, nil, + nil, + nil, }, { `CallbackParam`, @@ -960,8 +1000,8 @@ var bindTests = []struct { } } `, - `608060405234801561001057600080fd5b5061015e806100206000396000f3fe60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063d7a5aba214610040575b600080fd5b34801561004c57600080fd5b506100be6004803603602081101561006357600080fd5b810190808035806c0100000000000000000000000090049068010000000000000000900463ffffffff1677ffffffffffffffffffffffffffffffffffffffffffffffff169091602001919093929190939291905050506100c0565b005b818160016040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561011657600080fd5b505af115801561012a573d6000803e3d6000fd5b50505050505056fea165627a7a7230582062f87455ff84be90896dbb0c4e4ddb505c600d23089f8e80a512548440d7e2580029`, - `[ + []string{`608060405234801561001057600080fd5b5061015e806100206000396000f3fe60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063d7a5aba214610040575b600080fd5b34801561004c57600080fd5b506100be6004803603602081101561006357600080fd5b810190808035806c0100000000000000000000000090049068010000000000000000900463ffffffff1677ffffffffffffffffffffffffffffffffffffffffffffffff169091602001919093929190939291905050506100c0565b005b818160016040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561011657600080fd5b505af115801561012a573d6000803e3d6000fd5b50505050505056fea165627a7a7230582062f87455ff84be90896dbb0c4e4ddb505c600d23089f8e80a512548440d7e2580029`}, + []string{`[ { "constant": false, "inputs": [ @@ -976,7 +1016,7 @@ var bindTests = []struct { "stateMutability": "nonpayable", "type": "function" } - ]`, + ]`}, ` "strings" `, @@ -990,6 +1030,76 @@ var bindTests = []struct { "test(function)": "d7a5aba2", }, }, + nil, + nil, + }, + { + `UseLibrary`, + ` + library Math { + function add(uint a, uint b) public view returns(uint) { + return a + b; + } + } + + contract UseLibrary { + function add (uint c, uint d) public view returns(uint) { + return Math.add(c,d); + } + } + `, + []string{ + // Bytecode for the UseLibrary contract + `608060405234801561001057600080fd5b5061011d806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063771602f714602d575b600080fd5b604d60048036036040811015604157600080fd5b5080359060200135605f565b60408051918252519081900360200190f35b600073__$b98c933f0a6ececcd167bd4f9d3299b1a0$__63771602f784846040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801560b757600080fd5b505af415801560ca573d6000803e3d6000fd5b505050506040513d602081101560df57600080fd5b5051939250505056fea265627a7a72305820eb5c38f42445604cfa43d85e3aa5ecc48b0a646456c902dd48420ae7241d06f664736f6c63430005090032`, + // Bytecode for the Math contract + `60a3610024600b82828239805160001a607314601757fe5b30600052607381538281f3fe730000000000000000000000000000000000000000301460806040526004361060335760003560e01c8063771602f7146038575b600080fd5b605860048036036040811015604c57600080fd5b5080359060200135606a565b60408051918252519081900360200190f35b019056fea265627a7a723058206fc6c05f3078327f9c763edffdb5ab5f8bd212e293a1306c7d0ad05af3ad35f464736f6c63430005090032`, + }, + []string{ + `[{"constant":true,"inputs":[{"name":"c","type":"uint256"},{"name":"d","type":"uint256"}],"name":"add","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]`, + `[{"constant":true,"inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"}],"name":"add","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]`, + }, + ` + "math/big" + + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" + "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/params" + `, + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() + auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) + sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig) + + // Deploy the test contract + _, _, testContract, err := DeployUseLibrary(auth, sim) + if err != nil { + t.Fatalf("Failed to deploy test contract: %v", err) + } + + // Finish deploy. + sim.Commit() + + // Check that the library contract has been deployed + // by calling the contract's add function. + res, err := testContract.Add(&bind.CallOpts{ + From: auth.From, + Pending: false, + }, big.NewInt(1), big.NewInt(2)) + if err != nil { + t.Fatalf("Failed to call linked contract: %v", err) + } + if res.Cmp(big.NewInt(3)) != 0 { + t.Fatalf("Add did not return the correct result: %d != %d", res, 3) + } + `, + nil, + map[string]string{ + "b98c933f0a6ececcd167bd4f9d3299b1a0": "Math", + }, + []string{"UseLibrary", "Math"}, }, } @@ -1015,8 +1125,14 @@ func TestGolangBindings(t *testing.T) { } // Generate the test suite for all the contracts for i, tt := range bindTests { + var types []string + if tt.types != nil { + types = tt.types + } else { + types = []string{tt.name} + } // Generate the binding and create a Go source file in the workspace - bind, err := Bind([]string{tt.name}, []string{tt.abi}, []string{tt.bytecode}, tt.fsigs, "bindtest", LangGo) + bind, err := Bind(types, tt.abi, tt.bytecode, tt.fsigs, "bindtest", LangGo, tt.libs) if err != nil { t.Fatalf("test %d: failed to generate binding: %v", i, err) } diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go index f65c16bdcd9c..10e8ae9f0579 100644 --- a/accounts/abi/bind/template.go +++ b/accounts/abi/bind/template.go @@ -22,6 +22,7 @@ import "github.com/XinFinOrg/XDPoSChain/accounts/abi" type tmplData struct { Package string // Name of the package to place the generated file in Contracts map[string]*tmplContract // List of contracts to generate into this file + Libraries map[string]string // Map the bytecode's link pattern to the library name } // tmplContract contains the data needed to generate an individual contract binding. @@ -34,6 +35,8 @@ type tmplContract struct { Calls map[string]*tmplMethod // Contract calls that only read state data Transacts map[string]*tmplMethod // Contract calls that write state data Events map[string]*tmplEvent // Contract events accessors + Libraries map[string]string // Same as tmplData, but filtered to only keep what the contract needs + Library bool } // tmplMethod is a wrapper around an abi.Method that contains a few preprocessed @@ -95,15 +98,14 @@ var ( {{if $contract.FuncSigs}} // {{.Type}}FuncSigs maps the 4-byte function signature to its string representation. var {{.Type}}FuncSigs = map[string]string{ - {{range $strsig, $binsig := .FuncSigs}} - "{{$binsig}}": "{{$strsig}}", + {{range $strsig, $binsig := .FuncSigs}}"{{$binsig}}": "{{$strsig}}", {{end}} } {{end}} {{if .InputBin}} // {{.Type}}Bin is the compiled bytecode used for deploying new contracts. - const {{.Type}}Bin = ` + "`" + `{{.InputBin}}` + "`" + ` + var {{.Type}}Bin = "0x{{.InputBin}}" // Deploy{{.Type}} deploys a new Ethereum contract, binding an instance of {{.Type}} to it. func Deploy{{.Type}}(auth *bind.TransactOpts, backend bind.ContractBackend {{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type}}{{end}}) (common.Address, *types.Transaction, *{{.Type}}, error) { @@ -111,6 +113,10 @@ var ( if err != nil { return common.Address{}, nil, nil, err } + {{range $pattern, $name := .Libraries}} + {{decapitalise $name}}Addr, _, _, _ := Deploy{{capitalise $name}}(auth, backend) + {{$contract.Type}}Bin = strings.Replace({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:], -1) + {{end}} address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}}) if err != nil { return common.Address{}, nil, nil, err diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index b6b15335540e..1c4a2aa06980 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -25,6 +25,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" "github.com/XinFinOrg/XDPoSChain/common/compiler" + "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/internal/flags" "github.com/XinFinOrg/XDPoSChain/log" "github.com/urfave/cli/v2" @@ -136,6 +137,7 @@ func abigen(c *cli.Context) error { bins []string types []string sigs []map[string]string + libs = make(map[string]string) ) if c.String(solFlag.Name) != "" || c.String(vyFlag.Name) != "" || c.String(abiFlag.Name) == "-" { // Generate the list of types to exclude from binding @@ -184,6 +186,9 @@ func abigen(c *cli.Context) error { nameParts := strings.Split(name, ":") types = append(types, nameParts[len(nameParts)-1]) + + libPattern := crypto.Keccak256Hash([]byte(name)).String()[2:36] + libs[libPattern] = nameParts[len(nameParts)-1] } } else { // Otherwise load up the ABI, optional bytecode and type name from the parameters @@ -211,7 +216,7 @@ func abigen(c *cli.Context) error { types = append(types, kind) } // Generate the contract binding - code, err := bind.Bind(types, abis, bins, sigs, c.String(pkgFlag.Name), lang) + code, err := bind.Bind(types, abis, bins, sigs, c.String(pkgFlag.Name), lang, libs) if err != nil { fmt.Printf("Failed to generate ABI binding: %v\n", err) os.Exit(-1) From d6957d928334f0beb560b2bd086828e8234b40de Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 17:07:00 +0800 Subject: [PATCH 406/479] accounts/abi/bind: remove test case UseLibrary --- accounts/abi/bind/bind_test.go | 68 ---------------------------------- 1 file changed, 68 deletions(-) diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index 5382861f1392..538dd3c38e43 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -1033,74 +1033,6 @@ var bindTests = []struct { nil, nil, }, - { - `UseLibrary`, - ` - library Math { - function add(uint a, uint b) public view returns(uint) { - return a + b; - } - } - - contract UseLibrary { - function add (uint c, uint d) public view returns(uint) { - return Math.add(c,d); - } - } - `, - []string{ - // Bytecode for the UseLibrary contract - `608060405234801561001057600080fd5b5061011d806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063771602f714602d575b600080fd5b604d60048036036040811015604157600080fd5b5080359060200135605f565b60408051918252519081900360200190f35b600073__$b98c933f0a6ececcd167bd4f9d3299b1a0$__63771602f784846040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801560b757600080fd5b505af415801560ca573d6000803e3d6000fd5b505050506040513d602081101560df57600080fd5b5051939250505056fea265627a7a72305820eb5c38f42445604cfa43d85e3aa5ecc48b0a646456c902dd48420ae7241d06f664736f6c63430005090032`, - // Bytecode for the Math contract - `60a3610024600b82828239805160001a607314601757fe5b30600052607381538281f3fe730000000000000000000000000000000000000000301460806040526004361060335760003560e01c8063771602f7146038575b600080fd5b605860048036036040811015604c57600080fd5b5080359060200135606a565b60408051918252519081900360200190f35b019056fea265627a7a723058206fc6c05f3078327f9c763edffdb5ab5f8bd212e293a1306c7d0ad05af3ad35f464736f6c63430005090032`, - }, - []string{ - `[{"constant":true,"inputs":[{"name":"c","type":"uint256"},{"name":"d","type":"uint256"}],"name":"add","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]`, - `[{"constant":true,"inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"}],"name":"add","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]`, - }, - ` - "math/big" - - "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" - "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" - "github.com/XinFinOrg/XDPoSChain/core" - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/params" - `, - ` - // Generate a new random account and a funded simulator - key, _ := crypto.GenerateKey() - auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig) - - // Deploy the test contract - _, _, testContract, err := DeployUseLibrary(auth, sim) - if err != nil { - t.Fatalf("Failed to deploy test contract: %v", err) - } - - // Finish deploy. - sim.Commit() - - // Check that the library contract has been deployed - // by calling the contract's add function. - res, err := testContract.Add(&bind.CallOpts{ - From: auth.From, - Pending: false, - }, big.NewInt(1), big.NewInt(2)) - if err != nil { - t.Fatalf("Failed to call linked contract: %v", err) - } - if res.Cmp(big.NewInt(3)) != 0 { - t.Fatalf("Add did not return the correct result: %d != %d", res, 3) - } - `, - nil, - map[string]string{ - "b98c933f0a6ececcd167bd4f9d3299b1a0": "Math", - }, - []string{"UseLibrary", "Math"}, - }, } // Tests that packages generated by the binder can be successfully compiled and From 057a7dd780272e3400c871e4069520fdbc406bfd Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 16:52:03 +0800 Subject: [PATCH 407/479] cmd/abigen: refactor command line interface (#19797) --- cmd/abigen/main.go | 133 ++++++++++++++++-------------------- cmd/utils/flags.go | 16 ++--- common/compiler/solidity.go | 13 ++-- 3 files changed, 71 insertions(+), 91 deletions(-) diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index 1c4a2aa06980..6bd6efb4c249 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -24,6 +24,7 @@ import ( "strings" "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" + "github.com/XinFinOrg/XDPoSChain/cmd/utils" "github.com/XinFinOrg/XDPoSChain/common/compiler" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/internal/flags" @@ -52,6 +53,10 @@ var ( Name: "type", Usage: "Struct name for the binding (default = package name)", } + jsonFlag = &cli.StringFlag{ + Name: "combined-json", + Usage: "Path to the combined-json file generated by compiler", + } solFlag = &cli.StringFlag{ Name: "sol", Usage: "Path to the Ethereum contract Solidity source to build and bind", @@ -96,6 +101,7 @@ func init() { abiFlag, binFlag, typeFlag, + jsonFlag, solFlag, solcFlag, vyFlag, @@ -109,28 +115,11 @@ func init() { } func abigen(c *cli.Context) error { - if c.String(abiFlag.Name) == "" && c.String(solFlag.Name) == "" && c.String(vyFlag.Name) == "" { - fmt.Printf("No contract ABI (--abi), Solidity source (--sol), or Vyper source (--vy) specified\n") - os.Exit(-1) - } else if (c.String(abiFlag.Name) != "" || c.String(binFlag.Name) != "" || c.String(typeFlag.Name) != "") && (c.String(solFlag.Name) != "" || c.String(vyFlag.Name) != "") { - fmt.Printf("Contract ABI (--abi), bytecode (--bin) and type (--type) flags are mutually exclusive with the Solidity (--sol) and Vyper (--vy) flags\n") - os.Exit(-1) - } else if c.String(solFlag.Name) != "" && c.String(vyFlag.Name) == "" { - fmt.Printf("Solidity (--sol) and Vyper (--vy) flags are mutually exclusive\n") - os.Exit(-1) - } + utils.CheckExclusive(c, abiFlag, jsonFlag, solFlag, vyFlag) // Only one source can be selected. if c.String(pkgFlag.Name) == "" { - fmt.Printf("No destination package specified (--pkg)\n") - os.Exit(-1) - } - var lang bind.Lang - switch c.String(langFlag.Name) { - case "go": - lang = bind.LangGo - default: - fmt.Printf("Unsupported destination language \"%s\" (--lang)\n", c.String(langFlag.Name)) - os.Exit(-1) + utils.Fatalf("No destination package specified (--pkg)") } + lang := bind.LangGo // If the entire solidity code was specified, build and bind based on that var ( abis []string @@ -139,37 +128,69 @@ func abigen(c *cli.Context) error { sigs []map[string]string libs = make(map[string]string) ) - if c.String(solFlag.Name) != "" || c.String(vyFlag.Name) != "" || c.String(abiFlag.Name) == "-" { + if c.String(abiFlag.Name) != "" { + // Load up the ABI, optional bytecode and type name from the parameters + var ( + abi []byte + err error + ) + input := c.String(abiFlag.Name) + if input == "-" { + abi, err = io.ReadAll(os.Stdin) + } else { + abi, err = os.ReadFile(input) + } + if err != nil { + utils.Fatalf("Failed to read input ABI: %v", err) + } + abis = append(abis, string(abi)) + + var bin []byte + if binFile := c.String(binFlag.Name); binFile != "" { + if bin, err = os.ReadFile(binFile); err != nil { + utils.Fatalf("Failed to read input bytecode: %v", err) + } + if strings.Contains(string(bin), "//") { + utils.Fatalf("Contract has additional library references, please use other mode(e.g. --combined-json) to catch library infos") + } + } + bins = append(bins, string(bin)) + + kind := c.String(typeFlag.Name) + if kind == "" { + kind = c.String(pkgFlag.Name) + } + types = append(types, kind) + } else { // Generate the list of types to exclude from binding exclude := make(map[string]bool) for _, kind := range strings.Split(c.String(excFlag.Name), ",") { exclude[strings.ToLower(kind)] = true } - - var contracts map[string]*compiler.Contract var err error + var contracts map[string]*compiler.Contract switch { - case c.String(solFlag.Name) != "": + case c.IsSet(solFlag.Name): contracts, err = compiler.CompileSolidity(c.String(solcFlag.Name), c.String(solFlag.Name)) if err != nil { - fmt.Printf("Failed to build Solidity contract: %v\n", err) - os.Exit(-1) + utils.Fatalf("Failed to build Solidity contract: %v", err) } - case c.String(vyFlag.Name) != "": + case c.IsSet(vyFlag.Name): contracts, err = compiler.CompileVyper(c.String(vyperFlag.Name), c.String(vyFlag.Name)) if err != nil { - fmt.Printf("Failed to build Vyper contract: %v\n", err) - os.Exit(-1) + utils.Fatalf("Failed to build Vyper contract: %v", err) } - default: - contracts, err = contractsFromStdin() + case c.IsSet(jsonFlag.Name): + jsonOutput, err := os.ReadFile(c.String(jsonFlag.Name)) if err != nil { - fmt.Printf("Failed to read input ABIs from STDIN: %v\n", err) - os.Exit(-1) + utils.Fatalf("Failed to read combined-json from compiler: %v", err) + } + contracts, err = compiler.ParseCombinedJSON(jsonOutput, "", "", "", "") + if err != nil { + utils.Fatalf("Failed to read contract information from json output: %v", err) } } - // Gather all non-excluded contract for binding for name, contract := range contracts { if exclude[strings.ToLower(name)] { @@ -177,58 +198,30 @@ func abigen(c *cli.Context) error { } abi, err := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse if err != nil { - fmt.Printf("Failed to parse ABIs from compiler output: %v\n", err) - os.Exit(-1) + utils.Fatalf("Failed to parse ABIs from compiler output: %v", err) } abis = append(abis, string(abi)) bins = append(bins, contract.Code) sigs = append(sigs, contract.Hashes) - nameParts := strings.Split(name, ":") types = append(types, nameParts[len(nameParts)-1]) libPattern := crypto.Keccak256Hash([]byte(name)).String()[2:36] libs[libPattern] = nameParts[len(nameParts)-1] } - } else { - // Otherwise load up the ABI, optional bytecode and type name from the parameters - abi, err := os.ReadFile(c.String(abiFlag.Name)) - - if err != nil { - fmt.Printf("Failed to read input ABI: %v\n", err) - os.Exit(-1) - } - abis = append(abis, string(abi)) - - var bin []byte - if c.String(binFlag.Name) != "" { - if bin, err = os.ReadFile(c.String(binFlag.Name)); err != nil { - fmt.Printf("Failed to read input bytecode: %v\n", err) - os.Exit(-1) - } - } - bins = append(bins, string(bin)) - - kind := c.String(typeFlag.Name) - if kind == "" { - kind = c.String(pkgFlag.Name) - } - types = append(types, kind) } // Generate the contract binding code, err := bind.Bind(types, abis, bins, sigs, c.String(pkgFlag.Name), lang, libs) if err != nil { - fmt.Printf("Failed to generate ABI binding: %v\n", err) - os.Exit(-1) + utils.Fatalf("Failed to generate ABI binding: %v", err) } // Either flush it out to a file or display on the standard output - if c.String(outFlag.Name) == "" { + if !c.IsSet(outFlag.Name) { fmt.Printf("%s\n", code) return nil } if err := os.WriteFile(c.String(outFlag.Name), []byte(code), 0600); err != nil { - fmt.Printf("Failed to write ABI binding: %v\n", err) - os.Exit(-1) + utils.Fatalf("Failed to write ABI binding: %v", err) } return nil } @@ -241,11 +234,3 @@ func main() { os.Exit(1) } } - -func contractsFromStdin() (map[string]*compiler.Contract, error) { - bytes, err := io.ReadAll(os.Stdin) - if err != nil { - return nil, err - } - return compiler.ParseCombinedJSON(bytes, "", "", "", "") -} diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 9c25f50df4c0..64dc4b05004b 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1024,7 +1024,7 @@ func setWS(ctx *cli.Context, cfg *node.Config) { // setIPC creates an IPC path configuration from the set command line flags, // returning an empty string if IPC was explicitly disabled, or the set path. func setIPC(ctx *cli.Context, cfg *node.Config) { - checkExclusive(ctx, IPCDisabledFlag, IPCPathFlag) + CheckExclusive(ctx, IPCDisabledFlag, IPCPathFlag) switch { case ctx.Bool(IPCDisabledFlag.Name): cfg.IPCPath = "" @@ -1034,7 +1034,7 @@ func setIPC(ctx *cli.Context, cfg *node.Config) { } func setPrefix(ctx *cli.Context, cfg *node.Config) { - checkExclusive(ctx, Enable0xPrefixFlag, EnableXDCPrefixFlag) + CheckExclusive(ctx, Enable0xPrefixFlag, EnableXDCPrefixFlag) } // MakeDatabaseHandles raises out the number of allowed file handles per process @@ -1301,10 +1301,10 @@ func setEthash(ctx *cli.Context, cfg *ethconfig.Config) { } } -// checkExclusive verifies that only a single isntance of the provided flags was +// CheckExclusive verifies that only a single isntance of the provided flags was // set by the user. Each flag might optionally be followed by a string type to // specialize it further. -func checkExclusive(ctx *cli.Context, args ...interface{}) { +func CheckExclusive(ctx *cli.Context, args ...interface{}) { set := make([]string, 0, 1) for i := 0; i < len(args); i++ { // Make sure the next argument is a flag and skip if not set @@ -1381,10 +1381,10 @@ func SetXDCXConfig(ctx *cli.Context, cfg *XDCx.Config, XDCDataDir string) { // SetEthConfig applies eth-related command line flags to the config. func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { // Avoid conflicting network flags - checkExclusive(ctx, DeveloperFlag, TestnetFlag, RinkebyFlag) - checkExclusive(ctx, FastSyncFlag, LightModeFlag, SyncModeFlag) - checkExclusive(ctx, LightServFlag, LightModeFlag) - checkExclusive(ctx, LightServFlag, SyncModeFlag, "light") + CheckExclusive(ctx, DeveloperFlag, TestnetFlag, RinkebyFlag) + CheckExclusive(ctx, FastSyncFlag, LightModeFlag, SyncModeFlag) + CheckExclusive(ctx, LightServFlag, LightModeFlag) + CheckExclusive(ctx, LightServFlag, SyncModeFlag, "light") ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) setEtherbase(ctx, ks, cfg) diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go index 56e01ee334ca..16b91bf747f4 100644 --- a/common/compiler/solidity.go +++ b/common/compiler/solidity.go @@ -142,7 +142,6 @@ func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion strin if err := json.Unmarshal(combinedJSON, &output); err != nil { return nil, err } - // Compilation succeeded, assemble and return the contracts. contracts := make(map[string]*Contract) for name, info := range output.Contracts { @@ -151,14 +150,10 @@ func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion strin if err := json.Unmarshal([]byte(info.Abi), &abi); err != nil { return nil, fmt.Errorf("solc: error reading abi definition (%v)", err) } - var userdoc interface{} - if err := json.Unmarshal([]byte(info.Userdoc), &userdoc); err != nil { - return nil, fmt.Errorf("solc: error reading user doc: %v", err) - } - var devdoc interface{} - if err := json.Unmarshal([]byte(info.Devdoc), &devdoc); err != nil { - return nil, fmt.Errorf("solc: error reading dev doc: %v", err) - } + var userdoc, devdoc interface{} + json.Unmarshal([]byte(info.Userdoc), &userdoc) + json.Unmarshal([]byte(info.Devdoc), &devdoc) + contracts[name] = &Contract{ Code: "0x" + info.Bin, RuntimeCode: "0x" + info.BinRuntime, From 5a8110b2929152ef6619807b2bb3d8fe6f232d35 Mon Sep 17 00:00:00 2001 From: David Chase Date: Wed, 17 Jul 2019 04:23:43 -0400 Subject: [PATCH 408/479] common/bitutil: use result of TestBytes to prevent dead code elimination (#19846) Gollvm has very aggressive dead code elimination that completely removes one of these two benchmarks. To prevent this, use the result of the benchmark (a boolean), and to be "fair", make the transformation to both benchmarks. To be reliably assured of not removing the code, "use" means assigning to an exported global. Non-exported globals and //go:noinline functions are possibly subject to this optimization. --- common/bitutil/bitutil_test.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/common/bitutil/bitutil_test.go b/common/bitutil/bitutil_test.go index 93647031ef84..307bf731f765 100644 --- a/common/bitutil/bitutil_test.go +++ b/common/bitutil/bitutil_test.go @@ -190,6 +190,8 @@ func benchmarkBaseOR(b *testing.B, size int) { } } +var GloBool bool // Exported global will not be dead-code eliminated, at least not yet. + // Benchmarks the potentially optimized bit testing performance. func BenchmarkFastTest1KB(b *testing.B) { benchmarkFastTest(b, 1024) } func BenchmarkFastTest2KB(b *testing.B) { benchmarkFastTest(b, 2048) } @@ -197,9 +199,11 @@ func BenchmarkFastTest4KB(b *testing.B) { benchmarkFastTest(b, 4096) } func benchmarkFastTest(b *testing.B, size int) { p := make([]byte, size) + a := false for i := 0; i < b.N; i++ { - TestBytes(p) + a = a != TestBytes(p) } + GloBool = a // Use of benchmark "result" to prevent total dead code elimination. } // Benchmarks the baseline bit testing performance. @@ -209,7 +213,9 @@ func BenchmarkBaseTest4KB(b *testing.B) { benchmarkBaseTest(b, 4096) } func benchmarkBaseTest(b *testing.B, size int) { p := make([]byte, size) + a := false for i := 0; i < b.N; i++ { - safeTestBytes(p) + a = a != safeTestBytes(p) } + GloBool = a // Use of benchmark "result" to prevent total dead code elimination. } From 94b69fdf1e6f69f55839c69fb13a4c43b34641b3 Mon Sep 17 00:00:00 2001 From: shiqinfeng1 <150627601@qq.com> Date: Thu, 15 Aug 2019 16:33:06 +0800 Subject: [PATCH 409/479] common/compiler: support relative import paths (#17374 #19967) --- common/compiler/solidity.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go index 16b91bf747f4..f0363dc92b18 100644 --- a/common/compiler/solidity.go +++ b/common/compiler/solidity.go @@ -47,7 +47,8 @@ type solcOutput struct { func (s *Solidity) makeArgs() []string { p := []string{ "--combined-json", "bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc", - "--optimize", // code optimizer switched on + "--optimize", // code optimizer switched on + "--allow-paths", "., ./, ../", // default to support relative paths } if s.Major > 0 || s.Minor > 4 || s.Patch > 6 { p[1] += ",metadata,hashes" From 7f63a690eeef9fda43f389ae9e4c85f1eac881d7 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 16 Dec 2024 17:14:34 +0800 Subject: [PATCH 410/479] common: unify hex prefix check code (#19937) --- common/bytes.go | 37 ++++++++++++++++--------------------- common/types.go | 7 +++---- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/common/bytes.go b/common/bytes.go index 6be6116bb979..703705ee3f06 100644 --- a/common/bytes.go +++ b/common/bytes.go @@ -34,13 +34,10 @@ func ToHex(b []byte) string { // FromHex returns the bytes represented by the hexadecimal string s. // s may be prefixed with "0x". func FromHex(s string) []byte { - if len(s) > 1 { - if s[0:2] == "0x" || s[0:2] == "0X" { - s = s[2:] - } - if (s[0] == 'x' || s[0] == 'X') && (s[1] == 'd' || s[1] == 'D') && (s[2] == 'c' || s[2] == 'C') { - s = s[3:] - } + if has0xPrefix(s) { + s = s[2:] + } else if hasXdcPrefix(s) { + s = s[3:] } if len(s)%2 == 1 { s = "0" + s @@ -59,14 +56,14 @@ func CopyBytes(b []byte) (copiedBytes []byte) { return } -// hasXDCPrefix validates str begins with 'xdc' or 'XDC'. -func hasXDCPrefix(str string) bool { - return len(str) >= 3 && (str[0] == 'x' || str[0] == 'X') && (str[1] == 'd' || str[1] == 'D') && (str[2] == 'c' || str[2] == 'C') +// has0xPrefix validates str begins with '0x' or '0X'. +func has0xPrefix(str string) bool { + return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X') } -// hasHexPrefix validates str begins with '0x' or '0X'. -func hasHexPrefix(str string) bool { - return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X') +// hasXdcPrefix validates s begins with 'xdc' or 'XDC'. +func hasXdcPrefix(s string) bool { + return len(s) >= 3 && (s[0] == 'x' || s[0] == 'X') && (s[1] == 'd' || s[1] == 'D') && (s[2] == 'c' || s[2] == 'C') } // isHexCharacter returns bool of c being a valid hexadecimal. @@ -103,15 +100,13 @@ func Hex2BytesFixed(str string, flen int) []byte { h, _ := hex.DecodeString(str) if len(h) == flen { return h - } else { - if len(h) > flen { - return h[len(h)-flen:] - } else { - hh := make([]byte, flen) - copy(hh[flen-len(h):flen], h[:]) - return hh - } } + if len(h) > flen { + return h[len(h)-flen:] + } + hh := make([]byte, flen) + copy(hh[flen-len(h):flen], h[:]) + return hh } // RightPadBytes zero-pads slice to the right up to length l. diff --git a/common/types.go b/common/types.go index e2dd3b4b2db6..cf79aaa87027 100644 --- a/common/types.go +++ b/common/types.go @@ -209,11 +209,10 @@ func HexToAddress(s string) Address { return BytesToAddress(FromHex(s)) } // IsHexAddress verifies whether a string can represent a valid hex-encoded // Ethereum address or not. func IsHexAddress(s string) bool { - if hasXDCPrefix(s) { - s = s[3:] - } - if hasHexPrefix(s) { + if has0xPrefix(s) { s = s[2:] + } else if hasXdcPrefix(s) { + s = s[3:] } return len(s) == 2*AddressLength && isHex(s) } From 56e242b06ce6f5a2e66356c649c645d65aa6e167 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 16 Dec 2024 17:26:11 +0800 Subject: [PATCH 411/479] core/state: optimize some internals during encoding (#20038) --- common/bytes.go | 11 ++++++++ core/state/state_object.go | 2 +- core/state/state_object_test.go | 48 +++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 core/state/state_object_test.go diff --git a/common/bytes.go b/common/bytes.go index 703705ee3f06..9025e98b19a5 100644 --- a/common/bytes.go +++ b/common/bytes.go @@ -132,3 +132,14 @@ func LeftPadBytes(slice []byte, l int) []byte { return padded } + +// TrimLeftZeroes returns a subslice of s without leading zeroes +func TrimLeftZeroes(s []byte) []byte { + idx := 0 + for ; idx < len(s); idx++ { + if s[idx] != 0 { + break + } + } + return s[idx:] +} diff --git a/core/state/state_object.go b/core/state/state_object.go index bb19597cc6f9..7eda5c4d4d75 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -282,7 +282,7 @@ func (s *stateObject) updateTrie(db Database) Trie { continue } // Encoding []byte cannot fail, ok to ignore the error. - v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00")) + v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(value[:])) s.setError(tr.TryUpdate(key[:], v)) } return tr diff --git a/core/state/state_object_test.go b/core/state/state_object_test.go new file mode 100644 index 000000000000..a5b28dec88f6 --- /dev/null +++ b/core/state/state_object_test.go @@ -0,0 +1,48 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "bytes" + "testing" + + "github.com/XinFinOrg/XDPoSChain/common" +) + +func BenchmarkCutOriginal(b *testing.B) { + value := common.HexToHash("0x01") + for i := 0; i < b.N; i++ { + bytes.TrimLeft(value[:], "\x00") + } +} + +func BenchmarkCutsetterFn(b *testing.B) { + value := common.HexToHash("0x01") + cutSetFn := func(r rune) bool { + return int32(r) == int32(0) + } + for i := 0; i < b.N; i++ { + bytes.TrimLeftFunc(value[:], cutSetFn) + } +} + +func BenchmarkCutCustomTrim(b *testing.B) { + value := common.HexToHash("0x01") + for i := 0; i < b.N; i++ { + common.TrimLeftZeroes(value[:]) + } +} From f5e90eecff3435408ec80edd3433872d8539e673 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 16 Dec 2024 17:41:47 +0800 Subject: [PATCH 412/479] common: improve test (#20354) --- common/bytes_test.go | 52 +++++++++++++++++++++++--------------------- common/main_test.go | 25 --------------------- 2 files changed, 27 insertions(+), 50 deletions(-) delete mode 100644 common/main_test.go diff --git a/common/bytes_test.go b/common/bytes_test.go index 97dd34d159de..7cf6553377fd 100644 --- a/common/bytes_test.go +++ b/common/bytes_test.go @@ -19,41 +19,43 @@ package common import ( "bytes" "testing" - - checker "gopkg.in/check.v1" ) -type BytesSuite struct{} - -var _ = checker.Suite(&BytesSuite{}) +func TestCopyBytes(t *testing.T) { + input := []byte{1, 2, 3, 4} -func (s *BytesSuite) TestCopyBytes(c *checker.C) { - data1 := []byte{1, 2, 3, 4} - exp1 := []byte{1, 2, 3, 4} - res1 := CopyBytes(data1) - c.Assert(res1, checker.DeepEquals, exp1) + v := CopyBytes(input) + if !bytes.Equal(v, []byte{1, 2, 3, 4}) { + t.Fatal("not equal after copy") + } + v[0] = 99 + if bytes.Equal(v, input) { + t.Fatal("result is not a copy") + } } -func (s *BytesSuite) TestLeftPadBytes(c *checker.C) { - val1 := []byte{1, 2, 3, 4} - exp1 := []byte{0, 0, 0, 0, 1, 2, 3, 4} - - res1 := LeftPadBytes(val1, 8) - res2 := LeftPadBytes(val1, 2) +func TestLeftPadBytes(t *testing.T) { + val := []byte{1, 2, 3, 4} + padded := []byte{0, 0, 0, 0, 1, 2, 3, 4} - c.Assert(res1, checker.DeepEquals, exp1) - c.Assert(res2, checker.DeepEquals, val1) + if r := LeftPadBytes(val, 8); !bytes.Equal(r, padded) { + t.Fatalf("LeftPadBytes(%v, 8) == %v", val, r) + } + if r := LeftPadBytes(val, 2); !bytes.Equal(r, val) { + t.Fatalf("LeftPadBytes(%v, 2) == %v", val, r) + } } -func (s *BytesSuite) TestRightPadBytes(c *checker.C) { +func TestRightPadBytes(t *testing.T) { val := []byte{1, 2, 3, 4} - exp := []byte{1, 2, 3, 4, 0, 0, 0, 0} - - resstd := RightPadBytes(val, 8) - resshrt := RightPadBytes(val, 2) + padded := []byte{1, 2, 3, 4, 0, 0, 0, 0} - c.Assert(resstd, checker.DeepEquals, exp) - c.Assert(resshrt, checker.DeepEquals, val) + if r := RightPadBytes(val, 8); !bytes.Equal(r, padded) { + t.Fatalf("RightPadBytes(%v, 8) == %v", val, r) + } + if r := RightPadBytes(val, 2); !bytes.Equal(r, val) { + t.Fatalf("RightPadBytes(%v, 2) == %v", val, r) + } } func TestFromHex(t *testing.T) { diff --git a/common/main_test.go b/common/main_test.go deleted file mode 100644 index 149d09928a9c..000000000000 --- a/common/main_test.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2014 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package common - -import ( - "testing" - - checker "gopkg.in/check.v1" -) - -func Test(t *testing.T) { checker.TestingT(t) } From 30f835c6a4a21249c7c71639a2a52978b26d2db9 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 19 Dec 2024 13:57:52 +0800 Subject: [PATCH 413/479] accounts/abi: common/math: moved U256Bytes (#21020) --- accounts/abi/abi_test.go | 24 +++++++++++-------- accounts/abi/bind/template.go | 1 - accounts/abi/numbers.go | 6 ----- accounts/abi/numbers_test.go | 44 ----------------------------------- accounts/abi/pack.go | 6 ++--- common/math/big.go | 6 +++++ common/math/big_test.go | 10 ++++++++ 7 files changed, 33 insertions(+), 64 deletions(-) delete mode 100644 accounts/abi/numbers_test.go diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index 790505b8c043..1d8cd0162980 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -29,6 +29,7 @@ import ( "reflect" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/crypto" ) @@ -522,7 +523,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) { strvalue = common.RightPadBytes([]byte(strin), 32) fixedarrin1value1 = common.LeftPadBytes(fixedarrin1[0].Bytes(), 32) fixedarrin1value2 = common.LeftPadBytes(fixedarrin1[1].Bytes(), 32) - dynarroffset = U256(big.NewInt(int64(256 + ((len(strin)/32)+1)*32))) + dynarroffset = math.U256Bytes(big.NewInt(int64(256 + ((len(strin)/32)+1)*32))) dynarrlength = make([]byte, 32) dynarrlength[31] = byte(len(dynarrin)) dynarrinvalue1 = common.LeftPadBytes(dynarrin[0].Bytes(), 32) @@ -620,16 +621,19 @@ func TestBareEvents(t *testing.T) { } // TestUnpackEvent is based on this contract: -// contract T { -// event received(address sender, uint amount, bytes memo); -// event receivedAddr(address sender); -// function receive(bytes memo) external payable { -// received(msg.sender, msg.value, memo); -// receivedAddr(msg.sender); -// } -// } +// +// contract T { +// event received(address sender, uint amount, bytes memo); +// event receivedAddr(address sender); +// function receive(bytes memo) external payable { +// received(msg.sender, msg.value, memo); +// receivedAddr(msg.sender); +// } +// } +// // When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt: -// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]} +// +// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]} func TestUnpackEvent(t *testing.T) { const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]` abi, err := JSON(strings.NewReader(abiJSON)) diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go index 10e8ae9f0579..f7b1ebb62a86 100644 --- a/accounts/abi/bind/template.go +++ b/accounts/abi/bind/template.go @@ -84,7 +84,6 @@ var ( _ = big.NewInt _ = strings.NewReader _ = ethereum.ErrNotFound - _ = abi.U256 _ = bind.Bind _ = common.Big1 _ = types.BloomLookup diff --git a/accounts/abi/numbers.go b/accounts/abi/numbers.go index ac053c2da70d..d389db33b705 100644 --- a/accounts/abi/numbers.go +++ b/accounts/abi/numbers.go @@ -21,7 +21,6 @@ import ( "reflect" "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/common/math" ) var ( @@ -44,11 +43,6 @@ var ( int64_ts = reflect.TypeOf([]int64(nil)) ) -// U256 converts a big Int into a 256bit EVM number. -func U256(n *big.Int) []byte { - return math.PaddedBigBytes(math.U256(n), 32) -} - // checks whether the given reflect value is signed. This also works for slices with a number type func isSigned(v reflect.Value) bool { switch v.Type() { diff --git a/accounts/abi/numbers_test.go b/accounts/abi/numbers_test.go deleted file mode 100644 index b9ff5aef17d3..000000000000 --- a/accounts/abi/numbers_test.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package abi - -import ( - "bytes" - "math/big" - "reflect" - "testing" -) - -func TestNumberTypes(t *testing.T) { - ubytes := make([]byte, 32) - ubytes[31] = 1 - - unsigned := U256(big.NewInt(1)) - if !bytes.Equal(unsigned, ubytes) { - t.Errorf("expected %x got %x", ubytes, unsigned) - } -} - -func TestSigned(t *testing.T) { - if isSigned(reflect.ValueOf(uint(10))) { - t.Error("signed") - } - - if !isSigned(reflect.ValueOf(int(10))) { - t.Error("not signed") - } -} diff --git a/accounts/abi/pack.go b/accounts/abi/pack.go index c38be40c918c..0041349894e5 100644 --- a/accounts/abi/pack.go +++ b/accounts/abi/pack.go @@ -69,11 +69,11 @@ func packElement(t Type, reflectValue reflect.Value) []byte { func packNum(value reflect.Value) []byte { switch kind := value.Kind(); kind { case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return U256(new(big.Int).SetUint64(value.Uint())) + return math.U256Bytes(new(big.Int).SetUint64(value.Uint())) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return U256(big.NewInt(value.Int())) + return math.U256Bytes(big.NewInt(value.Int())) case reflect.Ptr: - return U256(value.Interface().(*big.Int)) + return math.U256Bytes(value.Interface().(*big.Int)) default: panic("abi: fatal error") } diff --git a/common/math/big.go b/common/math/big.go index d2cfb6ed5a9b..7254efed4823 100644 --- a/common/math/big.go +++ b/common/math/big.go @@ -176,6 +176,12 @@ func U256(x *big.Int) *big.Int { return x.And(x, tt256m1) } +// U256Bytes converts a big Int into a 256bit EVM number. +// This operation is destructive. +func U256Bytes(n *big.Int) []byte { + return PaddedBigBytes(U256(n), 32) +} + // S256 interprets x as a two's complement number. // x must not exceed 256 bits (the result is undefined if it does) and is not modified. // diff --git a/common/math/big_test.go b/common/math/big_test.go index 2c607bad5fa3..d9e5723c61c6 100644 --- a/common/math/big_test.go +++ b/common/math/big_test.go @@ -212,6 +212,16 @@ func TestU256(t *testing.T) { } } +func TestU256Bytes(t *testing.T) { + ubytes := make([]byte, 32) + ubytes[31] = 1 + + unsigned := U256Bytes(big.NewInt(1)) + if !bytes.Equal(unsigned, ubytes) { + t.Errorf("expected %x got %x", ubytes, unsigned) + } +} + func TestBigEndianByteAt(t *testing.T) { tests := []struct { x string From d3a943d0b33343533e2b7bc4d429e1d92b9cc691 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 9 Jul 2020 17:45:49 +0200 Subject: [PATCH 414/479] common/math: use math/bits intrinsics for Safe* (#21316) This is a resubmit of ledgerwatch/turbo-geth#556. The performance benefit of this change is negligible, but it does remove a TODO. --- common/math/integer.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/common/math/integer.go b/common/math/integer.go index 7eff4d3b0001..17a9fb1375d1 100644 --- a/common/math/integer.go +++ b/common/math/integer.go @@ -18,6 +18,7 @@ package math import ( "fmt" + "math/bits" "strconv" ) @@ -78,22 +79,20 @@ func MustParseUint64(s string) uint64 { return v } -// NOTE: The following methods need to be optimised using either bit checking or asm - -// SafeSub returns subtraction result and whether overflow occurred. +// SafeSub returns x-y and checks for overflow. func SafeSub(x, y uint64) (uint64, bool) { - return x - y, x < y + diff, borrowOut := bits.Sub64(x, y, 0) + return diff, borrowOut != 0 } -// SafeAdd returns the result and whether overflow occurred. +// SafeAdd returns x+y and checks for overflow. func SafeAdd(x, y uint64) (uint64, bool) { - return x + y, y > MaxUint64-x + sum, carryOut := bits.Add64(x, y, 0) + return sum, carryOut != 0 } -// SafeMul returns multiplication result and whether overflow occurred. +// SafeMul returns x*y and checks for overflow. func SafeMul(x, y uint64) (uint64, bool) { - if x == 0 || y == 0 { - return 0, false - } - return x * y, y > MaxUint64/x + hi, lo := bits.Mul64(x, y) + return lo, hi != 0 } From cf0c5c8cd223815cc75f122abdf6d8addcfa2265 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 16 Dec 2024 18:52:16 +0800 Subject: [PATCH 415/479] common/prque: refactor LazyQueue (#21236) --- common/prque/lazyqueue.go | 56 +++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/common/prque/lazyqueue.go b/common/prque/lazyqueue.go index 035b8ff9e7b9..fc6b9b2f26e9 100644 --- a/common/prque/lazyqueue.go +++ b/common/prque/lazyqueue.go @@ -26,9 +26,10 @@ import ( // LazyQueue is a priority queue data structure where priorities can change over // time and are only evaluated on demand. // Two callbacks are required: -// - priority evaluates the actual priority of an item -// - maxPriority gives an upper estimate for the priority in any moment between -// now and the given absolute time +// - priority evaluates the actual priority of an item +// - maxPriority gives an upper estimate for the priority in any moment between +// now and the given absolute time +// // If the upper estimate is exceeded then Update should be called for that item. // A global Refresh function should also be called periodically. type LazyQueue struct { @@ -36,14 +37,15 @@ type LazyQueue struct { // Items are stored in one of two internal queues ordered by estimated max // priority until the next and the next-after-next refresh. Update and Refresh // always places items in queue[1]. - queue [2]*sstack - popQueue *sstack - period time.Duration - maxUntil mclock.AbsTime - indexOffset int - setIndex SetIndexCallback - priority PriorityCallback - maxPriority MaxPriorityCallback + queue [2]*sstack + popQueue *sstack + period time.Duration + maxUntil mclock.AbsTime + indexOffset int + setIndex SetIndexCallback + priority PriorityCallback + maxPriority MaxPriorityCallback + lastRefresh1, lastRefresh2 mclock.AbsTime } type ( @@ -54,14 +56,17 @@ type ( // NewLazyQueue creates a new lazy queue func NewLazyQueue(setIndex SetIndexCallback, priority PriorityCallback, maxPriority MaxPriorityCallback, clock mclock.Clock, refreshPeriod time.Duration) *LazyQueue { q := &LazyQueue{ - popQueue: newSstack(nil), - setIndex: setIndex, - priority: priority, - maxPriority: maxPriority, - clock: clock, - period: refreshPeriod} + popQueue: newSstack(nil), + setIndex: setIndex, + priority: priority, + maxPriority: maxPriority, + clock: clock, + period: refreshPeriod, + lastRefresh1: clock.Now(), + lastRefresh2: clock.Now(), + } q.Reset() - q.Refresh() + q.refresh(clock.Now()) return q } @@ -71,9 +76,19 @@ func (q *LazyQueue) Reset() { q.queue[1] = newSstack(q.setIndex1) } -// Refresh should be called at least with the frequency specified by the refreshPeriod parameter +// Refresh performs queue re-evaluation if necessary func (q *LazyQueue) Refresh() { - q.maxUntil = q.clock.Now() + mclock.AbsTime(q.period) + now := q.clock.Now() + for time.Duration(now-q.lastRefresh2) >= q.period*2 { + q.refresh(now) + q.lastRefresh2 = q.lastRefresh1 + q.lastRefresh1 = now + } +} + +// refresh re-evaluates items in the older queue and swaps the two queues +func (q *LazyQueue) refresh(now mclock.AbsTime) { + q.maxUntil = now + mclock.AbsTime(q.period) for q.queue[0].Len() != 0 { q.Push(heap.Pop(q.queue[0]).(*item).value) } @@ -139,6 +154,7 @@ func (q *LazyQueue) MultiPop(callback func(data interface{}, priority int64) boo } return } + nextIndex = q.peekIndex() // re-check because callback is allowed to push items back } } } From 28739d786310766d3faef1f576eb85882dcbe4de Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 16 Dec 2024 19:19:30 +0800 Subject: [PATCH 416/479] common: remove usage of deprecated function (#21610) --- accounts/abi/abi_test.go | 6 +++--- common/bytes.go | 16 +++------------- .../XDPoS/engines/engine_v2/verifyHeader.go | 3 ++- contracts/blocksigner/blocksigner_test.go | 6 +++--- contracts/randomize/randomize_test.go | 3 ++- contracts/validator/validator_test.go | 3 ++- core/types/transaction.go | 9 +++++---- ethclient/ethclient.go | 6 +++--- 8 files changed, 23 insertions(+), 29 deletions(-) diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index 1d8cd0162980..968cdb694141 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -23,12 +23,12 @@ import ( "fmt" "log" "math/big" + "reflect" "strings" "testing" - "reflect" - "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/hexutil" "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/crypto" ) @@ -713,7 +713,7 @@ func TestABI_MethodById(t *testing.T) { } b := fmt.Sprintf("%v", m2) if a != b { - t.Errorf("Method %v (id %v) not 'findable' by id in ABI", name, common.ToHex(m.Id())) + t.Errorf("Method %v (id %v) not 'findable' by id in ABI", name, hexutil.Encode(m.Id())) } } diff --git a/common/bytes.go b/common/bytes.go index 9025e98b19a5..3aaf97bd68c7 100644 --- a/common/bytes.go +++ b/common/bytes.go @@ -17,19 +17,9 @@ // Package common contains various helper functions. package common -import "encoding/hex" - -// ToHex returns the hex representation of b, prefixed with '0x'. -// For empty slices, the return value is "0x0". -// -// Deprecated: use hexutil.Encode instead. -func ToHex(b []byte) string { - hex := Bytes2Hex(b) - if len(hex) == 0 { - hex = "0" - } - return "0x" + hex -} +import ( + "encoding/hex" +) // FromHex returns the bytes represented by the hexadecimal string s. // s may be prefixed with "0x". diff --git a/consensus/XDPoS/engines/engine_v2/verifyHeader.go b/consensus/XDPoS/engines/engine_v2/verifyHeader.go index 8341ae1c6be8..b90f942d0712 100644 --- a/consensus/XDPoS/engines/engine_v2/verifyHeader.go +++ b/consensus/XDPoS/engines/engine_v2/verifyHeader.go @@ -6,6 +6,7 @@ import ( "time" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/hexutil" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" "github.com/XinFinOrg/XDPoSChain/consensus/misc" @@ -171,7 +172,7 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade for index, mn := range masterNodes { log.Error("[verifyHeader] masternode list during validator verification", "Masternode Address", mn.Hex(), "index", index) } - log.Error("[verifyHeader] Error while verifying header validator signature", "BlockNumber", header.Number, "Hash", header.Hash().Hex(), "validator in hex", common.ToHex(header.Validator)) + log.Error("[verifyHeader] Error while verifying header validator signature", "BlockNumber", header.Number, "Hash", header.Hash().Hex(), "validator in hex", hexutil.Encode(header.Validator)) return err } if !verified { diff --git a/contracts/blocksigner/blocksigner_test.go b/contracts/blocksigner/blocksigner_test.go index 54f89b29026e..d1f8cbc0f486 100644 --- a/contracts/blocksigner/blocksigner_test.go +++ b/contracts/blocksigner/blocksigner_test.go @@ -18,14 +18,14 @@ package blocksigner import ( "context" "math/big" + "math/rand" "testing" "time" - "math/rand" - "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/hexutil" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/params" @@ -50,7 +50,7 @@ func TestBlockSigner(t *testing.T) { ctx, cancel := context.WithDeadline(context.Background(), d) defer cancel() code, _ := contractBackend.CodeAt(ctx, blockSignerAddress, nil) - t.Log("contract code", common.ToHex(code)) + t.Log("contract code", hexutil.Encode(code)) f := func(key, val common.Hash) bool { t.Log(key.Hex(), val.Hex()) return true diff --git a/contracts/randomize/randomize_test.go b/contracts/randomize/randomize_test.go index 3d4dead11d55..14d229196c6c 100644 --- a/contracts/randomize/randomize_test.go +++ b/contracts/randomize/randomize_test.go @@ -24,6 +24,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/hexutil" "github.com/XinFinOrg/XDPoSChain/contracts" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/types" @@ -56,7 +57,7 @@ func TestRandomize(t *testing.T) { ctx, cancel := context.WithDeadline(context.Background(), d) defer cancel() code, _ := contractBackend.CodeAt(ctx, randomizeAddress, nil) - t.Log("contract code", common.ToHex(code)) + t.Log("contract code", hexutil.Encode(code)) f := func(key, val common.Hash) bool { t.Log(key.Hex(), val.Hex()) return true diff --git a/contracts/validator/validator_test.go b/contracts/validator/validator_test.go index 6ebe924b5cf2..c2b5d0fde123 100644 --- a/contracts/validator/validator_test.go +++ b/contracts/validator/validator_test.go @@ -29,6 +29,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/hexutil" contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/state" @@ -68,7 +69,7 @@ func TestValidator(t *testing.T) { ctx, cancel := context.WithDeadline(context.Background(), d) defer cancel() code, _ := contractBackend.CodeAt(ctx, validatorAddress, nil) - t.Log("contract code", common.ToHex(code)) + t.Log("contract code", hexutil.Encode(code)) f := func(key, val common.Hash) bool { t.Log(key.Hex(), val.Hex()) return true diff --git a/core/types/transaction.go b/core/types/transaction.go index f0afdca06092..dca1ef50a5bf 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -27,6 +27,7 @@ import ( "time" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/hexutil" "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/rlp" @@ -513,7 +514,7 @@ func (tx *Transaction) IsSigningTransaction() bool { if len(data) != (32*2 + 4) { return false } - method := common.ToHex(data[0:4]) + method := hexutil.Encode(data[0:4]) return method == common.SignMethod } @@ -524,7 +525,7 @@ func (tx *Transaction) IsVotingTransaction() (bool, *common.Address) { } var end int data := tx.Data() - method := common.ToHex(data[0:4]) + method := hexutil.Encode(data[0:4]) if method == common.VoteMethod || method == common.ProposeMethod || method == common.ResignMethod { end = len(data) } else if method == common.UnvoteMethod { @@ -558,7 +559,7 @@ func (tx *Transaction) IsXDCXApplyTransaction() bool { if len(data) != (32 + 4) { return false } - method := common.ToHex(data[0:4]) + method := hexutil.Encode(data[0:4]) return method == common.XDCXApplyMethod } @@ -581,7 +582,7 @@ func (tx *Transaction) IsXDCZApplyTransaction() bool { if len(data) != (32 + 4) { return false } - method := common.ToHex(data[0:4]) + method := hexutil.Encode(data[0:4]) return method == common.XDCZApplyMethod } diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 936c08153421..6b391d03db0c 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -527,7 +527,7 @@ func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) er if err != nil { return err } - return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", common.ToHex(data)) + return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", hexutil.Encode(data)) } func toBlockNumArg(number *big.Int) string { @@ -550,7 +550,7 @@ func (ec *Client) SendOrderTransaction(ctx context.Context, tx *types.OrderTrans if err != nil { return err } - return ec.c.CallContext(ctx, nil, "XDCx_sendOrderRawTransaction", common.ToHex(data)) + return ec.c.CallContext(ctx, nil, "XDCx_sendOrderRawTransaction", hexutil.Encode(data)) } // SendLendingTransaction send lending to pool @@ -559,7 +559,7 @@ func (ec *Client) SendLendingTransaction(ctx context.Context, tx *types.LendingT if err != nil { return err } - return ec.c.CallContext(ctx, nil, "XDCx_sendLendingRawTransaction", common.ToHex(data)) + return ec.c.CallContext(ctx, nil, "XDCx_sendLendingRawTransaction", hexutil.Encode(data)) } func toCallArg(msg ethereum.CallMsg) interface{} { From dd318fc563bf0e369dee8aa622c503122e048f74 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 16 Dec 2024 19:24:01 +0800 Subject: [PATCH 417/479] common/hexutil: remove redundant conversion (#21903) --- common/hexutil/json_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/hexutil/json_test.go b/common/hexutil/json_test.go index 8a6b8643a1e9..ed7d6fad1a8e 100644 --- a/common/hexutil/json_test.go +++ b/common/hexutil/json_test.go @@ -88,7 +88,7 @@ func TestUnmarshalBytes(t *testing.T) { if !checkError(t, test.input, err, test.wantErr) { continue } - if !bytes.Equal(test.want.([]byte), []byte(v)) { + if !bytes.Equal(test.want.([]byte), v) { t.Errorf("input %s: value mismatch: got %x, want %x", test.input, &v, test.want) continue } From c2db0e42d7a3e68487864fc65034ccfc0a305f7c Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 17 Dec 2024 10:24:15 +0800 Subject: [PATCH 418/479] common: improve printing of Hash and Address (#21834) --- common/types.go | 94 +++++++++++++++++++----- common/types_test.go | 166 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 242 insertions(+), 18 deletions(-) diff --git a/common/types.go b/common/types.go index cf79aaa87027..d28075c273e1 100644 --- a/common/types.go +++ b/common/types.go @@ -130,10 +130,34 @@ func (h Hash) String() string { return h.Hex() } -// Format implements fmt.Formatter, forcing the byte slice to be formatted as is, -// without going through the stringer interface used for logging. +// Format implements fmt.Formatter. +// Hash supports the %v, %s, %v, %x, %X and %d format verbs. func (h Hash) Format(s fmt.State, c rune) { - fmt.Fprintf(s, "%"+string(c), h[:]) + hexb := make([]byte, 2+len(h)*2) + copy(hexb, "0x") + hex.Encode(hexb[2:], h[:]) + + switch c { + case 'x', 'X': + if !s.Flag('#') { + hexb = hexb[2:] + } + if c == 'X' { + hexb = bytes.ToUpper(hexb) + } + fallthrough + case 'v', 's': + s.Write(hexb) + case 'q': + q := []byte{'"'} + s.Write(q) + s.Write(hexb) + s.Write(q) + case 'd': + fmt.Fprint(s, ([len(h)]byte)(h)) + default: + fmt.Fprintf(s, "%%!%c(hash=%x)", c, h) + } } // UnmarshalText parses a hash in hex syntax. @@ -231,35 +255,69 @@ func (a Address) Hash() Hash { return BytesToHash(a[:]) } // Hex returns an EIP55-compliant hex string representation of the address. func (a Address) Hex() string { - unchecksummed := hex.EncodeToString(a[:]) + checksumed := a.checksumHex() + return "xdc" + string(checksumed[2:]) +} + +// String implements fmt.Stringer. +func (a Address) String() string { + return a.Hex() +} + +func (a *Address) checksumHex() []byte { + buf := a.hex() + + // compute checksum sha := sha3.NewLegacyKeccak256() - sha.Write([]byte(unchecksummed)) + sha.Write(buf[2:]) hash := sha.Sum(nil) - - result := []byte(unchecksummed) - for i := 0; i < len(result); i++ { - hashByte := hash[i/2] + for i := 2; i < len(buf); i++ { + hashByte := hash[(i-2)/2] if i%2 == 0 { hashByte = hashByte >> 4 } else { hashByte &= 0xf } - if result[i] > '9' && hashByte > 7 { - result[i] -= 32 + if buf[i] > '9' && hashByte > 7 { + buf[i] -= 32 } } - return "xdc" + string(result) + return buf[:] } -// String implements fmt.Stringer. -func (a Address) String() string { - return a.Hex() +func (a Address) hex() []byte { + var buf [len(a)*2 + 2]byte + copy(buf[:2], "0x") + hex.Encode(buf[2:], a[:]) + return buf[:] } -// Format implements fmt.Formatter, forcing the byte slice to be formatted as is, -// without going through the stringer interface used for logging. +// Format implements fmt.Formatter. +// Address supports the %v, %s, %q, %x, %X and %d format verbs. func (a Address) Format(s fmt.State, c rune) { - fmt.Fprintf(s, "%"+string(c), a[:]) + switch c { + case 'v', 's': + s.Write(a.checksumHex()) + case 'q': + q := []byte{'"'} + s.Write(q) + s.Write(a.checksumHex()) + s.Write(q) + case 'x', 'X': + // %x disables the checksum. + hex := a.hex() + if !s.Flag('#') { + hex = hex[2:] + } + if c == 'X' { + hex = bytes.ToUpper(hex) + } + s.Write(hex) + case 'd': + fmt.Fprint(s, ([len(a)]byte)(a)) + default: + fmt.Fprintf(s, "%%!%c(address=%x)", c, a) + } } // SetBytes sets the address to the value of b. diff --git a/common/types_test.go b/common/types_test.go index ea0fb22d2480..598286c08978 100644 --- a/common/types_test.go +++ b/common/types_test.go @@ -17,7 +17,9 @@ package common import ( + "bytes" "encoding/json" + "fmt" "math/big" "strings" "testing" @@ -156,6 +158,170 @@ func BenchmarkAddressHex(b *testing.B) { } } +func TestAddress_Format(t *testing.T) { + b := []byte{ + 0xb2, 0x6f, 0x2b, 0x34, 0x2a, 0xab, 0x24, 0xbc, 0xf6, 0x3e, + 0xa2, 0x18, 0xc6, 0xa9, 0x27, 0x4d, 0x30, 0xab, 0x9a, 0x15, + } + var addr Address + addr.SetBytes(b) + + tests := []struct { + name string + out string + want string + }{ + { + name: "println", + out: fmt.Sprintln(addr), + want: "0xB26f2b342AAb24BCF63ea218c6A9274D30Ab9A15\n", + }, + { + name: "print", + out: fmt.Sprint(addr), + want: "0xB26f2b342AAb24BCF63ea218c6A9274D30Ab9A15", + }, + { + name: "printf-s", + out: func() string { + buf := new(bytes.Buffer) + fmt.Fprintf(buf, "%s", addr) + return buf.String() + }(), + want: "0xB26f2b342AAb24BCF63ea218c6A9274D30Ab9A15", + }, + { + name: "printf-q", + out: fmt.Sprintf("%q", addr), + want: `"0xB26f2b342AAb24BCF63ea218c6A9274D30Ab9A15"`, + }, + { + name: "printf-x", + out: fmt.Sprintf("%x", addr), + want: "b26f2b342aab24bcf63ea218c6a9274d30ab9a15", + }, + { + name: "printf-X", + out: fmt.Sprintf("%X", addr), + want: "B26F2B342AAB24BCF63EA218C6A9274D30AB9A15", + }, + { + name: "printf-#x", + out: fmt.Sprintf("%#x", addr), + want: "0xb26f2b342aab24bcf63ea218c6a9274d30ab9a15", + }, + { + name: "printf-v", + out: fmt.Sprintf("%v", addr), + want: "0xB26f2b342AAb24BCF63ea218c6A9274D30Ab9A15", + }, + // The original default formatter for byte slice + { + name: "printf-d", + out: fmt.Sprintf("%d", addr), + want: "[178 111 43 52 42 171 36 188 246 62 162 24 198 169 39 77 48 171 154 21]", + }, + // Invalid format char. + { + name: "printf-t", + out: fmt.Sprintf("%t", addr), + want: "%!t(address=b26f2b342aab24bcf63ea218c6a9274d30ab9a15)", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.out != tt.want { + t.Errorf("%s does not render as expected:\n got %s\nwant %s", tt.name, tt.out, tt.want) + } + }) + } +} + +func TestHash_Format(t *testing.T) { + var hash Hash + hash.SetBytes([]byte{ + 0xb2, 0x6f, 0x2b, 0x34, 0x2a, 0xab, 0x24, 0xbc, 0xf6, 0x3e, + 0xa2, 0x18, 0xc6, 0xa9, 0x27, 0x4d, 0x30, 0xab, 0x9a, 0x15, + 0xa2, 0x18, 0xc6, 0xa9, 0x27, 0x4d, 0x30, 0xab, 0x9a, 0x15, + 0x10, 0x00, + }) + + tests := []struct { + name string + out string + want string + }{ + { + name: "println", + out: fmt.Sprintln(hash), + want: "0xb26f2b342aab24bcf63ea218c6a9274d30ab9a15a218c6a9274d30ab9a151000\n", + }, + { + name: "print", + out: fmt.Sprint(hash), + want: "0xb26f2b342aab24bcf63ea218c6a9274d30ab9a15a218c6a9274d30ab9a151000", + }, + { + name: "printf-s", + out: func() string { + buf := new(bytes.Buffer) + fmt.Fprintf(buf, "%s", hash) + return buf.String() + }(), + want: "0xb26f2b342aab24bcf63ea218c6a9274d30ab9a15a218c6a9274d30ab9a151000", + }, + { + name: "printf-q", + out: fmt.Sprintf("%q", hash), + want: `"0xb26f2b342aab24bcf63ea218c6a9274d30ab9a15a218c6a9274d30ab9a151000"`, + }, + { + name: "printf-x", + out: fmt.Sprintf("%x", hash), + want: "b26f2b342aab24bcf63ea218c6a9274d30ab9a15a218c6a9274d30ab9a151000", + }, + { + name: "printf-X", + out: fmt.Sprintf("%X", hash), + want: "B26F2B342AAB24BCF63EA218C6A9274D30AB9A15A218C6A9274D30AB9A151000", + }, + { + name: "printf-#x", + out: fmt.Sprintf("%#x", hash), + want: "0xb26f2b342aab24bcf63ea218c6a9274d30ab9a15a218c6a9274d30ab9a151000", + }, + { + name: "printf-#X", + out: fmt.Sprintf("%#X", hash), + want: "0XB26F2B342AAB24BCF63EA218C6A9274D30AB9A15A218C6A9274D30AB9A151000", + }, + { + name: "printf-v", + out: fmt.Sprintf("%v", hash), + want: "0xb26f2b342aab24bcf63ea218c6a9274d30ab9a15a218c6a9274d30ab9a151000", + }, + // The original default formatter for byte slice + { + name: "printf-d", + out: fmt.Sprintf("%d", hash), + want: "[178 111 43 52 42 171 36 188 246 62 162 24 198 169 39 77 48 171 154 21 162 24 198 169 39 77 48 171 154 21 16 0]", + }, + // Invalid format char. + { + name: "printf-t", + out: fmt.Sprintf("%t", hash), + want: "%!t(hash=b26f2b342aab24bcf63ea218c6a9274d30ab9a15a218c6a9274d30ab9a151000)", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.out != tt.want { + t.Errorf("%s does not render as expected:\n got %s\nwant %s", tt.name, tt.out, tt.want) + } + }) + } +} + func TestRemoveItemInArray(t *testing.T) { array := []Address{HexToAddress("0x0000003"), HexToAddress("0x0000001"), HexToAddress("0x0000002"), HexToAddress("0x0000003")} remove := []Address{HexToAddress("0x0000002"), HexToAddress("0x0000004"), HexToAddress("0x0000003")} From f01e639f6fd6c2dd34fddfba5f20fc83a48a695d Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 5 Jan 2021 14:48:22 +0100 Subject: [PATCH 419/479] common/compiler: fix parsing of solc output with solidity v.0.8.0 (#22092) Solidity 0.8.0 changes the way that output is marshalled. This patch allows to parse both the legacy format used previously and the new format. See also https://docs.soliditylang.org/en/breaking/080-breaking-changes.html#interface-changes --- common/compiler/solidity.go | 50 +++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go index f0363dc92b18..a023b5685c02 100644 --- a/common/compiler/solidity.go +++ b/common/compiler/solidity.go @@ -44,6 +44,20 @@ type solcOutput struct { Version string } +// solidity v.0.8 changes the way ABI, Devdoc and Userdoc are serialized +type solcOutputV8 struct { + Contracts map[string]struct { + BinRuntime string `json:"bin-runtime"` + SrcMapRuntime string `json:"srcmap-runtime"` + Bin, SrcMap, Metadata string + Abi interface{} + Devdoc interface{} + Userdoc interface{} + Hashes map[string]string + } + Version string +} + func (s *Solidity) makeArgs() []string { p := []string{ "--combined-json", "bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc", @@ -125,7 +139,6 @@ func (s *Solidity) run(cmd *exec.Cmd, source string) (map[string]*Contract, erro if err := cmd.Run(); err != nil { return nil, fmt.Errorf("solc: %v\n%s", err, stderr.Bytes()) } - return ParseCombinedJSON(stdout.Bytes(), source, s.Version, s.Version, strings.Join(s.makeArgs(), " ")) } @@ -141,7 +154,8 @@ func (s *Solidity) run(cmd *exec.Cmd, source string) (map[string]*Contract, erro func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion string, compilerVersion string, compilerOptions string) (map[string]*Contract, error) { var output solcOutput if err := json.Unmarshal(combinedJSON, &output); err != nil { - return nil, err + // Try to parse the output with the new solidity v.0.8.0 rules + return parseCombinedJSONV8(combinedJSON, source, languageVersion, compilerVersion, compilerOptions) } // Compilation succeeded, assemble and return the contracts. contracts := make(map[string]*Contract) @@ -176,3 +190,35 @@ func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion strin } return contracts, nil } + +// parseCombinedJSONV8 parses the direct output of solc --combined-output +// and parses it using the rules from solidity v.0.8.0 and later. +func parseCombinedJSONV8(combinedJSON []byte, source string, languageVersion string, compilerVersion string, compilerOptions string) (map[string]*Contract, error) { + var output solcOutputV8 + if err := json.Unmarshal(combinedJSON, &output); err != nil { + return nil, err + } + // Compilation succeeded, assemble and return the contracts. + contracts := make(map[string]*Contract) + for name, info := range output.Contracts { + contracts[name] = &Contract{ + Code: "0x" + info.Bin, + RuntimeCode: "0x" + info.BinRuntime, + Hashes: info.Hashes, + Info: ContractInfo{ + Source: source, + Language: "Solidity", + LanguageVersion: languageVersion, + CompilerVersion: compilerVersion, + CompilerOptions: compilerOptions, + SrcMap: info.SrcMap, + SrcMapRuntime: info.SrcMapRuntime, + AbiDefinition: info.Abi, + UserDoc: info.Userdoc, + DeveloperDoc: info.Devdoc, + Metadata: info.Metadata, + }, + } + } + return contracts, nil +} From d852f8cf88690847a272c6efc048e2d433204a63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Mon, 11 Jan 2021 10:31:03 +0200 Subject: [PATCH 420/479] common/prque: pull in tests and benchmarks from upstream (#22157) --- common/prque/prque_test.go | 130 ++++++++++++++++++++++++++++++++++++ common/prque/sstack_test.go | 100 +++++++++++++++++++++++++++ 2 files changed, 230 insertions(+) create mode 100644 common/prque/prque_test.go create mode 100644 common/prque/sstack_test.go diff --git a/common/prque/prque_test.go b/common/prque/prque_test.go new file mode 100644 index 000000000000..1cffcebad437 --- /dev/null +++ b/common/prque/prque_test.go @@ -0,0 +1,130 @@ +// CookieJar - A contestant's algorithm toolbox +// Copyright (c) 2013 Peter Szilagyi. All rights reserved. +// +// CookieJar is dual licensed: use of this source code is governed by a BSD +// license that can be found in the LICENSE file. Alternatively, the CookieJar +// toolbox may be used in accordance with the terms and conditions contained +// in a signed written agreement between you and the author(s). + +package prque + +import ( + "math/rand" + "testing" +) + +func TestPrque(t *testing.T) { + // Generate a batch of random data and a specific priority order + size := 16 * blockSize + prio := rand.Perm(size) + data := make([]int, size) + for i := 0; i < size; i++ { + data[i] = rand.Int() + } + queue := New(nil) + for rep := 0; rep < 2; rep++ { + // Fill a priority queue with the above data + for i := 0; i < size; i++ { + queue.Push(data[i], int64(prio[i])) + if queue.Size() != i+1 { + t.Errorf("queue size mismatch: have %v, want %v.", queue.Size(), i+1) + } + } + // Create a map the values to the priorities for easier verification + dict := make(map[int64]int) + for i := 0; i < size; i++ { + dict[int64(prio[i])] = data[i] + } + // Pop out the elements in priority order and verify them + prevPrio := int64(size + 1) + for !queue.Empty() { + val, prio := queue.Pop() + if prio > prevPrio { + t.Errorf("invalid priority order: %v after %v.", prio, prevPrio) + } + prevPrio = prio + if val != dict[prio] { + t.Errorf("push/pop mismatch: have %v, want %v.", val, dict[prio]) + } + delete(dict, prio) + } + } +} + +func TestReset(t *testing.T) { + // Generate a batch of random data and a specific priority order + size := 16 * blockSize + prio := rand.Perm(size) + data := make([]int, size) + for i := 0; i < size; i++ { + data[i] = rand.Int() + } + queue := New(nil) + for rep := 0; rep < 2; rep++ { + // Fill a priority queue with the above data + for i := 0; i < size; i++ { + queue.Push(data[i], int64(prio[i])) + if queue.Size() != i+1 { + t.Errorf("queue size mismatch: have %v, want %v.", queue.Size(), i+1) + } + } + // Create a map the values to the priorities for easier verification + dict := make(map[int64]int) + for i := 0; i < size; i++ { + dict[int64(prio[i])] = data[i] + } + // Pop out half the elements in priority order and verify them + prevPrio := int64(size + 1) + for i := 0; i < size/2; i++ { + val, prio := queue.Pop() + if prio > prevPrio { + t.Errorf("invalid priority order: %v after %v.", prio, prevPrio) + } + prevPrio = prio + if val != dict[prio] { + t.Errorf("push/pop mismatch: have %v, want %v.", val, dict[prio]) + } + delete(dict, prio) + } + // Reset and ensure it's empty + queue.Reset() + if !queue.Empty() { + t.Errorf("priority queue not empty after reset: %v", queue) + } + } +} + +func BenchmarkPush(b *testing.B) { + // Create some initial data + data := make([]int, b.N) + prio := make([]int64, b.N) + for i := 0; i < len(data); i++ { + data[i] = rand.Int() + prio[i] = rand.Int63() + } + // Execute the benchmark + b.ResetTimer() + queue := New(nil) + for i := 0; i < len(data); i++ { + queue.Push(data[i], prio[i]) + } +} + +func BenchmarkPop(b *testing.B) { + // Create some initial data + data := make([]int, b.N) + prio := make([]int64, b.N) + for i := 0; i < len(data); i++ { + data[i] = rand.Int() + prio[i] = rand.Int63() + } + queue := New(nil) + for i := 0; i < len(data); i++ { + queue.Push(data[i], prio[i]) + } + // Execute the benchmark + b.ResetTimer() + for !queue.Empty() { + queue.Pop() + } +} diff --git a/common/prque/sstack_test.go b/common/prque/sstack_test.go new file mode 100644 index 000000000000..2ff093579da9 --- /dev/null +++ b/common/prque/sstack_test.go @@ -0,0 +1,100 @@ +// CookieJar - A contestant's algorithm toolbox +// Copyright (c) 2013 Peter Szilagyi. All rights reserved. +// +// CookieJar is dual licensed: use of this source code is governed by a BSD +// license that can be found in the LICENSE file. Alternatively, the CookieJar +// toolbox may be used in accordance with the terms and conditions contained +// in a signed written agreement between you and the author(s). + +package prque + +import ( + "math/rand" + "sort" + "testing" +) + +func TestSstack(t *testing.T) { + // Create some initial data + size := 16 * blockSize + data := make([]*item, size) + for i := 0; i < size; i++ { + data[i] = &item{rand.Int(), rand.Int63()} + } + stack := newSstack(nil) + for rep := 0; rep < 2; rep++ { + // Push all the data into the stack, pop out every second + secs := []*item{} + for i := 0; i < size; i++ { + stack.Push(data[i]) + if i%2 == 0 { + secs = append(secs, stack.Pop().(*item)) + } + } + rest := []*item{} + for stack.Len() > 0 { + rest = append(rest, stack.Pop().(*item)) + } + // Make sure the contents of the resulting slices are ok + for i := 0; i < size; i++ { + if i%2 == 0 && data[i] != secs[i/2] { + t.Errorf("push/pop mismatch: have %v, want %v.", secs[i/2], data[i]) + } + if i%2 == 1 && data[i] != rest[len(rest)-i/2-1] { + t.Errorf("push/pop mismatch: have %v, want %v.", rest[len(rest)-i/2-1], data[i]) + } + } + } +} + +func TestSstackSort(t *testing.T) { + // Create some initial data + size := 16 * blockSize + data := make([]*item, size) + for i := 0; i < size; i++ { + data[i] = &item{rand.Int(), int64(i)} + } + // Push all the data into the stack + stack := newSstack(nil) + for _, val := range data { + stack.Push(val) + } + // Sort and pop the stack contents (should reverse the order) + sort.Sort(stack) + for _, val := range data { + out := stack.Pop() + if out != val { + t.Errorf("push/pop mismatch after sort: have %v, want %v.", out, val) + } + } +} + +func TestSstackReset(t *testing.T) { + // Create some initial data + size := 16 * blockSize + data := make([]*item, size) + for i := 0; i < size; i++ { + data[i] = &item{rand.Int(), rand.Int63()} + } + stack := newSstack(nil) + for rep := 0; rep < 2; rep++ { + // Push all the data into the stack, pop out every second + secs := []*item{} + for i := 0; i < size; i++ { + stack.Push(data[i]) + if i%2 == 0 { + secs = append(secs, stack.Pop().(*item)) + } + } + // Reset and verify both pulled and stack contents + stack.Reset() + if stack.Len() != 0 { + t.Errorf("stack not empty after reset: %v", stack) + } + for i := 0; i < size; i++ { + if i%2 == 0 && data[i] != secs[i/2] { + t.Errorf("push/pop mismatch: have %v, want %v.", secs[i/2], data[i]) + } + } + } +} From 6f19ace5e2a9a76ac6168c73af11d1d32a0e1d9c Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 17 Dec 2024 11:02:22 +0800 Subject: [PATCH 421/479] common/mclock: remove dependency on github.com/aristanetworks/goarista (#22211) --- common/mclock/mclock.go | 12 ++++++++---- common/mclock/mclock.s | 1 + go.mod | 1 - go.sum | 2 -- 4 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 common/mclock/mclock.s diff --git a/common/mclock/mclock.go b/common/mclock/mclock.go index 3aca257cb368..c05738cf2bf0 100644 --- a/common/mclock/mclock.go +++ b/common/mclock/mclock.go @@ -20,15 +20,19 @@ package mclock import ( "time" - "github.com/aristanetworks/goarista/monotime" + _ "unsafe" // for go:linkname ) +//go:noescape +//go:linkname nanotime runtime.nanotime +func nanotime() int64 + // AbsTime represents absolute monotonic time. -type AbsTime time.Duration +type AbsTime int64 // Now returns the current absolute monotonic time. func Now() AbsTime { - return AbsTime(monotime.Now()) + return AbsTime(nanotime()) } // Add returns t + d as absolute time. @@ -74,7 +78,7 @@ type System struct{} // Now returns the current monotonic time. func (c System) Now() AbsTime { - return AbsTime(monotime.Now()) + return Now() } // Sleep blocks for the given duration. diff --git a/common/mclock/mclock.s b/common/mclock/mclock.s new file mode 100644 index 000000000000..99a7a878f0da --- /dev/null +++ b/common/mclock/mclock.s @@ -0,0 +1 @@ +// This file exists in order to be able to use go:linkname. diff --git a/go.mod b/go.mod index 23a0995a49e8..9adb083eca74 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.22 require ( github.com/VictoriaMetrics/fastcache v1.12.2 - github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98 github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 github.com/cespare/cp v1.1.1 github.com/davecgh/go-spew v1.1.1 diff --git a/go.sum b/go.sum index bb2ab2b777cb..e59acf391656 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,6 @@ github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjC github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98 h1:7buXGE+m4OPjyo8rUJgA8RmARNMq+m99JJLR+Z+ZWN0= -github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98/go.mod h1:DLTg9Gp4FAXF5EpqYBQnUeBbRsNLY7b2HR94TE5XQtE= github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8= github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 h1:Eey/GGQ/E5Xp1P2Lyx1qj007hLZfbi0+CoVeJruGCtI= From e52587df53c7df46b11099266f8ca174448abfa3 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 17 Dec 2024 11:22:11 +0800 Subject: [PATCH 422/479] common: do not pass current time as param in priority funcs (#22183) --- common/prque/lazyqueue.go | 5 ++--- common/prque/lazyqueue_test.go | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/common/prque/lazyqueue.go b/common/prque/lazyqueue.go index fc6b9b2f26e9..1a9c1e0007c8 100644 --- a/common/prque/lazyqueue.go +++ b/common/prque/lazyqueue.go @@ -49,7 +49,7 @@ type LazyQueue struct { } type ( - PriorityCallback func(data interface{}, now mclock.AbsTime) int64 // actual priority callback + PriorityCallback func(data interface{}) int64 // actual priority callback MaxPriorityCallback func(data interface{}, until mclock.AbsTime) int64 // estimated maximum priority callback ) @@ -140,11 +140,10 @@ func (q *LazyQueue) peekIndex() int { // Pop multiple times. Popped items are passed to the callback. MultiPop returns // when the callback returns false or there are no more items to pop. func (q *LazyQueue) MultiPop(callback func(data interface{}, priority int64) bool) { - now := q.clock.Now() nextIndex := q.peekIndex() for nextIndex != -1 { data := heap.Pop(q.queue[nextIndex]).(*item).value - heap.Push(q.popQueue, &item{data, q.priority(data, now)}) + heap.Push(q.popQueue, &item{data, q.priority(data)}) nextIndex = q.peekIndex() for q.popQueue.Len() != 0 && (nextIndex == -1 || q.queue[nextIndex].blocks[0][0].priority < q.popQueue.blocks[0][0].priority) { i := heap.Pop(q.popQueue).(*item) diff --git a/common/prque/lazyqueue_test.go b/common/prque/lazyqueue_test.go index bb43bd06d79f..090d7120e311 100644 --- a/common/prque/lazyqueue_test.go +++ b/common/prque/lazyqueue_test.go @@ -40,7 +40,7 @@ type lazyItem struct { index int } -func testPriority(a interface{}, now mclock.AbsTime) int64 { +func testPriority(a interface{}) int64 { return a.(*lazyItem).p } @@ -56,7 +56,6 @@ func testSetIndex(a interface{}, i int) { } func TestLazyQueue(t *testing.T) { - rand.Seed(time.Now().UnixNano()) clock := &mclock.Simulated{} q := NewLazyQueue(testSetIndex, testPriority, testMaxPriority, clock, testQueueRefresh) From e2eb22dcac3dea0d4e5266c637e7a9ed995852b3 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 17 Dec 2024 11:37:21 +0800 Subject: [PATCH 423/479] common/prque: make Prque wrap-around priority handling optional (#22495) --- common/prque/lazyqueue.go | 6 +++--- common/prque/prque.go | 11 ++++++++--- common/prque/sstack.go | 20 +++++++++++++------- common/prque/sstack_test.go | 6 +++--- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/common/prque/lazyqueue.go b/common/prque/lazyqueue.go index 1a9c1e0007c8..85d7bf6eee7e 100644 --- a/common/prque/lazyqueue.go +++ b/common/prque/lazyqueue.go @@ -56,7 +56,7 @@ type ( // NewLazyQueue creates a new lazy queue func NewLazyQueue(setIndex SetIndexCallback, priority PriorityCallback, maxPriority MaxPriorityCallback, clock mclock.Clock, refreshPeriod time.Duration) *LazyQueue { q := &LazyQueue{ - popQueue: newSstack(nil), + popQueue: newSstack(nil, false), setIndex: setIndex, priority: priority, maxPriority: maxPriority, @@ -72,8 +72,8 @@ func NewLazyQueue(setIndex SetIndexCallback, priority PriorityCallback, maxPrior // Reset clears the contents of the queue func (q *LazyQueue) Reset() { - q.queue[0] = newSstack(q.setIndex0) - q.queue[1] = newSstack(q.setIndex1) + q.queue[0] = newSstack(q.setIndex0, false) + q.queue[1] = newSstack(q.setIndex1, false) } // Refresh performs queue re-evaluation if necessary diff --git a/common/prque/prque.go b/common/prque/prque.go index 3cc5a1adaf15..fb02e3418c28 100755 --- a/common/prque/prque.go +++ b/common/prque/prque.go @@ -28,7 +28,12 @@ type Prque struct { // New creates a new priority queue. func New(setIndex SetIndexCallback) *Prque { - return &Prque{newSstack(setIndex)} + return &Prque{newSstack(setIndex, false)} +} + +// NewWrapAround creates a new priority queue with wrap-around priority handling. +func NewWrapAround(setIndex SetIndexCallback) *Prque { + return &Prque{newSstack(setIndex, true)} } // Pushes a value with a given priority into the queue, expanding if necessary. @@ -36,13 +41,13 @@ func (p *Prque) Push(data interface{}, priority int64) { heap.Push(p.cont, &item{data, priority}) } -// Peek returns the value with the greates priority but does not pop it off. +// Peek returns the value with the greatest priority but does not pop it off. func (p *Prque) Peek() (interface{}, int64) { item := p.cont.blocks[0][0] return item.value, item.priority } -// Pops the value with the greates priority off the stack and returns it. +// Pops the value with the greatest priority off the stack and returns it. // Currently no shrinking is done. func (p *Prque) Pop() (interface{}, int64) { item := heap.Pop(p.cont).(*item) diff --git a/common/prque/sstack.go b/common/prque/sstack.go index 8518af54ff1a..b06a95413df0 100755 --- a/common/prque/sstack.go +++ b/common/prque/sstack.go @@ -31,22 +31,24 @@ type SetIndexCallback func(data interface{}, index int) // the stack (heap) functionality and the Len, Less and Swap methods for the // sortability requirements of the heaps. type sstack struct { - setIndex SetIndexCallback - size int - capacity int - offset int + setIndex SetIndexCallback + size int + capacity int + offset int + wrapAround bool blocks [][]*item active []*item } // Creates a new, empty stack. -func newSstack(setIndex SetIndexCallback) *sstack { +func newSstack(setIndex SetIndexCallback, wrapAround bool) *sstack { result := new(sstack) result.setIndex = setIndex result.active = make([]*item, blockSize) result.blocks = [][]*item{result.active} result.capacity = blockSize + result.wrapAround = wrapAround return result } @@ -94,7 +96,11 @@ func (s *sstack) Len() int { // Compares the priority of two elements of the stack (higher is first). // Required by sort.Interface. func (s *sstack) Less(i, j int) bool { - return (s.blocks[i/blockSize][i%blockSize].priority - s.blocks[j/blockSize][j%blockSize].priority) > 0 + a, b := s.blocks[i/blockSize][i%blockSize].priority, s.blocks[j/blockSize][j%blockSize].priority + if s.wrapAround { + return a-b > 0 + } + return a > b } // Swaps two elements in the stack. Required by sort.Interface. @@ -110,5 +116,5 @@ func (s *sstack) Swap(i, j int) { // Resets the stack, effectively clearing its contents. func (s *sstack) Reset() { - *s = *newSstack(s.setIndex) + *s = *newSstack(s.setIndex, false) } diff --git a/common/prque/sstack_test.go b/common/prque/sstack_test.go index 2ff093579da9..bc6298979cbc 100644 --- a/common/prque/sstack_test.go +++ b/common/prque/sstack_test.go @@ -21,7 +21,7 @@ func TestSstack(t *testing.T) { for i := 0; i < size; i++ { data[i] = &item{rand.Int(), rand.Int63()} } - stack := newSstack(nil) + stack := newSstack(nil, false) for rep := 0; rep < 2; rep++ { // Push all the data into the stack, pop out every second secs := []*item{} @@ -55,7 +55,7 @@ func TestSstackSort(t *testing.T) { data[i] = &item{rand.Int(), int64(i)} } // Push all the data into the stack - stack := newSstack(nil) + stack := newSstack(nil, false) for _, val := range data { stack.Push(val) } @@ -76,7 +76,7 @@ func TestSstackReset(t *testing.T) { for i := 0; i < size; i++ { data[i] = &item{rand.Int(), rand.Int63()} } - stack := newSstack(nil) + stack := newSstack(nil, false) for rep := 0; rep < 2; rep++ { // Push all the data into the stack, pop out every second secs := []*item{} From fb17f56852a26a5594d44863dc80dbad1d87f2b8 Mon Sep 17 00:00:00 2001 From: Paul-Armand Verhaegen Date: Thu, 10 Jun 2021 09:53:23 +0200 Subject: [PATCH 424/479] common: rename unused function with typo (#23025) This function is not used in the code base, so probably safe to do rename, or remove in its entirety, but I'm assuming the logic from the original creator still applies so rename probably better. --- common/debug.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/debug.go b/common/debug.go index 3ed8c1c796a2..8ad7ead5488e 100644 --- a/common/debug.go +++ b/common/debug.go @@ -37,8 +37,8 @@ func Report(extra ...interface{}) { fmt.Fprintln(os.Stderr, "#### BUG! PLEASE REPORT ####") } -// PrintDepricationWarning prinst the given string in a box using fmt.Println. -func PrintDepricationWarning(str string) { +// PrintDeprecationWarning prints the given string in a box using fmt.Println. +func PrintDeprecationWarning(str string) { line := strings.Repeat("#", len(str)+4) emptyLine := strings.Repeat(" ", len(str)) fmt.Printf(` From 4db72a0548047f7c4634eac135ade7e240333c85 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Tue, 31 Aug 2021 10:22:20 +0200 Subject: [PATCH 425/479] common: fixes format verb (#23495) --- common/types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/types.go b/common/types.go index d28075c273e1..24e8338af70f 100644 --- a/common/types.go +++ b/common/types.go @@ -131,7 +131,7 @@ func (h Hash) String() string { } // Format implements fmt.Formatter. -// Hash supports the %v, %s, %v, %x, %X and %d format verbs. +// Hash supports the %v, %s, %q, %x, %X and %d format verbs. func (h Hash) Format(s fmt.State, c rune) { hexb := make([]byte, 2+len(h)*2) copy(hexb, "0x") From 49dc5e85e19e477811f4e4ee0f47cd080b480858 Mon Sep 17 00:00:00 2001 From: lmittmann Date: Thu, 21 Oct 2021 11:43:23 +0200 Subject: [PATCH 426/479] common/hexutil: improve performance of EncodeBig (#23780) - use Text instead of fmt.Sprintf - reduced allocs from 6 to 2 - improved speed --- common/hexutil/hexutil.go | 11 ++++++----- common/hexutil/hexutil_test.go | 12 ++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/common/hexutil/hexutil.go b/common/hexutil/hexutil.go index 02c488a3f178..81e68046500b 100644 --- a/common/hexutil/hexutil.go +++ b/common/hexutil/hexutil.go @@ -18,7 +18,7 @@ Package hexutil implements hex encoding with 0x prefix. This encoding is used by the Ethereum RPC API to transport binary data in JSON payloads. -Encoding Rules +# Encoding Rules All hex data must have prefix "0x". @@ -175,13 +175,14 @@ func MustDecodeBig(input string) *big.Int { } // EncodeBig encodes bigint as a hex string with 0x prefix. -// The sign of the integer is ignored. func EncodeBig(bigint *big.Int) string { - nbits := bigint.BitLen() - if nbits == 0 { + if sign := bigint.Sign(); sign == 0 { return "0x0" + } else if sign > 0 { + return "0x" + bigint.Text(16) + } else { + return "-0x" + bigint.Text(16)[1:] } - return fmt.Sprintf("%#x", bigint) } func has0xPrefix(input string) bool { diff --git a/common/hexutil/hexutil_test.go b/common/hexutil/hexutil_test.go index ed6fccc3ca6f..f2b800d82c9d 100644 --- a/common/hexutil/hexutil_test.go +++ b/common/hexutil/hexutil_test.go @@ -201,3 +201,15 @@ func TestDecodeUint64(t *testing.T) { } } } + +func BenchmarkEncodeBig(b *testing.B) { + for _, bench := range encodeBigTests { + b.Run(bench.want, func(b *testing.B) { + b.ReportAllocs() + bigint := bench.input.(*big.Int) + for i := 0; i < b.N; i++ { + EncodeBig(bigint) + } + }) + } +} From 69ea5327b805adda15ca667e96e7058b348b4586 Mon Sep 17 00:00:00 2001 From: zhiqiangxu <652732310@qq.com> Date: Wed, 30 Mar 2022 04:38:59 +0800 Subject: [PATCH 427/479] common/compiler: add extra include paths to solidity compiler (#24541) This PR adds a ExtraAllowedPath field to Solidity and exposes two APIs: CompileSource and CompileFiles, which were hidden inside CompileSolidityString and CompileSolidity before. --- common/compiler/solidity.go | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go index a023b5685c02..eb7fd079b380 100644 --- a/common/compiler/solidity.go +++ b/common/compiler/solidity.go @@ -31,6 +31,7 @@ import ( type Solidity struct { Path, Version, FullVersion string Major, Minor, Patch int + ExtraAllowedPath []string } // --combined-output format @@ -58,11 +59,19 @@ type solcOutputV8 struct { Version string } +func (s *Solidity) allowedPaths() string { + paths := []string{".", "./", "../"} // default to support relative paths + if len(s.ExtraAllowedPath) > 0 { + paths = append(paths, s.ExtraAllowedPath...) + } + return strings.Join(paths, ", ") +} + func (s *Solidity) makeArgs() []string { p := []string{ "--combined-json", "bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc", - "--optimize", // code optimizer switched on - "--allow-paths", "., ./, ../", // default to support relative paths + "--optimize", // code optimizer switched on + "--allow-paths", s.allowedPaths(), } if s.Major > 0 || s.Minor > 4 || s.Patch > 6 { p[1] += ",metadata,hashes" @@ -108,10 +117,7 @@ func CompileSolidityString(solc, source string) (map[string]*Contract, error) { if err != nil { return nil, err } - args := append(s.makeArgs(), "--") - cmd := exec.Command(s.Path, append(args, "-")...) - cmd.Stdin = strings.NewReader(source) - return s.run(cmd, source) + return s.CompileSource(source) } // CompileSolidity compiles all given Solidity source files. @@ -119,11 +125,25 @@ func CompileSolidity(solc string, sourcefiles ...string) (map[string]*Contract, if len(sourcefiles) == 0 { return nil, errors.New("solc: no source files") } - source, err := slurpFiles(sourcefiles) + s, err := SolidityVersion(solc) if err != nil { return nil, err } - s, err := SolidityVersion(solc) + + return s.CompileFiles(sourcefiles...) +} + +// CompileSource builds and returns all the contracts contained within a source string. +func (s *Solidity) CompileSource(source string) (map[string]*Contract, error) { + args := append(s.makeArgs(), "--") + cmd := exec.Command(s.Path, append(args, "-")...) + cmd.Stdin = strings.NewReader(source) + return s.run(cmd, source) +} + +// CompileFiles compiles all given Solidity source files. +func (s *Solidity) CompileFiles(sourcefiles ...string) (map[string]*Contract, error) { + source, err := slurpFiles(sourcefiles) if err != nil { return nil, err } From d6d6906881e88bf7bc5011965738c7266878fa3b Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 17 Dec 2024 15:31:54 +0800 Subject: [PATCH 428/479] internal: add db operations to api (#24739) --- common/bytes.go | 12 ++++++++++++ internal/ethapi/api.go | 9 +++++++++ internal/web3ext/web3ext.go | 5 +++++ 3 files changed, 26 insertions(+) diff --git a/common/bytes.go b/common/bytes.go index 3aaf97bd68c7..fae05026a3d9 100644 --- a/common/bytes.go +++ b/common/bytes.go @@ -19,6 +19,9 @@ package common import ( "encoding/hex" + "errors" + + "github.com/XinFinOrg/XDPoSChain/common/hexutil" ) // FromHex returns the bytes represented by the hexadecimal string s. @@ -99,6 +102,15 @@ func Hex2BytesFixed(str string, flen int) []byte { return hh } +// ParseHexOrString tries to hexdecode b, but if the prefix is missing, it instead just returns the raw bytes +func ParseHexOrString(str string) ([]byte, error) { + b, err := hexutil.Decode(str) + if errors.Is(err, hexutil.ErrMissingPrefix) { + return []byte(str), nil + } + return b, err +} + // RightPadBytes zero-pads slice to the right up to length l. func RightPadBytes(slice []byte, l int) []byte { if l <= len(slice) { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 47d50b864f3d..f88c79d4940f 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -3499,6 +3499,15 @@ func (api *PrivateDebugAPI) SetHead(number hexutil.Uint64) { api.b.SetHead(uint64(number)) } +// DbGet returns the raw value of a key stored in the database. +func (api *PrivateDebugAPI) DbGet(key string) (hexutil.Bytes, error) { + blob, err := common.ParseHexOrString(key) + if err != nil { + return nil, err + } + return api.b.ChainDb().Get(blob) +} + // PublicNetAPI offers network related RPC methods type PublicNetAPI struct { net *p2p.Server diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index f726b5d6205b..68ec05444a00 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -470,6 +470,11 @@ web3._extend({ params: 2, inputFormatter:[null, null], }), + new web3._extend.Method({ + name: 'dbGet', + call: 'debug_dbGet', + params: 1 + }), ], properties: [] }); From ebc036682e23734537c8a2cd30dce554966a85a7 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 17 Dec 2024 16:05:35 +0800 Subject: [PATCH 429/479] common/compiler, cmd/abigen: remove solc/vyper compiler integration (#24936) --- cmd/abigen/main.go | 38 +------- common/compiler/helpers.go | 20 ----- common/compiler/solidity.go | 117 +------------------------ common/compiler/solidity_test.go | 79 ----------------- common/compiler/test.v.py | 3 - common/compiler/test_bad.v.py | 3 - common/compiler/vyper.go | 144 ------------------------------- common/compiler/vyper_test.go | 71 --------------- 8 files changed, 3 insertions(+), 472 deletions(-) delete mode 100644 common/compiler/solidity_test.go delete mode 100644 common/compiler/test.v.py delete mode 100644 common/compiler/test_bad.v.py delete mode 100644 common/compiler/vyper.go delete mode 100644 common/compiler/vyper_test.go diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index 6bd6efb4c249..9cdc47d61ff1 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -57,24 +57,6 @@ var ( Name: "combined-json", Usage: "Path to the combined-json file generated by compiler", } - solFlag = &cli.StringFlag{ - Name: "sol", - Usage: "Path to the Ethereum contract Solidity source to build and bind", - } - solcFlag = &cli.StringFlag{ - Name: "solc", - Usage: "Solidity compiler to use if source builds are requested", - Value: "solc", - } - vyFlag = &cli.StringFlag{ - Name: "vy", - Usage: "Path to the Ethereum contract Vyper source to build and bind", - } - vyperFlag = &cli.StringFlag{ - Name: "vyper", - Usage: "Vyper compiler to use if source builds are requested", - Value: "vyper", - } excFlag = &cli.StringFlag{ Name: "exc", Usage: "Comma separated types to exclude from binding", @@ -102,10 +84,6 @@ func init() { binFlag, typeFlag, jsonFlag, - solFlag, - solcFlag, - vyFlag, - vyperFlag, excFlag, pkgFlag, outFlag, @@ -115,7 +93,7 @@ func init() { } func abigen(c *cli.Context) error { - utils.CheckExclusive(c, abiFlag, jsonFlag, solFlag, vyFlag) // Only one source can be selected. + utils.CheckExclusive(c, abiFlag, jsonFlag) // Only one source can be selected. if c.String(pkgFlag.Name) == "" { utils.Fatalf("No destination package specified (--pkg)") } @@ -167,21 +145,9 @@ func abigen(c *cli.Context) error { for _, kind := range strings.Split(c.String(excFlag.Name), ",") { exclude[strings.ToLower(kind)] = true } - var err error var contracts map[string]*compiler.Contract - switch { - case c.IsSet(solFlag.Name): - contracts, err = compiler.CompileSolidity(c.String(solcFlag.Name), c.String(solFlag.Name)) - if err != nil { - utils.Fatalf("Failed to build Solidity contract: %v", err) - } - case c.IsSet(vyFlag.Name): - contracts, err = compiler.CompileVyper(c.String(vyperFlag.Name), c.String(vyFlag.Name)) - if err != nil { - utils.Fatalf("Failed to build Vyper contract: %v", err) - } - case c.IsSet(jsonFlag.Name): + if c.IsSet(jsonFlag.Name) { jsonOutput, err := os.ReadFile(c.String(jsonFlag.Name)) if err != nil { utils.Fatalf("Failed to read combined-json from compiler: %v", err) diff --git a/common/compiler/helpers.go b/common/compiler/helpers.go index 59d242af3df6..063fc1081102 100644 --- a/common/compiler/helpers.go +++ b/common/compiler/helpers.go @@ -17,14 +17,6 @@ // Package compiler wraps the Solidity and Vyper compiler executables (solc; vyper). package compiler -import ( - "bytes" - "os" - "regexp" -) - -var versionRegexp = regexp.MustCompile(`([0-9]+)\.([0-9]+)\.([0-9]+)`) - // Contract contains information about a compiled contract, alongside its code and runtime code. type Contract struct { Code string `json:"code"` @@ -51,15 +43,3 @@ type ContractInfo struct { DeveloperDoc interface{} `json:"developerDoc"` Metadata string `json:"metadata"` } - -func slurpFiles(files []string) (string, error) { - var concat bytes.Buffer - for _, file := range files { - content, err := os.ReadFile(file) - if err != nil { - return "", err - } - concat.Write(content) - } - return concat.String(), nil -} diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go index eb7fd079b380..a83fdf3f0969 100644 --- a/common/compiler/solidity.go +++ b/common/compiler/solidity.go @@ -14,26 +14,14 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// Package compiler wraps the Solidity and Vyper compiler executables (solc; vyper). +// Package compiler wraps the ABI compilation outputs. package compiler import ( - "bytes" "encoding/json" - "errors" "fmt" - "os/exec" - "strconv" - "strings" ) -// Solidity contains information about the solidity compiler. -type Solidity struct { - Path, Version, FullVersion string - Major, Minor, Patch int - ExtraAllowedPath []string -} - // --combined-output format type solcOutput struct { Contracts map[string]struct { @@ -59,109 +47,6 @@ type solcOutputV8 struct { Version string } -func (s *Solidity) allowedPaths() string { - paths := []string{".", "./", "../"} // default to support relative paths - if len(s.ExtraAllowedPath) > 0 { - paths = append(paths, s.ExtraAllowedPath...) - } - return strings.Join(paths, ", ") -} - -func (s *Solidity) makeArgs() []string { - p := []string{ - "--combined-json", "bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc", - "--optimize", // code optimizer switched on - "--allow-paths", s.allowedPaths(), - } - if s.Major > 0 || s.Minor > 4 || s.Patch > 6 { - p[1] += ",metadata,hashes" - } - return p -} - -// SolidityVersion runs solc and parses its version output. -func SolidityVersion(solc string) (*Solidity, error) { - if solc == "" { - solc = "solc" - } - var out bytes.Buffer - cmd := exec.Command(solc, "--version") - cmd.Stdout = &out - err := cmd.Run() - if err != nil { - return nil, err - } - matches := versionRegexp.FindStringSubmatch(out.String()) - if len(matches) != 4 { - return nil, fmt.Errorf("can't parse solc version %q", out.String()) - } - s := &Solidity{Path: cmd.Path, FullVersion: out.String(), Version: matches[0]} - if s.Major, err = strconv.Atoi(matches[1]); err != nil { - return nil, err - } - if s.Minor, err = strconv.Atoi(matches[2]); err != nil { - return nil, err - } - if s.Patch, err = strconv.Atoi(matches[3]); err != nil { - return nil, err - } - return s, nil -} - -// CompileSolidityString builds and returns all the contracts contained within a source string. -func CompileSolidityString(solc, source string) (map[string]*Contract, error) { - if len(source) == 0 { - return nil, errors.New("solc: empty source string") - } - s, err := SolidityVersion(solc) - if err != nil { - return nil, err - } - return s.CompileSource(source) -} - -// CompileSolidity compiles all given Solidity source files. -func CompileSolidity(solc string, sourcefiles ...string) (map[string]*Contract, error) { - if len(sourcefiles) == 0 { - return nil, errors.New("solc: no source files") - } - s, err := SolidityVersion(solc) - if err != nil { - return nil, err - } - - return s.CompileFiles(sourcefiles...) -} - -// CompileSource builds and returns all the contracts contained within a source string. -func (s *Solidity) CompileSource(source string) (map[string]*Contract, error) { - args := append(s.makeArgs(), "--") - cmd := exec.Command(s.Path, append(args, "-")...) - cmd.Stdin = strings.NewReader(source) - return s.run(cmd, source) -} - -// CompileFiles compiles all given Solidity source files. -func (s *Solidity) CompileFiles(sourcefiles ...string) (map[string]*Contract, error) { - source, err := slurpFiles(sourcefiles) - if err != nil { - return nil, err - } - args := append(s.makeArgs(), "--") - cmd := exec.Command(s.Path, append(args, sourcefiles...)...) - return s.run(cmd, source) -} - -func (s *Solidity) run(cmd *exec.Cmd, source string) (map[string]*Contract, error) { - var stderr, stdout bytes.Buffer - cmd.Stderr = &stderr - cmd.Stdout = &stdout - if err := cmd.Run(); err != nil { - return nil, fmt.Errorf("solc: %v\n%s", err, stderr.Bytes()) - } - return ParseCombinedJSON(stdout.Bytes(), source, s.Version, s.Version, strings.Join(s.makeArgs(), " ")) -} - // ParseCombinedJSON takes the direct output of a solc --combined-output run and // parses it into a map of string contract name to Contract structs. The // provided source, language and compiler version, and compiler options are all diff --git a/common/compiler/solidity_test.go b/common/compiler/solidity_test.go deleted file mode 100644 index bd68eaee31a2..000000000000 --- a/common/compiler/solidity_test.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2019 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package compiler - -import ( - "os/exec" - "testing" -) - -const ( - testSource = ` -pragma solidity >0.0.0; -contract test { - /// @notice Will multiply ` + "`a`" + ` by 7. - function multiply(uint a) public returns(uint d) { - return a * 7; - } -} -` -) - -func skipWithoutSolc(t *testing.T) { - if _, err := exec.LookPath("solc"); err != nil { - t.Skip(err) - } -} - -func TestSolidityCompiler(t *testing.T) { - t.SkipNow() - skipWithoutSolc(t) - - contracts, err := CompileSolidityString("", testSource) - if err != nil { - t.Fatalf("error compiling source. result %v: %v", contracts, err) - } - if len(contracts) != 1 { - t.Errorf("one contract expected, got %d", len(contracts)) - } - c, ok := contracts["test"] - if !ok { - c, ok = contracts[":test"] - if !ok { - t.Fatal("info for contract 'test' not present in result") - } - } - if c.Code == "" { - t.Error("empty code") - } - if c.Info.Source != testSource { - t.Error("wrong source") - } - if c.Info.CompilerVersion == "" { - t.Error("empty version") - } -} - -func TestSolidityCompileError(t *testing.T) { - skipWithoutSolc(t) - - contracts, err := CompileSolidityString("", testSource[4:]) - if err == nil { - t.Errorf("error expected compiling source. got none. result %v", contracts) - } - t.Logf("error: %v", err) -} diff --git a/common/compiler/test.v.py b/common/compiler/test.v.py deleted file mode 100644 index 35af56c8f6ef..000000000000 --- a/common/compiler/test.v.py +++ /dev/null @@ -1,3 +0,0 @@ -@public -def test(): - hello: int128 diff --git a/common/compiler/test_bad.v.py b/common/compiler/test_bad.v.py deleted file mode 100644 index 443ef7826325..000000000000 --- a/common/compiler/test_bad.v.py +++ /dev/null @@ -1,3 +0,0 @@ -lic -def test(): - hello: int128 diff --git a/common/compiler/vyper.go b/common/compiler/vyper.go deleted file mode 100644 index a9bca95e5901..000000000000 --- a/common/compiler/vyper.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2019 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package compiler wraps the Solidity and Vyper compiler executables (solc; vyper). -package compiler - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "os/exec" - "strconv" - "strings" -) - -// Vyper contains information about the vyper compiler. -type Vyper struct { - Path, Version, FullVersion string - Major, Minor, Patch int -} - -func (s *Vyper) makeArgs() []string { - p := []string{ - "-f", "combined_json", - } - return p -} - -// VyperVersion runs vyper and parses its version output. -func VyperVersion(vyper string) (*Vyper, error) { - if vyper == "" { - vyper = "vyper" - } - var out bytes.Buffer - cmd := exec.Command(vyper, "--version") - cmd.Stdout = &out - err := cmd.Run() - if err != nil { - return nil, err - } - matches := versionRegexp.FindStringSubmatch(out.String()) - if len(matches) != 4 { - return nil, fmt.Errorf("can't parse vyper version %q", out.String()) - } - s := &Vyper{Path: cmd.Path, FullVersion: out.String(), Version: matches[0]} - if s.Major, err = strconv.Atoi(matches[1]); err != nil { - return nil, err - } - if s.Minor, err = strconv.Atoi(matches[2]); err != nil { - return nil, err - } - if s.Patch, err = strconv.Atoi(matches[3]); err != nil { - return nil, err - } - return s, nil -} - -// CompileVyper compiles all given Vyper source files. -func CompileVyper(vyper string, sourcefiles ...string) (map[string]*Contract, error) { - if len(sourcefiles) == 0 { - return nil, errors.New("vyper: no source files") - } - source, err := slurpFiles(sourcefiles) - if err != nil { - return nil, err - } - s, err := VyperVersion(vyper) - if err != nil { - return nil, err - } - args := s.makeArgs() - cmd := exec.Command(s.Path, append(args, sourcefiles...)...) - return s.run(cmd, source) -} - -func (s *Vyper) run(cmd *exec.Cmd, source string) (map[string]*Contract, error) { - var stderr, stdout bytes.Buffer - cmd.Stderr = &stderr - cmd.Stdout = &stdout - if err := cmd.Run(); err != nil { - return nil, fmt.Errorf("vyper: %v\n%s", err, stderr.Bytes()) - } - - return ParseVyperJSON(stdout.Bytes(), source, s.Version, s.Version, strings.Join(s.makeArgs(), " ")) -} - -// ParseVyperJSON takes the direct output of a vyper --f combined_json run and -// parses it into a map of string contract name to Contract structs. The -// provided source, language and compiler version, and compiler options are all -// passed through into the Contract structs. -// -// The vyper output is expected to contain ABI and source mapping. -// -// Returns an error if the JSON is malformed or missing data, or if the JSON -// embedded within the JSON is malformed. -func ParseVyperJSON(combinedJSON []byte, source string, languageVersion string, compilerVersion string, compilerOptions string) (map[string]*Contract, error) { - var output map[string]interface{} - if err := json.Unmarshal(combinedJSON, &output); err != nil { - return nil, err - } - - // Compilation succeeded, assemble and return the contracts. - contracts := make(map[string]*Contract) - for name, info := range output { - // Parse the individual compilation results. - if name == "version" { - continue - } - c := info.(map[string]interface{}) - - contracts[name] = &Contract{ - Code: c["bytecode"].(string), - RuntimeCode: c["bytecode_runtime"].(string), - Info: ContractInfo{ - Source: source, - Language: "Vyper", - LanguageVersion: languageVersion, - CompilerVersion: compilerVersion, - CompilerOptions: compilerOptions, - SrcMap: c["source_map"], - SrcMapRuntime: "", - AbiDefinition: c["abi"], - UserDoc: "", - DeveloperDoc: "", - Metadata: "", - }, - } - } - return contracts, nil -} diff --git a/common/compiler/vyper_test.go b/common/compiler/vyper_test.go deleted file mode 100644 index 7761c92affc5..000000000000 --- a/common/compiler/vyper_test.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2019 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package compiler - -import ( - "os/exec" - "testing" -) - -func skipWithoutVyper(t *testing.T) { - if _, err := exec.LookPath("vyper"); err != nil { - t.Skip(err) - } -} - -func TestVyperCompiler(t *testing.T) { - skipWithoutVyper(t) - - testSource := []string{"test.v.py"} - source, err := slurpFiles(testSource) - if err != nil { - t.Error("couldn't read test files") - } - contracts, err := CompileVyper("", testSource...) - if err != nil { - t.Fatalf("error compiling test.v.py. result %v: %v", contracts, err) - } - if len(contracts) != 1 { - t.Errorf("one contract expected, got %d", len(contracts)) - } - c, ok := contracts["test.v.py"] - if !ok { - c, ok = contracts[":test"] - if !ok { - t.Fatal("info for contract 'test.v.py' not present in result") - } - } - if c.Code == "" { - t.Error("empty code") - } - if c.Info.Source != source { - t.Error("wrong source") - } - if c.Info.CompilerVersion == "" { - t.Error("empty version") - } -} - -func TestVyperCompileError(t *testing.T) { - skipWithoutVyper(t) - - contracts, err := CompileVyper("", "test_bad.v.py") - if err == nil { - t.Errorf("error expected compiling test_bad.v.py. got none. result %v", contracts) - } - t.Logf("error: %v", err) -} From febb456ddd3e96aad420a8764d3632ef42c28af6 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 14 Jun 2022 14:08:43 +0200 Subject: [PATCH 430/479] common: improve pretty duration regex (#25073) --- common/format.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/common/format.go b/common/format.go index fccc299620dc..197e8697d9af 100644 --- a/common/format.go +++ b/common/format.go @@ -17,7 +17,6 @@ package common import ( - "fmt" "regexp" "strings" "time" @@ -27,12 +26,12 @@ import ( // the unnecessary precision off from the formatted textual representation. type PrettyDuration time.Duration -var prettyDurationRe = regexp.MustCompile(`\.[0-9]+`) +var prettyDurationRe = regexp.MustCompile(`\.[0-9]{4,}`) // String implements the Stringer interface, allowing pretty printing of duration // values rounded to three decimals. func (d PrettyDuration) String() string { - label := fmt.Sprintf("%v", time.Duration(d)) + label := time.Duration(d).String() if match := prettyDurationRe.FindString(label); len(match) > 4 { label = strings.Replace(label, match, match[:4], 1) } From a03e11186ce0aa1c12eed059bb76aba67bf84e62 Mon Sep 17 00:00:00 2001 From: "Seungbae.yu" <72970043+dbadoy@users.noreply.github.com> Date: Wed, 29 Jun 2022 19:13:00 +0900 Subject: [PATCH 431/479] common: increase StorageSize test coverage (#25188) --- common/size_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/common/size_test.go b/common/size_test.go index 0938d483c4bb..28f053d39f5d 100644 --- a/common/size_test.go +++ b/common/size_test.go @@ -25,6 +25,8 @@ func TestStorageSizeString(t *testing.T) { size StorageSize str string }{ + {2839274474874, "2.58 TiB"}, + {2458492810, "2.29 GiB"}, {2381273, "2.27 MiB"}, {2192, "2.14 KiB"}, {12, "12.00 B"}, @@ -36,3 +38,22 @@ func TestStorageSizeString(t *testing.T) { } } } + +func TestStorageSizeTerminalString(t *testing.T) { + tests := []struct { + size StorageSize + str string + }{ + {2839274474874, "2.58TiB"}, + {2458492810, "2.29GiB"}, + {2381273, "2.27MiB"}, + {2192, "2.14KiB"}, + {12, "12.00B"}, + } + + for _, test := range tests { + if test.size.TerminalString() != test.str { + t.Errorf("%f: got %q, want %q", float64(test.size), test.size.TerminalString(), test.str) + } + } +} From 1bff428a39337e96f69f96587a4c93f051327516 Mon Sep 17 00:00:00 2001 From: "Seungbae.yu" <72970043+dbadoy@users.noreply.github.com> Date: Thu, 30 Jun 2022 13:24:04 +0900 Subject: [PATCH 432/479] common/prque: fix typo (#25206) --- common/prque/lazyqueue.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/prque/lazyqueue.go b/common/prque/lazyqueue.go index 85d7bf6eee7e..1fa744f399bc 100644 --- a/common/prque/lazyqueue.go +++ b/common/prque/lazyqueue.go @@ -164,7 +164,7 @@ func (q *LazyQueue) PopItem() interface{} { return i } -// Remove removes removes the item with the given index. +// Remove removes the item with the given index. func (q *LazyQueue) Remove(index int) interface{} { if index < 0 { return nil From e3edcc8429eb6ee9da82eb743395eaf710b46fcb Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 17 Dec 2024 16:30:54 +0800 Subject: [PATCH 433/479] common/math: lint whitespace --- common/math/big_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/common/math/big_test.go b/common/math/big_test.go index d9e5723c61c6..8ae7578319a7 100644 --- a/common/math/big_test.go +++ b/common/math/big_test.go @@ -171,7 +171,6 @@ func BenchmarkByteAt(b *testing.B) { } func BenchmarkByteAtOld(b *testing.B) { - bigint := MustParseBig256("0x18F8F8F1000111000110011100222004330052300000000000000000FEFCF3CC") for i := 0; i < b.N; i++ { PaddedBigBytes(bigint, 32) @@ -244,9 +243,9 @@ func TestBigEndianByteAt(t *testing.T) { if actual != test.exp { t.Fatalf("Expected [%v] %v:th byte to be %v, was %v.", test.x, test.y, test.exp, actual) } - } } + func TestLittleEndianByteAt(t *testing.T) { tests := []struct { x string @@ -277,7 +276,6 @@ func TestLittleEndianByteAt(t *testing.T) { if actual != test.exp { t.Fatalf("Expected [%v] %v:th byte to be %v, was %v.", test.x, test.y, test.exp, actual) } - } } From 32e033df3af376e4c037b04777155ac4e4a89028 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 17 Dec 2024 16:49:30 +0800 Subject: [PATCH 434/479] common, p2p: use AbsTime.Add instead of conversion (#25417) --- common/mclock/simclock.go | 4 ++-- common/prque/lazyqueue.go | 4 ++-- p2p/discv5/ticket.go | 8 ++++---- p2p/discv5/topic.go | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/common/mclock/simclock.go b/common/mclock/simclock.go index 766ca0f8736c..f5ad3f8bc0aa 100644 --- a/common/mclock/simclock.go +++ b/common/mclock/simclock.go @@ -58,7 +58,7 @@ func (s *Simulated) Run(d time.Duration) { s.mu.Lock() s.init() - end := s.now + AbsTime(d) + end := s.now.Add(d) var do []func() for len(s.scheduled) > 0 && s.scheduled[0].at <= end { ev := heap.Pop(&s.scheduled).(*simTimer) @@ -134,7 +134,7 @@ func (s *Simulated) AfterFunc(d time.Duration, fn func()) Timer { func (s *Simulated) schedule(d time.Duration, fn func()) *simTimer { s.init() - at := s.now + AbsTime(d) + at := s.now.Add(d) ev := &simTimer{do: fn, at: at, s: s} heap.Push(&s.scheduled, ev) s.cond.Broadcast() diff --git a/common/prque/lazyqueue.go b/common/prque/lazyqueue.go index 1fa744f399bc..883d210735ce 100644 --- a/common/prque/lazyqueue.go +++ b/common/prque/lazyqueue.go @@ -88,13 +88,13 @@ func (q *LazyQueue) Refresh() { // refresh re-evaluates items in the older queue and swaps the two queues func (q *LazyQueue) refresh(now mclock.AbsTime) { - q.maxUntil = now + mclock.AbsTime(q.period) + q.maxUntil = now.Add(q.period) for q.queue[0].Len() != 0 { q.Push(heap.Pop(q.queue[0]).(*item).value) } q.queue[0], q.queue[1] = q.queue[1], q.queue[0] q.indexOffset = 1 - q.indexOffset - q.maxUntil += mclock.AbsTime(q.period) + q.maxUntil = q.maxUntil.Add(q.period) } // Push adds an item to the queue diff --git a/p2p/discv5/ticket.go b/p2p/discv5/ticket.go index 4780e8b8661e..2ccbfad7ff71 100644 --- a/p2p/discv5/ticket.go +++ b/p2p/discv5/ticket.go @@ -107,7 +107,7 @@ func pongToTicket(localTime mclock.AbsTime, topics []Topic, node *Node, p *ingre } // Convert wait periods to local absolute time. for i, wp := range wps { - t.regTime[i] = localTime + mclock.AbsTime(time.Second*time.Duration(wp)) + t.regTime[i] = localTime.Add(time.Second * time.Duration(wp)) } return t, nil } @@ -349,7 +349,7 @@ func (s *ticketStore) nextFilteredTicket() (*ticketRef, time.Duration) { } log.Trace("Found discovery ticket to register", "node", ticket.t.node, "serial", ticket.t.serial, "wait", wait) - regTime := now + mclock.AbsTime(wait) + regTime := now.Add(wait) topic := ticket.t.topics[ticket.idx] if s.tickets[topic] != nil && regTime >= s.tickets[topic].nextReg { return ticket, wait @@ -614,7 +614,7 @@ func (s *ticketStore) cleanupTopicQueries(now mclock.AbsTime) { delete(s.queriesSent, n) } } - s.nextTopicQueryCleanup = now + mclock.AbsTime(topicQueryTimeout) + s.nextTopicQueryCleanup = now.Add(topicQueryTimeout) } func (s *ticketStore) gotTopicNodes(from *Node, hash common.Hash, nodes []rpcNode) (timeout bool) { @@ -625,7 +625,7 @@ func (s *ticketStore) gotTopicNodes(from *Node, hash common.Hash, nodes []rpcNod return true } q, ok := qq[hash] - if !ok || now > q.sent+mclock.AbsTime(topicQueryTimeout) { + if !ok || now > q.sent.Add(topicQueryTimeout) { return true } inside := float64(0) diff --git a/p2p/discv5/topic.go b/p2p/discv5/topic.go index c7c4ca93683e..060395e9633a 100644 --- a/p2p/discv5/topic.go +++ b/p2p/discv5/topic.go @@ -194,7 +194,7 @@ func (t *topicTable) addEntry(node *Node, topic Topic) { topic: topic, fifoIdx: fifoIdx, node: node, - expire: tm + mclock.AbsTime(fallbackRegistrationExpiry), + expire: tm.Add(fallbackRegistrationExpiry), } if printTestImgLogs { fmt.Printf("*+ %d %v %016x %016x\n", tm/1000000, topic, t.self.sha[:8], node.sha[:8]) @@ -251,7 +251,7 @@ func (t *topicTable) useTicket(node *Node, serialNo uint32, topics []Topic, idx } if serialNo != n.lastUsedTicket { n.lastUsedTicket = serialNo - n.noRegUntil = tm + mclock.AbsTime(noRegTimeout()) + n.noRegUntil = tm.Add(noRegTimeout()) t.storeTicketCounters(node) } @@ -263,7 +263,7 @@ func (t *topicTable) useTicket(node *Node, serialNo uint32, topics []Topic, idx t.addEntry(node, topics[idx]) } else { // if there is an active entry, don't move to the front of the FIFO but prolong expire time - e.expire = tm + mclock.AbsTime(fallbackRegistrationExpiry) + e.expire = tm.Add(fallbackRegistrationExpiry) } return true } @@ -293,7 +293,7 @@ func (topictab *topicTable) getTicket(node *Node, topics []Topic) *ticket { waitPeriod = minWaitPeriod } - t.regTime[i] = now + mclock.AbsTime(waitPeriod) + t.regTime[i] = now.Add(waitPeriod) } return t } From 3eb2ec42b5b0386ccc87b627f3f97aaba297b0b1 Mon Sep 17 00:00:00 2001 From: Henry <101552941+henry-0@users.noreply.github.com> Date: Mon, 1 Aug 2022 19:47:21 +0800 Subject: [PATCH 435/479] common/compiler: json unmarshalling error checks (#25449) complier/solidity:add json.Unmarshal err check --- common/compiler/solidity.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go index a83fdf3f0969..bf42a18041ae 100644 --- a/common/compiler/solidity.go +++ b/common/compiler/solidity.go @@ -66,13 +66,16 @@ func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion strin contracts := make(map[string]*Contract) for name, info := range output.Contracts { // Parse the individual compilation results. - var abi interface{} + var abi, userdoc, devdoc interface{} if err := json.Unmarshal([]byte(info.Abi), &abi); err != nil { return nil, fmt.Errorf("solc: error reading abi definition (%v)", err) } - var userdoc, devdoc interface{} - json.Unmarshal([]byte(info.Userdoc), &userdoc) - json.Unmarshal([]byte(info.Devdoc), &devdoc) + if err := json.Unmarshal([]byte(info.Userdoc), &userdoc); err != nil { + return nil, fmt.Errorf("solc: error reading userdoc definition (%v)", err) + } + if err := json.Unmarshal([]byte(info.Devdoc), &devdoc); err != nil { + return nil, fmt.Errorf("solc: error reading devdoc definition (%v)", err) + } contracts[name] = &Contract{ Code: "0x" + info.Bin, From 50591149927028e189f2102f68d4139c54757af6 Mon Sep 17 00:00:00 2001 From: Mark Tyneway Date: Tue, 8 Nov 2022 03:14:11 -0800 Subject: [PATCH 436/479] common/types: add `Address.Big` (#26132) Many of the other types have a function to convert the type to a big.Int, but Address was missing this function. It is useful to be able to turn an Address into a big.Int when doing EVM-like computations natively in Go. Sometimes a Solidity address type is casted to a uint256 and having a Big method on the Address type makes this easy. --- common/types.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/types.go b/common/types.go index 24e8338af70f..d32343ccd4bc 100644 --- a/common/types.go +++ b/common/types.go @@ -253,6 +253,9 @@ func (a Address) Bytes() []byte { return a[:] } // Hash converts an address to a hash by left-padding it with zeros. func (a Address) Hash() Hash { return BytesToHash(a[:]) } +// Big converts an address to a big integer. +func (a Address) Big() *big.Int { return new(big.Int).SetBytes(a[:]) } + // Hex returns an EIP55-compliant hex string representation of the address. func (a Address) Hex() string { checksumed := a.checksumHex() From 39e9d8f94def2a5e3eaaaf0ebf487614d15c3ca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Thu, 9 Feb 2023 13:03:54 +0200 Subject: [PATCH 437/479] common/prque: generic priority queue (#26290) --- XDCx/XDCx.go | 8 ++-- XDCxlending/XDCxlending.go | 8 ++-- common/prque/lazyqueue.go | 78 +++++++++++++++++----------------- common/prque/prque.go | 44 +++++++++---------- common/prque/prque_test.go | 27 ++++++------ common/prque/sstack.go | 65 +++++++++++++--------------- common/prque/sstack_test.go | 30 ++++++------- consensus/XDPoS/utils/types.go | 4 +- core/blockchain.go | 18 ++++---- core/txpool/lending_pool.go | 6 +-- core/txpool/order_pool.go | 6 +-- core/txpool/txpool.go | 6 +-- eth/downloader/queue.go | 48 ++++++++++----------- eth/fetcher/fetcher.go | 11 +++-- go.mod | 1 + go.sum | 2 + trie/sync.go | 4 +- 17 files changed, 178 insertions(+), 188 deletions(-) diff --git a/XDCx/XDCx.go b/XDCx/XDCx.go index fbb737240289..1c09249874a0 100644 --- a/XDCx/XDCx.go +++ b/XDCx/XDCx.go @@ -52,8 +52,8 @@ type XDCX struct { // Order related db XDCxDAO.XDCXDAO mongodb XDCxDAO.XDCXDAO - Triegc *prque.Prque // Priority queue mapping block numbers to tries to gc - StateCache tradingstate.Database // State database to reuse between imports (contains state cache) *XDCx_state.TradingStateDB + Triegc *prque.Prque[int64, common.Hash] // Priority queue mapping block numbers to tries to gc + StateCache tradingstate.Database // State database to reuse between imports (contains state cache) *XDCx_state.TradingStateDB orderNonce map[common.Address]*big.Int @@ -94,7 +94,7 @@ func NewMongoDBEngine(cfg *Config) *XDCxDAO.MongoDatabase { func New(cfg *Config) *XDCX { XDCX := &XDCX{ orderNonce: make(map[common.Address]*big.Int), - Triegc: prque.New(nil), + Triegc: prque.New[int64, common.Hash](nil), tokenDecimalCache: lru.NewCache[common.Address, *big.Int](defaultCacheLimit), orderCache: lru.NewCache[common.Hash, map[common.Hash]tradingstate.OrderHistoryItem](tradingstate.OrderCacheLimit), } @@ -579,7 +579,7 @@ func (XDCx *XDCX) HasTradingState(block *types.Block, author common.Address) boo return err == nil } -func (XDCx *XDCX) GetTriegc() *prque.Prque { +func (XDCx *XDCX) GetTriegc() *prque.Prque[int64, common.Hash] { return XDCx.Triegc } diff --git a/XDCxlending/XDCxlending.go b/XDCxlending/XDCxlending.go index a1f8e9749fd1..34a00095548a 100644 --- a/XDCxlending/XDCxlending.go +++ b/XDCxlending/XDCxlending.go @@ -36,8 +36,8 @@ var ( ) type Lending struct { - Triegc *prque.Prque // Priority queue mapping block numbers to tries to gc - StateCache lendingstate.Database // State database to reuse between imports (contains state cache) *lendingstate.TradingStateDB + Triegc *prque.Prque[int64, common.Hash] // Priority queue mapping block numbers to tries to gc + StateCache lendingstate.Database // State database to reuse between imports (contains state cache) *lendingstate.TradingStateDB orderNonce map[common.Address]*big.Int @@ -61,7 +61,7 @@ func (l *Lending) Stop() error { func New(XDCx *XDCx.XDCX) *Lending { lending := &Lending{ orderNonce: make(map[common.Address]*big.Int), - Triegc: prque.New(nil), + Triegc: prque.New[int64, common.Hash](nil), lendingItemHistory: lru.NewCache[common.Hash, map[common.Hash]lendingstate.LendingItemHistoryItem](defaultCacheLimit), lendingTradeHistory: lru.NewCache[common.Hash, map[common.Hash]lendingstate.LendingTradeHistoryItem](defaultCacheLimit), } @@ -682,7 +682,7 @@ func (l *Lending) HasLendingState(block *types.Block, author common.Address) boo return err == nil } -func (l *Lending) GetTriegc() *prque.Prque { +func (l *Lending) GetTriegc() *prque.Prque[int64, common.Hash] { return l.Triegc } diff --git a/common/prque/lazyqueue.go b/common/prque/lazyqueue.go index 883d210735ce..ae3cd798da00 100644 --- a/common/prque/lazyqueue.go +++ b/common/prque/lazyqueue.go @@ -21,6 +21,7 @@ import ( "time" "github.com/XinFinOrg/XDPoSChain/common/mclock" + "golang.org/x/exp/constraints" ) // LazyQueue is a priority queue data structure where priorities can change over @@ -32,31 +33,31 @@ import ( // // If the upper estimate is exceeded then Update should be called for that item. // A global Refresh function should also be called periodically. -type LazyQueue struct { +type LazyQueue[P constraints.Ordered, V any] struct { clock mclock.Clock // Items are stored in one of two internal queues ordered by estimated max // priority until the next and the next-after-next refresh. Update and Refresh // always places items in queue[1]. - queue [2]*sstack - popQueue *sstack + queue [2]*sstack[P, V] + popQueue *sstack[P, V] period time.Duration maxUntil mclock.AbsTime indexOffset int - setIndex SetIndexCallback - priority PriorityCallback - maxPriority MaxPriorityCallback + setIndex SetIndexCallback[V] + priority PriorityCallback[P, V] + maxPriority MaxPriorityCallback[P, V] lastRefresh1, lastRefresh2 mclock.AbsTime } type ( - PriorityCallback func(data interface{}) int64 // actual priority callback - MaxPriorityCallback func(data interface{}, until mclock.AbsTime) int64 // estimated maximum priority callback + PriorityCallback[P constraints.Ordered, V any] func(data V) P // actual priority callback + MaxPriorityCallback[P constraints.Ordered, V any] func(data V, until mclock.AbsTime) P // estimated maximum priority callback ) // NewLazyQueue creates a new lazy queue -func NewLazyQueue(setIndex SetIndexCallback, priority PriorityCallback, maxPriority MaxPriorityCallback, clock mclock.Clock, refreshPeriod time.Duration) *LazyQueue { - q := &LazyQueue{ - popQueue: newSstack(nil, false), +func NewLazyQueue[P constraints.Ordered, V any](setIndex SetIndexCallback[V], priority PriorityCallback[P, V], maxPriority MaxPriorityCallback[P, V], clock mclock.Clock, refreshPeriod time.Duration) *LazyQueue[P, V] { + q := &LazyQueue[P, V]{ + popQueue: newSstack[P, V](nil), setIndex: setIndex, priority: priority, maxPriority: maxPriority, @@ -71,13 +72,13 @@ func NewLazyQueue(setIndex SetIndexCallback, priority PriorityCallback, maxPrior } // Reset clears the contents of the queue -func (q *LazyQueue) Reset() { - q.queue[0] = newSstack(q.setIndex0, false) - q.queue[1] = newSstack(q.setIndex1, false) +func (q *LazyQueue[P, V]) Reset() { + q.queue[0] = newSstack[P, V](q.setIndex0) + q.queue[1] = newSstack[P, V](q.setIndex1) } // Refresh performs queue re-evaluation if necessary -func (q *LazyQueue) Refresh() { +func (q *LazyQueue[P, V]) Refresh() { now := q.clock.Now() for time.Duration(now-q.lastRefresh2) >= q.period*2 { q.refresh(now) @@ -87,10 +88,10 @@ func (q *LazyQueue) Refresh() { } // refresh re-evaluates items in the older queue and swaps the two queues -func (q *LazyQueue) refresh(now mclock.AbsTime) { +func (q *LazyQueue[P, V]) refresh(now mclock.AbsTime) { q.maxUntil = now.Add(q.period) for q.queue[0].Len() != 0 { - q.Push(heap.Pop(q.queue[0]).(*item).value) + q.Push(heap.Pop(q.queue[0]).(*item[P, V]).value) } q.queue[0], q.queue[1] = q.queue[1], q.queue[0] q.indexOffset = 1 - q.indexOffset @@ -98,22 +99,22 @@ func (q *LazyQueue) refresh(now mclock.AbsTime) { } // Push adds an item to the queue -func (q *LazyQueue) Push(data interface{}) { - heap.Push(q.queue[1], &item{data, q.maxPriority(data, q.maxUntil)}) +func (q *LazyQueue[P, V]) Push(data V) { + heap.Push(q.queue[1], &item[P, V]{data, q.maxPriority(data, q.maxUntil)}) } // Update updates the upper priority estimate for the item with the given queue index -func (q *LazyQueue) Update(index int) { +func (q *LazyQueue[P, V]) Update(index int) { q.Push(q.Remove(index)) } // Pop removes and returns the item with the greatest actual priority -func (q *LazyQueue) Pop() (interface{}, int64) { +func (q *LazyQueue[P, V]) Pop() (V, P) { var ( - resData interface{} - resPri int64 + resData V + resPri P ) - q.MultiPop(func(data interface{}, priority int64) bool { + q.MultiPop(func(data V, priority P) bool { resData = data resPri = priority return false @@ -123,7 +124,7 @@ func (q *LazyQueue) Pop() (interface{}, int64) { // peekIndex returns the index of the internal queue where the item with the // highest estimated priority is or -1 if both are empty -func (q *LazyQueue) peekIndex() int { +func (q *LazyQueue[P, V]) peekIndex() int { if q.queue[0].Len() != 0 { if q.queue[1].Len() != 0 && q.queue[1].blocks[0][0].priority > q.queue[0].blocks[0][0].priority { return 1 @@ -139,17 +140,17 @@ func (q *LazyQueue) peekIndex() int { // MultiPop pops multiple items from the queue and is more efficient than calling // Pop multiple times. Popped items are passed to the callback. MultiPop returns // when the callback returns false or there are no more items to pop. -func (q *LazyQueue) MultiPop(callback func(data interface{}, priority int64) bool) { +func (q *LazyQueue[P, V]) MultiPop(callback func(data V, priority P) bool) { nextIndex := q.peekIndex() for nextIndex != -1 { - data := heap.Pop(q.queue[nextIndex]).(*item).value - heap.Push(q.popQueue, &item{data, q.priority(data)}) + data := heap.Pop(q.queue[nextIndex]).(*item[P, V]).value + heap.Push(q.popQueue, &item[P, V]{data, q.priority(data)}) nextIndex = q.peekIndex() for q.popQueue.Len() != 0 && (nextIndex == -1 || q.queue[nextIndex].blocks[0][0].priority < q.popQueue.blocks[0][0].priority) { - i := heap.Pop(q.popQueue).(*item) + i := heap.Pop(q.popQueue).(*item[P, V]) if !callback(i.value, i.priority) { for q.popQueue.Len() != 0 { - q.Push(heap.Pop(q.popQueue).(*item).value) + q.Push(heap.Pop(q.popQueue).(*item[P, V]).value) } return } @@ -159,31 +160,28 @@ func (q *LazyQueue) MultiPop(callback func(data interface{}, priority int64) boo } // PopItem pops the item from the queue only, dropping the associated priority value. -func (q *LazyQueue) PopItem() interface{} { +func (q *LazyQueue[P, V]) PopItem() V { i, _ := q.Pop() return i } // Remove removes the item with the given index. -func (q *LazyQueue) Remove(index int) interface{} { - if index < 0 { - return nil - } - return heap.Remove(q.queue[index&1^q.indexOffset], index>>1).(*item).value +func (q *LazyQueue[P, V]) Remove(index int) V { + return heap.Remove(q.queue[index&1^q.indexOffset], index>>1).(*item[P, V]).value } // Empty checks whether the priority queue is empty. -func (q *LazyQueue) Empty() bool { +func (q *LazyQueue[P, V]) Empty() bool { return q.queue[0].Len() == 0 && q.queue[1].Len() == 0 } // Size returns the number of items in the priority queue. -func (q *LazyQueue) Size() int { +func (q *LazyQueue[P, V]) Size() int { return q.queue[0].Len() + q.queue[1].Len() } // setIndex0 translates internal queue item index to the virtual index space of LazyQueue -func (q *LazyQueue) setIndex0(data interface{}, index int) { +func (q *LazyQueue[P, V]) setIndex0(data V, index int) { if index == -1 { q.setIndex(data, -1) } else { @@ -192,6 +190,6 @@ func (q *LazyQueue) setIndex0(data interface{}, index int) { } // setIndex1 translates internal queue item index to the virtual index space of LazyQueue -func (q *LazyQueue) setIndex1(data interface{}, index int) { +func (q *LazyQueue[P, V]) setIndex1(data V, index int) { q.setIndex(data, index+index+1) } diff --git a/common/prque/prque.go b/common/prque/prque.go index fb02e3418c28..0e8c9f897fad 100755 --- a/common/prque/prque.go +++ b/common/prque/prque.go @@ -19,65 +19,59 @@ package prque import ( "container/heap" + + "golang.org/x/exp/constraints" ) // Priority queue data structure. -type Prque struct { - cont *sstack +type Prque[P constraints.Ordered, V any] struct { + cont *sstack[P, V] } // New creates a new priority queue. -func New(setIndex SetIndexCallback) *Prque { - return &Prque{newSstack(setIndex, false)} -} - -// NewWrapAround creates a new priority queue with wrap-around priority handling. -func NewWrapAround(setIndex SetIndexCallback) *Prque { - return &Prque{newSstack(setIndex, true)} +func New[P constraints.Ordered, V any](setIndex SetIndexCallback[V]) *Prque[P, V] { + return &Prque[P, V]{newSstack[P, V](setIndex)} } // Pushes a value with a given priority into the queue, expanding if necessary. -func (p *Prque) Push(data interface{}, priority int64) { - heap.Push(p.cont, &item{data, priority}) +func (p *Prque[P, V]) Push(data V, priority P) { + heap.Push(p.cont, &item[P, V]{data, priority}) } // Peek returns the value with the greatest priority but does not pop it off. -func (p *Prque) Peek() (interface{}, int64) { +func (p *Prque[P, V]) Peek() (V, P) { item := p.cont.blocks[0][0] return item.value, item.priority } // Pops the value with the greatest priority off the stack and returns it. // Currently no shrinking is done. -func (p *Prque) Pop() (interface{}, int64) { - item := heap.Pop(p.cont).(*item) +func (p *Prque[P, V]) Pop() (V, P) { + item := heap.Pop(p.cont).(*item[P, V]) return item.value, item.priority } // Pops only the item from the queue, dropping the associated priority value. -func (p *Prque) PopItem() interface{} { - return heap.Pop(p.cont).(*item).value +func (p *Prque[P, V]) PopItem() V { + return heap.Pop(p.cont).(*item[P, V]).value } // Remove removes the element with the given index. -func (p *Prque) Remove(i int) interface{} { - if i < 0 { - return nil - } - return heap.Remove(p.cont, i) +func (p *Prque[P, V]) Remove(i int) V { + return heap.Remove(p.cont, i).(*item[P, V]).value } // Checks whether the priority queue is empty. -func (p *Prque) Empty() bool { +func (p *Prque[P, V]) Empty() bool { return p.cont.Len() == 0 } // Returns the number of element in the priority queue. -func (p *Prque) Size() int { +func (p *Prque[P, V]) Size() int { return p.cont.Len() } // Clears the contents of the priority queue. -func (p *Prque) Reset() { - *p = *New(p.cont.setIndex) +func (p *Prque[P, V]) Reset() { + *p = *New[P, V](p.cont.setIndex) } diff --git a/common/prque/prque_test.go b/common/prque/prque_test.go index 1cffcebad437..c4910f205a00 100644 --- a/common/prque/prque_test.go +++ b/common/prque/prque_test.go @@ -21,22 +21,24 @@ func TestPrque(t *testing.T) { for i := 0; i < size; i++ { data[i] = rand.Int() } - queue := New(nil) + queue := New[int, int](nil) + for rep := 0; rep < 2; rep++ { // Fill a priority queue with the above data for i := 0; i < size; i++ { - queue.Push(data[i], int64(prio[i])) + queue.Push(data[i], prio[i]) if queue.Size() != i+1 { t.Errorf("queue size mismatch: have %v, want %v.", queue.Size(), i+1) } } // Create a map the values to the priorities for easier verification - dict := make(map[int64]int) + dict := make(map[int]int) for i := 0; i < size; i++ { - dict[int64(prio[i])] = data[i] + dict[prio[i]] = data[i] } + // Pop out the elements in priority order and verify them - prevPrio := int64(size + 1) + prevPrio := size + 1 for !queue.Empty() { val, prio := queue.Pop() if prio > prevPrio { @@ -59,22 +61,23 @@ func TestReset(t *testing.T) { for i := 0; i < size; i++ { data[i] = rand.Int() } - queue := New(nil) + queue := New[int, int](nil) + for rep := 0; rep < 2; rep++ { // Fill a priority queue with the above data for i := 0; i < size; i++ { - queue.Push(data[i], int64(prio[i])) + queue.Push(data[i], prio[i]) if queue.Size() != i+1 { t.Errorf("queue size mismatch: have %v, want %v.", queue.Size(), i+1) } } // Create a map the values to the priorities for easier verification - dict := make(map[int64]int) + dict := make(map[int]int) for i := 0; i < size; i++ { - dict[int64(prio[i])] = data[i] + dict[prio[i]] = data[i] } // Pop out half the elements in priority order and verify them - prevPrio := int64(size + 1) + prevPrio := size + 1 for i := 0; i < size/2; i++ { val, prio := queue.Pop() if prio > prevPrio { @@ -104,7 +107,7 @@ func BenchmarkPush(b *testing.B) { } // Execute the benchmark b.ResetTimer() - queue := New(nil) + queue := New[int64, int](nil) for i := 0; i < len(data); i++ { queue.Push(data[i], prio[i]) } @@ -118,7 +121,7 @@ func BenchmarkPop(b *testing.B) { data[i] = rand.Int() prio[i] = rand.Int63() } - queue := New(nil) + queue := New[int64, int](nil) for i := 0; i < len(data); i++ { queue.Push(data[i], prio[i]) } diff --git a/common/prque/sstack.go b/common/prque/sstack.go index b06a95413df0..5dcd1d9dd0c4 100755 --- a/common/prque/sstack.go +++ b/common/prque/sstack.go @@ -10,53 +10,50 @@ package prque +import "golang.org/x/exp/constraints" + // The size of a block of data const blockSize = 4096 // A prioritized item in the sorted stack. -// -// Note: priorities can "wrap around" the int64 range, a comes before b if (a.priority - b.priority) > 0. -// The difference between the lowest and highest priorities in the queue at any point should be less than 2^63. -type item struct { - value interface{} - priority int64 +type item[P constraints.Ordered, V any] struct { + value V + priority P } // SetIndexCallback is called when the element is moved to a new index. // Providing SetIndexCallback is optional, it is needed only if the application needs // to delete elements other than the top one. -type SetIndexCallback func(data interface{}, index int) +type SetIndexCallback[V any] func(data V, index int) // Internal sortable stack data structure. Implements the Push and Pop ops for // the stack (heap) functionality and the Len, Less and Swap methods for the // sortability requirements of the heaps. -type sstack struct { - setIndex SetIndexCallback - size int - capacity int - offset int - wrapAround bool +type sstack[P constraints.Ordered, V any] struct { + setIndex SetIndexCallback[V] + size int + capacity int + offset int - blocks [][]*item - active []*item + blocks [][]*item[P, V] + active []*item[P, V] } // Creates a new, empty stack. -func newSstack(setIndex SetIndexCallback, wrapAround bool) *sstack { - result := new(sstack) +func newSstack[P constraints.Ordered, V any](setIndex SetIndexCallback[V]) *sstack[P, V] { + result := new(sstack[P, V]) result.setIndex = setIndex - result.active = make([]*item, blockSize) - result.blocks = [][]*item{result.active} + result.active = make([]*item[P, V], blockSize) + result.blocks = [][]*item[P, V]{result.active} result.capacity = blockSize - result.wrapAround = wrapAround return result } // Pushes a value onto the stack, expanding it if necessary. Required by // heap.Interface. -func (s *sstack) Push(data interface{}) { +func (s *sstack[P, V]) Push(data any) { if s.size == s.capacity { - s.active = make([]*item, blockSize) + s.active = make([]*item[P, V], blockSize) s.blocks = append(s.blocks, s.active) s.capacity += blockSize s.offset = 0 @@ -65,16 +62,16 @@ func (s *sstack) Push(data interface{}) { s.offset = 0 } if s.setIndex != nil { - s.setIndex(data.(*item).value, s.size) + s.setIndex(data.(*item[P, V]).value, s.size) } - s.active[s.offset] = data.(*item) + s.active[s.offset] = data.(*item[P, V]) s.offset++ s.size++ } // Pops a value off the stack and returns it. Currently no shrinking is done. // Required by heap.Interface. -func (s *sstack) Pop() (res interface{}) { +func (s *sstack[P, V]) Pop() (res any) { s.size-- s.offset-- if s.offset < 0 { @@ -83,28 +80,24 @@ func (s *sstack) Pop() (res interface{}) { } res, s.active[s.offset] = s.active[s.offset], nil if s.setIndex != nil { - s.setIndex(res.(*item).value, -1) + s.setIndex(res.(*item[P, V]).value, -1) } return } // Returns the length of the stack. Required by sort.Interface. -func (s *sstack) Len() int { +func (s *sstack[P, V]) Len() int { return s.size } // Compares the priority of two elements of the stack (higher is first). // Required by sort.Interface. -func (s *sstack) Less(i, j int) bool { - a, b := s.blocks[i/blockSize][i%blockSize].priority, s.blocks[j/blockSize][j%blockSize].priority - if s.wrapAround { - return a-b > 0 - } - return a > b +func (s *sstack[P, V]) Less(i, j int) bool { + return s.blocks[i/blockSize][i%blockSize].priority > s.blocks[j/blockSize][j%blockSize].priority } // Swaps two elements in the stack. Required by sort.Interface. -func (s *sstack) Swap(i, j int) { +func (s *sstack[P, V]) Swap(i, j int) { ib, io, jb, jo := i/blockSize, i%blockSize, j/blockSize, j%blockSize a, b := s.blocks[jb][jo], s.blocks[ib][io] if s.setIndex != nil { @@ -115,6 +108,6 @@ func (s *sstack) Swap(i, j int) { } // Resets the stack, effectively clearing its contents. -func (s *sstack) Reset() { - *s = *newSstack(s.setIndex, false) +func (s *sstack[P, V]) Reset() { + *s = *newSstack[P, V](s.setIndex) } diff --git a/common/prque/sstack_test.go b/common/prque/sstack_test.go index bc6298979cbc..edc99955e837 100644 --- a/common/prque/sstack_test.go +++ b/common/prque/sstack_test.go @@ -17,23 +17,23 @@ import ( func TestSstack(t *testing.T) { // Create some initial data size := 16 * blockSize - data := make([]*item, size) + data := make([]*item[int64, int], size) for i := 0; i < size; i++ { - data[i] = &item{rand.Int(), rand.Int63()} + data[i] = &item[int64, int]{rand.Int(), rand.Int63()} } - stack := newSstack(nil, false) + stack := newSstack[int64, int](nil) for rep := 0; rep < 2; rep++ { // Push all the data into the stack, pop out every second - secs := []*item{} + secs := []*item[int64, int]{} for i := 0; i < size; i++ { stack.Push(data[i]) if i%2 == 0 { - secs = append(secs, stack.Pop().(*item)) + secs = append(secs, stack.Pop().(*item[int64, int])) } } - rest := []*item{} + rest := []*item[int64, int]{} for stack.Len() > 0 { - rest = append(rest, stack.Pop().(*item)) + rest = append(rest, stack.Pop().(*item[int64, int])) } // Make sure the contents of the resulting slices are ok for i := 0; i < size; i++ { @@ -50,12 +50,12 @@ func TestSstack(t *testing.T) { func TestSstackSort(t *testing.T) { // Create some initial data size := 16 * blockSize - data := make([]*item, size) + data := make([]*item[int64, int], size) for i := 0; i < size; i++ { - data[i] = &item{rand.Int(), int64(i)} + data[i] = &item[int64, int]{rand.Int(), int64(i)} } // Push all the data into the stack - stack := newSstack(nil, false) + stack := newSstack[int64, int](nil) for _, val := range data { stack.Push(val) } @@ -72,18 +72,18 @@ func TestSstackSort(t *testing.T) { func TestSstackReset(t *testing.T) { // Create some initial data size := 16 * blockSize - data := make([]*item, size) + data := make([]*item[int64, int], size) for i := 0; i < size; i++ { - data[i] = &item{rand.Int(), rand.Int63()} + data[i] = &item[int64, int]{rand.Int(), rand.Int63()} } - stack := newSstack(nil, false) + stack := newSstack[int64, int](nil) for rep := 0; rep < 2; rep++ { // Push all the data into the stack, pop out every second - secs := []*item{} + secs := []*item[int64, int]{} for i := 0; i < size; i++ { stack.Push(data[i]) if i%2 == 0 { - secs = append(secs, stack.Pop().(*item)) + secs = append(secs, stack.Pop().(*item[int64, int])) } } // Reset and verify both pulled and stack contents diff --git a/consensus/XDPoS/utils/types.go b/consensus/XDPoS/utils/types.go index fc4aaf0463aa..55265516f7e2 100644 --- a/consensus/XDPoS/utils/types.go +++ b/consensus/XDPoS/utils/types.go @@ -26,7 +26,7 @@ type TradingService interface { GetEmptyTradingState() (*tradingstate.TradingStateDB, error) HasTradingState(block *types.Block, author common.Address) bool GetStateCache() tradingstate.Database - GetTriegc() *prque.Prque + GetTriegc() *prque.Prque[int64, common.Hash] ApplyOrder(header *types.Header, coinbase common.Address, chain consensus.ChainContext, statedb *state.StateDB, XDCXstatedb *tradingstate.TradingStateDB, orderBook common.Hash, order *tradingstate.OrderItem) ([]map[string]string, []*tradingstate.OrderItem, error) UpdateMediumPriceBeforeEpoch(epochNumber uint64, tradingStateDB *tradingstate.TradingStateDB, statedb *state.StateDB) error IsSDKNode() bool @@ -40,7 +40,7 @@ type LendingService interface { GetLendingState(block *types.Block, author common.Address) (*lendingstate.LendingStateDB, error) HasLendingState(block *types.Block, author common.Address) bool GetStateCache() lendingstate.Database - GetTriegc() *prque.Prque + GetTriegc() *prque.Prque[int64, common.Hash] ApplyOrder(header *types.Header, coinbase common.Address, chain consensus.ChainContext, statedb *state.StateDB, lendingStateDB *lendingstate.LendingStateDB, tradingStateDb *tradingstate.TradingStateDB, lendingOrderBook common.Hash, order *lendingstate.LendingItem) ([]*lendingstate.LendingTrade, []*lendingstate.LendingItem, error) GetCollateralPrices(header *types.Header, chain consensus.ChainContext, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, collateralToken common.Address, lendingToken common.Address) (*big.Int, *big.Int, error) GetMediumTradePriceBeforeEpoch(chain consensus.ChainContext, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, baseToken common.Address, quoteToken common.Address) (*big.Int, error) diff --git a/core/blockchain.go b/core/blockchain.go index 3c74fc6f8e84..b6d328cc642b 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -137,8 +137,8 @@ type BlockChain struct { db ethdb.Database // Low level persistent database to store final content in XDCxDb ethdb.XDCxDatabase - triegc *prque.Prque // Priority queue mapping block numbers to tries to gc - gcproc time.Duration // Accumulates canonical block processing for trie dumping + triegc *prque.Prque[int64, common.Hash] // Priority queue mapping block numbers to tries to gc + gcproc time.Duration // Accumulates canonical block processing for trie dumping hc *HeaderChain rmLogsFeed event.Feed @@ -209,7 +209,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par chainConfig: chainConfig, cacheConfig: cacheConfig, db: db, - triegc: prque.New(nil), + triegc: prque.New[int64, common.Hash](nil), stateCache: state.NewDatabase(db), quit: make(chan struct{}), bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit), @@ -975,17 +975,17 @@ func (bc *BlockChain) saveData() { } } for !bc.triegc.Empty() { - triedb.Dereference(bc.triegc.PopItem().(common.Hash)) + triedb.Dereference(bc.triegc.PopItem()) } if tradingTriedb != nil && lendingTriedb != nil { if tradingService.GetTriegc() != nil { for !tradingService.GetTriegc().Empty() { - tradingTriedb.Dereference(tradingService.GetTriegc().PopItem().(common.Hash)) + tradingTriedb.Dereference(tradingService.GetTriegc().PopItem()) } } if lendingService.GetTriegc() != nil { for !lendingService.GetTriegc().Empty() { - lendingTriedb.Dereference(lendingService.GetTriegc().PopItem().(common.Hash)) + lendingTriedb.Dereference(lendingService.GetTriegc().PopItem()) } } } @@ -1328,7 +1328,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types. bc.triegc.Push(root, number) break } - triedb.Dereference(root.(common.Hash)) + triedb.Dereference(root) } if tradingService != nil { for !tradingService.GetTriegc().Empty() { @@ -1337,7 +1337,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types. tradingService.GetTriegc().Push(tradingRoot, number) break } - tradingTrieDb.Dereference(tradingRoot.(common.Hash)) + tradingTrieDb.Dereference(tradingRoot) } } if lendingService != nil { @@ -1347,7 +1347,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types. lendingService.GetTriegc().Push(lendingRoot, number) break } - lendingTrieDb.Dereference(lendingRoot.(common.Hash)) + lendingTrieDb.Dereference(lendingRoot) } } } diff --git a/core/txpool/lending_pool.go b/core/txpool/lending_pool.go index 4a199384cf12..e73d71fc31c5 100644 --- a/core/txpool/lending_pool.go +++ b/core/txpool/lending_pool.go @@ -997,7 +997,7 @@ func (pool *LendingPool) promoteExecutables(accounts []common.Address) { if pending > pool.config.GlobalSlots { pendingBeforeCap := pending // Assemble a spam order to penalize large transactors first - spammers := prque.New(nil) + spammers := prque.New[int64, common.Address](nil) for addr, list := range pool.pending { // Only evict transactions from high rollers if !pool.locals.contains(addr) && uint64(list.Len()) > pool.config.AccountSlots { @@ -1009,12 +1009,12 @@ func (pool *LendingPool) promoteExecutables(accounts []common.Address) { for pending > pool.config.GlobalSlots && !spammers.Empty() { // Retrieve the next offender if not local address offender, _ := spammers.Pop() - offenders = append(offenders, offender.(common.Address)) + offenders = append(offenders, offender) // Equalize balances until all the same or below threshold if len(offenders) > 1 { // Calculate the equalization threshold for all current offenders - threshold := pool.pending[offender.(common.Address)].Len() + threshold := pool.pending[offender].Len() // Iteratively reduce all offenders until below limit or threshold reached for pending > pool.config.GlobalSlots && pool.pending[offenders[len(offenders)-2]].Len() > threshold { diff --git a/core/txpool/order_pool.go b/core/txpool/order_pool.go index 4857d993886e..247fdf6cef4f 100644 --- a/core/txpool/order_pool.go +++ b/core/txpool/order_pool.go @@ -914,7 +914,7 @@ func (pool *OrderPool) promoteExecutables(accounts []common.Address) { if pending > pool.config.GlobalSlots { pendingBeforeCap := pending // Assemble a spam order to penalize large transactors first - spammers := prque.New(nil) + spammers := prque.New[int64, common.Address](nil) for addr, list := range pool.pending { // Only evict transactions from high rollers if !pool.locals.contains(addr) && uint64(list.Len()) > pool.config.AccountSlots { @@ -926,12 +926,12 @@ func (pool *OrderPool) promoteExecutables(accounts []common.Address) { for pending > pool.config.GlobalSlots && !spammers.Empty() { // Retrieve the next offender if not local address offender, _ := spammers.Pop() - offenders = append(offenders, offender.(common.Address)) + offenders = append(offenders, offender) // Equalize balances until all the same or below threshold if len(offenders) > 1 { // Calculate the equalization threshold for all current offenders - threshold := pool.pending[offender.(common.Address)].Len() + threshold := pool.pending[offender].Len() // Iteratively reduce all offenders until below limit or threshold reached for pending > pool.config.GlobalSlots && pool.pending[offenders[len(offenders)-2]].Len() > threshold { diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index fc57d9355aa9..fd5f2381699f 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -1535,7 +1535,7 @@ func (pool *TxPool) truncatePending() { pendingBeforeCap := pending // Assemble a spam order to penalize large transactors first - spammers := prque.New(nil) + spammers := prque.New[int64, common.Address](nil) for addr, list := range pool.pending { // Only evict transactions from high rollers if !pool.locals.contains(addr) && uint64(list.Len()) > pool.config.AccountSlots { @@ -1547,12 +1547,12 @@ func (pool *TxPool) truncatePending() { for pending > pool.config.GlobalSlots && !spammers.Empty() { // Retrieve the next offender if not local address offender, _ := spammers.Pop() - offenders = append(offenders, offender.(common.Address)) + offenders = append(offenders, offender) // Equalize balances until all the same or below threshold if len(offenders) > 1 { // Calculate the equalization threshold for all current offenders - threshold := pool.pending[offender.(common.Address)].Len() + threshold := pool.pending[offender].Len() // Iteratively reduce all offenders until below limit or threshold reached for pending > pool.config.GlobalSlots && pool.pending[offenders[len(offenders)-2]].Len() > threshold { diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index b21460c2efd6..115a780f9210 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -70,7 +70,7 @@ type queue struct { // Headers are "special", they download in batches, supported by a skeleton chain headerHead common.Hash // [eth/62] Hash of the last queued header to verify order headerTaskPool map[uint64]*types.Header // [eth/62] Pending header retrieval tasks, mapping starting indexes to skeleton headers - headerTaskQueue *prque.Prque // [eth/62] Priority queue of the skeleton indexes to fetch the filling headers for + headerTaskQueue *prque.Prque[int64, uint64] // [eth/62] Priority queue of the skeleton indexes to fetch the filling headers for headerPeerMiss map[string]map[uint64]struct{} // [eth/62] Set of per-peer header batches known to be unavailable headerPendPool map[string]*fetchRequest // [eth/62] Currently pending header retrieval operations headerResults []*types.Header // [eth/62] Result cache accumulating the completed headers @@ -79,15 +79,15 @@ type queue struct { headerContCh chan bool // [eth/62] Channel to notify when header download finishes // All data retrievals below are based on an already assembles header chain - blockTaskPool map[common.Hash]*types.Header // [eth/62] Pending block (body) retrieval tasks, mapping hashes to headers - blockTaskQueue *prque.Prque // [eth/62] Priority queue of the headers to fetch the blocks (bodies) for - blockPendPool map[string]*fetchRequest // [eth/62] Currently pending block (body) retrieval operations - blockDonePool map[common.Hash]struct{} // [eth/62] Set of the completed block (body) fetches + blockTaskPool map[common.Hash]*types.Header // [eth/62] Pending block (body) retrieval tasks, mapping hashes to headers + blockTaskQueue *prque.Prque[int64, *types.Header] // [eth/62] Priority queue of the headers to fetch the blocks (bodies) for + blockPendPool map[string]*fetchRequest // [eth/62] Currently pending block (body) retrieval operations + blockDonePool map[common.Hash]struct{} // [eth/62] Set of the completed block (body) fetches - receiptTaskPool map[common.Hash]*types.Header // [eth/63] Pending receipt retrieval tasks, mapping hashes to headers - receiptTaskQueue *prque.Prque // [eth/63] Priority queue of the headers to fetch the receipts for - receiptPendPool map[string]*fetchRequest // [eth/63] Currently pending receipt retrieval operations - receiptDonePool map[common.Hash]struct{} // [eth/63] Set of the completed receipt fetches + receiptTaskPool map[common.Hash]*types.Header // [eth/63] Pending receipt retrieval tasks, mapping hashes to headers + receiptTaskQueue *prque.Prque[int64, *types.Header] // [eth/63] Priority queue of the headers to fetch the receipts for + receiptPendPool map[string]*fetchRequest // [eth/63] Currently pending receipt retrieval operations + receiptDonePool map[common.Hash]struct{} // [eth/63] Set of the completed receipt fetches resultCache []*fetchResult // Downloaded but not yet delivered fetch results resultOffset uint64 // Offset of the first cached fetch result in the block chain @@ -105,11 +105,11 @@ func newQueue() *queue { headerPendPool: make(map[string]*fetchRequest), headerContCh: make(chan bool), blockTaskPool: make(map[common.Hash]*types.Header), - blockTaskQueue: prque.New(nil), + blockTaskQueue: prque.New[int64, *types.Header](nil), blockPendPool: make(map[string]*fetchRequest), blockDonePool: make(map[common.Hash]struct{}), receiptTaskPool: make(map[common.Hash]*types.Header), - receiptTaskQueue: prque.New(nil), + receiptTaskQueue: prque.New[int64, *types.Header](nil), receiptPendPool: make(map[string]*fetchRequest), receiptDonePool: make(map[common.Hash]struct{}), resultCache: make([]*fetchResult, blockCacheItems), @@ -277,7 +277,7 @@ func (q *queue) ScheduleSkeleton(from uint64, skeleton []*types.Header) { } // Shedule all the header retrieval tasks for the skeleton assembly q.headerTaskPool = make(map[uint64]*types.Header) - q.headerTaskQueue = prque.New(nil) + q.headerTaskQueue = prque.New[int64, uint64](nil) q.headerPeerMiss = make(map[string]map[uint64]struct{}) // Reset availability to correct invalid chains q.headerResults = make([]*types.Header, len(skeleton)*MaxHeaderFetch) q.headerProced = 0 @@ -427,12 +427,12 @@ func (q *queue) ReserveHeaders(p *peerConnection, count int) *fetchRequest { for send == 0 && !q.headerTaskQueue.Empty() { from, _ := q.headerTaskQueue.Pop() if q.headerPeerMiss[p.id] != nil { - if _, ok := q.headerPeerMiss[p.id][from.(uint64)]; ok { - skip = append(skip, from.(uint64)) + if _, ok := q.headerPeerMiss[p.id][from]; ok { + skip = append(skip, from) continue } } - send = from.(uint64) + send = from } // Merge all the skipped batches back for _, from := range skip { @@ -484,7 +484,7 @@ func (q *queue) ReserveReceipts(p *peerConnection, count int) (*fetchRequest, bo // Note, this method expects the queue lock to be already held for writing. The // reason the lock is not obtained in here is because the parameters already need // to access the queue, so they already need a lock anyway. -func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common.Hash]*types.Header, taskQueue *prque.Prque, +func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common.Hash]*types.Header, taskQueue *prque.Prque[int64, *types.Header], pendPool map[string]*fetchRequest, donePool map[common.Hash]struct{}, isNoop func(*types.Header) bool) (*fetchRequest, bool, error) { // Short circuit if the pool has been depleted, or if the peer's already // downloading something (sanity check not to corrupt state) @@ -503,7 +503,7 @@ func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common progress := false for proc := 0; proc < space && len(send) < count && !taskQueue.Empty(); proc++ { - header := taskQueue.PopItem().(*types.Header) + header := taskQueue.PopItem() hash := header.Hash() // If we're the first to request this task, initialise the result container @@ -586,12 +586,12 @@ func (q *queue) CancelReceipts(request *fetchRequest) { } // Cancel aborts a fetch request, returning all pending hashes to the task queue. -func (q *queue) cancel(request *fetchRequest, taskQueue *prque.Prque, pendPool map[string]*fetchRequest) { +func (q *queue) cancel(request *fetchRequest, taskQueue interface{}, pendPool map[string]*fetchRequest) { if request.From > 0 { - taskQueue.Push(request.From, -int64(request.From)) + taskQueue.(*prque.Prque[int64, uint64]).Push(request.From, -int64(request.From)) } for _, header := range request.Headers { - taskQueue.Push(header, -int64(header.Number.Uint64())) + taskQueue.(*prque.Prque[int64, *types.Header]).Push(header, -int64(header.Number.Uint64())) } delete(pendPool, request.Peer.id) } @@ -650,7 +650,7 @@ func (q *queue) ExpireReceipts(timeout time.Duration) map[string]int { // Note, this method expects the queue lock to be already held. The // reason the lock is not obtained in here is because the parameters already need // to access the queue, so they already need a lock anyway. -func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest, taskQueue *prque.Prque, timeoutMeter *metrics.Meter) map[string]int { +func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest, taskQueue interface{}, timeoutMeter *metrics.Meter) map[string]int { // Iterate over the expired requests and return each to the queue expiries := make(map[string]int) for id, request := range pendPool { @@ -660,10 +660,10 @@ func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest, // Return any non satisfied requests to the pool if request.From > 0 { - taskQueue.Push(request.From, -int64(request.From)) + taskQueue.(*prque.Prque[int64, uint64]).Push(request.From, -int64(request.From)) } for _, header := range request.Headers { - taskQueue.Push(header, -int64(header.Number.Uint64())) + taskQueue.(*prque.Prque[int64, *types.Header]).Push(header, -int64(header.Number.Uint64())) } // Add the peer to the expiry report along the the number of failed requests expiries[id] = len(request.Headers) @@ -804,7 +804,7 @@ func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt) (int, // Note, this method expects the queue lock to be already held for writing. The // reason the lock is not obtained in here is because the parameters already need // to access the queue, so they already need a lock anyway. -func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQueue *prque.Prque, +func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQueue *prque.Prque[int64, *types.Header], pendPool map[string]*fetchRequest, donePool map[common.Hash]struct{}, reqTimer *metrics.Timer, results int, reconstruct func(header *types.Header, index int, result *fetchResult) error) (int, error) { diff --git a/eth/fetcher/fetcher.go b/eth/fetcher/fetcher.go index 259781a6bb11..f8cf67f23cf8 100644 --- a/eth/fetcher/fetcher.go +++ b/eth/fetcher/fetcher.go @@ -131,11 +131,10 @@ type Fetcher struct { completing map[common.Hash]*announce // Blocks with headers, currently body-completing // Block cache - queue *prque.Prque // Queue containing the import operations (block number sorted) - queues map[string]int // Per peer block counts to prevent memory exhaustion - queued map[common.Hash]*inject // Set of already queued blocks (to dedup imports) + queue *prque.Prque[int64, *inject] // Queue containing the import operations (block number sorted) + queues map[string]int // Per peer block counts to prevent memory exhaustion + queued map[common.Hash]*inject // Set of already queued blocks (to dedup imports) knowns *lru.Cache[common.Hash, struct{}] - // Callbacks getBlock blockRetrievalFn // Retrieves a block from the local chain verifyHeader headerVerifierFn // Checks if a block's headers have a valid proof of work @@ -170,7 +169,7 @@ func New(getBlock blockRetrievalFn, verifyHeader headerVerifierFn, handlePropose fetching: make(map[common.Hash]*announce), fetched: make(map[common.Hash][]*announce), completing: make(map[common.Hash]*announce), - queue: prque.New(nil), + queue: prque.New[int64, *inject](nil), queues: make(map[string]int), queued: make(map[common.Hash]*inject), knowns: lru.NewCache[common.Hash, struct{}](blockLimit), @@ -304,7 +303,7 @@ func (f *Fetcher) loop() { // Import any queued blocks that could potentially fit height := f.chainHeight() for !f.queue.Empty() { - op := f.queue.PopItem().(*inject) + op := f.queue.PopItem() if f.queueChangeHook != nil { f.queueChangeHook(op.block.Hash(), false) } diff --git a/go.mod b/go.mod index 9adb083eca74..83100986b004 100644 --- a/go.mod +++ b/go.mod @@ -53,6 +53,7 @@ require ( github.com/mattn/go-isatty v0.0.17 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible github.com/urfave/cli/v2 v2.27.5 + golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9 gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) diff --git a/go.sum b/go.sum index e59acf391656..7b0965c69d3c 100644 --- a/go.sum +++ b/go.sum @@ -214,6 +214,8 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9 h1:yZNXmy+j/JpX19vZkVktWqAo7Gny4PBWYYK3zskGpx4= +golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/trie/sync.go b/trie/sync.go index 645789f6ea96..021a836f3ad9 100644 --- a/trie/sync.go +++ b/trie/sync.go @@ -73,7 +73,7 @@ type Sync struct { database ethdb.KeyValueReader // Persistent database to check for existing entries membatch *syncMemBatch // Memory buffer to avoid frequent database writes requests map[common.Hash]*request // Pending requests pertaining to a key hash - queue *prque.Prque // Priority queue with the pending requests + queue *prque.Prque[int64, any] // Priority queue with the pending requests bloom *SyncBloom // Bloom filter for fast Node existence checks } @@ -83,7 +83,7 @@ func NewSync(root common.Hash, database ethdb.KeyValueReader, callback LeafCallb database: database, membatch: newSyncMemBatch(), requests: make(map[common.Hash]*request), - queue: prque.New(nil), + queue: prque.New[int64, any](nil), // Ugh, can contain both string and hash, whyyy bloom: bloom, } ts.AddSubTrie(root, 0, common.Hash{}, callback) From b58802a4020f042ff15e1d8281638e8790c75608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Wed, 22 Feb 2023 13:55:09 +0200 Subject: [PATCH 438/479] common/math: allow HexOrDecimal to accept unquoted decimals too (#26758) --- common/math/big.go | 18 ++++++++++++++++++ common/math/integer.go | 11 +++++++++++ 2 files changed, 29 insertions(+) diff --git a/common/math/big.go b/common/math/big.go index 7254efed4823..d6b2c5097b8f 100644 --- a/common/math/big.go +++ b/common/math/big.go @@ -41,6 +41,24 @@ const ( // HexOrDecimal256 marshals big.Int as hex or decimal. type HexOrDecimal256 big.Int +// NewHexOrDecimal256 creates a new HexOrDecimal256 +func NewHexOrDecimal256(x int64) *HexOrDecimal256 { + b := big.NewInt(x) + h := HexOrDecimal256(*b) + return &h +} + +// UnmarshalJSON implements json.Unmarshaler. +// +// It is similar to UnmarshalText, but allows parsing real decimals too, not just +// quoted decimal strings. +func (i *HexOrDecimal256) UnmarshalJSON(input []byte) error { + if len(input) > 0 && input[0] == '"' { + input = input[1 : len(input)-1] + } + return i.UnmarshalText(input) +} + // UnmarshalText implements encoding.TextUnmarshaler. func (i *HexOrDecimal256) UnmarshalText(input []byte) error { bigint, ok := ParseBig256(string(input)) diff --git a/common/math/integer.go b/common/math/integer.go index 17a9fb1375d1..e78949299185 100644 --- a/common/math/integer.go +++ b/common/math/integer.go @@ -41,6 +41,17 @@ const ( // HexOrDecimal64 marshals uint64 as hex or decimal. type HexOrDecimal64 uint64 +// UnmarshalJSON implements json.Unmarshaler. +// +// It is similar to UnmarshalText, but allows parsing real decimals too, not just +// quoted decimal strings. +func (i *HexOrDecimal64) UnmarshalJSON(input []byte) error { + if len(input) > 0 && input[0] == '"' { + input = input[1 : len(input)-1] + } + return i.UnmarshalText(input) +} + // UnmarshalText implements encoding.TextUnmarshaler. func (i *HexOrDecimal64) UnmarshalText(input []byte) error { int, ok := ParseUint64(string(input)) From 12111d30ea4730c7f8826778f225c16933046fd1 Mon Sep 17 00:00:00 2001 From: lightclient <14004106+lightclient@users.noreply.github.com> Date: Mon, 3 Apr 2023 01:51:31 -0600 Subject: [PATCH 439/479] common: delete MakeName (#27023) common,p2p: remove unused function MakeName --- common/path.go | 9 --------- p2p/server.go | 1 - 2 files changed, 10 deletions(-) diff --git a/common/path.go b/common/path.go index 69820cfe5dec..c1e382fd29c4 100644 --- a/common/path.go +++ b/common/path.go @@ -17,19 +17,10 @@ package common import ( - "fmt" "os" "path/filepath" - "runtime" ) -// MakeName creates a node name that follows the ethereum convention -// for such names. It adds the operation system name and Go runtime version -// the name. -func MakeName(name, version string) string { - return fmt.Sprintf("%s/v%s/%s/%s", name, version, runtime.GOOS, runtime.Version()) -} - // FileExist checks if a file exists at filePath. func FileExist(filePath string) bool { _, err := os.Stat(filePath) diff --git a/p2p/server.go b/p2p/server.go index 51579ff7c30c..a2cdb1707724 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -81,7 +81,6 @@ type Config struct { DiscoveryV5 bool `toml:",omitempty"` // Name sets the node name of this server. - // Use common.MakeName to create a name that follows existing conventions. Name string `toml:"-"` // BootstrapNodes are used to establish connectivity From 59b14ed2cbd1c6cc612a5b0422678d180ce5df43 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 18 Dec 2024 19:02:48 +0800 Subject: [PATCH 440/479] internal/ethapi: make NewAccount return EIP-55 format (#26973) --- common/types.go | 14 ++++++++++++++ common/types_test.go | 24 ++++++++++++++++++++++++ internal/ethapi/api.go | 10 +++++++--- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/common/types.go b/common/types.go index d32343ccd4bc..5f2f366c99b2 100644 --- a/common/types.go +++ b/common/types.go @@ -19,6 +19,7 @@ package common import ( "bytes" "encoding/hex" + "encoding/json" "fmt" "math/big" "math/rand" @@ -405,3 +406,16 @@ func ExtractAddressFromBytes(bytePenalties []byte) []Address { } return penalties } + +// AddressEIP55 is an alias of Address with a customized json marshaller +type AddressEIP55 Address + +// String returns the hex representation of the address in the manner of EIP55. +func (addr AddressEIP55) String() string { + return Address(addr).Hex() +} + +// MarshalJSON marshals the address in the manner of EIP55. +func (addr AddressEIP55) MarshalJSON() ([]byte, error) { + return json.Marshal(addr.String()) +} diff --git a/common/types_test.go b/common/types_test.go index 598286c08978..3262f549310b 100644 --- a/common/types_test.go +++ b/common/types_test.go @@ -371,6 +371,30 @@ func TestStringToBinaryAddress(t *testing.T) { } } +func TestAddressEIP55(t *testing.T) { + addr := HexToAddress("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed") + addrEIP55 := AddressEIP55(addr) + + if addr.Hex() != addrEIP55.String() { + t.Fatal("AddressEIP55 should match original address hex") + } + + blob, err := addrEIP55.MarshalJSON() + if err != nil { + t.Fatal("Failed to marshal AddressEIP55", err) + } + if strings.Trim(string(blob), "\"") != addr.Hex() { + t.Fatal("Address with checksum is expected") + } + var dec Address + if err := json.Unmarshal(blob, &dec); err != nil { + t.Fatal("Failed to unmarshal AddressEIP55", err) + } + if addr != dec { + t.Fatal("Unexpected address after unmarshal") + } +} + func BenchmarkPrettyDuration(b *testing.B) { var x = PrettyDuration(time.Duration(int64(1203123912312))) b.Logf("Pre %s", time.Duration(x).String()) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index f88c79d4940f..c371f1442594 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -383,12 +383,16 @@ func (s *PrivateAccountAPI) DeriveAccount(url string, path string, pin *bool) (a } // NewAccount will create a new account and returns the address for the new account. -func (s *PrivateAccountAPI) NewAccount(password string) (common.Address, error) { +func (s *PrivateAccountAPI) NewAccount(password string) (common.AddressEIP55, error) { acc, err := fetchKeystore(s.am).NewAccount(password) if err == nil { - return acc.Address, nil + addrEIP55 := common.AddressEIP55(acc.Address) + log.Info("Your new key was generated", "address", addrEIP55.String()) + log.Warn("Please backup your key file!", "path", acc.URL.Path) + log.Warn("Please remember your password!") + return addrEIP55, nil } - return common.Address{}, err + return common.AddressEIP55{}, err } // fetchKeystore retrives the encrypted keystore from the account manager. From f74cccd1cbdaa5deef4ab78c6214a7c2f8631d65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Wed, 17 May 2023 16:39:33 +0200 Subject: [PATCH 441/479] beacon/types: add beacon chain data types (#27292) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * beacon/types: add beacon chain data types * beacon/merkle: added comments * go.mod: cleanups --------- Co-authored-by: Péter Szilágyi --- beacon/merkle/merkle.go | 67 ++++++++++ beacon/params/params.go | 44 +++++++ beacon/types/committee.go | 214 ++++++++++++++++++++++++++++++++ beacon/types/config.go | 176 ++++++++++++++++++++++++++ beacon/types/gen_header_json.go | 66 ++++++++++ beacon/types/header.go | 121 ++++++++++++++++++ beacon/types/update.go | 118 ++++++++++++++++++ common/types.go | 20 +++ go.mod | 3 + go.sum | 7 ++ 10 files changed, 836 insertions(+) create mode 100644 beacon/merkle/merkle.go create mode 100644 beacon/params/params.go create mode 100644 beacon/types/committee.go create mode 100644 beacon/types/config.go create mode 100644 beacon/types/gen_header_json.go create mode 100644 beacon/types/header.go create mode 100644 beacon/types/update.go diff --git a/beacon/merkle/merkle.go b/beacon/merkle/merkle.go new file mode 100644 index 000000000000..d1c18a2cf151 --- /dev/null +++ b/beacon/merkle/merkle.go @@ -0,0 +1,67 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package merkle implements proof verifications in binary merkle trees. +package merkle + +import ( + "crypto/sha256" + "errors" + "reflect" + + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/hexutil" +) + +// Value represents either a 32 byte leaf value or hash node in a binary merkle tree/partial proof. +type Value [32]byte + +// Values represent a series of merkle tree leaves/nodes. +type Values []Value + +var valueT = reflect.TypeOf(Value{}) + +// UnmarshalJSON parses a merkle value in hex syntax. +func (m *Value) UnmarshalJSON(input []byte) error { + return hexutil.UnmarshalFixedJSON(valueT, input, m[:]) +} + +// VerifyProof verifies a Merkle proof branch for a single value in a +// binary Merkle tree (index is a generalized tree index). +func VerifyProof(root common.Hash, index uint64, branch Values, value Value) error { + hasher := sha256.New() + for _, sibling := range branch { + hasher.Reset() + if index&1 == 0 { + hasher.Write(value[:]) + hasher.Write(sibling[:]) + } else { + hasher.Write(sibling[:]) + hasher.Write(value[:]) + } + hasher.Sum(value[:0]) + if index >>= 1; index == 0 { + return errors.New("branch has extra items") + } + } + if index != 1 { + return errors.New("branch is missing items") + } + if common.Hash(value) != root { + return errors.New("root mismatch") + } + return nil +} diff --git a/beacon/params/params.go b/beacon/params/params.go new file mode 100644 index 000000000000..ee9feb1acbea --- /dev/null +++ b/beacon/params/params.go @@ -0,0 +1,44 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package params + +const ( + EpochLength = 32 + SyncPeriodLength = 8192 + + BLSSignatureSize = 96 + BLSPubkeySize = 48 + + SyncCommitteeSize = 512 + SyncCommitteeBitmaskSize = SyncCommitteeSize / 8 + SyncCommitteeSupermajority = (SyncCommitteeSize*2 + 2) / 3 +) + +const ( + StateIndexGenesisTime = 32 + StateIndexGenesisValidators = 33 + StateIndexForkVersion = 141 + StateIndexLatestHeader = 36 + StateIndexBlockRoots = 37 + StateIndexStateRoots = 38 + StateIndexHistoricRoots = 39 + StateIndexFinalBlock = 105 + StateIndexSyncCommittee = 54 + StateIndexNextSyncCommittee = 55 + StateIndexExecPayload = 56 + StateIndexExecHead = 908 +) diff --git a/beacon/types/committee.go b/beacon/types/committee.go new file mode 100644 index 000000000000..80af480d2262 --- /dev/null +++ b/beacon/types/committee.go @@ -0,0 +1,214 @@ +// Copyright 2023 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "crypto/sha256" + "encoding/json" + "fmt" + "math/bits" + + "github.com/XinFinOrg/XDPoSChain/beacon/params" + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/hexutil" + bls "github.com/protolambda/bls12-381-util" +) + +// SerializedSyncCommitteeSize is the size of the sync committee plus the +// aggregate public key. +const SerializedSyncCommitteeSize = (params.SyncCommitteeSize + 1) * params.BLSPubkeySize + +// SerializedSyncCommittee is the serialized version of a sync committee +// plus the aggregate public key. +type SerializedSyncCommittee [SerializedSyncCommitteeSize]byte + +// jsonSyncCommittee is the JSON representation of a sync committee. +// +// See data structure definition here: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#syncaggregate +type jsonSyncCommittee struct { + Pubkeys []hexutil.Bytes `json:"pubkeys"` + Aggregate hexutil.Bytes `json:"aggregate_pubkey"` +} + +// MarshalJSON implements json.Marshaler. +func (s *SerializedSyncCommittee) MarshalJSON() ([]byte, error) { + sc := jsonSyncCommittee{Pubkeys: make([]hexutil.Bytes, params.SyncCommitteeSize)} + for i := range sc.Pubkeys { + sc.Pubkeys[i] = make(hexutil.Bytes, params.BLSPubkeySize) + copy(sc.Pubkeys[i][:], s[i*params.BLSPubkeySize:(i+1)*params.BLSPubkeySize]) + } + sc.Aggregate = make(hexutil.Bytes, params.BLSPubkeySize) + copy(sc.Aggregate[:], s[params.SyncCommitteeSize*params.BLSPubkeySize:]) + return json.Marshal(&sc) +} + +// UnmarshalJSON implements json.Marshaler. +func (s *SerializedSyncCommittee) UnmarshalJSON(input []byte) error { + var sc jsonSyncCommittee + if err := json.Unmarshal(input, &sc); err != nil { + return err + } + if len(sc.Pubkeys) != params.SyncCommitteeSize { + return fmt.Errorf("invalid number of pubkeys %d", len(sc.Pubkeys)) + } + for i, key := range sc.Pubkeys { + if len(key) != params.BLSPubkeySize { + return fmt.Errorf("pubkey %d has invalid size %d", i, len(key)) + } + copy(s[i*params.BLSPubkeySize:], key[:]) + } + if len(sc.Aggregate) != params.BLSPubkeySize { + return fmt.Errorf("invalid aggregate pubkey size %d", len(sc.Aggregate)) + } + copy(s[params.SyncCommitteeSize*params.BLSPubkeySize:], sc.Aggregate[:]) + return nil +} + +// Root calculates the root hash of the binary tree representation of a sync +// committee provided in serialized format. +// +// TODO(zsfelfoldi): Get rid of this when SSZ encoding lands. +func (s *SerializedSyncCommittee) Root() common.Hash { + var ( + hasher = sha256.New() + padding [64 - params.BLSPubkeySize]byte + data [params.SyncCommitteeSize]common.Hash + l = params.SyncCommitteeSize + ) + for i := range data { + hasher.Reset() + hasher.Write(s[i*params.BLSPubkeySize : (i+1)*params.BLSPubkeySize]) + hasher.Write(padding[:]) + hasher.Sum(data[i][:0]) + } + for l > 1 { + for i := 0; i < l/2; i++ { + hasher.Reset() + hasher.Write(data[i*2][:]) + hasher.Write(data[i*2+1][:]) + hasher.Sum(data[i][:0]) + } + l /= 2 + } + hasher.Reset() + hasher.Write(s[SerializedSyncCommitteeSize-params.BLSPubkeySize : SerializedSyncCommitteeSize]) + hasher.Write(padding[:]) + hasher.Sum(data[1][:0]) + hasher.Reset() + hasher.Write(data[0][:]) + hasher.Write(data[1][:]) + hasher.Sum(data[0][:0]) + return data[0] +} + +// Deserialize splits open the pubkeys into proper BLS key types. +func (s *SerializedSyncCommittee) Deserialize() (*SyncCommittee, error) { + sc := new(SyncCommittee) + for i := 0; i <= params.SyncCommitteeSize; i++ { + key := new(bls.Pubkey) + + var bytes [params.BLSPubkeySize]byte + copy(bytes[:], s[i*params.BLSPubkeySize:(i+1)*params.BLSPubkeySize]) + + if err := key.Deserialize(&bytes); err != nil { + return nil, err + } + if i < params.SyncCommitteeSize { + sc.keys[i] = key + } else { + sc.aggregate = key + } + } + return sc, nil +} + +// SyncCommittee is a set of sync committee signer pubkeys and the aggregate key. +// +// See data structure definition here: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#syncaggregate +type SyncCommittee struct { + keys [params.SyncCommitteeSize]*bls.Pubkey + aggregate *bls.Pubkey +} + +// VerifySignature returns true if the given sync aggregate is a valid signature +// or the given hash. +func (sc *SyncCommittee) VerifySignature(signingRoot common.Hash, signature *SyncAggregate) bool { + var ( + sig bls.Signature + keys = make([]*bls.Pubkey, 0, params.SyncCommitteeSize) + ) + if err := sig.Deserialize(&signature.Signature); err != nil { + return false + } + for i, key := range sc.keys { + if signature.Signers[i/8]&(byte(1)<<(i%8)) != 0 { + keys = append(keys, key) + } + } + return bls.FastAggregateVerify(keys, signingRoot[:], &sig) +} + +// SyncAggregate represents an aggregated BLS signature with Signers referring +// to a subset of the corresponding sync committee. +// +// See data structure definition here: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#syncaggregate +type SyncAggregate struct { + Signers [params.SyncCommitteeBitmaskSize]byte + Signature [params.BLSSignatureSize]byte +} + +type jsonSyncAggregate struct { + Signers hexutil.Bytes `json:"sync_committee_bits"` + Signature hexutil.Bytes `json:"sync_committee_signature"` +} + +// MarshalJSON implements json.Marshaler. +func (s *SyncAggregate) MarshalJSON() ([]byte, error) { + return json.Marshal(&jsonSyncAggregate{ + Signers: s.Signers[:], + Signature: s.Signature[:], + }) +} + +// UnmarshalJSON implements json.Marshaler. +func (s *SyncAggregate) UnmarshalJSON(input []byte) error { + var sc jsonSyncAggregate + if err := json.Unmarshal(input, &sc); err != nil { + return err + } + if len(sc.Signers) != params.SyncCommitteeBitmaskSize { + return fmt.Errorf("invalid signature bitmask size %d", len(sc.Signers)) + } + if len(sc.Signature) != params.BLSSignatureSize { + return fmt.Errorf("invalid signature size %d", len(sc.Signature)) + } + copy(s.Signers[:], sc.Signers) + copy(s.Signature[:], sc.Signature) + return nil +} + +// SignerCount returns the number of signers in the aggregate signature. +func (s *SyncAggregate) SignerCount() int { + var count int + for _, v := range s.Signers { + count += bits.OnesCount8(v) + } + return count +} diff --git a/beacon/types/config.go b/beacon/types/config.go new file mode 100644 index 000000000000..abe2b8f60193 --- /dev/null +++ b/beacon/types/config.go @@ -0,0 +1,176 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "crypto/sha256" + "fmt" + "os" + "sort" + "strconv" + "strings" + + "github.com/XinFinOrg/XDPoSChain/beacon/merkle" + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/hexutil" + "github.com/go-yaml/yaml" +) + +// syncCommitteeDomain specifies the signatures specific use to avoid clashes +// across signing different data structures. +const syncCommitteeDomain = 7 + +// Fork describes a single beacon chain fork and also stores the calculated +// signature domain used after this fork. +type Fork struct { + // Name of the fork in the chain config (config.yaml) file{ + Name string + + // Epoch when given fork version is activated + Epoch uint64 + + // Fork version, see https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#custom-types + Version []byte + + // calculated by computeDomain, based on fork version and genesis validators root + domain merkle.Value +} + +// computeDomain returns the signature domain based on the given fork version +// and genesis validator set root. +func (f *Fork) computeDomain(genesisValidatorsRoot common.Hash) { + var ( + hasher = sha256.New() + forkVersion32 merkle.Value + forkDataRoot merkle.Value + ) + copy(forkVersion32[:], f.Version) + hasher.Write(forkVersion32[:]) + hasher.Write(genesisValidatorsRoot[:]) + hasher.Sum(forkDataRoot[:0]) + + f.domain[0] = syncCommitteeDomain + copy(f.domain[4:], forkDataRoot[:28]) +} + +// Forks is the list of all beacon chain forks in the chain configuration. +type Forks []*Fork + +// domain returns the signature domain for the given epoch (assumes that domains +// have already been calculated). +func (f Forks) domain(epoch uint64) (merkle.Value, error) { + for i := len(f) - 1; i >= 0; i-- { + if epoch >= f[i].Epoch { + return f[i].domain, nil + } + } + return merkle.Value{}, fmt.Errorf("unknown fork for epoch %d", epoch) +} + +// SigningRoot calculates the signing root of the given header. +func (f Forks) SigningRoot(header Header) (common.Hash, error) { + domain, err := f.domain(header.Epoch()) + if err != nil { + return common.Hash{}, err + } + var ( + signingRoot common.Hash + headerHash = header.Hash() + hasher = sha256.New() + ) + hasher.Write(headerHash[:]) + hasher.Write(domain[:]) + hasher.Sum(signingRoot[:0]) + + return signingRoot, nil +} + +func (f Forks) Len() int { return len(f) } +func (f Forks) Swap(i, j int) { f[i], f[j] = f[j], f[i] } +func (f Forks) Less(i, j int) bool { return f[i].Epoch < f[j].Epoch } + +// ChainConfig contains the beacon chain configuration. +type ChainConfig struct { + GenesisTime uint64 // Unix timestamp of slot 0 + GenesisValidatorsRoot common.Hash // Root hash of the genesis validator set, used for signature domain calculation + Forks Forks +} + +// AddFork adds a new item to the list of forks. +func (c *ChainConfig) AddFork(name string, epoch uint64, version []byte) *ChainConfig { + fork := &Fork{ + Name: name, + Epoch: epoch, + Version: version, + } + fork.computeDomain(c.GenesisValidatorsRoot) + + c.Forks = append(c.Forks, fork) + sort.Sort(c.Forks) + + return c +} + +// LoadForks parses the beacon chain configuration file (config.yaml) and extracts +// the list of forks. +func (c *ChainConfig) LoadForks(path string) error { + file, err := os.ReadFile(path) + if err != nil { + return fmt.Errorf("failed to read beacon chain config file: %v", err) + } + config := make(map[string]string) + if err := yaml.Unmarshal(file, &config); err != nil { + return fmt.Errorf("failed to parse beacon chain config file: %v", err) + } + var ( + versions = make(map[string][]byte) + epochs = make(map[string]uint64) + ) + epochs["GENESIS"] = 0 + + for key, value := range config { + if strings.HasSuffix(key, "_FORK_VERSION") { + name := key[:len(key)-len("_FORK_VERSION")] + if v, err := hexutil.Decode(value); err == nil { + versions[name] = v + } else { + return fmt.Errorf("failed to decode hex fork id %q in beacon chain config file: %v", value, err) + } + } + if strings.HasSuffix(key, "_FORK_EPOCH") { + name := key[:len(key)-len("_FORK_EPOCH")] + if v, err := strconv.ParseUint(value, 10, 64); err == nil { + epochs[name] = v + } else { + return fmt.Errorf("failed to parse epoch number %q in beacon chain config file: %v", value, err) + } + } + } + for name, epoch := range epochs { + if version, ok := versions[name]; ok { + delete(versions, name) + c.AddFork(name, epoch, version) + } else { + return fmt.Errorf("fork id missing for %q in beacon chain config file", name) + } + } + for name := range versions { + return fmt.Errorf("epoch number missing for fork %q in beacon chain config file", name) + } + sort.Sort(c.Forks) + return nil +} diff --git a/beacon/types/gen_header_json.go b/beacon/types/gen_header_json.go new file mode 100644 index 000000000000..f15881c53fce --- /dev/null +++ b/beacon/types/gen_header_json.go @@ -0,0 +1,66 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package types + +import ( + "encoding/json" + "errors" + + "github.com/XinFinOrg/XDPoSChain/common" +) + +var _ = (*headerMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (h Header) MarshalJSON() ([]byte, error) { + type Header struct { + Slot common.Decimal `gencodec:"required" json:"slot"` + ProposerIndex common.Decimal `gencodec:"required" json:"proposer_index"` + ParentRoot common.Hash `gencodec:"required" json:"parent_root"` + StateRoot common.Hash `gencodec:"required" json:"state_root"` + BodyRoot common.Hash `gencodec:"required" json:"body_root"` + } + var enc Header + enc.Slot = common.Decimal(h.Slot) + enc.ProposerIndex = common.Decimal(h.ProposerIndex) + enc.ParentRoot = h.ParentRoot + enc.StateRoot = h.StateRoot + enc.BodyRoot = h.BodyRoot + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (h *Header) UnmarshalJSON(input []byte) error { + type Header struct { + Slot *common.Decimal `gencodec:"required" json:"slot"` + ProposerIndex *common.Decimal `gencodec:"required" json:"proposer_index"` + ParentRoot *common.Hash `gencodec:"required" json:"parent_root"` + StateRoot *common.Hash `gencodec:"required" json:"state_root"` + BodyRoot *common.Hash `gencodec:"required" json:"body_root"` + } + var dec Header + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Slot == nil { + return errors.New("missing required field 'slot' for Header") + } + h.Slot = uint64(*dec.Slot) + if dec.ProposerIndex == nil { + return errors.New("missing required field 'proposer_index' for Header") + } + h.ProposerIndex = uint64(*dec.ProposerIndex) + if dec.ParentRoot == nil { + return errors.New("missing required field 'parent_root' for Header") + } + h.ParentRoot = *dec.ParentRoot + if dec.StateRoot == nil { + return errors.New("missing required field 'state_root' for Header") + } + h.StateRoot = *dec.StateRoot + if dec.BodyRoot == nil { + return errors.New("missing required field 'body_root' for Header") + } + h.BodyRoot = *dec.BodyRoot + return nil +} diff --git a/beacon/types/header.go b/beacon/types/header.go new file mode 100644 index 000000000000..c4a21598b590 --- /dev/null +++ b/beacon/types/header.go @@ -0,0 +1,121 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package types implements a few types of the beacon chain for light client usage. +package types + +import ( + "crypto/sha256" + "encoding/binary" + + "github.com/XinFinOrg/XDPoSChain/beacon/merkle" + "github.com/XinFinOrg/XDPoSChain/beacon/params" + "github.com/XinFinOrg/XDPoSChain/common" +) + +//go:generate go run github.com/fjl/gencodec -type Header -field-override headerMarshaling -out gen_header_json.go + +const ( + headerIndexSlot = 8 + headerIndexProposerIndex = 9 + headerIndexParentRoot = 10 + headerIndexStateRoot = 11 + headerIndexBodyRoot = 12 +) + +// Header defines a beacon header. +// +// See data structure definition here: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblockheader +type Header struct { + // Monotonically increasing slot number for the beacon block (may be gapped) + Slot uint64 `gencodec:"required" json:"slot"` + + // Index into the validator table who created the beacon block + ProposerIndex uint64 `gencodec:"required" json:"proposer_index"` + + // SSZ hash of the parent beacon header + ParentRoot common.Hash `gencodec:"required" json:"parent_root"` + + // SSZ hash of the beacon state (https://github.com/ethereum/consensus-specs/blob/dev/specs/bellatrix/beacon-chain.md#beacon-state) + StateRoot common.Hash `gencodec:"required" json:"state_root"` + + // SSZ hash of the beacon block body (https://github.com/ethereum/consensus-specs/blob/dev/specs/bellatrix/beacon-chain.md#beaconblockbody) + BodyRoot common.Hash `gencodec:"required" json:"body_root"` +} + +// headerMarshaling is a field type overrides for gencodec. +type headerMarshaling struct { + Slot common.Decimal + ProposerIndex common.Decimal +} + +// Hash calculates the block root of the header. +// +// TODO(zsfelfoldi): Remove this when an SSZ encoder lands. +func (h *Header) Hash() common.Hash { + var values [16]merkle.Value // values corresponding to indices 8 to 15 of the beacon header tree + binary.LittleEndian.PutUint64(values[headerIndexSlot][:8], h.Slot) + binary.LittleEndian.PutUint64(values[headerIndexProposerIndex][:8], h.ProposerIndex) + values[headerIndexParentRoot] = merkle.Value(h.ParentRoot) + values[headerIndexStateRoot] = merkle.Value(h.StateRoot) + values[headerIndexBodyRoot] = merkle.Value(h.BodyRoot) + hasher := sha256.New() + for i := 7; i > 0; i-- { + hasher.Reset() + hasher.Write(values[i*2][:]) + hasher.Write(values[i*2+1][:]) + hasher.Sum(values[i][:0]) + } + return common.Hash(values[1]) +} + +// Epoch returns the epoch the header belongs to. +func (h *Header) Epoch() uint64 { + return h.Slot / params.EpochLength +} + +// SyncPeriod returns the sync period the header belongs to. +func (h *Header) SyncPeriod() uint64 { + return SyncPeriod(h.Slot) +} + +// SyncPeriodStart returns the first slot of the given period. +func SyncPeriodStart(period uint64) uint64 { + return period * params.SyncPeriodLength +} + +// SyncPeriod returns the sync period that the given slot belongs to. +func SyncPeriod(slot uint64) uint64 { + return slot / params.SyncPeriodLength +} + +// SignedHeader represents a beacon header signed by a sync committee. +// +// This structure is created from either an optimistic update or an instant update: +// - https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#lightclientoptimisticupdate +// - https://github.com/zsfelfoldi/beacon-APIs/blob/instant_update/apis/beacon/light_client/instant_update.yaml +type SignedHeader struct { + // Beacon header being signed + Header Header + + // Sync committee BLS signature aggregate + Signature SyncAggregate + + // Slot in which the signature has been created (newer than Header.Slot, + // determines the signing sync committee) + SignatureSlot uint64 +} diff --git a/beacon/types/update.go b/beacon/types/update.go new file mode 100644 index 000000000000..697ffedf534e --- /dev/null +++ b/beacon/types/update.go @@ -0,0 +1,118 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "errors" + "fmt" + + "github.com/XinFinOrg/XDPoSChain/beacon/merkle" + "github.com/XinFinOrg/XDPoSChain/beacon/params" + "github.com/XinFinOrg/XDPoSChain/common" +) + +// LightClientUpdate is a proof of the next sync committee root based on a header +// signed by the sync committee of the given period. Optionally, the update can +// prove quasi-finality by the signed header referring to a previous, finalized +// header from the same period, and the finalized header referring to the next +// sync committee root. +// +// See data structure definition here: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#lightclientupdate +type LightClientUpdate struct { + AttestedHeader SignedHeader // Arbitrary header out of the period signed by the sync committee + NextSyncCommitteeRoot common.Hash // Sync committee of the next period advertised in the current one + NextSyncCommitteeBranch merkle.Values // Proof for the next period's sync committee + + FinalizedHeader *Header `rlp:"nil"` // Optional header to announce a point of finality + FinalityBranch merkle.Values // Proof for the announced finality + + score *UpdateScore // Weight of the update to compare between competing ones +} + +// Validate verifies the validity of the update. +func (update *LightClientUpdate) Validate() error { + period := update.AttestedHeader.Header.SyncPeriod() + if SyncPeriod(update.AttestedHeader.SignatureSlot) != period { + return errors.New("signature slot and signed header are from different periods") + } + if update.FinalizedHeader != nil { + if update.FinalizedHeader.SyncPeriod() != period { + return errors.New("finalized header is from different period") + } + if err := merkle.VerifyProof(update.AttestedHeader.Header.StateRoot, params.StateIndexFinalBlock, update.FinalityBranch, merkle.Value(update.FinalizedHeader.Hash())); err != nil { + return fmt.Errorf("invalid finalized header proof: %w", err) + } + } + if err := merkle.VerifyProof(update.AttestedHeader.Header.StateRoot, params.StateIndexNextSyncCommittee, update.NextSyncCommitteeBranch, merkle.Value(update.NextSyncCommitteeRoot)); err != nil { + return fmt.Errorf("invalid next sync committee proof: %w", err) + } + return nil +} + +// Score returns the UpdateScore describing the proof strength of the update +// Note: thread safety can be ensured by always calling Score on a newly received +// or decoded update before making it potentially available for other threads +func (update *LightClientUpdate) Score() UpdateScore { + if update.score == nil { + update.score = &UpdateScore{ + SignerCount: uint32(update.AttestedHeader.Signature.SignerCount()), + SubPeriodIndex: uint32(update.AttestedHeader.Header.Slot & 0x1fff), + FinalizedHeader: update.FinalizedHeader != nil, + } + } + return *update.score +} + +// UpdateScore allows the comparison between updates at the same period in order +// to find the best update chain that provides the strongest proof of being canonical. +// +// UpdateScores have a tightly packed binary encoding format for efficient p2p +// protocol transmission. Each UpdateScore is encoded in 3 bytes. +// When interpreted as a 24 bit little indian unsigned integer: +// - the lowest 10 bits contain the number of signers in the header signature aggregate +// - the next 13 bits contain the "sub-period index" which is he signed header's +// slot modulo params.SyncPeriodLength (which is correlated with the risk of the chain being +// re-orged before the previous period boundary in case of non-finalized updates) +// - the highest bit is set when the update is finalized (meaning that the finality +// header referenced by the signed header is in the same period as the signed +// header, making reorgs before the period boundary impossible +type UpdateScore struct { + SignerCount uint32 // number of signers in the header signature aggregate + SubPeriodIndex uint32 // signed header's slot modulo params.SyncPeriodLength + FinalizedHeader bool // update is considered finalized if has finalized header from the same period and 2/3 signatures +} + +// finalized returns true if the update has a header signed by at least 2/3 of +// the committee, referring to a finalized header that refers to the next sync +// committee. This condition is a close approximation of the actual finality +// condition that can only be verified by full beacon nodes. +func (u *UpdateScore) finalized() bool { + return u.FinalizedHeader && u.SignerCount >= params.SyncCommitteeSupermajority +} + +// BetterThan returns true if update u is considered better than w. +func (u UpdateScore) BetterThan(w UpdateScore) bool { + var ( + uFinalized = u.finalized() + wFinalized = w.finalized() + ) + if uFinalized != wFinalized { + return uFinalized + } + return u.SignerCount > w.SignerCount +} diff --git a/common/types.go b/common/types.go index 5f2f366c99b2..5f9f58b18e94 100644 --- a/common/types.go +++ b/common/types.go @@ -24,6 +24,7 @@ import ( "math/big" "math/rand" "reflect" + "strconv" "github.com/XinFinOrg/XDPoSChain/common/hexutil" "golang.org/x/crypto/sha3" @@ -419,3 +420,22 @@ func (addr AddressEIP55) String() string { func (addr AddressEIP55) MarshalJSON() ([]byte, error) { return json.Marshal(addr.String()) } + +type Decimal uint64 + +func isString(input []byte) bool { + return len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' +} + +// UnmarshalJSON parses a hash in hex syntax. +func (d *Decimal) UnmarshalJSON(input []byte) error { + if !isString(input) { + return &json.UnmarshalTypeError{Value: "non-string", Type: reflect.TypeOf(uint64(0))} + } + if i, err := strconv.ParseInt(string(input[1:len(input)-1]), 10, 64); err == nil { + *d = Decimal(i) + return nil + } else { + return err + } +} diff --git a/go.mod b/go.mod index 83100986b004..fcbe37d9a954 100644 --- a/go.mod +++ b/go.mod @@ -68,14 +68,17 @@ require ( github.com/dlclark/regexp2 v1.10.0 // indirect github.com/go-ole/go-ole v1.2.5 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect + github.com/go-yaml/yaml v2.1.0+incompatible // indirect github.com/google/uuid v1.3.0 // indirect github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect + github.com/kilic/bls12-381 v0.1.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/naoina/go-stringutil v0.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect diff --git a/go.sum b/go.sum index 7b0965c69d3c..73c7510dc470 100644 --- a/go.sum +++ b/go.sum @@ -63,6 +63,8 @@ github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o= +github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -107,6 +109,8 @@ github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4d github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/karalabe/hid v1.0.0 h1:+/CIMNXhSU/zIJgnIvBD2nKHxS/bnRHhhs9xBryLpPo= github.com/karalabe/hid v1.0.0/go.mod h1:Vr51f8rUOLYrfrWDFlV12GGQgM5AT8sVh+2fY4MPeu8= +github.com/kilic/bls12-381 v0.1.0 h1:encrdjqKMEvabVQ7qYOKu1OvhqpK4s47wDYtNiPtlp4= +github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -171,6 +175,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/prometheus v1.7.2-0.20170814170113-3101606756c5 h1:K2PKeDFZidfjUWpXk05Gbxhwm8Rnz1l4O+u/bbbcCvc= github.com/prometheus/prometheus v1.7.2-0.20170814170113-3101606756c5/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s= +github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7 h1:cZC+usqsYgHtlBaGulVnZ1hfKAi8iWtujBnRLQE698c= +github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7/go.mod h1:IToEjHuttnUzwZI5KBSM/LOOW3qLbbrHOEfp3SbECGY= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8= @@ -249,6 +255,7 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From 441f8ed70c54d10767741af999c2df116c490d66 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 19 Dec 2024 11:01:21 +0800 Subject: [PATCH 442/479] common: add variable MaxHash (#28306) --- common/types.go | 3 +++ core/state_processor_test.go | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/common/types.go b/common/types.go index 5f9f58b18e94..60977e848670 100644 --- a/common/types.go +++ b/common/types.go @@ -54,6 +54,9 @@ const ( ) var ( + // MaxHash represents the maximum possible hash value. + MaxHash = HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + BlockSignersBinary = Address{19: 0x89} // xdc0000000000000000000000000000000000000089 MasternodeVotingSMCBinary = Address{19: 0x88} // xdc0000000000000000000000000000000000000088 RandomizeSMCBinary = Address{19: 0x90} // xdc0000000000000000000000000000000000000090 diff --git a/core/state_processor_test.go b/core/state_processor_test.go index cce40167528d..6de370ec980f 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -90,7 +90,7 @@ func TestStateProcessorErrors(t *testing.T) { blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}) ) defer blockchain.Stop() - bigNumber := new(big.Int).SetBytes(common.FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) + bigNumber := new(big.Int).SetBytes(common.MaxHash.Bytes()) tooBigNumber := new(big.Int).Set(bigNumber) tooBigNumber.Add(tooBigNumber, common.Big1) for i, tt := range []struct { From 4cc2b2ea5fee86c483bf3265b4d6404739df14d5 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 19 Dec 2024 11:57:47 +0800 Subject: [PATCH 443/479] tests/fuzzers: move fuzzers into native packages (#28467) --- common/bitutil/compress_test.go | 61 ++++++-- core/types/rlp_fuzzer_test.go | 147 ++++++++++++++++++ .../vm/contracts_fuzz_test.go | 29 +++- .../vm/runtime/runtime_fuzz_test.go | 6 +- tests/fuzzers/bitutil/compress_test.go | 68 -------- tests/fuzzers/rlp/rlp_fuzzer.go | 143 ----------------- tests/fuzzers/secp256k1/secp_test.go | 3 +- trie/trie_test.go | 65 ++++++-- 8 files changed, 275 insertions(+), 247 deletions(-) create mode 100644 core/types/rlp_fuzzer_test.go rename tests/fuzzers/rlp/rlp_test.go => core/vm/contracts_fuzz_test.go (57%) rename tests/fuzzers/runtime/runtime_test.go => core/vm/runtime/runtime_fuzz_test.go (87%) delete mode 100644 tests/fuzzers/bitutil/compress_test.go delete mode 100644 tests/fuzzers/rlp/rlp_fuzzer.go diff --git a/common/bitutil/compress_test.go b/common/bitutil/compress_test.go index 19a3324e8ba9..84f624f46c64 100644 --- a/common/bitutil/compress_test.go +++ b/common/bitutil/compress_test.go @@ -18,6 +18,7 @@ package bitutil import ( "bytes" + "fmt" "math/rand" "testing" @@ -48,19 +49,23 @@ func TestEncodingCycle(t *testing.T) { "0xdf7070533534333636313639343638373532313536346c1bc333393438373130707063363430353639343638373532313536346c1bc333393438336336346c65fe", } for i, tt := range tests { - data := hexutil.MustDecode(tt) - - proc, err := bitsetDecodeBytes(bitsetEncodeBytes(data), len(data)) - if err != nil { - t.Errorf("test %d: failed to decompress compressed data: %v", i, err) - continue - } - if !bytes.Equal(data, proc) { - t.Errorf("test %d: compress/decompress mismatch: have %x, want %x", i, proc, data) + if err := testEncodingCycle(hexutil.MustDecode(tt)); err != nil { + t.Errorf("test %d: %v", i, err) } } } +func testEncodingCycle(data []byte) error { + proc, err := bitsetDecodeBytes(bitsetEncodeBytes(data), len(data)) + if err != nil { + return fmt.Errorf("failed to decompress compressed data: %v", err) + } + if !bytes.Equal(data, proc) { + return fmt.Errorf("compress/decompress mismatch: have %x, want %x", proc, data) + } + return nil +} + // Tests that data bitset decoding and rencoding works and is bijective. func TestDecodingCycle(t *testing.T) { tests := []struct { @@ -179,3 +184,41 @@ func benchmarkEncoding(b *testing.B, bytes int, fill float64) { bitsetDecodeBytes(bitsetEncodeBytes(data), len(data)) } } + +func FuzzEncoder(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + if err := testEncodingCycle(data); err != nil { + t.Fatal(err) + } + }) +} + +func FuzzDecoder(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + fuzzDecode(data) + }) +} + +// fuzzDecode implements a go-fuzz fuzzer method to test the bit decoding and +// reencoding algorithm. +func fuzzDecode(data []byte) { + blob, err := DecompressBytes(data, 1024) + if err != nil { + return + } + // re-compress it (it's OK if the re-compressed differs from the + // original - the first input may not have been compressed at all) + comp := CompressBytes(blob) + if len(comp) > len(blob) { + // After compression, it must be smaller or equal + panic("bad compression") + } + // But decompressing it once again should work + decomp, err := DecompressBytes(data, 1024) + if err != nil { + panic(err) + } + if !bytes.Equal(decomp, blob) { + panic("content mismatch") + } +} diff --git a/core/types/rlp_fuzzer_test.go b/core/types/rlp_fuzzer_test.go new file mode 100644 index 000000000000..64fc43965620 --- /dev/null +++ b/core/types/rlp_fuzzer_test.go @@ -0,0 +1,147 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "bytes" + "fmt" + "math/big" + "testing" + + "github.com/XinFinOrg/XDPoSChain/rlp" + "github.com/holiman/uint256" +) + +func decodeEncode(input []byte, val interface{}) error { + if err := rlp.DecodeBytes(input, val); err != nil { + // not valid rlp, nothing to do + return nil + } + // If it _were_ valid rlp, we can encode it again + output, err := rlp.EncodeToBytes(val) + if err != nil { + return err + } + if !bytes.Equal(input, output) { + return fmt.Errorf("encode-decode is not equal, \ninput : %x\noutput: %x", input, output) + } + return nil +} + +func FuzzRLP(f *testing.F) { + f.Fuzz(fuzzRlp) +} + +func fuzzRlp(t *testing.T, input []byte) { + if len(input) == 0 || len(input) > 500*1024 { + return + } + rlp.Split(input) + if elems, _, err := rlp.SplitList(input); err == nil { + rlp.CountValues(elems) + } + rlp.NewStream(bytes.NewReader(input), 0).Decode(new(interface{})) + if err := decodeEncode(input, new(interface{})); err != nil { + t.Fatal(err) + } + { + var v struct { + Int uint + String string + Bytes []byte + } + if err := decodeEncode(input, &v); err != nil { + t.Fatal(err) + } + } + { + type Types struct { + Bool bool + Raw rlp.RawValue + Slice []*Types + Iface []interface{} + } + var v Types + if err := decodeEncode(input, &v); err != nil { + t.Fatal(err) + } + } + { + type AllTypes struct { + Int uint + String string + Bytes []byte + Bool bool + Raw rlp.RawValue + Slice []*AllTypes + Array [3]*AllTypes + Iface []interface{} + } + var v AllTypes + if err := decodeEncode(input, &v); err != nil { + t.Fatal(err) + } + } + { + if err := decodeEncode(input, [10]byte{}); err != nil { + t.Fatal(err) + } + } + { + var v struct { + Byte [10]byte + Rool [10]bool + } + if err := decodeEncode(input, &v); err != nil { + t.Fatal(err) + } + } + { + var h Header + if err := decodeEncode(input, &h); err != nil { + t.Fatal(err) + } + var b Block + if err := decodeEncode(input, &b); err != nil { + t.Fatal(err) + } + var tx Transaction + if err := decodeEncode(input, &tx); err != nil { + t.Fatal(err) + } + var txs Transactions + if err := decodeEncode(input, &txs); err != nil { + t.Fatal(err) + } + var rs Receipts + if err := decodeEncode(input, &rs); err != nil { + t.Fatal(err) + } + } + { + var v struct { + AnIntPtr *big.Int + AnInt big.Int + AnU256Ptr *uint256.Int + AnU256 uint256.Int + NotAnU256 [4]uint64 + } + if err := decodeEncode(input, &v); err != nil { + t.Fatal(err) + } + } +} diff --git a/tests/fuzzers/rlp/rlp_test.go b/core/vm/contracts_fuzz_test.go similarity index 57% rename from tests/fuzzers/rlp/rlp_test.go rename to core/vm/contracts_fuzz_test.go index 377b3961bf14..adf77bfe9ca4 100644 --- a/tests/fuzzers/rlp/rlp_test.go +++ b/core/vm/contracts_fuzz_test.go @@ -14,12 +14,31 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package rlp +package vm -import "testing" +import ( + "testing" -func Fuzz(f *testing.F) { - f.Fuzz(func(t *testing.T, data []byte) { - fuzz(data) + "github.com/XinFinOrg/XDPoSChain/common" +) + +func FuzzPrecompiledContracts(f *testing.F) { + // Create list of addresses + var addrs []common.Address + for k := range allPrecompiles { + addrs = append(addrs, k) + } + f.Fuzz(func(t *testing.T, addr uint8, input []byte) { + a := addrs[int(addr)%len(addrs)] + p := allPrecompiles[a] + gas := p.RequiredGas(input) + if gas > 10_000_000 { + return + } + inWant := string(input) + RunPrecompiledContract(nil, p, input, gas) + if inHave := string(input); inWant != inHave { + t.Errorf("Precompiled %v modified input data", a) + } }) } diff --git a/tests/fuzzers/runtime/runtime_test.go b/core/vm/runtime/runtime_fuzz_test.go similarity index 87% rename from tests/fuzzers/runtime/runtime_test.go rename to core/vm/runtime/runtime_fuzz_test.go index 97d7cdc71fb9..8a4d31d81983 100644 --- a/tests/fuzzers/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_fuzz_test.go @@ -18,13 +18,11 @@ package runtime import ( "testing" - - "github.com/XinFinOrg/XDPoSChain/core/vm/runtime" ) -func Fuzz(f *testing.F) { +func FuzzVmRuntime(f *testing.F) { f.Fuzz(func(t *testing.T, code, input []byte) { - runtime.Execute(code, input, &runtime.Config{ + Execute(code, input, &Config{ GasLimit: 12000000, }) }) diff --git a/tests/fuzzers/bitutil/compress_test.go b/tests/fuzzers/bitutil/compress_test.go deleted file mode 100644 index 0c6ce21c9d35..000000000000 --- a/tests/fuzzers/bitutil/compress_test.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2023 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package bitutil - -import ( - "bytes" - "testing" - - "github.com/XinFinOrg/XDPoSChain/common/bitutil" -) - -func FuzzEncoder(f *testing.F) { - f.Fuzz(func(t *testing.T, data []byte) { - fuzzEncode(data) - }) -} -func FuzzDecoder(f *testing.F) { - f.Fuzz(func(t *testing.T, data []byte) { - fuzzDecode(data) - }) -} - -// fuzzEncode implements a go-fuzz fuzzer method to test the bitset encoding and -// decoding algorithm. -func fuzzEncode(data []byte) { - proc, _ := bitutil.DecompressBytes(bitutil.CompressBytes(data), len(data)) - if !bytes.Equal(data, proc) { - panic("content mismatch") - } -} - -// fuzzDecode implements a go-fuzz fuzzer method to test the bit decoding and -// reencoding algorithm. -func fuzzDecode(data []byte) { - blob, err := bitutil.DecompressBytes(data, 1024) - if err != nil { - return - } - // re-compress it (it's OK if the re-compressed differs from the - // original - the first input may not have been compressed at all) - comp := bitutil.CompressBytes(blob) - if len(comp) > len(blob) { - // After compression, it must be smaller or equal - panic("bad compression") - } - // But decompressing it once again should work - decomp, err := bitutil.DecompressBytes(data, 1024) - if err != nil { - panic(err) - } - if !bytes.Equal(decomp, blob) { - panic("content mismatch") - } -} diff --git a/tests/fuzzers/rlp/rlp_fuzzer.go b/tests/fuzzers/rlp/rlp_fuzzer.go deleted file mode 100644 index 1ffd845ba055..000000000000 --- a/tests/fuzzers/rlp/rlp_fuzzer.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2019 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rlp - -import ( - "bytes" - "fmt" - "math/big" - - "github.com/XinFinOrg/XDPoSChain/core/types" - "github.com/XinFinOrg/XDPoSChain/rlp" - "github.com/holiman/uint256" -) - -func decodeEncode(input []byte, val interface{}, i int) { - if err := rlp.DecodeBytes(input, val); err == nil { - output, err := rlp.EncodeToBytes(val) - if err != nil { - panic(err) - } - if !bytes.Equal(input, output) { - panic(fmt.Sprintf("case %d: encode-decode is not equal, \ninput : %x\noutput: %x", i, input, output)) - } - } -} - -func fuzz(input []byte) int { - if len(input) == 0 { - return 0 - } - if len(input) > 500*1024 { - return 0 - } - - var i int - { - rlp.Split(input) - } - { - if elems, _, err := rlp.SplitList(input); err == nil { - rlp.CountValues(elems) - } - } - - { - rlp.NewStream(bytes.NewReader(input), 0).Decode(new(interface{})) - } - - { - decodeEncode(input, new(interface{}), i) - i++ - } - { - var v struct { - Int uint - String string - Bytes []byte - } - decodeEncode(input, &v, i) - i++ - } - - { - type Types struct { - Bool bool - Raw rlp.RawValue - Slice []*Types - Iface []interface{} - } - var v Types - decodeEncode(input, &v, i) - i++ - } - { - type AllTypes struct { - Int uint - String string - Bytes []byte - Bool bool - Raw rlp.RawValue - Slice []*AllTypes - Array [3]*AllTypes - Iface []interface{} - } - var v AllTypes - decodeEncode(input, &v, i) - i++ - } - { - decodeEncode(input, [10]byte{}, i) - i++ - } - { - var v struct { - Byte [10]byte - Rool [10]bool - } - decodeEncode(input, &v, i) - i++ - } - { - var h types.Header - decodeEncode(input, &h, i) - i++ - var b types.Block - decodeEncode(input, &b, i) - i++ - var t types.Transaction - decodeEncode(input, &t, i) - i++ - var txs types.Transactions - decodeEncode(input, &txs, i) - i++ - var rs types.Receipts - decodeEncode(input, &rs, i) - } - { - i++ - var v struct { - AnIntPtr *big.Int - AnInt big.Int - AnU256Ptr *uint256.Int - AnU256 uint256.Int - NotAnU256 [4]uint64 - } - decodeEncode(input, &v, i) - } - return 1 -} diff --git a/tests/fuzzers/secp256k1/secp_test.go b/tests/fuzzers/secp256k1/secp_test.go index 59bfe07cc20a..e8b655669c53 100644 --- a/tests/fuzzers/secp256k1/secp_test.go +++ b/tests/fuzzers/secp256k1/secp_test.go @@ -35,7 +35,7 @@ func Fuzz(f *testing.F) { }) } -func fuzz(dataP1, dataP2 []byte) int { +func fuzz(dataP1, dataP2 []byte) { var ( curveA = secp256k1.S256() curveB = btcec.S256() @@ -50,5 +50,4 @@ func fuzz(dataP1, dataP2 []byte) int { fmt.Printf("%s %s %s %s\n", x1, y1, x2, y2) panic(fmt.Sprintf("Addition failed: geth: %s %s btcd: %s %s", resAX, resAY, resBX, resBY)) } - return 0 } diff --git a/trie/trie_test.go b/trie/trie_test.go index 9311cbae6400..b8ed49364535 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -21,6 +21,7 @@ import ( "encoding/binary" "errors" "fmt" + "io" "math/big" "math/rand" "os" @@ -346,8 +347,9 @@ func TestRandomCases(t *testing.T) { {op: 1, key: common.Hex2Bytes("980c393656413a15c8da01978ed9f89feb80b502f58f2d640e3a2f5f7a99a7018f1b573befd92053ac6f78fca4a87268"), value: common.Hex2Bytes("")}, // step 24 {op: 1, key: common.Hex2Bytes("fd"), value: common.Hex2Bytes("")}, // step 25 } - runRandTest(rt) - + if err := runRandTest(rt); err != nil { + t.Fatal(err) + } } // randTest performs random trie operations. @@ -373,36 +375,53 @@ const ( ) func (randTest) Generate(r *rand.Rand, size int) reflect.Value { + var finishedFn = func() bool { + size-- + return size > 0 + } + return reflect.ValueOf(generateSteps(finishedFn, r)) +} + +func generateSteps(finished func() bool, r io.Reader) randTest { var allKeys [][]byte + var one = []byte{0} genKey := func() []byte { - if len(allKeys) < 2 || r.Intn(100) < 10 { + r.Read(one) + if len(allKeys) < 2 || one[0]%100 > 90 { // new key - key := make([]byte, r.Intn(50)) + size := one[0] % 50 + key := make([]byte, size) r.Read(key) allKeys = append(allKeys, key) return key } // use existing key - return allKeys[r.Intn(len(allKeys))] + idx := int(one[0]) % len(allKeys) + return allKeys[idx] } - var steps randTest - for i := 0; i < size; i++ { - step := randTestStep{op: r.Intn(opMax)} + for !finished() { + r.Read(one) + step := randTestStep{op: int(one[0]) % opMax} switch step.op { case opUpdate: step.key = genKey() step.value = make([]byte, 8) - binary.BigEndian.PutUint64(step.value, uint64(i)) + binary.BigEndian.PutUint64(step.value, uint64(len(steps))) case opGet, opDelete: step.key = genKey() } steps = append(steps, step) } - return reflect.ValueOf(steps) + return steps } -func runRandTest(rt randTest) bool { +// runRandTestBool coerces error to boolean, for use in quick.Check +func runRandTestBool(rt randTest) bool { + return runRandTest(rt) == nil +} + +func runRandTest(rt randTest) error { triedb := NewDatabase(memorydb.New()) tr, _ := New(common.Hash{}, triedb) @@ -432,12 +451,12 @@ func runRandTest(rt randTest) bool { hash, err := tr.Commit(nil) if err != nil { rt[i].err = err - return false + return err } newtr, err := New(hash, triedb) if err != nil { rt[i].err = err - return false + return err } tr = newtr case opItercheckhash: @@ -452,14 +471,14 @@ func runRandTest(rt randTest) bool { } // Abort the test on error. if rt[i].err != nil { - return false + return rt[i].err } } - return true + return nil } func TestRandom(t *testing.T) { - if err := quick.Check(runRandTest, nil); err != nil { + if err := quick.Check(runRandTestBool, nil); err != nil { if cerr, ok := err.(*quick.CheckError); ok { t.Fatalf("random test iteration %d failed: %s", cerr.Count, spew.Sdump(cerr.In)) } @@ -854,3 +873,17 @@ func TestDecodeNode(t *testing.T) { decodeNode(hash, elems) } } + +func FuzzTrie(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + var steps = 500 + var input = bytes.NewReader(data) + var finishedFn = func() bool { + steps-- + return steps < 0 || input.Len() == 0 + } + if err := runRandTest(generateSteps(finishedFn, input)); err != nil { + t.Fatal(err) + } + }) +} From ff13d16fe53ebbe8bcaeaf6e4905bdcd7cfe3656 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 19 Dec 2024 12:43:49 +0800 Subject: [PATCH 444/479] core/vm: make structlog/json-log stack hex again (#28628) --- common/hexutil/json.go | 45 ++++++++++++++++++++++++++++ common/hexutil/json_test.go | 60 +++++++++++++++++++++++++++++++++++++ core/vm/gen_structlog.go | 16 +++++++--- core/vm/logger.go | 1 + 4 files changed, 118 insertions(+), 4 deletions(-) diff --git a/common/hexutil/json.go b/common/hexutil/json.go index 2a1b06146ad7..8e04dcf327ce 100644 --- a/common/hexutil/json.go +++ b/common/hexutil/json.go @@ -23,6 +23,8 @@ import ( "math/big" "reflect" "strconv" + + "github.com/holiman/uint256" ) var ( @@ -30,6 +32,7 @@ var ( bigT = reflect.TypeOf((*Big)(nil)) uintT = reflect.TypeOf(Uint(0)) uint64T = reflect.TypeOf(Uint64(0)) + u256T = reflect.TypeOf((*uint256.Int)(nil)) ) // Bytes marshals/unmarshals as a JSON string with 0x prefix. @@ -195,6 +198,48 @@ func (b *Big) String() string { return EncodeBig(b.ToInt()) } +// U256 marshals/unmarshals as a JSON string with 0x prefix. +// The zero value marshals as "0x0". +type U256 uint256.Int + +// MarshalText implements encoding.TextMarshaler +func (b U256) MarshalText() ([]byte, error) { + u256 := (*uint256.Int)(&b) + return []byte(u256.Hex()), nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (b *U256) UnmarshalJSON(input []byte) error { + // The uint256.Int.UnmarshalJSON method accepts "dec", "0xhex"; we must be + // more strict, hence we check string and invoke SetFromHex directly. + if !isString(input) { + return errNonString(u256T) + } + // The hex decoder needs to accept empty string ("") as '0', which uint256.Int + // would reject. + if len(input) == 2 { + (*uint256.Int)(b).Clear() + return nil + } + err := (*uint256.Int)(b).SetFromHex(string(input[1 : len(input)-1])) + if err != nil { + return &json.UnmarshalTypeError{Value: err.Error(), Type: u256T} + } + return nil +} + +// UnmarshalText implements encoding.TextUnmarshaler +func (b *U256) UnmarshalText(input []byte) error { + // The uint256.Int.UnmarshalText method accepts "dec", "0xhex"; we must be + // more strict, hence we check string and invoke SetFromHex directly. + return (*uint256.Int)(b).SetFromHex(string(input)) +} + +// String returns the hex encoding of b. +func (b *U256) String() string { + return (*uint256.Int)(b).Hex() +} + // Uint64 marshals/unmarshals as a JSON string with 0x prefix. // The zero value marshals as "0x0". type Uint64 uint64 diff --git a/common/hexutil/json_test.go b/common/hexutil/json_test.go index ed7d6fad1a8e..7cca300951cd 100644 --- a/common/hexutil/json_test.go +++ b/common/hexutil/json_test.go @@ -23,6 +23,8 @@ import ( "errors" "math/big" "testing" + + "github.com/holiman/uint256" ) func checkError(t *testing.T, input string, got, want error) bool { @@ -176,6 +178,64 @@ func TestUnmarshalBig(t *testing.T) { } } +var unmarshalU256Tests = []unmarshalTest{ + // invalid encoding + {input: "", wantErr: errJSONEOF}, + {input: "null", wantErr: errNonString(u256T)}, + {input: "10", wantErr: errNonString(u256T)}, + {input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, u256T)}, + {input: `"0x"`, wantErr: wrapTypeError(ErrEmptyNumber, u256T)}, + {input: `"0x01"`, wantErr: wrapTypeError(ErrLeadingZero, u256T)}, + {input: `"0xx"`, wantErr: wrapTypeError(ErrSyntax, u256T)}, + {input: `"0x1zz01"`, wantErr: wrapTypeError(ErrSyntax, u256T)}, + { + input: `"0x10000000000000000000000000000000000000000000000000000000000000000"`, + wantErr: wrapTypeError(ErrBig256Range, u256T), + }, + + // valid encoding + {input: `""`, want: big.NewInt(0)}, + {input: `"0x0"`, want: big.NewInt(0)}, + {input: `"0x2"`, want: big.NewInt(0x2)}, + {input: `"0x2F2"`, want: big.NewInt(0x2f2)}, + {input: `"0X2F2"`, want: big.NewInt(0x2f2)}, + {input: `"0x1122aaff"`, want: big.NewInt(0x1122aaff)}, + {input: `"0xbBb"`, want: big.NewInt(0xbbb)}, + {input: `"0xfffffffff"`, want: big.NewInt(0xfffffffff)}, + { + input: `"0x112233445566778899aabbccddeeff"`, + want: referenceBig("112233445566778899aabbccddeeff"), + }, + { + input: `"0xffffffffffffffffffffffffffffffffffff"`, + want: referenceBig("ffffffffffffffffffffffffffffffffffff"), + }, + { + input: `"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"`, + want: referenceBig("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + }, +} + +func TestUnmarshalU256(t *testing.T) { + for _, test := range unmarshalU256Tests { + var v U256 + err := json.Unmarshal([]byte(test.input), &v) + if !checkError(t, test.input, err, test.wantErr) { + continue + } + if test.want == nil { + continue + } + want := new(uint256.Int) + want.SetFromBig(test.want.(*big.Int)) + have := (*uint256.Int)(&v) + if want.Cmp(have) != 0 { + t.Errorf("input %s: value mismatch: have %x, want %x", test.input, have, want) + continue + } + } +} + func BenchmarkUnmarshalBig(b *testing.B) { input := []byte(`"0x123456789abcdef123456789abcdef"`) for i := 0; i < b.N; i++ { diff --git a/core/vm/gen_structlog.go b/core/vm/gen_structlog.go index 1230944d57f3..a14635876ef5 100644 --- a/core/vm/gen_structlog.go +++ b/core/vm/gen_structlog.go @@ -22,7 +22,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) { GasCost math.HexOrDecimal64 `json:"gasCost"` Memory hexutil.Bytes `json:"memory"` MemorySize int `json:"memSize"` - Stack []uint256.Int `json:"stack"` + Stack []hexutil.U256 `json:"stack"` ReturnData hexutil.Bytes `json:"returnData"` Storage map[common.Hash]common.Hash `json:"-"` Depth int `json:"depth"` @@ -38,7 +38,12 @@ func (s StructLog) MarshalJSON() ([]byte, error) { enc.GasCost = math.HexOrDecimal64(s.GasCost) enc.Memory = s.Memory enc.MemorySize = s.MemorySize - enc.Stack = s.Stack + if s.Stack != nil { + enc.Stack = make([]hexutil.U256, len(s.Stack)) + for k, v := range s.Stack { + enc.Stack[k] = hexutil.U256(v) + } + } enc.ReturnData = s.ReturnData enc.Storage = s.Storage enc.Depth = s.Depth @@ -58,7 +63,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { GasCost *math.HexOrDecimal64 `json:"gasCost"` Memory *hexutil.Bytes `json:"memory"` MemorySize *int `json:"memSize"` - Stack []uint256.Int `json:"stack"` + Stack []hexutil.U256 `json:"stack"` ReturnData *hexutil.Bytes `json:"returnData"` Storage map[common.Hash]common.Hash `json:"-"` Depth *int `json:"depth"` @@ -88,7 +93,10 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { s.MemorySize = *dec.MemorySize } if dec.Stack != nil { - s.Stack = dec.Stack + s.Stack = make([]uint256.Int, len(dec.Stack)) + for k, v := range dec.Stack { + s.Stack[k] = uint256.Int(v) + } } if dec.ReturnData != nil { s.ReturnData = *dec.ReturnData diff --git a/core/vm/logger.go b/core/vm/logger.go index ce014b17fc1e..24178f54ff22 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -81,6 +81,7 @@ type structLogMarshaling struct { GasCost math.HexOrDecimal64 Memory hexutil.Bytes ReturnData hexutil.Bytes + Stack []hexutil.U256 OpName string `json:"opName"` // adds call to OpName() in MarshalJSON ErrorString string `json:"error"` // adds call to ErrorString() in MarshalJSON } From f2cd7c082bfc9a219a9be0cc9c72f71ab4259c1b Mon Sep 17 00:00:00 2001 From: Aaron Chen Date: Tue, 12 Mar 2024 20:05:31 +0800 Subject: [PATCH 445/479] common/math: copy result in Exp (#29233) common/math: does not change base parameter --- common/math/big.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/common/math/big.go b/common/math/big.go index d6b2c5097b8f..41bfa29aa0bc 100644 --- a/common/math/big.go +++ b/common/math/big.go @@ -189,7 +189,7 @@ func ReadBits(bigint *big.Int, buf []byte) { } } -// U256 encodes as a 256 bit two's complement number. This operation is destructive. +// U256 encodes x as a 256 bit two's complement number. This operation is destructive. func U256(x *big.Int) *big.Int { return x.And(x, tt256m1) } @@ -220,14 +220,15 @@ func S256(x *big.Int) *big.Int { // // Courtesy @karalabe and @chfast func Exp(base, exponent *big.Int) *big.Int { + copyBase := new(big.Int).Set(base) result := big.NewInt(1) for _, word := range exponent.Bits() { for i := 0; i < wordBits; i++ { if word&1 == 1 { - U256(result.Mul(result, base)) + U256(result.Mul(result, copyBase)) } - U256(base.Mul(base, base)) + U256(copyBase.Mul(copyBase, copyBase)) word >>= 1 } } From 009542a92e069d76fb97dd9a7faab16cc41a8c2a Mon Sep 17 00:00:00 2001 From: Justin Dhillon Date: Tue, 12 Mar 2024 23:54:40 -0700 Subject: [PATCH 446/479] accounts/usbwallet, common/bitutil: fix broken links in docs (#29078) fixes some links in documentation --- accounts/usbwallet/ledger.go | 2 +- common/bitutil/bitutil.go | 2 +- common/bitutil/bitutil_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/accounts/usbwallet/ledger.go b/accounts/usbwallet/ledger.go index f9d06aa032dc..005a02278358 100644 --- a/accounts/usbwallet/ledger.go +++ b/accounts/usbwallet/ledger.go @@ -16,7 +16,7 @@ // This file contains the implementation for interacting with the Ledger hardware // wallets. The wire protocol spec can be found in the Ledger Blue GitHub repo: -// https://raw.githubusercontent.com/LedgerHQ/blue-app-eth/master/doc/ethapp.asc +// https://github.com/LedgerHQ/app-ethereum/blob/develop/doc/ethapp.adoc package usbwallet diff --git a/common/bitutil/bitutil.go b/common/bitutil/bitutil.go index cd3e72169fc5..a18a6d18eed8 100644 --- a/common/bitutil/bitutil.go +++ b/common/bitutil/bitutil.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Adapted from: https://golang.org/src/crypto/cipher/xor.go +// Adapted from: https://go.dev/src/crypto/subtle/xor_generic.go // Package bitutil implements fast bitwise operations. package bitutil diff --git a/common/bitutil/bitutil_test.go b/common/bitutil/bitutil_test.go index 307bf731f765..12f3fe24a6c9 100644 --- a/common/bitutil/bitutil_test.go +++ b/common/bitutil/bitutil_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Adapted from: https://golang.org/src/crypto/cipher/xor_test.go +// Adapted from: https://go.dev/src/crypto/subtle/xor_test.go package bitutil From 57eb92da1c154cdacf2710c5f1ae868249bcee09 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 19 Dec 2024 13:23:40 +0800 Subject: [PATCH 447/479] common/prque: remove dependency on golang.org/exp (#29314) --- common/prque/lazyqueue.go | 10 +++++----- common/prque/prque.go | 7 +++---- common/prque/sstack.go | 8 ++++---- go.mod | 5 ++--- go.sum | 2 -- 5 files changed, 14 insertions(+), 18 deletions(-) diff --git a/common/prque/lazyqueue.go b/common/prque/lazyqueue.go index ae3cd798da00..02619543c2aa 100644 --- a/common/prque/lazyqueue.go +++ b/common/prque/lazyqueue.go @@ -17,11 +17,11 @@ package prque import ( + "cmp" "container/heap" "time" "github.com/XinFinOrg/XDPoSChain/common/mclock" - "golang.org/x/exp/constraints" ) // LazyQueue is a priority queue data structure where priorities can change over @@ -33,7 +33,7 @@ import ( // // If the upper estimate is exceeded then Update should be called for that item. // A global Refresh function should also be called periodically. -type LazyQueue[P constraints.Ordered, V any] struct { +type LazyQueue[P cmp.Ordered, V any] struct { clock mclock.Clock // Items are stored in one of two internal queues ordered by estimated max // priority until the next and the next-after-next refresh. Update and Refresh @@ -50,12 +50,12 @@ type LazyQueue[P constraints.Ordered, V any] struct { } type ( - PriorityCallback[P constraints.Ordered, V any] func(data V) P // actual priority callback - MaxPriorityCallback[P constraints.Ordered, V any] func(data V, until mclock.AbsTime) P // estimated maximum priority callback + PriorityCallback[P cmp.Ordered, V any] func(data V) P // actual priority callback + MaxPriorityCallback[P cmp.Ordered, V any] func(data V, until mclock.AbsTime) P // estimated maximum priority callback ) // NewLazyQueue creates a new lazy queue -func NewLazyQueue[P constraints.Ordered, V any](setIndex SetIndexCallback[V], priority PriorityCallback[P, V], maxPriority MaxPriorityCallback[P, V], clock mclock.Clock, refreshPeriod time.Duration) *LazyQueue[P, V] { +func NewLazyQueue[P cmp.Ordered, V any](setIndex SetIndexCallback[V], priority PriorityCallback[P, V], maxPriority MaxPriorityCallback[P, V], clock mclock.Clock, refreshPeriod time.Duration) *LazyQueue[P, V] { q := &LazyQueue[P, V]{ popQueue: newSstack[P, V](nil), setIndex: setIndex, diff --git a/common/prque/prque.go b/common/prque/prque.go index 0e8c9f897fad..ec8351020a10 100755 --- a/common/prque/prque.go +++ b/common/prque/prque.go @@ -18,18 +18,17 @@ package prque import ( + "cmp" "container/heap" - - "golang.org/x/exp/constraints" ) // Priority queue data structure. -type Prque[P constraints.Ordered, V any] struct { +type Prque[P cmp.Ordered, V any] struct { cont *sstack[P, V] } // New creates a new priority queue. -func New[P constraints.Ordered, V any](setIndex SetIndexCallback[V]) *Prque[P, V] { +func New[P cmp.Ordered, V any](setIndex SetIndexCallback[V]) *Prque[P, V] { return &Prque[P, V]{newSstack[P, V](setIndex)} } diff --git a/common/prque/sstack.go b/common/prque/sstack.go index 5dcd1d9dd0c4..ee6d7c0c3ac5 100755 --- a/common/prque/sstack.go +++ b/common/prque/sstack.go @@ -10,13 +10,13 @@ package prque -import "golang.org/x/exp/constraints" +import "cmp" // The size of a block of data const blockSize = 4096 // A prioritized item in the sorted stack. -type item[P constraints.Ordered, V any] struct { +type item[P cmp.Ordered, V any] struct { value V priority P } @@ -29,7 +29,7 @@ type SetIndexCallback[V any] func(data V, index int) // Internal sortable stack data structure. Implements the Push and Pop ops for // the stack (heap) functionality and the Len, Less and Swap methods for the // sortability requirements of the heaps. -type sstack[P constraints.Ordered, V any] struct { +type sstack[P cmp.Ordered, V any] struct { setIndex SetIndexCallback[V] size int capacity int @@ -40,7 +40,7 @@ type sstack[P constraints.Ordered, V any] struct { } // Creates a new, empty stack. -func newSstack[P constraints.Ordered, V any](setIndex SetIndexCallback[V]) *sstack[P, V] { +func newSstack[P cmp.Ordered, V any](setIndex SetIndexCallback[V]) *sstack[P, V] { result := new(sstack[P, V]) result.setIndex = setIndex result.active = make([]*item[P, V], blockSize) diff --git a/go.mod b/go.mod index fcbe37d9a954..57fa810ed42d 100644 --- a/go.mod +++ b/go.mod @@ -47,13 +47,14 @@ require ( github.com/deckarep/golang-set v1.8.0 github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 github.com/ethereum/c-kzg-4844 v0.4.0 + github.com/go-yaml/yaml v2.1.0+incompatible github.com/influxdata/influxdb-client-go/v2 v2.4.0 github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c github.com/kylelemons/godebug v1.1.0 github.com/mattn/go-isatty v0.0.17 + github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible github.com/urfave/cli/v2 v2.27.5 - golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9 gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) @@ -68,7 +69,6 @@ require ( github.com/dlclark/regexp2 v1.10.0 // indirect github.com/go-ole/go-ole v1.2.5 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect - github.com/go-yaml/yaml v2.1.0+incompatible // indirect github.com/google/uuid v1.3.0 // indirect github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect github.com/kilic/bls12-381 v0.1.0 // indirect @@ -78,7 +78,6 @@ require ( github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/naoina/go-stringutil v0.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect diff --git a/go.sum b/go.sum index 73c7510dc470..eefee5f64b33 100644 --- a/go.sum +++ b/go.sum @@ -220,8 +220,6 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= -golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9 h1:yZNXmy+j/JpX19vZkVktWqAo7Gny4PBWYYK3zskGpx4= -golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= From 62afcf6b85499ff96762f5eacae70676cd466f06 Mon Sep 17 00:00:00 2001 From: georgehao Date: Sat, 6 Apr 2024 17:09:30 +0800 Subject: [PATCH 448/479] common/prque: fix godoc comments (#29460) Co-authored-by: Felix Lange --- common/prque/prque.go | 14 +++++++------- common/prque/sstack.go | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/common/prque/prque.go b/common/prque/prque.go index ec8351020a10..cb0d9f35801a 100755 --- a/common/prque/prque.go +++ b/common/prque/prque.go @@ -22,7 +22,7 @@ import ( "container/heap" ) -// Priority queue data structure. +// Prque is a priority queue data structure. type Prque[P cmp.Ordered, V any] struct { cont *sstack[P, V] } @@ -32,7 +32,7 @@ func New[P cmp.Ordered, V any](setIndex SetIndexCallback[V]) *Prque[P, V] { return &Prque[P, V]{newSstack[P, V](setIndex)} } -// Pushes a value with a given priority into the queue, expanding if necessary. +// Push a value with a given priority into the queue, expanding if necessary. func (p *Prque[P, V]) Push(data V, priority P) { heap.Push(p.cont, &item[P, V]{data, priority}) } @@ -43,14 +43,14 @@ func (p *Prque[P, V]) Peek() (V, P) { return item.value, item.priority } -// Pops the value with the greatest priority off the stack and returns it. +// Pop the value with the greatest priority off the stack and returns it. // Currently no shrinking is done. func (p *Prque[P, V]) Pop() (V, P) { item := heap.Pop(p.cont).(*item[P, V]) return item.value, item.priority } -// Pops only the item from the queue, dropping the associated priority value. +// PopItem pops only the item from the queue, dropping the associated priority value. func (p *Prque[P, V]) PopItem() V { return heap.Pop(p.cont).(*item[P, V]).value } @@ -60,17 +60,17 @@ func (p *Prque[P, V]) Remove(i int) V { return heap.Remove(p.cont, i).(*item[P, V]).value } -// Checks whether the priority queue is empty. +// Empty checks whether the priority queue is empty. func (p *Prque[P, V]) Empty() bool { return p.cont.Len() == 0 } -// Returns the number of element in the priority queue. +// Size returns the number of element in the priority queue. func (p *Prque[P, V]) Size() int { return p.cont.Len() } -// Clears the contents of the priority queue. +// Reset clears the contents of the priority queue. func (p *Prque[P, V]) Reset() { *p = *New[P, V](p.cont.setIndex) } diff --git a/common/prque/sstack.go b/common/prque/sstack.go index ee6d7c0c3ac5..6865a51e2366 100755 --- a/common/prque/sstack.go +++ b/common/prque/sstack.go @@ -49,7 +49,7 @@ func newSstack[P cmp.Ordered, V any](setIndex SetIndexCallback[V]) *sstack[P, V] return result } -// Pushes a value onto the stack, expanding it if necessary. Required by +// Push a value onto the stack, expanding it if necessary. Required by // heap.Interface. func (s *sstack[P, V]) Push(data any) { if s.size == s.capacity { @@ -69,7 +69,7 @@ func (s *sstack[P, V]) Push(data any) { s.size++ } -// Pops a value off the stack and returns it. Currently no shrinking is done. +// Pop a value off the stack and returns it. Currently no shrinking is done. // Required by heap.Interface. func (s *sstack[P, V]) Pop() (res any) { s.size-- @@ -85,18 +85,18 @@ func (s *sstack[P, V]) Pop() (res any) { return } -// Returns the length of the stack. Required by sort.Interface. +// Len returns the length of the stack. Required by sort.Interface. func (s *sstack[P, V]) Len() int { return s.size } -// Compares the priority of two elements of the stack (higher is first). +// Less compares the priority of two elements of the stack (higher is first). // Required by sort.Interface. func (s *sstack[P, V]) Less(i, j int) bool { return s.blocks[i/blockSize][i%blockSize].priority > s.blocks[j/blockSize][j%blockSize].priority } -// Swaps two elements in the stack. Required by sort.Interface. +// Swap two elements in the stack. Required by sort.Interface. func (s *sstack[P, V]) Swap(i, j int) { ib, io, jb, jo := i/blockSize, i%blockSize, j/blockSize, j%blockSize a, b := s.blocks[jb][jo], s.blocks[ib][io] @@ -107,7 +107,7 @@ func (s *sstack[P, V]) Swap(i, j int) { s.blocks[ib][io], s.blocks[jb][jo] = a, b } -// Resets the stack, effectively clearing its contents. +// Reset the stack, effectively clearing its contents. func (s *sstack[P, V]) Reset() { *s = *newSstack[P, V](s.setIndex) } From b274a273b899528ac5cef9f7e8057e899bca379d Mon Sep 17 00:00:00 2001 From: tianyeyouyou <150894831+tianyeyouyou@users.noreply.github.com> Date: Fri, 31 May 2024 15:25:49 +0800 Subject: [PATCH 449/479] common/math: rename variable name `int` to `n` (#29890) * chore: rename variable name `int` to `in` * chore: rename variable name `int` to `n` --- common/math/big_test.go | 4 ++-- common/math/integer.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/math/big_test.go b/common/math/big_test.go index 8ae7578319a7..db69d6014c0a 100644 --- a/common/math/big_test.go +++ b/common/math/big_test.go @@ -180,9 +180,9 @@ func BenchmarkByteAtOld(b *testing.B) { func TestReadBits(t *testing.T) { check := func(input string) { want, _ := hex.DecodeString(input) - int, _ := new(big.Int).SetString(input, 16) + n, _ := new(big.Int).SetString(input, 16) buf := make([]byte, len(want)) - ReadBits(int, buf) + ReadBits(n, buf) if !bytes.Equal(buf, want) { t.Errorf("have: %x\nwant: %x", buf, want) } diff --git a/common/math/integer.go b/common/math/integer.go index e78949299185..7f8ded447c8d 100644 --- a/common/math/integer.go +++ b/common/math/integer.go @@ -54,11 +54,11 @@ func (i *HexOrDecimal64) UnmarshalJSON(input []byte) error { // UnmarshalText implements encoding.TextUnmarshaler. func (i *HexOrDecimal64) UnmarshalText(input []byte) error { - int, ok := ParseUint64(string(input)) + n, ok := ParseUint64(string(input)) if !ok { return fmt.Errorf("invalid hex or decimal integer %q", input) } - *i = HexOrDecimal64(int) + *i = HexOrDecimal64(n) return nil } From 6fda80ade8c9d282d4cf7cd198287943089ab348 Mon Sep 17 00:00:00 2001 From: Dean Eigenmann <7621705+decanus@users.noreply.github.com> Date: Mon, 17 Jun 2024 21:53:00 +0200 Subject: [PATCH 450/479] common/math: fix out of bounds access in json unmarshalling (#30014) Co-authored-by: Martin Holst Swende --- common/math/big.go | 2 +- common/math/integer.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/math/big.go b/common/math/big.go index 41bfa29aa0bc..6fd2b4b6ae6e 100644 --- a/common/math/big.go +++ b/common/math/big.go @@ -53,7 +53,7 @@ func NewHexOrDecimal256(x int64) *HexOrDecimal256 { // It is similar to UnmarshalText, but allows parsing real decimals too, not just // quoted decimal strings. func (i *HexOrDecimal256) UnmarshalJSON(input []byte) error { - if len(input) > 0 && input[0] == '"' { + if len(input) > 1 && input[0] == '"' { input = input[1 : len(input)-1] } return i.UnmarshalText(input) diff --git a/common/math/integer.go b/common/math/integer.go index 7f8ded447c8d..b0a67c31859c 100644 --- a/common/math/integer.go +++ b/common/math/integer.go @@ -46,7 +46,7 @@ type HexOrDecimal64 uint64 // It is similar to UnmarshalText, but allows parsing real decimals too, not just // quoted decimal strings. func (i *HexOrDecimal64) UnmarshalJSON(input []byte) error { - if len(input) > 0 && input[0] == '"' { + if len(input) > 1 && input[0] == '"' { input = input[1 : len(input)-1] } return i.UnmarshalText(input) From dff8e500855e5ad1e1f9b3ceee92145a2dee8cb1 Mon Sep 17 00:00:00 2001 From: Halimao <1065621723@qq.com> Date: Wed, 19 Jun 2024 17:06:52 +0800 Subject: [PATCH 451/479] common: using `ParseUint` instead of `ParseInt` (#30020) Since Decimal is defined as unsiged `uint64`, we should use `strconv.ParseUint` instead of `strconv.ParseInt` during unmarshalling. --------- Co-authored-by: Martin Holst Swende --- common/types.go | 2 +- common/types_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/common/types.go b/common/types.go index 60977e848670..c33539135577 100644 --- a/common/types.go +++ b/common/types.go @@ -435,7 +435,7 @@ func (d *Decimal) UnmarshalJSON(input []byte) error { if !isString(input) { return &json.UnmarshalTypeError{Value: "non-string", Type: reflect.TypeOf(uint64(0))} } - if i, err := strconv.ParseInt(string(input[1:len(input)-1]), 10, 64); err == nil { + if i, err := strconv.ParseUint(string(input[1:len(input)-1]), 10, 64); err == nil { *d = Decimal(i) return nil } else { diff --git a/common/types_test.go b/common/types_test.go index 3262f549310b..1faf10197705 100644 --- a/common/types_test.go +++ b/common/types_test.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/json" "fmt" + "math" "math/big" "strings" "testing" @@ -405,3 +406,29 @@ func BenchmarkPrettyDuration(b *testing.B) { } b.Logf("Post %s", a) } + +func TestDecimalUnmarshalJSON(t *testing.T) { + // These should error + for _, tc := range []string{``, `"`, `""`, `"-1"`} { + if err := new(Decimal).UnmarshalJSON([]byte(tc)); err == nil { + t.Errorf("input %s should cause error", tc) + } + } + // These should succeed + for _, tc := range []struct { + input string + want uint64 + }{ + {`"0"`, 0}, + {`"9223372036854775807"`, math.MaxInt64}, + {`"18446744073709551615"`, math.MaxUint64}, + } { + have := new(Decimal) + if err := have.UnmarshalJSON([]byte(tc.input)); err != nil { + t.Errorf("input %q triggered error: %v", tc.input, err) + } + if uint64(*have) != tc.want { + t.Errorf("input %q, have %d want %d", tc.input, *have, tc.want) + } + } +} From 6ec35b9644c4aec4344522954bdd368b7224eb85 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 19 Dec 2024 14:18:38 +0800 Subject: [PATCH 452/479] all: get rid of custom MaxUint64 and MaxUint64 (#30636) --- XDCx/tradingstate/statedb_test.go | 7 ++-- accounts/abi/bind/backends/simulated.go | 3 +- common/math/big.go | 35 ------------------- common/math/big_test.go | 45 ------------------------- common/math/integer.go | 16 --------- common/math/integer_test.go | 9 ++--- core/vm/common.go | 3 +- core/vm/contracts.go | 5 +-- internal/ethapi/api.go | 5 +-- internal/ethapi/transaction_args.go | 5 +-- les/odr_test.go | 5 +-- light/odr_test.go | 10 +++--- rlp/decode_test.go | 3 +- rpc/types_test.go | 2 +- 14 files changed, 33 insertions(+), 120 deletions(-) diff --git a/XDCx/tradingstate/statedb_test.go b/XDCx/tradingstate/statedb_test.go index 2979ca64188c..188dae651c9b 100644 --- a/XDCx/tradingstate/statedb_test.go +++ b/XDCx/tradingstate/statedb_test.go @@ -18,11 +18,12 @@ package tradingstate import ( "fmt" - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/common/math" - "github.com/XinFinOrg/XDPoSChain/core/rawdb" + "math" "math/big" "testing" + + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" ) func TestEchangeStates(t *testing.T) { diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index d0c7562e155c..caa243258437 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -20,6 +20,7 @@ import ( "context" "errors" "fmt" + gomath "math" "math/big" "os" "sync" @@ -476,7 +477,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.Cal // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. vmenv := vm.NewEVM(evmContext, txContext, statedb, nil, b.config, vm.Config{NoBaseFee: true}) - gaspool := new(core.GasPool).AddGas(math.MaxUint64) + gaspool := new(core.GasPool).AddGas(gomath.MaxUint64) owner := common.Address{} return core.NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner) } diff --git a/common/math/big.go b/common/math/big.go index 6fd2b4b6ae6e..5f401182a711 100644 --- a/common/math/big.go +++ b/common/math/big.go @@ -199,38 +199,3 @@ func U256(x *big.Int) *big.Int { func U256Bytes(n *big.Int) []byte { return PaddedBigBytes(U256(n), 32) } - -// S256 interprets x as a two's complement number. -// x must not exceed 256 bits (the result is undefined if it does) and is not modified. -// -// S256(0) = 0 -// S256(1) = 1 -// S256(2**255) = -2**255 -// S256(2**256-1) = -1 -func S256(x *big.Int) *big.Int { - if x.Cmp(tt255) < 0 { - return x - } - return new(big.Int).Sub(x, tt256) -} - -// Exp implements exponentiation by squaring. -// Exp returns a newly-allocated big integer and does not change -// base or exponent. The result is truncated to 256 bits. -// -// Courtesy @karalabe and @chfast -func Exp(base, exponent *big.Int) *big.Int { - copyBase := new(big.Int).Set(base) - result := big.NewInt(1) - - for _, word := range exponent.Bits() { - for i := 0; i < wordBits; i++ { - if word&1 == 1 { - U256(result.Mul(result, copyBase)) - } - U256(copyBase.Mul(copyBase, copyBase)) - word >>= 1 - } - } - return result -} diff --git a/common/math/big_test.go b/common/math/big_test.go index db69d6014c0a..102f18f0c659 100644 --- a/common/math/big_test.go +++ b/common/math/big_test.go @@ -278,48 +278,3 @@ func TestLittleEndianByteAt(t *testing.T) { } } } - -func TestS256(t *testing.T) { - tests := []struct{ x, y *big.Int }{ - {x: big.NewInt(0), y: big.NewInt(0)}, - {x: big.NewInt(1), y: big.NewInt(1)}, - {x: big.NewInt(2), y: big.NewInt(2)}, - { - x: new(big.Int).Sub(BigPow(2, 255), big.NewInt(1)), - y: new(big.Int).Sub(BigPow(2, 255), big.NewInt(1)), - }, - { - x: BigPow(2, 255), - y: new(big.Int).Neg(BigPow(2, 255)), - }, - { - x: new(big.Int).Sub(BigPow(2, 256), big.NewInt(1)), - y: big.NewInt(-1), - }, - { - x: new(big.Int).Sub(BigPow(2, 256), big.NewInt(2)), - y: big.NewInt(-2), - }, - } - for _, test := range tests { - if y := S256(test.x); y.Cmp(test.y) != 0 { - t.Errorf("S256(%x) = %x, want %x", test.x, y, test.y) - } - } -} - -func TestExp(t *testing.T) { - tests := []struct{ base, exponent, result *big.Int }{ - {base: big.NewInt(0), exponent: big.NewInt(0), result: big.NewInt(1)}, - {base: big.NewInt(1), exponent: big.NewInt(0), result: big.NewInt(1)}, - {base: big.NewInt(1), exponent: big.NewInt(1), result: big.NewInt(1)}, - {base: big.NewInt(1), exponent: big.NewInt(2), result: big.NewInt(1)}, - {base: big.NewInt(3), exponent: big.NewInt(144), result: MustParseBig256("507528786056415600719754159741696356908742250191663887263627442114881")}, - {base: big.NewInt(2), exponent: big.NewInt(255), result: MustParseBig256("57896044618658097711785492504343953926634992332820282019728792003956564819968")}, - } - for _, test := range tests { - if result := Exp(test.base, test.exponent); result.Cmp(test.result) != 0 { - t.Errorf("Exp(%d, %d) = %d, want %d", test.base, test.exponent, result, test.result) - } - } -} diff --git a/common/math/integer.go b/common/math/integer.go index b0a67c31859c..25ced8705300 100644 --- a/common/math/integer.go +++ b/common/math/integer.go @@ -22,22 +22,6 @@ import ( "strconv" ) -const ( - // Integer limit values. - MaxInt8 = 1<<7 - 1 - MinInt8 = -1 << 7 - MaxInt16 = 1<<15 - 1 - MinInt16 = -1 << 15 - MaxInt32 = 1<<31 - 1 - MinInt32 = -1 << 31 - MaxInt64 = 1<<63 - 1 - MinInt64 = -1 << 63 - MaxUint8 = 1<<8 - 1 - MaxUint16 = 1<<16 - 1 - MaxUint32 = 1<<32 - 1 - MaxUint64 = 1<<64 - 1 -) - // HexOrDecimal64 marshals uint64 as hex or decimal. type HexOrDecimal64 uint64 diff --git a/common/math/integer_test.go b/common/math/integer_test.go index b31c7c26c262..4643a43f20f3 100644 --- a/common/math/integer_test.go +++ b/common/math/integer_test.go @@ -17,6 +17,7 @@ package math import ( + "math" "testing" ) @@ -36,8 +37,8 @@ func TestOverflow(t *testing.T) { op operation }{ // add operations - {MaxUint64, 1, true, add}, - {MaxUint64 - 1, 1, false, add}, + {math.MaxUint64, 1, true, add}, + {math.MaxUint64 - 1, 1, false, add}, // sub operations {0, 1, true, sub}, @@ -46,8 +47,8 @@ func TestOverflow(t *testing.T) { // mul operations {0, 0, false, mul}, {10, 10, false, mul}, - {MaxUint64, 2, true, mul}, - {MaxUint64, 1, false, mul}, + {math.MaxUint64, 2, true, mul}, + {math.MaxUint64, 1, false, mul}, } { var overflows bool switch test.op { diff --git a/core/vm/common.go b/core/vm/common.go index bb11f4a393e5..d3cbbe97cb57 100644 --- a/core/vm/common.go +++ b/core/vm/common.go @@ -17,8 +17,9 @@ package vm import ( + "math" + "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/holiman/uint256" ) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 7da6949ded53..9f17ba33109d 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -20,6 +20,7 @@ import ( "crypto/sha256" "encoding/binary" "errors" + gomath "math" "math/big" "github.com/XinFinOrg/XDPoSChain/common" @@ -364,7 +365,7 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { // 2. Different divisor (`GQUADDIVISOR`) (3) gas.Div(gas, big3) if gas.BitLen() > 64 { - return math.MaxUint64 + return gomath.MaxUint64 } // 3. Minimum price of 200 gas if gas.Uint64() < 200 { @@ -377,7 +378,7 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { gas.Div(gas, big20) if gas.BitLen() > 64 { - return math.MaxUint64 + return gomath.MaxUint64 } return gas.Uint64() } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index c371f1442594..eb8b709aa0ef 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -24,6 +24,7 @@ import ( "math/big" "strings" "time" + gomath "math" "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate" "github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate" @@ -415,7 +416,7 @@ func (s *PrivateAccountAPI) ImportRawKey(privkey string, password string) (commo // the given password for duration seconds. If duration is nil it will use a // default of 300 seconds. It returns an indication if the account was unlocked. func (s *PrivateAccountAPI) UnlockAccount(addr common.Address, password string, duration *uint64) (bool, error) { - const max = uint64(time.Duration(math.MaxInt64) / time.Second) + const max = uint64(time.Duration(gomath.MaxInt64) / time.Second) var d time.Duration if duration == nil { d = 300 * time.Second @@ -1389,7 +1390,7 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash }() // Execute the message. - gp := new(core.GasPool).AddGas(math.MaxUint64) + gp := new(core.GasPool).AddGas(gomath.MaxUint64) owner := common.Address{} result, err := core.ApplyMessage(evm, msg, gp, owner) if err := vmError(); err != nil { diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 2202fe580e78..0bdf9903e572 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -21,6 +21,7 @@ import ( "context" "errors" "fmt" + gomath "math" "math/big" "github.com/XinFinOrg/XDPoSChain/common" @@ -99,7 +100,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGas if skipGasEstimation { // Skip gas usage estimation if a precise gas limit is not critical, e.g., in non-transaction calls. gas := hexutil.Uint64(b.RPCGasCap()) if gas == 0 { - gas = hexutil.Uint64(math.MaxUint64 / 2) + gas = hexutil.Uint64(gomath.MaxUint64 / 2) } args.Gas = &gas } else { // Estimate the gas usage otherwise. @@ -245,7 +246,7 @@ func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap gas = uint64(*args.Gas) } if gas == 0 { - gas = math.MaxUint64 / 2 + gas = gomath.MaxUint64 / 2 } if globalGasCap != 0 && globalGasCap < gas { log.Warn("Caller gas above allowance, capping", "requested", gas, "cap", globalGasCap) diff --git a/les/odr_test.go b/les/odr_test.go index 706853d504b2..ccff6c8ab840 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -19,6 +19,7 @@ package les import ( "bytes" "context" + gomath "math" "math/big" "testing" "time" @@ -140,7 +141,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai vmenv := vm.NewEVM(context, txContext, statedb, nil, config, vm.Config{NoBaseFee: true}) //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{}) - gp := new(core.GasPool).AddGas(math.MaxUint64) + gp := new(core.GasPool).AddGas(gomath.MaxUint64) owner := common.Address{} result, _ := core.ApplyMessage(vmenv, msg, gp, owner) res = append(res, result.Return()...) @@ -158,7 +159,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai context := core.NewEVMBlockContext(header, lc, nil) txContext := core.NewEVMTxContext(msg) vmenv := vm.NewEVM(context, txContext, statedb, nil, config, vm.Config{NoBaseFee: true}) - gp := new(core.GasPool).AddGas(math.MaxUint64) + gp := new(core.GasPool).AddGas(gomath.MaxUint64) owner := common.Address{} result, _ := core.ApplyMessage(vmenv, msg, gp, owner) if statedb.Error() == nil { diff --git a/light/odr_test.go b/light/odr_test.go index 79d3241c2048..80e42ac2eeb4 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -20,17 +20,17 @@ import ( "bytes" "context" "errors" + gomath "math" "math/big" "testing" "time" - "github.com/XinFinOrg/XDPoSChain/consensus" - "github.com/XinFinOrg/XDPoSChain/core/rawdb" - "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/math" + "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/ethash" "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" @@ -44,7 +44,7 @@ import ( var ( testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) - testBankFunds = big.NewInt(math.MaxInt64) + testBankFunds = big.NewInt(gomath.MaxInt64) acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") @@ -189,7 +189,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(header, chain, nil) vmenv := vm.NewEVM(context, txContext, st, nil, config, vm.Config{NoBaseFee: true}) - gp := new(core.GasPool).AddGas(math.MaxUint64) + gp := new(core.GasPool).AddGas(gomath.MaxUint64) owner := common.Address{} result, _ := core.ApplyMessage(vmenv, msg, gp, owner) res = append(res, result.Return()...) diff --git a/rlp/decode_test.go b/rlp/decode_test.go index 38cca38aa548..8cd40c44cc89 100644 --- a/rlp/decode_test.go +++ b/rlp/decode_test.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "io" + gomath "math" "math/big" "reflect" "strings" @@ -556,7 +557,7 @@ var decodeTests = []decodeTest{ // uint256 {input: "80", ptr: new(*uint256.Int), value: uint256.NewInt(0)}, {input: "01", ptr: new(*uint256.Int), value: uint256.NewInt(1)}, - {input: "88FFFFFFFFFFFFFFFF", ptr: new(*uint256.Int), value: uint256.NewInt(math.MaxUint64)}, + {input: "88FFFFFFFFFFFFFFFF", ptr: new(*uint256.Int), value: uint256.NewInt(gomath.MaxUint64)}, {input: "89FFFFFFFFFFFFFFFFFF", ptr: new(*uint256.Int), value: veryBigInt256}, {input: "10", ptr: new(uint256.Int), value: *uint256.NewInt(16)}, // non-pointer also works diff --git a/rpc/types_test.go b/rpc/types_test.go index eec0819d048c..47ab5f8aff77 100644 --- a/rpc/types_test.go +++ b/rpc/types_test.go @@ -18,10 +18,10 @@ package rpc import ( "encoding/json" + "math" "testing" "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/common/math" ) func TestBlockNumberJSONUnmarshal(t *testing.T) { From 156de2bf90523a45bd9a8ec7f5aa60ccf254ff19 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 19 Dec 2024 14:32:38 +0800 Subject: [PATCH 453/479] common/math: delete some further dead code (#30639) --- common/math/big.go | 41 ------------------- common/math/big_test.go | 91 ----------------------------------------- 2 files changed, 132 deletions(-) diff --git a/common/math/big.go b/common/math/big.go index 5f401182a711..5f59195e3d7e 100644 --- a/common/math/big.go +++ b/common/math/big.go @@ -23,12 +23,9 @@ import ( ) var ( - tt255 = BigPow(2, 255) tt256 = BigPow(2, 256) tt256m1 = new(big.Int).Sub(tt256, big.NewInt(1)) MaxBig256 = new(big.Int).Set(tt256m1) - tt63 = BigPow(2, 63) - MaxBig63 = new(big.Int).Sub(tt63, big.NewInt(1)) ) const ( @@ -127,16 +124,6 @@ func BigMin(x, y *big.Int) *big.Int { return x } -// FirstBitSet returns the index of the first 1 bit in v, counting from LSB. -func FirstBitSet(v *big.Int) int { - for i := 0; i < v.BitLen(); i++ { - if v.Bit(i) > 0 { - return i - } - } - return v.BitLen() -} - // PaddedBigBytes encodes a big integer as a big-endian byte slice. The length // of the slice is at least n bytes. func PaddedBigBytes(bigint *big.Int, n int) []byte { @@ -148,34 +135,6 @@ func PaddedBigBytes(bigint *big.Int, n int) []byte { return ret } -// bigEndianByteAt returns the byte at position n, -// in Big-Endian encoding -// So n==0 returns the least significant byte -func bigEndianByteAt(bigint *big.Int, n int) byte { - words := bigint.Bits() - // Check word-bucket the byte will reside in - i := n / wordBytes - if i >= len(words) { - return byte(0) - } - word := words[i] - // Offset of the byte - shift := 8 * uint(n%wordBytes) - - return byte(word >> shift) -} - -// Byte returns the byte at position n, -// with the supplied padlength in Little-Endian encoding. -// n==0 returns the MSB -// Example: bigint '5', padlength 32, n=31 => 5 -func Byte(bigint *big.Int, padlength, n int) byte { - if n >= padlength { - return byte(0) - } - return bigEndianByteAt(bigint, padlength-1-n) -} - // ReadBits encodes the absolute value of bigint as big-endian bytes. Callers must ensure // that buf has enough space. If buf is too short the result will be incomplete. func ReadBits(bigint *big.Int, buf []byte) { diff --git a/common/math/big_test.go b/common/math/big_test.go index 102f18f0c659..92a322e29d66 100644 --- a/common/math/big_test.go +++ b/common/math/big_test.go @@ -21,8 +21,6 @@ import ( "encoding/hex" "math/big" "testing" - - "github.com/XinFinOrg/XDPoSChain/common" ) func TestHexOrDecimal256(t *testing.T) { @@ -100,23 +98,6 @@ func TestBigMin(t *testing.T) { } } -func TestFirstBigSet(t *testing.T) { - tests := []struct { - num *big.Int - ix int - }{ - {big.NewInt(0), 0}, - {big.NewInt(1), 0}, - {big.NewInt(2), 1}, - {big.NewInt(0x100), 8}, - } - for _, test := range tests { - if ix := FirstBitSet(test.num); ix != test.ix { - t.Errorf("FirstBitSet(b%b) = %d, want %d", test.num, ix, test.ix) - } - } -} - func TestPaddedBigBytes(t *testing.T) { tests := []struct { num *big.Int @@ -156,20 +137,6 @@ func BenchmarkPaddedBigBytesSmallOnePadding(b *testing.B) { } } -func BenchmarkByteAtBrandNew(b *testing.B) { - bigint := MustParseBig256("0x18F8F8F1000111000110011100222004330052300000000000000000FEFCF3CC") - for i := 0; i < b.N; i++ { - bigEndianByteAt(bigint, 15) - } -} - -func BenchmarkByteAt(b *testing.B) { - bigint := MustParseBig256("0x18F8F8F1000111000110011100222004330052300000000000000000FEFCF3CC") - for i := 0; i < b.N; i++ { - bigEndianByteAt(bigint, 15) - } -} - func BenchmarkByteAtOld(b *testing.B) { bigint := MustParseBig256("0x18F8F8F1000111000110011100222004330052300000000000000000FEFCF3CC") for i := 0; i < b.N; i++ { @@ -220,61 +187,3 @@ func TestU256Bytes(t *testing.T) { t.Errorf("expected %x got %x", ubytes, unsigned) } } - -func TestBigEndianByteAt(t *testing.T) { - tests := []struct { - x string - y int - exp byte - }{ - {"00", 0, 0x00}, - {"01", 1, 0x00}, - {"00", 1, 0x00}, - {"01", 0, 0x01}, - {"0000000000000000000000000000000000000000000000000000000000102030", 0, 0x30}, - {"0000000000000000000000000000000000000000000000000000000000102030", 1, 0x20}, - {"ABCDEF0908070605040302010000000000000000000000000000000000000000", 31, 0xAB}, - {"ABCDEF0908070605040302010000000000000000000000000000000000000000", 32, 0x00}, - {"ABCDEF0908070605040302010000000000000000000000000000000000000000", 500, 0x00}, - } - for _, test := range tests { - v := new(big.Int).SetBytes(common.Hex2Bytes(test.x)) - actual := bigEndianByteAt(v, test.y) - if actual != test.exp { - t.Fatalf("Expected [%v] %v:th byte to be %v, was %v.", test.x, test.y, test.exp, actual) - } - } -} - -func TestLittleEndianByteAt(t *testing.T) { - tests := []struct { - x string - y int - exp byte - }{ - {"00", 0, 0x00}, - {"01", 1, 0x00}, - {"00", 1, 0x00}, - {"01", 0, 0x00}, - {"0000000000000000000000000000000000000000000000000000000000102030", 0, 0x00}, - {"0000000000000000000000000000000000000000000000000000000000102030", 1, 0x00}, - {"ABCDEF0908070605040302010000000000000000000000000000000000000000", 31, 0x00}, - {"ABCDEF0908070605040302010000000000000000000000000000000000000000", 32, 0x00}, - {"ABCDEF0908070605040302010000000000000000000000000000000000000000", 0, 0xAB}, - {"ABCDEF0908070605040302010000000000000000000000000000000000000000", 1, 0xCD}, - {"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", 0, 0x00}, - {"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", 1, 0xCD}, - {"0000000000000000000000000000000000000000000000000000000000102030", 31, 0x30}, - {"0000000000000000000000000000000000000000000000000000000000102030", 30, 0x20}, - {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 32, 0x0}, - {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 31, 0xFF}, - {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 0xFFFF, 0x0}, - } - for _, test := range tests { - v := new(big.Int).SetBytes(common.Hex2Bytes(test.x)) - actual := Byte(v, 32, test.y) - if actual != test.exp { - t.Fatalf("Expected [%v] %v:th byte to be %v, was %v.", test.x, test.y, test.exp, actual) - } - } -} From 6e33633d287dd750dab66b64d083644bcaa27922 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 19 Dec 2024 15:38:22 +0800 Subject: [PATCH 454/479] common: drop BigMin and BigMax, they pollute our dep graph (#30645) --- accounts/abi/bind/backends/simulated.go | 5 ++++- common/math/big.go | 16 ------------- common/math/big_test.go | 30 ------------------------- consensus/ethash/consensus.go | 4 +++- core/state_transition.go | 6 +++-- core/types/transaction.go | 16 +++++++++---- core/vm/contracts.go | 22 ++++++++++++------ internal/ethapi/api.go | 13 ++++++----- internal/ethapi/transaction_args.go | 12 +++++----- tests/state_test_util.go | 6 +++-- 10 files changed, 57 insertions(+), 73 deletions(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index caa243258437..694a3e51832b 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -449,7 +449,10 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.Cal // Backfill the legacy gasPrice for EVM execution, unless we're all zeroes call.GasPrice = new(big.Int) if call.GasFeeCap.BitLen() > 0 || call.GasTipCap.BitLen() > 0 { - call.GasPrice = math.BigMin(new(big.Int).Add(call.GasTipCap, head.BaseFee), call.GasFeeCap) + call.GasPrice = new(big.Int).Add(call.GasTipCap, head.BaseFee) + if call.GasPrice.Cmp(call.GasFeeCap) > 0 { + call.GasPrice.Set(call.GasFeeCap) + } } } } diff --git a/common/math/big.go b/common/math/big.go index 5f59195e3d7e..78e3c8255d97 100644 --- a/common/math/big.go +++ b/common/math/big.go @@ -108,22 +108,6 @@ func BigPow(a, b int64) *big.Int { return r.Exp(r, big.NewInt(b), nil) } -// BigMax returns the larger of x or y. -func BigMax(x, y *big.Int) *big.Int { - if x.Cmp(y) < 0 { - return y - } - return x -} - -// BigMin returns the smaller of x or y. -func BigMin(x, y *big.Int) *big.Int { - if x.Cmp(y) > 0 { - return y - } - return x -} - // PaddedBigBytes encodes a big integer as a big-endian byte slice. The length // of the slice is at least n bytes. func PaddedBigBytes(bigint *big.Int, n int) []byte { diff --git a/common/math/big_test.go b/common/math/big_test.go index 92a322e29d66..6f701b621a7a 100644 --- a/common/math/big_test.go +++ b/common/math/big_test.go @@ -68,36 +68,6 @@ func TestMustParseBig256(t *testing.T) { MustParseBig256("ggg") } -func TestBigMax(t *testing.T) { - a := big.NewInt(10) - b := big.NewInt(5) - - max1 := BigMax(a, b) - if max1 != a { - t.Errorf("Expected %d got %d", a, max1) - } - - max2 := BigMax(b, a) - if max2 != a { - t.Errorf("Expected %d got %d", a, max2) - } -} - -func TestBigMin(t *testing.T) { - a := big.NewInt(10) - b := big.NewInt(5) - - min1 := BigMin(a, b) - if min1 != b { - t.Errorf("Expected %d got %d", b, min1) - } - - min2 := BigMin(b, a) - if min2 != b { - t.Errorf("Expected %d got %d", b, min2) - } -} - func TestPaddedBigBytes(t *testing.T) { tests := []struct { num *big.Int diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 92806693aeb4..e6ecc9f7ab64 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -458,7 +458,9 @@ func calcDifficultyFrontier(time uint64, parent *types.Header) *big.Int { expDiff := periodCount.Sub(periodCount, big2) expDiff.Exp(big2, expDiff, nil) diff.Add(diff, expDiff) - diff = math.BigMax(diff, params.MinimumDifficulty) + if diff.Cmp(params.MinimumDifficulty) < 0 { + diff = params.MinimumDifficulty + } } return diff } diff --git a/core/state_transition.go b/core/state_transition.go index e18064501041..24375cf8ff96 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -23,7 +23,6 @@ import ( "math/big" "github.com/XinFinOrg/XDPoSChain/common" - cmath "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/crypto" @@ -397,7 +396,10 @@ func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult, } else { effectiveTip := st.gasPrice if st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber) { - effectiveTip = cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee)) + effectiveTip = new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee) + if effectiveTip.Cmp(st.gasTipCap) > 0 { + effectiveTip = st.gasTipCap + } } st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip)) } diff --git a/core/types/transaction.go b/core/types/transaction.go index dca1ef50a5bf..3054534e558d 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -28,7 +28,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/hexutil" - "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/rlp" ) @@ -364,10 +363,16 @@ func (tx *Transaction) EffectiveGasTip(baseFee *big.Int) (*big.Int, error) { } var err error gasFeeCap := tx.GasFeeCap() - if gasFeeCap.Cmp(baseFee) == -1 { + if gasFeeCap.Cmp(baseFee) < 0 { err = ErrGasFeeCapTooLow } - return math.BigMin(tx.GasTipCap(), gasFeeCap.Sub(gasFeeCap, baseFee)), err + gasFeeCap = gasFeeCap.Sub(gasFeeCap, baseFee) + + gasTipCap := tx.GasTipCap() + if gasTipCap.Cmp(gasFeeCap) < 0 { + return gasTipCap, err + } + return gasFeeCap, err } // EffectiveGasTipValue is identical to EffectiveGasTip, but does not return an @@ -453,7 +458,10 @@ func (tx *Transaction) AsMessage(s Signer, balanceFee, blockNumber, baseFee *big } } else if baseFee != nil { // If baseFee provided, set gasPrice to effectiveGasPrice. - msg.gasPrice = math.BigMin(msg.gasPrice.Add(msg.gasTipCap, baseFee), msg.gasFeeCap) + msg.gasPrice = msg.gasPrice.Add(msg.gasTipCap, baseFee) + if msg.gasPrice.Cmp(msg.gasFeeCap) > 0 { + msg.gasPrice.Set(msg.gasFeeCap) + } } var err error diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 9f17ba33109d..98e1fdc43666 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -20,11 +20,10 @@ import ( "crypto/sha256" "encoding/binary" "errors" - gomath "math" + "math" "math/big" "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/core/vm/privacy" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/crypto/blake2b" @@ -347,7 +346,12 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { } adjExpLen.Add(adjExpLen, big.NewInt(int64(msb))) // Calculate the gas cost of the operation - gas := new(big.Int).Set(math.BigMax(modLen, baseLen)) + gas := new(big.Int) + if modLen.Cmp(baseLen) < 0 { + gas.Set(baseLen) + } else { + gas.Set(modLen) + } if c.eip2565 { // EIP-2565 has three changes // 1. Different multComplexity (inlined here) @@ -361,11 +365,13 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { gas = gas.Div(gas, big8) gas.Mul(gas, gas) - gas.Mul(gas, math.BigMax(adjExpLen, big1)) + if adjExpLen.Cmp(big1) > 0 { + gas.Mul(gas, adjExpLen) + } // 2. Different divisor (`GQUADDIVISOR`) (3) gas.Div(gas, big3) if gas.BitLen() > 64 { - return gomath.MaxUint64 + return math.MaxUint64 } // 3. Minimum price of 200 gas if gas.Uint64() < 200 { @@ -374,11 +380,13 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { return gas.Uint64() } gas = modexpMultComplexity(gas) - gas.Mul(gas, math.BigMax(adjExpLen, big1)) + if adjExpLen.Cmp(big1) > 0 { + gas.Mul(gas, adjExpLen) + } gas.Div(gas, big20) if gas.BitLen() > 64 { - return gomath.MaxUint64 + return math.MaxUint64 } return gas.Uint64() } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index eb8b709aa0ef..5c9c55562b01 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -21,10 +21,10 @@ import ( "context" "errors" "fmt" + "math" "math/big" "strings" "time" - gomath "math" "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate" "github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate" @@ -34,7 +34,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/accounts/keystore" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/hexutil" - "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/common/sort" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" @@ -416,7 +415,7 @@ func (s *PrivateAccountAPI) ImportRawKey(privkey string, password string) (commo // the given password for duration seconds. If duration is nil it will use a // default of 300 seconds. It returns an indication if the account was unlocked. func (s *PrivateAccountAPI) UnlockAccount(addr common.Address, password string, duration *uint64) (bool, error) { - const max = uint64(time.Duration(gomath.MaxInt64) / time.Second) + const max = uint64(time.Duration(math.MaxInt64) / time.Second) var d time.Duration if duration == nil { d = 300 * time.Second @@ -1390,7 +1389,7 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash }() // Execute the message. - gp := new(core.GasPool).AddGas(gomath.MaxUint64) + gp := new(core.GasPool).AddGas(math.MaxUint64) owner := common.Address{} result, err := core.ApplyMessage(evm, msg, gp, owner) if err := vmError(); err != nil { @@ -1954,7 +1953,11 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber // if the transaction has been mined, compute the effective gas price if baseFee != nil && blockHash != (common.Hash{}) { // price = min(tip, gasFeeCap - baseFee) + baseFee - price := math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap()) + price := new(big.Int).Add(tx.GasTipCap(), baseFee) + txGasFeeCap := tx.GasFeeCap() + if price.Cmp(txGasFeeCap) > 0 { + price = txGasFeeCap + } result.GasPrice = (*hexutil.Big)(price) } else { result.GasPrice = (*hexutil.Big)(tx.GasFeeCap()) diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 0bdf9903e572..68807629a20d 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -21,12 +21,11 @@ import ( "context" "errors" "fmt" - gomath "math" + "math" "math/big" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/hexutil" - "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/rpc" @@ -100,7 +99,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGas if skipGasEstimation { // Skip gas usage estimation if a precise gas limit is not critical, e.g., in non-transaction calls. gas := hexutil.Uint64(b.RPCGasCap()) if gas == 0 { - gas = hexutil.Uint64(gomath.MaxUint64 / 2) + gas = hexutil.Uint64(math.MaxUint64 / 2) } args.Gas = &gas } else { // Estimate the gas usage otherwise. @@ -246,7 +245,7 @@ func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap gas = uint64(*args.Gas) } if gas == 0 { - gas = gomath.MaxUint64 / 2 + gas = math.MaxUint64 / 2 } if globalGasCap != 0 && globalGasCap < gas { log.Warn("Caller gas above allowance, capping", "requested", gas, "cap", globalGasCap) @@ -287,7 +286,10 @@ func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap // Backfill the legacy gasPrice for EVM execution, unless we're all zeroes gasPrice = new(big.Int) if gasFeeCap.BitLen() > 0 || gasTipCap.BitLen() > 0 { - gasPrice = math.BigMin(new(big.Int).Add(gasTipCap, baseFee), gasFeeCap) + gasPrice = gasPrice.Add(gasTipCap, baseFee) + if gasPrice.Cmp(gasFeeCap) > 0 { + gasPrice = gasFeeCap + } } } } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 6f9ace8ec3ef..74f5fc33c47a 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -275,8 +275,10 @@ func (tx *stTransaction) toMessage(ps stPostState, number *big.Int, baseFee *big if tx.MaxPriorityFeePerGas == nil { tx.MaxPriorityFeePerGas = tx.MaxFeePerGas } - gasPrice = math.BigMin(new(big.Int).Add(tx.MaxPriorityFeePerGas, baseFee), - tx.MaxFeePerGas) + gasPrice = new(big.Int).Add(tx.MaxPriorityFeePerGas, baseFee) + if gasPrice.Cmp(tx.MaxFeePerGas) > 0 { + gasPrice.Set(tx.MaxFeePerGas) + } } if gasPrice == nil { return nil, errors.New("no gas price provided") From 1c555b2defdeb420f83361a73ddb6f5fdd45e0ee Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 23 Dec 2024 19:28:54 +0800 Subject: [PATCH 455/479] core, light: get rid of the dual mutexes (#18436) --- core/blockchain.go | 45 ++++++++++++++++++++-------------------- core/blockchain_test.go | 11 +++++----- light/lightchain.go | 26 +++++++++-------------- light/lightchain_test.go | 7 ++++--- 4 files changed, 42 insertions(+), 47 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index b6d328cc642b..6496abd2f9fa 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -149,7 +149,6 @@ type BlockChain struct { scope event.SubscriptionScope genesisBlock *types.Block - mu sync.RWMutex // global mutex for locking chain operations chainmu sync.RWMutex // blockchain insertion lock procmu sync.RWMutex // block processor lock @@ -411,8 +410,8 @@ func (bc *BlockChain) loadLastState() error { func (bc *BlockChain) SetHead(head uint64) error { log.Warn("Rewinding blockchain", "target", head) - bc.mu.Lock() - defer bc.mu.Unlock() + bc.chainmu.Lock() + defer bc.chainmu.Unlock() // Rewind the header chain, deleting all block bodies until then delFn := func(hash common.Hash, num uint64) { @@ -476,10 +475,10 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error { return err } // If all checks out, manually set the head block - bc.mu.Lock() + bc.chainmu.Lock() bc.currentBlock.Store(block) headBlockGauge.Update(int64(block.NumberU64())) - bc.mu.Unlock() + bc.chainmu.Unlock() log.Info("Committed new head block", "number", block.Number(), "hash", hash) return nil @@ -598,8 +597,8 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error { if err := bc.SetHead(0); err != nil { return err } - bc.mu.Lock() - defer bc.mu.Unlock() + bc.chainmu.Lock() + defer bc.chainmu.Unlock() // Prepare the genesis block and reinitialise the chain if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil { @@ -672,8 +671,8 @@ func (bc *BlockChain) Export(w io.Writer) error { // ExportN writes a subset of the active chain to the given writer. func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { - bc.mu.RLock() - defer bc.mu.RUnlock() + bc.chainmu.RLock() + defer bc.chainmu.RUnlock() if first > last { return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last) @@ -690,7 +689,6 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { return err } } - return nil } @@ -1053,8 +1051,8 @@ const ( // Rollback is designed to remove a chain of links from the database that aren't // certain enough to be valid. func (bc *BlockChain) Rollback(chain []common.Hash) { - bc.mu.Lock() - defer bc.mu.Unlock() + bc.chainmu.Lock() + defer bc.chainmu.Unlock() for i := len(chain) - 1; i >= 0; i-- { hash := chain[i] @@ -1146,7 +1144,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ } // Update the head fast sync block if better - bc.mu.Lock() + bc.chainmu.Lock() head := blockChain[len(blockChain)-1] if td := bc.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case currentFastBlock := bc.CurrentFastBlock() @@ -1158,7 +1156,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ headFastBlockGauge.Update(int64(head.NumberU64())) } } - bc.mu.Unlock() + bc.chainmu.Unlock() log.Info("Imported new block receipts", "count", stats.processed, @@ -1188,6 +1186,15 @@ func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (e // WriteBlockWithState writes the block and all associated state to the database. func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB, tradingState *tradingstate.TradingStateDB, lendingState *lendingstate.LendingStateDB) (status WriteStatus, err error) { + bc.chainmu.Lock() + defer bc.chainmu.Unlock() + + return bc.writeBlockWithState(block, receipts, state, tradingState, lendingState) +} + +// writeBlockWithState writes the block and all associated state to the database, +// but is expects the chain mutex to be held. +func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB, tradingState *tradingstate.TradingStateDB, lendingState *lendingstate.LendingStateDB) (status WriteStatus, err error) { bc.wg.Add(1) defer bc.wg.Done() @@ -1197,9 +1204,6 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types. return NonStatTy, consensus.ErrUnknownAncestor } // Make sure no inconsistent state is leaked during insertion - bc.mu.Lock() - defer bc.mu.Unlock() - currentBlock := bc.CurrentBlock() localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) externTd := new(big.Int).Add(block.Difficulty(), ptd) @@ -1710,7 +1714,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, [] proctime := time.Since(bstart) // Write the block to the chain and get the status. - status, err := bc.WriteBlockWithState(block, receipts, statedb, tradingState, lendingState) + status, err := bc.writeBlockWithState(block, receipts, statedb, tradingState, lendingState) t3 := time.Now() if err != nil { return i, events, coalescedLogs, err @@ -2061,7 +2065,7 @@ func (bc *BlockChain) insertBlock(block *types.Block) ([]interface{}, []*types.L if bc.HasBlockAndFullState(block.Hash(), block.NumberU64()) { return events, coalescedLogs, nil } - status, err := bc.WriteBlockWithState(block, result.receipts, result.state, result.tradingState, result.lendingState) + status, err := bc.writeBlockWithState(block, result.receipts, result.state, result.tradingState, result.lendingState) if err != nil { return events, coalescedLogs, err @@ -2425,9 +2429,6 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i defer bc.wg.Done() whFunc := func(header *types.Header) error { - bc.mu.Lock() - defer bc.mu.Unlock() - _, err := bc.hc.WriteHeader(header) return err } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index de15d9ca6c5a..41283aa592aa 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -25,10 +25,9 @@ import ( "testing" "time" - "github.com/XinFinOrg/XDPoSChain/core/rawdb" - "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus/ethash" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" @@ -129,11 +128,11 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { blockchain.reportBlock(block, receipts, err) return err } - blockchain.mu.Lock() + blockchain.chainmu.Lock() WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash()))) rawdb.WriteBlock(blockchain.db, block) statedb.Commit(true) - blockchain.mu.Unlock() + blockchain.chainmu.Unlock() } return nil } @@ -147,10 +146,10 @@ func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error return err } // Manually insert the header into the database, but don't reorganise (allows subsequent testing) - blockchain.mu.Lock() + blockchain.chainmu.Lock() WriteTd(blockchain.db, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash))) rawdb.WriteHeader(blockchain.db, header) - blockchain.mu.Unlock() + blockchain.chainmu.Unlock() } return nil } diff --git a/light/lightchain.go b/light/lightchain.go index 9cbf95ddf9c6..7664750615a7 100644 --- a/light/lightchain.go +++ b/light/lightchain.go @@ -56,7 +56,6 @@ type LightChain struct { scope event.SubscriptionScope genesisBlock *types.Block - mu sync.RWMutex chainmu sync.RWMutex bodyCache *lru.Cache[common.Hash, *types.Body] @@ -159,8 +158,8 @@ func (lc *LightChain) loadLastState() error { // SetHead rewinds the local chain to a new head. Everything above the new // head will be deleted and the new one set. func (lc *LightChain) SetHead(head uint64) { - lc.mu.Lock() - defer lc.mu.Unlock() + lc.chainmu.Lock() + defer lc.chainmu.Unlock() lc.hc.SetHead(head, nil) lc.loadLastState() @@ -182,8 +181,8 @@ func (lc *LightChain) ResetWithGenesisBlock(genesis *types.Block) { // Dump the entire block chain and purge the caches lc.SetHead(0) - lc.mu.Lock() - defer lc.mu.Unlock() + lc.chainmu.Lock() + defer lc.chainmu.Unlock() // Prepare the genesis block and reinitialise the chain if err := core.WriteTd(lc.chainDb, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil { @@ -297,8 +296,8 @@ func (lc *LightChain) Stop() { // Rollback is designed to remove a chain of links from the database that aren't // certain enough to be valid. func (lc *LightChain) Rollback(chain []common.Hash) { - lc.mu.Lock() - defer lc.mu.Unlock() + lc.chainmu.Lock() + defer lc.chainmu.Unlock() for i := len(chain) - 1; i >= 0; i-- { hash := chain[i] @@ -344,19 +343,13 @@ func (lc *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i // Make sure only one thread manipulates the chain at once lc.chainmu.Lock() - defer func() { - lc.chainmu.Unlock() - time.Sleep(time.Millisecond * 10) // ugly hack; do not hog chain lock in case syncing is CPU-limited by validation - }() + defer lc.chainmu.Unlock() lc.wg.Add(1) defer lc.wg.Done() var events []interface{} whFunc := func(header *types.Header) error { - lc.mu.Lock() - defer lc.mu.Unlock() - status, err := lc.hc.WriteHeader(header) switch status { @@ -450,11 +443,12 @@ func (lc *LightChain) SyncCht(ctx context.Context) bool { num := chtCount*CHTFrequencyClient - 1 header, err := GetHeaderByNumber(ctx, lc.odr, num) if header != nil && err == nil { - lc.mu.Lock() + lc.chainmu.Lock() + defer lc.chainmu.Unlock() + if lc.hc.CurrentHeader().Number.Uint64() < header.Number.Uint64() { lc.hc.SetCurrentHeader(header) } - lc.mu.Unlock() return true } } diff --git a/light/lightchain_test.go b/light/lightchain_test.go index 7d762e2e3f73..4733810e94d9 100644 --- a/light/lightchain_test.go +++ b/light/lightchain_test.go @@ -18,10 +18,11 @@ package light import ( "context" - "github.com/XinFinOrg/XDPoSChain/core/rawdb" "math/big" "testing" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" + "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus/ethash" "github.com/XinFinOrg/XDPoSChain/core" @@ -122,10 +123,10 @@ func testHeaderChainImport(chain []*types.Header, lightchain *LightChain) error return err } // Manually insert the header into the database, but don't reorganize (allows subsequent testing) - lightchain.mu.Lock() + lightchain.chainmu.Lock() core.WriteTd(lightchain.chainDb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, lightchain.GetTdByHash(header.ParentHash))) rawdb.WriteHeader(lightchain.chainDb, header) - lightchain.mu.Unlock() + lightchain.chainmu.Unlock() } return nil } From d7e0e9d734a519835d2a461daf997ba09859045d Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 24 Dec 2024 15:09:18 +0800 Subject: [PATCH 456/479] core: write chain data in atomic way (#20287) --- core/blockchain.go | 302 ++++++++++++++++++------------- core/database_util.go | 5 - core/database_util_test.go | 4 +- core/headerchain.go | 159 +++++++++------- core/rawdb/accessors_chain.go | 130 ++++++++++++- core/rawdb/accessors_indexes.go | 56 ++++++ core/rawdb/accessors_metadata.go | 34 ++++ core/rawdb/schema.go | 33 ++++ light/lightchain.go | 28 ++- light/txpool.go | 5 +- 10 files changed, 548 insertions(+), 208 deletions(-) create mode 100644 core/rawdb/accessors_indexes.go create mode 100644 core/rawdb/accessors_metadata.go diff --git a/core/blockchain.go b/core/blockchain.go index 6496abd2f9fa..480fa606017b 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -413,12 +413,69 @@ func (bc *BlockChain) SetHead(head uint64) error { bc.chainmu.Lock() defer bc.chainmu.Unlock() + updateFn := func(db ethdb.KeyValueWriter, header *types.Header) { + // Rewind the block chain, ensuring we don't end up with a stateless head block + if currentBlock := bc.CurrentBlock(); currentBlock != nil && header.Number.Uint64() < currentBlock.NumberU64() { + newHeadBlock := bc.GetBlock(header.Hash(), header.Number.Uint64()) + if newHeadBlock == nil { + newHeadBlock = bc.genesisBlock + } else { + if _, err := state.New(newHeadBlock.Root(), bc.stateCache); err != nil { + // Rewound state missing, rolled back to before pivot, reset to genesis + newHeadBlock = bc.genesisBlock + } + } + rawdb.WriteHeadBlockHash(db, newHeadBlock.Hash()) + + // Degrade the chain markers if they are explicitly reverted. + // In theory we should update all in-memory markers in the + // last step, however the direction of SetHead is from high + // to low, so it's safe the update in-memory markers directly. + bc.currentBlock.Store(newHeadBlock) + headBlockGauge.Update(int64(newHeadBlock.NumberU64())) + } + + // Rewind the fast block in a simpleton way to the target head + if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock != nil && header.Number.Uint64() < currentFastBlock.NumberU64() { + newHeadFastBlock := bc.GetBlock(header.Hash(), header.Number.Uint64()) + // If either blocks reached nil, reset to the genesis state + if newHeadFastBlock == nil { + newHeadFastBlock = bc.genesisBlock + } + rawdb.WriteHeadFastBlockHash(db, newHeadFastBlock.Hash()) + + // Degrade the chain markers if they are explicitly reverted. + // In theory we should update all in-memory markers in the + // last step, however the direction of SetHead is from high + // to low, so it's safe the update in-memory markers directly. + bc.currentFastBlock.Store(newHeadFastBlock) + headFastBlockGauge.Update(int64(newHeadFastBlock.NumberU64())) + } + } + // Rewind the header chain, deleting all block bodies until then - delFn := func(hash common.Hash, num uint64) { - DeleteBody(bc.db, hash, num) + delFn := func(db ethdb.KeyValueWriter, hash common.Hash, num uint64) { + // Ignore the error here since light client won't hit this path + frozen, _ := bc.db.Ancients() + if num+1 <= frozen { + // Truncate all relative data(header, total difficulty, body, receipt + // and canonical hash) from ancient store. + if err := bc.db.TruncateAncients(num + 1); err != nil { + log.Crit("Failed to truncate ancient data", "number", num, "err", err) + } + + // Remove the hash <-> number mapping from the active store. + rawdb.DeleteHeaderNumber(db, hash) + } else { + // Remove relative body and receipts from the active store. + // The header, total difficulty and canonical hash will be + // removed in the hc.SetHead function. + rawdb.DeleteBody(db, hash, num) + rawdb.DeleteReceipts(db, hash, num) + } + // Todo(rjl493456442) txlookup, bloombits, etc } - bc.hc.SetHead(head, delFn) - currentHeader := bc.hc.CurrentHeader() + bc.hc.SetHead(head, updateFn, delFn) // Clear out any stale content from the caches bc.bodyCache.Purge() @@ -428,38 +485,6 @@ func (bc *BlockChain) SetHead(head uint64) error { bc.futureBlocks.Purge() bc.blocksHashCache.Purge() - // Rewind the block chain, ensuring we don't end up with a stateless head block - if currentBlock := bc.CurrentBlock(); currentBlock != nil && currentHeader.Number.Uint64() < currentBlock.NumberU64() { - bc.currentBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())) - headBlockGauge.Update(int64(currentHeader.Number.Uint64())) - } - if currentBlock := bc.CurrentBlock(); currentBlock != nil { - if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil { - // Rewound state missing, rolled back to before pivot, reset to genesis - bc.currentBlock.Store(bc.genesisBlock) - headBlockGauge.Update(int64(bc.genesisBlock.NumberU64())) - } - } - // Rewind the fast block in a simpleton way to the target head - if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock != nil && currentHeader.Number.Uint64() < currentFastBlock.NumberU64() { - bc.currentFastBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())) - headFastBlockGauge.Update(int64(currentHeader.Number.Uint64())) - } - // If either blocks reached nil, reset to the genesis state - if currentBlock := bc.CurrentBlock(); currentBlock == nil { - bc.currentBlock.Store(bc.genesisBlock) - headBlockGauge.Update(int64(bc.genesisBlock.NumberU64())) - } - if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock == nil { - bc.currentFastBlock.Store(bc.genesisBlock) - headFastBlockGauge.Update(int64(bc.genesisBlock.NumberU64())) - } - currentBlock := bc.CurrentBlock() - currentFastBlock := bc.CurrentFastBlock() - rawdb.WriteHeadBlockHash(bc.db, currentBlock.Hash()) - if err := WriteHeadFastBlockHash(bc.db, currentFastBlock.Hash()); err != nil { - log.Crit("Failed to reset head fast block", "err", err) - } return bc.loadLastState() } @@ -601,20 +626,22 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error { defer bc.chainmu.Unlock() // Prepare the genesis block and reinitialise the chain - if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil { - log.Crit("Failed to write genesis block TD", "err", err) + batch := bc.db.NewBatch() + rawdb.WriteTd(batch, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()) + rawdb.WriteBlock(batch, genesis) + if err := batch.Write(); err != nil { + log.Crit("Failed to write genesis block", "err", err) } - rawdb.WriteBlock(bc.db, genesis) + bc.writeHeadBlock(genesis, false) + + // Last update all in-memory chain markers bc.genesisBlock = genesis - bc.insert(bc.genesisBlock, false) bc.currentBlock.Store(bc.genesisBlock) headBlockGauge.Update(int64(bc.genesisBlock.NumberU64())) - bc.hc.SetGenesis(bc.genesisBlock.Header()) bc.hc.SetCurrentHeader(bc.genesisBlock.Header()) bc.currentFastBlock.Store(bc.genesisBlock) headFastBlockGauge.Update(int64(bc.genesisBlock.NumberU64())) - return nil } @@ -692,27 +719,45 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { return nil } -// insert injects a new head block into the current block chain. This method +// writeHeadBlock injects a new head block into the current block chain. This method // assumes that the block is indeed a true head. It will also reset the head // header and the head fast sync block to this very same block if they are older // or if they are on a different side chain. // // Note, this function assumes that the `mu` mutex is held! -func (bc *BlockChain) insert(block *types.Block, writeBlock bool) { - +func (bc *BlockChain) writeHeadBlock(block *types.Block, writeBlock bool) { blockHash := block.Hash() blockNumberU64 := block.NumberU64() // If the block is on a side chain or an unknown one, force other heads onto it too - updateHeads := GetCanonicalHash(bc.db, blockNumberU64) != blockHash + updateHeads := rawdb.ReadCanonicalHash(bc.db, blockNumberU64) != blockHash // Add the block to the canonical chain number scheme and mark as the head - rawdb.WriteCanonicalHash(bc.db, blockHash, blockNumberU64) - rawdb.WriteHeadBlockHash(bc.db, blockHash) + batch := bc.db.NewBatch() + rawdb.WriteCanonicalHash(batch, blockHash, blockNumberU64) + rawdb.WriteTxLookupEntries(batch, block) + rawdb.WriteHeadBlockHash(batch, blockHash) if writeBlock { - rawdb.WriteBlock(bc.db, block) + rawdb.WriteBlock(batch, block) + } + + // If the block is better than our head or is on a different chain, force update heads + if updateHeads { + rawdb.WriteHeadHeaderHash(batch, blockHash) + rawdb.WriteHeadFastBlockHash(batch, blockHash) + } + + // Flush the whole batch into the disk, exit the node if failed + if err := batch.Write(); err != nil { + log.Crit("Failed to update chain indexes and markers", "err", err) } + // Update all in-memory chain markers in the last step + if updateHeads { + bc.hc.SetCurrentHeader(block.Header()) + bc.currentFastBlock.Store(block) + headFastBlockGauge.Update(int64(blockNumberU64)) + } bc.currentBlock.Store(block) headBlockGauge.Update(int64(block.NumberU64())) @@ -723,17 +768,6 @@ func (bc *BlockChain) insert(block *types.Block, writeBlock bool) { engine.CacheNoneTIPSigningTxs(block.Header(), block.Transactions(), bc.GetReceiptsByHash(blockHash)) } } - - // If the block is better than our head or is on a different chain, force update heads - if updateHeads { - bc.hc.SetCurrentHeader(block.Header()) - - if err := WriteHeadFastBlockHash(bc.db, blockHash); err != nil { - log.Crit("Failed to insert head fast block hash", "err", err) - } - bc.currentFastBlock.Store(block) - headFastBlockGauge.Update(int64(block.NumberU64())) - } } // Genesis retrieves the chain's genesis block. @@ -1054,26 +1088,37 @@ func (bc *BlockChain) Rollback(chain []common.Hash) { bc.chainmu.Lock() defer bc.chainmu.Unlock() + batch := bc.db.NewBatch() for i := len(chain) - 1; i >= 0; i-- { hash := chain[i] + // Degrade the chain markers if they are explicitly reverted. + // In theory we should update all in-memory markers in the + // last step, however the direction of rollback is from high + // to low, so it's safe the update in-memory markers directly. currentHeader := bc.hc.CurrentHeader() if currentHeader.Hash() == hash { - bc.hc.SetCurrentHeader(bc.GetHeader(currentHeader.ParentHash, currentHeader.Number.Uint64()-1)) + newHeadHeader := bc.GetHeader(currentHeader.ParentHash, currentHeader.Number.Uint64()-1) + rawdb.WriteHeadHeaderHash(batch, currentHeader.ParentHash) + bc.hc.SetCurrentHeader(newHeadHeader) } if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock.Hash() == hash { newFastBlock := bc.GetBlock(currentFastBlock.ParentHash(), currentFastBlock.NumberU64()-1) + rawdb.WriteHeadFastBlockHash(batch, currentFastBlock.ParentHash()) bc.currentFastBlock.Store(newFastBlock) - WriteHeadFastBlockHash(bc.db, newFastBlock.Hash()) headFastBlockGauge.Update(int64(newFastBlock.NumberU64())) } if currentBlock := bc.CurrentBlock(); currentBlock.Hash() == hash { newBlock := bc.GetBlock(currentBlock.ParentHash(), currentBlock.NumberU64()-1) + rawdb.WriteHeadBlockHash(batch, currentBlock.ParentHash()) bc.currentBlock.Store(newBlock) headBlockGauge.Update(int64(newBlock.NumberU64())) - rawdb.WriteHeadBlockHash(bc.db, newBlock.Hash()) } } + if err := batch.Write(); err != nil { + log.Crit("Failed to rollback chain markers", "err", err) + } + // TODO: Truncate ancient data which exceeds the current header. } // InsertReceiptChain attempts to complete an already existing header chain with @@ -1126,8 +1171,10 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ if err := WriteTxLookupEntries(batch, block); err != nil { return i, fmt.Errorf("failed to write lookup metadata: %v", err) } - stats.processed++ + // Write everything belongs to the blocks into the database. So that + // we can ensure all components of body is completed(body, receipts, + // tx indexes) if batch.ValueSize() >= ethdb.IdealBatchSize { if err := batch.Write(); err != nil { return 0, err @@ -1135,7 +1182,11 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ bytes += batch.ValueSize() batch.Reset() } + stats.processed++ } + // Write everything belongs to the blocks into the database. So that + // we can ensure all components of body is completed(body, receipts, + // tx indexes) if batch.ValueSize() > 0 { bytes += batch.ValueSize() if err := batch.Write(); err != nil { @@ -1177,10 +1228,12 @@ func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (e bc.wg.Add(1) defer bc.wg.Done() - if err := bc.hc.WriteTd(block.Hash(), block.NumberU64(), td); err != nil { - return err + batch := bc.db.NewBatch() + rawdb.WriteTd(batch, block.Hash(), block.NumberU64(), td) + rawdb.WriteBlock(batch, block) + if err := batch.Write(); err != nil { + log.Crit("Failed to write block into disk", "err", err) } - rawdb.WriteBlock(bc.db, block) return nil } @@ -1208,17 +1261,25 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) externTd := new(big.Int).Add(block.Difficulty(), ptd) - // Irrelevant of the canonical status, write the block itself to the database - if err := bc.hc.WriteTd(block.Hash(), block.NumberU64(), externTd); err != nil { - return NonStatTy, err - } - // Write other block data using a batch. - batch := bc.db.NewBatch() - rawdb.WriteBlock(batch, block) + // Irrelevant of the canonical status, write the block itself to the database. + // + // Note all the components of block(td, hash->number map, header, body, receipts) + // should be written atomically. BlockBatch is used for containing all components. + blockBatch := bc.db.NewBatch() + rawdb.WriteTd(blockBatch, block.Hash(), block.NumberU64(), externTd) + rawdb.WriteBlock(blockBatch, block) + rawdb.WriteReceipts(blockBatch, block.Hash(), block.NumberU64(), receipts) + rawdb.WritePreimages(blockBatch, state.Preimages()) + if err := blockBatch.Write(); err != nil { + log.Crit("Failed to write block into disk", "err", err) + } + // Commit all cached state changes into underlying memory database. root, err := state.Commit(bc.chainConfig.IsEIP158(block.Number())) if err != nil { return NonStatTy, err } + triedb := bc.stateCache.TrieDB() + tradingRoot := common.Hash{} if tradingState != nil { tradingRoot, err = tradingState.Commit() @@ -1233,6 +1294,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. return NonStatTy, err } } + engine, _ := bc.Engine().(*XDPoS.XDPoS) var tradingTrieDb *trie.Database var tradingService utils.TradingService @@ -1248,7 +1310,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. lendingTrieDb = lendingService.GetStateCache().TrieDB() } } - triedb := bc.stateCache.TrieDB() + // If we're running an archive node, always flush if bc.cacheConfig.Disabled { if err := triedb.Commit(root, false); err != nil { @@ -1356,9 +1418,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. } } } - if err := WriteBlockReceipts(batch, block.Hash(), block.NumberU64(), receipts); err != nil { - return NonStatTy, err - } + // If the total difficulty is higher than our known, add it to the canonical chain // Second clause in the if statement reduces the vulnerability to selfish mining. // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf @@ -1368,24 +1428,6 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. // Split same-difficulty blocks by number reorg = block.NumberU64() > currentBlock.NumberU64() } - - // This is the ETH fix. We shall ultimately have this workflow, - // but due to below code has diverged significantly between ETH and XDC, and current issue we have, - // it's best to have it in a different PR with more investigations. - // if reorg { - // // Write the positional metadata for transaction and receipt lookups - // if err := WriteTxLookupEntries(batch, block); err != nil { - // return NonStatTy, err - // } - // // Write hash preimages - // if err := WritePreimages(bc.db, block.NumberU64(), state.Preimages()); err != nil { - // return NonStatTy, err - // } - // } - // if err := batch.Write(); err != nil { - // return NonStatTy, err - // } - if reorg { // Reorganise the chain if the parent is not the head block if block.ParentHash() != currentBlock.Hash() { @@ -1393,26 +1435,15 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. return NonStatTy, err } } - // Write the positional metadata for transaction and receipt lookups - if err := WriteTxLookupEntries(batch, block); err != nil { - return NonStatTy, err - } - // Write hash preimages - if err := WritePreimages(bc.db, block.NumberU64(), state.Preimages()); err != nil { - return NonStatTy, err - } status = CanonStatTy } else { status = SideStatTy } - if err := batch.Write(); err != nil { - return NonStatTy, err - } // Set new head. if status == CanonStatTy { // WriteBlock has already been called, no need to write again - bc.insert(block, false) + bc.writeHeadBlock(block, false) // prepare set of masternodes for the next epoch if bc.chainConfig.XDPoS != nil && ((block.NumberU64() % bc.chainConfig.XDPoS.Epoch) == (bc.chainConfig.XDPoS.Epoch - bc.chainConfig.XDPoS.Gap)) { err := bc.UpdateM1() @@ -2181,15 +2212,16 @@ func (bc *BlockChain) collectLogs(b *types.Block, removed bool) []*types.Log { return logs } -// reorgs takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them -// to be part of the new canonical chain and accumulates potential missing transactions and post an -// event about them +// reorg takes two blocks, an old chain and a new chain and will reconstruct the +// blocks and inserts them to be part of the new canonical chain and accumulates +// potential missing transactions and post an event about them. func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { var ( newChain types.Blocks oldChain types.Blocks commonBlock *types.Block deletedTxs types.Transactions + addedTxs types.Transactions deletedLogs []*types.Log ) log.Warn("Reorg", "oldBlock hash", oldBlock.Hash().Hex(), "number", oldBlock.NumberU64(), "newBlock hash", newBlock.Hash().Hex(), "number", newBlock.NumberU64()) @@ -2238,6 +2270,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { return errors.New("invalid new chain") } } + // Ensure XDPoS engine committed block will be not reverted if xdpos, ok := bc.Engine().(*XDPoS.XDPoS); ok { latestCommittedBlock := xdpos.EngineV2.GetLatestCommittedBlockInfo() @@ -2263,6 +2296,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { } } } + // Ensure the user sees large reorgs if len(oldChain) > 0 && len(newChain) > 0 { logFn := log.Warn @@ -2277,16 +2311,16 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { } else { log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "newnum", newBlock.Number(), "newhash", newBlock.Hash()) } - // Insert the new chain, taking care of the proper incremental order - var addedTxs types.Transactions + + // Insert the new chain(except the head block(reverse order)), + // taking care of the proper incremental order. for i := len(newChain) - 1; i >= 0; i-- { // insert the block in the canonical way, re-writing history - bc.insert(newChain[i], true) - // write lookup entries for hash based transaction/receipt searches - if err := WriteTxLookupEntries(bc.db, newChain[i]); err != nil { - return err - } + bc.writeHeadBlock(newChain[i], true) + + // Collect the new added transactions. addedTxs = append(addedTxs, newChain[i].Transactions()...) + // prepare set of masternodes for the next epoch if bc.chainConfig.XDPoS != nil && ((newChain[i].NumberU64() % bc.chainConfig.XDPoS.Epoch) == (bc.chainConfig.XDPoS.Epoch - bc.chainConfig.XDPoS.Gap)) { err := bc.UpdateM1() @@ -2295,20 +2329,36 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { } } } - // calculate the difference between deleted and added transactions - diff := types.TxDifference(deletedTxs, addedTxs) - // When transactions get deleted from the database that means the - // receipts that were created in the fork must also be deleted - for _, tx := range diff { - DeleteTxLookupEntry(bc.db, tx.Hash()) + + // Delete useless indexes right now which includes the non-canonical + // transaction indexes, canonical chain indexes which above the head. + indexesBatch := bc.db.NewBatch() + for _, tx := range types.TxDifference(deletedTxs, addedTxs) { + rawdb.DeleteTxLookupEntry(indexesBatch, tx.Hash()) + } + // Delete any canonical number assignments above the new head + number := bc.CurrentBlock().NumberU64() + for i := number + 1; ; i++ { + hash := rawdb.ReadCanonicalHash(bc.db, i) + if hash == (common.Hash{}) { + break + } + rawdb.DeleteCanonicalHash(indexesBatch, i) + } + if err := indexesBatch.Write(); err != nil { + log.Crit("Failed to delete useless indexes", "err", err) } + // If any logs need to be fired, do it now. In theory we could avoid creating + // this goroutine if there are no events to fire, but realistcally that only + // ever happens if we're reorging empty blocks, which will only happen on idle + // networks where performance is not an issue either way. if len(deletedLogs) > 0 { go bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs}) } if len(oldChain) > 0 { go func() { - for _, block := range oldChain { - bc.chainSideFeed.Send(ChainSideEvent{Block: block}) + for i := len(oldChain) - 1; i >= 0; i-- { + bc.chainSideFeed.Send(ChainSideEvent{Block: oldChain[i]}) } }() } diff --git a/core/database_util.go b/core/database_util.go index e65ac0ff8e2c..d25e06ac3a6f 100644 --- a/core/database_util.go +++ b/core/database_util.go @@ -487,11 +487,6 @@ func DeleteBlockReceipts(db DatabaseDeleter, hash common.Hash, number uint64) { db.Delete(append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...)) } -// DeleteTxLookupEntry removes all transaction data associated with a hash. -func DeleteTxLookupEntry(db DatabaseDeleter, hash common.Hash) { - db.Delete(append(lookupPrefix, hash.Bytes()...)) -} - // PreimageTable returns a Database instance with the key prefix for preimage entries. func PreimageTable(db ethdb.Database) ethdb.Database { return rawdb.NewTable(db, preimagePrefix) diff --git a/core/database_util_test.go b/core/database_util_test.go index 1f29908b963f..0c1494ba527e 100644 --- a/core/database_util_test.go +++ b/core/database_util_test.go @@ -24,8 +24,8 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/types" - "golang.org/x/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/rlp" + "golang.org/x/crypto/sha3" ) // Tests block header storage and retrieval operations. @@ -304,7 +304,7 @@ func TestLookupStorage(t *testing.T) { } // Delete the transactions and check purge for i, tx := range txs { - DeleteTxLookupEntry(db, tx.Hash()) + rawdb.DeleteTxLookupEntry(db, tx.Hash()) if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn != nil { t.Fatalf("tx #%d [%x]: deleted transaction returned: %v", i, tx.Hash(), txn) } diff --git a/core/headerchain.go b/core/headerchain.go index 51a876d2ecac..633803f2faab 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -45,6 +45,14 @@ const ( // HeaderChain implements the basic block header chain logic that is shared by // core.BlockChain and light.LightChain. It is not usable in itself, only as // a part of either structure. +// +// HeaderChain is responsible for maintaining the header chain including the +// header query and updating. +// +// The components maintained by headerchain includes: (1) total difficult +// (2) header (3) block hash -> number mapping (4) canonical number -> hash mapping +// and (5) head header flag. +// // It is not thread safe either, the encapsulating chain structures should do // the necessary mutex locking/unlocking. type HeaderChain struct { @@ -66,11 +74,8 @@ type HeaderChain struct { engine consensus.Engine } -// NewHeaderChain creates a new HeaderChain structure. -// -// getValidator should return the parent's validator -// procInterrupt points to the parent's interrupt semaphore -// wg points to the parent's shutdown wait group +// NewHeaderChain creates a new HeaderChain structure. ProcInterrupt points +// to the parent's interrupt semaphore. func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine consensus.Engine, procInterrupt func() bool) (*HeaderChain, error) { // Seed a fast but crypto originating random generator seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) @@ -143,41 +148,54 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er externTd := new(big.Int).Add(header.Difficulty, ptd) // Irrelevant of the canonical status, write the td and header to the database - if err := hc.WriteTd(hash, number, externTd); err != nil { - log.Crit("Failed to write header total difficulty", "err", err) + // + // Note all the components of header(td, hash->number index and header) should + // be written atomically. + headerBatch := hc.chainDb.NewBatch() + rawdb.WriteTd(headerBatch, hash, number, externTd) + rawdb.WriteHeader(headerBatch, header) + if err := headerBatch.Write(); err != nil { + log.Crit("Failed to write header into disk", "err", err) } - rawdb.WriteHeader(hc.chainDb, header) // If the total difficulty is higher than our known, add it to the canonical chain // Second clause in the if statement reduces the vulnerability to selfish mining. // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf if externTd.Cmp(localTd) > 0 || (externTd.Cmp(localTd) == 0 && mrand.Float64() < 0.5) { + // If the header can be added into canonical chain, adjust the + // header chain markers(canonical indexes and head header flag). + // + // Note all markers should be written atomically. + // Delete any canonical number assignments above the new head + markerBatch := hc.chainDb.NewBatch() for i := number + 1; ; i++ { - hash := GetCanonicalHash(hc.chainDb, i) + hash := rawdb.ReadCanonicalHash(hc.chainDb, i) if hash == (common.Hash{}) { break } - DeleteCanonicalHash(hc.chainDb, i) + rawdb.DeleteCanonicalHash(markerBatch, i) } + // Overwrite any stale canonical number assignments var ( headHash = header.ParentHash headNumber = header.Number.Uint64() - 1 headHeader = hc.GetHeader(headHash, headNumber) ) - for GetCanonicalHash(hc.chainDb, headNumber) != headHash { - rawdb.WriteCanonicalHash(hc.chainDb, headHash, headNumber) + for rawdb.ReadCanonicalHash(hc.chainDb, headNumber) != headHash { + rawdb.WriteCanonicalHash(markerBatch, headHash, headNumber) headHash = headHeader.ParentHash headNumber = headHeader.Number.Uint64() - 1 headHeader = hc.GetHeader(headHash, headNumber) } // Extend the canonical chain with the new header - rawdb.WriteCanonicalHash(hc.chainDb, hash, number) - if err := WriteHeadHeaderHash(hc.chainDb, hash); err != nil { - log.Crit("Failed to insert head header hash", "err", err) + rawdb.WriteCanonicalHash(markerBatch, hash, number) + rawdb.WriteHeadHeaderHash(markerBatch, hash) + if err := markerBatch.Write(); err != nil { + log.Crit("Failed to write header markers into disk", "err", err) } - + // Last step update all in-memory head header markers hc.currentHeaderHash = hash hc.currentHeader.Store(types.CopyHeader(header)) headHeaderGauge.Update(header.Number.Int64()) @@ -186,10 +204,9 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er } else { status = SideStatTy } - + hc.tdCache.Add(hash, externTd) hc.headerCache.Add(hash, header) hc.numberCache.Add(hash, number) - return } @@ -328,16 +345,6 @@ func (hc *HeaderChain) GetTdByHash(hash common.Hash) *big.Int { return hc.GetTd(hash, hc.GetBlockNumber(hash)) } -// WriteTd stores a block's total difficulty into the database, also caching it -// along the way. -func (hc *HeaderChain) WriteTd(hash common.Hash, number uint64, td *big.Int) error { - if err := WriteTd(hc.chainDb, hash, number, td); err != nil { - return err - } - hc.tdCache.Add(hash, new(big.Int).Set(td)) - return nil -} - // GetHeader retrieves a block header from the database by hash and number, // caching it if found. func (hc *HeaderChain) GetHeader(hash common.Hash, number uint64) *types.Header { @@ -361,12 +368,13 @@ func (hc *HeaderChain) GetHeaderByHash(hash common.Hash) *types.Header { } // HasHeader checks if a block header is present in the database or not. +// In theory, if header is present in the database, all relative components +// like td and hash->number should be present too. func (hc *HeaderChain) HasHeader(hash common.Hash, number uint64) bool { if hc.numberCache.Contains(hash) || hc.headerCache.Contains(hash) { return true } - ok, _ := hc.chainDb.Has(headerKey(hash, number)) - return ok + return rawdb.HasHeader(hc.chainDb, hash, number) } // GetHeaderByNumber retrieves a block header from the database by number, @@ -390,58 +398,79 @@ func (hc *HeaderChain) CurrentHeader() *types.Header { return hc.currentHeader.Load().(*types.Header) } -// SetCurrentHeader sets the current head header of the canonical chain. +// SetCurrentHeader sets the in-memory head header marker of the canonical chan +// as the given header. func (hc *HeaderChain) SetCurrentHeader(head *types.Header) { - if err := WriteHeadHeaderHash(hc.chainDb, head.Hash()); err != nil { - log.Crit("Failed to insert head header hash", "err", err) - } - hc.currentHeader.Store(head) hc.currentHeaderHash = head.Hash() headHeaderGauge.Update(head.Number.Int64()) } -// DeleteCallback is a callback function that is called by SetHead before -// each header is deleted. -type DeleteCallback func(common.Hash, uint64) - -// SetHead rewinds the local chain to a new head. Everything above the new head -// will be deleted and the new one set. -func (hc *HeaderChain) SetHead(head uint64, delFn DeleteCallback) { - height := uint64(0) +type ( + // UpdateHeadBlocksCallback is a callback function that is called by SetHead + // before head header is updated. + UpdateHeadBlocksCallback func(ethdb.KeyValueWriter, *types.Header) - if hdr := hc.CurrentHeader(); hdr != nil { - height = hdr.Number.Uint64() - } + // DeleteBlockContentCallback is a callback function that is called by SetHead + // before each header is deleted. + DeleteBlockContentCallback func(ethdb.KeyValueWriter, common.Hash, uint64) +) +// SetHead rewinds the local chain to a new head. In the case of headers, everything +// above the new head will be deleted and the new one set. In the case of blocks +// though, the head may be further rewound if block bodies are missing (non-archive +// nodes after a fast sync). +func (hc *HeaderChain) SetHead(head uint64, updateFn UpdateHeadBlocksCallback, delFn DeleteBlockContentCallback) { + var ( + parentHash common.Hash + batch = hc.chainDb.NewBatch() + ) for hdr := hc.CurrentHeader(); hdr != nil && hdr.Number.Uint64() > head; hdr = hc.CurrentHeader() { - hash := hdr.Hash() - num := hdr.Number.Uint64() + hash, num := hdr.Hash(), hdr.Number.Uint64() + + // Rewind block chain to new head. + parent := hc.GetHeader(hdr.ParentHash, num-1) + if parent == nil { + parent = hc.genesisHeader + } + parentHash = hdr.ParentHash + // Notably, since geth has the possibility for setting the head to a low + // height which is even lower than ancient head. + // In order to ensure that the head is always no higher than the data in + // the database(ancient store or active store), we need to update head + // first then remove the relative data from the database. + // + // Update head first(head fast block, head full block) before deleting the data. + markerBatch := hc.chainDb.NewBatch() + if updateFn != nil { + updateFn(markerBatch, parent) + } + // Update head header then. + rawdb.WriteHeadHeaderHash(markerBatch, parentHash) + if err := markerBatch.Write(); err != nil { + log.Crit("Failed to update chain markers", "error", err) + } + hc.currentHeader.Store(parent) + hc.currentHeaderHash = parentHash + headHeaderGauge.Update(parent.Number.Int64()) + + // Remove the relative data from the database. if delFn != nil { - delFn(hash, num) + delFn(batch, hash, num) } - DeleteHeader(hc.chainDb, hash, num) - DeleteTd(hc.chainDb, hash, num) - hc.currentHeader.Store(hc.GetHeader(hdr.ParentHash, hdr.Number.Uint64()-1)) + // Rewind header chain to new head. + rawdb.DeleteHeader(batch, hash, num) + rawdb.DeleteTd(batch, hash, num) + rawdb.DeleteCanonicalHash(batch, num) } - // Roll back the canonical chain numbering - for i := height; i > head; i-- { - DeleteCanonicalHash(hc.chainDb, i) + // Flush all accumulated deletions. + if err := batch.Write(); err != nil { + log.Crit("Failed to rewind block", "error", err) } // Clear out any stale content from the caches hc.headerCache.Purge() hc.tdCache.Purge() hc.numberCache.Purge() - - if hc.CurrentHeader() == nil { - hc.currentHeader.Store(hc.genesisHeader) - } - hc.currentHeaderHash = hc.CurrentHeader().Hash() - headHeaderGauge.Update(hc.CurrentHeader().Number.Int64()) - - if err := WriteHeadHeaderHash(hc.chainDb, hc.currentHeaderHash); err != nil { - log.Crit("Failed to reset head header hash", "err", err) - } } // SetGenesis sets a new genesis block header for the chain diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 7073e8431a60..b8cb9cbd7650 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -24,12 +24,32 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rlp" ) +// ReadCanonicalHash retrieves the hash assigned to a canonical block number. +func ReadCanonicalHash(db ethdb.Reader, number uint64) common.Hash { + data, _ := db.Ancient(freezerHashTable, number) + if len(data) == 0 { + data, _ = db.Get(headerHashKey(number)) + // In the background freezer is moving data from leveldb to flatten files. + // So during the first check for ancient db, the data is not yet in there, + // but when we reach into leveldb, the data was already moved. That would + // result in a not found error. + if len(data) == 0 { + data, _ = db.Ancient(freezerHashTable, number) + } + } + if len(data) == 0 { + return common.Hash{} + } + return common.BytesToHash(data) +} + // WriteCanonicalHash stores the hash assigned to a canonical block number. func WriteCanonicalHash(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { if err := db.Put(headerHashKey(number), hash.Bytes()); err != nil { @@ -37,6 +57,13 @@ func WriteCanonicalHash(db ethdb.KeyValueWriter, hash common.Hash, number uint64 } } +// DeleteCanonicalHash removes the number to hash canonical mapping. +func DeleteCanonicalHash(db ethdb.KeyValueWriter, number uint64) { + if err := db.Delete(headerHashKey(number)); err != nil { + log.Crit("Failed to delete number to hash mapping", "err", err) + } +} + // ReadHeaderNumber returns the header number assigned to a hash. func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 { data, _ := db.Get(headerNumberKey(hash)) @@ -56,6 +83,20 @@ func WriteHeaderNumber(db ethdb.KeyValueWriter, hash common.Hash, number uint64) } } +// DeleteHeaderNumber removes hash->number mapping. +func DeleteHeaderNumber(db ethdb.KeyValueWriter, hash common.Hash) { + if err := db.Delete(headerNumberKey(hash)); err != nil { + log.Crit("Failed to delete hash to number mapping", "err", err) + } +} + +// WriteHeadHeaderHash stores the hash of the current canonical head header. +func WriteHeadHeaderHash(db ethdb.KeyValueWriter, hash common.Hash) { + if err := db.Put(headHeaderKey, hash.Bytes()); err != nil { + log.Crit("Failed to store last header's hash", "err", err) + } +} + // WriteHeadBlockHash stores the head block's hash. func WriteHeadBlockHash(db ethdb.KeyValueWriter, hash common.Hash) { if err := db.Put(headBlockKey, hash.Bytes()); err != nil { @@ -63,10 +104,47 @@ func WriteHeadBlockHash(db ethdb.KeyValueWriter, hash common.Hash) { } } +// WriteHeadFastBlockHash stores the hash of the current fast-sync head block. +func WriteHeadFastBlockHash(db ethdb.KeyValueWriter, hash common.Hash) { + if err := db.Put(headFastBlockKey, hash.Bytes()); err != nil { + log.Crit("Failed to store last fast block's hash", "err", err) + } +} + // ReadHeaderRLP retrieves a block header in its raw RLP database encoding. func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue { - data, _ := db.Get(headerKey(number, hash)) - return data + // First try to look up the data in ancient database. Extra hash + // comparison is necessary since ancient database only maintains + // the canonical data. + data, _ := db.Ancient(freezerHeaderTable, number) + if len(data) > 0 && crypto.Keccak256Hash(data) == hash { + return data + } + // Then try to look up the data in leveldb. + data, _ = db.Get(headerKey(number, hash)) + if len(data) > 0 { + return data + } + // In the background freezer is moving data from leveldb to flatten files. + // So during the first check for ancient db, the data is not yet in there, + // but when we reach into leveldb, the data was already moved. That would + // result in a not found error. + data, _ = db.Ancient(freezerHeaderTable, number) + if len(data) > 0 && crypto.Keccak256Hash(data) == hash { + return data + } + return nil // Can't find the data anywhere. +} + +// HasHeader verifies the existence of a block header corresponding to the hash. +func HasHeader(db ethdb.Reader, hash common.Hash, number uint64) bool { + if has, err := db.Ancient(freezerHashTable, number); err == nil && common.BytesToHash(has) == hash { + return true + } + if has, err := db.Has(headerKey(number, hash)); !has || err != nil { + return false + } + return true } // ReadHeader retrieves the block header corresponding to the hash. @@ -104,6 +182,22 @@ func WriteHeader(db ethdb.KeyValueWriter, header *types.Header) { } } +// DeleteHeader removes all block header data associated with a hash. +func DeleteHeader(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { + deleteHeaderWithoutNumber(db, hash, number) + if err := db.Delete(headerNumberKey(hash)); err != nil { + log.Crit("Failed to delete hash to number mapping", "err", err) + } +} + +// deleteHeaderWithoutNumber removes only the block header but does not remove +// the hash to number mapping. +func deleteHeaderWithoutNumber(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { + if err := db.Delete(headerKey(number, hash)); err != nil { + log.Crit("Failed to delete header", "err", err) + } +} + // ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding. func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue { // First try to look up the data in ancient database. Extra hash @@ -165,6 +259,31 @@ func WriteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64, body *t WriteBodyRLP(db, hash, number, data) } +// DeleteBody removes all block body data associated with a hash. +func DeleteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { + if err := db.Delete(blockBodyKey(number, hash)); err != nil { + log.Crit("Failed to delete block body", "err", err) + } +} + +// WriteTd stores the total difficulty of a block into the database. +func WriteTd(db ethdb.KeyValueWriter, hash common.Hash, number uint64, td *big.Int) { + data, err := rlp.EncodeToBytes(td) + if err != nil { + log.Crit("Failed to RLP encode block total difficulty", "err", err) + } + if err := db.Put(headerTDKey(number, hash), data); err != nil { + log.Crit("Failed to store block total difficulty", "err", err) + } +} + +// DeleteTd removes all block total difficulty data associated with a hash. +func DeleteTd(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { + if err := db.Delete(headerTDKey(number, hash)); err != nil { + log.Crit("Failed to delete block total difficulty", "err", err) + } +} + // ReadReceiptsRLP retrieves all the transaction receipts belonging to a block in RLP encoding. func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue { // First try to look up the data in ancient database. Extra hash @@ -270,6 +389,13 @@ func WriteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64, rec } } +// DeleteReceipts removes all receipt data associated with a block hash. +func DeleteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { + if err := db.Delete(blockReceiptsKey(number, hash)); err != nil { + log.Crit("Failed to delete block receipts", "err", err) + } +} + // storedReceiptRLP is the storage encoding of a receipt. // Re-definition in core/types/receipt.go. type storedReceiptRLP struct { diff --git a/core/rawdb/accessors_indexes.go b/core/rawdb/accessors_indexes.go new file mode 100644 index 000000000000..edb263325705 --- /dev/null +++ b/core/rawdb/accessors_indexes.go @@ -0,0 +1,56 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rawdb + +import ( + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/ethdb" + "github.com/XinFinOrg/XDPoSChain/log" + "github.com/XinFinOrg/XDPoSChain/rlp" +) + +type TxLookupEntry struct { + BlockHash common.Hash + BlockIndex uint64 + Index uint64 +} + +// WriteTxLookupEntries stores a positional metadata for every transaction from +// a block, enabling hash based transaction and receipt lookups. +func WriteTxLookupEntries(db ethdb.KeyValueWriter, block *types.Block) { + // Iterate over each transaction and encode its metadata + for i, tx := range block.Transactions() { + entry := TxLookupEntry{ + BlockHash: block.Hash(), + BlockIndex: block.NumberU64(), + Index: uint64(i), + } + data, err := rlp.EncodeToBytes(entry) + if err != nil { + log.Crit("Failed to RLP encode TxLookupEntry", "err", err) + } + if err := db.Put(txLookupKey(tx.Hash()), data); err != nil { + log.Crit("Failed to store tx lookup entry", "err", err) + } + } +} + +// DeleteTxLookupEntry removes all transaction data associated with a hash. +func DeleteTxLookupEntry(db ethdb.KeyValueWriter, hash common.Hash) { + db.Delete(txLookupKey(hash)) +} diff --git a/core/rawdb/accessors_metadata.go b/core/rawdb/accessors_metadata.go new file mode 100644 index 000000000000..a56ab22d7b4c --- /dev/null +++ b/core/rawdb/accessors_metadata.go @@ -0,0 +1,34 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rawdb + +import ( + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/ethdb" + "github.com/XinFinOrg/XDPoSChain/log" +) + +// WritePreimages writes the provided set of preimages to the database. +func WritePreimages(db ethdb.KeyValueWriter, preimages map[common.Hash][]byte) { + for hash, preimage := range preimages { + if err := db.Put(preimageKey(hash), preimage); err != nil { + log.Crit("Failed to store trie preimage", "err", err) + } + } + preimageCounter.Inc(int64(len(preimages))) + preimageHitCounter.Inc(int64(len(preimages))) +} diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index 26d098347e74..a0a1852c7550 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -21,23 +21,41 @@ import ( "encoding/binary" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/metrics" ) // The fields below define the low level database schema prefixing. var ( + // headHeaderKey tracks the latest known header's hash. + headHeaderKey = []byte("LastHeader") + // headBlockKey tracks the latest known full block's hash. headBlockKey = []byte("LastBlock") + // headFastBlockKey tracks the latest known incomplete block's hash during fast sync. + headFastBlockKey = []byte("LastFast") + // Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes). headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header + headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td headerHashSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + headerHashSuffix -> hash headerNumberPrefix = []byte("H") // headerNumberPrefix + hash -> num (uint64 big endian) blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts + + txLookupPrefix = []byte("l") // txLookupPrefix + hash -> transaction/receipt lookup metadata + + preimagePrefix = []byte("secure-key-") // preimagePrefix + hash -> preimage + + preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil) + preimageHitCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil) ) const ( + // freezerHeaderTable indicates the name of the freezer header table. + freezerHeaderTable = "headers" + // freezerHashTable indicates the name of the freezer canonical hash table. freezerHashTable = "hashes" @@ -60,6 +78,11 @@ func headerKey(number uint64, hash common.Hash) []byte { return append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...) } +// headerTDKey = headerPrefix + num (uint64 big endian) + hash + headerTDSuffix +func headerTDKey(number uint64, hash common.Hash) []byte { + return append(headerKey(number, hash), headerTDSuffix...) +} + // headerHashKey = headerPrefix + num (uint64 big endian) + headerHashSuffix func headerHashKey(number uint64) []byte { return append(append(headerPrefix, encodeBlockNumber(number)...), headerHashSuffix...) @@ -79,3 +102,13 @@ func blockBodyKey(number uint64, hash common.Hash) []byte { func blockReceiptsKey(number uint64, hash common.Hash) []byte { return append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...) } + +// txLookupKey = txLookupPrefix + hash +func txLookupKey(hash common.Hash) []byte { + return append(txLookupPrefix, hash.Bytes()...) +} + +// preimageKey = preimagePrefix + hash +func preimageKey(hash common.Hash) []byte { + return append(preimagePrefix, hash.Bytes()...) +} diff --git a/light/lightchain.go b/light/lightchain.go index 7664750615a7..4b90cf3cca7a 100644 --- a/light/lightchain.go +++ b/light/lightchain.go @@ -146,7 +146,6 @@ func (lc *LightChain) loadLastState() error { lc.hc.SetCurrentHeader(header) } } - // Issue a status log and return header := lc.hc.CurrentHeader() headerTd := lc.GetTd(header.Hash(), header.Number.Uint64()) @@ -161,7 +160,7 @@ func (lc *LightChain) SetHead(head uint64) { lc.chainmu.Lock() defer lc.chainmu.Unlock() - lc.hc.SetHead(head, nil) + lc.hc.SetHead(head, nil, nil) lc.loadLastState() } @@ -185,10 +184,13 @@ func (lc *LightChain) ResetWithGenesisBlock(genesis *types.Block) { defer lc.chainmu.Unlock() // Prepare the genesis block and reinitialise the chain - if err := core.WriteTd(lc.chainDb, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil { - log.Crit("Failed to write genesis block TD", "err", err) + batch := lc.chainDb.NewBatch() + rawdb.WriteTd(batch, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()) + rawdb.WriteBlock(batch, genesis) + rawdb.WriteHeadHeaderHash(batch, genesis.Hash()) + if err := batch.Write(); err != nil { + log.Crit("Failed to reset genesis block", "err", err) } - rawdb.WriteBlock(lc.chainDb, genesis) lc.genesisBlock = genesis lc.hc.SetGenesis(lc.genesisBlock.Header()) lc.hc.SetCurrentHeader(lc.genesisBlock.Header()) @@ -299,13 +301,22 @@ func (lc *LightChain) Rollback(chain []common.Hash) { lc.chainmu.Lock() defer lc.chainmu.Unlock() + batch := lc.chainDb.NewBatch() for i := len(chain) - 1; i >= 0; i-- { hash := chain[i] + // Degrade the chain markers if they are explicitly reverted. + // In theory we should update all in-memory markers in the + // last step, however the direction of rollback is from high + // to low, so it's safe the update in-memory markers directly. if head := lc.hc.CurrentHeader(); head.Hash() == hash { + rawdb.WriteHeadHeaderHash(batch, head.ParentHash) lc.hc.SetCurrentHeader(lc.GetHeader(head.ParentHash, head.Number.Uint64()-1)) } } + if err := batch.Write(); err != nil { + log.Crit("Failed to rollback light chain", "error", err) + } } // postChainEvents iterates over the events generated by a chain insertion and @@ -441,12 +452,15 @@ func (lc *LightChain) SyncCht(ctx context.Context) bool { chtCount, _, _ := lc.odr.ChtIndexer().Sections() if headNum+1 < chtCount*CHTFrequencyClient { num := chtCount*CHTFrequencyClient - 1 - header, err := GetHeaderByNumber(ctx, lc.odr, num) - if header != nil && err == nil { + // Retrieve the latest useful header and update to it + if header, err := GetHeaderByNumber(ctx, lc.odr, num); header != nil && err == nil { lc.chainmu.Lock() defer lc.chainmu.Unlock() + // Ensure the chain didn't move past the latest block while retrieving it if lc.hc.CurrentHeader().Number.Uint64() < header.Number.Uint64() { + log.Info("Updated latest header based on CHT", "number", header.Number, "hash", header.Hash()) + rawdb.WriteHeadHeaderHash(lc.chainDb, header.Hash()) lc.hc.SetCurrentHeader(header) } return true diff --git a/light/txpool.go b/light/txpool.go index a1d9190a3874..7a7bf619d7f3 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -25,6 +25,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/txpool" "github.com/XinFinOrg/XDPoSChain/core/types" @@ -205,15 +206,17 @@ func (p *TxPool) checkMinedTxs(ctx context.Context, hash common.Hash, number uin // rollbackTxs marks the transactions contained in recently rolled back blocks // as rolled back. It also removes any positional lookup entries. func (p *TxPool) rollbackTxs(hash common.Hash, txc txStateChanges) { + batch := p.chainDb.NewBatch() if list, ok := p.mined[hash]; ok { for _, tx := range list { txHash := tx.Hash() - core.DeleteTxLookupEntry(p.chainDb, txHash) + rawdb.DeleteTxLookupEntry(batch, txHash) p.pending[txHash] = tx txc.setState(txHash, false) } delete(p.mined, hash) } + batch.Write() } // reorgOnNewHead sets a new head header, processing (and rolling back if necessary) From 2d5dc550d0c4daa648446c9de1b2dc797624116d Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 24 Dec 2024 17:36:11 +0800 Subject: [PATCH 457/479] core/rawdb: rename WriteTxLookupEntries to WriteTxLookupEntriesByBlock (#21480) --- core/blockchain.go | 2 +- core/rawdb/accessors_indexes.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 480fa606017b..d38b3eec6ace 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -735,7 +735,7 @@ func (bc *BlockChain) writeHeadBlock(block *types.Block, writeBlock bool) { // Add the block to the canonical chain number scheme and mark as the head batch := bc.db.NewBatch() rawdb.WriteCanonicalHash(batch, blockHash, blockNumberU64) - rawdb.WriteTxLookupEntries(batch, block) + rawdb.WriteTxLookupEntriesByBlock(batch, block) rawdb.WriteHeadBlockHash(batch, blockHash) if writeBlock { rawdb.WriteBlock(batch, block) diff --git a/core/rawdb/accessors_indexes.go b/core/rawdb/accessors_indexes.go index edb263325705..7f9409875193 100644 --- a/core/rawdb/accessors_indexes.go +++ b/core/rawdb/accessors_indexes.go @@ -30,9 +30,9 @@ type TxLookupEntry struct { Index uint64 } -// WriteTxLookupEntries stores a positional metadata for every transaction from +// WriteTxLookupEntriesByBlock stores a positional metadata for every transaction from // a block, enabling hash based transaction and receipt lookups. -func WriteTxLookupEntries(db ethdb.KeyValueWriter, block *types.Block) { +func WriteTxLookupEntriesByBlock(db ethdb.KeyValueWriter, block *types.Block) { // Iterate over each transaction and encode its metadata for i, tx := range block.Transactions() { entry := TxLookupEntry{ From 2baadc6e8768607c4b33cdeb510c882575e3e98b Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 24 Dec 2024 23:30:33 +0800 Subject: [PATCH 458/479] core: improve shutdown synchronization in BlockChain (#22853) --- core/blockchain.go | 179 +++++++++++++++++++++++++++------------- core/blockchain_test.go | 4 +- internal/syncx/mutex.go | 64 ++++++++++++++ 3 files changed, 189 insertions(+), 58 deletions(-) create mode 100644 internal/syncx/mutex.go diff --git a/core/blockchain.go b/core/blockchain.go index d38b3eec6ace..be6281b6cabe 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -48,6 +48,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/ethclient" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/event" + "github.com/XinFinOrg/XDPoSChain/internal/syncx" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/metrics" "github.com/XinFinOrg/XDPoSChain/params" @@ -81,6 +82,9 @@ var ( blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil) blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil) + errInsertionInterrupted = errors.New("insertion is interrupted") + errChainStopped = errors.New("blockchain is stopped") + CheckpointCh = make(chan int) ) @@ -149,8 +153,11 @@ type BlockChain struct { scope event.SubscriptionScope genesisBlock *types.Block - chainmu sync.RWMutex // blockchain insertion lock - procmu sync.RWMutex // block processor lock + // This mutex synchronizes chain write operations. + // Readers don't need to take it, they can just read the database. + chainmu *syncx.ClosableMutex + + procmu sync.RWMutex // block processor lock currentBlock atomic.Value // Current head of the block chain currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!) @@ -169,10 +176,10 @@ type BlockChain struct { // future blocks are blocks added for later processing futureBlocks *lru.Cache[common.Hash, *types.Block] - wg sync.WaitGroup // chain processing wait group for shutting down - quit chan struct{} // shutdown signal, closed in Stop. - running int32 // 0 if chain is running, 1 when stopped - procInterrupt int32 // interrupt signaler for block processing + wg sync.WaitGroup + quit chan struct{} // shutdown signal, closed in Stop. + running int32 // 0 if chain is running, 1 when stopped + procInterrupt int32 // interrupt signaler for block processing engine consensus.Engine processor Processor // block processor interface @@ -211,6 +218,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par triegc: prque.New[int64, common.Hash](nil), stateCache: state.NewDatabase(db), quit: make(chan struct{}), + chainmu: syncx.NewClosableMutex(), bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit), bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit), receiptsCache: lru.NewCache[common.Hash, types.Receipts](receiptsCacheLimit), @@ -261,8 +269,11 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par } } } - // Take ownership of this particular state - go bc.update() + + // Start future block processor. + bc.wg.Add(1) + go bc.futureBlocksLoop() + return bc, nil } @@ -410,7 +421,9 @@ func (bc *BlockChain) loadLastState() error { func (bc *BlockChain) SetHead(head uint64) error { log.Warn("Rewinding blockchain", "target", head) - bc.chainmu.Lock() + if !bc.chainmu.TryLock() { + return errChainStopped + } defer bc.chainmu.Unlock() updateFn := func(db ethdb.KeyValueWriter, header *types.Header) { @@ -499,8 +512,11 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error { if _, err := trie.NewSecure(block.Root(), bc.stateCache.TrieDB()); err != nil { return err } - // If all checks out, manually set the head block - bc.chainmu.Lock() + + // If all checks out, manually set the head block. + if !bc.chainmu.TryLock() { + return errChainStopped + } bc.currentBlock.Store(block) headBlockGauge.Update(int64(block.NumberU64())) bc.chainmu.Unlock() @@ -622,7 +638,9 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error { if err := bc.SetHead(0); err != nil { return err } - bc.chainmu.Lock() + if !bc.chainmu.TryLock() { + return errChainStopped + } defer bc.chainmu.Unlock() // Prepare the genesis block and reinitialise the chain @@ -698,8 +716,10 @@ func (bc *BlockChain) Export(w io.Writer) error { // ExportN writes a subset of the active chain to the given writer. func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { - bc.chainmu.RLock() - defer bc.chainmu.RUnlock() + if !bc.chainmu.TryLock() { + return errChainStopped + } + defer bc.chainmu.Unlock() if first > last { return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last) @@ -1033,15 +1053,38 @@ func (bc *BlockChain) Stop() { if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) { return } - // Unsubscribe all subscriptions registered from blockchain + + // Unsubscribe all subscriptions registered from blockchain. bc.scope.Close() + + // Signal shutdown to all goroutines. close(bc.quit) - atomic.StoreInt32(&bc.procInterrupt, 1) + bc.StopInsert() + + // Now wait for all chain modifications to end and persistent goroutines to exit. + // + // Note: Close waits for the mutex to become available, i.e. any running chain + // modification will have exited when Close returns. Since we also called StopInsert, + // the mutex should become available quickly. It cannot be taken again after Close has + // returned. + bc.chainmu.Close() bc.wg.Wait() bc.saveData() log.Info("Blockchain manager stopped") } +// StopInsert interrupts all insertion methods, causing them to return +// errInsertionInterrupted as soon as possible. Insertion is permanently disabled after +// calling this method. +func (bc *BlockChain) StopInsert() { + atomic.StoreInt32(&bc.procInterrupt, 1) +} + +// insertStopped returns true after StopInsert has been called. +func (bc *BlockChain) insertStopped() bool { + return atomic.LoadInt32(&bc.procInterrupt) == 1 +} + func (bc *BlockChain) procFutureBlocks() { blocks := make([]*types.Block, 0, bc.futureBlocks.Len()) for _, hash := range bc.futureBlocks.Keys() { @@ -1085,7 +1128,9 @@ const ( // Rollback is designed to remove a chain of links from the database that aren't // certain enough to be valid. func (bc *BlockChain) Rollback(chain []common.Hash) { - bc.chainmu.Lock() + if !bc.chainmu.TryLock() { + return + } defer bc.chainmu.Unlock() batch := bc.db.NewBatch() @@ -1124,6 +1169,8 @@ func (bc *BlockChain) Rollback(chain []common.Hash) { // InsertReceiptChain attempts to complete an already existing header chain with // transaction and receipt data. func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) { + // We don't require the chainMu here since we want to maximize the + // concurrency of header insertion and receipt insertion. bc.wg.Add(1) defer bc.wg.Done() @@ -1195,7 +1242,9 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ } // Update the head fast sync block if better - bc.chainmu.Lock() + if !bc.chainmu.TryLock() { + return 0, errChainStopped + } head := blockChain[len(blockChain)-1] if td := bc.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case currentFastBlock := bc.CurrentFastBlock() @@ -1221,12 +1270,13 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ var lastWrite uint64 -// WriteBlockWithoutState writes only the block and its metadata to the database, +// writeBlockWithoutState writes only the block and its metadata to the database, // but does not write any state. This is used to construct competing side forks // up to the point where they exceed the canonical total difficulty. -func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (err error) { - bc.wg.Add(1) - defer bc.wg.Done() +func (bc *BlockChain) writeBlockWithoutState(block *types.Block, td *big.Int) (err error) { + if bc.insertStopped() { + return errInsertionInterrupted + } batch := bc.db.NewBatch() rawdb.WriteTd(batch, block.Hash(), block.NumberU64(), td) @@ -1239,17 +1289,19 @@ func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (e // WriteBlockWithState writes the block and all associated state to the database. func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB, tradingState *tradingstate.TradingStateDB, lendingState *lendingstate.LendingStateDB) (status WriteStatus, err error) { - bc.chainmu.Lock() + if !bc.chainmu.TryLock() { + return NonStatTy, errInsertionInterrupted + } defer bc.chainmu.Unlock() - return bc.writeBlockWithState(block, receipts, state, tradingState, lendingState) } // writeBlockWithState writes the block and all associated state to the database, // but is expects the chain mutex to be held. func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB, tradingState *tradingstate.TradingStateDB, lendingState *lendingstate.LendingStateDB) (status WriteStatus, err error) { - bc.wg.Add(1) - defer bc.wg.Done() + if bc.insertStopped() { + return NonStatTy, errInsertionInterrupted + } // Calculate the total difficulty of the block ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1) @@ -1470,38 +1522,51 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. // // After insertion is done, all accumulated events will be fired. func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) { - n, events, logs, err := bc.insertChain(chain, true) - bc.PostChainEvents(events, logs) - return n, err -} - -// insertChain will execute the actual chain insertion and event aggregation. The -// only reason this method exists as a separate one is to make locking cleaner -// with deferred statements. -func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []interface{}, []*types.Log, error) { // Sanity check that we have something meaningful to import if len(chain) == 0 { - return 0, nil, nil, nil + return 0, nil } - engine, _ := bc.Engine().(*XDPoS.XDPoS) // Do a sanity check that the provided chain is actually ordered and linked for i := 1; i < len(chain); i++ { - if chain[i].NumberU64() != chain[i-1].NumberU64()+1 || chain[i].ParentHash() != chain[i-1].Hash() { + block, prev := chain[i], chain[i-1] + if block.NumberU64() != chain[i-1].NumberU64()+1 || block.ParentHash() != chain[i-1].Hash() { // Chain broke ancestry, log a messge (programming error) and skip insertion - log.Error("Non contiguous block insert", "number", chain[i].Number(), "hash", chain[i].Hash(), - "parent", chain[i].ParentHash(), "prevnumber", chain[i-1].Number(), "prevhash", chain[i-1].Hash()) + log.Error("Non contiguous block insert", + "number", block.Number(), + "hash", block.Hash(), + "parent", block.ParentHash(), + "prevnumber", prev.Number(), + "prevhash", prev.Hash()) - return 0, nil, nil, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, chain[i-1].NumberU64(), - chain[i-1].Hash().Bytes()[:4], i, chain[i].NumberU64(), chain[i].Hash().Bytes()[:4], chain[i].ParentHash().Bytes()[:4]) + return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, prev.NumberU64(), + prev.Hash().Bytes()[:4], i, block.NumberU64(), block.Hash().Bytes()[:4], block.ParentHash().Bytes()[:4]) } } - // Pre-checks passed, start the full block imports - bc.wg.Add(1) - defer bc.wg.Done() - bc.chainmu.Lock() + // Pre-check passed, start the full block imports. + if !bc.chainmu.TryLock() { + return 0, errChainStopped + } defer bc.chainmu.Unlock() + n, events, logs, err := bc.insertChain(chain, true) + bc.PostChainEvents(events, logs) + return n, err +} + +// insertChain is the internal implementation of InsertChain, which assumes that +// 1) chains are contiguous, and 2) The chain mutex is held. +// +// This method is split out so that import batches that require re-injecting +// historical blocks can do so without releasing the lock, which could lead to +// racey behaviour. If a sidechain import is in progress, and the historic state +// is imported, but then new canon-head is added before the actual sidechain +// completes, then the historic state could be pruned again +func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []interface{}, []*types.Log, error) { + // If the chain is terminating, don't even bother starting up. + if bc.insertStopped() { + return 0, nil, nil, nil + } // A queued approach to delivering events. This is generally // faster than direct delivery and requires much less mutex @@ -1578,7 +1643,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, [] localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) externTd := new(big.Int).Add(bc.GetTd(block.ParentHash(), block.NumberU64()-1), block.Difficulty()) if localTd.Cmp(externTd) > 0 { - if err = bc.WriteBlockWithoutState(block, externTd); err != nil { + if err = bc.writeBlockWithoutState(block, externTd); err != nil { return i, events, coalescedLogs, err } continue @@ -1596,10 +1661,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, [] } log.Debug("Number block need calculated again", "number", block.NumberU64(), "hash", block.Hash().Hex(), "winners", len(winner)) // Import all the pruned blocks to make the state available - bc.chainmu.Unlock() // During reorg, we use verifySeals=false _, evs, logs, err := bc.insertChain(winner, false) - bc.chainmu.Lock() events, coalescedLogs = evs, logs if err != nil { @@ -1628,6 +1691,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, [] var tradingService utils.TradingService var lendingService utils.LendingService isSDKNode := false + engine, _ := bc.Engine().(*XDPoS.XDPoS) if bc.Config().IsTIPXDCXReceiver(block.Number()) && bc.chainConfig.XDPoS != nil && engine != nil && block.NumberU64() > bc.chainConfig.XDPoS.Epoch { author, err := bc.Engine().Author(block.Header()) // Ignore error, we're past header validation if err != nil { @@ -2091,7 +2155,9 @@ func (bc *BlockChain) insertBlock(block *types.Block) ([]interface{}, []*types.L bc.wg.Add(1) defer bc.wg.Done() // Write the block to the chain and get the status. - bc.chainmu.Lock() + if !bc.chainmu.TryLock() { + return nil, nil, errChainStopped + } defer bc.chainmu.Unlock() if bc.HasBlockAndFullState(block.Hash(), block.NumberU64()) { return events, coalescedLogs, nil @@ -2387,7 +2453,10 @@ func (bc *BlockChain) PostChainEvents(events []interface{}, logs []*types.Log) { } } -func (bc *BlockChain) update() { +// futureBlocksLoop processes the 'future block' queue. +func (bc *BlockChain) futureBlocksLoop() { + defer bc.wg.Done() + futureTimer := time.NewTicker(10 * time.Millisecond) defer futureTimer.Stop() for { @@ -2471,13 +2540,11 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i return i, err } - // Make sure only one thread manipulates the chain at once - bc.chainmu.Lock() + if !bc.chainmu.TryLock() { + return 0, errChainStopped + } defer bc.chainmu.Unlock() - bc.wg.Add(1) - defer bc.wg.Done() - whFunc := func(header *types.Header) error { _, err := bc.hc.WriteHeader(header) return err diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 41283aa592aa..31ba9bb4a701 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -128,7 +128,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { blockchain.reportBlock(block, receipts, err) return err } - blockchain.chainmu.Lock() + blockchain.chainmu.MustLock() WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash()))) rawdb.WriteBlock(blockchain.db, block) statedb.Commit(true) @@ -146,7 +146,7 @@ func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error return err } // Manually insert the header into the database, but don't reorganise (allows subsequent testing) - blockchain.chainmu.Lock() + blockchain.chainmu.MustLock() WriteTd(blockchain.db, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash))) rawdb.WriteHeader(blockchain.db, header) blockchain.chainmu.Unlock() diff --git a/internal/syncx/mutex.go b/internal/syncx/mutex.go new file mode 100644 index 000000000000..96a21986c60c --- /dev/null +++ b/internal/syncx/mutex.go @@ -0,0 +1,64 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package syncx contains exotic synchronization primitives. +package syncx + +// ClosableMutex is a mutex that can also be closed. +// Once closed, it can never be taken again. +type ClosableMutex struct { + ch chan struct{} +} + +func NewClosableMutex() *ClosableMutex { + ch := make(chan struct{}, 1) + ch <- struct{}{} + return &ClosableMutex{ch} +} + +// TryLock attempts to lock cm. +// If the mutex is closed, TryLock returns false. +func (cm *ClosableMutex) TryLock() bool { + _, ok := <-cm.ch + return ok +} + +// MustLock locks cm. +// If the mutex is closed, MustLock panics. +func (cm *ClosableMutex) MustLock() { + _, ok := <-cm.ch + if !ok { + panic("mutex closed") + } +} + +// Unlock unlocks cm. +func (cm *ClosableMutex) Unlock() { + select { + case cm.ch <- struct{}{}: + default: + panic("Unlock of already-unlocked ClosableMutex") + } +} + +// Close locks the mutex, then closes it. +func (cm *ClosableMutex) Close() { + _, ok := <-cm.ch + if !ok { + panic("Close of already-closed ClosableMutex") + } + close(cm.ch) +} From d03859d49e8297aed73958d9ec0a60aebf97d694 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 25 Dec 2024 11:02:44 +0800 Subject: [PATCH 459/479] core: update the fast block on chain import at the end of the fast sync phase (#23576) --- core/blockchain.go | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index be6281b6cabe..9fd7582960a8 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -749,11 +749,10 @@ func (bc *BlockChain) writeHeadBlock(block *types.Block, writeBlock bool) { blockHash := block.Hash() blockNumberU64 := block.NumberU64() - // If the block is on a side chain or an unknown one, force other heads onto it too - updateHeads := rawdb.ReadCanonicalHash(bc.db, blockNumberU64) != blockHash - // Add the block to the canonical chain number scheme and mark as the head batch := bc.db.NewBatch() + rawdb.WriteHeadHeaderHash(batch, blockHash) + rawdb.WriteHeadFastBlockHash(batch, blockHash) rawdb.WriteCanonicalHash(batch, blockHash, blockNumberU64) rawdb.WriteTxLookupEntriesByBlock(batch, block) rawdb.WriteHeadBlockHash(batch, blockHash) @@ -761,23 +760,17 @@ func (bc *BlockChain) writeHeadBlock(block *types.Block, writeBlock bool) { rawdb.WriteBlock(batch, block) } - // If the block is better than our head or is on a different chain, force update heads - if updateHeads { - rawdb.WriteHeadHeaderHash(batch, blockHash) - rawdb.WriteHeadFastBlockHash(batch, blockHash) - } - // Flush the whole batch into the disk, exit the node if failed if err := batch.Write(); err != nil { log.Crit("Failed to update chain indexes and markers", "err", err) } // Update all in-memory chain markers in the last step - if updateHeads { - bc.hc.SetCurrentHeader(block.Header()) - bc.currentFastBlock.Store(block) - headFastBlockGauge.Update(int64(blockNumberU64)) - } + bc.hc.SetCurrentHeader(block.Header()) + + bc.currentFastBlock.Store(block) + headFastBlockGauge.Update(int64(blockNumberU64)) + bc.currentBlock.Store(block) headBlockGauge.Update(int64(block.NumberU64())) From c8c39efb630a9b4c573722b7101f97268e4a4b29 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 25 Dec 2024 11:33:56 +0800 Subject: [PATCH 460/479] core: remove unnecessary log copy (#27475) --- core/blockchain.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 9fd7582960a8..66b6fdcddc67 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2263,9 +2263,10 @@ func (bc *BlockChain) collectLogs(b *types.Block, removed bool) []*types.Log { var logs []*types.Log for _, receipt := range receipts { for _, log := range receipt.Logs { - l := *log - l.Removed = removed - logs = append(logs, &l) + if removed { + log.Removed = true + } + logs = append(logs, log) } } return logs From f8decab0611bda001b8462568f17d671ea369470 Mon Sep 17 00:00:00 2001 From: benjamin202410 Date: Sun, 29 Dec 2024 21:47:14 -0800 Subject: [PATCH 461/479] code refactor for performance (#782) Co-authored-by: liam.lai --- .github/workflows/deploy_rpc_image.yml | 7 +- .../XDPoS/engines/engine_v2/epochSwitch.go | 6 +- consensus/XDPoS/engines/engine_v2/timeout.go | 69 +++++++++++-------- consensus/XDPoS/engines/engine_v2/utils.go | 8 +-- params/config.go | 6 ++ params/config_test.go | 19 +++++ 6 files changed, 74 insertions(+), 41 deletions(-) diff --git a/.github/workflows/deploy_rpc_image.yml b/.github/workflows/deploy_rpc_image.yml index ebac65d963a5..088d809aeee8 100644 --- a/.github/workflows/deploy_rpc_image.yml +++ b/.github/workflows/deploy_rpc_image.yml @@ -6,19 +6,19 @@ on: network: type: choice description: 'devnet, testnet, or mainnet' - options: + options: - devnet - testnet - mainnet rpc_image: description: 'full image name' - + jobs: ansible: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - + - name: Update RPC nodes image uses: dawidd6/action-ansible-playbook@v2 with: @@ -38,4 +38,3 @@ jobs: - name: Send deployment notification run: | curl --location --request POST "66.94.98.186:8080/deploy?environment=${{inputs.network}}&service=xdc_rpc&version=${{inputs.rpc_image}}" - \ No newline at end of file diff --git a/consensus/XDPoS/engines/engine_v2/epochSwitch.go b/consensus/XDPoS/engines/engine_v2/epochSwitch.go index 40a684e335e3..9928b88fb25a 100644 --- a/consensus/XDPoS/engines/engine_v2/epochSwitch.go +++ b/consensus/XDPoS/engines/engine_v2/epochSwitch.go @@ -98,7 +98,7 @@ func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types // IsEpochSwitchAtRound() is used by miner to check whether it mines a block in the same epoch with parent func (x *XDPoS_v2) isEpochSwitchAtRound(round types.Round, parentHeader *types.Header) (bool, uint64, error) { - epochNum := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(round)/x.config.Epoch + epochNum := x.config.V2.SwitchEpoch + uint64(round)/x.config.Epoch // if parent is last v1 block and this is first v2 block, this is treated as epoch switch if parentHeader.Number.Cmp(x.config.V2.SwitchBlock) == 0 { return true, epochNum, nil @@ -127,7 +127,7 @@ func (x *XDPoS_v2) GetCurrentEpochSwitchBlock(chain consensus.ChainReader, block } currentCheckpointNumber := epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64() - epochNum := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(epochSwitchInfo.EpochSwitchBlockInfo.Round)/x.config.Epoch + epochNum := x.config.V2.SwitchEpoch + uint64(epochSwitchInfo.EpochSwitchBlockInfo.Round)/x.config.Epoch return currentCheckpointNumber, epochNum, nil } @@ -145,7 +145,7 @@ func (x *XDPoS_v2) IsEpochSwitch(header *types.Header) (bool, uint64, error) { } parentRound := quorumCert.ProposedBlockInfo.Round epochStartRound := round - round%types.Round(x.config.Epoch) - epochNum := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(round)/x.config.Epoch + epochNum := x.config.V2.SwitchEpoch + uint64(round)/x.config.Epoch // if parent is last v1 block and this is first v2 block, this is treated as epoch switch if quorumCert.ProposedBlockInfo.Number.Cmp(x.config.V2.SwitchBlock) == 0 { log.Info("[IsEpochSwitch] true, parent equals V2.SwitchBlock", "round", round, "number", header.Number.Uint64(), "hash", header.Hash()) diff --git a/consensus/XDPoS/engines/engine_v2/timeout.go b/consensus/XDPoS/engines/engine_v2/timeout.go index 875dd22c9c99..169e1db7bab0 100644 --- a/consensus/XDPoS/engines/engine_v2/timeout.go +++ b/consensus/XDPoS/engines/engine_v2/timeout.go @@ -79,6 +79,43 @@ func (x *XDPoS_v2) onTimeoutPoolThresholdReached(blockChainReader consensus.Chai return nil } +func (x *XDPoS_v2) getTCEpochInfo(chain consensus.ChainReader, timeoutCert *types.TimeoutCert) (*types.EpochSwitchInfo, error) { + + epochSwitchInfo, err := x.getEpochSwitchInfo(chain, (chain.CurrentHeader()), (chain.CurrentHeader()).Hash()) + if err != nil { + log.Error("[getTCEpochInfo] Error when getting epoch switch info", "error", err) + return nil, fmt.Errorf("fail on getTCEpochInfo due to failure in getting epoch switch info, %s", err) + } + + epochRound := epochSwitchInfo.EpochSwitchBlockInfo.Round + tempTCEpoch := x.config.V2.SwitchEpoch + uint64(epochRound)/x.config.Epoch + + epochBlockInfo := &types.BlockInfo{ + Hash: epochSwitchInfo.EpochSwitchBlockInfo.Hash, + Round: epochRound, + Number: epochSwitchInfo.EpochSwitchBlockInfo.Number, + } + log.Info("[getTCEpochInfo] Init epochInfo", "number", epochBlockInfo.Number, "round", epochRound, "tcRound", timeoutCert.Round, "tcEpoch", tempTCEpoch) + for epochBlockInfo.Round > timeoutCert.Round { + tempTCEpoch-- + epochBlockInfo, err = x.GetBlockByEpochNumber(chain, tempTCEpoch) + if err != nil { + log.Error("[getTCEpochInfo] Error when getting epoch block info by tc round", "error", err) + return nil, fmt.Errorf("fail on getTCEpochInfo due to failure in getting epoch block info tc round, %s", err) + } + log.Debug("[getTCEpochInfo] Loop to get right epochInfo", "number", epochBlockInfo.Number, "round", epochBlockInfo.Round, "tcRound", timeoutCert.Round, "tcEpoch", tempTCEpoch) + } + tcEpoch := tempTCEpoch + log.Info("[getTCEpochInfo] Final TC epochInfo", "number", epochBlockInfo.Number, "round", epochBlockInfo.Round, "tcRound", timeoutCert.Round, "tcEpoch", tcEpoch) + + epochInfo, err := x.getEpochSwitchInfo(chain, nil, epochBlockInfo.Hash) + if err != nil { + log.Error("[getTCEpochInfo] Error when getting epoch switch info", "error", err) + return nil, fmt.Errorf("fail on getTCEpochInfo due to failure in getting epoch switch info, %s", err) + } + return epochInfo, nil +} + func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.TimeoutCert) error { /* 1. Get epoch master node list by gapNumber @@ -110,37 +147,9 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time } } - epochSwitchInfo, err := x.getEpochSwitchInfo(chain, (chain.CurrentHeader()), (chain.CurrentHeader()).Hash()) + epochInfo, err := x.getTCEpochInfo(chain, timeoutCert) if err != nil { - log.Error("[verifyTC] Error when getting epoch switch info", "error", err) - return fmt.Errorf("fail on verifyTC due to failure in getting epoch switch info, %s", err) - } - - epochRound := epochSwitchInfo.EpochSwitchBlockInfo.Round - tempTCEpoch := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(epochRound)/x.config.Epoch - - epochBlockInfo := &types.BlockInfo{ - Hash: epochSwitchInfo.EpochSwitchBlockInfo.Hash, - Round: epochRound, - Number: epochSwitchInfo.EpochSwitchBlockInfo.Number, - } - log.Info("[verifyTC] Init epochInfo", "number", epochBlockInfo.Number, "round", epochRound, "tcRound", timeoutCert.Round, "tcEpoch", tempTCEpoch) - for epochBlockInfo.Round > timeoutCert.Round { - tempTCEpoch-- - epochBlockInfo, err = x.GetBlockByEpochNumber(chain, tempTCEpoch) - if err != nil { - log.Error("[verifyTC] Error when getting epoch block info by tc round", "error", err) - return fmt.Errorf("fail on verifyTC due to failure in getting epoch block info tc round, %s", err) - } - log.Debug("[verifyTC] Loop to get right epochInfo", "number", epochBlockInfo.Number, "round", epochBlockInfo.Round, "tcRound", timeoutCert.Round, "tcEpoch", tempTCEpoch) - } - tcEpoch := tempTCEpoch - log.Info("[verifyTC] Final TC epochInfo", "number", epochBlockInfo.Number, "round", epochBlockInfo.Round, "tcRound", timeoutCert.Round, "tcEpoch", tcEpoch) - - epochInfo, err := x.getEpochSwitchInfo(chain, nil, epochBlockInfo.Hash) - if err != nil { - log.Error("[verifyTC] Error when getting epoch switch info", "error", err) - return fmt.Errorf("fail on verifyTC due to failure in getting epoch switch info, %s", err) + return err } certThreshold := x.config.V2.Config(uint64(timeoutCert.Round)).CertThreshold diff --git a/consensus/XDPoS/engines/engine_v2/utils.go b/consensus/XDPoS/engines/engine_v2/utils.go index 49d79177e8c1..0a0f2ad7a68f 100644 --- a/consensus/XDPoS/engines/engine_v2/utils.go +++ b/consensus/XDPoS/engines/engine_v2/utils.go @@ -296,7 +296,7 @@ func (x *XDPoS_v2) GetBlockByEpochNumber(chain consensus.ChainReader, targetEpoc if err != nil { return nil, err } - epochNum := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(epochSwitchInfo.EpochSwitchBlockInfo.Round)/x.config.Epoch + epochNum := x.config.V2.SwitchEpoch + uint64(epochSwitchInfo.EpochSwitchBlockInfo.Round)/x.config.Epoch // if current epoch is this epoch, we early return the result if targetEpochNum == epochNum { return epochSwitchInfo.EpochSwitchBlockInfo, nil @@ -304,11 +304,11 @@ func (x *XDPoS_v2) GetBlockByEpochNumber(chain consensus.ChainReader, targetEpoc if targetEpochNum > epochNum { return nil, errors.New("input epoch number > current epoch number") } - if targetEpochNum < x.config.V2.SwitchBlock.Uint64()/x.config.Epoch { + if targetEpochNum < x.config.V2.SwitchEpoch { return nil, errors.New("input epoch number < v2 begin epoch number") } // the block's round should be in [estRound,estRound+Epoch-1] - estRound := types.Round((targetEpochNum - x.config.V2.SwitchBlock.Uint64()/x.config.Epoch) * x.config.Epoch) + estRound := types.Round((targetEpochNum - x.config.V2.SwitchEpoch) * x.config.Epoch) // check the round2epochBlockInfo cache blockInfo := x.getBlockByEpochNumberInCache(chain, estRound) if blockInfo != nil { @@ -330,7 +330,7 @@ func (x *XDPoS_v2) GetBlockByEpochNumber(chain consensus.ChainReader, targetEpoc return nil, err } for _, info := range epochSwitchInfos { - epochNum := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(info.EpochSwitchBlockInfo.Round)/x.config.Epoch + epochNum := x.config.V2.SwitchEpoch + uint64(info.EpochSwitchBlockInfo.Round)/x.config.Epoch if epochNum == targetEpochNum { return info.EpochSwitchBlockInfo, nil } diff --git a/params/config.go b/params/config.go index ba9b45212a7a..6630f9b197b6 100644 --- a/params/config.go +++ b/params/config.go @@ -180,6 +180,7 @@ var ( Gap: 450, FoudationWalletAddr: common.HexToAddress("xdc92a289fe95a85c53b8d0d113cbaef0c1ec98ac65"), V2: &V2{ + SwitchEpoch: common.TIPV2SwitchBlock.Uint64() / 900, SwitchBlock: common.TIPV2SwitchBlock, CurrentConfig: MainnetV2Configs[0], AllConfigs: MainnetV2Configs, @@ -222,6 +223,7 @@ var ( Gap: 450, FoudationWalletAddr: common.HexToAddress("xdc746249c61f5832c5eed53172776b460491bdcd5c"), V2: &V2{ + SwitchEpoch: common.TIPV2SwitchBlock.Uint64() / 900, SwitchBlock: common.TIPV2SwitchBlock, CurrentConfig: TestnetV2Configs[0], AllConfigs: TestnetV2Configs, @@ -246,6 +248,7 @@ var ( Gap: 450, FoudationWalletAddr: common.HexToAddress("0x746249c61f5832c5eed53172776b460491bdcd5c"), V2: &V2{ + SwitchEpoch: common.TIPV2SwitchBlock.Uint64() / 900, SwitchBlock: common.TIPV2SwitchBlock, CurrentConfig: DevnetV2Configs[0], AllConfigs: DevnetV2Configs, @@ -269,6 +272,7 @@ var ( Period: 15, Epoch: 900, V2: &V2{ + SwitchEpoch: 9999999999 / 900, SwitchBlock: big.NewInt(9999999999), CurrentConfig: MainnetV2Configs[0], AllConfigs: MainnetV2Configs, @@ -355,6 +359,7 @@ var ( FoudationWalletAddr: common.HexToAddress("0x0000000000000000000000000000000000000068"), Reward: 250, V2: &V2{ + SwitchEpoch: 1, SwitchBlock: big.NewInt(900), CurrentConfig: UnitTestV2Configs[0], AllConfigs: UnitTestV2Configs, @@ -451,6 +456,7 @@ type XDPoSConfig struct { type V2 struct { lock sync.RWMutex // Protects the signer fields + SwitchEpoch uint64 SwitchBlock *big.Int `json:"switchBlock"` CurrentConfig *V2Config `json:"config"` AllConfigs map[uint64]*V2Config `json:"allConfigs"` diff --git a/params/config_test.go b/params/config_test.go index 3b18db4b5977..bd4113e135f1 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -117,3 +117,22 @@ func TestBuildConfigIndex(t *testing.T) { expected := []uint64{900, 10, 0} assert.Equal(t, expected, index) } + +// Test switch epoch is switchblock divide into epoch per block +func TestSwitchEpoch(t *testing.T) { + config := XDCMainnetChainConfig.XDPoS + epoch := config.Epoch + assert.Equal(t, config.V2.SwitchEpoch, config.V2.SwitchBlock.Uint64()/epoch) + + config = TestnetChainConfig.XDPoS + epoch = config.Epoch + assert.Equal(t, config.V2.SwitchEpoch, config.V2.SwitchBlock.Uint64()/epoch) + + config = DevnetChainConfig.XDPoS + epoch = config.Epoch + assert.Equal(t, config.V2.SwitchEpoch, config.V2.SwitchBlock.Uint64()/epoch) + + config = TestXDPoSMockChainConfig.XDPoS + epoch = config.Epoch + assert.Equal(t, config.V2.SwitchEpoch, config.V2.SwitchBlock.Uint64()/epoch) +} From 6307460603097ebb0d7243be7713c4d5cbfaadc3 Mon Sep 17 00:00:00 2001 From: benjamin202410 Date: Sun, 29 Dec 2024 21:50:28 -0800 Subject: [PATCH 462/479] update bootnode list (#783) Co-authored-by: liam.lai --- cicd/mainnet/bootnodes.list | 39 +++++++++++++++++++------------------ cicd/testnet/bootnodes.list | 9 --------- 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/cicd/mainnet/bootnodes.list b/cicd/mainnet/bootnodes.list index f720193cfa98..4527bbbf5db1 100644 --- a/cicd/mainnet/bootnodes.list +++ b/cicd/mainnet/bootnodes.list @@ -1,31 +1,20 @@ -enode://11299e930438a12a7171d540e7ed594172babd7856aa518c4ca0a25564d6cf7fe4abaf74a68fd626118c205095f5aa40badf5f16f9651e1743383040f74323ca@154.53.57.144:30303 -enode://4ebfb7b3dd8d335fb5160410b9d4b494224caf7e2b5a2630f000a69b9edc83382b7c45536981a60e217876a1f553ed351846f14623e969df0796fb2d5d55b947@209.126.1.10:30303 -enode://ad638c179f9f72565bd78db98aa68d3c33b3a476421faeb07e185b18fa95d5999e72c15b9811526a65c0cf27573d25dc1905a5bf09d7de634b5fc5ef1cd661de@209.126.11.108:30303 -enode://1e331800102e30f83418b5b6eb59a7e325aaa05f9efb9756e6c6ba1624c8e17ba1dd4334a32c87f9022afe2bb3a53410d9f96a0b4ae3aecd574b08785d5f6014@209.126.2.33:30303 -enode://70e471eb021a3bca37407fabe461873380f100823a1ad610d564dd11514e7db2a9c79a74456adda3e3a48f6364ee08f390e3e0df9288eaceae1fc2d084647c92@209.126.0.250:30303 -enode://4406a66a21b7c242c4fa9605301e889419e4c0d4f503baed88b24f7521dc783a9259f9fb23ca4748cbad8d48cf59164419438b317a889488cc7bcf0d44023296@161.97.164.153:30303 -enode://021fb5b586c865f31db4fc82c9aa9e21110328fe88e1e99a65ca87031bcd6544785c68d47efe15e106c71b0066509d94a1379fb2a95ecdebab77d518b222422f@161.97.131.6:30303 -enode://25ce413f1f20ec9099d7c93ea583c749454f0523e906a4d2ad835fbd12eaf296976bd89a3ff1ab7a34d7afe25cc61572a4da5cd6f72c0551240c590f5a1affa2@161.97.131.145:30303 -enode://25faf1b866535701790398623ac86705a91dff419a6e460eaf80325220dc2525c97bbf0812085fd8dc9745d1d2a7ff024b7b36aa27f6e4fcb7a10ac508e3078a@161.97.172.27:30303 -enode://fc0caeb94dc942f06c56885e07206a0431f6f1045a46de6f7c65a93826220c84f1b72894ffae9091e2e05472f7926b59ed159ffc145ac0225dfb2ac1325b3629@161.97.128.201:30303 -enode://6358dd8ba702f8666259d07513f760d897739956e7b8ee4da29cc2461020f04c1d80f3d17becf69f1f0b9bfe5ed83754a5ebbad5a193e9d56266c668cf0ab4ba@161.97.129.254:30303 -enode://c5ab74d3cc2a7f68cf16b421ec0c19d1de2aa10de20d6fd4b6589174b216c81e930a7d395cd64099218838f7adb2fd9302a9b5d0650123930ce3ec553369e32b@212.38.189.120:30303 -enode://7698c7dd2f5ca348c2237bc62751c3bd8432d89cf082233540d9512a4d49b6d90fea395f0a2d79ccb90ef663c1cb9baa5204a494b904acb91fb42d7ef2991c1b@212.38.189.121:30303 -enode://7698c7dd2f5ca348c2237bc62751c3bd8432d89cf082233540d9512a4d49b6d90fea395f0a2d79ccb90ef663c1cb9baa5204a494b904acb91fb42d7ef2991c1b@212.38.189.122:30303 -enode://7923727ebf54d1fc9d9fef1508df7235b123b7d74a7dbd900f78ce169e5e3f30171291f22e7c9ea7c66e97ea405f75ad460073434e6f282b0c902fd0eb99047f@23.111.13.177:30303 -enode://c225c4954add10563b34f230ef9ae076760fb3cb347f15b2629ca94054b2c7b92593ae198830beb601a5515ca464af709229c7cbbaa0512bca4d3ef113ac8241@103.101.130.85:30303 -enode://c225c4954add10563b34f230ef9ae076760fb3cb347f15b2629ca94054b2c7b92593ae198830beb601a5515ca464af709229c7cbbaa0512bca4d3ef113ac8241@103.101.130.199:30303 -enode://f9b52fa47ef04a94d71b0e9723df4a453f42cb359573175acdc2eb693c36cd64c0afb697a04ad2c61d74bd3e60fb84d3077e3ce92d5bcaa642177064e77f4587@103.101.129.198:30303 -enode://c225c4954add10563b34f230ef9ae076760fb3cb347f15b2629ca94054b2c7b92593ae198830beb601a5515ca464af709229c7cbbaa0512bca4d3ef113ac8241@103.101.130.199:30303 enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@154.53.42.5:30303 enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@194.146.12.183:30303 enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@194.146.12.181:30303 enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@154.53.56.28:30303 enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@144.126.150.58:30303 +enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@194.146.12.182:30303 +enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@78.129.229.96:30301 +enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@5.152.223.199:30301 +enode://560113969268e30a430991e36fcb1a6ea8a2bc16f23bc1dabffd6f8c17649237d58d8cf554e9ce882184abdc9b3a607230123e2bcf819d4ed01ec02acee1c4c2@78.159.100.155:30303 +enode://a86d3268c060323572bfeb6df76081e4668ec44cc60ff028c1685a9a683c5d6497c987b5dfade81323d08875066218a1877484396e11f400ce1c2c9bd343c935@78.159.100.155:30304 +enode://b69648bc931e60ab59057af72f411567444725a165c8798ffdb420de3be2636c5823f69f8ea76895422d5797af439c97221daa153785ad4bc2e7a6719a6116ae@78.159.100.155:30305 +enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@107.191.46.179:30301 enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@107.191.63.81:30301 enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@108.61.208.82:30301 enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@137.220.52.242:30301 enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@137.220.53.156:30301 +enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@139.180.167.226:30301 enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@139.180.188.238:30301 enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@139.180.207.188:30301 enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@149.248.57.56:30301 @@ -34,3 +23,15 @@ enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@155.138.132.39:30301 enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@155.138.135.5:30301 enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@155.138.136.247:30301 +enode://07257a47f5609c284e43b6c08d54fa2bd1ef524e39d552da928938b9e86c1944aec5d4c7e46919b6953fe4ecd8b4847c6aa0e2303fdc417cb3439abd03706311@209.126.9.230:30303 +enode://3dc8823dba75781aad99534ff7ff0460d95120644421c3874d3759a25eb4c84ad1e7aa3129110901f1dd42d6c19cc02a7757c152c5d433ceb8e47c621d02e15e@144.126.146.184:30303 +enode://fc7d703932a2f53e203c6263462af69bfca3520c5130748f54f3ce85196f895f6be43e618544e66b511f78e926dcfe7e842e0b94937cdadb9d6e9f328a34796f@66.94.122.169:30303 +enode://8d6ecd0fcc842a3553847b7f66cdee2c008e295ba657ed63da4b8e6da303c6bb308b6e9a8d9c0d0cc357c690033696e5c8222f726a3f39338101ef9b1f4026f1@66.94.122.172:30303 +enode://f1bb123968485d1acb3d3b19230742e7018c91419d2acd5fb391d5150310c6b6a686de287394dd28087ba5e6019a8ccc494a4d8eddf21e8324e794173d7d9f35@66.94.100.116:30303 +enode://25bd979e84e09401b3dcbf506f859fb979ca5dd1d1687d500a5fa5798308119f4cbe49cd26136d6a480f855b5b3f99a26f696c01fff45630aba5aea24b958469@66.94.100.115:30303 +enode://a49ba70cb4991fc3dbb15f0133684c3501a46004a0d4f3abfa1137c5a1c50f5942b2b8bc9c9fa6649621275df3532681f1167c06ea2e1ab0cd6f71e33cf97d9b@86.48.31.135:30303 +enode://5de7e18451f56ae6506d28a85b0d274b03dec7028bdfaf9407816765d38352c654a35b4b260c710de0f2e33bd9df63420ec448d4b87d46b81a05729c9d1df350@66.94.122.40:30303 +enode://4515870a9dd133c3cbbcb194e0f73853fae510217dd35bb0ffe6793f9b21945e0a280708273e7f5b30330b6737858bedabbc6eebcf68d38089eb11e4fb6930d8@161.97.156.58:30303 +enode://1686e2536b5c27a443575e0e94daee0736d6a043e806bfe889b979003c43a6017f00aa2b4e5b820afbd02822f1918e07ce15b050ae55202355c1d803ed372b0c@161.97.175.15:30303 +enode://ad187cfa297ccd540b6afc1fbc4e5d552c27eadd788084b5667cd57b558109f338b9403f880d96d6efed65fbd741521ec81c77c01b0b7bce5dbafe30043369fd@194.233.74.229:30303 +enode://3dc8823dba75781aad99534ff7ff0460d95120644421c3874d3759a25eb4c84ad1e7aa3129110901f1dd42d6c19cc02a7757c152c5d433ceb8e47c621d02e15e@144.126.146.184:30303 \ No newline at end of file diff --git a/cicd/testnet/bootnodes.list b/cicd/testnet/bootnodes.list index 9fca6cadab7c..c2af1034635b 100644 --- a/cicd/testnet/bootnodes.list +++ b/cicd/testnet/bootnodes.list @@ -1,12 +1,3 @@ -enode://1c20e6b46ce608c1fe739e78611225b94e663535b74a1545b1667eac8ff75ed43216306d123306c10e043f228e42cc53cb2728655019292380313393eaaf6e23@188.227.164.51:30301 -enode://278a1ac8aab1381b460788dcf4dcaffd58252f0f4c57d95e4c68b4b2bbf0b0fee83b5c140b3e6eb3af8b8369e76fa6996ccc667f65f9ce35c1d14fbffbfdfd52@95.179.217.201:30301 -enode://518a2b963f0e41ef6520973766a8caad9b13c4261c50a73eedfeaa3ebf3cce623dadc132d8d42ec41a50195c925ca55abd4e2a96258f9d335e3f625e6c2df789@149.28.167.190:30301 -enode://63f5a65ffce84b7123562b8bc871ae63db6dadded14df1dae6ad09b4a30e722a72e522d99aaf0bbad19380a0dd3abe94768ed03a8f68204b5dee6ae16ddb4d83@194.233.77.19:30301 -enode://87497c34ce0102e888040d6ec530e73c33bb0330770105797cf5667f465115d49d53cb583a8b4e34ff14048448c63f03fbd1c269897d7b99716dcdb942b6d707@144.91.108.231:30301 -enode://8c372fc5859e69a49037768f2ece54f14d1d159ec3181d06d7b58f1a54dac4f2f13623f1377a7e506e48e5f33b1752a9726a44b33fdb158ce7d6d5a57c055f65@207.244.240.232:30301 -enode://bed408a65f2894b09ac11ec03eee6b4b1af279e27c81fbe942aa6300ebe5d734913c0c7b30cdbcf98dfebd7a46bf9c79cfc1a8878e20240b5da872dc358be571@66.94.121.62:30301 -enode://dd8973429effba6d6f8edd27efad8544553bb39c82c6350c09e82cd3cfdcdecaf6d0ce9820ed97482aedd0b9236e88d362ca031a65dcbdac52aa292e693723e3@144.126.150.69:30301 -enode://fb5616c009265a162c08fb3984192ffc4df18946dc4d591ba5480011169e6f3f324685b5e102e8123d32e27cd968220d6400b9f1f0a6d1611c130079cd1e71bb@161.97.93.168:30301 enode://75e95709fd89a6314b0d5363226e3e46c56d98c6ddd3ec3cbdffcb65fdfedc8cbad870e2af1382b97e5986c1465cdb00e621cc01e1910f622ba20ed2359a02a0@213.136.89.186:30304 enode://34ba74c6a0ef379040243e19c5f673b29155ec5928a5300f1cdfd2215983b7720d52585acb16f7199cc5abdd9ff3657305e901e097f951e35740b0a80fce3e18@185.217.126.17:30304 enode://d7c070939155be296a8b254d43a0927e6c6777f1352239499fab867af455b9c671bddd5f9ae359ddcd6af9a368746e2f993416bafff2d03a4d974d888daaf020@38.242.244.116:30304 From ac3b44eebf259a10e42165d8966958e1262806e0 Mon Sep 17 00:00:00 2001 From: benjamin202410 Date: Mon, 30 Dec 2024 18:31:31 -0800 Subject: [PATCH 463/479] remove devnet ecs terraform (#786) Co-authored-by: liam.lai --- .github/workflows/ci.yml | 78 ++----------- cicd/devnet/terraform/.env | 13 --- cicd/devnet/terraform/iam.tf | 28 ----- cicd/devnet/terraform/main.tf | 63 ----------- .../module/region/container-definition.tpl | 44 -------- cicd/devnet/terraform/module/region/ecs.tf | 96 ---------------- cicd/devnet/terraform/module/region/efs.tf | 67 ----------- cicd/devnet/terraform/module/region/main.tf | 102 ----------------- cicd/devnet/terraform/module/region/rpc.tf | 104 ------------------ .../terraform/module/region/variables.tf | 31 ------ cicd/devnet/terraform/s3.tf | 13 --- cicd/devnet/terraform/variables.tf | 39 ------- 12 files changed, 10 insertions(+), 668 deletions(-) delete mode 100644 cicd/devnet/terraform/.env delete mode 100644 cicd/devnet/terraform/iam.tf delete mode 100644 cicd/devnet/terraform/main.tf delete mode 100644 cicd/devnet/terraform/module/region/container-definition.tpl delete mode 100644 cicd/devnet/terraform/module/region/ecs.tf delete mode 100644 cicd/devnet/terraform/module/region/efs.tf delete mode 100644 cicd/devnet/terraform/module/region/main.tf delete mode 100644 cicd/devnet/terraform/module/region/rpc.tf delete mode 100644 cicd/devnet/terraform/module/region/variables.tf delete mode 100644 cicd/devnet/terraform/s3.tf delete mode 100644 cicd/devnet/terraform/variables.tf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3306363dd0fd..b6b7db7ffe5b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,23 +100,6 @@ jobs: echo "image_name=$image_name" echo "image_name=$image_name" >> "$GITHUB_OUTPUT" - devnet_terraform_apply: - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/') - needs: devnet_build_push - environment: devnet - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - steps: - - uses: actions/checkout@v4 - - name: Terraform Apply - run: | - git_hash=$(git rev-parse --short "$GITHUB_SHA") - cd cicd/devnet/terraform - terraform init ${{ env.tf_init_cli_options }} - terraform apply -var "docker_tag=dev-upgrade-${git_hash}" ${{ env.tf_apply_cli_options }} - rpcnode_terraform_apply: runs-on: ubuntu-latest if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/') @@ -143,54 +126,13 @@ jobs: --extra-vars network=ec2_rpcs --extra-vars rpc_image=${{ needs.devnet_build_push.outputs.output1 }} - devnet_dev-upgrade_node: - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/') - needs: rpcnode_terraform_apply - environment: devnet - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - steps: - - uses: actions/checkout@v4 - - name: ECS Update - run: | - aws ecs update-service --region ap-southeast-1 --cluster devnet-xdcnode-cluster --service ecs-service-rpc1 --force-new-deployment --no-cli-pager | head -n 10; - - testnet_dev-upgrade_node: - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/') - needs: rpcnode_terraform_apply - environment: testnet - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - steps: - - uses: actions/checkout@v4 - - name: ECS Update - run: | - aws ecs update-service --region ap-southeast-1 --cluster testnet-xdcnode-cluster --service ecs-service-testnet-rpc1 --force-new-deployment --no-cli-pager | head -n 10; - - mainnet_dev-upgrade_node: - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/') - needs: rpcnode_terraform_apply - environment: mainnet - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - steps: - - uses: actions/checkout@v4 - - name: ECS Update - run: | - aws ecs update-service --region ap-southeast-1 --cluster mainnet-xdcnode-cluster --service ecs-service-mainnet-rpc1 --force-new-deployment --no-cli-pager | head -n 10; - - devnet_send_notification: - runs-on: ubuntu-latest - needs: devnet_terraform_apply - if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/') - steps: - - uses: actions/checkout@v4 - - name: Send deployment notification - run: | - curl --location --request POST "66.94.98.186:8080/deploy?environment=devnet&service=xdc&version=${GITHUB_SHA}" + # TODO Uncomment until new devnet + # devnet_send_notification: + # runs-on: ubuntu-latest + # needs: devnet_terraform_apply + # if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/') + # steps: + # - uses: actions/checkout@v4 + # - name: Send deployment notification + # run: | + # curl --location --request POST "66.94.98.186:8080/deploy?environment=devnet&service=xdc&version=${GITHUB_SHA}" diff --git a/cicd/devnet/terraform/.env b/cicd/devnet/terraform/.env deleted file mode 100644 index 626762d2e6e3..000000000000 --- a/cicd/devnet/terraform/.env +++ /dev/null @@ -1,13 +0,0 @@ -log_level=2 - -# Ohio -us_east_2_start=11 -us_east_2_end=36 - -# Ireland -eu_west_1_start=37 -eu_west_1_end=62 - -# Sydney -ap_southeast_2_start=73 -ap_southeast_2_end=73 \ No newline at end of file diff --git a/cicd/devnet/terraform/iam.tf b/cicd/devnet/terraform/iam.tf deleted file mode 100644 index 5a9e984a3826..000000000000 --- a/cicd/devnet/terraform/iam.tf +++ /dev/null @@ -1,28 +0,0 @@ -# IAM policies -data "aws_iam_policy_document" "xdc_ecs_tasks_execution_role" { - statement { - actions = ["sts:AssumeRole"] - - principals { - type = "Service" - identifiers = ["ecs-tasks.amazonaws.com"] - } - } -} - -# Create the role -resource "aws_iam_role" "devnet_xdc_ecs_tasks_execution_role" { - name = "devnet-xdc-ecs-task-execution-role" - assume_role_policy = "${data.aws_iam_policy_document.xdc_ecs_tasks_execution_role.json}" -} - -# Attached the AWS managed policies to the new role -resource "aws_iam_role_policy_attachment" "devnet_xdc_ecs_tasks_execution_role" { - for_each = toset([ - "arn:aws:iam::aws:policy/AmazonElasticFileSystemClientFullAccess", - "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy", - "arn:aws:iam::aws:policy/AmazonElasticFileSystemsUtils" - ]) - role = aws_iam_role.devnet_xdc_ecs_tasks_execution_role.name - policy_arn = each.value -} diff --git a/cicd/devnet/terraform/main.tf b/cicd/devnet/terraform/main.tf deleted file mode 100644 index fc998b799ce7..000000000000 --- a/cicd/devnet/terraform/main.tf +++ /dev/null @@ -1,63 +0,0 @@ -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - } - } -} - -# Default -provider "aws" { - region = "us-east-1" -} - -provider "aws" { - alias = "us-east-2" - region = "us-east-2" -} - -module "us-east-2" { - source = "./module/region" - region = "us-east-2" - devnetNodeKeys = local.devnetNodeKeys["us-east-2"] - logLevel = local.logLevel - devnet_xdc_ecs_tasks_execution_role_arn = aws_iam_role.devnet_xdc_ecs_tasks_execution_role.arn - docker_tag = var.docker_tag - providers = { - aws = aws.us-east-2 - } -} - -provider "aws" { - alias = "eu-west-1" - region = "eu-west-1" -} - -module "eu-west-1" { - source = "./module/region" - region = "eu-west-1" - devnetNodeKeys = local.devnetNodeKeys["eu-west-1"] - logLevel = local.logLevel - devnet_xdc_ecs_tasks_execution_role_arn = aws_iam_role.devnet_xdc_ecs_tasks_execution_role.arn - docker_tag = var.docker_tag - providers = { - aws = aws.eu-west-1 - } -} - -provider "aws" { - alias = "ap-southeast-2" - region = "ap-southeast-2" -} - -module "ap-southeast-2" { - source = "./module/region" - region = "ap-southeast-2" - devnetNodeKeys = local.devnetNodeKeys["ap-southeast-2"] - logLevel = local.logLevel - devnet_xdc_ecs_tasks_execution_role_arn = aws_iam_role.devnet_xdc_ecs_tasks_execution_role.arn - docker_tag = var.docker_tag - providers = { - aws = aws.ap-southeast-2 - } -} diff --git a/cicd/devnet/terraform/module/region/container-definition.tpl b/cicd/devnet/terraform/module/region/container-definition.tpl deleted file mode 100644 index 008e98522ac1..000000000000 --- a/cicd/devnet/terraform/module/region/container-definition.tpl +++ /dev/null @@ -1,44 +0,0 @@ -[ - { - "name": "tfXdcNode", - "image": "xinfinorg/${image_environment}:${image_tag}", - "environment": [ - {"name": "PRIVATE_KEY", "value": "${private_key}"}, - {"name": "LOG_LEVEL", "value": "${log_level}"}, - {"name": "NODE_NAME", "value": "${node_name}"}, - {"name": "NETWORK", "value": "${chain_network}"} - ], - "essential": true, - "logConfiguration": { - "logDriver": "awslogs", - "options": { - "awslogs-group": "${cloudwatch_group}", - "awslogs-region": "${cloudwatch_region}", - "awslogs-stream-prefix": "ecs" - } - }, - "portMappings": [ - { - "hostPort": 8555, - "protocol": "tcp", - "containerPort": 8555 - }, - { - "hostPort": 8545, - "protocol": "tcp", - "containerPort": 8545 - }, - { - "hostPort": 30303, - "protocol": "tcp", - "containerPort": 30303 - } - ], - "mountPoints": [ - { - "containerPath": "/work/xdcchain", - "sourceVolume": "efs" - } - ] - } -] \ No newline at end of file diff --git a/cicd/devnet/terraform/module/region/ecs.tf b/cicd/devnet/terraform/module/region/ecs.tf deleted file mode 100644 index bd7a88b17aea..000000000000 --- a/cicd/devnet/terraform/module/region/ecs.tf +++ /dev/null @@ -1,96 +0,0 @@ -data template_file devnet_container_definition { - for_each = var.devnetNodeKeys - template = "${file("${path.module}/container-definition.tpl")}" - - vars = { - image_environment = "${lookup(each.value, "imageEnvironment", "devnet")}" - image_tag = "${lookup(each.value, "imageTag", var.docker_tag)}" - node_name = "${each.key}" - private_key = "${each.value.pk}" - cloudwatch_group = "tf-${each.key}" - cloudwatch_region = "${var.region}" - log_level = "${lookup(each.value, "logLevel", "${var.logLevel}")}" - chain_network = "devnet" - } -} - -resource "aws_ecs_task_definition" "devnet_task_definition_group" { - for_each = var.devnetNodeKeys - - family = "devnet-${each.key}" - requires_compatibilities = ["FARGATE"] - network_mode = "awsvpc" - container_definitions = data.template_file.devnet_container_definition[each.key].rendered - execution_role_arn = var.devnet_xdc_ecs_tasks_execution_role_arn - task_role_arn = var.devnet_xdc_ecs_tasks_execution_role_arn - - # New nodes will consume a lot more CPU usage than existing nodes. - # This is due to sync is resource heavy. Recommending set to below if doing sync: - # CPU = 2048, Memory = 4096 - # Please set it back to cpu 256 and memory of 2048 after sync is done to save the cost - # cpu = 256 - # memory = 2048 - cpu = 1024 - memory = 4096 - volume { - name = "efs" - - efs_volume_configuration { - file_system_id = aws_efs_file_system.devnet_efs[each.key].id - root_directory = "/" - transit_encryption = "ENABLED" - authorization_config { - access_point_id = aws_efs_access_point.devnet_efs_access_point[each.key].id - iam = "DISABLED" - } - } - } - - tags = { - Name = "TfDevnetEcs-${each.key}" - } -} - -data "aws_ecs_task_definition" "devnet_ecs_task_definition" { - for_each = var.devnetNodeKeys - task_definition = aws_ecs_task_definition.devnet_task_definition_group[each.key].family -} - -# ECS cluster -resource "aws_ecs_cluster" "devnet_ecs_cluster" { - name = "devnet-xdcnode-cluster" - tags = { - Name = "TfDevnetEcsCluster" - } -} - - -resource "aws_ecs_service" "devnet_ecs_service" { - for_each = var.enableFixedIp ? {} : var.devnetNodeKeys - name = "ecs-service-${each.key}" - cluster = aws_ecs_cluster.devnet_ecs_cluster.id - task_definition = "${aws_ecs_task_definition.devnet_task_definition_group[each.key].family}:${max(aws_ecs_task_definition.devnet_task_definition_group[each.key].revision, data.aws_ecs_task_definition.devnet_ecs_task_definition[each.key].revision)}" - launch_type = "FARGATE" - scheduling_strategy = "REPLICA" - desired_count = 1 - force_new_deployment = true - deployment_minimum_healthy_percent = 0 - deployment_maximum_percent = 100 - - network_configuration { - subnets = [aws_subnet.devnet_subnet.id] - assign_public_ip = true - security_groups = [ - aws_default_security_group.devnet_xdcnode_security_group.id - ] - } - - deployment_circuit_breaker { - enable = true - rollback = false - } - - tags = { - Name = "TfDevnetEcsService-${each.key}" - } -} \ No newline at end of file diff --git a/cicd/devnet/terraform/module/region/efs.tf b/cicd/devnet/terraform/module/region/efs.tf deleted file mode 100644 index d03adc85b77c..000000000000 --- a/cicd/devnet/terraform/module/region/efs.tf +++ /dev/null @@ -1,67 +0,0 @@ - -# EFS -resource "aws_security_group" "devnet_efs_security_group" { - name = "TfDevnetEfsSecurityGroup" - description = "Allow HTTP in and out of devnet EFS" - vpc_id = aws_vpc.devnet_vpc.id - - ingress { - from_port = 2049 - to_port = 2049 - protocol = "TCP" - security_groups = [aws_default_security_group.devnet_xdcnode_security_group.id] - } - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } - tags = { - Name = "TfDevnetEfs" - } -} - -resource "aws_efs_file_system" "devnet_efs" { - for_each = var.devnetNodeKeys - creation_token = "efs-${each.key}" - performance_mode = "generalPurpose" - throughput_mode = "bursting" - encrypted = "true" - lifecycle_policy { - transition_to_ia = "AFTER_30_DAYS" - } - tags = { - Name = "TfDevnetEfs${each.key}" - } - } - -resource "aws_efs_mount_target" "devnet_efs_efs_mount_target" { - for_each = var.devnetNodeKeys - file_system_id = aws_efs_file_system.devnet_efs[each.key].id - subnet_id = aws_subnet.devnet_subnet.id - security_groups = [aws_security_group.devnet_efs_security_group.id] -} - -resource "aws_efs_access_point" "devnet_efs_access_point" { - for_each = var.devnetNodeKeys - file_system_id = aws_efs_file_system.devnet_efs[each.key].id - root_directory { - path = "/${each.key}/database" - creation_info { - owner_gid = 1001 - owner_uid = 1001 - permissions = 777 - } - } - posix_user { - gid = 1001 - uid = 1001 - secondary_gids = [0] - } - - tags = { - Name = "TfDevnetEfsAccessPoint${each.key}" - } -} \ No newline at end of file diff --git a/cicd/devnet/terraform/module/region/main.tf b/cicd/devnet/terraform/module/region/main.tf deleted file mode 100644 index 10fc5b08e603..000000000000 --- a/cicd/devnet/terraform/module/region/main.tf +++ /dev/null @@ -1,102 +0,0 @@ -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - } - } -} - -resource "aws_vpc" "devnet_vpc" { - cidr_block = "10.0.0.0/16" - instance_tenancy = "default" - enable_dns_hostnames = true - - tags = { - Name = "TfDevnetVpc" - } -} - -resource "aws_subnet" "devnet_subnet" { - vpc_id = aws_vpc.devnet_vpc.id - cidr_block = "10.0.0.0/20" - map_public_ip_on_launch = true - - tags = { - Name = "TfDevnetVpcSubnet" - } -} - -resource "aws_internet_gateway" "devnet_gatewat" { - vpc_id = aws_vpc.devnet_vpc.id - - tags = { - Name = "TfDevnetGateway" - } -} - -resource "aws_route_table" "devnet_route_table" { - vpc_id = aws_vpc.devnet_vpc.id - - route { - cidr_block = "0.0.0.0/0" - gateway_id = aws_internet_gateway.devnet_gatewat.id - } - - tags = { - Name = "TfDevnetVpcRoutingTable" - } -} - -resource "aws_route_table_association" "devnet_route_table_association" { - subnet_id = aws_subnet.devnet_subnet.id - route_table_id = aws_route_table.devnet_route_table.id -} - -resource "aws_default_security_group" "devnet_xdcnode_security_group" { - vpc_id = aws_vpc.devnet_vpc.id - - ingress { - description = "listener port" - from_port = 30303 - to_port = 30303 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - description = "discovery port" - from_port = 30303 - to_port = 30303 - protocol = "udp" - cidr_blocks = ["0.0.0.0/0"] - } - - ingress { - description = "rpc port" - from_port = 8545 - to_port = 8545 - protocol = "tcp" - cidr_blocks = ["10.0.0.0/16"] - } - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } - tags = { - Name = "TfDevnetNode" - } -} - -# Logs -resource "aws_cloudwatch_log_group" "devnet_cloud_watch_group" { - for_each = var.devnetNodeKeys - - name = "tf-${each.key}" - retention_in_days = 14 # Logs are only kept for 14 days - tags = { - Name = "TfDevnetCloudWatchGroup${each.key}" - } -} \ No newline at end of file diff --git a/cicd/devnet/terraform/module/region/rpc.tf b/cicd/devnet/terraform/module/region/rpc.tf deleted file mode 100644 index 2ba07813c11f..000000000000 --- a/cicd/devnet/terraform/module/region/rpc.tf +++ /dev/null @@ -1,104 +0,0 @@ -# Allocate an Elastic IP for the NLB -resource "aws_eip" "nlb_eip" { - domain = "vpc" -} - - -# Create a Network Load Balancer -resource "aws_lb" "rpc_node_nlb" { - count = var.enableFixedIp ? 1 : 0 - name = "rpc-node-nlb" - load_balancer_type = "network" - - enable_deletion_protection = false - - subnet_mapping { - subnet_id = aws_subnet.devnet_subnet.id - allocation_id = aws_eip.nlb_eip.id - } -} - -# Listener and Target Group for the rpc node container -resource "aws_lb_target_group" "rpc_node_tg_8545" { - count = var.enableFixedIp ? 1 : 0 - name = "rpc-node-tg" - port = 8545 - protocol = "TCP" - vpc_id = aws_vpc.devnet_vpc.id - target_type = "ip" -} - -resource "aws_lb_listener" "rpc_node_listener_8545" { - count = var.enableFixedIp ? 1 : 0 - load_balancer_arn = aws_lb.rpc_node_nlb[0].arn - port = 8545 - protocol = "TCP" - - default_action { - type = "forward" - target_group_arn = aws_lb_target_group.rpc_node_tg_8545[0].arn - } -} - -resource "aws_ecs_service" "devnet_rpc_node_ecs_service" { - for_each = var.enableFixedIp ? var.devnetNodeKeys : {} - name = "ecs-service-${each.key}" - cluster = aws_ecs_cluster.devnet_ecs_cluster.id - task_definition = "${aws_ecs_task_definition.devnet_task_definition_group[each.key].family}:${max(aws_ecs_task_definition.devnet_task_definition_group[each.key].revision, data.aws_ecs_task_definition.devnet_ecs_task_definition[each.key].revision)}" - launch_type = "FARGATE" - scheduling_strategy = "REPLICA" - desired_count = 1 - force_new_deployment = true - deployment_minimum_healthy_percent = 0 - deployment_maximum_percent = 100 - - network_configuration { - subnets = [aws_subnet.devnet_subnet.id] - assign_public_ip = true - security_groups = [ - aws_default_security_group.devnet_xdcnode_security_group.id - ] - } - - deployment_circuit_breaker { - enable = true - rollback = false - } - - load_balancer { - target_group_arn = aws_lb_target_group.rpc_node_tg_8545[0].arn - container_name = "tfXdcNode" - container_port = 8545 - } - - depends_on = [ - aws_lb_listener.rpc_node_listener_8545 - ] - - tags = { - Name = "TfDevnetRpcNodeEcsService-${each.key}" - } -} - -# Target Group for port 30303 -resource "aws_lb_target_group" "rpc_node_tg_30303" { - count = var.enableFixedIp ? 1 : 0 - name = "rpc-node-tg-30303" - port = 30303 - protocol = "TCP" - vpc_id = aws_vpc.devnet_vpc.id - target_type = "ip" -} - -# Listener for port 30303 -resource "aws_lb_listener" "rpc_node_listener_30303" { - count = var.enableFixedIp ? 1 : 0 - load_balancer_arn = aws_lb.rpc_node_nlb[0].arn - port = 30303 - protocol = "TCP" - - default_action { - type = "forward" - target_group_arn = aws_lb_target_group.rpc_node_tg_30303[0].arn - } -} \ No newline at end of file diff --git a/cicd/devnet/terraform/module/region/variables.tf b/cicd/devnet/terraform/module/region/variables.tf deleted file mode 100644 index 00dcd2277cf4..000000000000 --- a/cicd/devnet/terraform/module/region/variables.tf +++ /dev/null @@ -1,31 +0,0 @@ -variable "region" { - description = "AWS region" - type = string -} - -variable "devnetNodeKeys" { - description = "each miner's key" - type = map -} - -variable "logLevel" { - description = "containers log level" - type = string -} - -variable "devnet_xdc_ecs_tasks_execution_role_arn" { - description = "aws iam role resource arn" - type = string -} - -variable "enableFixedIp" { - description = "a flag to indicate whether fixed ip should be associated to the nodes. This is used for RPC node" - type = bool - default = false -} - -variable docker_tag { - type = string - default = "latest" - description = "description" -} diff --git a/cicd/devnet/terraform/s3.tf b/cicd/devnet/terraform/s3.tf deleted file mode 100644 index cd30e5ce324a..000000000000 --- a/cicd/devnet/terraform/s3.tf +++ /dev/null @@ -1,13 +0,0 @@ -terraform { - backend "s3" { - bucket = "tf-xinfin-bucket" // This name need to be updated to be the same as local.s3BucketName. We can't use variable here. - key = "tf/terraform_devnet.tfstate" - region = "us-east-1" - encrypt = true - } -} - -data "aws_s3_object" "devnet_xdc_node_config" { - bucket = "tf-xinfin-bucket" - key = "node-config.json" -} diff --git a/cicd/devnet/terraform/variables.tf b/cicd/devnet/terraform/variables.tf deleted file mode 100644 index 12285d8ca76e..000000000000 --- a/cicd/devnet/terraform/variables.tf +++ /dev/null @@ -1,39 +0,0 @@ -variable docker_tag { - type = string - default = "latest" - description = "description" -} - -locals { - predefinedNodesConfig = jsondecode(data.aws_s3_object.devnet_xdc_node_config.body) - envs = { for tuple in regexall("(.*)=(.*)", file(".env")) : tuple[0] => tuple[1] } - logLevel = local.envs["log_level"] - - regions = [ - { - "name": "us-east-2", // Ohio - "start": local.envs["us_east_2_start"], - "end": local.envs["us_east_2_end"], - }, - { - "name": "eu-west-1", // Ireland - "start": local.envs["eu_west_1_start"], - "end": local.envs["eu_west_1_end"], - }, - { - "name": "ap-southeast-2", // Sydney - "start": local.envs["ap_southeast_2_start"], - "end": local.envs["ap_southeast_2_end"], - } - ] - - keyNames = { - for r in local.regions : - r.name => [for i in range(r.start, r.end+1) : "xdc${i}"] - } - - devnetNodeKeys = { - for r in local.regions : - r.name => { for i in local.keyNames[r.name]: i => local.predefinedNodesConfig[i] } - } -} From dd0fb11fa344136c8ec3df186f8334a5c9fceb31 Mon Sep 17 00:00:00 2001 From: benjamin202410 Date: Mon, 30 Dec 2024 18:31:47 -0800 Subject: [PATCH 464/479] reduce gas limit for devnet to 50M (#785) Co-authored-by: liam.lai --- cicd/devnet/start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cicd/devnet/start.sh b/cicd/devnet/start.sh index 8e52b1c92380..19250b52fcde 100755 --- a/cicd/devnet/start.sh +++ b/cicd/devnet/start.sh @@ -86,7 +86,7 @@ XDC --ethstats ${netstats} --gcmode archive \ --http-port $rpc_port \ --http-api db,eth,debug,net,shh,txpool,personal,web3,XDPoS \ --http-vhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \ ---miner-gasprice "1" --miner-gaslimit "420000000" --verbosity ${log_level} \ +--miner-gasprice "1" --miner-gaslimit "50000000" --verbosity ${log_level} \ --debugdatadir /work/xdcchain \ --store-reward \ --ws --ws-addr=0.0.0.0 --ws-port $ws_port \ From 44ac46f676c0c22967cdb088853dc389779f1a97 Mon Sep 17 00:00:00 2001 From: wanwiset25 Date: Wed, 26 Jun 2024 11:52:06 +0400 Subject: [PATCH 465/479] add local xdpos function add readme changes to support xdpos2 protocol from block 0 --- cicd/Dockerfile | 9 +- cicd/entry.sh | 2 +- cicd/local/README.md | 10 ++ cicd/local/start.sh | 83 +++++++++ cmd/puppeth/wizard_genesis.go | 12 +- common/constants/constants.go.local | 158 ++++++++++++++++++ consensus/XDPoS/engines/engine_v2/engine.go | 13 +- .../XDPoS/engines/engine_v2/epochSwitch.go | 20 +++ consensus/XDPoS/engines/engine_v2/snapshot.go | 4 + consensus/XDPoS/engines/engine_v2/timeout.go | 8 + consensus/XDPoS/engines/engine_v2/vote.go | 4 + eth/hooks/engine_v2_hooks.go | 9 + 12 files changed, 322 insertions(+), 10 deletions(-) create mode 100644 cicd/local/README.md create mode 100755 cicd/local/start.sh create mode 100644 common/constants/constants.go.local diff --git a/cicd/Dockerfile b/cicd/Dockerfile index 055492e548bd..c9f7de7cf6a2 100644 --- a/cicd/Dockerfile +++ b/cicd/Dockerfile @@ -5,11 +5,14 @@ RUN apk add make build-base linux-headers COPY . /builder RUN cd /builder && make && mv /builder/build/bin/XDC /builder/build/bin/XDC-mainnet +RUN mv /builder/common/constants/constants.go.testnet /builder/common/constants.go +RUN cd /builder && make && mv /builder/build/bin/XDC /builder/build/bin/XDC-testnet + RUN mv /builder/common/constants/constants.go.devnet /builder/common/constants.go RUN cd /builder && make && mv /builder/build/bin/XDC /builder/build/bin/XDC-devnet -RUN mv /builder/common/constants/constants.go.testnet /builder/common/constants.go -RUN cd /builder && make && mv /builder/build/bin/XDC /builder/build/bin/XDC-testnet +RUN mv /builder/common/constants/constants.go.local /builder/common/constants.go +RUN cd /builder && make && mv /builder/build/bin/XDC /builder/build/bin/XDC-local # The runtime image FROM alpine:3 @@ -18,11 +21,13 @@ WORKDIR /work RUN apk add --no-cache bash curl +COPY --from=builder /builder/build/bin/XDC-local /usr/bin COPY --from=builder /builder/build/bin/XDC-devnet /usr/bin COPY --from=builder /builder/build/bin/XDC-testnet /usr/bin COPY --from=builder /builder/build/bin/XDC-mainnet /usr/bin # # Copy over files +ADD cicd/local /work/local ADD cicd/devnet /work/devnet ADD cicd/testnet /work/testnet ADD cicd/mainnet /work/mainnet diff --git a/cicd/entry.sh b/cicd/entry.sh index 777bd58692e6..e7518fe9f603 100755 --- a/cicd/entry.sh +++ b/cicd/entry.sh @@ -1,7 +1,7 @@ #!/bin/bash if test -z "$NETWORK" then - echo "NETWORK env Must be set, mainnet/testnet/devnet" + echo "NETWORK env Must be set, mainnet/testnet/devnet/local" exit 1 fi diff --git a/cicd/local/README.md b/cicd/local/README.md new file mode 100644 index 000000000000..83d8dad0b179 --- /dev/null +++ b/cicd/local/README.md @@ -0,0 +1,10 @@ +To set up local xdpos you need pass env NETWORK=local and inject 2 files when starting the container +1. genesis.json - deploy to path "/work/genesis.json" in the container. + - Creating genesis.json using puppeth + 1. "make puppeth" from base repo directory + 2. run the binary (genesis wizard) "./build/bin/puppeth" + 3. the output genesis.json will be in your ~/.puppeth directory + +2. bootnodes.list - deploy to path "/work/bootnodes.list" in the container. + - check example bootnode format in cicd/devnet or cicd/testnet + - REQUIRES newline at the end of the file, or the last line won't read diff --git a/cicd/local/start.sh b/cicd/local/start.sh new file mode 100755 index 000000000000..cfd877de4066 --- /dev/null +++ b/cicd/local/start.sh @@ -0,0 +1,83 @@ +#!/bin/bash +if [ ! -d /work/xdcchain/XDC/chaindata ] +then + if test -z "$PRIVATE_KEY" + then + echo "PRIVATE_KEY environment variable has not been set." + exit 1 + fi + echo $PRIVATE_KEY >> /tmp/key + wallet=$(XDC account import --password .pwd --datadir /work/xdcchain /tmp/key | awk -F '[{}]' '{print $2}') + XDC --datadir /work/xdcchain init /work/genesis.json +else + wallet=$(XDC account list --datadir /work/xdcchain | head -n 1 | awk -F '[{}]' '{print $2}') +fi + +input="/work/bootnodes.list" +bootnodes="" +while IFS= read -r line +do + if [ -z "${bootnodes}" ] + then + bootnodes=$line + else + bootnodes="${bootnodes},$line" + fi +done < "$input" + +log_level=3 +if test -z "$LOG_LEVEL" +then + echo "Log level not set, default to verbosity of $log_level" +else + echo "Log level found, set to $LOG_LEVEL" + log_level=$LOG_LEVEL +fi + +port=30303 +if test -z "$PORT" +then + echo "PORT not set, default to $port" +else + echo "PORT found, set to $PORT" + port=$PORT +fi + +rpc_port=8545 +if test -z "$RPC_PORT" +then + echo "RPC_PORT not set, default to $rpc_port" +else + echo "RPC_PORT found, set to $RPC_PORT" + rpc_port=$RPC_PORT +fi + +ws_port=8555 +if test -z "$WS_PORT" +then + echo "WS_PORT not set, default to $ws_port" +else + echo "WS_PORT found, set to $WS_PORT" + ws_port=$WS_PORT +fi + +netstats="${NODE_NAME}-${wallet}:xinfin_xdpos_hybrid_network_stats@devnetstats.apothem.network:2000" + + +echo "Running a node with wallet: ${wallet}" +echo "Starting nodes with $bootnodes ..." + +# Note: --gcmode=archive means node will store all historical data. This will lead to high memory usage. But sync mode require archive to sync +# https://github.com/XinFinOrg/XDPoSChain/issues/268 + +XDC --ethstats ${netstats} --gcmode archive \ +--bootnodes ${bootnodes} --syncmode full \ +--datadir /work/xdcchain --networkid 551 \ +-port $port --rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 \ +--rpcport $rpc_port \ +--rpcapi db,eth,debug,net,shh,txpool,personal,web3,XDPoS \ +--rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \ +--gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \ +--debugdatadir /work/xdcchain \ +--ws --wsaddr=0.0.0.0 --wsport $ws_port \ +--wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log diff --git a/cmd/puppeth/wizard_genesis.go b/cmd/puppeth/wizard_genesis.go index d7a8f93cec02..af52abc8e459 100644 --- a/cmd/puppeth/wizard_genesis.go +++ b/cmd/puppeth/wizard_genesis.go @@ -51,11 +51,11 @@ func (w *wizard) makeGenesis() { Difficulty: big.NewInt(524288), Alloc: make(core.GenesisAlloc), Config: ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1), - EIP150Block: big.NewInt(2), - EIP155Block: big.NewInt(3), - EIP158Block: big.NewInt(3), - ByzantiumBlock: big.NewInt(4), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), }, } // Figure out which consensus engine to choose @@ -147,7 +147,7 @@ func (w *wizard) makeGenesis() { fmt.Println() fmt.Printf("Proportion of total masternodes v2 vote collection to generate a QC (float value), should be two thirds of masternodes? (default = %f)\n", 0.667) genesis.Config.XDPoS.V2.CurrentConfig.CertThreshold = w.readDefaultFloat(0.667) - + genesis.Config.XDPoS.V2.CurrentConfig.MaxMasternodes = 108 genesis.Config.XDPoS.V2.AllConfigs[0] = genesis.Config.XDPoS.V2.CurrentConfig fmt.Println() diff --git a/common/constants/constants.go.local b/common/constants/constants.go.local new file mode 100644 index 000000000000..6c5114313728 --- /dev/null +++ b/common/constants/constants.go.local @@ -0,0 +1,158 @@ +package common + +import ( + "math/big" +) + +const ( + RewardMasterPercent = 90 + RewardVoterPercent = 0 + RewardFoundationPercent = 10 + HexSignMethod = "e341eaa4" + HexSetSecret = "34d38600" + HexSetOpening = "e11f5ba2" + EpocBlockSecret = 800 + EpocBlockOpening = 850 + EpocBlockRandomize = 900 + MaxMasternodes = 18 + MaxMasternodesV2 = 108 // Last v1 masternodes + LimitPenaltyEpoch = 4 + LimitPenaltyEpochV2 = 0 + BlocksPerYearTest = uint64(200000) + BlocksPerYear = uint64(15768000) + LimitThresholdNonceInQueue = 10 + DefaultMinGasPrice = 250000000 + MergeSignRange = 15 + RangeReturnSigner = 150 + MinimunMinerBlockPerEpoch = 1 + + OneYear = uint64(365 * 86400) + LiquidateLendingTradeBlock = uint64(100) +) + +var Rewound = uint64(0) + +var TIP2019Block = big.NewInt(0) +var TIPSigning = big.NewInt(0) +var TIPRandomize = big.NewInt(0) + +var TIPV2SwitchBlock = big.NewInt(0) + +var TIPIncreaseMasternodes = big.NewInt(0) // Upgrade MN Count at Block. +var TIPNoHalvingMNReward = big.NewInt(0) // hardfork no halving masternodes reward +var BlackListHFNumber = uint64(0) +var TIPXDCX = big.NewInt(0) +var TIPXDCXLending = big.NewInt(0) +var TIPXDCXCancellationFee = big.NewInt(0) +var TIPXDCXCancellationFeeTestnet = big.NewInt(0) +var TIPXDCXMinerDisable = big.NewInt(0) +var TIPXDCXReceiverDisable = big.NewInt(0) +var BerlinBlock = big.NewInt(0) +var LondonBlock = big.NewInt(0) +var MergeBlock = big.NewInt(0) +var ShanghaiBlock = big.NewInt(0) +var Eip1559Block = big.NewInt(9999999999) + +var TIPXDCXTestnet = big.NewInt(0) +var IsTestnet bool = false +var Enable0xPrefix bool = true +var StoreRewardFolder string +var RollbackHash Hash +var BasePrice = big.NewInt(1000000000000000000) // 1 +var RelayerLockedFund = big.NewInt(20000) // 20000 XDC +var RelayerFee = big.NewInt(1000000000000000) // 0.001 +var XDCXBaseFee = big.NewInt(10000) // 1 / XDCXBaseFee +var RelayerCancelFee = big.NewInt(100000000000000) // 0.0001 +var XDCXBaseCancelFee = new(big.Int).Mul(XDCXBaseFee, big.NewInt(10)) // 1/ (XDCXBaseFee *10) +var RelayerLendingFee = big.NewInt(10000000000000000) // 0.01 +var RelayerLendingCancelFee = big.NewInt(1000000000000000) // 0.001 +var BaseLendingInterest = big.NewInt(100000000) // 1e8 + +var MinGasPrice = big.NewInt(DefaultMinGasPrice) +var RelayerRegistrationSMC = "0x16c63b79f9C8784168103C0b74E6A59EC2de4a02" +var RelayerRegistrationSMCTestnet = "0xA1996F69f47ba14Cb7f661010A7C31974277958c" +var LendingRegistrationSMC = "0x7d761afd7ff65a79e4173897594a194e3c506e57" +var LendingRegistrationSMCTestnet = "0x28d7fC2Cf5c18203aaCD7459EFC6Af0643C97bE8" +var TRC21IssuerSMCTestNet = HexToAddress("0x0E2C88753131CE01c7551B726b28BFD04e44003F") +var TRC21IssuerSMC = HexToAddress("0x8c0faeb5C6bEd2129b8674F262Fd45c4e9468bee") +var XDCXListingSMC = HexToAddress("0xDE34dD0f536170993E8CFF639DdFfCF1A85D3E53") +var XDCXListingSMCTestNet = HexToAddress("0x14B2Bf043b9c31827A472CE4F94294fE9a6277e0") +var TRC21GasPriceBefore = big.NewInt(2500) +var TRC21GasPrice = big.NewInt(250000000) +var RateTopUp = big.NewInt(90) // 90% +var BaseTopUp = big.NewInt(100) +var BaseRecall = big.NewInt(100) +var TIPTRC21Fee = big.NewInt(13523400) +var TIPTRC21FeeTestnet = big.NewInt(225000) +var BlockNumberGas50x = big.NewInt(11818181) +var LimitTimeFinality = uint64(30) // limit in 30 block + +var IgnoreSignerCheckBlockArray = map[uint64]bool{ + uint64(1032300): true, + uint64(1033200): true, + uint64(27307800): true, + uint64(28270800): true, +} +var Blacklist = map[Address]bool{ + HexToAddress("0x5248bfb72fd4f234e062d3e9bb76f08643004fcd"): true, + HexToAddress("0x5ac26105b35ea8935be382863a70281ec7a985e9"): true, + HexToAddress("0x09c4f991a41e7ca0645d7dfbfee160b55e562ea4"): true, + HexToAddress("0xb3157bbc5b401a45d6f60b106728bb82ebaa585b"): true, + HexToAddress("0x741277a8952128d5c2ffe0550f5001e4c8247674"): true, + HexToAddress("0x10ba49c1caa97d74b22b3e74493032b180cebe01"): true, + HexToAddress("0x07048d51d9e6179578a6e3b9ee28cdc183b865e4"): true, + HexToAddress("0x4b899001d73c7b4ec404a771d37d9be13b8983de"): true, + HexToAddress("0x85cb320a9007f26b7652c19a2a65db1da2d0016f"): true, + HexToAddress("0x06869dbd0e3a2ea37ddef832e20fa005c6f0ca39"): true, + HexToAddress("0x82e48bc7e2c93d89125428578fb405947764ad7c"): true, + HexToAddress("0x1f9a78534d61732367cbb43fc6c89266af67c989"): true, + HexToAddress("0x7c3b1fa91df55ff7af0cad9e0399384dc5c6641b"): true, + HexToAddress("0x5888dc1ceb0ff632713486b9418e59743af0fd20"): true, + HexToAddress("0xa512fa1c735fc3cc635624d591dd9ea1ce339ca5"): true, + HexToAddress("0x0832517654c7b7e36b1ef45d76de70326b09e2c7"): true, + HexToAddress("0xca14e3c4c78bafb60819a78ff6e6f0f709d2aea7"): true, + HexToAddress("0x652ce195a23035114849f7642b0e06647d13e57a"): true, + HexToAddress("0x29a79f00f16900999d61b6e171e44596af4fb5ae"): true, + HexToAddress("0xf9fd1c2b0af0d91b0b6754e55639e3f8478dd04a"): true, + HexToAddress("0xb835710c9901d5fe940ef1b99ed918902e293e35"): true, + HexToAddress("0x04dd29ce5c253377a9a3796103ea0d9a9e514153"): true, + HexToAddress("0x2b4b56846eaf05c1fd762b5e1ac802efd0ab871c"): true, + HexToAddress("0x1d1f909f6600b23ce05004f5500ab98564717996"): true, + HexToAddress("0x0dfdcebf80006dc9ab7aae8c216b51c6b6759e86"): true, + HexToAddress("0x2b373890a28e5e46197fbc04f303bbfdd344056f"): true, + HexToAddress("0xa8a3ef3dc5d8e36aee76f3671ec501ec31e28254"): true, + HexToAddress("0x4f3d18136fe2b5665c29bdaf74591fc6625ef427"): true, + HexToAddress("0x175d728b0e0f1facb5822a2e0c03bde93596e324"): true, + HexToAddress("0xd575c2611984fcd79513b80ab94f59dc5bab4916"): true, + HexToAddress("0x0579337873c97c4ba051310236ea847f5be41bc0"): true, + HexToAddress("0xed12a519cc15b286920fc15fd86106b3e6a16218"): true, + HexToAddress("0x492d26d852a0a0a2982bb40ec86fe394488c419e"): true, + HexToAddress("0xce5c7635d02dc4e1d6b46c256cae6323be294a32"): true, + HexToAddress("0x8b94db158b5e78a6c032c7e7c9423dec62c8b11c"): true, + HexToAddress("0x0e7c48c085b6b0aa7ca6e4cbcc8b9a92dc270eb4"): true, + HexToAddress("0x206e6508462033ef8425edc6c10789d241d49acb"): true, + HexToAddress("0x7710e7b7682f26cb5a1202e1cff094fbf7777758"): true, + HexToAddress("0xcb06f949313b46bbf53b8e6b2868a0c260ff9385"): true, + HexToAddress("0xf884e43533f61dc2997c0e19a6eff33481920c00"): true, + HexToAddress("0x8b635ef2e4c8fe21fc2bda027eb5f371d6aa2fc1"): true, + HexToAddress("0x10f01a27cf9b29d02ce53497312b96037357a361"): true, + HexToAddress("0x693dd49b0ed70f162d733cf20b6c43dc2a2b4d95"): true, + HexToAddress("0xe0bec72d1c2a7a7fb0532cdfac44ebab9f6f41ee"): true, + HexToAddress("0xc8793633a537938cb49cdbbffd45428f10e45b64"): true, + HexToAddress("0x0d07a6cbbe9fa5c4f154e5623bfe47fb4d857d8e"): true, + HexToAddress("0xd4080b289da95f70a586610c38268d8d4cf1e4c4"): true, + HexToAddress("0x8bcfb0caf41f0aa1b548cae76dcdd02e33866a1b"): true, + HexToAddress("0xabfef22b92366d3074676e77ea911ccaabfb64c1"): true, + HexToAddress("0xcc4df7a32faf3efba32c9688def5ccf9fefe443d"): true, + HexToAddress("0x7ec1e48a582475f5f2b7448a86c4ea7a26ea36f8"): true, + HexToAddress("0xe3de67289080f63b0c2612844256a25bb99ac0ad"): true, + HexToAddress("0x3ba623300cf9e48729039b3c9e0dee9b785d636e"): true, + HexToAddress("0x402f2cfc9c8942f5e7a12c70c625d07a5d52fe29"): true, + HexToAddress("0xd62358d42afbde095a4ca868581d85f9adcc3d61"): true, + HexToAddress("0x3969f86acb733526cd61e3c6e3b4660589f32bc6"): true, + HexToAddress("0x67615413d7cdadb2c435a946aec713a9a9794d39"): true, + HexToAddress("0xfe685f43acc62f92ab01a8da80d76455d39d3cb3"): true, + HexToAddress("0x3538a544021c07869c16b764424c5987409cba48"): true, + HexToAddress("0xe187cf86c2274b1f16e8225a7da9a75aba4f1f5f"): true, + HexToAddress("0x0000000000000000000000000000000000000011"): true, +} diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index bcbf4651cb9f..298b18114388 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -191,7 +191,10 @@ func (x *XDPoS_v2) initial(chain consensus.ChainReader, header *types.Header) er Signatures: nil, GapNumber: header.Number.Uint64() - x.config.Gap, } - + // prevent overflow + if header.Number.Uint64() < x.config.Gap { + quorumCert.GapNumber = 0 + } // can not call processQC because round is equal to default x.currentRound = 1 x.highestQuorumCert = quorumCert @@ -210,6 +213,10 @@ func (x *XDPoS_v2) initial(chain consensus.ChainReader, header *types.Header) er // Initial first v2 snapshot lastGapNum := x.config.V2.SwitchBlock.Uint64() - x.config.Gap + // prevent overflow + if x.config.V2.SwitchBlock.Uint64() < x.config.Gap { + lastGapNum = 0 + } lastGapHeader := chain.GetHeaderByNumber(lastGapNum) snap, _ := loadSnapshot(x.db, lastGapHeader.Hash()) @@ -846,6 +853,10 @@ func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert * } epochSwitchNumber := epochInfo.EpochSwitchBlockInfo.Number.Uint64() gapNumber := epochSwitchNumber - epochSwitchNumber%x.config.Epoch - x.config.Gap + // prevent overflow + if epochSwitchNumber-epochSwitchNumber%x.config.Epoch < x.config.Gap { + gapNumber = 0 + } if gapNumber != quorumCert.GapNumber { log.Error("[verifyQC] QC gap number mismatch", "epochSwitchNumber", epochSwitchNumber, "BlockNum", quorumCert.ProposedBlockInfo.Number, "BlockInfoHash", quorumCert.ProposedBlockInfo.Hash, "Gap", quorumCert.GapNumber, "GapShouldBe", gapNumber) return fmt.Errorf("gap number mismatch QC Gap %d, shouldBe %d", quorumCert.GapNumber, gapNumber) diff --git a/consensus/XDPoS/engines/engine_v2/epochSwitch.go b/consensus/XDPoS/engines/engine_v2/epochSwitch.go index 9928b88fb25a..5f9892e985ed 100644 --- a/consensus/XDPoS/engines/engine_v2/epochSwitch.go +++ b/consensus/XDPoS/engines/engine_v2/epochSwitch.go @@ -49,6 +49,26 @@ func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types } if isEpochSwitch { log.Debug("[getEpochSwitchInfo] header is epoch switch", "hash", hash.Hex(), "number", h.Number.Uint64()) + if h.Number.Uint64() == 0 { + log.Warn("[getEpochSwitchInfo] block 0, init epoch differently") + // handle genesis block differently as follows + masternodes := common.ExtractAddressFromBytes(h.Extra[32 : len(h.Extra)-65]) + penalties := []common.Address{} + standbynodes := []common.Address{} + epochSwitchInfo := &types.EpochSwitchInfo{ + Penalties: penalties, + Standbynodes: standbynodes, + Masternodes: masternodes, + MasternodesLen: len(masternodes), + EpochSwitchBlockInfo: &types.BlockInfo{ + Hash: hash, + Number: h.Number, + Round: 0, + }, + } + x.epochSwitches.Add(hash, epochSwitchInfo) + return epochSwitchInfo, nil + } quorumCert, round, masternodes, err := x.getExtraFields(h) if err != nil { log.Error("[getEpochSwitchInfo] get extra field", "err", err, "number", h.Number.Uint64()) diff --git a/consensus/XDPoS/engines/engine_v2/snapshot.go b/consensus/XDPoS/engines/engine_v2/snapshot.go index de4fb76d82bf..f0ce71a2d434 100644 --- a/consensus/XDPoS/engines/engine_v2/snapshot.go +++ b/consensus/XDPoS/engines/engine_v2/snapshot.go @@ -78,6 +78,10 @@ func (x *XDPoS_v2) getSnapshot(chain consensus.ChainReader, number uint64, isGap gapBlockNum = number } else { gapBlockNum = number - number%x.config.Epoch - x.config.Gap + //prevent overflow + if number-number%x.config.Epoch < x.config.Gap { + gapBlockNum = 0 + } } gapBlockHash := chain.GetHeaderByNumber(gapBlockNum).Hash() diff --git a/consensus/XDPoS/engines/engine_v2/timeout.go b/consensus/XDPoS/engines/engine_v2/timeout.go index 169e1db7bab0..ff689f45ef0c 100644 --- a/consensus/XDPoS/engines/engine_v2/timeout.go +++ b/consensus/XDPoS/engines/engine_v2/timeout.go @@ -231,6 +231,10 @@ func (x *XDPoS_v2) sendTimeout(chain consensus.ChainReader) error { // Notice this +1 is because we expect a block whos is the child of currentHeader currentNumber := currentBlockHeader.Number.Uint64() + 1 gapNumber = currentNumber - currentNumber%x.config.Epoch - x.config.Gap + // prevent overflow + if currentNumber-currentNumber%x.config.Epoch < x.config.Gap { + gapNumber = 0 + } log.Debug("[sendTimeout] is epoch switch when sending out timeout message", "currentNumber", currentNumber, "gapNumber", gapNumber) } else { epochSwitchInfo, err := x.getEpochSwitchInfo(chain, currentBlockHeader, currentBlockHeader.Hash()) @@ -239,6 +243,10 @@ func (x *XDPoS_v2) sendTimeout(chain consensus.ChainReader) error { return err } gapNumber = epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64() - epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64()%x.config.Epoch - x.config.Gap + // prevent overflow + if epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64()-epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64()%x.config.Epoch < x.config.Gap { + gapNumber = 0 + } log.Debug("[sendTimeout] non-epoch-switch block found its epoch block and calculated the gapNumber", "epochSwitchInfo.EpochSwitchBlockInfo.Number", epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64(), "gapNumber", gapNumber) } diff --git a/consensus/XDPoS/engines/engine_v2/vote.go b/consensus/XDPoS/engines/engine_v2/vote.go index f31e95c86284..675b0ca2c9ae 100644 --- a/consensus/XDPoS/engines/engine_v2/vote.go +++ b/consensus/XDPoS/engines/engine_v2/vote.go @@ -30,6 +30,10 @@ func (x *XDPoS_v2) sendVote(chainReader consensus.ChainReader, blockInfo *types. } epochSwitchNumber := epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64() gapNumber := epochSwitchNumber - epochSwitchNumber%x.config.Epoch - x.config.Gap + // prevent overflow + if epochSwitchNumber-epochSwitchNumber%x.config.Epoch < x.config.Gap { + gapNumber = 0 + } signedHash, err := x.signSignature(types.VoteSigHash(&types.VoteForSign{ ProposedBlockInfo: blockInfo, GapNumber: gapNumber, diff --git a/eth/hooks/engine_v2_hooks.go b/eth/hooks/engine_v2_hooks.go index bd5b0ffdd1df..e549802a100e 100644 --- a/eth/hooks/engine_v2_hooks.go +++ b/eth/hooks/engine_v2_hooks.go @@ -222,6 +222,11 @@ func GetSigningTxCount(c *XDPoS.XDPoS, chain consensus.ChainReader, header *type signers := make(map[common.Address]*contracts.RewardLog) mapBlkHash := map[uint64]common.Hash{} + // prevent overflow + if number == 0 { + return signers, nil + } + data := make(map[common.Hash][]common.Address) epochCount := 0 var masternodes []common.Address @@ -256,6 +261,10 @@ func GetSigningTxCount(c *XDPoS.XDPoS, chain consensus.ChainReader, header *type from := *tx.From() data[blkHash] = append(data[blkHash], from) } + // prevent overflow + if i == 0 { + return signers, nil + } } for i := startBlockNumber; i <= endBlockNumber; i++ { From 4859d5e7db63aeac6a3e18aa1f0d505d0ae18e70 Mon Sep 17 00:00:00 2001 From: wanwiset25 Date: Wed, 18 Dec 2024 14:36:23 +0700 Subject: [PATCH 466/479] new constants and genesis.json --- cicd/devnet/bootnodes.list | 4 +- cicd/devnet/genesis.json | 273 +++++++++++++++++++++------ cicd/entry.sh | 2 +- common/constants/constants.go.devnet | 36 ++-- 4 files changed, 232 insertions(+), 83 deletions(-) diff --git a/cicd/devnet/bootnodes.list b/cicd/devnet/bootnodes.list index 618052572b94..edffbabcf4a3 100644 --- a/cicd/devnet/bootnodes.list +++ b/cicd/devnet/bootnodes.list @@ -1,2 +1,2 @@ -enode://00d49d72a48164681906ad61924568da0d3049937efdbaed0b7533e34a99f55814f1839d909cdc82f78e04a36ac04737d80b41b22905c7d6cac3c80bb5cdbbc4@66.94.98.186:30301 -enode://d6793b02a478f13ed6d01c30778935f6f8f7461a75aebedcb310def4ed9b066f995a0dca046d0c7ea7f5ffdd8e3f1f53c6b6dce909d1693650504921aad62f1a@194.163.167.177:30301 +enode://c7a38ecc7fd5849eaefb3e928f882ab2f1260d5591bd1fef1db39c8ae0d9fe21d86e2d4efee6def0f4f16d0b6103f86f92f7e910295b849266fef83f05768596@10.244.1.77:30301 +enode://9160713d41656154ce3fdaa9d0f9799dd8546652b1784edecf82ce7a5b54a977f897ea617a9b3b4db513dc6b0dc98c1c3ad8a1914075ab87ded4e367c89dcd3b@10.244.2.53:30301 diff --git a/cicd/devnet/genesis.json b/cicd/devnet/genesis.json index 4ea005d6ea7e..b92159f33089 100644 --- a/cicd/devnet/genesis.json +++ b/cicd/devnet/genesis.json @@ -1,24 +1,47 @@ { "config": { "chainId": 551, - "homesteadBlock": 1, - "eip150Block": 2, + "homesteadBlock": 0, + "eip150Block": 0, "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "eip155Block": 3, - "eip158Block": 3, - "byzantiumBlock": 4, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, "XDPoS": { "period": 2, "epoch": 900, - "reward": 5000, + "reward": 10, "rewardCheckpoint": 900, "gap": 450, - "foudationWalletAddr": "0x746249c61f5832c5eed53172776b460491bdcd5c" + "foudationWalletAddr": "0xde5b54e8e7b585153add32f472e8d545e5d42a82", + "SkipV1Validation": false, + "v2": { + "switchBlock": 0, + "config": { + "maxMasternodes": 108, + "switchRound": 0, + "minePeriod": 2, + "timeoutSyncThreshold": 3, + "timeoutPeriod": 10, + "certificateThreshold": 0.667 + }, + "allConfigs": { + "0": { + "maxMasternodes": 108, + "switchRound": 0, + "minePeriod": 2, + "timeoutSyncThreshold": 3, + "timeoutPeriod": 10, + "certificateThreshold": 0.667 + } + }, + "SkipV2Validation": false + } } }, "nonce": "0x0", - "timestamp": "0x60c1b36a", - "extraData": "0x000000000000000000000000000000000000000000000000000000000000000037224936d88724f1de8686e52071d515b1af0eab52e78df27ca83be8716a77882da63a8dac227dac888c073313b36cf03cf1f739f39443551ff12bbe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "timestamp": "0x6771d3f2", + "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000311bdf9066246e68559816e7f636435867f824ef442a44a6fc20f5b8dfa8b304d7137581f7e6bef347318441696e9ae962633c16e04d53935272639d537fc89618edae86950e12687a612e15c4786b8473898cc3c5beca5841306b26fdb4e30411392a6a74826141342a4dc33a1045cefa4182b6212ec0317bc30fbeb7208192d1474b5f87ffbc056de43c11888c073313b36cf03cf1f739f39443551ff12bbe8d993351c0e2db739f9bcbfdeda94d73b50b16d3a242960b7ca1937e826bda6c397df74d9f9ab01aa89af636787499e81362e815e36f28763eac120babebf5a6cbe6113780cbe489e3eb0db882381aebaf81190100d82f41ad2c95898195c7a47dc59115bb5ec85408683795da2f604a5c0464868eabfcb6da489a1b4304f49aafaec938c7adc48539470624e1f9c75ce33e568d8fa3ace90497ee0c60dc921eefea93e384a6ccaaf28e33790a2d1b2625bf964dfc087e2622b02b0bb78713e872c02796ef64c8f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x47b760", "difficulty": "0x1", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -30,43 +53,151 @@ "0000000000000000000000000000000000000001": { "balance": "0x0" }, + "0000000000000000000000000000000000000068": { + "balance": "0xd3c21bcecceda10000000" + }, "0000000000000000000000000000000000000088": { "code": "0x606060405260043610610196576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063012679511461019b578063025e7c27146101c957806302aa9be21461022c57806306a49fce1461026e5780630db02622146102d85780630e3e4fb81461030157806315febd68146103715780632a3640b1146103a85780632d15cc041461042a5780632f9c4bba146104b8578063302b687214610522578063326586521461058e5780633477ee2e14610640578063441a3e70146106a357806358e7525f146106cf5780635b860d271461071c5780635b9cd8cc146107695780636dd7d8ea1461082457806372e44a3814610852578063a9a981a31461089f578063a9ff959e146108c8578063ae6e43f5146108f1578063b642facd1461092a578063c45607df146109a3578063d09f1ab4146109f0578063d161c76714610a19578063d51b9e9314610a42578063d55b7dff14610a93578063ef18374a14610abc578063f2ee3c7d14610ae5578063f5c9512514610b1e578063f8ac9dd514610b4c575b600080fd5b6101c7600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610b75565b005b34156101d457600080fd5b6101ea60048080359060200190919050506111fc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561023757600080fd5b61026c600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061123b565b005b341561027957600080fd5b610281611796565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156102c45780820151818401526020810190506102a9565b505050509050019250505060405180910390f35b34156102e357600080fd5b6102eb61182a565b6040518082815260200191505060405180910390f35b341561030c57600080fd5b610357600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611830565b604051808215151515815260200191505060405180910390f35b341561037c57600080fd5b610392600480803590602001909190505061185f565b6040518082815260200191505060405180910390f35b34156103b357600080fd5b6103e8600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506118bb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561043557600080fd5b610461600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611909565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156104a4578082015181840152602081019050610489565b505050509050019250505060405180910390f35b34156104c357600080fd5b6104cb6119dc565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561050e5780820151818401526020810190506104f3565b505050509050019250505060405180910390f35b341561052d57600080fd5b610578600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611a79565b6040518082815260200191505060405180910390f35b341561059957600080fd5b6105c5600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611b03565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156106055780820151818401526020810190506105ea565b50505050905090810190601f1680156106325780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561064b57600080fd5b6106616004808035906020019091905050611da2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156106ae57600080fd5b6106cd6004808035906020019091908035906020019091905050611de1565b005b34156106da57600080fd5b610706600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061208d565b6040518082815260200191505060405180910390f35b341561072757600080fd5b610753600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506120d9565b6040518082815260200191505060405180910390f35b341561077457600080fd5b6107a9600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506121a1565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156107e95780820151818401526020810190506107ce565b50505050905090810190601f1680156108165780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610850600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061226a565b005b341561085d57600080fd5b610889600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050612653565b6040518082815260200191505060405180910390f35b34156108aa57600080fd5b6108b261266b565b6040518082815260200191505060405180910390f35b34156108d357600080fd5b6108db612671565b6040518082815260200191505060405180910390f35b34156108fc57600080fd5b610928600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050612677565b005b341561093557600080fd5b610961600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050612c36565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156109ae57600080fd5b6109da600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050612ca2565b6040518082815260200191505060405180910390f35b34156109fb57600080fd5b610a03612cee565b6040518082815260200191505060405180910390f35b3415610a2457600080fd5b610a2c612cf4565b6040518082815260200191505060405180910390f35b3415610a4d57600080fd5b610a79600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050612cfa565b604051808215151515815260200191505060405180910390f35b3415610a9e57600080fd5b610aa6612d53565b6040518082815260200191505060405180910390f35b3415610ac757600080fd5b610acf612d59565b6040518082815260200191505060405180910390f35b3415610af057600080fd5b610b1c600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050612d63565b005b3415610b2957600080fd5b610b4a600480803590602001908201803590602001919091929050506134f1565b005b3415610b5757600080fd5b610b5f6135f0565b6040518082815260200191505060405180910390f35b6000600b543410151515610b8857600080fd5b6000600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002080549050141580610c1c57506000600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002080549050115b1515610c2757600080fd5b81600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160149054906101000a900460ff16151515610c8457600080fd5b610cd934600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101546135f690919063ffffffff16565b915060088054806001018281610cef919061362d565b9160005260206000209001600085909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506060604051908101604052803373ffffffffffffffffffffffffffffffffffffffff16815260200160011515815260200183815250600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160000160146101000a81548160ff02191690831515021790555060408201518160010155905050610eb834600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546135f690919063ffffffff16565b600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610f5160016009546135f690919063ffffffff16565b6009819055506000600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905014156110185760078054806001018281610fb6919061362d565b9160005260206000209001600033909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050600a600081548092919060010191905055505b600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054806001018281611069919061362d565b9160005260206000209001600085909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054806001018281611109919061362d565b9160005260206000209001600033909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550507f7635f1d87b47fba9f2b09e56eb4be75cca030e0cb179c1602ac9261d39a8f5c1338434604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001935050505060405180910390a1505050565b60078181548110151561120b57fe5b90600052602060002090016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000828280600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101515156112cd57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff16600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561140657600b546113f882600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461361490919063ffffffff16565b1015151561140557600080fd5b5b61145b84600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001015461361490919063ffffffff16565b600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018190555061153384600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461361490919063ffffffff16565b600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506115cb43600f546135f690919063ffffffff16565b9250611632846000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000868152602001908152602001600020546135f690919063ffffffff16565b6000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000858152602001908152602001600020819055506000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010180548060010182816116db9190613659565b9160005260206000209001600085909190915055507faa0e554f781c3c3b2be110a0557f260f11af9a8aa2c64bc1e7a31dbb21e32fa2338686604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001935050505060405180910390a15050505050565b61179e613685565b600880548060200260200160405190810160405280929190818152602001828054801561182057602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116117d6575b5050505050905090565b600a5481565b60056020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b60008060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000838152602001908152602001600020549050919050565b6006602052816000526040600020818154811015156118d657fe5b90600052602060002090016000915091509054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b611911613685565b600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156119d057602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311611986575b50505050509050919050565b6119e4613699565b6000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101805480602002602001604051908101604052809291908181526020018280548015611a6f57602002820191906000526020600020905b815481526020019060010190808311611a5b575b5050505050905090565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b611b0b6136ad565b611b1482612cfa565b15611c655760036000611b2684612c36565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600160036000611b6f86612c36565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905003815481101515611bba57fe5b90600052602060002090018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611c595780601f10611c2e57610100808354040283529160200191611c59565b820191906000526020600020905b815481529060010190602001808311611c3c57829003601f168201915b50505050509050611d9d565b600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905003815481101515611cf657fe5b90600052602060002090018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611d955780601f10611d6a57610100808354040283529160200191611d95565b820191906000526020600020905b815481529060010190602001808311611d7857829003601f168201915b505050505090505b919050565b600881815481101515611db157fe5b90600052602060002090016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008282600082111515611df457600080fd5b814310151515611e0357600080fd5b60008060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600084815260200190815260200160002054111515611e6457600080fd5b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010182815481101515611eb357fe5b906000526020600020900154141515611ecb57600080fd5b6000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160008681526020019081526020016000205492506000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000868152602001908152602001600020600090556000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010184815481101515611fc457fe5b9060005260206000209001600090553373ffffffffffffffffffffffffffffffffffffffff166108fc849081150290604051600060405180830381858888f19350505050151561201357600080fd5b7ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568338685604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001838152602001828152602001935050505060405180910390a15050505050565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101549050919050565b60008082600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160149054906101000a900460ff16151561213857600080fd5b61214184612c36565b915061214b612d59565b6064600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020540281151561219757fe5b0492505050919050565b6003602052816000526040600020818154811015156121bc57fe5b9060005260206000209001600091509150508054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156122625780601f1061223757610100808354040283529160200191612262565b820191906000526020600020905b81548152906001019060200180831161224557829003601f168201915b505050505081565b600c54341015151561227b57600080fd5b80600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160149054906101000a900460ff1615156122d757600080fd5b61232c34600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101546135f690919063ffffffff16565b600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101819055506000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054141561249b57600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480600101828161244b919061362d565b9160005260206000209001600033909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505b61252d34600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546135f690919063ffffffff16565b600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055507f66a9138482c99e9baf08860110ef332cc0c23b4a199a53593d8db0fc8f96fbfc338334604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001935050505060405180910390a15050565b60046020528060005260406000206000915090505481565b60095481565b600f5481565b6000806000833373ffffffffffffffffffffffffffffffffffffffff16600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561271957600080fd5b84600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160149054906101000a900460ff16151561277557600080fd5b6000600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160146101000a81548160ff0219169083151502179055506127e6600160095461361490919063ffffffff16565b600981905550600094505b6008805490508510156128bb578573ffffffffffffffffffffffffffffffffffffffff1660088681548110151561282457fe5b906000526020600020900160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156128ae5760088581548110151561287b57fe5b906000526020600020900160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556128bb565b84806001019550506127f1565b600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054935061299284600160008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001015461361490919063ffffffff16565b600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101819055506000600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550612a7243600e546135f690919063ffffffff16565b9250612ad9846000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000868152602001908152602001600020546135f690919063ffffffff16565b6000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000858152602001908152602001600020819055506000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018054806001018281612b829190613659565b9160005260206000209001600085909190915055507f4edf3e325d0063213a39f9085522994a1c44bea5f39e7d63ef61260a1e58c6d33387604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a1505050505050565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805490509050919050565b600d5481565b600e5481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160149054906101000a900460ff169050919050565b600b5481565b6000600a54905090565b600080612d6e613685565b600080600033600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160149054906101000a900460ff161515612dcf57600080fd5b87600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160149054906101000a900460ff161515612e2b57600080fd5b612e3433612c36565b9750612e3f89612c36565b9650600560008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515612ed757600080fd5b6001600560008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600460008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550604b612fc4612d59565b6064600460008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020540281151561301057fe5b041015156134e65760016008805490500360405180591061302e5750595b9080825280602002602001820160405250955060009450600093505b600880549050841015613357578673ffffffffffffffffffffffffffffffffffffffff166130b160088681548110151561308057fe5b906000526020600020900160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16612c36565b73ffffffffffffffffffffffffffffffffffffffff16141561334a576130e3600160095461361490919063ffffffff16565b6009819055506008848154811015156130f857fe5b906000526020600020900160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16868680600101975081518110151561313857fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505060088481548110151561318357fe5b906000526020600020900160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600160006008868154811015156131c457fe5b906000526020600020900160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600080820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556000820160146101000a81549060ff021916905560018201600090555050600360008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006132bb91906136c1565b600660008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600061330691906136e2565b600460008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600090555b838060010194505061304a565b600092505b600780549050831015613439578673ffffffffffffffffffffffffffffffffffffffff1660078481548110151561338f57fe5b906000526020600020900160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561342c576007838154811015156133e657fe5b906000526020600020900160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600a6000815480929190600190039190505550613439565b828060010193505061335c565b7fe18d61a5bf4aa2ab40afc88aa9039d27ae17ff4ec1c65f5f414df6f02ce8b35e8787604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019060200280838360005b838110156134d15780820151818401526020810190506134b6565b50505050905001935050505060405180910390a15b505050505050505050565b600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002080548060010182816135429190613703565b91600052602060002090016000848490919290919250919061356592919061372f565b50507f949360d814b28a3b393a68909efe1fee120ee09cac30f360a0f80ab5415a611a338383604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001806020018281038252848482818152602001925080828437820191505094505050505060405180910390a15050565b600c5481565b600080828401905083811015151561360a57fe5b8091505092915050565b600082821115151561362257fe5b818303905092915050565b8154818355818115116136545781836000526020600020918201910161365391906137af565b5b505050565b8154818355818115116136805781836000526020600020918201910161367f91906137af565b5b505050565b602060405190810160405280600081525090565b602060405190810160405280600081525090565b602060405190810160405280600081525090565b50805460008255906000526020600020908101906136df91906137d4565b50565b508054600082559060005260206000209081019061370091906137af565b50565b81548183558181151161372a5781836000526020600020918201910161372991906137d4565b5b505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061377057803560ff191683800117855561379e565b8280016001018555821561379e579182015b8281111561379d578235825591602001919060010190613782565b5b5090506137ab91906137af565b5090565b6137d191905b808211156137cd5760008160009055506001016137b5565b5090565b90565b6137fd91905b808211156137f957600081816137f09190613800565b506001016137da565b5090565b90565b50805460018160011615610100020316600290046000825580601f106138265750613845565b601f01602090049060005260206000209081019061384491906137af565b5b505600a165627a7a72305820f5bbb127b52ce86c873faef85cff176563476a5e49a3d88eaa9a06a8f432c9080029", "storage": { "0x0000000000000000000000000000000000000000000000000000000000000007": "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000008": "0x0000000000000000000000000000000000000000000000000000000000000003", - "0x0000000000000000000000000000000000000000000000000000000000000009": "0x0000000000000000000000000000000000000000000000000000000000000003", + "0x0000000000000000000000000000000000000000000000000000000000000008": "0x0000000000000000000000000000000000000000000000000000000000000012", + "0x0000000000000000000000000000000000000000000000000000000000000009": "0x0000000000000000000000000000000000000000000000000000000000000012", "0x000000000000000000000000000000000000000000000000000000000000000a": "0x0000000000000000000000000000000000000000000000000000000000000001", "0x000000000000000000000000000000000000000000000000000000000000000b": "0x000000000000000000000000000000000000000000084595161401484a000000", "0x000000000000000000000000000000000000000000000000000000000000000c": "0x00000000000000000000000000000000000000000000054b40b1f852bda00000", "0x000000000000000000000000000000000000000000000000000000000000000d": "0x0000000000000000000000000000000000000000000000000000000000000012", "0x000000000000000000000000000000000000000000000000000000000000000e": "0x000000000000000000000000000000000000000000000000000000000013c680", "0x000000000000000000000000000000000000000000000000000000000000000f": "0x0000000000000000000000000000000000000000000000000000000000069780", - "0x0cca6b5183395dc86a20cb86687ed86453de376e4e17bed8966e42a889c8725d": "0x0000000000000000000000000000000000000000000000000000000000000003", - "0x334b7f9d5f3d799751f76e65d323e6c91276efb6e64b0446a088d2fcf516b440": "0x000000000000000000000000000000000000000000084595161401484a000000", - "0x3657f70c5cac69bc1db087212204978b5e36fb39f142fc384dfbf897f13145e3": "0x000000000000000000000000a12a44433e64b76a9e388d87af02ce80580cf1f8", - "0x395dbd86e0de75e9c4a1fb35427f914ce460476379d076bd1b537fb4a716bab0": "0x000000000000000000000000000000000000000000084595161401484a000000", - "0x555ff3aa35f12513cb7de85980072f5394cf6321406b8b9b091f8a2974b83943": "0x00000000000000000000000037224936d88724f1de8686e52071d515b1af0eab", - "0x555ff3aa35f12513cb7de85980072f5394cf6321406b8b9b091f8a2974b83944": "0x00000000000000000000000052e78df27ca83be8716a77882da63a8dac227dac", - "0x555ff3aa35f12513cb7de85980072f5394cf6321406b8b9b091f8a2974b83945": "0x000000000000000000000000888c073313b36cf03cf1f739f39443551ff12bbe", - "0x6723efa5b718fd93a9c3fe445b0ce1acaeb21b7b0687a7a4586def1f3c294770": "0x000000000000000000000000000000000000000000084595161401484a000000", - "0x832066899179d47432d68c70d433422b9ba89f4037ef6cf4bc0b29aac1db8ae5": "0x000000000000000000000001a12a44433e64b76a9e388d87af02ce80580cf1f8", - "0x832066899179d47432d68c70d433422b9ba89f4037ef6cf4bc0b29aac1db8ae6": "0x000000000000000000000000000000000000000000084595161401484a000000", + "0x030c4482e31df0bec3a61ba9044822c841d3be0a21a4a43d22ffabb19a238030": "0x000000000000000000000000de5b54e8e7b585153add32f472e8d545e5d42a82", + "0x03857c1c3036c8068aecc274ccdef26c09d6bc8d7323c3b0af04944281aea3c5": "0x000000000000000000000000000000000000000000084595161401484a000000", + "0x04606a03b4103377c3cc40f7d070c9672b8c34f833337c95acd6cc3b9ef873be": "0x000000000000000000000001de5b54e8e7b585153add32f472e8d545e5d42a82", + "0x04606a03b4103377c3cc40f7d070c9672b8c34f833337c95acd6cc3b9ef873bf": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0x0f0a4bf138332e4dee2840995f18ef3f4e7ce9800a43298cd303f652f37e2760": "0x000000000000000000000000000000000000000000084595161401484a000000", + "0x0f77b98b641f695810840536460aceac3aa93db4b69360cb1e825fc50a01cbf8": "0x000000000000000000000000000000000000000000084595161401484a000000", + "0x125b08b20a070fdff51220f22ab5783fe8553385c29d73595065987e274ae2ba": "0x000000000000000000000001de5b54e8e7b585153add32f472e8d545e5d42a82", + "0x125b08b20a070fdff51220f22ab5783fe8553385c29d73595065987e274ae2bb": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0x141b2f299c67c202a3f56d8c761ef8412df00a99dc4d7df7132fea8b618710fb": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x15e82e9588701aace370d9b0ae88a15878117328c72679be3a43f1fde5480b52": "0x000000000000000000000000de5b54e8e7b585153add32f472e8d545e5d42a82", + "0x19474e4e22de9b7e74a0e935e50e07474f1922fca9ca508d627a1753b89a04c0": "0x000000000000000000000000de5b54e8e7b585153add32f472e8d545e5d42a82", + "0x1a53676945466c0303c4763ecd9fe35b640c62214817fc1cd445cd2df311d75e": "0x000000000000000000000000000000000000000000084595161401484a000000", + "0x1acfc824f6e62266e1ddfd5296eb428cb6235e11eb419042440ee2cd234f5064": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x206df8c61c26c41c2f5d8628f9b0af634f51cea5204c2860beac8743b789d8f9": "0x000000000000000000000000000000000000000000084595161401484a000000", + "0x213528a16906ba2c7ae2d13a88332596a8f7dba04445edce4dd35851adc6270b": "0x000000000000000000000000de5b54e8e7b585153add32f472e8d545e5d42a82", + "0x217e08ed8a88d1aa61780ebbfa2433d8ee93fe281a28caa61609ffd7feb6ff48": "0x000000000000000000000001de5b54e8e7b585153add32f472e8d545e5d42a82", + "0x217e08ed8a88d1aa61780ebbfa2433d8ee93fe281a28caa61609ffd7feb6ff49": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0x2bbcd29309cdf0c2e0fb87eb0912eda2715ff0d0800356af3b13b1877fc96294": "0x000000000000000000000000de5b54e8e7b585153add32f472e8d545e5d42a82", + "0x2ea8dbac375e20907f0f6064477fb65f425b25387bc4af4a9fa0d909542ad6ec": "0x000000000000000000000000de5b54e8e7b585153add32f472e8d545e5d42a82", + "0x2f46cce9be56b195a997053b94333e3e8608e77a9049008a7867ece9434d7490": "0x000000000000000000000000000000000000000000084595161401484a000000", + "0x32f30f989a9121096e27b0ceff2143dfd8ad0a279e39b2dbdd4720d758b1235e": "0x000000000000000000000000de5b54e8e7b585153add32f472e8d545e5d42a82", + "0x334998a4166b71bbee90c9a5395d45b437458344b336983ce195ac15c4cabf41": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x37bbbe931ce8669c7360584cc0c8719480098d88b382fa5147ab6070f6a995e9": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x383c8c93b8864dd3dd80dcce451bff3ccecfe0ef316daeffc3429e8c734ca219": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x424c760de55550140740213338215fb5e72732d77ff28c6fcdce25cc0da4b4df": "0x000000000000000000000000000000000000000000084595161401484a000000", + "0x42b4293dae5548c9e2c405a2d603c1b7c254591f10e1a19d475c9573a806ddbf": "0x000000000000000000000000000000000000000000084595161401484a000000", + "0x4315dd31061c8b8e85c5b7d9df3c6dda7f6e48ad32c7bd25549115df18f68eb7": "0x000000000000000000000000000000000000000000084595161401484a000000", + "0x433573bf8c109958dcd2a79e075574b3fa76ee1309194d6985ec9eb993cfc4a8": "0x000000000000000000000001de5b54e8e7b585153add32f472e8d545e5d42a82", + "0x433573bf8c109958dcd2a79e075574b3fa76ee1309194d6985ec9eb993cfc4a9": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0x471f33d3408681a476d422388b3e426fc8fd1f4db2b3cf07127fc0430da13d5a": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x48ddfeab6816c7d8ea504eaceba35584eed68f73e8bca46561c49f828945d870": "0x000000000000000000000000de5b54e8e7b585153add32f472e8d545e5d42a82", + "0x4bcc08cdec386d590e3c1b686244142bf6e504749f55207aea8cba6292b4a05c": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x4d08b2b0194071b50be4200cf623b02a2489a2090e82dca38562a22eab930991": "0x000000000000000000000000311bdf9066246e68559816e7f636435867f824ef", + "0x4d08b2b0194071b50be4200cf623b02a2489a2090e82dca38562a22eab930992": "0x000000000000000000000000442a44a6fc20f5b8dfa8b304d7137581f7e6bef3", + "0x4d08b2b0194071b50be4200cf623b02a2489a2090e82dca38562a22eab930993": "0x00000000000000000000000047318441696e9ae962633c16e04d53935272639d", + "0x4d08b2b0194071b50be4200cf623b02a2489a2090e82dca38562a22eab930994": "0x000000000000000000000000537fc89618edae86950e12687a612e15c4786b84", + "0x4d08b2b0194071b50be4200cf623b02a2489a2090e82dca38562a22eab930995": "0x00000000000000000000000073898cc3c5beca5841306b26fdb4e30411392a6a", + "0x4d08b2b0194071b50be4200cf623b02a2489a2090e82dca38562a22eab930996": "0x00000000000000000000000074826141342a4dc33a1045cefa4182b6212ec031", + "0x4d08b2b0194071b50be4200cf623b02a2489a2090e82dca38562a22eab930997": "0x0000000000000000000000007bc30fbeb7208192d1474b5f87ffbc056de43c11", + "0x4d08b2b0194071b50be4200cf623b02a2489a2090e82dca38562a22eab930998": "0x000000000000000000000000888c073313b36cf03cf1f739f39443551ff12bbe", + "0x4d08b2b0194071b50be4200cf623b02a2489a2090e82dca38562a22eab930999": "0x0000000000000000000000008d993351c0e2db739f9bcbfdeda94d73b50b16d3", + "0x4d08b2b0194071b50be4200cf623b02a2489a2090e82dca38562a22eab93099a": "0x000000000000000000000000a242960b7ca1937e826bda6c397df74d9f9ab01a", + "0x4d08b2b0194071b50be4200cf623b02a2489a2090e82dca38562a22eab93099b": "0x000000000000000000000000a89af636787499e81362e815e36f28763eac120b", + "0x4d08b2b0194071b50be4200cf623b02a2489a2090e82dca38562a22eab93099c": "0x000000000000000000000000abebf5a6cbe6113780cbe489e3eb0db882381aeb", + "0x4d08b2b0194071b50be4200cf623b02a2489a2090e82dca38562a22eab93099d": "0x000000000000000000000000af81190100d82f41ad2c95898195c7a47dc59115", + "0x4d08b2b0194071b50be4200cf623b02a2489a2090e82dca38562a22eab93099e": "0x000000000000000000000000bb5ec85408683795da2f604a5c0464868eabfcb6", + "0x4d08b2b0194071b50be4200cf623b02a2489a2090e82dca38562a22eab93099f": "0x000000000000000000000000da489a1b4304f49aafaec938c7adc48539470624", + "0x4d08b2b0194071b50be4200cf623b02a2489a2090e82dca38562a22eab9309a0": "0x000000000000000000000000e1f9c75ce33e568d8fa3ace90497ee0c60dc921e", + "0x4d08b2b0194071b50be4200cf623b02a2489a2090e82dca38562a22eab9309a1": "0x000000000000000000000000efea93e384a6ccaaf28e33790a2d1b2625bf964d", + "0x4d08b2b0194071b50be4200cf623b02a2489a2090e82dca38562a22eab9309a2": "0x000000000000000000000000fc087e2622b02b0bb78713e872c02796ef64c8f1", + "0x4df12e1188de9c93b847240eddba997020037c5f8010088c71c6f497ea6865ab": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x5479b436a6706e20dbebc992f8b75e09f5ef33b9bfc92f6eb52af893bc9f3ef5": "0x000000000000000000000000000000000000000000084595161401484a000000", + "0x5a72b959cac93391bff267c6c91f4da4895e3f8769bb512241703807da099182": "0x000000000000000000000000000000000000000000084595161401484a000000", + "0x6528b6895a372dd9abf5c9b1e2d763877056e06dcadeefb947482c05a58f1a46": "0x000000000000000000000001de5b54e8e7b585153add32f472e8d545e5d42a82", + "0x6528b6895a372dd9abf5c9b1e2d763877056e06dcadeefb947482c05a58f1a47": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0x690ae50193386ef0c1f3aecb62d156ec48e51576b8faca760d2e782148d2685a": "0x000000000000000000000000de5b54e8e7b585153add32f472e8d545e5d42a82", + "0x69dc30401af3a46fb5caf938fefd3bb163e08eed5018359c7ce99b0a6ac5bcad": "0x000000000000000000000000000000000000000000084595161401484a000000", + "0x6f24e27f6bf706ecff22e1cc7232c2b995103d66093d2976eeaa6ced126c7cf3": "0x000000000000000000000000000000000000000000084595161401484a000000", + "0x724b5bbb48fc97209628eb6b34adb8dd3cd583180ac721afdf706c4c57bbc253": "0x0000000000000000000000000000000000000000000000000000000000000012", + "0x76dfe65765497bb6461c122d3715541832ddc6bed4af9859d0afcafd98e85d3d": "0x000000000000000000000000000000000000000000084595161401484a000000", + "0x78131ffee5eb0b7466641acad70a5e37f247a658acd6910a0e0159013272500f": "0x000000000000000000000001de5b54e8e7b585153add32f472e8d545e5d42a82", + "0x78131ffee5eb0b7466641acad70a5e37f247a658acd6910a0e01590132725010": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0x7860877d0fdf5b8349b69cf151013101002a515fc77b7d9ac9deea724189b49d": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x78f000d26dc5cb7bb6625ad3bbdeb559c2237595558ca062c170e72b826dcaa8": "0x000000000000000000000000000000000000000000084595161401484a000000", + "0x7c5e55320fbb9389307827678ad755ae9bbff14bc2f0b7311e273ea6f816b5e6": "0x000000000000000000000000de5b54e8e7b585153add32f472e8d545e5d42a82", + "0x850302252de9a45ae306fefaef8c0b466423440d4916233feb535a91f5c13884": "0x000000000000000000000001de5b54e8e7b585153add32f472e8d545e5d42a82", + "0x850302252de9a45ae306fefaef8c0b466423440d4916233feb535a91f5c13885": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0x8f6c89a2950c399a6bd26ec8e4f8342cef555205270052553492ac8caab48637": "0x0000000000000000000000000000000000000000000000000000000000000001", "0x92174186dda351528a078fccba50a5ff5332041fd6f861d5ba9f30d9e7416b01": "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x9d721527fb4f716033fd64eb5c53e9ebfdcbded3e02421855e691c06bc3d0cda": "0x0000000000000000000000000000000000000000000000000000000000000001", - "0xa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688": "0x000000000000000000000000a12a44433e64b76a9e388d87af02ce80580cf1f8", - "0xadf2e2b096791f125211938ba4199c02152b91d8761229ae36342002e463de86": "0x000000000000000000000001a12a44433e64b76a9e388d87af02ce80580cf1f8", - "0xadf2e2b096791f125211938ba4199c02152b91d8761229ae36342002e463de87": "0x000000000000000000000000000000000000000000084595161401484a000000", - "0xbe5c577780cc5cb207d59bca6035e982dd13627ef17edb6a67300e707c7ab194": "0x000000000000000000000001a12a44433e64b76a9e388d87af02ce80580cf1f8", - "0xbe5c577780cc5cb207d59bca6035e982dd13627ef17edb6a67300e707c7ab195": "0x000000000000000000000000000000000000000000084595161401484a000000", - "0xd6e5468fdf7c0709dc80d32848fc6c62d31a193138d0962e6edc92694d422212": "0x000000000000000000000000a12a44433e64b76a9e388d87af02ce80580cf1f8", - "0xdbb72142c0e7c59d0ee1992f539a9bc4a22703f549c839728ce22c2c8ee01113": "0x0000000000000000000000000000000000000000000000000000000000000001", - "0xe810e986b7d9bc3e7cd3c6ce464ba1fc6c80bec03a51e6e041228d5df84e0121": "0x000000000000000000000000a12a44433e64b76a9e388d87af02ce80580cf1f8", - "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3": "0x00000000000000000000000037224936d88724f1de8686e52071d515b1af0eab", - "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee4": "0x00000000000000000000000052e78df27ca83be8716a77882da63a8dac227dac", - "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee5": "0x000000000000000000000000888c073313b36cf03cf1f739f39443551ff12bbe" + "0x927695b4feff242034e5b416f50ee8f8804c7c79d682e9b504e1d7ad0376d415": "0x000000000000000000000000de5b54e8e7b585153add32f472e8d545e5d42a82", + "0x9428e90ccbc254697b04337e1bfa9ed42a163a0530cac6ed589ff0e86db639ff": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x979ead4e6ad3f623d00d4c31505cce1a654a543281a073df41b8429781a4700a": "0x000000000000000000000001de5b54e8e7b585153add32f472e8d545e5d42a82", + "0x979ead4e6ad3f623d00d4c31505cce1a654a543281a073df41b8429781a4700b": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xa0654653e606a2b16e75ace42178d92a333f7bd8fd554600cd8c0dda8959a747": "0x000000000000000000000001de5b54e8e7b585153add32f472e8d545e5d42a82", + "0xa0654653e606a2b16e75ace42178d92a333f7bd8fd554600cd8c0dda8959a748": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xa16feed71a191c25a16b840899c1130128bae680b9fb5afedfad18ee25baa3cc": "0x000000000000000000000001de5b54e8e7b585153add32f472e8d545e5d42a82", + "0xa16feed71a191c25a16b840899c1130128bae680b9fb5afedfad18ee25baa3cd": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xa40e19ad57e57cf5498a358191ef21a46727990f0833f60ef287bb2512f473b2": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0xa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688": "0x000000000000000000000000de5b54e8e7b585153add32f472e8d545e5d42a82", + "0xadd3379ca12eb6e907655b06c632df1dfb1d3810478f04657d0eb1f2514ada7d": "0x000000000000000000000000000000000000000000084595161401484a000000", + "0xb07bd76227cbb5a5e93cd5635ab731faceb120715f763221afde9bf9c66d93e1": "0x000000000000000000000000de5b54e8e7b585153add32f472e8d545e5d42a82", + "0xb7a1ebae33d313df562db52e6a17cc25a4fc71072bc8f38e28cca51dca772d2f": "0x000000000000000000000001de5b54e8e7b585153add32f472e8d545e5d42a82", + "0xb7a1ebae33d313df562db52e6a17cc25a4fc71072bc8f38e28cca51dca772d30": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xbd072001a6fd52f3f36f5c32bd3ba9c4ffd3e427ab5661fb687b4b37342e32da": "0x000000000000000000000000de5b54e8e7b585153add32f472e8d545e5d42a82", + "0xbe19bdb2daf84051554e5f7b28c240f2394dfdc01ed9931b1d2270301ce6197a": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0xbe5c577780cc5cb207d59bca6035e982dd13627ef17edb6a67300e707c7ab194": "0x000000000000000000000001de5b54e8e7b585153add32f472e8d545e5d42a82", + "0xbe5c577780cc5cb207d59bca6035e982dd13627ef17edb6a67300e707c7ab195": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xd6b02d8d4bab22276f46bd01c273377c92e6ebf28cab145c363c2ad13651ab01": "0x000000000000000000000001de5b54e8e7b585153add32f472e8d545e5d42a82", + "0xd6b02d8d4bab22276f46bd01c273377c92e6ebf28cab145c363c2ad13651ab02": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xd6e5468fdf7c0709dc80d32848fc6c62d31a193138d0962e6edc92694d422212": "0x000000000000000000000000de5b54e8e7b585153add32f472e8d545e5d42a82", + "0xd71673afb2d4fa2bfb83c3892943b95ad4701e2fd78801199c07446743897d39": "0x000000000000000000000001de5b54e8e7b585153add32f472e8d545e5d42a82", + "0xd71673afb2d4fa2bfb83c3892943b95ad4701e2fd78801199c07446743897d3a": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xd9c5f37af5366d29a8e3839d99c1dbe7d1b1e3f1e2f5a3ac117401809b0d2501": "0x000000000000000000000001de5b54e8e7b585153add32f472e8d545e5d42a82", + "0xd9c5f37af5366d29a8e3839d99c1dbe7d1b1e3f1e2f5a3ac117401809b0d2502": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xd9cfd15c4a9619e16321f9e4042a566871512a8c0375122ca56a91ed5dfaa5e2": "0x000000000000000000000001de5b54e8e7b585153add32f472e8d545e5d42a82", + "0xd9cfd15c4a9619e16321f9e4042a566871512a8c0375122ca56a91ed5dfaa5e3": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xd9d4d93f562d5dc8184de584a08046769c25c10f1cebee1617c95d9212c093ec": "0x000000000000000000000000000000000000000000084595161401484a000000", + "0xe0d37705568ac8274db84c0b672ef9a5b02459e4607af2736a98b235c5342cf9": "0x000000000000000000000000de5b54e8e7b585153add32f472e8d545e5d42a82", + "0xe20533b9acd093b6b21eda56a3ad045315f0999f910d7ccada9ec110917d3dc2": "0x000000000000000000000000000000000000000000084595161401484a000000", + "0xe6920aeacdafc0d4f7573369e69a8a79eaee08f9d9f620e22831ee53d8de618c": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0xe6a03f7a1a989ef0b5a97e8964334cc7ddb72a67de4c24b5f891847210a9db11": "0x000000000000000000000001de5b54e8e7b585153add32f472e8d545e5d42a82", + "0xe6a03f7a1a989ef0b5a97e8964334cc7ddb72a67de4c24b5f891847210a9db12": "0x000000000000000000000000000000000000000000000a968163f0a57b400000", + "0xea2b7d5c8733427922e04399ac310174bb0431233be39c6a708646879c299ed9": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3": "0x000000000000000000000000311bdf9066246e68559816e7f636435867f824ef", + "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee4": "0x000000000000000000000000442a44a6fc20f5b8dfa8b304d7137581f7e6bef3", + "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee5": "0x00000000000000000000000047318441696e9ae962633c16e04d53935272639d", + "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee6": "0x000000000000000000000000537fc89618edae86950e12687a612e15c4786b84", + "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee7": "0x00000000000000000000000073898cc3c5beca5841306b26fdb4e30411392a6a", + "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee8": "0x00000000000000000000000074826141342a4dc33a1045cefa4182b6212ec031", + "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee9": "0x0000000000000000000000007bc30fbeb7208192d1474b5f87ffbc056de43c11", + "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636eea": "0x000000000000000000000000888c073313b36cf03cf1f739f39443551ff12bbe", + "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636eeb": "0x0000000000000000000000008d993351c0e2db739f9bcbfdeda94d73b50b16d3", + "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636eec": "0x000000000000000000000000a242960b7ca1937e826bda6c397df74d9f9ab01a", + "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636eed": "0x000000000000000000000000a89af636787499e81362e815e36f28763eac120b", + "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636eee": "0x000000000000000000000000abebf5a6cbe6113780cbe489e3eb0db882381aeb", + "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636eef": "0x000000000000000000000000af81190100d82f41ad2c95898195c7a47dc59115", + "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ef0": "0x000000000000000000000000bb5ec85408683795da2f604a5c0464868eabfcb6", + "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ef1": "0x000000000000000000000000da489a1b4304f49aafaec938c7adc48539470624", + "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ef2": "0x000000000000000000000000e1f9c75ce33e568d8fa3ace90497ee0c60dc921e", + "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ef3": "0x000000000000000000000000efea93e384a6ccaaf28e33790a2d1b2625bf964d", + "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ef4": "0x000000000000000000000000fc087e2622b02b0bb78713e872c02796ef64c8f1", + "0xf511943a3baf5478bbcb2a8fc05b1bd215ad2612dcea88999bb647d8a1bc497b": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0xf6c2c7fc452bd777ae6315a05df702a9c84c84319642f738c02d7265e7b03519": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0xf7c5c70c003782446997bfaca3df73d3f4529e5610bd8f00aa705bd9f2b7bea5": "0x000000000000000000000000de5b54e8e7b585153add32f472e8d545e5d42a82", + "0xfb99b5de22d1e4ca0e5857616be3d3df0df3f371c6dae7e7c3df24980cae362b": "0x000000000000000000000000de5b54e8e7b585153add32f472e8d545e5d42a82", + "0xfd6dda287eb8b2e65f6136280e7f54c250f820b79064da60c2f09859788892b6": "0x000000000000000000000000de5b54e8e7b585153add32f472e8d545e5d42a82", + "0xfdc6e9ef6103a34656ae73183b1f07e0d09db52b8500c3d560707c8f426d61b7": "0x000000000000000000000001de5b54e8e7b585153add32f472e8d545e5d42a82", + "0xfdc6e9ef6103a34656ae73183b1f07e0d09db52b8500c3d560707c8f426d61b8": "0x000000000000000000000000000000000000000000000a968163f0a57b400000" }, - "balance": "0x18d0bf423c03d8de000000" + "balance": "0xbe951906eba2aa800000" }, "0000000000000000000000000000000000000089": { "code": "0x6060604052600436106100565763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663e341eaa4811461005b578063e7ec6aef14610076578063f4145a83146100df575b600080fd5b341561006657600080fd5b610074600435602435610104565b005b341561008157600080fd5b61008c600435610227565b60405160208082528190810183818151815260200191508051906020019060200280838360005b838110156100cb5780820151838201526020016100b3565b505050509050019250505060405180910390f35b34156100ea57600080fd5b6100f26102ac565b60405190815260200160405180910390f35b438290101561011257600080fd5b600280546101289184910263ffffffff6102b216565b43111561013457600080fd5b600082815260016020819052604090912080549091810161015583826102c8565b5060009182526020808320919091018390558282528190526040902080546001810161018183826102c8565b506000918252602090912001805473ffffffffffffffffffffffffffffffffffffffff19163373ffffffffffffffffffffffffffffffffffffffff8116919091179091557f62855fa22e051687c32ac285857751f6d3f2c100c72756d8d30cb7ecb1f64f5490838360405173ffffffffffffffffffffffffffffffffffffffff909316835260208301919091526040808301919091526060909101905180910390a15050565b61022f6102f1565b600082815260208181526040918290208054909290918281020190519081016040528092919081815260200182805480156102a057602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610275575b50505050509050919050565b60025481565b6000828201838110156102c157fe5b9392505050565b8154818355818115116102ec576000838152602090206102ec918101908301610303565b505050565b60206040519081016040526000815290565b61032191905b8082111561031d5760008155600101610309565b5090565b905600a165627a7a72305820a8ceddaea8e4ae00991e2ae81c8c88e160dd8770f255523282c24c2df4c30ec70029", @@ -80,50 +211,68 @@ "balance": "0x0" }, "0000000000000000000000000000000000000099": { - "balance": "0x0" + "balance": "0x92e8434aaaf80e1800000" }, - "03c0d9bc556be68870b96976e81d32ebb49d335d": { + "311bdf9066246e68559816e7f636435867f824ef": { "balance": "0x200000000000000000000000000000000000000000000000000000000000000" }, - "065551f0dcac6f00cae11192d462db709be3758c": { - "balance": "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "442a44a6fc20f5b8dfa8b304d7137581f7e6bef3": { + "balance": "0x200000000000000000000000000000000000000000000000000000000000000" }, - "38b20a2594939531373efcd2e6aa54d03fc534be": { - "balance": "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "47318441696e9ae962633c16e04d53935272639d": { + "balance": "0x200000000000000000000000000000000000000000000000000000000000000" }, - "42e694adfd403152cd9cad82a62fb6cd403f150a": { - "balance": "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "537fc89618edae86950e12687a612e15c4786b84": { + "balance": "0x200000000000000000000000000000000000000000000000000000000000000" }, - "4398241671b3dd484fe3213a4fb7511f30e7d7c0": { - "balance": "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "73898cc3c5beca5841306b26fdb4e30411392a6a": { + "balance": "0x200000000000000000000000000000000000000000000000000000000000000" }, - "4e37f91e3bc69725326af96facf85c14128b07ed": { - "balance": "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "74826141342a4dc33a1045cefa4182b6212ec031": { + "balance": "0x200000000000000000000000000000000000000000000000000000000000000" }, - "664c4a7b15d91b07c468162f535909114c038b91": { - "balance": "0x7912752226cec5131e000000" + "7bc30fbeb7208192d1474b5f87ffbc056de43c11": { + "balance": "0x200000000000000000000000000000000000000000000000000000000000000" }, - "746249c61f5832c5eed53172776b460491bdcd5c": { - "balance": "0x0" + "888c073313b36cf03cf1f739f39443551ff12bbe": { + "balance": "0x200000000000000000000000000000000000000000000000000000000000000" }, - "a12a44433e64b76a9e388d87af02ce80580cf1f8": { - "balance": "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "8d993351c0e2db739f9bcbfdeda94d73b50b16d3": { + "balance": "0x200000000000000000000000000000000000000000000000000000000000000" }, - "7aa125338be075260e77c6a66a56c90a5dec4c58": { - "balance": "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "a242960b7ca1937e826bda6c397df74d9f9ab01a": { + "balance": "0x200000000000000000000000000000000000000000000000000000000000000" }, - "9a3787688fd210ec8f8d0224c6c50b8178d75bc0": { - "balance": "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "a89af636787499e81362e815e36f28763eac120b": { + "balance": "0x200000000000000000000000000000000000000000000000000000000000000" }, - "a65010026b83368ca05df6e8b467985d6de3eac5": { - "balance": "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "abebf5a6cbe6113780cbe489e3eb0db882381aeb": { + "balance": "0x200000000000000000000000000000000000000000000000000000000000000" }, - "03c0d9bc556BE68870B96976e81D32ebb49d335D": { - "balance": "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "af81190100d82f41ad2c95898195c7a47dc59115": { + "balance": "0x200000000000000000000000000000000000000000000000000000000000000" + }, + "bb5ec85408683795da2f604a5c0464868eabfcb6": { + "balance": "0x200000000000000000000000000000000000000000000000000000000000000" + }, + "da489a1b4304f49aafaec938c7adc48539470624": { + "balance": "0x200000000000000000000000000000000000000000000000000000000000000" + }, + "de5b54e8e7b585153add32f472e8d545e5d42a82": { + "balance": "0x200000000000000000000000000000000000000000000000000000000000000" + }, + "e1f9c75ce33e568d8fa3ace90497ee0c60dc921e": { + "balance": "0x200000000000000000000000000000000000000000000000000000000000000" + }, + "efea93e384a6ccaaf28e33790a2d1b2625bf964d": { + "balance": "0x200000000000000000000000000000000000000000000000000000000000000" + }, + "fc087e2622b02b0bb78713e872c02796ef64c8f1": { + "balance": "0x200000000000000000000000000000000000000000000000000000000000000" } - }, "number": "0x0", "gasUsed": "0x0", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "baseFeePerGas": null } \ No newline at end of file diff --git a/cicd/entry.sh b/cicd/entry.sh index e7518fe9f603..0cd11b4ea474 100755 --- a/cicd/entry.sh +++ b/cicd/entry.sh @@ -7,7 +7,7 @@ fi echo "Select to run $NETWORK..." ln -s /usr/bin/XDC-$NETWORK /usr/bin/XDC -cp /work/$NETWORK/* /work +cp -n /work/$NETWORK/* /work echo "Start Node..." /work/start.sh \ No newline at end of file diff --git a/common/constants/constants.go.devnet b/common/constants/constants.go.devnet index 81e8c0487cd2..712bfac8c57e 100644 --- a/common/constants/constants.go.devnet +++ b/common/constants/constants.go.devnet @@ -32,26 +32,26 @@ const ( var Rewound = uint64(0) -var TIP2019Block = big.NewInt(1) -var TIPSigning = big.NewInt(225000) -var TIPRandomize = big.NewInt(225000) +var TIP2019Block = big.NewInt(0) +var TIPSigning = big.NewInt(0) +var TIPRandomize = big.NewInt(0) -var TIPV2SwitchBlock = big.NewInt(7074000) +var TIPV2SwitchBlock = big.NewInt(0) -var TIPIncreaseMasternodes = big.NewInt(225000) // Upgrade MN Count at Block. -var TIPNoHalvingMNReward = big.NewInt(429987) // hardfork no halving masternodes reward -var BlackListHFNumber = uint64(225000) -var TIPXDCX = big.NewInt(225000) -var TIPXDCXLending = big.NewInt(225000) -var TIPXDCXCancellationFee = big.NewInt(225000) -var TIPXDCXCancellationFeeTestnet = big.NewInt(225000) -var TIPXDCXMinerDisable = big.NewInt(15894900) -var TIPXDCXReceiverDisable = big.NewInt(18018000) -var BerlinBlock = big.NewInt(16832700) -var LondonBlock = big.NewInt(16832700) -var MergeBlock = big.NewInt(16832700) -var ShanghaiBlock = big.NewInt(16832700) -var Eip1559Block = big.NewInt(23035500) +var TIPIncreaseMasternodes = big.NewInt(0) // Upgrade MN Count at Block. +var TIPNoHalvingMNReward = big.NewInt(0) // hardfork no halving masternodes reward +var BlackListHFNumber = uint64(0) +var TIPXDCX = big.NewInt(0) +var TIPXDCXLending = big.NewInt(0) +var TIPXDCXCancellationFee = big.NewInt(0) +var TIPXDCXCancellationFeeTestnet = big.NewInt(0) +var TIPXDCXMinerDisable = big.NewInt(0) +var TIPXDCXReceiverDisable = big.NewInt(0) +var BerlinBlock = big.NewInt(0) +var LondonBlock = big.NewInt(0) +var MergeBlock = big.NewInt(0) +var ShanghaiBlock = big.NewInt(0) +var Eip1559Block = big.NewInt(0) var TIPXDCXTestnet = big.NewInt(0) var IsTestnet bool = false From 654be4d8746724e13bd49aba154bab588b6f696d Mon Sep 17 00:00:00 2001 From: wanwiset25 Date: Mon, 30 Dec 2024 17:10:27 -0500 Subject: [PATCH 467/479] add cicd --- .github/workflows/ci.yml | 55 ++++++++++++++++++++++++++++ common/constants/constants.go.devnet | 2 +- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b6b7db7ffe5b..7ca0550c3db1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,6 +100,61 @@ jobs: echo "image_name=$image_name" echo "image_name=$image_name" >> "$GITHUB_OUTPUT" + devnet_k8_apply: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/') + needs: devnet_build_push + steps: + - uses: actions/checkout@v4 + - name: Set up kubectl + uses: azure/setup-kubectl@v4 + with: + version: 'v1.19.11' + - name: Decode and configure kubeconfig + run: | + mkdir -p $HOME/.kube + echo "${{ secrets.KUBE_CONFIG }}" | base64 --decode > $HOME/.kube/config + chmod 600 $HOME/.kube/config + git_hash=$(git rev-parse --short "$GITHUB_SHA") + kubectl set image deployment/devnet1 devnet1=xinfinorg/devnet:dev-upgrade-${git_hash} + kubectl set image deployment/devnet2 devnet2=xinfinorg/devnet:dev-upgrade-${git_hash} + kubectl set image deployment/devnet3 devnet3=xinfinorg/devnet:dev-upgrade-${git_hash} + kubectl set image deployment/devnet4 devnet4=xinfinorg/devnet:dev-upgrade-${git_hash} + kubectl set image deployment/devnet5 devnet5=xinfinorg/devnet:dev-upgrade-${git_hash} + kubectl set image deployment/devnet6 devnet6=xinfinorg/devnet:dev-upgrade-${git_hash} + kubectl set image deployment/devnet7 devnet7=xinfinorg/devnet:dev-upgrade-${git_hash} + kubectl set image deployment/devnet8 devnet8=xinfinorg/devnet:dev-upgrade-${git_hash} + kubectl set image deployment/devnet9 devnet9=xinfinorg/devnet:dev-upgrade-${git_hash} + kubectl set image deployment/devnet10 devnet10=xinfinorg/devnet:dev-upgrade-${git_hash} + kubectl set image deployment/devnet11 devnet11=xinfinorg/devnet:dev-upgrade-${git_hash} + kubectl set image deployment/devnet12 devnet12=xinfinorg/devnet:dev-upgrade-${git_hash} + kubectl set image deployment/devnet13 devnet13=xinfinorg/devnet:dev-upgrade-${git_hash} + kubectl set image deployment/devnet14 devnet14=xinfinorg/devnet:dev-upgrade-${git_hash} + kubectl set image deployment/devnet15 devnet15=xinfinorg/devnet:dev-upgrade-${git_hash} + kubectl set image deployment/devnet16 devnet16=xinfinorg/devnet:dev-upgrade-${git_hash} + kubectl set image deployment/devnet17 devnet17=xinfinorg/devnet:dev-upgrade-${git_hash} + kubectl set image deployment/devnet18 devnet18=xinfinorg/devnet:dev-upgrade-${git_hash} + + rpc_k8_apply: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/') + needs: devnet_build_push + steps: + - uses: actions/checkout@v4 + - name: Set up kubectl + uses: azure/setup-kubectl@v4 + with: + version: 'v1.19.11' + - name: Decode and configure kubeconfig + run: | + mkdir -p $HOME/.kube + echo "${{ secrets.KUBE_CONFIG }}" | base64 --decode > $HOME/.kube/config + chmod 600 $HOME/.kube/config + git_hash=$(git rev-parse --short "$GITHUB_SHA") + kubectl set image deployment/devnetrpc devnetrpc=xinfinorg/devnet:dev-upgrade-${git_hash} + kubectl set image deployment/testnetrpc testnetrpc=xinfinorg/devnet:dev-upgrade-${git_hash} + kubectl set image deployment/mainnetrpc mainnetrpc=xinfinorg/devnet:dev-upgrade-${git_hash} + rpcnode_terraform_apply: runs-on: ubuntu-latest if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/') diff --git a/common/constants/constants.go.devnet b/common/constants/constants.go.devnet index 712bfac8c57e..a71257ae6fc1 100644 --- a/common/constants/constants.go.devnet +++ b/common/constants/constants.go.devnet @@ -36,7 +36,7 @@ var TIP2019Block = big.NewInt(0) var TIPSigning = big.NewInt(0) var TIPRandomize = big.NewInt(0) -var TIPV2SwitchBlock = big.NewInt(0) +var TIPV2SwitchBlock = big.NewInt(1800) var TIPIncreaseMasternodes = big.NewInt(0) // Upgrade MN Count at Block. var TIPNoHalvingMNReward = big.NewInt(0) // hardfork no halving masternodes reward From 0404803366dd1ec0991cfef13dd55ae275a55d4f Mon Sep 17 00:00:00 2001 From: wanwiset25 Date: Mon, 30 Dec 2024 20:44:01 -0500 Subject: [PATCH 468/479] optimize shell script --- cicd/local/start.sh | 62 +++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 39 deletions(-) diff --git a/cicd/local/start.sh b/cicd/local/start.sh index cfd877de4066..30a4ed44642e 100755 --- a/cicd/local/start.sh +++ b/cicd/local/start.sh @@ -1,4 +1,6 @@ #!/bin/bash +set -eo pipefail + if [ ! -d /work/xdcchain/XDC/chaindata ] then if test -z "$PRIVATE_KEY" @@ -25,41 +27,13 @@ do fi done < "$input" -log_level=3 -if test -z "$LOG_LEVEL" -then - echo "Log level not set, default to verbosity of $log_level" -else - echo "Log level found, set to $LOG_LEVEL" - log_level=$LOG_LEVEL -fi +log_level="${LOG_LEVEL:-3}" -port=30303 -if test -z "$PORT" -then - echo "PORT not set, default to $port" -else - echo "PORT found, set to $PORT" - port=$PORT -fi +port="${PORT:-30303}" -rpc_port=8545 -if test -z "$RPC_PORT" -then - echo "RPC_PORT not set, default to $rpc_port" -else - echo "RPC_PORT found, set to $RPC_PORT" - rpc_port=$RPC_PORT -fi +rpc_port="${RPC_PORT:-8545}" -ws_port=8555 -if test -z "$WS_PORT" -then - echo "WS_PORT not set, default to $ws_port" -else - echo "WS_PORT found, set to $WS_PORT" - ws_port=$WS_PORT -fi +ws_port="${WS_PORT:-8555}" netstats="${NODE_NAME}-${wallet}:xinfin_xdpos_hybrid_network_stats@devnetstats.apothem.network:2000" @@ -70,14 +44,24 @@ echo "Starting nodes with $bootnodes ..." # Note: --gcmode=archive means node will store all historical data. This will lead to high memory usage. But sync mode require archive to sync # https://github.com/XinFinOrg/XDPoSChain/issues/268 -XDC --ethstats ${netstats} --gcmode archive \ ---bootnodes ${bootnodes} --syncmode full \ ---datadir /work/xdcchain --networkid 551 \ --port $port --rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 \ +XDC --ethstats ${netstats} \ +--gcmode archive \ +--bootnodes ${bootnodes} \ +--syncmode full \ +--datadir /work/xdcchain \ +--networkid 551 \ +-port $port \ +--rpc --rpccorsdomain "*" \ +--rpcaddr 0.0.0.0 \ --rpcport $rpc_port \ --rpcapi db,eth,debug,net,shh,txpool,personal,web3,XDPoS \ ---rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \ ---gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \ +--rpcvhosts "*" \ +--unlock "${wallet}" \ +--password /work/.pwd --mine \ +--gasprice "1" --targetgaslimit "420000000" \ +--verbosity ${log_level} \ --debugdatadir /work/xdcchain \ ---ws --wsaddr=0.0.0.0 --wsport $ws_port \ +--ws \ +--wsaddr=0.0.0.0 \ +--wsport $ws_port \ --wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log From 12488b86ed7f5e61085551da3f7672cfc42458bb Mon Sep 17 00:00:00 2001 From: wanwiset25 Date: Wed, 1 Jan 2025 21:51:11 -0500 Subject: [PATCH 469/479] add bootnodes --- cicd/devnet/bootnodes.list | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cicd/devnet/bootnodes.list b/cicd/devnet/bootnodes.list index edffbabcf4a3..d3b1228edd76 100644 --- a/cicd/devnet/bootnodes.list +++ b/cicd/devnet/bootnodes.list @@ -1,2 +1,4 @@ +enode://00d49d72a48164681906ad61924568da0d3049937efdbaed0b7533e34a99f55814f1839d909cdc82f78e04a36ac04737d80b41b22905c7d6cac3c80bb5cdbbc4@66.94.98.186:30301 +enode://d6793b02a478f13ed6d01c30778935f6f8f7461a75aebedcb310def4ed9b066f995a0dca046d0c7ea7f5ffdd8e3f1f53c6b6dce909d1693650504921aad62f1a@194.163.167.177:30301 enode://c7a38ecc7fd5849eaefb3e928f882ab2f1260d5591bd1fef1db39c8ae0d9fe21d86e2d4efee6def0f4f16d0b6103f86f92f7e910295b849266fef83f05768596@10.244.1.77:30301 enode://9160713d41656154ce3fdaa9d0f9799dd8546652b1784edecf82ce7a5b54a977f897ea617a9b3b4db513dc6b0dc98c1c3ad8a1914075ab87ded4e367c89dcd3b@10.244.2.53:30301 From d0566e4165d0113582dcb4d12a9a248824aefa54 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 3 Jan 2025 09:51:44 +0800 Subject: [PATCH 470/479] crypto: update to go version 1.22 (#28946) --- crypto/crypto.go | 15 ++++++-- crypto/ecies/ecies.go | 58 +++++++++++++++++------------- crypto/secp256k1/secp256_test.go | 3 +- crypto/signature_cgo.go | 7 ++-- crypto/signature_nocgo.go | 61 ++++++++++++++++++++++++++------ 5 files changed, 99 insertions(+), 45 deletions(-) diff --git a/crypto/crypto.go b/crypto/crypto.go index 22849aa6ea30..552e93f9cb5f 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -51,6 +51,15 @@ var ( var errInvalidPubkey = errors.New("invalid secp256k1 public key") +// EllipticCurve contains curve operations. +type EllipticCurve interface { + elliptic.Curve + + // Point marshaling/unmarshaing. + Marshal(x, y *big.Int) []byte + Unmarshal(data []byte) (x, y *big.Int) +} + // KeccakState wraps sha3.state. In addition to the usual hash methods, it also supports // Read to get a variable amount of data from the hash state. Read is faster than Sum // because it doesn't copy the internal state, but also modifies the internal state. @@ -148,7 +157,7 @@ func toECDSA(d []byte, strict bool) (*ecdsa.PrivateKey, error) { return nil, errors.New("invalid private key, zero or negative") } - priv.PublicKey.X, priv.PublicKey.Y = priv.PublicKey.Curve.ScalarBaseMult(d) + priv.PublicKey.X, priv.PublicKey.Y = S256().ScalarBaseMult(d) if priv.PublicKey.X == nil { return nil, errors.New("invalid private key") } @@ -165,7 +174,7 @@ func FromECDSA(priv *ecdsa.PrivateKey) []byte { // UnmarshalPubkey converts bytes to a secp256k1 public key. func UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) { - x, y := elliptic.Unmarshal(S256(), pub) + x, y := S256().Unmarshal(pub) if x == nil { return nil, errInvalidPubkey } @@ -176,7 +185,7 @@ func FromECDSAPub(pub *ecdsa.PublicKey) []byte { if pub == nil || pub.X == nil || pub.Y == nil { return nil } - return elliptic.Marshal(S256(), pub.X, pub.Y) + return S256().Marshal(pub.X, pub.Y) } // HexToECDSA parses a secp256k1 private key. diff --git a/crypto/ecies/ecies.go b/crypto/ecies/ecies.go index 738bb8f584aa..4a0d03f95839 100644 --- a/crypto/ecies/ecies.go +++ b/crypto/ecies/ecies.go @@ -40,6 +40,8 @@ import ( "hash" "io" "math/big" + + "github.com/XinFinOrg/XDPoSChain/crypto" ) var ( @@ -95,15 +97,15 @@ func ImportECDSA(prv *ecdsa.PrivateKey) *PrivateKey { // Generate an elliptic curve public / private keypair. If params is nil, // the recommended default parameters for the key will be chosen. func GenerateKey(rand io.Reader, curve elliptic.Curve, params *ECIESParams) (prv *PrivateKey, err error) { - pb, x, y, err := elliptic.GenerateKey(curve, rand) + sk, err := ecdsa.GenerateKey(curve, rand) if err != nil { return } prv = new(PrivateKey) - prv.PublicKey.X = x - prv.PublicKey.Y = y + prv.PublicKey.X = sk.X + prv.PublicKey.Y = sk.Y prv.PublicKey.Curve = curve - prv.D = new(big.Int).SetBytes(pb) + prv.D = new(big.Int).Set(sk.D) if params == nil { params = ParamsFromCurve(curve) } @@ -255,12 +257,15 @@ func Encrypt(rand io.Reader, pub *PublicKey, m, s1, s2 []byte) (ct []byte, err e d := messageTag(params.Hash, Km, em, s2) - Rb := elliptic.Marshal(pub.Curve, R.PublicKey.X, R.PublicKey.Y) - ct = make([]byte, len(Rb)+len(em)+len(d)) - copy(ct, Rb) - copy(ct[len(Rb):], em) - copy(ct[len(Rb)+len(em):], d) - return ct, nil + if curve, ok := pub.Curve.(crypto.EllipticCurve); ok { + Rb := curve.Marshal(R.PublicKey.X, R.PublicKey.Y) + ct = make([]byte, len(Rb)+len(em)+len(d)) + copy(ct, Rb) + copy(ct[len(Rb):], em) + copy(ct[len(Rb)+len(em):], d) + return ct, nil + } + return nil, ErrInvalidCurve } // Decrypt decrypts an ECIES ciphertext. @@ -297,21 +302,24 @@ func (prv *PrivateKey) Decrypt(c, s1, s2 []byte) (m []byte, err error) { R := new(PublicKey) R.Curve = prv.PublicKey.Curve - R.X, R.Y = elliptic.Unmarshal(R.Curve, c[:rLen]) - if R.X == nil { - return nil, ErrInvalidPublicKey - } - z, err := prv.GenerateShared(R, params.KeyLen, params.KeyLen) - if err != nil { - return nil, err - } - Ke, Km := deriveKeys(hash, z, s1, params.KeyLen) + if curve, ok := R.Curve.(crypto.EllipticCurve); ok { + R.X, R.Y = curve.Unmarshal(c[:rLen]) + if R.X == nil { + return nil, ErrInvalidPublicKey + } - d := messageTag(params.Hash, Km, c[mStart:mEnd], s2) - if subtle.ConstantTimeCompare(c[mEnd:], d) != 1 { - return nil, ErrInvalidMessage - } + z, err := prv.GenerateShared(R, params.KeyLen, params.KeyLen) + if err != nil { + return nil, err + } + Ke, Km := deriveKeys(hash, z, s1, params.KeyLen) - return symDecrypt(params, Ke, c[mStart:mEnd]) -} + d := messageTag(params.Hash, Km, c[mStart:mEnd], s2) + if subtle.ConstantTimeCompare(c[mEnd:], d) != 1 { + return nil, ErrInvalidMessage + } + return symDecrypt(params, Ke, c[mStart:mEnd]) + } + return nil, ErrInvalidCurve +} \ No newline at end of file diff --git a/crypto/secp256k1/secp256_test.go b/crypto/secp256k1/secp256_test.go index 74408d06d2bf..8bb870fa18bc 100644 --- a/crypto/secp256k1/secp256_test.go +++ b/crypto/secp256k1/secp256_test.go @@ -10,7 +10,6 @@ package secp256k1 import ( "bytes" "crypto/ecdsa" - "crypto/elliptic" "crypto/rand" "encoding/hex" "io" @@ -24,7 +23,7 @@ func generateKeyPair() (pubkey, privkey []byte) { if err != nil { panic(err) } - pubkey = elliptic.Marshal(S256(), key.X, key.Y) + pubkey = S256().Marshal(key.X, key.Y) privkey = make([]byte, 32) blob := key.D.Bytes() diff --git a/crypto/signature_cgo.go b/crypto/signature_cgo.go index 681165750304..bace593fb507 100644 --- a/crypto/signature_cgo.go +++ b/crypto/signature_cgo.go @@ -21,7 +21,6 @@ package crypto import ( "crypto/ecdsa" - "crypto/elliptic" "errors" "fmt" @@ -40,9 +39,7 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { if err != nil { return nil, err } - - x, y := elliptic.Unmarshal(S256(), s) - return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}, nil + return UnmarshalPubkey(s) } // Sign calculates an ECDSA signature. @@ -84,6 +81,6 @@ func CompressPubkey(pubkey *ecdsa.PublicKey) []byte { } // S256 returns an instance of the secp256k1 curve. -func S256() elliptic.Curve { +func S256() EllipticCurve { return secp256k1.S256() } diff --git a/crypto/signature_nocgo.go b/crypto/signature_nocgo.go index 6d628d758d93..4438c1defda1 100644 --- a/crypto/signature_nocgo.go +++ b/crypto/signature_nocgo.go @@ -21,9 +21,9 @@ package crypto import ( "crypto/ecdsa" - "crypto/elliptic" "errors" "fmt" + "math/big" "github.com/btcsuite/btcd/btcec/v2" btc_ecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa" @@ -58,7 +58,13 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { if err != nil { return nil, err } - return pub.ToECDSA(), nil + // We need to explicitly set the curve here, because we're wrapping + // the original curve to add (un-)marshalling + return &ecdsa.PublicKey{ + Curve: S256(), + X: pub.X(), + Y: pub.Y(), + }, nil } // Sign calculates an ECDSA signature. @@ -73,7 +79,7 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) { if len(hash) != 32 { return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash)) } - if prv.Curve != btcec.S256() { + if prv.Curve != S256() { return nil, errors.New("private key curve is not secp256k1") } // ecdsa.PrivateKey -> btcec.PrivateKey @@ -82,10 +88,7 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) { return nil, errors.New("invalid private key") } defer priv.Zero() - sig, err := btc_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey - if err != nil { - return nil, err - } + sig := btc_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey // Convert to Ethereum signature format with 'recovery id' v at the end. v := sig[0] - 27 copy(sig, sig[1:]) @@ -128,7 +131,13 @@ func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) { if err != nil { return nil, err } - return key.ToECDSA(), nil + // We need to explicitly set the curve here, because we're wrapping + // the original curve to add (un-)marshalling + return &ecdsa.PublicKey{ + Curve: S256(), + X: key.X(), + Y: key.Y(), + }, nil } // CompressPubkey encodes a public key to the 33-byte compressed format. The @@ -147,6 +156,38 @@ func CompressPubkey(pubkey *ecdsa.PublicKey) []byte { } // S256 returns an instance of the secp256k1 curve. -func S256() elliptic.Curve { - return btcec.S256() +func S256() EllipticCurve { + return btCurve{btcec.S256()} +} + +type btCurve struct { + *btcec.KoblitzCurve +} + +// Marshall converts a point given as (x, y) into a byte slice. +func (curve btCurve) Marshal(x, y *big.Int) []byte { + byteLen := (curve.Params().BitSize + 7) / 8 + + ret := make([]byte, 1+2*byteLen) + ret[0] = 4 // uncompressed point + + x.FillBytes(ret[1 : 1+byteLen]) + y.FillBytes(ret[1+byteLen : 1+2*byteLen]) + + return ret +} + +// Unmarshal converts a point, serialised by Marshal, into an x, y pair. On +// error, x = nil. +func (curve btCurve) Unmarshal(data []byte) (x, y *big.Int) { + byteLen := (curve.Params().BitSize + 7) / 8 + if len(data) != 1+2*byteLen { + return nil, nil + } + if data[0] != 4 { // uncompressed form + return nil, nil + } + x = new(big.Int).SetBytes(data[1 : 1+byteLen]) + y = new(big.Int).SetBytes(data[1+byteLen:]) + return } From efad15f9f4d5819083241a611a67506b827a70d1 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 3 Jan 2025 09:56:02 +0800 Subject: [PATCH 471/479] crypto: use clear from go1.21 (#29307) --- crypto/crypto.go | 4 +--- crypto/secp256k1/scalar_mult_cgo.go | 8 ++------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/crypto/crypto.go b/crypto/crypto.go index 552e93f9cb5f..a5da7111247b 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -287,7 +287,5 @@ func PubkeyToAddress(p ecdsa.PublicKey) common.Address { } func zeroBytes(bytes []byte) { - for i := range bytes { - bytes[i] = 0 - } + clear(bytes) } diff --git a/crypto/secp256k1/scalar_mult_cgo.go b/crypto/secp256k1/scalar_mult_cgo.go index 8afa9d023b07..bdf8eeede7df 100644 --- a/crypto/secp256k1/scalar_mult_cgo.go +++ b/crypto/secp256k1/scalar_mult_cgo.go @@ -44,12 +44,8 @@ func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, // Unpack the result and clear temporaries. x := new(big.Int).SetBytes(point[:32]) y := new(big.Int).SetBytes(point[32:]) - for i := range point { - point[i] = 0 - } - for i := range padded { - scalar[i] = 0 - } + clear(point) + clear(scalar) if res != 1 { return nil, nil } From 136e87fa4c7ef12b8bca8e3cadb75ad0b9d3bb60 Mon Sep 17 00:00:00 2001 From: George Ma <164313692+availhang@users.noreply.github.com> Date: Fri, 22 Mar 2024 20:29:12 +0800 Subject: [PATCH 472/479] crypto: fix docstrings and comments --- crypto/bn256/google/bn256.go | 2 +- crypto/secp256k1/secp256_test.go | 2 +- crypto/signature_nocgo.go | 2 +- crypto/signature_test.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crypto/bn256/google/bn256.go b/crypto/bn256/google/bn256.go index 93953e23a95f..aca9cf62de1b 100644 --- a/crypto/bn256/google/bn256.go +++ b/crypto/bn256/google/bn256.go @@ -29,7 +29,7 @@ import ( ) // BUG(agl): this implementation is not constant time. -// TODO(agl): keep GF(p²) elements in Mongomery form. +// TODO(agl): keep GF(p²) elements in Montgomery form. // G1 is an abstract cyclic group. The zero value is suitable for use as the // output of an operation, but cannot be used as an input. diff --git a/crypto/secp256k1/secp256_test.go b/crypto/secp256k1/secp256_test.go index 8bb870fa18bc..4827cc5b255c 100644 --- a/crypto/secp256k1/secp256_test.go +++ b/crypto/secp256k1/secp256_test.go @@ -48,7 +48,7 @@ func randSig() []byte { } // tests for malleability -// highest bit of signature ECDSA s value must be 0, in the 33th byte +// the highest bit of signature ECDSA s value must be 0, in the 33th byte func compactSigCheck(t *testing.T, sig []byte) { var b = int(sig[32]) if b < 0 { diff --git a/crypto/signature_nocgo.go b/crypto/signature_nocgo.go index 4438c1defda1..5ac3765c7106 100644 --- a/crypto/signature_nocgo.go +++ b/crypto/signature_nocgo.go @@ -164,7 +164,7 @@ type btCurve struct { *btcec.KoblitzCurve } -// Marshall converts a point given as (x, y) into a byte slice. +// Marshal converts a point given as (x, y) into a byte slice. func (curve btCurve) Marshal(x, y *big.Int) []byte { byteLen := (curve.Params().BitSize + 7) / 8 diff --git a/crypto/signature_test.go b/crypto/signature_test.go index 3d3331f4d7dc..ce790b1408eb 100644 --- a/crypto/signature_test.go +++ b/crypto/signature_test.go @@ -71,7 +71,7 @@ func TestVerifySignature(t *testing.T) { wrongkey := common.CopyBytes(testpubkey) wrongkey[10]++ if VerifySignature(wrongkey, testmsg, sig) { - t.Errorf("signature valid with with wrong public key") + t.Errorf("signature valid with wrong public key") } } From 104a97354abcaeac8187aba2da5410e14eef3992 Mon Sep 17 00:00:00 2001 From: SuiYuan <165623542+suiyuan1314@users.noreply.github.com> Date: Thu, 30 May 2024 22:24:16 +0800 Subject: [PATCH 473/479] crypto/secp256k1: change receiver variable name to lowercase (#29889) --- crypto/secp256k1/curve.go | 88 +++++++++++++-------------- crypto/secp256k1/scalar_mult_cgo.go | 2 +- crypto/secp256k1/scalar_mult_nocgo.go | 2 +- 3 files changed, 46 insertions(+), 46 deletions(-) diff --git a/crypto/secp256k1/curve.go b/crypto/secp256k1/curve.go index 9b26ab292859..85ba885d6f5f 100644 --- a/crypto/secp256k1/curve.go +++ b/crypto/secp256k1/curve.go @@ -79,52 +79,52 @@ type BitCurve struct { BitSize int // the size of the underlying field } -func (BitCurve *BitCurve) Params() *elliptic.CurveParams { +func (bitCurve *BitCurve) Params() *elliptic.CurveParams { return &elliptic.CurveParams{ - P: BitCurve.P, - N: BitCurve.N, - B: BitCurve.B, - Gx: BitCurve.Gx, - Gy: BitCurve.Gy, - BitSize: BitCurve.BitSize, + P: bitCurve.P, + N: bitCurve.N, + B: bitCurve.B, + Gx: bitCurve.Gx, + Gy: bitCurve.Gy, + BitSize: bitCurve.BitSize, } } // IsOnCurve returns true if the given (x,y) lies on the BitCurve. -func (BitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool { +func (bitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool { // y² = x³ + b y2 := new(big.Int).Mul(y, y) //y² - y2.Mod(y2, BitCurve.P) //y²%P + y2.Mod(y2, bitCurve.P) //y²%P x3 := new(big.Int).Mul(x, x) //x² x3.Mul(x3, x) //x³ - x3.Add(x3, BitCurve.B) //x³+B - x3.Mod(x3, BitCurve.P) //(x³+B)%P + x3.Add(x3, bitCurve.B) //x³+B + x3.Mod(x3, bitCurve.P) //(x³+B)%P return x3.Cmp(y2) == 0 } // affineFromJacobian reverses the Jacobian transform. See the comment at the // top of the file. -func (BitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { +func (bitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { if z.Sign() == 0 { return new(big.Int), new(big.Int) } - zinv := new(big.Int).ModInverse(z, BitCurve.P) + zinv := new(big.Int).ModInverse(z, bitCurve.P) zinvsq := new(big.Int).Mul(zinv, zinv) xOut = new(big.Int).Mul(x, zinvsq) - xOut.Mod(xOut, BitCurve.P) + xOut.Mod(xOut, bitCurve.P) zinvsq.Mul(zinvsq, zinv) yOut = new(big.Int).Mul(y, zinvsq) - yOut.Mod(yOut, BitCurve.P) + yOut.Mod(yOut, bitCurve.P) return } // Add returns the sum of (x1,y1) and (x2,y2) -func (BitCurve *BitCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { +func (bitCurve *BitCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { // If one point is at infinity, return the other point. // Adding the point at infinity to any point will preserve the other point. if x1.Sign() == 0 && y1.Sign() == 0 { @@ -135,27 +135,27 @@ func (BitCurve *BitCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { } z := new(big.Int).SetInt64(1) if x1.Cmp(x2) == 0 && y1.Cmp(y2) == 0 { - return BitCurve.affineFromJacobian(BitCurve.doubleJacobian(x1, y1, z)) + return bitCurve.affineFromJacobian(bitCurve.doubleJacobian(x1, y1, z)) } - return BitCurve.affineFromJacobian(BitCurve.addJacobian(x1, y1, z, x2, y2, z)) + return bitCurve.affineFromJacobian(bitCurve.addJacobian(x1, y1, z, x2, y2, z)) } // addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and // (x2, y2, z2) and returns their sum, also in Jacobian form. -func (BitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) { +func (bitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) { // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl z1z1 := new(big.Int).Mul(z1, z1) - z1z1.Mod(z1z1, BitCurve.P) + z1z1.Mod(z1z1, bitCurve.P) z2z2 := new(big.Int).Mul(z2, z2) - z2z2.Mod(z2z2, BitCurve.P) + z2z2.Mod(z2z2, bitCurve.P) u1 := new(big.Int).Mul(x1, z2z2) - u1.Mod(u1, BitCurve.P) + u1.Mod(u1, bitCurve.P) u2 := new(big.Int).Mul(x2, z1z1) - u2.Mod(u2, BitCurve.P) + u2.Mod(u2, bitCurve.P) h := new(big.Int).Sub(u2, u1) if h.Sign() == -1 { - h.Add(h, BitCurve.P) + h.Add(h, bitCurve.P) } i := new(big.Int).Lsh(h, 1) i.Mul(i, i) @@ -163,13 +163,13 @@ func (BitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int s1 := new(big.Int).Mul(y1, z2) s1.Mul(s1, z2z2) - s1.Mod(s1, BitCurve.P) + s1.Mod(s1, bitCurve.P) s2 := new(big.Int).Mul(y2, z1) s2.Mul(s2, z1z1) - s2.Mod(s2, BitCurve.P) + s2.Mod(s2, bitCurve.P) r := new(big.Int).Sub(s2, s1) if r.Sign() == -1 { - r.Add(r, BitCurve.P) + r.Add(r, bitCurve.P) } r.Lsh(r, 1) v := new(big.Int).Mul(u1, i) @@ -179,7 +179,7 @@ func (BitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int x3.Sub(x3, j) x3.Sub(x3, v) x3.Sub(x3, v) - x3.Mod(x3, BitCurve.P) + x3.Mod(x3, bitCurve.P) y3 := new(big.Int).Set(r) v.Sub(v, x3) @@ -187,33 +187,33 @@ func (BitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int s1.Mul(s1, j) s1.Lsh(s1, 1) y3.Sub(y3, s1) - y3.Mod(y3, BitCurve.P) + y3.Mod(y3, bitCurve.P) z3 := new(big.Int).Add(z1, z2) z3.Mul(z3, z3) z3.Sub(z3, z1z1) if z3.Sign() == -1 { - z3.Add(z3, BitCurve.P) + z3.Add(z3, bitCurve.P) } z3.Sub(z3, z2z2) if z3.Sign() == -1 { - z3.Add(z3, BitCurve.P) + z3.Add(z3, bitCurve.P) } z3.Mul(z3, h) - z3.Mod(z3, BitCurve.P) + z3.Mod(z3, bitCurve.P) return x3, y3, z3 } // Double returns 2*(x,y) -func (BitCurve *BitCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { +func (bitCurve *BitCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { z1 := new(big.Int).SetInt64(1) - return BitCurve.affineFromJacobian(BitCurve.doubleJacobian(x1, y1, z1)) + return bitCurve.affineFromJacobian(bitCurve.doubleJacobian(x1, y1, z1)) } // doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and // returns its double, also in Jacobian form. -func (BitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) { +func (bitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) { // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l a := new(big.Int).Mul(x, x) //X1² @@ -231,30 +231,30 @@ func (BitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, x3 := new(big.Int).Mul(big.NewInt(2), d) //2*D x3.Sub(f, x3) //F-2*D - x3.Mod(x3, BitCurve.P) + x3.Mod(x3, bitCurve.P) y3 := new(big.Int).Sub(d, x3) //D-X3 y3.Mul(e, y3) //E*(D-X3) y3.Sub(y3, new(big.Int).Mul(big.NewInt(8), c)) //E*(D-X3)-8*C - y3.Mod(y3, BitCurve.P) + y3.Mod(y3, bitCurve.P) z3 := new(big.Int).Mul(y, z) //Y1*Z1 z3.Mul(big.NewInt(2), z3) //3*Y1*Z1 - z3.Mod(z3, BitCurve.P) + z3.Mod(z3, bitCurve.P) return x3, y3, z3 } // ScalarBaseMult returns k*G, where G is the base point of the group and k is // an integer in big-endian form. -func (BitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) { - return BitCurve.ScalarMult(BitCurve.Gx, BitCurve.Gy, k) +func (bitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) { + return bitCurve.ScalarMult(bitCurve.Gx, bitCurve.Gy, k) } // Marshal converts a point into the form specified in section 4.3.6 of ANSI // X9.62. -func (BitCurve *BitCurve) Marshal(x, y *big.Int) []byte { - byteLen := (BitCurve.BitSize + 7) >> 3 +func (bitCurve *BitCurve) Marshal(x, y *big.Int) []byte { + byteLen := (bitCurve.BitSize + 7) >> 3 ret := make([]byte, 1+2*byteLen) ret[0] = 4 // uncompressed point flag readBits(x, ret[1:1+byteLen]) @@ -264,8 +264,8 @@ func (BitCurve *BitCurve) Marshal(x, y *big.Int) []byte { // Unmarshal converts a point, serialised by Marshal, into an x, y pair. On // error, x = nil. -func (BitCurve *BitCurve) Unmarshal(data []byte) (x, y *big.Int) { - byteLen := (BitCurve.BitSize + 7) >> 3 +func (bitCurve *BitCurve) Unmarshal(data []byte) (x, y *big.Int) { + byteLen := (bitCurve.BitSize + 7) >> 3 if len(data) != 1+2*byteLen { return } diff --git a/crypto/secp256k1/scalar_mult_cgo.go b/crypto/secp256k1/scalar_mult_cgo.go index bdf8eeede7df..d11c11faf85b 100644 --- a/crypto/secp256k1/scalar_mult_cgo.go +++ b/crypto/secp256k1/scalar_mult_cgo.go @@ -21,7 +21,7 @@ extern int secp256k1_ext_scalar_mul(const secp256k1_context* ctx, const unsigned */ import "C" -func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) { +func (bitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) { // Ensure scalar is exactly 32 bytes. We pad always, even if // scalar is 32 bytes long, to avoid a timing side channel. if len(scalar) > 32 { diff --git a/crypto/secp256k1/scalar_mult_nocgo.go b/crypto/secp256k1/scalar_mult_nocgo.go index 22f53ac6ae65..feb13a8dfd0e 100644 --- a/crypto/secp256k1/scalar_mult_nocgo.go +++ b/crypto/secp256k1/scalar_mult_nocgo.go @@ -9,6 +9,6 @@ package secp256k1 import "math/big" -func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) { +func (bitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) { panic("ScalarMult is not available when secp256k1 is built without cgo") } From e19093f344ef4324cc11f15cb7db80b8945279be Mon Sep 17 00:00:00 2001 From: zhiqiangxu <652732310@qq.com> Date: Tue, 9 Jul 2024 19:19:25 +0800 Subject: [PATCH 474/479] crypto: remove hardcoded value for secp256k1.N (#30126) --- crypto/crypto.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/crypto.go b/crypto/crypto.go index a5da7111247b..b30137bce534 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -45,7 +45,7 @@ const RecoveryIDOffset = 64 const DigestLength = 32 var ( - secp256k1N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16) + secp256k1N = S256().Params().N secp256k1halfN = new(big.Int).Div(secp256k1N, big.NewInt(2)) ) From 15be5ba464f6a2f3e08c87e95c31af277928869a Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 3 Jan 2025 10:19:25 +0800 Subject: [PATCH 475/479] crypto: use decred secp256k1 directly (#30595) --- crypto/signature_nocgo.go | 36 ++++++++++++++-------------- go.mod | 3 +-- go.sum | 2 -- tests/fuzzers/secp256k1/secp_test.go | 4 ++-- 4 files changed, 21 insertions(+), 24 deletions(-) diff --git a/crypto/signature_nocgo.go b/crypto/signature_nocgo.go index 5ac3765c7106..16a785a18600 100644 --- a/crypto/signature_nocgo.go +++ b/crypto/signature_nocgo.go @@ -25,8 +25,8 @@ import ( "fmt" "math/big" - "github.com/btcsuite/btcd/btcec/v2" - btc_ecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa" + "github.com/decred/dcrd/dcrec/secp256k1/v4" + decred_ecdsa "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa" ) // Ecrecover returns the uncompressed public key that created the given signature. @@ -39,16 +39,16 @@ func Ecrecover(hash, sig []byte) ([]byte, error) { return bytes, err } -func sigToPub(hash, sig []byte) (*btcec.PublicKey, error) { +func sigToPub(hash, sig []byte) (*secp256k1.PublicKey, error) { if len(sig) != SignatureLength { return nil, errors.New("invalid signature") } - // Convert to btcec input format with 'recovery id' v at the beginning. + // Convert to secp256k1 input format with 'recovery id' v at the beginning. btcsig := make([]byte, SignatureLength) btcsig[0] = sig[RecoveryIDOffset] + 27 copy(btcsig[1:], sig) - pub, _, err := btc_ecdsa.RecoverCompact(btcsig, hash) + pub, _, err := decred_ecdsa.RecoverCompact(btcsig, hash) return pub, err } @@ -82,13 +82,13 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) { if prv.Curve != S256() { return nil, errors.New("private key curve is not secp256k1") } - // ecdsa.PrivateKey -> btcec.PrivateKey - var priv btcec.PrivateKey + // ecdsa.PrivateKey -> secp256k1.PrivateKey + var priv secp256k1.PrivateKey if overflow := priv.Key.SetByteSlice(prv.D.Bytes()); overflow || priv.Key.IsZero() { return nil, errors.New("invalid private key") } defer priv.Zero() - sig := btc_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey + sig := decred_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey // Convert to Ethereum signature format with 'recovery id' v at the end. v := sig[0] - 27 copy(sig, sig[1:]) @@ -103,19 +103,19 @@ func VerifySignature(pubkey, hash, signature []byte) bool { if len(signature) != 64 { return false } - var r, s btcec.ModNScalar + var r, s secp256k1.ModNScalar if r.SetByteSlice(signature[:32]) { return false // overflow } if s.SetByteSlice(signature[32:]) { return false } - sig := btc_ecdsa.NewSignature(&r, &s) - key, err := btcec.ParsePubKey(pubkey) + sig := decred_ecdsa.NewSignature(&r, &s) + key, err := secp256k1.ParsePubKey(pubkey) if err != nil { return false } - // Reject malleable signatures. libsecp256k1 does this check but btcec doesn't. + // Reject malleable signatures. libsecp256k1 does this check but decred doesn't. if s.IsOverHalfOrder() { return false } @@ -127,7 +127,7 @@ func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) { if len(pubkey) != 33 { return nil, errors.New("invalid compressed public key length") } - key, err := btcec.ParsePubKey(pubkey) + key, err := secp256k1.ParsePubKey(pubkey) if err != nil { return nil, err } @@ -148,20 +148,20 @@ func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) { // when constructing a PrivateKey. func CompressPubkey(pubkey *ecdsa.PublicKey) []byte { // NOTE: the coordinates may be validated with - // btcec.ParsePubKey(FromECDSAPub(pubkey)) - var x, y btcec.FieldVal + // secp256k1.ParsePubKey(FromECDSAPub(pubkey)) + var x, y secp256k1.FieldVal x.SetByteSlice(pubkey.X.Bytes()) y.SetByteSlice(pubkey.Y.Bytes()) - return btcec.NewPublicKey(&x, &y).SerializeCompressed() + return secp256k1.NewPublicKey(&x, &y).SerializeCompressed() } // S256 returns an instance of the secp256k1 curve. func S256() EllipticCurve { - return btCurve{btcec.S256()} + return btCurve{secp256k1.S256()} } type btCurve struct { - *btcec.KoblitzCurve + *secp256k1.KoblitzCurve } // Marshal converts a point given as (x, y) into a byte slice. diff --git a/go.mod b/go.mod index 57fa810ed42d..3b58b4c90cdc 100644 --- a/go.mod +++ b/go.mod @@ -41,10 +41,10 @@ require ( ) require ( - github.com/btcsuite/btcd/btcec/v2 v2.3.4 github.com/consensys/gnark-crypto v0.10.0 github.com/crate-crypto/go-kzg-4844 v0.7.0 github.com/deckarep/golang-set v1.8.0 + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 github.com/ethereum/c-kzg-4844 v0.4.0 github.com/go-yaml/yaml v2.1.0+incompatible @@ -64,7 +64,6 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/deepmap/oapi-codegen v1.6.0 // indirect github.com/dlclark/regexp2 v1.10.0 // indirect github.com/go-ole/go-ole v1.2.5 // indirect diff --git a/go.sum b/go.sum index eefee5f64b33..baa6733abe5e 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,6 @@ github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 h1:Eey/GGQ/E5Xp1P2Lyx1qj007hLZfbi0+CoVeJruGCtI= github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= -github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= -github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= diff --git a/tests/fuzzers/secp256k1/secp_test.go b/tests/fuzzers/secp256k1/secp_test.go index e8b655669c53..9b7d7491381a 100644 --- a/tests/fuzzers/secp256k1/secp_test.go +++ b/tests/fuzzers/secp256k1/secp_test.go @@ -21,7 +21,7 @@ import ( "testing" "github.com/XinFinOrg/XDPoSChain/crypto/secp256k1" - "github.com/btcsuite/btcd/btcec/v2" + dcred_secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4" ) func TestFuzzer(t *testing.T) { @@ -38,7 +38,7 @@ func Fuzz(f *testing.F) { func fuzz(dataP1, dataP2 []byte) { var ( curveA = secp256k1.S256() - curveB = btcec.S256() + curveB = dcred_secp256k1.S256() ) // first point x1, y1 := curveB.ScalarBaseMult(dataP1) From e581093ce18590b8253fca23d59e5f389963411d Mon Sep 17 00:00:00 2001 From: kevaundray Date: Wed, 23 Oct 2024 07:11:25 +0100 Subject: [PATCH 476/479] crypto, tests/fuzzers: add gnark bn254 precompile methods for fuzzing (#30585) Makes the gnark precompile methods more amenable to fuzzing --- crypto/bn256/gnark/g1.go | 51 +++++++++++++++++++++ crypto/bn256/gnark/g2.go | 38 ++++++++++++++++ crypto/bn256/gnark/gt.go | 65 +++++++++++++++++++++++++++ crypto/bn256/gnark/pairing.go | 73 +++++++++++++++++++++++++++++++ tests/fuzzers/bn256/bn256_fuzz.go | 64 ++++++++++++++------------- 5 files changed, 261 insertions(+), 30 deletions(-) create mode 100644 crypto/bn256/gnark/g1.go create mode 100644 crypto/bn256/gnark/g2.go create mode 100644 crypto/bn256/gnark/gt.go create mode 100644 crypto/bn256/gnark/pairing.go diff --git a/crypto/bn256/gnark/g1.go b/crypto/bn256/gnark/g1.go new file mode 100644 index 000000000000..2f933dd53601 --- /dev/null +++ b/crypto/bn256/gnark/g1.go @@ -0,0 +1,51 @@ +package bn256 + +import ( + "math/big" + + "github.com/consensys/gnark-crypto/ecc/bn254" +) + +// G1 is the affine representation of a G1 group element. +// +// Since this code is used for precompiles, using Jacobian +// points are not beneficial because there are no intermediate +// points to allow us to save on inversions. +// +// Note: We also use this struct so that we can conform to the existing API +// that the precompiles want. +type G1 struct { + inner bn254.G1Affine +} + +// Add adds `a` and `b` together, storing the result in `g` +func (g *G1) Add(a, b *G1) { + g.inner.Add(&a.inner, &b.inner) +} + +// ScalarMult computes the scalar multiplication between `a` and +// `scalar`, storing the result in `g` +func (g *G1) ScalarMult(a *G1, scalar *big.Int) { + g.inner.ScalarMultiplication(&a.inner, scalar) +} + +// Unmarshal deserializes `buf` into `g` +// +// Note: whether the deserialization is of a compressed +// or an uncompressed point, is encoded in the bytes. +// +// For our purpose, the point will always be serialized +// as uncompressed, ie 64 bytes. +// +// This method also checks whether the point is on the +// curve and in the prime order subgroup. +func (g *G1) Unmarshal(buf []byte) (int, error) { + return g.inner.SetBytes(buf) +} + +// Marshal serializes the point into a byte slice. +// +// Note: The point is serialized as uncompressed. +func (p *G1) Marshal() []byte { + return p.inner.Marshal() +} diff --git a/crypto/bn256/gnark/g2.go b/crypto/bn256/gnark/g2.go new file mode 100644 index 000000000000..205373a59194 --- /dev/null +++ b/crypto/bn256/gnark/g2.go @@ -0,0 +1,38 @@ +package bn256 + +import ( + "github.com/consensys/gnark-crypto/ecc/bn254" +) + +// G2 is the affine representation of a G2 group element. +// +// Since this code is used for precompiles, using Jacobian +// points are not beneficial because there are no intermediate +// points and G2 in particular is only used for the pairing input. +// +// Note: We also use this struct so that we can conform to the existing API +// that the precompiles want. +type G2 struct { + inner bn254.G2Affine +} + +// Unmarshal deserializes `buf` into `g` +// +// Note: whether the deserialization is of a compressed +// or an uncompressed point, is encoded in the bytes. +// +// For our purpose, the point will always be serialized +// as uncompressed, ie 128 bytes. +// +// This method also checks whether the point is on the +// curve and in the prime order subgroup. +func (g *G2) Unmarshal(buf []byte) (int, error) { + return g.inner.SetBytes(buf) +} + +// Marshal serializes the point into a byte slice. +// +// Note: The point is serialized as uncompressed. +func (g *G2) Marshal() []byte { + return g.inner.Marshal() +} diff --git a/crypto/bn256/gnark/gt.go b/crypto/bn256/gnark/gt.go new file mode 100644 index 000000000000..c30022c5f898 --- /dev/null +++ b/crypto/bn256/gnark/gt.go @@ -0,0 +1,65 @@ +package bn256 + +import ( + "fmt" + "math/big" + + "github.com/consensys/gnark-crypto/ecc/bn254" +) + +// GT is the affine representation of a GT field element. +// +// Note: GT is not explicitly used in mainline code. +// It is needed for fuzzing. +type GT struct { + inner bn254.GT +} + +// Pair compute the optimal Ate pairing between a G1 and +// G2 element. +// +// Note: This method is not explicitly used in mainline code. +// It is needed for fuzzing. It should also be noted, +// that the output of this function may not match other +func Pair(a_ *G1, b_ *G2) *GT { + a := a_.inner + b := b_.inner + + pairingOutput, err := bn254.Pair([]bn254.G1Affine{a}, []bn254.G2Affine{b}) + + if err != nil { + // Since this method is only called during fuzzing, it is okay to panic here. + // We do not return an error to match the interface of the other bn256 libraries. + panic(fmt.Sprintf("gnark/bn254 encountered error: %v", err)) + } + + return >{ + inner: pairingOutput, + } +} + +// Unmarshal deserializes `buf` into `g` +// +// Note: This method is not explicitly used in mainline code. +// It is needed for fuzzing. +func (g *GT) Unmarshal(buf []byte) error { + return g.inner.SetBytes(buf) +} + +// Marshal serializes the point into a byte slice. +// +// Note: This method is not explicitly used in mainline code. +// It is needed for fuzzing. +func (g *GT) Marshal() []byte { + bytes := g.inner.Bytes() + return bytes[:] +} + +// Exp raises `base` to the power of `exponent` +// +// Note: This method is not explicitly used in mainline code. +// It is needed for fuzzing. +func (g *GT) Exp(base GT, exponent *big.Int) *GT { + g.inner.Exp(base.inner, exponent) + return g +} diff --git a/crypto/bn256/gnark/pairing.go b/crypto/bn256/gnark/pairing.go new file mode 100644 index 000000000000..39e8a657f466 --- /dev/null +++ b/crypto/bn256/gnark/pairing.go @@ -0,0 +1,73 @@ +package bn256 + +import ( + "github.com/consensys/gnark-crypto/ecc/bn254" +) + +// Computes the following relation: ∏ᵢ e(Pᵢ, Qᵢ) =? 1 +// +// To explain why gnark returns a (bool, error): +// +// - If the function `e` does not return a result then internally +// an error is returned. +// - If `e` returns a result, then error will be nil, +// but if this value is not `1` then the boolean value will be false +// +// We therefore check for an error, and return false if its non-nil and +// then return the value of the boolean if not. +func PairingCheck(a_ []*G1, b_ []*G2) bool { + a := getInnerG1s(a_) + b := getInnerG2s(b_) + + // Assume that len(a) == len(b) + // + // The pairing function will return + // false, if this is not the case. + size := len(a) + + // Check if input is empty -- gnark will + // return false on an empty input, however + // the ossified behavior is to return true + // on an empty input, so we add this if statement. + if size == 0 { + return true + } + + ok, err := bn254.PairingCheck(a, b) + if err != nil { + return false + } + return ok +} + +// getInnerG1s gets the inner gnark G1 elements. +// +// These methods are used for two reasons: +// +// - We use a new type `G1`, so we need to convert from +// []*G1 to []*bn254.G1Affine +// - The gnark API accepts slices of values and not slices of +// pointers to values, so we need to return []bn254.G1Affine +// instead of []*bn254.G1Affine. +func getInnerG1s(pointerSlice []*G1) []bn254.G1Affine { + gnarkValues := make([]bn254.G1Affine, 0, len(pointerSlice)) + for _, ptr := range pointerSlice { + if ptr != nil { + gnarkValues = append(gnarkValues, ptr.inner) + } + } + return gnarkValues +} + +// getInnerG2s gets the inner gnark G2 elements. +// +// The rationale for this method is the same as `getInnerG1s`. +func getInnerG2s(pointerSlice []*G2) []bn254.G2Affine { + gnarkValues := make([]bn254.G2Affine, 0, len(pointerSlice)) + for _, ptr := range pointerSlice { + if ptr != nil { + gnarkValues = append(gnarkValues, ptr.inner) + } + } + return gnarkValues +} diff --git a/tests/fuzzers/bn256/bn256_fuzz.go b/tests/fuzzers/bn256/bn256_fuzz.go index 972eaf4b5ebe..02ec37138dfb 100644 --- a/tests/fuzzers/bn256/bn256_fuzz.go +++ b/tests/fuzzers/bn256/bn256_fuzz.go @@ -23,11 +23,11 @@ import ( "math/big" cloudflare "github.com/XinFinOrg/XDPoSChain/crypto/bn256/cloudflare" + gnark "github.com/XinFinOrg/XDPoSChain/crypto/bn256/gnark" google "github.com/XinFinOrg/XDPoSChain/crypto/bn256/google" - "github.com/consensys/gnark-crypto/ecc/bn254" ) -func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1, *bn254.G1Affine) { +func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1, *gnark.G1) { _, xc, err := cloudflare.RandomG1(input) if err != nil { // insufficient input @@ -37,14 +37,14 @@ func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1, *bn254.G1Affine) if _, err := xg.Unmarshal(xc.Marshal()); err != nil { panic(fmt.Sprintf("Could not marshal cloudflare -> google: %v", err)) } - xs := new(bn254.G1Affine) - if err := xs.Unmarshal(xc.Marshal()); err != nil { + xs := new(gnark.G1) + if _, err := xs.Unmarshal(xc.Marshal()); err != nil { panic(fmt.Sprintf("Could not marshal cloudflare -> gnark: %v", err)) } return xc, xg, xs } -func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2, *bn254.G2Affine) { +func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2, *gnark.G2) { _, xc, err := cloudflare.RandomG2(input) if err != nil { // insufficient input @@ -54,14 +54,14 @@ func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2, *bn254.G2Affine) if _, err := xg.Unmarshal(xc.Marshal()); err != nil { panic(fmt.Sprintf("Could not marshal cloudflare -> google: %v", err)) } - xs := new(bn254.G2Affine) - if err := xs.Unmarshal(xc.Marshal()); err != nil { + xs := new(gnark.G2) + if _, err := xs.Unmarshal(xc.Marshal()); err != nil { panic(fmt.Sprintf("Could not marshal cloudflare -> gnark: %v", err)) } return xc, xg, xs } -// fuzzAdd fuzzez bn256 addition between the Google and Cloudflare libraries. +// fuzzAdd fuzzes bn256 addition between the Google, Cloudflare and Gnark libraries. func fuzzAdd(data []byte) int { input := bytes.NewReader(data) xc, xg, xs := getG1Points(input) @@ -72,7 +72,7 @@ func fuzzAdd(data []byte) int { if yc == nil { return 0 } - // Ensure both libs can parse the second curve point + // Ensure libs can parse the second curve point // Add the two points and ensure they result in the same output rc := new(cloudflare.G1) rc.Add(xc, yc) @@ -80,9 +80,8 @@ func fuzzAdd(data []byte) int { rg := new(google.G1) rg.Add(xg, yg) - tmpX := new(bn254.G1Jac).FromAffine(xs) - tmpY := new(bn254.G1Jac).FromAffine(ys) - rs := new(bn254.G1Affine).FromJacobian(tmpX.AddAssign(tmpY)) + rs := new(gnark.G1) + rs.Add(xs, ys) if !bytes.Equal(rc.Marshal(), rg.Marshal()) { panic("add mismatch: cloudflare/google") @@ -94,8 +93,8 @@ func fuzzAdd(data []byte) int { return 1 } -// fuzzMul fuzzez bn256 scalar multiplication between the Google and Cloudflare -// libraries. +// fuzzMul fuzzes bn256 scalar multiplication between the Google, Cloudflare +// and Gnark libraries. func fuzzMul(data []byte) int { input := bytes.NewReader(data) pc, pg, ps := getG1Points(input) @@ -122,15 +121,13 @@ func fuzzMul(data []byte) int { rg := new(google.G1) rg.ScalarMult(pg, new(big.Int).SetBytes(buf)) - rs := new(bn254.G1Jac) - psJac := new(bn254.G1Jac).FromAffine(ps) - rs.ScalarMultiplication(psJac, new(big.Int).SetBytes(buf)) - rsAffine := new(bn254.G1Affine).FromJacobian(rs) + rs := new(gnark.G1) + rs.ScalarMult(ps, new(big.Int).SetBytes(buf)) if !bytes.Equal(rc.Marshal(), rg.Marshal()) { panic("scalar mul mismatch: cloudflare/google") } - if !bytes.Equal(rc.Marshal(), rsAffine.Marshal()) { + if !bytes.Equal(rc.Marshal(), rs.Marshal()) { panic("scalar mul mismatch: cloudflare/gnark") } return 1 @@ -150,17 +147,26 @@ func fuzzPair(data []byte) int { // Pair the two points and ensure they result in the same output clPair := cloudflare.Pair(pc, tc).Marshal() gPair := google.Pair(pg, tg).Marshal() + sPair := gnark.Pair(ps, ts).Marshal() + if !bytes.Equal(clPair, gPair) { panic("pairing mismatch: cloudflare/google") } - cPair, err := bn254.Pair([]bn254.G1Affine{*ps}, []bn254.G2Affine{*ts}) - if err != nil { - panic(fmt.Sprintf("gnark/bn254 encountered error: %v", err)) + + normalizedClPair := normalizeGTToGnark(clPair).Marshal() + if !bytes.Equal(normalizedClPair, sPair) { + panic("pairing mismatch: cloudflare/gnark") } - // gnark uses a different pairing algorithm which might produce - // different but also correct outputs, we need to scale the output by s + return 1 +} +// normalizeGTToGnark scales a Cloudflare/Google GT element by `s` +// so that it can be compared with a gnark GT point. +// +// For the definition of `s` see 3.5 in https://eprint.iacr.org/2015/192.pdf +func normalizeGTToGnark(cloudflareOrGoogleGT []byte) *gnark.GT { + // Compute s = 2*u(6*u^2 + 3*u + 1) u, _ := new(big.Int).SetString("0x44e992b44a6909f1", 0) u_exp2 := new(big.Int).Exp(u, big.NewInt(2), nil) // u^2 u_6_exp2 := new(big.Int).Mul(big.NewInt(6), u_exp2) // 6*u^2 @@ -170,14 +176,12 @@ func fuzzPair(data []byte) int { u_2 := new(big.Int).Mul(big.NewInt(2), u) // 2*u s := u_2.Mul(u_2, inner) // 2*u(6*u^2 + 3*u + 1) - gRes := new(bn254.GT) - if err := gRes.SetBytes(clPair); err != nil { + // Scale the Cloudflare/Google GT element by `s` + gRes := new(gnark.GT) + if err := gRes.Unmarshal(cloudflareOrGoogleGT); err != nil { panic(err) } gRes = gRes.Exp(*gRes, s) - if !bytes.Equal(cPair.Marshal(), gRes.Marshal()) { - panic("pairing mismatch: cloudflare/gnark") - } - return 1 + return gRes } From 9ca3d10001401b02f07004b454cea97dfab2dd78 Mon Sep 17 00:00:00 2001 From: gitglorythegreat Date: Thu, 2 Jan 2025 21:04:06 +0800 Subject: [PATCH 477/479] crypto/bn256: fix MulScalar (#30974) The `a` parameter should be used in the `MulScalar` function. The upstream cloudflare and google repos have already merged fixes. Reference: * https://cs.opensource.google/go/x/crypto/+/8d7daa0c54b357f3071e11eaef7efc4e19a417e2 * https://github.com/cloudflare/bn256/pull/33 --- crypto/bn256/cloudflare/gfp12.go | 4 ++-- crypto/bn256/google/gfp12.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crypto/bn256/cloudflare/gfp12.go b/crypto/bn256/cloudflare/gfp12.go index 93fb368a7bf0..767350701f2d 100644 --- a/crypto/bn256/cloudflare/gfp12.go +++ b/crypto/bn256/cloudflare/gfp12.go @@ -105,8 +105,8 @@ func (e *gfP12) Mul(a, b *gfP12) *gfP12 { } func (e *gfP12) MulScalar(a *gfP12, b *gfP6) *gfP12 { - e.x.Mul(&e.x, b) - e.y.Mul(&e.y, b) + e.x.Mul(&a.x, b) + e.y.Mul(&a.y, b) return e } diff --git a/crypto/bn256/google/gfp12.go b/crypto/bn256/google/gfp12.go index f084eddf2126..2b0151ebcc49 100644 --- a/crypto/bn256/google/gfp12.go +++ b/crypto/bn256/google/gfp12.go @@ -125,8 +125,8 @@ func (e *gfP12) Mul(a, b *gfP12, pool *bnPool) *gfP12 { } func (e *gfP12) MulScalar(a *gfP12, b *gfP6, pool *bnPool) *gfP12 { - e.x.Mul(e.x, b, pool) - e.y.Mul(e.y, b, pool) + e.x.Mul(a.x, b, pool) + e.y.Mul(a.y, b, pool) return e } From 7091b934a52ddfc2d57586934f38d02e26f679bd Mon Sep 17 00:00:00 2001 From: Wanwiset Peerapatanapokin Date: Fri, 10 Jan 2025 15:44:00 +0700 Subject: [PATCH 478/479] ignore error and remove old function (#791) --- .github/workflows/ci.yml | 81 ++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 53 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ca0550c3db1..19d914017d70 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -116,24 +116,25 @@ jobs: echo "${{ secrets.KUBE_CONFIG }}" | base64 --decode > $HOME/.kube/config chmod 600 $HOME/.kube/config git_hash=$(git rev-parse --short "$GITHUB_SHA") - kubectl set image deployment/devnet1 devnet1=xinfinorg/devnet:dev-upgrade-${git_hash} - kubectl set image deployment/devnet2 devnet2=xinfinorg/devnet:dev-upgrade-${git_hash} - kubectl set image deployment/devnet3 devnet3=xinfinorg/devnet:dev-upgrade-${git_hash} - kubectl set image deployment/devnet4 devnet4=xinfinorg/devnet:dev-upgrade-${git_hash} - kubectl set image deployment/devnet5 devnet5=xinfinorg/devnet:dev-upgrade-${git_hash} - kubectl set image deployment/devnet6 devnet6=xinfinorg/devnet:dev-upgrade-${git_hash} - kubectl set image deployment/devnet7 devnet7=xinfinorg/devnet:dev-upgrade-${git_hash} - kubectl set image deployment/devnet8 devnet8=xinfinorg/devnet:dev-upgrade-${git_hash} - kubectl set image deployment/devnet9 devnet9=xinfinorg/devnet:dev-upgrade-${git_hash} - kubectl set image deployment/devnet10 devnet10=xinfinorg/devnet:dev-upgrade-${git_hash} - kubectl set image deployment/devnet11 devnet11=xinfinorg/devnet:dev-upgrade-${git_hash} - kubectl set image deployment/devnet12 devnet12=xinfinorg/devnet:dev-upgrade-${git_hash} - kubectl set image deployment/devnet13 devnet13=xinfinorg/devnet:dev-upgrade-${git_hash} - kubectl set image deployment/devnet14 devnet14=xinfinorg/devnet:dev-upgrade-${git_hash} - kubectl set image deployment/devnet15 devnet15=xinfinorg/devnet:dev-upgrade-${git_hash} - kubectl set image deployment/devnet16 devnet16=xinfinorg/devnet:dev-upgrade-${git_hash} - kubectl set image deployment/devnet17 devnet17=xinfinorg/devnet:dev-upgrade-${git_hash} - kubectl set image deployment/devnet18 devnet18=xinfinorg/devnet:dev-upgrade-${git_hash} + kubectl set image deployment/devnet1 devnet1=xinfinorg/devnet:dev-upgrade-${git_hash} || + kubectl set image deployment/devnet2 devnet2=xinfinorg/devnet:dev-upgrade-${git_hash} || + kubectl set image deployment/devnet3 devnet3=xinfinorg/devnet:dev-upgrade-${git_hash} || + kubectl set image deployment/devnet4 devnet4=xinfinorg/devnet:dev-upgrade-${git_hash} || + kubectl set image deployment/devnet5 devnet5=xinfinorg/devnet:dev-upgrade-${git_hash} || + kubectl set image deployment/devnet6 devnet6=xinfinorg/devnet:dev-upgrade-${git_hash} || + kubectl set image deployment/devnet7 devnet7=xinfinorg/devnet:dev-upgrade-${git_hash} || + kubectl set image deployment/devnet8 devnet8=xinfinorg/devnet:dev-upgrade-${git_hash} || + kubectl set image deployment/devnet9 devnet9=xinfinorg/devnet:dev-upgrade-${git_hash} || + kubectl set image deployment/devnet10 devnet10=xinfinorg/devnet:dev-upgrade-${git_hash} || + kubectl set image deployment/devnet11 devnet11=xinfinorg/devnet:dev-upgrade-${git_hash} || + kubectl set image deployment/devnet12 devnet12=xinfinorg/devnet:dev-upgrade-${git_hash} || + kubectl set image deployment/devnet13 devnet13=xinfinorg/devnet:dev-upgrade-${git_hash} || + kubectl set image deployment/devnet14 devnet14=xinfinorg/devnet:dev-upgrade-${git_hash} || + kubectl set image deployment/devnet15 devnet15=xinfinorg/devnet:dev-upgrade-${git_hash} || + kubectl set image deployment/devnet16 devnet16=xinfinorg/devnet:dev-upgrade-${git_hash} || + kubectl set image deployment/devnet17 devnet17=xinfinorg/devnet:dev-upgrade-${git_hash} || + kubectl set image deployment/devnet18 devnet18=xinfinorg/devnet:dev-upgrade-${git_hash} || + echo done rpc_k8_apply: runs-on: ubuntu-latest @@ -151,43 +152,17 @@ jobs: echo "${{ secrets.KUBE_CONFIG }}" | base64 --decode > $HOME/.kube/config chmod 600 $HOME/.kube/config git_hash=$(git rev-parse --short "$GITHUB_SHA") - kubectl set image deployment/devnetrpc devnetrpc=xinfinorg/devnet:dev-upgrade-${git_hash} - kubectl set image deployment/testnetrpc testnetrpc=xinfinorg/devnet:dev-upgrade-${git_hash} - kubectl set image deployment/mainnetrpc mainnetrpc=xinfinorg/devnet:dev-upgrade-${git_hash} - - rpcnode_terraform_apply: + kubectl set image deployment/devnetrpc devnetrpc=xinfinorg/devnet:dev-upgrade-${git_hash} || + kubectl set image deployment/testnetrpc testnetrpc=xinfinorg/devnet:dev-upgrade-${git_hash} || + kubectl set image deployment/mainnetrpc mainnetrpc=xinfinorg/devnet:dev-upgrade-${git_hash} || + echo done + + devnet_send_notification: runs-on: ubuntu-latest + needs: devnet_k8_apply if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/') - needs: devnet_build_push - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} steps: - uses: actions/checkout@v4 - - name: Terraform Apply + - name: Send deployment notification run: | - cd cicd/terraform - terraform init ${{ env.tf_init_cli_options }} - terraform apply ${{ env.tf_apply_cli_options }} - - - name: Update RPC nodes image - uses: dawidd6/action-ansible-playbook@v2 - with: - playbook: playbooks/update-image.yaml - directory: ./cicd/ansible - key: ${{secrets.SSH_PRIVATE_KEY_DEVNET}} - options: | - --inventory inventory.yaml - --extra-vars network=ec2_rpcs - --extra-vars rpc_image=${{ needs.devnet_build_push.outputs.output1 }} - - # TODO Uncomment until new devnet - # devnet_send_notification: - # runs-on: ubuntu-latest - # needs: devnet_terraform_apply - # if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/') - # steps: - # - uses: actions/checkout@v4 - # - name: Send deployment notification - # run: | - # curl --location --request POST "66.94.98.186:8080/deploy?environment=devnet&service=xdc&version=${GITHUB_SHA}" + curl --location --request POST "66.94.98.186:8080/deploy?environment=devnet&service=xdc&version=${GITHUB_SHA}" From baea5d3da7b775acdcd496dc578271bea482584b Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Sat, 11 Jan 2025 16:58:59 +0800 Subject: [PATCH 479/479] build: support golangci-lint v1.63.4 --- .gitignore | 1 + .golangci.yml | 79 ++++++++++ Makefile | 18 ++- build/checksums.txt | 126 ++++++++++++++++ build/ci.go | 53 ++++--- go.mod | 2 + internal/build/archive.go | 296 +++++++++++++++++++++++++++++++++++++ internal/build/download.go | 151 +++++++++++++++++++ internal/build/gotool.go | 180 ++++++++++++++++++++++ internal/build/util.go | 39 ++++- 10 files changed, 924 insertions(+), 21 deletions(-) create mode 100644 .golangci.yml create mode 100644 build/checksums.txt create mode 100644 internal/build/archive.go create mode 100644 internal/build/download.go create mode 100644 internal/build/gotool.go diff --git a/.gitignore b/.gitignore index 1a85f2f558a2..3ee7e66bfe84 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ build/_vendor/pkg # used by the Makefile /build/_workspace/ +/build/cache/ /build/bin/ /XDC*.zip diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 000000000000..3f8fb3078236 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,79 @@ +# This file configures github.com/golangci/golangci-lint. + +run: + timeout: 20m + tests: true + +linters: + disable-all: true + enable: + - goimports + - gosimple + - govet + - ineffassign + - misspell + - unconvert + - typecheck + - unused + - staticcheck + - bidichk + - durationcheck + - copyloopvar + - whitespace + - revive # only certain checks enabled + - durationcheck + - gocheckcompilerdirectives + - reassign + - mirror + - tenv + ### linters we tried and will not be using: + ### + # - structcheck # lots of false positives + # - errcheck #lot of false positives + # - contextcheck + # - errchkjson # lots of false positives + # - errorlint # this check crashes + # - exhaustive # silly check + # - makezero # false positives + # - nilerr # several intentional + +linters-settings: + gofmt: + simplify: true + revive: + enable-all-rules: false + # here we enable specific useful rules + # see https://golangci-lint.run/usage/linters/#revive for supported rules + rules: + - name: receiver-naming + severity: warning + disabled: false + exclude: [""] + +issues: + # default is true. Enables skipping of directories: + # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ + exclude-dirs-use-default: true + exclude-files: + - core/genesis_alloc.go + exclude-rules: + - path: crypto/bn256/cloudflare/optate.go + linters: + - deadcode + - staticcheck + - path: crypto/bn256/ + linters: + - revive + - path: cmd/utils/flags.go + text: "SA1019: cfg.TxLookupLimit is deprecated: use 'TransactionHistory' instead." + - path: cmd/utils/flags.go + text: "SA1019: ethconfig.Defaults.TxLookupLimit is deprecated: use 'TransactionHistory' instead." + - path: internal/build/pgp.go + text: 'SA1019: "golang.org/x/crypto/openpgp" is deprecated: this package is unmaintained except for security fixes.' + - path: core/vm/contracts.go + text: 'SA1019: "golang.org/x/crypto/ripemd160" is deprecated: RIPEMD-160 is a legacy hash and should not be used for new applications.' + exclude: + - 'SA1019: event.TypeMux is deprecated: use Feed' + - 'SA1019: strings.Title is deprecated' + - 'SA1019: strings.Title has been deprecated since Go 1.18 and an alternative has been available since Go 1.0: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead.' + - 'SA1029: should not use built-in type string as key for value' diff --git a/Makefile b/Makefile index 3bd726906ce2..e92835b952a0 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,7 @@ +# This Makefile is meant to be used by people that do not usually work +# with Go source code. If you know what GOPATH is then you probably +# don't need to bother with make. + .PHONY: XDC XDC-cross evm all test clean .PHONY: XDC-linux XDC-linux-386 XDC-linux-amd64 XDC-linux-mips64 XDC-linux-mips64le .PHONY: XDC-darwin XDC-darwin-386 XDC-darwin-amd64 @@ -5,11 +9,12 @@ GOBIN = $(shell pwd)/build/bin GOFMT = gofmt GO ?= 1.22.10 +GORUN = go run GO_PACKAGES = . GO_FILES := $(shell find $(shell go list -f '{{.Dir}}' $(GO_PACKAGES)) -name \*.go) - GIT = git +#? XDC: Build XDC. XDC: go run build/ci.go install ./cmd/XDC @echo "Done building." @@ -41,15 +46,26 @@ puppeth: @echo "Done building." @echo "Run \"$(GOBIN)/puppeth\" to launch puppeth." +#? all: Build all packages and executables. all: go run build/ci.go install +#? test: Run the tests. test: all go run build/ci.go test +#? lint: Run certain pre-selected linters. +lint: ## Run linters. + $(GORUN) build/ci.go lint + +#? clean: Clean go cache, built executables, and the auto generated folder. clean: rm -fr build/_workspace/pkg/ $(GOBIN)/* +#? fmt: Ensure consistent code formatting. +fmt: + gofmt -s -w $(shell find . -name "*.go") + # Cross Compilation Targets (xgo) XDC-cross: XDC-windows-amd64 XDC-darwin-amd64 XDC-linux diff --git a/build/checksums.txt b/build/checksums.txt new file mode 100644 index 000000000000..1484e6499f65 --- /dev/null +++ b/build/checksums.txt @@ -0,0 +1,126 @@ +# This file contains sha256 checksums of optional build dependencies. + +# version:spec-tests 2.1.0 +# https://github.com/ethereum/execution-spec-tests/releases +# https://github.com/ethereum/execution-spec-tests/releases/download/v2.1.0/ +ca89c76851b0900bfcc3cbb9a26cbece1f3d7c64a3bed38723e914713290df6c fixtures_develop.tar.gz + +# version:golang 1.22.10 +# https://go.dev/dl/ +1e94fd48be750d1fafb4d9b3b6dd31a6e9d2735d339bf2462bc97b64ca4c1037 go1.22.10.src.tar.gz +dd2c4ac3702658c2c20e3a8b394da1917d86156b2cb4312c9d2f657f80067874 go1.22.10.darwin-amd64.tar.gz +9d37bf96bbdd1669f1ab771c98ac7695f1b49df3c5c2bf10b699950f0fc2f52b go1.22.10.darwin-amd64.pkg +21cf49415ffe0755b45f2b63e75d136528a32f7bb7bdd0166f51d22a03eb0a3f go1.22.10.darwin-arm64.tar.gz +746558a1e677a469b297ed885d94d0cdc3410d2153f49f0109c833f5283e1ad1 go1.22.10.darwin-arm64.pkg +2ae9f00e9621489b75494fa2b8abfc5d09e0cae6effdd4c13867957ad2e4deba go1.22.10.linux-386.tar.gz +736ce492a19d756a92719a6121226087ccd91b652ed5caec40ad6dbfb2252092 go1.22.10.linux-amd64.tar.gz +5213c5e32fde3bd7da65516467b7ffbfe40d2bb5a5f58105e387eef450583eec go1.22.10.linux-arm64.tar.gz +a7bbbc80fe736269820bbdf3555e91ada5d18a5cde2276aac3b559bc1d52fc70 go1.22.10.linux-armv6l.tar.gz +20019b2e60dd0cdf63e4ec26852c1c015c1a27580b32a512b4be33a2539113ae go1.22.10.windows-386.zip +d6f7c00af6d54f09bf5b137cda0b34f75b1ac6fb95abe5a948240110f99d8fad go1.22.10.windows-386.msi +da66f107a0f4959f4615bede230c6bf145a6f01252c6d1ff2b107e293ba339df go1.22.10.windows-amd64.zip +454c45de32d325f175dc9ccd262944aec0d24b12c47a8f6d4000adad9f271adc go1.22.10.windows-amd64.msi +46af3f2506a8887ed1de0e38244730a2ba944cfed7804d6baa9773790d2a294d go1.22.10.aix-ppc64.tar.gz +1684ae4c10c4d30e6f4950fbb71d9cc096d0541e5cf769b3195e91f475249d9a go1.22.10.dragonfly-amd64.tar.gz +f82f5d194114963693e0f51fd56d55d8417ca556438062f2b0df608473b62837 go1.22.10.freebsd-386.tar.gz +cce9da240870a4430c5cf1227bcf29d37575043ff16f7982a69f1139c6f564b5 go1.22.10.freebsd-amd64.tar.gz +abae388d0d42563a242db0d405172cb73e09236f68000ff90c2a327ec8c5764c go1.22.10.freebsd-arm64.tar.gz +7c9c8fe30cbabbb4fb597f0f0ad1279fd2b320bc70831eba4c207b55ad46931d go1.22.10.freebsd-arm.tar.gz +d6f25fd79e17b84d1b61bec3e2fdffc47377b28b51a04b6bdbeac0199313e059 go1.22.10.freebsd-riscv64.tar.gz +4a1121d8331208cb614c6f0474b0e4fa3c3023c5e8a88fb8d37f7e3454589aa2 go1.22.10.illumos-amd64.tar.gz +0be34dbc69726b52414e0283736f997fee477379ebff66cebd7d8c35f4f64f9d go1.22.10.linux-loong64.tar.gz +bb7d7e99db7ee70063cb57bb7395c392b8b5ed87f37d733a1c91de935c70bb91 go1.22.10.linux-mips.tar.gz +c7f0571410297cb29e52d10fed7a2a21aeaeabb9539d0c04a6d778adf0fe7f1b go1.22.10.linux-mips64.tar.gz +e66c440c03dd19bf8423034cbde7f6813321beb18d3fcf2ef234c13a25467952 go1.22.10.linux-mips64le.tar.gz +b4e0061f62a9c1f874893aa4951a4883c19762a9dd64c5710554ec5a7aaf311a go1.22.10.linux-mipsle.tar.gz +4192158cdedc6a124aa32a099cc6bebebabf1f4d380c46c5e36ea52c30a3888b go1.22.10.linux-ppc64.tar.gz +db05b9838f69d741fb9a5301220b1a62014aee025b0baf341aba3d280087b981 go1.22.10.linux-ppc64le.tar.gz +aef9b186c1b9b58c0472dbf54978f97682852a91b2e8d6bf354e59ba9c24438a go1.22.10.linux-riscv64.tar.gz +4ab2286adb096576771801b5099760b1d625fd7b44080449151a4d9b21303672 go1.22.10.linux-s390x.tar.gz +32e66f6ae2082cce98ccb557f54965ae5452e1b705470ae403446062938db1d5 go1.22.10.netbsd-386.tar.gz +e876962e6032175d049259559978c10af578dce506ffb064ce61e60a5b5308cb go1.22.10.netbsd-amd64.tar.gz +b95696f92afe5062d4e17c48cf0756699a7522fbfd1b034bfb1eb2b636d34163 go1.22.10.netbsd-arm64.tar.gz +9d5e08939b95dca7d102eae7a832813ee12337187786f548848b10503ddc80cc go1.22.10.netbsd-arm.tar.gz +5c4294fed0bf981f81b8be979e955516d0a78019990f2f7462246ef4bb3960e5 go1.22.10.openbsd-386.tar.gz +c72b4d4f4cedce007e4d52a0e496169b0739b3bbe7fa13bc1322fe3b79d55221 go1.22.10.openbsd-amd64.tar.gz +1a90e0fb2e6ca881b78692594f0e29834f738c593ea627a631f8e81cdddfdc32 go1.22.10.openbsd-arm64.tar.gz +f6153ebd844ccc21ec8420fecccf9214dc0a7a572b28234ec1e00e04b0f438bb go1.22.10.openbsd-arm.tar.gz +5fe1abee2805334404da9c4e2aedb93764b31d6de3fe9d3cb49a2fd844992f68 go1.22.10.openbsd-ppc64.tar.gz +834fe42edf63a67b7b6c57fcfd1e8b159842f3615bfbc975b5ceed994db7e834 go1.22.10.plan9-386.tar.gz +9fa2282fccb668ef9e3cdb1fa3f0128994224f95267f54640779049df3bb3a6c go1.22.10.plan9-amd64.tar.gz +d66acc86e00ead41966bb599c26fda983a686232040996a307262e71b3576c48 go1.22.10.plan9-arm.tar.gz +40779a36f3f78b2ac1f3da5e6336f4fa24996d2412d5ca5685077103c8d3bbe2 go1.22.10.solaris-amd64.tar.gz +974656452fd7d104f34ee6e8ac92bb7431af84a1ce55226d9af485cb9ec23dd5 go1.22.10.windows-arm64.zip +f62491c8bbc811ddbdd25ee34af5bbd200f3d6086c1a3ebb64e435ce3b346a97 go1.22.10.windows-arm64.msi +de1fb78d98ebd054a6fce5b71d1dcd1d49a68db75e0b6fd3d98ae5818d81fdba go1.22.10.windows-arm.zip +5523272bb157db06311a907289c92caf59347d4b6721d407486256e61c593dc9 go1.22.10.windows-arm.msi + +# version:golangci 1.63.4 +# https://github.com/golangci/golangci-lint/releases/ +# https://github.com/golangci/golangci-lint/releases/download/v1.63.4/ +878d017cc360e4fb19510d39852c8189852e3c48e7ce0337577df73507c97d68 golangci-lint-1.63.4-darwin-amd64.tar.gz +a2b630c2ac8466393f0ccbbede4462387b6c190697a70bc2298c6d2123f21bbf golangci-lint-1.63.4-darwin-arm64.tar.gz +8938b74aa92888e561a1c5a4c175110b92f84e7d24733703e6d9ebc39e9cd5f8 golangci-lint-1.63.4-freebsd-386.tar.gz +054903339d620df2e760b978920100986e3b03bcb058f669d520a71dac9c34ed golangci-lint-1.63.4-freebsd-amd64.tar.gz +a19d499f961a02608348e8b626537a88edfaab6e1b6534f1eff742b5d6d750e4 golangci-lint-1.63.4-freebsd-armv6.tar.gz +00d616f0fb275b780ce4d26604bdd7fdbfe6bc9c63acd5a0b31498e1f7511108 golangci-lint-1.63.4-freebsd-armv7.tar.gz +d453688e0eabded3c1a97ff5a2777bb0df5a18851efdaaaf6b472e3e5713c33e golangci-lint-1.63.4-illumos-amd64.tar.gz +6b1bec847fc9f347d53712d05606a49d55d0e3b5c1bacadfed2393f3503de0e9 golangci-lint-1.63.4-linux-386.tar.gz +01abb14a4df47b5ca585eff3c34b105023cba92ec34ff17212dbb83855581690 golangci-lint-1.63.4-linux-amd64.tar.gz +51f0c79d19a92353e0465fb30a4901a0644a975d34e6f399ad2eebc0160bbb24 golangci-lint-1.63.4-linux-arm64.tar.gz +8d0a43f41e8424fbae10f7aa2dc29999f98112817c6dba63d7dc76832940a673 golangci-lint-1.63.4-linux-armv6.tar.gz +1045a047b31e9302c9160c7b0f199f4ac1bd02a1b221a2d9521bd3507f0cf671 golangci-lint-1.63.4-linux-armv7.tar.gz +933fe10ab50ce3bb0806e15a4ae69fe20f0549abf91dea0161236000ca706e67 golangci-lint-1.63.4-linux-loong64.tar.gz +45798630cbad5642862766051199fa862ef3c33d569cab12f01cac4f68e2ddd5 golangci-lint-1.63.4-linux-mips64.tar.gz +86ae25335ddb24975d2c915c1af0c7fad70dce99d0b4614fa4bee392de714aa2 golangci-lint-1.63.4-linux-mips64le.tar.gz +33dabd11aaba4b602938da98bcf49aabab55019557e0115cdc3dbcc3009768fa golangci-lint-1.63.4-linux-ppc64le.tar.gz +4e7a81230a663bcdf30bba5689ce96040abc76994dbc2003dce32c8dca8c06f3 golangci-lint-1.63.4-linux-riscv64.tar.gz +21370b49c7c47f4d9b8f982c952f940b01e65710174c3b4dad7b6452d58f92ec golangci-lint-1.63.4-linux-s390x.tar.gz +255866a6464c7e11bb7edd8e6e6ad54f11e1f01b82ba9ca229698ac788cd9724 golangci-lint-1.63.4-netbsd-386.tar.gz +2798c040ac658bda97224f204795199c81ac97bb207b21c02b664aaed380d5d2 golangci-lint-1.63.4-netbsd-amd64.tar.gz +b910eecffd0064103837e7e1abe870deb8ade22331e6dffe319f430d49399c8e golangci-lint-1.63.4-netbsd-arm64.tar.gz +df2693ef37147b457c3e2089614537dd2ae2e18e53641e756a5b404f4c72d3fa golangci-lint-1.63.4-netbsd-armv6.tar.gz +a28a533366974bd7834c4516cd6075bff3419a508d1ed7aa63ae8182768b352e golangci-lint-1.63.4-netbsd-armv7.tar.gz +368932775fb5c620b324dabf018155f3365f5e33c5af5b26e9321db373f96eea golangci-lint-1.63.4-windows-386.zip +184d13c2b8f5441576bec2a0d8ba7b2d45445595cf796b879a73bcc98c39f8c1 golangci-lint-1.63.4-windows-amd64.zip +4fabf175d5b05ef0858ded49527948eebac50e9093814979fd84555a75fb80a6 golangci-lint-1.63.4-windows-arm64.zip +e92be3f3ff30d4a849fb4b9a4c8d56837dee45269cb405a3ecad52fa034c781b golangci-lint-1.63.4-windows-armv6.zip +c71d348653b8f7fbb109bb10c1a481722bc6b0b2b6e731b897f99ac869f7653e golangci-lint-1.63.4-windows-armv7.zip + +# This is the builder on PPA that will build Go itself (inception-y), don't modify! +# +# This version is fine to be old and full of security holes, we just use it +# to build the latest Go. Don't change it. +# +# version:ppa-builder-1 1.19.6 +# https://go.dev/dl/ +d7f0013f82e6d7f862cc6cb5c8cdb48eef5f2e239b35baa97e2f1a7466043767 go1.19.6.src.tar.gz + +# version:ppa-builder-2 1.21.9 +# https://go.dev/dl/ +58f0c5ced45a0012bce2ff7a9df03e128abcc8818ebabe5027bb92bafe20e421 go1.21.9.src.tar.gz + +# version:protoc 27.1 +# https://github.com/protocolbuffers/protobuf/releases/ +# https://github.com/protocolbuffers/protobuf/releases/download/v27.1/ +8809c2ec85368c6b6e9af161b6771a153aa92670a24adbe46dd34fa02a04df2f protoc-27.1-linux-aarch_64.zip +5d21979a6d27475e810b76b88863d1e784fa01ffb15e511a3ec5bd1924d89426 protoc-27.1-linux-ppcle_64.zip +84d8852750ed186dc4a057a1a86bcac409be5362d6af04770f42367fee6b7bc1 protoc-27.1-linux-s390_64.zip +2f028796ff5741691650e0eea290e61ff2f1c0d87f8d31fe45ef47fd967cef0c protoc-27.1-linux-x86_32.zip +8970e3d8bbd67d53768fe8c2e3971bdd71e51cfe2001ca06dacad17258a7dae3 protoc-27.1-linux-x86_64.zip +03b7af1bf469e7285dc51976ee5fa99412704dbd1c017105114852a37b165c12 protoc-27.1-osx-aarch_64.zip +f14d3973cf13283d07c520ed6f4c12405ad41b9efd18089a1c74897037d742b5 protoc-27.1-osx-universal_binary.zip +8520d944f3a3890fa296a3b3b0d4bb18337337e2526bbbf1b507eeea3c2a1ec4 protoc-27.1-osx-x86_64.zip +6263718ff96547b8392a079f6fdf02a4156f2e8d13cd51649a0d03fb7afa2de8 protoc-27.1-win32.zip +da531c51ccd1290d8d34821f0ce4e219c7fbaa6f9825f5a3fb092a9d03fe6206 protoc-27.1-win64.zip + +# version:protoc-gen-go 1.34.2 +# https://github.com/protocolbuffers/protobuf-go/releases/ +# https://github.com/protocolbuffers/protobuf-go/releases/download/v1.34.2/ +9b48d8f90add02e8e94e14962fed74e7ce2b2d6bda4dd42f1f4fbccf0f766f1a protoc-gen-go.v1.34.2.darwin.amd64.tar.gz +17aca7f948dbb624049030cf841e35895cf34183ba006e721247fdeb95ff2780 protoc-gen-go.v1.34.2.darwin.arm64.tar.gz +a191849433fd489f1d44f37788d762658f3f5fb225f3a85d4ce6ba32666703ed protoc-gen-go.v1.34.2.linux.386.tar.gz +b87bc134dee55576a842141bf0ed27761c635d746780fce5dee038c6dd16554f protoc-gen-go.v1.34.2.linux.amd64.tar.gz +63d400167e75ab9f6690688f6fdc6a9455aa20bc1faa71e32149dbd322f7f198 protoc-gen-go.v1.34.2.linux.arm64.tar.gz +56e7675816db6e62be4f833a51544d5716b8420c462515579e05ca8444ab06ed protoc-gen-go.v1.34.2.windows.386.zip +abafd39612177dd4e9a65207cadd5374a9352d8611e8e040f8462fcfa3010daf protoc-gen-go.v1.34.2.windows.amd64.zip diff --git a/build/ci.go b/build/ci.go index ff3ea89d10f6..3bdb52a7a32e 100644 --- a/build/ci.go +++ b/build/ci.go @@ -246,37 +246,52 @@ func doTest(cmdline []string) { build.MustRun(gotest) } +// doLint runs golangci-lint on requested packages. func doLint(cmdline []string) { + var ( + cachedir = flag.String("cachedir", "./build/cache", "directory for caching golangci-lint binary.") + ) flag.CommandLine.Parse(cmdline) - packages := []string{"./..."} if len(flag.CommandLine.Args()) > 0 { packages = flag.CommandLine.Args() } - // Get golangci-lint and install all supported linters - build.MustRun(goTool("get", "github.com/golangci/golangci-lint/cmd/golangci-lint@v1.18.0")) - - // Run fast linters batched together - configs := []string{ - "run", - "--disable-all", - "--enable=vet", - "--enable=gofmt", - "--enable=misspell", - "--enable=goconst", - "--min-occurrences=6", // for goconst + + linter := downloadLinter(*cachedir) + lflags := []string{"run", "--config", ".golangci.yml"} + build.MustRunCommandWithOutput(linter, append(lflags, packages...)...) + fmt.Println("You have achieved perfection.") +} + +// downloadLinter downloads and unpacks golangci-lint. +func downloadLinter(cachedir string) string { + csdb := build.MustLoadChecksums("build/checksums.txt") + version, err := build.Version(csdb, "golangci") + if err != nil { + log.Fatal(err) } - build.MustRunCommand(filepath.Join(GOBIN, "golangci-lint"), append(configs, packages...)...) + arch := runtime.GOARCH + ext := ".tar.gz" - // Run slow linters one by one - for _, linter := range []string{"unconvert", "gosimple"} { - configs = []string{"--vendor", "--deadline=10m", "--disable-all", "--enable=" + linter} - build.MustRunCommand(filepath.Join(GOBIN, "golangci-lint"), append(configs, packages...)...) + if runtime.GOOS == "windows" { + ext = ".zip" + } + if arch == "arm" { + arch += "v" + os.Getenv("GOARM") + } + base := fmt.Sprintf("golangci-lint-%s-%s-%s", version, runtime.GOOS, arch) + url := fmt.Sprintf("https://github.com/golangci/golangci-lint/releases/download/v%s/%s%s", version, base, ext) + archivePath := filepath.Join(cachedir, base+ext) + if err := csdb.DownloadFile(url, archivePath); err != nil { + log.Fatal(err) } + if err := build.ExtractArchive(archivePath, cachedir); err != nil { + log.Fatal(err) + } + return filepath.Join(cachedir, base, "golangci-lint") } // Cross compilation - func doXgo(cmdline []string) { var ( alltools = flag.Bool("alltools", false, `Flag whether we're building all known tools, or only on in particular`) diff --git a/go.mod b/go.mod index 3b58b4c90cdc..9b6faf749c1b 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,8 @@ module github.com/XinFinOrg/XDPoSChain go 1.22 +toolchain go1.22.0 + require ( github.com/VictoriaMetrics/fastcache v1.12.2 github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 diff --git a/internal/build/archive.go b/internal/build/archive.go new file mode 100644 index 000000000000..645921c690a7 --- /dev/null +++ b/internal/build/archive.go @@ -0,0 +1,296 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package build + +import ( + "archive/tar" + "archive/zip" + "compress/gzip" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "strings" + "time" +) + +type Archive interface { + // Directory adds a new directory entry to the archive and sets the + // directory for subsequent calls to Header. + Directory(name string) error + + // Header adds a new file to the archive. The file is added to the directory + // set by Directory. The content of the file must be written to the returned + // writer. + Header(os.FileInfo) (io.Writer, error) + + // Close flushes the archive and closes the underlying file. + Close() error +} + +func NewArchive(file *os.File) (Archive, string) { + switch { + case strings.HasSuffix(file.Name(), ".zip"): + return NewZipArchive(file), strings.TrimSuffix(file.Name(), ".zip") + case strings.HasSuffix(file.Name(), ".tar.gz"): + return NewTarballArchive(file), strings.TrimSuffix(file.Name(), ".tar.gz") + default: + return nil, "" + } +} + +// AddFile appends an existing file to an archive. +func AddFile(a Archive, file string) error { + fd, err := os.Open(file) + if err != nil { + return err + } + defer fd.Close() + fi, err := fd.Stat() + if err != nil { + return err + } + w, err := a.Header(fi) + if err != nil { + return err + } + if _, err := io.Copy(w, fd); err != nil { + return err + } + return nil +} + +// WriteArchive creates an archive containing the given files. +func WriteArchive(name string, files []string) (err error) { + archfd, err := os.Create(name) + if err != nil { + return err + } + + defer func() { + archfd.Close() + // Remove the half-written archive on failure. + if err != nil { + os.Remove(name) + } + }() + archive, basename := NewArchive(archfd) + if archive == nil { + return errors.New("unknown archive extension") + } + fmt.Println(name) + if err := archive.Directory(basename); err != nil { + return err + } + for _, file := range files { + fmt.Println(" +", filepath.Base(file)) + if err := AddFile(archive, file); err != nil { + return err + } + } + return archive.Close() +} + +type ZipArchive struct { + dir string + zipw *zip.Writer + file io.Closer +} + +func NewZipArchive(w io.WriteCloser) Archive { + return &ZipArchive{"", zip.NewWriter(w), w} +} + +func (a *ZipArchive) Directory(name string) error { + a.dir = name + "/" + return nil +} + +func (a *ZipArchive) Header(fi os.FileInfo) (io.Writer, error) { + head, err := zip.FileInfoHeader(fi) + if err != nil { + return nil, fmt.Errorf("can't make zip header: %v", err) + } + head.Name = a.dir + head.Name + head.Method = zip.Deflate + w, err := a.zipw.CreateHeader(head) + if err != nil { + return nil, fmt.Errorf("can't add zip header: %v", err) + } + return w, nil +} + +func (a *ZipArchive) Close() error { + if err := a.zipw.Close(); err != nil { + return err + } + return a.file.Close() +} + +type TarballArchive struct { + dir string + tarw *tar.Writer + gzw *gzip.Writer + file io.Closer +} + +func NewTarballArchive(w io.WriteCloser) Archive { + gzw := gzip.NewWriter(w) + tarw := tar.NewWriter(gzw) + return &TarballArchive{"", tarw, gzw, w} +} + +func (a *TarballArchive) Directory(name string) error { + a.dir = name + "/" + return a.tarw.WriteHeader(&tar.Header{ + Name: a.dir, + Mode: 0755, + Typeflag: tar.TypeDir, + ModTime: time.Now(), + }) +} + +func (a *TarballArchive) Header(fi os.FileInfo) (io.Writer, error) { + head, err := tar.FileInfoHeader(fi, "") + if err != nil { + return nil, fmt.Errorf("can't make tar header: %v", err) + } + head.Name = a.dir + head.Name + if err := a.tarw.WriteHeader(head); err != nil { + return nil, fmt.Errorf("can't add tar header: %v", err) + } + return a.tarw, nil +} + +func (a *TarballArchive) Close() error { + if err := a.tarw.Close(); err != nil { + return err + } + if err := a.gzw.Close(); err != nil { + return err + } + return a.file.Close() +} + +// ExtractArchive unpacks a .zip or .tar.gz archive to the destination directory. +func ExtractArchive(archive string, dest string) error { + ar, err := os.Open(archive) + if err != nil { + return err + } + defer ar.Close() + + switch { + case strings.HasSuffix(archive, ".tar.gz"): + return extractTarball(ar, dest) + case strings.HasSuffix(archive, ".zip"): + return extractZip(ar, dest) + default: + return fmt.Errorf("unhandled archive type %s", archive) + } +} + +// extractTarball unpacks a .tar.gz file. +func extractTarball(ar io.Reader, dest string) error { + gzr, err := gzip.NewReader(ar) + if err != nil { + return err + } + defer gzr.Close() + + tr := tar.NewReader(gzr) + for { + // Move to the next file header. + header, err := tr.Next() + if err != nil { + if err == io.EOF { + return nil + } + return err + } + // We only care about regular files, directory modes + // and special file types are not supported. + if header.Typeflag == tar.TypeReg { + armode := header.FileInfo().Mode() + err := extractFile(header.Name, armode, tr, dest) + if err != nil { + return fmt.Errorf("extract %s: %v", header.Name, err) + } + } + } +} + +// extractZip unpacks the given .zip file. +func extractZip(ar *os.File, dest string) error { + info, err := ar.Stat() + if err != nil { + return err + } + zr, err := zip.NewReader(ar, info.Size()) + if err != nil { + return err + } + + for _, zf := range zr.File { + if !zf.Mode().IsRegular() { + continue + } + + data, err := zf.Open() + if err != nil { + return err + } + err = extractFile(zf.Name, zf.Mode(), data, dest) + data.Close() + if err != nil { + return fmt.Errorf("extract %s: %v", zf.Name, err) + } + } + return nil +} + +// extractFile extracts a single file from an archive. +func extractFile(arpath string, armode os.FileMode, data io.Reader, dest string) error { + // Check that path is inside destination directory. + target := filepath.Join(dest, filepath.FromSlash(arpath)) + if !strings.HasPrefix(target, filepath.Clean(dest)+string(os.PathSeparator)) { + return fmt.Errorf("path %q escapes archive destination", target) + } + + // Remove the preivously-extracted file if it exists + if err := os.RemoveAll(target); err != nil { + return err + } + + // Recreate the destination directory + if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil { + return err + } + + // Copy file data. + file, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY, armode) + if err != nil { + return err + } + if _, err = io.Copy(file, data); err != nil { + file.Close() + os.Remove(target) + return err + } + return file.Close() +} diff --git a/internal/build/download.go b/internal/build/download.go new file mode 100644 index 000000000000..50268227a501 --- /dev/null +++ b/internal/build/download.go @@ -0,0 +1,151 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package build + +import ( + "bufio" + "crypto/sha256" + "encoding/hex" + "fmt" + "io" + "log" + "net/http" + "os" + "path/filepath" + "strings" +) + +// ChecksumDB keeps file checksums. +type ChecksumDB struct { + allChecksums []string +} + +// MustLoadChecksums loads a file containing checksums. +func MustLoadChecksums(file string) *ChecksumDB { + content, err := os.ReadFile(file) + if err != nil { + log.Fatal("can't load checksum file: " + err.Error()) + } + return &ChecksumDB{strings.Split(strings.ReplaceAll(string(content), "\r\n", "\n"), "\n")} +} + +// Verify checks whether the given file is valid according to the checksum database. +func (db *ChecksumDB) Verify(path string) error { + fd, err := os.Open(path) + if err != nil { + return err + } + defer fd.Close() + + h := sha256.New() + if _, err := io.Copy(h, bufio.NewReader(fd)); err != nil { + return err + } + fileHash := hex.EncodeToString(h.Sum(nil)) + if !db.findHash(filepath.Base(path), fileHash) { + return fmt.Errorf("invalid file hash: %s %s", fileHash, filepath.Base(path)) + } + return nil +} + +func (db *ChecksumDB) findHash(basename, hash string) bool { + want := hash + " " + basename + for _, line := range db.allChecksums { + if strings.TrimSpace(line) == want { + return true + } + } + return false +} + +// DownloadFile downloads a file and verifies its checksum. +func (db *ChecksumDB) DownloadFile(url, dstPath string) error { + if err := db.Verify(dstPath); err == nil { + fmt.Printf("%s is up-to-date\n", dstPath) + return nil + } + fmt.Printf("%s is stale\n", dstPath) + fmt.Printf("downloading from %s\n", url) + + resp, err := http.Get(url) + if err != nil { + return fmt.Errorf("download error: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("download error: status %d", resp.StatusCode) + } + if err := os.MkdirAll(filepath.Dir(dstPath), 0755); err != nil { + return err + } + fd, err := os.OpenFile(dstPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) + if err != nil { + return err + } + dst := newDownloadWriter(fd, resp.ContentLength) + _, err = io.Copy(dst, resp.Body) + dst.Close() + if err != nil { + return err + } + return db.Verify(dstPath) +} + +type downloadWriter struct { + file *os.File + dstBuf *bufio.Writer + size int64 + written int64 + lastpct int64 +} + +func newDownloadWriter(dst *os.File, size int64) *downloadWriter { + return &downloadWriter{ + file: dst, + dstBuf: bufio.NewWriter(dst), + size: size, + } +} + +func (w *downloadWriter) Write(buf []byte) (int, error) { + n, err := w.dstBuf.Write(buf) + + // Report progress. + w.written += int64(n) + pct := w.written * 10 / w.size * 10 + if pct != w.lastpct { + if w.lastpct != 0 { + fmt.Print("...") + } + fmt.Print(pct, "%") + w.lastpct = pct + } + return n, err +} + +func (w *downloadWriter) Close() error { + if w.lastpct > 0 { + fmt.Println() // Finish the progress line. + } + flushErr := w.dstBuf.Flush() + closeErr := w.file.Close() + if flushErr != nil { + return flushErr + } + return closeErr +} diff --git a/internal/build/gotool.go b/internal/build/gotool.go new file mode 100644 index 000000000000..2a474604183f --- /dev/null +++ b/internal/build/gotool.go @@ -0,0 +1,180 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package build + +import ( + "fmt" + "log" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" +) + +type GoToolchain struct { + Root string // GOROOT + + // Cross-compilation variables. These are set when running the go tool. + GOARCH string + GOOS string + CC string +} + +// Go creates an invocation of the go command. +func (g *GoToolchain) Go(command string, args ...string) *exec.Cmd { + tool := g.goTool(command, args...) + + // Configure environment for cross build. + if g.GOARCH != "" && g.GOARCH != runtime.GOARCH { + tool.Env = append(tool.Env, "CGO_ENABLED=1") + tool.Env = append(tool.Env, "GOARCH="+g.GOARCH) + } + if g.GOOS != "" && g.GOOS != runtime.GOOS { + tool.Env = append(tool.Env, "GOOS="+g.GOOS) + } + // Configure C compiler. + if g.CC != "" { + tool.Env = append(tool.Env, "CC="+g.CC) + } else if os.Getenv("CC") != "" { + tool.Env = append(tool.Env, "CC="+os.Getenv("CC")) + } + // CKZG by default is not portable, append the necessary build flags to make + // it not rely on modern CPU instructions and enable linking against. + tool.Env = append(tool.Env, "CGO_CFLAGS=-O2 -g -D__BLST_PORTABLE__") + + return tool +} + +func (g *GoToolchain) goTool(command string, args ...string) *exec.Cmd { + if g.Root == "" { + g.Root = runtime.GOROOT() + } + tool := exec.Command(filepath.Join(g.Root, "bin", "go"), command) // nolint: gosec + tool.Args = append(tool.Args, args...) + tool.Env = append(tool.Env, "GOROOT="+g.Root) + + // Forward environment variables to the tool, but skip compiler target settings. + // TODO: what about GOARM? + skip := map[string]struct{}{"GOROOT": {}, "GOARCH": {}, "GOOS": {}, "GOBIN": {}, "CC": {}} + for _, e := range os.Environ() { + if i := strings.IndexByte(e, '='); i >= 0 { + if _, ok := skip[e[:i]]; ok { + continue + } + } + tool.Env = append(tool.Env, e) + } + return tool +} + +// DownloadGo downloads the Go binary distribution and unpacks it into a temporary +// directory. It returns the GOROOT of the unpacked toolchain. +func DownloadGo(csdb *ChecksumDB) string { + version, err := Version(csdb, "golang") + if err != nil { + log.Fatal(err) + } + // Shortcut: if the Go version that runs this script matches the + // requested version exactly, there is no need to download anything. + activeGo := strings.TrimPrefix(runtime.Version(), "go") + if activeGo == version { + log.Printf("-dlgo version matches active Go version %s, skipping download.", activeGo) + return runtime.GOROOT() + } + + ucache, err := os.UserCacheDir() + if err != nil { + log.Fatal(err) + } + + // For Arm architecture, GOARCH includes ISA version. + os := runtime.GOOS + arch := runtime.GOARCH + if arch == "arm" { + arch = "armv6l" + } + file := fmt.Sprintf("go%s.%s-%s", version, os, arch) + if os == "windows" { + file += ".zip" + } else { + file += ".tar.gz" + } + url := "https://golang.org/dl/" + file + dst := filepath.Join(ucache, file) + if err := csdb.DownloadFile(url, dst); err != nil { + log.Fatal(err) + } + + godir := filepath.Join(ucache, fmt.Sprintf("geth-go-%s-%s-%s", version, os, arch)) + if err := ExtractArchive(dst, godir); err != nil { + log.Fatal(err) + } + goroot, err := filepath.Abs(filepath.Join(godir, "go")) + if err != nil { + log.Fatal(err) + } + return goroot +} + +// Version returns the versions defined in the checksumdb. +func Version(csdb *ChecksumDB, version string) (string, error) { + for _, l := range csdb.allChecksums { + if !strings.HasPrefix(l, "# version:") { + continue + } + v := strings.Split(l, ":")[1] + parts := strings.Split(v, " ") + if len(parts) != 2 { + log.Print("Erroneous version-string", "v", l) + continue + } + if parts[0] == version { + return parts[1], nil + } + } + return "", fmt.Errorf("no version found for '%v'", version) +} + +// DownloadAndVerifyChecksums downloads all files and checks that they match +// the checksum given in checksums.txt. +// This task can be used to sanity-check new checksums. +func DownloadAndVerifyChecksums(csdb *ChecksumDB) { + var ( + base = "" + ucache = os.TempDir() + ) + for _, l := range csdb.allChecksums { + if strings.HasPrefix(l, "# https://") { + base = l[2:] + continue + } + if strings.HasPrefix(l, "#") { + continue + } + hashFile := strings.Split(l, " ") + if len(hashFile) != 2 { + continue + } + file := hashFile[1] + url := base + file + dst := filepath.Join(ucache, file) + if err := csdb.DownloadFile(url, dst); err != nil { + log.Print(err) + } + } +} diff --git a/internal/build/util.go b/internal/build/util.go index 3fcb411c70de..42f57a468181 100644 --- a/internal/build/util.go +++ b/internal/build/util.go @@ -26,7 +26,9 @@ import ( "path" "path/filepath" "runtime" + "strconv" "strings" + "time" ) var DryRunFlag = flag.Bool("n", false, "dry run, don't execute commands") @@ -34,7 +36,7 @@ var DryRunFlag = flag.Bool("n", false, "dry run, don't execute commands") // MustRun executes the given command and exits the host process for // any error. func MustRun(cmd *exec.Cmd) { - fmt.Println(">>>", strings.Join(cmd.Args, " ")) + fmt.Println(">>>", printArgs(cmd.Args)) if !*DryRunFlag { cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout @@ -44,10 +46,45 @@ func MustRun(cmd *exec.Cmd) { } } +func printArgs(args []string) string { + var s strings.Builder + for i, arg := range args { + if i > 0 { + s.WriteByte(' ') + } + if strings.IndexByte(arg, ' ') >= 0 { + arg = strconv.QuoteToASCII(arg) + } + s.WriteString(arg) + } + return s.String() +} + func MustRunCommand(cmd string, args ...string) { MustRun(exec.Command(cmd, args...)) } +// MustRunCommandWithOutput runs the given command, and ensures that some output will be +// printed while it runs. This is useful for CI builds where the process will be stopped +// when there is no output. +func MustRunCommandWithOutput(cmd string, args ...string) { + interval := time.NewTicker(time.Minute) + done := make(chan struct{}) + defer interval.Stop() + defer close(done) + go func() { + for { + select { + case <-interval.C: + fmt.Printf("Waiting for command %q\n", cmd) + case <-done: + return + } + } + }() + MustRun(exec.Command(cmd, args...)) +} + // GOPATH returns the value that the GOPATH environment // variable should be set to. func GOPATH() string {