| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- // 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 <http://www.gnu.org/licenses/>.
- 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"
- "github.com/urfave/cli/v2"
- )
- //go:generate go run github.com/fjl/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
- }
|