From 441c7f2b0fb264981483850e9d155f7b0e908439 Mon Sep 17 00:00:00 2001 From: lightclient <14004106+lightclient@users.noreply.github.com> Date: Mon, 22 Nov 2021 01:25:35 -0700 Subject: [PATCH] cmd/evm: add b11r tool (#23843) evm block-builder (a.k.a b11r) is a utility to help assemble blocks, for use during the test-creation process. --- cmd/evm/internal/t8ntool/block.go | 380 ++++++++++++++++++++++++ cmd/evm/internal/t8ntool/flags.go | 41 ++- cmd/evm/internal/t8ntool/gen_header.go | 135 +++++++++ cmd/evm/internal/t8ntool/transaction.go | 2 +- cmd/evm/internal/t8ntool/transition.go | 50 +--- cmd/evm/internal/t8ntool/utils.go | 54 ++++ cmd/evm/main.go | 20 ++ cmd/evm/t8n_test.go | 124 +++++++- cmd/evm/testdata/20/exp.json | 4 + cmd/evm/testdata/20/header.json | 14 + cmd/evm/testdata/20/ommers.json | 1 + cmd/evm/testdata/20/readme.md | 11 + cmd/evm/testdata/20/txs.rlp | 1 + cmd/evm/testdata/21/clique.json | 6 + cmd/evm/testdata/21/exp-clique.json | 4 + cmd/evm/testdata/21/exp.json | 4 + cmd/evm/testdata/21/header.json | 11 + cmd/evm/testdata/21/ommers.json | 1 + cmd/evm/testdata/21/readme.md | 23 ++ cmd/evm/testdata/21/txs.rlp | 1 + cmd/evm/testdata/22/exp-clique.json | 4 + cmd/evm/testdata/22/exp.json | 4 + cmd/evm/testdata/22/header.json | 11 + cmd/evm/testdata/22/ommers.json | 1 + cmd/evm/testdata/22/readme.md | 11 + cmd/evm/testdata/22/txs.rlp | 1 + 26 files changed, 881 insertions(+), 38 deletions(-) create mode 100644 cmd/evm/internal/t8ntool/block.go create mode 100644 cmd/evm/internal/t8ntool/gen_header.go create mode 100644 cmd/evm/internal/t8ntool/utils.go create mode 100644 cmd/evm/testdata/20/exp.json create mode 100644 cmd/evm/testdata/20/header.json create mode 100644 cmd/evm/testdata/20/ommers.json create mode 100644 cmd/evm/testdata/20/readme.md create mode 100644 cmd/evm/testdata/20/txs.rlp create mode 100644 cmd/evm/testdata/21/clique.json create mode 100644 cmd/evm/testdata/21/exp-clique.json create mode 100644 cmd/evm/testdata/21/exp.json create mode 100644 cmd/evm/testdata/21/header.json create mode 100644 cmd/evm/testdata/21/ommers.json create mode 100644 cmd/evm/testdata/21/readme.md create mode 100644 cmd/evm/testdata/21/txs.rlp create mode 100644 cmd/evm/testdata/22/exp-clique.json create mode 100644 cmd/evm/testdata/22/exp.json create mode 100644 cmd/evm/testdata/22/header.json create mode 100644 cmd/evm/testdata/22/ommers.json create mode 100644 cmd/evm/testdata/22/readme.md create mode 100644 cmd/evm/testdata/22/txs.rlp diff --git a/cmd/evm/internal/t8ntool/block.go b/cmd/evm/internal/t8ntool/block.go new file mode 100644 index 000000000..d4edd33bd --- /dev/null +++ b/cmd/evm/internal/t8ntool/block.go @@ -0,0 +1,380 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package t8ntool + +import ( + "crypto/ecdsa" + "encoding/json" + "errors" + "fmt" + "math/big" + "os" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/consensus/clique" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" + "gopkg.in/urfave/cli.v1" +) + +//go:generate gencodec -type header -field-override headerMarshaling -out gen_header.go +type header struct { + ParentHash common.Hash `json:"parentHash"` + OmmerHash *common.Hash `json:"sha3Uncles"` + Coinbase *common.Address `json:"miner"` + Root common.Hash `json:"stateRoot" gencodec:"required"` + TxHash *common.Hash `json:"transactionsRoot"` + ReceiptHash *common.Hash `json:"receiptsRoot"` + Bloom types.Bloom `json:"logsBloom"` + Difficulty *big.Int `json:"difficulty"` + Number *big.Int `json:"number" gencodec:"required"` + GasLimit uint64 `json:"gasLimit" gencodec:"required"` + GasUsed uint64 `json:"gasUsed"` + Time uint64 `json:"timestamp" gencodec:"required"` + Extra []byte `json:"extraData"` + MixDigest common.Hash `json:"mixHash"` + Nonce *types.BlockNonce `json:"nonce"` + BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"` +} + +type headerMarshaling struct { + Difficulty *math.HexOrDecimal256 + Number *math.HexOrDecimal256 + GasLimit math.HexOrDecimal64 + GasUsed math.HexOrDecimal64 + Time math.HexOrDecimal64 + Extra hexutil.Bytes + BaseFee *math.HexOrDecimal256 +} + +type bbInput struct { + Header *header `json:"header,omitempty"` + OmmersRlp []string `json:"ommers,omitempty"` + TxRlp string `json:"txs,omitempty"` + Clique *cliqueInput `json:"clique,omitempty"` + + Ethash bool `json:"-"` + EthashDir string `json:"-"` + PowMode ethash.Mode `json:"-"` + Txs []*types.Transaction `json:"-"` + Ommers []*types.Header `json:"-"` +} + +type cliqueInput struct { + Key *ecdsa.PrivateKey + Voted *common.Address + Authorize *bool + Vanity common.Hash +} + +// UnmarshalJSON implements json.Unmarshaler interface. +func (c *cliqueInput) UnmarshalJSON(input []byte) error { + var x struct { + Key *common.Hash `json:"secretKey"` + Voted *common.Address `json:"voted"` + Authorize *bool `json:"authorize"` + Vanity common.Hash `json:"vanity"` + } + if err := json.Unmarshal(input, &x); err != nil { + return err + } + if x.Key == nil { + return errors.New("missing required field 'secretKey' for cliqueInput") + } + if ecdsaKey, err := crypto.ToECDSA(x.Key[:]); err != nil { + return err + } else { + c.Key = ecdsaKey + } + c.Voted = x.Voted + c.Authorize = x.Authorize + c.Vanity = x.Vanity + return nil +} + +// ToBlock converts i into a *types.Block +func (i *bbInput) ToBlock() *types.Block { + header := &types.Header{ + ParentHash: i.Header.ParentHash, + UncleHash: types.EmptyUncleHash, + Coinbase: common.Address{}, + Root: i.Header.Root, + TxHash: types.EmptyRootHash, + ReceiptHash: types.EmptyRootHash, + Bloom: i.Header.Bloom, + Difficulty: common.Big0, + Number: i.Header.Number, + GasLimit: i.Header.GasLimit, + GasUsed: i.Header.GasUsed, + Time: i.Header.Time, + Extra: i.Header.Extra, + MixDigest: i.Header.MixDigest, + BaseFee: i.Header.BaseFee, + } + + // Fill optional values. + if i.Header.OmmerHash != nil { + header.UncleHash = *i.Header.OmmerHash + } else if len(i.Ommers) != 0 { + // Calculate the ommer hash if none is provided and there are ommers to hash + header.UncleHash = types.CalcUncleHash(i.Ommers) + } + if i.Header.Coinbase != nil { + header.Coinbase = *i.Header.Coinbase + } + if i.Header.TxHash != nil { + header.TxHash = *i.Header.TxHash + } + if i.Header.ReceiptHash != nil { + header.ReceiptHash = *i.Header.ReceiptHash + } + if i.Header.Nonce != nil { + header.Nonce = *i.Header.Nonce + } + if header.Difficulty != nil { + header.Difficulty = i.Header.Difficulty + } + return types.NewBlockWithHeader(header).WithBody(i.Txs, i.Ommers) +} + +// SealBlock seals the given block using the configured engine. +func (i *bbInput) SealBlock(block *types.Block) (*types.Block, error) { + switch { + case i.Ethash: + return i.sealEthash(block) + case i.Clique != nil: + return i.sealClique(block) + default: + return block, nil + } +} + +// sealEthash seals the given block using ethash. +func (i *bbInput) sealEthash(block *types.Block) (*types.Block, error) { + if i.Header.Nonce != nil { + return nil, NewError(ErrorConfig, fmt.Errorf("sealing with ethash will overwrite provided nonce")) + } + ethashConfig := ethash.Config{ + PowMode: i.PowMode, + DatasetDir: i.EthashDir, + CacheDir: i.EthashDir, + DatasetsInMem: 1, + DatasetsOnDisk: 2, + CachesInMem: 2, + CachesOnDisk: 3, + } + engine := ethash.New(ethashConfig, nil, true) + defer engine.Close() + // Use a buffered chan for results. + // If the testmode is used, the sealer will return quickly, and complain + // "Sealing result is not read by miner" if it cannot write the result. + results := make(chan *types.Block, 1) + if err := engine.Seal(nil, block, results, nil); err != nil { + panic(fmt.Sprintf("failed to seal block: %v", err)) + } + found := <-results + return block.WithSeal(found.Header()), nil +} + +// sealClique seals the given block using clique. +func (i *bbInput) sealClique(block *types.Block) (*types.Block, error) { + // If any clique value overwrites an explicit header value, fail + // to avoid silently building a block with unexpected values. + if i.Header.Extra != nil { + return nil, NewError(ErrorConfig, fmt.Errorf("sealing with clique will overwrite provided extra data")) + } + header := block.Header() + if i.Clique.Voted != nil { + if i.Header.Coinbase != nil { + return nil, NewError(ErrorConfig, fmt.Errorf("sealing with clique and voting will overwrite provided coinbase")) + } + header.Coinbase = *i.Clique.Voted + } + if i.Clique.Authorize != nil { + if i.Header.Nonce != nil { + return nil, NewError(ErrorConfig, fmt.Errorf("sealing with clique and voting will overwrite provided nonce")) + } + if *i.Clique.Authorize { + header.Nonce = [8]byte{} + } else { + header.Nonce = [8]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + } + } + // Extra is fixed 32 byte vanity and 65 byte signature + header.Extra = make([]byte, 32+65) + copy(header.Extra[0:32], i.Clique.Vanity.Bytes()[:]) + + // Sign the seal hash and fill in the rest of the extra data + h := clique.SealHash(header) + sighash, err := crypto.Sign(h[:], i.Clique.Key) + if err != nil { + return nil, err + } + copy(header.Extra[32:], sighash) + block = block.WithSeal(header) + return block, nil +} + +// BuildBlock constructs a block from the given inputs. +func BuildBlock(ctx *cli.Context) error { + // Configure the go-ethereum logger + glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) + glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name))) + log.Root().SetHandler(glogger) + + baseDir, err := createBasedir(ctx) + if err != nil { + return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err)) + } + inputData, err := readInput(ctx) + if err != nil { + return err + } + block := inputData.ToBlock() + block, err = inputData.SealBlock(block) + if err != nil { + return err + } + return dispatchBlock(ctx, baseDir, block) +} + +func readInput(ctx *cli.Context) (*bbInput, error) { + var ( + headerStr = ctx.String(InputHeaderFlag.Name) + ommersStr = ctx.String(InputOmmersFlag.Name) + txsStr = ctx.String(InputTxsRlpFlag.Name) + cliqueStr = ctx.String(SealCliqueFlag.Name) + ethashOn = ctx.Bool(SealEthashFlag.Name) + ethashDir = ctx.String(SealEthashDirFlag.Name) + ethashMode = ctx.String(SealEthashModeFlag.Name) + inputData = &bbInput{} + ) + if ethashOn && cliqueStr != "" { + return nil, NewError(ErrorConfig, fmt.Errorf("both ethash and clique sealing specified, only one may be chosen")) + } + if ethashOn { + inputData.Ethash = ethashOn + inputData.EthashDir = ethashDir + switch ethashMode { + case "normal": + inputData.PowMode = ethash.ModeNormal + case "test": + inputData.PowMode = ethash.ModeTest + case "fake": + inputData.PowMode = ethash.ModeFake + default: + return nil, NewError(ErrorConfig, fmt.Errorf("unknown pow mode: %s, supported modes: test, fake, normal", ethashMode)) + } + } + if headerStr == stdinSelector || ommersStr == stdinSelector || txsStr == stdinSelector || cliqueStr == stdinSelector { + decoder := json.NewDecoder(os.Stdin) + if err := decoder.Decode(inputData); err != nil { + return nil, NewError(ErrorJson, fmt.Errorf("failed unmarshaling stdin: %v", err)) + } + } + if cliqueStr != stdinSelector && cliqueStr != "" { + var clique cliqueInput + if err := readFile(cliqueStr, "clique", &clique); err != nil { + return nil, err + } + inputData.Clique = &clique + } + if headerStr != stdinSelector { + var env header + if err := readFile(headerStr, "header", &env); err != nil { + return nil, err + } + inputData.Header = &env + } + if ommersStr != stdinSelector && ommersStr != "" { + var ommers []string + if err := readFile(ommersStr, "ommers", &ommers); err != nil { + return nil, err + } + inputData.OmmersRlp = ommers + } + if txsStr != stdinSelector { + var txs string + if err := readFile(txsStr, "txs", &txs); err != nil { + return nil, err + } + inputData.TxRlp = txs + } + // Deserialize rlp txs and ommers + var ( + ommers = []*types.Header{} + txs = []*types.Transaction{} + ) + if inputData.TxRlp != "" { + if err := rlp.DecodeBytes(common.FromHex(inputData.TxRlp), &txs); err != nil { + return nil, NewError(ErrorRlp, fmt.Errorf("unable to decode transaction from rlp data: %v", err)) + } + inputData.Txs = txs + } + for _, str := range inputData.OmmersRlp { + type extblock struct { + Header *types.Header + Txs []*types.Transaction + Ommers []*types.Header + } + var ommer *extblock + if err := rlp.DecodeBytes(common.FromHex(str), &ommer); err != nil { + return nil, NewError(ErrorRlp, fmt.Errorf("unable to decode ommer from rlp data: %v", err)) + } + ommers = append(ommers, ommer.Header) + } + inputData.Ommers = ommers + + return inputData, nil +} + +// dispatchOutput writes the output data to either stderr or stdout, or to the specified +// files +func dispatchBlock(ctx *cli.Context, baseDir string, block *types.Block) error { + raw, _ := rlp.EncodeToBytes(block) + + type blockInfo struct { + Rlp hexutil.Bytes `json:"rlp"` + Hash common.Hash `json:"hash"` + } + var enc blockInfo + enc.Rlp = raw + enc.Hash = block.Hash() + + b, err := json.MarshalIndent(enc, "", " ") + if err != nil { + return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err)) + } + switch dest := ctx.String(OutputBlockFlag.Name); dest { + case "stdout": + os.Stdout.Write(b) + os.Stdout.WriteString("\n") + case "stderr": + os.Stderr.Write(b) + os.Stderr.WriteString("\n") + default: + if err := saveFile(baseDir, dest, enc); err != nil { + return err + } + } + return nil +} diff --git a/cmd/evm/internal/t8ntool/flags.go b/cmd/evm/internal/t8ntool/flags.go index 05b6ed164..7db39479c 100644 --- a/cmd/evm/internal/t8ntool/flags.go +++ b/cmd/evm/internal/t8ntool/flags.go @@ -68,6 +68,14 @@ var ( "\t - into the file ", Value: "result.json", } + OutputBlockFlag = cli.StringFlag{ + Name: "output.block", + Usage: "Determines where to put the `block` after building.\n" + + "\t`stdout` - into the stdout output\n" + + "\t`stderr` - into the stderr output\n" + + "\t - into the file ", + Value: "block.json", + } InputAllocFlag = cli.StringFlag{ Name: "input.alloc", Usage: "`stdin` or file name of where to find the prestate alloc to use.", @@ -81,10 +89,41 @@ var ( InputTxsFlag = cli.StringFlag{ Name: "input.txs", Usage: "`stdin` or file name of where to find the transactions to apply. " + - "If the file prefix is '.rlp', then the data is interpreted as an RLP list of signed transactions." + + "If the file extension is '.rlp', then the data is interpreted as an RLP list of signed transactions." + "The '.rlp' format is identical to the output.body format.", Value: "txs.json", } + InputHeaderFlag = cli.StringFlag{ + Name: "input.header", + Usage: "`stdin` or file name of where to find the block header to use.", + Value: "header.json", + } + InputOmmersFlag = cli.StringFlag{ + Name: "input.ommers", + Usage: "`stdin` or file name of where to find the list of ommer header RLPs to use.", + } + InputTxsRlpFlag = cli.StringFlag{ + Name: "input.txs", + Usage: "`stdin` or file name of where to find the transactions list in RLP form.", + Value: "txs.rlp", + } + SealCliqueFlag = cli.StringFlag{ + Name: "seal.clique", + Usage: "Seal block with Clique. `stdin` or file name of where to find the Clique sealing data.", + } + SealEthashFlag = cli.BoolFlag{ + Name: "seal.ethash", + Usage: "Seal block with ethash.", + } + SealEthashDirFlag = cli.StringFlag{ + Name: "seal.ethash.dir", + Usage: "Path to ethash DAG. If none exists, a new DAG will be generated.", + } + SealEthashModeFlag = cli.StringFlag{ + Name: "seal.ethash.mode", + Usage: "Defines the type and amount of PoW verification an ethash engine makes.", + Value: "normal", + } RewardFlag = cli.Int64Flag{ Name: "state.reward", Usage: "Mining reward. Set to -1 to disable", diff --git a/cmd/evm/internal/t8ntool/gen_header.go b/cmd/evm/internal/t8ntool/gen_header.go new file mode 100644 index 000000000..196e49dd7 --- /dev/null +++ b/cmd/evm/internal/t8ntool/gen_header.go @@ -0,0 +1,135 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package t8ntool + +import ( + "encoding/json" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/types" +) + +var _ = (*headerMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (h header) MarshalJSON() ([]byte, error) { + type header struct { + ParentHash common.Hash `json:"parentHash"` + OmmerHash *common.Hash `json:"sha3Uncles"` + Coinbase *common.Address `json:"miner"` + Root common.Hash `json:"stateRoot" gencodec:"required"` + TxHash *common.Hash `json:"transactionsRoot"` + ReceiptHash *common.Hash `json:"receiptsRoot"` + Bloom types.Bloom `json:"logsBloom"` + Difficulty *math.HexOrDecimal256 `json:"difficulty"` + Number *math.HexOrDecimal256 `json:"number" gencodec:"required"` + GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` + GasUsed math.HexOrDecimal64 `json:"gasUsed"` + Time math.HexOrDecimal64 `json:"timestamp" gencodec:"required"` + Extra hexutil.Bytes `json:"extraData"` + MixDigest common.Hash `json:"mixHash"` + Nonce *types.BlockNonce `json:"nonce"` + BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas" rlp:"optional"` + } + var enc header + enc.ParentHash = h.ParentHash + enc.OmmerHash = h.OmmerHash + enc.Coinbase = h.Coinbase + enc.Root = h.Root + enc.TxHash = h.TxHash + enc.ReceiptHash = h.ReceiptHash + enc.Bloom = h.Bloom + enc.Difficulty = (*math.HexOrDecimal256)(h.Difficulty) + enc.Number = (*math.HexOrDecimal256)(h.Number) + enc.GasLimit = math.HexOrDecimal64(h.GasLimit) + enc.GasUsed = math.HexOrDecimal64(h.GasUsed) + enc.Time = math.HexOrDecimal64(h.Time) + enc.Extra = h.Extra + enc.MixDigest = h.MixDigest + enc.Nonce = h.Nonce + enc.BaseFee = (*math.HexOrDecimal256)(h.BaseFee) + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (h *header) UnmarshalJSON(input []byte) error { + type header struct { + ParentHash *common.Hash `json:"parentHash"` + OmmerHash *common.Hash `json:"sha3Uncles"` + Coinbase *common.Address `json:"miner"` + Root *common.Hash `json:"stateRoot" gencodec:"required"` + TxHash *common.Hash `json:"transactionsRoot"` + ReceiptHash *common.Hash `json:"receiptsRoot"` + Bloom *types.Bloom `json:"logsBloom"` + Difficulty *math.HexOrDecimal256 `json:"difficulty"` + Number *math.HexOrDecimal256 `json:"number" gencodec:"required"` + GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` + GasUsed *math.HexOrDecimal64 `json:"gasUsed"` + Time *math.HexOrDecimal64 `json:"timestamp" gencodec:"required"` + Extra *hexutil.Bytes `json:"extraData"` + MixDigest *common.Hash `json:"mixHash"` + Nonce *types.BlockNonce `json:"nonce"` + BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas" rlp:"optional"` + } + var dec header + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.ParentHash != nil { + h.ParentHash = *dec.ParentHash + } + if dec.OmmerHash != nil { + h.OmmerHash = dec.OmmerHash + } + if dec.Coinbase != nil { + h.Coinbase = dec.Coinbase + } + if dec.Root == nil { + return errors.New("missing required field 'stateRoot' for header") + } + h.Root = *dec.Root + if dec.TxHash != nil { + h.TxHash = dec.TxHash + } + if dec.ReceiptHash != nil { + h.ReceiptHash = dec.ReceiptHash + } + if dec.Bloom != nil { + h.Bloom = *dec.Bloom + } + if dec.Difficulty != nil { + h.Difficulty = (*big.Int)(dec.Difficulty) + } + if dec.Number == nil { + return errors.New("missing required field 'number' for header") + } + h.Number = (*big.Int)(dec.Number) + if dec.GasLimit == nil { + return errors.New("missing required field 'gasLimit' for header") + } + h.GasLimit = uint64(*dec.GasLimit) + if dec.GasUsed != nil { + h.GasUsed = uint64(*dec.GasUsed) + } + if dec.Time == nil { + return errors.New("missing required field 'timestamp' for header") + } + h.Time = uint64(*dec.Time) + if dec.Extra != nil { + h.Extra = *dec.Extra + } + if dec.MixDigest != nil { + h.MixDigest = *dec.MixDigest + } + if dec.Nonce != nil { + h.Nonce = dec.Nonce + } + if dec.BaseFee != nil { + h.BaseFee = (*big.Int)(dec.BaseFee) + } + return nil +} diff --git a/cmd/evm/internal/t8ntool/transaction.go b/cmd/evm/internal/t8ntool/transaction.go index e4fe24108..6f1c964ad 100644 --- a/cmd/evm/internal/t8ntool/transaction.go +++ b/cmd/evm/internal/t8ntool/transaction.go @@ -82,7 +82,7 @@ func Transaction(ctx *cli.Context) error { ) // Construct the chainconfig if cConf, _, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil { - return NewError(ErrorVMConfig, fmt.Errorf("failed constructing chain configuration: %v", err)) + return NewError(ErrorConfig, fmt.Errorf("failed constructing chain configuration: %v", err)) } else { chainConfig = cConf } diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index 0aff715eb..8f203b0ed 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -43,11 +43,12 @@ import ( const ( ErrorEVM = 2 - ErrorVMConfig = 3 + ErrorConfig = 3 ErrorMissingBlockhash = 4 ErrorJson = 10 ErrorIO = 11 + ErrorRlp = 12 stdinSelector = "stdin" ) @@ -88,21 +89,14 @@ func Transition(ctx *cli.Context) error { log.Root().SetHandler(glogger) var ( - err error - tracer vm.EVMLogger - baseDir = "" + err error + tracer vm.EVMLogger ) var getTracer func(txIndex int, txHash common.Hash) (vm.EVMLogger, error) - // If user specified a basedir, make sure it exists - if ctx.IsSet(OutputBasedir.Name) { - if base := ctx.String(OutputBasedir.Name); len(base) > 0 { - err := os.MkdirAll(base, 0755) // //rw-r--r-- - if err != nil { - return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err)) - } - baseDir = base - } + baseDir, err := createBasedir(ctx) + if err != nil { + return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err)) } if ctx.Bool(TraceFlag.Name) { // Configure the EVM logger @@ -155,29 +149,17 @@ func Transition(ctx *cli.Context) error { } } if allocStr != stdinSelector { - inFile, err := os.Open(allocStr) - if err != nil { - return NewError(ErrorIO, fmt.Errorf("failed reading alloc file: %v", err)) - } - defer inFile.Close() - decoder := json.NewDecoder(inFile) - if err := decoder.Decode(&inputData.Alloc); err != nil { - return NewError(ErrorJson, fmt.Errorf("failed unmarshaling alloc-file: %v", err)) + if err := readFile(allocStr, "alloc", &inputData.Alloc); err != nil { + return err } } prestate.Pre = inputData.Alloc // Set the block environment if envStr != stdinSelector { - inFile, err := os.Open(envStr) - if err != nil { - return NewError(ErrorIO, fmt.Errorf("failed reading env file: %v", err)) - } - defer inFile.Close() - decoder := json.NewDecoder(inFile) var env stEnv - if err := decoder.Decode(&env); err != nil { - return NewError(ErrorJson, fmt.Errorf("failed unmarshaling env-file: %v", err)) + if err := readFile(envStr, "env", &env); err != nil { + return err } inputData.Env = &env } @@ -190,7 +172,7 @@ func Transition(ctx *cli.Context) error { // Construct the chainconfig var chainConfig *params.ChainConfig if cConf, extraEips, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil { - return NewError(ErrorVMConfig, fmt.Errorf("failed constructing chain configuration: %v", err)) + return NewError(ErrorConfig, fmt.Errorf("failed constructing chain configuration: %v", err)) } else { chainConfig = cConf vmConfig.ExtraEips = extraEips @@ -254,18 +236,18 @@ func Transition(ctx *cli.Context) error { // Sanity check, to not `panic` in state_transition if chainConfig.IsLondon(big.NewInt(int64(prestate.Env.Number))) { if prestate.Env.BaseFee == nil { - return NewError(ErrorVMConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section")) + return NewError(ErrorConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section")) } } if env := prestate.Env; env.Difficulty == nil { // If difficulty was not provided by caller, we need to calculate it. switch { case env.ParentDifficulty == nil: - return NewError(ErrorVMConfig, errors.New("currentDifficulty was not provided, and cannot be calculated due to missing parentDifficulty")) + return NewError(ErrorConfig, errors.New("currentDifficulty was not provided, and cannot be calculated due to missing parentDifficulty")) case env.Number == 0: - return NewError(ErrorVMConfig, errors.New("currentDifficulty needs to be provided for block number 0")) + return NewError(ErrorConfig, errors.New("currentDifficulty needs to be provided for block number 0")) case env.Timestamp <= env.ParentTimestamp: - return NewError(ErrorVMConfig, fmt.Errorf("currentDifficulty cannot be calculated -- currentTime (%d) needs to be after parent time (%d)", + return NewError(ErrorConfig, fmt.Errorf("currentDifficulty cannot be calculated -- currentTime (%d) needs to be after parent time (%d)", env.Timestamp, env.ParentTimestamp)) } prestate.Env.Difficulty = calcDifficulty(chainConfig, env.Number, env.Timestamp, diff --git a/cmd/evm/internal/t8ntool/utils.go b/cmd/evm/internal/t8ntool/utils.go new file mode 100644 index 000000000..1c54f09bf --- /dev/null +++ b/cmd/evm/internal/t8ntool/utils.go @@ -0,0 +1,54 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package t8ntool + +import ( + "encoding/json" + "fmt" + "os" + + "gopkg.in/urfave/cli.v1" +) + +// readFile reads the json-data in the provided path and marshals into dest. +func readFile(path, desc string, dest interface{}) error { + inFile, err := os.Open(path) + if err != nil { + return NewError(ErrorIO, fmt.Errorf("failed reading %s file: %v", desc, err)) + } + defer inFile.Close() + decoder := json.NewDecoder(inFile) + if err := decoder.Decode(dest); err != nil { + return NewError(ErrorJson, fmt.Errorf("failed unmarshaling %s file: %v", desc, err)) + } + return nil +} + +// createBasedir makes sure the basedir exists, if user specified one. +func createBasedir(ctx *cli.Context) (string, error) { + baseDir := "" + if ctx.IsSet(OutputBasedir.Name) { + if base := ctx.String(OutputBasedir.Name); len(base) > 0 { + err := os.MkdirAll(base, 0755) // //rw-r--r-- + if err != nil { + return "", err + } + baseDir = base + } + } + return baseDir, nil +} diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 26064efc3..66d221a70 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -167,6 +167,25 @@ var transactionCommand = cli.Command{ }, } +var blockBuilderCommand = cli.Command{ + Name: "block-builder", + Aliases: []string{"b11r"}, + Usage: "builds a block", + Action: t8ntool.BuildBlock, + Flags: []cli.Flag{ + t8ntool.OutputBasedir, + t8ntool.OutputBlockFlag, + t8ntool.InputHeaderFlag, + t8ntool.InputOmmersFlag, + t8ntool.InputTxsRlpFlag, + t8ntool.SealCliqueFlag, + t8ntool.SealEthashFlag, + t8ntool.SealEthashDirFlag, + t8ntool.SealEthashModeFlag, + t8ntool.VerbosityFlag, + }, +} + func init() { app.Flags = []cli.Flag{ BenchFlag, @@ -200,6 +219,7 @@ func init() { stateTestCommand, stateTransitionCommand, transactionCommand, + blockBuilderCommand, } cli.CommandHelpTemplate = flags.OriginCommandHelpTemplate } diff --git a/cmd/evm/t8n_test.go b/cmd/evm/t8n_test.go index b4b816f57..3c759570f 100644 --- a/cmd/evm/t8n_test.go +++ b/cmd/evm/t8n_test.go @@ -131,7 +131,7 @@ func TestT8n(t *testing.T) { output: t8nOutput{alloc: true, result: true}, expExitCode: 4, }, - { // Ommer test + { // Uncle test base: "./testdata/5", input: t8nInput{ "alloc.json", "txs.json", "env.json", "Byzantium", "0x80", @@ -171,7 +171,7 @@ func TestT8n(t *testing.T) { output: t8nOutput{result: true}, expOut: "exp2.json", }, - { // Difficulty calculation - with uncles + Berlin + { // Difficulty calculation - with ommers + Berlin base: "./testdata/14", input: t8nInput{ "alloc.json", "txs.json", "env.uncles.json", "Berlin", "", @@ -336,6 +336,126 @@ func TestT9n(t *testing.T) { } } +type b11rInput struct { + inEnv string + inOmmersRlp string + inTxsRlp string + inClique string + ethash bool + ethashMode string + ethashDir string +} + +func (args *b11rInput) get(base string) []string { + var out []string + if opt := args.inEnv; opt != "" { + out = append(out, "--input.header") + out = append(out, fmt.Sprintf("%v/%v", base, opt)) + } + if opt := args.inOmmersRlp; opt != "" { + out = append(out, "--input.ommers") + out = append(out, fmt.Sprintf("%v/%v", base, opt)) + } + if opt := args.inTxsRlp; opt != "" { + out = append(out, "--input.txs") + out = append(out, fmt.Sprintf("%v/%v", base, opt)) + } + if opt := args.inClique; opt != "" { + out = append(out, "--seal.clique") + out = append(out, fmt.Sprintf("%v/%v", base, opt)) + } + if args.ethash { + out = append(out, "--seal.ethash") + } + if opt := args.ethashMode; opt != "" { + out = append(out, "--seal.ethash.mode") + out = append(out, fmt.Sprintf("%v/%v", base, opt)) + } + if opt := args.ethashDir; opt != "" { + out = append(out, "--seal.ethash.dir") + out = append(out, fmt.Sprintf("%v/%v", base, opt)) + } + out = append(out, "--output.block") + out = append(out, "stdout") + return out +} + +func TestB11r(t *testing.T) { + tt := new(testT8n) + tt.TestCmd = cmdtest.NewTestCmd(t, tt) + for i, tc := range []struct { + base string + input b11rInput + expExitCode int + expOut string + }{ + { // unsealed block + base: "./testdata/20", + input: b11rInput{ + inEnv: "header.json", + inOmmersRlp: "ommers.json", + inTxsRlp: "txs.rlp", + }, + expOut: "exp.json", + }, + { // ethash test seal + base: "./testdata/21", + input: b11rInput{ + inEnv: "header.json", + inOmmersRlp: "ommers.json", + inTxsRlp: "txs.rlp", + }, + expOut: "exp.json", + }, + { // clique test seal + base: "./testdata/21", + input: b11rInput{ + inEnv: "header.json", + inOmmersRlp: "ommers.json", + inTxsRlp: "txs.rlp", + inClique: "clique.json", + }, + expOut: "exp-clique.json", + }, + { // block with ommers + base: "./testdata/22", + input: b11rInput{ + inEnv: "header.json", + inOmmersRlp: "ommers.json", + inTxsRlp: "txs.rlp", + }, + expOut: "exp.json", + }, + } { + + args := []string{"b11r"} + args = append(args, tc.input.get(tc.base)...) + + tt.Run("evm-test", args...) + tt.Logf("args:\n go run . %v\n", strings.Join(args, " ")) + // Compare the expected output, if provided + if tc.expOut != "" { + want, err := os.ReadFile(fmt.Sprintf("%v/%v", tc.base, tc.expOut)) + if err != nil { + t.Fatalf("test %d: could not read expected output: %v", i, err) + } + have := tt.Output() + ok, err := cmpJson(have, want) + switch { + case err != nil: + t.Logf(string(have)) + t.Fatalf("test %d, json parsing failed: %v", i, err) + case !ok: + t.Fatalf("test %d: output wrong, have \n%v\nwant\n%v\n", i, string(have), string(want)) + } + } + tt.WaitExit() + if have, want := tt.ExitStatus(), tc.expExitCode; have != want { + t.Fatalf("test %d: wrong exit code, have %d, want %d", i, have, want) + } + } +} + // cmpJson compares the JSON in two byte slices. func cmpJson(a, b []byte) (bool, error) { var j, j2 interface{} diff --git a/cmd/evm/testdata/20/exp.json b/cmd/evm/testdata/20/exp.json new file mode 100644 index 000000000..7bec6cefd --- /dev/null +++ b/cmd/evm/testdata/20/exp.json @@ -0,0 +1,4 @@ +{ + "rlp": "0xf902d9f90211a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794e997a23b159e2e2a5ce72333262972374b15425ca0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e99476574682f76312e302e312f6c696e75782f676f312e342e32a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf8897435673d874f7c8f8c2f85f8002825208948a8eafb1cf62bfbeb1741769dae1a9dd4799619201801ba09500e8ba27d3c33ca7764e107410f44cbd8c19794bde214d694683a7aa998cdba07235ae07e4bd6e0206d102b1f8979d6adab280466b6a82d2208ee08951f1f600f85f8002825208948a8eafb1cf62bfbeb1741769dae1a9dd4799619201801ba09500e8ba27d3c33ca7764e107410f44cbd8c19794bde214d694683a7aa998cdba07235ae07e4bd6e0206d102b1f8979d6adab280466b6a82d2208ee08951f1f600c0", + "hash": "0xaba9a3b6a4e96e9ecffcadaa5a2ae0589359455617535cd86589fe1dd26fe899" +} diff --git a/cmd/evm/testdata/20/header.json b/cmd/evm/testdata/20/header.json new file mode 100644 index 000000000..fb9b7fc56 --- /dev/null +++ b/cmd/evm/testdata/20/header.json @@ -0,0 +1,14 @@ +{ + "parentHash": "0xd6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34e", + "miner": "0xe997a23b159e2e2a5ce72333262972374b15425c", + "stateRoot": "0x325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2e", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x1000", + "number": "0xc3be", + "gasLimit": "0x50785", + "gasUsed": "0x0", + "timestamp": "0x55c5277e", + "extraData": "0x476574682f76312e302e312f6c696e75782f676f312e342e32", + "mixHash": "0x5865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf", + "nonce": "0x97435673d874f7c8" +} diff --git a/cmd/evm/testdata/20/ommers.json b/cmd/evm/testdata/20/ommers.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/cmd/evm/testdata/20/ommers.json @@ -0,0 +1 @@ +[] diff --git a/cmd/evm/testdata/20/readme.md b/cmd/evm/testdata/20/readme.md new file mode 100644 index 000000000..2c448a96e --- /dev/null +++ b/cmd/evm/testdata/20/readme.md @@ -0,0 +1,11 @@ +# Block building + +This test shows how `b11r` can be used to assemble an unsealed block. + +```console +$ go run . b11r --input.header=testdata/20/header.json --input.txs=testdata/20/txs.rlp --input.ommers=testdata/20/ommers.json --output.block=stdout +{ + "rlp": "0xf90216f90211a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794e997a23b159e2e2a5ce72333262972374b15425ca0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e99476574682f76312e302e312f6c696e75782f676f312e342e32a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf8897435673d874f7c8c0c0", + "hash": "0xaba9a3b6a4e96e9ecffcadaa5a2ae0589359455617535cd86589fe1dd26fe899" +} +``` diff --git a/cmd/evm/testdata/20/txs.rlp b/cmd/evm/testdata/20/txs.rlp new file mode 100644 index 000000000..3599ff065 --- /dev/null +++ b/cmd/evm/testdata/20/txs.rlp @@ -0,0 +1 @@ +"0xf8c2f85f8002825208948a8eafb1cf62bfbeb1741769dae1a9dd4799619201801ba09500e8ba27d3c33ca7764e107410f44cbd8c19794bde214d694683a7aa998cdba07235ae07e4bd6e0206d102b1f8979d6adab280466b6a82d2208ee08951f1f600f85f8002825208948a8eafb1cf62bfbeb1741769dae1a9dd4799619201801ba09500e8ba27d3c33ca7764e107410f44cbd8c19794bde214d694683a7aa998cdba07235ae07e4bd6e0206d102b1f8979d6adab280466b6a82d2208ee08951f1f600" \ No newline at end of file diff --git a/cmd/evm/testdata/21/clique.json b/cmd/evm/testdata/21/clique.json new file mode 100644 index 000000000..84fa259a0 --- /dev/null +++ b/cmd/evm/testdata/21/clique.json @@ -0,0 +1,6 @@ +{ + "secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "voted": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "authorize": false, + "vanity": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +} diff --git a/cmd/evm/testdata/21/exp-clique.json b/cmd/evm/testdata/21/exp-clique.json new file mode 100644 index 000000000..c990ba8aa --- /dev/null +++ b/cmd/evm/testdata/21/exp-clique.json @@ -0,0 +1,4 @@ +{ + "rlp": "0xf9025ff9025aa0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277eb861aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac540a67aaee364005841da84f488f6b6d0116dfb5103d091402c81a163d5f66666595e37f56f196d8c5c98da714dbfae68d6b7e1790cc734a20ec6ce52213ad800a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf88ffffffffffffffffc0c0", + "hash": "0x71c59102cc805dbe8741e1210ebe229a321eff144ac7276006fefe39e8357dc7" +} diff --git a/cmd/evm/testdata/21/exp.json b/cmd/evm/testdata/21/exp.json new file mode 100644 index 000000000..b3e5e7a83 --- /dev/null +++ b/cmd/evm/testdata/21/exp.json @@ -0,0 +1,4 @@ +{ + "rlp": "0xf901fdf901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000c0c0", + "hash": "0x801411e9f6609a659825690d13e4f75a3cfe9143952fa2d9573f3b0a5eb9ebbb" +} diff --git a/cmd/evm/testdata/21/header.json b/cmd/evm/testdata/21/header.json new file mode 100644 index 000000000..62abe3cc2 --- /dev/null +++ b/cmd/evm/testdata/21/header.json @@ -0,0 +1,11 @@ +{ + "parentHash": "0xd6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34e", + "stateRoot": "0x325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2e", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x1000", + "number": "0xc3be", + "gasLimit": "0x50785", + "gasUsed": "0x0", + "timestamp": "0x55c5277e", + "mixHash": "0x5865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf" +} diff --git a/cmd/evm/testdata/21/ommers.json b/cmd/evm/testdata/21/ommers.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/cmd/evm/testdata/21/ommers.json @@ -0,0 +1 @@ +[] diff --git a/cmd/evm/testdata/21/readme.md b/cmd/evm/testdata/21/readme.md new file mode 100644 index 000000000..b70f106ff --- /dev/null +++ b/cmd/evm/testdata/21/readme.md @@ -0,0 +1,23 @@ +# Sealed block building + +This test shows how `b11r` can be used to assemble a sealed block. + +## Ethash + +```console +$ go run . b11r --input.header=testdata/21/header.json --input.txs=testdata/21/txs.rlp --input.ommers=testdata/21/ommers.json --seal.ethash --seal.ethash.mode=test --output.block=stdout +{ + "rlp": "0xf901fdf901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000c0c0", + "hash": "0x801411e9f6609a659825690d13e4f75a3cfe9143952fa2d9573f3b0a5eb9ebbb" +} +``` + +## Clique + +```console +$ go run . b11r --input.header=testdata/21/header.json --input.txs=testdata/21/txs.rlp --input.ommers=testdata/21/ommers.json --seal.clique=testdata/21/clique.json --output.block=stdout +{ + "rlp": "0xf9025ff9025aa0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277eb861aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac540a67aaee364005841da84f488f6b6d0116dfb5103d091402c81a163d5f66666595e37f56f196d8c5c98da714dbfae68d6b7e1790cc734a20ec6ce52213ad800a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf88ffffffffffffffffc0c0", + "hash": "0x71c59102cc805dbe8741e1210ebe229a321eff144ac7276006fefe39e8357dc7" +} +``` diff --git a/cmd/evm/testdata/21/txs.rlp b/cmd/evm/testdata/21/txs.rlp new file mode 100644 index 000000000..e815397b3 --- /dev/null +++ b/cmd/evm/testdata/21/txs.rlp @@ -0,0 +1 @@ +"c0" diff --git a/cmd/evm/testdata/22/exp-clique.json b/cmd/evm/testdata/22/exp-clique.json new file mode 100644 index 000000000..c990ba8aa --- /dev/null +++ b/cmd/evm/testdata/22/exp-clique.json @@ -0,0 +1,4 @@ +{ + "rlp": "0xf9025ff9025aa0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277eb861aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac540a67aaee364005841da84f488f6b6d0116dfb5103d091402c81a163d5f66666595e37f56f196d8c5c98da714dbfae68d6b7e1790cc734a20ec6ce52213ad800a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf88ffffffffffffffffc0c0", + "hash": "0x71c59102cc805dbe8741e1210ebe229a321eff144ac7276006fefe39e8357dc7" +} diff --git a/cmd/evm/testdata/22/exp.json b/cmd/evm/testdata/22/exp.json new file mode 100644 index 000000000..14fd81997 --- /dev/null +++ b/cmd/evm/testdata/22/exp.json @@ -0,0 +1,4 @@ +{ + "rlp": "0xf905f5f901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea06eb9f0c3cd68c9e97134e6725d12b1f1d8f0644458da6870a37ff84c908fb1e7940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000c0f903f6f901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000f901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000", + "hash": "0xd9a81c8fcd57a7f2a0d2c375eff6ad192c30c3729a271303f0a9a7e1b357e755" +} diff --git a/cmd/evm/testdata/22/header.json b/cmd/evm/testdata/22/header.json new file mode 100644 index 000000000..62abe3cc2 --- /dev/null +++ b/cmd/evm/testdata/22/header.json @@ -0,0 +1,11 @@ +{ + "parentHash": "0xd6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34e", + "stateRoot": "0x325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2e", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x1000", + "number": "0xc3be", + "gasLimit": "0x50785", + "gasUsed": "0x0", + "timestamp": "0x55c5277e", + "mixHash": "0x5865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf" +} diff --git a/cmd/evm/testdata/22/ommers.json b/cmd/evm/testdata/22/ommers.json new file mode 100644 index 000000000..997015b3c --- /dev/null +++ b/cmd/evm/testdata/22/ommers.json @@ -0,0 +1 @@ +["0xf901fdf901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000c0c0","0xf901fdf901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000c0c0"] diff --git a/cmd/evm/testdata/22/readme.md b/cmd/evm/testdata/22/readme.md new file mode 100644 index 000000000..2cac8a243 --- /dev/null +++ b/cmd/evm/testdata/22/readme.md @@ -0,0 +1,11 @@ +# Building blocks with ommers + +This test shows how `b11r` can chain together ommer assembles into a canonical block. + +```console +$ echo "{ \"ommers\": [`go run . b11r --input.header=testdata/22/header.json --input.txs=testdata/22/txs.rlp --output.block=stdout | jq '.[\"rlp\"]'`,`go run . b11r --input.header=testdata/22/header.json --input.txs=testdata/22/txs.rlp --output.block=stdout | jq '.[\"rlp\"]'`]}" | go run . b11r --input.header=testdata/22/header.json --input.txs=testdata/22/txs.rlp --input.ommers=stdin --output.block=stdout +{ + "rlp": "0xf905f5f901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea06eb9f0c3cd68c9e97134e6725d12b1f1d8f0644458da6870a37ff84c908fb1e7940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000c0f903f6f901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000f901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000", + "hash": "0xd9a81c8fcd57a7f2a0d2c375eff6ad192c30c3729a271303f0a9a7e1b357e755" +} +``` diff --git a/cmd/evm/testdata/22/txs.rlp b/cmd/evm/testdata/22/txs.rlp new file mode 100644 index 000000000..e815397b3 --- /dev/null +++ b/cmd/evm/testdata/22/txs.rlp @@ -0,0 +1 @@ +"c0"