From a020259bb50296ddbd2cc6c0ff61a3efce77d9d9 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 19 Sep 2024 09:46:23 +0800 Subject: [PATCH 001/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] .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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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/242] 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()