diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 08188a2006cb..9ca9cb0ff3c3 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -157,6 +157,9 @@ var ( utils.ValContractFlag, utils.ValChainIdFlag, utils.ValChangeEpochIdFlag, + utils.ExternalCallEnableBlockNumber, + utils.ExternalCallRpcFlag, + utils.ExternalCallSupportChainId, utils.VMEnableDebugFlag, utils.NetworkIdFlag, utils.EthStatsURLFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index ad2a7adde7c9..1d306e6808da 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -198,6 +198,18 @@ var ( Name: "validator.rpc", Usage: "rpc for Validator to update validator set", } + ExternalCallEnableBlockNumber = cli.StringFlag{ + Name: "externalcall.enableblocknumber", + Usage: "define in which blockNumber the externalCall is supported", + } + ExternalCallRpcFlag = cli.StringFlag{ + Name: "externalcall.callrpc", + Usage: "rpc to launch independent external call client", + } + ExternalCallSupportChainId = cli.Uint64Flag{ + Name: "externalcall.supportchainid", + Usage: "chainId of the target chain of the external call", + } DeveloperFlag = cli.BoolFlag{ Name: "dev", Usage: "Ephemeral proof-of-authority network with a pre-funded developer account, mining enabled", @@ -1495,6 +1507,25 @@ func setTendermint(ctx *cli.Context, cfg *ethconfig.Config) { } } +func setExternalCall(ctx *cli.Context, cfg *ethconfig.Config) { + if ctx.GlobalIsSet(MiningEnabledFlag.Name) { + cfg.IsMiner = true + } + + if ctx.GlobalIsSet(ExternalCallEnableBlockNumber.Name) { + cfg.ExternalCallEnableBlockNumber.SetString(ctx.GlobalString(ExternalCallEnableBlockNumber.Name), 10) + } + + if ctx.GlobalIsSet(ExternalCallRpcFlag.Name) { + cfg.ExternalCallRpc = ctx.GlobalString(ExternalCallRpcFlag.Name) + } + + if ctx.GlobalIsSet(ExternalCallSupportChainId.Name) { + cfg.ExternalCallSupportChainId = ctx.GlobalUint64(ExternalCallSupportChainId.Name) + } + +} + func setMiner(ctx *cli.Context, cfg *miner.Config) { if ctx.GlobalIsSet(MinerNotifyFlag.Name) { cfg.Notify = strings.Split(ctx.GlobalString(MinerNotifyFlag.Name), ",") @@ -1606,6 +1637,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { setTxPool(ctx, &cfg.TxPool) setEthash(ctx, cfg) setTendermint(ctx, cfg) + setExternalCall(ctx, cfg) setMiner(ctx, &cfg.Miner) setWhitelist(ctx, cfg) setLes(ctx, cfg) diff --git a/consensus/tendermint/tendermint.go b/consensus/tendermint/tendermint.go index 25384ce9d99a..6526fd281613 100644 --- a/consensus/tendermint/tendermint.go +++ b/consensus/tendermint/tendermint.go @@ -381,10 +381,6 @@ func (c *Tendermint) verifyHeader(chain consensus.ChainHeaderReader, header *typ if header.MixDigest != (common.Hash{}) { return errInvalidMixDigest } - // Ensure that the block doesn't contain any uncles which are meaningless in PoA - if header.UncleHash != uncleHash { - return errInvalidUncleHash - } // Ensure that the block's difficulty is meaningful (may not be correct at this point) if header.Difficulty == nil || (header.Difficulty.Cmp(big.NewInt(1)) != 0) { return errInvalidDifficulty @@ -458,10 +454,10 @@ func (c *Tendermint) getEpochHeader(chain consensus.ChainHeaderReader, header *t // VerifyUncles implements consensus.Engine, always returning an error for any // uncles as this consensus mechanism doesn't permit uncles. func (c *Tendermint) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { - if len(block.Uncles()) > 0 { - return errors.New("uncles not allowed") + if len(block.Uncles()) <= len(block.Transactions()) { + return nil } - return nil + return fmt.Errorf("the number of uncles exceeds the number of transactions") } // Prepare implements consensus.Engine, preparing all the consensus fields of the @@ -482,7 +478,7 @@ func (c *Tendermint) Prepare(chain consensus.ChainHeaderReader, header *types.He func (c *Tendermint) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) { // No block rewards at the moment, so the state remains as is and uncles are dropped header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) - header.UncleHash = types.CalcUncleHash(nil) + header.UncleHash = types.CalcUncleHash(uncles) } // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, @@ -492,7 +488,7 @@ func (c *Tendermint) FinalizeAndAssemble(chain consensus.ChainHeaderReader, head c.Finalize(chain, header, state, txs, uncles) // Assemble and return the final block for sealing - return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)), nil + return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), nil } // Seal implements consensus.Engine, attempting to create a sealed block using diff --git a/core/block_validator.go b/core/block_validator.go index dc5402c3f320..b31462ab1c1a 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -59,6 +59,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { if err := v.engine.VerifyUncles(v.bc, block); err != nil { return err } + // The process of verifying uncleHash actually verifies all external call results in this block if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash { return fmt.Errorf("uncle root hash mismatch: have %x, want %x", hash, header.UncleHash) } diff --git a/core/blockchain.go b/core/blockchain.go index 1152b2fd5f22..6036130c6107 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1591,7 +1591,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) // Process block using the parent state as reference point substart := time.Now() - receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig) + receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig, false) if err != nil { bc.reportBlock(block, receipts, err) atomic.StoreUint32(&followupInterrupt, 1) @@ -2319,7 +2319,7 @@ func (bc *BlockChain) PreExecuteBlock(block *types.Block) (err error) { err = fmt.Errorf("txHash mismatch, got %s, expect %s", block.TxHash(), txHash) return } - receipts, _, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig) + receipts, _, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig, true) if err != nil { return } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 37a1a42d0c60..daf277b08950 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -160,7 +160,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { if err != nil { return err } - receipts, _, usedGas, err := blockchain.processor.Process(block, statedb, vm.Config{}) + receipts, _, usedGas, err := blockchain.processor.Process(block, statedb, vm.Config{}, false) if err != nil { blockchain.reportBlock(block, receipts, err) return err diff --git a/core/chain_makers.go b/core/chain_makers.go index c7bf60a4b06e..73ff4ec1f3f8 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -18,6 +18,8 @@ package core import ( "fmt" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/ethclient" "math/big" "github.com/ethereum/go-ethereum/common" @@ -25,7 +27,6 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" ) @@ -44,8 +45,9 @@ type BlockGen struct { receipts []*types.Receipt uncles []*types.Header - config *params.ChainConfig - engine consensus.Engine + config *params.ChainConfig + engine consensus.Engine + vmconfig *vm.Config } // SetCoinbase sets the coinbase of the generated block. @@ -103,10 +105,20 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) { b.SetCoinbase(common.Address{}) } b.statedb.Prepare(tx.Hash(), len(b.txs)) - receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{}) + + receipt, crossChainCallResult, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, *b.vmconfig) if err != nil { panic(err) } + + if len(crossChainCallResult) != 0 { + uncle := &types.Header{ + Number: big.NewInt(int64(len(b.txs))), + TxHash: tx.Hash(), + Extra: crossChainCallResult, + } + b.uncles = append(b.uncles, uncle) + } b.txs = append(b.txs, tx) b.receipts = append(b.receipts, receipt) } @@ -225,6 +237,16 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse chainreader := &fakeChainReader{config: config} genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, types.Receipts) { b := &BlockGen{i: i, chain: blocks, parent: parent, statedb: statedb, config: config, engine: engine} + if config.ExternalCall != nil { + // Here it is assumed that the node has an active rpc + client, err := ethclient.Dial(config.ExternalCall.CallRpc) + defer client.Close() + if err != nil { + return nil, nil + } + b.vmconfig = &vm.Config{ExternalCallClient: client} + } + b.header = makeHeader(chainreader, parent, statedb, b.engine) // Set the difficulty for clique block. The chain maker doesn't have access diff --git a/core/genesis.go b/core/genesis.go index 8b1893a1766a..8bf0628d6a2e 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -438,15 +438,18 @@ func DefaultWeb3QTestnetGenesisBlock() *Genesis { Timestamp: 1644537097, Alloc: map[common.Address]GenesisAccount{ common.HexToAddress("0x0E961a6A6235eFDB9a0F0BC753E395211B77cc28"): {Balance: new(big.Int).Mul(big.NewInt(1000000000000000000), big.NewInt(1000000000))}, // 1e9 Ether - common.HexToAddress("0x5C935469C5592Aeeac3372e922d9bCEabDF8830d"): {Balance: new(big.Int).Mul(big.NewInt(1000000000000000000), big.NewInt(1000000000))}, // 1e9 Ether + common.HexToAddress("0x024d6050275eec53b233B467AdA12d2C65B3AEce"): {Balance: new(big.Int).Mul(big.NewInt(1000000000000000000), big.NewInt(1000000000))}, // 1e9 Ether + common.HexToAddress("0x96f22a48DcD4dFb99A11560b24bee02F374cA77D"): {Balance: new(big.Int).Mul(big.NewInt(1000000000000000000), big.NewInt(1000000000))}, // 1e9 Ether + common.HexToAddress("0x560CD6c5054847c2AFCAae0dd9E34E22E6f47100"): {Balance: new(big.Int).Mul(big.NewInt(1000000000000000000), big.NewInt(1000000000))}, // 1e9 Ether + common.HexToAddress("0x3d2Bf29BaB6ec95422Eb143e856df3B0E775e50c"): {Balance: new(big.Int).Mul(big.NewInt(1000000000000000000), big.NewInt(1000000000))}, // 1e9 Ether + common.HexToAddress("0xb71af880FC87F2aA099251A2224ef3f38BE7f451"): {Balance: new(big.Int).Mul(big.NewInt(1000000000000000000), big.NewInt(1000000000))}, // 1e9 Ether }, NextValidators: []common.Address{ - common.HexToAddress("0x2cff0b8e36522eba76f6f5c328d58581243882e4"), - common.HexToAddress("0x959994471dee37411f579dd2820a8743cba20f46"), - common.HexToAddress("0x977cfc676bb06daed7ddfa7711bcfe8d50c93081"), - common.HexToAddress("0xcd21538af6e33ff6fcf1e2ca20f771413004cfd3"), + common.HexToAddress("0x96f22a48DcD4dFb99A11560b24bee02F374cA77D"), + common.HexToAddress("0x560CD6c5054847c2AFCAae0dd9E34E22E6f47100"), + common.HexToAddress("0x3d2Bf29BaB6ec95422Eb143e856df3B0E775e50c"), }, - NextValidatorPowers: []uint64{1, 1, 1, 1}, + NextValidatorPowers: []uint64{1, 1, 1}, } } diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index 10a1722940b0..95073d160124 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -68,7 +68,9 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c return // Also invalid block, bail out } statedb.Prepare(tx.Hash(), i) - if err := precacheTransaction(msg, p.config, gaspool, statedb, header, evm); err != nil { + // get the external_call_result from uncles of block + expectExternalCallResult := block.GetExternalCallResult(tx.Hash()) + if err := precacheTransaction(msg, p.config, gaspool, statedb, header, evm, expectExternalCallResult); err != nil { return // Ugh, something went horribly wrong, bail out } // If we're pre-byzantium, pre-load trie nodes for the intermediate root @@ -85,9 +87,11 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c // precacheTransaction attempts to apply a transaction to the given state database // and uses the input parameters for its environment. The goal is not to execute // the transaction successfully, rather to warm up touched data slots. -func precacheTransaction(msg types.Message, config *params.ChainConfig, gaspool *GasPool, statedb *state.StateDB, header *types.Header, evm *vm.EVM) error { +func precacheTransaction(msg types.Message, config *params.ChainConfig, gaspool *GasPool, statedb *state.StateDB, header *types.Header, evm *vm.EVM, expectExternalCallResult []byte) error { // Update the evm with the new transaction context. evm.Reset(NewEVMTxContext(msg), statedb) + // set the external_call_result + evm.SetCrossChainCallResults(expectExternalCallResult) // Add addresses to access list if applicable _, err := ApplyMessage(evm, msg, gaspool) return err diff --git a/core/state_processor.go b/core/state_processor.go index d4c77ae41042..4ec190c95925 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -17,6 +17,7 @@ package core import ( + "bytes" "fmt" "math/big" @@ -27,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" ) @@ -56,7 +58,7 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen // Process returns the receipts and logs accumulated during the process and // returns the amount of gas that was used in the process. If any of the // transactions failed to execute due to insufficient gas it will return an error. -func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) { +func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config, usingClient bool) (types.Receipts, []*types.Log, uint64, error) { var ( receipts types.Receipts usedGas = new(uint64) @@ -70,19 +72,50 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { misc.ApplyDAOHardFork(statedb) } + blockContext := NewEVMBlockContext(header, p.bc, nil) + // set the external_call_client as nil if the usingClient is false + if usingClient { + if cfg.ExternalCallClient == nil { + return nil, nil, 0, fmt.Errorf("external_call_client is not active") + } + } else { + cfg.ExternalCallClient = nil + } vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg) + // Iterate over and process the individual transactions + var uncleIndex int for i, tx := range block.Transactions() { msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number), header.BaseFee) if err != nil { return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } + statedb.Prepare(tx.Hash(), i) - receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv) + // get the externalCallResult from uncle + var expectResult []byte + if len(block.Uncles()) > uncleIndex { + expectResult = block.Uncles()[uncleIndex].GetTxExternalCallResult(tx) + if expectResult != nil { + uncleIndex++ + } + } + + receipt, crossChainCallResult, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv, expectResult) if err != nil { return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } + + if len(crossChainCallResult) > 0 || len(expectResult) > 0 { + if !bytes.Equal(expectResult, crossChainCallResult) { + log.Warn("Failed to verify cross_chain_result", "txHash", tx.Hash().Hex(), "expect_cross_chain_result", common.Bytes2Hex(expectResult), "cross_chain_result", common.Bytes2Hex(crossChainCallResult)) + return nil, nil, 0, fmt.Errorf("failed to verify cross_chain_result, tx: %s", tx.Hash().Hex()) + } else { + log.Debug("Verify cross_chain_result succeed", "txHash", tx.Hash().Hex(), "cross_chain_result", common.Bytes2Hex(crossChainCallResult)) + } + } + receipts = append(receipts, receipt) allLogs = append(allLogs, receipt.Logs...) } @@ -92,15 +125,18 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg return receipts, allLogs, *usedGas, nil } -func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) { +func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM, expectExternalCallResult []byte) (*types.Receipt, []byte, error) { // Create a new context to be used in the EVM environment. txContext := NewEVMTxContext(msg) evm.Reset(txContext, statedb) + // Set cross_chain_call_result as expectExternalCallResult when external_call_client is nil and external_call_result is not empty + evm.SetCrossChainCallResults(expectExternalCallResult) + // Apply the transaction to the current state (included in the env). result, err := ApplyMessage(evm, msg, gp) if err != nil { - return nil, err + return nil, nil, err } // Update the state with pending changes. @@ -134,20 +170,21 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon receipt.BlockHash = blockHash receipt.BlockNumber = blockNumber receipt.TransactionIndex = uint(statedb.TxIndex()) - return receipt, err + return receipt, result.CrossChainCallResults, err } // 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, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) { +func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, []byte, error) { msg, err := tx.AsMessage(types.MakeSigner(config, header.Number), header.BaseFee) if err != nil { - return nil, err + return nil, nil, err } // Create a new context to be used in the EVM environment blockContext := NewEVMBlockContext(header, bc, author) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg) - return applyTransaction(msg, config, bc, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv) + + return applyTransaction(msg, config, bc, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv, nil) } diff --git a/core/state_processor_test.go b/core/state_processor_test.go index aa8e4bebf9d4..7dc84a9ed44f 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -17,8 +17,22 @@ package core import ( + "bufio" + "bytes" + "context" "crypto/ecdsa" + "fmt" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/consensus/clique" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/state/snapshot" + "github.com/ethereum/go-ethereum/eth/tracers/logger" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/rlp" "math/big" + "strings" "testing" "github.com/ethereum/go-ethereum/common" @@ -35,6 +49,675 @@ import ( "golang.org/x/crypto/sha3" ) +type WrapTendermint struct { + *clique.Clique + Client *ethclient.Client +} + +func NewWrapTendermint(cli *clique.Clique, client *ethclient.Client) *WrapTendermint { + return &WrapTendermint{Clique: cli, Client: client} +} + +type TestChainContext struct { + tm consensus.Engine +} + +func NewTestChainContext(tm consensus.Engine) *TestChainContext { + return &TestChainContext{tm: tm} +} + +func (ctx *TestChainContext) Engine() consensus.Engine { + return ctx.tm +} + +func (ctx *TestChainContext) GetHeader(common.Hash, uint64) *types.Header { + return nil +} + +type WrapTx struct { + Tx *types.Transaction + GasUsed uint64 + Args *CrossChainCallArgment + ExpectTxData func() ([]byte, error) + ExpectTraces []*ExpectTrace + ExpectCCRBytes []byte + happenError error + Index int +} + +func (wt *WrapTx) SetExternalCallRes(crossChainCallRes []byte) { + wt.ExpectCCRBytes = crossChainCallRes +} + +func (wt *WrapTx) SetUnexpectErr(err error) { + wt.happenError = err +} + +func (wt *WrapTx) MarkTransitionIndex() string { + return fmt.Sprintf("[Transaction Index %d]", wt.Index) +} + +func NewWrapTx(tx *types.Transaction, args *CrossChainCallArgment) *WrapTx { + return &WrapTx{Tx: tx, Args: args} +} + +func (wt *WrapTx) VerifyCallResult(crossCallResult []byte, happenedError error, t *testing.T) { + if happenedError != nil { + if wt.happenError == nil { + t.Fatal("happened err:", happenedError) + return + } + + if happenedError.Error() != wt.happenError.Error() { + t.Fatal("\nexpect happen err:", wt.happenError, "\nactual happen err:", happenedError) + } else { + t.Log("expect happen err match:", happenedError.Error()) + } + happenedError = nil + return + } + + tracesWithVersion := &vm.CrossChainCallResultsWithVersion{} + err := rlp.DecodeBytes(crossCallResult, tracesWithVersion) + if err != nil { + t.Fatal(err) + } + + actualTraces := tracesWithVersion.Results + + if len(wt.ExpectTraces) != len(actualTraces) { + t.Fatalf("wrapTx.ExpectTraces length [%d] no match actualTraces length [%d]", len(wt.ExpectTraces), len(actualTraces)) + } + + for i, v := range actualTraces { + cs := v.CallRes + wt.ExpectTraces[i].verifyRes(cs, t, i, v.Success) + } + +} + +type ExpectTrace struct { + CallResultBytes []byte // call result + ExpectErrBytes error + success bool + UnExpectErr error +} + +func NewExpectTrace(callResultBytes []byte, expectErrBytes error, unExpectErr error) *ExpectTrace { + return &ExpectTrace{CallResultBytes: callResultBytes, ExpectErrBytes: expectErrBytes, UnExpectErr: unExpectErr} +} + +func (et *ExpectTrace) compareRes(cs []byte) bool { + return bytes.Equal(et.CallResultBytes, cs) +} + +func (et *ExpectTrace) verifyRes(cs []byte, t *testing.T, traceIndex int, success bool) { + + if success != true { + t.Error("the trace.Success should be true when execute succeed") + } + if bytes.Equal(et.CallResultBytes, cs) { + t.Logf("[TraceIndex %d] res match!!! \ncall_result{%s} ", traceIndex, common.Bytes2Hex(et.CallResultBytes)) + } else { + t.Errorf("[TraceIndex %d] res no match ,expect : %s, actual: %s", traceIndex, common.Bytes2Hex(et.CallResultBytes), common.Bytes2Hex(cs)) + } + +} + +type CrossChainCallArgment struct { + ChainId uint64 + TxHash common.Hash + LogIdx uint64 + MaxDataLen uint64 + Confirms uint64 + contractAbi abi.ABI + env *vm.PrecompiledContractCallEnv +} + +const CrossChainCallContract = ` +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "txHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "logIdx", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxDataLen", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "confirms", + "type": "uint256" + } + ], + "name": "callOnce", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "txHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "logIdx", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxDataLen", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "confirms", + "type": "uint256" + } + ], + "name": "callTwice", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "crossChainCallContract", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +]` + +func NewCrossChainCallArgment(chainconfig *params.ChainConfig, client *ethclient.Client, chainId uint64, txHash common.Hash, logIdx uint64, maxDataLen uint64, confirms uint64) *CrossChainCallArgment { + evmConfig := vm.Config{ExternalCallClient: client} + evm := vm.NewEVM(vm.BlockContext{}, vm.TxContext{}, nil, chainconfig, evmConfig) + env := vm.NewPrecompiledContractCallEnv(evm, nil) + abi, err := abi.JSON(strings.NewReader(CrossChainCallContract)) + if err != nil { + panic(err) + } + return &CrossChainCallArgment{ChainId: chainId, TxHash: txHash, LogIdx: logIdx, MaxDataLen: maxDataLen, Confirms: confirms, contractAbi: abi, env: env} +} + +func (c *CrossChainCallArgment) CallOncePack() ([]byte, error) { + return c.contractAbi.Pack("callOnce", big.NewInt(int64(c.ChainId)), c.TxHash, big.NewInt(int64(c.LogIdx)), big.NewInt(int64(c.MaxDataLen)), big.NewInt(int64(c.Confirms))) +} + +func (c *CrossChainCallArgment) CallTwicePack() ([]byte, error) { + return c.contractAbi.Pack("callTwice", big.NewInt(int64(c.ChainId)), c.TxHash, big.NewInt(int64(c.LogIdx)), big.NewInt(int64(c.MaxDataLen)), big.NewInt(int64(c.Confirms))) +} + +func (c *CrossChainCallArgment) CrossChainCallResult() (*vm.GetLogByTxHash, *vm.ExpectCallErr, error) { + return vm.GetExternalLog(context.Background(), c.env, c.ChainId, c.TxHash, c.LogIdx, c.MaxDataLen, c.Confirms) +} + +func (c *CrossChainCallArgment) CrossChainCallResultToExpectCallResult() *ExpectTrace { + cs, expErr, unExpErr := vm.GetExternalLog(context.Background(), c.env, c.ChainId, c.TxHash, c.LogIdx, c.MaxDataLen, c.Confirms) + if unExpErr != nil { + return NewExpectTrace(nil, nil, unExpErr) + } else if expErr != nil { + return NewExpectTrace(nil, expErr, nil) + } else { + pack, err := cs.ABIPack() + if err != nil { + panic(err) + } + return NewExpectTrace(pack, nil, nil) + } +} + +func TestApplyTransaction(t *testing.T) { + var ( + config = ¶ms.ChainConfig{ + ChainID: big.NewInt(3334), + 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), + MuirGlacierBlock: nil, + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + PisaBlock: big.NewInt(0), + ArrowGlacierBlock: nil, + ExternalCall: ¶ms.ExternalCallConfig{ + EnableBlockNumber: big.NewInt(0), + Version: 1, + SupportChainId: 4, + }, + } + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + ) + + var ( + db = rawdb.NewMemoryDatabase() + preAddr = common.HexToAddress("0x0000000000000000000000000000000000033303") + contractAddr = common.HexToAddress("0xa000000000000000000000000000000000000aaa") + gspec = &Genesis{ + Config: config, + Alloc: GenesisAlloc{ + addr1: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + common.HexToAddress("0xfd0810DD14796680f72adf1a371963d0745BCc64"): GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: math.MaxUint64, + }, + contractAddr: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: math.MaxUint64, + Code: common.FromHex("608060405234801561001057600080fd5b50600436106100415760003560e01c80632061536214610046578063518a3510146100775780638c95e054146100a7575b600080fd5b610060600480360381019061005b9190610510565b6100c5565b60405161006e9291906106df565b60405180910390f35b610091600480360381019061008c9190610510565b610382565b60405161009e91906106bd565b60405180910390f35b6100af6104df565b6040516100bc91906106a2565b60405180910390f35b606080600087878787876040516024016100e3959493929190610776565b6040516020818303038152906040527f99e20070000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090506000806203330373ffffffffffffffffffffffffffffffffffffffff168360405161018d919061068b565b6000604051808303816000865af19150503d80600081146101ca576040519150601f19603f3d011682016040523d82523d6000602084013e6101cf565b606091505b509150915081610214576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161020b90610756565b60405180910390fd5b60008a8a60018b6102259190610801565b8a8a60405160240161023b959493929190610776565b6040516020818303038152906040527f99e20070000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090506000806203330373ffffffffffffffffffffffffffffffffffffffff16836040516102e5919061068b565b6000604051808303816000865af19150503d8060008114610322576040519150601f19603f3d011682016040523d82523d6000602084013e610327565b606091505b50915091508161036c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161036390610736565b60405180910390fd5b8381975097505050505050509550959350505050565b60606000868686868660405160240161039f959493929190610776565b6040516020818303038152906040527f99e20070000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090506000806203330373ffffffffffffffffffffffffffffffffffffffff1683604051610449919061068b565b6000604051808303816000865af19150503d8060008114610486576040519150601f19603f3d011682016040523d82523d6000602084013e61048b565b606091505b5091509150816104d0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104c790610716565b60405180910390fd5b80935050505095945050505050565b6203330381565b6000813590506104f5816109dc565b92915050565b60008135905061050a816109f3565b92915050565b600080600080600060a0868803121561052c5761052b6108ff565b5b600061053a888289016104fb565b955050602061054b888289016104e6565b945050604061055c888289016104fb565b935050606061056d888289016104fb565b925050608061057e888289016104fb565b9150509295509295909350565b61059481610857565b82525050565b6105a381610869565b82525050565b60006105b4826107c9565b6105be81856107d4565b93506105ce81856020860161089d565b6105d781610904565b840191505092915050565b60006105ed826107c9565b6105f781856107e5565b935061060781856020860161089d565b80840191505092915050565b60006106206018836107f0565b915061062b82610915565b602082019050919050565b60006106436025836107f0565b915061064e8261093e565b604082019050919050565b60006106666025836107f0565b91506106718261098d565b604082019050919050565b61068581610893565b82525050565b600061069782846105e2565b915081905092915050565b60006020820190506106b7600083018461058b565b92915050565b600060208201905081810360008301526106d781846105a9565b905092915050565b600060408201905081810360008301526106f981856105a9565b9050818103602083015261070d81846105a9565b90509392505050565b6000602082019050818103600083015261072f81610613565b9050919050565b6000602082019050818103600083015261074f81610636565b9050919050565b6000602082019050818103600083015261076f81610659565b9050919050565b600060a08201905061078b600083018861067c565b610798602083018761059a565b6107a5604083018661067c565b6107b2606083018561067c565b6107bf608083018461067c565b9695505050505050565b600081519050919050565b600082825260208201905092915050565b600081905092915050565b600082825260208201905092915050565b600061080c82610893565b915061081783610893565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561084c5761084b6108d0565b5b828201905092915050565b600061086282610873565b9050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60005b838110156108bb5780820151818401526020810190506108a0565b838111156108ca576000848401525b50505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b6000601f19601f8301169050919050565b7f6661696c20746f2063726f737320636861696e2063616c6c0000000000000000600082015250565b7f63726f73732063616c6c2032206661696c20746f2063726f737320636861696e60008201527f2063616c6c000000000000000000000000000000000000000000000000000000602082015250565b7f63726f73732063616c6c2031206661696c20746f2063726f737320636861696e60008201527f2063616c6c000000000000000000000000000000000000000000000000000000602082015250565b6109e581610869565b81146109f057600080fd5b50565b6109fc81610893565b8114610a0757600080fd5b5056fea2646970667358221220cb96efc14e55caf807c664755d68fa44a3228605b33e42bc7be92933e03ba95364736f6c63430008070033"), + }, + }, + } + + globalchainId = config.ChainID + genesis = gspec.MustCommit(db) + ) + + externalClient, err := ethclient.Dial("https://rinkeby.infura.io/v3/4e3e18f80d8d4ad5959b7404e85e0143") + if err != nil { + t.Error(err) + } + + const RinkebyTxHash = "0x7ba399701b823976c367686562ca9fa11ecc81341d2b0026c5615740bd164e46" + const RinkebyTxHashNotFound = "0x0000000000000000000000000000000000000000000000000000000000000001" + + Args_CallOnce := NewCrossChainCallArgment(config, externalClient, 4, common.HexToHash(RinkebyTxHash), 0, 300, 10) + + Args_ExpectErrAsLogIdxExceed := NewCrossChainCallArgment(config, externalClient, 4, common.HexToHash(RinkebyTxHash), 2, 300, 10) + + Args_ExpectErrAsNotFound := NewCrossChainCallArgment(config, externalClient, 4, common.HexToHash(RinkebyTxHashNotFound), 2, 300, 10) + + Args_Twice_Trace0 := NewCrossChainCallArgment(config, externalClient, 4, common.HexToHash(RinkebyTxHash), 0, 300, 10) + Args_Twice_Trace1 := NewCrossChainCallArgment(config, externalClient, 4, common.HexToHash(RinkebyTxHash), 1, 300, 10) + Args_CallTwice := NewCrossChainCallArgment(config, externalClient, 4, common.HexToHash(RinkebyTxHash), 0, 300, 10) + + Args_ExpectErrChainIdNoSupport := NewCrossChainCallArgment(config, externalClient, 5, common.HexToHash(RinkebyTxHash), 0, 300, 10) + + _wrapTxs := []*WrapTx{ + &WrapTx{ + Tx: types.NewTx(&types.DynamicFeeTx{ + ChainID: globalchainId, + Nonce: 0, + To: &contractAddr, + Value: big.NewInt(0), + Gas: 5000000, + GasTipCap: big.NewInt(1000000000), + GasFeeCap: big.NewInt(6000000000), + Data: common.FromHex("0x518a351000000000000000000000000000000000000000000000000000000000000000047ba399701b823976c367686562ca9fa11ecc81341d2b0026c5615740bd164e460000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012c000000000000000000000000000000000000000000000000000000000000000a"), + }), + Args: Args_CallOnce, + ExpectTxData: Args_CallOnce.CallOncePack, + ExpectTraces: []*ExpectTrace{ + Args_CallOnce.CrossChainCallResultToExpectCallResult(), + }, + }, + // expect err match:CrossChainCall:logIdx out-of-bound + &WrapTx{ + Tx: types.NewTx(&types.DynamicFeeTx{ + ChainID: globalchainId, + Nonce: 0, + To: &contractAddr, + Value: big.NewInt(0), + Gas: 5000000, + GasTipCap: big.NewInt(1000000000), + GasFeeCap: big.NewInt(6000000000), + Data: common.FromHex("518a351000000000000000000000000000000000000000000000000000000000000000047ba399701b823976c367686562ca9fa11ecc81341d2b0026c5615740bd164e460000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000012c000000000000000000000000000000000000000000000000000000000000000a"), + }), + Args: Args_ExpectErrAsLogIdxExceed, + ExpectTxData: Args_ExpectErrAsLogIdxExceed.CallOncePack, + //ExpectTraces: []*ExpectTrace{ + // Args_ExpectErrAsLogIdxExceed.CrossChainCallResultToExpectCallResult(), + //}, + happenError: vm.NewExpectCallErr("CrossChainCall:logIdx out-of-bound"), + }, + // expect err txHash not found + &WrapTx{ + Tx: types.NewTx(&types.DynamicFeeTx{ + ChainID: globalchainId, + Nonce: 0, + To: &contractAddr, + Value: big.NewInt(0), + Gas: 5000000, + GasTipCap: big.NewInt(1000000000), + GasFeeCap: big.NewInt(6000000000), + Data: common.FromHex("518a3510000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000012c000000000000000000000000000000000000000000000000000000000000000a"), + }), + Args: Args_ExpectErrAsNotFound, + ExpectTxData: Args_ExpectErrAsNotFound.CallOncePack, + happenError: vm.NewExpectCallErr(ethereum.NotFound.Error()), + }, + // expect err chainId no support + &WrapTx{ + Tx: types.NewTx(&types.DynamicFeeTx{ + ChainID: globalchainId, + Nonce: 0, + To: &contractAddr, + Value: big.NewInt(0), + Gas: 5000000, + GasTipCap: big.NewInt(1000000000), + GasFeeCap: big.NewInt(6000000000), + Data: common.FromHex("518a351000000000000000000000000000000000000000000000000000000000000000057ba399701b823976c367686562ca9fa11ecc81341d2b0026c5615740bd164e460000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012c000000000000000000000000000000000000000000000000000000000000000a"), + }), + Args: Args_ExpectErrChainIdNoSupport, + ExpectTxData: Args_ExpectErrChainIdNoSupport.CallOncePack, + happenError: vm.NewExpectCallErr(fmt.Sprintf("CrossChainCall:chainId %d no support", Args_ExpectErrChainIdNoSupport.ChainId)), + }, + // external call twice + &WrapTx{ + Tx: types.NewTx(&types.DynamicFeeTx{ + ChainID: globalchainId, + Nonce: 0, + To: &contractAddr, + Value: big.NewInt(0), + Gas: 5000000, + GasTipCap: big.NewInt(1000000000), + GasFeeCap: big.NewInt(6000000000), + Data: common.FromHex("0x2061536200000000000000000000000000000000000000000000000000000000000000047ba399701b823976c367686562ca9fa11ecc81341d2b0026c5615740bd164e460000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012c000000000000000000000000000000000000000000000000000000000000000a"), + }), + Args: Args_CallTwice, + ExpectTxData: Args_CallTwice.CallTwicePack, + ExpectTraces: []*ExpectTrace{ + Args_Twice_Trace0.CrossChainCallResultToExpectCallResult(), + Args_Twice_Trace1.CrossChainCallResultToExpectCallResult(), + }, + }, + // call crossChainCall precompile contract directly + &WrapTx{ + Tx: types.NewTx(&types.DynamicFeeTx{ + ChainID: globalchainId, + Nonce: 0, + To: &preAddr, + Value: big.NewInt(0), + Gas: 5000000, + GasTipCap: big.NewInt(1000000000), + GasFeeCap: big.NewInt(6000000000), + Data: common.FromHex("0x99e2007000000000000000000000000000000000000000000000000000000000000000047ba399701b823976c367686562ca9fa11ecc81341d2b0026c5615740bd164e460000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012c000000000000000000000000000000000000000000000000000000000000000a"), + }), + //Args: Args_CallTwice, + //ExpectTxData: Args_CallTwice.CallTwicePack, + ExpectTraces: []*ExpectTrace{ + Args_CallOnce.CrossChainCallResultToExpectCallResult(), + }, + }, + } + + var wrapTxs []*WrapTx + for i, wrapTx := range _wrapTxs { + signer := types.LatestSignerForChainID(globalchainId) + signTx, err := types.SignTx(wrapTx.Tx, signer, key1) + if err != nil { + t.Error(err) + } + + wrapTx.Tx = signTx + + if wrapTx.ExpectTxData != nil { + txData, err := wrapTx.ExpectTxData() + if err != nil { + t.Fatal(err) + } + if common.Bytes2Hex(txData) != common.Bytes2Hex(wrapTx.Tx.Data()) { + t.Fatalf("%d Tx data no match. wrapTx.ExpectTxData():%s ,", i, common.Bytes2Hex(txData)) + } + } + + wrapTxs = append(wrapTxs, wrapTx) + } + + // evm executes a transaction while the external calling client is active + for _, wtx := range wrapTxs { + // prepare chainContext + cli := &clique.Clique{} + wtm := NewWrapTendermint(cli, nil) + chainContext := NewTestChainContext(wtm) + + // prepare block + block := genesis + gaspool := new(GasPool) + gaspool.AddGas(8000000000) + + buf := new(bytes.Buffer) + w := bufio.NewWriter(buf) + tracer := logger.NewJSONLogger(&logger.Config{}, w) + vmconfig := vm.Config{Debug: true, Tracer: tracer, ExternalCallClient: externalClient} + + _, statedb := MakePreState(db, gspec.Alloc, false) + _, crossCallResult, err := ApplyTransaction(config, chainContext, &addr1, gaspool, statedb, block.Header(), wtx.Tx, &wtx.GasUsed, vmconfig) + t.Log("CrossCallResult:", crossCallResult) + wtx.VerifyCallResult(crossCallResult, err, t) + wtx.SetExternalCallRes(crossCallResult) + } + + //evm executes a transaction while the external calling client is inactive + for index, wtx := range wrapTxs { + if wtx.happenError != nil { + continue + } + // prepare chainContext + cli := &clique.Clique{} + wtm := NewWrapTendermint(cli, nil) + chainContext := NewTestChainContext(wtm) + + // prepare block + block := genesis + gaspool := new(GasPool) + gaspool.AddGas(8000000000) + + var actualUsedGas uint64 = 0 + buf := new(bytes.Buffer) + w := bufio.NewWriter(buf) + tracer := logger.NewJSONLogger(&logger.Config{}, w) + // set the externalCallClient as nil + vmconfig := vm.Config{Debug: true, Tracer: tracer, ExternalCallClient: nil} + + _, statedb := MakePreState(db, gspec.Alloc, false) + + msg, err := wtx.Tx.AsMessage(types.MakeSigner(config, block.Header().Number), block.Header().BaseFee) + if err != nil { + t.Error(err) + } + // Create a new context to be used in the EVM environment + blockContext := NewEVMBlockContext(block.Header(), chainContext, &addr1) + vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, vmconfig) + + _, crossCallResult, err := applyTransaction(msg, config, chainContext, &addr1, gaspool, statedb, block.Header().Number, block.Header().Hash(), wtx.Tx, &actualUsedGas, vmenv, wtx.ExpectCCRBytes) + wtx.VerifyCallResult(crossCallResult, err, t) + + // compare gas use + if actualUsedGas != wtx.GasUsed { + t.Errorf("The gas consumption is different when the client is nil and not nil, txIndex=[%d] , nil gas used (%d) , no nil gas used (%d) ", index, actualUsedGas, wtx.GasUsed) + } + } +} + +func BenchmarkApplyTransactionWithCallResult(b *testing.B) { + var ( + config = ¶ms.ChainConfig{ + ChainID: big.NewInt(3334), + 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), + MuirGlacierBlock: nil, + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + PisaBlock: big.NewInt(0), + ArrowGlacierBlock: nil, + ExternalCall: ¶ms.ExternalCallConfig{ + EnableBlockNumber: big.NewInt(0), + Version: 1, + SupportChainId: 4, + }, + } + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + ) + + var ( + db = rawdb.NewMemoryDatabase() + contractAddr = common.HexToAddress("0xa000000000000000000000000000000000000aaa") + gspec = &Genesis{ + Config: config, + Alloc: GenesisAlloc{ + addr1: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + common.HexToAddress("0xfd0810DD14796680f72adf1a371963d0745BCc64"): GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: math.MaxUint64, + }, + contractAddr: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: math.MaxUint64, + Code: common.FromHex("608060405234801561001057600080fd5b50600436106100415760003560e01c80632061536214610046578063518a3510146100775780638c95e054146100a7575b600080fd5b610060600480360381019061005b9190610510565b6100c5565b60405161006e9291906106df565b60405180910390f35b610091600480360381019061008c9190610510565b610382565b60405161009e91906106bd565b60405180910390f35b6100af6104df565b6040516100bc91906106a2565b60405180910390f35b606080600087878787876040516024016100e3959493929190610776565b6040516020818303038152906040527f99e20070000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090506000806203330373ffffffffffffffffffffffffffffffffffffffff168360405161018d919061068b565b6000604051808303816000865af19150503d80600081146101ca576040519150601f19603f3d011682016040523d82523d6000602084013e6101cf565b606091505b509150915081610214576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161020b90610756565b60405180910390fd5b60008a8a60018b6102259190610801565b8a8a60405160240161023b959493929190610776565b6040516020818303038152906040527f99e20070000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090506000806203330373ffffffffffffffffffffffffffffffffffffffff16836040516102e5919061068b565b6000604051808303816000865af19150503d8060008114610322576040519150601f19603f3d011682016040523d82523d6000602084013e610327565b606091505b50915091508161036c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161036390610736565b60405180910390fd5b8381975097505050505050509550959350505050565b60606000868686868660405160240161039f959493929190610776565b6040516020818303038152906040527f99e20070000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090506000806203330373ffffffffffffffffffffffffffffffffffffffff1683604051610449919061068b565b6000604051808303816000865af19150503d8060008114610486576040519150601f19603f3d011682016040523d82523d6000602084013e61048b565b606091505b5091509150816104d0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104c790610716565b60405180910390fd5b80935050505095945050505050565b6203330381565b6000813590506104f5816109dc565b92915050565b60008135905061050a816109f3565b92915050565b600080600080600060a0868803121561052c5761052b6108ff565b5b600061053a888289016104fb565b955050602061054b888289016104e6565b945050604061055c888289016104fb565b935050606061056d888289016104fb565b925050608061057e888289016104fb565b9150509295509295909350565b61059481610857565b82525050565b6105a381610869565b82525050565b60006105b4826107c9565b6105be81856107d4565b93506105ce81856020860161089d565b6105d781610904565b840191505092915050565b60006105ed826107c9565b6105f781856107e5565b935061060781856020860161089d565b80840191505092915050565b60006106206018836107f0565b915061062b82610915565b602082019050919050565b60006106436025836107f0565b915061064e8261093e565b604082019050919050565b60006106666025836107f0565b91506106718261098d565b604082019050919050565b61068581610893565b82525050565b600061069782846105e2565b915081905092915050565b60006020820190506106b7600083018461058b565b92915050565b600060208201905081810360008301526106d781846105a9565b905092915050565b600060408201905081810360008301526106f981856105a9565b9050818103602083015261070d81846105a9565b90509392505050565b6000602082019050818103600083015261072f81610613565b9050919050565b6000602082019050818103600083015261074f81610636565b9050919050565b6000602082019050818103600083015261076f81610659565b9050919050565b600060a08201905061078b600083018861067c565b610798602083018761059a565b6107a5604083018661067c565b6107b2606083018561067c565b6107bf608083018461067c565b9695505050505050565b600081519050919050565b600082825260208201905092915050565b600081905092915050565b600082825260208201905092915050565b600061080c82610893565b915061081783610893565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561084c5761084b6108d0565b5b828201905092915050565b600061086282610873565b9050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60005b838110156108bb5780820151818401526020810190506108a0565b838111156108ca576000848401525b50505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b6000601f19601f8301169050919050565b7f6661696c20746f2063726f737320636861696e2063616c6c0000000000000000600082015250565b7f63726f73732063616c6c2032206661696c20746f2063726f737320636861696e60008201527f2063616c6c000000000000000000000000000000000000000000000000000000602082015250565b7f63726f73732063616c6c2031206661696c20746f2063726f737320636861696e60008201527f2063616c6c000000000000000000000000000000000000000000000000000000602082015250565b6109e581610869565b81146109f057600080fd5b50565b6109fc81610893565b8114610a0757600080fd5b5056fea2646970667358221220cb96efc14e55caf807c664755d68fa44a3228605b33e42bc7be92933e03ba95364736f6c63430008070033"), + }, + }, + } + + globalchainId = config.ChainID + genesis = gspec.MustCommit(db) + ) + + externalClient, err := ethclient.Dial("https://rinkeby.infura.io/v3/63aa34e959614d01a9a65d3f93b70e66") + if err != nil { + b.Error(err) + } + + const RinkebyTxHash = "0x7ba399701b823976c367686562ca9fa11ecc81341d2b0026c5615740bd164e46" + Args_CallOnce := NewCrossChainCallArgment(config, externalClient, 4, common.HexToHash(RinkebyTxHash), 0, 300, 10) + + gaspool := new(GasPool) + gaspool.AddGas(30000000) + cli := &clique.Clique{} + wtm := NewWrapTendermint(cli, nil) + chainContext := NewTestChainContext(wtm) + + // prepare block + block := genesis + + buf := new(bytes.Buffer) + w := bufio.NewWriter(buf) + tracer := logger.NewJSONLogger(&logger.Config{}, w) + // set the externalCallClient as nil + vmconfig := vm.Config{Debug: true, Tracer: tracer, ExternalCallClient: externalClient} + + _, statedb := MakePreState(db, gspec.Alloc, false) + for i := 0; i < b.N; i++ { + wrapTx := + &WrapTx{ + Tx: types.NewTx(&types.DynamicFeeTx{ + ChainID: globalchainId, + Nonce: uint64(i), + To: &contractAddr, + Value: big.NewInt(0), + Gas: 5000000, + GasTipCap: big.NewInt(1000000000), + GasFeeCap: big.NewInt(6000000000), + Data: common.FromHex("0x518a351000000000000000000000000000000000000000000000000000000000000000047ba399701b823976c367686562ca9fa11ecc81341d2b0026c5615740bd164e460000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012c000000000000000000000000000000000000000000000000000000000000000a"), + }), + Args: Args_CallOnce, + ExpectTxData: Args_CallOnce.CallOncePack, + ExpectCCRBytes: common.FromHex("f901ae01f901aaf901a7b901a0000000000000000000000000751320c36f413a6280ad54487766ae0f780b6f58000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000002dce721dc2d078c030530aeb5511eb76663a705797c2a4a4d41a70dddfb8efca9000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000028bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb00000000000000000000000000000000000000000000000001826b08"), + } + + signer := types.LatestSignerForChainID(globalchainId) + signTx, err := types.SignTx(wrapTx.Tx, signer, key1) + if err != nil { + b.Error(err) + } + + wrapTx.Tx = signTx + + if wrapTx.ExpectTxData != nil { + txData, err := wrapTx.ExpectTxData() + if err != nil { + b.Fatal(err) + } + if common.Bytes2Hex(txData) != common.Bytes2Hex(wrapTx.Tx.Data()) { + b.Fatalf("%d Tx data no match. wrapTx.ExpectTxData():%s ,", i, common.Bytes2Hex(txData)) + } + } + + var actualUsedGas uint64 = 0 + _, cs, err := ApplyTransaction(config, chainContext, &addr1, gaspool, statedb, block.Header(), wrapTx.Tx, &actualUsedGas, vmconfig) + b.Log("i:", i, "\nCS:", common.Bytes2Hex(cs), "\nGasUsed:", actualUsedGas) + if err != nil { + b.Fatal(err) + break + } + if !bytes.Equal(cs, wrapTx.ExpectCCRBytes) { + b.Fatal("cross_chain_result err") + } + if gaspool.Gas() < actualUsedGas { + break + } + statedb.Commit(config.IsEIP158(block.Number())) + + } +} + // 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 @@ -340,3 +1023,25 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr // Assemble and return the final block for sealing return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)) } + +func MakePreState(db ethdb.Database, accounts GenesisAlloc, snapshotter bool) (*snapshot.Tree, *state.StateDB) { + sdb := state.NewDatabase(db) + statedb, _ := state.New(common.Hash{}, sdb, nil) + for addr, a := range accounts { + statedb.SetCode(addr, a.Code) + statedb.SetNonce(addr, a.Nonce) + statedb.SetBalance(addr, a.Balance) + for k, v := range a.Storage { + statedb.SetState(addr, k, v) + } + } + // Commit and re-open to start with a clean state. + root, _ := statedb.Commit(false) + + var snaps *snapshot.Tree + if snapshotter { + snaps, _ = snapshot.New(db, sdb.TrieDB(), 1, root, false, true, false) + } + statedb, _ = state.New(root, sdb, snaps) + return snaps, statedb +} diff --git a/core/state_transition.go b/core/state_transition.go index def6ca7fd094..aadf3b47c9a5 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -18,6 +18,7 @@ package core import ( "fmt" + "github.com/ethereum/go-ethereum/rlp" "math" "math/big" @@ -82,9 +83,10 @@ type Message interface { // ExecutionResult includes all output after executing given evm // message no matter the execution itself is successful or not. type ExecutionResult struct { - UsedGas uint64 // Total used gas but include the refunded gas - Err error // Any error encountered during the execution(listed in core/vm/errors.go) - ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode) + UsedGas uint64 // Total used gas but include the refunded gas + Err error // Any error encountered during the execution(listed in core/vm/errors.go) + ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode) + CrossChainCallResults []byte // Return the external call result in the format rlp(version, abi.pack(evm.Interpreter().CrossChainCallResults())) } // Unwrap returns the internal evm error which allows us for further @@ -320,6 +322,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber, st.evm.Context.Random != nil); rules.IsBerlin { st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList()) } + var ( ret []byte vmerr error // vm errors do not effect consensus and are therefore not assigned to err @@ -332,6 +335,11 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value) } + // If an unexpected error occurs during evm, return immediately + if st.evm.CrossChainCallUnExpectErr() != nil { + return nil, st.evm.CrossChainCallUnExpectErr() + } + if !london { // Before EIP-3529: refunds were capped to gasUsed / 2 st.refundGas(params.RefundQuotient) @@ -345,6 +353,35 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip)) + // deal with the result of cross chain call + if len(st.evm.Interpreter().CrossChainCallResults()) != 0 { + results := st.evm.Interpreter().CrossChainCallResults() + + var version uint64 + if st.evm.ChainConfig().ExternalCall.Version != 0 { + version = st.evm.ChainConfig().ExternalCall.Version + } else { + version = 0 + } + + cr := vm.CrossChainCallResultsWithVersion{ + Version: version, + Results: results, + } + + cb, err := rlp.EncodeToBytes(cr) + if err != nil { + return nil, err + } + + return &ExecutionResult{ + UsedGas: st.gasUsed(), + Err: vmerr, + ReturnData: ret, + CrossChainCallResults: cb, + }, nil + } + return &ExecutionResult{ UsedGas: st.gasUsed(), Err: vmerr, diff --git a/core/types.go b/core/types.go index 4c5b74a49865..77d31929a39f 100644 --- a/core/types.go +++ b/core/types.go @@ -47,5 +47,5 @@ type Processor interface { // Process processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb and applying any rewards to both // the processor (coinbase) and any included uncles. - Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) + Process(block *types.Block, statedb *state.StateDB, cfg vm.Config, usingClient bool) (types.Receipts, []*types.Log, uint64, error) } diff --git a/core/types/block.go b/core/types/block.go index 85cd95df249b..a1378a3cb35d 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -173,6 +173,14 @@ func (h *Header) EmptyReceipts() bool { return h.ReceiptHash == EmptyRootHash } +// GetTxExternalCallResult return the external_call_result when the txHash matches between h.txHash and tx.Hash() +func (h *Header) GetTxExternalCallResult(tx *Transaction) []byte { + if h.TxHash == tx.Hash() { + return h.Extra + } + return nil +} + // Body is a simple (mutable, non-safe) data container for storing and moving // a block's data contents (transactions and uncles) together. type Body struct { @@ -365,6 +373,16 @@ func (b *Block) SanityCheck() error { return b.header.SanityCheck() } +// GetExternalCallResult return the external_call_result of the given transaction from uncles when it exists +func (b *Block) GetExternalCallResult(txHash common.Hash) []byte { + for _, u := range b.uncles { + if u.TxHash == txHash { + return u.Extra + } + } + return nil +} + type writeCounter common.StorageSize func (c *writeCounter) Write(b []byte) (int, error) { diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index 58c95071b288..ac5feb5a0962 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -70,6 +70,8 @@ var ( NewEIP2930Signer(big.NewInt(1)), common.Hex2Bytes("c9519f4f2b30335884581971573fadf60c6204f59a911df35ee8a540456b266032f1e8e2c5dd761f9e4f88f41c8310aeaba26a8bfcdacfedfa12ec3862d3752101"), ) + + globalchainId = big.NewInt(1) ) func TestDecodeEmptyTypedTx(t *testing.T) { @@ -96,7 +98,7 @@ func TestTransactionEncode(t *testing.T) { if err != nil { t.Fatalf("encode error: %v", err) } - should := common.FromHex("f86103018207d094b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a8255441ca098ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4aa08887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3") + should := common.FromHex("f86203018207d094b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a8255441ca098ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4aa08887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a380") if !bytes.Equal(txb, should) { t.Errorf("encoded RLP mismatch, got %x", txb) } @@ -138,14 +140,14 @@ func TestEIP2930Signer(t *testing.T) { signer: signer1, wantSignerHash: common.HexToHash("846ad7672f2a3a40c1f959cd4a8ad21786d620077084d84c8d7c077714caa139"), wantSenderErr: ErrInvalidChainId, - wantHash: common.HexToHash("1ccd12d8bbdb96ea391af49a35ab641e219b2dd638dea375f2bc94dd290f2549"), + wantHash: common.HexToHash("235b2d2101bef0b05e6b7f7a711fe0297e3a2c95da1b6442b439b76c00731090"), }, { tx: tx1, signer: signer1, wantSenderErr: ErrInvalidSig, wantSignerHash: common.HexToHash("846ad7672f2a3a40c1f959cd4a8ad21786d620077084d84c8d7c077714caa139"), - wantHash: common.HexToHash("1ccd12d8bbdb96ea391af49a35ab641e219b2dd638dea375f2bc94dd290f2549"), + wantHash: common.HexToHash("235b2d2101bef0b05e6b7f7a711fe0297e3a2c95da1b6442b439b76c00731090"), }, { // This checks what happens when trying to sign an unsigned tx for the wrong chain. @@ -196,7 +198,7 @@ func TestEIP2718TransactionEncode(t *testing.T) { if err != nil { t.Fatalf("encode error: %v", err) } - want := common.FromHex("b86601f8630103018261a894b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a825544c001a0c9519f4f2b30335884581971573fadf60c6204f59a911df35ee8a540456b2660a032f1e8e2c5dd761f9e4f88f41c8310aeaba26a8bfcdacfedfa12ec3862d37521") + want := common.FromHex("b86701f8640103018261a894b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a825544c001a0c9519f4f2b30335884581971573fadf60c6204f59a911df35ee8a540456b2660a032f1e8e2c5dd761f9e4f88f41c8310aeaba26a8bfcdacfedfa12ec3862d3752180") if !bytes.Equal(have, want) { t.Errorf("encoded RLP mismatch, got %x", have) } @@ -207,7 +209,7 @@ func TestEIP2718TransactionEncode(t *testing.T) { if err != nil { t.Fatalf("encode error: %v", err) } - want := common.FromHex("01f8630103018261a894b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a825544c001a0c9519f4f2b30335884581971573fadf60c6204f59a911df35ee8a540456b2660a032f1e8e2c5dd761f9e4f88f41c8310aeaba26a8bfcdacfedfa12ec3862d37521") + want := common.FromHex("01f8640103018261a894b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a825544c001a0c9519f4f2b30335884581971573fadf60c6204f59a911df35ee8a540456b2660a032f1e8e2c5dd761f9e4f88f41c8310aeaba26a8bfcdacfedfa12ec3862d3752180") if !bytes.Equal(have, want) { t.Errorf("encoded RLP mismatch, got %x", have) } @@ -227,13 +229,31 @@ func defaultTestKey() (*ecdsa.PrivateKey, common.Address) { } func TestRecipientEmpty(t *testing.T) { - _, addr := defaultTestKey() - tx, err := decodeTx(common.Hex2Bytes("f8498080808080011ca09b16de9d5bdee2cf56c28d16275a4da68cd30273e2525f3959f5d62557489921a0372ebd8fb3345f7db7b5a86d42e24d36e983e259b0664ceb8c227ec9af572f3d")) + //_, addr := defaultTestKey() + etx := NewTx(&LegacyTx{ + Nonce: 3, + Value: big.NewInt(10), + Gas: 25000, + GasPrice: big.NewInt(1), + Data: common.FromHex("554112233444"), + }) + + key, _ := crypto.GenerateKey() + addr := crypto.PubkeyToAddress(key.PublicKey) + + signer := &HomesteadSigner{} + signTx, err := SignTx(etx, signer, key) + if err != nil { + t.Error(err) + } + + toBytes, err := rlp.EncodeToBytes(signTx) + tx, err := decodeTx(toBytes) if err != nil { t.Fatal(err) } - from, err := Sender(HomesteadSigner{}, tx) + from, err := Sender(signer, tx) if err != nil { t.Fatal(err) } @@ -243,14 +263,32 @@ func TestRecipientEmpty(t *testing.T) { } func TestRecipientNormal(t *testing.T) { - _, addr := defaultTestKey() - tx, err := decodeTx(common.Hex2Bytes("f85d80808094000000000000000000000000000000000000000080011ca0527c0d8f5c63f7b9f41324a7c8a563ee1190bcbf0dac8ab446291bdbf32f5c79a0552c4ef0a09a04395074dab9ed34d3fbfb843c2f2546cc30fe89ec143ca94ca6")) + key, _ := crypto.GenerateKey() + addr := crypto.PubkeyToAddress(key.PublicKey) + + tx0 := NewTx(&LegacyTx{ + Nonce: 3, + To: &testAddr, + Value: big.NewInt(10), + Gas: 25000, + GasPrice: big.NewInt(1), + Data: common.FromHex("554112233444"), + }) + + signer := &HomesteadSigner{} + signTx, err := SignTx(tx0, signer, key) + if err != nil { + t.Error(err) + } + + toBytes, err := rlp.EncodeToBytes(signTx) + tx, err := decodeTx(toBytes) if err != nil { t.Fatal(err) } - from, err := Sender(HomesteadSigner{}, tx) + from, err := Sender(signer, tx) if err != nil { t.Fatal(err) } diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 1215c51035f7..112bca73c941 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -18,6 +18,8 @@ package vm import ( "bytes" + "context" + "crypto/sha256" "encoding/binary" "encoding/hex" @@ -25,6 +27,8 @@ import ( "fmt" "math/big" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" @@ -50,10 +54,18 @@ type PrecompiledContractCallEnv struct { caller ContractRef } +func NewPrecompiledContractCallEnv(evm *EVM, caller ContractRef) *PrecompiledContractCallEnv { + return &PrecompiledContractCallEnv{evm: evm, caller: caller} +} + type PrecompiledContractWithEVM interface { RunWith(env *PrecompiledContractCallEnv, input []byte) ([]byte, error) } +type PrecompiledContractToExternalCall interface { + RunWith(env *PrecompiledContractCallEnv, input []byte) ([]byte, uint64, error) +} + // PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum // contracts used in the Frontier and Homestead releases. var PrecompiledContractsHomestead = map[common.Address]PrecompiledContract{ @@ -118,6 +130,7 @@ var PrecompiledContractsPisa = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{9}): &blake2F{}, common.BytesToAddress([]byte{3, 0x33, 1}): &systemContractDeployer{}, common.BytesToAddress([]byte{3, 0x33, 2}): &sstoragePisa{}, + common.BytesToAddress([]byte{3, 0x33, 3}): &crossChainCall{}, } // PrecompiledContractsBLS contains the set of pre-compiled Ethereum @@ -181,7 +194,12 @@ func RunPrecompiledContract(env *PrecompiledContractCallEnv, p PrecompiledContra return nil, 0, ErrOutOfGas } suppliedGas -= gasCost - if pw, ok := p.(PrecompiledContractWithEVM); ok { + if pw, ok := p.(PrecompiledContractToExternalCall); ok { + var actualGasUsed uint64 + ret, actualGasUsed, err = pw.RunWith(env, input) + // gas refund + suppliedGas += gasCost - actualGasUsed + } else if pw, ok := p.(PrecompiledContractWithEVM); ok { ret, err = pw.RunWith(env, input) } else { ret, err = p.Run(input) @@ -1203,3 +1221,286 @@ func (c *bls12381MapG2) Run(input []byte) ([]byte, error) { // Encode the G2 point to 256 bytes return g.EncodePoint(r), nil } + +var ( + // getLogByTxHash(uint256 chainId , bytes32 txHash, uint256 logIdx ,uint256 maxDataLen, uint256 confirms) + getLogByTxHashId, _ = hex.DecodeString("99e20070") // getLogByTxHash(uint256,bytes32,uint256,uint256,uint256) +) + +type crossChainCall struct { +} + +// RequiredGas is the maximum gas consumption that will calculate the cross_chain_call +func (c *crossChainCall) RequiredGas(input []byte) uint64 { + if len(input) != 164 { + return 0 + } + + maxDataLen := new(big.Int).SetBytes(getData(input, 4+3*32, 32)).Uint64() + inputGas := uint64(len(input)) * params.ExternalCallByteGasCost + + var callResultGas uint64 = (32 + 32*7) * params.ExternalCallByteGasCost // pay address and topics + if maxDataLen%32 != 0 { + maxDataLen = maxDataLen + (32 - maxDataLen%32) + } + callResultGas += maxDataLen * params.ExternalCallByteGasCost + gasUsed := callResultGas + inputGas + params.ExternalCallGas + /* + The gas calculation formula is as follows: + gas_overpay = ExternalCallByteGasCost * (address_len + topics_len + data_len) + ExternalCallGas + + address_len = 32 + 000000000000000000000000751320c36f413a6280ad54487766ae0f780b6f58 + + topics_len = 3 *32 + 32 * 4(consider that the max number of topics is 4) + 0000000000000000000000000000000000000000000000000000000000000060 + 00000000000000000000000000000000000000000000000000000000000000c0 + 0000000000000000000000000000000000000000000000000000000000000002 + dce721dc2d078c030530aeb5511eb76663a705797c2a4a4d41a70dddfb8efca9 + 0000000000000000000000000000000000000000000000000000000000000001 + + data_len= 32 + (32 - maxDataLen % 32) + maxDatalen + 00000000000000000000000000000000000000000000000000000000000000c0 + 0000000000000000000000000000000000000000000000000000000000000002 + 0000000000000000000000000000000000000000000000000000000000000001 + 0000000000000000000000000000000000000000000000000000000000000060 + 0000000000000000000000000000000000000000000000000000000000000028 + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + bbbbbbbbbbbbbbbb000000000000000000000000000000000000000000000000 + */ + + return gasUsed +} + +func (c *crossChainCall) Run(input []byte) ([]byte, error) { + return nil, nil +} + +func (c *crossChainCall) RunWith(env *PrecompiledContractCallEnv, input []byte) ([]byte, uint64, error) { + if len(input) != 164 { + return nil, 0, fmt.Errorf("invalid length of input") + } + + if env.evm.ChainConfig().ExternalCall.EnableBlockNumber == nil { + return nil, 0, fmt.Errorf("crossChainCall: external call disable") + } + + if env.evm.Context.BlockNumber.Cmp(env.evm.ChainConfig().ExternalCall.EnableBlockNumber) == -1 { + return nil, 0, fmt.Errorf("cross chain call is active when when blockNumber reaches %d \n", env.evm.ChainConfig().ExternalCall.EnableBlockNumber.Int64()) + } + + ctx := context.Background() + if bytes.Equal(input[0:4], getLogByTxHashId) { + + client := env.evm.ExternalCallClient() + var list *CrossChainCallResults + + if client == nil { + Idx := env.evm.Interpreter().CallResultIdx() + if Idx >= uint64(len(env.evm.Interpreter().CrossChainCallResults())) { + // unexpect error + env.evm.setCrossChainCallUnExpectErr(ErrOutOfBoundsTracePtr) + return nil, 0, ErrOutOfBoundsTracePtr + } + list = env.evm.Interpreter().CrossChainCallResults()[Idx] + env.evm.Interpreter().AddCallResultIdx() + + if !list.Success { + return list.CallRes, list.GasUsed, ErrExecutionReverted + } + + return list.CallRes, list.GasUsed, nil + } else { + chainId := new(big.Int).SetBytes(getData(input, 4, 32)).Uint64() + txHash := common.BytesToHash(getData(input, 4+32, 32)) + logIdx := new(big.Int).SetBytes(getData(input, 4+64, 32)).Uint64() + maxDataLen := new(big.Int).SetBytes(getData(input, 4+96, 32)).Uint64() + confirms := new(big.Int).SetBytes(getData(input, 4+128, 32)).Uint64() + + callres, expErr, unexpErr := GetExternalLog(ctx, env, chainId, txHash, logIdx, maxDataLen, confirms) + + if unexpErr != nil { + env.evm.setCrossChainCallUnExpectErr(unexpErr) + return nil, 0, unexpErr + } else if expErr != nil { + // expect error uses the same error handling method as unexpect err + env.evm.setCrossChainCallUnExpectErr(expErr) + return nil, 0, expErr + } else { + // calculate actual cost of gas + actualGasUsed := callres.GasCost(params.ExternalCallByteGasCost) + actualGasUsed += uint64(len(input)) * params.ExternalCallByteGasCost + actualGasUsed += params.ExternalCallGas + + resultValuePack, err := callres.ABIPack() + if err != nil { + env.evm.setCrossChainCallUnExpectErr(err) + return nil, 0, err + } + list = &CrossChainCallResults{ + CallRes: resultValuePack, + Success: true, + GasUsed: actualGasUsed, + } + env.evm.Interpreter().AppendCrossChainCallResults(list) + + return list.CallRes, list.GasUsed, nil + } + + } + + } + + env.evm.setCrossChainCallUnExpectErr(ErrUnsupportMethod) + return nil, 0, ErrUnsupportMethod +} + +func GetExternalLog(ctx context.Context, env *PrecompiledContractCallEnv, chainId uint64, txHash common.Hash, logIdx uint64, maxDataLen uint64, confirms uint64) (cr *GetLogByTxHash, expErr *ExpectCallErr, unExpErr error) { + client := env.evm.ExternalCallClient() + + if chainId != env.evm.ChainConfig().ExternalCall.SupportChainId { + // expect error + expErr = NewExpectCallErr(fmt.Sprintf("CrossChainCall:chainId %d no support", chainId)) + return nil, expErr, nil + } + + receipt, err := client.TransactionReceipt(ctx, txHash) + if err != nil { + if err == ethereum.NotFound { + // expect Error + return nil, NewExpectCallErr(ethereum.NotFound.Error()), nil + } + // unexpect error + return nil, nil, err + } + + happenedBlockNumber := receipt.BlockNumber + latestBlockNumber, err := client.BlockNumber(ctx) + if err != nil { + // unexpect error + return nil, nil, err + } + + if latestBlockNumber-happenedBlockNumber.Uint64() < confirms { + // expect error + return nil, NewExpectCallErr("CrossChainCall:confirms no enough"), nil + } + + if logIdx >= uint64(len(receipt.Logs)) { + // expect error + return nil, NewExpectCallErr("CrossChainCall:logIdx out-of-bound"), nil + } + log := receipt.Logs[logIdx] + + var data []byte + var actualDataLen = uint64(len(log.Data)) + if actualDataLen <= maxDataLen { + data = log.Data + } else { + data = getData(log.Data, 0, maxDataLen) + actualDataLen = maxDataLen + } + + callres, err := NewGetLogByTxHash(log.Address, log.Topics, data) + if err != nil { + return nil, nil, err + } + + return callres, nil, nil + +} + +type GetLogByTxHash struct { + // address of the contract that generated the event + Address common.Address `json:"address" gencodec:"required"` + // list of topics provided by the contract. + Topics []common.Hash `json:"topics" gencodec:"required"` + // supplied by the contract, usually ABI-encoded + //Data []byte `json:"data" gencodec:"required"` + Data []byte `json:"data" gencodec:"required"` + + Args abi.Arguments +} + +func NewGetLogByTxHash(address common.Address, topics []common.Hash, data []byte) (*GetLogByTxHash, error) { + arg1Type, err := abi.NewType("address", "", nil) + if err != nil { + return nil, err + } + arg2Type, err := abi.NewType("bytes32[]", "", nil) + if err != nil { + return nil, err + } + arg3Type, err := abi.NewType("bytes", "", nil) + if err != nil { + return nil, err + } + + arg1 := abi.Argument{Name: "address", Type: arg1Type, Indexed: false} + arg2 := abi.Argument{Name: "topics", Type: arg2Type, Indexed: false} + arg3 := abi.Argument{Name: "data", Type: arg3Type, Indexed: false} + + var args = abi.Arguments{arg1, arg2, arg3} + + return &GetLogByTxHash{Address: address, Topics: topics, Data: data, Args: args}, nil +} + +func NewCallResultEmpty() (*GetLogByTxHash, error) { + arg1Type, err := abi.NewType("address", "", nil) + if err != nil { + return nil, err + } + arg2Type, err := abi.NewType("bytes32[]", "", nil) + if err != nil { + return nil, err + } + arg3Type, err := abi.NewType("bytes", "", nil) + if err != nil { + return nil, err + } + + arg1 := abi.Argument{Name: "address", Type: arg1Type, Indexed: false} + arg2 := abi.Argument{Name: "topics", Type: arg2Type, Indexed: false} + arg3 := abi.Argument{Name: "data", Type: arg3Type, Indexed: false} + + var args = abi.Arguments{arg1, arg2, arg3} + + return &GetLogByTxHash{Args: args}, nil +} + +func (c *GetLogByTxHash) ABIPack() ([]byte, error) { + packResult, err := c.Args.Pack(c.Address, c.Topics, c.Data) + if err != nil { + return nil, err + } + return packResult, nil +} + +func (c *GetLogByTxHash) GasCost(perBytePrice uint64) uint64 { + pack, _ := c.ABIPack() + return uint64(len(pack)) * perBytePrice +} + +type ExpectCallErr struct { + ErrMsg string +} + +func NewExpectCallErr(errMsg string) *ExpectCallErr { + return &ExpectCallErr{ErrMsg: errMsg} +} + +func (c *ExpectCallErr) Error() string { + return fmt.Sprintf("Expect Error:%s", c.ErrMsg) +} + +type CrossChainCallResults struct { + CallRes []byte + // todo + Success bool // judge by expect error or unexpect error? // false condition: + GasUsed uint64 +} + +type CrossChainCallResultsWithVersion struct { + Version uint64 + Results []*CrossChainCallResults +} diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index e2450fe4644c..6188f99276e9 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -20,6 +20,8 @@ import ( "bytes" "encoding/json" "fmt" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/params" "io/ioutil" "testing" "time" @@ -29,10 +31,10 @@ import ( // precompiledTest defines the input/output pairs for precompiled contract tests. type precompiledTest struct { - Input, Expected string - Gas uint64 - Name string - NoBenchmark bool // Benchmark primarily the worst-cases + Input, Expected, Want string + Gas uint64 + Name string + NoBenchmark bool // Benchmark primarily the worst-cases } // precompiledFailureTest defines the input/error pairs for precompiled @@ -91,12 +93,46 @@ var blake2FMalformedInputTests = []precompiledFailureTest{ }, } +var crossChainCallTest = []precompiledTest{ + { + Input: "99e20070000000000000000000000000000000000000000000000000000000000000000419c0c9b16f4e5cf388581ce71aea86641fdd877ce11af2c60d8db523cd2e02e3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000a", + Want: "000000000000000000000000a1230a772e3501b09fc6dcae819889bf30d1415e000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000002dce721dc2d078c030530aeb5511eb76663a705797c2a4a4d41a70dddfb8efca9000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002aaaa000000000000000000000000000000000000000000000000000000000000 ", + }, +} + +func TestCrossChainCall(t *testing.T) { + var test precompiledTest = crossChainCallTest[0] + p := &crossChainCall{} + + in := common.Hex2Bytes(test.Input) + gas := p.RequiredGas(in) + + eClient, err := ethclient.Dial("https://rinkeby.infura.io/v3/4e3e18f80d8d4ad5959b7404e85e0143") + if err != nil { + t.Error(err) + } + + evmConfig := Config{ExternalCallClient: eClient} + evm := NewEVM(BlockContext{}, TxContext{}, nil, params.Web3QGalileoChainConfig, evmConfig) + evmInterpreter := NewEVMInterpreter(evm, evm.Config) + evm.interpreter = evmInterpreter + + if res, _, err := RunPrecompiledContract(&PrecompiledContractCallEnv{evm: evm}, p, in, gas); err != nil { + t.Error(err) + } else { + if !bytes.Equal(common.Hex2Bytes(test.Want), res) { + t.Error("\ngot: ", common.Bytes2Hex(res), "\nwant:", test.Want) + } + } +} + func testPrecompiled(addr string, test precompiledTest, t *testing.T) { 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) { 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)) @@ -118,7 +154,9 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) { 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) } diff --git a/core/vm/errors.go b/core/vm/errors.go index 16b03383adfe..d5fcd98fbfa7 100644 --- a/core/vm/errors.go +++ b/core/vm/errors.go @@ -38,6 +38,9 @@ var ( ErrNonceUintOverflow = errors.New("nonce uint64 overflow") ErrCodeInsufficientStake = errors.New("insufficient staking for code") + // ErrOutOfBoundsTracePtr which happen when execute operation of cross chain call + ErrOutOfBoundsTracePtr = errors.New("CrossChainCall: tracePtr out-of-bound") + ErrUnsupportMethod = errors.New("unsupported method") // errStopToken is an internal token indicating interpreter loop termination, // never returned to outside callers. errStopToken = errors.New("stop token") diff --git a/core/vm/evm.go b/core/vm/evm.go index 02336b579cc5..2ef66a45f6a2 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -17,12 +17,14 @@ package vm import ( + "github.com/ethereum/go-ethereum/core/types" "math/big" "sync/atomic" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/sstorage" "github.com/holiman/uint256" @@ -106,6 +108,7 @@ type EVM struct { // Context provides auxiliary blockchain related information Context BlockContext TxContext + // StateDB gives access to the underlying state StateDB StateDB // Depth is the current call stack @@ -128,6 +131,8 @@ type EVM struct { // available gas is calculated in gasCall* according to the 63/64 rule and later // applied in opCall*. callGasTemp uint64 + + crossChainCallUnExpectErr error } // NewEVM returns a new EVM. The returned EVM is not thread safe and should @@ -145,11 +150,52 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig return evm } +func (evm *EVM) setCrossChainCallUnExpectErr(err error) { + if evm.crossChainCallUnExpectErr == nil { + evm.crossChainCallUnExpectErr = err + } +} + +func (evm *EVM) CrossChainCallUnExpectErr() error { + return evm.crossChainCallUnExpectErr +} + +func (evm *EVM) ExternalCallClient() *ethclient.Client { + return evm.Config.ExternalCallClient +} + +func (evm *EVM) SetCrossChainCallResults(result []byte) { + if evm.ExternalCallClient() == nil && evm.EnableExternalCall() && len(result) != 0 { + evm.Interpreter().SetCrossChainCallResults(result) + } +} + +func (evm *EVM) EnableExternalCall() bool { + if evm.ChainConfig().ExternalCall.EnableBlockNumber != nil && evm.Context.BlockNumber.Cmp(evm.ChainConfig().ExternalCall.EnableBlockNumber) != -1 { + return true + } + return false +} + +func (evm *EVM) SetCrossChainCallResultsByUncles(uncles []*types.Header, tx *types.Transaction) { + if evm.ExternalCallClient() == nil { + if len(uncles) != 0 { + for _, u := range uncles { + if u.TxHash == tx.Hash() { + evm.Interpreter().SetCrossChainCallResults(u.Extra) + break + } + } + } + } +} + // 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 + evm.interpreter.resetCrossChainCallResultsAndResultIdx() } // Cancel cancels any running EVM operation. This may be called concurrently and diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index df61980695fc..06dcf145564f 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -17,11 +17,13 @@ package vm import ( + "github.com/ethereum/go-ethereum/ethclient" "hash" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" ) // Config are the configuration options for the Interpreter @@ -34,7 +36,10 @@ type Config struct { JumpTable *JumpTable // EVM instruction table, automatically populated if unset ExtraEips []int // Additional EIPS that are to be enabled - IsJsonRpc bool // Whether the call is in context of JsonRpc + + ExternalCallClient *ethclient.Client // This client is used to make external calls + + IsJsonRpc bool // Whether the call is in context of JsonRpc } // ScopeContext contains the things that are per-call, such as stack and memory, @@ -63,6 +68,11 @@ type EVMInterpreter struct { readOnly bool // Whether to throw on stateful modifications returnData []byte // Last CALL's return data for subsequent reuse + + // crossChainCallResultList will store the return value of each cross-chain call, + // and the callResultIdx will increase by 1 for each cross-chain call. + crossChainCallResults []*CrossChainCallResults + callResultIdx uint64 } // NewEVMInterpreter returns a new instance of the Interpreter. @@ -108,6 +118,38 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { } } +func (in *EVMInterpreter) CallResultIdx() uint64 { + return in.callResultIdx +} + +func (in *EVMInterpreter) AddCallResultIdx() { + in.callResultIdx++ +} + +func (in *EVMInterpreter) CrossChainCallResults() []*CrossChainCallResults { + return in.crossChainCallResults +} + +func (in *EVMInterpreter) AppendCrossChainCallResults(trace *CrossChainCallResults) []*CrossChainCallResults { + in.crossChainCallResults = append(in.crossChainCallResults, trace) + return in.crossChainCallResults +} + +func (in *EVMInterpreter) SetCrossChainCallResults(b []byte) error { + cr := &CrossChainCallResultsWithVersion{} + err := rlp.DecodeBytes(b, cr) + if err != nil { + return err + } + in.crossChainCallResults = cr.Results + return nil +} + +func (in *EVMInterpreter) resetCrossChainCallResultsAndResultIdx() { + in.crossChainCallResults = nil + in.callResultIdx = 0 +} + // 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. // diff --git a/eth/backend.go b/eth/backend.go index 354526993393..d79db36a41bf 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -20,6 +20,7 @@ package eth import ( "errors" "fmt" + "github.com/ethereum/go-ethereum/ethclient" "math/big" "runtime" "sync" @@ -60,6 +61,11 @@ import ( "github.com/ethereum/go-ethereum/rpc" ) +const ( + NodeWithExternalCallClient = 1 + NodeWithoutExternalCallClient = 2 +) + // Config contains the configuration options of the ETH protocol. // Deprecated: use ethconfig.Config instead. type Config = ethconfig.Config @@ -169,8 +175,33 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { } } - log.Info("Initialised chain configuration", "config", chainConfig) + var externalCallClient *ethclient.Client + if chainConfig.ExternalCall != nil { + if config.ExternalCallSupportChainId != 0 { + chainConfig.ExternalCall.SupportChainId = config.ExternalCallSupportChainId + } + if config.ExternalCallRpc != "" { + chainConfig.ExternalCall.CallRpc = config.ExternalCallRpc + } + if config.ExternalCallEnableBlockNumber != nil { + chainConfig.ExternalCall.EnableBlockNumber = config.ExternalCallEnableBlockNumber + } + + if chainConfig.ExternalCall.EnableBlockNumber != nil { + // initialize external call client + externalCallClient, err = ethclient.Dial(chainConfig.ExternalCall.CallRpc) + if err != nil { + log.Error("Failed to initialize externalCallClient", "error", err) + return nil, err + } + } + + if chainConfig.ExternalCall.EnableBlockNumber != nil && config.IsMiner && externalCallClient == nil { + return nil, fmt.Errorf("VALIDATOR role must have an active external_call_client When CHAIN supports EXTERNAL_CALL") + } + + } if err := pruner.RecoverPruning(stack.ResolvePath(""), chainDb, stack.ResolvePath(config.TrieCleanCacheJournal)); err != nil { log.Error("Failed to recover state", "error", err) } @@ -210,8 +241,10 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { } } var ( - vmConfig = vm.Config{ + vmConfig = &vm.Config{ EnablePreimageRecording: config.EnablePreimageRecording, + // set up the externalCall config + ExternalCallClient: externalCallClient, } cacheConfig = &core.CacheConfig{ TrieCleanLimit: config.TrieCleanCache, @@ -225,10 +258,11 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { Preimages: config.Preimages, } ) - eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit) + eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, *vmConfig, eth.shouldPreserve, &config.TxLookupLimit) if err != nil { return nil, err } + // Rewind the chain in case of an incompatible config upgrade. if compat, ok := genesisErr.(*params.ConfigCompatError); ok { log.Warn("Rewinding chain to upgrade configuration", "err", compat) diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 2565b3e417ea..c809d87080d6 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -218,6 +218,12 @@ type Config struct { ValChainId uint64 ValidatorChangeEpochId uint64 + // ExternalCall config + IsMiner bool + ExternalCallEnableBlockNumber *big.Int + ExternalCallSupportChainId uint64 + ExternalCallRpc string + // Sstorage config SstorageFiles []string `toml:",omitempty"` SstorageShards []string `toml:",omitempty"` diff --git a/eth/state_accessor.go b/eth/state_accessor.go index f01db93a6785..243e950263f2 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -131,7 +131,7 @@ func (eth *Ethereum) StateAtBlock(block *types.Block, reexec uint64, base *state if current = eth.blockchain.GetBlockByNumber(next); current == nil { return nil, fmt.Errorf("block #%d not found", next) } - _, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{}) + _, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{}, false) if err != nil { return nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err) } diff --git a/eth/tracers/api.go b/eth/tracers/api.go index b16b68c1fa04..548ecfa43208 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -281,7 +281,7 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config TxIndex: i, TxHash: tx.Hash(), } - res, err := api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, config) + res, err := api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, config, task.block) if err != nil { task.results[i] = &txTraceResult{Error: err.Error()} log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) @@ -518,6 +518,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config if err != nil { return nil, err } + var ( roots []common.Hash signer = types.MakeSigner(api.backend.ChainConfig(), block.Number()) @@ -532,6 +533,8 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{}) ) statedb.Prepare(tx.Hash(), i) + // set the external_call_result from the block.Uncles() + vmenv.SetCrossChainCallResultsByUncles(block.Uncles(), tx) if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err) // We intentionally don't return the error here: if we do, then the RPC server will not @@ -606,7 +609,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac TxIndex: task.index, TxHash: txs[task.index].Hash(), } - res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config) + res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config, block) if err != nil { results[task.index] = &txTraceResult{Error: err.Error()} continue @@ -626,6 +629,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac msg, _ := tx.AsMessage(signer, block.BaseFee()) statedb.Prepare(tx.Hash(), i) vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{}) + vmenv.SetCrossChainCallResults(block.GetExternalCallResult(tx.Hash())) if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { failed = err break @@ -737,8 +741,10 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block } } // Execute the transaction and flush any traces to disk - vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf) statedb.Prepare(tx.Hash(), i) + // set the external call result + vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf) + vmenv.SetCrossChainCallResults(block.GetExternalCallResult(tx.Hash())) _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())) if writer != nil { writer.Flush() @@ -801,7 +807,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * TxIndex: int(index), TxHash: hash, } - return api.traceTx(ctx, msg, txctx, vmctx, statedb, config) + return api.traceTx(ctx, msg, txctx, vmctx, statedb, config, block) } // TraceCall lets you trace a given eth_call. It collects the structured logs @@ -855,19 +861,20 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc Reexec: config.Reexec, } } - return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig) + return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig, block) } // 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 *API) traceTx(ctx context.Context, message core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { +func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig, block *types.Block) (interface{}, error) { // Assemble the structured logger or the JavaScript tracer var ( tracer vm.EVMLogger err error txContext = core.NewEVMTxContext(message) ) + switch { case config == nil: tracer = logger.NewStructLogger(nil) @@ -897,6 +904,8 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex } // Run the transaction with tracing enabled. vmenv := vm.NewEVM(vmctx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true}) + crossChainRes := block.GetExternalCallResult(txctx.TxHash) + vmenv.SetCrossChainCallResults(crossChainRes) // Call Prepare to clear out the statedb access list statedb.Prepare(txctx.TxHash, txctx.TxIndex) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 68389efbf437..44532bc08ecc 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -273,6 +273,14 @@ func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash, return json.tx, err } +type ExternalCallResult struct { + BlockHash common.Hash `json:"blockHash"` + BlockNumber uint64 `json:"blockNumber"` + TxHash common.Hash `json:"transactionHash"` + TxIndex uint64 `json:"transactionIndex"` + ExternalCallResult []byte `json:"externalCallResult"` +} + // TransactionReceipt returns the receipt of a transaction by transaction hash. // Note that the receipt is not available for pending transactions. func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { @@ -286,6 +294,18 @@ func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (* return r, err } +// ExternalCallREsult returns the externalCallResult for the given transaction hash +func (ec *Client) ExternalCallResult(ctx context.Context, txHash common.Hash) (*ExternalCallResult, error) { + var r *ExternalCallResult + err := ec.c.CallContext(ctx, &r, "eth_getExternalCallResult", txHash) + if err == nil { + if r == nil { + return nil, ethereum.NotFound + } + } + return r, err +} + // SyncProgress retrieves the current progress of the sync algorithm. If there's // no sync currently running, it returns nil. func (ec *Client) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) { diff --git a/ethclient/test/w3qclient_test.go b/ethclient/test/w3qclient_test.go new file mode 100644 index 000000000000..62b8fd91077b --- /dev/null +++ b/ethclient/test/w3qclient_test.go @@ -0,0 +1,335 @@ +package test + +import ( + "context" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/params" + "io/ioutil" + "math/big" + "sync" + "testing" + "time" +) + +func generateTx(c *ethclient.Client, from common.Address, to *common.Address, nonce uint64, data []byte, value *big.Int, chainId *big.Int) (*types.Transaction, error) { + ctx := context.Background() + tipCap, err := c.SuggestGasTipCap(ctx) + if err != nil { + return nil, err + } + + latestHeader, err := c.HeaderByNumber(ctx, nil) + if err != nil { + return nil, err + } + + gasFeeCap := new(big.Int).Add( + tipCap, new(big.Int).Mul(latestHeader.BaseFee, big.NewInt(2)), + ) + + msg := ethereum.CallMsg{ + From: from, + To: to, + GasTipCap: tipCap, + GasFeeCap: gasFeeCap, + Value: value, + Data: data, + } + + gasLimit, err := c.EstimateGas(ctx, msg) + if err != nil { + return nil, err + } + + tx := types.NewTx(&types.DynamicFeeTx{ + ChainID: chainId, + Nonce: nonce, + To: to, + Value: big.NewInt(0), + GasTipCap: tipCap, + GasFeeCap: gasFeeCap, + Gas: gasLimit * 6 / 5, + Data: data, + }) + + return tx, nil + +} + +func generateTxWithGasLimit(c *ethclient.Client, from common.Address, to *common.Address, nonce uint64, data []byte, value *big.Int, chainId *big.Int, gasLimit uint64) (*types.Transaction, error) { + ctx := context.Background() + tipCap, err := c.SuggestGasTipCap(ctx) + if err != nil { + return nil, err + } + + latestHeader, err := c.HeaderByNumber(ctx, nil) + if err != nil { + return nil, err + } + + gasFeeCap := new(big.Int).Add( + tipCap, new(big.Int).Mul(latestHeader.BaseFee, big.NewInt(2)), + ) + + tx := types.NewTx(&types.DynamicFeeTx{ + ChainID: chainId, + Nonce: nonce, + To: to, + Value: big.NewInt(0), + GasTipCap: tipCap, + GasFeeCap: gasFeeCap, + Gas: gasLimit, + Data: data, + }) + + return tx, nil + +} + +const web3QRPCUrl_8545 = "http://127.0.0.1:8545" +const web3QRPCUrl_8570 = "http://127.0.0.1:8570" +const web3QValKetStoreFilePath = "/Users/chenyanlong/Work/go-ethereum/cmd/geth/data_val_0/keystore/UTC--2022-06-07T04-15-42.696295000Z--96f22a48dcd4dfb99a11560b24bee02f374ca77d" +const passwd = "123" +const contractInitCode = "0x608060405234801561001057600080fd5b50611a9b806100206000396000f3fe608060405234801561001057600080fd5b50600436106100b45760003560e01c8063518a351011610071578063518a3510146101905780638c95e054146101c05780639afb416c146101de578063a25a92aa146101fc578063d0496d6a14610218578063f9f8a58814610236576100b4565b806306661abd146100b957806318d9adab146100d757806320615362146101075780632fbc69e2146101385780633fa4f24514610156578063410198e214610174575b600080fd5b6100c1610252565b6040516100ce91906115b1565b60405180910390f35b6100f160048036038101906100ec91906111ac565b610258565b6040516100fe91906114bb565b60405180910390f35b610121600480360381019061011c91906111d9565b61027c565b60405161012f9291906114f8565b60405180910390f35b610140610539565b60405161014d919061152f565b60405180910390f35b61015e6105c7565b60405161016b91906115b1565b60405180910390f35b61018e60048036038101906101899190611254565b6105cd565b005b6101aa60048036038101906101a591906111d9565b610743565b6040516101b791906114d6565b60405180910390f35b6101c86108a0565b6040516101d591906114a0565b60405180910390f35b6101e66108a7565b6040516101f391906114a0565b60405180910390f35b610216600480360381019061021191906111d9565b6108cb565b005b610220610ad9565b60405161022d91906114d6565b60405180910390f35b610250600480360381019061024b91906111d9565b610b67565b005b60035481565b6001818154811061026857600080fd5b906000526020600020016000915090505481565b6060806000878787878760405160240161029a9594939291906115cc565b6040516020818303038152906040527f99e20070000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090506000806203330373ffffffffffffffffffffffffffffffffffffffff16836040516103449190611489565b6000604051808303816000865af19150503d8060008114610381576040519150601f19603f3d011682016040523d82523d6000602084013e610386565b606091505b5091509150816103cb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103c290611591565b60405180910390fd5b60008a8a60018b6103dc9190611715565b8a8a6040516024016103f29594939291906115cc565b6040516020818303038152906040527f99e20070000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090506000806203330373ffffffffffffffffffffffffffffffffffffffff168360405161049c9190611489565b6000604051808303816000865af19150503d80600081146104d9576040519150601f19603f3d011682016040523d82523d6000602084013e6104de565b606091505b509150915081610523576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161051a90611571565b60405180910390fd5b8381975097505050505050509550959350505050565b60058054610546906117f6565b80601f0160208091040260200160405190810160405280929190818152602001828054610572906117f6565b80156105bf5780601f10610594576101008083540402835291602001916105bf565b820191906000526020600020905b8154815290600101906020018083116105a257829003601f168201915b505050505081565b60025481565b600086868686866040516024016105e89594939291906115cc565b6040516020818303038152906040527f99e20070000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060005b82811015610739576000806203330373ffffffffffffffffffffffffffffffffffffffff168460405161069d9190611489565b6000604051808303816000865af19150503d80600081146106da576040519150601f19603f3d011682016040523d82523d6000602084013e6106df565b606091505b509150915081610724576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161071b90611551565b60405180910390fd5b5050808061073190611859565b91505061066a565b5050505050505050565b6060600086868686866040516024016107609594939291906115cc565b6040516020818303038152906040527f99e20070000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090506000806203330373ffffffffffffffffffffffffffffffffffffffff168360405161080a9190611489565b6000604051808303816000865af19150503d8060008114610847576040519150601f19603f3d011682016040523d82523d6000602084013e61084c565b606091505b509150915081610891576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161088890611551565b60405180910390fd5b80935050505095945050505050565b6203330381565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600085858585856040516024016108e69594939291906115cc565b6040516020818303038152906040527f99e20070000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090506000806203330373ffffffffffffffffffffffffffffffffffffffff16836040516109909190611489565b6000604051808303816000865af19150503d80600081146109cd576040519150601f19603f3d011682016040523d82523d6000602084013e6109d2565b606091505b509150915081610a0e57808060200190518101906109f09190611163565b60059080519060200190610a05929190610d5e565b50505050610ad2565b600080600083806020019051810190610a2791906110d8565b925092509250826000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508160019080519060200190610a83929190610de4565b50600080600083806020019051810190610a9d91906112e1565b92509250925082600281905550816003819055508060049080519060200190610ac7929190610e31565b505050505050505050505b5050505050565b60048054610ae6906117f6565b80601f0160208091040260200160405190810160405280929190818152602001828054610b12906117f6565b8015610b5f5780601f10610b3457610100808354040283529160200191610b5f565b820191906000526020600020905b815481529060010190602001808311610b4257829003601f168201915b505050505081565b60008585858585604051602401610b829594939291906115cc565b6040516020818303038152906040527f99e20070000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090506000806203330373ffffffffffffffffffffffffffffffffffffffff1683604051610c2c9190611489565b6000604051808303816000865af19150503d8060008114610c69576040519150601f19603f3d011682016040523d82523d6000602084013e610c6e565b606091505b509150915081610caa5780806020019051810190610c8c9190611163565b60059080519060200190610ca1929190610d5e565b50505050610d57565b600080600083806020019051810190610cc391906110d8565b925092509250826000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508160019080519060200190610d1f929190610de4565b50600080600083806020019051810190610d3991906112e1565b92509250925082600281905550816003819055505050505050505050505b5050505050565b828054610d6a906117f6565b90600052602060002090601f016020900481019282610d8c5760008555610dd3565b82601f10610da557805160ff1916838001178555610dd3565b82800160010185558215610dd3579182015b82811115610dd2578251825591602001919060010190610db7565b5b509050610de09190610eb7565b5090565b828054828255906000526020600020908101928215610e20579160200282015b82811115610e1f578251825591602001919060010190610e04565b5b509050610e2d9190610ed4565b5090565b828054610e3d906117f6565b90600052602060002090601f016020900481019282610e5f5760008555610ea6565b82601f10610e7857805160ff1916838001178555610ea6565b82800160010185558215610ea6579182015b82811115610ea5578251825591602001919060010190610e8a565b5b509050610eb39190610eb7565b5090565b5b80821115610ed0576000816000905550600101610eb8565b5090565b5b80821115610eed576000816000905550600101610ed5565b5090565b6000610f04610eff84611644565b61161f565b90508083825260208201905082856020860282011115610f2757610f26611934565b5b60005b85811015610f575781610f3d888261103d565b845260208401935060208301925050600181019050610f2a565b5050509392505050565b6000610f74610f6f84611670565b61161f565b905082815260208101848484011115610f9057610f8f611939565b5b610f9b8482856117c3565b509392505050565b6000610fb6610fb1846116a1565b61161f565b905082815260208101848484011115610fd257610fd1611939565b5b610fdd8482856117c3565b509392505050565b600081519050610ff481611a20565b92915050565b600082601f83011261100f5761100e61192f565b5b815161101f848260208601610ef1565b91505092915050565b60008135905061103781611a37565b92915050565b60008151905061104c81611a37565b92915050565b600082601f8301126110675761106661192f565b5b8151611077848260208601610f61565b91505092915050565b600082601f8301126110955761109461192f565b5b81516110a5848260208601610fa3565b91505092915050565b6000813590506110bd81611a4e565b92915050565b6000815190506110d281611a4e565b92915050565b6000806000606084860312156110f1576110f0611943565b5b60006110ff86828701610fe5565b935050602084015167ffffffffffffffff8111156111205761111f61193e565b5b61112c86828701610ffa565b925050604084015167ffffffffffffffff81111561114d5761114c61193e565b5b61115986828701611052565b9150509250925092565b60006020828403121561117957611178611943565b5b600082015167ffffffffffffffff8111156111975761119661193e565b5b6111a384828501611080565b91505092915050565b6000602082840312156111c2576111c1611943565b5b60006111d0848285016110ae565b91505092915050565b600080600080600060a086880312156111f5576111f4611943565b5b6000611203888289016110ae565b955050602061121488828901611028565b9450506040611225888289016110ae565b9350506060611236888289016110ae565b9250506080611247888289016110ae565b9150509295509295909350565b60008060008060008060c0878903121561127157611270611943565b5b600061127f89828a016110ae565b965050602061129089828a01611028565b95505060406112a189828a016110ae565b94505060606112b289828a016110ae565b93505060806112c389828a016110ae565b92505060a06112d489828a016110ae565b9150509295509295509295565b6000806000606084860312156112fa576112f9611943565b5b6000611308868287016110c3565b9350506020611319868287016110c3565b925050604084015167ffffffffffffffff81111561133a5761133961193e565b5b61134686828701611052565b9150509250925092565b6113598161176b565b82525050565b6113688161178f565b82525050565b6000611379826116d2565b61138381856116e8565b93506113938185602086016117c3565b61139c81611948565b840191505092915050565b60006113b2826116d2565b6113bc81856116f9565b93506113cc8185602086016117c3565b80840191505092915050565b60006113e3826116dd565b6113ed8185611704565b93506113fd8185602086016117c3565b61140681611948565b840191505092915050565b600061141e601883611704565b915061142982611959565b602082019050919050565b6000611441602583611704565b915061144c82611982565b604082019050919050565b6000611464602583611704565b915061146f826119d1565b604082019050919050565b611483816117b9565b82525050565b600061149582846113a7565b915081905092915050565b60006020820190506114b56000830184611350565b92915050565b60006020820190506114d0600083018461135f565b92915050565b600060208201905081810360008301526114f0818461136e565b905092915050565b60006040820190508181036000830152611512818561136e565b90508181036020830152611526818461136e565b90509392505050565b6000602082019050818103600083015261154981846113d8565b905092915050565b6000602082019050818103600083015261156a81611411565b9050919050565b6000602082019050818103600083015261158a81611434565b9050919050565b600060208201905081810360008301526115aa81611457565b9050919050565b60006020820190506115c6600083018461147a565b92915050565b600060a0820190506115e1600083018861147a565b6115ee602083018761135f565b6115fb604083018661147a565b611608606083018561147a565b611615608083018461147a565b9695505050505050565b600061162961163a565b90506116358282611828565b919050565b6000604051905090565b600067ffffffffffffffff82111561165f5761165e611900565b5b602082029050602081019050919050565b600067ffffffffffffffff82111561168b5761168a611900565b5b61169482611948565b9050602081019050919050565b600067ffffffffffffffff8211156116bc576116bb611900565b5b6116c582611948565b9050602081019050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600081905092915050565b600082825260208201905092915050565b6000611720826117b9565b915061172b836117b9565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156117605761175f6118a2565b5b828201905092915050565b600061177682611799565b9050919050565b600061178882611799565b9050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60005b838110156117e15780820151818401526020810190506117c6565b838111156117f0576000848401525b50505050565b6000600282049050600182168061180e57607f821691505b60208210811415611822576118216118d1565b5b50919050565b61183182611948565b810181811067ffffffffffffffff821117156118505761184f611900565b5b80604052505050565b6000611864826117b9565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415611897576118966118a2565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f6661696c20746f2063726f737320636861696e2063616c6c0000000000000000600082015250565b7f63726f73732063616c6c2032206661696c20746f2063726f737320636861696e60008201527f2063616c6c000000000000000000000000000000000000000000000000000000602082015250565b7f63726f73732063616c6c2031206661696c20746f2063726f737320636861696e60008201527f2063616c6c000000000000000000000000000000000000000000000000000000602082015250565b611a298161177d565b8114611a3457600080fd5b50565b611a408161178f565b8114611a4b57600080fd5b50565b611a57816117b9565b8114611a6257600080fd5b5056fea2646970667358221220e35a2b0eedb6ac9983709e1c0289fb6d85af7eae663ac86d1899375df12aa4c964736f6c63430008070033" +const externalCallData = "0x518a351000000000000000000000000000000000000000000000000000000000000000047ba399701b823976c367686562ca9fa11ecc81341d2b0026c5615740bd164e460000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012c000000000000000000000000000000000000000000000000000000000000000a" +const expectExternalCallResult = "d317fdd3569ed357fdd3569a7fdd356bb6fdd356b4d34d34d34d34d34d34d34d34d34d34d34d34ef9d77db4737e9fe35ddaeb6f3469de78e3cefbeba69ed1fefcd1be9fe7cd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34734d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d3675c7bbdb575cd9dd3bf1cd37d39df469e6f9e75d5e6fbebaeb76bbd39efdedcd9ae1ae1de356bbd1d75d7dbf1e7dc6bdd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34734d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d36d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34dbc6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6db6dbd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e7879c" + +const externalCallDataTo1337 = "0x518a35100000000000000000000000000000000000000000000000000000000000000539fd35bafc1b84c3ac816b8db2105f79edcdb651c608958ca2bef1e16996f0ff580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012c0000000000000000000000000000000000000000000000000000000000000000" +const expectExternalCallResult1337 = "d317fdd35f1ed357fdd35f1a7fdd35f3b6fdd35f34d34d34d34d34d34d34d34d34d34d34d34d34734d9ef7477c7b6d39f35edb7dee1ad3569c6f4ddbef9e346bbd3d73b6f7d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34734d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d3675c7bbdb575cd9dd3bf1cd37d39df469e6f9e75d5e6fbebaeb76bbd39efdedcd9ae1ae1de356bbd1d75d7dbf1e7dc6bdd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d36d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d36d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d79eb5eb5ebad7ad7ad7ad7aeb5eb5ebad7ad7ad7aeb5eb5eb5ebad7ad7aeb5eb5d34d34d34d34d34d34d34d34d34d34d34d35f36ebbe3c" + +const externalCallDataTo1337WithEmptyResult = "0x518a35100000000000000000000000000000000000000000000000000000000000003039d15af3b3caa1173c3918d13356d4c5c92a14ed9e46129cef526001f6be7fbf0e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +const expectExternalCallResult1337WithEmptyResult = "d317fc71bd357fc73c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34d7475cdb8e766fbe38775d5f73b6f4e5a6fae3773df1e777e5c6bcdb8efcd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c" + +const CallMuchTimesExternalCallDataTo1337WithEmptyResult = "0x410198e20000000000000000000000000000000000000000000000000000000000003039b08f3338d45b5569428a5237c4c48b665cd325d31c98cdebbc1f6d3bc0a9823d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032" +const CallMuchTimesExpectExternalCallResult1337WithEmptyResult = "d317fddbbd78d357fddbbd747fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c7fc73a6fc734d34d34d34d34d34d34d34d34d34d34d34d34df96dadded9ed1edb8edcf376b6efbd75f34ebaf366f57f6d5ee3debad9fd34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34eb4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d346b4d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f1edfdedad37f1adf8e3ae9a73cd3af75eb97faf5dd9fdbc6dd7baeb969c71ff7adfbd9bedeebae5e7b4ebd75dd34776d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d34d35f36e76e1c" + +func TestRPCReturn(t *testing.T) { + c, err := ethclient.Dial("http://127.0.0.1:8554") + if err != nil { + t.Error(err) + } + + //for i := 0; i < 50; i++ { + receipt, err := c.TransactionReceipt(context.Background(), common.HexToHash("0xb08f3338d45b5569428a5237c4c48b665cd325d31c98cdebbc1f6d3bc0a9823d")) + if err != nil { + t.Errorf("index %d , err %s", 0, err.Error()) + } + + t.Logf("index %d, context %v", 0, *receipt.Logs[0]) + //} + +} + +//func BenchmarkExternalTx(b *testing.B) { +func TestExternalTx(b *testing.T) { + maxTxNumbers := []uint64{10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140} + var chainId = big.NewInt(3333) + + f, err := ioutil.ReadFile(web3QValKetStoreFilePath) + if err != nil { + b.Error(err) + } + key, err := keystore.DecryptKey(f, passwd) + if err != nil { + b.Error(err) + } + + ctx := context.Background() + client, err := ethclient.Dial(web3QRPCUrl_8545) + bNumber, err := client.BlockNumber(context.Background()) + if err != nil { + b.Fatal(err) + } + + // deploy contract using called externalCall precompile contract + nonce, err := client.PendingNonceAt(ctx, key.Address) + if err != nil { + b.Fatal(err) + } + b.Log("Nonce:", nonce) + accountNonce := nonce + tx, err := generateTx(client, key.Address, nil, accountNonce, common.FromHex(contractInitCode), nil, chainId) + if err != nil { + b.Error(err) + } + accountNonce++ + + signer := types.MakeSigner(params.Web3QTestnetChainConfig, big.NewInt(0).SetUint64(bNumber)) + //signer := types.NewLondonSigner(chainId) + signedTx, err := types.SignTx(tx, signer, key.PrivateKey) + if err != nil { + b.Fatal(err) + } + + actualFrom, err := signer.Sender(signedTx) + if err != nil { + b.Fatal(err) + } + if actualFrom != key.Address { + b.Error("recover sender is error") + } + + err = client.SendTransaction(context.Background(), signedTx) + if err != nil { + b.Error(err) + } + + // get contract address + var receipt *types.Receipt + for { + receipt, err = client.TransactionReceipt(context.Background(), signedTx.Hash()) + + if err != nil { + if err == ethereum.NotFound { + time.Sleep(10 * time.Second) + } else { + b.Fatal(err) + } + } else { + break + } + } + + for _, max := range maxTxNumbers { + + // call external call + sendedTxHashs := make([]common.Hash, 0) + for n := uint64(0); n < max; n++ { + externalTx, err := generateTxWithGasLimit(client, key.Address, &receipt.ContractAddress, accountNonce, common.FromHex(CallMuchTimesExternalCallDataTo1337WithEmptyResult), nil, chainId, 1400000) + if err != nil { + b.Error(err) + } + esignedTx, err := types.SignTx(externalTx, signer, key.PrivateKey) + if err != nil { + b.Error(err) + } + + err = client.SendTransaction(context.Background(), esignedTx) + if err != nil { + b.Error(err) + } + + sendedTxHashs = append(sendedTxHashs, esignedTx.Hash()) + accountNonce++ + } + + var wg sync.WaitGroup + var txsCount sync.Map + var errTxsCount sync.Map + errTxsCount.Store("ErrTxCount", uint64(0)) + + for i, hash := range sendedTxHashs { + wg.Add(1) + go func(txHash common.Hash, j int) { + for { + extReceipt, err := client.TransactionReceipt(context.Background(), txHash) + if err != nil { + if err == ethereum.NotFound { + time.Sleep(10 * time.Second) + } else { + b.Fatalf("index:%d,txHash:%s , err:%s", j, txHash.Hex(), err.Error()) + break + } + } else { + if txHash != extReceipt.TxHash { + b.Errorf("TxHash no Equal,exp: %s ,actual: %s", txHash.Hex(), extReceipt.TxHash.Hex()) + break + } + + //b.Log("txhash:", extReceipt.TxHash, " index:", j, " receipt.BlockNumber:", extReceipt.BlockNumber.String(), " gas:", extReceipt.GasUsed) + load, ok := txsCount.Load(extReceipt.BlockNumber.Uint64()) + if ok { + count := load.(uint64) + count++ + txsCount.Store(extReceipt.BlockNumber.Uint64(), count) + } else { + txsCount.Store(extReceipt.BlockNumber.Uint64(), uint64(1)) + } + result, err := client.ExternalCallResult(context.Background(), txHash) + if err != nil { + errTxcount, ok := errTxsCount.Load("ErrTxCount") + if ok { + errTxcount := errTxcount.(uint64) + errTxcount++ + errTxsCount.Store("ErrTxCount", errTxcount) + } + b.Errorf("index:%d,txHash:%s , err:%s", j, extReceipt.TxHash.Hex(), err.Error()) + } else { + if common.Bytes2Hex(result.ExternalCallResult) != CallMuchTimesExpectExternalCallResult1337WithEmptyResult { + b.Errorf("index:%d,txHash:%s , err:%s ,actualRes:%s ", j, extReceipt.TxHash.Hex(), "no equal with expect call result", common.Bytes2Hex(result.ExternalCallResult)) + } + } + break + } + } + wg.Done() + }(hash, i) + } + wg.Wait() + + time.Sleep(8 * time.Second) + + txsCount.Range(func(key, value interface{}) bool { + b.Log("BlockNumber:", key, " TxsCount:", value) + bn, _ := key.(uint64) + b.Log("Produce Block Time:", calCulateBlockPeriod(client, uint64(bn)), " ms") + return true + }) + + errTxsCount.Range(func(key, value interface{}) bool { + b.Log(key, ":", value) + return true + }) + } + +} + +func calCulateBlockPeriod(c *ethclient.Client, bn uint64) uint64 { + b, err := c.BlockByNumber(context.Background(), big.NewInt(0).SetUint64(bn+1)) + if err != nil { + panic(err) + } + + bf, err := c.BlockByNumber(context.Background(), big.NewInt(0).SetUint64(bn)) + if err != nil { + panic(err) + } + + timePeriod := b.TimeMs() - bf.TimeMs() + return timePeriod +} + +func TestWaitingResult(t *testing.T) { + txHash := common.HexToHash("0xc9161880b5e89db79cb707b8ea874230277c80a3fdcdac37e8cfab8fae25711e") + client, err := ethclient.Dial(web3QRPCUrl_8545) + if err != nil { + t.Fatal(err) + } + transactionReceipt, err := client.ExternalCallResult(context.Background(), txHash) + if err != nil { + t.Fatal(err) + } else { + if common.Bytes2Hex(transactionReceipt.ExternalCallResult) != expectExternalCallResult { + t.Fatal("call result no equal") + } + } +} + +func TestGetBlock(t *testing.T) { + client, err := ethclient.Dial("http://127.0.0.1:8548") + if err != nil { + t.Fatal(err) + } + + number, err := client.BlockNumber(context.Background()) + if err != nil { + t.Fatal(err) + } + + t.Log(number) +} diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 1bcc939241be..5db17f8e848c 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -916,17 +916,24 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash if err != nil { return nil, err } - evm, vmError, err := b.GetEVM(ctx, msg, state, header, &vm.Config{NoBaseFee: true, IsJsonRpc: true}) + + // GetEvm will return the external_call_client within evmConfig when the input of evmConfig is nil + evm, vmError, err := b.GetEVM(ctx, msg, state, header, nil) if err != nil { return nil, err } + if evm.Config.ExternalCallClient == nil && evm.EnableExternalCall() { + return nil, fmt.Errorf("DoCall must matain a active external_call_client when external call is active") + } + evm.Config.NoBaseFee = true + evm.Config.IsJsonRpc = true + // Wait for the context to be done and cancel the evm. Even if the // EVM has finished, cancelling may be done (repeatedly) go func() { <-ctx.Done() evm.Cancel() }() - // Execute the message. gp := new(core.GasPool).AddGas(math.MaxUint64) result, err := core.ApplyMessage(evm, msg, gp) @@ -934,6 +941,8 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash return nil, err } + // todo: deal with cross chain call result + // If the timer caused an abort, return an appropriate error message if evm.Cancelled() { return nil, fmt.Errorf("execution aborted (timeout = %v)", timeout) @@ -1276,25 +1285,26 @@ func (s *PublicBlockChainAPI) rpcMarshalBlock(ctx context.Context, b *types.Bloc // RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction type RPCTransaction struct { - BlockHash *common.Hash `json:"blockHash"` - BlockNumber *hexutil.Big `json:"blockNumber"` - From common.Address `json:"from"` - Gas hexutil.Uint64 `json:"gas"` - GasPrice *hexutil.Big `json:"gasPrice"` - GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"` - GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"` - Hash common.Hash `json:"hash"` - Input hexutil.Bytes `json:"input"` - Nonce hexutil.Uint64 `json:"nonce"` - To *common.Address `json:"to"` - TransactionIndex *hexutil.Uint64 `json:"transactionIndex"` - Value *hexutil.Big `json:"value"` - Type hexutil.Uint64 `json:"type"` - Accesses *types.AccessList `json:"accessList,omitempty"` - ChainID *hexutil.Big `json:"chainId,omitempty"` - V *hexutil.Big `json:"v"` - R *hexutil.Big `json:"r"` - S *hexutil.Big `json:"s"` + BlockHash *common.Hash `json:"blockHash"` + BlockNumber *hexutil.Big `json:"blockNumber"` + From common.Address `json:"from"` + Gas hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"` + GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"` + Hash common.Hash `json:"hash"` + Input hexutil.Bytes `json:"input"` + Nonce hexutil.Uint64 `json:"nonce"` + To *common.Address `json:"to"` + TransactionIndex *hexutil.Uint64 `json:"transactionIndex"` + Value *hexutil.Big `json:"value"` + Type hexutil.Uint64 `json:"type"` + Accesses *types.AccessList `json:"accessList,omitempty"` + ChainID *hexutil.Big `json:"chainId,omitempty"` + ExternalCallResult hexutil.Bytes `json:"externalCallResult"` + V *hexutil.Big `json:"v"` + R *hexutil.Big `json:"r"` + S *hexutil.Big `json:"s"` } // newRPCTransaction returns a transaction that will serialize to the RPC @@ -1317,6 +1327,7 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber R: (*hexutil.Big)(r), S: (*hexutil.Big)(s), } + if blockHash != (common.Hash{}) { result.BlockHash = &blockHash result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber)) @@ -1469,11 +1480,17 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH // Apply the transaction with the access list tracer tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles) - config := vm.Config{Tracer: tracer, Debug: true, NoBaseFee: true} - vmenv, _, err := b.GetEVM(ctx, msg, statedb, header, &config) + // GetEvm will return the external_call_client within evmConfig when the input of evmConfig is nil + vmenv, _, err := b.GetEVM(ctx, msg, statedb, header, nil) if err != nil { return nil, 0, nil, err } + if vmenv.EnableExternalCall() && vmenv.Config.ExternalCallClient == nil { + return nil, 0, nil, fmt.Errorf("AccessList must matain a active external_call_client when executing transaction") + } + vmenv.Config.Tracer = tracer + vmenv.Config.Debug = true + vmenv.Config.NoBaseFee = true res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())) if err != nil { return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err) @@ -1609,6 +1626,43 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context, return tx.MarshalBinary() } +// GetExternalCallResult returns the externalCallResult for the given transaction hash +func (s *PublicTransactionPoolAPI) GetExternalCallResult(ctx context.Context, hash common.Hash) (map[string]interface{}, error) { + _, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash) + if err != nil { + return nil, nil + } + + // get external_call_result from uncles + block, err := s.b.BlockByHash(ctx, blockHash) + var externalCallResult []byte + var txWithExternalCallResult bool + if block != nil { + uncles := block.Uncles() + for _, uncle := range uncles { + if uncle.TxHash == hash { + externalCallResult = uncle.Extra + txWithExternalCallResult = true + break + } + } + } + + if !txWithExternalCallResult { + return nil, fmt.Errorf("can't find the externalCallResult for the given txhash %s", hash) + } + + fields := map[string]interface{}{ + "blockHash": blockHash, + "blockNumber": blockNumber, + "transactionHash": hash, + "transactionIndex": index, + "externalCallResult": hexutil.Bytes(externalCallResult), + } + + return fields, nil +} + // GetTransactionReceipt returns the transaction receipt for the given transaction hash. func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) { tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash) @@ -1624,6 +1678,18 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha } receipt := receipts[index] + // get external_call_result from uncles + block, err := s.b.BlockByHash(ctx, blockHash) + var externalCallResult []byte + if block != nil { + uncles := block.Uncles() + for _, uncle := range uncles { + if uncle.TxHash == hash { + externalCallResult = uncle.Extra + } + } + } + // Derive the sender. bigblock := new(big.Int).SetUint64(blockNumber) signer := types.MakeSigner(s.b.ChainConfig(), bigblock) @@ -1667,6 +1733,11 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha if receipt.ContractAddress != (common.Address{}) { fields["contractAddress"] = receipt.ContractAddress } + // set the external_call_result + if externalCallResult != nil { + fields["externalCallResult"] = hexutil.Bytes(externalCallResult) + } + return fields, nil } diff --git a/miner/worker.go b/miner/worker.go index 1ddd44d33a42..bab50af8c5b8 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -91,10 +91,11 @@ type environment struct { gasPool *core.GasPool // available gas used to pack transactions coinbase common.Address - header *types.Header - txs []*types.Transaction - receipts []*types.Receipt - uncles map[common.Hash]*types.Header + header *types.Header + txs []*types.Transaction + receipts []*types.Receipt + uncles map[common.Hash]*types.Header + sortUncles []common.Hash } // copy creates a deep copy of environment. @@ -122,15 +123,20 @@ func (env *environment) copy() *environment { for hash, uncle := range env.uncles { cpy.uncles[hash] = uncle } + cpy.sortUncles = make([]common.Hash, len(env.sortUncles)) + copy(cpy.sortUncles, env.sortUncles) + return cpy } // unclelist returns the contained uncles as the list format. func (env *environment) unclelist() []*types.Header { var uncles []*types.Header - for _, uncle := range env.uncles { - uncles = append(uncles, uncle) + + for _, uncleHash := range env.sortUncles { + uncles = append(uncles, env.uncles[uncleHash]) } + return uncles } @@ -829,6 +835,20 @@ func (w *worker) commitUncle(env *environment, uncle *types.Header) error { return nil } +// commitUncleDirectly is used when submitting cross_chain_call_result +func (w *worker) commitUncleDirectly(env *environment, uncle *types.Header) error { + hash := uncle.Hash() + if _, exist := env.uncles[hash]; exist { + return errors.New("uncle not unique") + } + env.uncles[hash] = uncle + if env.sortUncles == nil { + env.sortUncles = make([]common.Hash, 0) + } + env.sortUncles = append(env.sortUncles, hash) + return nil +} + // updateSnapshot updates pending snapshot block, receipts and state. func (w *worker) updateSnapshot(env *environment) { w.snapshotMu.Lock() @@ -848,11 +868,34 @@ func (w *worker) updateSnapshot(env *environment) { func (w *worker) commitTransaction(env *environment, tx *types.Transaction) ([]*types.Log, error) { snap := env.state.Snapshot() - receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig()) + evmConfig := *w.chain.GetVMConfig() + // the evmConfig is the pointer of blockchain.evmConfig, so the externalCallClient maintains one instance of externalCallClient. + if w.chainConfig.ExternalCall.EnableBlockNumber != nil && env.header.Number.Cmp(w.chainConfig.ExternalCall.EnableBlockNumber) != -1 { + if evmConfig.ExternalCallClient == nil { + panic(fmt.Errorf("worker: the external_call_client from blockchain.evmConfig is nil")) + } + } + + receipt, crossChainCallResult, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, evmConfig) if err != nil { + log.Debug("worker: transaction happen error", "txHash", tx.Hash().Hex(), "err", err.Error()) env.state.RevertToSnapshot(snap) return nil, err } + + if len(crossChainCallResult) != 0 { + uncle := &types.Header{ + Number: big.NewInt(int64(len(env.txs))), + TxHash: tx.Hash(), + Extra: crossChainCallResult, + } + err = w.commitUncleDirectly(env, uncle) + if err != nil { + return nil, err + } + log.Debug("worker: transaction with cross_chain_call_result", "txIndex", len(env.txs), "txHash", tx.Hash().Hex(), "cross_chain_result", common.Bytes2Hex(crossChainCallResult)) + } + env.txs = append(env.txs, tx) env.receipts = append(env.receipts, receipt) diff --git a/miner/worker_test.go b/miner/worker_test.go index dd029433b8bf..e45aafc5e3a4 100644 --- a/miner/worker_test.go +++ b/miner/worker_test.go @@ -207,6 +207,57 @@ func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consens return w, backend } +func TestCommitUnlces(t *testing.T) { + env := &environment{ + uncles: make(map[common.Hash]*types.Header, 0), + } + w := &worker{} + uncles := []*types.Header{ + &types.Header{ + Number: big.NewInt(int64(0)), + TxHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001"), + Extra: []byte{0, 1}, + }, + &types.Header{ + Number: big.NewInt(int64(1)), + TxHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000002"), + Extra: []byte{0, 1}, + }, + &types.Header{ + Number: big.NewInt(int64(2)), + TxHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000003"), + Extra: []byte{0, 1}, + }, + &types.Header{ + Number: big.NewInt(int64(5)), + TxHash: common.HexToHash("0x000000000000000000000000000000000000000000000000000000000000007"), + Extra: []byte{0, 1}, + }, + &types.Header{ + Number: big.NewInt(int64(10)), + TxHash: common.HexToHash("0x000000000000000000000000000000000000000000000000000000000000207"), + Extra: []byte{0, 1}, + }, + &types.Header{ + Number: big.NewInt(int64(100)), + TxHash: common.HexToHash("0x022000000000000000000000000000000000000000000000000000000000207"), + Extra: []byte{0, 1}, + }, + } + for i, u := range uncles { + err := w.commitUncleDirectly(env, u) + if err != nil { + t.Errorf("commit %d uncle with error: %s", i, err.Error()) + } + } + + actualUncles := env.unclelist() + if len(actualUncles) != len(uncles) { + t.Error("uncleList generated with invalid length") + } + +} + func TestGenerateBlockAndImportEthash(t *testing.T) { testGenerateBlockAndImport(t, false) } diff --git a/params/bootnodes.go b/params/bootnodes.go index 9dd3cbe09c20..d44c3fc9da1f 100644 --- a/params/bootnodes.go +++ b/params/bootnodes.go @@ -79,6 +79,7 @@ var GoerliBootnodes = []string{ var Web3QTestnetBootnodes = []string{ "enode://6e98836f67f9429c0f3488b7f714c0c78287a927d828cfb5c695714f45ae06b992b72d27187210a6d27a280df7189a515ccb4a550165036aadfde9a73f5b6ded@68.183.157.114:30303", "enode://0b1f72bd2cdf8af6b42e1f1b24b1e927afd393de4a43f69f5bc402e2754d06bc140c3c3baac12f94aaf0aa4513f2b216c768c5d1a66f0b3b6c923575f0db64b1@128.199.102.174:30303", + "enode://c5509679a461eef22dc63e5ed0109c3f408e8cb6e1313f1488452c5828eda85540c9eceec1c5c60217a108f54c5568d18d4cbe7dbff9b3796c1c7cb04e8950cb@127.0.0.1:30301", } // TODO: to update diff --git a/params/config.go b/params/config.go index fabfe9982b69..e009fec2b09f 100644 --- a/params/config.go +++ b/params/config.go @@ -257,8 +257,10 @@ var ( } Web3QTestnetValBootnodes = []string{ - "/ip4/68.183.157.114/udp/33333/quic/p2p/12D3KooWEZ94qZgJgUNYiLwXahknkniYgozxw5eocijZJkew6Mj5", - "/ip4/128.199.102.174/udp/33333/quic/p2p/12D3KooWNjKALie7Cdpb4KG8axgABA4VCCsKoBQdvYvRhVZhbtPk", + //"/ip4/68.183.157.114/udp/33333/quic/p2p/12D3KooWEZ94qZgJgUNYiLwXahknkniYgozxw5eocijZJkew6Mj5", + //"/ip4/128.199.102.174/udp/33333/quic/p2p/12D3KooWNjKALie7Cdpb4KG8axgABA4VCCsKoBQdvYvRhVZhbtPk", + "/ip4/127.0.0.1/udp/33333/quic/p2p/12D3KooWEZ94qZgJgUNYiLwXahknkniYgozxw5eocijZJkew6Mj5", + "/ip4/127.0.0.1/udp/33334/quic/p2p/12D3KooWRAPv94qoUn8dAa3NQpZGKjaBcdiaqCETrcuyo2rT2ZvV", } // Web3QTestnetChainConfig contains the chain parameters to run a node on the Web3Q test network. @@ -277,9 +279,10 @@ var ( MuirGlacierBlock: nil, BerlinBlock: big.NewInt(0), LondonBlock: big.NewInt(0), + PisaBlock: big.NewInt(0), ArrowGlacierBlock: nil, Tendermint: &TendermintConfig{ - Epoch: 1000, + Epoch: 100800, // expect 6s block interval = one week ValidatorContract: "", ContractChainID: 0, ValidatorChangeEpochId: 0, @@ -304,6 +307,12 @@ var ( ConsensusSyncRequestDuration: 500 * time.Millisecond, }, }, + ExternalCall: &ExternalCallConfig{ + EnableBlockNumber: big.NewInt(0), + Version: 1, + SupportChainId: 4, + CallRpc: "https://rinkeby.infura.io/v3/4e3e18f80d8d4ad5959b7404e85e0143", + }, } Web3QGalileoValBootnodes = []string{ @@ -386,7 +395,7 @@ var ( // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, new(EthashConfig), nil, nil} + AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, new(EthashConfig), nil, nil, nil} // AllCliqueProtocolChanges contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers into the Clique consensus. @@ -418,8 +427,13 @@ var ( Tendermint: nil, } - TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, new(EthashConfig), nil, nil} - TestRules = TestChainConfig.Rules(new(big.Int), false) + TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, big.NewInt(0), nil, new(EthashConfig), nil, nil, &ExternalCallConfig{ + big.NewInt(0), + 1, + 4, + "https://rinkeby.infura.io/v3/4e3e18f80d8d4ad5959b7404e85e0143", + }} + TestRules = TestChainConfig.Rules(new(big.Int), false) ) // TrustedCheckpoint represents a set of post-processed trie roots (CHT and @@ -510,6 +524,9 @@ type ChainConfig struct { Ethash *EthashConfig `json:"ethash,omitempty"` Clique *CliqueConfig `json:"clique,omitempty"` Tendermint *TendermintConfig `json:"tendermint,omitempty"` + + // external call config + ExternalCall *ExternalCallConfig `json:"externalCall,omitempty"` } // EthashConfig is the consensus engine configs for proof-of-work based sealing. @@ -541,6 +558,18 @@ type TendermintConfig struct { ConsensusConfig ConsensusConfig } +type ExternalCallConfig struct { + EnableBlockNumber *big.Int `json:"enableBlockNumber"` + Version uint64 `json:"version"` + SupportChainId uint64 `json:"supportChainId"` + CallRpc string `json:"callRpc"` +} + +// String implements the stringer interface +func (c *ExternalCallConfig) String() string { + return "externalCallConfig" +} + // String implements the stringer interface, returning the consensus engine details. func (c *CliqueConfig) String() string { return "clique" diff --git a/params/protocol_params.go b/params/protocol_params.go index 7eec6287ef42..35d4a93d94b0 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -167,6 +167,9 @@ const ( CalldataGasCostEIP4488 = 3 // NEW_CALLDATA_GAS_COST in EIP-4488 BaseMaxCalldataPerBlockEIP4488 = 1048576 // BASE_MAX_CALLDATA_PER_BLOCK in EIP-4488 CalldataPerTxStipendEIP4488 = 300 // CALLDATA_PER_TX_STIPEND in EIP-4488 + + ExternalCallByteGasCost = 3 + ExternalCallGas = 100000 ) // Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations diff --git a/tests/init.go b/tests/init.go index d6b5b3043d4b..2d8bf7e21c21 100644 --- a/tests/init.go +++ b/tests/init.go @@ -197,6 +197,25 @@ var Forks = map[string]*params.ChainConfig{ LondonBlock: big.NewInt(0), ArrowGlacierBlock: big.NewInt(0), }, + "Pisa": { + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + PisaBlock: big.NewInt(0), + ExternalCall: ¶ms.ExternalCallConfig{ + Version: 1, + }, + }, } // Returns the set of defined fork names diff --git a/tests/state_test.go b/tests/state_test.go index 4595bcee9079..365430b9e1ae 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -20,6 +20,7 @@ import ( "bufio" "bytes" "fmt" + "github.com/ethereum/go-ethereum/ethclient" "math/big" "os" "path/filepath" @@ -256,7 +257,9 @@ func runBenchmark(b *testing.B, t *StateTest) { } } -var web3QStateTestDir = filepath.Join(baseDir, "Web3QTest") +//var web3QStateTestDir = filepath.Join(baseDir, "Web3QTest/ExternalCall/") + +var web3QStateTestDir = filepath.Join(baseDir, "Web3QTest/") func TestWeb3QState(t *testing.T) { t.Parallel() @@ -271,13 +274,86 @@ func TestWeb3QState(t *testing.T) { subtest := subtest key := fmt.Sprintf("%s%d", subtest.Fork, subtest.Index) t.Run(key+"/trie", func(t *testing.T) { - config := vm.Config{} - _, db, err := test.Run(subtest, config, false) - err = st.checkFailure(t, err) + vmconfig := vm.Config{} + + config, eips, err := GetChainConfig(subtest.Fork) + if err != nil { + t.Error(err) + return + } + vmconfig.ExtraEips = eips + + block := test.genesis(config).ToBlock(nil) + _, statedb := MakePreState(rawdb.NewMemoryDatabase(), test.json.Pre, false) + + var baseFee *big.Int + if config.IsLondon(new(big.Int)) { + baseFee = test.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(0x0a) + } + } + post := test.json.Post[subtest.Fork][subtest.Index] + msg, err := test.json.Tx.toMessage(post, baseFee) if err != nil { - printStateTrie(db, test, t) t.Error(err) + return + } + + // Try to recover tx with current signer + if len(post.TxBytes) != 0 { + var ttx types.Transaction + err := ttx.UnmarshalBinary(post.TxBytes) + if err != nil { + t.Error(err) + return + } + + if _, err := types.Sender(types.LatestSigner(config), &ttx); err != nil { + t.Error(err) + return + } } + + // Prepare the EVM. + + txContext := core.NewEVMTxContext(msg) + context := core.NewEVMBlockContext(block.Header(), nil, &test.json.Env.Coinbase) + context.GetHash = vmTestBlockHash + context.BaseFee = baseFee + + eClient, err := ethclient.Dial("https://rinkeby.infura.io/v3/4e3e18f80d8d4ad5959b7404e85e0143") + if err != nil { + panic(err) + } + + vmconfig.ExternalCallClient = eClient + evm := vm.NewEVM(context, txContext, statedb, config, vmconfig) + + // Execute the message. + snapshot := statedb.Snapshot() + gaspool := new(core.GasPool) + gaspool.AddGas(block.GasLimit()) + + _, err = core.ApplyMessage(evm, msg, gaspool) + if err != nil { + t.Error("EVM ERROR:", err) + statedb.RevertToSnapshot(snapshot) + printStateTrie(statedb, test, t) + } + //t.Log("evm call result:", common.Bytes2Hex(res.ReturnData)) + // Commit block + statedb.Commit(config.IsEIP158(block.Number())) + statedb.AddBalance(block.Coinbase(), new(big.Int)) + root := statedb.IntermediateRoot(config.IsEIP158(block.Number())) + + if root != common.Hash(post.Root) { + t.Error(fmt.Errorf("post state root mismatch: got %x, want %x", root, post.Root)) + printStateTrie(statedb, test, t) + } + }) } }) diff --git a/tests/testdata b/tests/testdata index 92616f0c7bd8..8182b8e418e6 160000 --- a/tests/testdata +++ b/tests/testdata @@ -1 +1 @@ -Subproject commit 92616f0c7bd81c70f846b2fb83cf612d1737908f +Subproject commit 8182b8e418e6cd143e8ae481ac43e4e224df58f0