github.com/tacshi/go-ethereum@v0.0.0-20230616113857-84a434e20921/cmd/evm/internal/t8ntool/block.go (about) 1 // Copyright 2021 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // go-ethereum is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 17 package t8ntool 18 19 import ( 20 "crypto/ecdsa" 21 "encoding/json" 22 "errors" 23 "fmt" 24 "math/big" 25 "os" 26 27 "github.com/tacshi/go-ethereum/common" 28 "github.com/tacshi/go-ethereum/common/hexutil" 29 "github.com/tacshi/go-ethereum/common/math" 30 "github.com/tacshi/go-ethereum/consensus/clique" 31 "github.com/tacshi/go-ethereum/consensus/ethash" 32 "github.com/tacshi/go-ethereum/core/types" 33 "github.com/tacshi/go-ethereum/crypto" 34 "github.com/tacshi/go-ethereum/log" 35 "github.com/tacshi/go-ethereum/rlp" 36 "github.com/urfave/cli/v2" 37 ) 38 39 //go:generate go run github.com/fjl/gencodec -type header -field-override headerMarshaling -out gen_header.go 40 type header struct { 41 ParentHash common.Hash `json:"parentHash"` 42 OmmerHash *common.Hash `json:"sha3Uncles"` 43 Coinbase *common.Address `json:"miner"` 44 Root common.Hash `json:"stateRoot" gencodec:"required"` 45 TxHash *common.Hash `json:"transactionsRoot"` 46 ReceiptHash *common.Hash `json:"receiptsRoot"` 47 Bloom types.Bloom `json:"logsBloom"` 48 Difficulty *big.Int `json:"difficulty"` 49 Number *big.Int `json:"number" gencodec:"required"` 50 GasLimit uint64 `json:"gasLimit" gencodec:"required"` 51 GasUsed uint64 `json:"gasUsed"` 52 Time uint64 `json:"timestamp" gencodec:"required"` 53 Extra []byte `json:"extraData"` 54 MixDigest common.Hash `json:"mixHash"` 55 Nonce *types.BlockNonce `json:"nonce"` 56 BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"` 57 WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` 58 } 59 60 type headerMarshaling struct { 61 Difficulty *math.HexOrDecimal256 62 Number *math.HexOrDecimal256 63 GasLimit math.HexOrDecimal64 64 GasUsed math.HexOrDecimal64 65 Time math.HexOrDecimal64 66 Extra hexutil.Bytes 67 BaseFee *math.HexOrDecimal256 68 } 69 70 type bbInput struct { 71 Header *header `json:"header,omitempty"` 72 OmmersRlp []string `json:"ommers,omitempty"` 73 TxRlp string `json:"txs,omitempty"` 74 Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` 75 Clique *cliqueInput `json:"clique,omitempty"` 76 77 Ethash bool `json:"-"` 78 EthashDir string `json:"-"` 79 PowMode ethash.Mode `json:"-"` 80 Txs []*types.Transaction `json:"-"` 81 Ommers []*types.Header `json:"-"` 82 } 83 84 type cliqueInput struct { 85 Key *ecdsa.PrivateKey 86 Voted *common.Address 87 Authorize *bool 88 Vanity common.Hash 89 } 90 91 // UnmarshalJSON implements json.Unmarshaler interface. 92 func (c *cliqueInput) UnmarshalJSON(input []byte) error { 93 var x struct { 94 Key *common.Hash `json:"secretKey"` 95 Voted *common.Address `json:"voted"` 96 Authorize *bool `json:"authorize"` 97 Vanity common.Hash `json:"vanity"` 98 } 99 if err := json.Unmarshal(input, &x); err != nil { 100 return err 101 } 102 if x.Key == nil { 103 return errors.New("missing required field 'secretKey' for cliqueInput") 104 } 105 if ecdsaKey, err := crypto.ToECDSA(x.Key[:]); err != nil { 106 return err 107 } else { 108 c.Key = ecdsaKey 109 } 110 c.Voted = x.Voted 111 c.Authorize = x.Authorize 112 c.Vanity = x.Vanity 113 return nil 114 } 115 116 // ToBlock converts i into a *types.Block 117 func (i *bbInput) ToBlock() *types.Block { 118 header := &types.Header{ 119 ParentHash: i.Header.ParentHash, 120 UncleHash: types.EmptyUncleHash, 121 Coinbase: common.Address{}, 122 Root: i.Header.Root, 123 TxHash: types.EmptyTxsHash, 124 ReceiptHash: types.EmptyReceiptsHash, 125 Bloom: i.Header.Bloom, 126 Difficulty: common.Big0, 127 Number: i.Header.Number, 128 GasLimit: i.Header.GasLimit, 129 GasUsed: i.Header.GasUsed, 130 Time: i.Header.Time, 131 Extra: i.Header.Extra, 132 MixDigest: i.Header.MixDigest, 133 BaseFee: i.Header.BaseFee, 134 WithdrawalsHash: i.Header.WithdrawalsHash, 135 } 136 137 // Fill optional values. 138 if i.Header.OmmerHash != nil { 139 header.UncleHash = *i.Header.OmmerHash 140 } else if len(i.Ommers) != 0 { 141 // Calculate the ommer hash if none is provided and there are ommers to hash 142 header.UncleHash = types.CalcUncleHash(i.Ommers) 143 } 144 if i.Header.Coinbase != nil { 145 header.Coinbase = *i.Header.Coinbase 146 } 147 if i.Header.TxHash != nil { 148 header.TxHash = *i.Header.TxHash 149 } 150 if i.Header.ReceiptHash != nil { 151 header.ReceiptHash = *i.Header.ReceiptHash 152 } 153 if i.Header.Nonce != nil { 154 header.Nonce = *i.Header.Nonce 155 } 156 if header.Difficulty != nil { 157 header.Difficulty = i.Header.Difficulty 158 } 159 return types.NewBlockWithHeader(header).WithBody(i.Txs, i.Ommers).WithWithdrawals(i.Withdrawals) 160 } 161 162 // SealBlock seals the given block using the configured engine. 163 func (i *bbInput) SealBlock(block *types.Block) (*types.Block, error) { 164 switch { 165 case i.Ethash: 166 return i.sealEthash(block) 167 case i.Clique != nil: 168 return i.sealClique(block) 169 default: 170 return block, nil 171 } 172 } 173 174 // sealEthash seals the given block using ethash. 175 func (i *bbInput) sealEthash(block *types.Block) (*types.Block, error) { 176 if i.Header.Nonce != nil { 177 return nil, NewError(ErrorConfig, fmt.Errorf("sealing with ethash will overwrite provided nonce")) 178 } 179 ethashConfig := ethash.Config{ 180 PowMode: i.PowMode, 181 DatasetDir: i.EthashDir, 182 CacheDir: i.EthashDir, 183 DatasetsInMem: 1, 184 DatasetsOnDisk: 2, 185 CachesInMem: 2, 186 CachesOnDisk: 3, 187 } 188 engine := ethash.New(ethashConfig, nil, true) 189 defer engine.Close() 190 // Use a buffered chan for results. 191 // If the testmode is used, the sealer will return quickly, and complain 192 // "Sealing result is not read by miner" if it cannot write the result. 193 results := make(chan *types.Block, 1) 194 if err := engine.Seal(nil, block, results, nil); err != nil { 195 panic(fmt.Sprintf("failed to seal block: %v", err)) 196 } 197 found := <-results 198 return block.WithSeal(found.Header()), nil 199 } 200 201 // sealClique seals the given block using clique. 202 func (i *bbInput) sealClique(block *types.Block) (*types.Block, error) { 203 // If any clique value overwrites an explicit header value, fail 204 // to avoid silently building a block with unexpected values. 205 if i.Header.Extra != nil { 206 return nil, NewError(ErrorConfig, fmt.Errorf("sealing with clique will overwrite provided extra data")) 207 } 208 header := block.Header() 209 if i.Clique.Voted != nil { 210 if i.Header.Coinbase != nil { 211 return nil, NewError(ErrorConfig, fmt.Errorf("sealing with clique and voting will overwrite provided coinbase")) 212 } 213 header.Coinbase = *i.Clique.Voted 214 } 215 if i.Clique.Authorize != nil { 216 if i.Header.Nonce != nil { 217 return nil, NewError(ErrorConfig, fmt.Errorf("sealing with clique and voting will overwrite provided nonce")) 218 } 219 if *i.Clique.Authorize { 220 header.Nonce = [8]byte{} 221 } else { 222 header.Nonce = [8]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} 223 } 224 } 225 // Extra is fixed 32 byte vanity and 65 byte signature 226 header.Extra = make([]byte, 32+65) 227 copy(header.Extra[0:32], i.Clique.Vanity.Bytes()[:]) 228 229 // Sign the seal hash and fill in the rest of the extra data 230 h := clique.SealHash(header) 231 sighash, err := crypto.Sign(h[:], i.Clique.Key) 232 if err != nil { 233 return nil, err 234 } 235 copy(header.Extra[32:], sighash) 236 block = block.WithSeal(header) 237 return block, nil 238 } 239 240 // BuildBlock constructs a block from the given inputs. 241 func BuildBlock(ctx *cli.Context) error { 242 // Configure the go-ethereum logger 243 glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) 244 glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name))) 245 log.Root().SetHandler(glogger) 246 247 baseDir, err := createBasedir(ctx) 248 if err != nil { 249 return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err)) 250 } 251 inputData, err := readInput(ctx) 252 if err != nil { 253 return err 254 } 255 block := inputData.ToBlock() 256 block, err = inputData.SealBlock(block) 257 if err != nil { 258 return err 259 } 260 return dispatchBlock(ctx, baseDir, block) 261 } 262 263 func readInput(ctx *cli.Context) (*bbInput, error) { 264 var ( 265 headerStr = ctx.String(InputHeaderFlag.Name) 266 ommersStr = ctx.String(InputOmmersFlag.Name) 267 withdrawalsStr = ctx.String(InputWithdrawalsFlag.Name) 268 txsStr = ctx.String(InputTxsRlpFlag.Name) 269 cliqueStr = ctx.String(SealCliqueFlag.Name) 270 ethashOn = ctx.Bool(SealEthashFlag.Name) 271 ethashDir = ctx.String(SealEthashDirFlag.Name) 272 ethashMode = ctx.String(SealEthashModeFlag.Name) 273 inputData = &bbInput{} 274 ) 275 if ethashOn && cliqueStr != "" { 276 return nil, NewError(ErrorConfig, fmt.Errorf("both ethash and clique sealing specified, only one may be chosen")) 277 } 278 if ethashOn { 279 inputData.Ethash = ethashOn 280 inputData.EthashDir = ethashDir 281 switch ethashMode { 282 case "normal": 283 inputData.PowMode = ethash.ModeNormal 284 case "test": 285 inputData.PowMode = ethash.ModeTest 286 case "fake": 287 inputData.PowMode = ethash.ModeFake 288 default: 289 return nil, NewError(ErrorConfig, fmt.Errorf("unknown pow mode: %s, supported modes: test, fake, normal", ethashMode)) 290 } 291 } 292 if headerStr == stdinSelector || ommersStr == stdinSelector || txsStr == stdinSelector || cliqueStr == stdinSelector { 293 decoder := json.NewDecoder(os.Stdin) 294 if err := decoder.Decode(inputData); err != nil { 295 return nil, NewError(ErrorJson, fmt.Errorf("failed unmarshaling stdin: %v", err)) 296 } 297 } 298 if cliqueStr != stdinSelector && cliqueStr != "" { 299 var clique cliqueInput 300 if err := readFile(cliqueStr, "clique", &clique); err != nil { 301 return nil, err 302 } 303 inputData.Clique = &clique 304 } 305 if headerStr != stdinSelector { 306 var env header 307 if err := readFile(headerStr, "header", &env); err != nil { 308 return nil, err 309 } 310 inputData.Header = &env 311 } 312 if ommersStr != stdinSelector && ommersStr != "" { 313 var ommers []string 314 if err := readFile(ommersStr, "ommers", &ommers); err != nil { 315 return nil, err 316 } 317 inputData.OmmersRlp = ommers 318 } 319 if withdrawalsStr != stdinSelector && withdrawalsStr != "" { 320 var withdrawals []*types.Withdrawal 321 if err := readFile(withdrawalsStr, "withdrawals", &withdrawals); err != nil { 322 return nil, err 323 } 324 inputData.Withdrawals = withdrawals 325 } 326 if txsStr != stdinSelector { 327 var txs string 328 if err := readFile(txsStr, "txs", &txs); err != nil { 329 return nil, err 330 } 331 inputData.TxRlp = txs 332 } 333 // Deserialize rlp txs and ommers 334 var ( 335 ommers = []*types.Header{} 336 txs = []*types.Transaction{} 337 ) 338 if inputData.TxRlp != "" { 339 if err := rlp.DecodeBytes(common.FromHex(inputData.TxRlp), &txs); err != nil { 340 return nil, NewError(ErrorRlp, fmt.Errorf("unable to decode transaction from rlp data: %v", err)) 341 } 342 inputData.Txs = txs 343 } 344 for _, str := range inputData.OmmersRlp { 345 type extblock struct { 346 Header *types.Header 347 Txs []*types.Transaction 348 Ommers []*types.Header 349 } 350 var ommer *extblock 351 if err := rlp.DecodeBytes(common.FromHex(str), &ommer); err != nil { 352 return nil, NewError(ErrorRlp, fmt.Errorf("unable to decode ommer from rlp data: %v", err)) 353 } 354 ommers = append(ommers, ommer.Header) 355 } 356 inputData.Ommers = ommers 357 358 return inputData, nil 359 } 360 361 // dispatchOutput writes the output data to either stderr or stdout, or to the specified 362 // files 363 func dispatchBlock(ctx *cli.Context, baseDir string, block *types.Block) error { 364 raw, _ := rlp.EncodeToBytes(block) 365 type blockInfo struct { 366 Rlp hexutil.Bytes `json:"rlp"` 367 Hash common.Hash `json:"hash"` 368 } 369 enc := blockInfo{ 370 Rlp: raw, 371 Hash: block.Hash(), 372 } 373 b, err := json.MarshalIndent(enc, "", " ") 374 if err != nil { 375 return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err)) 376 } 377 switch dest := ctx.String(OutputBlockFlag.Name); dest { 378 case "stdout": 379 os.Stdout.Write(b) 380 os.Stdout.WriteString("\n") 381 case "stderr": 382 os.Stderr.Write(b) 383 os.Stderr.WriteString("\n") 384 default: 385 if err := saveFile(baseDir, dest, enc); err != nil { 386 return err 387 } 388 } 389 return nil 390 }